diff options
Diffstat (limited to 'drivers')
1949 files changed, 183336 insertions, 63471 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 48bbdbe43e69..26e434ad373c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -1,5 +1,3 @@ -# drivers/Kconfig - menu "Device Drivers" source "drivers/base/Kconfig" diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 28ccdbc05ac8..3597d73f28f6 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -537,7 +537,7 @@ static int __init acpi_memory_device_init(void) status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - acpi_memory_register_notify_handler, + acpi_memory_register_notify_handler, NULL, NULL, NULL); if (ACPI_FAILURE(status)) { @@ -561,7 +561,7 @@ static void __exit acpi_memory_device_exit(void) */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - acpi_memory_deregister_notify_handler, + acpi_memory_deregister_notify_handler, NULL, NULL, NULL); if (ACPI_FAILURE(status)) diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index e7973bc16846..7423052ece5a 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -28,7 +28,7 @@ acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o acpi-y += nsaccess.o nsload.o nssearch.o nsxfeval.o \ nsalloc.o nseval.o nsnames.o nsutils.o nsxfname.o \ nsdump.o nsinit.o nsobject.o nswalk.o nsxfobj.o \ - nsparse.o nspredef.o nsrepair.o + nsparse.o nspredef.o nsrepair.o nsrepair2.o acpi-$(ACPI_FUTURE_USAGE) += nsdumpdv.o diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 3acd9c6760ea..7d9ba6e57554 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -341,6 +341,7 @@ #define ACPI_ERROR_NAMESPACE(s, e) acpi_ns_report_error (AE_INFO, s, e); #define ACPI_ERROR_METHOD(s, n, p, e) acpi_ns_report_method_error (AE_INFO, s, n, p, e); #define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist +#define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist #else @@ -349,6 +350,8 @@ #define ACPI_ERROR_NAMESPACE(s, e) #define ACPI_ERROR_METHOD(s, n, p, e) #define ACPI_WARN_PREDEFINED(plist) +#define ACPI_INFO_PREDEFINED(plist) + #endif /* ACPI_NO_ERROR_MESSAGES */ /* diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index 09a2764c734b..ab83919dda61 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -104,7 +104,8 @@ acpi_ns_walk_namespace(acpi_object_type type, acpi_handle start_object, u32 max_depth, u32 flags, - acpi_walk_callback user_function, + acpi_walk_callback pre_order_visit, + acpi_walk_callback post_order_visit, void *context, void **return_value); struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node @@ -272,7 +273,8 @@ acpi_ns_get_attached_data(struct acpi_namespace_node *node, acpi_object_handler handler, void **data); /* - * nsrepair - return object repair for predefined methods/objects + * nsrepair - General return object repair for all + * predefined methods/objects */ acpi_status acpi_ns_repair_object(struct acpi_predefined_data *data, @@ -285,6 +287,16 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data, union acpi_operand_object **obj_desc_ptr); /* + * nsrepair2 - Return object repair for specific + * predefined methods/objects + */ +acpi_status +acpi_ns_complex_repairs(struct acpi_predefined_data *data, + struct acpi_namespace_node *node, + acpi_status validate_status, + union acpi_operand_object **return_object_ptr); + +/* * nssearch - Namespace searching and entry */ acpi_status diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 863a264b829e..3a451a21a3f9 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -386,6 +386,8 @@ u8 acpi_ut_valid_internal_object(void *object); union acpi_operand_object *acpi_ut_create_package_object(u32 count); +union acpi_operand_object *acpi_ut_create_integer_object(u64 value); + union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size); union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size); @@ -481,6 +483,11 @@ acpi_ut_predefined_warning(const char *module_name, char *pathname, u8 node_flags, const char *format, ...); +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_info(const char *module_name, + u32 line_number, + char *pathname, u8 node_flags, const char *format, ...); + /* Values for Base above (16=Hex, 10=Decimal) */ #define ACPI_ANY_BASE 0 diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c index 3aae13f30c5e..f23fa0be6fc2 100644 --- a/drivers/acpi/acpica/dsinit.c +++ b/drivers/acpi/acpica/dsinit.c @@ -192,7 +192,7 @@ acpi_ds_initialize_objects(u32 table_index, status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, acpi_ds_init_one_object, - &info, NULL); + NULL, &info, NULL); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace")); } diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c index 7d077bb2f525..0ba19f84ad82 100644 --- a/drivers/acpi/acpica/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -409,13 +409,11 @@ acpi_ds_method_data_get_value(u8 type, /* If slack enabled, init the local_x/arg_x to an Integer of value zero */ if (acpi_gbl_enable_interpreter_slack) { - object = - acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + object = acpi_ut_create_integer_object((u64) 0); if (!object) { return_ACPI_STATUS(AE_NO_MEMORY); } - object->integer.value = 0; node->object = object; } diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index 507e1f0bbdfd..9bc1ba076347 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -486,7 +486,7 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, * * Note: technically, this is an error, from ACPI spec: "It is an error * for NumElements to be less than the number of elements in the - * PackageList". However, we just print an error message and + * PackageList". However, we just print a message and * no exception is returned. This provides Windows compatibility. Some * BIOSs will alter the num_elements on the fly, creating this type * of ill-formed package object. @@ -510,9 +510,9 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, arg = arg->common.next; } - ACPI_WARNING((AE_INFO, - "Package List length (0x%X) larger than NumElements count (0x%X), truncated\n", - i, element_count)); + ACPI_INFO((AE_INFO, + "Actual Package length (0x%X) is larger than NumElements field (0x%X), truncated\n", + i, element_count)); } else if (i < element_count) { /* * Arg list (elements) was exhausted, but we did not reach num_elements count. diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 6de3a99d4cd4..10fc78517843 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -639,26 +639,42 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state, break; case AML_SCOPE_OP: - /* - * The Path is an object reference to an existing object. - * Don't enter the name into the namespace, but look it up - * for use later. - */ - status = - acpi_ns_lookup(walk_state->scope_info, buffer_ptr, - object_type, ACPI_IMODE_EXECUTE, - ACPI_NS_SEARCH_PARENT, walk_state, &(node)); - if (ACPI_FAILURE(status)) { -#ifdef ACPI_ASL_COMPILER - if (status == AE_NOT_FOUND) { - status = AE_OK; - } else { - ACPI_ERROR_NAMESPACE(buffer_ptr, status); + + /* Special case for Scope(\) -> refers to the Root node */ + + if (op && (op->named.node == acpi_gbl_root_node)) { + node = op->named.node; + + status = + acpi_ds_scope_stack_push(node, object_type, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); } + } else { + /* + * The Path is an object reference to an existing object. + * Don't enter the name into the namespace, but look it up + * for use later. + */ + status = + acpi_ns_lookup(walk_state->scope_info, buffer_ptr, + object_type, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, + &(node)); + if (ACPI_FAILURE(status)) { +#ifdef ACPI_ASL_COMPILER + if (status == AE_NOT_FOUND) { + status = AE_OK; + } else { + ACPI_ERROR_NAMESPACE(buffer_ptr, + status); + } #else - ACPI_ERROR_NAMESPACE(buffer_ptr, status); + ACPI_ERROR_NAMESPACE(buffer_ptr, status); #endif - return_ACPI_STATUS(status); + return_ACPI_STATUS(status); + } } /* diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index a60aaa7635f3..247920900187 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -945,8 +945,8 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device, ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK, - acpi_ev_save_method_info, gpe_block, - NULL); + acpi_ev_save_method_info, NULL, + gpe_block, NULL); /* Return the new block */ @@ -1022,8 +1022,8 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, - acpi_ev_match_prw_and_gpe, &gpe_info, - NULL); + acpi_ev_match_prw_and_gpe, NULL, + &gpe_info, NULL); } /* diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index 98c7f9c62653..0bc807c33a56 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -51,6 +51,10 @@ ACPI_MODULE_NAME("evregion") /* Local prototypes */ +static u8 +acpi_ev_has_default_handler(struct acpi_namespace_node *node, + acpi_adr_space_type space_id); + static acpi_status acpi_ev_reg_run(acpi_handle obj_handle, u32 level, void *context, void **return_value); @@ -142,6 +146,50 @@ acpi_status acpi_ev_install_region_handlers(void) /******************************************************************************* * + * FUNCTION: acpi_ev_has_default_handler + * + * PARAMETERS: Node - Namespace node for the device + * space_id - The address space ID + * + * RETURN: TRUE if default handler is installed, FALSE otherwise + * + * DESCRIPTION: Check if the default handler is installed for the requested + * space ID. + * + ******************************************************************************/ + +static u8 +acpi_ev_has_default_handler(struct acpi_namespace_node *node, + acpi_adr_space_type space_id) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + + /* Must have an existing internal object */ + + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { + handler_obj = obj_desc->device.handler; + + /* Walk the linked list of handlers for this object */ + + while (handler_obj) { + if (handler_obj->address_space.space_id == space_id) { + if (handler_obj->address_space.handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) { + return (TRUE); + } + } + + handler_obj = handler_obj->address_space.next; + } + } + + return (FALSE); +} + +/******************************************************************************* + * * FUNCTION: acpi_ev_initialize_op_regions * * PARAMETERS: None @@ -169,12 +217,18 @@ acpi_status acpi_ev_initialize_op_regions(void) for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { /* - * TBD: Make sure handler is the DEFAULT handler, otherwise - * _REG will have already been run. + * Make sure the installed handler is the DEFAULT handler. If not the + * default, the _REG methods will have already been run (when the + * handler was installed) */ - status = acpi_ev_execute_reg_methods(acpi_gbl_root_node, - acpi_gbl_default_address_spaces - [i]); + if (acpi_ev_has_default_handler(acpi_gbl_root_node, + acpi_gbl_default_address_spaces + [i])) { + status = + acpi_ev_execute_reg_methods(acpi_gbl_root_node, + acpi_gbl_default_address_spaces + [i]); + } } (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); @@ -235,23 +289,20 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function) * connection status 1 for connecting the handler, 0 for disconnecting * the handler (Passed as a parameter) */ - args[0] = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + args[0] = + acpi_ut_create_integer_object((u64) region_obj->region.space_id); if (!args[0]) { status = AE_NO_MEMORY; goto cleanup1; } - args[1] = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + args[1] = acpi_ut_create_integer_object((u64) function); if (!args[1]) { status = AE_NO_MEMORY; goto cleanup2; } - /* Setup the parameter objects */ - - args[0]->integer.value = region_obj->region.space_id; - args[1]->integer.value = function; - args[2] = NULL; + args[2] = NULL; /* Terminate list */ /* Execute the method, no return value */ @@ -971,8 +1022,8 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node, */ status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, - acpi_ev_install_handler, handler_obj, - NULL); + acpi_ev_install_handler, NULL, + handler_obj, NULL); unlock_and_exit: return_ACPI_STATUS(status); @@ -1008,7 +1059,7 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, */ status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, - &space_id, NULL); + NULL, &space_id, NULL); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 24afef81af39..46adfa541cbc 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -170,14 +170,12 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, /* Table not found, return an Integer=0 and AE_OK */ - ddb_handle = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + ddb_handle = acpi_ut_create_integer_object((u64) 0); if (!ddb_handle) { return_ACPI_STATUS(AE_NO_MEMORY); } - ddb_handle->integer.value = 0; *return_desc = ddb_handle; - return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index 37d0d39e60a6..51d5f224f9fa 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -167,7 +167,7 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, /* Create a new integer */ - return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + return_desc = acpi_ut_create_integer_object(result); if (!return_desc) { return_ACPI_STATUS(AE_NO_MEMORY); } @@ -177,7 +177,6 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, /* Save the Result */ - return_desc->integer.value = result; acpi_ex_truncate_for32bit_table(return_desc); *result_desc = return_desc; return_ACPI_STATUS(AE_OK); diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index 0b33d6c887b9..1588a2d660e7 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -162,13 +162,12 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, } else { /* Field will fit within an Integer (normal case) */ - buffer_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + buffer_desc = acpi_ut_create_integer_object((u64) 0); if (!buffer_desc) { return_ACPI_STATUS(AE_NO_MEMORY); } length = acpi_gbl_integer_byte_width; - buffer_desc->integer.value = 0; buffer = &buffer_desc->integer.value; } diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index 9635d21e568d..752fe48b2d20 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -100,12 +100,12 @@ acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state) /* Create a return object of type Integer */ - return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + return_desc = + acpi_ut_create_integer_object(acpi_os_get_timer()); if (!return_desc) { status = AE_NO_MEMORY; goto cleanup; } - return_desc->integer.value = acpi_os_get_timer(); break; default: /* Unknown opcode */ @@ -599,7 +599,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) switch (walk_state->opcode) { case AML_LNOT_OP: /* LNot (Operand) */ - return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + return_desc = acpi_ut_create_integer_object((u64) 0); if (!return_desc) { status = AE_NO_MEMORY; goto cleanup; @@ -702,13 +702,11 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) /* Allocate a descriptor to hold the type. */ - return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + return_desc = acpi_ut_create_integer_object((u64) type); if (!return_desc) { status = AE_NO_MEMORY; goto cleanup; } - - return_desc->integer.value = type; break; case AML_SIZE_OF_OP: /* size_of (source_object) */ @@ -777,13 +775,11 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) * Now that we have the size of the object, create a result * object to hold the value */ - return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + return_desc = acpi_ut_create_integer_object(value); if (!return_desc) { status = AE_NO_MEMORY; goto cleanup; } - - return_desc->integer.value = value; break; case AML_REF_OF_OP: /* ref_of (source_object) */ @@ -946,24 +942,24 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) * NOTE: index into a buffer is NOT a pointer to a * sub-buffer of the main buffer, it is only a pointer to a * single element (byte) of the buffer! + * + * Since we are returning the value of the buffer at the + * indexed location, we don't need to add an additional + * reference to the buffer itself. */ return_desc = - acpi_ut_create_internal_object - (ACPI_TYPE_INTEGER); + acpi_ut_create_integer_object((u64) + temp_desc-> + buffer. + pointer + [operand + [0]-> + reference. + value]); if (!return_desc) { status = AE_NO_MEMORY; goto cleanup; } - - /* - * Since we are returning the value of the buffer at the - * indexed location, we don't need to add an additional - * reference to the buffer itself. - */ - return_desc->integer.value = - temp_desc->buffer. - pointer[operand[0]->reference. - value]; break; case ACPI_TYPE_PACKAGE: diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c index ae43f7670a6c..295542e6bd51 100644 --- a/drivers/acpi/acpica/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -253,18 +253,15 @@ acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state * walk_state) } /* Create an integer for the return value */ + /* Default return value is ACPI_INTEGER_MAX if no match found */ - return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + return_desc = acpi_ut_create_integer_object(ACPI_INTEGER_MAX); if (!return_desc) { status = AE_NO_MEMORY; goto cleanup; } - /* Default return value if no match found */ - - return_desc->integer.value = ACPI_INTEGER_MAX; - /* * Examine each element until a match is found. Both match conditions * must be satisfied for a match to occur. Within the loop, diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index 2bad613db73a..2deb986861ca 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -634,8 +634,8 @@ acpi_ns_dump_objects(acpi_object_type type, (void)acpi_ns_walk_namespace(type, start_handle, max_depth, ACPI_NS_WALK_NO_UNLOCK | ACPI_NS_WALK_TEMP_NODES, - acpi_ns_dump_one_object, (void *)&info, - NULL); + acpi_ns_dump_one_object, NULL, + (void *)&info, NULL); } #endif /* ACPI_FUTURE_USAGE */ diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c index 0fe87f1aef16..36be7f0e97ec 100644 --- a/drivers/acpi/acpica/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -131,7 +131,8 @@ void acpi_ns_dump_root_devices(void) status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, sys_bus_handle, ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK, - acpi_ns_dump_one_device, NULL, NULL); + acpi_ns_dump_one_device, NULL, NULL, + NULL); } #endif diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index 846d1132feb1..f771e978c403 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -366,33 +366,49 @@ static void acpi_ns_exec_module_code(union acpi_operand_object *method_obj, struct acpi_evaluate_info *info) { - union acpi_operand_object *root_obj; + union acpi_operand_object *parent_obj; + struct acpi_namespace_node *parent_node; + acpi_object_type type; acpi_status status; ACPI_FUNCTION_TRACE(ns_exec_module_code); + /* + * Get the parent node. We cheat by using the next_object field + * of the method object descriptor. + */ + parent_node = ACPI_CAST_PTR(struct acpi_namespace_node, + method_obj->method.next_object); + type = acpi_ns_get_type(parent_node); + + /* Must clear next_object (acpi_ns_attach_object needs the field) */ + + method_obj->method.next_object = NULL; + /* Initialize the evaluation information block */ ACPI_MEMSET(info, 0, sizeof(struct acpi_evaluate_info)); - info->prefix_node = acpi_gbl_root_node; + info->prefix_node = parent_node; /* - * Get the currently attached root object. Add a reference, because the + * Get the currently attached parent object. Add a reference, because the * ref count will be decreased when the method object is installed to - * the root node. + * the parent node. */ - root_obj = acpi_ns_get_attached_object(acpi_gbl_root_node); - acpi_ut_add_reference(root_obj); + parent_obj = acpi_ns_get_attached_object(parent_node); + if (parent_obj) { + acpi_ut_add_reference(parent_obj); + } - /* Install the method (module-level code) in the root node */ + /* Install the method (module-level code) in the parent node */ - status = acpi_ns_attach_object(acpi_gbl_root_node, method_obj, + status = acpi_ns_attach_object(parent_node, method_obj, ACPI_TYPE_METHOD); if (ACPI_FAILURE(status)) { goto exit; } - /* Execute the root node as a control method */ + /* Execute the parent node as a control method */ status = acpi_ns_evaluate(info); @@ -401,15 +417,19 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj, /* Detach the temporary method object */ - acpi_ns_detach_object(acpi_gbl_root_node); + acpi_ns_detach_object(parent_node); - /* Restore the original root object */ + /* Restore the original parent object */ - status = - acpi_ns_attach_object(acpi_gbl_root_node, root_obj, - ACPI_TYPE_DEVICE); + if (parent_obj) { + status = acpi_ns_attach_object(parent_node, parent_obj, type); + } else { + parent_node->type = (u8)type; + } exit: - acpi_ut_remove_reference(root_obj); + if (parent_obj) { + acpi_ut_remove_reference(parent_obj); + } return_VOID; } diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c index 1d5b360eb25b..4f8abac231d2 100644 --- a/drivers/acpi/acpica/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -96,7 +96,7 @@ acpi_status acpi_ns_initialize_objects(void) /* Walk entire namespace from the supplied root */ status = acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, acpi_ns_init_one_object, + ACPI_UINT32_MAX, acpi_ns_init_one_object, NULL, &info, NULL); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace")); @@ -156,7 +156,8 @@ acpi_status acpi_ns_initialize_devices(void) status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, FALSE, - acpi_ns_find_ini_methods, &info, NULL); + acpi_ns_find_ini_methods, NULL, &info, + NULL); if (ACPI_FAILURE(status)) { goto error_exit; } @@ -189,7 +190,8 @@ acpi_status acpi_ns_initialize_devices(void) status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, FALSE, - acpi_ns_init_one_device, &info, NULL); + acpi_ns_init_one_device, NULL, &info, + NULL); ACPI_FREE(info.evaluate_info); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index f8427afeebdf..b05f42903c86 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -232,6 +232,12 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, status = acpi_ns_check_package(data, return_object_ptr); } + /* + * Perform additional, more complicated repairs on a per-name + * basis. + */ + status = acpi_ns_complex_repairs(data, node, status, return_object_ptr); + check_validation_status: /* * If the object validation failed or if we successfully repaired one @@ -601,7 +607,8 @@ acpi_ns_check_package(struct acpi_predefined_data *data, * there is only one entry). We may be able to repair this by * wrapping the returned Package with a new outer Package. */ - if ((*elements)->common.type != ACPI_TYPE_PACKAGE) { + if (*elements + && ((*elements)->common.type != ACPI_TYPE_PACKAGE)) { /* Create the new outer package and populate it */ @@ -673,6 +680,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, union acpi_operand_object *sub_package; union acpi_operand_object **sub_elements; acpi_status status; + u8 non_trailing_null = FALSE; u32 expected_count; u32 i; u32 j; @@ -680,6 +688,45 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, /* Validate each sub-Package in the parent Package */ for (i = 0; i < count; i++) { + /* + * Handling for NULL package elements. For now, we will simply allow + * a parent package with trailing NULL elements. This can happen if + * the package was defined to be longer than the initializer list. + * This is legal as per the ACPI specification. It is often used + * to allow for dynamic initialization of a Package. + * + * A future enhancement may be to simply truncate the package to + * remove the trailing NULL elements. + */ + if (!(*elements)) { + if (!non_trailing_null) { + + /* Ensure the remaining elements are all NULL */ + + for (j = 1; j < (count - i + 1); j++) { + if (elements[j]) { + non_trailing_null = TRUE; + } + } + + if (!non_trailing_null) { + + /* Ignore the trailing NULL elements */ + + return (AE_OK); + } + } + + /* There are trailing non-null elements, issue warning */ + + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, + data->node_flags, + "Found NULL element at package index %u", + i)); + elements++; + continue; + } + sub_package = *elements; sub_elements = sub_package->package.elements; diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index db2b2a99c3a8..d563f1a564a7 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -44,6 +44,7 @@ #include <acpi/acpi.h> #include "accommon.h" #include "acnamesp.h" +#include "acinterp.h" #include "acpredef.h" #define _COMPONENT ACPI_NAMESPACE @@ -76,7 +77,13 @@ acpi_ns_repair_object(struct acpi_predefined_data *data, union acpi_operand_object *return_object = *return_object_ptr; union acpi_operand_object *new_object; acpi_size length; + acpi_status status; + /* + * At this point, we know that the type of the returned object was not + * one of the expected types for this predefined name. Attempt to + * repair the object. Only a limited number of repairs are possible. + */ switch (return_object->common.type) { case ACPI_TYPE_BUFFER: @@ -111,43 +118,94 @@ acpi_ns_repair_object(struct acpi_predefined_data *data, */ ACPI_MEMCPY(new_object->string.pointer, return_object->buffer.pointer, length); + break; - /* - * If the original object is a package element, we need to: - * 1. Set the reference count of the new object to match the - * reference count of the old object. - * 2. Decrement the reference count of the original object. - */ - if (package_index != ACPI_NOT_PACKAGE_ELEMENT) { - new_object->common.reference_count = - return_object->common.reference_count; + case ACPI_TYPE_INTEGER: + + /* 1) Does the method/object legally return a buffer? */ + + if (expected_btypes & ACPI_RTYPE_BUFFER) { + /* + * Convert the Integer to a packed-byte buffer. _MAT needs + * this sometimes, if a read has been performed on a Field + * object that is less than or equal to the global integer + * size (32 or 64 bits). + */ + status = + acpi_ex_convert_to_buffer(return_object, + &new_object); + if (ACPI_FAILURE(status)) { + return (status); + } + } - if (return_object->common.reference_count > 1) { - return_object->common.reference_count--; + /* 2) Does the method/object legally return a string? */ + + else if (expected_btypes & ACPI_RTYPE_STRING) { + /* + * The only supported Integer-to-String conversion is to convert + * an integer of value 0 to a NULL string. The last element of + * _BIF and _BIX packages occasionally need this fix. + */ + if (return_object->integer.value != 0) { + return (AE_AML_OPERAND_TYPE); } - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, - data->node_flags, - "Converted Buffer to expected String at index %u", - package_index)); + /* Allocate a new NULL string object */ + + new_object = acpi_ut_create_string_object(0); + if (!new_object) { + return (AE_NO_MEMORY); + } } else { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, - data->node_flags, - "Converted Buffer to expected String")); + return (AE_AML_OPERAND_TYPE); } + break; - /* Delete old object, install the new return object */ + default: - acpi_ut_remove_reference(return_object); - *return_object_ptr = new_object; - data->flags |= ACPI_OBJECT_REPAIRED; - return (AE_OK); + /* We cannot repair this object */ - default: - break; + return (AE_AML_OPERAND_TYPE); + } + + /* Object was successfully repaired */ + + /* + * If the original object is a package element, we need to: + * 1. Set the reference count of the new object to match the + * reference count of the old object. + * 2. Decrement the reference count of the original object. + */ + if (package_index != ACPI_NOT_PACKAGE_ELEMENT) { + new_object->common.reference_count = + return_object->common.reference_count; + + if (return_object->common.reference_count > 1) { + return_object->common.reference_count--; + } + + ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Converted %s to expected %s at index %u", + acpi_ut_get_object_type_name + (return_object), + acpi_ut_get_object_type_name(new_object), + package_index)); + } else { + ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Converted %s to expected %s", + acpi_ut_get_object_type_name + (return_object), + acpi_ut_get_object_type_name + (new_object))); } - return (AE_AML_OPERAND_TYPE); + /* Delete old object, install the new return object */ + + acpi_ut_remove_reference(return_object); + *return_object_ptr = new_object; + data->flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); } /******************************************************************************* @@ -196,8 +254,8 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data, *obj_desc_ptr = pkg_obj_desc; data->flags |= ACPI_OBJECT_REPAIRED; - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, - "Incorrectly formed Package, attempting repair")); + ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Repaired Incorrectly formed Package")); return (AE_OK); } diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c new file mode 100644 index 000000000000..d07b68613818 --- /dev/null +++ b/drivers/acpi/acpica/nsrepair2.c @@ -0,0 +1,540 @@ +/****************************************************************************** + * + * Module Name: nsrepair2 - Repair for objects returned by specific + * predefined methods + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2009, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsrepair2") + +/* + * Information structure and handler for ACPI predefined names that can + * be repaired on a per-name basis. + */ +typedef +acpi_status(*acpi_repair_function) (struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + +typedef struct acpi_repair_info { + char name[ACPI_NAME_SIZE]; + acpi_repair_function repair_function; + +} acpi_repair_info; + +/* Local prototypes */ + +static const struct acpi_repair_info *acpi_ns_match_repairable_name(struct + acpi_namespace_node + *node); + +static acpi_status +acpi_ns_repair_ALR(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_PSS(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_TSS(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_check_sorted_list(struct acpi_predefined_data *data, + union acpi_operand_object *return_object, + u32 expected_count, + u32 sort_index, + u8 sort_direction, char *sort_key_name); + +static acpi_status +acpi_ns_remove_null_elements(union acpi_operand_object *package); + +static acpi_status +acpi_ns_sort_list(union acpi_operand_object **elements, + u32 count, u32 index, u8 sort_direction); + +/* Values for sort_direction above */ + +#define ACPI_SORT_ASCENDING 0 +#define ACPI_SORT_DESCENDING 1 + +/* + * This table contains the names of the predefined methods for which we can + * perform more complex repairs. + * + * _ALR: Sort the list ascending by ambient_illuminance if necessary + * _PSS: Sort the list descending by Power if necessary + * _TSS: Sort the list descending by Power if necessary + */ +static const struct acpi_repair_info acpi_ns_repairable_names[] = { + {"_ALR", acpi_ns_repair_ALR}, + {"_PSS", acpi_ns_repair_PSS}, + {"_TSS", acpi_ns_repair_TSS}, + {{0, 0, 0, 0}, NULL} /* Table terminator */ +}; + +/****************************************************************************** + * + * FUNCTION: acpi_ns_complex_repairs + * + * PARAMETERS: Data - Pointer to validation data structure + * Node - Namespace node for the method/object + * validate_status - Original status of earlier validation + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if repair was successful. If name is not + * matched, validate_status is returned. + * + * DESCRIPTION: Attempt to repair/convert a return object of a type that was + * not expected. + * + *****************************************************************************/ + +acpi_status +acpi_ns_complex_repairs(struct acpi_predefined_data *data, + struct acpi_namespace_node *node, + acpi_status validate_status, + union acpi_operand_object **return_object_ptr) +{ + const struct acpi_repair_info *predefined; + acpi_status status; + + /* Check if this name is in the list of repairable names */ + + predefined = acpi_ns_match_repairable_name(node); + if (!predefined) { + return (validate_status); + } + + status = predefined->repair_function(data, return_object_ptr); + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_match_repairable_name + * + * PARAMETERS: Node - Namespace node for the method/object + * + * RETURN: Pointer to entry in repair table. NULL indicates not found. + * + * DESCRIPTION: Check an object name against the repairable object list. + * + *****************************************************************************/ + +static const struct acpi_repair_info *acpi_ns_match_repairable_name(struct + acpi_namespace_node + *node) +{ + const struct acpi_repair_info *this_name; + + /* Search info table for a repairable predefined method/object name */ + + this_name = acpi_ns_repairable_names; + while (this_name->repair_function) { + if (ACPI_COMPARE_NAME(node->name.ascii, this_name->name)) { + return (this_name); + } + this_name++; + } + + return (NULL); /* Not found */ +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_ALR + * + * PARAMETERS: Data - Pointer to validation data structure + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _ALR object. If necessary, sort the object list + * ascending by the ambient illuminance values. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_ALR(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + acpi_status status; + + status = acpi_ns_check_sorted_list(data, return_object, 2, 1, + ACPI_SORT_ASCENDING, + "AmbientIlluminance"); + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_TSS + * + * PARAMETERS: Data - Pointer to validation data structure + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list + * descending by the power dissipation values. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_TSS(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + acpi_status status; + + status = acpi_ns_check_sorted_list(data, return_object, 5, 1, + ACPI_SORT_DESCENDING, + "PowerDissipation"); + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_PSS + * + * PARAMETERS: Data - Pointer to validation data structure + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _PSS object. If necessary, sort the object list + * by the CPU frequencies. Check that the power dissipation values + * are all proportional to CPU frequency (i.e., sorting by + * frequency should be the same as sorting by power.) + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_PSS(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object **outer_elements; + u32 outer_element_count; + union acpi_operand_object **elements; + union acpi_operand_object *obj_desc; + u32 previous_value; + acpi_status status; + u32 i; + + /* + * Entries (sub-packages) in the _PSS Package must be sorted by power + * dissipation, in descending order. If it appears that the list is + * incorrectly sorted, sort it. We sort by cpu_frequency, since this + * should be proportional to the power. + */ + status = acpi_ns_check_sorted_list(data, return_object, 6, 0, + ACPI_SORT_DESCENDING, + "CpuFrequency"); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * We now know the list is correctly sorted by CPU frequency. Check if + * the power dissipation values are proportional. + */ + previous_value = ACPI_UINT32_MAX; + outer_elements = return_object->package.elements; + outer_element_count = return_object->package.count; + + for (i = 0; i < outer_element_count; i++) { + elements = (*outer_elements)->package.elements; + obj_desc = elements[1]; /* Index1 = power_dissipation */ + + if ((u32) obj_desc->integer.value > previous_value) { + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, + data->node_flags, + "SubPackage[%u,%u] - suspicious power dissipation values", + i - 1, i)); + } + + previous_value = (u32) obj_desc->integer.value; + outer_elements++; + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_check_sorted_list + * + * PARAMETERS: Data - Pointer to validation data structure + * return_object - Pointer to the top-level returned object + * expected_count - Minimum length of each sub-package + * sort_index - Sub-package entry to sort on + * sort_direction - Ascending or descending + * sort_key_name - Name of the sort_index field + * + * RETURN: Status. AE_OK if the list is valid and is sorted correctly or + * has been repaired by sorting the list. + * + * DESCRIPTION: Check if the package list is valid and sorted correctly by the + * sort_index. If not, then sort the list. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_check_sorted_list(struct acpi_predefined_data *data, + union acpi_operand_object *return_object, + u32 expected_count, + u32 sort_index, + u8 sort_direction, char *sort_key_name) +{ + u32 outer_element_count; + union acpi_operand_object **outer_elements; + union acpi_operand_object **elements; + union acpi_operand_object *obj_desc; + u32 i; + u32 previous_value; + acpi_status status; + + /* The top-level object must be a package */ + + if (return_object->common.type != ACPI_TYPE_PACKAGE) { + return (AE_AML_OPERAND_TYPE); + } + + /* + * Detect any NULL package elements and remove them from the + * package. + * + * TBD: We may want to do this for all predefined names that + * return a variable-length package of packages. + */ + status = acpi_ns_remove_null_elements(return_object); + if (status == AE_NULL_ENTRY) { + ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "NULL elements removed from package")); + + /* Exit if package is now zero length */ + + if (!return_object->package.count) { + return (AE_NULL_ENTRY); + } + } + + outer_elements = return_object->package.elements; + outer_element_count = return_object->package.count; + if (!outer_element_count) { + return (AE_AML_PACKAGE_LIMIT); + } + + previous_value = 0; + if (sort_direction == ACPI_SORT_DESCENDING) { + previous_value = ACPI_UINT32_MAX; + } + + /* Examine each subpackage */ + + for (i = 0; i < outer_element_count; i++) { + + /* Each element of the top-level package must also be a package */ + + if ((*outer_elements)->common.type != ACPI_TYPE_PACKAGE) { + return (AE_AML_OPERAND_TYPE); + } + + /* Each sub-package must have the minimum length */ + + if ((*outer_elements)->package.count < expected_count) { + return (AE_AML_PACKAGE_LIMIT); + } + + elements = (*outer_elements)->package.elements; + obj_desc = elements[sort_index]; + + if (obj_desc->common.type != ACPI_TYPE_INTEGER) { + return (AE_AML_OPERAND_TYPE); + } + + /* + * The list must be sorted in the specified order. If we detect a + * discrepancy, issue a warning and sort the entire list + */ + if (((sort_direction == ACPI_SORT_ASCENDING) && + (obj_desc->integer.value < previous_value)) || + ((sort_direction == ACPI_SORT_DESCENDING) && + (obj_desc->integer.value > previous_value))) { + status = + acpi_ns_sort_list(return_object->package.elements, + outer_element_count, sort_index, + sort_direction); + if (ACPI_FAILURE(status)) { + return (status); + } + + data->flags |= ACPI_OBJECT_REPAIRED; + + ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, + data->node_flags, + "Repaired unsorted list - now sorted by %s", + sort_key_name)); + return (AE_OK); + } + + previous_value = (u32) obj_desc->integer.value; + outer_elements++; + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_remove_null_elements + * + * PARAMETERS: obj_desc - A Package object + * + * RETURN: Status. AE_NULL_ENTRY means that one or more elements were + * removed. + * + * DESCRIPTION: Remove all NULL package elements and update the package count. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc) +{ + union acpi_operand_object **source; + union acpi_operand_object **dest; + acpi_status status = AE_OK; + u32 count; + u32 new_count; + u32 i; + + count = obj_desc->package.count; + new_count = count; + + source = obj_desc->package.elements; + dest = source; + + /* Examine all elements of the package object */ + + for (i = 0; i < count; i++) { + if (!*source) { + status = AE_NULL_ENTRY; + new_count--; + } else { + *dest = *source; + dest++; + } + source++; + } + + if (status == AE_NULL_ENTRY) { + + /* NULL terminate list and update the package count */ + + *dest = NULL; + obj_desc->package.count = new_count; + } + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_sort_list + * + * PARAMETERS: Elements - Package object element list + * Count - Element count for above + * Index - Sort by which package element + * sort_direction - Ascending or Descending sort + * + * RETURN: Status + * + * DESCRIPTION: Sort the objects that are in a package element list. + * + * NOTE: Assumes that all NULL elements have been removed from the package. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_sort_list(union acpi_operand_object **elements, + u32 count, u32 index, u8 sort_direction) +{ + union acpi_operand_object *obj_desc1; + union acpi_operand_object *obj_desc2; + union acpi_operand_object *temp_obj; + u32 i; + u32 j; + + /* Simple bubble sort */ + + for (i = 1; i < count; i++) { + for (j = (count - 1); j >= i; j--) { + obj_desc1 = elements[j - 1]->package.elements[index]; + obj_desc2 = elements[j]->package.elements[index]; + + if (((sort_direction == ACPI_SORT_ASCENDING) && + (obj_desc1->integer.value > + obj_desc2->integer.value)) + || ((sort_direction == ACPI_SORT_DESCENDING) + && (obj_desc1->integer.value < + obj_desc2->integer.value))) { + temp_obj = elements[j - 1]; + elements[j - 1] = elements[j]; + elements[j] = temp_obj; + } + } + } + + return (AE_OK); +} diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index 35539df5c75d..d7e6b52b4482 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -165,24 +165,27 @@ struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type, * max_depth - Depth to which search is to reach * Flags - Whether to unlock the NS before invoking * the callback routine - * user_function - Called when an object of "Type" is found - * Context - Passed to user function - * return_value - from the user_function if terminated early. - * Otherwise, returns NULL. + * pre_order_visit - Called during tree pre-order visit + * when an object of "Type" is found + * post_order_visit - Called during tree post-order visit + * when an object of "Type" is found + * Context - Passed to user function(s) above + * return_value - from the user_function if terminated + * early. Otherwise, returns NULL. * RETURNS: Status * * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, * starting (and ending) at the node specified by start_handle. - * The user_function is called whenever a node that matches - * the type parameter is found. If the user function returns + * The callback function is called whenever a node that matches + * the type parameter is found. If the callback function returns * a non-zero value, the search is terminated immediately and * this value is returned to the caller. * * The point of this procedure is to provide a generic namespace * walk routine that can be called from multiple places to - * provide multiple services; the User Function can be tailored - * to each task, whether it is a print function, a compare - * function, etc. + * provide multiple services; the callback function(s) can be + * tailored to each task, whether it is a print function, + * a compare function, etc. * ******************************************************************************/ @@ -191,7 +194,8 @@ acpi_ns_walk_namespace(acpi_object_type type, acpi_handle start_node, u32 max_depth, u32 flags, - acpi_walk_callback user_function, + acpi_walk_callback pre_order_visit, + acpi_walk_callback post_order_visit, void *context, void **return_value) { acpi_status status; @@ -200,6 +204,7 @@ acpi_ns_walk_namespace(acpi_object_type type, struct acpi_namespace_node *parent_node; acpi_object_type child_type; u32 level; + u8 node_previously_visited = FALSE; ACPI_FUNCTION_TRACE(ns_walk_namespace); @@ -212,7 +217,7 @@ acpi_ns_walk_namespace(acpi_object_type type, /* Null child means "get first node" */ parent_node = start_node; - child_node = NULL; + child_node = acpi_ns_get_next_node(parent_node, NULL); child_type = ACPI_TYPE_ANY; level = 1; @@ -221,102 +226,129 @@ acpi_ns_walk_namespace(acpi_object_type type, * started. When Level is zero, the loop is done because we have * bubbled up to (and passed) the original parent handle (start_entry) */ - while (level > 0) { + while (level > 0 && child_node) { + status = AE_OK; - /* Get the next node in this scope. Null if not found */ + /* Found next child, get the type if we are not searching for ANY */ - status = AE_OK; - child_node = acpi_ns_get_next_node(parent_node, child_node); - if (child_node) { + if (type != ACPI_TYPE_ANY) { + child_type = child_node->type; + } - /* Found next child, get the type if we are not searching for ANY */ + /* + * Ignore all temporary namespace nodes (created during control + * method execution) unless told otherwise. These temporary nodes + * can cause a race condition because they can be deleted during + * the execution of the user function (if the namespace is + * unlocked before invocation of the user function.) Only the + * debugger namespace dump will examine the temporary nodes. + */ + if ((child_node->flags & ANOBJ_TEMPORARY) && + !(flags & ACPI_NS_WALK_TEMP_NODES)) { + status = AE_CTRL_DEPTH; + } - if (type != ACPI_TYPE_ANY) { - child_type = child_node->type; - } + /* Type must match requested type */ + else if (child_type == type) { /* - * Ignore all temporary namespace nodes (created during control - * method execution) unless told otherwise. These temporary nodes - * can cause a race condition because they can be deleted during - * the execution of the user function (if the namespace is - * unlocked before invocation of the user function.) Only the - * debugger namespace dump will examine the temporary nodes. + * Found a matching node, invoke the user callback function. + * Unlock the namespace if flag is set. */ - if ((child_node->flags & ANOBJ_TEMPORARY) && - !(flags & ACPI_NS_WALK_TEMP_NODES)) { - status = AE_CTRL_DEPTH; + if (flags & ACPI_NS_WALK_UNLOCK) { + mutex_status = + acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(mutex_status)) { + return_ACPI_STATUS(mutex_status); + } } - /* Type must match requested type */ - - else if (child_type == type) { - /* - * Found a matching node, invoke the user callback function. - * Unlock the namespace if flag is set. - */ - if (flags & ACPI_NS_WALK_UNLOCK) { - mutex_status = - acpi_ut_release_mutex - (ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(mutex_status)) { - return_ACPI_STATUS - (mutex_status); - } + /* + * Invoke the user function, either pre-order or post-order + * or both. + */ + if (!node_previously_visited) { + if (pre_order_visit) { + status = + pre_order_visit(child_node, level, + context, + return_value); } + } else { + if (post_order_visit) { + status = + post_order_visit(child_node, level, + context, + return_value); + } + } - status = - user_function(child_node, level, context, - return_value); - - if (flags & ACPI_NS_WALK_UNLOCK) { - mutex_status = - acpi_ut_acquire_mutex - (ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(mutex_status)) { - return_ACPI_STATUS - (mutex_status); - } + if (flags & ACPI_NS_WALK_UNLOCK) { + mutex_status = + acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(mutex_status)) { + return_ACPI_STATUS(mutex_status); } + } - switch (status) { - case AE_OK: - case AE_CTRL_DEPTH: + switch (status) { + case AE_OK: + case AE_CTRL_DEPTH: - /* Just keep going */ - break; + /* Just keep going */ + break; - case AE_CTRL_TERMINATE: + case AE_CTRL_TERMINATE: - /* Exit now, with OK status */ + /* Exit now, with OK status */ - return_ACPI_STATUS(AE_OK); + return_ACPI_STATUS(AE_OK); - default: + default: - /* All others are valid exceptions */ + /* All others are valid exceptions */ - return_ACPI_STATUS(status); - } + return_ACPI_STATUS(status); + } + } + + /* + * Depth first search: Attempt to go down another level in the + * namespace if we are allowed to. Don't go any further if we have + * reached the caller specified maximum depth or if the user + * function has specified that the maximum depth has been reached. + */ + if (!node_previously_visited && + (level < max_depth) && (status != AE_CTRL_DEPTH)) { + if (child_node->child) { + + /* There is at least one child of this node, visit it */ + + level++; + parent_node = child_node; + child_node = + acpi_ns_get_next_node(parent_node, NULL); + continue; } + } - /* - * Depth first search: Attempt to go down another level in the - * namespace if we are allowed to. Don't go any further if we have - * reached the caller specified maximum depth or if the user - * function has specified that the maximum depth has been reached. - */ - if ((level < max_depth) && (status != AE_CTRL_DEPTH)) { - if (child_node->child) { + /* No more children, re-visit this node */ - /* There is at least one child of this node, visit it */ + if (!node_previously_visited) { + node_previously_visited = TRUE; + continue; + } - level++; - parent_node = child_node; - child_node = NULL; - } - } - } else { + /* No more children, visit peers */ + + child_node = acpi_ns_get_next_node(parent_node, child_node); + if (child_node) { + node_previously_visited = FALSE; + } + + /* No peers, re-visit parent */ + + else { /* * No more children of this node (acpi_ns_get_next_node failed), go * back upwards in the namespace tree to the node's parent. @@ -324,6 +356,8 @@ acpi_ns_walk_namespace(acpi_object_type type, level--; child_node = parent_node; parent_node = acpi_ns_get_parent_node(parent_node); + + node_previously_visited = TRUE; } } diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index 4929dbdbc8f0..f2bd1da77001 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -433,8 +433,11 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info) * PARAMETERS: Type - acpi_object_type to search for * start_object - Handle in namespace where search begins * max_depth - Depth to which search is to reach - * user_function - Called when an object of "Type" is found - * Context - Passed to user function + * pre_order_visit - Called during tree pre-order visit + * when an object of "Type" is found + * post_order_visit - Called during tree post-order visit + * when an object of "Type" is found + * Context - Passed to user function(s) above * return_value - Location where return value of * user_function is put if terminated early * @@ -443,16 +446,16 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info) * * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, * starting (and ending) at the object specified by start_handle. - * The user_function is called whenever an object that matches - * the type parameter is found. If the user function returns + * The callback function is called whenever an object that matches + * the type parameter is found. If the callback function returns * a non-zero value, the search is terminated immediately and this * value is returned to the caller. * * The point of this procedure is to provide a generic namespace * walk routine that can be called from multiple places to - * provide multiple services; the User Function can be tailored - * to each task, whether it is a print function, a compare - * function, etc. + * provide multiple services; the callback function(s) can be + * tailored to each task, whether it is a print function, + * a compare function, etc. * ******************************************************************************/ @@ -460,7 +463,8 @@ acpi_status acpi_walk_namespace(acpi_object_type type, acpi_handle start_object, u32 max_depth, - acpi_walk_callback user_function, + acpi_walk_callback pre_order_visit, + acpi_walk_callback post_order_visit, void *context, void **return_value) { acpi_status status; @@ -469,7 +473,8 @@ acpi_walk_namespace(acpi_object_type type, /* Parameter validation */ - if ((type > ACPI_TYPE_LOCAL_MAX) || (!max_depth) || (!user_function)) { + if ((type > ACPI_TYPE_LOCAL_MAX) || + (!max_depth) || (!pre_order_visit && !post_order_visit)) { return_ACPI_STATUS(AE_BAD_PARAMETER); } @@ -501,8 +506,9 @@ acpi_walk_namespace(acpi_object_type type, } status = acpi_ns_walk_namespace(type, start_object, max_depth, - ACPI_NS_WALK_UNLOCK, user_function, - context, return_value); + ACPI_NS_WALK_UNLOCK, pre_order_visit, + post_order_visit, context, + return_value); (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); @@ -681,8 +687,8 @@ acpi_get_devices(const char *HID, status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, - acpi_ns_get_device_callback, &info, - return_value); + acpi_ns_get_device_callback, NULL, + &info, return_value); (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_ACPI_STATUS(status); diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index cd7995b3aed4..0988e4a8901d 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -87,7 +87,8 @@ acpi_ps_complete_final_op(struct acpi_walk_state *walk_state, union acpi_parse_object *op, acpi_status status); static void -acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id); +acpi_ps_link_module_code(union acpi_parse_object *parent_op, + u8 *aml_start, u32 aml_length, acpi_owner_id owner_id); /******************************************************************************* * @@ -479,11 +480,14 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, */ if (walk_state->pass_number == ACPI_IMODE_LOAD_PASS1) { - acpi_ps_link_module_code(aml_op_start, - walk_state-> + acpi_ps_link_module_code(op->common. + parent, + aml_op_start, + (u32) + (walk_state-> parser_state. pkg_end - - aml_op_start, + aml_op_start), walk_state-> owner_id); } @@ -598,7 +602,8 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, * * FUNCTION: acpi_ps_link_module_code * - * PARAMETERS: aml_start - Pointer to the AML + * PARAMETERS: parent_op - Parent parser op + * aml_start - Pointer to the AML * aml_length - Length of executable AML * owner_id - owner_id of module level code * @@ -611,11 +616,13 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, ******************************************************************************/ static void -acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id) +acpi_ps_link_module_code(union acpi_parse_object *parent_op, + u8 *aml_start, u32 aml_length, acpi_owner_id owner_id) { union acpi_operand_object *prev; union acpi_operand_object *next; union acpi_operand_object *method_obj; + struct acpi_namespace_node *parent_node; /* Get the tail of the list */ @@ -639,11 +646,24 @@ acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id) return; } + if (parent_op->common.node) { + parent_node = parent_op->common.node; + } else { + parent_node = acpi_gbl_root_node; + } + method_obj->method.aml_start = aml_start; method_obj->method.aml_length = aml_length; method_obj->method.owner_id = owner_id; method_obj->method.flags |= AOPOBJ_MODULE_LEVEL; + /* + * Save the parent node in next_object. This is cheating, but we + * don't want to expand the method object. + */ + method_obj->method.next_object = + ACPI_CAST_PTR(union acpi_operand_object, parent_node); + if (!prev) { acpi_gbl_module_code_list = method_obj; } else { diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index 70838e9b608c..4df8f139026c 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -610,17 +610,13 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state) implicit_return_obj) { previous_walk_state-> implicit_return_obj = - acpi_ut_create_internal_object - (ACPI_TYPE_INTEGER); + acpi_ut_create_integer_object + ((u64) 0); if (!previous_walk_state-> implicit_return_obj) { return_ACPI_STATUS (AE_NO_MEMORY); } - - previous_walk_state-> - implicit_return_obj-> - integer.value = 0; } /* Restart the calling control method */ diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index dd9731c29a79..12934ad6da8e 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -306,14 +306,12 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) */ if (acpi_gbl_enable_interpreter_slack) { walk_state->implicit_return_obj = - acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + acpi_ut_create_integer_object((u64) 0); if (!walk_state->implicit_return_obj) { status = AE_NO_MEMORY; acpi_ds_delete_walk_state(walk_state); goto cleanup; } - - walk_state->implicit_return_obj->integer.value = 0; } /* Parse the AML */ diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index 61f6315fce9f..6c6a5137b728 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -1161,3 +1161,45 @@ acpi_ut_predefined_warning(const char *module_name, ACPI_COMMON_MSG_SUFFIX; va_end(args); } + +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_info + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * Format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Info messages for the predefined validation module. Messages + * are only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_info(const char *module_name, + u32 line_number, + char *pathname, u8 node_flags, const char *format, ...) +{ + va_list args; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf("ACPI Info for %s: ", pathname); + + va_start(args, format); + acpi_os_vprintf(format, args); + ACPI_COMMON_MSG_SUFFIX; + va_end(args); +} diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index 0207b625274a..42e658b543f1 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -190,6 +190,35 @@ union acpi_operand_object *acpi_ut_create_package_object(u32 count) /******************************************************************************* * + * FUNCTION: acpi_ut_create_integer_object + * + * PARAMETERS: initial_value - Initial value for the integer + * + * RETURN: Pointer to a new Integer object, null on failure + * + * DESCRIPTION: Create an initialized integer object + * + ******************************************************************************/ + +union acpi_operand_object *acpi_ut_create_integer_object(u64 initial_value) +{ + union acpi_operand_object *integer_desc; + + ACPI_FUNCTION_TRACE(ut_create_integer_object); + + /* Create and initialize a new integer object */ + + integer_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!integer_desc) { + return_PTR(NULL); + } + + integer_desc->integer.value = initial_value; + return_PTR(integer_desc); +} + +/******************************************************************************* + * * FUNCTION: acpi_ut_create_buffer_object * * PARAMETERS: buffer_size - Size of buffer to be created diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 642bb305cb65..5faf6c21257d 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -258,7 +258,7 @@ static int __init acpi_container_init(void) acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - container_walk_namespace_cb, &action, NULL); + container_walk_namespace_cb, NULL, &action, NULL); return (0); } @@ -271,7 +271,7 @@ static void __exit acpi_container_exit(void) acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - container_walk_namespace_cb, &action, NULL); + container_walk_namespace_cb, NULL, &action, NULL); acpi_bus_unregister_driver(&acpi_container_driver); diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 7338b6a3e049..30be3c148f7e 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -1030,8 +1030,8 @@ static int dock_add(acpi_handle handle) /* Find dependent devices */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock_devices, dock_station, - NULL); + ACPI_UINT32_MAX, find_dock_devices, NULL, + dock_station, NULL); /* add the dock station as a device dependent on itself */ dd = alloc_dock_dependent_device(handle); @@ -1127,11 +1127,11 @@ static int __init dock_init(void) /* look for a dock station */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock, NULL, NULL); + ACPI_UINT32_MAX, find_dock, NULL, NULL, NULL); /* look for bay */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_bay, NULL, NULL); + ACPI_UINT32_MAX, find_bay, NULL, NULL, NULL); if (!dock_station_count) { printk(KERN_INFO PREFIX "No dock devices found.\n"); return 0; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index baef28c1e630..75b147f5c8fd 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -820,7 +820,7 @@ static int acpi_ec_add(struct acpi_device *device) /* Find and register all query methods */ acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, - acpi_ec_register_query_methods, ec, NULL); + acpi_ec_register_query_methods, NULL, ec, NULL); if (!first_ec) first_ec = ec; diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index c6645f26224b..4c8fcff662cf 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -113,7 +113,7 @@ acpi_handle acpi_get_child(acpi_handle parent, acpi_integer address) if (!parent) return NULL; acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, - 1, do_acpi_find_child, &find, NULL); + 1, do_acpi_find_child, NULL, &find, NULL); return find.handle; } diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 202dd0c976a3..2be2fb66204e 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -283,22 +283,24 @@ acpi_table_parse_srat(enum acpi_srat_type id, int __init acpi_numa_init(void) { + int ret = 0; + /* SRAT: Static Resource Affinity Table */ if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { acpi_table_parse_srat(ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY, acpi_parse_x2apic_affinity, NR_CPUS); acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY, acpi_parse_processor_affinity, NR_CPUS); - acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY, - acpi_parse_memory_affinity, - NR_NODE_MEMBLKS); + ret = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY, + acpi_parse_memory_affinity, + NR_NODE_MEMBLKS); } /* SLIT: System Locality Information Table */ acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit); acpi_numa_arch_fixup(); - return 0; + return ret; } int acpi_get_pxm(acpi_handle h) diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index 45da2bae36c8..11f219743204 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -219,12 +219,12 @@ walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) dbg("p2p bridge walk, pci_bus = %x\n", dev->subordinate->number); status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - user_function, &child_context, NULL); + user_function, NULL, &child_context, NULL); if (ACPI_FAILURE(status)) goto out; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - walk_p2p_bridge, &child_context, NULL); + walk_p2p_bridge, NULL, &child_context, NULL); out: pci_dev_put(dev); return AE_OK; @@ -277,12 +277,12 @@ walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function) dbg("root bridge walk, pci_bus = %x\n", pci_bus->number); status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - user_function, &context, NULL); + user_function, NULL, &context, NULL); if (ACPI_FAILURE(status)) return status; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - walk_p2p_bridge, &context, NULL); + walk_p2p_bridge, NULL, &context, NULL); if (ACPI_FAILURE(status)) err("%s: walk_p2p_bridge failure - %d\n", __func__, status); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index ec742a4e5635..cb4283f5a79d 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -1102,7 +1102,7 @@ void acpi_processor_install_hotplug_notify(void) acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - processor_walk_namespace_cb, &action, NULL); + processor_walk_namespace_cb, NULL, &action, NULL); #endif register_hotcpu_notifier(&acpi_cpu_notifier); } @@ -1115,7 +1115,7 @@ void acpi_processor_uninstall_hotplug_notify(void) acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - processor_walk_namespace_cb, &action, NULL); + processor_walk_namespace_cb, NULL, &action, NULL); #endif unregister_hotcpu_notifier(&acpi_cpu_notifier); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 14a7481c97d7..ff9f6226085d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1332,7 +1332,7 @@ static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, status = acpi_bus_check_add(handle, 0, ops, &device); if (ACPI_SUCCESS(status)) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, - acpi_bus_check_add, ops, &device); + acpi_bus_check_add, NULL, ops, &device); if (child) *child = device; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 575593a8b4e6..fc2f26b9b407 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -7,7 +7,7 @@ * video_detect.c: * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c * There a Linux specific (Spec does not provide a HID for video devices) is - * assinged + * assigned * * After PCI devices are glued with ACPI devices * acpi_get_pci_dev() can be called to identify ACPI graphics @@ -83,16 +83,16 @@ long acpi_is_video_device(struct acpi_device *device) if (!device) return 0; - /* Does this device able to support video switching ? */ + /* Is this device able to support video switching ? */ if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) || ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; - /* Does this device able to retrieve a video ROM ? */ + /* Is this device able to retrieve a video ROM ? */ if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy))) video_caps |= ACPI_VIDEO_ROM_AVAILABLE; - /* Does this device able to configure which video head to be POSTed ? */ + /* Is this device able to configure which video head to be POSTed ? */ if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) && ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) && ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy))) @@ -101,7 +101,7 @@ long acpi_is_video_device(struct acpi_device *device) /* Only check for backlight functionality if one of the above hit. */ if (video_caps) acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle, - ACPI_UINT32_MAX, acpi_backlight_cap_match, + ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL, &video_caps, NULL); return video_caps; @@ -137,7 +137,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv) * * if NULL is passed as argument all ACPI devices are enumerated and * all graphics capabilities of physically present devices are - * summerized and returned. This is cached and done only once. + * summarized and returned. This is cached and done only once. */ long acpi_video_get_capabilities(acpi_handle graphics_handle) { @@ -151,7 +151,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle) if (!graphics_handle) { /* Only do the global walk through all graphics devices once */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_video, + ACPI_UINT32_MAX, find_video, NULL, &caps, NULL); /* There might be boot param flags set already... */ acpi_video_support |= caps; @@ -173,7 +173,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle) return 0; } acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle, - ACPI_UINT32_MAX, find_video, + ACPI_UINT32_MAX, find_video, NULL, &caps, NULL); } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n", diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index f2df6e2a224c..676f08b004b3 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -3,7 +3,7 @@ # menuconfig ATA - tristate "Serial ATA (prod) and Parallel ATA (experimental) drivers" + tristate "Serial ATA and Parallel ATA drivers" depends on HAS_IOMEM depends on BLOCK depends on !(M32R || M68K) || BROKEN @@ -374,8 +374,8 @@ config PATA_HPT366 If unsure, say N. config PATA_HPT37X - tristate "HPT 370/370A/371/372/374/302 PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "HPT 370/370A/371/372/374/302 PATA support" + depends on PCI help This option enables support for the majority of the later HPT PATA controllers via the new ATA layer. @@ -383,8 +383,8 @@ config PATA_HPT37X If unsure, say N. config PATA_HPT3X2N - tristate "HPT 372N/302N PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "HPT 372N/302N PATA support" + depends on PCI help This option enables support for the N variant HPT PATA controllers via the new ATA layer @@ -401,7 +401,7 @@ config PATA_HPT3X3 If unsure, say N. config PATA_HPT3X3_DMA - bool "HPT 343/363 DMA support (Experimental)" + bool "HPT 343/363 DMA support" depends on PATA_HPT3X3 help This option enables DMA support for the HPT343/363 @@ -510,8 +510,8 @@ config PATA_NETCELL If unsure, say N. config PATA_NINJA32 - tristate "Ninja32/Delkin Cardbus ATA support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "Ninja32/Delkin Cardbus ATA support" + depends on PCI help This option enables support for the Ninja32, Delkin and possibly other brands of Cardbus ATA adapter @@ -573,6 +573,14 @@ config PATA_PCMCIA If unsure, say N. +config PATA_PDC2027X + tristate "Promise PATA 2027x support" + depends on PCI + help + This option enables support for Promise PATA pdc20268 to pdc20277 host adapters. + + If unsure, say N. + config PATA_PDC_OLD tristate "Older Promise PATA controller support" depends on PCI @@ -643,14 +651,6 @@ config PATA_SERVERWORKS If unsure, say N. -config PATA_PDC2027X - tristate "Promise PATA 2027x support" - depends on PCI - help - This option enables support for Promise PATA pdc20268 to pdc20277 host adapters. - - If unsure, say N. - config PATA_SIL680 tristate "CMD / Silicon Image 680 PATA support" depends on PCI @@ -667,6 +667,15 @@ config PATA_SIS If unsure, say N. +config PATA_TOSHIBA + tristate "Toshiba Piccolo support (Experimental)" + depends on PCI && EXPERIMENTAL + help + Support for the Toshiba Piccolo controllers. Currently only the + primary channel is supported by this driver. + + If unsure, say N. + config PATA_VIA tristate "VIA PATA support" depends on PCI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 01e126f343b3..d909435e9d81 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o obj-$(CONFIG_PATA_SC1200) += pata_sc1200.o obj-$(CONFIG_PATA_SERVERWORKS) += pata_serverworks.o obj-$(CONFIG_PATA_SIL680) += pata_sil680.o +obj-$(CONFIG_PATA_TOSHIBA) += pata_piccolo.o obj-$(CONFIG_PATA_VIA) += pata_via.o obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index a3241a1a710b..b8bea100a160 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -113,6 +113,7 @@ enum { board_ahci_mcp65 = 6, board_ahci_nopmp = 7, board_ahci_yesncq = 8, + board_ahci_nosntf = 9, /* global controller registers */ HOST_CAP = 0x00, /* host capabilities */ @@ -235,6 +236,7 @@ enum { AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */ AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as link offline */ + AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */ /* ap->flags bits */ @@ -508,7 +510,7 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - /* board_ahci_yesncq */ + [board_ahci_yesncq] = { AHCI_HFLAGS (AHCI_HFLAG_YES_NCQ), .flags = AHCI_FLAG_COMMON, @@ -516,6 +518,14 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, + [board_ahci_nosntf] = + { + AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF), + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }, }; static const struct pci_device_id ahci_pci_tbl[] = { @@ -531,7 +541,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */ { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */ { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */ - { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* ICH8 */ + { PCI_VDEVICE(INTEL, 0x2822), board_ahci_nosntf }, /* ICH8 */ { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */ { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */ { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */ @@ -849,6 +859,12 @@ static void ahci_save_initial_config(struct pci_dev *pdev, cap &= ~HOST_CAP_PMP; } + if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) { + dev_printk(KERN_INFO, &pdev->dev, + "controller can't do SNTF, turning off CAP_SNTF\n"); + cap &= ~HOST_CAP_SNTF; + } + if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361 && port_map != 1) { dev_printk(KERN_INFO, &pdev->dev, @@ -2988,6 +3004,14 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (pdev->vendor == PCI_VENDOR_ID_MARVELL && !marvell_enable) return -ENODEV; + /* Promise's PDC42819 is a SAS/SATA controller that has an AHCI mode. + * At the moment, we can only use the AHCI mode. Let the users know + * that for SAS drives they're out of luck. + */ + if (pdev->vendor == PCI_VENDOR_ID_PROMISE) + dev_printk(KERN_INFO, &pdev->dev, "PDC42819 " + "can only drive SATA devices with this driver\n"); + /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c index ecfd22b4f1ce..12e26c3c68e3 100644 --- a/drivers/ata/ata_generic.c +++ b/drivers/ata/ata_generic.c @@ -168,9 +168,12 @@ static struct pci_device_id ata_generic[] = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561), }, { PCI_DEVICE(PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558), }, { PCI_DEVICE(PCI_VENDOR_ID_CENATEK,PCI_DEVICE_ID_CENATEK_IDE), }, - { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO), }, +#if !defined(CONFIG_PATA_TOSHIBA) && !defined(CONFIG_PATA_TOSHIBA_MODULE) { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), }, { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), }, + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_3), }, + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_5), }, +#endif /* Must come last. If you add entries adjust this table appropriately */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xFFFFFF00UL, 1}, { 0, }, diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 9ac4e378992e..19136a7e1064 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -599,7 +599,7 @@ static const struct ich_laptop ich_laptop[] = { { 0x27DF, 0x1028, 0x02b0 }, /* ICH7 on unknown Dell */ { 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */ { 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */ - { 0x27DF, 0x103C, 0x361a }, /* ICH7 on unkown HP */ + { 0x27DF, 0x103C, 0x361a }, /* ICH7 on unknown HP */ { 0x27DF, 0x1071, 0xD221 }, /* ICH7 on Hercules EC-900 */ { 0x27DF, 0x152D, 0x0778 }, /* ICH7 on unknown Intel */ { 0x24CA, 0x1025, 0x0061 }, /* ICH4 on ACER Aspire 2023WLMi */ @@ -869,10 +869,10 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in (timings[pio][1] << 8); } - if (ap->udma_mask) { + if (ap->udma_mask) udma_enable &= ~(1 << devid); - pci_write_config_word(dev, master_port, master_data); - } + + pci_write_config_word(dev, master_port, master_data); } /* Don't scribble on 0x48 if the controller does not support UDMA */ if (ap->udma_mask) diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index b0882cddfd4c..1245838ac13d 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -807,12 +807,11 @@ static int ata_acpi_exec_tfs(struct ata_device *dev, int *nr_executed) * EH context. * * RETURNS: - * 0 on success, -errno on failure. + * 0 on success, -ENOENT if _SDD doesn't exist, -errno on failure. */ static int ata_acpi_push_id(struct ata_device *dev) { struct ata_port *ap = dev->link->ap; - int err; acpi_status status; struct acpi_object_list input; union acpi_object in_params[1]; @@ -835,12 +834,16 @@ static int ata_acpi_push_id(struct ata_device *dev) status = acpi_evaluate_object(dev->acpi_handle, "_SDD", &input, NULL); swap_buf_le16(dev->id, ATA_ID_WORDS); - err = ACPI_FAILURE(status) ? -EIO : 0; - if (err < 0) + if (status == AE_NOT_FOUND) + return -ENOENT; + + if (ACPI_FAILURE(status)) { ata_dev_printk(dev, KERN_WARNING, "ACPI _SDD failed (AE 0x%x)\n", status); + return -EIO; + } - return err; + return 0; } /** @@ -971,7 +974,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev) /* do _SDD if SATA */ if (acpi_sata) { rc = ata_acpi_push_id(dev); - if (rc) + if (rc && rc != -ENOENT) goto acpi_err; } diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index dc72690ed5db..22ff51bdbc8a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6616,6 +6616,13 @@ static int __init ata_init(void) { ata_parse_force_param(); + /* + * FIXME: In UP case, there is only one workqueue thread and if you + * have more than one PIO device, latency is bloody awful, with + * occasional multi-second "hiccups" as one PIO device waits for + * another. It's an ugly wart that users DO occasionally complain + * about; luckily most users have at most one PIO polled device. + */ ata_wq = create_workqueue("ata"); if (!ata_wq) goto free_force_tbl; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index bba2ae5df1c2..0ea97c942ced 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -110,6 +110,13 @@ static const unsigned long ata_eh_identify_timeouts[] = { ULONG_MAX, }; +static const unsigned long ata_eh_flush_timeouts[] = { + 15000, /* be generous with flush */ + 15000, /* ditto */ + 30000, /* and even more generous */ + ULONG_MAX, +}; + static const unsigned long ata_eh_other_timeouts[] = { 5000, /* same rationale as identify timeout */ 10000, /* ditto */ @@ -147,6 +154,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = { .timeouts = ata_eh_other_timeouts, }, { .commands = CMDS(ATA_CMD_INIT_DEV_PARAMS), .timeouts = ata_eh_other_timeouts, }, + { .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT), + .timeouts = ata_eh_flush_timeouts }, }; #undef CMDS @@ -3112,6 +3121,82 @@ static int atapi_eh_clear_ua(struct ata_device *dev) return 0; } +/** + * ata_eh_maybe_retry_flush - Retry FLUSH if necessary + * @dev: ATA device which may need FLUSH retry + * + * If @dev failed FLUSH, it needs to be reported upper layer + * immediately as it means that @dev failed to remap and already + * lost at least a sector and further FLUSH retrials won't make + * any difference to the lost sector. However, if FLUSH failed + * for other reasons, for example transmission error, FLUSH needs + * to be retried. + * + * This function determines whether FLUSH failure retry is + * necessary and performs it if so. + * + * RETURNS: + * 0 if EH can continue, -errno if EH needs to be repeated. + */ +static int ata_eh_maybe_retry_flush(struct ata_device *dev) +{ + struct ata_link *link = dev->link; + struct ata_port *ap = link->ap; + struct ata_queued_cmd *qc; + struct ata_taskfile tf; + unsigned int err_mask; + int rc = 0; + + /* did flush fail for this device? */ + if (!ata_tag_valid(link->active_tag)) + return 0; + + qc = __ata_qc_from_tag(ap, link->active_tag); + if (qc->dev != dev || (qc->tf.command != ATA_CMD_FLUSH_EXT && + qc->tf.command != ATA_CMD_FLUSH)) + return 0; + + /* if the device failed it, it should be reported to upper layers */ + if (qc->err_mask & AC_ERR_DEV) + return 0; + + /* flush failed for some other reason, give it another shot */ + ata_tf_init(dev, &tf); + + tf.command = qc->tf.command; + tf.flags |= ATA_TFLAG_DEVICE; + tf.protocol = ATA_PROT_NODATA; + + ata_dev_printk(dev, KERN_WARNING, "retrying FLUSH 0x%x Emask 0x%x\n", + tf.command, qc->err_mask); + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); + if (!err_mask) { + /* + * FLUSH is complete but there's no way to + * successfully complete a failed command from EH. + * Making sure retry is allowed at least once and + * retrying it should do the trick - whatever was in + * the cache is already on the platter and this won't + * cause infinite loop. + */ + qc->scsicmd->allowed = max(qc->scsicmd->allowed, 1); + } else { + ata_dev_printk(dev, KERN_WARNING, "FLUSH failed Emask 0x%x\n", + err_mask); + rc = -EIO; + + /* if device failed it, report it to upper layers */ + if (err_mask & AC_ERR_DEV) { + qc->err_mask |= AC_ERR_DEV; + qc->result_tf = tf; + if (!(ap->pflags & ATA_PFLAG_FROZEN)) + rc = 0; + } + } + return rc; +} + static int ata_link_nr_enabled(struct ata_link *link) { struct ata_device *dev; @@ -3455,6 +3540,15 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, } } + /* retry flush if necessary */ + ata_for_each_dev(dev, link, ALL) { + if (dev->class != ATA_DEV_ATA) + continue; + rc = ata_eh_maybe_retry_flush(dev); + if (rc) + goto dev_fail; + } + /* configure link power saving */ if (ehc->i.action & ATA_EH_LPM) ata_for_each_dev(dev, link, ALL) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index b4ee28dec521..1683ebda900b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -47,6 +47,7 @@ #include <linux/hdreg.h> #include <linux/uaccess.h> #include <linux/suspend.h> +#include <asm/unaligned.h> #include "libata.h" @@ -154,8 +155,7 @@ static ssize_t ata_scsi_lpm_put(struct device *dev, */ for (i = 1; i < ARRAY_SIZE(link_pm_policy); i++) { const int len = strlen(link_pm_policy[i].name); - if (strncmp(link_pm_policy[i].name, buf, len) == 0 && - buf[len] == '\n') { + if (strncmp(link_pm_policy[i].name, buf, len) == 0) { policy = link_pm_policy[i].value; break; } @@ -1208,6 +1208,7 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev) * ata_scsi_change_queue_depth - SCSI callback for queue depth config * @sdev: SCSI device to configure queue depth for * @queue_depth: new queue depth + * @reason: calling context * * This is libata standard hostt->change_queue_depth callback. * SCSI will call into this callback when user tries to set queue @@ -1219,12 +1220,16 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev) * RETURNS: * Newly configured queue depth. */ -int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth) +int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth, + int reason) { struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev; unsigned long flags; + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (queue_depth < 1 || queue_depth == sdev->queue_depth) return sdev->queue_depth; @@ -1964,6 +1969,7 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf) 0x80, /* page 0x80, unit serial no page */ 0x83, /* page 0x83, device ident page */ 0x89, /* page 0x89, ata info page */ + 0xb0, /* page 0xb0, block limits page */ 0xb1, /* page 0xb1, block device characteristics page */ }; @@ -2085,6 +2091,43 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf) return 0; } +static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf) +{ + u32 min_io_sectors; + + rbuf[1] = 0xb0; + rbuf[3] = 0x3c; /* required VPD size with unmap support */ + + /* + * Optimal transfer length granularity. + * + * This is always one physical block, but for disks with a smaller + * logical than physical sector size we need to figure out what the + * latter is. + */ + if (ata_id_has_large_logical_sectors(args->id)) + min_io_sectors = ata_id_logical_per_physical_sectors(args->id); + else + min_io_sectors = 1; + put_unaligned_be16(min_io_sectors, &rbuf[6]); + + /* + * Optimal unmap granularity. + * + * The ATA spec doesn't even know about a granularity or alignment + * for the TRIM command. We can leave away most of the unmap related + * VPD page entries, but we have specifify a granularity to signal + * that we support some form of unmap - in thise case via WRITE SAME + * with the unmap bit set. + */ + if (ata_id_has_trim(args->id)) { + put_unaligned_be32(65535 * 512 / 8, &rbuf[20]); + put_unaligned_be32(1, &rbuf[28]); + } + + return 0; +} + static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf) { int form_factor = ata_id_form_factor(args->id); @@ -2374,6 +2417,13 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf) rbuf[13] = log_per_phys; rbuf[14] = (lowest_aligned >> 8) & 0x3f; rbuf[15] = lowest_aligned; + + if (ata_id_has_trim(args->id)) { + rbuf[14] |= 0x80; /* TPE */ + + if (ata_id_has_zero_after_trim(args->id)) + rbuf[14] |= 0x40; /* TPRZ */ + } } return 0; @@ -2896,6 +2946,58 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) return 1; } +static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) +{ + struct ata_taskfile *tf = &qc->tf; + struct scsi_cmnd *scmd = qc->scsicmd; + struct ata_device *dev = qc->dev; + const u8 *cdb = scmd->cmnd; + u64 block; + u32 n_block; + u32 size; + void *buf; + + /* we may not issue DMA commands if no DMA mode is set */ + if (unlikely(!dev->dma_mode)) + goto invalid_fld; + + if (unlikely(scmd->cmd_len < 16)) + goto invalid_fld; + scsi_16_lba_len(cdb, &block, &n_block); + + /* for now we only support WRITE SAME with the unmap bit set */ + if (unlikely(!(cdb[1] & 0x8))) + goto invalid_fld; + + /* + * WRITE SAME always has a sector sized buffer as payload, this + * should never be a multiple entry S/G list. + */ + if (!scsi_sg_count(scmd)) + goto invalid_fld; + + buf = page_address(sg_page(scsi_sglist(scmd))); + size = ata_set_lba_range_entries(buf, 512, block, n_block); + + tf->protocol = ATA_PROT_DMA; + tf->hob_feature = 0; + tf->feature = ATA_DSM_TRIM; + tf->hob_nsect = (size / 512) >> 8; + tf->nsect = size / 512; + tf->command = ATA_CMD_DSM; + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | + ATA_TFLAG_WRITE; + + ata_qc_set_pc_nbytes(qc); + + return 0; + + invalid_fld: + ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00); + /* "Invalid field in cdb" */ + return 1; +} + /** * ata_get_xlat_func - check if SCSI to ATA translation is possible * @dev: ATA device @@ -2920,6 +3022,9 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) case WRITE_16: return ata_scsi_rw_xlat; + case 0x93 /*WRITE_SAME_16*/: + return ata_scsi_write_same_xlat; + case SYNCHRONIZE_CACHE: if (ata_try_flush_cache(dev)) return ata_scsi_flush_xlat; @@ -3109,6 +3214,9 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd, case 0x89: ata_scsi_rbuf_fill(&args, ata_scsiop_inq_89); break; + case 0xb0: + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b0); + break; case 0xb1: ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b1); break; diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index bbbb1fab1755..efa8773bef5a 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -736,7 +736,7 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf, /* * Use io*16_rep() accessors here as well to avoid pointlessly - * swapping bytes to and fro on the big endian machines... + * swapping bytes to and from on the big endian machines... */ if (rw == READ) { ioread16_rep(data_addr, pad, 1); @@ -776,7 +776,7 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf, void __iomem *data_addr = ap->ioaddr.data_addr; unsigned int words = buflen >> 2; int slop = buflen & 3; - + if (!(ap->pflags & ATA_PFLAG_PIO32)) return ata_sff_data_xfer(dev, buf, buflen, rw); @@ -795,7 +795,7 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf, /* * Use io*_rep() accessors here as well to avoid pointlessly - * swapping bytes to and fro on the big endian machines... + * swapping bytes to and from on the big endian machines... */ if (rw == READ) { if (slop < 3) @@ -2384,7 +2384,7 @@ void ata_sff_post_internal_cmd(struct ata_queued_cmd *qc) ap->hsm_task_state = HSM_ST_IDLE; if (ap->ioaddr.bmdma_addr) - ata_bmdma_stop(qc); + ap->ops->bmdma_stop(qc); spin_unlock_irqrestore(ap->lock, flags); } diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index 1432dc9d0ab8..9434114b2ca8 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -453,7 +453,9 @@ static void ali_init_chipset(struct pci_dev *pdev) /* Clear CD-ROM DMA write bit */ tmp &= 0x7F; /* Cable and UDMA */ - pci_write_config_byte(pdev, 0x4B, tmp | 0x09); + if (pdev->revision >= 0xc2) + tmp |= 0x01; + pci_write_config_byte(pdev, 0x4B, tmp | 0x08); /* * CD_ROM DMA on (0x53 bit 0). Enable this even if we want * to use PIO. 0x53 bit 1 (rev 20 only) - enable FIFO control diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c index f98dffedf4bc..dadfc358ba1c 100644 --- a/drivers/ata/pata_cmd64x.c +++ b/drivers/ata/pata_cmd64x.c @@ -31,7 +31,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_cmd64x" -#define DRV_VERSION "0.2.5" +#define DRV_VERSION "0.3.1" /* * CMD64x specific registers definition. @@ -254,17 +254,109 @@ static void cmd648_bmdma_stop(struct ata_queued_cmd *qc) } /** - * cmd646r1_dma_stop - DMA stop callback + * cmd64x_bmdma_stop - DMA stop callback * @qc: Command in progress * - * Stub for now while investigating the r1 quirk in the old driver. + * Track the completion of live DMA commands and clear the + * host->private_data DMA tracking flag as we do. */ -static void cmd646r1_bmdma_stop(struct ata_queued_cmd *qc) +static void cmd64x_bmdma_stop(struct ata_queued_cmd *qc) { + struct ata_port *ap = qc->ap; ata_bmdma_stop(qc); + WARN_ON(ap->host->private_data != ap); + ap->host->private_data = NULL; } +/** + * cmd64x_qc_defer - Defer logic for chip limits + * @qc: queued command + * + * Decide whether we can issue the command. Called under the host lock. + */ + +static int cmd64x_qc_defer(struct ata_queued_cmd *qc) +{ + struct ata_host *host = qc->ap->host; + struct ata_port *alt = host->ports[1 ^ qc->ap->port_no]; + int rc; + int dma = 0; + + /* Apply the ATA rules first */ + rc = ata_std_qc_defer(qc); + if (rc) + return rc; + + if (qc->tf.protocol == ATAPI_PROT_DMA || + qc->tf.protocol == ATA_PROT_DMA) + dma = 1; + + /* If the other port is not live then issue the command */ + if (alt == NULL || !alt->qc_active) { + if (dma) + host->private_data = qc->ap; + return 0; + } + /* If there is a live DMA command then wait */ + if (host->private_data != NULL) + return ATA_DEFER_PORT; + if (dma) + /* Cannot overlap our DMA command */ + return ATA_DEFER_PORT; + return 0; +} + +/** + * cmd64x_interrupt - ATA host interrupt handler + * @irq: irq line (unused) + * @dev_instance: pointer to our ata_host information structure + * + * Our interrupt handler for PCI IDE devices. Calls + * ata_sff_host_intr() for each port that is flagging an IRQ. We cannot + * use the defaults as we need to avoid touching status/altstatus during + * a DMA. + * + * LOCKING: + * Obtains host lock during operation. + * + * RETURNS: + * IRQ_NONE or IRQ_HANDLED. + */ +irqreturn_t cmd64x_interrupt(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct pci_dev *pdev = to_pci_dev(host->dev); + unsigned int i; + unsigned int handled = 0; + unsigned long flags; + static const u8 irq_reg[2] = { CFR, ARTTIM23 }; + static const u8 irq_mask[2] = { 1 << 2, 1 << 4 }; + + /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */ + spin_lock_irqsave(&host->lock, flags); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap; + u8 reg; + + pci_read_config_byte(pdev, irq_reg[i], ®); + ap = host->ports[i]; + if (ap && (reg & irq_mask[i]) && + !(ap->flags & ATA_FLAG_DISABLED)) { + struct ata_queued_cmd *qc; + + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) && + (qc->flags & ATA_QCFLAG_ACTIVE)) + handled |= ata_sff_host_intr(ap, qc); + } + } + + spin_unlock_irqrestore(&host->lock, flags); + + return IRQ_RETVAL(handled); +} static struct scsi_host_template cmd64x_sht = { ATA_BMDMA_SHT(DRV_NAME), }; @@ -273,6 +365,8 @@ static const struct ata_port_operations cmd64x_base_ops = { .inherits = &ata_bmdma_port_ops, .set_piomode = cmd64x_set_piomode, .set_dmamode = cmd64x_set_dmamode, + .bmdma_stop = cmd64x_bmdma_stop, + .qc_defer = cmd64x_qc_defer, }; static struct ata_port_operations cmd64x_port_ops = { @@ -282,7 +376,6 @@ static struct ata_port_operations cmd64x_port_ops = { static struct ata_port_operations cmd646r1_port_ops = { .inherits = &cmd64x_base_ops, - .bmdma_stop = cmd646r1_bmdma_stop, .cable_detect = ata_cable_40wire, }; @@ -290,12 +383,11 @@ static struct ata_port_operations cmd648_port_ops = { .inherits = &cmd64x_base_ops, .bmdma_stop = cmd648_bmdma_stop, .cable_detect = cmd648_cable_detect, + .qc_defer = ata_std_qc_defer }; static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { - u32 class_rev; - static const struct ata_port_info cmd_info[6] = { { /* CMD 643 - no UDMA */ .flags = ATA_FLAG_SLAVE_POSS, @@ -340,40 +432,43 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) const struct ata_port_info *ppi[] = { &cmd_info[id->driver_data], NULL }; u8 mrdmode; int rc; + struct ata_host *host; rc = pcim_enable_device(pdev); if (rc) return rc; - pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class_rev); - class_rev &= 0xFF; - if (id->driver_data == 0) /* 643 */ ata_pci_bmdma_clear_simplex(pdev); if (pdev->device == PCI_DEVICE_ID_CMD_646) { /* Does UDMA work ? */ - if (class_rev > 4) + if (pdev->revision > 4) ppi[0] = &cmd_info[2]; /* Early rev with other problems ? */ - else if (class_rev == 1) + else if (pdev->revision == 1) ppi[0] = &cmd_info[3]; } + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); pci_read_config_byte(pdev, MRDMODE, &mrdmode); mrdmode &= ~ 0x30; /* IRQ set up */ mrdmode |= 0x02; /* Memory read line enable */ pci_write_config_byte(pdev, MRDMODE, mrdmode); - /* Force PIO 0 here.. */ - /* PPC specific fixup copied from old driver */ #ifdef CONFIG_PPC pci_write_config_byte(pdev, UDIDETCR0, 0xF0); #endif + rc = ata_pci_sff_prepare_host(pdev, ppi, &host); + if (rc) + return rc; + /* We use this pointer to track the AP which has DMA running */ + host->private_data = NULL; - return ata_pci_sff_init_one(pdev, ppi, &cmd64x_sht, NULL); + pci_set_master(pdev); + return ata_pci_sff_activate_host(host, cmd64x_interrupt, &cmd64x_sht); } #ifdef CONFIG_PM diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c index 0df83cf74233..95ebdac517f2 100644 --- a/drivers/ata/pata_cs5520.c +++ b/drivers/ata/pata_cs5520.c @@ -90,48 +90,12 @@ static void cs5520_set_timings(struct ata_port *ap, struct ata_device *adev, int } /** - * cs5520_enable_dma - turn on DMA bits - * - * Turn on the DMA bits for this disk. Needed because the BIOS probably - * has not done the work for us. Belongs in the core SATA code. - */ - -static void cs5520_enable_dma(struct ata_port *ap, struct ata_device *adev) -{ - /* Set the DMA enable/disable flag */ - u8 reg = ioread8(ap->ioaddr.bmdma_addr + 0x02); - reg |= 1<<(adev->devno + 5); - iowrite8(reg, ap->ioaddr.bmdma_addr + 0x02); -} - -/** - * cs5520_set_dmamode - program DMA timings - * @ap: ATA port - * @adev: ATA device - * - * Program the DMA mode timings for the controller according to the pio - * clocking table. Note that this device sets the DMA timings to PIO - * mode values. This may seem bizarre but the 5520 architecture talks - * PIO mode to the disk and DMA mode to the controller so the underlying - * transfers are PIO timed. - */ - -static void cs5520_set_dmamode(struct ata_port *ap, struct ata_device *adev) -{ - static const int dma_xlate[3] = { XFER_PIO_0, XFER_PIO_3, XFER_PIO_4 }; - cs5520_set_timings(ap, adev, dma_xlate[adev->dma_mode]); - cs5520_enable_dma(ap, adev); -} - -/** * cs5520_set_piomode - program PIO timings * @ap: ATA port * @adev: ATA device * * Program the PIO mode timings for the controller according to the pio - * clocking table. We know pio_mode will equal dma_mode because of the - * CS5520 architecture. At least once we turned DMA on and wrote a - * mode setter. + * clocking table. */ static void cs5520_set_piomode(struct ata_port *ap, struct ata_device *adev) @@ -149,7 +113,6 @@ static struct ata_port_operations cs5520_port_ops = { .qc_prep = ata_sff_dumb_qc_prep, .cable_detect = ata_cable_40wire, .set_piomode = cs5520_set_piomode, - .set_dmamode = cs5520_set_dmamode, }; static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c index 403f56165cec..71cef9a962d4 100644 --- a/drivers/ata/pata_cs5535.c +++ b/drivers/ata/pata_cs5535.c @@ -4,7 +4,7 @@ * Alan Cox <alan@lxorguk.ukuu.org.uk> * * based upon cs5535.c from AMD <Jens.Altmann@amd.com> as cleaned up and - * made readable and Linux style by Wolfgang Zuleger <wolfgang.zuleger@gmx.de + * made readable and Linux style by Wolfgang Zuleger <wolfgang.zuleger@gmx.de> * and Alexander Kiausch <alex.kiausch@t-online.de> * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c index 6da4cb486c8d..ffee3978ec83 100644 --- a/drivers/ata/pata_cs5536.c +++ b/drivers/ata/pata_cs5536.c @@ -224,7 +224,7 @@ static struct scsi_host_template cs5536_sht = { }; static struct ata_port_operations cs5536_port_ops = { - .inherits = &ata_bmdma_port_ops, + .inherits = &ata_bmdma32_port_ops, .cable_detect = cs5536_cable_detect, .set_piomode = cs5536_set_piomode, .set_dmamode = cs5536_set_dmamode, diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c index 2a6412f5d117..b2e71e6473ed 100644 --- a/drivers/ata/pata_efar.c +++ b/drivers/ata/pata_efar.c @@ -2,6 +2,7 @@ * pata_efar.c - EFAR PIIX clone controller driver * * (C) 2005 Red Hat + * (C) 2009 Bartlomiej Zolnierkiewicz * * Some parts based on ata_piix.c by Jeff Garzik and others. * @@ -118,12 +119,12 @@ static void efar_set_piomode (struct ata_port *ap, struct ata_device *adev) int shift = 4 * ap->port_no; u8 slave_data; - idetm_data &= 0xCC0F; + idetm_data &= 0xFF0F; idetm_data |= (control << 4); /* Slave timing in separate register */ pci_read_config_byte(dev, 0x44, &slave_data); - slave_data &= 0x0F << shift; + slave_data &= ap->port_no ? 0x0F : 0xF0; slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << shift; pci_write_config_byte(dev, 0x44, slave_data); } @@ -200,7 +201,7 @@ static void efar_set_dmamode (struct ata_port *ap, struct ata_device *adev) master_data &= 0xFF4F; /* Mask out IORDY|TIME1|DMAONLY */ master_data |= control << 4; pci_read_config_byte(dev, 0x44, &slave_data); - slave_data &= (0x0F + 0xE1 * ap->port_no); + slave_data &= ap->port_no ? 0x0F : 0xF0; /* Load the matching timing */ slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << (ap->port_no ? 4 : 0); pci_write_config_byte(dev, 0x44, slave_data); @@ -251,7 +252,7 @@ static int efar_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) static const struct ata_port_info info = { .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, + .mwdma_mask = ATA_MWDMA12_ONLY, .udma_mask = ATA_UDMA4, .port_ops = &efar_ops, }; diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index d7f2da127d13..0bd48e8f21bd 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -27,7 +27,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_hpt366" -#define DRV_VERSION "0.6.2" +#define DRV_VERSION "0.6.7" struct hpt_clock { u8 xfer_mode; @@ -36,24 +36,22 @@ struct hpt_clock { /* key for bus clock timings * bit - * 0:3 data_high_time. inactive time of DIOW_/DIOR_ for PIO and MW - * DMA. cycles = value + 1 - * 4:8 data_low_time. active time of DIOW_/DIOR_ for PIO and MW - * DMA. cycles = value + 1 - * 9:12 cmd_high_time. inactive time of DIOW_/DIOR_ during task file + * 0:3 data_high_time. Inactive time of DIOW_/DIOR_ for PIO and MW DMA. + * cycles = value + 1 + * 4:7 data_low_time. Active time of DIOW_/DIOR_ for PIO and MW DMA. + * cycles = value + 1 + * 8:11 cmd_high_time. Inactive time of DIOW_/DIOR_ during task file * register access. - * 13:17 cmd_low_time. active time of DIOW_/DIOR_ during task file + * 12:15 cmd_low_time. Active time of DIOW_/DIOR_ during task file * register access. - * 18:21 udma_cycle_time. clock freq and clock cycles for UDMA xfer. - * during task file register access. - * 22:24 pre_high_time. time to initialize 1st cycle for PIO and MW DMA - * xfer. - * 25:27 cmd_pre_high_time. time to initialize 1st PIO cycle for task + * 16:18 udma_cycle_time. Clock cycles for UDMA xfer? + * 19:21 pre_high_time. Time to initialize 1st cycle for PIO and MW DMA xfer. + * 22:24 cmd_pre_high_time. Time to initialize 1st PIO cycle for task file * register access. - * 28 UDMA enable - * 29 DMA enable - * 30 PIO_MST enable. if set, the chip is in bus master mode during - * PIO. + * 28 UDMA enable. + * 29 DMA enable. + * 30 PIO_MST enable. If set, the chip is in bus master mode during + * PIO xfer. * 31 FIFO enable. */ @@ -344,7 +342,6 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id) const struct ata_port_info *ppi[] = { &info_hpt366, NULL }; void *hpriv = NULL; - u32 class_rev; u32 reg1; int rc; @@ -352,13 +349,10 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id) if (rc) return rc; - pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); - class_rev &= 0xFF; - /* May be a later chip in disguise. Check */ /* Newer chips are not in the HPT36x driver. Ignore them */ - if (class_rev > 2) - return -ENODEV; + if (dev->revision > 2) + return -ENODEV; hpt36x_init_chipset(dev); diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index d0a7df2e5ca7..4224cfccedef 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -24,7 +24,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_hpt37x" -#define DRV_VERSION "0.6.12" +#define DRV_VERSION "0.6.14" struct hpt_clock { u8 xfer_speed; @@ -303,72 +303,79 @@ static unsigned long hpt370a_filter(struct ata_device *adev, unsigned long mask) } /** - * hpt37x_pre_reset - reset the hpt37x bus - * @link: ATA link to reset - * @deadline: deadline jiffies for the operation + * hpt37x_cable_detect - Detect the cable type + * @ap: ATA port to detect on * - * Perform the initial reset handling for the 370/372 and 374 func 0 + * Return the cable type attached to this port */ -static int hpt37x_pre_reset(struct ata_link *link, unsigned long deadline) +static int hpt37x_cable_detect(struct ata_port *ap) { - u8 scr2, ata66; - struct ata_port *ap = link->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - static const struct pci_bits hpt37x_enable_bits[] = { - { 0x50, 1, 0x04, 0x04 }, - { 0x54, 1, 0x04, 0x04 } - }; - if (!pci_test_config_bits(pdev, &hpt37x_enable_bits[ap->port_no])) - return -ENOENT; + u8 scr2, ata66; pci_read_config_byte(pdev, 0x5B, &scr2); pci_write_config_byte(pdev, 0x5B, scr2 & ~0x01); + + udelay(10); /* debounce */ + /* Cable register now active */ pci_read_config_byte(pdev, 0x5A, &ata66); /* Restore state */ pci_write_config_byte(pdev, 0x5B, scr2); if (ata66 & (2 >> ap->port_no)) - ap->cbl = ATA_CBL_PATA40; + return ATA_CBL_PATA40; else - ap->cbl = ATA_CBL_PATA80; - - /* Reset the state machine */ - pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); - udelay(100); - - return ata_sff_prereset(link, deadline); + return ATA_CBL_PATA80; } -static int hpt374_fn1_pre_reset(struct ata_link *link, unsigned long deadline) +/** + * hpt374_fn1_cable_detect - Detect the cable type + * @ap: ATA port to detect on + * + * Return the cable type attached to this port + */ + +static int hpt374_fn1_cable_detect(struct ata_port *ap) { - static const struct pci_bits hpt37x_enable_bits[] = { - { 0x50, 1, 0x04, 0x04 }, - { 0x54, 1, 0x04, 0x04 } - }; - u16 mcr3; - u8 ata66; - struct ata_port *ap = link->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); unsigned int mcrbase = 0x50 + 4 * ap->port_no; - - if (!pci_test_config_bits(pdev, &hpt37x_enable_bits[ap->port_no])) - return -ENOENT; + u16 mcr3; + u8 ata66; /* Do the extra channel work */ pci_read_config_word(pdev, mcrbase + 2, &mcr3); - /* Set bit 15 of 0x52 to enable TCBLID as input - */ + /* Set bit 15 of 0x52 to enable TCBLID as input */ pci_write_config_word(pdev, mcrbase + 2, mcr3 | 0x8000); pci_read_config_byte(pdev, 0x5A, &ata66); /* Reset TCBLID/FCBLID to output */ pci_write_config_word(pdev, mcrbase + 2, mcr3); if (ata66 & (2 >> ap->port_no)) - ap->cbl = ATA_CBL_PATA40; + return ATA_CBL_PATA40; else - ap->cbl = ATA_CBL_PATA80; + return ATA_CBL_PATA80; +} + +/** + * hpt37x_pre_reset - reset the hpt37x bus + * @link: ATA link to reset + * @deadline: deadline jiffies for the operation + * + * Perform the initial reset handling for the HPT37x. + */ + +static int hpt37x_pre_reset(struct ata_link *link, unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + static const struct pci_bits hpt37x_enable_bits[] = { + { 0x50, 1, 0x04, 0x04 }, + { 0x54, 1, 0x04, 0x04 } + }; + if (!pci_test_config_bits(pdev, &hpt37x_enable_bits[ap->port_no])) + return -ENOENT; /* Reset the state machine */ pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); @@ -404,9 +411,8 @@ static void hpt370_set_piomode(struct ata_port *ap, struct ata_device *adev) pci_read_config_dword(pdev, addr1, ®); mode = hpt37x_find_mode(ap, adev->pio_mode); - mode &= ~0x8000000; /* No FIFO in PIO */ - mode &= ~0x30070000; /* Leave config bits alone */ - reg &= 0x30070000; /* Strip timing bits */ + mode &= 0xCFC3FFFF; /* Leave DMA bits alone */ + reg &= ~0xCFC3FFFF; /* Strip timing bits */ pci_write_config_dword(pdev, addr1, reg | mode); } @@ -423,8 +429,7 @@ static void hpt370_set_dmamode(struct ata_port *ap, struct ata_device *adev) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u32 addr1, addr2; - u32 reg; - u32 mode; + u32 reg, mode, mask; u8 fast; addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no); @@ -436,11 +441,12 @@ static void hpt370_set_dmamode(struct ata_port *ap, struct ata_device *adev) fast |= 0x01; pci_write_config_byte(pdev, addr2, fast); + mask = adev->dma_mode < XFER_UDMA_0 ? 0x31C001FF : 0x303C0000; + pci_read_config_dword(pdev, addr1, ®); mode = hpt37x_find_mode(ap, adev->dma_mode); - mode |= 0x8000000; /* FIFO in MWDMA or UDMA */ - mode &= ~0xC0000000; /* Leave config bits alone */ - reg &= 0xC0000000; /* Strip timing bits */ + mode &= mask; + reg &= ~mask; pci_write_config_dword(pdev, addr1, reg | mode); } @@ -508,9 +514,8 @@ static void hpt372_set_piomode(struct ata_port *ap, struct ata_device *adev) mode = hpt37x_find_mode(ap, adev->pio_mode); printk("Find mode for %d reports %X\n", adev->pio_mode, mode); - mode &= ~0x80000000; /* No FIFO in PIO */ - mode &= ~0x30070000; /* Leave config bits alone */ - reg &= 0x30070000; /* Strip timing bits */ + mode &= 0xCFC3FFFF; /* Leave DMA bits alone */ + reg &= ~0xCFC3FFFF; /* Strip timing bits */ pci_write_config_dword(pdev, addr1, reg | mode); } @@ -527,8 +532,7 @@ static void hpt372_set_dmamode(struct ata_port *ap, struct ata_device *adev) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u32 addr1, addr2; - u32 reg; - u32 mode; + u32 reg, mode, mask; u8 fast; addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no); @@ -539,12 +543,13 @@ static void hpt372_set_dmamode(struct ata_port *ap, struct ata_device *adev) fast &= ~0x07; pci_write_config_byte(pdev, addr2, fast); + mask = adev->dma_mode < XFER_UDMA_0 ? 0x31C001FF : 0x303C0000; + pci_read_config_dword(pdev, addr1, ®); mode = hpt37x_find_mode(ap, adev->dma_mode); printk("Find mode for DMA %d reports %X\n", adev->dma_mode, mode); - mode &= ~0xC0000000; /* Leave config bits alone */ - mode |= 0x80000000; /* FIFO in MWDMA or UDMA */ - reg &= 0xC0000000; /* Strip timing bits */ + mode &= mask; + reg &= ~mask; pci_write_config_dword(pdev, addr1, reg | mode); } @@ -584,6 +589,7 @@ static struct ata_port_operations hpt370_port_ops = { .bmdma_stop = hpt370_bmdma_stop, .mode_filter = hpt370_filter, + .cable_detect = hpt37x_cable_detect, .set_piomode = hpt370_set_piomode, .set_dmamode = hpt370_set_dmamode, .prereset = hpt37x_pre_reset, @@ -608,6 +614,7 @@ static struct ata_port_operations hpt372_port_ops = { .bmdma_stop = hpt37x_bmdma_stop, + .cable_detect = hpt37x_cable_detect, .set_piomode = hpt372_set_piomode, .set_dmamode = hpt372_set_dmamode, .prereset = hpt37x_pre_reset, @@ -620,7 +627,8 @@ static struct ata_port_operations hpt372_port_ops = { static struct ata_port_operations hpt374_fn1_port_ops = { .inherits = &hpt372_port_ops, - .prereset = hpt374_fn1_pre_reset, + .cable_detect = hpt374_fn1_cable_detect, + .prereset = hpt37x_pre_reset, }; /** @@ -791,9 +799,8 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) static const int MHz[4] = { 33, 40, 50, 66 }; void *private_data = NULL; const struct ata_port_info *ppi[] = { NULL, NULL }; - + u8 rev = dev->revision; u8 irqmask; - u32 class_rev; u8 mcr1; u32 freq; int prefer_dpll = 1; @@ -808,19 +815,16 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) if (rc) return rc; - pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); - class_rev &= 0xFF; - if (dev->device == PCI_DEVICE_ID_TTI_HPT366) { /* May be a later chip in disguise. Check */ /* Older chips are in the HPT366 driver. Ignore them */ - if (class_rev < 3) + if (rev < 3) return -ENODEV; /* N series chips have their own driver. Ignore */ - if (class_rev == 6) + if (rev == 6) return -ENODEV; - switch(class_rev) { + switch(rev) { case 3: ppi[0] = &info_hpt370; chip_table = &hpt370; @@ -836,28 +840,29 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) chip_table = &hpt372; break; default: - printk(KERN_ERR "pata_hpt37x: Unknown HPT366 subtype please report (%d).\n", class_rev); + printk(KERN_ERR "pata_hpt37x: Unknown HPT366 " + "subtype, please report (%d).\n", rev); return -ENODEV; } } else { switch(dev->device) { case PCI_DEVICE_ID_TTI_HPT372: /* 372N if rev >= 2*/ - if (class_rev >= 2) + if (rev >= 2) return -ENODEV; ppi[0] = &info_hpt372; chip_table = &hpt372a; break; case PCI_DEVICE_ID_TTI_HPT302: /* 302N if rev > 1 */ - if (class_rev > 1) + if (rev > 1) return -ENODEV; ppi[0] = &info_hpt372; /* Check this */ chip_table = &hpt302; break; case PCI_DEVICE_ID_TTI_HPT371: - if (class_rev > 1) + if (rev > 1) return -ENODEV; ppi[0] = &info_hpt372; chip_table = &hpt371; diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c index 3d59fe0a408d..9a09a1b11ca5 100644 --- a/drivers/ata/pata_hpt3x2n.c +++ b/drivers/ata/pata_hpt3x2n.c @@ -25,7 +25,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_hpt3x2n" -#define DRV_VERSION "0.3.4" +#define DRV_VERSION "0.3.7" enum { HPT_PCI_FAST = (1 << 31), @@ -80,14 +80,13 @@ static struct hpt_clock hpt3x2n_clocks[] = { { XFER_MW_DMA_2, 0x2c829c62 }, { XFER_MW_DMA_1, 0x2c829c66 }, - { XFER_MW_DMA_0, 0x2c829d2c }, + { XFER_MW_DMA_0, 0x2c829d2e }, { XFER_PIO_4, 0x0c829c62 }, { XFER_PIO_3, 0x0c829c84 }, { XFER_PIO_2, 0x0c829ca6 }, { XFER_PIO_1, 0x0d029d26 }, { XFER_PIO_0, 0x0d029d5e }, - { 0, 0x0d029d5e } }; /** @@ -128,12 +127,15 @@ static int hpt3x2n_cable_detect(struct ata_port *ap) pci_read_config_byte(pdev, 0x5B, &scr2); pci_write_config_byte(pdev, 0x5B, scr2 & ~0x01); + + udelay(10); /* debounce */ + /* Cable register now active */ pci_read_config_byte(pdev, 0x5A, &ata66); /* Restore state */ pci_write_config_byte(pdev, 0x5B, scr2); - if (ata66 & (1 << ap->port_no)) + if (ata66 & (2 >> ap->port_no)) return ATA_CBL_PATA40; else return ATA_CBL_PATA80; @@ -185,9 +187,8 @@ static void hpt3x2n_set_piomode(struct ata_port *ap, struct ata_device *adev) pci_read_config_dword(pdev, addr1, ®); mode = hpt3x2n_find_mode(ap, adev->pio_mode); - mode &= ~0x8000000; /* No FIFO in PIO */ - mode &= ~0x30070000; /* Leave config bits alone */ - reg &= 0x30070000; /* Strip timing bits */ + mode &= 0xCFC3FFFF; /* Leave DMA bits alone */ + reg &= ~0xCFC3FFFF; /* Strip timing bits */ pci_write_config_dword(pdev, addr1, reg | mode); } @@ -204,8 +205,7 @@ static void hpt3x2n_set_dmamode(struct ata_port *ap, struct ata_device *adev) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u32 addr1, addr2; - u32 reg; - u32 mode; + u32 reg, mode, mask; u8 fast; addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no); @@ -216,11 +216,12 @@ static void hpt3x2n_set_dmamode(struct ata_port *ap, struct ata_device *adev) fast &= ~0x07; pci_write_config_byte(pdev, addr2, fast); + mask = adev->dma_mode < XFER_UDMA_0 ? 0x31C001FF : 0x303C0000; + pci_read_config_dword(pdev, addr1, ®); mode = hpt3x2n_find_mode(ap, adev->dma_mode); - mode |= 0x8000000; /* FIFO in MWDMA or UDMA */ - mode &= ~0xC0000000; /* Leave config bits alone */ - reg &= 0xC0000000; /* Strip timing bits */ + mode &= mask; + reg &= ~mask; pci_write_config_dword(pdev, addr1, reg | mode); } @@ -447,10 +448,8 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) .port_ops = &hpt3x2n_port_ops }; const struct ata_port_info *ppi[] = { &info, NULL }; - + u8 rev = dev->revision; u8 irqmask; - u32 class_rev; - unsigned int pci_mhz; unsigned int f_low, f_high; int adjust; @@ -462,26 +461,23 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) if (rc) return rc; - pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); - class_rev &= 0xFF; - switch(dev->device) { case PCI_DEVICE_ID_TTI_HPT366: - if (class_rev < 6) + if (rev < 6) return -ENODEV; break; case PCI_DEVICE_ID_TTI_HPT371: - if (class_rev < 2) + if (rev < 2) return -ENODEV; /* 371N if rev > 1 */ break; case PCI_DEVICE_ID_TTI_HPT372: /* 372N if rev >= 2*/ - if (class_rev < 2) + if (rev < 2) return -ENODEV; break; case PCI_DEVICE_ID_TTI_HPT302: - if (class_rev < 2) + if (rev < 2) return -ENODEV; break; case PCI_DEVICE_ID_TTI_HPT372N: diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c index 7e310253b36b..c86c71639a95 100644 --- a/drivers/ata/pata_hpt3x3.c +++ b/drivers/ata/pata_hpt3x3.c @@ -255,8 +255,17 @@ static int hpt3x3_init_one(struct pci_dev *pdev, const struct pci_device_id *id) #ifdef CONFIG_PM static int hpt3x3_reinit_one(struct pci_dev *dev) { + struct ata_host *host = dev_get_drvdata(&dev->dev); + int rc; + + rc = ata_pci_device_do_resume(dev); + if (rc) + return rc; + hpt3x3_init_chipset(dev); - return ata_pci_device_resume(dev); + + ata_host_resume(host); + return 0; } #endif diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c index f156da8076f7..8f3325adceb3 100644 --- a/drivers/ata/pata_it8213.c +++ b/drivers/ata/pata_it8213.c @@ -22,7 +22,7 @@ #define DRV_VERSION "0.0.3" /** - * it8213_pre_reset - check for 40/80 pin + * it8213_pre_reset - probe begin * @link: link * @deadline: deadline jiffies for the operation * @@ -92,18 +92,17 @@ static void it8213_set_piomode (struct ata_port *ap, struct ata_device *adev) { 2, 1 }, { 2, 3 }, }; - if (pio > 2) - control |= 1; /* TIME1 enable */ + if (pio > 1) + control |= 1; /* TIME */ if (ata_pio_need_iordy(adev)) /* PIO 3/4 require IORDY */ - control |= 2; /* IORDY enable */ + control |= 2; /* IE */ /* Bit 2 is set for ATAPI on the IT8213 - reverse of ICH/PIIX */ if (adev->class != ATA_DEV_ATA) - control |= 4; + control |= 4; /* PPE */ pci_read_config_word(dev, idetm_port, &idetm_data); - /* Enable PPE, IE and TIME as appropriate */ - + /* Set PPE, IE, and TIME as appropriate */ if (adev->devno == 0) { idetm_data &= 0xCCF0; idetm_data |= control; @@ -112,17 +111,17 @@ static void it8213_set_piomode (struct ata_port *ap, struct ata_device *adev) } else { u8 slave_data; - idetm_data &= 0xCC0F; + idetm_data &= 0xFF0F; idetm_data |= (control << 4); /* Slave timing in separate register */ pci_read_config_byte(dev, 0x44, &slave_data); slave_data &= 0xF0; - slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << 4; + slave_data |= (timings[pio][0] << 2) | timings[pio][1]; pci_write_config_byte(dev, 0x44, slave_data); } - idetm_data |= 0x4000; /* Ensure SITRE is enabled */ + idetm_data |= 0x4000; /* Ensure SITRE is set */ pci_write_config_word(dev, idetm_port, idetm_data); } @@ -173,10 +172,10 @@ static void it8213_set_dmamode (struct ata_port *ap, struct ata_device *adev) udma_enable |= (1 << devid); - /* Load the UDMA mode number */ + /* Load the UDMA cycle time */ pci_read_config_word(dev, 0x4A, &udma_timing); udma_timing &= ~(3 << (4 * devid)); - udma_timing |= (udma & 3) << (4 * devid); + udma_timing |= u_speed << (4 * devid); pci_write_config_word(dev, 0x4A, udma_timing); /* Load the clock selection */ @@ -211,7 +210,7 @@ static void it8213_set_dmamode (struct ata_port *ap, struct ata_device *adev) master_data &= 0xFF4F; /* Mask out IORDY|TIME1|DMAONLY */ master_data |= control << 4; pci_read_config_byte(dev, 0x44, &slave_data); - slave_data &= (0x0F + 0xE1 * ap->port_no); + slave_data &= 0xF0; /* Load the matching timing */ slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << (ap->port_no ? 4 : 0); pci_write_config_byte(dev, 0x44, slave_data); @@ -263,7 +262,7 @@ static int it8213_init_one (struct pci_dev *pdev, const struct pci_device_id *en static const struct ata_port_info info = { .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, + .mwdma_mask = ATA_MWDMA12_ONLY, .udma_mask = ATA_UDMA4, /* FIXME: want UDMA 100? */ .port_ops = &it8213_ops, }; diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index 188bc2fcd22c..edc5c1fed150 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -955,7 +955,7 @@ static int it821x_reinit_one(struct pci_dev *pdev) static const struct pci_device_id it821x[] = { { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8211), }, { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8212), }, - { PCI_VDEVICE(RDC, 0x1010), }, + { PCI_VDEVICE(RDC, PCI_DEVICE_ID_RDC_D1010), }, { }, }; diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c index 6932e56d179c..9df1ff7e1eaa 100644 --- a/drivers/ata/pata_legacy.c +++ b/drivers/ata/pata_legacy.c @@ -25,6 +25,13 @@ * http://www.ryston.cz/petr/vlb/pdc20230b.html * http://www.ryston.cz/petr/vlb/pdc20230c.html * http://www.ryston.cz/petr/vlb/pdc20630.html + * QDI65x0: + * http://www.ryston.cz/petr/vlb/qd6500.html + * http://www.ryston.cz/petr/vlb/qd6580.html + * + * QDI65x0 probe code based on drivers/ide/legacy/qd65xx.c + * Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by + * Samuel Thibault <samuel.thibault@ens-lyon.org> * * Unsupported but docs exist: * Appian/Adaptec AIC25VL01/Cirrus Logic PD7220 @@ -35,7 +42,7 @@ * the MPIIX where the tuning is PCI side but the IDE is "ISA side". * * Specific support is included for the ht6560a/ht6560b/opti82c611a/ - * opti82c465mv/promise 20230c/20630/winbond83759A + * opti82c465mv/promise 20230c/20630/qdi65x0/winbond83759A * * Use the autospeed and pio_mask options with: * Appian ADI/2 aka CLPD7220 or AIC25VL01. @@ -672,7 +679,7 @@ static void qdi6580dp_set_piomode(struct ata_port *ap, struct ata_device *adev) outb(timing, ld_qdi->timing + 2 * ap->port_no); /* Clear the FIFO */ if (adev->class != ATA_DEV_ATA) - outb(0x5F, ld_qdi->timing + 3); + outb(0x5F, (ld_qdi->timing & 0xFFF0) + 3); } /** @@ -707,7 +714,7 @@ static void qdi6580_set_piomode(struct ata_port *ap, struct ata_device *adev) outb(timing, ld_qdi->timing + 2 * adev->devno); /* Clear the FIFO */ if (adev->class != ATA_DEV_ATA) - outb(0x5F, ld_qdi->timing + 3); + outb(0x5F, (ld_qdi->timing & 0xFFF0) + 3); } /** @@ -787,6 +794,7 @@ static struct ata_port_operations qdi6580_port_ops = { static struct ata_port_operations qdi6580dp_port_ops = { .inherits = &legacy_base_port_ops, .set_piomode = qdi6580dp_set_piomode, + .qc_issue = qdi_qc_issue, .sff_data_xfer = vlb32_data_xfer, }; diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c index 2096fb737f82..950da39cae3d 100644 --- a/drivers/ata/pata_marvell.c +++ b/drivers/ata/pata_marvell.c @@ -58,7 +58,7 @@ static int marvell_pata_active(struct pci_dev *pdev) } /** - * marvell_pre_reset - check for 40/80 pin + * marvell_pre_reset - probe begin * @link: link * @deadline: deadline jiffies for the operation * diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c index 773b1590b492..061aa1c41a48 100644 --- a/drivers/ata/pata_ns87415.c +++ b/drivers/ata/pata_ns87415.c @@ -325,6 +325,13 @@ static struct scsi_host_template ns87415_sht = { ATA_BMDMA_SHT(DRV_NAME), }; +static void ns87415_fixup(struct pci_dev *pdev) +{ + /* Select 512 byte sectors */ + pci_write_config_byte(pdev, 0x55, 0xEE); + /* Select PIO0 8bit clocking */ + pci_write_config_byte(pdev, 0x54, 0xB7); +} /** * ns87415_init_one - Register 87415 ATA PCI device with kernel services @@ -371,10 +378,8 @@ static int ns87415_init_one (struct pci_dev *pdev, const struct pci_device_id *e if (rc) return rc; - /* Select 512 byte sectors */ - pci_write_config_byte(pdev, 0x55, 0xEE); - /* Select PIO0 8bit clocking */ - pci_write_config_byte(pdev, 0x54, 0xB7); + ns87415_fixup(pdev); + return ata_pci_sff_init_one(pdev, ppi, &ns87415_sht, NULL); } @@ -384,6 +389,23 @@ static const struct pci_device_id ns87415_pci_tbl[] = { { } /* terminate list */ }; +#ifdef CONFIG_PM +static int ns87415_reinit_one(struct pci_dev *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + int rc; + + rc = ata_pci_device_do_resume(pdev); + if (rc) + return rc; + + ns87415_fixup(pdev); + + ata_host_resume(host); + return 0; +} +#endif + static struct pci_driver ns87415_pci_driver = { .name = DRV_NAME, .id_table = ns87415_pci_tbl, @@ -391,7 +413,7 @@ static struct pci_driver ns87415_pci_driver = { .remove = ata_pci_remove_one, #ifdef CONFIG_PM .suspend = ata_pci_device_suspend, - .resume = ata_pci_device_resume, + .resume = ns87415_reinit_one, #endif }; diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index 84ac5033ac89..9a8687db6b2d 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -239,7 +239,7 @@ static int oldpiix_init_one (struct pci_dev *pdev, const struct pci_device_id *e static const struct ata_port_info info = { .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, + .mwdma_mask = ATA_MWDMA12_ONLY, .port_ops = &oldpiix_pata_ops, }; const struct ata_port_info *ppi[] = { &info, NULL }; diff --git a/drivers/ata/pata_piccolo.c b/drivers/ata/pata_piccolo.c new file mode 100644 index 000000000000..bfe0180f3efa --- /dev/null +++ b/drivers/ata/pata_piccolo.c @@ -0,0 +1,140 @@ +/* + * pata_piccolo.c - Toshiba Piccolo PATA/SATA controller driver. + * + * This is basically an update to ata_generic.c to add Toshiba Piccolo support + * then split out to keep ata_generic "clean". + * + * Copyright 2005 Red Hat Inc, all rights reserved. + * + * Elements from ide/pci/generic.c + * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org> + * Portions (C) Copyright 2002 Red Hat Inc <alan@redhat.com> + * + * May be copied or modified under the terms of the GNU General Public License + * + * The timing data tables/programming info are courtesy of the NetBSD driver + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <scsi/scsi_host.h> +#include <linux/libata.h> + +#define DRV_NAME "pata_piccolo" +#define DRV_VERSION "0.0.1" + + + +static void tosh_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + static const u16 pio[6] = { /* For reg 0x50 low word & E088 */ + 0x0566, 0x0433, 0x0311, 0x0201, 0x0200, 0x0100 + }; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u16 conf; + pci_read_config_word(pdev, 0x50, &conf); + conf &= 0xE088; + conf |= pio[adev->pio_mode - XFER_PIO_0]; + pci_write_config_word(pdev, 0x50, conf); +} + +static void tosh_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u32 conf; + pci_read_config_dword(pdev, 0x5C, &conf); + conf &= 0x78FFE088; /* Keep the other bits */ + if (adev->dma_mode >= XFER_UDMA_0) { + int udma = adev->dma_mode - XFER_UDMA_0; + conf |= 0x80000000; + conf |= (udma + 2) << 28; + conf |= (2 - udma) * 0x111; /* spread into three nibbles */ + } else { + static const u32 mwdma[4] = { + 0x0655, 0x0200, 0x0200, 0x0100 + }; + conf |= mwdma[adev->dma_mode - XFER_MW_DMA_0]; + } + pci_write_config_dword(pdev, 0x5C, conf); +} + + +static struct scsi_host_template tosh_sht = { + ATA_BMDMA_SHT(DRV_NAME), +}; + +static struct ata_port_operations tosh_port_ops = { + .inherits = &ata_bmdma_port_ops, + .cable_detect = ata_cable_unknown, + .set_piomode = tosh_set_piomode, + .set_dmamode = tosh_set_dmamode +}; + +/** + * ata_tosh_init - attach generic IDE + * @dev: PCI device found + * @id: match entry + * + * Called each time a matching IDE interface is found. We check if the + * interface is one we wish to claim and if so we perform any chip + * specific hacks then let the ATA layer do the heavy lifting. + */ + +static int ata_tosh_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + static const struct ata_port_info info = { + .flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = ATA_PIO5, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA2, + .port_ops = &tosh_port_ops + }; + const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info }; + /* Just one port for the moment */ + return ata_pci_sff_init_one(dev, ppi, &tosh_sht, NULL); +} + +static struct pci_device_id ata_tosh[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), }, + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), }, + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_3), }, + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_5), }, + { 0, }, +}; + +static struct pci_driver ata_tosh_pci_driver = { + .name = DRV_NAME, + .id_table = ata_tosh, + .probe = ata_tosh_init_one, + .remove = ata_pci_remove_one, +#ifdef CONFIG_PM + .suspend = ata_pci_device_suspend, + .resume = ata_pci_device_resume, +#endif +}; + +static int __init ata_tosh_init(void) +{ + return pci_register_driver(&ata_tosh_pci_driver); +} + + +static void __exit ata_tosh_exit(void) +{ + pci_unregister_driver(&ata_tosh_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("Low level driver for Toshiba Piccolo ATA"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, ata_tosh); +MODULE_VERSION(DRV_VERSION); + +module_init(ata_tosh_init); +module_exit(ata_tosh_exit); + diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c index 4401b332eaab..4fd25e737d9a 100644 --- a/drivers/ata/pata_radisys.c +++ b/drivers/ata/pata_radisys.c @@ -139,9 +139,9 @@ static void radisys_set_dmamode (struct ata_port *ap, struct ata_device *adev) pci_read_config_byte(dev, 0x4A, &udma_mode); if (adev->xfer_mode == XFER_UDMA_2) - udma_mode &= ~ (1 << adev->devno); + udma_mode &= ~(2 << (adev->devno * 4)); else /* UDMA 4 */ - udma_mode |= (1 << adev->devno); + udma_mode |= (2 << (adev->devno * 4)); pci_write_config_byte(dev, 0x4A, udma_mode); diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c index c843a1e07c4f..237a24d41a2d 100644 --- a/drivers/ata/pata_rdc.c +++ b/drivers/ata/pata_rdc.c @@ -284,7 +284,7 @@ static struct ata_port_info rdc_port_info = { .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, + .mwdma_mask = ATA_MWDMA12_ONLY, .udma_mask = ATA_UDMA5, .port_ops = &rdc_pata_ops, }; diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c index a5e4dfe60b41..2932998fc4c6 100644 --- a/drivers/ata/pata_rz1000.c +++ b/drivers/ata/pata_rz1000.c @@ -105,11 +105,20 @@ static int rz1000_init_one (struct pci_dev *pdev, const struct pci_device_id *en #ifdef CONFIG_PM static int rz1000_reinit_one(struct pci_dev *pdev) { + struct ata_host *host = dev_get_drvdata(&pdev->dev); + int rc; + + rc = ata_pci_device_do_resume(pdev); + if (rc) + return rc; + /* If this fails on resume (which is a "cant happen" case), we must stop as any progress risks data loss */ if (rz1000_fifo_disable(pdev)) panic("rz1000 fifo"); - return ata_pci_device_resume(pdev); + + ata_host_resume(host); + return 0; } #endif diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c index 4cb649d8d38c..a2ace48a4610 100644 --- a/drivers/ata/pata_sil680.c +++ b/drivers/ata/pata_sil680.c @@ -212,13 +212,11 @@ static struct ata_port_operations sil680_port_ops = { static u8 sil680_init_chip(struct pci_dev *pdev, int *try_mmio) { - u32 class_rev = 0; u8 tmpbyte = 0; - pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class_rev); - class_rev &= 0xff; /* FIXME: double check */ - pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, (class_rev) ? 1 : 255); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, + pdev->revision ? 1 : 255); pci_write_config_byte(pdev, 0x80, 0x00); pci_write_config_byte(pdev, 0x84, 0x00); diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c index 488e77bcd22b..5c30d56dec84 100644 --- a/drivers/ata/pata_sis.c +++ b/drivers/ata/pata_sis.c @@ -2,7 +2,7 @@ * pata_sis.c - SiS ATA driver * * (C) 2005 Red Hat - * (C) 2007 Bartlomiej Zolnierkiewicz + * (C) 2007,2009 Bartlomiej Zolnierkiewicz * * Based upon linux/drivers/ide/pci/sis5513.c * Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org> @@ -829,6 +829,23 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) return ata_pci_sff_init_one(pdev, ppi, &sis_sht, chipset); } +#ifdef CONFIG_PM +static int sis_reinit_one(struct pci_dev *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + int rc; + + rc = ata_pci_device_do_resume(pdev); + if (rc) + return rc; + + sis_fixup(pdev, host->private_data); + + ata_host_resume(host); + return 0; +} +#endif + static const struct pci_device_id sis_pci_tbl[] = { { PCI_VDEVICE(SI, 0x5513), }, /* SiS 5513 */ { PCI_VDEVICE(SI, 0x5518), }, /* SiS 5518 */ @@ -844,7 +861,7 @@ static struct pci_driver sis_pci_driver = { .remove = ata_pci_remove_one, #ifdef CONFIG_PM .suspend = ata_pci_device_suspend, - .resume = ata_pci_device_resume, + .resume = sis_reinit_one, #endif }; diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 88984b803d6d..0d97890af681 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -303,14 +303,21 @@ static void via_do_set_mode(struct ata_port *ap, struct ata_device *adev, int mo } /* Set UDMA unless device is not UDMA capable */ - if (udma_type && t.udma) { - u8 cable80_status; + if (udma_type) { + u8 udma_etc; - /* Get 80-wire cable detection bit */ - pci_read_config_byte(pdev, 0x50 + offset, &cable80_status); - cable80_status &= 0x10; + pci_read_config_byte(pdev, 0x50 + offset, &udma_etc); - pci_write_config_byte(pdev, 0x50 + offset, ut | cable80_status); + /* clear transfer mode bit */ + udma_etc &= ~0x20; + + if (t.udma) { + /* preserve 80-wire cable detection bit */ + udma_etc &= 0x10; + udma_etc |= ut; + } + + pci_write_config_byte(pdev, 0x50 + offset, udma_etc); } } @@ -337,6 +344,32 @@ static void via_set_dmamode(struct ata_port *ap, struct ata_device *adev) } /** + * via_mode_filter - filter buggy device/mode pairs + * @dev: ATA device + * @mask: Mode bitmask + * + * We need to apply some minimal filtering for old controllers and at least + * one breed of Transcend SSD. Return the updated mask. + */ + +static unsigned long via_mode_filter(struct ata_device *dev, unsigned long mask) +{ + struct ata_host *host = dev->link->ap->host; + const struct via_isa_bridge *config = host->private_data; + unsigned char model_num[ATA_ID_PROD_LEN + 1]; + + if (config->id == PCI_DEVICE_ID_VIA_82C586_0) { + ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num)); + if (strcmp(model_num, "TS64GSSD25-M") == 0) { + ata_dev_printk(dev, KERN_WARNING, + "disabling UDMA mode due to reported lockups with this device.\n"); + mask &= ~ ATA_MASK_UDMA; + } + } + return ata_bmdma_mode_filter(dev, mask); +} + +/** * via_tf_load - send taskfile registers to host controller * @ap: Port to which output is sent * @tf: ATA taskfile register set @@ -427,6 +460,7 @@ static struct ata_port_operations via_port_ops = { .prereset = via_pre_reset, .sff_tf_load = via_tf_load, .port_start = via_port_start, + .mode_filter = via_mode_filter, }; static struct ata_port_operations via_port_ops_noirq = { @@ -526,7 +560,7 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) .port_ops = &via_port_ops }; const struct ata_port_info *ppi[] = { NULL, NULL }; - struct pci_dev *isa = NULL; + struct pci_dev *isa; const struct via_isa_bridge *config; static int printed_version; u8 enable; @@ -551,15 +585,13 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if ((isa = pci_get_device(PCI_VENDOR_ID_VIA + !!(config->flags & VIA_BAD_ID), config->id, NULL))) { + u8 rev = isa->revision; + pci_dev_put(isa); - if (isa->revision >= config->rev_min && - isa->revision <= config->rev_max) + if (rev >= config->rev_min && rev <= config->rev_max) break; - pci_dev_put(isa); } - pci_dev_put(isa); - if (!(config->flags & VIA_NO_ENABLES)) { /* 0x40 low bits indicate enabled channels */ pci_read_config_byte(pdev, 0x40 , &enable); diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 172b57e6543f..ce4136eea08f 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -34,7 +34,7 @@ enum { SATA_FSL_HOST_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_PMP | ATA_FLAG_NCQ), + ATA_FLAG_PMP | ATA_FLAG_NCQ | ATA_FLAG_AN), SATA_FSL_MAX_CMDS = SATA_FSL_QUEUE_DEPTH, SATA_FSL_CMD_HDR_SIZE = 16, /* 4 DWORDS */ @@ -43,9 +43,9 @@ enum { /* * SATA-FSL host controller supports a max. of (15+1) direct PRDEs, and * chained indirect PRDEs upto a max count of 63. - * We are allocating an array of 63 PRDEs contigiously, but PRDE#15 will + * We are allocating an array of 63 PRDEs contiguously, but PRDE#15 will * be setup as an indirect descriptor, pointing to it's next - * (contigious) PRDE. Though chained indirect PRDE arrays are + * (contiguous) PRDE. Though chained indirect PRDE arrays are * supported,it will be more efficient to use a direct PRDT and * a single chain/link to indirect PRDE array/PRDT. */ @@ -132,7 +132,7 @@ enum { INT_ON_SINGL_DEVICE_ERR = (1 << 1), INT_ON_CMD_COMPLETE = 1, - INT_ON_ERROR = INT_ON_FATAL_ERR | + INT_ON_ERROR = INT_ON_FATAL_ERR | INT_ON_SNOTIFY_UPDATE | INT_ON_PHYRDY_CHG | INT_ON_SINGL_DEVICE_ERR, /* @@ -153,7 +153,7 @@ enum { IE_ON_CMD_COMPLETE = 1, DEFAULT_PORT_IRQ_ENABLE_MASK = IE_ON_FATAL_ERR | IE_ON_PHYRDY_CHG | - IE_ON_SIGNATURE_UPDATE | + IE_ON_SIGNATURE_UPDATE | IE_ON_SNOTIFY_UPDATE | IE_ON_SINGL_DEVICE_ERR | IE_ON_CMD_COMPLETE, EXT_INDIRECT_SEG_PRD_FLAG = (1 << 31), @@ -314,7 +314,7 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc, u32 ttl_dwords = 0; /* - * NOTE : direct & indirect prdt's are contigiously allocated + * NOTE : direct & indirect prdt's are contiguously allocated */ struct prde *prd = (struct prde *)&((struct command_desc *) cmd_desc)->prdt; @@ -992,9 +992,8 @@ static void sata_fsl_error_intr(struct ata_port *ap) */ sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError); - if (unlikely(SError & 0xFFFF0000)) { + if (unlikely(SError & 0xFFFF0000)) sata_fsl_scr_write(&ap->link, SCR_ERROR, SError); - } DPRINTK("error_intr,hStat=0x%x,CE=0x%x,DE =0x%x,SErr=0x%x\n", hstatus, cereg, ioread32(hcr_base + DE), SError); @@ -1007,6 +1006,10 @@ static void sata_fsl_error_intr(struct ata_port *ap) freeze = 1; } + /* Handle SDB FIS receive & notify update */ + if (hstatus & INT_ON_SNOTIFY_UPDATE) + sata_async_notification(ap); + /* Handle PHYRDY change notification */ if (hstatus & INT_ON_PHYRDY_CHG) { DPRINTK("SATA FSL: PHYRDY change indication\n"); @@ -1070,9 +1073,9 @@ static void sata_fsl_error_intr(struct ata_port *ap) } /* record error info */ - if (qc) { + if (qc) qc->err_mask |= err_mask; - } else + else ehi->err_mask |= err_mask; ehi->action |= action; @@ -1103,7 +1106,6 @@ static void sata_fsl_host_intr(struct ata_port *ap) if (unlikely(SError & 0xFFFF0000)) { DPRINTK("serror @host_intr : 0x%x\n", SError); sata_fsl_error_intr(ap); - } if (unlikely(hstatus & INT_ON_ERROR)) { diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 6f5093b7c8c5..a8a7be0d06ff 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -2217,7 +2217,7 @@ static unsigned int mv_qc_issue_fis(struct ata_queued_cmd *qc) int err = 0; ata_tf_to_fis(&qc->tf, link->pmp, 1, (void *)fis); - err = mv_send_fis(ap, fis, sizeof(fis) / sizeof(fis[0])); + err = mv_send_fis(ap, fis, ARRAY_SIZE(fis)); if (err) return err; diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 1eb4e020eb5c..0c82d335c55d 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -1975,7 +1975,7 @@ static int nv_swncq_slave_config(struct scsi_device *sdev) ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num)); if (strncmp(model_num, "Maxtor", 6) == 0) { - ata_scsi_change_queue_depth(sdev, 1); + ata_scsi_change_queue_depth(sdev, 1, SCSI_QDEPTH_DEFAULT); ata_dev_printk(dev, KERN_NOTICE, "Disabling SWNCQ mode (depth %x)\n", sdev->queue_depth); } diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index e6946fc527d0..1370df6c420c 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -417,6 +417,10 @@ static struct ata_port_operations sil24_ops = { #endif }; +static int sata_sil24_msi; /* Disable MSI */ +module_param_named(msi, sata_sil24_msi, bool, S_IRUGO); +MODULE_PARM_DESC(msi, "Enable MSI (Default: false)"); + /* * Use bits 30-31 of port_flags to encode available port numbers. * Current maxium is 4. @@ -1340,6 +1344,11 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) sil24_init_controller(host); + if (sata_sil24_msi && !pci_enable_msi(pdev)) { + dev_printk(KERN_INFO, &pdev->dev, "Using MSI\n"); + pci_intx(pdev, 0); + } + pci_set_master(pdev); return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED, &sil24_sht); diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index 66e181345b3a..8af23411743c 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -2351,6 +2351,7 @@ static void __init amb_check_args (void) { MODULE_AUTHOR(maintainer_string); MODULE_DESCRIPTION(description_string); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("atmsar11.fw"); module_param(debug, ushort, 0644); module_param(cmds, uint, 0); module_param(txs, uint, 0); diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index f766cc46b4c4..bc53fed89b1e 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -2906,8 +2906,8 @@ fore200e_proc_read(struct atm_dev *dev, loff_t* pos, char* page) u32 media_index = FORE200E_MEDIA_INDEX(fore200e->bus->read(&fore200e->cp_queues->media_type)); u32 oc3_index; - if ((media_index < 0) || (media_index > 4)) - media_index = 5; + if (media_index > 4) + media_index = 5; switch (fore200e->loop_mode) { case ATM_LM_NONE: oc3_index = 0; diff --git a/drivers/atm/he.c b/drivers/atm/he.c index 70667033a568..e90665876c47 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -2739,7 +2739,7 @@ he_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user *arg) spin_lock_irqsave(&he_dev->global_lock, flags); switch (reg.type) { case HE_REGTYPE_PCI: - if (reg.addr < 0 || reg.addr >= HE_REGMAP_SIZE) { + if (reg.addr >= HE_REGMAP_SIZE) { err = -EINVAL; break; } diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index b2c1b37ab2e4..f734b345ac71 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -1132,7 +1132,7 @@ static int rx_pkt(struct atm_dev *dev) IF_ERR(printk(" cause: packet time out\n");) } else { - IF_ERR(printk(" cause: buffer over flow\n");) + IF_ERR(printk(" cause: buffer overflow\n");) } goto out_free_desc; } diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index c5f5186d62a3..51eed679a059 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -142,6 +142,9 @@ MODULE_AUTHOR("Traverse Technologies <support@traverse.com.au>"); MODULE_DESCRIPTION("Solos PCI driver"); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("solos-FPGA.bin"); +MODULE_FIRMWARE("solos-Firmware.bin"); +MODULE_FIRMWARE("solos-db-FPGA.bin"); MODULE_PARM_DESC(reset, "Reset Solos chips on startup"); MODULE_PARM_DESC(atmdebug, "Print ATM data"); MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade"); @@ -528,34 +531,37 @@ static int flash_upgrade(struct solos_card *card, int chip) int numblocks = 0; int offset; - if (chip == 0) { + switch (chip) { + case 0: fw_name = "solos-FPGA.bin"; blocksize = FPGA_BLOCK; - } - - if (chip == 1) { + break; + case 1: fw_name = "solos-Firmware.bin"; blocksize = SOLOS_BLOCK; - } - - if (chip == 2){ + break; + case 2: if (card->fpga_version > LEGACY_BUFFERS){ fw_name = "solos-db-FPGA.bin"; blocksize = FPGA_BLOCK; } else { - dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n"); + dev_info(&card->dev->dev, "FPGA version doesn't support" + " daughter board upgrades\n"); return -EPERM; } - } - - if (chip == 3){ + break; + case 3: if (card->fpga_version > LEGACY_BUFFERS){ fw_name = "solos-Firmware.bin"; blocksize = SOLOS_BLOCK; } else { - dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n"); - return -EPERM; + dev_info(&card->dev->dev, "FPGA version doesn't support" + " daughter board upgrades\n"); + return -EPERM; } + break; + default: + return -ENODEV; } if (request_firmware(&fw, fw_name, &card->dev->dev)) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 979d159b5cd1..ee95c76bfd3d 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -188,7 +188,7 @@ EXPORT_SYMBOL_GPL(wait_for_device_probe); * @dev: device to try to bind to the driver * * This function returns -ENODEV if the device is not registered, - * 1 if the device is bound sucessfully and 0 otherwise. + * 1 if the device is bound successfully and 0 otherwise. * * This function must be called with @dev->sem held. When called for a * USB interface, @dev->parent->sem must be held as well. diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 846d89e3d122..5a01ecef4af3 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -185,6 +185,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) } dev->power.runtime_status = RPM_SUSPENDING; + dev->power.deferred_resume = false; if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { spin_unlock_irq(&dev->power.lock); @@ -200,7 +201,6 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) if (retval) { dev->power.runtime_status = RPM_ACTIVE; pm_runtime_cancel_pending(dev); - dev->power.deferred_resume = false; if (retval == -EAGAIN || retval == -EBUSY) { notify = true; @@ -217,7 +217,6 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) wake_up_all(&dev->power.wait_queue); if (dev->power.deferred_resume) { - dev->power.deferred_resume = false; __pm_runtime_resume(dev, false); retval = -EAGAIN; goto out; @@ -626,6 +625,8 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay) goto out; dev->power.timer_expires = jiffies + msecs_to_jiffies(delay); + if (!dev->power.timer_expires) + dev->power.timer_expires = 1; mod_timer(&dev->power.suspend_timer, dev->power.timer_expires); out: @@ -659,13 +660,17 @@ static int __pm_request_resume(struct device *dev) pm_runtime_deactivate_timer(dev); + if (dev->power.runtime_status == RPM_SUSPENDING) { + dev->power.deferred_resume = true; + return retval; + } if (dev->power.request_pending) { /* If non-resume request is pending, we can overtake it. */ dev->power.request = retval ? RPM_REQ_NONE : RPM_REQ_RESUME; return retval; - } else if (retval) { - return retval; } + if (retval) + return retval; dev->power.request = RPM_REQ_RESUME; dev->power.request_pending = true; @@ -777,7 +782,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) } if (parent) { - spin_lock(&parent->power.lock); + spin_lock_nested(&parent->power.lock, SINGLE_DEPTH_NESTING); /* * It is invalid to put an active child under a parent that is @@ -786,12 +791,10 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) */ if (!parent->power.disable_depth && !parent->power.ignore_children - && parent->power.runtime_status != RPM_ACTIVE) { + && parent->power.runtime_status != RPM_ACTIVE) error = -EBUSY; - } else { - if (dev->power.runtime_status == RPM_SUSPENDED) - atomic_inc(&parent->power.child_count); - } + else if (dev->power.runtime_status == RPM_SUSPENDED) + atomic_inc(&parent->power.child_count); spin_unlock(&parent->power.lock); diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 1d886e079c58..77bfce52e9ca 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -271,6 +271,8 @@ config BLK_DEV_CRYPTOLOOP instead, which can be configured to be on-disk compatible with the cryptoloop device. +source "drivers/block/drbd/Kconfig" + config BLK_DEV_NBD tristate "Network block device support" depends on NET diff --git a/drivers/block/Makefile b/drivers/block/Makefile index cdaa3f8fddf0..aff5ac925c34 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -36,5 +36,6 @@ obj-$(CONFIG_BLK_DEV_UB) += ub.o obj-$(CONFIG_BLK_DEV_HD) += hd.o obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o +obj-$(CONFIG_BLK_DEV_DRBD) += drbd/ swim_mod-objs := swim.o swim_asm.o diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 847a9e57570a..a5af1d6dda8b 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1478,10 +1478,7 @@ void do_fd_request(struct request_queue * q) stdma_lock(floppy_irq, NULL); atari_disable_irq( IRQ_MFP_FDC ); - local_save_flags(flags); /* The request function is called with ints - local_irq_disable(); * disabled... so must save the IPL for later */ redo_fd_request(); - local_irq_restore(flags); atari_enable_irq( IRQ_MFP_FDC ); } diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 92b126394fa1..873e594860d3 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -179,19 +179,17 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time, int via_ioctl); static int deregister_disk(ctlr_info_t *h, int drv_index, int clear_all, int via_ioctl); -static void cciss_read_capacity(int ctlr, int logvol, int withirq, +static void cciss_read_capacity(int ctlr, int logvol, sector_t *total_size, unsigned int *block_size); -static void cciss_read_capacity_16(int ctlr, int logvol, int withirq, +static void cciss_read_capacity_16(int ctlr, int logvol, sector_t *total_size, unsigned int *block_size); static void cciss_geometry_inquiry(int ctlr, int logvol, - int withirq, sector_t total_size, + sector_t total_size, unsigned int block_size, InquiryData_struct *inq_buff, drive_info_struct *drv); static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *, __u32); static void start_io(ctlr_info_t *h); -static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size, - __u8 page_code, unsigned char *scsi3addr, int cmd_type); static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size, __u8 page_code, unsigned char scsi3addr[], int cmd_type); @@ -424,12 +422,9 @@ cciss_proc_write(struct file *file, const char __user *buf, if (strncmp(ENGAGE_SCSI, buffer, sizeof ENGAGE_SCSI - 1) == 0) { struct seq_file *seq = file->private_data; ctlr_info_t *h = seq->private; - int rc; - rc = cciss_engage_scsi(h->ctlr); - if (rc != 0) - err = -rc; - else + err = cciss_engage_scsi(h->ctlr); + if (err == 0) err = length; } else #endif /* CONFIG_CISS_SCSI_TAPE */ @@ -1657,9 +1652,11 @@ static void cciss_softirq_done(struct request *rq) { CommandList_struct *cmd = rq->completion_data; ctlr_info_t *h = hba[cmd->ctlr]; + SGDescriptor_struct *curr_sg = cmd->SG; unsigned long flags; u64bit temp64; int i, ddir; + int sg_index = 0; if (cmd->Request.Type.Direction == XFER_READ) ddir = PCI_DMA_FROMDEVICE; @@ -1669,9 +1666,22 @@ static void cciss_softirq_done(struct request *rq) /* command did not need to be retried */ /* unmap the DMA mapping for all the scatter gather elements */ for (i = 0; i < cmd->Header.SGList; i++) { - temp64.val32.lower = cmd->SG[i].Addr.lower; - temp64.val32.upper = cmd->SG[i].Addr.upper; - pci_unmap_page(h->pdev, temp64.val, cmd->SG[i].Len, ddir); + if (curr_sg[sg_index].Ext == CCISS_SG_CHAIN) { + temp64.val32.lower = cmd->SG[i].Addr.lower; + temp64.val32.upper = cmd->SG[i].Addr.upper; + pci_dma_sync_single_for_cpu(h->pdev, temp64.val, + cmd->SG[i].Len, ddir); + pci_unmap_single(h->pdev, temp64.val, + cmd->SG[i].Len, ddir); + /* Point to the next block */ + curr_sg = h->cmd_sg_list[cmd->cmdindex]->sgchain; + sg_index = 0; + } + temp64.val32.lower = curr_sg[sg_index].Addr.lower; + temp64.val32.upper = curr_sg[sg_index].Addr.upper; + pci_unmap_page(h->pdev, temp64.val, curr_sg[sg_index].Len, + ddir); + ++sg_index; } #ifdef CCISS_DEBUG @@ -1701,7 +1711,7 @@ static inline void log_unit_to_scsi3addr(ctlr_info_t *h, * via the inquiry page 0. Model, vendor, and rev are set to empty strings if * they cannot be read. */ -static void cciss_get_device_descr(int ctlr, int logvol, int withirq, +static void cciss_get_device_descr(int ctlr, int logvol, char *vendor, char *model, char *rev) { int rc; @@ -1717,14 +1727,8 @@ static void cciss_get_device_descr(int ctlr, int logvol, int withirq, return; log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol); - if (withirq) - rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf, - sizeof(InquiryData_struct), 0, - scsi3addr, TYPE_CMD); - else - rc = sendcmd(CISS_INQUIRY, ctlr, inq_buf, - sizeof(InquiryData_struct), 0, - scsi3addr, TYPE_CMD); + rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf, sizeof(*inq_buf), 0, + scsi3addr, TYPE_CMD); if (rc == IO_OK) { memcpy(vendor, &inq_buf->data_byte[8], VENDOR_LEN); vendor[VENDOR_LEN] = '\0'; @@ -1743,7 +1747,7 @@ static void cciss_get_device_descr(int ctlr, int logvol, int withirq, * number cannot be had, for whatever reason, 16 bytes of 0xff * are returned instead. */ -static void cciss_get_serial_no(int ctlr, int logvol, int withirq, +static void cciss_get_serial_no(int ctlr, int logvol, unsigned char *serial_no, int buflen) { #define PAGE_83_INQ_BYTES 64 @@ -1759,12 +1763,8 @@ static void cciss_get_serial_no(int ctlr, int logvol, int withirq, return; memset(serial_no, 0, buflen); log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol); - if (withirq) - rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf, - PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD); - else - rc = sendcmd(CISS_INQUIRY, ctlr, buf, - PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD); + rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf, + PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD); if (rc == IO_OK) memcpy(serial_no, &buf[8], buflen); kfree(buf); @@ -1793,10 +1793,10 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk, blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask); /* This is a hardware imposed limit. */ - blk_queue_max_hw_segments(disk->queue, MAXSGENTRIES); + blk_queue_max_hw_segments(disk->queue, h->maxsgentries); /* This is a limit in the driver and could be eliminated. */ - blk_queue_max_phys_segments(disk->queue, MAXSGENTRIES); + blk_queue_max_phys_segments(disk->queue, h->maxsgentries); blk_queue_max_sectors(disk->queue, h->cciss_max_sectors); @@ -1852,18 +1852,16 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, /* testing to see if 16-byte CDBs are already being used */ if (h->cciss_read == CCISS_READ_16) { - cciss_read_capacity_16(h->ctlr, drv_index, 1, + cciss_read_capacity_16(h->ctlr, drv_index, &total_size, &block_size); } else { - cciss_read_capacity(ctlr, drv_index, 1, - &total_size, &block_size); - + cciss_read_capacity(ctlr, drv_index, &total_size, &block_size); /* if read_capacity returns all F's this volume is >2TB */ /* in size so we switch to 16-byte CDB's for all */ /* read/write ops */ if (total_size == 0xFFFFFFFFULL) { - cciss_read_capacity_16(ctlr, drv_index, 1, + cciss_read_capacity_16(ctlr, drv_index, &total_size, &block_size); h->cciss_read = CCISS_READ_16; h->cciss_write = CCISS_WRITE_16; @@ -1873,14 +1871,14 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, } } - cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size, + cciss_geometry_inquiry(ctlr, drv_index, total_size, block_size, inq_buff, drvinfo); drvinfo->block_size = block_size; drvinfo->nr_blocks = total_size + 1; - cciss_get_device_descr(ctlr, drv_index, 1, drvinfo->vendor, + cciss_get_device_descr(ctlr, drv_index, drvinfo->vendor, drvinfo->model, drvinfo->rev); - cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no, + cciss_get_serial_no(ctlr, drv_index, drvinfo->serial_no, sizeof(drvinfo->serial_no)); /* Save the lunid in case we deregister the disk, below. */ memcpy(drvinfo->LunID, h->drv[drv_index]->LunID, @@ -2531,6 +2529,8 @@ static int check_target_status(ctlr_info_t *h, CommandList_struct *c) case 0: return IO_OK; /* no sense */ case 1: return IO_OK; /* recovered error */ default: + if (check_for_unit_attention(h, c)) + return IO_NEEDS_RETRY; printk(KERN_WARNING "cciss%d: cmd 0x%02x " "check condition, sense key = 0x%02x\n", h->ctlr, c->Request.CDB[0], @@ -2672,7 +2672,7 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size, } static void cciss_geometry_inquiry(int ctlr, int logvol, - int withirq, sector_t total_size, + sector_t total_size, unsigned int block_size, InquiryData_struct *inq_buff, drive_info_struct *drv) @@ -2683,14 +2683,8 @@ static void cciss_geometry_inquiry(int ctlr, int logvol, memset(inq_buff, 0, sizeof(InquiryData_struct)); log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol); - if (withirq) - return_code = sendcmd_withirq(CISS_INQUIRY, ctlr, - inq_buff, sizeof(*inq_buff), - 0xC1, scsi3addr, TYPE_CMD); - else - return_code = sendcmd(CISS_INQUIRY, ctlr, inq_buff, - sizeof(*inq_buff), 0xC1, scsi3addr, - TYPE_CMD); + return_code = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buff, + sizeof(*inq_buff), 0xC1, scsi3addr, TYPE_CMD); if (return_code == IO_OK) { if (inq_buff->data_byte[8] == 0xFF) { printk(KERN_WARNING @@ -2723,7 +2717,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol, } static void -cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size, +cciss_read_capacity(int ctlr, int logvol, sector_t *total_size, unsigned int *block_size) { ReadCapdata_struct *buf; @@ -2737,14 +2731,8 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size, } log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol); - if (withirq) - return_code = sendcmd_withirq(CCISS_READ_CAPACITY, - ctlr, buf, sizeof(ReadCapdata_struct), - 0, scsi3addr, TYPE_CMD); - else - return_code = sendcmd(CCISS_READ_CAPACITY, - ctlr, buf, sizeof(ReadCapdata_struct), - 0, scsi3addr, TYPE_CMD); + return_code = sendcmd_withirq(CCISS_READ_CAPACITY, ctlr, buf, + sizeof(ReadCapdata_struct), 0, scsi3addr, TYPE_CMD); if (return_code == IO_OK) { *total_size = be32_to_cpu(*(__be32 *) buf->total_size); *block_size = be32_to_cpu(*(__be32 *) buf->block_size); @@ -2756,8 +2744,8 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size, kfree(buf); } -static void -cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size, unsigned int *block_size) +static void cciss_read_capacity_16(int ctlr, int logvol, + sector_t *total_size, unsigned int *block_size) { ReadCapdata_struct_16 *buf; int return_code; @@ -2770,16 +2758,9 @@ cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size, } log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol); - if (withirq) { - return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16, - ctlr, buf, sizeof(ReadCapdata_struct_16), - 0, scsi3addr, TYPE_CMD); - } - else { - return_code = sendcmd(CCISS_READ_CAPACITY_16, - ctlr, buf, sizeof(ReadCapdata_struct_16), - 0, scsi3addr, TYPE_CMD); - } + return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16, + ctlr, buf, sizeof(ReadCapdata_struct_16), + 0, scsi3addr, TYPE_CMD); if (return_code == IO_OK) { *total_size = be64_to_cpu(*(__be64 *) buf->total_size); *block_size = be32_to_cpu(*(__be32 *) buf->block_size); @@ -2820,13 +2801,13 @@ static int cciss_revalidate(struct gendisk *disk) return 1; } if (h->cciss_read == CCISS_READ_10) { - cciss_read_capacity(h->ctlr, logvol, 1, + cciss_read_capacity(h->ctlr, logvol, &total_size, &block_size); } else { - cciss_read_capacity_16(h->ctlr, logvol, 1, + cciss_read_capacity_16(h->ctlr, logvol, &total_size, &block_size); } - cciss_geometry_inquiry(h->ctlr, logvol, 1, total_size, block_size, + cciss_geometry_inquiry(h->ctlr, logvol, total_size, block_size, inq_buff, drv); blk_queue_logical_block_size(drv->queue, drv->block_size); @@ -2837,167 +2818,6 @@ static int cciss_revalidate(struct gendisk *disk) } /* - * Wait polling for a command to complete. - * The memory mapped FIFO is polled for the completion. - * Used only at init time, interrupts from the HBA are disabled. - */ -static unsigned long pollcomplete(int ctlr) -{ - unsigned long done; - int i; - - /* Wait (up to 20 seconds) for a command to complete */ - - for (i = 20 * HZ; i > 0; i--) { - done = hba[ctlr]->access.command_completed(hba[ctlr]); - if (done == FIFO_EMPTY) - schedule_timeout_uninterruptible(1); - else - return done; - } - /* Invalid address to tell caller we ran out of time */ - return 1; -} - -/* Send command c to controller h and poll for it to complete. - * Turns interrupts off on the board. Used at driver init time - * and during SCSI error recovery. - */ -static int sendcmd_core(ctlr_info_t *h, CommandList_struct *c) -{ - int i; - unsigned long complete; - int status = IO_ERROR; - u64bit buff_dma_handle; - -resend_cmd1: - - /* Disable interrupt on the board. */ - h->access.set_intr_mask(h, CCISS_INTR_OFF); - - /* Make sure there is room in the command FIFO */ - /* Actually it should be completely empty at this time */ - /* unless we are in here doing error handling for the scsi */ - /* tape side of the driver. */ - for (i = 200000; i > 0; i--) { - /* if fifo isn't full go */ - if (!(h->access.fifo_full(h))) - break; - udelay(10); - printk(KERN_WARNING "cciss cciss%d: SendCmd FIFO full," - " waiting!\n", h->ctlr); - } - h->access.submit_command(h, c); /* Send the cmd */ - do { - complete = pollcomplete(h->ctlr); - -#ifdef CCISS_DEBUG - printk(KERN_DEBUG "cciss: command completed\n"); -#endif /* CCISS_DEBUG */ - - if (complete == 1) { - printk(KERN_WARNING - "cciss cciss%d: SendCmd Timeout out, " - "No command list address returned!\n", h->ctlr); - status = IO_ERROR; - break; - } - - /* Make sure it's the command we're expecting. */ - if ((complete & ~CISS_ERROR_BIT) != c->busaddr) { - printk(KERN_WARNING "cciss%d: Unexpected command " - "completion.\n", h->ctlr); - continue; - } - - /* It is our command. If no error, we're done. */ - if (!(complete & CISS_ERROR_BIT)) { - status = IO_OK; - break; - } - - /* There is an error... */ - - /* if data overrun or underun on Report command ignore it */ - if (((c->Request.CDB[0] == CISS_REPORT_LOG) || - (c->Request.CDB[0] == CISS_REPORT_PHYS) || - (c->Request.CDB[0] == CISS_INQUIRY)) && - ((c->err_info->CommandStatus == CMD_DATA_OVERRUN) || - (c->err_info->CommandStatus == CMD_DATA_UNDERRUN))) { - complete = c->busaddr; - status = IO_OK; - break; - } - - if (c->err_info->CommandStatus == CMD_UNSOLICITED_ABORT) { - printk(KERN_WARNING "cciss%d: unsolicited abort %p\n", - h->ctlr, c); - if (c->retry_count < MAX_CMD_RETRIES) { - printk(KERN_WARNING "cciss%d: retrying %p\n", - h->ctlr, c); - c->retry_count++; - /* erase the old error information */ - memset(c->err_info, 0, sizeof(c->err_info)); - goto resend_cmd1; - } - printk(KERN_WARNING "cciss%d: retried %p too many " - "times\n", h->ctlr, c); - status = IO_ERROR; - break; - } - - if (c->err_info->CommandStatus == CMD_UNABORTABLE) { - printk(KERN_WARNING "cciss%d: command could not be " - "aborted.\n", h->ctlr); - status = IO_ERROR; - break; - } - - if (c->err_info->CommandStatus == CMD_TARGET_STATUS) { - status = check_target_status(h, c); - break; - } - - printk(KERN_WARNING "cciss%d: sendcmd error\n", h->ctlr); - printk(KERN_WARNING "cmd = 0x%02x, CommandStatus = 0x%02x\n", - c->Request.CDB[0], c->err_info->CommandStatus); - status = IO_ERROR; - break; - - } while (1); - - /* unlock the data buffer from DMA */ - buff_dma_handle.val32.lower = c->SG[0].Addr.lower; - buff_dma_handle.val32.upper = c->SG[0].Addr.upper; - pci_unmap_single(h->pdev, (dma_addr_t) buff_dma_handle.val, - c->SG[0].Len, PCI_DMA_BIDIRECTIONAL); - return status; -} - -/* - * Send a command to the controller, and wait for it to complete. - * Used at init time, and during SCSI error recovery. - */ -static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size, - __u8 page_code, unsigned char *scsi3addr, int cmd_type) -{ - CommandList_struct *c; - int status; - - c = cmd_alloc(hba[ctlr], 1); - if (!c) { - printk(KERN_WARNING "cciss: unable to get memory"); - return IO_ERROR; - } - status = fill_cmd(c, cmd, ctlr, buff, size, page_code, - scsi3addr, cmd_type); - if (status == IO_OK) - status = sendcmd_core(hba[ctlr], c); - cmd_free(hba[ctlr], c, 1); - return status; -} - -/* * Map (physical) PCI mem into (virtual) kernel space */ static void __iomem *remap_pci_mem(ulong base, ulong size) @@ -3255,9 +3075,13 @@ static void do_cciss_request(struct request_queue *q) int seg; struct request *creq; u64bit temp64; - struct scatterlist tmp_sg[MAXSGENTRIES]; + struct scatterlist *tmp_sg; + SGDescriptor_struct *curr_sg; drive_info_struct *drv; int i, dir; + int nseg = 0; + int sg_index = 0; + int chained = 0; /* We call start_io here in case there is a command waiting on the * queue that has not been sent. @@ -3270,13 +3094,14 @@ static void do_cciss_request(struct request_queue *q) if (!creq) goto startio; - BUG_ON(creq->nr_phys_segments > MAXSGENTRIES); + BUG_ON(creq->nr_phys_segments > h->maxsgentries); if ((c = cmd_alloc(h, 1)) == NULL) goto full; blk_start_request(creq); + tmp_sg = h->scatter_list[c->cmdindex]; spin_unlock_irq(q->queue_lock); c->cmd_type = CMD_RWREQ; @@ -3305,7 +3130,7 @@ static void do_cciss_request(struct request_queue *q) (int)blk_rq_pos(creq), (int)blk_rq_sectors(creq)); #endif /* CCISS_DEBUG */ - sg_init_table(tmp_sg, MAXSGENTRIES); + sg_init_table(tmp_sg, h->maxsgentries); seg = blk_rq_map_sg(q, creq, tmp_sg); /* get the DMA records for the setup */ @@ -3314,25 +3139,70 @@ static void do_cciss_request(struct request_queue *q) else dir = PCI_DMA_TODEVICE; + curr_sg = c->SG; + sg_index = 0; + chained = 0; + for (i = 0; i < seg; i++) { - c->SG[i].Len = tmp_sg[i].length; + if (((sg_index+1) == (h->max_cmd_sgentries)) && + !chained && ((seg - i) > 1)) { + nseg = seg - i; + curr_sg[sg_index].Len = (nseg) * + sizeof(SGDescriptor_struct); + curr_sg[sg_index].Ext = CCISS_SG_CHAIN; + + /* Point to next chain block. */ + curr_sg = h->cmd_sg_list[c->cmdindex]->sgchain; + sg_index = 0; + chained = 1; + } + curr_sg[sg_index].Len = tmp_sg[i].length; temp64.val = (__u64) pci_map_page(h->pdev, sg_page(&tmp_sg[i]), - tmp_sg[i].offset, - tmp_sg[i].length, dir); - c->SG[i].Addr.lower = temp64.val32.lower; - c->SG[i].Addr.upper = temp64.val32.upper; - c->SG[i].Ext = 0; // we are not chaining + tmp_sg[i].offset, + tmp_sg[i].length, dir); + curr_sg[sg_index].Addr.lower = temp64.val32.lower; + curr_sg[sg_index].Addr.upper = temp64.val32.upper; + curr_sg[sg_index].Ext = 0; /* we are not chaining */ + + ++sg_index; + } + + if (chained) { + int len; + curr_sg = c->SG; + sg_index = h->max_cmd_sgentries - 1; + len = curr_sg[sg_index].Len; + /* Setup pointer to next chain block. + * Fill out last element in current chain + * block with address of next chain block. + */ + temp64.val = pci_map_single(h->pdev, + h->cmd_sg_list[c->cmdindex]->sgchain, + len, dir); + + h->cmd_sg_list[c->cmdindex]->sg_chain_dma = temp64.val; + curr_sg[sg_index].Addr.lower = temp64.val32.lower; + curr_sg[sg_index].Addr.upper = temp64.val32.upper; + + pci_dma_sync_single_for_device(h->pdev, + h->cmd_sg_list[c->cmdindex]->sg_chain_dma, + len, dir); } + /* track how many SG entries we are using */ if (seg > h->maxSG) h->maxSG = seg; #ifdef CCISS_DEBUG - printk(KERN_DEBUG "cciss: Submitting %u sectors in %d segments\n", - blk_rq_sectors(creq), seg); + printk(KERN_DEBUG "cciss: Submitting %ld sectors in %d segments " + "chained[%d]\n", + blk_rq_sectors(creq), seg, chained); #endif /* CCISS_DEBUG */ - c->Header.SGList = c->Header.SGTotal = seg; + c->Header.SGList = c->Header.SGTotal = seg + chained; + if (seg > h->max_cmd_sgentries) + c->Header.SGList = h->max_cmd_sgentries; + if (likely(blk_fs_request(creq))) { if(h->cciss_read == CCISS_READ_10) { c->Request.CDB[1] = 0; @@ -3513,28 +3383,33 @@ static int add_to_scan_list(struct ctlr_info *h) * @h: Pointer to the controller. * * Removes the controller from the rescan queue if present. Blocks if - * the controller is currently conducting a rescan. + * the controller is currently conducting a rescan. The controller + * can be in one of three states: + * 1. Doesn't need a scan + * 2. On the scan list, but not scanning yet (we remove it) + * 3. Busy scanning (and not on the list). In this case we want to wait for + * the scan to complete to make sure the scanning thread for this + * controller is completely idle. **/ static void remove_from_scan_list(struct ctlr_info *h) { struct ctlr_info *test_h, *tmp_h; - int scanning = 0; mutex_lock(&scan_mutex); list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) { - if (test_h == h) { + if (test_h == h) { /* state 2. */ list_del(&h->scan_list); complete_all(&h->scan_wait); mutex_unlock(&scan_mutex); return; } } - if (&h->busy_scanning) - scanning = 0; - mutex_unlock(&scan_mutex); - - if (scanning) + if (h->busy_scanning) { /* state 3. */ + mutex_unlock(&scan_mutex); wait_for_completion(&h->scan_wait); + } else { /* state 1, nothing to do. */ + mutex_unlock(&scan_mutex); + } } /** @@ -3573,13 +3448,11 @@ static int scan_thread(void *data) h->busy_scanning = 1; mutex_unlock(&scan_mutex); - if (h) { - rebuild_lun_table(h, 0, 0); - complete_all(&h->scan_wait); - mutex_lock(&scan_mutex); - h->busy_scanning = 0; - mutex_unlock(&scan_mutex); - } + rebuild_lun_table(h, 0, 0); + complete_all(&h->scan_wait); + mutex_lock(&scan_mutex); + h->busy_scanning = 0; + mutex_unlock(&scan_mutex); } } @@ -3605,8 +3478,22 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c) case REPORT_LUNS_CHANGED: printk(KERN_WARNING "cciss%d: report LUN data " "changed\n", h->ctlr); - add_to_scan_list(h); - wake_up_process(cciss_scan_thread); + /* + * Here, we could call add_to_scan_list and wake up the scan thread, + * except that it's quite likely that we will get more than one + * REPORT_LUNS_CHANGED condition in quick succession, which means + * that those which occur after the first one will likely happen + * *during* the scan_thread's rescan. And the rescan code is not + * robust enough to restart in the middle, undoing what it has already + * done, and it's not clear that it's even possible to do this, since + * part of what it does is notify the block layer, which starts + * doing it's own i/o to read partition tables and so on, and the + * driver doesn't have visibility to know what might need undoing. + * In any event, if possible, it is horribly complicated to get right + * so we just don't do it for now. + * + * Note: this REPORT_LUNS_CHANGED condition only occurs on the MSA2012. + */ return 1; break; case POWER_OR_RESET: @@ -3888,6 +3775,23 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) * leave a little room for ioctl calls. */ c->max_commands = readl(&(c->cfgtable->CmdsOutMax)); + c->maxsgentries = readl(&(c->cfgtable->MaxSGElements)); + + /* + * Limit native command to 32 s/g elements to save dma'able memory. + * Howvever spec says if 0, use 31 + */ + + c->max_cmd_sgentries = 31; + if (c->maxsgentries > 512) { + c->max_cmd_sgentries = 32; + c->chainsize = c->maxsgentries - c->max_cmd_sgentries + 1; + c->maxsgentries -= 1; /* account for chain pointer */ + } else { + c->maxsgentries = 31; /* Default to traditional value */ + c->chainsize = 0; /* traditional */ + } + c->product_name = products[prod_index].product_name; c->access = *(products[prod_index].access); c->nr_cmds = c->max_commands - 4; @@ -4214,6 +4118,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, { int i; int j = 0; + int k = 0; int rc; int dac, return_code; InquiryData_struct *inq_buff; @@ -4317,6 +4222,53 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, printk(KERN_ERR "cciss: out of memory"); goto clean4; } + + /* Need space for temp scatter list */ + hba[i]->scatter_list = kmalloc(hba[i]->max_commands * + sizeof(struct scatterlist *), + GFP_KERNEL); + for (k = 0; k < hba[i]->nr_cmds; k++) { + hba[i]->scatter_list[k] = kmalloc(sizeof(struct scatterlist) * + hba[i]->maxsgentries, + GFP_KERNEL); + if (hba[i]->scatter_list[k] == NULL) { + printk(KERN_ERR "cciss%d: could not allocate " + "s/g lists\n", i); + goto clean4; + } + } + hba[i]->cmd_sg_list = kmalloc(sizeof(struct Cmd_sg_list *) * + hba[i]->nr_cmds, + GFP_KERNEL); + if (!hba[i]->cmd_sg_list) { + printk(KERN_ERR "cciss%d: Cannot get memory for " + "s/g chaining.\n", i); + goto clean4; + } + /* Build up chain blocks for each command */ + if (hba[i]->chainsize > 0) { + for (j = 0; j < hba[i]->nr_cmds; j++) { + hba[i]->cmd_sg_list[j] = + kmalloc(sizeof(struct Cmd_sg_list), + GFP_KERNEL); + if (!hba[i]->cmd_sg_list[j]) { + printk(KERN_ERR "cciss%d: Cannot get memory " + "for chain block.\n", i); + goto clean4; + } + /* Need a block of chainsized s/g elements. */ + hba[i]->cmd_sg_list[j]->sgchain = + kmalloc((hba[i]->chainsize * + sizeof(SGDescriptor_struct)), + GFP_KERNEL); + if (!hba[i]->cmd_sg_list[j]->sgchain) { + printk(KERN_ERR "cciss%d: Cannot get memory " + "for s/g chains\n", i); + goto clean4; + } + } + } + spin_lock_init(&hba[i]->lock); /* Initialize the pdev driver private data. @@ -4362,7 +4314,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, cciss_procinit(i); - hba[i]->cciss_max_sectors = 2048; + hba[i]->cciss_max_sectors = 8192; rebuild_lun_table(hba[i], 1, 0); hba[i]->busy_initializing = 0; @@ -4370,6 +4322,20 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, clean4: kfree(hba[i]->cmd_pool_bits); + /* Free up sg elements */ + for (k = 0; k < hba[i]->nr_cmds; k++) + kfree(hba[i]->scatter_list[k]); + kfree(hba[i]->scatter_list); + /* Only free up extra s/g lists if controller supports them */ + if (hba[i]->chainsize > 0) { + for (j = 0; j < hba[i]->nr_cmds; j++) { + if (hba[i]->cmd_sg_list[j]) { + kfree(hba[i]->cmd_sg_list[j]->sgchain); + kfree(hba[i]->cmd_sg_list[j]); + } + } + kfree(hba[i]->cmd_sg_list); + } if (hba[i]->cmd_pool) pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(CommandList_struct), @@ -4400,30 +4366,28 @@ clean_no_release_regions: static void cciss_shutdown(struct pci_dev *pdev) { - ctlr_info_t *tmp_ptr; - int i; - char flush_buf[4]; + ctlr_info_t *h; + char *flush_buf; int return_code; - tmp_ptr = pci_get_drvdata(pdev); - if (tmp_ptr == NULL) - return; - i = tmp_ptr->ctlr; - if (hba[i] == NULL) + h = pci_get_drvdata(pdev); + flush_buf = kzalloc(4, GFP_KERNEL); + if (!flush_buf) { + printk(KERN_WARNING + "cciss:%d cache not flushed, out of memory.\n", + h->ctlr); return; - - /* Turn board interrupts off and send the flush cache command */ - /* sendcmd will turn off interrupt, and send the flush... - * To write all data in the battery backed cache to disks */ - memset(flush_buf, 0, 4); - return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0, - CTLR_LUNID, TYPE_CMD); - if (return_code == IO_OK) { - printk(KERN_INFO "Completed flushing cache on controller %d\n", i); - } else { - printk(KERN_WARNING "Error flushing cache on controller %d\n", i); } - free_irq(hba[i]->intr[2], hba[i]); + /* write all data in the battery backed cache to disk */ + memset(flush_buf, 0, 4); + return_code = sendcmd_withirq(CCISS_CACHE_FLUSH, h->ctlr, flush_buf, + 4, 0, CTLR_LUNID, TYPE_CMD); + kfree(flush_buf); + if (return_code != IO_OK) + printk(KERN_WARNING "cciss%d: Error flushing cache\n", + h->ctlr); + h->access.set_intr_mask(h, CCISS_INTR_OFF); + free_irq(h->intr[2], h); } static void __devexit cciss_remove_one(struct pci_dev *pdev) @@ -4485,6 +4449,20 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct), hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle); kfree(hba[i]->cmd_pool_bits); + /* Free up sg elements */ + for (j = 0; j < hba[i]->nr_cmds; j++) + kfree(hba[i]->scatter_list[j]); + kfree(hba[i]->scatter_list); + /* Only free up extra s/g lists if controller supports them */ + if (hba[i]->chainsize > 0) { + for (j = 0; j < hba[i]->nr_cmds; j++) { + if (hba[i]->cmd_sg_list[j]) { + kfree(hba[i]->cmd_sg_list[j]->sgchain); + kfree(hba[i]->cmd_sg_list[j]); + } + } + kfree(hba[i]->cmd_sg_list); + } /* * Deliberately omit pci_disable_device(): it does something nasty to * Smart Array controllers that pci_enable_device does not undo diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 31524cf42c77..1d95db254069 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -55,7 +55,13 @@ typedef struct _drive_info_struct char device_initialized; /* indicates whether dev is initialized */ } drive_info_struct; -struct ctlr_info +struct Cmd_sg_list { + SGDescriptor_struct *sgchain; + dma_addr_t sg_chain_dma; + int chain_block_size; +}; + +struct ctlr_info { int ctlr; char devname[8]; @@ -75,6 +81,16 @@ struct ctlr_info int num_luns; int highest_lun; int usage_count; /* number of opens all all minor devices */ + /* Need space for temp sg list + * number of scatter/gathers supported + * number of scatter/gathers in chained block + */ + struct scatterlist **scatter_list; + int maxsgentries; + int chainsize; + int max_cmd_sgentries; + struct Cmd_sg_list **cmd_sg_list; + # define DOORBELL_INT 0 # define PERF_MODE_INT 1 # define SIMPLE_MODE_INT 2 diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h index dbaed1ea0da3..6afa700890ff 100644 --- a/drivers/block/cciss_cmd.h +++ b/drivers/block/cciss_cmd.h @@ -5,9 +5,10 @@ //########################################################################### #define CISS_VERSION "1.00" -//general boundary defintions +//general boundary definitions #define SENSEINFOBYTES 32//note that this value may vary between host implementations -#define MAXSGENTRIES 31 +#define MAXSGENTRIES 32 +#define CCISS_SG_CHAIN 0x80000000 #define MAXREPLYQS 256 //Command Status value @@ -319,6 +320,10 @@ typedef struct _CfgTable_struct { BYTE ServerName[16]; DWORD HeartBeat; DWORD SCSI_Prefetch; + DWORD MaxSGElements; + DWORD MaxLogicalUnits; + DWORD MaxPhysicalDrives; + DWORD MaxPhysicalDrivesPerLogicalUnit; } CfgTable_struct; #pragma pack() #endif // CCISS_CMD_H diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c index 3315268b4ec7..5d0e46dc3632 100644 --- a/drivers/block/cciss_scsi.c +++ b/drivers/block/cciss_scsi.c @@ -755,7 +755,7 @@ complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag) cp, ei->ScsiStatus); #endif - cmd->result |= (ei->ScsiStatus < 1); + cmd->result |= (ei->ScsiStatus << 1); } else { /* scsi status is zero??? How??? */ @@ -1547,7 +1547,7 @@ cciss_engage_scsi(int ctlr) if (sa->registered) { printk("cciss%d: SCSI subsystem already engaged.\n", ctlr); spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); - return ENXIO; + return -ENXIO; } sa->registered = 1; spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); diff --git a/drivers/block/drbd/Kconfig b/drivers/block/drbd/Kconfig new file mode 100644 index 000000000000..f4acd04ebeef --- /dev/null +++ b/drivers/block/drbd/Kconfig @@ -0,0 +1,71 @@ +# +# DRBD device driver configuration +# + +comment "DRBD disabled because PROC_FS, INET or CONNECTOR not selected" + depends on !PROC_FS || !INET || !CONNECTOR + +config BLK_DEV_DRBD + tristate "DRBD Distributed Replicated Block Device support" + depends on PROC_FS && INET && CONNECTOR + select LRU_CACHE + default n + help + + NOTE: In order to authenticate connections you have to select + CRYPTO_HMAC and a hash function as well. + + DRBD is a shared-nothing, synchronously replicated block device. It + is designed to serve as a building block for high availability + clusters and in this context, is a "drop-in" replacement for shared + storage. Simplistically, you could see it as a network RAID 1. + + Each minor device has a role, which can be 'primary' or 'secondary'. + On the node with the primary device the application is supposed to + run and to access the device (/dev/drbdX). Every write is sent to + the local 'lower level block device' and, across the network, to the + node with the device in 'secondary' state. The secondary device + simply writes the data to its lower level block device. + + DRBD can also be used in dual-Primary mode (device writable on both + nodes), which means it can exhibit shared disk semantics in a + shared-nothing cluster. Needless to say, on top of dual-Primary + DRBD utilizing a cluster file system is necessary to maintain for + cache coherency. + + For automatic failover you need a cluster manager (e.g. heartbeat). + See also: http://www.drbd.org/, http://www.linux-ha.org + + If unsure, say N. + +config DRBD_FAULT_INJECTION + bool "DRBD fault injection" + depends on BLK_DEV_DRBD + help + + Say Y here if you want to simulate IO errors, in order to test DRBD's + behavior. + + The actual simulation of IO errors is done by writing 3 values to + /sys/module/drbd/parameters/ + + enable_faults: bitmask of... + 1 meta data write + 2 read + 4 resync data write + 8 read + 16 data write + 32 data read + 64 read ahead + 128 kmalloc of bitmap + 256 allocation of EE (epoch_entries) + + fault_devs: bitmask of minor numbers + fault_rate: frequency in percent + + Example: Simulate data write errors on /dev/drbd0 with a probability of 5%. + echo 16 > /sys/module/drbd/parameters/enable_faults + echo 1 > /sys/module/drbd/parameters/fault_devs + echo 5 > /sys/module/drbd/parameters/fault_rate + + If unsure, say N. diff --git a/drivers/block/drbd/Makefile b/drivers/block/drbd/Makefile new file mode 100644 index 000000000000..0d3f337ff5ff --- /dev/null +++ b/drivers/block/drbd/Makefile @@ -0,0 +1,5 @@ +drbd-y := drbd_bitmap.o drbd_proc.o +drbd-y += drbd_worker.o drbd_receiver.o drbd_req.o drbd_actlog.o +drbd-y += drbd_main.o drbd_strings.o drbd_nl.o + +obj-$(CONFIG_BLK_DEV_DRBD) += drbd.o diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c new file mode 100644 index 000000000000..17956ff6a08d --- /dev/null +++ b/drivers/block/drbd/drbd_actlog.c @@ -0,0 +1,1424 @@ +/* + drbd_actlog.c + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2003-2008, LINBIT Information Technologies GmbH. + Copyright (C) 2003-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2003-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include <linux/slab.h> +#include <linux/drbd.h> +#include "drbd_int.h" +#include "drbd_wrappers.h" + +/* We maintain a trivial check sum in our on disk activity log. + * With that we can ensure correct operation even when the storage + * device might do a partial (last) sector write while loosing power. + */ +struct __packed al_transaction { + u32 magic; + u32 tr_number; + struct __packed { + u32 pos; + u32 extent; } updates[1 + AL_EXTENTS_PT]; + u32 xor_sum; +}; + +struct update_odbm_work { + struct drbd_work w; + unsigned int enr; +}; + +struct update_al_work { + struct drbd_work w; + struct lc_element *al_ext; + struct completion event; + unsigned int enr; + /* if old_enr != LC_FREE, write corresponding bitmap sector, too */ + unsigned int old_enr; +}; + +struct drbd_atodb_wait { + atomic_t count; + struct completion io_done; + struct drbd_conf *mdev; + int error; +}; + + +int w_al_write_transaction(struct drbd_conf *, struct drbd_work *, int); + +static int _drbd_md_sync_page_io(struct drbd_conf *mdev, + struct drbd_backing_dev *bdev, + struct page *page, sector_t sector, + int rw, int size) +{ + struct bio *bio; + struct drbd_md_io md_io; + int ok; + + md_io.mdev = mdev; + init_completion(&md_io.event); + md_io.error = 0; + + if ((rw & WRITE) && !test_bit(MD_NO_BARRIER, &mdev->flags)) + rw |= (1 << BIO_RW_BARRIER); + rw |= ((1<<BIO_RW_UNPLUG) | (1<<BIO_RW_SYNCIO)); + + retry: + bio = bio_alloc(GFP_NOIO, 1); + bio->bi_bdev = bdev->md_bdev; + bio->bi_sector = sector; + ok = (bio_add_page(bio, page, size, 0) == size); + if (!ok) + goto out; + bio->bi_private = &md_io; + bio->bi_end_io = drbd_md_io_complete; + bio->bi_rw = rw; + + if (FAULT_ACTIVE(mdev, (rw & WRITE) ? DRBD_FAULT_MD_WR : DRBD_FAULT_MD_RD)) + bio_endio(bio, -EIO); + else + submit_bio(rw, bio); + wait_for_completion(&md_io.event); + ok = bio_flagged(bio, BIO_UPTODATE) && md_io.error == 0; + + /* check for unsupported barrier op. + * would rather check on EOPNOTSUPP, but that is not reliable. + * don't try again for ANY return value != 0 */ + if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && !ok)) { + /* Try again with no barrier */ + dev_warn(DEV, "Barriers not supported on meta data device - disabling\n"); + set_bit(MD_NO_BARRIER, &mdev->flags); + rw &= ~(1 << BIO_RW_BARRIER); + bio_put(bio); + goto retry; + } + out: + bio_put(bio); + return ok; +} + +int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev, + sector_t sector, int rw) +{ + int logical_block_size, mask, ok; + int offset = 0; + struct page *iop = mdev->md_io_page; + + D_ASSERT(mutex_is_locked(&mdev->md_io_mutex)); + + BUG_ON(!bdev->md_bdev); + + logical_block_size = bdev_logical_block_size(bdev->md_bdev); + if (logical_block_size == 0) + logical_block_size = MD_SECTOR_SIZE; + + /* in case logical_block_size != 512 [ s390 only? ] */ + if (logical_block_size != MD_SECTOR_SIZE) { + mask = (logical_block_size / MD_SECTOR_SIZE) - 1; + D_ASSERT(mask == 1 || mask == 3 || mask == 7); + D_ASSERT(logical_block_size == (mask+1) * MD_SECTOR_SIZE); + offset = sector & mask; + sector = sector & ~mask; + iop = mdev->md_io_tmpp; + + if (rw & WRITE) { + /* these are GFP_KERNEL pages, pre-allocated + * on device initialization */ + void *p = page_address(mdev->md_io_page); + void *hp = page_address(mdev->md_io_tmpp); + + ok = _drbd_md_sync_page_io(mdev, bdev, iop, sector, + READ, logical_block_size); + + if (unlikely(!ok)) { + dev_err(DEV, "drbd_md_sync_page_io(,%llus," + "READ [logical_block_size!=512]) failed!\n", + (unsigned long long)sector); + return 0; + } + + memcpy(hp + offset*MD_SECTOR_SIZE, p, MD_SECTOR_SIZE); + } + } + + if (sector < drbd_md_first_sector(bdev) || + sector > drbd_md_last_sector(bdev)) + dev_alert(DEV, "%s [%d]:%s(,%llus,%s) out of range md access!\n", + current->comm, current->pid, __func__, + (unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ"); + + ok = _drbd_md_sync_page_io(mdev, bdev, iop, sector, rw, logical_block_size); + if (unlikely(!ok)) { + dev_err(DEV, "drbd_md_sync_page_io(,%llus,%s) failed!\n", + (unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ"); + return 0; + } + + if (logical_block_size != MD_SECTOR_SIZE && !(rw & WRITE)) { + void *p = page_address(mdev->md_io_page); + void *hp = page_address(mdev->md_io_tmpp); + + memcpy(p, hp + offset*MD_SECTOR_SIZE, MD_SECTOR_SIZE); + } + + return ok; +} + +static struct lc_element *_al_get(struct drbd_conf *mdev, unsigned int enr) +{ + struct lc_element *al_ext; + struct lc_element *tmp; + unsigned long al_flags = 0; + + spin_lock_irq(&mdev->al_lock); + tmp = lc_find(mdev->resync, enr/AL_EXT_PER_BM_SECT); + if (unlikely(tmp != NULL)) { + struct bm_extent *bm_ext = lc_entry(tmp, struct bm_extent, lce); + if (test_bit(BME_NO_WRITES, &bm_ext->flags)) { + spin_unlock_irq(&mdev->al_lock); + return NULL; + } + } + al_ext = lc_get(mdev->act_log, enr); + al_flags = mdev->act_log->flags; + spin_unlock_irq(&mdev->al_lock); + + /* + if (!al_ext) { + if (al_flags & LC_STARVING) + dev_warn(DEV, "Have to wait for LRU element (AL too small?)\n"); + if (al_flags & LC_DIRTY) + dev_warn(DEV, "Ongoing AL update (AL device too slow?)\n"); + } + */ + + return al_ext; +} + +void drbd_al_begin_io(struct drbd_conf *mdev, sector_t sector) +{ + unsigned int enr = (sector >> (AL_EXTENT_SHIFT-9)); + struct lc_element *al_ext; + struct update_al_work al_work; + + D_ASSERT(atomic_read(&mdev->local_cnt) > 0); + + wait_event(mdev->al_wait, (al_ext = _al_get(mdev, enr))); + + if (al_ext->lc_number != enr) { + /* drbd_al_write_transaction(mdev,al_ext,enr); + * recurses into generic_make_request(), which + * disallows recursion, bios being serialized on the + * current->bio_tail list now. + * we have to delegate updates to the activity log + * to the worker thread. */ + init_completion(&al_work.event); + al_work.al_ext = al_ext; + al_work.enr = enr; + al_work.old_enr = al_ext->lc_number; + al_work.w.cb = w_al_write_transaction; + drbd_queue_work_front(&mdev->data.work, &al_work.w); + wait_for_completion(&al_work.event); + + mdev->al_writ_cnt++; + + spin_lock_irq(&mdev->al_lock); + lc_changed(mdev->act_log, al_ext); + spin_unlock_irq(&mdev->al_lock); + wake_up(&mdev->al_wait); + } +} + +void drbd_al_complete_io(struct drbd_conf *mdev, sector_t sector) +{ + unsigned int enr = (sector >> (AL_EXTENT_SHIFT-9)); + struct lc_element *extent; + unsigned long flags; + + spin_lock_irqsave(&mdev->al_lock, flags); + + extent = lc_find(mdev->act_log, enr); + + if (!extent) { + spin_unlock_irqrestore(&mdev->al_lock, flags); + dev_err(DEV, "al_complete_io() called on inactive extent %u\n", enr); + return; + } + + if (lc_put(mdev->act_log, extent) == 0) + wake_up(&mdev->al_wait); + + spin_unlock_irqrestore(&mdev->al_lock, flags); +} + +int +w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused) +{ + struct update_al_work *aw = container_of(w, struct update_al_work, w); + struct lc_element *updated = aw->al_ext; + const unsigned int new_enr = aw->enr; + const unsigned int evicted = aw->old_enr; + struct al_transaction *buffer; + sector_t sector; + int i, n, mx; + unsigned int extent_nr; + u32 xor_sum = 0; + + if (!get_ldev(mdev)) { + dev_err(DEV, "get_ldev() failed in w_al_write_transaction\n"); + complete(&((struct update_al_work *)w)->event); + return 1; + } + /* do we have to do a bitmap write, first? + * TODO reduce maximum latency: + * submit both bios, then wait for both, + * instead of doing two synchronous sector writes. */ + if (mdev->state.conn < C_CONNECTED && evicted != LC_FREE) + drbd_bm_write_sect(mdev, evicted/AL_EXT_PER_BM_SECT); + + mutex_lock(&mdev->md_io_mutex); /* protects md_io_page, al_tr_cycle, ... */ + buffer = (struct al_transaction *)page_address(mdev->md_io_page); + + buffer->magic = __constant_cpu_to_be32(DRBD_MAGIC); + buffer->tr_number = cpu_to_be32(mdev->al_tr_number); + + n = lc_index_of(mdev->act_log, updated); + + buffer->updates[0].pos = cpu_to_be32(n); + buffer->updates[0].extent = cpu_to_be32(new_enr); + + xor_sum ^= new_enr; + + mx = min_t(int, AL_EXTENTS_PT, + mdev->act_log->nr_elements - mdev->al_tr_cycle); + for (i = 0; i < mx; i++) { + unsigned idx = mdev->al_tr_cycle + i; + extent_nr = lc_element_by_index(mdev->act_log, idx)->lc_number; + buffer->updates[i+1].pos = cpu_to_be32(idx); + buffer->updates[i+1].extent = cpu_to_be32(extent_nr); + xor_sum ^= extent_nr; + } + for (; i < AL_EXTENTS_PT; i++) { + buffer->updates[i+1].pos = __constant_cpu_to_be32(-1); + buffer->updates[i+1].extent = __constant_cpu_to_be32(LC_FREE); + xor_sum ^= LC_FREE; + } + mdev->al_tr_cycle += AL_EXTENTS_PT; + if (mdev->al_tr_cycle >= mdev->act_log->nr_elements) + mdev->al_tr_cycle = 0; + + buffer->xor_sum = cpu_to_be32(xor_sum); + + sector = mdev->ldev->md.md_offset + + mdev->ldev->md.al_offset + mdev->al_tr_pos; + + if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) + drbd_chk_io_error(mdev, 1, TRUE); + + if (++mdev->al_tr_pos > + div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT)) + mdev->al_tr_pos = 0; + + D_ASSERT(mdev->al_tr_pos < MD_AL_MAX_SIZE); + mdev->al_tr_number++; + + mutex_unlock(&mdev->md_io_mutex); + + complete(&((struct update_al_work *)w)->event); + put_ldev(mdev); + + return 1; +} + +/** + * drbd_al_read_tr() - Read a single transaction from the on disk activity log + * @mdev: DRBD device. + * @bdev: Block device to read form. + * @b: pointer to an al_transaction. + * @index: On disk slot of the transaction to read. + * + * Returns -1 on IO error, 0 on checksum error and 1 upon success. + */ +static int drbd_al_read_tr(struct drbd_conf *mdev, + struct drbd_backing_dev *bdev, + struct al_transaction *b, + int index) +{ + sector_t sector; + int rv, i; + u32 xor_sum = 0; + + sector = bdev->md.md_offset + bdev->md.al_offset + index; + + /* Dont process error normally, + * as this is done before disk is attached! */ + if (!drbd_md_sync_page_io(mdev, bdev, sector, READ)) + return -1; + + rv = (be32_to_cpu(b->magic) == DRBD_MAGIC); + + for (i = 0; i < AL_EXTENTS_PT + 1; i++) + xor_sum ^= be32_to_cpu(b->updates[i].extent); + rv &= (xor_sum == be32_to_cpu(b->xor_sum)); + + return rv; +} + +/** + * drbd_al_read_log() - Restores the activity log from its on disk representation. + * @mdev: DRBD device. + * @bdev: Block device to read form. + * + * Returns 1 on success, returns 0 when reading the log failed due to IO errors. + */ +int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev) +{ + struct al_transaction *buffer; + int i; + int rv; + int mx; + int active_extents = 0; + int transactions = 0; + int found_valid = 0; + int from = 0; + int to = 0; + u32 from_tnr = 0; + u32 to_tnr = 0; + u32 cnr; + + mx = div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT); + + /* lock out all other meta data io for now, + * and make sure the page is mapped. + */ + mutex_lock(&mdev->md_io_mutex); + buffer = page_address(mdev->md_io_page); + + /* Find the valid transaction in the log */ + for (i = 0; i <= mx; i++) { + rv = drbd_al_read_tr(mdev, bdev, buffer, i); + if (rv == 0) + continue; + if (rv == -1) { + mutex_unlock(&mdev->md_io_mutex); + return 0; + } + cnr = be32_to_cpu(buffer->tr_number); + + if (++found_valid == 1) { + from = i; + to = i; + from_tnr = cnr; + to_tnr = cnr; + continue; + } + if ((int)cnr - (int)from_tnr < 0) { + D_ASSERT(from_tnr - cnr + i - from == mx+1); + from = i; + from_tnr = cnr; + } + if ((int)cnr - (int)to_tnr > 0) { + D_ASSERT(cnr - to_tnr == i - to); + to = i; + to_tnr = cnr; + } + } + + if (!found_valid) { + dev_warn(DEV, "No usable activity log found.\n"); + mutex_unlock(&mdev->md_io_mutex); + return 1; + } + + /* Read the valid transactions. + * dev_info(DEV, "Reading from %d to %d.\n",from,to); */ + i = from; + while (1) { + int j, pos; + unsigned int extent_nr; + unsigned int trn; + + rv = drbd_al_read_tr(mdev, bdev, buffer, i); + ERR_IF(rv == 0) goto cancel; + if (rv == -1) { + mutex_unlock(&mdev->md_io_mutex); + return 0; + } + + trn = be32_to_cpu(buffer->tr_number); + + spin_lock_irq(&mdev->al_lock); + + /* This loop runs backwards because in the cyclic + elements there might be an old version of the + updated element (in slot 0). So the element in slot 0 + can overwrite old versions. */ + for (j = AL_EXTENTS_PT; j >= 0; j--) { + pos = be32_to_cpu(buffer->updates[j].pos); + extent_nr = be32_to_cpu(buffer->updates[j].extent); + + if (extent_nr == LC_FREE) + continue; + + lc_set(mdev->act_log, extent_nr, pos); + active_extents++; + } + spin_unlock_irq(&mdev->al_lock); + + transactions++; + +cancel: + if (i == to) + break; + i++; + if (i > mx) + i = 0; + } + + mdev->al_tr_number = to_tnr+1; + mdev->al_tr_pos = to; + if (++mdev->al_tr_pos > + div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT)) + mdev->al_tr_pos = 0; + + /* ok, we are done with it */ + mutex_unlock(&mdev->md_io_mutex); + + dev_info(DEV, "Found %d transactions (%d active extents) in activity log.\n", + transactions, active_extents); + + return 1; +} + +static void atodb_endio(struct bio *bio, int error) +{ + struct drbd_atodb_wait *wc = bio->bi_private; + struct drbd_conf *mdev = wc->mdev; + struct page *page; + int uptodate = bio_flagged(bio, BIO_UPTODATE); + + /* strange behavior of some lower level drivers... + * fail the request by clearing the uptodate flag, + * but do not return any error?! */ + if (!error && !uptodate) + error = -EIO; + + drbd_chk_io_error(mdev, error, TRUE); + if (error && wc->error == 0) + wc->error = error; + + if (atomic_dec_and_test(&wc->count)) + complete(&wc->io_done); + + page = bio->bi_io_vec[0].bv_page; + put_page(page); + bio_put(bio); + mdev->bm_writ_cnt++; + put_ldev(mdev); +} + +#define S2W(s) ((s)<<(BM_EXT_SHIFT-BM_BLOCK_SHIFT-LN2_BPL)) +/* activity log to on disk bitmap -- prepare bio unless that sector + * is already covered by previously prepared bios */ +static int atodb_prepare_unless_covered(struct drbd_conf *mdev, + struct bio **bios, + unsigned int enr, + struct drbd_atodb_wait *wc) __must_hold(local) +{ + struct bio *bio; + struct page *page; + sector_t on_disk_sector = enr + mdev->ldev->md.md_offset + + mdev->ldev->md.bm_offset; + unsigned int page_offset = PAGE_SIZE; + int offset; + int i = 0; + int err = -ENOMEM; + + /* Check if that enr is already covered by an already created bio. + * Caution, bios[] is not NULL terminated, + * but only initialized to all NULL. + * For completely scattered activity log, + * the last invocation iterates over all bios, + * and finds the last NULL entry. + */ + while ((bio = bios[i])) { + if (bio->bi_sector == on_disk_sector) + return 0; + i++; + } + /* bios[i] == NULL, the next not yet used slot */ + + /* GFP_KERNEL, we are not in the write-out path */ + bio = bio_alloc(GFP_KERNEL, 1); + if (bio == NULL) + return -ENOMEM; + + if (i > 0) { + const struct bio_vec *prev_bv = bios[i-1]->bi_io_vec; + page_offset = prev_bv->bv_offset + prev_bv->bv_len; + page = prev_bv->bv_page; + } + if (page_offset == PAGE_SIZE) { + page = alloc_page(__GFP_HIGHMEM); + if (page == NULL) + goto out_bio_put; + page_offset = 0; + } else { + get_page(page); + } + + offset = S2W(enr); + drbd_bm_get_lel(mdev, offset, + min_t(size_t, S2W(1), drbd_bm_words(mdev) - offset), + kmap(page) + page_offset); + kunmap(page); + + bio->bi_private = wc; + bio->bi_end_io = atodb_endio; + bio->bi_bdev = mdev->ldev->md_bdev; + bio->bi_sector = on_disk_sector; + + if (bio_add_page(bio, page, MD_SECTOR_SIZE, page_offset) != MD_SECTOR_SIZE) + goto out_put_page; + + atomic_inc(&wc->count); + /* we already know that we may do this... + * get_ldev_if_state(mdev,D_ATTACHING); + * just get the extra reference, so that the local_cnt reflects + * the number of pending IO requests DRBD at its backing device. + */ + atomic_inc(&mdev->local_cnt); + + bios[i] = bio; + + return 0; + +out_put_page: + err = -EINVAL; + put_page(page); +out_bio_put: + bio_put(bio); + return err; +} + +/** + * drbd_al_to_on_disk_bm() - * Writes bitmap parts covered by active AL extents + * @mdev: DRBD device. + * + * Called when we detach (unconfigure) local storage, + * or when we go from R_PRIMARY to R_SECONDARY role. + */ +void drbd_al_to_on_disk_bm(struct drbd_conf *mdev) +{ + int i, nr_elements; + unsigned int enr; + struct bio **bios; + struct drbd_atodb_wait wc; + + ERR_IF (!get_ldev_if_state(mdev, D_ATTACHING)) + return; /* sorry, I don't have any act_log etc... */ + + wait_event(mdev->al_wait, lc_try_lock(mdev->act_log)); + + nr_elements = mdev->act_log->nr_elements; + + /* GFP_KERNEL, we are not in anyone's write-out path */ + bios = kzalloc(sizeof(struct bio *) * nr_elements, GFP_KERNEL); + if (!bios) + goto submit_one_by_one; + + atomic_set(&wc.count, 0); + init_completion(&wc.io_done); + wc.mdev = mdev; + wc.error = 0; + + for (i = 0; i < nr_elements; i++) { + enr = lc_element_by_index(mdev->act_log, i)->lc_number; + if (enr == LC_FREE) + continue; + /* next statement also does atomic_inc wc.count and local_cnt */ + if (atodb_prepare_unless_covered(mdev, bios, + enr/AL_EXT_PER_BM_SECT, + &wc)) + goto free_bios_submit_one_by_one; + } + + /* unnecessary optimization? */ + lc_unlock(mdev->act_log); + wake_up(&mdev->al_wait); + + /* all prepared, submit them */ + for (i = 0; i < nr_elements; i++) { + if (bios[i] == NULL) + break; + if (FAULT_ACTIVE(mdev, DRBD_FAULT_MD_WR)) { + bios[i]->bi_rw = WRITE; + bio_endio(bios[i], -EIO); + } else { + submit_bio(WRITE, bios[i]); + } + } + + drbd_blk_run_queue(bdev_get_queue(mdev->ldev->md_bdev)); + + /* always (try to) flush bitmap to stable storage */ + drbd_md_flush(mdev); + + /* In case we did not submit a single IO do not wait for + * them to complete. ( Because we would wait forever here. ) + * + * In case we had IOs and they are already complete, there + * is not point in waiting anyways. + * Therefore this if () ... */ + if (atomic_read(&wc.count)) + wait_for_completion(&wc.io_done); + + put_ldev(mdev); + + kfree(bios); + return; + + free_bios_submit_one_by_one: + /* free everything by calling the endio callback directly. */ + for (i = 0; i < nr_elements && bios[i]; i++) + bio_endio(bios[i], 0); + + kfree(bios); + + submit_one_by_one: + dev_warn(DEV, "Using the slow drbd_al_to_on_disk_bm()\n"); + + for (i = 0; i < mdev->act_log->nr_elements; i++) { + enr = lc_element_by_index(mdev->act_log, i)->lc_number; + if (enr == LC_FREE) + continue; + /* Really slow: if we have al-extents 16..19 active, + * sector 4 will be written four times! Synchronous! */ + drbd_bm_write_sect(mdev, enr/AL_EXT_PER_BM_SECT); + } + + lc_unlock(mdev->act_log); + wake_up(&mdev->al_wait); + put_ldev(mdev); +} + +/** + * drbd_al_apply_to_bm() - Sets the bitmap to diry(1) where covered ba active AL extents + * @mdev: DRBD device. + */ +void drbd_al_apply_to_bm(struct drbd_conf *mdev) +{ + unsigned int enr; + unsigned long add = 0; + char ppb[10]; + int i; + + wait_event(mdev->al_wait, lc_try_lock(mdev->act_log)); + + for (i = 0; i < mdev->act_log->nr_elements; i++) { + enr = lc_element_by_index(mdev->act_log, i)->lc_number; + if (enr == LC_FREE) + continue; + add += drbd_bm_ALe_set_all(mdev, enr); + } + + lc_unlock(mdev->act_log); + wake_up(&mdev->al_wait); + + dev_info(DEV, "Marked additional %s as out-of-sync based on AL.\n", + ppsize(ppb, Bit2KB(add))); +} + +static int _try_lc_del(struct drbd_conf *mdev, struct lc_element *al_ext) +{ + int rv; + + spin_lock_irq(&mdev->al_lock); + rv = (al_ext->refcnt == 0); + if (likely(rv)) + lc_del(mdev->act_log, al_ext); + spin_unlock_irq(&mdev->al_lock); + + return rv; +} + +/** + * drbd_al_shrink() - Removes all active extents form the activity log + * @mdev: DRBD device. + * + * Removes all active extents form the activity log, waiting until + * the reference count of each entry dropped to 0 first, of course. + * + * You need to lock mdev->act_log with lc_try_lock() / lc_unlock() + */ +void drbd_al_shrink(struct drbd_conf *mdev) +{ + struct lc_element *al_ext; + int i; + + D_ASSERT(test_bit(__LC_DIRTY, &mdev->act_log->flags)); + + for (i = 0; i < mdev->act_log->nr_elements; i++) { + al_ext = lc_element_by_index(mdev->act_log, i); + if (al_ext->lc_number == LC_FREE) + continue; + wait_event(mdev->al_wait, _try_lc_del(mdev, al_ext)); + } + + wake_up(&mdev->al_wait); +} + +static int w_update_odbm(struct drbd_conf *mdev, struct drbd_work *w, int unused) +{ + struct update_odbm_work *udw = container_of(w, struct update_odbm_work, w); + + if (!get_ldev(mdev)) { + if (__ratelimit(&drbd_ratelimit_state)) + dev_warn(DEV, "Can not update on disk bitmap, local IO disabled.\n"); + kfree(udw); + return 1; + } + + drbd_bm_write_sect(mdev, udw->enr); + put_ldev(mdev); + + kfree(udw); + + if (drbd_bm_total_weight(mdev) <= mdev->rs_failed) { + switch (mdev->state.conn) { + case C_SYNC_SOURCE: case C_SYNC_TARGET: + case C_PAUSED_SYNC_S: case C_PAUSED_SYNC_T: + drbd_resync_finished(mdev); + default: + /* nothing to do */ + break; + } + } + drbd_bcast_sync_progress(mdev); + + return 1; +} + + +/* ATTENTION. The AL's extents are 4MB each, while the extents in the + * resync LRU-cache are 16MB each. + * The caller of this function has to hold an get_ldev() reference. + * + * TODO will be obsoleted once we have a caching lru of the on disk bitmap + */ +static void drbd_try_clear_on_disk_bm(struct drbd_conf *mdev, sector_t sector, + int count, int success) +{ + struct lc_element *e; + struct update_odbm_work *udw; + + unsigned int enr; + + D_ASSERT(atomic_read(&mdev->local_cnt)); + + /* I simply assume that a sector/size pair never crosses + * a 16 MB extent border. (Currently this is true...) */ + enr = BM_SECT_TO_EXT(sector); + + e = lc_get(mdev->resync, enr); + if (e) { + struct bm_extent *ext = lc_entry(e, struct bm_extent, lce); + if (ext->lce.lc_number == enr) { + if (success) + ext->rs_left -= count; + else + ext->rs_failed += count; + if (ext->rs_left < ext->rs_failed) { + dev_err(DEV, "BAD! sector=%llus enr=%u rs_left=%d " + "rs_failed=%d count=%d\n", + (unsigned long long)sector, + ext->lce.lc_number, ext->rs_left, + ext->rs_failed, count); + dump_stack(); + + lc_put(mdev->resync, &ext->lce); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return; + } + } else { + /* Normally this element should be in the cache, + * since drbd_rs_begin_io() pulled it already in. + * + * But maybe an application write finished, and we set + * something outside the resync lru_cache in sync. + */ + int rs_left = drbd_bm_e_weight(mdev, enr); + if (ext->flags != 0) { + dev_warn(DEV, "changing resync lce: %d[%u;%02lx]" + " -> %d[%u;00]\n", + ext->lce.lc_number, ext->rs_left, + ext->flags, enr, rs_left); + ext->flags = 0; + } + if (ext->rs_failed) { + dev_warn(DEV, "Kicking resync_lru element enr=%u " + "out with rs_failed=%d\n", + ext->lce.lc_number, ext->rs_failed); + set_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags); + } + ext->rs_left = rs_left; + ext->rs_failed = success ? 0 : count; + lc_changed(mdev->resync, &ext->lce); + } + lc_put(mdev->resync, &ext->lce); + /* no race, we are within the al_lock! */ + + if (ext->rs_left == ext->rs_failed) { + ext->rs_failed = 0; + + udw = kmalloc(sizeof(*udw), GFP_ATOMIC); + if (udw) { + udw->enr = ext->lce.lc_number; + udw->w.cb = w_update_odbm; + drbd_queue_work_front(&mdev->data.work, &udw->w); + } else { + dev_warn(DEV, "Could not kmalloc an udw\n"); + set_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags); + } + } + } else { + dev_err(DEV, "lc_get() failed! locked=%d/%d flags=%lu\n", + mdev->resync_locked, + mdev->resync->nr_elements, + mdev->resync->flags); + } +} + +/* clear the bit corresponding to the piece of storage in question: + * size byte of data starting from sector. Only clear a bits of the affected + * one ore more _aligned_ BM_BLOCK_SIZE blocks. + * + * called by worker on C_SYNC_TARGET and receiver on SyncSource. + * + */ +void __drbd_set_in_sync(struct drbd_conf *mdev, sector_t sector, int size, + const char *file, const unsigned int line) +{ + /* Is called from worker and receiver context _only_ */ + unsigned long sbnr, ebnr, lbnr; + unsigned long count = 0; + sector_t esector, nr_sectors; + int wake_up = 0; + unsigned long flags; + + if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_SEGMENT_SIZE) { + dev_err(DEV, "drbd_set_in_sync: sector=%llus size=%d nonsense!\n", + (unsigned long long)sector, size); + return; + } + nr_sectors = drbd_get_capacity(mdev->this_bdev); + esector = sector + (size >> 9) - 1; + + ERR_IF(sector >= nr_sectors) return; + ERR_IF(esector >= nr_sectors) esector = (nr_sectors-1); + + lbnr = BM_SECT_TO_BIT(nr_sectors-1); + + /* we clear it (in sync). + * round up start sector, round down end sector. we make sure we only + * clear full, aligned, BM_BLOCK_SIZE (4K) blocks */ + if (unlikely(esector < BM_SECT_PER_BIT-1)) + return; + if (unlikely(esector == (nr_sectors-1))) + ebnr = lbnr; + else + ebnr = BM_SECT_TO_BIT(esector - (BM_SECT_PER_BIT-1)); + sbnr = BM_SECT_TO_BIT(sector + BM_SECT_PER_BIT-1); + + if (sbnr > ebnr) + return; + + /* + * ok, (capacity & 7) != 0 sometimes, but who cares... + * we count rs_{total,left} in bits, not sectors. + */ + spin_lock_irqsave(&mdev->al_lock, flags); + count = drbd_bm_clear_bits(mdev, sbnr, ebnr); + if (count) { + /* we need the lock for drbd_try_clear_on_disk_bm */ + if (jiffies - mdev->rs_mark_time > HZ*10) { + /* should be rolling marks, + * but we estimate only anyways. */ + if (mdev->rs_mark_left != drbd_bm_total_weight(mdev) && + mdev->state.conn != C_PAUSED_SYNC_T && + mdev->state.conn != C_PAUSED_SYNC_S) { + mdev->rs_mark_time = jiffies; + mdev->rs_mark_left = drbd_bm_total_weight(mdev); + } + } + if (get_ldev(mdev)) { + drbd_try_clear_on_disk_bm(mdev, sector, count, TRUE); + put_ldev(mdev); + } + /* just wake_up unconditional now, various lc_chaged(), + * lc_put() in drbd_try_clear_on_disk_bm(). */ + wake_up = 1; + } + spin_unlock_irqrestore(&mdev->al_lock, flags); + if (wake_up) + wake_up(&mdev->al_wait); +} + +/* + * this is intended to set one request worth of data out of sync. + * affects at least 1 bit, + * and at most 1+DRBD_MAX_SEGMENT_SIZE/BM_BLOCK_SIZE bits. + * + * called by tl_clear and drbd_send_dblock (==drbd_make_request). + * so this can be _any_ process. + */ +void __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size, + const char *file, const unsigned int line) +{ + unsigned long sbnr, ebnr, lbnr, flags; + sector_t esector, nr_sectors; + unsigned int enr, count; + struct lc_element *e; + + if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_SEGMENT_SIZE) { + dev_err(DEV, "sector: %llus, size: %d\n", + (unsigned long long)sector, size); + return; + } + + if (!get_ldev(mdev)) + return; /* no disk, no metadata, no bitmap to set bits in */ + + nr_sectors = drbd_get_capacity(mdev->this_bdev); + esector = sector + (size >> 9) - 1; + + ERR_IF(sector >= nr_sectors) + goto out; + ERR_IF(esector >= nr_sectors) + esector = (nr_sectors-1); + + lbnr = BM_SECT_TO_BIT(nr_sectors-1); + + /* we set it out of sync, + * we do not need to round anything here */ + sbnr = BM_SECT_TO_BIT(sector); + ebnr = BM_SECT_TO_BIT(esector); + + /* ok, (capacity & 7) != 0 sometimes, but who cares... + * we count rs_{total,left} in bits, not sectors. */ + spin_lock_irqsave(&mdev->al_lock, flags); + count = drbd_bm_set_bits(mdev, sbnr, ebnr); + + enr = BM_SECT_TO_EXT(sector); + e = lc_find(mdev->resync, enr); + if (e) + lc_entry(e, struct bm_extent, lce)->rs_left += count; + spin_unlock_irqrestore(&mdev->al_lock, flags); + +out: + put_ldev(mdev); +} + +static +struct bm_extent *_bme_get(struct drbd_conf *mdev, unsigned int enr) +{ + struct lc_element *e; + struct bm_extent *bm_ext; + int wakeup = 0; + unsigned long rs_flags; + + spin_lock_irq(&mdev->al_lock); + if (mdev->resync_locked > mdev->resync->nr_elements/2) { + spin_unlock_irq(&mdev->al_lock); + return NULL; + } + e = lc_get(mdev->resync, enr); + bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL; + if (bm_ext) { + if (bm_ext->lce.lc_number != enr) { + bm_ext->rs_left = drbd_bm_e_weight(mdev, enr); + bm_ext->rs_failed = 0; + lc_changed(mdev->resync, &bm_ext->lce); + wakeup = 1; + } + if (bm_ext->lce.refcnt == 1) + mdev->resync_locked++; + set_bit(BME_NO_WRITES, &bm_ext->flags); + } + rs_flags = mdev->resync->flags; + spin_unlock_irq(&mdev->al_lock); + if (wakeup) + wake_up(&mdev->al_wait); + + if (!bm_ext) { + if (rs_flags & LC_STARVING) + dev_warn(DEV, "Have to wait for element" + " (resync LRU too small?)\n"); + BUG_ON(rs_flags & LC_DIRTY); + } + + return bm_ext; +} + +static int _is_in_al(struct drbd_conf *mdev, unsigned int enr) +{ + struct lc_element *al_ext; + int rv = 0; + + spin_lock_irq(&mdev->al_lock); + if (unlikely(enr == mdev->act_log->new_number)) + rv = 1; + else { + al_ext = lc_find(mdev->act_log, enr); + if (al_ext) { + if (al_ext->refcnt) + rv = 1; + } + } + spin_unlock_irq(&mdev->al_lock); + + /* + if (unlikely(rv)) { + dev_info(DEV, "Delaying sync read until app's write is done\n"); + } + */ + return rv; +} + +/** + * drbd_rs_begin_io() - Gets an extent in the resync LRU cache and sets it to BME_LOCKED + * @mdev: DRBD device. + * @sector: The sector number. + * + * This functions sleeps on al_wait. Returns 1 on success, 0 if interrupted. + */ +int drbd_rs_begin_io(struct drbd_conf *mdev, sector_t sector) +{ + unsigned int enr = BM_SECT_TO_EXT(sector); + struct bm_extent *bm_ext; + int i, sig; + + sig = wait_event_interruptible(mdev->al_wait, + (bm_ext = _bme_get(mdev, enr))); + if (sig) + return 0; + + if (test_bit(BME_LOCKED, &bm_ext->flags)) + return 1; + + for (i = 0; i < AL_EXT_PER_BM_SECT; i++) { + sig = wait_event_interruptible(mdev->al_wait, + !_is_in_al(mdev, enr * AL_EXT_PER_BM_SECT + i)); + if (sig) { + spin_lock_irq(&mdev->al_lock); + if (lc_put(mdev->resync, &bm_ext->lce) == 0) { + clear_bit(BME_NO_WRITES, &bm_ext->flags); + mdev->resync_locked--; + wake_up(&mdev->al_wait); + } + spin_unlock_irq(&mdev->al_lock); + return 0; + } + } + + set_bit(BME_LOCKED, &bm_ext->flags); + + return 1; +} + +/** + * drbd_try_rs_begin_io() - Gets an extent in the resync LRU cache, does not sleep + * @mdev: DRBD device. + * @sector: The sector number. + * + * Gets an extent in the resync LRU cache, sets it to BME_NO_WRITES, then + * tries to set it to BME_LOCKED. Returns 0 upon success, and -EAGAIN + * if there is still application IO going on in this area. + */ +int drbd_try_rs_begin_io(struct drbd_conf *mdev, sector_t sector) +{ + unsigned int enr = BM_SECT_TO_EXT(sector); + const unsigned int al_enr = enr*AL_EXT_PER_BM_SECT; + struct lc_element *e; + struct bm_extent *bm_ext; + int i; + + spin_lock_irq(&mdev->al_lock); + if (mdev->resync_wenr != LC_FREE && mdev->resync_wenr != enr) { + /* in case you have very heavy scattered io, it may + * stall the syncer undefined if we give up the ref count + * when we try again and requeue. + * + * if we don't give up the refcount, but the next time + * we are scheduled this extent has been "synced" by new + * application writes, we'd miss the lc_put on the + * extent we keep the refcount on. + * so we remembered which extent we had to try again, and + * if the next requested one is something else, we do + * the lc_put here... + * we also have to wake_up + */ + e = lc_find(mdev->resync, mdev->resync_wenr); + bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL; + if (bm_ext) { + D_ASSERT(!test_bit(BME_LOCKED, &bm_ext->flags)); + D_ASSERT(test_bit(BME_NO_WRITES, &bm_ext->flags)); + clear_bit(BME_NO_WRITES, &bm_ext->flags); + mdev->resync_wenr = LC_FREE; + if (lc_put(mdev->resync, &bm_ext->lce) == 0) + mdev->resync_locked--; + wake_up(&mdev->al_wait); + } else { + dev_alert(DEV, "LOGIC BUG\n"); + } + } + /* TRY. */ + e = lc_try_get(mdev->resync, enr); + bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL; + if (bm_ext) { + if (test_bit(BME_LOCKED, &bm_ext->flags)) + goto proceed; + if (!test_and_set_bit(BME_NO_WRITES, &bm_ext->flags)) { + mdev->resync_locked++; + } else { + /* we did set the BME_NO_WRITES, + * but then could not set BME_LOCKED, + * so we tried again. + * drop the extra reference. */ + bm_ext->lce.refcnt--; + D_ASSERT(bm_ext->lce.refcnt > 0); + } + goto check_al; + } else { + /* do we rather want to try later? */ + if (mdev->resync_locked > mdev->resync->nr_elements-3) + goto try_again; + /* Do or do not. There is no try. -- Yoda */ + e = lc_get(mdev->resync, enr); + bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL; + if (!bm_ext) { + const unsigned long rs_flags = mdev->resync->flags; + if (rs_flags & LC_STARVING) + dev_warn(DEV, "Have to wait for element" + " (resync LRU too small?)\n"); + BUG_ON(rs_flags & LC_DIRTY); + goto try_again; + } + if (bm_ext->lce.lc_number != enr) { + bm_ext->rs_left = drbd_bm_e_weight(mdev, enr); + bm_ext->rs_failed = 0; + lc_changed(mdev->resync, &bm_ext->lce); + wake_up(&mdev->al_wait); + D_ASSERT(test_bit(BME_LOCKED, &bm_ext->flags) == 0); + } + set_bit(BME_NO_WRITES, &bm_ext->flags); + D_ASSERT(bm_ext->lce.refcnt == 1); + mdev->resync_locked++; + goto check_al; + } +check_al: + for (i = 0; i < AL_EXT_PER_BM_SECT; i++) { + if (unlikely(al_enr+i == mdev->act_log->new_number)) + goto try_again; + if (lc_is_used(mdev->act_log, al_enr+i)) + goto try_again; + } + set_bit(BME_LOCKED, &bm_ext->flags); +proceed: + mdev->resync_wenr = LC_FREE; + spin_unlock_irq(&mdev->al_lock); + return 0; + +try_again: + if (bm_ext) + mdev->resync_wenr = enr; + spin_unlock_irq(&mdev->al_lock); + return -EAGAIN; +} + +void drbd_rs_complete_io(struct drbd_conf *mdev, sector_t sector) +{ + unsigned int enr = BM_SECT_TO_EXT(sector); + struct lc_element *e; + struct bm_extent *bm_ext; + unsigned long flags; + + spin_lock_irqsave(&mdev->al_lock, flags); + e = lc_find(mdev->resync, enr); + bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL; + if (!bm_ext) { + spin_unlock_irqrestore(&mdev->al_lock, flags); + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "drbd_rs_complete_io() called, but extent not found\n"); + return; + } + + if (bm_ext->lce.refcnt == 0) { + spin_unlock_irqrestore(&mdev->al_lock, flags); + dev_err(DEV, "drbd_rs_complete_io(,%llu [=%u]) called, " + "but refcnt is 0!?\n", + (unsigned long long)sector, enr); + return; + } + + if (lc_put(mdev->resync, &bm_ext->lce) == 0) { + clear_bit(BME_LOCKED, &bm_ext->flags); + clear_bit(BME_NO_WRITES, &bm_ext->flags); + mdev->resync_locked--; + wake_up(&mdev->al_wait); + } + + spin_unlock_irqrestore(&mdev->al_lock, flags); +} + +/** + * drbd_rs_cancel_all() - Removes all extents from the resync LRU (even BME_LOCKED) + * @mdev: DRBD device. + */ +void drbd_rs_cancel_all(struct drbd_conf *mdev) +{ + spin_lock_irq(&mdev->al_lock); + + if (get_ldev_if_state(mdev, D_FAILED)) { /* Makes sure ->resync is there. */ + lc_reset(mdev->resync); + put_ldev(mdev); + } + mdev->resync_locked = 0; + mdev->resync_wenr = LC_FREE; + spin_unlock_irq(&mdev->al_lock); + wake_up(&mdev->al_wait); +} + +/** + * drbd_rs_del_all() - Gracefully remove all extents from the resync LRU + * @mdev: DRBD device. + * + * Returns 0 upon success, -EAGAIN if at least one reference count was + * not zero. + */ +int drbd_rs_del_all(struct drbd_conf *mdev) +{ + struct lc_element *e; + struct bm_extent *bm_ext; + int i; + + spin_lock_irq(&mdev->al_lock); + + if (get_ldev_if_state(mdev, D_FAILED)) { + /* ok, ->resync is there. */ + for (i = 0; i < mdev->resync->nr_elements; i++) { + e = lc_element_by_index(mdev->resync, i); + bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL; + if (bm_ext->lce.lc_number == LC_FREE) + continue; + if (bm_ext->lce.lc_number == mdev->resync_wenr) { + dev_info(DEV, "dropping %u in drbd_rs_del_all, apparently" + " got 'synced' by application io\n", + mdev->resync_wenr); + D_ASSERT(!test_bit(BME_LOCKED, &bm_ext->flags)); + D_ASSERT(test_bit(BME_NO_WRITES, &bm_ext->flags)); + clear_bit(BME_NO_WRITES, &bm_ext->flags); + mdev->resync_wenr = LC_FREE; + lc_put(mdev->resync, &bm_ext->lce); + } + if (bm_ext->lce.refcnt != 0) { + dev_info(DEV, "Retrying drbd_rs_del_all() later. " + "refcnt=%d\n", bm_ext->lce.refcnt); + put_ldev(mdev); + spin_unlock_irq(&mdev->al_lock); + return -EAGAIN; + } + D_ASSERT(!test_bit(BME_LOCKED, &bm_ext->flags)); + D_ASSERT(!test_bit(BME_NO_WRITES, &bm_ext->flags)); + lc_del(mdev->resync, &bm_ext->lce); + } + D_ASSERT(mdev->resync->used == 0); + put_ldev(mdev); + } + spin_unlock_irq(&mdev->al_lock); + + return 0; +} + +/** + * drbd_rs_failed_io() - Record information on a failure to resync the specified blocks + * @mdev: DRBD device. + * @sector: The sector number. + * @size: Size of failed IO operation, in byte. + */ +void drbd_rs_failed_io(struct drbd_conf *mdev, sector_t sector, int size) +{ + /* Is called from worker and receiver context _only_ */ + unsigned long sbnr, ebnr, lbnr; + unsigned long count; + sector_t esector, nr_sectors; + int wake_up = 0; + + if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_SEGMENT_SIZE) { + dev_err(DEV, "drbd_rs_failed_io: sector=%llus size=%d nonsense!\n", + (unsigned long long)sector, size); + return; + } + nr_sectors = drbd_get_capacity(mdev->this_bdev); + esector = sector + (size >> 9) - 1; + + ERR_IF(sector >= nr_sectors) return; + ERR_IF(esector >= nr_sectors) esector = (nr_sectors-1); + + lbnr = BM_SECT_TO_BIT(nr_sectors-1); + + /* + * round up start sector, round down end sector. we make sure we only + * handle full, aligned, BM_BLOCK_SIZE (4K) blocks */ + if (unlikely(esector < BM_SECT_PER_BIT-1)) + return; + if (unlikely(esector == (nr_sectors-1))) + ebnr = lbnr; + else + ebnr = BM_SECT_TO_BIT(esector - (BM_SECT_PER_BIT-1)); + sbnr = BM_SECT_TO_BIT(sector + BM_SECT_PER_BIT-1); + + if (sbnr > ebnr) + return; + + /* + * ok, (capacity & 7) != 0 sometimes, but who cares... + * we count rs_{total,left} in bits, not sectors. + */ + spin_lock_irq(&mdev->al_lock); + count = drbd_bm_count_bits(mdev, sbnr, ebnr); + if (count) { + mdev->rs_failed += count; + + if (get_ldev(mdev)) { + drbd_try_clear_on_disk_bm(mdev, sector, count, FALSE); + put_ldev(mdev); + } + + /* just wake_up unconditional now, various lc_chaged(), + * lc_put() in drbd_try_clear_on_disk_bm(). */ + wake_up = 1; + } + spin_unlock_irq(&mdev->al_lock); + if (wake_up) + wake_up(&mdev->al_wait); +} diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c new file mode 100644 index 000000000000..b61057e77882 --- /dev/null +++ b/drivers/block/drbd/drbd_bitmap.c @@ -0,0 +1,1327 @@ +/* + drbd_bitmap.c + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2004-2008, LINBIT Information Technologies GmbH. + Copyright (C) 2004-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2004-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/bitops.h> +#include <linux/vmalloc.h> +#include <linux/string.h> +#include <linux/drbd.h> +#include <asm/kmap_types.h> +#include "drbd_int.h" + +/* OPAQUE outside this file! + * interface defined in drbd_int.h + + * convention: + * function name drbd_bm_... => used elsewhere, "public". + * function name bm_... => internal to implementation, "private". + + * Note that since find_first_bit returns int, at the current granularity of + * the bitmap (4KB per byte), this implementation "only" supports up to + * 1<<(32+12) == 16 TB... + */ + +/* + * NOTE + * Access to the *bm_pages is protected by bm_lock. + * It is safe to read the other members within the lock. + * + * drbd_bm_set_bits is called from bio_endio callbacks, + * We may be called with irq already disabled, + * so we need spin_lock_irqsave(). + * And we need the kmap_atomic. + */ +struct drbd_bitmap { + struct page **bm_pages; + spinlock_t bm_lock; + /* WARNING unsigned long bm_*: + * 32bit number of bit offset is just enough for 512 MB bitmap. + * it will blow up if we make the bitmap bigger... + * not that it makes much sense to have a bitmap that large, + * rather change the granularity to 16k or 64k or something. + * (that implies other problems, however...) + */ + unsigned long bm_set; /* nr of set bits; THINK maybe atomic_t? */ + unsigned long bm_bits; + size_t bm_words; + size_t bm_number_of_pages; + sector_t bm_dev_capacity; + struct semaphore bm_change; /* serializes resize operations */ + + atomic_t bm_async_io; + wait_queue_head_t bm_io_wait; + + unsigned long bm_flags; + + /* debugging aid, in case we are still racy somewhere */ + char *bm_why; + struct task_struct *bm_task; +}; + +/* definition of bits in bm_flags */ +#define BM_LOCKED 0 +#define BM_MD_IO_ERROR 1 +#define BM_P_VMALLOCED 2 + +static int bm_is_locked(struct drbd_bitmap *b) +{ + return test_bit(BM_LOCKED, &b->bm_flags); +} + +#define bm_print_lock_info(m) __bm_print_lock_info(m, __func__) +static void __bm_print_lock_info(struct drbd_conf *mdev, const char *func) +{ + struct drbd_bitmap *b = mdev->bitmap; + if (!__ratelimit(&drbd_ratelimit_state)) + return; + dev_err(DEV, "FIXME %s in %s, bitmap locked for '%s' by %s\n", + current == mdev->receiver.task ? "receiver" : + current == mdev->asender.task ? "asender" : + current == mdev->worker.task ? "worker" : current->comm, + func, b->bm_why ?: "?", + b->bm_task == mdev->receiver.task ? "receiver" : + b->bm_task == mdev->asender.task ? "asender" : + b->bm_task == mdev->worker.task ? "worker" : "?"); +} + +void drbd_bm_lock(struct drbd_conf *mdev, char *why) +{ + struct drbd_bitmap *b = mdev->bitmap; + int trylock_failed; + + if (!b) { + dev_err(DEV, "FIXME no bitmap in drbd_bm_lock!?\n"); + return; + } + + trylock_failed = down_trylock(&b->bm_change); + + if (trylock_failed) { + dev_warn(DEV, "%s going to '%s' but bitmap already locked for '%s' by %s\n", + current == mdev->receiver.task ? "receiver" : + current == mdev->asender.task ? "asender" : + current == mdev->worker.task ? "worker" : current->comm, + why, b->bm_why ?: "?", + b->bm_task == mdev->receiver.task ? "receiver" : + b->bm_task == mdev->asender.task ? "asender" : + b->bm_task == mdev->worker.task ? "worker" : "?"); + down(&b->bm_change); + } + if (__test_and_set_bit(BM_LOCKED, &b->bm_flags)) + dev_err(DEV, "FIXME bitmap already locked in bm_lock\n"); + + b->bm_why = why; + b->bm_task = current; +} + +void drbd_bm_unlock(struct drbd_conf *mdev) +{ + struct drbd_bitmap *b = mdev->bitmap; + if (!b) { + dev_err(DEV, "FIXME no bitmap in drbd_bm_unlock!?\n"); + return; + } + + if (!__test_and_clear_bit(BM_LOCKED, &mdev->bitmap->bm_flags)) + dev_err(DEV, "FIXME bitmap not locked in bm_unlock\n"); + + b->bm_why = NULL; + b->bm_task = NULL; + up(&b->bm_change); +} + +/* word offset to long pointer */ +static unsigned long *__bm_map_paddr(struct drbd_bitmap *b, unsigned long offset, const enum km_type km) +{ + struct page *page; + unsigned long page_nr; + + /* page_nr = (word*sizeof(long)) >> PAGE_SHIFT; */ + page_nr = offset >> (PAGE_SHIFT - LN2_BPL + 3); + BUG_ON(page_nr >= b->bm_number_of_pages); + page = b->bm_pages[page_nr]; + + return (unsigned long *) kmap_atomic(page, km); +} + +static unsigned long * bm_map_paddr(struct drbd_bitmap *b, unsigned long offset) +{ + return __bm_map_paddr(b, offset, KM_IRQ1); +} + +static void __bm_unmap(unsigned long *p_addr, const enum km_type km) +{ + kunmap_atomic(p_addr, km); +}; + +static void bm_unmap(unsigned long *p_addr) +{ + return __bm_unmap(p_addr, KM_IRQ1); +} + +/* long word offset of _bitmap_ sector */ +#define S2W(s) ((s)<<(BM_EXT_SHIFT-BM_BLOCK_SHIFT-LN2_BPL)) +/* word offset from start of bitmap to word number _in_page_ + * modulo longs per page +#define MLPP(X) ((X) % (PAGE_SIZE/sizeof(long)) + hm, well, Philipp thinks gcc might not optimze the % into & (... - 1) + so do it explicitly: + */ +#define MLPP(X) ((X) & ((PAGE_SIZE/sizeof(long))-1)) + +/* Long words per page */ +#define LWPP (PAGE_SIZE/sizeof(long)) + +/* + * actually most functions herein should take a struct drbd_bitmap*, not a + * struct drbd_conf*, but for the debug macros I like to have the mdev around + * to be able to report device specific. + */ + +static void bm_free_pages(struct page **pages, unsigned long number) +{ + unsigned long i; + if (!pages) + return; + + for (i = 0; i < number; i++) { + if (!pages[i]) { + printk(KERN_ALERT "drbd: bm_free_pages tried to free " + "a NULL pointer; i=%lu n=%lu\n", + i, number); + continue; + } + __free_page(pages[i]); + pages[i] = NULL; + } +} + +static void bm_vk_free(void *ptr, int v) +{ + if (v) + vfree(ptr); + else + kfree(ptr); +} + +/* + * "have" and "want" are NUMBER OF PAGES. + */ +static struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want) +{ + struct page **old_pages = b->bm_pages; + struct page **new_pages, *page; + unsigned int i, bytes, vmalloced = 0; + unsigned long have = b->bm_number_of_pages; + + BUG_ON(have == 0 && old_pages != NULL); + BUG_ON(have != 0 && old_pages == NULL); + + if (have == want) + return old_pages; + + /* Trying kmalloc first, falling back to vmalloc. + * GFP_KERNEL is ok, as this is done when a lower level disk is + * "attached" to the drbd. Context is receiver thread or cqueue + * thread. As we have no disk yet, we are not in the IO path, + * not even the IO path of the peer. */ + bytes = sizeof(struct page *)*want; + new_pages = kmalloc(bytes, GFP_KERNEL); + if (!new_pages) { + new_pages = vmalloc(bytes); + if (!new_pages) + return NULL; + vmalloced = 1; + } + + memset(new_pages, 0, bytes); + if (want >= have) { + for (i = 0; i < have; i++) + new_pages[i] = old_pages[i]; + for (; i < want; i++) { + page = alloc_page(GFP_HIGHUSER); + if (!page) { + bm_free_pages(new_pages + have, i - have); + bm_vk_free(new_pages, vmalloced); + return NULL; + } + new_pages[i] = page; + } + } else { + for (i = 0; i < want; i++) + new_pages[i] = old_pages[i]; + /* NOT HERE, we are outside the spinlock! + bm_free_pages(old_pages + want, have - want); + */ + } + + if (vmalloced) + set_bit(BM_P_VMALLOCED, &b->bm_flags); + else + clear_bit(BM_P_VMALLOCED, &b->bm_flags); + + return new_pages; +} + +/* + * called on driver init only. TODO call when a device is created. + * allocates the drbd_bitmap, and stores it in mdev->bitmap. + */ +int drbd_bm_init(struct drbd_conf *mdev) +{ + struct drbd_bitmap *b = mdev->bitmap; + WARN_ON(b != NULL); + b = kzalloc(sizeof(struct drbd_bitmap), GFP_KERNEL); + if (!b) + return -ENOMEM; + spin_lock_init(&b->bm_lock); + init_MUTEX(&b->bm_change); + init_waitqueue_head(&b->bm_io_wait); + + mdev->bitmap = b; + + return 0; +} + +sector_t drbd_bm_capacity(struct drbd_conf *mdev) +{ + ERR_IF(!mdev->bitmap) return 0; + return mdev->bitmap->bm_dev_capacity; +} + +/* called on driver unload. TODO: call when a device is destroyed. + */ +void drbd_bm_cleanup(struct drbd_conf *mdev) +{ + ERR_IF (!mdev->bitmap) return; + bm_free_pages(mdev->bitmap->bm_pages, mdev->bitmap->bm_number_of_pages); + bm_vk_free(mdev->bitmap->bm_pages, test_bit(BM_P_VMALLOCED, &mdev->bitmap->bm_flags)); + kfree(mdev->bitmap); + mdev->bitmap = NULL; +} + +/* + * since (b->bm_bits % BITS_PER_LONG) != 0, + * this masks out the remaining bits. + * Returns the number of bits cleared. + */ +static int bm_clear_surplus(struct drbd_bitmap *b) +{ + const unsigned long mask = (1UL << (b->bm_bits & (BITS_PER_LONG-1))) - 1; + size_t w = b->bm_bits >> LN2_BPL; + int cleared = 0; + unsigned long *p_addr, *bm; + + p_addr = bm_map_paddr(b, w); + bm = p_addr + MLPP(w); + if (w < b->bm_words) { + cleared = hweight_long(*bm & ~mask); + *bm &= mask; + w++; bm++; + } + + if (w < b->bm_words) { + cleared += hweight_long(*bm); + *bm = 0; + } + bm_unmap(p_addr); + return cleared; +} + +static void bm_set_surplus(struct drbd_bitmap *b) +{ + const unsigned long mask = (1UL << (b->bm_bits & (BITS_PER_LONG-1))) - 1; + size_t w = b->bm_bits >> LN2_BPL; + unsigned long *p_addr, *bm; + + p_addr = bm_map_paddr(b, w); + bm = p_addr + MLPP(w); + if (w < b->bm_words) { + *bm |= ~mask; + bm++; w++; + } + + if (w < b->bm_words) { + *bm = ~(0UL); + } + bm_unmap(p_addr); +} + +static unsigned long __bm_count_bits(struct drbd_bitmap *b, const int swap_endian) +{ + unsigned long *p_addr, *bm, offset = 0; + unsigned long bits = 0; + unsigned long i, do_now; + + while (offset < b->bm_words) { + i = do_now = min_t(size_t, b->bm_words-offset, LWPP); + p_addr = __bm_map_paddr(b, offset, KM_USER0); + bm = p_addr + MLPP(offset); + while (i--) { +#ifndef __LITTLE_ENDIAN + if (swap_endian) + *bm = lel_to_cpu(*bm); +#endif + bits += hweight_long(*bm++); + } + __bm_unmap(p_addr, KM_USER0); + offset += do_now; + cond_resched(); + } + + return bits; +} + +static unsigned long bm_count_bits(struct drbd_bitmap *b) +{ + return __bm_count_bits(b, 0); +} + +static unsigned long bm_count_bits_swap_endian(struct drbd_bitmap *b) +{ + return __bm_count_bits(b, 1); +} + +/* offset and len in long words.*/ +static void bm_memset(struct drbd_bitmap *b, size_t offset, int c, size_t len) +{ + unsigned long *p_addr, *bm; + size_t do_now, end; + +#define BM_SECTORS_PER_BIT (BM_BLOCK_SIZE/512) + + end = offset + len; + + if (end > b->bm_words) { + printk(KERN_ALERT "drbd: bm_memset end > bm_words\n"); + return; + } + + while (offset < end) { + do_now = min_t(size_t, ALIGN(offset + 1, LWPP), end) - offset; + p_addr = bm_map_paddr(b, offset); + bm = p_addr + MLPP(offset); + if (bm+do_now > p_addr + LWPP) { + printk(KERN_ALERT "drbd: BUG BUG BUG! p_addr:%p bm:%p do_now:%d\n", + p_addr, bm, (int)do_now); + break; /* breaks to after catch_oob_access_end() only! */ + } + memset(bm, c, do_now * sizeof(long)); + bm_unmap(p_addr); + offset += do_now; + } +} + +/* + * make sure the bitmap has enough room for the attached storage, + * if necessary, resize. + * called whenever we may have changed the device size. + * returns -ENOMEM if we could not allocate enough memory, 0 on success. + * In case this is actually a resize, we copy the old bitmap into the new one. + * Otherwise, the bitmap is initialized to all bits set. + */ +int drbd_bm_resize(struct drbd_conf *mdev, sector_t capacity) +{ + struct drbd_bitmap *b = mdev->bitmap; + unsigned long bits, words, owords, obits, *p_addr, *bm; + unsigned long want, have, onpages; /* number of pages */ + struct page **npages, **opages = NULL; + int err = 0, growing; + int opages_vmalloced; + + ERR_IF(!b) return -ENOMEM; + + drbd_bm_lock(mdev, "resize"); + + dev_info(DEV, "drbd_bm_resize called with capacity == %llu\n", + (unsigned long long)capacity); + + if (capacity == b->bm_dev_capacity) + goto out; + + opages_vmalloced = test_bit(BM_P_VMALLOCED, &b->bm_flags); + + if (capacity == 0) { + spin_lock_irq(&b->bm_lock); + opages = b->bm_pages; + onpages = b->bm_number_of_pages; + owords = b->bm_words; + b->bm_pages = NULL; + b->bm_number_of_pages = + b->bm_set = + b->bm_bits = + b->bm_words = + b->bm_dev_capacity = 0; + spin_unlock_irq(&b->bm_lock); + bm_free_pages(opages, onpages); + bm_vk_free(opages, opages_vmalloced); + goto out; + } + bits = BM_SECT_TO_BIT(ALIGN(capacity, BM_SECT_PER_BIT)); + + /* if we would use + words = ALIGN(bits,BITS_PER_LONG) >> LN2_BPL; + a 32bit host could present the wrong number of words + to a 64bit host. + */ + words = ALIGN(bits, 64) >> LN2_BPL; + + if (get_ldev(mdev)) { + D_ASSERT((u64)bits <= (((u64)mdev->ldev->md.md_size_sect-MD_BM_OFFSET) << 12)); + put_ldev(mdev); + } + + /* one extra long to catch off by one errors */ + want = ALIGN((words+1)*sizeof(long), PAGE_SIZE) >> PAGE_SHIFT; + have = b->bm_number_of_pages; + if (want == have) { + D_ASSERT(b->bm_pages != NULL); + npages = b->bm_pages; + } else { + if (FAULT_ACTIVE(mdev, DRBD_FAULT_BM_ALLOC)) + npages = NULL; + else + npages = bm_realloc_pages(b, want); + } + + if (!npages) { + err = -ENOMEM; + goto out; + } + + spin_lock_irq(&b->bm_lock); + opages = b->bm_pages; + owords = b->bm_words; + obits = b->bm_bits; + + growing = bits > obits; + if (opages) + bm_set_surplus(b); + + b->bm_pages = npages; + b->bm_number_of_pages = want; + b->bm_bits = bits; + b->bm_words = words; + b->bm_dev_capacity = capacity; + + if (growing) { + bm_memset(b, owords, 0xff, words-owords); + b->bm_set += bits - obits; + } + + if (want < have) { + /* implicit: (opages != NULL) && (opages != npages) */ + bm_free_pages(opages + want, have - want); + } + + p_addr = bm_map_paddr(b, words); + bm = p_addr + MLPP(words); + *bm = DRBD_MAGIC; + bm_unmap(p_addr); + + (void)bm_clear_surplus(b); + + spin_unlock_irq(&b->bm_lock); + if (opages != npages) + bm_vk_free(opages, opages_vmalloced); + if (!growing) + b->bm_set = bm_count_bits(b); + dev_info(DEV, "resync bitmap: bits=%lu words=%lu\n", bits, words); + + out: + drbd_bm_unlock(mdev); + return err; +} + +/* inherently racy: + * if not protected by other means, return value may be out of date when + * leaving this function... + * we still need to lock it, since it is important that this returns + * bm_set == 0 precisely. + * + * maybe bm_set should be atomic_t ? + */ +static unsigned long _drbd_bm_total_weight(struct drbd_conf *mdev) +{ + struct drbd_bitmap *b = mdev->bitmap; + unsigned long s; + unsigned long flags; + + ERR_IF(!b) return 0; + ERR_IF(!b->bm_pages) return 0; + + spin_lock_irqsave(&b->bm_lock, flags); + s = b->bm_set; + spin_unlock_irqrestore(&b->bm_lock, flags); + + return s; +} + +unsigned long drbd_bm_total_weight(struct drbd_conf *mdev) +{ + unsigned long s; + /* if I don't have a disk, I don't know about out-of-sync status */ + if (!get_ldev_if_state(mdev, D_NEGOTIATING)) + return 0; + s = _drbd_bm_total_weight(mdev); + put_ldev(mdev); + return s; +} + +size_t drbd_bm_words(struct drbd_conf *mdev) +{ + struct drbd_bitmap *b = mdev->bitmap; + ERR_IF(!b) return 0; + ERR_IF(!b->bm_pages) return 0; + + return b->bm_words; +} + +unsigned long drbd_bm_bits(struct drbd_conf *mdev) +{ + struct drbd_bitmap *b = mdev->bitmap; + ERR_IF(!b) return 0; + + return b->bm_bits; +} + +/* merge number words from buffer into the bitmap starting at offset. + * buffer[i] is expected to be little endian unsigned long. + * bitmap must be locked by drbd_bm_lock. + * currently only used from receive_bitmap. + */ +void drbd_bm_merge_lel(struct drbd_conf *mdev, size_t offset, size_t number, + unsigned long *buffer) +{ + struct drbd_bitmap *b = mdev->bitmap; + unsigned long *p_addr, *bm; + unsigned long word, bits; + size_t end, do_now; + + end = offset + number; + + ERR_IF(!b) return; + ERR_IF(!b->bm_pages) return; + if (number == 0) + return; + WARN_ON(offset >= b->bm_words); + WARN_ON(end > b->bm_words); + + spin_lock_irq(&b->bm_lock); + while (offset < end) { + do_now = min_t(size_t, ALIGN(offset+1, LWPP), end) - offset; + p_addr = bm_map_paddr(b, offset); + bm = p_addr + MLPP(offset); + offset += do_now; + while (do_now--) { + bits = hweight_long(*bm); + word = *bm | lel_to_cpu(*buffer++); + *bm++ = word; + b->bm_set += hweight_long(word) - bits; + } + bm_unmap(p_addr); + } + /* with 32bit <-> 64bit cross-platform connect + * this is only correct for current usage, + * where we _know_ that we are 64 bit aligned, + * and know that this function is used in this way, too... + */ + if (end == b->bm_words) + b->bm_set -= bm_clear_surplus(b); + + spin_unlock_irq(&b->bm_lock); +} + +/* copy number words from the bitmap starting at offset into the buffer. + * buffer[i] will be little endian unsigned long. + */ +void drbd_bm_get_lel(struct drbd_conf *mdev, size_t offset, size_t number, + unsigned long *buffer) +{ + struct drbd_bitmap *b = mdev->bitmap; + unsigned long *p_addr, *bm; + size_t end, do_now; + + end = offset + number; + + ERR_IF(!b) return; + ERR_IF(!b->bm_pages) return; + + spin_lock_irq(&b->bm_lock); + if ((offset >= b->bm_words) || + (end > b->bm_words) || + (number <= 0)) + dev_err(DEV, "offset=%lu number=%lu bm_words=%lu\n", + (unsigned long) offset, + (unsigned long) number, + (unsigned long) b->bm_words); + else { + while (offset < end) { + do_now = min_t(size_t, ALIGN(offset+1, LWPP), end) - offset; + p_addr = bm_map_paddr(b, offset); + bm = p_addr + MLPP(offset); + offset += do_now; + while (do_now--) + *buffer++ = cpu_to_lel(*bm++); + bm_unmap(p_addr); + } + } + spin_unlock_irq(&b->bm_lock); +} + +/* set all bits in the bitmap */ +void drbd_bm_set_all(struct drbd_conf *mdev) +{ + struct drbd_bitmap *b = mdev->bitmap; + ERR_IF(!b) return; + ERR_IF(!b->bm_pages) return; + + spin_lock_irq(&b->bm_lock); + bm_memset(b, 0, 0xff, b->bm_words); + (void)bm_clear_surplus(b); + b->bm_set = b->bm_bits; + spin_unlock_irq(&b->bm_lock); +} + +/* clear all bits in the bitmap */ +void drbd_bm_clear_all(struct drbd_conf *mdev) +{ + struct drbd_bitmap *b = mdev->bitmap; + ERR_IF(!b) return; + ERR_IF(!b->bm_pages) return; + + spin_lock_irq(&b->bm_lock); + bm_memset(b, 0, 0, b->bm_words); + b->bm_set = 0; + spin_unlock_irq(&b->bm_lock); +} + +static void bm_async_io_complete(struct bio *bio, int error) +{ + struct drbd_bitmap *b = bio->bi_private; + int uptodate = bio_flagged(bio, BIO_UPTODATE); + + + /* strange behavior of some lower level drivers... + * fail the request by clearing the uptodate flag, + * but do not return any error?! + * do we want to WARN() on this? */ + if (!error && !uptodate) + error = -EIO; + + if (error) { + /* doh. what now? + * for now, set all bits, and flag MD_IO_ERROR */ + __set_bit(BM_MD_IO_ERROR, &b->bm_flags); + } + if (atomic_dec_and_test(&b->bm_async_io)) + wake_up(&b->bm_io_wait); + + bio_put(bio); +} + +static void bm_page_io_async(struct drbd_conf *mdev, struct drbd_bitmap *b, int page_nr, int rw) __must_hold(local) +{ + /* we are process context. we always get a bio */ + struct bio *bio = bio_alloc(GFP_KERNEL, 1); + unsigned int len; + sector_t on_disk_sector = + mdev->ldev->md.md_offset + mdev->ldev->md.bm_offset; + on_disk_sector += ((sector_t)page_nr) << (PAGE_SHIFT-9); + + /* this might happen with very small + * flexible external meta data device */ + len = min_t(unsigned int, PAGE_SIZE, + (drbd_md_last_sector(mdev->ldev) - on_disk_sector + 1)<<9); + + bio->bi_bdev = mdev->ldev->md_bdev; + bio->bi_sector = on_disk_sector; + bio_add_page(bio, b->bm_pages[page_nr], len, 0); + bio->bi_private = b; + bio->bi_end_io = bm_async_io_complete; + + if (FAULT_ACTIVE(mdev, (rw & WRITE) ? DRBD_FAULT_MD_WR : DRBD_FAULT_MD_RD)) { + bio->bi_rw |= rw; + bio_endio(bio, -EIO); + } else { + submit_bio(rw, bio); + } +} + +# if defined(__LITTLE_ENDIAN) + /* nothing to do, on disk == in memory */ +# define bm_cpu_to_lel(x) ((void)0) +# else +void bm_cpu_to_lel(struct drbd_bitmap *b) +{ + /* need to cpu_to_lel all the pages ... + * this may be optimized by using + * cpu_to_lel(-1) == -1 and cpu_to_lel(0) == 0; + * the following is still not optimal, but better than nothing */ + unsigned int i; + unsigned long *p_addr, *bm; + if (b->bm_set == 0) { + /* no page at all; avoid swap if all is 0 */ + i = b->bm_number_of_pages; + } else if (b->bm_set == b->bm_bits) { + /* only the last page */ + i = b->bm_number_of_pages - 1; + } else { + /* all pages */ + i = 0; + } + for (; i < b->bm_number_of_pages; i++) { + p_addr = kmap_atomic(b->bm_pages[i], KM_USER0); + for (bm = p_addr; bm < p_addr + PAGE_SIZE/sizeof(long); bm++) + *bm = cpu_to_lel(*bm); + kunmap_atomic(p_addr, KM_USER0); + } +} +# endif +/* lel_to_cpu == cpu_to_lel */ +# define bm_lel_to_cpu(x) bm_cpu_to_lel(x) + +/* + * bm_rw: read/write the whole bitmap from/to its on disk location. + */ +static int bm_rw(struct drbd_conf *mdev, int rw) __must_hold(local) +{ + struct drbd_bitmap *b = mdev->bitmap; + /* sector_t sector; */ + int bm_words, num_pages, i; + unsigned long now; + char ppb[10]; + int err = 0; + + WARN_ON(!bm_is_locked(b)); + + /* no spinlock here, the drbd_bm_lock should be enough! */ + + bm_words = drbd_bm_words(mdev); + num_pages = (bm_words*sizeof(long) + PAGE_SIZE-1) >> PAGE_SHIFT; + + /* on disk bitmap is little endian */ + if (rw == WRITE) + bm_cpu_to_lel(b); + + now = jiffies; + atomic_set(&b->bm_async_io, num_pages); + __clear_bit(BM_MD_IO_ERROR, &b->bm_flags); + + /* let the layers below us try to merge these bios... */ + for (i = 0; i < num_pages; i++) + bm_page_io_async(mdev, b, i, rw); + + drbd_blk_run_queue(bdev_get_queue(mdev->ldev->md_bdev)); + wait_event(b->bm_io_wait, atomic_read(&b->bm_async_io) == 0); + + if (test_bit(BM_MD_IO_ERROR, &b->bm_flags)) { + dev_alert(DEV, "we had at least one MD IO ERROR during bitmap IO\n"); + drbd_chk_io_error(mdev, 1, TRUE); + err = -EIO; + } + + now = jiffies; + if (rw == WRITE) { + /* swap back endianness */ + bm_lel_to_cpu(b); + /* flush bitmap to stable storage */ + drbd_md_flush(mdev); + } else /* rw == READ */ { + /* just read, if necessary adjust endianness */ + b->bm_set = bm_count_bits_swap_endian(b); + dev_info(DEV, "recounting of set bits took additional %lu jiffies\n", + jiffies - now); + } + now = b->bm_set; + + dev_info(DEV, "%s (%lu bits) marked out-of-sync by on disk bit-map.\n", + ppsize(ppb, now << (BM_BLOCK_SHIFT-10)), now); + + return err; +} + +/** + * drbd_bm_read() - Read the whole bitmap from its on disk location. + * @mdev: DRBD device. + */ +int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local) +{ + return bm_rw(mdev, READ); +} + +/** + * drbd_bm_write() - Write the whole bitmap to its on disk location. + * @mdev: DRBD device. + */ +int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local) +{ + return bm_rw(mdev, WRITE); +} + +/** + * drbd_bm_write_sect: Writes a 512 (MD_SECTOR_SIZE) byte piece of the bitmap + * @mdev: DRBD device. + * @enr: Extent number in the resync lru (happens to be sector offset) + * + * The BM_EXT_SIZE is on purpose exactly the amount of the bitmap covered + * by a single sector write. Therefore enr == sector offset from the + * start of the bitmap. + */ +int drbd_bm_write_sect(struct drbd_conf *mdev, unsigned long enr) __must_hold(local) +{ + sector_t on_disk_sector = enr + mdev->ldev->md.md_offset + + mdev->ldev->md.bm_offset; + int bm_words, num_words, offset; + int err = 0; + + mutex_lock(&mdev->md_io_mutex); + bm_words = drbd_bm_words(mdev); + offset = S2W(enr); /* word offset into bitmap */ + num_words = min(S2W(1), bm_words - offset); + if (num_words < S2W(1)) + memset(page_address(mdev->md_io_page), 0, MD_SECTOR_SIZE); + drbd_bm_get_lel(mdev, offset, num_words, + page_address(mdev->md_io_page)); + if (!drbd_md_sync_page_io(mdev, mdev->ldev, on_disk_sector, WRITE)) { + int i; + err = -EIO; + dev_err(DEV, "IO ERROR writing bitmap sector %lu " + "(meta-disk sector %llus)\n", + enr, (unsigned long long)on_disk_sector); + drbd_chk_io_error(mdev, 1, TRUE); + for (i = 0; i < AL_EXT_PER_BM_SECT; i++) + drbd_bm_ALe_set_all(mdev, enr*AL_EXT_PER_BM_SECT+i); + } + mdev->bm_writ_cnt++; + mutex_unlock(&mdev->md_io_mutex); + return err; +} + +/* NOTE + * find_first_bit returns int, we return unsigned long. + * should not make much difference anyways, but ... + * + * this returns a bit number, NOT a sector! + */ +#define BPP_MASK ((1UL << (PAGE_SHIFT+3)) - 1) +static unsigned long __bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo, + const int find_zero_bit, const enum km_type km) +{ + struct drbd_bitmap *b = mdev->bitmap; + unsigned long i = -1UL; + unsigned long *p_addr; + unsigned long bit_offset; /* bit offset of the mapped page. */ + + if (bm_fo > b->bm_bits) { + dev_err(DEV, "bm_fo=%lu bm_bits=%lu\n", bm_fo, b->bm_bits); + } else { + while (bm_fo < b->bm_bits) { + unsigned long offset; + bit_offset = bm_fo & ~BPP_MASK; /* bit offset of the page */ + offset = bit_offset >> LN2_BPL; /* word offset of the page */ + p_addr = __bm_map_paddr(b, offset, km); + + if (find_zero_bit) + i = find_next_zero_bit(p_addr, PAGE_SIZE*8, bm_fo & BPP_MASK); + else + i = find_next_bit(p_addr, PAGE_SIZE*8, bm_fo & BPP_MASK); + + __bm_unmap(p_addr, km); + if (i < PAGE_SIZE*8) { + i = bit_offset + i; + if (i >= b->bm_bits) + break; + goto found; + } + bm_fo = bit_offset + PAGE_SIZE*8; + } + i = -1UL; + } + found: + return i; +} + +static unsigned long bm_find_next(struct drbd_conf *mdev, + unsigned long bm_fo, const int find_zero_bit) +{ + struct drbd_bitmap *b = mdev->bitmap; + unsigned long i = -1UL; + + ERR_IF(!b) return i; + ERR_IF(!b->bm_pages) return i; + + spin_lock_irq(&b->bm_lock); + if (bm_is_locked(b)) + bm_print_lock_info(mdev); + + i = __bm_find_next(mdev, bm_fo, find_zero_bit, KM_IRQ1); + + spin_unlock_irq(&b->bm_lock); + return i; +} + +unsigned long drbd_bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo) +{ + return bm_find_next(mdev, bm_fo, 0); +} + +#if 0 +/* not yet needed for anything. */ +unsigned long drbd_bm_find_next_zero(struct drbd_conf *mdev, unsigned long bm_fo) +{ + return bm_find_next(mdev, bm_fo, 1); +} +#endif + +/* does not spin_lock_irqsave. + * you must take drbd_bm_lock() first */ +unsigned long _drbd_bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo) +{ + /* WARN_ON(!bm_is_locked(mdev)); */ + return __bm_find_next(mdev, bm_fo, 0, KM_USER1); +} + +unsigned long _drbd_bm_find_next_zero(struct drbd_conf *mdev, unsigned long bm_fo) +{ + /* WARN_ON(!bm_is_locked(mdev)); */ + return __bm_find_next(mdev, bm_fo, 1, KM_USER1); +} + +/* returns number of bits actually changed. + * for val != 0, we change 0 -> 1, return code positive + * for val == 0, we change 1 -> 0, return code negative + * wants bitnr, not sector. + * expected to be called for only a few bits (e - s about BITS_PER_LONG). + * Must hold bitmap lock already. */ +int __bm_change_bits_to(struct drbd_conf *mdev, const unsigned long s, + unsigned long e, int val, const enum km_type km) +{ + struct drbd_bitmap *b = mdev->bitmap; + unsigned long *p_addr = NULL; + unsigned long bitnr; + unsigned long last_page_nr = -1UL; + int c = 0; + + if (e >= b->bm_bits) { + dev_err(DEV, "ASSERT FAILED: bit_s=%lu bit_e=%lu bm_bits=%lu\n", + s, e, b->bm_bits); + e = b->bm_bits ? b->bm_bits -1 : 0; + } + for (bitnr = s; bitnr <= e; bitnr++) { + unsigned long offset = bitnr>>LN2_BPL; + unsigned long page_nr = offset >> (PAGE_SHIFT - LN2_BPL + 3); + if (page_nr != last_page_nr) { + if (p_addr) + __bm_unmap(p_addr, km); + p_addr = __bm_map_paddr(b, offset, km); + last_page_nr = page_nr; + } + if (val) + c += (0 == __test_and_set_bit(bitnr & BPP_MASK, p_addr)); + else + c -= (0 != __test_and_clear_bit(bitnr & BPP_MASK, p_addr)); + } + if (p_addr) + __bm_unmap(p_addr, km); + b->bm_set += c; + return c; +} + +/* returns number of bits actually changed. + * for val != 0, we change 0 -> 1, return code positive + * for val == 0, we change 1 -> 0, return code negative + * wants bitnr, not sector */ +int bm_change_bits_to(struct drbd_conf *mdev, const unsigned long s, + const unsigned long e, int val) +{ + unsigned long flags; + struct drbd_bitmap *b = mdev->bitmap; + int c = 0; + + ERR_IF(!b) return 1; + ERR_IF(!b->bm_pages) return 0; + + spin_lock_irqsave(&b->bm_lock, flags); + if (bm_is_locked(b)) + bm_print_lock_info(mdev); + + c = __bm_change_bits_to(mdev, s, e, val, KM_IRQ1); + + spin_unlock_irqrestore(&b->bm_lock, flags); + return c; +} + +/* returns number of bits changed 0 -> 1 */ +int drbd_bm_set_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e) +{ + return bm_change_bits_to(mdev, s, e, 1); +} + +/* returns number of bits changed 1 -> 0 */ +int drbd_bm_clear_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e) +{ + return -bm_change_bits_to(mdev, s, e, 0); +} + +/* sets all bits in full words, + * from first_word up to, but not including, last_word */ +static inline void bm_set_full_words_within_one_page(struct drbd_bitmap *b, + int page_nr, int first_word, int last_word) +{ + int i; + int bits; + unsigned long *paddr = kmap_atomic(b->bm_pages[page_nr], KM_USER0); + for (i = first_word; i < last_word; i++) { + bits = hweight_long(paddr[i]); + paddr[i] = ~0UL; + b->bm_set += BITS_PER_LONG - bits; + } + kunmap_atomic(paddr, KM_USER0); +} + +/* Same thing as drbd_bm_set_bits, but without taking the spin_lock_irqsave. + * You must first drbd_bm_lock(). + * Can be called to set the whole bitmap in one go. + * Sets bits from s to e _inclusive_. */ +void _drbd_bm_set_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e) +{ + /* First set_bit from the first bit (s) + * up to the next long boundary (sl), + * then assign full words up to the last long boundary (el), + * then set_bit up to and including the last bit (e). + * + * Do not use memset, because we must account for changes, + * so we need to loop over the words with hweight() anyways. + */ + unsigned long sl = ALIGN(s,BITS_PER_LONG); + unsigned long el = (e+1) & ~((unsigned long)BITS_PER_LONG-1); + int first_page; + int last_page; + int page_nr; + int first_word; + int last_word; + + if (e - s <= 3*BITS_PER_LONG) { + /* don't bother; el and sl may even be wrong. */ + __bm_change_bits_to(mdev, s, e, 1, KM_USER0); + return; + } + + /* difference is large enough that we can trust sl and el */ + + /* bits filling the current long */ + if (sl) + __bm_change_bits_to(mdev, s, sl-1, 1, KM_USER0); + + first_page = sl >> (3 + PAGE_SHIFT); + last_page = el >> (3 + PAGE_SHIFT); + + /* MLPP: modulo longs per page */ + /* LWPP: long words per page */ + first_word = MLPP(sl >> LN2_BPL); + last_word = LWPP; + + /* first and full pages, unless first page == last page */ + for (page_nr = first_page; page_nr < last_page; page_nr++) { + bm_set_full_words_within_one_page(mdev->bitmap, page_nr, first_word, last_word); + cond_resched(); + first_word = 0; + } + + /* last page (respectively only page, for first page == last page) */ + last_word = MLPP(el >> LN2_BPL); + bm_set_full_words_within_one_page(mdev->bitmap, last_page, first_word, last_word); + + /* possibly trailing bits. + * example: (e & 63) == 63, el will be e+1. + * if that even was the very last bit, + * it would trigger an assert in __bm_change_bits_to() + */ + if (el <= e) + __bm_change_bits_to(mdev, el, e, 1, KM_USER0); +} + +/* returns bit state + * wants bitnr, NOT sector. + * inherently racy... area needs to be locked by means of {al,rs}_lru + * 1 ... bit set + * 0 ... bit not set + * -1 ... first out of bounds access, stop testing for bits! + */ +int drbd_bm_test_bit(struct drbd_conf *mdev, const unsigned long bitnr) +{ + unsigned long flags; + struct drbd_bitmap *b = mdev->bitmap; + unsigned long *p_addr; + int i; + + ERR_IF(!b) return 0; + ERR_IF(!b->bm_pages) return 0; + + spin_lock_irqsave(&b->bm_lock, flags); + if (bm_is_locked(b)) + bm_print_lock_info(mdev); + if (bitnr < b->bm_bits) { + unsigned long offset = bitnr>>LN2_BPL; + p_addr = bm_map_paddr(b, offset); + i = test_bit(bitnr & BPP_MASK, p_addr) ? 1 : 0; + bm_unmap(p_addr); + } else if (bitnr == b->bm_bits) { + i = -1; + } else { /* (bitnr > b->bm_bits) */ + dev_err(DEV, "bitnr=%lu > bm_bits=%lu\n", bitnr, b->bm_bits); + i = 0; + } + + spin_unlock_irqrestore(&b->bm_lock, flags); + return i; +} + +/* returns number of bits set in the range [s, e] */ +int drbd_bm_count_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e) +{ + unsigned long flags; + struct drbd_bitmap *b = mdev->bitmap; + unsigned long *p_addr = NULL, page_nr = -1; + unsigned long bitnr; + int c = 0; + size_t w; + + /* If this is called without a bitmap, that is a bug. But just to be + * robust in case we screwed up elsewhere, in that case pretend there + * was one dirty bit in the requested area, so we won't try to do a + * local read there (no bitmap probably implies no disk) */ + ERR_IF(!b) return 1; + ERR_IF(!b->bm_pages) return 1; + + spin_lock_irqsave(&b->bm_lock, flags); + if (bm_is_locked(b)) + bm_print_lock_info(mdev); + for (bitnr = s; bitnr <= e; bitnr++) { + w = bitnr >> LN2_BPL; + if (page_nr != w >> (PAGE_SHIFT - LN2_BPL + 3)) { + page_nr = w >> (PAGE_SHIFT - LN2_BPL + 3); + if (p_addr) + bm_unmap(p_addr); + p_addr = bm_map_paddr(b, w); + } + ERR_IF (bitnr >= b->bm_bits) { + dev_err(DEV, "bitnr=%lu bm_bits=%lu\n", bitnr, b->bm_bits); + } else { + c += (0 != test_bit(bitnr - (page_nr << (PAGE_SHIFT+3)), p_addr)); + } + } + if (p_addr) + bm_unmap(p_addr); + spin_unlock_irqrestore(&b->bm_lock, flags); + return c; +} + + +/* inherently racy... + * return value may be already out-of-date when this function returns. + * but the general usage is that this is only use during a cstate when bits are + * only cleared, not set, and typically only care for the case when the return + * value is zero, or we already "locked" this "bitmap extent" by other means. + * + * enr is bm-extent number, since we chose to name one sector (512 bytes) + * worth of the bitmap a "bitmap extent". + * + * TODO + * I think since we use it like a reference count, we should use the real + * reference count of some bitmap extent element from some lru instead... + * + */ +int drbd_bm_e_weight(struct drbd_conf *mdev, unsigned long enr) +{ + struct drbd_bitmap *b = mdev->bitmap; + int count, s, e; + unsigned long flags; + unsigned long *p_addr, *bm; + + ERR_IF(!b) return 0; + ERR_IF(!b->bm_pages) return 0; + + spin_lock_irqsave(&b->bm_lock, flags); + if (bm_is_locked(b)) + bm_print_lock_info(mdev); + + s = S2W(enr); + e = min((size_t)S2W(enr+1), b->bm_words); + count = 0; + if (s < b->bm_words) { + int n = e-s; + p_addr = bm_map_paddr(b, s); + bm = p_addr + MLPP(s); + while (n--) + count += hweight_long(*bm++); + bm_unmap(p_addr); + } else { + dev_err(DEV, "start offset (%d) too large in drbd_bm_e_weight\n", s); + } + spin_unlock_irqrestore(&b->bm_lock, flags); + return count; +} + +/* set all bits covered by the AL-extent al_enr */ +unsigned long drbd_bm_ALe_set_all(struct drbd_conf *mdev, unsigned long al_enr) +{ + struct drbd_bitmap *b = mdev->bitmap; + unsigned long *p_addr, *bm; + unsigned long weight; + int count, s, e, i, do_now; + ERR_IF(!b) return 0; + ERR_IF(!b->bm_pages) return 0; + + spin_lock_irq(&b->bm_lock); + if (bm_is_locked(b)) + bm_print_lock_info(mdev); + weight = b->bm_set; + + s = al_enr * BM_WORDS_PER_AL_EXT; + e = min_t(size_t, s + BM_WORDS_PER_AL_EXT, b->bm_words); + /* assert that s and e are on the same page */ + D_ASSERT((e-1) >> (PAGE_SHIFT - LN2_BPL + 3) + == s >> (PAGE_SHIFT - LN2_BPL + 3)); + count = 0; + if (s < b->bm_words) { + i = do_now = e-s; + p_addr = bm_map_paddr(b, s); + bm = p_addr + MLPP(s); + while (i--) { + count += hweight_long(*bm); + *bm = -1UL; + bm++; + } + bm_unmap(p_addr); + b->bm_set += do_now*BITS_PER_LONG - count; + if (e == b->bm_words) + b->bm_set -= bm_clear_surplus(b); + } else { + dev_err(DEV, "start offset (%d) too large in drbd_bm_ALe_set_all\n", s); + } + weight = b->bm_set - weight; + spin_unlock_irq(&b->bm_lock); + return weight; +} diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h new file mode 100644 index 000000000000..2312d782fe99 --- /dev/null +++ b/drivers/block/drbd/drbd_int.h @@ -0,0 +1,2252 @@ +/* + drbd_int.h + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. + Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef _DRBD_INT_H +#define _DRBD_INT_H + +#include <linux/compiler.h> +#include <linux/types.h> +#include <linux/version.h> +#include <linux/list.h> +#include <linux/sched.h> +#include <linux/bitops.h> +#include <linux/slab.h> +#include <linux/crypto.h> +#include <linux/ratelimit.h> +#include <linux/tcp.h> +#include <linux/mutex.h> +#include <linux/major.h> +#include <linux/blkdev.h> +#include <linux/genhd.h> +#include <net/tcp.h> +#include <linux/lru_cache.h> + +#ifdef __CHECKER__ +# define __protected_by(x) __attribute__((require_context(x,1,999,"rdwr"))) +# define __protected_read_by(x) __attribute__((require_context(x,1,999,"read"))) +# define __protected_write_by(x) __attribute__((require_context(x,1,999,"write"))) +# define __must_hold(x) __attribute__((context(x,1,1), require_context(x,1,999,"call"))) +#else +# define __protected_by(x) +# define __protected_read_by(x) +# define __protected_write_by(x) +# define __must_hold(x) +#endif + +#define __no_warn(lock, stmt) do { __acquire(lock); stmt; __release(lock); } while (0) + +/* module parameter, defined in drbd_main.c */ +extern unsigned int minor_count; +extern int disable_sendpage; +extern int allow_oos; +extern unsigned int cn_idx; + +#ifdef CONFIG_DRBD_FAULT_INJECTION +extern int enable_faults; +extern int fault_rate; +extern int fault_devs; +#endif + +extern char usermode_helper[]; + + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* I don't remember why XCPU ... + * This is used to wake the asender, + * and to interrupt sending the sending task + * on disconnect. + */ +#define DRBD_SIG SIGXCPU + +/* This is used to stop/restart our threads. + * Cannot use SIGTERM nor SIGKILL, since these + * are sent out by init on runlevel changes + * I choose SIGHUP for now. + */ +#define DRBD_SIGKILL SIGHUP + +/* All EEs on the free list should have ID_VACANT (== 0) + * freshly allocated EEs get !ID_VACANT (== 1) + * so if it says "cannot dereference null pointer at adress 0x00000001", + * it is most likely one of these :( */ + +#define ID_IN_SYNC (4711ULL) +#define ID_OUT_OF_SYNC (4712ULL) + +#define ID_SYNCER (-1ULL) +#define ID_VACANT 0 +#define is_syncer_block_id(id) ((id) == ID_SYNCER) + +struct drbd_conf; + + +/* to shorten dev_warn(DEV, "msg"); and relatives statements */ +#define DEV (disk_to_dev(mdev->vdisk)) + +#define D_ASSERT(exp) if (!(exp)) \ + dev_err(DEV, "ASSERT( " #exp " ) in %s:%d\n", __FILE__, __LINE__) + +#define ERR_IF(exp) if (({ \ + int _b = (exp) != 0; \ + if (_b) dev_err(DEV, "%s: (%s) in %s:%d\n", \ + __func__, #exp, __FILE__, __LINE__); \ + _b; \ + })) + +/* Defines to control fault insertion */ +enum { + DRBD_FAULT_MD_WR = 0, /* meta data write */ + DRBD_FAULT_MD_RD = 1, /* read */ + DRBD_FAULT_RS_WR = 2, /* resync */ + DRBD_FAULT_RS_RD = 3, + DRBD_FAULT_DT_WR = 4, /* data */ + DRBD_FAULT_DT_RD = 5, + DRBD_FAULT_DT_RA = 6, /* data read ahead */ + DRBD_FAULT_BM_ALLOC = 7, /* bitmap allocation */ + DRBD_FAULT_AL_EE = 8, /* alloc ee */ + + DRBD_FAULT_MAX, +}; + +#ifdef CONFIG_DRBD_FAULT_INJECTION +extern unsigned int +_drbd_insert_fault(struct drbd_conf *mdev, unsigned int type); +static inline int +drbd_insert_fault(struct drbd_conf *mdev, unsigned int type) { + return fault_rate && + (enable_faults & (1<<type)) && + _drbd_insert_fault(mdev, type); +} +#define FAULT_ACTIVE(_m, _t) (drbd_insert_fault((_m), (_t))) + +#else +#define FAULT_ACTIVE(_m, _t) (0) +#endif + +/* integer division, round _UP_ to the next integer */ +#define div_ceil(A, B) ((A)/(B) + ((A)%(B) ? 1 : 0)) +/* usual integer division */ +#define div_floor(A, B) ((A)/(B)) + +/* drbd_meta-data.c (still in drbd_main.c) */ +/* 4th incarnation of the disk layout. */ +#define DRBD_MD_MAGIC (DRBD_MAGIC+4) + +extern struct drbd_conf **minor_table; +extern struct ratelimit_state drbd_ratelimit_state; + +/* on the wire */ +enum drbd_packets { + /* receiver (data socket) */ + P_DATA = 0x00, + P_DATA_REPLY = 0x01, /* Response to P_DATA_REQUEST */ + P_RS_DATA_REPLY = 0x02, /* Response to P_RS_DATA_REQUEST */ + P_BARRIER = 0x03, + P_BITMAP = 0x04, + P_BECOME_SYNC_TARGET = 0x05, + P_BECOME_SYNC_SOURCE = 0x06, + P_UNPLUG_REMOTE = 0x07, /* Used at various times to hint the peer */ + P_DATA_REQUEST = 0x08, /* Used to ask for a data block */ + P_RS_DATA_REQUEST = 0x09, /* Used to ask for a data block for resync */ + P_SYNC_PARAM = 0x0a, + P_PROTOCOL = 0x0b, + P_UUIDS = 0x0c, + P_SIZES = 0x0d, + P_STATE = 0x0e, + P_SYNC_UUID = 0x0f, + P_AUTH_CHALLENGE = 0x10, + P_AUTH_RESPONSE = 0x11, + P_STATE_CHG_REQ = 0x12, + + /* asender (meta socket */ + P_PING = 0x13, + P_PING_ACK = 0x14, + P_RECV_ACK = 0x15, /* Used in protocol B */ + P_WRITE_ACK = 0x16, /* Used in protocol C */ + P_RS_WRITE_ACK = 0x17, /* Is a P_WRITE_ACK, additionally call set_in_sync(). */ + P_DISCARD_ACK = 0x18, /* Used in proto C, two-primaries conflict detection */ + P_NEG_ACK = 0x19, /* Sent if local disk is unusable */ + P_NEG_DREPLY = 0x1a, /* Local disk is broken... */ + P_NEG_RS_DREPLY = 0x1b, /* Local disk is broken... */ + P_BARRIER_ACK = 0x1c, + P_STATE_CHG_REPLY = 0x1d, + + /* "new" commands, no longer fitting into the ordering scheme above */ + + P_OV_REQUEST = 0x1e, /* data socket */ + P_OV_REPLY = 0x1f, + P_OV_RESULT = 0x20, /* meta socket */ + P_CSUM_RS_REQUEST = 0x21, /* data socket */ + P_RS_IS_IN_SYNC = 0x22, /* meta socket */ + P_SYNC_PARAM89 = 0x23, /* data socket, protocol version 89 replacement for P_SYNC_PARAM */ + P_COMPRESSED_BITMAP = 0x24, /* compressed or otherwise encoded bitmap transfer */ + + P_MAX_CMD = 0x25, + P_MAY_IGNORE = 0x100, /* Flag to test if (cmd > P_MAY_IGNORE) ... */ + P_MAX_OPT_CMD = 0x101, + + /* special command ids for handshake */ + + P_HAND_SHAKE_M = 0xfff1, /* First Packet on the MetaSock */ + P_HAND_SHAKE_S = 0xfff2, /* First Packet on the Socket */ + + P_HAND_SHAKE = 0xfffe /* FIXED for the next century! */ +}; + +static inline const char *cmdname(enum drbd_packets cmd) +{ + /* THINK may need to become several global tables + * when we want to support more than + * one PRO_VERSION */ + static const char *cmdnames[] = { + [P_DATA] = "Data", + [P_DATA_REPLY] = "DataReply", + [P_RS_DATA_REPLY] = "RSDataReply", + [P_BARRIER] = "Barrier", + [P_BITMAP] = "ReportBitMap", + [P_BECOME_SYNC_TARGET] = "BecomeSyncTarget", + [P_BECOME_SYNC_SOURCE] = "BecomeSyncSource", + [P_UNPLUG_REMOTE] = "UnplugRemote", + [P_DATA_REQUEST] = "DataRequest", + [P_RS_DATA_REQUEST] = "RSDataRequest", + [P_SYNC_PARAM] = "SyncParam", + [P_SYNC_PARAM89] = "SyncParam89", + [P_PROTOCOL] = "ReportProtocol", + [P_UUIDS] = "ReportUUIDs", + [P_SIZES] = "ReportSizes", + [P_STATE] = "ReportState", + [P_SYNC_UUID] = "ReportSyncUUID", + [P_AUTH_CHALLENGE] = "AuthChallenge", + [P_AUTH_RESPONSE] = "AuthResponse", + [P_PING] = "Ping", + [P_PING_ACK] = "PingAck", + [P_RECV_ACK] = "RecvAck", + [P_WRITE_ACK] = "WriteAck", + [P_RS_WRITE_ACK] = "RSWriteAck", + [P_DISCARD_ACK] = "DiscardAck", + [P_NEG_ACK] = "NegAck", + [P_NEG_DREPLY] = "NegDReply", + [P_NEG_RS_DREPLY] = "NegRSDReply", + [P_BARRIER_ACK] = "BarrierAck", + [P_STATE_CHG_REQ] = "StateChgRequest", + [P_STATE_CHG_REPLY] = "StateChgReply", + [P_OV_REQUEST] = "OVRequest", + [P_OV_REPLY] = "OVReply", + [P_OV_RESULT] = "OVResult", + [P_MAX_CMD] = NULL, + }; + + if (cmd == P_HAND_SHAKE_M) + return "HandShakeM"; + if (cmd == P_HAND_SHAKE_S) + return "HandShakeS"; + if (cmd == P_HAND_SHAKE) + return "HandShake"; + if (cmd >= P_MAX_CMD) + return "Unknown"; + return cmdnames[cmd]; +} + +/* for sending/receiving the bitmap, + * possibly in some encoding scheme */ +struct bm_xfer_ctx { + /* "const" + * stores total bits and long words + * of the bitmap, so we don't need to + * call the accessor functions over and again. */ + unsigned long bm_bits; + unsigned long bm_words; + /* during xfer, current position within the bitmap */ + unsigned long bit_offset; + unsigned long word_offset; + + /* statistics; index: (h->command == P_BITMAP) */ + unsigned packets[2]; + unsigned bytes[2]; +}; + +extern void INFO_bm_xfer_stats(struct drbd_conf *mdev, + const char *direction, struct bm_xfer_ctx *c); + +static inline void bm_xfer_ctx_bit_to_word_offset(struct bm_xfer_ctx *c) +{ + /* word_offset counts "native long words" (32 or 64 bit), + * aligned at 64 bit. + * Encoded packet may end at an unaligned bit offset. + * In case a fallback clear text packet is transmitted in + * between, we adjust this offset back to the last 64bit + * aligned "native long word", which makes coding and decoding + * the plain text bitmap much more convenient. */ +#if BITS_PER_LONG == 64 + c->word_offset = c->bit_offset >> 6; +#elif BITS_PER_LONG == 32 + c->word_offset = c->bit_offset >> 5; + c->word_offset &= ~(1UL); +#else +# error "unsupported BITS_PER_LONG" +#endif +} + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +/* This is the layout for a packet on the wire. + * The byteorder is the network byte order. + * (except block_id and barrier fields. + * these are pointers to local structs + * and have no relevance for the partner, + * which just echoes them as received.) + * + * NOTE that the payload starts at a long aligned offset, + * regardless of 32 or 64 bit arch! + */ +struct p_header { + u32 magic; + u16 command; + u16 length; /* bytes of data after this header */ + u8 payload[0]; +} __packed; +/* 8 bytes. packet FIXED for the next century! */ + +/* + * short commands, packets without payload, plain p_header: + * P_PING + * P_PING_ACK + * P_BECOME_SYNC_TARGET + * P_BECOME_SYNC_SOURCE + * P_UNPLUG_REMOTE + */ + +/* + * commands with out-of-struct payload: + * P_BITMAP (no additional fields) + * P_DATA, P_DATA_REPLY (see p_data) + * P_COMPRESSED_BITMAP (see receive_compressed_bitmap) + */ + +/* these defines must not be changed without changing the protocol version */ +#define DP_HARDBARRIER 1 +#define DP_RW_SYNC 2 +#define DP_MAY_SET_IN_SYNC 4 + +struct p_data { + struct p_header head; + u64 sector; /* 64 bits sector number */ + u64 block_id; /* to identify the request in protocol B&C */ + u32 seq_num; + u32 dp_flags; +} __packed; + +/* + * commands which share a struct: + * p_block_ack: + * P_RECV_ACK (proto B), P_WRITE_ACK (proto C), + * P_DISCARD_ACK (proto C, two-primaries conflict detection) + * p_block_req: + * P_DATA_REQUEST, P_RS_DATA_REQUEST + */ +struct p_block_ack { + struct p_header head; + u64 sector; + u64 block_id; + u32 blksize; + u32 seq_num; +} __packed; + + +struct p_block_req { + struct p_header head; + u64 sector; + u64 block_id; + u32 blksize; + u32 pad; /* to multiple of 8 Byte */ +} __packed; + +/* + * commands with their own struct for additional fields: + * P_HAND_SHAKE + * P_BARRIER + * P_BARRIER_ACK + * P_SYNC_PARAM + * ReportParams + */ + +struct p_handshake { + struct p_header head; /* 8 bytes */ + u32 protocol_min; + u32 feature_flags; + u32 protocol_max; + + /* should be more than enough for future enhancements + * for now, feature_flags and the reserverd array shall be zero. + */ + + u32 _pad; + u64 reserverd[7]; +} __packed; +/* 80 bytes, FIXED for the next century */ + +struct p_barrier { + struct p_header head; + u32 barrier; /* barrier number _handle_ only */ + u32 pad; /* to multiple of 8 Byte */ +} __packed; + +struct p_barrier_ack { + struct p_header head; + u32 barrier; + u32 set_size; +} __packed; + +struct p_rs_param { + struct p_header head; + u32 rate; + + /* Since protocol version 88 and higher. */ + char verify_alg[0]; +} __packed; + +struct p_rs_param_89 { + struct p_header head; + u32 rate; + /* protocol version 89: */ + char verify_alg[SHARED_SECRET_MAX]; + char csums_alg[SHARED_SECRET_MAX]; +} __packed; + +struct p_protocol { + struct p_header head; + u32 protocol; + u32 after_sb_0p; + u32 after_sb_1p; + u32 after_sb_2p; + u32 want_lose; + u32 two_primaries; + + /* Since protocol version 87 and higher. */ + char integrity_alg[0]; + +} __packed; + +struct p_uuids { + struct p_header head; + u64 uuid[UI_EXTENDED_SIZE]; +} __packed; + +struct p_rs_uuid { + struct p_header head; + u64 uuid; +} __packed; + +struct p_sizes { + struct p_header head; + u64 d_size; /* size of disk */ + u64 u_size; /* user requested size */ + u64 c_size; /* current exported size */ + u32 max_segment_size; /* Maximal size of a BIO */ + u32 queue_order_type; +} __packed; + +struct p_state { + struct p_header head; + u32 state; +} __packed; + +struct p_req_state { + struct p_header head; + u32 mask; + u32 val; +} __packed; + +struct p_req_state_reply { + struct p_header head; + u32 retcode; +} __packed; + +struct p_drbd06_param { + u64 size; + u32 state; + u32 blksize; + u32 protocol; + u32 version; + u32 gen_cnt[5]; + u32 bit_map_gen[5]; +} __packed; + +struct p_discard { + struct p_header head; + u64 block_id; + u32 seq_num; + u32 pad; +} __packed; + +/* Valid values for the encoding field. + * Bump proto version when changing this. */ +enum drbd_bitmap_code { + /* RLE_VLI_Bytes = 0, + * and other bit variants had been defined during + * algorithm evaluation. */ + RLE_VLI_Bits = 2, +}; + +struct p_compressed_bm { + struct p_header head; + /* (encoding & 0x0f): actual encoding, see enum drbd_bitmap_code + * (encoding & 0x80): polarity (set/unset) of first runlength + * ((encoding >> 4) & 0x07): pad_bits, number of trailing zero bits + * used to pad up to head.length bytes + */ + u8 encoding; + + u8 code[0]; +} __packed; + +/* DCBP: Drbd Compressed Bitmap Packet ... */ +static inline enum drbd_bitmap_code +DCBP_get_code(struct p_compressed_bm *p) +{ + return (enum drbd_bitmap_code)(p->encoding & 0x0f); +} + +static inline void +DCBP_set_code(struct p_compressed_bm *p, enum drbd_bitmap_code code) +{ + BUG_ON(code & ~0xf); + p->encoding = (p->encoding & ~0xf) | code; +} + +static inline int +DCBP_get_start(struct p_compressed_bm *p) +{ + return (p->encoding & 0x80) != 0; +} + +static inline void +DCBP_set_start(struct p_compressed_bm *p, int set) +{ + p->encoding = (p->encoding & ~0x80) | (set ? 0x80 : 0); +} + +static inline int +DCBP_get_pad_bits(struct p_compressed_bm *p) +{ + return (p->encoding >> 4) & 0x7; +} + +static inline void +DCBP_set_pad_bits(struct p_compressed_bm *p, int n) +{ + BUG_ON(n & ~0x7); + p->encoding = (p->encoding & (~0x7 << 4)) | (n << 4); +} + +/* one bitmap packet, including the p_header, + * should fit within one _architecture independend_ page. + * so we need to use the fixed size 4KiB page size + * most architechtures have used for a long time. + */ +#define BM_PACKET_PAYLOAD_BYTES (4096 - sizeof(struct p_header)) +#define BM_PACKET_WORDS (BM_PACKET_PAYLOAD_BYTES/sizeof(long)) +#define BM_PACKET_VLI_BYTES_MAX (4096 - sizeof(struct p_compressed_bm)) +#if (PAGE_SIZE < 4096) +/* drbd_send_bitmap / receive_bitmap would break horribly */ +#error "PAGE_SIZE too small" +#endif + +union p_polymorph { + struct p_header header; + struct p_handshake handshake; + struct p_data data; + struct p_block_ack block_ack; + struct p_barrier barrier; + struct p_barrier_ack barrier_ack; + struct p_rs_param_89 rs_param_89; + struct p_protocol protocol; + struct p_sizes sizes; + struct p_uuids uuids; + struct p_state state; + struct p_req_state req_state; + struct p_req_state_reply req_state_reply; + struct p_block_req block_req; +} __packed; + +/**********************************************************************/ +enum drbd_thread_state { + None, + Running, + Exiting, + Restarting +}; + +struct drbd_thread { + spinlock_t t_lock; + struct task_struct *task; + struct completion stop; + enum drbd_thread_state t_state; + int (*function) (struct drbd_thread *); + struct drbd_conf *mdev; + int reset_cpu_mask; +}; + +static inline enum drbd_thread_state get_t_state(struct drbd_thread *thi) +{ + /* THINK testing the t_state seems to be uncritical in all cases + * (but thread_{start,stop}), so we can read it *without* the lock. + * --lge */ + + smp_rmb(); + return thi->t_state; +} + + +/* + * Having this as the first member of a struct provides sort of "inheritance". + * "derived" structs can be "drbd_queue_work()"ed. + * The callback should know and cast back to the descendant struct. + * drbd_request and drbd_epoch_entry are descendants of drbd_work. + */ +struct drbd_work; +typedef int (*drbd_work_cb)(struct drbd_conf *, struct drbd_work *, int cancel); +struct drbd_work { + struct list_head list; + drbd_work_cb cb; +}; + +struct drbd_tl_epoch; +struct drbd_request { + struct drbd_work w; + struct drbd_conf *mdev; + + /* if local IO is not allowed, will be NULL. + * if local IO _is_ allowed, holds the locally submitted bio clone, + * or, after local IO completion, the ERR_PTR(error). + * see drbd_endio_pri(). */ + struct bio *private_bio; + + struct hlist_node colision; + sector_t sector; + unsigned int size; + unsigned int epoch; /* barrier_nr */ + + /* barrier_nr: used to check on "completion" whether this req was in + * the current epoch, and we therefore have to close it, + * starting a new epoch... + */ + + /* up to here, the struct layout is identical to drbd_epoch_entry; + * we might be able to use that to our advantage... */ + + struct list_head tl_requests; /* ring list in the transfer log */ + struct bio *master_bio; /* master bio pointer */ + unsigned long rq_state; /* see comments above _req_mod() */ + int seq_num; + unsigned long start_time; +}; + +struct drbd_tl_epoch { + struct drbd_work w; + struct list_head requests; /* requests before */ + struct drbd_tl_epoch *next; /* pointer to the next barrier */ + unsigned int br_number; /* the barriers identifier. */ + int n_req; /* number of requests attached before this barrier */ +}; + +struct drbd_request; + +/* These Tl_epoch_entries may be in one of 6 lists: + active_ee .. data packet being written + sync_ee .. syncer block being written + done_ee .. block written, need to send P_WRITE_ACK + read_ee .. [RS]P_DATA_REQUEST being read +*/ + +struct drbd_epoch { + struct list_head list; + unsigned int barrier_nr; + atomic_t epoch_size; /* increased on every request added. */ + atomic_t active; /* increased on every req. added, and dec on every finished. */ + unsigned long flags; +}; + +/* drbd_epoch flag bits */ +enum { + DE_BARRIER_IN_NEXT_EPOCH_ISSUED, + DE_BARRIER_IN_NEXT_EPOCH_DONE, + DE_CONTAINS_A_BARRIER, + DE_HAVE_BARRIER_NUMBER, + DE_IS_FINISHING, +}; + +enum epoch_event { + EV_PUT, + EV_GOT_BARRIER_NR, + EV_BARRIER_DONE, + EV_BECAME_LAST, + EV_CLEANUP = 32, /* used as flag */ +}; + +struct drbd_epoch_entry { + struct drbd_work w; + struct drbd_conf *mdev; + struct bio *private_bio; + struct hlist_node colision; + sector_t sector; + unsigned int size; + struct drbd_epoch *epoch; + + /* up to here, the struct layout is identical to drbd_request; + * we might be able to use that to our advantage... */ + + unsigned int flags; + u64 block_id; +}; + +struct drbd_wq_barrier { + struct drbd_work w; + struct completion done; +}; + +struct digest_info { + int digest_size; + void *digest; +}; + +/* ee flag bits */ +enum { + __EE_CALL_AL_COMPLETE_IO, + __EE_CONFLICT_PENDING, + __EE_MAY_SET_IN_SYNC, + __EE_IS_BARRIER, +}; +#define EE_CALL_AL_COMPLETE_IO (1<<__EE_CALL_AL_COMPLETE_IO) +#define EE_CONFLICT_PENDING (1<<__EE_CONFLICT_PENDING) +#define EE_MAY_SET_IN_SYNC (1<<__EE_MAY_SET_IN_SYNC) +#define EE_IS_BARRIER (1<<__EE_IS_BARRIER) + +/* global flag bits */ +enum { + CREATE_BARRIER, /* next P_DATA is preceeded by a P_BARRIER */ + SIGNAL_ASENDER, /* whether asender wants to be interrupted */ + SEND_PING, /* whether asender should send a ping asap */ + + STOP_SYNC_TIMER, /* tell timer to cancel itself */ + UNPLUG_QUEUED, /* only relevant with kernel 2.4 */ + UNPLUG_REMOTE, /* sending a "UnplugRemote" could help */ + MD_DIRTY, /* current uuids and flags not yet on disk */ + DISCARD_CONCURRENT, /* Set on one node, cleared on the peer! */ + USE_DEGR_WFC_T, /* degr-wfc-timeout instead of wfc-timeout. */ + CLUSTER_ST_CHANGE, /* Cluster wide state change going on... */ + CL_ST_CHG_SUCCESS, + CL_ST_CHG_FAIL, + CRASHED_PRIMARY, /* This node was a crashed primary. + * Gets cleared when the state.conn + * goes into C_CONNECTED state. */ + WRITE_BM_AFTER_RESYNC, /* A kmalloc() during resync failed */ + NO_BARRIER_SUPP, /* underlying block device doesn't implement barriers */ + CONSIDER_RESYNC, + + MD_NO_BARRIER, /* meta data device does not support barriers, + so don't even try */ + SUSPEND_IO, /* suspend application io */ + BITMAP_IO, /* suspend application io; + once no more io in flight, start bitmap io */ + BITMAP_IO_QUEUED, /* Started bitmap IO */ + RESYNC_AFTER_NEG, /* Resync after online grow after the attach&negotiate finished. */ + NET_CONGESTED, /* The data socket is congested */ + + CONFIG_PENDING, /* serialization of (re)configuration requests. + * if set, also prevents the device from dying */ + DEVICE_DYING, /* device became unconfigured, + * but worker thread is still handling the cleanup. + * reconfiguring (nl_disk_conf, nl_net_conf) is dissalowed, + * while this is set. */ + RESIZE_PENDING, /* Size change detected locally, waiting for the response from + * the peer, if it changed there as well. */ +}; + +struct drbd_bitmap; /* opaque for drbd_conf */ + +/* TODO sort members for performance + * MAYBE group them further */ + +/* THINK maybe we actually want to use the default "event/%s" worker threads + * or similar in linux 2.6, which uses per cpu data and threads. + * + * To be general, this might need a spin_lock member. + * For now, please use the mdev->req_lock to protect list_head, + * see drbd_queue_work below. + */ +struct drbd_work_queue { + struct list_head q; + struct semaphore s; /* producers up it, worker down()s it */ + spinlock_t q_lock; /* to protect the list. */ +}; + +struct drbd_socket { + struct drbd_work_queue work; + struct mutex mutex; + struct socket *socket; + /* this way we get our + * send/receive buffers off the stack */ + union p_polymorph sbuf; + union p_polymorph rbuf; +}; + +struct drbd_md { + u64 md_offset; /* sector offset to 'super' block */ + + u64 la_size_sect; /* last agreed size, unit sectors */ + u64 uuid[UI_SIZE]; + u64 device_uuid; + u32 flags; + u32 md_size_sect; + + s32 al_offset; /* signed relative sector offset to al area */ + s32 bm_offset; /* signed relative sector offset to bitmap */ + + /* u32 al_nr_extents; important for restoring the AL + * is stored into sync_conf.al_extents, which in turn + * gets applied to act_log->nr_elements + */ +}; + +/* for sync_conf and other types... */ +#define NL_PACKET(name, number, fields) struct name { fields }; +#define NL_INTEGER(pn,pr,member) int member; +#define NL_INT64(pn,pr,member) __u64 member; +#define NL_BIT(pn,pr,member) unsigned member:1; +#define NL_STRING(pn,pr,member,len) unsigned char member[len]; int member ## _len; +#include "linux/drbd_nl.h" + +struct drbd_backing_dev { + struct block_device *backing_bdev; + struct block_device *md_bdev; + struct file *lo_file; + struct file *md_file; + struct drbd_md md; + struct disk_conf dc; /* The user provided config... */ + sector_t known_size; /* last known size of that backing device */ +}; + +struct drbd_md_io { + struct drbd_conf *mdev; + struct completion event; + int error; +}; + +struct bm_io_work { + struct drbd_work w; + char *why; + int (*io_fn)(struct drbd_conf *mdev); + void (*done)(struct drbd_conf *mdev, int rv); +}; + +enum write_ordering_e { + WO_none, + WO_drain_io, + WO_bdev_flush, + WO_bio_barrier +}; + +struct drbd_conf { + /* things that are stored as / read from meta data on disk */ + unsigned long flags; + + /* configured by drbdsetup */ + struct net_conf *net_conf; /* protected by get_net_conf() and put_net_conf() */ + struct syncer_conf sync_conf; + struct drbd_backing_dev *ldev __protected_by(local); + + sector_t p_size; /* partner's disk size */ + struct request_queue *rq_queue; + struct block_device *this_bdev; + struct gendisk *vdisk; + + struct drbd_socket data; /* data/barrier/cstate/parameter packets */ + struct drbd_socket meta; /* ping/ack (metadata) packets */ + int agreed_pro_version; /* actually used protocol version */ + unsigned long last_received; /* in jiffies, either socket */ + unsigned int ko_count; + struct drbd_work resync_work, + unplug_work, + md_sync_work; + struct timer_list resync_timer; + struct timer_list md_sync_timer; + + /* Used after attach while negotiating new disk state. */ + union drbd_state new_state_tmp; + + union drbd_state state; + wait_queue_head_t misc_wait; + wait_queue_head_t state_wait; /* upon each state change. */ + unsigned int send_cnt; + unsigned int recv_cnt; + unsigned int read_cnt; + unsigned int writ_cnt; + unsigned int al_writ_cnt; + unsigned int bm_writ_cnt; + atomic_t ap_bio_cnt; /* Requests we need to complete */ + atomic_t ap_pending_cnt; /* AP data packets on the wire, ack expected */ + atomic_t rs_pending_cnt; /* RS request/data packets on the wire */ + atomic_t unacked_cnt; /* Need to send replys for */ + atomic_t local_cnt; /* Waiting for local completion */ + atomic_t net_cnt; /* Users of net_conf */ + spinlock_t req_lock; + struct drbd_tl_epoch *unused_spare_tle; /* for pre-allocation */ + struct drbd_tl_epoch *newest_tle; + struct drbd_tl_epoch *oldest_tle; + struct list_head out_of_sequence_requests; + struct hlist_head *tl_hash; + unsigned int tl_hash_s; + + /* blocks to sync in this run [unit BM_BLOCK_SIZE] */ + unsigned long rs_total; + /* number of sync IOs that failed in this run */ + unsigned long rs_failed; + /* Syncer's start time [unit jiffies] */ + unsigned long rs_start; + /* cumulated time in PausedSyncX state [unit jiffies] */ + unsigned long rs_paused; + /* block not up-to-date at mark [unit BM_BLOCK_SIZE] */ + unsigned long rs_mark_left; + /* marks's time [unit jiffies] */ + unsigned long rs_mark_time; + /* skipped because csum was equeal [unit BM_BLOCK_SIZE] */ + unsigned long rs_same_csum; + + /* where does the admin want us to start? (sector) */ + sector_t ov_start_sector; + /* where are we now? (sector) */ + sector_t ov_position; + /* Start sector of out of sync range (to merge printk reporting). */ + sector_t ov_last_oos_start; + /* size of out-of-sync range in sectors. */ + sector_t ov_last_oos_size; + unsigned long ov_left; /* in bits */ + struct crypto_hash *csums_tfm; + struct crypto_hash *verify_tfm; + + struct drbd_thread receiver; + struct drbd_thread worker; + struct drbd_thread asender; + struct drbd_bitmap *bitmap; + unsigned long bm_resync_fo; /* bit offset for drbd_bm_find_next */ + + /* Used to track operations of resync... */ + struct lru_cache *resync; + /* Number of locked elements in resync LRU */ + unsigned int resync_locked; + /* resync extent number waiting for application requests */ + unsigned int resync_wenr; + + int open_cnt; + u64 *p_uuid; + struct drbd_epoch *current_epoch; + spinlock_t epoch_lock; + unsigned int epochs; + enum write_ordering_e write_ordering; + struct list_head active_ee; /* IO in progress */ + struct list_head sync_ee; /* IO in progress */ + struct list_head done_ee; /* send ack */ + struct list_head read_ee; /* IO in progress */ + struct list_head net_ee; /* zero-copy network send in progress */ + struct hlist_head *ee_hash; /* is proteced by req_lock! */ + unsigned int ee_hash_s; + + /* this one is protected by ee_lock, single thread */ + struct drbd_epoch_entry *last_write_w_barrier; + + int next_barrier_nr; + struct hlist_head *app_reads_hash; /* is proteced by req_lock */ + struct list_head resync_reads; + atomic_t pp_in_use; + wait_queue_head_t ee_wait; + struct page *md_io_page; /* one page buffer for md_io */ + struct page *md_io_tmpp; /* for logical_block_size != 512 */ + struct mutex md_io_mutex; /* protects the md_io_buffer */ + spinlock_t al_lock; + wait_queue_head_t al_wait; + struct lru_cache *act_log; /* activity log */ + unsigned int al_tr_number; + int al_tr_cycle; + int al_tr_pos; /* position of the next transaction in the journal */ + struct crypto_hash *cram_hmac_tfm; + struct crypto_hash *integrity_w_tfm; /* to be used by the worker thread */ + struct crypto_hash *integrity_r_tfm; /* to be used by the receiver thread */ + void *int_dig_out; + void *int_dig_in; + void *int_dig_vv; + wait_queue_head_t seq_wait; + atomic_t packet_seq; + unsigned int peer_seq; + spinlock_t peer_seq_lock; + unsigned int minor; + unsigned long comm_bm_set; /* communicated number of set bits. */ + cpumask_var_t cpu_mask; + struct bm_io_work bm_io_work; + u64 ed_uuid; /* UUID of the exposed data */ + struct mutex state_mutex; + char congestion_reason; /* Why we where congested... */ +}; + +static inline struct drbd_conf *minor_to_mdev(unsigned int minor) +{ + struct drbd_conf *mdev; + + mdev = minor < minor_count ? minor_table[minor] : NULL; + + return mdev; +} + +static inline unsigned int mdev_to_minor(struct drbd_conf *mdev) +{ + return mdev->minor; +} + +/* returns 1 if it was successfull, + * returns 0 if there was no data socket. + * so wherever you are going to use the data.socket, e.g. do + * if (!drbd_get_data_sock(mdev)) + * return 0; + * CODE(); + * drbd_put_data_sock(mdev); + */ +static inline int drbd_get_data_sock(struct drbd_conf *mdev) +{ + mutex_lock(&mdev->data.mutex); + /* drbd_disconnect() could have called drbd_free_sock() + * while we were waiting in down()... */ + if (unlikely(mdev->data.socket == NULL)) { + mutex_unlock(&mdev->data.mutex); + return 0; + } + return 1; +} + +static inline void drbd_put_data_sock(struct drbd_conf *mdev) +{ + mutex_unlock(&mdev->data.mutex); +} + +/* + * function declarations + *************************/ + +/* drbd_main.c */ + +enum chg_state_flags { + CS_HARD = 1, + CS_VERBOSE = 2, + CS_WAIT_COMPLETE = 4, + CS_SERIALIZE = 8, + CS_ORDERED = CS_WAIT_COMPLETE + CS_SERIALIZE, +}; + +extern void drbd_init_set_defaults(struct drbd_conf *mdev); +extern int drbd_change_state(struct drbd_conf *mdev, enum chg_state_flags f, + union drbd_state mask, union drbd_state val); +extern void drbd_force_state(struct drbd_conf *, union drbd_state, + union drbd_state); +extern int _drbd_request_state(struct drbd_conf *, union drbd_state, + union drbd_state, enum chg_state_flags); +extern int __drbd_set_state(struct drbd_conf *, union drbd_state, + enum chg_state_flags, struct completion *done); +extern void print_st_err(struct drbd_conf *, union drbd_state, + union drbd_state, int); +extern int drbd_thread_start(struct drbd_thread *thi); +extern void _drbd_thread_stop(struct drbd_thread *thi, int restart, int wait); +#ifdef CONFIG_SMP +extern void drbd_thread_current_set_cpu(struct drbd_conf *mdev); +extern void drbd_calc_cpu_mask(struct drbd_conf *mdev); +#else +#define drbd_thread_current_set_cpu(A) ({}) +#define drbd_calc_cpu_mask(A) ({}) +#endif +extern void drbd_free_resources(struct drbd_conf *mdev); +extern void tl_release(struct drbd_conf *mdev, unsigned int barrier_nr, + unsigned int set_size); +extern void tl_clear(struct drbd_conf *mdev); +extern void _tl_add_barrier(struct drbd_conf *, struct drbd_tl_epoch *); +extern void drbd_free_sock(struct drbd_conf *mdev); +extern int drbd_send(struct drbd_conf *mdev, struct socket *sock, + void *buf, size_t size, unsigned msg_flags); +extern int drbd_send_protocol(struct drbd_conf *mdev); +extern int drbd_send_uuids(struct drbd_conf *mdev); +extern int drbd_send_uuids_skip_initial_sync(struct drbd_conf *mdev); +extern int drbd_send_sync_uuid(struct drbd_conf *mdev, u64 val); +extern int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply); +extern int _drbd_send_state(struct drbd_conf *mdev); +extern int drbd_send_state(struct drbd_conf *mdev); +extern int _drbd_send_cmd(struct drbd_conf *mdev, struct socket *sock, + enum drbd_packets cmd, struct p_header *h, + size_t size, unsigned msg_flags); +#define USE_DATA_SOCKET 1 +#define USE_META_SOCKET 0 +extern int drbd_send_cmd(struct drbd_conf *mdev, int use_data_socket, + enum drbd_packets cmd, struct p_header *h, + size_t size); +extern int drbd_send_cmd2(struct drbd_conf *mdev, enum drbd_packets cmd, + char *data, size_t size); +extern int drbd_send_sync_param(struct drbd_conf *mdev, struct syncer_conf *sc); +extern int drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr, + u32 set_size); +extern int drbd_send_ack(struct drbd_conf *mdev, enum drbd_packets cmd, + struct drbd_epoch_entry *e); +extern int drbd_send_ack_rp(struct drbd_conf *mdev, enum drbd_packets cmd, + struct p_block_req *rp); +extern int drbd_send_ack_dp(struct drbd_conf *mdev, enum drbd_packets cmd, + struct p_data *dp); +extern int drbd_send_ack_ex(struct drbd_conf *mdev, enum drbd_packets cmd, + sector_t sector, int blksize, u64 block_id); +extern int drbd_send_block(struct drbd_conf *mdev, enum drbd_packets cmd, + struct drbd_epoch_entry *e); +extern int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req); +extern int _drbd_send_barrier(struct drbd_conf *mdev, + struct drbd_tl_epoch *barrier); +extern int drbd_send_drequest(struct drbd_conf *mdev, int cmd, + sector_t sector, int size, u64 block_id); +extern int drbd_send_drequest_csum(struct drbd_conf *mdev, + sector_t sector,int size, + void *digest, int digest_size, + enum drbd_packets cmd); +extern int drbd_send_ov_request(struct drbd_conf *mdev,sector_t sector,int size); + +extern int drbd_send_bitmap(struct drbd_conf *mdev); +extern int _drbd_send_bitmap(struct drbd_conf *mdev); +extern int drbd_send_sr_reply(struct drbd_conf *mdev, int retcode); +extern void drbd_free_bc(struct drbd_backing_dev *ldev); +extern void drbd_mdev_cleanup(struct drbd_conf *mdev); + +/* drbd_meta-data.c (still in drbd_main.c) */ +extern void drbd_md_sync(struct drbd_conf *mdev); +extern int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev); +/* maybe define them below as inline? */ +extern void drbd_uuid_set(struct drbd_conf *mdev, int idx, u64 val) __must_hold(local); +extern void _drbd_uuid_set(struct drbd_conf *mdev, int idx, u64 val) __must_hold(local); +extern void drbd_uuid_new_current(struct drbd_conf *mdev) __must_hold(local); +extern void _drbd_uuid_new_current(struct drbd_conf *mdev) __must_hold(local); +extern void drbd_uuid_set_bm(struct drbd_conf *mdev, u64 val) __must_hold(local); +extern void drbd_md_set_flag(struct drbd_conf *mdev, int flags) __must_hold(local); +extern void drbd_md_clear_flag(struct drbd_conf *mdev, int flags)__must_hold(local); +extern int drbd_md_test_flag(struct drbd_backing_dev *, int); +extern void drbd_md_mark_dirty(struct drbd_conf *mdev); +extern void drbd_queue_bitmap_io(struct drbd_conf *mdev, + int (*io_fn)(struct drbd_conf *), + void (*done)(struct drbd_conf *, int), + char *why); +extern int drbd_bmio_set_n_write(struct drbd_conf *mdev); +extern int drbd_bmio_clear_n_write(struct drbd_conf *mdev); +extern int drbd_bitmap_io(struct drbd_conf *mdev, int (*io_fn)(struct drbd_conf *), char *why); + + +/* Meta data layout + We reserve a 128MB Block (4k aligned) + * either at the end of the backing device + * or on a seperate meta data device. */ + +#define MD_RESERVED_SECT (128LU << 11) /* 128 MB, unit sectors */ +/* The following numbers are sectors */ +#define MD_AL_OFFSET 8 /* 8 Sectors after start of meta area */ +#define MD_AL_MAX_SIZE 64 /* = 32 kb LOG ~ 3776 extents ~ 14 GB Storage */ +/* Allows up to about 3.8TB */ +#define MD_BM_OFFSET (MD_AL_OFFSET + MD_AL_MAX_SIZE) + +/* Since the smalles IO unit is usually 512 byte */ +#define MD_SECTOR_SHIFT 9 +#define MD_SECTOR_SIZE (1<<MD_SECTOR_SHIFT) + +/* activity log */ +#define AL_EXTENTS_PT ((MD_SECTOR_SIZE-12)/8-1) /* 61 ; Extents per 512B sector */ +#define AL_EXTENT_SHIFT 22 /* One extent represents 4M Storage */ +#define AL_EXTENT_SIZE (1<<AL_EXTENT_SHIFT) + +#if BITS_PER_LONG == 32 +#define LN2_BPL 5 +#define cpu_to_lel(A) cpu_to_le32(A) +#define lel_to_cpu(A) le32_to_cpu(A) +#elif BITS_PER_LONG == 64 +#define LN2_BPL 6 +#define cpu_to_lel(A) cpu_to_le64(A) +#define lel_to_cpu(A) le64_to_cpu(A) +#else +#error "LN2 of BITS_PER_LONG unknown!" +#endif + +/* resync bitmap */ +/* 16MB sized 'bitmap extent' to track syncer usage */ +struct bm_extent { + int rs_left; /* number of bits set (out of sync) in this extent. */ + int rs_failed; /* number of failed resync requests in this extent. */ + unsigned long flags; + struct lc_element lce; +}; + +#define BME_NO_WRITES 0 /* bm_extent.flags: no more requests on this one! */ +#define BME_LOCKED 1 /* bm_extent.flags: syncer active on this one. */ + +/* drbd_bitmap.c */ +/* + * We need to store one bit for a block. + * Example: 1GB disk @ 4096 byte blocks ==> we need 32 KB bitmap. + * Bit 0 ==> local node thinks this block is binary identical on both nodes + * Bit 1 ==> local node thinks this block needs to be synced. + */ + +#define BM_BLOCK_SHIFT 12 /* 4k per bit */ +#define BM_BLOCK_SIZE (1<<BM_BLOCK_SHIFT) +/* (9+3) : 512 bytes @ 8 bits; representing 16M storage + * per sector of on disk bitmap */ +#define BM_EXT_SHIFT (BM_BLOCK_SHIFT + MD_SECTOR_SHIFT + 3) /* = 24 */ +#define BM_EXT_SIZE (1<<BM_EXT_SHIFT) + +#if (BM_EXT_SHIFT != 24) || (BM_BLOCK_SHIFT != 12) +#error "HAVE YOU FIXED drbdmeta AS WELL??" +#endif + +/* thus many _storage_ sectors are described by one bit */ +#define BM_SECT_TO_BIT(x) ((x)>>(BM_BLOCK_SHIFT-9)) +#define BM_BIT_TO_SECT(x) ((sector_t)(x)<<(BM_BLOCK_SHIFT-9)) +#define BM_SECT_PER_BIT BM_BIT_TO_SECT(1) + +/* bit to represented kilo byte conversion */ +#define Bit2KB(bits) ((bits)<<(BM_BLOCK_SHIFT-10)) + +/* in which _bitmap_ extent (resp. sector) the bit for a certain + * _storage_ sector is located in */ +#define BM_SECT_TO_EXT(x) ((x)>>(BM_EXT_SHIFT-9)) + +/* how much _storage_ sectors we have per bitmap sector */ +#define BM_EXT_TO_SECT(x) ((sector_t)(x) << (BM_EXT_SHIFT-9)) +#define BM_SECT_PER_EXT BM_EXT_TO_SECT(1) + +/* in one sector of the bitmap, we have this many activity_log extents. */ +#define AL_EXT_PER_BM_SECT (1 << (BM_EXT_SHIFT - AL_EXTENT_SHIFT)) +#define BM_WORDS_PER_AL_EXT (1 << (AL_EXTENT_SHIFT-BM_BLOCK_SHIFT-LN2_BPL)) + +#define BM_BLOCKS_PER_BM_EXT_B (BM_EXT_SHIFT - BM_BLOCK_SHIFT) +#define BM_BLOCKS_PER_BM_EXT_MASK ((1<<BM_BLOCKS_PER_BM_EXT_B) - 1) + +/* the extent in "PER_EXTENT" below is an activity log extent + * we need that many (long words/bytes) to store the bitmap + * of one AL_EXTENT_SIZE chunk of storage. + * we can store the bitmap for that many AL_EXTENTS within + * one sector of the _on_disk_ bitmap: + * bit 0 bit 37 bit 38 bit (512*8)-1 + * ...|........|........|.. // ..|........| + * sect. 0 `296 `304 ^(512*8*8)-1 + * +#define BM_WORDS_PER_EXT ( (AL_EXT_SIZE/BM_BLOCK_SIZE) / BITS_PER_LONG ) +#define BM_BYTES_PER_EXT ( (AL_EXT_SIZE/BM_BLOCK_SIZE) / 8 ) // 128 +#define BM_EXT_PER_SECT ( 512 / BM_BYTES_PER_EXTENT ) // 4 + */ + +#define DRBD_MAX_SECTORS_32 (0xffffffffLU) +#define DRBD_MAX_SECTORS_BM \ + ((MD_RESERVED_SECT - MD_BM_OFFSET) * (1LL<<(BM_EXT_SHIFT-9))) +#if DRBD_MAX_SECTORS_BM < DRBD_MAX_SECTORS_32 +#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_BM +#define DRBD_MAX_SECTORS_FLEX DRBD_MAX_SECTORS_BM +#elif !defined(CONFIG_LBD) && BITS_PER_LONG == 32 +#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_32 +#define DRBD_MAX_SECTORS_FLEX DRBD_MAX_SECTORS_32 +#else +#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_BM +/* 16 TB in units of sectors */ +#if BITS_PER_LONG == 32 +/* adjust by one page worth of bitmap, + * so we won't wrap around in drbd_bm_find_next_bit. + * you should use 64bit OS for that much storage, anyways. */ +#define DRBD_MAX_SECTORS_FLEX BM_BIT_TO_SECT(0xffff7fff) +#else +#define DRBD_MAX_SECTORS_FLEX BM_BIT_TO_SECT(0x1LU << 32) +#endif +#endif + +/* Sector shift value for the "hash" functions of tl_hash and ee_hash tables. + * With a value of 6 all IO in one 32K block make it to the same slot of the + * hash table. */ +#define HT_SHIFT 6 +#define DRBD_MAX_SEGMENT_SIZE (1U<<(9+HT_SHIFT)) + +/* Number of elements in the app_reads_hash */ +#define APP_R_HSIZE 15 + +extern int drbd_bm_init(struct drbd_conf *mdev); +extern int drbd_bm_resize(struct drbd_conf *mdev, sector_t sectors); +extern void drbd_bm_cleanup(struct drbd_conf *mdev); +extern void drbd_bm_set_all(struct drbd_conf *mdev); +extern void drbd_bm_clear_all(struct drbd_conf *mdev); +extern int drbd_bm_set_bits( + struct drbd_conf *mdev, unsigned long s, unsigned long e); +extern int drbd_bm_clear_bits( + struct drbd_conf *mdev, unsigned long s, unsigned long e); +/* bm_set_bits variant for use while holding drbd_bm_lock */ +extern void _drbd_bm_set_bits(struct drbd_conf *mdev, + const unsigned long s, const unsigned long e); +extern int drbd_bm_test_bit(struct drbd_conf *mdev, unsigned long bitnr); +extern int drbd_bm_e_weight(struct drbd_conf *mdev, unsigned long enr); +extern int drbd_bm_write_sect(struct drbd_conf *mdev, unsigned long enr) __must_hold(local); +extern int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local); +extern int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local); +extern unsigned long drbd_bm_ALe_set_all(struct drbd_conf *mdev, + unsigned long al_enr); +extern size_t drbd_bm_words(struct drbd_conf *mdev); +extern unsigned long drbd_bm_bits(struct drbd_conf *mdev); +extern sector_t drbd_bm_capacity(struct drbd_conf *mdev); +extern unsigned long drbd_bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo); +/* bm_find_next variants for use while you hold drbd_bm_lock() */ +extern unsigned long _drbd_bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo); +extern unsigned long _drbd_bm_find_next_zero(struct drbd_conf *mdev, unsigned long bm_fo); +extern unsigned long drbd_bm_total_weight(struct drbd_conf *mdev); +extern int drbd_bm_rs_done(struct drbd_conf *mdev); +/* for receive_bitmap */ +extern void drbd_bm_merge_lel(struct drbd_conf *mdev, size_t offset, + size_t number, unsigned long *buffer); +/* for _drbd_send_bitmap and drbd_bm_write_sect */ +extern void drbd_bm_get_lel(struct drbd_conf *mdev, size_t offset, + size_t number, unsigned long *buffer); + +extern void drbd_bm_lock(struct drbd_conf *mdev, char *why); +extern void drbd_bm_unlock(struct drbd_conf *mdev); + +extern int drbd_bm_count_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e); +/* drbd_main.c */ + +extern struct kmem_cache *drbd_request_cache; +extern struct kmem_cache *drbd_ee_cache; /* epoch entries */ +extern struct kmem_cache *drbd_bm_ext_cache; /* bitmap extents */ +extern struct kmem_cache *drbd_al_ext_cache; /* activity log extents */ +extern mempool_t *drbd_request_mempool; +extern mempool_t *drbd_ee_mempool; + +extern struct page *drbd_pp_pool; /* drbd's page pool */ +extern spinlock_t drbd_pp_lock; +extern int drbd_pp_vacant; +extern wait_queue_head_t drbd_pp_wait; + +extern rwlock_t global_state_lock; + +extern struct drbd_conf *drbd_new_device(unsigned int minor); +extern void drbd_free_mdev(struct drbd_conf *mdev); + +extern int proc_details; + +/* drbd_req */ +extern int drbd_make_request_26(struct request_queue *q, struct bio *bio); +extern int drbd_read_remote(struct drbd_conf *mdev, struct drbd_request *req); +extern int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct bio_vec *bvec); +extern int is_valid_ar_handle(struct drbd_request *, sector_t); + + +/* drbd_nl.c */ +extern void drbd_suspend_io(struct drbd_conf *mdev); +extern void drbd_resume_io(struct drbd_conf *mdev); +extern char *ppsize(char *buf, unsigned long long size); +extern sector_t drbd_new_dev_size(struct drbd_conf *, + struct drbd_backing_dev *); +enum determine_dev_size { dev_size_error = -1, unchanged = 0, shrunk = 1, grew = 2 }; +extern enum determine_dev_size drbd_determin_dev_size(struct drbd_conf *) __must_hold(local); +extern void resync_after_online_grow(struct drbd_conf *); +extern void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int) __must_hold(local); +extern int drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, + int force); +enum drbd_disk_state drbd_try_outdate_peer(struct drbd_conf *mdev); +extern int drbd_khelper(struct drbd_conf *mdev, char *cmd); + +/* drbd_worker.c */ +extern int drbd_worker(struct drbd_thread *thi); +extern int drbd_alter_sa(struct drbd_conf *mdev, int na); +extern void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side); +extern void resume_next_sg(struct drbd_conf *mdev); +extern void suspend_other_sg(struct drbd_conf *mdev); +extern int drbd_resync_finished(struct drbd_conf *mdev); +/* maybe rather drbd_main.c ? */ +extern int drbd_md_sync_page_io(struct drbd_conf *mdev, + struct drbd_backing_dev *bdev, sector_t sector, int rw); +extern void drbd_ov_oos_found(struct drbd_conf*, sector_t, int); + +static inline void ov_oos_print(struct drbd_conf *mdev) +{ + if (mdev->ov_last_oos_size) { + dev_err(DEV, "Out of sync: start=%llu, size=%lu (sectors)\n", + (unsigned long long)mdev->ov_last_oos_start, + (unsigned long)mdev->ov_last_oos_size); + } + mdev->ov_last_oos_size=0; +} + + +extern void drbd_csum(struct drbd_conf *, struct crypto_hash *, struct bio *, void *); +/* worker callbacks */ +extern int w_req_cancel_conflict(struct drbd_conf *, struct drbd_work *, int); +extern int w_read_retry_remote(struct drbd_conf *, struct drbd_work *, int); +extern int w_e_end_data_req(struct drbd_conf *, struct drbd_work *, int); +extern int w_e_end_rsdata_req(struct drbd_conf *, struct drbd_work *, int); +extern int w_e_end_csum_rs_req(struct drbd_conf *, struct drbd_work *, int); +extern int w_e_end_ov_reply(struct drbd_conf *, struct drbd_work *, int); +extern int w_e_end_ov_req(struct drbd_conf *, struct drbd_work *, int); +extern int w_ov_finished(struct drbd_conf *, struct drbd_work *, int); +extern int w_resync_inactive(struct drbd_conf *, struct drbd_work *, int); +extern int w_resume_next_sg(struct drbd_conf *, struct drbd_work *, int); +extern int w_io_error(struct drbd_conf *, struct drbd_work *, int); +extern int w_send_write_hint(struct drbd_conf *, struct drbd_work *, int); +extern int w_make_resync_request(struct drbd_conf *, struct drbd_work *, int); +extern int w_send_dblock(struct drbd_conf *, struct drbd_work *, int); +extern int w_send_barrier(struct drbd_conf *, struct drbd_work *, int); +extern int w_send_read_req(struct drbd_conf *, struct drbd_work *, int); +extern int w_prev_work_done(struct drbd_conf *, struct drbd_work *, int); +extern int w_e_reissue(struct drbd_conf *, struct drbd_work *, int); + +extern void resync_timer_fn(unsigned long data); + +/* drbd_receiver.c */ +extern int drbd_release_ee(struct drbd_conf *mdev, struct list_head *list); +extern struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev, + u64 id, + sector_t sector, + unsigned int data_size, + gfp_t gfp_mask) __must_hold(local); +extern void drbd_free_ee(struct drbd_conf *mdev, struct drbd_epoch_entry *e); +extern void drbd_wait_ee_list_empty(struct drbd_conf *mdev, + struct list_head *head); +extern void _drbd_wait_ee_list_empty(struct drbd_conf *mdev, + struct list_head *head); +extern void drbd_set_recv_tcq(struct drbd_conf *mdev, int tcq_enabled); +extern void _drbd_clear_done_ee(struct drbd_conf *mdev, struct list_head *to_be_freed); +extern void drbd_flush_workqueue(struct drbd_conf *mdev); + +/* yes, there is kernel_setsockopt, but only since 2.6.18. we don't need to + * mess with get_fs/set_fs, we know we are KERNEL_DS always. */ +static inline int drbd_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int optlen) +{ + int err; + if (level == SOL_SOCKET) + err = sock_setsockopt(sock, level, optname, optval, optlen); + else + err = sock->ops->setsockopt(sock, level, optname, optval, + optlen); + return err; +} + +static inline void drbd_tcp_cork(struct socket *sock) +{ + int __user val = 1; + (void) drbd_setsockopt(sock, SOL_TCP, TCP_CORK, + (char __user *)&val, sizeof(val)); +} + +static inline void drbd_tcp_uncork(struct socket *sock) +{ + int __user val = 0; + (void) drbd_setsockopt(sock, SOL_TCP, TCP_CORK, + (char __user *)&val, sizeof(val)); +} + +static inline void drbd_tcp_nodelay(struct socket *sock) +{ + int __user val = 1; + (void) drbd_setsockopt(sock, SOL_TCP, TCP_NODELAY, + (char __user *)&val, sizeof(val)); +} + +static inline void drbd_tcp_quickack(struct socket *sock) +{ + int __user val = 1; + (void) drbd_setsockopt(sock, SOL_TCP, TCP_QUICKACK, + (char __user *)&val, sizeof(val)); +} + +void drbd_bump_write_ordering(struct drbd_conf *mdev, enum write_ordering_e wo); + +/* drbd_proc.c */ +extern struct proc_dir_entry *drbd_proc; +extern struct file_operations drbd_proc_fops; +extern const char *drbd_conn_str(enum drbd_conns s); +extern const char *drbd_role_str(enum drbd_role s); + +/* drbd_actlog.c */ +extern void drbd_al_begin_io(struct drbd_conf *mdev, sector_t sector); +extern void drbd_al_complete_io(struct drbd_conf *mdev, sector_t sector); +extern void drbd_rs_complete_io(struct drbd_conf *mdev, sector_t sector); +extern int drbd_rs_begin_io(struct drbd_conf *mdev, sector_t sector); +extern int drbd_try_rs_begin_io(struct drbd_conf *mdev, sector_t sector); +extern void drbd_rs_cancel_all(struct drbd_conf *mdev); +extern int drbd_rs_del_all(struct drbd_conf *mdev); +extern void drbd_rs_failed_io(struct drbd_conf *mdev, + sector_t sector, int size); +extern int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *); +extern void __drbd_set_in_sync(struct drbd_conf *mdev, sector_t sector, + int size, const char *file, const unsigned int line); +#define drbd_set_in_sync(mdev, sector, size) \ + __drbd_set_in_sync(mdev, sector, size, __FILE__, __LINE__) +extern void __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, + int size, const char *file, const unsigned int line); +#define drbd_set_out_of_sync(mdev, sector, size) \ + __drbd_set_out_of_sync(mdev, sector, size, __FILE__, __LINE__) +extern void drbd_al_apply_to_bm(struct drbd_conf *mdev); +extern void drbd_al_to_on_disk_bm(struct drbd_conf *mdev); +extern void drbd_al_shrink(struct drbd_conf *mdev); + + +/* drbd_nl.c */ + +void drbd_nl_cleanup(void); +int __init drbd_nl_init(void); +void drbd_bcast_state(struct drbd_conf *mdev, union drbd_state); +void drbd_bcast_sync_progress(struct drbd_conf *mdev); +void drbd_bcast_ee(struct drbd_conf *mdev, + const char *reason, const int dgs, + const char* seen_hash, const char* calc_hash, + const struct drbd_epoch_entry* e); + + +/** + * DOC: DRBD State macros + * + * These macros are used to express state changes in easily readable form. + * + * The NS macros expand to a mask and a value, that can be bit ored onto the + * current state as soon as the spinlock (req_lock) was taken. + * + * The _NS macros are used for state functions that get called with the + * spinlock. These macros expand directly to the new state value. + * + * Besides the basic forms NS() and _NS() additional _?NS[23] are defined + * to express state changes that affect more than one aspect of the state. + * + * E.g. NS2(conn, C_CONNECTED, peer, R_SECONDARY) + * Means that the network connection was established and that the peer + * is in secondary role. + */ +#define role_MASK R_MASK +#define peer_MASK R_MASK +#define disk_MASK D_MASK +#define pdsk_MASK D_MASK +#define conn_MASK C_MASK +#define susp_MASK 1 +#define user_isp_MASK 1 +#define aftr_isp_MASK 1 + +#define NS(T, S) \ + ({ union drbd_state mask; mask.i = 0; mask.T = T##_MASK; mask; }), \ + ({ union drbd_state val; val.i = 0; val.T = (S); val; }) +#define NS2(T1, S1, T2, S2) \ + ({ union drbd_state mask; mask.i = 0; mask.T1 = T1##_MASK; \ + mask.T2 = T2##_MASK; mask; }), \ + ({ union drbd_state val; val.i = 0; val.T1 = (S1); \ + val.T2 = (S2); val; }) +#define NS3(T1, S1, T2, S2, T3, S3) \ + ({ union drbd_state mask; mask.i = 0; mask.T1 = T1##_MASK; \ + mask.T2 = T2##_MASK; mask.T3 = T3##_MASK; mask; }), \ + ({ union drbd_state val; val.i = 0; val.T1 = (S1); \ + val.T2 = (S2); val.T3 = (S3); val; }) + +#define _NS(D, T, S) \ + D, ({ union drbd_state __ns; __ns.i = D->state.i; __ns.T = (S); __ns; }) +#define _NS2(D, T1, S1, T2, S2) \ + D, ({ union drbd_state __ns; __ns.i = D->state.i; __ns.T1 = (S1); \ + __ns.T2 = (S2); __ns; }) +#define _NS3(D, T1, S1, T2, S2, T3, S3) \ + D, ({ union drbd_state __ns; __ns.i = D->state.i; __ns.T1 = (S1); \ + __ns.T2 = (S2); __ns.T3 = (S3); __ns; }) + +/* + * inline helper functions + *************************/ + +static inline void drbd_state_lock(struct drbd_conf *mdev) +{ + wait_event(mdev->misc_wait, + !test_and_set_bit(CLUSTER_ST_CHANGE, &mdev->flags)); +} + +static inline void drbd_state_unlock(struct drbd_conf *mdev) +{ + clear_bit(CLUSTER_ST_CHANGE, &mdev->flags); + wake_up(&mdev->misc_wait); +} + +static inline int _drbd_set_state(struct drbd_conf *mdev, + union drbd_state ns, enum chg_state_flags flags, + struct completion *done) +{ + int rv; + + read_lock(&global_state_lock); + rv = __drbd_set_state(mdev, ns, flags, done); + read_unlock(&global_state_lock); + + return rv; +} + +/** + * drbd_request_state() - Reqest a state change + * @mdev: DRBD device. + * @mask: mask of state bits to change. + * @val: value of new state bits. + * + * This is the most graceful way of requesting a state change. It is verbose + * quite verbose in case the state change is not possible, and all those + * state changes are globally serialized. + */ +static inline int drbd_request_state(struct drbd_conf *mdev, + union drbd_state mask, + union drbd_state val) +{ + return _drbd_request_state(mdev, mask, val, CS_VERBOSE + CS_ORDERED); +} + +#define __drbd_chk_io_error(m,f) __drbd_chk_io_error_(m,f, __func__) +static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach, const char *where) +{ + switch (mdev->ldev->dc.on_io_error) { + case EP_PASS_ON: + if (!forcedetach) { + if (printk_ratelimit()) + dev_err(DEV, "Local IO failed in %s." + "Passing error on...\n", where); + break; + } + /* NOTE fall through to detach case if forcedetach set */ + case EP_DETACH: + case EP_CALL_HELPER: + if (mdev->state.disk > D_FAILED) { + _drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL); + dev_err(DEV, "Local IO failed in %s." + "Detaching...\n", where); + } + break; + } +} + +/** + * drbd_chk_io_error: Handle the on_io_error setting, should be called from all io completion handlers + * @mdev: DRBD device. + * @error: Error code passed to the IO completion callback + * @forcedetach: Force detach. I.e. the error happened while accessing the meta data + * + * See also drbd_main.c:after_state_ch() if (os.disk > D_FAILED && ns.disk == D_FAILED) + */ +#define drbd_chk_io_error(m,e,f) drbd_chk_io_error_(m,e,f, __func__) +static inline void drbd_chk_io_error_(struct drbd_conf *mdev, + int error, int forcedetach, const char *where) +{ + if (error) { + unsigned long flags; + spin_lock_irqsave(&mdev->req_lock, flags); + __drbd_chk_io_error_(mdev, forcedetach, where); + spin_unlock_irqrestore(&mdev->req_lock, flags); + } +} + + +/** + * drbd_md_first_sector() - Returns the first sector number of the meta data area + * @bdev: Meta data block device. + * + * BTW, for internal meta data, this happens to be the maximum capacity + * we could agree upon with our peer node. + */ +static inline sector_t drbd_md_first_sector(struct drbd_backing_dev *bdev) +{ + switch (bdev->dc.meta_dev_idx) { + case DRBD_MD_INDEX_INTERNAL: + case DRBD_MD_INDEX_FLEX_INT: + return bdev->md.md_offset + bdev->md.bm_offset; + case DRBD_MD_INDEX_FLEX_EXT: + default: + return bdev->md.md_offset; + } +} + +/** + * drbd_md_last_sector() - Return the last sector number of the meta data area + * @bdev: Meta data block device. + */ +static inline sector_t drbd_md_last_sector(struct drbd_backing_dev *bdev) +{ + switch (bdev->dc.meta_dev_idx) { + case DRBD_MD_INDEX_INTERNAL: + case DRBD_MD_INDEX_FLEX_INT: + return bdev->md.md_offset + MD_AL_OFFSET - 1; + case DRBD_MD_INDEX_FLEX_EXT: + default: + return bdev->md.md_offset + bdev->md.md_size_sect; + } +} + +/* Returns the number of 512 byte sectors of the device */ +static inline sector_t drbd_get_capacity(struct block_device *bdev) +{ + /* return bdev ? get_capacity(bdev->bd_disk) : 0; */ + return bdev ? bdev->bd_inode->i_size >> 9 : 0; +} + +/** + * drbd_get_max_capacity() - Returns the capacity we announce to out peer + * @bdev: Meta data block device. + * + * returns the capacity we announce to out peer. we clip ourselves at the + * various MAX_SECTORS, because if we don't, current implementation will + * oops sooner or later + */ +static inline sector_t drbd_get_max_capacity(struct drbd_backing_dev *bdev) +{ + sector_t s; + switch (bdev->dc.meta_dev_idx) { + case DRBD_MD_INDEX_INTERNAL: + case DRBD_MD_INDEX_FLEX_INT: + s = drbd_get_capacity(bdev->backing_bdev) + ? min_t(sector_t, DRBD_MAX_SECTORS_FLEX, + drbd_md_first_sector(bdev)) + : 0; + break; + case DRBD_MD_INDEX_FLEX_EXT: + s = min_t(sector_t, DRBD_MAX_SECTORS_FLEX, + drbd_get_capacity(bdev->backing_bdev)); + /* clip at maximum size the meta device can support */ + s = min_t(sector_t, s, + BM_EXT_TO_SECT(bdev->md.md_size_sect + - bdev->md.bm_offset)); + break; + default: + s = min_t(sector_t, DRBD_MAX_SECTORS, + drbd_get_capacity(bdev->backing_bdev)); + } + return s; +} + +/** + * drbd_md_ss__() - Return the sector number of our meta data super block + * @mdev: DRBD device. + * @bdev: Meta data block device. + */ +static inline sector_t drbd_md_ss__(struct drbd_conf *mdev, + struct drbd_backing_dev *bdev) +{ + switch (bdev->dc.meta_dev_idx) { + default: /* external, some index */ + return MD_RESERVED_SECT * bdev->dc.meta_dev_idx; + case DRBD_MD_INDEX_INTERNAL: + /* with drbd08, internal meta data is always "flexible" */ + case DRBD_MD_INDEX_FLEX_INT: + /* sizeof(struct md_on_disk_07) == 4k + * position: last 4k aligned block of 4k size */ + if (!bdev->backing_bdev) { + if (__ratelimit(&drbd_ratelimit_state)) { + dev_err(DEV, "bdev->backing_bdev==NULL\n"); + dump_stack(); + } + return 0; + } + return (drbd_get_capacity(bdev->backing_bdev) & ~7ULL) + - MD_AL_OFFSET; + case DRBD_MD_INDEX_FLEX_EXT: + return 0; + } +} + +static inline void +_drbd_queue_work(struct drbd_work_queue *q, struct drbd_work *w) +{ + list_add_tail(&w->list, &q->q); + up(&q->s); +} + +static inline void +drbd_queue_work_front(struct drbd_work_queue *q, struct drbd_work *w) +{ + unsigned long flags; + spin_lock_irqsave(&q->q_lock, flags); + list_add(&w->list, &q->q); + up(&q->s); /* within the spinlock, + see comment near end of drbd_worker() */ + spin_unlock_irqrestore(&q->q_lock, flags); +} + +static inline void +drbd_queue_work(struct drbd_work_queue *q, struct drbd_work *w) +{ + unsigned long flags; + spin_lock_irqsave(&q->q_lock, flags); + list_add_tail(&w->list, &q->q); + up(&q->s); /* within the spinlock, + see comment near end of drbd_worker() */ + spin_unlock_irqrestore(&q->q_lock, flags); +} + +static inline void wake_asender(struct drbd_conf *mdev) +{ + if (test_bit(SIGNAL_ASENDER, &mdev->flags)) + force_sig(DRBD_SIG, mdev->asender.task); +} + +static inline void request_ping(struct drbd_conf *mdev) +{ + set_bit(SEND_PING, &mdev->flags); + wake_asender(mdev); +} + +static inline int drbd_send_short_cmd(struct drbd_conf *mdev, + enum drbd_packets cmd) +{ + struct p_header h; + return drbd_send_cmd(mdev, USE_DATA_SOCKET, cmd, &h, sizeof(h)); +} + +static inline int drbd_send_ping(struct drbd_conf *mdev) +{ + struct p_header h; + return drbd_send_cmd(mdev, USE_META_SOCKET, P_PING, &h, sizeof(h)); +} + +static inline int drbd_send_ping_ack(struct drbd_conf *mdev) +{ + struct p_header h; + return drbd_send_cmd(mdev, USE_META_SOCKET, P_PING_ACK, &h, sizeof(h)); +} + +static inline void drbd_thread_stop(struct drbd_thread *thi) +{ + _drbd_thread_stop(thi, FALSE, TRUE); +} + +static inline void drbd_thread_stop_nowait(struct drbd_thread *thi) +{ + _drbd_thread_stop(thi, FALSE, FALSE); +} + +static inline void drbd_thread_restart_nowait(struct drbd_thread *thi) +{ + _drbd_thread_stop(thi, TRUE, FALSE); +} + +/* counts how many answer packets packets we expect from our peer, + * for either explicit application requests, + * or implicit barrier packets as necessary. + * increased: + * w_send_barrier + * _req_mod(req, queue_for_net_write or queue_for_net_read); + * it is much easier and equally valid to count what we queue for the + * worker, even before it actually was queued or send. + * (drbd_make_request_common; recovery path on read io-error) + * decreased: + * got_BarrierAck (respective tl_clear, tl_clear_barrier) + * _req_mod(req, data_received) + * [from receive_DataReply] + * _req_mod(req, write_acked_by_peer or recv_acked_by_peer or neg_acked) + * [from got_BlockAck (P_WRITE_ACK, P_RECV_ACK)] + * for some reason it is NOT decreased in got_NegAck, + * but in the resulting cleanup code from report_params. + * we should try to remember the reason for that... + * _req_mod(req, send_failed or send_canceled) + * _req_mod(req, connection_lost_while_pending) + * [from tl_clear_barrier] + */ +static inline void inc_ap_pending(struct drbd_conf *mdev) +{ + atomic_inc(&mdev->ap_pending_cnt); +} + +#define ERR_IF_CNT_IS_NEGATIVE(which) \ + if (atomic_read(&mdev->which) < 0) \ + dev_err(DEV, "in %s:%d: " #which " = %d < 0 !\n", \ + __func__ , __LINE__ , \ + atomic_read(&mdev->which)) + +#define dec_ap_pending(mdev) do { \ + typecheck(struct drbd_conf *, mdev); \ + if (atomic_dec_and_test(&mdev->ap_pending_cnt)) \ + wake_up(&mdev->misc_wait); \ + ERR_IF_CNT_IS_NEGATIVE(ap_pending_cnt); } while (0) + +/* counts how many resync-related answers we still expect from the peer + * increase decrease + * C_SYNC_TARGET sends P_RS_DATA_REQUEST (and expects P_RS_DATA_REPLY) + * C_SYNC_SOURCE sends P_RS_DATA_REPLY (and expects P_WRITE_ACK whith ID_SYNCER) + * (or P_NEG_ACK with ID_SYNCER) + */ +static inline void inc_rs_pending(struct drbd_conf *mdev) +{ + atomic_inc(&mdev->rs_pending_cnt); +} + +#define dec_rs_pending(mdev) do { \ + typecheck(struct drbd_conf *, mdev); \ + atomic_dec(&mdev->rs_pending_cnt); \ + ERR_IF_CNT_IS_NEGATIVE(rs_pending_cnt); } while (0) + +/* counts how many answers we still need to send to the peer. + * increased on + * receive_Data unless protocol A; + * we need to send a P_RECV_ACK (proto B) + * or P_WRITE_ACK (proto C) + * receive_RSDataReply (recv_resync_read) we need to send a P_WRITE_ACK + * receive_DataRequest (receive_RSDataRequest) we need to send back P_DATA + * receive_Barrier_* we need to send a P_BARRIER_ACK + */ +static inline void inc_unacked(struct drbd_conf *mdev) +{ + atomic_inc(&mdev->unacked_cnt); +} + +#define dec_unacked(mdev) do { \ + typecheck(struct drbd_conf *, mdev); \ + atomic_dec(&mdev->unacked_cnt); \ + ERR_IF_CNT_IS_NEGATIVE(unacked_cnt); } while (0) + +#define sub_unacked(mdev, n) do { \ + typecheck(struct drbd_conf *, mdev); \ + atomic_sub(n, &mdev->unacked_cnt); \ + ERR_IF_CNT_IS_NEGATIVE(unacked_cnt); } while (0) + + +static inline void put_net_conf(struct drbd_conf *mdev) +{ + if (atomic_dec_and_test(&mdev->net_cnt)) + wake_up(&mdev->misc_wait); +} + +/** + * get_net_conf() - Increase ref count on mdev->net_conf; Returns 0 if nothing there + * @mdev: DRBD device. + * + * You have to call put_net_conf() when finished working with mdev->net_conf. + */ +static inline int get_net_conf(struct drbd_conf *mdev) +{ + int have_net_conf; + + atomic_inc(&mdev->net_cnt); + have_net_conf = mdev->state.conn >= C_UNCONNECTED; + if (!have_net_conf) + put_net_conf(mdev); + return have_net_conf; +} + +/** + * get_ldev() - Increase the ref count on mdev->ldev. Returns 0 if there is no ldev + * @M: DRBD device. + * + * You have to call put_ldev() when finished working with mdev->ldev. + */ +#define get_ldev(M) __cond_lock(local, _get_ldev_if_state(M,D_INCONSISTENT)) +#define get_ldev_if_state(M,MINS) __cond_lock(local, _get_ldev_if_state(M,MINS)) + +static inline void put_ldev(struct drbd_conf *mdev) +{ + __release(local); + if (atomic_dec_and_test(&mdev->local_cnt)) + wake_up(&mdev->misc_wait); + D_ASSERT(atomic_read(&mdev->local_cnt) >= 0); +} + +#ifndef __CHECKER__ +static inline int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_state mins) +{ + int io_allowed; + + atomic_inc(&mdev->local_cnt); + io_allowed = (mdev->state.disk >= mins); + if (!io_allowed) + put_ldev(mdev); + return io_allowed; +} +#else +extern int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_state mins); +#endif + +/* you must have an "get_ldev" reference */ +static inline void drbd_get_syncer_progress(struct drbd_conf *mdev, + unsigned long *bits_left, unsigned int *per_mil_done) +{ + /* + * this is to break it at compile time when we change that + * (we may feel 4TB maximum storage per drbd is not enough) + */ + typecheck(unsigned long, mdev->rs_total); + + /* note: both rs_total and rs_left are in bits, i.e. in + * units of BM_BLOCK_SIZE. + * for the percentage, we don't care. */ + + *bits_left = drbd_bm_total_weight(mdev) - mdev->rs_failed; + /* >> 10 to prevent overflow, + * +1 to prevent division by zero */ + if (*bits_left > mdev->rs_total) { + /* doh. maybe a logic bug somewhere. + * may also be just a race condition + * between this and a disconnect during sync. + * for now, just prevent in-kernel buffer overflow. + */ + smp_rmb(); + dev_warn(DEV, "cs:%s rs_left=%lu > rs_total=%lu (rs_failed %lu)\n", + drbd_conn_str(mdev->state.conn), + *bits_left, mdev->rs_total, mdev->rs_failed); + *per_mil_done = 0; + } else { + /* make sure the calculation happens in long context */ + unsigned long tmp = 1000UL - + (*bits_left >> 10)*1000UL + / ((mdev->rs_total >> 10) + 1UL); + *per_mil_done = tmp; + } +} + + +/* this throttles on-the-fly application requests + * according to max_buffers settings; + * maybe re-implement using semaphores? */ +static inline int drbd_get_max_buffers(struct drbd_conf *mdev) +{ + int mxb = 1000000; /* arbitrary limit on open requests */ + if (get_net_conf(mdev)) { + mxb = mdev->net_conf->max_buffers; + put_net_conf(mdev); + } + return mxb; +} + +static inline int drbd_state_is_stable(union drbd_state s) +{ + + /* DO NOT add a default clause, we want the compiler to warn us + * for any newly introduced state we may have forgotten to add here */ + + switch ((enum drbd_conns)s.conn) { + /* new io only accepted when there is no connection, ... */ + case C_STANDALONE: + case C_WF_CONNECTION: + /* ... or there is a well established connection. */ + case C_CONNECTED: + case C_SYNC_SOURCE: + case C_SYNC_TARGET: + case C_VERIFY_S: + case C_VERIFY_T: + case C_PAUSED_SYNC_S: + case C_PAUSED_SYNC_T: + /* maybe stable, look at the disk state */ + break; + + /* no new io accepted during tansitional states + * like handshake or teardown */ + case C_DISCONNECTING: + case C_UNCONNECTED: + case C_TIMEOUT: + case C_BROKEN_PIPE: + case C_NETWORK_FAILURE: + case C_PROTOCOL_ERROR: + case C_TEAR_DOWN: + case C_WF_REPORT_PARAMS: + case C_STARTING_SYNC_S: + case C_STARTING_SYNC_T: + case C_WF_BITMAP_S: + case C_WF_BITMAP_T: + case C_WF_SYNC_UUID: + case C_MASK: + /* not "stable" */ + return 0; + } + + switch ((enum drbd_disk_state)s.disk) { + case D_DISKLESS: + case D_INCONSISTENT: + case D_OUTDATED: + case D_CONSISTENT: + case D_UP_TO_DATE: + /* disk state is stable as well. */ + break; + + /* no new io accepted during tansitional states */ + case D_ATTACHING: + case D_FAILED: + case D_NEGOTIATING: + case D_UNKNOWN: + case D_MASK: + /* not "stable" */ + return 0; + } + + return 1; +} + +static inline int __inc_ap_bio_cond(struct drbd_conf *mdev) +{ + int mxb = drbd_get_max_buffers(mdev); + + if (mdev->state.susp) + return 0; + if (test_bit(SUSPEND_IO, &mdev->flags)) + return 0; + + /* to avoid potential deadlock or bitmap corruption, + * in various places, we only allow new application io + * to start during "stable" states. */ + + /* no new io accepted when attaching or detaching the disk */ + if (!drbd_state_is_stable(mdev->state)) + return 0; + + /* since some older kernels don't have atomic_add_unless, + * and we are within the spinlock anyways, we have this workaround. */ + if (atomic_read(&mdev->ap_bio_cnt) > mxb) + return 0; + if (test_bit(BITMAP_IO, &mdev->flags)) + return 0; + return 1; +} + +/* I'd like to use wait_event_lock_irq, + * but I'm not sure when it got introduced, + * and not sure when it has 3 or 4 arguments */ +static inline void inc_ap_bio(struct drbd_conf *mdev, int one_or_two) +{ + /* compare with after_state_ch, + * os.conn != C_WF_BITMAP_S && ns.conn == C_WF_BITMAP_S */ + DEFINE_WAIT(wait); + + /* we wait here + * as long as the device is suspended + * until the bitmap is no longer on the fly during connection + * handshake as long as we would exeed the max_buffer limit. + * + * to avoid races with the reconnect code, + * we need to atomic_inc within the spinlock. */ + + spin_lock_irq(&mdev->req_lock); + while (!__inc_ap_bio_cond(mdev)) { + prepare_to_wait(&mdev->misc_wait, &wait, TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&mdev->req_lock); + schedule(); + finish_wait(&mdev->misc_wait, &wait); + spin_lock_irq(&mdev->req_lock); + } + atomic_add(one_or_two, &mdev->ap_bio_cnt); + spin_unlock_irq(&mdev->req_lock); +} + +static inline void dec_ap_bio(struct drbd_conf *mdev) +{ + int mxb = drbd_get_max_buffers(mdev); + int ap_bio = atomic_dec_return(&mdev->ap_bio_cnt); + + D_ASSERT(ap_bio >= 0); + /* this currently does wake_up for every dec_ap_bio! + * maybe rather introduce some type of hysteresis? + * e.g. (ap_bio == mxb/2 || ap_bio == 0) ? */ + if (ap_bio < mxb) + wake_up(&mdev->misc_wait); + if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) { + if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags)) + drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w); + } +} + +static inline void drbd_set_ed_uuid(struct drbd_conf *mdev, u64 val) +{ + mdev->ed_uuid = val; +} + +static inline int seq_cmp(u32 a, u32 b) +{ + /* we assume wrap around at 32bit. + * for wrap around at 24bit (old atomic_t), + * we'd have to + * a <<= 8; b <<= 8; + */ + return (s32)(a) - (s32)(b); +} +#define seq_lt(a, b) (seq_cmp((a), (b)) < 0) +#define seq_gt(a, b) (seq_cmp((a), (b)) > 0) +#define seq_ge(a, b) (seq_cmp((a), (b)) >= 0) +#define seq_le(a, b) (seq_cmp((a), (b)) <= 0) +/* CAUTION: please no side effects in arguments! */ +#define seq_max(a, b) ((u32)(seq_gt((a), (b)) ? (a) : (b))) + +static inline void update_peer_seq(struct drbd_conf *mdev, unsigned int new_seq) +{ + unsigned int m; + spin_lock(&mdev->peer_seq_lock); + m = seq_max(mdev->peer_seq, new_seq); + mdev->peer_seq = m; + spin_unlock(&mdev->peer_seq_lock); + if (m == new_seq) + wake_up(&mdev->seq_wait); +} + +static inline void drbd_update_congested(struct drbd_conf *mdev) +{ + struct sock *sk = mdev->data.socket->sk; + if (sk->sk_wmem_queued > sk->sk_sndbuf * 4 / 5) + set_bit(NET_CONGESTED, &mdev->flags); +} + +static inline int drbd_queue_order_type(struct drbd_conf *mdev) +{ + /* sorry, we currently have no working implementation + * of distributed TCQ stuff */ +#ifndef QUEUE_ORDERED_NONE +#define QUEUE_ORDERED_NONE 0 +#endif + return QUEUE_ORDERED_NONE; +} + +static inline void drbd_blk_run_queue(struct request_queue *q) +{ + if (q && q->unplug_fn) + q->unplug_fn(q); +} + +static inline void drbd_kick_lo(struct drbd_conf *mdev) +{ + if (get_ldev(mdev)) { + drbd_blk_run_queue(bdev_get_queue(mdev->ldev->backing_bdev)); + put_ldev(mdev); + } +} + +static inline void drbd_md_flush(struct drbd_conf *mdev) +{ + int r; + + if (test_bit(MD_NO_BARRIER, &mdev->flags)) + return; + + r = blkdev_issue_flush(mdev->ldev->md_bdev, NULL); + if (r) { + set_bit(MD_NO_BARRIER, &mdev->flags); + dev_err(DEV, "meta data flush failed with status %d, disabling md-flushes\n", r); + } +} + +#endif diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c new file mode 100644 index 000000000000..157d1e4343c2 --- /dev/null +++ b/drivers/block/drbd/drbd_main.c @@ -0,0 +1,3699 @@ +/* + drbd.c + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. + Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + Thanks to Carter Burden, Bart Grantham and Gennadiy Nerubayev + from Logicworks, Inc. for making SDP replication support possible. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/drbd.h> +#include <asm/uaccess.h> +#include <asm/types.h> +#include <net/sock.h> +#include <linux/ctype.h> +#include <linux/smp_lock.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/memcontrol.h> +#include <linux/mm_inline.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/reboot.h> +#include <linux/notifier.h> +#include <linux/kthread.h> + +#define __KERNEL_SYSCALLS__ +#include <linux/unistd.h> +#include <linux/vmalloc.h> + +#include <linux/drbd_limits.h> +#include "drbd_int.h" +#include "drbd_req.h" /* only for _req_mod in tl_release and tl_clear */ + +#include "drbd_vli.h" + +struct after_state_chg_work { + struct drbd_work w; + union drbd_state os; + union drbd_state ns; + enum chg_state_flags flags; + struct completion *done; +}; + +int drbdd_init(struct drbd_thread *); +int drbd_worker(struct drbd_thread *); +int drbd_asender(struct drbd_thread *); + +int drbd_init(void); +static int drbd_open(struct block_device *bdev, fmode_t mode); +static int drbd_release(struct gendisk *gd, fmode_t mode); +static int w_after_state_ch(struct drbd_conf *mdev, struct drbd_work *w, int unused); +static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, + union drbd_state ns, enum chg_state_flags flags); +static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused); +static void md_sync_timer_fn(unsigned long data); +static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused); + +MODULE_AUTHOR("Philipp Reisner <phil@linbit.com>, " + "Lars Ellenberg <lars@linbit.com>"); +MODULE_DESCRIPTION("drbd - Distributed Replicated Block Device v" REL_VERSION); +MODULE_VERSION(REL_VERSION); +MODULE_LICENSE("GPL"); +MODULE_PARM_DESC(minor_count, "Maximum number of drbd devices (1-255)"); +MODULE_ALIAS_BLOCKDEV_MAJOR(DRBD_MAJOR); + +#include <linux/moduleparam.h> +/* allow_open_on_secondary */ +MODULE_PARM_DESC(allow_oos, "DONT USE!"); +/* thanks to these macros, if compiled into the kernel (not-module), + * this becomes the boot parameter drbd.minor_count */ +module_param(minor_count, uint, 0444); +module_param(disable_sendpage, bool, 0644); +module_param(allow_oos, bool, 0); +module_param(cn_idx, uint, 0444); +module_param(proc_details, int, 0644); + +#ifdef CONFIG_DRBD_FAULT_INJECTION +int enable_faults; +int fault_rate; +static int fault_count; +int fault_devs; +/* bitmap of enabled faults */ +module_param(enable_faults, int, 0664); +/* fault rate % value - applies to all enabled faults */ +module_param(fault_rate, int, 0664); +/* count of faults inserted */ +module_param(fault_count, int, 0664); +/* bitmap of devices to insert faults on */ +module_param(fault_devs, int, 0644); +#endif + +/* module parameter, defined */ +unsigned int minor_count = 32; +int disable_sendpage; +int allow_oos; +unsigned int cn_idx = CN_IDX_DRBD; +int proc_details; /* Detail level in proc drbd*/ + +/* Module parameter for setting the user mode helper program + * to run. Default is /sbin/drbdadm */ +char usermode_helper[80] = "/sbin/drbdadm"; + +module_param_string(usermode_helper, usermode_helper, sizeof(usermode_helper), 0644); + +/* in 2.6.x, our device mapping and config info contains our virtual gendisks + * as member "struct gendisk *vdisk;" + */ +struct drbd_conf **minor_table; + +struct kmem_cache *drbd_request_cache; +struct kmem_cache *drbd_ee_cache; /* epoch entries */ +struct kmem_cache *drbd_bm_ext_cache; /* bitmap extents */ +struct kmem_cache *drbd_al_ext_cache; /* activity log extents */ +mempool_t *drbd_request_mempool; +mempool_t *drbd_ee_mempool; + +/* I do not use a standard mempool, because: + 1) I want to hand out the pre-allocated objects first. + 2) I want to be able to interrupt sleeping allocation with a signal. + Note: This is a single linked list, the next pointer is the private + member of struct page. + */ +struct page *drbd_pp_pool; +spinlock_t drbd_pp_lock; +int drbd_pp_vacant; +wait_queue_head_t drbd_pp_wait; + +DEFINE_RATELIMIT_STATE(drbd_ratelimit_state, 5 * HZ, 5); + +static struct block_device_operations drbd_ops = { + .owner = THIS_MODULE, + .open = drbd_open, + .release = drbd_release, +}; + +#define ARRY_SIZE(A) (sizeof(A)/sizeof(A[0])) + +#ifdef __CHECKER__ +/* When checking with sparse, and this is an inline function, sparse will + give tons of false positives. When this is a real functions sparse works. + */ +int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_state mins) +{ + int io_allowed; + + atomic_inc(&mdev->local_cnt); + io_allowed = (mdev->state.disk >= mins); + if (!io_allowed) { + if (atomic_dec_and_test(&mdev->local_cnt)) + wake_up(&mdev->misc_wait); + } + return io_allowed; +} + +#endif + +/** + * DOC: The transfer log + * + * The transfer log is a single linked list of &struct drbd_tl_epoch objects. + * mdev->newest_tle points to the head, mdev->oldest_tle points to the tail + * of the list. There is always at least one &struct drbd_tl_epoch object. + * + * Each &struct drbd_tl_epoch has a circular double linked list of requests + * attached. + */ +static int tl_init(struct drbd_conf *mdev) +{ + struct drbd_tl_epoch *b; + + /* during device minor initialization, we may well use GFP_KERNEL */ + b = kmalloc(sizeof(struct drbd_tl_epoch), GFP_KERNEL); + if (!b) + return 0; + INIT_LIST_HEAD(&b->requests); + INIT_LIST_HEAD(&b->w.list); + b->next = NULL; + b->br_number = 4711; + b->n_req = 0; + b->w.cb = NULL; /* if this is != NULL, we need to dec_ap_pending in tl_clear */ + + mdev->oldest_tle = b; + mdev->newest_tle = b; + INIT_LIST_HEAD(&mdev->out_of_sequence_requests); + + mdev->tl_hash = NULL; + mdev->tl_hash_s = 0; + + return 1; +} + +static void tl_cleanup(struct drbd_conf *mdev) +{ + D_ASSERT(mdev->oldest_tle == mdev->newest_tle); + D_ASSERT(list_empty(&mdev->out_of_sequence_requests)); + kfree(mdev->oldest_tle); + mdev->oldest_tle = NULL; + kfree(mdev->unused_spare_tle); + mdev->unused_spare_tle = NULL; + kfree(mdev->tl_hash); + mdev->tl_hash = NULL; + mdev->tl_hash_s = 0; +} + +/** + * _tl_add_barrier() - Adds a barrier to the transfer log + * @mdev: DRBD device. + * @new: Barrier to be added before the current head of the TL. + * + * The caller must hold the req_lock. + */ +void _tl_add_barrier(struct drbd_conf *mdev, struct drbd_tl_epoch *new) +{ + struct drbd_tl_epoch *newest_before; + + INIT_LIST_HEAD(&new->requests); + INIT_LIST_HEAD(&new->w.list); + new->w.cb = NULL; /* if this is != NULL, we need to dec_ap_pending in tl_clear */ + new->next = NULL; + new->n_req = 0; + + newest_before = mdev->newest_tle; + /* never send a barrier number == 0, because that is special-cased + * when using TCQ for our write ordering code */ + new->br_number = (newest_before->br_number+1) ?: 1; + if (mdev->newest_tle != new) { + mdev->newest_tle->next = new; + mdev->newest_tle = new; + } +} + +/** + * tl_release() - Free or recycle the oldest &struct drbd_tl_epoch object of the TL + * @mdev: DRBD device. + * @barrier_nr: Expected identifier of the DRBD write barrier packet. + * @set_size: Expected number of requests before that barrier. + * + * In case the passed barrier_nr or set_size does not match the oldest + * &struct drbd_tl_epoch objects this function will cause a termination + * of the connection. + */ +void tl_release(struct drbd_conf *mdev, unsigned int barrier_nr, + unsigned int set_size) +{ + struct drbd_tl_epoch *b, *nob; /* next old barrier */ + struct list_head *le, *tle; + struct drbd_request *r; + + spin_lock_irq(&mdev->req_lock); + + b = mdev->oldest_tle; + + /* first some paranoia code */ + if (b == NULL) { + dev_err(DEV, "BAD! BarrierAck #%u received, but no epoch in tl!?\n", + barrier_nr); + goto bail; + } + if (b->br_number != barrier_nr) { + dev_err(DEV, "BAD! BarrierAck #%u received, expected #%u!\n", + barrier_nr, b->br_number); + goto bail; + } + if (b->n_req != set_size) { + dev_err(DEV, "BAD! BarrierAck #%u received with n_req=%u, expected n_req=%u!\n", + barrier_nr, set_size, b->n_req); + goto bail; + } + + /* Clean up list of requests processed during current epoch */ + list_for_each_safe(le, tle, &b->requests) { + r = list_entry(le, struct drbd_request, tl_requests); + _req_mod(r, barrier_acked); + } + /* There could be requests on the list waiting for completion + of the write to the local disk. To avoid corruptions of + slab's data structures we have to remove the lists head. + + Also there could have been a barrier ack out of sequence, overtaking + the write acks - which would be a bug and violating write ordering. + To not deadlock in case we lose connection while such requests are + still pending, we need some way to find them for the + _req_mode(connection_lost_while_pending). + + These have been list_move'd to the out_of_sequence_requests list in + _req_mod(, barrier_acked) above. + */ + list_del_init(&b->requests); + + nob = b->next; + if (test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) { + _tl_add_barrier(mdev, b); + if (nob) + mdev->oldest_tle = nob; + /* if nob == NULL b was the only barrier, and becomes the new + barrier. Therefore mdev->oldest_tle points already to b */ + } else { + D_ASSERT(nob != NULL); + mdev->oldest_tle = nob; + kfree(b); + } + + spin_unlock_irq(&mdev->req_lock); + dec_ap_pending(mdev); + + return; + +bail: + spin_unlock_irq(&mdev->req_lock); + drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR)); +} + + +/** + * tl_clear() - Clears all requests and &struct drbd_tl_epoch objects out of the TL + * @mdev: DRBD device. + * + * This is called after the connection to the peer was lost. The storage covered + * by the requests on the transfer gets marked as our of sync. Called from the + * receiver thread and the worker thread. + */ +void tl_clear(struct drbd_conf *mdev) +{ + struct drbd_tl_epoch *b, *tmp; + struct list_head *le, *tle; + struct drbd_request *r; + int new_initial_bnr = net_random(); + + spin_lock_irq(&mdev->req_lock); + + b = mdev->oldest_tle; + while (b) { + list_for_each_safe(le, tle, &b->requests) { + r = list_entry(le, struct drbd_request, tl_requests); + /* It would be nice to complete outside of spinlock. + * But this is easier for now. */ + _req_mod(r, connection_lost_while_pending); + } + tmp = b->next; + + /* there could still be requests on that ring list, + * in case local io is still pending */ + list_del(&b->requests); + + /* dec_ap_pending corresponding to queue_barrier. + * the newest barrier may not have been queued yet, + * in which case w.cb is still NULL. */ + if (b->w.cb != NULL) + dec_ap_pending(mdev); + + if (b == mdev->newest_tle) { + /* recycle, but reinit! */ + D_ASSERT(tmp == NULL); + INIT_LIST_HEAD(&b->requests); + INIT_LIST_HEAD(&b->w.list); + b->w.cb = NULL; + b->br_number = new_initial_bnr; + b->n_req = 0; + + mdev->oldest_tle = b; + break; + } + kfree(b); + b = tmp; + } + + /* we expect this list to be empty. */ + D_ASSERT(list_empty(&mdev->out_of_sequence_requests)); + + /* but just in case, clean it up anyways! */ + list_for_each_safe(le, tle, &mdev->out_of_sequence_requests) { + r = list_entry(le, struct drbd_request, tl_requests); + /* It would be nice to complete outside of spinlock. + * But this is easier for now. */ + _req_mod(r, connection_lost_while_pending); + } + + /* ensure bit indicating barrier is required is clear */ + clear_bit(CREATE_BARRIER, &mdev->flags); + + spin_unlock_irq(&mdev->req_lock); +} + +/** + * cl_wide_st_chg() - TRUE if the state change is a cluster wide one + * @mdev: DRBD device. + * @os: old (current) state. + * @ns: new (wanted) state. + */ +static int cl_wide_st_chg(struct drbd_conf *mdev, + union drbd_state os, union drbd_state ns) +{ + return (os.conn >= C_CONNECTED && ns.conn >= C_CONNECTED && + ((os.role != R_PRIMARY && ns.role == R_PRIMARY) || + (os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) || + (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S) || + (os.disk != D_DISKLESS && ns.disk == D_DISKLESS))) || + (os.conn >= C_CONNECTED && ns.conn == C_DISCONNECTING) || + (os.conn == C_CONNECTED && ns.conn == C_VERIFY_S); +} + +int drbd_change_state(struct drbd_conf *mdev, enum chg_state_flags f, + union drbd_state mask, union drbd_state val) +{ + unsigned long flags; + union drbd_state os, ns; + int rv; + + spin_lock_irqsave(&mdev->req_lock, flags); + os = mdev->state; + ns.i = (os.i & ~mask.i) | val.i; + rv = _drbd_set_state(mdev, ns, f, NULL); + ns = mdev->state; + spin_unlock_irqrestore(&mdev->req_lock, flags); + + return rv; +} + +/** + * drbd_force_state() - Impose a change which happens outside our control on our state + * @mdev: DRBD device. + * @mask: mask of state bits to change. + * @val: value of new state bits. + */ +void drbd_force_state(struct drbd_conf *mdev, + union drbd_state mask, union drbd_state val) +{ + drbd_change_state(mdev, CS_HARD, mask, val); +} + +static int is_valid_state(struct drbd_conf *mdev, union drbd_state ns); +static int is_valid_state_transition(struct drbd_conf *, + union drbd_state, union drbd_state); +static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os, + union drbd_state ns, int *warn_sync_abort); +int drbd_send_state_req(struct drbd_conf *, + union drbd_state, union drbd_state); + +static enum drbd_state_ret_codes _req_st_cond(struct drbd_conf *mdev, + union drbd_state mask, union drbd_state val) +{ + union drbd_state os, ns; + unsigned long flags; + int rv; + + if (test_and_clear_bit(CL_ST_CHG_SUCCESS, &mdev->flags)) + return SS_CW_SUCCESS; + + if (test_and_clear_bit(CL_ST_CHG_FAIL, &mdev->flags)) + return SS_CW_FAILED_BY_PEER; + + rv = 0; + spin_lock_irqsave(&mdev->req_lock, flags); + os = mdev->state; + ns.i = (os.i & ~mask.i) | val.i; + ns = sanitize_state(mdev, os, ns, NULL); + + if (!cl_wide_st_chg(mdev, os, ns)) + rv = SS_CW_NO_NEED; + if (!rv) { + rv = is_valid_state(mdev, ns); + if (rv == SS_SUCCESS) { + rv = is_valid_state_transition(mdev, ns, os); + if (rv == SS_SUCCESS) + rv = 0; /* cont waiting, otherwise fail. */ + } + } + spin_unlock_irqrestore(&mdev->req_lock, flags); + + return rv; +} + +/** + * drbd_req_state() - Perform an eventually cluster wide state change + * @mdev: DRBD device. + * @mask: mask of state bits to change. + * @val: value of new state bits. + * @f: flags + * + * Should not be called directly, use drbd_request_state() or + * _drbd_request_state(). + */ +static int drbd_req_state(struct drbd_conf *mdev, + union drbd_state mask, union drbd_state val, + enum chg_state_flags f) +{ + struct completion done; + unsigned long flags; + union drbd_state os, ns; + int rv; + + init_completion(&done); + + if (f & CS_SERIALIZE) + mutex_lock(&mdev->state_mutex); + + spin_lock_irqsave(&mdev->req_lock, flags); + os = mdev->state; + ns.i = (os.i & ~mask.i) | val.i; + ns = sanitize_state(mdev, os, ns, NULL); + + if (cl_wide_st_chg(mdev, os, ns)) { + rv = is_valid_state(mdev, ns); + if (rv == SS_SUCCESS) + rv = is_valid_state_transition(mdev, ns, os); + spin_unlock_irqrestore(&mdev->req_lock, flags); + + if (rv < SS_SUCCESS) { + if (f & CS_VERBOSE) + print_st_err(mdev, os, ns, rv); + goto abort; + } + + drbd_state_lock(mdev); + if (!drbd_send_state_req(mdev, mask, val)) { + drbd_state_unlock(mdev); + rv = SS_CW_FAILED_BY_PEER; + if (f & CS_VERBOSE) + print_st_err(mdev, os, ns, rv); + goto abort; + } + + wait_event(mdev->state_wait, + (rv = _req_st_cond(mdev, mask, val))); + + if (rv < SS_SUCCESS) { + drbd_state_unlock(mdev); + if (f & CS_VERBOSE) + print_st_err(mdev, os, ns, rv); + goto abort; + } + spin_lock_irqsave(&mdev->req_lock, flags); + os = mdev->state; + ns.i = (os.i & ~mask.i) | val.i; + rv = _drbd_set_state(mdev, ns, f, &done); + drbd_state_unlock(mdev); + } else { + rv = _drbd_set_state(mdev, ns, f, &done); + } + + spin_unlock_irqrestore(&mdev->req_lock, flags); + + if (f & CS_WAIT_COMPLETE && rv == SS_SUCCESS) { + D_ASSERT(current != mdev->worker.task); + wait_for_completion(&done); + } + +abort: + if (f & CS_SERIALIZE) + mutex_unlock(&mdev->state_mutex); + + return rv; +} + +/** + * _drbd_request_state() - Request a state change (with flags) + * @mdev: DRBD device. + * @mask: mask of state bits to change. + * @val: value of new state bits. + * @f: flags + * + * Cousin of drbd_request_state(), useful with the CS_WAIT_COMPLETE + * flag, or when logging of failed state change requests is not desired. + */ +int _drbd_request_state(struct drbd_conf *mdev, union drbd_state mask, + union drbd_state val, enum chg_state_flags f) +{ + int rv; + + wait_event(mdev->state_wait, + (rv = drbd_req_state(mdev, mask, val, f)) != SS_IN_TRANSIENT_STATE); + + return rv; +} + +static void print_st(struct drbd_conf *mdev, char *name, union drbd_state ns) +{ + dev_err(DEV, " %s = { cs:%s ro:%s/%s ds:%s/%s %c%c%c%c }\n", + name, + drbd_conn_str(ns.conn), + drbd_role_str(ns.role), + drbd_role_str(ns.peer), + drbd_disk_str(ns.disk), + drbd_disk_str(ns.pdsk), + ns.susp ? 's' : 'r', + ns.aftr_isp ? 'a' : '-', + ns.peer_isp ? 'p' : '-', + ns.user_isp ? 'u' : '-' + ); +} + +void print_st_err(struct drbd_conf *mdev, + union drbd_state os, union drbd_state ns, int err) +{ + if (err == SS_IN_TRANSIENT_STATE) + return; + dev_err(DEV, "State change failed: %s\n", drbd_set_st_err_str(err)); + print_st(mdev, " state", os); + print_st(mdev, "wanted", ns); +} + + +#define drbd_peer_str drbd_role_str +#define drbd_pdsk_str drbd_disk_str + +#define drbd_susp_str(A) ((A) ? "1" : "0") +#define drbd_aftr_isp_str(A) ((A) ? "1" : "0") +#define drbd_peer_isp_str(A) ((A) ? "1" : "0") +#define drbd_user_isp_str(A) ((A) ? "1" : "0") + +#define PSC(A) \ + ({ if (ns.A != os.A) { \ + pbp += sprintf(pbp, #A "( %s -> %s ) ", \ + drbd_##A##_str(os.A), \ + drbd_##A##_str(ns.A)); \ + } }) + +/** + * is_valid_state() - Returns an SS_ error code if ns is not valid + * @mdev: DRBD device. + * @ns: State to consider. + */ +static int is_valid_state(struct drbd_conf *mdev, union drbd_state ns) +{ + /* See drbd_state_sw_errors in drbd_strings.c */ + + enum drbd_fencing_p fp; + int rv = SS_SUCCESS; + + fp = FP_DONT_CARE; + if (get_ldev(mdev)) { + fp = mdev->ldev->dc.fencing; + put_ldev(mdev); + } + + if (get_net_conf(mdev)) { + if (!mdev->net_conf->two_primaries && + ns.role == R_PRIMARY && ns.peer == R_PRIMARY) + rv = SS_TWO_PRIMARIES; + put_net_conf(mdev); + } + + if (rv <= 0) + /* already found a reason to abort */; + else if (ns.role == R_SECONDARY && mdev->open_cnt) + rv = SS_DEVICE_IN_USE; + + else if (ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.disk < D_UP_TO_DATE) + rv = SS_NO_UP_TO_DATE_DISK; + + else if (fp >= FP_RESOURCE && + ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.pdsk >= D_UNKNOWN) + rv = SS_PRIMARY_NOP; + + else if (ns.role == R_PRIMARY && ns.disk <= D_INCONSISTENT && ns.pdsk <= D_INCONSISTENT) + rv = SS_NO_UP_TO_DATE_DISK; + + else if (ns.conn > C_CONNECTED && ns.disk < D_INCONSISTENT) + rv = SS_NO_LOCAL_DISK; + + else if (ns.conn > C_CONNECTED && ns.pdsk < D_INCONSISTENT) + rv = SS_NO_REMOTE_DISK; + + else if ((ns.conn == C_CONNECTED || + ns.conn == C_WF_BITMAP_S || + ns.conn == C_SYNC_SOURCE || + ns.conn == C_PAUSED_SYNC_S) && + ns.disk == D_OUTDATED) + rv = SS_CONNECTED_OUTDATES; + + else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && + (mdev->sync_conf.verify_alg[0] == 0)) + rv = SS_NO_VERIFY_ALG; + + else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && + mdev->agreed_pro_version < 88) + rv = SS_NOT_SUPPORTED; + + return rv; +} + +/** + * is_valid_state_transition() - Returns an SS_ error code if the state transition is not possible + * @mdev: DRBD device. + * @ns: new state. + * @os: old state. + */ +static int is_valid_state_transition(struct drbd_conf *mdev, + union drbd_state ns, union drbd_state os) +{ + int rv = SS_SUCCESS; + + if ((ns.conn == C_STARTING_SYNC_T || ns.conn == C_STARTING_SYNC_S) && + os.conn > C_CONNECTED) + rv = SS_RESYNC_RUNNING; + + if (ns.conn == C_DISCONNECTING && os.conn == C_STANDALONE) + rv = SS_ALREADY_STANDALONE; + + if (ns.disk > D_ATTACHING && os.disk == D_DISKLESS) + rv = SS_IS_DISKLESS; + + if (ns.conn == C_WF_CONNECTION && os.conn < C_UNCONNECTED) + rv = SS_NO_NET_CONFIG; + + if (ns.disk == D_OUTDATED && os.disk < D_OUTDATED && os.disk != D_ATTACHING) + rv = SS_LOWER_THAN_OUTDATED; + + if (ns.conn == C_DISCONNECTING && os.conn == C_UNCONNECTED) + rv = SS_IN_TRANSIENT_STATE; + + if (ns.conn == os.conn && ns.conn == C_WF_REPORT_PARAMS) + rv = SS_IN_TRANSIENT_STATE; + + if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && os.conn < C_CONNECTED) + rv = SS_NEED_CONNECTION; + + if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && + ns.conn != os.conn && os.conn > C_CONNECTED) + rv = SS_RESYNC_RUNNING; + + if ((ns.conn == C_STARTING_SYNC_S || ns.conn == C_STARTING_SYNC_T) && + os.conn < C_CONNECTED) + rv = SS_NEED_CONNECTION; + + return rv; +} + +/** + * sanitize_state() - Resolves implicitly necessary additional changes to a state transition + * @mdev: DRBD device. + * @os: old state. + * @ns: new state. + * @warn_sync_abort: + * + * When we loose connection, we have to set the state of the peers disk (pdsk) + * to D_UNKNOWN. This rule and many more along those lines are in this function. + */ +static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os, + union drbd_state ns, int *warn_sync_abort) +{ + enum drbd_fencing_p fp; + + fp = FP_DONT_CARE; + if (get_ldev(mdev)) { + fp = mdev->ldev->dc.fencing; + put_ldev(mdev); + } + + /* Disallow Network errors to configure a device's network part */ + if ((ns.conn >= C_TIMEOUT && ns.conn <= C_TEAR_DOWN) && + os.conn <= C_DISCONNECTING) + ns.conn = os.conn; + + /* After a network error (+C_TEAR_DOWN) only C_UNCONNECTED or C_DISCONNECTING can follow */ + if (os.conn >= C_TIMEOUT && os.conn <= C_TEAR_DOWN && + ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING) + ns.conn = os.conn; + + /* After C_DISCONNECTING only C_STANDALONE may follow */ + if (os.conn == C_DISCONNECTING && ns.conn != C_STANDALONE) + ns.conn = os.conn; + + if (ns.conn < C_CONNECTED) { + ns.peer_isp = 0; + ns.peer = R_UNKNOWN; + if (ns.pdsk > D_UNKNOWN || ns.pdsk < D_INCONSISTENT) + ns.pdsk = D_UNKNOWN; + } + + /* Clear the aftr_isp when becoming unconfigured */ + if (ns.conn == C_STANDALONE && ns.disk == D_DISKLESS && ns.role == R_SECONDARY) + ns.aftr_isp = 0; + + if (ns.conn <= C_DISCONNECTING && ns.disk == D_DISKLESS) + ns.pdsk = D_UNKNOWN; + + /* Abort resync if a disk fails/detaches */ + if (os.conn > C_CONNECTED && ns.conn > C_CONNECTED && + (ns.disk <= D_FAILED || ns.pdsk <= D_FAILED)) { + if (warn_sync_abort) + *warn_sync_abort = 1; + ns.conn = C_CONNECTED; + } + + if (ns.conn >= C_CONNECTED && + ((ns.disk == D_CONSISTENT || ns.disk == D_OUTDATED) || + (ns.disk == D_NEGOTIATING && ns.conn == C_WF_BITMAP_T))) { + switch (ns.conn) { + case C_WF_BITMAP_T: + case C_PAUSED_SYNC_T: + ns.disk = D_OUTDATED; + break; + case C_CONNECTED: + case C_WF_BITMAP_S: + case C_SYNC_SOURCE: + case C_PAUSED_SYNC_S: + ns.disk = D_UP_TO_DATE; + break; + case C_SYNC_TARGET: + ns.disk = D_INCONSISTENT; + dev_warn(DEV, "Implicitly set disk state Inconsistent!\n"); + break; + } + if (os.disk == D_OUTDATED && ns.disk == D_UP_TO_DATE) + dev_warn(DEV, "Implicitly set disk from Outdated to UpToDate\n"); + } + + if (ns.conn >= C_CONNECTED && + (ns.pdsk == D_CONSISTENT || ns.pdsk == D_OUTDATED)) { + switch (ns.conn) { + case C_CONNECTED: + case C_WF_BITMAP_T: + case C_PAUSED_SYNC_T: + case C_SYNC_TARGET: + ns.pdsk = D_UP_TO_DATE; + break; + case C_WF_BITMAP_S: + case C_PAUSED_SYNC_S: + ns.pdsk = D_OUTDATED; + break; + case C_SYNC_SOURCE: + ns.pdsk = D_INCONSISTENT; + dev_warn(DEV, "Implicitly set pdsk Inconsistent!\n"); + break; + } + if (os.pdsk == D_OUTDATED && ns.pdsk == D_UP_TO_DATE) + dev_warn(DEV, "Implicitly set pdsk from Outdated to UpToDate\n"); + } + + /* Connection breaks down before we finished "Negotiating" */ + if (ns.conn < C_CONNECTED && ns.disk == D_NEGOTIATING && + get_ldev_if_state(mdev, D_NEGOTIATING)) { + if (mdev->ed_uuid == mdev->ldev->md.uuid[UI_CURRENT]) { + ns.disk = mdev->new_state_tmp.disk; + ns.pdsk = mdev->new_state_tmp.pdsk; + } else { + dev_alert(DEV, "Connection lost while negotiating, no data!\n"); + ns.disk = D_DISKLESS; + ns.pdsk = D_UNKNOWN; + } + put_ldev(mdev); + } + + if (fp == FP_STONITH && + (ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.pdsk > D_OUTDATED) && + !(os.role == R_PRIMARY && os.conn < C_CONNECTED && os.pdsk > D_OUTDATED)) + ns.susp = 1; + + if (ns.aftr_isp || ns.peer_isp || ns.user_isp) { + if (ns.conn == C_SYNC_SOURCE) + ns.conn = C_PAUSED_SYNC_S; + if (ns.conn == C_SYNC_TARGET) + ns.conn = C_PAUSED_SYNC_T; + } else { + if (ns.conn == C_PAUSED_SYNC_S) + ns.conn = C_SYNC_SOURCE; + if (ns.conn == C_PAUSED_SYNC_T) + ns.conn = C_SYNC_TARGET; + } + + return ns; +} + +/* helper for __drbd_set_state */ +static void set_ov_position(struct drbd_conf *mdev, enum drbd_conns cs) +{ + if (cs == C_VERIFY_T) { + /* starting online verify from an arbitrary position + * does not fit well into the existing protocol. + * on C_VERIFY_T, we initialize ov_left and friends + * implicitly in receive_DataRequest once the + * first P_OV_REQUEST is received */ + mdev->ov_start_sector = ~(sector_t)0; + } else { + unsigned long bit = BM_SECT_TO_BIT(mdev->ov_start_sector); + if (bit >= mdev->rs_total) + mdev->ov_start_sector = + BM_BIT_TO_SECT(mdev->rs_total - 1); + mdev->ov_position = mdev->ov_start_sector; + } +} + +/** + * __drbd_set_state() - Set a new DRBD state + * @mdev: DRBD device. + * @ns: new state. + * @flags: Flags + * @done: Optional completion, that will get completed after the after_state_ch() finished + * + * Caller needs to hold req_lock, and global_state_lock. Do not call directly. + */ +int __drbd_set_state(struct drbd_conf *mdev, + union drbd_state ns, enum chg_state_flags flags, + struct completion *done) +{ + union drbd_state os; + int rv = SS_SUCCESS; + int warn_sync_abort = 0; + struct after_state_chg_work *ascw; + + os = mdev->state; + + ns = sanitize_state(mdev, os, ns, &warn_sync_abort); + + if (ns.i == os.i) + return SS_NOTHING_TO_DO; + + if (!(flags & CS_HARD)) { + /* pre-state-change checks ; only look at ns */ + /* See drbd_state_sw_errors in drbd_strings.c */ + + rv = is_valid_state(mdev, ns); + if (rv < SS_SUCCESS) { + /* If the old state was illegal as well, then let + this happen...*/ + + if (is_valid_state(mdev, os) == rv) { + dev_err(DEV, "Considering state change from bad state. " + "Error would be: '%s'\n", + drbd_set_st_err_str(rv)); + print_st(mdev, "old", os); + print_st(mdev, "new", ns); + rv = is_valid_state_transition(mdev, ns, os); + } + } else + rv = is_valid_state_transition(mdev, ns, os); + } + + if (rv < SS_SUCCESS) { + if (flags & CS_VERBOSE) + print_st_err(mdev, os, ns, rv); + return rv; + } + + if (warn_sync_abort) + dev_warn(DEV, "Resync aborted.\n"); + + { + char *pbp, pb[300]; + pbp = pb; + *pbp = 0; + PSC(role); + PSC(peer); + PSC(conn); + PSC(disk); + PSC(pdsk); + PSC(susp); + PSC(aftr_isp); + PSC(peer_isp); + PSC(user_isp); + dev_info(DEV, "%s\n", pb); + } + + /* solve the race between becoming unconfigured, + * worker doing the cleanup, and + * admin reconfiguring us: + * on (re)configure, first set CONFIG_PENDING, + * then wait for a potentially exiting worker, + * start the worker, and schedule one no_op. + * then proceed with configuration. + */ + if (ns.disk == D_DISKLESS && + ns.conn == C_STANDALONE && + ns.role == R_SECONDARY && + !test_and_set_bit(CONFIG_PENDING, &mdev->flags)) + set_bit(DEVICE_DYING, &mdev->flags); + + mdev->state.i = ns.i; + wake_up(&mdev->misc_wait); + wake_up(&mdev->state_wait); + + /* post-state-change actions */ + if (os.conn >= C_SYNC_SOURCE && ns.conn <= C_CONNECTED) { + set_bit(STOP_SYNC_TIMER, &mdev->flags); + mod_timer(&mdev->resync_timer, jiffies); + } + + /* aborted verify run. log the last position */ + if ((os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) && + ns.conn < C_CONNECTED) { + mdev->ov_start_sector = + BM_BIT_TO_SECT(mdev->rs_total - mdev->ov_left); + dev_info(DEV, "Online Verify reached sector %llu\n", + (unsigned long long)mdev->ov_start_sector); + } + + if ((os.conn == C_PAUSED_SYNC_T || os.conn == C_PAUSED_SYNC_S) && + (ns.conn == C_SYNC_TARGET || ns.conn == C_SYNC_SOURCE)) { + dev_info(DEV, "Syncer continues.\n"); + mdev->rs_paused += (long)jiffies-(long)mdev->rs_mark_time; + if (ns.conn == C_SYNC_TARGET) { + if (!test_and_clear_bit(STOP_SYNC_TIMER, &mdev->flags)) + mod_timer(&mdev->resync_timer, jiffies); + /* This if (!test_bit) is only needed for the case + that a device that has ceased to used its timer, + i.e. it is already in drbd_resync_finished() gets + paused and resumed. */ + } + } + + if ((os.conn == C_SYNC_TARGET || os.conn == C_SYNC_SOURCE) && + (ns.conn == C_PAUSED_SYNC_T || ns.conn == C_PAUSED_SYNC_S)) { + dev_info(DEV, "Resync suspended\n"); + mdev->rs_mark_time = jiffies; + if (ns.conn == C_PAUSED_SYNC_T) + set_bit(STOP_SYNC_TIMER, &mdev->flags); + } + + if (os.conn == C_CONNECTED && + (ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T)) { + mdev->ov_position = 0; + mdev->rs_total = + mdev->rs_mark_left = drbd_bm_bits(mdev); + if (mdev->agreed_pro_version >= 90) + set_ov_position(mdev, ns.conn); + else + mdev->ov_start_sector = 0; + mdev->ov_left = mdev->rs_total + - BM_SECT_TO_BIT(mdev->ov_position); + mdev->rs_start = + mdev->rs_mark_time = jiffies; + mdev->ov_last_oos_size = 0; + mdev->ov_last_oos_start = 0; + + if (ns.conn == C_VERIFY_S) { + dev_info(DEV, "Starting Online Verify from sector %llu\n", + (unsigned long long)mdev->ov_position); + mod_timer(&mdev->resync_timer, jiffies); + } + } + + if (get_ldev(mdev)) { + u32 mdf = mdev->ldev->md.flags & ~(MDF_CONSISTENT|MDF_PRIMARY_IND| + MDF_CONNECTED_IND|MDF_WAS_UP_TO_DATE| + MDF_PEER_OUT_DATED|MDF_CRASHED_PRIMARY); + + if (test_bit(CRASHED_PRIMARY, &mdev->flags)) + mdf |= MDF_CRASHED_PRIMARY; + if (mdev->state.role == R_PRIMARY || + (mdev->state.pdsk < D_INCONSISTENT && mdev->state.peer == R_PRIMARY)) + mdf |= MDF_PRIMARY_IND; + if (mdev->state.conn > C_WF_REPORT_PARAMS) + mdf |= MDF_CONNECTED_IND; + if (mdev->state.disk > D_INCONSISTENT) + mdf |= MDF_CONSISTENT; + if (mdev->state.disk > D_OUTDATED) + mdf |= MDF_WAS_UP_TO_DATE; + if (mdev->state.pdsk <= D_OUTDATED && mdev->state.pdsk >= D_INCONSISTENT) + mdf |= MDF_PEER_OUT_DATED; + if (mdf != mdev->ldev->md.flags) { + mdev->ldev->md.flags = mdf; + drbd_md_mark_dirty(mdev); + } + if (os.disk < D_CONSISTENT && ns.disk >= D_CONSISTENT) + drbd_set_ed_uuid(mdev, mdev->ldev->md.uuid[UI_CURRENT]); + put_ldev(mdev); + } + + /* Peer was forced D_UP_TO_DATE & R_PRIMARY, consider to resync */ + if (os.disk == D_INCONSISTENT && os.pdsk == D_INCONSISTENT && + os.peer == R_SECONDARY && ns.peer == R_PRIMARY) + set_bit(CONSIDER_RESYNC, &mdev->flags); + + /* Receiver should clean up itself */ + if (os.conn != C_DISCONNECTING && ns.conn == C_DISCONNECTING) + drbd_thread_stop_nowait(&mdev->receiver); + + /* Now the receiver finished cleaning up itself, it should die */ + if (os.conn != C_STANDALONE && ns.conn == C_STANDALONE) + drbd_thread_stop_nowait(&mdev->receiver); + + /* Upon network failure, we need to restart the receiver. */ + if (os.conn > C_TEAR_DOWN && + ns.conn <= C_TEAR_DOWN && ns.conn >= C_TIMEOUT) + drbd_thread_restart_nowait(&mdev->receiver); + + ascw = kmalloc(sizeof(*ascw), GFP_ATOMIC); + if (ascw) { + ascw->os = os; + ascw->ns = ns; + ascw->flags = flags; + ascw->w.cb = w_after_state_ch; + ascw->done = done; + drbd_queue_work(&mdev->data.work, &ascw->w); + } else { + dev_warn(DEV, "Could not kmalloc an ascw\n"); + } + + return rv; +} + +static int w_after_state_ch(struct drbd_conf *mdev, struct drbd_work *w, int unused) +{ + struct after_state_chg_work *ascw = + container_of(w, struct after_state_chg_work, w); + after_state_ch(mdev, ascw->os, ascw->ns, ascw->flags); + if (ascw->flags & CS_WAIT_COMPLETE) { + D_ASSERT(ascw->done != NULL); + complete(ascw->done); + } + kfree(ascw); + + return 1; +} + +static void abw_start_sync(struct drbd_conf *mdev, int rv) +{ + if (rv) { + dev_err(DEV, "Writing the bitmap failed not starting resync.\n"); + _drbd_request_state(mdev, NS(conn, C_CONNECTED), CS_VERBOSE); + return; + } + + switch (mdev->state.conn) { + case C_STARTING_SYNC_T: + _drbd_request_state(mdev, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE); + break; + case C_STARTING_SYNC_S: + drbd_start_resync(mdev, C_SYNC_SOURCE); + break; + } +} + +/** + * after_state_ch() - Perform after state change actions that may sleep + * @mdev: DRBD device. + * @os: old state. + * @ns: new state. + * @flags: Flags + */ +static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, + union drbd_state ns, enum chg_state_flags flags) +{ + enum drbd_fencing_p fp; + + if (os.conn != C_CONNECTED && ns.conn == C_CONNECTED) { + clear_bit(CRASHED_PRIMARY, &mdev->flags); + if (mdev->p_uuid) + mdev->p_uuid[UI_FLAGS] &= ~((u64)2); + } + + fp = FP_DONT_CARE; + if (get_ldev(mdev)) { + fp = mdev->ldev->dc.fencing; + put_ldev(mdev); + } + + /* Inform userspace about the change... */ + drbd_bcast_state(mdev, ns); + + if (!(os.role == R_PRIMARY && os.disk < D_UP_TO_DATE && os.pdsk < D_UP_TO_DATE) && + (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE)) + drbd_khelper(mdev, "pri-on-incon-degr"); + + /* Here we have the actions that are performed after a + state change. This function might sleep */ + + if (fp == FP_STONITH && ns.susp) { + /* case1: The outdate peer handler is successful: + * case2: The connection was established again: */ + if ((os.pdsk > D_OUTDATED && ns.pdsk <= D_OUTDATED) || + (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED)) { + tl_clear(mdev); + spin_lock_irq(&mdev->req_lock); + _drbd_set_state(_NS(mdev, susp, 0), CS_VERBOSE, NULL); + spin_unlock_irq(&mdev->req_lock); + } + } + /* Do not change the order of the if above and the two below... */ + if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) { /* attach on the peer */ + drbd_send_uuids(mdev); + drbd_send_state(mdev); + } + if (os.conn != C_WF_BITMAP_S && ns.conn == C_WF_BITMAP_S) + drbd_queue_bitmap_io(mdev, &drbd_send_bitmap, NULL, "send_bitmap (WFBitMapS)"); + + /* Lost contact to peer's copy of the data */ + if ((os.pdsk >= D_INCONSISTENT && + os.pdsk != D_UNKNOWN && + os.pdsk != D_OUTDATED) + && (ns.pdsk < D_INCONSISTENT || + ns.pdsk == D_UNKNOWN || + ns.pdsk == D_OUTDATED)) { + kfree(mdev->p_uuid); + mdev->p_uuid = NULL; + if (get_ldev(mdev)) { + if ((ns.role == R_PRIMARY || ns.peer == R_PRIMARY) && + mdev->ldev->md.uuid[UI_BITMAP] == 0 && ns.disk >= D_UP_TO_DATE) { + drbd_uuid_new_current(mdev); + drbd_send_uuids(mdev); + } + put_ldev(mdev); + } + } + + if (ns.pdsk < D_INCONSISTENT && get_ldev(mdev)) { + if (ns.peer == R_PRIMARY && mdev->ldev->md.uuid[UI_BITMAP] == 0) + drbd_uuid_new_current(mdev); + + /* D_DISKLESS Peer becomes secondary */ + if (os.peer == R_PRIMARY && ns.peer == R_SECONDARY) + drbd_al_to_on_disk_bm(mdev); + put_ldev(mdev); + } + + /* Last part of the attaching process ... */ + if (ns.conn >= C_CONNECTED && + os.disk == D_ATTACHING && ns.disk == D_NEGOTIATING) { + kfree(mdev->p_uuid); /* We expect to receive up-to-date UUIDs soon. */ + mdev->p_uuid = NULL; /* ...to not use the old ones in the mean time */ + drbd_send_sizes(mdev, 0); /* to start sync... */ + drbd_send_uuids(mdev); + drbd_send_state(mdev); + } + + /* We want to pause/continue resync, tell peer. */ + if (ns.conn >= C_CONNECTED && + ((os.aftr_isp != ns.aftr_isp) || + (os.user_isp != ns.user_isp))) + drbd_send_state(mdev); + + /* In case one of the isp bits got set, suspend other devices. */ + if ((!os.aftr_isp && !os.peer_isp && !os.user_isp) && + (ns.aftr_isp || ns.peer_isp || ns.user_isp)) + suspend_other_sg(mdev); + + /* Make sure the peer gets informed about eventual state + changes (ISP bits) while we were in WFReportParams. */ + if (os.conn == C_WF_REPORT_PARAMS && ns.conn >= C_CONNECTED) + drbd_send_state(mdev); + + /* We are in the progress to start a full sync... */ + if ((os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) || + (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S)) + drbd_queue_bitmap_io(mdev, &drbd_bmio_set_n_write, &abw_start_sync, "set_n_write from StartingSync"); + + /* We are invalidating our self... */ + if (os.conn < C_CONNECTED && ns.conn < C_CONNECTED && + os.disk > D_INCONSISTENT && ns.disk == D_INCONSISTENT) + drbd_queue_bitmap_io(mdev, &drbd_bmio_set_n_write, NULL, "set_n_write from invalidate"); + + if (os.disk > D_FAILED && ns.disk == D_FAILED) { + enum drbd_io_error_p eh; + + eh = EP_PASS_ON; + if (get_ldev_if_state(mdev, D_FAILED)) { + eh = mdev->ldev->dc.on_io_error; + put_ldev(mdev); + } + + drbd_rs_cancel_all(mdev); + /* since get_ldev() only works as long as disk>=D_INCONSISTENT, + and it is D_DISKLESS here, local_cnt can only go down, it can + not increase... It will reach zero */ + wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); + mdev->rs_total = 0; + mdev->rs_failed = 0; + atomic_set(&mdev->rs_pending_cnt, 0); + + spin_lock_irq(&mdev->req_lock); + _drbd_set_state(_NS(mdev, disk, D_DISKLESS), CS_HARD, NULL); + spin_unlock_irq(&mdev->req_lock); + + if (eh == EP_CALL_HELPER) + drbd_khelper(mdev, "local-io-error"); + } + + if (os.disk > D_DISKLESS && ns.disk == D_DISKLESS) { + + if (os.disk == D_FAILED) /* && ns.disk == D_DISKLESS*/ { + if (drbd_send_state(mdev)) + dev_warn(DEV, "Notified peer that my disk is broken.\n"); + else + dev_err(DEV, "Sending state in drbd_io_error() failed\n"); + } + + lc_destroy(mdev->resync); + mdev->resync = NULL; + lc_destroy(mdev->act_log); + mdev->act_log = NULL; + __no_warn(local, + drbd_free_bc(mdev->ldev); + mdev->ldev = NULL;); + + if (mdev->md_io_tmpp) + __free_page(mdev->md_io_tmpp); + } + + /* Disks got bigger while they were detached */ + if (ns.disk > D_NEGOTIATING && ns.pdsk > D_NEGOTIATING && + test_and_clear_bit(RESYNC_AFTER_NEG, &mdev->flags)) { + if (ns.conn == C_CONNECTED) + resync_after_online_grow(mdev); + } + + /* A resync finished or aborted, wake paused devices... */ + if ((os.conn > C_CONNECTED && ns.conn <= C_CONNECTED) || + (os.peer_isp && !ns.peer_isp) || + (os.user_isp && !ns.user_isp)) + resume_next_sg(mdev); + + /* Upon network connection, we need to start the receiver */ + if (os.conn == C_STANDALONE && ns.conn == C_UNCONNECTED) + drbd_thread_start(&mdev->receiver); + + /* Terminate worker thread if we are unconfigured - it will be + restarted as needed... */ + if (ns.disk == D_DISKLESS && + ns.conn == C_STANDALONE && + ns.role == R_SECONDARY) { + if (os.aftr_isp != ns.aftr_isp) + resume_next_sg(mdev); + /* set in __drbd_set_state, unless CONFIG_PENDING was set */ + if (test_bit(DEVICE_DYING, &mdev->flags)) + drbd_thread_stop_nowait(&mdev->worker); + } + + drbd_md_sync(mdev); +} + + +static int drbd_thread_setup(void *arg) +{ + struct drbd_thread *thi = (struct drbd_thread *) arg; + struct drbd_conf *mdev = thi->mdev; + unsigned long flags; + int retval; + +restart: + retval = thi->function(thi); + + spin_lock_irqsave(&thi->t_lock, flags); + + /* if the receiver has been "Exiting", the last thing it did + * was set the conn state to "StandAlone", + * if now a re-connect request comes in, conn state goes C_UNCONNECTED, + * and receiver thread will be "started". + * drbd_thread_start needs to set "Restarting" in that case. + * t_state check and assignment needs to be within the same spinlock, + * so either thread_start sees Exiting, and can remap to Restarting, + * or thread_start see None, and can proceed as normal. + */ + + if (thi->t_state == Restarting) { + dev_info(DEV, "Restarting %s\n", current->comm); + thi->t_state = Running; + spin_unlock_irqrestore(&thi->t_lock, flags); + goto restart; + } + + thi->task = NULL; + thi->t_state = None; + smp_mb(); + complete(&thi->stop); + spin_unlock_irqrestore(&thi->t_lock, flags); + + dev_info(DEV, "Terminating %s\n", current->comm); + + /* Release mod reference taken when thread was started */ + module_put(THIS_MODULE); + return retval; +} + +static void drbd_thread_init(struct drbd_conf *mdev, struct drbd_thread *thi, + int (*func) (struct drbd_thread *)) +{ + spin_lock_init(&thi->t_lock); + thi->task = NULL; + thi->t_state = None; + thi->function = func; + thi->mdev = mdev; +} + +int drbd_thread_start(struct drbd_thread *thi) +{ + struct drbd_conf *mdev = thi->mdev; + struct task_struct *nt; + unsigned long flags; + + const char *me = + thi == &mdev->receiver ? "receiver" : + thi == &mdev->asender ? "asender" : + thi == &mdev->worker ? "worker" : "NONSENSE"; + + /* is used from state engine doing drbd_thread_stop_nowait, + * while holding the req lock irqsave */ + spin_lock_irqsave(&thi->t_lock, flags); + + switch (thi->t_state) { + case None: + dev_info(DEV, "Starting %s thread (from %s [%d])\n", + me, current->comm, current->pid); + + /* Get ref on module for thread - this is released when thread exits */ + if (!try_module_get(THIS_MODULE)) { + dev_err(DEV, "Failed to get module reference in drbd_thread_start\n"); + spin_unlock_irqrestore(&thi->t_lock, flags); + return FALSE; + } + + init_completion(&thi->stop); + D_ASSERT(thi->task == NULL); + thi->reset_cpu_mask = 1; + thi->t_state = Running; + spin_unlock_irqrestore(&thi->t_lock, flags); + flush_signals(current); /* otherw. may get -ERESTARTNOINTR */ + + nt = kthread_create(drbd_thread_setup, (void *) thi, + "drbd%d_%s", mdev_to_minor(mdev), me); + + if (IS_ERR(nt)) { + dev_err(DEV, "Couldn't start thread\n"); + + module_put(THIS_MODULE); + return FALSE; + } + spin_lock_irqsave(&thi->t_lock, flags); + thi->task = nt; + thi->t_state = Running; + spin_unlock_irqrestore(&thi->t_lock, flags); + wake_up_process(nt); + break; + case Exiting: + thi->t_state = Restarting; + dev_info(DEV, "Restarting %s thread (from %s [%d])\n", + me, current->comm, current->pid); + /* fall through */ + case Running: + case Restarting: + default: + spin_unlock_irqrestore(&thi->t_lock, flags); + break; + } + + return TRUE; +} + + +void _drbd_thread_stop(struct drbd_thread *thi, int restart, int wait) +{ + unsigned long flags; + + enum drbd_thread_state ns = restart ? Restarting : Exiting; + + /* may be called from state engine, holding the req lock irqsave */ + spin_lock_irqsave(&thi->t_lock, flags); + + if (thi->t_state == None) { + spin_unlock_irqrestore(&thi->t_lock, flags); + if (restart) + drbd_thread_start(thi); + return; + } + + if (thi->t_state != ns) { + if (thi->task == NULL) { + spin_unlock_irqrestore(&thi->t_lock, flags); + return; + } + + thi->t_state = ns; + smp_mb(); + init_completion(&thi->stop); + if (thi->task != current) + force_sig(DRBD_SIGKILL, thi->task); + + } + + spin_unlock_irqrestore(&thi->t_lock, flags); + + if (wait) + wait_for_completion(&thi->stop); +} + +#ifdef CONFIG_SMP +/** + * drbd_calc_cpu_mask() - Generate CPU masks, spread over all CPUs + * @mdev: DRBD device. + * + * Forces all threads of a device onto the same CPU. This is beneficial for + * DRBD's performance. May be overwritten by user's configuration. + */ +void drbd_calc_cpu_mask(struct drbd_conf *mdev) +{ + int ord, cpu; + + /* user override. */ + if (cpumask_weight(mdev->cpu_mask)) + return; + + ord = mdev_to_minor(mdev) % cpumask_weight(cpu_online_mask); + for_each_online_cpu(cpu) { + if (ord-- == 0) { + cpumask_set_cpu(cpu, mdev->cpu_mask); + return; + } + } + /* should not be reached */ + cpumask_setall(mdev->cpu_mask); +} + +/** + * drbd_thread_current_set_cpu() - modifies the cpu mask of the _current_ thread + * @mdev: DRBD device. + * + * call in the "main loop" of _all_ threads, no need for any mutex, current won't die + * prematurely. + */ +void drbd_thread_current_set_cpu(struct drbd_conf *mdev) +{ + struct task_struct *p = current; + struct drbd_thread *thi = + p == mdev->asender.task ? &mdev->asender : + p == mdev->receiver.task ? &mdev->receiver : + p == mdev->worker.task ? &mdev->worker : + NULL; + ERR_IF(thi == NULL) + return; + if (!thi->reset_cpu_mask) + return; + thi->reset_cpu_mask = 0; + set_cpus_allowed_ptr(p, mdev->cpu_mask); +} +#endif + +/* the appropriate socket mutex must be held already */ +int _drbd_send_cmd(struct drbd_conf *mdev, struct socket *sock, + enum drbd_packets cmd, struct p_header *h, + size_t size, unsigned msg_flags) +{ + int sent, ok; + + ERR_IF(!h) return FALSE; + ERR_IF(!size) return FALSE; + + h->magic = BE_DRBD_MAGIC; + h->command = cpu_to_be16(cmd); + h->length = cpu_to_be16(size-sizeof(struct p_header)); + + sent = drbd_send(mdev, sock, h, size, msg_flags); + + ok = (sent == size); + if (!ok) + dev_err(DEV, "short sent %s size=%d sent=%d\n", + cmdname(cmd), (int)size, sent); + return ok; +} + +/* don't pass the socket. we may only look at it + * when we hold the appropriate socket mutex. + */ +int drbd_send_cmd(struct drbd_conf *mdev, int use_data_socket, + enum drbd_packets cmd, struct p_header *h, size_t size) +{ + int ok = 0; + struct socket *sock; + + if (use_data_socket) { + mutex_lock(&mdev->data.mutex); + sock = mdev->data.socket; + } else { + mutex_lock(&mdev->meta.mutex); + sock = mdev->meta.socket; + } + + /* drbd_disconnect() could have called drbd_free_sock() + * while we were waiting in down()... */ + if (likely(sock != NULL)) + ok = _drbd_send_cmd(mdev, sock, cmd, h, size, 0); + + if (use_data_socket) + mutex_unlock(&mdev->data.mutex); + else + mutex_unlock(&mdev->meta.mutex); + return ok; +} + +int drbd_send_cmd2(struct drbd_conf *mdev, enum drbd_packets cmd, char *data, + size_t size) +{ + struct p_header h; + int ok; + + h.magic = BE_DRBD_MAGIC; + h.command = cpu_to_be16(cmd); + h.length = cpu_to_be16(size); + + if (!drbd_get_data_sock(mdev)) + return 0; + + ok = (sizeof(h) == + drbd_send(mdev, mdev->data.socket, &h, sizeof(h), 0)); + ok = ok && (size == + drbd_send(mdev, mdev->data.socket, data, size, 0)); + + drbd_put_data_sock(mdev); + + return ok; +} + +int drbd_send_sync_param(struct drbd_conf *mdev, struct syncer_conf *sc) +{ + struct p_rs_param_89 *p; + struct socket *sock; + int size, rv; + const int apv = mdev->agreed_pro_version; + + size = apv <= 87 ? sizeof(struct p_rs_param) + : apv == 88 ? sizeof(struct p_rs_param) + + strlen(mdev->sync_conf.verify_alg) + 1 + : /* 89 */ sizeof(struct p_rs_param_89); + + /* used from admin command context and receiver/worker context. + * to avoid kmalloc, grab the socket right here, + * then use the pre-allocated sbuf there */ + mutex_lock(&mdev->data.mutex); + sock = mdev->data.socket; + + if (likely(sock != NULL)) { + enum drbd_packets cmd = apv >= 89 ? P_SYNC_PARAM89 : P_SYNC_PARAM; + + p = &mdev->data.sbuf.rs_param_89; + + /* initialize verify_alg and csums_alg */ + memset(p->verify_alg, 0, 2 * SHARED_SECRET_MAX); + + p->rate = cpu_to_be32(sc->rate); + + if (apv >= 88) + strcpy(p->verify_alg, mdev->sync_conf.verify_alg); + if (apv >= 89) + strcpy(p->csums_alg, mdev->sync_conf.csums_alg); + + rv = _drbd_send_cmd(mdev, sock, cmd, &p->head, size, 0); + } else + rv = 0; /* not ok */ + + mutex_unlock(&mdev->data.mutex); + + return rv; +} + +int drbd_send_protocol(struct drbd_conf *mdev) +{ + struct p_protocol *p; + int size, rv; + + size = sizeof(struct p_protocol); + + if (mdev->agreed_pro_version >= 87) + size += strlen(mdev->net_conf->integrity_alg) + 1; + + /* we must not recurse into our own queue, + * as that is blocked during handshake */ + p = kmalloc(size, GFP_NOIO); + if (p == NULL) + return 0; + + p->protocol = cpu_to_be32(mdev->net_conf->wire_protocol); + p->after_sb_0p = cpu_to_be32(mdev->net_conf->after_sb_0p); + p->after_sb_1p = cpu_to_be32(mdev->net_conf->after_sb_1p); + p->after_sb_2p = cpu_to_be32(mdev->net_conf->after_sb_2p); + p->want_lose = cpu_to_be32(mdev->net_conf->want_lose); + p->two_primaries = cpu_to_be32(mdev->net_conf->two_primaries); + + if (mdev->agreed_pro_version >= 87) + strcpy(p->integrity_alg, mdev->net_conf->integrity_alg); + + rv = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_PROTOCOL, + (struct p_header *)p, size); + kfree(p); + return rv; +} + +int _drbd_send_uuids(struct drbd_conf *mdev, u64 uuid_flags) +{ + struct p_uuids p; + int i; + + if (!get_ldev_if_state(mdev, D_NEGOTIATING)) + return 1; + + for (i = UI_CURRENT; i < UI_SIZE; i++) + p.uuid[i] = mdev->ldev ? cpu_to_be64(mdev->ldev->md.uuid[i]) : 0; + + mdev->comm_bm_set = drbd_bm_total_weight(mdev); + p.uuid[UI_SIZE] = cpu_to_be64(mdev->comm_bm_set); + uuid_flags |= mdev->net_conf->want_lose ? 1 : 0; + uuid_flags |= test_bit(CRASHED_PRIMARY, &mdev->flags) ? 2 : 0; + uuid_flags |= mdev->new_state_tmp.disk == D_INCONSISTENT ? 4 : 0; + p.uuid[UI_FLAGS] = cpu_to_be64(uuid_flags); + + put_ldev(mdev); + + return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_UUIDS, + (struct p_header *)&p, sizeof(p)); +} + +int drbd_send_uuids(struct drbd_conf *mdev) +{ + return _drbd_send_uuids(mdev, 0); +} + +int drbd_send_uuids_skip_initial_sync(struct drbd_conf *mdev) +{ + return _drbd_send_uuids(mdev, 8); +} + + +int drbd_send_sync_uuid(struct drbd_conf *mdev, u64 val) +{ + struct p_rs_uuid p; + + p.uuid = cpu_to_be64(val); + + return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_SYNC_UUID, + (struct p_header *)&p, sizeof(p)); +} + +int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply) +{ + struct p_sizes p; + sector_t d_size, u_size; + int q_order_type; + int ok; + + if (get_ldev_if_state(mdev, D_NEGOTIATING)) { + D_ASSERT(mdev->ldev->backing_bdev); + d_size = drbd_get_max_capacity(mdev->ldev); + u_size = mdev->ldev->dc.disk_size; + q_order_type = drbd_queue_order_type(mdev); + p.queue_order_type = cpu_to_be32(drbd_queue_order_type(mdev)); + put_ldev(mdev); + } else { + d_size = 0; + u_size = 0; + q_order_type = QUEUE_ORDERED_NONE; + } + + p.d_size = cpu_to_be64(d_size); + p.u_size = cpu_to_be64(u_size); + p.c_size = cpu_to_be64(trigger_reply ? 0 : drbd_get_capacity(mdev->this_bdev)); + p.max_segment_size = cpu_to_be32(queue_max_segment_size(mdev->rq_queue)); + p.queue_order_type = cpu_to_be32(q_order_type); + + ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_SIZES, + (struct p_header *)&p, sizeof(p)); + return ok; +} + +/** + * drbd_send_state() - Sends the drbd state to the peer + * @mdev: DRBD device. + */ +int drbd_send_state(struct drbd_conf *mdev) +{ + struct socket *sock; + struct p_state p; + int ok = 0; + + /* Grab state lock so we wont send state if we're in the middle + * of a cluster wide state change on another thread */ + drbd_state_lock(mdev); + + mutex_lock(&mdev->data.mutex); + + p.state = cpu_to_be32(mdev->state.i); /* Within the send mutex */ + sock = mdev->data.socket; + + if (likely(sock != NULL)) { + ok = _drbd_send_cmd(mdev, sock, P_STATE, + (struct p_header *)&p, sizeof(p), 0); + } + + mutex_unlock(&mdev->data.mutex); + + drbd_state_unlock(mdev); + return ok; +} + +int drbd_send_state_req(struct drbd_conf *mdev, + union drbd_state mask, union drbd_state val) +{ + struct p_req_state p; + + p.mask = cpu_to_be32(mask.i); + p.val = cpu_to_be32(val.i); + + return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_STATE_CHG_REQ, + (struct p_header *)&p, sizeof(p)); +} + +int drbd_send_sr_reply(struct drbd_conf *mdev, int retcode) +{ + struct p_req_state_reply p; + + p.retcode = cpu_to_be32(retcode); + + return drbd_send_cmd(mdev, USE_META_SOCKET, P_STATE_CHG_REPLY, + (struct p_header *)&p, sizeof(p)); +} + +int fill_bitmap_rle_bits(struct drbd_conf *mdev, + struct p_compressed_bm *p, + struct bm_xfer_ctx *c) +{ + struct bitstream bs; + unsigned long plain_bits; + unsigned long tmp; + unsigned long rl; + unsigned len; + unsigned toggle; + int bits; + + /* may we use this feature? */ + if ((mdev->sync_conf.use_rle == 0) || + (mdev->agreed_pro_version < 90)) + return 0; + + if (c->bit_offset >= c->bm_bits) + return 0; /* nothing to do. */ + + /* use at most thus many bytes */ + bitstream_init(&bs, p->code, BM_PACKET_VLI_BYTES_MAX, 0); + memset(p->code, 0, BM_PACKET_VLI_BYTES_MAX); + /* plain bits covered in this code string */ + plain_bits = 0; + + /* p->encoding & 0x80 stores whether the first run length is set. + * bit offset is implicit. + * start with toggle == 2 to be able to tell the first iteration */ + toggle = 2; + + /* see how much plain bits we can stuff into one packet + * using RLE and VLI. */ + do { + tmp = (toggle == 0) ? _drbd_bm_find_next_zero(mdev, c->bit_offset) + : _drbd_bm_find_next(mdev, c->bit_offset); + if (tmp == -1UL) + tmp = c->bm_bits; + rl = tmp - c->bit_offset; + + if (toggle == 2) { /* first iteration */ + if (rl == 0) { + /* the first checked bit was set, + * store start value, */ + DCBP_set_start(p, 1); + /* but skip encoding of zero run length */ + toggle = !toggle; + continue; + } + DCBP_set_start(p, 0); + } + + /* paranoia: catch zero runlength. + * can only happen if bitmap is modified while we scan it. */ + if (rl == 0) { + dev_err(DEV, "unexpected zero runlength while encoding bitmap " + "t:%u bo:%lu\n", toggle, c->bit_offset); + return -1; + } + + bits = vli_encode_bits(&bs, rl); + if (bits == -ENOBUFS) /* buffer full */ + break; + if (bits <= 0) { + dev_err(DEV, "error while encoding bitmap: %d\n", bits); + return 0; + } + + toggle = !toggle; + plain_bits += rl; + c->bit_offset = tmp; + } while (c->bit_offset < c->bm_bits); + + len = bs.cur.b - p->code + !!bs.cur.bit; + + if (plain_bits < (len << 3)) { + /* incompressible with this method. + * we need to rewind both word and bit position. */ + c->bit_offset -= plain_bits; + bm_xfer_ctx_bit_to_word_offset(c); + c->bit_offset = c->word_offset * BITS_PER_LONG; + return 0; + } + + /* RLE + VLI was able to compress it just fine. + * update c->word_offset. */ + bm_xfer_ctx_bit_to_word_offset(c); + + /* store pad_bits */ + DCBP_set_pad_bits(p, (8 - bs.cur.bit) & 0x7); + + return len; +} + +enum { OK, FAILED, DONE } +send_bitmap_rle_or_plain(struct drbd_conf *mdev, + struct p_header *h, struct bm_xfer_ctx *c) +{ + struct p_compressed_bm *p = (void*)h; + unsigned long num_words; + int len; + int ok; + + len = fill_bitmap_rle_bits(mdev, p, c); + + if (len < 0) + return FAILED; + + if (len) { + DCBP_set_code(p, RLE_VLI_Bits); + ok = _drbd_send_cmd(mdev, mdev->data.socket, P_COMPRESSED_BITMAP, h, + sizeof(*p) + len, 0); + + c->packets[0]++; + c->bytes[0] += sizeof(*p) + len; + + if (c->bit_offset >= c->bm_bits) + len = 0; /* DONE */ + } else { + /* was not compressible. + * send a buffer full of plain text bits instead. */ + num_words = min_t(size_t, BM_PACKET_WORDS, c->bm_words - c->word_offset); + len = num_words * sizeof(long); + if (len) + drbd_bm_get_lel(mdev, c->word_offset, num_words, (unsigned long*)h->payload); + ok = _drbd_send_cmd(mdev, mdev->data.socket, P_BITMAP, + h, sizeof(struct p_header) + len, 0); + c->word_offset += num_words; + c->bit_offset = c->word_offset * BITS_PER_LONG; + + c->packets[1]++; + c->bytes[1] += sizeof(struct p_header) + len; + + if (c->bit_offset > c->bm_bits) + c->bit_offset = c->bm_bits; + } + ok = ok ? ((len == 0) ? DONE : OK) : FAILED; + + if (ok == DONE) + INFO_bm_xfer_stats(mdev, "send", c); + return ok; +} + +/* See the comment at receive_bitmap() */ +int _drbd_send_bitmap(struct drbd_conf *mdev) +{ + struct bm_xfer_ctx c; + struct p_header *p; + int ret; + + ERR_IF(!mdev->bitmap) return FALSE; + + /* maybe we should use some per thread scratch page, + * and allocate that during initial device creation? */ + p = (struct p_header *) __get_free_page(GFP_NOIO); + if (!p) { + dev_err(DEV, "failed to allocate one page buffer in %s\n", __func__); + return FALSE; + } + + if (get_ldev(mdev)) { + if (drbd_md_test_flag(mdev->ldev, MDF_FULL_SYNC)) { + dev_info(DEV, "Writing the whole bitmap, MDF_FullSync was set.\n"); + drbd_bm_set_all(mdev); + if (drbd_bm_write(mdev)) { + /* write_bm did fail! Leave full sync flag set in Meta P_DATA + * but otherwise process as per normal - need to tell other + * side that a full resync is required! */ + dev_err(DEV, "Failed to write bitmap to disk!\n"); + } else { + drbd_md_clear_flag(mdev, MDF_FULL_SYNC); + drbd_md_sync(mdev); + } + } + put_ldev(mdev); + } + + c = (struct bm_xfer_ctx) { + .bm_bits = drbd_bm_bits(mdev), + .bm_words = drbd_bm_words(mdev), + }; + + do { + ret = send_bitmap_rle_or_plain(mdev, p, &c); + } while (ret == OK); + + free_page((unsigned long) p); + return (ret == DONE); +} + +int drbd_send_bitmap(struct drbd_conf *mdev) +{ + int err; + + if (!drbd_get_data_sock(mdev)) + return -1; + err = !_drbd_send_bitmap(mdev); + drbd_put_data_sock(mdev); + return err; +} + +int drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr, u32 set_size) +{ + int ok; + struct p_barrier_ack p; + + p.barrier = barrier_nr; + p.set_size = cpu_to_be32(set_size); + + if (mdev->state.conn < C_CONNECTED) + return FALSE; + ok = drbd_send_cmd(mdev, USE_META_SOCKET, P_BARRIER_ACK, + (struct p_header *)&p, sizeof(p)); + return ok; +} + +/** + * _drbd_send_ack() - Sends an ack packet + * @mdev: DRBD device. + * @cmd: Packet command code. + * @sector: sector, needs to be in big endian byte order + * @blksize: size in byte, needs to be in big endian byte order + * @block_id: Id, big endian byte order + */ +static int _drbd_send_ack(struct drbd_conf *mdev, enum drbd_packets cmd, + u64 sector, + u32 blksize, + u64 block_id) +{ + int ok; + struct p_block_ack p; + + p.sector = sector; + p.block_id = block_id; + p.blksize = blksize; + p.seq_num = cpu_to_be32(atomic_add_return(1, &mdev->packet_seq)); + + if (!mdev->meta.socket || mdev->state.conn < C_CONNECTED) + return FALSE; + ok = drbd_send_cmd(mdev, USE_META_SOCKET, cmd, + (struct p_header *)&p, sizeof(p)); + return ok; +} + +int drbd_send_ack_dp(struct drbd_conf *mdev, enum drbd_packets cmd, + struct p_data *dp) +{ + const int header_size = sizeof(struct p_data) + - sizeof(struct p_header); + int data_size = ((struct p_header *)dp)->length - header_size; + + return _drbd_send_ack(mdev, cmd, dp->sector, cpu_to_be32(data_size), + dp->block_id); +} + +int drbd_send_ack_rp(struct drbd_conf *mdev, enum drbd_packets cmd, + struct p_block_req *rp) +{ + return _drbd_send_ack(mdev, cmd, rp->sector, rp->blksize, rp->block_id); +} + +/** + * drbd_send_ack() - Sends an ack packet + * @mdev: DRBD device. + * @cmd: Packet command code. + * @e: Epoch entry. + */ +int drbd_send_ack(struct drbd_conf *mdev, + enum drbd_packets cmd, struct drbd_epoch_entry *e) +{ + return _drbd_send_ack(mdev, cmd, + cpu_to_be64(e->sector), + cpu_to_be32(e->size), + e->block_id); +} + +/* This function misuses the block_id field to signal if the blocks + * are is sync or not. */ +int drbd_send_ack_ex(struct drbd_conf *mdev, enum drbd_packets cmd, + sector_t sector, int blksize, u64 block_id) +{ + return _drbd_send_ack(mdev, cmd, + cpu_to_be64(sector), + cpu_to_be32(blksize), + cpu_to_be64(block_id)); +} + +int drbd_send_drequest(struct drbd_conf *mdev, int cmd, + sector_t sector, int size, u64 block_id) +{ + int ok; + struct p_block_req p; + + p.sector = cpu_to_be64(sector); + p.block_id = block_id; + p.blksize = cpu_to_be32(size); + + ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, cmd, + (struct p_header *)&p, sizeof(p)); + return ok; +} + +int drbd_send_drequest_csum(struct drbd_conf *mdev, + sector_t sector, int size, + void *digest, int digest_size, + enum drbd_packets cmd) +{ + int ok; + struct p_block_req p; + + p.sector = cpu_to_be64(sector); + p.block_id = BE_DRBD_MAGIC + 0xbeef; + p.blksize = cpu_to_be32(size); + + p.head.magic = BE_DRBD_MAGIC; + p.head.command = cpu_to_be16(cmd); + p.head.length = cpu_to_be16(sizeof(p) - sizeof(struct p_header) + digest_size); + + mutex_lock(&mdev->data.mutex); + + ok = (sizeof(p) == drbd_send(mdev, mdev->data.socket, &p, sizeof(p), 0)); + ok = ok && (digest_size == drbd_send(mdev, mdev->data.socket, digest, digest_size, 0)); + + mutex_unlock(&mdev->data.mutex); + + return ok; +} + +int drbd_send_ov_request(struct drbd_conf *mdev, sector_t sector, int size) +{ + int ok; + struct p_block_req p; + + p.sector = cpu_to_be64(sector); + p.block_id = BE_DRBD_MAGIC + 0xbabe; + p.blksize = cpu_to_be32(size); + + ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_OV_REQUEST, + (struct p_header *)&p, sizeof(p)); + return ok; +} + +/* called on sndtimeo + * returns FALSE if we should retry, + * TRUE if we think connection is dead + */ +static int we_should_drop_the_connection(struct drbd_conf *mdev, struct socket *sock) +{ + int drop_it; + /* long elapsed = (long)(jiffies - mdev->last_received); */ + + drop_it = mdev->meta.socket == sock + || !mdev->asender.task + || get_t_state(&mdev->asender) != Running + || mdev->state.conn < C_CONNECTED; + + if (drop_it) + return TRUE; + + drop_it = !--mdev->ko_count; + if (!drop_it) { + dev_err(DEV, "[%s/%d] sock_sendmsg time expired, ko = %u\n", + current->comm, current->pid, mdev->ko_count); + request_ping(mdev); + } + + return drop_it; /* && (mdev->state == R_PRIMARY) */; +} + +/* The idea of sendpage seems to be to put some kind of reference + * to the page into the skb, and to hand it over to the NIC. In + * this process get_page() gets called. + * + * As soon as the page was really sent over the network put_page() + * gets called by some part of the network layer. [ NIC driver? ] + * + * [ get_page() / put_page() increment/decrement the count. If count + * reaches 0 the page will be freed. ] + * + * This works nicely with pages from FSs. + * But this means that in protocol A we might signal IO completion too early! + * + * In order not to corrupt data during a resync we must make sure + * that we do not reuse our own buffer pages (EEs) to early, therefore + * we have the net_ee list. + * + * XFS seems to have problems, still, it submits pages with page_count == 0! + * As a workaround, we disable sendpage on pages + * with page_count == 0 or PageSlab. + */ +static int _drbd_no_send_page(struct drbd_conf *mdev, struct page *page, + int offset, size_t size) +{ + int sent = drbd_send(mdev, mdev->data.socket, kmap(page) + offset, size, 0); + kunmap(page); + if (sent == size) + mdev->send_cnt += size>>9; + return sent == size; +} + +static int _drbd_send_page(struct drbd_conf *mdev, struct page *page, + int offset, size_t size) +{ + mm_segment_t oldfs = get_fs(); + int sent, ok; + int len = size; + + /* e.g. XFS meta- & log-data is in slab pages, which have a + * page_count of 0 and/or have PageSlab() set. + * we cannot use send_page for those, as that does get_page(); + * put_page(); and would cause either a VM_BUG directly, or + * __page_cache_release a page that would actually still be referenced + * by someone, leading to some obscure delayed Oops somewhere else. */ + if (disable_sendpage || (page_count(page) < 1) || PageSlab(page)) + return _drbd_no_send_page(mdev, page, offset, size); + + drbd_update_congested(mdev); + set_fs(KERNEL_DS); + do { + sent = mdev->data.socket->ops->sendpage(mdev->data.socket, page, + offset, len, + MSG_NOSIGNAL); + if (sent == -EAGAIN) { + if (we_should_drop_the_connection(mdev, + mdev->data.socket)) + break; + else + continue; + } + if (sent <= 0) { + dev_warn(DEV, "%s: size=%d len=%d sent=%d\n", + __func__, (int)size, len, sent); + break; + } + len -= sent; + offset += sent; + } while (len > 0 /* THINK && mdev->cstate >= C_CONNECTED*/); + set_fs(oldfs); + clear_bit(NET_CONGESTED, &mdev->flags); + + ok = (len == 0); + if (likely(ok)) + mdev->send_cnt += size>>9; + return ok; +} + +static int _drbd_send_bio(struct drbd_conf *mdev, struct bio *bio) +{ + struct bio_vec *bvec; + int i; + __bio_for_each_segment(bvec, bio, i, 0) { + if (!_drbd_no_send_page(mdev, bvec->bv_page, + bvec->bv_offset, bvec->bv_len)) + return 0; + } + return 1; +} + +static int _drbd_send_zc_bio(struct drbd_conf *mdev, struct bio *bio) +{ + struct bio_vec *bvec; + int i; + __bio_for_each_segment(bvec, bio, i, 0) { + if (!_drbd_send_page(mdev, bvec->bv_page, + bvec->bv_offset, bvec->bv_len)) + return 0; + } + + return 1; +} + +/* Used to send write requests + * R_PRIMARY -> Peer (P_DATA) + */ +int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req) +{ + int ok = 1; + struct p_data p; + unsigned int dp_flags = 0; + void *dgb; + int dgs; + + if (!drbd_get_data_sock(mdev)) + return 0; + + dgs = (mdev->agreed_pro_version >= 87 && mdev->integrity_w_tfm) ? + crypto_hash_digestsize(mdev->integrity_w_tfm) : 0; + + p.head.magic = BE_DRBD_MAGIC; + p.head.command = cpu_to_be16(P_DATA); + p.head.length = + cpu_to_be16(sizeof(p) - sizeof(struct p_header) + dgs + req->size); + + p.sector = cpu_to_be64(req->sector); + p.block_id = (unsigned long)req; + p.seq_num = cpu_to_be32(req->seq_num = + atomic_add_return(1, &mdev->packet_seq)); + dp_flags = 0; + + /* NOTE: no need to check if barriers supported here as we would + * not pass the test in make_request_common in that case + */ + if (bio_rw_flagged(req->master_bio, BIO_RW_BARRIER)) { + dev_err(DEV, "ASSERT FAILED would have set DP_HARDBARRIER\n"); + /* dp_flags |= DP_HARDBARRIER; */ + } + if (bio_rw_flagged(req->master_bio, BIO_RW_SYNCIO)) + dp_flags |= DP_RW_SYNC; + /* for now handle SYNCIO and UNPLUG + * as if they still were one and the same flag */ + if (bio_rw_flagged(req->master_bio, BIO_RW_UNPLUG)) + dp_flags |= DP_RW_SYNC; + if (mdev->state.conn >= C_SYNC_SOURCE && + mdev->state.conn <= C_PAUSED_SYNC_T) + dp_flags |= DP_MAY_SET_IN_SYNC; + + p.dp_flags = cpu_to_be32(dp_flags); + set_bit(UNPLUG_REMOTE, &mdev->flags); + ok = (sizeof(p) == + drbd_send(mdev, mdev->data.socket, &p, sizeof(p), MSG_MORE)); + if (ok && dgs) { + dgb = mdev->int_dig_out; + drbd_csum(mdev, mdev->integrity_w_tfm, req->master_bio, dgb); + ok = drbd_send(mdev, mdev->data.socket, dgb, dgs, MSG_MORE); + } + if (ok) { + if (mdev->net_conf->wire_protocol == DRBD_PROT_A) + ok = _drbd_send_bio(mdev, req->master_bio); + else + ok = _drbd_send_zc_bio(mdev, req->master_bio); + } + + drbd_put_data_sock(mdev); + return ok; +} + +/* answer packet, used to send data back for read requests: + * Peer -> (diskless) R_PRIMARY (P_DATA_REPLY) + * C_SYNC_SOURCE -> C_SYNC_TARGET (P_RS_DATA_REPLY) + */ +int drbd_send_block(struct drbd_conf *mdev, enum drbd_packets cmd, + struct drbd_epoch_entry *e) +{ + int ok; + struct p_data p; + void *dgb; + int dgs; + + dgs = (mdev->agreed_pro_version >= 87 && mdev->integrity_w_tfm) ? + crypto_hash_digestsize(mdev->integrity_w_tfm) : 0; + + p.head.magic = BE_DRBD_MAGIC; + p.head.command = cpu_to_be16(cmd); + p.head.length = + cpu_to_be16(sizeof(p) - sizeof(struct p_header) + dgs + e->size); + + p.sector = cpu_to_be64(e->sector); + p.block_id = e->block_id; + /* p.seq_num = 0; No sequence numbers here.. */ + + /* Only called by our kernel thread. + * This one may be interrupted by DRBD_SIG and/or DRBD_SIGKILL + * in response to admin command or module unload. + */ + if (!drbd_get_data_sock(mdev)) + return 0; + + ok = sizeof(p) == drbd_send(mdev, mdev->data.socket, &p, + sizeof(p), MSG_MORE); + if (ok && dgs) { + dgb = mdev->int_dig_out; + drbd_csum(mdev, mdev->integrity_w_tfm, e->private_bio, dgb); + ok = drbd_send(mdev, mdev->data.socket, dgb, dgs, MSG_MORE); + } + if (ok) + ok = _drbd_send_zc_bio(mdev, e->private_bio); + + drbd_put_data_sock(mdev); + return ok; +} + +/* + drbd_send distinguishes two cases: + + Packets sent via the data socket "sock" + and packets sent via the meta data socket "msock" + + sock msock + -----------------+-------------------------+------------------------------ + timeout conf.timeout / 2 conf.timeout / 2 + timeout action send a ping via msock Abort communication + and close all sockets +*/ + +/* + * you must have down()ed the appropriate [m]sock_mutex elsewhere! + */ +int drbd_send(struct drbd_conf *mdev, struct socket *sock, + void *buf, size_t size, unsigned msg_flags) +{ + struct kvec iov; + struct msghdr msg; + int rv, sent = 0; + + if (!sock) + return -1000; + + /* THINK if (signal_pending) return ... ? */ + + iov.iov_base = buf; + iov.iov_len = size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = msg_flags | MSG_NOSIGNAL; + + if (sock == mdev->data.socket) { + mdev->ko_count = mdev->net_conf->ko_count; + drbd_update_congested(mdev); + } + do { + /* STRANGE + * tcp_sendmsg does _not_ use its size parameter at all ? + * + * -EAGAIN on timeout, -EINTR on signal. + */ +/* THINK + * do we need to block DRBD_SIG if sock == &meta.socket ?? + * otherwise wake_asender() might interrupt some send_*Ack ! + */ + rv = kernel_sendmsg(sock, &msg, &iov, 1, size); + if (rv == -EAGAIN) { + if (we_should_drop_the_connection(mdev, sock)) + break; + else + continue; + } + D_ASSERT(rv != 0); + if (rv == -EINTR) { + flush_signals(current); + rv = 0; + } + if (rv < 0) + break; + sent += rv; + iov.iov_base += rv; + iov.iov_len -= rv; + } while (sent < size); + + if (sock == mdev->data.socket) + clear_bit(NET_CONGESTED, &mdev->flags); + + if (rv <= 0) { + if (rv != -EAGAIN) { + dev_err(DEV, "%s_sendmsg returned %d\n", + sock == mdev->meta.socket ? "msock" : "sock", + rv); + drbd_force_state(mdev, NS(conn, C_BROKEN_PIPE)); + } else + drbd_force_state(mdev, NS(conn, C_TIMEOUT)); + } + + return sent; +} + +static int drbd_open(struct block_device *bdev, fmode_t mode) +{ + struct drbd_conf *mdev = bdev->bd_disk->private_data; + unsigned long flags; + int rv = 0; + + spin_lock_irqsave(&mdev->req_lock, flags); + /* to have a stable mdev->state.role + * and no race with updating open_cnt */ + + if (mdev->state.role != R_PRIMARY) { + if (mode & FMODE_WRITE) + rv = -EROFS; + else if (!allow_oos) + rv = -EMEDIUMTYPE; + } + + if (!rv) + mdev->open_cnt++; + spin_unlock_irqrestore(&mdev->req_lock, flags); + + return rv; +} + +static int drbd_release(struct gendisk *gd, fmode_t mode) +{ + struct drbd_conf *mdev = gd->private_data; + mdev->open_cnt--; + return 0; +} + +static void drbd_unplug_fn(struct request_queue *q) +{ + struct drbd_conf *mdev = q->queuedata; + + /* unplug FIRST */ + spin_lock_irq(q->queue_lock); + blk_remove_plug(q); + spin_unlock_irq(q->queue_lock); + + /* only if connected */ + spin_lock_irq(&mdev->req_lock); + if (mdev->state.pdsk >= D_INCONSISTENT && mdev->state.conn >= C_CONNECTED) { + D_ASSERT(mdev->state.role == R_PRIMARY); + if (test_and_clear_bit(UNPLUG_REMOTE, &mdev->flags)) { + /* add to the data.work queue, + * unless already queued. + * XXX this might be a good addition to drbd_queue_work + * anyways, to detect "double queuing" ... */ + if (list_empty(&mdev->unplug_work.list)) + drbd_queue_work(&mdev->data.work, + &mdev->unplug_work); + } + } + spin_unlock_irq(&mdev->req_lock); + + if (mdev->state.disk >= D_INCONSISTENT) + drbd_kick_lo(mdev); +} + +static void drbd_set_defaults(struct drbd_conf *mdev) +{ + mdev->sync_conf.after = DRBD_AFTER_DEF; + mdev->sync_conf.rate = DRBD_RATE_DEF; + mdev->sync_conf.al_extents = DRBD_AL_EXTENTS_DEF; + mdev->state = (union drbd_state) { + { .role = R_SECONDARY, + .peer = R_UNKNOWN, + .conn = C_STANDALONE, + .disk = D_DISKLESS, + .pdsk = D_UNKNOWN, + .susp = 0 + } }; +} + +void drbd_init_set_defaults(struct drbd_conf *mdev) +{ + /* the memset(,0,) did most of this. + * note: only assignments, no allocation in here */ + + drbd_set_defaults(mdev); + + /* for now, we do NOT yet support it, + * even though we start some framework + * to eventually support barriers */ + set_bit(NO_BARRIER_SUPP, &mdev->flags); + + atomic_set(&mdev->ap_bio_cnt, 0); + atomic_set(&mdev->ap_pending_cnt, 0); + atomic_set(&mdev->rs_pending_cnt, 0); + atomic_set(&mdev->unacked_cnt, 0); + atomic_set(&mdev->local_cnt, 0); + atomic_set(&mdev->net_cnt, 0); + atomic_set(&mdev->packet_seq, 0); + atomic_set(&mdev->pp_in_use, 0); + + mutex_init(&mdev->md_io_mutex); + mutex_init(&mdev->data.mutex); + mutex_init(&mdev->meta.mutex); + sema_init(&mdev->data.work.s, 0); + sema_init(&mdev->meta.work.s, 0); + mutex_init(&mdev->state_mutex); + + spin_lock_init(&mdev->data.work.q_lock); + spin_lock_init(&mdev->meta.work.q_lock); + + spin_lock_init(&mdev->al_lock); + spin_lock_init(&mdev->req_lock); + spin_lock_init(&mdev->peer_seq_lock); + spin_lock_init(&mdev->epoch_lock); + + INIT_LIST_HEAD(&mdev->active_ee); + INIT_LIST_HEAD(&mdev->sync_ee); + INIT_LIST_HEAD(&mdev->done_ee); + INIT_LIST_HEAD(&mdev->read_ee); + INIT_LIST_HEAD(&mdev->net_ee); + INIT_LIST_HEAD(&mdev->resync_reads); + INIT_LIST_HEAD(&mdev->data.work.q); + INIT_LIST_HEAD(&mdev->meta.work.q); + INIT_LIST_HEAD(&mdev->resync_work.list); + INIT_LIST_HEAD(&mdev->unplug_work.list); + INIT_LIST_HEAD(&mdev->md_sync_work.list); + INIT_LIST_HEAD(&mdev->bm_io_work.w.list); + mdev->resync_work.cb = w_resync_inactive; + mdev->unplug_work.cb = w_send_write_hint; + mdev->md_sync_work.cb = w_md_sync; + mdev->bm_io_work.w.cb = w_bitmap_io; + init_timer(&mdev->resync_timer); + init_timer(&mdev->md_sync_timer); + mdev->resync_timer.function = resync_timer_fn; + mdev->resync_timer.data = (unsigned long) mdev; + mdev->md_sync_timer.function = md_sync_timer_fn; + mdev->md_sync_timer.data = (unsigned long) mdev; + + init_waitqueue_head(&mdev->misc_wait); + init_waitqueue_head(&mdev->state_wait); + init_waitqueue_head(&mdev->ee_wait); + init_waitqueue_head(&mdev->al_wait); + init_waitqueue_head(&mdev->seq_wait); + + drbd_thread_init(mdev, &mdev->receiver, drbdd_init); + drbd_thread_init(mdev, &mdev->worker, drbd_worker); + drbd_thread_init(mdev, &mdev->asender, drbd_asender); + + mdev->agreed_pro_version = PRO_VERSION_MAX; + mdev->write_ordering = WO_bio_barrier; + mdev->resync_wenr = LC_FREE; +} + +void drbd_mdev_cleanup(struct drbd_conf *mdev) +{ + if (mdev->receiver.t_state != None) + dev_err(DEV, "ASSERT FAILED: receiver t_state == %d expected 0.\n", + mdev->receiver.t_state); + + /* no need to lock it, I'm the only thread alive */ + if (atomic_read(&mdev->current_epoch->epoch_size) != 0) + dev_err(DEV, "epoch_size:%d\n", atomic_read(&mdev->current_epoch->epoch_size)); + mdev->al_writ_cnt = + mdev->bm_writ_cnt = + mdev->read_cnt = + mdev->recv_cnt = + mdev->send_cnt = + mdev->writ_cnt = + mdev->p_size = + mdev->rs_start = + mdev->rs_total = + mdev->rs_failed = + mdev->rs_mark_left = + mdev->rs_mark_time = 0; + D_ASSERT(mdev->net_conf == NULL); + + drbd_set_my_capacity(mdev, 0); + if (mdev->bitmap) { + /* maybe never allocated. */ + drbd_bm_resize(mdev, 0); + drbd_bm_cleanup(mdev); + } + + drbd_free_resources(mdev); + + /* + * currently we drbd_init_ee only on module load, so + * we may do drbd_release_ee only on module unload! + */ + D_ASSERT(list_empty(&mdev->active_ee)); + D_ASSERT(list_empty(&mdev->sync_ee)); + D_ASSERT(list_empty(&mdev->done_ee)); + D_ASSERT(list_empty(&mdev->read_ee)); + D_ASSERT(list_empty(&mdev->net_ee)); + D_ASSERT(list_empty(&mdev->resync_reads)); + D_ASSERT(list_empty(&mdev->data.work.q)); + D_ASSERT(list_empty(&mdev->meta.work.q)); + D_ASSERT(list_empty(&mdev->resync_work.list)); + D_ASSERT(list_empty(&mdev->unplug_work.list)); + +} + + +static void drbd_destroy_mempools(void) +{ + struct page *page; + + while (drbd_pp_pool) { + page = drbd_pp_pool; + drbd_pp_pool = (struct page *)page_private(page); + __free_page(page); + drbd_pp_vacant--; + } + + /* D_ASSERT(atomic_read(&drbd_pp_vacant)==0); */ + + if (drbd_ee_mempool) + mempool_destroy(drbd_ee_mempool); + if (drbd_request_mempool) + mempool_destroy(drbd_request_mempool); + if (drbd_ee_cache) + kmem_cache_destroy(drbd_ee_cache); + if (drbd_request_cache) + kmem_cache_destroy(drbd_request_cache); + if (drbd_bm_ext_cache) + kmem_cache_destroy(drbd_bm_ext_cache); + if (drbd_al_ext_cache) + kmem_cache_destroy(drbd_al_ext_cache); + + drbd_ee_mempool = NULL; + drbd_request_mempool = NULL; + drbd_ee_cache = NULL; + drbd_request_cache = NULL; + drbd_bm_ext_cache = NULL; + drbd_al_ext_cache = NULL; + + return; +} + +static int drbd_create_mempools(void) +{ + struct page *page; + const int number = (DRBD_MAX_SEGMENT_SIZE/PAGE_SIZE) * minor_count; + int i; + + /* prepare our caches and mempools */ + drbd_request_mempool = NULL; + drbd_ee_cache = NULL; + drbd_request_cache = NULL; + drbd_bm_ext_cache = NULL; + drbd_al_ext_cache = NULL; + drbd_pp_pool = NULL; + + /* caches */ + drbd_request_cache = kmem_cache_create( + "drbd_req", sizeof(struct drbd_request), 0, 0, NULL); + if (drbd_request_cache == NULL) + goto Enomem; + + drbd_ee_cache = kmem_cache_create( + "drbd_ee", sizeof(struct drbd_epoch_entry), 0, 0, NULL); + if (drbd_ee_cache == NULL) + goto Enomem; + + drbd_bm_ext_cache = kmem_cache_create( + "drbd_bm", sizeof(struct bm_extent), 0, 0, NULL); + if (drbd_bm_ext_cache == NULL) + goto Enomem; + + drbd_al_ext_cache = kmem_cache_create( + "drbd_al", sizeof(struct lc_element), 0, 0, NULL); + if (drbd_al_ext_cache == NULL) + goto Enomem; + + /* mempools */ + drbd_request_mempool = mempool_create(number, + mempool_alloc_slab, mempool_free_slab, drbd_request_cache); + if (drbd_request_mempool == NULL) + goto Enomem; + + drbd_ee_mempool = mempool_create(number, + mempool_alloc_slab, mempool_free_slab, drbd_ee_cache); + if (drbd_request_mempool == NULL) + goto Enomem; + + /* drbd's page pool */ + spin_lock_init(&drbd_pp_lock); + + for (i = 0; i < number; i++) { + page = alloc_page(GFP_HIGHUSER); + if (!page) + goto Enomem; + set_page_private(page, (unsigned long)drbd_pp_pool); + drbd_pp_pool = page; + } + drbd_pp_vacant = number; + + return 0; + +Enomem: + drbd_destroy_mempools(); /* in case we allocated some */ + return -ENOMEM; +} + +static int drbd_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + /* just so we have it. you never know what interesting things we + * might want to do here some day... + */ + + return NOTIFY_DONE; +} + +static struct notifier_block drbd_notifier = { + .notifier_call = drbd_notify_sys, +}; + +static void drbd_release_ee_lists(struct drbd_conf *mdev) +{ + int rr; + + rr = drbd_release_ee(mdev, &mdev->active_ee); + if (rr) + dev_err(DEV, "%d EEs in active list found!\n", rr); + + rr = drbd_release_ee(mdev, &mdev->sync_ee); + if (rr) + dev_err(DEV, "%d EEs in sync list found!\n", rr); + + rr = drbd_release_ee(mdev, &mdev->read_ee); + if (rr) + dev_err(DEV, "%d EEs in read list found!\n", rr); + + rr = drbd_release_ee(mdev, &mdev->done_ee); + if (rr) + dev_err(DEV, "%d EEs in done list found!\n", rr); + + rr = drbd_release_ee(mdev, &mdev->net_ee); + if (rr) + dev_err(DEV, "%d EEs in net list found!\n", rr); +} + +/* caution. no locking. + * currently only used from module cleanup code. */ +static void drbd_delete_device(unsigned int minor) +{ + struct drbd_conf *mdev = minor_to_mdev(minor); + + if (!mdev) + return; + + /* paranoia asserts */ + if (mdev->open_cnt != 0) + dev_err(DEV, "open_cnt = %d in %s:%u", mdev->open_cnt, + __FILE__ , __LINE__); + + ERR_IF (!list_empty(&mdev->data.work.q)) { + struct list_head *lp; + list_for_each(lp, &mdev->data.work.q) { + dev_err(DEV, "lp = %p\n", lp); + } + }; + /* end paranoia asserts */ + + del_gendisk(mdev->vdisk); + + /* cleanup stuff that may have been allocated during + * device (re-)configuration or state changes */ + + if (mdev->this_bdev) + bdput(mdev->this_bdev); + + drbd_free_resources(mdev); + + drbd_release_ee_lists(mdev); + + /* should be free'd on disconnect? */ + kfree(mdev->ee_hash); + /* + mdev->ee_hash_s = 0; + mdev->ee_hash = NULL; + */ + + lc_destroy(mdev->act_log); + lc_destroy(mdev->resync); + + kfree(mdev->p_uuid); + /* mdev->p_uuid = NULL; */ + + kfree(mdev->int_dig_out); + kfree(mdev->int_dig_in); + kfree(mdev->int_dig_vv); + + /* cleanup the rest that has been + * allocated from drbd_new_device + * and actually free the mdev itself */ + drbd_free_mdev(mdev); +} + +static void drbd_cleanup(void) +{ + unsigned int i; + + unregister_reboot_notifier(&drbd_notifier); + + drbd_nl_cleanup(); + + if (minor_table) { + if (drbd_proc) + remove_proc_entry("drbd", NULL); + i = minor_count; + while (i--) + drbd_delete_device(i); + drbd_destroy_mempools(); + } + + kfree(minor_table); + + unregister_blkdev(DRBD_MAJOR, "drbd"); + + printk(KERN_INFO "drbd: module cleanup done.\n"); +} + +/** + * drbd_congested() - Callback for pdflush + * @congested_data: User data + * @bdi_bits: Bits pdflush is currently interested in + * + * Returns 1<<BDI_async_congested and/or 1<<BDI_sync_congested if we are congested. + */ +static int drbd_congested(void *congested_data, int bdi_bits) +{ + struct drbd_conf *mdev = congested_data; + struct request_queue *q; + char reason = '-'; + int r = 0; + + if (!__inc_ap_bio_cond(mdev)) { + /* DRBD has frozen IO */ + r = bdi_bits; + reason = 'd'; + goto out; + } + + if (get_ldev(mdev)) { + q = bdev_get_queue(mdev->ldev->backing_bdev); + r = bdi_congested(&q->backing_dev_info, bdi_bits); + put_ldev(mdev); + if (r) + reason = 'b'; + } + + if (bdi_bits & (1 << BDI_async_congested) && test_bit(NET_CONGESTED, &mdev->flags)) { + r |= (1 << BDI_async_congested); + reason = reason == 'b' ? 'a' : 'n'; + } + +out: + mdev->congestion_reason = reason; + return r; +} + +struct drbd_conf *drbd_new_device(unsigned int minor) +{ + struct drbd_conf *mdev; + struct gendisk *disk; + struct request_queue *q; + + /* GFP_KERNEL, we are outside of all write-out paths */ + mdev = kzalloc(sizeof(struct drbd_conf), GFP_KERNEL); + if (!mdev) + return NULL; + if (!zalloc_cpumask_var(&mdev->cpu_mask, GFP_KERNEL)) + goto out_no_cpumask; + + mdev->minor = minor; + + drbd_init_set_defaults(mdev); + + q = blk_alloc_queue(GFP_KERNEL); + if (!q) + goto out_no_q; + mdev->rq_queue = q; + q->queuedata = mdev; + blk_queue_max_segment_size(q, DRBD_MAX_SEGMENT_SIZE); + + disk = alloc_disk(1); + if (!disk) + goto out_no_disk; + mdev->vdisk = disk; + + set_disk_ro(disk, TRUE); + + disk->queue = q; + disk->major = DRBD_MAJOR; + disk->first_minor = minor; + disk->fops = &drbd_ops; + sprintf(disk->disk_name, "drbd%d", minor); + disk->private_data = mdev; + + mdev->this_bdev = bdget(MKDEV(DRBD_MAJOR, minor)); + /* we have no partitions. we contain only ourselves. */ + mdev->this_bdev->bd_contains = mdev->this_bdev; + + q->backing_dev_info.congested_fn = drbd_congested; + q->backing_dev_info.congested_data = mdev; + + blk_queue_make_request(q, drbd_make_request_26); + blk_queue_bounce_limit(q, BLK_BOUNCE_ANY); + blk_queue_merge_bvec(q, drbd_merge_bvec); + q->queue_lock = &mdev->req_lock; /* needed since we use */ + /* plugging on a queue, that actually has no requests! */ + q->unplug_fn = drbd_unplug_fn; + + mdev->md_io_page = alloc_page(GFP_KERNEL); + if (!mdev->md_io_page) + goto out_no_io_page; + + if (drbd_bm_init(mdev)) + goto out_no_bitmap; + /* no need to lock access, we are still initializing this minor device. */ + if (!tl_init(mdev)) + goto out_no_tl; + + mdev->app_reads_hash = kzalloc(APP_R_HSIZE*sizeof(void *), GFP_KERNEL); + if (!mdev->app_reads_hash) + goto out_no_app_reads; + + mdev->current_epoch = kzalloc(sizeof(struct drbd_epoch), GFP_KERNEL); + if (!mdev->current_epoch) + goto out_no_epoch; + + INIT_LIST_HEAD(&mdev->current_epoch->list); + mdev->epochs = 1; + + return mdev; + +/* out_whatever_else: + kfree(mdev->current_epoch); */ +out_no_epoch: + kfree(mdev->app_reads_hash); +out_no_app_reads: + tl_cleanup(mdev); +out_no_tl: + drbd_bm_cleanup(mdev); +out_no_bitmap: + __free_page(mdev->md_io_page); +out_no_io_page: + put_disk(disk); +out_no_disk: + blk_cleanup_queue(q); +out_no_q: + free_cpumask_var(mdev->cpu_mask); +out_no_cpumask: + kfree(mdev); + return NULL; +} + +/* counterpart of drbd_new_device. + * last part of drbd_delete_device. */ +void drbd_free_mdev(struct drbd_conf *mdev) +{ + kfree(mdev->current_epoch); + kfree(mdev->app_reads_hash); + tl_cleanup(mdev); + if (mdev->bitmap) /* should no longer be there. */ + drbd_bm_cleanup(mdev); + __free_page(mdev->md_io_page); + put_disk(mdev->vdisk); + blk_cleanup_queue(mdev->rq_queue); + free_cpumask_var(mdev->cpu_mask); + kfree(mdev); +} + + +int __init drbd_init(void) +{ + int err; + + if (sizeof(struct p_handshake) != 80) { + printk(KERN_ERR + "drbd: never change the size or layout " + "of the HandShake packet.\n"); + return -EINVAL; + } + + if (1 > minor_count || minor_count > 255) { + printk(KERN_ERR + "drbd: invalid minor_count (%d)\n", minor_count); +#ifdef MODULE + return -EINVAL; +#else + minor_count = 8; +#endif + } + + err = drbd_nl_init(); + if (err) + return err; + + err = register_blkdev(DRBD_MAJOR, "drbd"); + if (err) { + printk(KERN_ERR + "drbd: unable to register block device major %d\n", + DRBD_MAJOR); + return err; + } + + register_reboot_notifier(&drbd_notifier); + + /* + * allocate all necessary structs + */ + err = -ENOMEM; + + init_waitqueue_head(&drbd_pp_wait); + + drbd_proc = NULL; /* play safe for drbd_cleanup */ + minor_table = kzalloc(sizeof(struct drbd_conf *)*minor_count, + GFP_KERNEL); + if (!minor_table) + goto Enomem; + + err = drbd_create_mempools(); + if (err) + goto Enomem; + + drbd_proc = proc_create("drbd", S_IFREG | S_IRUGO , NULL, &drbd_proc_fops); + if (!drbd_proc) { + printk(KERN_ERR "drbd: unable to register proc file\n"); + goto Enomem; + } + + rwlock_init(&global_state_lock); + + printk(KERN_INFO "drbd: initialized. " + "Version: " REL_VERSION " (api:%d/proto:%d-%d)\n", + API_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX); + printk(KERN_INFO "drbd: %s\n", drbd_buildtag()); + printk(KERN_INFO "drbd: registered as block device major %d\n", + DRBD_MAJOR); + printk(KERN_INFO "drbd: minor_table @ 0x%p\n", minor_table); + + return 0; /* Success! */ + +Enomem: + drbd_cleanup(); + if (err == -ENOMEM) + /* currently always the case */ + printk(KERN_ERR "drbd: ran out of memory\n"); + else + printk(KERN_ERR "drbd: initialization failure\n"); + return err; +} + +void drbd_free_bc(struct drbd_backing_dev *ldev) +{ + if (ldev == NULL) + return; + + bd_release(ldev->backing_bdev); + bd_release(ldev->md_bdev); + + fput(ldev->lo_file); + fput(ldev->md_file); + + kfree(ldev); +} + +void drbd_free_sock(struct drbd_conf *mdev) +{ + if (mdev->data.socket) { + kernel_sock_shutdown(mdev->data.socket, SHUT_RDWR); + sock_release(mdev->data.socket); + mdev->data.socket = NULL; + } + if (mdev->meta.socket) { + kernel_sock_shutdown(mdev->meta.socket, SHUT_RDWR); + sock_release(mdev->meta.socket); + mdev->meta.socket = NULL; + } +} + + +void drbd_free_resources(struct drbd_conf *mdev) +{ + crypto_free_hash(mdev->csums_tfm); + mdev->csums_tfm = NULL; + crypto_free_hash(mdev->verify_tfm); + mdev->verify_tfm = NULL; + crypto_free_hash(mdev->cram_hmac_tfm); + mdev->cram_hmac_tfm = NULL; + crypto_free_hash(mdev->integrity_w_tfm); + mdev->integrity_w_tfm = NULL; + crypto_free_hash(mdev->integrity_r_tfm); + mdev->integrity_r_tfm = NULL; + + drbd_free_sock(mdev); + + __no_warn(local, + drbd_free_bc(mdev->ldev); + mdev->ldev = NULL;); +} + +/* meta data management */ + +struct meta_data_on_disk { + u64 la_size; /* last agreed size. */ + u64 uuid[UI_SIZE]; /* UUIDs. */ + u64 device_uuid; + u64 reserved_u64_1; + u32 flags; /* MDF */ + u32 magic; + u32 md_size_sect; + u32 al_offset; /* offset to this block */ + u32 al_nr_extents; /* important for restoring the AL */ + /* `-- act_log->nr_elements <-- sync_conf.al_extents */ + u32 bm_offset; /* offset to the bitmap, from here */ + u32 bm_bytes_per_bit; /* BM_BLOCK_SIZE */ + u32 reserved_u32[4]; + +} __packed; + +/** + * drbd_md_sync() - Writes the meta data super block if the MD_DIRTY flag bit is set + * @mdev: DRBD device. + */ +void drbd_md_sync(struct drbd_conf *mdev) +{ + struct meta_data_on_disk *buffer; + sector_t sector; + int i; + + if (!test_and_clear_bit(MD_DIRTY, &mdev->flags)) + return; + del_timer(&mdev->md_sync_timer); + + /* We use here D_FAILED and not D_ATTACHING because we try to write + * metadata even if we detach due to a disk failure! */ + if (!get_ldev_if_state(mdev, D_FAILED)) + return; + + mutex_lock(&mdev->md_io_mutex); + buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page); + memset(buffer, 0, 512); + + buffer->la_size = cpu_to_be64(drbd_get_capacity(mdev->this_bdev)); + for (i = UI_CURRENT; i < UI_SIZE; i++) + buffer->uuid[i] = cpu_to_be64(mdev->ldev->md.uuid[i]); + buffer->flags = cpu_to_be32(mdev->ldev->md.flags); + buffer->magic = cpu_to_be32(DRBD_MD_MAGIC); + + buffer->md_size_sect = cpu_to_be32(mdev->ldev->md.md_size_sect); + buffer->al_offset = cpu_to_be32(mdev->ldev->md.al_offset); + buffer->al_nr_extents = cpu_to_be32(mdev->act_log->nr_elements); + buffer->bm_bytes_per_bit = cpu_to_be32(BM_BLOCK_SIZE); + buffer->device_uuid = cpu_to_be64(mdev->ldev->md.device_uuid); + + buffer->bm_offset = cpu_to_be32(mdev->ldev->md.bm_offset); + + D_ASSERT(drbd_md_ss__(mdev, mdev->ldev) == mdev->ldev->md.md_offset); + sector = mdev->ldev->md.md_offset; + + if (drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) { + clear_bit(MD_DIRTY, &mdev->flags); + } else { + /* this was a try anyways ... */ + dev_err(DEV, "meta data update failed!\n"); + + drbd_chk_io_error(mdev, 1, TRUE); + } + + /* Update mdev->ldev->md.la_size_sect, + * since we updated it on metadata. */ + mdev->ldev->md.la_size_sect = drbd_get_capacity(mdev->this_bdev); + + mutex_unlock(&mdev->md_io_mutex); + put_ldev(mdev); +} + +/** + * drbd_md_read() - Reads in the meta data super block + * @mdev: DRBD device. + * @bdev: Device from which the meta data should be read in. + * + * Return 0 (NO_ERROR) on success, and an enum drbd_ret_codes in case + * something goes wrong. Currently only: ERR_IO_MD_DISK, ERR_MD_INVALID. + */ +int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev) +{ + struct meta_data_on_disk *buffer; + int i, rv = NO_ERROR; + + if (!get_ldev_if_state(mdev, D_ATTACHING)) + return ERR_IO_MD_DISK; + + mutex_lock(&mdev->md_io_mutex); + buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page); + + if (!drbd_md_sync_page_io(mdev, bdev, bdev->md.md_offset, READ)) { + /* NOTE: cant do normal error processing here as this is + called BEFORE disk is attached */ + dev_err(DEV, "Error while reading metadata.\n"); + rv = ERR_IO_MD_DISK; + goto err; + } + + if (be32_to_cpu(buffer->magic) != DRBD_MD_MAGIC) { + dev_err(DEV, "Error while reading metadata, magic not found.\n"); + rv = ERR_MD_INVALID; + goto err; + } + if (be32_to_cpu(buffer->al_offset) != bdev->md.al_offset) { + dev_err(DEV, "unexpected al_offset: %d (expected %d)\n", + be32_to_cpu(buffer->al_offset), bdev->md.al_offset); + rv = ERR_MD_INVALID; + goto err; + } + if (be32_to_cpu(buffer->bm_offset) != bdev->md.bm_offset) { + dev_err(DEV, "unexpected bm_offset: %d (expected %d)\n", + be32_to_cpu(buffer->bm_offset), bdev->md.bm_offset); + rv = ERR_MD_INVALID; + goto err; + } + if (be32_to_cpu(buffer->md_size_sect) != bdev->md.md_size_sect) { + dev_err(DEV, "unexpected md_size: %u (expected %u)\n", + be32_to_cpu(buffer->md_size_sect), bdev->md.md_size_sect); + rv = ERR_MD_INVALID; + goto err; + } + + if (be32_to_cpu(buffer->bm_bytes_per_bit) != BM_BLOCK_SIZE) { + dev_err(DEV, "unexpected bm_bytes_per_bit: %u (expected %u)\n", + be32_to_cpu(buffer->bm_bytes_per_bit), BM_BLOCK_SIZE); + rv = ERR_MD_INVALID; + goto err; + } + + bdev->md.la_size_sect = be64_to_cpu(buffer->la_size); + for (i = UI_CURRENT; i < UI_SIZE; i++) + bdev->md.uuid[i] = be64_to_cpu(buffer->uuid[i]); + bdev->md.flags = be32_to_cpu(buffer->flags); + mdev->sync_conf.al_extents = be32_to_cpu(buffer->al_nr_extents); + bdev->md.device_uuid = be64_to_cpu(buffer->device_uuid); + + if (mdev->sync_conf.al_extents < 7) + mdev->sync_conf.al_extents = 127; + + err: + mutex_unlock(&mdev->md_io_mutex); + put_ldev(mdev); + + return rv; +} + +/** + * drbd_md_mark_dirty() - Mark meta data super block as dirty + * @mdev: DRBD device. + * + * Call this function if you change anything that should be written to + * the meta-data super block. This function sets MD_DIRTY, and starts a + * timer that ensures that within five seconds you have to call drbd_md_sync(). + */ +void drbd_md_mark_dirty(struct drbd_conf *mdev) +{ + set_bit(MD_DIRTY, &mdev->flags); + mod_timer(&mdev->md_sync_timer, jiffies + 5*HZ); +} + + +static void drbd_uuid_move_history(struct drbd_conf *mdev) __must_hold(local) +{ + int i; + + for (i = UI_HISTORY_START; i < UI_HISTORY_END; i++) + mdev->ldev->md.uuid[i+1] = mdev->ldev->md.uuid[i]; +} + +void _drbd_uuid_set(struct drbd_conf *mdev, int idx, u64 val) __must_hold(local) +{ + if (idx == UI_CURRENT) { + if (mdev->state.role == R_PRIMARY) + val |= 1; + else + val &= ~((u64)1); + + drbd_set_ed_uuid(mdev, val); + } + + mdev->ldev->md.uuid[idx] = val; + drbd_md_mark_dirty(mdev); +} + + +void drbd_uuid_set(struct drbd_conf *mdev, int idx, u64 val) __must_hold(local) +{ + if (mdev->ldev->md.uuid[idx]) { + drbd_uuid_move_history(mdev); + mdev->ldev->md.uuid[UI_HISTORY_START] = mdev->ldev->md.uuid[idx]; + } + _drbd_uuid_set(mdev, idx, val); +} + +/** + * drbd_uuid_new_current() - Creates a new current UUID + * @mdev: DRBD device. + * + * Creates a new current UUID, and rotates the old current UUID into + * the bitmap slot. Causes an incremental resync upon next connect. + */ +void drbd_uuid_new_current(struct drbd_conf *mdev) __must_hold(local) +{ + u64 val; + + dev_info(DEV, "Creating new current UUID\n"); + D_ASSERT(mdev->ldev->md.uuid[UI_BITMAP] == 0); + mdev->ldev->md.uuid[UI_BITMAP] = mdev->ldev->md.uuid[UI_CURRENT]; + + get_random_bytes(&val, sizeof(u64)); + _drbd_uuid_set(mdev, UI_CURRENT, val); +} + +void drbd_uuid_set_bm(struct drbd_conf *mdev, u64 val) __must_hold(local) +{ + if (mdev->ldev->md.uuid[UI_BITMAP] == 0 && val == 0) + return; + + if (val == 0) { + drbd_uuid_move_history(mdev); + mdev->ldev->md.uuid[UI_HISTORY_START] = mdev->ldev->md.uuid[UI_BITMAP]; + mdev->ldev->md.uuid[UI_BITMAP] = 0; + } else { + if (mdev->ldev->md.uuid[UI_BITMAP]) + dev_warn(DEV, "bm UUID already set"); + + mdev->ldev->md.uuid[UI_BITMAP] = val; + mdev->ldev->md.uuid[UI_BITMAP] &= ~((u64)1); + + } + drbd_md_mark_dirty(mdev); +} + +/** + * drbd_bmio_set_n_write() - io_fn for drbd_queue_bitmap_io() or drbd_bitmap_io() + * @mdev: DRBD device. + * + * Sets all bits in the bitmap and writes the whole bitmap to stable storage. + */ +int drbd_bmio_set_n_write(struct drbd_conf *mdev) +{ + int rv = -EIO; + + if (get_ldev_if_state(mdev, D_ATTACHING)) { + drbd_md_set_flag(mdev, MDF_FULL_SYNC); + drbd_md_sync(mdev); + drbd_bm_set_all(mdev); + + rv = drbd_bm_write(mdev); + + if (!rv) { + drbd_md_clear_flag(mdev, MDF_FULL_SYNC); + drbd_md_sync(mdev); + } + + put_ldev(mdev); + } + + return rv; +} + +/** + * drbd_bmio_clear_n_write() - io_fn for drbd_queue_bitmap_io() or drbd_bitmap_io() + * @mdev: DRBD device. + * + * Clears all bits in the bitmap and writes the whole bitmap to stable storage. + */ +int drbd_bmio_clear_n_write(struct drbd_conf *mdev) +{ + int rv = -EIO; + + if (get_ldev_if_state(mdev, D_ATTACHING)) { + drbd_bm_clear_all(mdev); + rv = drbd_bm_write(mdev); + put_ldev(mdev); + } + + return rv; +} + +static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused) +{ + struct bm_io_work *work = container_of(w, struct bm_io_work, w); + int rv; + + D_ASSERT(atomic_read(&mdev->ap_bio_cnt) == 0); + + drbd_bm_lock(mdev, work->why); + rv = work->io_fn(mdev); + drbd_bm_unlock(mdev); + + clear_bit(BITMAP_IO, &mdev->flags); + wake_up(&mdev->misc_wait); + + if (work->done) + work->done(mdev, rv); + + clear_bit(BITMAP_IO_QUEUED, &mdev->flags); + work->why = NULL; + + return 1; +} + +/** + * drbd_queue_bitmap_io() - Queues an IO operation on the whole bitmap + * @mdev: DRBD device. + * @io_fn: IO callback to be called when bitmap IO is possible + * @done: callback to be called after the bitmap IO was performed + * @why: Descriptive text of the reason for doing the IO + * + * While IO on the bitmap happens we freeze application IO thus we ensure + * that drbd_set_out_of_sync() can not be called. This function MAY ONLY be + * called from worker context. It MUST NOT be used while a previous such + * work is still pending! + */ +void drbd_queue_bitmap_io(struct drbd_conf *mdev, + int (*io_fn)(struct drbd_conf *), + void (*done)(struct drbd_conf *, int), + char *why) +{ + D_ASSERT(current == mdev->worker.task); + + D_ASSERT(!test_bit(BITMAP_IO_QUEUED, &mdev->flags)); + D_ASSERT(!test_bit(BITMAP_IO, &mdev->flags)); + D_ASSERT(list_empty(&mdev->bm_io_work.w.list)); + if (mdev->bm_io_work.why) + dev_err(DEV, "FIXME going to queue '%s' but '%s' still pending?\n", + why, mdev->bm_io_work.why); + + mdev->bm_io_work.io_fn = io_fn; + mdev->bm_io_work.done = done; + mdev->bm_io_work.why = why; + + set_bit(BITMAP_IO, &mdev->flags); + if (atomic_read(&mdev->ap_bio_cnt) == 0) { + if (list_empty(&mdev->bm_io_work.w.list)) { + set_bit(BITMAP_IO_QUEUED, &mdev->flags); + drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w); + } else + dev_err(DEV, "FIXME avoided double queuing bm_io_work\n"); + } +} + +/** + * drbd_bitmap_io() - Does an IO operation on the whole bitmap + * @mdev: DRBD device. + * @io_fn: IO callback to be called when bitmap IO is possible + * @why: Descriptive text of the reason for doing the IO + * + * freezes application IO while that the actual IO operations runs. This + * functions MAY NOT be called from worker context. + */ +int drbd_bitmap_io(struct drbd_conf *mdev, int (*io_fn)(struct drbd_conf *), char *why) +{ + int rv; + + D_ASSERT(current != mdev->worker.task); + + drbd_suspend_io(mdev); + + drbd_bm_lock(mdev, why); + rv = io_fn(mdev); + drbd_bm_unlock(mdev); + + drbd_resume_io(mdev); + + return rv; +} + +void drbd_md_set_flag(struct drbd_conf *mdev, int flag) __must_hold(local) +{ + if ((mdev->ldev->md.flags & flag) != flag) { + drbd_md_mark_dirty(mdev); + mdev->ldev->md.flags |= flag; + } +} + +void drbd_md_clear_flag(struct drbd_conf *mdev, int flag) __must_hold(local) +{ + if ((mdev->ldev->md.flags & flag) != 0) { + drbd_md_mark_dirty(mdev); + mdev->ldev->md.flags &= ~flag; + } +} +int drbd_md_test_flag(struct drbd_backing_dev *bdev, int flag) +{ + return (bdev->md.flags & flag) != 0; +} + +static void md_sync_timer_fn(unsigned long data) +{ + struct drbd_conf *mdev = (struct drbd_conf *) data; + + drbd_queue_work_front(&mdev->data.work, &mdev->md_sync_work); +} + +static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused) +{ + dev_warn(DEV, "md_sync_timer expired! Worker calls drbd_md_sync().\n"); + drbd_md_sync(mdev); + + return 1; +} + +#ifdef CONFIG_DRBD_FAULT_INJECTION +/* Fault insertion support including random number generator shamelessly + * stolen from kernel/rcutorture.c */ +struct fault_random_state { + unsigned long state; + unsigned long count; +}; + +#define FAULT_RANDOM_MULT 39916801 /* prime */ +#define FAULT_RANDOM_ADD 479001701 /* prime */ +#define FAULT_RANDOM_REFRESH 10000 + +/* + * Crude but fast random-number generator. Uses a linear congruential + * generator, with occasional help from get_random_bytes(). + */ +static unsigned long +_drbd_fault_random(struct fault_random_state *rsp) +{ + long refresh; + + if (--rsp->count < 0) { + get_random_bytes(&refresh, sizeof(refresh)); + rsp->state += refresh; + rsp->count = FAULT_RANDOM_REFRESH; + } + rsp->state = rsp->state * FAULT_RANDOM_MULT + FAULT_RANDOM_ADD; + return swahw32(rsp->state); +} + +static char * +_drbd_fault_str(unsigned int type) { + static char *_faults[] = { + [DRBD_FAULT_MD_WR] = "Meta-data write", + [DRBD_FAULT_MD_RD] = "Meta-data read", + [DRBD_FAULT_RS_WR] = "Resync write", + [DRBD_FAULT_RS_RD] = "Resync read", + [DRBD_FAULT_DT_WR] = "Data write", + [DRBD_FAULT_DT_RD] = "Data read", + [DRBD_FAULT_DT_RA] = "Data read ahead", + [DRBD_FAULT_BM_ALLOC] = "BM allocation", + [DRBD_FAULT_AL_EE] = "EE allocation" + }; + + return (type < DRBD_FAULT_MAX) ? _faults[type] : "**Unknown**"; +} + +unsigned int +_drbd_insert_fault(struct drbd_conf *mdev, unsigned int type) +{ + static struct fault_random_state rrs = {0, 0}; + + unsigned int ret = ( + (fault_devs == 0 || + ((1 << mdev_to_minor(mdev)) & fault_devs) != 0) && + (((_drbd_fault_random(&rrs) % 100) + 1) <= fault_rate)); + + if (ret) { + fault_count++; + + if (printk_ratelimit()) + dev_warn(DEV, "***Simulating %s failure\n", + _drbd_fault_str(type)); + } + + return ret; +} +#endif + +const char *drbd_buildtag(void) +{ + /* DRBD built from external sources has here a reference to the + git hash of the source code. */ + + static char buildtag[38] = "\0uilt-in"; + + if (buildtag[0] == 0) { +#ifdef CONFIG_MODULES + if (THIS_MODULE != NULL) + sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion); + else +#endif + buildtag[0] = 'b'; + } + + return buildtag; +} + +module_init(drbd_init) +module_exit(drbd_cleanup) + +EXPORT_SYMBOL(drbd_conn_str); +EXPORT_SYMBOL(drbd_role_str); +EXPORT_SYMBOL(drbd_disk_str); +EXPORT_SYMBOL(drbd_set_st_err_str); diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c new file mode 100644 index 000000000000..436a090b532b --- /dev/null +++ b/drivers/block/drbd/drbd_nl.c @@ -0,0 +1,2364 @@ +/* + drbd_nl.c + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. + Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include <linux/module.h> +#include <linux/drbd.h> +#include <linux/in.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/slab.h> +#include <linux/connector.h> +#include <linux/blkpg.h> +#include <linux/cpumask.h> +#include "drbd_int.h" +#include "drbd_wrappers.h" +#include <asm/unaligned.h> +#include <linux/drbd_tag_magic.h> +#include <linux/drbd_limits.h> + +static unsigned short *tl_add_blob(unsigned short *, enum drbd_tags, const void *, int); +static unsigned short *tl_add_str(unsigned short *, enum drbd_tags, const char *); +static unsigned short *tl_add_int(unsigned short *, enum drbd_tags, const void *); + +/* see get_sb_bdev and bd_claim */ +static char *drbd_m_holder = "Hands off! this is DRBD's meta data device."; + +/* Generate the tag_list to struct functions */ +#define NL_PACKET(name, number, fields) \ +static int name ## _from_tags(struct drbd_conf *mdev, \ + unsigned short *tags, struct name *arg) __attribute__ ((unused)); \ +static int name ## _from_tags(struct drbd_conf *mdev, \ + unsigned short *tags, struct name *arg) \ +{ \ + int tag; \ + int dlen; \ + \ + while ((tag = get_unaligned(tags++)) != TT_END) { \ + dlen = get_unaligned(tags++); \ + switch (tag_number(tag)) { \ + fields \ + default: \ + if (tag & T_MANDATORY) { \ + dev_err(DEV, "Unknown tag: %d\n", tag_number(tag)); \ + return 0; \ + } \ + } \ + tags = (unsigned short *)((char *)tags + dlen); \ + } \ + return 1; \ +} +#define NL_INTEGER(pn, pr, member) \ + case pn: /* D_ASSERT( tag_type(tag) == TT_INTEGER ); */ \ + arg->member = get_unaligned((int *)(tags)); \ + break; +#define NL_INT64(pn, pr, member) \ + case pn: /* D_ASSERT( tag_type(tag) == TT_INT64 ); */ \ + arg->member = get_unaligned((u64 *)(tags)); \ + break; +#define NL_BIT(pn, pr, member) \ + case pn: /* D_ASSERT( tag_type(tag) == TT_BIT ); */ \ + arg->member = *(char *)(tags) ? 1 : 0; \ + break; +#define NL_STRING(pn, pr, member, len) \ + case pn: /* D_ASSERT( tag_type(tag) == TT_STRING ); */ \ + if (dlen > len) { \ + dev_err(DEV, "arg too long: %s (%u wanted, max len: %u bytes)\n", \ + #member, dlen, (unsigned int)len); \ + return 0; \ + } \ + arg->member ## _len = dlen; \ + memcpy(arg->member, tags, min_t(size_t, dlen, len)); \ + break; +#include "linux/drbd_nl.h" + +/* Generate the struct to tag_list functions */ +#define NL_PACKET(name, number, fields) \ +static unsigned short* \ +name ## _to_tags(struct drbd_conf *mdev, \ + struct name *arg, unsigned short *tags) __attribute__ ((unused)); \ +static unsigned short* \ +name ## _to_tags(struct drbd_conf *mdev, \ + struct name *arg, unsigned short *tags) \ +{ \ + fields \ + return tags; \ +} + +#define NL_INTEGER(pn, pr, member) \ + put_unaligned(pn | pr | TT_INTEGER, tags++); \ + put_unaligned(sizeof(int), tags++); \ + put_unaligned(arg->member, (int *)tags); \ + tags = (unsigned short *)((char *)tags+sizeof(int)); +#define NL_INT64(pn, pr, member) \ + put_unaligned(pn | pr | TT_INT64, tags++); \ + put_unaligned(sizeof(u64), tags++); \ + put_unaligned(arg->member, (u64 *)tags); \ + tags = (unsigned short *)((char *)tags+sizeof(u64)); +#define NL_BIT(pn, pr, member) \ + put_unaligned(pn | pr | TT_BIT, tags++); \ + put_unaligned(sizeof(char), tags++); \ + *(char *)tags = arg->member; \ + tags = (unsigned short *)((char *)tags+sizeof(char)); +#define NL_STRING(pn, pr, member, len) \ + put_unaligned(pn | pr | TT_STRING, tags++); \ + put_unaligned(arg->member ## _len, tags++); \ + memcpy(tags, arg->member, arg->member ## _len); \ + tags = (unsigned short *)((char *)tags + arg->member ## _len); +#include "linux/drbd_nl.h" + +void drbd_bcast_ev_helper(struct drbd_conf *mdev, char *helper_name); +void drbd_nl_send_reply(struct cn_msg *, int); + +int drbd_khelper(struct drbd_conf *mdev, char *cmd) +{ + char *envp[] = { "HOME=/", + "TERM=linux", + "PATH=/sbin:/usr/sbin:/bin:/usr/bin", + NULL, /* Will be set to address family */ + NULL, /* Will be set to address */ + NULL }; + + char mb[12], af[20], ad[60], *afs; + char *argv[] = {usermode_helper, cmd, mb, NULL }; + int ret; + + snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev)); + + if (get_net_conf(mdev)) { + switch (((struct sockaddr *)mdev->net_conf->peer_addr)->sa_family) { + case AF_INET6: + afs = "ipv6"; + snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI6", + &((struct sockaddr_in6 *)mdev->net_conf->peer_addr)->sin6_addr); + break; + case AF_INET: + afs = "ipv4"; + snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI4", + &((struct sockaddr_in *)mdev->net_conf->peer_addr)->sin_addr); + break; + default: + afs = "ssocks"; + snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI4", + &((struct sockaddr_in *)mdev->net_conf->peer_addr)->sin_addr); + } + snprintf(af, 20, "DRBD_PEER_AF=%s", afs); + envp[3]=af; + envp[4]=ad; + put_net_conf(mdev); + } + + dev_info(DEV, "helper command: %s %s %s\n", usermode_helper, cmd, mb); + + drbd_bcast_ev_helper(mdev, cmd); + ret = call_usermodehelper(usermode_helper, argv, envp, 1); + if (ret) + dev_warn(DEV, "helper command: %s %s %s exit code %u (0x%x)\n", + usermode_helper, cmd, mb, + (ret >> 8) & 0xff, ret); + else + dev_info(DEV, "helper command: %s %s %s exit code %u (0x%x)\n", + usermode_helper, cmd, mb, + (ret >> 8) & 0xff, ret); + + if (ret < 0) /* Ignore any ERRNOs we got. */ + ret = 0; + + return ret; +} + +enum drbd_disk_state drbd_try_outdate_peer(struct drbd_conf *mdev) +{ + char *ex_to_string; + int r; + enum drbd_disk_state nps; + enum drbd_fencing_p fp; + + D_ASSERT(mdev->state.pdsk == D_UNKNOWN); + + if (get_ldev_if_state(mdev, D_CONSISTENT)) { + fp = mdev->ldev->dc.fencing; + put_ldev(mdev); + } else { + dev_warn(DEV, "Not fencing peer, I'm not even Consistent myself.\n"); + return mdev->state.pdsk; + } + + if (fp == FP_STONITH) + _drbd_request_state(mdev, NS(susp, 1), CS_WAIT_COMPLETE); + + r = drbd_khelper(mdev, "fence-peer"); + + switch ((r>>8) & 0xff) { + case 3: /* peer is inconsistent */ + ex_to_string = "peer is inconsistent or worse"; + nps = D_INCONSISTENT; + break; + case 4: /* peer got outdated, or was already outdated */ + ex_to_string = "peer was fenced"; + nps = D_OUTDATED; + break; + case 5: /* peer was down */ + if (mdev->state.disk == D_UP_TO_DATE) { + /* we will(have) create(d) a new UUID anyways... */ + ex_to_string = "peer is unreachable, assumed to be dead"; + nps = D_OUTDATED; + } else { + ex_to_string = "peer unreachable, doing nothing since disk != UpToDate"; + nps = mdev->state.pdsk; + } + break; + case 6: /* Peer is primary, voluntarily outdate myself. + * This is useful when an unconnected R_SECONDARY is asked to + * become R_PRIMARY, but finds the other peer being active. */ + ex_to_string = "peer is active"; + dev_warn(DEV, "Peer is primary, outdating myself.\n"); + nps = D_UNKNOWN; + _drbd_request_state(mdev, NS(disk, D_OUTDATED), CS_WAIT_COMPLETE); + break; + case 7: + if (fp != FP_STONITH) + dev_err(DEV, "fence-peer() = 7 && fencing != Stonith !!!\n"); + ex_to_string = "peer was stonithed"; + nps = D_OUTDATED; + break; + default: + /* The script is broken ... */ + nps = D_UNKNOWN; + dev_err(DEV, "fence-peer helper broken, returned %d\n", (r>>8)&0xff); + return nps; + } + + dev_info(DEV, "fence-peer helper returned %d (%s)\n", + (r>>8) & 0xff, ex_to_string); + return nps; +} + + +int drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force) +{ + const int max_tries = 4; + int r = 0; + int try = 0; + int forced = 0; + union drbd_state mask, val; + enum drbd_disk_state nps; + + if (new_role == R_PRIMARY) + request_ping(mdev); /* Detect a dead peer ASAP */ + + mutex_lock(&mdev->state_mutex); + + mask.i = 0; mask.role = R_MASK; + val.i = 0; val.role = new_role; + + while (try++ < max_tries) { + r = _drbd_request_state(mdev, mask, val, CS_WAIT_COMPLETE); + + /* in case we first succeeded to outdate, + * but now suddenly could establish a connection */ + if (r == SS_CW_FAILED_BY_PEER && mask.pdsk != 0) { + val.pdsk = 0; + mask.pdsk = 0; + continue; + } + + if (r == SS_NO_UP_TO_DATE_DISK && force && + (mdev->state.disk == D_INCONSISTENT || + mdev->state.disk == D_OUTDATED)) { + mask.disk = D_MASK; + val.disk = D_UP_TO_DATE; + forced = 1; + continue; + } + + if (r == SS_NO_UP_TO_DATE_DISK && + mdev->state.disk == D_CONSISTENT && mask.pdsk == 0) { + D_ASSERT(mdev->state.pdsk == D_UNKNOWN); + nps = drbd_try_outdate_peer(mdev); + + if (nps == D_OUTDATED || nps == D_INCONSISTENT) { + val.disk = D_UP_TO_DATE; + mask.disk = D_MASK; + } + + val.pdsk = nps; + mask.pdsk = D_MASK; + + continue; + } + + if (r == SS_NOTHING_TO_DO) + goto fail; + if (r == SS_PRIMARY_NOP && mask.pdsk == 0) { + nps = drbd_try_outdate_peer(mdev); + + if (force && nps > D_OUTDATED) { + dev_warn(DEV, "Forced into split brain situation!\n"); + nps = D_OUTDATED; + } + + mask.pdsk = D_MASK; + val.pdsk = nps; + + continue; + } + if (r == SS_TWO_PRIMARIES) { + /* Maybe the peer is detected as dead very soon... + retry at most once more in this case. */ + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((mdev->net_conf->ping_timeo+1)*HZ/10); + if (try < max_tries) + try = max_tries - 1; + continue; + } + if (r < SS_SUCCESS) { + r = _drbd_request_state(mdev, mask, val, + CS_VERBOSE + CS_WAIT_COMPLETE); + if (r < SS_SUCCESS) + goto fail; + } + break; + } + + if (r < SS_SUCCESS) + goto fail; + + if (forced) + dev_warn(DEV, "Forced to consider local data as UpToDate!\n"); + + /* Wait until nothing is on the fly :) */ + wait_event(mdev->misc_wait, atomic_read(&mdev->ap_pending_cnt) == 0); + + if (new_role == R_SECONDARY) { + set_disk_ro(mdev->vdisk, TRUE); + if (get_ldev(mdev)) { + mdev->ldev->md.uuid[UI_CURRENT] &= ~(u64)1; + put_ldev(mdev); + } + } else { + if (get_net_conf(mdev)) { + mdev->net_conf->want_lose = 0; + put_net_conf(mdev); + } + set_disk_ro(mdev->vdisk, FALSE); + if (get_ldev(mdev)) { + if (((mdev->state.conn < C_CONNECTED || + mdev->state.pdsk <= D_FAILED) + && mdev->ldev->md.uuid[UI_BITMAP] == 0) || forced) + drbd_uuid_new_current(mdev); + + mdev->ldev->md.uuid[UI_CURRENT] |= (u64)1; + put_ldev(mdev); + } + } + + if ((new_role == R_SECONDARY) && get_ldev(mdev)) { + drbd_al_to_on_disk_bm(mdev); + put_ldev(mdev); + } + + if (mdev->state.conn >= C_WF_REPORT_PARAMS) { + /* if this was forced, we should consider sync */ + if (forced) + drbd_send_uuids(mdev); + drbd_send_state(mdev); + } + + drbd_md_sync(mdev); + + kobject_uevent(&disk_to_dev(mdev->vdisk)->kobj, KOBJ_CHANGE); + fail: + mutex_unlock(&mdev->state_mutex); + return r; +} + + +static int drbd_nl_primary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + struct primary primary_args; + + memset(&primary_args, 0, sizeof(struct primary)); + if (!primary_from_tags(mdev, nlp->tag_list, &primary_args)) { + reply->ret_code = ERR_MANDATORY_TAG; + return 0; + } + + reply->ret_code = + drbd_set_role(mdev, R_PRIMARY, primary_args.overwrite_peer); + + return 0; +} + +static int drbd_nl_secondary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + reply->ret_code = drbd_set_role(mdev, R_SECONDARY, 0); + + return 0; +} + +/* initializes the md.*_offset members, so we are able to find + * the on disk meta data */ +static void drbd_md_set_sector_offsets(struct drbd_conf *mdev, + struct drbd_backing_dev *bdev) +{ + sector_t md_size_sect = 0; + switch (bdev->dc.meta_dev_idx) { + default: + /* v07 style fixed size indexed meta data */ + bdev->md.md_size_sect = MD_RESERVED_SECT; + bdev->md.md_offset = drbd_md_ss__(mdev, bdev); + bdev->md.al_offset = MD_AL_OFFSET; + bdev->md.bm_offset = MD_BM_OFFSET; + break; + case DRBD_MD_INDEX_FLEX_EXT: + /* just occupy the full device; unit: sectors */ + bdev->md.md_size_sect = drbd_get_capacity(bdev->md_bdev); + bdev->md.md_offset = 0; + bdev->md.al_offset = MD_AL_OFFSET; + bdev->md.bm_offset = MD_BM_OFFSET; + break; + case DRBD_MD_INDEX_INTERNAL: + case DRBD_MD_INDEX_FLEX_INT: + bdev->md.md_offset = drbd_md_ss__(mdev, bdev); + /* al size is still fixed */ + bdev->md.al_offset = -MD_AL_MAX_SIZE; + /* we need (slightly less than) ~ this much bitmap sectors: */ + md_size_sect = drbd_get_capacity(bdev->backing_bdev); + md_size_sect = ALIGN(md_size_sect, BM_SECT_PER_EXT); + md_size_sect = BM_SECT_TO_EXT(md_size_sect); + md_size_sect = ALIGN(md_size_sect, 8); + + /* plus the "drbd meta data super block", + * and the activity log; */ + md_size_sect += MD_BM_OFFSET; + + bdev->md.md_size_sect = md_size_sect; + /* bitmap offset is adjusted by 'super' block size */ + bdev->md.bm_offset = -md_size_sect + MD_AL_OFFSET; + break; + } +} + +char *ppsize(char *buf, unsigned long long size) +{ + /* Needs 9 bytes at max. */ + static char units[] = { 'K', 'M', 'G', 'T', 'P', 'E' }; + int base = 0; + while (size >= 10000) { + /* shift + round */ + size = (size >> 10) + !!(size & (1<<9)); + base++; + } + sprintf(buf, "%lu %cB", (long)size, units[base]); + + return buf; +} + +/* there is still a theoretical deadlock when called from receiver + * on an D_INCONSISTENT R_PRIMARY: + * remote READ does inc_ap_bio, receiver would need to receive answer + * packet from remote to dec_ap_bio again. + * receiver receive_sizes(), comes here, + * waits for ap_bio_cnt == 0. -> deadlock. + * but this cannot happen, actually, because: + * R_PRIMARY D_INCONSISTENT, and peer's disk is unreachable + * (not connected, or bad/no disk on peer): + * see drbd_fail_request_early, ap_bio_cnt is zero. + * R_PRIMARY D_INCONSISTENT, and C_SYNC_TARGET: + * peer may not initiate a resize. + */ +void drbd_suspend_io(struct drbd_conf *mdev) +{ + set_bit(SUSPEND_IO, &mdev->flags); + wait_event(mdev->misc_wait, !atomic_read(&mdev->ap_bio_cnt)); +} + +void drbd_resume_io(struct drbd_conf *mdev) +{ + clear_bit(SUSPEND_IO, &mdev->flags); + wake_up(&mdev->misc_wait); +} + +/** + * drbd_determine_dev_size() - Sets the right device size obeying all constraints + * @mdev: DRBD device. + * + * Returns 0 on success, negative return values indicate errors. + * You should call drbd_md_sync() after calling this function. + */ +enum determine_dev_size drbd_determin_dev_size(struct drbd_conf *mdev) __must_hold(local) +{ + sector_t prev_first_sect, prev_size; /* previous meta location */ + sector_t la_size; + sector_t size; + char ppb[10]; + + int md_moved, la_size_changed; + enum determine_dev_size rv = unchanged; + + /* race: + * application request passes inc_ap_bio, + * but then cannot get an AL-reference. + * this function later may wait on ap_bio_cnt == 0. -> deadlock. + * + * to avoid that: + * Suspend IO right here. + * still lock the act_log to not trigger ASSERTs there. + */ + drbd_suspend_io(mdev); + + /* no wait necessary anymore, actually we could assert that */ + wait_event(mdev->al_wait, lc_try_lock(mdev->act_log)); + + prev_first_sect = drbd_md_first_sector(mdev->ldev); + prev_size = mdev->ldev->md.md_size_sect; + la_size = mdev->ldev->md.la_size_sect; + + /* TODO: should only be some assert here, not (re)init... */ + drbd_md_set_sector_offsets(mdev, mdev->ldev); + + size = drbd_new_dev_size(mdev, mdev->ldev); + + if (drbd_get_capacity(mdev->this_bdev) != size || + drbd_bm_capacity(mdev) != size) { + int err; + err = drbd_bm_resize(mdev, size); + if (unlikely(err)) { + /* currently there is only one error: ENOMEM! */ + size = drbd_bm_capacity(mdev)>>1; + if (size == 0) { + dev_err(DEV, "OUT OF MEMORY! " + "Could not allocate bitmap!\n"); + } else { + dev_err(DEV, "BM resizing failed. " + "Leaving size unchanged at size = %lu KB\n", + (unsigned long)size); + } + rv = dev_size_error; + } + /* racy, see comments above. */ + drbd_set_my_capacity(mdev, size); + mdev->ldev->md.la_size_sect = size; + dev_info(DEV, "size = %s (%llu KB)\n", ppsize(ppb, size>>1), + (unsigned long long)size>>1); + } + if (rv == dev_size_error) + goto out; + + la_size_changed = (la_size != mdev->ldev->md.la_size_sect); + + md_moved = prev_first_sect != drbd_md_first_sector(mdev->ldev) + || prev_size != mdev->ldev->md.md_size_sect; + + if (la_size_changed || md_moved) { + drbd_al_shrink(mdev); /* All extents inactive. */ + dev_info(DEV, "Writing the whole bitmap, %s\n", + la_size_changed && md_moved ? "size changed and md moved" : + la_size_changed ? "size changed" : "md moved"); + rv = drbd_bitmap_io(mdev, &drbd_bm_write, "size changed"); /* does drbd_resume_io() ! */ + drbd_md_mark_dirty(mdev); + } + + if (size > la_size) + rv = grew; + if (size < la_size) + rv = shrunk; +out: + lc_unlock(mdev->act_log); + wake_up(&mdev->al_wait); + drbd_resume_io(mdev); + + return rv; +} + +sector_t +drbd_new_dev_size(struct drbd_conf *mdev, struct drbd_backing_dev *bdev) +{ + sector_t p_size = mdev->p_size; /* partner's disk size. */ + sector_t la_size = bdev->md.la_size_sect; /* last agreed size. */ + sector_t m_size; /* my size */ + sector_t u_size = bdev->dc.disk_size; /* size requested by user. */ + sector_t size = 0; + + m_size = drbd_get_max_capacity(bdev); + + if (p_size && m_size) { + size = min_t(sector_t, p_size, m_size); + } else { + if (la_size) { + size = la_size; + if (m_size && m_size < size) + size = m_size; + if (p_size && p_size < size) + size = p_size; + } else { + if (m_size) + size = m_size; + if (p_size) + size = p_size; + } + } + + if (size == 0) + dev_err(DEV, "Both nodes diskless!\n"); + + if (u_size) { + if (u_size > size) + dev_err(DEV, "Requested disk size is too big (%lu > %lu)\n", + (unsigned long)u_size>>1, (unsigned long)size>>1); + else + size = u_size; + } + + return size; +} + +/** + * drbd_check_al_size() - Ensures that the AL is of the right size + * @mdev: DRBD device. + * + * Returns -EBUSY if current al lru is still used, -ENOMEM when allocation + * failed, and 0 on success. You should call drbd_md_sync() after you called + * this function. + */ +static int drbd_check_al_size(struct drbd_conf *mdev) +{ + struct lru_cache *n, *t; + struct lc_element *e; + unsigned int in_use; + int i; + + ERR_IF(mdev->sync_conf.al_extents < 7) + mdev->sync_conf.al_extents = 127; + + if (mdev->act_log && + mdev->act_log->nr_elements == mdev->sync_conf.al_extents) + return 0; + + in_use = 0; + t = mdev->act_log; + n = lc_create("act_log", drbd_al_ext_cache, + mdev->sync_conf.al_extents, sizeof(struct lc_element), 0); + + if (n == NULL) { + dev_err(DEV, "Cannot allocate act_log lru!\n"); + return -ENOMEM; + } + spin_lock_irq(&mdev->al_lock); + if (t) { + for (i = 0; i < t->nr_elements; i++) { + e = lc_element_by_index(t, i); + if (e->refcnt) + dev_err(DEV, "refcnt(%d)==%d\n", + e->lc_number, e->refcnt); + in_use += e->refcnt; + } + } + if (!in_use) + mdev->act_log = n; + spin_unlock_irq(&mdev->al_lock); + if (in_use) { + dev_err(DEV, "Activity log still in use!\n"); + lc_destroy(n); + return -EBUSY; + } else { + if (t) + lc_destroy(t); + } + drbd_md_mark_dirty(mdev); /* we changed mdev->act_log->nr_elemens */ + return 0; +} + +void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_seg_s) __must_hold(local) +{ + struct request_queue * const q = mdev->rq_queue; + struct request_queue * const b = mdev->ldev->backing_bdev->bd_disk->queue; + int max_segments = mdev->ldev->dc.max_bio_bvecs; + + if (b->merge_bvec_fn && !mdev->ldev->dc.use_bmbv) + max_seg_s = PAGE_SIZE; + + max_seg_s = min(queue_max_sectors(b) * queue_logical_block_size(b), max_seg_s); + + blk_queue_max_sectors(q, max_seg_s >> 9); + blk_queue_max_phys_segments(q, max_segments ? max_segments : MAX_PHYS_SEGMENTS); + blk_queue_max_hw_segments(q, max_segments ? max_segments : MAX_HW_SEGMENTS); + blk_queue_max_segment_size(q, max_seg_s); + blk_queue_logical_block_size(q, 512); + blk_queue_segment_boundary(q, PAGE_SIZE-1); + blk_stack_limits(&q->limits, &b->limits, 0); + + if (b->merge_bvec_fn) + dev_warn(DEV, "Backing device's merge_bvec_fn() = %p\n", + b->merge_bvec_fn); + dev_info(DEV, "max_segment_size ( = BIO size ) = %u\n", queue_max_segment_size(q)); + + if (q->backing_dev_info.ra_pages != b->backing_dev_info.ra_pages) { + dev_info(DEV, "Adjusting my ra_pages to backing device's (%lu -> %lu)\n", + q->backing_dev_info.ra_pages, + b->backing_dev_info.ra_pages); + q->backing_dev_info.ra_pages = b->backing_dev_info.ra_pages; + } +} + +/* serialize deconfig (worker exiting, doing cleanup) + * and reconfig (drbdsetup disk, drbdsetup net) + * + * wait for a potentially exiting worker, then restart it, + * or start a new one. + */ +static void drbd_reconfig_start(struct drbd_conf *mdev) +{ + wait_event(mdev->state_wait, !test_and_set_bit(CONFIG_PENDING, &mdev->flags)); + wait_event(mdev->state_wait, !test_bit(DEVICE_DYING, &mdev->flags)); + drbd_thread_start(&mdev->worker); +} + +/* if still unconfigured, stops worker again. + * if configured now, clears CONFIG_PENDING. + * wakes potential waiters */ +static void drbd_reconfig_done(struct drbd_conf *mdev) +{ + spin_lock_irq(&mdev->req_lock); + if (mdev->state.disk == D_DISKLESS && + mdev->state.conn == C_STANDALONE && + mdev->state.role == R_SECONDARY) { + set_bit(DEVICE_DYING, &mdev->flags); + drbd_thread_stop_nowait(&mdev->worker); + } else + clear_bit(CONFIG_PENDING, &mdev->flags); + spin_unlock_irq(&mdev->req_lock); + wake_up(&mdev->state_wait); +} + +/* does always return 0; + * interesting return code is in reply->ret_code */ +static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + enum drbd_ret_codes retcode; + enum determine_dev_size dd; + sector_t max_possible_sectors; + sector_t min_md_device_sectors; + struct drbd_backing_dev *nbc = NULL; /* new_backing_conf */ + struct inode *inode, *inode2; + struct lru_cache *resync_lru = NULL; + union drbd_state ns, os; + int rv; + int cp_discovered = 0; + int logical_block_size; + + drbd_reconfig_start(mdev); + + /* if you want to reconfigure, please tear down first */ + if (mdev->state.disk > D_DISKLESS) { + retcode = ERR_DISK_CONFIGURED; + goto fail; + } + + /* allocation not in the IO path, cqueue thread context */ + nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL); + if (!nbc) { + retcode = ERR_NOMEM; + goto fail; + } + + nbc->dc.disk_size = DRBD_DISK_SIZE_SECT_DEF; + nbc->dc.on_io_error = DRBD_ON_IO_ERROR_DEF; + nbc->dc.fencing = DRBD_FENCING_DEF; + nbc->dc.max_bio_bvecs = DRBD_MAX_BIO_BVECS_DEF; + + if (!disk_conf_from_tags(mdev, nlp->tag_list, &nbc->dc)) { + retcode = ERR_MANDATORY_TAG; + goto fail; + } + + if (nbc->dc.meta_dev_idx < DRBD_MD_INDEX_FLEX_INT) { + retcode = ERR_MD_IDX_INVALID; + goto fail; + } + + nbc->lo_file = filp_open(nbc->dc.backing_dev, O_RDWR, 0); + if (IS_ERR(nbc->lo_file)) { + dev_err(DEV, "open(\"%s\") failed with %ld\n", nbc->dc.backing_dev, + PTR_ERR(nbc->lo_file)); + nbc->lo_file = NULL; + retcode = ERR_OPEN_DISK; + goto fail; + } + + inode = nbc->lo_file->f_dentry->d_inode; + + if (!S_ISBLK(inode->i_mode)) { + retcode = ERR_DISK_NOT_BDEV; + goto fail; + } + + nbc->md_file = filp_open(nbc->dc.meta_dev, O_RDWR, 0); + if (IS_ERR(nbc->md_file)) { + dev_err(DEV, "open(\"%s\") failed with %ld\n", nbc->dc.meta_dev, + PTR_ERR(nbc->md_file)); + nbc->md_file = NULL; + retcode = ERR_OPEN_MD_DISK; + goto fail; + } + + inode2 = nbc->md_file->f_dentry->d_inode; + + if (!S_ISBLK(inode2->i_mode)) { + retcode = ERR_MD_NOT_BDEV; + goto fail; + } + + nbc->backing_bdev = inode->i_bdev; + if (bd_claim(nbc->backing_bdev, mdev)) { + printk(KERN_ERR "drbd: bd_claim(%p,%p); failed [%p;%p;%u]\n", + nbc->backing_bdev, mdev, + nbc->backing_bdev->bd_holder, + nbc->backing_bdev->bd_contains->bd_holder, + nbc->backing_bdev->bd_holders); + retcode = ERR_BDCLAIM_DISK; + goto fail; + } + + resync_lru = lc_create("resync", drbd_bm_ext_cache, + 61, sizeof(struct bm_extent), + offsetof(struct bm_extent, lce)); + if (!resync_lru) { + retcode = ERR_NOMEM; + goto release_bdev_fail; + } + + /* meta_dev_idx >= 0: external fixed size, + * possibly multiple drbd sharing one meta device. + * TODO in that case, paranoia check that [md_bdev, meta_dev_idx] is + * not yet used by some other drbd minor! + * (if you use drbd.conf + drbdadm, + * that should check it for you already; but if you don't, or someone + * fooled it, we need to double check here) */ + nbc->md_bdev = inode2->i_bdev; + if (bd_claim(nbc->md_bdev, (nbc->dc.meta_dev_idx < 0) ? (void *)mdev + : (void *) drbd_m_holder)) { + retcode = ERR_BDCLAIM_MD_DISK; + goto release_bdev_fail; + } + + if ((nbc->backing_bdev == nbc->md_bdev) != + (nbc->dc.meta_dev_idx == DRBD_MD_INDEX_INTERNAL || + nbc->dc.meta_dev_idx == DRBD_MD_INDEX_FLEX_INT)) { + retcode = ERR_MD_IDX_INVALID; + goto release_bdev2_fail; + } + + /* RT - for drbd_get_max_capacity() DRBD_MD_INDEX_FLEX_INT */ + drbd_md_set_sector_offsets(mdev, nbc); + + if (drbd_get_max_capacity(nbc) < nbc->dc.disk_size) { + dev_err(DEV, "max capacity %llu smaller than disk size %llu\n", + (unsigned long long) drbd_get_max_capacity(nbc), + (unsigned long long) nbc->dc.disk_size); + retcode = ERR_DISK_TO_SMALL; + goto release_bdev2_fail; + } + + if (nbc->dc.meta_dev_idx < 0) { + max_possible_sectors = DRBD_MAX_SECTORS_FLEX; + /* at least one MB, otherwise it does not make sense */ + min_md_device_sectors = (2<<10); + } else { + max_possible_sectors = DRBD_MAX_SECTORS; + min_md_device_sectors = MD_RESERVED_SECT * (nbc->dc.meta_dev_idx + 1); + } + + if (drbd_get_capacity(nbc->md_bdev) < min_md_device_sectors) { + retcode = ERR_MD_DISK_TO_SMALL; + dev_warn(DEV, "refusing attach: md-device too small, " + "at least %llu sectors needed for this meta-disk type\n", + (unsigned long long) min_md_device_sectors); + goto release_bdev2_fail; + } + + /* Make sure the new disk is big enough + * (we may currently be R_PRIMARY with no local disk...) */ + if (drbd_get_max_capacity(nbc) < + drbd_get_capacity(mdev->this_bdev)) { + retcode = ERR_DISK_TO_SMALL; + goto release_bdev2_fail; + } + + nbc->known_size = drbd_get_capacity(nbc->backing_bdev); + + if (nbc->known_size > max_possible_sectors) { + dev_warn(DEV, "==> truncating very big lower level device " + "to currently maximum possible %llu sectors <==\n", + (unsigned long long) max_possible_sectors); + if (nbc->dc.meta_dev_idx >= 0) + dev_warn(DEV, "==>> using internal or flexible " + "meta data may help <<==\n"); + } + + drbd_suspend_io(mdev); + /* also wait for the last barrier ack. */ + wait_event(mdev->misc_wait, !atomic_read(&mdev->ap_pending_cnt)); + /* and for any other previously queued work */ + drbd_flush_workqueue(mdev); + + retcode = _drbd_request_state(mdev, NS(disk, D_ATTACHING), CS_VERBOSE); + drbd_resume_io(mdev); + if (retcode < SS_SUCCESS) + goto release_bdev2_fail; + + if (!get_ldev_if_state(mdev, D_ATTACHING)) + goto force_diskless; + + drbd_md_set_sector_offsets(mdev, nbc); + + if (!mdev->bitmap) { + if (drbd_bm_init(mdev)) { + retcode = ERR_NOMEM; + goto force_diskless_dec; + } + } + + retcode = drbd_md_read(mdev, nbc); + if (retcode != NO_ERROR) + goto force_diskless_dec; + + if (mdev->state.conn < C_CONNECTED && + mdev->state.role == R_PRIMARY && + (mdev->ed_uuid & ~((u64)1)) != (nbc->md.uuid[UI_CURRENT] & ~((u64)1))) { + dev_err(DEV, "Can only attach to data with current UUID=%016llX\n", + (unsigned long long)mdev->ed_uuid); + retcode = ERR_DATA_NOT_CURRENT; + goto force_diskless_dec; + } + + /* Since we are diskless, fix the activity log first... */ + if (drbd_check_al_size(mdev)) { + retcode = ERR_NOMEM; + goto force_diskless_dec; + } + + /* Prevent shrinking of consistent devices ! */ + if (drbd_md_test_flag(nbc, MDF_CONSISTENT) && + drbd_new_dev_size(mdev, nbc) < nbc->md.la_size_sect) { + dev_warn(DEV, "refusing to truncate a consistent device\n"); + retcode = ERR_DISK_TO_SMALL; + goto force_diskless_dec; + } + + if (!drbd_al_read_log(mdev, nbc)) { + retcode = ERR_IO_MD_DISK; + goto force_diskless_dec; + } + + /* allocate a second IO page if logical_block_size != 512 */ + logical_block_size = bdev_logical_block_size(nbc->md_bdev); + if (logical_block_size == 0) + logical_block_size = MD_SECTOR_SIZE; + + if (logical_block_size != MD_SECTOR_SIZE) { + if (!mdev->md_io_tmpp) { + struct page *page = alloc_page(GFP_NOIO); + if (!page) + goto force_diskless_dec; + + dev_warn(DEV, "Meta data's bdev logical_block_size = %d != %d\n", + logical_block_size, MD_SECTOR_SIZE); + dev_warn(DEV, "Workaround engaged (has performance impact).\n"); + + mdev->md_io_tmpp = page; + } + } + + /* Reset the "barriers don't work" bits here, then force meta data to + * be written, to ensure we determine if barriers are supported. */ + if (nbc->dc.no_md_flush) + set_bit(MD_NO_BARRIER, &mdev->flags); + else + clear_bit(MD_NO_BARRIER, &mdev->flags); + + /* Point of no return reached. + * Devices and memory are no longer released by error cleanup below. + * now mdev takes over responsibility, and the state engine should + * clean it up somewhere. */ + D_ASSERT(mdev->ldev == NULL); + mdev->ldev = nbc; + mdev->resync = resync_lru; + nbc = NULL; + resync_lru = NULL; + + mdev->write_ordering = WO_bio_barrier; + drbd_bump_write_ordering(mdev, WO_bio_barrier); + + if (drbd_md_test_flag(mdev->ldev, MDF_CRASHED_PRIMARY)) + set_bit(CRASHED_PRIMARY, &mdev->flags); + else + clear_bit(CRASHED_PRIMARY, &mdev->flags); + + if (drbd_md_test_flag(mdev->ldev, MDF_PRIMARY_IND)) { + set_bit(CRASHED_PRIMARY, &mdev->flags); + cp_discovered = 1; + } + + mdev->send_cnt = 0; + mdev->recv_cnt = 0; + mdev->read_cnt = 0; + mdev->writ_cnt = 0; + + drbd_setup_queue_param(mdev, DRBD_MAX_SEGMENT_SIZE); + + /* If I am currently not R_PRIMARY, + * but meta data primary indicator is set, + * I just now recover from a hard crash, + * and have been R_PRIMARY before that crash. + * + * Now, if I had no connection before that crash + * (have been degraded R_PRIMARY), chances are that + * I won't find my peer now either. + * + * In that case, and _only_ in that case, + * we use the degr-wfc-timeout instead of the default, + * so we can automatically recover from a crash of a + * degraded but active "cluster" after a certain timeout. + */ + clear_bit(USE_DEGR_WFC_T, &mdev->flags); + if (mdev->state.role != R_PRIMARY && + drbd_md_test_flag(mdev->ldev, MDF_PRIMARY_IND) && + !drbd_md_test_flag(mdev->ldev, MDF_CONNECTED_IND)) + set_bit(USE_DEGR_WFC_T, &mdev->flags); + + dd = drbd_determin_dev_size(mdev); + if (dd == dev_size_error) { + retcode = ERR_NOMEM_BITMAP; + goto force_diskless_dec; + } else if (dd == grew) + set_bit(RESYNC_AFTER_NEG, &mdev->flags); + + if (drbd_md_test_flag(mdev->ldev, MDF_FULL_SYNC)) { + dev_info(DEV, "Assuming that all blocks are out of sync " + "(aka FullSync)\n"); + if (drbd_bitmap_io(mdev, &drbd_bmio_set_n_write, "set_n_write from attaching")) { + retcode = ERR_IO_MD_DISK; + goto force_diskless_dec; + } + } else { + if (drbd_bitmap_io(mdev, &drbd_bm_read, "read from attaching") < 0) { + retcode = ERR_IO_MD_DISK; + goto force_diskless_dec; + } + } + + if (cp_discovered) { + drbd_al_apply_to_bm(mdev); + drbd_al_to_on_disk_bm(mdev); + } + + spin_lock_irq(&mdev->req_lock); + os = mdev->state; + ns.i = os.i; + /* If MDF_CONSISTENT is not set go into inconsistent state, + otherwise investigate MDF_WasUpToDate... + If MDF_WAS_UP_TO_DATE is not set go into D_OUTDATED disk state, + otherwise into D_CONSISTENT state. + */ + if (drbd_md_test_flag(mdev->ldev, MDF_CONSISTENT)) { + if (drbd_md_test_flag(mdev->ldev, MDF_WAS_UP_TO_DATE)) + ns.disk = D_CONSISTENT; + else + ns.disk = D_OUTDATED; + } else { + ns.disk = D_INCONSISTENT; + } + + if (drbd_md_test_flag(mdev->ldev, MDF_PEER_OUT_DATED)) + ns.pdsk = D_OUTDATED; + + if ( ns.disk == D_CONSISTENT && + (ns.pdsk == D_OUTDATED || mdev->ldev->dc.fencing == FP_DONT_CARE)) + ns.disk = D_UP_TO_DATE; + + /* All tests on MDF_PRIMARY_IND, MDF_CONNECTED_IND, + MDF_CONSISTENT and MDF_WAS_UP_TO_DATE must happen before + this point, because drbd_request_state() modifies these + flags. */ + + /* In case we are C_CONNECTED postpone any decision on the new disk + state after the negotiation phase. */ + if (mdev->state.conn == C_CONNECTED) { + mdev->new_state_tmp.i = ns.i; + ns.i = os.i; + ns.disk = D_NEGOTIATING; + } + + rv = _drbd_set_state(mdev, ns, CS_VERBOSE, NULL); + ns = mdev->state; + spin_unlock_irq(&mdev->req_lock); + + if (rv < SS_SUCCESS) + goto force_diskless_dec; + + if (mdev->state.role == R_PRIMARY) + mdev->ldev->md.uuid[UI_CURRENT] |= (u64)1; + else + mdev->ldev->md.uuid[UI_CURRENT] &= ~(u64)1; + + drbd_md_mark_dirty(mdev); + drbd_md_sync(mdev); + + kobject_uevent(&disk_to_dev(mdev->vdisk)->kobj, KOBJ_CHANGE); + put_ldev(mdev); + reply->ret_code = retcode; + drbd_reconfig_done(mdev); + return 0; + + force_diskless_dec: + put_ldev(mdev); + force_diskless: + drbd_force_state(mdev, NS(disk, D_DISKLESS)); + drbd_md_sync(mdev); + release_bdev2_fail: + if (nbc) + bd_release(nbc->md_bdev); + release_bdev_fail: + if (nbc) + bd_release(nbc->backing_bdev); + fail: + if (nbc) { + if (nbc->lo_file) + fput(nbc->lo_file); + if (nbc->md_file) + fput(nbc->md_file); + kfree(nbc); + } + lc_destroy(resync_lru); + + reply->ret_code = retcode; + drbd_reconfig_done(mdev); + return 0; +} + +static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + reply->ret_code = drbd_request_state(mdev, NS(disk, D_DISKLESS)); + return 0; +} + +static int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + int i, ns; + enum drbd_ret_codes retcode; + struct net_conf *new_conf = NULL; + struct crypto_hash *tfm = NULL; + struct crypto_hash *integrity_w_tfm = NULL; + struct crypto_hash *integrity_r_tfm = NULL; + struct hlist_head *new_tl_hash = NULL; + struct hlist_head *new_ee_hash = NULL; + struct drbd_conf *odev; + char hmac_name[CRYPTO_MAX_ALG_NAME]; + void *int_dig_out = NULL; + void *int_dig_in = NULL; + void *int_dig_vv = NULL; + struct sockaddr *new_my_addr, *new_peer_addr, *taken_addr; + + drbd_reconfig_start(mdev); + + if (mdev->state.conn > C_STANDALONE) { + retcode = ERR_NET_CONFIGURED; + goto fail; + } + + /* allocation not in the IO path, cqueue thread context */ + new_conf = kmalloc(sizeof(struct net_conf), GFP_KERNEL); + if (!new_conf) { + retcode = ERR_NOMEM; + goto fail; + } + + memset(new_conf, 0, sizeof(struct net_conf)); + new_conf->timeout = DRBD_TIMEOUT_DEF; + new_conf->try_connect_int = DRBD_CONNECT_INT_DEF; + new_conf->ping_int = DRBD_PING_INT_DEF; + new_conf->max_epoch_size = DRBD_MAX_EPOCH_SIZE_DEF; + new_conf->max_buffers = DRBD_MAX_BUFFERS_DEF; + new_conf->unplug_watermark = DRBD_UNPLUG_WATERMARK_DEF; + new_conf->sndbuf_size = DRBD_SNDBUF_SIZE_DEF; + new_conf->rcvbuf_size = DRBD_RCVBUF_SIZE_DEF; + new_conf->ko_count = DRBD_KO_COUNT_DEF; + new_conf->after_sb_0p = DRBD_AFTER_SB_0P_DEF; + new_conf->after_sb_1p = DRBD_AFTER_SB_1P_DEF; + new_conf->after_sb_2p = DRBD_AFTER_SB_2P_DEF; + new_conf->want_lose = 0; + new_conf->two_primaries = 0; + new_conf->wire_protocol = DRBD_PROT_C; + new_conf->ping_timeo = DRBD_PING_TIMEO_DEF; + new_conf->rr_conflict = DRBD_RR_CONFLICT_DEF; + + if (!net_conf_from_tags(mdev, nlp->tag_list, new_conf)) { + retcode = ERR_MANDATORY_TAG; + goto fail; + } + + if (new_conf->two_primaries + && (new_conf->wire_protocol != DRBD_PROT_C)) { + retcode = ERR_NOT_PROTO_C; + goto fail; + }; + + if (mdev->state.role == R_PRIMARY && new_conf->want_lose) { + retcode = ERR_DISCARD; + goto fail; + } + + retcode = NO_ERROR; + + new_my_addr = (struct sockaddr *)&new_conf->my_addr; + new_peer_addr = (struct sockaddr *)&new_conf->peer_addr; + for (i = 0; i < minor_count; i++) { + odev = minor_to_mdev(i); + if (!odev || odev == mdev) + continue; + if (get_net_conf(odev)) { + taken_addr = (struct sockaddr *)&odev->net_conf->my_addr; + if (new_conf->my_addr_len == odev->net_conf->my_addr_len && + !memcmp(new_my_addr, taken_addr, new_conf->my_addr_len)) + retcode = ERR_LOCAL_ADDR; + + taken_addr = (struct sockaddr *)&odev->net_conf->peer_addr; + if (new_conf->peer_addr_len == odev->net_conf->peer_addr_len && + !memcmp(new_peer_addr, taken_addr, new_conf->peer_addr_len)) + retcode = ERR_PEER_ADDR; + + put_net_conf(odev); + if (retcode != NO_ERROR) + goto fail; + } + } + + if (new_conf->cram_hmac_alg[0] != 0) { + snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)", + new_conf->cram_hmac_alg); + tfm = crypto_alloc_hash(hmac_name, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + tfm = NULL; + retcode = ERR_AUTH_ALG; + goto fail; + } + + if (crypto_tfm_alg_type(crypto_hash_tfm(tfm)) + != CRYPTO_ALG_TYPE_HASH) { + retcode = ERR_AUTH_ALG_ND; + goto fail; + } + } + + if (new_conf->integrity_alg[0]) { + integrity_w_tfm = crypto_alloc_hash(new_conf->integrity_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(integrity_w_tfm)) { + integrity_w_tfm = NULL; + retcode=ERR_INTEGRITY_ALG; + goto fail; + } + + if (!drbd_crypto_is_hash(crypto_hash_tfm(integrity_w_tfm))) { + retcode=ERR_INTEGRITY_ALG_ND; + goto fail; + } + + integrity_r_tfm = crypto_alloc_hash(new_conf->integrity_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(integrity_r_tfm)) { + integrity_r_tfm = NULL; + retcode=ERR_INTEGRITY_ALG; + goto fail; + } + } + + ns = new_conf->max_epoch_size/8; + if (mdev->tl_hash_s != ns) { + new_tl_hash = kzalloc(ns*sizeof(void *), GFP_KERNEL); + if (!new_tl_hash) { + retcode = ERR_NOMEM; + goto fail; + } + } + + ns = new_conf->max_buffers/8; + if (new_conf->two_primaries && (mdev->ee_hash_s != ns)) { + new_ee_hash = kzalloc(ns*sizeof(void *), GFP_KERNEL); + if (!new_ee_hash) { + retcode = ERR_NOMEM; + goto fail; + } + } + + ((char *)new_conf->shared_secret)[SHARED_SECRET_MAX-1] = 0; + + if (integrity_w_tfm) { + i = crypto_hash_digestsize(integrity_w_tfm); + int_dig_out = kmalloc(i, GFP_KERNEL); + if (!int_dig_out) { + retcode = ERR_NOMEM; + goto fail; + } + int_dig_in = kmalloc(i, GFP_KERNEL); + if (!int_dig_in) { + retcode = ERR_NOMEM; + goto fail; + } + int_dig_vv = kmalloc(i, GFP_KERNEL); + if (!int_dig_vv) { + retcode = ERR_NOMEM; + goto fail; + } + } + + if (!mdev->bitmap) { + if(drbd_bm_init(mdev)) { + retcode = ERR_NOMEM; + goto fail; + } + } + + spin_lock_irq(&mdev->req_lock); + if (mdev->net_conf != NULL) { + retcode = ERR_NET_CONFIGURED; + spin_unlock_irq(&mdev->req_lock); + goto fail; + } + mdev->net_conf = new_conf; + + mdev->send_cnt = 0; + mdev->recv_cnt = 0; + + if (new_tl_hash) { + kfree(mdev->tl_hash); + mdev->tl_hash_s = mdev->net_conf->max_epoch_size/8; + mdev->tl_hash = new_tl_hash; + } + + if (new_ee_hash) { + kfree(mdev->ee_hash); + mdev->ee_hash_s = mdev->net_conf->max_buffers/8; + mdev->ee_hash = new_ee_hash; + } + + crypto_free_hash(mdev->cram_hmac_tfm); + mdev->cram_hmac_tfm = tfm; + + crypto_free_hash(mdev->integrity_w_tfm); + mdev->integrity_w_tfm = integrity_w_tfm; + + crypto_free_hash(mdev->integrity_r_tfm); + mdev->integrity_r_tfm = integrity_r_tfm; + + kfree(mdev->int_dig_out); + kfree(mdev->int_dig_in); + kfree(mdev->int_dig_vv); + mdev->int_dig_out=int_dig_out; + mdev->int_dig_in=int_dig_in; + mdev->int_dig_vv=int_dig_vv; + spin_unlock_irq(&mdev->req_lock); + + retcode = _drbd_request_state(mdev, NS(conn, C_UNCONNECTED), CS_VERBOSE); + + kobject_uevent(&disk_to_dev(mdev->vdisk)->kobj, KOBJ_CHANGE); + reply->ret_code = retcode; + drbd_reconfig_done(mdev); + return 0; + +fail: + kfree(int_dig_out); + kfree(int_dig_in); + kfree(int_dig_vv); + crypto_free_hash(tfm); + crypto_free_hash(integrity_w_tfm); + crypto_free_hash(integrity_r_tfm); + kfree(new_tl_hash); + kfree(new_ee_hash); + kfree(new_conf); + + reply->ret_code = retcode; + drbd_reconfig_done(mdev); + return 0; +} + +static int drbd_nl_disconnect(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + int retcode; + + retcode = _drbd_request_state(mdev, NS(conn, C_DISCONNECTING), CS_ORDERED); + + if (retcode == SS_NOTHING_TO_DO) + goto done; + else if (retcode == SS_ALREADY_STANDALONE) + goto done; + else if (retcode == SS_PRIMARY_NOP) { + /* Our statche checking code wants to see the peer outdated. */ + retcode = drbd_request_state(mdev, NS2(conn, C_DISCONNECTING, + pdsk, D_OUTDATED)); + } else if (retcode == SS_CW_FAILED_BY_PEER) { + /* The peer probably wants to see us outdated. */ + retcode = _drbd_request_state(mdev, NS2(conn, C_DISCONNECTING, + disk, D_OUTDATED), + CS_ORDERED); + if (retcode == SS_IS_DISKLESS || retcode == SS_LOWER_THAN_OUTDATED) { + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + retcode = SS_SUCCESS; + } + } + + if (retcode < SS_SUCCESS) + goto fail; + + if (wait_event_interruptible(mdev->state_wait, + mdev->state.conn != C_DISCONNECTING)) { + /* Do not test for mdev->state.conn == C_STANDALONE, since + someone else might connect us in the mean time! */ + retcode = ERR_INTR; + goto fail; + } + + done: + retcode = NO_ERROR; + fail: + drbd_md_sync(mdev); + reply->ret_code = retcode; + return 0; +} + +void resync_after_online_grow(struct drbd_conf *mdev) +{ + int iass; /* I am sync source */ + + dev_info(DEV, "Resync of new storage after online grow\n"); + if (mdev->state.role != mdev->state.peer) + iass = (mdev->state.role == R_PRIMARY); + else + iass = test_bit(DISCARD_CONCURRENT, &mdev->flags); + + if (iass) + drbd_start_resync(mdev, C_SYNC_SOURCE); + else + _drbd_request_state(mdev, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE + CS_SERIALIZE); +} + +static int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + struct resize rs; + int retcode = NO_ERROR; + int ldsc = 0; /* local disk size changed */ + enum determine_dev_size dd; + + memset(&rs, 0, sizeof(struct resize)); + if (!resize_from_tags(mdev, nlp->tag_list, &rs)) { + retcode = ERR_MANDATORY_TAG; + goto fail; + } + + if (mdev->state.conn > C_CONNECTED) { + retcode = ERR_RESIZE_RESYNC; + goto fail; + } + + if (mdev->state.role == R_SECONDARY && + mdev->state.peer == R_SECONDARY) { + retcode = ERR_NO_PRIMARY; + goto fail; + } + + if (!get_ldev(mdev)) { + retcode = ERR_NO_DISK; + goto fail; + } + + if (mdev->ldev->known_size != drbd_get_capacity(mdev->ldev->backing_bdev)) { + mdev->ldev->known_size = drbd_get_capacity(mdev->ldev->backing_bdev); + ldsc = 1; + } + + mdev->ldev->dc.disk_size = (sector_t)rs.resize_size; + dd = drbd_determin_dev_size(mdev); + drbd_md_sync(mdev); + put_ldev(mdev); + if (dd == dev_size_error) { + retcode = ERR_NOMEM_BITMAP; + goto fail; + } + + if (mdev->state.conn == C_CONNECTED && (dd != unchanged || ldsc)) { + if (dd == grew) + set_bit(RESIZE_PENDING, &mdev->flags); + + drbd_send_uuids(mdev); + drbd_send_sizes(mdev, 1); + } + + fail: + reply->ret_code = retcode; + return 0; +} + +static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + int retcode = NO_ERROR; + int err; + int ovr; /* online verify running */ + int rsr; /* re-sync running */ + struct crypto_hash *verify_tfm = NULL; + struct crypto_hash *csums_tfm = NULL; + struct syncer_conf sc; + cpumask_var_t new_cpu_mask; + + if (!zalloc_cpumask_var(&new_cpu_mask, GFP_KERNEL)) { + retcode = ERR_NOMEM; + goto fail; + } + + if (nlp->flags & DRBD_NL_SET_DEFAULTS) { + memset(&sc, 0, sizeof(struct syncer_conf)); + sc.rate = DRBD_RATE_DEF; + sc.after = DRBD_AFTER_DEF; + sc.al_extents = DRBD_AL_EXTENTS_DEF; + } else + memcpy(&sc, &mdev->sync_conf, sizeof(struct syncer_conf)); + + if (!syncer_conf_from_tags(mdev, nlp->tag_list, &sc)) { + retcode = ERR_MANDATORY_TAG; + goto fail; + } + + /* re-sync running */ + rsr = ( mdev->state.conn == C_SYNC_SOURCE || + mdev->state.conn == C_SYNC_TARGET || + mdev->state.conn == C_PAUSED_SYNC_S || + mdev->state.conn == C_PAUSED_SYNC_T ); + + if (rsr && strcmp(sc.csums_alg, mdev->sync_conf.csums_alg)) { + retcode = ERR_CSUMS_RESYNC_RUNNING; + goto fail; + } + + if (!rsr && sc.csums_alg[0]) { + csums_tfm = crypto_alloc_hash(sc.csums_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(csums_tfm)) { + csums_tfm = NULL; + retcode = ERR_CSUMS_ALG; + goto fail; + } + + if (!drbd_crypto_is_hash(crypto_hash_tfm(csums_tfm))) { + retcode = ERR_CSUMS_ALG_ND; + goto fail; + } + } + + /* online verify running */ + ovr = (mdev->state.conn == C_VERIFY_S || mdev->state.conn == C_VERIFY_T); + + if (ovr) { + if (strcmp(sc.verify_alg, mdev->sync_conf.verify_alg)) { + retcode = ERR_VERIFY_RUNNING; + goto fail; + } + } + + if (!ovr && sc.verify_alg[0]) { + verify_tfm = crypto_alloc_hash(sc.verify_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(verify_tfm)) { + verify_tfm = NULL; + retcode = ERR_VERIFY_ALG; + goto fail; + } + + if (!drbd_crypto_is_hash(crypto_hash_tfm(verify_tfm))) { + retcode = ERR_VERIFY_ALG_ND; + goto fail; + } + } + + /* silently ignore cpu mask on UP kernel */ + if (nr_cpu_ids > 1 && sc.cpu_mask[0] != 0) { + err = __bitmap_parse(sc.cpu_mask, 32, 0, + cpumask_bits(new_cpu_mask), nr_cpu_ids); + if (err) { + dev_warn(DEV, "__bitmap_parse() failed with %d\n", err); + retcode = ERR_CPU_MASK_PARSE; + goto fail; + } + } + + ERR_IF (sc.rate < 1) sc.rate = 1; + ERR_IF (sc.al_extents < 7) sc.al_extents = 127; /* arbitrary minimum */ +#define AL_MAX ((MD_AL_MAX_SIZE-1) * AL_EXTENTS_PT) + if (sc.al_extents > AL_MAX) { + dev_err(DEV, "sc.al_extents > %d\n", AL_MAX); + sc.al_extents = AL_MAX; + } +#undef AL_MAX + + /* most sanity checks done, try to assign the new sync-after + * dependency. need to hold the global lock in there, + * to avoid a race in the dependency loop check. */ + retcode = drbd_alter_sa(mdev, sc.after); + if (retcode != NO_ERROR) + goto fail; + + /* ok, assign the rest of it as well. + * lock against receive_SyncParam() */ + spin_lock(&mdev->peer_seq_lock); + mdev->sync_conf = sc; + + if (!rsr) { + crypto_free_hash(mdev->csums_tfm); + mdev->csums_tfm = csums_tfm; + csums_tfm = NULL; + } + + if (!ovr) { + crypto_free_hash(mdev->verify_tfm); + mdev->verify_tfm = verify_tfm; + verify_tfm = NULL; + } + spin_unlock(&mdev->peer_seq_lock); + + if (get_ldev(mdev)) { + wait_event(mdev->al_wait, lc_try_lock(mdev->act_log)); + drbd_al_shrink(mdev); + err = drbd_check_al_size(mdev); + lc_unlock(mdev->act_log); + wake_up(&mdev->al_wait); + + put_ldev(mdev); + drbd_md_sync(mdev); + + if (err) { + retcode = ERR_NOMEM; + goto fail; + } + } + + if (mdev->state.conn >= C_CONNECTED) + drbd_send_sync_param(mdev, &sc); + + if (!cpumask_equal(mdev->cpu_mask, new_cpu_mask)) { + cpumask_copy(mdev->cpu_mask, new_cpu_mask); + drbd_calc_cpu_mask(mdev); + mdev->receiver.reset_cpu_mask = 1; + mdev->asender.reset_cpu_mask = 1; + mdev->worker.reset_cpu_mask = 1; + } + + kobject_uevent(&disk_to_dev(mdev->vdisk)->kobj, KOBJ_CHANGE); +fail: + free_cpumask_var(new_cpu_mask); + crypto_free_hash(csums_tfm); + crypto_free_hash(verify_tfm); + reply->ret_code = retcode; + return 0; +} + +static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + int retcode; + + retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED); + + if (retcode < SS_SUCCESS && retcode != SS_NEED_CONNECTION) + retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T)); + + while (retcode == SS_NEED_CONNECTION) { + spin_lock_irq(&mdev->req_lock); + if (mdev->state.conn < C_CONNECTED) + retcode = _drbd_set_state(_NS(mdev, disk, D_INCONSISTENT), CS_VERBOSE, NULL); + spin_unlock_irq(&mdev->req_lock); + + if (retcode != SS_NEED_CONNECTION) + break; + + retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T)); + } + + reply->ret_code = retcode; + return 0; +} + +static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + + reply->ret_code = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S)); + + return 0; +} + +static int drbd_nl_pause_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + int retcode = NO_ERROR; + + if (drbd_request_state(mdev, NS(user_isp, 1)) == SS_NOTHING_TO_DO) + retcode = ERR_PAUSE_IS_SET; + + reply->ret_code = retcode; + return 0; +} + +static int drbd_nl_resume_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + int retcode = NO_ERROR; + + if (drbd_request_state(mdev, NS(user_isp, 0)) == SS_NOTHING_TO_DO) + retcode = ERR_PAUSE_IS_CLEAR; + + reply->ret_code = retcode; + return 0; +} + +static int drbd_nl_suspend_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + reply->ret_code = drbd_request_state(mdev, NS(susp, 1)); + + return 0; +} + +static int drbd_nl_resume_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + reply->ret_code = drbd_request_state(mdev, NS(susp, 0)); + return 0; +} + +static int drbd_nl_outdate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + reply->ret_code = drbd_request_state(mdev, NS(disk, D_OUTDATED)); + return 0; +} + +static int drbd_nl_get_config(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + unsigned short *tl; + + tl = reply->tag_list; + + if (get_ldev(mdev)) { + tl = disk_conf_to_tags(mdev, &mdev->ldev->dc, tl); + put_ldev(mdev); + } + + if (get_net_conf(mdev)) { + tl = net_conf_to_tags(mdev, mdev->net_conf, tl); + put_net_conf(mdev); + } + tl = syncer_conf_to_tags(mdev, &mdev->sync_conf, tl); + + put_unaligned(TT_END, tl++); /* Close the tag list */ + + return (int)((char *)tl - (char *)reply->tag_list); +} + +static int drbd_nl_get_state(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + unsigned short *tl = reply->tag_list; + union drbd_state s = mdev->state; + unsigned long rs_left; + unsigned int res; + + tl = get_state_to_tags(mdev, (struct get_state *)&s, tl); + + /* no local ref, no bitmap, no syncer progress. */ + if (s.conn >= C_SYNC_SOURCE && s.conn <= C_PAUSED_SYNC_T) { + if (get_ldev(mdev)) { + drbd_get_syncer_progress(mdev, &rs_left, &res); + tl = tl_add_int(tl, T_sync_progress, &res); + put_ldev(mdev); + } + } + put_unaligned(TT_END, tl++); /* Close the tag list */ + + return (int)((char *)tl - (char *)reply->tag_list); +} + +static int drbd_nl_get_uuids(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + unsigned short *tl; + + tl = reply->tag_list; + + if (get_ldev(mdev)) { + tl = tl_add_blob(tl, T_uuids, mdev->ldev->md.uuid, UI_SIZE*sizeof(u64)); + tl = tl_add_int(tl, T_uuids_flags, &mdev->ldev->md.flags); + put_ldev(mdev); + } + put_unaligned(TT_END, tl++); /* Close the tag list */ + + return (int)((char *)tl - (char *)reply->tag_list); +} + +/** + * drbd_nl_get_timeout_flag() - Used by drbdsetup to find out which timeout value to use + * @mdev: DRBD device. + * @nlp: Netlink/connector packet from drbdsetup + * @reply: Reply packet for drbdsetup + */ +static int drbd_nl_get_timeout_flag(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + unsigned short *tl; + char rv; + + tl = reply->tag_list; + + rv = mdev->state.pdsk == D_OUTDATED ? UT_PEER_OUTDATED : + test_bit(USE_DEGR_WFC_T, &mdev->flags) ? UT_DEGRADED : UT_DEFAULT; + + tl = tl_add_blob(tl, T_use_degraded, &rv, sizeof(rv)); + put_unaligned(TT_END, tl++); /* Close the tag list */ + + return (int)((char *)tl - (char *)reply->tag_list); +} + +static int drbd_nl_start_ov(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + /* default to resume from last known position, if possible */ + struct start_ov args = + { .start_sector = mdev->ov_start_sector }; + + if (!start_ov_from_tags(mdev, nlp->tag_list, &args)) { + reply->ret_code = ERR_MANDATORY_TAG; + return 0; + } + /* w_make_ov_request expects position to be aligned */ + mdev->ov_start_sector = args.start_sector & ~BM_SECT_PER_BIT; + reply->ret_code = drbd_request_state(mdev,NS(conn,C_VERIFY_S)); + return 0; +} + + +static int drbd_nl_new_c_uuid(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, + struct drbd_nl_cfg_reply *reply) +{ + int retcode = NO_ERROR; + int skip_initial_sync = 0; + int err; + + struct new_c_uuid args; + + memset(&args, 0, sizeof(struct new_c_uuid)); + if (!new_c_uuid_from_tags(mdev, nlp->tag_list, &args)) { + reply->ret_code = ERR_MANDATORY_TAG; + return 0; + } + + mutex_lock(&mdev->state_mutex); /* Protects us against serialized state changes. */ + + if (!get_ldev(mdev)) { + retcode = ERR_NO_DISK; + goto out; + } + + /* this is "skip initial sync", assume to be clean */ + if (mdev->state.conn == C_CONNECTED && mdev->agreed_pro_version >= 90 && + mdev->ldev->md.uuid[UI_CURRENT] == UUID_JUST_CREATED && args.clear_bm) { + dev_info(DEV, "Preparing to skip initial sync\n"); + skip_initial_sync = 1; + } else if (mdev->state.conn != C_STANDALONE) { + retcode = ERR_CONNECTED; + goto out_dec; + } + + drbd_uuid_set(mdev, UI_BITMAP, 0); /* Rotate UI_BITMAP to History 1, etc... */ + drbd_uuid_new_current(mdev); /* New current, previous to UI_BITMAP */ + + if (args.clear_bm) { + err = drbd_bitmap_io(mdev, &drbd_bmio_clear_n_write, "clear_n_write from new_c_uuid"); + if (err) { + dev_err(DEV, "Writing bitmap failed with %d\n",err); + retcode = ERR_IO_MD_DISK; + } + if (skip_initial_sync) { + drbd_send_uuids_skip_initial_sync(mdev); + _drbd_uuid_set(mdev, UI_BITMAP, 0); + spin_lock_irq(&mdev->req_lock); + _drbd_set_state(_NS2(mdev, disk, D_UP_TO_DATE, pdsk, D_UP_TO_DATE), + CS_VERBOSE, NULL); + spin_unlock_irq(&mdev->req_lock); + } + } + + drbd_md_sync(mdev); +out_dec: + put_ldev(mdev); +out: + mutex_unlock(&mdev->state_mutex); + + reply->ret_code = retcode; + return 0; +} + +static struct drbd_conf *ensure_mdev(struct drbd_nl_cfg_req *nlp) +{ + struct drbd_conf *mdev; + + if (nlp->drbd_minor >= minor_count) + return NULL; + + mdev = minor_to_mdev(nlp->drbd_minor); + + if (!mdev && (nlp->flags & DRBD_NL_CREATE_DEVICE)) { + struct gendisk *disk = NULL; + mdev = drbd_new_device(nlp->drbd_minor); + + spin_lock_irq(&drbd_pp_lock); + if (minor_table[nlp->drbd_minor] == NULL) { + minor_table[nlp->drbd_minor] = mdev; + disk = mdev->vdisk; + mdev = NULL; + } /* else: we lost the race */ + spin_unlock_irq(&drbd_pp_lock); + + if (disk) /* we won the race above */ + /* in case we ever add a drbd_delete_device(), + * don't forget the del_gendisk! */ + add_disk(disk); + else /* we lost the race above */ + drbd_free_mdev(mdev); + + mdev = minor_to_mdev(nlp->drbd_minor); + } + + return mdev; +} + +struct cn_handler_struct { + int (*function)(struct drbd_conf *, + struct drbd_nl_cfg_req *, + struct drbd_nl_cfg_reply *); + int reply_body_size; +}; + +static struct cn_handler_struct cnd_table[] = { + [ P_primary ] = { &drbd_nl_primary, 0 }, + [ P_secondary ] = { &drbd_nl_secondary, 0 }, + [ P_disk_conf ] = { &drbd_nl_disk_conf, 0 }, + [ P_detach ] = { &drbd_nl_detach, 0 }, + [ P_net_conf ] = { &drbd_nl_net_conf, 0 }, + [ P_disconnect ] = { &drbd_nl_disconnect, 0 }, + [ P_resize ] = { &drbd_nl_resize, 0 }, + [ P_syncer_conf ] = { &drbd_nl_syncer_conf, 0 }, + [ P_invalidate ] = { &drbd_nl_invalidate, 0 }, + [ P_invalidate_peer ] = { &drbd_nl_invalidate_peer, 0 }, + [ P_pause_sync ] = { &drbd_nl_pause_sync, 0 }, + [ P_resume_sync ] = { &drbd_nl_resume_sync, 0 }, + [ P_suspend_io ] = { &drbd_nl_suspend_io, 0 }, + [ P_resume_io ] = { &drbd_nl_resume_io, 0 }, + [ P_outdate ] = { &drbd_nl_outdate, 0 }, + [ P_get_config ] = { &drbd_nl_get_config, + sizeof(struct syncer_conf_tag_len_struct) + + sizeof(struct disk_conf_tag_len_struct) + + sizeof(struct net_conf_tag_len_struct) }, + [ P_get_state ] = { &drbd_nl_get_state, + sizeof(struct get_state_tag_len_struct) + + sizeof(struct sync_progress_tag_len_struct) }, + [ P_get_uuids ] = { &drbd_nl_get_uuids, + sizeof(struct get_uuids_tag_len_struct) }, + [ P_get_timeout_flag ] = { &drbd_nl_get_timeout_flag, + sizeof(struct get_timeout_flag_tag_len_struct)}, + [ P_start_ov ] = { &drbd_nl_start_ov, 0 }, + [ P_new_c_uuid ] = { &drbd_nl_new_c_uuid, 0 }, +}; + +static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms *nsp) +{ + struct drbd_nl_cfg_req *nlp = (struct drbd_nl_cfg_req *)req->data; + struct cn_handler_struct *cm; + struct cn_msg *cn_reply; + struct drbd_nl_cfg_reply *reply; + struct drbd_conf *mdev; + int retcode, rr; + int reply_size = sizeof(struct cn_msg) + + sizeof(struct drbd_nl_cfg_reply) + + sizeof(short int); + + if (!try_module_get(THIS_MODULE)) { + printk(KERN_ERR "drbd: try_module_get() failed!\n"); + return; + } + + if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) { + retcode = ERR_PERM; + goto fail; + } + + mdev = ensure_mdev(nlp); + if (!mdev) { + retcode = ERR_MINOR_INVALID; + goto fail; + } + + if (nlp->packet_type >= P_nl_after_last_packet) { + retcode = ERR_PACKET_NR; + goto fail; + } + + cm = cnd_table + nlp->packet_type; + + /* This may happen if packet number is 0: */ + if (cm->function == NULL) { + retcode = ERR_PACKET_NR; + goto fail; + } + + reply_size += cm->reply_body_size; + + /* allocation not in the IO path, cqueue thread context */ + cn_reply = kmalloc(reply_size, GFP_KERNEL); + if (!cn_reply) { + retcode = ERR_NOMEM; + goto fail; + } + reply = (struct drbd_nl_cfg_reply *) cn_reply->data; + + reply->packet_type = + cm->reply_body_size ? nlp->packet_type : P_nl_after_last_packet; + reply->minor = nlp->drbd_minor; + reply->ret_code = NO_ERROR; /* Might by modified by cm->function. */ + /* reply->tag_list; might be modified by cm->function. */ + + rr = cm->function(mdev, nlp, reply); + + cn_reply->id = req->id; + cn_reply->seq = req->seq; + cn_reply->ack = req->ack + 1; + cn_reply->len = sizeof(struct drbd_nl_cfg_reply) + rr; + cn_reply->flags = 0; + + rr = cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_KERNEL); + if (rr && rr != -ESRCH) + printk(KERN_INFO "drbd: cn_netlink_send()=%d\n", rr); + + kfree(cn_reply); + module_put(THIS_MODULE); + return; + fail: + drbd_nl_send_reply(req, retcode); + module_put(THIS_MODULE); +} + +static atomic_t drbd_nl_seq = ATOMIC_INIT(2); /* two. */ + +static unsigned short * +__tl_add_blob(unsigned short *tl, enum drbd_tags tag, const void *data, + unsigned short len, int nul_terminated) +{ + unsigned short l = tag_descriptions[tag_number(tag)].max_len; + len = (len < l) ? len : l; + put_unaligned(tag, tl++); + put_unaligned(len, tl++); + memcpy(tl, data, len); + tl = (unsigned short*)((char*)tl + len); + if (nul_terminated) + *((char*)tl - 1) = 0; + return tl; +} + +static unsigned short * +tl_add_blob(unsigned short *tl, enum drbd_tags tag, const void *data, int len) +{ + return __tl_add_blob(tl, tag, data, len, 0); +} + +static unsigned short * +tl_add_str(unsigned short *tl, enum drbd_tags tag, const char *str) +{ + return __tl_add_blob(tl, tag, str, strlen(str)+1, 0); +} + +static unsigned short * +tl_add_int(unsigned short *tl, enum drbd_tags tag, const void *val) +{ + put_unaligned(tag, tl++); + switch(tag_type(tag)) { + case TT_INTEGER: + put_unaligned(sizeof(int), tl++); + put_unaligned(*(int *)val, (int *)tl); + tl = (unsigned short*)((char*)tl+sizeof(int)); + break; + case TT_INT64: + put_unaligned(sizeof(u64), tl++); + put_unaligned(*(u64 *)val, (u64 *)tl); + tl = (unsigned short*)((char*)tl+sizeof(u64)); + break; + default: + /* someone did something stupid. */ + ; + } + return tl; +} + +void drbd_bcast_state(struct drbd_conf *mdev, union drbd_state state) +{ + char buffer[sizeof(struct cn_msg)+ + sizeof(struct drbd_nl_cfg_reply)+ + sizeof(struct get_state_tag_len_struct)+ + sizeof(short int)]; + struct cn_msg *cn_reply = (struct cn_msg *) buffer; + struct drbd_nl_cfg_reply *reply = + (struct drbd_nl_cfg_reply *)cn_reply->data; + unsigned short *tl = reply->tag_list; + + /* dev_warn(DEV, "drbd_bcast_state() got called\n"); */ + + tl = get_state_to_tags(mdev, (struct get_state *)&state, tl); + + put_unaligned(TT_END, tl++); /* Close the tag list */ + + cn_reply->id.idx = CN_IDX_DRBD; + cn_reply->id.val = CN_VAL_DRBD; + + cn_reply->seq = atomic_add_return(1, &drbd_nl_seq); + cn_reply->ack = 0; /* not used here. */ + cn_reply->len = sizeof(struct drbd_nl_cfg_reply) + + (int)((char *)tl - (char *)reply->tag_list); + cn_reply->flags = 0; + + reply->packet_type = P_get_state; + reply->minor = mdev_to_minor(mdev); + reply->ret_code = NO_ERROR; + + cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO); +} + +void drbd_bcast_ev_helper(struct drbd_conf *mdev, char *helper_name) +{ + char buffer[sizeof(struct cn_msg)+ + sizeof(struct drbd_nl_cfg_reply)+ + sizeof(struct call_helper_tag_len_struct)+ + sizeof(short int)]; + struct cn_msg *cn_reply = (struct cn_msg *) buffer; + struct drbd_nl_cfg_reply *reply = + (struct drbd_nl_cfg_reply *)cn_reply->data; + unsigned short *tl = reply->tag_list; + + /* dev_warn(DEV, "drbd_bcast_state() got called\n"); */ + + tl = tl_add_str(tl, T_helper, helper_name); + put_unaligned(TT_END, tl++); /* Close the tag list */ + + cn_reply->id.idx = CN_IDX_DRBD; + cn_reply->id.val = CN_VAL_DRBD; + + cn_reply->seq = atomic_add_return(1, &drbd_nl_seq); + cn_reply->ack = 0; /* not used here. */ + cn_reply->len = sizeof(struct drbd_nl_cfg_reply) + + (int)((char *)tl - (char *)reply->tag_list); + cn_reply->flags = 0; + + reply->packet_type = P_call_helper; + reply->minor = mdev_to_minor(mdev); + reply->ret_code = NO_ERROR; + + cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO); +} + +void drbd_bcast_ee(struct drbd_conf *mdev, + const char *reason, const int dgs, + const char* seen_hash, const char* calc_hash, + const struct drbd_epoch_entry* e) +{ + struct cn_msg *cn_reply; + struct drbd_nl_cfg_reply *reply; + struct bio_vec *bvec; + unsigned short *tl; + int i; + + if (!e) + return; + if (!reason || !reason[0]) + return; + + /* apparently we have to memcpy twice, first to prepare the data for the + * struct cn_msg, then within cn_netlink_send from the cn_msg to the + * netlink skb. */ + /* receiver thread context, which is not in the writeout path (of this node), + * but may be in the writeout path of the _other_ node. + * GFP_NOIO to avoid potential "distributed deadlock". */ + cn_reply = kmalloc( + sizeof(struct cn_msg)+ + sizeof(struct drbd_nl_cfg_reply)+ + sizeof(struct dump_ee_tag_len_struct)+ + sizeof(short int), + GFP_NOIO); + + if (!cn_reply) { + dev_err(DEV, "could not kmalloc buffer for drbd_bcast_ee, sector %llu, size %u\n", + (unsigned long long)e->sector, e->size); + return; + } + + reply = (struct drbd_nl_cfg_reply*)cn_reply->data; + tl = reply->tag_list; + + tl = tl_add_str(tl, T_dump_ee_reason, reason); + tl = tl_add_blob(tl, T_seen_digest, seen_hash, dgs); + tl = tl_add_blob(tl, T_calc_digest, calc_hash, dgs); + tl = tl_add_int(tl, T_ee_sector, &e->sector); + tl = tl_add_int(tl, T_ee_block_id, &e->block_id); + + put_unaligned(T_ee_data, tl++); + put_unaligned(e->size, tl++); + + __bio_for_each_segment(bvec, e->private_bio, i, 0) { + void *d = kmap(bvec->bv_page); + memcpy(tl, d + bvec->bv_offset, bvec->bv_len); + kunmap(bvec->bv_page); + tl=(unsigned short*)((char*)tl + bvec->bv_len); + } + put_unaligned(TT_END, tl++); /* Close the tag list */ + + cn_reply->id.idx = CN_IDX_DRBD; + cn_reply->id.val = CN_VAL_DRBD; + + cn_reply->seq = atomic_add_return(1,&drbd_nl_seq); + cn_reply->ack = 0; // not used here. + cn_reply->len = sizeof(struct drbd_nl_cfg_reply) + + (int)((char*)tl - (char*)reply->tag_list); + cn_reply->flags = 0; + + reply->packet_type = P_dump_ee; + reply->minor = mdev_to_minor(mdev); + reply->ret_code = NO_ERROR; + + cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO); + kfree(cn_reply); +} + +void drbd_bcast_sync_progress(struct drbd_conf *mdev) +{ + char buffer[sizeof(struct cn_msg)+ + sizeof(struct drbd_nl_cfg_reply)+ + sizeof(struct sync_progress_tag_len_struct)+ + sizeof(short int)]; + struct cn_msg *cn_reply = (struct cn_msg *) buffer; + struct drbd_nl_cfg_reply *reply = + (struct drbd_nl_cfg_reply *)cn_reply->data; + unsigned short *tl = reply->tag_list; + unsigned long rs_left; + unsigned int res; + + /* no local ref, no bitmap, no syncer progress, no broadcast. */ + if (!get_ldev(mdev)) + return; + drbd_get_syncer_progress(mdev, &rs_left, &res); + put_ldev(mdev); + + tl = tl_add_int(tl, T_sync_progress, &res); + put_unaligned(TT_END, tl++); /* Close the tag list */ + + cn_reply->id.idx = CN_IDX_DRBD; + cn_reply->id.val = CN_VAL_DRBD; + + cn_reply->seq = atomic_add_return(1, &drbd_nl_seq); + cn_reply->ack = 0; /* not used here. */ + cn_reply->len = sizeof(struct drbd_nl_cfg_reply) + + (int)((char *)tl - (char *)reply->tag_list); + cn_reply->flags = 0; + + reply->packet_type = P_sync_progress; + reply->minor = mdev_to_minor(mdev); + reply->ret_code = NO_ERROR; + + cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO); +} + +int __init drbd_nl_init(void) +{ + static struct cb_id cn_id_drbd; + int err, try=10; + + cn_id_drbd.val = CN_VAL_DRBD; + do { + cn_id_drbd.idx = cn_idx; + err = cn_add_callback(&cn_id_drbd, "cn_drbd", &drbd_connector_callback); + if (!err) + break; + cn_idx = (cn_idx + CN_IDX_STEP); + } while (try--); + + if (err) { + printk(KERN_ERR "drbd: cn_drbd failed to register\n"); + return err; + } + + return 0; +} + +void drbd_nl_cleanup(void) +{ + static struct cb_id cn_id_drbd; + + cn_id_drbd.idx = cn_idx; + cn_id_drbd.val = CN_VAL_DRBD; + + cn_del_callback(&cn_id_drbd); +} + +void drbd_nl_send_reply(struct cn_msg *req, int ret_code) +{ + char buffer[sizeof(struct cn_msg)+sizeof(struct drbd_nl_cfg_reply)]; + struct cn_msg *cn_reply = (struct cn_msg *) buffer; + struct drbd_nl_cfg_reply *reply = + (struct drbd_nl_cfg_reply *)cn_reply->data; + int rr; + + cn_reply->id = req->id; + + cn_reply->seq = req->seq; + cn_reply->ack = req->ack + 1; + cn_reply->len = sizeof(struct drbd_nl_cfg_reply); + cn_reply->flags = 0; + + reply->minor = ((struct drbd_nl_cfg_req *)req->data)->drbd_minor; + reply->ret_code = ret_code; + + rr = cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO); + if (rr && rr != -ESRCH) + printk(KERN_INFO "drbd: cn_netlink_send()=%d\n", rr); +} + diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c new file mode 100644 index 000000000000..bdd0b4943b10 --- /dev/null +++ b/drivers/block/drbd/drbd_proc.c @@ -0,0 +1,265 @@ +/* + drbd_proc.c + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. + Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include <linux/module.h> + +#include <asm/uaccess.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/drbd.h> +#include "drbd_int.h" + +static int drbd_proc_open(struct inode *inode, struct file *file); + + +struct proc_dir_entry *drbd_proc; +struct file_operations drbd_proc_fops = { + .owner = THIS_MODULE, + .open = drbd_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +/*lge + * progress bars shamelessly adapted from driver/md/md.c + * output looks like + * [=====>..............] 33.5% (23456/123456) + * finish: 2:20:20 speed: 6,345 (6,456) K/sec + */ +static void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq) +{ + unsigned long db, dt, dbdt, rt, rs_left; + unsigned int res; + int i, x, y; + + drbd_get_syncer_progress(mdev, &rs_left, &res); + + x = res/50; + y = 20-x; + seq_printf(seq, "\t["); + for (i = 1; i < x; i++) + seq_printf(seq, "="); + seq_printf(seq, ">"); + for (i = 0; i < y; i++) + seq_printf(seq, "."); + seq_printf(seq, "] "); + + seq_printf(seq, "sync'ed:%3u.%u%% ", res / 10, res % 10); + /* if more than 1 GB display in MB */ + if (mdev->rs_total > 0x100000L) + seq_printf(seq, "(%lu/%lu)M\n\t", + (unsigned long) Bit2KB(rs_left >> 10), + (unsigned long) Bit2KB(mdev->rs_total >> 10)); + else + seq_printf(seq, "(%lu/%lu)K\n\t", + (unsigned long) Bit2KB(rs_left), + (unsigned long) Bit2KB(mdev->rs_total)); + + /* see drivers/md/md.c + * We do not want to overflow, so the order of operands and + * the * 100 / 100 trick are important. We do a +1 to be + * safe against division by zero. We only estimate anyway. + * + * dt: time from mark until now + * db: blocks written from mark until now + * rt: remaining time + */ + dt = (jiffies - mdev->rs_mark_time) / HZ; + + if (dt > 20) { + /* if we made no update to rs_mark_time for too long, + * we are stalled. show that. */ + seq_printf(seq, "stalled\n"); + return; + } + + if (!dt) + dt++; + db = mdev->rs_mark_left - rs_left; + rt = (dt * (rs_left / (db/100+1)))/100; /* seconds */ + + seq_printf(seq, "finish: %lu:%02lu:%02lu", + rt / 3600, (rt % 3600) / 60, rt % 60); + + /* current speed average over (SYNC_MARKS * SYNC_MARK_STEP) jiffies */ + dbdt = Bit2KB(db/dt); + if (dbdt > 1000) + seq_printf(seq, " speed: %ld,%03ld", + dbdt/1000, dbdt % 1000); + else + seq_printf(seq, " speed: %ld", dbdt); + + /* mean speed since syncer started + * we do account for PausedSync periods */ + dt = (jiffies - mdev->rs_start - mdev->rs_paused) / HZ; + if (dt <= 0) + dt = 1; + db = mdev->rs_total - rs_left; + dbdt = Bit2KB(db/dt); + if (dbdt > 1000) + seq_printf(seq, " (%ld,%03ld)", + dbdt/1000, dbdt % 1000); + else + seq_printf(seq, " (%ld)", dbdt); + + seq_printf(seq, " K/sec\n"); +} + +static void resync_dump_detail(struct seq_file *seq, struct lc_element *e) +{ + struct bm_extent *bme = lc_entry(e, struct bm_extent, lce); + + seq_printf(seq, "%5d %s %s\n", bme->rs_left, + bme->flags & BME_NO_WRITES ? "NO_WRITES" : "---------", + bme->flags & BME_LOCKED ? "LOCKED" : "------" + ); +} + +static int drbd_seq_show(struct seq_file *seq, void *v) +{ + int i, hole = 0; + const char *sn; + struct drbd_conf *mdev; + + static char write_ordering_chars[] = { + [WO_none] = 'n', + [WO_drain_io] = 'd', + [WO_bdev_flush] = 'f', + [WO_bio_barrier] = 'b', + }; + + seq_printf(seq, "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n", + API_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag()); + + /* + cs .. connection state + ro .. node role (local/remote) + ds .. disk state (local/remote) + protocol + various flags + ns .. network send + nr .. network receive + dw .. disk write + dr .. disk read + al .. activity log write count + bm .. bitmap update write count + pe .. pending (waiting for ack or data reply) + ua .. unack'd (still need to send ack or data reply) + ap .. application requests accepted, but not yet completed + ep .. number of epochs currently "on the fly", P_BARRIER_ACK pending + wo .. write ordering mode currently in use + oos .. known out-of-sync kB + */ + + for (i = 0; i < minor_count; i++) { + mdev = minor_to_mdev(i); + if (!mdev) { + hole = 1; + continue; + } + if (hole) { + hole = 0; + seq_printf(seq, "\n"); + } + + sn = drbd_conn_str(mdev->state.conn); + + if (mdev->state.conn == C_STANDALONE && + mdev->state.disk == D_DISKLESS && + mdev->state.role == R_SECONDARY) { + seq_printf(seq, "%2d: cs:Unconfigured\n", i); + } else { + seq_printf(seq, + "%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c\n" + " ns:%u nr:%u dw:%u dr:%u al:%u bm:%u " + "lo:%d pe:%d ua:%d ap:%d ep:%d wo:%c", + i, sn, + drbd_role_str(mdev->state.role), + drbd_role_str(mdev->state.peer), + drbd_disk_str(mdev->state.disk), + drbd_disk_str(mdev->state.pdsk), + (mdev->net_conf == NULL ? ' ' : + (mdev->net_conf->wire_protocol - DRBD_PROT_A+'A')), + mdev->state.susp ? 's' : 'r', + mdev->state.aftr_isp ? 'a' : '-', + mdev->state.peer_isp ? 'p' : '-', + mdev->state.user_isp ? 'u' : '-', + mdev->congestion_reason ?: '-', + mdev->send_cnt/2, + mdev->recv_cnt/2, + mdev->writ_cnt/2, + mdev->read_cnt/2, + mdev->al_writ_cnt, + mdev->bm_writ_cnt, + atomic_read(&mdev->local_cnt), + atomic_read(&mdev->ap_pending_cnt) + + atomic_read(&mdev->rs_pending_cnt), + atomic_read(&mdev->unacked_cnt), + atomic_read(&mdev->ap_bio_cnt), + mdev->epochs, + write_ordering_chars[mdev->write_ordering] + ); + seq_printf(seq, " oos:%lu\n", + Bit2KB(drbd_bm_total_weight(mdev))); + } + if (mdev->state.conn == C_SYNC_SOURCE || + mdev->state.conn == C_SYNC_TARGET) + drbd_syncer_progress(mdev, seq); + + if (mdev->state.conn == C_VERIFY_S || mdev->state.conn == C_VERIFY_T) + seq_printf(seq, "\t%3d%% %lu/%lu\n", + (int)((mdev->rs_total-mdev->ov_left) / + (mdev->rs_total/100+1)), + mdev->rs_total - mdev->ov_left, + mdev->rs_total); + + if (proc_details >= 1 && get_ldev_if_state(mdev, D_FAILED)) { + lc_seq_printf_stats(seq, mdev->resync); + lc_seq_printf_stats(seq, mdev->act_log); + put_ldev(mdev); + } + + if (proc_details >= 2) { + if (mdev->resync) { + lc_seq_dump_details(seq, mdev->resync, "rs_left", + resync_dump_detail); + } + } + } + + return 0; +} + +static int drbd_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, drbd_seq_show, PDE(inode)->data); +} + +/* PROC FS stuff end */ diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c new file mode 100644 index 000000000000..c548f24f54a1 --- /dev/null +++ b/drivers/block/drbd/drbd_receiver.c @@ -0,0 +1,4426 @@ +/* + drbd_receiver.c + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. + Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <linux/module.h> + +#include <asm/uaccess.h> +#include <net/sock.h> + +#include <linux/version.h> +#include <linux/drbd.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/in.h> +#include <linux/mm.h> +#include <linux/memcontrol.h> +#include <linux/mm_inline.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/pkt_sched.h> +#define __KERNEL_SYSCALLS__ +#include <linux/unistd.h> +#include <linux/vmalloc.h> +#include <linux/random.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/scatterlist.h> +#include "drbd_int.h" +#include "drbd_req.h" + +#include "drbd_vli.h" + +struct flush_work { + struct drbd_work w; + struct drbd_epoch *epoch; +}; + +enum finish_epoch { + FE_STILL_LIVE, + FE_DESTROYED, + FE_RECYCLED, +}; + +static int drbd_do_handshake(struct drbd_conf *mdev); +static int drbd_do_auth(struct drbd_conf *mdev); + +static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *, struct drbd_epoch *, enum epoch_event); +static int e_end_block(struct drbd_conf *, struct drbd_work *, int); + +static struct drbd_epoch *previous_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch) +{ + struct drbd_epoch *prev; + spin_lock(&mdev->epoch_lock); + prev = list_entry(epoch->list.prev, struct drbd_epoch, list); + if (prev == epoch || prev == mdev->current_epoch) + prev = NULL; + spin_unlock(&mdev->epoch_lock); + return prev; +} + +#define GFP_TRY (__GFP_HIGHMEM | __GFP_NOWARN) + +static struct page *drbd_pp_first_page_or_try_alloc(struct drbd_conf *mdev) +{ + struct page *page = NULL; + + /* Yes, testing drbd_pp_vacant outside the lock is racy. + * So what. It saves a spin_lock. */ + if (drbd_pp_vacant > 0) { + spin_lock(&drbd_pp_lock); + page = drbd_pp_pool; + if (page) { + drbd_pp_pool = (struct page *)page_private(page); + set_page_private(page, 0); /* just to be polite */ + drbd_pp_vacant--; + } + spin_unlock(&drbd_pp_lock); + } + /* GFP_TRY, because we must not cause arbitrary write-out: in a DRBD + * "criss-cross" setup, that might cause write-out on some other DRBD, + * which in turn might block on the other node at this very place. */ + if (!page) + page = alloc_page(GFP_TRY); + if (page) + atomic_inc(&mdev->pp_in_use); + return page; +} + +/* kick lower level device, if we have more than (arbitrary number) + * reference counts on it, which typically are locally submitted io + * requests. don't use unacked_cnt, so we speed up proto A and B, too. */ +static void maybe_kick_lo(struct drbd_conf *mdev) +{ + if (atomic_read(&mdev->local_cnt) >= mdev->net_conf->unplug_watermark) + drbd_kick_lo(mdev); +} + +static void reclaim_net_ee(struct drbd_conf *mdev, struct list_head *to_be_freed) +{ + struct drbd_epoch_entry *e; + struct list_head *le, *tle; + + /* The EEs are always appended to the end of the list. Since + they are sent in order over the wire, they have to finish + in order. As soon as we see the first not finished we can + stop to examine the list... */ + + list_for_each_safe(le, tle, &mdev->net_ee) { + e = list_entry(le, struct drbd_epoch_entry, w.list); + if (drbd_bio_has_active_page(e->private_bio)) + break; + list_move(le, to_be_freed); + } +} + +static void drbd_kick_lo_and_reclaim_net(struct drbd_conf *mdev) +{ + LIST_HEAD(reclaimed); + struct drbd_epoch_entry *e, *t; + + maybe_kick_lo(mdev); + spin_lock_irq(&mdev->req_lock); + reclaim_net_ee(mdev, &reclaimed); + spin_unlock_irq(&mdev->req_lock); + + list_for_each_entry_safe(e, t, &reclaimed, w.list) + drbd_free_ee(mdev, e); +} + +/** + * drbd_pp_alloc() - Returns a page, fails only if a signal comes in + * @mdev: DRBD device. + * @retry: whether or not to retry allocation forever (or until signalled) + * + * Tries to allocate a page, first from our own page pool, then from the + * kernel, unless this allocation would exceed the max_buffers setting. + * If @retry is non-zero, retry until DRBD frees a page somewhere else. + */ +static struct page *drbd_pp_alloc(struct drbd_conf *mdev, int retry) +{ + struct page *page = NULL; + DEFINE_WAIT(wait); + + if (atomic_read(&mdev->pp_in_use) < mdev->net_conf->max_buffers) { + page = drbd_pp_first_page_or_try_alloc(mdev); + if (page) + return page; + } + + for (;;) { + prepare_to_wait(&drbd_pp_wait, &wait, TASK_INTERRUPTIBLE); + + drbd_kick_lo_and_reclaim_net(mdev); + + if (atomic_read(&mdev->pp_in_use) < mdev->net_conf->max_buffers) { + page = drbd_pp_first_page_or_try_alloc(mdev); + if (page) + break; + } + + if (!retry) + break; + + if (signal_pending(current)) { + dev_warn(DEV, "drbd_pp_alloc interrupted!\n"); + break; + } + + schedule(); + } + finish_wait(&drbd_pp_wait, &wait); + + return page; +} + +/* Must not be used from irq, as that may deadlock: see drbd_pp_alloc. + * Is also used from inside an other spin_lock_irq(&mdev->req_lock) */ +static void drbd_pp_free(struct drbd_conf *mdev, struct page *page) +{ + int free_it; + + spin_lock(&drbd_pp_lock); + if (drbd_pp_vacant > (DRBD_MAX_SEGMENT_SIZE/PAGE_SIZE)*minor_count) { + free_it = 1; + } else { + set_page_private(page, (unsigned long)drbd_pp_pool); + drbd_pp_pool = page; + drbd_pp_vacant++; + free_it = 0; + } + spin_unlock(&drbd_pp_lock); + + atomic_dec(&mdev->pp_in_use); + + if (free_it) + __free_page(page); + + wake_up(&drbd_pp_wait); +} + +static void drbd_pp_free_bio_pages(struct drbd_conf *mdev, struct bio *bio) +{ + struct page *p_to_be_freed = NULL; + struct page *page; + struct bio_vec *bvec; + int i; + + spin_lock(&drbd_pp_lock); + __bio_for_each_segment(bvec, bio, i, 0) { + if (drbd_pp_vacant > (DRBD_MAX_SEGMENT_SIZE/PAGE_SIZE)*minor_count) { + set_page_private(bvec->bv_page, (unsigned long)p_to_be_freed); + p_to_be_freed = bvec->bv_page; + } else { + set_page_private(bvec->bv_page, (unsigned long)drbd_pp_pool); + drbd_pp_pool = bvec->bv_page; + drbd_pp_vacant++; + } + } + spin_unlock(&drbd_pp_lock); + atomic_sub(bio->bi_vcnt, &mdev->pp_in_use); + + while (p_to_be_freed) { + page = p_to_be_freed; + p_to_be_freed = (struct page *)page_private(page); + set_page_private(page, 0); /* just to be polite */ + put_page(page); + } + + wake_up(&drbd_pp_wait); +} + +/* +You need to hold the req_lock: + _drbd_wait_ee_list_empty() + +You must not have the req_lock: + drbd_free_ee() + drbd_alloc_ee() + drbd_init_ee() + drbd_release_ee() + drbd_ee_fix_bhs() + drbd_process_done_ee() + drbd_clear_done_ee() + drbd_wait_ee_list_empty() +*/ + +struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev, + u64 id, + sector_t sector, + unsigned int data_size, + gfp_t gfp_mask) __must_hold(local) +{ + struct request_queue *q; + struct drbd_epoch_entry *e; + struct page *page; + struct bio *bio; + unsigned int ds; + + if (FAULT_ACTIVE(mdev, DRBD_FAULT_AL_EE)) + return NULL; + + e = mempool_alloc(drbd_ee_mempool, gfp_mask & ~__GFP_HIGHMEM); + if (!e) { + if (!(gfp_mask & __GFP_NOWARN)) + dev_err(DEV, "alloc_ee: Allocation of an EE failed\n"); + return NULL; + } + + bio = bio_alloc(gfp_mask & ~__GFP_HIGHMEM, div_ceil(data_size, PAGE_SIZE)); + if (!bio) { + if (!(gfp_mask & __GFP_NOWARN)) + dev_err(DEV, "alloc_ee: Allocation of a bio failed\n"); + goto fail1; + } + + bio->bi_bdev = mdev->ldev->backing_bdev; + bio->bi_sector = sector; + + ds = data_size; + while (ds) { + page = drbd_pp_alloc(mdev, (gfp_mask & __GFP_WAIT)); + if (!page) { + if (!(gfp_mask & __GFP_NOWARN)) + dev_err(DEV, "alloc_ee: Allocation of a page failed\n"); + goto fail2; + } + if (!bio_add_page(bio, page, min_t(int, ds, PAGE_SIZE), 0)) { + drbd_pp_free(mdev, page); + dev_err(DEV, "alloc_ee: bio_add_page(s=%llu," + "data_size=%u,ds=%u) failed\n", + (unsigned long long)sector, data_size, ds); + + q = bdev_get_queue(bio->bi_bdev); + if (q->merge_bvec_fn) { + struct bvec_merge_data bvm = { + .bi_bdev = bio->bi_bdev, + .bi_sector = bio->bi_sector, + .bi_size = bio->bi_size, + .bi_rw = bio->bi_rw, + }; + int l = q->merge_bvec_fn(q, &bvm, + &bio->bi_io_vec[bio->bi_vcnt]); + dev_err(DEV, "merge_bvec_fn() = %d\n", l); + } + + /* dump more of the bio. */ + dev_err(DEV, "bio->bi_max_vecs = %d\n", bio->bi_max_vecs); + dev_err(DEV, "bio->bi_vcnt = %d\n", bio->bi_vcnt); + dev_err(DEV, "bio->bi_size = %d\n", bio->bi_size); + dev_err(DEV, "bio->bi_phys_segments = %d\n", bio->bi_phys_segments); + + goto fail2; + break; + } + ds -= min_t(int, ds, PAGE_SIZE); + } + + D_ASSERT(data_size == bio->bi_size); + + bio->bi_private = e; + e->mdev = mdev; + e->sector = sector; + e->size = bio->bi_size; + + e->private_bio = bio; + e->block_id = id; + INIT_HLIST_NODE(&e->colision); + e->epoch = NULL; + e->flags = 0; + + return e; + + fail2: + drbd_pp_free_bio_pages(mdev, bio); + bio_put(bio); + fail1: + mempool_free(e, drbd_ee_mempool); + + return NULL; +} + +void drbd_free_ee(struct drbd_conf *mdev, struct drbd_epoch_entry *e) +{ + struct bio *bio = e->private_bio; + drbd_pp_free_bio_pages(mdev, bio); + bio_put(bio); + D_ASSERT(hlist_unhashed(&e->colision)); + mempool_free(e, drbd_ee_mempool); +} + +int drbd_release_ee(struct drbd_conf *mdev, struct list_head *list) +{ + LIST_HEAD(work_list); + struct drbd_epoch_entry *e, *t; + int count = 0; + + spin_lock_irq(&mdev->req_lock); + list_splice_init(list, &work_list); + spin_unlock_irq(&mdev->req_lock); + + list_for_each_entry_safe(e, t, &work_list, w.list) { + drbd_free_ee(mdev, e); + count++; + } + return count; +} + + +/* + * This function is called from _asender only_ + * but see also comments in _req_mod(,barrier_acked) + * and receive_Barrier. + * + * Move entries from net_ee to done_ee, if ready. + * Grab done_ee, call all callbacks, free the entries. + * The callbacks typically send out ACKs. + */ +static int drbd_process_done_ee(struct drbd_conf *mdev) +{ + LIST_HEAD(work_list); + LIST_HEAD(reclaimed); + struct drbd_epoch_entry *e, *t; + int ok = (mdev->state.conn >= C_WF_REPORT_PARAMS); + + spin_lock_irq(&mdev->req_lock); + reclaim_net_ee(mdev, &reclaimed); + list_splice_init(&mdev->done_ee, &work_list); + spin_unlock_irq(&mdev->req_lock); + + list_for_each_entry_safe(e, t, &reclaimed, w.list) + drbd_free_ee(mdev, e); + + /* possible callbacks here: + * e_end_block, and e_end_resync_block, e_send_discard_ack. + * all ignore the last argument. + */ + list_for_each_entry_safe(e, t, &work_list, w.list) { + /* list_del not necessary, next/prev members not touched */ + ok = e->w.cb(mdev, &e->w, !ok) && ok; + drbd_free_ee(mdev, e); + } + wake_up(&mdev->ee_wait); + + return ok; +} + +void _drbd_wait_ee_list_empty(struct drbd_conf *mdev, struct list_head *head) +{ + DEFINE_WAIT(wait); + + /* avoids spin_lock/unlock + * and calling prepare_to_wait in the fast path */ + while (!list_empty(head)) { + prepare_to_wait(&mdev->ee_wait, &wait, TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&mdev->req_lock); + drbd_kick_lo(mdev); + schedule(); + finish_wait(&mdev->ee_wait, &wait); + spin_lock_irq(&mdev->req_lock); + } +} + +void drbd_wait_ee_list_empty(struct drbd_conf *mdev, struct list_head *head) +{ + spin_lock_irq(&mdev->req_lock); + _drbd_wait_ee_list_empty(mdev, head); + spin_unlock_irq(&mdev->req_lock); +} + +/* see also kernel_accept; which is only present since 2.6.18. + * also we want to log which part of it failed, exactly */ +static int drbd_accept(struct drbd_conf *mdev, const char **what, + struct socket *sock, struct socket **newsock) +{ + struct sock *sk = sock->sk; + int err = 0; + + *what = "listen"; + err = sock->ops->listen(sock, 5); + if (err < 0) + goto out; + + *what = "sock_create_lite"; + err = sock_create_lite(sk->sk_family, sk->sk_type, sk->sk_protocol, + newsock); + if (err < 0) + goto out; + + *what = "accept"; + err = sock->ops->accept(sock, *newsock, 0); + if (err < 0) { + sock_release(*newsock); + *newsock = NULL; + goto out; + } + (*newsock)->ops = sock->ops; + +out: + return err; +} + +static int drbd_recv_short(struct drbd_conf *mdev, struct socket *sock, + void *buf, size_t size, int flags) +{ + mm_segment_t oldfs; + struct kvec iov = { + .iov_base = buf, + .iov_len = size, + }; + struct msghdr msg = { + .msg_iovlen = 1, + .msg_iov = (struct iovec *)&iov, + .msg_flags = (flags ? flags : MSG_WAITALL | MSG_NOSIGNAL) + }; + int rv; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + rv = sock_recvmsg(sock, &msg, size, msg.msg_flags); + set_fs(oldfs); + + return rv; +} + +static int drbd_recv(struct drbd_conf *mdev, void *buf, size_t size) +{ + mm_segment_t oldfs; + struct kvec iov = { + .iov_base = buf, + .iov_len = size, + }; + struct msghdr msg = { + .msg_iovlen = 1, + .msg_iov = (struct iovec *)&iov, + .msg_flags = MSG_WAITALL | MSG_NOSIGNAL + }; + int rv; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + for (;;) { + rv = sock_recvmsg(mdev->data.socket, &msg, size, msg.msg_flags); + if (rv == size) + break; + + /* Note: + * ECONNRESET other side closed the connection + * ERESTARTSYS (on sock) we got a signal + */ + + if (rv < 0) { + if (rv == -ECONNRESET) + dev_info(DEV, "sock was reset by peer\n"); + else if (rv != -ERESTARTSYS) + dev_err(DEV, "sock_recvmsg returned %d\n", rv); + break; + } else if (rv == 0) { + dev_info(DEV, "sock was shut down by peer\n"); + break; + } else { + /* signal came in, or peer/link went down, + * after we read a partial message + */ + /* D_ASSERT(signal_pending(current)); */ + break; + } + }; + + set_fs(oldfs); + + if (rv != size) + drbd_force_state(mdev, NS(conn, C_BROKEN_PIPE)); + + return rv; +} + +static struct socket *drbd_try_connect(struct drbd_conf *mdev) +{ + const char *what; + struct socket *sock; + struct sockaddr_in6 src_in6; + int err; + int disconnect_on_error = 1; + + if (!get_net_conf(mdev)) + return NULL; + + what = "sock_create_kern"; + err = sock_create_kern(((struct sockaddr *)mdev->net_conf->my_addr)->sa_family, + SOCK_STREAM, IPPROTO_TCP, &sock); + if (err < 0) { + sock = NULL; + goto out; + } + + sock->sk->sk_rcvtimeo = + sock->sk->sk_sndtimeo = mdev->net_conf->try_connect_int*HZ; + + /* explicitly bind to the configured IP as source IP + * for the outgoing connections. + * This is needed for multihomed hosts and to be + * able to use lo: interfaces for drbd. + * Make sure to use 0 as port number, so linux selects + * a free one dynamically. + */ + memcpy(&src_in6, mdev->net_conf->my_addr, + min_t(int, mdev->net_conf->my_addr_len, sizeof(src_in6))); + if (((struct sockaddr *)mdev->net_conf->my_addr)->sa_family == AF_INET6) + src_in6.sin6_port = 0; + else + ((struct sockaddr_in *)&src_in6)->sin_port = 0; /* AF_INET & AF_SCI */ + + what = "bind before connect"; + err = sock->ops->bind(sock, + (struct sockaddr *) &src_in6, + mdev->net_conf->my_addr_len); + if (err < 0) + goto out; + + /* connect may fail, peer not yet available. + * stay C_WF_CONNECTION, don't go Disconnecting! */ + disconnect_on_error = 0; + what = "connect"; + err = sock->ops->connect(sock, + (struct sockaddr *)mdev->net_conf->peer_addr, + mdev->net_conf->peer_addr_len, 0); + +out: + if (err < 0) { + if (sock) { + sock_release(sock); + sock = NULL; + } + switch (-err) { + /* timeout, busy, signal pending */ + case ETIMEDOUT: case EAGAIN: case EINPROGRESS: + case EINTR: case ERESTARTSYS: + /* peer not (yet) available, network problem */ + case ECONNREFUSED: case ENETUNREACH: + case EHOSTDOWN: case EHOSTUNREACH: + disconnect_on_error = 0; + break; + default: + dev_err(DEV, "%s failed, err = %d\n", what, err); + } + if (disconnect_on_error) + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + } + put_net_conf(mdev); + return sock; +} + +static struct socket *drbd_wait_for_connect(struct drbd_conf *mdev) +{ + int timeo, err; + struct socket *s_estab = NULL, *s_listen; + const char *what; + + if (!get_net_conf(mdev)) + return NULL; + + what = "sock_create_kern"; + err = sock_create_kern(((struct sockaddr *)mdev->net_conf->my_addr)->sa_family, + SOCK_STREAM, IPPROTO_TCP, &s_listen); + if (err) { + s_listen = NULL; + goto out; + } + + timeo = mdev->net_conf->try_connect_int * HZ; + timeo += (random32() & 1) ? timeo / 7 : -timeo / 7; /* 28.5% random jitter */ + + s_listen->sk->sk_reuse = 1; /* SO_REUSEADDR */ + s_listen->sk->sk_rcvtimeo = timeo; + s_listen->sk->sk_sndtimeo = timeo; + + what = "bind before listen"; + err = s_listen->ops->bind(s_listen, + (struct sockaddr *) mdev->net_conf->my_addr, + mdev->net_conf->my_addr_len); + if (err < 0) + goto out; + + err = drbd_accept(mdev, &what, s_listen, &s_estab); + +out: + if (s_listen) + sock_release(s_listen); + if (err < 0) { + if (err != -EAGAIN && err != -EINTR && err != -ERESTARTSYS) { + dev_err(DEV, "%s failed, err = %d\n", what, err); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + } + } + put_net_conf(mdev); + + return s_estab; +} + +static int drbd_send_fp(struct drbd_conf *mdev, + struct socket *sock, enum drbd_packets cmd) +{ + struct p_header *h = (struct p_header *) &mdev->data.sbuf.header; + + return _drbd_send_cmd(mdev, sock, cmd, h, sizeof(*h), 0); +} + +static enum drbd_packets drbd_recv_fp(struct drbd_conf *mdev, struct socket *sock) +{ + struct p_header *h = (struct p_header *) &mdev->data.sbuf.header; + int rr; + + rr = drbd_recv_short(mdev, sock, h, sizeof(*h), 0); + + if (rr == sizeof(*h) && h->magic == BE_DRBD_MAGIC) + return be16_to_cpu(h->command); + + return 0xffff; +} + +/** + * drbd_socket_okay() - Free the socket if its connection is not okay + * @mdev: DRBD device. + * @sock: pointer to the pointer to the socket. + */ +static int drbd_socket_okay(struct drbd_conf *mdev, struct socket **sock) +{ + int rr; + char tb[4]; + + if (!*sock) + return FALSE; + + rr = drbd_recv_short(mdev, *sock, tb, 4, MSG_DONTWAIT | MSG_PEEK); + + if (rr > 0 || rr == -EAGAIN) { + return TRUE; + } else { + sock_release(*sock); + *sock = NULL; + return FALSE; + } +} + +/* + * return values: + * 1 yes, we have a valid connection + * 0 oops, did not work out, please try again + * -1 peer talks different language, + * no point in trying again, please go standalone. + * -2 We do not have a network config... + */ +static int drbd_connect(struct drbd_conf *mdev) +{ + struct socket *s, *sock, *msock; + int try, h, ok; + + D_ASSERT(!mdev->data.socket); + + if (test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) + dev_err(DEV, "CREATE_BARRIER flag was set in drbd_connect - now cleared!\n"); + + if (drbd_request_state(mdev, NS(conn, C_WF_CONNECTION)) < SS_SUCCESS) + return -2; + + clear_bit(DISCARD_CONCURRENT, &mdev->flags); + + sock = NULL; + msock = NULL; + + do { + for (try = 0;;) { + /* 3 tries, this should take less than a second! */ + s = drbd_try_connect(mdev); + if (s || ++try >= 3) + break; + /* give the other side time to call bind() & listen() */ + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + } + + if (s) { + if (!sock) { + drbd_send_fp(mdev, s, P_HAND_SHAKE_S); + sock = s; + s = NULL; + } else if (!msock) { + drbd_send_fp(mdev, s, P_HAND_SHAKE_M); + msock = s; + s = NULL; + } else { + dev_err(DEV, "Logic error in drbd_connect()\n"); + goto out_release_sockets; + } + } + + if (sock && msock) { + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + ok = drbd_socket_okay(mdev, &sock); + ok = drbd_socket_okay(mdev, &msock) && ok; + if (ok) + break; + } + +retry: + s = drbd_wait_for_connect(mdev); + if (s) { + try = drbd_recv_fp(mdev, s); + drbd_socket_okay(mdev, &sock); + drbd_socket_okay(mdev, &msock); + switch (try) { + case P_HAND_SHAKE_S: + if (sock) { + dev_warn(DEV, "initial packet S crossed\n"); + sock_release(sock); + } + sock = s; + break; + case P_HAND_SHAKE_M: + if (msock) { + dev_warn(DEV, "initial packet M crossed\n"); + sock_release(msock); + } + msock = s; + set_bit(DISCARD_CONCURRENT, &mdev->flags); + break; + default: + dev_warn(DEV, "Error receiving initial packet\n"); + sock_release(s); + if (random32() & 1) + goto retry; + } + } + + if (mdev->state.conn <= C_DISCONNECTING) + goto out_release_sockets; + if (signal_pending(current)) { + flush_signals(current); + smp_rmb(); + if (get_t_state(&mdev->receiver) == Exiting) + goto out_release_sockets; + } + + if (sock && msock) { + ok = drbd_socket_okay(mdev, &sock); + ok = drbd_socket_okay(mdev, &msock) && ok; + if (ok) + break; + } + } while (1); + + msock->sk->sk_reuse = 1; /* SO_REUSEADDR */ + sock->sk->sk_reuse = 1; /* SO_REUSEADDR */ + + sock->sk->sk_allocation = GFP_NOIO; + msock->sk->sk_allocation = GFP_NOIO; + + sock->sk->sk_priority = TC_PRIO_INTERACTIVE_BULK; + msock->sk->sk_priority = TC_PRIO_INTERACTIVE; + + if (mdev->net_conf->sndbuf_size) { + sock->sk->sk_sndbuf = mdev->net_conf->sndbuf_size; + sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK; + } + + if (mdev->net_conf->rcvbuf_size) { + sock->sk->sk_rcvbuf = mdev->net_conf->rcvbuf_size; + sock->sk->sk_userlocks |= SOCK_RCVBUF_LOCK; + } + + /* NOT YET ... + * sock->sk->sk_sndtimeo = mdev->net_conf->timeout*HZ/10; + * sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; + * first set it to the P_HAND_SHAKE timeout, + * which we set to 4x the configured ping_timeout. */ + sock->sk->sk_sndtimeo = + sock->sk->sk_rcvtimeo = mdev->net_conf->ping_timeo*4*HZ/10; + + msock->sk->sk_sndtimeo = mdev->net_conf->timeout*HZ/10; + msock->sk->sk_rcvtimeo = mdev->net_conf->ping_int*HZ; + + /* we don't want delays. + * we use TCP_CORK where apropriate, though */ + drbd_tcp_nodelay(sock); + drbd_tcp_nodelay(msock); + + mdev->data.socket = sock; + mdev->meta.socket = msock; + mdev->last_received = jiffies; + + D_ASSERT(mdev->asender.task == NULL); + + h = drbd_do_handshake(mdev); + if (h <= 0) + return h; + + if (mdev->cram_hmac_tfm) { + /* drbd_request_state(mdev, NS(conn, WFAuth)); */ + if (!drbd_do_auth(mdev)) { + dev_err(DEV, "Authentication of peer failed\n"); + return -1; + } + } + + if (drbd_request_state(mdev, NS(conn, C_WF_REPORT_PARAMS)) < SS_SUCCESS) + return 0; + + sock->sk->sk_sndtimeo = mdev->net_conf->timeout*HZ/10; + sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; + + atomic_set(&mdev->packet_seq, 0); + mdev->peer_seq = 0; + + drbd_thread_start(&mdev->asender); + + drbd_send_protocol(mdev); + drbd_send_sync_param(mdev, &mdev->sync_conf); + drbd_send_sizes(mdev, 0); + drbd_send_uuids(mdev); + drbd_send_state(mdev); + clear_bit(USE_DEGR_WFC_T, &mdev->flags); + clear_bit(RESIZE_PENDING, &mdev->flags); + + return 1; + +out_release_sockets: + if (sock) + sock_release(sock); + if (msock) + sock_release(msock); + return -1; +} + +static int drbd_recv_header(struct drbd_conf *mdev, struct p_header *h) +{ + int r; + + r = drbd_recv(mdev, h, sizeof(*h)); + + if (unlikely(r != sizeof(*h))) { + dev_err(DEV, "short read expecting header on sock: r=%d\n", r); + return FALSE; + }; + h->command = be16_to_cpu(h->command); + h->length = be16_to_cpu(h->length); + if (unlikely(h->magic != BE_DRBD_MAGIC)) { + dev_err(DEV, "magic?? on data m: 0x%lx c: %d l: %d\n", + (long)be32_to_cpu(h->magic), + h->command, h->length); + return FALSE; + } + mdev->last_received = jiffies; + + return TRUE; +} + +static enum finish_epoch drbd_flush_after_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch) +{ + int rv; + + if (mdev->write_ordering >= WO_bdev_flush && get_ldev(mdev)) { + rv = blkdev_issue_flush(mdev->ldev->backing_bdev, NULL); + if (rv) { + dev_err(DEV, "local disk flush failed with status %d\n", rv); + /* would rather check on EOPNOTSUPP, but that is not reliable. + * don't try again for ANY return value != 0 + * if (rv == -EOPNOTSUPP) */ + drbd_bump_write_ordering(mdev, WO_drain_io); + } + put_ldev(mdev); + } + + return drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE); +} + +static int w_flush(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct flush_work *fw = (struct flush_work *)w; + struct drbd_epoch *epoch = fw->epoch; + + kfree(w); + + if (!test_and_set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags)) + drbd_flush_after_epoch(mdev, epoch); + + drbd_may_finish_epoch(mdev, epoch, EV_PUT | + (mdev->state.conn < C_CONNECTED ? EV_CLEANUP : 0)); + + return 1; +} + +/** + * drbd_may_finish_epoch() - Applies an epoch_event to the epoch's state, eventually finishes it. + * @mdev: DRBD device. + * @epoch: Epoch object. + * @ev: Epoch event. + */ +static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev, + struct drbd_epoch *epoch, + enum epoch_event ev) +{ + int finish, epoch_size; + struct drbd_epoch *next_epoch; + int schedule_flush = 0; + enum finish_epoch rv = FE_STILL_LIVE; + + spin_lock(&mdev->epoch_lock); + do { + next_epoch = NULL; + finish = 0; + + epoch_size = atomic_read(&epoch->epoch_size); + + switch (ev & ~EV_CLEANUP) { + case EV_PUT: + atomic_dec(&epoch->active); + break; + case EV_GOT_BARRIER_NR: + set_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags); + + /* Special case: If we just switched from WO_bio_barrier to + WO_bdev_flush we should not finish the current epoch */ + if (test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags) && epoch_size == 1 && + mdev->write_ordering != WO_bio_barrier && + epoch == mdev->current_epoch) + clear_bit(DE_CONTAINS_A_BARRIER, &epoch->flags); + break; + case EV_BARRIER_DONE: + set_bit(DE_BARRIER_IN_NEXT_EPOCH_DONE, &epoch->flags); + break; + case EV_BECAME_LAST: + /* nothing to do*/ + break; + } + + if (epoch_size != 0 && + atomic_read(&epoch->active) == 0 && + test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags) && + epoch->list.prev == &mdev->current_epoch->list && + !test_bit(DE_IS_FINISHING, &epoch->flags)) { + /* Nearly all conditions are met to finish that epoch... */ + if (test_bit(DE_BARRIER_IN_NEXT_EPOCH_DONE, &epoch->flags) || + mdev->write_ordering == WO_none || + (epoch_size == 1 && test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) || + ev & EV_CLEANUP) { + finish = 1; + set_bit(DE_IS_FINISHING, &epoch->flags); + } else if (!test_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags) && + mdev->write_ordering == WO_bio_barrier) { + atomic_inc(&epoch->active); + schedule_flush = 1; + } + } + if (finish) { + if (!(ev & EV_CLEANUP)) { + spin_unlock(&mdev->epoch_lock); + drbd_send_b_ack(mdev, epoch->barrier_nr, epoch_size); + spin_lock(&mdev->epoch_lock); + } + dec_unacked(mdev); + + if (mdev->current_epoch != epoch) { + next_epoch = list_entry(epoch->list.next, struct drbd_epoch, list); + list_del(&epoch->list); + ev = EV_BECAME_LAST | (ev & EV_CLEANUP); + mdev->epochs--; + kfree(epoch); + + if (rv == FE_STILL_LIVE) + rv = FE_DESTROYED; + } else { + epoch->flags = 0; + atomic_set(&epoch->epoch_size, 0); + /* atomic_set(&epoch->active, 0); is alrady zero */ + if (rv == FE_STILL_LIVE) + rv = FE_RECYCLED; + } + } + + if (!next_epoch) + break; + + epoch = next_epoch; + } while (1); + + spin_unlock(&mdev->epoch_lock); + + if (schedule_flush) { + struct flush_work *fw; + fw = kmalloc(sizeof(*fw), GFP_ATOMIC); + if (fw) { + fw->w.cb = w_flush; + fw->epoch = epoch; + drbd_queue_work(&mdev->data.work, &fw->w); + } else { + dev_warn(DEV, "Could not kmalloc a flush_work obj\n"); + set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags); + /* That is not a recursion, only one level */ + drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE); + drbd_may_finish_epoch(mdev, epoch, EV_PUT); + } + } + + return rv; +} + +/** + * drbd_bump_write_ordering() - Fall back to an other write ordering method + * @mdev: DRBD device. + * @wo: Write ordering method to try. + */ +void drbd_bump_write_ordering(struct drbd_conf *mdev, enum write_ordering_e wo) __must_hold(local) +{ + enum write_ordering_e pwo; + static char *write_ordering_str[] = { + [WO_none] = "none", + [WO_drain_io] = "drain", + [WO_bdev_flush] = "flush", + [WO_bio_barrier] = "barrier", + }; + + pwo = mdev->write_ordering; + wo = min(pwo, wo); + if (wo == WO_bio_barrier && mdev->ldev->dc.no_disk_barrier) + wo = WO_bdev_flush; + if (wo == WO_bdev_flush && mdev->ldev->dc.no_disk_flush) + wo = WO_drain_io; + if (wo == WO_drain_io && mdev->ldev->dc.no_disk_drain) + wo = WO_none; + mdev->write_ordering = wo; + if (pwo != mdev->write_ordering || wo == WO_bio_barrier) + dev_info(DEV, "Method to ensure write ordering: %s\n", write_ordering_str[mdev->write_ordering]); +} + +/** + * w_e_reissue() - Worker callback; Resubmit a bio, without BIO_RW_BARRIER set + * @mdev: DRBD device. + * @w: work object. + * @cancel: The connection will be closed anyways (unused in this callback) + */ +int w_e_reissue(struct drbd_conf *mdev, struct drbd_work *w, int cancel) __releases(local) +{ + struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w; + struct bio *bio = e->private_bio; + + /* We leave DE_CONTAINS_A_BARRIER and EE_IS_BARRIER in place, + (and DE_BARRIER_IN_NEXT_EPOCH_ISSUED in the previous Epoch) + so that we can finish that epoch in drbd_may_finish_epoch(). + That is necessary if we already have a long chain of Epochs, before + we realize that BIO_RW_BARRIER is actually not supported */ + + /* As long as the -ENOTSUPP on the barrier is reported immediately + that will never trigger. If it is reported late, we will just + print that warning and continue correctly for all future requests + with WO_bdev_flush */ + if (previous_epoch(mdev, e->epoch)) + dev_warn(DEV, "Write ordering was not enforced (one time event)\n"); + + /* prepare bio for re-submit, + * re-init volatile members */ + /* we still have a local reference, + * get_ldev was done in receive_Data. */ + bio->bi_bdev = mdev->ldev->backing_bdev; + bio->bi_sector = e->sector; + bio->bi_size = e->size; + bio->bi_idx = 0; + + bio->bi_flags &= ~(BIO_POOL_MASK - 1); + bio->bi_flags |= 1 << BIO_UPTODATE; + + /* don't know whether this is necessary: */ + bio->bi_phys_segments = 0; + bio->bi_next = NULL; + + /* these should be unchanged: */ + /* bio->bi_end_io = drbd_endio_write_sec; */ + /* bio->bi_vcnt = whatever; */ + + e->w.cb = e_end_block; + + /* This is no longer a barrier request. */ + bio->bi_rw &= ~(1UL << BIO_RW_BARRIER); + + drbd_generic_make_request(mdev, DRBD_FAULT_DT_WR, bio); + + return 1; +} + +static int receive_Barrier(struct drbd_conf *mdev, struct p_header *h) +{ + int rv, issue_flush; + struct p_barrier *p = (struct p_barrier *)h; + struct drbd_epoch *epoch; + + ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE; + + rv = drbd_recv(mdev, h->payload, h->length); + ERR_IF(rv != h->length) return FALSE; + + inc_unacked(mdev); + + if (mdev->net_conf->wire_protocol != DRBD_PROT_C) + drbd_kick_lo(mdev); + + mdev->current_epoch->barrier_nr = p->barrier; + rv = drbd_may_finish_epoch(mdev, mdev->current_epoch, EV_GOT_BARRIER_NR); + + /* P_BARRIER_ACK may imply that the corresponding extent is dropped from + * the activity log, which means it would not be resynced in case the + * R_PRIMARY crashes now. + * Therefore we must send the barrier_ack after the barrier request was + * completed. */ + switch (mdev->write_ordering) { + case WO_bio_barrier: + case WO_none: + if (rv == FE_RECYCLED) + return TRUE; + break; + + case WO_bdev_flush: + case WO_drain_io: + D_ASSERT(rv == FE_STILL_LIVE); + set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &mdev->current_epoch->flags); + drbd_wait_ee_list_empty(mdev, &mdev->active_ee); + rv = drbd_flush_after_epoch(mdev, mdev->current_epoch); + if (rv == FE_RECYCLED) + return TRUE; + + /* The asender will send all the ACKs and barrier ACKs out, since + all EEs moved from the active_ee to the done_ee. We need to + provide a new epoch object for the EEs that come in soon */ + break; + } + + /* receiver context, in the writeout path of the other node. + * avoid potential distributed deadlock */ + epoch = kmalloc(sizeof(struct drbd_epoch), GFP_NOIO); + if (!epoch) { + dev_warn(DEV, "Allocation of an epoch failed, slowing down\n"); + issue_flush = !test_and_set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags); + drbd_wait_ee_list_empty(mdev, &mdev->active_ee); + if (issue_flush) { + rv = drbd_flush_after_epoch(mdev, mdev->current_epoch); + if (rv == FE_RECYCLED) + return TRUE; + } + + drbd_wait_ee_list_empty(mdev, &mdev->done_ee); + + return TRUE; + } + + epoch->flags = 0; + atomic_set(&epoch->epoch_size, 0); + atomic_set(&epoch->active, 0); + + spin_lock(&mdev->epoch_lock); + if (atomic_read(&mdev->current_epoch->epoch_size)) { + list_add(&epoch->list, &mdev->current_epoch->list); + mdev->current_epoch = epoch; + mdev->epochs++; + } else { + /* The current_epoch got recycled while we allocated this one... */ + kfree(epoch); + } + spin_unlock(&mdev->epoch_lock); + + return TRUE; +} + +/* used from receive_RSDataReply (recv_resync_read) + * and from receive_Data */ +static struct drbd_epoch_entry * +read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __must_hold(local) +{ + struct drbd_epoch_entry *e; + struct bio_vec *bvec; + struct page *page; + struct bio *bio; + int dgs, ds, i, rr; + void *dig_in = mdev->int_dig_in; + void *dig_vv = mdev->int_dig_vv; + + dgs = (mdev->agreed_pro_version >= 87 && mdev->integrity_r_tfm) ? + crypto_hash_digestsize(mdev->integrity_r_tfm) : 0; + + if (dgs) { + rr = drbd_recv(mdev, dig_in, dgs); + if (rr != dgs) { + dev_warn(DEV, "short read receiving data digest: read %d expected %d\n", + rr, dgs); + return NULL; + } + } + + data_size -= dgs; + + ERR_IF(data_size & 0x1ff) return NULL; + ERR_IF(data_size > DRBD_MAX_SEGMENT_SIZE) return NULL; + + /* GFP_NOIO, because we must not cause arbitrary write-out: in a DRBD + * "criss-cross" setup, that might cause write-out on some other DRBD, + * which in turn might block on the other node at this very place. */ + e = drbd_alloc_ee(mdev, id, sector, data_size, GFP_NOIO); + if (!e) + return NULL; + bio = e->private_bio; + ds = data_size; + bio_for_each_segment(bvec, bio, i) { + page = bvec->bv_page; + rr = drbd_recv(mdev, kmap(page), min_t(int, ds, PAGE_SIZE)); + kunmap(page); + if (rr != min_t(int, ds, PAGE_SIZE)) { + drbd_free_ee(mdev, e); + dev_warn(DEV, "short read receiving data: read %d expected %d\n", + rr, min_t(int, ds, PAGE_SIZE)); + return NULL; + } + ds -= rr; + } + + if (dgs) { + drbd_csum(mdev, mdev->integrity_r_tfm, bio, dig_vv); + if (memcmp(dig_in, dig_vv, dgs)) { + dev_err(DEV, "Digest integrity check FAILED.\n"); + drbd_bcast_ee(mdev, "digest failed", + dgs, dig_in, dig_vv, e); + drbd_free_ee(mdev, e); + return NULL; + } + } + mdev->recv_cnt += data_size>>9; + return e; +} + +/* drbd_drain_block() just takes a data block + * out of the socket input buffer, and discards it. + */ +static int drbd_drain_block(struct drbd_conf *mdev, int data_size) +{ + struct page *page; + int rr, rv = 1; + void *data; + + page = drbd_pp_alloc(mdev, 1); + + data = kmap(page); + while (data_size) { + rr = drbd_recv(mdev, data, min_t(int, data_size, PAGE_SIZE)); + if (rr != min_t(int, data_size, PAGE_SIZE)) { + rv = 0; + dev_warn(DEV, "short read receiving data: read %d expected %d\n", + rr, min_t(int, data_size, PAGE_SIZE)); + break; + } + data_size -= rr; + } + kunmap(page); + drbd_pp_free(mdev, page); + return rv; +} + +static int recv_dless_read(struct drbd_conf *mdev, struct drbd_request *req, + sector_t sector, int data_size) +{ + struct bio_vec *bvec; + struct bio *bio; + int dgs, rr, i, expect; + void *dig_in = mdev->int_dig_in; + void *dig_vv = mdev->int_dig_vv; + + dgs = (mdev->agreed_pro_version >= 87 && mdev->integrity_r_tfm) ? + crypto_hash_digestsize(mdev->integrity_r_tfm) : 0; + + if (dgs) { + rr = drbd_recv(mdev, dig_in, dgs); + if (rr != dgs) { + dev_warn(DEV, "short read receiving data reply digest: read %d expected %d\n", + rr, dgs); + return 0; + } + } + + data_size -= dgs; + + /* optimistically update recv_cnt. if receiving fails below, + * we disconnect anyways, and counters will be reset. */ + mdev->recv_cnt += data_size>>9; + + bio = req->master_bio; + D_ASSERT(sector == bio->bi_sector); + + bio_for_each_segment(bvec, bio, i) { + expect = min_t(int, data_size, bvec->bv_len); + rr = drbd_recv(mdev, + kmap(bvec->bv_page)+bvec->bv_offset, + expect); + kunmap(bvec->bv_page); + if (rr != expect) { + dev_warn(DEV, "short read receiving data reply: " + "read %d expected %d\n", + rr, expect); + return 0; + } + data_size -= rr; + } + + if (dgs) { + drbd_csum(mdev, mdev->integrity_r_tfm, bio, dig_vv); + if (memcmp(dig_in, dig_vv, dgs)) { + dev_err(DEV, "Digest integrity check FAILED. Broken NICs?\n"); + return 0; + } + } + + D_ASSERT(data_size == 0); + return 1; +} + +/* e_end_resync_block() is called via + * drbd_process_done_ee() by asender only */ +static int e_end_resync_block(struct drbd_conf *mdev, struct drbd_work *w, int unused) +{ + struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w; + sector_t sector = e->sector; + int ok; + + D_ASSERT(hlist_unhashed(&e->colision)); + + if (likely(drbd_bio_uptodate(e->private_bio))) { + drbd_set_in_sync(mdev, sector, e->size); + ok = drbd_send_ack(mdev, P_RS_WRITE_ACK, e); + } else { + /* Record failure to sync */ + drbd_rs_failed_io(mdev, sector, e->size); + + ok = drbd_send_ack(mdev, P_NEG_ACK, e); + } + dec_unacked(mdev); + + return ok; +} + +static int recv_resync_read(struct drbd_conf *mdev, sector_t sector, int data_size) __releases(local) +{ + struct drbd_epoch_entry *e; + + e = read_in_block(mdev, ID_SYNCER, sector, data_size); + if (!e) { + put_ldev(mdev); + return FALSE; + } + + dec_rs_pending(mdev); + + e->private_bio->bi_end_io = drbd_endio_write_sec; + e->private_bio->bi_rw = WRITE; + e->w.cb = e_end_resync_block; + + inc_unacked(mdev); + /* corresponding dec_unacked() in e_end_resync_block() + * respective _drbd_clear_done_ee */ + + spin_lock_irq(&mdev->req_lock); + list_add(&e->w.list, &mdev->sync_ee); + spin_unlock_irq(&mdev->req_lock); + + drbd_generic_make_request(mdev, DRBD_FAULT_RS_WR, e->private_bio); + /* accounting done in endio */ + + maybe_kick_lo(mdev); + return TRUE; +} + +static int receive_DataReply(struct drbd_conf *mdev, struct p_header *h) +{ + struct drbd_request *req; + sector_t sector; + unsigned int header_size, data_size; + int ok; + struct p_data *p = (struct p_data *)h; + + header_size = sizeof(*p) - sizeof(*h); + data_size = h->length - header_size; + + ERR_IF(data_size == 0) return FALSE; + + if (drbd_recv(mdev, h->payload, header_size) != header_size) + return FALSE; + + sector = be64_to_cpu(p->sector); + + spin_lock_irq(&mdev->req_lock); + req = _ar_id_to_req(mdev, p->block_id, sector); + spin_unlock_irq(&mdev->req_lock); + if (unlikely(!req)) { + dev_err(DEV, "Got a corrupt block_id/sector pair(1).\n"); + return FALSE; + } + + /* hlist_del(&req->colision) is done in _req_may_be_done, to avoid + * special casing it there for the various failure cases. + * still no race with drbd_fail_pending_reads */ + ok = recv_dless_read(mdev, req, sector, data_size); + + if (ok) + req_mod(req, data_received); + /* else: nothing. handled from drbd_disconnect... + * I don't think we may complete this just yet + * in case we are "on-disconnect: freeze" */ + + return ok; +} + +static int receive_RSDataReply(struct drbd_conf *mdev, struct p_header *h) +{ + sector_t sector; + unsigned int header_size, data_size; + int ok; + struct p_data *p = (struct p_data *)h; + + header_size = sizeof(*p) - sizeof(*h); + data_size = h->length - header_size; + + ERR_IF(data_size == 0) return FALSE; + + if (drbd_recv(mdev, h->payload, header_size) != header_size) + return FALSE; + + sector = be64_to_cpu(p->sector); + D_ASSERT(p->block_id == ID_SYNCER); + + if (get_ldev(mdev)) { + /* data is submitted to disk within recv_resync_read. + * corresponding put_ldev done below on error, + * or in drbd_endio_write_sec. */ + ok = recv_resync_read(mdev, sector, data_size); + } else { + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Can not write resync data to local disk.\n"); + + ok = drbd_drain_block(mdev, data_size); + + drbd_send_ack_dp(mdev, P_NEG_ACK, p); + } + + return ok; +} + +/* e_end_block() is called via drbd_process_done_ee(). + * this means this function only runs in the asender thread + */ +static int e_end_block(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w; + sector_t sector = e->sector; + struct drbd_epoch *epoch; + int ok = 1, pcmd; + + if (e->flags & EE_IS_BARRIER) { + epoch = previous_epoch(mdev, e->epoch); + if (epoch) + drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE + (cancel ? EV_CLEANUP : 0)); + } + + if (mdev->net_conf->wire_protocol == DRBD_PROT_C) { + if (likely(drbd_bio_uptodate(e->private_bio))) { + pcmd = (mdev->state.conn >= C_SYNC_SOURCE && + mdev->state.conn <= C_PAUSED_SYNC_T && + e->flags & EE_MAY_SET_IN_SYNC) ? + P_RS_WRITE_ACK : P_WRITE_ACK; + ok &= drbd_send_ack(mdev, pcmd, e); + if (pcmd == P_RS_WRITE_ACK) + drbd_set_in_sync(mdev, sector, e->size); + } else { + ok = drbd_send_ack(mdev, P_NEG_ACK, e); + /* we expect it to be marked out of sync anyways... + * maybe assert this? */ + } + dec_unacked(mdev); + } + /* we delete from the conflict detection hash _after_ we sent out the + * P_WRITE_ACK / P_NEG_ACK, to get the sequence number right. */ + if (mdev->net_conf->two_primaries) { + spin_lock_irq(&mdev->req_lock); + D_ASSERT(!hlist_unhashed(&e->colision)); + hlist_del_init(&e->colision); + spin_unlock_irq(&mdev->req_lock); + } else { + D_ASSERT(hlist_unhashed(&e->colision)); + } + + drbd_may_finish_epoch(mdev, e->epoch, EV_PUT + (cancel ? EV_CLEANUP : 0)); + + return ok; +} + +static int e_send_discard_ack(struct drbd_conf *mdev, struct drbd_work *w, int unused) +{ + struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w; + int ok = 1; + + D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C); + ok = drbd_send_ack(mdev, P_DISCARD_ACK, e); + + spin_lock_irq(&mdev->req_lock); + D_ASSERT(!hlist_unhashed(&e->colision)); + hlist_del_init(&e->colision); + spin_unlock_irq(&mdev->req_lock); + + dec_unacked(mdev); + + return ok; +} + +/* Called from receive_Data. + * Synchronize packets on sock with packets on msock. + * + * This is here so even when a P_DATA packet traveling via sock overtook an Ack + * packet traveling on msock, they are still processed in the order they have + * been sent. + * + * Note: we don't care for Ack packets overtaking P_DATA packets. + * + * In case packet_seq is larger than mdev->peer_seq number, there are + * outstanding packets on the msock. We wait for them to arrive. + * In case we are the logically next packet, we update mdev->peer_seq + * ourselves. Correctly handles 32bit wrap around. + * + * Assume we have a 10 GBit connection, that is about 1<<30 byte per second, + * about 1<<21 sectors per second. So "worst" case, we have 1<<3 == 8 seconds + * for the 24bit wrap (historical atomic_t guarantee on some archs), and we have + * 1<<9 == 512 seconds aka ages for the 32bit wrap around... + * + * returns 0 if we may process the packet, + * -ERESTARTSYS if we were interrupted (by disconnect signal). */ +static int drbd_wait_peer_seq(struct drbd_conf *mdev, const u32 packet_seq) +{ + DEFINE_WAIT(wait); + unsigned int p_seq; + long timeout; + int ret = 0; + spin_lock(&mdev->peer_seq_lock); + for (;;) { + prepare_to_wait(&mdev->seq_wait, &wait, TASK_INTERRUPTIBLE); + if (seq_le(packet_seq, mdev->peer_seq+1)) + break; + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + p_seq = mdev->peer_seq; + spin_unlock(&mdev->peer_seq_lock); + timeout = schedule_timeout(30*HZ); + spin_lock(&mdev->peer_seq_lock); + if (timeout == 0 && p_seq == mdev->peer_seq) { + ret = -ETIMEDOUT; + dev_err(DEV, "ASSERT FAILED waited 30 seconds for sequence update, forcing reconnect\n"); + break; + } + } + finish_wait(&mdev->seq_wait, &wait); + if (mdev->peer_seq+1 == packet_seq) + mdev->peer_seq++; + spin_unlock(&mdev->peer_seq_lock); + return ret; +} + +/* mirrored write */ +static int receive_Data(struct drbd_conf *mdev, struct p_header *h) +{ + sector_t sector; + struct drbd_epoch_entry *e; + struct p_data *p = (struct p_data *)h; + int header_size, data_size; + int rw = WRITE; + u32 dp_flags; + + header_size = sizeof(*p) - sizeof(*h); + data_size = h->length - header_size; + + ERR_IF(data_size == 0) return FALSE; + + if (drbd_recv(mdev, h->payload, header_size) != header_size) + return FALSE; + + if (!get_ldev(mdev)) { + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Can not write mirrored data block " + "to local disk.\n"); + spin_lock(&mdev->peer_seq_lock); + if (mdev->peer_seq+1 == be32_to_cpu(p->seq_num)) + mdev->peer_seq++; + spin_unlock(&mdev->peer_seq_lock); + + drbd_send_ack_dp(mdev, P_NEG_ACK, p); + atomic_inc(&mdev->current_epoch->epoch_size); + return drbd_drain_block(mdev, data_size); + } + + /* get_ldev(mdev) successful. + * Corresponding put_ldev done either below (on various errors), + * or in drbd_endio_write_sec, if we successfully submit the data at + * the end of this function. */ + + sector = be64_to_cpu(p->sector); + e = read_in_block(mdev, p->block_id, sector, data_size); + if (!e) { + put_ldev(mdev); + return FALSE; + } + + e->private_bio->bi_end_io = drbd_endio_write_sec; + e->w.cb = e_end_block; + + spin_lock(&mdev->epoch_lock); + e->epoch = mdev->current_epoch; + atomic_inc(&e->epoch->epoch_size); + atomic_inc(&e->epoch->active); + + if (mdev->write_ordering == WO_bio_barrier && atomic_read(&e->epoch->epoch_size) == 1) { + struct drbd_epoch *epoch; + /* Issue a barrier if we start a new epoch, and the previous epoch + was not a epoch containing a single request which already was + a Barrier. */ + epoch = list_entry(e->epoch->list.prev, struct drbd_epoch, list); + if (epoch == e->epoch) { + set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags); + rw |= (1<<BIO_RW_BARRIER); + e->flags |= EE_IS_BARRIER; + } else { + if (atomic_read(&epoch->epoch_size) > 1 || + !test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) { + set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags); + set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags); + rw |= (1<<BIO_RW_BARRIER); + e->flags |= EE_IS_BARRIER; + } + } + } + spin_unlock(&mdev->epoch_lock); + + dp_flags = be32_to_cpu(p->dp_flags); + if (dp_flags & DP_HARDBARRIER) { + dev_err(DEV, "ASSERT FAILED would have submitted barrier request\n"); + /* rw |= (1<<BIO_RW_BARRIER); */ + } + if (dp_flags & DP_RW_SYNC) + rw |= (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG); + if (dp_flags & DP_MAY_SET_IN_SYNC) + e->flags |= EE_MAY_SET_IN_SYNC; + + /* I'm the receiver, I do hold a net_cnt reference. */ + if (!mdev->net_conf->two_primaries) { + spin_lock_irq(&mdev->req_lock); + } else { + /* don't get the req_lock yet, + * we may sleep in drbd_wait_peer_seq */ + const int size = e->size; + const int discard = test_bit(DISCARD_CONCURRENT, &mdev->flags); + DEFINE_WAIT(wait); + struct drbd_request *i; + struct hlist_node *n; + struct hlist_head *slot; + int first; + + D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C); + BUG_ON(mdev->ee_hash == NULL); + BUG_ON(mdev->tl_hash == NULL); + + /* conflict detection and handling: + * 1. wait on the sequence number, + * in case this data packet overtook ACK packets. + * 2. check our hash tables for conflicting requests. + * we only need to walk the tl_hash, since an ee can not + * have a conflict with an other ee: on the submitting + * node, the corresponding req had already been conflicting, + * and a conflicting req is never sent. + * + * Note: for two_primaries, we are protocol C, + * so there cannot be any request that is DONE + * but still on the transfer log. + * + * unconditionally add to the ee_hash. + * + * if no conflicting request is found: + * submit. + * + * if any conflicting request is found + * that has not yet been acked, + * AND I have the "discard concurrent writes" flag: + * queue (via done_ee) the P_DISCARD_ACK; OUT. + * + * if any conflicting request is found: + * block the receiver, waiting on misc_wait + * until no more conflicting requests are there, + * or we get interrupted (disconnect). + * + * we do not just write after local io completion of those + * requests, but only after req is done completely, i.e. + * we wait for the P_DISCARD_ACK to arrive! + * + * then proceed normally, i.e. submit. + */ + if (drbd_wait_peer_seq(mdev, be32_to_cpu(p->seq_num))) + goto out_interrupted; + + spin_lock_irq(&mdev->req_lock); + + hlist_add_head(&e->colision, ee_hash_slot(mdev, sector)); + +#define OVERLAPS overlaps(i->sector, i->size, sector, size) + slot = tl_hash_slot(mdev, sector); + first = 1; + for (;;) { + int have_unacked = 0; + int have_conflict = 0; + prepare_to_wait(&mdev->misc_wait, &wait, + TASK_INTERRUPTIBLE); + hlist_for_each_entry(i, n, slot, colision) { + if (OVERLAPS) { + /* only ALERT on first iteration, + * we may be woken up early... */ + if (first) + dev_alert(DEV, "%s[%u] Concurrent local write detected!" + " new: %llus +%u; pending: %llus +%u\n", + current->comm, current->pid, + (unsigned long long)sector, size, + (unsigned long long)i->sector, i->size); + if (i->rq_state & RQ_NET_PENDING) + ++have_unacked; + ++have_conflict; + } + } +#undef OVERLAPS + if (!have_conflict) + break; + + /* Discard Ack only for the _first_ iteration */ + if (first && discard && have_unacked) { + dev_alert(DEV, "Concurrent write! [DISCARD BY FLAG] sec=%llus\n", + (unsigned long long)sector); + inc_unacked(mdev); + e->w.cb = e_send_discard_ack; + list_add_tail(&e->w.list, &mdev->done_ee); + + spin_unlock_irq(&mdev->req_lock); + + /* we could probably send that P_DISCARD_ACK ourselves, + * but I don't like the receiver using the msock */ + + put_ldev(mdev); + wake_asender(mdev); + finish_wait(&mdev->misc_wait, &wait); + return TRUE; + } + + if (signal_pending(current)) { + hlist_del_init(&e->colision); + + spin_unlock_irq(&mdev->req_lock); + + finish_wait(&mdev->misc_wait, &wait); + goto out_interrupted; + } + + spin_unlock_irq(&mdev->req_lock); + if (first) { + first = 0; + dev_alert(DEV, "Concurrent write! [W AFTERWARDS] " + "sec=%llus\n", (unsigned long long)sector); + } else if (discard) { + /* we had none on the first iteration. + * there must be none now. */ + D_ASSERT(have_unacked == 0); + } + schedule(); + spin_lock_irq(&mdev->req_lock); + } + finish_wait(&mdev->misc_wait, &wait); + } + + list_add(&e->w.list, &mdev->active_ee); + spin_unlock_irq(&mdev->req_lock); + + switch (mdev->net_conf->wire_protocol) { + case DRBD_PROT_C: + inc_unacked(mdev); + /* corresponding dec_unacked() in e_end_block() + * respective _drbd_clear_done_ee */ + break; + case DRBD_PROT_B: + /* I really don't like it that the receiver thread + * sends on the msock, but anyways */ + drbd_send_ack(mdev, P_RECV_ACK, e); + break; + case DRBD_PROT_A: + /* nothing to do */ + break; + } + + if (mdev->state.pdsk == D_DISKLESS) { + /* In case we have the only disk of the cluster, */ + drbd_set_out_of_sync(mdev, e->sector, e->size); + e->flags |= EE_CALL_AL_COMPLETE_IO; + drbd_al_begin_io(mdev, e->sector); + } + + e->private_bio->bi_rw = rw; + drbd_generic_make_request(mdev, DRBD_FAULT_DT_WR, e->private_bio); + /* accounting done in endio */ + + maybe_kick_lo(mdev); + return TRUE; + +out_interrupted: + /* yes, the epoch_size now is imbalanced. + * but we drop the connection anyways, so we don't have a chance to + * receive a barrier... atomic_inc(&mdev->epoch_size); */ + put_ldev(mdev); + drbd_free_ee(mdev, e); + return FALSE; +} + +static int receive_DataRequest(struct drbd_conf *mdev, struct p_header *h) +{ + sector_t sector; + const sector_t capacity = drbd_get_capacity(mdev->this_bdev); + struct drbd_epoch_entry *e; + struct digest_info *di = NULL; + int size, digest_size; + unsigned int fault_type; + struct p_block_req *p = + (struct p_block_req *)h; + const int brps = sizeof(*p)-sizeof(*h); + + if (drbd_recv(mdev, h->payload, brps) != brps) + return FALSE; + + sector = be64_to_cpu(p->sector); + size = be32_to_cpu(p->blksize); + + if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_SEGMENT_SIZE) { + dev_err(DEV, "%s:%d: sector: %llus, size: %u\n", __FILE__, __LINE__, + (unsigned long long)sector, size); + return FALSE; + } + if (sector + (size>>9) > capacity) { + dev_err(DEV, "%s:%d: sector: %llus, size: %u\n", __FILE__, __LINE__, + (unsigned long long)sector, size); + return FALSE; + } + + if (!get_ldev_if_state(mdev, D_UP_TO_DATE)) { + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Can not satisfy peer's read request, " + "no local data.\n"); + drbd_send_ack_rp(mdev, h->command == P_DATA_REQUEST ? P_NEG_DREPLY : + P_NEG_RS_DREPLY , p); + return TRUE; + } + + /* GFP_NOIO, because we must not cause arbitrary write-out: in a DRBD + * "criss-cross" setup, that might cause write-out on some other DRBD, + * which in turn might block on the other node at this very place. */ + e = drbd_alloc_ee(mdev, p->block_id, sector, size, GFP_NOIO); + if (!e) { + put_ldev(mdev); + return FALSE; + } + + e->private_bio->bi_rw = READ; + e->private_bio->bi_end_io = drbd_endio_read_sec; + + switch (h->command) { + case P_DATA_REQUEST: + e->w.cb = w_e_end_data_req; + fault_type = DRBD_FAULT_DT_RD; + break; + case P_RS_DATA_REQUEST: + e->w.cb = w_e_end_rsdata_req; + fault_type = DRBD_FAULT_RS_RD; + /* Eventually this should become asynchronously. Currently it + * blocks the whole receiver just to delay the reading of a + * resync data block. + * the drbd_work_queue mechanism is made for this... + */ + if (!drbd_rs_begin_io(mdev, sector)) { + /* we have been interrupted, + * probably connection lost! */ + D_ASSERT(signal_pending(current)); + goto out_free_e; + } + break; + + case P_OV_REPLY: + case P_CSUM_RS_REQUEST: + fault_type = DRBD_FAULT_RS_RD; + digest_size = h->length - brps ; + di = kmalloc(sizeof(*di) + digest_size, GFP_NOIO); + if (!di) + goto out_free_e; + + di->digest_size = digest_size; + di->digest = (((char *)di)+sizeof(struct digest_info)); + + if (drbd_recv(mdev, di->digest, digest_size) != digest_size) + goto out_free_e; + + e->block_id = (u64)(unsigned long)di; + if (h->command == P_CSUM_RS_REQUEST) { + D_ASSERT(mdev->agreed_pro_version >= 89); + e->w.cb = w_e_end_csum_rs_req; + } else if (h->command == P_OV_REPLY) { + e->w.cb = w_e_end_ov_reply; + dec_rs_pending(mdev); + break; + } + + if (!drbd_rs_begin_io(mdev, sector)) { + /* we have been interrupted, probably connection lost! */ + D_ASSERT(signal_pending(current)); + goto out_free_e; + } + break; + + case P_OV_REQUEST: + if (mdev->state.conn >= C_CONNECTED && + mdev->state.conn != C_VERIFY_T) + dev_warn(DEV, "ASSERT FAILED: got P_OV_REQUEST while being %s\n", + drbd_conn_str(mdev->state.conn)); + if (mdev->ov_start_sector == ~(sector_t)0 && + mdev->agreed_pro_version >= 90) { + mdev->ov_start_sector = sector; + mdev->ov_position = sector; + mdev->ov_left = mdev->rs_total - BM_SECT_TO_BIT(sector); + dev_info(DEV, "Online Verify start sector: %llu\n", + (unsigned long long)sector); + } + e->w.cb = w_e_end_ov_req; + fault_type = DRBD_FAULT_RS_RD; + /* Eventually this should become asynchronous. Currently it + * blocks the whole receiver just to delay the reading of a + * resync data block. + * the drbd_work_queue mechanism is made for this... + */ + if (!drbd_rs_begin_io(mdev, sector)) { + /* we have been interrupted, + * probably connection lost! */ + D_ASSERT(signal_pending(current)); + goto out_free_e; + } + break; + + + default: + dev_err(DEV, "unexpected command (%s) in receive_DataRequest\n", + cmdname(h->command)); + fault_type = DRBD_FAULT_MAX; + } + + spin_lock_irq(&mdev->req_lock); + list_add(&e->w.list, &mdev->read_ee); + spin_unlock_irq(&mdev->req_lock); + + inc_unacked(mdev); + + drbd_generic_make_request(mdev, fault_type, e->private_bio); + maybe_kick_lo(mdev); + + return TRUE; + +out_free_e: + kfree(di); + put_ldev(mdev); + drbd_free_ee(mdev, e); + return FALSE; +} + +static int drbd_asb_recover_0p(struct drbd_conf *mdev) __must_hold(local) +{ + int self, peer, rv = -100; + unsigned long ch_self, ch_peer; + + self = mdev->ldev->md.uuid[UI_BITMAP] & 1; + peer = mdev->p_uuid[UI_BITMAP] & 1; + + ch_peer = mdev->p_uuid[UI_SIZE]; + ch_self = mdev->comm_bm_set; + + switch (mdev->net_conf->after_sb_0p) { + case ASB_CONSENSUS: + case ASB_DISCARD_SECONDARY: + case ASB_CALL_HELPER: + dev_err(DEV, "Configuration error.\n"); + break; + case ASB_DISCONNECT: + break; + case ASB_DISCARD_YOUNGER_PRI: + if (self == 0 && peer == 1) { + rv = -1; + break; + } + if (self == 1 && peer == 0) { + rv = 1; + break; + } + /* Else fall through to one of the other strategies... */ + case ASB_DISCARD_OLDER_PRI: + if (self == 0 && peer == 1) { + rv = 1; + break; + } + if (self == 1 && peer == 0) { + rv = -1; + break; + } + /* Else fall through to one of the other strategies... */ + dev_warn(DEV, "Discard younger/older primary did not find a decision\n" + "Using discard-least-changes instead\n"); + case ASB_DISCARD_ZERO_CHG: + if (ch_peer == 0 && ch_self == 0) { + rv = test_bit(DISCARD_CONCURRENT, &mdev->flags) + ? -1 : 1; + break; + } else { + if (ch_peer == 0) { rv = 1; break; } + if (ch_self == 0) { rv = -1; break; } + } + if (mdev->net_conf->after_sb_0p == ASB_DISCARD_ZERO_CHG) + break; + case ASB_DISCARD_LEAST_CHG: + if (ch_self < ch_peer) + rv = -1; + else if (ch_self > ch_peer) + rv = 1; + else /* ( ch_self == ch_peer ) */ + /* Well, then use something else. */ + rv = test_bit(DISCARD_CONCURRENT, &mdev->flags) + ? -1 : 1; + break; + case ASB_DISCARD_LOCAL: + rv = -1; + break; + case ASB_DISCARD_REMOTE: + rv = 1; + } + + return rv; +} + +static int drbd_asb_recover_1p(struct drbd_conf *mdev) __must_hold(local) +{ + int self, peer, hg, rv = -100; + + self = mdev->ldev->md.uuid[UI_BITMAP] & 1; + peer = mdev->p_uuid[UI_BITMAP] & 1; + + switch (mdev->net_conf->after_sb_1p) { + case ASB_DISCARD_YOUNGER_PRI: + case ASB_DISCARD_OLDER_PRI: + case ASB_DISCARD_LEAST_CHG: + case ASB_DISCARD_LOCAL: + case ASB_DISCARD_REMOTE: + dev_err(DEV, "Configuration error.\n"); + break; + case ASB_DISCONNECT: + break; + case ASB_CONSENSUS: + hg = drbd_asb_recover_0p(mdev); + if (hg == -1 && mdev->state.role == R_SECONDARY) + rv = hg; + if (hg == 1 && mdev->state.role == R_PRIMARY) + rv = hg; + break; + case ASB_VIOLENTLY: + rv = drbd_asb_recover_0p(mdev); + break; + case ASB_DISCARD_SECONDARY: + return mdev->state.role == R_PRIMARY ? 1 : -1; + case ASB_CALL_HELPER: + hg = drbd_asb_recover_0p(mdev); + if (hg == -1 && mdev->state.role == R_PRIMARY) { + self = drbd_set_role(mdev, R_SECONDARY, 0); + /* drbd_change_state() does not sleep while in SS_IN_TRANSIENT_STATE, + * we might be here in C_WF_REPORT_PARAMS which is transient. + * we do not need to wait for the after state change work either. */ + self = drbd_change_state(mdev, CS_VERBOSE, NS(role, R_SECONDARY)); + if (self != SS_SUCCESS) { + drbd_khelper(mdev, "pri-lost-after-sb"); + } else { + dev_warn(DEV, "Successfully gave up primary role.\n"); + rv = hg; + } + } else + rv = hg; + } + + return rv; +} + +static int drbd_asb_recover_2p(struct drbd_conf *mdev) __must_hold(local) +{ + int self, peer, hg, rv = -100; + + self = mdev->ldev->md.uuid[UI_BITMAP] & 1; + peer = mdev->p_uuid[UI_BITMAP] & 1; + + switch (mdev->net_conf->after_sb_2p) { + case ASB_DISCARD_YOUNGER_PRI: + case ASB_DISCARD_OLDER_PRI: + case ASB_DISCARD_LEAST_CHG: + case ASB_DISCARD_LOCAL: + case ASB_DISCARD_REMOTE: + case ASB_CONSENSUS: + case ASB_DISCARD_SECONDARY: + dev_err(DEV, "Configuration error.\n"); + break; + case ASB_VIOLENTLY: + rv = drbd_asb_recover_0p(mdev); + break; + case ASB_DISCONNECT: + break; + case ASB_CALL_HELPER: + hg = drbd_asb_recover_0p(mdev); + if (hg == -1) { + /* drbd_change_state() does not sleep while in SS_IN_TRANSIENT_STATE, + * we might be here in C_WF_REPORT_PARAMS which is transient. + * we do not need to wait for the after state change work either. */ + self = drbd_change_state(mdev, CS_VERBOSE, NS(role, R_SECONDARY)); + if (self != SS_SUCCESS) { + drbd_khelper(mdev, "pri-lost-after-sb"); + } else { + dev_warn(DEV, "Successfully gave up primary role.\n"); + rv = hg; + } + } else + rv = hg; + } + + return rv; +} + +static void drbd_uuid_dump(struct drbd_conf *mdev, char *text, u64 *uuid, + u64 bits, u64 flags) +{ + if (!uuid) { + dev_info(DEV, "%s uuid info vanished while I was looking!\n", text); + return; + } + dev_info(DEV, "%s %016llX:%016llX:%016llX:%016llX bits:%llu flags:%llX\n", + text, + (unsigned long long)uuid[UI_CURRENT], + (unsigned long long)uuid[UI_BITMAP], + (unsigned long long)uuid[UI_HISTORY_START], + (unsigned long long)uuid[UI_HISTORY_END], + (unsigned long long)bits, + (unsigned long long)flags); +} + +/* + 100 after split brain try auto recover + 2 C_SYNC_SOURCE set BitMap + 1 C_SYNC_SOURCE use BitMap + 0 no Sync + -1 C_SYNC_TARGET use BitMap + -2 C_SYNC_TARGET set BitMap + -100 after split brain, disconnect +-1000 unrelated data + */ +static int drbd_uuid_compare(struct drbd_conf *mdev, int *rule_nr) __must_hold(local) +{ + u64 self, peer; + int i, j; + + self = mdev->ldev->md.uuid[UI_CURRENT] & ~((u64)1); + peer = mdev->p_uuid[UI_CURRENT] & ~((u64)1); + + *rule_nr = 10; + if (self == UUID_JUST_CREATED && peer == UUID_JUST_CREATED) + return 0; + + *rule_nr = 20; + if ((self == UUID_JUST_CREATED || self == (u64)0) && + peer != UUID_JUST_CREATED) + return -2; + + *rule_nr = 30; + if (self != UUID_JUST_CREATED && + (peer == UUID_JUST_CREATED || peer == (u64)0)) + return 2; + + if (self == peer) { + int rct, dc; /* roles at crash time */ + + if (mdev->p_uuid[UI_BITMAP] == (u64)0 && mdev->ldev->md.uuid[UI_BITMAP] != (u64)0) { + + if (mdev->agreed_pro_version < 91) + return -1001; + + if ((mdev->ldev->md.uuid[UI_BITMAP] & ~((u64)1)) == (mdev->p_uuid[UI_HISTORY_START] & ~((u64)1)) && + (mdev->ldev->md.uuid[UI_HISTORY_START] & ~((u64)1)) == (mdev->p_uuid[UI_HISTORY_START + 1] & ~((u64)1))) { + dev_info(DEV, "was SyncSource, missed the resync finished event, corrected myself:\n"); + drbd_uuid_set_bm(mdev, 0UL); + + drbd_uuid_dump(mdev, "self", mdev->ldev->md.uuid, + mdev->state.disk >= D_NEGOTIATING ? drbd_bm_total_weight(mdev) : 0, 0); + *rule_nr = 34; + } else { + dev_info(DEV, "was SyncSource (peer failed to write sync_uuid)\n"); + *rule_nr = 36; + } + + return 1; + } + + if (mdev->ldev->md.uuid[UI_BITMAP] == (u64)0 && mdev->p_uuid[UI_BITMAP] != (u64)0) { + + if (mdev->agreed_pro_version < 91) + return -1001; + + if ((mdev->ldev->md.uuid[UI_HISTORY_START] & ~((u64)1)) == (mdev->p_uuid[UI_BITMAP] & ~((u64)1)) && + (mdev->ldev->md.uuid[UI_HISTORY_START + 1] & ~((u64)1)) == (mdev->p_uuid[UI_HISTORY_START] & ~((u64)1))) { + dev_info(DEV, "was SyncTarget, peer missed the resync finished event, corrected peer:\n"); + + mdev->p_uuid[UI_HISTORY_START + 1] = mdev->p_uuid[UI_HISTORY_START]; + mdev->p_uuid[UI_HISTORY_START] = mdev->p_uuid[UI_BITMAP]; + mdev->p_uuid[UI_BITMAP] = 0UL; + + drbd_uuid_dump(mdev, "peer", mdev->p_uuid, mdev->p_uuid[UI_SIZE], mdev->p_uuid[UI_FLAGS]); + *rule_nr = 35; + } else { + dev_info(DEV, "was SyncTarget (failed to write sync_uuid)\n"); + *rule_nr = 37; + } + + return -1; + } + + /* Common power [off|failure] */ + rct = (test_bit(CRASHED_PRIMARY, &mdev->flags) ? 1 : 0) + + (mdev->p_uuid[UI_FLAGS] & 2); + /* lowest bit is set when we were primary, + * next bit (weight 2) is set when peer was primary */ + *rule_nr = 40; + + switch (rct) { + case 0: /* !self_pri && !peer_pri */ return 0; + case 1: /* self_pri && !peer_pri */ return 1; + case 2: /* !self_pri && peer_pri */ return -1; + case 3: /* self_pri && peer_pri */ + dc = test_bit(DISCARD_CONCURRENT, &mdev->flags); + return dc ? -1 : 1; + } + } + + *rule_nr = 50; + peer = mdev->p_uuid[UI_BITMAP] & ~((u64)1); + if (self == peer) + return -1; + + *rule_nr = 51; + peer = mdev->p_uuid[UI_HISTORY_START] & ~((u64)1); + if (self == peer) { + self = mdev->ldev->md.uuid[UI_HISTORY_START] & ~((u64)1); + peer = mdev->p_uuid[UI_HISTORY_START + 1] & ~((u64)1); + if (self == peer) { + /* The last P_SYNC_UUID did not get though. Undo the last start of + resync as sync source modifications of the peer's UUIDs. */ + + if (mdev->agreed_pro_version < 91) + return -1001; + + mdev->p_uuid[UI_BITMAP] = mdev->p_uuid[UI_HISTORY_START]; + mdev->p_uuid[UI_HISTORY_START] = mdev->p_uuid[UI_HISTORY_START + 1]; + return -1; + } + } + + *rule_nr = 60; + self = mdev->ldev->md.uuid[UI_CURRENT] & ~((u64)1); + for (i = UI_HISTORY_START; i <= UI_HISTORY_END; i++) { + peer = mdev->p_uuid[i] & ~((u64)1); + if (self == peer) + return -2; + } + + *rule_nr = 70; + self = mdev->ldev->md.uuid[UI_BITMAP] & ~((u64)1); + peer = mdev->p_uuid[UI_CURRENT] & ~((u64)1); + if (self == peer) + return 1; + + *rule_nr = 71; + self = mdev->ldev->md.uuid[UI_HISTORY_START] & ~((u64)1); + if (self == peer) { + self = mdev->ldev->md.uuid[UI_HISTORY_START + 1] & ~((u64)1); + peer = mdev->p_uuid[UI_HISTORY_START] & ~((u64)1); + if (self == peer) { + /* The last P_SYNC_UUID did not get though. Undo the last start of + resync as sync source modifications of our UUIDs. */ + + if (mdev->agreed_pro_version < 91) + return -1001; + + _drbd_uuid_set(mdev, UI_BITMAP, mdev->ldev->md.uuid[UI_HISTORY_START]); + _drbd_uuid_set(mdev, UI_HISTORY_START, mdev->ldev->md.uuid[UI_HISTORY_START + 1]); + + dev_info(DEV, "Undid last start of resync:\n"); + + drbd_uuid_dump(mdev, "self", mdev->ldev->md.uuid, + mdev->state.disk >= D_NEGOTIATING ? drbd_bm_total_weight(mdev) : 0, 0); + + return 1; + } + } + + + *rule_nr = 80; + peer = mdev->p_uuid[UI_CURRENT] & ~((u64)1); + for (i = UI_HISTORY_START; i <= UI_HISTORY_END; i++) { + self = mdev->ldev->md.uuid[i] & ~((u64)1); + if (self == peer) + return 2; + } + + *rule_nr = 90; + self = mdev->ldev->md.uuid[UI_BITMAP] & ~((u64)1); + peer = mdev->p_uuid[UI_BITMAP] & ~((u64)1); + if (self == peer && self != ((u64)0)) + return 100; + + *rule_nr = 100; + for (i = UI_HISTORY_START; i <= UI_HISTORY_END; i++) { + self = mdev->ldev->md.uuid[i] & ~((u64)1); + for (j = UI_HISTORY_START; j <= UI_HISTORY_END; j++) { + peer = mdev->p_uuid[j] & ~((u64)1); + if (self == peer) + return -100; + } + } + + return -1000; +} + +/* drbd_sync_handshake() returns the new conn state on success, or + CONN_MASK (-1) on failure. + */ +static enum drbd_conns drbd_sync_handshake(struct drbd_conf *mdev, enum drbd_role peer_role, + enum drbd_disk_state peer_disk) __must_hold(local) +{ + int hg, rule_nr; + enum drbd_conns rv = C_MASK; + enum drbd_disk_state mydisk; + + mydisk = mdev->state.disk; + if (mydisk == D_NEGOTIATING) + mydisk = mdev->new_state_tmp.disk; + + dev_info(DEV, "drbd_sync_handshake:\n"); + drbd_uuid_dump(mdev, "self", mdev->ldev->md.uuid, mdev->comm_bm_set, 0); + drbd_uuid_dump(mdev, "peer", mdev->p_uuid, + mdev->p_uuid[UI_SIZE], mdev->p_uuid[UI_FLAGS]); + + hg = drbd_uuid_compare(mdev, &rule_nr); + + dev_info(DEV, "uuid_compare()=%d by rule %d\n", hg, rule_nr); + + if (hg == -1000) { + dev_alert(DEV, "Unrelated data, aborting!\n"); + return C_MASK; + } + if (hg == -1001) { + dev_alert(DEV, "To resolve this both sides have to support at least protocol\n"); + return C_MASK; + } + + if ((mydisk == D_INCONSISTENT && peer_disk > D_INCONSISTENT) || + (peer_disk == D_INCONSISTENT && mydisk > D_INCONSISTENT)) { + int f = (hg == -100) || abs(hg) == 2; + hg = mydisk > D_INCONSISTENT ? 1 : -1; + if (f) + hg = hg*2; + dev_info(DEV, "Becoming sync %s due to disk states.\n", + hg > 0 ? "source" : "target"); + } + + if (hg == 100 || (hg == -100 && mdev->net_conf->always_asbp)) { + int pcount = (mdev->state.role == R_PRIMARY) + + (peer_role == R_PRIMARY); + int forced = (hg == -100); + + switch (pcount) { + case 0: + hg = drbd_asb_recover_0p(mdev); + break; + case 1: + hg = drbd_asb_recover_1p(mdev); + break; + case 2: + hg = drbd_asb_recover_2p(mdev); + break; + } + if (abs(hg) < 100) { + dev_warn(DEV, "Split-Brain detected, %d primaries, " + "automatically solved. Sync from %s node\n", + pcount, (hg < 0) ? "peer" : "this"); + if (forced) { + dev_warn(DEV, "Doing a full sync, since" + " UUIDs where ambiguous.\n"); + hg = hg*2; + } + } + } + + if (hg == -100) { + if (mdev->net_conf->want_lose && !(mdev->p_uuid[UI_FLAGS]&1)) + hg = -1; + if (!mdev->net_conf->want_lose && (mdev->p_uuid[UI_FLAGS]&1)) + hg = 1; + + if (abs(hg) < 100) + dev_warn(DEV, "Split-Brain detected, manually solved. " + "Sync from %s node\n", + (hg < 0) ? "peer" : "this"); + } + + if (hg == -100) { + dev_alert(DEV, "Split-Brain detected, dropping connection!\n"); + drbd_khelper(mdev, "split-brain"); + return C_MASK; + } + + if (hg > 0 && mydisk <= D_INCONSISTENT) { + dev_err(DEV, "I shall become SyncSource, but I am inconsistent!\n"); + return C_MASK; + } + + if (hg < 0 && /* by intention we do not use mydisk here. */ + mdev->state.role == R_PRIMARY && mdev->state.disk >= D_CONSISTENT) { + switch (mdev->net_conf->rr_conflict) { + case ASB_CALL_HELPER: + drbd_khelper(mdev, "pri-lost"); + /* fall through */ + case ASB_DISCONNECT: + dev_err(DEV, "I shall become SyncTarget, but I am primary!\n"); + return C_MASK; + case ASB_VIOLENTLY: + dev_warn(DEV, "Becoming SyncTarget, violating the stable-data" + "assumption\n"); + } + } + + if (abs(hg) >= 2) { + dev_info(DEV, "Writing the whole bitmap, full sync required after drbd_sync_handshake.\n"); + if (drbd_bitmap_io(mdev, &drbd_bmio_set_n_write, "set_n_write from sync_handshake")) + return C_MASK; + } + + if (hg > 0) { /* become sync source. */ + rv = C_WF_BITMAP_S; + } else if (hg < 0) { /* become sync target */ + rv = C_WF_BITMAP_T; + } else { + rv = C_CONNECTED; + if (drbd_bm_total_weight(mdev)) { + dev_info(DEV, "No resync, but %lu bits in bitmap!\n", + drbd_bm_total_weight(mdev)); + } + } + + return rv; +} + +/* returns 1 if invalid */ +static int cmp_after_sb(enum drbd_after_sb_p peer, enum drbd_after_sb_p self) +{ + /* ASB_DISCARD_REMOTE - ASB_DISCARD_LOCAL is valid */ + if ((peer == ASB_DISCARD_REMOTE && self == ASB_DISCARD_LOCAL) || + (self == ASB_DISCARD_REMOTE && peer == ASB_DISCARD_LOCAL)) + return 0; + + /* any other things with ASB_DISCARD_REMOTE or ASB_DISCARD_LOCAL are invalid */ + if (peer == ASB_DISCARD_REMOTE || peer == ASB_DISCARD_LOCAL || + self == ASB_DISCARD_REMOTE || self == ASB_DISCARD_LOCAL) + return 1; + + /* everything else is valid if they are equal on both sides. */ + if (peer == self) + return 0; + + /* everything es is invalid. */ + return 1; +} + +static int receive_protocol(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_protocol *p = (struct p_protocol *)h; + int header_size, data_size; + int p_proto, p_after_sb_0p, p_after_sb_1p, p_after_sb_2p; + int p_want_lose, p_two_primaries; + char p_integrity_alg[SHARED_SECRET_MAX] = ""; + + header_size = sizeof(*p) - sizeof(*h); + data_size = h->length - header_size; + + if (drbd_recv(mdev, h->payload, header_size) != header_size) + return FALSE; + + p_proto = be32_to_cpu(p->protocol); + p_after_sb_0p = be32_to_cpu(p->after_sb_0p); + p_after_sb_1p = be32_to_cpu(p->after_sb_1p); + p_after_sb_2p = be32_to_cpu(p->after_sb_2p); + p_want_lose = be32_to_cpu(p->want_lose); + p_two_primaries = be32_to_cpu(p->two_primaries); + + if (p_proto != mdev->net_conf->wire_protocol) { + dev_err(DEV, "incompatible communication protocols\n"); + goto disconnect; + } + + if (cmp_after_sb(p_after_sb_0p, mdev->net_conf->after_sb_0p)) { + dev_err(DEV, "incompatible after-sb-0pri settings\n"); + goto disconnect; + } + + if (cmp_after_sb(p_after_sb_1p, mdev->net_conf->after_sb_1p)) { + dev_err(DEV, "incompatible after-sb-1pri settings\n"); + goto disconnect; + } + + if (cmp_after_sb(p_after_sb_2p, mdev->net_conf->after_sb_2p)) { + dev_err(DEV, "incompatible after-sb-2pri settings\n"); + goto disconnect; + } + + if (p_want_lose && mdev->net_conf->want_lose) { + dev_err(DEV, "both sides have the 'want_lose' flag set\n"); + goto disconnect; + } + + if (p_two_primaries != mdev->net_conf->two_primaries) { + dev_err(DEV, "incompatible setting of the two-primaries options\n"); + goto disconnect; + } + + if (mdev->agreed_pro_version >= 87) { + unsigned char *my_alg = mdev->net_conf->integrity_alg; + + if (drbd_recv(mdev, p_integrity_alg, data_size) != data_size) + return FALSE; + + p_integrity_alg[SHARED_SECRET_MAX-1] = 0; + if (strcmp(p_integrity_alg, my_alg)) { + dev_err(DEV, "incompatible setting of the data-integrity-alg\n"); + goto disconnect; + } + dev_info(DEV, "data-integrity-alg: %s\n", + my_alg[0] ? my_alg : (unsigned char *)"<not-used>"); + } + + return TRUE; + +disconnect: + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return FALSE; +} + +/* helper function + * input: alg name, feature name + * return: NULL (alg name was "") + * ERR_PTR(error) if something goes wrong + * or the crypto hash ptr, if it worked out ok. */ +struct crypto_hash *drbd_crypto_alloc_digest_safe(const struct drbd_conf *mdev, + const char *alg, const char *name) +{ + struct crypto_hash *tfm; + + if (!alg[0]) + return NULL; + + tfm = crypto_alloc_hash(alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + dev_err(DEV, "Can not allocate \"%s\" as %s (reason: %ld)\n", + alg, name, PTR_ERR(tfm)); + return tfm; + } + if (!drbd_crypto_is_hash(crypto_hash_tfm(tfm))) { + crypto_free_hash(tfm); + dev_err(DEV, "\"%s\" is not a digest (%s)\n", alg, name); + return ERR_PTR(-EINVAL); + } + return tfm; +} + +static int receive_SyncParam(struct drbd_conf *mdev, struct p_header *h) +{ + int ok = TRUE; + struct p_rs_param_89 *p = (struct p_rs_param_89 *)h; + unsigned int header_size, data_size, exp_max_sz; + struct crypto_hash *verify_tfm = NULL; + struct crypto_hash *csums_tfm = NULL; + const int apv = mdev->agreed_pro_version; + + exp_max_sz = apv <= 87 ? sizeof(struct p_rs_param) + : apv == 88 ? sizeof(struct p_rs_param) + + SHARED_SECRET_MAX + : /* 89 */ sizeof(struct p_rs_param_89); + + if (h->length > exp_max_sz) { + dev_err(DEV, "SyncParam packet too long: received %u, expected <= %u bytes\n", + h->length, exp_max_sz); + return FALSE; + } + + if (apv <= 88) { + header_size = sizeof(struct p_rs_param) - sizeof(*h); + data_size = h->length - header_size; + } else /* apv >= 89 */ { + header_size = sizeof(struct p_rs_param_89) - sizeof(*h); + data_size = h->length - header_size; + D_ASSERT(data_size == 0); + } + + /* initialize verify_alg and csums_alg */ + memset(p->verify_alg, 0, 2 * SHARED_SECRET_MAX); + + if (drbd_recv(mdev, h->payload, header_size) != header_size) + return FALSE; + + mdev->sync_conf.rate = be32_to_cpu(p->rate); + + if (apv >= 88) { + if (apv == 88) { + if (data_size > SHARED_SECRET_MAX) { + dev_err(DEV, "verify-alg too long, " + "peer wants %u, accepting only %u byte\n", + data_size, SHARED_SECRET_MAX); + return FALSE; + } + + if (drbd_recv(mdev, p->verify_alg, data_size) != data_size) + return FALSE; + + /* we expect NUL terminated string */ + /* but just in case someone tries to be evil */ + D_ASSERT(p->verify_alg[data_size-1] == 0); + p->verify_alg[data_size-1] = 0; + + } else /* apv >= 89 */ { + /* we still expect NUL terminated strings */ + /* but just in case someone tries to be evil */ + D_ASSERT(p->verify_alg[SHARED_SECRET_MAX-1] == 0); + D_ASSERT(p->csums_alg[SHARED_SECRET_MAX-1] == 0); + p->verify_alg[SHARED_SECRET_MAX-1] = 0; + p->csums_alg[SHARED_SECRET_MAX-1] = 0; + } + + if (strcmp(mdev->sync_conf.verify_alg, p->verify_alg)) { + if (mdev->state.conn == C_WF_REPORT_PARAMS) { + dev_err(DEV, "Different verify-alg settings. me=\"%s\" peer=\"%s\"\n", + mdev->sync_conf.verify_alg, p->verify_alg); + goto disconnect; + } + verify_tfm = drbd_crypto_alloc_digest_safe(mdev, + p->verify_alg, "verify-alg"); + if (IS_ERR(verify_tfm)) { + verify_tfm = NULL; + goto disconnect; + } + } + + if (apv >= 89 && strcmp(mdev->sync_conf.csums_alg, p->csums_alg)) { + if (mdev->state.conn == C_WF_REPORT_PARAMS) { + dev_err(DEV, "Different csums-alg settings. me=\"%s\" peer=\"%s\"\n", + mdev->sync_conf.csums_alg, p->csums_alg); + goto disconnect; + } + csums_tfm = drbd_crypto_alloc_digest_safe(mdev, + p->csums_alg, "csums-alg"); + if (IS_ERR(csums_tfm)) { + csums_tfm = NULL; + goto disconnect; + } + } + + + spin_lock(&mdev->peer_seq_lock); + /* lock against drbd_nl_syncer_conf() */ + if (verify_tfm) { + strcpy(mdev->sync_conf.verify_alg, p->verify_alg); + mdev->sync_conf.verify_alg_len = strlen(p->verify_alg) + 1; + crypto_free_hash(mdev->verify_tfm); + mdev->verify_tfm = verify_tfm; + dev_info(DEV, "using verify-alg: \"%s\"\n", p->verify_alg); + } + if (csums_tfm) { + strcpy(mdev->sync_conf.csums_alg, p->csums_alg); + mdev->sync_conf.csums_alg_len = strlen(p->csums_alg) + 1; + crypto_free_hash(mdev->csums_tfm); + mdev->csums_tfm = csums_tfm; + dev_info(DEV, "using csums-alg: \"%s\"\n", p->csums_alg); + } + spin_unlock(&mdev->peer_seq_lock); + } + + return ok; +disconnect: + /* just for completeness: actually not needed, + * as this is not reached if csums_tfm was ok. */ + crypto_free_hash(csums_tfm); + /* but free the verify_tfm again, if csums_tfm did not work out */ + crypto_free_hash(verify_tfm); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return FALSE; +} + +static void drbd_setup_order_type(struct drbd_conf *mdev, int peer) +{ + /* sorry, we currently have no working implementation + * of distributed TCQ */ +} + +/* warn if the arguments differ by more than 12.5% */ +static void warn_if_differ_considerably(struct drbd_conf *mdev, + const char *s, sector_t a, sector_t b) +{ + sector_t d; + if (a == 0 || b == 0) + return; + d = (a > b) ? (a - b) : (b - a); + if (d > (a>>3) || d > (b>>3)) + dev_warn(DEV, "Considerable difference in %s: %llus vs. %llus\n", s, + (unsigned long long)a, (unsigned long long)b); +} + +static int receive_sizes(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_sizes *p = (struct p_sizes *)h; + enum determine_dev_size dd = unchanged; + unsigned int max_seg_s; + sector_t p_size, p_usize, my_usize; + int ldsc = 0; /* local disk size changed */ + enum drbd_conns nconn; + + ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE; + if (drbd_recv(mdev, h->payload, h->length) != h->length) + return FALSE; + + p_size = be64_to_cpu(p->d_size); + p_usize = be64_to_cpu(p->u_size); + + if (p_size == 0 && mdev->state.disk == D_DISKLESS) { + dev_err(DEV, "some backing storage is needed\n"); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return FALSE; + } + + /* just store the peer's disk size for now. + * we still need to figure out whether we accept that. */ + mdev->p_size = p_size; + +#define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r)) + if (get_ldev(mdev)) { + warn_if_differ_considerably(mdev, "lower level device sizes", + p_size, drbd_get_max_capacity(mdev->ldev)); + warn_if_differ_considerably(mdev, "user requested size", + p_usize, mdev->ldev->dc.disk_size); + + /* if this is the first connect, or an otherwise expected + * param exchange, choose the minimum */ + if (mdev->state.conn == C_WF_REPORT_PARAMS) + p_usize = min_not_zero((sector_t)mdev->ldev->dc.disk_size, + p_usize); + + my_usize = mdev->ldev->dc.disk_size; + + if (mdev->ldev->dc.disk_size != p_usize) { + mdev->ldev->dc.disk_size = p_usize; + dev_info(DEV, "Peer sets u_size to %lu sectors\n", + (unsigned long)mdev->ldev->dc.disk_size); + } + + /* Never shrink a device with usable data during connect. + But allow online shrinking if we are connected. */ + if (drbd_new_dev_size(mdev, mdev->ldev) < + drbd_get_capacity(mdev->this_bdev) && + mdev->state.disk >= D_OUTDATED && + mdev->state.conn < C_CONNECTED) { + dev_err(DEV, "The peer's disk size is too small!\n"); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + mdev->ldev->dc.disk_size = my_usize; + put_ldev(mdev); + return FALSE; + } + put_ldev(mdev); + } +#undef min_not_zero + + if (get_ldev(mdev)) { + dd = drbd_determin_dev_size(mdev); + put_ldev(mdev); + if (dd == dev_size_error) + return FALSE; + drbd_md_sync(mdev); + } else { + /* I am diskless, need to accept the peer's size. */ + drbd_set_my_capacity(mdev, p_size); + } + + if (mdev->p_uuid && mdev->state.conn <= C_CONNECTED && get_ldev(mdev)) { + nconn = drbd_sync_handshake(mdev, + mdev->state.peer, mdev->state.pdsk); + put_ldev(mdev); + + if (nconn == C_MASK) { + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return FALSE; + } + + if (drbd_request_state(mdev, NS(conn, nconn)) < SS_SUCCESS) { + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return FALSE; + } + } + + if (get_ldev(mdev)) { + if (mdev->ldev->known_size != drbd_get_capacity(mdev->ldev->backing_bdev)) { + mdev->ldev->known_size = drbd_get_capacity(mdev->ldev->backing_bdev); + ldsc = 1; + } + + max_seg_s = be32_to_cpu(p->max_segment_size); + if (max_seg_s != queue_max_segment_size(mdev->rq_queue)) + drbd_setup_queue_param(mdev, max_seg_s); + + drbd_setup_order_type(mdev, be32_to_cpu(p->queue_order_type)); + put_ldev(mdev); + } + + if (mdev->state.conn > C_WF_REPORT_PARAMS) { + if (be64_to_cpu(p->c_size) != + drbd_get_capacity(mdev->this_bdev) || ldsc) { + /* we have different sizes, probably peer + * needs to know my new size... */ + drbd_send_sizes(mdev, 0); + } + if (test_and_clear_bit(RESIZE_PENDING, &mdev->flags) || + (dd == grew && mdev->state.conn == C_CONNECTED)) { + if (mdev->state.pdsk >= D_INCONSISTENT && + mdev->state.disk >= D_INCONSISTENT) + resync_after_online_grow(mdev); + else + set_bit(RESYNC_AFTER_NEG, &mdev->flags); + } + } + + return TRUE; +} + +static int receive_uuids(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_uuids *p = (struct p_uuids *)h; + u64 *p_uuid; + int i; + + ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE; + if (drbd_recv(mdev, h->payload, h->length) != h->length) + return FALSE; + + p_uuid = kmalloc(sizeof(u64)*UI_EXTENDED_SIZE, GFP_NOIO); + + for (i = UI_CURRENT; i < UI_EXTENDED_SIZE; i++) + p_uuid[i] = be64_to_cpu(p->uuid[i]); + + kfree(mdev->p_uuid); + mdev->p_uuid = p_uuid; + + if (mdev->state.conn < C_CONNECTED && + mdev->state.disk < D_INCONSISTENT && + mdev->state.role == R_PRIMARY && + (mdev->ed_uuid & ~((u64)1)) != (p_uuid[UI_CURRENT] & ~((u64)1))) { + dev_err(DEV, "Can only connect to data with current UUID=%016llX\n", + (unsigned long long)mdev->ed_uuid); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return FALSE; + } + + if (get_ldev(mdev)) { + int skip_initial_sync = + mdev->state.conn == C_CONNECTED && + mdev->agreed_pro_version >= 90 && + mdev->ldev->md.uuid[UI_CURRENT] == UUID_JUST_CREATED && + (p_uuid[UI_FLAGS] & 8); + if (skip_initial_sync) { + dev_info(DEV, "Accepted new current UUID, preparing to skip initial sync\n"); + drbd_bitmap_io(mdev, &drbd_bmio_clear_n_write, + "clear_n_write from receive_uuids"); + _drbd_uuid_set(mdev, UI_CURRENT, p_uuid[UI_CURRENT]); + _drbd_uuid_set(mdev, UI_BITMAP, 0); + _drbd_set_state(_NS2(mdev, disk, D_UP_TO_DATE, pdsk, D_UP_TO_DATE), + CS_VERBOSE, NULL); + drbd_md_sync(mdev); + } + put_ldev(mdev); + } + + /* Before we test for the disk state, we should wait until an eventually + ongoing cluster wide state change is finished. That is important if + we are primary and are detaching from our disk. We need to see the + new disk state... */ + wait_event(mdev->misc_wait, !test_bit(CLUSTER_ST_CHANGE, &mdev->flags)); + if (mdev->state.conn >= C_CONNECTED && mdev->state.disk < D_INCONSISTENT) + drbd_set_ed_uuid(mdev, p_uuid[UI_CURRENT]); + + return TRUE; +} + +/** + * convert_state() - Converts the peer's view of the cluster state to our point of view + * @ps: The state as seen by the peer. + */ +static union drbd_state convert_state(union drbd_state ps) +{ + union drbd_state ms; + + static enum drbd_conns c_tab[] = { + [C_CONNECTED] = C_CONNECTED, + + [C_STARTING_SYNC_S] = C_STARTING_SYNC_T, + [C_STARTING_SYNC_T] = C_STARTING_SYNC_S, + [C_DISCONNECTING] = C_TEAR_DOWN, /* C_NETWORK_FAILURE, */ + [C_VERIFY_S] = C_VERIFY_T, + [C_MASK] = C_MASK, + }; + + ms.i = ps.i; + + ms.conn = c_tab[ps.conn]; + ms.peer = ps.role; + ms.role = ps.peer; + ms.pdsk = ps.disk; + ms.disk = ps.pdsk; + ms.peer_isp = (ps.aftr_isp | ps.user_isp); + + return ms; +} + +static int receive_req_state(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_req_state *p = (struct p_req_state *)h; + union drbd_state mask, val; + int rv; + + ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE; + if (drbd_recv(mdev, h->payload, h->length) != h->length) + return FALSE; + + mask.i = be32_to_cpu(p->mask); + val.i = be32_to_cpu(p->val); + + if (test_bit(DISCARD_CONCURRENT, &mdev->flags) && + test_bit(CLUSTER_ST_CHANGE, &mdev->flags)) { + drbd_send_sr_reply(mdev, SS_CONCURRENT_ST_CHG); + return TRUE; + } + + mask = convert_state(mask); + val = convert_state(val); + + rv = drbd_change_state(mdev, CS_VERBOSE, mask, val); + + drbd_send_sr_reply(mdev, rv); + drbd_md_sync(mdev); + + return TRUE; +} + +static int receive_state(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_state *p = (struct p_state *)h; + enum drbd_conns nconn, oconn; + union drbd_state ns, peer_state; + enum drbd_disk_state real_peer_disk; + int rv; + + ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) + return FALSE; + + if (drbd_recv(mdev, h->payload, h->length) != h->length) + return FALSE; + + peer_state.i = be32_to_cpu(p->state); + + real_peer_disk = peer_state.disk; + if (peer_state.disk == D_NEGOTIATING) { + real_peer_disk = mdev->p_uuid[UI_FLAGS] & 4 ? D_INCONSISTENT : D_CONSISTENT; + dev_info(DEV, "real peer disk state = %s\n", drbd_disk_str(real_peer_disk)); + } + + spin_lock_irq(&mdev->req_lock); + retry: + oconn = nconn = mdev->state.conn; + spin_unlock_irq(&mdev->req_lock); + + if (nconn == C_WF_REPORT_PARAMS) + nconn = C_CONNECTED; + + if (mdev->p_uuid && peer_state.disk >= D_NEGOTIATING && + get_ldev_if_state(mdev, D_NEGOTIATING)) { + int cr; /* consider resync */ + + /* if we established a new connection */ + cr = (oconn < C_CONNECTED); + /* if we had an established connection + * and one of the nodes newly attaches a disk */ + cr |= (oconn == C_CONNECTED && + (peer_state.disk == D_NEGOTIATING || + mdev->state.disk == D_NEGOTIATING)); + /* if we have both been inconsistent, and the peer has been + * forced to be UpToDate with --overwrite-data */ + cr |= test_bit(CONSIDER_RESYNC, &mdev->flags); + /* if we had been plain connected, and the admin requested to + * start a sync by "invalidate" or "invalidate-remote" */ + cr |= (oconn == C_CONNECTED && + (peer_state.conn >= C_STARTING_SYNC_S && + peer_state.conn <= C_WF_BITMAP_T)); + + if (cr) + nconn = drbd_sync_handshake(mdev, peer_state.role, real_peer_disk); + + put_ldev(mdev); + if (nconn == C_MASK) { + if (mdev->state.disk == D_NEGOTIATING) { + drbd_force_state(mdev, NS(disk, D_DISKLESS)); + nconn = C_CONNECTED; + } else if (peer_state.disk == D_NEGOTIATING) { + dev_err(DEV, "Disk attach process on the peer node was aborted.\n"); + peer_state.disk = D_DISKLESS; + } else { + D_ASSERT(oconn == C_WF_REPORT_PARAMS); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return FALSE; + } + } + } + + spin_lock_irq(&mdev->req_lock); + if (mdev->state.conn != oconn) + goto retry; + clear_bit(CONSIDER_RESYNC, &mdev->flags); + ns.i = mdev->state.i; + ns.conn = nconn; + ns.peer = peer_state.role; + ns.pdsk = real_peer_disk; + ns.peer_isp = (peer_state.aftr_isp | peer_state.user_isp); + if ((nconn == C_CONNECTED || nconn == C_WF_BITMAP_S) && ns.disk == D_NEGOTIATING) + ns.disk = mdev->new_state_tmp.disk; + + rv = _drbd_set_state(mdev, ns, CS_VERBOSE | CS_HARD, NULL); + ns = mdev->state; + spin_unlock_irq(&mdev->req_lock); + + if (rv < SS_SUCCESS) { + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return FALSE; + } + + if (oconn > C_WF_REPORT_PARAMS) { + if (nconn > C_CONNECTED && peer_state.conn <= C_CONNECTED && + peer_state.disk != D_NEGOTIATING ) { + /* we want resync, peer has not yet decided to sync... */ + /* Nowadays only used when forcing a node into primary role and + setting its disk to UpToDate with that */ + drbd_send_uuids(mdev); + drbd_send_state(mdev); + } + } + + mdev->net_conf->want_lose = 0; + + drbd_md_sync(mdev); /* update connected indicator, la_size, ... */ + + return TRUE; +} + +static int receive_sync_uuid(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_rs_uuid *p = (struct p_rs_uuid *)h; + + wait_event(mdev->misc_wait, + mdev->state.conn == C_WF_SYNC_UUID || + mdev->state.conn < C_CONNECTED || + mdev->state.disk < D_NEGOTIATING); + + /* D_ASSERT( mdev->state.conn == C_WF_SYNC_UUID ); */ + + ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE; + if (drbd_recv(mdev, h->payload, h->length) != h->length) + return FALSE; + + /* Here the _drbd_uuid_ functions are right, current should + _not_ be rotated into the history */ + if (get_ldev_if_state(mdev, D_NEGOTIATING)) { + _drbd_uuid_set(mdev, UI_CURRENT, be64_to_cpu(p->uuid)); + _drbd_uuid_set(mdev, UI_BITMAP, 0UL); + + drbd_start_resync(mdev, C_SYNC_TARGET); + + put_ldev(mdev); + } else + dev_err(DEV, "Ignoring SyncUUID packet!\n"); + + return TRUE; +} + +enum receive_bitmap_ret { OK, DONE, FAILED }; + +static enum receive_bitmap_ret +receive_bitmap_plain(struct drbd_conf *mdev, struct p_header *h, + unsigned long *buffer, struct bm_xfer_ctx *c) +{ + unsigned num_words = min_t(size_t, BM_PACKET_WORDS, c->bm_words - c->word_offset); + unsigned want = num_words * sizeof(long); + + if (want != h->length) { + dev_err(DEV, "%s:want (%u) != h->length (%u)\n", __func__, want, h->length); + return FAILED; + } + if (want == 0) + return DONE; + if (drbd_recv(mdev, buffer, want) != want) + return FAILED; + + drbd_bm_merge_lel(mdev, c->word_offset, num_words, buffer); + + c->word_offset += num_words; + c->bit_offset = c->word_offset * BITS_PER_LONG; + if (c->bit_offset > c->bm_bits) + c->bit_offset = c->bm_bits; + + return OK; +} + +static enum receive_bitmap_ret +recv_bm_rle_bits(struct drbd_conf *mdev, + struct p_compressed_bm *p, + struct bm_xfer_ctx *c) +{ + struct bitstream bs; + u64 look_ahead; + u64 rl; + u64 tmp; + unsigned long s = c->bit_offset; + unsigned long e; + int len = p->head.length - (sizeof(*p) - sizeof(p->head)); + int toggle = DCBP_get_start(p); + int have; + int bits; + + bitstream_init(&bs, p->code, len, DCBP_get_pad_bits(p)); + + bits = bitstream_get_bits(&bs, &look_ahead, 64); + if (bits < 0) + return FAILED; + + for (have = bits; have > 0; s += rl, toggle = !toggle) { + bits = vli_decode_bits(&rl, look_ahead); + if (bits <= 0) + return FAILED; + + if (toggle) { + e = s + rl -1; + if (e >= c->bm_bits) { + dev_err(DEV, "bitmap overflow (e:%lu) while decoding bm RLE packet\n", e); + return FAILED; + } + _drbd_bm_set_bits(mdev, s, e); + } + + if (have < bits) { + dev_err(DEV, "bitmap decoding error: h:%d b:%d la:0x%08llx l:%u/%u\n", + have, bits, look_ahead, + (unsigned int)(bs.cur.b - p->code), + (unsigned int)bs.buf_len); + return FAILED; + } + look_ahead >>= bits; + have -= bits; + + bits = bitstream_get_bits(&bs, &tmp, 64 - have); + if (bits < 0) + return FAILED; + look_ahead |= tmp << have; + have += bits; + } + + c->bit_offset = s; + bm_xfer_ctx_bit_to_word_offset(c); + + return (s == c->bm_bits) ? DONE : OK; +} + +static enum receive_bitmap_ret +decode_bitmap_c(struct drbd_conf *mdev, + struct p_compressed_bm *p, + struct bm_xfer_ctx *c) +{ + if (DCBP_get_code(p) == RLE_VLI_Bits) + return recv_bm_rle_bits(mdev, p, c); + + /* other variants had been implemented for evaluation, + * but have been dropped as this one turned out to be "best" + * during all our tests. */ + + dev_err(DEV, "receive_bitmap_c: unknown encoding %u\n", p->encoding); + drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR)); + return FAILED; +} + +void INFO_bm_xfer_stats(struct drbd_conf *mdev, + const char *direction, struct bm_xfer_ctx *c) +{ + /* what would it take to transfer it "plaintext" */ + unsigned plain = sizeof(struct p_header) * + ((c->bm_words+BM_PACKET_WORDS-1)/BM_PACKET_WORDS+1) + + c->bm_words * sizeof(long); + unsigned total = c->bytes[0] + c->bytes[1]; + unsigned r; + + /* total can not be zero. but just in case: */ + if (total == 0) + return; + + /* don't report if not compressed */ + if (total >= plain) + return; + + /* total < plain. check for overflow, still */ + r = (total > UINT_MAX/1000) ? (total / (plain/1000)) + : (1000 * total / plain); + + if (r > 1000) + r = 1000; + + r = 1000 - r; + dev_info(DEV, "%s bitmap stats [Bytes(packets)]: plain %u(%u), RLE %u(%u), " + "total %u; compression: %u.%u%%\n", + direction, + c->bytes[1], c->packets[1], + c->bytes[0], c->packets[0], + total, r/10, r % 10); +} + +/* Since we are processing the bitfield from lower addresses to higher, + it does not matter if the process it in 32 bit chunks or 64 bit + chunks as long as it is little endian. (Understand it as byte stream, + beginning with the lowest byte...) If we would use big endian + we would need to process it from the highest address to the lowest, + in order to be agnostic to the 32 vs 64 bits issue. + + returns 0 on failure, 1 if we successfully received it. */ +static int receive_bitmap(struct drbd_conf *mdev, struct p_header *h) +{ + struct bm_xfer_ctx c; + void *buffer; + enum receive_bitmap_ret ret; + int ok = FALSE; + + wait_event(mdev->misc_wait, !atomic_read(&mdev->ap_bio_cnt)); + + drbd_bm_lock(mdev, "receive bitmap"); + + /* maybe we should use some per thread scratch page, + * and allocate that during initial device creation? */ + buffer = (unsigned long *) __get_free_page(GFP_NOIO); + if (!buffer) { + dev_err(DEV, "failed to allocate one page buffer in %s\n", __func__); + goto out; + } + + c = (struct bm_xfer_ctx) { + .bm_bits = drbd_bm_bits(mdev), + .bm_words = drbd_bm_words(mdev), + }; + + do { + if (h->command == P_BITMAP) { + ret = receive_bitmap_plain(mdev, h, buffer, &c); + } else if (h->command == P_COMPRESSED_BITMAP) { + /* MAYBE: sanity check that we speak proto >= 90, + * and the feature is enabled! */ + struct p_compressed_bm *p; + + if (h->length > BM_PACKET_PAYLOAD_BYTES) { + dev_err(DEV, "ReportCBitmap packet too large\n"); + goto out; + } + /* use the page buff */ + p = buffer; + memcpy(p, h, sizeof(*h)); + if (drbd_recv(mdev, p->head.payload, h->length) != h->length) + goto out; + if (p->head.length <= (sizeof(*p) - sizeof(p->head))) { + dev_err(DEV, "ReportCBitmap packet too small (l:%u)\n", p->head.length); + return FAILED; + } + ret = decode_bitmap_c(mdev, p, &c); + } else { + dev_warn(DEV, "receive_bitmap: h->command neither ReportBitMap nor ReportCBitMap (is 0x%x)", h->command); + goto out; + } + + c.packets[h->command == P_BITMAP]++; + c.bytes[h->command == P_BITMAP] += sizeof(struct p_header) + h->length; + + if (ret != OK) + break; + + if (!drbd_recv_header(mdev, h)) + goto out; + } while (ret == OK); + if (ret == FAILED) + goto out; + + INFO_bm_xfer_stats(mdev, "receive", &c); + + if (mdev->state.conn == C_WF_BITMAP_T) { + ok = !drbd_send_bitmap(mdev); + if (!ok) + goto out; + /* Omit CS_ORDERED with this state transition to avoid deadlocks. */ + ok = _drbd_request_state(mdev, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE); + D_ASSERT(ok == SS_SUCCESS); + } else if (mdev->state.conn != C_WF_BITMAP_S) { + /* admin may have requested C_DISCONNECTING, + * other threads may have noticed network errors */ + dev_info(DEV, "unexpected cstate (%s) in receive_bitmap\n", + drbd_conn_str(mdev->state.conn)); + } + + ok = TRUE; + out: + drbd_bm_unlock(mdev); + if (ok && mdev->state.conn == C_WF_BITMAP_S) + drbd_start_resync(mdev, C_SYNC_SOURCE); + free_page((unsigned long) buffer); + return ok; +} + +static int receive_skip(struct drbd_conf *mdev, struct p_header *h) +{ + /* TODO zero copy sink :) */ + static char sink[128]; + int size, want, r; + + dev_warn(DEV, "skipping unknown optional packet type %d, l: %d!\n", + h->command, h->length); + + size = h->length; + while (size > 0) { + want = min_t(int, size, sizeof(sink)); + r = drbd_recv(mdev, sink, want); + ERR_IF(r <= 0) break; + size -= r; + } + return size == 0; +} + +static int receive_UnplugRemote(struct drbd_conf *mdev, struct p_header *h) +{ + if (mdev->state.disk >= D_INCONSISTENT) + drbd_kick_lo(mdev); + + /* Make sure we've acked all the TCP data associated + * with the data requests being unplugged */ + drbd_tcp_quickack(mdev->data.socket); + + return TRUE; +} + +typedef int (*drbd_cmd_handler_f)(struct drbd_conf *, struct p_header *); + +static drbd_cmd_handler_f drbd_default_handler[] = { + [P_DATA] = receive_Data, + [P_DATA_REPLY] = receive_DataReply, + [P_RS_DATA_REPLY] = receive_RSDataReply, + [P_BARRIER] = receive_Barrier, + [P_BITMAP] = receive_bitmap, + [P_COMPRESSED_BITMAP] = receive_bitmap, + [P_UNPLUG_REMOTE] = receive_UnplugRemote, + [P_DATA_REQUEST] = receive_DataRequest, + [P_RS_DATA_REQUEST] = receive_DataRequest, + [P_SYNC_PARAM] = receive_SyncParam, + [P_SYNC_PARAM89] = receive_SyncParam, + [P_PROTOCOL] = receive_protocol, + [P_UUIDS] = receive_uuids, + [P_SIZES] = receive_sizes, + [P_STATE] = receive_state, + [P_STATE_CHG_REQ] = receive_req_state, + [P_SYNC_UUID] = receive_sync_uuid, + [P_OV_REQUEST] = receive_DataRequest, + [P_OV_REPLY] = receive_DataRequest, + [P_CSUM_RS_REQUEST] = receive_DataRequest, + /* anything missing from this table is in + * the asender_tbl, see get_asender_cmd */ + [P_MAX_CMD] = NULL, +}; + +static drbd_cmd_handler_f *drbd_cmd_handler = drbd_default_handler; +static drbd_cmd_handler_f *drbd_opt_cmd_handler; + +static void drbdd(struct drbd_conf *mdev) +{ + drbd_cmd_handler_f handler; + struct p_header *header = &mdev->data.rbuf.header; + + while (get_t_state(&mdev->receiver) == Running) { + drbd_thread_current_set_cpu(mdev); + if (!drbd_recv_header(mdev, header)) { + drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR)); + break; + } + + if (header->command < P_MAX_CMD) + handler = drbd_cmd_handler[header->command]; + else if (P_MAY_IGNORE < header->command + && header->command < P_MAX_OPT_CMD) + handler = drbd_opt_cmd_handler[header->command-P_MAY_IGNORE]; + else if (header->command > P_MAX_OPT_CMD) + handler = receive_skip; + else + handler = NULL; + + if (unlikely(!handler)) { + dev_err(DEV, "unknown packet type %d, l: %d!\n", + header->command, header->length); + drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR)); + break; + } + if (unlikely(!handler(mdev, header))) { + dev_err(DEV, "error receiving %s, l: %d!\n", + cmdname(header->command), header->length); + drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR)); + break; + } + } +} + +static void drbd_fail_pending_reads(struct drbd_conf *mdev) +{ + struct hlist_head *slot; + struct hlist_node *pos; + struct hlist_node *tmp; + struct drbd_request *req; + int i; + + /* + * Application READ requests + */ + spin_lock_irq(&mdev->req_lock); + for (i = 0; i < APP_R_HSIZE; i++) { + slot = mdev->app_reads_hash+i; + hlist_for_each_entry_safe(req, pos, tmp, slot, colision) { + /* it may (but should not any longer!) + * be on the work queue; if that assert triggers, + * we need to also grab the + * spin_lock_irq(&mdev->data.work.q_lock); + * and list_del_init here. */ + D_ASSERT(list_empty(&req->w.list)); + /* It would be nice to complete outside of spinlock. + * But this is easier for now. */ + _req_mod(req, connection_lost_while_pending); + } + } + for (i = 0; i < APP_R_HSIZE; i++) + if (!hlist_empty(mdev->app_reads_hash+i)) + dev_warn(DEV, "ASSERT FAILED: app_reads_hash[%d].first: " + "%p, should be NULL\n", i, mdev->app_reads_hash[i].first); + + memset(mdev->app_reads_hash, 0, APP_R_HSIZE*sizeof(void *)); + spin_unlock_irq(&mdev->req_lock); +} + +void drbd_flush_workqueue(struct drbd_conf *mdev) +{ + struct drbd_wq_barrier barr; + + barr.w.cb = w_prev_work_done; + init_completion(&barr.done); + drbd_queue_work(&mdev->data.work, &barr.w); + wait_for_completion(&barr.done); +} + +static void drbd_disconnect(struct drbd_conf *mdev) +{ + enum drbd_fencing_p fp; + union drbd_state os, ns; + int rv = SS_UNKNOWN_ERROR; + unsigned int i; + + if (mdev->state.conn == C_STANDALONE) + return; + if (mdev->state.conn >= C_WF_CONNECTION) + dev_err(DEV, "ASSERT FAILED cstate = %s, expected < WFConnection\n", + drbd_conn_str(mdev->state.conn)); + + /* asender does not clean up anything. it must not interfere, either */ + drbd_thread_stop(&mdev->asender); + + mutex_lock(&mdev->data.mutex); + drbd_free_sock(mdev); + mutex_unlock(&mdev->data.mutex); + + spin_lock_irq(&mdev->req_lock); + _drbd_wait_ee_list_empty(mdev, &mdev->active_ee); + _drbd_wait_ee_list_empty(mdev, &mdev->sync_ee); + _drbd_wait_ee_list_empty(mdev, &mdev->read_ee); + spin_unlock_irq(&mdev->req_lock); + + /* We do not have data structures that would allow us to + * get the rs_pending_cnt down to 0 again. + * * On C_SYNC_TARGET we do not have any data structures describing + * the pending RSDataRequest's we have sent. + * * On C_SYNC_SOURCE there is no data structure that tracks + * the P_RS_DATA_REPLY blocks that we sent to the SyncTarget. + * And no, it is not the sum of the reference counts in the + * resync_LRU. The resync_LRU tracks the whole operation including + * the disk-IO, while the rs_pending_cnt only tracks the blocks + * on the fly. */ + drbd_rs_cancel_all(mdev); + mdev->rs_total = 0; + mdev->rs_failed = 0; + atomic_set(&mdev->rs_pending_cnt, 0); + wake_up(&mdev->misc_wait); + + /* make sure syncer is stopped and w_resume_next_sg queued */ + del_timer_sync(&mdev->resync_timer); + set_bit(STOP_SYNC_TIMER, &mdev->flags); + resync_timer_fn((unsigned long)mdev); + + /* wait for all w_e_end_data_req, w_e_end_rsdata_req, w_send_barrier, + * w_make_resync_request etc. which may still be on the worker queue + * to be "canceled" */ + drbd_flush_workqueue(mdev); + + /* This also does reclaim_net_ee(). If we do this too early, we might + * miss some resync ee and pages.*/ + drbd_process_done_ee(mdev); + + kfree(mdev->p_uuid); + mdev->p_uuid = NULL; + + if (!mdev->state.susp) + tl_clear(mdev); + + drbd_fail_pending_reads(mdev); + + dev_info(DEV, "Connection closed\n"); + + drbd_md_sync(mdev); + + fp = FP_DONT_CARE; + if (get_ldev(mdev)) { + fp = mdev->ldev->dc.fencing; + put_ldev(mdev); + } + + if (mdev->state.role == R_PRIMARY) { + if (fp >= FP_RESOURCE && mdev->state.pdsk >= D_UNKNOWN) { + enum drbd_disk_state nps = drbd_try_outdate_peer(mdev); + drbd_request_state(mdev, NS(pdsk, nps)); + } + } + + spin_lock_irq(&mdev->req_lock); + os = mdev->state; + if (os.conn >= C_UNCONNECTED) { + /* Do not restart in case we are C_DISCONNECTING */ + ns = os; + ns.conn = C_UNCONNECTED; + rv = _drbd_set_state(mdev, ns, CS_VERBOSE, NULL); + } + spin_unlock_irq(&mdev->req_lock); + + if (os.conn == C_DISCONNECTING) { + struct hlist_head *h; + wait_event(mdev->misc_wait, atomic_read(&mdev->net_cnt) == 0); + + /* we must not free the tl_hash + * while application io is still on the fly */ + wait_event(mdev->misc_wait, atomic_read(&mdev->ap_bio_cnt) == 0); + + spin_lock_irq(&mdev->req_lock); + /* paranoia code */ + for (h = mdev->ee_hash; h < mdev->ee_hash + mdev->ee_hash_s; h++) + if (h->first) + dev_err(DEV, "ASSERT FAILED ee_hash[%u].first == %p, expected NULL\n", + (int)(h - mdev->ee_hash), h->first); + kfree(mdev->ee_hash); + mdev->ee_hash = NULL; + mdev->ee_hash_s = 0; + + /* paranoia code */ + for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++) + if (h->first) + dev_err(DEV, "ASSERT FAILED tl_hash[%u] == %p, expected NULL\n", + (int)(h - mdev->tl_hash), h->first); + kfree(mdev->tl_hash); + mdev->tl_hash = NULL; + mdev->tl_hash_s = 0; + spin_unlock_irq(&mdev->req_lock); + + crypto_free_hash(mdev->cram_hmac_tfm); + mdev->cram_hmac_tfm = NULL; + + kfree(mdev->net_conf); + mdev->net_conf = NULL; + drbd_request_state(mdev, NS(conn, C_STANDALONE)); + } + + /* tcp_close and release of sendpage pages can be deferred. I don't + * want to use SO_LINGER, because apparently it can be deferred for + * more than 20 seconds (longest time I checked). + * + * Actually we don't care for exactly when the network stack does its + * put_page(), but release our reference on these pages right here. + */ + i = drbd_release_ee(mdev, &mdev->net_ee); + if (i) + dev_info(DEV, "net_ee not empty, killed %u entries\n", i); + i = atomic_read(&mdev->pp_in_use); + if (i) + dev_info(DEV, "pp_in_use = %u, expected 0\n", i); + + D_ASSERT(list_empty(&mdev->read_ee)); + D_ASSERT(list_empty(&mdev->active_ee)); + D_ASSERT(list_empty(&mdev->sync_ee)); + D_ASSERT(list_empty(&mdev->done_ee)); + + /* ok, no more ee's on the fly, it is safe to reset the epoch_size */ + atomic_set(&mdev->current_epoch->epoch_size, 0); + D_ASSERT(list_empty(&mdev->current_epoch->list)); +} + +/* + * We support PRO_VERSION_MIN to PRO_VERSION_MAX. The protocol version + * we can agree on is stored in agreed_pro_version. + * + * feature flags and the reserved array should be enough room for future + * enhancements of the handshake protocol, and possible plugins... + * + * for now, they are expected to be zero, but ignored. + */ +static int drbd_send_handshake(struct drbd_conf *mdev) +{ + /* ASSERT current == mdev->receiver ... */ + struct p_handshake *p = &mdev->data.sbuf.handshake; + int ok; + + if (mutex_lock_interruptible(&mdev->data.mutex)) { + dev_err(DEV, "interrupted during initial handshake\n"); + return 0; /* interrupted. not ok. */ + } + + if (mdev->data.socket == NULL) { + mutex_unlock(&mdev->data.mutex); + return 0; + } + + memset(p, 0, sizeof(*p)); + p->protocol_min = cpu_to_be32(PRO_VERSION_MIN); + p->protocol_max = cpu_to_be32(PRO_VERSION_MAX); + ok = _drbd_send_cmd( mdev, mdev->data.socket, P_HAND_SHAKE, + (struct p_header *)p, sizeof(*p), 0 ); + mutex_unlock(&mdev->data.mutex); + return ok; +} + +/* + * return values: + * 1 yes, we have a valid connection + * 0 oops, did not work out, please try again + * -1 peer talks different language, + * no point in trying again, please go standalone. + */ +static int drbd_do_handshake(struct drbd_conf *mdev) +{ + /* ASSERT current == mdev->receiver ... */ + struct p_handshake *p = &mdev->data.rbuf.handshake; + const int expect = sizeof(struct p_handshake) + -sizeof(struct p_header); + int rv; + + rv = drbd_send_handshake(mdev); + if (!rv) + return 0; + + rv = drbd_recv_header(mdev, &p->head); + if (!rv) + return 0; + + if (p->head.command != P_HAND_SHAKE) { + dev_err(DEV, "expected HandShake packet, received: %s (0x%04x)\n", + cmdname(p->head.command), p->head.command); + return -1; + } + + if (p->head.length != expect) { + dev_err(DEV, "expected HandShake length: %u, received: %u\n", + expect, p->head.length); + return -1; + } + + rv = drbd_recv(mdev, &p->head.payload, expect); + + if (rv != expect) { + dev_err(DEV, "short read receiving handshake packet: l=%u\n", rv); + return 0; + } + + p->protocol_min = be32_to_cpu(p->protocol_min); + p->protocol_max = be32_to_cpu(p->protocol_max); + if (p->protocol_max == 0) + p->protocol_max = p->protocol_min; + + if (PRO_VERSION_MAX < p->protocol_min || + PRO_VERSION_MIN > p->protocol_max) + goto incompat; + + mdev->agreed_pro_version = min_t(int, PRO_VERSION_MAX, p->protocol_max); + + dev_info(DEV, "Handshake successful: " + "Agreed network protocol version %d\n", mdev->agreed_pro_version); + + return 1; + + incompat: + dev_err(DEV, "incompatible DRBD dialects: " + "I support %d-%d, peer supports %d-%d\n", + PRO_VERSION_MIN, PRO_VERSION_MAX, + p->protocol_min, p->protocol_max); + return -1; +} + +#if !defined(CONFIG_CRYPTO_HMAC) && !defined(CONFIG_CRYPTO_HMAC_MODULE) +static int drbd_do_auth(struct drbd_conf *mdev) +{ + dev_err(DEV, "This kernel was build without CONFIG_CRYPTO_HMAC.\n"); + dev_err(DEV, "You need to disable 'cram-hmac-alg' in drbd.conf.\n"); + return 0; +} +#else +#define CHALLENGE_LEN 64 +static int drbd_do_auth(struct drbd_conf *mdev) +{ + char my_challenge[CHALLENGE_LEN]; /* 64 Bytes... */ + struct scatterlist sg; + char *response = NULL; + char *right_response = NULL; + char *peers_ch = NULL; + struct p_header p; + unsigned int key_len = strlen(mdev->net_conf->shared_secret); + unsigned int resp_size; + struct hash_desc desc; + int rv; + + desc.tfm = mdev->cram_hmac_tfm; + desc.flags = 0; + + rv = crypto_hash_setkey(mdev->cram_hmac_tfm, + (u8 *)mdev->net_conf->shared_secret, key_len); + if (rv) { + dev_err(DEV, "crypto_hash_setkey() failed with %d\n", rv); + rv = 0; + goto fail; + } + + get_random_bytes(my_challenge, CHALLENGE_LEN); + + rv = drbd_send_cmd2(mdev, P_AUTH_CHALLENGE, my_challenge, CHALLENGE_LEN); + if (!rv) + goto fail; + + rv = drbd_recv_header(mdev, &p); + if (!rv) + goto fail; + + if (p.command != P_AUTH_CHALLENGE) { + dev_err(DEV, "expected AuthChallenge packet, received: %s (0x%04x)\n", + cmdname(p.command), p.command); + rv = 0; + goto fail; + } + + if (p.length > CHALLENGE_LEN*2) { + dev_err(DEV, "expected AuthChallenge payload too big.\n"); + rv = 0; + goto fail; + } + + peers_ch = kmalloc(p.length, GFP_NOIO); + if (peers_ch == NULL) { + dev_err(DEV, "kmalloc of peers_ch failed\n"); + rv = 0; + goto fail; + } + + rv = drbd_recv(mdev, peers_ch, p.length); + + if (rv != p.length) { + dev_err(DEV, "short read AuthChallenge: l=%u\n", rv); + rv = 0; + goto fail; + } + + resp_size = crypto_hash_digestsize(mdev->cram_hmac_tfm); + response = kmalloc(resp_size, GFP_NOIO); + if (response == NULL) { + dev_err(DEV, "kmalloc of response failed\n"); + rv = 0; + goto fail; + } + + sg_init_table(&sg, 1); + sg_set_buf(&sg, peers_ch, p.length); + + rv = crypto_hash_digest(&desc, &sg, sg.length, response); + if (rv) { + dev_err(DEV, "crypto_hash_digest() failed with %d\n", rv); + rv = 0; + goto fail; + } + + rv = drbd_send_cmd2(mdev, P_AUTH_RESPONSE, response, resp_size); + if (!rv) + goto fail; + + rv = drbd_recv_header(mdev, &p); + if (!rv) + goto fail; + + if (p.command != P_AUTH_RESPONSE) { + dev_err(DEV, "expected AuthResponse packet, received: %s (0x%04x)\n", + cmdname(p.command), p.command); + rv = 0; + goto fail; + } + + if (p.length != resp_size) { + dev_err(DEV, "expected AuthResponse payload of wrong size\n"); + rv = 0; + goto fail; + } + + rv = drbd_recv(mdev, response , resp_size); + + if (rv != resp_size) { + dev_err(DEV, "short read receiving AuthResponse: l=%u\n", rv); + rv = 0; + goto fail; + } + + right_response = kmalloc(resp_size, GFP_NOIO); + if (response == NULL) { + dev_err(DEV, "kmalloc of right_response failed\n"); + rv = 0; + goto fail; + } + + sg_set_buf(&sg, my_challenge, CHALLENGE_LEN); + + rv = crypto_hash_digest(&desc, &sg, sg.length, right_response); + if (rv) { + dev_err(DEV, "crypto_hash_digest() failed with %d\n", rv); + rv = 0; + goto fail; + } + + rv = !memcmp(response, right_response, resp_size); + + if (rv) + dev_info(DEV, "Peer authenticated using %d bytes of '%s' HMAC\n", + resp_size, mdev->net_conf->cram_hmac_alg); + + fail: + kfree(peers_ch); + kfree(response); + kfree(right_response); + + return rv; +} +#endif + +int drbdd_init(struct drbd_thread *thi) +{ + struct drbd_conf *mdev = thi->mdev; + unsigned int minor = mdev_to_minor(mdev); + int h; + + sprintf(current->comm, "drbd%d_receiver", minor); + + dev_info(DEV, "receiver (re)started\n"); + + do { + h = drbd_connect(mdev); + if (h == 0) { + drbd_disconnect(mdev); + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + if (h == -1) { + dev_warn(DEV, "Discarding network configuration.\n"); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + } + } while (h == 0); + + if (h > 0) { + if (get_net_conf(mdev)) { + drbdd(mdev); + put_net_conf(mdev); + } + } + + drbd_disconnect(mdev); + + dev_info(DEV, "receiver terminated\n"); + return 0; +} + +/* ********* acknowledge sender ******** */ + +static int got_RqSReply(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_req_state_reply *p = (struct p_req_state_reply *)h; + + int retcode = be32_to_cpu(p->retcode); + + if (retcode >= SS_SUCCESS) { + set_bit(CL_ST_CHG_SUCCESS, &mdev->flags); + } else { + set_bit(CL_ST_CHG_FAIL, &mdev->flags); + dev_err(DEV, "Requested state change failed by peer: %s (%d)\n", + drbd_set_st_err_str(retcode), retcode); + } + wake_up(&mdev->state_wait); + + return TRUE; +} + +static int got_Ping(struct drbd_conf *mdev, struct p_header *h) +{ + return drbd_send_ping_ack(mdev); + +} + +static int got_PingAck(struct drbd_conf *mdev, struct p_header *h) +{ + /* restore idle timeout */ + mdev->meta.socket->sk->sk_rcvtimeo = mdev->net_conf->ping_int*HZ; + + return TRUE; +} + +static int got_IsInSync(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_block_ack *p = (struct p_block_ack *)h; + sector_t sector = be64_to_cpu(p->sector); + int blksize = be32_to_cpu(p->blksize); + + D_ASSERT(mdev->agreed_pro_version >= 89); + + update_peer_seq(mdev, be32_to_cpu(p->seq_num)); + + drbd_rs_complete_io(mdev, sector); + drbd_set_in_sync(mdev, sector, blksize); + /* rs_same_csums is supposed to count in units of BM_BLOCK_SIZE */ + mdev->rs_same_csum += (blksize >> BM_BLOCK_SHIFT); + dec_rs_pending(mdev); + + return TRUE; +} + +/* when we receive the ACK for a write request, + * verify that we actually know about it */ +static struct drbd_request *_ack_id_to_req(struct drbd_conf *mdev, + u64 id, sector_t sector) +{ + struct hlist_head *slot = tl_hash_slot(mdev, sector); + struct hlist_node *n; + struct drbd_request *req; + + hlist_for_each_entry(req, n, slot, colision) { + if ((unsigned long)req == (unsigned long)id) { + if (req->sector != sector) { + dev_err(DEV, "_ack_id_to_req: found req %p but it has " + "wrong sector (%llus versus %llus)\n", req, + (unsigned long long)req->sector, + (unsigned long long)sector); + break; + } + return req; + } + } + dev_err(DEV, "_ack_id_to_req: failed to find req %p, sector %llus in list\n", + (void *)(unsigned long)id, (unsigned long long)sector); + return NULL; +} + +typedef struct drbd_request *(req_validator_fn) + (struct drbd_conf *mdev, u64 id, sector_t sector); + +static int validate_req_change_req_state(struct drbd_conf *mdev, + u64 id, sector_t sector, req_validator_fn validator, + const char *func, enum drbd_req_event what) +{ + struct drbd_request *req; + struct bio_and_error m; + + spin_lock_irq(&mdev->req_lock); + req = validator(mdev, id, sector); + if (unlikely(!req)) { + spin_unlock_irq(&mdev->req_lock); + dev_err(DEV, "%s: got a corrupt block_id/sector pair\n", func); + return FALSE; + } + __req_mod(req, what, &m); + spin_unlock_irq(&mdev->req_lock); + + if (m.bio) + complete_master_bio(mdev, &m); + return TRUE; +} + +static int got_BlockAck(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_block_ack *p = (struct p_block_ack *)h; + sector_t sector = be64_to_cpu(p->sector); + int blksize = be32_to_cpu(p->blksize); + enum drbd_req_event what; + + update_peer_seq(mdev, be32_to_cpu(p->seq_num)); + + if (is_syncer_block_id(p->block_id)) { + drbd_set_in_sync(mdev, sector, blksize); + dec_rs_pending(mdev); + return TRUE; + } + switch (be16_to_cpu(h->command)) { + case P_RS_WRITE_ACK: + D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C); + what = write_acked_by_peer_and_sis; + break; + case P_WRITE_ACK: + D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C); + what = write_acked_by_peer; + break; + case P_RECV_ACK: + D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_B); + what = recv_acked_by_peer; + break; + case P_DISCARD_ACK: + D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C); + what = conflict_discarded_by_peer; + break; + default: + D_ASSERT(0); + return FALSE; + } + + return validate_req_change_req_state(mdev, p->block_id, sector, + _ack_id_to_req, __func__ , what); +} + +static int got_NegAck(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_block_ack *p = (struct p_block_ack *)h; + sector_t sector = be64_to_cpu(p->sector); + + if (__ratelimit(&drbd_ratelimit_state)) + dev_warn(DEV, "Got NegAck packet. Peer is in troubles?\n"); + + update_peer_seq(mdev, be32_to_cpu(p->seq_num)); + + if (is_syncer_block_id(p->block_id)) { + int size = be32_to_cpu(p->blksize); + dec_rs_pending(mdev); + drbd_rs_failed_io(mdev, sector, size); + return TRUE; + } + return validate_req_change_req_state(mdev, p->block_id, sector, + _ack_id_to_req, __func__ , neg_acked); +} + +static int got_NegDReply(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_block_ack *p = (struct p_block_ack *)h; + sector_t sector = be64_to_cpu(p->sector); + + update_peer_seq(mdev, be32_to_cpu(p->seq_num)); + dev_err(DEV, "Got NegDReply; Sector %llus, len %u; Fail original request.\n", + (unsigned long long)sector, be32_to_cpu(p->blksize)); + + return validate_req_change_req_state(mdev, p->block_id, sector, + _ar_id_to_req, __func__ , neg_acked); +} + +static int got_NegRSDReply(struct drbd_conf *mdev, struct p_header *h) +{ + sector_t sector; + int size; + struct p_block_ack *p = (struct p_block_ack *)h; + + sector = be64_to_cpu(p->sector); + size = be32_to_cpu(p->blksize); + D_ASSERT(p->block_id == ID_SYNCER); + + update_peer_seq(mdev, be32_to_cpu(p->seq_num)); + + dec_rs_pending(mdev); + + if (get_ldev_if_state(mdev, D_FAILED)) { + drbd_rs_complete_io(mdev, sector); + drbd_rs_failed_io(mdev, sector, size); + put_ldev(mdev); + } + + return TRUE; +} + +static int got_BarrierAck(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_barrier_ack *p = (struct p_barrier_ack *)h; + + tl_release(mdev, p->barrier, be32_to_cpu(p->set_size)); + + return TRUE; +} + +static int got_OVResult(struct drbd_conf *mdev, struct p_header *h) +{ + struct p_block_ack *p = (struct p_block_ack *)h; + struct drbd_work *w; + sector_t sector; + int size; + + sector = be64_to_cpu(p->sector); + size = be32_to_cpu(p->blksize); + + update_peer_seq(mdev, be32_to_cpu(p->seq_num)); + + if (be64_to_cpu(p->block_id) == ID_OUT_OF_SYNC) + drbd_ov_oos_found(mdev, sector, size); + else + ov_oos_print(mdev); + + drbd_rs_complete_io(mdev, sector); + dec_rs_pending(mdev); + + if (--mdev->ov_left == 0) { + w = kmalloc(sizeof(*w), GFP_NOIO); + if (w) { + w->cb = w_ov_finished; + drbd_queue_work_front(&mdev->data.work, w); + } else { + dev_err(DEV, "kmalloc(w) failed."); + ov_oos_print(mdev); + drbd_resync_finished(mdev); + } + } + return TRUE; +} + +struct asender_cmd { + size_t pkt_size; + int (*process)(struct drbd_conf *mdev, struct p_header *h); +}; + +static struct asender_cmd *get_asender_cmd(int cmd) +{ + static struct asender_cmd asender_tbl[] = { + /* anything missing from this table is in + * the drbd_cmd_handler (drbd_default_handler) table, + * see the beginning of drbdd() */ + [P_PING] = { sizeof(struct p_header), got_Ping }, + [P_PING_ACK] = { sizeof(struct p_header), got_PingAck }, + [P_RECV_ACK] = { sizeof(struct p_block_ack), got_BlockAck }, + [P_WRITE_ACK] = { sizeof(struct p_block_ack), got_BlockAck }, + [P_RS_WRITE_ACK] = { sizeof(struct p_block_ack), got_BlockAck }, + [P_DISCARD_ACK] = { sizeof(struct p_block_ack), got_BlockAck }, + [P_NEG_ACK] = { sizeof(struct p_block_ack), got_NegAck }, + [P_NEG_DREPLY] = { sizeof(struct p_block_ack), got_NegDReply }, + [P_NEG_RS_DREPLY] = { sizeof(struct p_block_ack), got_NegRSDReply}, + [P_OV_RESULT] = { sizeof(struct p_block_ack), got_OVResult }, + [P_BARRIER_ACK] = { sizeof(struct p_barrier_ack), got_BarrierAck }, + [P_STATE_CHG_REPLY] = { sizeof(struct p_req_state_reply), got_RqSReply }, + [P_RS_IS_IN_SYNC] = { sizeof(struct p_block_ack), got_IsInSync }, + [P_MAX_CMD] = { 0, NULL }, + }; + if (cmd > P_MAX_CMD || asender_tbl[cmd].process == NULL) + return NULL; + return &asender_tbl[cmd]; +} + +int drbd_asender(struct drbd_thread *thi) +{ + struct drbd_conf *mdev = thi->mdev; + struct p_header *h = &mdev->meta.rbuf.header; + struct asender_cmd *cmd = NULL; + + int rv, len; + void *buf = h; + int received = 0; + int expect = sizeof(struct p_header); + int empty; + + sprintf(current->comm, "drbd%d_asender", mdev_to_minor(mdev)); + + current->policy = SCHED_RR; /* Make this a realtime task! */ + current->rt_priority = 2; /* more important than all other tasks */ + + while (get_t_state(thi) == Running) { + drbd_thread_current_set_cpu(mdev); + if (test_and_clear_bit(SEND_PING, &mdev->flags)) { + ERR_IF(!drbd_send_ping(mdev)) goto reconnect; + mdev->meta.socket->sk->sk_rcvtimeo = + mdev->net_conf->ping_timeo*HZ/10; + } + + /* conditionally cork; + * it may hurt latency if we cork without much to send */ + if (!mdev->net_conf->no_cork && + 3 < atomic_read(&mdev->unacked_cnt)) + drbd_tcp_cork(mdev->meta.socket); + while (1) { + clear_bit(SIGNAL_ASENDER, &mdev->flags); + flush_signals(current); + if (!drbd_process_done_ee(mdev)) { + dev_err(DEV, "process_done_ee() = NOT_OK\n"); + goto reconnect; + } + /* to avoid race with newly queued ACKs */ + set_bit(SIGNAL_ASENDER, &mdev->flags); + spin_lock_irq(&mdev->req_lock); + empty = list_empty(&mdev->done_ee); + spin_unlock_irq(&mdev->req_lock); + /* new ack may have been queued right here, + * but then there is also a signal pending, + * and we start over... */ + if (empty) + break; + } + /* but unconditionally uncork unless disabled */ + if (!mdev->net_conf->no_cork) + drbd_tcp_uncork(mdev->meta.socket); + + /* short circuit, recv_msg would return EINTR anyways. */ + if (signal_pending(current)) + continue; + + rv = drbd_recv_short(mdev, mdev->meta.socket, + buf, expect-received, 0); + clear_bit(SIGNAL_ASENDER, &mdev->flags); + + flush_signals(current); + + /* Note: + * -EINTR (on meta) we got a signal + * -EAGAIN (on meta) rcvtimeo expired + * -ECONNRESET other side closed the connection + * -ERESTARTSYS (on data) we got a signal + * rv < 0 other than above: unexpected error! + * rv == expected: full header or command + * rv < expected: "woken" by signal during receive + * rv == 0 : "connection shut down by peer" + */ + if (likely(rv > 0)) { + received += rv; + buf += rv; + } else if (rv == 0) { + dev_err(DEV, "meta connection shut down by peer.\n"); + goto reconnect; + } else if (rv == -EAGAIN) { + if (mdev->meta.socket->sk->sk_rcvtimeo == + mdev->net_conf->ping_timeo*HZ/10) { + dev_err(DEV, "PingAck did not arrive in time.\n"); + goto reconnect; + } + set_bit(SEND_PING, &mdev->flags); + continue; + } else if (rv == -EINTR) { + continue; + } else { + dev_err(DEV, "sock_recvmsg returned %d\n", rv); + goto reconnect; + } + + if (received == expect && cmd == NULL) { + if (unlikely(h->magic != BE_DRBD_MAGIC)) { + dev_err(DEV, "magic?? on meta m: 0x%lx c: %d l: %d\n", + (long)be32_to_cpu(h->magic), + h->command, h->length); + goto reconnect; + } + cmd = get_asender_cmd(be16_to_cpu(h->command)); + len = be16_to_cpu(h->length); + if (unlikely(cmd == NULL)) { + dev_err(DEV, "unknown command?? on meta m: 0x%lx c: %d l: %d\n", + (long)be32_to_cpu(h->magic), + h->command, h->length); + goto disconnect; + } + expect = cmd->pkt_size; + ERR_IF(len != expect-sizeof(struct p_header)) + goto reconnect; + } + if (received == expect) { + D_ASSERT(cmd != NULL); + if (!cmd->process(mdev, h)) + goto reconnect; + + buf = h; + received = 0; + expect = sizeof(struct p_header); + cmd = NULL; + } + } + + if (0) { +reconnect: + drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE)); + } + if (0) { +disconnect: + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + } + clear_bit(SIGNAL_ASENDER, &mdev->flags); + + D_ASSERT(mdev->state.conn < C_CONNECTED); + dev_info(DEV, "asender terminated\n"); + + return 0; +} diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c new file mode 100644 index 000000000000..de81ab7b4627 --- /dev/null +++ b/drivers/block/drbd/drbd_req.c @@ -0,0 +1,1125 @@ +/* + drbd_req.c + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. + Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include <linux/module.h> + +#include <linux/slab.h> +#include <linux/drbd.h> +#include "drbd_int.h" +#include "drbd_req.h" + + +/* Update disk stats at start of I/O request */ +static void _drbd_start_io_acct(struct drbd_conf *mdev, struct drbd_request *req, struct bio *bio) +{ + const int rw = bio_data_dir(bio); + int cpu; + cpu = part_stat_lock(); + part_stat_inc(cpu, &mdev->vdisk->part0, ios[rw]); + part_stat_add(cpu, &mdev->vdisk->part0, sectors[rw], bio_sectors(bio)); + part_inc_in_flight(&mdev->vdisk->part0, rw); + part_stat_unlock(); +} + +/* Update disk stats when completing request upwards */ +static void _drbd_end_io_acct(struct drbd_conf *mdev, struct drbd_request *req) +{ + int rw = bio_data_dir(req->master_bio); + unsigned long duration = jiffies - req->start_time; + int cpu; + cpu = part_stat_lock(); + part_stat_add(cpu, &mdev->vdisk->part0, ticks[rw], duration); + part_round_stats(cpu, &mdev->vdisk->part0); + part_dec_in_flight(&mdev->vdisk->part0, rw); + part_stat_unlock(); +} + +static void _req_is_done(struct drbd_conf *mdev, struct drbd_request *req, const int rw) +{ + const unsigned long s = req->rq_state; + /* if it was a write, we may have to set the corresponding + * bit(s) out-of-sync first. If it had a local part, we need to + * release the reference to the activity log. */ + if (rw == WRITE) { + /* remove it from the transfer log. + * well, only if it had been there in the first + * place... if it had not (local only or conflicting + * and never sent), it should still be "empty" as + * initialized in drbd_req_new(), so we can list_del() it + * here unconditionally */ + list_del(&req->tl_requests); + /* Set out-of-sync unless both OK flags are set + * (local only or remote failed). + * Other places where we set out-of-sync: + * READ with local io-error */ + if (!(s & RQ_NET_OK) || !(s & RQ_LOCAL_OK)) + drbd_set_out_of_sync(mdev, req->sector, req->size); + + if ((s & RQ_NET_OK) && (s & RQ_LOCAL_OK) && (s & RQ_NET_SIS)) + drbd_set_in_sync(mdev, req->sector, req->size); + + /* one might be tempted to move the drbd_al_complete_io + * to the local io completion callback drbd_endio_pri. + * but, if this was a mirror write, we may only + * drbd_al_complete_io after this is RQ_NET_DONE, + * otherwise the extent could be dropped from the al + * before it has actually been written on the peer. + * if we crash before our peer knows about the request, + * but after the extent has been dropped from the al, + * we would forget to resync the corresponding extent. + */ + if (s & RQ_LOCAL_MASK) { + if (get_ldev_if_state(mdev, D_FAILED)) { + drbd_al_complete_io(mdev, req->sector); + put_ldev(mdev); + } else if (__ratelimit(&drbd_ratelimit_state)) { + dev_warn(DEV, "Should have called drbd_al_complete_io(, %llu), " + "but my Disk seems to have failed :(\n", + (unsigned long long) req->sector); + } + } + } + + /* if it was a local io error, we want to notify our + * peer about that, and see if we need to + * detach the disk and stuff. + * to avoid allocating some special work + * struct, reuse the request. */ + + /* THINK + * why do we do this not when we detect the error, + * but delay it until it is "done", i.e. possibly + * until the next barrier ack? */ + + if (rw == WRITE && + ((s & RQ_LOCAL_MASK) && !(s & RQ_LOCAL_OK))) { + if (!(req->w.list.next == LIST_POISON1 || + list_empty(&req->w.list))) { + /* DEBUG ASSERT only; if this triggers, we + * probably corrupt the worker list here */ + dev_err(DEV, "req->w.list.next = %p\n", req->w.list.next); + dev_err(DEV, "req->w.list.prev = %p\n", req->w.list.prev); + } + req->w.cb = w_io_error; + drbd_queue_work(&mdev->data.work, &req->w); + /* drbd_req_free() is done in w_io_error */ + } else { + drbd_req_free(req); + } +} + +static void queue_barrier(struct drbd_conf *mdev) +{ + struct drbd_tl_epoch *b; + + /* We are within the req_lock. Once we queued the barrier for sending, + * we set the CREATE_BARRIER bit. It is cleared as soon as a new + * barrier/epoch object is added. This is the only place this bit is + * set. It indicates that the barrier for this epoch is already queued, + * and no new epoch has been created yet. */ + if (test_bit(CREATE_BARRIER, &mdev->flags)) + return; + + b = mdev->newest_tle; + b->w.cb = w_send_barrier; + /* inc_ap_pending done here, so we won't + * get imbalanced on connection loss. + * dec_ap_pending will be done in got_BarrierAck + * or (on connection loss) in tl_clear. */ + inc_ap_pending(mdev); + drbd_queue_work(&mdev->data.work, &b->w); + set_bit(CREATE_BARRIER, &mdev->flags); +} + +static void _about_to_complete_local_write(struct drbd_conf *mdev, + struct drbd_request *req) +{ + const unsigned long s = req->rq_state; + struct drbd_request *i; + struct drbd_epoch_entry *e; + struct hlist_node *n; + struct hlist_head *slot; + + /* before we can signal completion to the upper layers, + * we may need to close the current epoch */ + if (mdev->state.conn >= C_CONNECTED && + req->epoch == mdev->newest_tle->br_number) + queue_barrier(mdev); + + /* we need to do the conflict detection stuff, + * if we have the ee_hash (two_primaries) and + * this has been on the network */ + if ((s & RQ_NET_DONE) && mdev->ee_hash != NULL) { + const sector_t sector = req->sector; + const int size = req->size; + + /* ASSERT: + * there must be no conflicting requests, since + * they must have been failed on the spot */ +#define OVERLAPS overlaps(sector, size, i->sector, i->size) + slot = tl_hash_slot(mdev, sector); + hlist_for_each_entry(i, n, slot, colision) { + if (OVERLAPS) { + dev_alert(DEV, "LOGIC BUG: completed: %p %llus +%u; " + "other: %p %llus +%u\n", + req, (unsigned long long)sector, size, + i, (unsigned long long)i->sector, i->size); + } + } + + /* maybe "wake" those conflicting epoch entries + * that wait for this request to finish. + * + * currently, there can be only _one_ such ee + * (well, or some more, which would be pending + * P_DISCARD_ACK not yet sent by the asender...), + * since we block the receiver thread upon the + * first conflict detection, which will wait on + * misc_wait. maybe we want to assert that? + * + * anyways, if we found one, + * we just have to do a wake_up. */ +#undef OVERLAPS +#define OVERLAPS overlaps(sector, size, e->sector, e->size) + slot = ee_hash_slot(mdev, req->sector); + hlist_for_each_entry(e, n, slot, colision) { + if (OVERLAPS) { + wake_up(&mdev->misc_wait); + break; + } + } + } +#undef OVERLAPS +} + +void complete_master_bio(struct drbd_conf *mdev, + struct bio_and_error *m) +{ + bio_endio(m->bio, m->error); + dec_ap_bio(mdev); +} + +/* Helper for __req_mod(). + * Set m->bio to the master bio, if it is fit to be completed, + * or leave it alone (it is initialized to NULL in __req_mod), + * if it has already been completed, or cannot be completed yet. + * If m->bio is set, the error status to be returned is placed in m->error. + */ +void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m) +{ + const unsigned long s = req->rq_state; + struct drbd_conf *mdev = req->mdev; + /* only WRITES may end up here without a master bio (on barrier ack) */ + int rw = req->master_bio ? bio_data_dir(req->master_bio) : WRITE; + + /* we must not complete the master bio, while it is + * still being processed by _drbd_send_zc_bio (drbd_send_dblock) + * not yet acknowledged by the peer + * not yet completed by the local io subsystem + * these flags may get cleared in any order by + * the worker, + * the receiver, + * the bio_endio completion callbacks. + */ + if (s & RQ_NET_QUEUED) + return; + if (s & RQ_NET_PENDING) + return; + if (s & RQ_LOCAL_PENDING) + return; + + if (req->master_bio) { + /* this is data_received (remote read) + * or protocol C P_WRITE_ACK + * or protocol B P_RECV_ACK + * or protocol A "handed_over_to_network" (SendAck) + * or canceled or failed, + * or killed from the transfer log due to connection loss. + */ + + /* + * figure out whether to report success or failure. + * + * report success when at least one of the operations succeeded. + * or, to put the other way, + * only report failure, when both operations failed. + * + * what to do about the failures is handled elsewhere. + * what we need to do here is just: complete the master_bio. + * + * local completion error, if any, has been stored as ERR_PTR + * in private_bio within drbd_endio_pri. + */ + int ok = (s & RQ_LOCAL_OK) || (s & RQ_NET_OK); + int error = PTR_ERR(req->private_bio); + + /* remove the request from the conflict detection + * respective block_id verification hash */ + if (!hlist_unhashed(&req->colision)) + hlist_del(&req->colision); + else + D_ASSERT((s & RQ_NET_MASK) == 0); + + /* for writes we need to do some extra housekeeping */ + if (rw == WRITE) + _about_to_complete_local_write(mdev, req); + + /* Update disk stats */ + _drbd_end_io_acct(mdev, req); + + m->error = ok ? 0 : (error ?: -EIO); + m->bio = req->master_bio; + req->master_bio = NULL; + } + + if ((s & RQ_NET_MASK) == 0 || (s & RQ_NET_DONE)) { + /* this is disconnected (local only) operation, + * or protocol C P_WRITE_ACK, + * or protocol A or B P_BARRIER_ACK, + * or killed from the transfer log due to connection loss. */ + _req_is_done(mdev, req, rw); + } + /* else: network part and not DONE yet. that is + * protocol A or B, barrier ack still pending... */ +} + +/* + * checks whether there was an overlapping request + * or ee already registered. + * + * if so, return 1, in which case this request is completed on the spot, + * without ever being submitted or send. + * + * return 0 if it is ok to submit this request. + * + * NOTE: + * paranoia: assume something above us is broken, and issues different write + * requests for the same block simultaneously... + * + * To ensure these won't be reordered differently on both nodes, resulting in + * diverging data sets, we discard the later one(s). Not that this is supposed + * to happen, but this is the rationale why we also have to check for + * conflicting requests with local origin, and why we have to do so regardless + * of whether we allowed multiple primaries. + * + * BTW, in case we only have one primary, the ee_hash is empty anyways, and the + * second hlist_for_each_entry becomes a noop. This is even simpler than to + * grab a reference on the net_conf, and check for the two_primaries flag... + */ +static int _req_conflicts(struct drbd_request *req) +{ + struct drbd_conf *mdev = req->mdev; + const sector_t sector = req->sector; + const int size = req->size; + struct drbd_request *i; + struct drbd_epoch_entry *e; + struct hlist_node *n; + struct hlist_head *slot; + + D_ASSERT(hlist_unhashed(&req->colision)); + + if (!get_net_conf(mdev)) + return 0; + + /* BUG_ON */ + ERR_IF (mdev->tl_hash_s == 0) + goto out_no_conflict; + BUG_ON(mdev->tl_hash == NULL); + +#define OVERLAPS overlaps(i->sector, i->size, sector, size) + slot = tl_hash_slot(mdev, sector); + hlist_for_each_entry(i, n, slot, colision) { + if (OVERLAPS) { + dev_alert(DEV, "%s[%u] Concurrent local write detected! " + "[DISCARD L] new: %llus +%u; " + "pending: %llus +%u\n", + current->comm, current->pid, + (unsigned long long)sector, size, + (unsigned long long)i->sector, i->size); + goto out_conflict; + } + } + + if (mdev->ee_hash_s) { + /* now, check for overlapping requests with remote origin */ + BUG_ON(mdev->ee_hash == NULL); +#undef OVERLAPS +#define OVERLAPS overlaps(e->sector, e->size, sector, size) + slot = ee_hash_slot(mdev, sector); + hlist_for_each_entry(e, n, slot, colision) { + if (OVERLAPS) { + dev_alert(DEV, "%s[%u] Concurrent remote write detected!" + " [DISCARD L] new: %llus +%u; " + "pending: %llus +%u\n", + current->comm, current->pid, + (unsigned long long)sector, size, + (unsigned long long)e->sector, e->size); + goto out_conflict; + } + } + } +#undef OVERLAPS + +out_no_conflict: + /* this is like it should be, and what we expected. + * our users do behave after all... */ + put_net_conf(mdev); + return 0; + +out_conflict: + put_net_conf(mdev); + return 1; +} + +/* obviously this could be coded as many single functions + * instead of one huge switch, + * or by putting the code directly in the respective locations + * (as it has been before). + * + * but having it this way + * enforces that it is all in this one place, where it is easier to audit, + * it makes it obvious that whatever "event" "happens" to a request should + * happen "atomically" within the req_lock, + * and it enforces that we have to think in a very structured manner + * about the "events" that may happen to a request during its life time ... + */ +void __req_mod(struct drbd_request *req, enum drbd_req_event what, + struct bio_and_error *m) +{ + struct drbd_conf *mdev = req->mdev; + m->bio = NULL; + + switch (what) { + default: + dev_err(DEV, "LOGIC BUG in %s:%u\n", __FILE__ , __LINE__); + break; + + /* does not happen... + * initialization done in drbd_req_new + case created: + break; + */ + + case to_be_send: /* via network */ + /* reached via drbd_make_request_common + * and from w_read_retry_remote */ + D_ASSERT(!(req->rq_state & RQ_NET_MASK)); + req->rq_state |= RQ_NET_PENDING; + inc_ap_pending(mdev); + break; + + case to_be_submitted: /* locally */ + /* reached via drbd_make_request_common */ + D_ASSERT(!(req->rq_state & RQ_LOCAL_MASK)); + req->rq_state |= RQ_LOCAL_PENDING; + break; + + case completed_ok: + if (bio_data_dir(req->master_bio) == WRITE) + mdev->writ_cnt += req->size>>9; + else + mdev->read_cnt += req->size>>9; + + req->rq_state |= (RQ_LOCAL_COMPLETED|RQ_LOCAL_OK); + req->rq_state &= ~RQ_LOCAL_PENDING; + + _req_may_be_done(req, m); + put_ldev(mdev); + break; + + case write_completed_with_error: + req->rq_state |= RQ_LOCAL_COMPLETED; + req->rq_state &= ~RQ_LOCAL_PENDING; + + dev_alert(DEV, "Local WRITE failed sec=%llus size=%u\n", + (unsigned long long)req->sector, req->size); + /* and now: check how to handle local io error. */ + __drbd_chk_io_error(mdev, FALSE); + _req_may_be_done(req, m); + put_ldev(mdev); + break; + + case read_ahead_completed_with_error: + /* it is legal to fail READA */ + req->rq_state |= RQ_LOCAL_COMPLETED; + req->rq_state &= ~RQ_LOCAL_PENDING; + _req_may_be_done(req, m); + put_ldev(mdev); + break; + + case read_completed_with_error: + drbd_set_out_of_sync(mdev, req->sector, req->size); + + req->rq_state |= RQ_LOCAL_COMPLETED; + req->rq_state &= ~RQ_LOCAL_PENDING; + + dev_alert(DEV, "Local READ failed sec=%llus size=%u\n", + (unsigned long long)req->sector, req->size); + /* _req_mod(req,to_be_send); oops, recursion... */ + D_ASSERT(!(req->rq_state & RQ_NET_MASK)); + req->rq_state |= RQ_NET_PENDING; + inc_ap_pending(mdev); + + __drbd_chk_io_error(mdev, FALSE); + put_ldev(mdev); + /* NOTE: if we have no connection, + * or know the peer has no good data either, + * then we don't actually need to "queue_for_net_read", + * but we do so anyways, since the drbd_io_error() + * and the potential state change to "Diskless" + * needs to be done from process context */ + + /* fall through: _req_mod(req,queue_for_net_read); */ + + case queue_for_net_read: + /* READ or READA, and + * no local disk, + * or target area marked as invalid, + * or just got an io-error. */ + /* from drbd_make_request_common + * or from bio_endio during read io-error recovery */ + + /* so we can verify the handle in the answer packet + * corresponding hlist_del is in _req_may_be_done() */ + hlist_add_head(&req->colision, ar_hash_slot(mdev, req->sector)); + + set_bit(UNPLUG_REMOTE, &mdev->flags); + + D_ASSERT(req->rq_state & RQ_NET_PENDING); + req->rq_state |= RQ_NET_QUEUED; + req->w.cb = (req->rq_state & RQ_LOCAL_MASK) + ? w_read_retry_remote + : w_send_read_req; + drbd_queue_work(&mdev->data.work, &req->w); + break; + + case queue_for_net_write: + /* assert something? */ + /* from drbd_make_request_common only */ + + hlist_add_head(&req->colision, tl_hash_slot(mdev, req->sector)); + /* corresponding hlist_del is in _req_may_be_done() */ + + /* NOTE + * In case the req ended up on the transfer log before being + * queued on the worker, it could lead to this request being + * missed during cleanup after connection loss. + * So we have to do both operations here, + * within the same lock that protects the transfer log. + * + * _req_add_to_epoch(req); this has to be after the + * _maybe_start_new_epoch(req); which happened in + * drbd_make_request_common, because we now may set the bit + * again ourselves to close the current epoch. + * + * Add req to the (now) current epoch (barrier). */ + + /* otherwise we may lose an unplug, which may cause some remote + * io-scheduler timeout to expire, increasing maximum latency, + * hurting performance. */ + set_bit(UNPLUG_REMOTE, &mdev->flags); + + /* see drbd_make_request_common, + * just after it grabs the req_lock */ + D_ASSERT(test_bit(CREATE_BARRIER, &mdev->flags) == 0); + + req->epoch = mdev->newest_tle->br_number; + list_add_tail(&req->tl_requests, + &mdev->newest_tle->requests); + + /* increment size of current epoch */ + mdev->newest_tle->n_req++; + + /* queue work item to send data */ + D_ASSERT(req->rq_state & RQ_NET_PENDING); + req->rq_state |= RQ_NET_QUEUED; + req->w.cb = w_send_dblock; + drbd_queue_work(&mdev->data.work, &req->w); + + /* close the epoch, in case it outgrew the limit */ + if (mdev->newest_tle->n_req >= mdev->net_conf->max_epoch_size) + queue_barrier(mdev); + + break; + + case send_canceled: + /* treat it the same */ + case send_failed: + /* real cleanup will be done from tl_clear. just update flags + * so it is no longer marked as on the worker queue */ + req->rq_state &= ~RQ_NET_QUEUED; + /* if we did it right, tl_clear should be scheduled only after + * this, so this should not be necessary! */ + _req_may_be_done(req, m); + break; + + case handed_over_to_network: + /* assert something? */ + if (bio_data_dir(req->master_bio) == WRITE && + mdev->net_conf->wire_protocol == DRBD_PROT_A) { + /* this is what is dangerous about protocol A: + * pretend it was successfully written on the peer. */ + if (req->rq_state & RQ_NET_PENDING) { + dec_ap_pending(mdev); + req->rq_state &= ~RQ_NET_PENDING; + req->rq_state |= RQ_NET_OK; + } /* else: neg-ack was faster... */ + /* it is still not yet RQ_NET_DONE until the + * corresponding epoch barrier got acked as well, + * so we know what to dirty on connection loss */ + } + req->rq_state &= ~RQ_NET_QUEUED; + req->rq_state |= RQ_NET_SENT; + /* because _drbd_send_zc_bio could sleep, and may want to + * dereference the bio even after the "write_acked_by_peer" and + * "completed_ok" events came in, once we return from + * _drbd_send_zc_bio (drbd_send_dblock), we have to check + * whether it is done already, and end it. */ + _req_may_be_done(req, m); + break; + + case connection_lost_while_pending: + /* transfer log cleanup after connection loss */ + /* assert something? */ + if (req->rq_state & RQ_NET_PENDING) + dec_ap_pending(mdev); + req->rq_state &= ~(RQ_NET_OK|RQ_NET_PENDING); + req->rq_state |= RQ_NET_DONE; + /* if it is still queued, we may not complete it here. + * it will be canceled soon. */ + if (!(req->rq_state & RQ_NET_QUEUED)) + _req_may_be_done(req, m); + break; + + case write_acked_by_peer_and_sis: + req->rq_state |= RQ_NET_SIS; + case conflict_discarded_by_peer: + /* for discarded conflicting writes of multiple primaries, + * there is no need to keep anything in the tl, potential + * node crashes are covered by the activity log. */ + if (what == conflict_discarded_by_peer) + dev_alert(DEV, "Got DiscardAck packet %llus +%u!" + " DRBD is not a random data generator!\n", + (unsigned long long)req->sector, req->size); + req->rq_state |= RQ_NET_DONE; + /* fall through */ + case write_acked_by_peer: + /* protocol C; successfully written on peer. + * Nothing to do here. + * We want to keep the tl in place for all protocols, to cater + * for volatile write-back caches on lower level devices. + * + * A barrier request is expected to have forced all prior + * requests onto stable storage, so completion of a barrier + * request could set NET_DONE right here, and not wait for the + * P_BARRIER_ACK, but that is an unnecessary optimization. */ + + /* this makes it effectively the same as for: */ + case recv_acked_by_peer: + /* protocol B; pretends to be successfully written on peer. + * see also notes above in handed_over_to_network about + * protocol != C */ + req->rq_state |= RQ_NET_OK; + D_ASSERT(req->rq_state & RQ_NET_PENDING); + dec_ap_pending(mdev); + req->rq_state &= ~RQ_NET_PENDING; + _req_may_be_done(req, m); + break; + + case neg_acked: + /* assert something? */ + if (req->rq_state & RQ_NET_PENDING) + dec_ap_pending(mdev); + req->rq_state &= ~(RQ_NET_OK|RQ_NET_PENDING); + + req->rq_state |= RQ_NET_DONE; + _req_may_be_done(req, m); + /* else: done by handed_over_to_network */ + break; + + case barrier_acked: + if (req->rq_state & RQ_NET_PENDING) { + /* barrier came in before all requests have been acked. + * this is bad, because if the connection is lost now, + * we won't be able to clean them up... */ + dev_err(DEV, "FIXME (barrier_acked but pending)\n"); + list_move(&req->tl_requests, &mdev->out_of_sequence_requests); + } + D_ASSERT(req->rq_state & RQ_NET_SENT); + req->rq_state |= RQ_NET_DONE; + _req_may_be_done(req, m); + break; + + case data_received: + D_ASSERT(req->rq_state & RQ_NET_PENDING); + dec_ap_pending(mdev); + req->rq_state &= ~RQ_NET_PENDING; + req->rq_state |= (RQ_NET_OK|RQ_NET_DONE); + _req_may_be_done(req, m); + break; + }; +} + +/* we may do a local read if: + * - we are consistent (of course), + * - or we are generally inconsistent, + * BUT we are still/already IN SYNC for this area. + * since size may be bigger than BM_BLOCK_SIZE, + * we may need to check several bits. + */ +static int drbd_may_do_local_read(struct drbd_conf *mdev, sector_t sector, int size) +{ + unsigned long sbnr, ebnr; + sector_t esector, nr_sectors; + + if (mdev->state.disk == D_UP_TO_DATE) + return 1; + if (mdev->state.disk >= D_OUTDATED) + return 0; + if (mdev->state.disk < D_INCONSISTENT) + return 0; + /* state.disk == D_INCONSISTENT We will have a look at the BitMap */ + nr_sectors = drbd_get_capacity(mdev->this_bdev); + esector = sector + (size >> 9) - 1; + + D_ASSERT(sector < nr_sectors); + D_ASSERT(esector < nr_sectors); + + sbnr = BM_SECT_TO_BIT(sector); + ebnr = BM_SECT_TO_BIT(esector); + + return 0 == drbd_bm_count_bits(mdev, sbnr, ebnr); +} + +static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio) +{ + const int rw = bio_rw(bio); + const int size = bio->bi_size; + const sector_t sector = bio->bi_sector; + struct drbd_tl_epoch *b = NULL; + struct drbd_request *req; + int local, remote; + int err = -EIO; + + /* allocate outside of all locks; */ + req = drbd_req_new(mdev, bio); + if (!req) { + dec_ap_bio(mdev); + /* only pass the error to the upper layers. + * if user cannot handle io errors, that's not our business. */ + dev_err(DEV, "could not kmalloc() req\n"); + bio_endio(bio, -ENOMEM); + return 0; + } + + local = get_ldev(mdev); + if (!local) { + bio_put(req->private_bio); /* or we get a bio leak */ + req->private_bio = NULL; + } + if (rw == WRITE) { + remote = 1; + } else { + /* READ || READA */ + if (local) { + if (!drbd_may_do_local_read(mdev, sector, size)) { + /* we could kick the syncer to + * sync this extent asap, wait for + * it, then continue locally. + * Or just issue the request remotely. + */ + local = 0; + bio_put(req->private_bio); + req->private_bio = NULL; + put_ldev(mdev); + } + } + remote = !local && mdev->state.pdsk >= D_UP_TO_DATE; + } + + /* If we have a disk, but a READA request is mapped to remote, + * we are R_PRIMARY, D_INCONSISTENT, SyncTarget. + * Just fail that READA request right here. + * + * THINK: maybe fail all READA when not local? + * or make this configurable... + * if network is slow, READA won't do any good. + */ + if (rw == READA && mdev->state.disk >= D_INCONSISTENT && !local) { + err = -EWOULDBLOCK; + goto fail_and_free_req; + } + + /* For WRITES going to the local disk, grab a reference on the target + * extent. This waits for any resync activity in the corresponding + * resync extent to finish, and, if necessary, pulls in the target + * extent into the activity log, which involves further disk io because + * of transactional on-disk meta data updates. */ + if (rw == WRITE && local) + drbd_al_begin_io(mdev, sector); + + remote = remote && (mdev->state.pdsk == D_UP_TO_DATE || + (mdev->state.pdsk == D_INCONSISTENT && + mdev->state.conn >= C_CONNECTED)); + + if (!(local || remote)) { + dev_err(DEV, "IO ERROR: neither local nor remote disk\n"); + goto fail_free_complete; + } + + /* For WRITE request, we have to make sure that we have an + * unused_spare_tle, in case we need to start a new epoch. + * I try to be smart and avoid to pre-allocate always "just in case", + * but there is a race between testing the bit and pointer outside the + * spinlock, and grabbing the spinlock. + * if we lost that race, we retry. */ + if (rw == WRITE && remote && + mdev->unused_spare_tle == NULL && + test_bit(CREATE_BARRIER, &mdev->flags)) { +allocate_barrier: + b = kmalloc(sizeof(struct drbd_tl_epoch), GFP_NOIO); + if (!b) { + dev_err(DEV, "Failed to alloc barrier.\n"); + err = -ENOMEM; + goto fail_free_complete; + } + } + + /* GOOD, everything prepared, grab the spin_lock */ + spin_lock_irq(&mdev->req_lock); + + if (remote) { + remote = (mdev->state.pdsk == D_UP_TO_DATE || + (mdev->state.pdsk == D_INCONSISTENT && + mdev->state.conn >= C_CONNECTED)); + if (!remote) + dev_warn(DEV, "lost connection while grabbing the req_lock!\n"); + if (!(local || remote)) { + dev_err(DEV, "IO ERROR: neither local nor remote disk\n"); + spin_unlock_irq(&mdev->req_lock); + goto fail_free_complete; + } + } + + if (b && mdev->unused_spare_tle == NULL) { + mdev->unused_spare_tle = b; + b = NULL; + } + if (rw == WRITE && remote && + mdev->unused_spare_tle == NULL && + test_bit(CREATE_BARRIER, &mdev->flags)) { + /* someone closed the current epoch + * while we were grabbing the spinlock */ + spin_unlock_irq(&mdev->req_lock); + goto allocate_barrier; + } + + + /* Update disk stats */ + _drbd_start_io_acct(mdev, req, bio); + + /* _maybe_start_new_epoch(mdev); + * If we need to generate a write barrier packet, we have to add the + * new epoch (barrier) object, and queue the barrier packet for sending, + * and queue the req's data after it _within the same lock_, otherwise + * we have race conditions were the reorder domains could be mixed up. + * + * Even read requests may start a new epoch and queue the corresponding + * barrier packet. To get the write ordering right, we only have to + * make sure that, if this is a write request and it triggered a + * barrier packet, this request is queued within the same spinlock. */ + if (remote && mdev->unused_spare_tle && + test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) { + _tl_add_barrier(mdev, mdev->unused_spare_tle); + mdev->unused_spare_tle = NULL; + } else { + D_ASSERT(!(remote && rw == WRITE && + test_bit(CREATE_BARRIER, &mdev->flags))); + } + + /* NOTE + * Actually, 'local' may be wrong here already, since we may have failed + * to write to the meta data, and may become wrong anytime because of + * local io-error for some other request, which would lead to us + * "detaching" the local disk. + * + * 'remote' may become wrong any time because the network could fail. + * + * This is a harmless race condition, though, since it is handled + * correctly at the appropriate places; so it just defers the failure + * of the respective operation. + */ + + /* mark them early for readability. + * this just sets some state flags. */ + if (remote) + _req_mod(req, to_be_send); + if (local) + _req_mod(req, to_be_submitted); + + /* check this request on the collision detection hash tables. + * if we have a conflict, just complete it here. + * THINK do we want to check reads, too? (I don't think so...) */ + if (rw == WRITE && _req_conflicts(req)) { + /* this is a conflicting request. + * even though it may have been only _partially_ + * overlapping with one of the currently pending requests, + * without even submitting or sending it, we will + * pretend that it was successfully served right now. + */ + if (local) { + bio_put(req->private_bio); + req->private_bio = NULL; + drbd_al_complete_io(mdev, req->sector); + put_ldev(mdev); + local = 0; + } + if (remote) + dec_ap_pending(mdev); + _drbd_end_io_acct(mdev, req); + /* THINK: do we want to fail it (-EIO), or pretend success? */ + bio_endio(req->master_bio, 0); + req->master_bio = NULL; + dec_ap_bio(mdev); + drbd_req_free(req); + remote = 0; + } + + /* NOTE remote first: to get the concurrent write detection right, + * we must register the request before start of local IO. */ + if (remote) { + /* either WRITE and C_CONNECTED, + * or READ, and no local disk, + * or READ, but not in sync. + */ + _req_mod(req, (rw == WRITE) + ? queue_for_net_write + : queue_for_net_read); + } + spin_unlock_irq(&mdev->req_lock); + kfree(b); /* if someone else has beaten us to it... */ + + if (local) { + req->private_bio->bi_bdev = mdev->ldev->backing_bdev; + + if (FAULT_ACTIVE(mdev, rw == WRITE ? DRBD_FAULT_DT_WR + : rw == READ ? DRBD_FAULT_DT_RD + : DRBD_FAULT_DT_RA)) + bio_endio(req->private_bio, -EIO); + else + generic_make_request(req->private_bio); + } + + /* we need to plug ALWAYS since we possibly need to kick lo_dev. + * we plug after submit, so we won't miss an unplug event */ + drbd_plug_device(mdev); + + return 0; + +fail_free_complete: + if (rw == WRITE && local) + drbd_al_complete_io(mdev, sector); +fail_and_free_req: + if (local) { + bio_put(req->private_bio); + req->private_bio = NULL; + put_ldev(mdev); + } + bio_endio(bio, err); + drbd_req_free(req); + dec_ap_bio(mdev); + kfree(b); + + return 0; +} + +/* helper function for drbd_make_request + * if we can determine just by the mdev (state) that this request will fail, + * return 1 + * otherwise return 0 + */ +static int drbd_fail_request_early(struct drbd_conf *mdev, int is_write) +{ + /* Unconfigured */ + if (mdev->state.conn == C_DISCONNECTING && + mdev->state.disk == D_DISKLESS) + return 1; + + if (mdev->state.role != R_PRIMARY && + (!allow_oos || is_write)) { + if (__ratelimit(&drbd_ratelimit_state)) { + dev_err(DEV, "Process %s[%u] tried to %s; " + "since we are not in Primary state, " + "we cannot allow this\n", + current->comm, current->pid, + is_write ? "WRITE" : "READ"); + } + return 1; + } + + /* + * Paranoia: we might have been primary, but sync target, or + * even diskless, then lost the connection. + * This should have been handled (panic? suspend?) somewhere + * else. But maybe it was not, so check again here. + * Caution: as long as we do not have a read/write lock on mdev, + * to serialize state changes, this is racy, since we may lose + * the connection *after* we test for the cstate. + */ + if (mdev->state.disk < D_UP_TO_DATE && mdev->state.pdsk < D_UP_TO_DATE) { + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Sorry, I have no access to good data anymore.\n"); + return 1; + } + + return 0; +} + +int drbd_make_request_26(struct request_queue *q, struct bio *bio) +{ + unsigned int s_enr, e_enr; + struct drbd_conf *mdev = (struct drbd_conf *) q->queuedata; + + if (drbd_fail_request_early(mdev, bio_data_dir(bio) & WRITE)) { + bio_endio(bio, -EPERM); + return 0; + } + + /* Reject barrier requests if we know the underlying device does + * not support them. + * XXX: Need to get this info from peer as well some how so we + * XXX: reject if EITHER side/data/metadata area does not support them. + * + * because of those XXX, this is not yet enabled, + * i.e. in drbd_init_set_defaults we set the NO_BARRIER_SUPP bit. + */ + if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags))) { + /* dev_warn(DEV, "Rejecting barrier request as underlying device does not support\n"); */ + bio_endio(bio, -EOPNOTSUPP); + return 0; + } + + /* + * what we "blindly" assume: + */ + D_ASSERT(bio->bi_size > 0); + D_ASSERT((bio->bi_size & 0x1ff) == 0); + D_ASSERT(bio->bi_idx == 0); + + /* to make some things easier, force alignment of requests within the + * granularity of our hash tables */ + s_enr = bio->bi_sector >> HT_SHIFT; + e_enr = (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT; + + if (likely(s_enr == e_enr)) { + inc_ap_bio(mdev, 1); + return drbd_make_request_common(mdev, bio); + } + + /* can this bio be split generically? + * Maybe add our own split-arbitrary-bios function. */ + if (bio->bi_vcnt != 1 || bio->bi_idx != 0 || bio->bi_size > DRBD_MAX_SEGMENT_SIZE) { + /* rather error out here than BUG in bio_split */ + dev_err(DEV, "bio would need to, but cannot, be split: " + "(vcnt=%u,idx=%u,size=%u,sector=%llu)\n", + bio->bi_vcnt, bio->bi_idx, bio->bi_size, + (unsigned long long)bio->bi_sector); + bio_endio(bio, -EINVAL); + } else { + /* This bio crosses some boundary, so we have to split it. */ + struct bio_pair *bp; + /* works for the "do not cross hash slot boundaries" case + * e.g. sector 262269, size 4096 + * s_enr = 262269 >> 6 = 4097 + * e_enr = (262269+8-1) >> 6 = 4098 + * HT_SHIFT = 6 + * sps = 64, mask = 63 + * first_sectors = 64 - (262269 & 63) = 3 + */ + const sector_t sect = bio->bi_sector; + const int sps = 1 << HT_SHIFT; /* sectors per slot */ + const int mask = sps - 1; + const sector_t first_sectors = sps - (sect & mask); + bp = bio_split(bio, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + bio_split_pool, +#endif + first_sectors); + + /* we need to get a "reference count" (ap_bio_cnt) + * to avoid races with the disconnect/reconnect/suspend code. + * In case we need to split the bio here, we need to get two references + * atomically, otherwise we might deadlock when trying to submit the + * second one! */ + inc_ap_bio(mdev, 2); + + D_ASSERT(e_enr == s_enr + 1); + + drbd_make_request_common(mdev, &bp->bio1); + drbd_make_request_common(mdev, &bp->bio2); + bio_pair_release(bp); + } + return 0; +} + +/* This is called by bio_add_page(). With this function we reduce + * the number of BIOs that span over multiple DRBD_MAX_SEGMENT_SIZEs + * units (was AL_EXTENTs). + * + * we do the calculation within the lower 32bit of the byte offsets, + * since we don't care for actual offset, but only check whether it + * would cross "activity log extent" boundaries. + * + * As long as the BIO is empty we have to allow at least one bvec, + * regardless of size and offset. so the resulting bio may still + * cross extent boundaries. those are dealt with (bio_split) in + * drbd_make_request_26. + */ +int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct bio_vec *bvec) +{ + struct drbd_conf *mdev = (struct drbd_conf *) q->queuedata; + unsigned int bio_offset = + (unsigned int)bvm->bi_sector << 9; /* 32 bit */ + unsigned int bio_size = bvm->bi_size; + int limit, backing_limit; + + limit = DRBD_MAX_SEGMENT_SIZE + - ((bio_offset & (DRBD_MAX_SEGMENT_SIZE-1)) + bio_size); + if (limit < 0) + limit = 0; + if (bio_size == 0) { + if (limit <= bvec->bv_len) + limit = bvec->bv_len; + } else if (limit && get_ldev(mdev)) { + struct request_queue * const b = + mdev->ldev->backing_bdev->bd_disk->queue; + if (b->merge_bvec_fn && mdev->ldev->dc.use_bmbv) { + backing_limit = b->merge_bvec_fn(b, bvm, bvec); + limit = min(limit, backing_limit); + } + put_ldev(mdev); + } + return limit; +} diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h new file mode 100644 index 000000000000..f22c1bc8ec7e --- /dev/null +++ b/drivers/block/drbd/drbd_req.h @@ -0,0 +1,326 @@ +/* + drbd_req.h + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2006-2008, LINBIT Information Technologies GmbH. + Copyright (C) 2006-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + Copyright (C) 2006-2008, Philipp Reisner <philipp.reisner@linbit.com>. + + DRBD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + DRBD is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _DRBD_REQ_H +#define _DRBD_REQ_H + +#include <linux/module.h> + +#include <linux/slab.h> +#include <linux/drbd.h> +#include "drbd_int.h" +#include "drbd_wrappers.h" + +/* The request callbacks will be called in irq context by the IDE drivers, + and in Softirqs/Tasklets/BH context by the SCSI drivers, + and by the receiver and worker in kernel-thread context. + Try to get the locking right :) */ + +/* + * Objects of type struct drbd_request do only exist on a R_PRIMARY node, and are + * associated with IO requests originating from the block layer above us. + * + * There are quite a few things that may happen to a drbd request + * during its lifetime. + * + * It will be created. + * It will be marked with the intention to be + * submitted to local disk and/or + * send via the network. + * + * It has to be placed on the transfer log and other housekeeping lists, + * In case we have a network connection. + * + * It may be identified as a concurrent (write) request + * and be handled accordingly. + * + * It may me handed over to the local disk subsystem. + * It may be completed by the local disk subsystem, + * either sucessfully or with io-error. + * In case it is a READ request, and it failed locally, + * it may be retried remotely. + * + * It may be queued for sending. + * It may be handed over to the network stack, + * which may fail. + * It may be acknowledged by the "peer" according to the wire_protocol in use. + * this may be a negative ack. + * It may receive a faked ack when the network connection is lost and the + * transfer log is cleaned up. + * Sending may be canceled due to network connection loss. + * When it finally has outlived its time, + * corresponding dirty bits in the resync-bitmap may be cleared or set, + * it will be destroyed, + * and completion will be signalled to the originator, + * with or without "success". + */ + +enum drbd_req_event { + created, + to_be_send, + to_be_submitted, + + /* XXX yes, now I am inconsistent... + * these two are not "events" but "actions" + * oh, well... */ + queue_for_net_write, + queue_for_net_read, + + send_canceled, + send_failed, + handed_over_to_network, + connection_lost_while_pending, + recv_acked_by_peer, + write_acked_by_peer, + write_acked_by_peer_and_sis, /* and set_in_sync */ + conflict_discarded_by_peer, + neg_acked, + barrier_acked, /* in protocol A and B */ + data_received, /* (remote read) */ + + read_completed_with_error, + read_ahead_completed_with_error, + write_completed_with_error, + completed_ok, + nothing, /* for tracing only */ +}; + +/* encoding of request states for now. we don't actually need that many bits. + * we don't need to do atomic bit operations either, since most of the time we + * need to look at the connection state and/or manipulate some lists at the + * same time, so we should hold the request lock anyways. + */ +enum drbd_req_state_bits { + /* 210 + * 000: no local possible + * 001: to be submitted + * UNUSED, we could map: 011: submitted, completion still pending + * 110: completed ok + * 010: completed with error + */ + __RQ_LOCAL_PENDING, + __RQ_LOCAL_COMPLETED, + __RQ_LOCAL_OK, + + /* 76543 + * 00000: no network possible + * 00001: to be send + * 00011: to be send, on worker queue + * 00101: sent, expecting recv_ack (B) or write_ack (C) + * 11101: sent, + * recv_ack (B) or implicit "ack" (A), + * still waiting for the barrier ack. + * master_bio may already be completed and invalidated. + * 11100: write_acked (C), + * data_received (for remote read, any protocol) + * or finally the barrier ack has arrived (B,A)... + * request can be freed + * 01100: neg-acked (write, protocol C) + * or neg-d-acked (read, any protocol) + * or killed from the transfer log + * during cleanup after connection loss + * request can be freed + * 01000: canceled or send failed... + * request can be freed + */ + + /* if "SENT" is not set, yet, this can still fail or be canceled. + * if "SENT" is set already, we still wait for an Ack packet. + * when cleared, the master_bio may be completed. + * in (B,A) the request object may still linger on the transaction log + * until the corresponding barrier ack comes in */ + __RQ_NET_PENDING, + + /* If it is QUEUED, and it is a WRITE, it is also registered in the + * transfer log. Currently we need this flag to avoid conflicts between + * worker canceling the request and tl_clear_barrier killing it from + * transfer log. We should restructure the code so this conflict does + * no longer occur. */ + __RQ_NET_QUEUED, + + /* well, actually only "handed over to the network stack". + * + * TODO can potentially be dropped because of the similar meaning + * of RQ_NET_SENT and ~RQ_NET_QUEUED. + * however it is not exactly the same. before we drop it + * we must ensure that we can tell a request with network part + * from a request without, regardless of what happens to it. */ + __RQ_NET_SENT, + + /* when set, the request may be freed (if RQ_NET_QUEUED is clear). + * basically this means the corresponding P_BARRIER_ACK was received */ + __RQ_NET_DONE, + + /* whether or not we know (C) or pretend (B,A) that the write + * was successfully written on the peer. + */ + __RQ_NET_OK, + + /* peer called drbd_set_in_sync() for this write */ + __RQ_NET_SIS, + + /* keep this last, its for the RQ_NET_MASK */ + __RQ_NET_MAX, +}; + +#define RQ_LOCAL_PENDING (1UL << __RQ_LOCAL_PENDING) +#define RQ_LOCAL_COMPLETED (1UL << __RQ_LOCAL_COMPLETED) +#define RQ_LOCAL_OK (1UL << __RQ_LOCAL_OK) + +#define RQ_LOCAL_MASK ((RQ_LOCAL_OK << 1)-1) /* 0x07 */ + +#define RQ_NET_PENDING (1UL << __RQ_NET_PENDING) +#define RQ_NET_QUEUED (1UL << __RQ_NET_QUEUED) +#define RQ_NET_SENT (1UL << __RQ_NET_SENT) +#define RQ_NET_DONE (1UL << __RQ_NET_DONE) +#define RQ_NET_OK (1UL << __RQ_NET_OK) +#define RQ_NET_SIS (1UL << __RQ_NET_SIS) + +/* 0x1f8 */ +#define RQ_NET_MASK (((1UL << __RQ_NET_MAX)-1) & ~RQ_LOCAL_MASK) + +/* epoch entries */ +static inline +struct hlist_head *ee_hash_slot(struct drbd_conf *mdev, sector_t sector) +{ + BUG_ON(mdev->ee_hash_s == 0); + return mdev->ee_hash + + ((unsigned int)(sector>>HT_SHIFT) % mdev->ee_hash_s); +} + +/* transfer log (drbd_request objects) */ +static inline +struct hlist_head *tl_hash_slot(struct drbd_conf *mdev, sector_t sector) +{ + BUG_ON(mdev->tl_hash_s == 0); + return mdev->tl_hash + + ((unsigned int)(sector>>HT_SHIFT) % mdev->tl_hash_s); +} + +/* application reads (drbd_request objects) */ +static struct hlist_head *ar_hash_slot(struct drbd_conf *mdev, sector_t sector) +{ + return mdev->app_reads_hash + + ((unsigned int)(sector) % APP_R_HSIZE); +} + +/* when we receive the answer for a read request, + * verify that we actually know about it */ +static inline struct drbd_request *_ar_id_to_req(struct drbd_conf *mdev, + u64 id, sector_t sector) +{ + struct hlist_head *slot = ar_hash_slot(mdev, sector); + struct hlist_node *n; + struct drbd_request *req; + + hlist_for_each_entry(req, n, slot, colision) { + if ((unsigned long)req == (unsigned long)id) { + D_ASSERT(req->sector == sector); + return req; + } + } + return NULL; +} + +static inline struct drbd_request *drbd_req_new(struct drbd_conf *mdev, + struct bio *bio_src) +{ + struct bio *bio; + struct drbd_request *req = + mempool_alloc(drbd_request_mempool, GFP_NOIO); + if (likely(req)) { + bio = bio_clone(bio_src, GFP_NOIO); /* XXX cannot fail?? */ + + req->rq_state = 0; + req->mdev = mdev; + req->master_bio = bio_src; + req->private_bio = bio; + req->epoch = 0; + req->sector = bio->bi_sector; + req->size = bio->bi_size; + req->start_time = jiffies; + INIT_HLIST_NODE(&req->colision); + INIT_LIST_HEAD(&req->tl_requests); + INIT_LIST_HEAD(&req->w.list); + + bio->bi_private = req; + bio->bi_end_io = drbd_endio_pri; + bio->bi_next = NULL; + } + return req; +} + +static inline void drbd_req_free(struct drbd_request *req) +{ + mempool_free(req, drbd_request_mempool); +} + +static inline int overlaps(sector_t s1, int l1, sector_t s2, int l2) +{ + return !((s1 + (l1>>9) <= s2) || (s1 >= s2 + (l2>>9))); +} + +/* Short lived temporary struct on the stack. + * We could squirrel the error to be returned into + * bio->bi_size, or similar. But that would be too ugly. */ +struct bio_and_error { + struct bio *bio; + int error; +}; + +extern void _req_may_be_done(struct drbd_request *req, + struct bio_and_error *m); +extern void __req_mod(struct drbd_request *req, enum drbd_req_event what, + struct bio_and_error *m); +extern void complete_master_bio(struct drbd_conf *mdev, + struct bio_and_error *m); + +/* use this if you don't want to deal with calling complete_master_bio() + * outside the spinlock, e.g. when walking some list on cleanup. */ +static inline void _req_mod(struct drbd_request *req, enum drbd_req_event what) +{ + struct drbd_conf *mdev = req->mdev; + struct bio_and_error m; + + /* __req_mod possibly frees req, do not touch req after that! */ + __req_mod(req, what, &m); + if (m.bio) + complete_master_bio(mdev, &m); +} + +/* completion of master bio is outside of spinlock. + * If you need it irqsave, do it your self! */ +static inline void req_mod(struct drbd_request *req, + enum drbd_req_event what) +{ + struct drbd_conf *mdev = req->mdev; + struct bio_and_error m; + spin_lock_irq(&mdev->req_lock); + __req_mod(req, what, &m); + spin_unlock_irq(&mdev->req_lock); + + if (m.bio) + complete_master_bio(mdev, &m); +} +#endif diff --git a/drivers/block/drbd/drbd_strings.c b/drivers/block/drbd/drbd_strings.c new file mode 100644 index 000000000000..76863e3f05be --- /dev/null +++ b/drivers/block/drbd/drbd_strings.c @@ -0,0 +1,113 @@ +/* + drbd.h + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2003-2008, LINBIT Information Technologies GmbH. + Copyright (C) 2003-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2003-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <linux/drbd.h> + +static const char *drbd_conn_s_names[] = { + [C_STANDALONE] = "StandAlone", + [C_DISCONNECTING] = "Disconnecting", + [C_UNCONNECTED] = "Unconnected", + [C_TIMEOUT] = "Timeout", + [C_BROKEN_PIPE] = "BrokenPipe", + [C_NETWORK_FAILURE] = "NetworkFailure", + [C_PROTOCOL_ERROR] = "ProtocolError", + [C_WF_CONNECTION] = "WFConnection", + [C_WF_REPORT_PARAMS] = "WFReportParams", + [C_TEAR_DOWN] = "TearDown", + [C_CONNECTED] = "Connected", + [C_STARTING_SYNC_S] = "StartingSyncS", + [C_STARTING_SYNC_T] = "StartingSyncT", + [C_WF_BITMAP_S] = "WFBitMapS", + [C_WF_BITMAP_T] = "WFBitMapT", + [C_WF_SYNC_UUID] = "WFSyncUUID", + [C_SYNC_SOURCE] = "SyncSource", + [C_SYNC_TARGET] = "SyncTarget", + [C_PAUSED_SYNC_S] = "PausedSyncS", + [C_PAUSED_SYNC_T] = "PausedSyncT", + [C_VERIFY_S] = "VerifyS", + [C_VERIFY_T] = "VerifyT", +}; + +static const char *drbd_role_s_names[] = { + [R_PRIMARY] = "Primary", + [R_SECONDARY] = "Secondary", + [R_UNKNOWN] = "Unknown" +}; + +static const char *drbd_disk_s_names[] = { + [D_DISKLESS] = "Diskless", + [D_ATTACHING] = "Attaching", + [D_FAILED] = "Failed", + [D_NEGOTIATING] = "Negotiating", + [D_INCONSISTENT] = "Inconsistent", + [D_OUTDATED] = "Outdated", + [D_UNKNOWN] = "DUnknown", + [D_CONSISTENT] = "Consistent", + [D_UP_TO_DATE] = "UpToDate", +}; + +static const char *drbd_state_sw_errors[] = { + [-SS_TWO_PRIMARIES] = "Multiple primaries not allowed by config", + [-SS_NO_UP_TO_DATE_DISK] = "Refusing to be Primary without at least one UpToDate disk", + [-SS_NO_LOCAL_DISK] = "Can not resync without local disk", + [-SS_NO_REMOTE_DISK] = "Can not resync without remote disk", + [-SS_CONNECTED_OUTDATES] = "Refusing to be Outdated while Connected", + [-SS_PRIMARY_NOP] = "Refusing to be Primary while peer is not outdated", + [-SS_RESYNC_RUNNING] = "Can not start OV/resync since it is already active", + [-SS_ALREADY_STANDALONE] = "Can not disconnect a StandAlone device", + [-SS_CW_FAILED_BY_PEER] = "State change was refused by peer node", + [-SS_IS_DISKLESS] = "Device is diskless, the requested operation requires a disk", + [-SS_DEVICE_IN_USE] = "Device is held open by someone", + [-SS_NO_NET_CONFIG] = "Have no net/connection configuration", + [-SS_NO_VERIFY_ALG] = "Need a verify algorithm to start online verify", + [-SS_NEED_CONNECTION] = "Need a connection to start verify or resync", + [-SS_NOT_SUPPORTED] = "Peer does not support protocol", + [-SS_LOWER_THAN_OUTDATED] = "Disk state is lower than outdated", + [-SS_IN_TRANSIENT_STATE] = "In transient state, retry after next state change", + [-SS_CONCURRENT_ST_CHG] = "Concurrent state changes detected and aborted", +}; + +const char *drbd_conn_str(enum drbd_conns s) +{ + /* enums are unsigned... */ + return s > C_PAUSED_SYNC_T ? "TOO_LARGE" : drbd_conn_s_names[s]; +} + +const char *drbd_role_str(enum drbd_role s) +{ + return s > R_SECONDARY ? "TOO_LARGE" : drbd_role_s_names[s]; +} + +const char *drbd_disk_str(enum drbd_disk_state s) +{ + return s > D_UP_TO_DATE ? "TOO_LARGE" : drbd_disk_s_names[s]; +} + +const char *drbd_set_st_err_str(enum drbd_state_ret_codes err) +{ + return err <= SS_AFTER_LAST_ERROR ? "TOO_SMALL" : + err > SS_TWO_PRIMARIES ? "TOO_LARGE" + : drbd_state_sw_errors[-err]; +} diff --git a/drivers/block/drbd/drbd_vli.h b/drivers/block/drbd/drbd_vli.h new file mode 100644 index 000000000000..fc824006e721 --- /dev/null +++ b/drivers/block/drbd/drbd_vli.h @@ -0,0 +1,351 @@ +/* +-*- linux-c -*- + drbd_receiver.c + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. + Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _DRBD_VLI_H +#define _DRBD_VLI_H + +/* + * At a granularity of 4KiB storage represented per bit, + * and stroage sizes of several TiB, + * and possibly small-bandwidth replication, + * the bitmap transfer time can take much too long, + * if transmitted in plain text. + * + * We try to reduce the transfered bitmap information + * by encoding runlengths of bit polarity. + * + * We never actually need to encode a "zero" (runlengths are positive). + * But then we have to store the value of the first bit. + * The first bit of information thus shall encode if the first runlength + * gives the number of set or unset bits. + * + * We assume that large areas are either completely set or unset, + * which gives good compression with any runlength method, + * even when encoding the runlength as fixed size 32bit/64bit integers. + * + * Still, there may be areas where the polarity flips every few bits, + * and encoding the runlength sequence of those areas with fix size + * integers would be much worse than plaintext. + * + * We want to encode small runlength values with minimum code length, + * while still being able to encode a Huge run of all zeros. + * + * Thus we need a Variable Length Integer encoding, VLI. + * + * For some cases, we produce more code bits than plaintext input. + * We need to send incompressible chunks as plaintext, skip over them + * and then see if the next chunk compresses better. + * + * We don't care too much about "excellent" compression ratio for large + * runlengths (all set/all clear): whether we achieve a factor of 100 + * or 1000 is not that much of an issue. + * We do not want to waste too much on short runlengths in the "noisy" + * parts of the bitmap, though. + * + * There are endless variants of VLI, we experimented with: + * * simple byte-based + * * various bit based with different code word length. + * + * To avoid yet an other configuration parameter (choice of bitmap compression + * algorithm) which was difficult to explain and tune, we just chose the one + * variant that turned out best in all test cases. + * Based on real world usage patterns, with device sizes ranging from a few GiB + * to several TiB, file server/mailserver/webserver/mysql/postgress, + * mostly idle to really busy, the all time winner (though sometimes only + * marginally better) is: + */ + +/* + * encoding is "visualised" as + * __little endian__ bitstream, least significant bit first (left most) + * + * this particular encoding is chosen so that the prefix code + * starts as unary encoding the level, then modified so that + * 10 levels can be described in 8bit, with minimal overhead + * for the smaller levels. + * + * Number of data bits follow fibonacci sequence, with the exception of the + * last level (+1 data bit, so it makes 64bit total). The only worse code when + * encoding bit polarity runlength is 1 plain bits => 2 code bits. +prefix data bits max val Nº data bits +0 x 0x2 1 +10 x 0x4 1 +110 xx 0x8 2 +1110 xxx 0x10 3 +11110 xxx xx 0x30 5 +111110 xx xxxxxx 0x130 8 +11111100 xxxxxxxx xxxxx 0x2130 13 +11111110 xxxxxxxx xxxxxxxx xxxxx 0x202130 21 +11111101 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xx 0x400202130 34 +11111111 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 56 + * maximum encodable value: 0x100000400202130 == 2**56 + some */ + +/* compression "table": + transmitted x 0.29 + as plaintext x ........................ + x ........................ + x ........................ + x 0.59 0.21........................ + x ........................................................ + x .. c ................................................... + x 0.44.. o ................................................... + x .......... d ................................................... + x .......... e ................................................... + X............. ................................................... + x.............. b ................................................... +2.0x............... i ................................................... + #X................ t ................................................... + #................. s ........................... plain bits .......... +-+----------------------------------------------------------------------- + 1 16 32 64 +*/ + +/* LEVEL: (total bits, prefix bits, prefix value), + * sorted ascending by number of total bits. + * The rest of the code table is calculated at compiletime from this. */ + +/* fibonacci data 1, 1, ... */ +#define VLI_L_1_1() do { \ + LEVEL( 2, 1, 0x00); \ + LEVEL( 3, 2, 0x01); \ + LEVEL( 5, 3, 0x03); \ + LEVEL( 7, 4, 0x07); \ + LEVEL(10, 5, 0x0f); \ + LEVEL(14, 6, 0x1f); \ + LEVEL(21, 8, 0x3f); \ + LEVEL(29, 8, 0x7f); \ + LEVEL(42, 8, 0xbf); \ + LEVEL(64, 8, 0xff); \ + } while (0) + +/* finds a suitable level to decode the least significant part of in. + * returns number of bits consumed. + * + * BUG() for bad input, as that would mean a buggy code table. */ +static inline int vli_decode_bits(u64 *out, const u64 in) +{ + u64 adj = 1; + +#define LEVEL(t,b,v) \ + do { \ + if ((in & ((1 << b) -1)) == v) { \ + *out = ((in & ((~0ULL) >> (64-t))) >> b) + adj; \ + return t; \ + } \ + adj += 1ULL << (t - b); \ + } while (0) + + VLI_L_1_1(); + + /* NOT REACHED, if VLI_LEVELS code table is defined properly */ + BUG(); +#undef LEVEL +} + +/* return number of code bits needed, + * or negative error number */ +static inline int __vli_encode_bits(u64 *out, const u64 in) +{ + u64 max = 0; + u64 adj = 1; + + if (in == 0) + return -EINVAL; + +#define LEVEL(t,b,v) do { \ + max += 1ULL << (t - b); \ + if (in <= max) { \ + if (out) \ + *out = ((in - adj) << b) | v; \ + return t; \ + } \ + adj = max + 1; \ + } while (0) + + VLI_L_1_1(); + + return -EOVERFLOW; +#undef LEVEL +} + +#undef VLI_L_1_1 + +/* code from here down is independend of actually used bit code */ + +/* + * Code length is determined by some unique (e.g. unary) prefix. + * This encodes arbitrary bit length, not whole bytes: we have a bit-stream, + * not a byte stream. + */ + +/* for the bitstream, we need a cursor */ +struct bitstream_cursor { + /* the current byte */ + u8 *b; + /* the current bit within *b, nomalized: 0..7 */ + unsigned int bit; +}; + +/* initialize cursor to point to first bit of stream */ +static inline void bitstream_cursor_reset(struct bitstream_cursor *cur, void *s) +{ + cur->b = s; + cur->bit = 0; +} + +/* advance cursor by that many bits; maximum expected input value: 64, + * but depending on VLI implementation, it may be more. */ +static inline void bitstream_cursor_advance(struct bitstream_cursor *cur, unsigned int bits) +{ + bits += cur->bit; + cur->b = cur->b + (bits >> 3); + cur->bit = bits & 7; +} + +/* the bitstream itself knows its length */ +struct bitstream { + struct bitstream_cursor cur; + unsigned char *buf; + size_t buf_len; /* in bytes */ + + /* for input stream: + * number of trailing 0 bits for padding + * total number of valid bits in stream: buf_len * 8 - pad_bits */ + unsigned int pad_bits; +}; + +static inline void bitstream_init(struct bitstream *bs, void *s, size_t len, unsigned int pad_bits) +{ + bs->buf = s; + bs->buf_len = len; + bs->pad_bits = pad_bits; + bitstream_cursor_reset(&bs->cur, bs->buf); +} + +static inline void bitstream_rewind(struct bitstream *bs) +{ + bitstream_cursor_reset(&bs->cur, bs->buf); + memset(bs->buf, 0, bs->buf_len); +} + +/* Put (at most 64) least significant bits of val into bitstream, and advance cursor. + * Ignores "pad_bits". + * Returns zero if bits == 0 (nothing to do). + * Returns number of bits used if successful. + * + * If there is not enough room left in bitstream, + * leaves bitstream unchanged and returns -ENOBUFS. + */ +static inline int bitstream_put_bits(struct bitstream *bs, u64 val, const unsigned int bits) +{ + unsigned char *b = bs->cur.b; + unsigned int tmp; + + if (bits == 0) + return 0; + + if ((bs->cur.b + ((bs->cur.bit + bits -1) >> 3)) - bs->buf >= bs->buf_len) + return -ENOBUFS; + + /* paranoia: strip off hi bits; they should not be set anyways. */ + if (bits < 64) + val &= ~0ULL >> (64 - bits); + + *b++ |= (val & 0xff) << bs->cur.bit; + + for (tmp = 8 - bs->cur.bit; tmp < bits; tmp += 8) + *b++ |= (val >> tmp) & 0xff; + + bitstream_cursor_advance(&bs->cur, bits); + return bits; +} + +/* Fetch (at most 64) bits from bitstream into *out, and advance cursor. + * + * If more than 64 bits are requested, returns -EINVAL and leave *out unchanged. + * + * If there are less than the requested number of valid bits left in the + * bitstream, still fetches all available bits. + * + * Returns number of actually fetched bits. + */ +static inline int bitstream_get_bits(struct bitstream *bs, u64 *out, int bits) +{ + u64 val; + unsigned int n; + + if (bits > 64) + return -EINVAL; + + if (bs->cur.b + ((bs->cur.bit + bs->pad_bits + bits -1) >> 3) - bs->buf >= bs->buf_len) + bits = ((bs->buf_len - (bs->cur.b - bs->buf)) << 3) + - bs->cur.bit - bs->pad_bits; + + if (bits == 0) { + *out = 0; + return 0; + } + + /* get the high bits */ + val = 0; + n = (bs->cur.bit + bits + 7) >> 3; + /* n may be at most 9, if cur.bit + bits > 64 */ + /* which means this copies at most 8 byte */ + if (n) { + memcpy(&val, bs->cur.b+1, n - 1); + val = le64_to_cpu(val) << (8 - bs->cur.bit); + } + + /* we still need the low bits */ + val |= bs->cur.b[0] >> bs->cur.bit; + + /* and mask out bits we don't want */ + val &= ~0ULL >> (64 - bits); + + bitstream_cursor_advance(&bs->cur, bits); + *out = val; + + return bits; +} + +/* encodes @in as vli into @bs; + + * return values + * > 0: number of bits successfully stored in bitstream + * -ENOBUFS @bs is full + * -EINVAL input zero (invalid) + * -EOVERFLOW input too large for this vli code (invalid) + */ +static inline int vli_encode_bits(struct bitstream *bs, u64 in) +{ + u64 code = code; + int bits = __vli_encode_bits(&code, in); + + if (bits <= 0) + return bits; + + return bitstream_put_bits(bs, code, bits); +} + +#endif diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c new file mode 100644 index 000000000000..ed8796f1112d --- /dev/null +++ b/drivers/block/drbd/drbd_worker.c @@ -0,0 +1,1512 @@ +/* + drbd_worker.c + + This file is part of DRBD by Philipp Reisner and Lars Ellenberg. + + Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. + Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. + Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. + + drbd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + drbd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with drbd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/drbd.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include <linux/memcontrol.h> +#include <linux/mm_inline.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/scatterlist.h> + +#include "drbd_int.h" +#include "drbd_req.h" + +#define SLEEP_TIME (HZ/10) + +static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel); + + + +/* defined here: + drbd_md_io_complete + drbd_endio_write_sec + drbd_endio_read_sec + drbd_endio_pri + + * more endio handlers: + atodb_endio in drbd_actlog.c + drbd_bm_async_io_complete in drbd_bitmap.c + + * For all these callbacks, note the following: + * The callbacks will be called in irq context by the IDE drivers, + * and in Softirqs/Tasklets/BH context by the SCSI drivers. + * Try to get the locking right :) + * + */ + + +/* About the global_state_lock + Each state transition on an device holds a read lock. In case we have + to evaluate the sync after dependencies, we grab a write lock, because + we need stable states on all devices for that. */ +rwlock_t global_state_lock; + +/* used for synchronous meta data and bitmap IO + * submitted by drbd_md_sync_page_io() + */ +void drbd_md_io_complete(struct bio *bio, int error) +{ + struct drbd_md_io *md_io; + + md_io = (struct drbd_md_io *)bio->bi_private; + md_io->error = error; + + complete(&md_io->event); +} + +/* reads on behalf of the partner, + * "submitted" by the receiver + */ +void drbd_endio_read_sec(struct bio *bio, int error) __releases(local) +{ + unsigned long flags = 0; + struct drbd_epoch_entry *e = NULL; + struct drbd_conf *mdev; + int uptodate = bio_flagged(bio, BIO_UPTODATE); + + e = bio->bi_private; + mdev = e->mdev; + + if (error) + dev_warn(DEV, "read: error=%d s=%llus\n", error, + (unsigned long long)e->sector); + if (!error && !uptodate) { + dev_warn(DEV, "read: setting error to -EIO s=%llus\n", + (unsigned long long)e->sector); + /* strange behavior of some lower level drivers... + * fail the request by clearing the uptodate flag, + * but do not return any error?! */ + error = -EIO; + } + + D_ASSERT(e->block_id != ID_VACANT); + + spin_lock_irqsave(&mdev->req_lock, flags); + mdev->read_cnt += e->size >> 9; + list_del(&e->w.list); + if (list_empty(&mdev->read_ee)) + wake_up(&mdev->ee_wait); + spin_unlock_irqrestore(&mdev->req_lock, flags); + + drbd_chk_io_error(mdev, error, FALSE); + drbd_queue_work(&mdev->data.work, &e->w); + put_ldev(mdev); +} + +/* writes on behalf of the partner, or resync writes, + * "submitted" by the receiver. + */ +void drbd_endio_write_sec(struct bio *bio, int error) __releases(local) +{ + unsigned long flags = 0; + struct drbd_epoch_entry *e = NULL; + struct drbd_conf *mdev; + sector_t e_sector; + int do_wake; + int is_syncer_req; + int do_al_complete_io; + int uptodate = bio_flagged(bio, BIO_UPTODATE); + int is_barrier = bio_rw_flagged(bio, BIO_RW_BARRIER); + + e = bio->bi_private; + mdev = e->mdev; + + if (error) + dev_warn(DEV, "write: error=%d s=%llus\n", error, + (unsigned long long)e->sector); + if (!error && !uptodate) { + dev_warn(DEV, "write: setting error to -EIO s=%llus\n", + (unsigned long long)e->sector); + /* strange behavior of some lower level drivers... + * fail the request by clearing the uptodate flag, + * but do not return any error?! */ + error = -EIO; + } + + /* error == -ENOTSUPP would be a better test, + * alas it is not reliable */ + if (error && is_barrier && e->flags & EE_IS_BARRIER) { + drbd_bump_write_ordering(mdev, WO_bdev_flush); + spin_lock_irqsave(&mdev->req_lock, flags); + list_del(&e->w.list); + e->w.cb = w_e_reissue; + /* put_ldev actually happens below, once we come here again. */ + __release(local); + spin_unlock_irqrestore(&mdev->req_lock, flags); + drbd_queue_work(&mdev->data.work, &e->w); + return; + } + + D_ASSERT(e->block_id != ID_VACANT); + + spin_lock_irqsave(&mdev->req_lock, flags); + mdev->writ_cnt += e->size >> 9; + is_syncer_req = is_syncer_block_id(e->block_id); + + /* after we moved e to done_ee, + * we may no longer access it, + * it may be freed/reused already! + * (as soon as we release the req_lock) */ + e_sector = e->sector; + do_al_complete_io = e->flags & EE_CALL_AL_COMPLETE_IO; + + list_del(&e->w.list); /* has been on active_ee or sync_ee */ + list_add_tail(&e->w.list, &mdev->done_ee); + + /* No hlist_del_init(&e->colision) here, we did not send the Ack yet, + * neither did we wake possibly waiting conflicting requests. + * done from "drbd_process_done_ee" within the appropriate w.cb + * (e_end_block/e_end_resync_block) or from _drbd_clear_done_ee */ + + do_wake = is_syncer_req + ? list_empty(&mdev->sync_ee) + : list_empty(&mdev->active_ee); + + if (error) + __drbd_chk_io_error(mdev, FALSE); + spin_unlock_irqrestore(&mdev->req_lock, flags); + + if (is_syncer_req) + drbd_rs_complete_io(mdev, e_sector); + + if (do_wake) + wake_up(&mdev->ee_wait); + + if (do_al_complete_io) + drbd_al_complete_io(mdev, e_sector); + + wake_asender(mdev); + put_ldev(mdev); + +} + +/* read, readA or write requests on R_PRIMARY coming from drbd_make_request + */ +void drbd_endio_pri(struct bio *bio, int error) +{ + unsigned long flags; + struct drbd_request *req = bio->bi_private; + struct drbd_conf *mdev = req->mdev; + struct bio_and_error m; + enum drbd_req_event what; + int uptodate = bio_flagged(bio, BIO_UPTODATE); + + if (error) + dev_warn(DEV, "p %s: error=%d\n", + bio_data_dir(bio) == WRITE ? "write" : "read", error); + if (!error && !uptodate) { + dev_warn(DEV, "p %s: setting error to -EIO\n", + bio_data_dir(bio) == WRITE ? "write" : "read"); + /* strange behavior of some lower level drivers... + * fail the request by clearing the uptodate flag, + * but do not return any error?! */ + error = -EIO; + } + + /* to avoid recursion in __req_mod */ + if (unlikely(error)) { + what = (bio_data_dir(bio) == WRITE) + ? write_completed_with_error + : (bio_rw(bio) == READA) + ? read_completed_with_error + : read_ahead_completed_with_error; + } else + what = completed_ok; + + bio_put(req->private_bio); + req->private_bio = ERR_PTR(error); + + spin_lock_irqsave(&mdev->req_lock, flags); + __req_mod(req, what, &m); + spin_unlock_irqrestore(&mdev->req_lock, flags); + + if (m.bio) + complete_master_bio(mdev, &m); +} + +int w_io_error(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_request *req = container_of(w, struct drbd_request, w); + + /* NOTE: mdev->ldev can be NULL by the time we get here! */ + /* D_ASSERT(mdev->ldev->dc.on_io_error != EP_PASS_ON); */ + + /* the only way this callback is scheduled is from _req_may_be_done, + * when it is done and had a local write error, see comments there */ + drbd_req_free(req); + + return TRUE; +} + +int w_read_retry_remote(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_request *req = container_of(w, struct drbd_request, w); + + /* We should not detach for read io-error, + * but try to WRITE the P_DATA_REPLY to the failed location, + * to give the disk the chance to relocate that block */ + + spin_lock_irq(&mdev->req_lock); + if (cancel || + mdev->state.conn < C_CONNECTED || + mdev->state.pdsk <= D_INCONSISTENT) { + _req_mod(req, send_canceled); + spin_unlock_irq(&mdev->req_lock); + dev_alert(DEV, "WE ARE LOST. Local IO failure, no peer.\n"); + return 1; + } + spin_unlock_irq(&mdev->req_lock); + + return w_send_read_req(mdev, w, 0); +} + +int w_resync_inactive(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + ERR_IF(cancel) return 1; + dev_err(DEV, "resync inactive, but callback triggered??\n"); + return 1; /* Simply ignore this! */ +} + +void drbd_csum(struct drbd_conf *mdev, struct crypto_hash *tfm, struct bio *bio, void *digest) +{ + struct hash_desc desc; + struct scatterlist sg; + struct bio_vec *bvec; + int i; + + desc.tfm = tfm; + desc.flags = 0; + + sg_init_table(&sg, 1); + crypto_hash_init(&desc); + + __bio_for_each_segment(bvec, bio, i, 0) { + sg_set_page(&sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset); + crypto_hash_update(&desc, &sg, sg.length); + } + crypto_hash_final(&desc, digest); +} + +static int w_e_send_csum(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w); + int digest_size; + void *digest; + int ok; + + D_ASSERT(e->block_id == DRBD_MAGIC + 0xbeef); + + if (unlikely(cancel)) { + drbd_free_ee(mdev, e); + return 1; + } + + if (likely(drbd_bio_uptodate(e->private_bio))) { + digest_size = crypto_hash_digestsize(mdev->csums_tfm); + digest = kmalloc(digest_size, GFP_NOIO); + if (digest) { + drbd_csum(mdev, mdev->csums_tfm, e->private_bio, digest); + + inc_rs_pending(mdev); + ok = drbd_send_drequest_csum(mdev, + e->sector, + e->size, + digest, + digest_size, + P_CSUM_RS_REQUEST); + kfree(digest); + } else { + dev_err(DEV, "kmalloc() of digest failed.\n"); + ok = 0; + } + } else + ok = 1; + + drbd_free_ee(mdev, e); + + if (unlikely(!ok)) + dev_err(DEV, "drbd_send_drequest(..., csum) failed\n"); + return ok; +} + +#define GFP_TRY (__GFP_HIGHMEM | __GFP_NOWARN) + +static int read_for_csum(struct drbd_conf *mdev, sector_t sector, int size) +{ + struct drbd_epoch_entry *e; + + if (!get_ldev(mdev)) + return 0; + + /* GFP_TRY, because if there is no memory available right now, this may + * be rescheduled for later. It is "only" background resync, after all. */ + e = drbd_alloc_ee(mdev, DRBD_MAGIC+0xbeef, sector, size, GFP_TRY); + if (!e) { + put_ldev(mdev); + return 2; + } + + spin_lock_irq(&mdev->req_lock); + list_add(&e->w.list, &mdev->read_ee); + spin_unlock_irq(&mdev->req_lock); + + e->private_bio->bi_end_io = drbd_endio_read_sec; + e->private_bio->bi_rw = READ; + e->w.cb = w_e_send_csum; + + mdev->read_cnt += size >> 9; + drbd_generic_make_request(mdev, DRBD_FAULT_RS_RD, e->private_bio); + + return 1; +} + +void resync_timer_fn(unsigned long data) +{ + unsigned long flags; + struct drbd_conf *mdev = (struct drbd_conf *) data; + int queue; + + spin_lock_irqsave(&mdev->req_lock, flags); + + if (likely(!test_and_clear_bit(STOP_SYNC_TIMER, &mdev->flags))) { + queue = 1; + if (mdev->state.conn == C_VERIFY_S) + mdev->resync_work.cb = w_make_ov_request; + else + mdev->resync_work.cb = w_make_resync_request; + } else { + queue = 0; + mdev->resync_work.cb = w_resync_inactive; + } + + spin_unlock_irqrestore(&mdev->req_lock, flags); + + /* harmless race: list_empty outside data.work.q_lock */ + if (list_empty(&mdev->resync_work.list) && queue) + drbd_queue_work(&mdev->data.work, &mdev->resync_work); +} + +int w_make_resync_request(struct drbd_conf *mdev, + struct drbd_work *w, int cancel) +{ + unsigned long bit; + sector_t sector; + const sector_t capacity = drbd_get_capacity(mdev->this_bdev); + int max_segment_size = queue_max_segment_size(mdev->rq_queue); + int number, i, size, pe, mx; + int align, queued, sndbuf; + + if (unlikely(cancel)) + return 1; + + if (unlikely(mdev->state.conn < C_CONNECTED)) { + dev_err(DEV, "Confused in w_make_resync_request()! cstate < Connected"); + return 0; + } + + if (mdev->state.conn != C_SYNC_TARGET) + dev_err(DEV, "%s in w_make_resync_request\n", + drbd_conn_str(mdev->state.conn)); + + if (!get_ldev(mdev)) { + /* Since we only need to access mdev->rsync a + get_ldev_if_state(mdev,D_FAILED) would be sufficient, but + to continue resync with a broken disk makes no sense at + all */ + dev_err(DEV, "Disk broke down during resync!\n"); + mdev->resync_work.cb = w_resync_inactive; + return 1; + } + + number = SLEEP_TIME * mdev->sync_conf.rate / ((BM_BLOCK_SIZE/1024)*HZ); + pe = atomic_read(&mdev->rs_pending_cnt); + + mutex_lock(&mdev->data.mutex); + if (mdev->data.socket) + mx = mdev->data.socket->sk->sk_rcvbuf / sizeof(struct p_block_req); + else + mx = 1; + mutex_unlock(&mdev->data.mutex); + + /* For resync rates >160MB/sec, allow more pending RS requests */ + if (number > mx) + mx = number; + + /* Limit the number of pending RS requests to no more than the peer's receive buffer */ + if ((pe + number) > mx) { + number = mx - pe; + } + + for (i = 0; i < number; i++) { + /* Stop generating RS requests, when half of the send buffer is filled */ + mutex_lock(&mdev->data.mutex); + if (mdev->data.socket) { + queued = mdev->data.socket->sk->sk_wmem_queued; + sndbuf = mdev->data.socket->sk->sk_sndbuf; + } else { + queued = 1; + sndbuf = 0; + } + mutex_unlock(&mdev->data.mutex); + if (queued > sndbuf / 2) + goto requeue; + +next_sector: + size = BM_BLOCK_SIZE; + bit = drbd_bm_find_next(mdev, mdev->bm_resync_fo); + + if (bit == -1UL) { + mdev->bm_resync_fo = drbd_bm_bits(mdev); + mdev->resync_work.cb = w_resync_inactive; + put_ldev(mdev); + return 1; + } + + sector = BM_BIT_TO_SECT(bit); + + if (drbd_try_rs_begin_io(mdev, sector)) { + mdev->bm_resync_fo = bit; + goto requeue; + } + mdev->bm_resync_fo = bit + 1; + + if (unlikely(drbd_bm_test_bit(mdev, bit) == 0)) { + drbd_rs_complete_io(mdev, sector); + goto next_sector; + } + +#if DRBD_MAX_SEGMENT_SIZE > BM_BLOCK_SIZE + /* try to find some adjacent bits. + * we stop if we have already the maximum req size. + * + * Additionally always align bigger requests, in order to + * be prepared for all stripe sizes of software RAIDs. + * + * we _do_ care about the agreed-upon q->max_segment_size + * here, as splitting up the requests on the other side is more + * difficult. the consequence is, that on lvm and md and other + * "indirect" devices, this is dead code, since + * q->max_segment_size will be PAGE_SIZE. + */ + align = 1; + for (;;) { + if (size + BM_BLOCK_SIZE > max_segment_size) + break; + + /* Be always aligned */ + if (sector & ((1<<(align+3))-1)) + break; + + /* do not cross extent boundaries */ + if (((bit+1) & BM_BLOCKS_PER_BM_EXT_MASK) == 0) + break; + /* now, is it actually dirty, after all? + * caution, drbd_bm_test_bit is tri-state for some + * obscure reason; ( b == 0 ) would get the out-of-band + * only accidentally right because of the "oddly sized" + * adjustment below */ + if (drbd_bm_test_bit(mdev, bit+1) != 1) + break; + bit++; + size += BM_BLOCK_SIZE; + if ((BM_BLOCK_SIZE << align) <= size) + align++; + i++; + } + /* if we merged some, + * reset the offset to start the next drbd_bm_find_next from */ + if (size > BM_BLOCK_SIZE) + mdev->bm_resync_fo = bit + 1; +#endif + + /* adjust very last sectors, in case we are oddly sized */ + if (sector + (size>>9) > capacity) + size = (capacity-sector)<<9; + if (mdev->agreed_pro_version >= 89 && mdev->csums_tfm) { + switch (read_for_csum(mdev, sector, size)) { + case 0: /* Disk failure*/ + put_ldev(mdev); + return 0; + case 2: /* Allocation failed */ + drbd_rs_complete_io(mdev, sector); + mdev->bm_resync_fo = BM_SECT_TO_BIT(sector); + goto requeue; + /* case 1: everything ok */ + } + } else { + inc_rs_pending(mdev); + if (!drbd_send_drequest(mdev, P_RS_DATA_REQUEST, + sector, size, ID_SYNCER)) { + dev_err(DEV, "drbd_send_drequest() failed, aborting...\n"); + dec_rs_pending(mdev); + put_ldev(mdev); + return 0; + } + } + } + + if (mdev->bm_resync_fo >= drbd_bm_bits(mdev)) { + /* last syncer _request_ was sent, + * but the P_RS_DATA_REPLY not yet received. sync will end (and + * next sync group will resume), as soon as we receive the last + * resync data block, and the last bit is cleared. + * until then resync "work" is "inactive" ... + */ + mdev->resync_work.cb = w_resync_inactive; + put_ldev(mdev); + return 1; + } + + requeue: + mod_timer(&mdev->resync_timer, jiffies + SLEEP_TIME); + put_ldev(mdev); + return 1; +} + +static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + int number, i, size; + sector_t sector; + const sector_t capacity = drbd_get_capacity(mdev->this_bdev); + + if (unlikely(cancel)) + return 1; + + if (unlikely(mdev->state.conn < C_CONNECTED)) { + dev_err(DEV, "Confused in w_make_ov_request()! cstate < Connected"); + return 0; + } + + number = SLEEP_TIME*mdev->sync_conf.rate / ((BM_BLOCK_SIZE/1024)*HZ); + if (atomic_read(&mdev->rs_pending_cnt) > number) + goto requeue; + + number -= atomic_read(&mdev->rs_pending_cnt); + + sector = mdev->ov_position; + for (i = 0; i < number; i++) { + if (sector >= capacity) { + mdev->resync_work.cb = w_resync_inactive; + return 1; + } + + size = BM_BLOCK_SIZE; + + if (drbd_try_rs_begin_io(mdev, sector)) { + mdev->ov_position = sector; + goto requeue; + } + + if (sector + (size>>9) > capacity) + size = (capacity-sector)<<9; + + inc_rs_pending(mdev); + if (!drbd_send_ov_request(mdev, sector, size)) { + dec_rs_pending(mdev); + return 0; + } + sector += BM_SECT_PER_BIT; + } + mdev->ov_position = sector; + + requeue: + mod_timer(&mdev->resync_timer, jiffies + SLEEP_TIME); + return 1; +} + + +int w_ov_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + kfree(w); + ov_oos_print(mdev); + drbd_resync_finished(mdev); + + return 1; +} + +static int w_resync_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + kfree(w); + + drbd_resync_finished(mdev); + + return 1; +} + +int drbd_resync_finished(struct drbd_conf *mdev) +{ + unsigned long db, dt, dbdt; + unsigned long n_oos; + union drbd_state os, ns; + struct drbd_work *w; + char *khelper_cmd = NULL; + + /* Remove all elements from the resync LRU. Since future actions + * might set bits in the (main) bitmap, then the entries in the + * resync LRU would be wrong. */ + if (drbd_rs_del_all(mdev)) { + /* In case this is not possible now, most probably because + * there are P_RS_DATA_REPLY Packets lingering on the worker's + * queue (or even the read operations for those packets + * is not finished by now). Retry in 100ms. */ + + drbd_kick_lo(mdev); + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + w = kmalloc(sizeof(struct drbd_work), GFP_ATOMIC); + if (w) { + w->cb = w_resync_finished; + drbd_queue_work(&mdev->data.work, w); + return 1; + } + dev_err(DEV, "Warn failed to drbd_rs_del_all() and to kmalloc(w).\n"); + } + + dt = (jiffies - mdev->rs_start - mdev->rs_paused) / HZ; + if (dt <= 0) + dt = 1; + db = mdev->rs_total; + dbdt = Bit2KB(db/dt); + mdev->rs_paused /= HZ; + + if (!get_ldev(mdev)) + goto out; + + spin_lock_irq(&mdev->req_lock); + os = mdev->state; + + /* This protects us against multiple calls (that can happen in the presence + of application IO), and against connectivity loss just before we arrive here. */ + if (os.conn <= C_CONNECTED) + goto out_unlock; + + ns = os; + ns.conn = C_CONNECTED; + + dev_info(DEV, "%s done (total %lu sec; paused %lu sec; %lu K/sec)\n", + (os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) ? + "Online verify " : "Resync", + dt + mdev->rs_paused, mdev->rs_paused, dbdt); + + n_oos = drbd_bm_total_weight(mdev); + + if (os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) { + if (n_oos) { + dev_alert(DEV, "Online verify found %lu %dk block out of sync!\n", + n_oos, Bit2KB(1)); + khelper_cmd = "out-of-sync"; + } + } else { + D_ASSERT((n_oos - mdev->rs_failed) == 0); + + if (os.conn == C_SYNC_TARGET || os.conn == C_PAUSED_SYNC_T) + khelper_cmd = "after-resync-target"; + + if (mdev->csums_tfm && mdev->rs_total) { + const unsigned long s = mdev->rs_same_csum; + const unsigned long t = mdev->rs_total; + const int ratio = + (t == 0) ? 0 : + (t < 100000) ? ((s*100)/t) : (s/(t/100)); + dev_info(DEV, "%u %% had equal check sums, eliminated: %luK; " + "transferred %luK total %luK\n", + ratio, + Bit2KB(mdev->rs_same_csum), + Bit2KB(mdev->rs_total - mdev->rs_same_csum), + Bit2KB(mdev->rs_total)); + } + } + + if (mdev->rs_failed) { + dev_info(DEV, " %lu failed blocks\n", mdev->rs_failed); + + if (os.conn == C_SYNC_TARGET || os.conn == C_PAUSED_SYNC_T) { + ns.disk = D_INCONSISTENT; + ns.pdsk = D_UP_TO_DATE; + } else { + ns.disk = D_UP_TO_DATE; + ns.pdsk = D_INCONSISTENT; + } + } else { + ns.disk = D_UP_TO_DATE; + ns.pdsk = D_UP_TO_DATE; + + if (os.conn == C_SYNC_TARGET || os.conn == C_PAUSED_SYNC_T) { + if (mdev->p_uuid) { + int i; + for (i = UI_BITMAP ; i <= UI_HISTORY_END ; i++) + _drbd_uuid_set(mdev, i, mdev->p_uuid[i]); + drbd_uuid_set(mdev, UI_BITMAP, mdev->ldev->md.uuid[UI_CURRENT]); + _drbd_uuid_set(mdev, UI_CURRENT, mdev->p_uuid[UI_CURRENT]); + } else { + dev_err(DEV, "mdev->p_uuid is NULL! BUG\n"); + } + } + + drbd_uuid_set_bm(mdev, 0UL); + + if (mdev->p_uuid) { + /* Now the two UUID sets are equal, update what we + * know of the peer. */ + int i; + for (i = UI_CURRENT ; i <= UI_HISTORY_END ; i++) + mdev->p_uuid[i] = mdev->ldev->md.uuid[i]; + } + } + + _drbd_set_state(mdev, ns, CS_VERBOSE, NULL); +out_unlock: + spin_unlock_irq(&mdev->req_lock); + put_ldev(mdev); +out: + mdev->rs_total = 0; + mdev->rs_failed = 0; + mdev->rs_paused = 0; + mdev->ov_start_sector = 0; + + if (test_and_clear_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags)) { + dev_warn(DEV, "Writing the whole bitmap, due to failed kmalloc\n"); + drbd_queue_bitmap_io(mdev, &drbd_bm_write, NULL, "write from resync_finished"); + } + + if (khelper_cmd) + drbd_khelper(mdev, khelper_cmd); + + return 1; +} + +/* helper */ +static void move_to_net_ee_or_free(struct drbd_conf *mdev, struct drbd_epoch_entry *e) +{ + if (drbd_bio_has_active_page(e->private_bio)) { + /* This might happen if sendpage() has not finished */ + spin_lock_irq(&mdev->req_lock); + list_add_tail(&e->w.list, &mdev->net_ee); + spin_unlock_irq(&mdev->req_lock); + } else + drbd_free_ee(mdev, e); +} + +/** + * w_e_end_data_req() - Worker callback, to send a P_DATA_REPLY packet in response to a P_DATA_REQUEST + * @mdev: DRBD device. + * @w: work object. + * @cancel: The connection will be closed anyways + */ +int w_e_end_data_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w); + int ok; + + if (unlikely(cancel)) { + drbd_free_ee(mdev, e); + dec_unacked(mdev); + return 1; + } + + if (likely(drbd_bio_uptodate(e->private_bio))) { + ok = drbd_send_block(mdev, P_DATA_REPLY, e); + } else { + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Sending NegDReply. sector=%llus.\n", + (unsigned long long)e->sector); + + ok = drbd_send_ack(mdev, P_NEG_DREPLY, e); + } + + dec_unacked(mdev); + + move_to_net_ee_or_free(mdev, e); + + if (unlikely(!ok)) + dev_err(DEV, "drbd_send_block() failed\n"); + return ok; +} + +/** + * w_e_end_rsdata_req() - Worker callback to send a P_RS_DATA_REPLY packet in response to a P_RS_DATA_REQUESTRS + * @mdev: DRBD device. + * @w: work object. + * @cancel: The connection will be closed anyways + */ +int w_e_end_rsdata_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w); + int ok; + + if (unlikely(cancel)) { + drbd_free_ee(mdev, e); + dec_unacked(mdev); + return 1; + } + + if (get_ldev_if_state(mdev, D_FAILED)) { + drbd_rs_complete_io(mdev, e->sector); + put_ldev(mdev); + } + + if (likely(drbd_bio_uptodate(e->private_bio))) { + if (likely(mdev->state.pdsk >= D_INCONSISTENT)) { + inc_rs_pending(mdev); + ok = drbd_send_block(mdev, P_RS_DATA_REPLY, e); + } else { + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Not sending RSDataReply, " + "partner DISKLESS!\n"); + ok = 1; + } + } else { + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Sending NegRSDReply. sector %llus.\n", + (unsigned long long)e->sector); + + ok = drbd_send_ack(mdev, P_NEG_RS_DREPLY, e); + + /* update resync data with failure */ + drbd_rs_failed_io(mdev, e->sector, e->size); + } + + dec_unacked(mdev); + + move_to_net_ee_or_free(mdev, e); + + if (unlikely(!ok)) + dev_err(DEV, "drbd_send_block() failed\n"); + return ok; +} + +int w_e_end_csum_rs_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w); + struct digest_info *di; + int digest_size; + void *digest = NULL; + int ok, eq = 0; + + if (unlikely(cancel)) { + drbd_free_ee(mdev, e); + dec_unacked(mdev); + return 1; + } + + drbd_rs_complete_io(mdev, e->sector); + + di = (struct digest_info *)(unsigned long)e->block_id; + + if (likely(drbd_bio_uptodate(e->private_bio))) { + /* quick hack to try to avoid a race against reconfiguration. + * a real fix would be much more involved, + * introducing more locking mechanisms */ + if (mdev->csums_tfm) { + digest_size = crypto_hash_digestsize(mdev->csums_tfm); + D_ASSERT(digest_size == di->digest_size); + digest = kmalloc(digest_size, GFP_NOIO); + } + if (digest) { + drbd_csum(mdev, mdev->csums_tfm, e->private_bio, digest); + eq = !memcmp(digest, di->digest, digest_size); + kfree(digest); + } + + if (eq) { + drbd_set_in_sync(mdev, e->sector, e->size); + mdev->rs_same_csum++; + ok = drbd_send_ack(mdev, P_RS_IS_IN_SYNC, e); + } else { + inc_rs_pending(mdev); + e->block_id = ID_SYNCER; + ok = drbd_send_block(mdev, P_RS_DATA_REPLY, e); + } + } else { + ok = drbd_send_ack(mdev, P_NEG_RS_DREPLY, e); + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Sending NegDReply. I guess it gets messy.\n"); + } + + dec_unacked(mdev); + + kfree(di); + + move_to_net_ee_or_free(mdev, e); + + if (unlikely(!ok)) + dev_err(DEV, "drbd_send_block/ack() failed\n"); + return ok; +} + +int w_e_end_ov_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w); + int digest_size; + void *digest; + int ok = 1; + + if (unlikely(cancel)) + goto out; + + if (unlikely(!drbd_bio_uptodate(e->private_bio))) + goto out; + + digest_size = crypto_hash_digestsize(mdev->verify_tfm); + /* FIXME if this allocation fails, online verify will not terminate! */ + digest = kmalloc(digest_size, GFP_NOIO); + if (digest) { + drbd_csum(mdev, mdev->verify_tfm, e->private_bio, digest); + inc_rs_pending(mdev); + ok = drbd_send_drequest_csum(mdev, e->sector, e->size, + digest, digest_size, P_OV_REPLY); + if (!ok) + dec_rs_pending(mdev); + kfree(digest); + } + +out: + drbd_free_ee(mdev, e); + + dec_unacked(mdev); + + return ok; +} + +void drbd_ov_oos_found(struct drbd_conf *mdev, sector_t sector, int size) +{ + if (mdev->ov_last_oos_start + mdev->ov_last_oos_size == sector) { + mdev->ov_last_oos_size += size>>9; + } else { + mdev->ov_last_oos_start = sector; + mdev->ov_last_oos_size = size>>9; + } + drbd_set_out_of_sync(mdev, sector, size); + set_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags); +} + +int w_e_end_ov_reply(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w); + struct digest_info *di; + int digest_size; + void *digest; + int ok, eq = 0; + + if (unlikely(cancel)) { + drbd_free_ee(mdev, e); + dec_unacked(mdev); + return 1; + } + + /* after "cancel", because after drbd_disconnect/drbd_rs_cancel_all + * the resync lru has been cleaned up already */ + drbd_rs_complete_io(mdev, e->sector); + + di = (struct digest_info *)(unsigned long)e->block_id; + + if (likely(drbd_bio_uptodate(e->private_bio))) { + digest_size = crypto_hash_digestsize(mdev->verify_tfm); + digest = kmalloc(digest_size, GFP_NOIO); + if (digest) { + drbd_csum(mdev, mdev->verify_tfm, e->private_bio, digest); + + D_ASSERT(digest_size == di->digest_size); + eq = !memcmp(digest, di->digest, digest_size); + kfree(digest); + } + } else { + ok = drbd_send_ack(mdev, P_NEG_RS_DREPLY, e); + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "Sending NegDReply. I guess it gets messy.\n"); + } + + dec_unacked(mdev); + + kfree(di); + + if (!eq) + drbd_ov_oos_found(mdev, e->sector, e->size); + else + ov_oos_print(mdev); + + ok = drbd_send_ack_ex(mdev, P_OV_RESULT, e->sector, e->size, + eq ? ID_IN_SYNC : ID_OUT_OF_SYNC); + + drbd_free_ee(mdev, e); + + if (--mdev->ov_left == 0) { + ov_oos_print(mdev); + drbd_resync_finished(mdev); + } + + return ok; +} + +int w_prev_work_done(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_wq_barrier *b = container_of(w, struct drbd_wq_barrier, w); + complete(&b->done); + return 1; +} + +int w_send_barrier(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_tl_epoch *b = container_of(w, struct drbd_tl_epoch, w); + struct p_barrier *p = &mdev->data.sbuf.barrier; + int ok = 1; + + /* really avoid racing with tl_clear. w.cb may have been referenced + * just before it was reassigned and re-queued, so double check that. + * actually, this race was harmless, since we only try to send the + * barrier packet here, and otherwise do nothing with the object. + * but compare with the head of w_clear_epoch */ + spin_lock_irq(&mdev->req_lock); + if (w->cb != w_send_barrier || mdev->state.conn < C_CONNECTED) + cancel = 1; + spin_unlock_irq(&mdev->req_lock); + if (cancel) + return 1; + + if (!drbd_get_data_sock(mdev)) + return 0; + p->barrier = b->br_number; + /* inc_ap_pending was done where this was queued. + * dec_ap_pending will be done in got_BarrierAck + * or (on connection loss) in w_clear_epoch. */ + ok = _drbd_send_cmd(mdev, mdev->data.socket, P_BARRIER, + (struct p_header *)p, sizeof(*p), 0); + drbd_put_data_sock(mdev); + + return ok; +} + +int w_send_write_hint(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + if (cancel) + return 1; + return drbd_send_short_cmd(mdev, P_UNPLUG_REMOTE); +} + +/** + * w_send_dblock() - Worker callback to send a P_DATA packet in order to mirror a write request + * @mdev: DRBD device. + * @w: work object. + * @cancel: The connection will be closed anyways + */ +int w_send_dblock(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_request *req = container_of(w, struct drbd_request, w); + int ok; + + if (unlikely(cancel)) { + req_mod(req, send_canceled); + return 1; + } + + ok = drbd_send_dblock(mdev, req); + req_mod(req, ok ? handed_over_to_network : send_failed); + + return ok; +} + +/** + * w_send_read_req() - Worker callback to send a read request (P_DATA_REQUEST) packet + * @mdev: DRBD device. + * @w: work object. + * @cancel: The connection will be closed anyways + */ +int w_send_read_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_request *req = container_of(w, struct drbd_request, w); + int ok; + + if (unlikely(cancel)) { + req_mod(req, send_canceled); + return 1; + } + + ok = drbd_send_drequest(mdev, P_DATA_REQUEST, req->sector, req->size, + (unsigned long)req); + + if (!ok) { + /* ?? we set C_TIMEOUT or C_BROKEN_PIPE in drbd_send(); + * so this is probably redundant */ + if (mdev->state.conn >= C_CONNECTED) + drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE)); + } + req_mod(req, ok ? handed_over_to_network : send_failed); + + return ok; +} + +static int _drbd_may_sync_now(struct drbd_conf *mdev) +{ + struct drbd_conf *odev = mdev; + + while (1) { + if (odev->sync_conf.after == -1) + return 1; + odev = minor_to_mdev(odev->sync_conf.after); + ERR_IF(!odev) return 1; + if ((odev->state.conn >= C_SYNC_SOURCE && + odev->state.conn <= C_PAUSED_SYNC_T) || + odev->state.aftr_isp || odev->state.peer_isp || + odev->state.user_isp) + return 0; + } +} + +/** + * _drbd_pause_after() - Pause resync on all devices that may not resync now + * @mdev: DRBD device. + * + * Called from process context only (admin command and after_state_ch). + */ +static int _drbd_pause_after(struct drbd_conf *mdev) +{ + struct drbd_conf *odev; + int i, rv = 0; + + for (i = 0; i < minor_count; i++) { + odev = minor_to_mdev(i); + if (!odev) + continue; + if (odev->state.conn == C_STANDALONE && odev->state.disk == D_DISKLESS) + continue; + if (!_drbd_may_sync_now(odev)) + rv |= (__drbd_set_state(_NS(odev, aftr_isp, 1), CS_HARD, NULL) + != SS_NOTHING_TO_DO); + } + + return rv; +} + +/** + * _drbd_resume_next() - Resume resync on all devices that may resync now + * @mdev: DRBD device. + * + * Called from process context only (admin command and worker). + */ +static int _drbd_resume_next(struct drbd_conf *mdev) +{ + struct drbd_conf *odev; + int i, rv = 0; + + for (i = 0; i < minor_count; i++) { + odev = minor_to_mdev(i); + if (!odev) + continue; + if (odev->state.conn == C_STANDALONE && odev->state.disk == D_DISKLESS) + continue; + if (odev->state.aftr_isp) { + if (_drbd_may_sync_now(odev)) + rv |= (__drbd_set_state(_NS(odev, aftr_isp, 0), + CS_HARD, NULL) + != SS_NOTHING_TO_DO) ; + } + } + return rv; +} + +void resume_next_sg(struct drbd_conf *mdev) +{ + write_lock_irq(&global_state_lock); + _drbd_resume_next(mdev); + write_unlock_irq(&global_state_lock); +} + +void suspend_other_sg(struct drbd_conf *mdev) +{ + write_lock_irq(&global_state_lock); + _drbd_pause_after(mdev); + write_unlock_irq(&global_state_lock); +} + +static int sync_after_error(struct drbd_conf *mdev, int o_minor) +{ + struct drbd_conf *odev; + + if (o_minor == -1) + return NO_ERROR; + if (o_minor < -1 || minor_to_mdev(o_minor) == NULL) + return ERR_SYNC_AFTER; + + /* check for loops */ + odev = minor_to_mdev(o_minor); + while (1) { + if (odev == mdev) + return ERR_SYNC_AFTER_CYCLE; + + /* dependency chain ends here, no cycles. */ + if (odev->sync_conf.after == -1) + return NO_ERROR; + + /* follow the dependency chain */ + odev = minor_to_mdev(odev->sync_conf.after); + } +} + +int drbd_alter_sa(struct drbd_conf *mdev, int na) +{ + int changes; + int retcode; + + write_lock_irq(&global_state_lock); + retcode = sync_after_error(mdev, na); + if (retcode == NO_ERROR) { + mdev->sync_conf.after = na; + do { + changes = _drbd_pause_after(mdev); + changes |= _drbd_resume_next(mdev); + } while (changes); + } + write_unlock_irq(&global_state_lock); + return retcode; +} + +/** + * drbd_start_resync() - Start the resync process + * @mdev: DRBD device. + * @side: Either C_SYNC_SOURCE or C_SYNC_TARGET + * + * This function might bring you directly into one of the + * C_PAUSED_SYNC_* states. + */ +void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side) +{ + union drbd_state ns; + int r; + + if (mdev->state.conn >= C_SYNC_SOURCE) { + dev_err(DEV, "Resync already running!\n"); + return; + } + + /* In case a previous resync run was aborted by an IO error/detach on the peer. */ + drbd_rs_cancel_all(mdev); + + if (side == C_SYNC_TARGET) { + /* Since application IO was locked out during C_WF_BITMAP_T and + C_WF_SYNC_UUID we are still unmodified. Before going to C_SYNC_TARGET + we check that we might make the data inconsistent. */ + r = drbd_khelper(mdev, "before-resync-target"); + r = (r >> 8) & 0xff; + if (r > 0) { + dev_info(DEV, "before-resync-target handler returned %d, " + "dropping connection.\n", r); + drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); + return; + } + } + + drbd_state_lock(mdev); + + if (!get_ldev_if_state(mdev, D_NEGOTIATING)) { + drbd_state_unlock(mdev); + return; + } + + if (side == C_SYNC_TARGET) { + mdev->bm_resync_fo = 0; + } else /* side == C_SYNC_SOURCE */ { + u64 uuid; + + get_random_bytes(&uuid, sizeof(u64)); + drbd_uuid_set(mdev, UI_BITMAP, uuid); + drbd_send_sync_uuid(mdev, uuid); + + D_ASSERT(mdev->state.disk == D_UP_TO_DATE); + } + + write_lock_irq(&global_state_lock); + ns = mdev->state; + + ns.aftr_isp = !_drbd_may_sync_now(mdev); + + ns.conn = side; + + if (side == C_SYNC_TARGET) + ns.disk = D_INCONSISTENT; + else /* side == C_SYNC_SOURCE */ + ns.pdsk = D_INCONSISTENT; + + r = __drbd_set_state(mdev, ns, CS_VERBOSE, NULL); + ns = mdev->state; + + if (ns.conn < C_CONNECTED) + r = SS_UNKNOWN_ERROR; + + if (r == SS_SUCCESS) { + mdev->rs_total = + mdev->rs_mark_left = drbd_bm_total_weight(mdev); + mdev->rs_failed = 0; + mdev->rs_paused = 0; + mdev->rs_start = + mdev->rs_mark_time = jiffies; + mdev->rs_same_csum = 0; + _drbd_pause_after(mdev); + } + write_unlock_irq(&global_state_lock); + drbd_state_unlock(mdev); + put_ldev(mdev); + + if (r == SS_SUCCESS) { + dev_info(DEV, "Began resync as %s (will sync %lu KB [%lu bits set]).\n", + drbd_conn_str(ns.conn), + (unsigned long) mdev->rs_total << (BM_BLOCK_SHIFT-10), + (unsigned long) mdev->rs_total); + + if (mdev->rs_total == 0) { + /* Peer still reachable? Beware of failing before-resync-target handlers! */ + request_ping(mdev); + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(mdev->net_conf->ping_timeo*HZ/9); /* 9 instead 10 */ + drbd_resync_finished(mdev); + return; + } + + /* ns.conn may already be != mdev->state.conn, + * we may have been paused in between, or become paused until + * the timer triggers. + * No matter, that is handled in resync_timer_fn() */ + if (ns.conn == C_SYNC_TARGET) + mod_timer(&mdev->resync_timer, jiffies); + + drbd_md_sync(mdev); + } +} + +int drbd_worker(struct drbd_thread *thi) +{ + struct drbd_conf *mdev = thi->mdev; + struct drbd_work *w = NULL; + LIST_HEAD(work_list); + int intr = 0, i; + + sprintf(current->comm, "drbd%d_worker", mdev_to_minor(mdev)); + + while (get_t_state(thi) == Running) { + drbd_thread_current_set_cpu(mdev); + + if (down_trylock(&mdev->data.work.s)) { + mutex_lock(&mdev->data.mutex); + if (mdev->data.socket && !mdev->net_conf->no_cork) + drbd_tcp_uncork(mdev->data.socket); + mutex_unlock(&mdev->data.mutex); + + intr = down_interruptible(&mdev->data.work.s); + + mutex_lock(&mdev->data.mutex); + if (mdev->data.socket && !mdev->net_conf->no_cork) + drbd_tcp_cork(mdev->data.socket); + mutex_unlock(&mdev->data.mutex); + } + + if (intr) { + D_ASSERT(intr == -EINTR); + flush_signals(current); + ERR_IF (get_t_state(thi) == Running) + continue; + break; + } + + if (get_t_state(thi) != Running) + break; + /* With this break, we have done a down() but not consumed + the entry from the list. The cleanup code takes care of + this... */ + + w = NULL; + spin_lock_irq(&mdev->data.work.q_lock); + ERR_IF(list_empty(&mdev->data.work.q)) { + /* something terribly wrong in our logic. + * we were able to down() the semaphore, + * but the list is empty... doh. + * + * what is the best thing to do now? + * try again from scratch, restarting the receiver, + * asender, whatnot? could break even more ugly, + * e.g. when we are primary, but no good local data. + * + * I'll try to get away just starting over this loop. + */ + spin_unlock_irq(&mdev->data.work.q_lock); + continue; + } + w = list_entry(mdev->data.work.q.next, struct drbd_work, list); + list_del_init(&w->list); + spin_unlock_irq(&mdev->data.work.q_lock); + + if (!w->cb(mdev, w, mdev->state.conn < C_CONNECTED)) { + /* dev_warn(DEV, "worker: a callback failed! \n"); */ + if (mdev->state.conn >= C_CONNECTED) + drbd_force_state(mdev, + NS(conn, C_NETWORK_FAILURE)); + } + } + D_ASSERT(test_bit(DEVICE_DYING, &mdev->flags)); + D_ASSERT(test_bit(CONFIG_PENDING, &mdev->flags)); + + spin_lock_irq(&mdev->data.work.q_lock); + i = 0; + while (!list_empty(&mdev->data.work.q)) { + list_splice_init(&mdev->data.work.q, &work_list); + spin_unlock_irq(&mdev->data.work.q_lock); + + while (!list_empty(&work_list)) { + w = list_entry(work_list.next, struct drbd_work, list); + list_del_init(&w->list); + w->cb(mdev, w, 1); + i++; /* dead debugging code */ + } + + spin_lock_irq(&mdev->data.work.q_lock); + } + sema_init(&mdev->data.work.s, 0); + /* DANGEROUS race: if someone did queue his work within the spinlock, + * but up() ed outside the spinlock, we could get an up() on the + * semaphore without corresponding list entry. + * So don't do that. + */ + spin_unlock_irq(&mdev->data.work.q_lock); + + D_ASSERT(mdev->state.disk == D_DISKLESS && mdev->state.conn == C_STANDALONE); + /* _drbd_set_state only uses stop_nowait. + * wait here for the Exiting receiver. */ + drbd_thread_stop(&mdev->receiver); + drbd_mdev_cleanup(mdev); + + dev_info(DEV, "worker terminated\n"); + + clear_bit(DEVICE_DYING, &mdev->flags); + clear_bit(CONFIG_PENDING, &mdev->flags); + wake_up(&mdev->state_wait); + + return 0; +} diff --git a/drivers/block/drbd/drbd_wrappers.h b/drivers/block/drbd/drbd_wrappers.h new file mode 100644 index 000000000000..f93fa111ce50 --- /dev/null +++ b/drivers/block/drbd/drbd_wrappers.h @@ -0,0 +1,91 @@ +#ifndef _DRBD_WRAPPERS_H +#define _DRBD_WRAPPERS_H + +#include <linux/ctype.h> +#include <linux/mm.h> + +/* see get_sb_bdev and bd_claim */ +extern char *drbd_sec_holder; + +/* sets the number of 512 byte sectors of our virtual device */ +static inline void drbd_set_my_capacity(struct drbd_conf *mdev, + sector_t size) +{ + /* set_capacity(mdev->this_bdev->bd_disk, size); */ + set_capacity(mdev->vdisk, size); + mdev->this_bdev->bd_inode->i_size = (loff_t)size << 9; +} + +#define drbd_bio_uptodate(bio) bio_flagged(bio, BIO_UPTODATE) + +static inline int drbd_bio_has_active_page(struct bio *bio) +{ + struct bio_vec *bvec; + int i; + + __bio_for_each_segment(bvec, bio, i, 0) { + if (page_count(bvec->bv_page) > 1) + return 1; + } + + return 0; +} + +/* bi_end_io handlers */ +extern void drbd_md_io_complete(struct bio *bio, int error); +extern void drbd_endio_read_sec(struct bio *bio, int error); +extern void drbd_endio_write_sec(struct bio *bio, int error); +extern void drbd_endio_pri(struct bio *bio, int error); + +/* + * used to submit our private bio + */ +static inline void drbd_generic_make_request(struct drbd_conf *mdev, + int fault_type, struct bio *bio) +{ + __release(local); + if (!bio->bi_bdev) { + printk(KERN_ERR "drbd%d: drbd_generic_make_request: " + "bio->bi_bdev == NULL\n", + mdev_to_minor(mdev)); + dump_stack(); + bio_endio(bio, -ENODEV); + return; + } + + if (FAULT_ACTIVE(mdev, fault_type)) + bio_endio(bio, -EIO); + else + generic_make_request(bio); +} + +static inline void drbd_plug_device(struct drbd_conf *mdev) +{ + struct request_queue *q; + q = bdev_get_queue(mdev->this_bdev); + + spin_lock_irq(q->queue_lock); + +/* XXX the check on !blk_queue_plugged is redundant, + * implicitly checked in blk_plug_device */ + + if (!blk_queue_plugged(q)) { + blk_plug_device(q); + del_timer(&q->unplug_timer); + /* unplugging should not happen automatically... */ + } + spin_unlock_irq(q->queue_lock); +} + +static inline int drbd_crypto_is_hash(struct crypto_tfm *tfm) +{ + return (crypto_tfm_alg_type(tfm) & CRYPTO_ALG_TYPE_HASH_MASK) + == CRYPTO_ALG_TYPE_HASH; +} + +#ifndef __CHECKER__ +# undef __cond_lock +# define __cond_lock(x,c) (c) +#endif + +#endif diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index 3bb7c47c869f..1fb6c3135fc8 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -123,7 +123,15 @@ static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev, { struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); - unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); + unsigned long timeout; + + for (timeout = 20; timeout; timeout--) { + if (!notify[3]) + return 0; + udelay(10); + } + + timeout = jiffies + msecs_to_jiffies(timeout_ms); do { if (!notify[3]) diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c index 4617bd12f63b..d43b5cb864ef 100644 --- a/drivers/bluetooth/btmrvl_debugfs.c +++ b/drivers/bluetooth/btmrvl_debugfs.c @@ -29,7 +29,6 @@ struct btmrvl_debugfs_data { struct dentry *root_dir, *config_dir, *status_dir; /* config */ - struct dentry *drvdbg; struct dentry *psmode; struct dentry *pscmd; struct dentry *hsmode; diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 411c7a77082d..523d197b9824 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -131,6 +131,7 @@ void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb); int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb); int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd); +int btmrvl_enable_ps(struct btmrvl_private *priv); int btmrvl_prepare_command(struct btmrvl_private *priv); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index e605563b4eaa..f97771ce432c 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -189,6 +189,38 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) } EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd); +int btmrvl_enable_ps(struct btmrvl_private *priv) +{ + struct sk_buff *skb; + struct btmrvl_cmd *cmd; + + skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); + if (skb == NULL) { + BT_ERR("No free skb"); + return -ENOMEM; + } + + cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); + cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, + BT_CMD_AUTO_SLEEP_MODE)); + cmd->length = 1; + + if (priv->btmrvl_dev.psmode) + cmd->data[0] = BT_PS_ENABLE; + else + cmd->data[0] = BT_PS_DISABLE; + + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + + skb->dev = (void *) priv->btmrvl_dev.hcidev; + skb_queue_head(&priv->adapter->tx_queue, skb); + + BT_DBG("Queue PSMODE Command:%d", cmd->data[0]); + + return 0; +} +EXPORT_SYMBOL_GPL(btmrvl_enable_ps); + static int btmrvl_enable_hs(struct btmrvl_private *priv) { struct sk_buff *skb; @@ -258,28 +290,7 @@ int btmrvl_prepare_command(struct btmrvl_private *priv) if (priv->btmrvl_dev.pscmd) { priv->btmrvl_dev.pscmd = 0; - - skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); - if (skb == NULL) { - BT_ERR("No free skb"); - return -ENOMEM; - } - - cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); - cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_AUTO_SLEEP_MODE)); - cmd->length = 1; - - if (priv->btmrvl_dev.psmode) - cmd->data[0] = BT_PS_ENABLE; - else - cmd->data[0] = BT_PS_DISABLE; - - bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; - - skb->dev = (void *) priv->btmrvl_dev.hcidev; - skb_queue_head(&priv->adapter->tx_queue, skb); - - BT_DBG("Queue PSMODE Command:%d", cmd->data[0]); + btmrvl_enable_ps(priv); } if (priv->btmrvl_dev.hscmd) { diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 5b33b85790f2..f36defa37764 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -535,7 +535,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) break; default: - BT_ERR("Unknow packet type:%d", type); + BT_ERR("Unknown packet type:%d", type); print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload, blksz * buf_block_len); @@ -930,6 +930,8 @@ static int btmrvl_sdio_probe(struct sdio_func *func, priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + priv->btmrvl_dev.psmode = 1; + btmrvl_enable_ps(priv); return 0; @@ -1001,3 +1003,5 @@ MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE("sd8688_helper.bin"); +MODULE_FIRMWARE("sd8688.bin"); diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 4895f0e05322..aa0919386b8c 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -214,7 +214,7 @@ static int hci_uart_send_frame(struct sk_buff *skb) struct hci_uart *hu; if (!hdev) { - BT_ERR("Frame for uknown device (hdev=NULL)"); + BT_ERR("Frame for unknown device (hdev=NULL)"); return -ENODEV; } diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index d5cde6d86f89..7595274103fd 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -41,8 +41,6 @@ #define VERSION "1.3" -static int minor = MISC_DYNAMIC_MINOR; - struct vhci_data { struct hci_dev *hdev; @@ -218,12 +216,6 @@ static unsigned int vhci_poll(struct file *file, poll_table *wait) return POLLOUT | POLLWRNORM; } -static int vhci_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -EINVAL; -} - static int vhci_open(struct inode *inode, struct file *file) { struct vhci_data *data; @@ -284,10 +276,10 @@ static int vhci_release(struct inode *inode, struct file *file) } static const struct file_operations vhci_fops = { + .owner = THIS_MODULE, .read = vhci_read, .write = vhci_write, .poll = vhci_poll, - .ioctl = vhci_ioctl, .open = vhci_open, .release = vhci_release, }; @@ -302,18 +294,12 @@ static int __init vhci_init(void) { BT_INFO("Virtual HCI driver ver %s", VERSION); - if (misc_register(&vhci_miscdev) < 0) { - BT_ERR("Can't register misc device with minor %d", minor); - return -EIO; - } - - return 0; + return misc_register(&vhci_miscdev); } static void __exit vhci_exit(void) { - if (misc_deregister(&vhci_miscdev) < 0) - BT_ERR("Can't unregister misc device with minor %d", minor); + misc_deregister(&vhci_miscdev); } module_init(vhci_init); diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 614da5b8613a..e3749d0ba68b 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -3557,67 +3557,65 @@ static ctl_table cdrom_table[] = { .data = &cdrom_sysctl_settings.info, .maxlen = CDROM_STR_SIZE, .mode = 0444, - .proc_handler = &cdrom_sysctl_info, + .proc_handler = cdrom_sysctl_info, }, { .procname = "autoclose", .data = &cdrom_sysctl_settings.autoclose, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler, + .proc_handler = cdrom_sysctl_handler, }, { .procname = "autoeject", .data = &cdrom_sysctl_settings.autoeject, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler, + .proc_handler = cdrom_sysctl_handler, }, { .procname = "debug", .data = &cdrom_sysctl_settings.debug, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler, + .proc_handler = cdrom_sysctl_handler, }, { .procname = "lock", .data = &cdrom_sysctl_settings.lock, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler, + .proc_handler = cdrom_sysctl_handler, }, { .procname = "check_media", .data = &cdrom_sysctl_settings.check, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler + .proc_handler = cdrom_sysctl_handler }, - { .ctl_name = 0 } + { } }; static ctl_table cdrom_cdrom_table[] = { { - .ctl_name = DEV_CDROM, .procname = "cdrom", .maxlen = 0, .mode = 0555, .child = cdrom_table, }, - { .ctl_name = 0 } + { } }; /* Make sure that /proc/sys/dev is there */ static ctl_table cdrom_root_table[] = { { - .ctl_name = CTL_DEV, .procname = "dev", .maxlen = 0, .mode = 0555, .child = cdrom_cdrom_table, }, - { .ctl_name = 0 } + { } }; static struct ctl_table_header *cdrom_sysctl_header; diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c index a762283d2a21..e789e6c9a422 100644 --- a/drivers/cdrom/gdrom.c +++ b/drivers/cdrom/gdrom.c @@ -214,7 +214,7 @@ static void gdrom_spicommand(void *spi_string, int buflen) gdrom_getsense(NULL); return; } - outsw(PHYSADDR(GDROM_DATA_REG), cmd, 6); + outsw(GDROM_DATA_REG, cmd, 6); } @@ -298,7 +298,7 @@ static int gdrom_readtoc_cmd(struct gdromtoc *toc, int session) err = -EINVAL; goto cleanup_readtoc; } - insw(PHYSADDR(GDROM_DATA_REG), toc, tocsize/2); + insw(GDROM_DATA_REG, toc, tocsize/2); if (gd.status & 0x01) err = -EINVAL; @@ -449,7 +449,7 @@ static int gdrom_getsense(short *bufstring) GDROM_DEFAULT_TIMEOUT); if (gd.pending) goto cleanup_sense; - insw(PHYSADDR(GDROM_DATA_REG), &sense, sense_command->buflen/2); + insw(GDROM_DATA_REG, &sense, sense_command->buflen/2); if (sense[1] & 40) { printk(KERN_INFO "GDROM: Drive not ready - command aborted\n"); goto cleanup_sense; @@ -586,7 +586,7 @@ static void gdrom_readdisk_dma(struct work_struct *work) spin_unlock(&gdrom_lock); block = blk_rq_pos(req)/GD_TO_BLK + GD_SESSION_OFFSET; block_cnt = blk_rq_sectors(req)/GD_TO_BLK; - ctrl_outl(PHYSADDR(req->buffer), GDROM_DMA_STARTADDR_REG); + ctrl_outl(virt_to_phys(req->buffer), GDROM_DMA_STARTADDR_REG); ctrl_outl(block_cnt * GDROM_HARD_SECTOR, GDROM_DMA_LENGTH_REG); ctrl_outl(1, GDROM_DMA_DIRECTION_REG); ctrl_outl(1, GDROM_DMA_ENABLE_REG); @@ -615,7 +615,7 @@ static void gdrom_readdisk_dma(struct work_struct *work) cpu_relax(); gd.pending = 1; gd.transfer = 1; - outsw(PHYSADDR(GDROM_DATA_REG), &read_command->cmd, 6); + outsw(GDROM_DATA_REG, &read_command->cmd, 6); timeout = jiffies + HZ / 2; /* Wait for any pending DMA to finish */ while (ctrl_inb(GDROM_DMA_STATUS_REG) && diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index a96f3197e60f..43412c03969e 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -676,25 +676,25 @@ static int agp_open(struct inode *inode, struct file *file) int minor = iminor(inode); struct agp_file_private *priv; struct agp_client *client; - int rc = -ENXIO; - - lock_kernel(); - mutex_lock(&(agp_fe.agp_mutex)); if (minor != AGPGART_MINOR) - goto err_out; + return -ENXIO; + + mutex_lock(&(agp_fe.agp_mutex)); priv = kzalloc(sizeof(struct agp_file_private), GFP_KERNEL); - if (priv == NULL) - goto err_out_nomem; + if (priv == NULL) { + mutex_unlock(&(agp_fe.agp_mutex)); + return -ENOMEM; + } set_bit(AGP_FF_ALLOW_CLIENT, &priv->access_flags); priv->my_pid = current->pid; - if (capable(CAP_SYS_RAWIO)) { + if (capable(CAP_SYS_RAWIO)) /* Root priv, can be controller */ set_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags); - } + client = agp_find_client_by_pid(current->pid); if (client != NULL) { @@ -704,16 +704,10 @@ static int agp_open(struct inode *inode, struct file *file) file->private_data = (void *) priv; agp_insert_file_private(priv); DBG("private=%p, client=%p", priv, client); - mutex_unlock(&(agp_fe.agp_mutex)); - unlock_kernel(); - return 0; -err_out_nomem: - rc = -ENOMEM; -err_out: mutex_unlock(&(agp_fe.agp_mutex)); - unlock_kernel(); - return rc; + + return 0; } diff --git a/drivers/char/cs5535_gpio.c b/drivers/char/cs5535_gpio.c index 04ba906b4880..4d830dc482ef 100644 --- a/drivers/char/cs5535_gpio.c +++ b/drivers/char/cs5535_gpio.c @@ -17,7 +17,7 @@ #include <linux/cdev.h> #include <linux/ioport.h> #include <linux/pci.h> -#include <linux/smp_lock.h> + #include <asm/uaccess.h> #include <asm/io.h> @@ -158,7 +158,6 @@ static int cs5535_gpio_open(struct inode *inode, struct file *file) { u32 m = iminor(inode); - cycle_kernel_lock(); /* the mask says which pins are usable by this driver */ if ((mask & (1 << m)) == 0) return -EINVAL; diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c index 34d15d548236..26a47dc88f61 100644 --- a/drivers/char/efirtc.c +++ b/drivers/char/efirtc.c @@ -27,8 +27,6 @@ * - Add module support */ - -#include <linux/smp_lock.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/miscdevice.h> @@ -174,13 +172,12 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd, return -EINVAL; case RTC_RD_TIME: - lock_kernel(); spin_lock_irqsave(&efi_rtc_lock, flags); status = efi.get_time(&eft, &cap); spin_unlock_irqrestore(&efi_rtc_lock,flags); - unlock_kernel(); + if (status != EFI_SUCCESS) { /* should never happen */ printk(KERN_ERR "efitime: can't read time\n"); @@ -202,13 +199,11 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd, convert_to_efi_time(&wtime, &eft); - lock_kernel(); spin_lock_irqsave(&efi_rtc_lock, flags); status = efi.set_time(&eft); spin_unlock_irqrestore(&efi_rtc_lock,flags); - unlock_kernel(); return status == EFI_SUCCESS ? 0 : -EINVAL; @@ -224,7 +219,6 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd, convert_to_efi_time(&wtime, &eft); - lock_kernel(); spin_lock_irqsave(&efi_rtc_lock, flags); /* * XXX Fixme: @@ -235,19 +229,16 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd, status = efi.set_wakeup_time((efi_bool_t)enabled, &eft); spin_unlock_irqrestore(&efi_rtc_lock,flags); - unlock_kernel(); return status == EFI_SUCCESS ? 0 : -EINVAL; case RTC_WKALM_RD: - lock_kernel(); spin_lock_irqsave(&efi_rtc_lock, flags); status = efi.get_wakeup_time((efi_bool_t *)&enabled, (efi_bool_t *)&pending, &eft); spin_unlock_irqrestore(&efi_rtc_lock,flags); - unlock_kernel(); if (status != EFI_SUCCESS) return -EINVAL; @@ -277,7 +268,6 @@ static int efi_rtc_open(struct inode *inode, struct file *file) * We do accept multiple open files at the same time as we * synchronize on the per call operation. */ - cycle_kernel_lock(); return 0; } diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c index ef31738c2cbe..fda4181b5e67 100644 --- a/drivers/char/generic_nvram.c +++ b/drivers/char/generic_nvram.c @@ -19,7 +19,6 @@ #include <linux/miscdevice.h> #include <linux/fcntl.h> #include <linux/init.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/nvram.h> #ifdef CONFIG_PPC_PMAC @@ -32,7 +31,6 @@ static ssize_t nvram_len; static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) { - lock_kernel(); switch (origin) { case 1: offset += file->f_pos; @@ -41,12 +39,11 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) offset += nvram_len; break; } - if (offset < 0) { - unlock_kernel(); + if (offset < 0) return -EINVAL; - } + file->f_pos = offset; - unlock_kernel(); + return file->f_pos; } diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 70a770ac0138..e481c5938bad 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -675,36 +675,33 @@ static int hpet_is_known(struct hpet_data *hdp) static ctl_table hpet_table[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "max-user-freq", .data = &hpet_max_freq, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, - {.ctl_name = 0} + {} }; static ctl_table hpet_root[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "hpet", .maxlen = 0, .mode = 0555, .child = hpet_table, }, - {.ctl_name = 0} + {} }; static ctl_table dev_root[] = { { - .ctl_name = CTL_DEV, .procname = "dev", .maxlen = 0, .mode = 0555, .child = hpet_root, }, - {.ctl_name = 0} + {} }; static struct ctl_table_header *sysctl_header; diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 1573aebd54b5..e989f67bb61f 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -52,7 +52,9 @@ static struct hwrng *current_rng; static LIST_HEAD(rng_list); static DEFINE_MUTEX(rng_mutex); - +static int data_avail; +static u8 rng_buffer[SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES] + __cacheline_aligned; static inline int hwrng_init(struct hwrng *rng) { @@ -67,19 +69,6 @@ static inline void hwrng_cleanup(struct hwrng *rng) rng->cleanup(rng); } -static inline int hwrng_data_present(struct hwrng *rng, int wait) -{ - if (!rng->data_present) - return 1; - return rng->data_present(rng, wait); -} - -static inline int hwrng_data_read(struct hwrng *rng, u32 *data) -{ - return rng->data_read(rng, data); -} - - static int rng_dev_open(struct inode *inode, struct file *filp) { /* enforce read-only access to this chrdev */ @@ -87,58 +76,90 @@ static int rng_dev_open(struct inode *inode, struct file *filp) return -EINVAL; if (filp->f_mode & FMODE_WRITE) return -EINVAL; - cycle_kernel_lock(); + return 0; +} + +static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, + int wait) { + int present; + + if (rng->read) + return rng->read(rng, (void *)buffer, size, wait); + + if (rng->data_present) + present = rng->data_present(rng, wait); + else + present = 1; + + if (present) + return rng->data_read(rng, (u32 *)buffer); + return 0; } static ssize_t rng_dev_read(struct file *filp, char __user *buf, size_t size, loff_t *offp) { - u32 data; ssize_t ret = 0; int err = 0; - int bytes_read; + int bytes_read, len; while (size) { - err = -ERESTARTSYS; - if (mutex_lock_interruptible(&rng_mutex)) + if (mutex_lock_interruptible(&rng_mutex)) { + err = -ERESTARTSYS; goto out; + } + if (!current_rng) { - mutex_unlock(&rng_mutex); err = -ENODEV; - goto out; + goto out_unlock; } - bytes_read = 0; - if (hwrng_data_present(current_rng, - !(filp->f_flags & O_NONBLOCK))) - bytes_read = hwrng_data_read(current_rng, &data); - mutex_unlock(&rng_mutex); - - err = -EAGAIN; - if (!bytes_read && (filp->f_flags & O_NONBLOCK)) - goto out; - if (bytes_read < 0) { - err = bytes_read; - goto out; + if (!data_avail) { + bytes_read = rng_get_data(current_rng, rng_buffer, + sizeof(rng_buffer), + !(filp->f_flags & O_NONBLOCK)); + if (bytes_read < 0) { + err = bytes_read; + goto out_unlock; + } + data_avail = bytes_read; } - err = -EFAULT; - while (bytes_read && size) { - if (put_user((u8)data, buf++)) - goto out; - size--; - ret++; - bytes_read--; - data >>= 8; + if (!data_avail) { + if (filp->f_flags & O_NONBLOCK) { + err = -EAGAIN; + goto out_unlock; + } + } else { + len = data_avail; + if (len > size) + len = size; + + data_avail -= len; + + if (copy_to_user(buf + ret, rng_buffer + data_avail, + len)) { + err = -EFAULT; + goto out_unlock; + } + + size -= len; + ret += len; } + mutex_unlock(&rng_mutex); + if (need_resched()) schedule_timeout_interruptible(1); - err = -ERESTARTSYS; - if (signal_pending(current)) + + if (signal_pending(current)) { + err = -ERESTARTSYS; goto out; + } } +out_unlock: + mutex_unlock(&rng_mutex); out: return ret ? : err; } @@ -280,7 +301,7 @@ int hwrng_register(struct hwrng *rng) struct hwrng *old_rng, *tmp; if (rng->name == NULL || - rng->data_read == NULL) + (rng->data_read == NULL && rng->read == NULL)) goto out; mutex_lock(&rng_mutex); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 915157fcff98..bdaef8e94021 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -16,6 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + #include <linux/err.h> #include <linux/hw_random.h> #include <linux/scatterlist.h> @@ -23,78 +24,64 @@ #include <linux/virtio.h> #include <linux/virtio_rng.h> -/* The host will fill any buffer we give it with sweet, sweet randomness. We - * give it 64 bytes at a time, and the hwrng framework takes it 4 bytes at a - * time. */ -#define RANDOM_DATA_SIZE 64 - static struct virtqueue *vq; -static u32 *random_data; -static unsigned int data_left; +static unsigned int data_avail; static DECLARE_COMPLETION(have_data); +static bool busy; static void random_recv_done(struct virtqueue *vq) { - unsigned int len; - /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!vq->vq_ops->get_buf(vq, &len)) + if (!vq->vq_ops->get_buf(vq, &data_avail)) return; - data_left += len; complete(&have_data); } -static void register_buffer(void) +/* The host will fill any buffer we give it with sweet, sweet randomness. */ +static void register_buffer(u8 *buf, size_t size) { struct scatterlist sg; - sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left); + sg_init_one(&sg, buf, size); + /* There should always be room for one buffer. */ - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) < 0) + if (vq->vq_ops->add_buf(vq, &sg, 0, 1, buf) < 0) BUG(); + vq->vq_ops->kick(vq); } -/* At least we don't udelay() in a loop like some other drivers. */ -static int virtio_data_present(struct hwrng *rng, int wait) +static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) { - if (data_left >= sizeof(u32)) - return 1; -again: + if (!busy) { + busy = true; + init_completion(&have_data); + register_buffer(buf, size); + } + if (!wait) return 0; wait_for_completion(&have_data); - /* Not enough? Re-register. */ - if (unlikely(data_left < sizeof(u32))) { - register_buffer(); - goto again; - } + busy = false; - return 1; + return data_avail; } -/* virtio_data_present() must have succeeded before this is called. */ -static int virtio_data_read(struct hwrng *rng, u32 *data) +static void virtio_cleanup(struct hwrng *rng) { - BUG_ON(data_left < sizeof(u32)); - data_left -= sizeof(u32); - *data = random_data[data_left / 4]; - - if (data_left < sizeof(u32)) { - init_completion(&have_data); - register_buffer(); - } - return sizeof(*data); + if (busy) + wait_for_completion(&have_data); } + static struct hwrng virtio_hwrng = { - .name = "virtio", - .data_present = virtio_data_present, - .data_read = virtio_data_read, + .name = "virtio", + .cleanup = virtio_cleanup, + .read = virtio_read, }; static int virtrng_probe(struct virtio_device *vdev) @@ -112,7 +99,6 @@ static int virtrng_probe(struct virtio_device *vdev) return err; } - register_buffer(); return 0; } @@ -138,21 +124,11 @@ static struct virtio_driver virtio_rng = { static int __init init(void) { - int err; - - random_data = kmalloc(RANDOM_DATA_SIZE, GFP_KERNEL); - if (!random_data) - return -ENOMEM; - - err = register_virtio_driver(&virtio_rng); - if (err) - kfree(random_data); - return err; + return register_virtio_driver(&virtio_rng); } static void __exit fini(void) { - kfree(random_data); unregister_virtio_driver(&virtio_rng); } module_init(init); diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 2e66b5f773dd..0dec5da000ef 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -660,26 +660,23 @@ static struct ipmi_smi_watcher smi_watcher = { #include <linux/sysctl.h> static ctl_table ipmi_table[] = { - { .ctl_name = DEV_IPMI_POWEROFF_POWERCYCLE, - .procname = "poweroff_powercycle", + { .procname = "poweroff_powercycle", .data = &poweroff_powercycle, .maxlen = sizeof(poweroff_powercycle), .mode = 0644, - .proc_handler = &proc_dointvec }, + .proc_handler = proc_dointvec }, { } }; static ctl_table ipmi_dir_table[] = { - { .ctl_name = DEV_IPMI, - .procname = "ipmi", + { .procname = "ipmi", .mode = 0555, .child = ipmi_table }, { } }; static ctl_table ipmi_root_table[] = { - { .ctl_name = CTL_DEV, - .procname = "dev", + { .procname = "dev", .mode = 0555, .child = ipmi_dir_table }, { } diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 950837cf9e9c..5619007e7e05 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -46,8 +46,6 @@ extern void ctrl_alt_del(void); -#define to_handle_h(n) container_of(n, struct input_handle, h_node) - /* * Exported functions/variables */ @@ -132,6 +130,7 @@ int shift_state = 0; */ static struct input_handler kbd_handler; +static DEFINE_SPINLOCK(kbd_event_lock); static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ static int dead_key_next; @@ -190,78 +189,85 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier); * etc.). So this means that scancodes for the extra function keys won't * be valid for the first event device, but will be for the second. */ + +struct getset_keycode_data { + unsigned int scancode; + unsigned int keycode; + int error; +}; + +static int getkeycode_helper(struct input_handle *handle, void *data) +{ + struct getset_keycode_data *d = data; + + d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode); + + return d->error == 0; /* stop as soon as we successfully get one */ +} + int getkeycode(unsigned int scancode) { - struct input_handle *handle; - int keycode; - int error = -ENODEV; + struct getset_keycode_data d = { scancode, 0, -ENODEV }; - list_for_each_entry(handle, &kbd_handler.h_list, h_node) { - error = input_get_keycode(handle->dev, scancode, &keycode); - if (!error) - return keycode; - } + input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper); - return error; + return d.error ?: d.keycode; +} + +static int setkeycode_helper(struct input_handle *handle, void *data) +{ + struct getset_keycode_data *d = data; + + d->error = input_set_keycode(handle->dev, d->scancode, d->keycode); + + return d->error == 0; /* stop as soon as we successfully set one */ } int setkeycode(unsigned int scancode, unsigned int keycode) { - struct input_handle *handle; - int error = -ENODEV; + struct getset_keycode_data d = { scancode, keycode, -ENODEV }; - list_for_each_entry(handle, &kbd_handler.h_list, h_node) { - error = input_set_keycode(handle->dev, scancode, keycode); - if (!error) - break; - } + input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper); - return error; + return d.error; } /* * Making beeps and bells. */ -static void kd_nosound(unsigned long ignored) + +static int kd_sound_helper(struct input_handle *handle, void *data) { - struct input_handle *handle; + unsigned int *hz = data; + struct input_dev *dev = handle->dev; - list_for_each_entry(handle, &kbd_handler.h_list, h_node) { - if (test_bit(EV_SND, handle->dev->evbit)) { - if (test_bit(SND_TONE, handle->dev->sndbit)) - input_inject_event(handle, EV_SND, SND_TONE, 0); - if (test_bit(SND_BELL, handle->dev->sndbit)) - input_inject_event(handle, EV_SND, SND_BELL, 0); - } + if (test_bit(EV_SND, dev->evbit)) { + if (test_bit(SND_TONE, dev->sndbit)) + input_inject_event(handle, EV_SND, SND_TONE, *hz); + if (test_bit(SND_BELL, handle->dev->sndbit)) + input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0); } + + return 0; +} + +static void kd_nosound(unsigned long ignored) +{ + static unsigned int zero; + + input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper); } static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0); void kd_mksound(unsigned int hz, unsigned int ticks) { - struct list_head *node; + del_timer_sync(&kd_mksound_timer); - del_timer(&kd_mksound_timer); + input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper); - if (hz) { - list_for_each_prev(node, &kbd_handler.h_list) { - struct input_handle *handle = to_handle_h(node); - if (test_bit(EV_SND, handle->dev->evbit)) { - if (test_bit(SND_TONE, handle->dev->sndbit)) { - input_inject_event(handle, EV_SND, SND_TONE, hz); - break; - } - if (test_bit(SND_BELL, handle->dev->sndbit)) { - input_inject_event(handle, EV_SND, SND_BELL, 1); - break; - } - } - } - if (ticks) - mod_timer(&kd_mksound_timer, jiffies + ticks); - } else - kd_nosound(0); + if (hz && ticks) + mod_timer(&kd_mksound_timer, jiffies + ticks); } EXPORT_SYMBOL(kd_mksound); @@ -269,27 +275,34 @@ EXPORT_SYMBOL(kd_mksound); * Setting the keyboard rate. */ -int kbd_rate(struct kbd_repeat *rep) +static int kbd_rate_helper(struct input_handle *handle, void *data) { - struct list_head *node; - unsigned int d = 0; - unsigned int p = 0; - - list_for_each(node, &kbd_handler.h_list) { - struct input_handle *handle = to_handle_h(node); - struct input_dev *dev = handle->dev; - - if (test_bit(EV_REP, dev->evbit)) { - if (rep->delay > 0) - input_inject_event(handle, EV_REP, REP_DELAY, rep->delay); - if (rep->period > 0) - input_inject_event(handle, EV_REP, REP_PERIOD, rep->period); - d = dev->rep[REP_DELAY]; - p = dev->rep[REP_PERIOD]; - } + struct input_dev *dev = handle->dev; + struct kbd_repeat *rep = data; + + if (test_bit(EV_REP, dev->evbit)) { + + if (rep[0].delay > 0) + input_inject_event(handle, + EV_REP, REP_DELAY, rep[0].delay); + if (rep[0].period > 0) + input_inject_event(handle, + EV_REP, REP_PERIOD, rep[0].period); + + rep[1].delay = dev->rep[REP_DELAY]; + rep[1].period = dev->rep[REP_PERIOD]; } - rep->delay = d; - rep->period = p; + + return 0; +} + +int kbd_rate(struct kbd_repeat *rep) +{ + struct kbd_repeat data[2] = { *rep }; + + input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper); + *rep = data[1]; /* Copy currently used settings */ + return 0; } @@ -997,36 +1010,36 @@ static inline unsigned char getleds(void) return leds; } +static int kbd_update_leds_helper(struct input_handle *handle, void *data) +{ + unsigned char leds = *(unsigned char *)data; + + if (test_bit(EV_LED, handle->dev->evbit)) { + input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); + input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); + input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); + } + + return 0; +} + /* - * This routine is the bottom half of the keyboard interrupt - * routine, and runs with all interrupts enabled. It does - * console changing, led setting and copy_to_cooked, which can - * take a reasonably long time. - * - * Aside from timing (which isn't really that important for - * keyboard interrupts as they happen often), using the software - * interrupt routines for this thing allows us to easily mask - * this when we don't want any of the above to happen. - * This allows for easy and efficient race-condition prevention - * for kbd_start => input_inject_event(dev, EV_LED, ...) => ... + * This is the tasklet that updates LED state on all keyboards + * attached to the box. The reason we use tasklet is that we + * need to handle the scenario when keyboard handler is not + * registered yet but we already getting updates form VT to + * update led state. */ - static void kbd_bh(unsigned long dummy) { - struct list_head *node; unsigned char leds = getleds(); if (leds != ledstate) { - list_for_each(node, &kbd_handler.h_list) { - struct input_handle *handle = to_handle_h(node); - input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); - input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_inject_event(handle, EV_SYN, SYN_REPORT, 0); - } + input_handler_for_each_handle(&kbd_handler, &leds, + kbd_update_leds_helper); + ledstate = leds; } - - ledstate = leds; } DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); @@ -1136,7 +1149,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char u static void kbd_rawcode(unsigned char data) { struct vc_data *vc = vc_cons[fg_console].d; - kbd = kbd_table + fg_console; + kbd = kbd_table + vc->vc_num; if (kbd->kbdmode == VC_RAW) put_queue(vc, data); } @@ -1157,7 +1170,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) tty->driver_data = vc; } - kbd = kbd_table + fg_console; + kbd = kbd_table + vc->vc_num; if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT) sysrq_alt = down ? keycode : 0; @@ -1296,10 +1309,16 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) static void kbd_event(struct input_handle *handle, unsigned int event_type, unsigned int event_code, int value) { + /* We are called with interrupts disabled, just take the lock */ + spin_lock(&kbd_event_lock); + if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev)) kbd_rawcode(value); if (event_type == EV_KEY) kbd_keycode(event_code, value, HW_RAW(handle->dev)); + + spin_unlock(&kbd_event_lock); + tasklet_schedule(&keyboard_tasklet); do_poke_blanked_console = 1; schedule_console_callback(); @@ -1363,15 +1382,11 @@ static void kbd_disconnect(struct input_handle *handle) */ static void kbd_start(struct input_handle *handle) { - unsigned char leds = ledstate; - tasklet_disable(&keyboard_tasklet); - if (leds != 0xff) { - input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); - input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_inject_event(handle, EV_SYN, SYN_REPORT, 0); - } + + if (ledstate != 0xff) + kbd_update_leds_helper(handle, &ledstate); + tasklet_enable(&keyboard_tasklet); } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index a074fceb67d3..30eff80fed6f 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -5,7 +5,7 @@ * * Added devfs support. * Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu> - * Shared /dev/zero mmaping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com> + * Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com> */ #include <linux/mm.h> @@ -26,7 +26,6 @@ #include <linux/bootmem.h> #include <linux/splice.h> #include <linux/pfn.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -892,29 +891,23 @@ static int memory_open(struct inode *inode, struct file *filp) { int minor; const struct memdev *dev; - int ret = -ENXIO; - - lock_kernel(); minor = iminor(inode); if (minor >= ARRAY_SIZE(devlist)) - goto out; + return -ENXIO; dev = &devlist[minor]; if (!dev->fops) - goto out; + return -ENXIO; filp->f_op = dev->fops; if (dev->dev_info) filp->f_mapping->backing_dev_info = dev->dev_info; if (dev->fops->open) - ret = dev->fops->open(inode, filp); - else - ret = 0; -out: - unlock_kernel(); - return ret; + return dev->fops->open(inode, filp); + + return 0; } static const struct file_operations memory_fops = { diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 07fa612a58d5..96f1cd086dd2 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -49,7 +49,6 @@ #include <linux/device.h> #include <linux/tty.h> #include <linux/kmod.h> -#include <linux/smp_lock.h> /* * Head entry for the doubly linked miscdevice list @@ -118,8 +117,7 @@ static int misc_open(struct inode * inode, struct file * file) struct miscdevice *c; int err = -ENODEV; const struct file_operations *old_fops, *new_fops = NULL; - - lock_kernel(); + mutex_lock(&misc_mtx); list_for_each_entry(c, &misc_list, list) { @@ -157,7 +155,6 @@ static int misc_open(struct inode * inode, struct file * file) fops_put(old_fops); fail: mutex_unlock(&misc_mtx); - unlock_kernel(); return err; } diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 1997270bb6f4..ecb89d798e35 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -248,7 +248,7 @@ static const struct vm_operations_struct mspec_vm_ops = { /* * mspec_mmap * - * Called when mmaping the device. Initializes the vma with a fault handler + * Called when mmapping the device. Initializes the vma with a fault handler * and private data structure necessary to allocate, track, and free the * underlying pages. */ diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index 6934025a1ac1..c1d8b54c816d 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -602,7 +602,7 @@ static void receive_char(struct r3964_info *pInfo, const unsigned char c) } break; case R3964_WAIT_FOR_RX_REPEAT: - /* FALLTROUGH */ + /* FALLTHROUGH */ case R3964_IDLE: if (c == STX) { /* Prevent rx_queue from overflow: */ diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 88cee4099be9..4008e2ce73c1 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -38,7 +38,6 @@ #define NVRAM_VERSION "1.3" #include <linux/module.h> -#include <linux/smp_lock.h> #include <linux/nvram.h> #define PC 1 @@ -111,6 +110,7 @@ #include <linux/spinlock.h> #include <linux/io.h> #include <linux/uaccess.h> +#include <linux/smp_lock.h> #include <asm/system.h> @@ -214,7 +214,6 @@ void nvram_set_checksum(void) static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) { - lock_kernel(); switch (origin) { case 0: /* nothing to do */ @@ -226,7 +225,7 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) offset += NVRAM_BYTES; break; } - unlock_kernel(); + return (offset >= 0) ? (file->f_pos = offset) : -EINVAL; } diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c index 3f7da8cf3a80..8ecbcc174c15 100644 --- a/drivers/char/pc8736x_gpio.c +++ b/drivers/char/pc8736x_gpio.c @@ -20,7 +20,6 @@ #include <linux/mutex.h> #include <linux/nsc_gpio.h> #include <linux/platform_device.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #define DEVNAME "pc8736x_gpio" @@ -223,7 +222,6 @@ static int pc8736x_gpio_open(struct inode *inode, struct file *file) unsigned m = iminor(inode); file->private_data = &pc8736x_gpio_ops; - cycle_kernel_lock(); dev_dbg(&pdev->dev, "open %d\n", m); if (m >= PC8736X_GPIO_CT) diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 62f282e67638..d86c0bc05c1c 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -431,30 +431,25 @@ static struct cdev ptmx_cdev; static struct ctl_table pty_table[] = { { - .ctl_name = PTY_MAX, .procname = "max", .maxlen = sizeof(int), .mode = 0644, .data = &pty_limit, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &pty_limit_min, .extra2 = &pty_limit_max, }, { - .ctl_name = PTY_NR, .procname = "nr", .maxlen = sizeof(int), .mode = 0444, .data = &pty_count, - .proc_handler = &proc_dointvec, - }, { - .ctl_name = 0 - } + .proc_handler = proc_dointvec, + }, + {} }; static struct ctl_table pty_kern_table[] = { { - .ctl_name = KERN_PTY, .procname = "pty", .mode = 0555, .child = pty_table, @@ -464,7 +459,6 @@ static struct ctl_table pty_kern_table[] = { static struct ctl_table pty_root_table[] = { { - .ctl_name = CTL_KERN, .procname = "kernel", .mode = 0555, .child = pty_kern_table, diff --git a/drivers/char/random.c b/drivers/char/random.c index 04b505e5a5e2..dcd08635cf1b 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1257,94 +1257,54 @@ static int proc_do_uuid(ctl_table *table, int write, return proc_dostring(&fake_table, write, buffer, lenp, ppos); } -static int uuid_strategy(ctl_table *table, - void __user *oldval, size_t __user *oldlenp, - void __user *newval, size_t newlen) -{ - unsigned char tmp_uuid[16], *uuid; - unsigned int len; - - if (!oldval || !oldlenp) - return 1; - - uuid = table->data; - if (!uuid) { - uuid = tmp_uuid; - uuid[8] = 0; - } - if (uuid[8] == 0) - generate_random_uuid(uuid); - - if (get_user(len, oldlenp)) - return -EFAULT; - if (len) { - if (len > 16) - len = 16; - if (copy_to_user(oldval, uuid, len) || - put_user(len, oldlenp)) - return -EFAULT; - } - return 1; -} - static int sysctl_poolsize = INPUT_POOL_WORDS * 32; ctl_table random_table[] = { { - .ctl_name = RANDOM_POOLSIZE, .procname = "poolsize", .data = &sysctl_poolsize, .maxlen = sizeof(int), .mode = 0444, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, { - .ctl_name = RANDOM_ENTROPY_COUNT, .procname = "entropy_avail", .maxlen = sizeof(int), .mode = 0444, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, .data = &input_pool.entropy_count, }, { - .ctl_name = RANDOM_READ_THRESH, .procname = "read_wakeup_threshold", .data = &random_read_wakeup_thresh, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &min_read_thresh, .extra2 = &max_read_thresh, }, { - .ctl_name = RANDOM_WRITE_THRESH, .procname = "write_wakeup_threshold", .data = &random_write_wakeup_thresh, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &min_write_thresh, .extra2 = &max_write_thresh, }, { - .ctl_name = RANDOM_BOOT_ID, .procname = "boot_id", .data = &sysctl_bootid, .maxlen = 16, .mode = 0444, - .proc_handler = &proc_do_uuid, - .strategy = &uuid_strategy, + .proc_handler = proc_do_uuid, }, { - .ctl_name = RANDOM_UUID, .procname = "uuid", .maxlen = 16, .mode = 0444, - .proc_handler = &proc_do_uuid, - .strategy = &uuid_strategy, + .proc_handler = proc_do_uuid, }, - { .ctl_name = 0 } + { } }; #endif /* CONFIG_SYSCTL */ diff --git a/drivers/char/rio/route.h b/drivers/char/rio/route.h index 20ed73f3fd7b..46e963771c30 100644 --- a/drivers/char/rio/route.h +++ b/drivers/char/rio/route.h @@ -67,7 +67,7 @@ typedef struct COST_ROUTE COST_ROUTE; struct COST_ROUTE { unsigned char cost; /* Cost down this link */ - unsigned char route[NODE_BYTES]; /* Nodes thorough this route */ + unsigned char route[NODE_BYTES]; /* Nodes through this route */ }; typedef struct ROUTE_STR ROUTE_STR; diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index bc4ab3e54550..95acb8c880f4 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -282,34 +282,31 @@ static irqreturn_t rtc_interrupt(int irq, void *dev_id) */ static ctl_table rtc_table[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "max-user-freq", .data = &rtc_max_user_freq, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, - { .ctl_name = 0 } + { } }; static ctl_table rtc_root[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "rtc", .mode = 0555, .child = rtc_table, }, - { .ctl_name = 0 } + { } }; static ctl_table dev_root[] = { { - .ctl_name = CTL_DEV, .procname = "dev", .mode = 0555, .child = rtc_root, }, - { .ctl_name = 0 } + { } }; static struct ctl_table_header *sysctl_header; diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c index 1d9100561c8a..99e5272e3c53 100644 --- a/drivers/char/scx200_gpio.c +++ b/drivers/char/scx200_gpio.c @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -52,7 +51,6 @@ static int scx200_gpio_open(struct inode *inode, struct file *file) unsigned m = iminor(inode); file->private_data = &scx200_gpio_ops; - cycle_kernel_lock(); if (m >= MAX_PINS) return -EINVAL; return nonseekable_open(inode, file); diff --git a/drivers/char/tb0219.c b/drivers/char/tb0219.c index b3ec9b10e292..cad4eb65f13d 100644 --- a/drivers/char/tb0219.c +++ b/drivers/char/tb0219.c @@ -21,7 +21,6 @@ #include <linux/fs.h> #include <linux/init.h> #include <linux/module.h> -#include <linux/smp_lock.h> #include <asm/io.h> #include <asm/reboot.h> @@ -38,7 +37,7 @@ MODULE_PARM_DESC(major, "Major device number"); static void (*old_machine_restart)(char *command); static void __iomem *tb0219_base; -static spinlock_t tb0219_lock; +static DEFINE_SPINLOCK(tb0219_lock); #define tb0219_read(offset) readw(tb0219_base + (offset)) #define tb0219_write(offset, value) writew((value), tb0219_base + (offset)) @@ -237,7 +236,6 @@ static int tanbac_tb0219_open(struct inode *inode, struct file *file) { unsigned int minor; - cycle_kernel_lock(); minor = iminor(inode); switch (minor) { case 0: @@ -306,8 +304,6 @@ static int __devinit tb0219_probe(struct platform_device *dev) return retval; } - spin_lock_init(&tb0219_lock); - old_machine_restart = _machine_restart; _machine_restart = tb0219_restart; diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 8e67d5c642a4..6bd5f8866c74 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -315,7 +315,7 @@ EXPORT_SYMBOL(tty_termios_input_baud_rate); * For maximal back compatibility with legacy SYS5/POSIX *nix behaviour * we need to carefully set the bits when the user does not get the * desired speed. We allow small margins and preserve as much of possible - * of the input intent to keep compatiblity. + * of the input intent to keep compatibility. * * Locking: Caller should hold termios lock. This is already held * when calling this function from the driver termios handler. diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 0c80c68cd047..1e3d728dbf7e 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -161,6 +161,8 @@ static void set_palette(struct vc_data *vc); static int printable; /* Is console ready for printing? */ int default_utf8 = true; module_param(default_utf8, int, S_IRUGO | S_IWUSR); +int global_cursor_default = -1; +module_param(global_cursor_default, int, S_IRUGO | S_IWUSR); /* * ignore_poke: don't unblank the screen when things are typed. This is @@ -775,6 +777,12 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ vc_cons[currcons].d = NULL; return -ENOMEM; } + + /* If no drivers have overridden us and the user didn't pass a + boot option, default to displaying the cursor */ + if (global_cursor_default == -1) + global_cursor_default = 1; + vc_init(vc, vc->vc_rows, vc->vc_cols, 1); vcs_make_sysfs(currcons); atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m); @@ -1616,7 +1624,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear) vc->vc_decscnm = 0; vc->vc_decom = 0; vc->vc_decawm = 1; - vc->vc_deccm = 1; + vc->vc_deccm = global_cursor_default; vc->vc_decim = 0; set_kbd(vc, decarm); @@ -4078,6 +4086,7 @@ EXPORT_SYMBOL(fg_console); EXPORT_SYMBOL(console_blank_hook); EXPORT_SYMBOL(console_blanked); EXPORT_SYMBOL(vc_cons); +EXPORT_SYMBOL(global_cursor_default); #ifndef VT_SINGLE_DRIVER EXPORT_SYMBOL(take_over_console); EXPORT_SYMBOL(give_up_console); diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c index 70b59642a708..724c164d31c9 100644 --- a/drivers/cpuidle/governor.c +++ b/drivers/cpuidle/governor.c @@ -21,7 +21,7 @@ struct cpuidle_governor *cpuidle_curr_governor; * __cpuidle_find_governor - finds a governor of the specified name * @str: the name * - * Must be called with cpuidle_lock aquired. + * Must be called with cpuidle_lock acquired. */ static struct cpuidle_governor * __cpuidle_find_governor(const char *str) { @@ -39,7 +39,7 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str) * @gov: the new target governor * * NOTE: "gov" can be NULL to specify disabled - * Must be called with cpuidle_lock aquired. + * Must be called with cpuidle_lock acquired. */ int cpuidle_switch_governor(struct cpuidle_governor *gov) { diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c index 5f753fc08730..09ad9154d86c 100644 --- a/drivers/crypto/hifn_795x.c +++ b/drivers/crypto/hifn_795x.c @@ -863,7 +863,7 @@ static int hifn_init_pubrng(struct hifn_device *dev) dev->dmareg |= HIFN_DMAIER_PUBDONE; hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg); - dprintk("Chip %s: Public key engine has been sucessfully " + dprintk("Chip %s: Public key engine has been successfully " "initialised.\n", dev->name); } diff --git a/drivers/dio/dio-driver.c b/drivers/dio/dio-driver.c index 9c0c9afcd0ac..a7b174ef4c85 100644 --- a/drivers/dio/dio-driver.c +++ b/drivers/dio/dio-driver.c @@ -140,5 +140,4 @@ postcore_initcall(dio_driver_init); EXPORT_SYMBOL(dio_match_device); EXPORT_SYMBOL(dio_register_driver); EXPORT_SYMBOL(dio_unregister_driver); -EXPORT_SYMBOL(dio_dev_driver); EXPORT_SYMBOL(dio_bus_type); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index b401dadad4a8..eb140ff38c27 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -54,7 +54,7 @@ config DW_DMAC config AT_HDMAC tristate "Atmel AHB DMA support" - depends on ARCH_AT91SAM9RL + depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 select DMA_ENGINE help Support the Atmel AHB DMA controller. This can be integrated in diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 7585c4164bd5..c52ac9efd0bf 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -99,7 +99,7 @@ static struct at_desc *atc_alloc_descriptor(struct dma_chan *chan, } /** - * atc_desc_get - get a unsused descriptor from free_list + * atc_desc_get - get an unused descriptor from free_list * @atchan: channel we want a new descriptor for */ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index e4864e894e4f..7083bcc1b9c7 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -38,15 +38,14 @@ #include "core.h" -int fw_compute_block_crc(u32 *block) +int fw_compute_block_crc(__be32 *block) { - __be32 be32_block[256]; - int i, length; + int length; + u16 crc; - length = (*block >> 16) & 0xff; - for (i = 0; i < length; i++) - be32_block[i] = cpu_to_be32(block[i + 1]); - *block |= crc_itu_t(0, (u8 *) be32_block, length * 4); + length = (be32_to_cpu(block[0]) >> 16) & 0xff; + crc = crc_itu_t(0, (u8 *)&block[1], length * 4); + *block |= cpu_to_be32(crc); return length; } @@ -57,6 +56,8 @@ static LIST_HEAD(card_list); static LIST_HEAD(descriptor_list); static int descriptor_count; +static __be32 tmp_config_rom[256]; + #define BIB_CRC(v) ((v) << 0) #define BIB_CRC_LENGTH(v) ((v) << 16) #define BIB_INFO_LENGTH(v) ((v) << 24) @@ -72,11 +73,10 @@ static int descriptor_count; #define BIB_CMC ((1) << 30) #define BIB_IMC ((1) << 31) -static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length) +static size_t generate_config_rom(struct fw_card *card, __be32 *config_rom) { struct fw_descriptor *desc; - static u32 config_rom[256]; - int i, j, length; + int i, j, k, length; /* * Initialize contents of config rom buffer. On the OHCI @@ -87,40 +87,39 @@ static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length) * the version stored in the OHCI registers. */ - memset(config_rom, 0, sizeof(config_rom)); - config_rom[0] = BIB_CRC_LENGTH(4) | BIB_INFO_LENGTH(4) | BIB_CRC(0); - config_rom[1] = 0x31333934; - - config_rom[2] = + config_rom[0] = cpu_to_be32( + BIB_CRC_LENGTH(4) | BIB_INFO_LENGTH(4) | BIB_CRC(0)); + config_rom[1] = cpu_to_be32(0x31333934); + config_rom[2] = cpu_to_be32( BIB_LINK_SPEED(card->link_speed) | BIB_GENERATION(card->config_rom_generation++ % 14 + 2) | BIB_MAX_ROM(2) | BIB_MAX_RECEIVE(card->max_receive) | - BIB_BMC | BIB_ISC | BIB_CMC | BIB_IMC; - config_rom[3] = card->guid >> 32; - config_rom[4] = card->guid; + BIB_BMC | BIB_ISC | BIB_CMC | BIB_IMC); + config_rom[3] = cpu_to_be32(card->guid >> 32); + config_rom[4] = cpu_to_be32(card->guid); /* Generate root directory. */ - i = 5; - config_rom[i++] = 0; - config_rom[i++] = 0x0c0083c0; /* node capabilities */ - j = i + descriptor_count; + config_rom[6] = cpu_to_be32(0x0c0083c0); /* node capabilities */ + i = 7; + j = 7 + descriptor_count; /* Generate root directory entries for descriptors. */ list_for_each_entry (desc, &descriptor_list, link) { if (desc->immediate > 0) - config_rom[i++] = desc->immediate; - config_rom[i] = desc->key | (j - i); + config_rom[i++] = cpu_to_be32(desc->immediate); + config_rom[i] = cpu_to_be32(desc->key | (j - i)); i++; j += desc->length; } /* Update root directory length. */ - config_rom[5] = (i - 5 - 1) << 16; + config_rom[5] = cpu_to_be32((i - 5 - 1) << 16); /* End of root directory, now copy in descriptors. */ list_for_each_entry (desc, &descriptor_list, link) { - memcpy(&config_rom[i], desc->data, desc->length * 4); + for (k = 0; k < desc->length; k++) + config_rom[i + k] = cpu_to_be32(desc->data[k]); i += desc->length; } @@ -131,20 +130,17 @@ static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length) for (i = 0; i < j; i += length + 1) length = fw_compute_block_crc(config_rom + i); - *config_rom_length = j; - - return config_rom; + return j; } static void update_config_roms(void) { struct fw_card *card; - u32 *config_rom; size_t length; list_for_each_entry (card, &card_list, link) { - config_rom = generate_config_rom(card, &length); - card->driver->set_config_rom(card, config_rom, length); + length = generate_config_rom(card, tmp_config_rom); + card->driver->set_config_rom(card, tmp_config_rom, length); } } @@ -211,11 +207,8 @@ static const char gap_count_table[] = { void fw_schedule_bm_work(struct fw_card *card, unsigned long delay) { - int scheduled; - fw_card_get(card); - scheduled = schedule_delayed_work(&card->work, delay); - if (!scheduled) + if (!schedule_delayed_work(&card->work, delay)) fw_card_put(card); } @@ -435,7 +428,6 @@ EXPORT_SYMBOL(fw_card_initialize); int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid) { - u32 *config_rom; size_t length; int ret; @@ -445,8 +437,8 @@ int fw_card_add(struct fw_card *card, mutex_lock(&card_mutex); - config_rom = generate_config_rom(card, &length); - ret = card->driver->enable(card, config_rom, length); + length = generate_config_rom(card, tmp_config_rom); + ret = card->driver->enable(card, tmp_config_rom, length); if (ret == 0) list_add_tail(&card->link, &card_list); @@ -465,7 +457,8 @@ EXPORT_SYMBOL(fw_card_add); * shutdown still need to be provided by the card driver. */ -static int dummy_enable(struct fw_card *card, u32 *config_rom, size_t length) +static int dummy_enable(struct fw_card *card, + const __be32 *config_rom, size_t length) { BUG(); return -1; @@ -478,7 +471,7 @@ static int dummy_update_phy_reg(struct fw_card *card, int address, } static int dummy_set_config_rom(struct fw_card *card, - u32 *config_rom, size_t length) + const __be32 *config_rom, size_t length) { /* * We take the card out of card_list before setting the dummy diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 5089331544ed..231e6ee5ba43 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -130,9 +130,22 @@ struct iso_resource { struct iso_resource_event *e_alloc, *e_dealloc; }; -static void schedule_iso_resource(struct iso_resource *); static void release_iso_resource(struct client *, struct client_resource *); +static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) +{ + client_get(r->client); + if (!schedule_delayed_work(&r->work, delay)) + client_put(r->client); +} + +static void schedule_if_iso_resource(struct client_resource *resource) +{ + if (resource->release == release_iso_resource) + schedule_iso_resource(container_of(resource, + struct iso_resource, resource), 0); +} + /* * dequeue_event() just kfree()'s the event, so the event has to be * the first field in a struct XYZ_event. @@ -166,7 +179,7 @@ struct iso_interrupt_event { struct iso_resource_event { struct event event; - struct fw_cdev_event_iso_resource resource; + struct fw_cdev_event_iso_resource iso_resource; }; static inline void __user *u64_to_uptr(__u64 value) @@ -314,11 +327,8 @@ static void for_each_client(struct fw_device *device, static int schedule_reallocations(int id, void *p, void *data) { - struct client_resource *r = p; + schedule_if_iso_resource(p); - if (r->release == release_iso_resource) - schedule_iso_resource(container_of(r, - struct iso_resource, resource)); return 0; } @@ -414,9 +424,7 @@ static int add_client_resource(struct client *client, &resource->handle); if (ret >= 0) { client_get(client); - if (resource->release == release_iso_resource) - schedule_iso_resource(container_of(resource, - struct iso_resource, resource)); + schedule_if_iso_resource(resource); } spin_unlock_irqrestore(&client->lock, flags); @@ -428,26 +436,26 @@ static int add_client_resource(struct client *client, static int release_client_resource(struct client *client, u32 handle, client_resource_release_fn_t release, - struct client_resource **resource) + struct client_resource **return_resource) { - struct client_resource *r; + struct client_resource *resource; spin_lock_irq(&client->lock); if (client->in_shutdown) - r = NULL; + resource = NULL; else - r = idr_find(&client->resource_idr, handle); - if (r && r->release == release) + resource = idr_find(&client->resource_idr, handle); + if (resource && resource->release == release) idr_remove(&client->resource_idr, handle); spin_unlock_irq(&client->lock); - if (!(r && r->release == release)) + if (!(resource && resource->release == release)) return -EINVAL; - if (resource) - *resource = r; + if (return_resource) + *return_resource = resource; else - r->release(client, r); + resource->release(client, resource); client_put(client); @@ -699,6 +707,7 @@ static int ioctl_send_response(struct client *client, void *buffer) struct fw_cdev_send_response *request = buffer; struct client_resource *resource; struct inbound_transaction_resource *r; + int ret = 0; if (release_client_resource(client, request->handle, release_request, &resource) < 0) @@ -708,13 +717,17 @@ static int ioctl_send_response(struct client *client, void *buffer) resource); if (request->length < r->length) r->length = request->length; - if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) - return -EFAULT; + + if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) { + ret = -EFAULT; + goto out; + } fw_send_response(client->device->card, r->request, request->rcode); + out: kfree(r); - return 0; + return ret; } static int ioctl_initiate_bus_reset(struct client *client, void *buffer) @@ -1028,8 +1041,7 @@ static void iso_resource_work(struct work_struct *work) /* Allow 1000ms grace period for other reallocations. */ if (todo == ISO_RES_ALLOC && time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { - if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3))) - client_get(client); + schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); skip = true; } else { /* We could be called twice within the same generation. */ @@ -1097,12 +1109,12 @@ static void iso_resource_work(struct work_struct *work) e = r->e_dealloc; r->e_dealloc = NULL; } - e->resource.handle = r->resource.handle; - e->resource.channel = channel; - e->resource.bandwidth = bandwidth; + e->iso_resource.handle = r->resource.handle; + e->iso_resource.channel = channel; + e->iso_resource.bandwidth = bandwidth; queue_event(client, &e->event, - &e->resource, sizeof(e->resource), NULL, 0); + &e->iso_resource, sizeof(e->iso_resource), NULL, 0); if (free) { cancel_delayed_work(&r->work); @@ -1114,13 +1126,6 @@ static void iso_resource_work(struct work_struct *work) client_put(client); } -static void schedule_iso_resource(struct iso_resource *r) -{ - client_get(r->client); - if (!schedule_delayed_work(&r->work, 0)) - client_put(r->client); -} - static void release_iso_resource(struct client *client, struct client_resource *resource) { @@ -1129,7 +1134,7 @@ static void release_iso_resource(struct client *client, spin_lock_irq(&client->lock); r->todo = ISO_RES_DEALLOC; - schedule_iso_resource(r); + schedule_iso_resource(r, 0); spin_unlock_irq(&client->lock); } @@ -1162,10 +1167,10 @@ static int init_iso_resource(struct client *client, r->e_alloc = e1; r->e_dealloc = e2; - e1->resource.closure = request->closure; - e1->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; - e2->resource.closure = request->closure; - e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; + e1->iso_resource.closure = request->closure; + e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; + e2->iso_resource.closure = request->closure; + e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; if (todo == ISO_RES_ALLOC) { r->resource.release = release_iso_resource; @@ -1175,7 +1180,7 @@ static int init_iso_resource(struct client *client, } else { r->resource.release = NULL; r->resource.handle = -1; - schedule_iso_resource(r); + schedule_iso_resource(r, 0); } request->handle = r->resource.handle; @@ -1295,7 +1300,23 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { static int dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg) { - char buffer[256]; + char buffer[sizeof(union { + struct fw_cdev_get_info _00; + struct fw_cdev_send_request _01; + struct fw_cdev_allocate _02; + struct fw_cdev_deallocate _03; + struct fw_cdev_send_response _04; + struct fw_cdev_initiate_bus_reset _05; + struct fw_cdev_add_descriptor _06; + struct fw_cdev_remove_descriptor _07; + struct fw_cdev_create_iso_context _08; + struct fw_cdev_queue_iso _09; + struct fw_cdev_start_iso _0a; + struct fw_cdev_stop_iso _0b; + struct fw_cdev_get_cycle_timer _0c; + struct fw_cdev_allocate_iso_resource _0d; + struct fw_cdev_send_stream_packet _13; + })]; int ret; if (_IOC_TYPE(cmd) != '#' || @@ -1390,10 +1411,10 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) static int shutdown_resource(int id, void *p, void *data) { - struct client_resource *r = p; + struct client_resource *resource = p; struct client *client = data; - r->release(client, r); + resource->release(client, resource); client_put(client); return 0; @@ -1402,7 +1423,7 @@ static int shutdown_resource(int id, void *p, void *data) static int fw_device_op_release(struct inode *inode, struct file *file) { struct client *client = file->private_data; - struct event *e, *next_e; + struct event *event, *next_event; mutex_lock(&client->device->client_list_mutex); list_del(&client->link); @@ -1423,8 +1444,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file) idr_remove_all(&client->resource_idr); idr_destroy(&client->resource_idr); - list_for_each_entry_safe(e, next_e, &client->event_list, link) - kfree(e); + list_for_each_entry_safe(event, next_event, &client->event_list, link) + kfree(event); client_put(client); diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index fddf2b358936..93ec64cdeef7 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -28,9 +28,9 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/string.h> #include <asm/atomic.h> +#include <asm/byteorder.h> #include <asm/system.h> #include "core.h" @@ -183,7 +183,7 @@ static inline struct fw_node *fw_node(struct list_head *l) * This function builds the tree representation of the topology given * by the self IDs from the latest bus reset. During the construction * of the tree, the function checks that the self IDs are valid and - * internally consistent. On succcess this function returns the + * internally consistent. On success this function returns the * fw_node corresponding to the local card otherwise NULL. */ static struct fw_node *build_tree(struct fw_card *card, @@ -510,13 +510,16 @@ static void update_tree(struct fw_card *card, struct fw_node *root) static void update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count) { - int node_count; + int node_count = (card->root_node->node_id & 0x3f) + 1; + __be32 *map = card->topology_map; + + *map++ = cpu_to_be32((self_id_count + 2) << 16); + *map++ = cpu_to_be32(be32_to_cpu(card->topology_map[1]) + 1); + *map++ = cpu_to_be32((node_count << 16) | self_id_count); + + while (self_id_count--) + *map++ = cpu_to_be32p(self_ids++); - card->topology_map[1]++; - node_count = (card->root_node->node_id & 0x3f) + 1; - card->topology_map[2] = (node_count << 16) | self_id_count; - card->topology_map[0] = (self_id_count + 2) << 16; - memcpy(&card->topology_map[3], self_ids, self_id_count * 4); fw_compute_block_crc(card->topology_map); } diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index da628c72a462..842739df23e2 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -218,12 +218,15 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, packet->header_length = 16; packet->payload_length = 0; break; + + default: + WARN(1, KERN_ERR "wrong tcode %d", tcode); } common: packet->speed = speed; packet->generation = generation; packet->ack = 0; - packet->payload_bus = 0; + packet->payload_mapped = false; } /** @@ -595,11 +598,10 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, break; default: - BUG(); - return; + WARN(1, KERN_ERR "wrong tcode %d", tcode); } - response->payload_bus = 0; + response->payload_mapped = false; } EXPORT_SYMBOL(fw_fill_response); @@ -810,8 +812,7 @@ static void handle_topology_map(struct fw_card *card, struct fw_request *request int speed, unsigned long long offset, void *payload, size_t length, void *callback_data) { - int i, start, end; - __be32 *map; + int start; if (!TCODE_IS_READ_REQUEST(tcode)) { fw_send_response(card, request, RCODE_TYPE_ERROR); @@ -824,11 +825,7 @@ static void handle_topology_map(struct fw_card *card, struct fw_request *request } start = (offset - topology_map_region.start) / 4; - end = start + length / 4; - map = payload; - - for (i = 0; i < length / 4; i++) - map[i] = cpu_to_be32(card->topology_map[start + i]); + memcpy(payload, &card->topology_map[start], length); fw_send_response(card, request, RCODE_COMPLETE); } diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 7ff6e7585152..ed3b1a765c00 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -40,7 +40,8 @@ struct fw_card_driver { * enable the PHY or set the link_on bit and initiate a bus * reset. */ - int (*enable)(struct fw_card *card, u32 *config_rom, size_t length); + int (*enable)(struct fw_card *card, + const __be32 *config_rom, size_t length); int (*update_phy_reg)(struct fw_card *card, int address, int clear_bits, int set_bits); @@ -48,10 +49,10 @@ struct fw_card_driver { /* * Update the config rom for an enabled card. This function * should change the config rom that is presented on the bus - * an initiate a bus reset. + * and initiate a bus reset. */ int (*set_config_rom)(struct fw_card *card, - u32 *config_rom, size_t length); + const __be32 *config_rom, size_t length); void (*send_request)(struct fw_card *card, struct fw_packet *packet); void (*send_response)(struct fw_card *card, struct fw_packet *packet); @@ -93,7 +94,7 @@ int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid); void fw_core_remove_card(struct fw_card *card); int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset); -int fw_compute_block_crc(u32 *block); +int fw_compute_block_crc(__be32 *block); void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); static inline struct fw_card *fw_card_get(struct fw_card *card) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 94260aa76aa3..ae4556f0c0c1 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -205,7 +205,7 @@ struct fw_ohci { dma_addr_t config_rom_bus; __be32 *next_config_rom; dma_addr_t next_config_rom_bus; - u32 next_header; + __be32 next_header; struct ar_context ar_request_ctx; struct ar_context ar_response_ctx; @@ -997,7 +997,8 @@ static int at_context_queue_packet(struct context *ctx, packet->ack = RCODE_SEND_ERROR; return -1; } - packet->payload_bus = payload_bus; + packet->payload_bus = payload_bus; + packet->payload_mapped = true; d[2].req_count = cpu_to_le16(packet->payload_length); d[2].data_address = cpu_to_le32(payload_bus); @@ -1025,7 +1026,7 @@ static int at_context_queue_packet(struct context *ctx, */ if (ohci->generation != packet->generation || reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) { - if (packet->payload_length > 0) + if (packet->payload_mapped) dma_unmap_single(ohci->card.device, payload_bus, packet->payload_length, DMA_TO_DEVICE); packet->ack = RCODE_GENERATION; @@ -1061,7 +1062,7 @@ static int handle_at_packet(struct context *context, /* This packet was cancelled, just continue. */ return 1; - if (packet->payload_bus) + if (packet->payload_mapped) dma_unmap_single(ohci->card.device, packet->payload_bus, packet->payload_length, DMA_TO_DEVICE); @@ -1357,8 +1358,9 @@ static void bus_reset_tasklet(unsigned long data) */ reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(ohci->config_rom[2])); - ohci->config_rom[0] = cpu_to_be32(ohci->next_header); - reg_write(ohci, OHCI1394_ConfigROMhdr, ohci->next_header); + ohci->config_rom[0] = ohci->next_header; + reg_write(ohci, OHCI1394_ConfigROMhdr, + be32_to_cpu(ohci->next_header)); } #ifdef CONFIG_FIREWIRE_OHCI_REMOTE_DMA @@ -1477,7 +1479,17 @@ static int software_reset(struct fw_ohci *ohci) return -EBUSY; } -static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) +static void copy_config_rom(__be32 *dest, const __be32 *src, size_t length) +{ + size_t size = length * 4; + + memcpy(dest, src, size); + if (size < CONFIG_ROM_SIZE) + memset(&dest[length], 0, CONFIG_ROM_SIZE - size); +} + +static int ohci_enable(struct fw_card *card, + const __be32 *config_rom, size_t length) { struct fw_ohci *ohci = fw_ohci(card); struct pci_dev *dev = to_pci_dev(card->device); @@ -1579,8 +1591,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) if (ohci->next_config_rom == NULL) return -ENOMEM; - memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE); - fw_memcpy_to_be32(ohci->next_config_rom, config_rom, length * 4); + copy_config_rom(ohci->next_config_rom, config_rom, length); } else { /* * In the suspend case, config_rom is NULL, which @@ -1590,7 +1601,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) ohci->next_config_rom_bus = ohci->config_rom_bus; } - ohci->next_header = be32_to_cpu(ohci->next_config_rom[0]); + ohci->next_header = ohci->next_config_rom[0]; ohci->next_config_rom[0] = 0; reg_write(ohci, OHCI1394_ConfigROMhdr, 0); reg_write(ohci, OHCI1394_BusOptions, @@ -1624,7 +1635,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) } static int ohci_set_config_rom(struct fw_card *card, - u32 *config_rom, size_t length) + const __be32 *config_rom, size_t length) { struct fw_ohci *ohci; unsigned long flags; @@ -1673,9 +1684,7 @@ static int ohci_set_config_rom(struct fw_card *card, ohci->next_config_rom = next_config_rom; ohci->next_config_rom_bus = next_config_rom_bus; - memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE); - fw_memcpy_to_be32(ohci->next_config_rom, config_rom, - length * 4); + copy_config_rom(ohci->next_config_rom, config_rom, length); ohci->next_header = config_rom[0]; ohci->next_config_rom[0] = 0; @@ -1729,7 +1738,7 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) if (packet->ack != 0) goto out; - if (packet->payload_bus) + if (packet->payload_mapped) dma_unmap_single(ohci->card.device, packet->payload_bus, packet->payload_length, DMA_TO_DEVICE); diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 98dbbda3ad41..d485cdd8cbac 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -820,20 +820,25 @@ static void sbp2_release_target(struct kref *kref) fw_device_put(device); } -static struct workqueue_struct *sbp2_wq; +static void sbp2_target_get(struct sbp2_target *tgt) +{ + kref_get(&tgt->kref); +} static void sbp2_target_put(struct sbp2_target *tgt) { kref_put(&tgt->kref, sbp2_release_target); } +static struct workqueue_struct *sbp2_wq; + /* * Always get the target's kref when scheduling work on one its units. * Each workqueue job is responsible to call sbp2_target_put() upon return. */ static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay) { - kref_get(&lu->tgt->kref); + sbp2_target_get(lu->tgt); if (!queue_delayed_work(sbp2_wq, &lu->work, delay)) sbp2_target_put(lu->tgt); } diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 938100f14b16..3a2ccb09e2f8 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -429,7 +429,7 @@ static bool dmi_matches(const struct dmi_system_id *dmi) for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) { int s = dmi->matches[i].slot; if (s == DMI_NONE) - continue; + break; if (dmi_ident[s] && strstr(dmi_ident[s], dmi->matches[i].substr)) continue; @@ -440,6 +440,15 @@ static bool dmi_matches(const struct dmi_system_id *dmi) } /** + * dmi_is_end_of_table - check for end-of-table marker + * @dmi: pointer to the dmi_system_id structure to check + */ +static bool dmi_is_end_of_table(const struct dmi_system_id *dmi) +{ + return dmi->matches[0].slot == DMI_NONE; +} + +/** * dmi_check_system - check system DMI data * @list: array of dmi_system_id structures to match against * All non-null elements of the list must match @@ -457,7 +466,7 @@ int dmi_check_system(const struct dmi_system_id *list) int count = 0; const struct dmi_system_id *d; - for (d = list; d->ident; d++) + for (d = list; !dmi_is_end_of_table(d); d++) if (dmi_matches(d)) { count++; if (d->callback && d->callback(d)) @@ -484,7 +493,7 @@ const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list) { const struct dmi_system_id *d; - for (d = list; d->ident; d++) + for (d = list; !dmi_is_end_of_table(d); d++) if (dmi_matches(d)) return d; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 5cae0b3eee9b..3f7c500b2115 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -272,7 +272,7 @@ EXPORT_SYMBOL(drm_mode_object_find); * functions & device file and adds it to the master fd list. * * RETURNS: - * Zero on success, error code on falure. + * Zero on success, error code on failure. */ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs) @@ -2328,7 +2328,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, } else if (connector->funcs->set_property) ret = connector->funcs->set_property(connector, property, out_resp->value); - /* store the property value if succesful */ + /* store the property value if successful */ if (!ret) drm_connector_property_set_value(connector, property, out_resp->value); out: diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index abfc27b0c2ea..a2a3fa599923 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1309,7 +1309,7 @@ out_free_list: * i915_gem_release_mmap - remove physical page mappings * @obj: obj in question * - * Preserve the reservation of the mmaping with the DRM core code, but + * Preserve the reservation of the mmapping with the DRM core code, but * relinquish ownership of the pages back to the system. * * It is vital that we remove the page mapping if we have mapped a tiled diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 2b0fe54cd92c..40fcf6fdef38 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -70,7 +70,7 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = { /** - * Curretly it is assumed that the old framebuffer is reused. + * Currently it is assumed that the old framebuffer is reused. * * LOCKING * caller should hold the mode config lock. diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 05598ae10c4b..eb365021bb5a 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -899,7 +899,7 @@ static int intel_lid_present(void) acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - check_lid_device, &lid_present, NULL); + check_lid_device, NULL, &lid_present, NULL); return lid_present; } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 083bec2e50f9..e7fa3279e2f8 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2726,7 +2726,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) /* Wrap with our custom algo which switches to DDC mode */ intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo; - /* In defaut case sdvo lvds is false */ + /* In default case sdvo lvds is false */ intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps); if (intel_sdvo_output_setup(intel_output, diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index c11ddddfb3b6..6643afc36cea 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -1141,7 +1141,7 @@ typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS { /* ucTableFormatRevision=1,ucTableContentRevision=2 */ typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 { USHORT usPixelClock; /* in 10KHz; for bios convenient */ - UCHAR ucMisc; /* see PANEL_ENCODER_MISC_xx defintions below */ + UCHAR ucMisc; /* see PANEL_ENCODER_MISC_xx definitions below */ UCHAR ucAction; /* 0: turn off encoder */ /* 1: setup and turn on encoder */ UCHAR ucTruncate; /* bit0=0: Disable truncate */ @@ -1424,7 +1424,7 @@ typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO { /* Structures used in FirmwareInfoTable */ /****************************************************************************/ -/* usBIOSCapability Defintion: */ +/* usBIOSCapability Definition: */ /* Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; */ /* Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; */ /* Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; */ @@ -2386,7 +2386,7 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 { } ATOM_ANALOG_TV_INFO_V1_2; /**************************************************************************/ -/* VRAM usage and their defintions */ +/* VRAM usage and their definitions */ /* One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. */ /* Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. */ @@ -3046,7 +3046,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO { #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC 2 #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3 -/* Byte aligned defintion for BIOS usage */ +/* Byte aligned definition for BIOS usage */ #define ATOM_S0_CRT1_MONOb0 0x01 #define ATOM_S0_CRT1_COLORb0 0x02 #define ATOM_S0_CRT1_MASKb0 (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0) @@ -3131,7 +3131,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO { #define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30 #define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK 0xC0000000L -/* Byte aligned defintion for BIOS usage */ +/* Byte aligned definition for BIOS usage */ #define ATOM_S2_TV1_STANDARD_MASKb0 0x0F #define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF #define ATOM_S2_CRT1_DPMS_STATEb2 0x01 @@ -3190,7 +3190,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO { #define ATOM_S3_ALLOW_FAST_PWR_SWITCH 0x40000000L #define ATOM_S3_RQST_GPU_USE_MIN_PWR 0x80000000L -/* Byte aligned defintion for BIOS usage */ +/* Byte aligned definition for BIOS usage */ #define ATOM_S3_CRT1_ACTIVEb0 0x01 #define ATOM_S3_LCD1_ACTIVEb0 0x02 #define ATOM_S3_TV1_ACTIVEb0 0x04 @@ -3230,7 +3230,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO { #define ATOM_S4_LCD1_REFRESH_MASK 0x0000FF00L #define ATOM_S4_LCD1_REFRESH_SHIFT 8 -/* Byte aligned defintion for BIOS usage */ +/* Byte aligned definition for BIOS usage */ #define ATOM_S4_LCD1_PANEL_ID_MASKb0 0x0FF #define ATOM_S4_LCD1_REFRESH_MASKb1 ATOM_S4_LCD1_PANEL_ID_MASKb0 #define ATOM_S4_VRAM_INFO_MASKb2 ATOM_S4_LCD1_PANEL_ID_MASKb0 @@ -3310,7 +3310,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO { #define ATOM_S6_VRI_BRIGHTNESS_CHANGE 0x40000000L #define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK 0x80000000L -/* Byte aligned defintion for BIOS usage */ +/* Byte aligned definition for BIOS usage */ #define ATOM_S6_DEVICE_CHANGEb0 0x01 #define ATOM_S6_SCALER_CHANGEb0 0x02 #define ATOM_S6_LID_CHANGEb0 0x04 diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 278f646bc18e..6740ed24358f 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -394,11 +394,11 @@ int r600_mc_init(struct radeon_device *rdev) * AGP so that GPU can catch out of VRAM/AGP access */ if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) { - /* Enought place before */ + /* Enough place before */ rdev->mc.vram_location = rdev->mc.gtt_location - rdev->mc.mc_vram_size; } else if (tmp > rdev->mc.mc_vram_size) { - /* Enought place after */ + /* Enough place after */ rdev->mc.vram_location = rdev->mc.gtt_location + rdev->mc.gtt_size; } else { diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index b38c4c8e2c61..d10eb43645c8 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -59,7 +59,7 @@ static struct fb_ops radeonfb_ops = { }; /** - * Curretly it is assumed that the old framebuffer is reused. + * Currently it is assumed that the old framebuffer is reused. * * LOCKING * caller should hold the mode config lock. diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c index 38537d971a3e..067167cb39ca 100644 --- a/drivers/gpu/drm/radeon/radeon_state.c +++ b/drivers/gpu/drm/radeon/radeon_state.c @@ -1950,7 +1950,7 @@ static void radeon_apply_surface_regs(int surf_index, * Note that refcount can be at most 2, since during a free refcount=3 * might mean we have to allocate a new surface which might not always * be available. - * For example : we allocate three contigous surfaces ABC. If B is + * For example : we allocate three contiguous surfaces ABC. If B is * freed, we suddenly need two surfaces to store A and C, which might * not always be available. */ diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 1381e06d6af3..eda4ade24c3a 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -378,7 +378,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo, new_mem->mem_type == TTM_PL_SYSTEM) || (old_mem->mem_type == TTM_PL_SYSTEM && new_mem->mem_type == TTM_PL_TT)) { - /* bind is enought */ + /* bind is enough */ radeon_move_null(bo, new_mem); return 0; } diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index b0efd0ddae7a..5e06ee7076f5 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -829,11 +829,11 @@ int rv770_mc_init(struct radeon_device *rdev) * AGP so that GPU can catch out of VRAM/AGP access */ if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) { - /* Enought place before */ + /* Enough place before */ rdev->mc.vram_location = rdev->mc.gtt_location - rdev->mc.mc_vram_size; } else if (tmp > rdev->mc.mc_vram_size) { - /* Enought place after */ + /* Enough place after */ rdev->mc.vram_location = rdev->mc.gtt_location + rdev->mc.gtt_size; } else { diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index c70927ecda21..61c5572d2b91 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -427,7 +427,7 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, /* * We need to use vmap to get the desired page protection - * or to make the buffer object look contigous. + * or to make the buffer object look contiguous. */ prot = (mem->placement & TTM_PL_FLAG_CACHED) ? PAGE_KERNEL : diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7d05c4bb201e..80792d38d25c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -39,8 +39,6 @@ * Version Information */ -#define DRIVER_VERSION "v2.6" -#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina" #define DRIVER_DESC "HID core driver" #define DRIVER_LICENSE "GPL" @@ -1294,6 +1292,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, @@ -1326,6 +1325,8 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, @@ -1620,6 +1621,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_KYE, 0x0058) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, @@ -1918,5 +1920,8 @@ static void __exit hid_exit(void) module_init(hid_init); module_exit(hid_exit); +MODULE_AUTHOR("Andreas Gal"); +MODULE_AUTHOR("Vojtech Pavlik"); +MODULE_AUTHOR("Jiri Kosina"); MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 62e9cb10e88c..998b6f443d7d 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -126,6 +126,8 @@ static const struct hid_device_id cp_devices[] = { .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2), .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3), + .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE), .driver_data = CP_2WHEEL_MOUSE_HACK }, { } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index adbef5d069c4..3839340e293a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -117,6 +117,7 @@ #define USB_DEVICE_ID_CH_PRO_PEDALS 0x00f2 #define USB_DEVICE_ID_CH_COMBATSTICK 0x00f4 #define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff +#define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK 0x00d3 #define USB_VENDOR_ID_CHERRY 0x046a #define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 @@ -145,6 +146,7 @@ #define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 #define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 #define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 +#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1 #define USB_VENDOR_ID_DEALEXTREAME 0x10c5 #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a @@ -304,6 +306,8 @@ #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 +#define USB_DEVICE_ID_SPACETRAVELLER 0xc623 +#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 #define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 #define USB_DEVICE_ID_DINOVO_EDGE 0xc714 #define USB_DEVICE_ID_DINOVO_MINI 0xc71f @@ -346,6 +350,9 @@ #define USB_VENDOR_ID_NEC 0x073e #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 +#define USB_VENDOR_ID_NEXTWINDOW 0x1926 +#define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003 + #define USB_VENDOR_ID_NTRIG 0x1b96 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001 diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 0f870a3243ed..9fcd3d017ab3 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -33,6 +33,7 @@ #define LG_NOGET 0x100 #define LG_FF 0x200 #define LG_FF2 0x400 +#define LG_RDESC_REL_ABS 0x800 /* * Certain Logitech keyboards send in report #3 keys which are far @@ -51,6 +52,13 @@ static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, rdesc[84] = rdesc[89] = 0x4d; rdesc[85] = rdesc[90] = 0x10; } + if ((quirks & LG_RDESC_REL_ABS) && rsize >= 50 && + rdesc[32] == 0x81 && rdesc[33] == 0x06 && + rdesc[49] == 0x81 && rdesc[50] == 0x06) { + dev_info(&hdev->dev, "fixing up rel/abs in Logitech " + "report descriptor\n"); + rdesc[33] = rdesc[50] = 0x02; + } } #define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ @@ -89,6 +97,22 @@ static int lg_ultrax_remote_mapping(struct hid_input *hi, return 1; } +static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) + return 0; + + switch (usage->hid & HID_USAGE) { + + case 0x00d: lg_map_key_clear(KEY_MEDIA); break; + default: + return 0; + + } + return 1; +} + static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage, unsigned long **bit, int *max) { @@ -164,6 +188,10 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, lg_ultrax_remote_mapping(hi, usage, bit, max)) return 1; + if (hdev->product == USB_DEVICE_ID_DINOVO_MINI && + lg_dinovo_mapping(hi, usage, bit, max)) + return 1; + if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) return 1; @@ -303,8 +331,13 @@ static const struct hid_device_id lg_devices[] = { .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), .driver_data = LG_FF2 }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR), + .driver_data = LG_RDESC_REL_ABS }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER), + .driver_data = LG_RDESC_REL_ABS }, { } }; + MODULE_DEVICE_TABLE(hid, lg_devices); static struct hid_driver lg_driver = { diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 03bd703255a3..0258289f3b3e 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -41,8 +41,6 @@ * Version Information */ -#define DRIVER_VERSION "v2.6" -#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina" #define DRIVER_DESC "USB HID core driver" #define DRIVER_LICENSE "GPL" @@ -998,7 +996,8 @@ static int usbhid_start(struct hid_device *hid) usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma; usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - usbhid_init_reports(hid); + if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS)) + usbhid_init_reports(hid); set_bit(HID_STARTED, &usbhid->iofl); @@ -1395,8 +1394,7 @@ static int __init hid_init(void) retval = usb_register(&hid_driver); if (retval) goto usb_register_fail; - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); return 0; usb_register_fail: @@ -1423,6 +1421,8 @@ static void __exit hid_exit(void) module_init(hid_init); module_exit(hid_exit); -MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_AUTHOR("Andreas Gal"); +MODULE_AUTHOR("Vojtech Pavlik"); +MODULE_AUTHOR("Jiri Kosina"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 484e3eec2f88..e565dbe91d97 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -1181,12 +1181,11 @@ static void pidff_reset(struct pidff_device *pidff) usbhid_wait_io(hid); if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { - int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0]; - while (sim_effects < 2) { + while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) { if (i++ > 20) { printk(KERN_WARNING "hid-pidff: device reports " "%d simultaneous effects\n", - sim_effects); + pidff->pool[PID_SIMULTANEOUS_MAX].value[0]); break; } debug("pid_pool requested again"); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 0d9045aa2c4b..38773dc2821b 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -37,6 +37,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_NATSU, USB_DEVICE_ID_NATSU_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_NEXTWINDOW, USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN, HID_QUIRK_MULTI_INPUT}, { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, @@ -53,6 +54,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, @@ -280,7 +282,7 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) if (idVendor == USB_VENDOR_ID_NCR && idProduct >= USB_DEVICE_ID_NCR_FIRST && idProduct <= USB_DEVICE_ID_NCR_LAST) - return HID_QUIRK_NOGET; + return HID_QUIRK_NO_INIT_REPORTS; down_read(&dquirks_rwsem); bl_entry = usbhid_exists_dquirk(idVendor, idProduct); diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 8b6ee247bfe4..867e08433e4b 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -450,7 +450,6 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); if (!uref_multi) return -ENOMEM; - lock_kernel(); uref = &uref_multi->uref; if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { if (copy_from_user(uref_multi, user_arg, @@ -528,7 +527,6 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, case HIDIOCGCOLLECTIONINDEX: i = field->usage[uref->usage_index].collection_index; - unlock_kernel(); kfree(uref_multi); return i; case HIDIOCGUSAGES: @@ -547,15 +545,12 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, } goodreturn: - unlock_kernel(); kfree(uref_multi); return 0; fault: - unlock_kernel(); kfree(uref_multi); return -EFAULT; inval: - unlock_kernel(); kfree(uref_multi); return -EINVAL; } diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index b342926dd7fc..f843443ba5c3 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -266,7 +266,7 @@ static int usb_kbd_probe(struct usb_interface *iface, le16_to_cpu(dev->descriptor.idProduct)); usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); - strlcpy(kbd->phys, "/input0", sizeof(kbd->phys)); + strlcat(kbd->phys, "/input0", sizeof(kbd->phys)); input_dev->name = kbd->name; input_dev->phys = kbd->phys; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 700e93adeb33..9e640c62ebd9 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -191,21 +191,27 @@ config SENSORS_ADT7470 will be called adt7470. config SENSORS_ADT7473 - tristate "Analog Devices ADT7473" + tristate "Analog Devices ADT7473 (DEPRECATED)" depends on I2C && EXPERIMENTAL + select SENSORS_ADT7475 help If you say yes here you get support for the Analog Devices ADT7473 temperature monitoring chips. + This driver is deprecated, you should use the adt7475 driver + instead. + This driver can also be built as a module. If so, the module will be called adt7473. config SENSORS_ADT7475 - tristate "Analog Devices ADT7475" + tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490" depends on I2C && EXPERIMENTAL + select HWMON_VID help If you say yes here you get support for the Analog Devices - ADT7475 hardware monitoring chips. + ADT7473, ADT7475, ADT7476 and ADT7490 hardware monitoring + chips. This driver can also be build as a module. If so, the module will be called adt7475. @@ -305,12 +311,12 @@ config SENSORS_F71805F will be called f71805f. config SENSORS_F71882FG - tristate "Fintek F71858FG, F71862FG, F71882FG and F8000" + tristate "Fintek F71858FG, F71862FG, F71882FG, F71889FG and F8000" depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring - features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG - and F8000 Super-I/O chips. + features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG, + F71889FG and F8000 Super-I/O chips. This driver can also be built as a module. If so, the module will be called f71882fg. @@ -442,6 +448,15 @@ config SENSORS_LM70 This driver can also be built as a module. If so, the module will be called lm70. +config SENSORS_LM73 + tristate "National Semiconductor LM73" + depends on I2C + help + If you say yes here you get support for National Semiconductor LM73 + sensor chips. + This driver can also be built as a module. If so, the module + will be called lm73. + config SENSORS_LM75 tristate "National Semiconductor LM75 and compatibles" depends on I2C @@ -841,7 +856,7 @@ config SENSORS_W83781D config SENSORS_W83791D tristate "Winbond W83791D" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for the Winbond W83791D chip. @@ -1008,6 +1023,12 @@ config SENSORS_APPLESMC Say Y here if you have an applicable laptop and want to experience the awesome power of applesmc. +config SENSORS_MC13783_ADC + tristate "Freescale MC13783 ADC" + depends on MFD_MC13783 + help + Support for the A/D converter on MC13783 PMIC. + if ACPI comment "ACPI drivers" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 9f46cb019cc6..33c2ee105284 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o +obj-$(CONFIG_SENSORS_LM73) += lm73.o obj-$(CONFIG_SENSORS_LM75) += lm75.o obj-$(CONFIG_SENSORS_LM77) += lm77.o obj-$(CONFIG_SENSORS_LM78) += lm78.o @@ -73,6 +74,7 @@ obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o +obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index afc594318125..33acf29531af 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -288,9 +288,8 @@ static int adm1021_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - int i; - const char *type_name = ""; - int conv_rate, status, config; + const char *type_name; + int conv_rate, status, config, man_id, dev_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { pr_debug("adm1021: detect failed, " @@ -303,62 +302,37 @@ static int adm1021_detect(struct i2c_client *client, int kind, ADM1021_REG_CONV_RATE_R); config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R); - /* Now, we do the remaining detection. */ - if (kind < 0) { - if ((status & 0x03) != 0x00 || (config & 0x3F) != 0x00 - || (conv_rate & 0xF8) != 0x00) { - pr_debug("adm1021: detect failed, " - "chip not detected!\n"); - return -ENODEV; - } + /* Check unused bits */ + if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) { + pr_debug("adm1021: detect failed, chip not detected!\n"); + return -ENODEV; } /* Determine the chip type. */ - if (kind <= 0) { - i = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID); - if (i == 0x41) - if ((i2c_smbus_read_byte_data(client, - ADM1021_REG_DEV_ID) & 0xF0) == 0x30) - kind = adm1023; - else - kind = adm1021; - else if (i == 0x49) - kind = thmc10; - else if (i == 0x23) - kind = gl523sm; - else if ((i == 0x4d) && - (i2c_smbus_read_byte_data(client, - ADM1021_REG_DEV_ID) == 0x01)) - kind = max1617a; - else if (i == 0x54) - kind = mc1066; - /* LM84 Mfr ID in a different place, and it has more unused bits */ - else if (conv_rate == 0x00 - && (kind == 0 /* skip extra detection */ - || ((config & 0x7F) == 0x00 - && (status & 0xAB) == 0x00))) - kind = lm84; - else - kind = max1617; - } + man_id = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID); + dev_id = i2c_smbus_read_byte_data(client, ADM1021_REG_DEV_ID); - if (kind == max1617) { - type_name = "max1617"; - } else if (kind == max1617a) { + if (man_id == 0x4d && dev_id == 0x01) type_name = "max1617a"; - } else if (kind == adm1021) { - type_name = "adm1021"; - } else if (kind == adm1023) { - type_name = "adm1023"; - } else if (kind == thmc10) { + else if (man_id == 0x41) { + if ((dev_id & 0xF0) == 0x30) + type_name = "adm1023"; + else + type_name = "adm1021"; + } else if (man_id == 0x49) type_name = "thmc10"; - } else if (kind == lm84) { - type_name = "lm84"; - } else if (kind == gl523sm) { + else if (man_id == 0x23) type_name = "gl523sm"; - } else if (kind == mc1066) { + else if (man_id == 0x54) type_name = "mc1066"; - } + /* LM84 Mfr ID in a different place, and it has more unused bits */ + else if (conv_rate == 0x00 + && (config & 0x7F) == 0x00 + && (status & 0xAB) == 0x00) + type_name = "lm84"; + else + type_name = "max1617"; + pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n", type_name, i2c_adapter_id(adapter), client->addr); strlcpy(info->type, type_name, I2C_NAME_SIZE); diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index 4db04d603ec9..db6ac2b04f6f 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -2,7 +2,7 @@ * adm1025.c * * Copyright (C) 2000 Chen-Yuan Wu <gwu@esoft.com> - * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> * * The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6 * voltages (including its own power source) and up to two temperatures @@ -413,67 +413,34 @@ static int adm1025_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - const char *name = ""; - u8 config; + const char *name; + u8 man_id, chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - config = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG); - if (kind < 0) { /* detection */ - if ((config & 0x80) != 0x00 - || (i2c_smbus_read_byte_data(client, - ADM1025_REG_STATUS1) & 0xC0) != 0x00 - || (i2c_smbus_read_byte_data(client, - ADM1025_REG_STATUS2) & 0xBC) != 0x00) { - dev_dbg(&adapter->dev, - "ADM1025 detection failed at 0x%02x.\n", - client->addr); - return -ENODEV; - } + /* Check for unused bits */ + if ((i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG) & 0x80) + || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS1) & 0xC0) + || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS2) & 0xBC)) { + dev_dbg(&adapter->dev, "ADM1025 detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u8 man_id, chip_id; - - man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID); - - if (man_id == 0x41) { /* Analog Devices */ - if ((chip_id & 0xF0) == 0x20) { /* ADM1025/ADM1025A */ - kind = adm1025; - } - } else - if (man_id == 0xA1) { /* Philips */ - if (client->addr != 0x2E - && (chip_id & 0xF0) == 0x20) { /* NE1619 */ - kind = ne1619; - } - } - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } - } + /* Identification */ + chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID); + if ((chip_id & 0xF0) != 0x20) + return -ENODEV; - if (kind == adm1025) { + man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID); + if (man_id == 0x41) name = "adm1025"; - } else if (kind == ne1619) { + else if (man_id == 0xA1 && client->addr != 0x2E) name = "ne1619"; - } + else + return -ENODEV; + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index ff7de40b6e35..fb5363985e21 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -1672,35 +1672,26 @@ static int adm1026_detect(struct i2c_client *client, int kind, i2c_adapter_id(client->adapter), client->addr, company, verstep); - /* If auto-detecting, Determine the chip type. */ - if (kind <= 0) { - dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x " - "...\n", i2c_adapter_id(adapter), address); - if (company == ADM1026_COMPANY_ANALOG_DEV - && verstep == ADM1026_VERSTEP_ADM1026) { - kind = adm1026; - } else if (company == ADM1026_COMPANY_ANALOG_DEV - && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized stepping " - "0x%02x. Defaulting to ADM1026.\n", verstep); - kind = adm1026; - } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Found version/stepping " - "0x%02x. Assuming generic ADM1026.\n", - verstep); - kind = any_chip; - } else { - dev_dbg(&adapter->dev, "Autodetection failed\n"); - /* Not an ADM1026 ... */ - if (kind == 0) { /* User used force=x,y */ - dev_err(&adapter->dev, "Generic ADM1026 not " - "found at %d,0x%02x. Try " - "force_adm1026.\n", - i2c_adapter_id(adapter), address); - } - return -ENODEV; - } + /* Determine the chip type. */ + dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x...\n", + i2c_adapter_id(adapter), address); + if (company == ADM1026_COMPANY_ANALOG_DEV + && verstep == ADM1026_VERSTEP_ADM1026) { + /* Analog Devices ADM1026 */ + } else if (company == ADM1026_COMPANY_ANALOG_DEV + && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { + dev_err(&adapter->dev, "Unrecognized stepping " + "0x%02x. Defaulting to ADM1026.\n", verstep); + } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { + dev_err(&adapter->dev, "Found version/stepping " + "0x%02x. Assuming generic ADM1026.\n", + verstep); + } else { + dev_dbg(&adapter->dev, "Autodetection failed\n"); + /* Not an ADM1026... */ + return -ENODEV; } + strlcpy(info->type, "adm1026", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 36718150b475..ef91e2a4a567 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -301,59 +301,36 @@ static int adm1029_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + u8 man_id, chip_id, temp_devices_installed, nb_fan_support; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* Now we do the detection and identification. A negative kind - * means that the driver was loaded with no force parameter - * (default), so we must both detect and identify the chip - * (actually there is only one possible kind of chip for now, adm1029). - * A zero kind means that the driver was loaded with the force - * parameter, the detection step shall be skipped. A positive kind - * means that the driver was loaded with the force parameter and a - * given kind of chip is requested, so both the detection and the - * identification steps are skipped. */ - - /* Default to an adm1029 if forced */ - if (kind == 0) - kind = adm1029; - /* ADM1029 doesn't have CHIP ID, check just MAN ID * For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED, * ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values * documented */ - if (kind <= 0) { /* identification */ - u8 man_id, chip_id, temp_devices_installed, nb_fan_support; - - man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID); - temp_devices_installed = i2c_smbus_read_byte_data(client, + man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID); + temp_devices_installed = i2c_smbus_read_byte_data(client, ADM1029_REG_TEMP_DEVICES_INSTALLED); - nb_fan_support = i2c_smbus_read_byte_data(client, + nb_fan_support = i2c_smbus_read_byte_data(client, ADM1029_REG_NB_FAN_SUPPORT); - /* 0x41 is Analog Devices */ - if (man_id == 0x41 && (temp_devices_installed & 0xf9) == 0x01 - && nb_fan_support == 0x03) { - if ((chip_id & 0xF0) == 0x00) { - kind = adm1029; - } else { - /* There are no "official" CHIP ID, so actually - * we use Major/Minor revision for that */ - printk(KERN_INFO - "adm1029: Unknown major revision %x, " - "please let us know\n", chip_id); - } - } + /* 0x41 is Analog Devices */ + if (man_id != 0x41 || (temp_devices_installed & 0xf9) != 0x01 + || nb_fan_support != 0x03) + return -ENODEV; - if (kind <= 0) { /* identification failed */ - pr_debug("adm1029: Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X)\n", man_id, chip_id); - return -ENODEV; - } + if ((chip_id & 0xF0) != 0x00) { + /* There are no "official" CHIP ID, so actually + * we use Major/Minor revision for that */ + pr_info("adm1029: Unknown major revision %x, " + "please let us know\n", chip_id); + return -ENODEV; } + strlcpy(info->type, "adm1029", I2C_NAME_SIZE); return 0; @@ -432,7 +409,7 @@ static int adm1029_remove(struct i2c_client *client) } /* -function that update the status of the chips (temperature for exemple) +function that update the status of the chips (temperature for example) */ static struct adm1029_data *adm1029_update_device(struct device *dev) { diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 56905955352c..0e722175aae0 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -817,31 +817,19 @@ static int adm1031_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - const char *name = ""; + const char *name; + int id, co; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind < 0) { - int id, co; - id = i2c_smbus_read_byte_data(client, 0x3d); - co = i2c_smbus_read_byte_data(client, 0x3e); + id = i2c_smbus_read_byte_data(client, 0x3d); + co = i2c_smbus_read_byte_data(client, 0x3e); - if (!((id == 0x31 || id == 0x30) && co == 0x41)) - return -ENODEV; - kind = (id == 0x30) ? adm1030 : adm1031; - } - - if (kind <= 0) - kind = adm1031; + if (!((id == 0x31 || id == 0x30) && co == 0x41)) + return -ENODEV; + name = (id == 0x30) ? "adm1030" : "adm1031"; - /* Given the detected chip type, set the chip name and the - * auto fan control helper table. */ - if (kind == adm1030) { - name = "adm1030"; - } else if (kind == adm1031) { - name = "adm1031"; - } strlcpy(info->type, name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 2444b15f2e9d..20e0481cc206 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -556,51 +556,34 @@ static int adm9240_detect(struct i2c_client *new_client, int kind, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind == 0) { - kind = adm9240; - } - - if (kind < 0) { - - /* verify chip: reg address should match i2c address */ - if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) - != address) { - dev_err(&adapter->dev, "detect fail: address match, " - "0x%02x\n", address); - return -ENODEV; - } - - /* check known chip manufacturer */ - man_id = i2c_smbus_read_byte_data(new_client, - ADM9240_REG_MAN_ID); - if (man_id == 0x23) { - kind = adm9240; - } else if (man_id == 0xda) { - kind = ds1780; - } else if (man_id == 0x01) { - kind = lm81; - } else { - dev_err(&adapter->dev, "detect fail: unknown manuf, " - "0x%02x\n", man_id); - return -ENODEV; - } - - /* successful detect, print chip info */ - die_rev = i2c_smbus_read_byte_data(new_client, - ADM9240_REG_DIE_REV); - dev_info(&adapter->dev, "found %s revision %u\n", - man_id == 0x23 ? "ADM9240" : - man_id == 0xda ? "DS1780" : "LM81", die_rev); + /* verify chip: reg address should match i2c address */ + if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) + != address) { + dev_err(&adapter->dev, "detect fail: address match, 0x%02x\n", + address); + return -ENODEV; } - /* either forced or detected chip kind */ - if (kind == adm9240) { + /* check known chip manufacturer */ + man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID); + if (man_id == 0x23) { name = "adm9240"; - } else if (kind == ds1780) { + } else if (man_id == 0xda) { name = "ds1780"; - } else if (kind == lm81) { + } else if (man_id == 0x01) { name = "lm81"; + } else { + dev_err(&adapter->dev, "detect fail: unknown manuf, 0x%02x\n", + man_id); + return -ENODEV; } + + /* successful detect, print chip info */ + die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV); + dev_info(&adapter->dev, "found %s revision %u\n", + man_id == 0x23 ? "ADM9240" : + man_id == 0xda ? "DS1780" : "LM81", die_rev); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 5c39b4af1b23..451977bca7d6 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -191,6 +191,7 @@ static int ads7828_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int ch; /* Check we have a valid client */ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) @@ -202,20 +203,17 @@ static int ads7828_detect(struct i2c_client *client, int kind, - Read from the 8 channel addresses - Check the top 4 bits of each result are not set (12 data bits) */ - if (kind < 0) { - int ch; - for (ch = 0; ch < ADS7828_NCH; ch++) { - u16 in_data; - u8 cmd = channel_cmd_byte(ch); - in_data = ads7828_read_value(client, cmd); - if (in_data & 0xF000) { - printk(KERN_DEBUG - "%s : Doesn't look like an ads7828 device\n", - __func__); - return -ENODEV; - } + for (ch = 0; ch < ADS7828_NCH; ch++) { + u16 in_data; + u8 cmd = channel_cmd_byte(ch); + in_data = ads7828_read_value(client, cmd); + if (in_data & 0xF000) { + pr_debug("%s : Doesn't look like an ads7828 device\n", + __func__); + return -ENODEV; } } + strlcpy(info->type, "ads7828", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 1852f27bac51..f9c9562b6a94 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -1906,27 +1906,22 @@ static int adt7462_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - int vendor, device, revision; - - vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR); - if (vendor != ADT7462_VENDOR) - return -ENODEV; + vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR); + if (vendor != ADT7462_VENDOR) + return -ENODEV; - device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE); - if (device != ADT7462_DEVICE) - return -ENODEV; + device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE); + if (device != ADT7462_DEVICE) + return -ENODEV; - revision = i2c_smbus_read_byte_data(client, - ADT7462_REG_REVISION); - if (revision != ADT7462_REVISION) - return -ENODEV; - } else - dev_dbg(&adapter->dev, "detection forced\n"); + revision = i2c_smbus_read_byte_data(client, ADT7462_REG_REVISION); + if (revision != ADT7462_REVISION) + return -ENODEV; strlcpy(info->type, "adt7462", I2C_NAME_SIZE); diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 633e1a1e9d79..32b1750a6890 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1229,27 +1229,22 @@ static int adt7470_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - int vendor, device, revision; - - vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR); - if (vendor != ADT7470_VENDOR) - return -ENODEV; + vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR); + if (vendor != ADT7470_VENDOR) + return -ENODEV; - device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE); - if (device != ADT7470_DEVICE) - return -ENODEV; + device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE); + if (device != ADT7470_DEVICE) + return -ENODEV; - revision = i2c_smbus_read_byte_data(client, - ADT7470_REG_REVISION); - if (revision != ADT7470_REVISION) - return -ENODEV; - } else - dev_dbg(&adapter->dev, "detection forced\n"); + revision = i2c_smbus_read_byte_data(client, ADT7470_REG_REVISION); + if (revision != ADT7470_REVISION) + return -ENODEV; strlcpy(info->type, "adt7470", I2C_NAME_SIZE); diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c index 0a6ce2367b42..aea244db974e 100644 --- a/drivers/hwmon/adt7473.c +++ b/drivers/hwmon/adt7473.c @@ -174,7 +174,6 @@ static const struct i2c_device_id adt7473_id[] = { { "adt7473", adt7473 }, { } }; -MODULE_DEVICE_TABLE(i2c, adt7473_id); static struct i2c_driver adt7473_driver = { .class = I2C_CLASS_HWMON, @@ -1090,27 +1089,22 @@ static int adt7473_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - int vendor, device, revision; - - vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR); - if (vendor != ADT7473_VENDOR) - return -ENODEV; + vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR); + if (vendor != ADT7473_VENDOR) + return -ENODEV; - device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE); - if (device != ADT7473_DEVICE) - return -ENODEV; + device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE); + if (device != ADT7473_DEVICE) + return -ENODEV; - revision = i2c_smbus_read_byte_data(client, - ADT7473_REG_REVISION); - if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69) - return -ENODEV; - } else - dev_dbg(&adapter->dev, "detection forced\n"); + revision = i2c_smbus_read_byte_data(client, ADT7473_REG_REVISION); + if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69) + return -ENODEV; strlcpy(info->type, "adt7473", I2C_NAME_SIZE); @@ -1171,6 +1165,8 @@ static int adt7473_remove(struct i2c_client *client) static int __init adt7473_init(void) { + pr_notice("The adt7473 driver is deprecated, please use the adt7475 " + "driver instead\n"); return i2c_add_driver(&adt7473_driver); } diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index b5a95193c694..99abfddedbc3 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -3,7 +3,8 @@ * Copyright (C) 2007-2008, Advanced Micro Devices, Inc. * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net> * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com> - + * Copyright (C) 2009 Jean Delvare <khali@linux-fr.org> + * * Derived from the lm83 driver by Jean Delvare * * This program is free software; you can redistribute it and/or modify @@ -17,6 +18,7 @@ #include <linux/i2c.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/hwmon-vid.h> #include <linux/err.h> /* Indexes for the sysfs hooks */ @@ -39,7 +41,12 @@ /* 7475 Common Registers */ -#define REG_VOLTAGE_BASE 0x21 +#define REG_DEVREV2 0x12 /* ADT7490 only */ + +#define REG_VTT 0x1E /* ADT7490 only */ +#define REG_EXTEND3 0x1F /* ADT7490 only */ + +#define REG_VOLTAGE_BASE 0x20 #define REG_TEMP_BASE 0x25 #define REG_TACH_BASE 0x28 #define REG_PWM_BASE 0x30 @@ -47,12 +54,15 @@ #define REG_DEVID 0x3D #define REG_VENDID 0x3E +#define REG_DEVID2 0x3F #define REG_STATUS1 0x41 #define REG_STATUS2 0x42 -#define REG_VOLTAGE_MIN_BASE 0x46 -#define REG_VOLTAGE_MAX_BASE 0x47 +#define REG_VID 0x43 /* ADT7476 only */ + +#define REG_VOLTAGE_MIN_BASE 0x44 +#define REG_VOLTAGE_MAX_BASE 0x45 #define REG_TEMP_MIN_BASE 0x4E #define REG_TEMP_MAX_BASE 0x4F @@ -73,16 +83,39 @@ #define REG_TEMP_OFFSET_BASE 0x70 +#define REG_CONFIG2 0x73 + #define REG_EXTEND1 0x76 #define REG_EXTEND2 0x77 + +#define REG_CONFIG3 0x78 #define REG_CONFIG5 0x7C +#define REG_CONFIG4 0x7D + +#define REG_STATUS4 0x81 /* ADT7490 only */ + +#define REG_VTT_MIN 0x84 /* ADT7490 only */ +#define REG_VTT_MAX 0x86 /* ADT7490 only */ + +#define VID_VIDSEL 0x80 /* ADT7476 only */ + +#define CONFIG2_ATTN 0x20 + +#define CONFIG3_SMBALERT 0x01 +#define CONFIG3_THERM 0x02 + +#define CONFIG4_PINFUNC 0x03 +#define CONFIG4_MAXDUTY 0x08 +#define CONFIG4_ATTN_IN10 0x30 +#define CONFIG4_ATTN_IN43 0xC0 #define CONFIG5_TWOSCOMP 0x01 #define CONFIG5_TEMPOFFSET 0x02 +#define CONFIG5_VIDGPIO 0x10 /* ADT7476 only */ /* ADT7475 Settings */ -#define ADT7475_VOLTAGE_COUNT 2 +#define ADT7475_VOLTAGE_COUNT 5 /* Not counting Vtt */ #define ADT7475_TEMP_COUNT 3 #define ADT7475_TACH_COUNT 4 #define ADT7475_PWM_COUNT 3 @@ -113,12 +146,15 @@ #define TEMP_OFFSET_REG(idx) (REG_TEMP_OFFSET_BASE + (idx)) #define TEMP_TRANGE_REG(idx) (REG_TEMP_TRANGE_BASE + (idx)) -static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END }; +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -I2C_CLIENT_INSMOD_1(adt7475); +I2C_CLIENT_INSMOD_4(adt7473, adt7475, adt7476, adt7490); static const struct i2c_device_id adt7475_id[] = { + { "adt7473", adt7473 }, { "adt7475", adt7475 }, + { "adt7476", adt7476 }, + { "adt7490", adt7490 }, { } }; MODULE_DEVICE_TABLE(i2c, adt7475_id); @@ -131,15 +167,24 @@ struct adt7475_data { unsigned long limits_updated; char valid; + u8 config4; u8 config5; - u16 alarms; - u16 voltage[3][3]; + u8 has_voltage; + u8 bypass_attn; /* Bypass voltage attenuator */ + u8 has_pwm2:1; + u8 has_fan4:1; + u8 has_vid:1; + u32 alarms; + u16 voltage[3][6]; u16 temp[7][3]; u16 tach[2][4]; u8 pwm[4][3]; u8 range[3]; u8 pwmctl[3]; u8 pwmchan[3]; + + u8 vid; + u8 vrm; }; static struct i2c_driver adt7475_driver; @@ -196,26 +241,35 @@ static inline u16 rpm2tach(unsigned long rpm) return SENSORS_LIMIT((90000 * 60) / rpm, 1, 0xFFFF); } -static inline int reg2vcc(u16 reg) -{ - return (4296 * reg) / 1000; -} +/* Scaling factors for voltage inputs, taken from the ADT7490 datasheet */ +static const int adt7473_in_scaling[ADT7475_VOLTAGE_COUNT + 1][2] = { + { 45, 94 }, /* +2.5V */ + { 175, 525 }, /* Vccp */ + { 68, 71 }, /* Vcc */ + { 93, 47 }, /* +5V */ + { 120, 20 }, /* +12V */ + { 45, 45 }, /* Vtt */ +}; -static inline int reg2vccp(u16 reg) +static inline int reg2volt(int channel, u16 reg, u8 bypass_attn) { - return (2929 * reg) / 1000; -} + const int *r = adt7473_in_scaling[channel]; -static inline u16 vcc2reg(long vcc) -{ - vcc = SENSORS_LIMIT(vcc, 0, 4396); - return (vcc * 1000) / 4296; + if (bypass_attn & (1 << channel)) + return DIV_ROUND_CLOSEST(reg * 2250, 1024); + return DIV_ROUND_CLOSEST(reg * (r[0] + r[1]) * 2250, r[1] * 1024); } -static inline u16 vccp2reg(long vcc) +static inline u16 volt2reg(int channel, long volt, u8 bypass_attn) { - vcc = SENSORS_LIMIT(vcc, 0, 2998); - return (vcc * 1000) / 2929; + const int *r = adt7473_in_scaling[channel]; + long reg; + + if (bypass_attn & (1 << channel)) + reg = (volt * 1024) / 2250; + else + reg = (volt * r[1] * 1024) / ((r[0] + r[1]) * 2250); + return SENSORS_LIMIT(reg, 0, 1023) & (0xff << 2); } static u16 adt7475_read_word(struct i2c_client *client, int reg) @@ -271,12 +325,11 @@ static ssize_t show_voltage(struct device *dev, struct device_attribute *attr, switch (sattr->nr) { case ALARM: return sprintf(buf, "%d\n", - (data->alarms >> (sattr->index + 1)) & 1); + (data->alarms >> sattr->index) & 1); default: val = data->voltage[sattr->nr][sattr->index]; return sprintf(buf, "%d\n", - sattr->index == - 0 ? reg2vccp(val) : reg2vcc(val)); + reg2volt(sattr->index, val, data->bypass_attn)); } } @@ -296,12 +349,19 @@ static ssize_t set_voltage(struct device *dev, struct device_attribute *attr, mutex_lock(&data->lock); data->voltage[sattr->nr][sattr->index] = - sattr->index ? vcc2reg(val) : vccp2reg(val); + volt2reg(sattr->index, val, data->bypass_attn); - if (sattr->nr == MIN) - reg = VOLTAGE_MIN_REG(sattr->index); - else - reg = VOLTAGE_MAX_REG(sattr->index); + if (sattr->index < ADT7475_VOLTAGE_COUNT) { + if (sattr->nr == MIN) + reg = VOLTAGE_MIN_REG(sattr->index); + else + reg = VOLTAGE_MAX_REG(sattr->index); + } else { + if (sattr->nr == MIN) + reg = REG_VTT_MIN; + else + reg = REG_VTT_MAX; + } i2c_smbus_write_byte_data(client, reg, data->voltage[sattr->nr][sattr->index] >> 2); @@ -778,18 +838,103 @@ static ssize_t set_pwmfreq(struct device *dev, struct device_attribute *attr, return count; } -static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 0); -static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage, +static ssize_t show_pwm_at_crit(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + return sprintf(buf, "%d\n", !!(data->config4 & CONFIG4_MAXDUTY)); +} + +static ssize_t set_pwm_at_crit(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->lock); + data->config4 = i2c_smbus_read_byte_data(client, REG_CONFIG4); + if (val) + data->config4 |= CONFIG4_MAXDUTY; + else + data->config4 &= ~CONFIG4_MAXDUTY; + i2c_smbus_write_byte_data(client, REG_CONFIG4, data->config4); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_vrm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct adt7475_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", (int)data->vrm); +} + +static ssize_t set_vrm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct adt7475_data *data = dev_get_drvdata(dev); + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + if (val < 0 || val > 255) + return -EINVAL; + data->vrm = val; + + return count; +} + +static ssize_t show_vid(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_voltage, NULL, INPUT, 0); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_voltage, set_voltage, MAX, 0); -static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_voltage, +static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_voltage, set_voltage, MIN, 0); -static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0); -static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 1); -static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage, +static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0); +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 1); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage, set_voltage, MAX, 1); -static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage, +static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_voltage, set_voltage, MIN, 1); -static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1); +static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1); +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 2); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 2); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 2); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 2); +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_voltage, NULL, INPUT, 3); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 3); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 3); +static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_voltage, NULL, ALARM, 3); +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_voltage, NULL, INPUT, 4); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 4); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 4); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_voltage, NULL, ALARM, 8); +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_voltage, NULL, INPUT, 5); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 5); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 5); +static SENSOR_DEVICE_ATTR_2(in5_alarm, S_IRUGO, show_voltage, NULL, ALARM, 31); static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, INPUT, 0); static SENSOR_DEVICE_ATTR_2(temp1_alarm, S_IRUGO, show_temp, NULL, ALARM, 0); static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_temp, NULL, FAULT, 0); @@ -893,6 +1038,13 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm, static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm, MAX, 2); +/* Non-standard name, might need revisiting */ +static DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO, + show_pwm_at_crit, set_pwm_at_crit); + +static DEVICE_ATTR(vrm, S_IWUSR | S_IRUGO, show_vrm, set_vrm); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + static struct attribute *adt7475_attrs[] = { &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_max.dev_attr.attr, @@ -940,60 +1092,156 @@ static struct attribute *adt7475_attrs[] = { &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan3_min.dev_attr.attr, &sensor_dev_attr_fan3_alarm.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan4_min.dev_attr.attr, - &sensor_dev_attr_fan4_alarm.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_freq.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm2_freq.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm3_freq.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &dev_attr_pwm_use_point2_pwm_at_crit.attr, NULL, }; -struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; +static struct attribute *fan4_attrs[] = { + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *pwm2_attrs[] = { + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + NULL +}; + +static struct attribute *in0_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *in3_attrs[] = { + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *in4_attrs[] = { + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *in5_attrs[] = { + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *vid_attrs[] = { + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + NULL +}; + +static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; +static struct attribute_group fan4_attr_group = { .attrs = fan4_attrs }; +static struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs }; +static struct attribute_group in0_attr_group = { .attrs = in0_attrs }; +static struct attribute_group in3_attr_group = { .attrs = in3_attrs }; +static struct attribute_group in4_attr_group = { .attrs = in4_attrs }; +static struct attribute_group in5_attr_group = { .attrs = in5_attrs }; +static struct attribute_group vid_attr_group = { .attrs = vid_attrs }; static int adt7475_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendid, devid, devid2; + const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - if (adt7475_read(REG_VENDID) != 0x41 || - adt7475_read(REG_DEVID) != 0x75) { - dev_err(&adapter->dev, - "Couldn't detect a adt7475 part at 0x%02x\n", - (unsigned int)client->addr); - return -ENODEV; - } + vendid = adt7475_read(REG_VENDID); + devid2 = adt7475_read(REG_DEVID2); + if (vendid != 0x41 || /* Analog Devices */ + (devid2 & 0xf8) != 0x68) + return -ENODEV; + + devid = adt7475_read(REG_DEVID); + if (devid == 0x73) + name = "adt7473"; + else if (devid == 0x75 && client->addr == 0x2e) + name = "adt7475"; + else if (devid == 0x76) + name = "adt7476"; + else if ((devid2 & 0xfc) == 0x6c) + name = "adt7490"; + else { + dev_dbg(&adapter->dev, + "Couldn't detect an ADT7473/75/76/90 part at " + "0x%02x\n", (unsigned int)client->addr); + return -ENODEV; } - strlcpy(info->type, adt7475_id[0].name, I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } +static void adt7475_remove_files(struct i2c_client *client, + struct adt7475_data *data) +{ + sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); + if (data->has_fan4) + sysfs_remove_group(&client->dev.kobj, &fan4_attr_group); + if (data->has_pwm2) + sysfs_remove_group(&client->dev.kobj, &pwm2_attr_group); + if (data->has_voltage & (1 << 0)) + sysfs_remove_group(&client->dev.kobj, &in0_attr_group); + if (data->has_voltage & (1 << 3)) + sysfs_remove_group(&client->dev.kobj, &in3_attr_group); + if (data->has_voltage & (1 << 4)) + sysfs_remove_group(&client->dev.kobj, &in4_attr_group); + if (data->has_voltage & (1 << 5)) + sysfs_remove_group(&client->dev.kobj, &in5_attr_group); + if (data->has_vid) + sysfs_remove_group(&client->dev.kobj, &vid_attr_group); +} + static int adt7475_probe(struct i2c_client *client, const struct i2c_device_id *id) { + static const char *names[] = { + [adt7473] = "ADT7473", + [adt7475] = "ADT7475", + [adt7476] = "ADT7476", + [adt7490] = "ADT7490", + }; + struct adt7475_data *data; - int i, ret = 0; + int i, ret = 0, revision; + u8 config2, config3; data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) @@ -1002,6 +1250,70 @@ static int adt7475_probe(struct i2c_client *client, mutex_init(&data->lock); i2c_set_clientdata(client, data); + /* Initialize device-specific values */ + switch (id->driver_data) { + case adt7476: + data->has_voltage = 0x0e; /* in1 to in3 */ + revision = adt7475_read(REG_DEVID2) & 0x07; + break; + case adt7490: + data->has_voltage = 0x3e; /* in1 to in5 */ + revision = adt7475_read(REG_DEVID2) & 0x03; + if (revision == 0x03) + revision += adt7475_read(REG_DEVREV2); + break; + default: + data->has_voltage = 0x06; /* in1, in2 */ + revision = adt7475_read(REG_DEVID2) & 0x07; + } + + config3 = adt7475_read(REG_CONFIG3); + /* Pin PWM2 may alternatively be used for ALERT output */ + if (!(config3 & CONFIG3_SMBALERT)) + data->has_pwm2 = 1; + /* Meaning of this bit is inverted for the ADT7473-1 */ + if (id->driver_data == adt7473 && revision >= 1) + data->has_pwm2 = !data->has_pwm2; + + data->config4 = adt7475_read(REG_CONFIG4); + /* Pin TACH4 may alternatively be used for THERM */ + if ((data->config4 & CONFIG4_PINFUNC) == 0x0) + data->has_fan4 = 1; + + /* THERM configuration is more complex on the ADT7476 and ADT7490, + because 2 different pins (TACH4 and +2.5 Vin) can be used for + this function */ + if (id->driver_data == adt7490) { + if ((data->config4 & CONFIG4_PINFUNC) == 0x1 && + !(config3 & CONFIG3_THERM)) + data->has_fan4 = 1; + } + if (id->driver_data == adt7476 || id->driver_data == adt7490) { + if (!(config3 & CONFIG3_THERM) || + (data->config4 & CONFIG4_PINFUNC) == 0x1) + data->has_voltage |= (1 << 0); /* in0 */ + } + + /* On the ADT7476, the +12V input pin may instead be used as VID5, + and VID pins may alternatively be used as GPIO */ + if (id->driver_data == adt7476) { + u8 vid = adt7475_read(REG_VID); + if (!(vid & VID_VIDSEL)) + data->has_voltage |= (1 << 4); /* in4 */ + + data->has_vid = !(adt7475_read(REG_CONFIG5) & CONFIG5_VIDGPIO); + } + + /* Voltage attenuators can be bypassed, globally or individually */ + config2 = adt7475_read(REG_CONFIG2); + if (config2 & CONFIG2_ATTN) { + data->bypass_attn = (0x3 << 3) | 0x3; + } else { + data->bypass_attn = ((data->config4 & CONFIG4_ATTN_IN10) >> 4) | + ((data->config4 & CONFIG4_ATTN_IN43) >> 3); + } + data->bypass_attn &= data->has_voltage; + /* Call adt7475_read_pwm for all pwm's as this will reprogram any pwm's which are disabled to manual mode with 0% duty cycle */ for (i = 0; i < ADT7475_PWM_COUNT; i++) @@ -1011,16 +1323,70 @@ static int adt7475_probe(struct i2c_client *client, if (ret) goto efree; + /* Features that can be disabled individually */ + if (data->has_fan4) { + ret = sysfs_create_group(&client->dev.kobj, &fan4_attr_group); + if (ret) + goto eremove; + } + if (data->has_pwm2) { + ret = sysfs_create_group(&client->dev.kobj, &pwm2_attr_group); + if (ret) + goto eremove; + } + if (data->has_voltage & (1 << 0)) { + ret = sysfs_create_group(&client->dev.kobj, &in0_attr_group); + if (ret) + goto eremove; + } + if (data->has_voltage & (1 << 3)) { + ret = sysfs_create_group(&client->dev.kobj, &in3_attr_group); + if (ret) + goto eremove; + } + if (data->has_voltage & (1 << 4)) { + ret = sysfs_create_group(&client->dev.kobj, &in4_attr_group); + if (ret) + goto eremove; + } + if (data->has_voltage & (1 << 5)) { + ret = sysfs_create_group(&client->dev.kobj, &in5_attr_group); + if (ret) + goto eremove; + } + if (data->has_vid) { + data->vrm = vid_which_vrm(); + ret = sysfs_create_group(&client->dev.kobj, &vid_attr_group); + if (ret) + goto eremove; + } + data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); goto eremove; } + dev_info(&client->dev, "%s device, revision %d\n", + names[id->driver_data], revision); + if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2) + dev_info(&client->dev, "Optional features:%s%s%s%s%s\n", + (data->has_voltage & (1 << 0)) ? " in0" : "", + (data->has_voltage & (1 << 4)) ? " in4" : "", + data->has_fan4 ? " fan4" : "", + data->has_pwm2 ? " pwm2" : "", + data->has_vid ? " vid" : ""); + if (data->bypass_attn) + dev_info(&client->dev, "Bypassing attenuators on:%s%s%s%s\n", + (data->bypass_attn & (1 << 0)) ? " in0" : "", + (data->bypass_attn & (1 << 1)) ? " in1" : "", + (data->bypass_attn & (1 << 3)) ? " in3" : "", + (data->bypass_attn & (1 << 4)) ? " in4" : ""); + return 0; eremove: - sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); + adt7475_remove_files(client, data); efree: kfree(data); return ret; @@ -1031,7 +1397,7 @@ static int adt7475_remove(struct i2c_client *client) struct adt7475_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); + adt7475_remove_files(client, data); kfree(data); return 0; @@ -1116,7 +1482,7 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct adt7475_data *data = i2c_get_clientdata(client); - u8 ext; + u16 ext; int i; mutex_lock(&data->lock); @@ -1127,25 +1493,44 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) data->alarms = adt7475_read(REG_STATUS2) << 8; data->alarms |= adt7475_read(REG_STATUS1); - ext = adt7475_read(REG_EXTEND1); - for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) + ext = (adt7475_read(REG_EXTEND2) << 8) | + adt7475_read(REG_EXTEND1); + for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { + if (!(data->has_voltage & (1 << i))) + continue; data->voltage[INPUT][i] = (adt7475_read(VOLTAGE_REG(i)) << 2) | - ((ext >> ((i + 1) * 2)) & 3); + ((ext >> (i * 2)) & 3); + } - ext = adt7475_read(REG_EXTEND2); for (i = 0; i < ADT7475_TEMP_COUNT; i++) data->temp[INPUT][i] = (adt7475_read(TEMP_REG(i)) << 2) | - ((ext >> ((i + 1) * 2)) & 3); + ((ext >> ((i + 5) * 2)) & 3); - for (i = 0; i < ADT7475_TACH_COUNT; i++) + if (data->has_voltage & (1 << 5)) { + data->alarms |= adt7475_read(REG_STATUS4) << 24; + ext = adt7475_read(REG_EXTEND3); + data->voltage[INPUT][5] = adt7475_read(REG_VTT) << 2 | + ((ext >> 4) & 3); + } + + for (i = 0; i < ADT7475_TACH_COUNT; i++) { + if (i == 3 && !data->has_fan4) + continue; data->tach[INPUT][i] = adt7475_read_word(client, TACH_REG(i)); + } /* Updated by hw when in auto mode */ - for (i = 0; i < ADT7475_PWM_COUNT; i++) + for (i = 0; i < ADT7475_PWM_COUNT; i++) { + if (i == 1 && !data->has_pwm2) + continue; data->pwm[INPUT][i] = adt7475_read(PWM_REG(i)); + } + + if (data->has_vid) + data->vid = adt7475_read(REG_VID) & 0x3f; data->measure_updated = jiffies; } @@ -1153,9 +1538,12 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) /* Limits and settings, should never change update every 60 seconds */ if (time_after(jiffies, data->limits_updated + HZ * 60) || !data->valid) { + data->config4 = adt7475_read(REG_CONFIG4); data->config5 = adt7475_read(REG_CONFIG5); for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { + if (!(data->has_voltage & (1 << i))) + continue; /* Adjust values so they match the input precision */ data->voltage[MIN][i] = adt7475_read(VOLTAGE_MIN_REG(i)) << 2; @@ -1163,6 +1551,11 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) adt7475_read(VOLTAGE_MAX_REG(i)) << 2; } + if (data->has_voltage & (1 << 5)) { + data->voltage[MIN][5] = adt7475_read(REG_VTT_MIN) << 2; + data->voltage[MAX][5] = adt7475_read(REG_VTT_MAX) << 2; + } + for (i = 0; i < ADT7475_TEMP_COUNT; i++) { /* Adjust values so they match the input precision */ data->temp[MIN][i] = @@ -1178,11 +1571,16 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) } adt7475_read_hystersis(client); - for (i = 0; i < ADT7475_TACH_COUNT; i++) + for (i = 0; i < ADT7475_TACH_COUNT; i++) { + if (i == 3 && !data->has_fan4) + continue; data->tach[MIN][i] = adt7475_read_word(client, TACH_MIN_REG(i)); + } for (i = 0; i < ADT7475_PWM_COUNT; i++) { + if (i == 1 && !data->has_pwm2) + continue; data->pwm[MAX][i] = adt7475_read(PWM_MAX_REG(i)); data->pwm[MIN][i] = adt7475_read(PWM_MIN_REG(i)); /* Set the channel and control information */ diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 8acf82977e7b..480f80ea1fa0 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -701,6 +701,7 @@ static int asb100_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int val1, val2; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { pr_debug("asb100.o: detect failed, " @@ -708,50 +709,30 @@ static int asb100_detect(struct i2c_client *client, int kind, return -ENODEV; } - /* The chip may be stuck in some other bank than bank 0. This may - make reading other information impossible. Specify a force=... or - force_*=... parameter, and the chip will be reset to the right - bank. */ - if (kind < 0) { - - int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK); - int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); - - /* If we're in bank 0 */ - if ((!(val1 & 0x07)) && - /* Check for ASB100 ID (low byte) */ - (((!(val1 & 0x80)) && (val2 != 0x94)) || - /* Check for ASB100 ID (high byte ) */ - ((val1 & 0x80) && (val2 != 0x06)))) { - pr_debug("asb100.o: detect failed, " - "bad chip id 0x%02x!\n", val2); - return -ENODEV; - } + val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK); + val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); - } /* kind < 0 */ + /* If we're in bank 0 */ + if ((!(val1 & 0x07)) && + /* Check for ASB100 ID (low byte) */ + (((!(val1 & 0x80)) && (val2 != 0x94)) || + /* Check for ASB100 ID (high byte ) */ + ((val1 & 0x80) && (val2 != 0x06)))) { + pr_debug("asb100: detect failed, bad chip id 0x%02x!\n", val2); + return -ENODEV; + } - /* We have either had a force parameter, or we have already detected - Winbond. Put it now into bank 0 and Vendor ID High Byte */ + /* Put it now into bank 0 and Vendor ID High Byte */ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, (i2c_smbus_read_byte_data(client, ASB100_REG_BANK) & 0x78) | 0x80); /* Determine the chip type. */ - if (kind <= 0) { - int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID); - int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); - - if ((val1 == 0x31) && (val2 == 0x06)) - kind = asb100; - else { - if (kind == 0) - dev_warn(&adapter->dev, "ignoring " - "'force' parameter for unknown chip " - "at adapter %d, address 0x%02x.\n", - i2c_adapter_id(adapter), client->addr); - return -ENODEV; - } - } + val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID); + val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); + + if (val1 != 0x31 || val2 != 0x06) + return -ENODEV; strlcpy(info->type, "asb100", I2C_NAME_SIZE); diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 27d62574284f..4377bb0cc526 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2220,33 +2220,23 @@ static int dme1737_i2c_detect(struct i2c_client *client, int kind, return -ENODEV; } - /* A negative kind means that the driver was loaded with no force - * parameter (default), so we must identify the chip. */ - if (kind < 0) { - company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY); - verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP); - - if (company == DME1737_COMPANY_SMSC && - (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) { - kind = dme1737; - } else if (company == DME1737_COMPANY_SMSC && - verstep == SCH5027_VERSTEP) { - kind = sch5027; - } else { - return -ENODEV; - } - } + company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY); + verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP); - if (kind == sch5027) { + if (company == DME1737_COMPANY_SMSC && + verstep == SCH5027_VERSTEP) { name = "sch5027"; - } else { - kind = dme1737; + + } else if (company == DME1737_COMPANY_SMSC && + (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) { name = "dme1737"; + } else { + return -ENODEV; } dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n", - kind == sch5027 ? "SCH5027" : "DME1737", client->addr, - verstep); + verstep == SCH5027_VERSTEP ? "SCH5027" : "DME1737", + client->addr, verstep); strlcpy(info->type, name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 53f88f511816..2a4c6a05b14f 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -237,20 +237,16 @@ static int ds1621_detect(struct i2c_client *client, int kind, return -ENODEV; /* Now, we do the remaining detection. It is lousy. */ - if (kind < 0) { - /* The NVB bit should be low if no EEPROM write has been - requested during the latest 10ms, which is highly - improbable in our case. */ - conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); - if (conf < 0 || conf & DS1621_REG_CONFIG_NVB) + /* The NVB bit should be low if no EEPROM write has been requested + during the latest 10ms, which is highly improbable in our case. */ + conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); + if (conf < 0 || conf & DS1621_REG_CONFIG_NVB) + return -ENODEV; + /* The 7 lowest bits of a temperature should always be 0. */ + for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) { + temp = i2c_smbus_read_word_data(client, DS1621_REG_TEMP[i]); + if (temp < 0 || (temp & 0x7f00)) return -ENODEV; - /* The 7 lowest bits of a temperature should always be 0. */ - for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) { - temp = i2c_smbus_read_word_data(client, - DS1621_REG_TEMP[i]); - if (temp < 0 || (temp & 0x7f00)) - return -ENODEV; - } } strlcpy(info->type, "ds1621", I2C_NAME_SIZE); diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 4146105f1a57..a95fa4256caa 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -48,6 +48,7 @@ #define SIO_F71858_ID 0x0507 /* Chipset ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ +#define SIO_F71889_ID 0x0723 /* Chipset ID */ #define SIO_F8000_ID 0x0581 /* Chipset ID */ #define REGION_LENGTH 8 @@ -95,12 +96,13 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -enum chips { f71858fg, f71862fg, f71882fg, f8000 }; +enum chips { f71858fg, f71862fg, f71882fg, f71889fg, f8000 }; static const char *f71882fg_names[] = { "f71858fg", "f71862fg", "f71882fg", + "f71889fg", "f8000", }; @@ -155,7 +157,7 @@ struct f71882fg_data { u8 pwm_auto_point_hyst[2]; u8 pwm_auto_point_mapping[4]; u8 pwm_auto_point_pwm[4][5]; - u8 pwm_auto_point_temp[4][4]; + s8 pwm_auto_point_temp[4][4]; }; /* Sysfs in */ @@ -258,7 +260,9 @@ static struct platform_driver f71882fg_driver = { static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -/* Temp and in attr for the f71858fg */ +/* Temp and in attr for the f71858fg, the f71858fg is special as it + has its temperature indexes start at 0 (the others start at 1) and + it only has 3 voltage inputs */ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), @@ -302,8 +306,8 @@ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; -/* Temp and in attr common to both the f71862fg and f71882fg */ -static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { +/* Temp and in attr common to the f71862fg, f71882fg and f71889fg */ +static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), @@ -371,8 +375,8 @@ static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), }; -/* Temp and in attr found only on the f71882fg */ -static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { +/* For models with in1 alarm capability */ +static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = { SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 0, 1), SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, @@ -383,6 +387,7 @@ static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { /* Temp and in attr for the f8000 Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max) is used as hysteresis value to clear alarms + Also like the f71858fg its temperature indexes start at 0 */ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), @@ -413,61 +418,70 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { }; /* Fan / PWM attr common to all models */ -static struct sensor_device_attribute_2 fxxxx_fan_attr[] = { +static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { { SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, store_fan_full_speed, 0, 0), SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0), - SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), - SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 1), - SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), - SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), - SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 2), - SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), - SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0), SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, store_pwm_enable, 0, 0), SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 0), - SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 0), - +}, { + SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), + SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 1), + SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, store_pwm_enable, 0, 1), SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 1), - SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 1), - +}, { + SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), + SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 2), + SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, store_pwm_enable, 0, 2), SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 2), - SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 2), -}; +}, { + SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), + SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 3), + SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), + SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3), + SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 3), + SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 3), +} }; -/* Fan / PWM attr for the f71862fg, less pwms and less zones per pwm than the - f71882fg */ -static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { +/* Attr for models which can beep on Fan alarm */ +static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 0), SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 1), SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 2), + SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 3), +}; +/* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the + f71858fg / f71882fg / f71889fg */ +static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 1, 0), @@ -487,6 +501,9 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 1, 1), @@ -506,6 +523,9 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 1, 2), @@ -526,8 +546,11 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 3, 2), }; -/* Fan / PWM attr common to both the f71882fg and f71858fg */ -static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { +/* PWM attr common to the f71858fg, f71882fg and f71889fg */ +static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 0), @@ -565,7 +588,10 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 0), SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), - +}, { + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 1), @@ -603,7 +629,10 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 1), SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), - +}, { + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 2), @@ -641,30 +670,7 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 2), SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), -}; - -/* Fan / PWM attr found on the f71882fg but not on the f71858fg */ -static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { - SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 0), - SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 1), - SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 2), - - SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), - SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 3), - SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 3), - SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), - - SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3), - SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 3), - SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, - show_pwm_interpolate, store_pwm_interpolate, 0, 3), +}, { SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 3), @@ -705,14 +711,20 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 3), SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 3), -}; +} }; -/* Fan / PWM attr for the f8000, zones mapped to temp instead of to pwm! - Also the register block at offset A0 maps to TEMP1 (so our temp2, as the - F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */ +/* Fan attr specific to the f8000 (4th fan input can only measure speed) */ static struct sensor_device_attribute_2 f8000_fan_attr[] = { SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), +}; +/* PWM attr for the f8000, zones mapped to temp instead of to pwm! + Also the register block at offset A0 maps to TEMP1 (so our temp2, as the + F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */ +static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 2), @@ -751,6 +763,9 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = { SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), SENSOR_ATTR_2(temp2_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 0), @@ -789,6 +804,9 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = { SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), SENSOR_ATTR_2(temp3_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 1), @@ -929,7 +947,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) /* Update once every 60 seconds */ if ( time_after(jiffies, data->last_limits + 60 * HZ ) || !data->valid) { - if (data->type == f71882fg) { + if (data->type == f71882fg || data->type == f71889fg) { data->in1_max = f71882fg_read8(data, F71882FG_REG_IN1_HIGH); data->in_beep = @@ -951,7 +969,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_TEMP_HYST(1)); } - if (data->type == f71862fg || data->type == f71882fg) { + if (data->type == f71862fg || data->type == f71882fg || + data->type == f71889fg) { data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); data->temp_beep = f71882fg_read8(data, @@ -961,15 +980,33 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->temp_type[2] = (reg & 0x04) ? 2 : 4; data->temp_type[3] = (reg & 0x08) ? 2 : 4; } - reg2 = f71882fg_read8(data, F71882FG_REG_PECI); - if ((reg2 & 0x03) == 0x01) - data->temp_type[1] = 6 /* PECI */; - else if ((reg2 & 0x03) == 0x02) - data->temp_type[1] = 5 /* AMDSI */; - else if (data->type == f71862fg || data->type == f71882fg) - data->temp_type[1] = (reg & 0x02) ? 2 : 4; - else - data->temp_type[1] = 2; /* Only supports BJT */ + /* Determine temp index 1 sensor type */ + if (data->type == f71889fg) { + reg2 = f71882fg_read8(data, F71882FG_REG_START); + switch ((reg2 & 0x60) >> 5) { + case 0x00: /* BJT / Thermistor */ + data->temp_type[1] = (reg & 0x02) ? 2 : 4; + break; + case 0x01: /* AMDSI */ + data->temp_type[1] = 5; + break; + case 0x02: /* PECI */ + case 0x03: /* Ibex Peak ?? Report as PECI for now */ + data->temp_type[1] = 6; + break; + } + } else { + reg2 = f71882fg_read8(data, F71882FG_REG_PECI); + if ((reg2 & 0x03) == 0x01) + data->temp_type[1] = 6; /* PECI */ + else if ((reg2 & 0x03) == 0x02) + data->temp_type[1] = 5; /* AMDSI */ + else if (data->type == f71862fg || + data->type == f71882fg) + data->temp_type[1] = (reg & 0x02) ? 2 : 4; + else /* f71858fg and f8000 only support BJT */ + data->temp_type[1] = 2; + } data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); @@ -1046,7 +1083,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) if (data->type == f8000) data->fan[3] = f71882fg_read16(data, F71882FG_REG_FAN(3)); - if (data->type == f71882fg) + if (data->type == f71882fg || data->type == f71889fg) data->in_status = f71882fg_read8(data, F71882FG_REG_IN_STATUS); for (nr = 0; nr < nr_ins; nr++) @@ -1764,7 +1801,11 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev, int pwm = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; long val = simple_strtol(buf, NULL, 10) / 1000; - val = SENSORS_LIMIT(val, 0, 255); + + if (data->type == f71889fg) + val = SENSORS_LIMIT(val, -128, 127); + else + val = SENSORS_LIMIT(val, 0, 127); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); @@ -1794,6 +1835,15 @@ static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev, return 0; } +static void f71882fg_remove_sysfs_files(struct platform_device *pdev, + struct sensor_device_attribute_2 *attr, int count) +{ + int i; + + for (i = 0; i < count; i++) + device_remove_file(&pdev->dev, &attr[i].dev_attr); +} + static int __devinit f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; @@ -1846,16 +1896,17 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) ARRAY_SIZE(f71858fg_in_temp_attr)); break; case f71882fg: + case f71889fg: err = f71882fg_create_sysfs_files(pdev, - f71882fg_in_temp_attr, - ARRAY_SIZE(f71882fg_in_temp_attr)); + fxxxx_in1_alarm_attr, + ARRAY_SIZE(fxxxx_in1_alarm_attr)); if (err) goto exit_unregister_sysfs; /* fall through! */ case f71862fg: err = f71882fg_create_sysfs_files(pdev, - f718x2fg_in_temp_attr, - ARRAY_SIZE(f718x2fg_in_temp_attr)); + fxxxx_in_temp_attr, + ARRAY_SIZE(fxxxx_in_temp_attr)); break; case f8000: err = f71882fg_create_sysfs_files(pdev, @@ -1883,6 +1934,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) err = (data->pwm_enable & 0x15) != 0x15; break; case f71882fg: + case f71889fg: err = 0; break; case f8000: @@ -1897,34 +1949,55 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) goto exit_unregister_sysfs; } - err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr, - ARRAY_SIZE(fxxxx_fan_attr)); + err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0], + ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); if (err) goto exit_unregister_sysfs; - switch (data->type) { - case f71862fg: - err = f71882fg_create_sysfs_files(pdev, - f71862fg_fan_attr, - ARRAY_SIZE(f71862fg_fan_attr)); - break; - case f71882fg: + if (data->type == f71862fg || data->type == f71882fg || + data->type == f71889fg) { err = f71882fg_create_sysfs_files(pdev, - f71882fg_fan_attr, - ARRAY_SIZE(f71882fg_fan_attr)); + fxxxx_fan_beep_attr, nr_fans); if (err) goto exit_unregister_sysfs; - /* fall through! */ - case f71858fg: + } + + switch (data->type) { + case f71862fg: err = f71882fg_create_sysfs_files(pdev, - f71882fg_f71858fg_fan_attr, - ARRAY_SIZE(f71882fg_f71858fg_fan_attr)); + f71862fg_auto_pwm_attr, + ARRAY_SIZE(f71862fg_auto_pwm_attr)); break; case f8000: err = f71882fg_create_sysfs_files(pdev, f8000_fan_attr, ARRAY_SIZE(f8000_fan_attr)); + if (err) + goto exit_unregister_sysfs; + err = f71882fg_create_sysfs_files(pdev, + f8000_auto_pwm_attr, + ARRAY_SIZE(f8000_auto_pwm_attr)); break; + case f71889fg: + for (i = 0; i < nr_fans; i++) { + data->pwm_auto_point_mapping[i] = + f71882fg_read8(data, + F71882FG_REG_POINT_MAPPING(i)); + if (data->pwm_auto_point_mapping[i] & 0x80) + break; + } + if (i != nr_fans) { + dev_warn(&pdev->dev, + "Auto pwm controlled by raw digital " + "data, disabling pwm auto_point " + "sysfs attributes\n"); + break; + } + /* fall through */ + default: /* f71858fg / f71882fg */ + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); } if (err) goto exit_unregister_sysfs; @@ -1954,33 +2027,76 @@ exit_free: static int f71882fg_remove(struct platform_device *pdev) { - int i; struct f71882fg_data *data = platform_get_drvdata(pdev); + int nr_fans = (data->type == f71882fg) ? 4 : 3; + u8 start_reg = f71882fg_read8(data, F71882FG_REG_START); platform_set_drvdata(pdev, NULL); if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); - /* Note we are not looping over all attr arrays we have as the ones - below are supersets of the ones skipped. */ device_remove_file(&pdev->dev, &dev_attr_name); - for (i = 0; i < ARRAY_SIZE(f718x2fg_in_temp_attr); i++) - device_remove_file(&pdev->dev, - &f718x2fg_in_temp_attr[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) - device_remove_file(&pdev->dev, - &f71882fg_in_temp_attr[i].dev_attr); + if (start_reg & 0x01) { + switch (data->type) { + case f71858fg: + if (data->temp_config & 0x10) + f71882fg_remove_sysfs_files(pdev, + f8000_in_temp_attr, + ARRAY_SIZE(f8000_in_temp_attr)); + else + f71882fg_remove_sysfs_files(pdev, + f71858fg_in_temp_attr, + ARRAY_SIZE(f71858fg_in_temp_attr)); + break; + case f71882fg: + case f71889fg: + f71882fg_remove_sysfs_files(pdev, + fxxxx_in1_alarm_attr, + ARRAY_SIZE(fxxxx_in1_alarm_attr)); + /* fall through! */ + case f71862fg: + f71882fg_remove_sysfs_files(pdev, + fxxxx_in_temp_attr, + ARRAY_SIZE(fxxxx_in_temp_attr)); + break; + case f8000: + f71882fg_remove_sysfs_files(pdev, + f8000_in_temp_attr, + ARRAY_SIZE(f8000_in_temp_attr)); + break; + } + } - for (i = 0; i < ARRAY_SIZE(fxxxx_fan_attr); i++) - device_remove_file(&pdev->dev, &fxxxx_fan_attr[i].dev_attr); + if (start_reg & 0x02) { + f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0], + ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); - for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) - device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); + if (data->type == f71862fg || data->type == f71882fg || + data->type == f71889fg) + f71882fg_remove_sysfs_files(pdev, + fxxxx_fan_beep_attr, nr_fans); - for (i = 0; i < ARRAY_SIZE(f8000_fan_attr); i++) - device_remove_file(&pdev->dev, &f8000_fan_attr[i].dev_attr); + switch (data->type) { + case f71862fg: + f71882fg_remove_sysfs_files(pdev, + f71862fg_auto_pwm_attr, + ARRAY_SIZE(f71862fg_auto_pwm_attr)); + break; + case f8000: + f71882fg_remove_sysfs_files(pdev, + f8000_fan_attr, + ARRAY_SIZE(f8000_fan_attr)); + f71882fg_remove_sysfs_files(pdev, + f8000_auto_pwm_attr, + ARRAY_SIZE(f8000_auto_pwm_attr)); + break; + default: /* f71858fg / f71882fg / f71889fg */ + f71882fg_remove_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + } + } kfree(data); @@ -2012,11 +2128,15 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, case SIO_F71882_ID: sio_data->type = f71882fg; break; + case SIO_F71889_ID: + sio_data->type = f71889fg; + break; case SIO_F8000_ID: sio_data->type = f8000; break; default: - printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n"); + printk(KERN_INFO DRVNAME ": Unsupported Fintek device: %04x\n", + (unsigned int)devid); goto exit; } diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index e2107e533ede..40dfbcd3f3f2 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -681,30 +681,20 @@ static int f75375_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - u8 version = 0; - const char *name = ""; - - if (kind < 0) { - u16 vendid = f75375_read16(client, F75375_REG_VENDOR); - u16 chipid = f75375_read16(client, F75375_CHIP_ID); - version = f75375_read8(client, F75375_REG_VERSION); - if (chipid == 0x0306 && vendid == 0x1934) { - kind = f75375; - } else if (chipid == 0x0204 && vendid == 0x1934) { - kind = f75373; - } else { - dev_err(&adapter->dev, - "failed,%02X,%02X,%02X\n", - chipid, version, vendid); - return -ENODEV; - } - } + u16 vendid, chipid; + u8 version; + const char *name; - if (kind == f75375) { + vendid = f75375_read16(client, F75375_REG_VENDOR); + chipid = f75375_read16(client, F75375_CHIP_ID); + if (chipid == 0x0306 && vendid == 0x1934) name = "f75375"; - } else if (kind == f75373) { + else if (chipid == 0x0204 && vendid == 0x1934) name = "f75373"; - } + else + return -ENODEV; + + version = f75375_read8(client, F75375_REG_VERSION); dev_info(&adapter->dev, "found %s version: %02X\n", name, version); strlcpy(info->type, name, I2C_NAME_SIZE); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index da1b1f9488af..281829cd1533 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -1000,43 +1000,38 @@ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy) } } -static int fschmd_detect(struct i2c_client *client, int kind, +static int fschmd_detect(struct i2c_client *client, int _kind, struct i2c_board_info *info) { + enum chips kind; struct i2c_adapter *adapter = client->adapter; + char id[4]; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; /* Detect & Identify the chip */ - if (kind <= 0) { - char id[4]; - - id[0] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_IDENT_0); - id[1] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_IDENT_1); - id[2] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_IDENT_2); - id[3] = '\0'; - - if (!strcmp(id, "PEG")) - kind = fscpos; - else if (!strcmp(id, "HER")) - kind = fscher; - else if (!strcmp(id, "SCY")) - kind = fscscy; - else if (!strcmp(id, "HRC")) - kind = fschrc; - else if (!strcmp(id, "HMD")) - kind = fschmd; - else if (!strcmp(id, "HDS")) - kind = fschds; - else if (!strcmp(id, "SYL")) - kind = fscsyl; - else - return -ENODEV; - } + id[0] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_0); + id[1] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_1); + id[2] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_2); + id[3] = '\0'; + + if (!strcmp(id, "PEG")) + kind = fscpos; + else if (!strcmp(id, "HER")) + kind = fscher; + else if (!strcmp(id, "SCY")) + kind = fscscy; + else if (!strcmp(id, "HRC")) + kind = fschrc; + else if (!strcmp(id, "HMD")) + kind = fschmd; + else if (!strcmp(id, "HDS")) + kind = fschds; + else if (!strcmp(id, "SYL")) + kind = fscsyl; + else + return -ENODEV; strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE); diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index 7820df45d77a..1d69458aa0b6 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -488,36 +488,21 @@ static int gl518_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - int i; + int rev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; /* Now, we do the remaining detection. */ - - if (kind < 0) { - if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80) - || (gl518_read_value(client, GL518_REG_CONF) & 0x80)) - return -ENODEV; - } + if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80) + || (gl518_read_value(client, GL518_REG_CONF) & 0x80)) + return -ENODEV; /* Determine the chip type. */ - if (kind <= 0) { - i = gl518_read_value(client, GL518_REG_REVISION); - if (i == 0x00) { - kind = gl518sm_r00; - } else if (i == 0x80) { - kind = gl518sm_r80; - } else { - if (kind <= 0) - dev_info(&adapter->dev, - "Ignoring 'force' parameter for unknown " - "chip at adapter %d, address 0x%02x\n", - i2c_adapter_id(adapter), client->addr); - return -ENODEV; - } - } + rev = gl518_read_value(client, GL518_REG_REVISION); + if (rev != 0x00 && rev != 0x80) + return -ENODEV; strlcpy(info->type, "gl518sm", I2C_NAME_SIZE); diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 19616f2242b0..92b5720ceaff 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -691,13 +691,11 @@ static int gl520_detect(struct i2c_client *client, int kind, return -ENODEV; /* Determine the chip type. */ - if (kind < 0) { - if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) || - ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) || - ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) { - dev_dbg(&client->dev, "Unknown chip type, skipping\n"); - return -ENODEV; - } + if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) || + ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) || + ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) { + dev_dbg(&client->dev, "Unknown chip type, skipping\n"); + return -ENODEV; } strlcpy(info->type, "gl520sm", I2C_NAME_SIZE); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index a3749cb0f181..0ffe84d190bb 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -124,6 +124,8 @@ superio_exit(void) #define IT87_BASE_REG 0x60 /* Logical device 7 registers (IT8712F and later) */ +#define IT87_SIO_GPIO3_REG 0x27 +#define IT87_SIO_GPIO5_REG 0x29 #define IT87_SIO_PINX2_REG 0x2c /* Pin selection */ #define IT87_SIO_VID_REG 0xfc /* VID value */ @@ -244,7 +246,9 @@ struct it87_sio_data { /* Values read from Super-I/O config space */ u8 revision; u8 vid_value; - /* Values set based on DMI strings */ + /* Features skipped based on config or DMI */ + u8 skip_vid; + u8 skip_fan; u8 skip_pwm; }; @@ -1028,11 +1032,35 @@ static int __init it87_find(unsigned short *address, chip_type, *address, sio_data->revision); /* Read GPIO config and VID value from LDN 7 (GPIO) */ - if (sio_data->type != it87) { + if (sio_data->type == it87) { + /* The IT8705F doesn't have VID pins at all */ + sio_data->skip_vid = 1; + } else { int reg; superio_select(GPIO); - if (sio_data->type == it8718 || sio_data->type == it8720) + /* We need at least 4 VID pins */ + reg = superio_inb(IT87_SIO_GPIO3_REG); + if (reg & 0x0f) { + pr_info("it87: VID is disabled (pins used for GPIO)\n"); + sio_data->skip_vid = 1; + } + + /* Check if fan3 is there or not */ + if (reg & (1 << 6)) + sio_data->skip_pwm |= (1 << 2); + if (reg & (1 << 7)) + sio_data->skip_fan |= (1 << 2); + + /* Check if fan2 is there or not */ + reg = superio_inb(IT87_SIO_GPIO5_REG); + if (reg & (1 << 1)) + sio_data->skip_pwm |= (1 << 1); + if (reg & (1 << 2)) + sio_data->skip_fan |= (1 << 1); + + if ((sio_data->type == it8718 || sio_data->type == it8720) + && !(sio_data->skip_vid)) sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); reg = superio_inb(IT87_SIO_PINX2_REG); @@ -1236,8 +1264,7 @@ static int __devinit it87_probe(struct platform_device *pdev) } } - if (data->type == it8712 || data->type == it8716 - || data->type == it8718 || data->type == it8720) { + if (!sio_data->skip_vid) { data->vrm = vid_which_vrm(); /* VID reading from Super-I/O config space if available */ data->vid = sio_data->vid_value; @@ -1355,8 +1382,10 @@ static int __devinit it87_check_pwm(struct device *dev) /* Called when we have found a new IT87. */ static void __devinit it87_init_device(struct platform_device *pdev) { + struct it87_sio_data *sio_data = pdev->dev.platform_data; struct it87_data *data = platform_get_drvdata(pdev); int tmp, i; + u8 mask; /* initialize to sane defaults: * - if the chip is in manual pwm mode, this will be overwritten with @@ -1402,10 +1431,11 @@ static void __devinit it87_init_device(struct platform_device *pdev) } /* Check if tachometers are reset manually or by some reason */ + mask = 0x70 & ~(sio_data->skip_fan << 4); data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); - if ((data->fan_main_ctrl & 0x70) == 0) { + if ((data->fan_main_ctrl & mask) == 0) { /* Enable all fan tachometers */ - data->fan_main_ctrl |= 0x70; + data->fan_main_ctrl |= mask; it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); } data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; @@ -1428,6 +1458,9 @@ static void __devinit it87_init_device(struct platform_device *pdev) } } + /* Fan input pins may be used for alternative functions */ + data->has_fan &= ~sio_data->skip_fan; + /* Set current fan mode registers and the default settings for the * other mode registers */ for (i = 0; i < 3; i++) { diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 3195a265f0e9..5da66ab04f74 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -427,40 +427,34 @@ static int lm63_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + u8 man_id, chip_id, reg_config1, reg_config2; + u8 reg_alert_status, reg_alert_mask; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind < 0) { /* must identify */ - u8 man_id, chip_id, reg_config1, reg_config2; - u8 reg_alert_status, reg_alert_mask; - - man_id = i2c_smbus_read_byte_data(new_client, - LM63_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, - LM63_REG_CHIP_ID); - reg_config1 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG1); - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG2); - reg_alert_status = i2c_smbus_read_byte_data(new_client, - LM63_REG_ALERT_STATUS); - reg_alert_mask = i2c_smbus_read_byte_data(new_client, - LM63_REG_ALERT_MASK); - - if (man_id == 0x01 /* National Semiconductor */ - && chip_id == 0x41 /* LM63 */ - && (reg_config1 & 0x18) == 0x00 - && (reg_config2 & 0xF8) == 0x00 - && (reg_alert_status & 0x20) == 0x00 - && (reg_alert_mask & 0xA4) == 0xA4) { - kind = lm63; - } else { /* failed */ - dev_dbg(&adapter->dev, "Unsupported chip " - "(man_id=0x%02X, chip_id=0x%02X).\n", - man_id, chip_id); - return -ENODEV; - } + man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID); + + reg_config1 = i2c_smbus_read_byte_data(new_client, + LM63_REG_CONFIG1); + reg_config2 = i2c_smbus_read_byte_data(new_client, + LM63_REG_CONFIG2); + reg_alert_status = i2c_smbus_read_byte_data(new_client, + LM63_REG_ALERT_STATUS); + reg_alert_mask = i2c_smbus_read_byte_data(new_client, + LM63_REG_ALERT_MASK); + + if (man_id != 0x01 /* National Semiconductor */ + || chip_id != 0x41 /* LM63 */ + || (reg_config1 & 0x18) != 0x00 + || (reg_config2 & 0xF8) != 0x00 + || (reg_alert_status & 0x20) != 0x00 + || (reg_alert_mask & 0xA4) != 0xA4) { + dev_dbg(&adapter->dev, + "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, "lm63", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c new file mode 100644 index 000000000000..0bf8b2a8e9f0 --- /dev/null +++ b/drivers/hwmon/lm73.c @@ -0,0 +1,205 @@ +/* + * LM73 Sensor driver + * Based on LM75 + * + * Copyright (C) 2007, CenoSYS (www.cenosys.com). + * Copyright (C) 2009, Bollore telecom (www.bolloretelecom.eu). + * + * Guillaume Ligneul <guillaume.ligneul@gmail.com> + * Adrien Demarez <adrien.demarez@bolloretelecom.eu> + * Jeremy Laine <jeremy.laine@bolloretelecom.eu> + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> + + +/* Addresses scanned */ +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, + 0x4d, 0x4e, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(lm73); + +/* LM73 registers */ +#define LM73_REG_INPUT 0x00 +#define LM73_REG_CONF 0x01 +#define LM73_REG_MAX 0x02 +#define LM73_REG_MIN 0x03 +#define LM73_REG_CTRL 0x04 +#define LM73_REG_ID 0x07 + +#define LM73_ID 0x9001 /* or 0x190 after a swab16() */ +#define DRVNAME "lm73" +#define LM73_TEMP_MIN (-40) +#define LM73_TEMP_MAX 150 + +/*-----------------------------------------------------------------------*/ + + +static ssize_t set_temp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + long temp; + short value; + + int status = strict_strtol(buf, 10, &temp); + if (status < 0) + return status; + + /* Write value */ + value = (short) SENSORS_LIMIT(temp/250, (LM73_TEMP_MIN*4), + (LM73_TEMP_MAX*4)) << 5; + i2c_smbus_write_word_data(client, attr->index, swab16(value)); + return count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + /* use integer division instead of equivalent right shift to + guarantee arithmetic shift and preserve the sign */ + int temp = ((s16) (swab16(i2c_smbus_read_word_data(client, + attr->index)))*250) / 32; + return sprintf(buf, "%d\n", temp); +} + + +/*-----------------------------------------------------------------------*/ + +/* sysfs attributes for hwmon */ + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + show_temp, set_temp, LM73_REG_MAX); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, + show_temp, set_temp, LM73_REG_MIN); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + show_temp, NULL, LM73_REG_INPUT); + + +static struct attribute *lm73_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + + NULL +}; + +static const struct attribute_group lm73_group = { + .attrs = lm73_attributes, +}; + +/*-----------------------------------------------------------------------*/ + +/* device probe and removal */ + +static int +lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *hwmon_dev; + int status; + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &lm73_group); + if (status) + return status; + + hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(hwmon_dev)) { + status = PTR_ERR(hwmon_dev); + goto exit_remove; + } + i2c_set_clientdata(client, hwmon_dev); + + dev_info(&client->dev, "%s: sensor '%s'\n", + dev_name(hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &lm73_group); + return status; +} + +static int lm73_remove(struct i2c_client *client) +{ + struct device *hwmon_dev = i2c_get_clientdata(client); + + hwmon_device_unregister(hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm73_group); + i2c_set_clientdata(client, NULL); + return 0; +} + +static const struct i2c_device_id lm73_ids[] = { + { "lm73", lm73 }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, lm73_ids); + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm73_detect(struct i2c_client *new_client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + u16 id; + u8 ctrl; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + /* Check device ID */ + id = i2c_smbus_read_word_data(new_client, LM73_REG_ID); + ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL); + if ((id != LM73_ID) || (ctrl & 0x10)) + return -ENODEV; + + strlcpy(info->type, "lm73", I2C_NAME_SIZE); + + return 0; +} + +static struct i2c_driver lm73_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm73", + }, + .probe = lm73_probe, + .remove = lm73_remove, + .id_table = lm73_ids, + .detect = lm73_detect, + .address_data = &addr_data, +}; + +/* module glue */ + +static int __init sensors_lm73_init(void) +{ + return i2c_add_driver(&lm73_driver); +} + +static void __exit sensors_lm73_exit(void) +{ + i2c_del_driver(&lm73_driver); +} + +MODULE_AUTHOR("Guillaume Ligneul <guillaume.ligneul@gmail.com>"); +MODULE_DESCRIPTION("LM73 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm73_init); +module_exit(sensors_lm73_exit); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 55bd87c15c9a..e392548cccb8 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -239,6 +239,7 @@ static int lm75_detect(struct i2c_client *new_client, int kind, { struct i2c_adapter *adapter = new_client->adapter; int i; + int cur, conf, hyst, os; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) @@ -251,40 +252,35 @@ static int lm75_detect(struct i2c_client *new_client, int kind, The cycling+unused addresses combination is not tested, since it would significantly slow the detection down and would hardly add any value. */ - if (kind < 0) { - int cur, conf, hyst, os; - - /* Unused addresses */ - cur = i2c_smbus_read_word_data(new_client, 0); - conf = i2c_smbus_read_byte_data(new_client, 1); - hyst = i2c_smbus_read_word_data(new_client, 2); - if (i2c_smbus_read_word_data(new_client, 4) != hyst - || i2c_smbus_read_word_data(new_client, 5) != hyst - || i2c_smbus_read_word_data(new_client, 6) != hyst - || i2c_smbus_read_word_data(new_client, 7) != hyst) - return -ENODEV; - os = i2c_smbus_read_word_data(new_client, 3); - if (i2c_smbus_read_word_data(new_client, 4) != os - || i2c_smbus_read_word_data(new_client, 5) != os - || i2c_smbus_read_word_data(new_client, 6) != os - || i2c_smbus_read_word_data(new_client, 7) != os) - return -ENODEV; - /* Unused bits */ - if (conf & 0xe0) - return -ENODEV; + /* Unused addresses */ + cur = i2c_smbus_read_word_data(new_client, 0); + conf = i2c_smbus_read_byte_data(new_client, 1); + hyst = i2c_smbus_read_word_data(new_client, 2); + if (i2c_smbus_read_word_data(new_client, 4) != hyst + || i2c_smbus_read_word_data(new_client, 5) != hyst + || i2c_smbus_read_word_data(new_client, 6) != hyst + || i2c_smbus_read_word_data(new_client, 7) != hyst) + return -ENODEV; + os = i2c_smbus_read_word_data(new_client, 3); + if (i2c_smbus_read_word_data(new_client, 4) != os + || i2c_smbus_read_word_data(new_client, 5) != os + || i2c_smbus_read_word_data(new_client, 6) != os + || i2c_smbus_read_word_data(new_client, 7) != os) + return -ENODEV; - /* Addresses cycling */ - for (i = 8; i < 0xff; i += 8) - if (i2c_smbus_read_byte_data(new_client, i + 1) != conf - || i2c_smbus_read_word_data(new_client, i + 2) != hyst - || i2c_smbus_read_word_data(new_client, i + 3) != os) - return -ENODEV; + /* Unused bits */ + if (conf & 0xe0) + return -ENODEV; + + /* Addresses cycling */ + for (i = 8; i < 0xff; i += 8) { + if (i2c_smbus_read_byte_data(new_client, i + 1) != conf + || i2c_smbus_read_word_data(new_client, i + 2) != hyst + || i2c_smbus_read_word_data(new_client, i + 3) != os) + return -ENODEV; } - /* NOTE: we treat "force=..." and "force_lm75=..." the same. - * Only new-style driver binding distinguishes chip types. - */ strlcpy(info->type, "lm75", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index 866b401ab6e8..ac067fd19482 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -249,6 +249,7 @@ static int lm77_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + int i, cur, conf, hyst, crit, min, max; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) @@ -265,51 +266,48 @@ static int lm77_detect(struct i2c_client *new_client, int kind, 4. registers cycling over 8-address boundaries Word-sized registers are high-byte first. */ - if (kind < 0) { - int i, cur, conf, hyst, crit, min, max; - - /* addresses cycling */ - cur = i2c_smbus_read_word_data(new_client, 0); - conf = i2c_smbus_read_byte_data(new_client, 1); - hyst = i2c_smbus_read_word_data(new_client, 2); - crit = i2c_smbus_read_word_data(new_client, 3); - min = i2c_smbus_read_word_data(new_client, 4); - max = i2c_smbus_read_word_data(new_client, 5); - for (i = 8; i <= 0xff; i += 8) - if (i2c_smbus_read_byte_data(new_client, i + 1) != conf - || i2c_smbus_read_word_data(new_client, i + 2) != hyst - || i2c_smbus_read_word_data(new_client, i + 3) != crit - || i2c_smbus_read_word_data(new_client, i + 4) != min - || i2c_smbus_read_word_data(new_client, i + 5) != max) - return -ENODEV; - - /* sign bits */ - if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0) - || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0) - || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0) - || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0) - || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0)) - return -ENODEV; - /* unused bits */ - if (conf & 0xe0) + /* addresses cycling */ + cur = i2c_smbus_read_word_data(new_client, 0); + conf = i2c_smbus_read_byte_data(new_client, 1); + hyst = i2c_smbus_read_word_data(new_client, 2); + crit = i2c_smbus_read_word_data(new_client, 3); + min = i2c_smbus_read_word_data(new_client, 4); + max = i2c_smbus_read_word_data(new_client, 5); + for (i = 8; i <= 0xff; i += 8) { + if (i2c_smbus_read_byte_data(new_client, i + 1) != conf + || i2c_smbus_read_word_data(new_client, i + 2) != hyst + || i2c_smbus_read_word_data(new_client, i + 3) != crit + || i2c_smbus_read_word_data(new_client, i + 4) != min + || i2c_smbus_read_word_data(new_client, i + 5) != max) return -ENODEV; + } - /* 0x06 and 0x07 return the last read value */ - cur = i2c_smbus_read_word_data(new_client, 0); - if (i2c_smbus_read_word_data(new_client, 6) != cur - || i2c_smbus_read_word_data(new_client, 7) != cur) - return -ENODEV; - hyst = i2c_smbus_read_word_data(new_client, 2); - if (i2c_smbus_read_word_data(new_client, 6) != hyst - || i2c_smbus_read_word_data(new_client, 7) != hyst) - return -ENODEV; - min = i2c_smbus_read_word_data(new_client, 4); - if (i2c_smbus_read_word_data(new_client, 6) != min - || i2c_smbus_read_word_data(new_client, 7) != min) - return -ENODEV; + /* sign bits */ + if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0) + || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0) + || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0) + || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0) + || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0)) + return -ENODEV; - } + /* unused bits */ + if (conf & 0xe0) + return -ENODEV; + + /* 0x06 and 0x07 return the last read value */ + cur = i2c_smbus_read_word_data(new_client, 0); + if (i2c_smbus_read_word_data(new_client, 6) != cur + || i2c_smbus_read_word_data(new_client, 7) != cur) + return -ENODEV; + hyst = i2c_smbus_read_word_data(new_client, 2); + if (i2c_smbus_read_word_data(new_client, 6) != hyst + || i2c_smbus_read_word_data(new_client, 7) != hyst) + return -ENODEV; + min = i2c_smbus_read_word_data(new_client, 4); + if (i2c_smbus_read_word_data(new_client, 6) != min + || i2c_smbus_read_word_data(new_client, 7) != min) + return -ENODEV; strlcpy(info->type, "lm77", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index f7e70163e016..5978291cebb3 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -576,52 +576,34 @@ static int lm78_i2c_detect(struct i2c_client *client, int kind, if (isa) mutex_lock(&isa->update_lock); - if (kind < 0) { - if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80) - || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) - != address) - goto err_nodev; - - /* Explicitly prevent the misdetection of Winbond chips */ - i = i2c_smbus_read_byte_data(client, 0x4f); - if (i == 0xa3 || i == 0x5c) - goto err_nodev; - } + if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80) + || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) != address) + goto err_nodev; + + /* Explicitly prevent the misdetection of Winbond chips */ + i = i2c_smbus_read_byte_data(client, 0x4f); + if (i == 0xa3 || i == 0x5c) + goto err_nodev; /* Determine the chip type. */ - if (kind <= 0) { - i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID); - if (i == 0x00 || i == 0x20 /* LM78 */ - || i == 0x40) /* LM78-J */ - kind = lm78; - else if ((i & 0xfe) == 0xc0) - kind = lm79; - else { - if (kind == 0) - dev_warn(&adapter->dev, "Ignoring 'force' " - "parameter for unknown chip at " - "adapter %d, address 0x%02x\n", - i2c_adapter_id(adapter), address); - goto err_nodev; - } + i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID); + if (i == 0x00 || i == 0x20 /* LM78 */ + || i == 0x40) /* LM78-J */ + client_name = "lm78"; + else if ((i & 0xfe) == 0xc0) + client_name = "lm79"; + else + goto err_nodev; - if (lm78_alias_detect(client, i)) { - dev_dbg(&adapter->dev, "Device at 0x%02x appears to " - "be the same as ISA device\n", address); - goto err_nodev; - } + if (lm78_alias_detect(client, i)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + goto err_nodev; } if (isa) mutex_unlock(&isa->update_lock); - switch (kind) { - case lm79: - client_name = "lm79"; - break; - default: - client_name = "lm78"; - } strlcpy(info->type, client_name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index e59e2d1f080c..08b03e6ed0b7 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -1,7 +1,7 @@ /* * lm83.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> * * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is * a sensor chip made by National Semiconductor. It reports up to four @@ -295,69 +295,40 @@ static int lm83_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - const char *name = ""; + const char *name; + u8 man_id, chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* Now we do the detection and identification. A negative kind - * means that the driver was loaded with no force parameter - * (default), so we must both detect and identify the chip - * (actually there is only one possible kind of chip for now, LM83). - * A zero kind means that the driver was loaded with the force - * parameter, the detection step shall be skipped. A positive kind - * means that the driver was loaded with the force parameter and a - * given kind of chip is requested, so both the detection and the - * identification steps are skipped. */ - - /* Default to an LM83 if forced */ - if (kind == 0) - kind = lm83; - - if (kind < 0) { /* detection */ - if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1) - & 0xA8) != 0x00) || - ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2) - & 0x48) != 0x00) || - ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG) - & 0x41) != 0x00)) { - dev_dbg(&adapter->dev, - "LM83 detection failed at 0x%02x.\n", - new_client->addr); - return -ENODEV; - } + /* Detection */ + if ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1) & 0xA8) || + (i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2) & 0x48) || + (i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG) & 0x41)) { + dev_dbg(&adapter->dev, "LM83 detection failed at 0x%02x\n", + new_client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u8 man_id, chip_id; - - man_id = i2c_smbus_read_byte_data(new_client, - LM83_REG_R_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, - LM83_REG_R_CHIP_ID); - - if (man_id == 0x01) { /* National Semiconductor */ - if (chip_id == 0x03) { - kind = lm83; - } else - if (chip_id == 0x01) { - kind = lm82; - } - } - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } - } + /* Identification */ + man_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_MAN_ID); + if (man_id != 0x01) /* National Semiconductor */ + return -ENODEV; - if (kind == lm83) { + chip_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_CHIP_ID); + switch (chip_id) { + case 0x03: name = "lm83"; - } else - if (kind == lm82) { + break; + case 0x01: name = "lm82"; + break; + default: + /* identification failed */ + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, name, I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 6c53d987de10..d56da2e74708 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -5,7 +5,7 @@ Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com> - Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org> + Copyright (C) 2007--2009 Jean Delvare <khali@linux-fr.org> Chip details at <http://www.national.com/ds/LM/LM85.pdf> @@ -1162,107 +1162,80 @@ static int lm85_detect(struct i2c_client *client, int kind, struct i2c_adapter *adapter = client->adapter; int address = client->addr; const char *type_name; + int company, verstep; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { /* We need to be able to do byte I/O */ return -ENODEV; } - /* If auto-detecting, determine the chip type */ - if (kind < 0) { - int company = lm85_read_value(client, LM85_REG_COMPANY); - int verstep = lm85_read_value(client, LM85_REG_VERSTEP); - - dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " - "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", - address, company, verstep); - - /* All supported chips have the version in common */ - if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC && - (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) { - dev_dbg(&adapter->dev, "Autodetection failed: " - "unsupported version\n"); - return -ENODEV; - } - kind = any_chip; - - /* Now, refine the detection */ - if (company == LM85_COMPANY_NATIONAL) { - switch (verstep) { - case LM85_VERSTEP_LM85C: - kind = lm85c; - break; - case LM85_VERSTEP_LM85B: - kind = lm85b; - break; - case LM85_VERSTEP_LM96000_1: - case LM85_VERSTEP_LM96000_2: - /* Check for Winbond WPCD377I */ - if (lm85_is_fake(client)) { - dev_dbg(&adapter->dev, - "Found Winbond WPCD377I, " - "ignoring\n"); - return -ENODEV; - } - break; - } - } else if (company == LM85_COMPANY_ANALOG_DEV) { - switch (verstep) { - case LM85_VERSTEP_ADM1027: - kind = adm1027; - break; - case LM85_VERSTEP_ADT7463: - case LM85_VERSTEP_ADT7463C: - kind = adt7463; - break; - case LM85_VERSTEP_ADT7468_1: - case LM85_VERSTEP_ADT7468_2: - kind = adt7468; - break; - } - } else if (company == LM85_COMPANY_SMSC) { - switch (verstep) { - case LM85_VERSTEP_EMC6D100_A0: - case LM85_VERSTEP_EMC6D100_A1: - /* Note: we can't tell a '100 from a '101 */ - kind = emc6d100; - break; - case LM85_VERSTEP_EMC6D102: - kind = emc6d102; - break; + /* Determine the chip type */ + company = lm85_read_value(client, LM85_REG_COMPANY); + verstep = lm85_read_value(client, LM85_REG_VERSTEP); + + dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " + "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + address, company, verstep); + + /* All supported chips have the version in common */ + if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC && + (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) { + dev_dbg(&adapter->dev, + "Autodetection failed: unsupported version\n"); + return -ENODEV; + } + type_name = "lm85"; + + /* Now, refine the detection */ + if (company == LM85_COMPANY_NATIONAL) { + switch (verstep) { + case LM85_VERSTEP_LM85C: + type_name = "lm85c"; + break; + case LM85_VERSTEP_LM85B: + type_name = "lm85b"; + break; + case LM85_VERSTEP_LM96000_1: + case LM85_VERSTEP_LM96000_2: + /* Check for Winbond WPCD377I */ + if (lm85_is_fake(client)) { + dev_dbg(&adapter->dev, + "Found Winbond WPCD377I, ignoring\n"); + return -ENODEV; } - } else { - dev_dbg(&adapter->dev, "Autodetection failed: " - "unknown vendor\n"); - return -ENODEV; + break; + } + } else if (company == LM85_COMPANY_ANALOG_DEV) { + switch (verstep) { + case LM85_VERSTEP_ADM1027: + type_name = "adm1027"; + break; + case LM85_VERSTEP_ADT7463: + case LM85_VERSTEP_ADT7463C: + type_name = "adt7463"; + break; + case LM85_VERSTEP_ADT7468_1: + case LM85_VERSTEP_ADT7468_2: + type_name = "adt7468"; + break; } + } else if (company == LM85_COMPANY_SMSC) { + switch (verstep) { + case LM85_VERSTEP_EMC6D100_A0: + case LM85_VERSTEP_EMC6D100_A1: + /* Note: we can't tell a '100 from a '101 */ + type_name = "emc6d100"; + break; + case LM85_VERSTEP_EMC6D102: + type_name = "emc6d102"; + break; + } + } else { + dev_dbg(&adapter->dev, + "Autodetection failed: unknown vendor\n"); + return -ENODEV; } - switch (kind) { - case lm85b: - type_name = "lm85b"; - break; - case lm85c: - type_name = "lm85c"; - break; - case adm1027: - type_name = "adm1027"; - break; - case adt7463: - type_name = "adt7463"; - break; - case adt7468: - type_name = "adt7468"; - break; - case emc6d100: - type_name = "emc6d100"; - break; - case emc6d102: - type_name = "emc6d102"; - break; - default: - type_name = "lm85"; - } strlcpy(info->type, type_name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 2e4a3cea95f7..4929b1815eee 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -666,37 +666,32 @@ static int lm87_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - static const char *names[] = { "lm87", "adm1024" }; + const char *name; + u8 cid, rev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* Default to an LM87 if forced */ - if (kind == 0) - kind = lm87; + if (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80) + return -ENODEV; /* Now, we do the remaining detection. */ - if (kind < 0) { - u8 cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID); - u8 rev = lm87_read_value(new_client, LM87_REG_REVISION); - - if (cid == 0x02 /* National Semiconductor */ - && (rev >= 0x01 && rev <= 0x08)) - kind = lm87; - else if (cid == 0x41 /* Analog Devices */ - && (rev & 0xf0) == 0x10) - kind = adm1024; - - if (kind < 0 - || (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)) { - dev_dbg(&adapter->dev, - "LM87 detection failed at 0x%02x.\n", - new_client->addr); - return -ENODEV; - } + cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID); + rev = lm87_read_value(new_client, LM87_REG_REVISION); + + if (cid == 0x02 /* National Semiconductor */ + && (rev >= 0x01 && rev <= 0x08)) + name = "lm87"; + else if (cid == 0x41 /* Analog Devices */ + && (rev & 0xf0) == 0x10) + name = "adm1024"; + else { + dev_dbg(&adapter->dev, "LM87 detection failed at 0x%02x\n", + new_client->addr); + return -ENODEV; } - strlcpy(info->type, names[kind - 1], I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 1aff7575799d..b7c905f50ed4 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1,7 +1,7 @@ /* * lm90.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> * * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to @@ -661,154 +661,118 @@ static int lm90_detect(struct i2c_client *new_client, int kind, { struct i2c_adapter *adapter = new_client->adapter; int address = new_client->addr; - const char *name = ""; + const char *name = NULL; + int man_id, chip_id, reg_config1, reg_convrate; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - - /* Default to an LM90 if forced */ - if (kind == 0) - kind = lm90; - - if (kind < 0) { /* detection and identification */ - int man_id, chip_id, reg_config1, reg_convrate; - - if ((man_id = i2c_smbus_read_byte_data(new_client, + /* detection and identification */ + if ((man_id = i2c_smbus_read_byte_data(new_client, LM90_REG_R_MAN_ID)) < 0 - || (chip_id = i2c_smbus_read_byte_data(new_client, + || (chip_id = i2c_smbus_read_byte_data(new_client, LM90_REG_R_CHIP_ID)) < 0 - || (reg_config1 = i2c_smbus_read_byte_data(new_client, + || (reg_config1 = i2c_smbus_read_byte_data(new_client, LM90_REG_R_CONFIG1)) < 0 - || (reg_convrate = i2c_smbus_read_byte_data(new_client, + || (reg_convrate = i2c_smbus_read_byte_data(new_client, LM90_REG_R_CONVRATE)) < 0) + return -ENODEV; + + if ((address == 0x4C || address == 0x4D) + && man_id == 0x01) { /* National Semiconductor */ + int reg_config2; + + reg_config2 = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CONFIG2); + if (reg_config2 < 0) return -ENODEV; - - if ((address == 0x4C || address == 0x4D) - && man_id == 0x01) { /* National Semiconductor */ - int reg_config2; - - if ((reg_config2 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG2)) < 0) - return -ENODEV; - - if ((reg_config1 & 0x2A) == 0x00 - && (reg_config2 & 0xF8) == 0x00 - && reg_convrate <= 0x09) { - if (address == 0x4C - && (chip_id & 0xF0) == 0x20) { /* LM90 */ - kind = lm90; - } else - if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */ - kind = lm99; - dev_info(&adapter->dev, - "Assuming LM99 chip at " - "0x%02x\n", address); - dev_info(&adapter->dev, - "If it is an LM89, pass " - "force_lm86=%d,0x%02x when " - "loading the lm90 driver\n", - i2c_adapter_id(adapter), - address); - } else - if (address == 0x4C - && (chip_id & 0xF0) == 0x10) { /* LM86 */ - kind = lm86; - } - } - } else - if ((address == 0x4C || address == 0x4D) - && man_id == 0x41) { /* Analog Devices */ - if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ - && (reg_config1 & 0x3F) == 0x00 - && reg_convrate <= 0x0A) { - kind = adm1032; - } else - if (chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1B) == 0x00 - && reg_convrate <= 0x0A) { - kind = adt7461; - } - } else - if (man_id == 0x4D) { /* Maxim */ - /* - * The MAX6657, MAX6658 and MAX6659 do NOT have a - * chip_id register. Reading from that address will - * return the last read value, which in our case is - * those of the man_id register. Likewise, the config1 - * register seems to lack a low nibble, so the value - * will be those of the previous read, so in our case - * those of the man_id register. - */ - if (chip_id == man_id - && (address == 0x4C || address == 0x4D) - && (reg_config1 & 0x1F) == (man_id & 0x0F) - && reg_convrate <= 0x09) { - kind = max6657; + + if ((reg_config1 & 0x2A) == 0x00 + && (reg_config2 & 0xF8) == 0x00 + && reg_convrate <= 0x09) { + if (address == 0x4C + && (chip_id & 0xF0) == 0x20) { /* LM90 */ + name = "lm90"; } else - /* The chip_id register of the MAX6680 and MAX6681 - * holds the revision of the chip. - * the lowest bit of the config1 register is unused - * and should return zero when read, so should the - * second to last bit of config1 (software reset) - */ - if (chip_id == 0x01 - && (reg_config1 & 0x03) == 0x00 - && reg_convrate <= 0x07) { - kind = max6680; + if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */ + name = "lm99"; + dev_info(&adapter->dev, + "Assuming LM99 chip at 0x%02x\n", + address); + dev_info(&adapter->dev, + "If it is an LM89, instantiate it " + "with the new_device sysfs " + "interface\n"); } else - /* The chip_id register of the MAX6646/6647/6649 - * holds the revision of the chip. - * The lowest 6 bits of the config1 register are - * unused and should return zero when read. - */ - if (chip_id == 0x59 - && (reg_config1 & 0x3f) == 0x00 - && reg_convrate <= 0x07) { - kind = max6646; + if (address == 0x4C + && (chip_id & 0xF0) == 0x10) { /* LM86 */ + name = "lm86"; } } - - if (kind <= 0) { /* identification failed */ - dev_dbg(&adapter->dev, - "Unsupported chip at 0x%02x (man_id=0x%02X, " - "chip_id=0x%02X)\n", address, man_id, chip_id); - return -ENODEV; + } else + if ((address == 0x4C || address == 0x4D) + && man_id == 0x41) { /* Analog Devices */ + if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ + && (reg_config1 & 0x3F) == 0x00 + && reg_convrate <= 0x0A) { + name = "adm1032"; + /* The ADM1032 supports PEC, but only if combined + transactions are not used. */ + if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE)) + info->flags |= I2C_CLIENT_PEC; + } else + if (chip_id == 0x51 /* ADT7461 */ + && (reg_config1 & 0x1B) == 0x00 + && reg_convrate <= 0x0A) { + name = "adt7461"; + } + } else + if (man_id == 0x4D) { /* Maxim */ + /* + * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id + * register. Reading from that address will return the last + * read value, which in our case is those of the man_id + * register. Likewise, the config1 register seems to lack a + * low nibble, so the value will be those of the previous + * read, so in our case those of the man_id register. + */ + if (chip_id == man_id + && (address == 0x4C || address == 0x4D) + && (reg_config1 & 0x1F) == (man_id & 0x0F) + && reg_convrate <= 0x09) { + name = "max6657"; + } else + /* + * The chip_id register of the MAX6680 and MAX6681 holds the + * revision of the chip. The lowest bit of the config1 register + * is unused and should return zero when read, so should the + * second to last bit of config1 (software reset). + */ + if (chip_id == 0x01 + && (reg_config1 & 0x03) == 0x00 + && reg_convrate <= 0x07) { + name = "max6680"; + } else + /* + * The chip_id register of the MAX6646/6647/6649 holds the + * revision of the chip. The lowest 6 bits of the config1 + * register are unused and should return zero when read. + */ + if (chip_id == 0x59 + && (reg_config1 & 0x3f) == 0x00 + && reg_convrate <= 0x07) { + name = "max6646"; } } - /* Fill the i2c board info */ - if (kind == lm90) { - name = "lm90"; - } else if (kind == adm1032) { - name = "adm1032"; - /* The ADM1032 supports PEC, but only if combined - transactions are not used. */ - if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) - info->flags |= I2C_CLIENT_PEC; - } else if (kind == lm99) { - name = "lm99"; - } else if (kind == lm86) { - name = "lm86"; - } else if (kind == max6657) { - name = "max6657"; - } else if (kind == max6680) { - name = "max6680"; - } else if (kind == adt7461) { - name = "adt7461"; - } else if (kind == max6646) { - name = "max6646"; + if (!name) { /* identification failed */ + dev_dbg(&adapter->dev, + "Unsupported chip at 0x%02x (man_id=0x%02X, " + "chip_id=0x%02X)\n", address, man_id, chip_id); + return -ENODEV; } + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index b2e00c5a7eec..47ac698709dc 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -323,31 +323,22 @@ static int lm92_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + u8 config; + u16 man_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - /* A negative kind means that the driver was loaded with no force - parameter (default), so we must identify the chip. */ - if (kind < 0) { - u8 config = i2c_smbus_read_byte_data(new_client, - LM92_REG_CONFIG); - u16 man_id = i2c_smbus_read_word_data(new_client, - LM92_REG_MAN_ID); - - if ((config & 0xe0) == 0x00 - && man_id == 0x0180) { - pr_info("lm92: Found National Semiconductor LM92 chip\n"); - kind = lm92; - } else - if (max6635_check(new_client)) { - pr_info("lm92: Found Maxim MAX6635 chip\n"); - kind = lm92; /* No separate prefix */ - } - else - return -ENODEV; - } + config = i2c_smbus_read_byte_data(new_client, LM92_REG_CONFIG); + man_id = i2c_smbus_read_word_data(new_client, LM92_REG_MAN_ID); + + if ((config & 0xe0) == 0x00 && man_id == 0x0180) + pr_info("lm92: Found National Semiconductor LM92 chip\n"); + else if (max6635_check(new_client)) + pr_info("lm92: Found Maxim MAX6635 chip\n"); + else + return -ENODEV; strlcpy(info->type, "lm92", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index fc36cadf36fb..124dd7cea54c 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -928,7 +928,7 @@ static void lm93_update_client_common(struct lm93_data *data, data->prochot_interval = lm93_read_byte(client, LM93_REG_PROCHOT_INTERVAL); - /* Fan Boost Termperature registers */ + /* Fan Boost Temperature registers */ for (i = 0; i < 4; i++) data->boost[i] = lm93_read_byte(client, LM93_REG_BOOST(i)); @@ -2505,34 +2505,24 @@ static int lm93_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int mfr, ver; if (!i2c_check_functionality(adapter, LM93_SMBUS_FUNC_MIN)) return -ENODEV; /* detection */ - if (kind < 0) { - int mfr = lm93_read_byte(client, LM93_REG_MFR_ID); - - if (mfr != 0x01) { - dev_dbg(&adapter->dev,"detect failed, " - "bad manufacturer id 0x%02x!\n", mfr); - return -ENODEV; - } + mfr = lm93_read_byte(client, LM93_REG_MFR_ID); + if (mfr != 0x01) { + dev_dbg(&adapter->dev, + "detect failed, bad manufacturer id 0x%02x!\n", mfr); + return -ENODEV; } - if (kind <= 0) { - int ver = lm93_read_byte(client, LM93_REG_VER); - - if ((ver == LM93_MFR_ID) || (ver == LM93_MFR_ID_PROTOTYPE)) { - kind = lm93; - } else { - dev_dbg(&adapter->dev,"detect failed, " - "bad version id 0x%02x!\n", ver); - if (kind == 0) - dev_dbg(&adapter->dev, - "(ignored 'force' parameter)\n"); - return -ENODEV; - } + ver = lm93_read_byte(client, LM93_REG_VER); + if (ver != LM93_MFR_ID && ver != LM93_MFR_ID_PROTOTYPE) { + dev_dbg(&adapter->dev, + "detect failed, bad version id 0x%02x!\n", ver); + return -ENODEV; } strlcpy(info->type, "lm93", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index e34f9e402a2c..906b896cf1d0 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -315,51 +315,23 @@ static int lm95241_detect(struct i2c_client *new_client, int kind, { struct i2c_adapter *adapter = new_client->adapter; int address = new_client->addr; - const char *name = ""; + const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - if (kind < 0) { /* detection */ - if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) - != MANUFACTURER_ID) - || (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) - < DEFAULT_REVISION)) { - dev_dbg(&adapter->dev, - "LM95241 detection failed at 0x%02x.\n", - address); - return -ENODEV; - } - } - - if (kind <= 0) { /* identification */ - if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) - == MANUFACTURER_ID) - && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) - >= DEFAULT_REVISION)) { - - kind = lm95241; - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, "Unsupported chip\n"); - return -ENODEV; - } - } + if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) + == MANUFACTURER_ID) + && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) + >= DEFAULT_REVISION)) { + name = "lm95241"; + } else { + dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n", + address); + return -ENODEV; } /* Fill the i2c board info */ - if (kind == lm95241) - name = "lm95241"; strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 7897754f3a5c..7fcf5ff89e7f 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -226,58 +226,34 @@ static const struct attribute_group max1619_group = { */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int max1619_detect(struct i2c_client *new_client, int kind, +static int max1619_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; - u8 reg_config=0, reg_convrate=0, reg_status=0; + struct i2c_adapter *adapter = client->adapter; + u8 reg_config, reg_convrate, reg_status, man_id, chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - if (kind < 0) { /* detection */ - reg_config = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_CONFIG); - reg_convrate = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_CONVRATE); - reg_status = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_STATUS); - if ((reg_config & 0x03) != 0x00 - || reg_convrate > 0x07 || (reg_status & 0x61 ) !=0x00) { - dev_dbg(&adapter->dev, - "MAX1619 detection failed at 0x%02x.\n", - new_client->addr); - return -ENODEV; - } + /* detection */ + reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); + reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE); + reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS); + if ((reg_config & 0x03) != 0x00 + || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) { + dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u8 man_id, chip_id; - - man_id = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_CHIP_ID); - - if ((man_id == 0x4D) && (chip_id == 0x04)) - kind = max1619; - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } + /* identification */ + man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID); + if (man_id != 0x4D || chip_id != 0x04) { + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, "max1619", I2C_NAME_SIZE); diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 58f66be61b1f..1da561e0cb37 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -534,7 +534,7 @@ static int max6650_detect(struct i2c_client *client, int kind, struct i2c_adapter *adapter = client->adapter; int address = client->addr; - dev_dbg(&adapter->dev, "max6650_detect called, kind = %d\n", kind); + dev_dbg(&adapter->dev, "max6650_detect called\n"); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support " @@ -542,23 +542,7 @@ static int max6650_detect(struct i2c_client *client, int kind, return -ENODEV; } - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip (actually there is only - * one possible kind of chip for now, max6650). A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - * - * Currently I can find no way to distinguish between a MAX6650 and - * a MAX6651. This driver has only been tried on the former. - */ - - if ((kind < 0) && - ( (i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0) + if (((i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0) ||(i2c_smbus_read_byte_data(client, MAX6650_REG_GPIO_STAT) & 0xE0) ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN) & 0xE0) ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM) & 0xE0) diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c new file mode 100644 index 000000000000..883fa8197da4 --- /dev/null +++ b/drivers/hwmon/mc13783-adc.c @@ -0,0 +1,236 @@ +/* + * Driver for the Freescale Semiconductor MC13783 adc. + * + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009 Sascha Hauer, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/mfd/mc13783-private.h> +#include <linux/platform_device.h> +#include <linux/hwmon-sysfs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/hwmon.h> +#include <linux/init.h> +#include <linux/err.h> + +#define MC13783_ADC_NAME "mc13783-adc" + +struct mc13783_adc_priv { + struct mc13783 *mc13783; + struct device *hwmon_dev; +}; + +static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + return sprintf(buf, "mc13783_adc\n"); +} + +static int mc13783_adc_read(struct device *dev, + struct device_attribute *devattr, unsigned int *val) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + unsigned int channel = attr->index; + unsigned int sample[4]; + int ret; + + ret = mc13783_adc_do_conversion(priv->mc13783, + MC13783_ADC_MODE_MULT_CHAN, + channel, sample); + if (ret) + return ret; + + channel &= 0x7; + + *val = (sample[channel % 4] >> (channel > 3 ? 14 : 2)) & 0x3ff; + + return 0; +} + +static ssize_t mc13783_adc_read_bp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + unsigned val; + int ret = mc13783_adc_read(dev, devattr, &val); + + if (ret) + return ret; + + /* + * BP (channel 2) reports with offset 2.4V to the actual value to fit + * the input range of the ADC. unit = 2.25mV = 9/4 mV. + */ + val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400; + + return sprintf(buf, "%u\n", val); +} + +static ssize_t mc13783_adc_read_gp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + unsigned val; + int ret = mc13783_adc_read(dev, devattr, &val); + + if (ret) + return ret; + + /* + * input range is [0, 2.3V], val has 10 bits, so each bit + * is worth 9/4 mV. + */ + val = DIV_ROUND_CLOSEST(val * 9, 4); + + return sprintf(buf, "%u\n", val); +} + +static DEVICE_ATTR(name, S_IRUGO, mc13783_adc_show_name, NULL); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, mc13783_adc_read_bp, NULL, 2); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, mc13783_adc_read_gp, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, mc13783_adc_read_gp, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, mc13783_adc_read_gp, NULL, 7); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, mc13783_adc_read_gp, NULL, 8); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, mc13783_adc_read_gp, NULL, 9); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, mc13783_adc_read_gp, NULL, 10); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, mc13783_adc_read_gp, NULL, 11); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, mc13783_adc_read_gp, NULL, 12); +static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, mc13783_adc_read_gp, NULL, 13); +static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, mc13783_adc_read_gp, NULL, 14); +static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, mc13783_adc_read_gp, NULL, 15); + +static struct attribute *mc13783_attr[] = { + &dev_attr_name.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group mc13783_group = { + .attrs = mc13783_attr, +}; + +/* last four channels may be occupied by the touchscreen */ +static struct attribute *mc13783_attr_ts[] = { + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in13_input.dev_attr.attr, + &sensor_dev_attr_in14_input.dev_attr.attr, + &sensor_dev_attr_in15_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group mc13783_group_ts = { + .attrs = mc13783_attr_ts, +}; + +static int __init mc13783_adc_probe(struct platform_device *pdev) +{ + struct mc13783_adc_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mc13783 = dev_get_drvdata(pdev->dev.parent); + + platform_set_drvdata(pdev, priv); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group); + if (ret) + goto out_err_create1; + + if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts); + if (ret) + goto out_err_create2; + + priv->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + dev_err(&pdev->dev, + "hwmon_device_register failed with %d.\n", ret); + goto out_err_register; + } + + + return 0; + +out_err_register: + + if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); +out_err_create2: + + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); +out_err_create1: + + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return ret; +} + +static int __devexit mc13783_adc_remove(struct platform_device *pdev) +{ + struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + + hwmon_device_unregister(priv->hwmon_dev); + + if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); + + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); + + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return 0; +} + +static struct platform_driver mc13783_adc_driver = { + .remove = __devexit_p(mc13783_adc_remove), + .driver = { + .owner = THIS_MODULE, + .name = MC13783_ADC_NAME, + }, +}; + +static int __init mc13783_adc_init(void) +{ + return platform_driver_probe(&mc13783_adc_driver, mc13783_adc_probe); +} + +static void __exit mc13783_adc_exit(void) +{ + platform_driver_unregister(&mc13783_adc_driver); +} + +module_init(mc13783_adc_init); +module_exit(mc13783_adc_exit); + +MODULE_DESCRIPTION("MC13783 ADC driver"); +MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" MC13783_ADC_NAME); diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index 71835412529f..3f3f9a47acfd 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -323,7 +323,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) } for (i = 0; i < ARRAY_SIZE(pdata->in); i++) { - struct s3c24xx_adc_hwmon_incfg *cfg = pdata->in[i]; + struct s3c_hwmon_chcfg *cfg = pdata->in[i]; if (!cfg) continue; @@ -333,7 +333,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) "channel %d multiplier too large\n", i); - if (cfg->divider == 0) { + if (cfg->div == 0) { dev_err(&dev->dev, "channel %d divider zero\n", i); continue; } diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index 8bb5cb532d4d..4d88c045781c 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -491,24 +491,22 @@ static int smsc47m192_detect(struct i2c_client *client, int kind, return -ENODEV; /* Detection criteria from sensors_detect script */ - if (kind < 0) { - if (i2c_smbus_read_byte_data(client, + version = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VERSION); + if (i2c_smbus_read_byte_data(client, SMSC47M192_REG_COMPANY_ID) == 0x55 - && ((version = i2c_smbus_read_byte_data(client, - SMSC47M192_REG_VERSION)) & 0xf0) == 0x20 - && (i2c_smbus_read_byte_data(client, + && (version & 0xf0) == 0x20 + && (i2c_smbus_read_byte_data(client, SMSC47M192_REG_VID) & 0x70) == 0x00 - && (i2c_smbus_read_byte_data(client, + && (i2c_smbus_read_byte_data(client, SMSC47M192_REG_VID4) & 0xfe) == 0x80) { - dev_info(&adapter->dev, - "found SMSC47M192 or compatible, " - "version 2, stepping A%d\n", version & 0x0f); - } else { - dev_dbg(&adapter->dev, - "SMSC47M192 detection failed at 0x%02x\n", - client->addr); - return -ENODEV; - } + dev_info(&adapter->dev, + "found SMSC47M192 or compatible, " + "version 2, stepping A%d\n", version & 0x0f); + } else { + dev_dbg(&adapter->dev, + "SMSC47M192 detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } strlcpy(info->type, "smsc47m192", I2C_NAME_SIZE); diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 7d97431e132f..4b793849c738 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -36,7 +36,11 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD_2(thmc50, adm1022); -I2C_CLIENT_MODULE_PARM(adm1022_temp3, "List of adapter,address pairs " + +static unsigned short adm1022_temp3[16]; +static unsigned int adm1022_temp3_num; +module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0); +MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs " "to enable 3rd temperature (ADM1022 only)"); /* Many THMC50 constants specified below */ @@ -289,7 +293,6 @@ static int thmc50_detect(struct i2c_client *client, int kind, unsigned revision; unsigned config; struct i2c_adapter *adapter = client->adapter; - int err = 0; const char *type_name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -301,31 +304,13 @@ static int thmc50_detect(struct i2c_client *client, int kind, pr_debug("thmc50: Probing for THMC50 at 0x%2X on bus %d\n", client->addr, i2c_adapter_id(client->adapter)); - /* Now, we do the remaining detection. */ company = i2c_smbus_read_byte_data(client, THMC50_REG_COMPANY_ID); revision = i2c_smbus_read_byte_data(client, THMC50_REG_DIE_CODE); config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF); + if (revision < 0xc0 || (config & 0x10)) + return -ENODEV; - if (kind == 0) - kind = thmc50; - else if (kind < 0) { - err = -ENODEV; - if (revision >= 0xc0 && ((config & 0x10) == 0)) { - if (company == 0x49) { - kind = thmc50; - err = 0; - } else if (company == 0x41) { - kind = adm1022; - err = 0; - } - } - } - if (err == -ENODEV) { - pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n"); - return err; - } - - if (kind == adm1022) { + if (company == 0x41) { int id = i2c_adapter_id(client->adapter); int i; @@ -340,9 +325,13 @@ static int thmc50_detect(struct i2c_client *client, int kind, config); break; } - } else { + } else if (company == 0x49) { type_name = "thmc50"; + } else { + pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n"); + return -ENODEV; } + pr_debug("thmc50: Detected %s (version %x, revision %x)\n", type_name, (revision >> 4) - 0xc, revision & 0xf); diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 7b34f2cd08bb..ee9673467c4a 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -488,46 +488,43 @@ static void tmp401_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config); } -static int tmp401_detect(struct i2c_client *client, int kind, +static int tmp401_detect(struct i2c_client *client, int _kind, struct i2c_board_info *info) { + enum chips kind; struct i2c_adapter *adapter = client->adapter; + u8 reg; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; /* Detect and identify the chip */ - if (kind <= 0) { - u8 reg; - - reg = i2c_smbus_read_byte_data(client, - TMP401_MANUFACTURER_ID_REG); - if (reg != TMP401_MANUFACTURER_ID) - return -ENODEV; - - reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG); - - switch (reg) { - case TMP401_DEVICE_ID: - kind = tmp401; - break; - case TMP411_DEVICE_ID: - kind = tmp411; - break; - default: - return -ENODEV; - } + reg = i2c_smbus_read_byte_data(client, TMP401_MANUFACTURER_ID_REG); + if (reg != TMP401_MANUFACTURER_ID) + return -ENODEV; - reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); - if (reg & 0x1b) - return -ENODEV; + reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG); - reg = i2c_smbus_read_byte_data(client, - TMP401_CONVERSION_RATE_READ); - /* Datasheet says: 0x1-0x6 */ - if (reg > 15) - return -ENODEV; + switch (reg) { + case TMP401_DEVICE_ID: + kind = tmp401; + break; + case TMP411_DEVICE_ID: + kind = tmp411; + break; + default: + return -ENODEV; } + + reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); + if (reg & 0x1b) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, TMP401_CONVERSION_RATE_READ); + /* Datasheet says: 0x1-0x6 */ + if (reg > 15) + return -ENODEV; + strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 20924343431b..bb5464a289ca 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -223,39 +223,36 @@ static int tmp421_init_client(struct i2c_client *client) return 0; } -static int tmp421_detect(struct i2c_client *client, int kind, +static int tmp421_detect(struct i2c_client *client, int _kind, struct i2c_board_info *info) { + enum chips kind; struct i2c_adapter *adapter = client->adapter; const char *names[] = { "TMP421", "TMP422", "TMP423" }; + u8 reg; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - u8 reg; - - reg = i2c_smbus_read_byte_data(client, - TMP421_MANUFACTURER_ID_REG); - if (reg != TMP421_MANUFACTURER_ID) - return -ENODEV; - - reg = i2c_smbus_read_byte_data(client, - TMP421_DEVICE_ID_REG); - switch (reg) { - case TMP421_DEVICE_ID: - kind = tmp421; - break; - case TMP422_DEVICE_ID: - kind = tmp422; - break; - case TMP423_DEVICE_ID: - kind = tmp423; - break; - default: - return -ENODEV; - } + reg = i2c_smbus_read_byte_data(client, TMP421_MANUFACTURER_ID_REG); + if (reg != TMP421_MANUFACTURER_ID) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, TMP421_DEVICE_ID_REG); + switch (reg) { + case TMP421_DEVICE_ID: + kind = tmp421; + break; + case TMP422_DEVICE_ID: + kind = tmp422; + break; + case TMP423_DEVICE_ID: + kind = tmp423; + break; + default: + return -ENODEV; } + strlcpy(info->type, tmp421_id[kind - 1].name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n", names[kind - 1], client->addr); diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 2be28ac4ede0..b257c7223733 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -59,10 +59,11 @@ static struct platform_device *pdev; #define DRVNAME "w83627hf" enum chips { w83627hf, w83627thf, w83697hf, w83637hf, w83687thf }; -static u16 force_addr; -module_param(force_addr, ushort, 0); -MODULE_PARM_DESC(force_addr, - "Initialize the base address of the sensors"); +struct w83627hf_sio_data { + enum chips type; + int sioaddr; +}; + static u8 force_i2c = 0x1f; module_param(force_i2c, byte, 0); MODULE_PARM_DESC(force_i2c, @@ -77,9 +78,7 @@ module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); /* modified from kernel/include/traps.c */ -static int REG; /* The register to read/write */ #define DEV 0x07 /* Register: Logical device select */ -static int VAL; /* The value to read/write */ /* logical device numbers for superio_select (below) */ #define W83627HF_LD_FDC 0x00 @@ -109,37 +108,37 @@ static int VAL; /* The value to read/write */ #define W83687THF_VID_DATA 0xF1 /* w83687thf only */ static inline void -superio_outb(int reg, int val) +superio_outb(struct w83627hf_sio_data *sio, int reg, int val) { - outb(reg, REG); - outb(val, VAL); + outb(reg, sio->sioaddr); + outb(val, sio->sioaddr + 1); } static inline int -superio_inb(int reg) +superio_inb(struct w83627hf_sio_data *sio, int reg) { - outb(reg, REG); - return inb(VAL); + outb(reg, sio->sioaddr); + return inb(sio->sioaddr + 1); } static inline void -superio_select(int ld) +superio_select(struct w83627hf_sio_data *sio, int ld) { - outb(DEV, REG); - outb(ld, VAL); + outb(DEV, sio->sioaddr); + outb(ld, sio->sioaddr + 1); } static inline void -superio_enter(void) +superio_enter(struct w83627hf_sio_data *sio) { - outb(0x87, REG); - outb(0x87, REG); + outb(0x87, sio->sioaddr); + outb(0x87, sio->sioaddr); } static inline void -superio_exit(void) +superio_exit(struct w83627hf_sio_data *sio) { - outb(0xAA, REG); + outb(0xAA, sio->sioaddr); } #define W627_DEVID 0x52 @@ -380,10 +379,6 @@ struct w83627hf_data { u8 vrm_ovt; /* Register value, 627THF/637HF/687THF only */ }; -struct w83627hf_sio_data { - enum chips type; -}; - static int w83627hf_probe(struct platform_device *pdev); static int __devexit w83627hf_remove(struct platform_device *pdev); @@ -1140,11 +1135,8 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, "W83687THF", }; - REG = sioaddr; - VAL = sioaddr + 1; - - superio_enter(); - val = force_id ? force_id : superio_inb(DEVID); + superio_enter(sio_data); + val = force_id ? force_id : superio_inb(sio_data, DEVID); switch (val) { case W627_DEVID: sio_data->type = w83627hf; @@ -1168,16 +1160,9 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, goto exit; } - superio_select(W83627HF_LD_HWM); - force_addr &= WINB_ALIGNMENT; - if (force_addr) { - printk(KERN_WARNING DRVNAME ": Forcing address 0x%x\n", - force_addr); - superio_outb(WINB_BASE_REG, force_addr >> 8); - superio_outb(WINB_BASE_REG + 1, force_addr & 0xff); - } - val = (superio_inb(WINB_BASE_REG) << 8) | - superio_inb(WINB_BASE_REG + 1); + superio_select(sio_data, W83627HF_LD_HWM); + val = (superio_inb(sio_data, WINB_BASE_REG) << 8) | + superio_inb(sio_data, WINB_BASE_REG + 1); *addr = val & WINB_ALIGNMENT; if (*addr == 0) { printk(KERN_WARNING DRVNAME ": Base address not set, " @@ -1185,18 +1170,19 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, goto exit; } - val = superio_inb(WINB_ACT_REG); + val = superio_inb(sio_data, WINB_ACT_REG); if (!(val & 0x01)) { printk(KERN_WARNING DRVNAME ": Enabling HWM logical device\n"); - superio_outb(WINB_ACT_REG, val | 0x01); + superio_outb(sio_data, WINB_ACT_REG, val | 0x01); } err = 0; + sio_data->sioaddr = sioaddr; pr_info(DRVNAME ": Found %s chip at %#x\n", names[sio_data->type], *addr); exit: - superio_exit(); + superio_exit(sio_data); return err; } @@ -1511,20 +1497,21 @@ static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) static int __devinit w83627thf_read_gpio5(struct platform_device *pdev) { + struct w83627hf_sio_data *sio_data = pdev->dev.platform_data; int res = 0xff, sel; - superio_enter(); - superio_select(W83627HF_LD_GPIO5); + superio_enter(sio_data); + superio_select(sio_data, W83627HF_LD_GPIO5); /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(W83627THF_GPIO5_EN) & (1<<3))) { + if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) { dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n"); goto exit; } /* Make sure the pins are configured for input There must be at least five (VRM 9), and possibly 6 (VRM 10) */ - sel = superio_inb(W83627THF_GPIO5_IOSR) & 0x3f; + sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f; if ((sel & 0x1f) != 0x1f) { dev_dbg(&pdev->dev, "GPIO5 not configured for VID " "function\n"); @@ -1532,37 +1519,38 @@ static int __devinit w83627thf_read_gpio5(struct platform_device *pdev) } dev_info(&pdev->dev, "Reading VID from GPIO5\n"); - res = superio_inb(W83627THF_GPIO5_DR) & sel; + res = superio_inb(sio_data, W83627THF_GPIO5_DR) & sel; exit: - superio_exit(); + superio_exit(sio_data); return res; } static int __devinit w83687thf_read_vid(struct platform_device *pdev) { + struct w83627hf_sio_data *sio_data = pdev->dev.platform_data; int res = 0xff; - superio_enter(); - superio_select(W83627HF_LD_HWM); + superio_enter(sio_data); + superio_select(sio_data, W83627HF_LD_HWM); /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(W83687THF_VID_EN) & (1 << 2))) { + if (!(superio_inb(sio_data, W83687THF_VID_EN) & (1 << 2))) { dev_dbg(&pdev->dev, "VID disabled, no VID function\n"); goto exit; } /* Make sure the pins are configured for input */ - if (!(superio_inb(W83687THF_VID_CFG) & (1 << 4))) { + if (!(superio_inb(sio_data, W83687THF_VID_CFG) & (1 << 4))) { dev_dbg(&pdev->dev, "VID configured as output, " "no VID function\n"); goto exit; } - res = superio_inb(W83687THF_VID_DATA) & 0x3f; + res = superio_inb(sio_data, W83687THF_VID_DATA) & 0x3f; exit: - superio_exit(); + superio_exit(sio_data); return res; } diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index d27ed1bac002..7ab7967da0a0 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1054,11 +1054,11 @@ static int w83781d_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { - int val1 = 0, val2; + int val1, val2; struct w83781d_data *isa = w83781d_data_if_isa(); struct i2c_adapter *adapter = client->adapter; int address = client->addr; - const char *client_name = ""; + const char *client_name; enum vendor { winbond, asus } vendid; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -1070,98 +1070,73 @@ w83781d_detect(struct i2c_client *client, int kind, if (isa) mutex_lock(&isa->update_lock); - /* The w8378?d may be stuck in some other bank than bank 0. This may - make reading other information impossible. Specify a force=... or - force_*=... parameter, and the Winbond will be reset to the right - bank. */ - if (kind < 0) { - if (i2c_smbus_read_byte_data - (client, W83781D_REG_CONFIG) & 0x80) { - dev_dbg(&adapter->dev, "Detection of w83781d chip " - "failed at step 3\n"); - goto err_nodev; - } - val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK); - val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); - /* Check for Winbond or Asus ID if in bank 0 */ - if ((!(val1 & 0x07)) && - (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) - || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { - dev_dbg(&adapter->dev, "Detection of w83781d chip " - "failed at step 4\n"); + if (i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG) & 0x80) { + dev_dbg(&adapter->dev, + "Detection of w83781d chip failed at step 3\n"); + goto err_nodev; + } + + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK); + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); + /* Check for Winbond or Asus ID if in bank 0 */ + if (!(val1 & 0x07) && + ((!(val1 & 0x80) && val2 != 0xa3 && val2 != 0xc3) || + ( (val1 & 0x80) && val2 != 0x5c && val2 != 0x12))) { + dev_dbg(&adapter->dev, + "Detection of w83781d chip failed at step 4\n"); + goto err_nodev; + } + /* If Winbond SMBus, check address at 0x48. + Asus doesn't support, except for as99127f rev.2 */ + if ((!(val1 & 0x80) && val2 == 0xa3) || + ( (val1 & 0x80) && val2 == 0x5c)) { + if (i2c_smbus_read_byte_data(client, W83781D_REG_I2C_ADDR) + != address) { + dev_dbg(&adapter->dev, + "Detection of w83781d chip failed at step 5\n"); goto err_nodev; } - /* If Winbond SMBus, check address at 0x48. - Asus doesn't support, except for as99127f rev.2 */ - if ((!(val1 & 0x80) && (val2 == 0xa3)) || - ((val1 & 0x80) && (val2 == 0x5c))) { - if (i2c_smbus_read_byte_data - (client, W83781D_REG_I2C_ADDR) != address) { - dev_dbg(&adapter->dev, "Detection of w83781d " - "chip failed at step 5\n"); - goto err_nodev; - } - } } - /* We have either had a force parameter, or we have already detected the - Winbond. Put it now into bank 0 and Vendor ID High Byte */ + /* Put it now into bank 0 and Vendor ID High Byte */ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, (i2c_smbus_read_byte_data(client, W83781D_REG_BANK) & 0x78) | 0x80); - /* Determine the chip type. */ - if (kind <= 0) { - /* get vendor ID */ - val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); - if (val2 == 0x5c) - vendid = winbond; - else if (val2 == 0x12) - vendid = asus; - else { - dev_dbg(&adapter->dev, "w83781d chip vendor is " - "neither Winbond nor Asus\n"); - goto err_nodev; - } - - val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID); - if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) - kind = w83781d; - else if (val1 == 0x30 && vendid == winbond) - kind = w83782d; - else if (val1 == 0x40 && vendid == winbond && address == 0x2d) - kind = w83783s; - else if (val1 == 0x31) - kind = as99127f; - else { - if (kind == 0) - dev_warn(&adapter->dev, "Ignoring 'force' " - "parameter for unknown chip at " - "address 0x%02x\n", address); - goto err_nodev; - } - - if ((kind == w83781d || kind == w83782d) - && w83781d_alias_detect(client, val1)) { - dev_dbg(&adapter->dev, "Device at 0x%02x appears to " - "be the same as ISA device\n", address); - goto err_nodev; - } + /* Get the vendor ID */ + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); + if (val2 == 0x5c) + vendid = winbond; + else if (val2 == 0x12) + vendid = asus; + else { + dev_dbg(&adapter->dev, + "w83781d chip vendor is neither Winbond nor Asus\n"); + goto err_nodev; } - if (isa) - mutex_unlock(&isa->update_lock); - - if (kind == w83781d) { + /* Determine the chip type. */ + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID); + if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) client_name = "w83781d"; - } else if (kind == w83782d) { + else if (val1 == 0x30 && vendid == winbond) client_name = "w83782d"; - } else if (kind == w83783s) { + else if (val1 == 0x40 && vendid == winbond && address == 0x2d) client_name = "w83783s"; - } else if (kind == as99127f) { + else if (val1 == 0x31) client_name = "as99127f"; + else + goto err_nodev; + + if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + goto err_nodev; } + if (isa) + mutex_unlock(&isa->update_lock); + strlcpy(info->type, client_name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 97851c5ba3a3..0410bf12c521 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1270,56 +1270,32 @@ static int w83791d_detect(struct i2c_client *client, int kind, return -ENODEV; } - /* The w83791d may be stuck in some other bank than bank 0. This may - make reading other information impossible. Specify a force=... - parameter, and the Winbond will be reset to the right bank. */ - if (kind < 0) { - if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) { - return -ENODEV; - } - val1 = w83791d_read(client, W83791D_REG_BANK); - val2 = w83791d_read(client, W83791D_REG_CHIPMAN); - /* Check for Winbond ID if in bank 0 */ - if (!(val1 & 0x07)) { - /* yes it is Bank0 */ - if (((!(val1 & 0x80)) && (val2 != 0xa3)) || - ((val1 & 0x80) && (val2 != 0x5c))) { - return -ENODEV; - } - } - /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR - should match */ - if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) { + if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) + return -ENODEV; + + val1 = w83791d_read(client, W83791D_REG_BANK); + val2 = w83791d_read(client, W83791D_REG_CHIPMAN); + /* Check for Winbond ID if in bank 0 */ + if (!(val1 & 0x07)) { + if ((!(val1 & 0x80) && val2 != 0xa3) || + ( (val1 & 0x80) && val2 != 0x5c)) { return -ENODEV; } } + /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR + should match */ + if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) + return -ENODEV; - /* We either have a force parameter or we have reason to - believe it is a Winbond chip. Either way, we want bank 0 and - Vendor ID high byte */ + /* We want bank 0 and Vendor ID high byte */ val1 = w83791d_read(client, W83791D_REG_BANK) & 0x78; w83791d_write(client, W83791D_REG_BANK, val1 | 0x80); /* Verify it is a Winbond w83791d */ - if (kind <= 0) { - /* get vendor ID */ - val2 = w83791d_read(client, W83791D_REG_CHIPMAN); - if (val2 != 0x5c) { /* the vendor is NOT Winbond */ - return -ENODEV; - } - val1 = w83791d_read(client, W83791D_REG_WCHIPID); - if (val1 == 0x71) { - kind = w83791d; - } else { - if (kind == 0) - dev_warn(&adapter->dev, - "w83791d: Ignoring 'force' parameter " - "for unknown chip at adapter %d, " - "address 0x%02x\n", - i2c_adapter_id(adapter), address); - return -ENODEV; - } - } + val1 = w83791d_read(client, W83791D_REG_WCHIPID); + val2 = w83791d_read(client, W83791D_REG_CHIPMAN); + if (val1 != 0x71 || val2 != 0x5c) + return -ENODEV; strlcpy(info->type, "w83791d", I2C_NAME_SIZE); diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 2be16194ddf3..38978851333f 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -1273,58 +1273,33 @@ w83792d_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) return -ENODEV; } - /* The w83792d may be stuck in some other bank than bank 0. This may - make reading other information impossible. Specify a force=... or - force_*=... parameter, and the Winbond will be reset to the right - bank. */ - if (kind < 0) { - if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80) { - return -ENODEV; - } - val1 = w83792d_read_value(client, W83792D_REG_BANK); - val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN); - /* Check for Winbond ID if in bank 0 */ - if (!(val1 & 0x07)) { /* is Bank0 */ - if (((!(val1 & 0x80)) && (val2 != 0xa3)) || - ((val1 & 0x80) && (val2 != 0x5c))) { - return -ENODEV; - } - } - /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR - should match */ - if (w83792d_read_value(client, - W83792D_REG_I2C_ADDR) != address) { + if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80) + return -ENODEV; + + val1 = w83792d_read_value(client, W83792D_REG_BANK); + val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN); + /* Check for Winbond ID if in bank 0 */ + if (!(val1 & 0x07)) { /* is Bank0 */ + if ((!(val1 & 0x80) && val2 != 0xa3) || + ( (val1 & 0x80) && val2 != 0x5c)) return -ENODEV; - } } + /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR + should match */ + if (w83792d_read_value(client, W83792D_REG_I2C_ADDR) != address) + return -ENODEV; - /* We have either had a force parameter, or we have already detected the - Winbond. Put it now into bank 0 and Vendor ID High Byte */ + /* Put it now into bank 0 and Vendor ID High Byte */ w83792d_write_value(client, W83792D_REG_BANK, (w83792d_read_value(client, W83792D_REG_BANK) & 0x78) | 0x80); /* Determine the chip type. */ - if (kind <= 0) { - /* get vendor ID */ - val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN); - if (val2 != 0x5c) { /* the vendor is NOT Winbond */ - return -ENODEV; - } - val1 = w83792d_read_value(client, W83792D_REG_WCHIPID); - if (val1 == 0x7a) { - kind = w83792d; - } else { - if (kind == 0) - dev_warn(&adapter->dev, - "w83792d: Ignoring 'force' parameter for" - " unknown chip at adapter %d, address" - " 0x%02x\n", i2c_adapter_id(adapter), - address); - return -ENODEV; - } - } + val1 = w83792d_read_value(client, W83792D_REG_WCHIPID); + val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN); + if (val1 != 0x7a || val2 != 0x5c) + return -ENODEV; strlcpy(info->type, "w83792d", I2C_NAME_SIZE); diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 47dd398f7258..80a2191bf127 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1164,7 +1164,7 @@ ERROR_SC_0: static int w83793_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { - u8 tmp, bank; + u8 tmp, bank, chip_id; struct i2c_adapter *adapter = client->adapter; unsigned short address = client->addr; @@ -1174,44 +1174,27 @@ static int w83793_detect(struct i2c_client *client, int kind, bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL); - if (kind < 0) { - tmp = bank & 0x80 ? 0x5c : 0xa3; - /* Check Winbond vendor ID */ - if (tmp != i2c_smbus_read_byte_data(client, - W83793_REG_VENDORID)) { - pr_debug("w83793: Detection failed at check " - "vendor id\n"); - return -ENODEV; - } - - /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR - should match */ - if ((bank & 0x07) == 0 - && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) != - (address << 1)) { - pr_debug("w83793: Detection failed at check " - "i2c addr\n"); - return -ENODEV; - } - + tmp = bank & 0x80 ? 0x5c : 0xa3; + /* Check Winbond vendor ID */ + if (tmp != i2c_smbus_read_byte_data(client, W83793_REG_VENDORID)) { + pr_debug("w83793: Detection failed at check vendor id\n"); + return -ENODEV; } - /* We have either had a force parameter, or we have already detected the - Winbond. Determine the chip type now */ - - if (kind <= 0) { - if (0x7b == i2c_smbus_read_byte_data(client, - W83793_REG_CHIPID)) { - kind = w83793; - } else { - if (kind == 0) - dev_warn(&adapter->dev, "w83793: Ignoring " - "'force' parameter for unknown chip " - "at address 0x%02x\n", address); - return -ENODEV; - } + /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR + should match */ + if ((bank & 0x07) == 0 + && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) != + (address << 1)) { + pr_debug("w83793: Detection failed at check i2c addr\n"); + return -ENODEV; } + /* Determine the chip type now */ + chip_id = i2c_smbus_read_byte_data(client, W83793_REG_CHIPID); + if (chip_id != 0x7b) + return -ENODEV; + strlcpy(info->type, "w83793", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index ea295b9fc4f4..9b6c4c10fba7 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -1,7 +1,7 @@ /* * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> * * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made * by Winbond. It reports a single external temperature with a 1 deg @@ -146,60 +146,36 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 1); */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int w83l785ts_detect(struct i2c_client *new_client, int kind, +static int w83l785ts_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; + u16 man_id; + u8 chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip (actually there is only - * one possible kind of chip for now, W83L785TS-S). A zero kind means - * that the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - if (kind < 0) { /* detection */ - if (((w83l785ts_read_value(new_client, - W83L785TS_REG_CONFIG, 0) & 0x80) != 0x00) - || ((w83l785ts_read_value(new_client, - W83L785TS_REG_TYPE, 0) & 0xFC) != 0x00)) { - dev_dbg(&adapter->dev, - "W83L785TS-S detection failed at 0x%02x.\n", - new_client->addr); - return -ENODEV; - } + /* detection */ + if ((w83l785ts_read_value(client, W83L785TS_REG_CONFIG, 0) & 0x80) + || (w83l785ts_read_value(client, W83L785TS_REG_TYPE, 0) & 0xFC)) { + dev_dbg(&adapter->dev, + "W83L785TS-S detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u16 man_id; - u8 chip_id; - - man_id = (w83l785ts_read_value(new_client, - W83L785TS_REG_MAN_ID1, 0) << 8) + - w83l785ts_read_value(new_client, - W83L785TS_REG_MAN_ID2, 0); - chip_id = w83l785ts_read_value(new_client, - W83L785TS_REG_CHIP_ID, 0); - - if (man_id == 0x5CA3) { /* Winbond */ - if (chip_id == 0x70) { /* W83L785TS-S */ - kind = w83l785ts; - } - } - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%04X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } + /* Identification */ + man_id = (w83l785ts_read_value(client, W83L785TS_REG_MAN_ID1, 0) << 8) + + w83l785ts_read_value(client, W83L785TS_REG_MAN_ID2, 0); + chip_id = w83l785ts_read_value(client, W83L785TS_REG_CHIP_ID, 0); + + if (man_id != 0x5CA3 /* Winbond */ + || chip_id != 0x70) { /* W83L785TS-S */ + dev_dbg(&adapter->dev, + "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, "w83l785ts", I2C_NAME_SIZE); diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index badca769f350..27da7d2b15fb 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -590,53 +590,31 @@ w83l786ng_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + u16 man_id; + u8 chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { return -ENODEV; } - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip (actually there is only - * one possible kind of chip for now, W83L786NG). A zero kind means - * that the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - if (kind < 0) { /* detection */ - if (((w83l786ng_read_value(client, - W83L786NG_REG_CONFIG) & 0x80) != 0x00)) { - dev_dbg(&adapter->dev, - "W83L786NG detection failed at 0x%02x.\n", - client->addr); - return -ENODEV; - } + /* Detection */ + if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) { + dev_dbg(&adapter->dev, "W83L786NG detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u16 man_id; - u8 chip_id; - - man_id = (w83l786ng_read_value(client, - W83L786NG_REG_MAN_ID1) << 8) + - w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2); - chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID); - - if (man_id == 0x5CA3) { /* Winbond */ - if (chip_id == 0x80) { /* W83L786NG */ - kind = w83l786ng; - } - } + /* Identification */ + man_id = (w83l786ng_read_value(client, W83L786NG_REG_MAN_ID1) << 8) + + w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2); + chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID); - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%04X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } + if (man_id != 0x5CA3 || /* Winbond */ + chip_id != 0x80) { /* W83L786NG */ + dev_dbg(&adapter->dev, + "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, "w83l786ng", I2C_NAME_SIZE); diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index d7ece131b4f4..8d8a00e5a30e 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -5,6 +5,7 @@ menuconfig I2C tristate "I2C support" depends on HAS_IOMEM + select RT_MUTEXES ---help--- I2C (pronounce: I-square-C) is a slow serial bus protocol used in many micro controller applications and developed by Philips. SMBus, diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e8fe7f169e25..5f318ce29770 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -640,22 +640,6 @@ config I2C_TINY_USB This driver can also be built as a module. If so, the module will be called i2c-tiny-usb. -comment "Graphics adapter I2C/DDC channel drivers" - depends on PCI - -config I2C_VOODOO3 - tristate "Voodoo 3 (DEPRECATED)" - depends on PCI - select I2C_ALGOBIT - help - If you say yes to this option, support will be included for the - Voodoo 3 I2C interface. This driver is deprecated and you should - use the tdfxfb driver instead, which additionally provides - framebuffer support. - - This driver can also be built as a module. If so, the module - will be called i2c-voodoo3. - comment "Other I2C/SMBus bus drivers" config I2C_ACORN diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index ff937ac69f5b..302c551977bb 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -61,9 +61,6 @@ obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o -# Graphics adapter I2C/DDC channel drivers -obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o - # Other I2C/SMBus bus drivers obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index d108450df064..8de7d7b87bb0 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -138,7 +138,7 @@ static unsigned short ali1535_smba; Note the differences between kernels with the old PCI BIOS interface and newer kernels with the real PCI interface. In compat.h some things are defined to make the transition easier. */ -static int ali1535_setup(struct pci_dev *dev) +static int __devinit ali1535_setup(struct pci_dev *dev) { int retval = -ENODEV; unsigned char temp; diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index d627fceb790b..e7e3205f1286 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -131,7 +131,7 @@ MODULE_PARM_DESC(force_addr, static struct pci_driver ali15x3_driver; static unsigned short ali15x3_smba; -static int ali15x3_setup(struct pci_dev *ALI15X3_dev) +static int __devinit ali15x3_setup(struct pci_dev *ALI15X3_dev) { u16 a; unsigned char temp; diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c index b444762e9b9f..9e18ef97f156 100644 --- a/drivers/i2c/busses/i2c-designware.c +++ b/drivers/i2c/busses/i2c-designware.c @@ -1,5 +1,5 @@ /* - * Synopsys Designware I2C adapter driver (master only). + * Synopsys DesignWare I2C adapter driver (master only). * * Based on the TI DAVINCI I2C adapter driver. * @@ -49,7 +49,20 @@ #define DW_IC_FS_SCL_LCNT 0x20 #define DW_IC_INTR_STAT 0x2c #define DW_IC_INTR_MASK 0x30 +#define DW_IC_RAW_INTR_STAT 0x34 +#define DW_IC_RX_TL 0x38 +#define DW_IC_TX_TL 0x3c #define DW_IC_CLR_INTR 0x40 +#define DW_IC_CLR_RX_UNDER 0x44 +#define DW_IC_CLR_RX_OVER 0x48 +#define DW_IC_CLR_TX_OVER 0x4c +#define DW_IC_CLR_RD_REQ 0x50 +#define DW_IC_CLR_TX_ABRT 0x54 +#define DW_IC_CLR_RX_DONE 0x58 +#define DW_IC_CLR_ACTIVITY 0x5c +#define DW_IC_CLR_STOP_DET 0x60 +#define DW_IC_CLR_START_DET 0x64 +#define DW_IC_CLR_GEN_CALL 0x68 #define DW_IC_ENABLE 0x6c #define DW_IC_STATUS 0x70 #define DW_IC_TXFLR 0x74 @@ -64,9 +77,23 @@ #define DW_IC_CON_RESTART_EN 0x20 #define DW_IC_CON_SLAVE_DISABLE 0x40 -#define DW_IC_INTR_TX_EMPTY 0x10 -#define DW_IC_INTR_TX_ABRT 0x40 +#define DW_IC_INTR_RX_UNDER 0x001 +#define DW_IC_INTR_RX_OVER 0x002 +#define DW_IC_INTR_RX_FULL 0x004 +#define DW_IC_INTR_TX_OVER 0x008 +#define DW_IC_INTR_TX_EMPTY 0x010 +#define DW_IC_INTR_RD_REQ 0x020 +#define DW_IC_INTR_TX_ABRT 0x040 +#define DW_IC_INTR_RX_DONE 0x080 +#define DW_IC_INTR_ACTIVITY 0x100 #define DW_IC_INTR_STOP_DET 0x200 +#define DW_IC_INTR_START_DET 0x400 +#define DW_IC_INTR_GEN_CALL 0x800 + +#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \ + DW_IC_INTR_TX_EMPTY | \ + DW_IC_INTR_TX_ABRT | \ + DW_IC_INTR_STOP_DET) #define DW_IC_STATUS_ACTIVITY 0x1 @@ -96,31 +123,49 @@ #define ABRT_SBYTE_ACKDET 7 #define ABRT_SBYTE_NORSTRT 9 #define ABRT_10B_RD_NORSTRT 10 -#define ARB_MASTER_DIS 11 +#define ABRT_MASTER_DIS 11 #define ARB_LOST 12 +#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) +#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) +#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) +#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) +#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) +#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) +#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) +#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) +#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) +#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) +#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST) + +#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \ + DW_IC_TX_ABRT_10ADDR1_NOACK | \ + DW_IC_TX_ABRT_10ADDR2_NOACK | \ + DW_IC_TX_ABRT_TXDATA_NOACK | \ + DW_IC_TX_ABRT_GCALL_NOACK) + static char *abort_sources[] = { - [ABRT_7B_ADDR_NOACK] = + [ABRT_7B_ADDR_NOACK] = "slave address not acknowledged (7bit mode)", - [ABRT_10ADDR1_NOACK] = + [ABRT_10ADDR1_NOACK] = "first address byte not acknowledged (10bit mode)", - [ABRT_10ADDR2_NOACK] = + [ABRT_10ADDR2_NOACK] = "second address byte not acknowledged (10bit mode)", - [ABRT_TXDATA_NOACK] = + [ABRT_TXDATA_NOACK] = "data not acknowledged", - [ABRT_GCALL_NOACK] = + [ABRT_GCALL_NOACK] = "no acknowledgement for a general call", - [ABRT_GCALL_READ] = + [ABRT_GCALL_READ] = "read after general call", - [ABRT_SBYTE_ACKDET] = + [ABRT_SBYTE_ACKDET] = "start byte acknowledged", - [ABRT_SBYTE_NORSTRT] = + [ABRT_SBYTE_NORSTRT] = "trying to send start byte when restart is disabled", - [ABRT_10B_RD_NORSTRT] = + [ABRT_10B_RD_NORSTRT] = "trying to read when restart is disabled (10bit mode)", - [ARB_MASTER_DIS] = + [ABRT_MASTER_DIS] = "trying to use disabled adapter", - [ARB_LOST] = + [ARB_LOST] = "lost arbitration", }; @@ -129,7 +174,6 @@ static char *abort_sources[] = { * @dev: driver model device node * @base: IO registers pointer * @cmd_complete: tx completion indicator - * @pump_msg: continue in progress transfers * @lock: protect this struct and IO registers * @clk: input reference clock * @cmd_err: run time hadware error code @@ -155,27 +199,81 @@ struct dw_i2c_dev { struct device *dev; void __iomem *base; struct completion cmd_complete; - struct tasklet_struct pump_msg; struct mutex lock; struct clk *clk; int cmd_err; struct i2c_msg *msgs; int msgs_num; int msg_write_idx; - u16 tx_buf_len; + u32 tx_buf_len; u8 *tx_buf; int msg_read_idx; - u16 rx_buf_len; + u32 rx_buf_len; u8 *rx_buf; int msg_err; unsigned int status; - u16 abort_source; + u32 abort_source; int irq; struct i2c_adapter adapter; unsigned int tx_fifo_depth; unsigned int rx_fifo_depth; }; +static u32 +i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) +{ + /* + * DesignWare I2C core doesn't seem to have solid strategy to meet + * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec + * will result in violation of the tHD;STA spec. + */ + if (cond) + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH + * + * This is based on the DW manuals, and represents an ideal + * configuration. The resulting I2C bus speed will be + * faster than any of the others. + * + * If your hardware is free from tHD;STA issue, try this one. + */ + return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset; + else + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) + * + * This is just experimental rule; the tHD;STA period turned + * out to be proportinal to (_HCNT + 3). With this setting, + * we could meet both tHIGH and tHD;STA timing specs. + * + * If unsure, you'd better to take this alternative. + * + * The reason why we need to take into account "tf" here, + * is the same as described in i2c_dw_scl_lcnt(). + */ + return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset; +} + +static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) +{ + /* + * Conditional expression: + * + * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + * + * DW I2C core starts counting the SCL CNTs for the LOW period + * of the SCL clock (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset; +} + /** * i2c_dw_init() - initialize the designware i2c master hardware * @dev: device private data @@ -187,25 +285,49 @@ struct dw_i2c_dev { static void i2c_dw_init(struct dw_i2c_dev *dev) { u32 input_clock_khz = clk_get_rate(dev->clk) / 1000; - u16 ic_con; + u32 ic_con, hcnt, lcnt; /* Disable the adapter */ - writeb(0, dev->base + DW_IC_ENABLE); + writel(0, dev->base + DW_IC_ENABLE); /* set standard and fast speed deviders for high/low periods */ - writew((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */ - dev->base + DW_IC_SS_SCL_HCNT); - writew((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */ - dev->base + DW_IC_SS_SCL_LCNT); - writew((input_clock_khz * 6 / 10000)+1, /* fast speed high, 0.6us */ - dev->base + DW_IC_FS_SCL_HCNT); - writew((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */ - dev->base + DW_IC_FS_SCL_LCNT); + + /* Standard-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 40, /* tHD;STA = tHIGH = 4.0 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 47, /* tLOW = 4.7 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT); + writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT); + dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + /* Fast-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 6, /* tHD;STA = tHIGH = 0.6 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 13, /* tLOW = 1.3 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT); + writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT); + dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + /* Configure Tx/Rx FIFO threshold levels */ + writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL); + writel(0, dev->base + DW_IC_RX_TL); /* configure the i2c master */ ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; - writew(ic_con, dev->base + DW_IC_CON); + writel(ic_con, dev->base + DW_IC_CON); } /* @@ -215,7 +337,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) { int timeout = TIMEOUT; - while (readb(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { + while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { if (timeout <= 0) { dev_warn(dev->dev, "timeout waiting for bus ready\n"); return -ETIMEDOUT; @@ -227,106 +349,125 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) return 0; } +static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 ic_con; + + /* Disable the adapter */ + writel(0, dev->base + DW_IC_ENABLE); + + /* set the slave (target) address */ + writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR); + + /* if the slave address is ten bit address, enable 10BITADDR */ + ic_con = readl(dev->base + DW_IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) + ic_con |= DW_IC_CON_10BITADDR_MASTER; + else + ic_con &= ~DW_IC_CON_10BITADDR_MASTER; + writel(ic_con, dev->base + DW_IC_CON); + + /* Enable the adapter */ + writel(1, dev->base + DW_IC_ENABLE); + + /* Enable interrupts */ + writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK); +} + /* - * Initiate low level master read/write transaction. - * This function is called from i2c_dw_xfer when starting a transfer. - * This function is also called from dw_i2c_pump_msg to continue a transfer - * that is longer than the size of the TX FIFO. + * Initiate (and continue) low level master read/write transaction. + * This function is only called from i2c_dw_isr, and pumping i2c_msg + * messages into the tx buffer. Even if the size of i2c_msg data is + * longer than the size of the tx buffer, it handles everything. */ static void -i2c_dw_xfer_msg(struct i2c_adapter *adap) +i2c_dw_xfer_msg(struct dw_i2c_dev *dev) { - struct dw_i2c_dev *dev = i2c_get_adapdata(adap); struct i2c_msg *msgs = dev->msgs; - int num = dev->msgs_num; - u16 ic_con, intr_mask; - int tx_limit = dev->tx_fifo_depth - readb(dev->base + DW_IC_TXFLR); - int rx_limit = dev->rx_fifo_depth - readb(dev->base + DW_IC_RXFLR); - u16 addr = msgs[dev->msg_write_idx].addr; - u16 buf_len = dev->tx_buf_len; - - if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { - /* Disable the adapter */ - writeb(0, dev->base + DW_IC_ENABLE); - - /* set the slave (target) address */ - writew(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR); + u32 intr_mask; + int tx_limit, rx_limit; + u32 addr = msgs[dev->msg_write_idx].addr; + u32 buf_len = dev->tx_buf_len; + u8 *buf = dev->tx_buf;; - /* if the slave address is ten bit address, enable 10BITADDR */ - ic_con = readw(dev->base + DW_IC_CON); - if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) - ic_con |= DW_IC_CON_10BITADDR_MASTER; - else - ic_con &= ~DW_IC_CON_10BITADDR_MASTER; - writew(ic_con, dev->base + DW_IC_CON); + intr_mask = DW_IC_INTR_DEFAULT_MASK; - /* Enable the adapter */ - writeb(1, dev->base + DW_IC_ENABLE); - } - - for (; dev->msg_write_idx < num; dev->msg_write_idx++) { - /* if target address has changed, we need to + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + /* + * if target address has changed, we need to * reprogram the target address in the i2c * adapter when we are done with this transfer */ - if (msgs[dev->msg_write_idx].addr != addr) - return; + if (msgs[dev->msg_write_idx].addr != addr) { + dev_err(dev->dev, + "%s: invalid target address\n", __func__); + dev->msg_err = -EINVAL; + break; + } if (msgs[dev->msg_write_idx].len == 0) { dev_err(dev->dev, "%s: invalid message length\n", __func__); dev->msg_err = -EINVAL; - return; + break; } if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { /* new i2c_msg */ - dev->tx_buf = msgs[dev->msg_write_idx].buf; + buf = msgs[dev->msg_write_idx].buf; buf_len = msgs[dev->msg_write_idx].len; } + tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR); + rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR); + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { - writew(0x100, dev->base + DW_IC_DATA_CMD); + writel(0x100, dev->base + DW_IC_DATA_CMD); rx_limit--; } else - writew(*(dev->tx_buf++), - dev->base + DW_IC_DATA_CMD); + writel(*buf++, dev->base + DW_IC_DATA_CMD); tx_limit--; buf_len--; } + + dev->tx_buf = buf; + dev->tx_buf_len = buf_len; + + if (buf_len > 0) { + /* more bytes to be written */ + dev->status |= STATUS_WRITE_IN_PROGRESS; + break; + } else + dev->status &= ~STATUS_WRITE_IN_PROGRESS; } - intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT; - if (buf_len > 0) { /* more bytes to be written */ - intr_mask |= DW_IC_INTR_TX_EMPTY; - dev->status |= STATUS_WRITE_IN_PROGRESS; - } else - dev->status &= ~STATUS_WRITE_IN_PROGRESS; - writew(intr_mask, dev->base + DW_IC_INTR_MASK); + /* + * If i2c_msg index search is completed, we don't need TX_EMPTY + * interrupt any more. + */ + if (dev->msg_write_idx == dev->msgs_num) + intr_mask &= ~DW_IC_INTR_TX_EMPTY; + + if (dev->msg_err) + intr_mask = 0; - dev->tx_buf_len = buf_len; + writel(intr_mask, dev->base + DW_IC_INTR_MASK); } static void -i2c_dw_read(struct i2c_adapter *adap) +i2c_dw_read(struct dw_i2c_dev *dev) { - struct dw_i2c_dev *dev = i2c_get_adapdata(adap); struct i2c_msg *msgs = dev->msgs; - int num = dev->msgs_num; - u16 addr = msgs[dev->msg_read_idx].addr; - int rx_valid = readw(dev->base + DW_IC_RXFLR); + int rx_valid; - for (; dev->msg_read_idx < num; dev->msg_read_idx++) { - u16 len; + for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) { + u32 len; u8 *buf; if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD)) continue; - /* different i2c client, reprogram the i2c adapter */ - if (msgs[dev->msg_read_idx].addr != addr) - return; - if (!(dev->status & STATUS_READ_IN_PROGRESS)) { len = msgs[dev->msg_read_idx].len; buf = msgs[dev->msg_read_idx].buf; @@ -335,8 +476,10 @@ i2c_dw_read(struct i2c_adapter *adap) buf = dev->rx_buf; } + rx_valid = readl(dev->base + DW_IC_RXFLR); + for (; len > 0 && rx_valid > 0; len--, rx_valid--) - *buf++ = readb(dev->base + DW_IC_DATA_CMD); + *buf++ = readl(dev->base + DW_IC_DATA_CMD); if (len > 0) { dev->status |= STATUS_READ_IN_PROGRESS; @@ -348,6 +491,29 @@ i2c_dw_read(struct i2c_adapter *adap) } } +static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) +{ + unsigned long abort_source = dev->abort_source; + int i; + + if (abort_source & DW_IC_TX_ABRT_NOACK) { + for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_dbg(dev->dev, + "%s: %s\n", __func__, abort_sources[i]); + return -EREMOTEIO; + } + + for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + + if (abort_source & DW_IC_TX_ARB_LOST) + return -EAGAIN; + else if (abort_source & DW_IC_TX_ABRT_GCALL_READ) + return -EINVAL; /* wrong msgs[] data */ + else + return -EIO; +} + /* * Prepare controller for a transaction and call i2c_dw_xfer_msg */ @@ -369,13 +535,14 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev->msg_read_idx = 0; dev->msg_err = 0; dev->status = STATUS_IDLE; + dev->abort_source = 0; ret = i2c_dw_wait_bus_not_busy(dev); if (ret < 0) goto done; /* start the transfers */ - i2c_dw_xfer_msg(adap); + i2c_dw_xfer_init(dev); /* wait for tx to complete */ ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ); @@ -394,23 +561,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) /* no error */ if (likely(!dev->cmd_err)) { - /* read rx fifo, and disable the adapter */ - do { - i2c_dw_read(adap); - } while (dev->status & STATUS_READ_IN_PROGRESS); - writeb(0, dev->base + DW_IC_ENABLE); + /* Disable the adapter */ + writel(0, dev->base + DW_IC_ENABLE); ret = num; goto done; } /* We have an error */ if (dev->cmd_err == DW_IC_ERR_TX_ABRT) { - unsigned long abort_source = dev->abort_source; - int i; - - for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) { - dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); - } + ret = i2c_dw_handle_tx_abort(dev); + goto done; } ret = -EIO; @@ -422,21 +582,67 @@ done: static u32 i2c_dw_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; + return I2C_FUNC_I2C | + I2C_FUNC_10BIT_ADDR | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; } -static void dw_i2c_pump_msg(unsigned long data) +static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) { - struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data; - u16 intr_mask; - - i2c_dw_read(&dev->adapter); - i2c_dw_xfer_msg(&dev->adapter); - - intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT; - if (dev->status & STATUS_WRITE_IN_PROGRESS) - intr_mask |= DW_IC_INTR_TX_EMPTY; - writew(intr_mask, dev->base + DW_IC_INTR_MASK); + u32 stat; + + /* + * The IC_INTR_STAT register just indicates "enabled" interrupts. + * Ths unmasked raw version of interrupt status bits are available + * in the IC_RAW_INTR_STAT register. + * + * That is, + * stat = readl(IC_INTR_STAT); + * equals to, + * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK); + * + * The raw version might be useful for debugging purposes. + */ + stat = readl(dev->base + DW_IC_INTR_STAT); + + /* + * Do not use the IC_CLR_INTR register to clear interrupts, or + * you'll miss some interrupts, triggered during the period from + * readl(IC_INTR_STAT) to readl(IC_CLR_INTR). + * + * Instead, use the separately-prepared IC_CLR_* registers. + */ + if (stat & DW_IC_INTR_RX_UNDER) + readl(dev->base + DW_IC_CLR_RX_UNDER); + if (stat & DW_IC_INTR_RX_OVER) + readl(dev->base + DW_IC_CLR_RX_OVER); + if (stat & DW_IC_INTR_TX_OVER) + readl(dev->base + DW_IC_CLR_TX_OVER); + if (stat & DW_IC_INTR_RD_REQ) + readl(dev->base + DW_IC_CLR_RD_REQ); + if (stat & DW_IC_INTR_TX_ABRT) { + /* + * The IC_TX_ABRT_SOURCE register is cleared whenever + * the IC_CLR_TX_ABRT is read. Preserve it beforehand. + */ + dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE); + readl(dev->base + DW_IC_CLR_TX_ABRT); + } + if (stat & DW_IC_INTR_RX_DONE) + readl(dev->base + DW_IC_CLR_RX_DONE); + if (stat & DW_IC_INTR_ACTIVITY) + readl(dev->base + DW_IC_CLR_ACTIVITY); + if (stat & DW_IC_INTR_STOP_DET) + readl(dev->base + DW_IC_CLR_STOP_DET); + if (stat & DW_IC_INTR_START_DET) + readl(dev->base + DW_IC_CLR_START_DET); + if (stat & DW_IC_INTR_GEN_CALL) + readl(dev->base + DW_IC_CLR_GEN_CALL); + + return stat; } /* @@ -446,20 +652,37 @@ static void dw_i2c_pump_msg(unsigned long data) static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) { struct dw_i2c_dev *dev = dev_id; - u16 stat; + u32 stat; - stat = readw(dev->base + DW_IC_INTR_STAT); + stat = i2c_dw_read_clear_intrbits(dev); dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); + if (stat & DW_IC_INTR_TX_ABRT) { - dev->abort_source = readw(dev->base + DW_IC_TX_ABRT_SOURCE); dev->cmd_err |= DW_IC_ERR_TX_ABRT; dev->status = STATUS_IDLE; - } else if (stat & DW_IC_INTR_TX_EMPTY) - tasklet_schedule(&dev->pump_msg); - readb(dev->base + DW_IC_CLR_INTR); /* clear interrupts */ - writew(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */ - if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) + /* + * Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + writel(0, dev->base + DW_IC_INTR_MASK); + goto tx_aborted; + } + + if (stat & DW_IC_INTR_RX_FULL) + i2c_dw_read(dev); + + if (stat & DW_IC_INTR_TX_EMPTY) + i2c_dw_xfer_msg(dev); + + /* + * No need to modify or disable the interrupt mask here. + * i2c_dw_xfer_msg() will take care of it according to + * the current transmit status. + */ + +tx_aborted: + if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) complete(&dev->cmd_complete); return IRQ_HANDLED; @@ -474,8 +697,8 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev) { struct dw_i2c_dev *dev; struct i2c_adapter *adap; - struct resource *mem, *irq, *ioarea; - int r; + struct resource *mem, *ioarea; + int irq, r; /* NOTE: driver uses the static register mapping */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -484,10 +707,10 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev) return -EINVAL; } - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); - return -EINVAL; + return irq; /* -ENXIO */ } ioarea = request_mem_region(mem->start, resource_size(mem), @@ -504,10 +727,9 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev) } init_completion(&dev->cmd_complete); - tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev); mutex_init(&dev->lock); dev->dev = get_device(&pdev->dev); - dev->irq = irq->start; + dev->irq = irq; platform_set_drvdata(pdev, dev); dev->clk = clk_get(&pdev->dev, NULL); @@ -531,8 +753,8 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev) } i2c_dw_init(dev); - writew(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */ - r = request_irq(dev->irq, i2c_dw_isr, 0, pdev->name, dev); + writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */ + r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev); if (r) { dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); goto err_iounmap; @@ -587,7 +809,7 @@ static int __devexit dw_i2c_remove(struct platform_device *pdev) clk_put(dev->clk); dev->clk = NULL; - writeb(0, dev->base + DW_IC_ENABLE); + writel(0, dev->base + DW_IC_ENABLE); free_irq(dev->irq, dev); kfree(dev); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 55edcfe5b851..df6ab553f975 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -767,6 +767,9 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id /* set up the sysfs linkage to our parent device */ i801_adapter.dev.parent = &dev->dev; + /* Retry up to 3 times on lost arbitration */ + i801_adapter.retries = 3; + snprintf(i801_adapter.name, sizeof(i801_adapter.name), "SMBus I801 adapter at %04lx", i801_smba); err = i2c_add_adapter(&i801_adapter); diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index a75c75e77b92..5901707fc66a 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -56,12 +56,6 @@ iic_cook_addr(struct i2c_msg *msg) if (msg->flags & I2C_M_RD) addr |= 1; - /* - * Read or Write? - */ - if (msg->flags & I2C_M_REV_DIR_ADDR) - addr ^= 1; - return addr; } diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index bbab0e166630..ed387ffa4730 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -338,9 +338,6 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, if (msg->flags & I2C_M_RD) dir = 1; - if (msg->flags & I2C_M_REV_DIR_ADDR) - dir ^= 1; - if (msg->flags & I2C_M_TEN) { drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir; drv_data->addr2 = (u32)msg->addr & 0xff; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 827da0858136..75bf3ad18099 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -178,6 +178,12 @@ struct omap_i2c_dev { unsigned b_hw:1; /* bad h/w fixes */ unsigned idle:1; u16 iestate; /* Saved interrupt register */ + u16 pscstate; + u16 scllstate; + u16 sclhstate; + u16 bufstate; + u16 syscstate; + u16 westate; }; static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, @@ -230,9 +236,18 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev) clk_enable(dev->iclk); clk_enable(dev->fclk); + if (cpu_is_omap34xx()) { + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate); + omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate); + omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate); + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, dev->bufstate); + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, dev->syscstate); + omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + } dev->idle = 0; - if (dev->iestate) - omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); } static void omap_i2c_idle(struct omap_i2c_dev *dev) @@ -258,7 +273,7 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev) static int omap_i2c_init(struct omap_i2c_dev *dev) { - u16 psc = 0, scll = 0, sclh = 0; + u16 psc = 0, scll = 0, sclh = 0, buf = 0; u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0; unsigned long fclk_rate = 12000000; unsigned long timeout; @@ -287,24 +302,22 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) SYSC_AUTOIDLE_MASK); } else if (dev->rev >= OMAP_I2C_REV_ON_3430) { - u32 v; - - v = SYSC_AUTOIDLE_MASK; - v |= SYSC_ENAWAKEUP_MASK; - v |= (SYSC_IDLEMODE_SMART << + dev->syscstate = SYSC_AUTOIDLE_MASK; + dev->syscstate |= SYSC_ENAWAKEUP_MASK; + dev->syscstate |= (SYSC_IDLEMODE_SMART << __ffs(SYSC_SIDLEMODE_MASK)); - v |= (SYSC_CLOCKACTIVITY_FCLK << + dev->syscstate |= (SYSC_CLOCKACTIVITY_FCLK << __ffs(SYSC_CLOCKACTIVITY_MASK)); - omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, v); + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, + dev->syscstate); /* * Enabling all wakup sources to stop I2C freezing on * WFI instruction. * REVISIT: Some wkup sources might not be needed. */ - omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, - OMAP_I2C_WE_ALL); - + dev->westate = OMAP_I2C_WE_ALL; + omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); } } omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); @@ -394,23 +407,28 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll); omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh); - if (dev->fifo_size) - /* Note: setup required fifo size - 1 */ - omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, - (dev->fifo_size - 1) << 8 | /* RTRSH */ - OMAP_I2C_BUF_RXFIF_CLR | - (dev->fifo_size - 1) | /* XTRSH */ - OMAP_I2C_BUF_TXFIF_CLR); + if (dev->fifo_size) { + /* Note: setup required fifo size - 1. RTRSH and XTRSH */ + buf = (dev->fifo_size - 1) << 8 | OMAP_I2C_BUF_RXFIF_CLR | + (dev->fifo_size - 1) | OMAP_I2C_BUF_TXFIF_CLR; + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf); + } /* Take the I2C module out of reset: */ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); /* Enable interrupts */ - omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, - (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | + dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | OMAP_I2C_IE_AL) | ((dev->fifo_size) ? - (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0)); + (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0); + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); + if (cpu_is_omap34xx()) { + dev->pscstate = psc; + dev->scllstate = scll; + dev->sclhstate = sclh; + dev->bufstate = buf; + } return 0; } diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index fbab6846ae64..5d1c2603a130 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -638,7 +638,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) /* Register this adapter with the I2C subsystem */ i2c_pnx->adapter->dev.parent = &pdev->dev; - ret = i2c_add_adapter(i2c_pnx->adapter); + i2c_pnx->adapter->nr = pdev->id; + ret = i2c_add_numbered_adapter(i2c_pnx->adapter); if (ret < 0) { dev_err(&pdev->dev, "I2C: Failed to add bus\n"); goto out_irq; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 3c9d71f60187..1c440a70ec61 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -49,48 +49,38 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, int rc = 0; int read = (read_write == I2C_SMBUS_READ); int addrdir = (addr << 1) | read; + int mode, subsize, len; + u32 subaddr; + u8 *buf; u8 local[2]; - rc = pmac_i2c_open(bus, 0); - if (rc) - return rc; + if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) { + mode = pmac_i2c_mode_std; + subsize = 0; + subaddr = 0; + } else { + mode = read ? pmac_i2c_mode_combined : pmac_i2c_mode_stdsub; + subsize = 1; + subaddr = command; + } switch (size) { case I2C_SMBUS_QUICK: - rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 0, 0, NULL, 0); + buf = NULL; + len = 0; break; case I2C_SMBUS_BYTE: - rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 0, 0, &data->byte, 1); - break; case I2C_SMBUS_BYTE_DATA: - rc = pmac_i2c_setmode(bus, read ? - pmac_i2c_mode_combined : - pmac_i2c_mode_stdsub); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 1, command, &data->byte, 1); + buf = &data->byte; + len = 1; break; case I2C_SMBUS_WORD_DATA: - rc = pmac_i2c_setmode(bus, read ? - pmac_i2c_mode_combined : - pmac_i2c_mode_stdsub); - if (rc) - goto bail; if (!read) { local[0] = data->word & 0xff; local[1] = (data->word >> 8) & 0xff; } - rc = pmac_i2c_xfer(bus, addrdir, 1, command, local, 2); - if (rc == 0 && read) { - data->word = ((u16)local[1]) << 8; - data->word |= local[0]; - } + buf = local; + len = 2; break; /* Note that these are broken vs. the expected smbus API where @@ -105,28 +95,44 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, * a repeat start/addr phase (but not stop in between) */ case I2C_SMBUS_BLOCK_DATA: - rc = pmac_i2c_setmode(bus, read ? - pmac_i2c_mode_combined : - pmac_i2c_mode_stdsub); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 1, command, data->block, - data->block[0] + 1); - + buf = data->block; + len = data->block[0] + 1; break; case I2C_SMBUS_I2C_BLOCK_DATA: - rc = pmac_i2c_setmode(bus, read ? - pmac_i2c_mode_combined : - pmac_i2c_mode_stdsub); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 1, command, - &data->block[1], data->block[0]); + buf = &data->block[1]; + len = data->block[0]; break; default: - rc = -EINVAL; + return -EINVAL; + } + + rc = pmac_i2c_open(bus, 0); + if (rc) { + dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc); + return rc; + } + + rc = pmac_i2c_setmode(bus, mode); + if (rc) { + dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n", + mode, rc); + goto bail; } + + rc = pmac_i2c_xfer(bus, addrdir, subsize, subaddr, buf, len); + if (rc) { + dev_err(&adap->dev, + "I2C transfer at 0x%02x failed, size %d, err %d\n", + addrdir >> 1, size, rc); + goto bail; + } + + if (size == I2C_SMBUS_WORD_DATA && read) { + data->word = ((u16)local[1]) << 8; + data->word |= local[0]; + } + bail: pmac_i2c_close(bus); return rc; @@ -146,20 +152,33 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap, int read; int addrdir; + if (num != 1) { + dev_err(&adap->dev, + "Multi-message I2C transactions not supported\n"); + return -EOPNOTSUPP; + } + if (msgs->flags & I2C_M_TEN) return -EINVAL; read = (msgs->flags & I2C_M_RD) != 0; addrdir = (msgs->addr << 1) | read; - if (msgs->flags & I2C_M_REV_DIR_ADDR) - addrdir ^= 1; rc = pmac_i2c_open(bus, 0); - if (rc) + if (rc) { + dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc); return rc; + } rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); - if (rc) + if (rc) { + dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n", + pmac_i2c_mode_std, rc); goto bail; + } rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len); + if (rc < 0) + dev_err(&adap->dev, "I2C %s 0x%02x failed, err %d\n", + addrdir & 1 ? "read from" : "write to", addrdir >> 1, + rc); bail: pmac_i2c_close(bus); return rc < 0 ? rc : 1; @@ -183,19 +202,16 @@ static const struct i2c_algorithm i2c_powermac_algorithm = { static int __devexit i2c_powermac_remove(struct platform_device *dev) { struct i2c_adapter *adapter = platform_get_drvdata(dev); - struct pmac_i2c_bus *bus = i2c_get_adapdata(adapter); int rc; rc = i2c_del_adapter(adapter); - pmac_i2c_detach_adapter(bus, adapter); - i2c_set_adapdata(adapter, NULL); /* We aren't that prepared to deal with this... */ if (rc) printk(KERN_WARNING "i2c-powermac.c: Failed to remove bus %s !\n", adapter->name); platform_set_drvdata(dev, NULL); - kfree(adapter); + memset(adapter, 0, sizeof(*adapter)); return 0; } @@ -206,12 +222,12 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) struct pmac_i2c_bus *bus = dev->dev.platform_data; struct device_node *parent = NULL; struct i2c_adapter *adapter; - char name[32]; const char *basename; int rc; if (bus == NULL) return -EINVAL; + adapter = pmac_i2c_get_adapter(bus); /* Ok, now we need to make up a name for the interface that will * match what we used to do in the past, that is basically the @@ -237,29 +253,22 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) default: return -EINVAL; } - snprintf(name, 32, "%s %d", basename, pmac_i2c_get_channel(bus)); + snprintf(adapter->name, sizeof(adapter->name), "%s %d", basename, + pmac_i2c_get_channel(bus)); of_node_put(parent); - adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); - if (adapter == NULL) { - printk(KERN_ERR "i2c-powermac: can't allocate inteface !\n"); - return -ENOMEM; - } platform_set_drvdata(dev, adapter); - strcpy(adapter->name, name); adapter->algo = &i2c_powermac_algorithm; i2c_set_adapdata(adapter, bus); adapter->dev.parent = &dev->dev; - pmac_i2c_attach_adapter(bus, adapter); rc = i2c_add_adapter(adapter); if (rc) { printk(KERN_ERR "i2c-powermac: Adapter %s registration " - "failed\n", name); - i2c_set_adapdata(adapter, NULL); - pmac_i2c_detach_adapter(bus, adapter); + "failed\n", adapter->name); + memset(adapter, 0, sizeof(*adapter)); } - printk(KERN_INFO "PowerMac i2c bus %s registered\n", name); + printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name); if (!strncmp(basename, "uni-n", 5)) { struct device_node *np; diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index b4a55d407bf5..365e0becaf12 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -363,7 +363,7 @@ static int acpi_smbus_cmi_add(struct acpi_device *device) smbus_cmi->cap_write = 0; acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1, - acpi_smbus_cmi_query_methods, smbus_cmi, NULL); + acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL); if (smbus_cmi->cap_info == 0) goto err; diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index 139f0c7f12a4..844569f7d8b7 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -142,7 +142,7 @@ static void sis5595_write(u8 reg, u8 data) outb(data, sis5595_base + SMB_DAT); } -static int sis5595_setup(struct pci_dev *SIS5595_dev) +static int __devinit sis5595_setup(struct pci_dev *SIS5595_dev) { u16 a; u8 val; diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 70ca41e90e58..68cff7af7013 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -389,7 +389,7 @@ static u32 sis630_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BLOCK_DATA; } -static int sis630_setup(struct pci_dev *sis630_dev) +static int __devinit sis630_setup(struct pci_dev *sis630_dev) { unsigned char b; struct pci_dev *dummy = NULL; diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c index 1b7b2af94036..0c770eabe85e 100644 --- a/drivers/i2c/busses/i2c-stub.c +++ b/drivers/i2c/busses/i2c-stub.c @@ -35,6 +35,10 @@ module_param_array(chip_addr, ushort, NULL, S_IRUGO); MODULE_PARM_DESC(chip_addr, "Chip addresses (up to 10, between 0x03 and 0x77)"); +static unsigned long functionality = ~0UL; +module_param(functionality, ulong, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(functionality, "Override functionality bitfield"); + struct stub_chip { u8 pointer; u16 words[256]; /* Byte operations use the LSB as per SMBus @@ -48,7 +52,7 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data) { s32 ret; - int i; + int i, len; struct stub_chip *chip = NULL; /* Search for the right chip */ @@ -118,6 +122,29 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, ret = 0; break; + case I2C_SMBUS_I2C_BLOCK_DATA: + len = data->block[0]; + if (read_write == I2C_SMBUS_WRITE) { + for (i = 0; i < len; i++) { + chip->words[command + i] &= 0xff00; + chip->words[command + i] |= data->block[1 + i]; + } + dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, " + "wrote %d bytes at 0x%02x.\n", + addr, len, command); + } else { + for (i = 0; i < len; i++) { + data->block[1 + i] = + chip->words[command + i] & 0xff; + } + dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, " + "read %d bytes at 0x%02x.\n", + addr, len, command); + } + + ret = 0; + break; + default: dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); ret = -EOPNOTSUPP; @@ -129,8 +156,9 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, static u32 stub_func(struct i2c_adapter *adapter) { - return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA; + return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK) & functionality; } static const struct i2c_algorithm smbus_algorithm = { diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c deleted file mode 100644 index 7663d57833a0..000000000000 --- a/drivers/i2c/busses/i2c-voodoo3.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>, - Philip Edelbrock <phil@netroedge.com>, - Ralph Metzler <rjkm@thp.uni-koeln.de>, and - Mark D. Studebaker <mdsxyz123@yahoo.com> - - Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and - Simon Vogl - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* This interfaces to the I2C bus of the Voodoo3 to gain access to - the BT869 and possibly other I2C devices. */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> -#include <asm/io.h> - -/* the only registers we use */ -#define REG 0x78 -#define REG2 0x70 - -/* bit locations in the register */ -#define DDC_ENAB 0x00040000 -#define DDC_SCL_OUT 0x00080000 -#define DDC_SDA_OUT 0x00100000 -#define DDC_SCL_IN 0x00200000 -#define DDC_SDA_IN 0x00400000 -#define I2C_ENAB 0x00800000 -#define I2C_SCL_OUT 0x01000000 -#define I2C_SDA_OUT 0x02000000 -#define I2C_SCL_IN 0x04000000 -#define I2C_SDA_IN 0x08000000 - -/* initialization states */ -#define INIT2 0x2 -#define INIT3 0x4 - -/* delays */ -#define CYCLE_DELAY 10 -#define TIMEOUT (HZ / 2) - - -static void __iomem *ioaddr; - -/* The voo GPIO registers don't have individual masks for each bit - so we always have to read before writing. */ - -static void bit_vooi2c_setscl(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if (val) - r |= I2C_SCL_OUT; - else - r &= ~I2C_SCL_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -static void bit_vooi2c_setsda(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if (val) - r |= I2C_SDA_OUT; - else - r &= ~I2C_SDA_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -/* The GPIO pins are open drain, so the pins always remain outputs. - We rely on the i2c-algo-bit routines to set the pins high before - reading the input from other chips. */ - -static int bit_vooi2c_getscl(void *data) -{ - return (0 != (readl(ioaddr + REG) & I2C_SCL_IN)); -} - -static int bit_vooi2c_getsda(void *data) -{ - return (0 != (readl(ioaddr + REG) & I2C_SDA_IN)); -} - -static void bit_vooddc_setscl(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if (val) - r |= DDC_SCL_OUT; - else - r &= ~DDC_SCL_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -static void bit_vooddc_setsda(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if (val) - r |= DDC_SDA_OUT; - else - r &= ~DDC_SDA_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -static int bit_vooddc_getscl(void *data) -{ - return (0 != (readl(ioaddr + REG) & DDC_SCL_IN)); -} - -static int bit_vooddc_getsda(void *data) -{ - return (0 != (readl(ioaddr + REG) & DDC_SDA_IN)); -} - -static int config_v3(struct pci_dev *dev) -{ - unsigned long cadr; - - /* map Voodoo3 memory */ - cadr = dev->resource[0].start; - cadr &= PCI_BASE_ADDRESS_MEM_MASK; - ioaddr = ioremap_nocache(cadr, 0x1000); - if (ioaddr) { - writel(0x8160, ioaddr + REG2); - writel(0xcffc0020, ioaddr + REG); - dev_info(&dev->dev, "Using Banshee/Voodoo3 I2C device at %p\n", ioaddr); - return 0; - } - return -ENODEV; -} - -static struct i2c_algo_bit_data voo_i2c_bit_data = { - .setsda = bit_vooi2c_setsda, - .setscl = bit_vooi2c_setscl, - .getsda = bit_vooi2c_getsda, - .getscl = bit_vooi2c_getscl, - .udelay = CYCLE_DELAY, - .timeout = TIMEOUT -}; - -static struct i2c_adapter voodoo3_i2c_adapter = { - .owner = THIS_MODULE, - .name = "I2C Voodoo3/Banshee adapter", - .algo_data = &voo_i2c_bit_data, -}; - -static struct i2c_algo_bit_data voo_ddc_bit_data = { - .setsda = bit_vooddc_setsda, - .setscl = bit_vooddc_setscl, - .getsda = bit_vooddc_getsda, - .getscl = bit_vooddc_getscl, - .udelay = CYCLE_DELAY, - .timeout = TIMEOUT -}; - -static struct i2c_adapter voodoo3_ddc_adapter = { - .owner = THIS_MODULE, - .class = I2C_CLASS_DDC, - .name = "DDC Voodoo3/Banshee adapter", - .algo_data = &voo_ddc_bit_data, -}; - -static struct pci_device_id voodoo3_ids[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3) }, - { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE) }, - { 0, } -}; - -MODULE_DEVICE_TABLE (pci, voodoo3_ids); - -static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int retval; - - retval = config_v3(dev); - if (retval) - return retval; - - /* set up the sysfs linkage to our parent device */ - voodoo3_i2c_adapter.dev.parent = &dev->dev; - voodoo3_ddc_adapter.dev.parent = &dev->dev; - - retval = i2c_bit_add_bus(&voodoo3_i2c_adapter); - if (retval) - return retval; - retval = i2c_bit_add_bus(&voodoo3_ddc_adapter); - if (retval) - i2c_del_adapter(&voodoo3_i2c_adapter); - return retval; -} - -static void __devexit voodoo3_remove(struct pci_dev *dev) -{ - i2c_del_adapter(&voodoo3_i2c_adapter); - i2c_del_adapter(&voodoo3_ddc_adapter); - iounmap(ioaddr); -} - -static struct pci_driver voodoo3_driver = { - .name = "voodoo3_smbus", - .id_table = voodoo3_ids, - .probe = voodoo3_probe, - .remove = __devexit_p(voodoo3_remove), -}; - -static int __init i2c_voodoo3_init(void) -{ - return pci_register_driver(&voodoo3_driver); -} - -static void __exit i2c_voodoo3_exit(void) -{ - pci_unregister_driver(&voodoo3_driver); -} - - -MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " - "Philip Edelbrock <phil@netroedge.com>, " - "Ralph Metzler <rjkm@thp.uni-koeln.de>, " - "and Mark D. Studebaker <mdsxyz123@yahoo.com>"); -MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver"); -MODULE_LICENSE("GPL"); - -module_init(i2c_voodoo3_init); -module_exit(i2c_voodoo3_exit); diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index f9618f4d4e47..ae4539d99bef 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -6,16 +6,6 @@ menu "Miscellaneous I2C Chip support" -config DS1682 - tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" - depends on EXPERIMENTAL - help - If you say yes here you get support for Dallas Semiconductor - DS1682 Total Elapsed Time Recorder. - - This driver can also be built as a module. If so, the module - will be called ds1682. - config SENSORS_TSL2550 tristate "Taos TSL2550 ambient light sensor" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 749cf3606294..fe0af0f81f2d 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -10,7 +10,6 @@ # * I/O expander drivers go to drivers/gpio # -obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 296504355142..4f34823e86b1 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -558,11 +558,9 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter) up_read(&__i2c_board_lock); } -static int i2c_do_add_adapter(struct device_driver *d, void *data) +static int i2c_do_add_adapter(struct i2c_driver *driver, + struct i2c_adapter *adap) { - struct i2c_driver *driver = to_i2c_driver(d); - struct i2c_adapter *adap = data; - /* Detect supported devices on that bus, and instantiate them */ i2c_detect(adap, driver); @@ -574,6 +572,11 @@ static int i2c_do_add_adapter(struct device_driver *d, void *data) return 0; } +static int __process_new_adapter(struct device_driver *d, void *data) +{ + return i2c_do_add_adapter(to_i2c_driver(d), data); +} + static int i2c_register_adapter(struct i2c_adapter *adap) { int res = 0, dummy; @@ -584,7 +587,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) goto out_list; } - mutex_init(&adap->bus_lock); + rt_mutex_init(&adap->bus_lock); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) @@ -614,7 +617,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) /* Notify drivers */ mutex_lock(&core_lock); dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, - i2c_do_add_adapter); + __process_new_adapter); mutex_unlock(&core_lock); return 0; @@ -715,10 +718,9 @@ retry: } EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); -static int i2c_do_del_adapter(struct device_driver *d, void *data) +static int i2c_do_del_adapter(struct i2c_driver *driver, + struct i2c_adapter *adapter) { - struct i2c_driver *driver = to_i2c_driver(d); - struct i2c_adapter *adapter = data; struct i2c_client *client, *_n; int res; @@ -750,6 +752,11 @@ static int __unregister_client(struct device *dev, void *dummy) return 0; } +static int __process_removed_adapter(struct device_driver *d, void *data) +{ + return i2c_do_del_adapter(to_i2c_driver(d), data); +} + /** * i2c_del_adapter - unregister I2C adapter * @adap: the adapter being unregistered @@ -777,7 +784,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) /* Tell drivers about this removal */ mutex_lock(&core_lock); res = bus_for_each_drv(&i2c_bus_type, NULL, adap, - i2c_do_del_adapter); + __process_removed_adapter); mutex_unlock(&core_lock); if (res) return res; @@ -826,22 +833,11 @@ EXPORT_SYMBOL(i2c_del_adapter); /* ------------------------------------------------------------------------- */ -static int __attach_adapter(struct device *dev, void *data) +static int __process_new_driver(struct device *dev, void *data) { - struct i2c_adapter *adapter; - struct i2c_driver *driver = data; - if (dev->type != &i2c_adapter_type) return 0; - adapter = to_i2c_adapter(dev); - - i2c_detect(adapter, driver); - - /* Legacy drivers scan i2c busses directly */ - if (driver->attach_adapter) - driver->attach_adapter(adapter); - - return 0; + return i2c_do_add_adapter(data, to_i2c_adapter(dev)); } /* @@ -873,40 +869,18 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) INIT_LIST_HEAD(&driver->clients); /* Walk the adapters that are already present */ mutex_lock(&core_lock); - bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter); + bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver); mutex_unlock(&core_lock); return 0; } EXPORT_SYMBOL(i2c_register_driver); -static int __detach_adapter(struct device *dev, void *data) +static int __process_removed_driver(struct device *dev, void *data) { - struct i2c_adapter *adapter; - struct i2c_driver *driver = data; - struct i2c_client *client, *_n; - if (dev->type != &i2c_adapter_type) return 0; - adapter = to_i2c_adapter(dev); - - /* Remove the devices we created ourselves as the result of hardware - * probing (using a driver's detect method) */ - list_for_each_entry_safe(client, _n, &driver->clients, detected) { - dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", - client->name, client->addr); - list_del(&client->detected); - i2c_unregister_device(client); - } - - if (driver->detach_adapter) { - if (driver->detach_adapter(adapter)) - dev_err(&adapter->dev, - "detach_adapter failed for driver [%s]\n", - driver->driver.name); - } - - return 0; + return i2c_do_del_adapter(data, to_i2c_adapter(dev)); } /** @@ -917,7 +891,7 @@ static int __detach_adapter(struct device *dev, void *data) void i2c_del_driver(struct i2c_driver *driver) { mutex_lock(&core_lock); - bus_for_each_dev(&i2c_bus_type, NULL, driver, __detach_adapter); + bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_removed_driver); mutex_unlock(&core_lock); driver_unregister(&driver->driver); @@ -1092,12 +1066,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) #endif if (in_atomic() || irqs_disabled()) { - ret = mutex_trylock(&adap->bus_lock); + ret = rt_mutex_trylock(&adap->bus_lock); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { - mutex_lock_nested(&adap->bus_lock, adap->level); + rt_mutex_lock(&adap->bus_lock); } /* Retry automatically on arbitration loss */ @@ -1109,7 +1083,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (time_after(jiffies, orig_jiffies + adap->timeout)) break; } - mutex_unlock(&adap->bus_lock); + rt_mutex_unlock(&adap->bus_lock); return ret; } else { @@ -1180,7 +1154,7 @@ EXPORT_SYMBOL(i2c_master_recv); * ---------------------------------------------------- */ -static int i2c_detect_address(struct i2c_client *temp_client, int kind, +static int i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver) { struct i2c_board_info info; @@ -1199,22 +1173,18 @@ static int i2c_detect_address(struct i2c_client *temp_client, int kind, if (i2c_check_addr(adapter, addr)) return 0; - /* Make sure there is something at this address, unless forced */ - if (kind < 0) { - if (i2c_smbus_xfer(adapter, addr, 0, 0, 0, - I2C_SMBUS_QUICK, NULL) < 0) - return 0; + /* Make sure there is something at this address */ + if (i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0) + return 0; - /* prevent 24RF08 corruption */ - if ((addr & ~0x0f) == 0x50) - i2c_smbus_xfer(adapter, addr, 0, 0, 0, - I2C_SMBUS_QUICK, NULL); - } + /* Prevent 24RF08 corruption */ + if ((addr & ~0x0f) == 0x50) + i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL); /* Finally call the custom detection function */ memset(&info, 0, sizeof(struct i2c_board_info)); info.addr = addr; - err = driver->detect(temp_client, kind, &info); + err = driver->detect(temp_client, -1, &info); if (err) { /* -ENODEV is returned if the detection fails. We catch it here as this isn't an error. */ @@ -1259,40 +1229,13 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) return -ENOMEM; temp_client->adapter = adapter; - /* Force entries are done first, and are not affected by ignore - entries */ - if (address_data->forces) { - const unsigned short * const *forces = address_data->forces; - int kind; - - for (kind = 0; forces[kind]; kind++) { - for (i = 0; forces[kind][i] != I2C_CLIENT_END; - i += 2) { - if (forces[kind][i] == adap_id - || forces[kind][i] == ANY_I2C_BUS) { - dev_dbg(&adapter->dev, "found force " - "parameter for adapter %d, " - "addr 0x%02x, kind %d\n", - adap_id, forces[kind][i + 1], - kind); - temp_client->addr = forces[kind][i + 1]; - err = i2c_detect_address(temp_client, - kind, driver); - if (err) - goto exit_free; - } - } - } - } - /* Stop here if the classes do not match */ if (!(adapter->class & driver->class)) goto exit_free; /* Stop here if we can't use SMBUS_QUICK */ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) { - if (address_data->probe[0] == I2C_CLIENT_END - && address_data->normal_i2c[0] == I2C_CLIENT_END) + if (address_data->normal_i2c[0] == I2C_CLIENT_END) goto exit_free; dev_warn(&adapter->dev, "SMBus Quick command not supported, " @@ -1301,48 +1244,12 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) goto exit_free; } - /* Probe entries are done second, and are not affected by ignore - entries either */ - for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) { - if (address_data->probe[i] == adap_id - || address_data->probe[i] == ANY_I2C_BUS) { - dev_dbg(&adapter->dev, "found probe parameter for " - "adapter %d, addr 0x%02x\n", adap_id, - address_data->probe[i + 1]); - temp_client->addr = address_data->probe[i + 1]; - err = i2c_detect_address(temp_client, -1, driver); - if (err) - goto exit_free; - } - } - - /* Normal entries are done last, unless shadowed by an ignore entry */ for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) { - int j, ignore; - - ignore = 0; - for (j = 0; address_data->ignore[j] != I2C_CLIENT_END; - j += 2) { - if ((address_data->ignore[j] == adap_id || - address_data->ignore[j] == ANY_I2C_BUS) - && address_data->ignore[j + 1] - == address_data->normal_i2c[i]) { - dev_dbg(&adapter->dev, "found ignore " - "parameter for adapter %d, " - "addr 0x%02x\n", adap_id, - address_data->ignore[j + 1]); - ignore = 1; - break; - } - } - if (ignore) - continue; - dev_dbg(&adapter->dev, "found normal entry for adapter %d, " "addr 0x%02x\n", adap_id, address_data->normal_i2c[i]); temp_client->addr = address_data->normal_i2c[i]; - err = i2c_detect_address(temp_client, -1, driver); + err = i2c_detect_address(temp_client, driver); if (err) goto exit_free; } @@ -1913,7 +1820,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, flags &= I2C_M_TEN | I2C_CLIENT_PEC; if (adapter->algo->smbus_xfer) { - mutex_lock(&adapter->bus_lock); + rt_mutex_lock(&adapter->bus_lock); /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; @@ -1927,7 +1834,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, orig_jiffies + adapter->timeout)) break; } - mutex_unlock(&adapter->bus_lock); + rt_mutex_unlock(&adapter->bus_lock); } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, command, protocol, data); diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 7e13d2df9af3..f4110aa49600 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -34,7 +34,6 @@ #include <linux/list.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> -#include <linux/smp_lock.h> #include <linux/jiffies.h> #include <asm/uaccess.h> @@ -445,20 +444,14 @@ static int i2cdev_open(struct inode *inode, struct file *file) struct i2c_client *client; struct i2c_adapter *adap; struct i2c_dev *i2c_dev; - int ret = 0; - lock_kernel(); i2c_dev = i2c_dev_get_by_minor(minor); - if (!i2c_dev) { - ret = -ENODEV; - goto out; - } + if (!i2c_dev) + return -ENODEV; adap = i2c_get_adapter(i2c_dev->adap->nr); - if (!adap) { - ret = -ENODEV; - goto out; - } + if (!adap) + return -ENODEV; /* This creates an anonymous i2c_client, which may later be * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. @@ -470,8 +463,7 @@ static int i2cdev_open(struct inode *inode, struct file *file) client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) { i2c_put_adapter(adap); - ret = -ENOMEM; - goto out; + return -ENOMEM; } snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); client->driver = &i2cdev_driver; @@ -479,9 +471,7 @@ static int i2cdev_open(struct inode *inode, struct file *file) client->adapter = adap; file->private_data = client; -out: - unlock_kernel(); - return ret; + return 0; } static int i2cdev_release(struct inode *inode, struct file *file) diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 9a5d0aaac9d0..98ccfeb3f5aa 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -7,50 +7,25 @@ config HAVE_IDE bool menuconfig IDE - tristate "ATA/ATAPI/MFM/RLL support" + tristate "ATA/ATAPI/MFM/RLL support (DEPRECATED)" depends on HAVE_IDE depends on BLOCK ---help--- - If you say Y here, your kernel will be able to manage low cost mass - storage units such as ATA/(E)IDE and ATAPI units. The most common - cases are IDE hard drives and ATAPI CD-ROM drives. - - If your system is pure SCSI and doesn't use these interfaces, you - can say N here. - - Integrated Disk Electronics (IDE aka ATA-1) is a connecting standard - for mass storage units such as hard disks. It was designed by - Western Digital and Compaq Computer in 1984. It was then named - ST506. Quite a number of disks use the IDE interface. - - AT Attachment (ATA) is the superset of the IDE specifications. - ST506 was also called ATA-1. - - Fast-IDE is ATA-2 (also named Fast ATA), Enhanced IDE (EIDE) is - ATA-3. It provides support for larger disks (up to 8.4GB by means of - the LBA standard), more disks (4 instead of 2) and for other mass - storage units such as tapes and cdrom. UDMA/33 (aka UltraDMA/33) is - ATA-4 and provides faster (and more CPU friendly) transfer modes - than previous PIO (Programmed processor Input/Output) from previous - ATA/IDE standards by means of fast DMA controllers. - - ATA Packet Interface (ATAPI) is a protocol used by EIDE tape and - CD-ROM drives, similar in many respects to the SCSI protocol. - - SMART IDE (Self Monitoring, Analysis and Reporting Technology) was - designed in order to prevent data corruption and disk crash by - detecting pre hardware failure conditions (heat, access time, and - the like...). Disks built since June 1995 may follow this standard. - The kernel itself doesn't manage this; however there are quite a - number of user programs such as smart that can query the status of - SMART parameters from disk drives. + If you say Y here, your kernel will be able to manage ATA/(E)IDE and + ATAPI units. The most common cases are IDE hard drives and ATAPI + CD-ROM drives. + + This subsystem is currently in maintenance mode with only bug fix + changes applied. Users of ATA hardware are encouraged to migrate to + the newer ATA subsystem ("Serial ATA (prod) and Parallel ATA + (experimental) drivers") which is more actively maintained. To compile this driver as a module, choose M here: the module will be called ide-core. For further information, please read <file:Documentation/ide/ide.txt>. - If unsure, say Y. + If unsure, say N. if IDE diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c index e59b6dee9ae2..0abc43f3101e 100644 --- a/drivers/ide/alim15x3.c +++ b/drivers/ide/alim15x3.c @@ -40,16 +40,6 @@ #define DRV_NAME "alim15x3" /* - * Allow UDMA on M1543C-E chipset for WDC disks that ignore CRC checking - * (this is DANGEROUS and could result in data corruption). - */ -static int wdc_udma; - -module_param(wdc_udma, bool, 0); -MODULE_PARM_DESC(wdc_udma, - "allow UDMA on M1543C-E chipset for WDC disks (DANGEROUS)"); - -/* * ALi devices are not plug in. Otherwise these static values would * need to go. They ought to go away anyway */ @@ -132,7 +122,7 @@ static u8 ali_udma_filter(ide_drive_t *drive) if (m5229_revision > 0x20 && m5229_revision < 0xC2) { if (drive->media != ide_disk) return 0; - if (wdc_udma == 0 && chip_is_1543c_e && + if (chip_is_1543c_e && strstr((char *)&drive->id[ATA_ID_PROD], "WDC ")) return 0; } diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c index 58121bd6c115..87cef0c440ad 100644 --- a/drivers/ide/au1xxx-ide.c +++ b/drivers/ide/au1xxx-ide.c @@ -532,14 +532,13 @@ static int au_ide_probe(struct platform_device *dev) goto out; } - if (!request_mem_region(res->start, res->end - res->start + 1, - dev->name)) { + if (!request_mem_region(res->start, resource_size(res), dev->name)) { pr_debug("%s: request_mem_region failed\n", DRV_NAME); ret = -EBUSY; goto out; } - ahwif->regbase = (u32)ioremap(res->start, res->end - res->start + 1); + ahwif->regbase = (u32)ioremap(res->start, resource_size(res)); if (ahwif->regbase == 0) { ret = -ENOMEM; goto out; @@ -575,7 +574,7 @@ static int au_ide_remove(struct platform_device *dev) iounmap((void *)ahwif->regbase); res = platform_get_resource(dev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); return 0; } diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index ca0c46f6580a..f2500c8826bb 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -20,14 +20,6 @@ #define DRV_NAME "cmd64x" -#define CMD_DEBUG 0 - -#if CMD_DEBUG -#define cmdprintk(x...) printk(x) -#else -#define cmdprintk(x...) -#endif - /* * CMD64x specific registers definition. */ @@ -76,9 +68,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_ {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0}; static const u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM2, DRWTIM3}; - cmdprintk("program_cycle_times parameters: total=%d, active=%d\n", - cycle_time, active_time); - cycle_count = quantize_timing( cycle_time, clock_time); active_count = quantize_timing(active_time, clock_time); recovery_count = cycle_count - active_count; @@ -94,9 +83,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_ if (active_count > 16) /* shouldn't actually happen... */ active_count = 16; - cmdprintk("Final counts: total=%d, active=%d, recovery=%d\n", - cycle_count, active_count, recovery_count); - /* * Convert values to internal chipset representation */ @@ -106,7 +92,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_ /* Program the active/recovery counts into the DRWTIM register */ drwtim = (active_count << 4) | recovery_count; (void) pci_write_config_byte(dev, drwtim_regs[drive->dn], drwtim); - cmdprintk("Write 0x%02x to reg 0x%x\n", drwtim, drwtim_regs[drive->dn]); } /* @@ -150,7 +135,6 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio) if (setup_count > 5) /* shouldn't actually happen... */ setup_count = 5; - cmdprintk("Final address setup count: %d\n", setup_count); /* * Program the address setup clocks into the ARTTIM registers. @@ -162,7 +146,6 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio) arttim &= ~0xc0; arttim |= setup_values[setup_count]; (void) pci_write_config_byte(dev, arttim_regs[drive->dn], arttim); - cmdprintk("Write 0x%02x to reg 0x%x\n", arttim, arttim_regs[drive->dn]); } /* diff --git a/drivers/ide/cs5535.c b/drivers/ide/cs5535.c index 983d957a0189..b883838adc24 100644 --- a/drivers/ide/cs5535.c +++ b/drivers/ide/cs5535.c @@ -187,6 +187,7 @@ static int __devinit cs5535_init_one(struct pci_dev *dev, static const struct pci_device_id cs5535_pci_tbl[] = { { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_CS5535_IDE), 0 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5535_IDE), }, { 0, }, }; diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c index 74fc5401f407..d6e2cbbc53a0 100644 --- a/drivers/ide/cy82c693.c +++ b/drivers/ide/cy82c693.c @@ -51,11 +51,6 @@ #define DRV_NAME "cy82c693" /* - * The following are used to debug the driver. - */ -#define CY82C693_DEBUG_INFO 0 - -/* * NOTE: the value for busmaster timeout is tricky and I got it by * trial and error! By using a to low value will cause DMA timeouts * and drop IDE performance, and by using a to high value will cause @@ -176,11 +171,6 @@ static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode) outb(index, CY82_INDEX_PORT); outb(data, CY82_DATA_PORT); -#if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", - drive->name, hwif->channel, drive->dn & 1, mode & 3, single); -#endif /* CY82C693_DEBUG_INFO */ - /* * note: below we set the value for Bus Master IDE TimeOut Register * I'm not absolutly sure what this does, but it solved my problem @@ -194,11 +184,6 @@ static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode) data = BUSMASTER_TIMEOUT; outb(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT); outb(data, CY82_DATA_PORT); - -#if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n", - drive->name, data); -#endif /* CY82C693_DEBUG_INFO */ } static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) @@ -239,8 +224,6 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r); pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w); pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8); - - addrCtrl &= 0xF; } else { /* * set slave drive @@ -257,17 +240,7 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r); pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w); pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8); - - addrCtrl >>= 4; - addrCtrl &= 0xF; } - -#if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to " - "(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", - drive->name, hwif->channel, drive->dn & 1, - addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8); -#endif /* CY82C693_DEBUG_INFO */ } static void __devinit init_iops_cy82c693(ide_hwif_t *hwif) diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c index 7ce68ef6b904..4d90ac2dbb1b 100644 --- a/drivers/ide/hpt366.c +++ b/drivers/ide/hpt366.c @@ -297,68 +297,6 @@ static u32 twenty_five_base_hpt36x[] = { /* XFER_PIO_0 */ 0xc0d08585 }; -#if 0 -/* These are the timing tables from the HighPoint open source drivers... */ -static u32 thirty_three_base_hpt37x[] = { - /* XFER_UDMA_6 */ 0x12446231, /* 0x12646231 ?? */ - /* XFER_UDMA_5 */ 0x12446231, - /* XFER_UDMA_4 */ 0x12446231, - /* XFER_UDMA_3 */ 0x126c6231, - /* XFER_UDMA_2 */ 0x12486231, - /* XFER_UDMA_1 */ 0x124c6233, - /* XFER_UDMA_0 */ 0x12506297, - - /* XFER_MW_DMA_2 */ 0x22406c31, - /* XFER_MW_DMA_1 */ 0x22406c33, - /* XFER_MW_DMA_0 */ 0x22406c97, - - /* XFER_PIO_4 */ 0x06414e31, - /* XFER_PIO_3 */ 0x06414e42, - /* XFER_PIO_2 */ 0x06414e53, - /* XFER_PIO_1 */ 0x06814e93, - /* XFER_PIO_0 */ 0x06814ea7 -}; - -static u32 fifty_base_hpt37x[] = { - /* XFER_UDMA_6 */ 0x12848242, - /* XFER_UDMA_5 */ 0x12848242, - /* XFER_UDMA_4 */ 0x12ac8242, - /* XFER_UDMA_3 */ 0x128c8242, - /* XFER_UDMA_2 */ 0x120c8242, - /* XFER_UDMA_1 */ 0x12148254, - /* XFER_UDMA_0 */ 0x121882ea, - - /* XFER_MW_DMA_2 */ 0x22808242, - /* XFER_MW_DMA_1 */ 0x22808254, - /* XFER_MW_DMA_0 */ 0x228082ea, - - /* XFER_PIO_4 */ 0x0a81f442, - /* XFER_PIO_3 */ 0x0a81f443, - /* XFER_PIO_2 */ 0x0a81f454, - /* XFER_PIO_1 */ 0x0ac1f465, - /* XFER_PIO_0 */ 0x0ac1f48a -}; - -static u32 sixty_six_base_hpt37x[] = { - /* XFER_UDMA_6 */ 0x1c869c62, - /* XFER_UDMA_5 */ 0x1cae9c62, /* 0x1c8a9c62 */ - /* XFER_UDMA_4 */ 0x1c8a9c62, - /* XFER_UDMA_3 */ 0x1c8e9c62, - /* XFER_UDMA_2 */ 0x1c929c62, - /* XFER_UDMA_1 */ 0x1c9a9c62, - /* XFER_UDMA_0 */ 0x1c829c62, - - /* XFER_MW_DMA_2 */ 0x2c829c62, - /* XFER_MW_DMA_1 */ 0x2c829c66, - /* XFER_MW_DMA_0 */ 0x2c829d2e, - - /* XFER_PIO_4 */ 0x0c829c62, - /* XFER_PIO_3 */ 0x0c829c84, - /* XFER_PIO_2 */ 0x0c829ca6, - /* XFER_PIO_1 */ 0x0d029d26, - /* XFER_PIO_0 */ 0x0d029d5e -}; -#else /* * The following are the new timing tables with PIO mode data/taskfile transfer * overclocking fixed... @@ -424,16 +362,13 @@ static u32 sixty_six_base_hpt37x[] = { /* XFER_PIO_1 */ 0x0d02ff26, /* XFER_PIO_0 */ 0x0d42ff7f }; -#endif -#define HPT366_DEBUG_DRIVE_INFO 0 #define HPT371_ALLOW_ATA133_6 1 #define HPT302_ALLOW_ATA133_6 1 #define HPT372_ALLOW_ATA133_6 1 #define HPT370_ALLOW_ATA100_5 0 #define HPT366_ALLOW_ATA66_4 1 #define HPT366_ALLOW_ATA66_3 1 -#define HPT366_MAX_DEVS 8 /* Supported ATA clock frequencies */ enum ata_clock { diff --git a/drivers/ide/ide-pci-generic.c b/drivers/ide/ide-pci-generic.c index 39d4e01f5c9c..a743e68a8903 100644 --- a/drivers/ide/ide-pci-generic.c +++ b/drivers/ide/ide-pci-generic.c @@ -162,9 +162,10 @@ static const struct pci_device_id generic_pci_tbl[] = { #ifdef CONFIG_BLK_DEV_IDE_SATA { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_8237_SATA), 5 }, #endif - { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO), 4 }, { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), 4 }, { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), 4 }, + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_3), 4 }, + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_5), 4 }, { PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), 6 }, /* * Must come last. If you add entries adjust diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 58fc920d5c32..6a0e62542167 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -221,6 +221,8 @@ typedef struct ide_tape_obj { static DEFINE_MUTEX(idetape_ref_mutex); +static DEFINE_MUTEX(idetape_chrdev_mutex); + static struct class *idetape_sysfs_class; static void ide_tape_release(struct device *); @@ -1457,10 +1459,11 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp) if (i >= MAX_HWIFS * MAX_DRIVES) return -ENXIO; - lock_kernel(); + mutex_lock(&idetape_chrdev_mutex); + tape = ide_tape_get(NULL, true, i); if (!tape) { - unlock_kernel(); + mutex_unlock(&idetape_chrdev_mutex); return -ENXIO; } @@ -1519,12 +1522,15 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp) tape->door_locked = DOOR_LOCKED; } } - unlock_kernel(); + mutex_unlock(&idetape_chrdev_mutex); + return 0; out_put_tape: ide_tape_put(tape); - unlock_kernel(); + + mutex_unlock(&idetape_chrdev_mutex); + return retval; } @@ -1551,7 +1557,8 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp) ide_drive_t *drive = tape->drive; unsigned int minor = iminor(inode); - lock_kernel(); + mutex_lock(&idetape_chrdev_mutex); + tape = drive->driver_data; ide_debug_log(IDE_DBG_FUNC, "enter"); @@ -1575,7 +1582,9 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp) } clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags); ide_tape_put(tape); - unlock_kernel(); + + mutex_unlock(&idetape_chrdev_mutex); + return 0; } diff --git a/drivers/ide/ide_platform.c b/drivers/ide/ide_platform.c index b579fbe88370..42965b3e30b9 100644 --- a/drivers/ide/ide_platform.c +++ b/drivers/ide/ide_platform.c @@ -81,14 +81,14 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) if (mmio) { base = devm_ioremap(&pdev->dev, - res_base->start, res_base->end - res_base->start + 1); + res_base->start, resource_size(res_base)); alt_base = devm_ioremap(&pdev->dev, - res_alt->start, res_alt->end - res_alt->start + 1); + res_alt->start, resource_size(res_alt)); } else { base = devm_ioport_map(&pdev->dev, - res_base->start, res_base->end - res_base->start + 1); + res_base->start, resource_size(res_base)); alt_base = devm_ioport_map(&pdev->dev, - res_alt->start, res_alt->end - res_alt->start + 1); + res_alt->start, resource_size(res_alt)); } memset(&hw, 0, sizeof(hw)); diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c index cb812f3700e8..35161dd840a0 100644 --- a/drivers/ide/pdc202xx_old.c +++ b/drivers/ide/pdc202xx_old.c @@ -21,8 +21,6 @@ #define DRV_NAME "pdc202xx_old" -#define PDC202XX_DEBUG_DRIVE_INFO 0 - static void pdc_old_disable_66MHz_clock(ide_hwif_t *); static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed) @@ -34,11 +32,6 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed) u8 AP = 0, BP = 0, CP = 0; u8 TA = 0, TB = 0, TC = 0; -#if PDC202XX_DEBUG_DRIVE_INFO - u32 drive_conf = 0; - pci_read_config_dword(dev, drive_pci, &drive_conf); -#endif - /* * TODO: do this once per channel */ @@ -89,14 +82,6 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed) pci_write_config_byte(dev, drive_pci + 1, BP | TB); pci_write_config_byte(dev, drive_pci + 2, CP | TC); } - -#if PDC202XX_DEBUG_DRIVE_INFO - printk(KERN_DEBUG "%s: %s drive%d 0x%08x ", - drive->name, ide_xfer_verbose(speed), - drive->dn, drive_conf); - pci_read_config_dword(dev, drive_pci, &drive_conf); - printk("0x%08x\n", drive_conf); -#endif } static void pdc202xx_set_pio_mode(ide_drive_t *drive, const u8 pio) diff --git a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c index 3b88eba04c9c..468706082fb5 100644 --- a/drivers/ide/sis5513.c +++ b/drivers/ide/sis5513.c @@ -632,12 +632,3 @@ module_exit(sis5513_ide_exit); MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik"); MODULE_DESCRIPTION("PCI driver module for SIS IDE"); MODULE_LICENSE("GPL"); - -/* - * TODO: - * - CLEANUP - * - More checks in the config registers (force values instead of - * relying on the BIOS setting them correctly). - * - Further optimisations ? - * . for example ATA66+ regs 0x48 & 0x4A - */ diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c index d698da470d6f..3c2bbf0057ea 100644 --- a/drivers/ide/sl82c105.c +++ b/drivers/ide/sl82c105.c @@ -24,13 +24,6 @@ #define DRV_NAME "sl82c105" -#undef DEBUG - -#ifdef DEBUG -#define DBG(arg) printk arg -#else -#define DBG(fmt,...) -#endif /* * SL82C105 PCI config register 0x40 bits. */ @@ -104,9 +97,6 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed) unsigned long timings = (unsigned long)ide_get_drivedata(drive); u16 drv_ctrl; - DBG(("sl82c105_tune_chipset(drive:%s, speed:%s)\n", - drive->name, ide_xfer_verbose(speed))); - drv_ctrl = mwdma_timings[speed - XFER_MW_DMA_0]; /* @@ -196,8 +186,6 @@ static void sl82c105_dma_start(ide_drive_t *drive) struct pci_dev *dev = to_pci_dev(hwif->dev); int reg = 0x44 + drive->dn * 4; - DBG(("%s(drive:%s)\n", __func__, drive->name)); - pci_write_config_word(dev, reg, (unsigned long)ide_get_drivedata(drive) >> 16); @@ -209,8 +197,6 @@ static void sl82c105_dma_clear(ide_drive_t *drive) { struct pci_dev *dev = to_pci_dev(drive->hwif->dev); - DBG(("sl82c105_dma_clear(drive:%s)\n", drive->name)); - sl82c105_reset_host(dev); } @@ -218,11 +204,7 @@ static int sl82c105_dma_end(ide_drive_t *drive) { struct pci_dev *dev = to_pci_dev(drive->hwif->dev); int reg = 0x44 + drive->dn * 4; - int ret; - - DBG(("%s(drive:%s)\n", __func__, drive->name)); - - ret = ide_dma_end(drive); + int ret = ide_dma_end(drive); pci_write_config_word(dev, reg, (unsigned long)ide_get_drivedata(drive)); @@ -239,8 +221,6 @@ static void sl82c105_resetproc(ide_drive_t *drive) struct pci_dev *dev = to_pci_dev(drive->hwif->dev); u32 val; - DBG(("sl82c105_resetproc(drive:%s)\n", drive->name)); - pci_read_config_dword(dev, 0x40, &val); val |= (CTRL_P1F16 | CTRL_P0F16); pci_write_config_dword(dev, 0x40, val); @@ -291,8 +271,6 @@ static int init_chipset_sl82c105(struct pci_dev *dev) { u32 val; - DBG(("init_chipset_sl82c105()\n")); - pci_read_config_dword(dev, 0x40, &val); val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16; pci_write_config_dword(dev, 0x40, val); diff --git a/drivers/ide/slc90e66.c b/drivers/ide/slc90e66.c index 9aec78d3bcff..1ccfb40e7215 100644 --- a/drivers/ide/slc90e66.c +++ b/drivers/ide/slc90e66.c @@ -91,8 +91,7 @@ static void slc90e66_set_dma_mode(ide_drive_t *drive, const u8 speed) if (!(reg48 & u_flag)) pci_write_config_word(dev, 0x48, reg48|u_flag); - /* FIXME: (reg4a & a_speed) ? */ - if ((reg4a & u_speed) != u_speed) { + if ((reg4a & a_speed) != u_speed) { pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); pci_read_config_word(dev, 0x4a, ®4a); pci_write_config_word(dev, 0x4a, reg4a|u_speed); diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c index ea89fddeed91..fd59c0d235b5 100644 --- a/drivers/ide/tx4938ide.c +++ b/drivers/ide/tx4938ide.c @@ -146,7 +146,7 @@ static int __init tx4938ide_probe(struct platform_device *pdev) return -ENODEV; if (!devm_request_mem_region(&pdev->dev, res->start, - res->end - res->start + 1, "tx4938ide")) + resource_size(res), "tx4938ide")) return -EBUSY; mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start, 8 << pdata->ioport_shift); diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index 2cd00b5b45b4..9fd4a0d3206e 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -125,7 +125,7 @@ 0 - no debugging messages 1 - some debugging messages, but none during DMA frame transmission 2 - lots of messages, including during DMA frame transmission - (will cause undeflows if your machine is too slow!) + (will cause underflows if your machine is too slow!) */ #define DV1394_DEBUG_LEVEL 0 diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index 65c1429e4129..d0dc1db80b29 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -82,6 +82,7 @@ * */ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/slab.h> @@ -434,7 +435,6 @@ static void initialize_dma_trm_ctx(struct dma_trm_ctx *d) /* Count the number of available iso contexts */ static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg) { - int i,ctx=0; u32 tmp; reg_write(ohci, reg, 0xffffffff); @@ -443,11 +443,7 @@ static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg) DBGMSG("Iso contexts reg: %08x implemented: %08x", reg, tmp); /* Count the number of contexts */ - for (i=0; i<32; i++) { - if (tmp & 1) ctx++; - tmp >>= 1; - } - return ctx; + return hweight32(tmp); } /* Global initialization */ diff --git a/drivers/ieee802154/fakehard.c b/drivers/ieee802154/fakehard.c index 7c544f7c74c4..d9d0e13efe47 100644 --- a/drivers/ieee802154/fakehard.c +++ b/drivers/ieee802154/fakehard.c @@ -32,9 +32,29 @@ #include <net/nl802154.h> #include <net/wpan-phy.h> -struct wpan_phy *net_to_phy(struct net_device *dev) +struct fakehard_priv { + struct wpan_phy *phy; +}; + +static struct wpan_phy *fake_to_phy(const struct net_device *dev) { - return container_of(dev->dev.parent, struct wpan_phy, dev); + struct fakehard_priv *priv = netdev_priv(dev); + return priv->phy; +} + +/** + * fake_get_phy - Return a phy corresponding to this device. + * @dev: The network device for which to return the wan-phy object + * + * This function returns a wpan-phy object corresponding to the passed + * network device. Reference counter for wpan-phy object is incremented, + * so when the wpan-phy isn't necessary, you should drop the reference + * via @wpan_phy_put() call. + */ +static struct wpan_phy *fake_get_phy(const struct net_device *dev) +{ + struct wpan_phy *phy = fake_to_phy(dev); + return to_phy(get_device(&phy->dev)); } /** @@ -43,7 +63,7 @@ struct wpan_phy *net_to_phy(struct net_device *dev) * * Return the ID of the PAN from the PIB. */ -static u16 fake_get_pan_id(struct net_device *dev) +static u16 fake_get_pan_id(const struct net_device *dev) { BUG_ON(dev->type != ARPHRD_IEEE802154); @@ -58,7 +78,7 @@ static u16 fake_get_pan_id(struct net_device *dev) * device. If the device has not yet had a short address assigned * then this should return 0xFFFF to indicate a lack of association. */ -static u16 fake_get_short_addr(struct net_device *dev) +static u16 fake_get_short_addr(const struct net_device *dev) { BUG_ON(dev->type != ARPHRD_IEEE802154); @@ -78,7 +98,7 @@ static u16 fake_get_short_addr(struct net_device *dev) * Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006 * document. */ -static u8 fake_get_dsn(struct net_device *dev) +static u8 fake_get_dsn(const struct net_device *dev) { BUG_ON(dev->type != ARPHRD_IEEE802154); @@ -98,7 +118,7 @@ static u8 fake_get_dsn(struct net_device *dev) * Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006 * document. */ -static u8 fake_get_bsn(struct net_device *dev) +static u8 fake_get_bsn(const struct net_device *dev) { BUG_ON(dev->type != ARPHRD_IEEE802154); @@ -121,7 +141,7 @@ static u8 fake_get_bsn(struct net_device *dev) static int fake_assoc_req(struct net_device *dev, struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap) { - struct wpan_phy *phy = net_to_phy(dev); + struct wpan_phy *phy = fake_to_phy(dev); mutex_lock(&phy->pib_lock); phy->current_channel = channel; @@ -196,7 +216,7 @@ static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr, u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, u8 coord_realign) { - struct wpan_phy *phy = net_to_phy(dev); + struct wpan_phy *phy = fake_to_phy(dev); mutex_lock(&phy->pib_lock); phy->current_channel = channel; @@ -239,6 +259,8 @@ static struct ieee802154_mlme_ops fake_mlme = { .start_req = fake_start_req, .scan_req = fake_scan_req, + .get_phy = fake_get_phy, + .get_pan_id = fake_get_pan_id, .get_short_addr = fake_get_short_addr, .get_dsn = fake_get_dsn, @@ -310,7 +332,7 @@ static const struct net_device_ops fake_ops = { static void ieee802154_fake_destruct(struct net_device *dev) { - struct wpan_phy *phy = net_to_phy(dev); + struct wpan_phy *phy = fake_to_phy(dev); wpan_phy_unregister(phy); free_netdev(dev); @@ -335,13 +357,14 @@ static void ieee802154_fake_setup(struct net_device *dev) static int __devinit ieee802154fake_probe(struct platform_device *pdev) { struct net_device *dev; + struct fakehard_priv *priv; struct wpan_phy *phy = wpan_phy_alloc(0); int err; if (!phy) return -ENOMEM; - dev = alloc_netdev(0, "hardwpan%d", ieee802154_fake_setup); + dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d", ieee802154_fake_setup); if (!dev) { wpan_phy_free(phy); return -ENOMEM; @@ -353,12 +376,23 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev) dev->addr_len); memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); - phy->channels_supported = (1 << 27) - 1; + /* + * For now we'd like to emulate 2.4 GHz-only device, + * both O-QPSK and CSS + */ + /* 2.4 GHz O-QPSK 802.15.4-2003 */ + phy->channels_supported[0] |= 0x7FFF800; + /* 2.4 GHz CSS 802.15.4a-2007 */ + phy->channels_supported[3] |= 0x3fff; + phy->transmit_power = 0xbf; dev->netdev_ops = &fake_ops; dev->ml_priv = &fake_mlme; + priv = netdev_priv(dev); + priv->phy = phy; + /* * If the name is a format string the caller wants us to do a * name allocation. @@ -369,11 +403,12 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev) goto out; } + wpan_phy_set_dev(phy, &pdev->dev); SET_NETDEV_DEV(dev, &phy->dev); platform_set_drvdata(pdev, dev); - err = wpan_phy_register(&pdev->dev, phy); + err = wpan_phy_register(phy); if (err) goto out; diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c index 72ed3396b721..0677fc7dfd51 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.c +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c @@ -589,7 +589,7 @@ static int cxio_hal_destroy_ctrl_qp(struct cxio_rdev *rdev_p) /* write len bytes of data into addr (32B aligned address) * If data is NULL, clear len byte of memory to zero. - * caller aquires the ctrl_qp lock before the call + * caller acquires the ctrl_qp lock before the call */ static int cxio_hal_ctrl_qp_write_mem(struct cxio_rdev *rdev_p, u32 addr, u32 len, void *data) diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 40dbe54056c7..73933a41ce84 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -1821,7 +1821,6 @@ done: static int ipath_open(struct inode *in, struct file *fp) { /* The real work is performed later in ipath_assign_port() */ - cycle_kernel_lock(); fp->private_data = kzalloc(sizeof(struct ipath_filedata), GFP_KERNEL); return fp->private_data ? 0 : -ENOMEM; } diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c index 4bd39c8af80f..37d12e5efa49 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6110.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c @@ -381,7 +381,7 @@ static const ipath_err_t infinipath_hwe_htclnkbbyte1crcerr = #define IPATH_GPIO_SCL \ (1ULL << (_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT)) -/* keep the code below somewhat more readonable; not used elsewhere */ +/* keep the code below somewhat more readable; not used elsewhere */ #define _IPATH_HTLINK0_CRCBITS (infinipath_hwe_htclnkabyte0crcerr | \ infinipath_hwe_htclnkabyte1crcerr) #define _IPATH_HTLINK1_CRCBITS (infinipath_hwe_htclnkbbyte0crcerr | \ diff --git a/drivers/infiniband/hw/ipath/ipath_sd7220.c b/drivers/infiniband/hw/ipath/ipath_sd7220.c index aa47eb549520..2a68d9f624dd 100644 --- a/drivers/infiniband/hw/ipath/ipath_sd7220.c +++ b/drivers/infiniband/hw/ipath/ipath_sd7220.c @@ -614,7 +614,7 @@ static int epb_trans(struct ipath_devdata *dd, u16 reg, u64 i_val, u64 *o_vp) * @wd: Write Data - value to set in register * @mask: ones where data should be spliced into reg. * - * Basic register read/modify/write, with un-needed acesses elided. That is, + * Basic register read/modify/write, with un-needed accesses elided. That is, * a mask of zero will prevent write, while a mask of 0xFF will prevent read. * returns current (presumed, if a write was done) contents of selected * register, or <0 if errors. @@ -989,7 +989,7 @@ static struct rxeq_init { /* Set DFELTHFDR/HDR thresholds */ RXEQ_VAL(7, 8, 0, 0, 0, 0), /* FDR */ RXEQ_VAL(7, 0x21, 0, 0, 0, 0), /* HDR */ - /* Set TLTHFDR/HDR theshold */ + /* Set TLTHFDR/HDR threshold */ RXEQ_VAL(7, 9, 2, 2, 2, 2), /* FDR */ RXEQ_VAL(7, 0x23, 2, 2, 2, 2), /* HDR */ /* Set Preamp setting 2 (ZFR/ZCNT) */ diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 219b10397b4d..256a00c6aeea 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -352,7 +352,7 @@ static int set_kernel_sq_size(struct mlx4_ib_dev *dev, struct ib_qp_cap *cap, * anymore, so we do this only if selective signaling is off. * * Further, on 32-bit platforms, we can't use vmap() to make - * the QP buffer virtually contigious. Thus we have to use + * the QP buffer virtually contiguous. Thus we have to use * constant-sized WRs to make sure a WR is always fully within * a single page-sized chunk. * diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index e593af3354b8..de18fdfdadf2 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -1080,11 +1080,14 @@ static int nes_netdev_set_rx_csum(struct net_device *netdev, u32 enable) /** - * nes_netdev_get_stats_count + * nes_netdev_get_sset_count */ -static int nes_netdev_get_stats_count(struct net_device *netdev) +static int nes_netdev_get_sset_count(struct net_device *netdev, int stringset) { - return NES_ETHTOOL_STAT_COUNT; + if (stringset == ETH_SS_STATS) + return NES_ETHTOOL_STAT_COUNT; + else + return -EINVAL; } @@ -1264,7 +1267,6 @@ static void nes_netdev_get_drvinfo(struct net_device *netdev, sprintf(drvinfo->fw_version, "%u.%u", nesadapter->firmware_version>>16, nesadapter->firmware_version & 0x000000ff); strcpy(drvinfo->version, DRV_VERSION); - drvinfo->n_stats = nes_netdev_get_stats_count(netdev); drvinfo->testinfo_len = 0; drvinfo->eedump_len = 0; drvinfo->regdump_len = 0; @@ -1516,7 +1518,7 @@ static const struct ethtool_ops nes_ethtool_ops = { .get_rx_csum = nes_netdev_get_rx_csum, .get_sg = ethtool_op_get_sg, .get_strings = nes_netdev_get_strings, - .get_stats_count = nes_netdev_get_stats_count, + .get_sset_count = nes_netdev_get_sset_count, .get_ethtool_stats = nes_netdev_get_ethtool_stats, .get_drvinfo = nes_netdev_get_drvinfo, .get_coalesce = nes_netdev_get_coalesce, diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index add9188663ff..5f7a6fca0a4d 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -625,6 +625,7 @@ static struct iscsi_transport iscsi_iser_transport = { ISCSI_USERNAME | ISCSI_PASSWORD | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | + ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO | ISCSI_PING_TMO | ISCSI_RECV_TMO | ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, .host_param_mask = ISCSI_HOST_HWADDRESS | diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index ea9e1556e0d6..8579f32ce38e 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -499,7 +499,7 @@ void iser_conn_init(struct iser_conn *ib_conn) /** * starts the process of connecting to the target - * sleeps untill the connection is established or rejected + * sleeps until the connection is established or rejected */ int iser_connect(struct iser_conn *ib_conn, struct sockaddr_in *src_addr, diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index cd50c00ab20f..07c2cd43109c 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -8,7 +8,7 @@ menu "Input device support" config INPUT tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED default y - ---help--- + help Say Y here if you have any input device (mouse, keyboard, tablet, joystick, steering wheel ...) connected to your system and want it to be available to applications. This includes standard PS/2 @@ -27,8 +27,7 @@ if INPUT config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" - default n - ---help--- + help Say Y here if you have memoryless force-feedback input device such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual Power 2, or similar. You will also need to enable hardware-specific @@ -52,12 +51,25 @@ config INPUT_POLLDEV To compile this driver as a module, choose M here: the module will be called input-polldev. +config INPUT_SPARSEKMAP + tristate "Sparse keymap support library" + help + Say Y here if you are using a driver for an input + device that uses sparse keymap. This option is only + useful for out-of-tree drivers since in-tree drivers + select it automatically. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sparse-keymap. + comment "Userland interfaces" config INPUT_MOUSEDEV tristate "Mouse interface" if EMBEDDED default y - ---help--- + help Say Y here if you want your mouse to be accessible as char devices 13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an emulated IntelliMouse Explorer PS/2 mouse. That way, all user space @@ -73,7 +85,7 @@ config INPUT_MOUSEDEV_PSAUX bool "Provide legacy /dev/psaux device" default y depends on INPUT_MOUSEDEV - ---help--- + help Say Y here if you want your mouse also be accessible as char device 10:1 - /dev/psaux. The data available through /dev/psaux is exactly the same as the data from /dev/input/mice. @@ -103,7 +115,7 @@ config INPUT_MOUSEDEV_SCREEN_Y config INPUT_JOYDEV tristate "Joystick interface" - ---help--- + help Say Y here if you want your joystick or gamepad to be accessible as char device 13:0+ - /dev/input/jsX device. @@ -125,7 +137,7 @@ config INPUT_EVDEV config INPUT_EVBUG tristate "Event debugging" - ---help--- + help Say Y here if you have a problem with the input subsystem and want all events (keypresses, mouse movements), to be output to the system log. While this is useful for debugging, it's also @@ -140,7 +152,7 @@ config INPUT_EVBUG config INPUT_APMPOWER tristate "Input Power Event -> APM Bridge" if EMBEDDED depends on INPUT && APM_EMULATION - ---help--- + help Say Y here if you want suspend key events to trigger a user requested suspend through APM. This is useful on embedded systems where such behaviour is desired without userspace @@ -153,6 +165,7 @@ config XEN_KBDDEV_FRONTEND tristate "Xen virtual keyboard and mouse support" depends on XEN_FBDEV_FRONTEND default y + select XEN_XENBUS_FRONTEND help This driver implements the front-end of the Xen virtual keyboard and mouse device driver. It communicates with a back-end diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 4c9c745a7020..7ad212d31f99 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -9,6 +9,7 @@ input-core-objs := input.o input-compat.o ff-core.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o +obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index 38df81fcdc3a..b2f07aa1604b 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -353,7 +353,7 @@ int input_ff_create(struct input_dev *dev, int max_effects) EXPORT_SYMBOL_GPL(input_ff_create); /** - * input_ff_free() - frees force feedback portion of input device + * input_ff_destroy() - frees force feedback portion of input device * @dev: input device supporting force feedback * * This function is only needed in error path as input core will @@ -369,6 +369,7 @@ void input_ff_destroy(struct input_dev *dev) if (ff->destroy) ff->destroy(ff); kfree(ff->private); + kfree(ff->effects); kfree(ff); dev->ff = NULL; } diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 0d3ce7a50fb1..aa6713b4a988 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -56,14 +56,10 @@ static void input_polldev_stop_workqueue(void) mutex_unlock(&polldev_mutex); } -static void input_polled_device_work(struct work_struct *work) +static void input_polldev_queue_work(struct input_polled_dev *dev) { - struct input_polled_dev *dev = - container_of(work, struct input_polled_dev, work.work); unsigned long delay; - dev->poll(dev); - delay = msecs_to_jiffies(dev->poll_interval); if (delay >= HZ) delay = round_jiffies_relative(delay); @@ -71,6 +67,15 @@ static void input_polled_device_work(struct work_struct *work) queue_delayed_work(polldev_wq, &dev->work, delay); } +static void input_polled_device_work(struct work_struct *work) +{ + struct input_polled_dev *dev = + container_of(work, struct input_polled_dev, work.work); + + dev->poll(dev); + input_polldev_queue_work(dev); +} + static int input_open_polled_device(struct input_dev *input) { struct input_polled_dev *dev = input_get_drvdata(input); @@ -80,11 +85,12 @@ static int input_open_polled_device(struct input_dev *input) if (error) return error; - if (dev->flush) - dev->flush(dev); + if (dev->open) + dev->open(dev); - queue_delayed_work(polldev_wq, &dev->work, - msecs_to_jiffies(dev->poll_interval)); + /* Only start polling if polling is enabled */ + if (dev->poll_interval > 0) + queue_delayed_work(polldev_wq, &dev->work, 0); return 0; } @@ -95,8 +101,88 @@ static void input_close_polled_device(struct input_dev *input) cancel_delayed_work_sync(&dev->work); input_polldev_stop_workqueue(); + + if (dev->close) + dev->close(dev); } +/* SYSFS interface */ + +static ssize_t input_polldev_get_poll(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval); +} + +static ssize_t input_polldev_set_poll(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + struct input_dev *input = polldev->input; + unsigned long interval; + + if (strict_strtoul(buf, 0, &interval)) + return -EINVAL; + + if (interval < polldev->poll_interval_min) + return -EINVAL; + + if (interval > polldev->poll_interval_max) + return -EINVAL; + + mutex_lock(&input->mutex); + + polldev->poll_interval = interval; + + if (input->users) { + cancel_delayed_work_sync(&polldev->work); + if (polldev->poll_interval > 0) + input_polldev_queue_work(polldev); + } + + mutex_unlock(&input->mutex); + + return count; +} + +static DEVICE_ATTR(poll, S_IRUGO | S_IWUSR, input_polldev_get_poll, + input_polldev_set_poll); + + +static ssize_t input_polldev_get_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval_max); +} + +static DEVICE_ATTR(max, S_IRUGO, input_polldev_get_max, NULL); + +static ssize_t input_polldev_get_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval_min); +} + +static DEVICE_ATTR(min, S_IRUGO, input_polldev_get_min, NULL); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_poll.attr, + &dev_attr_max.attr, + &dev_attr_min.attr, + NULL +}; + +static struct attribute_group input_polldev_attribute_group = { + .attrs = sysfs_attrs +}; + /** * input_allocate_polled_device - allocated memory polled device * @@ -126,7 +212,7 @@ EXPORT_SYMBOL(input_allocate_polled_device); * @dev: device to free * * The function frees memory allocated for polling device and drops - * reference to the associated input device (if present). + * reference to the associated input device. */ void input_free_polled_device(struct input_polled_dev *dev) { @@ -150,15 +236,38 @@ EXPORT_SYMBOL(input_free_polled_device); int input_register_polled_device(struct input_polled_dev *dev) { struct input_dev *input = dev->input; + int error; input_set_drvdata(input, dev); INIT_DELAYED_WORK(&dev->work, input_polled_device_work); if (!dev->poll_interval) dev->poll_interval = 500; + if (!dev->poll_interval_max) + dev->poll_interval_max = dev->poll_interval; input->open = input_open_polled_device; input->close = input_close_polled_device; - return input_register_device(input); + error = input_register_device(input); + if (error) + return error; + + error = sysfs_create_group(&input->dev.kobj, + &input_polldev_attribute_group); + if (error) { + input_unregister_device(input); + return error; + } + + /* + * Take extra reference to the underlying input device so + * that it survives call to input_unregister_polled_device() + * and is deleted only after input_free_polled_device() + * has been invoked. This is needed to ease task of freeing + * sparse keymaps. + */ + input_get_device(input); + + return 0; } EXPORT_SYMBOL(input_register_polled_device); @@ -169,13 +278,13 @@ EXPORT_SYMBOL(input_register_polled_device); * The function unregisters previously registered polled input * device from input layer. Polling is stopped and device is * ready to be freed with call to input_free_polled_device(). - * Callers should not attempt to access dev->input pointer - * after calling this function. */ void input_unregister_polled_device(struct input_polled_dev *dev) { + sysfs_remove_group(&dev->input->dev.kobj, + &input_polldev_attribute_group); + input_unregister_device(dev->input); - dev->input = NULL; } EXPORT_SYMBOL(input_unregister_polled_device); diff --git a/drivers/input/input.c b/drivers/input/input.c index 2266ecbfbc01..5c16001959cc 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1658,6 +1658,38 @@ void input_unregister_handler(struct input_handler *handler) EXPORT_SYMBOL(input_unregister_handler); /** + * input_handler_for_each_handle - handle iterator + * @handler: input handler to iterate + * @data: data for the callback + * @fn: function to be called for each handle + * + * Iterate over @bus's list of devices, and call @fn for each, passing + * it @data and stop when @fn returns a non-zero value. The function is + * using RCU to traverse the list and therefore may be usind in atonic + * contexts. The @fn callback is invoked from RCU critical section and + * thus must not sleep. + */ +int input_handler_for_each_handle(struct input_handler *handler, void *data, + int (*fn)(struct input_handle *, void *)) +{ + struct input_handle *handle; + int retval = 0; + + rcu_read_lock(); + + list_for_each_entry_rcu(handle, &handler->h_list, h_node) { + retval = fn(handle, data); + if (retval) + break; + } + + rcu_read_unlock(); + + return retval; +} +EXPORT_SYMBOL(input_handler_for_each_handle); + +/** * input_register_handle - register a new input handle * @handle: handle to register * @@ -1690,7 +1722,7 @@ int input_register_handle(struct input_handle *handle) * we can't be racing with input_unregister_handle() * and so separate lock is not needed here. */ - list_add_tail(&handle->h_node, &handler->h_list); + list_add_tail_rcu(&handle->h_node, &handler->h_list); if (handler->start) handler->start(handle); @@ -1713,7 +1745,7 @@ void input_unregister_handle(struct input_handle *handle) { struct input_dev *dev = handle->dev; - list_del_init(&handle->h_node); + list_del_rcu(&handle->h_node); /* * Take dev->mutex to prevent race with input_release_device(). @@ -1721,6 +1753,7 @@ void input_unregister_handle(struct input_handle *handle) mutex_lock(&dev->mutex); list_del_rcu(&handle->d_node); mutex_unlock(&dev->mutex); + synchronize_rcu(); } EXPORT_SYMBOL(input_unregister_handle); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 79e3edcced1a..482cb1204e43 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -125,6 +125,7 @@ static const struct xpad_device { { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, + { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", MAP_DPAD_TO_AXES, XTYPE_XBOX }, @@ -146,6 +147,7 @@ static const struct xpad_device { { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN } }; @@ -212,6 +214,7 @@ static struct usb_device_id xpad_table [] = { XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ XPAD_XBOX360_VENDOR(0x1bad), /* Rock Band Drums */ + XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ { } }; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ee98b1bc5d89..203b88a82b56 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -361,6 +361,16 @@ config KEYBOARD_SH_KEYSC To compile this driver as a module, choose M here: the module will be called sh_keysc. +config KEYBOARD_DAVINCI + tristate "TI DaVinci Key Scan" + depends on ARCH_DAVINCI_DM365 + help + Say Y to enable keypad module support for the TI DaVinci + platforms (DM365). + + To compile this driver as a module, choose M here: the + module will be called davinci_keyscan. + config KEYBOARD_OMAP tristate "TI OMAP keypad support" depends on (ARCH_OMAP1 || ARCH_OMAP2) diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index babad5e58b77..68c017235ce9 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o +obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 28e6110d1ff8..a3573570c52f 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1567,9 +1567,8 @@ static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { +static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { { - .ident = "Dell Laptop", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ @@ -1578,7 +1577,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_dell_laptop_forced_release_keys, }, { - .ident = "Dell Laptop", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ @@ -1587,7 +1585,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_dell_laptop_forced_release_keys, }, { - .ident = "HP 2133", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"), @@ -1596,7 +1593,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_hp_forced_release_keys, }, { - .ident = "HP Pavilion ZV6100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"), @@ -1605,7 +1601,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "HP Presario R4000", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"), @@ -1614,7 +1609,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "HP Presario R4100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"), @@ -1623,7 +1617,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "HP Presario R4200", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"), @@ -1632,7 +1625,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "Inventec Symphony", + /* Inventec Symphony */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"), DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"), @@ -1641,7 +1634,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "Samsung NC10", + /* Samsung NC10 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), @@ -1650,7 +1643,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_samsung_forced_release_keys, }, { - .ident = "Samsung NC20", + /* Samsung NC20 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "NC20"), @@ -1659,7 +1652,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_samsung_forced_release_keys, }, { - .ident = "Samsung SQ45S70S", + /* Samsung SQ45S70S */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"), @@ -1668,7 +1661,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_samsung_forced_release_keys, }, { - .ident = "Fujitsu Amilo PA 1510", + /* Fujitsu Amilo PA 1510 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"), @@ -1677,7 +1670,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "Fujitsu Amilo Pi 3525", + /* Fujitsu Amilo Pi 3525 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"), @@ -1686,7 +1679,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_amilo_pi3525_forced_release_keys, }, { - .ident = "Fujitsu Amilo Xi 3650", + /* Fujitsu Amilo Xi 3650 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 3650"), @@ -1695,7 +1688,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_amilo_xi3650_forced_release_keys, }, { - .ident = "Soltech Corporation TA12", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "TA12"), @@ -1704,7 +1696,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkdb_soltech_ta12_forced_release_keys, }, { - .ident = "OQO Model 01+", + /* OQO Model 01+ */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "OQO"), DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c new file mode 100644 index 000000000000..6e52d855f637 --- /dev/null +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -0,0 +1,337 @@ +/* + * DaVinci Key Scan Driver for TI platforms + * + * Copyright (C) 2009 Texas Instruments, Inc + * + * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com> + * + * Intial Code: Sandeep Paulraj <s-paulraj@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/errno.h> + +#include <asm/irq.h> + +#include <mach/hardware.h> +#include <mach/irqs.h> +#include <mach/keyscan.h> + +/* Key scan registers */ +#define DAVINCI_KEYSCAN_KEYCTRL 0x0000 +#define DAVINCI_KEYSCAN_INTENA 0x0004 +#define DAVINCI_KEYSCAN_INTFLAG 0x0008 +#define DAVINCI_KEYSCAN_INTCLR 0x000c +#define DAVINCI_KEYSCAN_STRBWIDTH 0x0010 +#define DAVINCI_KEYSCAN_INTERVAL 0x0014 +#define DAVINCI_KEYSCAN_CONTTIME 0x0018 +#define DAVINCI_KEYSCAN_CURRENTST 0x001c +#define DAVINCI_KEYSCAN_PREVSTATE 0x0020 +#define DAVINCI_KEYSCAN_EMUCTRL 0x0024 +#define DAVINCI_KEYSCAN_IODFTCTRL 0x002c + +/* Key Control Register (KEYCTRL) */ +#define DAVINCI_KEYSCAN_KEYEN 0x00000001 +#define DAVINCI_KEYSCAN_PREVMODE 0x00000002 +#define DAVINCI_KEYSCAN_CHATOFF 0x00000004 +#define DAVINCI_KEYSCAN_AUTODET 0x00000008 +#define DAVINCI_KEYSCAN_SCANMODE 0x00000010 +#define DAVINCI_KEYSCAN_OUTTYPE 0x00000020 + +/* Masks for the interrupts */ +#define DAVINCI_KEYSCAN_INT_CONT 0x00000008 +#define DAVINCI_KEYSCAN_INT_OFF 0x00000004 +#define DAVINCI_KEYSCAN_INT_ON 0x00000002 +#define DAVINCI_KEYSCAN_INT_CHANGE 0x00000001 +#define DAVINCI_KEYSCAN_INT_ALL 0x0000000f + +struct davinci_ks { + struct input_dev *input; + struct davinci_ks_platform_data *pdata; + int irq; + void __iomem *base; + resource_size_t pbase; + size_t base_size; + unsigned short keymap[]; +}; + +/* Initializing the kp Module */ +static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks) +{ + struct device *dev = &davinci_ks->input->dev; + struct davinci_ks_platform_data *pdata = davinci_ks->pdata; + u32 matrix_ctrl; + + /* Enable all interrupts */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + /* Clear interrupts if any */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTCLR); + + /* Setup the scan period = strobe + interval */ + __raw_writel(pdata->strobe, + davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH); + __raw_writel(pdata->interval, + davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL); + __raw_writel(0x01, + davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME); + + /* Define matrix type */ + switch (pdata->matrix_type) { + case DAVINCI_KEYSCAN_MATRIX_4X4: + matrix_ctrl = 0; + break; + case DAVINCI_KEYSCAN_MATRIX_5X3: + matrix_ctrl = (1 << 6); + break; + default: + dev_err(dev->parent, "wrong matrix type\n"); + return -EINVAL; + } + + /* Enable key scan module and set matrix type */ + __raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN | + matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL); + + return 0; +} + +static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id) +{ + struct davinci_ks *davinci_ks = dev_id; + struct device *dev = &davinci_ks->input->dev; + unsigned short *keymap = davinci_ks->keymap; + int keymapsize = davinci_ks->pdata->keymapsize; + u32 prev_status, new_status, changed; + bool release; + int keycode = KEY_UNKNOWN; + int i; + + /* Disable interrupt */ + __raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + /* Reading previous and new status of the key scan */ + prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE); + new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST); + + changed = prev_status ^ new_status; + + if (changed) { + /* + * It goes through all bits in 'changed' to ensure + * that no key changes are being missed + */ + for (i = 0 ; i < keymapsize; i++) { + if ((changed>>i) & 0x1) { + keycode = keymap[i]; + release = (new_status >> i) & 0x1; + dev_dbg(dev->parent, "key %d %s\n", keycode, + release ? "released" : "pressed"); + input_report_key(davinci_ks->input, keycode, + !release); + input_sync(davinci_ks->input); + } + } + /* Clearing interrupt */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTCLR); + } + + /* Enable interrupts */ + __raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + return IRQ_HANDLED; +} + +static int __init davinci_ks_probe(struct platform_device *pdev) +{ + struct davinci_ks *davinci_ks; + struct input_dev *key_dev; + struct resource *res, *mem; + struct device *dev = &pdev->dev; + struct davinci_ks_platform_data *pdata = pdev->dev.platform_data; + int error, i; + + if (!pdata->keymap) { + dev_dbg(dev, "no keymap from pdata\n"); + return -EINVAL; + } + + davinci_ks = kzalloc(sizeof(struct davinci_ks) + + sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL); + if (!davinci_ks) { + dev_dbg(dev, "could not allocate memory for private data\n"); + return -ENOMEM; + } + + memcpy(davinci_ks->keymap, pdata->keymap, + sizeof(unsigned short) * pdata->keymapsize); + + key_dev = input_allocate_device(); + if (!key_dev) { + dev_dbg(dev, "could not allocate input device\n"); + error = -ENOMEM; + goto fail1; + } + + davinci_ks->input = key_dev; + + davinci_ks->irq = platform_get_irq(pdev, 0); + if (davinci_ks->irq < 0) { + dev_err(dev, "no key scan irq\n"); + error = davinci_ks->irq; + goto fail2; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no mem resource\n"); + error = -EINVAL; + goto fail2; + } + + davinci_ks->pbase = res->start; + davinci_ks->base_size = resource_size(res); + + mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size, + pdev->name); + if (!mem) { + dev_err(dev, "key scan registers at %08x are not free\n", + davinci_ks->pbase); + error = -EBUSY; + goto fail2; + } + + davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size); + if (!davinci_ks->base) { + dev_err(dev, "can't ioremap MEM resource.\n"); + error = -ENOMEM; + goto fail3; + } + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, key_dev->evbit); + + /* Setup input device */ + __set_bit(EV_KEY, key_dev->evbit); + + /* Setup the platform data */ + davinci_ks->pdata = pdata; + + for (i = 0; i < davinci_ks->pdata->keymapsize; i++) + __set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit); + + key_dev->name = "davinci_keyscan"; + key_dev->phys = "davinci_keyscan/input0"; + key_dev->dev.parent = &pdev->dev; + key_dev->id.bustype = BUS_HOST; + key_dev->id.vendor = 0x0001; + key_dev->id.product = 0x0001; + key_dev->id.version = 0x0001; + key_dev->keycode = davinci_ks->keymap; + key_dev->keycodesize = sizeof(davinci_ks->keymap[0]); + key_dev->keycodemax = davinci_ks->pdata->keymapsize; + + error = input_register_device(davinci_ks->input); + if (error < 0) { + dev_err(dev, "unable to register davinci key scan device\n"); + goto fail4; + } + + error = request_irq(davinci_ks->irq, davinci_ks_interrupt, + IRQF_DISABLED, pdev->name, davinci_ks); + if (error < 0) { + dev_err(dev, "unable to register davinci key scan interrupt\n"); + goto fail5; + } + + error = davinci_ks_initialize(davinci_ks); + if (error < 0) { + dev_err(dev, "unable to initialize davinci key scan device\n"); + goto fail6; + } + + platform_set_drvdata(pdev, davinci_ks); + return 0; + +fail6: + free_irq(davinci_ks->irq, davinci_ks); +fail5: + input_unregister_device(davinci_ks->input); + key_dev = NULL; +fail4: + iounmap(davinci_ks->base); +fail3: + release_mem_region(davinci_ks->pbase, davinci_ks->base_size); +fail2: + input_free_device(key_dev); +fail1: + kfree(davinci_ks); + + return error; +} + +static int __devexit davinci_ks_remove(struct platform_device *pdev) +{ + struct davinci_ks *davinci_ks = platform_get_drvdata(pdev); + + free_irq(davinci_ks->irq, davinci_ks); + + input_unregister_device(davinci_ks->input); + + iounmap(davinci_ks->base); + release_mem_region(davinci_ks->pbase, davinci_ks->base_size); + + platform_set_drvdata(pdev, NULL); + + kfree(davinci_ks); + + return 0; +} + +static struct platform_driver davinci_ks_driver = { + .driver = { + .name = "davinci_keyscan", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(davinci_ks_remove), +}; + +static int __init davinci_ks_init(void) +{ + return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe); +} +module_init(davinci_ks_init); + +static void __exit davinci_ks_exit(void) +{ + platform_driver_unregister(&davinci_ks_driver); +} +module_exit(davinci_ks_exit); + +MODULE_AUTHOR("Miguel Aguilar"); +MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 77d130914259..1aff3b76effd 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -23,8 +23,7 @@ #include <linux/input.h> #include <linux/gpio_keys.h> #include <linux/workqueue.h> - -#include <asm/gpio.h> +#include <linux/gpio.h> struct gpio_button_data { struct gpio_keys_button *button; @@ -38,10 +37,8 @@ struct gpio_keys_drvdata { struct gpio_button_data data[0]; }; -static void gpio_keys_report_event(struct work_struct *work) +static void gpio_keys_report_event(struct gpio_button_data *bdata) { - struct gpio_button_data *bdata = - container_of(work, struct gpio_button_data, work); struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; @@ -51,6 +48,14 @@ static void gpio_keys_report_event(struct work_struct *work) input_sync(input); } +static void gpio_keys_work_func(struct work_struct *work) +{ + struct gpio_button_data *bdata = + container_of(work, struct gpio_button_data, work); + + gpio_keys_report_event(bdata); +} + static void gpio_keys_timer(unsigned long _data) { struct gpio_button_data *data = (struct gpio_button_data *)_data; @@ -74,10 +79,62 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static int __devinit gpio_keys_setup_key(struct device *dev, + struct gpio_button_data *bdata, + struct gpio_keys_button *button) +{ + char *desc = button->desc ? button->desc : "gpio_keys"; + int irq, error; + + setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata); + INIT_WORK(&bdata->work, gpio_keys_work_func); + + error = gpio_request(button->gpio, desc); + if (error < 0) { + dev_err(dev, "failed to request GPIO %d, error %d\n", + button->gpio, error); + goto fail2; + } + + error = gpio_direction_input(button->gpio); + if (error < 0) { + dev_err(dev, "failed to configure" + " direction for GPIO %d, error %d\n", + button->gpio, error); + goto fail3; + } + + irq = gpio_to_irq(button->gpio); + if (irq < 0) { + error = irq; + dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", + button->gpio, error); + goto fail3; + } + + error = request_irq(irq, gpio_keys_isr, + IRQF_SHARED | + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + desc, bdata); + if (error) { + dev_err(dev, "Unable to claim irq %d; error %d\n", + irq, error); + goto fail3; + } + + return 0; + +fail3: + gpio_free(button->gpio); +fail2: + return error; +} + static int __devinit gpio_keys_probe(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata; + struct device *dev = &pdev->dev; struct input_dev *input; int i, error; int wakeup = 0; @@ -87,6 +144,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) GFP_KERNEL); input = input_allocate_device(); if (!ddata || !input) { + dev_err(dev, "failed to allocate state\n"); error = -ENOMEM; goto fail1; } @@ -111,52 +169,14 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; - int irq; unsigned int type = button->type ?: EV_KEY; bdata->input = input; bdata->button = button; - setup_timer(&bdata->timer, - gpio_keys_timer, (unsigned long)bdata); - INIT_WORK(&bdata->work, gpio_keys_report_event); - - error = gpio_request(button->gpio, button->desc ?: "gpio_keys"); - if (error < 0) { - pr_err("gpio-keys: failed to request GPIO %d," - " error %d\n", button->gpio, error); - goto fail2; - } - - error = gpio_direction_input(button->gpio); - if (error < 0) { - pr_err("gpio-keys: failed to configure input" - " direction for GPIO %d, error %d\n", - button->gpio, error); - gpio_free(button->gpio); - goto fail2; - } - - irq = gpio_to_irq(button->gpio); - if (irq < 0) { - error = irq; - pr_err("gpio-keys: Unable to get irq number" - " for GPIO %d, error %d\n", - button->gpio, error); - gpio_free(button->gpio); - goto fail2; - } - error = request_irq(irq, gpio_keys_isr, - IRQF_SHARED | - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - button->desc ? button->desc : "gpio_keys", - bdata); - if (error) { - pr_err("gpio-keys: Unable to claim irq %d; error %d\n", - irq, error); - gpio_free(button->gpio); + error = gpio_keys_setup_key(dev, bdata, button); + if (error) goto fail2; - } if (button->wakeup) wakeup = 1; @@ -166,11 +186,16 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { - pr_err("gpio-keys: Unable to register input device, " + dev_err(dev, "Unable to register input device, " "error: %d\n", error); goto fail2; } + /* get current state of buttons */ + for (i = 0; i < pdata->nbuttons; i++) + gpio_keys_report_event(&ddata->data[i]); + input_sync(input); + device_init_wakeup(&pdev->dev, wakeup); return 0; @@ -239,18 +264,21 @@ static int gpio_keys_suspend(struct device *dev) static int gpio_keys_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; int i; - if (device_may_wakeup(&pdev->dev)) { - for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; - if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); - disable_irq_wake(irq); - } + for (i = 0; i < pdata->nbuttons; i++) { + + struct gpio_keys_button *button = &pdata->buttons[i]; + if (button->wakeup && device_may_wakeup(&pdev->dev)) { + int irq = gpio_to_irq(button->gpio); + disable_irq_wake(irq); } + + gpio_keys_report_event(&ddata->data[i]); } + input_sync(ddata->input); return 0; } diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c index f9847e0fb553..fa9bb6d235e2 100644 --- a/drivers/input/keyboard/lkkbd.c +++ b/drivers/input/keyboard/lkkbd.c @@ -72,9 +72,9 @@ #define DRIVER_DESC "LK keyboard driver" -MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_LICENSE ("GPL"); +MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); /* * Known parameters: @@ -85,27 +85,27 @@ MODULE_LICENSE ("GPL"); * Please notice that there's not yet an API to set these at runtime. */ static int bell_volume = 100; /* % */ -module_param (bell_volume, int, 0); -MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%"); +module_param(bell_volume, int, 0); +MODULE_PARM_DESC(bell_volume, "Bell volume (in %). default is 100%"); static int keyclick_volume = 100; /* % */ -module_param (keyclick_volume, int, 0); -MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%"); +module_param(keyclick_volume, int, 0); +MODULE_PARM_DESC(keyclick_volume, "Keyclick volume (in %), default is 100%"); static int ctrlclick_volume = 100; /* % */ -module_param (ctrlclick_volume, int, 0); -MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%"); +module_param(ctrlclick_volume, int, 0); +MODULE_PARM_DESC(ctrlclick_volume, "Ctrlclick volume (in %), default is 100%"); static int lk201_compose_is_alt; -module_param (lk201_compose_is_alt, int, 0); -MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key " - "will act as an Alt key"); +module_param(lk201_compose_is_alt, int, 0); +MODULE_PARM_DESC(lk201_compose_is_alt, + "If set non-zero, LK201' Compose key will act as an Alt key"); #undef LKKBD_DEBUG #ifdef LKKBD_DEBUG -#define DBG(x...) printk (x) +#define DBG(x...) printk(x) #else #define DBG(x...) do {} while (0) #endif @@ -122,7 +122,7 @@ MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key " #define LK_MODE_DOWN 0x80 #define LK_MODE_AUTODOWN 0x82 #define LK_MODE_UPDOWN 0x86 -#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3)) +#define LK_CMD_SET_MODE(mode, div) ((mode) | ((div) << 3)) /* Misc commands */ #define LK_CMD_ENABLE_KEYCLICK 0x1b @@ -152,11 +152,8 @@ MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key " #define LK_NUM_KEYCODES 256 #define LK_NUM_IGNORE_BYTES 6 -typedef u_int16_t lk_keycode_t; - - -static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = { +static unsigned short lkkbd_keycode[LK_NUM_KEYCODES] = { [0x56] = KEY_F1, [0x57] = KEY_F2, [0x58] = KEY_F3, @@ -268,7 +265,7 @@ static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = { }; #define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do { \ - if (test_bit (LED, (LK)->dev->led)) \ + if (test_bit(LED, (LK)->dev->led)) \ VAR_ON |= BITS; \ else \ VAR_OFF |= BITS; \ @@ -278,7 +275,7 @@ static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = { * Per-keyboard data */ struct lkkbd { - lk_keycode_t keycode[LK_NUM_KEYCODES]; + unsigned short keycode[LK_NUM_KEYCODES]; int ignore_bytes; unsigned char id[LK_NUM_IGNORE_BYTES]; struct input_dev *dev; @@ -301,26 +298,25 @@ static struct { unsigned char *name; } lk_response[] = { #define RESPONSE(x) { .value = (x), .name = #x, } - RESPONSE (LK_STUCK_KEY), - RESPONSE (LK_SELFTEST_FAILED), - RESPONSE (LK_ALL_KEYS_UP), - RESPONSE (LK_METRONOME), - RESPONSE (LK_OUTPUT_ERROR), - RESPONSE (LK_INPUT_ERROR), - RESPONSE (LK_KBD_LOCKED), - RESPONSE (LK_KBD_TEST_MODE_ACK), - RESPONSE (LK_PREFIX_KEY_DOWN), - RESPONSE (LK_MODE_CHANGE_ACK), - RESPONSE (LK_RESPONSE_RESERVED), + RESPONSE(LK_STUCK_KEY), + RESPONSE(LK_SELFTEST_FAILED), + RESPONSE(LK_ALL_KEYS_UP), + RESPONSE(LK_METRONOME), + RESPONSE(LK_OUTPUT_ERROR), + RESPONSE(LK_INPUT_ERROR), + RESPONSE(LK_KBD_LOCKED), + RESPONSE(LK_KBD_TEST_MODE_ACK), + RESPONSE(LK_PREFIX_KEY_DOWN), + RESPONSE(LK_MODE_CHANGE_ACK), + RESPONSE(LK_RESPONSE_RESERVED), #undef RESPONSE }; -static unsigned char * -response_name (unsigned char value) +static unsigned char *response_name(unsigned char value) { int i; - for (i = 0; i < ARRAY_SIZE (lk_response); i++) + for (i = 0; i < ARRAY_SIZE(lk_response); i++) if (lk_response[i].value == value) return lk_response[i].name; @@ -331,8 +327,7 @@ response_name (unsigned char value) /* * Calculate volume parameter byte for a given volume. */ -static unsigned char -volume_to_hw (int volume_percent) +static unsigned char volume_to_hw(int volume_percent) { unsigned char ret = 0; @@ -363,8 +358,7 @@ volume_to_hw (int volume_percent) return ret; } -static void -lkkbd_detection_done (struct lkkbd *lk) +static void lkkbd_detection_done(struct lkkbd *lk) { int i; @@ -377,190 +371,202 @@ lkkbd_detection_done (struct lkkbd *lk) * Print keyboard name and modify Compose=Alt on user's request. */ switch (lk->id[4]) { - case 1: - strlcpy (lk->name, "DEC LK201 keyboard", - sizeof (lk->name)); - - if (lk201_compose_is_alt) - lk->keycode[0xb1] = KEY_LEFTALT; - break; - - case 2: - strlcpy (lk->name, "DEC LK401 keyboard", - sizeof (lk->name)); - break; - - default: - strlcpy (lk->name, "Unknown DEC keyboard", - sizeof (lk->name)); - printk (KERN_ERR "lkkbd: keyboard on %s is unknown, " - "please report to Jan-Benedict Glaw " - "<jbglaw@lug-owl.de>\n", lk->phys); - printk (KERN_ERR "lkkbd: keyboard ID'ed as:"); - for (i = 0; i < LK_NUM_IGNORE_BYTES; i++) - printk (" 0x%02x", lk->id[i]); - printk ("\n"); - break; + case 1: + strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name)); + + if (lk201_compose_is_alt) + lk->keycode[0xb1] = KEY_LEFTALT; + break; + + case 2: + strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name)); + break; + + default: + strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name)); + printk(KERN_ERR + "lkkbd: keyboard on %s is unknown, please report to " + "Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys); + printk(KERN_ERR "lkkbd: keyboard ID'ed as:"); + for (i = 0; i < LK_NUM_IGNORE_BYTES; i++) + printk(" 0x%02x", lk->id[i]); + printk("\n"); + break; } - printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n", - lk->phys, lk->name); + + printk(KERN_INFO "lkkbd: keyboard on %s identified as: %s\n", + lk->phys, lk->name); /* * Report errors during keyboard boot-up. */ switch (lk->id[2]) { - case 0x00: - /* All okay */ - break; - - case LK_STUCK_KEY: - printk (KERN_ERR "lkkbd: Stuck key on keyboard at " - "%s\n", lk->phys); - break; - - case LK_SELFTEST_FAILED: - printk (KERN_ERR "lkkbd: Selftest failed on keyboard " - "at %s, keyboard may not work " - "properly\n", lk->phys); - break; - - default: - printk (KERN_ERR "lkkbd: Unknown error %02x on " - "keyboard at %s\n", lk->id[2], - lk->phys); - break; + case 0x00: + /* All okay */ + break; + + case LK_STUCK_KEY: + printk(KERN_ERR "lkkbd: Stuck key on keyboard at %s\n", + lk->phys); + break; + + case LK_SELFTEST_FAILED: + printk(KERN_ERR + "lkkbd: Selftest failed on keyboard at %s, " + "keyboard may not work properly\n", lk->phys); + break; + + default: + printk(KERN_ERR + "lkkbd: Unknown error %02x on keyboard at %s\n", + lk->id[2], lk->phys); + break; } /* * Try to hint user if there's a stuck key. */ if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0) - printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode " - "is 0x%04x\n", lk->id[3], - lk->keycode[lk->id[3]]); - - return; + printk(KERN_ERR + "Scancode of stuck key is 0x%02x, keycode is 0x%04x\n", + lk->id[3], lk->keycode[lk->id[3]]); } /* * lkkbd_interrupt() is called by the low level driver when a character * is received. */ -static irqreturn_t -lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags) +static irqreturn_t lkkbd_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) { - struct lkkbd *lk = serio_get_drvdata (serio); + struct lkkbd *lk = serio_get_drvdata(serio); + struct input_dev *input_dev = lk->dev; + unsigned int keycode; int i; - DBG (KERN_INFO "Got byte 0x%02x\n", data); + DBG(KERN_INFO "Got byte 0x%02x\n", data); if (lk->ignore_bytes > 0) { - DBG (KERN_INFO "Ignoring a byte on %s\n", lk->name); + DBG(KERN_INFO "Ignoring a byte on %s\n", lk->name); lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; if (lk->ignore_bytes == 0) - lkkbd_detection_done (lk); + lkkbd_detection_done(lk); return IRQ_HANDLED; } switch (data) { - case LK_ALL_KEYS_UP: - for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++) - if (lk->keycode[i] != KEY_RESERVED) - input_report_key (lk->dev, lk->keycode[i], 0); - input_sync (lk->dev); - break; - - case 0x01: - DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n"); - lk->ignore_bytes = LK_NUM_IGNORE_BYTES; - lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; - schedule_work (&lk->tq); - break; - - case LK_METRONOME: - case LK_OUTPUT_ERROR: - case LK_INPUT_ERROR: - case LK_KBD_LOCKED: - case LK_KBD_TEST_MODE_ACK: - case LK_PREFIX_KEY_DOWN: - case LK_MODE_CHANGE_ACK: - case LK_RESPONSE_RESERVED: - DBG (KERN_INFO "Got %s and don't know how to handle...\n", - response_name (data)); - break; - - default: - if (lk->keycode[data] != KEY_RESERVED) { - if (!test_bit (lk->keycode[data], lk->dev->key)) - input_report_key (lk->dev, lk->keycode[data], 1); - else - input_report_key (lk->dev, lk->keycode[data], 0); - input_sync (lk->dev); - } else - printk (KERN_WARNING "%s: Unknown key with " - "scancode 0x%02x on %s.\n", - __FILE__, data, lk->name); + case LK_ALL_KEYS_UP: + for (i = 0; i < ARRAY_SIZE(lkkbd_keycode); i++) + input_report_key(input_dev, lk->keycode[i], 0); + input_sync(input_dev); + break; + + case 0x01: + DBG(KERN_INFO "Got 0x01, scheduling re-initialization\n"); + lk->ignore_bytes = LK_NUM_IGNORE_BYTES; + lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; + schedule_work(&lk->tq); + break; + + case LK_METRONOME: + case LK_OUTPUT_ERROR: + case LK_INPUT_ERROR: + case LK_KBD_LOCKED: + case LK_KBD_TEST_MODE_ACK: + case LK_PREFIX_KEY_DOWN: + case LK_MODE_CHANGE_ACK: + case LK_RESPONSE_RESERVED: + DBG(KERN_INFO "Got %s and don't know how to handle...\n", + response_name(data)); + break; + + default: + keycode = lk->keycode[data]; + if (keycode != KEY_RESERVED) { + input_report_key(input_dev, keycode, + !test_bit(keycode, input_dev->key)); + input_sync(input_dev); + } else { + printk(KERN_WARNING + "%s: Unknown key with scancode 0x%02x on %s.\n", + __FILE__, data, lk->name); + } } return IRQ_HANDLED; } +static void lkkbd_toggle_leds(struct lkkbd *lk) +{ + struct serio *serio = lk->serio; + unsigned char leds_on = 0; + unsigned char leds_off = 0; + + CHECK_LED(lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK); + CHECK_LED(lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE); + CHECK_LED(lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK); + CHECK_LED(lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT); + if (leds_on != 0) { + serio_write(serio, LK_CMD_LED_ON); + serio_write(serio, leds_on); + } + if (leds_off != 0) { + serio_write(serio, LK_CMD_LED_OFF); + serio_write(serio, leds_off); + } +} + +static void lkkbd_toggle_keyclick(struct lkkbd *lk, bool on) +{ + struct serio *serio = lk->serio; + + if (on) { + DBG("%s: Activating key clicks\n", __func__); + serio_write(serio, LK_CMD_ENABLE_KEYCLICK); + serio_write(serio, volume_to_hw(lk->keyclick_volume)); + serio_write(serio, LK_CMD_ENABLE_CTRCLICK); + serio_write(serio, volume_to_hw(lk->ctrlclick_volume)); + } else { + DBG("%s: Deactivating key clicks\n", __func__); + serio_write(serio, LK_CMD_DISABLE_KEYCLICK); + serio_write(serio, LK_CMD_DISABLE_CTRCLICK); + } + +} + /* * lkkbd_event() handles events from the input module. */ -static int -lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code, - int value) +static int lkkbd_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) { - struct lkkbd *lk = input_get_drvdata (dev); - unsigned char leds_on = 0; - unsigned char leds_off = 0; + struct lkkbd *lk = input_get_drvdata(dev); switch (type) { - case EV_LED: - CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK); - CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE); - CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK); - CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT); - if (leds_on != 0) { - serio_write (lk->serio, LK_CMD_LED_ON); - serio_write (lk->serio, leds_on); - } - if (leds_off != 0) { - serio_write (lk->serio, LK_CMD_LED_OFF); - serio_write (lk->serio, leds_off); - } + case EV_LED: + lkkbd_toggle_leds(lk); + return 0; + + case EV_SND: + switch (code) { + case SND_CLICK: + lkkbd_toggle_keyclick(lk, value); return 0; - case EV_SND: - switch (code) { - case SND_CLICK: - if (value == 0) { - DBG ("%s: Deactivating key clicks\n", __func__); - serio_write (lk->serio, LK_CMD_DISABLE_KEYCLICK); - serio_write (lk->serio, LK_CMD_DISABLE_CTRCLICK); - } else { - DBG ("%s: Activating key clicks\n", __func__); - serio_write (lk->serio, LK_CMD_ENABLE_KEYCLICK); - serio_write (lk->serio, volume_to_hw (lk->keyclick_volume)); - serio_write (lk->serio, LK_CMD_ENABLE_CTRCLICK); - serio_write (lk->serio, volume_to_hw (lk->ctrlclick_volume)); - } - return 0; - - case SND_BELL: - if (value != 0) - serio_write (lk->serio, LK_CMD_SOUND_BELL); - - return 0; - } - break; - - default: - printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n", - __func__, type, code, value); + case SND_BELL: + if (value != 0) + serio_write(lk->serio, LK_CMD_SOUND_BELL); + + return 0; + } + + break; + + default: + printk(KERN_ERR "%s(): Got unknown type %d, code %d, value %d\n", + __func__, type, code, value); } return -1; @@ -570,79 +576,56 @@ lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code, * lkkbd_reinit() sets leds and beeps to a state the computer remembers they * were in. */ -static void -lkkbd_reinit (struct work_struct *work) +static void lkkbd_reinit(struct work_struct *work) { struct lkkbd *lk = container_of(work, struct lkkbd, tq); int division; - unsigned char leds_on = 0; - unsigned char leds_off = 0; /* Ask for ID */ - serio_write (lk->serio, LK_CMD_REQUEST_ID); + serio_write(lk->serio, LK_CMD_REQUEST_ID); /* Reset parameters */ - serio_write (lk->serio, LK_CMD_SET_DEFAULTS); + serio_write(lk->serio, LK_CMD_SET_DEFAULTS); /* Set LEDs */ - CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK); - CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE); - CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK); - CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT); - if (leds_on != 0) { - serio_write (lk->serio, LK_CMD_LED_ON); - serio_write (lk->serio, leds_on); - } - if (leds_off != 0) { - serio_write (lk->serio, LK_CMD_LED_OFF); - serio_write (lk->serio, leds_off); - } + lkkbd_toggle_leds(lk); /* * Try to activate extended LK401 mode. This command will * only work with a LK401 keyboard and grants access to * LAlt, RAlt, RCompose and RShift. */ - serio_write (lk->serio, LK_CMD_ENABLE_LK401); + serio_write(lk->serio, LK_CMD_ENABLE_LK401); /* Set all keys to UPDOWN mode */ for (division = 1; division <= 14; division++) - serio_write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN, - division)); + serio_write(lk->serio, + LK_CMD_SET_MODE(LK_MODE_UPDOWN, division)); /* Enable bell and set volume */ - serio_write (lk->serio, LK_CMD_ENABLE_BELL); - serio_write (lk->serio, volume_to_hw (lk->bell_volume)); + serio_write(lk->serio, LK_CMD_ENABLE_BELL); + serio_write(lk->serio, volume_to_hw(lk->bell_volume)); /* Enable/disable keyclick (and possibly set volume) */ - if (test_bit (SND_CLICK, lk->dev->snd)) { - serio_write (lk->serio, LK_CMD_ENABLE_KEYCLICK); - serio_write (lk->serio, volume_to_hw (lk->keyclick_volume)); - serio_write (lk->serio, LK_CMD_ENABLE_CTRCLICK); - serio_write (lk->serio, volume_to_hw (lk->ctrlclick_volume)); - } else { - serio_write (lk->serio, LK_CMD_DISABLE_KEYCLICK); - serio_write (lk->serio, LK_CMD_DISABLE_CTRCLICK); - } + lkkbd_toggle_keyclick(lk, test_bit(SND_CLICK, lk->dev->snd)); /* Sound the bell if needed */ - if (test_bit (SND_BELL, lk->dev->snd)) - serio_write (lk->serio, LK_CMD_SOUND_BELL); + if (test_bit(SND_BELL, lk->dev->snd)) + serio_write(lk->serio, LK_CMD_SOUND_BELL); } /* * lkkbd_connect() probes for a LK keyboard and fills the necessary structures. */ -static int -lkkbd_connect (struct serio *serio, struct serio_driver *drv) +static int lkkbd_connect(struct serio *serio, struct serio_driver *drv) { struct lkkbd *lk; struct input_dev *input_dev; int i; int err; - lk = kzalloc (sizeof (struct lkkbd), GFP_KERNEL); - input_dev = input_allocate_device (); + lk = kzalloc(sizeof(struct lkkbd), GFP_KERNEL); + input_dev = input_allocate_device(); if (!lk || !input_dev) { err = -ENOMEM; goto fail1; @@ -650,14 +633,14 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv) lk->serio = serio; lk->dev = input_dev; - INIT_WORK (&lk->tq, lkkbd_reinit); + INIT_WORK(&lk->tq, lkkbd_reinit); lk->bell_volume = bell_volume; lk->keyclick_volume = keyclick_volume; lk->ctrlclick_volume = ctrlclick_volume; - memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES); + memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode)); - strlcpy (lk->name, "DEC LK keyboard", sizeof(lk->name)); - snprintf (lk->phys, sizeof(lk->phys), "%s/input0", serio->phys); + strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name)); + snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys); input_dev->name = lk->name; input_dev->phys = lk->phys; @@ -668,62 +651,61 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv) input_dev->dev.parent = &serio->dev; input_dev->event = lkkbd_event; - input_set_drvdata (input_dev, lk); + input_set_drvdata(input_dev, lk); - set_bit (EV_KEY, input_dev->evbit); - set_bit (EV_LED, input_dev->evbit); - set_bit (EV_SND, input_dev->evbit); - set_bit (EV_REP, input_dev->evbit); - set_bit (LED_CAPSL, input_dev->ledbit); - set_bit (LED_SLEEP, input_dev->ledbit); - set_bit (LED_COMPOSE, input_dev->ledbit); - set_bit (LED_SCROLLL, input_dev->ledbit); - set_bit (SND_BELL, input_dev->sndbit); - set_bit (SND_CLICK, input_dev->sndbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_LED, input_dev->evbit); + __set_bit(EV_SND, input_dev->evbit); + __set_bit(EV_REP, input_dev->evbit); + __set_bit(LED_CAPSL, input_dev->ledbit); + __set_bit(LED_SLEEP, input_dev->ledbit); + __set_bit(LED_COMPOSE, input_dev->ledbit); + __set_bit(LED_SCROLLL, input_dev->ledbit); + __set_bit(SND_BELL, input_dev->sndbit); + __set_bit(SND_CLICK, input_dev->sndbit); input_dev->keycode = lk->keycode; - input_dev->keycodesize = sizeof (lk_keycode_t); - input_dev->keycodemax = LK_NUM_KEYCODES; + input_dev->keycodesize = sizeof(lk->keycode[0]); + input_dev->keycodemax = ARRAY_SIZE(lk->keycode); for (i = 0; i < LK_NUM_KEYCODES; i++) - __set_bit (lk->keycode[i], input_dev->keybit); + __set_bit(lk->keycode[i], input_dev->keybit); __clear_bit(KEY_RESERVED, input_dev->keybit); - serio_set_drvdata (serio, lk); + serio_set_drvdata(serio, lk); - err = serio_open (serio, drv); + err = serio_open(serio, drv); if (err) goto fail2; - err = input_register_device (lk->dev); + err = input_register_device(lk->dev); if (err) goto fail3; - serio_write (lk->serio, LK_CMD_POWERCYCLE_RESET); + serio_write(lk->serio, LK_CMD_POWERCYCLE_RESET); return 0; - fail3: serio_close (serio); - fail2: serio_set_drvdata (serio, NULL); - fail1: input_free_device (input_dev); - kfree (lk); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(lk); return err; } /* * lkkbd_disconnect() unregisters and closes behind us. */ -static void -lkkbd_disconnect (struct serio *serio) +static void lkkbd_disconnect(struct serio *serio) { - struct lkkbd *lk = serio_get_drvdata (serio); - - input_get_device (lk->dev); - input_unregister_device (lk->dev); - serio_close (serio); - serio_set_drvdata (serio, NULL); - input_put_device (lk->dev); - kfree (lk); + struct lkkbd *lk = serio_get_drvdata(serio); + + input_get_device(lk->dev); + input_unregister_device(lk->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(lk->dev); + kfree(lk); } static struct serio_device_id lkkbd_serio_ids[] = { @@ -752,18 +734,16 @@ static struct serio_driver lkkbd_drv = { /* * The functions for insering/removing us as a module. */ -static int __init -lkkbd_init (void) +static int __init lkkbd_init(void) { return serio_register_driver(&lkkbd_drv); } -static void __exit -lkkbd_exit (void) +static void __exit lkkbd_exit(void) { serio_unregister_driver(&lkkbd_drv); } -module_init (lkkbd_init); -module_exit (lkkbd_exit); +module_init(lkkbd_init); +module_exit(lkkbd_exit); diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 91cfe5170265..34f4a29d4973 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -213,8 +213,9 @@ static void matrix_keypad_stop(struct input_dev *dev) } #ifdef CONFIG_PM -static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state) +static int matrix_keypad_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct matrix_keypad *keypad = platform_get_drvdata(pdev); const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; @@ -228,8 +229,9 @@ static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t stat return 0; } -static int matrix_keypad_resume(struct platform_device *pdev) +static int matrix_keypad_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct matrix_keypad *keypad = platform_get_drvdata(pdev); const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; @@ -242,9 +244,9 @@ static int matrix_keypad_resume(struct platform_device *pdev) return 0; } -#else -#define matrix_keypad_suspend NULL -#define matrix_keypad_resume NULL + +static const SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops, + matrix_keypad_suspend, matrix_keypad_resume); #endif static int __devinit init_matrix_gpio(struct platform_device *pdev, @@ -417,11 +419,12 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev) static struct platform_driver matrix_keypad_driver = { .probe = matrix_keypad_probe, .remove = __devexit_p(matrix_keypad_remove), - .suspend = matrix_keypad_suspend, - .resume = matrix_keypad_resume, .driver = { .name = "matrix-keypad", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &matrix_keypad_pm_ops, +#endif }, }; diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index bba85add35a3..1a494d505431 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -35,12 +35,12 @@ #include <linux/mutex.h> #include <linux/errno.h> #include <mach/gpio.h> -#include <mach/keypad.h> -#include <mach/menelaus.h> +#include <plat/keypad.h> +#include <plat/menelaus.h> #include <asm/irq.h> #include <mach/hardware.h> #include <asm/io.h> -#include <mach/mux.h> +#include <plat/mux.h> #undef NEW_BOARD_LEARNING_MODE diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 887af79b7bff..076111fc72d2 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -18,9 +18,9 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> +#include <linux/input/sh_keysc.h> #include <linux/clk.h> #include <linux/io.h> -#include <asm/sh_keysc.h> #define KYCR1_OFFS 0x00 #define KYCR2_OFFS 0x04 diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index a9bb2544b2de..16ec5233441c 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -80,6 +80,7 @@ config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86 && !X86_64 select INPUT_POLLDEV + select INPUT_SPARSEKMAP select NEW_LEDS select LEDS_CLASS select CHECK_SIGNATURE @@ -281,6 +282,7 @@ config INPUT_RB532_BUTTON config INPUT_DM355EVM tristate "TI DaVinci DM355 EVM Keypad and IR Remote" depends on MFD_DM355EVM_MSP + select INPUT_SPARSEKMAP help Supports the pushbuttons and IR remote used with the DM355 EVM board. diff --git a/drivers/input/misc/ati_remote.c b/drivers/input/misc/ati_remote.c index e290fde35e74..614b65d78fe9 100644 --- a/drivers/input/misc/ati_remote.c +++ b/drivers/input/misc/ati_remote.c @@ -766,7 +766,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de ati_remote->interface = interface; usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys)); - strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys)); + strlcat(ati_remote->phys, "/input0", sizeof(ati_remote->phys)); if (udev->manufacturer) strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name)); diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c index f2b67dc81d80..766c06911f41 100644 --- a/drivers/input/misc/dm355evm_keys.c +++ b/drivers/input/misc/dm355evm_keys.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/platform_device.h> #include <linux/interrupt.h> @@ -33,12 +34,8 @@ struct dm355evm_keys { int irq; }; -/* These initial keycodes can be remapped by dm355evm_setkeycode(). */ -static struct { - u16 event; - u16 keycode; -} dm355evm_keys[] = { - +/* These initial keycodes can be remapped */ +static const struct key_entry dm355evm_keys[] = { /* * Pushbuttons on the EVM board ... note that the labels for these * are SW10/SW11/etc on the PC board. The left/right orientation @@ -47,11 +44,11 @@ static struct { * is to the right. (That is, rotate the board counter-clockwise * by 90 degrees from the SW10/etc and "DM355 EVM" labels.) */ - { 0x00d8, KEY_OK, }, /* SW12 */ - { 0x00b8, KEY_UP, }, /* SW13 */ - { 0x00e8, KEY_DOWN, }, /* SW11 */ - { 0x0078, KEY_LEFT, }, /* SW14 */ - { 0x00f0, KEY_RIGHT, }, /* SW10 */ + { KE_KEY, 0x00d8, { KEY_OK } }, /* SW12 */ + { KE_KEY, 0x00b8, { KEY_UP } }, /* SW13 */ + { KE_KEY, 0x00e8, { KEY_DOWN } }, /* SW11 */ + { KE_KEY, 0x0078, { KEY_LEFT } }, /* SW14 */ + { KE_KEY, 0x00f0, { KEY_RIGHT } }, /* SW10 */ /* * IR buttons ... codes assigned to match the universal remote @@ -65,35 +62,35 @@ static struct { * RC5 codes are 14 bits, with two start bits (0x3 prefix) * and a toggle bit (masked out below). */ - { 0x300c, KEY_POWER, }, /* NOTE: docs omit this */ - { 0x3000, KEY_NUMERIC_0, }, - { 0x3001, KEY_NUMERIC_1, }, - { 0x3002, KEY_NUMERIC_2, }, - { 0x3003, KEY_NUMERIC_3, }, - { 0x3004, KEY_NUMERIC_4, }, - { 0x3005, KEY_NUMERIC_5, }, - { 0x3006, KEY_NUMERIC_6, }, - { 0x3007, KEY_NUMERIC_7, }, - { 0x3008, KEY_NUMERIC_8, }, - { 0x3009, KEY_NUMERIC_9, }, - { 0x3022, KEY_ENTER, }, - { 0x30ec, KEY_MODE, }, /* "tv/vcr/..." */ - { 0x300f, KEY_SELECT, }, /* "info" */ - { 0x3020, KEY_CHANNELUP, }, /* "up" */ - { 0x302e, KEY_MENU, }, /* "in/out" */ - { 0x3011, KEY_VOLUMEDOWN, }, /* "left" */ - { 0x300d, KEY_MUTE, }, /* "ok" */ - { 0x3010, KEY_VOLUMEUP, }, /* "right" */ - { 0x301e, KEY_SUBTITLE, }, /* "cc" */ - { 0x3021, KEY_CHANNELDOWN, }, /* "down" */ - { 0x3022, KEY_PREVIOUS, }, - { 0x3026, KEY_SLEEP, }, - { 0x3172, KEY_REWIND, }, /* NOTE: docs wrongly say 0x30ca */ - { 0x3175, KEY_PLAY, }, - { 0x3174, KEY_FASTFORWARD, }, - { 0x3177, KEY_RECORD, }, - { 0x3176, KEY_STOP, }, - { 0x3169, KEY_PAUSE, }, + { KE_KEY, 0x300c, { KEY_POWER } }, /* NOTE: docs omit this */ + { KE_KEY, 0x3000, { KEY_NUMERIC_0 } }, + { KE_KEY, 0x3001, { KEY_NUMERIC_1 } }, + { KE_KEY, 0x3002, { KEY_NUMERIC_2 } }, + { KE_KEY, 0x3003, { KEY_NUMERIC_3 } }, + { KE_KEY, 0x3004, { KEY_NUMERIC_4 } }, + { KE_KEY, 0x3005, { KEY_NUMERIC_5 } }, + { KE_KEY, 0x3006, { KEY_NUMERIC_6 } }, + { KE_KEY, 0x3007, { KEY_NUMERIC_7 } }, + { KE_KEY, 0x3008, { KEY_NUMERIC_8 } }, + { KE_KEY, 0x3009, { KEY_NUMERIC_9 } }, + { KE_KEY, 0x3022, { KEY_ENTER } }, + { KE_KEY, 0x30ec, { KEY_MODE } }, /* "tv/vcr/..." */ + { KE_KEY, 0x300f, { KEY_SELECT } }, /* "info" */ + { KE_KEY, 0x3020, { KEY_CHANNELUP } }, /* "up" */ + { KE_KEY, 0x302e, { KEY_MENU } }, /* "in/out" */ + { KE_KEY, 0x3011, { KEY_VOLUMEDOWN } }, /* "left" */ + { KE_KEY, 0x300d, { KEY_MUTE } }, /* "ok" */ + { KE_KEY, 0x3010, { KEY_VOLUMEUP } }, /* "right" */ + { KE_KEY, 0x301e, { KEY_SUBTITLE } }, /* "cc" */ + { KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */ + { KE_KEY, 0x3022, { KEY_PREVIOUS } }, + { KE_KEY, 0x3026, { KEY_SLEEP } }, + { KE_KEY, 0x3172, { KEY_REWIND } }, /* NOTE: docs wrongly say 0x30ca */ + { KE_KEY, 0x3175, { KEY_PLAY } }, + { KE_KEY, 0x3174, { KEY_FASTFORWARD } }, + { KE_KEY, 0x3177, { KEY_RECORD } }, + { KE_KEY, 0x3176, { KEY_STOP } }, + { KE_KEY, 0x3169, { KEY_PAUSE } }, }; /* @@ -105,19 +102,18 @@ static struct { */ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) { - struct dm355evm_keys *keys = _keys; - int status; + static u16 last_event; + struct dm355evm_keys *keys = _keys; + const struct key_entry *ke; + unsigned int keycode; + int status; + u16 event; /* For simplicity we ignore INPUT_COUNT and just read * events until we get the "queue empty" indicator. * Reading INPUT_LOW decrements the count. */ for (;;) { - static u16 last_event; - u16 event; - int keycode; - int i; - status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH); if (status < 0) { dev_dbg(keys->dev, "input high err %d\n", @@ -156,14 +152,9 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) /* ignore the RC5 toggle bit */ event &= ~0x0800; - /* find the key, or leave it as unknown */ - keycode = KEY_UNKNOWN; - for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) { - if (dm355evm_keys[i].event != event) - continue; - keycode = dm355evm_keys[i].keycode; - break; - } + /* find the key, or report it as unknown */ + ke = sparse_keymap_entry_from_scancode(keys->input, event); + keycode = ke ? ke->keycode : KEY_UNKNOWN; dev_dbg(keys->dev, "input event 0x%04x--> keycode %d\n", event, keycode); @@ -174,36 +165,8 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) input_report_key(keys->input, keycode, 0); input_sync(keys->input); } - return IRQ_HANDLED; -} -static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode) -{ - u16 old_keycode; - unsigned i; - - if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys)) - return -EINVAL; - - old_keycode = dm355evm_keys[index].keycode; - dm355evm_keys[index].keycode = keycode; - set_bit(keycode, dev->keybit); - - for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) { - if (dm355evm_keys[index].keycode == old_keycode) - goto done; - } - clear_bit(old_keycode, dev->keybit); -done: - return 0; -} - -static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode) -{ - if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys)) - return -EINVAL; - - return dm355evm_keys[index].keycode; + return IRQ_HANDLED; } /*----------------------------------------------------------------------*/ @@ -213,7 +176,6 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev) struct dm355evm_keys *keys; struct input_dev *input; int status; - int i; /* allocate instance struct and input dev */ keys = kzalloc(sizeof *keys, GFP_KERNEL); @@ -242,31 +204,30 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev) input->id.product = 0x0355; input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); - input->evbit[0] = BIT(EV_KEY); - for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) - __set_bit(dm355evm_keys[i].keycode, input->keybit); - - input->setkeycode = dm355evm_setkeycode; - input->getkeycode = dm355evm_getkeycode; + status = sparse_keymap_setup(input, dm355evm_keys, NULL); + if (status) + goto fail1; /* REVISIT: flush the event queue? */ status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq, IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys); if (status < 0) - goto fail1; + goto fail2; /* register */ status = input_register_device(input); if (status < 0) - goto fail2; + goto fail3; platform_set_drvdata(pdev, keys); return 0; -fail2: +fail3: free_irq(keys->irq, keys); +fail2: + sparse_keymap_free(input); fail1: input_free_device(input); kfree(keys); @@ -280,6 +241,7 @@ static int __devexit dm355evm_keys_remove(struct platform_device *pdev) struct dm355evm_keys *keys = platform_get_drvdata(pdev); free_irq(keys->irq, keys); + sparse_keymap_free(keys->input); input_unregister_device(keys->input); kfree(keys); diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c index ea821b546969..ad730e15afc0 100644 --- a/drivers/input/misc/hp_sdc_rtc.c +++ b/drivers/input/misc/hp_sdc_rtc.c @@ -35,7 +35,6 @@ #include <linux/hp_sdc.h> #include <linux/errno.h> -#include <linux/smp_lock.h> #include <linux/types.h> #include <linux/init.h> #include <linux/module.h> @@ -409,7 +408,6 @@ static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait) static int hp_sdc_rtc_open(struct inode *inode, struct file *file) { - cycle_kernel_lock(); return 0; } diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index a53c4885fbad..668913d12044 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -338,7 +338,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i pm->input = input_dev; usb_make_path(udev, pm->phys, sizeof(pm->phys)); - strlcpy(pm->phys, "/input0", sizeof(pm->phys)); + strlcat(pm->phys, "/input0", sizeof(pm->phys)); spin_lock_init(&pm->lock); diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index a932179c4c9e..38da6ab04384 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -21,6 +21,7 @@ #include <linux/dmi.h> #include <linux/init.h> #include <linux/input-polldev.h> +#include <linux/input/sparse-keymap.h> #include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/kernel.h> @@ -224,19 +225,8 @@ static void bios_set_state(u8 subsys, int enable) /* Hardware database */ -struct key_entry { - char type; /* See KE_* below */ - u8 code; - union { - u16 keycode; /* For KE_KEY */ - struct { /* For KE_SW */ - u8 code; - u8 value; - } sw; - }; -}; - -enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH }; +#define KE_WIFI (KE_LAST + 1) +#define KE_BLUETOOTH (KE_LAST + 2) #define FE_MAIL_LED 0x01 #define FE_WIFI_LED 0x02 @@ -644,10 +634,10 @@ static struct key_entry keymap_prestigio[] __initdata = { * a list of buttons and their key codes (reported when loading this module * with force=1) and the output of dmidecode to $MODULE_AUTHOR. */ -static struct dmi_system_id dmi_ids[] __initdata = { +static const struct dmi_system_id __initconst dmi_ids[] = { { + /* Fujitsu-Siemens Amilo Pro V2000 */ .callback = dmi_matched, - .ident = "Fujitsu-Siemens Amilo Pro V2000", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"), @@ -655,8 +645,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v2000 }, { + /* Fujitsu-Siemens Amilo Pro Edition V3505 */ .callback = dmi_matched, - .ident = "Fujitsu-Siemens Amilo Pro Edition V3505", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"), @@ -664,8 +654,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v3505 }, { + /* Fujitsu-Siemens Amilo M7400 */ .callback = dmi_matched, - .ident = "Fujitsu-Siemens Amilo M7400", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "), @@ -673,8 +663,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v2000 }, { + /* Maxdata Pro 7000 DX */ .callback = dmi_matched, - .ident = "Maxdata Pro 7000 DX", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"), DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"), @@ -682,8 +672,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v2000 }, { + /* Fujitsu N3510 */ .callback = dmi_matched, - .ident = "Fujitsu N3510", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "N3510"), @@ -691,8 +681,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fujitsu_n3510 }, { + /* Acer Aspire 1500 */ .callback = dmi_matched, - .ident = "Acer Aspire 1500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"), @@ -700,8 +690,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_1500 }, { + /* Acer Aspire 1600 */ .callback = dmi_matched, - .ident = "Acer Aspire 1600", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"), @@ -709,8 +699,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_1600 }, { + /* Acer Aspire 3020 */ .callback = dmi_matched, - .ident = "Acer Aspire 3020", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"), @@ -718,8 +708,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_5020 }, { + /* Acer Aspire 5020 */ .callback = dmi_matched, - .ident = "Acer Aspire 5020", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"), @@ -727,8 +717,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_5020 }, { + /* Acer TravelMate 2100 */ .callback = dmi_matched, - .ident = "Acer TravelMate 2100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"), @@ -736,8 +726,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_5020 }, { + /* Acer TravelMate 2410 */ .callback = dmi_matched, - .ident = "Acer TravelMate 2410", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"), @@ -745,8 +735,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_2410 }, { + /* Acer TravelMate C300 */ .callback = dmi_matched, - .ident = "Acer TravelMate C300", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"), @@ -754,8 +744,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_300 }, { + /* Acer TravelMate C100 */ .callback = dmi_matched, - .ident = "Acer TravelMate C100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"), @@ -763,8 +753,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_300 }, { + /* Acer TravelMate C110 */ .callback = dmi_matched, - .ident = "Acer TravelMate C110", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"), @@ -772,8 +762,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_110 }, { + /* Acer TravelMate 380 */ .callback = dmi_matched, - .ident = "Acer TravelMate 380", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"), @@ -781,8 +771,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_380 }, { + /* Acer TravelMate 370 */ .callback = dmi_matched, - .ident = "Acer TravelMate 370", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"), @@ -790,8 +780,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */ }, { + /* Acer TravelMate 220 */ .callback = dmi_matched, - .ident = "Acer TravelMate 220", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"), @@ -799,8 +789,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_220 }, { + /* Acer TravelMate 260 */ .callback = dmi_matched, - .ident = "Acer TravelMate 260", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"), @@ -808,8 +798,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_220 }, { + /* Acer TravelMate 230 */ .callback = dmi_matched, - .ident = "Acer TravelMate 230", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"), @@ -818,8 +808,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_230 }, { + /* Acer TravelMate 280 */ .callback = dmi_matched, - .ident = "Acer TravelMate 280", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"), @@ -827,8 +817,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_230 }, { + /* Acer TravelMate 240 */ .callback = dmi_matched, - .ident = "Acer TravelMate 240", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"), @@ -836,8 +826,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_240 }, { + /* Acer TravelMate 250 */ .callback = dmi_matched, - .ident = "Acer TravelMate 250", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"), @@ -845,8 +835,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_240 }, { + /* Acer TravelMate 2424NWXCi */ .callback = dmi_matched, - .ident = "Acer TravelMate 2424NWXCi", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"), @@ -854,8 +844,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_240 }, { + /* Acer TravelMate 350 */ .callback = dmi_matched, - .ident = "Acer TravelMate 350", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"), @@ -863,8 +853,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_350 }, { + /* Acer TravelMate 360 */ .callback = dmi_matched, - .ident = "Acer TravelMate 360", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), @@ -872,8 +862,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_360 }, { + /* Acer TravelMate 610 */ .callback = dmi_matched, - .ident = "Acer TravelMate 610", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ACER"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"), @@ -881,8 +871,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_610 }, { + /* Acer TravelMate 620 */ .callback = dmi_matched, - .ident = "Acer TravelMate 620", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"), @@ -890,8 +880,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_630 }, { + /* Acer TravelMate 630 */ .callback = dmi_matched, - .ident = "Acer TravelMate 630", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"), @@ -899,8 +889,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_630 }, { + /* AOpen 1559AS */ .callback = dmi_matched, - .ident = "AOpen 1559AS", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "E2U"), DMI_MATCH(DMI_BOARD_NAME, "E2U"), @@ -908,8 +898,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_aopen_1559as }, { + /* Medion MD 9783 */ .callback = dmi_matched, - .ident = "Medion MD 9783", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"), @@ -917,8 +907,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_ms2111 }, { + /* Medion MD 40100 */ .callback = dmi_matched, - .ident = "Medion MD 40100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"), @@ -926,8 +916,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_md40100 }, { + /* Medion MD 2900 */ .callback = dmi_matched, - .ident = "Medion MD 2900", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"), @@ -935,8 +925,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_md2900 }, { + /* Medion MD 42200 */ .callback = dmi_matched, - .ident = "Medion MD 42200", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Medion"), DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"), @@ -944,8 +934,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v2000 }, { + /* Medion MD 96500 */ .callback = dmi_matched, - .ident = "Medion MD 96500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"), @@ -953,8 +943,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_md96500 }, { + /* Medion MD 95400 */ .callback = dmi_matched, - .ident = "Medion MD 95400", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"), @@ -962,8 +952,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_md96500 }, { + /* Fujitsu Siemens Amilo D7820 */ .callback = dmi_matched, - .ident = "Fujitsu Siemens Amilo D7820", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */ DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"), @@ -971,8 +961,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_d88x0 }, { + /* Fujitsu Siemens Amilo D88x0 */ .callback = dmi_matched, - .ident = "Fujitsu Siemens Amilo D88x0", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"), @@ -1037,21 +1027,6 @@ static unsigned long jiffies_last_press; static bool wifi_enabled; static bool bluetooth_enabled; -static void report_key(struct input_dev *dev, unsigned int keycode) -{ - input_report_key(dev, keycode, 1); - input_sync(dev); - input_report_key(dev, keycode, 0); - input_sync(dev); -} - -static void report_switch(struct input_dev *dev, unsigned int code, int value) -{ - input_report_switch(dev, code, value); - input_sync(dev); -} - - /* led management */ static void wistron_mail_led_set(struct led_classdev *led_cdev, enum led_brightness value) @@ -1128,43 +1103,13 @@ static inline void wistron_led_resume(void) led_classdev_resume(&wistron_wifi_led); } -static struct key_entry *wistron_get_entry_by_scancode(int code) -{ - struct key_entry *key; - - for (key = keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *wistron_get_entry_by_keycode(int keycode) -{ - struct key_entry *key; - - for (key = keymap; key->type != KE_END; key++) - if (key->type == KE_KEY && keycode == key->keycode) - return key; - - return NULL; -} - static void handle_key(u8 code) { - const struct key_entry *key = wistron_get_entry_by_scancode(code); + const struct key_entry *key = + sparse_keymap_entry_from_scancode(wistron_idev->input, code); if (key) { switch (key->type) { - case KE_KEY: - report_key(wistron_idev->input, key->keycode); - break; - - case KE_SW: - report_switch(wistron_idev->input, - key->sw.code, key->sw.value); - break; - case KE_WIFI: if (have_wifi) { wifi_enabled = !wifi_enabled; @@ -1180,7 +1125,9 @@ static void handle_key(u8 code) break; default: - BUG(); + sparse_keymap_report_entry(wistron_idev->input, + key, 1, true); + break; } jiffies_last_press = jiffies; } else @@ -1220,42 +1167,39 @@ static void wistron_poll(struct input_polled_dev *dev) dev->poll_interval = POLL_INTERVAL_DEFAULT; } -static int wistron_getkeycode(struct input_dev *dev, int scancode, int *keycode) +static int __devinit wistron_setup_keymap(struct input_dev *dev, + struct key_entry *entry) { - const struct key_entry *key = wistron_get_entry_by_scancode(scancode); + switch (entry->type) { - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} + /* if wifi or bluetooth are not available, create normal keys */ + case KE_WIFI: + if (!have_wifi) { + entry->type = KE_KEY; + entry->keycode = KEY_WLAN; + } + break; -static int wistron_setkeycode(struct input_dev *dev, int scancode, int keycode) -{ - struct key_entry *key; - int old_keycode; - - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; - - key = wistron_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!wistron_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; + case KE_BLUETOOTH: + if (!have_bluetooth) { + entry->type = KE_KEY; + entry->keycode = KEY_BLUETOOTH; + } + break; + + case KE_END: + if (entry->code & FE_UNTESTED) + printk(KERN_WARNING "Untested laptop multimedia keys, " + "please report success or failure to " + "eric.piel@tremplin-utc.net\n"); + break; } - return -EINVAL; + return 0; } static int __devinit setup_input_dev(void) { - struct key_entry *key; struct input_dev *input_dev; int error; @@ -1263,7 +1207,7 @@ static int __devinit setup_input_dev(void) if (!wistron_idev) return -ENOMEM; - wistron_idev->flush = wistron_flush; + wistron_idev->open = wistron_flush; wistron_idev->poll = wistron_poll; wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT; @@ -1273,56 +1217,21 @@ static int __devinit setup_input_dev(void) input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &wistron_device->dev; - input_dev->getkeycode = wistron_getkeycode; - input_dev->setkeycode = wistron_setkeycode; - - for (key = keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, input_dev->evbit); - set_bit(key->keycode, input_dev->keybit); - break; - - case KE_SW: - set_bit(EV_SW, input_dev->evbit); - set_bit(key->sw.code, input_dev->swbit); - break; - - /* if wifi or bluetooth are not available, create normal keys */ - case KE_WIFI: - if (!have_wifi) { - key->type = KE_KEY; - key->keycode = KEY_WLAN; - key--; - } - break; - - case KE_BLUETOOTH: - if (!have_bluetooth) { - key->type = KE_KEY; - key->keycode = KEY_BLUETOOTH; - key--; - } - break; - - default: - break; - } - } - - /* reads information flags on KE_END */ - if (key->code & FE_UNTESTED) - printk(KERN_WARNING "Untested laptop multimedia keys, " - "please report success or failure to eric.piel" - "@tremplin-utc.net\n"); + error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap); + if (error) + goto err_free_dev; error = input_register_polled_device(wistron_idev); - if (error) { - input_free_polled_device(wistron_idev); - return error; - } + if (error) + goto err_free_keymap; return 0; + + err_free_keymap: + sparse_keymap_free(input_dev); + err_free_dev: + input_free_polled_device(wistron_idev); + return error; } /* Driver core */ @@ -1371,6 +1280,7 @@ static int __devexit wistron_remove(struct platform_device *dev) { wistron_led_remove(); input_unregister_polled_device(wistron_idev); + sparse_keymap_free(wistron_idev->input); input_free_polled_device(wistron_idev); bios_detach(); diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index f36110689aae..a3f492a50850 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -28,13 +28,16 @@ #define dbg(format, arg...) do {} while (0) #endif -#define ALPS_DUALPOINT 0x01 -#define ALPS_WHEEL 0x02 -#define ALPS_FW_BK_1 0x04 -#define ALPS_4BTN 0x08 -#define ALPS_OLDPROTO 0x10 -#define ALPS_PASS 0x20 -#define ALPS_FW_BK_2 0x40 + +#define ALPS_OLDPROTO 0x01 /* old style input */ +#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ +#define ALPS_PASS 0x04 /* device has a pass-through port */ + +#define ALPS_WHEEL 0x08 /* hardware wheel present */ +#define ALPS_FW_BK_1 0x10 /* front & back buttons present */ +#define ALPS_FW_BK_2 0x20 /* front & back buttons present */ +#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ + static const struct alps_model_info alps_model_data[] = { { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ @@ -56,7 +59,7 @@ static const struct alps_model_info alps_model_data[] = { { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */ - { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 }, /* Dell Vostro 1400 */ + { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ }; /* @@ -83,6 +86,7 @@ static const struct alps_model_info alps_model_data[] = { static void alps_process_packet(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; @@ -101,7 +105,7 @@ static void alps_process_packet(struct psmouse *psmouse) return; } - if (priv->i->flags & ALPS_OLDPROTO) { + if (model->flags & ALPS_OLDPROTO) { left = packet[2] & 0x10; right = packet[2] & 0x08; middle = 0; @@ -117,12 +121,12 @@ static void alps_process_packet(struct psmouse *psmouse) z = packet[5]; } - if (priv->i->flags & ALPS_FW_BK_1) { + if (model->flags & ALPS_FW_BK_1) { back = packet[0] & 0x10; forward = packet[2] & 4; } - if (priv->i->flags & ALPS_FW_BK_2) { + if (model->flags & ALPS_FW_BK_2) { back = packet[3] & 4; forward = packet[2] & 4; if ((middle = forward && back)) @@ -132,7 +136,7 @@ static void alps_process_packet(struct psmouse *psmouse) ges = packet[2] & 1; fin = packet[2] & 2; - if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) { + if ((model->flags & ALPS_DUALPOINT) && z == 127) { input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); @@ -150,7 +154,8 @@ static void alps_process_packet(struct psmouse *psmouse) input_report_key(dev, BTN_MIDDLE, middle); /* Convert hardware tap to a reasonable Z value */ - if (ges && !fin) z = 40; + if (ges && !fin) + z = 40; /* * A "tap and drag" operation is reported by the hardware as a transition @@ -166,8 +171,10 @@ static void alps_process_packet(struct psmouse *psmouse) } priv->prev_fin = fin; - if (z > 30) input_report_key(dev, BTN_TOUCH, 1); - if (z < 25) input_report_key(dev, BTN_TOUCH, 0); + if (z > 30) + input_report_key(dev, BTN_TOUCH, 1); + if (z < 25) + input_report_key(dev, BTN_TOUCH, 0); if (z > 0) { input_report_abs(dev, ABS_X, x); @@ -177,14 +184,21 @@ static void alps_process_packet(struct psmouse *psmouse) input_report_abs(dev, ABS_PRESSURE, z); input_report_key(dev, BTN_TOOL_FINGER, z > 0); - if (priv->i->flags & ALPS_WHEEL) + if (model->flags & ALPS_WHEEL) input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07)); - if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { input_report_key(dev, BTN_FORWARD, forward); input_report_key(dev, BTN_BACK, back); } + if (model->flags & ALPS_FOUR_BUTTONS) { + input_report_key(dev, BTN_0, packet[2] & 4); + input_report_key(dev, BTN_1, packet[0] & 0x10); + input_report_key(dev, BTN_2, packet[3] & 4); + input_report_key(dev, BTN_3, packet[0] & 0x20); + } + input_sync(dev); } @@ -393,15 +407,12 @@ static int alps_poll(struct psmouse *psmouse) return 0; } -static int alps_hw_init(struct psmouse *psmouse, int *version) +static int alps_hw_init(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; - priv->i = alps_get_model(psmouse, version); - if (!priv->i) - return -1; - - if ((priv->i->flags & ALPS_PASS) && + if ((model->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, true)) { return -1; } @@ -416,7 +427,7 @@ static int alps_hw_init(struct psmouse *psmouse, int *version) return -1; } - if ((priv->i->flags & ALPS_PASS) && + if ((model->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, false)) { return -1; } @@ -432,12 +443,15 @@ static int alps_hw_init(struct psmouse *psmouse, int *version) static int alps_reconnect(struct psmouse *psmouse) { + const struct alps_model_info *model; + psmouse_reset(psmouse); - if (alps_hw_init(psmouse, NULL)) + model = alps_get_model(psmouse, NULL); + if (!model) return -1; - return 0; + return alps_hw_init(psmouse); } static void alps_disconnect(struct psmouse *psmouse) @@ -452,6 +466,7 @@ static void alps_disconnect(struct psmouse *psmouse) int alps_init(struct psmouse *psmouse) { struct alps_data *priv; + const struct alps_model_info *model; struct input_dev *dev1 = psmouse->dev, *dev2; int version; @@ -463,33 +478,48 @@ int alps_init(struct psmouse *psmouse) priv->dev2 = dev2; psmouse->private = priv; - if (alps_hw_init(psmouse, &version)) + model = alps_get_model(psmouse, &version); + if (!model) + goto init_fail; + + priv->i = model; + + if (alps_hw_init(psmouse)) goto init_fail; dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER); - dev1->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + dev1->keybit[BIT_WORD(BTN_LEFT)] |= + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); - if (priv->i->flags & ALPS_WHEEL) { + if (model->flags & ALPS_WHEEL) { dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL); } - if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD); dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK); } + if (model->flags & ALPS_FOUR_BUTTONS) { + dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); + dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); + dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); + dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); + } else { + dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); + } + snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); dev2->phys = priv->phys; - dev2->name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; + dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; dev2->id.bustype = BUS_I8042; dev2->id.vendor = 0x0002; dev2->id.product = PSMOUSE_ALPS; @@ -497,9 +527,9 @@ int alps_init(struct psmouse *psmouse) dev2->dev.parent = &psmouse->ps2dev.serio->dev; dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - dev2->relbit[BIT_WORD(REL_X)] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y); - dev2->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + dev2->keybit[BIT_WORD(BTN_LEFT)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); if (input_register_device(priv->dev2)) goto init_fail; diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index fda35e615abf..b27684f267bf 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -420,6 +420,7 @@ static void elantech_set_input_params(struct psmouse *psmouse) __set_bit(EV_KEY, dev->evbit); __set_bit(EV_ABS, dev->evbit); + __clear_bit(EV_REL, dev->evbit); __set_bit(BTN_LEFT, dev->keybit); __set_bit(BTN_RIGHT, dev->keybit); diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index de1e553028b7..b146237266d8 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -430,19 +430,6 @@ static int hgpk_register(struct psmouse *psmouse) struct input_dev *dev = psmouse->dev; int err; - /* unset the things that psmouse-base sets which we don't have */ - __clear_bit(BTN_MIDDLE, dev->keybit); - - /* set the things we do have */ - __set_bit(EV_KEY, dev->evbit); - __set_bit(EV_REL, dev->evbit); - - __set_bit(REL_X, dev->relbit); - __set_bit(REL_Y, dev->relbit); - - __set_bit(BTN_LEFT, dev->keybit); - __set_bit(BTN_RIGHT, dev->keybit); - /* register handlers */ psmouse->protocol_handler = hgpk_process_byte; psmouse->poll = hgpk_poll; diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 82811558ec33..2e6bdfea0165 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -25,11 +25,13 @@ struct lifebook_data { char phys[32]; }; +static bool lifebook_present; + static const char *desired_serio_phys; -static int lifebook_set_serio_phys(const struct dmi_system_id *d) +static int lifebook_limit_serio3(const struct dmi_system_id *d) { - desired_serio_phys = d->driver_data; + desired_serio_phys = "isa0060/serio3"; return 0; } @@ -41,53 +43,53 @@ static int lifebook_set_6byte_proto(const struct dmi_system_id *d) return 0; } -static const struct dmi_system_id lifebook_dmi_table[] = { +static const struct dmi_system_id __initconst lifebook_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) { - .ident = "FLORA-ie 55mi", + /* FLORA-ie 55mi */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"), }, }, { - .ident = "LifeBook B", + /* LifeBook B */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"), }, }, { - .ident = "Lifebook B", + /* Lifebook B */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"), }, }, { - .ident = "Lifebook B-2130", + /* Lifebook B-2130 */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"), }, }, { - .ident = "Lifebook B213x/B2150", + /* Lifebook B213x/B2150 */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"), }, }, { - .ident = "Zephyr", + /* Zephyr */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"), }, }, { - .ident = "CF-18", + /* Panasonic CF-18 */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"), }, - .callback = lifebook_set_serio_phys, - .driver_data = "isa0060/serio3", + .callback = lifebook_limit_serio3, }, { - .ident = "Panasonic CF-28", + /* Panasonic CF-28 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"), @@ -95,7 +97,7 @@ static const struct dmi_system_id lifebook_dmi_table[] = { .callback = lifebook_set_6byte_proto, }, { - .ident = "Panasonic CF-29", + /* Panasonic CF-29 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), @@ -103,21 +105,27 @@ static const struct dmi_system_id lifebook_dmi_table[] = { .callback = lifebook_set_6byte_proto, }, { - .ident = "CF-72", + /* Panasonic CF-72 */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"), }, .callback = lifebook_set_6byte_proto, }, { - .ident = "Lifebook B142", + /* Lifebook B142 */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"), }, }, { } +#endif }; +void __init lifebook_module_init(void) +{ + lifebook_present = dmi_check_system(lifebook_dmi_table); +} + static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) { struct lifebook_data *priv = psmouse->private; @@ -198,10 +206,10 @@ static int lifebook_absolute_mode(struct psmouse *psmouse) return -1; /* - Enable absolute output -- ps2_command fails always but if - you leave this call out the touchsreen will never send - absolute coordinates - */ + * Enable absolute output -- ps2_command fails always but if + * you leave this call out the touchsreen will never send + * absolute coordinates + */ param = lifebook_use_6byte_proto ? 0x08 : 0x07; ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); @@ -243,7 +251,7 @@ static void lifebook_disconnect(struct psmouse *psmouse) int lifebook_detect(struct psmouse *psmouse, bool set_properties) { - if (!dmi_check_system(lifebook_dmi_table)) + if (!lifebook_present) return -1; if (desired_serio_phys && @@ -283,8 +291,8 @@ static int lifebook_create_relative_device(struct psmouse *psmouse) dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - dev2->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT); + dev2->keybit[BIT_WORD(BTN_LEFT)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); error = input_register_device(priv->dev2); if (error) @@ -309,6 +317,7 @@ int lifebook_init(struct psmouse *psmouse) dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); dev1->relbit[0] = 0; + dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0; dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0); diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h index 407cb226bc0a..4c4326c6f504 100644 --- a/drivers/input/mouse/lifebook.h +++ b/drivers/input/mouse/lifebook.h @@ -12,9 +12,13 @@ #define _LIFEBOOK_H #ifdef CONFIG_MOUSE_PS2_LIFEBOOK +void lifebook_module_init(void); int lifebook_detect(struct psmouse *psmouse, bool set_properties); int lifebook_init(struct psmouse *psmouse); #else +inline void lifebook_module_init(void) +{ +} inline int lifebook_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index ab5dc5f5fd83..543c240a85f2 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -404,8 +404,8 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties) } } - if (buttons < 3) - __clear_bit(BTN_MIDDLE, psmouse->dev->keybit); + if (buttons >= 3) + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); if (model_info) ps2pp_set_model_properties(psmouse, model_info, use_ps2pp); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 07c53798301a..fd0bc094616a 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -425,6 +425,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties) return -1; if (set_properties) { + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_EXTRA, psmouse->dev->keybit); __set_bit(BTN_SIDE, psmouse->dev->keybit); __set_bit(REL_WHEEL, psmouse->dev->relbit); @@ -460,8 +461,10 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(REL_WHEEL, psmouse->dev->relbit); - if (!psmouse->vendor) psmouse->vendor = "Generic"; - if (!psmouse->name) psmouse->name = "Wheel Mouse"; + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Wheel Mouse"; psmouse->pktsize = 4; } @@ -504,8 +507,10 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) __set_bit(BTN_SIDE, psmouse->dev->keybit); __set_bit(BTN_EXTRA, psmouse->dev->keybit); - if (!psmouse->vendor) psmouse->vendor = "Generic"; - if (!psmouse->name) psmouse->name = "Explorer Mouse"; + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Explorer Mouse"; psmouse->pktsize = 4; } @@ -536,6 +541,7 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties) return -1; if (set_properties) { + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_EXTRA, psmouse->dev->keybit); psmouse->vendor = "Kensington"; @@ -551,8 +557,16 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties) static int ps2bare_detect(struct psmouse *psmouse, bool set_properties) { if (set_properties) { - if (!psmouse->vendor) psmouse->vendor = "Generic"; - if (!psmouse->name) psmouse->name = "Mouse"; + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Mouse"; + +/* + * We have no way of figuring true number of buttons so let's + * assume that the device has 3. + */ + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); } return 0; @@ -567,6 +581,8 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties) if (set_properties) { psmouse->vendor = "Cortron"; psmouse->name = "PS/2 Trackball"; + + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_SIDE, psmouse->dev->keybit); } @@ -1184,15 +1200,16 @@ static void psmouse_disconnect(struct serio *serio) mutex_unlock(&psmouse_mutex); } -static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto) +static int psmouse_switch_protocol(struct psmouse *psmouse, + const struct psmouse_protocol *proto) { struct input_dev *input_dev = psmouse->dev; input_dev->dev.parent = &psmouse->ps2dev.serio->dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + input_dev->keybit[BIT_WORD(BTN_MOUSE)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); psmouse->set_rate = psmouse_set_rate; @@ -1209,8 +1226,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse return -1; psmouse->type = proto->type; - } - else + } else psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, true); @@ -1680,6 +1696,9 @@ static int __init psmouse_init(void) { int err; + lifebook_module_init(); + synaptics_module_init(); + kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); if (!kpsmoused_wq) { printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n"); diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index f84cbd97c884..77b9fd0b3fbf 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -836,6 +836,7 @@ int fsp_init(struct psmouse *psmouse) priv->flags |= FSPDRV_FLAG_EN_OPC; /* Set up various supported input event bits */ + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_BACK, psmouse->dev->keybit); __set_bit(BTN_FORWARD, psmouse->dev->keybit); __set_bit(REL_WHEEL, psmouse->dev->relbit); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f4a61252bcc9..05689e732191 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -24,6 +24,7 @@ */ #include <linux/module.h> +#include <linux/dmi.h> #include <linux/input.h> #include <linux/serio.h> #include <linux/libps2.h> @@ -629,25 +630,26 @@ static int synaptics_reconnect(struct psmouse *psmouse) return 0; } -#if defined(__i386__) -#include <linux/dmi.h> -static const struct dmi_system_id toshiba_dmi_table[] = { +static bool impaired_toshiba_kbc; + +static const struct dmi_system_id __initconst toshiba_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) { - .ident = "Toshiba Satellite", + /* Toshiba Satellite */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), }, }, { - .ident = "Toshiba Dynabook", + /* Toshiba Dynabook */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), }, }, { - .ident = "Toshiba Portege M300", + /* Toshiba Portege M300 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), @@ -655,7 +657,7 @@ static const struct dmi_system_id toshiba_dmi_table[] = { }, { - .ident = "Toshiba Portege M300", + /* Toshiba Portege M300 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), @@ -664,8 +666,13 @@ static const struct dmi_system_id toshiba_dmi_table[] = { }, { } -}; #endif +}; + +void __init synaptics_module_init(void) +{ + impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); +} int synaptics_init(struct psmouse *psmouse) { @@ -718,18 +725,16 @@ int synaptics_init(struct psmouse *psmouse) if (SYN_CAP_PASS_THROUGH(priv->capabilities)) synaptics_pt_create(psmouse); -#if defined(__i386__) /* * Toshiba's KBC seems to have trouble handling data from * Synaptics as full rate, switch to lower rate which is roughly * thye same as rate of standard PS/2 mouse. */ - if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) { + if (psmouse->rate >= 80 && impaired_toshiba_kbc) { printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n", dmi_get_system_info(DMI_PRODUCT_NAME)); psmouse->rate = 40; } -#endif return 0; @@ -740,6 +745,10 @@ int synaptics_init(struct psmouse *psmouse) #else /* CONFIG_MOUSE_PS2_SYNAPTICS */ +void __init synaptics_module_init(void) +{ +} + int synaptics_init(struct psmouse *psmouse) { return -ENOSYS; diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 871f6fe377f9..838e7f2c9b30 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -105,6 +105,7 @@ struct synaptics_data { int scroll; }; +void synaptics_module_init(void); int synaptics_detect(struct psmouse *psmouse, bool set_properties); int synaptics_init(struct psmouse *psmouse); void synaptics_reset(struct psmouse *psmouse); diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 7283c78044af..9867dfe2a638 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -420,8 +420,8 @@ static void synaptics_i2c_check_params(struct synaptics_i2c *touch) } /* Control the Device polling rate / Work Handler sleep time */ -unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch, - bool have_data) +static unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch, + bool have_data) { unsigned long delay, nodata_count_thres; @@ -520,7 +520,7 @@ static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch) __set_bit(BTN_LEFT, input->keybit); } -struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) +static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) { struct synaptics_i2c *touch; diff --git a/drivers/input/mouse/touchkit_ps2.c b/drivers/input/mouse/touchkit_ps2.c index 0308a0faa94d..909431c31ab4 100644 --- a/drivers/input/mouse/touchkit_ps2.c +++ b/drivers/input/mouse/touchkit_ps2.c @@ -86,7 +86,8 @@ int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties) if (set_properties) { dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - __set_bit(BTN_TOUCH, dev->keybit); + dev->keybit[BIT_WORD(BTN_MOUSE)] = 0; + dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0); input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0); diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index e354362f2971..63d4a67830f2 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -284,7 +284,6 @@ static int trackpoint_reconnect(struct psmouse *psmouse) int trackpoint_detect(struct psmouse *psmouse, bool set_properties) { - struct trackpoint_data *priv; struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char firmware_id; unsigned char button_info; @@ -301,8 +300,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) button_info = 0; } - psmouse->private = priv = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); - if (!priv) + psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); + if (!psmouse->private) return -1; psmouse->vendor = "IBM"; @@ -311,7 +310,10 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) psmouse->reconnect = trackpoint_reconnect; psmouse->disconnect = trackpoint_disconnect; - trackpoint_defaults(priv); + if ((button_info & 0x0f) >= 3) + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + + trackpoint_defaults(psmouse->private); trackpoint_sync(psmouse); error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group); @@ -319,7 +321,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) printk(KERN_ERR "trackpoint.c: failed to create sysfs attributes, error: %d\n", error); - kfree(priv); + kfree(psmouse->private); + psmouse->private = NULL; return -1; } diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index 70111443678e..bf2c0c80d6cc 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -86,27 +86,28 @@ #define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet" -MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_LICENSE ("GPL"); +MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); #undef VSXXXAA_DEBUG #ifdef VSXXXAA_DEBUG -#define DBG(x...) printk (x) +#define DBG(x...) printk(x) #else #define DBG(x...) do {} while (0) #endif #define VSXXXAA_INTRO_MASK 0x80 #define VSXXXAA_INTRO_HEAD 0x80 -#define IS_HDR_BYTE(x) (((x) & VSXXXAA_INTRO_MASK) \ - == VSXXXAA_INTRO_HEAD) +#define IS_HDR_BYTE(x) \ + (((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD) #define VSXXXAA_PACKET_MASK 0xe0 #define VSXXXAA_PACKET_REL 0x80 #define VSXXXAA_PACKET_ABS 0xc0 #define VSXXXAA_PACKET_POR 0xa0 -#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == (type)) +#define MATCH_PACKET_TYPE(data, type) \ + (((data) & VSXXXAA_PACKET_MASK) == (type)) @@ -123,52 +124,50 @@ struct vsxxxaa { char phys[32]; }; -static void -vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num) +static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num) { - if (num >= mouse->count) + if (num >= mouse->count) { mouse->count = 0; - else { - memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num); + } else { + memmove(mouse->buf, mouse->buf + num - 1, BUFLEN - num); mouse->count -= num; } } -static void -vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte) +static void vsxxxaa_queue_byte(struct vsxxxaa *mouse, unsigned char byte) { if (mouse->count == BUFLEN) { - printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n", - mouse->name, mouse->phys); - vsxxxaa_drop_bytes (mouse, 1); + printk(KERN_ERR "%s on %s: Dropping a byte of full buffer.\n", + mouse->name, mouse->phys); + vsxxxaa_drop_bytes(mouse, 1); } - DBG (KERN_INFO "Queueing byte 0x%02x\n", byte); + + DBG(KERN_INFO "Queueing byte 0x%02x\n", byte); mouse->buf[mouse->count++] = byte; } -static void -vsxxxaa_detection_done (struct vsxxxaa *mouse) +static void vsxxxaa_detection_done(struct vsxxxaa *mouse) { switch (mouse->type) { - case 0x02: - strlcpy (mouse->name, "DEC VSXXX-AA/-GA mouse", - sizeof (mouse->name)); - break; - - case 0x04: - strlcpy (mouse->name, "DEC VSXXX-AB digitizer", - sizeof (mouse->name)); - break; - - default: - snprintf (mouse->name, sizeof (mouse->name), - "unknown DEC pointer device (type = 0x%02x)", - mouse->type); - break; + case 0x02: + strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse", + sizeof(mouse->name)); + break; + + case 0x04: + strlcpy(mouse->name, "DEC VSXXX-AB digitizer", + sizeof(mouse->name)); + break; + + default: + snprintf(mouse->name, sizeof(mouse->name), + "unknown DEC pointer device (type = 0x%02x)", + mouse->type); + break; } - printk (KERN_INFO + printk(KERN_INFO "Found %s version 0x%02x from country 0x%02x on port %s\n", mouse->name, mouse->version, mouse->country, mouse->phys); } @@ -176,42 +175,38 @@ vsxxxaa_detection_done (struct vsxxxaa *mouse) /* * Returns number of bytes to be dropped, 0 if packet is okay. */ -static int -vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len) +static int vsxxxaa_check_packet(struct vsxxxaa *mouse, int packet_len) { int i; /* First byte must be a header byte */ - if (!IS_HDR_BYTE (mouse->buf[0])) { - DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]); + if (!IS_HDR_BYTE(mouse->buf[0])) { + DBG("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]); return 1; } /* Check all following bytes */ - if (packet_len > 1) { - for (i = 1; i < packet_len; i++) { - if (IS_HDR_BYTE (mouse->buf[i])) { - printk (KERN_ERR "Need to drop %d bytes " - "of a broken packet.\n", - i - 1); - DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n", - packet_len, i, mouse->buf[i]); - return i - 1; - } + for (i = 1; i < packet_len; i++) { + if (IS_HDR_BYTE(mouse->buf[i])) { + printk(KERN_ERR + "Need to drop %d bytes of a broken packet.\n", + i - 1); + DBG(KERN_INFO "check: len=%d, b[%d]=0x%02x\n", + packet_len, i, mouse->buf[i]); + return i - 1; } } return 0; } -static __inline__ int -vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len) +static inline int vsxxxaa_smells_like_packet(struct vsxxxaa *mouse, + unsigned char type, size_t len) { - return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type); + return mouse->count >= len && MATCH_PACKET_TYPE(mouse->buf[0], type); } -static void -vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse) +static void vsxxxaa_handle_REL_packet(struct vsxxxaa *mouse) { struct input_dev *dev = mouse->dev; unsigned char *buf = mouse->buf; @@ -232,43 +227,42 @@ vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse) * 0, bit 4 of byte 0 is direction. */ dx = buf[1] & 0x7f; - dx *= ((buf[0] >> 4) & 0x01)? 1: -1; + dx *= ((buf[0] >> 4) & 0x01) ? 1 : -1; /* * Low 7 bit of byte 2 are abs(dy), bit 7 is * 0, bit 3 of byte 0 is direction. */ dy = buf[2] & 0x7f; - dy *= ((buf[0] >> 3) & 0x01)? -1: 1; + dy *= ((buf[0] >> 3) & 0x01) ? -1 : 1; /* * Get button state. It's the low three bits * (for three buttons) of byte 0. */ - left = (buf[0] & 0x04)? 1: 0; - middle = (buf[0] & 0x02)? 1: 0; - right = (buf[0] & 0x01)? 1: 0; + left = buf[0] & 0x04; + middle = buf[0] & 0x02; + right = buf[0] & 0x01; - vsxxxaa_drop_bytes (mouse, 3); + vsxxxaa_drop_bytes(mouse, 3); - DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n", - mouse->name, mouse->phys, dx, dy, - left? "L": "l", middle? "M": "m", right? "R": "r"); + DBG(KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n", + mouse->name, mouse->phys, dx, dy, + left ? "L" : "l", middle ? "M" : "m", right ? "R" : "r"); /* * Report what we've found so far... */ - input_report_key (dev, BTN_LEFT, left); - input_report_key (dev, BTN_MIDDLE, middle); - input_report_key (dev, BTN_RIGHT, right); - input_report_key (dev, BTN_TOUCH, 0); - input_report_rel (dev, REL_X, dx); - input_report_rel (dev, REL_Y, dy); - input_sync (dev); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, 0); + input_report_rel(dev, REL_X, dx); + input_report_rel(dev, REL_Y, dy); + input_sync(dev); } -static void -vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse) +static void vsxxxaa_handle_ABS_packet(struct vsxxxaa *mouse) { struct input_dev *dev = mouse->dev; unsigned char *buf = mouse->buf; @@ -296,32 +290,31 @@ vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse) /* * Get button state. It's bits <4..1> of byte 0. */ - left = (buf[0] & 0x02)? 1: 0; - middle = (buf[0] & 0x04)? 1: 0; - right = (buf[0] & 0x08)? 1: 0; - touch = (buf[0] & 0x10)? 1: 0; + left = buf[0] & 0x02; + middle = buf[0] & 0x04; + right = buf[0] & 0x08; + touch = buf[0] & 0x10; - vsxxxaa_drop_bytes (mouse, 5); + vsxxxaa_drop_bytes(mouse, 5); - DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n", - mouse->name, mouse->phys, x, y, - left? "L": "l", middle? "M": "m", - right? "R": "r", touch? "T": "t"); + DBG(KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n", + mouse->name, mouse->phys, x, y, + left ? "L" : "l", middle ? "M" : "m", + right ? "R" : "r", touch ? "T" : "t"); /* * Report what we've found so far... */ - input_report_key (dev, BTN_LEFT, left); - input_report_key (dev, BTN_MIDDLE, middle); - input_report_key (dev, BTN_RIGHT, right); - input_report_key (dev, BTN_TOUCH, touch); - input_report_abs (dev, ABS_X, x); - input_report_abs (dev, ABS_Y, y); - input_sync (dev); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, touch); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_sync(dev); } -static void -vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse) +static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse) { struct input_dev *dev = mouse->dev; unsigned char *buf = mouse->buf; @@ -356,24 +349,24 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse) * (for three buttons) of byte 0. Maybe even the bit <3> * has some meaning if a tablet is attached. */ - left = (buf[0] & 0x04)? 1: 0; - middle = (buf[0] & 0x02)? 1: 0; - right = (buf[0] & 0x01)? 1: 0; + left = buf[0] & 0x04; + middle = buf[0] & 0x02; + right = buf[0] & 0x01; - vsxxxaa_drop_bytes (mouse, 4); - vsxxxaa_detection_done (mouse); + vsxxxaa_drop_bytes(mouse, 4); + vsxxxaa_detection_done(mouse); if (error <= 0x1f) { /* No (serious) error. Report buttons */ - input_report_key (dev, BTN_LEFT, left); - input_report_key (dev, BTN_MIDDLE, middle); - input_report_key (dev, BTN_RIGHT, right); - input_report_key (dev, BTN_TOUCH, 0); - input_sync (dev); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, 0); + input_sync(dev); if (error != 0) - printk (KERN_INFO "Your %s on %s reports error=0x%02x\n", - mouse->name, mouse->phys, error); + printk(KERN_INFO "Your %s on %s reports error=0x%02x\n", + mouse->name, mouse->phys, error); } @@ -381,18 +374,18 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse) * If the mouse was hot-plugged, we need to force differential mode * now... However, give it a second to recover from it's reset. */ - printk (KERN_NOTICE "%s on %s: Forceing standard packet format, " - "incremental streaming mode and 72 samples/sec\n", - mouse->name, mouse->phys); - serio_write (mouse->serio, 'S'); /* Standard format */ - mdelay (50); - serio_write (mouse->serio, 'R'); /* Incremental */ - mdelay (50); - serio_write (mouse->serio, 'L'); /* 72 samples/sec */ + printk(KERN_NOTICE + "%s on %s: Forcing standard packet format, " + "incremental streaming mode and 72 samples/sec\n", + mouse->name, mouse->phys); + serio_write(mouse->serio, 'S'); /* Standard format */ + mdelay(50); + serio_write(mouse->serio, 'R'); /* Incremental */ + mdelay(50); + serio_write(mouse->serio, 'L'); /* 72 samples/sec */ } -static void -vsxxxaa_parse_buffer (struct vsxxxaa *mouse) +static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse) { unsigned char *buf = mouse->buf; int stray_bytes; @@ -409,122 +402,107 @@ vsxxxaa_parse_buffer (struct vsxxxaa *mouse) * activity on the mouse. */ while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) { - printk (KERN_ERR "%s on %s: Dropping a byte to regain " - "sync with mouse data stream...\n", - mouse->name, mouse->phys); - vsxxxaa_drop_bytes (mouse, 1); + printk(KERN_ERR "%s on %s: Dropping a byte to regain " + "sync with mouse data stream...\n", + mouse->name, mouse->phys); + vsxxxaa_drop_bytes(mouse, 1); } /* * Check for packets we know about. */ - if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) { + if (vsxxxaa_smells_like_packet(mouse, VSXXXAA_PACKET_REL, 3)) { /* Check for broken packet */ - stray_bytes = vsxxxaa_check_packet (mouse, 3); - if (stray_bytes > 0) { - printk (KERN_ERR "Dropping %d bytes now...\n", - stray_bytes); - vsxxxaa_drop_bytes (mouse, stray_bytes); - continue; - } - - vsxxxaa_handle_REL_packet (mouse); - continue; /* More to parse? */ - } + stray_bytes = vsxxxaa_check_packet(mouse, 3); + if (!stray_bytes) + vsxxxaa_handle_REL_packet(mouse); - if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) { + } else if (vsxxxaa_smells_like_packet(mouse, + VSXXXAA_PACKET_ABS, 5)) { /* Check for broken packet */ - stray_bytes = vsxxxaa_check_packet (mouse, 5); - if (stray_bytes > 0) { - printk (KERN_ERR "Dropping %d bytes now...\n", - stray_bytes); - vsxxxaa_drop_bytes (mouse, stray_bytes); - continue; - } - - vsxxxaa_handle_ABS_packet (mouse); - continue; /* More to parse? */ - } + stray_bytes = vsxxxaa_check_packet(mouse, 5); + if (!stray_bytes) + vsxxxaa_handle_ABS_packet(mouse); - if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) { + } else if (vsxxxaa_smells_like_packet(mouse, + VSXXXAA_PACKET_POR, 4)) { /* Check for broken packet */ - stray_bytes = vsxxxaa_check_packet (mouse, 4); - if (stray_bytes > 0) { - printk (KERN_ERR "Dropping %d bytes now...\n", - stray_bytes); - vsxxxaa_drop_bytes (mouse, stray_bytes); - continue; - } - - vsxxxaa_handle_POR_packet (mouse); - continue; /* More to parse? */ + stray_bytes = vsxxxaa_check_packet(mouse, 4); + if (!stray_bytes) + vsxxxaa_handle_POR_packet(mouse); + + } else { + break; /* No REL, ABS or POR packet found */ + } + + if (stray_bytes > 0) { + printk(KERN_ERR "Dropping %d bytes now...\n", + stray_bytes); + vsxxxaa_drop_bytes(mouse, stray_bytes); } - break; /* No REL, ABS or POR packet found */ } while (1); } -static irqreturn_t -vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags) +static irqreturn_t vsxxxaa_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) { - struct vsxxxaa *mouse = serio_get_drvdata (serio); + struct vsxxxaa *mouse = serio_get_drvdata(serio); - vsxxxaa_queue_byte (mouse, data); - vsxxxaa_parse_buffer (mouse); + vsxxxaa_queue_byte(mouse, data); + vsxxxaa_parse_buffer(mouse); return IRQ_HANDLED; } -static void -vsxxxaa_disconnect (struct serio *serio) +static void vsxxxaa_disconnect(struct serio *serio) { - struct vsxxxaa *mouse = serio_get_drvdata (serio); + struct vsxxxaa *mouse = serio_get_drvdata(serio); - serio_close (serio); - serio_set_drvdata (serio, NULL); - input_unregister_device (mouse->dev); - kfree (mouse); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(mouse->dev); + kfree(mouse); } -static int -vsxxxaa_connect (struct serio *serio, struct serio_driver *drv) +static int vsxxxaa_connect(struct serio *serio, struct serio_driver *drv) { struct vsxxxaa *mouse; struct input_dev *input_dev; int err = -ENOMEM; - mouse = kzalloc (sizeof (struct vsxxxaa), GFP_KERNEL); - input_dev = input_allocate_device (); + mouse = kzalloc(sizeof(struct vsxxxaa), GFP_KERNEL); + input_dev = input_allocate_device(); if (!mouse || !input_dev) goto fail1; mouse->dev = input_dev; mouse->serio = serio; - strlcat (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer", - sizeof (mouse->name)); - snprintf (mouse->phys, sizeof (mouse->phys), "%s/input0", serio->phys); + strlcat(mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer", + sizeof(mouse->name)); + snprintf(mouse->phys, sizeof(mouse->phys), "%s/input0", serio->phys); input_dev->name = mouse->name; input_dev->phys = mouse->phys; input_dev->id.bustype = BUS_RS232; input_dev->dev.parent = &serio->dev; - set_bit (EV_KEY, input_dev->evbit); /* We have buttons */ - set_bit (EV_REL, input_dev->evbit); - set_bit (EV_ABS, input_dev->evbit); - set_bit (BTN_LEFT, input_dev->keybit); /* We have 3 buttons */ - set_bit (BTN_MIDDLE, input_dev->keybit); - set_bit (BTN_RIGHT, input_dev->keybit); - set_bit (BTN_TOUCH, input_dev->keybit); /* ...and Tablet */ - set_bit (REL_X, input_dev->relbit); - set_bit (REL_Y, input_dev->relbit); - input_set_abs_params (input_dev, ABS_X, 0, 1023, 0, 0); - input_set_abs_params (input_dev, ABS_Y, 0, 1023, 0, 0); - - serio_set_drvdata (serio, mouse); - - err = serio_open (serio, drv); + __set_bit(EV_KEY, input_dev->evbit); /* We have buttons */ + __set_bit(EV_REL, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_LEFT, input_dev->keybit); /* We have 3 buttons */ + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_TOUCH, input_dev->keybit); /* ...and Tablet */ + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0); + + serio_set_drvdata(serio, mouse); + + err = serio_open(serio, drv); if (err) goto fail2; @@ -532,18 +510,18 @@ vsxxxaa_connect (struct serio *serio, struct serio_driver *drv) * Request selftest. Standard packet format and differential * mode will be requested after the device ID'ed successfully. */ - serio_write (serio, 'T'); /* Test */ + serio_write(serio, 'T'); /* Test */ - err = input_register_device (input_dev); + err = input_register_device(input_dev); if (err) goto fail3; return 0; - fail3: serio_close (serio); - fail2: serio_set_drvdata (serio, NULL); - fail1: input_free_device (input_dev); - kfree (mouse); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(mouse); return err; } @@ -570,18 +548,16 @@ static struct serio_driver vsxxxaa_drv = { .disconnect = vsxxxaa_disconnect, }; -static int __init -vsxxxaa_init (void) +static int __init vsxxxaa_init(void) { return serio_register_driver(&vsxxxaa_drv); } -static void __exit -vsxxxaa_exit (void) +static void __exit vsxxxaa_exit(void) { serio_unregister_driver(&vsxxxaa_drv); } -module_init (vsxxxaa_init); -module_exit (vsxxxaa_exit); +module_init(vsxxxaa_init); +module_exit(vsxxxaa_exit); diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index aa533ceffe34..7e319d65ec57 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -201,4 +201,12 @@ config SERIO_XILINX_XPS_PS2 To compile this driver as a module, choose M here: the module will be called xilinx_ps2. +config SERIO_ALTERA_PS2 + tristate "Altera UP PS/2 controller" + help + Say Y here if you have Altera University Program PS/2 ports. + + To compile this driver as a module, choose M here: the + module will be called altera_ps2. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 9b6c8135955f..bf945f789d05 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o obj-$(CONFIG_SERIO_LIBPS2) += libps2.o obj-$(CONFIG_SERIO_RAW) += serio_raw.o obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o +obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c new file mode 100644 index 000000000000..f479ea50919f --- /dev/null +++ b/drivers/input/serio/altera_ps2.c @@ -0,0 +1,200 @@ +/* + * Altera University Program PS2 controller driver + * + * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw> + * + * Based on sa1111ps2.c, which is: + * Copyright (C) 2002 Russell King + * + * 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/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#define DRV_NAME "altera_ps2" + +struct ps2if { + struct serio *io; + struct resource *iomem_res; + void __iomem *base; + unsigned irq; +}; + +/* + * Read all bytes waiting in the PS2 port. There should be + * at the most one, but we loop for safety. + */ +static irqreturn_t altera_ps2_rxint(int irq, void *dev_id) +{ + struct ps2if *ps2if = dev_id; + unsigned int status; + int handled = IRQ_NONE; + + while ((status = readl(ps2if->base)) & 0xffff0000) { + serio_interrupt(ps2if->io, status & 0xff, 0); + handled = IRQ_HANDLED; + } + + return handled; +} + +/* + * Write a byte to the PS2 port. + */ +static int altera_ps2_write(struct serio *io, unsigned char val) +{ + struct ps2if *ps2if = io->port_data; + + writel(val, ps2if->base); + return 0; +} + +static int altera_ps2_open(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + /* clear fifo */ + while (readl(ps2if->base) & 0xffff0000) + /* empty */; + + writel(1, ps2if->base + 4); /* enable rx irq */ + return 0; +} + +static void altera_ps2_close(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + writel(0, ps2if->base); /* disable rx irq */ +} + +/* + * Add one device to this driver. + */ +static int altera_ps2_probe(struct platform_device *pdev) +{ + struct ps2if *ps2if; + struct serio *serio; + int error; + + ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { + error = -ENOMEM; + goto err_free_mem; + } + + serio->id.type = SERIO_8042; + serio->write = altera_ps2_write; + serio->open = altera_ps2_open; + serio->close = altera_ps2_close; + strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name)); + strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &pdev->dev; + ps2if->io = serio; + + ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (ps2if->iomem_res == NULL) { + error = -ENOENT; + goto err_free_mem; + } + + ps2if->irq = platform_get_irq(pdev, 0); + if (ps2if->irq < 0) { + error = -ENXIO; + goto err_free_mem; + } + + if (!request_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res), pdev->name)) { + error = -EBUSY; + goto err_free_mem; + } + + ps2if->base = ioremap(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + if (!ps2if->base) { + error = -ENOMEM; + goto err_free_res; + } + + error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if); + if (error) { + dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n", + ps2if->irq, error); + goto err_unmap; + } + + dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq); + + serio_register_port(ps2if->io); + platform_set_drvdata(pdev, ps2if); + + return 0; + + err_unmap: + iounmap(ps2if->base); + err_free_res: + release_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + err_free_mem: + kfree(ps2if); + kfree(serio); + return error; +} + +/* + * Remove one device from this driver. + */ +static int altera_ps2_remove(struct platform_device *pdev) +{ + struct ps2if *ps2if = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + serio_unregister_port(ps2if->io); + free_irq(ps2if->irq, ps2if); + iounmap(ps2if->base); + release_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + kfree(ps2if); + + return 0; +} + +/* + * Our device driver structure + */ +static struct platform_driver altera_ps2_driver = { + .probe = altera_ps2_probe, + .remove = altera_ps2_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +static int __init altera_ps2_init(void) +{ + return platform_driver_register(&altera_ps2_driver); +} + +static void __exit altera_ps2_exit(void) +{ + platform_driver_unregister(&altera_ps2_driver); +} + +module_init(altera_ps2_init); +module_exit(altera_ps2_exit); + +MODULE_DESCRIPTION("Altera University Program PS2 controller driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 1c9410d1822c..bcc2d30ec245 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -955,7 +955,7 @@ static int __init hp_sdc_init_hppa(struct parisc_device *d) INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed); ret = hp_sdc_init(); - /* after sucessfull initialization give SDC some time to settle + /* after successfull initialization give SDC some time to settle * and then load the hp_sdc_mlc upper layer driver */ if (!ret) schedule_delayed_work(&moduleloader_work, diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index 820e51673b26..7d2b820ef58d 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -125,7 +125,7 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id, break; default: - printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data); + printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n", data); break; } diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 2bcf1ace27c0..7fbffe431bc5 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -67,10 +67,12 @@ static inline void i8042_write_command(int val) #include <linux/dmi.h> -static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { { - /* AUX LOOP command does not raise AUX IRQ */ - .ident = "Arima-Rioworks HDAMB", + /* + * Arima-Rioworks HDAMB - + * AUX LOOP command does not raise AUX IRQ + */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), @@ -78,7 +80,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "ASUS G1S", + /* ASUS G1S */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), DMI_MATCH(DMI_BOARD_NAME, "G1S"), @@ -86,8 +88,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - /* AUX LOOP command does not raise AUX IRQ */ - .ident = "ASUS P65UP5", + /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"), @@ -95,7 +96,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Compaq Proliant 8500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), @@ -103,7 +103,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Compaq Proliant DL760", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), @@ -111,7 +110,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "OQO Model 01", + /* OQO Model 01 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "OQO"), DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), @@ -119,8 +118,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - /* AUX LOOP does not work properly */ - .ident = "ULI EV4873", + /* ULI EV4873 - AUX LOOP does not work properly */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ULI"), DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), @@ -128,7 +126,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Microsoft Virtual Machine", + /* Microsoft Virtual Machine */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), @@ -136,7 +134,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Medion MAM 2070", + /* Medion MAM 2070 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), @@ -144,7 +142,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Blue FB5601", + /* Blue FB5601 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "blue"), DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), @@ -152,7 +150,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Gigabyte M912", + /* Gigabyte M912 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), DMI_MATCH(DMI_PRODUCT_NAME, "M912"), @@ -160,7 +158,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "HP DV9700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), @@ -177,72 +174,72 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { * ... apparently some Toshibas don't like MUX mode either and * die horrible death on reboot. */ -static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { - .ident = "Fujitsu Lifebook P7010/P7010D", + /* Fujitsu Lifebook P7010/P7010D */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), }, }, { - .ident = "Fujitsu Lifebook P7010", + /* Fujitsu Lifebook P7010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), }, }, { - .ident = "Fujitsu Lifebook P5020D", + /* Fujitsu Lifebook P5020D */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), }, }, { - .ident = "Fujitsu Lifebook S2000", + /* Fujitsu Lifebook S2000 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), }, }, { - .ident = "Fujitsu Lifebook S6230", + /* Fujitsu Lifebook S6230 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), }, }, { - .ident = "Fujitsu T70H", + /* Fujitsu T70H */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), }, }, { - .ident = "Fujitsu-Siemens Lifebook T3010", + /* Fujitsu-Siemens Lifebook T3010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), }, }, { - .ident = "Fujitsu-Siemens Lifebook E4010", + /* Fujitsu-Siemens Lifebook E4010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), }, }, { - .ident = "Fujitsu-Siemens Amilo Pro 2010", + /* Fujitsu-Siemens Amilo Pro 2010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), }, }, { - .ident = "Fujitsu-Siemens Amilo Pro 2030", + /* Fujitsu-Siemens Amilo Pro 2030 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), @@ -253,7 +250,7 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { * No data is coming from the touchscreen unless KBC * is in legacy mode. */ - .ident = "Panasonic CF-29", + /* Panasonic CF-29 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), @@ -261,10 +258,10 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, { /* - * Errors on MUX ports are reported without raising AUXDATA + * HP Pavilion DV4017EA - + * errors on MUX ports are reported without raising AUXDATA * causing "spurious NAK" messages. */ - .ident = "HP Pavilion DV4017EA", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), @@ -272,9 +269,9 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, { /* - * Like DV4017EA does not raise AUXERR for errors on MUX ports. + * HP Pavilion ZT1000 - + * like DV4017EA does not raise AUXERR for errors on MUX ports. */ - .ident = "HP Pavilion ZT1000", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), @@ -283,44 +280,41 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, { /* - * Like DV4017EA does not raise AUXERR for errors on MUX ports. + * HP Pavilion DV4270ca - + * like DV4017EA does not raise AUXERR for errors on MUX ports. */ - .ident = "HP Pavilion DV4270ca", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), }, }, { - .ident = "Toshiba P10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), }, }, { - .ident = "Toshiba Equium A110", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), }, }, { - .ident = "Alienware Sentia", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), }, }, { - .ident = "Sharp Actius MM20", + /* Sharp Actius MM20 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), }, }, { - .ident = "Sony Vaio FS-115b", + /* Sony Vaio FS-115b */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), @@ -328,73 +322,72 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, { /* - * Reset and GET ID commands issued via KBD port are + * Sony Vaio FZ-240E - + * reset and GET ID commands issued via KBD port are * sometimes being delivered to AUX3. */ - .ident = "Sony Vaio FZ-240E", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), }, }, { - .ident = "Amoi M636/A737", + /* Amoi M636/A737 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), }, }, { - .ident = "Lenovo 3000 n100", + /* Lenovo 3000 n100 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), }, }, { - .ident = "Acer Aspire 1360", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), }, }, { - .ident = "Gericom Bellagio", + /* Gericom Bellagio */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), }, }, { - .ident = "IBM 2656", + /* IBM 2656 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), DMI_MATCH(DMI_PRODUCT_NAME, "2656"), }, }, { - .ident = "Dell XPS M1530", + /* Dell XPS M1530 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), }, }, { - .ident = "Compal HEL80I", + /* Compal HEL80I */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), }, }, { - .ident = "Dell Vostro 1510", + /* Dell Vostro 1510 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), }, }, { - .ident = "Acer Aspire 5536", + /* Acer Aspire 5536 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), @@ -404,65 +397,65 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { { } }; -static struct dmi_system_id __initdata i8042_dmi_reset_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { { - .ident = "MSI Wind U-100", + /* MSI Wind U-100 */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "U-100"), DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), }, }, { - .ident = "LG Electronics X110", + /* LG Electronics X110 */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "X110"), DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), }, }, { - .ident = "Acer Aspire One 150", + /* Acer Aspire One 150 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), }, }, { - .ident = "Advent 4211", + /* Advent 4211 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), }, }, { - .ident = "Medion Akoya Mini E1210", + /* Medion Akoya Mini E1210 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), DMI_MATCH(DMI_PRODUCT_NAME, "E1210"), }, }, { - .ident = "Mivvy M310", + /* Mivvy M310 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), DMI_MATCH(DMI_PRODUCT_NAME, "N10"), }, }, { - .ident = "Dell Vostro 1320", + /* Dell Vostro 1320 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), }, }, { - .ident = "Dell Vostro 1520", + /* Dell Vostro 1520 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), }, }, { - .ident = "Dell Vostro 1720", + /* Dell Vostro 1720 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), @@ -472,16 +465,16 @@ static struct dmi_system_id __initdata i8042_dmi_reset_table[] = { }; #ifdef CONFIG_PNP -static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = { { - .ident = "Intel MBO Desktop D845PESV", + /* Intel MBO Desktop D845PESV */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), }, }, { - .ident = "MSI Wind U-100", + /* MSI Wind U-100 */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "U-100"), DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), @@ -490,27 +483,23 @@ static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = { { } }; -static struct dmi_system_id __initdata i8042_dmi_laptop_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { { - .ident = "Portable", .matches = { DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ }, }, { - .ident = "Laptop", .matches = { DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ }, }, { - .ident = "Notebook", .matches = { DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ }, }, { - .ident = "Sub-Notebook", .matches = { DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ }, @@ -525,58 +514,58 @@ static struct dmi_system_id __initdata i8042_dmi_laptop_table[] = { * Originally, this was just confined to older laptops, but a few Acer laptops * have turned up in 2007 that also need this again. */ -static struct dmi_system_id __initdata i8042_dmi_dritek_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { { - .ident = "Acer Aspire 5630", + /* Acer Aspire 5630 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), }, }, { - .ident = "Acer Aspire 5650", + /* Acer Aspire 5650 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), }, }, { - .ident = "Acer Aspire 5680", + /* Acer Aspire 5680 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), }, }, { - .ident = "Acer Aspire 5720", + /* Acer Aspire 5720 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), }, }, { - .ident = "Acer Aspire 9110", + /* Acer Aspire 9110 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), }, }, { - .ident = "Acer TravelMate 660", + /* Acer TravelMate 660 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), }, }, { - .ident = "Acer TravelMate 2490", + /* Acer TravelMate 2490 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), }, }, { - .ident = "Acer TravelMate 4280", + /* Acer TravelMate 4280 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c new file mode 100644 index 000000000000..fbd3987af57f --- /dev/null +++ b/drivers/input/sparse-keymap.c @@ -0,0 +1,250 @@ +/* + * Generic support for sparse keymaps + * + * Copyright (c) 2009 Dmitry Torokhov + * + * Derived from wistron button driver: + * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> + * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> + * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> + * + * 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/input.h> +#include <linux/input/sparse-keymap.h> + +MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); +MODULE_DESCRIPTION("Generic support for sparse keymaps"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); + +/** + * sparse_keymap_entry_from_scancode - perform sparse keymap lookup + * @dev: Input device using sparse keymap + * @code: Scan code + * + * This function is used to perform &struct key_entry lookup in an + * input device using sparse keymap. + */ +struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev, + unsigned int code) +{ + struct key_entry *key; + + for (key = dev->keycode; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} +EXPORT_SYMBOL(sparse_keymap_entry_from_scancode); + +/** + * sparse_keymap_entry_from_keycode - perform sparse keymap lookup + * @dev: Input device using sparse keymap + * @keycode: Key code + * + * This function is used to perform &struct key_entry lookup in an + * input device using sparse keymap. + */ +struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev, + unsigned int keycode) +{ + struct key_entry *key; + + for (key = dev->keycode; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} +EXPORT_SYMBOL(sparse_keymap_entry_from_keycode); + +static int sparse_keymap_getkeycode(struct input_dev *dev, + int scancode, int *keycode) +{ + const struct key_entry *key = + sparse_keymap_entry_from_scancode(dev, scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int sparse_keymap_setkeycode(struct input_dev *dev, + int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = sparse_keymap_entry_from_scancode(dev, scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!sparse_keymap_entry_from_keycode(dev, old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + +/** + * sparse_keymap_setup - set up sparse keymap for an input device + * @dev: Input device + * @keymap: Keymap in form of array of &key_entry structures ending + * with %KE_END type entry + * @setup: Function that can be used to adjust keymap entries + * depending on device's deeds, may be %NULL + * + * The function calculates size and allocates copy of the original + * keymap after which sets up input device event bits appropriately. + * Before destroying input device allocated keymap should be freed + * with a call to sparse_keymap_free(). + */ +int sparse_keymap_setup(struct input_dev *dev, + const struct key_entry *keymap, + int (*setup)(struct input_dev *, struct key_entry *)) +{ + size_t map_size = 1; /* to account for the last KE_END entry */ + const struct key_entry *e; + struct key_entry *map, *entry; + int i; + int error; + + for (e = keymap; e->type != KE_END; e++) + map_size++; + + map = kcalloc(map_size, sizeof (struct key_entry), GFP_KERNEL); + if (!map) + return -ENOMEM; + + memcpy(map, keymap, map_size * sizeof (struct key_entry)); + + for (i = 0; i < map_size; i++) { + entry = &map[i]; + + if (setup) { + error = setup(dev, entry); + if (error) + goto err_out; + } + + switch (entry->type) { + case KE_KEY: + __set_bit(EV_KEY, dev->evbit); + __set_bit(entry->keycode, dev->keybit); + break; + + case KE_SW: + __set_bit(EV_SW, dev->evbit); + __set_bit(entry->sw.code, dev->swbit); + break; + } + } + + dev->keycode = map; + dev->keycodemax = map_size; + dev->getkeycode = sparse_keymap_getkeycode; + dev->setkeycode = sparse_keymap_setkeycode; + + return 0; + + err_out: + kfree(keymap); + return error; + +} +EXPORT_SYMBOL(sparse_keymap_setup); + +/** + * sparse_keymap_free - free memory allocated for sparse keymap + * @dev: Input device using sparse keymap + * + * This function is used to free memory allocated by sparse keymap + * in an input device that was set up by sparse_keymap_setup(). + */ +void sparse_keymap_free(struct input_dev *dev) +{ + kfree(dev->keycode); + dev->keycode = NULL; + dev->keycodemax = 0; + dev->getkeycode = NULL; + dev->setkeycode = NULL; +} +EXPORT_SYMBOL(sparse_keymap_free); + +/** + * sparse_keymap_report_entry - report event corresponding to given key entry + * @dev: Input device for which event should be reported + * @ke: key entry describing event + * @value: Value that should be reported (ignored by %KE_SW entries) + * @autorelease: Signals whether release event should be emitted for %KE_KEY + * entries right after reporting press event, ignored by all other + * entries + * + * This function is used to report input event described by given + * &struct key_entry. + */ +void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke, + unsigned int value, bool autorelease) +{ + switch (ke->type) { + case KE_KEY: + input_report_key(dev, ke->keycode, value); + input_sync(dev); + if (value && autorelease) { + input_report_key(dev, ke->keycode, 0); + input_sync(dev); + } + break; + + case KE_SW: + value = ke->sw.value; + /* fall through */ + + case KE_VSW: + input_report_switch(dev, ke->sw.code, value); + break; + } +} +EXPORT_SYMBOL(sparse_keymap_report_entry); + +/** + * sparse_keymap_report_event - report event corresponding to given scancode + * @dev: Input device using sparse keymap + * @code: Scan code + * @value: Value that should be reported (ignored by %KE_SW entries) + * @autorelease: Signals whether release event should be emitted for %KE_KEY + * entries right after reporting press event, ignored by all other + * entries + * + * This function is used to perform lookup in an input device using sparse + * keymap and report corresponding event. Returns %true if lookup was + * successful and %false otherwise. + */ +bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code, + unsigned int value, bool autorelease) +{ + const struct key_entry *ke = + sparse_keymap_entry_from_scancode(dev, code); + + if (ke) { + sparse_keymap_report_entry(dev, ke, value, autorelease); + return true; + } + + return false; +} +EXPORT_SYMBOL(sparse_keymap_report_event); + diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 8cc453c85ea7..32fc8ba039aa 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -111,6 +111,18 @@ config TOUCHSCREEN_DA9034 Say Y here to enable the support for the touchscreen found on Dialog Semiconductor DA9034 PMIC. +config TOUCHSCREEN_DYNAPRO + tristate "Dynapro serial touchscreen" + select SERIO + help + Say Y here if you have a Dynapro serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called dynapro. + config TOUCHSCREEN_EETI tristate "EETI touchscreen panel support" depends on I2C @@ -133,6 +145,18 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_S3C2410 + tristate "Samsung S3C2410 touchscreen input driver" + depends on ARCH_S3C2410 + select S3C24XX_ADC + help + Say Y here if you have the s3c2410 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s3c2410_ts. + config TOUCHSCREEN_GUNZE tristate "Gunze AHL-51S touchscreen" select SERIO @@ -297,7 +321,7 @@ config TOUCHSCREEN_TOUCHWIN config TOUCHSCREEN_ATMEL_TSADCC tristate "Atmel Touchscreen Interface" - depends on ARCH_AT91SAM9RL + depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 help Say Y here if you have a 4-wire touchscreen connected to the ADC Controller on your Atmel SoC (such as the AT91SAM9RL). @@ -418,6 +442,7 @@ config TOUCHSCREEN_USB_COMPOSITE - IdealTEK URTC1000 - GoTop Super_Q2/GogoPen/PenPower tablets - JASTEC USB Touch Controller/DigiTech DTR-02U + - Zytronic controllers Have a look at <http://linux.chapter7.ch/touchkit/> for a usage description and the required user-space stuff. @@ -490,6 +515,16 @@ config TOUCHSCREEN_USB_E2I bool "e2i Touchscreen controller (e.g. from Mimo 740)" depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_USB_ZYTRONIC + default y + bool "Zytronic controller" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ETT_TC5UH + default y + bool "ET&T TC5UH touchscreen controler support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + config TOUCHSCREEN_TOUCHIT213 tristate "Sahara TouchIT-213 touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 15fa62cffc77..f1f59c9e1211 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o @@ -25,7 +26,9 @@ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o +obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o @@ -41,4 +44,3 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o -obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 09c810999b92..52d2ca147d8f 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -29,10 +29,9 @@ #include <linux/spi/ads7846.h> #include <asm/irq.h> - /* * This code has been heavily tested on a Nokia 770, and lightly - * tested on other ads7846 devices (OSK/Mistral, Lubbock). + * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz). * TSC2046 is just newer ads7846 silicon. * Support for ads7843 tested on Atmel at91sam926x-EK. * Support for ads7845 has only been stubbed in. @@ -43,7 +42,7 @@ * have to maintain our own SW IRQ disabled status. This should be * removed as soon as the affected platform's IRQ handling is fixed. * - * app note sbaa036 talks in more detail about accurate sampling... + * App note sbaa036 talks in more detail about accurate sampling... * that ought to help in situations like LCDs inducing noise (which * can also be helped by using synch signals) and more generally. * This driver tries to utilize the measures described in the app @@ -566,10 +565,8 @@ static void ads7846_rx(void *ads) * once more the measurement */ if (packet->tc.ignore || Rt > ts->pressure_max) { -#ifdef VERBOSE - pr_debug("%s: ignored %d pressure %d\n", - dev_name(&ts->spi->dev), packet->tc.ignore, Rt); -#endif + dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", + packet->tc.ignore, Rt); hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_MODE_REL); return; @@ -598,9 +595,7 @@ static void ads7846_rx(void *ads) if (!ts->pendown) { input_report_key(input, BTN_TOUCH, 1); ts->pendown = 1; -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "DOWN\n"); -#endif + dev_vdbg(&ts->spi->dev, "DOWN\n"); } if (ts->swap_xy) @@ -608,12 +603,10 @@ static void ads7846_rx(void *ads) input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_Y, y); - input_report_abs(input, ABS_PRESSURE, Rt); + input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt); input_sync(input); -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); -#endif + dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); } hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), @@ -723,9 +716,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) input_sync(input); ts->pendown = 0; -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "UP\n"); -#endif + dev_vdbg(&ts->spi->dev, "UP\n"); } /* measurement cycle ended */ diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index 35377f583e28..a12242f77e23 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -59,7 +59,7 @@ #define ATMEL_WM97XX_AC97C_IRQ (29) #define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */ #else -#error Unkown CPU, this driver only supports AT32AP700X CPUs. +#error Unknown CPU, this driver only supports AT32AP700X CPUs. #endif struct continuous { diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index 9c7fce4d74d0..3d9b5166ebe9 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -22,6 +22,8 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <mach/board.h> +#include <mach/cpu.h> /* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */ @@ -36,7 +38,9 @@ #define ATMEL_TSADCC_LOWRES (1 << 4) /* Resolution selection */ #define ATMEL_TSADCC_SLEEP (1 << 5) /* Sleep mode */ #define ATMEL_TSADCC_PENDET (1 << 6) /* Pen Detect selection */ +#define ATMEL_TSADCC_PRES (1 << 7) /* Pressure Measurement Selection */ #define ATMEL_TSADCC_PRESCAL (0x3f << 8) /* Prescalar Rate Selection */ +#define ATMEL_TSADCC_EPRESCAL (0xff << 8) /* Prescalar Rate Selection (Extended) */ #define ATMEL_TSADCC_STARTUP (0x7f << 16) /* Start Up time */ #define ATMEL_TSADCC_SHTIM (0xf << 24) /* Sample & Hold time */ #define ATMEL_TSADCC_PENDBC (0xf << 28) /* Pen Detect debouncing time */ @@ -84,7 +88,13 @@ #define ATMEL_TSADCC_CDR4 0x40 /* Channel Data 4 */ #define ATMEL_TSADCC_CDR5 0x44 /* Channel Data 5 */ -#define ADC_CLOCK 1000000 +#define ATMEL_TSADCC_XPOS 0x50 +#define ATMEL_TSADCC_Z1DAT 0x54 +#define ATMEL_TSADCC_Z2DAT 0x58 + +#define PRESCALER_VAL(x) ((x) >> 8) + +#define ADC_DEFAULT_CLOCK 100000 struct atmel_tsadcc { struct input_dev *input; @@ -172,6 +182,7 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) struct atmel_tsadcc *ts_dev; struct input_dev *input_dev; struct resource *res; + struct at91_tsadcc_data *pdata = pdev->dev.platform_data; int err = 0; unsigned int prsc; unsigned int reg; @@ -242,31 +253,49 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) input_dev->phys = ts_dev->phys; input_dev->dev.parent = &pdev->dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - + __set_bit(EV_ABS, input_dev->evbit); input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + /* clk_enable() always returns 0, no need to check it */ clk_enable(ts_dev->clk); prsc = clk_get_rate(ts_dev->clk); dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc); - prsc = prsc / ADC_CLOCK / 2 - 1; + if (!pdata) + goto err_fail; + + if (!pdata->adc_clock) + pdata->adc_clock = ADC_DEFAULT_CLOCK; + + prsc = (prsc / (2 * pdata->adc_clock)) - 1; + + /* saturate if this value is too high */ + if (cpu_is_at91sam9rl()) { + if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL)) + prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL); + } else { + if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL)) + prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL); + } + + dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc); reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE | ((0x00 << 5) & ATMEL_TSADCC_SLEEP) | /* Normal Mode */ ((0x01 << 6) & ATMEL_TSADCC_PENDET) | /* Enable Pen Detect */ - ((prsc << 8) & ATMEL_TSADCC_PRESCAL) | /* PRESCAL */ - ((0x13 << 16) & ATMEL_TSADCC_STARTUP) | /* STARTUP */ - ((0x0F << 28) & ATMEL_TSADCC_PENDBC); /* PENDBC */ + (prsc << 8) | + ((0x26 << 16) & ATMEL_TSADCC_STARTUP) | + ((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC); atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST); atmel_tsadcc_write(ATMEL_TSADCC_MR, reg); atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE); - atmel_tsadcc_write(ATMEL_TSADCC_TSR, (0x3 << 24) & ATMEL_TSADCC_TSSHTIM); + atmel_tsadcc_write(ATMEL_TSADCC_TSR, + (pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM); atmel_tsadcc_read(ATMEL_TSADCC_SR); atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT); diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c new file mode 100644 index 000000000000..455353908bdf --- /dev/null +++ b/drivers/input/touchscreen/dynapro.c @@ -0,0 +1,206 @@ +/* + * Dynapro serial touchscreen driver + * + * Copyright (c) 2009 Tias Guns + * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and + * Richard Lemon + * + */ + +/* + * 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. + */ + +/* + * 2009/09/19 Tias Guns <tias@ulyssis.org> + * Copied inexio.c and edited for Dynapro protocol (from retired Xorg module) + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +#define DRIVER_DESC "Dynapro serial touchscreen driver" + +MODULE_AUTHOR("Tias Guns <tias@ulyssis.org>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define DYNAPRO_FORMAT_TOUCH_BIT 0x40 +#define DYNAPRO_FORMAT_LENGTH 3 +#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80 + +#define DYNAPRO_MIN_XC 0 +#define DYNAPRO_MAX_XC 0x3ff +#define DYNAPRO_MIN_YC 0 +#define DYNAPRO_MAX_YC 0x3ff + +#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4)) +#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7)) +#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct dynapro { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[DYNAPRO_FORMAT_LENGTH]; + char phys[32]; +}; + +static void dynapro_process_data(struct dynapro *pdynapro) +{ + struct input_dev *dev = pdynapro->dev; + + if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) { + input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data)); + input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data)); + input_report_key(dev, BTN_TOUCH, + DYNAPRO_GET_TOUCHED(pdynapro->data)); + input_sync(dev); + + pdynapro->idx = 0; + } +} + +static irqreturn_t dynapro_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct dynapro *pdynapro = serio_get_drvdata(serio); + + pdynapro->data[pdynapro->idx] = data; + + if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0]) + dynapro_process_data(pdynapro); + else + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + pdynapro->data[0]); + + return IRQ_HANDLED; +} + +static void dynapro_disconnect(struct serio *serio) +{ + struct dynapro *pdynapro = serio_get_drvdata(serio); + + input_get_device(pdynapro->dev); + input_unregister_device(pdynapro->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(pdynapro->dev); + kfree(pdynapro); +} + +/* + * dynapro_connect() is the routine that is called when someone adds a + * new serio device that supports dynapro protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ + +static int dynapro_connect(struct serio *serio, struct serio_driver *drv) +{ + struct dynapro *pdynapro; + struct input_dev *input_dev; + int err; + + pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pdynapro || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pdynapro->serio = serio; + pdynapro->dev = input_dev; + snprintf(pdynapro->phys, sizeof(pdynapro->phys), + "%s/input0", serio->phys); + + input_dev->name = "Dynapro Serial TouchScreen"; + input_dev->phys = pdynapro->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_DYNAPRO; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(pdynapro->dev, ABS_X, + DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0); + input_set_abs_params(pdynapro->dev, ABS_Y, + DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0); + + serio_set_drvdata(serio, pdynapro); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pdynapro->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pdynapro); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id dynapro_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_DYNAPRO, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, dynapro_serio_ids); + +static struct serio_driver dynapro_drv = { + .driver = { + .name = "dynapro", + }, + .description = DRIVER_DESC, + .id_table = dynapro_serio_ids, + .interrupt = dynapro_interrupt, + .connect = dynapro_connect, + .disconnect = dynapro_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init dynapro_init(void) +{ + return serio_register_driver(&dynapro_drv); +} + +static void __exit dynapro_exit(void) +{ + serio_unregister_driver(&dynapro_drv); +} + +module_init(dynapro_init); +module_exit(dynapro_exit); diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index 8fc3b08deb3b..6cdcf2a6e036 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -14,7 +14,7 @@ * * Notes: * This is a wm97xx extended touch driver to capture touch - * data in a continuous manner on the Intel XScale archictecture + * data in a continuous manner on the Intel XScale architecture * * Features: * - codecs supported:- WM9705, WM9712, WM9713 @@ -131,7 +131,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm) /* When the AC97 queue has been drained we need to allow time * to buffer up samples otherwise we end up spinning polling * for samples. The controller can't have a suitably low - * threashold set to use the notifications it gives. + * threshold set to use the notifications it gives. */ schedule_timeout_uninterruptible(1); diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c new file mode 100644 index 000000000000..6386b441ef85 --- /dev/null +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -0,0 +1,457 @@ +/* + * Samsung S3C24XX touchscreen driver + * + * This program is free software; you can redistribute it and/or modify + * it under the term of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org> + * Copyright 2008 Ben Dooks <ben-linux@fluff.org> + * Copyright 2009 Simtec Electronics <linux@simtec.co.uk> + * + * Additional work by Herbert Pötzl <herbert@13thfloor.at> and + * Harald Welte <laforge@openmoko.org> + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <plat/adc.h> +#include <plat/regs-adc.h> + +#include <mach/regs-gpio.h> +#include <mach/ts.h> + +#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0)) + +#define INT_DOWN (0) +#define INT_UP (1 << 8) + +#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \ + S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | \ + S3C2410_ADCTSC_XY_PST(3)) + +#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \ + S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | \ + S3C2410_ADCTSC_AUTO_PST | \ + S3C2410_ADCTSC_XY_PST(0)) + +/* Per-touchscreen data. */ + +/** + * struct s3c2410ts - driver touchscreen state. + * @client: The ADC client we registered with the core driver. + * @dev: The device we are bound to. + * @input: The input device we registered with the input subsystem. + * @clock: The clock for the adc. + * @io: Pointer to the IO base. + * @xp: The accumulated X position data. + * @yp: The accumulated Y position data. + * @irq_tc: The interrupt number for pen up/down interrupt + * @count: The number of samples collected. + * @shift: The log2 of the maximum count to read in one go. + */ +struct s3c2410ts { + struct s3c_adc_client *client; + struct device *dev; + struct input_dev *input; + struct clk *clock; + void __iomem *io; + unsigned long xp; + unsigned long yp; + int irq_tc; + int count; + int shift; +}; + +static struct s3c2410ts ts; + +/** + * s3c2410_ts_connect - configure gpio for s3c2410 systems + * + * Configure the GPIO for the S3C2410 system, where we have external FETs + * connected to the device (later systems such as the S3C2440 integrate + * these into the device). +*/ +static inline void s3c2410_ts_connect(void) +{ + s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON); + s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON); + s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON); + s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON); +} + +/** + * get_down - return the down state of the pen + * @data0: The data read from ADCDAT0 register. + * @data1: The data read from ADCDAT1 register. + * + * Return non-zero if both readings show that the pen is down. + */ +static inline bool get_down(unsigned long data0, unsigned long data1) +{ + /* returns true if both data values show stylus down */ + return (!(data0 & S3C2410_ADCDAT0_UPDOWN) && + !(data1 & S3C2410_ADCDAT0_UPDOWN)); +} + +static void touch_timer_fire(unsigned long data) +{ + unsigned long data0; + unsigned long data1; + bool down; + + data0 = readl(ts.io + S3C2410_ADCDAT0); + data1 = readl(ts.io + S3C2410_ADCDAT1); + + down = get_down(data0, data1); + + if (ts.count == (1 << ts.shift)) { + ts.xp >>= ts.shift; + ts.yp >>= ts.shift; + + dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n", + __func__, ts.xp, ts.yp, ts.count); + + input_report_abs(ts.input, ABS_X, ts.xp); + input_report_abs(ts.input, ABS_Y, ts.yp); + + input_report_key(ts.input, BTN_TOUCH, 1); + input_sync(ts.input); + + ts.xp = 0; + ts.yp = 0; + ts.count = 0; + } + + if (down) { + s3c_adc_start(ts.client, 0, 1 << ts.shift); + } else { + ts.count = 0; + + input_report_key(ts.input, BTN_TOUCH, 0); + input_sync(ts.input); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + } +} + +static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0); + +/** + * stylus_irq - touchscreen stylus event interrupt + * @irq: The interrupt number + * @dev_id: The device ID. + * + * Called when the IRQ_TC is fired for a pen up or down event. + */ +static irqreturn_t stylus_irq(int irq, void *dev_id) +{ + unsigned long data0; + unsigned long data1; + bool down; + + data0 = readl(ts.io + S3C2410_ADCDAT0); + data1 = readl(ts.io + S3C2410_ADCDAT1); + + down = get_down(data0, data1); + + /* TODO we should never get an interrupt with down set while + * the timer is running, but maybe we ought to verify that the + * timer isn't running anyways. */ + + if (down) + s3c_adc_start(ts.client, 0, 1 << ts.shift); + else + dev_info(ts.dev, "%s: count=%d\n", __func__, ts.count); + + return IRQ_HANDLED; +} + +/** + * s3c24xx_ts_conversion - ADC conversion callback + * @client: The client that was registered with the ADC core. + * @data0: The reading from ADCDAT0. + * @data1: The reading from ADCDAT1. + * @left: The number of samples left. + * + * Called when a conversion has finished. + */ +static void s3c24xx_ts_conversion(struct s3c_adc_client *client, + unsigned data0, unsigned data1, + unsigned *left) +{ + dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1); + + ts.xp += data0; + ts.yp += data1; + + ts.count++; + + /* From tests, it seems that it is unlikely to get a pen-up + * event during the conversion process which means we can + * ignore any pen-up events with less than the requisite + * count done. + * + * In several thousand conversions, no pen-ups where detected + * before count completed. + */ +} + +/** + * s3c24xx_ts_select - ADC selection callback. + * @client: The client that was registered with the ADC core. + * @select: The reason for select. + * + * Called when the ADC core selects (or deslects) us as a client. + */ +static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select) +{ + if (select) { + writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, + ts.io + S3C2410_ADCTSC); + } else { + mod_timer(&touch_timer, jiffies+1); + writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC); + } +} + +/** + * s3c2410ts_probe - device core probe entry point + * @pdev: The device we are being bound to. + * + * Initialise, find and allocate any resources we need to run and then + * register with the ADC and input systems. + */ +static int __devinit s3c2410ts_probe(struct platform_device *pdev) +{ + struct s3c2410_ts_mach_info *info; + struct device *dev = &pdev->dev; + struct input_dev *input_dev; + struct resource *res; + int ret = -EINVAL; + + /* Initialise input stuff */ + memset(&ts, 0, sizeof(struct s3c2410ts)); + + ts.dev = dev; + + info = pdev->dev.platform_data; + if (!info) { + dev_err(dev, "no platform data, cannot attach\n"); + return -EINVAL; + } + + dev_dbg(dev, "initialising touchscreen\n"); + + ts.clock = clk_get(dev, "adc"); + if (IS_ERR(ts.clock)) { + dev_err(dev, "cannot get adc clock source\n"); + return -ENOENT; + } + + clk_enable(ts.clock); + dev_dbg(dev, "got and enabled clocks\n"); + + ts.irq_tc = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "no resource for interrupt\n"); + goto err_clk; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no resource for registers\n"); + ret = -ENOENT; + goto err_clk; + } + + ts.io = ioremap(res->start, resource_size(res)); + if (ts.io == NULL) { + dev_err(dev, "cannot map registers\n"); + ret = -ENOMEM; + goto err_clk; + } + + /* Configure the touchscreen external FETs on the S3C2410 */ + if (!platform_get_device_id(pdev)->driver_data) + s3c2410_ts_connect(); + + ts.client = s3c_adc_register(pdev, s3c24xx_ts_select, + s3c24xx_ts_conversion, 1); + if (IS_ERR(ts.client)) { + dev_err(dev, "failed to register adc client\n"); + ret = PTR_ERR(ts.client); + goto err_iomap; + } + + /* Initialise registers */ + if ((info->delay & 0xffff) > 0) + writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Unable to allocate the input device !!\n"); + ret = -ENOMEM; + goto err_iomap; + } + + ts.input = input_dev; + ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0); + input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0); + + ts.input->name = "S3C24XX TouchScreen"; + ts.input->id.bustype = BUS_HOST; + ts.input->id.vendor = 0xDEAD; + ts.input->id.product = 0xBEEF; + ts.input->id.version = 0x0102; + + ts.shift = info->oversampling_shift; + + ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED, + "s3c2410_ts_pen", ts.input); + if (ret) { + dev_err(dev, "cannot get TC interrupt\n"); + goto err_inputdev; + } + + dev_info(dev, "driver attached, registering input device\n"); + + /* All went ok, so register to the input system */ + ret = input_register_device(ts.input); + if (ret < 0) { + dev_err(dev, "failed to register input device\n"); + ret = -EIO; + goto err_tcirq; + } + + return 0; + + err_tcirq: + free_irq(ts.irq_tc, ts.input); + err_inputdev: + input_unregister_device(ts.input); + err_iomap: + iounmap(ts.io); + err_clk: + del_timer_sync(&touch_timer); + clk_put(ts.clock); + return ret; +} + +/** + * s3c2410ts_remove - device core removal entry point + * @pdev: The device we are being removed from. + * + * Free up our state ready to be removed. + */ +static int __devexit s3c2410ts_remove(struct platform_device *pdev) +{ + free_irq(ts.irq_tc, ts.input); + del_timer_sync(&touch_timer); + + clk_disable(ts.clock); + clk_put(ts.clock); + + input_unregister_device(ts.input); + iounmap(ts.io); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c2410ts_suspend(struct device *dev) +{ + writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC); + disable_irq(ts.irq_tc); + clk_disable(ts.clock); + + return 0; +} + +static int s3c2410ts_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c2410_ts_mach_info *info = pdev->dev.platform_data; + + clk_enable(ts.clock); + + /* Initialise registers */ + if ((info->delay & 0xffff) > 0) + writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + + return 0; +} + +static struct dev_pm_ops s3c_ts_pmops = { + .suspend = s3c2410ts_suspend, + .resume = s3c2410ts_resume, +}; +#endif + +static struct platform_device_id s3cts_driver_ids[] = { + { "s3c2410-ts", 0 }, + { "s3c2440-ts", 1 }, + { } +}; +MODULE_DEVICE_TABLE(platform, s3cts_driver_ids); + +static struct platform_driver s3c_ts_driver = { + .driver = { + .name = "s3c24xx-ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &s3c_ts_pmops, +#endif + }, + .id_table = s3cts_driver_ids, + .probe = s3c2410ts_probe, + .remove = __devexit_p(s3c2410ts_remove), +}; + +static int __init s3c2410ts_init(void) +{ + return platform_driver_register(&s3c_ts_driver); +} + +static void __exit s3c2410ts_exit(void) +{ + platform_driver_unregister(&s3c_ts_driver); +} + +module_init(s3c2410ts_init); +module_exit(s3c2410ts_exit); + +MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, " + "Ben Dooks <ben@simtec.co.uk>, " + "Simtec Electronics <linux@simtec.co.uk>"); +MODULE_DESCRIPTION("S3C24XX Touchscreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 095f84b1f56e..89dcbe7b4b02 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -355,10 +355,13 @@ static int ucb1400_ts_probe(struct platform_device *dev) goto err; } - error = ucb1400_ts_detect_irq(ucb); - if (error) { - printk(KERN_ERR "UCB1400: IRQ probe failed\n"); - goto err_free_devs; + /* Only in case the IRQ line wasn't supplied, try detecting it */ + if (ucb->irq < 0) { + error = ucb1400_ts_detect_irq(ucb); + if (error) { + printk(KERN_ERR "UCB1400: IRQ probe failed\n"); + goto err_free_devs; + } } init_waitqueue_head(&ucb->ts_wait); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 68ece5801a58..09a5e7341bd5 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -14,6 +14,7 @@ * - General Touch * - GoTop Super_Q2/GogoPen/PenPower tablets * - JASTEC USB touch controller/DigiTech DTR-02U + * - Zytronic capacitive touchscreen * * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) @@ -73,6 +74,15 @@ struct usbtouch_device_info { int min_press, max_press; int rept_size; + /* + * Always service the USB devices irq not just when the input device is + * open. This is useful when devices have a watchdog which prevents us + * from periodically polling the device. Leave this unset unless your + * touchscreen device requires it, as it does consume more of the USB + * bandwidth. + */ + bool irq_always; + void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len); /* @@ -121,6 +131,8 @@ enum { DEVTYPE_GOTOP, DEVTYPE_JASTEC, DEVTYPE_E2I, + DEVTYPE_ZYTRONIC, + DEVTYPE_TC5UH, }; #define USB_DEVICE_HID_CLASS(vend, prod) \ @@ -201,6 +213,15 @@ static struct usb_device_id usbtouch_devices[] = { #ifdef CONFIG_TOUCHSCREEN_USB_E2I {USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I}, #endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC + {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH + {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH}, +#endif + {} }; @@ -538,6 +559,19 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) } #endif +/***************************************************************************** + * ET&T TC5UH part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH +static int tc5uh_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1]; + dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3]; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif /***************************************************************************** * IdealTEK URTC1000 Part @@ -621,6 +655,39 @@ static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt) } #endif +/***************************************************************************** + * Zytronic Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC +static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + switch (pkt[0]) { + case 0x3A: /* command response */ + dbg("%s: Command response %d", __func__, pkt[1]); + break; + + case 0xC0: /* down */ + dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7); + dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7); + dev->touch = 1; + dbg("%s: down %d,%d", __func__, dev->x, dev->y); + return 1; + + case 0x80: /* up */ + dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7); + dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7); + dev->touch = 0; + dbg("%s: up %d,%d", __func__, dev->x, dev->y); + return 1; + + default: + dbg("%s: Unknown return %d", __func__, pkt[0]); + break; + } + + return 0; +} +#endif /***************************************************************************** * the different device descriptors @@ -783,6 +850,29 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .read_data = e2i_read_data, }, #endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC + [DEVTYPE_ZYTRONIC] = { + .min_xc = 0x0, + .max_xc = 0x03ff, + .min_yc = 0x0, + .max_yc = 0x03ff, + .rept_size = 5, + .read_data = zytronic_read_data, + .irq_always = true, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH + [DEVTYPE_TC5UH] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 5, + .read_data = tc5uh_read_data, + }, +#endif }; @@ -933,8 +1023,10 @@ static int usbtouch_open(struct input_dev *input) usbtouch->irq->dev = usbtouch->udev; - if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) - return -EIO; + if (!usbtouch->type->irq_always) { + if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) + return -EIO; + } return 0; } @@ -943,7 +1035,8 @@ static void usbtouch_close(struct input_dev *input) { struct usbtouch_usb *usbtouch = input_get_drvdata(input); - usb_kill_urb(usbtouch->irq); + if (!usbtouch->type->irq_always) + usb_kill_urb(usbtouch->irq); } @@ -1066,6 +1159,9 @@ static int usbtouch_probe(struct usb_interface *intf, usb_set_intfdata(intf, usbtouch); + if (usbtouch->type->irq_always) + usb_submit_urb(usbtouch->irq, GFP_KERNEL); + return 0; out_free_buffers: @@ -1087,7 +1183,7 @@ static void usbtouch_disconnect(struct usb_interface *intf) dbg("%s - usbtouch is initialized, cleaning up", __func__); usb_set_intfdata(intf, NULL); - usb_kill_urb(usbtouch->irq); + /* this will stop IO via close */ input_unregister_device(usbtouch->input); usb_free_urb(usbtouch->irq); usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch); diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c index 41e4359c277c..eca54dbdf493 100644 --- a/drivers/input/touchscreen/zylonite-wm97xx.c +++ b/drivers/input/touchscreen/zylonite-wm97xx.c @@ -96,7 +96,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm) /* When the AC97 queue has been drained we need to allow time * to buffer up samples otherwise we end up spinning polling * for samples. The controller can't have a suitably low - * threashold set to use the notifications it gives. + * threshold set to use the notifications it gives. */ msleep(1); diff --git a/drivers/isdn/act2000/Kconfig b/drivers/isdn/act2000/Kconfig index 3fc1a5434ef7..fa2673fc69c2 100644 --- a/drivers/isdn/act2000/Kconfig +++ b/drivers/isdn/act2000/Kconfig @@ -1,6 +1,3 @@ -# -# Config.in for IBM Active 2000 ISDN driver -# config ISDN_DRV_ACT2000 tristate "IBM Active 2000 support" depends on ISA @@ -10,4 +7,3 @@ config ISDN_DRV_ACT2000 into the card using a utility which is part of the latest isdn4k-utils package. Please read the file <file:Documentation/isdn/README.act2000> for more information. - diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig index e1afd60924fb..b2a04755c96a 100644 --- a/drivers/isdn/capi/Kconfig +++ b/drivers/isdn/capi/Kconfig @@ -1,6 +1,3 @@ -# -# Config.in for the CAPI subsystem -# config ISDN_DRV_AVMB1_VERBOSE_REASON bool "Verbose reason code reporting" default y @@ -59,4 +56,3 @@ config ISDN_CAPI_CAPIDRV the legacy isdn4linux link layer. If you have a card which is supported by a CAPI driver, but still want to use old features like ippp interfaces or ttyI emulation, say Y/M here. - diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c index 3e6d17f42a98..66b7d7a86474 100644 --- a/drivers/isdn/capi/capidrv.c +++ b/drivers/isdn/capi/capidrv.c @@ -830,7 +830,7 @@ static void handle_controller(_cmsg * cmsg) case 0: break; case 1: s = "unknown class"; break; case 2: s = "unknown function"; break; - default: s = "unkown error"; break; + default: s = "unknown error"; break; } if (s) printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n", diff --git a/drivers/isdn/gigaset/Kconfig b/drivers/isdn/gigaset/Kconfig index 18ab8652aa57..dcefedc7044a 100644 --- a/drivers/isdn/gigaset/Kconfig +++ b/drivers/isdn/gigaset/Kconfig @@ -1,6 +1,5 @@ menuconfig ISDN_DRV_GIGASET tristate "Siemens Gigaset support" - depends on ISDN_I4L select CRC_CCITT select BITREVERSE help @@ -11,9 +10,33 @@ menuconfig ISDN_DRV_GIGASET If you have one of these devices, say M here and for at least one of the connection specific parts that follow. This will build a module called "gigaset". + Note: If you build your ISDN subsystem (ISDN_CAPI or ISDN_I4L) + as a module, you have to build this driver as a module too, + otherwise the Gigaset device won't show up as an ISDN device. if ISDN_DRV_GIGASET +config GIGASET_CAPI + bool "Gigaset CAPI support (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on ISDN_CAPI='y'||(ISDN_CAPI='m'&&ISDN_DRV_GIGASET='m') + default ISDN_I4L='n' + help + Build the Gigaset driver as a CAPI 2.0 driver interfacing with + the Kernel CAPI subsystem. To use it with the old ISDN4Linux + subsystem you'll have to enable the capidrv glue driver. + (select ISDN_CAPI_CAPIDRV.) + Say N to build the old native ISDN4Linux variant. + +config GIGASET_I4L + bool + depends on ISDN_I4L='y'||(ISDN_I4L='m'&&ISDN_DRV_GIGASET='m') + default !GIGASET_CAPI + +config GIGASET_DUMMYLL + bool + default !GIGASET_CAPI&&!GIGASET_I4L + config GIGASET_BASE tristate "Gigaset base station support" depends on USB diff --git a/drivers/isdn/gigaset/Makefile b/drivers/isdn/gigaset/Makefile index e9d3189f56b7..c453b72272a0 100644 --- a/drivers/isdn/gigaset/Makefile +++ b/drivers/isdn/gigaset/Makefile @@ -1,4 +1,7 @@ -gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o asyncdata.o +gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o +gigaset-$(CONFIG_GIGASET_CAPI) += capi.o +gigaset-$(CONFIG_GIGASET_I4L) += i4l.o +gigaset-$(CONFIG_GIGASET_DUMMYLL) += dummyll.o usb_gigaset-y := usb-gigaset.o ser_gigaset-y := ser-gigaset.o bas_gigaset-y := bas-gigaset.o isocdata.o diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c index 44a58e6f8f65..ccb2a7b7c41d 100644 --- a/drivers/isdn/gigaset/asyncdata.c +++ b/drivers/isdn/gigaset/asyncdata.c @@ -19,7 +19,7 @@ /* check if byte must be stuffed/escaped * I'm not sure which data should be encoded. - * Therefore I will go the hard way and decode every value + * Therefore I will go the hard way and encode every value * less than 0x20, the flag sequence and the control escape char. */ static inline int muststuff(unsigned char c) @@ -35,303 +35,383 @@ static inline int muststuff(unsigned char c) /* == data input =========================================================== */ -/* process a block of received bytes in command mode (modem response) +/* process a block of received bytes in command mode + * (mstate != MS_LOCKED && (inputstate & INS_command)) + * Append received bytes to the command response buffer and forward them + * line by line to the response handler. Exit whenever a mode/state change + * might have occurred. * Return value: * number of processed bytes */ -static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes, - struct inbuf_t *inbuf) +static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf) { + unsigned char *src = inbuf->data + inbuf->head; struct cardstate *cs = inbuf->cs; - unsigned cbytes = cs->cbytes; - int inputstate = inbuf->inputstate; - int startbytes = numbytes; - - for (;;) { - cs->respdata[cbytes] = c; - if (c == 10 || c == 13) { - gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)", + unsigned cbytes = cs->cbytes; + unsigned procbytes = 0; + unsigned char c; + + while (procbytes < numbytes) { + c = *src++; + procbytes++; + + switch (c) { + case '\n': + if (cbytes == 0 && cs->respdata[0] == '\r') { + /* collapse LF with preceding CR */ + cs->respdata[0] = 0; + break; + } + /* --v-- fall through --v-- */ + case '\r': + /* end of message line, pass to response handler */ + gig_dbg(DEBUG_TRANSCMD, "%s: End of Message (%d Bytes)", __func__, cbytes); + if (cbytes >= MAX_RESP_SIZE) { + dev_warn(cs->dev, "response too large (%d)\n", + cbytes); + cbytes = MAX_RESP_SIZE; + } cs->cbytes = cbytes; - gigaset_handle_modem_response(cs); /* can change - cs->dle */ + gigaset_handle_modem_response(cs); cbytes = 0; - if (cs->dle && - !(inputstate & INS_DLE_command)) { - inputstate &= ~INS_command; - break; - } - } else { - /* advance in line buffer, checking for overflow */ - if (cbytes < MAX_RESP_SIZE - 1) - cbytes++; - else - dev_warn(cs->dev, "response too large\n"); - } + /* store EOL byte for CRLF collapsing */ + cs->respdata[0] = c; - if (!numbytes) - break; - c = *src++; - --numbytes; - if (c == DLE_FLAG && - (cs->dle || inputstate & INS_DLE_command)) { - inputstate |= INS_DLE_char; - break; + /* cs->dle may have changed */ + if (cs->dle && !(inbuf->inputstate & INS_DLE_command)) + inbuf->inputstate &= ~INS_command; + + /* return for reevaluating state */ + goto exit; + + case DLE_FLAG: + if (inbuf->inputstate & INS_DLE_char) { + /* quoted DLE: clear quote flag */ + inbuf->inputstate &= ~INS_DLE_char; + } else if (cs->dle || + (inbuf->inputstate & INS_DLE_command)) { + /* DLE escape, pass up for handling */ + inbuf->inputstate |= INS_DLE_char; + goto exit; + } + /* quoted or not in DLE mode: treat as regular data */ + /* --v-- fall through --v-- */ + default: + /* append to line buffer if possible */ + if (cbytes < MAX_RESP_SIZE) + cs->respdata[cbytes] = c; + cbytes++; } } - +exit: cs->cbytes = cbytes; - inbuf->inputstate = inputstate; - - return startbytes - numbytes; + return procbytes; } -/* process a block of received bytes in lock mode (tty i/f) +/* process a block of received bytes in lock mode + * All received bytes are passed unmodified to the tty i/f. * Return value: * number of processed bytes */ -static inline int lock_loop(unsigned char *src, int numbytes, - struct inbuf_t *inbuf) +static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf) { - struct cardstate *cs = inbuf->cs; - - gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", - numbytes, src); - gigaset_if_receive(cs, src, numbytes); + unsigned char *src = inbuf->data + inbuf->head; + gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src); + gigaset_if_receive(inbuf->cs, src, numbytes); return numbytes; } +/* set up next receive skb for data mode + */ +static void new_rcv_skb(struct bc_state *bcs) +{ + struct cardstate *cs = bcs->cs; + unsigned short hw_hdr_len = cs->hw_hdr_len; + + if (bcs->ignore) { + bcs->skb = NULL; + return; + } + + bcs->skb = dev_alloc_skb(SBUFSIZE + hw_hdr_len); + if (bcs->skb == NULL) { + dev_warn(cs->dev, "could not allocate new skb\n"); + return; + } + skb_reserve(bcs->skb, hw_hdr_len); +} + /* process a block of received bytes in HDLC data mode + * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC) * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes. * When a frame is complete, check the FCS and pass valid frames to the LL. * If DLE is encountered, return immediately to let the caller handle it. * Return value: * number of processed bytes - * numbytes (all bytes processed) on error --FIXME */ -static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes, - struct inbuf_t *inbuf) +static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf) { struct cardstate *cs = inbuf->cs; - struct bc_state *bcs = inbuf->bcs; + struct bc_state *bcs = cs->bcs; int inputstate = bcs->inputstate; __u16 fcs = bcs->fcs; struct sk_buff *skb = bcs->skb; - unsigned char error; - struct sk_buff *compskb; - int startbytes = numbytes; - int l; + unsigned char *src = inbuf->data + inbuf->head; + unsigned procbytes = 0; + unsigned char c; - if (unlikely(inputstate & INS_byte_stuff)) { + if (inputstate & INS_byte_stuff) { + if (!numbytes) + return 0; inputstate &= ~INS_byte_stuff; goto byte_stuff; } - for (;;) { - if (unlikely(c == PPP_ESCAPE)) { - if (unlikely(!numbytes)) { - inputstate |= INS_byte_stuff; + + while (procbytes < numbytes) { + c = *src++; + procbytes++; + if (c == DLE_FLAG) { + if (inputstate & INS_DLE_char) { + /* quoted DLE: clear quote flag */ + inputstate &= ~INS_DLE_char; + } else if (cs->dle || (inputstate & INS_DLE_command)) { + /* DLE escape, pass up for handling */ + inputstate |= INS_DLE_char; break; } - c = *src++; - --numbytes; - if (unlikely(c == DLE_FLAG && - (cs->dle || - inbuf->inputstate & INS_DLE_command))) { - inbuf->inputstate |= INS_DLE_char; + } + + if (c == PPP_ESCAPE) { + /* byte stuffing indicator: pull in next byte */ + if (procbytes >= numbytes) { + /* end of buffer, save for later processing */ inputstate |= INS_byte_stuff; break; } byte_stuff: + c = *src++; + procbytes++; + if (c == DLE_FLAG) { + if (inputstate & INS_DLE_char) { + /* quoted DLE: clear quote flag */ + inputstate &= ~INS_DLE_char; + } else if (cs->dle || + (inputstate & INS_DLE_command)) { + /* DLE escape, pass up for handling */ + inputstate |= + INS_DLE_char | INS_byte_stuff; + break; + } + } c ^= PPP_TRANS; - if (unlikely(!muststuff(c))) - gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c); - } else if (unlikely(c == PPP_FLAG)) { - if (unlikely(inputstate & INS_skip_frame)) { -#ifdef CONFIG_GIGASET_DEBUG - if (!(inputstate & INS_have_data)) { /* 7E 7E */ - ++bcs->emptycount; - } else - gig_dbg(DEBUG_HDLC, - "7e----------------------------"); -#endif - - /* end of frame */ - error = 1; - gigaset_rcv_error(NULL, cs, bcs); - } else if (!(inputstate & INS_have_data)) { /* 7E 7E */ #ifdef CONFIG_GIGASET_DEBUG - ++bcs->emptycount; + if (!muststuff(c)) + gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c); #endif - break; - } else { + } else if (c == PPP_FLAG) { + /* end of frame: process content if any */ + if (inputstate & INS_have_data) { gig_dbg(DEBUG_HDLC, "7e----------------------------"); - /* end of frame */ - error = 0; - - if (unlikely(fcs != PPP_GOODFCS)) { + /* check and pass received frame */ + if (!skb) { + /* skipped frame */ + gigaset_isdn_rcv_err(bcs); + } else if (skb->len < 2) { + /* frame too short for FCS */ + dev_warn(cs->dev, + "short frame (%d)\n", + skb->len); + gigaset_isdn_rcv_err(bcs); + dev_kfree_skb_any(skb); + } else if (fcs != PPP_GOODFCS) { + /* frame check error */ dev_err(cs->dev, "Checksum failed, %u bytes corrupted!\n", skb->len); - compskb = NULL; - gigaset_rcv_error(compskb, cs, bcs); - error = 1; + gigaset_isdn_rcv_err(bcs); + dev_kfree_skb_any(skb); } else { - if (likely((l = skb->len) > 2)) { - skb->tail -= 2; - skb->len -= 2; - } else { - dev_kfree_skb(skb); - skb = NULL; - inputstate |= INS_skip_frame; - if (l == 1) { - dev_err(cs->dev, - "invalid packet size (1)!\n"); - error = 1; - gigaset_rcv_error(NULL, - cs, bcs); - } - } - if (likely(!(error || - (inputstate & - INS_skip_frame)))) { - gigaset_rcv_skb(skb, cs, bcs); - } + /* good frame */ + __skb_trim(skb, skb->len - 2); + gigaset_skb_rcvd(bcs, skb); } - } - if (unlikely(error)) - if (skb) - dev_kfree_skb(skb); - - fcs = PPP_INITFCS; - inputstate &= ~(INS_have_data | INS_skip_frame); - if (unlikely(bcs->ignore)) { - inputstate |= INS_skip_frame; - skb = NULL; - } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) { - skb_reserve(skb, HW_HDR_LEN); + /* prepare reception of next frame */ + inputstate &= ~INS_have_data; + new_rcv_skb(bcs); + skb = bcs->skb; } else { - dev_warn(cs->dev, - "could not allocate new skb\n"); - inputstate |= INS_skip_frame; + /* empty frame (7E 7E) */ +#ifdef CONFIG_GIGASET_DEBUG + ++bcs->emptycount; +#endif + if (!skb) { + /* skipped (?) */ + gigaset_isdn_rcv_err(bcs); + new_rcv_skb(bcs); + skb = bcs->skb; + } } - break; - } else if (unlikely(muststuff(c))) { + fcs = PPP_INITFCS; + continue; +#ifdef CONFIG_GIGASET_DEBUG + } else if (muststuff(c)) { /* Should not happen. Possible after ZDLE=1<CR><LF>. */ gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c); +#endif } - /* add character */ - + /* regular data byte, append to skb */ #ifdef CONFIG_GIGASET_DEBUG - if (unlikely(!(inputstate & INS_have_data))) { + if (!(inputstate & INS_have_data)) { gig_dbg(DEBUG_HDLC, "7e (%d x) ================", bcs->emptycount); bcs->emptycount = 0; } #endif - inputstate |= INS_have_data; - - if (likely(!(inputstate & INS_skip_frame))) { - if (unlikely(skb->len == SBUFSIZE)) { + if (skb) { + if (skb->len == SBUFSIZE) { dev_warn(cs->dev, "received packet too long\n"); dev_kfree_skb_any(skb); - skb = NULL; - inputstate |= INS_skip_frame; - break; + /* skip remainder of packet */ + bcs->skb = skb = NULL; + } else { + *__skb_put(skb, 1) = c; + fcs = crc_ccitt_byte(fcs, c); } - *__skb_put(skb, 1) = c; - fcs = crc_ccitt_byte(fcs, c); - } - - if (unlikely(!numbytes)) - break; - c = *src++; - --numbytes; - if (unlikely(c == DLE_FLAG && - (cs->dle || - inbuf->inputstate & INS_DLE_command))) { - inbuf->inputstate |= INS_DLE_char; - break; } } + bcs->inputstate = inputstate; bcs->fcs = fcs; - bcs->skb = skb; - return startbytes - numbytes; + return procbytes; } /* process a block of received bytes in transparent data mode + * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 != L2_HDLC) * Invert bytes, undoing byte stuffing and watching for DLE escapes. * If DLE is encountered, return immediately to let the caller handle it. * Return value: * number of processed bytes - * numbytes (all bytes processed) on error --FIXME */ -static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes, - struct inbuf_t *inbuf) +static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf) { struct cardstate *cs = inbuf->cs; - struct bc_state *bcs = inbuf->bcs; + struct bc_state *bcs = cs->bcs; int inputstate = bcs->inputstate; struct sk_buff *skb = bcs->skb; - int startbytes = numbytes; + unsigned char *src = inbuf->data + inbuf->head; + unsigned procbytes = 0; + unsigned char c; - for (;;) { - /* add character */ - inputstate |= INS_have_data; + if (!skb) { + /* skip this block */ + new_rcv_skb(bcs); + return numbytes; + } - if (likely(!(inputstate & INS_skip_frame))) { - if (unlikely(skb->len == SBUFSIZE)) { - //FIXME just pass skb up and allocate a new one - dev_warn(cs->dev, "received packet too long\n"); - dev_kfree_skb_any(skb); - skb = NULL; - inputstate |= INS_skip_frame; + while (procbytes < numbytes && skb->len < SBUFSIZE) { + c = *src++; + procbytes++; + + if (c == DLE_FLAG) { + if (inputstate & INS_DLE_char) { + /* quoted DLE: clear quote flag */ + inputstate &= ~INS_DLE_char; + } else if (cs->dle || (inputstate & INS_DLE_command)) { + /* DLE escape, pass up for handling */ + inputstate |= INS_DLE_char; break; } - *__skb_put(skb, 1) = bitrev8(c); } - if (unlikely(!numbytes)) - break; - c = *src++; - --numbytes; - if (unlikely(c == DLE_FLAG && - (cs->dle || - inbuf->inputstate & INS_DLE_command))) { - inbuf->inputstate |= INS_DLE_char; - break; - } + /* regular data byte: append to current skb */ + inputstate |= INS_have_data; + *__skb_put(skb, 1) = bitrev8(c); } /* pass data up */ - if (likely(inputstate & INS_have_data)) { - if (likely(!(inputstate & INS_skip_frame))) { - gigaset_rcv_skb(skb, cs, bcs); - } - inputstate &= ~(INS_have_data | INS_skip_frame); - if (unlikely(bcs->ignore)) { - inputstate |= INS_skip_frame; - skb = NULL; - } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) - != NULL)) { - skb_reserve(skb, HW_HDR_LEN); + if (inputstate & INS_have_data) { + gigaset_skb_rcvd(bcs, skb); + inputstate &= ~INS_have_data; + new_rcv_skb(bcs); + } + + bcs->inputstate = inputstate; + return procbytes; +} + +/* process DLE escapes + * Called whenever a DLE sequence might be encountered in the input stream. + * Either processes the entire DLE sequence or, if that isn't possible, + * notes the fact that an initial DLE has been received in the INS_DLE_char + * inputstate flag and resumes processing of the sequence on the next call. + */ +static void handle_dle(struct inbuf_t *inbuf) +{ + struct cardstate *cs = inbuf->cs; + + if (cs->mstate == MS_LOCKED) + return; /* no DLE processing in lock mode */ + + if (!(inbuf->inputstate & INS_DLE_char)) { + /* no DLE pending */ + if (inbuf->data[inbuf->head] == DLE_FLAG && + (cs->dle || inbuf->inputstate & INS_DLE_command)) { + /* start of DLE sequence */ + inbuf->head++; + if (inbuf->head == inbuf->tail || + inbuf->head == RBUFSIZE) { + /* end of buffer, save for later processing */ + inbuf->inputstate |= INS_DLE_char; + return; + } } else { - dev_warn(cs->dev, "could not allocate new skb\n"); - inputstate |= INS_skip_frame; + /* regular data byte */ + return; } } - bcs->inputstate = inputstate; - bcs->skb = skb; - return startbytes - numbytes; + /* consume pending DLE */ + inbuf->inputstate &= ~INS_DLE_char; + + switch (inbuf->data[inbuf->head]) { + case 'X': /* begin of event message */ + if (inbuf->inputstate & INS_command) + dev_notice(cs->dev, + "received <DLE>X in command mode\n"); + inbuf->inputstate |= INS_command | INS_DLE_command; + inbuf->head++; /* byte consumed */ + break; + case '.': /* end of event message */ + if (!(inbuf->inputstate & INS_DLE_command)) + dev_notice(cs->dev, + "received <DLE>. without <DLE>X\n"); + inbuf->inputstate &= ~INS_DLE_command; + /* return to data mode if in DLE mode */ + if (cs->dle) + inbuf->inputstate &= ~INS_command; + inbuf->head++; /* byte consumed */ + break; + case DLE_FLAG: /* DLE in data stream */ + /* mark as quoted */ + inbuf->inputstate |= INS_DLE_char; + if (!(cs->dle || inbuf->inputstate & INS_DLE_command)) + dev_notice(cs->dev, + "received <DLE><DLE> not in DLE mode\n"); + break; /* quoted byte left in buffer */ + default: + dev_notice(cs->dev, "received <DLE><%02x>\n", + inbuf->data[inbuf->head]); + /* quoted byte left in buffer */ + } } /** @@ -345,94 +425,39 @@ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes, */ void gigaset_m10x_input(struct inbuf_t *inbuf) { - struct cardstate *cs; - unsigned tail, head, numbytes; - unsigned char *src, c; - int procbytes; - - head = inbuf->head; - tail = inbuf->tail; - gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); - - if (head != tail) { - cs = inbuf->cs; - src = inbuf->data + head; - numbytes = (head > tail ? RBUFSIZE : tail) - head; - gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes); + struct cardstate *cs = inbuf->cs; + unsigned numbytes, procbytes; - while (numbytes) { - if (cs->mstate == MS_LOCKED) { - procbytes = lock_loop(src, numbytes, inbuf); - src += procbytes; - numbytes -= procbytes; - } else { - c = *src++; - --numbytes; - if (c == DLE_FLAG && (cs->dle || - inbuf->inputstate & INS_DLE_command)) { - if (!(inbuf->inputstate & INS_DLE_char)) { - inbuf->inputstate |= INS_DLE_char; - goto nextbyte; - } - /* <DLE> <DLE> => <DLE> in data stream */ - inbuf->inputstate &= ~INS_DLE_char; - } + gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", inbuf->head, inbuf->tail); - if (!(inbuf->inputstate & INS_DLE_char)) { - - /* FIXME use function pointers? */ - if (inbuf->inputstate & INS_command) - procbytes = cmd_loop(c, src, numbytes, inbuf); - else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC) - procbytes = hdlc_loop(c, src, numbytes, inbuf); - else - procbytes = iraw_loop(c, src, numbytes, inbuf); - - src += procbytes; - numbytes -= procbytes; - } else { /* DLE char */ - inbuf->inputstate &= ~INS_DLE_char; - switch (c) { - case 'X': /*begin of command*/ - if (inbuf->inputstate & INS_command) - dev_warn(cs->dev, - "received <DLE> 'X' in command mode\n"); - inbuf->inputstate |= - INS_command | INS_DLE_command; - break; - case '.': /*end of command*/ - if (!(inbuf->inputstate & INS_command)) - dev_warn(cs->dev, - "received <DLE> '.' in hdlc mode\n"); - inbuf->inputstate &= cs->dle ? - ~(INS_DLE_command|INS_command) - : ~INS_DLE_command; - break; - //case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */ - default: - dev_err(cs->dev, - "received 0x10 0x%02x!\n", - (int) c); - /* FIXME: reset driver?? */ - } - } - } -nextbyte: - if (!numbytes) { - /* end of buffer, check for wrap */ - if (head > tail) { - head = 0; - src = inbuf->data; - numbytes = tail; - } else { - head = tail; - break; - } - } - } + while (inbuf->head != inbuf->tail) { + /* check for DLE escape */ + handle_dle(inbuf); - gig_dbg(DEBUG_INTR, "setting head to %u", head); - inbuf->head = head; + /* process a contiguous block of bytes */ + numbytes = (inbuf->head > inbuf->tail ? + RBUFSIZE : inbuf->tail) - inbuf->head; + gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes); + /* + * numbytes may be 0 if handle_dle() ate the last byte. + * This does no harm, *_loop() will just return 0 immediately. + */ + + if (cs->mstate == MS_LOCKED) + procbytes = lock_loop(numbytes, inbuf); + else if (inbuf->inputstate & INS_command) + procbytes = cmd_loop(numbytes, inbuf); + else if (cs->bcs->proto2 == L2_HDLC) + procbytes = hdlc_loop(numbytes, inbuf); + else + procbytes = iraw_loop(numbytes, inbuf); + inbuf->head += procbytes; + + /* check for buffer wraparound */ + if (inbuf->head >= RBUFSIZE) + inbuf->head = 0; + + gig_dbg(DEBUG_INTR, "head set to %u", inbuf->head); } } EXPORT_SYMBOL_GPL(gigaset_m10x_input); @@ -440,16 +465,16 @@ EXPORT_SYMBOL_GPL(gigaset_m10x_input); /* == data output ========================================================== */ -/* Encoding of a PPP packet into an octet stuffed HDLC frame - * with FCS, opening and closing flags. +/* + * Encode a data packet into an octet stuffed HDLC frame with FCS, + * opening and closing flags, preserving headroom data. * parameters: - * skb skb containing original packet (freed upon return) - * head number of headroom bytes to allocate in result skb - * tail number of tailroom bytes to allocate in result skb + * skb skb containing original packet (freed upon return) * Return value: * pointer to newly allocated skb containing the result frame + * and the original link layer header, NULL on error */ -static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail) +static struct sk_buff *HDLC_Encode(struct sk_buff *skb) { struct sk_buff *hdlc_skb; __u16 fcs; @@ -471,16 +496,19 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail) /* size of new buffer: original size + number of stuffing bytes * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes + * + room for link layer header */ - hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head); + hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + skb->mac_len); if (!hdlc_skb) { - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); return NULL; } - skb_reserve(hdlc_skb, head); - /* Copy acknowledge request into new skb */ - memcpy(hdlc_skb->head, skb->head, 2); + /* Copy link layer header into new skb */ + skb_reset_mac_header(hdlc_skb); + skb_reserve(hdlc_skb, skb->mac_len); + memcpy(skb_mac_header(hdlc_skb), skb_mac_header(skb), skb->mac_len); + hdlc_skb->mac_len = skb->mac_len; /* Add flag sequence in front of everything.. */ *(skb_put(hdlc_skb, 1)) = PPP_FLAG; @@ -511,33 +539,42 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail) *(skb_put(hdlc_skb, 1)) = PPP_FLAG; - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); return hdlc_skb; } -/* Encoding of a raw packet into an octet stuffed bit inverted frame +/* + * Encode a data packet into an octet stuffed raw bit inverted frame, + * preserving headroom data. * parameters: - * skb skb containing original packet (freed upon return) - * head number of headroom bytes to allocate in result skb - * tail number of tailroom bytes to allocate in result skb + * skb skb containing original packet (freed upon return) * Return value: * pointer to newly allocated skb containing the result frame + * and the original link layer header, NULL on error */ -static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) +static struct sk_buff *iraw_encode(struct sk_buff *skb) { struct sk_buff *iraw_skb; unsigned char c; unsigned char *cp; int len; - /* worst case: every byte must be stuffed */ - iraw_skb = dev_alloc_skb(2*skb->len + tail + head); + /* size of new buffer (worst case = every byte must be stuffed): + * 2 * original size + room for link layer header + */ + iraw_skb = dev_alloc_skb(2*skb->len + skb->mac_len); if (!iraw_skb) { - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); return NULL; } - skb_reserve(iraw_skb, head); + /* copy link layer header into new skb */ + skb_reset_mac_header(iraw_skb); + skb_reserve(iraw_skb, skb->mac_len); + memcpy(skb_mac_header(iraw_skb), skb_mac_header(skb), skb->mac_len); + iraw_skb->mac_len = skb->mac_len; + + /* copy and stuff data */ cp = skb->data; len = skb->len; while (len--) { @@ -546,7 +583,7 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) *(skb_put(iraw_skb, 1)) = c; *(skb_put(iraw_skb, 1)) = c; } - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); return iraw_skb; } @@ -555,8 +592,10 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) * @bcs: B channel descriptor structure. * @skb: data to send. * - * Called by i4l.c to encode and queue an skb for sending, and start + * Called by LL to encode and queue an skb for sending, and start * transmission if necessary. + * Once the payload data has been transmitted completely, gigaset_skb_sent() + * will be called with the skb's link layer header preserved. * * Return value: * number of bytes accepted for sending (skb->len) if ok, @@ -564,24 +603,25 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) */ int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb) { + struct cardstate *cs = bcs->cs; unsigned len = skb->len; unsigned long flags; - if (bcs->proto2 == ISDN_PROTO_L2_HDLC) - skb = HDLC_Encode(skb, HW_HDR_LEN, 0); + if (bcs->proto2 == L2_HDLC) + skb = HDLC_Encode(skb); else - skb = iraw_encode(skb, HW_HDR_LEN, 0); + skb = iraw_encode(skb); if (!skb) { - dev_err(bcs->cs->dev, + dev_err(cs->dev, "unable to allocate memory for encoding!\n"); return -ENOMEM; } skb_queue_tail(&bcs->squeue, skb); - spin_lock_irqsave(&bcs->cs->lock, flags); - if (bcs->cs->connected) - tasklet_schedule(&bcs->cs->write_tasklet); - spin_unlock_irqrestore(&bcs->cs->lock, flags); + spin_lock_irqsave(&cs->lock, flags); + if (cs->connected) + tasklet_schedule(&cs->write_tasklet); + spin_unlock_irqrestore(&cs->lock, flags); return len; /* ok so far */ } diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index 5ed1d99eb9f3..95ebc5129895 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -57,7 +57,7 @@ MODULE_PARM_DESC(cidmode, "Call-ID mode"); #define USB_SX353_PRODUCT_ID 0x0022 /* table of devices that work with this driver */ -static const struct usb_device_id gigaset_table [] = { +static const struct usb_device_id gigaset_table[] = { { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) }, { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) }, { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) }, @@ -137,7 +137,7 @@ struct bas_cardstate { #define BS_RESETTING 0x200 /* waiting for HD_RESET_INTERRUPT_PIPE_ACK */ -static struct gigaset_driver *driver = NULL; +static struct gigaset_driver *driver; /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver gigaset_usb_driver = { @@ -601,11 +601,12 @@ static int atread_submit(struct cardstate *cs, int timeout) ucs->dr_cmd_in.wLength = cpu_to_le16(ucs->rcvbuf_size); usb_fill_control_urb(ucs->urb_cmd_in, ucs->udev, usb_rcvctrlpipe(ucs->udev, 0), - (unsigned char*) & ucs->dr_cmd_in, + (unsigned char *) &ucs->dr_cmd_in, ucs->rcvbuf, ucs->rcvbuf_size, read_ctrl_callback, cs->inbuf); - if ((ret = usb_submit_urb(ucs->urb_cmd_in, GFP_ATOMIC)) != 0) { + ret = usb_submit_urb(ucs->urb_cmd_in, GFP_ATOMIC); + if (ret != 0) { update_basstate(ucs, 0, BS_ATRDPEND); dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n", get_usb_rcmsg(ret)); @@ -652,13 +653,11 @@ static void read_int_callback(struct urb *urb) return; case -ENODEV: /* device removed */ case -ESHUTDOWN: /* device shut down */ - //FIXME use this as disconnect indicator? gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__); return; default: /* severe trouble */ dev_warn(cs->dev, "interrupt read: %s\n", get_usb_statmsg(status)); - //FIXME corrective action? resubmission always ok? goto resubmit; } @@ -742,7 +741,8 @@ static void read_int_callback(struct urb *urb) kfree(ucs->rcvbuf); ucs->rcvbuf_size = 0; } - if ((ucs->rcvbuf = kmalloc(l, GFP_ATOMIC)) == NULL) { + ucs->rcvbuf = kmalloc(l, GFP_ATOMIC); + if (ucs->rcvbuf == NULL) { spin_unlock_irqrestore(&cs->lock, flags); dev_err(cs->dev, "out of memory receiving AT data\n"); error_reset(cs); @@ -750,12 +750,12 @@ static void read_int_callback(struct urb *urb) } ucs->rcvbuf_size = l; ucs->retry_cmd_in = 0; - if ((rc = atread_submit(cs, BAS_TIMEOUT)) < 0) { + rc = atread_submit(cs, BAS_TIMEOUT); + if (rc < 0) { kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; if (rc != -ENODEV) { - //FIXME corrective action? spin_unlock_irqrestore(&cs->lock, flags); error_reset(cs); break; @@ -911,7 +911,7 @@ static int starturbs(struct bc_state *bcs) int rc; /* initialize L2 reception */ - if (bcs->proto2 == ISDN_PROTO_L2_HDLC) + if (bcs->proto2 == L2_HDLC) bcs->inputstate |= INS_flag_hunt; /* submit all isochronous input URBs */ @@ -940,7 +940,8 @@ static int starturbs(struct bc_state *bcs) } dump_urb(DEBUG_ISO, "Initial isoc read", urb); - if ((rc = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc != 0) goto error; } @@ -1045,7 +1046,8 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) /* compute frame length according to flow control */ ifd->length = BAS_NORMFRAME; - if ((corrbytes = atomic_read(&ubc->corrbytes)) != 0) { + corrbytes = atomic_read(&ubc->corrbytes); + if (corrbytes != 0) { gig_dbg(DEBUG_ISO, "%s: corrbytes=%d", __func__, corrbytes); if (corrbytes > BAS_HIGHFRAME - BAS_NORMFRAME) @@ -1064,7 +1066,7 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) "%s: buffer busy at frame %d", __func__, nframe); /* tasklet will be restarted from - gigaset_send_skb() */ + gigaset_isoc_send_skb() */ } else { dev_err(ucx->bcs->cs->dev, "%s: buffer error %d at frame %d\n", @@ -1284,7 +1286,8 @@ static void read_iso_tasklet(unsigned long data) for (;;) { /* retrieve URB */ spin_lock_irqsave(&ubc->isoinlock, flags); - if (!(urb = ubc->isoindone)) { + urb = ubc->isoindone; + if (!urb) { spin_unlock_irqrestore(&ubc->isoinlock, flags); return; } @@ -1371,7 +1374,7 @@ static void read_iso_tasklet(unsigned long data) "isochronous read: %d data bytes missing\n", totleft); - error: +error: /* URB processed, resubmit */ for (frame = 0; frame < BAS_NUMFRAMES; frame++) { urb->iso_frame_desc[frame].status = 0; @@ -1568,7 +1571,7 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout) ucs->dr_ctrl.wLength = 0; usb_fill_control_urb(ucs->urb_ctrl, ucs->udev, usb_sndctrlpipe(ucs->udev, 0), - (unsigned char*) &ucs->dr_ctrl, NULL, 0, + (unsigned char *) &ucs->dr_ctrl, NULL, 0, write_ctrl_callback, ucs); ucs->retry_ctrl = 0; ret = usb_submit_urb(ucs->urb_ctrl, GFP_ATOMIC); @@ -1621,7 +1624,8 @@ static int gigaset_init_bchannel(struct bc_state *bcs) return -EHOSTUNREACH; } - if ((ret = starturbs(bcs)) < 0) { + ret = starturbs(bcs); + if (ret < 0) { dev_err(cs->dev, "could not start isochronous I/O for channel B%d: %s\n", bcs->channel + 1, @@ -1633,7 +1637,8 @@ static int gigaset_init_bchannel(struct bc_state *bcs) } req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL; - if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) { + ret = req_submit(bcs, req, 0, BAS_TIMEOUT); + if (ret < 0) { dev_err(cs->dev, "could not open channel B%d\n", bcs->channel + 1); stopurbs(bcs->hw.bas); @@ -1677,7 +1682,8 @@ static int gigaset_close_bchannel(struct bc_state *bcs) /* channel running: tell device to close it */ req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL; - if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) + ret = req_submit(bcs, req, 0, BAS_TIMEOUT); + if (ret < 0) dev_err(cs->dev, "closing channel B%d failed\n", bcs->channel + 1); @@ -1703,10 +1709,12 @@ static void complete_cb(struct cardstate *cs) gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "write_command: sent %u bytes, %u left", cs->curlen, cs->cmdbytes); - if ((cs->cmdbuf = cb->next) != NULL) { + if (cb->next != NULL) { + cs->cmdbuf = cb->next; cs->cmdbuf->prev = NULL; cs->curlen = cs->cmdbuf->len; } else { + cs->cmdbuf = NULL; cs->lastcmdbuf = NULL; cs->curlen = 0; } @@ -1833,7 +1841,7 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len) ucs->dr_cmd_out.wLength = cpu_to_le16(len); usb_fill_control_urb(ucs->urb_cmd_out, ucs->udev, usb_sndctrlpipe(ucs->udev, 0), - (unsigned char*) &ucs->dr_cmd_out, buf, len, + (unsigned char *) &ucs->dr_cmd_out, buf, len, write_command_callback, cs); rc = usb_submit_urb(ucs->urb_cmd_out, GFP_ATOMIC); if (unlikely(rc)) { @@ -1953,7 +1961,8 @@ static int gigaset_write_cmd(struct cardstate *cs, if (len > IF_WRITEBUF) len = IF_WRITEBUF; - if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { + cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC); + if (!cb) { dev_err(cs->dev, "%s: out of memory\n", __func__); rc = -ENOMEM; goto notqueued; @@ -2100,14 +2109,15 @@ static int gigaset_initbcshw(struct bc_state *bcs) } ubc->isooutdone = ubc->isooutfree = ubc->isooutovfl = NULL; ubc->numsub = 0; - if (!(ubc->isooutbuf = kmalloc(sizeof(struct isowbuf_t), GFP_KERNEL))) { + ubc->isooutbuf = kmalloc(sizeof(struct isowbuf_t), GFP_KERNEL); + if (!ubc->isooutbuf) { pr_err("out of memory\n"); kfree(ubc); bcs->hw.bas = NULL; return 0; } tasklet_init(&ubc->sent_tasklet, - &write_iso_tasklet, (unsigned long) bcs); + write_iso_tasklet, (unsigned long) bcs); spin_lock_init(&ubc->isoinlock); for (i = 0; i < BAS_INURBS; ++i) @@ -2128,7 +2138,7 @@ static int gigaset_initbcshw(struct bc_state *bcs) ubc->shared0s = 0; ubc->stolen0s = 0; tasklet_init(&ubc->rcvd_tasklet, - &read_iso_tasklet, (unsigned long) bcs); + read_iso_tasklet, (unsigned long) bcs); return 1; } @@ -2252,7 +2262,8 @@ static int gigaset_probe(struct usb_interface *interface, gig_dbg(DEBUG_ANY, "%s: wrong alternate setting %d - trying to switch", __func__, hostif->desc.bAlternateSetting); - if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3) < 0) { + if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3) + < 0) { dev_warn(&udev->dev, "usb_set_interface failed, " "device %d interface %d altsetting %d\n", udev->devnum, hostif->desc.bInterfaceNumber, @@ -2321,14 +2332,16 @@ static int gigaset_probe(struct usb_interface *interface, (endpoint->bEndpointAddress) & 0x0f), ucs->int_in_buf, IP_MSGSIZE, read_int_callback, cs, endpoint->bInterval); - if ((rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL)) != 0) { + rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL); + if (rc != 0) { dev_err(cs->dev, "could not submit interrupt URB: %s\n", get_usb_rcmsg(rc)); goto error; } /* tell the device that the driver is ready */ - if ((rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0)) != 0) + rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0); + if (rc != 0) goto error; /* tell common part that the device is ready */ @@ -2524,9 +2537,10 @@ static int __init bas_gigaset_init(void) int result; /* allocate memory for our driver state and intialize it */ - if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, - GIGASET_MODULENAME, GIGASET_DEVNAME, - &gigops, THIS_MODULE)) == NULL) + driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, + GIGASET_MODULENAME, GIGASET_DEVNAME, + &gigops, THIS_MODULE); + if (driver == NULL) goto error; /* register this driver with the USB subsystem */ diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c new file mode 100644 index 000000000000..3f5cd06af104 --- /dev/null +++ b/drivers/isdn/gigaset/capi.c @@ -0,0 +1,2292 @@ +/* + * Kernel CAPI interface for the Gigaset driver + * + * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>. + * + * ===================================================================== + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * ===================================================================== + */ + +#include "gigaset.h" +#include <linux/ctype.h> +#include <linux/isdn/capilli.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> + +/* missing from kernelcapi.h */ +#define CapiNcpiNotSupportedByProtocol 0x0001 +#define CapiFlagsNotSupportedByProtocol 0x0002 +#define CapiAlertAlreadySent 0x0003 +#define CapiFacilitySpecificFunctionNotSupported 0x3011 + +/* missing from capicmd.h */ +#define CAPI_CONNECT_IND_BASELEN (CAPI_MSG_BASELEN+4+2+8*1) +#define CAPI_CONNECT_ACTIVE_IND_BASELEN (CAPI_MSG_BASELEN+4+3*1) +#define CAPI_CONNECT_B3_IND_BASELEN (CAPI_MSG_BASELEN+4+1) +#define CAPI_CONNECT_B3_ACTIVE_IND_BASELEN (CAPI_MSG_BASELEN+4+1) +#define CAPI_DATA_B3_REQ_LEN64 (CAPI_MSG_BASELEN+4+4+2+2+2+8) +#define CAPI_DATA_B3_CONF_LEN (CAPI_MSG_BASELEN+4+2+2) +#define CAPI_DISCONNECT_IND_LEN (CAPI_MSG_BASELEN+4+2) +#define CAPI_DISCONNECT_B3_IND_BASELEN (CAPI_MSG_BASELEN+4+2+1) +#define CAPI_FACILITY_CONF_BASELEN (CAPI_MSG_BASELEN+4+2+2+1) +/* most _CONF messages contain only Controller/PLCI/NCCI and Info parameters */ +#define CAPI_STDCONF_LEN (CAPI_MSG_BASELEN+4+2) + +#define CAPI_FACILITY_HANDSET 0x0000 +#define CAPI_FACILITY_DTMF 0x0001 +#define CAPI_FACILITY_V42BIS 0x0002 +#define CAPI_FACILITY_SUPPSVC 0x0003 +#define CAPI_FACILITY_WAKEUP 0x0004 +#define CAPI_FACILITY_LI 0x0005 + +#define CAPI_SUPPSVC_GETSUPPORTED 0x0000 + +/* missing from capiutil.h */ +#define CAPIMSG_PLCI_PART(m) CAPIMSG_U8(m, 9) +#define CAPIMSG_NCCI_PART(m) CAPIMSG_U16(m, 10) +#define CAPIMSG_HANDLE_REQ(m) CAPIMSG_U16(m, 18) /* DATA_B3_REQ/_IND only! */ +#define CAPIMSG_FLAGS(m) CAPIMSG_U16(m, 20) +#define CAPIMSG_SETCONTROLLER(m, contr) capimsg_setu8(m, 8, contr) +#define CAPIMSG_SETPLCI_PART(m, plci) capimsg_setu8(m, 9, plci) +#define CAPIMSG_SETNCCI_PART(m, ncci) capimsg_setu16(m, 10, ncci) +#define CAPIMSG_SETFLAGS(m, flags) capimsg_setu16(m, 20, flags) + +/* parameters with differing location in DATA_B3_CONF/_RESP: */ +#define CAPIMSG_SETHANDLE_CONF(m, handle) capimsg_setu16(m, 12, handle) +#define CAPIMSG_SETINFO_CONF(m, info) capimsg_setu16(m, 14, info) + +/* Flags (DATA_B3_REQ/_IND) */ +#define CAPI_FLAGS_DELIVERY_CONFIRMATION 0x04 +#define CAPI_FLAGS_RESERVED (~0x1f) + +/* buffer sizes */ +#define MAX_BC_OCTETS 11 +#define MAX_HLC_OCTETS 3 +#define MAX_NUMBER_DIGITS 20 +#define MAX_FMT_IE_LEN 20 + +/* values for gigaset_capi_appl.connected */ +#define APCONN_NONE 0 /* inactive/listening */ +#define APCONN_SETUP 1 /* connecting */ +#define APCONN_ACTIVE 2 /* B channel up */ + +/* registered application data structure */ +struct gigaset_capi_appl { + struct list_head ctrlist; + struct gigaset_capi_appl *bcnext; + u16 id; + u16 nextMessageNumber; + u32 listenInfoMask; + u32 listenCIPmask; + int connected; +}; + +/* CAPI specific controller data structure */ +struct gigaset_capi_ctr { + struct capi_ctr ctr; + struct list_head appls; + struct sk_buff_head sendqueue; + atomic_t sendqlen; + /* two _cmsg structures possibly used concurrently: */ + _cmsg hcmsg; /* for message composition triggered from hardware */ + _cmsg acmsg; /* for dissection of messages sent from application */ + u8 bc_buf[MAX_BC_OCTETS+1]; + u8 hlc_buf[MAX_HLC_OCTETS+1]; + u8 cgpty_buf[MAX_NUMBER_DIGITS+3]; + u8 cdpty_buf[MAX_NUMBER_DIGITS+2]; +}; + +/* CIP Value table (from CAPI 2.0 standard, ch. 6.1) */ +static struct { + u8 *bc; + u8 *hlc; +} cip2bchlc[] = { + [1] = { "8090A3", NULL }, + /* Speech (A-law) */ + [2] = { "8890", NULL }, + /* Unrestricted digital information */ + [3] = { "8990", NULL }, + /* Restricted digital information */ + [4] = { "9090A3", NULL }, + /* 3,1 kHz audio (A-law) */ + [5] = { "9190", NULL }, + /* 7 kHz audio */ + [6] = { "9890", NULL }, + /* Video */ + [7] = { "88C0C6E6", NULL }, + /* Packet mode */ + [8] = { "8890218F", NULL }, + /* 56 kbit/s rate adaptation */ + [9] = { "9190A5", NULL }, + /* Unrestricted digital information with tones/announcements */ + [16] = { "8090A3", "9181" }, + /* Telephony */ + [17] = { "9090A3", "9184" }, + /* Group 2/3 facsimile */ + [18] = { "8890", "91A1" }, + /* Group 4 facsimile Class 1 */ + [19] = { "8890", "91A4" }, + /* Teletex service basic and mixed mode + and Group 4 facsimile service Classes II and III */ + [20] = { "8890", "91A8" }, + /* Teletex service basic and processable mode */ + [21] = { "8890", "91B1" }, + /* Teletex service basic mode */ + [22] = { "8890", "91B2" }, + /* International interworking for Videotex */ + [23] = { "8890", "91B5" }, + /* Telex */ + [24] = { "8890", "91B8" }, + /* Message Handling Systems in accordance with X.400 */ + [25] = { "8890", "91C1" }, + /* OSI application in accordance with X.200 */ + [26] = { "9190A5", "9181" }, + /* 7 kHz telephony */ + [27] = { "9190A5", "916001" }, + /* Video telephony, first connection */ + [28] = { "8890", "916002" }, + /* Video telephony, second connection */ +}; + +/* + * helper functions + * ================ + */ + +/* + * emit unsupported parameter warning + */ +static inline void ignore_cstruct_param(struct cardstate *cs, _cstruct param, + char *msgname, char *paramname) +{ + if (param && *param) + dev_warn(cs->dev, "%s: ignoring unsupported parameter: %s\n", + msgname, paramname); +} + +/* + * check for legal hex digit + */ +static inline int ishexdigit(char c) +{ + if (c >= '0' && c <= '9') + return 1; + if (c >= 'A' && c <= 'F') + return 1; + if (c >= 'a' && c <= 'f') + return 1; + return 0; +} + +/* + * convert hex to binary + */ +static inline u8 hex2bin(char c) +{ + int result = c & 0x0f; + if (c & 0x40) + result += 9; + return result; +} + +/* + * convert an IE from Gigaset hex string to ETSI binary representation + * including length byte + * return value: result length, -1 on error + */ +static int encode_ie(char *in, u8 *out, int maxlen) +{ + int l = 0; + while (*in) { + if (!ishexdigit(in[0]) || !ishexdigit(in[1]) || l >= maxlen) + return -1; + out[++l] = (hex2bin(in[0]) << 4) + hex2bin(in[1]); + in += 2; + } + out[0] = l; + return l; +} + +/* + * convert an IE from ETSI binary representation including length byte + * to Gigaset hex string + */ +static void decode_ie(u8 *in, char *out) +{ + int i = *in; + while (i-- > 0) { + /* ToDo: conversion to upper case necessary? */ + *out++ = toupper(hex_asc_hi(*++in)); + *out++ = toupper(hex_asc_lo(*in)); + } +} + +/* + * retrieve application data structure for an application ID + */ +static inline struct gigaset_capi_appl * +get_appl(struct gigaset_capi_ctr *iif, u16 appl) +{ + struct gigaset_capi_appl *ap; + + list_for_each_entry(ap, &iif->appls, ctrlist) + if (ap->id == appl) + return ap; + return NULL; +} + +/* + * dump CAPI message to kernel messages for debugging + */ +static inline void dump_cmsg(enum debuglevel level, const char *tag, _cmsg *p) +{ +#ifdef CONFIG_GIGASET_DEBUG + _cdebbuf *cdb; + + if (!(gigaset_debuglevel & level)) + return; + + cdb = capi_cmsg2str(p); + if (cdb) { + gig_dbg(level, "%s: [%d] %s", tag, p->ApplId, cdb->buf); + cdebbuf_free(cdb); + } else { + gig_dbg(level, "%s: [%d] %s", tag, p->ApplId, + capi_cmd2str(p->Command, p->Subcommand)); + } +#endif +} + +static inline void dump_rawmsg(enum debuglevel level, const char *tag, + unsigned char *data) +{ +#ifdef CONFIG_GIGASET_DEBUG + char *dbgline; + int i, l; + + if (!(gigaset_debuglevel & level)) + return; + + l = CAPIMSG_LEN(data); + if (l < 12) { + gig_dbg(level, "%s: ??? LEN=%04d", tag, l); + return; + } + gig_dbg(level, "%s: 0x%02x:0x%02x: ID=%03d #0x%04x LEN=%04d NCCI=0x%x", + tag, CAPIMSG_COMMAND(data), CAPIMSG_SUBCOMMAND(data), + CAPIMSG_APPID(data), CAPIMSG_MSGID(data), l, + CAPIMSG_CONTROL(data)); + l -= 12; + dbgline = kmalloc(3*l, GFP_ATOMIC); + if (!dbgline) + return; + for (i = 0; i < l; i++) { + dbgline[3*i] = hex_asc_hi(data[12+i]); + dbgline[3*i+1] = hex_asc_lo(data[12+i]); + dbgline[3*i+2] = ' '; + } + dbgline[3*l-1] = '\0'; + gig_dbg(level, " %s", dbgline); + kfree(dbgline); + if (CAPIMSG_COMMAND(data) == CAPI_DATA_B3 && + (CAPIMSG_SUBCOMMAND(data) == CAPI_REQ || + CAPIMSG_SUBCOMMAND(data) == CAPI_IND) && + CAPIMSG_DATALEN(data) > 0) { + l = CAPIMSG_DATALEN(data); + dbgline = kmalloc(3*l, GFP_ATOMIC); + if (!dbgline) + return; + data += CAPIMSG_LEN(data); + for (i = 0; i < l; i++) { + dbgline[3*i] = hex_asc_hi(data[i]); + dbgline[3*i+1] = hex_asc_lo(data[i]); + dbgline[3*i+2] = ' '; + } + dbgline[3*l-1] = '\0'; + gig_dbg(level, " %s", dbgline); + kfree(dbgline); + } +#endif +} + +/* + * format CAPI IE as string + */ + +static const char *format_ie(const char *ie) +{ + static char result[3*MAX_FMT_IE_LEN]; + int len, count; + char *pout = result; + + if (!ie) + return "NULL"; + + count = len = ie[0]; + if (count > MAX_FMT_IE_LEN) + count = MAX_FMT_IE_LEN-1; + while (count--) { + *pout++ = hex_asc_hi(*++ie); + *pout++ = hex_asc_lo(*ie); + *pout++ = ' '; + } + if (len > MAX_FMT_IE_LEN) { + *pout++ = '.'; + *pout++ = '.'; + *pout++ = '.'; + } + *--pout = 0; + return result; +} + + +/* + * driver interface functions + * ========================== + */ + +/** + * gigaset_skb_sent() - acknowledge transmission of outgoing skb + * @bcs: B channel descriptor structure. + * @skb: sent data. + * + * Called by hardware module {bas,ser,usb}_gigaset when the data in a + * skb has been successfully sent, for signalling completion to the LL. + */ +void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb) +{ + struct cardstate *cs = bcs->cs; + struct gigaset_capi_ctr *iif = cs->iif; + struct gigaset_capi_appl *ap = bcs->ap; + unsigned char *req = skb_mac_header(dskb); + struct sk_buff *cskb; + u16 flags; + + /* update statistics */ + ++bcs->trans_up; + + if (!ap) { + dev_err(cs->dev, "%s: no application\n", __func__); + return; + } + + /* don't send further B3 messages if disconnected */ + if (ap->connected < APCONN_ACTIVE) { + gig_dbg(DEBUG_LLDATA, "disconnected, discarding ack"); + return; + } + + /* ToDo: honor unset "delivery confirmation" bit */ + flags = CAPIMSG_FLAGS(req); + + /* build DATA_B3_CONF message */ + cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC); + if (!cskb) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + return; + } + /* frequent message, avoid _cmsg overhead */ + CAPIMSG_SETLEN(cskb->data, CAPI_DATA_B3_CONF_LEN); + CAPIMSG_SETAPPID(cskb->data, ap->id); + CAPIMSG_SETCOMMAND(cskb->data, CAPI_DATA_B3); + CAPIMSG_SETSUBCOMMAND(cskb->data, CAPI_CONF); + CAPIMSG_SETMSGID(cskb->data, CAPIMSG_MSGID(req)); + CAPIMSG_SETCONTROLLER(cskb->data, iif->ctr.cnr); + CAPIMSG_SETPLCI_PART(cskb->data, bcs->channel + 1); + CAPIMSG_SETNCCI_PART(cskb->data, 1); + CAPIMSG_SETHANDLE_CONF(cskb->data, CAPIMSG_HANDLE_REQ(req)); + if (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION) + CAPIMSG_SETINFO_CONF(cskb->data, + CapiFlagsNotSupportedByProtocol); + else + CAPIMSG_SETINFO_CONF(cskb->data, CAPI_NOERROR); + + /* emit message */ + dump_rawmsg(DEBUG_LLDATA, "DATA_B3_CONF", cskb->data); + capi_ctr_handle_message(&iif->ctr, ap->id, cskb); +} +EXPORT_SYMBOL_GPL(gigaset_skb_sent); + +/** + * gigaset_skb_rcvd() - pass received skb to LL + * @bcs: B channel descriptor structure. + * @skb: received data. + * + * Called by hardware module {bas,ser,usb}_gigaset when user data has + * been successfully received, for passing to the LL. + * Warning: skb must not be accessed anymore! + */ +void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb) +{ + struct cardstate *cs = bcs->cs; + struct gigaset_capi_ctr *iif = cs->iif; + struct gigaset_capi_appl *ap = bcs->ap; + int len = skb->len; + + /* update statistics */ + bcs->trans_down++; + + if (!ap) { + dev_err(cs->dev, "%s: no application\n", __func__); + return; + } + + /* don't send further B3 messages if disconnected */ + if (ap->connected < APCONN_ACTIVE) { + gig_dbg(DEBUG_LLDATA, "disconnected, discarding data"); + dev_kfree_skb_any(skb); + return; + } + + /* + * prepend DATA_B3_IND message to payload + * Parameters: NCCI = 1, all others 0/unused + * frequent message, avoid _cmsg overhead + */ + skb_push(skb, CAPI_DATA_B3_REQ_LEN); + CAPIMSG_SETLEN(skb->data, CAPI_DATA_B3_REQ_LEN); + CAPIMSG_SETAPPID(skb->data, ap->id); + CAPIMSG_SETCOMMAND(skb->data, CAPI_DATA_B3); + CAPIMSG_SETSUBCOMMAND(skb->data, CAPI_IND); + CAPIMSG_SETMSGID(skb->data, ap->nextMessageNumber++); + CAPIMSG_SETCONTROLLER(skb->data, iif->ctr.cnr); + CAPIMSG_SETPLCI_PART(skb->data, bcs->channel + 1); + CAPIMSG_SETNCCI_PART(skb->data, 1); + /* Data parameter not used */ + CAPIMSG_SETDATALEN(skb->data, len); + /* Data handle parameter not used */ + CAPIMSG_SETFLAGS(skb->data, 0); + /* Data64 parameter not present */ + + /* emit message */ + dump_rawmsg(DEBUG_LLDATA, "DATA_B3_IND", skb->data); + capi_ctr_handle_message(&iif->ctr, ap->id, skb); +} +EXPORT_SYMBOL_GPL(gigaset_skb_rcvd); + +/** + * gigaset_isdn_rcv_err() - signal receive error + * @bcs: B channel descriptor structure. + * + * Called by hardware module {bas,ser,usb}_gigaset when a receive error + * has occurred, for signalling to the LL. + */ +void gigaset_isdn_rcv_err(struct bc_state *bcs) +{ + /* if currently ignoring packets, just count down */ + if (bcs->ignore) { + bcs->ignore--; + return; + } + + /* update statistics */ + bcs->corrupted++; + + /* ToDo: signal error -> LL */ +} +EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err); + +/** + * gigaset_isdn_icall() - signal incoming call + * @at_state: connection state structure. + * + * Called by main module at tasklet level to notify the LL that an incoming + * call has been received. @at_state contains the parameters of the call. + * + * Return value: call disposition (ICALL_*) + */ +int gigaset_isdn_icall(struct at_state_t *at_state) +{ + struct cardstate *cs = at_state->cs; + struct bc_state *bcs = at_state->bcs; + struct gigaset_capi_ctr *iif = cs->iif; + struct gigaset_capi_appl *ap; + u32 actCIPmask; + struct sk_buff *skb; + unsigned int msgsize; + int i; + + /* + * ToDo: signal calls without a free B channel, too + * (requires a u8 handle for the at_state structure that can + * be stored in the PLCI and used in the CONNECT_RESP message + * handler to retrieve it) + */ + if (!bcs) + return ICALL_IGNORE; + + /* prepare CONNECT_IND message, using B channel number as PLCI */ + capi_cmsg_header(&iif->hcmsg, 0, CAPI_CONNECT, CAPI_IND, 0, + iif->ctr.cnr | ((bcs->channel + 1) << 8)); + + /* minimum size, all structs empty */ + msgsize = CAPI_CONNECT_IND_BASELEN; + + /* Bearer Capability (mandatory) */ + if (at_state->str_var[STR_ZBC]) { + /* pass on BC from Gigaset */ + if (encode_ie(at_state->str_var[STR_ZBC], iif->bc_buf, + MAX_BC_OCTETS) < 0) { + dev_warn(cs->dev, "RING ignored - bad BC %s\n", + at_state->str_var[STR_ZBC]); + return ICALL_IGNORE; + } + + /* look up corresponding CIP value */ + iif->hcmsg.CIPValue = 0; /* default if nothing found */ + for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++) + if (cip2bchlc[i].bc != NULL && + cip2bchlc[i].hlc == NULL && + !strcmp(cip2bchlc[i].bc, + at_state->str_var[STR_ZBC])) { + iif->hcmsg.CIPValue = i; + break; + } + } else { + /* no BC (internal call): assume CIP 1 (speech, A-law) */ + iif->hcmsg.CIPValue = 1; + encode_ie(cip2bchlc[1].bc, iif->bc_buf, MAX_BC_OCTETS); + } + iif->hcmsg.BC = iif->bc_buf; + msgsize += iif->hcmsg.BC[0]; + + /* High Layer Compatibility (optional) */ + if (at_state->str_var[STR_ZHLC]) { + /* pass on HLC from Gigaset */ + if (encode_ie(at_state->str_var[STR_ZHLC], iif->hlc_buf, + MAX_HLC_OCTETS) < 0) { + dev_warn(cs->dev, "RING ignored - bad HLC %s\n", + at_state->str_var[STR_ZHLC]); + return ICALL_IGNORE; + } + iif->hcmsg.HLC = iif->hlc_buf; + msgsize += iif->hcmsg.HLC[0]; + + /* look up corresponding CIP value */ + /* keep BC based CIP value if none found */ + if (at_state->str_var[STR_ZBC]) + for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++) + if (cip2bchlc[i].hlc != NULL && + !strcmp(cip2bchlc[i].hlc, + at_state->str_var[STR_ZHLC]) && + !strcmp(cip2bchlc[i].bc, + at_state->str_var[STR_ZBC])) { + iif->hcmsg.CIPValue = i; + break; + } + } + + /* Called Party Number (optional) */ + if (at_state->str_var[STR_ZCPN]) { + i = strlen(at_state->str_var[STR_ZCPN]); + if (i > MAX_NUMBER_DIGITS) { + dev_warn(cs->dev, "RING ignored - bad number %s\n", + at_state->str_var[STR_ZBC]); + return ICALL_IGNORE; + } + iif->cdpty_buf[0] = i + 1; + iif->cdpty_buf[1] = 0x80; /* type / numbering plan unknown */ + memcpy(iif->cdpty_buf+2, at_state->str_var[STR_ZCPN], i); + iif->hcmsg.CalledPartyNumber = iif->cdpty_buf; + msgsize += iif->hcmsg.CalledPartyNumber[0]; + } + + /* Calling Party Number (optional) */ + if (at_state->str_var[STR_NMBR]) { + i = strlen(at_state->str_var[STR_NMBR]); + if (i > MAX_NUMBER_DIGITS) { + dev_warn(cs->dev, "RING ignored - bad number %s\n", + at_state->str_var[STR_ZBC]); + return ICALL_IGNORE; + } + iif->cgpty_buf[0] = i + 2; + iif->cgpty_buf[1] = 0x00; /* type / numbering plan unknown */ + iif->cgpty_buf[2] = 0x80; /* pres. allowed, not screened */ + memcpy(iif->cgpty_buf+3, at_state->str_var[STR_NMBR], i); + iif->hcmsg.CallingPartyNumber = iif->cgpty_buf; + msgsize += iif->hcmsg.CallingPartyNumber[0]; + } + + /* remaining parameters (not supported, always left NULL): + * - CalledPartySubaddress + * - CallingPartySubaddress + * - AdditionalInfo + * - BChannelinformation + * - Keypadfacility + * - Useruserdata + * - Facilitydataarray + */ + + gig_dbg(DEBUG_CMD, "icall: PLCI %x CIP %d BC %s", + iif->hcmsg.adr.adrPLCI, iif->hcmsg.CIPValue, + format_ie(iif->hcmsg.BC)); + gig_dbg(DEBUG_CMD, "icall: HLC %s", + format_ie(iif->hcmsg.HLC)); + gig_dbg(DEBUG_CMD, "icall: CgPty %s", + format_ie(iif->hcmsg.CallingPartyNumber)); + gig_dbg(DEBUG_CMD, "icall: CdPty %s", + format_ie(iif->hcmsg.CalledPartyNumber)); + + /* scan application list for matching listeners */ + bcs->ap = NULL; + actCIPmask = 1 | (1 << iif->hcmsg.CIPValue); + list_for_each_entry(ap, &iif->appls, ctrlist) + if (actCIPmask & ap->listenCIPmask) { + /* build CONNECT_IND message for this application */ + iif->hcmsg.ApplId = ap->id; + iif->hcmsg.Messagenumber = ap->nextMessageNumber++; + + skb = alloc_skb(msgsize, GFP_ATOMIC); + if (!skb) { + dev_err(cs->dev, "%s: out of memory\n", + __func__); + break; + } + capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize)); + dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg); + + /* add to listeners on this B channel, update state */ + ap->bcnext = bcs->ap; + bcs->ap = ap; + bcs->chstate |= CHS_NOTIFY_LL; + ap->connected = APCONN_SETUP; + + /* emit message */ + capi_ctr_handle_message(&iif->ctr, ap->id, skb); + } + + /* + * Return "accept" if any listeners. + * Gigaset will send ALERTING. + * There doesn't seem to be a way to avoid this. + */ + return bcs->ap ? ICALL_ACCEPT : ICALL_IGNORE; +} + +/* + * send a DISCONNECT_IND message to an application + * does not sleep, clobbers the controller's hcmsg structure + */ +static void send_disconnect_ind(struct bc_state *bcs, + struct gigaset_capi_appl *ap, u16 reason) +{ + struct cardstate *cs = bcs->cs; + struct gigaset_capi_ctr *iif = cs->iif; + struct sk_buff *skb; + + if (ap->connected == APCONN_NONE) + return; + + capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT, CAPI_IND, + ap->nextMessageNumber++, + iif->ctr.cnr | ((bcs->channel + 1) << 8)); + iif->hcmsg.Reason = reason; + skb = alloc_skb(CAPI_DISCONNECT_IND_LEN, GFP_ATOMIC); + if (!skb) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + return; + } + capi_cmsg2message(&iif->hcmsg, __skb_put(skb, CAPI_DISCONNECT_IND_LEN)); + dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg); + ap->connected = APCONN_NONE; + capi_ctr_handle_message(&iif->ctr, ap->id, skb); +} + +/* + * send a DISCONNECT_B3_IND message to an application + * Parameters: NCCI = 1, NCPI empty, Reason_B3 = 0 + * does not sleep, clobbers the controller's hcmsg structure + */ +static void send_disconnect_b3_ind(struct bc_state *bcs, + struct gigaset_capi_appl *ap) +{ + struct cardstate *cs = bcs->cs; + struct gigaset_capi_ctr *iif = cs->iif; + struct sk_buff *skb; + + /* nothing to do if no logical connection active */ + if (ap->connected < APCONN_ACTIVE) + return; + ap->connected = APCONN_SETUP; + + capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND, + ap->nextMessageNumber++, + iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16)); + skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_ATOMIC); + if (!skb) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + return; + } + capi_cmsg2message(&iif->hcmsg, + __skb_put(skb, CAPI_DISCONNECT_B3_IND_BASELEN)); + dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg); + capi_ctr_handle_message(&iif->ctr, ap->id, skb); +} + +/** + * gigaset_isdn_connD() - signal D channel connect + * @bcs: B channel descriptor structure. + * + * Called by main module at tasklet level to notify the LL that the D channel + * connection has been established. + */ +void gigaset_isdn_connD(struct bc_state *bcs) +{ + struct cardstate *cs = bcs->cs; + struct gigaset_capi_ctr *iif = cs->iif; + struct gigaset_capi_appl *ap = bcs->ap; + struct sk_buff *skb; + unsigned int msgsize; + + if (!ap) { + dev_err(cs->dev, "%s: no application\n", __func__); + return; + } + while (ap->bcnext) { + /* this should never happen */ + dev_warn(cs->dev, "%s: dropping extra application %u\n", + __func__, ap->bcnext->id); + send_disconnect_ind(bcs, ap->bcnext, + CapiCallGivenToOtherApplication); + ap->bcnext = ap->bcnext->bcnext; + } + if (ap->connected == APCONN_NONE) { + dev_warn(cs->dev, "%s: application %u not connected\n", + __func__, ap->id); + return; + } + + /* prepare CONNECT_ACTIVE_IND message + * Note: LLC not supported by device + */ + capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_CONNECT_ACTIVE, CAPI_IND, + ap->nextMessageNumber++, + iif->ctr.cnr | ((bcs->channel + 1) << 8)); + + /* minimum size, all structs empty */ + msgsize = CAPI_CONNECT_ACTIVE_IND_BASELEN; + + /* ToDo: set parameter: Connected number + * (requires ev-layer state machine extension to collect + * ZCON device reply) + */ + + /* build and emit CONNECT_ACTIVE_IND message */ + skb = alloc_skb(msgsize, GFP_ATOMIC); + if (!skb) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + return; + } + capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize)); + dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg); + capi_ctr_handle_message(&iif->ctr, ap->id, skb); +} + +/** + * gigaset_isdn_hupD() - signal D channel hangup + * @bcs: B channel descriptor structure. + * + * Called by main module at tasklet level to notify the LL that the D channel + * connection has been shut down. + */ +void gigaset_isdn_hupD(struct bc_state *bcs) +{ + struct gigaset_capi_appl *ap; + + /* + * ToDo: pass on reason code reported by device + * (requires ev-layer state machine extension to collect + * ZCAU device reply) + */ + for (ap = bcs->ap; ap != NULL; ap = ap->bcnext) { + send_disconnect_b3_ind(bcs, ap); + send_disconnect_ind(bcs, ap, 0); + } + bcs->ap = NULL; +} + +/** + * gigaset_isdn_connB() - signal B channel connect + * @bcs: B channel descriptor structure. + * + * Called by main module at tasklet level to notify the LL that the B channel + * connection has been established. + */ +void gigaset_isdn_connB(struct bc_state *bcs) +{ + struct cardstate *cs = bcs->cs; + struct gigaset_capi_ctr *iif = cs->iif; + struct gigaset_capi_appl *ap = bcs->ap; + struct sk_buff *skb; + unsigned int msgsize; + u8 command; + + if (!ap) { + dev_err(cs->dev, "%s: no application\n", __func__); + return; + } + while (ap->bcnext) { + /* this should never happen */ + dev_warn(cs->dev, "%s: dropping extra application %u\n", + __func__, ap->bcnext->id); + send_disconnect_ind(bcs, ap->bcnext, + CapiCallGivenToOtherApplication); + ap->bcnext = ap->bcnext->bcnext; + } + if (!ap->connected) { + dev_warn(cs->dev, "%s: application %u not connected\n", + __func__, ap->id); + return; + } + + /* + * emit CONNECT_B3_ACTIVE_IND if we already got CONNECT_B3_REQ; + * otherwise we have to emit CONNECT_B3_IND first, and follow up with + * CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP + * Parameters in both cases always: NCCI = 1, NCPI empty + */ + if (ap->connected >= APCONN_ACTIVE) { + command = CAPI_CONNECT_B3_ACTIVE; + msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN; + } else { + command = CAPI_CONNECT_B3; + msgsize = CAPI_CONNECT_B3_IND_BASELEN; + } + capi_cmsg_header(&iif->hcmsg, ap->id, command, CAPI_IND, + ap->nextMessageNumber++, + iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16)); + skb = alloc_skb(msgsize, GFP_ATOMIC); + if (!skb) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + return; + } + capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize)); + dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg); + ap->connected = APCONN_ACTIVE; + capi_ctr_handle_message(&iif->ctr, ap->id, skb); +} + +/** + * gigaset_isdn_hupB() - signal B channel hangup + * @bcs: B channel descriptor structure. + * + * Called by main module to notify the LL that the B channel connection has + * been shut down. + */ +void gigaset_isdn_hupB(struct bc_state *bcs) +{ + struct cardstate *cs = bcs->cs; + struct gigaset_capi_appl *ap = bcs->ap; + + /* ToDo: assure order of DISCONNECT_B3_IND and DISCONNECT_IND ? */ + + if (!ap) { + dev_err(cs->dev, "%s: no application\n", __func__); + return; + } + + send_disconnect_b3_ind(bcs, ap); +} + +/** + * gigaset_isdn_start() - signal device availability + * @cs: device descriptor structure. + * + * Called by main module to notify the LL that the device is available for + * use. + */ +void gigaset_isdn_start(struct cardstate *cs) +{ + struct gigaset_capi_ctr *iif = cs->iif; + + /* fill profile data: manufacturer name */ + strcpy(iif->ctr.manu, "Siemens"); + /* CAPI and device version */ + iif->ctr.version.majorversion = 2; /* CAPI 2.0 */ + iif->ctr.version.minorversion = 0; + /* ToDo: check/assert cs->gotfwver? */ + iif->ctr.version.majormanuversion = cs->fwver[0]; + iif->ctr.version.minormanuversion = cs->fwver[1]; + /* number of B channels supported */ + iif->ctr.profile.nbchannel = cs->channels; + /* global options: internal controller, supplementary services */ + iif->ctr.profile.goptions = 0x11; + /* B1 protocols: 64 kbit/s HDLC or transparent */ + iif->ctr.profile.support1 = 0x03; + /* B2 protocols: transparent only */ + /* ToDo: X.75 SLP ? */ + iif->ctr.profile.support2 = 0x02; + /* B3 protocols: transparent only */ + iif->ctr.profile.support3 = 0x01; + /* no serial number */ + strcpy(iif->ctr.serial, "0"); + capi_ctr_ready(&iif->ctr); +} + +/** + * gigaset_isdn_stop() - signal device unavailability + * @cs: device descriptor structure. + * + * Called by main module to notify the LL that the device is no longer + * available for use. + */ +void gigaset_isdn_stop(struct cardstate *cs) +{ + struct gigaset_capi_ctr *iif = cs->iif; + capi_ctr_down(&iif->ctr); +} + +/* + * kernel CAPI callback methods + * ============================ + */ + +/* + * load firmware + */ +static int gigaset_load_firmware(struct capi_ctr *ctr, capiloaddata *data) +{ + struct cardstate *cs = ctr->driverdata; + + /* AVM specific operation, not needed for Gigaset -- ignore */ + dev_notice(cs->dev, "load_firmware ignored\n"); + + return 0; +} + +/* + * reset (deactivate) controller + */ +static void gigaset_reset_ctr(struct capi_ctr *ctr) +{ + struct cardstate *cs = ctr->driverdata; + + /* AVM specific operation, not needed for Gigaset -- ignore */ + dev_notice(cs->dev, "reset_ctr ignored\n"); +} + +/* + * register CAPI application + */ +static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl, + capi_register_params *rp) +{ + struct gigaset_capi_ctr *iif + = container_of(ctr, struct gigaset_capi_ctr, ctr); + struct cardstate *cs = ctr->driverdata; + struct gigaset_capi_appl *ap; + + list_for_each_entry(ap, &iif->appls, ctrlist) + if (ap->id == appl) { + dev_notice(cs->dev, + "application %u already registered\n", appl); + return; + } + + ap = kzalloc(sizeof(*ap), GFP_KERNEL); + if (!ap) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + return; + } + ap->id = appl; + + list_add(&ap->ctrlist, &iif->appls); +} + +/* + * release CAPI application + */ +static void gigaset_release_appl(struct capi_ctr *ctr, u16 appl) +{ + struct gigaset_capi_ctr *iif + = container_of(ctr, struct gigaset_capi_ctr, ctr); + struct cardstate *cs = iif->ctr.driverdata; + struct gigaset_capi_appl *ap, *tmp; + + list_for_each_entry_safe(ap, tmp, &iif->appls, ctrlist) + if (ap->id == appl) { + if (ap->connected != APCONN_NONE) { + dev_err(cs->dev, + "%s: application %u still connected\n", + __func__, ap->id); + /* ToDo: clear active connection */ + } + list_del(&ap->ctrlist); + kfree(ap); + } + +} + +/* + * ===================================================================== + * outgoing CAPI message handler + * ===================================================================== + */ + +/* + * helper function: emit reply message with given Info value + */ +static void send_conf(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb, + u16 info) +{ + /* + * _CONF replies always only have NCCI and Info parameters + * so they'll fit into the _REQ message skb + */ + capi_cmsg_answer(&iif->acmsg); + iif->acmsg.Info = info; + capi_cmsg2message(&iif->acmsg, skb->data); + __skb_trim(skb, CAPI_STDCONF_LEN); + dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg); + capi_ctr_handle_message(&iif->ctr, ap->id, skb); +} + +/* + * process FACILITY_REQ message + */ +static void do_facility_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + struct cardstate *cs = iif->ctr.driverdata; + _cmsg *cmsg = &iif->acmsg; + struct sk_buff *cskb; + u8 *pparam; + unsigned int msgsize = CAPI_FACILITY_CONF_BASELEN; + u16 function, info; + static u8 confparam[10]; /* max. 9 octets + length byte */ + + /* decode message */ + capi_message2cmsg(cmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + + /* + * Facility Request Parameter is not decoded by capi_message2cmsg() + * encoding depends on Facility Selector + */ + switch (cmsg->FacilitySelector) { + case CAPI_FACILITY_DTMF: /* ToDo */ + info = CapiFacilityNotSupported; + confparam[0] = 2; /* length */ + /* DTMF information: Unknown DTMF request */ + capimsg_setu16(confparam, 1, 2); + break; + + case CAPI_FACILITY_V42BIS: /* not supported */ + info = CapiFacilityNotSupported; + confparam[0] = 2; /* length */ + /* V.42 bis information: not available */ + capimsg_setu16(confparam, 1, 1); + break; + + case CAPI_FACILITY_SUPPSVC: + /* decode Function parameter */ + pparam = cmsg->FacilityRequestParameter; + if (pparam == NULL || *pparam < 2) { + dev_notice(cs->dev, "%s: %s missing\n", "FACILITY_REQ", + "Facility Request Parameter"); + send_conf(iif, ap, skb, CapiIllMessageParmCoding); + return; + } + function = CAPIMSG_U16(pparam, 1); + switch (function) { + case CAPI_SUPPSVC_GETSUPPORTED: + info = CapiSuccess; + /* Supplementary Service specific parameter */ + confparam[3] = 6; /* length */ + /* Supplementary services info: Success */ + capimsg_setu16(confparam, 4, CapiSuccess); + /* Supported Services: none */ + capimsg_setu32(confparam, 6, 0); + break; + /* ToDo: add supported services */ + default: + info = CapiFacilitySpecificFunctionNotSupported; + /* Supplementary Service specific parameter */ + confparam[3] = 2; /* length */ + /* Supplementary services info: not supported */ + capimsg_setu16(confparam, 4, + CapiSupplementaryServiceNotSupported); + } + + /* Facility confirmation parameter */ + confparam[0] = confparam[3] + 3; /* total length */ + /* Function: copy from _REQ message */ + capimsg_setu16(confparam, 1, function); + /* Supplementary Service specific parameter already set above */ + break; + + case CAPI_FACILITY_WAKEUP: /* ToDo */ + info = CapiFacilityNotSupported; + confparam[0] = 2; /* length */ + /* Number of accepted awake request parameters: 0 */ + capimsg_setu16(confparam, 1, 0); + break; + + default: + info = CapiFacilityNotSupported; + confparam[0] = 0; /* empty struct */ + } + + /* send FACILITY_CONF with given Info and confirmation parameter */ + capi_cmsg_answer(cmsg); + cmsg->Info = info; + cmsg->FacilityConfirmationParameter = confparam; + msgsize += confparam[0]; /* length */ + cskb = alloc_skb(msgsize, GFP_ATOMIC); + if (!cskb) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + return; + } + capi_cmsg2message(cmsg, __skb_put(cskb, msgsize)); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + capi_ctr_handle_message(&iif->ctr, ap->id, cskb); +} + + +/* + * process LISTEN_REQ message + * just store the masks in the application data structure + */ +static void do_listen_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + /* decode message */ + capi_message2cmsg(&iif->acmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg); + + /* store listening parameters */ + ap->listenInfoMask = iif->acmsg.InfoMask; + ap->listenCIPmask = iif->acmsg.CIPmask; + send_conf(iif, ap, skb, CapiSuccess); +} + +/* + * process ALERT_REQ message + * nothing to do, Gigaset always alerts anyway + */ +static void do_alert_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + /* decode message */ + capi_message2cmsg(&iif->acmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg); + send_conf(iif, ap, skb, CapiAlertAlreadySent); +} + +/* + * process CONNECT_REQ message + * allocate a B channel, prepare dial commands, queue a DIAL event, + * emit CONNECT_CONF reply + */ +static void do_connect_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + struct cardstate *cs = iif->ctr.driverdata; + _cmsg *cmsg = &iif->acmsg; + struct bc_state *bcs; + char **commands; + char *s; + u8 *pp; + int i, l; + u16 info; + + /* decode message */ + capi_message2cmsg(cmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + + /* get free B channel & construct PLCI */ + bcs = gigaset_get_free_channel(cs); + if (!bcs) { + dev_notice(cs->dev, "%s: no B channel available\n", + "CONNECT_REQ"); + send_conf(iif, ap, skb, CapiNoPlciAvailable); + return; + } + ap->bcnext = NULL; + bcs->ap = ap; + cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8; + + /* build command table */ + commands = kzalloc(AT_NUM*(sizeof *commands), GFP_KERNEL); + if (!commands) + goto oom; + + /* encode parameter: Called party number */ + pp = cmsg->CalledPartyNumber; + if (pp == NULL || *pp == 0) { + dev_notice(cs->dev, "%s: %s missing\n", + "CONNECT_REQ", "Called party number"); + info = CapiIllMessageParmCoding; + goto error; + } + l = *pp++; + /* check type of number/numbering plan byte */ + switch (*pp) { + case 0x80: /* unknown type / unknown numbering plan */ + case 0x81: /* unknown type / ISDN/Telephony numbering plan */ + break; + default: /* others: warn about potential misinterpretation */ + dev_notice(cs->dev, "%s: %s type/plan 0x%02x unsupported\n", + "CONNECT_REQ", "Called party number", *pp); + } + pp++; + l--; + /* translate "**" internal call prefix to CTP value */ + if (l >= 2 && pp[0] == '*' && pp[1] == '*') { + s = "^SCTP=0\r"; + pp += 2; + l -= 2; + } else { + s = "^SCTP=1\r"; + } + commands[AT_TYPE] = kstrdup(s, GFP_KERNEL); + if (!commands[AT_TYPE]) + goto oom; + commands[AT_DIAL] = kmalloc(l+3, GFP_KERNEL); + if (!commands[AT_DIAL]) + goto oom; + snprintf(commands[AT_DIAL], l+3, "D%.*s\r", l, pp); + + /* encode parameter: Calling party number */ + pp = cmsg->CallingPartyNumber; + if (pp != NULL && *pp > 0) { + l = *pp++; + + /* check type of number/numbering plan byte */ + /* ToDo: allow for/handle Ext=1? */ + switch (*pp) { + case 0x00: /* unknown type / unknown numbering plan */ + case 0x01: /* unknown type / ISDN/Telephony num. plan */ + break; + default: + dev_notice(cs->dev, + "%s: %s type/plan 0x%02x unsupported\n", + "CONNECT_REQ", "Calling party number", *pp); + } + pp++; + l--; + + /* check presentation indicator */ + if (!l) { + dev_notice(cs->dev, "%s: %s IE truncated\n", + "CONNECT_REQ", "Calling party number"); + info = CapiIllMessageParmCoding; + goto error; + } + switch (*pp & 0xfc) { /* ignore Screening indicator */ + case 0x80: /* Presentation allowed */ + s = "^SCLIP=1\r"; + break; + case 0xa0: /* Presentation restricted */ + s = "^SCLIP=0\r"; + break; + default: + dev_notice(cs->dev, "%s: invalid %s 0x%02x\n", + "CONNECT_REQ", + "Presentation/Screening indicator", + *pp); + s = "^SCLIP=1\r"; + } + commands[AT_CLIP] = kstrdup(s, GFP_KERNEL); + if (!commands[AT_CLIP]) + goto oom; + pp++; + l--; + + if (l) { + /* number */ + commands[AT_MSN] = kmalloc(l+8, GFP_KERNEL); + if (!commands[AT_MSN]) + goto oom; + snprintf(commands[AT_MSN], l+8, "^SMSN=%*s\r", l, pp); + } + } + + /* check parameter: CIP Value */ + if (cmsg->CIPValue > ARRAY_SIZE(cip2bchlc) || + (cmsg->CIPValue > 0 && cip2bchlc[cmsg->CIPValue].bc == NULL)) { + dev_notice(cs->dev, "%s: unknown CIP value %d\n", + "CONNECT_REQ", cmsg->CIPValue); + info = CapiCipValueUnknown; + goto error; + } + + /* check/encode parameter: BC */ + if (cmsg->BC && cmsg->BC[0]) { + /* explicit BC overrides CIP */ + l = 2*cmsg->BC[0] + 7; + commands[AT_BC] = kmalloc(l, GFP_KERNEL); + if (!commands[AT_BC]) + goto oom; + strcpy(commands[AT_BC], "^SBC="); + decode_ie(cmsg->BC, commands[AT_BC]+5); + strcpy(commands[AT_BC] + l - 2, "\r"); + } else if (cip2bchlc[cmsg->CIPValue].bc) { + l = strlen(cip2bchlc[cmsg->CIPValue].bc) + 7; + commands[AT_BC] = kmalloc(l, GFP_KERNEL); + if (!commands[AT_BC]) + goto oom; + snprintf(commands[AT_BC], l, "^SBC=%s\r", + cip2bchlc[cmsg->CIPValue].bc); + } + + /* check/encode parameter: HLC */ + if (cmsg->HLC && cmsg->HLC[0]) { + /* explicit HLC overrides CIP */ + l = 2*cmsg->HLC[0] + 7; + commands[AT_HLC] = kmalloc(l, GFP_KERNEL); + if (!commands[AT_HLC]) + goto oom; + strcpy(commands[AT_HLC], "^SHLC="); + decode_ie(cmsg->HLC, commands[AT_HLC]+5); + strcpy(commands[AT_HLC] + l - 2, "\r"); + } else if (cip2bchlc[cmsg->CIPValue].hlc) { + l = strlen(cip2bchlc[cmsg->CIPValue].hlc) + 7; + commands[AT_HLC] = kmalloc(l, GFP_KERNEL); + if (!commands[AT_HLC]) + goto oom; + snprintf(commands[AT_HLC], l, "^SHLC=%s\r", + cip2bchlc[cmsg->CIPValue].hlc); + } + + /* check/encode parameter: B Protocol */ + if (cmsg->BProtocol == CAPI_DEFAULT) { + bcs->proto2 = L2_HDLC; + dev_warn(cs->dev, + "B2 Protocol X.75 SLP unsupported, using Transparent\n"); + } else { + switch (cmsg->B1protocol) { + case 0: + bcs->proto2 = L2_HDLC; + break; + case 1: + bcs->proto2 = L2_BITSYNC; + break; + default: + dev_warn(cs->dev, + "B1 Protocol %u unsupported, using Transparent\n", + cmsg->B1protocol); + bcs->proto2 = L2_BITSYNC; + } + if (cmsg->B2protocol != 1) + dev_warn(cs->dev, + "B2 Protocol %u unsupported, using Transparent\n", + cmsg->B2protocol); + if (cmsg->B3protocol != 0) + dev_warn(cs->dev, + "B3 Protocol %u unsupported, using Transparent\n", + cmsg->B3protocol); + ignore_cstruct_param(cs, cmsg->B1configuration, + "CONNECT_REQ", "B1 Configuration"); + ignore_cstruct_param(cs, cmsg->B2configuration, + "CONNECT_REQ", "B2 Configuration"); + ignore_cstruct_param(cs, cmsg->B3configuration, + "CONNECT_REQ", "B3 Configuration"); + } + commands[AT_PROTO] = kmalloc(9, GFP_KERNEL); + if (!commands[AT_PROTO]) + goto oom; + snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2); + + /* ToDo: check/encode remaining parameters */ + ignore_cstruct_param(cs, cmsg->CalledPartySubaddress, + "CONNECT_REQ", "Called pty subaddr"); + ignore_cstruct_param(cs, cmsg->CallingPartySubaddress, + "CONNECT_REQ", "Calling pty subaddr"); + ignore_cstruct_param(cs, cmsg->LLC, + "CONNECT_REQ", "LLC"); + if (cmsg->AdditionalInfo != CAPI_DEFAULT) { + ignore_cstruct_param(cs, cmsg->BChannelinformation, + "CONNECT_REQ", "B Channel Information"); + ignore_cstruct_param(cs, cmsg->Keypadfacility, + "CONNECT_REQ", "Keypad Facility"); + ignore_cstruct_param(cs, cmsg->Useruserdata, + "CONNECT_REQ", "User-User Data"); + ignore_cstruct_param(cs, cmsg->Facilitydataarray, + "CONNECT_REQ", "Facility Data Array"); + } + + /* encode parameter: B channel to use */ + commands[AT_ISO] = kmalloc(9, GFP_KERNEL); + if (!commands[AT_ISO]) + goto oom; + snprintf(commands[AT_ISO], 9, "^SISO=%u\r", + (unsigned) bcs->channel + 1); + + /* queue & schedule EV_DIAL event */ + if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands, + bcs->at_state.seq_index, NULL)) + goto oom; + gig_dbg(DEBUG_CMD, "scheduling DIAL"); + gigaset_schedule_event(cs); + ap->connected = APCONN_SETUP; + send_conf(iif, ap, skb, CapiSuccess); + return; + +oom: + dev_err(cs->dev, "%s: out of memory\n", __func__); + info = CAPI_MSGOSRESOURCEERR; +error: + if (commands) + for (i = 0; i < AT_NUM; i++) + kfree(commands[i]); + kfree(commands); + gigaset_free_channel(bcs); + send_conf(iif, ap, skb, info); +} + +/* + * process CONNECT_RESP message + * checks protocol parameters and queues an ACCEPT or HUP event + */ +static void do_connect_resp(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + struct cardstate *cs = iif->ctr.driverdata; + _cmsg *cmsg = &iif->acmsg; + struct bc_state *bcs; + struct gigaset_capi_appl *oap; + int channel; + + /* decode message */ + capi_message2cmsg(cmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + dev_kfree_skb_any(skb); + + /* extract and check channel number from PLCI */ + channel = (cmsg->adr.adrPLCI >> 8) & 0xff; + if (!channel || channel > cs->channels) { + dev_notice(cs->dev, "%s: invalid %s 0x%02x\n", + "CONNECT_RESP", "PLCI", cmsg->adr.adrPLCI); + return; + } + bcs = cs->bcs + channel - 1; + + switch (cmsg->Reject) { + case 0: /* Accept */ + /* drop all competing applications, keep only this one */ + for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) + if (oap != ap) + send_disconnect_ind(bcs, oap, + CapiCallGivenToOtherApplication); + ap->bcnext = NULL; + bcs->ap = ap; + bcs->chstate |= CHS_NOTIFY_LL; + + /* check/encode B channel protocol */ + if (cmsg->BProtocol == CAPI_DEFAULT) { + bcs->proto2 = L2_HDLC; + dev_warn(cs->dev, + "B2 Protocol X.75 SLP unsupported, using Transparent\n"); + } else { + switch (cmsg->B1protocol) { + case 0: + bcs->proto2 = L2_HDLC; + break; + case 1: + bcs->proto2 = L2_BITSYNC; + break; + default: + dev_warn(cs->dev, + "B1 Protocol %u unsupported, using Transparent\n", + cmsg->B1protocol); + bcs->proto2 = L2_BITSYNC; + } + if (cmsg->B2protocol != 1) + dev_warn(cs->dev, + "B2 Protocol %u unsupported, using Transparent\n", + cmsg->B2protocol); + if (cmsg->B3protocol != 0) + dev_warn(cs->dev, + "B3 Protocol %u unsupported, using Transparent\n", + cmsg->B3protocol); + ignore_cstruct_param(cs, cmsg->B1configuration, + "CONNECT_RESP", "B1 Configuration"); + ignore_cstruct_param(cs, cmsg->B2configuration, + "CONNECT_RESP", "B2 Configuration"); + ignore_cstruct_param(cs, cmsg->B3configuration, + "CONNECT_RESP", "B3 Configuration"); + } + + /* ToDo: check/encode remaining parameters */ + ignore_cstruct_param(cs, cmsg->ConnectedNumber, + "CONNECT_RESP", "Connected Number"); + ignore_cstruct_param(cs, cmsg->ConnectedSubaddress, + "CONNECT_RESP", "Connected Subaddress"); + ignore_cstruct_param(cs, cmsg->LLC, + "CONNECT_RESP", "LLC"); + if (cmsg->AdditionalInfo != CAPI_DEFAULT) { + ignore_cstruct_param(cs, cmsg->BChannelinformation, + "CONNECT_RESP", "BChannel Information"); + ignore_cstruct_param(cs, cmsg->Keypadfacility, + "CONNECT_RESP", "Keypad Facility"); + ignore_cstruct_param(cs, cmsg->Useruserdata, + "CONNECT_RESP", "User-User Data"); + ignore_cstruct_param(cs, cmsg->Facilitydataarray, + "CONNECT_RESP", "Facility Data Array"); + } + + /* Accept call */ + if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state, + EV_ACCEPT, NULL, 0, NULL)) + return; + gig_dbg(DEBUG_CMD, "scheduling ACCEPT"); + gigaset_schedule_event(cs); + return; + + case 1: /* Ignore */ + /* send DISCONNECT_IND to this application */ + send_disconnect_ind(bcs, ap, 0); + + /* remove it from the list of listening apps */ + if (bcs->ap == ap) { + bcs->ap = ap->bcnext; + if (bcs->ap == NULL) + /* last one: stop ev-layer hupD notifications */ + bcs->chstate &= ~CHS_NOTIFY_LL; + return; + } + for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) { + if (oap->bcnext == ap) { + oap->bcnext = oap->bcnext->bcnext; + return; + } + } + dev_err(cs->dev, "%s: application %u not found\n", + __func__, ap->id); + return; + + default: /* Reject */ + /* drop all competing applications, keep only this one */ + for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) + if (oap != ap) + send_disconnect_ind(bcs, oap, + CapiCallGivenToOtherApplication); + ap->bcnext = NULL; + bcs->ap = ap; + + /* reject call - will trigger DISCONNECT_IND for this app */ + dev_info(cs->dev, "%s: Reject=%x\n", + "CONNECT_RESP", cmsg->Reject); + if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state, + EV_HUP, NULL, 0, NULL)) + return; + gig_dbg(DEBUG_CMD, "scheduling HUP"); + gigaset_schedule_event(cs); + return; + } +} + +/* + * process CONNECT_B3_REQ message + * build NCCI and emit CONNECT_B3_CONF reply + */ +static void do_connect_b3_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + struct cardstate *cs = iif->ctr.driverdata; + _cmsg *cmsg = &iif->acmsg; + int channel; + + /* decode message */ + capi_message2cmsg(cmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + + /* extract and check channel number from PLCI */ + channel = (cmsg->adr.adrPLCI >> 8) & 0xff; + if (!channel || channel > cs->channels) { + dev_notice(cs->dev, "%s: invalid %s 0x%02x\n", + "CONNECT_B3_REQ", "PLCI", cmsg->adr.adrPLCI); + send_conf(iif, ap, skb, CapiIllContrPlciNcci); + return; + } + + /* mark logical connection active */ + ap->connected = APCONN_ACTIVE; + + /* build NCCI: always 1 (one B3 connection only) */ + cmsg->adr.adrNCCI |= 1 << 16; + + /* NCPI parameter: not applicable for B3 Transparent */ + ignore_cstruct_param(cs, cmsg->NCPI, "CONNECT_B3_REQ", "NCPI"); + send_conf(iif, ap, skb, (cmsg->NCPI && cmsg->NCPI[0]) ? + CapiNcpiNotSupportedByProtocol : CapiSuccess); +} + +/* + * process CONNECT_B3_RESP message + * Depending on the Reject parameter, either emit CONNECT_B3_ACTIVE_IND + * or queue EV_HUP and emit DISCONNECT_B3_IND. + * The emitted message is always shorter than the received one, + * allowing to reuse the skb. + */ +static void do_connect_b3_resp(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + struct cardstate *cs = iif->ctr.driverdata; + _cmsg *cmsg = &iif->acmsg; + struct bc_state *bcs; + int channel; + unsigned int msgsize; + u8 command; + + /* decode message */ + capi_message2cmsg(cmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + + /* extract and check channel number and NCCI */ + channel = (cmsg->adr.adrNCCI >> 8) & 0xff; + if (!channel || channel > cs->channels || + ((cmsg->adr.adrNCCI >> 16) & 0xffff) != 1) { + dev_notice(cs->dev, "%s: invalid %s 0x%02x\n", + "CONNECT_B3_RESP", "NCCI", cmsg->adr.adrNCCI); + dev_kfree_skb_any(skb); + return; + } + bcs = &cs->bcs[channel-1]; + + if (cmsg->Reject) { + /* Reject: clear B3 connect received flag */ + ap->connected = APCONN_SETUP; + + /* trigger hangup, causing eventual DISCONNECT_IND */ + if (!gigaset_add_event(cs, &bcs->at_state, + EV_HUP, NULL, 0, NULL)) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + dev_kfree_skb_any(skb); + return; + } + gig_dbg(DEBUG_CMD, "scheduling HUP"); + gigaset_schedule_event(cs); + + /* emit DISCONNECT_B3_IND */ + command = CAPI_DISCONNECT_B3; + msgsize = CAPI_DISCONNECT_B3_IND_BASELEN; + } else { + /* + * Accept: emit CONNECT_B3_ACTIVE_IND immediately, as + * we only send CONNECT_B3_IND if the B channel is up + */ + command = CAPI_CONNECT_B3_ACTIVE; + msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN; + } + capi_cmsg_header(cmsg, ap->id, command, CAPI_IND, + ap->nextMessageNumber++, cmsg->adr.adrNCCI); + __skb_trim(skb, msgsize); + capi_cmsg2message(cmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + capi_ctr_handle_message(&iif->ctr, ap->id, skb); +} + +/* + * process DISCONNECT_REQ message + * schedule EV_HUP and emit DISCONNECT_B3_IND if necessary, + * emit DISCONNECT_CONF reply + */ +static void do_disconnect_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + struct cardstate *cs = iif->ctr.driverdata; + _cmsg *cmsg = &iif->acmsg; + struct bc_state *bcs; + _cmsg *b3cmsg; + struct sk_buff *b3skb; + int channel; + + /* decode message */ + capi_message2cmsg(cmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + + /* extract and check channel number from PLCI */ + channel = (cmsg->adr.adrPLCI >> 8) & 0xff; + if (!channel || channel > cs->channels) { + dev_notice(cs->dev, "%s: invalid %s 0x%02x\n", + "DISCONNECT_REQ", "PLCI", cmsg->adr.adrPLCI); + send_conf(iif, ap, skb, CapiIllContrPlciNcci); + return; + } + bcs = cs->bcs + channel - 1; + + /* ToDo: process parameter: Additional info */ + if (cmsg->AdditionalInfo != CAPI_DEFAULT) { + ignore_cstruct_param(cs, cmsg->BChannelinformation, + "DISCONNECT_REQ", "B Channel Information"); + ignore_cstruct_param(cs, cmsg->Keypadfacility, + "DISCONNECT_REQ", "Keypad Facility"); + ignore_cstruct_param(cs, cmsg->Useruserdata, + "DISCONNECT_REQ", "User-User Data"); + ignore_cstruct_param(cs, cmsg->Facilitydataarray, + "DISCONNECT_REQ", "Facility Data Array"); + } + + /* skip if DISCONNECT_IND already sent */ + if (!ap->connected) + return; + + /* check for active logical connection */ + if (ap->connected >= APCONN_ACTIVE) { + /* + * emit DISCONNECT_B3_IND with cause 0x3301 + * use separate cmsg structure, as the content of iif->acmsg + * is still needed for creating the _CONF message + */ + b3cmsg = kmalloc(sizeof(*b3cmsg), GFP_KERNEL); + if (!b3cmsg) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); + return; + } + capi_cmsg_header(b3cmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND, + ap->nextMessageNumber++, + cmsg->adr.adrPLCI | (1 << 16)); + b3cmsg->Reason_B3 = CapiProtocolErrorLayer1; + b3skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_KERNEL); + if (b3skb == NULL) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); + return; + } + capi_cmsg2message(b3cmsg, + __skb_put(b3skb, CAPI_DISCONNECT_B3_IND_BASELEN)); + kfree(b3cmsg); + capi_ctr_handle_message(&iif->ctr, ap->id, b3skb); + } + + /* trigger hangup, causing eventual DISCONNECT_IND */ + if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); + return; + } + gig_dbg(DEBUG_CMD, "scheduling HUP"); + gigaset_schedule_event(cs); + + /* emit reply */ + send_conf(iif, ap, skb, CapiSuccess); +} + +/* + * process DISCONNECT_B3_REQ message + * schedule EV_HUP and emit DISCONNECT_B3_CONF reply + */ +static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + struct cardstate *cs = iif->ctr.driverdata; + _cmsg *cmsg = &iif->acmsg; + int channel; + + /* decode message */ + capi_message2cmsg(cmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, cmsg); + + /* extract and check channel number and NCCI */ + channel = (cmsg->adr.adrNCCI >> 8) & 0xff; + if (!channel || channel > cs->channels || + ((cmsg->adr.adrNCCI >> 16) & 0xffff) != 1) { + dev_notice(cs->dev, "%s: invalid %s 0x%02x\n", + "DISCONNECT_B3_REQ", "NCCI", cmsg->adr.adrNCCI); + send_conf(iif, ap, skb, CapiIllContrPlciNcci); + return; + } + + /* reject if logical connection not active */ + if (ap->connected < APCONN_ACTIVE) { + send_conf(iif, ap, skb, + CapiMessageNotSupportedInCurrentState); + return; + } + + /* trigger hangup, causing eventual DISCONNECT_B3_IND */ + if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state, + EV_HUP, NULL, 0, NULL)) { + dev_err(cs->dev, "%s: out of memory\n", __func__); + send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); + return; + } + gig_dbg(DEBUG_CMD, "scheduling HUP"); + gigaset_schedule_event(cs); + + /* NCPI parameter: not applicable for B3 Transparent */ + ignore_cstruct_param(cs, cmsg->NCPI, + "DISCONNECT_B3_REQ", "NCPI"); + send_conf(iif, ap, skb, (cmsg->NCPI && cmsg->NCPI[0]) ? + CapiNcpiNotSupportedByProtocol : CapiSuccess); +} + +/* + * process DATA_B3_REQ message + */ +static void do_data_b3_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + struct cardstate *cs = iif->ctr.driverdata; + int channel = CAPIMSG_PLCI_PART(skb->data); + u16 ncci = CAPIMSG_NCCI_PART(skb->data); + u16 msglen = CAPIMSG_LEN(skb->data); + u16 datalen = CAPIMSG_DATALEN(skb->data); + u16 flags = CAPIMSG_FLAGS(skb->data); + + /* frequent message, avoid _cmsg overhead */ + dump_rawmsg(DEBUG_LLDATA, "DATA_B3_REQ", skb->data); + + gig_dbg(DEBUG_LLDATA, + "Receiving data from LL (ch: %d, flg: %x, sz: %d|%d)", + channel, flags, msglen, datalen); + + /* check parameters */ + if (channel == 0 || channel > cs->channels || ncci != 1) { + dev_notice(cs->dev, "%s: invalid %s 0x%02x\n", + "DATA_B3_REQ", "NCCI", CAPIMSG_NCCI(skb->data)); + send_conf(iif, ap, skb, CapiIllContrPlciNcci); + return; + } + if (msglen != CAPI_DATA_B3_REQ_LEN && msglen != CAPI_DATA_B3_REQ_LEN64) + dev_notice(cs->dev, "%s: unexpected length %d\n", + "DATA_B3_REQ", msglen); + if (msglen + datalen != skb->len) + dev_notice(cs->dev, "%s: length mismatch (%d+%d!=%d)\n", + "DATA_B3_REQ", msglen, datalen, skb->len); + if (msglen + datalen > skb->len) { + /* message too short for announced data length */ + send_conf(iif, ap, skb, CapiIllMessageParmCoding); /* ? */ + return; + } + if (flags & CAPI_FLAGS_RESERVED) { + dev_notice(cs->dev, "%s: reserved flags set (%x)\n", + "DATA_B3_REQ", flags); + send_conf(iif, ap, skb, CapiIllMessageParmCoding); + return; + } + + /* reject if logical connection not active */ + if (ap->connected < APCONN_ACTIVE) { + send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState); + return; + } + + /* pull CAPI message into link layer header */ + skb_reset_mac_header(skb); + skb->mac_len = msglen; + skb_pull(skb, msglen); + + /* pass to device-specific module */ + if (cs->ops->send_skb(&cs->bcs[channel-1], skb) < 0) { + send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); + return; + } + + /* DATA_B3_CONF reply will be sent by gigaset_skb_sent() */ + + /* + * ToDo: honor unset "delivery confirmation" bit + * (send DATA_B3_CONF immediately?) + */ +} + +/* + * process RESET_B3_REQ message + * just always reply "not supported by current protocol" + */ +static void do_reset_b3_req(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + /* decode message */ + capi_message2cmsg(&iif->acmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg); + send_conf(iif, ap, skb, + CapiResetProcedureNotSupportedByCurrentProtocol); +} + +/* + * dump unsupported/ignored messages at most twice per minute, + * some apps send those very frequently + */ +static unsigned long ignored_msg_dump_time; + +/* + * unsupported CAPI message handler + */ +static void do_unsupported(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + /* decode message */ + capi_message2cmsg(&iif->acmsg, skb->data); + if (printk_timed_ratelimit(&ignored_msg_dump_time, 30 * 1000)) + dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg); + send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState); +} + +/* + * CAPI message handler: no-op + */ +static void do_nothing(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + if (printk_timed_ratelimit(&ignored_msg_dump_time, 30 * 1000)) { + /* decode message */ + capi_message2cmsg(&iif->acmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg); + } + dev_kfree_skb_any(skb); +} + +static void do_data_b3_resp(struct gigaset_capi_ctr *iif, + struct gigaset_capi_appl *ap, + struct sk_buff *skb) +{ + dump_rawmsg(DEBUG_LLDATA, __func__, skb->data); + dev_kfree_skb_any(skb); +} + +/* table of outgoing CAPI message handlers with lookup function */ +typedef void (*capi_send_handler_t)(struct gigaset_capi_ctr *, + struct gigaset_capi_appl *, + struct sk_buff *); + +static struct { + u16 cmd; + capi_send_handler_t handler; +} capi_send_handler_table[] = { + /* most frequent messages first for faster lookup */ + { CAPI_DATA_B3_REQ, do_data_b3_req }, + { CAPI_DATA_B3_RESP, do_data_b3_resp }, + + { CAPI_ALERT_REQ, do_alert_req }, + { CAPI_CONNECT_ACTIVE_RESP, do_nothing }, + { CAPI_CONNECT_B3_ACTIVE_RESP, do_nothing }, + { CAPI_CONNECT_B3_REQ, do_connect_b3_req }, + { CAPI_CONNECT_B3_RESP, do_connect_b3_resp }, + { CAPI_CONNECT_B3_T90_ACTIVE_RESP, do_nothing }, + { CAPI_CONNECT_REQ, do_connect_req }, + { CAPI_CONNECT_RESP, do_connect_resp }, + { CAPI_DISCONNECT_B3_REQ, do_disconnect_b3_req }, + { CAPI_DISCONNECT_B3_RESP, do_nothing }, + { CAPI_DISCONNECT_REQ, do_disconnect_req }, + { CAPI_DISCONNECT_RESP, do_nothing }, + { CAPI_FACILITY_REQ, do_facility_req }, + { CAPI_FACILITY_RESP, do_nothing }, + { CAPI_LISTEN_REQ, do_listen_req }, + { CAPI_SELECT_B_PROTOCOL_REQ, do_unsupported }, + { CAPI_RESET_B3_REQ, do_reset_b3_req }, + { CAPI_RESET_B3_RESP, do_nothing }, + + /* + * ToDo: support overlap sending (requires ev-layer state + * machine extension to generate additional ATD commands) + */ + { CAPI_INFO_REQ, do_unsupported }, + { CAPI_INFO_RESP, do_nothing }, + + /* + * ToDo: what's the proper response for these? + */ + { CAPI_MANUFACTURER_REQ, do_nothing }, + { CAPI_MANUFACTURER_RESP, do_nothing }, +}; + +/* look up handler */ +static inline capi_send_handler_t lookup_capi_send_handler(const u16 cmd) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(capi_send_handler_table); i++) + if (capi_send_handler_table[i].cmd == cmd) + return capi_send_handler_table[i].handler; + return NULL; +} + + +/** + * gigaset_send_message() - accept a CAPI message from an application + * @ctr: controller descriptor structure. + * @skb: CAPI message. + * + * Return value: CAPI error code + * Note: capidrv (and probably others, too) only uses the return value to + * decide whether it has to free the skb (only if result != CAPI_NOERROR (0)) + */ +static u16 gigaset_send_message(struct capi_ctr *ctr, struct sk_buff *skb) +{ + struct gigaset_capi_ctr *iif + = container_of(ctr, struct gigaset_capi_ctr, ctr); + struct cardstate *cs = ctr->driverdata; + struct gigaset_capi_appl *ap; + capi_send_handler_t handler; + + /* can only handle linear sk_buffs */ + if (skb_linearize(skb) < 0) { + dev_warn(cs->dev, "%s: skb_linearize failed\n", __func__); + return CAPI_MSGOSRESOURCEERR; + } + + /* retrieve application data structure */ + ap = get_appl(iif, CAPIMSG_APPID(skb->data)); + if (!ap) { + dev_notice(cs->dev, "%s: application %u not registered\n", + __func__, CAPIMSG_APPID(skb->data)); + return CAPI_ILLAPPNR; + } + + /* look up command */ + handler = lookup_capi_send_handler(CAPIMSG_CMD(skb->data)); + if (!handler) { + /* unknown/unsupported message type */ + if (printk_ratelimit()) + dev_notice(cs->dev, "%s: unsupported message %u\n", + __func__, CAPIMSG_CMD(skb->data)); + return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; + } + + /* serialize */ + if (atomic_add_return(1, &iif->sendqlen) > 1) { + /* queue behind other messages */ + skb_queue_tail(&iif->sendqueue, skb); + return CAPI_NOERROR; + } + + /* process message */ + handler(iif, ap, skb); + + /* process other messages arrived in the meantime */ + while (atomic_sub_return(1, &iif->sendqlen) > 0) { + skb = skb_dequeue(&iif->sendqueue); + if (!skb) { + /* should never happen */ + dev_err(cs->dev, "%s: send queue empty\n", __func__); + continue; + } + ap = get_appl(iif, CAPIMSG_APPID(skb->data)); + if (!ap) { + /* could that happen? */ + dev_warn(cs->dev, "%s: application %u vanished\n", + __func__, CAPIMSG_APPID(skb->data)); + continue; + } + handler = lookup_capi_send_handler(CAPIMSG_CMD(skb->data)); + if (!handler) { + /* should never happen */ + dev_err(cs->dev, "%s: handler %x vanished\n", + __func__, CAPIMSG_CMD(skb->data)); + continue; + } + handler(iif, ap, skb); + } + + return CAPI_NOERROR; +} + +/** + * gigaset_procinfo() - build single line description for controller + * @ctr: controller descriptor structure. + * + * Return value: pointer to generated string (null terminated) + */ +static char *gigaset_procinfo(struct capi_ctr *ctr) +{ + return ctr->name; /* ToDo: more? */ +} + +/** + * gigaset_ctr_read_proc() - build controller proc file entry + * @page: buffer of PAGE_SIZE bytes for receiving the entry. + * @start: unused. + * @off: unused. + * @count: unused. + * @eof: unused. + * @ctr: controller descriptor structure. + * + * Return value: length of generated entry + */ +static int gigaset_ctr_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctr) +{ + struct cardstate *cs = ctr->driverdata; + char *s; + int i; + int len = 0; + len += sprintf(page+len, "%-16s %s\n", "name", ctr->name); + len += sprintf(page+len, "%-16s %s %s\n", "dev", + dev_driver_string(cs->dev), dev_name(cs->dev)); + len += sprintf(page+len, "%-16s %d\n", "id", cs->myid); + if (cs->gotfwver) + len += sprintf(page+len, "%-16s %d.%d.%d.%d\n", "firmware", + cs->fwver[0], cs->fwver[1], cs->fwver[2], cs->fwver[3]); + len += sprintf(page+len, "%-16s %d\n", "channels", + cs->channels); + len += sprintf(page+len, "%-16s %s\n", "onechannel", + cs->onechannel ? "yes" : "no"); + + switch (cs->mode) { + case M_UNKNOWN: + s = "unknown"; + break; + case M_CONFIG: + s = "config"; + break; + case M_UNIMODEM: + s = "Unimodem"; + break; + case M_CID: + s = "CID"; + break; + default: + s = "??"; + } + len += sprintf(page+len, "%-16s %s\n", "mode", s); + + switch (cs->mstate) { + case MS_UNINITIALIZED: + s = "uninitialized"; + break; + case MS_INIT: + s = "init"; + break; + case MS_LOCKED: + s = "locked"; + break; + case MS_SHUTDOWN: + s = "shutdown"; + break; + case MS_RECOVER: + s = "recover"; + break; + case MS_READY: + s = "ready"; + break; + default: + s = "??"; + } + len += sprintf(page+len, "%-16s %s\n", "mstate", s); + + len += sprintf(page+len, "%-16s %s\n", "running", + cs->running ? "yes" : "no"); + len += sprintf(page+len, "%-16s %s\n", "connected", + cs->connected ? "yes" : "no"); + len += sprintf(page+len, "%-16s %s\n", "isdn_up", + cs->isdn_up ? "yes" : "no"); + len += sprintf(page+len, "%-16s %s\n", "cidmode", + cs->cidmode ? "yes" : "no"); + + for (i = 0; i < cs->channels; i++) { + len += sprintf(page+len, "[%d]%-13s %d\n", i, "corrupted", + cs->bcs[i].corrupted); + len += sprintf(page+len, "[%d]%-13s %d\n", i, "trans_down", + cs->bcs[i].trans_down); + len += sprintf(page+len, "[%d]%-13s %d\n", i, "trans_up", + cs->bcs[i].trans_up); + len += sprintf(page+len, "[%d]%-13s %d\n", i, "chstate", + cs->bcs[i].chstate); + switch (cs->bcs[i].proto2) { + case L2_BITSYNC: + s = "bitsync"; + break; + case L2_HDLC: + s = "HDLC"; + break; + case L2_VOICE: + s = "voice"; + break; + default: + s = "??"; + } + len += sprintf(page+len, "[%d]%-13s %s\n", i, "proto2", s); + } + return len; +} + + +static struct capi_driver capi_driver_gigaset = { + .name = "gigaset", + .revision = "1.0", +}; + +/** + * gigaset_isdn_register() - register to LL + * @cs: device descriptor structure. + * @isdnid: device name. + * + * Called by main module to register the device with the LL. + * + * Return value: 1 for success, 0 for failure + */ +int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) +{ + struct gigaset_capi_ctr *iif; + int rc; + + pr_info("Kernel CAPI interface\n"); + + iif = kmalloc(sizeof(*iif), GFP_KERNEL); + if (!iif) { + pr_err("%s: out of memory\n", __func__); + return 0; + } + + /* register driver with CAPI (ToDo: what for?) */ + register_capi_driver(&capi_driver_gigaset); + + /* prepare controller structure */ + iif->ctr.owner = THIS_MODULE; + iif->ctr.driverdata = cs; + strncpy(iif->ctr.name, isdnid, sizeof(iif->ctr.name)); + iif->ctr.driver_name = "gigaset"; + iif->ctr.load_firmware = gigaset_load_firmware; + iif->ctr.reset_ctr = gigaset_reset_ctr; + iif->ctr.register_appl = gigaset_register_appl; + iif->ctr.release_appl = gigaset_release_appl; + iif->ctr.send_message = gigaset_send_message; + iif->ctr.procinfo = gigaset_procinfo; + iif->ctr.ctr_read_proc = gigaset_ctr_read_proc; + INIT_LIST_HEAD(&iif->appls); + skb_queue_head_init(&iif->sendqueue); + atomic_set(&iif->sendqlen, 0); + + /* register controller with CAPI */ + rc = attach_capi_ctr(&iif->ctr); + if (rc) { + pr_err("attach_capi_ctr failed (%d)\n", rc); + unregister_capi_driver(&capi_driver_gigaset); + kfree(iif); + return 0; + } + + cs->iif = iif; + cs->hw_hdr_len = CAPI_DATA_B3_REQ_LEN; + return 1; +} + +/** + * gigaset_isdn_unregister() - unregister from LL + * @cs: device descriptor structure. + * + * Called by main module to unregister the device from the LL. + */ +void gigaset_isdn_unregister(struct cardstate *cs) +{ + struct gigaset_capi_ctr *iif = cs->iif; + + detach_capi_ctr(&iif->ctr); + kfree(iif); + cs->iif = NULL; + unregister_capi_driver(&capi_driver_gigaset); +} diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 33dcd8d72b7c..82ed1cd14ff5 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -108,7 +108,7 @@ int gigaset_enterconfigmode(struct cardstate *cs) { int i, r; - cs->control_state = TIOCM_RTS; //FIXME + cs->control_state = TIOCM_RTS; r = setflags(cs, TIOCM_DTR, 200); if (r < 0) @@ -132,10 +132,10 @@ int gigaset_enterconfigmode(struct cardstate *cs) error: dev_err(cs->dev, "error %d on setuartbits\n", -r); - cs->control_state = TIOCM_RTS|TIOCM_DTR; // FIXME is this a good value? + cs->control_state = TIOCM_RTS|TIOCM_DTR; cs->ops->set_modem_ctrl(cs, 0, TIOCM_RTS|TIOCM_DTR); - return -1; //r + return -1; } static int test_timeout(struct at_state_t *at_state) @@ -150,10 +150,9 @@ static int test_timeout(struct at_state_t *at_state) } if (!gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL, - at_state->timer_index, NULL)) { - //FIXME what should we do? - } - + at_state->timer_index, NULL)) + dev_err(at_state->cs->dev, "%s: out of memory\n", + __func__); return 1; } @@ -207,6 +206,32 @@ int gigaset_get_channel(struct bc_state *bcs) return 1; } +struct bc_state *gigaset_get_free_channel(struct cardstate *cs) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&cs->lock, flags); + if (!try_module_get(cs->driver->owner)) { + gig_dbg(DEBUG_ANY, + "could not get module for allocating channel"); + spin_unlock_irqrestore(&cs->lock, flags); + return NULL; + } + for (i = 0; i < cs->channels; ++i) + if (!cs->bcs[i].use_count) { + ++cs->bcs[i].use_count; + cs->bcs[i].busy = 1; + spin_unlock_irqrestore(&cs->lock, flags); + gig_dbg(DEBUG_ANY, "allocated channel %d", i); + return cs->bcs + i; + } + module_put(cs->driver->owner); + spin_unlock_irqrestore(&cs->lock, flags); + gig_dbg(DEBUG_ANY, "no free channel"); + return NULL; +} + void gigaset_free_channel(struct bc_state *bcs) { unsigned long flags; @@ -367,16 +392,15 @@ static void gigaset_freebcs(struct bc_state *bcs) int i; gig_dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel); - if (!bcs->cs->ops->freebcshw(bcs)) { + if (!bcs->cs->ops->freebcshw(bcs)) gig_dbg(DEBUG_INIT, "failed"); - } gig_dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel); clear_at_state(&bcs->at_state); gig_dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel); + dev_kfree_skb(bcs->skb); + bcs->skb = NULL; - if (bcs->skb) - dev_kfree_skb(bcs->skb); for (i = 0; i < AT_NUM; ++i) { kfree(bcs->commands[i]); bcs->commands[i] = NULL; @@ -463,6 +487,12 @@ void gigaset_freecs(struct cardstate *cs) switch (cs->cs_init) { default: + /* clear B channel structures */ + for (i = 0; i < cs->channels; ++i) { + gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i); + gigaset_freebcs(cs->bcs + i); + } + /* clear device sysfs */ gigaset_free_dev_sysfs(cs); @@ -471,28 +501,20 @@ void gigaset_freecs(struct cardstate *cs) gig_dbg(DEBUG_INIT, "clearing hw"); cs->ops->freecshw(cs); - //FIXME cmdbuf - /* fall through */ case 2: /* error in initcshw */ /* Deregister from LL */ make_invalid(cs, VALID_ID); - gig_dbg(DEBUG_INIT, "clearing iif"); - gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD); + gigaset_isdn_unregister(cs); /* fall through */ - case 1: /* error when regestering to LL */ + case 1: /* error when registering to LL */ gig_dbg(DEBUG_INIT, "clearing at_state"); clear_at_state(&cs->at_state); dealloc_at_states(cs); /* fall through */ - case 0: /* error in one call to initbcs */ - for (i = 0; i < cs->channels; ++i) { - gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i); - gigaset_freebcs(cs->bcs + i); - } - + case 0: /* error in basic setup */ clear_events(cs); gig_dbg(DEBUG_INIT, "freeing inbuf"); kfree(cs->inbuf); @@ -534,16 +556,13 @@ void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs, } -static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs, - struct cardstate *cs, int inputstate) +static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct cardstate *cs) /* inbuf->read must be allocated before! */ { inbuf->head = 0; inbuf->tail = 0; inbuf->cs = cs; - inbuf->bcs = bcs; /*base driver: NULL*/ - inbuf->rcvbuf = NULL; - inbuf->inputstate = inputstate; + inbuf->inputstate = INS_command; } /** @@ -599,7 +618,7 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, { int i; - bcs->tx_skb = NULL; //FIXME -> hw part + bcs->tx_skb = NULL; skb_queue_head_init(&bcs->squeue); @@ -618,13 +637,13 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, bcs->fcs = PPP_INITFCS; bcs->inputstate = 0; if (cs->ignoreframes) { - bcs->inputstate |= INS_skip_frame; bcs->skb = NULL; - } else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL) - skb_reserve(bcs->skb, HW_HDR_LEN); - else { - pr_err("out of memory\n"); - bcs->inputstate |= INS_skip_frame; + } else { + bcs->skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len); + if (bcs->skb != NULL) + skb_reserve(bcs->skb, cs->hw_hdr_len); + else + pr_err("out of memory\n"); } bcs->channel = channel; @@ -645,8 +664,8 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, gig_dbg(DEBUG_INIT, " failed"); gig_dbg(DEBUG_INIT, " freeing bcs[%d]->skb", channel); - if (bcs->skb) - dev_kfree_skb(bcs->skb); + dev_kfree_skb(bcs->skb); + bcs->skb = NULL; return NULL; } @@ -673,12 +692,13 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, int onechannel, int ignoreframes, int cidmode, const char *modulename) { - struct cardstate *cs = NULL; + struct cardstate *cs; unsigned long flags; int i; gig_dbg(DEBUG_INIT, "allocating cs"); - if (!(cs = alloc_cs(drv))) { + cs = alloc_cs(drv); + if (!cs) { pr_err("maximum number of devices exceeded\n"); return NULL; } @@ -707,7 +727,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, cs->ev_tail = 0; cs->ev_head = 0; - tasklet_init(&cs->event_tasklet, &gigaset_handle_event, + tasklet_init(&cs->event_tasklet, gigaset_handle_event, (unsigned long) cs); cs->commands_pending = 0; cs->cur_at_seq = 0; @@ -726,14 +746,6 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, cs->mode = M_UNKNOWN; cs->mstate = MS_UNINITIALIZED; - for (i = 0; i < channels; ++i) { - gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", i); - if (!gigaset_initbcs(cs->bcs + i, cs, i)) { - pr_err("could not allocate channel %d data\n", i); - goto error; - } - } - ++cs->cs_init; gig_dbg(DEBUG_INIT, "setting up at_state"); @@ -743,10 +755,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, cs->cbytes = 0; gig_dbg(DEBUG_INIT, "setting up inbuf"); - if (onechannel) { //FIXME distinction necessary? - gigaset_inbuf_init(cs->inbuf, cs->bcs, cs, INS_command); - } else - gigaset_inbuf_init(cs->inbuf, NULL, cs, INS_command); + gigaset_inbuf_init(cs->inbuf, cs); cs->connected = 0; cs->isdn_up = 0; @@ -758,7 +767,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, cs->cmdbytes = 0; gig_dbg(DEBUG_INIT, "setting up iif"); - if (!gigaset_register_to_LL(cs, modulename)) { + if (!gigaset_isdn_register(cs, modulename)) { pr_err("error registering ISDN device\n"); goto error; } @@ -777,6 +786,15 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, /* set up device sysfs */ gigaset_init_dev_sysfs(cs); + /* set up channel data structures */ + for (i = 0; i < channels; ++i) { + gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i); + if (!gigaset_initbcs(cs->bcs + i, cs, i)) { + pr_err("could not allocate channel %d data\n", i); + goto error; + } + } + spin_lock_irqsave(&cs->lock, flags); cs->running = 1; spin_unlock_irqrestore(&cs->lock, flags); @@ -824,9 +842,10 @@ void gigaset_bcs_reinit(struct bc_state *bcs) bcs->chstate = 0; bcs->ignore = cs->ignoreframes; - if (bcs->ignore) - bcs->inputstate |= INS_skip_frame; - + if (bcs->ignore) { + dev_kfree_skb(bcs->skb); + bcs->skb = NULL; + } cs->ops->reinitbcshw(bcs); } @@ -847,8 +866,6 @@ static void cleanup_cs(struct cardstate *cs) free_strings(&cs->at_state); gigaset_at_init(&cs->at_state, NULL, cs, 0); - kfree(cs->inbuf->rcvbuf); - cs->inbuf->rcvbuf = NULL; cs->inbuf->inputstate = INS_command; cs->inbuf->head = 0; cs->inbuf->tail = 0; @@ -911,15 +928,13 @@ int gigaset_start(struct cardstate *cs) cs->ops->baud_rate(cs, B115200); cs->ops->set_line_ctrl(cs, CS8); cs->control_state = TIOCM_DTR|TIOCM_RTS; - } else { - //FIXME use some saved values? } cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_START, NULL, 0, NULL)) { cs->waiting = 0; - //FIXME what should we do? + dev_err(cs->dev, "%s: out of memory\n", __func__); goto error; } @@ -959,7 +974,7 @@ int gigaset_shutdown(struct cardstate *cs) cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) { - //FIXME what should we do? + dev_err(cs->dev, "%s: out of memory\n", __func__); goto exit; } @@ -990,7 +1005,7 @@ void gigaset_stop(struct cardstate *cs) cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL)) { - //FIXME what should we do? + dev_err(cs->dev, "%s: out of memory\n", __func__); goto exit; } diff --git a/drivers/isdn/gigaset/dummyll.c b/drivers/isdn/gigaset/dummyll.c new file mode 100644 index 000000000000..5b27c996af6d --- /dev/null +++ b/drivers/isdn/gigaset/dummyll.c @@ -0,0 +1,68 @@ +/* + * Dummy LL interface for the Gigaset driver + * + * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>. + * + * ===================================================================== + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * ===================================================================== + */ + +#include "gigaset.h" + +void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) +{ +} +EXPORT_SYMBOL_GPL(gigaset_skb_sent); + +void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb) +{ +} +EXPORT_SYMBOL_GPL(gigaset_skb_rcvd); + +void gigaset_isdn_rcv_err(struct bc_state *bcs) +{ +} +EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err); + +int gigaset_isdn_icall(struct at_state_t *at_state) +{ + return ICALL_IGNORE; +} + +void gigaset_isdn_connD(struct bc_state *bcs) +{ +} + +void gigaset_isdn_hupD(struct bc_state *bcs) +{ +} + +void gigaset_isdn_connB(struct bc_state *bcs) +{ +} + +void gigaset_isdn_hupB(struct bc_state *bcs) +{ +} + +void gigaset_isdn_start(struct cardstate *cs) +{ +} + +void gigaset_isdn_stop(struct cardstate *cs) +{ +} + +int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) +{ + pr_info("no ISDN subsystem interface\n"); + return 1; +} + +void gigaset_isdn_unregister(struct cardstate *cs) +{ +} diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index cc768caa38f5..ddeb0456d202 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c @@ -40,8 +40,8 @@ /* Possible ASCII responses */ #define RSP_OK 0 -//#define RSP_BUSY 1 -//#define RSP_CONNECT 2 +#define RSP_BUSY 1 +#define RSP_CONNECT 2 #define RSP_ZGCI 3 #define RSP_RING 4 #define RSP_ZAOC 5 @@ -68,7 +68,6 @@ #define RSP_ZHLC (RSP_STR + STR_ZHLC) #define RSP_ERROR -1 /* ERROR */ #define RSP_WRONG_CID -2 /* unknown cid in cmd */ -//#define RSP_EMPTY -3 #define RSP_UNKNOWN -4 /* unknown response */ #define RSP_FAIL -5 /* internal error */ #define RSP_INVAL -6 /* invalid response */ @@ -76,9 +75,9 @@ #define RSP_NONE -19 #define RSP_STRING -20 #define RSP_NULL -21 -//#define RSP_RETRYFAIL -22 -//#define RSP_RETRY -23 -//#define RSP_SKIP -24 +#define RSP_RETRYFAIL -22 +#define RSP_RETRY -23 +#define RSP_SKIP -24 #define RSP_INIT -27 #define RSP_ANY -26 #define RSP_LAST -28 @@ -127,7 +126,6 @@ #define ACT_NOTIFY_BC_UP 39 #define ACT_DIAL 40 #define ACT_ACCEPT 41 -#define ACT_PROTO_L2 42 #define ACT_HUP 43 #define ACT_IF_LOCK 44 #define ACT_START 45 @@ -159,229 +157,229 @@ #define SEQ_UMMODE 11 -// 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid), 400: hup, 500: reset, 600: dial, 700: ring +/* 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid), + * 400: hup, 500: reset, 600: dial, 700: ring */ struct reply_t gigaset_tab_nocid[] = { - /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */ - - /* initialize device, set cid mode if possible */ - //{RSP_INIT, -1, -1,100, 900, 0, {ACT_TEST}}, - //{RSP_ERROR, 900,900, -1, 0, 0, {ACT_FAILINIT}}, - //{RSP_OK, 900,900, -1, 100, INIT_TIMEOUT, - // {ACT_TIMEOUT}}, - - {RSP_INIT, -1, -1,SEQ_INIT, 100, INIT_TIMEOUT, - {ACT_TIMEOUT}}, /* wait until device is ready */ - - {EV_TIMEOUT, 100,100, -1, 101, 3, {0}, "Z\r"}, /* device in transparent mode? try to initialize it. */ - {RSP_OK, 101,103, -1, 120, 5, {ACT_GETSTRING}, "+GMR\r"}, /* get version */ - - {EV_TIMEOUT, 101,101, -1, 102, 5, {0}, "Z\r"}, /* timeout => try once again. */ - {RSP_ERROR, 101,101, -1, 102, 5, {0}, "Z\r"}, /* error => try once again. */ - - {EV_TIMEOUT, 102,102, -1, 108, 5, {ACT_SETDLE1}, "^SDLE=0\r"}, /* timeout => try again in DLE mode. */ - {RSP_OK, 108,108, -1, 104,-1}, - {RSP_ZDLE, 104,104, 0, 103, 5, {0}, "Z\r"}, - {EV_TIMEOUT, 104,104, -1, 0, 0, {ACT_FAILINIT}}, - {RSP_ERROR, 108,108, -1, 0, 0, {ACT_FAILINIT}}, - - {EV_TIMEOUT, 108,108, -1, 105, 2, {ACT_SETDLE0, - ACT_HUPMODEM, - ACT_TIMEOUT}}, /* still timeout => connection in unimodem mode? */ - {EV_TIMEOUT, 105,105, -1, 103, 5, {0}, "Z\r"}, - - {RSP_ERROR, 102,102, -1, 107, 5, {0}, "^GETPRE\r"}, /* ERROR on ATZ => maybe in config mode? */ - {RSP_OK, 107,107, -1, 0, 0, {ACT_CONFIGMODE}}, - {RSP_ERROR, 107,107, -1, 0, 0, {ACT_FAILINIT}}, - {EV_TIMEOUT, 107,107, -1, 0, 0, {ACT_FAILINIT}}, - - {RSP_ERROR, 103,103, -1, 0, 0, {ACT_FAILINIT}}, - {EV_TIMEOUT, 103,103, -1, 0, 0, {ACT_FAILINIT}}, - - {RSP_STRING, 120,120, -1, 121,-1, {ACT_SETVER}}, - - {EV_TIMEOUT, 120,121, -1, 0, 0, {ACT_FAILVER, ACT_INIT}}, - {RSP_ERROR, 120,121, -1, 0, 0, {ACT_FAILVER, ACT_INIT}}, - {RSP_OK, 121,121, -1, 0, 0, {ACT_GOTVER, ACT_INIT}}, - - /* leave dle mode */ - {RSP_INIT, 0, 0,SEQ_DLE0, 201, 5, {0}, "^SDLE=0\r"}, - {RSP_OK, 201,201, -1, 202,-1}, - {RSP_ZDLE, 202,202, 0, 0, 0, {ACT_DLE0}}, - {RSP_NODEV, 200,249, -1, 0, 0, {ACT_FAKEDLE0}}, - {RSP_ERROR, 200,249, -1, 0, 0, {ACT_FAILDLE0}}, - {EV_TIMEOUT, 200,249, -1, 0, 0, {ACT_FAILDLE0}}, - - /* enter dle mode */ - {RSP_INIT, 0, 0,SEQ_DLE1, 251, 5, {0}, "^SDLE=1\r"}, - {RSP_OK, 251,251, -1, 252,-1}, - {RSP_ZDLE, 252,252, 1, 0, 0, {ACT_DLE1}}, - {RSP_ERROR, 250,299, -1, 0, 0, {ACT_FAILDLE1}}, - {EV_TIMEOUT, 250,299, -1, 0, 0, {ACT_FAILDLE1}}, - - /* incoming call */ - {RSP_RING, -1, -1, -1, -1,-1, {ACT_RING}}, - - /* get cid */ - //{RSP_INIT, 0, 0,300, 901, 0, {ACT_TEST}}, - //{RSP_ERROR, 901,901, -1, 0, 0, {ACT_FAILCID}}, - //{RSP_OK, 901,901, -1, 301, 5, {0}, "^SGCI?\r"}, - - {RSP_INIT, 0, 0,SEQ_CID, 301, 5, {0}, "^SGCI?\r"}, - {RSP_OK, 301,301, -1, 302,-1}, - {RSP_ZGCI, 302,302, -1, 0, 0, {ACT_CID}}, - {RSP_ERROR, 301,349, -1, 0, 0, {ACT_FAILCID}}, - {EV_TIMEOUT, 301,349, -1, 0, 0, {ACT_FAILCID}}, - - /* enter cid mode */ - {RSP_INIT, 0, 0,SEQ_CIDMODE, 150, 5, {0}, "^SGCI=1\r"}, - {RSP_OK, 150,150, -1, 0, 0, {ACT_CMODESET}}, - {RSP_ERROR, 150,150, -1, 0, 0, {ACT_FAILCMODE}}, - {EV_TIMEOUT, 150,150, -1, 0, 0, {ACT_FAILCMODE}}, - - /* leave cid mode */ - //{RSP_INIT, 0, 0,SEQ_UMMODE, 160, 5, {0}, "^SGCI=0\r"}, - {RSP_INIT, 0, 0,SEQ_UMMODE, 160, 5, {0}, "Z\r"}, - {RSP_OK, 160,160, -1, 0, 0, {ACT_UMODESET}}, - {RSP_ERROR, 160,160, -1, 0, 0, {ACT_FAILUMODE}}, - {EV_TIMEOUT, 160,160, -1, 0, 0, {ACT_FAILUMODE}}, - - /* abort getting cid */ - {RSP_INIT, 0, 0,SEQ_NOCID, 0, 0, {ACT_ABORTCID}}, - - /* reset */ - {RSP_INIT, 0, 0,SEQ_SHUTDOWN, 504, 5, {0}, "Z\r"}, - {RSP_OK, 504,504, -1, 0, 0, {ACT_SDOWN}}, - {RSP_ERROR, 501,599, -1, 0, 0, {ACT_FAILSDOWN}}, - {EV_TIMEOUT, 501,599, -1, 0, 0, {ACT_FAILSDOWN}}, - {RSP_NODEV, 501,599, -1, 0, 0, {ACT_FAKESDOWN}}, - - {EV_PROC_CIDMODE,-1, -1, -1, -1,-1, {ACT_PROC_CIDMODE}}, //FIXME - {EV_IF_LOCK, -1, -1, -1, -1,-1, {ACT_IF_LOCK}}, //FIXME - {EV_IF_VER, -1, -1, -1, -1,-1, {ACT_IF_VER}}, //FIXME - {EV_START, -1, -1, -1, -1,-1, {ACT_START}}, //FIXME - {EV_STOP, -1, -1, -1, -1,-1, {ACT_STOP}}, //FIXME - {EV_SHUTDOWN, -1, -1, -1, -1,-1, {ACT_SHUTDOWN}}, //FIXME - - /* misc. */ - {RSP_ERROR, -1, -1, -1, -1, -1, {ACT_ERROR} }, - {RSP_EMPTY, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZCFGT, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZCFG, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZLOG, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZMWI, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZABINFO, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZSMLSTCHG,-1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - - {RSP_ZCAU, -1, -1, -1, -1,-1, {ACT_ZCAU}}, - {RSP_NONE, -1, -1, -1, -1,-1, {ACT_DEBUG}}, - {RSP_ANY, -1, -1, -1, -1,-1, {ACT_WARN}}, - {RSP_LAST} +/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, + * action, command */ + +/* initialize device, set cid mode if possible */ +{RSP_INIT, -1, -1, SEQ_INIT, 100, 1, {ACT_TIMEOUT} }, + +{EV_TIMEOUT, 100, 100, -1, 101, 3, {0}, "Z\r"}, +{RSP_OK, 101, 103, -1, 120, 5, {ACT_GETSTRING}, + "+GMR\r"}, + +{EV_TIMEOUT, 101, 101, -1, 102, 5, {0}, "Z\r"}, +{RSP_ERROR, 101, 101, -1, 102, 5, {0}, "Z\r"}, + +{EV_TIMEOUT, 102, 102, -1, 108, 5, {ACT_SETDLE1}, + "^SDLE=0\r"}, +{RSP_OK, 108, 108, -1, 104, -1}, +{RSP_ZDLE, 104, 104, 0, 103, 5, {0}, "Z\r"}, +{EV_TIMEOUT, 104, 104, -1, 0, 0, {ACT_FAILINIT} }, +{RSP_ERROR, 108, 108, -1, 0, 0, {ACT_FAILINIT} }, + +{EV_TIMEOUT, 108, 108, -1, 105, 2, {ACT_SETDLE0, + ACT_HUPMODEM, + ACT_TIMEOUT} }, +{EV_TIMEOUT, 105, 105, -1, 103, 5, {0}, "Z\r"}, + +{RSP_ERROR, 102, 102, -1, 107, 5, {0}, "^GETPRE\r"}, +{RSP_OK, 107, 107, -1, 0, 0, {ACT_CONFIGMODE} }, +{RSP_ERROR, 107, 107, -1, 0, 0, {ACT_FAILINIT} }, +{EV_TIMEOUT, 107, 107, -1, 0, 0, {ACT_FAILINIT} }, + +{RSP_ERROR, 103, 103, -1, 0, 0, {ACT_FAILINIT} }, +{EV_TIMEOUT, 103, 103, -1, 0, 0, {ACT_FAILINIT} }, + +{RSP_STRING, 120, 120, -1, 121, -1, {ACT_SETVER} }, + +{EV_TIMEOUT, 120, 121, -1, 0, 0, {ACT_FAILVER, + ACT_INIT} }, +{RSP_ERROR, 120, 121, -1, 0, 0, {ACT_FAILVER, + ACT_INIT} }, +{RSP_OK, 121, 121, -1, 0, 0, {ACT_GOTVER, + ACT_INIT} }, + +/* leave dle mode */ +{RSP_INIT, 0, 0, SEQ_DLE0, 201, 5, {0}, "^SDLE=0\r"}, +{RSP_OK, 201, 201, -1, 202, -1}, +{RSP_ZDLE, 202, 202, 0, 0, 0, {ACT_DLE0} }, +{RSP_NODEV, 200, 249, -1, 0, 0, {ACT_FAKEDLE0} }, +{RSP_ERROR, 200, 249, -1, 0, 0, {ACT_FAILDLE0} }, +{EV_TIMEOUT, 200, 249, -1, 0, 0, {ACT_FAILDLE0} }, + +/* enter dle mode */ +{RSP_INIT, 0, 0, SEQ_DLE1, 251, 5, {0}, "^SDLE=1\r"}, +{RSP_OK, 251, 251, -1, 252, -1}, +{RSP_ZDLE, 252, 252, 1, 0, 0, {ACT_DLE1} }, +{RSP_ERROR, 250, 299, -1, 0, 0, {ACT_FAILDLE1} }, +{EV_TIMEOUT, 250, 299, -1, 0, 0, {ACT_FAILDLE1} }, + +/* incoming call */ +{RSP_RING, -1, -1, -1, -1, -1, {ACT_RING} }, + +/* get cid */ +{RSP_INIT, 0, 0, SEQ_CID, 301, 5, {0}, "^SGCI?\r"}, +{RSP_OK, 301, 301, -1, 302, -1}, +{RSP_ZGCI, 302, 302, -1, 0, 0, {ACT_CID} }, +{RSP_ERROR, 301, 349, -1, 0, 0, {ACT_FAILCID} }, +{EV_TIMEOUT, 301, 349, -1, 0, 0, {ACT_FAILCID} }, + +/* enter cid mode */ +{RSP_INIT, 0, 0, SEQ_CIDMODE, 150, 5, {0}, "^SGCI=1\r"}, +{RSP_OK, 150, 150, -1, 0, 0, {ACT_CMODESET} }, +{RSP_ERROR, 150, 150, -1, 0, 0, {ACT_FAILCMODE} }, +{EV_TIMEOUT, 150, 150, -1, 0, 0, {ACT_FAILCMODE} }, + +/* leave cid mode */ +{RSP_INIT, 0, 0, SEQ_UMMODE, 160, 5, {0}, "Z\r"}, +{RSP_OK, 160, 160, -1, 0, 0, {ACT_UMODESET} }, +{RSP_ERROR, 160, 160, -1, 0, 0, {ACT_FAILUMODE} }, +{EV_TIMEOUT, 160, 160, -1, 0, 0, {ACT_FAILUMODE} }, + +/* abort getting cid */ +{RSP_INIT, 0, 0, SEQ_NOCID, 0, 0, {ACT_ABORTCID} }, + +/* reset */ +{RSP_INIT, 0, 0, SEQ_SHUTDOWN, 504, 5, {0}, "Z\r"}, +{RSP_OK, 504, 504, -1, 0, 0, {ACT_SDOWN} }, +{RSP_ERROR, 501, 599, -1, 0, 0, {ACT_FAILSDOWN} }, +{EV_TIMEOUT, 501, 599, -1, 0, 0, {ACT_FAILSDOWN} }, +{RSP_NODEV, 501, 599, -1, 0, 0, {ACT_FAKESDOWN} }, + +{EV_PROC_CIDMODE, -1, -1, -1, -1, -1, {ACT_PROC_CIDMODE} }, +{EV_IF_LOCK, -1, -1, -1, -1, -1, {ACT_IF_LOCK} }, +{EV_IF_VER, -1, -1, -1, -1, -1, {ACT_IF_VER} }, +{EV_START, -1, -1, -1, -1, -1, {ACT_START} }, +{EV_STOP, -1, -1, -1, -1, -1, {ACT_STOP} }, +{EV_SHUTDOWN, -1, -1, -1, -1, -1, {ACT_SHUTDOWN} }, + +/* misc. */ +{RSP_ERROR, -1, -1, -1, -1, -1, {ACT_ERROR} }, +{RSP_ZCFGT, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZCFG, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZLOG, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZMWI, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZABINFO, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZSMLSTCHG, -1, -1, -1, -1, -1, {ACT_DEBUG} }, + +{RSP_ZCAU, -1, -1, -1, -1, -1, {ACT_ZCAU} }, +{RSP_NONE, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ANY, -1, -1, -1, -1, -1, {ACT_WARN} }, +{RSP_LAST} }; -// 600: start dialing, 650: dial in progress, 800: connection is up, 700: ring, 400: hup, 750: accepted icall +/* 600: start dialing, 650: dial in progress, 800: connection is up, 700: ring, + * 400: hup, 750: accepted icall */ struct reply_t gigaset_tab_cid[] = { - /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */ - - /* dial */ - {EV_DIAL, -1, -1, -1, -1,-1, {ACT_DIAL}}, //FIXME - {RSP_INIT, 0, 0,SEQ_DIAL, 601, 5, {ACT_CMD+AT_BC}}, - {RSP_OK, 601,601, -1, 602, 5, {ACT_CMD+AT_HLC}}, - {RSP_NULL, 602,602, -1, 603, 5, {ACT_CMD+AT_PROTO}}, - {RSP_OK, 602,602, -1, 603, 5, {ACT_CMD+AT_PROTO}}, - {RSP_OK, 603,603, -1, 604, 5, {ACT_CMD+AT_TYPE}}, - {RSP_OK, 604,604, -1, 605, 5, {ACT_CMD+AT_MSN}}, - {RSP_OK, 605,605, -1, 606, 5, {ACT_CMD+AT_ISO}}, - {RSP_NULL, 605,605, -1, 606, 5, {ACT_CMD+AT_ISO}}, - {RSP_OK, 606,606, -1, 607, 5, {0}, "+VLS=17\r"}, - {RSP_OK, 607,607, -1, 608,-1}, - {RSP_ZSAU, 608,608,ZSAU_PROCEEDING, 609, 5, {ACT_CMD+AT_DIAL}}, - {RSP_OK, 609,609, -1, 650, 0, {ACT_DIALING}}, - - {RSP_ERROR, 601,609, -1, 0, 0, {ACT_ABORTDIAL}}, - {EV_TIMEOUT, 601,609, -1, 0, 0, {ACT_ABORTDIAL}}, - - /* optional dialing responses */ - {EV_BC_OPEN, 650,650, -1, 651,-1}, - {RSP_ZVLS, 608,651, 17, -1,-1, {ACT_DEBUG}}, - {RSP_ZCTP, 609,651, -1, -1,-1, {ACT_DEBUG}}, - {RSP_ZCPN, 609,651, -1, -1,-1, {ACT_DEBUG}}, - {RSP_ZSAU, 650,651,ZSAU_CALL_DELIVERED, -1,-1, {ACT_DEBUG}}, - - /* connect */ - {RSP_ZSAU, 650,650,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT}}, - {RSP_ZSAU, 651,651,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT, - ACT_NOTIFY_BC_UP}}, - {RSP_ZSAU, 750,750,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT}}, - {RSP_ZSAU, 751,751,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT, - ACT_NOTIFY_BC_UP}}, - {EV_BC_OPEN, 800,800, -1, 800,-1, {ACT_NOTIFY_BC_UP}}, - - /* remote hangup */ - {RSP_ZSAU, 650,651,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEREJECT}}, - {RSP_ZSAU, 750,751,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP}}, - {RSP_ZSAU, 800,800,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP}}, - - /* hangup */ - {EV_HUP, -1, -1, -1, -1,-1, {ACT_HUP}}, //FIXME - {RSP_INIT, -1, -1,SEQ_HUP, 401, 5, {0}, "+VLS=0\r"}, /* hang up */ //-1,-1? - {RSP_OK, 401,401, -1, 402, 5}, - {RSP_ZVLS, 402,402, 0, 403, 5}, - {RSP_ZSAU, 403, 403, ZSAU_DISCONNECT_REQ, -1, -1, {ACT_DEBUG} }, - {RSP_ZSAU, 403, 403, ZSAU_NULL, 0, 0, {ACT_DISCONNECT} }, - {RSP_NODEV, 401, 403, -1, 0, 0, {ACT_FAKEHUP} }, - {RSP_ERROR, 401,401, -1, 0, 0, {ACT_ABORTHUP}}, - {EV_TIMEOUT, 401,403, -1, 0, 0, {ACT_ABORTHUP}}, - - {EV_BC_CLOSED, 0, 0, -1, 0,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME new constate + timeout - - /* ring */ - {RSP_ZBC, 700,700, -1, -1,-1, {0}}, - {RSP_ZHLC, 700,700, -1, -1,-1, {0}}, - {RSP_NMBR, 700,700, -1, -1,-1, {0}}, - {RSP_ZCPN, 700,700, -1, -1,-1, {0}}, - {RSP_ZCTP, 700,700, -1, -1,-1, {0}}, - {EV_TIMEOUT, 700,700, -1, 720,720, {ACT_ICALL}}, - {EV_BC_CLOSED,720,720, -1, 0,-1, {ACT_NOTIFY_BC_DOWN}}, - - /*accept icall*/ - {EV_ACCEPT, -1, -1, -1, -1,-1, {ACT_ACCEPT}}, //FIXME - {RSP_INIT, 720,720,SEQ_ACCEPT, 721, 5, {ACT_CMD+AT_PROTO}}, - {RSP_OK, 721,721, -1, 722, 5, {ACT_CMD+AT_ISO}}, - {RSP_OK, 722,722, -1, 723, 5, {0}, "+VLS=17\r"}, /* set "Endgeraetemodus" */ - {RSP_OK, 723,723, -1, 724, 5, {0}}, - {RSP_ZVLS, 724,724, 17, 750,50, {ACT_ACCEPTED}}, - {RSP_ERROR, 721,729, -1, 0, 0, {ACT_ABORTACCEPT}}, - {EV_TIMEOUT, 721,729, -1, 0, 0, {ACT_ABORTACCEPT}}, - {RSP_ZSAU, 700,729,ZSAU_NULL, 0, 0, {ACT_ABORTACCEPT}}, - {RSP_ZSAU, 700,729,ZSAU_ACTIVE, 0, 0, {ACT_ABORTACCEPT}}, - {RSP_ZSAU, 700,729,ZSAU_DISCONNECT_IND, 0, 0, {ACT_ABORTACCEPT}}, - - {EV_BC_OPEN, 750,750, -1, 751,-1}, - {EV_TIMEOUT, 750,751, -1, 0, 0, {ACT_CONNTIMEOUT}}, - - /* B channel closed (general case) */ - {EV_BC_CLOSED, -1, -1, -1, -1,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME - - /* misc. */ - {EV_PROTO_L2, -1, -1, -1, -1,-1, {ACT_PROTO_L2}}, //FIXME - - {RSP_ZCON, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZCCR, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZAOC, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - {RSP_ZCSTR, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME - - {RSP_ZCAU, -1, -1, -1, -1,-1, {ACT_ZCAU}}, - {RSP_NONE, -1, -1, -1, -1,-1, {ACT_DEBUG}}, - {RSP_ANY, -1, -1, -1, -1,-1, {ACT_WARN}}, - {RSP_LAST} +/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, + * action, command */ + +/* dial */ +{EV_DIAL, -1, -1, -1, -1, -1, {ACT_DIAL} }, +{RSP_INIT, 0, 0, SEQ_DIAL, 601, 5, {ACT_CMD+AT_BC} }, +{RSP_OK, 601, 601, -1, 602, 5, {ACT_CMD+AT_HLC} }, +{RSP_NULL, 602, 602, -1, 603, 5, {ACT_CMD+AT_PROTO} }, +{RSP_OK, 602, 602, -1, 603, 5, {ACT_CMD+AT_PROTO} }, +{RSP_OK, 603, 603, -1, 604, 5, {ACT_CMD+AT_TYPE} }, +{RSP_OK, 604, 604, -1, 605, 5, {ACT_CMD+AT_MSN} }, +{RSP_NULL, 605, 605, -1, 606, 5, {ACT_CMD+AT_CLIP} }, +{RSP_OK, 605, 605, -1, 606, 5, {ACT_CMD+AT_CLIP} }, +{RSP_NULL, 606, 606, -1, 607, 5, {ACT_CMD+AT_ISO} }, +{RSP_OK, 606, 606, -1, 607, 5, {ACT_CMD+AT_ISO} }, +{RSP_OK, 607, 607, -1, 608, 5, {0}, "+VLS=17\r"}, +{RSP_OK, 608, 608, -1, 609, -1}, +{RSP_ZSAU, 609, 609, ZSAU_PROCEEDING, 610, 5, {ACT_CMD+AT_DIAL} }, +{RSP_OK, 610, 610, -1, 650, 0, {ACT_DIALING} }, + +{RSP_ERROR, 601, 610, -1, 0, 0, {ACT_ABORTDIAL} }, +{EV_TIMEOUT, 601, 610, -1, 0, 0, {ACT_ABORTDIAL} }, + +/* optional dialing responses */ +{EV_BC_OPEN, 650, 650, -1, 651, -1}, +{RSP_ZVLS, 609, 651, 17, -1, -1, {ACT_DEBUG} }, +{RSP_ZCTP, 610, 651, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZCPN, 610, 651, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZSAU, 650, 651, ZSAU_CALL_DELIVERED, -1, -1, {ACT_DEBUG} }, + +/* connect */ +{RSP_ZSAU, 650, 650, ZSAU_ACTIVE, 800, -1, {ACT_CONNECT} }, +{RSP_ZSAU, 651, 651, ZSAU_ACTIVE, 800, -1, {ACT_CONNECT, + ACT_NOTIFY_BC_UP} }, +{RSP_ZSAU, 750, 750, ZSAU_ACTIVE, 800, -1, {ACT_CONNECT} }, +{RSP_ZSAU, 751, 751, ZSAU_ACTIVE, 800, -1, {ACT_CONNECT, + ACT_NOTIFY_BC_UP} }, +{EV_BC_OPEN, 800, 800, -1, 800, -1, {ACT_NOTIFY_BC_UP} }, + +/* remote hangup */ +{RSP_ZSAU, 650, 651, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEREJECT} }, +{RSP_ZSAU, 750, 751, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} }, +{RSP_ZSAU, 800, 800, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} }, + +/* hangup */ +{EV_HUP, -1, -1, -1, -1, -1, {ACT_HUP} }, +{RSP_INIT, -1, -1, SEQ_HUP, 401, 5, {0}, "+VLS=0\r"}, +{RSP_OK, 401, 401, -1, 402, 5}, +{RSP_ZVLS, 402, 402, 0, 403, 5}, +{RSP_ZSAU, 403, 403, ZSAU_DISCONNECT_REQ, -1, -1, {ACT_DEBUG} }, +{RSP_ZSAU, 403, 403, ZSAU_NULL, 0, 0, {ACT_DISCONNECT} }, +{RSP_NODEV, 401, 403, -1, 0, 0, {ACT_FAKEHUP} }, +{RSP_ERROR, 401, 401, -1, 0, 0, {ACT_ABORTHUP} }, +{EV_TIMEOUT, 401, 403, -1, 0, 0, {ACT_ABORTHUP} }, + +{EV_BC_CLOSED, 0, 0, -1, 0, -1, {ACT_NOTIFY_BC_DOWN} }, + +/* ring */ +{RSP_ZBC, 700, 700, -1, -1, -1, {0} }, +{RSP_ZHLC, 700, 700, -1, -1, -1, {0} }, +{RSP_NMBR, 700, 700, -1, -1, -1, {0} }, +{RSP_ZCPN, 700, 700, -1, -1, -1, {0} }, +{RSP_ZCTP, 700, 700, -1, -1, -1, {0} }, +{EV_TIMEOUT, 700, 700, -1, 720, 720, {ACT_ICALL} }, +{EV_BC_CLOSED, 720, 720, -1, 0, -1, {ACT_NOTIFY_BC_DOWN} }, + +/*accept icall*/ +{EV_ACCEPT, -1, -1, -1, -1, -1, {ACT_ACCEPT} }, +{RSP_INIT, 720, 720, SEQ_ACCEPT, 721, 5, {ACT_CMD+AT_PROTO} }, +{RSP_OK, 721, 721, -1, 722, 5, {ACT_CMD+AT_ISO} }, +{RSP_OK, 722, 722, -1, 723, 5, {0}, "+VLS=17\r"}, +{RSP_OK, 723, 723, -1, 724, 5, {0} }, +{RSP_ZVLS, 724, 724, 17, 750, 50, {ACT_ACCEPTED} }, +{RSP_ERROR, 721, 729, -1, 0, 0, {ACT_ABORTACCEPT} }, +{EV_TIMEOUT, 721, 729, -1, 0, 0, {ACT_ABORTACCEPT} }, +{RSP_ZSAU, 700, 729, ZSAU_NULL, 0, 0, {ACT_ABORTACCEPT} }, +{RSP_ZSAU, 700, 729, ZSAU_ACTIVE, 0, 0, {ACT_ABORTACCEPT} }, +{RSP_ZSAU, 700, 729, ZSAU_DISCONNECT_IND, 0, 0, {ACT_ABORTACCEPT} }, + +{EV_BC_OPEN, 750, 750, -1, 751, -1}, +{EV_TIMEOUT, 750, 751, -1, 0, 0, {ACT_CONNTIMEOUT} }, + +/* B channel closed (general case) */ +{EV_BC_CLOSED, -1, -1, -1, -1, -1, {ACT_NOTIFY_BC_DOWN} }, + +/* misc. */ +{RSP_ZCON, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZCCR, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZAOC, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ZCSTR, -1, -1, -1, -1, -1, {ACT_DEBUG} }, + +{RSP_ZCAU, -1, -1, -1, -1, -1, {ACT_ZCAU} }, +{RSP_NONE, -1, -1, -1, -1, -1, {ACT_DEBUG} }, +{RSP_ANY, -1, -1, -1, -1, -1, {ACT_WARN} }, +{RSP_LAST} }; -static const struct resp_type_t resp_type[] = +static const struct resp_type_t { + unsigned char *response; + int resp_code; + int type; +} resp_type[] = { - /*{"", RSP_EMPTY, RT_NOTHING},*/ {"OK", RSP_OK, RT_NOTHING}, {"ERROR", RSP_ERROR, RT_NOTHING}, {"ZSAU", RSP_ZSAU, RT_ZSAU}, @@ -405,7 +403,21 @@ static const struct resp_type_t resp_type[] = {"ZLOG", RSP_ZLOG, RT_NOTHING}, {"ZABINFO", RSP_ZABINFO, RT_NOTHING}, {"ZSMLSTCHG", RSP_ZSMLSTCHG, RT_NOTHING}, - {NULL,0,0} + {NULL, 0, 0} +}; + +static const struct zsau_resp_t { + unsigned char *str; + int code; +} zsau_resp[] = +{ + {"OUTGOING_CALL_PROCEEDING", ZSAU_OUTGOING_CALL_PROCEEDING}, + {"CALL_DELIVERED", ZSAU_CALL_DELIVERED}, + {"ACTIVE", ZSAU_ACTIVE}, + {"DISCONNECT_IND", ZSAU_DISCONNECT_IND}, + {"NULL", ZSAU_NULL}, + {"DISCONNECT_REQ", ZSAU_DISCONNECT_REQ}, + {NULL, ZSAU_UNKNOWN} }; /* @@ -470,7 +482,6 @@ static int cid_of_response(char *s) if (cid < 1 || cid > 65535) return -1; /* CID out of range */ return cid; - //FIXME is ;<digit>+ at end of non-CID response really impossible? } /** @@ -487,6 +498,7 @@ void gigaset_handle_modem_response(struct cardstate *cs) int params; int i, j; const struct resp_type_t *rt; + const struct zsau_resp_t *zr; int curarg; unsigned long flags; unsigned next, tail, head; @@ -613,24 +625,14 @@ void gigaset_handle_modem_response(struct cardstate *cs) event->parameter = ZSAU_NONE; break; } - if (!strcmp(argv[curarg], "OUTGOING_CALL_PROCEEDING")) - event->parameter = ZSAU_OUTGOING_CALL_PROCEEDING; - else if (!strcmp(argv[curarg], "CALL_DELIVERED")) - event->parameter = ZSAU_CALL_DELIVERED; - else if (!strcmp(argv[curarg], "ACTIVE")) - event->parameter = ZSAU_ACTIVE; - else if (!strcmp(argv[curarg], "DISCONNECT_IND")) - event->parameter = ZSAU_DISCONNECT_IND; - else if (!strcmp(argv[curarg], "NULL")) - event->parameter = ZSAU_NULL; - else if (!strcmp(argv[curarg], "DISCONNECT_REQ")) - event->parameter = ZSAU_DISCONNECT_REQ; - else { - event->parameter = ZSAU_UNKNOWN; + for (zr = zsau_resp; zr->str; ++zr) + if (!strcmp(argv[curarg], zr->str)) + break; + event->parameter = zr->code; + if (!zr->str) dev_warn(cs->dev, "%s: unknown parameter %s after ZSAU\n", __func__, argv[curarg]); - } ++curarg; break; case RT_STRING: @@ -714,7 +716,7 @@ static void disconnect(struct at_state_t **at_state_p) /* notify LL */ if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) { bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL); - gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP); + gigaset_isdn_hupD(bcs); } } else { /* no B channel assigned: just deallocate */ @@ -872,12 +874,12 @@ static void bchannel_down(struct bc_state *bcs) { if (bcs->chstate & CHS_B_UP) { bcs->chstate &= ~CHS_B_UP; - gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP); + gigaset_isdn_hupB(bcs); } if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) { bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL); - gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP); + gigaset_isdn_hupD(bcs); } gigaset_free_channel(bcs); @@ -894,15 +896,17 @@ static void bchannel_up(struct bc_state *bcs) } bcs->chstate |= CHS_B_UP; - gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN); + gigaset_isdn_connB(bcs); } -static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_index) +static void start_dial(struct at_state_t *at_state, void *data, + unsigned seq_index) { struct bc_state *bcs = at_state->bcs; struct cardstate *cs = at_state->cs; - int retval; + char **commands = data; unsigned long flags; + int i; bcs->chstate |= CHS_NOTIFY_LL; @@ -913,10 +917,10 @@ static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_ind } spin_unlock_irqrestore(&cs->lock, flags); - retval = gigaset_isdn_setup_dial(at_state, data); - if (retval != 0) - goto error; - + for (i = 0; i < AT_NUM; ++i) { + kfree(bcs->commands[i]); + bcs->commands[i] = commands[i]; + } at_state->pending_commands |= PC_CID; gig_dbg(DEBUG_CMD, "Scheduling PC_CID"); @@ -924,6 +928,10 @@ static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_ind return; error: + for (i = 0; i < AT_NUM; ++i) { + kfree(commands[i]); + commands[i] = NULL; + } at_state->pending_commands |= PC_NOCID; gig_dbg(DEBUG_CMD, "Scheduling PC_NOCID"); cs->commands_pending = 1; @@ -933,20 +941,31 @@ error: static void start_accept(struct at_state_t *at_state) { struct cardstate *cs = at_state->cs; - int retval; + struct bc_state *bcs = at_state->bcs; + int i; - retval = gigaset_isdn_setup_accept(at_state); + for (i = 0; i < AT_NUM; ++i) { + kfree(bcs->commands[i]); + bcs->commands[i] = NULL; + } - if (retval == 0) { - at_state->pending_commands |= PC_ACCEPT; - gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT"); - cs->commands_pending = 1; - } else { + bcs->commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC); + bcs->commands[AT_ISO] = kmalloc(9, GFP_ATOMIC); + if (!bcs->commands[AT_PROTO] || !bcs->commands[AT_ISO]) { + dev_err(at_state->cs->dev, "out of memory\n"); /* error reset */ at_state->pending_commands |= PC_HUP; gig_dbg(DEBUG_CMD, "Scheduling PC_HUP"); cs->commands_pending = 1; + return; } + + snprintf(bcs->commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2); + snprintf(bcs->commands[AT_ISO], 9, "^SISO=%u\r", bcs->channel + 1); + + at_state->pending_commands |= PC_ACCEPT; + gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT"); + cs->commands_pending = 1; } static void do_start(struct cardstate *cs) @@ -957,9 +976,7 @@ static void do_start(struct cardstate *cs) schedule_init(cs, MS_INIT); cs->isdn_up = 1; - gigaset_i4l_cmd(cs, ISDN_STAT_RUN); - // FIXME: not in locked mode - // FIXME 2: only after init sequence + gigaset_isdn_start(cs); cs->waiting = 0; wake_up(&cs->waitqueue); @@ -975,7 +992,7 @@ static void finish_shutdown(struct cardstate *cs) /* Tell the LL that the device is not available .. */ if (cs->isdn_up) { cs->isdn_up = 0; - gigaset_i4l_cmd(cs, ISDN_STAT_STOP); + gigaset_isdn_stop(cs); } /* The rest is done by cleanup_cs () in user mode. */ @@ -1113,7 +1130,6 @@ static int do_lock(struct cardstate *cs) break; case MS_LOCKED: - //retval = -EACCES; break; default: return -EBUSY; @@ -1276,7 +1292,7 @@ static void do_action(int action, struct cardstate *cs, break; } bcs->chstate |= CHS_D_UP; - gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN); + gigaset_isdn_connD(bcs); cs->ops->init_bchannel(bcs); break; case ACT_DLE1: @@ -1284,7 +1300,7 @@ static void do_action(int action, struct cardstate *cs, bcs = cs->bcs + cs->curchannel; bcs->chstate |= CHS_D_UP; - gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN); + gigaset_isdn_connD(bcs); cs->ops->init_bchannel(bcs); break; case ACT_FAKEHUP: @@ -1369,7 +1385,7 @@ static void do_action(int action, struct cardstate *cs, cs->cur_at_seq = SEQ_NONE; break; - case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL processing */ + case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL procssng */ disconnect(p_at_state); break; @@ -1443,17 +1459,6 @@ static void do_action(int action, struct cardstate *cs, __func__, at_state->ConState); cs->cur_at_seq = SEQ_NONE; break; -#ifdef CONFIG_GIGASET_DEBUG - case ACT_TEST: - { - static int count = 3; //2; //1; - *p_genresp = 1; - *p_resp_code = count ? RSP_ERROR : RSP_OK; - if (count > 0) - --count; - } - break; -#endif case ACT_DEBUG: gig_dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d", __func__, ev->type, at_state->ConState); @@ -1474,11 +1479,6 @@ static void do_action(int action, struct cardstate *cs, case ACT_ACCEPT: start_accept(at_state); break; - case ACT_PROTO_L2: - gig_dbg(DEBUG_CMD, "set protocol to %u", - (unsigned) ev->parameter); - at_state->bcs->proto2 = ev->parameter; - break; case ACT_HUP: at_state->pending_commands |= PC_HUP; cs->commands_pending = 1; @@ -1493,7 +1493,7 @@ static void do_action(int action, struct cardstate *cs, do_start(cs); break; - /* events from the interface */ // FIXME without ACT_xxxx? + /* events from the interface */ case ACT_IF_LOCK: cs->cmd_result = ev->parameter ? do_lock(cs) : do_unlock(cs); cs->waiting = 0; @@ -1512,7 +1512,7 @@ static void do_action(int action, struct cardstate *cs, wake_up(&cs->waitqueue); break; - /* events from the proc file system */ // FIXME without ACT_xxxx? + /* events from the proc file system */ case ACT_PROC_CIDMODE: spin_lock_irqsave(&cs->lock, flags); if (ev->parameter != cs->cidmode) { @@ -1649,7 +1649,8 @@ static void process_event(struct cardstate *cs, struct event_t *ev) for (curact = 0; curact < MAXACT; ++curact) { /* The row tells us what we should do .. */ - do_action(rep->action[curact], cs, bcs, &at_state, &p_command, &genresp, &resp_code, ev); + do_action(rep->action[curact], cs, bcs, &at_state, &p_command, + &genresp, &resp_code, ev); if (!at_state) break; /* may be freed after disconnect */ } @@ -1661,13 +1662,14 @@ static void process_event(struct cardstate *cs, struct event_t *ev) if (genresp) { spin_lock_irqsave(&cs->lock, flags); - at_state->timer_expires = 0; //FIXME - at_state->timer_active = 0; //FIXME + at_state->timer_expires = 0; + at_state->timer_active = 0; spin_unlock_irqrestore(&cs->lock, flags); - gigaset_add_event(cs, at_state, resp_code, NULL, 0, NULL); + gigaset_add_event(cs, at_state, resp_code, + NULL, 0, NULL); } else { /* Send command to modem if not NULL... */ - if (p_command/*rep->command*/) { + if (p_command) { if (cs->connected) send_command(cs, p_command, sendcid, cs->dle, @@ -1754,7 +1756,8 @@ static void process_command_flags(struct cardstate *cs) } } - /* only switch back to unimodem mode, if no commands are pending and no channels are up */ + /* only switch back to unimodem mode if no commands are pending and + * no channels are up */ spin_lock_irqsave(&cs->lock, flags); if (cs->at_state.pending_commands == PC_UMMODE && !cs->cidmode @@ -1813,9 +1816,8 @@ static void process_command_flags(struct cardstate *cs) if (cs->at_state.pending_commands & PC_INIT) { cs->at_state.pending_commands &= ~PC_INIT; - cs->dle = 0; //FIXME + cs->dle = 0; cs->inbuf->inputstate = INS_command; - //FIXME reset card state (or -> LOCK0)? schedule_sequence(cs, &cs->at_state, SEQ_INIT); return; } diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h index a2f6125739eb..e963a6c2e86d 100644 --- a/drivers/isdn/gigaset/gigaset.h +++ b/drivers/isdn/gigaset/gigaset.h @@ -23,7 +23,6 @@ #include <linux/compiler.h> #include <linux/types.h> #include <linux/spinlock.h> -#include <linux/isdnif.h> #include <linux/usb.h> #include <linux/skbuff.h> #include <linux/netdevice.h> @@ -35,12 +34,11 @@ #include <linux/list.h> #include <asm/atomic.h> -#define GIG_VERSION {0,5,0,0} -#define GIG_COMPAT {0,4,0,0} +#define GIG_VERSION {0, 5, 0, 0} +#define GIG_COMPAT {0, 4, 0, 0} #define MAX_REC_PARAMS 10 /* Max. number of params in response string */ #define MAX_RESP_SIZE 512 /* Max. size of a response string */ -#define HW_HDR_LEN 2 /* Header size used to store ack info */ #define MAX_EVENTS 64 /* size of event queue */ @@ -135,35 +133,32 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, #define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT) #define IN_VENDOR_REQ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT) -/* int-in-events 3070 */ +/* interrupt pipe messages */ #define HD_B1_FLOW_CONTROL 0x80 #define HD_B2_FLOW_CONTROL 0x81 -#define HD_RECEIVEATDATA_ACK (0x35) // 3070 - // att: HD_RECEIVE>>AT<<DATA_ACK -#define HD_READY_SEND_ATDATA (0x36) // 3070 -#define HD_OPEN_ATCHANNEL_ACK (0x37) // 3070 -#define HD_CLOSE_ATCHANNEL_ACK (0x38) // 3070 -#define HD_DEVICE_INIT_OK (0x11) // ISurf USB + 3070 -#define HD_OPEN_B1CHANNEL_ACK (0x51) // ISurf USB + 3070 -#define HD_OPEN_B2CHANNEL_ACK (0x52) // ISurf USB + 3070 -#define HD_CLOSE_B1CHANNEL_ACK (0x53) // ISurf USB + 3070 -#define HD_CLOSE_B2CHANNEL_ACK (0x54) // ISurf USB + 3070 -// Powermangment -#define HD_SUSPEND_END (0x61) // ISurf USB -// Configuration -#define HD_RESET_INTERRUPT_PIPE_ACK (0xFF) // ISurf USB + 3070 - -/* control requests 3070 */ -#define HD_OPEN_B1CHANNEL (0x23) // ISurf USB + 3070 -#define HD_CLOSE_B1CHANNEL (0x24) // ISurf USB + 3070 -#define HD_OPEN_B2CHANNEL (0x25) // ISurf USB + 3070 -#define HD_CLOSE_B2CHANNEL (0x26) // ISurf USB + 3070 -#define HD_RESET_INTERRUPT_PIPE (0x27) // ISurf USB + 3070 -#define HD_DEVICE_INIT_ACK (0x34) // ISurf USB + 3070 -#define HD_WRITE_ATMESSAGE (0x12) // 3070 -#define HD_READ_ATMESSAGE (0x13) // 3070 -#define HD_OPEN_ATCHANNEL (0x28) // 3070 -#define HD_CLOSE_ATCHANNEL (0x29) // 3070 +#define HD_RECEIVEATDATA_ACK (0x35) /* 3070 */ +#define HD_READY_SEND_ATDATA (0x36) /* 3070 */ +#define HD_OPEN_ATCHANNEL_ACK (0x37) /* 3070 */ +#define HD_CLOSE_ATCHANNEL_ACK (0x38) /* 3070 */ +#define HD_DEVICE_INIT_OK (0x11) /* ISurf USB + 3070 */ +#define HD_OPEN_B1CHANNEL_ACK (0x51) /* ISurf USB + 3070 */ +#define HD_OPEN_B2CHANNEL_ACK (0x52) /* ISurf USB + 3070 */ +#define HD_CLOSE_B1CHANNEL_ACK (0x53) /* ISurf USB + 3070 */ +#define HD_CLOSE_B2CHANNEL_ACK (0x54) /* ISurf USB + 3070 */ +#define HD_SUSPEND_END (0x61) /* ISurf USB */ +#define HD_RESET_INTERRUPT_PIPE_ACK (0xFF) /* ISurf USB + 3070 */ + +/* control requests */ +#define HD_OPEN_B1CHANNEL (0x23) /* ISurf USB + 3070 */ +#define HD_CLOSE_B1CHANNEL (0x24) /* ISurf USB + 3070 */ +#define HD_OPEN_B2CHANNEL (0x25) /* ISurf USB + 3070 */ +#define HD_CLOSE_B2CHANNEL (0x26) /* ISurf USB + 3070 */ +#define HD_RESET_INTERRUPT_PIPE (0x27) /* ISurf USB + 3070 */ +#define HD_DEVICE_INIT_ACK (0x34) /* ISurf USB + 3070 */ +#define HD_WRITE_ATMESSAGE (0x12) /* 3070 */ +#define HD_READ_ATMESSAGE (0x13) /* 3070 */ +#define HD_OPEN_ATCHANNEL (0x28) /* 3070 */ +#define HD_CLOSE_ATCHANNEL (0x29) /* 3070 */ /* number of B channels supported by base driver */ #define BAS_CHANNELS 2 @@ -193,7 +188,9 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, #define AT_PROTO 4 #define AT_TYPE 5 #define AT_HLC 6 -#define AT_NUM 7 +#define AT_CLIP 7 +/* total number */ +#define AT_NUM 8 /* variables in struct at_state_t */ #define VAR_ZSAU 0 @@ -216,7 +213,6 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, #define EV_START -110 #define EV_STOP -111 #define EV_IF_LOCK -112 -#define EV_PROTO_L2 -113 #define EV_ACCEPT -114 #define EV_DIAL -115 #define EV_HUP -116 @@ -224,12 +220,11 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, #define EV_BC_CLOSED -118 /* input state */ -#define INS_command 0x0001 -#define INS_DLE_char 0x0002 +#define INS_command 0x0001 /* receiving messages (not payload data) */ +#define INS_DLE_char 0x0002 /* DLE flag received (in DLE mode) */ #define INS_byte_stuff 0x0004 #define INS_have_data 0x0008 -#define INS_skip_frame 0x0010 -#define INS_DLE_command 0x0020 +#define INS_DLE_command 0x0020 /* DLE message start (<DLE> X) received */ #define INS_flag_hunt 0x0040 /* channel state */ @@ -259,6 +254,11 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, #define SM_LOCKED 0 #define SM_ISDN 1 /* default */ +/* layer 2 protocols (AT^SBPR=...) */ +#define L2_BITSYNC 0 +#define L2_HDLC 1 +#define L2_VOICE 2 + struct gigaset_ops; struct gigaset_driver; @@ -286,8 +286,6 @@ extern struct reply_t gigaset_tab_cid[]; extern struct reply_t gigaset_tab_nocid[]; struct inbuf_t { - unsigned char *rcvbuf; /* usb-gigaset receive buffer */ - struct bc_state *bcs; struct cardstate *cs; int inputstate; int head, tail; @@ -359,12 +357,6 @@ struct at_state_t { struct bc_state *bcs; }; -struct resp_type_t { - unsigned char *response; - int resp_code; /* RSP_XXXX */ - int type; /* RT_XXXX */ -}; - struct event_t { int type; void *ptr, *arg; @@ -395,7 +387,7 @@ struct bc_state { unsigned chstate; /* bitmap (CHS_*) */ int ignore; - unsigned proto2; /* Layer 2 protocol (ISDN_PROTO_L2_*) */ + unsigned proto2; /* layer 2 protocol (L2_*) */ char *commands[AT_NUM]; /* see AT_XXXX */ #ifdef CONFIG_GIGASET_DEBUG @@ -410,6 +402,8 @@ struct bc_state { struct usb_bc_state *usb; /* usb hardware driver (m105) */ struct bas_bc_state *bas; /* usb hardware driver (base) */ } hw; + + void *ap; /* LL application structure */ }; struct cardstate { @@ -456,12 +450,13 @@ struct cardstate { unsigned running; /* !=0 if events are handled */ unsigned connected; /* !=0 if hardware is connected */ - unsigned isdn_up; /* !=0 after ISDN_STAT_RUN */ + unsigned isdn_up; /* !=0 after gigaset_isdn_start() */ unsigned cidmode; int myid; /* id for communication with LL */ - isdn_if iif; + void *iif; /* LL interface structure */ + unsigned short hw_hdr_len; /* headroom needed in data skbs */ struct reply_t *tabnocid; struct reply_t *tabcid; @@ -476,8 +471,8 @@ struct cardstate { struct timer_list timer; int retry_count; - int dle; /* !=0 if modem commands/responses are - dle encoded */ + int dle; /* !=0 if DLE mode is active + (ZDLE=1 received -- M10x only) */ int cur_at_seq; /* sequence of AT commands being processed */ int curchannel; /* channel those commands are meant @@ -616,7 +611,9 @@ struct gigaset_ops { int (*baud_rate)(struct cardstate *cs, unsigned cflag); int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag); - /* Called from i4l.c to put an skb into the send-queue. */ + /* Called from LL interface to put an skb into the send-queue. + * After sending is completed, gigaset_skb_sent() must be called + * with the skb's link layer header preserved. */ int (*send_skb)(struct bc_state *bcs, struct sk_buff *skb); /* Called from ev-layer.c to process a block of data @@ -625,7 +622,8 @@ struct gigaset_ops { }; -/* = Common structures and definitions ======================================= */ +/* = Common structures and definitions ======================================= + */ /* Parser states for DLE-Event: * <DLE-EVENT>: <DLE_FLAG> "X" <EVENT> <DLE_FLAG> "." @@ -638,8 +636,7 @@ struct gigaset_ops { * Functions implemented in asyncdata.c */ -/* Called from i4l.c to put an skb into the send-queue. - * After sending gigaset_skb_sent() should be called. */ +/* Called from LL interface to put an skb into the send queue. */ int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb); /* Called from ev-layer.c to process a block of data @@ -650,8 +647,7 @@ void gigaset_m10x_input(struct inbuf_t *inbuf); * Functions implemented in isocdata.c */ -/* Called from i4l.c to put an skb into the send-queue. - * After sending gigaset_skb_sent() should be called. */ +/* Called from LL interface to put an skb into the send queue. */ int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb); /* Called from ev-layer.c to process a block of data @@ -674,36 +670,26 @@ void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle); int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size); /* =========================================================================== - * Functions implemented in i4l.c/gigaset.h + * Functions implemented in LL interface */ -/* Called by gigaset_initcs() for setting up with the isdn4linux subsystem */ -int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid); +/* Called from common.c for setting up/shutting down with the ISDN subsystem */ +int gigaset_isdn_register(struct cardstate *cs, const char *isdnid); +void gigaset_isdn_unregister(struct cardstate *cs); -/* Called from xxx-gigaset.c to indicate completion of sending an skb */ +/* Called from hardware module to indicate completion of an skb */ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb); +void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb); +void gigaset_isdn_rcv_err(struct bc_state *bcs); /* Called from common.c/ev-layer.c to indicate events relevant to the LL */ +void gigaset_isdn_start(struct cardstate *cs); +void gigaset_isdn_stop(struct cardstate *cs); int gigaset_isdn_icall(struct at_state_t *at_state); -int gigaset_isdn_setup_accept(struct at_state_t *at_state); -int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data); - -void gigaset_i4l_cmd(struct cardstate *cs, int cmd); -void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd); - - -static inline void gigaset_isdn_rcv_err(struct bc_state *bcs) -{ - isdn_ctrl response; - - /* error -> LL */ - gig_dbg(DEBUG_CMD, "sending L1ERR"); - response.driver = bcs->cs->myid; - response.command = ISDN_STAT_L1ERR; - response.arg = bcs->channel; - response.parm.errcode = ISDN_STAT_L1ERR_RECV; - bcs->cs->iif.statcallb(&response); -} +void gigaset_isdn_connD(struct bc_state *bcs); +void gigaset_isdn_hupD(struct bc_state *bcs); +void gigaset_isdn_connB(struct bc_state *bcs); +void gigaset_isdn_hupB(struct bc_state *bcs); /* =========================================================================== * Functions implemented in ev-layer.c @@ -732,6 +718,7 @@ void gigaset_bcs_reinit(struct bc_state *bcs); void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs, struct cardstate *cs, int cid); int gigaset_get_channel(struct bc_state *bcs); +struct bc_state *gigaset_get_free_channel(struct cardstate *cs); void gigaset_free_channel(struct bc_state *bcs); int gigaset_get_channels(struct cardstate *cs); void gigaset_free_channels(struct cardstate *cs); @@ -781,7 +768,7 @@ struct event_t *gigaset_add_event(struct cardstate *cs, void *ptr, int parameter, void *arg); /* Called on CONFIG1 command from frontend. */ -int gigaset_enterconfigmode(struct cardstate *cs); //0: success <0: errorcode +int gigaset_enterconfigmode(struct cardstate *cs); /* cs->lock must not be locked */ static inline void gigaset_schedule_event(struct cardstate *cs) @@ -816,35 +803,6 @@ static inline void gigaset_bchannel_up(struct bc_state *bcs) /* handling routines for sk_buff */ /* ============================= */ -/* pass received skb to LL - * Warning: skb must not be accessed anymore! - */ -static inline void gigaset_rcv_skb(struct sk_buff *skb, - struct cardstate *cs, - struct bc_state *bcs) -{ - cs->iif.rcvcallb_skb(cs->myid, bcs->channel, skb); - bcs->trans_down++; -} - -/* handle reception of corrupted skb - * Warning: skb must not be accessed anymore! - */ -static inline void gigaset_rcv_error(struct sk_buff *procskb, - struct cardstate *cs, - struct bc_state *bcs) -{ - if (procskb) - dev_kfree_skb(procskb); - - if (bcs->ignore) - --bcs->ignore; - else { - ++bcs->corrupted; - gigaset_isdn_rcv_err(bcs); - } -} - /* append received bytes to inbuf */ int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src, unsigned numbytes); diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c index 654489d836cd..c129ee47a8fb 100644 --- a/drivers/isdn/gigaset/i4l.c +++ b/drivers/isdn/gigaset/i4l.c @@ -14,6 +14,9 @@ */ #include "gigaset.h" +#include <linux/isdnif.h> + +#define HW_HDR_LEN 2 /* Header size used to store ack info */ /* == Handling of I4L IO =====================================================*/ @@ -36,12 +39,12 @@ static int writebuf_from_LL(int driverID, int channel, int ack, struct sk_buff *skb) { - struct cardstate *cs; + struct cardstate *cs = gigaset_get_cs_by_id(driverID); struct bc_state *bcs; + unsigned char *ack_header; unsigned len; - unsigned skblen; - if (!(cs = gigaset_get_cs_by_id(driverID))) { + if (!cs) { pr_err("%s: invalid driver ID (%d)\n", __func__, driverID); return -ENODEV; } @@ -75,11 +78,23 @@ static int writebuf_from_LL(int driverID, int channel, int ack, return -EINVAL; } - skblen = ack ? len : 0; - skb->head[0] = skblen & 0xff; - skb->head[1] = skblen >> 8; - gig_dbg(DEBUG_MCMD, "skb: len=%u, skblen=%u: %02x %02x", - len, skblen, (unsigned) skb->head[0], (unsigned) skb->head[1]); + /* set up acknowledgement header */ + if (skb_headroom(skb) < HW_HDR_LEN) { + /* should never happen */ + dev_err(cs->dev, "%s: insufficient skb headroom\n", __func__); + return -ENOMEM; + } + skb_set_mac_header(skb, -HW_HDR_LEN); + skb->mac_len = HW_HDR_LEN; + ack_header = skb_mac_header(skb); + if (ack) { + ack_header[0] = len & 0xff; + ack_header[1] = len >> 8; + } else { + ack_header[0] = ack_header[1] = 0; + } + gig_dbg(DEBUG_MCMD, "skb: len=%u, ack=%d: %02x %02x", + len, ack, ack_header[0], ack_header[1]); /* pass to device-specific module */ return cs->ops->send_skb(bcs, skb); @@ -95,6 +110,8 @@ static int writebuf_from_LL(int driverID, int channel, int ack, */ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) { + isdn_if *iif = bcs->cs->iif; + unsigned char *ack_header = skb_mac_header(skb); unsigned len; isdn_ctrl response; @@ -104,8 +121,7 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) dev_warn(bcs->cs->dev, "%s: skb->len==%d\n", __func__, skb->len); - len = (unsigned char) skb->head[0] | - (unsigned) (unsigned char) skb->head[1] << 8; + len = ack_header[0] + ((unsigned) ack_header[1] << 8); if (len) { gig_dbg(DEBUG_MCMD, "ACKing to LL (id: %d, ch: %d, sz: %u)", bcs->cs->myid, bcs->channel, len); @@ -114,71 +130,177 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) response.command = ISDN_STAT_BSENT; response.arg = bcs->channel; response.parm.length = len; - bcs->cs->iif.statcallb(&response); + iif->statcallb(&response); } } EXPORT_SYMBOL_GPL(gigaset_skb_sent); +/** + * gigaset_skb_rcvd() - pass received skb to LL + * @bcs: B channel descriptor structure. + * @skb: received data. + * + * Called by hardware module {bas,ser,usb}_gigaset when user data has + * been successfully received, for passing to the LL. + * Warning: skb must not be accessed anymore! + */ +void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb) +{ + isdn_if *iif = bcs->cs->iif; + + iif->rcvcallb_skb(bcs->cs->myid, bcs->channel, skb); + bcs->trans_down++; +} +EXPORT_SYMBOL_GPL(gigaset_skb_rcvd); + +/** + * gigaset_isdn_rcv_err() - signal receive error + * @bcs: B channel descriptor structure. + * + * Called by hardware module {bas,ser,usb}_gigaset when a receive error + * has occurred, for signalling to the LL. + */ +void gigaset_isdn_rcv_err(struct bc_state *bcs) +{ + isdn_if *iif = bcs->cs->iif; + isdn_ctrl response; + + /* if currently ignoring packets, just count down */ + if (bcs->ignore) { + bcs->ignore--; + return; + } + + /* update statistics */ + bcs->corrupted++; + + /* error -> LL */ + gig_dbg(DEBUG_CMD, "sending L1ERR"); + response.driver = bcs->cs->myid; + response.command = ISDN_STAT_L1ERR; + response.arg = bcs->channel; + response.parm.errcode = ISDN_STAT_L1ERR_RECV; + iif->statcallb(&response); +} +EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err); + /* This function will be called by LL to send commands * NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL, * so don't put too much effort into it. */ static int command_from_LL(isdn_ctrl *cntrl) { - struct cardstate *cs = gigaset_get_cs_by_id(cntrl->driver); + struct cardstate *cs; struct bc_state *bcs; int retval = 0; - struct setup_parm *sp; + char **commands; + int ch; + int i; + size_t l; gigaset_debugdrivers(); - if (!cs) { + gig_dbg(DEBUG_CMD, "driver: %d, command: %d, arg: 0x%lx", + cntrl->driver, cntrl->command, cntrl->arg); + + cs = gigaset_get_cs_by_id(cntrl->driver); + if (cs == NULL) { pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver); return -ENODEV; } + ch = cntrl->arg & 0xff; switch (cntrl->command) { case ISDN_CMD_IOCTL: - gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)", - cntrl->driver, cntrl->arg); - dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n"); return -EINVAL; case ISDN_CMD_DIAL: gig_dbg(DEBUG_ANY, - "ISDN_CMD_DIAL (driver: %d, ch: %ld, " - "phone: %s, ownmsn: %s, si1: %d, si2: %d)", - cntrl->driver, cntrl->arg, + "ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)", cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn, cntrl->parm.setup.si1, cntrl->parm.setup.si2); - if (cntrl->arg >= cs->channels) { + if (ch >= cs->channels) { dev_err(cs->dev, - "ISDN_CMD_DIAL: invalid channel (%d)\n", - (int) cntrl->arg); + "ISDN_CMD_DIAL: invalid channel (%d)\n", ch); return -EINVAL; } - - bcs = cs->bcs + cntrl->arg; - + bcs = cs->bcs + ch; if (!gigaset_get_channel(bcs)) { dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n"); return -EBUSY; } - sp = kmalloc(sizeof *sp, GFP_ATOMIC); - if (!sp) { + commands = kzalloc(AT_NUM*(sizeof *commands), GFP_ATOMIC); + if (!commands) { gigaset_free_channel(bcs); dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n"); return -ENOMEM; } - *sp = cntrl->parm.setup; - if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp, + l = 3 + strlen(cntrl->parm.setup.phone); + commands[AT_DIAL] = kmalloc(l, GFP_ATOMIC); + if (!commands[AT_DIAL]) + goto oom; + if (cntrl->parm.setup.phone[0] == '*' && + cntrl->parm.setup.phone[1] == '*') { + /* internal call: translate ** prefix to CTP value */ + commands[AT_TYPE] = kstrdup("^SCTP=0\r", GFP_ATOMIC); + if (!commands[AT_TYPE]) + goto oom; + snprintf(commands[AT_DIAL], l, + "D%s\r", cntrl->parm.setup.phone+2); + } else { + commands[AT_TYPE] = kstrdup("^SCTP=1\r", GFP_ATOMIC); + if (!commands[AT_TYPE]) + goto oom; + snprintf(commands[AT_DIAL], l, + "D%s\r", cntrl->parm.setup.phone); + } + + l = strlen(cntrl->parm.setup.eazmsn); + if (l) { + l += 8; + commands[AT_MSN] = kmalloc(l, GFP_ATOMIC); + if (!commands[AT_MSN]) + goto oom; + snprintf(commands[AT_MSN], l, "^SMSN=%s\r", + cntrl->parm.setup.eazmsn); + } + + switch (cntrl->parm.setup.si1) { + case 1: /* audio */ + /* BC = 9090A3: 3.1 kHz audio, A-law */ + commands[AT_BC] = kstrdup("^SBC=9090A3\r", GFP_ATOMIC); + if (!commands[AT_BC]) + goto oom; + break; + case 7: /* data */ + default: /* hope the app knows what it is doing */ + /* BC = 8890: unrestricted digital information */ + commands[AT_BC] = kstrdup("^SBC=8890\r", GFP_ATOMIC); + if (!commands[AT_BC]) + goto oom; + } + /* ToDo: other si1 values, inspect si2, set HLC/LLC */ + + commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC); + if (!commands[AT_PROTO]) + goto oom; + snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2); + + commands[AT_ISO] = kmalloc(9, GFP_ATOMIC); + if (!commands[AT_ISO]) + goto oom; + snprintf(commands[AT_ISO], 9, "^SISO=%u\r", + (unsigned) bcs->channel + 1); + + if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands, bcs->at_state.seq_index, NULL)) { - //FIXME what should we do? - kfree(sp); + for (i = 0; i < AT_NUM; ++i) + kfree(commands[i]); + kfree(commands); gigaset_free_channel(bcs); return -ENOMEM; } @@ -186,115 +308,102 @@ static int command_from_LL(isdn_ctrl *cntrl) gig_dbg(DEBUG_CMD, "scheduling DIAL"); gigaset_schedule_event(cs); break; - case ISDN_CMD_ACCEPTD: //FIXME - gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD"); - - if (cntrl->arg >= cs->channels) { + case ISDN_CMD_ACCEPTD: + if (ch >= cs->channels) { dev_err(cs->dev, - "ISDN_CMD_ACCEPTD: invalid channel (%d)\n", - (int) cntrl->arg); + "ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch); return -EINVAL; } - - if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state, - EV_ACCEPT, NULL, 0, NULL)) { - //FIXME what should we do? + bcs = cs->bcs + ch; + if (!gigaset_add_event(cs, &bcs->at_state, + EV_ACCEPT, NULL, 0, NULL)) return -ENOMEM; - } gig_dbg(DEBUG_CMD, "scheduling ACCEPT"); gigaset_schedule_event(cs); break; case ISDN_CMD_ACCEPTB: - gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTB"); break; case ISDN_CMD_HANGUP: - gig_dbg(DEBUG_ANY, "ISDN_CMD_HANGUP (ch: %d)", - (int) cntrl->arg); - - if (cntrl->arg >= cs->channels) { + if (ch >= cs->channels) { dev_err(cs->dev, - "ISDN_CMD_HANGUP: invalid channel (%d)\n", - (int) cntrl->arg); + "ISDN_CMD_HANGUP: invalid channel (%d)\n", ch); return -EINVAL; } - - if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state, - EV_HUP, NULL, 0, NULL)) { - //FIXME what should we do? + bcs = cs->bcs + ch; + if (!gigaset_add_event(cs, &bcs->at_state, + EV_HUP, NULL, 0, NULL)) return -ENOMEM; - } gig_dbg(DEBUG_CMD, "scheduling HUP"); gigaset_schedule_event(cs); break; - case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ //FIXME - gig_dbg(DEBUG_ANY, "ISDN_CMD_CLREAZ"); + case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ + dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n"); break; - case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ //FIXME - gig_dbg(DEBUG_ANY, - "ISDN_CMD_SETEAZ (id: %d, ch: %ld, number: %s)", - cntrl->driver, cntrl->arg, cntrl->parm.num); + case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ + dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n", + cntrl->parm.num); break; case ISDN_CMD_SETL2: /* Set L2 to given protocol */ - gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL2 (ch: %ld, proto: %lx)", - cntrl->arg & 0xff, (cntrl->arg >> 8)); - - if ((cntrl->arg & 0xff) >= cs->channels) { + if (ch >= cs->channels) { dev_err(cs->dev, - "ISDN_CMD_SETL2: invalid channel (%d)\n", - (int) cntrl->arg & 0xff); + "ISDN_CMD_SETL2: invalid channel (%d)\n", ch); return -EINVAL; } - - if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg & 0xff].at_state, - EV_PROTO_L2, NULL, cntrl->arg >> 8, - NULL)) { - //FIXME what should we do? - return -ENOMEM; + bcs = cs->bcs + ch; + if (bcs->chstate & CHS_D_UP) { + dev_err(cs->dev, + "ISDN_CMD_SETL2: channel active (%d)\n", ch); + return -EINVAL; + } + switch (cntrl->arg >> 8) { + case ISDN_PROTO_L2_HDLC: + gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_HDLC"); + bcs->proto2 = L2_HDLC; + break; + case ISDN_PROTO_L2_TRANS: + gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_VOICE"); + bcs->proto2 = L2_VOICE; + break; + default: + dev_err(cs->dev, + "ISDN_CMD_SETL2: unsupported protocol (%lu)\n", + cntrl->arg >> 8); + return -EINVAL; } - - gig_dbg(DEBUG_CMD, "scheduling PROTO_L2"); - gigaset_schedule_event(cs); break; case ISDN_CMD_SETL3: /* Set L3 to given protocol */ - gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL3 (ch: %ld, proto: %lx)", - cntrl->arg & 0xff, (cntrl->arg >> 8)); - - if ((cntrl->arg & 0xff) >= cs->channels) { + if (ch >= cs->channels) { dev_err(cs->dev, - "ISDN_CMD_SETL3: invalid channel (%d)\n", - (int) cntrl->arg & 0xff); + "ISDN_CMD_SETL3: invalid channel (%d)\n", ch); return -EINVAL; } if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) { dev_err(cs->dev, - "ISDN_CMD_SETL3: invalid protocol %lu\n", + "ISDN_CMD_SETL3: unsupported protocol (%lu)\n", cntrl->arg >> 8); return -EINVAL; } break; case ISDN_CMD_PROCEED: - gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED"); //FIXME + gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED"); break; case ISDN_CMD_ALERT: - gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME + gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); if (cntrl->arg >= cs->channels) { dev_err(cs->dev, "ISDN_CMD_ALERT: invalid channel (%d)\n", (int) cntrl->arg); return -EINVAL; } - //bcs = cs->bcs + cntrl->arg; - //bcs->proto2 = -1; - // FIXME break; case ISDN_CMD_REDIR: - gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR"); //FIXME + gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR"); break; case ISDN_CMD_PROT_IO: gig_dbg(DEBUG_ANY, "ISDN_CMD_PROT_IO"); @@ -324,149 +433,34 @@ static int command_from_LL(isdn_ctrl *cntrl) } return retval; + +oom: + dev_err(bcs->cs->dev, "out of memory\n"); + for (i = 0; i < AT_NUM; ++i) + kfree(commands[i]); + return -ENOMEM; } -void gigaset_i4l_cmd(struct cardstate *cs, int cmd) +static void gigaset_i4l_cmd(struct cardstate *cs, int cmd) { + isdn_if *iif = cs->iif; isdn_ctrl command; command.driver = cs->myid; command.command = cmd; command.arg = 0; - cs->iif.statcallb(&command); + iif->statcallb(&command); } -void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd) +static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd) { + isdn_if *iif = bcs->cs->iif; isdn_ctrl command; command.driver = bcs->cs->myid; command.command = cmd; command.arg = bcs->channel; - bcs->cs->iif.statcallb(&command); -} - -int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data) -{ - struct bc_state *bcs = at_state->bcs; - unsigned proto; - const char *bc; - size_t length[AT_NUM]; - size_t l; - int i; - struct setup_parm *sp = data; - - switch (bcs->proto2) { - case ISDN_PROTO_L2_HDLC: - proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */ - break; - case ISDN_PROTO_L2_TRANS: - proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */ - break; - default: - dev_err(bcs->cs->dev, "%s: invalid L2 protocol: %u\n", - __func__, bcs->proto2); - return -EINVAL; - } - - switch (sp->si1) { - case 1: /* audio */ - bc = "9090A3"; /* 3.1 kHz audio, A-law */ - break; - case 7: /* data */ - default: /* hope the app knows what it is doing */ - bc = "8890"; /* unrestricted digital information */ - } - //FIXME add missing si1 values from 1TR6, inspect si2, set HLC/LLC - - length[AT_DIAL ] = 1 + strlen(sp->phone) + 1 + 1; - l = strlen(sp->eazmsn); - length[AT_MSN ] = l ? 6 + l + 1 + 1 : 0; - length[AT_BC ] = 5 + strlen(bc) + 1 + 1; - length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */ - length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */ - length[AT_TYPE ] = 6 + 1 + 1 + 1; /* call type: 1 character */ - length[AT_HLC ] = 0; - - for (i = 0; i < AT_NUM; ++i) { - kfree(bcs->commands[i]); - bcs->commands[i] = NULL; - if (length[i] && - !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) { - dev_err(bcs->cs->dev, "out of memory\n"); - return -ENOMEM; - } - } - - /* type = 1: extern, 0: intern, 2: recall, 3: door, 4: centrex */ - if (sp->phone[0] == '*' && sp->phone[1] == '*') { - /* internal call: translate ** prefix to CTP value */ - snprintf(bcs->commands[AT_DIAL], length[AT_DIAL], - "D%s\r", sp->phone+2); - strncpy(bcs->commands[AT_TYPE], "^SCTP=0\r", length[AT_TYPE]); - } else { - snprintf(bcs->commands[AT_DIAL], length[AT_DIAL], - "D%s\r", sp->phone); - strncpy(bcs->commands[AT_TYPE], "^SCTP=1\r", length[AT_TYPE]); - } - - if (bcs->commands[AT_MSN]) - snprintf(bcs->commands[AT_MSN], length[AT_MSN], - "^SMSN=%s\r", sp->eazmsn); - snprintf(bcs->commands[AT_BC ], length[AT_BC ], - "^SBC=%s\r", bc); - snprintf(bcs->commands[AT_PROTO], length[AT_PROTO], - "^SBPR=%u\r", proto); - snprintf(bcs->commands[AT_ISO ], length[AT_ISO ], - "^SISO=%u\r", (unsigned)bcs->channel + 1); - - return 0; -} - -int gigaset_isdn_setup_accept(struct at_state_t *at_state) -{ - unsigned proto; - size_t length[AT_NUM]; - int i; - struct bc_state *bcs = at_state->bcs; - - switch (bcs->proto2) { - case ISDN_PROTO_L2_HDLC: - proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */ - break; - case ISDN_PROTO_L2_TRANS: - proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */ - break; - default: - dev_err(at_state->cs->dev, "%s: invalid protocol: %u\n", - __func__, bcs->proto2); - return -EINVAL; - } - - length[AT_DIAL ] = 0; - length[AT_MSN ] = 0; - length[AT_BC ] = 0; - length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */ - length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */ - length[AT_TYPE ] = 0; - length[AT_HLC ] = 0; - - for (i = 0; i < AT_NUM; ++i) { - kfree(bcs->commands[i]); - bcs->commands[i] = NULL; - if (length[i] && - !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) { - dev_err(at_state->cs->dev, "out of memory\n"); - return -ENOMEM; - } - } - - snprintf(bcs->commands[AT_PROTO], length[AT_PROTO], - "^SBPR=%u\r", proto); - snprintf(bcs->commands[AT_ISO ], length[AT_ISO ], - "^SISO=%u\r", (unsigned) bcs->channel + 1); - - return 0; + iif->statcallb(&command); } /** @@ -482,13 +476,14 @@ int gigaset_isdn_icall(struct at_state_t *at_state) { struct cardstate *cs = at_state->cs; struct bc_state *bcs = at_state->bcs; + isdn_if *iif = cs->iif; isdn_ctrl response; int retval; /* fill ICALL structure */ response.parm.setup.si1 = 0; /* default: unknown */ response.parm.setup.si2 = 0; - response.parm.setup.screen = 0; //FIXME how to set these? + response.parm.setup.screen = 0; response.parm.setup.plan = 0; if (!at_state->str_var[STR_ZBC]) { /* no BC (internal call): assume speech, A-law */ @@ -509,29 +504,27 @@ int gigaset_isdn_icall(struct at_state_t *at_state) return ICALL_IGNORE; } if (at_state->str_var[STR_NMBR]) { - strncpy(response.parm.setup.phone, at_state->str_var[STR_NMBR], - sizeof response.parm.setup.phone - 1); - response.parm.setup.phone[sizeof response.parm.setup.phone - 1] = 0; + strlcpy(response.parm.setup.phone, at_state->str_var[STR_NMBR], + sizeof response.parm.setup.phone); } else response.parm.setup.phone[0] = 0; if (at_state->str_var[STR_ZCPN]) { - strncpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN], - sizeof response.parm.setup.eazmsn - 1); - response.parm.setup.eazmsn[sizeof response.parm.setup.eazmsn - 1] = 0; + strlcpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN], + sizeof response.parm.setup.eazmsn); } else response.parm.setup.eazmsn[0] = 0; if (!bcs) { dev_notice(cs->dev, "no channel for incoming call\n"); response.command = ISDN_STAT_ICALLW; - response.arg = 0; //FIXME + response.arg = 0; } else { gig_dbg(DEBUG_CMD, "Sending ICALL"); response.command = ISDN_STAT_ICALL; - response.arg = bcs->channel; //FIXME + response.arg = bcs->channel; } response.driver = cs->myid; - retval = cs->iif.statcallb(&response); + retval = iif->statcallb(&response); gig_dbg(DEBUG_CMD, "Response: %d", retval); switch (retval) { case 0: /* no takers */ @@ -560,16 +553,109 @@ int gigaset_isdn_icall(struct at_state_t *at_state) } } -/* Set Callback function pointer */ -int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid) +/** + * gigaset_isdn_connD() - signal D channel connect + * @bcs: B channel descriptor structure. + * + * Called by main module to notify the LL that the D channel connection has + * been established. + */ +void gigaset_isdn_connD(struct bc_state *bcs) { - isdn_if *iif = &cs->iif; + gig_dbg(DEBUG_CMD, "sending DCONN"); + gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN); +} - gig_dbg(DEBUG_ANY, "Register driver capabilities to LL"); +/** + * gigaset_isdn_hupD() - signal D channel hangup + * @bcs: B channel descriptor structure. + * + * Called by main module to notify the LL that the D channel connection has + * been shut down. + */ +void gigaset_isdn_hupD(struct bc_state *bcs) +{ + gig_dbg(DEBUG_CMD, "sending DHUP"); + gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP); +} + +/** + * gigaset_isdn_connB() - signal B channel connect + * @bcs: B channel descriptor structure. + * + * Called by main module to notify the LL that the B channel connection has + * been established. + */ +void gigaset_isdn_connB(struct bc_state *bcs) +{ + gig_dbg(DEBUG_CMD, "sending BCONN"); + gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN); +} + +/** + * gigaset_isdn_hupB() - signal B channel hangup + * @bcs: B channel descriptor structure. + * + * Called by main module to notify the LL that the B channel connection has + * been shut down. + */ +void gigaset_isdn_hupB(struct bc_state *bcs) +{ + gig_dbg(DEBUG_CMD, "sending BHUP"); + gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP); +} + +/** + * gigaset_isdn_start() - signal device availability + * @cs: device descriptor structure. + * + * Called by main module to notify the LL that the device is available for + * use. + */ +void gigaset_isdn_start(struct cardstate *cs) +{ + gig_dbg(DEBUG_CMD, "sending RUN"); + gigaset_i4l_cmd(cs, ISDN_STAT_RUN); +} + +/** + * gigaset_isdn_stop() - signal device unavailability + * @cs: device descriptor structure. + * + * Called by main module to notify the LL that the device is no longer + * available for use. + */ +void gigaset_isdn_stop(struct cardstate *cs) +{ + gig_dbg(DEBUG_CMD, "sending STOP"); + gigaset_i4l_cmd(cs, ISDN_STAT_STOP); +} + +/** + * gigaset_isdn_register() - register to LL + * @cs: device descriptor structure. + * @isdnid: device name. + * + * Called by main module to register the device with the LL. + * + * Return value: 1 for success, 0 for failure + */ +int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) +{ + isdn_if *iif; + + pr_info("ISDN4Linux interface\n"); + + iif = kmalloc(sizeof *iif, GFP_KERNEL); + if (!iif) { + pr_err("out of memory\n"); + return 0; + } if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index) >= sizeof iif->id) { pr_err("ID too long: %s\n", isdnid); + kfree(iif); return 0; } @@ -593,9 +679,26 @@ int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid) if (!register_isdn(iif)) { pr_err("register_isdn failed\n"); + kfree(iif); return 0; } + cs->iif = iif; cs->myid = iif->channels; /* Set my device id */ + cs->hw_hdr_len = HW_HDR_LEN; return 1; } + +/** + * gigaset_isdn_unregister() - unregister from LL + * @cs: device descriptor structure. + * + * Called by main module to unregister the device from the LL. + */ +void gigaset_isdn_unregister(struct cardstate *cs) +{ + gig_dbg(DEBUG_CMD, "sending UNLOAD"); + gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD); + kfree(cs->iif); + cs->iif = NULL; +} diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index 6a8e1384e7bd..d2260b0055fc 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -162,7 +162,7 @@ static int if_open(struct tty_struct *tty, struct file *filp) return -ENODEV; if (mutex_lock_interruptible(&cs->mutex)) - return -ERESTARTSYS; // FIXME -EINTR? + return -ERESTARTSYS; tty->driver_data = cs; ++cs->open_count; @@ -171,7 +171,7 @@ static int if_open(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&cs->lock, flags); cs->tty = tty; spin_unlock_irqrestore(&cs->lock, flags); - tty->low_latency = 1; //FIXME test + tty->low_latency = 1; } mutex_unlock(&cs->mutex); @@ -228,7 +228,7 @@ static int if_ioctl(struct tty_struct *tty, struct file *file, gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd); if (mutex_lock_interruptible(&cs->mutex)) - return -ERESTARTSYS; // FIXME -EINTR? + return -ERESTARTSYS; if (!cs->connected) { gig_dbg(DEBUG_IF, "not connected"); @@ -299,9 +299,8 @@ static int if_tiocmget(struct tty_struct *tty, struct file *file) gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); if (mutex_lock_interruptible(&cs->mutex)) - return -ERESTARTSYS; // FIXME -EINTR? + return -ERESTARTSYS; - // FIXME read from device? retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR); mutex_unlock(&cs->mutex); @@ -326,7 +325,7 @@ static int if_tiocmset(struct tty_struct *tty, struct file *file, cs->minor_index, __func__, set, clear); if (mutex_lock_interruptible(&cs->mutex)) - return -ERESTARTSYS; // FIXME -EINTR? + return -ERESTARTSYS; if (!cs->connected) { gig_dbg(DEBUG_IF, "not connected"); @@ -356,7 +355,7 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count) gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); if (mutex_lock_interruptible(&cs->mutex)) - return -ERESTARTSYS; // FIXME -EINTR? + return -ERESTARTSYS; if (!cs->connected) { gig_dbg(DEBUG_IF, "not connected"); @@ -390,7 +389,7 @@ static int if_write_room(struct tty_struct *tty) gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); if (mutex_lock_interruptible(&cs->mutex)) - return -ERESTARTSYS; // FIXME -EINTR? + return -ERESTARTSYS; if (!cs->connected) { gig_dbg(DEBUG_IF, "not connected"); @@ -455,9 +454,8 @@ static void if_throttle(struct tty_struct *tty) gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */ else if (!cs->open_count) dev_warn(cs->dev, "%s: device not opened\n", __func__); - else { - //FIXME - } + else + gig_dbg(DEBUG_ANY, "%s: not implemented\n", __func__); mutex_unlock(&cs->mutex); } @@ -480,9 +478,8 @@ static void if_unthrottle(struct tty_struct *tty) gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */ else if (!cs->open_count) dev_warn(cs->dev, "%s: device not opened\n", __func__); - else { - //FIXME - } + else + gig_dbg(DEBUG_ANY, "%s: not implemented\n", __func__); mutex_unlock(&cs->mutex); } @@ -515,10 +512,9 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old) goto out; } - // stolen from mct_u232.c iflag = tty->termios->c_iflag; cflag = tty->termios->c_cflag; - old_cflag = old ? old->c_cflag : cflag; //FIXME? + old_cflag = old ? old->c_cflag : cflag; gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x", cs->minor_index, iflag, cflag, old_cflag); @@ -588,7 +584,7 @@ void gigaset_if_init(struct cardstate *cs) if (!drv->have_tty) return; - tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs); + tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs); mutex_lock(&cs->mutex); cs->tty_dev = tty_register_device(drv->tty, cs->minor_index, NULL); @@ -632,7 +628,8 @@ void gigaset_if_receive(struct cardstate *cs, struct tty_struct *tty; spin_lock_irqsave(&cs->lock, flags); - if ((tty = cs->tty) == NULL) + tty = cs->tty; + if (tty == NULL) gig_dbg(DEBUG_ANY, "receive on closed device"); else { tty_buffer_request_room(tty, len); @@ -659,9 +656,9 @@ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname, drv->have_tty = 0; - if ((drv->tty = alloc_tty_driver(minors)) == NULL) + drv->tty = tty = alloc_tty_driver(minors); + if (tty == NULL) goto enomem; - tty = drv->tty; tty->magic = TTY_DRIVER_MAGIC, tty->major = GIG_MAJOR, @@ -676,8 +673,8 @@ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname, tty->owner = THIS_MODULE; - tty->init_termios = tty_std_termios; //FIXME - tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME + tty->init_termios = tty_std_termios; + tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(tty, &if_ops); ret = tty_register_driver(tty); diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c index 9f3ef7b4248c..85394a6ebae8 100644 --- a/drivers/isdn/gigaset/isocdata.c +++ b/drivers/isdn/gigaset/isocdata.c @@ -41,7 +41,8 @@ static inline int isowbuf_freebytes(struct isowbuf_t *iwb) read = iwb->read; write = iwb->write; - if ((freebytes = read - write) > 0) { + freebytes = read - write; + if (freebytes > 0) { /* no wraparound: need padding space within regular area */ return freebytes - BAS_OUTBUFPAD; } else if (read < BAS_OUTBUFPAD) { @@ -53,29 +54,6 @@ static inline int isowbuf_freebytes(struct isowbuf_t *iwb) } } -/* compare two offsets within the buffer - * The buffer is seen as circular, with the read position as start - * returns -1/0/1 if position a </=/> position b without crossing 'read' - */ -static inline int isowbuf_poscmp(struct isowbuf_t *iwb, int a, int b) -{ - int read; - if (a == b) - return 0; - read = iwb->read; - if (a < b) { - if (a < read && read <= b) - return +1; - else - return -1; - } else { - if (b < read && read <= a) - return -1; - else - return +1; - } -} - /* start writing * acquire the write semaphore * return true if acquired, false if busy @@ -271,7 +249,7 @@ static inline void dump_bytes(enum debuglevel level, const char *tag, * bit 14..13 = number of bits added by stuffing */ static const u16 stufftab[5 * 256] = { -// previous 1s = 0: +/* previous 1s = 0: */ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x201f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, @@ -289,7 +267,7 @@ static const u16 stufftab[5 * 256] = { 0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x0cef, 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x2ddf, -// previous 1s = 1: +/* previous 1s = 1: */ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x200f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x202f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x204f, @@ -307,7 +285,7 @@ static const u16 stufftab[5 * 256] = { 0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dcf, 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x31ef, -// previous 1s = 2: +/* previous 1s = 2: */ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x2007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x2017, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x2027, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x2037, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x2047, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x2057, @@ -325,7 +303,7 @@ static const u16 stufftab[5 * 256] = { 0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dc7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dd7, 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x31e7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x41f7, -// previous 1s = 3: +/* previous 1s = 3: */ 0x0000, 0x0001, 0x0002, 0x2003, 0x0004, 0x0005, 0x0006, 0x200b, 0x0008, 0x0009, 0x000a, 0x2013, 0x000c, 0x000d, 0x000e, 0x201b, 0x0010, 0x0011, 0x0012, 0x2023, 0x0014, 0x0015, 0x0016, 0x202b, 0x0018, 0x0019, 0x001a, 0x2033, 0x001c, 0x001d, 0x001e, 0x203b, 0x0020, 0x0021, 0x0022, 0x2043, 0x0024, 0x0025, 0x0026, 0x204b, 0x0028, 0x0029, 0x002a, 0x2053, 0x002c, 0x002d, 0x002e, 0x205b, @@ -343,7 +321,7 @@ static const u16 stufftab[5 * 256] = { 0x0ce0, 0x0ce1, 0x0ce2, 0x2dc3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dcb, 0x0ce8, 0x0ce9, 0x0cea, 0x2dd3, 0x0cec, 0x0ced, 0x0cee, 0x2ddb, 0x10f0, 0x10f1, 0x10f2, 0x31e3, 0x10f4, 0x10f5, 0x10f6, 0x31eb, 0x20f8, 0x20f9, 0x20fa, 0x41f3, 0x257c, 0x257d, 0x29be, 0x46fb, -// previous 1s = 4: +/* previous 1s = 4: */ 0x0000, 0x2001, 0x0002, 0x2005, 0x0004, 0x2009, 0x0006, 0x200d, 0x0008, 0x2011, 0x000a, 0x2015, 0x000c, 0x2019, 0x000e, 0x201d, 0x0010, 0x2021, 0x0012, 0x2025, 0x0014, 0x2029, 0x0016, 0x202d, 0x0018, 0x2031, 0x001a, 0x2035, 0x001c, 0x2039, 0x001e, 0x203d, 0x0020, 0x2041, 0x0022, 0x2045, 0x0024, 0x2049, 0x0026, 0x204d, 0x0028, 0x2051, 0x002a, 0x2055, 0x002c, 0x2059, 0x002e, 0x205d, @@ -367,7 +345,8 @@ static const u16 stufftab[5 * 256] = { * parameters: * cin input byte * ones number of trailing '1' bits in result before this step - * iwb pointer to output buffer structure (write semaphore must be held) + * iwb pointer to output buffer structure + * (write semaphore must be held) * return value: * number of trailing '1' bits in result after this step */ @@ -408,7 +387,8 @@ static inline int hdlc_bitstuff_byte(struct isowbuf_t *iwb, unsigned char cin, * parameters: * in input buffer * count number of bytes in input buffer - * iwb pointer to output buffer structure (write semaphore must be held) + * iwb pointer to output buffer structure + * (write semaphore must be held) * return value: * position of end of packet in output buffer on success, * -EAGAIN if write semaphore busy or buffer full @@ -440,7 +420,8 @@ static inline int hdlc_buildframe(struct isowbuf_t *iwb, fcs = crc_ccitt_byte(fcs, c); } - /* bitstuff and append FCS (complemented, least significant byte first) */ + /* bitstuff and append FCS + * (complemented, least significant byte first) */ fcs ^= 0xffff; ones = hdlc_bitstuff_byte(iwb, fcs & 0x00ff, ones); ones = hdlc_bitstuff_byte(iwb, (fcs >> 8) & 0x00ff, ones); @@ -459,7 +440,8 @@ static inline int hdlc_buildframe(struct isowbuf_t *iwb, * parameters: * in input buffer * count number of bytes in input buffer - * iwb pointer to output buffer structure (write semaphore must be held) + * iwb pointer to output buffer structure + * (write semaphore must be held) * return value: * position of end of packet in output buffer on success, * -EAGAIN if write semaphore busy or buffer full @@ -500,7 +482,7 @@ int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len) int result; switch (bcs->proto2) { - case ISDN_PROTO_L2_HDLC: + case L2_HDLC: result = hdlc_buildframe(bcs->hw.bas->isooutbuf, in, len); gig_dbg(DEBUG_ISO, "%s: %d bytes HDLC -> %d", __func__, len, result); @@ -542,8 +524,9 @@ static inline void hdlc_flush(struct bc_state *bcs) if (likely(bcs->skb != NULL)) skb_trim(bcs->skb, 0); else if (!bcs->ignore) { - if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL) - skb_reserve(bcs->skb, HW_HDR_LEN); + bcs->skb = dev_alloc_skb(SBUFSIZE + bcs->cs->hw_hdr_len); + if (bcs->skb) + skb_reserve(bcs->skb, bcs->cs->hw_hdr_len); else dev_err(bcs->cs->dev, "could not allocate skb\n"); } @@ -557,43 +540,46 @@ static inline void hdlc_flush(struct bc_state *bcs) */ static inline void hdlc_done(struct bc_state *bcs) { + struct cardstate *cs = bcs->cs; struct sk_buff *procskb; + unsigned int len; if (unlikely(bcs->ignore)) { bcs->ignore--; hdlc_flush(bcs); return; } - - if ((procskb = bcs->skb) == NULL) { + procskb = bcs->skb; + if (procskb == NULL) { /* previous error */ gig_dbg(DEBUG_ISO, "%s: skb=NULL", __func__); - gigaset_rcv_error(NULL, bcs->cs, bcs); + gigaset_isdn_rcv_err(bcs); } else if (procskb->len < 2) { - dev_notice(bcs->cs->dev, "received short frame (%d octets)\n", + dev_notice(cs->dev, "received short frame (%d octets)\n", procskb->len); bcs->hw.bas->runts++; - gigaset_rcv_error(procskb, bcs->cs, bcs); + dev_kfree_skb_any(procskb); + gigaset_isdn_rcv_err(bcs); } else if (bcs->fcs != PPP_GOODFCS) { - dev_notice(bcs->cs->dev, "frame check error (0x%04x)\n", - bcs->fcs); + dev_notice(cs->dev, "frame check error (0x%04x)\n", bcs->fcs); bcs->hw.bas->fcserrs++; - gigaset_rcv_error(procskb, bcs->cs, bcs); + dev_kfree_skb_any(procskb); + gigaset_isdn_rcv_err(bcs); } else { - procskb->len -= 2; /* subtract FCS */ - procskb->tail -= 2; - gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)", - __func__, procskb->len); + len = procskb->len; + __skb_trim(procskb, len -= 2); /* subtract FCS */ + gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)", __func__, len); dump_bytes(DEBUG_STREAM_DUMP, - "rcv data", procskb->data, procskb->len); - bcs->hw.bas->goodbytes += procskb->len; - gigaset_rcv_skb(procskb, bcs->cs, bcs); + "rcv data", procskb->data, len); + bcs->hw.bas->goodbytes += len; + gigaset_skb_rcvd(bcs, procskb); } - if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL) - skb_reserve(bcs->skb, HW_HDR_LEN); + bcs->skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len); + if (bcs->skb) + skb_reserve(bcs->skb, cs->hw_hdr_len); else - dev_err(bcs->cs->dev, "could not allocate skb\n"); + dev_err(cs->dev, "could not allocate skb\n"); bcs->fcs = PPP_INITFCS; } @@ -610,12 +596,8 @@ static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits) dev_notice(bcs->cs->dev, "received partial byte (%d bits)\n", inbits); bcs->hw.bas->alignerrs++; - gigaset_rcv_error(bcs->skb, bcs->cs, bcs); - - if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL) - skb_reserve(bcs->skb, HW_HDR_LEN); - else - dev_err(bcs->cs->dev, "could not allocate skb\n"); + gigaset_isdn_rcv_err(bcs); + __skb_trim(bcs->skb, 0); bcs->fcs = PPP_INITFCS; } @@ -646,10 +628,10 @@ static const unsigned char bitcounts[256] = { }; /* hdlc_unpack - * perform HDLC frame processing (bit unstuffing, flag detection, FCS calculation) - * on a sequence of received data bytes (8 bits each, LSB first) - * pass on successfully received, complete frames as SKBs via gigaset_rcv_skb - * notify of errors via gigaset_rcv_error + * perform HDLC frame processing (bit unstuffing, flag detection, FCS + * calculation) on a sequence of received data bytes (8 bits each, LSB first) + * pass on successfully received, complete frames as SKBs via gigaset_skb_rcvd + * notify of errors via gigaset_isdn_rcv_err * tally frames, errors etc. in BC structure counters * parameters: * src received data @@ -665,9 +647,12 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, /* load previous state: * inputstate = set of flag bits: - * - INS_flag_hunt: no complete opening flag received since connection setup or last abort - * - INS_have_data: at least one complete data byte received since last flag - * seqlen = number of consecutive '1' bits in last 7 input stream bits (0..7) + * - INS_flag_hunt: no complete opening flag received since connection + * setup or last abort + * - INS_have_data: at least one complete data byte received since last + * flag + * seqlen = number of consecutive '1' bits in last 7 input stream bits + * (0..7) * inbyte = accumulated partial data byte (if !INS_flag_hunt) * inbits = number of valid bits in inbyte, starting at LSB (0..6) */ @@ -701,9 +686,11 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, inbyte = c >> (lead1 + 1); inbits = 7 - lead1; if (trail1 >= 8) { - /* interior stuffing: omitting the MSB handles most cases */ + /* interior stuffing: + * omitting the MSB handles most cases, + * correct the incorrectly handled + * cases individually */ inbits--; - /* correct the incorrectly handled cases individually */ switch (c) { case 0xbe: inbyte = 0x3f; @@ -729,13 +716,14 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, hdlc_flush(bcs); inputstate |= INS_flag_hunt; } else if (seqlen == 6) { - /* closing flag, including (6 - lead1) '1's and one '0' from inbits */ + /* closing flag, including (6 - lead1) '1's + * and one '0' from inbits */ if (inbits > 7 - lead1) { hdlc_frag(bcs, inbits + lead1 - 7); inputstate &= ~INS_have_data; } else { if (inbits < 7 - lead1) - ubc->stolen0s ++; + ubc->stolen0s++; if (inputstate & INS_have_data) { hdlc_done(bcs); inputstate &= ~INS_have_data; @@ -744,7 +732,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, if (c == PPP_FLAG) { /* complete flag, LSB overlaps preceding flag */ - ubc->shared0s ++; + ubc->shared0s++; inbits = 0; inbyte = 0; } else if (trail1 != 7) { @@ -752,9 +740,11 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, inbyte = c >> (lead1 + 1); inbits = 7 - lead1; if (trail1 >= 8) { - /* interior stuffing: omitting the MSB handles most cases */ + /* interior stuffing: + * omitting the MSB handles most cases, + * correct the incorrectly handled + * cases individually */ inbits--; - /* correct the incorrectly handled cases individually */ switch (c) { case 0xbe: inbyte = 0x3f; @@ -762,7 +752,8 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, } } } else { - /* abort sequence follows, skb already empty anyway */ + /* abort sequence follows, + * skb already empty anyway */ ubc->aborts++; inputstate |= INS_flag_hunt; } @@ -787,14 +778,17 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, } else { /* stuffed data */ if (trail1 < 7) { /* => seqlen == 5 */ - /* stuff bit at position lead1, no interior stuffing */ + /* stuff bit at position lead1, + * no interior stuffing */ unsigned char mask = (1 << lead1) - 1; c = (c & mask) | ((c & ~mask) >> 1); inbyte |= c << inbits; inbits += 7; } else if (seqlen < 5) { /* trail1 >= 8 */ - /* interior stuffing: omitting the MSB handles most cases */ - /* correct the incorrectly handled cases individually */ + /* interior stuffing: + * omitting the MSB handles most cases, + * correct the incorrectly handled + * cases individually */ switch (c) { case 0xbe: c = 0x7e; @@ -804,8 +798,9 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, inbits += 7; } else { /* seqlen == 5 && trail1 >= 8 */ - /* stuff bit at lead1 *and* interior stuffing */ - switch (c) { /* unstuff individually */ + /* stuff bit at lead1 *and* interior + * stuffing -- unstuff individually */ + switch (c) { case 0x7d: c = 0x3f; break; @@ -841,7 +836,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, } /* trans_receive - * pass on received USB frame transparently as SKB via gigaset_rcv_skb + * pass on received USB frame transparently as SKB via gigaset_skb_rcvd * invert bytes * tally frames, errors etc. in BC structure counters * parameters: @@ -852,6 +847,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, static inline void trans_receive(unsigned char *src, unsigned count, struct bc_state *bcs) { + struct cardstate *cs = bcs->cs; struct sk_buff *skb; int dobytes; unsigned char *dst; @@ -861,13 +857,14 @@ static inline void trans_receive(unsigned char *src, unsigned count, hdlc_flush(bcs); return; } - if (unlikely((skb = bcs->skb) == NULL)) { - bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN); + skb = bcs->skb; + if (unlikely(skb == NULL)) { + bcs->skb = skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len); if (!skb) { - dev_err(bcs->cs->dev, "could not allocate skb\n"); + dev_err(cs->dev, "could not allocate skb\n"); return; } - skb_reserve(skb, HW_HDR_LEN); + skb_reserve(skb, cs->hw_hdr_len); } bcs->hw.bas->goodbytes += skb->len; dobytes = TRANSBUFSIZE - skb->len; @@ -881,23 +878,24 @@ static inline void trans_receive(unsigned char *src, unsigned count, if (dobytes == 0) { dump_bytes(DEBUG_STREAM_DUMP, "rcv data", skb->data, skb->len); - gigaset_rcv_skb(skb, bcs->cs, bcs); - bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN); + gigaset_skb_rcvd(bcs, skb); + bcs->skb = skb = + dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len); if (!skb) { - dev_err(bcs->cs->dev, - "could not allocate skb\n"); + dev_err(cs->dev, "could not allocate skb\n"); return; } - skb_reserve(bcs->skb, HW_HDR_LEN); + skb_reserve(skb, cs->hw_hdr_len); dobytes = TRANSBUFSIZE; } } } -void gigaset_isoc_receive(unsigned char *src, unsigned count, struct bc_state *bcs) +void gigaset_isoc_receive(unsigned char *src, unsigned count, + struct bc_state *bcs) { switch (bcs->proto2) { - case ISDN_PROTO_L2_HDLC: + case L2_HDLC: hdlc_unpack(src, count, bcs); break; default: /* assume transparent */ @@ -981,8 +979,10 @@ void gigaset_isoc_input(struct inbuf_t *inbuf) * @bcs: B channel descriptor structure. * @skb: data to send. * - * Called by i4l.c to queue an skb for sending, and start transmission if + * Called by LL to queue an skb for sending, and start transmission if * necessary. + * Once the payload data has been transmitted completely, gigaset_skb_sent() + * will be called with the skb's link layer header preserved. * * Return value: * number of bytes accepted for sending (skb->len) if ok, diff --git a/drivers/isdn/gigaset/proc.c b/drivers/isdn/gigaset/proc.c index 9715aad9c3f0..758a00c1d2e2 100644 --- a/drivers/isdn/gigaset/proc.c +++ b/drivers/isdn/gigaset/proc.c @@ -39,7 +39,7 @@ static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr, return -EINVAL; if (mutex_lock_interruptible(&cs->mutex)) - return -ERESTARTSYS; // FIXME -EINTR? + return -ERESTARTSYS; cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE, diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 3071a52467ed..168d585d64d8 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -164,9 +164,15 @@ static void gigaset_modem_fill(unsigned long data) { struct cardstate *cs = (struct cardstate *) data; struct bc_state *bcs; + struct sk_buff *nextskb; int sent = 0; - if (!cs || !(bcs = cs->bcs)) { + if (!cs) { + gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__); + return; + } + bcs = cs->bcs; + if (!bcs) { gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__); return; } @@ -179,9 +185,11 @@ static void gigaset_modem_fill(unsigned long data) return; /* no command to send; get skb */ - if (!(bcs->tx_skb = skb_dequeue(&bcs->squeue))) + nextskb = skb_dequeue(&bcs->squeue); + if (!nextskb) /* no skb either, nothing to do */ return; + bcs->tx_skb = nextskb; gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)", (unsigned long) bcs->tx_skb); @@ -236,19 +244,20 @@ static void flush_send_queue(struct cardstate *cs) * number of bytes queued, or error code < 0 */ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, - int len, struct tasklet_struct *wake_tasklet) + int len, struct tasklet_struct *wake_tasklet) { struct cmdbuf_t *cb; unsigned long flags; gigaset_dbg_buffer(cs->mstate != MS_LOCKED ? - DEBUG_TRANSCMD : DEBUG_LOCKCMD, - "CMD Transmit", len, buf); + DEBUG_TRANSCMD : DEBUG_LOCKCMD, + "CMD Transmit", len, buf); if (len <= 0) return 0; - if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { + cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC); + if (!cb) { dev_err(cs->dev, "%s: out of memory!\n", __func__); return -ENOMEM; } @@ -392,7 +401,6 @@ static void gigaset_device_release(struct device *dev) struct platform_device *pdev = to_platform_device(dev); /* adapted from platform_device_release() in drivers/base/platform.c */ - //FIXME is this actually necessary? kfree(dev->platform_data); kfree(pdev->resource); } @@ -404,16 +412,20 @@ static void gigaset_device_release(struct device *dev) static int gigaset_initcshw(struct cardstate *cs) { int rc; + struct ser_cardstate *scs; - if (!(cs->hw.ser = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL))) { + scs = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL); + if (!scs) { pr_err("out of memory\n"); return 0; } + cs->hw.ser = scs; cs->hw.ser->dev.name = GIGASET_MODULENAME; cs->hw.ser->dev.id = cs->minor_index; cs->hw.ser->dev.dev.release = gigaset_device_release; - if ((rc = platform_device_register(&cs->hw.ser->dev)) != 0) { + rc = platform_device_register(&cs->hw.ser->dev); + if (rc != 0) { pr_err("error %d registering platform device\n", rc); kfree(cs->hw.ser); cs->hw.ser = NULL; @@ -422,7 +434,7 @@ static int gigaset_initcshw(struct cardstate *cs) dev_set_drvdata(&cs->hw.ser->dev.dev, cs); tasklet_init(&cs->write_tasklet, - &gigaset_modem_fill, (unsigned long) cs); + gigaset_modem_fill, (unsigned long) cs); return 1; } @@ -434,7 +446,8 @@ static int gigaset_initcshw(struct cardstate *cs) * Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c * and by "if_lock" and "if_termios" in interface.c */ -static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, unsigned new_state) +static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, + unsigned new_state) { struct tty_struct *tty = cs->hw.ser->tty; unsigned int set, clear; @@ -520,8 +533,8 @@ gigaset_tty_open(struct tty_struct *tty) } /* allocate memory for our device state and intialize it */ - if (!(cs = gigaset_initcs(driver, 1, 1, 0, cidmode, - GIGASET_MODULENAME))) + cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME); + if (!cs) goto error; cs->dev = &cs->hw.ser->dev.dev; @@ -690,7 +703,8 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, if (!cs) return; - if (!(inbuf = cs->inbuf)) { + inbuf = cs->inbuf; + if (!inbuf) { dev_err(cs->dev, "%s: no inbuf\n", __func__); cs_put(cs); return; @@ -770,18 +784,21 @@ static int __init ser_gigaset_init(void) int rc; gig_dbg(DEBUG_INIT, "%s", __func__); - if ((rc = platform_driver_register(&device_driver)) != 0) { + rc = platform_driver_register(&device_driver); + if (rc != 0) { pr_err("error %d registering platform driver\n", rc); return rc; } /* allocate memory for our driver state and intialize it */ - if (!(driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, + driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, GIGASET_MODULENAME, GIGASET_DEVNAME, - &ops, THIS_MODULE))) + &ops, THIS_MODULE); + if (!driver) goto error; - if ((rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc)) != 0) { + rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc); + if (rc != 0) { pr_err("error %d registering line discipline\n", rc); goto error; } @@ -808,7 +825,8 @@ static void __exit ser_gigaset_exit(void) driver = NULL; } - if ((rc = tty_unregister_ldisc(N_GIGASET_M101)) != 0) + rc = tty_unregister_ldisc(N_GIGASET_M101); + if (rc != 0) pr_err("error %d unregistering line discipline\n", rc); platform_driver_unregister(&device_driver); diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c index 4deb1ab0dbf8..3ab1daeb276b 100644 --- a/drivers/isdn/gigaset/usb-gigaset.c +++ b/drivers/isdn/gigaset/usb-gigaset.c @@ -43,14 +43,14 @@ MODULE_PARM_DESC(cidmode, "Call-ID mode"); #define GIGASET_MODULENAME "usb_gigaset" #define GIGASET_DEVNAME "ttyGU" -#define IF_WRITEBUF 2000 //FIXME // WAKEUP_CHARS: 256 +#define IF_WRITEBUF 2000 /* arbitrary limit */ /* Values for the Gigaset M105 Data */ #define USB_M105_VENDOR_ID 0x0681 #define USB_M105_PRODUCT_ID 0x0009 /* table of devices that work with this driver */ -static const struct usb_device_id gigaset_table [] = { +static const struct usb_device_id gigaset_table[] = { { USB_DEVICE(USB_M105_VENDOR_ID, USB_M105_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -97,8 +97,8 @@ MODULE_DEVICE_TABLE(usb, gigaset_table); * 41 19 -- -- -- -- 06 00 00 00 00 xx 11 13 * Used after every "configuration sequence" (RQ 12, RQs 01/03/13). * xx is usually 0x00 but was 0x7e before starting data transfer - * in unimodem mode. So, this might be an array of characters that need - * special treatment ("commit all bufferd data"?), 11=^Q, 13=^S. + * in unimodem mode. So, this might be an array of characters that + * need special treatment ("commit all bufferd data"?), 11=^Q, 13=^S. * * Unimodem mode: use "modprobe ppp_async flag_time=0" as the device _needs_ two * flags per packet. @@ -114,7 +114,7 @@ static int gigaset_suspend(struct usb_interface *intf, pm_message_t message); static int gigaset_resume(struct usb_interface *intf); static int gigaset_pre_reset(struct usb_interface *intf); -static struct gigaset_driver *driver = NULL; +static struct gigaset_driver *driver; /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver gigaset_usb_driver = { @@ -141,6 +141,7 @@ struct usb_cardstate { struct urb *bulk_out_urb; /* Input buffer */ + unsigned char *rcvbuf; int rcvbuf_size; struct urb *read_urb; __u8 int_in_endpointAddr; @@ -164,13 +165,11 @@ static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, val = tiocm_to_gigaset(new_state); gig_dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask); - // don't use this in an interrupt/BH r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 7, 0x41, (val & 0xff) | ((mask & 0xff) << 8), 0, NULL, 0, 2000 /* timeout? */); if (r < 0) return r; - //.. return 0; } @@ -220,7 +219,6 @@ static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag) cflag &= CBAUD; switch (cflag) { - //FIXME more values? case B300: rate = 300; break; case B600: rate = 600; break; case B1200: rate = 1200; break; @@ -273,7 +271,7 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag) /* set the number of stop bits */ if (cflag & CSTOPB) { if ((cflag & CSIZE) == CS5) - val |= 1; /* 1.5 stop bits */ //FIXME is this okay? + val |= 1; /* 1.5 stop bits */ else val |= 2; /* 2 stop bits */ } @@ -282,7 +280,7 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag) } - /*================================================================================================================*/ +/*============================================================================*/ static int gigaset_init_bchannel(struct bc_state *bcs) { /* nothing to do for M10x */ @@ -344,7 +342,6 @@ static void gigaset_modem_fill(unsigned long data) if (write_modem(cs) < 0) { gig_dbg(DEBUG_OUTPUT, "modem_fill: write_modem failed"); - // FIXME should we tell the LL? again = 1; /* no callback will be called! */ } } @@ -356,8 +353,8 @@ static void gigaset_modem_fill(unsigned long data) */ static void gigaset_read_int_callback(struct urb *urb) { - struct inbuf_t *inbuf = urb->context; - struct cardstate *cs = inbuf->cs; + struct cardstate *cs = urb->context; + struct inbuf_t *inbuf = cs->inbuf; int status = urb->status; int r; unsigned numbytes; @@ -368,7 +365,7 @@ static void gigaset_read_int_callback(struct urb *urb) numbytes = urb->actual_length; if (numbytes) { - src = inbuf->rcvbuf; + src = cs->hw.usb->rcvbuf; if (unlikely(*src)) dev_warn(cs->dev, "%s: There was no leading 0, but 0x%02x!\n", @@ -440,7 +437,7 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb) struct cmdbuf_t *tcb; unsigned long flags; int count; - int status = -ENOENT; // FIXME + int status = -ENOENT; struct usb_cardstate *ucs = cs->hw.usb; do { @@ -480,7 +477,9 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb) ucs->busy = 1; spin_lock_irqsave(&cs->lock, flags); - status = cs->connected ? usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) : -ENODEV; + status = cs->connected ? + usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) : + -ENODEV; spin_unlock_irqrestore(&cs->lock, flags); if (status) { @@ -510,8 +509,8 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, if (len <= 0) return 0; - - if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { + cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC); + if (!cb) { dev_err(cs->dev, "%s: out of memory\n", __func__); return -ENOMEM; } @@ -615,7 +614,7 @@ static int gigaset_initcshw(struct cardstate *cs) ucs->bulk_out_urb = NULL; ucs->read_urb = NULL; tasklet_init(&cs->write_tasklet, - &gigaset_modem_fill, (unsigned long) cs); + gigaset_modem_fill, (unsigned long) cs); return 1; } @@ -637,9 +636,7 @@ static int write_modem(struct cardstate *cs) return -EINVAL; } - /* Copy data to bulk out buffer and // FIXME copying not necessary - * transmit data - */ + /* Copy data to bulk out buffer and transmit data */ count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size); skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count); skb_pull(bcs->tx_skb, count); @@ -650,7 +647,8 @@ static int write_modem(struct cardstate *cs) if (cs->connected) { usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev, usb_sndbulkpipe(ucs->udev, - ucs->bulk_out_endpointAddr & 0x0f), + ucs->bulk_out_endpointAddr & + 0x0f), ucs->bulk_out_buffer, count, gigaset_write_bulk_callback, cs); ret = usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC); @@ -666,7 +664,7 @@ static int write_modem(struct cardstate *cs) if (!bcs->tx_skb->len) { /* skb sent completely */ - gigaset_skb_sent(bcs, bcs->tx_skb); //FIXME also, when ret<0? + gigaset_skb_sent(bcs, bcs->tx_skb); gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!", (unsigned long) bcs->tx_skb); @@ -763,8 +761,8 @@ static int gigaset_probe(struct usb_interface *interface, buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); ucs->rcvbuf_size = buffer_size; ucs->int_in_endpointAddr = endpoint->bEndpointAddress; - cs->inbuf[0].rcvbuf = kmalloc(buffer_size, GFP_KERNEL); - if (!cs->inbuf[0].rcvbuf) { + ucs->rcvbuf = kmalloc(buffer_size, GFP_KERNEL); + if (!ucs->rcvbuf) { dev_err(cs->dev, "Couldn't allocate rcvbuf\n"); retval = -ENOMEM; goto error; @@ -773,9 +771,9 @@ static int gigaset_probe(struct usb_interface *interface, usb_fill_int_urb(ucs->read_urb, udev, usb_rcvintpipe(udev, endpoint->bEndpointAddress & 0x0f), - cs->inbuf[0].rcvbuf, buffer_size, + ucs->rcvbuf, buffer_size, gigaset_read_int_callback, - cs->inbuf + 0, endpoint->bInterval); + cs, endpoint->bInterval); retval = usb_submit_urb(ucs->read_urb, GFP_KERNEL); if (retval) { @@ -789,7 +787,7 @@ static int gigaset_probe(struct usb_interface *interface, if (!gigaset_start(cs)) { tasklet_kill(&cs->write_tasklet); - retval = -ENODEV; //FIXME + retval = -ENODEV; goto error; } return 0; @@ -798,11 +796,11 @@ error: usb_kill_urb(ucs->read_urb); kfree(ucs->bulk_out_buffer); usb_free_urb(ucs->bulk_out_urb); - kfree(cs->inbuf[0].rcvbuf); + kfree(ucs->rcvbuf); usb_free_urb(ucs->read_urb); usb_set_intfdata(interface, NULL); ucs->read_urb = ucs->bulk_out_urb = NULL; - cs->inbuf[0].rcvbuf = ucs->bulk_out_buffer = NULL; + ucs->rcvbuf = ucs->bulk_out_buffer = NULL; usb_put_dev(ucs->udev); ucs->udev = NULL; ucs->interface = NULL; @@ -831,10 +829,10 @@ static void gigaset_disconnect(struct usb_interface *interface) kfree(ucs->bulk_out_buffer); usb_free_urb(ucs->bulk_out_urb); - kfree(cs->inbuf[0].rcvbuf); + kfree(ucs->rcvbuf); usb_free_urb(ucs->read_urb); ucs->read_urb = ucs->bulk_out_urb = NULL; - cs->inbuf[0].rcvbuf = ucs->bulk_out_buffer = NULL; + ucs->rcvbuf = ucs->bulk_out_buffer = NULL; usb_put_dev(ucs->udev); ucs->interface = NULL; @@ -916,9 +914,10 @@ static int __init usb_gigaset_init(void) int result; /* allocate memory for our driver state and intialize it */ - if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, - GIGASET_MODULENAME, GIGASET_DEVNAME, - &ops, THIS_MODULE)) == NULL) + driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, + GIGASET_MODULENAME, GIGASET_DEVNAME, + &ops, THIS_MODULE); + if (driver == NULL) goto error; /* register this driver with the USB subsystem */ diff --git a/drivers/isdn/hardware/eicon/di.c b/drivers/isdn/hardware/eicon/di.c index b029d130eb21..cb14ae3e7154 100644 --- a/drivers/isdn/hardware/eicon/di.c +++ b/drivers/isdn/hardware/eicon/di.c @@ -806,7 +806,7 @@ static void xdi_xlog_request (byte Adapter, byte Id, DELIVERY - indication entered isdn_rc function RNR=... - application had returned RNR=... after the look ahead callback - RNum=0 - aplication had not returned any buffer to copy + RNum=0 - application had not returned any buffer to copy this indication and will copy it self COMPLETE - XDI had copied the data to the buffers provided bu the application and is about to issue the diff --git a/drivers/isdn/hardware/eicon/maintidi.c b/drivers/isdn/hardware/eicon/maintidi.c index 41c26e756452..534978bdf382 100644 --- a/drivers/isdn/hardware/eicon/maintidi.c +++ b/drivers/isdn/hardware/eicon/maintidi.c @@ -385,7 +385,7 @@ static int SuperTraceMessageInput (void* hLib) { } break; default: - diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind (DMA mode): %02x", Ind); + diva_mnt_internal_dprintf (0, DLI_ERR, "Unknown IDI Ind (DMA mode): %02x", Ind); } p += (this_ind_length+1); total_length -= (4 + this_ind_length); @@ -420,7 +420,7 @@ static int SuperTraceMessageInput (void* hLib) { } break; default: - diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind: %02x", Ind); + diva_mnt_internal_dprintf (0, DLI_ERR, "Unknown IDI Ind: %02x", Ind); } } } diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c index fc46a26cb14f..a64bb6c67ba7 100644 --- a/drivers/isdn/hardware/mISDN/hfcsusb.c +++ b/drivers/isdn/hardware/mISDN/hfcsusb.c @@ -721,7 +721,7 @@ hfcsusb_setup_bch(struct bchannel *bch, int protocol) switch (protocol) { case (-1): /* used for init */ bch->state = -1; - /* fall trough */ + /* fall through */ case (ISDN_P_NONE): if (bch->state == ISDN_P_NONE) return 0; /* already in idle state */ diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h index 43efe7358fa3..369196adae03 100644 --- a/drivers/isdn/hardware/mISDN/hfcsusb.h +++ b/drivers/isdn/hardware/mISDN/hfcsusb.h @@ -150,7 +150,7 @@ symbolic(struct hfcusb_symbolic_list list[], const int num) for (i = 0; list[i].name != NULL; i++) if (list[i].num == num) return list[i].name; - return "<unkown USB Error>"; + return "<unknown USB Error>"; } /* USB descriptor need to contain one of the following EndPoint combination: */ diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c index de352a17673a..09095c747110 100644 --- a/drivers/isdn/hardware/mISDN/mISDNisar.c +++ b/drivers/isdn/hardware/mISDN/mISDNisar.c @@ -860,7 +860,7 @@ isar_pump_statev_modem(struct isar_ch *ch, u8 devt) { pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name); break; default: - pr_info("u%s: nknown pump stev %x\n", ch->is->name, devt); + pr_info("u%s: unknown pump stev %x\n", ch->is->name, devt); break; } } diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c index ff3a4e290da3..7726afdbb40b 100644 --- a/drivers/isdn/hardware/mISDN/speedfax.c +++ b/drivers/isdn/hardware/mISDN/speedfax.c @@ -110,6 +110,7 @@ set_debug(const char *val, struct kernel_param *kp) MODULE_AUTHOR("Karsten Keil"); MODULE_LICENSE("GPL v2"); MODULE_VERSION(SPEEDFAX_REV); +MODULE_FIRMWARE("isdn/ISAR.BIN"); module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Speedfax debug mask"); module_param(irqloops, uint, S_IRUGO | S_IWUSR); diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c index a420b64472e3..aaaeaafd86f4 100644 --- a/drivers/isdn/hisax/hfc_usb.c +++ b/drivers/isdn/hisax/hfc_usb.c @@ -1086,7 +1086,7 @@ hfc_usb_l2l1(struct hisax_if *my_hisax_if, int pr, void *arg) break; default: DBG(HFCUSB_DBG_STATES, - "HFC_USB: hfc_usb_d_l2l1: unkown state : %#x", pr); + "HFC_USB: hfc_usb_d_l2l1: unknown state : %#x", pr); break; } } diff --git a/drivers/isdn/hysdn/Kconfig b/drivers/isdn/hysdn/Kconfig index c9e4231968ef..e86bc6583d71 100644 --- a/drivers/isdn/hysdn/Kconfig +++ b/drivers/isdn/hysdn/Kconfig @@ -1,6 +1,3 @@ -# -# Config.in for HYSDN ISDN driver -# config HYSDN tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)" depends on m && PROC_FS && PCI @@ -15,4 +12,3 @@ config HYSDN_CAPI depends on HYSDN && ISDN_CAPI help Say Y here if you like to use Hypercope's CAPI 2.0 interface. - diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 90b56ed8651f..507e13d9a57c 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -1563,7 +1563,7 @@ isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data) *(__be32 *)(p + 4) = cpu_to_be32(CISCO_SLARP_KEEPALIVE); *(__be32 *)(p + 8) = cpu_to_be32(lp->cisco_myseq); *(__be32 *)(p + 12) = cpu_to_be32(lp->cisco_yourseq); - *(__be16 *)(p + 16) = cpu_to_be16(0xffff); // reliablity, always 0xffff + *(__be16 *)(p + 16) = cpu_to_be16(0xffff); // reliability, always 0xffff p += 18; isdn_net_write_super(lp, skb); diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 642d5aaf53ce..45df6675e8ed 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -836,7 +836,7 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) unsigned short hl; struct sk_buff *skb; /* - * we need to reserve enought space in front of + * we need to reserve enough space in front of * sk_buff. old call to dev_alloc_skb only reserved * 16 bytes, now we are looking what the driver want */ @@ -1326,7 +1326,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev) struct sk_buff *new_skb; unsigned short hl; /* - * we need to reserve enought space in front of + * we need to reserve enough space in front of * sk_buff. old call to dev_alloc_skb only reserved * 16 bytes, now we are looking what the driver want. */ @@ -1674,7 +1674,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * - insert new fragment into the proper sequence slot (once that's done * newfrag will be set to NULL) * - reassemble any complete fragment sequence (non-null 'start' - * indicates there is a continguous sequence present) + * indicates there is a contiguous sequence present) * - discard any incomplete sequences that are below minseq -- due * to the fact that sender always increment sequence number, if there * is an incomplete sequence below minseq, no new fragments would diff --git a/drivers/isdn/i4l/isdn_ttyfax.c b/drivers/isdn/i4l/isdn_ttyfax.c index 78f7660c1d0e..4c41f191d4e2 100644 --- a/drivers/isdn/i4l/isdn_ttyfax.c +++ b/drivers/isdn/i4l/isdn_ttyfax.c @@ -470,7 +470,7 @@ isdn_tty_cmd_FCLASS2(char **p, modem_info * info) } return 0; } - /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */ + /* BADMUL=value - dummy 0=disable errorchk disabled (threshold multiplier) */ if (!strncmp(p[0], "BADMUL", 6)) { p[0] += 6; switch (*p[0]) { diff --git a/drivers/isdn/icn/Kconfig b/drivers/isdn/icn/Kconfig index 89d15eed765e..4534f21a1852 100644 --- a/drivers/isdn/icn/Kconfig +++ b/drivers/isdn/icn/Kconfig @@ -1,6 +1,3 @@ -# -# Config.in for ICN ISDN driver -# config ISDN_DRV_ICN tristate "ICN 2B and 4B support" depends on ISA @@ -13,4 +10,3 @@ config ISDN_DRV_ICN separately. See <file:Documentation/isdn/README> and <file:Documentation/isdn/README.icn> for more information. - diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 77ee2867c8b4..43ff4d3b046e 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -110,7 +110,7 @@ * crossconnections and conferences via software if not possible through * hardware. If hardware capability is available, hardware is used. * - * Echo: Is generated by CMX and is used to check performane of hard and + * Echo: Is generated by CMX and is used to check performance of hard and * software CMX. * * The CMX has special functions for conferences with one, two and more diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index feb0fa45b664..fcfe17a19a61 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -779,7 +779,7 @@ base_sock_create(struct net *net, struct socket *sock, int protocol) } static int -mISDN_sock_create(struct net *net, struct socket *sock, int proto) +mISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern) { int err = -EPROTONOSUPPORT; @@ -808,8 +808,7 @@ mISDN_sock_create(struct net *net, struct socket *sock, int proto) return err; } -static struct -net_proto_family mISDN_sock_family_ops = { +static const struct net_proto_family mISDN_sock_family_ops = { .owner = THIS_MODULE, .family = PF_ISDN, .create = mISDN_sock_create, diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c index e04bad6c5baf..6d4da6095885 100644 --- a/drivers/isdn/mISDN/tei.c +++ b/drivers/isdn/mISDN/tei.c @@ -725,7 +725,7 @@ tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg) if (tm->rcnt == 1) { if (*debug & DEBUG_L2_TEI) tm->tei_m.printdebug(fi, - "check req for tei %d sucessful\n", tm->l2->tei); + "check req for tei %d successful\n", tm->l2->tei); mISDN_FsmChangeState(fi, ST_TEI_NOP); } else if (tm->rcnt > 1) { /* duplicate assignment; remove */ diff --git a/drivers/isdn/pcbit/Kconfig b/drivers/isdn/pcbit/Kconfig index ffba6eca1244..e9b2dd85d410 100644 --- a/drivers/isdn/pcbit/Kconfig +++ b/drivers/isdn/pcbit/Kconfig @@ -1,6 +1,3 @@ -# -# Config.in for PCBIT ISDN driver -# config ISDN_DRV_PCBIT tristate "PCBIT-D support" depends on ISA && (BROKEN || X86) @@ -11,4 +8,3 @@ config ISDN_DRV_PCBIT the card using a utility which is distributed separately. See <file:Documentation/isdn/README> and <file:Documentation/isdn/README.pcbit> for more information. - diff --git a/drivers/isdn/sc/Kconfig b/drivers/isdn/sc/Kconfig index e6510ca7bf43..7469863a7925 100644 --- a/drivers/isdn/sc/Kconfig +++ b/drivers/isdn/sc/Kconfig @@ -1,6 +1,3 @@ -# -# Config.in for Spellcaster ISDN driver -# config ISDN_DRV_SC tristate "Spellcaster support" depends on ISA @@ -9,4 +6,3 @@ config ISDN_DRV_SC driver currently builds only in a modularized version. To build it, choose M here: the module will be called sc. See <file:Documentation/isdn/README.sc> for more information. - diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c index 446050759b4d..b9826032450b 100644 --- a/drivers/leds/leds-ams-delta.c +++ b/drivers/leds/leds-ams-delta.c @@ -12,7 +12,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> -#include <mach/board-ams-delta.h> +#include <plat/board-ams-delta.h> /* * Our context diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c index 5d91362e3066..1f7c10f6b7f2 100644 --- a/drivers/leds/leds-locomo.c +++ b/drivers/leds/leds-locomo.c @@ -44,7 +44,7 @@ static void locomoled_brightness_set1(struct led_classdev *led_cdev, static struct led_classdev locomo_led0 = { .name = "locomo:amber:charge", - .default_trigger = "sharpsl-charge", + .default_trigger = "main-battery-charging", .brightness_set = locomoled_brightness_set0, }; diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c index 6a8221893256..a3d25da2f275 100644 --- a/drivers/macintosh/ans-lcd.c +++ b/drivers/macintosh/ans-lcd.c @@ -3,7 +3,6 @@ */ #include <linux/types.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/miscdevice.h> @@ -26,6 +25,7 @@ static unsigned long anslcd_short_delay = 80; static unsigned long anslcd_long_delay = 3280; static volatile unsigned char __iomem *anslcd_ptr; +static DEFINE_MUTEX(anslcd_mutex); #undef DEBUG @@ -65,26 +65,31 @@ anslcd_write( struct file * file, const char __user * buf, if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; + + mutex_lock(&anslcd_mutex); for ( i = *ppos; count > 0; ++i, ++p, --count ) { char c; __get_user(c, p); anslcd_write_byte_data( c ); } + mutex_unlock(&anslcd_mutex); *ppos = i; return p - buf; } -static int -anslcd_ioctl( struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg ) +static long +anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { char ch, __user *temp; + long ret = 0; #ifdef DEBUG printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg); #endif + mutex_lock(&anslcd_mutex); + switch ( cmd ) { case ANSLCD_CLEAR: @@ -93,7 +98,7 @@ anslcd_ioctl( struct inode * inode, struct file * file, anslcd_write_byte_ctrl ( 0x06 ); anslcd_write_byte_ctrl ( 0x01 ); anslcd_write_byte_ctrl ( 0x02 ); - return 0; + break; case ANSLCD_SENDCTRL: temp = (char __user *) arg; __get_user(ch, temp); @@ -101,33 +106,37 @@ anslcd_ioctl( struct inode * inode, struct file * file, anslcd_write_byte_ctrl ( ch ); __get_user(ch, temp); } - return 0; + break; case ANSLCD_SETSHORTDELAY: if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - anslcd_short_delay=arg; - return 0; + ret =-EACCES; + else + anslcd_short_delay=arg; + break; case ANSLCD_SETLONGDELAY: if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - anslcd_long_delay=arg; - return 0; + ret = -EACCES; + else + anslcd_long_delay=arg; + break; default: - return -EINVAL; + ret = -EINVAL; } + + mutex_unlock(&anslcd_mutex); + return ret; } static int anslcd_open( struct inode * inode, struct file * file ) { - cycle_kernel_lock(); return 0; } const struct file_operations anslcd_fops = { - .write = anslcd_write, - .ioctl = anslcd_ioctl, - .open = anslcd_open, + .write = anslcd_write, + .unlocked_ioctl = anslcd_ioctl, + .open = anslcd_open, }; static struct miscdevice anslcd_dev = { @@ -168,6 +177,7 @@ anslcd_init(void) printk(KERN_DEBUG "LCD: init\n"); #endif + mutex_lock(&anslcd_mutex); anslcd_write_byte_ctrl ( 0x38 ); anslcd_write_byte_ctrl ( 0x0c ); anslcd_write_byte_ctrl ( 0x06 ); @@ -176,6 +186,7 @@ anslcd_init(void) for(a=0;a<80;a++) { anslcd_write_byte_data(anslcd_logo[a]); } + mutex_unlock(&anslcd_mutex); return 0; } diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c index cc9f27514aef..7b4ef5bb556b 100644 --- a/drivers/macintosh/mac_hid.c +++ b/drivers/macintosh/mac_hid.c @@ -27,54 +27,49 @@ static int mouse_last_keycode; /* file(s) in /proc/sys/dev/mac_hid */ static ctl_table mac_hid_files[] = { { - .ctl_name = DEV_MAC_HID_MOUSE_BUTTON_EMULATION, .procname = "mouse_button_emulation", .data = &mouse_emulate_buttons, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, { - .ctl_name = DEV_MAC_HID_MOUSE_BUTTON2_KEYCODE, .procname = "mouse_button2_keycode", .data = &mouse_button2_keycode, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, { - .ctl_name = DEV_MAC_HID_MOUSE_BUTTON3_KEYCODE, .procname = "mouse_button3_keycode", .data = &mouse_button3_keycode, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, - { .ctl_name = 0 } + { } }; /* dir in /proc/sys/dev */ static ctl_table mac_hid_dir[] = { { - .ctl_name = DEV_MAC_HID, .procname = "mac_hid", .maxlen = 0, .mode = 0555, .child = mac_hid_files, }, - { .ctl_name = 0 } + { } }; /* /proc/sys/dev itself, in case that is not there yet */ static ctl_table mac_hid_root_dir[] = { { - .ctl_name = CTL_DEV, .procname = "dev", .maxlen = 0, .mode = 0555, .child = mac_hid_dir, }, - { .ctl_name = 0 } + { } }; static struct ctl_table_header *mac_hid_sysctl_header; diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 8b9364434aa0..3fbe41b0ac07 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -15,7 +15,7 @@ * * WARNING: This driver has only been testen on Apple's * 1.25 MHz Dual G4 (March 03). It is tuned for a CPU - * temperatur around 57 C. + * temperature around 57 C. * * Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se) * diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index ed1038164019..e412980763bd 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -988,7 +988,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad_cipher; } - /* Compatiblity mode for old dm-crypt cipher strings */ + /* Compatibility mode for old dm-crypt cipher strings */ if (!chainmode || (strcmp(chainmode, "plain") == 0 && !ivmode)) { chainmode = "cbc"; ivmode = "plain"; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 32d0b878eccc..dce971dbdfa3 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1116,8 +1116,9 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath) return limit_reached; } -static void pg_init_done(struct dm_path *path, int errors) +static void pg_init_done(void *data, int errors) { + struct dm_path *path = data; struct pgpath *pgpath = path_to_pgpath(path); struct priority_group *pg = pgpath->pg; struct multipath *m = pg->m; @@ -1183,12 +1184,11 @@ static void pg_init_done(struct dm_path *path, int errors) static void activate_path(struct work_struct *work) { - int ret; struct pgpath *pgpath = container_of(work, struct pgpath, activate_path); - ret = scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev)); - pg_init_done(&pgpath->path, ret); + scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev), + pg_init_done, &pgpath->path); } /* diff --git a/drivers/md/md.c b/drivers/md/md.c index b182f86a19dd..5f154ef1e4be 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -98,44 +98,40 @@ static struct ctl_table_header *raid_table_header; static ctl_table raid_table[] = { { - .ctl_name = DEV_RAID_SPEED_LIMIT_MIN, .procname = "speed_limit_min", .data = &sysctl_speed_limit_min, .maxlen = sizeof(int), .mode = S_IRUGO|S_IWUSR, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, { - .ctl_name = DEV_RAID_SPEED_LIMIT_MAX, .procname = "speed_limit_max", .data = &sysctl_speed_limit_max, .maxlen = sizeof(int), .mode = S_IRUGO|S_IWUSR, - .proc_handler = &proc_dointvec, + .proc_handler = proc_dointvec, }, - { .ctl_name = 0 } + { } }; static ctl_table raid_dir_table[] = { { - .ctl_name = DEV_RAID, .procname = "raid", .maxlen = 0, .mode = S_IRUGO|S_IXUGO, .child = raid_table, }, - { .ctl_name = 0 } + { } }; static ctl_table raid_root_table[] = { { - .ctl_name = CTL_DEV, .procname = "dev", .maxlen = 0, .mode = 0555, .child = raid_dir_table, }, - { .ctl_name = 0 } + { } }; static const struct block_device_operations md_fops; diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 351b98b9b302..169b337b7c9d 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,6 +1,6 @@ saa7146-objs := saa7146_i2c.o saa7146_core.o saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o -ir-common-objs := ir-functions.o ir-keymaps.o +ir-common-objs := ir-functions.o ir-keymaps.o ir-keytable.o obj-y += tuners/ obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index abd4791acb0e..e616f624ceaa 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -34,22 +34,19 @@ static int repeat = 1; module_param(repeat, int, 0444); MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)"); -static int debug; /* debug level (0,1,2) */ -module_param(debug, int, 0644); - -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG fmt , ## arg) +int media_ir_debug; /* media_ir_debug level (0,1,2) */ +module_param_named(debug, media_ir_debug, int, 0644); /* -------------------------------------------------------------------------- */ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) { if (KEY_RESERVED == ir->keycode) { - printk(KERN_INFO "%s: unknown key: key=0x%02x raw=0x%02x down=%d\n", - dev->name,ir->ir_key,ir->ir_raw,ir->keypressed); + printk(KERN_INFO "%s: unknown key: key=0x%02x down=%d\n", + dev->name, ir->ir_key, ir->keypressed); return; } - dprintk(1,"%s: key event code=%d down=%d\n", + IR_dprintk(1,"%s: key event code=%d down=%d\n", dev->name,ir->keycode,ir->keypressed); input_report_key(dev,ir->keycode,ir->keypressed); input_sync(dev); @@ -57,39 +54,34 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) /* -------------------------------------------------------------------------- */ -void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, +int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, int ir_type, struct ir_scancode_table *ir_codes) { - int i; - ir->ir_type = ir_type; - memset(ir->ir_codes, 0, sizeof(ir->ir_codes)); + ir->keytable.size = ir_roundup_tablesize(ir_codes->size); + ir->keytable.scan = kzalloc(ir->keytable.size * + sizeof(struct ir_scancode), GFP_KERNEL); + if (!ir->keytable.scan) + return -ENOMEM; - /* - * FIXME: This is a temporary workaround to use the new IR tables - * with the old approach. Later patches will replace this to a - * proper method - */ + IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n", + ir->keytable.size, + ir->keytable.size * sizeof(ir->keytable.scan)); - if (ir_codes) - for (i = 0; i < ir_codes->size; i++) - if (ir_codes->scan[i].scancode < IR_KEYTAB_SIZE) - ir->ir_codes[ir_codes->scan[i].scancode] = ir_codes->scan[i].keycode; + ir_copy_table(&ir->keytable, ir_codes); + ir_set_keycode_table(dev, &ir->keytable); - dev->keycode = ir->ir_codes; - dev->keycodesize = sizeof(IR_KEYTAB_TYPE); - dev->keycodemax = IR_KEYTAB_SIZE; - for (i = 0; i < IR_KEYTAB_SIZE; i++) - set_bit(ir->ir_codes[i], dev->keybit); clear_bit(0, dev->keybit); - set_bit(EV_KEY, dev->evbit); if (repeat) set_bit(EV_REP, dev->evbit); + + return 0; } EXPORT_SYMBOL_GPL(ir_input_init); + void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) { if (ir->keypressed) { @@ -100,9 +92,9 @@ void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) EXPORT_SYMBOL_GPL(ir_input_nokey); void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, - u32 ir_key, u32 ir_raw) + u32 ir_key) { - u32 keycode = IR_KEYCODE(ir->ir_codes, ir_key); + u32 keycode = ir_g_keycode_from_table(dev, ir_key); if (ir->keypressed && ir->keycode != keycode) { ir->keypressed = 0; @@ -110,7 +102,6 @@ void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, } if (!ir->keypressed) { ir->ir_key = ir_key; - ir->ir_raw = ir_raw; ir->keycode = keycode; ir->keypressed = 1; ir_input_key_event(dev,ir); @@ -275,7 +266,7 @@ EXPORT_SYMBOL_GPL(ir_decode_biphase); * saa7134 */ /* decode raw bit pattern to RC5 code */ -static u32 ir_rc5_decode(unsigned int code) +u32 ir_rc5_decode(unsigned int code) { unsigned int org_code = code; unsigned int pair; @@ -295,15 +286,16 @@ static u32 ir_rc5_decode(unsigned int code) rc5 |= 1; break; case 3: - dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code); + IR_dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code); return 0; } } - dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " + IR_dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " "instr=%x\n", rc5, org_code, RC5_START(rc5), RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); return rc5; } +EXPORT_SYMBOL_GPL(ir_rc5_decode); void ir_rc5_timer_end(unsigned long data) { @@ -330,20 +322,20 @@ void ir_rc5_timer_end(unsigned long data) /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */ if (gap < 28000) { - dprintk(1, "ir-common: spurious timer_end\n"); + IR_dprintk(1, "ir-common: spurious timer_end\n"); return; } if (ir->last_bit < 20) { /* ignore spurious codes (caused by light/other remotes) */ - dprintk(1, "ir-common: short code: %x\n", ir->code); + IR_dprintk(1, "ir-common: short code: %x\n", ir->code); } else { ir->code = (ir->code << ir->shift_by) | 1; rc5 = ir_rc5_decode(ir->code); /* two start bits? */ if (RC5_START(rc5) != ir->start) { - dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5)); + IR_dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5)); /* right address? */ } else if (RC5_ADDR(rc5) == ir->addr) { @@ -353,11 +345,10 @@ void ir_rc5_timer_end(unsigned long data) /* Good code, decide if repeat/repress */ if (toggle != RC5_TOGGLE(ir->last_rc5) || instr != RC5_INSTR(ir->last_rc5)) { - dprintk(1, "ir-common: instruction %x, toggle %x\n", instr, + IR_dprintk(1, "ir-common: instruction %x, toggle %x\n", instr, toggle); ir_input_nokey(ir->dev, &ir->ir); - ir_input_keydown(ir->dev, &ir->ir, instr, - instr); + ir_input_keydown(ir->dev, &ir->ir, instr); } /* Set/reset key-up timer */ @@ -376,7 +367,7 @@ void ir_rc5_timer_keyup(unsigned long data) { struct card_ir *ir = (struct card_ir *)data; - dprintk(1, "ir-common: key released\n"); + IR_dprintk(1, "ir-common: key released\n"); ir_input_nokey(ir->dev, &ir->ir); } EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup); diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index f6790172736a..328c973a0838 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1705,6 +1705,7 @@ static struct ir_scancode ir_codes_winfast[] = { { 0x37, KEY_RADIO }, /* FM */ { 0x38, KEY_DVD }, + { 0x1a, KEY_MODE}, /* change to MCE mode on Y04G0051 */ { 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */ { 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */ { 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */ @@ -1846,6 +1847,76 @@ struct ir_scancode_table ir_codes_hauppauge_new_table = { }; EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table); +/* + * Hauppauge:the newer, gray remotes (seems there are multiple + * slightly different versions), shipped with cx88+ivtv cards. + * + * This table contains the complete RC5 code, instead of just the data part + */ +static struct ir_scancode ir_codes_rc5_hauppauge_new[] = { + /* Keys 0 to 9 */ + { 0x1e00, KEY_0 }, + { 0x1e01, KEY_1 }, + { 0x1e02, KEY_2 }, + { 0x1e03, KEY_3 }, + { 0x1e04, KEY_4 }, + { 0x1e05, KEY_5 }, + { 0x1e06, KEY_6 }, + { 0x1e07, KEY_7 }, + { 0x1e08, KEY_8 }, + { 0x1e09, KEY_9 }, + + { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */ + { 0x1e0b, KEY_RED }, /* red button */ + { 0x1e0c, KEY_RADIO }, + { 0x1e0d, KEY_MENU }, + { 0x1e0e, KEY_SUBTITLE }, /* also the # key */ + { 0x1e0f, KEY_MUTE }, + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + { 0x1e12, KEY_PREVIOUS }, /* previous channel */ + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e18, KEY_VIDEO }, /* Videos */ + { 0x1e19, KEY_AUDIO }, /* Music */ + /* 0x1e1a: Pictures - presume this means + "Multimedia Home Platform" - + no "PICTURES" key in input.h + */ + { 0x1e1a, KEY_MHP }, + + { 0x1e1b, KEY_EPG }, /* Guide */ + { 0x1e1c, KEY_TV }, + { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ + { 0x1e1f, KEY_EXIT }, /* back/exit */ + { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ + { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */ + { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x1e25, KEY_ENTER }, /* OK */ + { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */ + { 0x1e29, KEY_BLUE }, /* blue key */ + { 0x1e2e, KEY_GREEN }, /* green button */ + { 0x1e30, KEY_PAUSE }, /* pause */ + { 0x1e32, KEY_REWIND }, /* backward << */ + { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ + { 0x1e35, KEY_PLAY }, + { 0x1e36, KEY_STOP }, + { 0x1e37, KEY_RECORD }, /* recording */ + { 0x1e38, KEY_YELLOW }, /* yellow key */ + { 0x1e3b, KEY_SELECT }, /* top right button */ + { 0x1e3c, KEY_ZOOM }, /* full */ + { 0x1e3d, KEY_POWER }, /* system power (green button) */ +}; + +struct ir_scancode_table ir_codes_rc5_hauppauge_new_table = { + .scan = ir_codes_rc5_hauppauge_new, + .size = ARRAY_SIZE(ir_codes_rc5_hauppauge_new), +}; +EXPORT_SYMBOL_GPL(ir_codes_rc5_hauppauge_new_table); + static struct ir_scancode ir_codes_npgtech[] = { { 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */ { 0x2a, KEY_FRONT }, @@ -2964,6 +3035,101 @@ struct ir_scancode_table ir_codes_dm1105_nec_table = { }; EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table); +static struct ir_scancode ir_codes_tevii_nec[] = { + { 0x0a, KEY_POWER2}, + { 0x0c, KEY_MUTE}, + { 0x11, KEY_1}, + { 0x12, KEY_2}, + { 0x13, KEY_3}, + { 0x14, KEY_4}, + { 0x15, KEY_5}, + { 0x16, KEY_6}, + { 0x17, KEY_7}, + { 0x18, KEY_8}, + { 0x19, KEY_9}, + { 0x10, KEY_0}, + { 0x1c, KEY_MENU}, + { 0x0f, KEY_VOLUMEDOWN}, + { 0x1a, KEY_LAST}, + { 0x0e, KEY_OPEN}, + { 0x04, KEY_RECORD}, + { 0x09, KEY_VOLUMEUP}, + { 0x08, KEY_CHANNELUP}, + { 0x07, KEY_PVR}, + { 0x0b, KEY_TIME}, + { 0x02, KEY_RIGHT}, + { 0x03, KEY_LEFT}, + { 0x00, KEY_UP}, + { 0x1f, KEY_OK}, + { 0x01, KEY_DOWN}, + { 0x05, KEY_TUNER}, + { 0x06, KEY_CHANNELDOWN}, + { 0x40, KEY_PLAYPAUSE}, + { 0x1e, KEY_REWIND}, + { 0x1b, KEY_FAVORITES}, + { 0x1d, KEY_BACK}, + { 0x4d, KEY_FASTFORWARD}, + { 0x44, KEY_EPG}, + { 0x4c, KEY_INFO}, + { 0x41, KEY_AB}, + { 0x43, KEY_AUDIO}, + { 0x45, KEY_SUBTITLE}, + { 0x4a, KEY_LIST}, + { 0x46, KEY_F1}, + { 0x47, KEY_F2}, + { 0x5e, KEY_F3}, + { 0x5c, KEY_F4}, + { 0x52, KEY_F5}, + { 0x5a, KEY_F6}, + { 0x56, KEY_MODE}, + { 0x58, KEY_SWITCHVIDEOMODE}, +}; +struct ir_scancode_table ir_codes_tevii_nec_table = { + .scan = ir_codes_tevii_nec, + .size = ARRAY_SIZE(ir_codes_tevii_nec), +}; +EXPORT_SYMBOL_GPL(ir_codes_tevii_nec_table); + +static struct ir_scancode ir_codes_tbs_nec[] = { + { 0x04, KEY_POWER2}, /*power*/ + { 0x14, KEY_MUTE}, /*mute*/ + { 0x07, KEY_1}, + { 0x06, KEY_2}, + { 0x05, KEY_3}, + { 0x0b, KEY_4}, + { 0x0a, KEY_5}, + { 0x09, KEY_6}, + { 0x0f, KEY_7}, + { 0x0e, KEY_8}, + { 0x0d, KEY_9}, + { 0x12, KEY_0}, + { 0x16, KEY_CHANNELUP}, /*ch+*/ + { 0x11, KEY_CHANNELDOWN},/*ch-*/ + { 0x13, KEY_VOLUMEUP}, /*vol+*/ + { 0x0c, KEY_VOLUMEDOWN},/*vol-*/ + { 0x03, KEY_RECORD}, /*rec*/ + { 0x18, KEY_PAUSE}, /*pause*/ + { 0x19, KEY_OK}, /*ok*/ + { 0x1a, KEY_CAMERA}, /* snapshot */ + { 0x01, KEY_UP}, + { 0x10, KEY_LEFT}, + { 0x02, KEY_RIGHT}, + { 0x08, KEY_DOWN}, + { 0x15, KEY_FAVORITES}, + { 0x17, KEY_SUBTITLE}, + { 0x1d, KEY_ZOOM}, + { 0x1f, KEY_EXIT}, + { 0x1e, KEY_MENU}, + { 0x1c, KEY_EPG}, + { 0x00, KEY_PREVIOUS}, + { 0x1b, KEY_MODE}, +}; +struct ir_scancode_table ir_codes_tbs_nec_table = { + .scan = ir_codes_tbs_nec, + .size = ARRAY_SIZE(ir_codes_tbs_nec), +}; +EXPORT_SYMBOL_GPL(ir_codes_tbs_nec_table); + /* Terratec Cinergy Hybrid T USB XS Devin Heitmueller <dheitmueller@linuxtv.org> */ @@ -3147,3 +3313,4 @@ struct ir_scancode_table ir_codes_gadmei_rm008z_table = { .size = ARRAY_SIZE(ir_codes_gadmei_rm008z), }; EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table); + diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c new file mode 100644 index 000000000000..26ce5bc2fdd5 --- /dev/null +++ b/drivers/media/common/ir-keytable.c @@ -0,0 +1,429 @@ +/* ir-register.c - handle IR scancode->keycode tables + * + * Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com> + */ + +#include <linux/usb/input.h> + +#include <media/ir-common.h> + +#define IR_TAB_MIN_SIZE 32 +#define IR_TAB_MAX_SIZE 1024 + +/** + * ir_seek_table() - returns the element order on the table + * @rc_tab: the ir_scancode_table with the keymap to be used + * @scancode: the scancode that we're seeking + * + * This routine is used by the input routines when a key is pressed at the + * IR. The scancode is received and needs to be converted into a keycode. + * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the + * corresponding keycode from the table. + */ +static int ir_seek_table(struct ir_scancode_table *rc_tab, u32 scancode) +{ + int rc; + unsigned long flags; + struct ir_scancode *keymap = rc_tab->scan; + + spin_lock_irqsave(&rc_tab->lock, flags); + + /* FIXME: replace it by a binary search */ + + for (rc = 0; rc < rc_tab->size; rc++) + if (keymap[rc].scancode == scancode) + goto exit; + + /* Not found */ + rc = -EINVAL; + +exit: + spin_unlock_irqrestore(&rc_tab->lock, flags); + return rc; +} + +/** + * ir_roundup_tablesize() - gets an optimum value for the table size + * @n_elems: minimum number of entries to store keycodes + * + * This routine is used to choose the keycode table size. + * + * In order to have some empty space for new keycodes, + * and knowing in advance that kmalloc allocates only power of two + * segments, it optimizes the allocated space to have some spare space + * for those new keycodes by using the maximum number of entries that + * will be effectively be allocated by kmalloc. + * In order to reduce the quantity of table resizes, it has a minimum + * table size of IR_TAB_MIN_SIZE. + */ +int ir_roundup_tablesize(int n_elems) +{ + size_t size; + + if (n_elems < IR_TAB_MIN_SIZE) + n_elems = IR_TAB_MIN_SIZE; + + /* + * As kmalloc only allocates sizes of power of two, get as + * much entries as possible for the allocated memory segment + */ + size = roundup_pow_of_two(n_elems * sizeof(struct ir_scancode)); + n_elems = size / sizeof(struct ir_scancode); + + return n_elems; +} + +/** + * ir_copy_table() - copies a keytable, discarding the unused entries + * @destin: destin table + * @origin: origin table + * + * Copies all entries where the keycode is not KEY_UNKNOWN/KEY_RESERVED + */ + +int ir_copy_table(struct ir_scancode_table *destin, + const struct ir_scancode_table *origin) +{ + int i, j = 0; + + for (i = 0; i < origin->size; i++) { + if (origin->scan[i].keycode == KEY_UNKNOWN || + origin->scan[i].keycode == KEY_RESERVED) + continue; + + memcpy(&destin->scan[j], &origin->scan[i], sizeof(struct ir_scancode)); + j++; + } + destin->size = j; + + IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", destin->size); + + return 0; +} + +/** + * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table + * @dev: the struct input_dev device descriptor + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + * This routine is used to handle evdev EVIOCGKEY ioctl. + * If the key is not found, returns -EINVAL, otherwise, returns 0. + */ +static int ir_getkeycode(struct input_dev *dev, + int scancode, int *keycode) +{ + int elem; + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + + elem = ir_seek_table(rc_tab, scancode); + if (elem >= 0) { + *keycode = rc_tab->scan[elem].keycode; + return 0; + } + + /* + * Scancode not found and table can't be expanded + */ + if (elem < 0 && rc_tab->size == IR_TAB_MAX_SIZE) + return -EINVAL; + + /* + * If is there extra space, returns KEY_RESERVED, + * otherwise, input core won't let ir_setkeycode to work + */ + *keycode = KEY_RESERVED; + return 0; +} + + +/** + * ir_is_resize_needed() - Check if the table needs rezise + * @table: keycode table that may need to resize + * @n_elems: minimum number of entries to store keycodes + * + * Considering that kmalloc uses power of two storage areas, this + * routine detects if the real alloced size will change. If not, it + * just returns without doing nothing. Otherwise, it will extend or + * reduce the table size to meet the new needs. + * + * It returns 0 if no resize is needed, 1 otherwise. + */ +static int ir_is_resize_needed(struct ir_scancode_table *table, int n_elems) +{ + int cur_size = ir_roundup_tablesize(table->size); + int new_size = ir_roundup_tablesize(n_elems); + + if (cur_size == new_size) + return 0; + + /* Resize is needed */ + return 1; +} + +/** + * ir_delete_key() - remove a keycode from the table + * @rc_tab: keycode table + * @elem: element to be removed + * + */ +static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem) +{ + unsigned long flags = 0; + int newsize = rc_tab->size - 1; + int resize = ir_is_resize_needed(rc_tab, newsize); + struct ir_scancode *oldkeymap = rc_tab->scan; + struct ir_scancode *newkeymap; + + if (resize) { + newkeymap = kzalloc(ir_roundup_tablesize(newsize) * + sizeof(*newkeymap), GFP_ATOMIC); + + /* There's no memory for resize. Keep the old table */ + if (!newkeymap) + resize = 0; + } + + if (!resize) { + newkeymap = oldkeymap; + + /* We'll modify the live table. Lock it */ + spin_lock_irqsave(&rc_tab->lock, flags); + } + + /* + * Copy the elements before the one that will be deleted + * if (!resize), both oldkeymap and newkeymap points + * to the same place, so, there's no need to copy + */ + if (resize && elem > 0) + memcpy(newkeymap, oldkeymap, + elem * sizeof(*newkeymap)); + + /* + * Copy the other elements overwriting the element to be removed + * This operation applies to both resize and non-resize case + */ + if (elem < newsize) + memcpy(&newkeymap[elem], &oldkeymap[elem + 1], + (newsize - elem) * sizeof(*newkeymap)); + + if (resize) { + /* + * As the copy happened to a temporary table, only here + * it needs to lock while replacing the table pointers + * to use the new table + */ + spin_lock_irqsave(&rc_tab->lock, flags); + rc_tab->size = newsize; + rc_tab->scan = newkeymap; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + /* Frees the old keytable */ + kfree(oldkeymap); + } else { + rc_tab->size = newsize; + spin_unlock_irqrestore(&rc_tab->lock, flags); + } +} + +/** + * ir_insert_key() - insert a keycode at the table + * @rc_tab: keycode table + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + */ +static int ir_insert_key(struct ir_scancode_table *rc_tab, + int scancode, int keycode) +{ + unsigned long flags; + int elem = rc_tab->size; + int newsize = rc_tab->size + 1; + int resize = ir_is_resize_needed(rc_tab, newsize); + struct ir_scancode *oldkeymap = rc_tab->scan; + struct ir_scancode *newkeymap; + + if (resize) { + newkeymap = kzalloc(ir_roundup_tablesize(newsize) * + sizeof(*newkeymap), GFP_ATOMIC); + if (!newkeymap) + return -ENOMEM; + + memcpy(newkeymap, oldkeymap, + rc_tab->size * sizeof(*newkeymap)); + } else + newkeymap = oldkeymap; + + /* Stores the new code at the table */ + IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n", + rc_tab->size, scancode, keycode); + + spin_lock_irqsave(&rc_tab->lock, flags); + rc_tab->size = newsize; + if (resize) { + rc_tab->scan = newkeymap; + kfree(oldkeymap); + } + newkeymap[elem].scancode = scancode; + newkeymap[elem].keycode = keycode; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + return 0; +} + +/** + * ir_setkeycode() - set a keycode at the evdev scancode ->keycode table + * @dev: the struct input_dev device descriptor + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + * This routine is used to handle evdev EVIOCSKEY ioctl. + * There's one caveat here: how can we increase the size of the table? + * If the key is not found, returns -EINVAL, otherwise, returns 0. + */ +static int ir_setkeycode(struct input_dev *dev, + int scancode, int keycode) +{ + int rc = 0; + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + struct ir_scancode *keymap = rc_tab->scan; + unsigned long flags; + + /* + * Handle keycode table deletions + * + * If userspace is adding a KEY_UNKNOWN or KEY_RESERVED, + * deal as a trial to remove an existing scancode attribution + * if table become too big, reduce it to save space + */ + if (keycode == KEY_UNKNOWN || keycode == KEY_RESERVED) { + rc = ir_seek_table(rc_tab, scancode); + if (rc < 0) + return 0; + + IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", rc, scancode); + clear_bit(keymap[rc].keycode, dev->keybit); + ir_delete_key(rc_tab, rc); + + return 0; + } + + /* + * Handle keycode replacements + * + * If the scancode exists, just replace by the new value + */ + rc = ir_seek_table(rc_tab, scancode); + if (rc >= 0) { + IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", + rc, scancode, keycode); + + clear_bit(keymap[rc].keycode, dev->keybit); + + spin_lock_irqsave(&rc_tab->lock, flags); + keymap[rc].keycode = keycode; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + set_bit(keycode, dev->keybit); + + return 0; + } + + /* + * Handle new scancode inserts + * + * reallocate table if needed and insert a new keycode + */ + + /* Avoid growing the table indefinitely */ + if (rc_tab->size + 1 > IR_TAB_MAX_SIZE) + return -EINVAL; + + rc = ir_insert_key(rc_tab, scancode, keycode); + if (rc < 0) + return rc; + set_bit(keycode, dev->keybit); + + return 0; +} + +/** + * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode + * @input_dev: the struct input_dev descriptor of the device + * @scancode: the scancode that we're seeking + * + * This routine is used by the input routines when a key is pressed at the + * IR. The scancode is received and needs to be converted into a keycode. + * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the + * corresponding keycode from the table. + */ +u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) +{ + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + struct ir_scancode *keymap = rc_tab->scan; + int elem; + + elem = ir_seek_table(rc_tab, scancode); + if (elem >= 0) { + IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", + dev->name, scancode, keymap[elem].keycode); + + return rc_tab->scan[elem].keycode; + } + + printk(KERN_INFO "%s: unknown key for scancode 0x%04x\n", + dev->name, scancode); + + /* Reports userspace that an unknown keycode were got */ + return KEY_RESERVED; +} + +/** + * ir_set_keycode_table() - sets the IR keycode table and add the handlers + * for keymap table get/set + * @input_dev: the struct input_dev descriptor of the device + * @rc_tab: the struct ir_scancode_table table of scancode/keymap + * + * This routine is used to initialize the input infrastructure to work with + * an IR. + * It should be called before registering the IR device. + */ +int ir_set_keycode_table(struct input_dev *input_dev, + struct ir_scancode_table *rc_tab) +{ + struct ir_scancode *keymap = rc_tab->scan; + int i; + + spin_lock_init(&rc_tab->lock); + + if (rc_tab->scan == NULL || !rc_tab->size) + return -EINVAL; + + /* set the bits for the keys */ + IR_dprintk(1, "key map size: %d\n", rc_tab->size); + for (i = 0; i < rc_tab->size; i++) { + IR_dprintk(1, "#%d: setting bit for keycode 0x%04x\n", + i, keymap[i].keycode); + set_bit(keymap[i].keycode, input_dev->keybit); + } + + input_dev->getkeycode = ir_getkeycode; + input_dev->setkeycode = ir_setkeycode; + input_set_drvdata(input_dev, rc_tab); + + return 0; +} + +void ir_input_free(struct input_dev *dev) +{ + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + + IR_dprintk(1, "Freed keycode table\n"); + + rc_tab->size = 0; + kfree(rc_tab->scan); + rc_tab->scan = NULL; +} +EXPORT_SYMBOL_GPL(ir_input_free); + diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c index 7e8f56815998..48cb154c7a46 100644 --- a/drivers/media/common/saa7146_i2c.c +++ b/drivers/media/common/saa7146_i2c.c @@ -98,7 +98,7 @@ static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op) op_count++; - /* loop throgh all bytes of message i */ + /* loop through all bytes of message i */ for(j = 0; j < m[i].len; j++) { /* write back all bytes that could have been read */ m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8)); diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 552dab442d78..becbaadb3b77 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -1205,6 +1205,13 @@ static int buffer_activate (struct saa7146_dev *dev, return 0; } +static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) +{ + saa7146_pgtable_free(dev->pci, &buf->pt[0]); + saa7146_pgtable_free(dev->pci, &buf->pt[1]); + saa7146_pgtable_free(dev->pci, &buf->pt[2]); +} + static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { @@ -1257,16 +1264,12 @@ static int buffer_prepare(struct videobuf_queue *q, sfmt = format_by_fourcc(dev,buf->fmt->pixelformat); + release_all_pagetables(dev, buf); if( 0 != IS_PLANAR(sfmt->trans)) { - saa7146_pgtable_free(dev->pci, &buf->pt[0]); - saa7146_pgtable_free(dev->pci, &buf->pt[1]); - saa7146_pgtable_free(dev->pci, &buf->pt[2]); - saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); } else { - saa7146_pgtable_free(dev->pci, &buf->pt[0]); saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); } @@ -1329,6 +1332,9 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_CAP(("vbuf:%p\n",vb)); + + release_all_pagetables(dev, buf); + saa7146_dma_free(dev,q,buf); } diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 607d319ce8ed..409a4261e5b5 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -172,4 +172,11 @@ config MEDIA_TUNER_MC44S803 help Say Y here to support the Freescale MC44S803 based tuners +config MEDIA_TUNER_MAX2165 + tristate "Maxim MAX2165 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + A driver for the silicon tuner MAX2165 from Maxim. + endif # MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 4132b2be79e5..a5438523f30d 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o +obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c new file mode 100644 index 000000000000..3d03640cf1fe --- /dev/null +++ b/drivers/media/common/tuners/max2165.c @@ -0,0 +1,442 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/videodev2.h> +#include <linux/delay.h> +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +#include "dvb_frontend.h" + +#include "max2165.h" +#include "max2165_priv.h" +#include "tuner-i2c.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "max2165: " args); \ + } while (0) + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; + + msg.addr = priv->config->i2c_address; + + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg, 1); + + if (ret != 1) + dprintk(KERN_DEBUG "%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EIO : 0; +} + +static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data) +{ + int ret; + u8 dev_addr = priv->config->i2c_address; + + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }, + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret != 2) { + dprintk(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n", + __func__, reg, ret); + return -EIO; + } + + *p_data = b1[0]; + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, b1[0]); + return 0; +} + +static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg, + u8 mask, u8 data) +{ + int ret; + u8 v; + + data &= mask; + ret = max2165_read_reg(priv, reg, &v); + if (ret != 0) + return ret; + v &= ~mask; + v |= data; + ret = max2165_write_reg(priv, reg, v); + + return ret; +} + +static int max2165_read_rom_table(struct max2165_priv *priv) +{ + u8 dat[3]; + int i; + + for (i = 0; i < 3; i++) { + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1); + max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]); + } + + priv->tf_ntch_low_cfg = dat[0] >> 4; + priv->tf_ntch_hi_cfg = dat[0] & 0x0F; + priv->tf_balun_low_ref = dat[1] & 0x0F; + priv->tf_balun_hi_ref = dat[1] >> 4; + priv->bb_filter_7mhz_cfg = dat[2] & 0x0F; + priv->bb_filter_8mhz_cfg = dat[2] >> 4; + + dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg); + dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg); + dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref); + dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref); + dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg); + dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg); + + return 0; +} + +static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/) +{ + u8 v; + + v = (osc / 2); + if (v == 2) + v = 0x7; + else + v -= 8; + + max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v); + + return 0; +} + +static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw) +{ + u8 val; + + if (bw == BANDWIDTH_8_MHZ) + val = priv->bb_filter_8mhz_cfg; + else + val = priv->bb_filter_7mhz_cfg; + + max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4); + + return 0; +} + +int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction) +{ + u32 remainder; + u32 q, f = 0; + int i; + + if (0 == divisor) + return -1; + + q = dividend / divisor; + remainder = dividend - q * divisor; + + for (i = 0; i < 31; i++) { + remainder <<= 1; + if (remainder >= divisor) { + f += 1; + remainder -= divisor; + } + f <<= 1; + } + + *quotient = q; + *fraction = f; + + return 0; +} + +static int max2165_set_rf(struct max2165_priv *priv, u32 freq) +{ + u8 tf; + u8 tf_ntch; + u32 t; + u32 quotient, fraction; + + /* Set PLL divider according to RF frequency */ + fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, + "ient, &fraction); + + /* 20-bit fraction */ + fraction >>= 12; + + max2165_write_reg(priv, REG_NDIV_INT, quotient); + max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16); + max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8); + max2165_write_reg(priv, REG_NDIV_FRAC0, fraction); + + /* Norch Filter */ + tf_ntch = (freq < 725000000) ? + priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg; + + /* Tracking filter balun */ + t = priv->tf_balun_low_ref; + t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref) + * (freq / 1000 - 470000) / (780000 - 470000); + + tf = t; + dprintk("tf = %X\n", tf); + tf |= tf_ntch << 4; + + max2165_write_reg(priv, REG_TRACK_FILTER, tf); + + return 0; +} + +static void max2165_debug_status(struct max2165_priv *priv) +{ + u8 status, autotune; + u8 auto_vco_success, auto_vco_active; + u8 pll_locked; + u8 dc_offset_low, dc_offset_hi; + u8 signal_lv_over_threshold; + u8 vco, vco_sub_band, adc; + + max2165_read_reg(priv, REG_STATUS, &status); + max2165_read_reg(priv, REG_AUTOTUNE, &autotune); + + auto_vco_success = (status >> 6) & 0x01; + auto_vco_active = (status >> 5) & 0x01; + pll_locked = (status >> 4) & 0x01; + dc_offset_low = (status >> 3) & 0x01; + dc_offset_hi = (status >> 2) & 0x01; + signal_lv_over_threshold = status & 0x01; + + vco = autotune >> 6; + vco_sub_band = (autotune >> 3) & 0x7; + adc = autotune & 0x7; + + dprintk("auto VCO active: %d, auto VCO success: %d\n", + auto_vco_active, auto_vco_success); + dprintk("PLL locked: %d\n", pll_locked); + dprintk("DC offset low: %d, DC offset high: %d\n", + dc_offset_low, dc_offset_hi); + dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold); + dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc); +} + +static int max2165_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct max2165_priv *priv = fe->tuner_priv; + int ret; + + dprintk("%s() frequency=%d (Hz)\n", __func__, params->frequency); + if (fe->ops.info.type == FE_ATSC) { + return -EINVAL; + } else if (fe->ops.info.type == FE_OFDM) { + dprintk("%s() OFDM\n", __func__); + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + return -EINVAL; + case BANDWIDTH_7_MHZ: + case BANDWIDTH_8_MHZ: + priv->frequency = params->frequency; + priv->bandwidth = params->u.ofdm.bandwidth; + break; + default: + printk(KERN_ERR "MAX2165 bandwidth not set!\n"); + return -EINVAL; + } + } else { + printk(KERN_ERR "MAX2165 modulation type not supported!\n"); + return -EINVAL; + } + + dprintk("%s() frequency=%d\n", __func__, priv->frequency); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + max2165_set_bandwidth(priv, priv->bandwidth); + ret = max2165_set_rf(priv, priv->frequency); + mdelay(50); + max2165_debug_status(priv); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 0) + return -EREMOTEIO; + + return 0; +} + +static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + *freq = priv->frequency; + return 0; +} + +static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + *bw = priv->bandwidth; + return 0; +} + +static int max2165_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct max2165_priv *priv = fe->tuner_priv; + u16 lock_status = 0; + + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + max2165_debug_status(priv); + *status = lock_status; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_sleep(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +static int max2165_init(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* Setup initial values */ + /* Fractional Mode on */ + max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18); + /* LNA on */ + max2165_write_reg(priv, REG_LNA, 0x01); + max2165_write_reg(priv, REG_PLL_CFG, 0x7A); + max2165_write_reg(priv, REG_TEST, 0x08); + max2165_write_reg(priv, REG_SHUTDOWN, 0x40); + max2165_write_reg(priv, REG_VCO_CTRL, 0x84); + max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3); + max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75); + max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00); + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00); + + max2165_set_osc(priv, priv->config->osc_clk); + + max2165_read_rom_table(priv); + + max2165_set_bandwidth(priv, BANDWIDTH_8_MHZ); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_release(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + kfree(priv); + fe->tuner_priv = NULL; + + return 0; +} + +static const struct dvb_tuner_ops max2165_tuner_ops = { + .info = { + .name = "Maxim MAX2165", + .frequency_min = 470000000, + .frequency_max = 780000000, + .frequency_step = 50000, + }, + + .release = max2165_release, + .init = max2165_init, + .sleep = max2165_sleep, + + .set_params = max2165_set_params, + .set_analog_params = NULL, + .get_frequency = max2165_get_frequency, + .get_bandwidth = max2165_get_bandwidth, + .get_status = max2165_get_status +}; + +struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + struct max2165_priv *priv = NULL; + + dprintk("%s(%d-%04x)\n", __func__, + i2c ? i2c_adapter_id(i2c) : -1, + cfg ? cfg->i2c_address : -1); + + priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + priv->config = cfg; + priv->i2c = i2c; + fe->tuner_priv = priv; + + max2165_init(fe); + max2165_debug_status(priv); + + return fe; +} +EXPORT_SYMBOL(max2165_attach); + +MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); +MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/common/tuners/max2165.h new file mode 100644 index 000000000000..c063c36a93d3 --- /dev/null +++ b/drivers/media/common/tuners/max2165.h @@ -0,0 +1,48 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MAX2165_H__ +#define __MAX2165_H__ + +struct dvb_frontend; +struct i2c_adapter; + +struct max2165_config { + u8 i2c_address; + u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */ +}; + +#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \ + (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE)) +extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg); +#else +static inline struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/common/tuners/max2165_priv.h new file mode 100644 index 000000000000..91bbe021a08d --- /dev/null +++ b/drivers/media/common/tuners/max2165_priv.h @@ -0,0 +1,60 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MAX2165_PRIV_H__ +#define __MAX2165_PRIV_H__ + +#define REG_NDIV_INT 0x00 +#define REG_NDIV_FRAC2 0x01 +#define REG_NDIV_FRAC1 0x02 +#define REG_NDIV_FRAC0 0x03 +#define REG_TRACK_FILTER 0x04 +#define REG_LNA 0x05 +#define REG_PLL_CFG 0x06 +#define REG_TEST 0x07 +#define REG_SHUTDOWN 0x08 +#define REG_VCO_CTRL 0x09 +#define REG_BASEBAND_CTRL 0x0A +#define REG_DC_OFFSET_CTRL 0x0B +#define REG_DC_OFFSET_DAC 0x0C +#define REG_ROM_TABLE_ADDR 0x0D + +/* Read Only Registers */ +#define REG_ROM_TABLE_DATA 0x10 +#define REG_STATUS 0x11 +#define REG_AUTOTUNE 0x12 + +struct max2165_priv { + struct max2165_config *config; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; + + u8 tf_ntch_low_cfg; + u8 tf_ntch_hi_cfg; + u8 tf_balun_low_ref; + u8 tf_balun_hi_ref; + u8 bb_filter_7mhz_cfg; + u8 bb_filter_8mhz_cfg; +}; + +#endif diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c index 0803dab58fff..605e28b73263 100644 --- a/drivers/media/common/tuners/mxl5005s.c +++ b/drivers/media/common/tuners/mxl5005s.c @@ -2789,7 +2789,10 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) /* add for 2.6.5 Special setting for QAM */ if (state->Mod_Type == MXL_QAM) { - if (state->RF_IN < 680000000) + if (state->config->qam_gain != 0) + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, + state->config->qam_gain); + else if (state->RF_IN < 680000000) status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); else status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h index 7ac6815b30aa..fc8a1ffc53b4 100644 --- a/drivers/media/common/tuners/mxl5005s.h +++ b/drivers/media/common/tuners/mxl5005s.h @@ -108,6 +108,10 @@ struct mxl5005s_config { #define MXL_LOW_IF 1 u8 if_mode; + /* Some boards need to override the built-in logic for determining + the gain when in QAM mode (the HVR-1600 is one such case) */ + u8 qam_gain; + /* Stuff I don't know what to do with */ u8 AgcMasterByte; }; diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c index 2d02698d4f4f..7eb1bf75cd07 100644 --- a/drivers/media/common/tuners/mxl5007t.c +++ b/drivers/media/common/tuners/mxl5007t.c @@ -196,7 +196,7 @@ static void copy_reg_bits(struct reg_pair_t *reg_pair1, i = j = 0; while (reg_pair1[i].reg || reg_pair1[i].val) { - while (reg_pair2[j].reg || reg_pair2[j].reg) { + while (reg_pair2[j].reg || reg_pair2[j].val) { if (reg_pair1[i].reg != reg_pair2[j].reg) { j++; continue; diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c index 155c93eb75da..e1f678281a58 100644 --- a/drivers/media/common/tuners/tda18271-common.c +++ b/drivers/media/common/tuners/tda18271-common.c @@ -326,12 +326,24 @@ int tda18271_init_regs(struct dvb_frontend *fe) regs[R_EB22] = 0x48; regs[R_EB23] = 0xb0; - if (priv->small_i2c) { + switch (priv->small_i2c) { + case TDA18271_08_BYTE_CHUNK_INIT: + tda18271_write_regs(fe, 0x00, 0x08); + tda18271_write_regs(fe, 0x08, 0x08); + tda18271_write_regs(fe, 0x10, 0x08); + tda18271_write_regs(fe, 0x18, 0x08); + tda18271_write_regs(fe, 0x20, 0x07); + break; + case TDA18271_16_BYTE_CHUNK_INIT: tda18271_write_regs(fe, 0x00, 0x10); tda18271_write_regs(fe, 0x10, 0x10); tda18271_write_regs(fe, 0x20, 0x07); - } else + break; + case TDA18271_39_BYTE_CHUNK_INIT: + default: tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); + break; + } /* setup agc1 gain */ regs[R_EB17] = 0x00; diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 3a50ce96fcb9..b2e15456d5f3 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -256,8 +256,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; - int tm_current, rfcal_comp, approx, i, ret; - u8 dc_over_dt, rf_tab; + int i, ret; + u8 tm_current, dc_over_dt, rf_tab; + s32 rfcal_comp, approx; /* power up */ ret = tda18271_set_standby_mode(fe, 0, 0, 0); @@ -277,11 +278,11 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, return i; if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { - approx = map[i].rf_a1 * - (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab; + approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) + + map[i].rf_b1 + rf_tab; } else { - approx = map[i].rf_a2 * - (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab; + approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) + + map[i].rf_b2 + rf_tab; } if (approx < 0) @@ -292,9 +293,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); /* calculate temperature compensation */ - rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000; + rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000; - regs[R_EB14] = approx + rfcal_comp; + regs[R_EB14] = (unsigned char)(approx + rfcal_comp); ret = tda18271_write_regs(fe, R_EB14, 1); fail: return ret; @@ -572,6 +573,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; int bcal, rf, i; + s32 divisor, dividend; #define RF1 0 #define RF2 1 #define RF3 2 @@ -610,20 +612,22 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) switch (rf) { case RF1: map[i].rf_a1 = 0; - map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1]; + map[i].rf_b1 = (s32)(prog_cal[RF1] - prog_tab[RF1]); map[i].rf1 = rf_freq[RF1] / 1000; break; case RF2: - map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] - - prog_cal[RF1] + prog_tab[RF1]) / - (s32)((rf_freq[RF2] - rf_freq[RF1]) / 1000); + dividend = (s32)(prog_cal[RF2] - prog_tab[RF2]) - + (s32)(prog_cal[RF1] + prog_tab[RF1]); + divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000; + map[i].rf_a1 = (dividend / divisor); map[i].rf2 = rf_freq[RF2] / 1000; break; case RF3: - map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] - - prog_cal[RF2] + prog_tab[RF2]) / - (s32)((rf_freq[RF3] - rf_freq[RF2]) / 1000); - map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2]; + dividend = (s32)(prog_cal[RF3] - prog_tab[RF3]) - + (s32)(prog_cal[RF2] + prog_tab[RF2]); + divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000; + map[i].rf_a2 = (dividend / divisor); + map[i].rf_b2 = (s32)(prog_cal[RF2] - prog_tab[RF2]); map[i].rf3 = rf_freq[RF3] / 1000; break; default: @@ -1181,6 +1185,48 @@ static int tda18271_get_id(struct dvb_frontend *fe) return ret; } +static int tda18271_setup_configuration(struct dvb_frontend *fe, + struct tda18271_config *cfg) +{ + struct tda18271_priv *priv = fe->tuner_priv; + + priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; + priv->role = (cfg) ? cfg->role : TDA18271_MASTER; + priv->config = (cfg) ? cfg->config : 0; + priv->small_i2c = (cfg) ? + cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; + priv->output_opt = (cfg) ? + cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; + + return 0; +} + +static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg) +{ + /* tda18271_cal_on_startup == -1 when cal module option is unset */ + return ((tda18271_cal_on_startup == -1) ? + /* honor configuration setting */ + ((cfg) && (cfg->rf_cal_on_startup)) : + /* module option overrides configuration setting */ + (tda18271_cal_on_startup)) ? 1 : 0; +} + +static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; + + tda18271_setup_configuration(fe, cfg); + + if (tda18271_need_cal_on_startup(cfg)) + tda18271_init(fe); + + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + + return 0; +} + static struct dvb_tuner_ops tda18271_tuner_ops = { .info = { .name = "NXP TDA18271HD", @@ -1193,6 +1239,7 @@ static struct dvb_tuner_ops tda18271_tuner_ops = { .set_params = tda18271_set_params, .set_analog_params = tda18271_set_analog_params, .release = tda18271_release, + .set_config = tda18271_set_config, .get_frequency = tda18271_get_frequency, .get_bandwidth = tda18271_get_bandwidth, }; @@ -1213,33 +1260,14 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, case 0: goto fail; case 1: - { /* new tuner instance */ - int rf_cal_on_startup; - - priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; - priv->role = (cfg) ? cfg->role : TDA18271_MASTER; - priv->config = (cfg) ? cfg->config : 0; - priv->small_i2c = (cfg) ? cfg->small_i2c : 0; - priv->output_opt = (cfg) ? - cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; - - /* tda18271_cal_on_startup == -1 when cal - * module option is unset */ - if (tda18271_cal_on_startup == -1) { - /* honor attach-time configuration */ - rf_cal_on_startup = - ((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0; - } else { - /* module option overrides attach configuration */ - rf_cal_on_startup = tda18271_cal_on_startup; - } + fe->tuner_priv = priv; + + tda18271_setup_configuration(fe, cfg); priv->cal_initialized = false; mutex_init(&priv->lock); - fe->tuner_priv = priv; - if (tda_fail(tda18271_get_id(fe))) goto fail; @@ -1249,12 +1277,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, mutex_lock(&priv->lock); tda18271_init_regs(fe); - if ((rf_cal_on_startup) && (priv->id == TDA18271HDC2)) + if ((tda18271_need_cal_on_startup(cfg)) && + (priv->id == TDA18271HDC2)) tda18271c2_rf_cal_init(fe); mutex_unlock(&priv->lock); break; - } default: /* existing tuner instance */ fe->tuner_priv = priv; @@ -1271,7 +1299,11 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->small_i2c = cfg->small_i2c; if (cfg->output_opt) priv->output_opt = cfg->output_opt; + if (cfg->std_map) + tda18271_update_std_map(fe, cfg->std_map); } + if (tda18271_need_cal_on_startup(cfg)) + tda18271_init(fe); break; } @@ -1298,7 +1330,7 @@ EXPORT_SYMBOL_GPL(tda18271_attach); MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.3"); +MODULE_VERSION("0.4"); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c index e21fdeff3ddf..e7f84c705da8 100644 --- a/drivers/media/common/tuners/tda18271-maps.c +++ b/drivers/media/common/tuners/tda18271-maps.c @@ -978,6 +978,7 @@ static struct tda18271_cid_target_map tda18271_cid_target[] = { int tda18271_lookup_cid_target(struct dvb_frontend *fe, u32 *freq, u8 *cid_target, u16 *count_limit) { + struct tda18271_priv *priv = fe->tuner_priv; int i = 0; while ((tda18271_cid_target[i].rfmax * 1000) < *freq) { diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h index 2bee229acd91..9589ab0576d2 100644 --- a/drivers/media/common/tuners/tda18271-priv.h +++ b/drivers/media/common/tuners/tda18271-priv.h @@ -80,10 +80,10 @@ struct tda18271_rf_tracking_filter_cal { u32 rf1; u32 rf2; u32 rf3; - int rf_a1; - int rf_b1; - int rf_a2; - int rf_b2; + s32 rf_a1; + s32 rf_b1; + s32 rf_a2; + s32 rf_b2; }; enum tda18271_pll { @@ -109,11 +109,12 @@ struct tda18271_priv { enum tda18271_i2c_gate gate; enum tda18271_ver id; enum tda18271_output_options output_opt; + enum tda18271_small_i2c small_i2c; unsigned int config; /* interface to saa713x / tda829x */ - unsigned int tm_rfcal; unsigned int cal_initialized:1; - unsigned int small_i2c:1; + + u8 tm_rfcal; struct tda18271_map_layout *maps; struct tda18271_std_map std; @@ -135,27 +136,37 @@ extern int tda18271_debug; #define DBG_ADV 8 #define DBG_CAL 16 -#define tda_printk(kern, fmt, arg...) \ - printk(kern "%s: " fmt, __func__, ##arg) - -#define tda_dprintk(lvl, fmt, arg...) do {\ +#define tda_printk(st, kern, fmt, arg...) do {\ + if (st) { \ + struct tda18271_priv *state = st; \ + printk(kern "%s: [%d-%04x|%s] " fmt, __func__, \ + i2c_adapter_id(state->i2c_props.adap), \ + state->i2c_props.addr, \ + (state->role == TDA18271_MASTER) \ + ? "M" : "S", ##arg); \ + } else \ + printk(kern "%s: " fmt, __func__, ##arg); \ +} while (0) + +#define tda_dprintk(st, lvl, fmt, arg...) do {\ if (tda18271_debug & lvl) \ - tda_printk(KERN_DEBUG, fmt, ##arg); } while (0) + tda_printk(st, KERN_DEBUG, fmt, ##arg); } while (0) #define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg) -#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg) -#define tda_err(fmt, arg...) tda_printk(KERN_ERR, fmt, ##arg) -#define tda_dbg(fmt, arg...) tda_dprintk(DBG_INFO, fmt, ##arg) -#define tda_map(fmt, arg...) tda_dprintk(DBG_MAP, fmt, ##arg) -#define tda_reg(fmt, arg...) tda_dprintk(DBG_REG, fmt, ##arg) -#define tda_cal(fmt, arg...) tda_dprintk(DBG_CAL, fmt, ##arg) +#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg) +#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg) +#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg) +#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg) +#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg) +#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg) #define tda_fail(ret) \ ({ \ int __ret; \ __ret = (ret < 0); \ if (__ret) \ - tda_printk(KERN_ERR, "error %d on line %d\n", ret, __LINE__);\ + tda_printk(priv, KERN_ERR, \ + "error %d on line %d\n", ret, __LINE__); \ __ret; \ }) diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h index 323f2912128d..d7fcc36dc6e6 100644 --- a/drivers/media/common/tuners/tda18271.h +++ b/drivers/media/common/tuners/tda18271.h @@ -78,6 +78,12 @@ enum tda18271_output_options { TDA18271_OUTPUT_XT_OFF = 2, }; +enum tda18271_small_i2c { + TDA18271_39_BYTE_CHUNK_INIT = 0, + TDA18271_16_BYTE_CHUNK_INIT = 1, + TDA18271_08_BYTE_CHUNK_INIT = 2, +}; + struct tda18271_config { /* override default if freq / std settings (optional) */ struct tda18271_std_map *std_map; @@ -91,12 +97,12 @@ struct tda18271_config { /* output options that can be disabled */ enum tda18271_output_options output_opt; + /* some i2c providers cant write all 39 registers at once */ + enum tda18271_small_i2c small_i2c; + /* force rf tracking filter calibration on startup */ unsigned int rf_cal_on_startup:1; - /* some i2c providers cant write all 39 registers at once */ - unsigned int small_i2c:1; - /* interface to saa713x / tda829x */ unsigned int config; }; diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index 064d14c8d7b2..c190b0dedee4 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -33,6 +33,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); static int deemphasis_50; +module_param(deemphasis_50, int, 0644); MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); /* ---------------------------------------------------------------------- */ diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c index 544cdbe88a6c..a71c100c95df 100644 --- a/drivers/media/common/tuners/tda9887.c +++ b/drivers/media/common/tuners/tda9887.c @@ -463,7 +463,7 @@ static int tda9887_set_insmod(struct dvb_frontend *fe) buf[1] &= ~cQSS; } - if (adjust >= 0x00 && adjust < 0x20) { + if (adjust < 0x20) { buf[2] &= ~cTopMask; buf[2] |= adjust; } diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index f4ffcdc9b848..432003dded7c 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -61,6 +61,7 @@ struct xc5000_priv { u32 bandwidth; u8 video_standard; u8 rf_mode; + u8 radio_input; }; /* Misc Defines */ @@ -632,8 +633,12 @@ static int xc5000_set_params(struct dvb_frontend *fe, struct xc5000_priv *priv = fe->tuner_priv; int ret; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { + dprintk(1, "Unable to load firmware and init tuner\n"); + return -EINVAL; + } + } dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency); @@ -739,15 +744,12 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) return ret; } -static int xc5000_set_analog_params(struct dvb_frontend *fe, +static int xc5000_set_tv_freq(struct dvb_frontend *fe, struct analog_parameters *params) { struct xc5000_priv *priv = fe->tuner_priv; int ret; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); - dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", __func__, params->frequency); @@ -827,6 +829,86 @@ tune_channel: return 0; } +static int xc5000_set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + u8 radio_input; + + dprintk(1, "%s() frequency=%d (in units of khz)\n", + __func__, params->frequency); + + if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) { + dprintk(1, "%s() radio input not configured\n", __func__); + return -EINVAL; + } + + if (priv->radio_input == XC5000_RADIO_FM1) + radio_input = FM_Radio_INPUT1; + else if (priv->radio_input == XC5000_RADIO_FM2) + radio_input = FM_Radio_INPUT2; + else { + dprintk(1, "%s() unknown radio input %d\n", __func__, + priv->radio_input); + return -EINVAL; + } + + priv->freq_hz = params->frequency * 125 / 2; + + priv->rf_mode = XC_RF_MODE_AIR; + + ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode, + XC5000_Standard[radio_input].AudioMode); + + if (ret != XC_RESULT_SUCCESS) { + printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); + return -EREMOTEIO; + } + + ret = xc_SetSignalSource(priv, priv->rf_mode); + if (ret != XC_RESULT_SUCCESS) { + printk(KERN_ERR + "xc5000: xc_SetSignalSource(%d) failed\n", + priv->rf_mode); + return -EREMOTEIO; + } + + xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG); + + return 0; +} + +static int xc5000_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { + dprintk(1, "Unable to load firmware and init tuner\n"); + return -EINVAL; + } + } + + switch (params->mode) { + case V4L2_TUNER_RADIO: + ret = xc5000_set_radio_freq(fe, params); + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: + ret = xc5000_set_tv_freq(fe, params); + break; + } + + return ret; +} + + static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc5000_priv *priv = fe->tuner_priv; @@ -1000,6 +1082,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, priv->if_khz = cfg->if_khz; } + if (priv->radio_input == 0) + priv->radio_input = cfg->radio_input; + /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. */ diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index f4c146698a00..e6d7236c9ea1 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -30,11 +30,17 @@ struct i2c_adapter; struct xc5000_config { u8 i2c_address; u32 if_khz; + u8 radio_input; }; /* xc5000 callback command */ #define XC5000_TUNER_RESET 0 +/* Possible Radio inputs */ +#define XC5000_RADIO_NOT_CONFIGURED 0 +#define XC5000_RADIO_FM1 1 +#define XC5000_RADIO_FM2 2 + /* For each bridge framework, when it attaches either analog or digital, * it has to store a reference back to its _core equivalent structure, * so that it can service the hardware by steering gpio's etc. diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index b1857c19bbd2..78fc469f0f69 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -215,7 +215,7 @@ static int cx24108_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend freq = 2150000; /* satellite IF is 950..2150MHz */ /* decide which VCO to use for the input frequency */ - for(i = 1; (i < ARRAY_SIZE(osci)) && (osci[i] < freq); i++); + for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++); printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq); band=bandsel[i]; /* the gain values must be set by SetSymbolrate */ diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index 2d099e271751..53e3f2a7d31a 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -510,7 +510,7 @@ static void dm1105_emit_key(struct work_struct *work) data = (ircom >> 8) & 0x7f; - ir_input_keydown(ir->input_dev, &ir->ir, data, data); + ir_input_keydown(ir->input_dev, &ir->ir, data); ir_input_nokey(ir->input_dev, &ir->ir); } @@ -589,7 +589,12 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), "pci-%s/ir0", pci_name(dm1105->pdev)); - ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes); + if (err < 0) { + input_free_device(input_dev); + return err; + } + input_dev->name = "DVB on-card IR receiver"; input_dev->phys = dm1105->ir.input_phys; input_dev->id.bustype = BUS_PCI; @@ -608,6 +613,7 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) err = input_register_device(input_dev); if (err) { + ir_input_free(input_dev); input_free_device(input_dev); return err; } @@ -617,8 +623,8 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105) { + ir_input_free(dm1105->ir.input_dev); input_unregister_device(dm1105->ir.input_dev); - } static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb) diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index 91c537bca8ad..b78cfb7d1897 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -30,6 +30,7 @@ #include <linux/string.h> #include <linux/crc32.h> #include <asm/uaccess.h> +#include <asm/div64.h> #include "dvb_demux.h" @@ -44,6 +45,11 @@ module_param(dvb_demux_tscheck, int, 0644); MODULE_PARM_DESC(dvb_demux_tscheck, "enable transport stream continuity and TEI check"); +static int dvb_demux_speedcheck; +module_param(dvb_demux_speedcheck, int, 0644); +MODULE_PARM_DESC(dvb_demux_speedcheck, + "enable transport stream speed check"); + #define dprintk_tscheck(x...) do { \ if (dvb_demux_tscheck && printk_ratelimit()) \ printk(x); \ @@ -387,6 +393,39 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) u16 pid = ts_pid(buf); int dvr_done = 0; + if (dvb_demux_speedcheck) { + struct timespec cur_time, delta_time; + u64 speed_bytes, speed_timedelta; + + demux->speed_pkts_cnt++; + + /* show speed every SPEED_PKTS_INTERVAL packets */ + if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) { + cur_time = current_kernel_time(); + + if (demux->speed_last_time.tv_sec != 0 && + demux->speed_last_time.tv_nsec != 0) { + delta_time = timespec_sub(cur_time, + demux->speed_last_time); + speed_bytes = (u64)demux->speed_pkts_cnt + * 188 * 8; + /* convert to 1024 basis */ + speed_bytes = 1000 * div64_u64(speed_bytes, + 1024); + speed_timedelta = + (u64)timespec_to_ns(&delta_time); + speed_timedelta = div64_u64(speed_timedelta, + 1000000); /* nsec -> usec */ + printk(KERN_INFO "TS speed %llu Kbits/sec \n", + div64_u64(speed_bytes, + speed_timedelta)); + }; + + demux->speed_last_time = cur_time; + demux->speed_pkts_cnt = 0; + }; + }; + if (dvb_demux_tscheck) { if (!demux->cnt_storage) demux->cnt_storage = vmalloc(MAX_PID + 1); diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h index 2fe05d03240d..a7d876fd02dd 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.h +++ b/drivers/media/dvb/dvb-core/dvb_demux.h @@ -44,6 +44,8 @@ #define MAX_PID 0x1fff +#define SPEED_PKTS_INTERVAL 50000 + struct dvb_demux_filter { struct dmx_section_filter filter; u8 maskandmode[DMX_MAX_FILTER_SIZE]; @@ -131,6 +133,9 @@ struct dvb_demux { spinlock_t lock; uint8_t *cnt_storage; /* for TS continuity check */ + + struct timespec speed_last_time; /* for TS speed check */ + uint32_t speed_pkts_cnt; /* for TS speed check */ }; int dvb_dmx_init(struct dvb_demux *dvbdemux); diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 98082416aa52..07461222a7f5 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -895,104 +895,27 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe) } static struct dtv_cmds_h dtv_cmds[] = { - [DTV_TUNE] = { - .name = "DTV_TUNE", - .cmd = DTV_TUNE, - .set = 1, - }, - [DTV_CLEAR] = { - .name = "DTV_CLEAR", - .cmd = DTV_CLEAR, - .set = 1, - }, + _DTV_CMD(DTV_TUNE, 1, 0), + _DTV_CMD(DTV_CLEAR, 1, 0), /* Set */ - [DTV_FREQUENCY] = { - .name = "DTV_FREQUENCY", - .cmd = DTV_FREQUENCY, - .set = 1, - }, - [DTV_BANDWIDTH_HZ] = { - .name = "DTV_BANDWIDTH_HZ", - .cmd = DTV_BANDWIDTH_HZ, - .set = 1, - }, - [DTV_MODULATION] = { - .name = "DTV_MODULATION", - .cmd = DTV_MODULATION, - .set = 1, - }, - [DTV_INVERSION] = { - .name = "DTV_INVERSION", - .cmd = DTV_INVERSION, - .set = 1, - }, - [DTV_DISEQC_MASTER] = { - .name = "DTV_DISEQC_MASTER", - .cmd = DTV_DISEQC_MASTER, - .set = 1, - .buffer = 1, - }, - [DTV_SYMBOL_RATE] = { - .name = "DTV_SYMBOL_RATE", - .cmd = DTV_SYMBOL_RATE, - .set = 1, - }, - [DTV_INNER_FEC] = { - .name = "DTV_INNER_FEC", - .cmd = DTV_INNER_FEC, - .set = 1, - }, - [DTV_VOLTAGE] = { - .name = "DTV_VOLTAGE", - .cmd = DTV_VOLTAGE, - .set = 1, - }, - [DTV_TONE] = { - .name = "DTV_TONE", - .cmd = DTV_TONE, - .set = 1, - }, - [DTV_PILOT] = { - .name = "DTV_PILOT", - .cmd = DTV_PILOT, - .set = 1, - }, - [DTV_ROLLOFF] = { - .name = "DTV_ROLLOFF", - .cmd = DTV_ROLLOFF, - .set = 1, - }, - [DTV_DELIVERY_SYSTEM] = { - .name = "DTV_DELIVERY_SYSTEM", - .cmd = DTV_DELIVERY_SYSTEM, - .set = 1, - }, - [DTV_HIERARCHY] = { - .name = "DTV_HIERARCHY", - .cmd = DTV_HIERARCHY, - .set = 1, - }, - [DTV_CODE_RATE_HP] = { - .name = "DTV_CODE_RATE_HP", - .cmd = DTV_CODE_RATE_HP, - .set = 1, - }, - [DTV_CODE_RATE_LP] = { - .name = "DTV_CODE_RATE_LP", - .cmd = DTV_CODE_RATE_LP, - .set = 1, - }, - [DTV_GUARD_INTERVAL] = { - .name = "DTV_GUARD_INTERVAL", - .cmd = DTV_GUARD_INTERVAL, - .set = 1, - }, - [DTV_TRANSMISSION_MODE] = { - .name = "DTV_TRANSMISSION_MODE", - .cmd = DTV_TRANSMISSION_MODE, - .set = 1, - }, + _DTV_CMD(DTV_FREQUENCY, 1, 0), + _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0), + _DTV_CMD(DTV_MODULATION, 1, 0), + _DTV_CMD(DTV_INVERSION, 1, 0), + _DTV_CMD(DTV_DISEQC_MASTER, 1, 1), + _DTV_CMD(DTV_SYMBOL_RATE, 1, 0), + _DTV_CMD(DTV_INNER_FEC, 1, 0), + _DTV_CMD(DTV_VOLTAGE, 1, 0), + _DTV_CMD(DTV_TONE, 1, 0), + _DTV_CMD(DTV_PILOT, 1, 0), + _DTV_CMD(DTV_ROLLOFF, 1, 0), + _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0), + _DTV_CMD(DTV_HIERARCHY, 1, 0), + _DTV_CMD(DTV_CODE_RATE_HP, 1, 0), + _DTV_CMD(DTV_CODE_RATE_LP, 1, 0), + _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0), + _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0), _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0), _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0), @@ -1035,43 +958,13 @@ static struct dtv_cmds_h dtv_cmds[] = { _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0), /* Get */ - [DTV_DISEQC_SLAVE_REPLY] = { - .name = "DTV_DISEQC_SLAVE_REPLY", - .cmd = DTV_DISEQC_SLAVE_REPLY, - .set = 0, - .buffer = 1, - }, - - [DTV_API_VERSION] = { - .name = "DTV_API_VERSION", - .cmd = DTV_API_VERSION, - .set = 0, - }, - [DTV_CODE_RATE_HP] = { - .name = "DTV_CODE_RATE_HP", - .cmd = DTV_CODE_RATE_HP, - .set = 0, - }, - [DTV_CODE_RATE_LP] = { - .name = "DTV_CODE_RATE_LP", - .cmd = DTV_CODE_RATE_LP, - .set = 0, - }, - [DTV_GUARD_INTERVAL] = { - .name = "DTV_GUARD_INTERVAL", - .cmd = DTV_GUARD_INTERVAL, - .set = 0, - }, - [DTV_TRANSMISSION_MODE] = { - .name = "DTV_TRANSMISSION_MODE", - .cmd = DTV_TRANSMISSION_MODE, - .set = 0, - }, - [DTV_HIERARCHY] = { - .name = "DTV_HIERARCHY", - .cmd = DTV_HIERARCHY, - .set = 0, - }, + _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1), + _DTV_CMD(DTV_API_VERSION, 0, 0), + _DTV_CMD(DTV_CODE_RATE_HP, 0, 0), + _DTV_CMD(DTV_CODE_RATE_LP, 0, 0), + _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0), + _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0), + _DTV_CMD(DTV_HIERARCHY, 0, 0), }; static void dtv_property_dump(struct dtv_property *tvp) @@ -1712,7 +1605,18 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; - int err = -EOPNOTSUPP; + int cb_err, err = -EOPNOTSUPP; + + if (fe->dvb->fe_ioctl_override) { + cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg, + DVB_FE_IOCTL_PRE); + if (cb_err < 0) + return cb_err; + if (cb_err > 0) + return 0; + /* fe_ioctl_override returning 0 allows + * dvb-core to continue handling the ioctl */ + } switch (cmd) { case FE_GET_INFO: { @@ -1978,6 +1882,13 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, break; }; + if (fe->dvb->fe_ioctl_override) { + cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg, + DVB_FE_IOCTL_POST); + if (cb_err < 0) + return cb_err; + } + return err; } diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 810f07d63246..52e4ce4304ee 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -160,7 +160,7 @@ struct tuner_state { * search callback possible return status * * DVBFE_ALGO_SEARCH_SUCCESS - * The frontend search algorithm completed and returned succesfully + * The frontend search algorithm completed and returned successfully * * DVBFE_ALGO_SEARCH_ASLEEP * The frontend search algorithm is sleeping diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 0241a7c5c34a..8b8558fcb042 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -1396,7 +1396,7 @@ static int dvb_net_do_ioctl(struct inode *inode, struct file *file, return ret; } - /* binary compatiblity cruft */ + /* binary compatibility cruft */ case __NET_ADD_IF_OLD: { struct __dvb_net_if_old *dvbnetif = parg; diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 01fc70484743..f7b499d4a3c0 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -54,6 +54,8 @@ module_param_array(adapter_nr, short, NULL, 0444); \ MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers") +struct dvb_frontend; + struct dvb_adapter { int num; struct list_head list_head; @@ -69,6 +71,32 @@ struct dvb_adapter { int mfe_shared; /* indicates mutually exclusive frontends */ struct dvb_device *mfe_dvbdev; /* frontend device in use */ struct mutex mfe_lock; /* access lock for thread creation */ + + /* Allow the adapter/bridge driver to perform an action before and/or + * after the core handles an ioctl: + * + * DVB_FE_IOCTL_PRE indicates that the ioctl has not yet been handled. + * DVB_FE_IOCTL_POST indicates that the ioctl has been handled. + * + * When DVB_FE_IOCTL_PRE is passed to the callback as the stage arg: + * + * return 0 to allow dvb-core to handle the ioctl. + * return a positive int to prevent dvb-core from handling the ioctl, + * and exit without error. + * return a negative int to prevent dvb-core from handling the ioctl, + * and return that value as an error. + * + * When DVB_FE_IOCTL_POST is passed to the callback as the stage arg: + * + * return 0 to allow the dvb_frontend ioctl handler to exit normally. + * return a negative int to cause the dvb_frontend ioctl handler to + * return that value as an error. + */ +#define DVB_FE_IOCTL_PRE 0 +#define DVB_FE_IOCTL_POST 1 + int (*fe_ioctl_override)(struct dvb_frontend *fe, + unsigned int cmd, void *parg, + unsigned int stage); }; diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 0e4b97fba384..2dee1bf73577 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -322,3 +322,11 @@ config DVB_USB_FRIIO depends on DVB_USB help Say Y here to support the Japanese DTV receiver Friio. + +config DVB_USB_EC168 + tristate "E3C EC168 DVB-T USB2.0 support" + depends on DVB_USB && EXPERIMENTAL + select DVB_EC100 + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the E3C EC168 DVB-T USB2.0 receiver. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 85b83a43d55d..72c92cb69a22 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -82,6 +82,9 @@ obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o dvb-usb-friio-objs = friio.o friio-fe.o obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o +dvb-usb-ec168-objs = ec168.o +obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index cf042b309b46..8b60a601fb82 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -730,7 +730,7 @@ static int af9015_read_config(struct usb_device *udev) goto error; deb_info("%s: IR mode:%d\n", __func__, val); for (i = 0; i < af9015_properties_count; i++) { - if (val == AF9015_IR_MODE_DISABLED || val == 0x04) { + if (val == AF9015_IR_MODE_DISABLED) { af9015_properties[i].rc_key_map = NULL; af9015_properties[i].rc_key_map_size = 0; } else if (dvb_usb_af9015_remote) { @@ -868,6 +868,16 @@ static int af9015_read_config(struct usb_device *udev) af9015_config.ir_table_size = ARRAY_SIZE(af9015_ir_table_avermedia); break; + case USB_VID_MSI_2: + af9015_properties[i].rc_key_map = + af9015_rc_keys_msi_digivox_iii; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii); + af9015_config.ir_table = + af9015_ir_table_msi_digivox_iii; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_msi_digivox_iii); + break; } } } @@ -1283,6 +1293,8 @@ static struct usb_device_id af9015_usb_table[] = { {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)}, {USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)}, /* 25 */{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1296,7 +1308,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .firmware = "dvb-usb-af9015.fw", .no_reconnect = 1, - .size_of_priv = sizeof(struct af9015_state), \ + .size_of_priv = sizeof(struct af9015_state), .num_adapters = 2, .adapter = { @@ -1402,7 +1414,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .firmware = "dvb-usb-af9015.fw", .no_reconnect = 1, - .size_of_priv = sizeof(struct af9015_state), \ + .size_of_priv = sizeof(struct af9015_state), .num_adapters = 2, .adapter = { @@ -1508,7 +1520,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .firmware = "dvb-usb-af9015.fw", .no_reconnect = 1, - .size_of_priv = sizeof(struct af9015_state), \ + .size_of_priv = sizeof(struct af9015_state), .num_adapters = 2, .adapter = { @@ -1554,7 +1566,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .i2c_algo = &af9015_i2c_algo, - .num_device_descs = 4, /* max 9 */ + .num_device_descs = 6, /* max 9 */ .devices = { { .name = "AverMedia AVerTV Volar GPS 805 (A805)", @@ -1577,6 +1589,17 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = {&af9015_usb_table[24], NULL}, .warm_ids = {NULL}, }, + { + .name = "KWorld PlusTV DVB-T PCI Pro Card " \ + "(DVB-T PC160-T)", + .cold_ids = {&af9015_usb_table[26], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Sveon STV20 Tuner USB DVB-T HDTV", + .cold_ids = {&af9015_usb_table[27], NULL}, + .warm_ids = {NULL}, + }, } }, }; diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index c41f30e4a1b8..931c8515830d 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -95,6 +95,7 @@ enum af9015_ir_mode { AF9015_IR_MODE_HID, AF9015_IR_MODE_RLC, AF9015_IR_MODE_RC6, + AF9015_IR_MODE_POLLING, /* just guess */ }; struct af9015_state { @@ -119,6 +120,7 @@ enum af9015_remote { /* 5 */ AF9015_REMOTE_AVERMEDIA_KS, }; +/* LeadTek - Y04G0051 */ /* Leadtek WinFast DTV Dongle Gold */ static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = { { 0x001e, KEY_1 }, @@ -131,64 +133,96 @@ static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = { { 0x0025, KEY_8 }, { 0x0026, KEY_9 }, { 0x0027, KEY_0 }, - { 0x0028, KEY_ENTER }, - { 0x004f, KEY_VOLUMEUP }, - { 0x0050, KEY_VOLUMEDOWN }, - { 0x0051, KEY_CHANNELDOWN }, - { 0x0052, KEY_CHANNELUP }, + { 0x0028, KEY_OK }, + { 0x004f, KEY_RIGHT }, + { 0x0050, KEY_LEFT }, + { 0x0051, KEY_DOWN }, + { 0x0052, KEY_UP }, + { 0x011a, KEY_POWER2 }, + { 0x04b4, KEY_TV }, + { 0x04b3, KEY_RED }, + { 0x04b2, KEY_GREEN }, + { 0x04b1, KEY_YELLOW }, + { 0x04b0, KEY_BLUE }, + { 0x003d, KEY_TEXT }, + { 0x0113, KEY_SLEEP }, + { 0x0010, KEY_MUTE }, + { 0x0105, KEY_ESC }, + { 0x0009, KEY_SCREEN }, + { 0x010f, KEY_MENU }, + { 0x003f, KEY_CHANNEL }, + { 0x0013, KEY_REWIND }, + { 0x0012, KEY_PLAY }, + { 0x0011, KEY_FASTFORWARD }, + { 0x0005, KEY_PREVIOUS }, + { 0x0029, KEY_STOP }, + { 0x002b, KEY_NEXT }, + { 0x0041, KEY_EPG }, + { 0x0019, KEY_VIDEO }, + { 0x0016, KEY_AUDIO }, + { 0x0037, KEY_DOT }, + { 0x002a, KEY_AGAIN }, + { 0x002c, KEY_CAMERA }, + { 0x003c, KEY_NEW }, + { 0x0115, KEY_RECORD }, + { 0x010b, KEY_TIME }, + { 0x0043, KEY_VOLUMEUP }, + { 0x0042, KEY_VOLUMEDOWN }, + { 0x004b, KEY_CHANNELUP }, + { 0x004e, KEY_CHANNELDOWN }, }; static u8 af9015_ir_table_leadtek[] = { - 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, - 0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, - 0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, - 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, - 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, - 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, - 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, - 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, - 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, - 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, - 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, - 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, - 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, - 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, - 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, - 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, - 0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, - 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, - 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, - 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, - 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, - 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, - 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, - 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, - 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, - 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, - 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, - 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, - 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, - 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, - 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, - 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, - 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, - 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, - 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, - 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, - 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, - 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, - 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, - 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, - 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, - 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, - 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, - 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, - 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, + 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, /* KEY_POWER2 */ + 0x03, 0xfc, 0x56, 0xa9, 0xb4, 0x04, 0x00, /* KEY_TV */ + 0x03, 0xfc, 0x4b, 0xb4, 0xb3, 0x04, 0x00, /* KEY_RED */ + 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, /* KEY_GREEN */ + 0x03, 0xfc, 0x4d, 0xb2, 0xb1, 0x04, 0x00, /* KEY_YELLOW */ + 0x03, 0xfc, 0x4e, 0xb1, 0xb0, 0x04, 0x00, /* KEY_BLUE */ + 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, /* KEY_TEXT */ + 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, /* KEY_SLEEP */ + 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, /* KEY_MUTE */ + 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, /* KEY_ESC */ + 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, /* KEY_STOP (1)*/ + 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, /* KEY_UP */ + 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, /* KEY_SCREEN */ + 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, /* KEY_LEFT */ + 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, /* KEY_OK (1) */ + 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, /* KEY_RIGHT */ + 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, /* KEY_MENU */ + 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, /* KEY_DOWN */ + 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, /* KEY_CHANNEL */ + 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, /* KEY_REWIND */ + 0x03, 0xfc, 0x43, 0xbc, 0x12, 0x00, 0x00, /* KEY_PLAY */ + 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, /* KEY_FASTFORWARD */ + 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, /* KEY_VIDEO (1) */ + 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, /* KEY_PREVIOUS */ + 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, /* KEY_STOP (2) */ + 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, /* KEY_NEXT */ + 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, /* KEY_EPG */ + 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, /* KEY_1 */ + 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, /* KEY_2 */ + 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, /* KEY_3 */ + 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, /* KEY_VIDEO (2) */ + 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, /* KEY_4 */ + 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, /* KEY_5 */ + 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, /* KEY_6 */ + 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, /* KEY_AUDIO */ + 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, /* KEY_7 */ + 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, /* KEY_8 */ + 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, /* KEY_9 */ + 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* KEY_OK (2) */ + 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, /* KEY_DOT */ + 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, /* KEY_0 */ + 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, /* KEY_AGAIN */ + 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, /* KEY_CAMERA */ + 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, /* KEY_NEW */ + 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, /* KEY_RECORD */ + 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, /* KEY_TIME */ + 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, /* KEY_VOLUMEUP */ + 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, /* KEY_VOLUMEDOWN */ + 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, /* KEY_CHANNELUP */ + 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, /* KEY_CHANNELDOWN */ }; /* TwinHan AzureWave AD-TU700(704J) */ @@ -746,4 +780,75 @@ static u8 af9015_ir_table_trekstor[] = { 0x00, 0xff, 0x84, 0x7b, 0x27, 0x07, 0x00, }; +/* MSI DIGIVOX mini III */ +static struct dvb_usb_rc_key af9015_rc_keys_msi_digivox_iii[] = { + { 0x0713, KEY_POWER }, /* [red power button] */ + { 0x073b, KEY_VIDEO }, /* Source */ + { 0x073e, KEY_ZOOM }, /* Zoom */ + { 0x070b, KEY_POWER2 }, /* ShutDown */ + { 0x071e, KEY_1 }, + { 0x071f, KEY_2 }, + { 0x0720, KEY_3 }, + { 0x0721, KEY_4 }, + { 0x0722, KEY_5 }, + { 0x0723, KEY_6 }, + { 0x0724, KEY_7 }, + { 0x0725, KEY_8 }, + { 0x0726, KEY_9 }, + { 0x0727, KEY_0 }, + { 0x0752, KEY_CHANNELUP }, /* CH+ */ + { 0x0751, KEY_CHANNELDOWN }, /* CH- */ + { 0x0750, KEY_VOLUMEUP }, /* Vol+ */ + { 0x074f, KEY_VOLUMEDOWN }, /* Vol- */ + { 0x0705, KEY_ESC }, /* [back up arrow] */ + { 0x0708, KEY_OK }, /* [enter arrow] */ + { 0x073f, KEY_RECORD }, /* Rec */ + { 0x0716, KEY_STOP }, /* Stop */ + { 0x072a, KEY_PLAY }, /* Play */ + { 0x073c, KEY_MUTE }, /* Mute */ + { 0x0718, KEY_UP }, + { 0x0707, KEY_DOWN }, + { 0x070f, KEY_LEFT }, + { 0x0715, KEY_RIGHT }, + { 0x0736, KEY_RED }, + { 0x0737, KEY_GREEN }, + { 0x072d, KEY_YELLOW }, + { 0x072e, KEY_BLUE }, +}; + +static u8 af9015_ir_table_msi_digivox_iii[] = { + 0x61, 0xd6, 0x43, 0xbc, 0x13, 0x07, 0x00, /* KEY_POWER */ + 0x61, 0xd6, 0x01, 0xfe, 0x3b, 0x07, 0x00, /* KEY_VIDEO */ + 0x61, 0xd6, 0x0b, 0xf4, 0x3e, 0x07, 0x00, /* KEY_ZOOM */ + 0x61, 0xd6, 0x03, 0xfc, 0x0b, 0x07, 0x00, /* KEY_POWER2 */ + 0x61, 0xd6, 0x04, 0xfb, 0x1e, 0x07, 0x00, /* KEY_1 */ + 0x61, 0xd6, 0x08, 0xf7, 0x1f, 0x07, 0x00, /* KEY_2 */ + 0x61, 0xd6, 0x02, 0xfd, 0x20, 0x07, 0x00, /* KEY_3 */ + 0x61, 0xd6, 0x0f, 0xf0, 0x21, 0x07, 0x00, /* KEY_4 */ + 0x61, 0xd6, 0x05, 0xfa, 0x22, 0x07, 0x00, /* KEY_5 */ + 0x61, 0xd6, 0x06, 0xf9, 0x23, 0x07, 0x00, /* KEY_6 */ + 0x61, 0xd6, 0x0c, 0xf3, 0x24, 0x07, 0x00, /* KEY_7 */ + 0x61, 0xd6, 0x0d, 0xf2, 0x25, 0x07, 0x00, /* KEY_8 */ + 0x61, 0xd6, 0x0a, 0xf5, 0x26, 0x07, 0x00, /* KEY_9 */ + 0x61, 0xd6, 0x11, 0xee, 0x27, 0x07, 0x00, /* KEY_0 */ + 0x61, 0xd6, 0x09, 0xf6, 0x52, 0x07, 0x00, /* KEY_CHANNELUP */ + 0x61, 0xd6, 0x07, 0xf8, 0x51, 0x07, 0x00, /* KEY_CHANNELDOWN */ + 0x61, 0xd6, 0x0e, 0xf1, 0x50, 0x07, 0x00, /* KEY_VOLUMEUP */ + 0x61, 0xd6, 0x13, 0xec, 0x4f, 0x07, 0x00, /* KEY_VOLUMEDOWN */ + 0x61, 0xd6, 0x10, 0xef, 0x05, 0x07, 0x00, /* KEY_ESC */ + 0x61, 0xd6, 0x12, 0xed, 0x08, 0x07, 0x00, /* KEY_OK */ + 0x61, 0xd6, 0x14, 0xeb, 0x3f, 0x07, 0x00, /* KEY_RECORD */ + 0x61, 0xd6, 0x15, 0xea, 0x16, 0x07, 0x00, /* KEY_STOP */ + 0x61, 0xd6, 0x16, 0xe9, 0x2a, 0x07, 0x00, /* KEY_PLAY */ + 0x61, 0xd6, 0x17, 0xe8, 0x3c, 0x07, 0x00, /* KEY_MUTE */ + 0x61, 0xd6, 0x18, 0xe7, 0x18, 0x07, 0x00, /* KEY_UP */ + 0x61, 0xd6, 0x19, 0xe6, 0x07, 0x07, 0x00, /* KEY_DOWN */ + 0x61, 0xd6, 0x1a, 0xe5, 0x0f, 0x07, 0x00, /* KEY_LEFT */ + 0x61, 0xd6, 0x1b, 0xe4, 0x15, 0x07, 0x00, /* KEY_RIGHT */ + 0x61, 0xd6, 0x1c, 0xe3, 0x36, 0x07, 0x00, /* KEY_RED */ + 0x61, 0xd6, 0x1d, 0xe2, 0x37, 0x07, 0x00, /* KEY_GREEN */ + 0x61, 0xd6, 0x1e, 0xe1, 0x2d, 0x07, 0x00, /* KEY_YELLOW */ + 0x61, 0xd6, 0x1f, 0xe0, 0x2e, 0x07, 0x00, /* KEY_BLUE */ +}; + #endif diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index 2ae7f648effe..bb69f3719f9a 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -344,7 +344,7 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) if (ret) return ret; - err("Unkown Anysee version: %02x %02x %02x. "\ + err("Unknown Anysee version: %02x %02x %02x. "\ "Please report the <linux-dvb@linuxtv.org>.", hw_info[0], hw_info[1], hw_info[2]); diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 2a53dd096eef..05fb28e9c69e 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -36,9 +36,11 @@ #include "tuner-xc2028.h" #include "tuner-simple.h" #include "mxl5005s.h" +#include "max2165.h" #include "dib7000p.h" #include "dib0070.h" #include "lgs8gxx.h" +#include "atbm8830.h" /* debug */ static int dvb_usb_cxusb_debug; @@ -714,6 +716,11 @@ static struct mxl5005s_config d680_dmb_tuner = { .AgcMasterByte = 0x00, }; +static struct max2165_config mygica_d689_max2165_cfg = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + /* Callbacks for DVB USB */ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { @@ -813,6 +820,14 @@ static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) return (fe == NULL) ? -EIO : 0; } +static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe; + fe = dvb_attach(max2165_attach, adap->fe, + &adap->dev->i2c_adap, &mygica_d689_max2165_cfg); + return (fe == NULL) ? -EIO : 0; +} + static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) { u8 b; @@ -1160,6 +1175,55 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) return 0; } +static struct atbm8830_config mygica_d689_atbm8830_cfg = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x40, + .serial_ts = 0, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, +}; + +static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + + /* Select required USB configuration */ + if (usb_set_interface(d->udev, 0, 0) < 0) + err("set interface failed"); + + /* Unblock all USB pipes */ + usb_clear_halt(d->udev, + usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint)); + + + /* Reset the tuner */ + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { + err("clear tuner gpio failed"); + return -EIO; + } + msleep(100); + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) { + err("set tuner gpio failed"); + return -EIO; + } + msleep(100); + + /* Attach frontend */ + adap->fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg, + &d->i2c_adap); + if (adap->fe == NULL) + return -EIO; + + return 0; +} + /* * DViCO has shipped two devices with the same USB ID, but only one of them * needs a firmware download. Check the device class details to see if they @@ -1240,6 +1304,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties; static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties; static struct dvb_usb_device_properties cxusb_aver_a868r_properties; static struct dvb_usb_device_properties cxusb_d680_dmb_properties; +static struct dvb_usb_device_properties cxusb_mygica_d689_properties; static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1268,6 +1333,8 @@ static int cxusb_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, + THIS_MODULE, NULL, adapter_nr) || 0) return 0; @@ -1294,6 +1361,7 @@ static struct usb_device_id cxusb_table [] = { { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) }, { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) }, { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) }, + { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -1837,6 +1905,55 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { } }; +static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, + .frontend_attach = cxusb_mygica_d689_frontend_attach, + .tuner_attach = cxusb_mygica_d689_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }, + }, + + .power_ctrl = cxusb_d680_dmb_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc_interval = 100, + .rc_key_map = d680_dmb_rc_keys, + .rc_key_map_size = ARRAY_SIZE(d680_dmb_rc_keys), + .rc_query = cxusb_d680_dmb_rc_query, + + .num_device_descs = 1, + .devices = { + { + "Mygica D689 DMB-TH", + { NULL }, + { &cxusb_table[19], NULL }, + }, + } +}; + static struct usb_driver cxusb_driver = { .name = "dvb_usb_cxusb", .probe = cxusb_probe, diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 6bd8951ea02b..684146f98eb7 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -558,8 +558,7 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event, struct dib0700_rc_response { u8 report_id; u8 data_state; - u8 system_msb; - u8 system_lsb; + u16 system; u8 data; u8 not_data; }; @@ -589,37 +588,50 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event, return 0; } - if (actlen != sizeof(buf)) { - /* We didn't get back the 6 byte message we expected */ - err("Unexpected RC response size [%d]", actlen); - return -1; - } - poll_reply.report_id = buf[0]; - poll_reply.data_state = buf[1]; - poll_reply.system_msb = buf[2]; - poll_reply.system_lsb = buf[3]; - poll_reply.data = buf[4]; - poll_reply.not_data = buf[5]; + switch (dvb_usb_dib0700_ir_proto) { + case 0: + poll_reply.report_id = 0; + poll_reply.data_state = 1; + poll_reply.system = buf[2]; + poll_reply.data = buf[4]; + poll_reply.not_data = buf[5]; + + /* NEC protocol sends repeat code as 0 0 0 FF */ + if ((poll_reply.system == 0x00) && (poll_reply.data == 0x00) + && (poll_reply.not_data == 0xff)) { + poll_reply.data_state = 2; + break; + } + break; + default: + if (actlen != sizeof(buf)) { + /* We didn't get back the 6 byte message we expected */ + err("Unexpected RC response size [%d]", actlen); + return -1; + } + + poll_reply.report_id = buf[0]; + poll_reply.data_state = buf[1]; + poll_reply.system = (buf[2] << 8) | buf[3]; + poll_reply.data = buf[4]; + poll_reply.not_data = buf[5]; - /* - info("rid=%02x ds=%02x sm=%02x sl=%02x d=%02x nd=%02x\n", - poll_reply.report_id, poll_reply.data_state, - poll_reply.system_msb, poll_reply.system_lsb, - poll_reply.data, poll_reply.not_data); - */ + break; + } if ((poll_reply.data + poll_reply.not_data) != 0xff) { /* Key failed integrity check */ - err("key failed integrity check: %02x %02x %02x %02x", - poll_reply.system_msb, poll_reply.system_lsb, + err("key failed integrity check: %04x %02x %02x", + poll_reply.system, poll_reply.data, poll_reply.not_data); return -1; } + /* Find the key in the map */ for (i = 0; i < d->props.rc_key_map_size; i++) { - if (rc5_custom(&keymap[i]) == poll_reply.system_lsb && + if (rc5_custom(&keymap[i]) == (poll_reply.system & 0xff) && rc5_data(&keymap[i]) == poll_reply.data) { *event = keymap[i].event; found = 1; @@ -628,8 +640,8 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event, } if (found == 0) { - err("Unknown remote controller key: %02x %02x %02x %02x", - poll_reply.system_msb, poll_reply.system_lsb, + err("Unknown remote controller key: %04x %02x %02x", + poll_reply.system, poll_reply.data, poll_reply.not_data); d->last_event = 0; return 0; @@ -874,6 +886,49 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = { { 0x1d37, KEY_RECORD }, { 0x1d3b, KEY_GOTO }, { 0x1d3d, KEY_POWER }, + + /* Key codes for the Pixelview SBTVD remote (proto NEC) */ + { 0x8613, KEY_MUTE }, + { 0x8612, KEY_POWER }, + { 0x8601, KEY_1 }, + { 0x8602, KEY_2 }, + { 0x8603, KEY_3 }, + { 0x8604, KEY_4 }, + { 0x8605, KEY_5 }, + { 0x8606, KEY_6 }, + { 0x8607, KEY_7 }, + { 0x8608, KEY_8 }, + { 0x8609, KEY_9 }, + { 0x8600, KEY_0 }, + { 0x860d, KEY_CHANNELUP }, + { 0x8619, KEY_CHANNELDOWN }, + { 0x8610, KEY_VOLUMEUP }, + { 0x860c, KEY_VOLUMEDOWN }, + + { 0x860a, KEY_CAMERA }, + { 0x860b, KEY_ZOOM }, + { 0x861b, KEY_BACKSPACE }, + { 0x8615, KEY_ENTER }, + + { 0x861d, KEY_UP }, + { 0x861e, KEY_DOWN }, + { 0x860e, KEY_LEFT }, + { 0x860f, KEY_RIGHT }, + + { 0x8618, KEY_RECORD }, + { 0x861a, KEY_STOP }, + + /* Key codes for the EvolutePC TVWay+ remote (proto NEC) */ + { 0x7a00, KEY_MENU }, + { 0x7a01, KEY_RECORD }, + { 0x7a02, KEY_PLAY }, + { 0x7a03, KEY_STOP }, + { 0x7a10, KEY_CHANNELUP }, + { 0x7a11, KEY_CHANNELDOWN }, + { 0x7a12, KEY_VOLUMEUP }, + { 0x7a13, KEY_VOLUMEDOWN }, + { 0x7a40, KEY_POWER }, + { 0x7a41, KEY_MUTE }, }; /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ @@ -1133,6 +1188,7 @@ static struct dib0070_config dib7770p_dib0070_config = { .clock_khz = 12000, .clock_pad_drive = 0, .flip_chip = 1, + .charge_pump = 2, }; static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) @@ -1209,6 +1265,16 @@ static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +{ + return dib7000p_pid_filter(adapter->fe, index, pid, onoff); +} + +static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +{ + return dib7000p_pid_filter_ctrl(adapter->fe, onoff); +} + static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { 60000, 15000, // internal, sampling 1, 20, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass @@ -1500,6 +1566,15 @@ static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int stk807x_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +{ + return dib8000_pid_filter(adapter->fe, index, pid, onoff); +} + +static int stk807x_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +{ + return dib8000_pid_filter_ctrl(adapter->fe, onoff); +} /* STK807x */ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) @@ -1861,6 +1936,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) }, { USB_DEVICE(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD) }, + { USB_DEVICE(USB_VID_EVOLUTEPC, USB_PID_TVWAY_PLUS) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1895,6 +1971,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700p_frontend_attach, .tuner_attach = stk7700p_tuner_attach, @@ -1976,11 +2056,19 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700d_frontend_attach, .tuner_attach = stk7700d_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }, { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700d_frontend_attach, .tuner_attach = stk7700d_tuner_attach, @@ -2023,6 +2111,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700P2_frontend_attach, .tuner_attach = stk7700d_tuner_attach, @@ -2055,6 +2147,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070p_frontend_attach, .tuner_attach = dib7070p_tuner_attach, @@ -2122,6 +2218,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070p_frontend_attach, .tuner_attach = dib7070p_tuner_attach, @@ -2157,6 +2257,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070pd_frontend_attach0, .tuner_attach = dib7070p_tuner_attach, @@ -2164,6 +2268,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .size_of_priv = sizeof(struct dib0700_adapter_state), }, { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070pd_frontend_attach1, .tuner_attach = dib7070p_tuner_attach, @@ -2210,6 +2318,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700ph_frontend_attach, .tuner_attach = stk7700ph_tuner_attach, @@ -2322,6 +2434,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070p_frontend_attach, .tuner_attach = dib7770p_tuner_attach, @@ -2353,6 +2469,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk807x_pid_filter, + .pid_filter_ctrl = stk807x_pid_filter_ctrl, .frontend_attach = stk807x_frontend_attach, .tuner_attach = dib807x_tuner_attach, @@ -2363,7 +2483,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 2, + .num_device_descs = 3, .devices = { { "DiBcom STK807xP reference design", { &dib0700_usb_id_table[62], NULL }, @@ -2373,6 +2493,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[63], NULL }, { NULL }, }, + { "EvolutePC TVWay+", + { &dib0700_usb_id_table[64], NULL }, + { NULL }, + }, }, .rc_interval = DEFAULT_RC_INTERVAL, @@ -2384,6 +2508,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk807x_pid_filter, + .pid_filter_ctrl = stk807x_pid_filter_ctrl, .frontend_attach = stk807xpvr_frontend_attach0, .tuner_attach = dib807x_tuner_attach, @@ -2393,6 +2521,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { sizeof(struct dib0700_adapter_state), }, { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk807x_pid_filter, + .pid_filter_ctrl = stk807x_pid_filter_ctrl, .frontend_attach = stk807xpvr_frontend_attach1, .tuner_attach = dib807x_tuner_attach, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index eeef50bff4f9..5c0126dc1ff9 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -242,7 +242,7 @@ static struct dvb_usb_device_properties dibusb1_1_properties = { { &dibusb_dib3000mb_table[9], &dibusb_dib3000mb_table[11], NULL }, { &dibusb_dib3000mb_table[10], &dibusb_dib3000mb_table[12], NULL }, }, - { "Unkown USB1.1 DVB-T device ???? please report the name to the author", + { "Unknown USB1.1 DVB-T device ???? please report the name to the author", { &dibusb_dib3000mb_table[13], NULL }, { &dibusb_dib3000mb_table[14], NULL }, }, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c index 8a7d87bcd1d9..df1ec3e69f4a 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c @@ -88,6 +88,7 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums) goto err; } adap->dvb_adap.priv = adap; + adap->dvb_adap.fe_ioctl_override = adap->props.fe_ioctl_override; if (adap->dev->props.read_mac_address) { if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index a548c14c1944..f1602d4ace6d 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -27,6 +27,7 @@ #define USB_VID_DIBCOM 0x10b8 #define USB_VID_DPOSH 0x1498 #define USB_VID_DVICO 0x0fe9 +#define USB_VID_E3C 0x18b4 #define USB_VID_ELGATO 0x0fd9 #define USB_VID_EMPIA 0xeb1a #define USB_VID_GENPIX 0x09c0 @@ -61,6 +62,7 @@ #define USB_VID_XTENSIONS 0x1ae7 #define USB_VID_HUMAX_COEX 0x10b9 #define USB_VID_774 0x7a69 +#define USB_VID_EVOLUTEPC 0x1e59 /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 @@ -103,6 +105,11 @@ #define USB_PID_DIBCOM_STK7770P 0x1e80 #define USB_PID_DPOSH_M9206_COLD 0x9206 #define USB_PID_DPOSH_M9206_WARM 0xa090 +#define USB_PID_E3C_EC168 0x1689 +#define USB_PID_E3C_EC168_2 0xfffa +#define USB_PID_E3C_EC168_3 0xfffb +#define USB_PID_E3C_EC168_4 0x1001 +#define USB_PID_E3C_EC168_5 0x1002 #define USB_PID_UNIWILL_STK7700P 0x6003 #define USB_PID_GENIUS_TVGO_DVB_T03 0x4012 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 @@ -115,6 +122,7 @@ #define USB_PID_KWORLD_395U_3 0xe395 #define USB_PID_KWORLD_MC810 0xc810 #define USB_PID_KWORLD_PC160_2T 0xc160 +#define USB_PID_KWORLD_PC160_T 0xc161 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 @@ -271,10 +279,13 @@ #define USB_PID_TELESTAR_STARSTICK_2 0x8000 #define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807 #define USB_PID_SONY_PLAYTV 0x0003 +#define USB_PID_MYGICA_D689 0xd811 #define USB_PID_ELGATO_EYETV_DTT 0x0021 #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 #define USB_PID_FRIIO_WHITE 0x0001 +#define USB_PID_TVWAY_PLUS 0x0002 +#define USB_PID_SVEON_STV20 0xe39d #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index edde87c6aa3a..6b5ded9e7d5d 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -259,7 +259,7 @@ int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d, *state = REMOTE_KEY_REPEAT; break; default: - deb_err("unkown type of remote status: %d\n",keybuf[0]); + deb_err("unknown type of remote status: %d\n",keybuf[0]); break; } return 0; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index fe2b87efb3f1..0143aef19ecd 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -162,6 +162,9 @@ struct dvb_usb_adapter_properties { struct usb_data_stream_properties stream; int size_of_priv; + + int (*fe_ioctl_override) (struct dvb_frontend *, + unsigned int, void *, unsigned int); }; /** diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c new file mode 100644 index 000000000000..52f5d4f0f230 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/ec168.c @@ -0,0 +1,440 @@ +/* + * E3C EC168 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ec168.h" +#include "ec100.h" +#include "mxl5005s.h" + +/* debug */ +static int dvb_usb_ec168_debug; +module_param_named(debug, dvb_usb_ec168_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static struct ec100_config ec168_ec100_config; + +static int ec168_rw_udev(struct usb_device *udev, struct ec168_req *req) +{ + int ret; + unsigned int pipe; + u8 request, requesttype; + u8 buf[req->size]; + + switch (req->cmd) { + case DOWNLOAD_FIRMWARE: + case GPIO: + case WRITE_I2C: + case STREAMING_CTRL: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = req->cmd; + break; + case READ_I2C: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = req->cmd; + break; + case GET_CONFIG: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = CONFIG; + break; + case SET_CONFIG: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = CONFIG; + break; + case WRITE_DEMOD: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = DEMOD_RW; + break; + case READ_DEMOD: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = DEMOD_RW; + break; + default: + err("unknown command:%02x", req->cmd); + ret = -EPERM; + goto error; + } + + if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) { + /* write */ + memcpy(buf, req->data, req->size); + pipe = usb_sndctrlpipe(udev, 0); + } else { + /* read */ + pipe = usb_rcvctrlpipe(udev, 0); + } + + msleep(1); /* avoid I2C errors */ + + ret = usb_control_msg(udev, pipe, request, requesttype, req->value, + req->index, buf, sizeof(buf), EC168_USB_TIMEOUT); + + ec168_debug_dump(request, requesttype, req->value, req->index, buf, + req->size, deb_xfer); + + if (ret < 0) + goto error; + else + ret = 0; + + /* read request, copy returned data to return buf */ + if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) + memcpy(req->data, buf, req->size); + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req) +{ + return ec168_rw_udev(d->udev, req); +} + +/* I2C */ +static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct ec168_req req; + int i = 0; + int ret; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + if (msg[i].addr == ec168_ec100_config.demod_address) { + req.cmd = READ_DEMOD; + req.value = 0; + req.index = 0xff00 + msg[i].buf[0]; /* reg */ + req.size = msg[i+1].len; /* bytes to read */ + req.data = &msg[i+1].buf[0]; + ret = ec168_ctrl_msg(d, &req); + i += 2; + } else { + err("I2C read not implemented"); + ret = -ENOSYS; + i += 2; + } + } else { + if (msg[i].addr == ec168_ec100_config.demod_address) { + req.cmd = WRITE_DEMOD; + req.value = msg[i].buf[1]; /* val */ + req.index = 0xff00 + msg[i].buf[0]; /* reg */ + req.size = 0; + req.data = NULL; + ret = ec168_ctrl_msg(d, &req); + i += 1; + } else { + req.cmd = WRITE_I2C; + req.value = msg[i].buf[0]; /* val */ + req.index = 0x0100 + msg[i].addr; /* I2C addr */ + req.size = msg[i].len-1; + req.data = &msg[i].buf[1]; + ret = ec168_ctrl_msg(d, &req); + i += 1; + } + } + if (ret) + goto error; + + } + ret = i; + +error: + mutex_unlock(&d->i2c_mutex); + return i; +} + + +static u32 ec168_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm ec168_i2c_algo = { + .master_xfer = ec168_i2c_xfer, + .functionality = ec168_i2c_func, +}; + +/* Callbacks for DVB USB */ +static struct ec100_config ec168_ec100_config = { + .demod_address = 0xff, /* not real address, demod is integrated */ +}; + +static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap) +{ + deb_info("%s:\n", __func__); + adap->fe = dvb_attach(ec100_attach, &ec168_ec100_config, + &adap->dev->i2c_adap); + if (adap->fe == NULL) + return -ENODEV; + + return 0; +} + +static struct mxl5005s_config ec168_mxl5003s_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_OFF, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb_info("%s:\n", __func__); + return dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap, + &ec168_mxl5003s_config) == NULL ? -ENODEV : 0; +} + +static int ec168_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL}; + deb_info("%s: onoff:%d\n", __func__, onoff); + if (onoff) + req.index = 0x0102; + return ec168_ctrl_msg(adap->dev, &req); +} + +static int ec168_download_firmware(struct usb_device *udev, + const struct firmware *fw) +{ + int i, len, packets, remainder, ret; + u16 addr = 0x0000; /* firmware start address */ + struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL}; + deb_info("%s:\n", __func__); + + #define FW_PACKET_MAX_DATA 2048 + packets = fw->size / FW_PACKET_MAX_DATA; + remainder = fw->size % FW_PACKET_MAX_DATA; + len = FW_PACKET_MAX_DATA; + for (i = 0; i <= packets; i++) { + if (i == packets) /* set size of the last packet */ + len = remainder; + + req.size = len; + req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA); + req.index = addr; + addr += FW_PACKET_MAX_DATA; + + ret = ec168_rw_udev(udev, &req); + if (ret) { + err("firmware download failed:%d packet:%d", ret, i); + goto error; + } + } + req.size = 0; + + /* set "warm"? */ + req.cmd = SET_CONFIG; + req.value = 0; + req.index = 0x0001; + ret = ec168_rw_udev(udev, &req); + if (ret) + goto error; + + /* really needed - no idea what does */ + req.cmd = GPIO; + req.value = 0; + req.index = 0x0206; + ret = ec168_rw_udev(udev, &req); + if (ret) + goto error; + + /* activate tuner I2C? */ + req.cmd = WRITE_I2C; + req.value = 0; + req.index = 0x00c6; + ret = ec168_rw_udev(udev, &req); + if (ret) + goto error; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec168_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, int *cold) +{ + int ret; + u8 reply; + struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply}; + deb_info("%s:\n", __func__); + + ret = ec168_rw_udev(udev, &req); + if (ret) + goto error; + + deb_info("%s: reply:%02x\n", __func__, reply); + + if (reply == 0x01) + *cold = 0; + else + *cold = 1; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties ec168_properties; + +static int ec168_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + deb_info("%s: interface:%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + ret = dvb_usb_device_init(intf, &ec168_properties, THIS_MODULE, NULL, + adapter_nr); + if (ret) + goto error; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +#define E3C_EC168_1689 0 +#define E3C_EC168_FFFA 1 +#define E3C_EC168_FFFB 2 +#define E3C_EC168_1001 3 +#define E3C_EC168_1002 4 + +static struct usb_device_id ec168_id[] = { + [E3C_EC168_1689] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168)}, + [E3C_EC168_FFFA] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2)}, + [E3C_EC168_FFFB] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3)}, + [E3C_EC168_1001] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4)}, + [E3C_EC168_1002] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5)}, + {} /* terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ec168_id); + +static struct dvb_usb_device_properties ec168_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = ec168_download_firmware, + .firmware = "dvb-usb-ec168.fw", + .no_reconnect = 1, + + .size_of_priv = 0, + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = ec168_streaming_ctrl, + .frontend_attach = ec168_ec100_frontend_attach, + .tuner_attach = ec168_mxl5003s_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = (32*512), + } + } + }, + } + }, + + .identify_state = ec168_identify_state, + + .i2c_algo = &ec168_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "E3C EC168 DVB-T USB2.0 reference design", + .cold_ids = { + &ec168_id[E3C_EC168_1689], + &ec168_id[E3C_EC168_FFFA], + &ec168_id[E3C_EC168_FFFB], + &ec168_id[E3C_EC168_1001], + &ec168_id[E3C_EC168_1002], + NULL}, + .warm_ids = {NULL}, + }, + } +}; + +static struct usb_driver ec168_driver = { + .name = "dvb_usb_ec168", + .probe = ec168_probe, + .disconnect = dvb_usb_device_exit, + .id_table = ec168_id, +}; + +/* module stuff */ +static int __init ec168_module_init(void) +{ + int ret; + deb_info("%s:\n", __func__); + ret = usb_register(&ec168_driver); + if (ret) + err("module init failed:%d", ret); + + return ret; +} + +static void __exit ec168_module_exit(void) +{ + deb_info("%s:\n", __func__); + /* deregister this driver from the USB subsystem */ + usb_deregister(&ec168_driver); +} + +module_init(ec168_module_init); +module_exit(ec168_module_exit); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("E3C EC168 DVB-T USB2.0 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/ec168.h b/drivers/media/dvb/dvb-usb/ec168.h new file mode 100644 index 000000000000..e7e0b831314e --- /dev/null +++ b/drivers/media/dvb/dvb-usb/ec168.h @@ -0,0 +1,73 @@ +/* + * E3C EC168 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef EC168_H +#define EC168_H + +#define DVB_USB_LOG_PREFIX "ec168" +#include "dvb-usb.h" + +#define deb_info(args...) dprintk(dvb_usb_ec168_debug, 0x01, args) +#define deb_rc(args...) dprintk(dvb_usb_ec168_debug, 0x02, args) +#define deb_xfer(args...) dprintk(dvb_usb_ec168_debug, 0x04, args) +#define deb_reg(args...) dprintk(dvb_usb_ec168_debug, 0x08, args) +#define deb_i2c(args...) dprintk(dvb_usb_ec168_debug, 0x10, args) +#define deb_fw(args...) dprintk(dvb_usb_ec168_debug, 0x20, args) + +#define ec168_debug_dump(r, t, v, i, b, l, func) { \ + int loop_; \ + func("%02x %02x %02x %02x %02x %02x %02x %02x", \ + t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \ + if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ + func(" >>> "); \ + else \ + func(" <<< "); \ + for (loop_ = 0; loop_ < l; loop_++) \ + func("%02x ", b[loop_]); \ + func("\n");\ +} + +#define EC168_USB_TIMEOUT 1000 + +struct ec168_req { + u8 cmd; /* [1] */ + u16 value; /* [2|3] */ + u16 index; /* [4|5] */ + u16 size; /* [6|7] */ + u8 *data; +}; + +enum ec168_cmd { + DOWNLOAD_FIRMWARE = 0x00, + CONFIG = 0x01, + DEMOD_RW = 0x03, + GPIO = 0x04, + STREAMING_CTRL = 0x10, + READ_I2C = 0x20, + WRITE_I2C = 0x21, + HID_DOWNLOAD = 0x30, + GET_CONFIG, + SET_CONFIG, + READ_DEMOD, + WRITE_DEMOD, +}; + +#endif diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c index c4dfe25cf60d..9cbbe42ca44b 100644 --- a/drivers/media/dvb/dvb-usb/friio-fe.c +++ b/drivers/media/dvb/dvb-usb/friio-fe.c @@ -232,12 +232,6 @@ static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state) return 0; } -static int jdvbt90502_read_ber(struct dvb_frontend *fe, u32 *ber) -{ - *ber = 0; - return 0; -} - static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { @@ -264,26 +258,26 @@ static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe, return 0; } -static int jdvbt90502_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - *snr = 0x0101; - return 0; -} - -static int jdvbt90502_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) -{ - *ucblocks = 0; - return 0; -} -static int jdvbt90502_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *fs) +/* filter out un-supported properties to notify users */ +static int jdvbt90502_set_property(struct dvb_frontend *fe, + struct dtv_property *tvp) { - fs->min_delay_ms = 500; - fs->step_size = 0; - fs->max_drift = 0; - - return 0; + int r = 0; + + switch (tvp->cmd) { + case DTV_DELIVERY_SYSTEM: + if (tvp->u.data != SYS_ISDBT) + r = -EINVAL; + break; + case DTV_CLEAR: + case DTV_TUNE: + case DTV_FREQUENCY: + break; + default: + r = -EINVAL; + } + return r; } static int jdvbt90502_get_frontend(struct dvb_frontend *fe, @@ -314,6 +308,9 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe, deb_fe("%s: Freq:%d\n", __func__, p->frequency); + /* for recovery from DTV_CLEAN */ + fe->dtv_property_cache.delivery_system = SYS_ISDBT; + ret = jdvbt90502_pll_set_freq(state, p->frequency); if (ret) { deb_fe("%s:ret == %d\n", __func__, ret); @@ -323,12 +320,6 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe, return 0; } -static int jdvbt90502_sleep(struct dvb_frontend *fe) -{ - deb_fe("%s called.\n", __func__); - return 0; -} - /** * (reg, val) commad list to initialize this module. @@ -394,6 +385,7 @@ static int jdvbt90502_init(struct dvb_frontend *fe) if (ret != 1) goto error; } + fe->dtv_property_cache.delivery_system = SYS_ISDBT; msleep(100); return 0; @@ -468,16 +460,13 @@ static struct dvb_frontend_ops jdvbt90502_ops = { .release = jdvbt90502_release, .init = jdvbt90502_init, - .sleep = jdvbt90502_sleep, .write = _jdvbt90502_write, + .set_property = jdvbt90502_set_property, + .set_frontend = jdvbt90502_set_frontend, .get_frontend = jdvbt90502_get_frontend, - .get_tune_settings = jdvbt90502_get_tune_settings, .read_status = jdvbt90502_read_status, - .read_ber = jdvbt90502_read_ber, .read_signal_strength = jdvbt90502_read_signal_strength, - .read_snr = jdvbt90502_read_snr, - .read_ucblocks = jdvbt90502_read_ucblocks, }; diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c index 9da2cc95ca13..f9702e3756b6 100644 --- a/drivers/media/dvb/dvb-usb/usb-urb.c +++ b/drivers/media/dvb/dvb-usb/usb-urb.c @@ -56,7 +56,7 @@ static void usb_urb_complete(struct urb *urb) stream->complete(stream, b, urb->actual_length); break; default: - err("unkown endpoint type in completition handler."); + err("unknown endpoint type in completition handler."); return; } usb_submit_urb(urb,GFP_ATOMIC); @@ -228,7 +228,7 @@ int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properti case USB_ISOC: return usb_isoc_urb_init(stream); default: - err("unkown URB-type for data transfer."); + err("unknown URB-type for data transfer."); return -EINVAL; } } diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig index 69028253e984..4afa29256df1 100644 --- a/drivers/media/dvb/firewire/Kconfig +++ b/drivers/media/dvb/firewire/Kconfig @@ -1,6 +1,6 @@ config DVB_FIREDTV tristate "FireDTV and FloppyDTV" - depends on DVB_CORE && IEEE1394 + depends on DVB_CORE && (FIREWIRE || IEEE1394) help Support for DVB receivers from Digital Everywhere which are connected via IEEE 1394 (FireWire). @@ -13,8 +13,11 @@ config DVB_FIREDTV if DVB_FIREDTV +config DVB_FIREDTV_FIREWIRE + def_bool FIREWIRE = y || (FIREWIRE = m && DVB_FIREDTV = m) + config DVB_FIREDTV_IEEE1394 - def_bool IEEE1394 + def_bool IEEE1394 = y || (IEEE1394 = m && DVB_FIREDTV = m) config DVB_FIREDTV_INPUT def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile index 2034695ba194..da84203d51c6 100644 --- a/drivers/media/dvb/firewire/Makefile +++ b/drivers/media/dvb/firewire/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_DVB_FIREDTV) += firedtv.o firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o +firedtv-$(CONFIG_DVB_FIREDTV_FIREWIRE) += firedtv-fw.o firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c index 2b6eeeab5b25..7c5459c27b75 100644 --- a/drivers/media/dvb/firewire/firedtv-1394.c +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -1,5 +1,5 @@ /* - * FireDTV driver (formerly known as FireSAT) + * FireDTV driver -- ieee1394 I/O backend * * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com> @@ -26,13 +26,16 @@ #include <iso.h> #include <nodemgr.h> +#include <dvb_demux.h> + #include "firedtv.h" static LIST_HEAD(node_list); static DEFINE_SPINLOCK(node_list_lock); -#define FIREWIRE_HEADER_SIZE 4 -#define CIP_HEADER_SIZE 8 +#define CIP_HEADER_SIZE 8 +#define MPEG2_TS_HEADER_SIZE 4 +#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188) static void rawiso_activity_cb(struct hpsb_iso *iso) { @@ -62,20 +65,20 @@ static void rawiso_activity_cb(struct hpsb_iso *iso) buf = dma_region_i(&iso->data_buf, unsigned char, iso->infos[packet].offset + CIP_HEADER_SIZE); count = (iso->infos[packet].len - CIP_HEADER_SIZE) / - (188 + FIREWIRE_HEADER_SIZE); + MPEG2_TS_SOURCE_PACKET_SIZE; /* ignore empty packet */ if (iso->infos[packet].len <= CIP_HEADER_SIZE) continue; while (count--) { - if (buf[FIREWIRE_HEADER_SIZE] == 0x47) + if (buf[MPEG2_TS_HEADER_SIZE] == 0x47) dvb_dmx_swfilter_packets(&fdtv->demux, - &buf[FIREWIRE_HEADER_SIZE], 1); + &buf[MPEG2_TS_HEADER_SIZE], 1); else dev_err(fdtv->device, "skipping invalid packet\n"); - buf += 188 + FIREWIRE_HEADER_SIZE; + buf += MPEG2_TS_SOURCE_PACKET_SIZE; } } out: @@ -87,15 +90,20 @@ static inline struct node_entry *node_of(struct firedtv *fdtv) return container_of(fdtv->device, struct unit_directory, device)->ne; } -static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg) +static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) { - return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data, - (__force quadlet_t)arg); + int ret; + + ret = hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, + (__force quadlet_t *)&data[1], (__force quadlet_t)data[0]); + data[0] = data[1]; + + return ret; } -static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len) +static int node_read(struct firedtv *fdtv, u64 addr, void *data) { - return hpsb_node_read(node_of(fdtv), addr, data, len); + return hpsb_node_read(node_of(fdtv), addr, data, 4); } static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) @@ -212,6 +220,7 @@ static int node_probe(struct device *dev) goto fail; avc_register_remote_control(fdtv); + return 0; fail: spin_lock_irq(&node_list_lock); @@ -220,6 +229,7 @@ fail: fdtv_unregister_rc(fdtv); fail_free: kfree(fdtv); + return err; } @@ -233,10 +243,9 @@ static int node_remove(struct device *dev) list_del(&fdtv->list); spin_unlock_irq(&node_list_lock); - cancel_work_sync(&fdtv->remote_ctrl_work); fdtv_unregister_rc(fdtv); - kfree(fdtv); + return 0; } @@ -252,6 +261,7 @@ static int node_update(struct unit_directory *ud) static struct hpsb_protocol_driver fdtv_driver = { .name = "firedtv", + .id_table = fdtv_id_table, .update = node_update, .driver = { .probe = node_probe, @@ -264,12 +274,11 @@ static struct hpsb_highlevel fdtv_highlevel = { .fcp_request = fcp_request, }; -int __init fdtv_1394_init(struct ieee1394_device_id id_table[]) +int __init fdtv_1394_init(void) { int ret; hpsb_register_highlevel(&fdtv_highlevel); - fdtv_driver.id_table = id_table; ret = hpsb_register_protocol(&fdtv_driver); if (ret) { printk(KERN_ERR "firedtv: failed to register protocol\n"); diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index 485d061319ab..50c42a4b972b 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -1236,14 +1236,14 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) #define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL -static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) +static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data) { int ret; if (mutex_lock_interruptible(&fdtv->avc_mutex)) return -EINTR; - ret = fdtv->backend->read(fdtv, addr, buf, len); + ret = fdtv->backend->read(fdtv, addr, data); if (ret < 0) dev_err(fdtv->device, "CMP: read I/O error\n"); @@ -1251,14 +1251,14 @@ static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) return ret; } -static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg) +static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) { int ret; if (mutex_lock_interruptible(&fdtv->avc_mutex)) return -EINTR; - ret = fdtv->backend->lock(fdtv, addr, data, arg); + ret = fdtv->backend->lock(fdtv, addr, data); if (ret < 0) dev_err(fdtv->device, "CMP: lock I/O error\n"); @@ -1288,25 +1288,25 @@ static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) { - __be32 old_opcr, opcr; + __be32 old_opcr, opcr[2]; u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); int attempts = 0; int ret; - ret = cmp_read(fdtv, &opcr, opcr_address, 4); + ret = cmp_read(fdtv, opcr_address, opcr); if (ret < 0) return ret; repeat: - if (!get_opcr_online(opcr)) { + if (!get_opcr_online(*opcr)) { dev_err(fdtv->device, "CMP: output offline\n"); return -EBUSY; } - old_opcr = opcr; + old_opcr = *opcr; - if (get_opcr_p2p_connections(opcr)) { - if (get_opcr_channel(opcr) != channel) { + if (get_opcr_p2p_connections(*opcr)) { + if (get_opcr_channel(*opcr) != channel) { dev_err(fdtv->device, "CMP: cannot change channel\n"); return -EBUSY; } @@ -1314,11 +1314,11 @@ repeat: /* We don't allocate isochronous resources. */ } else { - set_opcr_channel(&opcr, channel); - set_opcr_data_rate(&opcr, 2); /* S400 */ + set_opcr_channel(opcr, channel); + set_opcr_data_rate(opcr, 2); /* S400 */ /* FIXME: this is for the worst case - optimize */ - set_opcr_overhead_id(&opcr, 0); + set_opcr_overhead_id(opcr, 0); /* * FIXME: allocate isochronous channel and bandwidth at IRM @@ -1326,13 +1326,16 @@ repeat: */ } - set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); + set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1); - ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr); + opcr[1] = *opcr; + opcr[0] = old_opcr; + + ret = cmp_lock(fdtv, opcr_address, opcr); if (ret < 0) return ret; - if (old_opcr != opcr) { + if (old_opcr != *opcr) { /* * FIXME: if old_opcr.P2P_Connections > 0, * deallocate isochronous channel and bandwidth at IRM @@ -1350,27 +1353,30 @@ repeat: void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) { - __be32 old_opcr, opcr; + __be32 old_opcr, opcr[2]; u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); int attempts = 0; - if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0) + if (cmp_read(fdtv, opcr_address, opcr) < 0) return; repeat: - if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || - get_opcr_channel(opcr) != channel) { + if (!get_opcr_online(*opcr) || !get_opcr_p2p_connections(*opcr) || + get_opcr_channel(*opcr) != channel) { dev_err(fdtv->device, "CMP: no connection to break\n"); return; } - old_opcr = opcr; - set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); + old_opcr = *opcr; + set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) - 1); + + opcr[1] = *opcr; + opcr[0] = old_opcr; - if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr) < 0) + if (cmp_lock(fdtv, opcr_address, opcr) < 0) return; - if (old_opcr != opcr) { + if (old_opcr != *opcr) { /* * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last * owner, deallocate isochronous channel and bandwidth at IRM diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c index 5742fde79d99..fc9996c13e13 100644 --- a/drivers/media/dvb/firewire/firedtv-dvb.c +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -297,7 +297,7 @@ struct firedtv *fdtv_alloc(struct device *dev, #define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d #define AVC_SW_VERSION_ENTRY 0x010001 -static struct ieee1394_device_id fdtv_id_table[] = { +const struct ieee1394_device_id fdtv_id_table[] = { { /* FloppyDTV S/CI and FloppyDTV S2 */ .match_flags = MATCH_FLAGS, @@ -346,12 +346,23 @@ MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); static int __init fdtv_init(void) { - return fdtv_1394_init(fdtv_id_table); + int ret; + + ret = fdtv_fw_init(); + if (ret < 0) + return ret; + + ret = fdtv_1394_init(); + if (ret < 0) + fdtv_fw_exit(); + + return ret; } static void __exit fdtv_exit(void) { fdtv_1394_exit(); + fdtv_fw_exit(); } module_init(fdtv_init); diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c new file mode 100644 index 000000000000..fe44789ab037 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-fw.c @@ -0,0 +1,376 @@ +/* + * FireDTV driver -- firewire I/O backend + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <asm/page.h> + +#include <dvb_demux.h> + +#include "firedtv.h" + +static LIST_HEAD(node_list); +static DEFINE_SPINLOCK(node_list_lock); + +static inline struct fw_device *device_of(struct firedtv *fdtv) +{ + return fw_device(fdtv->device->parent); +} + +static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len, + int tcode) +{ + struct fw_device *device = device_of(fdtv); + int rcode, generation = device->generation; + + smp_rmb(); /* node_id vs. generation */ + + rcode = fw_run_transaction(device->card, tcode, device->node_id, + generation, device->max_speed, addr, data, len); + + return rcode != RCODE_COMPLETE ? -EIO : 0; +} + +static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) +{ + return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP); +} + +static int node_read(struct firedtv *fdtv, u64 addr, void *data) +{ + return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST); +} + +static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST); +} + +#define ISO_HEADER_SIZE 4 +#define CIP_HEADER_SIZE 8 +#define MPEG2_TS_HEADER_SIZE 4 +#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188) + +#define MAX_PACKET_SIZE 1024 /* 776, rounded up to 2^n */ +#define PACKETS_PER_PAGE (PAGE_SIZE / MAX_PACKET_SIZE) +#define N_PACKETS 64 /* buffer size */ +#define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE) +#define IRQ_INTERVAL 16 + +struct firedtv_receive_context { + struct fw_iso_context *context; + struct fw_iso_buffer buffer; + int interrupt_packet; + int current_packet; + char *pages[N_PAGES]; +}; + +static int queue_iso(struct firedtv_receive_context *ctx, int index) +{ + struct fw_iso_packet p; + + p.payload_length = MAX_PACKET_SIZE; + p.interrupt = !(++ctx->interrupt_packet & (IRQ_INTERVAL - 1)); + p.skip = 0; + p.header_length = ISO_HEADER_SIZE; + + return fw_iso_context_queue(ctx->context, &p, &ctx->buffer, + index * MAX_PACKET_SIZE); +} + +static void handle_iso(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, void *data) +{ + struct firedtv *fdtv = data; + struct firedtv_receive_context *ctx = fdtv->backend_data; + __be32 *h, *h_end; + int length, err, i = ctx->current_packet; + char *p, *p_end; + + for (h = header, h_end = h + header_length / 4; h < h_end; h++) { + length = be32_to_cpup(h) >> 16; + if (unlikely(length > MAX_PACKET_SIZE)) { + dev_err(fdtv->device, "length = %d\n", length); + length = MAX_PACKET_SIZE; + } + + p = ctx->pages[i / PACKETS_PER_PAGE] + + (i % PACKETS_PER_PAGE) * MAX_PACKET_SIZE; + p_end = p + length; + + for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end; + p += MPEG2_TS_SOURCE_PACKET_SIZE) + dvb_dmx_swfilter_packets(&fdtv->demux, p, 1); + + err = queue_iso(ctx, i); + if (unlikely(err)) + dev_err(fdtv->device, "requeue failed\n"); + + i = (i + 1) & (N_PACKETS - 1); + } + ctx->current_packet = i; +} + +static int start_iso(struct firedtv *fdtv) +{ + struct firedtv_receive_context *ctx; + struct fw_device *device = device_of(fdtv); + int i, err; + + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->context = fw_iso_context_create(device->card, + FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel, + device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv); + if (IS_ERR(ctx->context)) { + err = PTR_ERR(ctx->context); + goto fail_free; + } + + err = fw_iso_buffer_init(&ctx->buffer, device->card, + N_PAGES, DMA_FROM_DEVICE); + if (err) + goto fail_context_destroy; + + ctx->interrupt_packet = 0; + ctx->current_packet = 0; + + for (i = 0; i < N_PAGES; i++) + ctx->pages[i] = page_address(ctx->buffer.pages[i]); + + for (i = 0; i < N_PACKETS; i++) { + err = queue_iso(ctx, i); + if (err) + goto fail; + } + + err = fw_iso_context_start(ctx->context, -1, 0, + FW_ISO_CONTEXT_MATCH_ALL_TAGS); + if (err) + goto fail; + + fdtv->backend_data = ctx; + + return 0; +fail: + fw_iso_buffer_destroy(&ctx->buffer, device->card); +fail_context_destroy: + fw_iso_context_destroy(ctx->context); +fail_free: + kfree(ctx); + + return err; +} + +static void stop_iso(struct firedtv *fdtv) +{ + struct firedtv_receive_context *ctx = fdtv->backend_data; + + fw_iso_context_stop(ctx->context); + fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card); + fw_iso_context_destroy(ctx->context); + kfree(ctx); +} + +static const struct firedtv_backend backend = { + .lock = node_lock, + .read = node_read, + .write = node_write, + .start_iso = start_iso, + .stop_iso = stop_iso, +}; + +static void handle_fcp(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, int generation, + int speed, unsigned long long offset, + void *payload, size_t length, void *callback_data) +{ + struct firedtv *f, *fdtv = NULL; + struct fw_device *device; + unsigned long flags; + int su; + + if ((tcode != TCODE_WRITE_QUADLET_REQUEST && + tcode != TCODE_WRITE_BLOCK_REQUEST) || + offset != CSR_REGISTER_BASE + CSR_FCP_RESPONSE || + length == 0 || + (((u8 *)payload)[0] & 0xf0) != 0) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + + su = ((u8 *)payload)[1] & 0x7; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) { + device = device_of(f); + if (device->generation != generation) + continue; + + smp_rmb(); /* node_id vs. generation */ + + if (device->card == card && + device->node_id == source && + (f->subunit == su || (f->subunit == 0 && su == 0x7))) { + fdtv = f; + break; + } + } + spin_unlock_irqrestore(&node_list_lock, flags); + + if (fdtv) { + avc_recv(fdtv, payload, length); + fw_send_response(card, request, RCODE_COMPLETE); + } +} + +static struct fw_address_handler fcp_handler = { + .length = CSR_FCP_END - CSR_FCP_RESPONSE, + .address_callback = handle_fcp, +}; + +static const struct fw_address_region fcp_region = { + .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, + .end = CSR_REGISTER_BASE + CSR_FCP_END, +}; + +/* Adjust the template string if models with longer names appear. */ +#define MAX_MODEL_NAME_LEN ((int)DIV_ROUND_UP(sizeof("FireDTV ????"), 4)) + +static size_t model_name(u32 *directory, __be32 *buffer) +{ + struct fw_csr_iterator ci; + int i, length, key, value, last_key = 0; + u32 *block = NULL; + + fw_csr_iterator_init(&ci, directory); + while (fw_csr_iterator_next(&ci, &key, &value)) { + if (last_key == CSR_MODEL && + key == (CSR_DESCRIPTOR | CSR_LEAF)) + block = ci.p - 1 + value; + last_key = key; + } + + if (block == NULL) + return 0; + + length = min((int)(block[0] >> 16) - 2, MAX_MODEL_NAME_LEN); + if (length <= 0) + return 0; + + /* fast-forward to text string */ + block += 3; + + for (i = 0; i < length; i++) + buffer[i] = cpu_to_be32(block[i]); + + return length * 4; +} + +static int node_probe(struct device *dev) +{ + struct firedtv *fdtv; + __be32 name[MAX_MODEL_NAME_LEN]; + int name_len, err; + + name_len = model_name(fw_unit(dev)->directory, name); + + fdtv = fdtv_alloc(dev, &backend, (char *)name, name_len); + if (!fdtv) + return -ENOMEM; + + err = fdtv_register_rc(fdtv, dev); + if (err) + goto fail_free; + + spin_lock_irq(&node_list_lock); + list_add_tail(&fdtv->list, &node_list); + spin_unlock_irq(&node_list_lock); + + err = avc_identify_subunit(fdtv); + if (err) + goto fail; + + err = fdtv_dvb_register(fdtv); + if (err) + goto fail; + + avc_register_remote_control(fdtv); + + return 0; +fail: + spin_lock_irq(&node_list_lock); + list_del(&fdtv->list); + spin_unlock_irq(&node_list_lock); + fdtv_unregister_rc(fdtv); +fail_free: + kfree(fdtv); + + return err; +} + +static int node_remove(struct device *dev) +{ + struct firedtv *fdtv = dev_get_drvdata(dev); + + fdtv_dvb_unregister(fdtv); + + spin_lock_irq(&node_list_lock); + list_del(&fdtv->list); + spin_unlock_irq(&node_list_lock); + + fdtv_unregister_rc(fdtv); + + kfree(fdtv); + return 0; +} + +static void node_update(struct fw_unit *unit) +{ + struct firedtv *fdtv = dev_get_drvdata(&unit->device); + + if (fdtv->isochannel >= 0) + cmp_establish_pp_connection(fdtv, fdtv->subunit, + fdtv->isochannel); +} + +static struct fw_driver fdtv_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "firedtv", + .bus = &fw_bus_type, + .probe = node_probe, + .remove = node_remove, + }, + .update = node_update, + .id_table = fdtv_id_table, +}; + +int __init fdtv_fw_init(void) +{ + int ret; + + ret = fw_core_add_address_handler(&fcp_handler, &fcp_region); + if (ret < 0) + return ret; + + return driver_register(&fdtv_driver.driver); +} + +void fdtv_fw_exit(void) +{ + driver_unregister(&fdtv_driver.driver); + fw_core_remove_address_handler(&fcp_handler); +} diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/dvb/firewire/firedtv-rc.c index 27bca2e283df..599d66e5843d 100644 --- a/drivers/media/dvb/firewire/firedtv-rc.c +++ b/drivers/media/dvb/firewire/firedtv-rc.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/types.h> +#include <linux/workqueue.h> #include "firedtv.h" @@ -163,6 +164,7 @@ fail: void fdtv_unregister_rc(struct firedtv *fdtv) { + cancel_work_sync(&fdtv->remote_ctrl_work); kfree(fdtv->remote_ctrl_dev->keycode); input_unregister_device(fdtv->remote_ctrl_dev); } diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h index d48530b81e61..35080dbb3c66 100644 --- a/drivers/media/dvb/firewire/firedtv.h +++ b/drivers/media/dvb/firewire/firedtv.h @@ -16,6 +16,7 @@ #include <linux/dvb/dmx.h> #include <linux/dvb/frontend.h> #include <linux/list.h> +#include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/spinlock_types.h> #include <linux/types.h> @@ -72,8 +73,8 @@ struct input_dev; struct firedtv; struct firedtv_backend { - int (*lock)(struct firedtv *fdtv, u64 addr, void *data, __be32 arg); - int (*read)(struct firedtv *fdtv, u64 addr, void *data, size_t len); + int (*lock)(struct firedtv *fdtv, u64 addr, __be32 data[]); + int (*read)(struct firedtv *fdtv, u64 addr, void *data); int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len); int (*start_iso)(struct firedtv *fdtv); void (*stop_iso)(struct firedtv *fdtv); @@ -119,10 +120,10 @@ struct firedtv { /* firedtv-1394.c */ #ifdef CONFIG_DVB_FIREDTV_IEEE1394 -int fdtv_1394_init(struct ieee1394_device_id id_table[]); +int fdtv_1394_init(void); void fdtv_1394_exit(void); #else -static inline int fdtv_1394_init(struct ieee1394_device_id it[]) { return 0; } +static inline int fdtv_1394_init(void) { return 0; } static inline void fdtv_1394_exit(void) {} #endif @@ -163,10 +164,20 @@ struct firedtv *fdtv_alloc(struct device *dev, const struct firedtv_backend *backend, const char *name, size_t name_len); extern const char *fdtv_model_names[]; +extern const struct ieee1394_device_id fdtv_id_table[]; /* firedtv-fe.c */ void fdtv_frontend_init(struct firedtv *fdtv); +/* firedtv-fw.c */ +#ifdef CONFIG_DVB_FIREDTV_FIREWIRE +int fdtv_fw_init(void); +void fdtv_fw_exit(void); +#else +static inline int fdtv_fw_init(void) { return 0; } +static inline void fdtv_fw_exit(void) {} +#endif + /* firedtv-rc.c */ #ifdef CONFIG_DVB_FIREDTV_INPUT int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index d7c4837fa71c..58aac018f109 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -201,6 +201,13 @@ config DVB_SI21XX help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_DS3000 + tristate "Montage Tehnology DS3000 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + comment "DVB-T (terrestrial) frontends" depends on DVB_CORE @@ -342,6 +349,13 @@ config DVB_AF9013 help Say Y when you want to support this frontend. +config DVB_EC100 + tristate "E3C EC100" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + Say Y when you want to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -557,6 +571,13 @@ config DVB_LGS8GXX help A DMB-TH tuner module. Say Y when you want to support this frontend. +config DVB_ATBM8830 + tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DMB-TH tuner module. Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 3523767e7a76..823482535d11 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o +obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o obj-$(CONFIG_DVB_AF9013) += af9013.o obj-$(CONFIG_DVB_CX24116) += cx24116.o @@ -76,3 +77,5 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o obj-$(CONFIG_DVB_STV090x) += stv090x.o obj-$(CONFIG_DVB_STV6110x) += stv6110x.o obj-$(CONFIG_DVB_ISL6423) += isl6423.o +obj-$(CONFIG_DVB_EC100) += ec100.o +obj-$(CONFIG_DVB_DS3000) += ds3000.o diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c new file mode 100644 index 000000000000..59881a5944eb --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830.c @@ -0,0 +1,495 @@ +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <asm/div64.h> +#include "dvb_frontend.h" + +#include "atbm8830.h" +#include "atbm8830_priv.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "atbm8830: " args); \ + } while (0) + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) +{ + int ret = 0; + u8 dev_addr; + u8 buf1[] = { reg >> 8, reg & 0xFF }; + u8 buf2[] = { data }; + struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; + struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; + + dev_addr = priv->config->demod_address; + msg1.addr = dev_addr; + msg2.addr = dev_addr; + + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n", + __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg1, 1); + if (ret != 1) + return -EIO; + + ret = i2c_transfer(priv->i2c, &msg2, 1); + return (ret != 1) ? -EIO : 0; +} + +static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) +{ + int ret; + u8 dev_addr; + + u8 buf1[] = { reg >> 8, reg & 0xFF }; + u8 buf2[] = { 0 }; + struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; + struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; + + dev_addr = priv->config->demod_address; + msg1.addr = dev_addr; + msg2.addr = dev_addr; + + ret = i2c_transfer(priv->i2c, &msg1, 1); + if (ret != 1) { + dprintk(KERN_DEBUG "%s: error reg=0x%04x, ret=%i\n", + __func__, reg, ret); + return -EIO; + } + + ret = i2c_transfer(priv->i2c, &msg2, 1); + if (ret != 1) + return -EIO; + + *p_data = buf2[0]; + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n", + __func__, reg, buf2[0]); + + return 0; +} + +/* Lock register latch so that multi-register read is atomic */ +static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) +{ + return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); +} + +static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) +{ + u32 val; + u64 t; + + /* 0x100000 * freq / 30.4MHz */ + t = (u64)0x100000 * freq; + do_div(t, 30400); + val = t; + + atbm8830_write_reg(priv, REG_OSC_CLK, val); + atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); + atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); + + return 0; +} + +static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) +{ + + u32 fs = priv->config->osc_clk_freq; + u64 t; + u32 val; + u8 dat; + + if (freq != 0) { + /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */ + t = (u64) 2 * 31416 * (freq - fs); + t <<= 22; + do_div(t, fs); + do_div(t, 1000); + val = t; + + atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); + atbm8830_write_reg(priv, REG_IF_FREQ, val); + atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); + atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); + + atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); + dat &= 0xFC; + atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); + } else { + /* Zero IF */ + atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); + + atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); + dat &= 0xFC; + dat |= 0x02; + atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); + + if (priv->config->zif_swap_iq) + atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); + else + atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); + } + + return 0; +} + +static int is_locked(struct atbm_state *priv, u8 *locked) +{ + u8 status; + + atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); + + if (locked != NULL) + *locked = (status == 1); + return 0; +} + + +static int set_static_channel_mode(struct atbm_state *priv) +{ + int i; + + for (i = 0; i < 5; i++) + atbm8830_write_reg(priv, 0x099B + i, 0x08); + + atbm8830_write_reg(priv, 0x095B, 0x7F); + atbm8830_write_reg(priv, 0x09CB, 0x01); + atbm8830_write_reg(priv, 0x09CC, 0x7F); + atbm8830_write_reg(priv, 0x09CD, 0x7F); + atbm8830_write_reg(priv, 0x0E01, 0x20); + + /* For single carrier */ + atbm8830_write_reg(priv, 0x0B03, 0x0A); + atbm8830_write_reg(priv, 0x0935, 0x10); + atbm8830_write_reg(priv, 0x0936, 0x08); + atbm8830_write_reg(priv, 0x093E, 0x08); + atbm8830_write_reg(priv, 0x096E, 0x06); + + /* frame_count_max0 */ + atbm8830_write_reg(priv, 0x0B09, 0x00); + /* frame_count_max1 */ + atbm8830_write_reg(priv, 0x0B0A, 0x08); + + return 0; +} + +static int set_ts_config(struct atbm_state *priv) +{ + const struct atbm8830_config *cfg = priv->config; + + /*Set parallel/serial ts mode*/ + atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); + atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); + /*Set ts sampling edge*/ + atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, + cfg->ts_sampling_edge ? 1 : 0); + /*Set ts clock freerun*/ + atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, + cfg->ts_clk_gated ? 0 : 1); + + return 0; +} + +static int atbm8830_init(struct dvb_frontend *fe) +{ + struct atbm_state *priv = fe->demodulator_priv; + const struct atbm8830_config *cfg = priv->config; + + /*Set oscillator frequency*/ + set_osc_freq(priv, cfg->osc_clk_freq); + + /*Set IF frequency*/ + set_if_freq(priv, cfg->if_freq); + + + /*Set static channel mode*/ + set_static_channel_mode(priv); + + set_ts_config(priv); + /*Turn off DSP reset*/ + atbm8830_write_reg(priv, 0x000A, 0); + + /*SW version test*/ + atbm8830_write_reg(priv, 0x020C, 11); + + /* Run */ + atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); + + return 0; +} + + +static void atbm8830_release(struct dvb_frontend *fe) +{ + struct atbm_state *state = fe->demodulator_priv; + dprintk("%s\n", __func__); + + kfree(state); +} + +static int atbm8830_set_fe(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fe_params) +{ + struct atbm_state *priv = fe->demodulator_priv; + int i; + u8 locked = 0; + dprintk("%s\n", __func__); + + /* set frequency */ + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe, fe_params); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* start auto lock */ + for (i = 0; i < 10; i++) { + mdelay(100); + dprintk("Try %d\n", i); + is_locked(priv, &locked); + if (locked != 0) { + dprintk("ATBM8830 locked!\n"); + break; + } + } + + return 0; +} + +static int atbm8830_get_fe(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fe_params) +{ + dprintk("%s\n", __func__); + + /* TODO: get real readings from device */ + /* inversion status */ + fe_params->inversion = INVERSION_OFF; + + /* bandwidth */ + fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + + fe_params->u.ofdm.code_rate_HP = FEC_AUTO; + fe_params->u.ofdm.code_rate_LP = FEC_AUTO; + + fe_params->u.ofdm.constellation = QAM_AUTO; + + /* transmission mode */ + fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + + /* guard interval */ + fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + + /* hierarchy */ + fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE; + + return 0; +} + +static int atbm8830_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 0; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) +{ + struct atbm_state *priv = fe->demodulator_priv; + u8 locked = 0; + u8 agc_locked = 0; + + dprintk("%s\n", __func__); + *fe_status = 0; + + is_locked(priv, &locked); + if (locked) { + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } + dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); + + atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); + dprintk("AGC Lock: %d\n", agc_locked); + + return 0; +} + +static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct atbm_state *priv = fe->demodulator_priv; + u32 frame_err; + u8 t; + + dprintk("%s\n", __func__); + + atbm8830_reglatch_lock(priv, 1); + + atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); + frame_err = t & 0x7F; + frame_err <<= 8; + atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); + frame_err |= t; + + atbm8830_reglatch_lock(priv, 0); + + *ber = frame_err * 100 / 32767; + + dprintk("%s: ber=0x%x\n", __func__, *ber); + return 0; +} + +static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) +{ + struct atbm_state *priv = fe->demodulator_priv; + u32 pwm; + u8 t; + + dprintk("%s\n", __func__); + atbm8830_reglatch_lock(priv, 1); + + atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); + pwm = t & 0x03; + pwm <<= 8; + atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); + pwm |= t; + + atbm8830_reglatch_lock(priv, 0); + + dprintk("AGC PWM = 0x%02X\n", pwm); + pwm = 0x400 - pwm; + + *signal = pwm * 0x10000 / 0x400; + + return 0; +} + +static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + dprintk("%s\n", __func__); + *snr = 0; + return 0; +} + +static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + dprintk("%s\n", __func__); + *ucblocks = 0; + return 0; +} + +static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct atbm_state *priv = fe->demodulator_priv; + + return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); +} + +static struct dvb_frontend_ops atbm8830_ops = { + .info = { + .name = "AltoBeam ATBM8830/8831 DMB-TH", + .type = FE_OFDM, + .frequency_min = 474000000, + .frequency_max = 858000000, + .frequency_stepsize = 10000, + .caps = + FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = atbm8830_release, + + .init = atbm8830_init, + .sleep = NULL, + .write = NULL, + .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, + + .set_frontend = atbm8830_set_fe, + .get_frontend = atbm8830_get_fe, + .get_tune_settings = atbm8830_get_tune_settings, + + .read_status = atbm8830_read_status, + .read_ber = atbm8830_read_ber, + .read_signal_strength = atbm8830_read_signal_strength, + .read_snr = atbm8830_read_snr, + .read_ucblocks = atbm8830_read_ucblocks, +}; + +struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c) +{ + struct atbm_state *priv = NULL; + u8 data = 0; + + dprintk("%s()\n", __func__); + + if (config == NULL || i2c == NULL) + return NULL; + + priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); + if (priv == NULL) + goto error_out; + + priv->config = config; + priv->i2c = i2c; + + /* check if the demod is there */ + if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { + dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", + __func__, priv->config->demod_address); + goto error_out; + } + dprintk("atbm8830 chip id: 0x%02X\n", data); + + memcpy(&priv->frontend.ops, &atbm8830_ops, + sizeof(struct dvb_frontend_ops)); + priv->frontend.demodulator_priv = priv; + + atbm8830_init(&priv->frontend); + + atbm8830_i2c_gate_ctrl(&priv->frontend, 1); + + return &priv->frontend; + +error_out: + dprintk("%s() error_out\n", __func__); + kfree(priv); + return NULL; + +} +EXPORT_SYMBOL(atbm8830_attach); + +MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); +MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/atbm8830.h b/drivers/media/dvb/frontends/atbm8830.h new file mode 100644 index 000000000000..e8149f393300 --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830.h @@ -0,0 +1,76 @@ +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ATBM8830_H__ +#define __ATBM8830_H__ + +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +#define ATBM8830_PROD_8830 0 +#define ATBM8830_PROD_8831 1 + +struct atbm8830_config { + + /* product type */ + u8 prod; + + /* the demodulator's i2c address */ + u8 demod_address; + + /* parallel or serial transport stream */ + u8 serial_ts; + + /* transport stream clock output only when receving valid stream */ + u8 ts_clk_gated; + + /* Decoder sample TS data at rising edge of clock */ + u8 ts_sampling_edge; + + /* Oscillator clock frequency */ + u32 osc_clk_freq; /* in kHz */ + + /* IF frequency */ + u32 if_freq; /* in kHz */ + + /* Swap I/Q for zero IF */ + u8 zif_swap_iq; + + /* Tuner AGC settings */ + u8 agc_min; + u8 agc_max; + u8 agc_hold_loop; +}; + +#if defined(CONFIG_DVB_ATBM8830) || \ + (defined(CONFIG_DVB_ATBM8830_MODULE) && defined(MODULE)) +extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_ATBM8830 */ + +#endif /* __ATBM8830_H__ */ diff --git a/drivers/media/dvb/frontends/atbm8830_priv.h b/drivers/media/dvb/frontends/atbm8830_priv.h new file mode 100644 index 000000000000..ce960f76092a --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830_priv.h @@ -0,0 +1,75 @@ +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ATBM8830_PRIV_H +#define __ATBM8830_PRIV_H + +struct atbm_state { + struct i2c_adapter *i2c; + /* configuration settings */ + const struct atbm8830_config *config; + struct dvb_frontend frontend; +}; + +#define REG_CHIP_ID 0x0000 +#define REG_TUNER_BASEBAND 0x0001 +#define REG_DEMOD_RUN 0x0004 +#define REG_DSP_RESET 0x0005 +#define REG_RAM_RESET 0x0006 +#define REG_ADC_RESET 0x0007 +#define REG_TSPORT_RESET 0x0008 +#define REG_BLKERR_POL 0x000C +#define REG_I2C_GATE 0x0103 +#define REG_TS_SAMPLE_EDGE 0x0301 +#define REG_TS_PKT_LEN_204 0x0302 +#define REG_TS_PKT_LEN_AUTO 0x0303 +#define REG_TS_SERIAL 0x0305 +#define REG_TS_CLK_FREERUN 0x0306 +#define REG_TS_VALID_MODE 0x0307 +#define REG_TS_CLK_MODE 0x030B /* 1 for serial, 0 for parallel */ + +#define REG_TS_ERRBIT_USE 0x030C +#define REG_LOCK_STATUS 0x030D +#define REG_ADC_CONFIG 0x0602 +#define REG_CARRIER_OFFSET 0x0827 /* 0x0827-0x0829 little endian */ +#define REG_DETECTED_PN_MODE 0x082D +#define REG_READ_LATCH 0x084D +#define REG_IF_FREQ 0x0A00 /* 0x0A00-0x0A02 little endian */ +#define REG_OSC_CLK 0x0A03 /* 0x0A03-0x0A05 little endian */ +#define REG_BYPASS_CCI 0x0A06 +#define REG_ANALOG_LUMA_DETECTED 0x0A25 +#define REG_ANALOG_AUDIO_DETECTED 0x0A26 +#define REG_ANALOG_CHROMA_DETECTED 0x0A39 +#define REG_FRAME_ERR_CNT 0x0B04 +#define REG_USE_EXT_ADC 0x0C00 +#define REG_SWAP_I_Q 0x0C01 +#define REG_TPS_MANUAL 0x0D01 +#define REG_TPS_CONFIG 0x0D02 +#define REG_BYPASS_DEINTERLEAVER 0x0E00 +#define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */ +#define REG_AGC_MIN 0x1020 +#define REG_AGC_MAX 0x1023 +#define REG_AGC_LOCK 0x1027 +#define REG_AGC_PWM_VAL 0x1028 /* 0x1028-0x1029 little endian */ +#define REG_AGC_HOLD_LOOP 0x1031 + +#endif + diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c index 74981ee923c8..2dc2723b724a 100644 --- a/drivers/media/dvb/frontends/au8522_decoder.c +++ b/drivers/media/dvb/frontends/au8522_decoder.c @@ -23,7 +23,6 @@ /* Developer notes: * * VBI support is not yet working - * Saturation and hue setting are not yet working * Enough is implemented here for CVBS and S-Video inputs, but the actual * analog demodulator code isn't implemented (not needed for xc5000 since it * has its own demodulator and outputs CVBS) @@ -236,8 +235,10 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) state->contrast = 0x79; au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80); au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80); + state->saturation = 0x80; au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00); au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00); + state->hue = 0x00; /* Other decoder registers */ au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00); @@ -315,7 +316,7 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 || input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) { /* Despite what the table says, for the HVR-950q we still need - to be in CVBS mode for the S-Video input (reason uknown). */ + to be in CVBS mode for the S-Video input (reason unknown). */ /* filter_coef_type = 3; */ filter_coef_type = 5; } else { @@ -504,7 +505,19 @@ static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ctrl->value); break; case V4L2_CID_SATURATION: + state->saturation = ctrl->value; + au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, + ctrl->value); + au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, + ctrl->value); + break; case V4L2_CID_HUE: + state->hue = ctrl->value; + au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, + ctrl->value >> 8); + au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, + ctrl->value & 0xFF); + break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: @@ -534,7 +547,11 @@ static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ctrl->value = state->contrast; break; case V4L2_CID_SATURATION: + ctrl->value = state->saturation; + break; case V4L2_CID_HUE: + ctrl->value = state->hue; + break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: @@ -632,8 +649,9 @@ static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) case V4L2_CID_BRIGHTNESS: return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_HUE: - /* Not yet implemented */ + return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0); default: break; } diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h index f328f2b3ad3d..c74c4e72fe91 100644 --- a/drivers/media/dvb/frontends/au8522_priv.h +++ b/drivers/media/dvb/frontends/au8522_priv.h @@ -62,6 +62,8 @@ struct au8522_state { u32 rev; u8 brightness; u8 contrast; + u8 saturation; + s16 hue; }; /* These are routines shared by both the VSB/QAM demodulator and the analog diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c index ffbcfabd83f0..00a4e8f03304 100644 --- a/drivers/media/dvb/frontends/cx24110.c +++ b/drivers/media/dvb/frontends/cx24110.c @@ -1,4 +1,4 @@ - /* +/* cx24110 - Single Chip Satellite Channel Receiver driver module Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on @@ -96,7 +96,7 @@ static struct {u8 reg; u8 data;} cx24110_regdata[]= {0x42,0x00}, /* @ middle bytes " */ {0x43,0x00}, /* @ LSB " */ /* leave the carrier tracking loop parameters on default */ - /* leave the bit timing loop parameters at gefault */ + /* leave the bit timing loop parameters at default */ {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */ /* the cx24108 data sheet for symbol rates above 15MS/s */ {0x57,0x00}, /* @ Filter sigma delta enabled, positive */ diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c index 075b2b57cf09..e9ee55592fd3 100644 --- a/drivers/media/dvb/frontends/cx24113.c +++ b/drivers/media/dvb/frontends/cx24113.c @@ -589,7 +589,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, info("detected CX24113 variant\n"); break; case REV_CX24113: - info("sucessfully detected\n"); + info("successfully detected\n"); break; default: err("unsupported device id: %x\n", state->rev); diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c index 136b9d2164d7..ad4c8cfd8090 100644 --- a/drivers/media/dvb/frontends/dib3000mb.c +++ b/drivers/media/dvb/frontends/dib3000mb.c @@ -154,7 +154,7 @@ static int dib3000mb_set_frontend(struct dvb_frontend* fe, case BANDWIDTH_AUTO: return -EOPNOTSUPP; default: - err("unkown bandwidth value."); + err("unknown bandwidth value."); return -EINVAL; } } diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 0781f94e05d2..750ae61a20f4 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -108,7 +108,7 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) outreg = 0; fifo_threshold = 1792; - smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1); + smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); dprintk( "setting output mode for demod %p to %d", &state->demod, mode); @@ -162,18 +162,19 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff) if (state->div_force_off) { dprintk( "diversity combination deactivated - forced by COFDM parameters"); onoff = 0; - } + dib7000p_write_word(state, 207, 0); + } else + dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); + state->div_state = (u8)onoff; if (onoff) { dib7000p_write_word(state, 204, 6); dib7000p_write_word(state, 205, 16); /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ - dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); } else { dib7000p_write_word(state, 204, 1); dib7000p_write_word(state, 205, 0); - dib7000p_write_word(state, 207, 0); } return 0; @@ -1188,7 +1189,7 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat) *stat |= FE_HAS_VITERBI; if (lock & 0x0010) *stat |= FE_HAS_SYNC; - if (lock & 0x0008) + if ((lock & 0x0038) == 0x38) *stat |= FE_HAS_LOCK; return 0; @@ -1302,6 +1303,24 @@ struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum di } EXPORT_SYMBOL(dib7000p_get_i2c_master); +int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 val = dib7000p_read_word(state, 235) & 0xffef; + val |= (onoff & 0x1) << 4; + dprintk("PID filter enabled %d", onoff); + return dib7000p_write_word(state, 235, val); +} +EXPORT_SYMBOL(dib7000p_pid_filter_ctrl); + +int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); + return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0); +} +EXPORT_SYMBOL(dib7000p_pid_filter); + int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) { struct dib7000p_state st = { .i2c_adap = i2c }; @@ -1314,8 +1333,10 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau /* designated i2c address */ new_addr = (0x40 + k) << 1; st.i2c_addr = new_addr; + dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */ if (dib7000p_identify(&st) != 0) { st.i2c_addr = default_addr; + dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */ if (dib7000p_identify(&st) != 0) { dprintk("DiB7000P #%d: not identified\n", k); return -EIO; @@ -1372,6 +1393,8 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, demod->demodulator_priv = st; memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); + dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */ + if (dib7000p_identify(st) != 0) goto error; diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h index 02a4c82f0c70..805dd13a97ee 100644 --- a/drivers/media/dvb/frontends/dib7000p.h +++ b/drivers/media/dvb/frontends/dib7000p.h @@ -51,6 +51,8 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value); extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); +extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); +extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); #else static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, @@ -95,6 +97,17 @@ static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap) printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } +static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} #endif #endif diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 852c790d09d9..898400d331a3 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -954,7 +954,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear u8 guard, crate, constellation, timeI; u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled - const s16 *ncoeff, *ana_fe; + const s16 *ncoeff = NULL, *ana_fe; u16 tmcc_pow = 0; u16 coff_pow = 0x2800; u16 init_prbs = 0xfff; @@ -2121,7 +2121,7 @@ static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) else result -= intlog10(2) * 10 * noise_exp - 100; - *snr = result / (1 << 24); + *snr = result / ((1 << 24) / 10); return 0; } @@ -2195,6 +2195,25 @@ struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000 EXPORT_SYMBOL(dib8000_get_i2c_master); +int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib8000_state *st = fe->demodulator_priv; + u16 val = dib8000_read_word(st, 299) & 0xffef; + val |= (onoff & 0x1) << 4; + + dprintk("pid filter enabled %d", onoff); + return dib8000_write_word(st, 299, val); +} +EXPORT_SYMBOL(dib8000_pid_filter_ctrl); + +int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib8000_state *st = fe->demodulator_priv; + dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); + return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0); +} +EXPORT_SYMBOL(dib8000_pid_filter); + static const struct dvb_frontend_ops dib8000_ops = { .info = { .name = "DiBcom 8000 ISDB-T", diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h index a86de340dd54..8c89482b738a 100644 --- a/drivers/media/dvb/frontends/dib8000.h +++ b/drivers/media/dvb/frontends/dib8000.h @@ -44,6 +44,8 @@ extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value); +extern int dib8000_pid_filter_ctrl(struct dvb_frontend *, u8 onoff); +extern int dib8000_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); #else static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) { @@ -74,6 +76,18 @@ int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } + +int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} #endif #endif diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c index 010075535221..868b78bfae75 100644 --- a/drivers/media/dvb/frontends/drx397xD.c +++ b/drivers/media/dvb/frontends/drx397xD.c @@ -81,7 +81,7 @@ static struct { #include "drx397xD_fw.h" }; -/* use only with writer lock aquired */ +/* use only with writer lock acquired */ static void _drx_release_fw(struct drx397xD_state *s, enum fw_ix ix) { memset(&fw[ix].data[0], 0, sizeof(fw[0].data)); diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c new file mode 100644 index 000000000000..cff3535566fe --- /dev/null +++ b/drivers/media/dvb/frontends/ds3000.c @@ -0,0 +1,1367 @@ +/* + Montage Technology DS3000/TS2020 - DVBS/S2 Demodulator/Tuner driver + Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + + Copyright (C) 2009 TurboSight.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/firmware.h> + +#include "dvb_frontend.h" +#include "ds3000.h" + +static int debug; + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(args); \ + } while (0) + +/* as of March 2009 current DS3000 firmware version is 1.78 */ +/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */ +#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw" + +#define DS3000_SAMPLE_RATE 96000 /* in kHz */ +#define DS3000_XTAL_FREQ 27000 /* in kHz */ + +/* Register values to initialise the demod in DVB-S mode */ +static u8 ds3000_dvbs_init_tab[] = { + 0x23, 0x05, + 0x08, 0x03, + 0x0c, 0x00, + 0x21, 0x54, + 0x25, 0x82, + 0x27, 0x31, + 0x30, 0x08, + 0x31, 0x40, + 0x32, 0x32, + 0x33, 0x35, + 0x35, 0xff, + 0x3a, 0x00, + 0x37, 0x10, + 0x38, 0x10, + 0x39, 0x02, + 0x42, 0x60, + 0x4a, 0x40, + 0x4b, 0x04, + 0x4d, 0x91, + 0x5d, 0xc8, + 0x50, 0x77, + 0x51, 0x77, + 0x52, 0x36, + 0x53, 0x36, + 0x56, 0x01, + 0x63, 0x43, + 0x64, 0x30, + 0x65, 0x40, + 0x68, 0x26, + 0x69, 0x4c, + 0x70, 0x20, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x40, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x60, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x80, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0xa0, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x1f, + 0x76, 0x00, + 0x77, 0xd1, + 0x78, 0x0c, + 0x79, 0x80, + 0x7f, 0x04, + 0x7c, 0x00, + 0x80, 0x86, + 0x81, 0xa6, + 0x85, 0x04, + 0xcd, 0xf4, + 0x90, 0x33, + 0xa0, 0x44, + 0xc0, 0x18, + 0xc3, 0x10, + 0xc4, 0x08, + 0xc5, 0x80, + 0xc6, 0x80, + 0xc7, 0x0a, + 0xc8, 0x1a, + 0xc9, 0x80, + 0xfe, 0x92, + 0xe0, 0xf8, + 0xe6, 0x8b, + 0xd0, 0x40, + 0xf8, 0x20, + 0xfa, 0x0f, + 0xfd, 0x20, + 0xad, 0x20, + 0xae, 0x07, + 0xb8, 0x00, +}; + +/* Register values to initialise the demod in DVB-S2 mode */ +static u8 ds3000_dvbs2_init_tab[] = { + 0x23, 0x0f, + 0x08, 0x07, + 0x0c, 0x00, + 0x21, 0x54, + 0x25, 0x82, + 0x27, 0x31, + 0x30, 0x08, + 0x31, 0x32, + 0x32, 0x32, + 0x33, 0x35, + 0x35, 0xff, + 0x3a, 0x00, + 0x37, 0x10, + 0x38, 0x10, + 0x39, 0x02, + 0x42, 0x60, + 0x4a, 0x80, + 0x4b, 0x04, + 0x4d, 0x81, + 0x5d, 0x88, + 0x50, 0x36, + 0x51, 0x36, + 0x52, 0x36, + 0x53, 0x36, + 0x63, 0x60, + 0x64, 0x10, + 0x65, 0x10, + 0x68, 0x04, + 0x69, 0x29, + 0x70, 0x20, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x40, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x60, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x80, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0xa0, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x1f, + 0xa0, 0x44, + 0xc0, 0x08, + 0xc1, 0x10, + 0xc2, 0x08, + 0xc3, 0x10, + 0xc4, 0x08, + 0xc5, 0xf0, + 0xc6, 0xf0, + 0xc7, 0x0a, + 0xc8, 0x1a, + 0xc9, 0x80, + 0xca, 0x23, + 0xcb, 0x24, + 0xce, 0x74, + 0x90, 0x03, + 0x76, 0x80, + 0x77, 0x42, + 0x78, 0x0a, + 0x79, 0x80, + 0xad, 0x40, + 0xae, 0x07, + 0x7f, 0xd4, + 0x7c, 0x00, + 0x80, 0xa8, + 0x81, 0xda, + 0x7c, 0x01, + 0x80, 0xda, + 0x81, 0xec, + 0x7c, 0x02, + 0x80, 0xca, + 0x81, 0xeb, + 0x7c, 0x03, + 0x80, 0xba, + 0x81, 0xdb, + 0x85, 0x08, + 0x86, 0x00, + 0x87, 0x02, + 0x89, 0x80, + 0x8b, 0x44, + 0x8c, 0xaa, + 0x8a, 0x10, + 0xba, 0x00, + 0xf5, 0x04, + 0xfe, 0x44, + 0xd2, 0x32, + 0xb8, 0x00, +}; + +/* DS3000 doesn't need some parameters as input and auto-detects them */ +/* save input from the application of those parameters */ +struct ds3000_tuning { + u32 frequency; + u32 symbol_rate; + fe_spectral_inversion_t inversion; + enum fe_code_rate fec; + + /* input values */ + u8 inversion_val; + fe_modulation_t delivery; + u8 rolloff; +}; + +struct ds3000_state { + struct i2c_adapter *i2c; + const struct ds3000_config *config; + + struct dvb_frontend frontend; + + struct ds3000_tuning dcur; + struct ds3000_tuning dnxt; + + u8 skip_fw_load; + + /* previous uncorrected block counter for DVB-S2 */ + u16 prevUCBS2; +}; + +static int ds3000_writereg(struct ds3000_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); + + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," + " value == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = 0x60, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); + + ds3000_writereg(state, 0x03, 0x11); + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk("%s: writereg error(err == %i, reg == 0x%02x," + " value == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +/* I2C write for 8k firmware load */ +static int ds3000_writeFW(struct ds3000_state *state, int reg, + const u8 *data, u16 len) +{ + int i, ret = -EREMOTEIO; + struct i2c_msg msg; + u8 *buf; + + buf = kmalloc(3, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "Unable to kmalloc\n"); + ret = -ENOMEM; + goto error; + } + + *(buf) = reg; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = 3; + + for (i = 0; i < len; i += 2) { + memcpy(buf + 1, data + i, 2); + + dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + printk(KERN_ERR "%s: write error(err == %i, " + "reg == 0x%02x\n", __func__, ret, reg); + ret = -EREMOTEIO; + } + } + +error: + kfree(buf); + + return ret; +} + +static int ds3000_readreg(struct ds3000_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); + return ret; + } + + dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); + + return b1[0]; +} + +static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = 0x60, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = 0x60, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ds3000_writereg(state, 0x03, 0x12); + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); + return ret; + } + + dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); + + return b1[0]; +} + +static int ds3000_set_inversion(struct ds3000_state *state, + fe_spectral_inversion_t inversion) +{ + dprintk("%s(%d)\n", __func__, inversion); + + switch (inversion) { + case INVERSION_OFF: + case INVERSION_ON: + case INVERSION_AUTO: + break; + default: + return -EINVAL; + } + + state->dnxt.inversion = inversion; + + return 0; +} + +static int ds3000_set_symbolrate(struct ds3000_state *state, u32 rate) +{ + int ret = 0; + + dprintk("%s()\n", __func__); + + dprintk("%s() symbol_rate = %d\n", __func__, state->dnxt.symbol_rate); + + /* check if symbol rate is within limits */ + if ((state->dnxt.symbol_rate > + state->frontend.ops.info.symbol_rate_max) || + (state->dnxt.symbol_rate < + state->frontend.ops.info.symbol_rate_min)) + ret = -EOPNOTSUPP; + + state->dnxt.symbol_rate = rate; + + return ret; +} + +static int ds3000_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw); + +static int ds3000_firmware_ondemand(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + const struct firmware *fw; + int ret = 0; + + dprintk("%s()\n", __func__); + + if (ds3000_readreg(state, 0xb2) <= 0) + return ret; + + if (state->skip_fw_load) + return 0; + /* Load firmware */ + /* request the firmware, this will block until someone uploads it */ + printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__, + DS3000_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE, + state->i2c->dev.parent); + printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__); + if (ret) { + printk(KERN_ERR "%s: No firmware uploaded (timeout or file not " + "found?)\n", __func__); + return ret; + } + + /* Make sure we don't recurse back through here during loading */ + state->skip_fw_load = 1; + + ret = ds3000_load_firmware(fe, fw); + if (ret) + printk("%s: Writing firmware to device failed\n", __func__); + + release_firmware(fw); + + dprintk("%s: Firmware upload %s\n", __func__, + ret == 0 ? "complete" : "failed"); + + /* Ensure firmware is always loaded if required */ + state->skip_fw_load = 0; + + return ret; +} + +static int ds3000_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw) +{ + struct ds3000_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", + fw->size, + fw->data[0], + fw->data[1], + fw->data[fw->size - 2], + fw->data[fw->size - 1]); + + /* Begin the firmware load process */ + ds3000_writereg(state, 0xb2, 0x01); + /* write the entire firmware */ + ds3000_writeFW(state, 0xb0, fw->data, fw->size); + ds3000_writereg(state, 0xb2, 0x00); + + return 0; +} + +static void ds3000_dump_registers(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + int x, y, reg = 0, val; + + for (y = 0; y < 16; y++) { + dprintk("%s: %02x: ", __func__, y); + for (x = 0; x < 16; x++) { + reg = (y << 4) + x; + val = ds3000_readreg(state, reg); + if (x != 15) + dprintk("%02x ", val); + else + dprintk("%02x\n", val); + } + } + dprintk("%s: -- DS3000 DUMP DONE --\n", __func__); +} + +static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int lock; + + *status = 0; + + switch (c->delivery_system) { + case SYS_DVBS: + lock = ds3000_readreg(state, 0xd1); + if ((lock & 0x07) == 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + + break; + case SYS_DVBS2: + lock = ds3000_readreg(state, 0x0d); + if ((lock & 0x8f) == 0x8f) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + + break; + default: + return 1; + } + + dprintk("%s: status = 0x%02x\n", __func__, lock); + + return 0; +} + +#define FE_IS_TUNED (FE_HAS_SIGNAL + FE_HAS_LOCK) +static int ds3000_is_tuned(struct dvb_frontend *fe) +{ + fe_status_t tunerstat; + + ds3000_read_status(fe, &tunerstat); + + return ((tunerstat & FE_IS_TUNED) == FE_IS_TUNED); +} + +/* read DS3000 BER value */ +static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 data; + u32 ber_reading, lpdc_frames; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + /* set the number of bytes checked during + BER estimation */ + ds3000_writereg(state, 0xf9, 0x04); + /* read BER estimation status */ + data = ds3000_readreg(state, 0xf8); + /* check if BER estimation is ready */ + if ((data & 0x10) == 0) { + /* this is the number of error bits, + to calculate the bit error rate + divide to 8388608 */ + *ber = (ds3000_readreg(state, 0xf7) << 8) | + ds3000_readreg(state, 0xf6); + /* start counting error bits */ + /* need to be set twice + otherwise it fails sometimes */ + data |= 0x10; + ds3000_writereg(state, 0xf8, data); + ds3000_writereg(state, 0xf8, data); + } else + /* used to indicate that BER estimation + is not ready, i.e. BER is unknown */ + *ber = 0xffffffff; + break; + case SYS_DVBS2: + /* read the number of LPDC decoded frames */ + lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) | + (ds3000_readreg(state, 0xd6) << 8) | + ds3000_readreg(state, 0xd5); + /* read the number of packets with bad CRC */ + ber_reading = (ds3000_readreg(state, 0xf8) << 8) | + ds3000_readreg(state, 0xf7); + if (lpdc_frames > 750) { + /* clear LPDC frame counters */ + ds3000_writereg(state, 0xd1, 0x01); + /* clear bad packets counter */ + ds3000_writereg(state, 0xf9, 0x01); + /* enable bad packets counter */ + ds3000_writereg(state, 0xf9, 0x00); + /* enable LPDC frame counters */ + ds3000_writereg(state, 0xd1, 0x00); + *ber = ber_reading; + } else + /* used to indicate that BER estimation is not ready, + i.e. BER is unknown */ + *ber = 0xffffffff; + break; + default: + return 1; + } + + return 0; +} + +/* read TS2020 signal strength */ +static int ds3000_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct ds3000_state *state = fe->demodulator_priv; + u16 sig_reading, sig_strength; + u8 rfgain, bbgain; + + dprintk("%s()\n", __func__); + + rfgain = ds3000_tuner_readreg(state, 0x3d) & 0x1f; + bbgain = ds3000_tuner_readreg(state, 0x21) & 0x1f; + + if (rfgain > 15) + rfgain = 15; + if (bbgain > 13) + bbgain = 13; + + sig_reading = rfgain * 2 + bbgain * 3; + + sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; + + /* cook the value to be suitable for szap-s2 human readable output */ + *signal_strength = sig_strength * 1000; + + dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, + sig_reading, *signal_strength); + + return 0; +} + +/* calculate DS3000 snr value in dB */ +static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 snr_reading, snr_value; + u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp; + static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */ + 0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03, + 0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717, + 0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505 + }; + static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */ + 0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103, + 0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5, + 0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6, + 0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888, + 0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51, + 0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68, + 0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206, + 0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a, + 0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649, + 0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813, + 0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1, + 0x49e9, 0x4a20, 0x4a57 + }; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + snr_reading = ds3000_readreg(state, 0xff); + snr_reading /= 8; + if (snr_reading == 0) + *snr = 0x0000; + else { + if (snr_reading > 20) + snr_reading = 20; + snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = snr_value * 8 * 655; + } + dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, + snr_reading, *snr); + break; + case SYS_DVBS2: + dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) + + (ds3000_readreg(state, 0x8d) << 4); + dvbs2_signal_reading = ds3000_readreg(state, 0x8e); + tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1; + if (dvbs2_signal_reading == 0) { + *snr = 0x0000; + return 0; + } + if (dvbs2_noise_reading == 0) { + snr_value = 0x0013; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = 0xffff; + return 0; + } + if (tmp > dvbs2_noise_reading) { + snr_reading = tmp / dvbs2_noise_reading; + if (snr_reading > 80) + snr_reading = 80; + snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = snr_value * 5 * 655; + } else { + snr_reading = dvbs2_noise_reading / tmp; + if (snr_reading > 80) + snr_reading = 80; + *snr = -(dvbs2_snr_tab[snr_reading] / 1000); + } + dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, + snr_reading, *snr); + break; + default: + return 1; + } + + return 0; +} + +/* read DS3000 uncorrected blocks */ +static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 data; + u16 _ucblocks; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + *ucblocks = (ds3000_readreg(state, 0xf5) << 8) | + ds3000_readreg(state, 0xf4); + data = ds3000_readreg(state, 0xf8); + /* clear packet counters */ + data &= ~0x20; + ds3000_writereg(state, 0xf8, data); + /* enable packet counters */ + data |= 0x20; + ds3000_writereg(state, 0xf8, data); + break; + case SYS_DVBS2: + _ucblocks = (ds3000_readreg(state, 0xe2) << 8) | + ds3000_readreg(state, 0xe1); + if (_ucblocks > state->prevUCBS2) + *ucblocks = _ucblocks - state->prevUCBS2; + else + *ucblocks = state->prevUCBS2 - _ucblocks; + state->prevUCBS2 = _ucblocks; + break; + default: + return 1; + } + + return 0; +} + +/* Overwrite the current tuning params, we are about to tune */ +static void ds3000_clone_params(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); +} + +static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct ds3000_state *state = fe->demodulator_priv; + u8 data; + + dprintk("%s(%d)\n", __func__, tone); + if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { + printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); + return -EINVAL; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + switch (tone) { + case SEC_TONE_ON: + dprintk("%s: setting tone on\n", __func__); + data = ds3000_readreg(state, 0xa1); + data &= ~0x43; + data |= 0x04; + ds3000_writereg(state, 0xa1, data); + break; + case SEC_TONE_OFF: + dprintk("%s: setting tone off\n", __func__); + data = ds3000_readreg(state, 0xa2); + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + break; + } + + return 0; +} + +static int ds3000_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *d) +{ + struct ds3000_state *state = fe->demodulator_priv; + int i; + u8 data; + + /* Dump DiSEqC message */ + dprintk("%s(", __func__); + for (i = 0 ; i < d->msg_len;) { + dprintk("0x%02x", d->msg[i]); + if (++i < d->msg_len) + dprintk(", "); + } + + /* enable DiSEqC message send pin */ + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + /* DiSEqC message */ + for (i = 0; i < d->msg_len; i++) + ds3000_writereg(state, 0xa3 + i, d->msg[i]); + + data = ds3000_readreg(state, 0xa1); + /* clear DiSEqC message length and status, + enable DiSEqC message send */ + data &= ~0xf8; + /* set DiSEqC mode, modulation active during 33 pulses, + set DiSEqC message length */ + data |= ((d->msg_len - 1) << 3) | 0x07; + ds3000_writereg(state, 0xa1, data); + + /* wait up to 150ms for DiSEqC transmission to complete */ + for (i = 0; i < 15; i++) { + data = ds3000_readreg(state, 0xa1); + if ((data & 0x40) == 0) + break; + msleep(10); + } + + /* DiSEqC timeout after 150ms */ + if (i == 15) { + data = ds3000_readreg(state, 0xa1); + data &= ~0x80; + data |= 0x40; + ds3000_writereg(state, 0xa1, data); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 1; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 0; +} + +/* Send DiSEqC burst */ +static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t burst) +{ + struct ds3000_state *state = fe->demodulator_priv; + int i; + u8 data; + + dprintk("%s()\n", __func__); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + /* DiSEqC burst */ + if (burst == SEC_MINI_A) + /* Unmodulated tone burst */ + ds3000_writereg(state, 0xa1, 0x02); + else if (burst == SEC_MINI_B) + /* Modulated tone burst */ + ds3000_writereg(state, 0xa1, 0x01); + else + return -EINVAL; + + msleep(13); + for (i = 0; i < 5; i++) { + data = ds3000_readreg(state, 0xa1); + if ((data & 0x40) == 0) + break; + msleep(1); + } + + if (i == 5) { + data = ds3000_readreg(state, 0xa1); + data &= ~0x80; + data |= 0x40; + ds3000_writereg(state, 0xa1, data); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 1; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 0; +} + +static void ds3000_release(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + dprintk("%s\n", __func__); + kfree(state); +} + +static struct dvb_frontend_ops ds3000_ops; + +struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c) +{ + struct ds3000_state *state = NULL; + int ret; + + dprintk("%s\n", __func__); + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct ds3000_state), GFP_KERNEL); + if (state == NULL) { + printk(KERN_ERR "Unable to kmalloc\n"); + goto error2; + } + + /* setup the state */ + memset(state, 0, sizeof(struct ds3000_state)); + + state->config = config; + state->i2c = i2c; + state->prevUCBS2 = 0; + + /* check if the demod is present */ + ret = ds3000_readreg(state, 0x00) & 0xfe; + if (ret != 0xe0) { + printk(KERN_ERR "Invalid probe, probably not a DS3000\n"); + goto error3; + } + + printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n", + ds3000_readreg(state, 0x02), + ds3000_readreg(state, 0x01)); + + memcpy(&state->frontend.ops, &ds3000_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error3: + kfree(state); +error2: + return NULL; +} +EXPORT_SYMBOL(ds3000_attach); + +static int ds3000_set_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int ds3000_get_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int ds3000_tune(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + int ret = 0, retune, i; + u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf; + u16 value, ndiv; + u32 f3db; + + dprintk("%s() ", __func__); + + /* Load the firmware if required */ + ret = ds3000_firmware_ondemand(fe); + if (ret != 0) { + printk(KERN_ERR "%s: Unable initialise the firmware\n", + __func__); + return ret; + } + + state->dnxt.delivery = c->modulation; + state->dnxt.frequency = c->frequency; + state->dnxt.rolloff = 2; /* fixme */ + state->dnxt.fec = c->fec_inner; + + ret = ds3000_set_inversion(state, p->inversion); + if (ret != 0) + return ret; + + ret = ds3000_set_symbolrate(state, c->symbol_rate); + if (ret != 0) + return ret; + + /* discard the 'current' tuning parameters and prepare to tune */ + ds3000_clone_params(fe); + + retune = 1; /* try 1 times */ + dprintk("%s: retune = %d\n", __func__, retune); + dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); + dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); + dprintk("%s: FEC = %d \n", __func__, + state->dcur.fec); + dprintk("%s: Inversion = %d\n", __func__, state->dcur.inversion); + + do { + /* Reset status register */ + status = 0; + /* Tune */ + /* TS2020 init */ + ds3000_tuner_writereg(state, 0x42, 0x73); + ds3000_tuner_writereg(state, 0x05, 0x01); + ds3000_tuner_writereg(state, 0x62, 0xf5); + /* unknown */ + ds3000_tuner_writereg(state, 0x07, 0x02); + ds3000_tuner_writereg(state, 0x10, 0x00); + ds3000_tuner_writereg(state, 0x60, 0x79); + ds3000_tuner_writereg(state, 0x08, 0x01); + ds3000_tuner_writereg(state, 0x00, 0x01); + /* calculate and set freq divider */ + if (state->dcur.frequency < 1146000) { + ds3000_tuner_writereg(state, 0x10, 0x11); + ndiv = ((state->dcur.frequency * (6 + 8) * 4) + + (DS3000_XTAL_FREQ / 2)) / + DS3000_XTAL_FREQ - 1024; + } else { + ds3000_tuner_writereg(state, 0x10, 0x01); + ndiv = ((state->dcur.frequency * (6 + 8) * 2) + + (DS3000_XTAL_FREQ / 2)) / + DS3000_XTAL_FREQ - 1024; + } + + ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8); + ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff); + + /* set pll */ + ds3000_tuner_writereg(state, 0x03, 0x06); + ds3000_tuner_writereg(state, 0x51, 0x0f); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x10); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + /* unknown */ + ds3000_tuner_writereg(state, 0x51, 0x17); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x08); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + value = ds3000_tuner_readreg(state, 0x3d); + value &= 0x0f; + if ((value > 4) && (value < 15)) { + value -= 3; + if (value < 4) + value = 4; + value = ((value << 3) | 0x01) & 0x79; + } + + ds3000_tuner_writereg(state, 0x60, value); + ds3000_tuner_writereg(state, 0x51, 0x17); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x08); + ds3000_tuner_writereg(state, 0x50, 0x00); + + /* set low-pass filter period */ + ds3000_tuner_writereg(state, 0x04, 0x2e); + ds3000_tuner_writereg(state, 0x51, 0x1b); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x04); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + f3db = ((state->dcur.symbol_rate / 1000) << 2) / 5 + 2000; + if ((state->dcur.symbol_rate / 1000) < 5000) + f3db += 3000; + if (f3db < 7000) + f3db = 7000; + if (f3db > 40000) + f3db = 40000; + + /* set low-pass filter baseband */ + value = ds3000_tuner_readreg(state, 0x26); + mlpf = 0x2e * 207 / ((value << 1) + 151); + mlpf_max = mlpf * 135 / 100; + mlpf_min = mlpf * 78 / 100; + if (mlpf_max > 63) + mlpf_max = 63; + + /* rounded to the closest integer */ + nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2)) + / (2766 * DS3000_XTAL_FREQ); + if (nlpf > 23) + nlpf = 23; + if (nlpf < 1) + nlpf = 1; + + /* rounded to the closest integer */ + mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + + if (mlpf_new < mlpf_min) { + nlpf++; + mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + } + + if (mlpf_new > mlpf_max) + mlpf_new = mlpf_max; + + ds3000_tuner_writereg(state, 0x04, mlpf_new); + ds3000_tuner_writereg(state, 0x06, nlpf); + ds3000_tuner_writereg(state, 0x51, 0x1b); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x04); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + /* unknown */ + ds3000_tuner_writereg(state, 0x51, 0x1e); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x01); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(60); + + /* ds3000 global reset */ + ds3000_writereg(state, 0x07, 0x80); + ds3000_writereg(state, 0x07, 0x00); + /* ds3000 build-in uC reset */ + ds3000_writereg(state, 0xb2, 0x01); + /* ds3000 software reset */ + ds3000_writereg(state, 0x00, 0x01); + + switch (c->delivery_system) { + case SYS_DVBS: + /* initialise the demod in DVB-S mode */ + for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2) + ds3000_writereg(state, + ds3000_dvbs_init_tab[i], + ds3000_dvbs_init_tab[i + 1]); + value = ds3000_readreg(state, 0xfe); + value &= 0xc0; + value |= 0x1b; + ds3000_writereg(state, 0xfe, value); + break; + case SYS_DVBS2: + /* initialise the demod in DVB-S2 mode */ + for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2) + ds3000_writereg(state, + ds3000_dvbs2_init_tab[i], + ds3000_dvbs2_init_tab[i + 1]); + ds3000_writereg(state, 0xfe, 0x54); + break; + default: + return 1; + } + + /* enable 27MHz clock output */ + ds3000_writereg(state, 0x29, 0x80); + /* enable ac coupling */ + ds3000_writereg(state, 0x25, 0x8a); + + /* enhance symbol rate performance */ + if ((state->dcur.symbol_rate / 1000) <= 5000) { + value = 29777 / (state->dcur.symbol_rate / 1000) + 1; + if (value % 2 != 0) + value++; + ds3000_writereg(state, 0xc3, 0x0d); + ds3000_writereg(state, 0xc8, value); + ds3000_writereg(state, 0xc4, 0x10); + ds3000_writereg(state, 0xc7, 0x0e); + } else if ((state->dcur.symbol_rate / 1000) <= 10000) { + value = 92166 / (state->dcur.symbol_rate / 1000) + 1; + if (value % 2 != 0) + value++; + ds3000_writereg(state, 0xc3, 0x07); + ds3000_writereg(state, 0xc8, value); + ds3000_writereg(state, 0xc4, 0x09); + ds3000_writereg(state, 0xc7, 0x12); + } else if ((state->dcur.symbol_rate / 1000) <= 20000) { + value = 64516 / (state->dcur.symbol_rate / 1000) + 1; + ds3000_writereg(state, 0xc3, value); + ds3000_writereg(state, 0xc8, 0x0e); + ds3000_writereg(state, 0xc4, 0x07); + ds3000_writereg(state, 0xc7, 0x18); + } else { + value = 129032 / (state->dcur.symbol_rate / 1000) + 1; + ds3000_writereg(state, 0xc3, value); + ds3000_writereg(state, 0xc8, 0x0a); + ds3000_writereg(state, 0xc4, 0x05); + ds3000_writereg(state, 0xc7, 0x24); + } + + /* normalized symbol rate rounded to the closest integer */ + value = (((state->dcur.symbol_rate / 1000) << 16) + + (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE; + ds3000_writereg(state, 0x61, value & 0x00ff); + ds3000_writereg(state, 0x62, (value & 0xff00) >> 8); + + /* co-channel interference cancellation disabled */ + ds3000_writereg(state, 0x56, 0x00); + + /* equalizer disabled */ + ds3000_writereg(state, 0x76, 0x00); + + /*ds3000_writereg(state, 0x08, 0x03); + ds3000_writereg(state, 0xfd, 0x22); + ds3000_writereg(state, 0x08, 0x07); + ds3000_writereg(state, 0xfd, 0x42); + ds3000_writereg(state, 0x08, 0x07);*/ + + /* ds3000 out of software reset */ + ds3000_writereg(state, 0x00, 0x00); + /* start ds3000 build-in uC */ + ds3000_writereg(state, 0xb2, 0x00); + + /* TODO: calculate and set carrier offset */ + + /* wait before retrying */ + for (i = 0; i < 30 ; i++) { + if (ds3000_is_tuned(fe)) { + dprintk("%s: Tuned\n", __func__); + ds3000_dump_registers(fe); + goto tuned; + } + msleep(1); + } + + dprintk("%s: Not tuned\n", __func__); + ds3000_dump_registers(fe); + + } while (--retune); + +tuned: + return ret; +} + +static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return DVBFE_ALGO_SW; +} + +/* + * Initialise or wake up device + * + * Power config will reset and load initial firmware if required + */ +static int ds3000_initfe(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +/* Put device to sleep */ +static int ds3000_sleep(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +static struct dvb_frontend_ops ds3000_ops = { + + .info = { + .name = "Montage Technology DS3000/TS2020", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1011, /* kHz for QPSK frontends */ + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = ds3000_release, + + .init = ds3000_initfe, + .sleep = ds3000_sleep, + .read_status = ds3000_read_status, + .read_ber = ds3000_read_ber, + .read_signal_strength = ds3000_read_signal_strength, + .read_snr = ds3000_read_snr, + .read_ucblocks = ds3000_read_ucblocks, + .set_tone = ds3000_set_tone, + .diseqc_send_master_cmd = ds3000_send_diseqc_msg, + .diseqc_send_burst = ds3000_diseqc_send_burst, + .get_frontend_algo = ds3000_get_algo, + + .set_property = ds3000_set_property, + .get_property = ds3000_get_property, + .set_frontend = ds3000_tune, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +MODULE_DESCRIPTION("DVB Frontend module for Montage Technology " + "DS3000/TS2020 hardware"); +MODULE_AUTHOR("Konstantin Dimitrov"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb/frontends/ds3000.h new file mode 100644 index 000000000000..67f67038740a --- /dev/null +++ b/drivers/media/dvb/frontends/ds3000.h @@ -0,0 +1,45 @@ +/* + Montage Technology DS3000/TS2020 - DVBS/S2 Satellite demod/tuner driver + Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + + Copyright (C) 2009 TurboSight.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef DS3000_H +#define DS3000_H + +#include <linux/dvb/frontend.h> + +struct ds3000_config { + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if defined(CONFIG_DVB_DS3000) || \ + (defined(CONFIG_DVB_DS3000_MODULE) && defined(MODULE)) +extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_DS3000 */ +#endif /* DS3000_H */ diff --git a/drivers/media/dvb/frontends/ec100.c b/drivers/media/dvb/frontends/ec100.c new file mode 100644 index 000000000000..2414dc6ee5d9 --- /dev/null +++ b/drivers/media/dvb/frontends/ec100.c @@ -0,0 +1,335 @@ +/* + * E3C EC100 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "dvb_frontend.h" +#include "ec100_priv.h" +#include "ec100.h" + +int ec100_debug; +module_param_named(debug, ec100_debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +struct ec100_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct ec100_config config; + + u16 ber; +}; + +/* write single register */ +static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = state->config.demod_address, + .flags = 0, + .len = 2, + .buf = buf}; + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + warn("I2C write failed reg:%02x", reg); + return -EREMOTEIO; + } + return 0; +} + +/* read single register */ +static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { + .addr = state->config.demod_address, + .flags = 0, + .len = 1, + .buf = ® + }, { + .addr = state->config.demod_address, + .flags = I2C_M_RD, + .len = 1, + .buf = val + } + }; + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + warn("I2C read failed reg:%02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int ec100_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp, tmp2; + + deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency, + params->u.ofdm.bandwidth); + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, params); + + ret = ec100_write_reg(state, 0x04, 0x06); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x67, 0x58); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x05, 0x18); + if (ret) + goto error; + + /* reg/bw | 6 | 7 | 8 + -------+------+------+------ + A 0x1b | 0xa1 | 0xe7 | 0x2c + A 0x1c | 0x55 | 0x63 | 0x72 + -------+------+------+------ + B 0x1b | 0xb7 | 0x00 | 0x49 + B 0x1c | 0x55 | 0x64 | 0x72 */ + + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + tmp = 0xb7; + tmp2 = 0x55; + break; + case BANDWIDTH_7_MHZ: + tmp = 0x00; + tmp2 = 0x64; + break; + case BANDWIDTH_8_MHZ: + default: + tmp = 0x49; + tmp2 = 0x72; + } + + ret = ec100_write_reg(state, 0x1b, tmp); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x1c, tmp2); + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */ + if (ret) + goto error; + ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */ + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x08, 0x24); + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x00, 0x00); /* go */ + if (ret) + goto error; + ret = ec100_write_reg(state, 0x00, 0x20); /* go */ + if (ret) + goto error; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec100_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 300; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + *status = 0; + + ret = ec100_read_reg(state, 0x42, &tmp); + if (ret) + goto error; + + if (tmp & 0x80) { + /* bit7 set - have lock */ + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + } else { + ret = ec100_read_reg(state, 0x01, &tmp); + if (ret) + goto error; + + if (tmp & 0x10) { + /* bit4 set - have signal */ + *status |= FE_HAS_SIGNAL; + if (!(tmp & 0x01)) { + /* bit0 clear - have ~valid signal */ + *status |= FE_HAS_CARRIER | FE_HAS_VITERBI; + } + } + } + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp, tmp2; + u16 ber2; + + *ber = 0; + + ret = ec100_read_reg(state, 0x65, &tmp); + if (ret) + goto error; + ret = ec100_read_reg(state, 0x66, &tmp2); + if (ret) + goto error; + + ber2 = (tmp2 << 8) | tmp; + + /* if counter overflow or clear */ + if (ber2 < state->ber) + *ber = ber2; + else + *ber = ber2 - state->ber; + + state->ber = ber2; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + + ret = ec100_read_reg(state, 0x24, &tmp); + if (ret) { + *strength = 0; + goto error; + } + + *strength = ((tmp << 8) | tmp); + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + return 0; +} + +static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static void ec100_release(struct dvb_frontend *fe) +{ + struct ec100_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops ec100_ops; + +struct dvb_frontend *ec100_attach(const struct ec100_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct ec100_state *state = NULL; + u8 tmp; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config, config, sizeof(struct ec100_config)); + + /* check if the demod is there */ + ret = ec100_read_reg(state, 0x33, &tmp); + if (ret || tmp != 0x0b) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &ec100_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(ec100_attach); + +static struct dvb_frontend_ops ec100_ops = { + .info = { + .name = "E3C EC100 DVB-T", + .type = FE_OFDM, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS + }, + + .release = ec100_release, + .set_frontend = ec100_set_frontend, + .get_tune_settings = ec100_get_tune_settings, + .read_status = ec100_read_status, + .read_ber = ec100_read_ber, + .read_signal_strength = ec100_read_signal_strength, + .read_snr = ec100_read_snr, + .read_ucblocks = ec100_read_ucblocks, +}; + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/ec100.h b/drivers/media/dvb/frontends/ec100.h new file mode 100644 index 000000000000..ee8e52417958 --- /dev/null +++ b/drivers/media/dvb/frontends/ec100.h @@ -0,0 +1,46 @@ +/* + * E3C EC100 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef EC100_H +#define EC100_H + +#include <linux/dvb/frontend.h> + +struct ec100_config { + /* demodulator's I2C address */ + u8 demod_address; +}; + + +#if defined(CONFIG_DVB_EC100) || \ + (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE)) +extern struct dvb_frontend *ec100_attach(const struct ec100_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ec100_attach( + const struct ec100_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* EC100_H */ diff --git a/drivers/media/dvb/frontends/ec100_priv.h b/drivers/media/dvb/frontends/ec100_priv.h new file mode 100644 index 000000000000..5c990144bc47 --- /dev/null +++ b/drivers/media/dvb/frontends/ec100_priv.h @@ -0,0 +1,39 @@ +/* + * E3C EC100 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef EC100_PRIV +#define EC100_PRIV + +#define LOG_PREFIX "ec100" + +#define dprintk(var, level, args...) \ + do { if ((var & level)) printk(args); } while (0) + +#define deb_info(args...) dprintk(ec100_debug, 0x01, args) + +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +#endif /* EC100_PRIV */ diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index 056387b41a8f..43971e63baa7 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -479,7 +479,7 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status) switch (state->current_modulation) { case QAM_256: case QAM_64: - /* Need to undestand why there are 3 lock levels here */ + /* Need to understand why there are 3 lock levels here */ if ((buf[0] & 0x07) == 0x07) *status |= FE_HAS_CARRIER; break; @@ -520,7 +520,7 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status) switch (state->current_modulation) { case QAM_256: case QAM_64: - /* Need to undestand why there are 3 lock levels here */ + /* Need to understand why there are 3 lock levels here */ if ((buf[0] & 0x07) == 0x07) *status |= FE_HAS_CARRIER; else diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c index fb3011518427..0e2f61a8978f 100644 --- a/drivers/media/dvb/frontends/s5h1409.c +++ b/drivers/media/dvb/frontends/s5h1409.c @@ -44,7 +44,15 @@ struct s5h1409_state { int if_freq; u32 is_qam_locked; - u32 qam_state; + + /* QAM tuning state goes through the following state transitions */ +#define QAM_STATE_UNTUNED 0 +#define QAM_STATE_TUNING_STARTED 1 +#define QAM_STATE_INTERLEAVE_SET 2 +#define QAM_STATE_QAM_OPTIMIZED_L1 3 +#define QAM_STATE_QAM_OPTIMIZED_L2 4 +#define QAM_STATE_QAM_OPTIMIZED_L3 5 + u8 qam_state; }; static int debug; @@ -347,7 +355,7 @@ static int s5h1409_softreset(struct dvb_frontend *fe) s5h1409_writereg(state, 0xf5, 0); s5h1409_writereg(state, 0xf5, 1); state->is_qam_locked = 0; - state->qam_state = 0; + state->qam_state = QAM_STATE_UNTUNED; return 0; } @@ -474,6 +482,59 @@ static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe) struct s5h1409_state *state = fe->demodulator_priv; u16 reg; + if (state->qam_state < QAM_STATE_INTERLEAVE_SET) { + /* We should not perform amhum optimization until + the interleave mode has been configured */ + return; + } + + if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) { + /* We've already reached the maximum optimization level, so + dont bother banging on the status registers */ + return; + } + + /* QAM EQ lock check */ + reg = s5h1409_readreg(state, 0xf0); + + if ((reg >> 13) & 0x1) { + reg &= 0xff; + + s5h1409_writereg(state, 0x96, 0x000c); + if (reg < 0x68) { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) { + dprintk("%s() setting QAM state to OPT_L3\n", + __func__); + s5h1409_writereg(state, 0x93, 0x3130); + s5h1409_writereg(state, 0x9e, 0x2836); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3; + } + } else { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) { + dprintk("%s() setting QAM state to OPT_L2\n", + __func__); + s5h1409_writereg(state, 0x93, 0x3332); + s5h1409_writereg(state, 0x9e, 0x2c37); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2; + } + } + + } else { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) { + dprintk("%s() setting QAM state to OPT_L1\n", __func__); + s5h1409_writereg(state, 0x96, 0x0008); + s5h1409_writereg(state, 0x93, 0x3332); + s5h1409_writereg(state, 0x9e, 0x2c37); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1; + } + } +} + +static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg; + if (state->is_qam_locked) return; @@ -506,6 +567,44 @@ static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe) struct s5h1409_state *state = fe->demodulator_priv; u16 reg, reg1, reg2; + if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) { + /* We've done the optimization already */ + return; + } + + reg = s5h1409_readreg(state, 0xf1); + + /* Master lock */ + if ((reg >> 15) & 0x1) { + if (state->qam_state == QAM_STATE_UNTUNED || + state->qam_state == QAM_STATE_TUNING_STARTED) { + dprintk("%s() setting QAM state to INTERLEAVE_SET\n", + __func__); + reg1 = s5h1409_readreg(state, 0xb2); + reg2 = s5h1409_readreg(state, 0xad); + + s5h1409_writereg(state, 0x96, 0x0020); + s5h1409_writereg(state, 0xad, + (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff))); + state->qam_state = QAM_STATE_INTERLEAVE_SET; + } + } else { + if (state->qam_state == QAM_STATE_UNTUNED) { + dprintk("%s() setting QAM state to TUNING_STARTED\n", + __func__); + s5h1409_writereg(state, 0x96, 0x08); + s5h1409_writereg(state, 0xab, + s5h1409_readreg(state, 0xab) | 0x1001); + state->qam_state = QAM_STATE_TUNING_STARTED; + } + } +} + +static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg, reg1, reg2; + reg = s5h1409_readreg(state, 0xf1); /* Master lock */ @@ -553,16 +652,24 @@ static int s5h1409_set_frontend(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 0); } - /* Optimize the demod for QAM */ - if (p->u.vsb.modulation != VSB_8) { - s5h1409_set_qam_amhum_mode(fe); - s5h1409_set_qam_interleave_mode(fe); - } - /* Issue a reset to the demod so it knows to resync against the newly tuned frequency */ s5h1409_softreset(fe); + /* Optimize the demod for QAM */ + if (state->current_modulation != VSB_8) { + /* This almost certainly applies to all boards, but for now + only do it for the HVR-1600. Once the other boards are + tested, the "legacy" versions can just go away */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + s5h1409_set_qam_interleave_mode(fe); + s5h1409_set_qam_amhum_mode(fe); + } else { + s5h1409_set_qam_amhum_mode_legacy(fe); + s5h1409_set_qam_interleave_mode_legacy(fe); + } + } + return 0; } @@ -614,6 +721,21 @@ static int s5h1409_init(struct dvb_frontend *fe) /* The datasheet says that after initialisation, VSB is default */ state->current_modulation = VSB_8; + /* Optimize for the HVR-1600 if appropriate. Note that some of these + may get folded into the generic case after testing with other + devices */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + /* VSB AGC REF */ + s5h1409_writereg(state, 0x09, 0x0050); + + /* Unknown but Windows driver does it... */ + s5h1409_writereg(state, 0x21, 0x0001); + s5h1409_writereg(state, 0x50, 0x030e); + + /* QAM AGC REF */ + s5h1409_writereg(state, 0x82, 0x0800); + } + if (state->config->output_mode == S5H1409_SERIAL_OUTPUT) s5h1409_writereg(state, 0xab, s5h1409_readreg(state, 0xab) | 0x100); /* Serial */ @@ -641,6 +763,17 @@ static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status) *status = 0; + /* Optimize the demod for QAM */ + if (state->current_modulation != VSB_8) { + /* This almost certainly applies to all boards, but for now + only do it for the HVR-1600. Once the other boards are + tested, the "legacy" versions can just go away */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + s5h1409_set_qam_interleave_mode(fe); + s5h1409_set_qam_amhum_mode(fe); + } + } + /* Get the demodulator status */ reg = s5h1409_readreg(state, 0xf1); if (reg & 0x1000) diff --git a/drivers/media/dvb/frontends/s5h1409.h b/drivers/media/dvb/frontends/s5h1409.h index 070d9743e330..91f2ebd1a534 100644 --- a/drivers/media/dvb/frontends/s5h1409.h +++ b/drivers/media/dvb/frontends/s5h1409.h @@ -57,6 +57,13 @@ struct s5h1409_config { #define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 #define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 u16 mpeg_timing; + + /* HVR-1600 optimizations (to better work with MXL5005s) + Note: some of these are likely to be folded into the generic driver + after being regression tested with other boards */ +#define S5H1409_HVR1600_NOOPTIMIZE 0 +#define S5H1409_HVR1600_OPTIMIZE 1 + u8 hvr1600_opt; }; #if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) \ diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c index a04c782fff8d..1570669837ea 100644 --- a/drivers/media/dvb/frontends/stb0899_drv.c +++ b/drivers/media/dvb/frontends/stb0899_drv.c @@ -1571,7 +1571,7 @@ static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_fron * stb0899_track * periodically check the signal level against a specified * threshold level and perform derotator centering. - * called once we have a lock from a succesful search + * called once we have a lock from a successful search * event. * * Will be called periodically called to maintain the diff --git a/drivers/media/dvb/frontends/stb6100_proc.h b/drivers/media/dvb/frontends/stb6100_proc.h new file mode 100644 index 000000000000..112163a48622 --- /dev/null +++ b/drivers/media/dvb/frontends/stb6100_proc.h @@ -0,0 +1,138 @@ +/* + STB6100 Silicon Tuner wrapper + Copyright (C)2009 Igor M. Liplianin (liplianin@me.by) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state state; + int err = 0; + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_state) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &state); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + *frequency = state.frequency; + } + + return 0; +} + +static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state state; + int err = 0; + + state.frequency = frequency; + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &state); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + } + + return 0; +} + +static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state state; + int err = 0; + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_state) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &state); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + *bandwidth = state.bandwidth; + } + + return 0; +} + +static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state state; + int err = 0; + + state.bandwidth = bandwidth; + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &state); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + } + + return 0; +} diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h index bf4e9b633044..29c3fa85c227 100644 --- a/drivers/media/dvb/frontends/stv0900.h +++ b/drivers/media/dvb/frontends/stv0900.h @@ -36,6 +36,7 @@ struct stv0900_reg { struct stv0900_config { u8 demod_address; + u8 demod_mode; u32 xtal; u8 clkmode;/* 0 for CLKI, 2 for XTALI */ @@ -48,6 +49,8 @@ struct stv0900_config { u8 tun2_maddress; u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */ u8 tun2_adc; + /* Set device param to start dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); }; #if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \ diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 3bde3324a032..df49ea0983bc 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -34,7 +34,7 @@ #include "stv0900_priv.h" #include "stv0900_init.h" -static int stvdebug = 1; +int stvdebug = 1; module_param_named(debug, stvdebug, int, 0644); /* internal params node */ @@ -105,7 +105,8 @@ static struct stv0900_inode *append_internal(struct stv0900_internal *internal) while (new_node->next_inode != NULL) new_node = new_node->next_inode; - new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL); + new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), + GFP_KERNEL); if (new_node->next_inode != NULL) new_node = new_node->next_inode; else @@ -128,13 +129,13 @@ s32 ge2comp(s32 a, s32 width) return (a >= (1 << (width - 1))) ? (a - (1 << width)) : a; } -void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr, +void stv0900_write_reg(struct stv0900_internal *intp, u16 reg_addr, u8 reg_data) { u8 data[3]; int ret; struct i2c_msg i2cmsg = { - .addr = i_params->i2c_addr, + .addr = intp->i2c_addr, .flags = 0, .len = 3, .buf = data, @@ -144,33 +145,33 @@ void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr, data[1] = LSB(reg_addr); data[2] = reg_data; - ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1); + ret = i2c_transfer(intp->i2c_adap, &i2cmsg, 1); if (ret != 1) - dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret); + dprintk("%s: i2c error %d\n", __func__, ret); } -u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg) +u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg) { int ret; u8 b0[] = { MSB(reg), LSB(reg) }; u8 buf = 0; struct i2c_msg msg[] = { { - .addr = i_params->i2c_addr, + .addr = intp->i2c_addr, .flags = 0, .buf = b0, .len = 2, }, { - .addr = i_params->i2c_addr, + .addr = intp->i2c_addr, .flags = I2C_M_RD, .buf = &buf, .len = 1, }, }; - ret = i2c_transfer(i_params->i2c_adap, msg, 2); + ret = i2c_transfer(intp->i2c_adap, msg, 2); if (ret != 2) - dprintk(KERN_ERR "%s: i2c error %d, reg[0x%02x]\n", + dprintk("%s: i2c error %d, reg[0x%02x]\n", __func__, ret, reg); return buf; @@ -190,169 +191,165 @@ void extract_mask_pos(u32 label, u8 *mask, u8 *pos) (*pos) = (i - 1); } -void stv0900_write_bits(struct stv0900_internal *i_params, u32 label, u8 val) +void stv0900_write_bits(struct stv0900_internal *intp, u32 label, u8 val) { u8 reg, mask, pos; - reg = stv0900_read_reg(i_params, (label >> 16) & 0xffff); + reg = stv0900_read_reg(intp, (label >> 16) & 0xffff); extract_mask_pos(label, &mask, &pos); val = mask & (val << pos); reg = (reg & (~mask)) | val; - stv0900_write_reg(i_params, (label >> 16) & 0xffff, reg); + stv0900_write_reg(intp, (label >> 16) & 0xffff, reg); } -u8 stv0900_get_bits(struct stv0900_internal *i_params, u32 label) +u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label) { u8 val = 0xff; u8 mask, pos; extract_mask_pos(label, &mask, &pos); - val = stv0900_read_reg(i_params, label >> 16); + val = stv0900_read_reg(intp, label >> 16); val = (val & mask) >> pos; return val; } -enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *i_params) +enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp) { s32 i; - enum fe_stv0900_error error; - - if (i_params != NULL) { - i_params->chip_id = stv0900_read_reg(i_params, R0900_MID); - if (i_params->errs == STV0900_NO_ERROR) { - /*Startup sequence*/ - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5c); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5c); - stv0900_write_reg(i_params, R0900_P1_TNRCFG, 0x6c); - stv0900_write_reg(i_params, R0900_P2_TNRCFG, 0x6f); - stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x20); - stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x20); - stv0900_write_reg(i_params, R0900_NCOARSE, 0x13); - msleep(3); - stv0900_write_reg(i_params, R0900_I2CCFG, 0x08); - - switch (i_params->clkmode) { - case 0: - case 2: - stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20 - | i_params->clkmode); - break; - default: - /* preserve SELOSCI bit */ - i = 0x02 & stv0900_read_reg(i_params, R0900_SYNTCTRL); - stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20 | i); - break; - } - msleep(3); - for (i = 0; i < 182; i++) - stv0900_write_reg(i_params, STV0900_InitVal[i][0], STV0900_InitVal[i][1]); + if (intp == NULL) + return STV0900_INVALID_HANDLE; - if (stv0900_read_reg(i_params, R0900_MID) >= 0x20) { - stv0900_write_reg(i_params, R0900_TSGENERAL, 0x0c); - for (i = 0; i < 32; i++) - stv0900_write_reg(i_params, STV0900_Cut20_AddOnVal[i][0], STV0900_Cut20_AddOnVal[i][1]); - } + intp->chip_id = stv0900_read_reg(intp, R0900_MID); - stv0900_write_reg(i_params, R0900_P1_FSPYCFG, 0x6c); - stv0900_write_reg(i_params, R0900_P2_FSPYCFG, 0x6c); - stv0900_write_reg(i_params, R0900_TSTRES0, 0x80); - stv0900_write_reg(i_params, R0900_TSTRES0, 0x00); - } - error = i_params->errs; - } else - error = STV0900_INVALID_HANDLE; + if (intp->errs != STV0900_NO_ERROR) + return intp->errs; - return error; + /*Startup sequence*/ + stv0900_write_reg(intp, R0900_P1_DMDISTATE, 0x5c); + stv0900_write_reg(intp, R0900_P2_DMDISTATE, 0x5c); + msleep(3); + stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x6c); + stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x6f); + stv0900_write_reg(intp, R0900_P1_I2CRPT, 0x20); + stv0900_write_reg(intp, R0900_P2_I2CRPT, 0x20); + stv0900_write_reg(intp, R0900_NCOARSE, 0x13); + msleep(3); + stv0900_write_reg(intp, R0900_I2CCFG, 0x08); + switch (intp->clkmode) { + case 0: + case 2: + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 + | intp->clkmode); + break; + default: + /* preserve SELOSCI bit */ + i = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | i); + break; + } + + msleep(3); + for (i = 0; i < 181; i++) + stv0900_write_reg(intp, STV0900_InitVal[i][0], + STV0900_InitVal[i][1]); + + if (stv0900_read_reg(intp, R0900_MID) >= 0x20) { + stv0900_write_reg(intp, R0900_TSGENERAL, 0x0c); + for (i = 0; i < 32; i++) + stv0900_write_reg(intp, STV0900_Cut20_AddOnVal[i][0], + STV0900_Cut20_AddOnVal[i][1]); + } + + stv0900_write_reg(intp, R0900_P1_FSPYCFG, 0x6c); + stv0900_write_reg(intp, R0900_P2_FSPYCFG, 0x6c); + + stv0900_write_reg(intp, R0900_P1_PDELCTRL2, 0x01); + stv0900_write_reg(intp, R0900_P2_PDELCTRL2, 0x21); + + stv0900_write_reg(intp, R0900_P1_PDELCTRL3, 0x20); + stv0900_write_reg(intp, R0900_P2_PDELCTRL3, 0x20); + + stv0900_write_reg(intp, R0900_TSTRES0, 0x80); + stv0900_write_reg(intp, R0900_TSTRES0, 0x00); + + return STV0900_NO_ERROR; } -u32 stv0900_get_mclk_freq(struct stv0900_internal *i_params, u32 ext_clk) +u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk) { u32 mclk = 90000000, div = 0, ad_div = 0; - div = stv0900_get_bits(i_params, F0900_M_DIV); - ad_div = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6); + div = stv0900_get_bits(intp, F0900_M_DIV); + ad_div = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); mclk = (div + 1) * ext_clk / ad_div; - dprintk(KERN_INFO "%s: Calculated Mclk = %d\n", __func__, mclk); + dprintk("%s: Calculated Mclk = %d\n", __func__, mclk); return mclk; } -enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *i_params, u32 mclk) +enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk) { - enum fe_stv0900_error error = STV0900_NO_ERROR; u32 m_div, clk_sel; - dprintk(KERN_INFO "%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, - i_params->quartz); + dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, + intp->quartz); - if (i_params == NULL) - error = STV0900_INVALID_HANDLE; - else { - if (i_params->errs) - error = STV0900_I2C_ERROR; - else { - clk_sel = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6); - m_div = ((clk_sel * mclk) / i_params->quartz) - 1; - stv0900_write_bits(i_params, F0900_M_DIV, m_div); - i_params->mclk = stv0900_get_mclk_freq(i_params, - i_params->quartz); - - /*Set the DiseqC frequency to 22KHz */ - /* - Formula: - DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg) - DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg) - */ - m_div = i_params->mclk / 704000; - stv0900_write_reg(i_params, R0900_P1_F22TX, m_div); - stv0900_write_reg(i_params, R0900_P1_F22RX, m_div); - - stv0900_write_reg(i_params, R0900_P2_F22TX, m_div); - stv0900_write_reg(i_params, R0900_P2_F22RX, m_div); - - if ((i_params->errs)) - error = STV0900_I2C_ERROR; - } - } + if (intp == NULL) + return STV0900_INVALID_HANDLE; - return error; + if (intp->errs) + return STV0900_I2C_ERROR; + + clk_sel = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); + m_div = ((clk_sel * mclk) / intp->quartz) - 1; + stv0900_write_bits(intp, F0900_M_DIV, m_div); + intp->mclk = stv0900_get_mclk_freq(intp, + intp->quartz); + + /*Set the DiseqC frequency to 22KHz */ + /* + Formula: + DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg) + DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg) + */ + m_div = intp->mclk / 704000; + stv0900_write_reg(intp, R0900_P1_F22TX, m_div); + stv0900_write_reg(intp, R0900_P1_F22RX, m_div); + + stv0900_write_reg(intp, R0900_P2_F22TX, m_div); + stv0900_write_reg(intp, R0900_P2_F22RX, m_div); + + if ((intp->errs)) + return STV0900_I2C_ERROR; + + return STV0900_NO_ERROR; } -u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr, +u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr, enum fe_stv0900_demod_num demod) { u32 lsb, msb, hsb, err_val; - s32 err1field_hsb, err1field_msb, err1field_lsb; - s32 err2field_hsb, err2field_msb, err2field_lsb; - - dmd_reg(err1field_hsb, F0900_P1_ERR_CNT12, F0900_P2_ERR_CNT12); - dmd_reg(err1field_msb, F0900_P1_ERR_CNT11, F0900_P2_ERR_CNT11); - dmd_reg(err1field_lsb, F0900_P1_ERR_CNT10, F0900_P2_ERR_CNT10); - - dmd_reg(err2field_hsb, F0900_P1_ERR_CNT22, F0900_P2_ERR_CNT22); - dmd_reg(err2field_msb, F0900_P1_ERR_CNT21, F0900_P2_ERR_CNT21); - dmd_reg(err2field_lsb, F0900_P1_ERR_CNT20, F0900_P2_ERR_CNT20); switch (cntr) { case 0: default: - hsb = stv0900_get_bits(i_params, err1field_hsb); - msb = stv0900_get_bits(i_params, err1field_msb); - lsb = stv0900_get_bits(i_params, err1field_lsb); + hsb = stv0900_get_bits(intp, ERR_CNT12); + msb = stv0900_get_bits(intp, ERR_CNT11); + lsb = stv0900_get_bits(intp, ERR_CNT10); break; case 1: - hsb = stv0900_get_bits(i_params, err2field_hsb); - msb = stv0900_get_bits(i_params, err2field_msb); - lsb = stv0900_get_bits(i_params, err2field_lsb); + hsb = stv0900_get_bits(intp, ERR_CNT22); + msb = stv0900_get_bits(intp, ERR_CNT21); + lsb = stv0900_get_bits(intp, ERR_CNT20); break; } @@ -364,26 +361,22 @@ u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr, static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - u32 fi2c; - - dmd_reg(fi2c, F0900_P1_I2CT_ON, F0900_P2_I2CT_ON); - - stv0900_write_bits(i_params, fi2c, enable); + stv0900_write_bits(intp, I2CT_ON, enable); return 0; } -static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, +static void stv0900_set_ts_parallel_serial(struct stv0900_internal *intp, enum fe_stv0900_clock_type path1_ts, enum fe_stv0900_clock_type path2_ts) { - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - if (i_params->chip_id >= 0x20) { + if (intp->chip_id >= 0x20) { switch (path1_ts) { case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: @@ -391,20 +384,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, case STV0900_SERIAL_PUNCT_CLOCK: case STV0900_SERIAL_CONT_CLOCK: default: - stv0900_write_reg(i_params, R0900_TSGENERAL, + stv0900_write_reg(intp, R0900_TSGENERAL, 0x00); break; case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: - stv0900_write_reg(i_params, R0900_TSGENERAL, + stv0900_write_reg(intp, R0900_TSGENERAL, 0x06); - stv0900_write_bits(i_params, + stv0900_write_bits(intp, F0900_P1_TSFIFO_MANSPEED, 3); - stv0900_write_bits(i_params, + stv0900_write_bits(intp, F0900_P2_TSFIFO_MANSPEED, 0); - stv0900_write_reg(i_params, + stv0900_write_reg(intp, R0900_P1_TSSPEED, 0x14); - stv0900_write_reg(i_params, + stv0900_write_reg(intp, R0900_P2_TSSPEED, 0x28); break; } @@ -416,14 +409,14 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, case STV0900_SERIAL_PUNCT_CLOCK: case STV0900_SERIAL_CONT_CLOCK: default: - stv0900_write_reg(i_params, + stv0900_write_reg(intp, R0900_TSGENERAL, 0x0C); break; case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: - stv0900_write_reg(i_params, + stv0900_write_reg(intp, R0900_TSGENERAL, 0x0A); - dprintk(KERN_INFO "%s: 0x0a\n", __func__); + dprintk("%s: 0x0a\n", __func__); break; } break; @@ -436,20 +429,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, case STV0900_SERIAL_PUNCT_CLOCK: case STV0900_SERIAL_CONT_CLOCK: default: - stv0900_write_reg(i_params, R0900_TSGENERAL1X, + stv0900_write_reg(intp, R0900_TSGENERAL1X, 0x10); break; case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: - stv0900_write_reg(i_params, R0900_TSGENERAL1X, + stv0900_write_reg(intp, R0900_TSGENERAL1X, 0x16); - stv0900_write_bits(i_params, + stv0900_write_bits(intp, F0900_P1_TSFIFO_MANSPEED, 3); - stv0900_write_bits(i_params, + stv0900_write_bits(intp, F0900_P2_TSFIFO_MANSPEED, 0); - stv0900_write_reg(i_params, R0900_P1_TSSPEED, + stv0900_write_reg(intp, R0900_P1_TSSPEED, 0x14); - stv0900_write_reg(i_params, R0900_P2_TSSPEED, + stv0900_write_reg(intp, R0900_P2_TSSPEED, 0x28); break; } @@ -462,14 +455,14 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, case STV0900_SERIAL_PUNCT_CLOCK: case STV0900_SERIAL_CONT_CLOCK: default: - stv0900_write_reg(i_params, R0900_TSGENERAL1X, + stv0900_write_reg(intp, R0900_TSGENERAL1X, 0x14); break; case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: - stv0900_write_reg(i_params, R0900_TSGENERAL1X, + stv0900_write_reg(intp, R0900_TSGENERAL1X, 0x12); - dprintk(KERN_INFO "%s: 0x12\n", __func__); + dprintk("%s: 0x12\n", __func__); break; } @@ -479,20 +472,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, switch (path1_ts) { case STV0900_PARALLEL_PUNCT_CLOCK: - stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00); - stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); break; case STV0900_DVBCI_CLOCK: - stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00); - stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); break; case STV0900_SERIAL_PUNCT_CLOCK: - stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01); - stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); break; case STV0900_SERIAL_CONT_CLOCK: - stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01); - stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); break; default: break; @@ -500,29 +493,29 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, switch (path2_ts) { case STV0900_PARALLEL_PUNCT_CLOCK: - stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00); - stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); break; case STV0900_DVBCI_CLOCK: - stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00); - stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); break; case STV0900_SERIAL_PUNCT_CLOCK: - stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01); - stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); break; case STV0900_SERIAL_CONT_CLOCK: - stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01); - stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); break; default: break; } - stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1); - stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 0); - stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1); - stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 0); + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); } void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, @@ -574,7 +567,7 @@ void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) } } -static s32 stv0900_get_rf_level(struct stv0900_internal *i_params, +static s32 stv0900_get_rf_level(struct stv0900_internal *intp, const struct stv0900_table *lookup, enum fe_stv0900_demod_num demod) { @@ -584,45 +577,41 @@ static s32 stv0900_get_rf_level(struct stv0900_internal *i_params, i, rf_lvl = 0; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - if ((lookup != NULL) && lookup->size) { - switch (demod) { - case STV0900_DEMOD_1: - default: - agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE1), - stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE0)); - break; - case STV0900_DEMOD_2: - agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE1), - stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE0)); - break; - } + if ((lookup == NULL) || (lookup->size <= 0)) + return 0; - imin = 0; - imax = lookup->size - 1; - if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[imax].regval)) { - while ((imax - imin) > 1) { - i = (imax + imin) >> 1; + agc_gain = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), + stv0900_get_bits(intp, AGCIQ_VALUE0)); - if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[i].regval)) - imax = i; - else - imin = i; - } + imin = 0; + imax = lookup->size - 1; + if (INRANGE(lookup->table[imin].regval, agc_gain, + lookup->table[imax].regval)) { + while ((imax - imin) > 1) { + i = (imax + imin) >> 1; - rf_lvl = (((s32)agc_gain - lookup->table[imin].regval) - * (lookup->table[imax].realval - lookup->table[imin].realval) - / (lookup->table[imax].regval - lookup->table[imin].regval)) - + lookup->table[imin].realval; - } else if (agc_gain > lookup->table[0].regval) - rf_lvl = 5; - else if (agc_gain < lookup->table[lookup->size-1].regval) - rf_lvl = -100; + if (INRANGE(lookup->table[imin].regval, + agc_gain, + lookup->table[i].regval)) + imax = i; + else + imin = i; + } - } + rf_lvl = (s32)agc_gain - lookup->table[imin].regval; + rf_lvl *= (lookup->table[imax].realval - + lookup->table[imin].realval); + rf_lvl /= (lookup->table[imax].regval - + lookup->table[imin].regval); + rf_lvl += lookup->table[imin].realval; + } else if (agc_gain > lookup->table[0].regval) + rf_lvl = 5; + else if (agc_gain < lookup->table[lookup->size-1].regval) + rf_lvl = -100; - dprintk(KERN_INFO "%s: RFLevel = %d\n", __func__, rf_lvl); + dprintk("%s: RFLevel = %d\n", __func__, rf_lvl); return rf_lvl; } @@ -634,50 +623,51 @@ static int stv0900_read_signal_strength(struct dvb_frontend *fe, u16 *strength) s32 rflevel = stv0900_get_rf_level(internal, &stv0900_rf, state->demod); - *strength = (rflevel + 100) * (16383 / 105); + rflevel = (rflevel + 100) * (65535 / 70); + if (rflevel < 0) + rflevel = 0; + + if (rflevel > 65535) + rflevel = 65535; + + *strength = rflevel; return 0; } - static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, const struct stv0900_table *lookup) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 c_n = -100, - regval, imin, imax, + s32 c_n = -100, + regval, + imin, + imax, i, - lock_flag_field, noise_field1, noise_field0; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - dmd_reg(lock_flag_field, F0900_P1_LOCK_DEFINITIF, - F0900_P2_LOCK_DEFINITIF); if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { - dmd_reg(noise_field1, F0900_P1_NOSPLHT_NORMED1, - F0900_P2_NOSPLHT_NORMED1); - dmd_reg(noise_field0, F0900_P1_NOSPLHT_NORMED0, - F0900_P2_NOSPLHT_NORMED0); + noise_field1 = NOSPLHT_NORMED1; + noise_field0 = NOSPLHT_NORMED0; } else { - dmd_reg(noise_field1, F0900_P1_NOSDATAT_NORMED1, - F0900_P2_NOSDATAT_NORMED1); - dmd_reg(noise_field0, F0900_P1_NOSDATAT_NORMED0, - F0900_P2_NOSDATAT_NORMED0); + noise_field1 = NOSDATAT_NORMED1; + noise_field0 = NOSDATAT_NORMED0; } - if (stv0900_get_bits(i_params, lock_flag_field)) { + if (stv0900_get_bits(intp, LOCK_DEFINITIF)) { if ((lookup != NULL) && lookup->size) { regval = 0; msleep(5); for (i = 0; i < 16; i++) { - regval += MAKEWORD(stv0900_get_bits(i_params, + regval += MAKEWORD(stv0900_get_bits(intp, noise_field1), - stv0900_get_bits(i_params, + stv0900_get_bits(intp, noise_field0)); msleep(1); } @@ -715,10 +705,9 @@ static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; u8 err_val1, err_val0; - s32 err_field1, err_field0; u32 header_err_val = 0; *ucblocks = 0x0; @@ -726,24 +715,14 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) /* DVB-S2 delineator errors count */ /* retreiving number for errnous headers */ - dmd_reg(err_field0, R0900_P1_BBFCRCKO0, - R0900_P2_BBFCRCKO0); - dmd_reg(err_field1, R0900_P1_BBFCRCKO1, - R0900_P2_BBFCRCKO1); - - err_val1 = stv0900_read_reg(i_params, err_field1); - err_val0 = stv0900_read_reg(i_params, err_field0); - header_err_val = (err_val1<<8) | err_val0; + err_val1 = stv0900_read_reg(intp, BBFCRCKO1); + err_val0 = stv0900_read_reg(intp, BBFCRCKO0); + header_err_val = (err_val1 << 8) | err_val0; /* retreiving number for errnous packets */ - dmd_reg(err_field0, R0900_P1_UPCRCKO0, - R0900_P2_UPCRCKO0); - dmd_reg(err_field1, R0900_P1_UPCRCKO1, - R0900_P2_UPCRCKO1); - - err_val1 = stv0900_read_reg(i_params, err_field1); - err_val0 = stv0900_read_reg(i_params, err_field0); - *ucblocks = (err_val1<<8) | err_val0; + err_val1 = stv0900_read_reg(intp, UPCRCKO1); + err_val0 = stv0900_read_reg(intp, UPCRCKO0); + *ucblocks = (err_val1 << 8) | err_val0; *ucblocks += header_err_val; } @@ -752,33 +731,27 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr) { - *snr = stv0900_carr_get_quality(fe, + s32 snrlcl = stv0900_carr_get_quality(fe, (const struct stv0900_table *)&stv0900_s2_cn); - *snr += 30; - *snr *= (16383 / 1030); + snrlcl = (snrlcl + 30) * 384; + if (snrlcl < 0) + snrlcl = 0; + + if (snrlcl > 65535) + snrlcl = 65535; + + *snr = snrlcl; return 0; } -static u32 stv0900_get_ber(struct stv0900_internal *i_params, +static u32 stv0900_get_ber(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { u32 ber = 10000000, i; - s32 dmd_state_reg; s32 demod_state; - s32 vstatus_reg; - s32 prvit_field; - s32 pdel_status_reg; - s32 pdel_lock_field; - - dmd_reg(dmd_state_reg, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(vstatus_reg, R0900_P1_VSTATUSVIT, R0900_P2_VSTATUSVIT); - dmd_reg(prvit_field, F0900_P1_PRFVIT, F0900_P2_PRFVIT); - dmd_reg(pdel_status_reg, R0900_P1_PDELSTATUS1, R0900_P2_PDELSTATUS1); - dmd_reg(pdel_lock_field, F0900_P1_PKTDELIN_LOCK, - F0900_P2_PKTDELIN_LOCK); - demod_state = stv0900_get_bits(i_params, dmd_state_reg); + demod_state = stv0900_get_bits(intp, HEADER_MODE); switch (demod_state) { case STV0900_SEARCH: @@ -790,11 +763,11 @@ static u32 stv0900_get_ber(struct stv0900_internal *i_params, ber = 0; for (i = 0; i < 5; i++) { msleep(5); - ber += stv0900_get_err_count(i_params, 0, demod); + ber += stv0900_get_err_count(intp, 0, demod); } ber /= 5; - if (stv0900_get_bits(i_params, prvit_field)) { + if (stv0900_get_bits(intp, PRFVIT)) { ber *= 9766; ber = ber >> 13; } @@ -804,11 +777,11 @@ static u32 stv0900_get_ber(struct stv0900_internal *i_params, ber = 0; for (i = 0; i < 5; i++) { msleep(5); - ber += stv0900_get_err_count(i_params, 0, demod); + ber += stv0900_get_err_count(intp, 0, demod); } ber /= 5; - if (stv0900_get_bits(i_params, pdel_lock_field)) { + if (stv0900_get_bits(intp, PKTDELIN_LOCK)) { ber *= 9766; ber = ber >> 13; } @@ -829,20 +802,16 @@ static int stv0900_read_ber(struct dvb_frontend *fe, u32 *ber) return 0; } -int stv0900_get_demod_lock(struct stv0900_internal *i_params, +int stv0900_get_demod_lock(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod, s32 time_out) { s32 timer = 0, - lock = 0, - header_field, - lock_field; + lock = 0; enum fe_stv0900_search_state dmd_state; - dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF); while ((timer < time_out) && (lock == 0)) { - dmd_state = stv0900_get_bits(i_params, header_field); + dmd_state = stv0900_get_bits(intp, HEADER_MODE); dprintk("Demod State = %d\n", dmd_state); switch (dmd_state) { case STV0900_SEARCH: @@ -852,7 +821,7 @@ int stv0900_get_demod_lock(struct stv0900_internal *i_params, break; case STV0900_DVBS2_FOUND: case STV0900_DVBS_FOUND: - lock = stv0900_get_bits(i_params, lock_field); + lock = stv0900_get_bits(intp, LOCK_DEFINITIF); break; } @@ -870,56 +839,40 @@ int stv0900_get_demod_lock(struct stv0900_internal *i_params, return lock; } -void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params, +void stv0900_stop_all_s2_modcod(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { s32 regflist, i; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - dmd_reg(regflist, R0900_P1_MODCODLST0, R0900_P2_MODCODLST0); + regflist = MODCODLST0; for (i = 0; i < 16; i++) - stv0900_write_reg(i_params, regflist + i, 0xff); + stv0900_write_reg(intp, regflist + i, 0xff); } -void stv0900_activate_s2_modcode(struct stv0900_internal *i_params, +void stv0900_activate_s2_modcod(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { u32 matype, - mod_code, - fmod, - reg_index, - field_index; + mod_code, + fmod, + reg_index, + field_index; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - if (i_params->chip_id <= 0x11) { + if (intp->chip_id <= 0x11) { msleep(5); - switch (demod) { - case STV0900_DEMOD_1: - default: - mod_code = stv0900_read_reg(i_params, - R0900_P1_PLHMODCOD); - matype = mod_code & 0x3; - mod_code = (mod_code & 0x7f) >> 2; - - reg_index = R0900_P1_MODCODLSTF - mod_code / 2; - field_index = mod_code % 2; - break; - case STV0900_DEMOD_2: - mod_code = stv0900_read_reg(i_params, - R0900_P2_PLHMODCOD); - matype = mod_code & 0x3; - mod_code = (mod_code & 0x7f) >> 2; - - reg_index = R0900_P2_MODCODLSTF - mod_code / 2; - field_index = mod_code % 2; - break; - } + mod_code = stv0900_read_reg(intp, PLHMODCOD); + matype = mod_code & 0x3; + mod_code = (mod_code & 0x7f) >> 2; + reg_index = MODCODLSTF - mod_code / 2; + field_index = mod_code % 2; switch (matype) { case 0: @@ -938,70 +891,41 @@ void stv0900_activate_s2_modcode(struct stv0900_internal *i_params, } if ((INRANGE(STV0900_QPSK_12, mod_code, STV0900_8PSK_910)) - && (matype <= 1)) { + && (matype <= 1)) { if (field_index == 0) - stv0900_write_reg(i_params, reg_index, + stv0900_write_reg(intp, reg_index, 0xf0 | fmod); else - stv0900_write_reg(i_params, reg_index, + stv0900_write_reg(intp, reg_index, (fmod << 4) | 0xf); } - } else if (i_params->chip_id >= 0x12) { - switch (demod) { - case STV0900_DEMOD_1: - default: - for (reg_index = 0; reg_index < 7; reg_index++) - stv0900_write_reg(i_params, R0900_P1_MODCODLST0 + reg_index, 0xff); - stv0900_write_reg(i_params, R0900_P1_MODCODLSTE, 0xff); - stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0xcf); - for (reg_index = 0; reg_index < 8; reg_index++) - stv0900_write_reg(i_params, R0900_P1_MODCODLST7 + reg_index, 0xcc); + } else if (intp->chip_id >= 0x12) { + for (reg_index = 0; reg_index < 7; reg_index++) + stv0900_write_reg(intp, MODCODLST0 + reg_index, 0xff); - break; - case STV0900_DEMOD_2: - for (reg_index = 0; reg_index < 7; reg_index++) - stv0900_write_reg(i_params, R0900_P2_MODCODLST0 + reg_index, 0xff); + stv0900_write_reg(intp, MODCODLSTE, 0xff); + stv0900_write_reg(intp, MODCODLSTF, 0xcf); + for (reg_index = 0; reg_index < 8; reg_index++) + stv0900_write_reg(intp, MODCODLST7 + reg_index, 0xcc); - stv0900_write_reg(i_params, R0900_P2_MODCODLSTE, 0xff); - stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0xcf); - for (reg_index = 0; reg_index < 8; reg_index++) - stv0900_write_reg(i_params, R0900_P2_MODCODLST7 + reg_index, 0xcc); - - break; - } } } -void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params, +void stv0900_activate_s2_modcod_single(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { u32 reg_index; - dprintk(KERN_INFO "%s\n", __func__); - - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_reg(i_params, R0900_P1_MODCODLST0, 0xff); - stv0900_write_reg(i_params, R0900_P1_MODCODLST1, 0xf0); - stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0x0f); - for (reg_index = 0; reg_index < 13; reg_index++) - stv0900_write_reg(i_params, - R0900_P1_MODCODLST2 + reg_index, 0); + dprintk("%s\n", __func__); - break; - case STV0900_DEMOD_2: - stv0900_write_reg(i_params, R0900_P2_MODCODLST0, 0xff); - stv0900_write_reg(i_params, R0900_P2_MODCODLST1, 0xf0); - stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0x0f); - for (reg_index = 0; reg_index < 13; reg_index++) - stv0900_write_reg(i_params, - R0900_P2_MODCODLST2 + reg_index, 0); + stv0900_write_reg(intp, MODCODLST0, 0xff); + stv0900_write_reg(intp, MODCODLST1, 0xf0); + stv0900_write_reg(intp, MODCODLSTF, 0x0f); + for (reg_index = 0; reg_index < 13; reg_index++) + stv0900_write_reg(intp, MODCODLST2 + reg_index, 0); - break; - } } static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe) @@ -1012,7 +936,7 @@ static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe) static int stb0900_set_property(struct dvb_frontend *fe, struct dtv_property *tvp) { - dprintk(KERN_INFO "%s(..)\n", __func__); + dprintk("%s(..)\n", __func__); return 0; } @@ -1020,166 +944,123 @@ static int stb0900_set_property(struct dvb_frontend *fe, static int stb0900_get_property(struct dvb_frontend *fe, struct dtv_property *tvp) { - dprintk(KERN_INFO "%s(..)\n", __func__); + dprintk("%s(..)\n", __func__); return 0; } -void stv0900_start_search(struct stv0900_internal *i_params, +void stv0900_start_search(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1f); - - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xaa); - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55); - - if (i_params->dmd1_symbol_rate <= 5000000) { - stv0900_write_reg(i_params, R0900_P1_CARCFG, 0x44); - stv0900_write_reg(i_params, R0900_P1_CFRUP1, 0x0f); - stv0900_write_reg(i_params, R0900_P1_CFRUP0, 0xff); - stv0900_write_reg(i_params, R0900_P1_CFRLOW1, 0xf0); - stv0900_write_reg(i_params, R0900_P1_CFRLOW0, 0x00); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68); + u32 freq; + s16 freq_s16 ; + + stv0900_write_bits(intp, DEMOD_MODE, 0x1f); + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0xaa); + + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x55); + + if (intp->chip_id <= 0x20) { + if (intp->symbol_rate[0] <= 5000000) { + stv0900_write_reg(intp, CARCFG, 0x44); + stv0900_write_reg(intp, CFRUP1, 0x0f); + stv0900_write_reg(intp, CFRUP0, 0xff); + stv0900_write_reg(intp, CFRLOW1, 0xf0); + stv0900_write_reg(intp, CFRLOW0, 0x00); + stv0900_write_reg(intp, RTCS2, 0x68); } else { - stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xc4); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44); - } - - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0); - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41); - stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41); - - if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) { - stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82); - stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0); - } + stv0900_write_reg(intp, CARCFG, 0xc4); + stv0900_write_reg(intp, RTCS2, 0x44); } - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xe0); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xc0); - stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - stv0900_write_bits(i_params, F0900_P1_S1S2_SEQUENTIAL, 0); - stv0900_write_reg(i_params, R0900_P1_RTC, 0x88); - if (i_params->chip_id >= 0x20) { - if (i_params->dmd1_symbol_rate < 2000000) { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x39); - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x40); - } - - if (i_params->dmd1_symbol_rate < 10000000) { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4c); - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20); - } else { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4b); - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20); - } + } else { /*cut 3.0 above*/ + if (intp->symbol_rate[demod] <= 5000000) + stv0900_write_reg(intp, RTCS2, 0x68); + else + stv0900_write_reg(intp, RTCS2, 0x44); + stv0900_write_reg(intp, CARCFG, 0x46); + if (intp->srch_algo[demod] == STV0900_WARM_START) { + freq = 1000 << 16; + freq /= (intp->mclk / 1000); + freq_s16 = (s16)freq; } else { - if (i_params->dmd1_symbol_rate < 10000000) - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xef); + freq = (intp->srch_range[demod] / 2000); + if (intp->symbol_rate[demod] <= 5000000) + freq += 80; else - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed); - } + freq += 600; - switch (i_params->dmd1_srch_algo) { - case STV0900_WARM_START: - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - break; - case STV0900_COLD_START: - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15); - break; - default: - break; - } - - break; - case STV0900_DEMOD_2: - stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1f); - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xaa); - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55); - - if (i_params->dmd2_symbol_rate <= 5000000) { - stv0900_write_reg(i_params, R0900_P2_CARCFG, 0x44); - stv0900_write_reg(i_params, R0900_P2_CFRUP1, 0x0f); - stv0900_write_reg(i_params, R0900_P2_CFRUP0, 0xff); - stv0900_write_reg(i_params, R0900_P2_CFRLOW1, 0xf0); - stv0900_write_reg(i_params, R0900_P2_CFRLOW0, 0x00); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68); - } else { - stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xc4); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44); + freq = freq << 16; + freq /= (intp->mclk / 1000); + freq_s16 = (s16)freq; } - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0); + stv0900_write_bits(intp, CFR_UP1, MSB(freq_s16)); + stv0900_write_bits(intp, CFR_UP0, LSB(freq_s16)); + freq_s16 *= (-1); + stv0900_write_bits(intp, CFR_LOW1, MSB(freq_s16)); + stv0900_write_bits(intp, CFR_LOW0, LSB(freq_s16)); + } - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41); - stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41); - if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) { - stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82); - stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0); - } - } + stv0900_write_reg(intp, CFRINIT1, 0); + stv0900_write_reg(intp, CFRINIT0, 0); - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xe0); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xc0); - stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - stv0900_write_bits(i_params, F0900_P2_S1S2_SEQUENTIAL, 0); - stv0900_write_reg(i_params, R0900_P2_RTC, 0x88); - if (i_params->chip_id >= 0x20) { - if (i_params->dmd2_symbol_rate < 2000000) { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x39); - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x40); - } + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, EQUALCFG, 0x41); + stv0900_write_reg(intp, FFECFG, 0x41); - if (i_params->dmd2_symbol_rate < 10000000) { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4c); - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20); - } else { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4b); - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20); - } + if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || + (intp->srch_standard[demod] == STV0900_SEARCH_DSS) || + (intp->srch_standard[demod] == STV0900_AUTO_SEARCH)) { + stv0900_write_reg(intp, VITSCALE, + 0x82); + stv0900_write_reg(intp, VAVSRVIT, 0x0); + } + } + stv0900_write_reg(intp, SFRSTEP, 0x00); + stv0900_write_reg(intp, TMGTHRISE, 0xe0); + stv0900_write_reg(intp, TMGTHFALL, 0xc0); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_bits(intp, S1S2_SEQUENTIAL, 0); + stv0900_write_reg(intp, RTC, 0x88); + if (intp->chip_id >= 0x20) { + if (intp->symbol_rate[demod] < 2000000) { + if (intp->chip_id <= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x39); + else /*cut 3.0*/ + stv0900_write_reg(intp, CARFREQ, 0x89); + + stv0900_write_reg(intp, CARHDR, 0x40); + } else if (intp->symbol_rate[demod] < 10000000) { + stv0900_write_reg(intp, CARFREQ, 0x4c); + stv0900_write_reg(intp, CARHDR, 0x20); } else { - if (i_params->dmd2_symbol_rate < 10000000) - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xef); - else - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed); + stv0900_write_reg(intp, CARFREQ, 0x4b); + stv0900_write_reg(intp, CARHDR, 0x20); } - switch (i_params->dmd2_srch_algo) { - case STV0900_WARM_START: - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - break; - case STV0900_COLD_START: - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15); - break; - default: - break; - } + } else { + if (intp->symbol_rate[demod] < 10000000) + stv0900_write_reg(intp, CARFREQ, 0xef); + else + stv0900_write_reg(intp, CARFREQ, 0xed); + } + switch (intp->srch_algo[demod]) { + case STV0900_WARM_START: + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x18); + break; + case STV0900_COLD_START: + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + break; + default: break; } } @@ -1188,33 +1069,40 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, s32 pilot, u8 chip_id) { u8 aclc_value = 0x29; - s32 i; - const struct stv0900_car_loop_optim *car_loop_s2; - - dprintk(KERN_INFO "%s\n", __func__); - - if (chip_id <= 0x12) - car_loop_s2 = FE_STV0900_S2CarLoop; - else if (chip_id == 0x20) - car_loop_s2 = FE_STV0900_S2CarLoopCut20; - else - car_loop_s2 = FE_STV0900_S2CarLoop; + s32 i; + const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2; + + dprintk("%s\n", __func__); + + if (chip_id <= 0x12) { + cls2 = FE_STV0900_S2CarLoop; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; + cllas2 = FE_STV0900_S2APSKCarLoopCut30; + } else if (chip_id == 0x20) { + cls2 = FE_STV0900_S2CarLoopCut20; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut20; + cllas2 = FE_STV0900_S2APSKCarLoopCut20; + } else { + cls2 = FE_STV0900_S2CarLoopCut30; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; + cllas2 = FE_STV0900_S2APSKCarLoopCut30; + } if (modcode < STV0900_QPSK_12) { i = 0; - while ((i < 3) && (modcode != FE_STV0900_S2LowQPCarLoopCut20[i].modcode)) + while ((i < 3) && (modcode != cllqs2[i].modcode)) i++; if (i >= 3) i = 2; } else { i = 0; - while ((i < 14) && (modcode != car_loop_s2[i].modcode)) + while ((i < 14) && (modcode != cls2[i].modcode)) i++; if (i >= 14) { i = 0; - while ((i < 11) && (modcode != FE_STV0900_S2APSKCarLoopCut20[i].modcode)) + while ((i < 11) && (modcode != cllas2[i].modcode)) i++; if (i >= 11) @@ -1225,76 +1113,82 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, if (modcode <= STV0900_QPSK_25) { if (pilot) { if (srate <= 3000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_2; + aclc_value = cllqs2[i].car_loop_pilots_on_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_5; + aclc_value = cllqs2[i].car_loop_pilots_on_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_10; + aclc_value = cllqs2[i].car_loop_pilots_on_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_20; + aclc_value = cllqs2[i].car_loop_pilots_on_20; else - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_30; + aclc_value = cllqs2[i].car_loop_pilots_on_30; } else { if (srate <= 3000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_2; + aclc_value = cllqs2[i].car_loop_pilots_off_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_5; + aclc_value = cllqs2[i].car_loop_pilots_off_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_10; + aclc_value = cllqs2[i].car_loop_pilots_off_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_20; + aclc_value = cllqs2[i].car_loop_pilots_off_20; else - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_30; + aclc_value = cllqs2[i].car_loop_pilots_off_30; } } else if (modcode <= STV0900_8PSK_910) { if (pilot) { if (srate <= 3000000) - aclc_value = car_loop_s2[i].car_loop_pilots_on_2; + aclc_value = cls2[i].car_loop_pilots_on_2; else if (srate <= 7000000) - aclc_value = car_loop_s2[i].car_loop_pilots_on_5; + aclc_value = cls2[i].car_loop_pilots_on_5; else if (srate <= 15000000) - aclc_value = car_loop_s2[i].car_loop_pilots_on_10; + aclc_value = cls2[i].car_loop_pilots_on_10; else if (srate <= 25000000) - aclc_value = car_loop_s2[i].car_loop_pilots_on_20; + aclc_value = cls2[i].car_loop_pilots_on_20; else - aclc_value = car_loop_s2[i].car_loop_pilots_on_30; + aclc_value = cls2[i].car_loop_pilots_on_30; } else { if (srate <= 3000000) - aclc_value = car_loop_s2[i].car_loop_pilots_off_2; + aclc_value = cls2[i].car_loop_pilots_off_2; else if (srate <= 7000000) - aclc_value = car_loop_s2[i].car_loop_pilots_off_5; + aclc_value = cls2[i].car_loop_pilots_off_5; else if (srate <= 15000000) - aclc_value = car_loop_s2[i].car_loop_pilots_off_10; + aclc_value = cls2[i].car_loop_pilots_off_10; else if (srate <= 25000000) - aclc_value = car_loop_s2[i].car_loop_pilots_off_20; + aclc_value = cls2[i].car_loop_pilots_off_20; else - aclc_value = car_loop_s2[i].car_loop_pilots_off_30; + aclc_value = cls2[i].car_loop_pilots_off_30; } } else { if (srate <= 3000000) - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_2; + aclc_value = cllas2[i].car_loop_pilots_on_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_5; + aclc_value = cllas2[i].car_loop_pilots_on_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_10; + aclc_value = cllas2[i].car_loop_pilots_on_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_20; + aclc_value = cllas2[i].car_loop_pilots_on_20; else - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_30; + aclc_value = cllas2[i].car_loop_pilots_on_30; } return aclc_value; } -u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modulation, u8 chip_id) +u8 stv0900_get_optim_short_carr_loop(s32 srate, + enum fe_stv0900_modulation modulation, + u8 chip_id) { + const struct stv0900_short_frames_car_loop_optim *s2scl; + const struct stv0900_short_frames_car_loop_optim_vs_mod *s2sclc30; s32 mod_index = 0; - u8 aclc_value = 0x0b; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); + + s2scl = FE_STV0900_S2ShortCarLoop; + s2sclc30 = FE_STV0900_S2ShortCarLoopCut30; switch (modulation) { case STV0900_QPSK: @@ -1312,75 +1206,116 @@ u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modul break; } - switch (chip_id) { - case 0x20: + if (chip_id >= 0x30) { if (srate <= 3000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_2; + aclc_value = s2sclc30[mod_index].car_loop_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_5; + aclc_value = s2sclc30[mod_index].car_loop_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_10; + aclc_value = s2sclc30[mod_index].car_loop_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_20; + aclc_value = s2sclc30[mod_index].car_loop_20; else - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_30; + aclc_value = s2sclc30[mod_index].car_loop_30; - break; - case 0x12: - default: + } else if (chip_id >= 0x20) { if (srate <= 3000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_2; + aclc_value = s2scl[mod_index].car_loop_cut20_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_5; + aclc_value = s2scl[mod_index].car_loop_cut20_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_10; + aclc_value = s2scl[mod_index].car_loop_cut20_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_20; + aclc_value = s2scl[mod_index].car_loop_cut20_20; else - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_30; + aclc_value = s2scl[mod_index].car_loop_cut20_30; + + } else { + if (srate <= 3000000) + aclc_value = s2scl[mod_index].car_loop_cut12_2; + else if (srate <= 7000000) + aclc_value = s2scl[mod_index].car_loop_cut12_5; + else if (srate <= 15000000) + aclc_value = s2scl[mod_index].car_loop_cut12_10; + else if (srate <= 25000000) + aclc_value = s2scl[mod_index].car_loop_cut12_20; + else + aclc_value = s2scl[mod_index].car_loop_cut12_30; - break; } return aclc_value; } -static enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *i_params, +static +enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp, enum fe_stv0900_demod_mode LDPC_Mode, enum fe_stv0900_demod_num demod) { enum fe_stv0900_error error = STV0900_NO_ERROR; + s32 reg_ind; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); switch (LDPC_Mode) { case STV0900_DUAL: default: - if ((i_params->demod_mode != STV0900_DUAL) - || (stv0900_get_bits(i_params, F0900_DDEMOD) != 1)) { - stv0900_write_reg(i_params, R0900_GENCFG, 0x1d); - - i_params->demod_mode = STV0900_DUAL; - - stv0900_write_bits(i_params, F0900_FRESFEC, 1); - stv0900_write_bits(i_params, F0900_FRESFEC, 0); + if ((intp->demod_mode != STV0900_DUAL) + || (stv0900_get_bits(intp, F0900_DDEMOD) != 1)) { + stv0900_write_reg(intp, R0900_GENCFG, 0x1d); + + intp->demod_mode = STV0900_DUAL; + + stv0900_write_bits(intp, F0900_FRESFEC, 1); + stv0900_write_bits(intp, F0900_FRESFEC, 0); + + for (reg_ind = 0; reg_ind < 7; reg_ind++) + stv0900_write_reg(intp, + R0900_P1_MODCODLST0 + reg_ind, + 0xff); + for (reg_ind = 0; reg_ind < 8; reg_ind++) + stv0900_write_reg(intp, + R0900_P1_MODCODLST7 + reg_ind, + 0xcc); + + stv0900_write_reg(intp, R0900_P1_MODCODLSTE, 0xff); + stv0900_write_reg(intp, R0900_P1_MODCODLSTF, 0xcf); + + for (reg_ind = 0; reg_ind < 7; reg_ind++) + stv0900_write_reg(intp, + R0900_P2_MODCODLST0 + reg_ind, + 0xff); + for (reg_ind = 0; reg_ind < 8; reg_ind++) + stv0900_write_reg(intp, + R0900_P2_MODCODLST7 + reg_ind, + 0xcc); + + stv0900_write_reg(intp, R0900_P2_MODCODLSTE, 0xff); + stv0900_write_reg(intp, R0900_P2_MODCODLSTF, 0xcf); } break; case STV0900_SINGLE: - if (demod == STV0900_DEMOD_2) - stv0900_write_reg(i_params, R0900_GENCFG, 0x06); - else - stv0900_write_reg(i_params, R0900_GENCFG, 0x04); + if (demod == STV0900_DEMOD_2) { + stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_1); + stv0900_activate_s2_modcod_single(intp, + STV0900_DEMOD_2); + stv0900_write_reg(intp, R0900_GENCFG, 0x06); + } else { + stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_2); + stv0900_activate_s2_modcod_single(intp, + STV0900_DEMOD_1); + stv0900_write_reg(intp, R0900_GENCFG, 0x04); + } - i_params->demod_mode = STV0900_SINGLE; + intp->demod_mode = STV0900_SINGLE; - stv0900_write_bits(i_params, F0900_FRESFEC, 1); - stv0900_write_bits(i_params, F0900_FRESFEC, 0); - stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1); - stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0); - stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1); - stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0); + stv0900_write_bits(intp, F0900_FRESFEC, 1); + stv0900_write_bits(intp, F0900_FRESFEC, 0); + stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 1); + stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 0); + stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 1); + stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 0); break; } @@ -1393,131 +1328,131 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, struct stv0900_state *state = fe->demodulator_priv; enum fe_stv0900_error error = STV0900_NO_ERROR; enum fe_stv0900_error demodError = STV0900_NO_ERROR; + struct stv0900_internal *intp = NULL; + int selosci, i; struct stv0900_inode *temp_int = find_inode(state->i2c_adap, state->config->demod_address); - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - if (temp_int != NULL) { + if ((temp_int != NULL) && (p_init->demod_mode == STV0900_DUAL)) { state->internal = temp_int->internal; (state->internal->dmds_used)++; - dprintk(KERN_INFO "%s: Find Internal Structure!\n", __func__); + dprintk("%s: Find Internal Structure!\n", __func__); return STV0900_NO_ERROR; } else { - state->internal = kmalloc(sizeof(struct stv0900_internal), GFP_KERNEL); + state->internal = kmalloc(sizeof(struct stv0900_internal), + GFP_KERNEL); temp_int = append_internal(state->internal); state->internal->dmds_used = 1; state->internal->i2c_adap = state->i2c_adap; state->internal->i2c_addr = state->config->demod_address; state->internal->clkmode = state->config->clkmode; state->internal->errs = STV0900_NO_ERROR; - dprintk(KERN_INFO "%s: Create New Internal Structure!\n", __func__); + dprintk("%s: Create New Internal Structure!\n", __func__); } - if (state->internal != NULL) { - demodError = stv0900_initialize(state->internal); - if (demodError == STV0900_NO_ERROR) { - error = STV0900_NO_ERROR; - } else { - if (demodError == STV0900_INVALID_HANDLE) - error = STV0900_INVALID_HANDLE; - else - error = STV0900_I2C_ERROR; - } + if (state->internal == NULL) { + error = STV0900_INVALID_HANDLE; + return error; + } - if (state->internal != NULL) { - if (error == STV0900_NO_ERROR) { - state->internal->demod_mode = p_init->demod_mode; - - stv0900_st_dvbs2_single(state->internal, state->internal->demod_mode, STV0900_DEMOD_1); - - state->internal->chip_id = stv0900_read_reg(state->internal, R0900_MID); - state->internal->rolloff = p_init->rolloff; - state->internal->quartz = p_init->dmd_ref_clk; - - stv0900_write_bits(state->internal, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); - stv0900_write_bits(state->internal, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); - - state->internal->ts_config = p_init->ts_config; - if (state->internal->ts_config == NULL) - stv0900_set_ts_parallel_serial(state->internal, - p_init->path1_ts_clock, - p_init->path2_ts_clock); - else { - for (i = 0; state->internal->ts_config[i].addr != 0xffff; i++) - stv0900_write_reg(state->internal, - state->internal->ts_config[i].addr, - state->internal->ts_config[i].val); - - stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 1); - stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 0); - stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 1); - stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 0); - } + demodError = stv0900_initialize(state->internal); + if (demodError == STV0900_NO_ERROR) { + error = STV0900_NO_ERROR; + } else { + if (demodError == STV0900_INVALID_HANDLE) + error = STV0900_INVALID_HANDLE; + else + error = STV0900_I2C_ERROR; - stv0900_write_bits(state->internal, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); - switch (p_init->tuner1_adc) { - case 1: - stv0900_write_reg(state->internal, R0900_TSTTNR1, 0x26); - break; - default: - break; - } + return error; + } - stv0900_write_bits(state->internal, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress); - switch (p_init->tuner2_adc) { - case 1: - stv0900_write_reg(state->internal, R0900_TSTTNR3, 0x26); - break; - default: - break; - } + if (state->internal == NULL) { + error = STV0900_INVALID_HANDLE; + return error; + } - stv0900_write_bits(state->internal, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inversion); - stv0900_write_bits(state->internal, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inversion); - stv0900_set_mclk(state->internal, 135000000); - msleep(3); - - switch (state->internal->clkmode) { - case 0: - case 2: - stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | state->internal->clkmode); - break; - default: - selosci = 0x02 & stv0900_read_reg(state->internal, R0900_SYNTCTRL); - stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | selosci); - break; - } - msleep(3); + intp = state->internal; - state->internal->mclk = stv0900_get_mclk_freq(state->internal, state->internal->quartz); - if (state->internal->errs) - error = STV0900_I2C_ERROR; - } - } else { - error = STV0900_INVALID_HANDLE; - } + intp->demod_mode = p_init->demod_mode; + stv0900_st_dvbs2_single(intp, intp->demod_mode, STV0900_DEMOD_1); + intp->chip_id = stv0900_read_reg(intp, R0900_MID); + intp->rolloff = p_init->rolloff; + intp->quartz = p_init->dmd_ref_clk; + + stv0900_write_bits(intp, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); + stv0900_write_bits(intp, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); + + intp->ts_config = p_init->ts_config; + if (intp->ts_config == NULL) + stv0900_set_ts_parallel_serial(intp, + p_init->path1_ts_clock, + p_init->path2_ts_clock); + else { + for (i = 0; intp->ts_config[i].addr != 0xffff; i++) + stv0900_write_reg(intp, + intp->ts_config[i].addr, + intp->ts_config[i].val); + + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); + } + + stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); + switch (p_init->tuner1_adc) { + case 1: + stv0900_write_reg(intp, R0900_TSTTNR1, 0x26); + break; + default: + break; + } + + stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress); + switch (p_init->tuner2_adc) { + case 1: + stv0900_write_reg(intp, R0900_TSTTNR3, 0x26); + break; + default: + break; + } + + stv0900_write_bits(intp, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inv); + stv0900_write_bits(intp, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inv); + stv0900_set_mclk(intp, 135000000); + msleep(3); + + switch (intp->clkmode) { + case 0: + case 2: + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | intp->clkmode); + break; + default: + selosci = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | selosci); + break; } + msleep(3); + + intp->mclk = stv0900_get_mclk_freq(intp, intp->quartz); + if (intp->errs) + error = STV0900_I2C_ERROR; return error; } -static int stv0900_status(struct stv0900_internal *i_params, +static int stv0900_status(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { enum fe_stv0900_search_state demod_state; - s32 mode_field, delin_field, lock_field, fifo_field, lockedvit_field; int locked = FALSE; - dmd_reg(mode_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF); - dmd_reg(delin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK); - dmd_reg(fifo_field, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK); - dmd_reg(lockedvit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT); - - demod_state = stv0900_get_bits(i_params, mode_field); + demod_state = stv0900_get_bits(intp, HEADER_MODE); switch (demod_state) { case STV0900_SEARCH: case STV0900_PLH_DETECTED: @@ -1525,17 +1460,19 @@ static int stv0900_status(struct stv0900_internal *i_params, locked = FALSE; break; case STV0900_DVBS2_FOUND: - locked = stv0900_get_bits(i_params, lock_field) && - stv0900_get_bits(i_params, delin_field) && - stv0900_get_bits(i_params, fifo_field); + locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && + stv0900_get_bits(intp, PKTDELIN_LOCK) && + stv0900_get_bits(intp, TSFIFO_LINEOK); break; case STV0900_DVBS_FOUND: - locked = stv0900_get_bits(i_params, lock_field) && - stv0900_get_bits(i_params, lockedvit_field) && - stv0900_get_bits(i_params, fifo_field); + locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && + stv0900_get_bits(intp, LOCKEDVIT) && + stv0900_get_bits(intp, TSFIFO_LINEOK); break; } + dprintk("%s: locked = %d\n", __func__, locked); + return locked; } @@ -1543,7 +1480,8 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct stv0900_search_params p_search; @@ -1551,10 +1489,16 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe, enum fe_stv0900_error error = STV0900_NO_ERROR; - dprintk(KERN_INFO "%s: ", __func__); + dprintk("%s: ", __func__); + + if (!(INRANGE(100000, c->symbol_rate, 70000000))) + return DVBFE_ALGO_SEARCH_FAILED; + + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); p_result.locked = FALSE; - p_search.path = state->demod; + p_search.path = demod; p_search.frequency = c->frequency; p_search.symbol_rate = c->symbol_rate; p_search.search_range = 10000000; @@ -1563,103 +1507,47 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe, p_search.iq_inversion = STV0900_IQ_AUTO; p_search.search_algo = STV0900_BLIND_SEARCH; - if ((INRANGE(100000, p_search.symbol_rate, 70000000)) && - (INRANGE(100000, p_search.search_range, 50000000))) { - switch (p_search.path) { - case STV0900_DEMOD_1: - default: - i_params->dmd1_srch_standard = p_search.standard; - i_params->dmd1_symbol_rate = p_search.symbol_rate; - i_params->dmd1_srch_range = p_search.search_range; - i_params->tuner1_freq = p_search.frequency; - i_params->dmd1_srch_algo = p_search.search_algo; - i_params->dmd1_srch_iq_inv = p_search.iq_inversion; - i_params->dmd1_fec = p_search.fec; + intp->srch_standard[demod] = p_search.standard; + intp->symbol_rate[demod] = p_search.symbol_rate; + intp->srch_range[demod] = p_search.search_range; + intp->freq[demod] = p_search.frequency; + intp->srch_algo[demod] = p_search.search_algo; + intp->srch_iq_inv[demod] = p_search.iq_inversion; + intp->fec[demod] = p_search.fec; + if ((stv0900_algo(fe) == STV0900_RANGEOK) && + (intp->errs == STV0900_NO_ERROR)) { + p_result.locked = intp->result[demod].locked; + p_result.standard = intp->result[demod].standard; + p_result.frequency = intp->result[demod].frequency; + p_result.symbol_rate = intp->result[demod].symbol_rate; + p_result.fec = intp->result[demod].fec; + p_result.modcode = intp->result[demod].modcode; + p_result.pilot = intp->result[demod].pilot; + p_result.frame_len = intp->result[demod].frame_len; + p_result.spectrum = intp->result[demod].spectrum; + p_result.rolloff = intp->result[demod].rolloff; + p_result.modulation = intp->result[demod].modulation; + } else { + p_result.locked = FALSE; + switch (intp->err[demod]) { + case STV0900_I2C_ERROR: + error = STV0900_I2C_ERROR; break; - - case STV0900_DEMOD_2: - i_params->dmd2_srch_stndrd = p_search.standard; - i_params->dmd2_symbol_rate = p_search.symbol_rate; - i_params->dmd2_srch_range = p_search.search_range; - i_params->tuner2_freq = p_search.frequency; - i_params->dmd2_srch_algo = p_search.search_algo; - i_params->dmd2_srch_iq_inv = p_search.iq_inversion; - i_params->dmd2_fec = p_search.fec; + case STV0900_NO_ERROR: + default: + error = STV0900_SEARCH_FAILED; break; } - - if ((stv0900_algo(fe) == STV0900_RANGEOK) && - (i_params->errs == STV0900_NO_ERROR)) { - switch (p_search.path) { - case STV0900_DEMOD_1: - default: - p_result.locked = i_params->dmd1_rslts.locked; - p_result.standard = i_params->dmd1_rslts.standard; - p_result.frequency = i_params->dmd1_rslts.frequency; - p_result.symbol_rate = i_params->dmd1_rslts.symbol_rate; - p_result.fec = i_params->dmd1_rslts.fec; - p_result.modcode = i_params->dmd1_rslts.modcode; - p_result.pilot = i_params->dmd1_rslts.pilot; - p_result.frame_length = i_params->dmd1_rslts.frame_length; - p_result.spectrum = i_params->dmd1_rslts.spectrum; - p_result.rolloff = i_params->dmd1_rslts.rolloff; - p_result.modulation = i_params->dmd1_rslts.modulation; - break; - case STV0900_DEMOD_2: - p_result.locked = i_params->dmd2_rslts.locked; - p_result.standard = i_params->dmd2_rslts.standard; - p_result.frequency = i_params->dmd2_rslts.frequency; - p_result.symbol_rate = i_params->dmd2_rslts.symbol_rate; - p_result.fec = i_params->dmd2_rslts.fec; - p_result.modcode = i_params->dmd2_rslts.modcode; - p_result.pilot = i_params->dmd2_rslts.pilot; - p_result.frame_length = i_params->dmd2_rslts.frame_length; - p_result.spectrum = i_params->dmd2_rslts.spectrum; - p_result.rolloff = i_params->dmd2_rslts.rolloff; - p_result.modulation = i_params->dmd2_rslts.modulation; - break; - } - - } else { - p_result.locked = FALSE; - switch (p_search.path) { - case STV0900_DEMOD_1: - switch (i_params->dmd1_err) { - case STV0900_I2C_ERROR: - error = STV0900_I2C_ERROR; - break; - case STV0900_NO_ERROR: - default: - error = STV0900_SEARCH_FAILED; - break; - } - break; - case STV0900_DEMOD_2: - switch (i_params->dmd2_err) { - case STV0900_I2C_ERROR: - error = STV0900_I2C_ERROR; - break; - case STV0900_NO_ERROR: - default: - error = STV0900_SEARCH_FAILED; - break; - } - break; - } - } - - } else - error = STV0900_BAD_PARAMETER; + } if ((p_result.locked == TRUE) && (error == STV0900_NO_ERROR)) { - dprintk(KERN_INFO "Search Success\n"); + dprintk("Search Success\n"); return DVBFE_ALGO_SEARCH_SUCCESS; } else { - dprintk(KERN_INFO "Search Fail\n"); + dprintk("Search Fail\n"); return DVBFE_ALGO_SEARCH_FAILED; } - return DVBFE_ALGO_SEARCH_ERROR; } static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status) @@ -1690,16 +1578,13 @@ static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 rst_field; - - dmd_reg(rst_field, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE); if (stop_ts == TRUE) - stv0900_write_bits(i_params, rst_field, 1); + stv0900_write_bits(intp, RST_HWARE, 1); else - stv0900_write_bits(i_params, rst_field, 0); + stv0900_write_bits(intp, RST_HWARE, 0); return 0; } @@ -1707,23 +1592,19 @@ static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts) static int stv0900_diseqc_init(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 mode_field, reset_field; - dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE); - dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET); - - stv0900_write_bits(i_params, mode_field, state->config->diseqc_mode); - stv0900_write_bits(i_params, reset_field, 1); - stv0900_write_bits(i_params, reset_field, 0); + stv0900_write_bits(intp, DISTX_MODE, state->config->diseqc_mode); + stv0900_write_bits(intp, DISEQC_RESET, 1); + stv0900_write_bits(intp, DISEQC_RESET, 0); return 0; } static int stv0900_init(struct dvb_frontend *fe) { - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); stv0900_stop_ts(fe, 1); stv0900_diseqc_init(fe); @@ -1731,48 +1612,24 @@ static int stv0900_init(struct dvb_frontend *fe) return 0; } -static int stv0900_diseqc_send(struct stv0900_internal *i_params , u8 *Data, +static int stv0900_diseqc_send(struct stv0900_internal *intp , u8 *data, u32 NbData, enum fe_stv0900_demod_num demod) { s32 i = 0; - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 1); - while (i < NbData) { - while (stv0900_get_bits(i_params, F0900_P1_FIFO_FULL)) - ;/* checkpatch complains */ - stv0900_write_reg(i_params, R0900_P1_DISTXDATA, Data[i]); - i++; - } - - stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 0); - i = 0; - while ((stv0900_get_bits(i_params, F0900_P1_TX_IDLE) != 1) && (i < 10)) { - msleep(10); - i++; - } - - break; - case STV0900_DEMOD_2: - stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 1); - - while (i < NbData) { - while (stv0900_get_bits(i_params, F0900_P2_FIFO_FULL)) - ;/* checkpatch complains */ - stv0900_write_reg(i_params, R0900_P2_DISTXDATA, Data[i]); - i++; - } - - stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 0); - i = 0; - while ((stv0900_get_bits(i_params, F0900_P2_TX_IDLE) != 1) && (i < 10)) { - msleep(10); - i++; - } + stv0900_write_bits(intp, DIS_PRECHARGE, 1); + while (i < NbData) { + while (stv0900_get_bits(intp, FIFO_FULL)) + ;/* checkpatch complains */ + stv0900_write_reg(intp, DISTXDATA, data[i]); + i++; + } - break; + stv0900_write_bits(intp, DIS_PRECHARGE, 0); + i = 0; + while ((stv0900_get_bits(intp, TX_IDLE) != 1) && (i < 10)) { + msleep(10); + i++; } return 0; @@ -1792,22 +1649,21 @@ static int stv0900_send_master_cmd(struct dvb_frontend *fe, static int stv0900_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 mode_field; - u32 diseqc_fifo; + u8 data; - dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE); - dmd_reg(diseqc_fifo, R0900_P1_DISTXDATA, R0900_P2_DISTXDATA); switch (burst) { case SEC_MINI_A: - stv0900_write_bits(i_params, mode_field, 3);/* Unmodulated */ - stv0900_write_reg(i_params, diseqc_fifo, 0x00); + stv0900_write_bits(intp, DISTX_MODE, 3);/* Unmodulated */ + data = 0x00; + stv0900_diseqc_send(intp, &data, 1, state->demod); break; case SEC_MINI_B: - stv0900_write_bits(i_params, mode_field, 2);/* Modulated */ - stv0900_write_reg(i_params, diseqc_fifo, 0xff); + stv0900_write_bits(intp, DISTX_MODE, 2);/* Modulated */ + data = 0xff; + stv0900_diseqc_send(intp, &data, 1, state->demod); break; } @@ -1818,68 +1674,54 @@ static int stv0900_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; s32 i = 0; - switch (state->demod) { - case STV0900_DEMOD_1: - default: - reply->msg_len = 0; - - while ((stv0900_get_bits(i_params, F0900_P1_RX_END) != 1) && (i < 10)) { - msleep(10); - i++; - } - - if (stv0900_get_bits(i_params, F0900_P1_RX_END)) { - reply->msg_len = stv0900_get_bits(i_params, F0900_P1_FIFO_BYTENBR); - - for (i = 0; i < reply->msg_len; i++) - reply->msg[i] = stv0900_read_reg(i_params, R0900_P1_DISRXDATA); - } - break; - case STV0900_DEMOD_2: - reply->msg_len = 0; + reply->msg_len = 0; - while ((stv0900_get_bits(i_params, F0900_P2_RX_END) != 1) && (i < 10)) { - msleep(10); - i++; - } + while ((stv0900_get_bits(intp, RX_END) != 1) && (i < 10)) { + msleep(10); + i++; + } - if (stv0900_get_bits(i_params, F0900_P2_RX_END)) { - reply->msg_len = stv0900_get_bits(i_params, F0900_P2_FIFO_BYTENBR); + if (stv0900_get_bits(intp, RX_END)) { + reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR); - for (i = 0; i < reply->msg_len; i++) - reply->msg[i] = stv0900_read_reg(i_params, R0900_P2_DISRXDATA); - } - break; + for (i = 0; i < reply->msg_len; i++) + reply->msg[i] = stv0900_read_reg(intp, DISRXDATA); } return 0; } -static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t toneoff) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 mode_field, reset_field; - - dprintk(KERN_INFO "%s: %s\n", __func__, ((tone == 0) ? "Off" : "On")); - dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE); - dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET); + dprintk("%s: %s\n", __func__, ((toneoff == 0) ? "On" : "Off")); - if (tone) { - /*Set the DiseqC mode to 22Khz continues tone*/ - stv0900_write_bits(i_params, mode_field, 0); - stv0900_write_bits(i_params, reset_field, 1); + switch (toneoff) { + case SEC_TONE_ON: + /*Set the DiseqC mode to 22Khz _continues_ tone*/ + stv0900_write_bits(intp, DISTX_MODE, 0); + stv0900_write_bits(intp, DISEQC_RESET, 1); /*release DiseqC reset to enable the 22KHz tone*/ - stv0900_write_bits(i_params, reset_field, 0); - } else { - stv0900_write_bits(i_params, mode_field, 0); + stv0900_write_bits(intp, DISEQC_RESET, 0); + break; + case SEC_TONE_OFF: + /*return diseqc mode to config->diseqc_mode. + Usually it's without _continues_ tone */ + stv0900_write_bits(intp, DISTX_MODE, + state->config->diseqc_mode); /*maintain the DiseqC reset to disable the 22KHz tone*/ - stv0900_write_bits(i_params, reset_field, 1); + stv0900_write_bits(intp, DISEQC_RESET, 1); + stv0900_write_bits(intp, DISEQC_RESET, 0); + break; + default: + return -EINVAL; } return 0; @@ -1889,11 +1731,11 @@ static void stv0900_release(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); if ((--(state->internal->dmds_used)) <= 0) { - dprintk(KERN_INFO "%s: Actually removing\n", __func__); + dprintk("%s: Actually removing\n", __func__); remove_inode(state->internal); kfree(state->internal); @@ -1963,17 +1805,17 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, case 0: case 1: init_params.dmd_ref_clk = config->xtal; - init_params.demod_mode = STV0900_DUAL; + init_params.demod_mode = config->demod_mode; init_params.rolloff = STV0900_35; init_params.path1_ts_clock = config->path1_mode; init_params.tun1_maddress = config->tun1_maddress; - init_params.tun1_iq_inversion = STV0900_IQ_NORMAL; + init_params.tun1_iq_inv = STV0900_IQ_NORMAL; init_params.tuner1_adc = config->tun1_adc; init_params.path2_ts_clock = config->path2_mode; init_params.ts_config = config->ts_config_regs; init_params.tun2_maddress = config->tun2_maddress; init_params.tuner2_adc = config->tun2_adc; - init_params.tun2_iq_inversion = STV0900_IQ_SWAPPED; + init_params.tun2_iq_inv = STV0900_IQ_SWAPPED; err_stv0900 = stv0900_init_internal(&state->frontend, &init_params); diff --git a/drivers/media/dvb/frontends/stv0900_init.h b/drivers/media/dvb/frontends/stv0900_init.h index ff388b47a4e3..b684df9995d8 100644 --- a/drivers/media/dvb/frontends/stv0900_init.h +++ b/drivers/media/dvb/frontends/stv0900_init.h @@ -141,85 +141,228 @@ struct stv0900_short_frames_car_loop_optim { }; +struct stv0900_short_frames_car_loop_optim_vs_mod { + enum fe_stv0900_modulation modulation; + u8 car_loop_2; /* SR<3msps */ + u8 car_loop_5; /* 3<SR<=7msps */ + u8 car_loop_10; /* 7<SR<=15msps */ + u8 car_loop_20; /* 10<SR<=25msps */ + u8 car_loop_30; /* 10<SR<=45msps */ +}; + /* Cut 1.x Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */ -static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoop[14] = { - /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ - { STV0900_QPSK_12, 0x1C, 0x0D, 0x1B, 0x2C, 0x3A, 0x1C, 0x2A, 0x3B, 0x2A, 0x1B }, - { STV0900_QPSK_35, 0x2C, 0x0D, 0x2B, 0x2C, 0x3A, 0x0C, 0x3A, 0x2B, 0x2A, 0x0B }, - { STV0900_QPSK_23, 0x2C, 0x0D, 0x2B, 0x2C, 0x0B, 0x0C, 0x3A, 0x1B, 0x2A, 0x3A }, - { STV0900_QPSK_34, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, - { STV0900_QPSK_45, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, - { STV0900_QPSK_56, 0x0D, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, - { STV0900_QPSK_89, 0x0D, 0x0D, 0x3B, 0x1C, 0x1B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, - { STV0900_QPSK_910, 0x1D, 0x0D, 0x3B, 0x1C, 0x1B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, - { STV0900_8PSK_35, 0x29, 0x3B, 0x09, 0x2B, 0x38, 0x0B, 0x18, 0x1A, 0x08, 0x0A }, - { STV0900_8PSK_23, 0x0A, 0x3B, 0x29, 0x2B, 0x19, 0x0B, 0x38, 0x1A, 0x18, 0x0A }, - { STV0900_8PSK_34, 0x3A, 0x3B, 0x2A, 0x2B, 0x39, 0x0B, 0x19, 0x1A, 0x38, 0x0A }, - { STV0900_8PSK_56, 0x1B, 0x3B, 0x0B, 0x2B, 0x1A, 0x0B, 0x39, 0x1A, 0x19, 0x0A }, - { STV0900_8PSK_89, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, 0x0B, 0x39, 0x1A, 0x29, 0x39 }, - { STV0900_8PSK_910, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, 0x0B, 0x39, 0x1A, 0x29, 0x39 } +static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoop[14] = { + /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_QPSK_12, 0x1C, 0x0D, 0x1B, 0x2C, 0x3A, + 0x1C, 0x2A, 0x3B, 0x2A, 0x1B }, + { STV0900_QPSK_35, 0x2C, 0x0D, 0x2B, 0x2C, 0x3A, + 0x0C, 0x3A, 0x2B, 0x2A, 0x0B }, + { STV0900_QPSK_23, 0x2C, 0x0D, 0x2B, 0x2C, 0x0B, + 0x0C, 0x3A, 0x1B, 0x2A, 0x3A }, + { STV0900_QPSK_34, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_QPSK_45, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_QPSK_56, 0x0D, 0x0D, 0x3B, 0x1C, 0x0B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_QPSK_89, 0x0D, 0x0D, 0x3B, 0x1C, 0x1B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_QPSK_910, 0x1D, 0x0D, 0x3B, 0x1C, 0x1B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_8PSK_35, 0x29, 0x3B, 0x09, 0x2B, 0x38, + 0x0B, 0x18, 0x1A, 0x08, 0x0A }, + { STV0900_8PSK_23, 0x0A, 0x3B, 0x29, 0x2B, 0x19, + 0x0B, 0x38, 0x1A, 0x18, 0x0A }, + { STV0900_8PSK_34, 0x3A, 0x3B, 0x2A, 0x2B, 0x39, + 0x0B, 0x19, 0x1A, 0x38, 0x0A }, + { STV0900_8PSK_56, 0x1B, 0x3B, 0x0B, 0x2B, 0x1A, + 0x0B, 0x39, 0x1A, 0x19, 0x0A }, + { STV0900_8PSK_89, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, + 0x0B, 0x39, 0x1A, 0x29, 0x39 }, + { STV0900_8PSK_910, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, + 0x0B, 0x39, 0x1A, 0x29, 0x39 } }; /* Cut 2.0 Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */ -static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut20[14] = { - /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ - { STV0900_QPSK_12, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, 0x1F, 0x3D, 0x3E, 0x3D, 0x1E }, - { STV0900_QPSK_35, 0x2F, 0x3F, 0x2E, 0x2F, 0x3D, 0x0F, 0x0E, 0x2E, 0x3D, 0x0E }, - { STV0900_QPSK_23, 0x2F, 0x3F, 0x2E, 0x2F, 0x0E, 0x0F, 0x0E, 0x1E, 0x3D, 0x3D }, - { STV0900_QPSK_34, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, - { STV0900_QPSK_45, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, - { STV0900_QPSK_56, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, - { STV0900_QPSK_89, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, - { STV0900_QPSK_910, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, - { STV0900_8PSK_35, 0x3c, 0x0c, 0x1c, 0x3b, 0x0c, 0x3b, 0x2b, 0x2b, 0x1b, 0x2b }, - { STV0900_8PSK_23, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, 0x3b, 0x0c, 0x2b, 0x2b, 0x2b }, - { STV0900_8PSK_34, 0x0e, 0x1c, 0x3d, 0x0c, 0x0d, 0x3b, 0x2c, 0x3b, 0x0c, 0x2b }, - { STV0900_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, 0x1e, 0x3c, 0x2d, 0x2c, 0x1d }, - { STV0900_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x0d, 0x2d, 0x3c, 0x1d }, - { STV0900_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x1d, 0x2d, 0x0d, 0x1d } +static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut20[14] = { + /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_QPSK_12, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, + 0x1F, 0x3D, 0x3E, 0x3D, 0x1E }, + { STV0900_QPSK_35, 0x2F, 0x3F, 0x2E, 0x2F, 0x3D, + 0x0F, 0x0E, 0x2E, 0x3D, 0x0E }, + { STV0900_QPSK_23, 0x2F, 0x3F, 0x2E, 0x2F, 0x0E, + 0x0F, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_34, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_45, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_56, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_89, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_910, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_8PSK_35, 0x3c, 0x0c, 0x1c, 0x3b, 0x0c, + 0x3b, 0x2b, 0x2b, 0x1b, 0x2b }, + { STV0900_8PSK_23, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, + 0x3b, 0x0c, 0x2b, 0x2b, 0x2b }, + { STV0900_8PSK_34, 0x0e, 0x1c, 0x3d, 0x0c, 0x0d, + 0x3b, 0x2c, 0x3b, 0x0c, 0x2b }, + { STV0900_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, + 0x1e, 0x3c, 0x2d, 0x2c, 0x1d }, + { STV0900_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, + 0x1e, 0x0d, 0x2d, 0x3c, 0x1d }, + { STV0900_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, + 0x1e, 0x1d, 0x2d, 0x0d, 0x1d }, }; /* Cut 2.0 Tracking carrier loop carrier 16APSK 2/3 to 32APSK 9/10 long Frame */ static const struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut20[11] = { - /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ - { STV0900_16APSK_23, 0x0C, 0x0C, 0x0C, 0x0C, 0x1D, 0x0C, 0x3C, 0x0C, 0x2C, 0x0C }, - { STV0900_16APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, 0x0C, 0x2D, 0x0C, 0x1D, 0x0C }, - { STV0900_16APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x0C, 0x3D, 0x0C, 0x2D, 0x0C }, - { STV0900_16APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x0C, 0x3D, 0x0C, 0x2D, 0x0C }, - { STV0900_16APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, 0x0C, 0x0E, 0x0C, 0x3D, 0x0C }, - { STV0900_16APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, 0x0C, 0x0E, 0x0C, 0x3D, 0x0C }, - { STV0900_32APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, - { STV0900_32APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, - { STV0900_32APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, - { STV0900_32APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, - { STV0900_32APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C } + /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_16APSK_23, 0x0C, 0x0C, 0x0C, 0x0C, 0x1D, + 0x0C, 0x3C, 0x0C, 0x2C, 0x0C }, + { STV0900_16APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, + 0x0C, 0x2D, 0x0C, 0x1D, 0x0C }, + { STV0900_16APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, + 0x0C, 0x3D, 0x0C, 0x2D, 0x0C }, + { STV0900_16APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, + 0x0C, 0x3D, 0x0C, 0x2D, 0x0C }, + { STV0900_16APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, + 0x0C, 0x0E, 0x0C, 0x3D, 0x0C }, + { STV0900_16APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, + 0x0C, 0x0E, 0x0C, 0x3D, 0x0C }, + { STV0900_32APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { STV0900_32APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { STV0900_32APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { STV0900_32APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { STV0900_32APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, }; /* Cut 2.0 Tracking carrier loop carrier QPSK 1/4 to QPSK 2/5 long Frame */ static const struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut20[3] = { - /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ - { STV0900_QPSK_14, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, 0x2F, 0x2D, 0x1F, 0x3D, 0x3E }, - { STV0900_QPSK_13, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, 0x2F, 0x3D, 0x0F, 0x3D, 0x2E }, - { STV0900_QPSK_25, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, 0x1F, 0x3D, 0x3E, 0x3D, 0x2E } + /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_QPSK_14, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, + 0x2F, 0x2D, 0x1F, 0x3D, 0x3E }, + { STV0900_QPSK_13, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, + 0x2F, 0x3D, 0x0F, 0x3D, 0x2E }, + { STV0900_QPSK_25, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, + 0x1F, 0x3D, 0x3E, 0x3D, 0x2E } }; /* Cut 2.0 Tracking carrier loop carrier short Frame, cut 1.2 and 2.0 */ -static const struct stv0900_short_frames_car_loop_optim FE_STV0900_S2ShortCarLoop[4] = { - /*Mod 2M_cut1.2 2M_cut2.0 5M_cut1.2 5M_cut2.0 10M_cut1.2 10M_cut2.0 20M_cut1.2 20M_cut2.0 30M_cut1.2 30M_cut2.0 */ - { STV0900_QPSK, 0x3C, 0x2F, 0x2B, 0x2E, 0x0B, 0x0E, 0x3A, 0x0E, 0x2A, 0x3D }, - { STV0900_8PSK, 0x0B, 0x3E, 0x2A, 0x0E, 0x0A, 0x2D, 0x19, 0x0D, 0x09, 0x3C }, - { STV0900_16APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, 0x1E, 0x3A, 0x3D, 0x2A, 0x2D }, - { STV0900_32APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, 0x1E, 0x3A, 0x3D, 0x2A, 0x2D } +static const +struct stv0900_short_frames_car_loop_optim FE_STV0900_S2ShortCarLoop[4] = { + /*Mod 2Mcut1.2 2Mcut2.0 5Mcut1.2 5Mcut2.0 10Mcut1.2 + 10Mcut2.0 20Mcut1.2 20M_cut2.0 30Mcut1.2 30Mcut2.0*/ + { STV0900_QPSK, 0x3C, 0x2F, 0x2B, 0x2E, 0x0B, + 0x0E, 0x3A, 0x0E, 0x2A, 0x3D }, + { STV0900_8PSK, 0x0B, 0x3E, 0x2A, 0x0E, 0x0A, + 0x2D, 0x19, 0x0D, 0x09, 0x3C }, + { STV0900_16APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, + 0x1E, 0x3A, 0x3D, 0x2A, 0x2D }, + { STV0900_32APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, + 0x1E, 0x3A, 0x3D, 0x2A, 0x2D } +}; + +static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut30[14] = { + /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_QPSK_12, 0x3C, 0x2C, 0x0C, 0x2C, 0x1B, + 0x2C, 0x1B, 0x1C, 0x0B, 0x3B }, + { STV0900_QPSK_35, 0x0D, 0x0D, 0x0C, 0x0D, 0x1B, + 0x3C, 0x1B, 0x1C, 0x0B, 0x3B }, + { STV0900_QPSK_23, 0x1D, 0x0D, 0x0C, 0x1D, 0x2B, + 0x3C, 0x1B, 0x1C, 0x0B, 0x3B }, + { STV0900_QPSK_34, 0x1D, 0x1D, 0x0C, 0x1D, 0x2B, + 0x3C, 0x1B, 0x1C, 0x0B, 0x3B }, + { STV0900_QPSK_45, 0x2D, 0x1D, 0x1C, 0x1D, 0x2B, + 0x3C, 0x2B, 0x0C, 0x1B, 0x3B }, + { STV0900_QPSK_56, 0x2D, 0x1D, 0x1C, 0x1D, 0x2B, + 0x3C, 0x2B, 0x0C, 0x1B, 0x3B }, + { STV0900_QPSK_89, 0x3D, 0x2D, 0x1C, 0x1D, 0x3B, + 0x3C, 0x2B, 0x0C, 0x1B, 0x3B }, + { STV0900_QPSK_910, 0x3D, 0x2D, 0x1C, 0x1D, 0x3B, + 0x3C, 0x2B, 0x0C, 0x1B, 0x3B }, + { STV0900_8PSK_35, 0x39, 0x19, 0x39, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x09, 0x19 }, + { STV0900_8PSK_23, 0x2A, 0x39, 0x1A, 0x0A, 0x39, + 0x0A, 0x29, 0x39, 0x29, 0x0A }, + { STV0900_8PSK_34, 0x0B, 0x3A, 0x0B, 0x0B, 0x3A, + 0x1B, 0x1A, 0x0B, 0x1A, 0x3A }, + { STV0900_8PSK_56, 0x0C, 0x1B, 0x3B, 0x2B, 0x1B, + 0x3B, 0x3A, 0x3B, 0x3A, 0x1B }, + { STV0900_8PSK_89, 0x2C, 0x2C, 0x2C, 0x1C, 0x2B, + 0x0C, 0x0B, 0x3B, 0x0B, 0x1B }, + { STV0900_8PSK_910, 0x2C, 0x3C, 0x2C, 0x1C, 0x3B, + 0x1C, 0x0B, 0x3B, 0x0B, 0x1B } +}; + +static const +struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut30[11] = { + /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_16APSK_23, 0x0A, 0x0A, 0x0A, 0x0A, 0x1A, + 0x0A, 0x3A, 0x0A, 0x2A, 0x0A }, + { STV0900_16APSK_34, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, + 0x0A, 0x3B, 0x0A, 0x1B, 0x0A }, + { STV0900_16APSK_45, 0x0A, 0x0A, 0x0A, 0x0A, 0x1B, + 0x0A, 0x3B, 0x0A, 0x2B, 0x0A }, + { STV0900_16APSK_56, 0x0A, 0x0A, 0x0A, 0x0A, 0x1B, + 0x0A, 0x3B, 0x0A, 0x2B, 0x0A }, + { STV0900_16APSK_89, 0x0A, 0x0A, 0x0A, 0x0A, 0x2B, + 0x0A, 0x0C, 0x0A, 0x3B, 0x0A }, + { STV0900_16APSK_910, 0x0A, 0x0A, 0x0A, 0x0A, 0x2B, + 0x0A, 0x0C, 0x0A, 0x3B, 0x0A }, + { STV0900_32APSK_34, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }, + { STV0900_32APSK_45, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }, + { STV0900_32APSK_56, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }, + { STV0900_32APSK_89, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }, + { STV0900_32APSK_910, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A } +}; + +static const +struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut30[3] = { + /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff*/ + { STV0900_QPSK_14, 0x0C, 0x3C, 0x0B, 0x3C, 0x2A, + 0x2C, 0x2A, 0x1C, 0x3A, 0x3B }, + { STV0900_QPSK_13, 0x0C, 0x3C, 0x0B, 0x3C, 0x2A, + 0x2C, 0x3A, 0x0C, 0x3A, 0x2B }, + { STV0900_QPSK_25, 0x1C, 0x3C, 0x1B, 0x3C, 0x3A, + 0x1C, 0x3A, 0x3B, 0x3A, 0x2B } +}; + +static const struct stv0900_short_frames_car_loop_optim_vs_mod +FE_STV0900_S2ShortCarLoopCut30[4] = { + /*Mod 2Mcut3.0 5Mcut3.0 10Mcut3.0 20Mcut3.0 30Mcut3.0*/ + { STV0900_QPSK, 0x2C, 0x2B, 0x0B, 0x0B, 0x3A }, + { STV0900_8PSK, 0x3B, 0x0B, 0x2A, 0x0A, 0x39 }, + { STV0900_16APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }, + { STV0900_32APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }, + }; -static const u16 STV0900_InitVal[182][2] = { +static const u16 STV0900_InitVal[181][2] = { { R0900_OUTCFG , 0x00 }, - { R0900_MODECFG , 0xff }, { R0900_AGCRF1CFG , 0x11 }, { R0900_AGCRF2CFG , 0x13 }, { R0900_TSGENERAL1X , 0x14 }, @@ -381,7 +524,7 @@ static const u16 STV0900_InitVal[182][2] = { { R0900_GAINLLR_NF15 , 0x1A }, { R0900_GAINLLR_NF16 , 0x1F }, { R0900_GAINLLR_NF17 , 0x21 }, - { R0900_RCCFGH , 0x20 }, + { R0900_RCCFG2 , 0x20 }, { R0900_P1_FECM , 0x01 }, /*disable DSS modes*/ { R0900_P2_FECM , 0x01 }, /*disable DSS modes*/ { R0900_P1_PRVIT , 0x2F }, /*disable puncture rate 6/7*/ diff --git a/drivers/media/dvb/frontends/stv0900_priv.h b/drivers/media/dvb/frontends/stv0900_priv.h index 5ed7a145c7d3..d8ba8a984abe 100644 --- a/drivers/media/dvb/frontends/stv0900_priv.h +++ b/drivers/media/dvb/frontends/stv0900_priv.h @@ -46,22 +46,6 @@ #define FALSE (!TRUE) #endif -#define dmd_reg(a, b, c) \ - do { \ - a = 0; \ - switch (demod) { \ - case STV0900_DEMOD_1: \ - default: \ - a = b; \ - break; \ - case STV0900_DEMOD_2: \ - a = c; \ - break; \ - } \ - } while (0) - -static int stvdebug; - #define dprintk(args...) \ do { \ if (stvdebug) \ @@ -70,6 +54,8 @@ static int stvdebug; #define STV0900_MAXLOOKUPSIZE 500 #define STV0900_BLIND_SEARCH_AGC2_TH 700 +#define STV0900_BLIND_SEARCH_AGC2_TH_CUT30 1400 +#define IQPOWER_THRESHOLD 30 /* One point of the lookup table */ struct stv000_lookpoint { @@ -263,14 +249,14 @@ struct stv0900_init_params{ int tuner1_adc; /* IQ from the tuner1 to the demod */ - enum stv0900_iq_inversion tun1_iq_inversion; + enum stv0900_iq_inversion tun1_iq_inv; enum fe_stv0900_clock_type path2_ts_clock; u8 tun2_maddress; int tuner2_adc; /* IQ from the tuner2 to the demod */ - enum stv0900_iq_inversion tun2_iq_inversion; + enum stv0900_iq_inversion tun2_iq_inv; struct stv0900_reg *ts_config; }; @@ -300,7 +286,7 @@ struct stv0900_signal_info { enum fe_stv0900_modcode modcode; enum fe_stv0900_modulation modulation; enum fe_stv0900_pilot pilot; - enum fe_stv0900_frame_length frame_length; + enum fe_stv0900_frame_length frame_len; enum stv0900_iq_inversion spectrum; enum fe_stv0900_rolloff rolloff; @@ -318,47 +304,25 @@ struct stv0900_internal{ /* Demodulator use for single demod or for dual demod) */ enum fe_stv0900_demod_mode demod_mode; - /*Demod 1*/ - s32 tuner1_freq; - s32 tuner1_bw; - s32 dmd1_symbol_rate; - s32 dmd1_srch_range; + /*Demods */ + s32 freq[2]; + s32 bw[2]; + s32 symbol_rate[2]; + s32 srch_range[2]; /* algorithm for search Blind, Cold or Warm*/ - enum fe_stv0900_search_algo dmd1_srch_algo; + enum fe_stv0900_search_algo srch_algo[2]; /* search standard: Auto, DVBS1/DSS only or DVBS2 only*/ - enum fe_stv0900_search_standard dmd1_srch_standard; + enum fe_stv0900_search_standard srch_standard[2]; /* inversion search : auto, auto norma first, normal or inverted */ - enum fe_stv0900_search_iq dmd1_srch_iq_inv; - enum fe_stv0900_modcode dmd1_modcode; - enum fe_stv0900_modulation dmd1_modulation; - enum fe_stv0900_fec dmd1_fec; - - struct stv0900_signal_info dmd1_rslts; - enum fe_stv0900_signal_type dmd1_state; + enum fe_stv0900_search_iq srch_iq_inv[2]; + enum fe_stv0900_modcode modcode[2]; + enum fe_stv0900_modulation modulation[2]; + enum fe_stv0900_fec fec[2]; - enum fe_stv0900_error dmd1_err; + struct stv0900_signal_info result[2]; + enum fe_stv0900_error err[2]; - /*Demod 2*/ - s32 tuner2_freq; - s32 tuner2_bw; - s32 dmd2_symbol_rate; - s32 dmd2_srch_range; - - enum fe_stv0900_search_algo dmd2_srch_algo; - enum fe_stv0900_search_standard dmd2_srch_stndrd; - /* inversion search : auto, auto normal first, normal or inverted */ - enum fe_stv0900_search_iq dmd2_srch_iq_inv; - enum fe_stv0900_modcode dmd2_modcode; - enum fe_stv0900_modulation dmd2_modulation; - enum fe_stv0900_fec dmd2_fec; - - /* results of the search*/ - struct stv0900_signal_info dmd2_rslts; - /* current state of the search algorithm */ - enum fe_stv0900_signal_type dmd2_state; - - enum fe_stv0900_error dmd2_err; struct i2c_adapter *i2c_adap; u8 i2c_addr; @@ -379,6 +343,8 @@ struct stv0900_state { int demod; }; +extern int stvdebug; + extern s32 ge2comp(s32 a, s32 width); extern void stv0900_write_reg(struct stv0900_internal *i_params, @@ -418,13 +384,14 @@ extern u8 stv0900_get_optim_short_carr_loop(s32 srate, extern void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params, enum fe_stv0900_demod_num demod); -extern void stv0900_activate_s2_modcode(struct stv0900_internal *i_params, +extern void stv0900_activate_s2_modcod(struct stv0900_internal *i_params, enum fe_stv0900_demod_num demod); -extern void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params, +extern void stv0900_activate_s2_modcod_single(struct stv0900_internal *i_params, enum fe_stv0900_demod_num demod); -extern enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, +extern enum +fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, enum fe_stv0900_demod_num demod); #endif diff --git a/drivers/media/dvb/frontends/stv0900_reg.h b/drivers/media/dvb/frontends/stv0900_reg.h index 264f9cf9a17e..7b8edf192e97 100644 --- a/drivers/media/dvb/frontends/stv0900_reg.h +++ b/drivers/media/dvb/frontends/stv0900_reg.h @@ -14,7 +14,7 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * @@ -26,3762 +26,3950 @@ #ifndef STV0900_REG_H #define STV0900_REG_H +extern s32 shiftx(s32 x, int demod, s32 shift); + +#define REGx(x) shiftx(x, demod, 0x200) +#define FLDx(x) shiftx(x, demod, 0x2000000) + /*MID*/ -#define R0900_MID 0xf100 -#define F0900_MCHIP_IDENT 0xf10000f0 -#define F0900_MRELEASE 0xf100000f +#define R0900_MID 0xf100 +#define F0900_MCHIP_IDENT 0xf10000f0 +#define F0900_MRELEASE 0xf100000f /*DACR1*/ -#define R0900_DACR1 0xf113 -#define F0900_DAC_MODE 0xf11300e0 -#define F0900_DAC_VALUE1 0xf113000f +#define R0900_DACR1 0xf113 +#define F0900_DAC_MODE 0xf11300e0 +#define F0900_DAC_VALUE1 0xf113000f /*DACR2*/ -#define R0900_DACR2 0xf114 -#define F0900_DAC_VALUE0 0xf11400ff +#define R0900_DACR2 0xf114 +#define F0900_DAC_VALUE0 0xf11400ff /*OUTCFG*/ -#define R0900_OUTCFG 0xf11c -#define F0900_INV_DATA6 0xf11c0080 -#define F0900_OUTSERRS1_HZ 0xf11c0040 -#define F0900_OUTSERRS2_HZ 0xf11c0020 -#define F0900_OUTSERRS3_HZ 0xf11c0010 -#define F0900_OUTPARRS3_HZ 0xf11c0008 -#define F0900_OUTHZ3_CONTROL 0xf11c0007 - -/*MODECFG*/ -#define R0900_MODECFG 0xf11d -#define F0900_FECSPY_SEL_2 0xf11d0020 -#define F0900_HWARE_SEL_2 0xf11d0010 -#define F0900_PKTDEL_SEL_2 0xf11d0008 -#define F0900_DISEQC_SEL_2 0xf11d0004 -#define F0900_VIT_SEL_2 0xf11d0002 -#define F0900_DEMOD_SEL_2 0xf11d0001 +#define R0900_OUTCFG 0xf11c +#define F0900_OUTSERRS1_HZ 0xf11c0040 +#define F0900_OUTSERRS2_HZ 0xf11c0020 +#define F0900_OUTSERRS3_HZ 0xf11c0010 +#define F0900_OUTPARRS3_HZ 0xf11c0008 /*IRQSTATUS3*/ -#define R0900_IRQSTATUS3 0xf120 -#define F0900_SPLL_LOCK 0xf1200020 -#define F0900_SSTREAM_LCK_3 0xf1200010 -#define F0900_SSTREAM_LCK_2 0xf1200008 -#define F0900_SSTREAM_LCK_1 0xf1200004 -#define F0900_SDVBS1_PRF_2 0xf1200002 -#define F0900_SDVBS1_PRF_1 0xf1200001 +#define R0900_IRQSTATUS3 0xf120 +#define F0900_SPLL_LOCK 0xf1200020 +#define F0900_SSTREAM_LCK_3 0xf1200010 +#define F0900_SSTREAM_LCK_2 0xf1200008 +#define F0900_SSTREAM_LCK_1 0xf1200004 +#define F0900_SDVBS1_PRF_2 0xf1200002 +#define F0900_SDVBS1_PRF_1 0xf1200001 /*IRQSTATUS2*/ -#define R0900_IRQSTATUS2 0xf121 -#define F0900_SSPY_ENDSIM_3 0xf1210080 -#define F0900_SSPY_ENDSIM_2 0xf1210040 -#define F0900_SSPY_ENDSIM_1 0xf1210020 -#define F0900_SPKTDEL_ERROR_2 0xf1210010 -#define F0900_SPKTDEL_LOCKB_2 0xf1210008 -#define F0900_SPKTDEL_LOCK_2 0xf1210004 -#define F0900_SPKTDEL_ERROR_1 0xf1210002 -#define F0900_SPKTDEL_LOCKB_1 0xf1210001 +#define R0900_IRQSTATUS2 0xf121 +#define F0900_SSPY_ENDSIM_3 0xf1210080 +#define F0900_SSPY_ENDSIM_2 0xf1210040 +#define F0900_SSPY_ENDSIM_1 0xf1210020 +#define F0900_SPKTDEL_ERROR_2 0xf1210010 +#define F0900_SPKTDEL_LOCKB_2 0xf1210008 +#define F0900_SPKTDEL_LOCK_2 0xf1210004 +#define F0900_SPKTDEL_ERROR_1 0xf1210002 +#define F0900_SPKTDEL_LOCKB_1 0xf1210001 /*IRQSTATUS1*/ -#define R0900_IRQSTATUS1 0xf122 -#define F0900_SPKTDEL_LOCK_1 0xf1220080 -#define F0900_SEXTPINB2 0xf1220040 -#define F0900_SEXTPIN2 0xf1220020 -#define F0900_SEXTPINB1 0xf1220010 -#define F0900_SEXTPIN1 0xf1220008 -#define F0900_SDEMOD_LOCKB_2 0xf1220004 -#define F0900_SDEMOD_LOCK_2 0xf1220002 -#define F0900_SDEMOD_IRQ_2 0xf1220001 +#define R0900_IRQSTATUS1 0xf122 +#define F0900_SPKTDEL_LOCK_1 0xf1220080 +#define F0900_SDEMOD_LOCKB_2 0xf1220004 +#define F0900_SDEMOD_LOCK_2 0xf1220002 +#define F0900_SDEMOD_IRQ_2 0xf1220001 /*IRQSTATUS0*/ -#define R0900_IRQSTATUS0 0xf123 -#define F0900_SDEMOD_LOCKB_1 0xf1230080 -#define F0900_SDEMOD_LOCK_1 0xf1230040 -#define F0900_SDEMOD_IRQ_1 0xf1230020 -#define F0900_SBCH_ERRFLAG 0xf1230010 -#define F0900_SDISEQC2RX_IRQ 0xf1230008 -#define F0900_SDISEQC2TX_IRQ 0xf1230004 -#define F0900_SDISEQC1RX_IRQ 0xf1230002 -#define F0900_SDISEQC1TX_IRQ 0xf1230001 +#define R0900_IRQSTATUS0 0xf123 +#define F0900_SDEMOD_LOCKB_1 0xf1230080 +#define F0900_SDEMOD_LOCK_1 0xf1230040 +#define F0900_SDEMOD_IRQ_1 0xf1230020 +#define F0900_SBCH_ERRFLAG 0xf1230010 +#define F0900_SDISEQC2RX_IRQ 0xf1230008 +#define F0900_SDISEQC2TX_IRQ 0xf1230004 +#define F0900_SDISEQC1RX_IRQ 0xf1230002 +#define F0900_SDISEQC1TX_IRQ 0xf1230001 /*IRQMASK3*/ -#define R0900_IRQMASK3 0xf124 -#define F0900_MPLL_LOCK 0xf1240020 -#define F0900_MSTREAM_LCK_3 0xf1240010 -#define F0900_MSTREAM_LCK_2 0xf1240008 -#define F0900_MSTREAM_LCK_1 0xf1240004 -#define F0900_MDVBS1_PRF_2 0xf1240002 -#define F0900_MDVBS1_PRF_1 0xf1240001 +#define R0900_IRQMASK3 0xf124 +#define F0900_MPLL_LOCK 0xf1240020 +#define F0900_MSTREAM_LCK_3 0xf1240010 +#define F0900_MSTREAM_LCK_2 0xf1240008 +#define F0900_MSTREAM_LCK_1 0xf1240004 +#define F0900_MDVBS1_PRF_2 0xf1240002 +#define F0900_MDVBS1_PRF_1 0xf1240001 /*IRQMASK2*/ -#define R0900_IRQMASK2 0xf125 -#define F0900_MSPY_ENDSIM_3 0xf1250080 -#define F0900_MSPY_ENDSIM_2 0xf1250040 -#define F0900_MSPY_ENDSIM_1 0xf1250020 -#define F0900_MPKTDEL_ERROR_2 0xf1250010 -#define F0900_MPKTDEL_LOCKB_2 0xf1250008 -#define F0900_MPKTDEL_LOCK_2 0xf1250004 -#define F0900_MPKTDEL_ERROR_1 0xf1250002 -#define F0900_MPKTDEL_LOCKB_1 0xf1250001 +#define R0900_IRQMASK2 0xf125 +#define F0900_MSPY_ENDSIM_3 0xf1250080 +#define F0900_MSPY_ENDSIM_2 0xf1250040 +#define F0900_MSPY_ENDSIM_1 0xf1250020 +#define F0900_MPKTDEL_ERROR_2 0xf1250010 +#define F0900_MPKTDEL_LOCKB_2 0xf1250008 +#define F0900_MPKTDEL_LOCK_2 0xf1250004 +#define F0900_MPKTDEL_ERROR_1 0xf1250002 +#define F0900_MPKTDEL_LOCKB_1 0xf1250001 /*IRQMASK1*/ -#define R0900_IRQMASK1 0xf126 -#define F0900_MPKTDEL_LOCK_1 0xf1260080 -#define F0900_MEXTPINB2 0xf1260040 -#define F0900_MEXTPIN2 0xf1260020 -#define F0900_MEXTPINB1 0xf1260010 -#define F0900_MEXTPIN1 0xf1260008 -#define F0900_MDEMOD_LOCKB_2 0xf1260004 -#define F0900_MDEMOD_LOCK_2 0xf1260002 -#define F0900_MDEMOD_IRQ_2 0xf1260001 +#define R0900_IRQMASK1 0xf126 +#define F0900_MPKTDEL_LOCK_1 0xf1260080 +#define F0900_MEXTPINB2 0xf1260040 +#define F0900_MEXTPIN2 0xf1260020 +#define F0900_MEXTPINB1 0xf1260010 +#define F0900_MEXTPIN1 0xf1260008 +#define F0900_MDEMOD_LOCKB_2 0xf1260004 +#define F0900_MDEMOD_LOCK_2 0xf1260002 +#define F0900_MDEMOD_IRQ_2 0xf1260001 /*IRQMASK0*/ -#define R0900_IRQMASK0 0xf127 -#define F0900_MDEMOD_LOCKB_1 0xf1270080 -#define F0900_MDEMOD_LOCK_1 0xf1270040 -#define F0900_MDEMOD_IRQ_1 0xf1270020 -#define F0900_MBCH_ERRFLAG 0xf1270010 -#define F0900_MDISEQC2RX_IRQ 0xf1270008 -#define F0900_MDISEQC2TX_IRQ 0xf1270004 -#define F0900_MDISEQC1RX_IRQ 0xf1270002 -#define F0900_MDISEQC1TX_IRQ 0xf1270001 +#define R0900_IRQMASK0 0xf127 +#define F0900_MDEMOD_LOCKB_1 0xf1270080 +#define F0900_MDEMOD_LOCK_1 0xf1270040 +#define F0900_MDEMOD_IRQ_1 0xf1270020 +#define F0900_MBCH_ERRFLAG 0xf1270010 +#define F0900_MDISEQC2RX_IRQ 0xf1270008 +#define F0900_MDISEQC2TX_IRQ 0xf1270004 +#define F0900_MDISEQC1RX_IRQ 0xf1270002 +#define F0900_MDISEQC1TX_IRQ 0xf1270001 /*I2CCFG*/ -#define R0900_I2CCFG 0xf129 -#define F0900_I2C2_FASTMODE 0xf1290080 -#define F0900_STATUS_WR2 0xf1290040 -#define F0900_I2C2ADDR_INC 0xf1290030 -#define F0900_I2C_FASTMODE 0xf1290008 -#define F0900_STATUS_WR 0xf1290004 -#define F0900_I2CADDR_INC 0xf1290003 +#define R0900_I2CCFG 0xf129 +#define F0900_I2C_FASTMODE 0xf1290008 +#define F0900_I2CADDR_INC 0xf1290003 /*P1_I2CRPT*/ -#define R0900_P1_I2CRPT 0xf12a -#define F0900_P1_I2CT_ON 0xf12a0080 -#define F0900_P1_ENARPT_LEVEL 0xf12a0070 -#define F0900_P1_SCLT_DELAY 0xf12a0008 -#define F0900_P1_STOP_ENABLE 0xf12a0004 -#define F0900_P1_STOP_SDAT2SDA 0xf12a0002 +#define R0900_P1_I2CRPT 0xf12a +#define I2CRPT shiftx(R0900_P1_I2CRPT, demod, -1) +#define F0900_P1_I2CT_ON 0xf12a0080 +#define I2CT_ON shiftx(F0900_P1_I2CT_ON, demod, -0x10000) +#define F0900_P1_ENARPT_LEVEL 0xf12a0070 +#define F0900_P1_SCLT_DELAY 0xf12a0008 +#define F0900_P1_STOP_ENABLE 0xf12a0004 +#define F0900_P1_STOP_SDAT2SDA 0xf12a0002 /*P2_I2CRPT*/ -#define R0900_P2_I2CRPT 0xf12b -#define F0900_P2_I2CT_ON 0xf12b0080 -#define F0900_P2_ENARPT_LEVEL 0xf12b0070 -#define F0900_P2_SCLT_DELAY 0xf12b0008 -#define F0900_P2_STOP_ENABLE 0xf12b0004 -#define F0900_P2_STOP_SDAT2SDA 0xf12b0002 +#define R0900_P2_I2CRPT 0xf12b +#define F0900_P2_I2CT_ON 0xf12b0080 +#define F0900_P2_ENARPT_LEVEL 0xf12b0070 +#define F0900_P2_SCLT_DELAY 0xf12b0008 +#define F0900_P2_STOP_ENABLE 0xf12b0004 +#define F0900_P2_STOP_SDAT2SDA 0xf12b0002 + +/*IOPVALUE6*/ +#define R0900_IOPVALUE6 0xf138 +#define F0900_VSCL 0xf1380004 +#define F0900_VSDA 0xf1380002 +#define F0900_VDATA3_0 0xf1380001 + +/*IOPVALUE5*/ +#define R0900_IOPVALUE5 0xf139 +#define F0900_VDATA3_1 0xf1390080 +#define F0900_VDATA3_2 0xf1390040 +#define F0900_VDATA3_3 0xf1390020 +#define F0900_VDATA3_4 0xf1390010 +#define F0900_VDATA3_5 0xf1390008 +#define F0900_VDATA3_6 0xf1390004 +#define F0900_VDATA3_7 0xf1390002 +#define F0900_VCLKOUT3 0xf1390001 + +/*IOPVALUE4*/ +#define R0900_IOPVALUE4 0xf13a +#define F0900_VSTROUT3 0xf13a0080 +#define F0900_VDPN3 0xf13a0040 +#define F0900_VERROR3 0xf13a0020 +#define F0900_VDATA2_7 0xf13a0010 +#define F0900_VCLKOUT2 0xf13a0008 +#define F0900_VSTROUT2 0xf13a0004 +#define F0900_VDPN2 0xf13a0002 +#define F0900_VERROR2 0xf13a0001 + +/*IOPVALUE3*/ +#define R0900_IOPVALUE3 0xf13b +#define F0900_VDATA1_7 0xf13b0080 +#define F0900_VCLKOUT1 0xf13b0040 +#define F0900_VSTROUT1 0xf13b0020 +#define F0900_VDPN1 0xf13b0010 +#define F0900_VERROR1 0xf13b0008 +#define F0900_VCLKOUT27 0xf13b0004 +#define F0900_VDISEQCOUT2 0xf13b0002 +#define F0900_VSCLT2 0xf13b0001 + +/*IOPVALUE2*/ +#define R0900_IOPVALUE2 0xf13c +#define F0900_VSDAT2 0xf13c0080 +#define F0900_VAGCRF2 0xf13c0040 +#define F0900_VDISEQCOUT1 0xf13c0020 +#define F0900_VSCLT1 0xf13c0010 +#define F0900_VSDAT1 0xf13c0008 +#define F0900_VAGCRF1 0xf13c0004 +#define F0900_VDIRCLK 0xf13c0002 +#define F0900_VSTDBY 0xf13c0001 + +/*IOPVALUE1*/ +#define R0900_IOPVALUE1 0xf13d +#define F0900_VCS1 0xf13d0080 +#define F0900_VCS0 0xf13d0040 +#define F0900_VGPIO13 0xf13d0020 +#define F0900_VGPIO12 0xf13d0010 +#define F0900_VGPIO11 0xf13d0008 +#define F0900_VGPIO10 0xf13d0004 +#define F0900_VGPIO9 0xf13d0002 +#define F0900_VGPIO8 0xf13d0001 + +/*IOPVALUE0*/ +#define R0900_IOPVALUE0 0xf13e +#define F0900_VGPIO7 0xf13e0080 +#define F0900_VGPIO6 0xf13e0040 +#define F0900_VGPIO5 0xf13e0020 +#define F0900_VGPIO4 0xf13e0010 +#define F0900_VGPIO3 0xf13e0008 +#define F0900_VGPIO2 0xf13e0004 +#define F0900_VGPIO1 0xf13e0002 +#define F0900_VCLKI2 0xf13e0001 /*CLKI2CFG*/ -#define R0900_CLKI2CFG 0xf140 -#define F0900_CLKI2_OPD 0xf1400080 -#define F0900_CLKI2_CONFIG 0xf140007e -#define F0900_CLKI2_XOR 0xf1400001 +#define R0900_CLKI2CFG 0xf140 +#define F0900_CLKI2_OPD 0xf1400080 +#define F0900_CLKI2_CONFIG 0xf140007e +#define F0900_CLKI2_XOR 0xf1400001 /*GPIO1CFG*/ -#define R0900_GPIO1CFG 0xf141 -#define F0900_GPIO1_OPD 0xf1410080 -#define F0900_GPIO1_CONFIG 0xf141007e -#define F0900_GPIO1_XOR 0xf1410001 +#define R0900_GPIO1CFG 0xf141 +#define F0900_GPIO1_OPD 0xf1410080 +#define F0900_GPIO1_CONFIG 0xf141007e +#define F0900_GPIO1_XOR 0xf1410001 /*GPIO2CFG*/ -#define R0900_GPIO2CFG 0xf142 -#define F0900_GPIO2_OPD 0xf1420080 -#define F0900_GPIO2_CONFIG 0xf142007e -#define F0900_GPIO2_XOR 0xf1420001 +#define R0900_GPIO2CFG 0xf142 +#define F0900_GPIO2_OPD 0xf1420080 +#define F0900_GPIO2_CONFIG 0xf142007e +#define F0900_GPIO2_XOR 0xf1420001 /*GPIO3CFG*/ -#define R0900_GPIO3CFG 0xf143 -#define F0900_GPIO3_OPD 0xf1430080 -#define F0900_GPIO3_CONFIG 0xf143007e -#define F0900_GPIO3_XOR 0xf1430001 +#define R0900_GPIO3CFG 0xf143 +#define F0900_GPIO3_OPD 0xf1430080 +#define F0900_GPIO3_CONFIG 0xf143007e +#define F0900_GPIO3_XOR 0xf1430001 /*GPIO4CFG*/ -#define R0900_GPIO4CFG 0xf144 -#define F0900_GPIO4_OPD 0xf1440080 -#define F0900_GPIO4_CONFIG 0xf144007e -#define F0900_GPIO4_XOR 0xf1440001 +#define R0900_GPIO4CFG 0xf144 +#define F0900_GPIO4_OPD 0xf1440080 +#define F0900_GPIO4_CONFIG 0xf144007e +#define F0900_GPIO4_XOR 0xf1440001 /*GPIO5CFG*/ -#define R0900_GPIO5CFG 0xf145 -#define F0900_GPIO5_OPD 0xf1450080 -#define F0900_GPIO5_CONFIG 0xf145007e -#define F0900_GPIO5_XOR 0xf1450001 +#define R0900_GPIO5CFG 0xf145 +#define F0900_GPIO5_OPD 0xf1450080 +#define F0900_GPIO5_CONFIG 0xf145007e +#define F0900_GPIO5_XOR 0xf1450001 /*GPIO6CFG*/ -#define R0900_GPIO6CFG 0xf146 -#define F0900_GPIO6_OPD 0xf1460080 -#define F0900_GPIO6_CONFIG 0xf146007e -#define F0900_GPIO6_XOR 0xf1460001 +#define R0900_GPIO6CFG 0xf146 +#define F0900_GPIO6_OPD 0xf1460080 +#define F0900_GPIO6_CONFIG 0xf146007e +#define F0900_GPIO6_XOR 0xf1460001 /*GPIO7CFG*/ -#define R0900_GPIO7CFG 0xf147 -#define F0900_GPIO7_OPD 0xf1470080 -#define F0900_GPIO7_CONFIG 0xf147007e -#define F0900_GPIO7_XOR 0xf1470001 +#define R0900_GPIO7CFG 0xf147 +#define F0900_GPIO7_OPD 0xf1470080 +#define F0900_GPIO7_CONFIG 0xf147007e +#define F0900_GPIO7_XOR 0xf1470001 /*GPIO8CFG*/ -#define R0900_GPIO8CFG 0xf148 -#define F0900_GPIO8_OPD 0xf1480080 -#define F0900_GPIO8_CONFIG 0xf148007e -#define F0900_GPIO8_XOR 0xf1480001 +#define R0900_GPIO8CFG 0xf148 +#define F0900_GPIO8_OPD 0xf1480080 +#define F0900_GPIO8_CONFIG 0xf148007e +#define F0900_GPIO8_XOR 0xf1480001 /*GPIO9CFG*/ -#define R0900_GPIO9CFG 0xf149 -#define F0900_GPIO9_OPD 0xf1490080 -#define F0900_GPIO9_CONFIG 0xf149007e -#define F0900_GPIO9_XOR 0xf1490001 +#define R0900_GPIO9CFG 0xf149 +#define F0900_GPIO9_OPD 0xf1490080 +#define F0900_GPIO9_CONFIG 0xf149007e +#define F0900_GPIO9_XOR 0xf1490001 /*GPIO10CFG*/ -#define R0900_GPIO10CFG 0xf14a -#define F0900_GPIO10_OPD 0xf14a0080 -#define F0900_GPIO10_CONFIG 0xf14a007e -#define F0900_GPIO10_XOR 0xf14a0001 +#define R0900_GPIO10CFG 0xf14a +#define F0900_GPIO10_OPD 0xf14a0080 +#define F0900_GPIO10_CONFIG 0xf14a007e +#define F0900_GPIO10_XOR 0xf14a0001 /*GPIO11CFG*/ -#define R0900_GPIO11CFG 0xf14b -#define F0900_GPIO11_OPD 0xf14b0080 -#define F0900_GPIO11_CONFIG 0xf14b007e -#define F0900_GPIO11_XOR 0xf14b0001 +#define R0900_GPIO11CFG 0xf14b +#define F0900_GPIO11_OPD 0xf14b0080 +#define F0900_GPIO11_CONFIG 0xf14b007e +#define F0900_GPIO11_XOR 0xf14b0001 /*GPIO12CFG*/ -#define R0900_GPIO12CFG 0xf14c -#define F0900_GPIO12_OPD 0xf14c0080 -#define F0900_GPIO12_CONFIG 0xf14c007e -#define F0900_GPIO12_XOR 0xf14c0001 +#define R0900_GPIO12CFG 0xf14c +#define F0900_GPIO12_OPD 0xf14c0080 +#define F0900_GPIO12_CONFIG 0xf14c007e +#define F0900_GPIO12_XOR 0xf14c0001 /*GPIO13CFG*/ -#define R0900_GPIO13CFG 0xf14d -#define F0900_GPIO13_OPD 0xf14d0080 -#define F0900_GPIO13_CONFIG 0xf14d007e -#define F0900_GPIO13_XOR 0xf14d0001 +#define R0900_GPIO13CFG 0xf14d +#define F0900_GPIO13_OPD 0xf14d0080 +#define F0900_GPIO13_CONFIG 0xf14d007e +#define F0900_GPIO13_XOR 0xf14d0001 /*CS0CFG*/ -#define R0900_CS0CFG 0xf14e -#define F0900_CS0_OPD 0xf14e0080 -#define F0900_CS0_CONFIG 0xf14e007e -#define F0900_CS0_XOR 0xf14e0001 +#define R0900_CS0CFG 0xf14e +#define F0900_CS0_OPD 0xf14e0080 +#define F0900_CS0_CONFIG 0xf14e007e +#define F0900_CS0_XOR 0xf14e0001 /*CS1CFG*/ -#define R0900_CS1CFG 0xf14f -#define F0900_CS1_OPD 0xf14f0080 -#define F0900_CS1_CONFIG 0xf14f007e -#define F0900_CS1_XOR 0xf14f0001 +#define R0900_CS1CFG 0xf14f +#define F0900_CS1_OPD 0xf14f0080 +#define F0900_CS1_CONFIG 0xf14f007e +#define F0900_CS1_XOR 0xf14f0001 /*STDBYCFG*/ -#define R0900_STDBYCFG 0xf150 -#define F0900_STDBY_OPD 0xf1500080 -#define F0900_STDBY_CONFIG 0xf150007e -#define F0900_STBDY_XOR 0xf1500001 +#define R0900_STDBYCFG 0xf150 +#define F0900_STDBY_OPD 0xf1500080 +#define F0900_STDBY_CONFIG 0xf150007e +#define F0900_STBDY_XOR 0xf1500001 /*DIRCLKCFG*/ -#define R0900_DIRCLKCFG 0xf151 -#define F0900_DIRCLK_OPD 0xf1510080 -#define F0900_DIRCLK_CONFIG 0xf151007e -#define F0900_DIRCLK_XOR 0xf1510001 +#define R0900_DIRCLKCFG 0xf151 +#define F0900_DIRCLK_OPD 0xf1510080 +#define F0900_DIRCLK_CONFIG 0xf151007e +#define F0900_DIRCLK_XOR 0xf1510001 /*AGCRF1CFG*/ -#define R0900_AGCRF1CFG 0xf152 -#define F0900_AGCRF1_OPD 0xf1520080 -#define F0900_AGCRF1_CONFIG 0xf152007e -#define F0900_AGCRF1_XOR 0xf1520001 +#define R0900_AGCRF1CFG 0xf152 +#define F0900_AGCRF1_OPD 0xf1520080 +#define F0900_AGCRF1_CONFIG 0xf152007e +#define F0900_AGCRF1_XOR 0xf1520001 /*SDAT1CFG*/ -#define R0900_SDAT1CFG 0xf153 -#define F0900_SDAT1_OPD 0xf1530080 -#define F0900_SDAT1_CONFIG 0xf153007e -#define F0900_SDAT1_XOR 0xf1530001 +#define R0900_SDAT1CFG 0xf153 +#define F0900_SDAT1_OPD 0xf1530080 +#define F0900_SDAT1_CONFIG 0xf153007e +#define F0900_SDAT1_XOR 0xf1530001 /*SCLT1CFG*/ -#define R0900_SCLT1CFG 0xf154 -#define F0900_SCLT1_OPD 0xf1540080 -#define F0900_SCLT1_CONFIG 0xf154007e -#define F0900_SCLT1_XOR 0xf1540001 +#define R0900_SCLT1CFG 0xf154 +#define F0900_SCLT1_OPD 0xf1540080 +#define F0900_SCLT1_CONFIG 0xf154007e +#define F0900_SCLT1_XOR 0xf1540001 /*DISEQCO1CFG*/ -#define R0900_DISEQCO1CFG 0xf155 -#define F0900_DISEQCO1_OPD 0xf1550080 -#define F0900_DISEQCO1_CONFIG 0xf155007e -#define F0900_DISEQC1_XOR 0xf1550001 +#define R0900_DISEQCO1CFG 0xf155 +#define F0900_DISEQCO1_OPD 0xf1550080 +#define F0900_DISEQCO1_CONFIG 0xf155007e +#define F0900_DISEQC1_XOR 0xf1550001 /*AGCRF2CFG*/ -#define R0900_AGCRF2CFG 0xf156 -#define F0900_AGCRF2_OPD 0xf1560080 -#define F0900_AGCRF2_CONFIG 0xf156007e -#define F0900_AGCRF2_XOR 0xf1560001 +#define R0900_AGCRF2CFG 0xf156 +#define F0900_AGCRF2_OPD 0xf1560080 +#define F0900_AGCRF2_CONFIG 0xf156007e +#define F0900_AGCRF2_XOR 0xf1560001 /*SDAT2CFG*/ -#define R0900_SDAT2CFG 0xf157 -#define F0900_SDAT2_OPD 0xf1570080 -#define F0900_SDAT2_CONFIG 0xf157007e -#define F0900_SDAT2_XOR 0xf1570001 +#define R0900_SDAT2CFG 0xf157 +#define F0900_SDAT2_OPD 0xf1570080 +#define F0900_SDAT2_CONFIG 0xf157007e +#define F0900_SDAT2_XOR 0xf1570001 /*SCLT2CFG*/ -#define R0900_SCLT2CFG 0xf158 -#define F0900_SCLT2_OPD 0xf1580080 -#define F0900_SCLT2_CONFIG 0xf158007e -#define F0900_SCLT2_XOR 0xf1580001 +#define R0900_SCLT2CFG 0xf158 +#define F0900_SCLT2_OPD 0xf1580080 +#define F0900_SCLT2_CONFIG 0xf158007e +#define F0900_SCLT2_XOR 0xf1580001 /*DISEQCO2CFG*/ -#define R0900_DISEQCO2CFG 0xf159 -#define F0900_DISEQCO2_OPD 0xf1590080 -#define F0900_DISEQCO2_CONFIG 0xf159007e -#define F0900_DISEQC2_XOR 0xf1590001 +#define R0900_DISEQCO2CFG 0xf159 +#define F0900_DISEQCO2_OPD 0xf1590080 +#define F0900_DISEQCO2_CONFIG 0xf159007e +#define F0900_DISEQC2_XOR 0xf1590001 /*CLKOUT27CFG*/ -#define R0900_CLKOUT27CFG 0xf15a -#define F0900_CLKOUT27_OPD 0xf15a0080 -#define F0900_CLKOUT27_CONFIG 0xf15a007e -#define F0900_CLKOUT27_XOR 0xf15a0001 +#define R0900_CLKOUT27CFG 0xf15a +#define F0900_CLKOUT27_OPD 0xf15a0080 +#define F0900_CLKOUT27_CONFIG 0xf15a007e +#define F0900_CLKOUT27_XOR 0xf15a0001 /*ERROR1CFG*/ -#define R0900_ERROR1CFG 0xf15b -#define F0900_ERROR1_OPD 0xf15b0080 -#define F0900_ERROR1_CONFIG 0xf15b007e -#define F0900_ERROR1_XOR 0xf15b0001 +#define R0900_ERROR1CFG 0xf15b +#define F0900_ERROR1_OPD 0xf15b0080 +#define F0900_ERROR1_CONFIG 0xf15b007e +#define F0900_ERROR1_XOR 0xf15b0001 /*DPN1CFG*/ -#define R0900_DPN1CFG 0xf15c -#define F0900_DPN1_OPD 0xf15c0080 -#define F0900_DPN1_CONFIG 0xf15c007e -#define F0900_DPN1_XOR 0xf15c0001 +#define R0900_DPN1CFG 0xf15c +#define F0900_DPN1_OPD 0xf15c0080 +#define F0900_DPN1_CONFIG 0xf15c007e +#define F0900_DPN1_XOR 0xf15c0001 /*STROUT1CFG*/ -#define R0900_STROUT1CFG 0xf15d -#define F0900_STROUT1_OPD 0xf15d0080 -#define F0900_STROUT1_CONFIG 0xf15d007e -#define F0900_STROUT1_XOR 0xf15d0001 +#define R0900_STROUT1CFG 0xf15d +#define F0900_STROUT1_OPD 0xf15d0080 +#define F0900_STROUT1_CONFIG 0xf15d007e +#define F0900_STROUT1_XOR 0xf15d0001 /*CLKOUT1CFG*/ -#define R0900_CLKOUT1CFG 0xf15e -#define F0900_CLKOUT1_OPD 0xf15e0080 -#define F0900_CLKOUT1_CONFIG 0xf15e007e -#define F0900_CLKOUT1_XOR 0xf15e0001 +#define R0900_CLKOUT1CFG 0xf15e +#define F0900_CLKOUT1_OPD 0xf15e0080 +#define F0900_CLKOUT1_CONFIG 0xf15e007e +#define F0900_CLKOUT1_XOR 0xf15e0001 /*DATA71CFG*/ -#define R0900_DATA71CFG 0xf15f -#define F0900_DATA71_OPD 0xf15f0080 -#define F0900_DATA71_CONFIG 0xf15f007e -#define F0900_DATA71_XOR 0xf15f0001 +#define R0900_DATA71CFG 0xf15f +#define F0900_DATA71_OPD 0xf15f0080 +#define F0900_DATA71_CONFIG 0xf15f007e +#define F0900_DATA71_XOR 0xf15f0001 /*ERROR2CFG*/ -#define R0900_ERROR2CFG 0xf160 -#define F0900_ERROR2_OPD 0xf1600080 -#define F0900_ERROR2_CONFIG 0xf160007e -#define F0900_ERROR2_XOR 0xf1600001 +#define R0900_ERROR2CFG 0xf160 +#define F0900_ERROR2_OPD 0xf1600080 +#define F0900_ERROR2_CONFIG 0xf160007e +#define F0900_ERROR2_XOR 0xf1600001 /*DPN2CFG*/ -#define R0900_DPN2CFG 0xf161 -#define F0900_DPN2_OPD 0xf1610080 -#define F0900_DPN2_CONFIG 0xf161007e -#define F0900_DPN2_XOR 0xf1610001 +#define R0900_DPN2CFG 0xf161 +#define F0900_DPN2_OPD 0xf1610080 +#define F0900_DPN2_CONFIG 0xf161007e +#define F0900_DPN2_XOR 0xf1610001 /*STROUT2CFG*/ -#define R0900_STROUT2CFG 0xf162 -#define F0900_STROUT2_OPD 0xf1620080 -#define F0900_STROUT2_CONFIG 0xf162007e -#define F0900_STROUT2_XOR 0xf1620001 +#define R0900_STROUT2CFG 0xf162 +#define F0900_STROUT2_OPD 0xf1620080 +#define F0900_STROUT2_CONFIG 0xf162007e +#define F0900_STROUT2_XOR 0xf1620001 /*CLKOUT2CFG*/ -#define R0900_CLKOUT2CFG 0xf163 -#define F0900_CLKOUT2_OPD 0xf1630080 -#define F0900_CLKOUT2_CONFIG 0xf163007e -#define F0900_CLKOUT2_XOR 0xf1630001 +#define R0900_CLKOUT2CFG 0xf163 +#define F0900_CLKOUT2_OPD 0xf1630080 +#define F0900_CLKOUT2_CONFIG 0xf163007e +#define F0900_CLKOUT2_XOR 0xf1630001 /*DATA72CFG*/ -#define R0900_DATA72CFG 0xf164 -#define F0900_DATA72_OPD 0xf1640080 -#define F0900_DATA72_CONFIG 0xf164007e -#define F0900_DATA72_XOR 0xf1640001 +#define R0900_DATA72CFG 0xf164 +#define F0900_DATA72_OPD 0xf1640080 +#define F0900_DATA72_CONFIG 0xf164007e +#define F0900_DATA72_XOR 0xf1640001 /*ERROR3CFG*/ -#define R0900_ERROR3CFG 0xf165 -#define F0900_ERROR3_OPD 0xf1650080 -#define F0900_ERROR3_CONFIG 0xf165007e -#define F0900_ERROR3_XOR 0xf1650001 +#define R0900_ERROR3CFG 0xf165 +#define F0900_ERROR3_OPD 0xf1650080 +#define F0900_ERROR3_CONFIG 0xf165007e +#define F0900_ERROR3_XOR 0xf1650001 /*DPN3CFG*/ -#define R0900_DPN3CFG 0xf166 -#define F0900_DPN3_OPD 0xf1660080 -#define F0900_DPN3_CONFIG 0xf166007e -#define F0900_DPN3_XOR 0xf1660001 +#define R0900_DPN3CFG 0xf166 +#define F0900_DPN3_OPD 0xf1660080 +#define F0900_DPN3_CONFIG 0xf166007e +#define F0900_DPN3_XOR 0xf1660001 /*STROUT3CFG*/ -#define R0900_STROUT3CFG 0xf167 -#define F0900_STROUT3_OPD 0xf1670080 -#define F0900_STROUT3_CONFIG 0xf167007e -#define F0900_STROUT3_XOR 0xf1670001 +#define R0900_STROUT3CFG 0xf167 +#define F0900_STROUT3_OPD 0xf1670080 +#define F0900_STROUT3_CONFIG 0xf167007e +#define F0900_STROUT3_XOR 0xf1670001 /*CLKOUT3CFG*/ -#define R0900_CLKOUT3CFG 0xf168 -#define F0900_CLKOUT3_OPD 0xf1680080 -#define F0900_CLKOUT3_CONFIG 0xf168007e -#define F0900_CLKOUT3_XOR 0xf1680001 +#define R0900_CLKOUT3CFG 0xf168 +#define F0900_CLKOUT3_OPD 0xf1680080 +#define F0900_CLKOUT3_CONFIG 0xf168007e +#define F0900_CLKOUT3_XOR 0xf1680001 /*DATA73CFG*/ -#define R0900_DATA73CFG 0xf169 -#define F0900_DATA73_OPD 0xf1690080 -#define F0900_DATA73_CONFIG 0xf169007e -#define F0900_DATA73_XOR 0xf1690001 +#define R0900_DATA73CFG 0xf169 +#define F0900_DATA73_OPD 0xf1690080 +#define F0900_DATA73_CONFIG 0xf169007e +#define F0900_DATA73_XOR 0xf1690001 + +/*STRSTATUS1*/ +#define R0900_STRSTATUS1 0xf16a +#define F0900_STRSTATUS_SEL2 0xf16a00f0 +#define F0900_STRSTATUS_SEL1 0xf16a000f + +/*STRSTATUS2*/ +#define R0900_STRSTATUS2 0xf16b +#define F0900_STRSTATUS_SEL4 0xf16b00f0 +#define F0900_STRSTATUS_SEL3 0xf16b000f + +/*STRSTATUS3*/ +#define R0900_STRSTATUS3 0xf16c +#define F0900_STRSTATUS_SEL6 0xf16c00f0 +#define F0900_STRSTATUS_SEL5 0xf16c000f /*FSKTFC2*/ -#define R0900_FSKTFC2 0xf170 -#define F0900_FSKT_KMOD 0xf17000fc -#define F0900_FSKT_CAR2 0xf1700003 +#define R0900_FSKTFC2 0xf170 +#define F0900_FSKT_KMOD 0xf17000fc +#define F0900_FSKT_CAR2 0xf1700003 /*FSKTFC1*/ -#define R0900_FSKTFC1 0xf171 -#define F0900_FSKT_CAR1 0xf17100ff +#define R0900_FSKTFC1 0xf171 +#define F0900_FSKT_CAR1 0xf17100ff /*FSKTFC0*/ -#define R0900_FSKTFC0 0xf172 -#define F0900_FSKT_CAR0 0xf17200ff +#define R0900_FSKTFC0 0xf172 +#define F0900_FSKT_CAR0 0xf17200ff /*FSKTDELTAF1*/ -#define R0900_FSKTDELTAF1 0xf173 -#define F0900_FSKT_DELTAF1 0xf173000f +#define R0900_FSKTDELTAF1 0xf173 +#define F0900_FSKT_DELTAF1 0xf173000f /*FSKTDELTAF0*/ -#define R0900_FSKTDELTAF0 0xf174 -#define F0900_FSKT_DELTAF0 0xf17400ff +#define R0900_FSKTDELTAF0 0xf174 +#define F0900_FSKT_DELTAF0 0xf17400ff /*FSKTCTRL*/ -#define R0900_FSKTCTRL 0xf175 -#define F0900_FSKT_EN_SGN 0xf1750040 -#define F0900_FSKT_MOD_SGN 0xf1750020 -#define F0900_FSKT_MOD_EN 0xf175001c -#define F0900_FSKT_DACMODE 0xf1750003 +#define R0900_FSKTCTRL 0xf175 +#define F0900_FSKT_EN_SGN 0xf1750040 +#define F0900_FSKT_MOD_SGN 0xf1750020 +#define F0900_FSKT_MOD_EN 0xf175001c +#define F0900_FSKT_DACMODE 0xf1750003 /*FSKRFC2*/ -#define R0900_FSKRFC2 0xf176 -#define F0900_FSKR_DETSGN 0xf1760040 -#define F0900_FSKR_OUTSGN 0xf1760020 -#define F0900_FSKR_KAGC 0xf176001c -#define F0900_FSKR_CAR2 0xf1760003 +#define R0900_FSKRFC2 0xf176 +#define F0900_FSKR_DETSGN 0xf1760040 +#define F0900_FSKR_OUTSGN 0xf1760020 +#define F0900_FSKR_KAGC 0xf176001c +#define F0900_FSKR_CAR2 0xf1760003 /*FSKRFC1*/ -#define R0900_FSKRFC1 0xf177 -#define F0900_FSKR_CAR1 0xf17700ff +#define R0900_FSKRFC1 0xf177 +#define F0900_FSKR_CAR1 0xf17700ff /*FSKRFC0*/ -#define R0900_FSKRFC0 0xf178 -#define F0900_FSKR_CAR0 0xf17800ff +#define R0900_FSKRFC0 0xf178 +#define F0900_FSKR_CAR0 0xf17800ff /*FSKRK1*/ -#define R0900_FSKRK1 0xf179 -#define F0900_FSKR_K1_EXP 0xf17900e0 -#define F0900_FSKR_K1_MANT 0xf179001f +#define R0900_FSKRK1 0xf179 +#define F0900_FSKR_K1_EXP 0xf17900e0 +#define F0900_FSKR_K1_MANT 0xf179001f /*FSKRK2*/ -#define R0900_FSKRK2 0xf17a -#define F0900_FSKR_K2_EXP 0xf17a00e0 -#define F0900_FSKR_K2_MANT 0xf17a001f +#define R0900_FSKRK2 0xf17a +#define F0900_FSKR_K2_EXP 0xf17a00e0 +#define F0900_FSKR_K2_MANT 0xf17a001f /*FSKRAGCR*/ -#define R0900_FSKRAGCR 0xf17b -#define F0900_FSKR_OUTCTL 0xf17b00c0 -#define F0900_FSKR_AGC_REF 0xf17b003f +#define R0900_FSKRAGCR 0xf17b +#define F0900_FSKR_OUTCTL 0xf17b00c0 +#define F0900_FSKR_AGC_REF 0xf17b003f /*FSKRAGC*/ -#define R0900_FSKRAGC 0xf17c -#define F0900_FSKR_AGC_ACCU 0xf17c00ff +#define R0900_FSKRAGC 0xf17c +#define F0900_FSKR_AGC_ACCU 0xf17c00ff /*FSKRALPHA*/ -#define R0900_FSKRALPHA 0xf17d -#define F0900_FSKR_ALPHA_EXP 0xf17d001c -#define F0900_FSKR_ALPHA_M 0xf17d0003 +#define R0900_FSKRALPHA 0xf17d +#define F0900_FSKR_ALPHA_EXP 0xf17d001c +#define F0900_FSKR_ALPHA_M 0xf17d0003 /*FSKRPLTH1*/ -#define R0900_FSKRPLTH1 0xf17e -#define F0900_FSKR_BETA 0xf17e00f0 -#define F0900_FSKR_PLL_TRESH1 0xf17e000f +#define R0900_FSKRPLTH1 0xf17e +#define F0900_FSKR_BETA 0xf17e00f0 +#define F0900_FSKR_PLL_TRESH1 0xf17e000f /*FSKRPLTH0*/ -#define R0900_FSKRPLTH0 0xf17f -#define F0900_FSKR_PLL_TRESH0 0xf17f00ff +#define R0900_FSKRPLTH0 0xf17f +#define F0900_FSKR_PLL_TRESH0 0xf17f00ff /*FSKRDF1*/ -#define R0900_FSKRDF1 0xf180 -#define F0900_FSKR_OUT 0xf1800080 -#define F0900_FSKR_DELTAF1 0xf180001f +#define R0900_FSKRDF1 0xf180 +#define F0900_FSKR_OUT 0xf1800080 +#define F0900_FSKR_DELTAF1 0xf180001f /*FSKRDF0*/ -#define R0900_FSKRDF0 0xf181 -#define F0900_FSKR_DELTAF0 0xf18100ff +#define R0900_FSKRDF0 0xf181 +#define F0900_FSKR_DELTAF0 0xf18100ff /*FSKRSTEPP*/ -#define R0900_FSKRSTEPP 0xf182 -#define F0900_FSKR_STEP_PLUS 0xf18200ff +#define R0900_FSKRSTEPP 0xf182 +#define F0900_FSKR_STEP_PLUS 0xf18200ff /*FSKRSTEPM*/ -#define R0900_FSKRSTEPM 0xf183 -#define F0900_FSKR_STEP_MINUS 0xf18300ff +#define R0900_FSKRSTEPM 0xf183 +#define F0900_FSKR_STEP_MINUS 0xf18300ff /*FSKRDET1*/ -#define R0900_FSKRDET1 0xf184 -#define F0900_FSKR_DETECT 0xf1840080 -#define F0900_FSKR_CARDET_ACCU1 0xf184000f +#define R0900_FSKRDET1 0xf184 +#define F0900_FSKR_DETECT 0xf1840080 +#define F0900_FSKR_CARDET_ACCU1 0xf184000f /*FSKRDET0*/ -#define R0900_FSKRDET0 0xf185 -#define F0900_FSKR_CARDET_ACCU0 0xf18500ff +#define R0900_FSKRDET0 0xf185 +#define F0900_FSKR_CARDET_ACCU0 0xf18500ff /*FSKRDTH1*/ -#define R0900_FSKRDTH1 0xf186 -#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0 -#define F0900_FSKR_CARDET_THRESH1 0xf186000f +#define R0900_FSKRDTH1 0xf186 +#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0 +#define F0900_FSKR_CARDET_THRESH1 0xf186000f /*FSKRDTH0*/ -#define R0900_FSKRDTH0 0xf187 -#define F0900_FSKR_CARDET_THRESH0 0xf18700ff +#define R0900_FSKRDTH0 0xf187 +#define F0900_FSKR_CARDET_THRESH0 0xf18700ff /*FSKRLOSS*/ -#define R0900_FSKRLOSS 0xf188 -#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff +#define R0900_FSKRLOSS 0xf188 +#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff /*P2_DISTXCTL*/ -#define R0900_P2_DISTXCTL 0xf190 -#define F0900_P2_TIM_OFF 0xf1900080 -#define F0900_P2_DISEQC_RESET 0xf1900040 -#define F0900_P2_TIM_CMD 0xf1900030 -#define F0900_P2_DIS_PRECHARGE 0xf1900008 -#define F0900_P2_DISTX_MODE 0xf1900007 +#define R0900_P2_DISTXCTL 0xf190 +#define F0900_P2_TIM_OFF 0xf1900080 +#define F0900_P2_DISEQC_RESET 0xf1900040 +#define F0900_P2_TIM_CMD 0xf1900030 +#define F0900_P2_DIS_PRECHARGE 0xf1900008 +#define F0900_P2_DISTX_MODE 0xf1900007 /*P2_DISRXCTL*/ -#define R0900_P2_DISRXCTL 0xf191 -#define F0900_P2_RECEIVER_ON 0xf1910080 -#define F0900_P2_IGNO_SHORT22K 0xf1910040 -#define F0900_P2_ONECHIP_TRX 0xf1910020 -#define F0900_P2_EXT_ENVELOP 0xf1910010 -#define F0900_P2_PIN_SELECT 0xf191000c -#define F0900_P2_IRQ_RXEND 0xf1910002 -#define F0900_P2_IRQ_4NBYTES 0xf1910001 +#define R0900_P2_DISRXCTL 0xf191 +#define F0900_P2_RECEIVER_ON 0xf1910080 +#define F0900_P2_IGNO_SHORT22K 0xf1910040 +#define F0900_P2_ONECHIP_TRX 0xf1910020 +#define F0900_P2_EXT_ENVELOP 0xf1910010 +#define F0900_P2_PIN_SELECT0 0xf191000c +#define F0900_P2_IRQ_RXEND 0xf1910002 +#define F0900_P2_IRQ_4NBYTES 0xf1910001 /*P2_DISRX_ST0*/ -#define R0900_P2_DISRX_ST0 0xf194 -#define F0900_P2_RX_END 0xf1940080 -#define F0900_P2_RX_ACTIVE 0xf1940040 -#define F0900_P2_SHORT_22KHZ 0xf1940020 -#define F0900_P2_CONT_TONE 0xf1940010 -#define F0900_P2_FIFO_4BREADY 0xf1940008 -#define F0900_P2_FIFO_EMPTY 0xf1940004 -#define F0900_P2_ABORT_DISRX 0xf1940001 +#define R0900_P2_DISRX_ST0 0xf194 +#define F0900_P2_RX_END 0xf1940080 +#define F0900_P2_RX_ACTIVE 0xf1940040 +#define F0900_P2_SHORT_22KHZ 0xf1940020 +#define F0900_P2_CONT_TONE 0xf1940010 +#define F0900_P2_FIFO_4BREADY 0xf1940008 +#define F0900_P2_FIFO_EMPTY 0xf1940004 +#define F0900_P2_ABORT_DISRX 0xf1940001 /*P2_DISRX_ST1*/ -#define R0900_P2_DISRX_ST1 0xf195 -#define F0900_P2_RX_FAIL 0xf1950080 -#define F0900_P2_FIFO_PARITYFAIL 0xf1950040 -#define F0900_P2_RX_NONBYTE 0xf1950020 -#define F0900_P2_FIFO_OVERFLOW 0xf1950010 -#define F0900_P2_FIFO_BYTENBR 0xf195000f +#define R0900_P2_DISRX_ST1 0xf195 +#define F0900_P2_RX_FAIL 0xf1950080 +#define F0900_P2_FIFO_PARITYFAIL 0xf1950040 +#define F0900_P2_RX_NONBYTE 0xf1950020 +#define F0900_P2_FIFO_OVERFLOW 0xf1950010 +#define F0900_P2_FIFO_BYTENBR 0xf195000f /*P2_DISRXDATA*/ -#define R0900_P2_DISRXDATA 0xf196 -#define F0900_P2_DISRX_DATA 0xf19600ff +#define R0900_P2_DISRXDATA 0xf196 +#define F0900_P2_DISRX_DATA 0xf19600ff /*P2_DISTXDATA*/ -#define R0900_P2_DISTXDATA 0xf197 -#define F0900_P2_DISEQC_FIFO 0xf19700ff +#define R0900_P2_DISTXDATA 0xf197 +#define F0900_P2_DISEQC_FIFO 0xf19700ff /*P2_DISTXSTATUS*/ -#define R0900_P2_DISTXSTATUS 0xf198 -#define F0900_P2_TX_FAIL 0xf1980080 -#define F0900_P2_FIFO_FULL 0xf1980040 -#define F0900_P2_TX_IDLE 0xf1980020 -#define F0900_P2_GAP_BURST 0xf1980010 -#define F0900_P2_TXFIFO_BYTES 0xf198000f +#define R0900_P2_DISTXSTATUS 0xf198 +#define F0900_P2_TX_FAIL 0xf1980080 +#define F0900_P2_FIFO_FULL 0xf1980040 +#define F0900_P2_TX_IDLE 0xf1980020 +#define F0900_P2_GAP_BURST 0xf1980010 +#define F0900_P2_TXFIFO_BYTES 0xf198000f /*P2_F22TX*/ -#define R0900_P2_F22TX 0xf199 -#define F0900_P2_F22_REG 0xf19900ff +#define R0900_P2_F22TX 0xf199 +#define F0900_P2_F22_REG 0xf19900ff /*P2_F22RX*/ -#define R0900_P2_F22RX 0xf19a -#define F0900_P2_F22RX_REG 0xf19a00ff +#define R0900_P2_F22RX 0xf19a +#define F0900_P2_F22RX_REG 0xf19a00ff /*P2_ACRPRESC*/ -#define R0900_P2_ACRPRESC 0xf19c -#define F0900_P2_ACR_CODFRDY 0xf19c0008 -#define F0900_P2_ACR_PRESC 0xf19c0007 +#define R0900_P2_ACRPRESC 0xf19c +#define F0900_P2_ACR_PRESC 0xf19c0007 /*P2_ACRDIV*/ -#define R0900_P2_ACRDIV 0xf19d -#define F0900_P2_ACR_DIV 0xf19d00ff +#define R0900_P2_ACRDIV 0xf19d +#define F0900_P2_ACR_DIV 0xf19d00ff /*P1_DISTXCTL*/ -#define R0900_P1_DISTXCTL 0xf1a0 -#define F0900_P1_TIM_OFF 0xf1a00080 -#define F0900_P1_DISEQC_RESET 0xf1a00040 -#define F0900_P1_TIM_CMD 0xf1a00030 -#define F0900_P1_DIS_PRECHARGE 0xf1a00008 -#define F0900_P1_DISTX_MODE 0xf1a00007 +#define R0900_P1_DISTXCTL 0xf1a0 +#define DISTXCTL shiftx(R0900_P1_DISTXCTL, demod, 0x10) +#define F0900_P1_TIM_OFF 0xf1a00080 +#define F0900_P1_DISEQC_RESET 0xf1a00040 +#define DISEQC_RESET shiftx(F0900_P1_DISEQC_RESET, demod, 0x100000) +#define F0900_P1_TIM_CMD 0xf1a00030 +#define F0900_P1_DIS_PRECHARGE 0xf1a00008 +#define DIS_PRECHARGE shiftx(F0900_P1_DIS_PRECHARGE, demod, 0x100000) +#define F0900_P1_DISTX_MODE 0xf1a00007 +#define DISTX_MODE shiftx(F0900_P1_DISTX_MODE, demod, 0x100000) /*P1_DISRXCTL*/ -#define R0900_P1_DISRXCTL 0xf1a1 -#define F0900_P1_RECEIVER_ON 0xf1a10080 -#define F0900_P1_IGNO_SHORT22K 0xf1a10040 -#define F0900_P1_ONECHIP_TRX 0xf1a10020 -#define F0900_P1_EXT_ENVELOP 0xf1a10010 -#define F0900_P1_PIN_SELECT 0xf1a1000c -#define F0900_P1_IRQ_RXEND 0xf1a10002 -#define F0900_P1_IRQ_4NBYTES 0xf1a10001 +#define R0900_P1_DISRXCTL 0xf1a1 +#define DISRXCTL shiftx(R0900_P1_DISRXCTL, demod, 0x10) +#define F0900_P1_RECEIVER_ON 0xf1a10080 +#define F0900_P1_IGNO_SHORT22K 0xf1a10040 +#define F0900_P1_ONECHIP_TRX 0xf1a10020 +#define F0900_P1_EXT_ENVELOP 0xf1a10010 +#define F0900_P1_PIN_SELECT0 0xf1a1000c +#define F0900_P1_IRQ_RXEND 0xf1a10002 +#define F0900_P1_IRQ_4NBYTES 0xf1a10001 /*P1_DISRX_ST0*/ -#define R0900_P1_DISRX_ST0 0xf1a4 -#define F0900_P1_RX_END 0xf1a40080 -#define F0900_P1_RX_ACTIVE 0xf1a40040 -#define F0900_P1_SHORT_22KHZ 0xf1a40020 -#define F0900_P1_CONT_TONE 0xf1a40010 -#define F0900_P1_FIFO_4BREADY 0xf1a40008 -#define F0900_P1_FIFO_EMPTY 0xf1a40004 -#define F0900_P1_ABORT_DISRX 0xf1a40001 +#define R0900_P1_DISRX_ST0 0xf1a4 +#define DISRX_ST0 shiftx(R0900_P1_DISRX_ST0, demod, 0x10) +#define F0900_P1_RX_END 0xf1a40080 +#define RX_END shiftx(F0900_P1_RX_END, demod, 0x100000) +#define F0900_P1_RX_ACTIVE 0xf1a40040 +#define F0900_P1_SHORT_22KHZ 0xf1a40020 +#define F0900_P1_CONT_TONE 0xf1a40010 +#define F0900_P1_FIFO_4BREADY 0xf1a40008 +#define F0900_P1_FIFO_EMPTY 0xf1a40004 +#define F0900_P1_ABORT_DISRX 0xf1a40001 /*P1_DISRX_ST1*/ -#define R0900_P1_DISRX_ST1 0xf1a5 -#define F0900_P1_RX_FAIL 0xf1a50080 -#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040 -#define F0900_P1_RX_NONBYTE 0xf1a50020 -#define F0900_P1_FIFO_OVERFLOW 0xf1a50010 -#define F0900_P1_FIFO_BYTENBR 0xf1a5000f +#define R0900_P1_DISRX_ST1 0xf1a5 +#define DISRX_ST1 shiftx(R0900_P1_DISRX_ST1, demod, 0x10) +#define F0900_P1_RX_FAIL 0xf1a50080 +#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040 +#define F0900_P1_RX_NONBYTE 0xf1a50020 +#define F0900_P1_FIFO_OVERFLOW 0xf1a50010 +#define F0900_P1_FIFO_BYTENBR 0xf1a5000f +#define FIFO_BYTENBR shiftx(F0900_P1_FIFO_BYTENBR, demod, 0x100000) /*P1_DISRXDATA*/ -#define R0900_P1_DISRXDATA 0xf1a6 -#define F0900_P1_DISRX_DATA 0xf1a600ff +#define R0900_P1_DISRXDATA 0xf1a6 +#define DISRXDATA shiftx(R0900_P1_DISRXDATA, demod, 0x10) +#define F0900_P1_DISRX_DATA 0xf1a600ff /*P1_DISTXDATA*/ -#define R0900_P1_DISTXDATA 0xf1a7 -#define F0900_P1_DISEQC_FIFO 0xf1a700ff +#define R0900_P1_DISTXDATA 0xf1a7 +#define DISTXDATA shiftx(R0900_P1_DISTXDATA, demod, 0x10) +#define F0900_P1_DISEQC_FIFO 0xf1a700ff /*P1_DISTXSTATUS*/ -#define R0900_P1_DISTXSTATUS 0xf1a8 -#define F0900_P1_TX_FAIL 0xf1a80080 -#define F0900_P1_FIFO_FULL 0xf1a80040 -#define F0900_P1_TX_IDLE 0xf1a80020 -#define F0900_P1_GAP_BURST 0xf1a80010 -#define F0900_P1_TXFIFO_BYTES 0xf1a8000f +#define R0900_P1_DISTXSTATUS 0xf1a8 +#define F0900_P1_TX_FAIL 0xf1a80080 +#define F0900_P1_FIFO_FULL 0xf1a80040 +#define FIFO_FULL shiftx(F0900_P1_FIFO_FULL, demod, 0x100000) +#define F0900_P1_TX_IDLE 0xf1a80020 +#define TX_IDLE shiftx(F0900_P1_TX_IDLE, demod, 0x100000) +#define F0900_P1_GAP_BURST 0xf1a80010 +#define F0900_P1_TXFIFO_BYTES 0xf1a8000f /*P1_F22TX*/ -#define R0900_P1_F22TX 0xf1a9 -#define F0900_P1_F22_REG 0xf1a900ff +#define R0900_P1_F22TX 0xf1a9 +#define F22TX shiftx(R0900_P1_F22TX, demod, 0x10) +#define F0900_P1_F22_REG 0xf1a900ff /*P1_F22RX*/ -#define R0900_P1_F22RX 0xf1aa -#define F0900_P1_F22RX_REG 0xf1aa00ff +#define R0900_P1_F22RX 0xf1aa +#define F22RX shiftx(R0900_P1_F22RX, demod, 0x10) +#define F0900_P1_F22RX_REG 0xf1aa00ff /*P1_ACRPRESC*/ -#define R0900_P1_ACRPRESC 0xf1ac -#define F0900_P1_ACR_CODFRDY 0xf1ac0008 -#define F0900_P1_ACR_PRESC 0xf1ac0007 +#define R0900_P1_ACRPRESC 0xf1ac +#define ACRPRESC shiftx(R0900_P1_ACRPRESC, demod, 0x10) +#define F0900_P1_ACR_PRESC 0xf1ac0007 /*P1_ACRDIV*/ -#define R0900_P1_ACRDIV 0xf1ad -#define F0900_P1_ACR_DIV 0xf1ad00ff +#define R0900_P1_ACRDIV 0xf1ad +#define ACRDIV shiftx(R0900_P1_ACRDIV, demod, 0x10) +#define F0900_P1_ACR_DIV 0xf1ad00ff /*NCOARSE*/ -#define R0900_NCOARSE 0xf1b3 -#define F0900_M_DIV 0xf1b300ff +#define R0900_NCOARSE 0xf1b3 +#define F0900_M_DIV 0xf1b300ff /*SYNTCTRL*/ -#define R0900_SYNTCTRL 0xf1b6 -#define F0900_STANDBY 0xf1b60080 -#define F0900_BYPASSPLLCORE 0xf1b60040 -#define F0900_SELX1RATIO 0xf1b60020 -#define F0900_I2C_TUD 0xf1b60010 -#define F0900_STOP_PLL 0xf1b60008 -#define F0900_BYPASSPLLFSK 0xf1b60004 -#define F0900_SELOSCI 0xf1b60002 -#define F0900_BYPASSPLLADC 0xf1b60001 +#define R0900_SYNTCTRL 0xf1b6 +#define F0900_STANDBY 0xf1b60080 +#define F0900_BYPASSPLLCORE 0xf1b60040 +#define F0900_SELX1RATIO 0xf1b60020 +#define F0900_STOP_PLL 0xf1b60008 +#define F0900_BYPASSPLLFSK 0xf1b60004 +#define F0900_SELOSCI 0xf1b60002 +#define F0900_BYPASSPLLADC 0xf1b60001 /*FILTCTRL*/ -#define R0900_FILTCTRL 0xf1b7 -#define F0900_INV_CLK135 0xf1b70080 -#define F0900_PERM_BYPDIS 0xf1b70040 -#define F0900_SEL_FSKCKDIV 0xf1b70004 -#define F0900_INV_CLKFSK 0xf1b70002 -#define F0900_BYPASS_APPLI 0xf1b70001 +#define R0900_FILTCTRL 0xf1b7 +#define F0900_INV_CLK135 0xf1b70080 +#define F0900_SEL_FSKCKDIV 0xf1b70004 +#define F0900_INV_CLKFSK 0xf1b70002 +#define F0900_BYPASS_APPLI 0xf1b70001 /*PLLSTAT*/ -#define R0900_PLLSTAT 0xf1b8 -#define F0900_ACM_SEL 0xf1b80080 -#define F0900_DTV_SEL 0xf1b80040 -#define F0900_PLLLOCK 0xf1b80001 +#define R0900_PLLSTAT 0xf1b8 +#define F0900_PLLLOCK 0xf1b80001 /*STOPCLK1*/ -#define R0900_STOPCLK1 0xf1c2 -#define F0900_STOP_CLKPKDT2 0xf1c20040 -#define F0900_STOP_CLKPKDT1 0xf1c20020 -#define F0900_STOP_CLKFEC 0xf1c20010 -#define F0900_STOP_CLKADCI2 0xf1c20008 -#define F0900_INV_CLKADCI2 0xf1c20004 -#define F0900_STOP_CLKADCI1 0xf1c20002 -#define F0900_INV_CLKADCI1 0xf1c20001 +#define R0900_STOPCLK1 0xf1c2 +#define F0900_STOP_CLKPKDT2 0xf1c20040 +#define F0900_STOP_CLKPKDT1 0xf1c20020 +#define F0900_STOP_CLKFEC 0xf1c20010 +#define F0900_STOP_CLKADCI2 0xf1c20008 +#define F0900_INV_CLKADCI2 0xf1c20004 +#define F0900_STOP_CLKADCI1 0xf1c20002 +#define F0900_INV_CLKADCI1 0xf1c20001 /*STOPCLK2*/ -#define R0900_STOPCLK2 0xf1c3 -#define F0900_STOP_CLKSAMP2 0xf1c30010 -#define F0900_STOP_CLKSAMP1 0xf1c30008 -#define F0900_STOP_CLKVIT2 0xf1c30004 -#define F0900_STOP_CLKVIT1 0xf1c30002 -#define F0900_STOP_CLKTS 0xf1c30001 +#define R0900_STOPCLK2 0xf1c3 +#define F0900_STOP_CLKSAMP2 0xf1c30010 +#define F0900_STOP_CLKSAMP1 0xf1c30008 +#define F0900_STOP_CLKVIT2 0xf1c30004 +#define F0900_STOP_CLKVIT1 0xf1c30002 +#define STOP_CLKVIT shiftx(F0900_STOP_CLKVIT1, demod, -2) +#define F0900_STOP_CLKTS 0xf1c30001 /*TSTTNR0*/ -#define R0900_TSTTNR0 0xf1df -#define F0900_SEL_FSK 0xf1df0080 -#define F0900_FSK_PON 0xf1df0004 -#define F0900_FSK_OPENLOOP 0xf1df0002 +#define R0900_TSTTNR0 0xf1df +#define F0900_SEL_FSK 0xf1df0080 +#define F0900_FSK_PON 0xf1df0004 /*TSTTNR1*/ -#define R0900_TSTTNR1 0xf1e0 -#define F0900_BYPASS_ADC1 0xf1e00080 -#define F0900_INVADC1_CKOUT 0xf1e00040 -#define F0900_SELIQSRC1 0xf1e00030 -#define F0900_ADC1_PON 0xf1e00002 -#define F0900_ADC1_INMODE 0xf1e00001 +#define R0900_TSTTNR1 0xf1e0 +#define F0900_ADC1_PON 0xf1e00002 +#define F0900_ADC1_INMODE 0xf1e00001 /*TSTTNR2*/ -#define R0900_TSTTNR2 0xf1e1 -#define F0900_DISEQC1_PON 0xf1e10020 -#define F0900_DISEQC1_TEST 0xf1e1001f +#define R0900_TSTTNR2 0xf1e1 +#define F0900_DISEQC1_PON 0xf1e10020 /*TSTTNR3*/ -#define R0900_TSTTNR3 0xf1e2 -#define F0900_BYPASS_ADC2 0xf1e20080 -#define F0900_INVADC2_CKOUT 0xf1e20040 -#define F0900_SELIQSRC2 0xf1e20030 -#define F0900_ADC2_PON 0xf1e20002 -#define F0900_ADC2_INMODE 0xf1e20001 +#define R0900_TSTTNR3 0xf1e2 +#define F0900_ADC2_PON 0xf1e20002 +#define F0900_ADC2_INMODE 0xf1e20001 /*TSTTNR4*/ -#define R0900_TSTTNR4 0xf1e3 -#define F0900_DISEQC2_PON 0xf1e30020 -#define F0900_DISEQC2_TEST 0xf1e3001f +#define R0900_TSTTNR4 0xf1e3 +#define F0900_DISEQC2_PON 0xf1e30020 /*P2_IQCONST*/ -#define R0900_P2_IQCONST 0xf200 -#define F0900_P2_CONSTEL_SELECT 0xf2000060 -#define F0900_P2_IQSYMB_SEL 0xf200001f +#define R0900_P2_IQCONST 0xf200 +#define F0900_P2_CONSTEL_SELECT 0xf2000060 +#define F0900_P2_IQSYMB_SEL 0xf200001f /*P2_NOSCFG*/ -#define R0900_P2_NOSCFG 0xf201 -#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020 -#define F0900_P2_NOSPLH_BETA 0xf2010018 -#define F0900_P2_NOSDATA_BETA 0xf2010007 +#define R0900_P2_NOSCFG 0xf201 +#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020 +#define F0900_P2_NOSPLH_BETA 0xf2010018 +#define F0900_P2_NOSDATA_BETA 0xf2010007 /*P2_ISYMB*/ -#define R0900_P2_ISYMB 0xf202 -#define F0900_P2_I_SYMBOL 0xf20201ff +#define R0900_P2_ISYMB 0xf202 +#define F0900_P2_I_SYMBOL 0xf20201ff /*P2_QSYMB*/ -#define R0900_P2_QSYMB 0xf203 -#define F0900_P2_Q_SYMBOL 0xf20301ff +#define R0900_P2_QSYMB 0xf203 +#define F0900_P2_Q_SYMBOL 0xf20301ff /*P2_AGC1CFG*/ -#define R0900_P2_AGC1CFG 0xf204 -#define F0900_P2_DC_FROZEN 0xf2040080 -#define F0900_P2_DC_CORRECT 0xf2040040 -#define F0900_P2_AMM_FROZEN 0xf2040020 -#define F0900_P2_AMM_CORRECT 0xf2040010 -#define F0900_P2_QUAD_FROZEN 0xf2040008 -#define F0900_P2_QUAD_CORRECT 0xf2040004 -#define F0900_P2_DCCOMP_SLOW 0xf2040002 -#define F0900_P2_IQMISM_SLOW 0xf2040001 +#define R0900_P2_AGC1CFG 0xf204 +#define F0900_P2_DC_FROZEN 0xf2040080 +#define F0900_P2_DC_CORRECT 0xf2040040 +#define F0900_P2_AMM_FROZEN 0xf2040020 +#define F0900_P2_AMM_CORRECT 0xf2040010 +#define F0900_P2_QUAD_FROZEN 0xf2040008 +#define F0900_P2_QUAD_CORRECT 0xf2040004 /*P2_AGC1CN*/ -#define R0900_P2_AGC1CN 0xf206 -#define F0900_P2_AGC1_LOCKED 0xf2060080 -#define F0900_P2_AGC1_OVERFLOW 0xf2060040 -#define F0900_P2_AGC1_NOSLOWLK 0xf2060020 -#define F0900_P2_AGC1_MINPOWER 0xf2060010 -#define F0900_P2_AGCOUT_FAST 0xf2060008 -#define F0900_P2_AGCIQ_BETA 0xf2060007 +#define R0900_P2_AGC1CN 0xf206 +#define F0900_P2_AGC1_LOCKED 0xf2060080 +#define F0900_P2_AGC1_MINPOWER 0xf2060010 +#define F0900_P2_AGCOUT_FAST 0xf2060008 +#define F0900_P2_AGCIQ_BETA 0xf2060007 /*P2_AGC1REF*/ -#define R0900_P2_AGC1REF 0xf207 -#define F0900_P2_AGCIQ_REF 0xf20700ff +#define R0900_P2_AGC1REF 0xf207 +#define F0900_P2_AGCIQ_REF 0xf20700ff /*P2_IDCCOMP*/ -#define R0900_P2_IDCCOMP 0xf208 -#define F0900_P2_IAVERAGE_ADJ 0xf20801ff +#define R0900_P2_IDCCOMP 0xf208 +#define F0900_P2_IAVERAGE_ADJ 0xf20801ff /*P2_QDCCOMP*/ -#define R0900_P2_QDCCOMP 0xf209 -#define F0900_P2_QAVERAGE_ADJ 0xf20901ff +#define R0900_P2_QDCCOMP 0xf209 +#define F0900_P2_QAVERAGE_ADJ 0xf20901ff /*P2_POWERI*/ -#define R0900_P2_POWERI 0xf20a -#define F0900_P2_POWER_I 0xf20a00ff +#define R0900_P2_POWERI 0xf20a +#define F0900_P2_POWER_I 0xf20a00ff /*P2_POWERQ*/ -#define R0900_P2_POWERQ 0xf20b -#define F0900_P2_POWER_Q 0xf20b00ff +#define R0900_P2_POWERQ 0xf20b +#define F0900_P2_POWER_Q 0xf20b00ff /*P2_AGC1AMM*/ -#define R0900_P2_AGC1AMM 0xf20c -#define F0900_P2_AMM_VALUE 0xf20c00ff +#define R0900_P2_AGC1AMM 0xf20c +#define F0900_P2_AMM_VALUE 0xf20c00ff /*P2_AGC1QUAD*/ -#define R0900_P2_AGC1QUAD 0xf20d -#define F0900_P2_QUAD_VALUE 0xf20d01ff +#define R0900_P2_AGC1QUAD 0xf20d +#define F0900_P2_QUAD_VALUE 0xf20d01ff /*P2_AGCIQIN1*/ -#define R0900_P2_AGCIQIN1 0xf20e -#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff +#define R0900_P2_AGCIQIN1 0xf20e +#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff /*P2_AGCIQIN0*/ -#define R0900_P2_AGCIQIN0 0xf20f -#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff +#define R0900_P2_AGCIQIN0 0xf20f +#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff /*P2_DEMOD*/ -#define R0900_P2_DEMOD 0xf210 -#define F0900_P2_DEMOD_STOP 0xf2100040 -#define F0900_P2_SPECINV_CONTROL 0xf2100030 -#define F0900_P2_FORCE_ENASAMP 0xf2100008 -#define F0900_P2_MANUAL_ROLLOFF 0xf2100004 -#define F0900_P2_ROLLOFF_CONTROL 0xf2100003 +#define R0900_P2_DEMOD 0xf210 +#define F0900_P2_MANUALS2_ROLLOFF 0xf2100080 +#define F0900_P2_SPECINV_CONTROL 0xf2100030 +#define F0900_P2_FORCE_ENASAMP 0xf2100008 +#define F0900_P2_MANUALSX_ROLLOFF 0xf2100004 +#define F0900_P2_ROLLOFF_CONTROL 0xf2100003 /*P2_DMDMODCOD*/ -#define R0900_P2_DMDMODCOD 0xf211 -#define F0900_P2_MANUAL_MODCOD 0xf2110080 -#define F0900_P2_DEMOD_MODCOD 0xf211007c -#define F0900_P2_DEMOD_TYPE 0xf2110003 +#define R0900_P2_DMDMODCOD 0xf211 +#define F0900_P2_MANUAL_MODCOD 0xf2110080 +#define F0900_P2_DEMOD_MODCOD 0xf211007c +#define F0900_P2_DEMOD_TYPE 0xf2110003 /*P2_DSTATUS*/ -#define R0900_P2_DSTATUS 0xf212 -#define F0900_P2_CAR_LOCK 0xf2120080 -#define F0900_P2_TMGLOCK_QUALITY 0xf2120060 -#define F0900_P2_SDVBS1_ENABLE 0xf2120010 -#define F0900_P2_LOCK_DEFINITIF 0xf2120008 -#define F0900_P2_TIMING_IS_LOCKED 0xf2120004 -#define F0900_P2_COARSE_TMGLOCK 0xf2120002 -#define F0900_P2_COARSE_CARLOCK 0xf2120001 +#define R0900_P2_DSTATUS 0xf212 +#define F0900_P2_CAR_LOCK 0xf2120080 +#define F0900_P2_TMGLOCK_QUALITY 0xf2120060 +#define F0900_P2_LOCK_DEFINITIF 0xf2120008 +#define F0900_P2_OVADC_DETECT 0xf2120001 /*P2_DSTATUS2*/ -#define R0900_P2_DSTATUS2 0xf213 -#define F0900_P2_DEMOD_DELOCK 0xf2130080 -#define F0900_P2_DEMOD_TIMEOUT 0xf2130040 -#define F0900_P2_MODCODRQ_SYNCTAG 0xf2130020 -#define F0900_P2_POLYPH_SATEVENT 0xf2130010 -#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008 -#define F0900_P2_AGC2_OVERFLOW 0xf2130004 -#define F0900_P2_CFR_OVERFLOW 0xf2130002 -#define F0900_P2_GAMMA_OVERUNDER 0xf2130001 +#define R0900_P2_DSTATUS2 0xf213 +#define F0900_P2_DEMOD_DELOCK 0xf2130080 +#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008 +#define F0900_P2_AGC2_OVERFLOW 0xf2130004 +#define F0900_P2_CFR_OVERFLOW 0xf2130002 +#define F0900_P2_GAMMA_OVERUNDER 0xf2130001 /*P2_DMDCFGMD*/ -#define R0900_P2_DMDCFGMD 0xf214 -#define F0900_P2_DVBS2_ENABLE 0xf2140080 -#define F0900_P2_DVBS1_ENABLE 0xf2140040 -#define F0900_P2_CFR_AUTOSCAN 0xf2140020 -#define F0900_P2_SCAN_ENABLE 0xf2140010 -#define F0900_P2_TUN_AUTOSCAN 0xf2140008 -#define F0900_P2_NOFORCE_RELOCK 0xf2140004 -#define F0900_P2_TUN_RNG 0xf2140003 +#define R0900_P2_DMDCFGMD 0xf214 +#define F0900_P2_DVBS2_ENABLE 0xf2140080 +#define F0900_P2_DVBS1_ENABLE 0xf2140040 +#define F0900_P2_SCAN_ENABLE 0xf2140010 +#define F0900_P2_CFR_AUTOSCAN 0xf2140008 +#define F0900_P2_TUN_RNG 0xf2140003 /*P2_DMDCFG2*/ -#define R0900_P2_DMDCFG2 0xf215 -#define F0900_P2_AGC1_WAITLOCK 0xf2150080 -#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040 -#define F0900_P2_OVERFLOW_TIMEOUT 0xf2150020 -#define F0900_P2_SCANFAIL_TIMEOUT 0xf2150010 -#define F0900_P2_DMDTOUT_BACK 0xf2150008 -#define F0900_P2_CARLOCK_S1ENABLE 0xf2150004 -#define F0900_P2_COARSE_LK3MODE 0xf2150002 -#define F0900_P2_COARSE_LK2MODE 0xf2150001 +#define R0900_P2_DMDCFG2 0xf215 +#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040 +#define F0900_P2_INFINITE_RELOCK 0xf2150010 /*P2_DMDISTATE*/ -#define R0900_P2_DMDISTATE 0xf216 -#define F0900_P2_I2C_NORESETDMODE 0xf2160080 -#define F0900_P2_FORCE_ETAPED 0xf2160040 -#define F0900_P2_SDMDRST_DIRCLK 0xf2160020 -#define F0900_P2_I2C_DEMOD_MODE 0xf216001f +#define R0900_P2_DMDISTATE 0xf216 +#define F0900_P2_I2C_DEMOD_MODE 0xf216001f /*P2_DMDT0M*/ -#define R0900_P2_DMDT0M 0xf217 -#define F0900_P2_DMDT0_MIN 0xf21700ff +#define R0900_P2_DMDT0M 0xf217 +#define F0900_P2_DMDT0_MIN 0xf21700ff /*P2_DMDSTATE*/ -#define R0900_P2_DMDSTATE 0xf21b -#define F0900_P2_DEMOD_LOCKED 0xf21b0080 -#define F0900_P2_HEADER_MODE 0xf21b0060 -#define F0900_P2_DEMOD_MODE 0xf21b001f +#define R0900_P2_DMDSTATE 0xf21b +#define F0900_P2_HEADER_MODE 0xf21b0060 /*P2_DMDFLYW*/ -#define R0900_P2_DMDFLYW 0xf21c -#define F0900_P2_I2C_IRQVAL 0xf21c00f0 -#define F0900_P2_FLYWHEEL_CPT 0xf21c000f +#define R0900_P2_DMDFLYW 0xf21c +#define F0900_P2_I2C_IRQVAL 0xf21c00f0 +#define F0900_P2_FLYWHEEL_CPT 0xf21c000f /*P2_DSTATUS3*/ -#define R0900_P2_DSTATUS3 0xf21d -#define F0900_P2_CFR_ZIGZAG 0xf21d0080 -#define F0900_P2_DEMOD_CFGMODE 0xf21d0060 -#define F0900_P2_GAMMA_LOWBAUDRATE 0xf21d0010 -#define F0900_P2_RELOCK_MODE 0xf21d0008 -#define F0900_P2_DEMOD_FAIL 0xf21d0004 -#define F0900_P2_ETAPE1A_DVBXMEM 0xf21d0003 +#define R0900_P2_DSTATUS3 0xf21d +#define F0900_P2_DEMOD_CFGMODE 0xf21d0060 /*P2_DMDCFG3*/ -#define R0900_P2_DMDCFG3 0xf21e -#define F0900_P2_DVBS1_TMGWAIT 0xf21e0080 -#define F0900_P2_NO_BWCENTERING 0xf21e0040 -#define F0900_P2_INV_SEQSRCH 0xf21e0020 -#define F0900_P2_DIS_SFRUPLOW_TRK 0xf21e0010 -#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008 -#define F0900_P2_LOCKTIME_MODE 0xf21e0007 +#define R0900_P2_DMDCFG3 0xf21e +#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008 /*P2_DMDCFG4*/ -#define R0900_P2_DMDCFG4 0xf21f -#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008 -#define F0900_P2_DIS_CLKENABLE 0xf21f0004 -#define F0900_P2_DIS_HDRDIVLOCK 0xf21f0002 -#define F0900_P2_NO_TNRWBINIT 0xf21f0001 +#define R0900_P2_DMDCFG4 0xf21f +#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008 /*P2_CORRELMANT*/ -#define R0900_P2_CORRELMANT 0xf220 -#define F0900_P2_CORREL_MANT 0xf22000ff +#define R0900_P2_CORRELMANT 0xf220 +#define F0900_P2_CORREL_MANT 0xf22000ff /*P2_CORRELABS*/ -#define R0900_P2_CORRELABS 0xf221 -#define F0900_P2_CORREL_ABS 0xf22100ff +#define R0900_P2_CORRELABS 0xf221 +#define F0900_P2_CORREL_ABS 0xf22100ff /*P2_CORRELEXP*/ -#define R0900_P2_CORRELEXP 0xf222 -#define F0900_P2_CORREL_ABSEXP 0xf22200f0 -#define F0900_P2_CORREL_EXP 0xf222000f +#define R0900_P2_CORRELEXP 0xf222 +#define F0900_P2_CORREL_ABSEXP 0xf22200f0 +#define F0900_P2_CORREL_EXP 0xf222000f /*P2_PLHMODCOD*/ -#define R0900_P2_PLHMODCOD 0xf224 -#define F0900_P2_SPECINV_DEMOD 0xf2240080 -#define F0900_P2_PLH_MODCOD 0xf224007c -#define F0900_P2_PLH_TYPE 0xf2240003 - -/*P2_AGCK32*/ -#define R0900_P2_AGCK32 0xf22b -#define F0900_P2_R3ADJOFF_32APSK 0xf22b0080 -#define F0900_P2_R2ADJOFF_32APSK 0xf22b0040 -#define F0900_P2_R1ADJOFF_32APSK 0xf22b0020 -#define F0900_P2_RADJ_32APSK 0xf22b001f +#define R0900_P2_PLHMODCOD 0xf224 +#define F0900_P2_SPECINV_DEMOD 0xf2240080 +#define F0900_P2_PLH_MODCOD 0xf224007c +#define F0900_P2_PLH_TYPE 0xf2240003 + +/*P2_DMDREG*/ +#define R0900_P2_DMDREG 0xf225 +#define F0900_P2_DECIM_PLFRAMES 0xf2250001 /*P2_AGC2O*/ -#define R0900_P2_AGC2O 0xf22c -#define F0900_P2_AGC2REF_ADJUSTING 0xf22c0080 -#define F0900_P2_AGC2_COARSEFAST 0xf22c0040 -#define F0900_P2_AGC2_LKSQRT 0xf22c0020 -#define F0900_P2_AGC2_LKMODE 0xf22c0010 -#define F0900_P2_AGC2_LKEQUA 0xf22c0008 -#define F0900_P2_AGC2_COEF 0xf22c0007 +#define R0900_P2_AGC2O 0xf22c +#define F0900_P2_AGC2_COEF 0xf22c0007 /*P2_AGC2REF*/ -#define R0900_P2_AGC2REF 0xf22d -#define F0900_P2_AGC2_REF 0xf22d00ff +#define R0900_P2_AGC2REF 0xf22d +#define F0900_P2_AGC2_REF 0xf22d00ff /*P2_AGC1ADJ*/ -#define R0900_P2_AGC1ADJ 0xf22e -#define F0900_P2_AGC1ADJ_MANUAL 0xf22e0080 -#define F0900_P2_AGC1_ADJUSTED 0xf22e017f +#define R0900_P2_AGC1ADJ 0xf22e +#define F0900_P2_AGC1_ADJUSTED 0xf22e007f /*P2_AGC2I1*/ -#define R0900_P2_AGC2I1 0xf236 -#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff +#define R0900_P2_AGC2I1 0xf236 +#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff /*P2_AGC2I0*/ -#define R0900_P2_AGC2I0 0xf237 -#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff +#define R0900_P2_AGC2I0 0xf237 +#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff /*P2_CARCFG*/ -#define R0900_P2_CARCFG 0xf238 -#define F0900_P2_CFRUPLOW_AUTO 0xf2380080 -#define F0900_P2_CFRUPLOW_TEST 0xf2380040 -#define F0900_P2_EN_CAR2CENTER 0xf2380020 -#define F0900_P2_CARHDR_NODIV8 0xf2380010 -#define F0900_P2_I2C_ROTA 0xf2380008 -#define F0900_P2_ROTAON 0xf2380004 -#define F0900_P2_PH_DET_ALGO 0xf2380003 +#define R0900_P2_CARCFG 0xf238 +#define F0900_P2_CFRUPLOW_AUTO 0xf2380080 +#define F0900_P2_CFRUPLOW_TEST 0xf2380040 +#define F0900_P2_ROTAON 0xf2380004 +#define F0900_P2_PH_DET_ALGO 0xf2380003 /*P2_ACLC*/ -#define R0900_P2_ACLC 0xf239 -#define F0900_P2_STOP_S2ALPHA 0xf23900c0 -#define F0900_P2_CAR_ALPHA_MANT 0xf2390030 -#define F0900_P2_CAR_ALPHA_EXP 0xf239000f +#define R0900_P2_ACLC 0xf239 +#define F0900_P2_CAR_ALPHA_MANT 0xf2390030 +#define F0900_P2_CAR_ALPHA_EXP 0xf239000f /*P2_BCLC*/ -#define R0900_P2_BCLC 0xf23a -#define F0900_P2_STOP_S2BETA 0xf23a00c0 -#define F0900_P2_CAR_BETA_MANT 0xf23a0030 -#define F0900_P2_CAR_BETA_EXP 0xf23a000f +#define R0900_P2_BCLC 0xf23a +#define F0900_P2_CAR_BETA_MANT 0xf23a0030 +#define F0900_P2_CAR_BETA_EXP 0xf23a000f /*P2_CARFREQ*/ -#define R0900_P2_CARFREQ 0xf23d -#define F0900_P2_KC_COARSE_EXP 0xf23d00f0 -#define F0900_P2_BETA_FREQ 0xf23d000f +#define R0900_P2_CARFREQ 0xf23d +#define F0900_P2_KC_COARSE_EXP 0xf23d00f0 +#define F0900_P2_BETA_FREQ 0xf23d000f /*P2_CARHDR*/ -#define R0900_P2_CARHDR 0xf23e -#define F0900_P2_K_FREQ_HDR 0xf23e00ff +#define R0900_P2_CARHDR 0xf23e +#define F0900_P2_K_FREQ_HDR 0xf23e00ff /*P2_LDT*/ -#define R0900_P2_LDT 0xf23f -#define F0900_P2_CARLOCK_THRES 0xf23f01ff +#define R0900_P2_LDT 0xf23f +#define F0900_P2_CARLOCK_THRES 0xf23f01ff /*P2_LDT2*/ -#define R0900_P2_LDT2 0xf240 -#define F0900_P2_CARLOCK_THRES2 0xf24001ff +#define R0900_P2_LDT2 0xf240 +#define F0900_P2_CARLOCK_THRES2 0xf24001ff /*P2_CFRICFG*/ -#define R0900_P2_CFRICFG 0xf241 -#define F0900_P2_CFRINIT_UNVALRNG 0xf2410080 -#define F0900_P2_CFRINIT_LUNVALCPT 0xf2410040 -#define F0900_P2_CFRINIT_ABORTDBL 0xf2410020 -#define F0900_P2_CFRINIT_ABORTPRED 0xf2410010 -#define F0900_P2_CFRINIT_UNVALSKIP 0xf2410008 -#define F0900_P2_CFRINIT_CSTINC 0xf2410004 -#define F0900_P2_NEG_CFRSTEP 0xf2410001 +#define R0900_P2_CFRICFG 0xf241 +#define F0900_P2_NEG_CFRSTEP 0xf2410001 /*P2_CFRUP1*/ -#define R0900_P2_CFRUP1 0xf242 -#define F0900_P2_CFR_UP1 0xf24201ff +#define R0900_P2_CFRUP1 0xf242 +#define F0900_P2_CFR_UP1 0xf24201ff /*P2_CFRUP0*/ -#define R0900_P2_CFRUP0 0xf243 -#define F0900_P2_CFR_UP0 0xf24300ff +#define R0900_P2_CFRUP0 0xf243 +#define F0900_P2_CFR_UP0 0xf24300ff /*P2_CFRLOW1*/ -#define R0900_P2_CFRLOW1 0xf246 -#define F0900_P2_CFR_LOW1 0xf24601ff +#define R0900_P2_CFRLOW1 0xf246 +#define F0900_P2_CFR_LOW1 0xf24601ff /*P2_CFRLOW0*/ -#define R0900_P2_CFRLOW0 0xf247 -#define F0900_P2_CFR_LOW0 0xf24700ff +#define R0900_P2_CFRLOW0 0xf247 +#define F0900_P2_CFR_LOW0 0xf24700ff /*P2_CFRINIT1*/ -#define R0900_P2_CFRINIT1 0xf248 -#define F0900_P2_CFR_INIT1 0xf24801ff +#define R0900_P2_CFRINIT1 0xf248 +#define F0900_P2_CFR_INIT1 0xf24801ff /*P2_CFRINIT0*/ -#define R0900_P2_CFRINIT0 0xf249 -#define F0900_P2_CFR_INIT0 0xf24900ff +#define R0900_P2_CFRINIT0 0xf249 +#define F0900_P2_CFR_INIT0 0xf24900ff /*P2_CFRINC1*/ -#define R0900_P2_CFRINC1 0xf24a -#define F0900_P2_MANUAL_CFRINC 0xf24a0080 -#define F0900_P2_CFR_INC1 0xf24a017f +#define R0900_P2_CFRINC1 0xf24a +#define F0900_P2_MANUAL_CFRINC 0xf24a0080 +#define F0900_P2_CFR_INC1 0xf24a003f /*P2_CFRINC0*/ -#define R0900_P2_CFRINC0 0xf24b -#define F0900_P2_CFR_INC0 0xf24b00f0 +#define R0900_P2_CFRINC0 0xf24b +#define F0900_P2_CFR_INC0 0xf24b00f8 /*P2_CFR2*/ -#define R0900_P2_CFR2 0xf24c -#define F0900_P2_CAR_FREQ2 0xf24c01ff +#define R0900_P2_CFR2 0xf24c +#define F0900_P2_CAR_FREQ2 0xf24c01ff /*P2_CFR1*/ -#define R0900_P2_CFR1 0xf24d -#define F0900_P2_CAR_FREQ1 0xf24d00ff +#define R0900_P2_CFR1 0xf24d +#define F0900_P2_CAR_FREQ1 0xf24d00ff /*P2_CFR0*/ -#define R0900_P2_CFR0 0xf24e -#define F0900_P2_CAR_FREQ0 0xf24e00ff +#define R0900_P2_CFR0 0xf24e +#define F0900_P2_CAR_FREQ0 0xf24e00ff /*P2_LDI*/ -#define R0900_P2_LDI 0xf24f -#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff +#define R0900_P2_LDI 0xf24f +#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff /*P2_TMGCFG*/ -#define R0900_P2_TMGCFG 0xf250 -#define F0900_P2_TMGLOCK_BETA 0xf25000c0 -#define F0900_P2_NOTMG_GROUPDELAY 0xf2500020 -#define F0900_P2_DO_TIMING_CORR 0xf2500010 -#define F0900_P2_MANUAL_SCAN 0xf250000c -#define F0900_P2_TMG_MINFREQ 0xf2500003 +#define R0900_P2_TMGCFG 0xf250 +#define F0900_P2_TMGLOCK_BETA 0xf25000c0 +#define F0900_P2_DO_TIMING_CORR 0xf2500010 +#define F0900_P2_TMG_MINFREQ 0xf2500003 /*P2_RTC*/ -#define R0900_P2_RTC 0xf251 -#define F0900_P2_TMGALPHA_EXP 0xf25100f0 -#define F0900_P2_TMGBETA_EXP 0xf251000f +#define R0900_P2_RTC 0xf251 +#define F0900_P2_TMGALPHA_EXP 0xf25100f0 +#define F0900_P2_TMGBETA_EXP 0xf251000f /*P2_RTCS2*/ -#define R0900_P2_RTCS2 0xf252 -#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0 -#define F0900_P2_TMGBETAS2_EXP 0xf252000f +#define R0900_P2_RTCS2 0xf252 +#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0 +#define F0900_P2_TMGBETAS2_EXP 0xf252000f /*P2_TMGTHRISE*/ -#define R0900_P2_TMGTHRISE 0xf253 -#define F0900_P2_TMGLOCK_THRISE 0xf25300ff +#define R0900_P2_TMGTHRISE 0xf253 +#define F0900_P2_TMGLOCK_THRISE 0xf25300ff /*P2_TMGTHFALL*/ -#define R0900_P2_TMGTHFALL 0xf254 -#define F0900_P2_TMGLOCK_THFALL 0xf25400ff +#define R0900_P2_TMGTHFALL 0xf254 +#define F0900_P2_TMGLOCK_THFALL 0xf25400ff /*P2_SFRUPRATIO*/ -#define R0900_P2_SFRUPRATIO 0xf255 -#define F0900_P2_SFR_UPRATIO 0xf25500ff +#define R0900_P2_SFRUPRATIO 0xf255 +#define F0900_P2_SFR_UPRATIO 0xf25500ff /*P2_SFRLOWRATIO*/ -#define R0900_P2_SFRLOWRATIO 0xf256 -#define F0900_P2_SFR_LOWRATIO 0xf25600ff +#define R0900_P2_SFRLOWRATIO 0xf256 +#define F0900_P2_SFR_LOWRATIO 0xf25600ff /*P2_KREFTMG*/ -#define R0900_P2_KREFTMG 0xf258 -#define F0900_P2_KREF_TMG 0xf25800ff +#define R0900_P2_KREFTMG 0xf258 +#define F0900_P2_KREF_TMG 0xf25800ff /*P2_SFRSTEP*/ -#define R0900_P2_SFRSTEP 0xf259 -#define F0900_P2_SFR_SCANSTEP 0xf25900f0 -#define F0900_P2_SFR_CENTERSTEP 0xf259000f +#define R0900_P2_SFRSTEP 0xf259 +#define F0900_P2_SFR_SCANSTEP 0xf25900f0 +#define F0900_P2_SFR_CENTERSTEP 0xf259000f /*P2_TMGCFG2*/ -#define R0900_P2_TMGCFG2 0xf25a -#define F0900_P2_DIS_AUTOSAMP 0xf25a0008 -#define F0900_P2_SCANINIT_QUART 0xf25a0004 -#define F0900_P2_NOTMG_DVBS1DERAT 0xf25a0002 -#define F0900_P2_SFRRATIO_FINE 0xf25a0001 +#define R0900_P2_TMGCFG2 0xf25a +#define F0900_P2_SFRRATIO_FINE 0xf25a0001 + +/*P2_KREFTMG2*/ +#define R0900_P2_KREFTMG2 0xf25b +#define F0900_P2_KREF_TMG2 0xf25b00ff /*P2_SFRINIT1*/ -#define R0900_P2_SFRINIT1 0xf25e -#define F0900_P2_SFR_INIT1 0xf25e00ff +#define R0900_P2_SFRINIT1 0xf25e +#define F0900_P2_SFR_INIT1 0xf25e007f /*P2_SFRINIT0*/ -#define R0900_P2_SFRINIT0 0xf25f -#define F0900_P2_SFR_INIT0 0xf25f00ff +#define R0900_P2_SFRINIT0 0xf25f +#define F0900_P2_SFR_INIT0 0xf25f00ff /*P2_SFRUP1*/ -#define R0900_P2_SFRUP1 0xf260 -#define F0900_P2_AUTO_GUP 0xf2600080 -#define F0900_P2_SYMB_FREQ_UP1 0xf260007f +#define R0900_P2_SFRUP1 0xf260 +#define F0900_P2_AUTO_GUP 0xf2600080 +#define F0900_P2_SYMB_FREQ_UP1 0xf260007f /*P2_SFRUP0*/ -#define R0900_P2_SFRUP0 0xf261 -#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff +#define R0900_P2_SFRUP0 0xf261 +#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff /*P2_SFRLOW1*/ -#define R0900_P2_SFRLOW1 0xf262 -#define F0900_P2_AUTO_GLOW 0xf2620080 -#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f +#define R0900_P2_SFRLOW1 0xf262 +#define F0900_P2_AUTO_GLOW 0xf2620080 +#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f /*P2_SFRLOW0*/ -#define R0900_P2_SFRLOW0 0xf263 -#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff +#define R0900_P2_SFRLOW0 0xf263 +#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff /*P2_SFR3*/ -#define R0900_P2_SFR3 0xf264 -#define F0900_P2_SYMB_FREQ3 0xf26400ff +#define R0900_P2_SFR3 0xf264 +#define F0900_P2_SYMB_FREQ3 0xf26400ff /*P2_SFR2*/ -#define R0900_P2_SFR2 0xf265 -#define F0900_P2_SYMB_FREQ2 0xf26500ff +#define R0900_P2_SFR2 0xf265 +#define F0900_P2_SYMB_FREQ2 0xf26500ff /*P2_SFR1*/ -#define R0900_P2_SFR1 0xf266 -#define F0900_P2_SYMB_FREQ1 0xf26600ff +#define R0900_P2_SFR1 0xf266 +#define F0900_P2_SYMB_FREQ1 0xf26600ff /*P2_SFR0*/ -#define R0900_P2_SFR0 0xf267 -#define F0900_P2_SYMB_FREQ0 0xf26700ff +#define R0900_P2_SFR0 0xf267 +#define F0900_P2_SYMB_FREQ0 0xf26700ff /*P2_TMGREG2*/ -#define R0900_P2_TMGREG2 0xf268 -#define F0900_P2_TMGREG2 0xf26800ff +#define R0900_P2_TMGREG2 0xf268 +#define F0900_P2_TMGREG2 0xf26800ff /*P2_TMGREG1*/ -#define R0900_P2_TMGREG1 0xf269 -#define F0900_P2_TMGREG1 0xf26900ff +#define R0900_P2_TMGREG1 0xf269 +#define F0900_P2_TMGREG1 0xf26900ff /*P2_TMGREG0*/ -#define R0900_P2_TMGREG0 0xf26a -#define F0900_P2_TMGREG0 0xf26a00ff +#define R0900_P2_TMGREG0 0xf26a +#define F0900_P2_TMGREG0 0xf26a00ff /*P2_TMGLOCK1*/ -#define R0900_P2_TMGLOCK1 0xf26b -#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff +#define R0900_P2_TMGLOCK1 0xf26b +#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff /*P2_TMGLOCK0*/ -#define R0900_P2_TMGLOCK0 0xf26c -#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff +#define R0900_P2_TMGLOCK0 0xf26c +#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff /*P2_TMGOBS*/ -#define R0900_P2_TMGOBS 0xf26d -#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0 -#define F0900_P2_SCAN_SIGN 0xf26d0030 -#define F0900_P2_TMG_SCANNING 0xf26d0008 -#define F0900_P2_CHCENTERING_MODE 0xf26d0004 -#define F0900_P2_TMG_SCANFAIL 0xf26d0002 +#define R0900_P2_TMGOBS 0xf26d +#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0 /*P2_EQUALCFG*/ -#define R0900_P2_EQUALCFG 0xf26f -#define F0900_P2_NOTMG_NEGALWAIT 0xf26f0080 -#define F0900_P2_EQUAL_ON 0xf26f0040 -#define F0900_P2_SEL_EQUALCOR 0xf26f0038 -#define F0900_P2_MU_EQUALDFE 0xf26f0007 +#define R0900_P2_EQUALCFG 0xf26f +#define F0900_P2_EQUAL_ON 0xf26f0040 +#define F0900_P2_MU_EQUALDFE 0xf26f0007 /*P2_EQUAI1*/ -#define R0900_P2_EQUAI1 0xf270 -#define F0900_P2_EQUA_ACCI1 0xf27001ff +#define R0900_P2_EQUAI1 0xf270 +#define F0900_P2_EQUA_ACCI1 0xf27001ff /*P2_EQUAQ1*/ -#define R0900_P2_EQUAQ1 0xf271 -#define F0900_P2_EQUA_ACCQ1 0xf27101ff +#define R0900_P2_EQUAQ1 0xf271 +#define F0900_P2_EQUA_ACCQ1 0xf27101ff /*P2_EQUAI2*/ -#define R0900_P2_EQUAI2 0xf272 -#define F0900_P2_EQUA_ACCI2 0xf27201ff +#define R0900_P2_EQUAI2 0xf272 +#define F0900_P2_EQUA_ACCI2 0xf27201ff /*P2_EQUAQ2*/ -#define R0900_P2_EQUAQ2 0xf273 -#define F0900_P2_EQUA_ACCQ2 0xf27301ff +#define R0900_P2_EQUAQ2 0xf273 +#define F0900_P2_EQUA_ACCQ2 0xf27301ff /*P2_EQUAI3*/ -#define R0900_P2_EQUAI3 0xf274 -#define F0900_P2_EQUA_ACCI3 0xf27401ff +#define R0900_P2_EQUAI3 0xf274 +#define F0900_P2_EQUA_ACCI3 0xf27401ff /*P2_EQUAQ3*/ -#define R0900_P2_EQUAQ3 0xf275 -#define F0900_P2_EQUA_ACCQ3 0xf27501ff +#define R0900_P2_EQUAQ3 0xf275 +#define F0900_P2_EQUA_ACCQ3 0xf27501ff /*P2_EQUAI4*/ -#define R0900_P2_EQUAI4 0xf276 -#define F0900_P2_EQUA_ACCI4 0xf27601ff +#define R0900_P2_EQUAI4 0xf276 +#define F0900_P2_EQUA_ACCI4 0xf27601ff /*P2_EQUAQ4*/ -#define R0900_P2_EQUAQ4 0xf277 -#define F0900_P2_EQUA_ACCQ4 0xf27701ff +#define R0900_P2_EQUAQ4 0xf277 +#define F0900_P2_EQUA_ACCQ4 0xf27701ff /*P2_EQUAI5*/ -#define R0900_P2_EQUAI5 0xf278 -#define F0900_P2_EQUA_ACCI5 0xf27801ff +#define R0900_P2_EQUAI5 0xf278 +#define F0900_P2_EQUA_ACCI5 0xf27801ff /*P2_EQUAQ5*/ -#define R0900_P2_EQUAQ5 0xf279 -#define F0900_P2_EQUA_ACCQ5 0xf27901ff +#define R0900_P2_EQUAQ5 0xf279 +#define F0900_P2_EQUA_ACCQ5 0xf27901ff /*P2_EQUAI6*/ -#define R0900_P2_EQUAI6 0xf27a -#define F0900_P2_EQUA_ACCI6 0xf27a01ff +#define R0900_P2_EQUAI6 0xf27a +#define F0900_P2_EQUA_ACCI6 0xf27a01ff /*P2_EQUAQ6*/ -#define R0900_P2_EQUAQ6 0xf27b -#define F0900_P2_EQUA_ACCQ6 0xf27b01ff +#define R0900_P2_EQUAQ6 0xf27b +#define F0900_P2_EQUA_ACCQ6 0xf27b01ff /*P2_EQUAI7*/ -#define R0900_P2_EQUAI7 0xf27c -#define F0900_P2_EQUA_ACCI7 0xf27c01ff +#define R0900_P2_EQUAI7 0xf27c +#define F0900_P2_EQUA_ACCI7 0xf27c01ff /*P2_EQUAQ7*/ -#define R0900_P2_EQUAQ7 0xf27d -#define F0900_P2_EQUA_ACCQ7 0xf27d01ff +#define R0900_P2_EQUAQ7 0xf27d +#define F0900_P2_EQUA_ACCQ7 0xf27d01ff /*P2_EQUAI8*/ -#define R0900_P2_EQUAI8 0xf27e -#define F0900_P2_EQUA_ACCI8 0xf27e01ff +#define R0900_P2_EQUAI8 0xf27e +#define F0900_P2_EQUA_ACCI8 0xf27e01ff /*P2_EQUAQ8*/ -#define R0900_P2_EQUAQ8 0xf27f -#define F0900_P2_EQUA_ACCQ8 0xf27f01ff +#define R0900_P2_EQUAQ8 0xf27f +#define F0900_P2_EQUA_ACCQ8 0xf27f01ff /*P2_NNOSDATAT1*/ -#define R0900_P2_NNOSDATAT1 0xf280 -#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff +#define R0900_P2_NNOSDATAT1 0xf280 +#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff /*P2_NNOSDATAT0*/ -#define R0900_P2_NNOSDATAT0 0xf281 -#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff +#define R0900_P2_NNOSDATAT0 0xf281 +#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff /*P2_NNOSDATA1*/ -#define R0900_P2_NNOSDATA1 0xf282 -#define F0900_P2_NOSDATA_NORMED1 0xf28200ff +#define R0900_P2_NNOSDATA1 0xf282 +#define F0900_P2_NOSDATA_NORMED1 0xf28200ff /*P2_NNOSDATA0*/ -#define R0900_P2_NNOSDATA0 0xf283 -#define F0900_P2_NOSDATA_NORMED0 0xf28300ff +#define R0900_P2_NNOSDATA0 0xf283 +#define F0900_P2_NOSDATA_NORMED0 0xf28300ff /*P2_NNOSPLHT1*/ -#define R0900_P2_NNOSPLHT1 0xf284 -#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff +#define R0900_P2_NNOSPLHT1 0xf284 +#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff /*P2_NNOSPLHT0*/ -#define R0900_P2_NNOSPLHT0 0xf285 -#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff +#define R0900_P2_NNOSPLHT0 0xf285 +#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff /*P2_NNOSPLH1*/ -#define R0900_P2_NNOSPLH1 0xf286 -#define F0900_P2_NOSPLH_NORMED1 0xf28600ff +#define R0900_P2_NNOSPLH1 0xf286 +#define F0900_P2_NOSPLH_NORMED1 0xf28600ff /*P2_NNOSPLH0*/ -#define R0900_P2_NNOSPLH0 0xf287 -#define F0900_P2_NOSPLH_NORMED0 0xf28700ff +#define R0900_P2_NNOSPLH0 0xf287 +#define F0900_P2_NOSPLH_NORMED0 0xf28700ff /*P2_NOSDATAT1*/ -#define R0900_P2_NOSDATAT1 0xf288 -#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff +#define R0900_P2_NOSDATAT1 0xf288 +#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff /*P2_NOSDATAT0*/ -#define R0900_P2_NOSDATAT0 0xf289 -#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff +#define R0900_P2_NOSDATAT0 0xf289 +#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff /*P2_NOSDATA1*/ -#define R0900_P2_NOSDATA1 0xf28a -#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff +#define R0900_P2_NOSDATA1 0xf28a +#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff /*P2_NOSDATA0*/ -#define R0900_P2_NOSDATA0 0xf28b -#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff +#define R0900_P2_NOSDATA0 0xf28b +#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff /*P2_NOSPLHT1*/ -#define R0900_P2_NOSPLHT1 0xf28c -#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff +#define R0900_P2_NOSPLHT1 0xf28c +#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff /*P2_NOSPLHT0*/ -#define R0900_P2_NOSPLHT0 0xf28d -#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff +#define R0900_P2_NOSPLHT0 0xf28d +#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff /*P2_NOSPLH1*/ -#define R0900_P2_NOSPLH1 0xf28e -#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff +#define R0900_P2_NOSPLH1 0xf28e +#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff /*P2_NOSPLH0*/ -#define R0900_P2_NOSPLH0 0xf28f -#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff +#define R0900_P2_NOSPLH0 0xf28f +#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff /*P2_CAR2CFG*/ -#define R0900_P2_CAR2CFG 0xf290 -#define F0900_P2_DESCRAMB_OFF 0xf2900080 -#define F0900_P2_PN4_SELECT 0xf2900040 -#define F0900_P2_CFR2_STOPDVBS1 0xf2900020 -#define F0900_P2_STOP_CFR2UPDATE 0xf2900010 -#define F0900_P2_STOP_NCO2UPDATE 0xf2900008 -#define F0900_P2_ROTA2ON 0xf2900004 -#define F0900_P2_PH_DET_ALGO2 0xf2900003 - -/*P2_ACLC2*/ -#define R0900_P2_ACLC2 0xf291 -#define F0900_P2_CAR2_PUNCT_ADERAT 0xf2910040 -#define F0900_P2_CAR2_ALPHA_MANT 0xf2910030 -#define F0900_P2_CAR2_ALPHA_EXP 0xf291000f - -/*P2_BCLC2*/ -#define R0900_P2_BCLC2 0xf292 -#define F0900_P2_DVBS2_NIP 0xf2920080 -#define F0900_P2_CAR2_PUNCT_BDERAT 0xf2920040 -#define F0900_P2_CAR2_BETA_MANT 0xf2920030 -#define F0900_P2_CAR2_BETA_EXP 0xf292000f +#define R0900_P2_CAR2CFG 0xf290 +#define F0900_P2_CARRIER3_DISABLE 0xf2900040 +#define F0900_P2_ROTA2ON 0xf2900004 +#define F0900_P2_PH_DET_ALGO2 0xf2900003 + +/*P2_CFR2CFR1*/ +#define R0900_P2_CFR2CFR1 0xf291 +#define F0900_P2_CFR2TOCFR1_DVBS1 0xf29100c0 +#define F0900_P2_EN_S2CAR2CENTER 0xf2910020 +#define F0900_P2_DIS_BCHERRCFR2 0xf2910010 +#define F0900_P2_CFR2TOCFR1_BETA 0xf2910007 /*P2_CFR22*/ -#define R0900_P2_CFR22 0xf293 -#define F0900_P2_CAR2_FREQ2 0xf29301ff +#define R0900_P2_CFR22 0xf293 +#define F0900_P2_CAR2_FREQ2 0xf29301ff /*P2_CFR21*/ -#define R0900_P2_CFR21 0xf294 -#define F0900_P2_CAR2_FREQ1 0xf29400ff +#define R0900_P2_CFR21 0xf294 +#define F0900_P2_CAR2_FREQ1 0xf29400ff /*P2_CFR20*/ -#define R0900_P2_CFR20 0xf295 -#define F0900_P2_CAR2_FREQ0 0xf29500ff +#define R0900_P2_CFR20 0xf295 +#define F0900_P2_CAR2_FREQ0 0xf29500ff /*P2_ACLC2S2Q*/ -#define R0900_P2_ACLC2S2Q 0xf297 -#define F0900_P2_ENAB_SPSKSYMB 0xf2970080 -#define F0900_P2_CAR2S2_QADERAT 0xf2970040 -#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030 -#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f +#define R0900_P2_ACLC2S2Q 0xf297 +#define F0900_P2_ENAB_SPSKSYMB 0xf2970080 +#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030 +#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f /*P2_ACLC2S28*/ -#define R0900_P2_ACLC2S28 0xf298 -#define F0900_P2_OLDI3Q_MODE 0xf2980080 -#define F0900_P2_CAR2S2_8ADERAT 0xf2980040 -#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030 -#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f +#define R0900_P2_ACLC2S28 0xf298 +#define F0900_P2_OLDI3Q_MODE 0xf2980080 +#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030 +#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f /*P2_ACLC2S216A*/ -#define R0900_P2_ACLC2S216A 0xf299 -#define F0900_P2_CAR2S2_16ADERAT 0xf2990040 -#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030 -#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f +#define R0900_P2_ACLC2S216A 0xf299 +#define F0900_P2_DIS_C3STOPA2 0xf2990080 +#define F0900_P2_CAR2S2_16ADERAT 0xf2990040 +#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030 +#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f /*P2_ACLC2S232A*/ -#define R0900_P2_ACLC2S232A 0xf29a -#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040 -#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030 -#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f +#define R0900_P2_ACLC2S232A 0xf29a +#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040 +#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030 +#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f /*P2_BCLC2S2Q*/ -#define R0900_P2_BCLC2S2Q 0xf29c -#define F0900_P2_DVBS2S2Q_NIP 0xf29c0080 -#define F0900_P2_CAR2S2_QBDERAT 0xf29c0040 -#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030 -#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f +#define R0900_P2_BCLC2S2Q 0xf29c +#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030 +#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f /*P2_BCLC2S28*/ -#define R0900_P2_BCLC2S28 0xf29d -#define F0900_P2_DVBS2S28_NIP 0xf29d0080 -#define F0900_P2_CAR2S2_8BDERAT 0xf29d0040 -#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030 -#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f +#define R0900_P2_BCLC2S28 0xf29d +#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030 +#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f /*P2_BCLC2S216A*/ -#define R0900_P2_BCLC2S216A 0xf29e -#define F0900_P2_DVBS2S216A_NIP 0xf29e0080 -#define F0900_P2_CAR2S2_16BDERAT 0xf29e0040 -#define F0900_P2_CAR2S2_16A_BETA_M 0xf29e0030 -#define F0900_P2_CAR2S2_16A_BETA_E 0xf29e000f +#define R0900_P2_BCLC2S216A 0xf29e /*P2_BCLC2S232A*/ -#define R0900_P2_BCLC2S232A 0xf29f -#define F0900_P2_DVBS2S232A_NIP 0xf29f0080 -#define F0900_P2_CAR2S2_32BDERAT 0xf29f0040 -#define F0900_P2_CAR2S2_32A_BETA_M 0xf29f0030 -#define F0900_P2_CAR2S2_32A_BETA_E 0xf29f000f +#define R0900_P2_BCLC2S232A 0xf29f /*P2_PLROOT2*/ -#define R0900_P2_PLROOT2 0xf2ac -#define F0900_P2_SHORTFR_DISABLE 0xf2ac0080 -#define F0900_P2_LONGFR_DISABLE 0xf2ac0040 -#define F0900_P2_DUMMYPL_DISABLE 0xf2ac0020 -#define F0900_P2_SHORTFR_AVOID 0xf2ac0010 -#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c -#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003 +#define R0900_P2_PLROOT2 0xf2ac +#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c +#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003 /*P2_PLROOT1*/ -#define R0900_P2_PLROOT1 0xf2ad -#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff +#define R0900_P2_PLROOT1 0xf2ad +#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff /*P2_PLROOT0*/ -#define R0900_P2_PLROOT0 0xf2ae -#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff +#define R0900_P2_PLROOT0 0xf2ae +#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff /*P2_MODCODLST0*/ -#define R0900_P2_MODCODLST0 0xf2b0 -#define F0900_P2_EN_TOKEN31 0xf2b00080 -#define F0900_P2_SYNCTAG_SELECT 0xf2b00040 -#define F0900_P2_MODCODRQ_MODE 0xf2b00030 +#define R0900_P2_MODCODLST0 0xf2b0 /*P2_MODCODLST1*/ -#define R0900_P2_MODCODLST1 0xf2b1 -#define F0900_P2_DIS_MODCOD29 0xf2b100f0 -#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f +#define R0900_P2_MODCODLST1 0xf2b1 +#define F0900_P2_DIS_MODCOD29 0xf2b100f0 +#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f /*P2_MODCODLST2*/ -#define R0900_P2_MODCODLST2 0xf2b2 -#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0 -#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f +#define R0900_P2_MODCODLST2 0xf2b2 +#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0 +#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f /*P2_MODCODLST3*/ -#define R0900_P2_MODCODLST3 0xf2b3 -#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0 -#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f +#define R0900_P2_MODCODLST3 0xf2b3 +#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0 +#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f /*P2_MODCODLST4*/ -#define R0900_P2_MODCODLST4 0xf2b4 -#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0 -#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f +#define R0900_P2_MODCODLST4 0xf2b4 +#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0 +#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f /*P2_MODCODLST5*/ -#define R0900_P2_MODCODLST5 0xf2b5 -#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0 -#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f +#define R0900_P2_MODCODLST5 0xf2b5 +#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0 +#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f /*P2_MODCODLST6*/ -#define R0900_P2_MODCODLST6 0xf2b6 -#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0 -#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f +#define R0900_P2_MODCODLST6 0xf2b6 +#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0 +#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f /*P2_MODCODLST7*/ -#define R0900_P2_MODCODLST7 0xf2b7 -#define F0900_P2_DIS_8P_9_10 0xf2b700f0 -#define F0900_P2_DIS_8P_8_9 0xf2b7000f +#define R0900_P2_MODCODLST7 0xf2b7 +#define F0900_P2_DIS_8P_9_10 0xf2b700f0 +#define F0900_P2_DIS_8P_8_9 0xf2b7000f /*P2_MODCODLST8*/ -#define R0900_P2_MODCODLST8 0xf2b8 -#define F0900_P2_DIS_8P_5_6 0xf2b800f0 -#define F0900_P2_DIS_8P_3_4 0xf2b8000f +#define R0900_P2_MODCODLST8 0xf2b8 +#define F0900_P2_DIS_8P_5_6 0xf2b800f0 +#define F0900_P2_DIS_8P_3_4 0xf2b8000f /*P2_MODCODLST9*/ -#define R0900_P2_MODCODLST9 0xf2b9 -#define F0900_P2_DIS_8P_2_3 0xf2b900f0 -#define F0900_P2_DIS_8P_3_5 0xf2b9000f +#define R0900_P2_MODCODLST9 0xf2b9 +#define F0900_P2_DIS_8P_2_3 0xf2b900f0 +#define F0900_P2_DIS_8P_3_5 0xf2b9000f /*P2_MODCODLSTA*/ -#define R0900_P2_MODCODLSTA 0xf2ba -#define F0900_P2_DIS_QP_9_10 0xf2ba00f0 -#define F0900_P2_DIS_QP_8_9 0xf2ba000f +#define R0900_P2_MODCODLSTA 0xf2ba +#define F0900_P2_DIS_QP_9_10 0xf2ba00f0 +#define F0900_P2_DIS_QP_8_9 0xf2ba000f /*P2_MODCODLSTB*/ -#define R0900_P2_MODCODLSTB 0xf2bb -#define F0900_P2_DIS_QP_5_6 0xf2bb00f0 -#define F0900_P2_DIS_QP_4_5 0xf2bb000f +#define R0900_P2_MODCODLSTB 0xf2bb +#define F0900_P2_DIS_QP_5_6 0xf2bb00f0 +#define F0900_P2_DIS_QP_4_5 0xf2bb000f /*P2_MODCODLSTC*/ -#define R0900_P2_MODCODLSTC 0xf2bc -#define F0900_P2_DIS_QP_3_4 0xf2bc00f0 -#define F0900_P2_DIS_QP_2_3 0xf2bc000f +#define R0900_P2_MODCODLSTC 0xf2bc +#define F0900_P2_DIS_QP_3_4 0xf2bc00f0 +#define F0900_P2_DIS_QP_2_3 0xf2bc000f /*P2_MODCODLSTD*/ -#define R0900_P2_MODCODLSTD 0xf2bd -#define F0900_P2_DIS_QP_3_5 0xf2bd00f0 -#define F0900_P2_DIS_QP_1_2 0xf2bd000f +#define R0900_P2_MODCODLSTD 0xf2bd +#define F0900_P2_DIS_QP_3_5 0xf2bd00f0 +#define F0900_P2_DIS_QP_1_2 0xf2bd000f /*P2_MODCODLSTE*/ -#define R0900_P2_MODCODLSTE 0xf2be -#define F0900_P2_DIS_QP_2_5 0xf2be00f0 -#define F0900_P2_DIS_QP_1_3 0xf2be000f +#define R0900_P2_MODCODLSTE 0xf2be +#define F0900_P2_DIS_QP_2_5 0xf2be00f0 +#define F0900_P2_DIS_QP_1_3 0xf2be000f /*P2_MODCODLSTF*/ -#define R0900_P2_MODCODLSTF 0xf2bf -#define F0900_P2_DIS_QP_1_4 0xf2bf00f0 -#define F0900_P2_DDEMOD_SET 0xf2bf0002 -#define F0900_P2_DDEMOD_MASK 0xf2bf0001 +#define R0900_P2_MODCODLSTF 0xf2bf +#define F0900_P2_DIS_QP_1_4 0xf2bf00f0 + +/*P2_GAUSSR0*/ +#define R0900_P2_GAUSSR0 0xf2c0 +#define F0900_P2_EN_CCIMODE 0xf2c00080 +#define F0900_P2_R0_GAUSSIEN 0xf2c0007f + +/*P2_CCIR0*/ +#define R0900_P2_CCIR0 0xf2c1 +#define F0900_P2_CCIDETECT_PLHONLY 0xf2c10080 +#define F0900_P2_R0_CCI 0xf2c1007f + +/*P2_CCIQUANT*/ +#define R0900_P2_CCIQUANT 0xf2c2 +#define F0900_P2_CCI_BETA 0xf2c200e0 +#define F0900_P2_CCI_QUANT 0xf2c2001f + +/*P2_CCITHRES*/ +#define R0900_P2_CCITHRES 0xf2c3 +#define F0900_P2_CCI_THRESHOLD 0xf2c300ff + +/*P2_CCIACC*/ +#define R0900_P2_CCIACC 0xf2c4 +#define F0900_P2_CCI_VALUE 0xf2c400ff /*P2_DMDRESCFG*/ -#define R0900_P2_DMDRESCFG 0xf2c6 -#define F0900_P2_DMDRES_RESET 0xf2c60080 -#define F0900_P2_DMDRES_NOISESQR 0xf2c60010 -#define F0900_P2_DMDRES_STRALL 0xf2c60008 -#define F0900_P2_DMDRES_NEWONLY 0xf2c60004 -#define F0900_P2_DMDRES_NOSTORE 0xf2c60002 -#define F0900_P2_DMDRES_AGC2MEM 0xf2c60001 +#define R0900_P2_DMDRESCFG 0xf2c6 +#define F0900_P2_DMDRES_RESET 0xf2c60080 +#define F0900_P2_DMDRES_STRALL 0xf2c60008 +#define F0900_P2_DMDRES_NEWONLY 0xf2c60004 +#define F0900_P2_DMDRES_NOSTORE 0xf2c60002 /*P2_DMDRESADR*/ -#define R0900_P2_DMDRESADR 0xf2c7 -#define F0900_P2_SUSP_PREDCANAL 0xf2c70080 -#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040 -#define F0900_P2_DMDRES_MEMFULL 0xf2c70030 -#define F0900_P2_DMDRES_RESNBR 0xf2c7000f +#define R0900_P2_DMDRESADR 0xf2c7 +#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040 +#define F0900_P2_DMDRES_MEMFULL 0xf2c70030 +#define F0900_P2_DMDRES_RESNBR 0xf2c7000f /*P2_DMDRESDATA7*/ -#define R0900_P2_DMDRESDATA7 0xf2c8 -#define F0900_P2_DMDRES_DATA7 0xf2c800ff +#define R0900_P2_DMDRESDATA7 0xf2c8 +#define F0900_P2_DMDRES_DATA7 0xf2c800ff /*P2_DMDRESDATA6*/ -#define R0900_P2_DMDRESDATA6 0xf2c9 -#define F0900_P2_DMDRES_DATA6 0xf2c900ff +#define R0900_P2_DMDRESDATA6 0xf2c9 +#define F0900_P2_DMDRES_DATA6 0xf2c900ff /*P2_DMDRESDATA5*/ -#define R0900_P2_DMDRESDATA5 0xf2ca -#define F0900_P2_DMDRES_DATA5 0xf2ca00ff +#define R0900_P2_DMDRESDATA5 0xf2ca +#define F0900_P2_DMDRES_DATA5 0xf2ca00ff /*P2_DMDRESDATA4*/ -#define R0900_P2_DMDRESDATA4 0xf2cb -#define F0900_P2_DMDRES_DATA4 0xf2cb00ff +#define R0900_P2_DMDRESDATA4 0xf2cb +#define F0900_P2_DMDRES_DATA4 0xf2cb00ff /*P2_DMDRESDATA3*/ -#define R0900_P2_DMDRESDATA3 0xf2cc -#define F0900_P2_DMDRES_DATA3 0xf2cc00ff +#define R0900_P2_DMDRESDATA3 0xf2cc +#define F0900_P2_DMDRES_DATA3 0xf2cc00ff /*P2_DMDRESDATA2*/ -#define R0900_P2_DMDRESDATA2 0xf2cd -#define F0900_P2_DMDRES_DATA2 0xf2cd00ff +#define R0900_P2_DMDRESDATA2 0xf2cd +#define F0900_P2_DMDRES_DATA2 0xf2cd00ff /*P2_DMDRESDATA1*/ -#define R0900_P2_DMDRESDATA1 0xf2ce -#define F0900_P2_DMDRES_DATA1 0xf2ce00ff +#define R0900_P2_DMDRESDATA1 0xf2ce +#define F0900_P2_DMDRES_DATA1 0xf2ce00ff /*P2_DMDRESDATA0*/ -#define R0900_P2_DMDRESDATA0 0xf2cf -#define F0900_P2_DMDRES_DATA0 0xf2cf00ff +#define R0900_P2_DMDRESDATA0 0xf2cf +#define F0900_P2_DMDRES_DATA0 0xf2cf00ff /*P2_FFEI1*/ -#define R0900_P2_FFEI1 0xf2d0 -#define F0900_P2_FFE_ACCI1 0xf2d001ff +#define R0900_P2_FFEI1 0xf2d0 +#define F0900_P2_FFE_ACCI1 0xf2d001ff /*P2_FFEQ1*/ -#define R0900_P2_FFEQ1 0xf2d1 -#define F0900_P2_FFE_ACCQ1 0xf2d101ff +#define R0900_P2_FFEQ1 0xf2d1 +#define F0900_P2_FFE_ACCQ1 0xf2d101ff /*P2_FFEI2*/ -#define R0900_P2_FFEI2 0xf2d2 -#define F0900_P2_FFE_ACCI2 0xf2d201ff +#define R0900_P2_FFEI2 0xf2d2 +#define F0900_P2_FFE_ACCI2 0xf2d201ff /*P2_FFEQ2*/ -#define R0900_P2_FFEQ2 0xf2d3 -#define F0900_P2_FFE_ACCQ2 0xf2d301ff +#define R0900_P2_FFEQ2 0xf2d3 +#define F0900_P2_FFE_ACCQ2 0xf2d301ff /*P2_FFEI3*/ -#define R0900_P2_FFEI3 0xf2d4 -#define F0900_P2_FFE_ACCI3 0xf2d401ff +#define R0900_P2_FFEI3 0xf2d4 +#define F0900_P2_FFE_ACCI3 0xf2d401ff /*P2_FFEQ3*/ -#define R0900_P2_FFEQ3 0xf2d5 -#define F0900_P2_FFE_ACCQ3 0xf2d501ff +#define R0900_P2_FFEQ3 0xf2d5 +#define F0900_P2_FFE_ACCQ3 0xf2d501ff /*P2_FFEI4*/ -#define R0900_P2_FFEI4 0xf2d6 -#define F0900_P2_FFE_ACCI4 0xf2d601ff +#define R0900_P2_FFEI4 0xf2d6 +#define F0900_P2_FFE_ACCI4 0xf2d601ff /*P2_FFEQ4*/ -#define R0900_P2_FFEQ4 0xf2d7 -#define F0900_P2_FFE_ACCQ4 0xf2d701ff +#define R0900_P2_FFEQ4 0xf2d7 +#define F0900_P2_FFE_ACCQ4 0xf2d701ff /*P2_FFECFG*/ -#define R0900_P2_FFECFG 0xf2d8 -#define F0900_P2_EQUALFFE_ON 0xf2d80040 -#define F0900_P2_EQUAL_USEDSYMB 0xf2d80030 -#define F0900_P2_MU_EQUALFFE 0xf2d80007 +#define R0900_P2_FFECFG 0xf2d8 +#define F0900_P2_EQUALFFE_ON 0xf2d80040 +#define F0900_P2_MU_EQUALFFE 0xf2d80007 /*P2_TNRCFG*/ -#define R0900_P2_TNRCFG 0xf2e0 -#define F0900_P2_TUN_ACKFAIL 0xf2e00080 -#define F0900_P2_TUN_TYPE 0xf2e00070 -#define F0900_P2_TUN_SECSTOP 0xf2e00008 -#define F0900_P2_TUN_VCOSRCH 0xf2e00004 -#define F0900_P2_TUN_MADDRESS 0xf2e00003 +#define R0900_P2_TNRCFG 0xf2e0 +#define F0900_P2_TUN_ACKFAIL 0xf2e00080 +#define F0900_P2_TUN_TYPE 0xf2e00070 +#define F0900_P2_TUN_SECSTOP 0xf2e00008 +#define F0900_P2_TUN_VCOSRCH 0xf2e00004 +#define F0900_P2_TUN_MADDRESS 0xf2e00003 /*P2_TNRCFG2*/ -#define R0900_P2_TNRCFG2 0xf2e1 -#define F0900_P2_TUN_IQSWAP 0xf2e10080 -#define F0900_P2_STB6110_STEP2MHZ 0xf2e10040 -#define F0900_P2_STB6120_DBLI2C 0xf2e10020 -#define F0900_P2_DIS_FCCK 0xf2e10010 -#define F0900_P2_DIS_LPEN 0xf2e10008 -#define F0900_P2_DIS_BWCALC 0xf2e10004 -#define F0900_P2_SHORT_WAITSTATES 0xf2e10002 -#define F0900_P2_DIS_2BWAGC1 0xf2e10001 +#define R0900_P2_TNRCFG2 0xf2e1 +#define F0900_P2_TUN_IQSWAP 0xf2e10080 +#define F0900_P2_DIS_BWCALC 0xf2e10004 +#define F0900_P2_SHORT_WAITSTATES 0xf2e10002 /*P2_TNRXTAL*/ -#define R0900_P2_TNRXTAL 0xf2e4 -#define F0900_P2_TUN_MCLKDECIMAL 0xf2e400e0 -#define F0900_P2_TUN_XTALFREQ 0xf2e4001f +#define R0900_P2_TNRXTAL 0xf2e4 +#define F0900_P2_TUN_XTALFREQ 0xf2e4001f /*P2_TNRSTEPS*/ -#define R0900_P2_TNRSTEPS 0xf2e7 -#define F0900_P2_TUNER_BW1P6 0xf2e70080 -#define F0900_P2_BWINC_OFFSET 0xf2e70070 -#define F0900_P2_SOFTSTEP_RNG 0xf2e70008 -#define F0900_P2_TUN_BWOFFSET 0xf2e70107 +#define R0900_P2_TNRSTEPS 0xf2e7 +#define F0900_P2_TUNER_BW0P125 0xf2e70080 +#define F0900_P2_BWINC_OFFSET 0xf2e70170 +#define F0900_P2_SOFTSTEP_RNG 0xf2e70008 +#define F0900_P2_TUN_BWOFFSET 0xf2e70007 /*P2_TNRGAIN*/ -#define R0900_P2_TNRGAIN 0xf2e8 -#define F0900_P2_TUN_KDIVEN 0xf2e800c0 -#define F0900_P2_STB6X00_OCK 0xf2e80030 -#define F0900_P2_TUN_GAIN 0xf2e8000f +#define R0900_P2_TNRGAIN 0xf2e8 +#define F0900_P2_TUN_KDIVEN 0xf2e800c0 +#define F0900_P2_STB6X00_OCK 0xf2e80030 +#define F0900_P2_TUN_GAIN 0xf2e8000f /*P2_TNRRF1*/ -#define R0900_P2_TNRRF1 0xf2e9 -#define F0900_P2_TUN_RFFREQ2 0xf2e900ff +#define R0900_P2_TNRRF1 0xf2e9 +#define F0900_P2_TUN_RFFREQ2 0xf2e900ff /*P2_TNRRF0*/ -#define R0900_P2_TNRRF0 0xf2ea -#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff +#define R0900_P2_TNRRF0 0xf2ea +#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff /*P2_TNRBW*/ -#define R0900_P2_TNRBW 0xf2eb -#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0 -#define F0900_P2_TUN_BW 0xf2eb003f +#define R0900_P2_TNRBW 0xf2eb +#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0 +#define F0900_P2_TUN_BW 0xf2eb003f /*P2_TNRADJ*/ -#define R0900_P2_TNRADJ 0xf2ec -#define F0900_P2_STB61X0_RCLK 0xf2ec0080 -#define F0900_P2_STB61X0_CALTIME 0xf2ec0040 -#define F0900_P2_STB6X00_DLB 0xf2ec0038 -#define F0900_P2_STB6000_FCL 0xf2ec0007 +#define R0900_P2_TNRADJ 0xf2ec +#define F0900_P2_STB61X0_CALTIME 0xf2ec0040 /*P2_TNRCTL2*/ -#define R0900_P2_TNRCTL2 0xf2ed -#define F0900_P2_STB61X0_LCP1_RCCKOFF 0xf2ed0080 -#define F0900_P2_STB61X0_LCP0 0xf2ed0040 -#define F0900_P2_STB61X0_XTOUT_RFOUTS 0xf2ed0020 -#define F0900_P2_STB61X0_XTON_MCKDV 0xf2ed0010 -#define F0900_P2_STB61X0_CALOFF_DCOFF 0xf2ed0008 -#define F0900_P2_STB6110_LPT 0xf2ed0004 -#define F0900_P2_STB6110_RX 0xf2ed0002 -#define F0900_P2_STB6110_SYN 0xf2ed0001 +#define R0900_P2_TNRCTL2 0xf2ed +#define F0900_P2_STB61X0_RCCKOFF 0xf2ed0080 +#define F0900_P2_STB61X0_ICP_SDOFF 0xf2ed0040 +#define F0900_P2_STB61X0_DCLOOPOFF 0xf2ed0020 +#define F0900_P2_STB61X0_REFOUTSEL 0xf2ed0010 +#define F0900_P2_STB61X0_CALOFF 0xf2ed0008 +#define F0900_P2_STB6XX0_LPT_BEN 0xf2ed0004 +#define F0900_P2_STB6XX0_RX_OSCP 0xf2ed0002 +#define F0900_P2_STB6XX0_SYN 0xf2ed0001 /*P2_TNRCFG3*/ -#define R0900_P2_TNRCFG3 0xf2ee -#define F0900_P2_STB6120_DISCTRL1 0xf2ee0080 -#define F0900_P2_STB6120_INVORDER 0xf2ee0040 -#define F0900_P2_STB6120_ENCTRL6 0xf2ee0020 -#define F0900_P2_TUN_PLLFREQ 0xf2ee001c -#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003 +#define R0900_P2_TNRCFG3 0xf2ee +#define F0900_P2_TUN_PLLFREQ 0xf2ee001c +#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003 /*P2_TNRLAUNCH*/ -#define R0900_P2_TNRLAUNCH 0xf2f0 +#define R0900_P2_TNRLAUNCH 0xf2f0 /*P2_TNRLD*/ -#define R0900_P2_TNRLD 0xf2f0 -#define F0900_P2_TUNLD_VCOING 0xf2f00080 -#define F0900_P2_TUN_REG1FAIL 0xf2f00040 -#define F0900_P2_TUN_REG2FAIL 0xf2f00020 -#define F0900_P2_TUN_REG3FAIL 0xf2f00010 -#define F0900_P2_TUN_REG4FAIL 0xf2f00008 -#define F0900_P2_TUN_REG5FAIL 0xf2f00004 -#define F0900_P2_TUN_BWING 0xf2f00002 -#define F0900_P2_TUN_LOCKED 0xf2f00001 +#define R0900_P2_TNRLD 0xf2f0 +#define F0900_P2_TUNLD_VCOING 0xf2f00080 +#define F0900_P2_TUN_REG1FAIL 0xf2f00040 +#define F0900_P2_TUN_REG2FAIL 0xf2f00020 +#define F0900_P2_TUN_REG3FAIL 0xf2f00010 +#define F0900_P2_TUN_REG4FAIL 0xf2f00008 +#define F0900_P2_TUN_REG5FAIL 0xf2f00004 +#define F0900_P2_TUN_BWING 0xf2f00002 +#define F0900_P2_TUN_LOCKED 0xf2f00001 /*P2_TNROBSL*/ -#define R0900_P2_TNROBSL 0xf2f6 -#define F0900_P2_TUN_I2CABORTED 0xf2f60080 -#define F0900_P2_TUN_LPEN 0xf2f60040 -#define F0900_P2_TUN_FCCK 0xf2f60020 -#define F0900_P2_TUN_I2CLOCKED 0xf2f60010 -#define F0900_P2_TUN_PROGDONE 0xf2f6000c -#define F0900_P2_TUN_RFRESTE1 0xf2f60003 +#define R0900_P2_TNROBSL 0xf2f6 +#define F0900_P2_TUN_I2CABORTED 0xf2f60080 +#define F0900_P2_TUN_LPEN 0xf2f60040 +#define F0900_P2_TUN_FCCK 0xf2f60020 +#define F0900_P2_TUN_I2CLOCKED 0xf2f60010 +#define F0900_P2_TUN_PROGDONE 0xf2f6000c +#define F0900_P2_TUN_RFRESTE1 0xf2f60003 /*P2_TNRRESTE*/ -#define R0900_P2_TNRRESTE 0xf2f7 -#define F0900_P2_TUN_RFRESTE0 0xf2f700ff +#define R0900_P2_TNRRESTE 0xf2f7 +#define F0900_P2_TUN_RFRESTE0 0xf2f700ff /*P2_SMAPCOEF7*/ -#define R0900_P2_SMAPCOEF7 0xf300 -#define F0900_P2_DIS_QSCALE 0xf3000080 -#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f +#define R0900_P2_SMAPCOEF7 0xf300 +#define F0900_P2_DIS_QSCALE 0xf3000080 +#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f /*P2_SMAPCOEF6*/ -#define R0900_P2_SMAPCOEF6 0xf301 -#define F0900_P2_DIS_NEWSCALE 0xf3010008 -#define F0900_P2_ADJ_8PSKLLR1 0xf3010004 -#define F0900_P2_OLD_8PSKLLR1 0xf3010002 -#define F0900_P2_DIS_AB8PSK 0xf3010001 +#define R0900_P2_SMAPCOEF6 0xf301 +#define F0900_P2_ADJ_8PSKLLR1 0xf3010004 +#define F0900_P2_OLD_8PSKLLR1 0xf3010002 +#define F0900_P2_DIS_AB8PSK 0xf3010001 /*P2_SMAPCOEF5*/ -#define R0900_P2_SMAPCOEF5 0xf302 -#define F0900_P2_DIS_8SCALE 0xf3020080 -#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f +#define R0900_P2_SMAPCOEF5 0xf302 +#define F0900_P2_DIS_8SCALE 0xf3020080 +#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f + +/*P2_NCO2MAX1*/ +#define R0900_P2_NCO2MAX1 0xf314 +#define F0900_P2_TETA2_MAXVABS1 0xf31400ff + +/*P2_NCO2MAX0*/ +#define R0900_P2_NCO2MAX0 0xf315 +#define F0900_P2_TETA2_MAXVABS0 0xf31500ff + +/*P2_NCO2FR1*/ +#define R0900_P2_NCO2FR1 0xf316 +#define F0900_P2_NCO2FINAL_ANGLE1 0xf31600ff + +/*P2_NCO2FR0*/ +#define R0900_P2_NCO2FR0 0xf317 +#define F0900_P2_NCO2FINAL_ANGLE0 0xf31700ff + +/*P2_CFR2AVRGE1*/ +#define R0900_P2_CFR2AVRGE1 0xf318 +#define F0900_P2_I2C_CFR2AVERAGE1 0xf31800ff + +/*P2_CFR2AVRGE0*/ +#define R0900_P2_CFR2AVRGE0 0xf319 +#define F0900_P2_I2C_CFR2AVERAGE0 0xf31900ff /*P2_DMDPLHSTAT*/ -#define R0900_P2_DMDPLHSTAT 0xf320 -#define F0900_P2_PLH_STATISTIC 0xf32000ff +#define R0900_P2_DMDPLHSTAT 0xf320 +#define F0900_P2_PLH_STATISTIC 0xf32000ff /*P2_LOCKTIME3*/ -#define R0900_P2_LOCKTIME3 0xf322 -#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff +#define R0900_P2_LOCKTIME3 0xf322 +#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff /*P2_LOCKTIME2*/ -#define R0900_P2_LOCKTIME2 0xf323 -#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff +#define R0900_P2_LOCKTIME2 0xf323 +#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff /*P2_LOCKTIME1*/ -#define R0900_P2_LOCKTIME1 0xf324 -#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff +#define R0900_P2_LOCKTIME1 0xf324 +#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff /*P2_LOCKTIME0*/ -#define R0900_P2_LOCKTIME0 0xf325 -#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff +#define R0900_P2_LOCKTIME0 0xf325 +#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff /*P2_VITSCALE*/ -#define R0900_P2_VITSCALE 0xf332 -#define F0900_P2_NVTH_NOSRANGE 0xf3320080 -#define F0900_P2_VERROR_MAXMODE 0xf3320040 -#define F0900_P2_KDIV_MODE 0xf3320030 -#define F0900_P2_NSLOWSN_LOCKED 0xf3320008 -#define F0900_P2_DELOCK_PRFLOSS 0xf3320004 -#define F0900_P2_DIS_RSFLOCK 0xf3320002 +#define R0900_P2_VITSCALE 0xf332 +#define F0900_P2_NVTH_NOSRANGE 0xf3320080 +#define F0900_P2_VERROR_MAXMODE 0xf3320040 +#define F0900_P2_NSLOWSN_LOCKED 0xf3320008 +#define F0900_P2_DIS_RSFLOCK 0xf3320002 /*P2_FECM*/ -#define R0900_P2_FECM 0xf333 -#define F0900_P2_DSS_DVB 0xf3330080 -#define F0900_P2_DEMOD_BYPASS 0xf3330040 -#define F0900_P2_CMP_SLOWMODE 0xf3330020 -#define F0900_P2_DSS_SRCH 0xf3330010 -#define F0900_P2_DIFF_MODEVIT 0xf3330004 -#define F0900_P2_SYNCVIT 0xf3330002 -#define F0900_P2_IQINV 0xf3330001 +#define R0900_P2_FECM 0xf333 +#define F0900_P2_DSS_DVB 0xf3330080 +#define F0900_P2_DSS_SRCH 0xf3330010 +#define F0900_P2_SYNCVIT 0xf3330002 +#define F0900_P2_IQINV 0xf3330001 /*P2_VTH12*/ -#define R0900_P2_VTH12 0xf334 -#define F0900_P2_VTH12 0xf33400ff +#define R0900_P2_VTH12 0xf334 +#define F0900_P2_VTH12 0xf33400ff /*P2_VTH23*/ -#define R0900_P2_VTH23 0xf335 -#define F0900_P2_VTH23 0xf33500ff +#define R0900_P2_VTH23 0xf335 +#define F0900_P2_VTH23 0xf33500ff /*P2_VTH34*/ -#define R0900_P2_VTH34 0xf336 -#define F0900_P2_VTH34 0xf33600ff +#define R0900_P2_VTH34 0xf336 +#define F0900_P2_VTH34 0xf33600ff /*P2_VTH56*/ -#define R0900_P2_VTH56 0xf337 -#define F0900_P2_VTH56 0xf33700ff +#define R0900_P2_VTH56 0xf337 +#define F0900_P2_VTH56 0xf33700ff /*P2_VTH67*/ -#define R0900_P2_VTH67 0xf338 -#define F0900_P2_VTH67 0xf33800ff +#define R0900_P2_VTH67 0xf338 +#define F0900_P2_VTH67 0xf33800ff /*P2_VTH78*/ -#define R0900_P2_VTH78 0xf339 -#define F0900_P2_VTH78 0xf33900ff +#define R0900_P2_VTH78 0xf339 +#define F0900_P2_VTH78 0xf33900ff /*P2_VITCURPUN*/ -#define R0900_P2_VITCURPUN 0xf33a -#define F0900_P2_VIT_MAPPING 0xf33a00e0 -#define F0900_P2_VIT_CURPUN 0xf33a001f +#define R0900_P2_VITCURPUN 0xf33a +#define F0900_P2_VIT_CURPUN 0xf33a001f /*P2_VERROR*/ -#define R0900_P2_VERROR 0xf33b -#define F0900_P2_REGERR_VIT 0xf33b00ff +#define R0900_P2_VERROR 0xf33b +#define F0900_P2_REGERR_VIT 0xf33b00ff /*P2_PRVIT*/ -#define R0900_P2_PRVIT 0xf33c -#define F0900_P2_DIS_VTHLOCK 0xf33c0040 -#define F0900_P2_E7_8VIT 0xf33c0020 -#define F0900_P2_E6_7VIT 0xf33c0010 -#define F0900_P2_E5_6VIT 0xf33c0008 -#define F0900_P2_E3_4VIT 0xf33c0004 -#define F0900_P2_E2_3VIT 0xf33c0002 -#define F0900_P2_E1_2VIT 0xf33c0001 +#define R0900_P2_PRVIT 0xf33c +#define F0900_P2_DIS_VTHLOCK 0xf33c0040 +#define F0900_P2_E7_8VIT 0xf33c0020 +#define F0900_P2_E6_7VIT 0xf33c0010 +#define F0900_P2_E5_6VIT 0xf33c0008 +#define F0900_P2_E3_4VIT 0xf33c0004 +#define F0900_P2_E2_3VIT 0xf33c0002 +#define F0900_P2_E1_2VIT 0xf33c0001 /*P2_VAVSRVIT*/ -#define R0900_P2_VAVSRVIT 0xf33d -#define F0900_P2_AMVIT 0xf33d0080 -#define F0900_P2_FROZENVIT 0xf33d0040 -#define F0900_P2_SNVIT 0xf33d0030 -#define F0900_P2_TOVVIT 0xf33d000c -#define F0900_P2_HYPVIT 0xf33d0003 +#define R0900_P2_VAVSRVIT 0xf33d +#define F0900_P2_AMVIT 0xf33d0080 +#define F0900_P2_FROZENVIT 0xf33d0040 +#define F0900_P2_SNVIT 0xf33d0030 +#define F0900_P2_TOVVIT 0xf33d000c +#define F0900_P2_HYPVIT 0xf33d0003 /*P2_VSTATUSVIT*/ -#define R0900_P2_VSTATUSVIT 0xf33e -#define F0900_P2_VITERBI_ON 0xf33e0080 -#define F0900_P2_END_LOOPVIT 0xf33e0040 -#define F0900_P2_VITERBI_DEPRF 0xf33e0020 -#define F0900_P2_PRFVIT 0xf33e0010 -#define F0900_P2_LOCKEDVIT 0xf33e0008 -#define F0900_P2_VITERBI_DELOCK 0xf33e0004 -#define F0900_P2_VIT_DEMODSEL 0xf33e0002 -#define F0900_P2_VITERBI_COMPOUT 0xf33e0001 +#define R0900_P2_VSTATUSVIT 0xf33e +#define F0900_P2_PRFVIT 0xf33e0010 +#define F0900_P2_LOCKEDVIT 0xf33e0008 /*P2_VTHINUSE*/ -#define R0900_P2_VTHINUSE 0xf33f -#define F0900_P2_VIT_INUSE 0xf33f00ff +#define R0900_P2_VTHINUSE 0xf33f +#define F0900_P2_VIT_INUSE 0xf33f00ff /*P2_KDIV12*/ -#define R0900_P2_KDIV12 0xf340 -#define F0900_P2_KDIV12_MANUAL 0xf3400080 -#define F0900_P2_K_DIVIDER_12 0xf340007f +#define R0900_P2_KDIV12 0xf340 +#define F0900_P2_K_DIVIDER_12 0xf340007f /*P2_KDIV23*/ -#define R0900_P2_KDIV23 0xf341 -#define F0900_P2_KDIV23_MANUAL 0xf3410080 -#define F0900_P2_K_DIVIDER_23 0xf341007f +#define R0900_P2_KDIV23 0xf341 +#define F0900_P2_K_DIVIDER_23 0xf341007f /*P2_KDIV34*/ -#define R0900_P2_KDIV34 0xf342 -#define F0900_P2_KDIV34_MANUAL 0xf3420080 -#define F0900_P2_K_DIVIDER_34 0xf342007f +#define R0900_P2_KDIV34 0xf342 +#define F0900_P2_K_DIVIDER_34 0xf342007f /*P2_KDIV56*/ -#define R0900_P2_KDIV56 0xf343 -#define F0900_P2_KDIV56_MANUAL 0xf3430080 -#define F0900_P2_K_DIVIDER_56 0xf343007f +#define R0900_P2_KDIV56 0xf343 +#define F0900_P2_K_DIVIDER_56 0xf343007f /*P2_KDIV67*/ -#define R0900_P2_KDIV67 0xf344 -#define F0900_P2_KDIV67_MANUAL 0xf3440080 -#define F0900_P2_K_DIVIDER_67 0xf344007f +#define R0900_P2_KDIV67 0xf344 +#define F0900_P2_K_DIVIDER_67 0xf344007f /*P2_KDIV78*/ -#define R0900_P2_KDIV78 0xf345 -#define F0900_P2_KDIV78_MANUAL 0xf3450080 -#define F0900_P2_K_DIVIDER_78 0xf345007f +#define R0900_P2_KDIV78 0xf345 +#define F0900_P2_K_DIVIDER_78 0xf345007f /*P2_PDELCTRL1*/ -#define R0900_P2_PDELCTRL1 0xf350 -#define F0900_P2_INV_MISMASK 0xf3500080 -#define F0900_P2_FORCE_ACCEPTED 0xf3500040 -#define F0900_P2_FILTER_EN 0xf3500020 -#define F0900_P2_FORCE_PKTDELINUSE 0xf3500010 -#define F0900_P2_HYSTEN 0xf3500008 -#define F0900_P2_HYSTSWRST 0xf3500004 -#define F0900_P2_EN_MIS00 0xf3500002 -#define F0900_P2_ALGOSWRST 0xf3500001 +#define R0900_P2_PDELCTRL1 0xf350 +#define F0900_P2_INV_MISMASK 0xf3500080 +#define F0900_P2_FILTER_EN 0xf3500020 +#define F0900_P2_EN_MIS00 0xf3500002 +#define F0900_P2_ALGOSWRST 0xf3500001 /*P2_PDELCTRL2*/ -#define R0900_P2_PDELCTRL2 0xf351 -#define F0900_P2_FORCE_CONTINUOUS 0xf3510080 -#define F0900_P2_RESET_UPKO_COUNT 0xf3510040 -#define F0900_P2_USER_PKTDELIN_NB 0xf3510020 -#define F0900_P2_FORCE_LOCKED 0xf3510010 -#define F0900_P2_DATA_UNBBSCRAM 0xf3510008 -#define F0900_P2_FORCE_LONGPKT 0xf3510004 -#define F0900_P2_FRAME_MODE 0xf3510002 +#define R0900_P2_PDELCTRL2 0xf351 +#define F0900_P2_RESET_UPKO_COUNT 0xf3510040 +#define F0900_P2_FRAME_MODE 0xf3510002 +#define F0900_P2_NOBCHERRFLG_USE 0xf3510001 /*P2_HYSTTHRESH*/ -#define R0900_P2_HYSTTHRESH 0xf354 -#define F0900_P2_UNLCK_THRESH 0xf35400f0 -#define F0900_P2_DELIN_LCK_THRESH 0xf354000f +#define R0900_P2_HYSTTHRESH 0xf354 +#define F0900_P2_UNLCK_THRESH 0xf35400f0 +#define F0900_P2_DELIN_LCK_THRESH 0xf354000f /*P2_ISIENTRY*/ -#define R0900_P2_ISIENTRY 0xf35e -#define F0900_P2_ISI_ENTRY 0xf35e00ff +#define R0900_P2_ISIENTRY 0xf35e +#define F0900_P2_ISI_ENTRY 0xf35e00ff /*P2_ISIBITENA*/ -#define R0900_P2_ISIBITENA 0xf35f -#define F0900_P2_ISI_BIT_EN 0xf35f00ff +#define R0900_P2_ISIBITENA 0xf35f +#define F0900_P2_ISI_BIT_EN 0xf35f00ff /*P2_MATSTR1*/ -#define R0900_P2_MATSTR1 0xf360 -#define F0900_P2_MATYPE_CURRENT1 0xf36000ff +#define R0900_P2_MATSTR1 0xf360 +#define F0900_P2_MATYPE_CURRENT1 0xf36000ff /*P2_MATSTR0*/ -#define R0900_P2_MATSTR0 0xf361 -#define F0900_P2_MATYPE_CURRENT0 0xf36100ff +#define R0900_P2_MATSTR0 0xf361 +#define F0900_P2_MATYPE_CURRENT0 0xf36100ff /*P2_UPLSTR1*/ -#define R0900_P2_UPLSTR1 0xf362 -#define F0900_P2_UPL_CURRENT1 0xf36200ff +#define R0900_P2_UPLSTR1 0xf362 +#define F0900_P2_UPL_CURRENT1 0xf36200ff /*P2_UPLSTR0*/ -#define R0900_P2_UPLSTR0 0xf363 -#define F0900_P2_UPL_CURRENT0 0xf36300ff +#define R0900_P2_UPLSTR0 0xf363 +#define F0900_P2_UPL_CURRENT0 0xf36300ff /*P2_DFLSTR1*/ -#define R0900_P2_DFLSTR1 0xf364 -#define F0900_P2_DFL_CURRENT1 0xf36400ff +#define R0900_P2_DFLSTR1 0xf364 +#define F0900_P2_DFL_CURRENT1 0xf36400ff /*P2_DFLSTR0*/ -#define R0900_P2_DFLSTR0 0xf365 -#define F0900_P2_DFL_CURRENT0 0xf36500ff +#define R0900_P2_DFLSTR0 0xf365 +#define F0900_P2_DFL_CURRENT0 0xf36500ff /*P2_SYNCSTR*/ -#define R0900_P2_SYNCSTR 0xf366 -#define F0900_P2_SYNC_CURRENT 0xf36600ff +#define R0900_P2_SYNCSTR 0xf366 +#define F0900_P2_SYNC_CURRENT 0xf36600ff /*P2_SYNCDSTR1*/ -#define R0900_P2_SYNCDSTR1 0xf367 -#define F0900_P2_SYNCD_CURRENT1 0xf36700ff +#define R0900_P2_SYNCDSTR1 0xf367 +#define F0900_P2_SYNCD_CURRENT1 0xf36700ff /*P2_SYNCDSTR0*/ -#define R0900_P2_SYNCDSTR0 0xf368 -#define F0900_P2_SYNCD_CURRENT0 0xf36800ff +#define R0900_P2_SYNCDSTR0 0xf368 +#define F0900_P2_SYNCD_CURRENT0 0xf36800ff /*P2_PDELSTATUS1*/ -#define R0900_P2_PDELSTATUS1 0xf369 -#define F0900_P2_PKTDELIN_DELOCK 0xf3690080 -#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040 -#define F0900_P2_CONTINUOUS_STREAM 0xf3690020 -#define F0900_P2_UNACCEPTED_STREAM 0xf3690010 -#define F0900_P2_BCH_ERROR_FLAG 0xf3690008 -#define F0900_P2_BBHCRCKO 0xf3690004 -#define F0900_P2_PKTDELIN_LOCK 0xf3690002 -#define F0900_P2_FIRST_LOCK 0xf3690001 +#define R0900_P2_PDELSTATUS1 0xf369 +#define F0900_P2_PKTDELIN_DELOCK 0xf3690080 +#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040 +#define F0900_P2_CONTINUOUS_STREAM 0xf3690020 +#define F0900_P2_UNACCEPTED_STREAM 0xf3690010 +#define F0900_P2_BCH_ERROR_FLAG 0xf3690008 +#define F0900_P2_PKTDELIN_LOCK 0xf3690002 +#define F0900_P2_FIRST_LOCK 0xf3690001 /*P2_PDELSTATUS2*/ -#define R0900_P2_PDELSTATUS2 0xf36a -#define F0900_P2_PKTDEL_DEMODSEL 0xf36a0080 -#define F0900_P2_FRAME_MODCOD 0xf36a007c -#define F0900_P2_FRAME_TYPE 0xf36a0003 +#define R0900_P2_PDELSTATUS2 0xf36a +#define F0900_P2_FRAME_MODCOD 0xf36a007c +#define F0900_P2_FRAME_TYPE 0xf36a0003 /*P2_BBFCRCKO1*/ -#define R0900_P2_BBFCRCKO1 0xf36b -#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff +#define R0900_P2_BBFCRCKO1 0xf36b +#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff /*P2_BBFCRCKO0*/ -#define R0900_P2_BBFCRCKO0 0xf36c -#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff +#define R0900_P2_BBFCRCKO0 0xf36c +#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff /*P2_UPCRCKO1*/ -#define R0900_P2_UPCRCKO1 0xf36d -#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff +#define R0900_P2_UPCRCKO1 0xf36d +#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff /*P2_UPCRCKO0*/ -#define R0900_P2_UPCRCKO0 0xf36e -#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff +#define R0900_P2_UPCRCKO0 0xf36e +#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff + +/*P2_PDELCTRL3*/ +#define R0900_P2_PDELCTRL3 0xf36f +#define F0900_P2_PKTDEL_CONTFAIL 0xf36f0080 +#define F0900_P2_NOFIFO_BCHERR 0xf36f0020 /*P2_TSSTATEM*/ -#define R0900_P2_TSSTATEM 0xf370 -#define F0900_P2_TSDIL_ON 0xf3700080 -#define F0900_P2_TSSKIPRS_ON 0xf3700040 -#define F0900_P2_TSRS_ON 0xf3700020 -#define F0900_P2_TSDESCRAMB_ON 0xf3700010 -#define F0900_P2_TSFRAME_MODE 0xf3700008 -#define F0900_P2_TS_DISABLE 0xf3700004 -#define F0900_P2_TSACM_MODE 0xf3700002 -#define F0900_P2_TSOUT_NOSYNC 0xf3700001 +#define R0900_P2_TSSTATEM 0xf370 +#define F0900_P2_TSDIL_ON 0xf3700080 +#define F0900_P2_TSRS_ON 0xf3700020 +#define F0900_P2_TSDESCRAMB_ON 0xf3700010 +#define F0900_P2_TSFRAME_MODE 0xf3700008 +#define F0900_P2_TS_DISABLE 0xf3700004 +#define F0900_P2_TSOUT_NOSYNC 0xf3700001 /*P2_TSCFGH*/ -#define R0900_P2_TSCFGH 0xf372 -#define F0900_P2_TSFIFO_DVBCI 0xf3720080 -#define F0900_P2_TSFIFO_SERIAL 0xf3720040 -#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020 -#define F0900_P2_TSFIFO_DUTY50 0xf3720010 -#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008 -#define F0900_P2_TSFIFO_ERRMODE 0xf3720006 -#define F0900_P2_RST_HWARE 0xf3720001 +#define R0900_P2_TSCFGH 0xf372 +#define F0900_P2_TSFIFO_DVBCI 0xf3720080 +#define F0900_P2_TSFIFO_SERIAL 0xf3720040 +#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020 +#define F0900_P2_TSFIFO_DUTY50 0xf3720010 +#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008 +#define F0900_P2_TSFIFO_ERRMODE 0xf3720006 +#define F0900_P2_RST_HWARE 0xf3720001 /*P2_TSCFGM*/ -#define R0900_P2_TSCFGM 0xf373 -#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0 -#define F0900_P2_TSFIFO_PERMDATA 0xf3730020 -#define F0900_P2_TSFIFO_NONEWSGNL 0xf3730010 -#define F0900_P2_TSFIFO_BITSPEED 0xf3730008 -#define F0900_P2_NPD_SPECDVBS2 0xf3730004 -#define F0900_P2_TSFIFO_STOPCKDIS 0xf3730002 -#define F0900_P2_TSFIFO_INVDATA 0xf3730001 +#define R0900_P2_TSCFGM 0xf373 +#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0 +#define F0900_P2_TSFIFO_PERMDATA 0xf3730020 +#define F0900_P2_TSFIFO_DPUNACT 0xf3730002 +#define F0900_P2_TSFIFO_INVDATA 0xf3730001 /*P2_TSCFGL*/ -#define R0900_P2_TSCFGL 0xf374 -#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0 -#define F0900_P2_BCHERROR_MODE 0xf3740030 -#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008 -#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004 -#define F0900_P2_TSFIFO_DPUNACT 0xf3740002 -#define F0900_P2_TSFIFO_NPDOFF 0xf3740001 +#define R0900_P2_TSCFGL 0xf374 +#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0 +#define F0900_P2_BCHERROR_MODE 0xf3740030 +#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008 +#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004 +#define F0900_P2_TSFIFO_BITSPEED 0xf3740003 /*P2_TSINSDELH*/ -#define R0900_P2_TSINSDELH 0xf376 -#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080 -#define F0900_P2_TSDEL_XXHEADER 0xf3760040 -#define F0900_P2_TSDEL_BBHEADER 0xf3760020 -#define F0900_P2_TSDEL_DATAFIELD 0xf3760010 -#define F0900_P2_TSINSDEL_ISCR 0xf3760008 -#define F0900_P2_TSINSDEL_NPD 0xf3760004 -#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002 -#define F0900_P2_TSINSDEL_CRC8 0xf3760001 +#define R0900_P2_TSINSDELH 0xf376 +#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080 +#define F0900_P2_TSDEL_XXHEADER 0xf3760040 +#define F0900_P2_TSDEL_BBHEADER 0xf3760020 +#define F0900_P2_TSDEL_DATAFIELD 0xf3760010 +#define F0900_P2_TSINSDEL_ISCR 0xf3760008 +#define F0900_P2_TSINSDEL_NPD 0xf3760004 +#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002 +#define F0900_P2_TSINSDEL_CRC8 0xf3760001 + +/*P2_TSDIVN*/ +#define R0900_P2_TSDIVN 0xf379 +#define F0900_P2_TSFIFO_SPEEDMODE 0xf37900c0 + +/*P2_TSCFG4*/ +#define R0900_P2_TSCFG4 0xf37a +#define F0900_P2_TSFIFO_TSSPEEDMODE 0xf37a00c0 /*P2_TSSPEED*/ -#define R0900_P2_TSSPEED 0xf380 -#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff +#define R0900_P2_TSSPEED 0xf380 +#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff /*P2_TSSTATUS*/ -#define R0900_P2_TSSTATUS 0xf381 -#define F0900_P2_TSFIFO_LINEOK 0xf3810080 -#define F0900_P2_TSFIFO_ERROR 0xf3810040 -#define F0900_P2_TSFIFO_DATA7 0xf3810020 -#define F0900_P2_TSFIFO_NOSYNC 0xf3810010 -#define F0900_P2_ISCR_INITIALIZED 0xf3810008 -#define F0900_P2_ISCR_UPDATED 0xf3810004 -#define F0900_P2_SOFFIFO_UNREGUL 0xf3810002 -#define F0900_P2_DIL_READY 0xf3810001 +#define R0900_P2_TSSTATUS 0xf381 +#define F0900_P2_TSFIFO_LINEOK 0xf3810080 +#define F0900_P2_TSFIFO_ERROR 0xf3810040 +#define F0900_P2_DIL_READY 0xf3810001 /*P2_TSSTATUS2*/ -#define R0900_P2_TSSTATUS2 0xf382 -#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080 -#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040 -#define F0900_P2_DILXX_RESET 0xf3820020 -#define F0900_P2_TSSERIAL_IMPOS 0xf3820010 -#define F0900_P2_TSFIFO_LINENOK 0xf3820008 -#define F0900_P2_BITSPEED_EVENT 0xf3820004 -#define F0900_P2_SCRAMBDETECT 0xf3820002 -#define F0900_P2_ULDTV67_FALSELOCK 0xf3820001 +#define R0900_P2_TSSTATUS2 0xf382 +#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080 +#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040 +#define F0900_P2_DILXX_RESET 0xf3820020 +#define F0900_P2_TSSERIAL_IMPOS 0xf3820010 +#define F0900_P2_SCRAMBDETECT 0xf3820002 /*P2_TSBITRATE1*/ -#define R0900_P2_TSBITRATE1 0xf383 -#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff +#define R0900_P2_TSBITRATE1 0xf383 +#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff /*P2_TSBITRATE0*/ -#define R0900_P2_TSBITRATE0 0xf384 -#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff +#define R0900_P2_TSBITRATE0 0xf384 +#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff /*P2_ERRCTRL1*/ -#define R0900_P2_ERRCTRL1 0xf398 -#define F0900_P2_ERR_SOURCE1 0xf39800f0 -#define F0900_P2_NUM_EVENT1 0xf3980007 +#define R0900_P2_ERRCTRL1 0xf398 +#define F0900_P2_ERR_SOURCE1 0xf39800f0 +#define F0900_P2_NUM_EVENT1 0xf3980007 /*P2_ERRCNT12*/ -#define R0900_P2_ERRCNT12 0xf399 -#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080 -#define F0900_P2_ERR_CNT12 0xf399007f +#define R0900_P2_ERRCNT12 0xf399 +#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080 +#define F0900_P2_ERR_CNT12 0xf399007f /*P2_ERRCNT11*/ -#define R0900_P2_ERRCNT11 0xf39a -#define F0900_P2_ERR_CNT11 0xf39a00ff +#define R0900_P2_ERRCNT11 0xf39a +#define F0900_P2_ERR_CNT11 0xf39a00ff /*P2_ERRCNT10*/ -#define R0900_P2_ERRCNT10 0xf39b -#define F0900_P2_ERR_CNT10 0xf39b00ff +#define R0900_P2_ERRCNT10 0xf39b +#define F0900_P2_ERR_CNT10 0xf39b00ff /*P2_ERRCTRL2*/ -#define R0900_P2_ERRCTRL2 0xf39c -#define F0900_P2_ERR_SOURCE2 0xf39c00f0 -#define F0900_P2_NUM_EVENT2 0xf39c0007 +#define R0900_P2_ERRCTRL2 0xf39c +#define F0900_P2_ERR_SOURCE2 0xf39c00f0 +#define F0900_P2_NUM_EVENT2 0xf39c0007 /*P2_ERRCNT22*/ -#define R0900_P2_ERRCNT22 0xf39d -#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080 -#define F0900_P2_ERR_CNT22 0xf39d007f +#define R0900_P2_ERRCNT22 0xf39d +#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080 +#define F0900_P2_ERR_CNT22 0xf39d007f /*P2_ERRCNT21*/ -#define R0900_P2_ERRCNT21 0xf39e -#define F0900_P2_ERR_CNT21 0xf39e00ff +#define R0900_P2_ERRCNT21 0xf39e +#define F0900_P2_ERR_CNT21 0xf39e00ff /*P2_ERRCNT20*/ -#define R0900_P2_ERRCNT20 0xf39f -#define F0900_P2_ERR_CNT20 0xf39f00ff +#define R0900_P2_ERRCNT20 0xf39f +#define F0900_P2_ERR_CNT20 0xf39f00ff /*P2_FECSPY*/ -#define R0900_P2_FECSPY 0xf3a0 -#define F0900_P2_SPY_ENABLE 0xf3a00080 -#define F0900_P2_NO_SYNCBYTE 0xf3a00040 -#define F0900_P2_SERIAL_MODE 0xf3a00020 -#define F0900_P2_UNUSUAL_PACKET 0xf3a00010 -#define F0900_P2_BER_PACKMODE 0xf3a00008 -#define F0900_P2_BERMETER_LMODE 0xf3a00002 -#define F0900_P2_BERMETER_RESET 0xf3a00001 +#define R0900_P2_FECSPY 0xf3a0 +#define F0900_P2_SPY_ENABLE 0xf3a00080 +#define F0900_P2_NO_SYNCBYTE 0xf3a00040 +#define F0900_P2_SERIAL_MODE 0xf3a00020 +#define F0900_P2_UNUSUAL_PACKET 0xf3a00010 +#define F0900_P2_BERMETER_DATAMODE 0xf3a00008 +#define F0900_P2_BERMETER_LMODE 0xf3a00002 +#define F0900_P2_BERMETER_RESET 0xf3a00001 /*P2_FSPYCFG*/ -#define R0900_P2_FSPYCFG 0xf3a1 -#define F0900_P2_FECSPY_INPUT 0xf3a100c0 -#define F0900_P2_RST_ON_ERROR 0xf3a10020 -#define F0900_P2_ONE_SHOT 0xf3a10010 -#define F0900_P2_I2C_MODE 0xf3a1000c -#define F0900_P2_SPY_HYSTERESIS 0xf3a10003 +#define R0900_P2_FSPYCFG 0xf3a1 +#define F0900_P2_FECSPY_INPUT 0xf3a100c0 +#define F0900_P2_RST_ON_ERROR 0xf3a10020 +#define F0900_P2_ONE_SHOT 0xf3a10010 +#define F0900_P2_I2C_MODE 0xf3a1000c +#define F0900_P2_SPY_HYSTERESIS 0xf3a10003 /*P2_FSPYDATA*/ -#define R0900_P2_FSPYDATA 0xf3a2 -#define F0900_P2_SPY_STUFFING 0xf3a20080 -#define F0900_P2_NOERROR_PKTJITTER 0xf3a20040 -#define F0900_P2_SPY_CNULLPKT 0xf3a20020 -#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f +#define R0900_P2_FSPYDATA 0xf3a2 +#define F0900_P2_SPY_STUFFING 0xf3a20080 +#define F0900_P2_SPY_CNULLPKT 0xf3a20020 +#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f /*P2_FSPYOUT*/ -#define R0900_P2_FSPYOUT 0xf3a3 -#define F0900_P2_FSPY_DIRECT 0xf3a30080 -#define F0900_P2_SPY_OUTDATA_BUS 0xf3a30038 -#define F0900_P2_STUFF_MODE 0xf3a30007 +#define R0900_P2_FSPYOUT 0xf3a3 +#define F0900_P2_FSPY_DIRECT 0xf3a30080 +#define F0900_P2_STUFF_MODE 0xf3a30007 /*P2_FSTATUS*/ -#define R0900_P2_FSTATUS 0xf3a4 -#define F0900_P2_SPY_ENDSIM 0xf3a40080 -#define F0900_P2_VALID_SIM 0xf3a40040 -#define F0900_P2_FOUND_SIGNAL 0xf3a40020 -#define F0900_P2_DSS_SYNCBYTE 0xf3a40010 -#define F0900_P2_RESULT_STATE 0xf3a4000f +#define R0900_P2_FSTATUS 0xf3a4 +#define F0900_P2_SPY_ENDSIM 0xf3a40080 +#define F0900_P2_VALID_SIM 0xf3a40040 +#define F0900_P2_FOUND_SIGNAL 0xf3a40020 +#define F0900_P2_DSS_SYNCBYTE 0xf3a40010 +#define F0900_P2_RESULT_STATE 0xf3a4000f /*P2_FBERCPT4*/ -#define R0900_P2_FBERCPT4 0xf3a8 -#define F0900_P2_FBERMETER_CPT4 0xf3a800ff +#define R0900_P2_FBERCPT4 0xf3a8 +#define F0900_P2_FBERMETER_CPT4 0xf3a800ff /*P2_FBERCPT3*/ -#define R0900_P2_FBERCPT3 0xf3a9 -#define F0900_P2_FBERMETER_CPT3 0xf3a900ff +#define R0900_P2_FBERCPT3 0xf3a9 +#define F0900_P2_FBERMETER_CPT3 0xf3a900ff /*P2_FBERCPT2*/ -#define R0900_P2_FBERCPT2 0xf3aa -#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff +#define R0900_P2_FBERCPT2 0xf3aa +#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff /*P2_FBERCPT1*/ -#define R0900_P2_FBERCPT1 0xf3ab -#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff +#define R0900_P2_FBERCPT1 0xf3ab +#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff /*P2_FBERCPT0*/ -#define R0900_P2_FBERCPT0 0xf3ac -#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff +#define R0900_P2_FBERCPT0 0xf3ac +#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff /*P2_FBERERR2*/ -#define R0900_P2_FBERERR2 0xf3ad -#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff +#define R0900_P2_FBERERR2 0xf3ad +#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff /*P2_FBERERR1*/ -#define R0900_P2_FBERERR1 0xf3ae -#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff +#define R0900_P2_FBERERR1 0xf3ae +#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff /*P2_FBERERR0*/ -#define R0900_P2_FBERERR0 0xf3af -#define F0900_P2_FBERMETER_ERR0 0xf3af00ff +#define R0900_P2_FBERERR0 0xf3af +#define F0900_P2_FBERMETER_ERR0 0xf3af00ff /*P2_FSPYBER*/ -#define R0900_P2_FSPYBER 0xf3b2 -#define F0900_P2_FSPYOBS_XORREAD 0xf3b20040 -#define F0900_P2_FSPYBER_OBSMODE 0xf3b20020 -#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010 -#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008 -#define F0900_P2_FSPYBER_CTIME 0xf3b20007 +#define R0900_P2_FSPYBER 0xf3b2 +#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010 +#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008 +#define F0900_P2_FSPYBER_CTIME 0xf3b20007 /*P1_IQCONST*/ -#define R0900_P1_IQCONST 0xf400 -#define F0900_P1_CONSTEL_SELECT 0xf4000060 -#define F0900_P1_IQSYMB_SEL 0xf400001f +#define R0900_P1_IQCONST 0xf400 +#define IQCONST REGx(R0900_P1_IQCONST) +#define F0900_P1_CONSTEL_SELECT 0xf4000060 +#define F0900_P1_IQSYMB_SEL 0xf400001f /*P1_NOSCFG*/ -#define R0900_P1_NOSCFG 0xf401 -#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020 -#define F0900_P1_NOSPLH_BETA 0xf4010018 -#define F0900_P1_NOSDATA_BETA 0xf4010007 +#define R0900_P1_NOSCFG 0xf401 +#define NOSCFG REGx(R0900_P1_NOSCFG) +#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020 +#define F0900_P1_NOSPLH_BETA 0xf4010018 +#define F0900_P1_NOSDATA_BETA 0xf4010007 /*P1_ISYMB*/ -#define R0900_P1_ISYMB 0xf402 -#define F0900_P1_I_SYMBOL 0xf40201ff +#define R0900_P1_ISYMB 0xf402 +#define ISYMB REGx(R0900_P1_ISYMB) +#define F0900_P1_I_SYMBOL 0xf40201ff /*P1_QSYMB*/ -#define R0900_P1_QSYMB 0xf403 -#define F0900_P1_Q_SYMBOL 0xf40301ff +#define R0900_P1_QSYMB 0xf403 +#define QSYMB REGx(R0900_P1_QSYMB) +#define F0900_P1_Q_SYMBOL 0xf40301ff /*P1_AGC1CFG*/ -#define R0900_P1_AGC1CFG 0xf404 -#define F0900_P1_DC_FROZEN 0xf4040080 -#define F0900_P1_DC_CORRECT 0xf4040040 -#define F0900_P1_AMM_FROZEN 0xf4040020 -#define F0900_P1_AMM_CORRECT 0xf4040010 -#define F0900_P1_QUAD_FROZEN 0xf4040008 -#define F0900_P1_QUAD_CORRECT 0xf4040004 -#define F0900_P1_DCCOMP_SLOW 0xf4040002 -#define F0900_P1_IQMISM_SLOW 0xf4040001 +#define R0900_P1_AGC1CFG 0xf404 +#define AGC1CFG REGx(R0900_P1_AGC1CFG) +#define F0900_P1_DC_FROZEN 0xf4040080 +#define F0900_P1_DC_CORRECT 0xf4040040 +#define F0900_P1_AMM_FROZEN 0xf4040020 +#define F0900_P1_AMM_CORRECT 0xf4040010 +#define F0900_P1_QUAD_FROZEN 0xf4040008 +#define F0900_P1_QUAD_CORRECT 0xf4040004 /*P1_AGC1CN*/ -#define R0900_P1_AGC1CN 0xf406 -#define F0900_P1_AGC1_LOCKED 0xf4060080 -#define F0900_P1_AGC1_OVERFLOW 0xf4060040 -#define F0900_P1_AGC1_NOSLOWLK 0xf4060020 -#define F0900_P1_AGC1_MINPOWER 0xf4060010 -#define F0900_P1_AGCOUT_FAST 0xf4060008 -#define F0900_P1_AGCIQ_BETA 0xf4060007 +#define R0900_P1_AGC1CN 0xf406 +#define AGC1CN REGx(R0900_P1_AGC1CN) +#define F0900_P1_AGC1_LOCKED 0xf4060080 +#define F0900_P1_AGC1_MINPOWER 0xf4060010 +#define F0900_P1_AGCOUT_FAST 0xf4060008 +#define F0900_P1_AGCIQ_BETA 0xf4060007 /*P1_AGC1REF*/ -#define R0900_P1_AGC1REF 0xf407 -#define F0900_P1_AGCIQ_REF 0xf40700ff +#define R0900_P1_AGC1REF 0xf407 +#define AGC1REF REGx(R0900_P1_AGC1REF) +#define F0900_P1_AGCIQ_REF 0xf40700ff /*P1_IDCCOMP*/ -#define R0900_P1_IDCCOMP 0xf408 -#define F0900_P1_IAVERAGE_ADJ 0xf40801ff +#define R0900_P1_IDCCOMP 0xf408 +#define IDCCOMP REGx(R0900_P1_IDCCOMP) +#define F0900_P1_IAVERAGE_ADJ 0xf40801ff /*P1_QDCCOMP*/ -#define R0900_P1_QDCCOMP 0xf409 -#define F0900_P1_QAVERAGE_ADJ 0xf40901ff +#define R0900_P1_QDCCOMP 0xf409 +#define QDCCOMP REGx(R0900_P1_QDCCOMP) +#define F0900_P1_QAVERAGE_ADJ 0xf40901ff /*P1_POWERI*/ -#define R0900_P1_POWERI 0xf40a -#define F0900_P1_POWER_I 0xf40a00ff +#define R0900_P1_POWERI 0xf40a +#define POWERI REGx(R0900_P1_POWERI) +#define F0900_P1_POWER_I 0xf40a00ff +#define POWER_I FLDx(F0900_P1_POWER_I) /*P1_POWERQ*/ -#define R0900_P1_POWERQ 0xf40b -#define F0900_P1_POWER_Q 0xf40b00ff +#define R0900_P1_POWERQ 0xf40b +#define POWERQ REGx(R0900_P1_POWERQ) +#define F0900_P1_POWER_Q 0xf40b00ff +#define POWER_Q FLDx(F0900_P1_POWER_Q) /*P1_AGC1AMM*/ -#define R0900_P1_AGC1AMM 0xf40c -#define F0900_P1_AMM_VALUE 0xf40c00ff +#define R0900_P1_AGC1AMM 0xf40c +#define AGC1AMM REGx(R0900_P1_AGC1AMM) +#define F0900_P1_AMM_VALUE 0xf40c00ff /*P1_AGC1QUAD*/ -#define R0900_P1_AGC1QUAD 0xf40d -#define F0900_P1_QUAD_VALUE 0xf40d01ff +#define R0900_P1_AGC1QUAD 0xf40d +#define AGC1QUAD REGx(R0900_P1_AGC1QUAD) +#define F0900_P1_QUAD_VALUE 0xf40d01ff /*P1_AGCIQIN1*/ -#define R0900_P1_AGCIQIN1 0xf40e -#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff +#define R0900_P1_AGCIQIN1 0xf40e +#define AGCIQIN1 REGx(R0900_P1_AGCIQIN1) +#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff +#define AGCIQ_VALUE1 FLDx(F0900_P1_AGCIQ_VALUE1) /*P1_AGCIQIN0*/ -#define R0900_P1_AGCIQIN0 0xf40f -#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff +#define R0900_P1_AGCIQIN0 0xf40f +#define AGCIQIN0 REGx(R0900_P1_AGCIQIN0) +#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff +#define AGCIQ_VALUE0 FLDx(F0900_P1_AGCIQ_VALUE0) /*P1_DEMOD*/ -#define R0900_P1_DEMOD 0xf410 -#define F0900_P1_DEMOD_STOP 0xf4100040 -#define F0900_P1_SPECINV_CONTROL 0xf4100030 -#define F0900_P1_FORCE_ENASAMP 0xf4100008 -#define F0900_P1_MANUAL_ROLLOFF 0xf4100004 -#define F0900_P1_ROLLOFF_CONTROL 0xf4100003 +#define R0900_P1_DEMOD 0xf410 +#define DEMOD REGx(R0900_P1_DEMOD) +#define F0900_P1_MANUALS2_ROLLOFF 0xf4100080 +#define MANUALS2_ROLLOFF FLDx(F0900_P1_MANUALS2_ROLLOFF) + +#define F0900_P1_SPECINV_CONTROL 0xf4100030 +#define SPECINV_CONTROL FLDx(F0900_P1_SPECINV_CONTROL) +#define F0900_P1_FORCE_ENASAMP 0xf4100008 +#define F0900_P1_MANUALSX_ROLLOFF 0xf4100004 +#define MANUALSX_ROLLOFF FLDx(F0900_P1_MANUALSX_ROLLOFF) +#define F0900_P1_ROLLOFF_CONTROL 0xf4100003 +#define ROLLOFF_CONTROL FLDx(F0900_P1_ROLLOFF_CONTROL) /*P1_DMDMODCOD*/ -#define R0900_P1_DMDMODCOD 0xf411 -#define F0900_P1_MANUAL_MODCOD 0xf4110080 -#define F0900_P1_DEMOD_MODCOD 0xf411007c -#define F0900_P1_DEMOD_TYPE 0xf4110003 +#define R0900_P1_DMDMODCOD 0xf411 +#define DMDMODCOD REGx(R0900_P1_DMDMODCOD) +#define F0900_P1_MANUAL_MODCOD 0xf4110080 +#define F0900_P1_DEMOD_MODCOD 0xf411007c +#define DEMOD_MODCOD FLDx(F0900_P1_DEMOD_MODCOD) +#define F0900_P1_DEMOD_TYPE 0xf4110003 +#define DEMOD_TYPE FLDx(F0900_P1_DEMOD_TYPE) /*P1_DSTATUS*/ -#define R0900_P1_DSTATUS 0xf412 -#define F0900_P1_CAR_LOCK 0xf4120080 -#define F0900_P1_TMGLOCK_QUALITY 0xf4120060 -#define F0900_P1_SDVBS1_ENABLE 0xf4120010 -#define F0900_P1_LOCK_DEFINITIF 0xf4120008 -#define F0900_P1_TIMING_IS_LOCKED 0xf4120004 -#define F0900_P1_COARSE_TMGLOCK 0xf4120002 -#define F0900_P1_COARSE_CARLOCK 0xf4120001 +#define R0900_P1_DSTATUS 0xf412 +#define DSTATUS REGx(R0900_P1_DSTATUS) +#define F0900_P1_CAR_LOCK 0xf4120080 +#define F0900_P1_TMGLOCK_QUALITY 0xf4120060 +#define TMGLOCK_QUALITY FLDx(F0900_P1_TMGLOCK_QUALITY) +#define F0900_P1_LOCK_DEFINITIF 0xf4120008 +#define LOCK_DEFINITIF FLDx(F0900_P1_LOCK_DEFINITIF) +#define F0900_P1_OVADC_DETECT 0xf4120001 /*P1_DSTATUS2*/ -#define R0900_P1_DSTATUS2 0xf413 -#define F0900_P1_DEMOD_DELOCK 0xf4130080 -#define F0900_P1_DEMOD_TIMEOUT 0xf4130040 -#define F0900_P1_MODCODRQ_SYNCTAG 0xf4130020 -#define F0900_P1_POLYPH_SATEVENT 0xf4130010 -#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008 -#define F0900_P1_AGC2_OVERFLOW 0xf4130004 -#define F0900_P1_CFR_OVERFLOW 0xf4130002 -#define F0900_P1_GAMMA_OVERUNDER 0xf4130001 +#define R0900_P1_DSTATUS2 0xf413 +#define DSTATUS2 REGx(R0900_P1_DSTATUS2) +#define F0900_P1_DEMOD_DELOCK 0xf4130080 +#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008 +#define F0900_P1_AGC2_OVERFLOW 0xf4130004 +#define F0900_P1_CFR_OVERFLOW 0xf4130002 +#define F0900_P1_GAMMA_OVERUNDER 0xf4130001 /*P1_DMDCFGMD*/ -#define R0900_P1_DMDCFGMD 0xf414 -#define F0900_P1_DVBS2_ENABLE 0xf4140080 -#define F0900_P1_DVBS1_ENABLE 0xf4140040 -#define F0900_P1_CFR_AUTOSCAN 0xf4140020 -#define F0900_P1_SCAN_ENABLE 0xf4140010 -#define F0900_P1_TUN_AUTOSCAN 0xf4140008 -#define F0900_P1_NOFORCE_RELOCK 0xf4140004 -#define F0900_P1_TUN_RNG 0xf4140003 +#define R0900_P1_DMDCFGMD 0xf414 +#define DMDCFGMD REGx(R0900_P1_DMDCFGMD) +#define F0900_P1_DVBS2_ENABLE 0xf4140080 +#define DVBS2_ENABLE FLDx(F0900_P1_DVBS2_ENABLE) +#define F0900_P1_DVBS1_ENABLE 0xf4140040 +#define DVBS1_ENABLE FLDx(F0900_P1_DVBS1_ENABLE) +#define F0900_P1_SCAN_ENABLE 0xf4140010 +#define SCAN_ENABLE FLDx(F0900_P1_SCAN_ENABLE) +#define F0900_P1_CFR_AUTOSCAN 0xf4140008 +#define CFR_AUTOSCAN FLDx(F0900_P1_CFR_AUTOSCAN) +#define F0900_P1_TUN_RNG 0xf4140003 /*P1_DMDCFG2*/ -#define R0900_P1_DMDCFG2 0xf415 -#define F0900_P1_AGC1_WAITLOCK 0xf4150080 -#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040 -#define F0900_P1_OVERFLOW_TIMEOUT 0xf4150020 -#define F0900_P1_SCANFAIL_TIMEOUT 0xf4150010 -#define F0900_P1_DMDTOUT_BACK 0xf4150008 -#define F0900_P1_CARLOCK_S1ENABLE 0xf4150004 -#define F0900_P1_COARSE_LK3MODE 0xf4150002 -#define F0900_P1_COARSE_LK2MODE 0xf4150001 +#define R0900_P1_DMDCFG2 0xf415 +#define DMDCFG2 REGx(R0900_P1_DMDCFG2) +#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040 +#define S1S2_SEQUENTIAL FLDx(F0900_P1_S1S2_SEQUENTIAL) +#define F0900_P1_INFINITE_RELOCK 0xf4150010 /*P1_DMDISTATE*/ -#define R0900_P1_DMDISTATE 0xf416 -#define F0900_P1_I2C_NORESETDMODE 0xf4160080 -#define F0900_P1_FORCE_ETAPED 0xf4160040 -#define F0900_P1_SDMDRST_DIRCLK 0xf4160020 -#define F0900_P1_I2C_DEMOD_MODE 0xf416001f +#define R0900_P1_DMDISTATE 0xf416 +#define DMDISTATE REGx(R0900_P1_DMDISTATE) +#define F0900_P1_I2C_DEMOD_MODE 0xf416001f +#define DEMOD_MODE FLDx(F0900_P1_I2C_DEMOD_MODE) /*P1_DMDT0M*/ -#define R0900_P1_DMDT0M 0xf417 -#define F0900_P1_DMDT0_MIN 0xf41700ff +#define R0900_P1_DMDT0M 0xf417 +#define DMDT0M REGx(R0900_P1_DMDT0M) +#define F0900_P1_DMDT0_MIN 0xf41700ff /*P1_DMDSTATE*/ -#define R0900_P1_DMDSTATE 0xf41b -#define F0900_P1_DEMOD_LOCKED 0xf41b0080 -#define F0900_P1_HEADER_MODE 0xf41b0060 -#define F0900_P1_DEMOD_MODE 0xf41b001f +#define R0900_P1_DMDSTATE 0xf41b +#define DMDSTATE REGx(R0900_P1_DMDSTATE) +#define F0900_P1_HEADER_MODE 0xf41b0060 +#define HEADER_MODE FLDx(F0900_P1_HEADER_MODE) /*P1_DMDFLYW*/ -#define R0900_P1_DMDFLYW 0xf41c -#define F0900_P1_I2C_IRQVAL 0xf41c00f0 -#define F0900_P1_FLYWHEEL_CPT 0xf41c000f +#define R0900_P1_DMDFLYW 0xf41c +#define DMDFLYW REGx(R0900_P1_DMDFLYW) +#define F0900_P1_I2C_IRQVAL 0xf41c00f0 +#define F0900_P1_FLYWHEEL_CPT 0xf41c000f +#define FLYWHEEL_CPT FLDx(F0900_P1_FLYWHEEL_CPT) /*P1_DSTATUS3*/ -#define R0900_P1_DSTATUS3 0xf41d -#define F0900_P1_CFR_ZIGZAG 0xf41d0080 -#define F0900_P1_DEMOD_CFGMODE 0xf41d0060 -#define F0900_P1_GAMMA_LOWBAUDRATE 0xf41d0010 -#define F0900_P1_RELOCK_MODE 0xf41d0008 -#define F0900_P1_DEMOD_FAIL 0xf41d0004 -#define F0900_P1_ETAPE1A_DVBXMEM 0xf41d0003 +#define R0900_P1_DSTATUS3 0xf41d +#define DSTATUS3 REGx(R0900_P1_DSTATUS3) +#define F0900_P1_DEMOD_CFGMODE 0xf41d0060 /*P1_DMDCFG3*/ -#define R0900_P1_DMDCFG3 0xf41e -#define F0900_P1_DVBS1_TMGWAIT 0xf41e0080 -#define F0900_P1_NO_BWCENTERING 0xf41e0040 -#define F0900_P1_INV_SEQSRCH 0xf41e0020 -#define F0900_P1_DIS_SFRUPLOW_TRK 0xf41e0010 -#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008 -#define F0900_P1_LOCKTIME_MODE 0xf41e0007 +#define R0900_P1_DMDCFG3 0xf41e +#define DMDCFG3 REGx(R0900_P1_DMDCFG3) +#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008 /*P1_DMDCFG4*/ -#define R0900_P1_DMDCFG4 0xf41f -#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008 -#define F0900_P1_DIS_CLKENABLE 0xf41f0004 -#define F0900_P1_DIS_HDRDIVLOCK 0xf41f0002 -#define F0900_P1_NO_TNRWBINIT 0xf41f0001 +#define R0900_P1_DMDCFG4 0xf41f +#define DMDCFG4 REGx(R0900_P1_DMDCFG4) +#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008 /*P1_CORRELMANT*/ -#define R0900_P1_CORRELMANT 0xf420 -#define F0900_P1_CORREL_MANT 0xf42000ff +#define R0900_P1_CORRELMANT 0xf420 +#define CORRELMANT REGx(R0900_P1_CORRELMANT) +#define F0900_P1_CORREL_MANT 0xf42000ff /*P1_CORRELABS*/ -#define R0900_P1_CORRELABS 0xf421 -#define F0900_P1_CORREL_ABS 0xf42100ff +#define R0900_P1_CORRELABS 0xf421 +#define CORRELABS REGx(R0900_P1_CORRELABS) +#define F0900_P1_CORREL_ABS 0xf42100ff /*P1_CORRELEXP*/ -#define R0900_P1_CORRELEXP 0xf422 -#define F0900_P1_CORREL_ABSEXP 0xf42200f0 -#define F0900_P1_CORREL_EXP 0xf422000f +#define R0900_P1_CORRELEXP 0xf422 +#define CORRELEXP REGx(R0900_P1_CORRELEXP) +#define F0900_P1_CORREL_ABSEXP 0xf42200f0 +#define F0900_P1_CORREL_EXP 0xf422000f /*P1_PLHMODCOD*/ -#define R0900_P1_PLHMODCOD 0xf424 -#define F0900_P1_SPECINV_DEMOD 0xf4240080 -#define F0900_P1_PLH_MODCOD 0xf424007c -#define F0900_P1_PLH_TYPE 0xf4240003 - -/*P1_AGCK32*/ -#define R0900_P1_AGCK32 0xf42b -#define F0900_P1_R3ADJOFF_32APSK 0xf42b0080 -#define F0900_P1_R2ADJOFF_32APSK 0xf42b0040 -#define F0900_P1_R1ADJOFF_32APSK 0xf42b0020 -#define F0900_P1_RADJ_32APSK 0xf42b001f +#define R0900_P1_PLHMODCOD 0xf424 +#define PLHMODCOD REGx(R0900_P1_PLHMODCOD) +#define F0900_P1_SPECINV_DEMOD 0xf4240080 +#define SPECINV_DEMOD FLDx(F0900_P1_SPECINV_DEMOD) +#define F0900_P1_PLH_MODCOD 0xf424007c +#define F0900_P1_PLH_TYPE 0xf4240003 + +/*P1_DMDREG*/ +#define R0900_P1_DMDREG 0xf425 +#define DMDREG REGx(R0900_P1_DMDREG) +#define F0900_P1_DECIM_PLFRAMES 0xf4250001 /*P1_AGC2O*/ -#define R0900_P1_AGC2O 0xf42c -#define F0900_P1_AGC2REF_ADJUSTING 0xf42c0080 -#define F0900_P1_AGC2_COARSEFAST 0xf42c0040 -#define F0900_P1_AGC2_LKSQRT 0xf42c0020 -#define F0900_P1_AGC2_LKMODE 0xf42c0010 -#define F0900_P1_AGC2_LKEQUA 0xf42c0008 -#define F0900_P1_AGC2_COEF 0xf42c0007 +#define R0900_P1_AGC2O 0xf42c +#define AGC2O REGx(R0900_P1_AGC2O) +#define F0900_P1_AGC2_COEF 0xf42c0007 /*P1_AGC2REF*/ -#define R0900_P1_AGC2REF 0xf42d -#define F0900_P1_AGC2_REF 0xf42d00ff +#define R0900_P1_AGC2REF 0xf42d +#define AGC2REF REGx(R0900_P1_AGC2REF) +#define F0900_P1_AGC2_REF 0xf42d00ff /*P1_AGC1ADJ*/ -#define R0900_P1_AGC1ADJ 0xf42e -#define F0900_P1_AGC1ADJ_MANUAL 0xf42e0080 -#define F0900_P1_AGC1_ADJUSTED 0xf42e017f +#define R0900_P1_AGC1ADJ 0xf42e +#define AGC1ADJ REGx(R0900_P1_AGC1ADJ) +#define F0900_P1_AGC1_ADJUSTED 0xf42e007f /*P1_AGC2I1*/ -#define R0900_P1_AGC2I1 0xf436 -#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff +#define R0900_P1_AGC2I1 0xf436 +#define AGC2I1 REGx(R0900_P1_AGC2I1) +#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff /*P1_AGC2I0*/ -#define R0900_P1_AGC2I0 0xf437 -#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff +#define R0900_P1_AGC2I0 0xf437 +#define AGC2I0 REGx(R0900_P1_AGC2I0) +#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff /*P1_CARCFG*/ -#define R0900_P1_CARCFG 0xf438 -#define F0900_P1_CFRUPLOW_AUTO 0xf4380080 -#define F0900_P1_CFRUPLOW_TEST 0xf4380040 -#define F0900_P1_EN_CAR2CENTER 0xf4380020 -#define F0900_P1_CARHDR_NODIV8 0xf4380010 -#define F0900_P1_I2C_ROTA 0xf4380008 -#define F0900_P1_ROTAON 0xf4380004 -#define F0900_P1_PH_DET_ALGO 0xf4380003 +#define R0900_P1_CARCFG 0xf438 +#define CARCFG REGx(R0900_P1_CARCFG) +#define F0900_P1_CFRUPLOW_AUTO 0xf4380080 +#define F0900_P1_CFRUPLOW_TEST 0xf4380040 +#define F0900_P1_ROTAON 0xf4380004 +#define F0900_P1_PH_DET_ALGO 0xf4380003 /*P1_ACLC*/ -#define R0900_P1_ACLC 0xf439 -#define F0900_P1_STOP_S2ALPHA 0xf43900c0 -#define F0900_P1_CAR_ALPHA_MANT 0xf4390030 -#define F0900_P1_CAR_ALPHA_EXP 0xf439000f +#define R0900_P1_ACLC 0xf439 +#define ACLC REGx(R0900_P1_ACLC) +#define F0900_P1_CAR_ALPHA_MANT 0xf4390030 +#define F0900_P1_CAR_ALPHA_EXP 0xf439000f /*P1_BCLC*/ -#define R0900_P1_BCLC 0xf43a -#define F0900_P1_STOP_S2BETA 0xf43a00c0 -#define F0900_P1_CAR_BETA_MANT 0xf43a0030 -#define F0900_P1_CAR_BETA_EXP 0xf43a000f +#define R0900_P1_BCLC 0xf43a +#define BCLC REGx(R0900_P1_BCLC) +#define F0900_P1_CAR_BETA_MANT 0xf43a0030 +#define F0900_P1_CAR_BETA_EXP 0xf43a000f /*P1_CARFREQ*/ -#define R0900_P1_CARFREQ 0xf43d -#define F0900_P1_KC_COARSE_EXP 0xf43d00f0 -#define F0900_P1_BETA_FREQ 0xf43d000f +#define R0900_P1_CARFREQ 0xf43d +#define CARFREQ REGx(R0900_P1_CARFREQ) +#define F0900_P1_KC_COARSE_EXP 0xf43d00f0 +#define F0900_P1_BETA_FREQ 0xf43d000f /*P1_CARHDR*/ -#define R0900_P1_CARHDR 0xf43e -#define F0900_P1_K_FREQ_HDR 0xf43e00ff +#define R0900_P1_CARHDR 0xf43e +#define CARHDR REGx(R0900_P1_CARHDR) +#define F0900_P1_K_FREQ_HDR 0xf43e00ff /*P1_LDT*/ -#define R0900_P1_LDT 0xf43f -#define F0900_P1_CARLOCK_THRES 0xf43f01ff +#define R0900_P1_LDT 0xf43f +#define LDT REGx(R0900_P1_LDT) +#define F0900_P1_CARLOCK_THRES 0xf43f01ff /*P1_LDT2*/ -#define R0900_P1_LDT2 0xf440 -#define F0900_P1_CARLOCK_THRES2 0xf44001ff +#define R0900_P1_LDT2 0xf440 +#define LDT2 REGx(R0900_P1_LDT2) +#define F0900_P1_CARLOCK_THRES2 0xf44001ff /*P1_CFRICFG*/ -#define R0900_P1_CFRICFG 0xf441 -#define F0900_P1_CFRINIT_UNVALRNG 0xf4410080 -#define F0900_P1_CFRINIT_LUNVALCPT 0xf4410040 -#define F0900_P1_CFRINIT_ABORTDBL 0xf4410020 -#define F0900_P1_CFRINIT_ABORTPRED 0xf4410010 -#define F0900_P1_CFRINIT_UNVALSKIP 0xf4410008 -#define F0900_P1_CFRINIT_CSTINC 0xf4410004 -#define F0900_P1_NEG_CFRSTEP 0xf4410001 +#define R0900_P1_CFRICFG 0xf441 +#define CFRICFG REGx(R0900_P1_CFRICFG) +#define F0900_P1_NEG_CFRSTEP 0xf4410001 /*P1_CFRUP1*/ -#define R0900_P1_CFRUP1 0xf442 -#define F0900_P1_CFR_UP1 0xf44201ff +#define R0900_P1_CFRUP1 0xf442 +#define CFRUP1 REGx(R0900_P1_CFRUP1) +#define F0900_P1_CFR_UP1 0xf44201ff +#define CFR_UP1 FLDx(F0900_P1_CFR_UP1) /*P1_CFRUP0*/ -#define R0900_P1_CFRUP0 0xf443 -#define F0900_P1_CFR_UP0 0xf44300ff +#define R0900_P1_CFRUP0 0xf443 +#define CFRUP0 REGx(R0900_P1_CFRUP0) +#define F0900_P1_CFR_UP0 0xf44300ff +#define CFR_UP0 FLDx(F0900_P1_CFR_UP0) /*P1_CFRLOW1*/ -#define R0900_P1_CFRLOW1 0xf446 -#define F0900_P1_CFR_LOW1 0xf44601ff +#define R0900_P1_CFRLOW1 0xf446 +#define CFRLOW1 REGx(R0900_P1_CFRLOW1) +#define F0900_P1_CFR_LOW1 0xf44601ff +#define CFR_LOW1 FLDx(F0900_P1_CFR_LOW1) /*P1_CFRLOW0*/ -#define R0900_P1_CFRLOW0 0xf447 -#define F0900_P1_CFR_LOW0 0xf44700ff +#define R0900_P1_CFRLOW0 0xf447 +#define CFRLOW0 REGx(R0900_P1_CFRLOW0) +#define F0900_P1_CFR_LOW0 0xf44700ff +#define CFR_LOW0 FLDx(F0900_P1_CFR_LOW0) /*P1_CFRINIT1*/ -#define R0900_P1_CFRINIT1 0xf448 -#define F0900_P1_CFR_INIT1 0xf44801ff +#define R0900_P1_CFRINIT1 0xf448 +#define CFRINIT1 REGx(R0900_P1_CFRINIT1) +#define F0900_P1_CFR_INIT1 0xf44801ff +#define CFR_INIT1 FLDx(F0900_P1_CFR_INIT1) /*P1_CFRINIT0*/ -#define R0900_P1_CFRINIT0 0xf449 -#define F0900_P1_CFR_INIT0 0xf44900ff +#define R0900_P1_CFRINIT0 0xf449 +#define CFRINIT0 REGx(R0900_P1_CFRINIT0) +#define F0900_P1_CFR_INIT0 0xf44900ff +#define CFR_INIT0 FLDx(F0900_P1_CFR_INIT0) /*P1_CFRINC1*/ -#define R0900_P1_CFRINC1 0xf44a -#define F0900_P1_MANUAL_CFRINC 0xf44a0080 -#define F0900_P1_CFR_INC1 0xf44a017f +#define R0900_P1_CFRINC1 0xf44a +#define CFRINC1 REGx(R0900_P1_CFRINC1) +#define F0900_P1_MANUAL_CFRINC 0xf44a0080 +#define F0900_P1_CFR_INC1 0xf44a003f /*P1_CFRINC0*/ -#define R0900_P1_CFRINC0 0xf44b -#define F0900_P1_CFR_INC0 0xf44b00f0 +#define R0900_P1_CFRINC0 0xf44b +#define CFRINC0 REGx(R0900_P1_CFRINC0) +#define F0900_P1_CFR_INC0 0xf44b00f8 /*P1_CFR2*/ -#define R0900_P1_CFR2 0xf44c -#define F0900_P1_CAR_FREQ2 0xf44c01ff +#define R0900_P1_CFR2 0xf44c +#define CFR2 REGx(R0900_P1_CFR2) +#define F0900_P1_CAR_FREQ2 0xf44c01ff +#define CAR_FREQ2 FLDx(F0900_P1_CAR_FREQ2) /*P1_CFR1*/ -#define R0900_P1_CFR1 0xf44d -#define F0900_P1_CAR_FREQ1 0xf44d00ff +#define R0900_P1_CFR1 0xf44d +#define CFR1 REGx(R0900_P1_CFR1) +#define F0900_P1_CAR_FREQ1 0xf44d00ff +#define CAR_FREQ1 FLDx(F0900_P1_CAR_FREQ1) /*P1_CFR0*/ -#define R0900_P1_CFR0 0xf44e -#define F0900_P1_CAR_FREQ0 0xf44e00ff +#define R0900_P1_CFR0 0xf44e +#define CFR0 REGx(R0900_P1_CFR0) +#define F0900_P1_CAR_FREQ0 0xf44e00ff +#define CAR_FREQ0 FLDx(F0900_P1_CAR_FREQ0) /*P1_LDI*/ -#define R0900_P1_LDI 0xf44f -#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff +#define R0900_P1_LDI 0xf44f +#define LDI REGx(R0900_P1_LDI) +#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff /*P1_TMGCFG*/ -#define R0900_P1_TMGCFG 0xf450 -#define F0900_P1_TMGLOCK_BETA 0xf45000c0 -#define F0900_P1_NOTMG_GROUPDELAY 0xf4500020 -#define F0900_P1_DO_TIMING_CORR 0xf4500010 -#define F0900_P1_MANUAL_SCAN 0xf450000c -#define F0900_P1_TMG_MINFREQ 0xf4500003 +#define R0900_P1_TMGCFG 0xf450 +#define TMGCFG REGx(R0900_P1_TMGCFG) +#define F0900_P1_TMGLOCK_BETA 0xf45000c0 +#define F0900_P1_DO_TIMING_CORR 0xf4500010 +#define F0900_P1_TMG_MINFREQ 0xf4500003 /*P1_RTC*/ -#define R0900_P1_RTC 0xf451 -#define F0900_P1_TMGALPHA_EXP 0xf45100f0 -#define F0900_P1_TMGBETA_EXP 0xf451000f +#define R0900_P1_RTC 0xf451 +#define RTC REGx(R0900_P1_RTC) +#define F0900_P1_TMGALPHA_EXP 0xf45100f0 +#define F0900_P1_TMGBETA_EXP 0xf451000f /*P1_RTCS2*/ -#define R0900_P1_RTCS2 0xf452 -#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0 -#define F0900_P1_TMGBETAS2_EXP 0xf452000f +#define R0900_P1_RTCS2 0xf452 +#define RTCS2 REGx(R0900_P1_RTCS2) +#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0 +#define F0900_P1_TMGBETAS2_EXP 0xf452000f /*P1_TMGTHRISE*/ -#define R0900_P1_TMGTHRISE 0xf453 -#define F0900_P1_TMGLOCK_THRISE 0xf45300ff +#define R0900_P1_TMGTHRISE 0xf453 +#define TMGTHRISE REGx(R0900_P1_TMGTHRISE) +#define F0900_P1_TMGLOCK_THRISE 0xf45300ff /*P1_TMGTHFALL*/ -#define R0900_P1_TMGTHFALL 0xf454 -#define F0900_P1_TMGLOCK_THFALL 0xf45400ff +#define R0900_P1_TMGTHFALL 0xf454 +#define TMGTHFALL REGx(R0900_P1_TMGTHFALL) +#define F0900_P1_TMGLOCK_THFALL 0xf45400ff /*P1_SFRUPRATIO*/ -#define R0900_P1_SFRUPRATIO 0xf455 -#define F0900_P1_SFR_UPRATIO 0xf45500ff +#define R0900_P1_SFRUPRATIO 0xf455 +#define SFRUPRATIO REGx(R0900_P1_SFRUPRATIO) +#define F0900_P1_SFR_UPRATIO 0xf45500ff /*P1_SFRLOWRATIO*/ -#define R0900_P1_SFRLOWRATIO 0xf456 -#define F0900_P1_SFR_LOWRATIO 0xf45600ff +#define R0900_P1_SFRLOWRATIO 0xf456 +#define F0900_P1_SFR_LOWRATIO 0xf45600ff /*P1_KREFTMG*/ -#define R0900_P1_KREFTMG 0xf458 -#define F0900_P1_KREF_TMG 0xf45800ff +#define R0900_P1_KREFTMG 0xf458 +#define KREFTMG REGx(R0900_P1_KREFTMG) +#define F0900_P1_KREF_TMG 0xf45800ff /*P1_SFRSTEP*/ -#define R0900_P1_SFRSTEP 0xf459 -#define F0900_P1_SFR_SCANSTEP 0xf45900f0 -#define F0900_P1_SFR_CENTERSTEP 0xf459000f +#define R0900_P1_SFRSTEP 0xf459 +#define SFRSTEP REGx(R0900_P1_SFRSTEP) +#define F0900_P1_SFR_SCANSTEP 0xf45900f0 +#define F0900_P1_SFR_CENTERSTEP 0xf459000f /*P1_TMGCFG2*/ -#define R0900_P1_TMGCFG2 0xf45a -#define F0900_P1_DIS_AUTOSAMP 0xf45a0008 -#define F0900_P1_SCANINIT_QUART 0xf45a0004 -#define F0900_P1_NOTMG_DVBS1DERAT 0xf45a0002 -#define F0900_P1_SFRRATIO_FINE 0xf45a0001 +#define R0900_P1_TMGCFG2 0xf45a +#define TMGCFG2 REGx(R0900_P1_TMGCFG2) +#define F0900_P1_SFRRATIO_FINE 0xf45a0001 + +/*P1_KREFTMG2*/ +#define R0900_P1_KREFTMG2 0xf45b +#define KREFTMG2 REGx(R0900_P1_KREFTMG2) +#define F0900_P1_KREF_TMG2 0xf45b00ff /*P1_SFRINIT1*/ -#define R0900_P1_SFRINIT1 0xf45e -#define F0900_P1_SFR_INIT1 0xf45e00ff +#define R0900_P1_SFRINIT1 0xf45e +#define SFRINIT1 REGx(R0900_P1_SFRINIT1) +#define F0900_P1_SFR_INIT1 0xf45e007f /*P1_SFRINIT0*/ -#define R0900_P1_SFRINIT0 0xf45f -#define F0900_P1_SFR_INIT0 0xf45f00ff +#define R0900_P1_SFRINIT0 0xf45f +#define SFRINIT0 REGx(R0900_P1_SFRINIT0) +#define F0900_P1_SFR_INIT0 0xf45f00ff /*P1_SFRUP1*/ -#define R0900_P1_SFRUP1 0xf460 -#define F0900_P1_AUTO_GUP 0xf4600080 -#define F0900_P1_SYMB_FREQ_UP1 0xf460007f +#define R0900_P1_SFRUP1 0xf460 +#define SFRUP1 REGx(R0900_P1_SFRUP1) +#define F0900_P1_AUTO_GUP 0xf4600080 +#define AUTO_GUP FLDx(F0900_P1_AUTO_GUP) +#define F0900_P1_SYMB_FREQ_UP1 0xf460007f /*P1_SFRUP0*/ -#define R0900_P1_SFRUP0 0xf461 -#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff +#define R0900_P1_SFRUP0 0xf461 +#define SFRUP0 REGx(R0900_P1_SFRUP0) +#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff /*P1_SFRLOW1*/ -#define R0900_P1_SFRLOW1 0xf462 -#define F0900_P1_AUTO_GLOW 0xf4620080 -#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f +#define R0900_P1_SFRLOW1 0xf462 +#define SFRLOW1 REGx(R0900_P1_SFRLOW1) +#define F0900_P1_AUTO_GLOW 0xf4620080 +#define AUTO_GLOW FLDx(F0900_P1_AUTO_GLOW) +#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f /*P1_SFRLOW0*/ -#define R0900_P1_SFRLOW0 0xf463 -#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff +#define R0900_P1_SFRLOW0 0xf463 +#define SFRLOW0 REGx(R0900_P1_SFRLOW0) +#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff /*P1_SFR3*/ -#define R0900_P1_SFR3 0xf464 -#define F0900_P1_SYMB_FREQ3 0xf46400ff +#define R0900_P1_SFR3 0xf464 +#define SFR3 REGx(R0900_P1_SFR3) +#define F0900_P1_SYMB_FREQ3 0xf46400ff +#define SYMB_FREQ3 FLDx(F0900_P1_SYMB_FREQ3) /*P1_SFR2*/ -#define R0900_P1_SFR2 0xf465 -#define F0900_P1_SYMB_FREQ2 0xf46500ff +#define R0900_P1_SFR2 0xf465 +#define SFR2 REGx(R0900_P1_SFR2) +#define F0900_P1_SYMB_FREQ2 0xf46500ff +#define SYMB_FREQ2 FLDx(F0900_P1_SYMB_FREQ2) /*P1_SFR1*/ -#define R0900_P1_SFR1 0xf466 -#define F0900_P1_SYMB_FREQ1 0xf46600ff +#define R0900_P1_SFR1 0xf466 +#define SFR1 REGx(R0900_P1_SFR1) +#define F0900_P1_SYMB_FREQ1 0xf46600ff +#define SYMB_FREQ1 FLDx(F0900_P1_SYMB_FREQ1) /*P1_SFR0*/ -#define R0900_P1_SFR0 0xf467 -#define F0900_P1_SYMB_FREQ0 0xf46700ff +#define R0900_P1_SFR0 0xf467 +#define SFR0 REGx(R0900_P1_SFR0) +#define F0900_P1_SYMB_FREQ0 0xf46700ff +#define SYMB_FREQ0 FLDx(F0900_P1_SYMB_FREQ0) /*P1_TMGREG2*/ -#define R0900_P1_TMGREG2 0xf468 -#define F0900_P1_TMGREG2 0xf46800ff +#define R0900_P1_TMGREG2 0xf468 +#define TMGREG2 REGx(R0900_P1_TMGREG2) +#define F0900_P1_TMGREG2 0xf46800ff /*P1_TMGREG1*/ -#define R0900_P1_TMGREG1 0xf469 -#define F0900_P1_TMGREG1 0xf46900ff +#define R0900_P1_TMGREG1 0xf469 +#define TMGREG1 REGx(R0900_P1_TMGREG1) +#define F0900_P1_TMGREG1 0xf46900ff /*P1_TMGREG0*/ -#define R0900_P1_TMGREG0 0xf46a -#define F0900_P1_TMGREG0 0xf46a00ff +#define R0900_P1_TMGREG0 0xf46a +#define TMGREG0 REGx(R0900_P1_TMGREG0) +#define F0900_P1_TMGREG0 0xf46a00ff /*P1_TMGLOCK1*/ -#define R0900_P1_TMGLOCK1 0xf46b -#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff +#define R0900_P1_TMGLOCK1 0xf46b +#define TMGLOCK1 REGx(R0900_P1_TMGLOCK1) +#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff /*P1_TMGLOCK0*/ -#define R0900_P1_TMGLOCK0 0xf46c -#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff +#define R0900_P1_TMGLOCK0 0xf46c +#define TMGLOCK0 REGx(R0900_P1_TMGLOCK0) +#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff /*P1_TMGOBS*/ -#define R0900_P1_TMGOBS 0xf46d -#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0 -#define F0900_P1_SCAN_SIGN 0xf46d0030 -#define F0900_P1_TMG_SCANNING 0xf46d0008 -#define F0900_P1_CHCENTERING_MODE 0xf46d0004 -#define F0900_P1_TMG_SCANFAIL 0xf46d0002 +#define R0900_P1_TMGOBS 0xf46d +#define TMGOBS REGx(R0900_P1_TMGOBS) +#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0 +#define ROLLOFF_STATUS FLDx(F0900_P1_ROLLOFF_STATUS) /*P1_EQUALCFG*/ -#define R0900_P1_EQUALCFG 0xf46f -#define F0900_P1_NOTMG_NEGALWAIT 0xf46f0080 -#define F0900_P1_EQUAL_ON 0xf46f0040 -#define F0900_P1_SEL_EQUALCOR 0xf46f0038 -#define F0900_P1_MU_EQUALDFE 0xf46f0007 +#define R0900_P1_EQUALCFG 0xf46f +#define EQUALCFG REGx(R0900_P1_EQUALCFG) +#define F0900_P1_EQUAL_ON 0xf46f0040 +#define F0900_P1_MU_EQUALDFE 0xf46f0007 /*P1_EQUAI1*/ -#define R0900_P1_EQUAI1 0xf470 -#define F0900_P1_EQUA_ACCI1 0xf47001ff +#define R0900_P1_EQUAI1 0xf470 +#define EQUAI1 REGx(R0900_P1_EQUAI1) +#define F0900_P1_EQUA_ACCI1 0xf47001ff /*P1_EQUAQ1*/ -#define R0900_P1_EQUAQ1 0xf471 -#define F0900_P1_EQUA_ACCQ1 0xf47101ff +#define R0900_P1_EQUAQ1 0xf471 +#define EQUAQ1 REGx(R0900_P1_EQUAQ1) +#define F0900_P1_EQUA_ACCQ1 0xf47101ff /*P1_EQUAI2*/ -#define R0900_P1_EQUAI2 0xf472 -#define F0900_P1_EQUA_ACCI2 0xf47201ff +#define R0900_P1_EQUAI2 0xf472 +#define EQUAI2 REGx(R0900_P1_EQUAI2) +#define F0900_P1_EQUA_ACCI2 0xf47201ff /*P1_EQUAQ2*/ -#define R0900_P1_EQUAQ2 0xf473 -#define F0900_P1_EQUA_ACCQ2 0xf47301ff +#define R0900_P1_EQUAQ2 0xf473 +#define EQUAQ2 REGx(R0900_P1_EQUAQ2) +#define F0900_P1_EQUA_ACCQ2 0xf47301ff /*P1_EQUAI3*/ -#define R0900_P1_EQUAI3 0xf474 -#define F0900_P1_EQUA_ACCI3 0xf47401ff +#define R0900_P1_EQUAI3 0xf474 +#define EQUAI3 REGx(R0900_P1_EQUAI3) +#define F0900_P1_EQUA_ACCI3 0xf47401ff /*P1_EQUAQ3*/ -#define R0900_P1_EQUAQ3 0xf475 -#define F0900_P1_EQUA_ACCQ3 0xf47501ff +#define R0900_P1_EQUAQ3 0xf475 +#define EQUAQ3 REGx(R0900_P1_EQUAQ3) +#define F0900_P1_EQUA_ACCQ3 0xf47501ff /*P1_EQUAI4*/ -#define R0900_P1_EQUAI4 0xf476 -#define F0900_P1_EQUA_ACCI4 0xf47601ff +#define R0900_P1_EQUAI4 0xf476 +#define EQUAI4 REGx(R0900_P1_EQUAI4) +#define F0900_P1_EQUA_ACCI4 0xf47601ff /*P1_EQUAQ4*/ -#define R0900_P1_EQUAQ4 0xf477 -#define F0900_P1_EQUA_ACCQ4 0xf47701ff +#define R0900_P1_EQUAQ4 0xf477 +#define EQUAQ4 REGx(R0900_P1_EQUAQ4) +#define F0900_P1_EQUA_ACCQ4 0xf47701ff /*P1_EQUAI5*/ -#define R0900_P1_EQUAI5 0xf478 -#define F0900_P1_EQUA_ACCI5 0xf47801ff +#define R0900_P1_EQUAI5 0xf478 +#define EQUAI5 REGx(R0900_P1_EQUAI5) +#define F0900_P1_EQUA_ACCI5 0xf47801ff /*P1_EQUAQ5*/ -#define R0900_P1_EQUAQ5 0xf479 -#define F0900_P1_EQUA_ACCQ5 0xf47901ff +#define R0900_P1_EQUAQ5 0xf479 +#define EQUAQ5 REGx(R0900_P1_EQUAQ5) +#define F0900_P1_EQUA_ACCQ5 0xf47901ff /*P1_EQUAI6*/ -#define R0900_P1_EQUAI6 0xf47a -#define F0900_P1_EQUA_ACCI6 0xf47a01ff +#define R0900_P1_EQUAI6 0xf47a +#define EQUAI6 REGx(R0900_P1_EQUAI6) +#define F0900_P1_EQUA_ACCI6 0xf47a01ff /*P1_EQUAQ6*/ -#define R0900_P1_EQUAQ6 0xf47b -#define F0900_P1_EQUA_ACCQ6 0xf47b01ff +#define R0900_P1_EQUAQ6 0xf47b +#define EQUAQ6 REGx(R0900_P1_EQUAQ6) +#define F0900_P1_EQUA_ACCQ6 0xf47b01ff /*P1_EQUAI7*/ -#define R0900_P1_EQUAI7 0xf47c -#define F0900_P1_EQUA_ACCI7 0xf47c01ff +#define R0900_P1_EQUAI7 0xf47c +#define EQUAI7 REGx(R0900_P1_EQUAI7) +#define F0900_P1_EQUA_ACCI7 0xf47c01ff /*P1_EQUAQ7*/ -#define R0900_P1_EQUAQ7 0xf47d -#define F0900_P1_EQUA_ACCQ7 0xf47d01ff +#define R0900_P1_EQUAQ7 0xf47d +#define EQUAQ7 REGx(R0900_P1_EQUAQ7) +#define F0900_P1_EQUA_ACCQ7 0xf47d01ff /*P1_EQUAI8*/ -#define R0900_P1_EQUAI8 0xf47e -#define F0900_P1_EQUA_ACCI8 0xf47e01ff +#define R0900_P1_EQUAI8 0xf47e +#define EQUAI8 REGx(R0900_P1_EQUAI8) +#define F0900_P1_EQUA_ACCI8 0xf47e01ff /*P1_EQUAQ8*/ -#define R0900_P1_EQUAQ8 0xf47f -#define F0900_P1_EQUA_ACCQ8 0xf47f01ff +#define R0900_P1_EQUAQ8 0xf47f +#define EQUAQ8 REGx(R0900_P1_EQUAQ8) +#define F0900_P1_EQUA_ACCQ8 0xf47f01ff /*P1_NNOSDATAT1*/ -#define R0900_P1_NNOSDATAT1 0xf480 -#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff +#define R0900_P1_NNOSDATAT1 0xf480 +#define NNOSDATAT1 REGx(R0900_P1_NNOSDATAT1) +#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff +#define NOSDATAT_NORMED1 FLDx(F0900_P1_NOSDATAT_NORMED1) /*P1_NNOSDATAT0*/ -#define R0900_P1_NNOSDATAT0 0xf481 -#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff +#define R0900_P1_NNOSDATAT0 0xf481 +#define NNOSDATAT0 REGx(R0900_P1_NNOSDATAT0) +#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff +#define NOSDATAT_NORMED0 FLDx(F0900_P1_NOSDATAT_NORMED0) /*P1_NNOSDATA1*/ -#define R0900_P1_NNOSDATA1 0xf482 -#define F0900_P1_NOSDATA_NORMED1 0xf48200ff +#define R0900_P1_NNOSDATA1 0xf482 +#define NNOSDATA1 REGx(R0900_P1_NNOSDATA1) +#define F0900_P1_NOSDATA_NORMED1 0xf48200ff /*P1_NNOSDATA0*/ -#define R0900_P1_NNOSDATA0 0xf483 -#define F0900_P1_NOSDATA_NORMED0 0xf48300ff +#define R0900_P1_NNOSDATA0 0xf483 +#define NNOSDATA0 REGx(R0900_P1_NNOSDATA0) +#define F0900_P1_NOSDATA_NORMED0 0xf48300ff /*P1_NNOSPLHT1*/ -#define R0900_P1_NNOSPLHT1 0xf484 -#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff +#define R0900_P1_NNOSPLHT1 0xf484 +#define NNOSPLHT1 REGx(R0900_P1_NNOSPLHT1) +#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff +#define NOSPLHT_NORMED1 FLDx(F0900_P1_NOSPLHT_NORMED1) /*P1_NNOSPLHT0*/ -#define R0900_P1_NNOSPLHT0 0xf485 -#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff +#define R0900_P1_NNOSPLHT0 0xf485 +#define NNOSPLHT0 REGx(R0900_P1_NNOSPLHT0) +#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff +#define NOSPLHT_NORMED0 FLDx(F0900_P1_NOSPLHT_NORMED0) /*P1_NNOSPLH1*/ -#define R0900_P1_NNOSPLH1 0xf486 -#define F0900_P1_NOSPLH_NORMED1 0xf48600ff +#define R0900_P1_NNOSPLH1 0xf486 +#define NNOSPLH1 REGx(R0900_P1_NNOSPLH1) +#define F0900_P1_NOSPLH_NORMED1 0xf48600ff /*P1_NNOSPLH0*/ -#define R0900_P1_NNOSPLH0 0xf487 -#define F0900_P1_NOSPLH_NORMED0 0xf48700ff +#define R0900_P1_NNOSPLH0 0xf487 +#define NNOSPLH0 REGx(R0900_P1_NNOSPLH0) +#define F0900_P1_NOSPLH_NORMED0 0xf48700ff /*P1_NOSDATAT1*/ -#define R0900_P1_NOSDATAT1 0xf488 -#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff +#define R0900_P1_NOSDATAT1 0xf488 +#define NOSDATAT1 REGx(R0900_P1_NOSDATAT1) +#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff /*P1_NOSDATAT0*/ -#define R0900_P1_NOSDATAT0 0xf489 -#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff +#define R0900_P1_NOSDATAT0 0xf489 +#define NOSDATAT0 REGx(R0900_P1_NOSDATAT0) +#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff /*P1_NOSDATA1*/ -#define R0900_P1_NOSDATA1 0xf48a -#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff +#define R0900_P1_NOSDATA1 0xf48a +#define NOSDATA1 REGx(R0900_P1_NOSDATA1) +#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff /*P1_NOSDATA0*/ -#define R0900_P1_NOSDATA0 0xf48b -#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff +#define R0900_P1_NOSDATA0 0xf48b +#define NOSDATA0 REGx(R0900_P1_NOSDATA0) +#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff /*P1_NOSPLHT1*/ -#define R0900_P1_NOSPLHT1 0xf48c -#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff +#define R0900_P1_NOSPLHT1 0xf48c +#define NOSPLHT1 REGx(R0900_P1_NOSPLHT1) +#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff /*P1_NOSPLHT0*/ -#define R0900_P1_NOSPLHT0 0xf48d -#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff +#define R0900_P1_NOSPLHT0 0xf48d +#define NOSPLHT0 REGx(R0900_P1_NOSPLHT0) +#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff /*P1_NOSPLH1*/ -#define R0900_P1_NOSPLH1 0xf48e -#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff +#define R0900_P1_NOSPLH1 0xf48e +#define NOSPLH1 REGx(R0900_P1_NOSPLH1) +#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff /*P1_NOSPLH0*/ -#define R0900_P1_NOSPLH0 0xf48f -#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff +#define R0900_P1_NOSPLH0 0xf48f +#define NOSPLH0 REGx(R0900_P1_NOSPLH0) +#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff /*P1_CAR2CFG*/ -#define R0900_P1_CAR2CFG 0xf490 -#define F0900_P1_DESCRAMB_OFF 0xf4900080 -#define F0900_P1_PN4_SELECT 0xf4900040 -#define F0900_P1_CFR2_STOPDVBS1 0xf4900020 -#define F0900_P1_STOP_CFR2UPDATE 0xf4900010 -#define F0900_P1_STOP_NCO2UPDATE 0xf4900008 -#define F0900_P1_ROTA2ON 0xf4900004 -#define F0900_P1_PH_DET_ALGO2 0xf4900003 - -/*P1_ACLC2*/ -#define R0900_P1_ACLC2 0xf491 -#define F0900_P1_CAR2_PUNCT_ADERAT 0xf4910040 -#define F0900_P1_CAR2_ALPHA_MANT 0xf4910030 -#define F0900_P1_CAR2_ALPHA_EXP 0xf491000f - -/*P1_BCLC2*/ -#define R0900_P1_BCLC2 0xf492 -#define F0900_P1_DVBS2_NIP 0xf4920080 -#define F0900_P1_CAR2_PUNCT_BDERAT 0xf4920040 -#define F0900_P1_CAR2_BETA_MANT 0xf4920030 -#define F0900_P1_CAR2_BETA_EXP 0xf492000f +#define R0900_P1_CAR2CFG 0xf490 +#define CAR2CFG REGx(R0900_P1_CAR2CFG) +#define F0900_P1_CARRIER3_DISABLE 0xf4900040 +#define F0900_P1_ROTA2ON 0xf4900004 +#define F0900_P1_PH_DET_ALGO2 0xf4900003 + +/*P1_CFR2CFR1*/ +#define R0900_P1_CFR2CFR1 0xf491 +#define CFR2CFR1 REGx(R0900_P1_CFR2CFR1) +#define F0900_P1_CFR2TOCFR1_DVBS1 0xf49100c0 +#define F0900_P1_EN_S2CAR2CENTER 0xf4910020 +#define F0900_P1_DIS_BCHERRCFR2 0xf4910010 +#define F0900_P1_CFR2TOCFR1_BETA 0xf4910007 /*P1_CFR22*/ -#define R0900_P1_CFR22 0xf493 -#define F0900_P1_CAR2_FREQ2 0xf49301ff +#define R0900_P1_CFR22 0xf493 +#define CFR22 REGx(R0900_P1_CFR22) +#define F0900_P1_CAR2_FREQ2 0xf49301ff /*P1_CFR21*/ -#define R0900_P1_CFR21 0xf494 -#define F0900_P1_CAR2_FREQ1 0xf49400ff +#define R0900_P1_CFR21 0xf494 +#define CFR21 REGx(R0900_P1_CFR21) +#define F0900_P1_CAR2_FREQ1 0xf49400ff /*P1_CFR20*/ -#define R0900_P1_CFR20 0xf495 -#define F0900_P1_CAR2_FREQ0 0xf49500ff +#define R0900_P1_CFR20 0xf495 +#define CFR20 REGx(R0900_P1_CFR20) +#define F0900_P1_CAR2_FREQ0 0xf49500ff /*P1_ACLC2S2Q*/ -#define R0900_P1_ACLC2S2Q 0xf497 -#define F0900_P1_ENAB_SPSKSYMB 0xf4970080 -#define F0900_P1_CAR2S2_QADERAT 0xf4970040 -#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030 -#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f +#define R0900_P1_ACLC2S2Q 0xf497 +#define ACLC2S2Q REGx(R0900_P1_ACLC2S2Q) +#define F0900_P1_ENAB_SPSKSYMB 0xf4970080 +#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030 +#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f /*P1_ACLC2S28*/ -#define R0900_P1_ACLC2S28 0xf498 -#define F0900_P1_OLDI3Q_MODE 0xf4980080 -#define F0900_P1_CAR2S2_8ADERAT 0xf4980040 -#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030 -#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f +#define R0900_P1_ACLC2S28 0xf498 +#define ACLC2S28 REGx(R0900_P1_ACLC2S28) +#define F0900_P1_OLDI3Q_MODE 0xf4980080 +#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030 +#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f /*P1_ACLC2S216A*/ -#define R0900_P1_ACLC2S216A 0xf499 -#define F0900_P1_CAR2S2_16ADERAT 0xf4990040 -#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030 -#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f +#define R0900_P1_ACLC2S216A 0xf499 +#define ACLC2S216A REGx(R0900_P1_ACLC2S216A) +#define F0900_P1_DIS_C3STOPA2 0xf4990080 +#define F0900_P1_CAR2S2_16ADERAT 0xf4990040 +#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030 +#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f /*P1_ACLC2S232A*/ -#define R0900_P1_ACLC2S232A 0xf49a -#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040 -#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030 -#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f +#define R0900_P1_ACLC2S232A 0xf49a +#define ACLC2S232A REGx(R0900_P1_ACLC2S232A) +#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040 +#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030 +#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f /*P1_BCLC2S2Q*/ -#define R0900_P1_BCLC2S2Q 0xf49c -#define F0900_P1_DVBS2S2Q_NIP 0xf49c0080 -#define F0900_P1_CAR2S2_QBDERAT 0xf49c0040 -#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030 -#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f +#define R0900_P1_BCLC2S2Q 0xf49c +#define BCLC2S2Q REGx(R0900_P1_BCLC2S2Q) +#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030 +#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f /*P1_BCLC2S28*/ -#define R0900_P1_BCLC2S28 0xf49d -#define F0900_P1_DVBS2S28_NIP 0xf49d0080 -#define F0900_P1_CAR2S2_8BDERAT 0xf49d0040 -#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030 -#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f +#define R0900_P1_BCLC2S28 0xf49d +#define BCLC2S28 REGx(R0900_P1_BCLC2S28) +#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030 +#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f /*P1_BCLC2S216A*/ -#define R0900_P1_BCLC2S216A 0xf49e -#define F0900_P1_DVBS2S216A_NIP 0xf49e0080 -#define F0900_P1_CAR2S2_16BDERAT 0xf49e0040 -#define F0900_P1_CAR2S2_16A_BETA_M 0xf49e0030 -#define F0900_P1_CAR2S2_16A_BETA_E 0xf49e000f +#define R0900_P1_BCLC2S216A 0xf49e +#define BCLC2S216A REGx(R0900_P1_BCLC2S216A) /*P1_BCLC2S232A*/ -#define R0900_P1_BCLC2S232A 0xf49f -#define F0900_P1_DVBS2S232A_NIP 0xf49f0080 -#define F0900_P1_CAR2S2_32BDERAT 0xf49f0040 -#define F0900_P1_CAR2S2_32A_BETA_M 0xf49f0030 -#define F0900_P1_CAR2S2_32A_BETA_E 0xf49f000f +#define R0900_P1_BCLC2S232A 0xf49f +#define BCLC2S232A REGx(R0900_P1_BCLC2S232A) /*P1_PLROOT2*/ -#define R0900_P1_PLROOT2 0xf4ac -#define F0900_P1_SHORTFR_DISABLE 0xf4ac0080 -#define F0900_P1_LONGFR_DISABLE 0xf4ac0040 -#define F0900_P1_DUMMYPL_DISABLE 0xf4ac0020 -#define F0900_P1_SHORTFR_AVOID 0xf4ac0010 -#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c -#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003 +#define R0900_P1_PLROOT2 0xf4ac +#define PLROOT2 REGx(R0900_P1_PLROOT2) +#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c +#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003 /*P1_PLROOT1*/ -#define R0900_P1_PLROOT1 0xf4ad -#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff +#define R0900_P1_PLROOT1 0xf4ad +#define PLROOT1 REGx(R0900_P1_PLROOT1) +#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff /*P1_PLROOT0*/ -#define R0900_P1_PLROOT0 0xf4ae -#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff +#define R0900_P1_PLROOT0 0xf4ae +#define PLROOT0 REGx(R0900_P1_PLROOT0) +#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff /*P1_MODCODLST0*/ -#define R0900_P1_MODCODLST0 0xf4b0 -#define F0900_P1_EN_TOKEN31 0xf4b00080 -#define F0900_P1_SYNCTAG_SELECT 0xf4b00040 -#define F0900_P1_MODCODRQ_MODE 0xf4b00030 +#define R0900_P1_MODCODLST0 0xf4b0 +#define MODCODLST0 REGx(R0900_P1_MODCODLST0) /*P1_MODCODLST1*/ -#define R0900_P1_MODCODLST1 0xf4b1 -#define F0900_P1_DIS_MODCOD29 0xf4b100f0 -#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f +#define R0900_P1_MODCODLST1 0xf4b1 +#define MODCODLST1 REGx(R0900_P1_MODCODLST1) +#define F0900_P1_DIS_MODCOD29 0xf4b100f0 +#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f /*P1_MODCODLST2*/ -#define R0900_P1_MODCODLST2 0xf4b2 -#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0 -#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f +#define R0900_P1_MODCODLST2 0xf4b2 +#define MODCODLST2 REGx(R0900_P1_MODCODLST2) +#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0 +#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f /*P1_MODCODLST3*/ -#define R0900_P1_MODCODLST3 0xf4b3 -#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0 -#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f +#define R0900_P1_MODCODLST3 0xf4b3 +#define MODCODLST3 REGx(R0900_P1_MODCODLST3) +#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0 +#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f /*P1_MODCODLST4*/ -#define R0900_P1_MODCODLST4 0xf4b4 -#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0 -#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f +#define R0900_P1_MODCODLST4 0xf4b4 +#define MODCODLST4 REGx(R0900_P1_MODCODLST4) +#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0 +#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f /*P1_MODCODLST5*/ -#define R0900_P1_MODCODLST5 0xf4b5 -#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0 -#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f +#define R0900_P1_MODCODLST5 0xf4b5 +#define MODCODLST5 REGx(R0900_P1_MODCODLST5) +#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0 +#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f /*P1_MODCODLST6*/ -#define R0900_P1_MODCODLST6 0xf4b6 -#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0 -#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f +#define R0900_P1_MODCODLST6 0xf4b6 +#define MODCODLST6 REGx(R0900_P1_MODCODLST6) +#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0 +#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f /*P1_MODCODLST7*/ -#define R0900_P1_MODCODLST7 0xf4b7 -#define F0900_P1_DIS_8P_9_10 0xf4b700f0 -#define F0900_P1_DIS_8P_8_9 0xf4b7000f +#define R0900_P1_MODCODLST7 0xf4b7 +#define MODCODLST7 REGx(R0900_P1_MODCODLST7) +#define F0900_P1_DIS_8P_9_10 0xf4b700f0 +#define F0900_P1_DIS_8P_8_9 0xf4b7000f /*P1_MODCODLST8*/ -#define R0900_P1_MODCODLST8 0xf4b8 -#define F0900_P1_DIS_8P_5_6 0xf4b800f0 -#define F0900_P1_DIS_8P_3_4 0xf4b8000f +#define R0900_P1_MODCODLST8 0xf4b8 +#define MODCODLST8 REGx(R0900_P1_MODCODLST8) +#define F0900_P1_DIS_8P_5_6 0xf4b800f0 +#define F0900_P1_DIS_8P_3_4 0xf4b8000f /*P1_MODCODLST9*/ -#define R0900_P1_MODCODLST9 0xf4b9 -#define F0900_P1_DIS_8P_2_3 0xf4b900f0 -#define F0900_P1_DIS_8P_3_5 0xf4b9000f +#define R0900_P1_MODCODLST9 0xf4b9 +#define MODCODLST9 REGx(R0900_P1_MODCODLST9) +#define F0900_P1_DIS_8P_2_3 0xf4b900f0 +#define F0900_P1_DIS_8P_3_5 0xf4b9000f /*P1_MODCODLSTA*/ -#define R0900_P1_MODCODLSTA 0xf4ba -#define F0900_P1_DIS_QP_9_10 0xf4ba00f0 -#define F0900_P1_DIS_QP_8_9 0xf4ba000f +#define R0900_P1_MODCODLSTA 0xf4ba +#define MODCODLSTA REGx(R0900_P1_MODCODLSTA) +#define F0900_P1_DIS_QP_9_10 0xf4ba00f0 +#define F0900_P1_DIS_QP_8_9 0xf4ba000f /*P1_MODCODLSTB*/ -#define R0900_P1_MODCODLSTB 0xf4bb -#define F0900_P1_DIS_QP_5_6 0xf4bb00f0 -#define F0900_P1_DIS_QP_4_5 0xf4bb000f +#define R0900_P1_MODCODLSTB 0xf4bb +#define MODCODLSTB REGx(R0900_P1_MODCODLSTB) +#define F0900_P1_DIS_QP_5_6 0xf4bb00f0 +#define F0900_P1_DIS_QP_4_5 0xf4bb000f /*P1_MODCODLSTC*/ -#define R0900_P1_MODCODLSTC 0xf4bc -#define F0900_P1_DIS_QP_3_4 0xf4bc00f0 -#define F0900_P1_DIS_QP_2_3 0xf4bc000f +#define R0900_P1_MODCODLSTC 0xf4bc +#define MODCODLSTC REGx(R0900_P1_MODCODLSTC) +#define F0900_P1_DIS_QP_3_4 0xf4bc00f0 +#define F0900_P1_DIS_QP_2_3 0xf4bc000f /*P1_MODCODLSTD*/ -#define R0900_P1_MODCODLSTD 0xf4bd -#define F0900_P1_DIS_QP_3_5 0xf4bd00f0 -#define F0900_P1_DIS_QP_1_2 0xf4bd000f +#define R0900_P1_MODCODLSTD 0xf4bd +#define MODCODLSTD REGx(R0900_P1_MODCODLSTD) +#define F0900_P1_DIS_QP_3_5 0xf4bd00f0 +#define F0900_P1_DIS_QP_1_2 0xf4bd000f /*P1_MODCODLSTE*/ -#define R0900_P1_MODCODLSTE 0xf4be -#define F0900_P1_DIS_QP_2_5 0xf4be00f0 -#define F0900_P1_DIS_QP_1_3 0xf4be000f +#define R0900_P1_MODCODLSTE 0xf4be +#define MODCODLSTE REGx(R0900_P1_MODCODLSTE) +#define F0900_P1_DIS_QP_2_5 0xf4be00f0 +#define F0900_P1_DIS_QP_1_3 0xf4be000f /*P1_MODCODLSTF*/ -#define R0900_P1_MODCODLSTF 0xf4bf -#define F0900_P1_DIS_QP_1_4 0xf4bf00f0 -#define F0900_P1_DDEMOD_SET 0xf4bf0002 -#define F0900_P1_DDEMOD_MASK 0xf4bf0001 +#define R0900_P1_MODCODLSTF 0xf4bf +#define MODCODLSTF REGx(R0900_P1_MODCODLSTF) +#define F0900_P1_DIS_QP_1_4 0xf4bf00f0 + +/*P1_GAUSSR0*/ +#define R0900_P1_GAUSSR0 0xf4c0 +#define GAUSSR0 REGx(R0900_P1_GAUSSR0) +#define F0900_P1_EN_CCIMODE 0xf4c00080 +#define F0900_P1_R0_GAUSSIEN 0xf4c0007f + +/*P1_CCIR0*/ +#define R0900_P1_CCIR0 0xf4c1 +#define CCIR0 REGx(R0900_P1_CCIR0) +#define F0900_P1_CCIDETECT_PLHONLY 0xf4c10080 +#define F0900_P1_R0_CCI 0xf4c1007f + +/*P1_CCIQUANT*/ +#define R0900_P1_CCIQUANT 0xf4c2 +#define CCIQUANT REGx(R0900_P1_CCIQUANT) +#define F0900_P1_CCI_BETA 0xf4c200e0 +#define F0900_P1_CCI_QUANT 0xf4c2001f + +/*P1_CCITHRES*/ +#define R0900_P1_CCITHRES 0xf4c3 +#define CCITHRES REGx(R0900_P1_CCITHRES) +#define F0900_P1_CCI_THRESHOLD 0xf4c300ff + +/*P1_CCIACC*/ +#define R0900_P1_CCIACC 0xf4c4 +#define CCIACC REGx(R0900_P1_CCIACC) +#define F0900_P1_CCI_VALUE 0xf4c400ff /*P1_DMDRESCFG*/ -#define R0900_P1_DMDRESCFG 0xf4c6 -#define F0900_P1_DMDRES_RESET 0xf4c60080 -#define F0900_P1_DMDRES_NOISESQR 0xf4c60010 -#define F0900_P1_DMDRES_STRALL 0xf4c60008 -#define F0900_P1_DMDRES_NEWONLY 0xf4c60004 -#define F0900_P1_DMDRES_NOSTORE 0xf4c60002 -#define F0900_P1_DMDRES_AGC2MEM 0xf4c60001 +#define R0900_P1_DMDRESCFG 0xf4c6 +#define DMDRESCFG REGx(R0900_P1_DMDRESCFG) +#define F0900_P1_DMDRES_RESET 0xf4c60080 +#define F0900_P1_DMDRES_STRALL 0xf4c60008 +#define F0900_P1_DMDRES_NEWONLY 0xf4c60004 +#define F0900_P1_DMDRES_NOSTORE 0xf4c60002 /*P1_DMDRESADR*/ -#define R0900_P1_DMDRESADR 0xf4c7 -#define F0900_P1_SUSP_PREDCANAL 0xf4c70080 -#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040 -#define F0900_P1_DMDRES_MEMFULL 0xf4c70030 -#define F0900_P1_DMDRES_RESNBR 0xf4c7000f +#define R0900_P1_DMDRESADR 0xf4c7 +#define DMDRESADR REGx(R0900_P1_DMDRESADR) +#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040 +#define F0900_P1_DMDRES_MEMFULL 0xf4c70030 +#define F0900_P1_DMDRES_RESNBR 0xf4c7000f /*P1_DMDRESDATA7*/ -#define R0900_P1_DMDRESDATA7 0xf4c8 -#define F0900_P1_DMDRES_DATA7 0xf4c800ff +#define R0900_P1_DMDRESDATA7 0xf4c8 +#define F0900_P1_DMDRES_DATA7 0xf4c800ff /*P1_DMDRESDATA6*/ -#define R0900_P1_DMDRESDATA6 0xf4c9 -#define F0900_P1_DMDRES_DATA6 0xf4c900ff +#define R0900_P1_DMDRESDATA6 0xf4c9 +#define F0900_P1_DMDRES_DATA6 0xf4c900ff /*P1_DMDRESDATA5*/ -#define R0900_P1_DMDRESDATA5 0xf4ca -#define F0900_P1_DMDRES_DATA5 0xf4ca00ff +#define R0900_P1_DMDRESDATA5 0xf4ca +#define F0900_P1_DMDRES_DATA5 0xf4ca00ff /*P1_DMDRESDATA4*/ -#define R0900_P1_DMDRESDATA4 0xf4cb -#define F0900_P1_DMDRES_DATA4 0xf4cb00ff +#define R0900_P1_DMDRESDATA4 0xf4cb +#define F0900_P1_DMDRES_DATA4 0xf4cb00ff /*P1_DMDRESDATA3*/ -#define R0900_P1_DMDRESDATA3 0xf4cc -#define F0900_P1_DMDRES_DATA3 0xf4cc00ff +#define R0900_P1_DMDRESDATA3 0xf4cc +#define F0900_P1_DMDRES_DATA3 0xf4cc00ff /*P1_DMDRESDATA2*/ -#define R0900_P1_DMDRESDATA2 0xf4cd -#define F0900_P1_DMDRES_DATA2 0xf4cd00ff +#define R0900_P1_DMDRESDATA2 0xf4cd +#define F0900_P1_DMDRES_DATA2 0xf4cd00ff /*P1_DMDRESDATA1*/ -#define R0900_P1_DMDRESDATA1 0xf4ce -#define F0900_P1_DMDRES_DATA1 0xf4ce00ff +#define R0900_P1_DMDRESDATA1 0xf4ce +#define F0900_P1_DMDRES_DATA1 0xf4ce00ff /*P1_DMDRESDATA0*/ -#define R0900_P1_DMDRESDATA0 0xf4cf -#define F0900_P1_DMDRES_DATA0 0xf4cf00ff +#define R0900_P1_DMDRESDATA0 0xf4cf +#define F0900_P1_DMDRES_DATA0 0xf4cf00ff /*P1_FFEI1*/ -#define R0900_P1_FFEI1 0xf4d0 -#define F0900_P1_FFE_ACCI1 0xf4d001ff +#define R0900_P1_FFEI1 0xf4d0 +#define FFEI1 REGx(R0900_P1_FFEI1) +#define F0900_P1_FFE_ACCI1 0xf4d001ff /*P1_FFEQ1*/ -#define R0900_P1_FFEQ1 0xf4d1 -#define F0900_P1_FFE_ACCQ1 0xf4d101ff +#define R0900_P1_FFEQ1 0xf4d1 +#define FFEQ1 REGx(R0900_P1_FFEQ1) +#define F0900_P1_FFE_ACCQ1 0xf4d101ff /*P1_FFEI2*/ -#define R0900_P1_FFEI2 0xf4d2 -#define F0900_P1_FFE_ACCI2 0xf4d201ff +#define R0900_P1_FFEI2 0xf4d2 +#define FFEI2 REGx(R0900_P1_FFEI2) +#define F0900_P1_FFE_ACCI2 0xf4d201ff /*P1_FFEQ2*/ -#define R0900_P1_FFEQ2 0xf4d3 -#define F0900_P1_FFE_ACCQ2 0xf4d301ff +#define R0900_P1_FFEQ2 0xf4d3 +#define FFEQ2 REGx(R0900_P1_FFEQ2) +#define F0900_P1_FFE_ACCQ2 0xf4d301ff /*P1_FFEI3*/ -#define R0900_P1_FFEI3 0xf4d4 -#define F0900_P1_FFE_ACCI3 0xf4d401ff +#define R0900_P1_FFEI3 0xf4d4 +#define FFEI3 REGx(R0900_P1_FFEI3) +#define F0900_P1_FFE_ACCI3 0xf4d401ff /*P1_FFEQ3*/ -#define R0900_P1_FFEQ3 0xf4d5 -#define F0900_P1_FFE_ACCQ3 0xf4d501ff +#define R0900_P1_FFEQ3 0xf4d5 +#define FFEQ3 REGx(R0900_P1_FFEQ3) +#define F0900_P1_FFE_ACCQ3 0xf4d501ff /*P1_FFEI4*/ -#define R0900_P1_FFEI4 0xf4d6 -#define F0900_P1_FFE_ACCI4 0xf4d601ff +#define R0900_P1_FFEI4 0xf4d6 +#define FFEI4 REGx(R0900_P1_FFEI4) +#define F0900_P1_FFE_ACCI4 0xf4d601ff /*P1_FFEQ4*/ -#define R0900_P1_FFEQ4 0xf4d7 -#define F0900_P1_FFE_ACCQ4 0xf4d701ff +#define R0900_P1_FFEQ4 0xf4d7 +#define FFEQ4 REGx(R0900_P1_FFEQ4) +#define F0900_P1_FFE_ACCQ4 0xf4d701ff /*P1_FFECFG*/ -#define R0900_P1_FFECFG 0xf4d8 -#define F0900_P1_EQUALFFE_ON 0xf4d80040 -#define F0900_P1_EQUAL_USEDSYMB 0xf4d80030 -#define F0900_P1_MU_EQUALFFE 0xf4d80007 +#define R0900_P1_FFECFG 0xf4d8 +#define FFECFG REGx(R0900_P1_FFECFG) +#define F0900_P1_EQUALFFE_ON 0xf4d80040 +#define F0900_P1_MU_EQUALFFE 0xf4d80007 /*P1_TNRCFG*/ -#define R0900_P1_TNRCFG 0xf4e0 -#define F0900_P1_TUN_ACKFAIL 0xf4e00080 -#define F0900_P1_TUN_TYPE 0xf4e00070 -#define F0900_P1_TUN_SECSTOP 0xf4e00008 -#define F0900_P1_TUN_VCOSRCH 0xf4e00004 -#define F0900_P1_TUN_MADDRESS 0xf4e00003 +#define R0900_P1_TNRCFG 0xf4e0 +#define TNRCFG REGx(R0900_P1_TNRCFG) +#define F0900_P1_TUN_ACKFAIL 0xf4e00080 +#define F0900_P1_TUN_TYPE 0xf4e00070 +#define F0900_P1_TUN_SECSTOP 0xf4e00008 +#define F0900_P1_TUN_VCOSRCH 0xf4e00004 +#define F0900_P1_TUN_MADDRESS 0xf4e00003 /*P1_TNRCFG2*/ -#define R0900_P1_TNRCFG2 0xf4e1 -#define F0900_P1_TUN_IQSWAP 0xf4e10080 -#define F0900_P1_STB6110_STEP2MHZ 0xf4e10040 -#define F0900_P1_STB6120_DBLI2C 0xf4e10020 -#define F0900_P1_DIS_FCCK 0xf4e10010 -#define F0900_P1_DIS_LPEN 0xf4e10008 -#define F0900_P1_DIS_BWCALC 0xf4e10004 -#define F0900_P1_SHORT_WAITSTATES 0xf4e10002 -#define F0900_P1_DIS_2BWAGC1 0xf4e10001 +#define R0900_P1_TNRCFG2 0xf4e1 +#define TNRCFG2 REGx(R0900_P1_TNRCFG2) +#define F0900_P1_TUN_IQSWAP 0xf4e10080 +#define F0900_P1_DIS_BWCALC 0xf4e10004 +#define F0900_P1_SHORT_WAITSTATES 0xf4e10002 /*P1_TNRXTAL*/ -#define R0900_P1_TNRXTAL 0xf4e4 -#define F0900_P1_TUN_MCLKDECIMAL 0xf4e400e0 -#define F0900_P1_TUN_XTALFREQ 0xf4e4001f +#define R0900_P1_TNRXTAL 0xf4e4 +#define TNRXTAL REGx(R0900_P1_TNRXTAL) +#define F0900_P1_TUN_XTALFREQ 0xf4e4001f /*P1_TNRSTEPS*/ -#define R0900_P1_TNRSTEPS 0xf4e7 -#define F0900_P1_TUNER_BW1P6 0xf4e70080 -#define F0900_P1_BWINC_OFFSET 0xf4e70070 -#define F0900_P1_SOFTSTEP_RNG 0xf4e70008 -#define F0900_P1_TUN_BWOFFSET 0xf4e70107 +#define R0900_P1_TNRSTEPS 0xf4e7 +#define TNRSTEPS REGx(R0900_P1_TNRSTEPS) +#define F0900_P1_TUNER_BW0P125 0xf4e70080 +#define F0900_P1_BWINC_OFFSET 0xf4e70170 +#define F0900_P1_SOFTSTEP_RNG 0xf4e70008 +#define F0900_P1_TUN_BWOFFSET 0xf4e70007 /*P1_TNRGAIN*/ -#define R0900_P1_TNRGAIN 0xf4e8 -#define F0900_P1_TUN_KDIVEN 0xf4e800c0 -#define F0900_P1_STB6X00_OCK 0xf4e80030 -#define F0900_P1_TUN_GAIN 0xf4e8000f +#define R0900_P1_TNRGAIN 0xf4e8 +#define TNRGAIN REGx(R0900_P1_TNRGAIN) +#define F0900_P1_TUN_KDIVEN 0xf4e800c0 +#define F0900_P1_STB6X00_OCK 0xf4e80030 +#define F0900_P1_TUN_GAIN 0xf4e8000f /*P1_TNRRF1*/ -#define R0900_P1_TNRRF1 0xf4e9 -#define F0900_P1_TUN_RFFREQ2 0xf4e900ff +#define R0900_P1_TNRRF1 0xf4e9 +#define TNRRF1 REGx(R0900_P1_TNRRF1) +#define F0900_P1_TUN_RFFREQ2 0xf4e900ff /*P1_TNRRF0*/ -#define R0900_P1_TNRRF0 0xf4ea -#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff +#define R0900_P1_TNRRF0 0xf4ea +#define TNRRF0 REGx(R0900_P1_TNRRF0) +#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff /*P1_TNRBW*/ -#define R0900_P1_TNRBW 0xf4eb -#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0 -#define F0900_P1_TUN_BW 0xf4eb003f +#define R0900_P1_TNRBW 0xf4eb +#define TNRBW REGx(R0900_P1_TNRBW) +#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0 +#define F0900_P1_TUN_BW 0xf4eb003f /*P1_TNRADJ*/ -#define R0900_P1_TNRADJ 0xf4ec -#define F0900_P1_STB61X0_RCLK 0xf4ec0080 -#define F0900_P1_STB61X0_CALTIME 0xf4ec0040 -#define F0900_P1_STB6X00_DLB 0xf4ec0038 -#define F0900_P1_STB6000_FCL 0xf4ec0007 +#define R0900_P1_TNRADJ 0xf4ec +#define TNRADJ REGx(R0900_P1_TNRADJ) +#define F0900_P1_STB61X0_CALTIME 0xf4ec0040 /*P1_TNRCTL2*/ -#define R0900_P1_TNRCTL2 0xf4ed -#define F0900_P1_STB61X0_LCP1_RCCKOFF 0xf4ed0080 -#define F0900_P1_STB61X0_LCP0 0xf4ed0040 -#define F0900_P1_STB61X0_XTOUT_RFOUTS 0xf4ed0020 -#define F0900_P1_STB61X0_XTON_MCKDV 0xf4ed0010 -#define F0900_P1_STB61X0_CALOFF_DCOFF 0xf4ed0008 -#define F0900_P1_STB6110_LPT 0xf4ed0004 -#define F0900_P1_STB6110_RX 0xf4ed0002 -#define F0900_P1_STB6110_SYN 0xf4ed0001 +#define R0900_P1_TNRCTL2 0xf4ed +#define TNRCTL2 REGx(R0900_P1_TNRCTL2) +#define F0900_P1_STB61X0_RCCKOFF 0xf4ed0080 +#define F0900_P1_STB61X0_ICP_SDOFF 0xf4ed0040 +#define F0900_P1_STB61X0_DCLOOPOFF 0xf4ed0020 +#define F0900_P1_STB61X0_REFOUTSEL 0xf4ed0010 +#define F0900_P1_STB61X0_CALOFF 0xf4ed0008 +#define F0900_P1_STB6XX0_LPT_BEN 0xf4ed0004 +#define F0900_P1_STB6XX0_RX_OSCP 0xf4ed0002 +#define F0900_P1_STB6XX0_SYN 0xf4ed0001 /*P1_TNRCFG3*/ -#define R0900_P1_TNRCFG3 0xf4ee -#define F0900_P1_STB6120_DISCTRL1 0xf4ee0080 -#define F0900_P1_STB6120_INVORDER 0xf4ee0040 -#define F0900_P1_STB6120_ENCTRL6 0xf4ee0020 -#define F0900_P1_TUN_PLLFREQ 0xf4ee001c -#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003 +#define R0900_P1_TNRCFG3 0xf4ee +#define TNRCFG3 REGx(R0900_P1_TNRCFG3) +#define F0900_P1_TUN_PLLFREQ 0xf4ee001c +#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003 /*P1_TNRLAUNCH*/ -#define R0900_P1_TNRLAUNCH 0xf4f0 +#define R0900_P1_TNRLAUNCH 0xf4f0 +#define TNRLAUNCH REGx(R0900_P1_TNRLAUNCH) /*P1_TNRLD*/ -#define R0900_P1_TNRLD 0xf4f0 -#define F0900_P1_TUNLD_VCOING 0xf4f00080 -#define F0900_P1_TUN_REG1FAIL 0xf4f00040 -#define F0900_P1_TUN_REG2FAIL 0xf4f00020 -#define F0900_P1_TUN_REG3FAIL 0xf4f00010 -#define F0900_P1_TUN_REG4FAIL 0xf4f00008 -#define F0900_P1_TUN_REG5FAIL 0xf4f00004 -#define F0900_P1_TUN_BWING 0xf4f00002 -#define F0900_P1_TUN_LOCKED 0xf4f00001 +#define R0900_P1_TNRLD 0xf4f0 +#define TNRLD REGx(R0900_P1_TNRLD) +#define F0900_P1_TUNLD_VCOING 0xf4f00080 +#define F0900_P1_TUN_REG1FAIL 0xf4f00040 +#define F0900_P1_TUN_REG2FAIL 0xf4f00020 +#define F0900_P1_TUN_REG3FAIL 0xf4f00010 +#define F0900_P1_TUN_REG4FAIL 0xf4f00008 +#define F0900_P1_TUN_REG5FAIL 0xf4f00004 +#define F0900_P1_TUN_BWING 0xf4f00002 +#define F0900_P1_TUN_LOCKED 0xf4f00001 /*P1_TNROBSL*/ -#define R0900_P1_TNROBSL 0xf4f6 -#define F0900_P1_TUN_I2CABORTED 0xf4f60080 -#define F0900_P1_TUN_LPEN 0xf4f60040 -#define F0900_P1_TUN_FCCK 0xf4f60020 -#define F0900_P1_TUN_I2CLOCKED 0xf4f60010 -#define F0900_P1_TUN_PROGDONE 0xf4f6000c -#define F0900_P1_TUN_RFRESTE1 0xf4f60003 +#define R0900_P1_TNROBSL 0xf4f6 +#define TNROBSL REGx(R0900_P1_TNROBSL) +#define F0900_P1_TUN_I2CABORTED 0xf4f60080 +#define F0900_P1_TUN_LPEN 0xf4f60040 +#define F0900_P1_TUN_FCCK 0xf4f60020 +#define F0900_P1_TUN_I2CLOCKED 0xf4f60010 +#define F0900_P1_TUN_PROGDONE 0xf4f6000c +#define F0900_P1_TUN_RFRESTE1 0xf4f60003 /*P1_TNRRESTE*/ -#define R0900_P1_TNRRESTE 0xf4f7 -#define F0900_P1_TUN_RFRESTE0 0xf4f700ff +#define R0900_P1_TNRRESTE 0xf4f7 +#define TNRRESTE REGx(R0900_P1_TNRRESTE) +#define F0900_P1_TUN_RFRESTE0 0xf4f700ff /*P1_SMAPCOEF7*/ -#define R0900_P1_SMAPCOEF7 0xf500 -#define F0900_P1_DIS_QSCALE 0xf5000080 -#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f +#define R0900_P1_SMAPCOEF7 0xf500 +#define SMAPCOEF7 REGx(R0900_P1_SMAPCOEF7) +#define F0900_P1_DIS_QSCALE 0xf5000080 +#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f /*P1_SMAPCOEF6*/ -#define R0900_P1_SMAPCOEF6 0xf501 -#define F0900_P1_DIS_NEWSCALE 0xf5010008 -#define F0900_P1_ADJ_8PSKLLR1 0xf5010004 -#define F0900_P1_OLD_8PSKLLR1 0xf5010002 -#define F0900_P1_DIS_AB8PSK 0xf5010001 +#define R0900_P1_SMAPCOEF6 0xf501 +#define SMAPCOEF6 REGx(R0900_P1_SMAPCOEF6) +#define F0900_P1_ADJ_8PSKLLR1 0xf5010004 +#define F0900_P1_OLD_8PSKLLR1 0xf5010002 +#define F0900_P1_DIS_AB8PSK 0xf5010001 /*P1_SMAPCOEF5*/ -#define R0900_P1_SMAPCOEF5 0xf502 -#define F0900_P1_DIS_8SCALE 0xf5020080 -#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f +#define R0900_P1_SMAPCOEF5 0xf502 +#define SMAPCOEF5 REGx(R0900_P1_SMAPCOEF5) +#define F0900_P1_DIS_8SCALE 0xf5020080 +#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f + +/*P1_NCO2MAX1*/ +#define R0900_P1_NCO2MAX1 0xf514 +#define NCO2MAX1 REGx(R0900_P1_NCO2MAX1) +#define F0900_P1_TETA2_MAXVABS1 0xf51400ff + +/*P1_NCO2MAX0*/ +#define R0900_P1_NCO2MAX0 0xf515 +#define NCO2MAX0 REGx(R0900_P1_NCO2MAX0) +#define F0900_P1_TETA2_MAXVABS0 0xf51500ff + +/*P1_NCO2FR1*/ +#define R0900_P1_NCO2FR1 0xf516 +#define NCO2FR1 REGx(R0900_P1_NCO2FR1) +#define F0900_P1_NCO2FINAL_ANGLE1 0xf51600ff + +/*P1_NCO2FR0*/ +#define R0900_P1_NCO2FR0 0xf517 +#define NCO2FR0 REGx(R0900_P1_NCO2FR0) +#define F0900_P1_NCO2FINAL_ANGLE0 0xf51700ff + +/*P1_CFR2AVRGE1*/ +#define R0900_P1_CFR2AVRGE1 0xf518 +#define CFR2AVRGE1 REGx(R0900_P1_CFR2AVRGE1) +#define F0900_P1_I2C_CFR2AVERAGE1 0xf51800ff + +/*P1_CFR2AVRGE0*/ +#define R0900_P1_CFR2AVRGE0 0xf519 +#define CFR2AVRGE0 REGx(R0900_P1_CFR2AVRGE0) +#define F0900_P1_I2C_CFR2AVERAGE0 0xf51900ff /*P1_DMDPLHSTAT*/ -#define R0900_P1_DMDPLHSTAT 0xf520 -#define F0900_P1_PLH_STATISTIC 0xf52000ff +#define R0900_P1_DMDPLHSTAT 0xf520 +#define DMDPLHSTAT REGx(R0900_P1_DMDPLHSTAT) +#define F0900_P1_PLH_STATISTIC 0xf52000ff /*P1_LOCKTIME3*/ -#define R0900_P1_LOCKTIME3 0xf522 -#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff +#define R0900_P1_LOCKTIME3 0xf522 +#define LOCKTIME3 REGx(R0900_P1_LOCKTIME3) +#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff /*P1_LOCKTIME2*/ -#define R0900_P1_LOCKTIME2 0xf523 -#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff +#define R0900_P1_LOCKTIME2 0xf523 +#define LOCKTIME2 REGx(R0900_P1_LOCKTIME2) +#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff /*P1_LOCKTIME1*/ -#define R0900_P1_LOCKTIME1 0xf524 -#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff +#define R0900_P1_LOCKTIME1 0xf524 +#define LOCKTIME1 REGx(R0900_P1_LOCKTIME1) +#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff /*P1_LOCKTIME0*/ -#define R0900_P1_LOCKTIME0 0xf525 -#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff +#define R0900_P1_LOCKTIME0 0xf525 +#define LOCKTIME0 REGx(R0900_P1_LOCKTIME0) +#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff /*P1_VITSCALE*/ -#define R0900_P1_VITSCALE 0xf532 -#define F0900_P1_NVTH_NOSRANGE 0xf5320080 -#define F0900_P1_VERROR_MAXMODE 0xf5320040 -#define F0900_P1_KDIV_MODE 0xf5320030 -#define F0900_P1_NSLOWSN_LOCKED 0xf5320008 -#define F0900_P1_DELOCK_PRFLOSS 0xf5320004 -#define F0900_P1_DIS_RSFLOCK 0xf5320002 +#define R0900_P1_VITSCALE 0xf532 +#define VITSCALE REGx(R0900_P1_VITSCALE) +#define F0900_P1_NVTH_NOSRANGE 0xf5320080 +#define F0900_P1_VERROR_MAXMODE 0xf5320040 +#define F0900_P1_NSLOWSN_LOCKED 0xf5320008 +#define F0900_P1_DIS_RSFLOCK 0xf5320002 /*P1_FECM*/ -#define R0900_P1_FECM 0xf533 -#define F0900_P1_DSS_DVB 0xf5330080 -#define F0900_P1_DEMOD_BYPASS 0xf5330040 -#define F0900_P1_CMP_SLOWMODE 0xf5330020 -#define F0900_P1_DSS_SRCH 0xf5330010 -#define F0900_P1_DIFF_MODEVIT 0xf5330004 -#define F0900_P1_SYNCVIT 0xf5330002 -#define F0900_P1_IQINV 0xf5330001 +#define R0900_P1_FECM 0xf533 +#define FECM REGx(R0900_P1_FECM) +#define F0900_P1_DSS_DVB 0xf5330080 +#define DSS_DVB FLDx(F0900_P1_DSS_DVB) +#define F0900_P1_DSS_SRCH 0xf5330010 +#define F0900_P1_SYNCVIT 0xf5330002 +#define F0900_P1_IQINV 0xf5330001 +#define IQINV FLDx(F0900_P1_IQINV) /*P1_VTH12*/ -#define R0900_P1_VTH12 0xf534 -#define F0900_P1_VTH12 0xf53400ff +#define R0900_P1_VTH12 0xf534 +#define VTH12 REGx(R0900_P1_VTH12) +#define F0900_P1_VTH12 0xf53400ff /*P1_VTH23*/ -#define R0900_P1_VTH23 0xf535 -#define F0900_P1_VTH23 0xf53500ff +#define R0900_P1_VTH23 0xf535 +#define VTH23 REGx(R0900_P1_VTH23) +#define F0900_P1_VTH23 0xf53500ff /*P1_VTH34*/ -#define R0900_P1_VTH34 0xf536 -#define F0900_P1_VTH34 0xf53600ff +#define R0900_P1_VTH34 0xf536 +#define VTH34 REGx(R0900_P1_VTH34) +#define F0900_P1_VTH34 0xf53600ff /*P1_VTH56*/ -#define R0900_P1_VTH56 0xf537 -#define F0900_P1_VTH56 0xf53700ff +#define R0900_P1_VTH56 0xf537 +#define VTH56 REGx(R0900_P1_VTH56) +#define F0900_P1_VTH56 0xf53700ff /*P1_VTH67*/ -#define R0900_P1_VTH67 0xf538 -#define F0900_P1_VTH67 0xf53800ff +#define R0900_P1_VTH67 0xf538 +#define VTH67 REGx(R0900_P1_VTH67) +#define F0900_P1_VTH67 0xf53800ff /*P1_VTH78*/ -#define R0900_P1_VTH78 0xf539 -#define F0900_P1_VTH78 0xf53900ff +#define R0900_P1_VTH78 0xf539 +#define VTH78 REGx(R0900_P1_VTH78) +#define F0900_P1_VTH78 0xf53900ff /*P1_VITCURPUN*/ -#define R0900_P1_VITCURPUN 0xf53a -#define F0900_P1_VIT_MAPPING 0xf53a00e0 -#define F0900_P1_VIT_CURPUN 0xf53a001f +#define R0900_P1_VITCURPUN 0xf53a +#define VITCURPUN REGx(R0900_P1_VITCURPUN) +#define F0900_P1_VIT_CURPUN 0xf53a001f +#define VIT_CURPUN FLDx(F0900_P1_VIT_CURPUN) /*P1_VERROR*/ -#define R0900_P1_VERROR 0xf53b -#define F0900_P1_REGERR_VIT 0xf53b00ff +#define R0900_P1_VERROR 0xf53b +#define VERROR REGx(R0900_P1_VERROR) +#define F0900_P1_REGERR_VIT 0xf53b00ff /*P1_PRVIT*/ -#define R0900_P1_PRVIT 0xf53c -#define F0900_P1_DIS_VTHLOCK 0xf53c0040 -#define F0900_P1_E7_8VIT 0xf53c0020 -#define F0900_P1_E6_7VIT 0xf53c0010 -#define F0900_P1_E5_6VIT 0xf53c0008 -#define F0900_P1_E3_4VIT 0xf53c0004 -#define F0900_P1_E2_3VIT 0xf53c0002 -#define F0900_P1_E1_2VIT 0xf53c0001 +#define R0900_P1_PRVIT 0xf53c +#define PRVIT REGx(R0900_P1_PRVIT) +#define F0900_P1_DIS_VTHLOCK 0xf53c0040 +#define F0900_P1_E7_8VIT 0xf53c0020 +#define F0900_P1_E6_7VIT 0xf53c0010 +#define F0900_P1_E5_6VIT 0xf53c0008 +#define F0900_P1_E3_4VIT 0xf53c0004 +#define F0900_P1_E2_3VIT 0xf53c0002 +#define F0900_P1_E1_2VIT 0xf53c0001 /*P1_VAVSRVIT*/ -#define R0900_P1_VAVSRVIT 0xf53d -#define F0900_P1_AMVIT 0xf53d0080 -#define F0900_P1_FROZENVIT 0xf53d0040 -#define F0900_P1_SNVIT 0xf53d0030 -#define F0900_P1_TOVVIT 0xf53d000c -#define F0900_P1_HYPVIT 0xf53d0003 +#define R0900_P1_VAVSRVIT 0xf53d +#define VAVSRVIT REGx(R0900_P1_VAVSRVIT) +#define F0900_P1_AMVIT 0xf53d0080 +#define F0900_P1_FROZENVIT 0xf53d0040 +#define F0900_P1_SNVIT 0xf53d0030 +#define F0900_P1_TOVVIT 0xf53d000c +#define F0900_P1_HYPVIT 0xf53d0003 /*P1_VSTATUSVIT*/ -#define R0900_P1_VSTATUSVIT 0xf53e -#define F0900_P1_VITERBI_ON 0xf53e0080 -#define F0900_P1_END_LOOPVIT 0xf53e0040 -#define F0900_P1_VITERBI_DEPRF 0xf53e0020 -#define F0900_P1_PRFVIT 0xf53e0010 -#define F0900_P1_LOCKEDVIT 0xf53e0008 -#define F0900_P1_VITERBI_DELOCK 0xf53e0004 -#define F0900_P1_VIT_DEMODSEL 0xf53e0002 -#define F0900_P1_VITERBI_COMPOUT 0xf53e0001 +#define R0900_P1_VSTATUSVIT 0xf53e +#define VSTATUSVIT REGx(R0900_P1_VSTATUSVIT) +#define F0900_P1_PRFVIT 0xf53e0010 +#define PRFVIT FLDx(F0900_P1_PRFVIT) +#define F0900_P1_LOCKEDVIT 0xf53e0008 +#define LOCKEDVIT FLDx(F0900_P1_LOCKEDVIT) /*P1_VTHINUSE*/ -#define R0900_P1_VTHINUSE 0xf53f -#define F0900_P1_VIT_INUSE 0xf53f00ff +#define R0900_P1_VTHINUSE 0xf53f +#define VTHINUSE REGx(R0900_P1_VTHINUSE) +#define F0900_P1_VIT_INUSE 0xf53f00ff /*P1_KDIV12*/ -#define R0900_P1_KDIV12 0xf540 -#define F0900_P1_KDIV12_MANUAL 0xf5400080 -#define F0900_P1_K_DIVIDER_12 0xf540007f +#define R0900_P1_KDIV12 0xf540 +#define KDIV12 REGx(R0900_P1_KDIV12) +#define F0900_P1_K_DIVIDER_12 0xf540007f /*P1_KDIV23*/ -#define R0900_P1_KDIV23 0xf541 -#define F0900_P1_KDIV23_MANUAL 0xf5410080 -#define F0900_P1_K_DIVIDER_23 0xf541007f +#define R0900_P1_KDIV23 0xf541 +#define KDIV23 REGx(R0900_P1_KDIV23) +#define F0900_P1_K_DIVIDER_23 0xf541007f /*P1_KDIV34*/ -#define R0900_P1_KDIV34 0xf542 -#define F0900_P1_KDIV34_MANUAL 0xf5420080 -#define F0900_P1_K_DIVIDER_34 0xf542007f +#define R0900_P1_KDIV34 0xf542 +#define KDIV34 REGx(R0900_P1_KDIV34) +#define F0900_P1_K_DIVIDER_34 0xf542007f /*P1_KDIV56*/ -#define R0900_P1_KDIV56 0xf543 -#define F0900_P1_KDIV56_MANUAL 0xf5430080 -#define F0900_P1_K_DIVIDER_56 0xf543007f +#define R0900_P1_KDIV56 0xf543 +#define KDIV56 REGx(R0900_P1_KDIV56) +#define F0900_P1_K_DIVIDER_56 0xf543007f /*P1_KDIV67*/ -#define R0900_P1_KDIV67 0xf544 -#define F0900_P1_KDIV67_MANUAL 0xf5440080 -#define F0900_P1_K_DIVIDER_67 0xf544007f +#define R0900_P1_KDIV67 0xf544 +#define KDIV67 REGx(R0900_P1_KDIV67) +#define F0900_P1_K_DIVIDER_67 0xf544007f /*P1_KDIV78*/ -#define R0900_P1_KDIV78 0xf545 -#define F0900_P1_KDIV78_MANUAL 0xf5450080 -#define F0900_P1_K_DIVIDER_78 0xf545007f +#define R0900_P1_KDIV78 0xf545 +#define KDIV78 REGx(R0900_P1_KDIV78) +#define F0900_P1_K_DIVIDER_78 0xf545007f /*P1_PDELCTRL1*/ -#define R0900_P1_PDELCTRL1 0xf550 -#define F0900_P1_INV_MISMASK 0xf5500080 -#define F0900_P1_FORCE_ACCEPTED 0xf5500040 -#define F0900_P1_FILTER_EN 0xf5500020 -#define F0900_P1_FORCE_PKTDELINUSE 0xf5500010 -#define F0900_P1_HYSTEN 0xf5500008 -#define F0900_P1_HYSTSWRST 0xf5500004 -#define F0900_P1_EN_MIS00 0xf5500002 -#define F0900_P1_ALGOSWRST 0xf5500001 +#define R0900_P1_PDELCTRL1 0xf550 +#define PDELCTRL1 REGx(R0900_P1_PDELCTRL1) +#define F0900_P1_INV_MISMASK 0xf5500080 +#define F0900_P1_FILTER_EN 0xf5500020 +#define F0900_P1_EN_MIS00 0xf5500002 +#define F0900_P1_ALGOSWRST 0xf5500001 +#define ALGOSWRST FLDx(F0900_P1_ALGOSWRST) /*P1_PDELCTRL2*/ -#define R0900_P1_PDELCTRL2 0xf551 -#define F0900_P1_FORCE_CONTINUOUS 0xf5510080 -#define F0900_P1_RESET_UPKO_COUNT 0xf5510040 -#define F0900_P1_USER_PKTDELIN_NB 0xf5510020 -#define F0900_P1_FORCE_LOCKED 0xf5510010 -#define F0900_P1_DATA_UNBBSCRAM 0xf5510008 -#define F0900_P1_FORCE_LONGPKT 0xf5510004 -#define F0900_P1_FRAME_MODE 0xf5510002 +#define R0900_P1_PDELCTRL2 0xf551 +#define PDELCTRL2 REGx(R0900_P1_PDELCTRL2) +#define F0900_P1_RESET_UPKO_COUNT 0xf5510040 +#define RESET_UPKO_COUNT FLDx(F0900_P1_RESET_UPKO_COUNT) +#define F0900_P1_FRAME_MODE 0xf5510002 +#define F0900_P1_NOBCHERRFLG_USE 0xf5510001 /*P1_HYSTTHRESH*/ -#define R0900_P1_HYSTTHRESH 0xf554 -#define F0900_P1_UNLCK_THRESH 0xf55400f0 -#define F0900_P1_DELIN_LCK_THRESH 0xf554000f +#define R0900_P1_HYSTTHRESH 0xf554 +#define HYSTTHRESH REGx(R0900_P1_HYSTTHRESH) +#define F0900_P1_UNLCK_THRESH 0xf55400f0 +#define F0900_P1_DELIN_LCK_THRESH 0xf554000f /*P1_ISIENTRY*/ -#define R0900_P1_ISIENTRY 0xf55e -#define F0900_P1_ISI_ENTRY 0xf55e00ff +#define R0900_P1_ISIENTRY 0xf55e +#define ISIENTRY REGx(R0900_P1_ISIENTRY) +#define F0900_P1_ISI_ENTRY 0xf55e00ff /*P1_ISIBITENA*/ -#define R0900_P1_ISIBITENA 0xf55f -#define F0900_P1_ISI_BIT_EN 0xf55f00ff +#define R0900_P1_ISIBITENA 0xf55f +#define ISIBITENA REGx(R0900_P1_ISIBITENA) +#define F0900_P1_ISI_BIT_EN 0xf55f00ff /*P1_MATSTR1*/ -#define R0900_P1_MATSTR1 0xf560 -#define F0900_P1_MATYPE_CURRENT1 0xf56000ff +#define R0900_P1_MATSTR1 0xf560 +#define MATSTR1 REGx(R0900_P1_MATSTR1) +#define F0900_P1_MATYPE_CURRENT1 0xf56000ff /*P1_MATSTR0*/ -#define R0900_P1_MATSTR0 0xf561 -#define F0900_P1_MATYPE_CURRENT0 0xf56100ff +#define R0900_P1_MATSTR0 0xf561 +#define MATSTR0 REGx(R0900_P1_MATSTR0) +#define F0900_P1_MATYPE_CURRENT0 0xf56100ff /*P1_UPLSTR1*/ -#define R0900_P1_UPLSTR1 0xf562 -#define F0900_P1_UPL_CURRENT1 0xf56200ff +#define R0900_P1_UPLSTR1 0xf562 +#define UPLSTR1 REGx(R0900_P1_UPLSTR1) +#define F0900_P1_UPL_CURRENT1 0xf56200ff /*P1_UPLSTR0*/ -#define R0900_P1_UPLSTR0 0xf563 -#define F0900_P1_UPL_CURRENT0 0xf56300ff +#define R0900_P1_UPLSTR0 0xf563 +#define UPLSTR0 REGx(R0900_P1_UPLSTR0) +#define F0900_P1_UPL_CURRENT0 0xf56300ff /*P1_DFLSTR1*/ -#define R0900_P1_DFLSTR1 0xf564 -#define F0900_P1_DFL_CURRENT1 0xf56400ff +#define R0900_P1_DFLSTR1 0xf564 +#define DFLSTR1 REGx(R0900_P1_DFLSTR1) +#define F0900_P1_DFL_CURRENT1 0xf56400ff /*P1_DFLSTR0*/ -#define R0900_P1_DFLSTR0 0xf565 -#define F0900_P1_DFL_CURRENT0 0xf56500ff +#define R0900_P1_DFLSTR0 0xf565 +#define DFLSTR0 REGx(R0900_P1_DFLSTR0) +#define F0900_P1_DFL_CURRENT0 0xf56500ff /*P1_SYNCSTR*/ -#define R0900_P1_SYNCSTR 0xf566 -#define F0900_P1_SYNC_CURRENT 0xf56600ff +#define R0900_P1_SYNCSTR 0xf566 +#define SYNCSTR REGx(R0900_P1_SYNCSTR) +#define F0900_P1_SYNC_CURRENT 0xf56600ff /*P1_SYNCDSTR1*/ -#define R0900_P1_SYNCDSTR1 0xf567 -#define F0900_P1_SYNCD_CURRENT1 0xf56700ff +#define R0900_P1_SYNCDSTR1 0xf567 +#define SYNCDSTR1 REGx(R0900_P1_SYNCDSTR1) +#define F0900_P1_SYNCD_CURRENT1 0xf56700ff /*P1_SYNCDSTR0*/ -#define R0900_P1_SYNCDSTR0 0xf568 -#define F0900_P1_SYNCD_CURRENT0 0xf56800ff +#define R0900_P1_SYNCDSTR0 0xf568 +#define SYNCDSTR0 REGx(R0900_P1_SYNCDSTR0) +#define F0900_P1_SYNCD_CURRENT0 0xf56800ff /*P1_PDELSTATUS1*/ -#define R0900_P1_PDELSTATUS1 0xf569 -#define F0900_P1_PKTDELIN_DELOCK 0xf5690080 -#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040 -#define F0900_P1_CONTINUOUS_STREAM 0xf5690020 -#define F0900_P1_UNACCEPTED_STREAM 0xf5690010 -#define F0900_P1_BCH_ERROR_FLAG 0xf5690008 -#define F0900_P1_BBHCRCKO 0xf5690004 -#define F0900_P1_PKTDELIN_LOCK 0xf5690002 -#define F0900_P1_FIRST_LOCK 0xf5690001 +#define R0900_P1_PDELSTATUS1 0xf569 +#define F0900_P1_PKTDELIN_DELOCK 0xf5690080 +#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040 +#define F0900_P1_CONTINUOUS_STREAM 0xf5690020 +#define F0900_P1_UNACCEPTED_STREAM 0xf5690010 +#define F0900_P1_BCH_ERROR_FLAG 0xf5690008 +#define F0900_P1_PKTDELIN_LOCK 0xf5690002 +#define PKTDELIN_LOCK FLDx(F0900_P1_PKTDELIN_LOCK) +#define F0900_P1_FIRST_LOCK 0xf5690001 /*P1_PDELSTATUS2*/ -#define R0900_P1_PDELSTATUS2 0xf56a -#define F0900_P1_PKTDEL_DEMODSEL 0xf56a0080 -#define F0900_P1_FRAME_MODCOD 0xf56a007c -#define F0900_P1_FRAME_TYPE 0xf56a0003 +#define R0900_P1_PDELSTATUS2 0xf56a +#define F0900_P1_FRAME_MODCOD 0xf56a007c +#define F0900_P1_FRAME_TYPE 0xf56a0003 /*P1_BBFCRCKO1*/ -#define R0900_P1_BBFCRCKO1 0xf56b -#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff +#define R0900_P1_BBFCRCKO1 0xf56b +#define BBFCRCKO1 REGx(R0900_P1_BBFCRCKO1) +#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff /*P1_BBFCRCKO0*/ -#define R0900_P1_BBFCRCKO0 0xf56c -#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff +#define R0900_P1_BBFCRCKO0 0xf56c +#define BBFCRCKO0 REGx(R0900_P1_BBFCRCKO0) +#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff /*P1_UPCRCKO1*/ -#define R0900_P1_UPCRCKO1 0xf56d -#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff +#define R0900_P1_UPCRCKO1 0xf56d +#define UPCRCKO1 REGx(R0900_P1_UPCRCKO1) +#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff /*P1_UPCRCKO0*/ -#define R0900_P1_UPCRCKO0 0xf56e -#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff +#define R0900_P1_UPCRCKO0 0xf56e +#define UPCRCKO0 REGx(R0900_P1_UPCRCKO0) +#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff + +/*P1_PDELCTRL3*/ +#define R0900_P1_PDELCTRL3 0xf56f +#define PDELCTRL3 REGx(R0900_P1_PDELCTRL3) +#define F0900_P1_PKTDEL_CONTFAIL 0xf56f0080 +#define F0900_P1_NOFIFO_BCHERR 0xf56f0020 /*P1_TSSTATEM*/ -#define R0900_P1_TSSTATEM 0xf570 -#define F0900_P1_TSDIL_ON 0xf5700080 -#define F0900_P1_TSSKIPRS_ON 0xf5700040 -#define F0900_P1_TSRS_ON 0xf5700020 -#define F0900_P1_TSDESCRAMB_ON 0xf5700010 -#define F0900_P1_TSFRAME_MODE 0xf5700008 -#define F0900_P1_TS_DISABLE 0xf5700004 -#define F0900_P1_TSACM_MODE 0xf5700002 -#define F0900_P1_TSOUT_NOSYNC 0xf5700001 +#define R0900_P1_TSSTATEM 0xf570 +#define TSSTATEM REGx(R0900_P1_TSSTATEM) +#define F0900_P1_TSDIL_ON 0xf5700080 +#define F0900_P1_TSRS_ON 0xf5700020 +#define F0900_P1_TSDESCRAMB_ON 0xf5700010 +#define F0900_P1_TSFRAME_MODE 0xf5700008 +#define F0900_P1_TS_DISABLE 0xf5700004 +#define F0900_P1_TSOUT_NOSYNC 0xf5700001 /*P1_TSCFGH*/ -#define R0900_P1_TSCFGH 0xf572 -#define F0900_P1_TSFIFO_DVBCI 0xf5720080 -#define F0900_P1_TSFIFO_SERIAL 0xf5720040 -#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020 -#define F0900_P1_TSFIFO_DUTY50 0xf5720010 -#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008 -#define F0900_P1_TSFIFO_ERRMODE 0xf5720006 -#define F0900_P1_RST_HWARE 0xf5720001 +#define R0900_P1_TSCFGH 0xf572 +#define TSCFGH REGx(R0900_P1_TSCFGH) +#define F0900_P1_TSFIFO_DVBCI 0xf5720080 +#define F0900_P1_TSFIFO_SERIAL 0xf5720040 +#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020 +#define F0900_P1_TSFIFO_DUTY50 0xf5720010 +#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008 +#define F0900_P1_TSFIFO_ERRMODE 0xf5720006 +#define F0900_P1_RST_HWARE 0xf5720001 +#define RST_HWARE FLDx(F0900_P1_RST_HWARE) /*P1_TSCFGM*/ -#define R0900_P1_TSCFGM 0xf573 -#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0 -#define F0900_P1_TSFIFO_PERMDATA 0xf5730020 -#define F0900_P1_TSFIFO_NONEWSGNL 0xf5730010 -#define F0900_P1_TSFIFO_BITSPEED 0xf5730008 -#define F0900_P1_NPD_SPECDVBS2 0xf5730004 -#define F0900_P1_TSFIFO_STOPCKDIS 0xf5730002 -#define F0900_P1_TSFIFO_INVDATA 0xf5730001 +#define R0900_P1_TSCFGM 0xf573 +#define TSCFGM REGx(R0900_P1_TSCFGM) +#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0 +#define F0900_P1_TSFIFO_PERMDATA 0xf5730020 +#define F0900_P1_TSFIFO_DPUNACT 0xf5730002 +#define F0900_P1_TSFIFO_INVDATA 0xf5730001 /*P1_TSCFGL*/ -#define R0900_P1_TSCFGL 0xf574 -#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0 -#define F0900_P1_BCHERROR_MODE 0xf5740030 -#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008 -#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004 -#define F0900_P1_TSFIFO_DPUNACT 0xf5740002 -#define F0900_P1_TSFIFO_NPDOFF 0xf5740001 +#define R0900_P1_TSCFGL 0xf574 +#define TSCFGL REGx(R0900_P1_TSCFGL) +#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0 +#define F0900_P1_BCHERROR_MODE 0xf5740030 +#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008 +#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004 +#define F0900_P1_TSFIFO_BITSPEED 0xf5740003 /*P1_TSINSDELH*/ -#define R0900_P1_TSINSDELH 0xf576 -#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080 -#define F0900_P1_TSDEL_XXHEADER 0xf5760040 -#define F0900_P1_TSDEL_BBHEADER 0xf5760020 -#define F0900_P1_TSDEL_DATAFIELD 0xf5760010 -#define F0900_P1_TSINSDEL_ISCR 0xf5760008 -#define F0900_P1_TSINSDEL_NPD 0xf5760004 -#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002 -#define F0900_P1_TSINSDEL_CRC8 0xf5760001 +#define R0900_P1_TSINSDELH 0xf576 +#define TSINSDELH REGx(R0900_P1_TSINSDELH) +#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080 +#define F0900_P1_TSDEL_XXHEADER 0xf5760040 +#define F0900_P1_TSDEL_BBHEADER 0xf5760020 +#define F0900_P1_TSDEL_DATAFIELD 0xf5760010 +#define F0900_P1_TSINSDEL_ISCR 0xf5760008 +#define F0900_P1_TSINSDEL_NPD 0xf5760004 +#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002 +#define F0900_P1_TSINSDEL_CRC8 0xf5760001 + +/*P1_TSDIVN*/ +#define R0900_P1_TSDIVN 0xf579 +#define TSDIVN REGx(R0900_P1_TSDIVN) +#define F0900_P1_TSFIFO_SPEEDMODE 0xf57900c0 + +/*P1_TSCFG4*/ +#define R0900_P1_TSCFG4 0xf57a +#define TSCFG4 REGx(R0900_P1_TSCFG4) +#define F0900_P1_TSFIFO_TSSPEEDMODE 0xf57a00c0 /*P1_TSSPEED*/ -#define R0900_P1_TSSPEED 0xf580 -#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff +#define R0900_P1_TSSPEED 0xf580 +#define TSSPEED REGx(R0900_P1_TSSPEED) +#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff /*P1_TSSTATUS*/ -#define R0900_P1_TSSTATUS 0xf581 -#define F0900_P1_TSFIFO_LINEOK 0xf5810080 -#define F0900_P1_TSFIFO_ERROR 0xf5810040 -#define F0900_P1_TSFIFO_DATA7 0xf5810020 -#define F0900_P1_TSFIFO_NOSYNC 0xf5810010 -#define F0900_P1_ISCR_INITIALIZED 0xf5810008 -#define F0900_P1_ISCR_UPDATED 0xf5810004 -#define F0900_P1_SOFFIFO_UNREGUL 0xf5810002 -#define F0900_P1_DIL_READY 0xf5810001 +#define R0900_P1_TSSTATUS 0xf581 +#define TSSTATUS REGx(R0900_P1_TSSTATUS) +#define F0900_P1_TSFIFO_LINEOK 0xf5810080 +#define TSFIFO_LINEOK FLDx(F0900_P1_TSFIFO_LINEOK) +#define F0900_P1_TSFIFO_ERROR 0xf5810040 +#define F0900_P1_DIL_READY 0xf5810001 /*P1_TSSTATUS2*/ -#define R0900_P1_TSSTATUS2 0xf582 -#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080 -#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040 -#define F0900_P1_DILXX_RESET 0xf5820020 -#define F0900_P1_TSSERIAL_IMPOS 0xf5820010 -#define F0900_P1_TSFIFO_LINENOK 0xf5820008 -#define F0900_P1_BITSPEED_EVENT 0xf5820004 -#define F0900_P1_SCRAMBDETECT 0xf5820002 -#define F0900_P1_ULDTV67_FALSELOCK 0xf5820001 +#define R0900_P1_TSSTATUS2 0xf582 +#define TSSTATUS2 REGx(R0900_P1_TSSTATUS2) +#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080 +#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040 +#define F0900_P1_DILXX_RESET 0xf5820020 +#define F0900_P1_TSSERIAL_IMPOS 0xf5820010 +#define F0900_P1_SCRAMBDETECT 0xf5820002 /*P1_TSBITRATE1*/ -#define R0900_P1_TSBITRATE1 0xf583 -#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff +#define R0900_P1_TSBITRATE1 0xf583 +#define TSBITRATE1 REGx(R0900_P1_TSBITRATE1) +#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff /*P1_TSBITRATE0*/ -#define R0900_P1_TSBITRATE0 0xf584 -#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff +#define R0900_P1_TSBITRATE0 0xf584 +#define TSBITRATE0 REGx(R0900_P1_TSBITRATE0) +#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff /*P1_ERRCTRL1*/ -#define R0900_P1_ERRCTRL1 0xf598 -#define F0900_P1_ERR_SOURCE1 0xf59800f0 -#define F0900_P1_NUM_EVENT1 0xf5980007 +#define R0900_P1_ERRCTRL1 0xf598 +#define ERRCTRL1 REGx(R0900_P1_ERRCTRL1) +#define F0900_P1_ERR_SOURCE1 0xf59800f0 +#define F0900_P1_NUM_EVENT1 0xf5980007 /*P1_ERRCNT12*/ -#define R0900_P1_ERRCNT12 0xf599 -#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080 -#define F0900_P1_ERR_CNT12 0xf599007f +#define R0900_P1_ERRCNT12 0xf599 +#define ERRCNT12 REGx(R0900_P1_ERRCNT12) +#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080 +#define F0900_P1_ERR_CNT12 0xf599007f +#define ERR_CNT12 FLDx(F0900_P1_ERR_CNT12) /*P1_ERRCNT11*/ -#define R0900_P1_ERRCNT11 0xf59a -#define F0900_P1_ERR_CNT11 0xf59a00ff +#define R0900_P1_ERRCNT11 0xf59a +#define ERRCNT11 REGx(R0900_P1_ERRCNT11) +#define F0900_P1_ERR_CNT11 0xf59a00ff +#define ERR_CNT11 FLDx(F0900_P1_ERR_CNT11) /*P1_ERRCNT10*/ -#define R0900_P1_ERRCNT10 0xf59b -#define F0900_P1_ERR_CNT10 0xf59b00ff +#define R0900_P1_ERRCNT10 0xf59b +#define ERRCNT10 REGx(R0900_P1_ERRCNT10) +#define F0900_P1_ERR_CNT10 0xf59b00ff +#define ERR_CNT10 FLDx(F0900_P1_ERR_CNT10) /*P1_ERRCTRL2*/ -#define R0900_P1_ERRCTRL2 0xf59c -#define F0900_P1_ERR_SOURCE2 0xf59c00f0 -#define F0900_P1_NUM_EVENT2 0xf59c0007 +#define R0900_P1_ERRCTRL2 0xf59c +#define ERRCTRL2 REGx(R0900_P1_ERRCTRL2) +#define F0900_P1_ERR_SOURCE2 0xf59c00f0 +#define F0900_P1_NUM_EVENT2 0xf59c0007 /*P1_ERRCNT22*/ -#define R0900_P1_ERRCNT22 0xf59d -#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080 -#define F0900_P1_ERR_CNT22 0xf59d007f +#define R0900_P1_ERRCNT22 0xf59d +#define ERRCNT22 REGx(R0900_P1_ERRCNT22) +#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080 +#define F0900_P1_ERR_CNT22 0xf59d007f +#define ERR_CNT22 FLDx(F0900_P1_ERR_CNT22) /*P1_ERRCNT21*/ -#define R0900_P1_ERRCNT21 0xf59e -#define F0900_P1_ERR_CNT21 0xf59e00ff +#define R0900_P1_ERRCNT21 0xf59e +#define ERRCNT21 REGx(R0900_P1_ERRCNT21) +#define F0900_P1_ERR_CNT21 0xf59e00ff +#define ERR_CNT21 FLDx(F0900_P1_ERR_CNT21) /*P1_ERRCNT20*/ -#define R0900_P1_ERRCNT20 0xf59f -#define F0900_P1_ERR_CNT20 0xf59f00ff +#define R0900_P1_ERRCNT20 0xf59f +#define ERRCNT20 REGx(R0900_P1_ERRCNT20) +#define F0900_P1_ERR_CNT20 0xf59f00ff +#define ERR_CNT20 FLDx(F0900_P1_ERR_CNT20) /*P1_FECSPY*/ -#define R0900_P1_FECSPY 0xf5a0 -#define F0900_P1_SPY_ENABLE 0xf5a00080 -#define F0900_P1_NO_SYNCBYTE 0xf5a00040 -#define F0900_P1_SERIAL_MODE 0xf5a00020 -#define F0900_P1_UNUSUAL_PACKET 0xf5a00010 -#define F0900_P1_BER_PACKMODE 0xf5a00008 -#define F0900_P1_BERMETER_LMODE 0xf5a00002 -#define F0900_P1_BERMETER_RESET 0xf5a00001 +#define R0900_P1_FECSPY 0xf5a0 +#define FECSPY REGx(R0900_P1_FECSPY) +#define F0900_P1_SPY_ENABLE 0xf5a00080 +#define F0900_P1_NO_SYNCBYTE 0xf5a00040 +#define F0900_P1_SERIAL_MODE 0xf5a00020 +#define F0900_P1_UNUSUAL_PACKET 0xf5a00010 +#define F0900_P1_BERMETER_DATAMODE 0xf5a00008 +#define F0900_P1_BERMETER_LMODE 0xf5a00002 +#define F0900_P1_BERMETER_RESET 0xf5a00001 /*P1_FSPYCFG*/ -#define R0900_P1_FSPYCFG 0xf5a1 -#define F0900_P1_FECSPY_INPUT 0xf5a100c0 -#define F0900_P1_RST_ON_ERROR 0xf5a10020 -#define F0900_P1_ONE_SHOT 0xf5a10010 -#define F0900_P1_I2C_MODE 0xf5a1000c -#define F0900_P1_SPY_HYSTERESIS 0xf5a10003 +#define R0900_P1_FSPYCFG 0xf5a1 +#define FSPYCFG REGx(R0900_P1_FSPYCFG) +#define F0900_P1_FECSPY_INPUT 0xf5a100c0 +#define F0900_P1_RST_ON_ERROR 0xf5a10020 +#define F0900_P1_ONE_SHOT 0xf5a10010 +#define F0900_P1_I2C_MODE 0xf5a1000c +#define F0900_P1_SPY_HYSTERESIS 0xf5a10003 /*P1_FSPYDATA*/ -#define R0900_P1_FSPYDATA 0xf5a2 -#define F0900_P1_SPY_STUFFING 0xf5a20080 -#define F0900_P1_NOERROR_PKTJITTER 0xf5a20040 -#define F0900_P1_SPY_CNULLPKT 0xf5a20020 -#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f +#define R0900_P1_FSPYDATA 0xf5a2 +#define FSPYDATA REGx(R0900_P1_FSPYDATA) +#define F0900_P1_SPY_STUFFING 0xf5a20080 +#define F0900_P1_SPY_CNULLPKT 0xf5a20020 +#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f /*P1_FSPYOUT*/ -#define R0900_P1_FSPYOUT 0xf5a3 -#define F0900_P1_FSPY_DIRECT 0xf5a30080 -#define F0900_P1_SPY_OUTDATA_BUS 0xf5a30038 -#define F0900_P1_STUFF_MODE 0xf5a30007 +#define R0900_P1_FSPYOUT 0xf5a3 +#define FSPYOUT REGx(R0900_P1_FSPYOUT) +#define F0900_P1_FSPY_DIRECT 0xf5a30080 +#define F0900_P1_STUFF_MODE 0xf5a30007 /*P1_FSTATUS*/ -#define R0900_P1_FSTATUS 0xf5a4 -#define F0900_P1_SPY_ENDSIM 0xf5a40080 -#define F0900_P1_VALID_SIM 0xf5a40040 -#define F0900_P1_FOUND_SIGNAL 0xf5a40020 -#define F0900_P1_DSS_SYNCBYTE 0xf5a40010 -#define F0900_P1_RESULT_STATE 0xf5a4000f +#define R0900_P1_FSTATUS 0xf5a4 +#define FSTATUS REGx(R0900_P1_FSTATUS) +#define F0900_P1_SPY_ENDSIM 0xf5a40080 +#define F0900_P1_VALID_SIM 0xf5a40040 +#define F0900_P1_FOUND_SIGNAL 0xf5a40020 +#define F0900_P1_DSS_SYNCBYTE 0xf5a40010 +#define F0900_P1_RESULT_STATE 0xf5a4000f /*P1_FBERCPT4*/ -#define R0900_P1_FBERCPT4 0xf5a8 -#define F0900_P1_FBERMETER_CPT4 0xf5a800ff +#define R0900_P1_FBERCPT4 0xf5a8 +#define FBERCPT4 REGx(R0900_P1_FBERCPT4) +#define F0900_P1_FBERMETER_CPT4 0xf5a800ff /*P1_FBERCPT3*/ -#define R0900_P1_FBERCPT3 0xf5a9 -#define F0900_P1_FBERMETER_CPT3 0xf5a900ff +#define R0900_P1_FBERCPT3 0xf5a9 +#define FBERCPT3 REGx(R0900_P1_FBERCPT3) +#define F0900_P1_FBERMETER_CPT3 0xf5a900ff /*P1_FBERCPT2*/ -#define R0900_P1_FBERCPT2 0xf5aa -#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff +#define R0900_P1_FBERCPT2 0xf5aa +#define FBERCPT2 REGx(R0900_P1_FBERCPT2) +#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff /*P1_FBERCPT1*/ -#define R0900_P1_FBERCPT1 0xf5ab -#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff +#define R0900_P1_FBERCPT1 0xf5ab +#define FBERCPT1 REGx(R0900_P1_FBERCPT1) +#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff /*P1_FBERCPT0*/ -#define R0900_P1_FBERCPT0 0xf5ac -#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff +#define R0900_P1_FBERCPT0 0xf5ac +#define FBERCPT0 REGx(R0900_P1_FBERCPT0) +#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff /*P1_FBERERR2*/ -#define R0900_P1_FBERERR2 0xf5ad -#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff +#define R0900_P1_FBERERR2 0xf5ad +#define FBERERR2 REGx(R0900_P1_FBERERR2) +#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff /*P1_FBERERR1*/ -#define R0900_P1_FBERERR1 0xf5ae -#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff +#define R0900_P1_FBERERR1 0xf5ae +#define FBERERR1 REGx(R0900_P1_FBERERR1) +#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff /*P1_FBERERR0*/ -#define R0900_P1_FBERERR0 0xf5af -#define F0900_P1_FBERMETER_ERR0 0xf5af00ff +#define R0900_P1_FBERERR0 0xf5af +#define FBERERR0 REGx(R0900_P1_FBERERR0) +#define F0900_P1_FBERMETER_ERR0 0xf5af00ff /*P1_FSPYBER*/ -#define R0900_P1_FSPYBER 0xf5b2 -#define F0900_P1_FSPYOBS_XORREAD 0xf5b20040 -#define F0900_P1_FSPYBER_OBSMODE 0xf5b20020 -#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010 -#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008 -#define F0900_P1_FSPYBER_CTIME 0xf5b20007 - -/*RCCFGH*/ -#define R0900_RCCFGH 0xf600 -#define F0900_TSRCFIFO_DVBCI 0xf6000080 -#define F0900_TSRCFIFO_SERIAL 0xf6000040 -#define F0900_TSRCFIFO_DISABLE 0xf6000020 -#define F0900_TSFIFO_2TORC 0xf6000010 -#define F0900_TSRCFIFO_HSGNLOUT 0xf6000008 -#define F0900_TSRCFIFO_ERRMODE 0xf6000006 +#define R0900_P1_FSPYBER 0xf5b2 +#define FSPYBER REGx(R0900_P1_FSPYBER) +#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010 +#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008 +#define F0900_P1_FSPYBER_CTIME 0xf5b20007 + +/*RCCFG2*/ +#define R0900_RCCFG2 0xf600 /*TSGENERAL*/ -#define R0900_TSGENERAL 0xf630 -#define F0900_TSFIFO_BCLK1ALL 0xf6300020 -#define F0900_MUXSTREAM_OUTMODE 0xf6300008 -#define F0900_TSFIFO_PERMPARAL 0xf6300006 -#define F0900_RST_REEDSOLO 0xf6300001 +#define R0900_TSGENERAL 0xf630 +#define F0900_TSFIFO_DISTS2PAR 0xf6300040 +#define F0900_MUXSTREAM_OUTMODE 0xf6300008 +#define F0900_TSFIFO_PERMPARAL 0xf6300006 /*TSGENERAL1X*/ -#define R0900_TSGENERAL1X 0xf670 -#define F0900_TSFIFO1X_BCLK1ALL 0xf6700020 -#define F0900_MUXSTREAM1X_OUTMODE 0xf6700008 -#define F0900_TSFIFO1X_PERMPARAL 0xf6700006 -#define F0900_RST1X_REEDSOLO 0xf6700001 +#define R0900_TSGENERAL1X 0xf670 /*NBITER_NF4*/ -#define R0900_NBITER_NF4 0xfa03 -#define F0900_NBITER_NF_QP_1_2 0xfa0300ff +#define R0900_NBITER_NF4 0xfa03 +#define F0900_NBITER_NF_QP_1_2 0xfa0300ff /*NBITER_NF5*/ -#define R0900_NBITER_NF5 0xfa04 -#define F0900_NBITER_NF_QP_3_5 0xfa0400ff +#define R0900_NBITER_NF5 0xfa04 +#define F0900_NBITER_NF_QP_3_5 0xfa0400ff /*NBITER_NF6*/ -#define R0900_NBITER_NF6 0xfa05 -#define F0900_NBITER_NF_QP_2_3 0xfa0500ff +#define R0900_NBITER_NF6 0xfa05 +#define F0900_NBITER_NF_QP_2_3 0xfa0500ff /*NBITER_NF7*/ -#define R0900_NBITER_NF7 0xfa06 -#define F0900_NBITER_NF_QP_3_4 0xfa0600ff +#define R0900_NBITER_NF7 0xfa06 +#define F0900_NBITER_NF_QP_3_4 0xfa0600ff /*NBITER_NF8*/ -#define R0900_NBITER_NF8 0xfa07 -#define F0900_NBITER_NF_QP_4_5 0xfa0700ff +#define R0900_NBITER_NF8 0xfa07 +#define F0900_NBITER_NF_QP_4_5 0xfa0700ff /*NBITER_NF9*/ -#define R0900_NBITER_NF9 0xfa08 -#define F0900_NBITER_NF_QP_5_6 0xfa0800ff +#define R0900_NBITER_NF9 0xfa08 +#define F0900_NBITER_NF_QP_5_6 0xfa0800ff /*NBITER_NF10*/ -#define R0900_NBITER_NF10 0xfa09 -#define F0900_NBITER_NF_QP_8_9 0xfa0900ff +#define R0900_NBITER_NF10 0xfa09 +#define F0900_NBITER_NF_QP_8_9 0xfa0900ff /*NBITER_NF11*/ -#define R0900_NBITER_NF11 0xfa0a -#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff +#define R0900_NBITER_NF11 0xfa0a +#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff /*NBITER_NF12*/ -#define R0900_NBITER_NF12 0xfa0b -#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff +#define R0900_NBITER_NF12 0xfa0b +#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff /*NBITER_NF13*/ -#define R0900_NBITER_NF13 0xfa0c -#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff +#define R0900_NBITER_NF13 0xfa0c +#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff /*NBITER_NF14*/ -#define R0900_NBITER_NF14 0xfa0d -#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff +#define R0900_NBITER_NF14 0xfa0d +#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff /*NBITER_NF15*/ -#define R0900_NBITER_NF15 0xfa0e -#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff +#define R0900_NBITER_NF15 0xfa0e +#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff /*NBITER_NF16*/ -#define R0900_NBITER_NF16 0xfa0f -#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff +#define R0900_NBITER_NF16 0xfa0f +#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff /*NBITER_NF17*/ -#define R0900_NBITER_NF17 0xfa10 -#define F0900_NBITER_NF_8P_9_10 0xfa1000ff +#define R0900_NBITER_NF17 0xfa10 +#define F0900_NBITER_NF_8P_9_10 0xfa1000ff /*NBITERNOERR*/ -#define R0900_NBITERNOERR 0xfa3f -#define F0900_NBITER_STOP_CRIT 0xfa3f000f +#define R0900_NBITERNOERR 0xfa3f +#define F0900_NBITER_STOP_CRIT 0xfa3f000f /*GAINLLR_NF4*/ -#define R0900_GAINLLR_NF4 0xfa43 -#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f +#define R0900_GAINLLR_NF4 0xfa43 +#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f /*GAINLLR_NF5*/ -#define R0900_GAINLLR_NF5 0xfa44 -#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f +#define R0900_GAINLLR_NF5 0xfa44 +#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f /*GAINLLR_NF6*/ -#define R0900_GAINLLR_NF6 0xfa45 -#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f +#define R0900_GAINLLR_NF6 0xfa45 +#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f /*GAINLLR_NF7*/ -#define R0900_GAINLLR_NF7 0xfa46 -#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f +#define R0900_GAINLLR_NF7 0xfa46 +#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f /*GAINLLR_NF8*/ -#define R0900_GAINLLR_NF8 0xfa47 -#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f +#define R0900_GAINLLR_NF8 0xfa47 +#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f /*GAINLLR_NF9*/ -#define R0900_GAINLLR_NF9 0xfa48 -#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f +#define R0900_GAINLLR_NF9 0xfa48 +#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f /*GAINLLR_NF10*/ -#define R0900_GAINLLR_NF10 0xfa49 -#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f +#define R0900_GAINLLR_NF10 0xfa49 +#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f /*GAINLLR_NF11*/ -#define R0900_GAINLLR_NF11 0xfa4a -#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f +#define R0900_GAINLLR_NF11 0xfa4a +#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f /*GAINLLR_NF12*/ -#define R0900_GAINLLR_NF12 0xfa4b -#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f +#define R0900_GAINLLR_NF12 0xfa4b +#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f /*GAINLLR_NF13*/ -#define R0900_GAINLLR_NF13 0xfa4c -#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f +#define R0900_GAINLLR_NF13 0xfa4c +#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f /*GAINLLR_NF14*/ -#define R0900_GAINLLR_NF14 0xfa4d -#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f +#define R0900_GAINLLR_NF14 0xfa4d +#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f /*GAINLLR_NF15*/ -#define R0900_GAINLLR_NF15 0xfa4e -#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f +#define R0900_GAINLLR_NF15 0xfa4e +#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f /*GAINLLR_NF16*/ -#define R0900_GAINLLR_NF16 0xfa4f -#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f +#define R0900_GAINLLR_NF16 0xfa4f +#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f /*GAINLLR_NF17*/ -#define R0900_GAINLLR_NF17 0xfa50 -#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f +#define R0900_GAINLLR_NF17 0xfa50 +#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f /*CFGEXT*/ -#define R0900_CFGEXT 0xfa80 -#define F0900_STAGMODE 0xfa800080 -#define F0900_BYPBCH 0xfa800040 -#define F0900_BYPLDPC 0xfa800020 -#define F0900_LDPCMODE 0xfa800010 -#define F0900_INVLLRSIGN 0xfa800008 -#define F0900_SHORTMULT 0xfa800004 -#define F0900_EXTERNTX 0xfa800001 +#define R0900_CFGEXT 0xfa80 +#define F0900_STAGMODE 0xfa800080 +#define F0900_BYPBCH 0xfa800040 +#define F0900_BYPLDPC 0xfa800020 +#define F0900_LDPCMODE 0xfa800010 +#define F0900_INVLLRSIGN 0xfa800008 +#define F0900_SHORTMULT 0xfa800004 +#define F0900_EXTERNTX 0xfa800001 /*GENCFG*/ -#define R0900_GENCFG 0xfa86 -#define F0900_BROADCAST 0xfa860010 -#define F0900_NOSHFRD2 0xfa860008 -#define F0900_BCHERRFLAG 0xfa860004 -#define F0900_PRIORITY 0xfa860002 -#define F0900_DDEMOD 0xfa860001 +#define R0900_GENCFG 0xfa86 +#define F0900_BROADCAST 0xfa860010 +#define F0900_PRIORITY 0xfa860002 +#define F0900_DDEMOD 0xfa860001 /*LDPCERR1*/ -#define R0900_LDPCERR1 0xfa96 -#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff +#define R0900_LDPCERR1 0xfa96 +#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff /*LDPCERR0*/ -#define R0900_LDPCERR0 0xfa97 -#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff +#define R0900_LDPCERR0 0xfa97 +#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff /*BCHERR*/ -#define R0900_BCHERR 0xfa98 -#define F0900_ERRORFLAG 0xfa980010 -#define F0900_BCH_ERRORS_COUNTER 0xfa98000f +#define R0900_BCHERR 0xfa98 +#define F0900_ERRORFLAG 0xfa980010 +#define F0900_BCH_ERRORS_COUNTER 0xfa98000f /*TSTRES0*/ -#define R0900_TSTRES0 0xff11 -#define F0900_FRESFEC 0xff110080 -#define F0900_FRESTS 0xff110040 -#define F0900_FRESVIT1 0xff110020 -#define F0900_FRESVIT2 0xff110010 -#define F0900_FRESSYM1 0xff110008 -#define F0900_FRESSYM2 0xff110004 -#define F0900_FRESMAS 0xff110002 -#define F0900_FRESINT 0xff110001 +#define R0900_TSTRES0 0xff11 +#define F0900_FRESFEC 0xff110080 + +/*P2_TCTL4*/ +#define R0900_P2_TCTL4 0xff28 +#define F0900_P2_PN4_SELECT 0xff280020 + +/*P1_TCTL4*/ +#define R0900_P1_TCTL4 0xff48 +#define TCTL4 shiftx(R0900_P1_TCTL4, demod, 0x20) +#define F0900_P1_PN4_SELECT 0xff480020 /*P2_TSTDISRX*/ -#define R0900_P2_TSTDISRX 0xff65 -#define F0900_P2_EN_DISRX 0xff650080 -#define F0900_P2_TST_CURRSRC 0xff650040 -#define F0900_P2_IN_DIGSIGNAL 0xff650020 -#define F0900_P2_HIZ_CURRENTSRC 0xff650010 -#define F0900_TST_P2_PIN_SELECT 0xff650008 -#define F0900_P2_TST_DISRX 0xff650007 +#define R0900_P2_TSTDISRX 0xff65 +#define F0900_P2_PIN_SELECT1 0xff650008 /*P1_TSTDISRX*/ -#define R0900_P1_TSTDISRX 0xff67 -#define F0900_P1_EN_DISRX 0xff670080 -#define F0900_P1_TST_CURRSRC 0xff670040 -#define F0900_P1_IN_DIGSIGNAL 0xff670020 -#define F0900_P1_HIZ_CURRENTSRC 0xff670010 -#define F0900_TST_P1_PIN_SELECT 0xff670008 -#define F0900_P1_TST_DISRX 0xff670007 - -#define STV0900_NBREGS 684 -#define STV0900_NBFIELDS 1702 +#define R0900_P1_TSTDISRX 0xff67 +#define TSTDISRX shiftx(R0900_P1_TSTDISRX, demod, 2) +#define F0900_P1_PIN_SELECT1 0xff670008 +#define PIN_SELECT1 shiftx(F0900_P1_PIN_SELECT1, demod, 0x20000) + +#define STV0900_NBREGS 723 +#define STV0900_NBFIELDS 1420 #endif diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c index 962fde1437ce..b8da87fa637f 100644 --- a/drivers/media/dvb/frontends/stv0900_sw.c +++ b/drivers/media/dvb/frontends/stv0900_sw.c @@ -27,56 +27,45 @@ #include "stv0900_reg.h" #include "stv0900_priv.h" -int stv0900_check_signal_presence(struct stv0900_internal *i_params, +s32 shiftx(s32 x, int demod, s32 shift) +{ + if (demod == 1) + return x - shift; + + return x; +} + +int stv0900_check_signal_presence(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - s32 carr_offset, - agc2_integr, - max_carrier; + s32 carr_offset, + agc2_integr, + max_carrier; - int no_signal; + int no_signal = FALSE; - switch (demod) { - case STV0900_DEMOD_1: - default: - carr_offset = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8) - | stv0900_read_reg(i_params, - R0900_P1_CFR1); - carr_offset = ge2comp(carr_offset, 16); - agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) - | stv0900_read_reg(i_params, - R0900_P1_AGC2I0); - max_carrier = i_params->dmd1_srch_range / 1000; - break; - case STV0900_DEMOD_2: - carr_offset = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8) - | stv0900_read_reg(i_params, - R0900_P2_CFR1); - carr_offset = ge2comp(carr_offset, 16); - agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8) - | stv0900_read_reg(i_params, - R0900_P2_AGC2I0); - max_carrier = i_params->dmd2_srch_range / 1000; - break; - } + carr_offset = (stv0900_read_reg(intp, CFR2) << 8) + | stv0900_read_reg(intp, CFR1); + carr_offset = ge2comp(carr_offset, 16); + agc2_integr = (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); + max_carrier = intp->srch_range[demod] / 1000; max_carrier += (max_carrier / 10); max_carrier = 65536 * (max_carrier / 2); - max_carrier /= i_params->mclk / 1000; + max_carrier /= intp->mclk / 1000; if (max_carrier > 0x4000) max_carrier = 0x4000; if ((agc2_integr > 0x2000) - || (carr_offset > + 2*max_carrier) - || (carr_offset < -2*max_carrier)) + || (carr_offset > (2 * max_carrier)) + || (carr_offset < (-2 * max_carrier))) no_signal = TRUE; - else - no_signal = FALSE; return no_signal; } -static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params, +static void stv0900_get_sw_loop_params(struct stv0900_internal *intp, s32 *frequency_inc, s32 *sw_timeout, s32 *steps, enum fe_stv0900_demod_num demod) @@ -85,30 +74,19 @@ static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params, enum fe_stv0900_search_standard standard; - switch (demod) { - case STV0900_DEMOD_1: - default: - srate = i_params->dmd1_symbol_rate; - max_carrier = i_params->dmd1_srch_range / 1000; - max_carrier += max_carrier / 10; - standard = i_params->dmd1_srch_standard; - break; - case STV0900_DEMOD_2: - srate = i_params->dmd2_symbol_rate; - max_carrier = i_params->dmd2_srch_range / 1000; - max_carrier += max_carrier / 10; - standard = i_params->dmd2_srch_stndrd; - break; - } + srate = intp->symbol_rate[demod]; + max_carrier = intp->srch_range[demod] / 1000; + max_carrier += max_carrier / 10; + standard = intp->srch_standard[demod]; max_carrier = 65536 * (max_carrier / 2); - max_carrier /= i_params->mclk / 1000; + max_carrier /= intp->mclk / 1000; if (max_carrier > 0x4000) max_carrier = 0x4000; freq_inc = srate; - freq_inc /= i_params->mclk >> 10; + freq_inc /= intp->mclk >> 10; freq_inc = freq_inc << 6; switch (standard) { @@ -154,7 +132,7 @@ static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params, } -static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, +static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp, s32 FreqIncr, s32 Timeout, int zigzag, s32 MaxStep, enum fe_stv0900_demod_num demod) { @@ -164,20 +142,11 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, freqOffset, max_carrier; - switch (demod) { - case STV0900_DEMOD_1: - default: - max_carrier = i_params->dmd1_srch_range / 1000; - max_carrier += (max_carrier / 10); - break; - case STV0900_DEMOD_2: - max_carrier = i_params->dmd2_srch_range / 1000; - max_carrier += (max_carrier / 10); - break; - } + max_carrier = intp->srch_range[demod] / 1000; + max_carrier += (max_carrier / 10); max_carrier = 65536 * (max_carrier / 2); - max_carrier /= i_params->mclk / 1000; + max_carrier /= intp->mclk / 1000; if (max_carrier > 0x4000) max_carrier = 0x4000; @@ -190,40 +159,15 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, stepCpt = 0; do { - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, - (freqOffset / 256) & 0xFF); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, - freqOffset & 0xFF); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1); - - if (i_params->chip_id == 0x12) { - stv0900_write_bits(i_params, - F0900_P1_RST_HWARE, 1); - stv0900_write_bits(i_params, - F0900_P1_RST_HWARE, 0); - } - break; - case STV0900_DEMOD_2: - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, - (freqOffset / 256) & 0xFF); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, - freqOffset & 0xFF); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1); - - if (i_params->chip_id == 0x12) { - stv0900_write_bits(i_params, - F0900_P2_RST_HWARE, 1); - stv0900_write_bits(i_params, - F0900_P2_RST_HWARE, 0); - } - break; + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, (freqOffset / 256) & 0xff); + stv0900_write_reg(intp, CFRINIT0, freqOffset & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x18); + stv0900_write_bits(intp, ALGOSWRST, 1); + + if (intp->chip_id == 0x12) { + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); } if (zigzag == TRUE) { @@ -235,8 +179,8 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, freqOffset += + 2 * FreqIncr; stepCpt++; - lock = stv0900_get_demod_lock(i_params, demod, Timeout); - no_signal = stv0900_check_signal_presence(i_params, demod); + lock = stv0900_get_demod_lock(intp, demod, Timeout); + no_signal = stv0900_check_signal_presence(intp, demod); } while ((lock == FALSE) && (no_signal == FALSE) @@ -244,269 +188,138 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, && ((freqOffset + FreqIncr) > -max_carrier) && (stepCpt < MaxStep)); - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0); - break; - case STV0900_DEMOD_2: - stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0); - break; - } + stv0900_write_bits(intp, ALGOSWRST, 0); return lock; } -int stv0900_sw_algo(struct stv0900_internal *i_params, +int stv0900_sw_algo(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - int lock = FALSE; - - int no_signal, - zigzag; - s32 dvbs2_fly_wheel; - - s32 freqIncrement, softStepTimeout, trialCounter, max_steps; - - stv0900_get_sw_loop_params(i_params, &freqIncrement, &softStepTimeout, + int lock = FALSE, + no_signal, + zigzag; + s32 s2fw, + fqc_inc, + sft_stp_tout, + trial_cntr, + max_steps; + + stv0900_get_sw_loop_params(intp, &fqc_inc, &sft_stp_tout, &max_steps, demod); - switch (demod) { - case STV0900_DEMOD_1: - default: - switch (i_params->dmd1_srch_standard) { - case STV0900_SEARCH_DVBS1: - case STV0900_SEARCH_DSS: - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CARFREQ, - 0x3B); - else - stv0900_write_reg(i_params, R0900_P1_CARFREQ, - 0xef); - - stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x49); - zigzag = FALSE; - break; - case STV0900_SEARCH_DVBS2: - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CORRELABS, - 0x79); - else - stv0900_write_reg(i_params, R0900_P1_CORRELABS, - 0x68); + switch (intp->srch_standard[demod]) { + case STV0900_SEARCH_DVBS1: + case STV0900_SEARCH_DSS: + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x3b); + else + stv0900_write_reg(intp, CARFREQ, 0xef); - stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, - 0x89); + stv0900_write_reg(intp, DMDCFGMD, 0x49); + zigzag = FALSE; + break; + case STV0900_SEARCH_DVBS2: + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CORRELABS, 0x79); + else + stv0900_write_reg(intp, CORRELABS, 0x68); - zigzag = TRUE; - break; - case STV0900_AUTO_SEARCH: - default: - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, - 0x3B); - stv0900_write_reg(i_params, R0900_P1_CORRELABS, - 0x79); - } else { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, - 0xef); - stv0900_write_reg(i_params, R0900_P1_CORRELABS, - 0x68); - } + stv0900_write_reg(intp, DMDCFGMD, 0x89); - stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, - 0xc9); - zigzag = FALSE; - break; + zigzag = TRUE; + break; + case STV0900_AUTO_SEARCH: + default: + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x3b); + stv0900_write_reg(intp, CORRELABS, 0x79); + } else { + stv0900_write_reg(intp, CARFREQ, 0xef); + stv0900_write_reg(intp, CORRELABS, 0x68); } - trialCounter = 0; - do { - lock = stv0900_search_carr_sw_loop(i_params, - freqIncrement, - softStepTimeout, - zigzag, - max_steps, - demod); - no_signal = stv0900_check_signal_presence(i_params, - demod); - trialCounter++; - if ((lock == TRUE) - || (no_signal == TRUE) - || (trialCounter == 2)) { - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, - R0900_P1_CARFREQ, - 0x49); - stv0900_write_reg(i_params, - R0900_P1_CORRELABS, - 0x9e); - } else { - stv0900_write_reg(i_params, - R0900_P1_CARFREQ, - 0xed); - stv0900_write_reg(i_params, - R0900_P1_CORRELABS, - 0x88); - } - - if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS2_FOUND)) { - msleep(softStepTimeout); - dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT); - - if (dvbs2_fly_wheel < 0xd) { - msleep(softStepTimeout); - dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT); - } - - if (dvbs2_fly_wheel < 0xd) { - lock = FALSE; - - if (trialCounter < 2) { - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x79); - else - stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x68); - - stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x89); - } - } - } - } - - } while ((lock == FALSE) - && (trialCounter < 2) - && (no_signal == FALSE)); - + stv0900_write_reg(intp, DMDCFGMD, 0xc9); + zigzag = FALSE; break; - case STV0900_DEMOD_2: - switch (i_params->dmd2_srch_stndrd) { - case STV0900_SEARCH_DVBS1: - case STV0900_SEARCH_DSS: - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CARFREQ, - 0x3b); - else - stv0900_write_reg(i_params, R0900_P2_CARFREQ, - 0xef); - - stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, - 0x49); - zigzag = FALSE; - break; - case STV0900_SEARCH_DVBS2: - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CORRELABS, - 0x79); - else - stv0900_write_reg(i_params, R0900_P2_CORRELABS, - 0x68); + } - stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89); - zigzag = TRUE; - break; - case STV0900_AUTO_SEARCH: - default: - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, - 0x3b); - stv0900_write_reg(i_params, R0900_P2_CORRELABS, - 0x79); + trial_cntr = 0; + do { + lock = stv0900_search_carr_sw_loop(intp, + fqc_inc, + sft_stp_tout, + zigzag, + max_steps, + demod); + no_signal = stv0900_check_signal_presence(intp, demod); + trial_cntr++; + if ((lock == TRUE) + || (no_signal == TRUE) + || (trial_cntr == 2)) { + + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x49); + stv0900_write_reg(intp, CORRELABS, 0x9e); } else { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, - 0xef); - stv0900_write_reg(i_params, R0900_P2_CORRELABS, - 0x68); + stv0900_write_reg(intp, CARFREQ, 0xed); + stv0900_write_reg(intp, CORRELABS, 0x88); } - stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0xc9); - - zigzag = FALSE; - break; - } + if ((stv0900_get_bits(intp, HEADER_MODE) == + STV0900_DVBS2_FOUND) && + (lock == TRUE)) { + msleep(sft_stp_tout); + s2fw = stv0900_get_bits(intp, FLYWHEEL_CPT); - trialCounter = 0; - - do { - lock = stv0900_search_carr_sw_loop(i_params, - freqIncrement, - softStepTimeout, - zigzag, - max_steps, - demod); - no_signal = stv0900_check_signal_presence(i_params, - demod); - trialCounter++; - if ((lock == TRUE) - || (no_signal == TRUE) - || (trialCounter == 2)) { - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, - R0900_P2_CARFREQ, - 0x49); - stv0900_write_reg(i_params, - R0900_P2_CORRELABS, - 0x9e); - } else { - stv0900_write_reg(i_params, - R0900_P2_CARFREQ, - 0xed); - stv0900_write_reg(i_params, - R0900_P2_CORRELABS, - 0x88); + if (s2fw < 0xd) { + msleep(sft_stp_tout); + s2fw = stv0900_get_bits(intp, + FLYWHEEL_CPT); } - if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS2_FOUND)) { - msleep(softStepTimeout); - dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT); - if (dvbs2_fly_wheel < 0xd) { - msleep(softStepTimeout); - dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT); - } + if (s2fw < 0xd) { + lock = FALSE; - if (dvbs2_fly_wheel < 0xd) { - lock = FALSE; - if (trialCounter < 2) { - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x79); - else - stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x68); + if (trial_cntr < 2) { + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, + CORRELABS, + 0x79); + else + stv0900_write_reg(intp, + CORRELABS, + 0x68); - stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89); - } + stv0900_write_reg(intp, + DMDCFGMD, + 0x89); } } } + } - } while ((lock == FALSE) && (trialCounter < 2) && (no_signal == FALSE)); - - break; - } + } while ((lock == FALSE) + && (trial_cntr < 2) + && (no_signal == FALSE)); return lock; } -static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params, +static u32 stv0900_get_symbol_rate(struct stv0900_internal *intp, u32 mclk, enum fe_stv0900_demod_num demod) { - s32 sfr_field3, sfr_field2, sfr_field1, sfr_field0, - rem1, rem2, intval1, intval2, srate; - - dmd_reg(sfr_field3, F0900_P1_SYMB_FREQ3, F0900_P2_SYMB_FREQ3); - dmd_reg(sfr_field2, F0900_P1_SYMB_FREQ2, F0900_P2_SYMB_FREQ2); - dmd_reg(sfr_field1, F0900_P1_SYMB_FREQ1, F0900_P2_SYMB_FREQ1); - dmd_reg(sfr_field0, F0900_P1_SYMB_FREQ0, F0900_P2_SYMB_FREQ0); - - srate = (stv0900_get_bits(i_params, sfr_field3) << 24) + - (stv0900_get_bits(i_params, sfr_field2) << 16) + - (stv0900_get_bits(i_params, sfr_field1) << 8) + - (stv0900_get_bits(i_params, sfr_field0)); + s32 rem1, rem2, intval1, intval2, srate; + + srate = (stv0900_get_bits(intp, SYMB_FREQ3) << 24) + + (stv0900_get_bits(intp, SYMB_FREQ2) << 16) + + (stv0900_get_bits(intp, SYMB_FREQ1) << 8) + + (stv0900_get_bits(intp, SYMB_FREQ0)); dprintk("lock: srate=%d r0=0x%x r1=0x%x r2=0x%x r3=0x%x \n", - srate, stv0900_get_bits(i_params, sfr_field0), - stv0900_get_bits(i_params, sfr_field1), - stv0900_get_bits(i_params, sfr_field2), - stv0900_get_bits(i_params, sfr_field3)); + srate, stv0900_get_bits(intp, SYMB_FREQ0), + stv0900_get_bits(intp, SYMB_FREQ1), + stv0900_get_bits(intp, SYMB_FREQ2), + stv0900_get_bits(intp, SYMB_FREQ3)); intval1 = (mclk) >> 16; intval2 = (srate) >> 16; @@ -520,18 +333,15 @@ static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params, return srate; } -static void stv0900_set_symbol_rate(struct stv0900_internal *i_params, +static void stv0900_set_symbol_rate(struct stv0900_internal *intp, u32 mclk, u32 srate, enum fe_stv0900_demod_num demod) { - s32 sfr_init_reg; u32 symb; - dprintk(KERN_INFO "%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk, + dprintk("%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk, srate, demod); - dmd_reg(sfr_init_reg, R0900_P1_SFRINIT1, R0900_P2_SFRINIT1); - if (srate > 60000000) { symb = srate << 4; symb /= (mclk >> 12); @@ -543,19 +353,16 @@ static void stv0900_set_symbol_rate(struct stv0900_internal *i_params, symb /= (mclk >> 7); } - stv0900_write_reg(i_params, sfr_init_reg, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, sfr_init_reg + 1, (symb & 0xFF)); + stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0x7f); + stv0900_write_reg(intp, SFRINIT1 + 1, (symb & 0xff)); } -static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params, +static void stv0900_set_max_symbol_rate(struct stv0900_internal *intp, u32 mclk, u32 srate, enum fe_stv0900_demod_num demod) { - s32 sfr_max_reg; u32 symb; - dmd_reg(sfr_max_reg, R0900_P1_SFRUP1, R0900_P2_SFRUP1); - srate = 105 * (srate / 100); if (srate > 60000000) { @@ -570,23 +377,20 @@ static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params, } if (symb < 0x7fff) { - stv0900_write_reg(i_params, sfr_max_reg, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, sfr_max_reg + 1, (symb & 0xFF)); + stv0900_write_reg(intp, SFRUP1, (symb >> 8) & 0x7f); + stv0900_write_reg(intp, SFRUP1 + 1, (symb & 0xff)); } else { - stv0900_write_reg(i_params, sfr_max_reg, 0x7F); - stv0900_write_reg(i_params, sfr_max_reg + 1, 0xFF); + stv0900_write_reg(intp, SFRUP1, 0x7f); + stv0900_write_reg(intp, SFRUP1 + 1, 0xff); } } -static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params, +static void stv0900_set_min_symbol_rate(struct stv0900_internal *intp, u32 mclk, u32 srate, enum fe_stv0900_demod_num demod) { - s32 sfr_min_reg; u32 symb; - dmd_reg(sfr_min_reg, R0900_P1_SFRLOW1, R0900_P2_SFRLOW1); - srate = 95 * (srate / 100); if (srate > 60000000) { symb = srate << 4; @@ -601,22 +405,20 @@ static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params, symb /= (mclk >> 7); } - stv0900_write_reg(i_params, sfr_min_reg, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, sfr_min_reg + 1, (symb & 0xFF)); + stv0900_write_reg(intp, SFRLOW1, (symb >> 8) & 0xff); + stv0900_write_reg(intp, SFRLOW1 + 1, (symb & 0xff)); } -static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params, +static s32 stv0900_get_timing_offst(struct stv0900_internal *intp, u32 srate, enum fe_stv0900_demod_num demod) { - s32 tmgreg, - timingoffset; + s32 timingoffset; - dmd_reg(tmgreg, R0900_P1_TMGREG2, R0900_P2_TMGREG2); - timingoffset = (stv0900_read_reg(i_params, tmgreg) << 16) + - (stv0900_read_reg(i_params, tmgreg + 1) << 8) + - (stv0900_read_reg(i_params, tmgreg + 2)); + timingoffset = (stv0900_read_reg(intp, TMGREG2) << 16) + + (stv0900_read_reg(intp, TMGREG2 + 1) << 8) + + (stv0900_read_reg(intp, TMGREG2 + 2)); timingoffset = ge2comp(timingoffset, 24); @@ -630,22 +432,19 @@ static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params, return timingoffset; } -static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *i_params, +static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - s32 rolloff, man_fld, matstr_reg, rolloff_ctl_fld; - - dmd_reg(man_fld, F0900_P1_MANUAL_ROLLOFF, F0900_P2_MANUAL_ROLLOFF); - dmd_reg(matstr_reg, R0900_P1_MATSTR1, R0900_P2_MATSTR1); - dmd_reg(rolloff_ctl_fld, F0900_P1_ROLLOFF_CONTROL, - F0900_P2_ROLLOFF_CONTROL); - - if (i_params->chip_id == 0x10) { - stv0900_write_bits(i_params, man_fld, 1); - rolloff = stv0900_read_reg(i_params, matstr_reg) & 0x03; - stv0900_write_bits(i_params, rolloff_ctl_fld, rolloff); - } else - stv0900_write_bits(i_params, man_fld, 0); + s32 rolloff; + + if (intp->chip_id == 0x10) { + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); + rolloff = stv0900_read_reg(intp, MATSTR1) & 0x03; + stv0900_write_bits(intp, ROLLOFF_CONTROL, rolloff); + } else if (intp->chip_id <= 0x20) + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 0); + else /* cut 3.0 */ + stv0900_write_bits(intp, MANUALS2_ROLLOFF, 0); } static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro) @@ -668,84 +467,47 @@ static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro) return srate + (srate * rolloff) / 100; } -static int stv0900_check_timing_lock(struct stv0900_internal *i_params, +static int stv0900_check_timing_lock(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { int timingLock = FALSE; - s32 i, - timingcpt = 0; - u8 carFreq, - tmgTHhigh, - tmgTHLow; - - switch (demod) { - case STV0900_DEMOD_1: - default: - carFreq = stv0900_read_reg(i_params, R0900_P1_CARFREQ); - tmgTHhigh = stv0900_read_reg(i_params, R0900_P1_TMGTHRISE); - tmgTHLow = stv0900_read_reg(i_params, R0900_P1_TMGTHFALL); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x0); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P1_RTC, 0x80); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x40); - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x0); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0x0); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0x0); - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x65); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - msleep(7); - - for (i = 0; i < 10; i++) { - if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2) - timingcpt++; - - msleep(1); - } - - if (timingcpt >= 3) - timingLock = TRUE; - - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38); - stv0900_write_reg(i_params, R0900_P1_RTC, 0x88); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68); - stv0900_write_reg(i_params, R0900_P1_CARFREQ, carFreq); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, tmgTHhigh); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, tmgTHLow); - break; - case STV0900_DEMOD_2: - carFreq = stv0900_read_reg(i_params, R0900_P2_CARFREQ); - tmgTHhigh = stv0900_read_reg(i_params, R0900_P2_TMGTHRISE); - tmgTHLow = stv0900_read_reg(i_params, R0900_P2_TMGTHFALL); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P2_RTC, 0x80); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x40); - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x0); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0x0); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0x0); - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x65); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - msleep(5); - for (i = 0; i < 10; i++) { - if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2) - timingcpt++; + s32 i, + timingcpt = 0; + u8 car_freq, + tmg_th_high, + tmg_th_low; + + car_freq = stv0900_read_reg(intp, CARFREQ); + tmg_th_high = stv0900_read_reg(intp, TMGTHRISE); + tmg_th_low = stv0900_read_reg(intp, TMGTHFALL); + stv0900_write_reg(intp, TMGTHRISE, 0x20); + stv0900_write_reg(intp, TMGTHFALL, 0x0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, RTC, 0x80); + stv0900_write_reg(intp, RTCS2, 0x40); + stv0900_write_reg(intp, CARFREQ, 0x0); + stv0900_write_reg(intp, CFRINIT1, 0x0); + stv0900_write_reg(intp, CFRINIT0, 0x0); + stv0900_write_reg(intp, AGC2REF, 0x65); + stv0900_write_reg(intp, DMDISTATE, 0x18); + msleep(7); + + for (i = 0; i < 10; i++) { + if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) + timingcpt++; + + msleep(1); + } - msleep(1); - } + if (timingcpt >= 3) + timingLock = TRUE; - if (timingcpt >= 3) - timingLock = TRUE; - - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38); - stv0900_write_reg(i_params, R0900_P2_RTC, 0x88); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68); - stv0900_write_reg(i_params, R0900_P2_CARFREQ, carFreq); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, tmgTHhigh); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, tmgTHLow); - break; - } + stv0900_write_reg(intp, AGC2REF, 0x38); + stv0900_write_reg(intp, RTC, 0x88); + stv0900_write_reg(intp, RTCS2, 0x68); + stv0900_write_reg(intp, CARFREQ, car_freq); + stv0900_write_reg(intp, TMGTHRISE, tmg_th_high); + stv0900_write_reg(intp, TMGTHFALL, tmg_th_low); return timingLock; } @@ -754,142 +516,114 @@ static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe, s32 demod_timeout) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; + int lock = FALSE, + d = demod; + s32 srate, + search_range, + locktimeout, + currier_step, + nb_steps, + current_step, + direction, + tuner_freq, + timeout, + freq; - int lock = FALSE; - s32 srate, search_range, locktimeout, - currier_step, nb_steps, current_step, - direction, tuner_freq, timeout; - - switch (demod) { - case STV0900_DEMOD_1: - default: - srate = i_params->dmd1_symbol_rate; - search_range = i_params->dmd1_srch_range; - break; - - case STV0900_DEMOD_2: - srate = i_params->dmd2_symbol_rate; - search_range = i_params->dmd2_srch_range; - break; - } + srate = intp->symbol_rate[d]; + search_range = intp->srch_range[d]; if (srate >= 10000000) locktimeout = demod_timeout / 3; else locktimeout = demod_timeout / 2; - lock = stv0900_get_demod_lock(i_params, demod, locktimeout); - - if (lock == FALSE) { - if (srate >= 10000000) { - if (stv0900_check_timing_lock(i_params, demod) == TRUE) { - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15); - break; - case STV0900_DEMOD_2: - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15); - break; - } + lock = stv0900_get_demod_lock(intp, d, locktimeout); - lock = stv0900_get_demod_lock(i_params, demod, demod_timeout); - } else - lock = FALSE; - } else { - if (srate <= 4000000) - currier_step = 1000; - else if (srate <= 7000000) - currier_step = 2000; - else if (srate <= 10000000) - currier_step = 3000; - else - currier_step = 5000; - - nb_steps = ((search_range / 1000) / currier_step); - nb_steps /= 2; - nb_steps = (2 * (nb_steps + 1)); - if (nb_steps < 0) - nb_steps = 2; - else if (nb_steps > 12) - nb_steps = 12; - - current_step = 1; - direction = 1; + if (lock != FALSE) + return lock; + + if (srate >= 10000000) { + if (stv0900_check_timing_lock(intp, d) == TRUE) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + lock = stv0900_get_demod_lock(intp, d, demod_timeout); + } else + lock = FALSE; + + return lock; + } + + if (intp->chip_id <= 0x20) { + if (srate <= 1000000) + currier_step = 500; + else if (srate <= 4000000) + currier_step = 1000; + else if (srate <= 7000000) + currier_step = 2000; + else if (srate <= 10000000) + currier_step = 3000; + else + currier_step = 5000; + + if (srate >= 2000000) { timeout = (demod_timeout / 3); if (timeout > 1000) timeout = 1000; + } else + timeout = (demod_timeout / 2); + } else { + /*cut 3.0 */ + currier_step = srate / 4000; + timeout = (demod_timeout * 3) / 4; + } - switch (demod) { - case STV0900_DEMOD_1: - default: - if (lock == FALSE) { - tuner_freq = i_params->tuner1_freq; - i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + i_params->dmd1_symbol_rate; + nb_steps = ((search_range / 1000) / currier_step); - while ((current_step <= nb_steps) && (lock == FALSE)) { + if ((nb_steps % 2) != 0) + nb_steps += 1; - if (direction > 0) - tuner_freq += (current_step * currier_step); - else - tuner_freq -= (current_step * currier_step); - - stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C); - if (i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS2) { - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); - } - - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15); - lock = stv0900_get_demod_lock(i_params, demod, timeout); - direction *= -1; - current_step++; - } - } - break; - case STV0900_DEMOD_2: - if (lock == FALSE) { - tuner_freq = i_params->tuner2_freq; - i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + srate; + if (nb_steps <= 0) + nb_steps = 2; + else if (nb_steps > 12) + nb_steps = 12; - while ((current_step <= nb_steps) && (lock == FALSE)) { + current_step = 1; + direction = 1; - if (direction > 0) - tuner_freq += (current_step * currier_step); - else - tuner_freq -= (current_step * currier_step); - - stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C); - if (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS2) { - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - } - - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15); - lock = stv0900_get_demod_lock(i_params, demod, timeout); - direction *= -1; - current_step++; - } - } - break; - } + if (intp->chip_id <= 0x20) { + tuner_freq = intp->freq[d]; + intp->bw[d] = stv0900_carrier_width(intp->symbol_rate[d], + intp->rolloff) + intp->symbol_rate[d]; + } else + tuner_freq = 0; + + while ((current_step <= nb_steps) && (lock == FALSE)) { + if (direction > 0) + tuner_freq += (current_step * currier_step); + else + tuner_freq -= (current_step * currier_step); + + if (intp->chip_id <= 0x20) { + stv0900_set_tuner(fe, tuner_freq, intp->bw[d]); + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, 0); + stv0900_write_reg(intp, CFRINIT0, 0); + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + } else { + stv0900_write_reg(intp, DMDISTATE, 0x1c); + freq = (tuner_freq * 65536) / (intp->mclk / 1000); + stv0900_write_bits(intp, CFR_INIT1, MSB(freq)); + stv0900_write_bits(intp, CFR_INIT0, LSB(freq)); + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x05); } + + lock = stv0900_get_demod_lock(intp, d, timeout); + direction *= -1; + current_step++; } return lock; @@ -931,9 +665,7 @@ static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout, } else if (srate <= 20000000) { (*demod_timeout) = 400; (*fec_timeout) = 130; - } - - else { + } else { (*demod_timeout) = 300; (*fec_timeout) = 100; } @@ -946,95 +678,77 @@ static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout, (*demod_timeout) /= 2; } -static void stv0900_set_viterbi_tracq(struct stv0900_internal *i_params, +static void stv0900_set_viterbi_tracq(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - s32 vth_reg; + s32 vth_reg = VTH12; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12); - - stv0900_write_reg(i_params, vth_reg++, 0xd0); - stv0900_write_reg(i_params, vth_reg++, 0x7d); - stv0900_write_reg(i_params, vth_reg++, 0x53); - stv0900_write_reg(i_params, vth_reg++, 0x2F); - stv0900_write_reg(i_params, vth_reg++, 0x24); - stv0900_write_reg(i_params, vth_reg++, 0x1F); + stv0900_write_reg(intp, vth_reg++, 0xd0); + stv0900_write_reg(intp, vth_reg++, 0x7d); + stv0900_write_reg(intp, vth_reg++, 0x53); + stv0900_write_reg(intp, vth_reg++, 0x2f); + stv0900_write_reg(intp, vth_reg++, 0x24); + stv0900_write_reg(intp, vth_reg++, 0x1f); } -static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params, - enum fe_stv0900_search_standard Standard, - enum fe_stv0900_fec PunctureRate, +static void stv0900_set_viterbi_standard(struct stv0900_internal *intp, + enum fe_stv0900_search_standard standard, + enum fe_stv0900_fec fec, enum fe_stv0900_demod_num demod) { + dprintk("%s: ViterbiStandard = ", __func__); - s32 fecmReg, - prvitReg; - - dprintk(KERN_INFO "%s: ViterbiStandard = ", __func__); - - switch (demod) { - case STV0900_DEMOD_1: - default: - fecmReg = R0900_P1_FECM; - prvitReg = R0900_P1_PRVIT; - break; - case STV0900_DEMOD_2: - fecmReg = R0900_P2_FECM; - prvitReg = R0900_P2_PRVIT; - break; - } - - switch (Standard) { + switch (standard) { case STV0900_AUTO_SEARCH: dprintk("Auto\n"); - stv0900_write_reg(i_params, fecmReg, 0x10); - stv0900_write_reg(i_params, prvitReg, 0x3F); + stv0900_write_reg(intp, FECM, 0x10); + stv0900_write_reg(intp, PRVIT, 0x3f); break; case STV0900_SEARCH_DVBS1: dprintk("DVBS1\n"); - stv0900_write_reg(i_params, fecmReg, 0x00); - switch (PunctureRate) { + stv0900_write_reg(intp, FECM, 0x00); + switch (fec) { case STV0900_FEC_UNKNOWN: default: - stv0900_write_reg(i_params, prvitReg, 0x2F); + stv0900_write_reg(intp, PRVIT, 0x2f); break; case STV0900_FEC_1_2: - stv0900_write_reg(i_params, prvitReg, 0x01); + stv0900_write_reg(intp, PRVIT, 0x01); break; case STV0900_FEC_2_3: - stv0900_write_reg(i_params, prvitReg, 0x02); + stv0900_write_reg(intp, PRVIT, 0x02); break; case STV0900_FEC_3_4: - stv0900_write_reg(i_params, prvitReg, 0x04); + stv0900_write_reg(intp, PRVIT, 0x04); break; case STV0900_FEC_5_6: - stv0900_write_reg(i_params, prvitReg, 0x08); + stv0900_write_reg(intp, PRVIT, 0x08); break; case STV0900_FEC_7_8: - stv0900_write_reg(i_params, prvitReg, 0x20); + stv0900_write_reg(intp, PRVIT, 0x20); break; } break; case STV0900_SEARCH_DSS: dprintk("DSS\n"); - stv0900_write_reg(i_params, fecmReg, 0x80); - switch (PunctureRate) { + stv0900_write_reg(intp, FECM, 0x80); + switch (fec) { case STV0900_FEC_UNKNOWN: default: - stv0900_write_reg(i_params, prvitReg, 0x13); + stv0900_write_reg(intp, PRVIT, 0x13); break; case STV0900_FEC_1_2: - stv0900_write_reg(i_params, prvitReg, 0x01); + stv0900_write_reg(intp, PRVIT, 0x01); break; case STV0900_FEC_2_3: - stv0900_write_reg(i_params, prvitReg, 0x02); + stv0900_write_reg(intp, PRVIT, 0x02); break; case STV0900_FEC_6_7: - stv0900_write_reg(i_params, prvitReg, 0x10); + stv0900_write_reg(intp, PRVIT, 0x10); break; } break; @@ -1043,340 +757,277 @@ static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params, } } -static void stv0900_track_optimization(struct dvb_frontend *fe) +static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) { - struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; - enum fe_stv0900_demod_num demod = state->demod; + enum fe_stv0900_fec prate; + s32 rate_fld = stv0900_get_bits(intp, VIT_CURPUN); - s32 srate, pilots, aclc, freq1, freq0, - i = 0, timed, timef, blindTunSw = 0; + switch (rate_fld) { + case 13: + prate = STV0900_FEC_1_2; + break; + case 18: + prate = STV0900_FEC_2_3; + break; + case 21: + prate = STV0900_FEC_3_4; + break; + case 24: + prate = STV0900_FEC_5_6; + break; + case 25: + prate = STV0900_FEC_6_7; + break; + case 26: + prate = STV0900_FEC_7_8; + break; + default: + prate = STV0900_FEC_UNKNOWN; + break; + } - enum fe_stv0900_rolloff rolloff; - enum fe_stv0900_modcode foundModcod; + return prate; +} - dprintk(KERN_INFO "%s\n", __func__); +void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, + u32 srate) +{ + if (intp->chip_id >= 0x30) { + if (srate >= 15000000) { + stv0900_write_reg(intp, ACLC, 0x2b); + stv0900_write_reg(intp, BCLC, 0x1a); + } else if ((srate >= 7000000) && (15000000 > srate)) { + stv0900_write_reg(intp, ACLC, 0x0c); + stv0900_write_reg(intp, BCLC, 0x1b); + } else if (srate < 7000000) { + stv0900_write_reg(intp, ACLC, 0x2c); + stv0900_write_reg(intp, BCLC, 0x1c); + } - srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate += stv0900_get_timing_offst(i_params, srate, demod); + } else { /*cut 2.0 and 1.x*/ + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + } - switch (demod) { - case STV0900_DEMOD_1: - default: - switch (i_params->dmd1_rslts.standard) { - case STV0900_DVBS1_STANDARD: - if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) { - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - } +} - stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff); - stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75); - break; - case STV0900_DSS_STANDARD: - if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) { - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - } +static void stv0900_track_optimization(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; - stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff); - stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75); - break; - case STV0900_DVBS2_STANDARD: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); - stv0900_write_reg(i_params, R0900_P1_ACLC, 0); - stv0900_write_reg(i_params, R0900_P1_BCLC, 0); - if (i_params->dmd1_rslts.frame_length == STV0900_LONG_FRAME) { - foundModcod = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD); - pilots = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01; - aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id); - if (foundModcod <= STV0900_QPSK_910) - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc); - else if (foundModcod <= STV0900_8PSK_910) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc); - } + s32 srate, + pilots, + aclc, + freq1, + freq0, + i = 0, + timed, + timef, + blind_tun_sw = 0, + modulation; - if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) { - if (foundModcod <= STV0900_16APSK_910) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc); - } else if (foundModcod <= STV0900_32APSK_910) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc); - } - } + enum fe_stv0900_rolloff rolloff; + enum fe_stv0900_modcode foundModcod; - } else { - aclc = stv0900_get_optim_short_carr_loop(srate, i_params->dmd1_rslts.modulation, i_params->chip_id); - if (i_params->dmd1_rslts.modulation == STV0900_QPSK) - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc); - - else if (i_params->dmd1_rslts.modulation == STV0900_8PSK) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc); - } else if (i_params->dmd1_rslts.modulation == STV0900_16APSK) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc); - } else if (i_params->dmd1_rslts.modulation == STV0900_32APSK) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc); - } + dprintk("%s\n", __func__); - } + srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + srate += stv0900_get_timing_offst(intp, srate, demod); - if (i_params->chip_id <= 0x11) { - if (i_params->demod_mode != STV0900_SINGLE) - stv0900_activate_s2_modcode(i_params, demod); + switch (intp->result[demod].standard) { + case STV0900_DVBS1_STANDARD: + case STV0900_DSS_STANDARD: + dprintk("%s: found DVB-S or DSS\n", __func__); + if (intp->srch_standard[demod] == STV0900_AUTO_SEARCH) { + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 0); + } - } + stv0900_write_bits(intp, ROLLOFF_CONTROL, intp->rolloff); + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67); - break; - case STV0900_UNKNOWN_STANDARD: - default: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); + if (intp->chip_id < 0x30) { + stv0900_write_reg(intp, ERRCTRL1, 0x75); break; } - freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2); - freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1); - rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS); - if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) { - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00); - stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01); - stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod); - stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod); - stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod); - blindTunSw = 1; + if (stv0900_get_vit_fec(intp, demod) == STV0900_FEC_1_2) { + stv0900_write_reg(intp, GAUSSR0, 0x98); + stv0900_write_reg(intp, CCIR0, 0x18); + } else { + stv0900_write_reg(intp, GAUSSR0, 0x18); + stv0900_write_reg(intp, CCIR0, 0x18); } - if (i_params->chip_id >= 0x20) { - if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) { - stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0a); - stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x0); + stv0900_write_reg(intp, ERRCTRL1, 0x75); + break; + case STV0900_DVBS2_STANDARD: + dprintk("%s: found DVB-S2\n", __func__); + stv0900_write_bits(intp, DVBS1_ENABLE, 0); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_reg(intp, ACLC, 0); + stv0900_write_reg(intp, BCLC, 0); + if (intp->result[demod].frame_len == STV0900_LONG_FRAME) { + foundModcod = stv0900_get_bits(intp, DEMOD_MODCOD); + pilots = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; + aclc = stv0900_get_optim_carr_loop(srate, + foundModcod, + pilots, + intp->chip_id); + if (foundModcod <= STV0900_QPSK_910) + stv0900_write_reg(intp, ACLC2S2Q, aclc); + else if (foundModcod <= STV0900_8PSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S28, aclc); } - } - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x08); - - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0x0A); - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38); - - if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd1_symbol_rate < 10000000)) { - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - i_params->tuner1_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000; - - if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) { - if (i_params->dmd1_srch_algo != STV0900_WARM_START) - stv0900_set_bandwidth(fe, i_params->tuner1_bw); + if ((intp->demod_mode == STV0900_SINGLE) && + (foundModcod > STV0900_8PSK_910)) { + if (foundModcod <= STV0900_16APSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S216A, + aclc); + } else if (foundModcod <= STV0900_32APSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S232A, + aclc); + } } - if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000)) - msleep(50); - else - msleep(5); - - stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START); - - if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) { - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - i = 0; - while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) { - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - i++; - } + } else { + modulation = intp->result[demod].modulation; + aclc = stv0900_get_optim_short_carr_loop(srate, + modulation, intp->chip_id); + if (modulation == STV0900_QPSK) + stv0900_write_reg(intp, ACLC2S2Q, aclc); + else if (modulation == STV0900_8PSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S28, aclc); + } else if (modulation == STV0900_16APSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S216A, aclc); + } else if (modulation == STV0900_32APSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S232A, aclc); } } - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49); + if (intp->chip_id <= 0x11) { + if (intp->demod_mode != STV0900_SINGLE) + stv0900_activate_s2_modcod(intp, demod); - if ((i_params->dmd1_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd1_rslts.standard == STV0900_DSS_STANDARD)) - stv0900_set_viterbi_tracq(i_params, demod); + } + stv0900_write_reg(intp, ERRCTRL1, 0x67); break; + case STV0900_UNKNOWN_STANDARD: + default: + dprintk("%s: found unknown standard\n", __func__); + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + break; + } - case STV0900_DEMOD_2: - switch (i_params->dmd2_rslts.standard) { - case STV0900_DVBS1_STANDARD: - - if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) { - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - } - - stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff); - stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75); - break; - case STV0900_DSS_STANDARD: - if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) { - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - } - - stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff); - stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75); - break; - case STV0900_DVBS2_STANDARD: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - stv0900_write_reg(i_params, R0900_P2_ACLC, 0); - stv0900_write_reg(i_params, R0900_P2_BCLC, 0); - if (i_params->dmd2_rslts.frame_length == STV0900_LONG_FRAME) { - foundModcod = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD); - pilots = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01; - aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id); - if (foundModcod <= STV0900_QPSK_910) - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc); - else if (foundModcod <= STV0900_8PSK_910) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc); - } + freq1 = stv0900_read_reg(intp, CFR2); + freq0 = stv0900_read_reg(intp, CFR1); + rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { + stv0900_write_reg(intp, SFRSTEP, 0x00); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, TMGCFG2, 0xc1); + stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); + blind_tun_sw = 1; + if (intp->result[demod].standard != STV0900_DVBS2_STANDARD) + stv0900_set_dvbs1_track_car_loop(intp, demod, srate); - if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) { - if (foundModcod <= STV0900_16APSK_910) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc); - } else if (foundModcod <= STV0900_32APSK_910) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc); - } + } - } + if (intp->chip_id >= 0x20) { + if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || + (intp->srch_standard[demod] == + STV0900_SEARCH_DSS) || + (intp->srch_standard[demod] == + STV0900_AUTO_SEARCH)) { + stv0900_write_reg(intp, VAVSRVIT, 0x0a); + stv0900_write_reg(intp, VITSCALE, 0x0); + } + } - } else { - aclc = stv0900_get_optim_short_carr_loop(srate, - i_params->dmd2_rslts.modulation, - i_params->chip_id); - - if (i_params->dmd2_rslts.modulation == STV0900_QPSK) - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc); - - else if (i_params->dmd2_rslts.modulation == STV0900_8PSK) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc); - } else if (i_params->dmd2_rslts.modulation == STV0900_16APSK) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc); - } else if (i_params->dmd2_rslts.modulation == STV0900_32APSK) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc); - } - } + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x08); - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67); + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0x0a); - break; - case STV0900_UNKNOWN_STANDARD: - default: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - break; - } + stv0900_write_reg(intp, AGC2REF, 0x38); - freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2); - freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1); - rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS); - if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) { - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00); - stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01); - stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod); - stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod); - stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod); - blindTunSw = 1; - } + if ((intp->chip_id >= 0x20) || + (blind_tun_sw == 1) || + (intp->symbol_rate[demod] < 10000000)) { + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + intp->bw[demod] = stv0900_carrier_width(srate, + intp->rolloff) + 10000000; - if (i_params->chip_id >= 0x20) { - if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) { - stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0a); - stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x0); - } + if ((intp->chip_id >= 0x20) || (blind_tun_sw == 1)) { + if (intp->srch_algo[demod] != STV0900_WARM_START) + stv0900_set_bandwidth(fe, intp->bw[demod]); } - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x08); - - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0x0a); - - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38); - if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd2_symbol_rate < 10000000)) { - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000; + if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) || + (intp->symbol_rate[demod] < 10000000)) + msleep(50); + else + msleep(5); - if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) { - if (i_params->dmd2_srch_algo != STV0900_WARM_START) - stv0900_set_bandwidth(fe, i_params->tuner2_bw); - } + stv0900_get_lock_timeout(&timed, &timef, srate, + STV0900_WARM_START); - if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000)) - msleep(50); - else - msleep(5); - - stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START); - if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) { - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - i = 0; - while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) { - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - i++; - } + if (stv0900_get_demod_lock(intp, demod, timed / 2) == FALSE) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + i = 0; + while ((stv0900_get_demod_lock(intp, + demod, + timed / 2) == FALSE) && + (i <= 2)) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + i++; } } - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49); + } - if ((i_params->dmd2_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd2_rslts.standard == STV0900_DSS_STANDARD)) - stv0900_set_viterbi_tracq(i_params, demod); + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x49); + + if ((intp->result[demod].standard == STV0900_DVBS1_STANDARD) || + (intp->result[demod].standard == STV0900_DSS_STANDARD)) + stv0900_set_viterbi_tracq(intp, demod); - break; - } } -static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv0900_demod_num demod, s32 time_out) +static int stv0900_get_fec_lock(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, s32 time_out) { - s32 timer = 0, lock = 0, header_field, pktdelin_field, lock_vit_field; + s32 timer = 0, lock = 0; enum fe_stv0900_search_state dmd_state; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(pktdelin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK); - dmd_reg(lock_vit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT); - - dmd_state = stv0900_get_bits(i_params, header_field); + dmd_state = stv0900_get_bits(intp, HEADER_MODE); while ((timer < time_out) && (lock == 0)) { switch (dmd_state) { @@ -1386,10 +1037,10 @@ static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv09 lock = 0; break; case STV0900_DVBS2_FOUND: - lock = stv0900_get_bits(i_params, pktdelin_field); + lock = stv0900_get_bits(intp, PKTDELIN_LOCK); break; case STV0900_DVBS_FOUND: - lock = stv0900_get_bits(i_params, lock_vit_field); + lock = stv0900_get_bits(intp, LOCKEDVIT); break; } @@ -1400,46 +1051,44 @@ static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv09 } if (lock) - dprintk("DEMOD FEC LOCK OK\n"); + dprintk("%s: DEMOD FEC LOCK OK\n", __func__); else - dprintk("DEMOD FEC LOCK FAIL\n"); + dprintk("%s: DEMOD FEC LOCK FAIL\n", __func__); return lock; } -static int stv0900_wait_for_lock(struct stv0900_internal *i_params, +static int stv0900_wait_for_lock(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod, s32 dmd_timeout, s32 fec_timeout) { - s32 timer = 0, lock = 0, str_merg_rst_fld, str_merg_lock_fld; - - dprintk(KERN_INFO "%s\n", __func__); + s32 timer = 0, lock = 0; - dmd_reg(str_merg_rst_fld, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE); - dmd_reg(str_merg_lock_fld, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK); + dprintk("%s\n", __func__); - lock = stv0900_get_demod_lock(i_params, demod, dmd_timeout); + lock = stv0900_get_demod_lock(intp, demod, dmd_timeout); if (lock) - lock = lock && stv0900_get_fec_lock(i_params, demod, fec_timeout); + lock = lock && stv0900_get_fec_lock(intp, demod, fec_timeout); if (lock) { lock = 0; - dprintk(KERN_INFO "%s: Timer = %d, time_out = %d\n", __func__, timer, fec_timeout); + dprintk("%s: Timer = %d, time_out = %d\n", + __func__, timer, fec_timeout); while ((timer < fec_timeout) && (lock == 0)) { - lock = stv0900_get_bits(i_params, str_merg_lock_fld); + lock = stv0900_get_bits(intp, TSFIFO_LINEOK); msleep(1); timer++; } } if (lock) - dprintk(KERN_INFO "%s: DEMOD LOCK OK\n", __func__); + dprintk("%s: DEMOD LOCK OK\n", __func__); else - dprintk(KERN_INFO "%s: DEMOD LOCK FAIL\n", __func__); + dprintk("%s: DEMOD LOCK FAIL\n", __func__); if (lock) return TRUE; @@ -1451,43 +1100,43 @@ enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, enum fe_stv0900_demod_num demod) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_tracking_standard fnd_standard; - s32 state_field, - dss_dvb_field; - dprintk(KERN_INFO "%s\n", __func__); + int hdr_mode = stv0900_get_bits(intp, HEADER_MODE); - dmd_reg(state_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(dss_dvb_field, F0900_P1_DSS_DVB, F0900_P2_DSS_DVB); - - if (stv0900_get_bits(i_params, state_field) == 2) + switch (hdr_mode) { + case 2: fnd_standard = STV0900_DVBS2_STANDARD; - - else if (stv0900_get_bits(i_params, state_field) == 3) { - if (stv0900_get_bits(i_params, dss_dvb_field) == 1) + break; + case 3: + if (stv0900_get_bits(intp, DSS_DVB) == 1) fnd_standard = STV0900_DSS_STANDARD; else fnd_standard = STV0900_DVBS1_STANDARD; - } else + + break; + default: fnd_standard = STV0900_UNKNOWN_STANDARD; + } + + dprintk("%s: standard %d\n", __func__, fnd_standard); return fnd_standard; } -static s32 stv0900_get_carr_freq(struct stv0900_internal *i_params, u32 mclk, +static s32 stv0900_get_carr_freq(struct stv0900_internal *intp, u32 mclk, enum fe_stv0900_demod_num demod) { - s32 cfr_field2, cfr_field1, cfr_field0, - derot, rem1, rem2, intval1, intval2; + s32 derot, + rem1, + rem2, + intval1, + intval2; - dmd_reg(cfr_field2, F0900_P1_CAR_FREQ2, F0900_P2_CAR_FREQ2); - dmd_reg(cfr_field1, F0900_P1_CAR_FREQ1, F0900_P2_CAR_FREQ1); - dmd_reg(cfr_field0, F0900_P1_CAR_FREQ0, F0900_P2_CAR_FREQ0); - - derot = (stv0900_get_bits(i_params, cfr_field2) << 16) + - (stv0900_get_bits(i_params, cfr_field1) << 8) + - (stv0900_get_bits(i_params, cfr_field0)); + derot = (stv0900_get_bits(intp, CAR_FREQ2) << 16) + + (stv0900_get_bits(intp, CAR_FREQ1) << 8) + + (stv0900_get_bits(intp, CAR_FREQ0)); derot = ge2comp(derot, 24); intval1 = mclk >> 12; @@ -1505,7 +1154,7 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) { struct dvb_frontend_ops *frontend_ops = NULL; struct dvb_tuner_ops *tuner_ops = NULL; - u32 frequency = 0; + u32 freq = 0; if (&fe->ops) frontend_ops = &fe->ops; @@ -1514,304 +1163,159 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_frequency) { - if ((tuner_ops->get_frequency(fe, &frequency)) < 0) + if ((tuner_ops->get_frequency(fe, &freq)) < 0) dprintk("%s: Invalid parameter\n", __func__); else - dprintk("%s: Frequency=%d\n", __func__, frequency); - - } - - return frequency; -} - -static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *i_params, - enum fe_stv0900_demod_num demod) -{ - s32 rate_fld, vit_curpun_fld; - enum fe_stv0900_fec prate; + dprintk("%s: Frequency=%d\n", __func__, freq); - dmd_reg(vit_curpun_fld, F0900_P1_VIT_CURPUN, F0900_P2_VIT_CURPUN); - rate_fld = stv0900_get_bits(i_params, vit_curpun_fld); - - switch (rate_fld) { - case 13: - prate = STV0900_FEC_1_2; - break; - case 18: - prate = STV0900_FEC_2_3; - break; - case 21: - prate = STV0900_FEC_3_4; - break; - case 24: - prate = STV0900_FEC_5_6; - break; - case 25: - prate = STV0900_FEC_6_7; - break; - case 26: - prate = STV0900_FEC_7_8; - break; - default: - prate = STV0900_FEC_UNKNOWN; - break; } - return prate; + return freq; } -static enum fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) +static enum +fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; enum fe_stv0900_signal_type range = STV0900_OUTOFRANGE; - s32 offsetFreq, - srate_offset, - i = 0; + struct stv0900_signal_info *result = &intp->result[demod]; + s32 offsetFreq, + srate_offset; + int i = 0, + d = demod; u8 timing; msleep(5); - switch (demod) { - case STV0900_DEMOD_1: - default: - if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) { - timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2); - i = 0; - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x5c); - - while ((i <= 50) && (timing != 0) && (timing != 0xFF)) { - timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2); - msleep(5); - i += 5; - } + if (intp->srch_algo[d] == STV0900_BLIND_SEARCH) { + timing = stv0900_read_reg(intp, TMGREG2); + i = 0; + stv0900_write_reg(intp, SFRSTEP, 0x5c); + + while ((i <= 50) && (timing != 0) && (timing != 0xff)) { + timing = stv0900_read_reg(intp, TMGREG2); + msleep(5); + i += 5; } + } - i_params->dmd1_rslts.standard = stv0900_get_standard(fe, demod); - i_params->dmd1_rslts.frequency = stv0900_get_tuner_freq(fe); - offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000; - i_params->dmd1_rslts.frequency += offsetFreq; - i_params->dmd1_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd1_rslts.symbol_rate, demod); - i_params->dmd1_rslts.symbol_rate += srate_offset; - i_params->dmd1_rslts.fec = stv0900_get_vit_fec(i_params, demod); - i_params->dmd1_rslts.modcode = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD); - i_params->dmd1_rslts.pilot = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01; - i_params->dmd1_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE)) >> 1; - i_params->dmd1_rslts.rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS); - switch (i_params->dmd1_rslts.standard) { - case STV0900_DVBS2_STANDARD: - i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_SPECINV_DEMOD); - if (i_params->dmd1_rslts.modcode <= STV0900_QPSK_910) - i_params->dmd1_rslts.modulation = STV0900_QPSK; - else if (i_params->dmd1_rslts.modcode <= STV0900_8PSK_910) - i_params->dmd1_rslts.modulation = STV0900_8PSK; - else if (i_params->dmd1_rslts.modcode <= STV0900_16APSK_910) - i_params->dmd1_rslts.modulation = STV0900_16APSK; - else if (i_params->dmd1_rslts.modcode <= STV0900_32APSK_910) - i_params->dmd1_rslts.modulation = STV0900_32APSK; - else - i_params->dmd1_rslts.modulation = STV0900_UNKNOWN; - break; - case STV0900_DVBS1_STANDARD: - case STV0900_DSS_STANDARD: - i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_IQINV); - i_params->dmd1_rslts.modulation = STV0900_QPSK; - break; - default: - break; - } - - if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000)) { - offsetFreq = i_params->dmd1_rslts.frequency - i_params->tuner1_freq; - i_params->tuner1_freq = stv0900_get_tuner_freq(fe); - if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500)) - range = STV0900_RANGEOK; - else - if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd1_rslts.symbol_rate, i_params->dmd1_rslts.rolloff) / 2000)) - range = STV0900_RANGEOK; - else - range = STV0900_OUTOFRANGE; - - } else { - if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500)) - range = STV0900_RANGEOK; - else - range = STV0900_OUTOFRANGE; - } + result->standard = stv0900_get_standard(fe, d); + result->frequency = stv0900_get_tuner_freq(fe); + offsetFreq = stv0900_get_carr_freq(intp, intp->mclk, d) / 1000; + result->frequency += offsetFreq; + result->symbol_rate = stv0900_get_symbol_rate(intp, intp->mclk, d); + srate_offset = stv0900_get_timing_offst(intp, result->symbol_rate, d); + result->symbol_rate += srate_offset; + result->fec = stv0900_get_vit_fec(intp, d); + result->modcode = stv0900_get_bits(intp, DEMOD_MODCOD); + result->pilot = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; + result->frame_len = ((u32)stv0900_get_bits(intp, DEMOD_TYPE)) >> 1; + result->rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); + switch (result->standard) { + case STV0900_DVBS2_STANDARD: + result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD); + if (result->modcode <= STV0900_QPSK_910) + result->modulation = STV0900_QPSK; + else if (result->modcode <= STV0900_8PSK_910) + result->modulation = STV0900_8PSK; + else if (result->modcode <= STV0900_16APSK_910) + result->modulation = STV0900_16APSK; + else if (result->modcode <= STV0900_32APSK_910) + result->modulation = STV0900_32APSK; + else + result->modulation = STV0900_UNKNOWN; break; - case STV0900_DEMOD_2: - if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) { - timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2); - i = 0; - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x5c); - - while ((i <= 50) && (timing != 0) && (timing != 0xff)) { - timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2); - msleep(5); - i += 5; - } - } - - i_params->dmd2_rslts.standard = stv0900_get_standard(fe, demod); - i_params->dmd2_rslts.frequency = stv0900_get_tuner_freq(fe); - offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000; - i_params->dmd2_rslts.frequency += offsetFreq; - i_params->dmd2_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd2_rslts.symbol_rate, demod); - i_params->dmd2_rslts.symbol_rate += srate_offset; - i_params->dmd2_rslts.fec = stv0900_get_vit_fec(i_params, demod); - i_params->dmd2_rslts.modcode = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD); - i_params->dmd2_rslts.pilot = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01; - i_params->dmd2_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE)) >> 1; - i_params->dmd2_rslts.rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS); - switch (i_params->dmd2_rslts.standard) { - case STV0900_DVBS2_STANDARD: - i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_SPECINV_DEMOD); - if (i_params->dmd2_rslts.modcode <= STV0900_QPSK_910) - i_params->dmd2_rslts.modulation = STV0900_QPSK; - else if (i_params->dmd2_rslts.modcode <= STV0900_8PSK_910) - i_params->dmd2_rslts.modulation = STV0900_8PSK; - else if (i_params->dmd2_rslts.modcode <= STV0900_16APSK_910) - i_params->dmd2_rslts.modulation = STV0900_16APSK; - else if (i_params->dmd2_rslts.modcode <= STV0900_32APSK_910) - i_params->dmd2_rslts.modulation = STV0900_32APSK; - else - i_params->dmd2_rslts.modulation = STV0900_UNKNOWN; - break; - case STV0900_DVBS1_STANDARD: - case STV0900_DSS_STANDARD: - i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_IQINV); - i_params->dmd2_rslts.modulation = STV0900_QPSK; - break; - default: - break; - } + case STV0900_DVBS1_STANDARD: + case STV0900_DSS_STANDARD: + result->spectrum = stv0900_get_bits(intp, IQINV); + result->modulation = STV0900_QPSK; + break; + default: + break; + } - if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000)) { - offsetFreq = i_params->dmd2_rslts.frequency - i_params->tuner2_freq; - i_params->tuner2_freq = stv0900_get_tuner_freq(fe); + if ((intp->srch_algo[d] == STV0900_BLIND_SEARCH) || + (intp->symbol_rate[d] < 10000000)) { + offsetFreq = result->frequency - intp->freq[d]; + intp->freq[d] = stv0900_get_tuner_freq(fe); + if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) + range = STV0900_RANGEOK; + else if (ABS(offsetFreq) <= + (stv0900_carrier_width(result->symbol_rate, + result->rolloff) / 2000)) + range = STV0900_RANGEOK; - if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500)) - range = STV0900_RANGEOK; - else - if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd2_rslts.symbol_rate, i_params->dmd2_rslts.rolloff) / 2000)) - range = STV0900_RANGEOK; - else - range = STV0900_OUTOFRANGE; - } else { - if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500)) - range = STV0900_RANGEOK; - else - range = STV0900_OUTOFRANGE; - } + } else if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) + range = STV0900_RANGEOK; - break; - } + dprintk("%s: range %d\n", __func__, range); return range; } -static enum fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe) +static enum +fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - - s32 srate, demod_timeout, - fec_timeout, freq1, freq0; enum fe_stv0900_signal_type signal_type = STV0900_NODATA; - switch (demod) { - case STV0900_DEMOD_1: - default: - i_params->dmd1_rslts.locked = FALSE; - if (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) { - srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate += stv0900_get_timing_offst(i_params, srate, demod); - if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) - stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod); - - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START); - freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2); - freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) { - i_params->dmd1_rslts.locked = TRUE; - signal_type = stv0900_get_signal_params(fe); - stv0900_track_optimization(fe); - } else { - stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1c); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) { - i_params->dmd1_rslts.locked = TRUE; - signal_type = stv0900_get_signal_params(fe); - stv0900_track_optimization(fe); - } - - } - - } else - i_params->dmd1_rslts.locked = FALSE; - - break; - case STV0900_DEMOD_2: - i_params->dmd2_rslts.locked = FALSE; - if (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) { - srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate += stv0900_get_timing_offst(i_params, srate, demod); - - if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) - stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod); - - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START); - freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2); - freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - - if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) { - i_params->dmd2_rslts.locked = TRUE; + s32 srate, + demod_timeout, + fec_timeout, + freq1, + freq0; + + intp->result[demod].locked = FALSE; + + if (stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) { + srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + srate += stv0900_get_timing_offst(intp, srate, demod); + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) + stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); + + stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, + srate, STV0900_WARM_START); + freq1 = stv0900_read_reg(intp, CFR2); + freq0 = stv0900_read_reg(intp, CFR1); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_bits(intp, SPECINV_CONTROL, + STV0900_IQ_FORCE_SWAPPED); + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + if (stv0900_wait_for_lock(intp, demod, + demod_timeout, fec_timeout) == TRUE) { + intp->result[demod].locked = TRUE; + signal_type = stv0900_get_signal_params(fe); + stv0900_track_optimization(fe); + } else { + stv0900_write_bits(intp, SPECINV_CONTROL, + STV0900_IQ_FORCE_NORMAL); + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + if (stv0900_wait_for_lock(intp, demod, + demod_timeout, fec_timeout) == TRUE) { + intp->result[demod].locked = TRUE; signal_type = stv0900_get_signal_params(fe); stv0900_track_optimization(fe); - } else { - stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1c); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - - if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) { - i_params->dmd2_rslts.locked = TRUE; - signal_type = stv0900_get_signal_params(fe); - stv0900_track_optimization(fe); - } - } - } else - i_params->dmd1_rslts.locked = FALSE; + } - break; - } + } else + intp->result[demod].locked = FALSE; return signal_type; } -static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params, +static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { u32 minagc2level = 0xffff, @@ -1820,105 +1324,54 @@ static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params, s32 i, j, nb_steps, direction; - dprintk(KERN_INFO "%s\n", __func__); - - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38); - stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1); - - stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83); - stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0); - - stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82); - stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0); - stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0); + dprintk("%s\n", __func__); - stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod); - nb_steps = -1 + (i_params->dmd1_srch_range / 1000000); - nb_steps /= 2; - nb_steps = (2 * nb_steps) + 1; + stv0900_write_reg(intp, AGC2REF, 0x38); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); - if (nb_steps < 0) - nb_steps = 1; + stv0900_write_bits(intp, AUTO_GUP, 1); + stv0900_write_bits(intp, AUTO_GLOW, 1); - direction = 1; + stv0900_write_reg(intp, DMDT0M, 0x0); - freq_step = (1000000 << 8) / (i_params->mclk >> 8); + stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); + nb_steps = -1 + (intp->srch_range[demod] / 1000000); + nb_steps /= 2; + nb_steps = (2 * nb_steps) + 1; - init_freq = 0; + if (nb_steps < 0) + nb_steps = 1; - for (i = 0; i < nb_steps; i++) { - if (direction > 0) - init_freq = init_freq + (freq_step * i); - else - init_freq = init_freq - (freq_step * i); + direction = 1; - direction *= -1; - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (init_freq >> 8) & 0xff); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, init_freq & 0xff); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x58); - msleep(10); - agc2level = 0; + freq_step = (1000000 << 8) / (intp->mclk >> 8); - for (j = 0; j < 10; j++) - agc2level += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P1_AGC2I0); + init_freq = 0; - agc2level /= 10; - - if (agc2level < minagc2level) - minagc2level = agc2level; - } - break; - case STV0900_DEMOD_2: - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38); - stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1); - stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83); - stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0); - stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82); - stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0); - stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0); - stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod); - nb_steps = -1 + (i_params->dmd2_srch_range / 1000000); - nb_steps /= 2; - nb_steps = (2 * nb_steps) + 1; - - if (nb_steps < 0) - nb_steps = 1; - - direction = 1; - freq_step = (1000000 << 8) / (i_params->mclk >> 8); - init_freq = 0; - for (i = 0; i < nb_steps; i++) { - if (direction > 0) - init_freq = init_freq + (freq_step * i); - else - init_freq = init_freq - (freq_step * i); + for (i = 0; i < nb_steps; i++) { + if (direction > 0) + init_freq = init_freq + (freq_step * i); + else + init_freq = init_freq - (freq_step * i); - direction *= -1; + direction *= -1; + stv0900_write_reg(intp, DMDISTATE, 0x5C); + stv0900_write_reg(intp, CFRINIT1, (init_freq >> 8) & 0xff); + stv0900_write_reg(intp, CFRINIT0, init_freq & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x58); + msleep(10); + agc2level = 0; - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (init_freq >> 8) & 0xff); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, init_freq & 0xff); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x58); + for (j = 0; j < 10; j++) + agc2level += (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); - msleep(10); - agc2level = 0; - for (j = 0; j < 10; j++) - agc2level += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P2_AGC2I0); + agc2level /= 10; - agc2level /= 10; + if (agc2level < minagc2level) + minagc2level = agc2level; - if (agc2level < minagc2level) - minagc2level = agc2level; - } - break; } return (u16)minagc2level; @@ -1927,336 +1380,192 @@ static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params, static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - int timingLock = FALSE; + int timing_lck = FALSE; s32 i, timingcpt = 0, direction = 1, nb_steps, current_step = 0, tuner_freq; + u32 agc2_th, + coarse_srate = 0, + agc2_integr = 0, + currier_step = 1200; - u32 coarse_srate = 0, agc2_integr = 0, currier_step = 1200; - - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0x12); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xf0); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xe0); - stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1); - stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83); - stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0); - stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82); - stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0); - stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0); - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x50); - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x6a); - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x95); - } else { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed); - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x73); - } + if (intp->chip_id >= 0x30) + agc2_th = 0x2e00; + else + agc2_th = 0x1f00; + + stv0900_write_bits(intp, DEMOD_MODE, 0x1f); + stv0900_write_reg(intp, TMGCFG, 0x12); + stv0900_write_reg(intp, TMGTHRISE, 0xf0); + stv0900_write_reg(intp, TMGTHFALL, 0xe0); + stv0900_write_bits(intp, SCAN_ENABLE, 1); + stv0900_write_bits(intp, CFR_AUTOSCAN, 1); + stv0900_write_reg(intp, SFRUP1, 0x83); + stv0900_write_reg(intp, SFRUP0, 0xc0); + stv0900_write_reg(intp, SFRLOW1, 0x82); + stv0900_write_reg(intp, SFRLOW0, 0xa0); + stv0900_write_reg(intp, DMDT0M, 0x0); + stv0900_write_reg(intp, AGC2REF, 0x50); + + if (intp->chip_id >= 0x30) { + stv0900_write_reg(intp, CARFREQ, 0x99); + stv0900_write_reg(intp, SFRSTEP, 0x98); + } else if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x6a); + stv0900_write_reg(intp, SFRSTEP, 0x95); + } else { + stv0900_write_reg(intp, CARFREQ, 0xed); + stv0900_write_reg(intp, SFRSTEP, 0x73); + } - if (i_params->dmd1_symbol_rate <= 2000000) - currier_step = 1000; - else if (i_params->dmd1_symbol_rate <= 5000000) - currier_step = 2000; - else if (i_params->dmd1_symbol_rate <= 12000000) - currier_step = 3000; - else + if (intp->symbol_rate[demod] <= 2000000) + currier_step = 1000; + else if (intp->symbol_rate[demod] <= 5000000) + currier_step = 2000; + else if (intp->symbol_rate[demod] <= 12000000) + currier_step = 3000; + else currier_step = 5000; - nb_steps = -1 + ((i_params->dmd1_srch_range / 1000) / currier_step); - nb_steps /= 2; - nb_steps = (2 * nb_steps) + 1; - - if (nb_steps < 0) - nb_steps = 1; - - else if (nb_steps > 10) { - nb_steps = 11; - currier_step = (i_params->dmd1_srch_range / 1000) / 10; - } - - current_step = 0; - - direction = 1; - tuner_freq = i_params->tuner1_freq; - - while ((timingLock == FALSE) && (current_step < nb_steps)) { - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5F); - stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x0); - - msleep(50); - - for (i = 0; i < 10; i++) { - if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2) - timingcpt++; - - agc2_integr += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) | stv0900_read_reg(i_params, R0900_P1_AGC2I0); - - } + nb_steps = -1 + ((intp->srch_range[demod] / 1000) / currier_step); + nb_steps /= 2; + nb_steps = (2 * nb_steps) + 1; - agc2_integr /= 10; - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - current_step++; - direction *= -1; + if (nb_steps < 0) + nb_steps = 1; + else if (nb_steps > 10) { + nb_steps = 11; + currier_step = (intp->srch_range[demod] / 1000) / 10; + } - dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started. tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", tuner_freq, agc2_integr, coarse_srate, timingcpt); + current_step = 0; + direction = 1; - if ((timingcpt >= 5) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000)) { - timingLock = TRUE; - } + tuner_freq = intp->freq[demod]; - else if (current_step < nb_steps) { - if (direction > 0) - tuner_freq += (current_step * currier_step); - else - tuner_freq -= (current_step * currier_step); + while ((timing_lck == FALSE) && (current_step < nb_steps)) { + stv0900_write_reg(intp, DMDISTATE, 0x5f); + stv0900_write_bits(intp, DEMOD_MODE, 0); - stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw); - } - } + msleep(50); - if (timingLock == FALSE) - coarse_srate = 0; - else - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - break; - case STV0900_DEMOD_2: - stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0x12); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xf0); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xe0); - stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1); - stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83); - stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0); - stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82); - stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0); - stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0); - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x50); - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x6a); - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x95); - } else { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed); - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x73); - } - - if (i_params->dmd2_symbol_rate <= 2000000) - currier_step = 1000; - else if (i_params->dmd2_symbol_rate <= 5000000) - currier_step = 2000; - else if (i_params->dmd2_symbol_rate <= 12000000) - currier_step = 3000; - else - currier_step = 5000; - - - nb_steps = -1 + ((i_params->dmd2_srch_range / 1000) / currier_step); - nb_steps /= 2; - nb_steps = (2 * nb_steps) + 1; + for (i = 0; i < 10; i++) { + if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) + timingcpt++; - if (nb_steps < 0) - nb_steps = 1; - else if (nb_steps > 10) { - nb_steps = 11; - currier_step = (i_params->dmd2_srch_range / 1000) / 10; + agc2_integr += (stv0900_read_reg(intp, AGC2I1) << 8) | + stv0900_read_reg(intp, AGC2I0); } - current_step = 0; - direction = 1; - tuner_freq = i_params->tuner2_freq; - - while ((timingLock == FALSE) && (current_step < nb_steps)) { - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5F); - stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x0); - - msleep(50); - timingcpt = 0; - - for (i = 0; i < 20; i++) { - if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2) - timingcpt++; - agc2_integr += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P2_AGC2I0); - } - - agc2_integr /= 20; - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - if ((timingcpt >= 10) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000)) - timingLock = TRUE; - else { - current_step++; - direction *= -1; - - if (direction > 0) - tuner_freq += (current_step * currier_step); - else - tuner_freq -= (current_step * currier_step); + agc2_integr /= 10; + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + current_step++; + direction *= -1; + + dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started." + " tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", + tuner_freq, agc2_integr, coarse_srate, timingcpt); + + if ((timingcpt >= 5) && + (agc2_integr < agc2_th) && + (coarse_srate < 55000000) && + (coarse_srate > 850000)) + timing_lck = TRUE; + else if (current_step < nb_steps) { + if (direction > 0) + tuner_freq += (current_step * currier_step); + else + tuner_freq -= (current_step * currier_step); - stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw); - } + stv0900_set_tuner(fe, tuner_freq, intp->bw[demod]); } - - if (timingLock == FALSE) - coarse_srate = 0; - else - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - break; } + if (timing_lck == FALSE) + coarse_srate = 0; + else + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + return coarse_srate; } static u32 stv0900_search_srate_fine(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - u32 coarse_srate, - coarse_freq, - symb; + u32 coarse_srate, + coarse_freq, + symb, + symbmax, + symbmin, + symbcomp; + + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + + if (coarse_srate > 3000000) { + symbmax = 13 * (coarse_srate / 10); + symbmax = (symbmax / 1000) * 65536; + symbmax /= (intp->mclk / 1000); + + symbmin = 10 * (coarse_srate / 13); + symbmin = (symbmin / 1000)*65536; + symbmin /= (intp->mclk / 1000); + + symb = (coarse_srate / 1000) * 65536; + symb /= (intp->mclk / 1000); + } else { + symbmax = 13 * (coarse_srate / 10); + symbmax = (symbmax / 100) * 65536; + symbmax /= (intp->mclk / 100); - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); + symbmin = 10 * (coarse_srate / 14); + symbmin = (symbmin / 100) * 65536; + symbmin /= (intp->mclk / 100); - switch (demod) { - case STV0900_DEMOD_1: - default: - coarse_freq = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8) - | stv0900_read_reg(i_params, R0900_P1_CFR1); - symb = 13 * (coarse_srate / 10); - - if (symb < i_params->dmd1_symbol_rate) - coarse_srate = 0; - else { - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x00); - stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49); - else - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed); - - if (coarse_srate > 3000000) { - symb = 13 * (coarse_srate / 10); - symb = (symb / 1000) * 65536; - symb /= (i_params->mclk / 1000); - stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF)); - - symb = 10 * (coarse_srate / 13); - symb = (symb / 1000) * 65536; - symb /= (i_params->mclk / 1000); - - stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF)); - - symb = (coarse_srate / 1000) * 65536; - symb /= (i_params->mclk / 1000); - stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF)); - } else { - symb = 13 * (coarse_srate / 10); - symb = (symb / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF)); - - symb = 10 * (coarse_srate / 14); - symb = (symb / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF)); - - symb = (coarse_srate / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF)); - } + symb = (coarse_srate / 100) * 65536; + symb /= (intp->mclk / 100); + } - stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (coarse_freq >> 8) & 0xff); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, coarse_freq & 0xff); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15); - } - break; - case STV0900_DEMOD_2: - coarse_freq = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8) - | stv0900_read_reg(i_params, R0900_P2_CFR1); - - symb = 13 * (coarse_srate / 10); - - if (symb < i_params->dmd2_symbol_rate) - coarse_srate = 0; - else { - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0x00); - stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49); - else - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed); - - if (coarse_srate > 3000000) { - symb = 13 * (coarse_srate / 10); - symb = (symb / 1000) * 65536; - symb /= (i_params->mclk / 1000); - stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF)); - - symb = 10 * (coarse_srate / 13); - symb = (symb / 1000) * 65536; - symb /= (i_params->mclk / 1000); - - stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF)); - - symb = (coarse_srate / 1000) * 65536; - symb /= (i_params->mclk / 1000); - stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF)); - } else { - symb = 13 * (coarse_srate / 10); - symb = (symb / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF)); - - symb = 10 * (coarse_srate / 14); - symb = (symb / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF)); - - symb = (coarse_srate / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF)); - } + symbcomp = 13 * (coarse_srate / 10); + coarse_freq = (stv0900_read_reg(intp, CFR2) << 8) + | stv0900_read_reg(intp, CFR1); + + if (symbcomp < intp->symbol_rate[demod]) + coarse_srate = 0; + else { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, TMGCFG2, 0xc1); + stv0900_write_reg(intp, TMGTHRISE, 0x20); + stv0900_write_reg(intp, TMGTHFALL, 0x00); + stv0900_write_reg(intp, TMGCFG, 0xd2); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, AGC2REF, 0x38); + + if (intp->chip_id >= 0x30) + stv0900_write_reg(intp, CARFREQ, 0x79); + else if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x49); + else + stv0900_write_reg(intp, CARFREQ, 0xed); - stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (coarse_freq >> 8) & 0xff); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, coarse_freq & 0xff); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15); - } + stv0900_write_reg(intp, SFRUP1, (symbmax >> 8) & 0x7f); + stv0900_write_reg(intp, SFRUP0, (symbmax & 0xff)); - break; + stv0900_write_reg(intp, SFRLOW1, (symbmin >> 8) & 0x7f); + stv0900_write_reg(intp, SFRLOW0, (symbmin & 0xff)); + + stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0xff); + stv0900_write_reg(intp, SFRINIT0, (symb & 0xff)); + + stv0900_write_reg(intp, DMDT0M, 0x20); + stv0900_write_reg(intp, CFRINIT1, (coarse_freq >> 8) & 0xff); + stv0900_write_reg(intp, CFRINIT0, coarse_freq & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x15); } return coarse_srate; @@ -2265,163 +1574,135 @@ static u32 stv0900_search_srate_fine(struct dvb_frontend *fe) static int stv0900_blind_search_algo(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - u8 k_ref_tmg, k_ref_tmg_max, k_ref_tmg_min; - u32 coarse_srate; - int lock = FALSE, coarse_fail = FALSE; - s32 demod_timeout = 500, fec_timeout = 50, kref_tmg_reg, fail_cpt, i, agc2_overflow; - u16 agc2_integr; - u8 dstatus2; - - dprintk(KERN_INFO "%s\n", __func__); - - if (i_params->chip_id < 0x20) { + u8 k_ref_tmg, + k_ref_tmg_max, + k_ref_tmg_min; + u32 coarse_srate, + agc2_th; + int lock = FALSE, + coarse_fail = FALSE; + s32 demod_timeout = 500, + fec_timeout = 50, + fail_cpt, + i, + agc2_overflow; + u16 agc2_int; + u8 dstatus2; + + dprintk("%s\n", __func__); + + if (intp->chip_id < 0x20) { k_ref_tmg_max = 233; k_ref_tmg_min = 143; } else { - k_ref_tmg_max = 120; - k_ref_tmg_min = 30; + k_ref_tmg_max = 110; + k_ref_tmg_min = 10; } - agc2_integr = stv0900_blind_check_agc2_min_level(i_params, demod); - - if (agc2_integr > STV0900_BLIND_SEARCH_AGC2_TH) { - lock = FALSE; - - } else { - switch (demod) { - case STV0900_DEMOD_1: - default: - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xAA); - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55); - - stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xC4); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44); - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41); - stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41); - stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82); - stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0); - } - - kref_tmg_reg = R0900_P1_KREFTMG; - break; - case STV0900_DEMOD_2: - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xAA); - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55); + if (intp->chip_id <= 0x20) + agc2_th = STV0900_BLIND_SEARCH_AGC2_TH; + else + agc2_th = STV0900_BLIND_SEARCH_AGC2_TH_CUT30; - stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xC4); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44); + agc2_int = stv0900_blind_check_agc2_min_level(intp, demod); - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41); - stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41); - stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82); - stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0); - } + if (agc2_int > STV0900_BLIND_SEARCH_AGC2_TH) + return FALSE; - kref_tmg_reg = R0900_P2_KREFTMG; - break; - } + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0xaa); - k_ref_tmg = k_ref_tmg_max; + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x55); + else + stv0900_write_reg(intp, CARHDR, 0x20); - do { - stv0900_write_reg(i_params, kref_tmg_reg, k_ref_tmg); - if (stv0900_search_srate_coarse(fe) != 0) { - coarse_srate = stv0900_search_srate_fine(fe); + if (intp->chip_id <= 0x20) + stv0900_write_reg(intp, CARCFG, 0xc4); + else + stv0900_write_reg(intp, CARCFG, 0x6); - if (coarse_srate != 0) { - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, coarse_srate, STV0900_BLIND_SEARCH); - lock = stv0900_get_demod_lock(i_params, demod, demod_timeout); - } else - lock = FALSE; - } else { - fail_cpt = 0; - agc2_overflow = 0; + stv0900_write_reg(intp, RTCS2, 0x44); - switch (demod) { - case STV0900_DEMOD_1: - default: - for (i = 0; i < 10; i++) { - agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P1_AGC2I0); + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, EQUALCFG, 0x41); + stv0900_write_reg(intp, FFECFG, 0x41); + stv0900_write_reg(intp, VITSCALE, 0x82); + stv0900_write_reg(intp, VAVSRVIT, 0x0); + } - if (agc2_integr >= 0xff00) - agc2_overflow++; + k_ref_tmg = k_ref_tmg_max; - dstatus2 = stv0900_read_reg(i_params, R0900_P1_DSTATUS2); + do { + stv0900_write_reg(intp, KREFTMG, k_ref_tmg); + if (stv0900_search_srate_coarse(fe) != 0) { + coarse_srate = stv0900_search_srate_fine(fe); + + if (coarse_srate != 0) { + stv0900_get_lock_timeout(&demod_timeout, + &fec_timeout, + coarse_srate, + STV0900_BLIND_SEARCH); + lock = stv0900_get_demod_lock(intp, + demod, + demod_timeout); + } else + lock = FALSE; + } else { + fail_cpt = 0; + agc2_overflow = 0; - if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1)) - fail_cpt++; - } - break; - case STV0900_DEMOD_2: - for (i = 0; i < 10; i++) { - agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P2_AGC2I0); + for (i = 0; i < 10; i++) { + agc2_int = (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); - if (agc2_integr >= 0xff00) - agc2_overflow++; + if (agc2_int >= 0xff00) + agc2_overflow++; - dstatus2 = stv0900_read_reg(i_params, R0900_P2_DSTATUS2); + dstatus2 = stv0900_read_reg(intp, DSTATUS2); - if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1)) - fail_cpt++; - } - break; - } + if (((dstatus2 & 0x1) == 0x1) && + ((dstatus2 >> 7) == 1)) + fail_cpt++; + } - if ((fail_cpt > 7) || (agc2_overflow > 7)) - coarse_fail = TRUE; + if ((fail_cpt > 7) || (agc2_overflow > 7)) + coarse_fail = TRUE; - lock = FALSE; - } - k_ref_tmg -= 30; - } while ((k_ref_tmg >= k_ref_tmg_min) && (lock == FALSE) && (coarse_fail == FALSE)); - } + lock = FALSE; + } + k_ref_tmg -= 30; + } while ((k_ref_tmg >= k_ref_tmg_min) && + (lock == FALSE) && + (coarse_fail == FALSE)); return lock; } -static void stv0900_set_viterbi_acq(struct stv0900_internal *i_params, +static void stv0900_set_viterbi_acq(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - s32 vth_reg; + s32 vth_reg = VTH12; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12); - - stv0900_write_reg(i_params, vth_reg++, 0x96); - stv0900_write_reg(i_params, vth_reg++, 0x64); - stv0900_write_reg(i_params, vth_reg++, 0x36); - stv0900_write_reg(i_params, vth_reg++, 0x23); - stv0900_write_reg(i_params, vth_reg++, 0x1E); - stv0900_write_reg(i_params, vth_reg++, 0x19); + stv0900_write_reg(intp, vth_reg++, 0x96); + stv0900_write_reg(intp, vth_reg++, 0x64); + stv0900_write_reg(intp, vth_reg++, 0x36); + stv0900_write_reg(intp, vth_reg++, 0x23); + stv0900_write_reg(intp, vth_reg++, 0x1e); + stv0900_write_reg(intp, vth_reg++, 0x19); } -static void stv0900_set_search_standard(struct stv0900_internal *i_params, +static void stv0900_set_search_standard(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - int sstndrd; - - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - sstndrd = i_params->dmd1_srch_standard; - if (demod == 1) - sstndrd = i_params->dmd2_srch_stndrd; - - switch (sstndrd) { + switch (intp->srch_standard[demod]) { case STV0900_SEARCH_DVBS1: dprintk("Search Standard = DVBS1\n"); break; @@ -2436,129 +1717,74 @@ static void stv0900_set_search_standard(struct stv0900_internal *i_params, break; } - switch (demod) { - case STV0900_DEMOD_1: - default: - switch (i_params->dmd1_srch_standard) { - case STV0900_SEARCH_DVBS1: - case STV0900_SEARCH_DSS: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - - stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0); - stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x22); - - stv0900_set_viterbi_acq(i_params, demod); - stv0900_set_viterbi_standard(i_params, - i_params->dmd1_srch_standard, - i_params->dmd1_fec, demod); - - break; - case STV0900_SEARCH_DVBS2: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 1); - stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26); - if (i_params->demod_mode != STV0900_SINGLE) { - if (i_params->chip_id <= 0x11) - stv0900_stop_all_s2_modcod(i_params, demod); - else - stv0900_activate_s2_modcode(i_params, demod); - - } else - stv0900_activate_s2_modcode_single(i_params, demod); - - stv0900_set_viterbi_tracq(i_params, demod); - - break; - case STV0900_AUTO_SEARCH: - default: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0); - stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26); - if (i_params->demod_mode != STV0900_SINGLE) { - if (i_params->chip_id <= 0x11) - stv0900_stop_all_s2_modcod(i_params, demod); - else - stv0900_activate_s2_modcode(i_params, demod); + switch (intp->srch_standard[demod]) { + case STV0900_SEARCH_DVBS1: + case STV0900_SEARCH_DSS: + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 0); + stv0900_write_bits(intp, STOP_CLKVIT, 0); + stv0900_set_dvbs1_track_car_loop(intp, + demod, + intp->symbol_rate[demod]); + stv0900_write_reg(intp, CAR2CFG, 0x22); + + stv0900_set_viterbi_acq(intp, demod); + stv0900_set_viterbi_standard(intp, + intp->srch_standard[demod], + intp->fec[demod], demod); - } else - stv0900_activate_s2_modcode_single(i_params, demod); + break; + case STV0900_SEARCH_DVBS2: + stv0900_write_bits(intp, DVBS1_ENABLE, 0); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_bits(intp, STOP_CLKVIT, 1); + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ + stv0900_write_reg(intp, CAR2CFG, 0x26); + else + stv0900_write_reg(intp, CAR2CFG, 0x66); - if (i_params->dmd1_symbol_rate >= 2000000) - stv0900_set_viterbi_acq(i_params, demod); + if (intp->demod_mode != STV0900_SINGLE) { + if (intp->chip_id <= 0x11) + stv0900_stop_all_s2_modcod(intp, demod); else - stv0900_set_viterbi_tracq(i_params, demod); + stv0900_activate_s2_modcod(intp, demod); - stv0900_set_viterbi_standard(i_params, i_params->dmd1_srch_standard, i_params->dmd1_fec, demod); + } else + stv0900_activate_s2_modcod_single(intp, demod); - break; - } - break; - case STV0900_DEMOD_2: - switch (i_params->dmd2_srch_stndrd) { - case STV0900_SEARCH_DVBS1: - case STV0900_SEARCH_DSS: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0); - stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x22); - stv0900_set_viterbi_acq(i_params, demod); - stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod); - break; - case STV0900_SEARCH_DVBS2: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 1); - stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26); - if (i_params->demod_mode != STV0900_SINGLE) - stv0900_activate_s2_modcode(i_params, demod); - else - stv0900_activate_s2_modcode_single(i_params, demod); + stv0900_set_viterbi_tracq(intp, demod); - stv0900_set_viterbi_tracq(i_params, demod); - break; - case STV0900_AUTO_SEARCH: - default: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0); - stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26); - if (i_params->demod_mode != STV0900_SINGLE) - stv0900_activate_s2_modcode(i_params, demod); - else - stv0900_activate_s2_modcode_single(i_params, demod); + break; + case STV0900_AUTO_SEARCH: + default: + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_bits(intp, STOP_CLKVIT, 0); + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + stv0900_set_dvbs1_track_car_loop(intp, + demod, + intp->symbol_rate[demod]); + if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ + stv0900_write_reg(intp, CAR2CFG, 0x26); + else + stv0900_write_reg(intp, CAR2CFG, 0x66); - if (i_params->dmd2_symbol_rate >= 2000000) - stv0900_set_viterbi_acq(i_params, demod); + if (intp->demod_mode != STV0900_SINGLE) { + if (intp->chip_id <= 0x11) + stv0900_stop_all_s2_modcod(intp, demod); else - stv0900_set_viterbi_tracq(i_params, demod); + stv0900_activate_s2_modcod(intp, demod); - stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod); + } else + stv0900_activate_s2_modcod_single(intp, demod); - break; - } + stv0900_set_viterbi_tracq(intp, demod); + stv0900_set_viterbi_standard(intp, + intp->srch_standard[demod], + intp->fec[demod], demod); break; } @@ -2567,10 +1793,11 @@ static void stv0900_set_search_standard(struct stv0900_internal *i_params, enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 demod_timeout = 500, fec_timeout = 50, stream_merger_field; + s32 demod_timeout = 500, fec_timeout = 50; + s32 aq_power, agc1_power, i; int lock = FALSE, low_sr = FALSE; @@ -2578,157 +1805,117 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) enum fe_stv0900_search_algo algo; int no_signal = FALSE; - dprintk(KERN_INFO "%s\n", __func__); - - switch (demod) { - case STV0900_DEMOD_1: - default: - algo = i_params->dmd1_srch_algo; + dprintk("%s\n", __func__); - stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1); - stream_merger_field = F0900_P1_RST_HWARE; - - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C); - - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x9e); + algo = intp->srch_algo[demod]; + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_reg(intp, DMDISTATE, 0x5c); + if (intp->chip_id >= 0x20) { + if (intp->symbol_rate[demod] > 5000000) + stv0900_write_reg(intp, CORRELABS, 0x9e); else - stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x88); - - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd1_symbol_rate, i_params->dmd1_srch_algo); - - if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) { - i_params->tuner1_bw = 2 * 36000000; - - stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x00); - stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70); - - stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod); - } else { - stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20); - stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2); - - if (i_params->dmd1_symbol_rate < 2000000) - stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x63); - else - stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70); - - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38); - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0x5a); - - if (i_params->dmd1_srch_algo == STV0900_COLD_START) - i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10; - else if (i_params->dmd1_srch_algo == STV0900_WARM_START) - i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000; - } else { - stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0xc1); - i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10; - } - - stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01); - - stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod); - stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod); - stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod); - if (i_params->dmd1_symbol_rate >= 10000000) - low_sr = FALSE; - else - low_sr = TRUE; - - } - - stv0900_set_tuner(fe, i_params->tuner1_freq, i_params->tuner1_bw); - - stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, i_params->dmd1_srch_iq_inv); - stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1); - - stv0900_set_search_standard(i_params, demod); + stv0900_write_reg(intp, CORRELABS, 0x82); + } else + stv0900_write_reg(intp, CORRELABS, 0x88); - if (i_params->dmd1_srch_algo != STV0900_BLIND_SEARCH) - stv0900_start_search(i_params, demod); - break; - case STV0900_DEMOD_2: - algo = i_params->dmd2_srch_algo; + stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, + intp->symbol_rate[demod], + intp->srch_algo[demod]); - stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1); + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { + intp->bw[demod] = 2 * 36000000; - stream_merger_field = F0900_P2_RST_HWARE; + stv0900_write_reg(intp, TMGCFG2, 0xc0); + stv0900_write_reg(intp, CORRELMANT, 0x70); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C); + stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); + } else { + stv0900_write_reg(intp, DMDT0M, 0x20); + stv0900_write_reg(intp, TMGCFG, 0xd2); - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x9e); + if (intp->symbol_rate[demod] < 2000000) + stv0900_write_reg(intp, CORRELMANT, 0x63); else - stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x88); + stv0900_write_reg(intp, CORRELMANT, 0x70); - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd2_symbol_rate, i_params->dmd2_srch_algo); + stv0900_write_reg(intp, AGC2REF, 0x38); - if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) { - i_params->tuner2_bw = 2 * 36000000; + intp->bw[demod] = + stv0900_carrier_width(intp->symbol_rate[demod], + intp->rolloff); + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, KREFTMG, 0x5a); - stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x00); - stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70); + if (intp->srch_algo[demod] == STV0900_COLD_START) { + intp->bw[demod] += 10000000; + intp->bw[demod] *= 15; + intp->bw[demod] /= 10; + } else if (intp->srch_algo[demod] == STV0900_WARM_START) + intp->bw[demod] += 10000000; - stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod); } else { - stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20); - stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2); + stv0900_write_reg(intp, KREFTMG, 0xc1); + intp->bw[demod] += 10000000; + intp->bw[demod] *= 15; + intp->bw[demod] /= 10; + } - if (i_params->dmd2_symbol_rate < 2000000) - stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x63); - else - stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70); + stv0900_write_reg(intp, TMGCFG2, 0xc1); - if (i_params->dmd2_symbol_rate >= 10000000) - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38); - else - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x60); + stv0900_set_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + stv0900_set_max_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + stv0900_set_min_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + if (intp->symbol_rate[demod] >= 10000000) + low_sr = FALSE; + else + low_sr = TRUE; - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0x5a); + } - if (i_params->dmd2_srch_algo == STV0900_COLD_START) - i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate, - i_params->rolloff) + 10000000)) / 10; - else if (i_params->dmd2_srch_algo == STV0900_WARM_START) - i_params->tuner2_bw = stv0900_carrier_width(i_params->dmd2_symbol_rate, - i_params->rolloff) + 10000000; - } else { - stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0xc1); - i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate, - i_params->rolloff) + 10000000)) / 10; - } + stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]); - stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01); + agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), + stv0900_get_bits(intp, AGCIQ_VALUE0)); - stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod); - stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod); - stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod); - if (i_params->dmd2_symbol_rate >= 10000000) - low_sr = FALSE; - else - low_sr = TRUE; + aq_power = 0; - } + if (agc1_power == 0) { + for (i = 0; i < 5; i++) + aq_power += (stv0900_get_bits(intp, POWER_I) + + stv0900_get_bits(intp, POWER_Q)) / 2; - stv0900_set_tuner(fe, i_params->tuner2_freq, i_params->tuner2_bw); + aq_power /= 5; + } - stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, i_params->dmd2_srch_iq_inv); - stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1); + if ((agc1_power == 0) && (aq_power < IQPOWER_THRESHOLD)) { + intp->result[demod].locked = FALSE; + signal_type = STV0900_NOAGC1; + dprintk("%s: NO AGC1, POWERI, POWERQ\n", __func__); + } else { + stv0900_write_bits(intp, SPECINV_CONTROL, + intp->srch_iq_inv[demod]); + if (intp->chip_id <= 0x20) /*cut 2.0*/ + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); + else /*cut 3.0*/ + stv0900_write_bits(intp, MANUALS2_ROLLOFF, 1); - stv0900_set_search_standard(i_params, demod); + stv0900_set_search_standard(intp, demod); - if (i_params->dmd2_srch_algo != STV0900_BLIND_SEARCH) - stv0900_start_search(i_params, demod); - break; + if (intp->srch_algo[demod] != STV0900_BLIND_SEARCH) + stv0900_start_search(intp, demod); } - if (i_params->chip_id == 0x12) { - stv0900_write_bits(i_params, stream_merger_field, 0); + if (signal_type == STV0900_NOAGC1) + return signal_type; + + if (intp->chip_id == 0x12) { + stv0900_write_bits(intp, RST_HWARE, 0); msleep(3); - stv0900_write_bits(i_params, stream_merger_field, 1); - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); } if (algo == STV0900_BLIND_SEARCH) @@ -2736,12 +1923,12 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) else if (algo == STV0900_COLD_START) lock = stv0900_get_demod_cold_lock(fe, demod_timeout); else if (algo == STV0900_WARM_START) - lock = stv0900_get_demod_lock(i_params, demod, demod_timeout); + lock = stv0900_get_demod_lock(intp, demod, demod_timeout); if ((lock == FALSE) && (algo == STV0900_COLD_START)) { if (low_sr == FALSE) { - if (stv0900_check_timing_lock(i_params, demod) == TRUE) - lock = stv0900_sw_algo(i_params, demod); + if (stv0900_check_timing_lock(intp, demod) == TRUE) + lock = stv0900_sw_algo(intp, demod); } } @@ -2750,98 +1937,64 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) if ((lock == TRUE) && (signal_type == STV0900_RANGEOK)) { stv0900_track_optimization(fe); - if (i_params->chip_id <= 0x11) { - if ((stv0900_get_standard(fe, STV0900_DEMOD_1) == STV0900_DVBS1_STANDARD) && (stv0900_get_standard(fe, STV0900_DEMOD_2) == STV0900_DVBS1_STANDARD)) { + if (intp->chip_id <= 0x11) { + if ((stv0900_get_standard(fe, 0) == + STV0900_DVBS1_STANDARD) && + (stv0900_get_standard(fe, 1) == + STV0900_DVBS1_STANDARD)) { msleep(20); - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 0); } else { - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 0); msleep(3); - stv0900_write_bits(i_params, stream_merger_field, 1); - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); } - } else if (i_params->chip_id == 0x20) { - stv0900_write_bits(i_params, stream_merger_field, 0); + + } else if (intp->chip_id >= 0x20) { + stv0900_write_bits(intp, RST_HWARE, 0); msleep(3); - stv0900_write_bits(i_params, stream_merger_field, 1); - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); } - if (stv0900_wait_for_lock(i_params, demod, fec_timeout, fec_timeout) == TRUE) { + if (stv0900_wait_for_lock(intp, demod, + fec_timeout, fec_timeout) == TRUE) { lock = TRUE; - switch (demod) { - case STV0900_DEMOD_1: - default: - i_params->dmd1_rslts.locked = TRUE; - if (i_params->dmd1_rslts.standard == STV0900_DVBS2_STANDARD) { - stv0900_set_dvbs2_rolloff(i_params, demod); - stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0x40); - stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67); - } else { - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75); - } - - stv0900_write_reg(i_params, R0900_P1_FBERCPT4, 0); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL2, 0xc1); - break; - case STV0900_DEMOD_2: - i_params->dmd2_rslts.locked = TRUE; - - if (i_params->dmd2_rslts.standard == STV0900_DVBS2_STANDARD) { - stv0900_set_dvbs2_rolloff(i_params, demod); - stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x60); - stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x20); - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67); - } else { - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75); - } - - stv0900_write_reg(i_params, R0900_P2_FBERCPT4, 0); - - stv0900_write_reg(i_params, R0900_P2_ERRCTRL2, 0xc1); - break; + intp->result[demod].locked = TRUE; + if (intp->result[demod].standard == + STV0900_DVBS2_STANDARD) { + stv0900_set_dvbs2_rolloff(intp, demod); + stv0900_write_bits(intp, RESET_UPKO_COUNT, 1); + stv0900_write_bits(intp, RESET_UPKO_COUNT, 0); + stv0900_write_reg(intp, ERRCTRL1, 0x67); + } else { + stv0900_write_reg(intp, ERRCTRL1, 0x75); } + + stv0900_write_reg(intp, FBERCPT4, 0); + stv0900_write_reg(intp, ERRCTRL2, 0xc1); } else { lock = FALSE; signal_type = STV0900_NODATA; - no_signal = stv0900_check_signal_presence(i_params, demod); - - switch (demod) { - case STV0900_DEMOD_1: - default: - i_params->dmd1_rslts.locked = FALSE; - break; - case STV0900_DEMOD_2: - i_params->dmd2_rslts.locked = FALSE; - break; - } + no_signal = stv0900_check_signal_presence(intp, demod); + + intp->result[demod].locked = FALSE; } } - if ((signal_type == STV0900_NODATA) && (no_signal == FALSE)) { - switch (demod) { - case STV0900_DEMOD_1: - default: - if (i_params->chip_id <= 0x11) { - if ((stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) && - (i_params->dmd1_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST)) - signal_type = stv0900_dvbs1_acq_workaround(fe); - } else - i_params->dmd1_rslts.locked = FALSE; + if ((signal_type != STV0900_NODATA) || (no_signal != FALSE)) + return signal_type; - break; - case STV0900_DEMOD_2: - if (i_params->chip_id <= 0x11) { - if ((stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) && - (i_params->dmd2_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST)) - signal_type = stv0900_dvbs1_acq_workaround(fe); - } else - i_params->dmd2_rslts.locked = FALSE; - break; - } + if (intp->chip_id > 0x11) { + intp->result[demod].locked = FALSE; + return signal_type; } + if ((stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) && + (intp->srch_iq_inv[demod] <= STV0900_IQ_AUTO_NORMAL_FIRST)) + signal_type = stv0900_dvbs1_acq_workaround(fe); + return signal_type; } diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 488bdfb34fb3..48edd542242e 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -825,7 +825,7 @@ static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate sym /= (state->mclk >> 7); } - if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0xff)) < 0) /* MSB */ + if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */ goto err; if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */ goto err; @@ -1238,6 +1238,8 @@ static int stv090x_delivery_search(struct stv090x_state *state) goto err; } + if (stv090x_set_vit_thtracq(state) < 0) + goto err; break; case STV090x_SEARCH_AUTO: @@ -1278,17 +1280,8 @@ static int stv090x_delivery_search(struct stv090x_state *state) goto err; } - if (state->srate >= 2000000) { - /* Srate >= 2MSPS, Viterbi threshold to acquire */ - if (stv090x_set_vit_thacq(state) < 0) - goto err; - } else { - /* Srate < 2MSPS, Reset Viterbi thresholdto track - * and then re-acquire - */ - if (stv090x_set_vit_thtracq(state) < 0) - goto err; - } + if (stv090x_set_vit_thacq(state) < 0) + goto err; if (stv090x_set_viterbi(state) < 0) goto err; @@ -1317,7 +1310,7 @@ static int stv090x_start_search(struct stv090x_state *state) goto err; if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, CFRUP1, 0xff) < 0) + if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0) goto err; if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0) goto err; @@ -1371,7 +1364,7 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, CFRUP1, LSB(freq)) < 0) + if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0) goto err; freq *= -1; @@ -1422,6 +1415,9 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) + goto err; + if (state->dev_ver >= 0x20) { /*Frequency offset detector setting*/ if (state->srate < 2000000) { @@ -1430,20 +1426,22 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0) goto err; } else { - /* Cut 2 */ + /* Cut 3 */ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0) goto err; } if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0) goto err; - } - - if (state->srate < 10000000) { + } else if (state->srate < 10000000) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) + goto err; } else { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) + goto err; } } else { if (state->srate < 10000000) { @@ -1485,14 +1483,14 @@ err: static int stv090x_get_agc2_min_level(struct stv090x_state *state) { - u32 agc2_min = 0, agc2 = 0, freq_init, freq_step, reg; + u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg; s32 i, j, steps, dir; if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) goto err; reg = STV090x_READ_DEMOD(state, DMDCFGMD); - STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1); - STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 1); + STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) goto err; @@ -1509,10 +1507,8 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state) if (stv090x_set_srate(state, 1000000) < 0) goto err; - steps = -1 + state->search_range / 1000000; - steps /= 2; - steps = (2 * steps) + 1; - if (steps < 0) + steps = state->search_range / 1000000; + if (steps <= 0) steps = 1; dir = 1; @@ -1525,7 +1521,7 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state) else freq_init = freq_init - (freq_step * i); - dir = -1; + dir *= -1; if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */ goto err; @@ -1536,13 +1532,14 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */ goto err; msleep(10); + + agc2 = 0; for (j = 0; j < 10; j++) { - agc2 += STV090x_READ_DEMOD(state, AGC2I1) << 8; - agc2 |= STV090x_READ_DEMOD(state, AGC2I0); + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); } agc2 /= 10; - agc2_min = 0xffff; - if (agc2 < 0xffff) + if (agc2 < agc2_min) agc2_min = agc2; } @@ -1584,6 +1581,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) int tmg_lock = 0, i; s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq; u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg; + u32 agc2th; + + if (state->dev_ver >= 0x30) + agc2th = 0x2e00; + else + agc2th = 0x1f00; reg = STV090x_READ_DEMOD(state, DMDISTATE); STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */ @@ -1591,13 +1594,15 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) goto err; if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) + goto err; if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0) goto err; if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0) goto err; reg = STV090x_READ_DEMOD(state, DMDCFGMD); STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1); - STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 1); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) goto err; @@ -1611,13 +1616,13 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) goto err; if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x60) < 0) + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0) goto err; if (state->dev_ver >= 0x30) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0) + if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0) goto err; } else if (state->dev_ver >= 0x20) { @@ -1652,23 +1657,31 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) while ((!tmg_lock) && (cur_step < steps)) { if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */ goto err; - reg = STV090x_READ_DEMOD(state, DMDISTATE); - STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x00); /* trigger acquisition */ - if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) + if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0) + goto err; + /* trigger acquisition */ + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0) goto err; msleep(50); for (i = 0; i < 10; i++) { reg = STV090x_READ_DEMOD(state, DSTATUS); if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2) tmg_cpt++; - agc2 += STV090x_READ_DEMOD(state, AGC2I1) << 8; - agc2 |= STV090x_READ_DEMOD(state, AGC2I0); + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); } agc2 /= 10; srate_coarse = stv090x_get_srate(state, state->mclk); cur_step++; dir *= -1; - if ((tmg_cpt >= 5) && (agc2 < 0x1f00) && (srate_coarse < 55000000) && (srate_coarse > 850000)) + if ((tmg_cpt >= 5) && (agc2 < agc2th) && + (srate_coarse < 50000000) && (srate_coarse > 850000)) tmg_lock = 1; else if (cur_step < steps) { if (dir > 0) @@ -1681,7 +1694,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) goto err; if (state->config->tuner_set_frequency) { - if (state->config->tuner_set_frequency(fe, state->frequency) < 0) + if (state->config->tuner_set_frequency(fe, freq) < 0) goto err; } @@ -1738,7 +1751,7 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state) else { if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */ goto err; - if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0x01) < 0) + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) goto err; if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0) goto err; @@ -1751,6 +1764,9 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) + goto err; + if (state->dev_ver >= 0x30) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0) goto err; @@ -1856,12 +1872,12 @@ static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout) static int stv090x_blind_search(struct stv090x_state *state) { u32 agc2, reg, srate_coarse; - s32 timeout_dmd = 500, cpt_fail, agc2_ovflw, i; + s32 cpt_fail, agc2_ovflw, i; u8 k_ref, k_max, k_min; int coarse_fail, lock; - k_max = 120; - k_min = 30; + k_max = 110; + k_min = 10; agc2 = stv090x_get_agc2_min_level(state); @@ -1900,7 +1916,8 @@ static int stv090x_blind_search(struct stv090x_state *state) srate_coarse = stv090x_srate_srch_fine(state); if (srate_coarse != 0) { stv090x_get_lock_tmg(state); - lock = stv090x_get_dmdlock(state, timeout_dmd); + lock = stv090x_get_dmdlock(state, + state->DemodTimeout); } else { lock = 0; } @@ -1908,8 +1925,8 @@ static int stv090x_blind_search(struct stv090x_state *state) cpt_fail = 0; agc2_ovflw = 0; for (i = 0; i < 10; i++) { - agc2 = STV090x_READ_DEMOD(state, AGC2I1) << 8; - agc2 |= STV090x_READ_DEMOD(state, AGC2I0); + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); if (agc2 >= 0xff00) agc2_ovflw++; reg = STV090x_READ_DEMOD(state, DSTATUS2); @@ -1923,7 +1940,7 @@ static int stv090x_blind_search(struct stv090x_state *state) lock = 0; } - k_ref -= 30; + k_ref -= 20; } while ((k_ref >= k_min) && (!lock) && (!coarse_fail)); } @@ -2062,7 +2079,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) goto err; if (state->config->tuner_set_frequency) { - if (state->config->tuner_set_frequency(fe, state->frequency) < 0) + if (state->config->tuner_set_frequency(fe, freq) < 0) goto err; } @@ -2093,17 +2110,6 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) goto err; STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c); - if (state->delsys == STV090x_DVBS2) { - reg = STV090x_READ_DEMOD(state, DMDCFGMD); - STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); - STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); - if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) - goto err; - STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); - STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); - if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) - goto err; - } if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) goto err; if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) @@ -2425,7 +2431,7 @@ static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk) derot = (int_1 * int_2) + ((int_1 * tmp_2) >> 12) + - ((int_1 * tmp_1) >> 12); + ((int_2 * tmp_1) >> 12); return derot; } @@ -2732,7 +2738,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) switch (state->delsys) { case STV090x_DVBS1: case STV090x_DSS: - if (state->algo == STV090x_SEARCH_AUTO) { + if (state->search_mode == STV090x_SEARCH_AUTO) { reg = STV090x_READ_DEMOD(state, DMDCFGMD); STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); @@ -2857,6 +2863,9 @@ static int stv090x_optimize_track(struct stv090x_state *state) if (stv090x_set_srate(state, srate) < 0) goto err; blind_tune = 1; + + if (stv090x_dvbs_track_crl(state) < 0) + goto err; } if (state->dev_ver >= 0x20) { @@ -3042,7 +3051,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) struct dvb_frontend *fe = &state->frontend; enum stv090x_signal_state signal_state = STV090x_NOCARRIER; u32 reg; - s32 timeout_dmd = 500, timeout_fec = 50, agc1_power, power_iq = 0, i; + s32 agc1_power, power_iq = 0, i; int lock = 0, low_sr = 0, no_signal = 0; reg = STV090x_READ_DEMOD(state, TSCFGH); @@ -3054,8 +3063,13 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) goto err; if (state->dev_ver >= 0x20) { - if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) /* cut 2.0 */ - goto err; + if (state->srate > 5000000) { + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0) + goto err; + } } stv090x_get_lock_tmg(state); @@ -3175,7 +3189,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) { dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq); lock = 0; - + signal_state = STV090x_NOAGC1; } else { reg = STV090x_READ_DEMOD(state, DEMOD); STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion); @@ -3199,18 +3213,17 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) } } - /* need to check for AGC1 state */ - - + if (signal_state == STV090x_NOAGC1) + return signal_state; if (state->algo == STV090x_BLIND_SEARCH) lock = stv090x_blind_search(state); else if (state->algo == STV090x_COLD_SEARCH) - lock = stv090x_get_coldlock(state, timeout_dmd); + lock = stv090x_get_coldlock(state, state->DemodTimeout); else if (state->algo == STV090x_WARM_SEARCH) - lock = stv090x_get_dmdlock(state, timeout_dmd); + lock = stv090x_get_dmdlock(state, state->DemodTimeout); if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) { if (!low_sr) { @@ -3245,8 +3258,9 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) goto err; } - if (stv090x_get_lock(state, timeout_fec, timeout_fec)) { - lock = 1; + lock = stv090x_get_lock(state, state->FecTimeout, + state->FecTimeout); + if (lock) { if (state->delsys == STV090x_DVBS2) { stv090x_set_s2rolloff(state); @@ -3273,7 +3287,6 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0) goto err; } else { - lock = 0; signal_state = STV090x_NODATA; no_signal = stv090x_chk_signal(state); } @@ -3296,7 +3309,13 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron state->search_mode = STV090x_SEARCH_AUTO; state->algo = STV090x_COLD_SEARCH; state->fec = STV090x_PRERR; - state->search_range = 2000000; + if (state->srate > 10000000) { + dprintk(FE_DEBUG, 1, "Search range: 10 MHz"); + state->search_range = 10000000; + } else { + dprintk(FE_DEBUG, 1, "Search range: 5 MHz"); + state->search_range = 5000000; + } if (stv090x_algo(state) == STV090x_RANGEOK) { dprintk(FE_DEBUG, 1, "Search success!"); @@ -3309,7 +3328,6 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron return DVBFE_ALGO_SEARCH_ERROR; } -/* FIXME! */ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct stv090x_state *state = fe->demodulator_priv; @@ -3331,9 +3349,15 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2"); reg = STV090x_READ_DEMOD(state, DSTATUS); if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) { - reg = STV090x_READ_DEMOD(state, TSSTATUS); - if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) { - *status = FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + reg = STV090x_READ_DEMOD(state, PDELSTATUS1); + if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) { + reg = STV090x_READ_DEMOD(state, TSSTATUS); + if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) { + *status = FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } } } break; @@ -3412,14 +3436,12 @@ static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val) int res = 0; int min = 0, med; - if (val < tab[min].read) - res = tab[min].real; - else if (val >= tab[max].read) - res = tab[max].real; - else { + if ((val >= tab[min].read && val < tab[max].read) || + (val >= tab[max].read && val < tab[min].read)) { while ((max - min) > 1) { med = (max + min) / 2; - if (val >= tab[min].read && val < tab[med].read) + if ((val >= tab[min].read && val < tab[med].read) || + (val >= tab[med].read && val < tab[min].read)) max = med; else min = med; @@ -3428,6 +3450,18 @@ static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val) (tab[max].real - tab[min].real) / (tab[max].read - tab[min].read)) + tab[min].real; + } else { + if (tab[min].read < tab[max].read) { + if (val < tab[min].read) + res = tab[min].real; + else if (val >= tab[max].read) + res = tab[max].real; + } else { + if (val >= tab[min].read) + res = tab[min].real; + else if (val < tab[max].read) + res = tab[max].real; + } } return res; @@ -3437,16 +3471,22 @@ static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct stv090x_state *state = fe->demodulator_priv; u32 reg; - s32 agc; + s32 agc_0, agc_1, agc; + s32 str; reg = STV090x_READ_DEMOD(state, AGCIQIN1); - agc = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); + agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); + reg = STV090x_READ_DEMOD(state, AGCIQIN0); + agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); + agc = MAKEWORD16(agc_1, agc_0); - *strength = stv090x_table_lookup(stv090x_rf_tab, ARRAY_SIZE(stv090x_rf_tab) - 1, agc); + str = stv090x_table_lookup(stv090x_rf_tab, + ARRAY_SIZE(stv090x_rf_tab) - 1, agc); if (agc > stv090x_rf_tab[0].read) - *strength = 5; + str = 0; else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read) - *strength = -100; + str = -100; + *strength = (str + 100) * 0xFFFF / 100; return 0; } @@ -3457,6 +3497,8 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) u32 reg_0, reg_1, reg, i; s32 val_0, val_1, val = 0; u8 lock_f; + s32 div; + u32 last; switch (state->delsys) { case STV090x_DVBS2: @@ -3468,14 +3510,15 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1); val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD); reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0); - val_0 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD); + val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD); val += MAKEWORD16(val_1, val_0); msleep(1); } val /= 16; - *cnr = stv090x_table_lookup(stv090x_s2cn_tab, ARRAY_SIZE(stv090x_s2cn_tab) - 1, val); - if (val < stv090x_s2cn_tab[ARRAY_SIZE(stv090x_s2cn_tab) - 1].read) - *cnr = 1000; + last = ARRAY_SIZE(stv090x_s2cn_tab) - 1; + div = stv090x_s2cn_tab[0].read - + stv090x_s2cn_tab[last].read; + *cnr = 0xFFFF - ((val * 0xFFFF) / div); } break; @@ -3489,14 +3532,15 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1); val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD); reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0); - val_0 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD); + val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD); val += MAKEWORD16(val_1, val_0); msleep(1); } val /= 16; - *cnr = stv090x_table_lookup(stv090x_s1cn_tab, ARRAY_SIZE(stv090x_s1cn_tab) - 1, val); - if (val < stv090x_s2cn_tab[ARRAY_SIZE(stv090x_s1cn_tab) - 1].read) - *cnr = 1000; + last = ARRAY_SIZE(stv090x_s1cn_tab) - 1; + div = stv090x_s1cn_tab[0].read - + stv090x_s1cn_tab[last].read; + *cnr = 0xFFFF - ((val * 0xFFFF) / div); } break; default: @@ -3732,6 +3776,8 @@ static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc { u32 reg = 0; + reg = stv090x_read_reg(state, STV090x_GENCFG); + switch (ldpc_mode) { case STV090x_DUAL: default: diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb/frontends/stv090x_priv.h index 5a4a01740d88..5921a8d6c89f 100644 --- a/drivers/media/dvb/frontends/stv090x_priv.h +++ b/drivers/media/dvb/frontends/stv090x_priv.h @@ -83,7 +83,7 @@ #define STV090x_IQPOWER_THRESHOLD 30 #define STV090x_SEARCH_AGC2_TH_CUT20 700 -#define STV090x_SEARCH_AGC2_TH_CUT30 1200 +#define STV090x_SEARCH_AGC2_TH_CUT30 1400 #define STV090x_SEARCH_AGC2_TH(__ver) \ ((__ver <= 0x20) ? \ @@ -91,6 +91,7 @@ STV090x_SEARCH_AGC2_TH_CUT30) enum stv090x_signal_state { + STV090x_NOAGC1, STV090x_NOCARRIER, STV090x_NODATA, STV090x_DATAOK, diff --git a/drivers/media/dvb/frontends/stv090x_reg.h b/drivers/media/dvb/frontends/stv090x_reg.h index 57b6abbbd32d..2502855dd784 100644 --- a/drivers/media/dvb/frontends/stv090x_reg.h +++ b/drivers/media/dvb/frontends/stv090x_reg.h @@ -44,7 +44,7 @@ #define STV090x_OFFST_OUTSERRS2_HZ_FIELD 5 #define STV090x_WIDTH_OUTSERRS2_HZ_FIELD 1 #define STV090x_OFFST_OUTSERRS3_HZ_FIELD 4 -#define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1 +#define STV090x_WIDTH_OUTSERRS3_HZ_FIELD 1 #define STV090x_OFFST_OUTPARRS3_HZ_FIELD 3 #define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1 @@ -113,24 +113,24 @@ #define STV090x_IRQMASK3 0xf124 #define STV090x_OFFST_MPLL_LOCK_FIELD 5 #define STV090x_WIDTH_MPLL_LOCK_FIELD 1 -#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 2 -#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 3 -#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 2 -#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 3 +#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 4 +#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 1 +#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 3 +#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 1 #define STV090x_OFFST_MSTREAM_LCK_1_FIELD 2 -#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 3 +#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 1 #define STV090x_OFFST_MDVBS1_PRF_2_FIELD 1 #define STV090x_WIDTH_MDVBS1_PRF_2_FIELD 1 #define STV090x_OFFST_MDVBS1_PRF_1_FIELD 0 #define STV090x_WIDTH_MDVBS1_PRF_1_FIELD 1 #define STV090x_IRQMASK2 0xf125 -#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 5 -#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 3 -#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 5 -#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 3 +#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 7 +#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 1 +#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 6 +#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 1 #define STV090x_OFFST_MSPY_ENDSIM_1_FIELD 5 -#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 3 +#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 1 #define STV090x_OFFST_MPKTDEL_ERROR_2_FIELD 4 #define STV090x_WIDTH_MPKTDEL_ERROR_2_FIELD 1 #define STV090x_OFFST_MPKTDEL_LOCKB_2_FIELD 3 @@ -370,7 +370,7 @@ #define STV090x_OFFST_SELX1RATIO_FIELD 5 #define STV090x_WIDTH_SELX1RATIO_FIELD 1 #define STV090x_OFFST_STOP_PLL_FIELD 3 -#define STV090x_WIDTH_SELX1RATIO_FIELD 1 +#define STV090x_WIDTH_STOP_PLL_FIELD 1 #define STV090x_OFFST_BYPASSPLLFSK_FIELD 2 #define STV090x_WIDTH_BYPASSPLLFSK_FIELD 1 #define STV090x_OFFST_SELOSCI_FIELD 1 @@ -616,7 +616,7 @@ #define STV090x_OFFST_Px_CONT_TONE_FIELD 4 #define STV090x_WIDTH_Px_CONT_TONE_FIELD 1 #define STV090x_OFFST_Px_FIFO_4BREADY_FIELD 3 -#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 2 +#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 1 #define STV090x_OFFST_Px_FIFO_EMPTY_FIELD 2 #define STV090x_WIDTH_Px_FIFO_EMPTY_FIELD 1 #define STV090x_OFFST_Px_ABORT_DISRX_FIELD 0 @@ -847,12 +847,10 @@ #define STV090x_WIDTH_Px_DVBS2_ENABLE_FIELD 1 #define STV090x_OFFST_Px_DVBS1_ENABLE_FIELD 6 #define STV090x_WIDTH_Px_DVBS1_ENABLE_FIELD 1 -#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 5 /* check */ -#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1 -#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4 /* check */ +#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4 #define STV090x_WIDTH_Px_SCAN_ENABLE_FIELD 1 -#define STV090x_OFFST_Px_TUN_AUTOSCAN_FIELD 3 -#define STV090x_WIDTH_Px_TUN_AUTOSCAN_FIELD 1 +#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 3 +#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1 #define STV090x_OFFST_Px_NOFORCE_RELOCK_FIELD 2 #define STV090x_WIDTH_Px_NOFORCE_RELOCK_FIELD 1 #define STV090x_OFFST_Px_TUN_RNG_FIELD 0 @@ -885,7 +883,7 @@ #define STV090x_P2_DMDFLYW STV090x_Px_DMDFLYW(2) #define STV090x_OFFST_Px_I2C_IRQVAL_FIELD 4 #define STV090x_WIDTH_Px_I2C_IRQVAL_FIELD 4 -#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0 /* check */ +#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0 #define STV090x_WIDTH_Px_FLYWHEEL_CPT_FIELD 4 #define STV090x_Px_DSTATUS3(__x) (0xF41D - (__x - 1) * 0x200) @@ -1048,12 +1046,12 @@ #define STV090x_P1_CFRINC1 STV090x_Px_CFRINC1(1) #define STV090x_P2_CFRINC1 STV090x_Px_CFRINC1(2) #define STV090x_OFFST_Px_CFR_INC1_FIELD 0 -#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7 +#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7 /* check */ #define STV090x_Px_CFRINC0(__x) (0xF44B - (__x - 1) * 0x200) #define STV090x_P1_CFRINC0 STV090x_Px_CFRINC0(1) #define STV090x_P2_CFRINC0 STV090x_Px_CFRINC0(2) -#define STV090x_OFFST_Px_CFR_INC0_FIELD 4 +#define STV090x_OFFST_Px_CFR_INC0_FIELD 4 /* check */ #define STV090x_WIDTH_Px_CFR_INC0_FIELD 4 #define STV090x_Pn_CFRy(__x, __y) (0xF44E - (__x - 1) * 0x200 - __y * 0x1) @@ -1145,14 +1143,14 @@ #define STV090x_Px_SFRINIT1(__x) (0xF45E - (__x - 1) * 0x200) #define STV090x_P1_SFRINIT1 STV090x_Px_SFRINIT1(1) #define STV090x_P2_SFRINIT1 STV090x_Px_SFRINIT1(2) -#define STV090x_OFFST_Px_SFR_INIT_FIELD 0 -#define STV090x_WIDTH_Px_SFR_INIT_FIELD 8 +#define STV090x_OFFST_Px_SFR_INIT1_FIELD 0 +#define STV090x_WIDTH_Px_SFR_INIT1_FIELD 7 #define STV090x_Px_SFRINIT0(__x) (0xF45F - (__x - 1) * 0x200) #define STV090x_P1_SFRINIT0 STV090x_Px_SFRINIT0(1) #define STV090x_P2_SFRINIT0 STV090x_Px_SFRINIT0(2) -#define STV090x_OFFST_Px_SFR_INIT_FIELD 0 -#define STV090x_WIDTH_Px_SFR_INIT_FIELD 8 +#define STV090x_OFFST_Px_SFR_INIT0_FIELD 0 +#define STV090x_WIDTH_Px_SFR_INIT0_FIELD 8 #define STV090x_Px_SFRUP1(__x) (0xF460 - (__x - 1) * 0x200) #define STV090x_P1_SFRUP1 STV090x_Px_SFRUP1(1) @@ -1178,7 +1176,7 @@ #define STV090x_OFFST_Px_SYMB_FREQ_LOW0_FIELD 0 #define STV090x_WIDTH_Px_SYMB_FREQ_LOW0_FIELD 8 -#define STV090x_Px_SFRy(__x, __y) (0xF464 - (__x-1) * 0x200 + (3 - __y)) +#define STV090x_Px_SFRy(__x, __y) (0xF467 - (__x-1) * 0x200 - __y) #define STV090x_P1_SFR0 STV090x_Px_SFRy(1, 0) #define STV090x_P1_SFR1 STV090x_Px_SFRy(1, 1) #define STV090x_P1_SFR2 STV090x_Px_SFRy(1, 2) @@ -1188,7 +1186,7 @@ #define STV090x_P2_SFR2 STV090x_Px_SFRy(2, 2) #define STV090x_P2_SFR3 STV090x_Px_SFRy(2, 3) #define STV090x_OFFST_Px_SYMB_FREQ_FIELD 0 -#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 32 +#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 8 #define STV090x_Px_TMGREG2(__x) (0xF468 - (__x - 1) * 0x200) #define STV090x_P1_TMGREG2 STV090x_Px_TMGREG2(1) @@ -1198,7 +1196,7 @@ #define STV090x_Px_TMGREG1(__x) (0xF469 - (__x - 1) * 0x200) #define STV090x_P1_TMGREG1 STV090x_Px_TMGREG1(1) -#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2) +#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2) #define STV090x_OFFST_Px_TMGREG_FIELD 0 #define STV090x_WIDTH_Px_TMGREG_FIELD 8 @@ -1230,7 +1228,7 @@ #define STV090x_OFFST_Px_MU_EQUALDFE_FIELD 0 #define STV090x_WIDTH_Px_MU_EQUALDFE_FIELD 3 -#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x - 1) * 0x200 + (__y - 1)) +#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x-1) * 0x200 + 2 * (__y-1)) #define STV090x_P1_EQUAI1 STV090x_Px_EQUAIy(1, 1) #define STV090x_P1_EQUAI2 STV090x_Px_EQUAIy(1, 2) #define STV090x_P1_EQUAI3 STV090x_Px_EQUAIy(1, 3) @@ -1251,7 +1249,7 @@ #define STV090x_OFFST_Px_EQUA_ACCIy_FIELD 0 #define STV090x_WIDTH_Px_EQUA_ACCIy_FIELD 8 -#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x - 1) * 0x200 + (__y - 1)) +#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x-1) * 0x200 + 2 * (__y-1)) #define STV090x_P1_EQUAQ1 STV090x_Px_EQUAQy(1, 1) #define STV090x_P1_EQUAQ2 STV090x_Px_EQUAQy(1, 2) #define STV090x_P1_EQUAQ3 STV090x_Px_EQUAQy(1, 3) @@ -1390,7 +1388,7 @@ #define STV090x_OFFST_Px_CAR2S2_16A_ALPH_E_FIELD 0 #define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_E_FIELD 4 -#define STV090x_Px_ACLC2S232A(__x) (0xf499 - (__x - 1) * 0x200) +#define STV090x_Px_ACLC2S232A(__x) (0xf49A - (__x - 1) * 0x200) #define STV090x_P1_ACLC2S232A STV090x_Px_ACLC2S232A(1) #define STV090x_P2_ACLC2S232A STV090x_Px_ACLC2S232A(2) #define STV090x_OFFST_Px_CAR2S2_32A_ALPH_M_FIELD 4 @@ -1414,7 +1412,7 @@ #define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0 #define STV090x_WIDTH_Px_CAR2S2_8_BETA_E_FIELD 4 -#define STV090x_Px_BCLC2S216A(__x) (0xf49d - (__x - 1) * 0x200) +#define STV090x_Px_BCLC2S216A(__x) (0xf49e - (__x - 1) * 0x200) #define STV090x_P1_BCLC2S216A STV090x_Px_BCLC2S216A(1) #define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(1) #define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD 4 @@ -1422,7 +1420,7 @@ #define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD 0 #define STV090x_WIDTH_Px_CAR2S2_16A_BETA_E_FIELD 4 -#define STV090x_Px_BCLC2S232A(__x) (0xf49d - (__x - 1) * 0x200) +#define STV090x_Px_BCLC2S232A(__x) (0xf49f - (__x - 1) * 0x200) #define STV090x_P1_BCLC2S232A STV090x_Px_BCLC2S232A(1) #define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(1) #define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD 4 @@ -1458,7 +1456,7 @@ #define STV090x_P1_MODCODLST1 STV090x_Px_MODCODLST1(1) #define STV090x_P2_MODCODLST1 STV090x_Px_MODCODLST1(2) #define STV090x_OFFST_Px_DIS_MODCOD29_FIELD 4 -#define STV090x_WIDTH_Px_DIS_MODCOD29T_FIELD 4 +#define STV090x_WIDTH_Px_DIS_MODCOD29_FIELD 4 #define STV090x_OFFST_Px_DIS_32PSK_9_10_FIELD 0 #define STV090x_WIDTH_Px_DIS_32PSK_9_10_FIELD 4 @@ -2180,7 +2178,7 @@ #define STV090x_WIDTH_Px_TSFIFOSPEED_STORE_FIELD 1 #define STV090x_OFFST_Px_DILXX_RESET_FIELD 5 #define STV090x_WIDTH_Px_DILXX_RESET_FIELD 1 -#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 5 +#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 4 #define STV090x_WIDTH_Px_TSSERIAL_IMPOS_FIELD 1 #define STV090x_OFFST_Px_SCRAMBDETECT_FIELD 1 #define STV090x_WIDTH_Px_SCRAMBDETECT_FIELD 1 @@ -2190,7 +2188,7 @@ #define STV090x_P1_TSBITRATE1 STV090x_Px_TSBITRATEy(1, 1) #define STV090x_P2_TSBITRATE0 STV090x_Px_TSBITRATEy(2, 0) #define STV090x_P2_TSBITRATE1 STV090x_Px_TSBITRATEy(2, 1) -#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 7 +#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 0 #define STV090x_WIDTH_Px_TSFIFO_BITRATE_FIELD 8 #define STV090x_Px_ERRCTRL1(__x) (0xF598 - (__x - 1) * 0x200) diff --git a/drivers/media/dvb/frontends/stv6110.c b/drivers/media/dvb/frontends/stv6110.c index dcf1b21ea974..bef0cc838471 100644 --- a/drivers/media/dvb/frontends/stv6110.c +++ b/drivers/media/dvb/frontends/stv6110.c @@ -37,6 +37,7 @@ struct stv6110_priv { u32 mclk; u8 clk_div; + u8 gain; u8 regs[8]; }; @@ -255,7 +256,7 @@ static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency) u8 ret = 0x04; u32 divider, ref, p, presc, i, result_freq, vco_freq; s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val; - s32 srate; u8 gain; + s32 srate; dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__, frequency, priv->mclk); @@ -273,15 +274,8 @@ static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency) } else srate = 15000000; - if (srate >= 15000000) - gain = 3; /* +6 dB */ - else if (srate >= 5000000) - gain = 3; /* +6 dB */ - else - gain = 3; /* +6 dB */ - priv->regs[RSTV6110_CTRL2] &= ~0x0f; - priv->regs[RSTV6110_CTRL2] |= (gain & 0x0f); + priv->regs[RSTV6110_CTRL2] |= (priv->gain & 0x0f); if (frequency <= 1023000) { p = 1; @@ -436,6 +430,7 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, priv->i2c = i2c; priv->mclk = config->mclk; priv->clk_div = config->clk_div; + priv->gain = config->gain; memcpy(&priv->regs, ®0[1], 8); diff --git a/drivers/media/dvb/frontends/stv6110.h b/drivers/media/dvb/frontends/stv6110.h index 9db2402410f6..fe71bba6a26e 100644 --- a/drivers/media/dvb/frontends/stv6110.h +++ b/drivers/media/dvb/frontends/stv6110.h @@ -41,6 +41,7 @@ struct stv6110_config { u8 i2c_address; u32 mclk; + u8 gain; u8 clk_div; /* divisor value for the output clock */ }; diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c index 3d8a2e01c9c4..bcfcb652464c 100644 --- a/drivers/media/dvb/frontends/stv6110x.c +++ b/drivers/media/dvb/frontends/stv6110x.c @@ -95,7 +95,7 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) { struct stv6110x_state *stv6110x = fe->tuner_priv; u32 rDiv, divider; - s32 pVal, pCalc, rDivOpt = 0; + s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000; u8 i; STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16)); @@ -121,8 +121,10 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) for (rDiv = 0; rDiv <= 3; rDiv++) { pCalc = (REFCLOCK_kHz / 100) / R_DIV(rDiv); - if ((abs((s32)(pCalc - pVal))) < (abs((s32)(1000 - pVal)))) + if ((abs((s32)(pCalc - pVal))) < (abs((s32)(pCalcOpt - pVal)))) rDivOpt = rDiv; + + pCalcOpt = (REFCLOCK_kHz / 100) / R_DIV(rDivOpt); } divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz; diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c index 1fd8306371e2..81e623a90f09 100644 --- a/drivers/media/dvb/pt1/pt1.c +++ b/drivers/media/dvb/pt1/pt1.c @@ -27,7 +27,6 @@ #include <linux/pci.h> #include <linux/kthread.h> #include <linux/freezer.h> -#include <linux/vmalloc.h> #include "dvbdev.h" #include "dvb_demux.h" diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c index 2db940f8635f..fc6594996e79 100644 --- a/drivers/media/dvb/pt1/va1j5jf8007s.c +++ b/drivers/media/dvb/pt1/va1j5jf8007s.c @@ -48,6 +48,60 @@ struct va1j5jf8007s_state { enum va1j5jf8007s_tune_state tune_state; }; +static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct va1j5jf8007s_state *state; + u8 addr; + int i; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + s32 word, x1, x2, x3, x4, x5, y; + + state = fe->demodulator_priv; + addr = state->config->demod_address; + + word = 0; + for (i = 0; i < 2; i++) { + write_buf[0] = 0xbc + i; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + word <<= 8; + word |= read_buf[0]; + } + + word -= 3000; + if (word < 0) + word = 0; + + x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000); + x2 = (s64)x1 * x1 >> 31; + x3 = (s64)x2 * x1 >> 31; + x4 = (s64)x2 * x2 >> 31; + x5 = (s64)x4 * x1 >> 31; + + y = (58857ll << 23) / 1000; + y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30; + y += (s64)x2 * ((88977ll << 24) / 1000) >> 28; + y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27; + y += (s64)x4 * ((14341ll << 27) / 1000) >> 27; + y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28; + + *snr = y < 0 ? 0 : y >> 15; + return 0; +} + static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; @@ -337,7 +391,7 @@ va1j5jf8007s_tune(struct dvb_frontend *fe, { struct va1j5jf8007s_state *state; int ret; - int lock; + int lock = 0; state = fe->demodulator_priv; @@ -536,6 +590,7 @@ static struct dvb_frontend_ops va1j5jf8007s_ops = { FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, }, + .read_snr = va1j5jf8007s_read_snr, .get_frontend_algo = va1j5jf8007s_get_frontend_algo, .read_status = va1j5jf8007s_read_status, .tune = va1j5jf8007s_tune, diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/dvb/pt1/va1j5jf8007t.c index 71117f4ca7e6..3db4f3e34e8f 100644 --- a/drivers/media/dvb/pt1/va1j5jf8007t.c +++ b/drivers/media/dvb/pt1/va1j5jf8007t.c @@ -46,6 +46,52 @@ struct va1j5jf8007t_state { enum va1j5jf8007t_tune_state tune_state; }; +static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct va1j5jf8007t_state *state; + u8 addr; + int i; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + s32 word, x, y; + + state = fe->demodulator_priv; + addr = state->config->demod_address; + + word = 0; + for (i = 0; i < 3; i++) { + write_buf[0] = 0x8b + i; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + word <<= 8; + word |= read_buf[0]; + } + + if (!word) + return -EIO; + + x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24)); + y = (24ll << 46) / 1000000; + y = ((s64)y * x >> 30) - (16ll << 40) / 10000; + y = ((s64)y * x >> 29) + (398ll << 35) / 10000; + y = ((s64)y * x >> 30) + (5491ll << 29) / 10000; + y = ((s64)y * x >> 30) + (30965ll << 23) / 10000; + *snr = y >> 15; + return 0; +} + static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; @@ -224,7 +270,7 @@ va1j5jf8007t_tune(struct dvb_frontend *fe, { struct va1j5jf8007t_state *state; int ret; - int lock, retry; + int lock = 0, retry = 0; state = fe->demodulator_priv; @@ -393,6 +439,7 @@ static struct dvb_frontend_ops va1j5jf8007t_ops = { FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, }, + .read_snr = va1j5jf8007t_read_snr, .get_frontend_algo = va1j5jf8007t_get_frontend_algo, .read_status = va1j5jf8007t_read_status, .tune = va1j5jf8007t_tune, diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index 0420e2885e75..1067b22eb0c6 100644 --- a/drivers/media/dvb/siano/sms-cards.c +++ b/drivers/media/dvb/siano/sms-cards.c @@ -97,7 +97,7 @@ static struct sms_board sms_boards[] = { }, }; -struct sms_board *sms_get_board(int id) +struct sms_board *sms_get_board(unsigned id) { BUG_ON(id >= ARRAY_SIZE(sms_boards)); @@ -294,6 +294,8 @@ int sms_board_load_modules(int id) case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A: case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B: case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: request_module("smsdvb"); break; default: diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h index 38f062f6ad68..8f19fc000b46 100644 --- a/drivers/media/dvb/siano/sms-cards.h +++ b/drivers/media/dvb/siano/sms-cards.h @@ -81,7 +81,7 @@ struct sms_board { int led_power, led_hi, led_lo, lna_ctrl, rf_switch; }; -struct sms_board *sms_get_board(int id); +struct sms_board *sms_get_board(unsigned id); extern struct smscore_device_t *coredev; diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index fa6a62369a78..ca758bcb48c9 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -1373,7 +1373,7 @@ static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, *pGroupCfg = 1; - if (PinNum >= 0 && PinNum <= 1) { + if (PinNum <= 1) { *pTranslatedPinNum = 0; *pGroupNum = 9; *pGroupCfg = 2; diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c index d1d652e7f890..24206cbda264 100644 --- a/drivers/media/dvb/siano/smssdio.c +++ b/drivers/media/dvb/siano/smssdio.c @@ -78,7 +78,7 @@ struct smssdio_device { static int smssdio_sendrequest(void *context, void *buffer, size_t size) { - int ret; + int ret = 0; struct smssdio_device *smsdev; smsdev = context; diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 8d65c652ba50..baf3159a3aa6 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -2425,7 +2425,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called */ saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - /* set event counter 1 treshold to maximum allowed value (rEC p55) */ + /* set event counter 1 threshold to maximum allowed value (rEC p55) */ saa7146_write(dev, ECT1R, 0x3fff ); #endif /* Set RPS1 Address register to point to RPS code (r108 p42) */ @@ -2559,7 +2559,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called */ saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - /* set event counter 1 treshold to maximum allowed value (rEC p55) */ + /* set event counter 1 threshold to maximum allowed value (rEC p55) */ saa7146_write(dev, ECT1R, 0x3fff ); #endif /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */ diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 8ea915227674..983672aa2450 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -1055,7 +1055,7 @@ static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { { STB0899_TSCFGH , 0x0c }, { STB0899_TSCFGM , 0x00 }, { STB0899_TSCFGL , 0x0c }, - { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */ + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ { STB0899_RSSYNCDEL , 0x00 }, { STB0899_TSINHDELH , 0x02 }, { STB0899_TSINHDELM , 0x00 }, diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index b5c681372b6c..7d193ebc0aea 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -178,7 +178,7 @@ static void msp430_ir_interrupt(unsigned long data) if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) { ir_input_nokey(dev, &budget_ci->ir.state); ir_input_keydown(dev, &budget_ci->ir.state, - budget_ci->ir.ir_key, raw); + budget_ci->ir.ir_key); budget_ci->ir.last_raw = raw; } @@ -224,8 +224,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x1011: case 0x1012: /* The hauppauge keymap is a superset of these remotes */ - ir_input_init(input_dev, &budget_ci->ir.state, + error = ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5, &ir_codes_hauppauge_new_table); + if (error < 0) + goto out2; if (rc5_device < 0) budget_ci->ir.rc5_device = 0x1f; @@ -236,8 +238,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x1017: case 0x101a: /* for the Technotrend 1500 bundled remote */ - ir_input_init(input_dev, &budget_ci->ir.state, + error = ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5, &ir_codes_tt_1500_table); + if (error < 0) + goto out2; if (rc5_device < 0) budget_ci->ir.rc5_device = IR_DEVICE_ANY; @@ -246,8 +250,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci) break; default: /* unknown remote */ - ir_input_init(input_dev, &budget_ci->ir.state, + error = ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5, &ir_codes_budget_ci_old_table); + if (error < 0) + goto out2; if (rc5_device < 0) budget_ci->ir.rc5_device = IR_DEVICE_ANY; @@ -280,6 +286,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) return 0; out2: + ir_input_free(input_dev); input_free_device(input_dev); out1: return error; @@ -297,6 +304,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) del_timer_sync(&dev->timer); ir_input_nokey(dev, &budget_ci->ir.state); + ir_input_free(dev); input_unregister_device(dev); } @@ -1248,7 +1256,7 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { { STB0899_TSCFGH , 0x0c }, { STB0899_TSCFGM , 0x00 }, { STB0899_TSCFGL , 0x0c }, - { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */ + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ { STB0899_RSSYNCDEL , 0x00 }, { STB0899_TSINHDELH , 0x02 }, { STB0899_TSINHDELM , 0x00 }, diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c index 60136688a9a4..9c92f9ddd223 100644 --- a/drivers/media/dvb/ttpci/budget-patch.c +++ b/drivers/media/dvb/ttpci/budget-patch.c @@ -456,7 +456,7 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled // use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - // set event counter 1 treshold to maximum allowed value (rEC p55) + // set event counter 1 threshold to maximum allowed value (rEC p55) saa7146_write(dev, ECT1R, 0x3fff ); #endif // Fix VSYNC level diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c index d91e0638448f..53baccbab17f 100644 --- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -1198,7 +1198,7 @@ static int ttusb_init_rc( struct ttusb_dec *dec) int err; usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys)); - strlcpy(dec->rc_phys, "/input0", sizeof(dec->rc_phys)); + strlcat(dec->rc_phys, "/input0", sizeof(dec->rc_phys)); input_dev = input_allocate_device(); if (!input_dev) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index a87a477c87f2..4c2b8a246772 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -195,6 +195,25 @@ config RADIO_MAESTRO To compile this driver as a module, choose M here: the module will be called radio-maestro. +config RADIO_MIROPCM20 + tristate "miroSOUND PCM20 radio" + depends on ISA && VIDEO_V4L2 && SND + select SND_ISA + select SND_MIRO + ---help--- + Choose Y here if you have this FM radio card. You also need to enable + the ALSA sound system. This choice automatically selects the ALSA + sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this + is required for the radio-miropcm20. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + <file:Documentation/video4linux/API.html>. + + To compile this driver as a module, choose M here: the + module will be called radio-miropcm20. + config RADIO_SF16FMI tristate "SF16FMI Radio" depends on ISA && VIDEO_V4L2 @@ -401,4 +420,16 @@ config RADIO_TEA5764_XTAL Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N here if TEA5764 reference frequency is connected in FREQIN. +config RADIO_TEF6862 + tristate "TEF6862 Car Radio Enhanced Selectivity Tuner" + depends on I2C && VIDEO_V4L2 + ---help--- + Say Y here if you want to use the TEF6862 Car Radio Enhanced + Selectivity Tuner, found for instance on the Russellville development + board. On the russellville the device is connected to internal + timberdale I2C bus. + + To compile this driver as a module, choose M here: the + module will be called TEF6862. + endif # RADIO_ADAPTERS diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 2a1be3bf4f7c..01922ada6914 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -18,9 +18,11 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o +obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o +obj-$(CONFIG_RADIO_TEF6862) += tef6862.o EXTRA_CFLAGS += -Isound diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c new file mode 100644 index 000000000000..4ff885445fd4 --- /dev/null +++ b/drivers/media/radio/radio-miropcm20.c @@ -0,0 +1,270 @@ +/* Miro PCM20 radio driver for Linux radio support + * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl> + * Thanks to Norberto Pellici for the ACI device interface specification + * The API part is based on the radiotrack driver by M. Kirkwood + * This driver relies on the aci mixer provided by the snd-miro + * ALSA driver. + * Look there for further info... + */ + +/* What ever you think about the ACI, version 0x07 is not very well! + * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono + * conditions... Robert + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <sound/aci.h> + +static int radio_nr = -1; +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); + +static int mono; +module_param(mono, bool, 0); +MODULE_PARM_DESC(mono, "Force tuner into mono mode."); + +struct pcm20 { + struct v4l2_device v4l2_dev; + struct video_device vdev; + unsigned long freq; + int muted; + struct snd_miro_aci *aci; +}; + +static struct pcm20 pcm20_card = { + .freq = 87*16000, + .muted = 1, +}; + +static int pcm20_mute(struct pcm20 *dev, unsigned char mute) +{ + dev->muted = mute; + return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1); +} + +static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo) +{ + return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1); +} + +static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) +{ + unsigned char freql; + unsigned char freqh; + struct snd_miro_aci *aci = dev->aci; + + dev->freq = freq; + + freq /= 160; + if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0)) + freq /= 10; /* I don't know exactly which version + * needs this hack */ + freql = freq & 0xff; + freqh = freq >> 8; + + pcm20_stereo(dev, !mono); + return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh); +} + +static const struct v4l2_file_operations pcm20_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, +}; + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "Miro PCM20", sizeof(v->driver)); + strlcpy(v->card, "Miro PCM20", sizeof(v->card)); + strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); + v->version = 0x1; + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index) /* Only 1 tuner */ + return -EINVAL; + strlcpy(v->name, "FM", sizeof(v->name)); + v->type = V4L2_TUNER_RADIO; + v->rangelow = 87*16000; + v->rangehigh = 108*16000; + v->signal = 0xffff; + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + return v->index ? -EINVAL : 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct pcm20 *dev = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + + f->type = V4L2_TUNER_RADIO; + f->frequency = dev->freq; + return 0; +} + + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct pcm20 *dev = video_drvdata(file); + + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + dev->freq = f->frequency; + pcm20_setfreq(dev, f->frequency); + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct pcm20 *dev = video_drvdata(file); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = dev->muted; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct pcm20 *dev = video_drvdata(file); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + pcm20_mute(dev, ctrl->value); + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + a->index = 0; + strlcpy(a->name, "Radio", sizeof(a->name)); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + return a->index ? -EINVAL : 0; +} + +static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, +}; + +static int __init pcm20_init(void) +{ + struct pcm20 *dev = &pcm20_card; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + int res; + + dev->aci = snd_aci_get_aci(); + if (dev->aci == NULL) { + v4l2_err(v4l2_dev, + "you must load the snd-miro driver first!\n"); + return -ENODEV; + } + strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name)); + + + res = v4l2_device_register(NULL, v4l2_dev); + if (res < 0) { + v4l2_err(v4l2_dev, "could not register v4l2_device\n"); + return -EINVAL; + } + + strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); + dev->vdev.v4l2_dev = v4l2_dev; + dev->vdev.fops = &pcm20_fops; + dev->vdev.ioctl_ops = &pcm20_ioctl_ops; + dev->vdev.release = video_device_release_empty; + video_set_drvdata(&dev->vdev, dev); + + if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) + goto fail; + + v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n"); + return 0; +fail: + v4l2_device_unregister(v4l2_dev); + return -EINVAL; +} + +MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt"); +MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card."); +MODULE_LICENSE("GPL"); + +static void __exit pcm20_cleanup(void) +{ + struct pcm20 *dev = &pcm20_card; + + video_unregister_device(&dev->vdev); + v4l2_device_unregister(&dev->v4l2_dev); +} + +module_init(pcm20_init); +module_exit(pcm20_cleanup); diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index a1239083472d..949f60513d9e 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -28,7 +28,7 @@ * http://av-usbradio.sourceforge.net/index.php * http://sourceforge.net/projects/av-usbradio/ * Latest release of theirs project was in 2005. - * Probably, this driver could be improved trough using their + * Probably, this driver could be improved through using their * achievements (specifications given). * Also, Faidon Liambotis <paravoid@debian.org> wrote nice driver for this radio * in 2007. He allowed to use his driver to improve current mr800 radio driver. @@ -85,6 +85,9 @@ MODULE_LICENSE("GPL"); #define amradio_dev_warn(dev, fmt, arg...) \ dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg) +#define amradio_dev_err(dev, fmt, arg...) \ + dev_err(dev, MR800_DRIVER_NAME " - " fmt, ##arg) + /* Probably USB_TIMEOUT should be modified in module parameter */ #define BUFFER_LENGTH 8 #define USB_TIMEOUT 500 @@ -129,18 +132,20 @@ static int usb_amradio_resume(struct usb_interface *intf); struct amradio_device { /* reference to USB and video device */ struct usb_device *usbdev; - struct video_device *videodev; + struct usb_interface *intf; + struct video_device videodev; struct v4l2_device v4l2_dev; unsigned char *buffer; struct mutex lock; /* buffer locking */ int curfreq; int stereo; - int users; - int removed; int muted; + int initialized; }; +#define vdev_to_amradio(r) container_of(r, struct amradio_device, videodev) + /* USB Device ID List */ static struct usb_device_id usb_amradio_device_table[] = { {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT, @@ -159,7 +164,7 @@ static struct usb_driver usb_amradio_driver = { .resume = usb_amradio_resume, .reset_resume = usb_amradio_resume, .id_table = usb_amradio_device_table, - .supports_autosuspend = 0, + .supports_autosuspend = 1, }; /* switch on/off the radio. Send 8 bytes to device */ @@ -168,11 +173,7 @@ static int amradio_set_mute(struct amradio_device *radio, char argument) int retval; int size; - /* safety check */ - if (radio->removed) - return -EIO; - - mutex_lock(&radio->lock); + BUG_ON(!mutex_is_locked(&radio->lock)); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; @@ -187,14 +188,12 @@ static int amradio_set_mute(struct amradio_device *radio, char argument) (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) { - mutex_unlock(&radio->lock); + amradio_dev_warn(&radio->videodev.dev, "set mute failed\n"); return retval; } radio->muted = argument; - mutex_unlock(&radio->lock); - return retval; } @@ -205,11 +204,7 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) int size; unsigned short freq_send = 0x10 + (freq >> 3) / 25; - /* safety check */ - if (radio->removed) - return -EIO; - - mutex_lock(&radio->lock); + BUG_ON(!mutex_is_locked(&radio->lock)); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; @@ -223,10 +218,8 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); - if (retval < 0 || size != BUFFER_LENGTH) { - mutex_unlock(&radio->lock); - return retval; - } + if (retval < 0 || size != BUFFER_LENGTH) + goto out_err; /* frequency is calculated from freq_send and placed in first 2 bytes */ radio->buffer[0] = (freq_send >> 8) & 0xff; @@ -240,13 +233,15 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); - if (retval < 0 || size != BUFFER_LENGTH) { - mutex_unlock(&radio->lock); - return retval; - } + if (retval < 0 || size != BUFFER_LENGTH) + goto out_err; - mutex_unlock(&radio->lock); + radio->curfreq = freq; + goto out; +out_err: + amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n"); +out: return retval; } @@ -255,11 +250,7 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument) int retval; int size; - /* safety check */ - if (radio->removed) - return -EIO; - - mutex_lock(&radio->lock); + BUG_ON(!mutex_is_locked(&radio->lock)); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; @@ -274,14 +265,14 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument) (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) { - radio->stereo = -1; - mutex_unlock(&radio->lock); + amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n"); return retval; } - radio->stereo = 1; - - mutex_unlock(&radio->lock); + if (argument == WANT_STEREO) + radio->stereo = 1; + else + radio->stereo = 0; return retval; } @@ -296,19 +287,19 @@ static void usb_amradio_disconnect(struct usb_interface *intf) struct amradio_device *radio = usb_get_intfdata(intf); mutex_lock(&radio->lock); - radio->removed = 1; + radio->usbdev = NULL; mutex_unlock(&radio->lock); usb_set_intfdata(intf, NULL); - video_unregister_device(radio->videodev); v4l2_device_disconnect(&radio->v4l2_dev); + video_unregister_device(&radio->videodev); } /* vidioc_querycap - query device capabilities */ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { - struct amradio_device *radio = video_drvdata(file); + struct amradio_device *radio = file->private_data; strlcpy(v->driver, "radio-mr800", sizeof(v->driver)); strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card)); @@ -322,13 +313,9 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + struct amradio_device *radio = file->private_data; int retval; - /* safety check */ - if (radio->removed) - return -EIO; - if (v->index > 0) return -EINVAL; @@ -341,9 +328,6 @@ static int vidioc_g_tuner(struct file *file, void *priv, * amradio_set_stereo shouldn't be here */ retval = amradio_set_stereo(radio, WANT_STEREO); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set stereo failed\n"); strcpy(v->name, "FM"); v->type = V4L2_TUNER_RADIO; @@ -357,19 +341,16 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->audmode = V4L2_TUNER_MODE_MONO; v->signal = 0xffff; /* Can't get the signal strength, sad.. */ v->afc = 0; /* Don't know what is this */ - return 0; + + return retval; } /* vidioc_s_tuner - set tuner attributes */ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - int retval; - - /* safety check */ - if (radio->removed) - return -EIO; + struct amradio_device *radio = file->private_data; + int retval = -EINVAL; if (v->index > 0) return -EINVAL; @@ -378,57 +359,33 @@ static int vidioc_s_tuner(struct file *file, void *priv, switch (v->audmode) { case V4L2_TUNER_MODE_MONO: retval = amradio_set_stereo(radio, WANT_MONO); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set mono failed\n"); break; case V4L2_TUNER_MODE_STEREO: retval = amradio_set_stereo(radio, WANT_STEREO); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set stereo failed\n"); break; - default: - return -EINVAL; } - return 0; + return retval; } /* vidioc_s_frequency - set tuner radio frequency */ static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + struct amradio_device *radio = file->private_data; - /* safety check */ - if (radio->removed) - return -EIO; - - mutex_lock(&radio->lock); - radio->curfreq = f->frequency; - mutex_unlock(&radio->lock); - - retval = amradio_setfreq(radio, radio->curfreq); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set frequency failed\n"); - return 0; + return amradio_setfreq(radio, f->frequency); } /* vidioc_g_frequency - get tuner radio frequency */ static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - - /* safety check */ - if (radio->removed) - return -EIO; + struct amradio_device *radio = file->private_data; f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; + return 0; } @@ -448,17 +405,14 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - - /* safety check */ - if (radio->removed) - return -EIO; + struct amradio_device *radio = file->private_data; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = radio->muted; return 0; } + return -EINVAL; } @@ -466,33 +420,20 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - int retval; - - /* safety check */ - if (radio->removed) - return -EIO; + struct amradio_device *radio = file->private_data; + int retval = -EINVAL; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { + if (ctrl->value) retval = amradio_set_mute(radio, AMRADIO_STOP); - if (retval < 0) { - amradio_dev_warn(&radio->videodev->dev, - "amradio_stop failed\n"); - return -1; - } - } else { + else retval = amradio_set_mute(radio, AMRADIO_START); - if (retval < 0) { - amradio_dev_warn(&radio->videodev->dev, - "amradio_start failed\n"); - return -1; - } - } - return 0; + + break; } - return -EINVAL; + + return retval; } /* vidioc_g_audio - get audio attributes */ @@ -531,75 +472,108 @@ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) return 0; } -/* open device - amradio_start() and amradio_setfreq() */ -static int usb_amradio_open(struct file *file) +static int usb_amradio_init(struct amradio_device *radio) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); int retval; - lock_kernel(); + retval = amradio_set_mute(radio, AMRADIO_STOP); + if (retval) + goto out_err; - radio->users = 1; - radio->muted = 1; + retval = amradio_set_stereo(radio, WANT_STEREO); + if (retval) + goto out_err; - retval = amradio_set_mute(radio, AMRADIO_START); - if (retval < 0) { - amradio_dev_warn(&radio->videodev->dev, - "radio did not start up properly\n"); - radio->users = 0; - unlock_kernel(); - return -EIO; + radio->initialized = 1; + goto out; + +out_err: + amradio_dev_err(&radio->videodev.dev, "initialization failed\n"); +out: + return retval; +} + +/* open device - amradio_start() and amradio_setfreq() */ +static int usb_amradio_open(struct file *file) +{ + struct amradio_device *radio = vdev_to_amradio(video_devdata(file)); + int retval = 0; + + mutex_lock(&radio->lock); + + if (!radio->usbdev) { + retval = -EIO; + goto unlock; } - retval = amradio_set_stereo(radio, WANT_STEREO); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set stereo failed\n"); + file->private_data = radio; + retval = usb_autopm_get_interface(radio->intf); + if (retval) + goto unlock; - retval = amradio_setfreq(radio, radio->curfreq); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set frequency failed\n"); + if (unlikely(!radio->initialized)) { + retval = usb_amradio_init(radio); + if (retval) + usb_autopm_put_interface(radio->intf); + } - unlock_kernel(); - return 0; +unlock: + mutex_unlock(&radio->lock); + return retval; } /*close device */ static int usb_amradio_close(struct file *file) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - int retval; - - if (!radio) - return -ENODEV; + struct amradio_device *radio = file->private_data; + int retval = 0; mutex_lock(&radio->lock); - radio->users = 0; + + if (!radio->usbdev) + retval = -EIO; + else + usb_autopm_put_interface(radio->intf); + mutex_unlock(&radio->lock); + return retval; +} - if (!radio->removed) { - retval = amradio_set_mute(radio, AMRADIO_STOP); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "amradio_stop failed\n"); +static long usb_amradio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct amradio_device *radio = file->private_data; + long retval = 0; + + mutex_lock(&radio->lock); + + if (!radio->usbdev) { + retval = -EIO; + goto unlock; } - return 0; + retval = video_ioctl2(file, cmd, arg); + +unlock: + mutex_unlock(&radio->lock); + return retval; } /* Suspend device - stop device. Need to be checked and fixed */ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) { struct amradio_device *radio = usb_get_intfdata(intf); - int retval; - retval = amradio_set_mute(radio, AMRADIO_STOP); - if (retval < 0) - dev_warn(&intf->dev, "amradio_stop failed\n"); + mutex_lock(&radio->lock); + + if (!radio->muted && radio->initialized) { + amradio_set_mute(radio, AMRADIO_STOP); + radio->muted = 0; + } dev_info(&intf->dev, "going into suspend..\n"); + mutex_unlock(&radio->lock); return 0; } @@ -607,14 +581,26 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) static int usb_amradio_resume(struct usb_interface *intf) { struct amradio_device *radio = usb_get_intfdata(intf); - int retval; - retval = amradio_set_mute(radio, AMRADIO_START); - if (retval < 0) - dev_warn(&intf->dev, "amradio_start failed\n"); + mutex_lock(&radio->lock); + + if (unlikely(!radio->initialized)) + goto unlock; + + if (radio->stereo) + amradio_set_stereo(radio, WANT_STEREO); + else + amradio_set_stereo(radio, WANT_MONO); + + amradio_setfreq(radio, radio->curfreq); + if (!radio->muted) + amradio_set_mute(radio, AMRADIO_START); + +unlock: dev_info(&intf->dev, "coming out of suspend..\n"); + mutex_unlock(&radio->lock); return 0; } @@ -623,7 +609,7 @@ static const struct v4l2_file_operations usb_amradio_fops = { .owner = THIS_MODULE, .open = usb_amradio_open, .release = usb_amradio_close, - .ioctl = video_ioctl2, + .ioctl = usb_amradio_ioctl, }; static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { @@ -643,10 +629,7 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { static void usb_amradio_video_device_release(struct video_device *videodev) { - struct amradio_device *radio = video_get_drvdata(videodev); - - /* we call v4l to free radio->videodev */ - video_device_release(videodev); + struct amradio_device *radio = vdev_to_amradio(videodev); v4l2_device_unregister(&radio->v4l2_dev); @@ -660,70 +643,63 @@ static int usb_amradio_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct amradio_device *radio; - struct v4l2_device *v4l2_dev; - int retval; + int retval = 0; radio = kzalloc(sizeof(struct amradio_device), GFP_KERNEL); if (!radio) { dev_err(&intf->dev, "kmalloc for amradio_device failed\n"); - return -ENOMEM; + retval = -ENOMEM; + goto err; } radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); if (!radio->buffer) { dev_err(&intf->dev, "kmalloc for radio->buffer failed\n"); - kfree(radio); - return -ENOMEM; + retval = -ENOMEM; + goto err_nobuf; } - v4l2_dev = &radio->v4l2_dev; - retval = v4l2_device_register(&intf->dev, v4l2_dev); + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); if (retval < 0) { dev_err(&intf->dev, "couldn't register v4l2_device\n"); - kfree(radio->buffer); - kfree(radio); - return retval; + goto err_v4l2; } - radio->videodev = video_device_alloc(); - - if (!radio->videodev) { - dev_err(&intf->dev, "video_device_alloc failed\n"); - kfree(radio->buffer); - kfree(radio); - return -ENOMEM; - } + strlcpy(radio->videodev.name, radio->v4l2_dev.name, + sizeof(radio->videodev.name)); + radio->videodev.v4l2_dev = &radio->v4l2_dev; + radio->videodev.fops = &usb_amradio_fops; + radio->videodev.ioctl_ops = &usb_amradio_ioctl_ops; + radio->videodev.release = usb_amradio_video_device_release; - strlcpy(radio->videodev->name, v4l2_dev->name, sizeof(radio->videodev->name)); - radio->videodev->v4l2_dev = v4l2_dev; - radio->videodev->fops = &usb_amradio_fops; - radio->videodev->ioctl_ops = &usb_amradio_ioctl_ops; - radio->videodev->release = usb_amradio_video_device_release; - - radio->removed = 0; - radio->users = 0; radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; radio->curfreq = 95.16 * FREQ_MUL; - radio->stereo = -1; mutex_init(&radio->lock); - video_set_drvdata(radio->videodev, radio); + video_set_drvdata(&radio->videodev, radio); - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); + retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, + radio_nr); if (retval < 0) { dev_err(&intf->dev, "could not register video device\n"); - video_device_release(radio->videodev); - v4l2_device_unregister(v4l2_dev); - kfree(radio->buffer); - kfree(radio); - return -EIO; + goto err_vdev; } usb_set_intfdata(intf, radio); return 0; + +err_vdev: + v4l2_device_unregister(&radio->v4l2_dev); +err_v4l2: + kfree(radio->buffer); +err_nobuf: + kfree(radio); +err: + return retval; } static int __init amradio_init(void) diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c new file mode 100644 index 000000000000..6e607ff0c169 --- /dev/null +++ b/drivers/media/radio/tef6862.c @@ -0,0 +1,232 @@ +/* + * tef6862.c Philips TEF6862 Car Radio Enhanced Selectivity Tuner + * Copyright (c) 2009 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> + +#define DRIVER_NAME "tef6862" + +#define FREQ_MUL 16000 + +#define TEF6862_LO_FREQ (875 * FREQ_MUL / 10) +#define TEF6862_HI_FREQ (108 * FREQ_MUL) + +/* Write mode sub addresses */ +#define WM_SUB_BANDWIDTH 0x0 +#define WM_SUB_PLLM 0x1 +#define WM_SUB_PLLL 0x2 +#define WM_SUB_DAA 0x3 +#define WM_SUB_AGC 0x4 +#define WM_SUB_BAND 0x5 +#define WM_SUB_CONTROL 0x6 +#define WM_SUB_LEVEL 0x7 +#define WM_SUB_IFCF 0x8 +#define WM_SUB_IFCAP 0x9 +#define WM_SUB_ACD 0xA +#define WM_SUB_TEST 0xF + +/* Different modes of the MSA register */ +#define MODE_BUFFER 0x0 +#define MODE_PRESET 0x1 +#define MODE_SEARCH 0x2 +#define MODE_AF_UPDATE 0x3 +#define MODE_JUMP 0x4 +#define MODE_CHECK 0x5 +#define MODE_LOAD 0x6 +#define MODE_END 0x7 +#define MODE_SHIFT 5 + +struct tef6862_state { + struct v4l2_subdev sd; + unsigned long freq; +}; + +static inline struct tef6862_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tef6862_state, sd); +} + +static u16 tef6862_sigstr(struct i2c_client *client) +{ + u8 buf[4]; + int err = i2c_master_recv(client, buf, sizeof(buf)); + if (err == sizeof(buf)) + return buf[3] << 8; + return 0; +} + +static int tef6862_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + /* only support FM for now */ + strlcpy(v->name, "FM", sizeof(v->name)); + v->type = V4L2_TUNER_RADIO; + v->rangelow = TEF6862_LO_FREQ; + v->rangehigh = TEF6862_HI_FREQ; + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_STEREO; + v->signal = tef6862_sigstr(v4l2_get_subdevdata(sd)); + + return 0; +} + +static int tef6862_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +{ + return v->index ? -EINVAL : 0; +} + +static int tef6862_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tef6862_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 pll; + u8 i2cmsg[3]; + int err; + + if (f->tuner != 0) + return -EINVAL; + + pll = 1964 + ((f->frequency - TEF6862_LO_FREQ) * 20) / FREQ_MUL; + i2cmsg[0] = (MODE_PRESET << MODE_SHIFT) | WM_SUB_PLLM; + i2cmsg[1] = (pll >> 8) & 0xff; + i2cmsg[2] = pll & 0xff; + + err = i2c_master_send(client, i2cmsg, sizeof(i2cmsg)); + if (!err) + state->freq = f->frequency; + return err; +} + +static int tef6862_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tef6862_state *state = to_state(sd); + + if (f->tuner != 0) + return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = state->freq; + return 0; +} + +static int tef6862_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEF6862, 0); +} + +static const struct v4l2_subdev_tuner_ops tef6862_tuner_ops = { + .g_tuner = tef6862_g_tuner, + .s_tuner = tef6862_s_tuner, + .s_frequency = tef6862_s_frequency, + .g_frequency = tef6862_g_frequency, +}; + +static const struct v4l2_subdev_core_ops tef6862_core_ops = { + .g_chip_ident = tef6862_g_chip_ident, +}; + +static const struct v4l2_subdev_ops tef6862_ops = { + .core = &tef6862_core_ops, + .tuner = &tef6862_tuner_ops, +}; + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int __devinit tef6862_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tef6862_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + state->freq = TEF6862_LO_FREQ; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tef6862_ops); + + return 0; +} + +static int __devexit tef6862_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id tef6862_id[] = { + {DRIVER_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tef6862_id); + +static struct i2c_driver tef6862_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, + .probe = tef6862_probe, + .remove = tef6862_remove, + .id_table = tef6862_id, +}; + +static __init int tef6862_init(void) +{ + return i2c_add_driver(&tef6862_driver); +} + +static __exit void tef6862_exit(void) +{ + i2c_del_driver(&tef6862_driver); +} + +module_init(tef6862_init); +module_exit(tef6862_exit); + +MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e6186b338a12..9dc74c93bf24 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -600,7 +600,7 @@ source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 help Say Y if you have such a thing. @@ -847,6 +847,12 @@ config SOC_CAMERA_MT9V022 help This driver supports MT9V022 cameras from Micron +config SOC_CAMERA_RJ54N1 + tristate "rj54n1cb0c support" + depends on SOC_CAMERA && I2C + help + This is a rj54n1cb0c video driver + config SOC_CAMERA_TW9910 tristate "tw9910 support" depends on SOC_CAMERA && I2C @@ -865,6 +871,12 @@ config SOC_CAMERA_OV772X help This is a ov772x camera driver +config SOC_CAMERA_OV9640 + tristate "ov9640 camera support" + depends on SOC_CAMERA && I2C + help + This is a ov9640 camera driver + config MX1_VIDEO bool @@ -939,9 +951,14 @@ source "drivers/media/video/usbvideo/Kconfig" source "drivers/media/video/et61x251/Kconfig" config VIDEO_OVCAMCHIP - tristate "OmniVision Camera Chip support" + tristate "OmniVision Camera Chip support (DEPRECATED)" depends on I2C && VIDEO_V4L1 ---help--- + This driver is DEPRECATED please use the gspca ov519 module + instead. Note that for the ov511 / ov518 support of the gspca module + you need atleast version 0.6.0 of libv4l and for the w9968cf + atleast version 0.6.3 of libv4l. + Support for the OmniVision OV6xxx and OV7xxx series of camera chips. This driver is intended to be used with the ov511 and w9968cf USB camera drivers. @@ -950,9 +967,13 @@ config VIDEO_OVCAMCHIP module will be called ovcamchip. config USB_W9968CF - tristate "USB W996[87]CF JPEG Dual Mode Camera support" + tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)" depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP ---help--- + This driver is DEPRECATED please use the gspca ov519 module + instead. Note that for the w9968cf support of the gspca module + you need atleast version 0.6.3 of libv4l. + Say Y here if you want support for cameras based on OV681 or Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. @@ -995,9 +1016,13 @@ config USB_SE401 source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 - tristate "USB STV680 (Pencam) Camera support" + tristate "USB STV680 (Pencam) Camera support (DEPRECATED)" depends on VIDEO_V4L1 ---help--- + This driver is DEPRECATED please use the gspca stv0680 module + instead. Note that for the gspca stv0680 module you need + atleast version 0.6.3 of libv4l. + Say Y here if you want to connect this type of camera to your computer's USB port. This includes the Pencam line of cameras. See <file:Documentation/video4linux/stv680.txt> for more information diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index e541932a789b..7a2dcc34111c 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -77,6 +77,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o +obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o +obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o # And now the v4l2 drivers: diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c index 1b3cbd02a7fd..0826f0dabc17 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c @@ -27,17 +27,40 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <linux/mutex.h> #define DRIVER_NAME "adv7180" -#define ADV7180_INPUT_CONTROL_REG 0x00 -#define ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM 0x00 -#define ADV7180_AUTODETECT_ENABLE_REG 0x07 -#define ADV7180_AUTODETECT_DEFAULT 0x7f - - -#define ADV7180_STATUS1_REG 0x10 -#define ADV7180_STATUS1_AUTOD_MASK 0x70 +#define ADV7180_INPUT_CONTROL_REG 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 +#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 +#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 +#define ADV7180_INPUT_CONTROL_PAL60 0x60 +#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 +#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 +#define ADV7180_INPUT_CONTROL_PAL_N 0x90 +#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 +#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 + +#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 +#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 + +#define ADV7180_AUTODETECT_ENABLE_REG 0x07 +#define ADV7180_AUTODETECT_DEFAULT 0x7f + +#define ADV7180_ADI_CTRL_REG 0x0e +#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 + +#define ADV7180_STATUS1_REG 0x10 +#define ADV7180_STATUS1_IN_LOCK 0x01 +#define ADV7180_STATUS1_AUTOD_MASK 0x70 #define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 #define ADV7180_STATUS1_AUTOD_PAL_M 0x20 @@ -50,18 +73,37 @@ #define ADV7180_IDENT_REG 0x11 #define ADV7180_ID_7180 0x18 +#define ADV7180_ICONF1_ADI 0x40 +#define ADV7180_ICONF1_ACTIVE_LOW 0x01 +#define ADV7180_ICONF1_PSYNC_ONLY 0x10 +#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 + +#define ADV7180_IRQ1_LOCK 0x01 +#define ADV7180_IRQ1_UNLOCK 0x02 +#define ADV7180_ISR1_ADI 0x42 +#define ADV7180_ICR1_ADI 0x43 +#define ADV7180_IMR1_ADI 0x44 +#define ADV7180_IMR2_ADI 0x48 +#define ADV7180_IRQ3_AD_CHANGE 0x08 +#define ADV7180_ISR3_ADI 0x4A +#define ADV7180_ICR3_ADI 0x4B +#define ADV7180_IMR3_ADI 0x4C +#define ADV7180_IMR4_ADI 0x50 struct adv7180_state { - struct v4l2_subdev sd; + struct v4l2_subdev sd; + struct work_struct work; + struct mutex mutex; /* mutual excl. when accessing chip */ + int irq; + v4l2_std_id curr_norm; + bool autodetect; }; -static v4l2_std_id determine_norm(struct i2c_client *client) +static v4l2_std_id adv7180_std_to_v4l2(u8 status1) { - u8 status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); - switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { case ADV7180_STATUS1_AUTOD_NTSM_M_J: - return V4L2_STD_NTSC_M_JP; + return V4L2_STD_NTSC; case ADV7180_STATUS1_AUTOD_NTSC_4_43: return V4L2_STD_NTSC_443; case ADV7180_STATUS1_AUTOD_PAL_M: @@ -81,6 +123,53 @@ static v4l2_std_id determine_norm(struct i2c_client *client) } } +static int v4l2_std_to_adv7180(v4l2_std_id std) +{ + if (std == V4L2_STD_PAL_60) + return ADV7180_INPUT_CONTROL_PAL60; + if (std == V4L2_STD_NTSC_443) + return ADV7180_INPUT_CONTROL_NTSC_443; + if (std == V4L2_STD_PAL_N) + return ADV7180_INPUT_CONTROL_PAL_N; + if (std == V4L2_STD_PAL_M) + return ADV7180_INPUT_CONTROL_PAL_M; + if (std == V4L2_STD_PAL_Nc) + return ADV7180_INPUT_CONTROL_PAL_COMB_N; + + if (std & V4L2_STD_PAL) + return ADV7180_INPUT_CONTROL_PAL_BG; + if (std & V4L2_STD_NTSC) + return ADV7180_INPUT_CONTROL_NTSC_M; + if (std & V4L2_STD_SECAM) + return ADV7180_INPUT_CONTROL_PAL_SECAM; + + return -EINVAL; +} + +static u32 adv7180_status_to_v4l2(u8 status1) +{ + if (!(status1 & ADV7180_STATUS1_IN_LOCK)) + return V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int __adv7180_status(struct i2c_client *client, u32 *status, + v4l2_std_id *std) +{ + int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); + + if (status1 < 0) + return status1; + + if (status) + *status = adv7180_status_to_v4l2(status1); + if (std) + *std = adv7180_std_to_v4l2(status1); + + return 0; +} + static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) { return container_of(sd, struct adv7180_state, sd); @@ -88,10 +177,31 @@ static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7180_state *state = to_state(sd); + int err = mutex_lock_interruptible(&state->mutex); + if (err) + return err; + + /* when we are interrupt driven we know the state */ + if (!state->autodetect || state->irq > 0) + *std = state->curr_norm; + else + err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); + + mutex_unlock(&state->mutex); + return err; +} - *std = determine_norm(client); - return 0; +static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); + mutex_unlock(&state->mutex); + return ret; } static int adv7180_g_chip_ident(struct v4l2_subdev *sd, @@ -102,12 +212,51 @@ static int adv7180_g_chip_ident(struct v4l2_subdev *sd, return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); } +static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + /* all standards -> autodetect */ + if (std == V4L2_STD_ALL) { + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); + if (ret < 0) + goto out; + + __adv7180_status(client, NULL, &state->curr_norm); + state->autodetect = true; + } else { + ret = v4l2_std_to_adv7180(std); + if (ret < 0) + goto out; + + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, ret); + if (ret < 0) + goto out; + + state->curr_norm = std; + state->autodetect = false; + } + ret = 0; +out: + mutex_unlock(&state->mutex); + return ret; +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .querystd = adv7180_querystd, + .g_input_status = adv7180_g_input_status, }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { .g_chip_ident = adv7180_g_chip_ident, + .s_std = adv7180_s_std, }; static const struct v4l2_subdev_ops adv7180_ops = { @@ -115,12 +264,45 @@ static const struct v4l2_subdev_ops adv7180_ops = { .video = &adv7180_video_ops, }; +static void adv7180_work(struct work_struct *work) +{ + struct adv7180_state *state = container_of(work, struct adv7180_state, + work); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + u8 isr3; + + mutex_lock(&state->mutex); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); + /* clear */ + i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); + + if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) + __adv7180_status(client, NULL, &state->curr_norm); + mutex_unlock(&state->mutex); + + enable_irq(state->irq); +} + +static irqreturn_t adv7180_irq(int irq, void *devid) +{ + struct adv7180_state *state = devid; + + schedule_work(&state->work); + + disable_irq_nosync(state->irq); + + return IRQ_HANDLED; +} + /* * Generic i2c probe * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int adv7180_probe(struct i2c_client *client, +static __devinit int adv7180_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adv7180_state *state; @@ -135,32 +317,111 @@ static int adv7180_probe(struct i2c_client *client, client->addr << 1, client->adapter->name); state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; + if (state == NULL) { + ret = -ENOMEM; + goto err; + } + + state->irq = client->irq; + INIT_WORK(&state->work, adv7180_work); + mutex_init(&state->mutex); + state->autodetect = true; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); /* Initialize adv7180 */ - /* enable autodetection */ + /* Enable autodetection */ ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM); - if (ret > 0) - ret = i2c_smbus_write_byte_data(client, - ADV7180_AUTODETECT_ENABLE_REG, - ADV7180_AUTODETECT_DEFAULT); - if (ret < 0) { - printk(KERN_ERR DRIVER_NAME - ": Failed to communicate to chip: %d\n", ret); - return ret; + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) + goto err_unreg_subdev; + + /* ITU-R BT.656-4 compatible */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_EXTENDED_OUTPUT_CONTROL_REG, + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + if (ret < 0) + goto err_unreg_subdev; + + /* read current norm */ + __adv7180_status(client, NULL, &state->curr_norm); + + /* register for interrupts */ + if (state->irq > 0) { + ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, + state); + if (ret) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + if (ret < 0) + goto err_unreg_subdev; + + /* config the Interrupt pin to be active low */ + ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, + ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + /* enable AD change interrupts interrupts */ + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, + ADV7180_IRQ3_AD_CHANGE); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + 0); + if (ret < 0) + goto err_unreg_subdev; } return 0; + +err_unreg_subdev: + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(state); +err: + printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret); + return ret; } -static int adv7180_remove(struct i2c_client *client) +static __devexit int adv7180_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + + if (state->irq > 0) { + free_irq(client->irq, state); + if (cancel_work_sync(&state->work)) { + /* + * Work was pending, therefore we need to enable + * IRQ here to balance the disable_irq() done in the + * interrupt handler. + */ + enable_irq(state->irq); + } + } + mutex_destroy(&state->mutex); v4l2_device_unregister_subdev(sd); kfree(to_state(sd)); return 0; @@ -179,7 +440,7 @@ static struct i2c_driver adv7180_driver = { .name = DRIVER_NAME, }, .probe = adv7180_probe, - .remove = adv7180_remove, + .remove = __devexit_p(adv7180_remove), .id_table = adv7180_id, }; diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 51527d7b55a7..1485aee18d58 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -830,7 +830,7 @@ static int au0828_v4l2_close(struct file *filp) au0828_uninit_isoc(dev); /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* When close the device, set the usb intf0 into alt0 to free USB bandwidth */ diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index f9330e3529c3..5bb0f9e71583 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -299,7 +299,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "set input %x\n", input); - if (input < 0 || input > 7) + if (input > 7) return -EINVAL; if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index ebd51afe8761..84a957e52c4b 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -73,12 +73,12 @@ static void ir_handle_key(struct bttv *btv) if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev,&ir->ir,data,data); + ir_input_keydown(ir->dev, &ir->ir, data); } else { /* HACK: Probably, ir->mask_keydown is missing for this board */ if (btv->c.type == BTTV_BOARD_WINFAST2000) - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); ir_input_nokey(ir->dev,&ir->ir); } @@ -104,7 +104,7 @@ static void ir_enltv_handle_key(struct bttv *btv) gpio, data, (gpio & ir->mask_keyup) ? " up" : "up/down"); - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); if (keyup) ir_input_nokey(ir->dev, &ir->ir); } else { @@ -118,7 +118,7 @@ static void ir_enltv_handle_key(struct bttv *btv) if (keyup) ir_input_nokey(ir->dev, &ir->ir); else - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); } ir->last_gpio = data | keyup; @@ -368,7 +368,10 @@ int bttv_input_init(struct bttv *btv) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(btv->c.pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -400,6 +403,7 @@ int bttv_input_init(struct bttv *btv) bttv_ir_stop(btv); btv->remote = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -411,6 +415,7 @@ void bttv_input_fini(struct bttv *btv) return; bttv_ir_stop(btv); + ir_input_free(btv->remote->dev); input_unregister_device(btv->remote->dev); kfree(btv->remote); btv->remote = NULL; diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 536dedb23ba3..4392c76af5df 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -99,10 +99,8 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } -static int cx18_av_init(struct v4l2_subdev *sd, u32 val) +static void cx18_av_init(struct cx18 *cx) { - struct cx18 *cx = v4l2_get_subdevdata(sd); - /* * The crystal freq used in calculations in this driver will be * 28.636360 MHz. @@ -125,7 +123,6 @@ static int cx18_av_init(struct v4l2_subdev *sd, u32 val) /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); - return 0; } static void cx18_av_initialize(struct v4l2_subdev *sd) @@ -198,7 +195,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd) cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000); /* Setup the Video and and Aux/Audio PLLs */ - cx18_av_init(sd, 0); + cx18_av_init(cx); /* set video to auto-detect */ /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ @@ -1355,7 +1352,6 @@ static int cx18_av_s_register(struct v4l2_subdev *sd, static const struct v4l2_subdev_core_ops cx18_av_general_ops = { .g_chip_ident = cx18_av_g_chip_ident, .log_status = cx18_av_log_status, - .init = cx18_av_init, .load_fw = cx18_av_load_fw, .reset = cx18_av_reset, .queryctrl = cx18_av_queryctrl, @@ -1399,6 +1395,7 @@ int cx18_av_probe(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; struct v4l2_subdev *sd; + int err; state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff; state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO) @@ -1417,5 +1414,8 @@ int cx18_av_probe(struct cx18 *cx) snprintf(sd->name, sizeof(sd->name), "%s %03x", cx->v4l2_dev.name, (state->rev >> 4)); sd->grp_id = CX18_HW_418_AV; - return v4l2_device_register_subdev(&cx->v4l2_dev, sd); + err = v4l2_device_register_subdev(&cx->v4l2_dev, sd); + if (!err) + cx18_av_init(cx); + return err; } diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index 9b84a0c58e0e..cafb7e99b9a0 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -294,7 +294,7 @@ struct cx18_av_state { #define CXADEC_QAM_CONST_DEC 0x924 #define CXADEC_QAM_ROTATOR_FREQ 0x948 -/* Bit defintions / settings used in Mako Audio */ +/* Bit definitions / settings used in Mako Audio */ #define CXADEC_PREF_MODE_MONO_LANGA 0 #define CXADEC_PREF_MODE_MONO_LANGB 1 #define CXADEC_PREF_MODE_MONO_LANGC 2 diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 444e3c7c563e..af3d71607dc9 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -34,6 +34,9 @@ #define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \ CX18_HW_Z8F0811_IR_TX_HAUP) +#define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \ + CX18_HW_Z8F0811_IR_TX_HAUP) + /* video inputs */ #define CX18_CARD_INPUT_VID_TUNER 1 #define CX18_CARD_INPUT_SVIDEO1 2 diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 6dd51e27582c..7f65a47f12e1 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -87,7 +87,6 @@ static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE; static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE; static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE; static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE; -/* VBI bufsize based on standards supported by card tuner for now */ static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE; static int enc_ts_bufs = -1; @@ -128,7 +127,6 @@ module_param(enc_ts_bufsize, int, 0644); module_param(enc_mpg_bufsize, int, 0644); module_param(enc_idx_bufsize, int, 0644); module_param(enc_yuv_bufsize, int, 0644); -/* VBI bufsize based on standards supported by card tuner for now */ module_param(enc_pcm_bufsize, int, 0644); module_param(enc_ts_bufs, int, 0644); @@ -211,7 +209,9 @@ MODULE_PARM_DESC(enc_yuv_buffers, "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); MODULE_PARM_DESC(enc_yuv_bufsize, "Size of an encoder YUV buffer (kB)\n" - "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE)); + "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n" + "\t\t\t(multiples of size required for 32 screen lines)\n" + "\t\t\tDefault: 102"); MODULE_PARM_DESC(enc_yuv_bufs, "Number of encoder YUV buffers\n" "\t\t\tDefault is computed from other enc_yuv_* parameters"); @@ -220,7 +220,7 @@ MODULE_PARM_DESC(enc_vbi_buffers, "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); MODULE_PARM_DESC(enc_vbi_bufs, "Number of encoder VBI buffers\n" - "\t\t\tDefault is computed from enc_vbi_buffers & tuner std"); + "\t\t\tDefault is computed from enc_vbi_buffers"); MODULE_PARM_DESC(enc_pcm_buffers, "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); @@ -499,10 +499,27 @@ static void cx18_process_options(struct cx18 *cx) continue; } /* + * YUV is a special case where the stream_buf_size needs to be + * an integral multiple of 33.75 kB (storage for 32 screens + * lines to maintain alignment in case of lost buffers + */ + if (i == CX18_ENC_STREAM_TYPE_YUV) { + cx->stream_buf_size[i] *= 1024; + cx->stream_buf_size[i] -= + (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE); + + if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE) + cx->stream_buf_size[i] = + CX18_UNIT_ENC_YUV_BUFSIZE; + } + /* + * YUV is a special case where the stream_buf_size is + * now in bytes. * VBI is a special case where the stream_buf_size is fixed * and already in bytes */ - if (i == CX18_ENC_STREAM_TYPE_VBI) { + if (i == CX18_ENC_STREAM_TYPE_VBI || + i == CX18_ENC_STREAM_TYPE_YUV) { if (cx->stream_buffers[i] < 0) { cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 * 1024 @@ -513,18 +530,24 @@ static void cx18_process_options(struct cx18 *cx) cx->stream_buffers[i] * cx->stream_buf_size[i]/(1024 * 1024); } - continue; - } - /* All other streams have stream_buf_size in kB at this point */ - if (cx->stream_buffers[i] < 0) { - cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 - / cx->stream_buf_size[i]; } else { - /* N.B. This might round down to 0 */ - cx->options.megabytes[i] = - cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024; + /* All other streams have stream_buf_size in kB here */ + if (cx->stream_buffers[i] < 0) { + cx->stream_buffers[i] = + cx->options.megabytes[i] * 1024 + / cx->stream_buf_size[i]; + } else { + /* N.B. This might round down to 0 */ + cx->options.megabytes[i] = + cx->stream_buffers[i] + * cx->stream_buf_size[i] / 1024; + } + /* convert from kB to bytes */ + cx->stream_buf_size[i] *= 1024; } - cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */ + CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, " + "%d bytes\n", i, cx->options.megabytes[i], + cx->stream_buffers[i], cx->stream_buf_size[i]); } cx->options.cardtype = cardtype[cx->instance]; @@ -669,6 +692,12 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; + /* IVTV style VBI insertion into MPEG streams */ + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list); + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list); + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list); + list_add(&cx->vbi.sliced_mpeg_buf.list, + &cx->vbi.sliced_mpeg_mdl.buf_list); return 0; } @@ -883,7 +912,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, CX18_ERR("Could not register A/V decoder subdevice\n"); goto free_map; } - cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0); /* Initialize GPIO Reset Controller to do chip resets during i2c init */ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { @@ -1200,7 +1228,7 @@ static struct pci_driver cx18_pci_driver = { .remove = cx18_remove, }; -static int module_start(void) +static int __init module_start(void) { printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION); @@ -1224,7 +1252,7 @@ static int module_start(void) return 0; } -static void module_cleanup(void) +static void __exit module_cleanup(void) { pci_unregister_driver(&cx18_pci_driver); } diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index c6a1e907f63a..e3f7911a7385 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -50,6 +50,7 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> #include <media/tuner.h> +#include <media/ir-kbd-i2c.h> #include "cx18-mailbox.h" #include "cx18-av-core.h" #include "cx23418.h" @@ -120,12 +121,16 @@ /* Maximum firmware DMA buffers per stream */ #define CX18_MAX_FW_MDLS_PER_STREAM 63 +/* YUV buffer sizes in bytes to ensure integer # of frames per buffer */ +#define CX18_UNIT_ENC_YUV_BUFSIZE (720 * 32 * 3 / 2) /* bytes */ +#define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32) +#define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32) + /* DMA buffer, default size in kB allocated */ #define CX18_DEFAULT_ENC_TS_BUFSIZE 32 #define CX18_DEFAULT_ENC_MPG_BUFSIZE 32 #define CX18_DEFAULT_ENC_IDX_BUFSIZE 32 -#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128 -/* Default VBI bufsize based on standards supported by card tuner for now */ +#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1) #define CX18_DEFAULT_ENC_PCM_BUFSIZE 4 /* i2c stuff */ @@ -246,8 +251,8 @@ struct cx18_options { int radio; /* enable/disable radio */ }; -/* per-buffer bit flags */ -#define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ +/* per-mdl bit flags */ +#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */ /* per-stream, s_flags */ #define CX18_F_S_CLAIMED 3 /* this stream is claimed */ @@ -274,18 +279,29 @@ struct cx18_options { struct cx18_buffer { struct list_head list; dma_addr_t dma_handle; - u32 id; - unsigned long b_flags; - unsigned skipped; char *buf; u32 bytesused; u32 readpos; }; +struct cx18_mdl { + struct list_head list; + u32 id; /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */ + + unsigned int skipped; + unsigned long m_flags; + + struct list_head buf_list; + struct cx18_buffer *curr_buf; /* current buffer in list for reading */ + + u32 bytesused; + u32 readpos; +}; + struct cx18_queue { struct list_head list; - atomic_t buffers; + atomic_t depth; u32 bytesused; spinlock_t lock; }; @@ -337,7 +353,7 @@ struct cx18_stream { const char *name; /* name of the stream */ int type; /* stream type */ u32 handle; /* task handle */ - unsigned mdl_offset; + unsigned int mdl_base_idx; u32 id; unsigned long s_flags; /* status flags, see above */ @@ -346,14 +362,20 @@ struct cx18_stream { PCI_DMA_NONE */ wait_queue_head_t waitq; - /* Buffer Stats */ - u32 buffers; - u32 buf_size; + /* Buffers */ + struct list_head buf_pool; /* buffers not attached to an MDL */ + u32 buffers; /* total buffers owned by this stream */ + u32 buf_size; /* size in bytes of a single buffer */ + + /* MDL sizes - all stream MDLs are the same size */ + u32 bufs_per_mdl; + u32 mdl_size; /* total bytes in all buffers in a mdl */ - /* Buffer Queues */ - struct cx18_queue q_free; /* free buffers */ - struct cx18_queue q_busy; /* busy buffers - in use by firmware */ - struct cx18_queue q_full; /* full buffers - data for user apps */ + /* MDL Queues */ + struct cx18_queue q_free; /* free - in rotation, not committed */ + struct cx18_queue q_busy; /* busy - in use by firmware */ + struct cx18_queue q_full; /* full - data for user apps */ + struct cx18_queue q_idle; /* idle - not in rotation */ struct work_struct out_work_order; @@ -481,10 +503,11 @@ struct vbi_info { u32 inserted_frame; /* - * A dummy driver stream transfer buffer with a copy of the next + * A dummy driver stream transfer mdl & buffer with a copy of the next * sliced_mpeg_data[] buffer for output to userland apps. * Only used in cx18-fileops.c, but its state needs to persist at times. */ + struct cx18_mdl sliced_mpeg_mdl; struct cx18_buffer sliced_mpeg_buf; }; @@ -511,10 +534,9 @@ struct cx18 { u8 is_60hz; u8 nof_inputs; /* number of video inputs */ u8 nof_audio_inputs; /* number of audio inputs */ - u16 buffer_id; /* buffer ID counter */ u32 v4l2_cap; /* V4L2 capabilities of card */ u32 hw_flags; /* Hardware description of the board */ - unsigned mdl_offset; + unsigned int free_mdl_idx; struct cx18_scb __iomem *scb; /* pointer to SCB */ struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/ struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/ @@ -585,6 +607,8 @@ struct cx18 { struct i2c_algo_bit_data i2c_algo[2]; struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; + struct IR_i2c_init_data ir_i2c_init_data; + /* gpio */ u32 gpio_dir; u32 gpio_val; diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 51a0c33b25b7..71ad2d1b4c2c 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -61,6 +61,7 @@ static struct mxl5005s_config hauppauge_hvr1600_tuner = { .top = MXL5005S_TOP_25P2, .mod_mode = MXL_DIGITAL_MODE, .if_mode = MXL_ZERO_IF, + .qam_gain = 0x02, .AgcMasterByte = 0x00, }; @@ -71,7 +72,8 @@ static struct s5h1409_config hauppauge_hvr1600_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK + .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE }; /* @@ -360,9 +362,10 @@ int cx18_dvb_register(struct cx18_stream *stream) dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); CX18_INFO("DVB Frontend registered\n"); - CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n", + CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n", stream->dvb.dvb_adapter.num, stream->name, - stream->buffers, stream->buf_size/1024); + stream->buffers, stream->buf_size/1024, + (stream->buf_size * 100 / 1024) % 100); mutex_init(&dvb->feedlock); dvb->enabled = 1; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 04d9c2508b86..4e278db31cc9 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -166,11 +166,12 @@ static void cx18_dualwatch(struct cx18 *cx) } -static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err) +static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block, + int *err) { struct cx18 *cx = s->cx; struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; DEFINE_WAIT(wait); *err = 0; @@ -185,32 +186,33 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, } if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { - while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { + while ((mdl = cx18_dequeue(s_vbi, + &s_vbi->q_full))) { /* byteswap and process VBI data */ - cx18_process_vbi_data(cx, buf, + cx18_process_vbi_data(cx, mdl, s_vbi->type); - cx18_stream_put_buf_fw(s_vbi, buf); + cx18_stream_put_mdl_fw(s_vbi, mdl); } } - buf = &cx->vbi.sliced_mpeg_buf; - if (buf->readpos != buf->bytesused) - return buf; + mdl = &cx->vbi.sliced_mpeg_mdl; + if (mdl->readpos != mdl->bytesused) + return mdl; } /* do we have new data? */ - buf = cx18_dequeue(s, &s->q_full); - if (buf) { - if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP, - &buf->b_flags)) - return buf; + mdl = cx18_dequeue(s, &s->q_full); + if (mdl) { + if (!test_and_clear_bit(CX18_F_M_NEED_SWAP, + &mdl->m_flags)) + return mdl; if (s->type == CX18_ENC_STREAM_TYPE_MPG) /* byteswap MPG data */ - cx18_buf_swap(buf); + cx18_mdl_swap(mdl); else { /* byteswap and process VBI data */ - cx18_process_vbi_data(cx, buf, s->type); + cx18_process_vbi_data(cx, mdl, s->type); } - return buf; + return mdl; } /* return if end of stream */ @@ -229,7 +231,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become available before we were added to the waitqueue */ - if (!atomic_read(&s->q_full.buffers)) + if (!atomic_read(&s->q_full.depth)) schedule(); finish_wait(&s->waitq, &wait); if (signal_pending(current)) { @@ -241,21 +243,28 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, } } -static void cx18_setup_sliced_vbi_buf(struct cx18 *cx) +static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx) { + struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl; + struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf; int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; - cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx]; - cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx]; - cx->vbi.sliced_mpeg_buf.readpos = 0; + buf->buf = cx->vbi.sliced_mpeg_data[idx]; + buf->bytesused = cx->vbi.sliced_mpeg_size[idx]; + buf->readpos = 0; + + mdl->curr_buf = NULL; + mdl->bytesused = cx->vbi.sliced_mpeg_size[idx]; + mdl->readpos = 0; } static size_t cx18_copy_buf_to_user(struct cx18_stream *s, - struct cx18_buffer *buf, char __user *ubuf, size_t ucount) + struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop) { struct cx18 *cx = s->cx; size_t len = buf->bytesused - buf->readpos; + *stop = false; if (len > ucount) len = ucount; if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && @@ -335,7 +344,8 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, /* We declare we actually found a Program Pack*/ cx->search_pack_header = 0; /* expect vid PES */ len = (char *)q - start; - cx18_setup_sliced_vbi_buf(cx); + cx18_setup_sliced_vbi_mdl(cx); + *stop = true; break; } } @@ -352,6 +362,60 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, return len; } +/** + * list_entry_is_past_end - check if a previous loop cursor is off list end + * @pos: the type * previously used as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Check if the entry's list_head is the head of the list, thus it's not a + * real entry but was the loop cursor that walked past the end + */ +#define list_entry_is_past_end(pos, head, member) \ + (&pos->member == (head)) + +static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, + struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) +{ + size_t tot_written = 0; + int rc; + bool stop = false; + + if (mdl->curr_buf == NULL) + mdl->curr_buf = list_first_entry(&mdl->buf_list, + struct cx18_buffer, list); + + if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { + /* + * For some reason we've exhausted the buffers, but the MDL + * object still said some data was unread. + * Fix that and bail out. + */ + mdl->readpos = mdl->bytesused; + return 0; + } + + list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { + + if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) + continue; + + rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written, + ucount - tot_written, &stop); + if (rc < 0) + return rc; + mdl->readpos += rc; + tot_written += rc; + + if (stop || /* Forced stopping point for VBI insertion */ + tot_written >= ucount || /* Reader request statisfied */ + mdl->curr_buf->readpos < mdl->curr_buf->bytesused || + mdl->readpos >= mdl->bytesused) /* MDL buffers drained */ + break; + } + return tot_written; +} + static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, size_t tot_count, int non_block) { @@ -373,12 +437,12 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, single_frame = 1; for (;;) { - struct cx18_buffer *buf; + struct cx18_mdl *mdl; int rc; - buf = cx18_get_buffer(s, non_block, &rc); + mdl = cx18_get_mdl(s, non_block, &rc); /* if there is no data available... */ - if (buf == NULL) { + if (mdl == NULL) { /* if we got data, then return that regardless */ if (tot_written) break; @@ -392,20 +456,20 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, return rc; } - rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written, + rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written, tot_count - tot_written); - if (buf != &cx->vbi.sliced_mpeg_buf) { - if (buf->readpos == buf->bytesused) - cx18_stream_put_buf_fw(s, buf); + if (mdl != &cx->vbi.sliced_mpeg_mdl) { + if (mdl->readpos == mdl->bytesused) + cx18_stream_put_mdl_fw(s, mdl); else - cx18_push(s, buf, &s->q_full); - } else if (buf->readpos == buf->bytesused) { + cx18_push(s, mdl, &s->q_full); + } else if (mdl->readpos == mdl->bytesused) { int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; cx->vbi.sliced_mpeg_size[idx] = 0; cx->vbi.inserted_frame++; - cx->vbi_data_inserted += buf->bytesused; + cx->vbi_data_inserted += mdl->bytesused; } if (rc < 0) return rc; @@ -543,7 +607,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) CX18_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); - if (atomic_read(&s->q_full.buffers)) + if (atomic_read(&s->q_full.depth)) return POLLIN | POLLRDNORM; if (eof) return POLLHUP; diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 2477461e84d7..eecf29af916c 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -28,7 +28,6 @@ #include "cx18-gpio.h" #include "cx18-i2c.h" #include "cx18-irq.h" -#include <media/ir-kbd-i2c.h> #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 @@ -97,17 +96,11 @@ static const char * const hw_devicenames[] = { "ir_rx_z8f0811_haup", }; -static const struct IR_i2c_init_data z8f0811_ir_init_data = { - .ir_codes = &ir_codes_hauppauge_new_table, - .internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR, - .type = IR_TYPE_RC5, - .name = "CX23418 Z8F0811 Hauppauge", -}; - -static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, - u8 addr) +static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, + const char *type, u8 addr) { struct i2c_board_info info; + struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data; unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; memset(&info, 0, sizeof(struct i2c_board_info)); @@ -116,9 +109,11 @@ static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case CX18_HW_Z8F0811_IR_RX_HAUP: - info.platform_data = (void *) &z8f0811_ir_init_data; - break; - default: + init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = IR_TYPE_RC5; + init_data->name = cx->card_name; + info.platform_data = init_data; break; } @@ -154,8 +149,8 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) return sd != NULL ? 0 : -1; } - if (hw & CX18_HW_Z8F0811_IR_HAUP) - return cx18_i2c_new_ir(adap, hw, type, hw_addrs[idx]); + if (hw & CX18_HW_IR_ANY) + return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]); /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index fc76e4d6ffa7..3e4fc192fdec 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -910,7 +910,8 @@ static int cx18_log_status(struct file *file, void *fh) continue; CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, - atomic_read(&s->q_full.buffers) * 100 / s->buffers, + atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100 + / s->buffers, (s->buffers * s->buf_size) / 1024, s->buffers); } CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index afe46c3d4057..f231dd09c720 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -131,13 +131,39 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) * Functions that run in a work_queue work handling context */ +static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + if (!s->dvb.enabled || mdl->bytesused == 0) + return; + + /* We ignore mdl and buf readpos accounting here - it doesn't matter */ + + /* The likely case */ + if (list_is_singular(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, + list); + if (buf->bytesused) + dvb_dmx_swfilter(&s->dvb.demux, + buf->buf, buf->bytesused); + return; + } + + list_for_each_entry(buf, &mdl->buf_list, list) { + if (buf->bytesused == 0) + break; + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, buf->bytesused); + } +} + static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) { u32 handle, mdl_ack_count, id; struct cx18_mailbox *mb; struct cx18_mdl_ack *mdl_ack; struct cx18_stream *s; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; int i; mb = &order->mb; @@ -158,7 +184,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) id = mdl_ack->id; /* * Simple integrity check for processing a stale (and possibly - * inconsistent mailbox): make sure the buffer id is in the + * inconsistent mailbox): make sure the MDL id is in the * valid range for the stream. * * We go through the trouble of dealing with stale mailboxes @@ -169,44 +195,42 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) * There are occasions when we get a half changed mailbox, * which this check catches for a handle & id mismatch. If the * handle and id do correspond, the worst case is that we - * completely lost the old buffer, but pick up the new buffer + * completely lost the old MDL, but pick up the new MDL * early (but the new mdl_ack is guaranteed to be good in this * case as the firmware wouldn't point us to a new mdl_ack until * it's filled in). * - * cx18_queue_get buf() will detect the lost buffers + * cx18_queue_get_mdl() will detect the lost MDLs * and send them back to q_free for fw rotation eventually. */ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && - !(id >= s->mdl_offset && - id < (s->mdl_offset + s->buffers))) { + !(id >= s->mdl_base_idx && + id < (s->mdl_base_idx + s->buffers))) { CX18_WARN("Fell behind! Ignoring stale mailbox with " - " inconsistent data. Lost buffer for mailbox " + " inconsistent data. Lost MDL for mailbox " "seq no %d\n", mb->request); break; } - buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); + mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used); - CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); - if (buf == NULL) { - CX18_WARN("Could not find buf %d for stream %s\n", + CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id); + if (mdl == NULL) { + CX18_WARN("Could not find MDL %d for stream %s\n", id, s->name); continue; } CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", - s->name, buf->bytesused); + s->name, mdl->bytesused); if (s->type != CX18_ENC_STREAM_TYPE_TS) - cx18_enqueue(s, buf, &s->q_full); + cx18_enqueue(s, mdl, &s->q_full); else { - if (s->dvb.enabled) - dvb_dmx_swfilter(&s->dvb.demux, buf->buf, - buf->bytesused); - cx18_enqueue(s, buf, &s->q_free); + cx18_mdl_send_to_dvb(s, mdl); + cx18_enqueue(s, mdl, &s->q_free); } } - /* Put as many buffers as possible back into fw use */ + /* Put as many MDLs as possible back into fw use */ cx18_stream_load_fw_queue(s); wake_up(&cx->dma_waitq); @@ -616,7 +640,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) /* * Wait for XPU to perform extra actions for the caller in some cases. - * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers + * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs * back in a burst shortly thereafter */ if (info->flags & API_SLOW) diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index e23aaac5b280..33a3491c4537 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -39,14 +39,14 @@ struct cx18; /* - * This structure is used by CPU to provide completed buffers information - * Its structure is dictrated by the layout of the SCB, required by the - * firmware, but its defintion needs to be here, instead of in cx18-scb.h, + * This structure is used by CPU to provide completed MDL & buffers information. + * Its structure is dictated by the layout of the SCB, required by the + * firmware, but its definition needs to be here, instead of in cx18-scb.h, * for mailbox work order scheduling */ struct cx18_mdl_ack { u32 id; /* ID of a completed MDL */ - u32 data_used; /* Total data filled in the MDL for buffer 'id' */ + u32 data_used; /* Total data filled in the MDL with 'id' */ }; /* The cx18_mailbox struct is the mailbox structure which is used for passing diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index fa1ed7897d97..63304823cef5 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -26,6 +26,7 @@ #include "cx18-queue.h" #include "cx18-streams.h" #include "cx18-scb.h" +#include "cx18-io.h" void cx18_buf_swap(struct cx18_buffer *buf) { @@ -35,151 +36,312 @@ void cx18_buf_swap(struct cx18_buffer *buf) swab32s((u32 *)(buf->buf + i)); } +void _cx18_mdl_swap(struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + list_for_each_entry(buf, &mdl->buf_list, list) { + if (buf->bytesused == 0) + break; + cx18_buf_swap(buf); + } +} + void cx18_queue_init(struct cx18_queue *q) { INIT_LIST_HEAD(&q->list); - atomic_set(&q->buffers, 0); + atomic_set(&q->depth, 0); q->bytesused = 0; } -struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q, int to_front) { - /* clear the buffer if it is not to be enqueued to the full queue */ + /* clear the mdl if it is not to be enqueued to the full queue */ if (q != &s->q_full) { - buf->bytesused = 0; - buf->readpos = 0; - buf->b_flags = 0; - buf->skipped = 0; + mdl->bytesused = 0; + mdl->readpos = 0; + mdl->m_flags = 0; + mdl->skipped = 0; + mdl->curr_buf = NULL; } /* q_busy is restricted to a max buffer count imposed by firmware */ if (q == &s->q_busy && - atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) + atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM) q = &s->q_free; spin_lock(&q->lock); if (to_front) - list_add(&buf->list, &q->list); /* LIFO */ + list_add(&mdl->list, &q->list); /* LIFO */ else - list_add_tail(&buf->list, &q->list); /* FIFO */ - q->bytesused += buf->bytesused - buf->readpos; - atomic_inc(&q->buffers); + list_add_tail(&mdl->list, &q->list); /* FIFO */ + q->bytesused += mdl->bytesused - mdl->readpos; + atomic_inc(&q->depth); spin_unlock(&q->lock); return q; } -struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) +struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) { - struct cx18_buffer *buf = NULL; + struct cx18_mdl *mdl = NULL; spin_lock(&q->lock); if (!list_empty(&q->list)) { - buf = list_first_entry(&q->list, struct cx18_buffer, list); - list_del_init(&buf->list); - q->bytesused -= buf->bytesused - buf->readpos; - buf->skipped = 0; - atomic_dec(&q->buffers); + mdl = list_first_entry(&q->list, struct cx18_mdl, list); + list_del_init(&mdl->list); + q->bytesused -= mdl->bytesused - mdl->readpos; + mdl->skipped = 0; + atomic_dec(&q->depth); } spin_unlock(&q->lock); - return buf; + return mdl; +} + +static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + u32 buf_size = s->buf_size; + u32 bytesused = mdl->bytesused; + + list_for_each_entry(buf, &mdl->buf_list, list) { + buf->readpos = 0; + if (bytesused >= buf_size) { + buf->bytesused = buf_size; + bytesused -= buf_size; + } else { + buf->bytesused = bytesused; + bytesused = 0; + } + cx18_buf_sync_for_cpu(s, buf); + } +} + +static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + if (list_is_singular(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, + list); + buf->bytesused = mdl->bytesused; + buf->readpos = 0; + cx18_buf_sync_for_cpu(s, buf); + } else { + _cx18_mdl_update_bufs_for_cpu(s, mdl); + } } -struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, +struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, u32 bytesused) { struct cx18 *cx = s->cx; - struct cx18_buffer *buf; - struct cx18_buffer *tmp; - struct cx18_buffer *ret = NULL; + struct cx18_mdl *mdl; + struct cx18_mdl *tmp; + struct cx18_mdl *ret = NULL; LIST_HEAD(sweep_up); /* * We don't have to acquire multiple q locks here, because we are * serialized by the single threaded work handler. - * Buffers from the firmware will thus remain in order as + * MDLs from the firmware will thus remain in order as * they are moved from q_busy to q_full or to the dvb ring buffer. */ spin_lock(&s->q_busy.lock); - list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { + list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) { /* * We should find what the firmware told us is done, * right at the front of the queue. If we don't, we likely have - * missed a buffer done message from the firmware. - * Once we skip a buffer repeatedly, relative to the size of + * missed an mdl done message from the firmware. + * Once we skip an mdl repeatedly, relative to the size of * q_busy, we have high confidence we've missed it. */ - if (buf->id != id) { - buf->skipped++; - if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { - /* buffer must have fallen out of rotation */ - CX18_WARN("Skipped %s, buffer %d, %d " + if (mdl->id != id) { + mdl->skipped++; + if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) { + /* mdl must have fallen out of rotation */ + CX18_WARN("Skipped %s, MDL %d, %d " "times - it must have dropped out of " - "rotation\n", s->name, buf->id, - buf->skipped); + "rotation\n", s->name, mdl->id, + mdl->skipped); /* Sweep it up to put it back into rotation */ - list_move_tail(&buf->list, &sweep_up); - atomic_dec(&s->q_busy.buffers); + list_move_tail(&mdl->list, &sweep_up); + atomic_dec(&s->q_busy.depth); } continue; } /* - * We pull the desired buffer off of the queue here. Something + * We pull the desired mdl off of the queue here. Something * will have to put it back on a queue later. */ - list_del_init(&buf->list); - atomic_dec(&s->q_busy.buffers); - ret = buf; + list_del_init(&mdl->list); + atomic_dec(&s->q_busy.depth); + ret = mdl; break; } spin_unlock(&s->q_busy.lock); /* - * We found the buffer for which we were looking. Get it ready for + * We found the mdl for which we were looking. Get it ready for * the caller to put on q_full or in the dvb ring buffer. */ if (ret != NULL) { ret->bytesused = bytesused; ret->skipped = 0; - /* readpos and b_flags were 0'ed when the buf went on q_busy */ - cx18_buf_sync_for_cpu(s, ret); + /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */ + cx18_mdl_update_bufs_for_cpu(s, ret); if (s->type != CX18_ENC_STREAM_TYPE_TS) - set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags); + set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags); } - /* Put any buffers the firmware is ignoring back into normal rotation */ - list_for_each_entry_safe(buf, tmp, &sweep_up, list) { - list_del_init(&buf->list); - cx18_enqueue(s, buf, &s->q_free); + /* Put any mdls the firmware is ignoring back into normal rotation */ + list_for_each_entry_safe(mdl, tmp, &sweep_up, list) { + list_del_init(&mdl->list); + cx18_enqueue(s, mdl, &s->q_free); } return ret; } -/* Move all buffers of a queue to q_free, while flushing the buffers */ -static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) +/* Move all mdls of a queue, while flushing the mdl */ +static void cx18_queue_flush(struct cx18_stream *s, + struct cx18_queue *q_src, struct cx18_queue *q_dst) { - struct cx18_buffer *buf; + struct cx18_mdl *mdl; - if (q == &s->q_free) + /* It only makes sense to flush to q_free or q_idle */ + if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy) return; - spin_lock(&q->lock); - while (!list_empty(&q->list)) { - buf = list_first_entry(&q->list, struct cx18_buffer, list); - list_move_tail(&buf->list, &s->q_free.list); - buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; - atomic_inc(&s->q_free.buffers); + spin_lock(&q_src->lock); + spin_lock(&q_dst->lock); + while (!list_empty(&q_src->list)) { + mdl = list_first_entry(&q_src->list, struct cx18_mdl, list); + list_move_tail(&mdl->list, &q_dst->list); + mdl->bytesused = 0; + mdl->readpos = 0; + mdl->m_flags = 0; + mdl->skipped = 0; + mdl->curr_buf = NULL; + atomic_inc(&q_dst->depth); } - cx18_queue_init(q); - spin_unlock(&q->lock); + cx18_queue_init(q_src); + spin_unlock(&q_src->lock); + spin_unlock(&q_dst->lock); } void cx18_flush_queues(struct cx18_stream *s) { - cx18_queue_flush(s, &s->q_busy); - cx18_queue_flush(s, &s->q_full); + cx18_queue_flush(s, &s->q_busy, &s->q_free); + cx18_queue_flush(s, &s->q_full, &s->q_free); +} + +/* + * Note, s->buf_pool is not protected by a lock, + * the stream better not have *anything* going on when calling this + */ +void cx18_unload_queues(struct cx18_stream *s) +{ + struct cx18_queue *q_idle = &s->q_idle; + struct cx18_mdl *mdl; + struct cx18_buffer *buf; + + /* Move all MDLS to q_idle */ + cx18_queue_flush(s, &s->q_busy, q_idle); + cx18_queue_flush(s, &s->q_full, q_idle); + cx18_queue_flush(s, &s->q_free, q_idle); + + /* Reset MDL id's and move all buffers back to the stream's buf_pool */ + spin_lock(&q_idle->lock); + list_for_each_entry(mdl, &q_idle->list, list) { + while (!list_empty(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, + struct cx18_buffer, list); + list_move_tail(&buf->list, &s->buf_pool); + buf->bytesused = 0; + buf->readpos = 0; + } + mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */ + /* all other mdl fields were cleared by cx18_queue_flush() */ + } + spin_unlock(&q_idle->lock); +} + +/* + * Note, s->buf_pool is not protected by a lock, + * the stream better not have *anything* going on when calling this + */ +void cx18_load_queues(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + struct cx18_mdl *mdl; + struct cx18_buffer *buf; + int mdl_id; + int i; + u32 partial_buf_size; + + /* + * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free + * Excess MDLs are left on q_idle + * Excess buffers are left in buf_pool and/or on an MDL in q_idle + */ + mdl_id = s->mdl_base_idx; + for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl; + mdl != NULL && i == s->bufs_per_mdl; + mdl = cx18_dequeue(s, &s->q_idle)) { + + mdl->id = mdl_id; + + for (i = 0; i < s->bufs_per_mdl; i++) { + if (list_empty(&s->buf_pool)) + break; + + buf = list_first_entry(&s->buf_pool, struct cx18_buffer, + list); + list_move_tail(&buf->list, &mdl->buf_list); + + /* update the firmware's MDL array with this buffer */ + cx18_writel(cx, buf->dma_handle, + &cx->scb->cpu_mdl[mdl_id + i].paddr); + cx18_writel(cx, s->buf_size, + &cx->scb->cpu_mdl[mdl_id + i].length); + } + + if (i == s->bufs_per_mdl) { + /* + * The encoder doesn't honor s->mdl_size. So in the + * case of a non-integral number of buffers to meet + * mdl_size, we lie about the size of the last buffer + * in the MDL to get the encoder to really only send + * us mdl_size bytes per MDL transfer. + */ + partial_buf_size = s->mdl_size % s->buf_size; + if (partial_buf_size) { + cx18_writel(cx, partial_buf_size, + &cx->scb->cpu_mdl[mdl_id + i - 1].length); + } + cx18_enqueue(s, mdl, &s->q_free); + } else { + /* Not enough buffers for this MDL; we won't use it */ + cx18_push(s, mdl, &s->q_idle); + } + mdl_id += i; + } +} + +void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl) +{ + int dma = s->dma; + u32 buf_size = s->buf_size; + struct pci_dev *pci_dev = s->cx->pci_dev; + struct cx18_buffer *buf; + + list_for_each_entry(buf, &mdl->buf_list, list) + pci_dma_sync_single_for_device(pci_dev, buf->dma_handle, + buf_size, dma); } int cx18_stream_alloc(struct cx18_stream *s) @@ -190,44 +352,62 @@ int cx18_stream_alloc(struct cx18_stream *s) if (s->buffers == 0) return 0; - CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n", + CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers " + "(%d.%02d kB total)\n", s->name, s->buffers, s->buf_size, - s->buffers * s->buf_size / 1024); + s->buffers * s->buf_size / 1024, + (s->buffers * s->buf_size * 100 / 1024) % 100); - if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - + if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] - (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - ((char __iomem *)cx->scb->cpu_mdl)); CX18_ERR("Too many buffers, cannot fit in SCB area\n"); CX18_ERR("Max buffers = %zd\n", - bufsz / sizeof(struct cx18_mdl)); + bufsz / sizeof(struct cx18_mdl_ent)); return -ENOMEM; } - s->mdl_offset = cx->mdl_offset; + s->mdl_base_idx = cx->free_mdl_idx; - /* allocate stream buffers. Initially all buffers are in q_free. */ + /* allocate stream buffers and MDLs */ for (i = 0; i < s->buffers; i++) { - struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer), - GFP_KERNEL|__GFP_NOWARN); + struct cx18_mdl *mdl; + struct cx18_buffer *buf; - if (buf == NULL) + /* 1 MDL per buffer to handle the worst & also default case */ + mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN); + if (mdl == NULL) break; + + buf = kzalloc(sizeof(struct cx18_buffer), + GFP_KERNEL|__GFP_NOWARN); + if (buf == NULL) { + kfree(mdl); + break; + } + buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); if (buf->buf == NULL) { + kfree(mdl); kfree(buf); break; } - buf->id = cx->buffer_id++; + + INIT_LIST_HEAD(&mdl->list); + INIT_LIST_HEAD(&mdl->buf_list); + mdl->id = s->mdl_base_idx; /* a somewhat safe value */ + cx18_enqueue(s, mdl, &s->q_idle); + INIT_LIST_HEAD(&buf->list); buf->dma_handle = pci_map_single(s->cx->pci_dev, buf->buf, s->buf_size, s->dma); cx18_buf_sync_for_cpu(s, buf); - cx18_enqueue(s, buf, &s->q_free); + list_add_tail(&buf->list, &s->buf_pool); } if (i == s->buffers) { - cx->mdl_offset += s->buffers; + cx->free_mdl_idx += s->buffers; return 0; } CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name); @@ -237,13 +417,21 @@ int cx18_stream_alloc(struct cx18_stream *s) void cx18_stream_free(struct cx18_stream *s) { + struct cx18_mdl *mdl; struct cx18_buffer *buf; - /* move all buffers to q_free */ - cx18_flush_queues(s); + /* move all buffers to buf_pool and all MDLs to q_idle */ + cx18_unload_queues(s); + + /* empty q_idle */ + while ((mdl = cx18_dequeue(s, &s->q_idle))) + kfree(mdl); + + /* empty buf_pool */ + while (!list_empty(&s->buf_pool)) { + buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list); + list_del_init(&buf->list); - /* empty q_free */ - while ((buf = cx18_dequeue(s, &s->q_free))) { pci_unmap_single(s->cx->pci_dev, buf->dma_handle, s->buf_size, s->dma); kfree(buf->buf); diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h index 4de06269d88f..88a6d34ad3bb 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/drivers/media/video/cx18/cx18-queue.h @@ -40,32 +40,59 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s, s->buf_size, s->dma); } +void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl); + +static inline void cx18_mdl_sync_for_device(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + if (list_is_singular(&mdl->buf_list)) + cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list, + struct cx18_buffer, + list)); + else + _cx18_mdl_sync_for_device(s, mdl); +} + void cx18_buf_swap(struct cx18_buffer *buf); +void _cx18_mdl_swap(struct cx18_mdl *mdl); + +static inline void cx18_mdl_swap(struct cx18_mdl *mdl) +{ + if (list_is_singular(&mdl->buf_list)) + cx18_buf_swap(list_first_entry(&mdl->buf_list, + struct cx18_buffer, list)); + else + _cx18_mdl_swap(mdl); +} /* cx18_queue utility functions */ -struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q, int to_front); static inline -struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q) { - return _cx18_enqueue(s, buf, q, 0); /* FIFO */ + return _cx18_enqueue(s, mdl, q, 0); /* FIFO */ } static inline -struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q) { - return _cx18_enqueue(s, buf, q, 1); /* LIFO */ + return _cx18_enqueue(s, mdl, q, 1); /* LIFO */ } void cx18_queue_init(struct cx18_queue *q); -struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); -struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, +struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); +struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, u32 bytesused); void cx18_flush_queues(struct cx18_stream *s); +/* queue MDL reconfiguration helpers */ +void cx18_unload_queues(struct cx18_stream *s); +void cx18_load_queues(struct cx18_stream *s); + /* cx18_stream utility functions */ int cx18_stream_alloc(struct cx18_stream *s); void cx18_stream_free(struct cx18_stream *s); diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h index 1dc1c431f5a1..368f23d08709 100644 --- a/drivers/media/video/cx18/cx18-scb.h +++ b/drivers/media/video/cx18/cx18-scb.h @@ -81,7 +81,7 @@ /* This structure is used by EPU to provide memory descriptors in its memory */ -struct cx18_mdl { +struct cx18_mdl_ent { u32 paddr; /* Physical address of a buffer segment */ u32 length; /* Length of the buffer segment */ }; @@ -272,7 +272,7 @@ struct cx18_scb { struct cx18_mailbox ppu2epu_mb; struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS]; - struct cx18_mdl cpu_mdl[1]; + struct cx18_mdl_ent cpu_mdl[1]; }; void cx18_init_scb(struct cx18 *cx); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 7df513a2dba8..c398651dd74c 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -115,6 +115,9 @@ static void cx18_stream_init(struct cx18 *cx, int type) s->dma = cx18_stream_info[type].dma; s->buffers = cx->stream_buffers[type]; s->buf_size = cx->stream_buf_size[type]; + INIT_LIST_HEAD(&s->buf_pool); + s->bufs_per_mdl = 1; + s->mdl_size = s->buf_size * s->bufs_per_mdl; init_waitqueue_head(&s->waitq); s->id = -1; @@ -124,6 +127,8 @@ static void cx18_stream_init(struct cx18 *cx, int type) cx18_queue_init(&s->q_busy); spin_lock_init(&s->q_full.lock); cx18_queue_init(&s->q_full); + spin_lock_init(&s->q_idle.lock); + cx18_queue_init(&s->q_idle); INIT_WORK(&s->out_work_order, cx18_out_work_handler); } @@ -257,9 +262,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type) switch (vfl_type) { case VFL_TYPE_GRABBER: - CX18_INFO("Registered device video%d for %s (%d x %d kB)\n", + CX18_INFO("Registered device video%d for %s " + "(%d x %d.%02d kB)\n", num, s->name, cx->stream_buffers[type], - cx->stream_buf_size[type]/1024); + cx->stream_buf_size[type] / 1024, + (cx->stream_buf_size[type] * 100 / 1024) % 100); break; case VFL_TYPE_RADIO: @@ -441,8 +448,8 @@ static void cx18_vbi_setup(struct cx18_stream *s) } static -struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf) +struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s, + struct cx18_mdl *mdl) { struct cx18 *cx = s->cx; struct cx18_queue *q; @@ -451,16 +458,16 @@ struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, if (s->handle == CX18_INVALID_TASK_HANDLE || test_bit(CX18_F_S_STOPPING, &s->s_flags) || !test_bit(CX18_F_S_STREAMING, &s->s_flags)) - return cx18_enqueue(s, buf, &s->q_free); + return cx18_enqueue(s, mdl, &s->q_free); - q = cx18_enqueue(s, buf, &s->q_busy); + q = cx18_enqueue(s, mdl, &s->q_busy); if (q != &s->q_busy) - return q; /* The firmware has the max buffers it can handle */ + return q; /* The firmware has the max MDLs it can handle */ - cx18_buf_sync_for_device(s, buf); + cx18_mdl_sync_for_device(s, mdl); cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, - (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, - 1, buf->id, s->buf_size); + (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem, + s->bufs_per_mdl, mdl->id, s->mdl_size); return q; } @@ -468,19 +475,19 @@ static void _cx18_stream_load_fw_queue(struct cx18_stream *s) { struct cx18_queue *q; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; - if (atomic_read(&s->q_free.buffers) == 0 || - atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) + if (atomic_read(&s->q_free.depth) == 0 || + atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM) return; /* Move from q_free to q_busy notifying the firmware, until the limit */ do { - buf = cx18_dequeue(s, &s->q_free); - if (buf == NULL) + mdl = cx18_dequeue(s, &s->q_free); + if (mdl == NULL) break; - q = _cx18_stream_put_buf_fw(s, buf); - } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM + q = _cx18_stream_put_mdl_fw(s, mdl); + } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM && q == &s->q_busy); } @@ -492,11 +499,51 @@ void cx18_out_work_handler(struct work_struct *work) _cx18_stream_load_fw_queue(s); } +static void cx18_stream_configure_mdls(struct cx18_stream *s) +{ + cx18_unload_queues(s); + + switch (s->type) { + case CX18_ENC_STREAM_TYPE_YUV: + /* + * Height should be a multiple of 32 lines. + * Set the MDL size to the exact size needed for one frame. + * Use enough buffers per MDL to cover the MDL size + */ + s->mdl_size = 720 * s->cx->params.height * 3 / 2; + s->bufs_per_mdl = s->mdl_size / s->buf_size; + if (s->mdl_size % s->buf_size) + s->bufs_per_mdl++; + break; + case CX18_ENC_STREAM_TYPE_VBI: + s->bufs_per_mdl = 1; + if (cx18_raw_vbi(s->cx)) { + s->mdl_size = (s->cx->is_60hz ? 12 : 18) + * 2 * vbi_active_samples; + } else { + /* + * See comment in cx18_vbi_setup() below about the + * extra lines we capture in sliced VBI mode due to + * the lines on which EAV RP codes toggle. + */ + s->mdl_size = s->cx->is_60hz + ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz + : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; + } + break; + default: + s->bufs_per_mdl = 1; + s->mdl_size = s->buf_size * s->bufs_per_mdl; + break; + } + + cx18_load_queues(s); +} + int cx18_start_v4l2_encode_stream(struct cx18_stream *s) { u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; - struct cx18_buffer *buf; int captype = 0; struct cx18_api_func_private priv; @@ -619,14 +666,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); /* Init all the cpu_mdls for this stream */ - cx18_flush_queues(s); - spin_lock(&s->q_free.lock); - list_for_each_entry(buf, &s->q_free.list, list) { - cx18_writel(cx, buf->dma_handle, - &cx->scb->cpu_mdl[buf->id].paddr); - cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); - } - spin_unlock(&s->q_free.lock); + cx18_stream_configure_mdls(s); _cx18_stream_load_fw_queue(s); /* begin_capture */ diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 1afc3fd9d822..4a01db5e5a35 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -28,18 +28,18 @@ int cx18_streams_setup(struct cx18 *cx); int cx18_streams_register(struct cx18 *cx); void cx18_streams_cleanup(struct cx18 *cx, int unregister); -/* Related to submission of buffers to firmware */ +/* Related to submission of mdls to firmware */ static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) { struct cx18 *cx = s->cx; queue_work(cx->out_work_queue, &s->out_work_order); } -static inline void cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf) +static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s, + struct cx18_mdl *mdl) { - /* Put buf on q_free; the out work handler will move buf(s) to q_busy */ - cx18_enqueue(s, buf, &s->q_free); + /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */ + cx18_enqueue(s, mdl, &s->q_free); cx18_stream_load_fw_queue(s); } diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index c2aef4add31d..574c1c6974f8 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -105,6 +105,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) /* Compress raw VBI format, removes leading SAV codes and surplus space after the frame. Returns new compressed size. */ +/* FIXME - this function ignores the input size. */ static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) { u32 line_size = vbi_active_samples; @@ -185,8 +186,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, return line; } -void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, - int streamtype) +static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf) { /* * The CX23418 provides a 12 byte header in its raw VBI buffers to us: @@ -203,9 +203,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, u32 pts; int lines; - if (streamtype != CX18_ENC_STREAM_TYPE_VBI) - return; - /* * The CX23418 sends us data that is 32 bit little-endian swapped, * but we want the raw VBI bytes in the order they were in the raster @@ -250,3 +247,31 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, copy_vbi_data(cx, lines, pts); cx->vbi.frame++; } + +void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, + int streamtype) +{ + struct cx18_buffer *buf; + u32 orig_used; + + if (streamtype != CX18_ENC_STREAM_TYPE_VBI) + return; + + /* + * Big assumption here: + * Every buffer hooked to the MDL's buf_list is a complete VBI frame + * that ends at the end of the buffer. + * + * To assume anything else would make the code in this file + * more complex, or require extra memcpy()'s to make the + * buffers satisfy the above assumption. It's just simpler to set + * up the encoder buffer transfers to make the assumption true. + */ + list_for_each_entry(buf, &mdl->buf_list, list) { + orig_used = buf->bytesused; + if (orig_used == 0) + break; + _cx18_process_vbi_data(cx, buf); + mdl->bytesused -= (orig_used - buf->bytesused); + } +} diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h index e7e1ae427f34..b365cf4b4668 100644 --- a/drivers/media/video/cx18/cx18-vbi.h +++ b/drivers/media/video/cx18/cx18-vbi.h @@ -21,6 +21,6 @@ * 02111-1307 USA */ -void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, +void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, int streamtype); int cx18_used_line(struct cx18 *cx, int line, int field); diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 45494b094e7f..9c0b5bb1b019 100644 --- a/drivers/media/video/cx18/cx18-version.h +++ b/drivers/media/video/cx18/cx18-version.h @@ -24,7 +24,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 -#define CX18_DRIVER_VERSION_MINOR 2 +#define CX18_DRIVER_VERSION_MINOR 3 #define CX18_DRIVER_VERSION_PATCHLEVEL 0 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 9956abf576c5..868806effdcf 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -363,7 +363,7 @@ /* Description: This command provides the offset to a Memory Descriptor List IN[0] - Task handle. Handle of the task to start IN[1] - Offset of the MDL from the beginning of the local DDR. - IN[2] - Number of cx18_mdl structures in the array pointed to by IN[1] + IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1] IN[3] - Buffer ID IN[4] - Total buffer length ReturnCode - One of the ERR_DE_... */ diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index 28f48f41f218..c2174413ab29 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -2414,9 +2414,11 @@ int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev) cx231xx_info("No ACK after %d msec -GPIO I2C failed!", nInit * 10); - /* readAck - throuth clock stretch ,slave has given a SCL signal, - so the SDA data can be directly read. */ + /* + * readAck + * through clock stretch, slave has given a SCL signal, + * so the SDA data can be directly read. + */ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) { diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c index 48f22fa38e6c..cd135f01b9c1 100644 --- a/drivers/media/video/cx231xx/cx231xx-input.c +++ b/drivers/media/video/cx231xx/cx231xx-input.c @@ -126,8 +126,7 @@ static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) if (do_sendkey) { dprintk("sending keypress\n"); - ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], - poll_result.rc_data[0]); + ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0]); ir_input_nokey(ir->input, &ir->ir); } @@ -198,7 +197,11 @@ int cx231xx_ir_init(struct cx231xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, + dev->board.ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; @@ -223,6 +226,7 @@ err_out_stop: cx231xx_ir_stop(ir); dev->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -237,6 +241,7 @@ int cx231xx_ir_fini(struct cx231xx *dev) return 0; cx231xx_ir_stop(ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 36503725d973..d095aa0d6d19 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -2106,7 +2106,7 @@ static int cx231xx_v4l2_close(struct file *filp) } /* Save some power by putting tuner to sleep */ - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); /* do this before setting alternate! */ cx231xx_uninit_isoc(dev); diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index fd3fc3e3198a..bcdda9a9aa96 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -18,7 +18,9 @@ config VIDEO_CX23885 select DVB_TDA10048 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_STV6110 if !DVB_FE_CUSTOMISE + select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_DS3000 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index ab8ea35c9bfb..5787ae243631 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -1,6 +1,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ - netup-init.o cimax2.o netup-eeprom.o + cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \ + netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 6c3b51ce3372..0eed852c61e9 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -37,6 +37,7 @@ #include <media/cx2341x.h> #include "cx23885.h" +#include "cx23885-ioctl.h" #define CX23885_FIRM_IMAGE_SIZE 376836 #define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" @@ -318,7 +319,7 @@ static int mc417_wait_ready(struct cx23885_dev *dev) } } -static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) +int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) { u32 regval; @@ -382,7 +383,7 @@ static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) return mc417_wait_ready(dev); } -static int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) +int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) { int retval; u32 regval; @@ -1724,6 +1725,11 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_log_status = vidioc_log_status, .vidioc_querymenu = vidioc_querymenu, .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_chip_ident = cx23885_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cx23885_g_register, + .vidioc_s_register = cx23885_s_register, +#endif }; static struct video_device cx23885_mpeg_template = { diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index bfdf79f1033c..1ec48169277d 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -28,6 +28,7 @@ #include "cx23885.h" #include "tuner-xc2028.h" #include "netup-init.h" +#include "cx23888-ir.h" /* ------------------------------------------------------------------ */ /* board config info */ @@ -199,11 +200,61 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_MYGICA_X8506] = { .name = "Mygica X8506 DMB-TH", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, + .input = { + { + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_COMPOSITE2, + }, + { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_COMPOSITE8, + }, + { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_SVIDEO_LUMA3 | + CX25840_SVIDEO_CHROMA4, + }, + { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_COMPONENT_ON | + CX25840_VIN1_CH1 | + CX25840_VIN6_CH2 | + CX25840_VIN7_CH3, + }, + }, }, [CX23885_BOARD_MAGICPRO_PROHDTVE2] = { .name = "Magic-Pro ProHDTV Extreme 2", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, + .input = { + { + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_COMPOSITE2, + }, + { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_COMPOSITE8, + }, + { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_SVIDEO_LUMA3 | + CX25840_SVIDEO_CHROMA4, + }, + { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_COMPONENT_ON | + CX25840_VIN1_CH1 | + CX25840_VIN6_CH2 | + CX25840_VIN7_CH3, + }, + }, }, [CX23885_BOARD_HAUPPAUGE_HVR1850] = { .name = "Hauppauge WinTV-HVR1850", @@ -214,6 +265,15 @@ struct cx23885_board cx23885_boards[] = { .name = "Compro VideoMate E800", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1290] = { + .name = "Hauppauge WinTV-HVR1290", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_MYGICA_X8558PRO] = { + .name = "Mygica X8558 PRO DMB-TH", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -349,6 +409,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x1858, .subdevice = 0xe800, .card = CX23885_BOARD_COMPRO_VIDEOMATE_E800, + }, { + .subvendor = 0x0070, + .subdevice = 0x8551, + .card = CX23885_BOARD_HAUPPAUGE_HVR1290, + }, { + .subvendor = 0x14f1, + .subdevice = 0x8578, + .card = CX23885_BOARD_MYGICA_X8558PRO, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -509,9 +577,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) * DVB-T and MPEG2 HW Encoder */ break; case 85021: - /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM, + /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM, Dual channel ATSC and MPEG2 HW Encoder */ break; + case 85721: + /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR, + Dual channel ATSC and Basic analog */ + break; default: printk(KERN_WARNING "%s: warning: " "unknown hauppauge model #%d\n", @@ -710,10 +782,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); break; case CX23885_BOARD_TBS_6920: - case CX23885_BOARD_TEVII_S470: cx_write(MC417_CTL, 0x00000036); cx_write(MC417_OEN, 0x00001000); - cx_write(MC417_RWD, 0x00001800); + cx_set(MC417_RWD, 0x00000002); + mdelay(200); + cx_clear(MC417_RWD, 0x00000800); + mdelay(200); + cx_set(MC417_RWD, 0x00000800); + mdelay(200); break; case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: /* GPIO-0 INTA from CiMax1 @@ -758,15 +834,26 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_MYGICA_X8506: case CX23885_BOARD_MAGICPRO_PROHDTVE2: + /* GPIO-0 (0)Analog / (1)Digital TV */ /* GPIO-1 reset XC5000 */ /* GPIO-2 reset LGS8GL5 / LGS8G75 */ - cx_set(GP0_IO, 0x00060000); - cx_clear(GP0_IO, 0x00000006); + cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1); + cx23885_gpio_clear(dev, GPIO_1 | GPIO_2); mdelay(100); - cx_set(GP0_IO, 0x00060006); + cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2); + mdelay(100); + break; + case CX23885_BOARD_MYGICA_X8558PRO: + /* GPIO-0 reset first ATBM8830 */ + /* GPIO-1 reset second ATBM8830 */ + cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1); + cx23885_gpio_clear(dev, GPIO_0 | GPIO_1); + mdelay(100); + cx23885_gpio_set(dev, GPIO_0 | GPIO_1); mdelay(100); break; case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: /* GPIO-0 656_CLK */ /* GPIO-1 656_D0 */ /* GPIO-2 Wake# */ @@ -801,6 +888,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) int cx23885_ir_init(struct cx23885_dev *dev) { + int ret = 0; switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: @@ -812,15 +900,46 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: - case CX23885_BOARD_HAUPPAUGE_HVR1850: /* FIXME: Implement me */ break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + ret = cx23888_ir_probe(dev); + if (ret) + break; + dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR); + dev->pci_irqmask |= PCI_MSK_IR; + break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: request_module("ir-kbd-i2c"); break; } - return 0; + return ret; +} + +void cx23885_ir_fini(struct cx23885_dev *dev) +{ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + dev->pci_irqmask &= ~PCI_MSK_IR; + cx_clear(PCI_INT_MSK, PCI_MSK_IR); + cx23888_ir_remove(dev); + dev->sd_ir = NULL; + break; + } +} + +void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) +{ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR)) + cx_set(PCI_INT_MSK, PCI_MSK_IR); + break; + } } void cx23885_card_setup(struct cx23885_dev *dev) @@ -853,6 +972,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -886,8 +1006,12 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; - case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_TBS_6920: + ts1->gen_ctrl_val = 0x4; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; + case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_DVBWORLD_2005: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -907,6 +1031,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_MYGICA_X8558PRO: + ts1->gen_ctrl_val = 0x5; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: @@ -922,6 +1054,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_HAUPPAUGE_HVR1290: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -939,6 +1072,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + case CX23885_BOARD_HAUPPAUGE_HVR1290: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index c31284ba19dd..04b12d27bc13 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -32,6 +32,9 @@ #include "cx23885.h" #include "cimax2.h" +#include "cx23888-ir.h" +#include "cx23885-ir.h" +#include "cx23885-input.h" MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); @@ -753,6 +756,23 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev) __func__, dev->hwrevision); } +/* Find the first v4l2_subdev member of the group id in hw */ +struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw) +{ + struct v4l2_subdev *result = NULL; + struct v4l2_subdev *sd; + + spin_lock(&dev->v4l2_dev.lock); + v4l2_device_for_each_subdev(sd, &dev->v4l2_dev) { + if (sd->grp_id == hw) { + result = sd; + break; + } + } + spin_unlock(&dev->v4l2_dev.lock); + return result; +} + static int cx23885_dev_setup(struct cx23885_dev *dev) { int i; @@ -899,7 +919,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); cx23885_card_setup(dev); - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); cx23885_ir_init(dev); if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { @@ -1637,6 +1657,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) u32 ts1_status, ts1_mask; u32 ts2_status, ts2_mask; int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0; + bool ir_handled = false; pci_status = cx_read(PCI_INT_STAT); pci_mask = cx_read(PCI_INT_MSK); @@ -1662,18 +1683,12 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", ts2_status, ts2_mask, ts2_count); - if ((pci_status & PCI_MSK_RISC_RD) || - (pci_status & PCI_MSK_RISC_WR) || - (pci_status & PCI_MSK_AL_RD) || - (pci_status & PCI_MSK_AL_WR) || - (pci_status & PCI_MSK_APB_DMA) || - (pci_status & PCI_MSK_VID_C) || - (pci_status & PCI_MSK_VID_B) || - (pci_status & PCI_MSK_VID_A) || - (pci_status & PCI_MSK_AUD_INT) || - (pci_status & PCI_MSK_AUD_EXT) || - (pci_status & PCI_MSK_GPIO0) || - (pci_status & PCI_MSK_GPIO1)) { + if (pci_status & (PCI_MSK_RISC_RD | PCI_MSK_RISC_WR | + PCI_MSK_AL_RD | PCI_MSK_AL_WR | PCI_MSK_APB_DMA | + PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A | + PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT | + PCI_MSK_GPIO0 | PCI_MSK_GPIO1 | + PCI_MSK_IR)) { if (pci_status & PCI_MSK_RISC_RD) dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n", @@ -1722,6 +1737,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (pci_status & PCI_MSK_GPIO1) dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n", PCI_MSK_GPIO1); + + if (pci_status & PCI_MSK_IR) + dprintk(7, " (PCI_MSK_IR 0x%08x)\n", + PCI_MSK_IR); } if (cx23885_boards[dev->board].cimax > 0 && @@ -1752,12 +1771,48 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (vida_status) handled += cx23885_video_irq(dev, vida_status); + if (pci_status & PCI_MSK_IR) { + v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine, + pci_status, &ir_handled); + if (ir_handled) + handled++; + } + if (handled) cx_write(PCI_INT_STAT, pci_status); out: return IRQ_RETVAL(handled); } +static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct cx23885_dev *dev; + + if (sd == NULL) + return; + + dev = to_cx23885(sd->v4l2_dev); + + switch (notification) { + case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */ + if (sd == dev->sd_ir) + cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg); + break; + case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */ + if (sd == dev->sd_ir) + cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg); + break; + } +} + +static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev) +{ + INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler); + INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler); + dev->v4l2_dev.notify = cx23885_v4l2_dev_notify; +} + static inline int encoder_on_portb(struct cx23885_dev *dev) { return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER; @@ -1816,6 +1871,26 @@ void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask) printk(KERN_INFO "%s: Unsupported\n", dev->name); } +u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask) +{ + if (mask & 0x00000007) + return (cx_read(GP0_IO) >> 8) & mask & 0x7; + + if (mask & 0x0007fff8) { + if (encoder_on_portb(dev) || encoder_on_portc(dev)) + printk(KERN_ERR + "%s: Reading GPIO moving on encoder ports\n", + dev->name); + return (cx_read(MC417_RWD) & ((mask & 0x7fff8) >> 3)) << 3; + } + + /* TODO: 23-19 */ + if (mask & 0x00f80000) + printk(KERN_INFO "%s: Unsupported\n", dev->name); + + return 0; +} + void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) { if ((mask & 0x00000007) && asoutput) @@ -1854,6 +1929,9 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, if (err < 0) goto fail_free; + /* Prepare to handle notifications from subdevices */ + cx23885_v4l2_dev_notify_init(dev); + /* pci init */ dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { @@ -1896,6 +1974,14 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, break; } + /* + * The CX2388[58] IR controller can start firing interrupts when + * enabled, so these have to take place after the cx23885_irq() handler + * is hooked up by the call to request_irq() above. + */ + cx23885_ir_pci_int_enable(dev); + cx23885_input_init(dev); + return 0; fail_irq: @@ -1912,6 +1998,9 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev) struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct cx23885_dev *dev = to_cx23885(v4l2_dev); + cx23885_input_fini(dev); + cx23885_ir_fini(dev); + cx23885_shutdown(dev); pci_disable_device(pci_dev); @@ -1957,7 +2046,7 @@ static struct pci_driver cx23885_pci_driver = { .resume = NULL, }; -static int cx23885_init(void) +static int __init cx23885_init(void) { printk(KERN_INFO "cx23885 driver version %d.%d.%d loaded\n", (CX23885_VERSION_CODE >> 16) & 0xff, @@ -1970,7 +2059,7 @@ static int cx23885_init(void) return pci_register_driver(&cx23885_pci_driver); } -static void cx23885_fini(void) +static void __exit cx23885_fini(void) { pci_unregister_driver(&cx23885_pci_driver); } diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 45e13ee66dc7..e45d2df08138 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -38,6 +38,7 @@ #include "tda18271.h" #include "lgdt330x.h" #include "xc5000.h" +#include "max2165.h" #include "tda10048.h" #include "tuner-xc2028.h" #include "tuner-simple.h" @@ -54,6 +55,9 @@ #include "netup-eeprom.h" #include "netup-init.h" #include "lgdt3305.h" +#include "atbm8830.h" +#include "ds3000.h" +#include "cx23885-f300.h" static unsigned int debug; @@ -400,6 +404,7 @@ static struct stv0900_reg stv0900_ts_regs[] = { static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, + .demod_mode = 1, /* dual */ .xtal = 8000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ @@ -414,34 +419,22 @@ static struct stv6110_config netup_stv6110_tunerconfig_a = { .i2c_address = 0x60, .mclk = 16000000, .clk_div = 1, + .gain = 8, /* +16 dB - maximum gain */ }; static struct stv6110_config netup_stv6110_tunerconfig_b = { .i2c_address = 0x63, .mclk = 16000000, .clk_div = 1, + .gain = 8, /* +16 dB - maximum gain */ }; -static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct cx23885_tsport *port = fe->dvb->priv; - struct cx23885_dev *dev = port->dev; - - if (voltage == SEC_VOLTAGE_18) - cx_write(MC417_RWD, 0x00001e00);/* GPIO-13 high */ - else if (voltage == SEC_VOLTAGE_13) - cx_write(MC417_RWD, 0x00001a00);/* GPIO-13 low */ - else - cx_write(MC417_RWD, 0x00001800);/* GPIO-12 low */ - return 0; -} - static struct cx24116_config tbs_cx24116_config = { - .demod_address = 0x05, + .demod_address = 0x55, }; -static struct cx24116_config tevii_cx24116_config = { - .demod_address = 0x55, +static struct ds3000_config tevii_ds3000_config = { + .demod_address = 0x68, }; static struct cx24116_config dvbworld_cx24116_config = { @@ -486,11 +479,40 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe, break; } break; + case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + /* Select Digital TV */ + cx23885_gpio_set(dev, GPIO_0); + break; } - return (port->set_frontend_save) ? - port->set_frontend_save(fe, param) : -ENODEV; + return 0; } +static int cx23885_dvb_fe_ioctl_override(struct dvb_frontend *fe, + unsigned int cmd, void *parg, + unsigned int stage) +{ + int err = 0; + + switch (stage) { + case DVB_FE_IOCTL_PRE: + + switch (cmd) { + case FE_SET_FRONTEND: + err = cx23885_dvb_set_frontend(fe, + (struct dvb_frontend_parameters *) parg); + break; + } + break; + + case DVB_FE_IOCTL_POST: + /* no post-ioctl handling required */ + break; + } + return err; +}; + + static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = { .prod = LGS8GXX_PROD_LGS8G75, .demod_address = 0x19, @@ -511,6 +533,38 @@ static struct xc5000_config magicpro_prohdtve2_xc5000_config = { .if_khz = 6500, }; +static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x44, + .serial_ts = 0, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, +}; + +static struct max2165_config mygic_x8558pro_max2165_cfg1 = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + +static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x44, + .serial_ts = 1, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, +}; + +static struct max2165_config mygic_x8558pro_max2165_cfg2 = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -550,12 +604,6 @@ static int dvb_register(struct cx23885_tsport *port) 0x60, &dev->i2c_bus[1].i2c_adap, &hauppauge_hvr127x_config); } - - /* FIXME: temporary hack */ - /* define bridge override to set_frontend */ - port->set_frontend_save = fe0->dvb.frontend->ops.set_frontend; - fe0->dvb.frontend->ops.set_frontend = cx23885_dvb_set_frontend; - break; case CX23885_BOARD_HAUPPAUGE_HVR1255: i2c_bus = &dev->i2c_bus[0]; @@ -772,23 +820,23 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_TBS_6920: - i2c_bus = &dev->i2c_bus[0]; + i2c_bus = &dev->i2c_bus[1]; fe0->dvb.frontend = dvb_attach(cx24116_attach, - &tbs_cx24116_config, - &i2c_bus->i2c_adap); + &tbs_cx24116_config, + &i2c_bus->i2c_adap); if (fe0->dvb.frontend != NULL) - fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; break; case CX23885_BOARD_TEVII_S470: i2c_bus = &dev->i2c_bus[1]; - fe0->dvb.frontend = dvb_attach(cx24116_attach, - &tevii_cx24116_config, - &i2c_bus->i2c_adap); + fe0->dvb.frontend = dvb_attach(ds3000_attach, + &tevii_ds3000_config, + &i2c_bus->i2c_adap); if (fe0->dvb.frontend != NULL) - fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; break; case CX23885_BOARD_DVBWORLD_2005: @@ -814,8 +862,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, - LNBH24_TTX, 0x09)) + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x09)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -835,8 +883,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, - LNBH24_TTX, 0x0a)) + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x0a)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -872,6 +920,7 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(s5h1411_attach, &hcw_s5h1411_config, @@ -881,6 +930,36 @@ static int dvb_register(struct cx23885_tsport *port) 0x60, &dev->i2c_bus[0].i2c_adap, &hauppauge_tda18271_config); break; + case CX23885_BOARD_MYGICA_X8558PRO: + switch (port->nr) { + /* port B */ + case 1: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(atbm8830_attach, + &mygica_x8558pro_atbm8830_cfg1, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(max2165_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &mygic_x8558pro_max2165_cfg1); + } + break; + /* port C */ + case 2: + i2c_bus = &dev->i2c_bus[1]; + fe0->dvb.frontend = dvb_attach(atbm8830_attach, + &mygica_x8558pro_atbm8830_cfg2, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(max2165_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &mygic_x8558pro_max2165_cfg2); + } + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " @@ -897,14 +976,15 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->callback = cx23885_tuner_callback; /* Put the analog decoder in standby to keep it quiet */ - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); if (fe0->dvb.frontend->ops.analog_ops.standby) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); /* register everything */ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, - &dev->pci->dev, adapter_nr, 0); + &dev->pci->dev, adapter_nr, 0, + cx23885_dvb_fe_ioctl_override); /* init CI & MAC */ switch (dev->board) { @@ -940,7 +1020,7 @@ int cx23885_dvb_register(struct cx23885_tsport *port) int err, i; /* Here we need to allocate the correct number of frontends, - * as reflected in the cards struct. The reality is that currrently + * as reflected in the cards struct. The reality is that currently * no cx23885 boards support this - yet. But, if we don't modify this * code then the second frontend would never be allocated (later) * and fail with error before the attach in dvb_register(). diff --git a/drivers/media/video/cx23885/cx23885-f300.c b/drivers/media/video/cx23885/cx23885-f300.c new file mode 100644 index 000000000000..93998f220986 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-f300.c @@ -0,0 +1,177 @@ +/* + * Driver for Silicon Labs C8051F300 microcontroller. + * + * It is used for LNB power control in TeVii S470, + * TBS 6920 PCIe DVB-S2 cards. + * + * Microcontroller connected to cx23885 GPIO pins: + * GPIO0 - data - P0.3 F300 + * GPIO1 - reset - P0.2 F300 + * GPIO2 - clk - P0.1 F300 + * GPIO3 - busy - P0.0 F300 + * + * Copyright (C) 2009 Igor M. Liplianin <liplianin@me.by> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx23885.h" + +#define F300_DATA GPIO_0 +#define F300_RESET GPIO_1 +#define F300_CLK GPIO_2 +#define F300_BUSY GPIO_3 + +static void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl) +{ + cx23885_gpio_enable(dev, line, 1); + if (lvl == 1) + cx23885_gpio_set(dev, line); + else + cx23885_gpio_clear(dev, line); +} + +static u8 f300_get_line(struct cx23885_dev *dev, u32 line) +{ + cx23885_gpio_enable(dev, line, 0); + + return cx23885_gpio_get(dev, line); +} + +static void f300_send_byte(struct cx23885_dev *dev, u8 dta) +{ + u8 i; + + for (i = 0; i < 8; i++) { + f300_set_line(dev, F300_CLK, 0); + udelay(30); + f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */ + udelay(30); + dta <<= 1; + f300_set_line(dev, F300_CLK, 1); + udelay(30); + } +} + +static u8 f300_get_byte(struct cx23885_dev *dev) +{ + u8 i, dta = 0; + + for (i = 0; i < 8; i++) { + f300_set_line(dev, F300_CLK, 0); + udelay(30); + dta <<= 1; + f300_set_line(dev, F300_CLK, 1); + udelay(30); + dta |= f300_get_line(dev, F300_DATA);/* msb first */ + + } + + return dta; +} + +static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf) +{ + struct cx23885_tsport *port = fe->dvb->priv; + struct cx23885_dev *dev = port->dev; + u8 i, temp, ret = 0; + + temp = buf[0]; + for (i = 0; i < buf[0]; i++) + temp += buf[i + 1]; + temp = (~temp + 1);/* get check sum */ + buf[1 + buf[0]] = temp; + + f300_set_line(dev, F300_RESET, 1); + f300_set_line(dev, F300_CLK, 1); + udelay(30); + f300_set_line(dev, F300_DATA, 1); + msleep(1); + + /* question: */ + f300_set_line(dev, F300_RESET, 0);/* begin to send data */ + msleep(1); + + f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */ + msleep(1); + + temp = buf[0]; + temp += 2; + for (i = 0; i < temp; i++) + f300_send_byte(dev, buf[i]); + + f300_set_line(dev, F300_RESET, 1);/* sent data over */ + f300_set_line(dev, F300_DATA, 1); + + /* answer: */ + temp = 0; + for (i = 0; ((i < 8) & (temp == 0)); i++) { + msleep(1); + if (f300_get_line(dev, F300_BUSY) == 0) + temp = 1; + } + + if (i > 7) { + printk(KERN_ERR "%s: timeout, the slave no response\n", + __func__); + ret = 1; /* timeout, the slave no response */ + } else { /* the slave not busy, prepare for getting data */ + f300_set_line(dev, F300_RESET, 0);/*ready...*/ + msleep(1); + f300_send_byte(dev, 0xe1);/* 0xe1 is Read */ + msleep(1); + temp = f300_get_byte(dev);/*get the data length */ + if (temp > 14) + temp = 14; + + for (i = 0; i < (temp + 1); i++) + f300_get_byte(dev);/* get data to empty buffer */ + + f300_set_line(dev, F300_RESET, 1);/* received data over */ + f300_set_line(dev, F300_DATA, 1); + } + + return ret; +} + +int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + u8 buf[16]; + + buf[0] = 0x05; + buf[1] = 0x38;/* write port */ + buf[2] = 0x01;/* A port, lnb power */ + + switch (voltage) { + case SEC_VOLTAGE_13: + buf[3] = 0x01;/* power on */ + buf[4] = 0x02;/* B port, H/V */ + buf[5] = 0x00;/*13V v*/ + break; + case SEC_VOLTAGE_18: + buf[3] = 0x01; + buf[4] = 0x02; + buf[5] = 0x01;/* 18V h*/ + break; + case SEC_VOLTAGE_OFF: + buf[3] = 0x00;/* power off */ + buf[4] = 0x00; + buf[5] = 0x00; + break; + } + + return f300_xfer(fe, buf); +} diff --git a/drivers/media/video/cx23885/cx23885-f300.h b/drivers/media/video/cx23885/cx23885-f300.h new file mode 100644 index 000000000000..e73344c94963 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-f300.h @@ -0,0 +1,2 @@ +extern int f300_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c new file mode 100644 index 000000000000..469e083dd5f8 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -0,0 +1,427 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared remote control input device + * + * Most of this file is + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * However, the cx23885_input_{init,fini} functions contained herein are + * derived from Linux kernel files linux/media/video/.../...-input.c marked as: + * + * Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + * Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> + * Markus Rechberger <mrechberger@gmail.com> + * Mauro Carvalho Chehab <mchehab@infradead.org> + * Sascha Sommer <saschasommer@freenet.de> + * Copyright (C) 2004, 2005 Chris Pascoe + * Copyright (C) 2003, 2004 Gerd Knorr + * Copyright (C) 2003 Pavel Machek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/input.h> +#include <media/ir-common.h> +#include <media/v4l2-subdev.h> + +#include "cx23885.h" + +#define RC5_BITS 14 +#define RC5_HALF_BITS (2*RC5_BITS) +#define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1) + +#define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */ +#define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */ + +#define RC5_EXTENDED_COMMAND_OFFSET 64 + +static inline unsigned int rc5_command(u32 rc5_baseband) +{ + return RC5_INSTR(rc5_baseband) + + ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED) + ? RC5_EXTENDED_COMMAND_OFFSET : 0); +} + +static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + unsigned int code, command; + u32 rc5; + + /* Ignore codes that are too short to be valid RC-5 */ + if (ir_input->last_bit < (RC5_HALF_BITS - 1)) + return; + + /* The library has the manchester coding backwards; XOR to adapt. */ + code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK; + rc5 = ir_rc5_decode(code); + + switch (RC5_START(rc5)) { + case RC5_START_BITS_NORMAL: + break; + case RC5_START_BITS_EXTENDED: + /* Don't allow if the remote only emits standard commands */ + if (ir_input->start == RC5_START_BITS_NORMAL) + return; + break; + default: + return; + } + + if (ir_input->addr != RC5_ADDR(rc5)) + return; + + /* Don't generate a keypress for RC-5 auto-repeated keypresses */ + command = rc5_command(rc5); + if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) || + command != rc5_command(ir_input->last_rc5) || + /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */ + RC5_START(ir_input->last_rc5) == 0) { + /* This keypress is differnet: not an auto repeat */ + ir_input_nokey(ir_input->dev, &ir_input->ir); + ir_input_keydown(ir_input->dev, &ir_input->ir, command); + } + ir_input->last_rc5 = rc5; + + /* Schedule when we should do the key up event: ir_input_nokey() */ + mod_timer(&ir_input->timer_keyup, + jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout)); +} + +static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev, + u32 ns_pulse) +{ + const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */ + struct card_ir *ir_input = dev->ir_input; + int i, level, quarterbits, halfbits; + + if (!ir_input->active) { + ir_input->active = 1; + /* assume an initial space that we may not detect or measure */ + ir_input->code = 0; + ir_input->last_bit = 0; + } + + if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) { + ir_input->last_bit++; /* Account for the final space */ + ir_input->active = 0; + cx23885_input_process_raw_rc5(dev); + return; + } + + level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0; + + /* Skip any leading space to sync to the start bit */ + if (ir_input->last_bit == 0 && level == 0) + return; + + /* + * With valid RC-5 we can get up to two consecutive half-bits in a + * single pulse measurment. Experiments have shown that the duration + * of a half-bit can vary. Make sure we always end up with an even + * number of quarter bits at the same level (mark or space). + */ + ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + quarterbits = ns_pulse / rc5_quarterbit_ns; + if (quarterbits & 1) + quarterbits++; + halfbits = quarterbits / 2; + + for (i = 0; i < halfbits; i++) { + ir_input->last_bit++; + ir_input->code |= (level << ir_input->last_bit); + + if (ir_input->last_bit >= RC5_HALF_BITS-1) { + ir_input->active = 0; + cx23885_input_process_raw_rc5(dev); + /* + * If level is 1, a leading mark is invalid for RC5. + * If level is 0, we scan past extra intial space. + * Either way we don't want to reactivate collecting + * marks or spaces here with any left over half-bits. + */ + break; + } + } +} + +static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev, + bool add_eom) +{ + struct card_ir *ir_input = dev->ir_input; + struct ir_input_state *ir_input_state = &ir_input->ir; + + u32 ns_pulse[RC5_HALF_BITS+1]; + ssize_t num = 0; + int count, i; + + do { + v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse, + sizeof(ns_pulse), &num); + + count = num / sizeof(u32); + + /* Append an end of Rx seq, if the caller requested */ + if (add_eom && count < ARRAY_SIZE(ns_pulse)) { + ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; + count++; + } + + /* Just drain the Rx FIFO, if we're called, but not RC-5 */ + if (ir_input_state->ir_type != IR_TYPE_RC5) + continue; + + for (i = 0; i < count; i++) + cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]); + } while (num != 0); +} + +void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) +{ + struct v4l2_subdev_ir_parameters params; + int overrun, data_available; + + if (dev->sd_ir == NULL || events == 0) + return; + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* + * The only board we handle right now. However other boards + * using the CX2388x integrated IR controller should be similar + */ + break; + default: + return; + } + + overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN | + V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN); + + data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED | + V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ); + + if (overrun) { + /* If there was a FIFO overrun, stop the device */ + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + params.enable = false; + /* Mitigate race with cx23885_input_ir_stop() */ + params.shutdown = atomic_read(&dev->ir_input_stopping); + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + } + + if (data_available) + cx23885_input_process_pulse_widths_rc5(dev, overrun); + + if (overrun) { + /* If there was a FIFO overrun, clear & restart the device */ + params.enable = true; + /* Mitigate race with cx23885_input_ir_stop() */ + params.shutdown = atomic_read(&dev->ir_input_stopping); + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + } +} + +static void cx23885_input_ir_start(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + struct ir_input_state *ir_input_state = &ir_input->ir; + struct v4l2_subdev_ir_parameters params; + + if (dev->sd_ir == NULL) + return; + + atomic_set(&dev->ir_input_stopping, 0); + + /* keyup timer set up, if needed */ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + setup_timer(&ir_input->timer_keyup, + ir_rc5_timer_keyup, /* Not actually RC-5 specific */ + (unsigned long) ir_input); + if (ir_input_state->ir_type == IR_TYPE_RC5) { + /* + * RC-5 repeats a held key every + * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms + */ + ir_input->rc5_key_timeout = 115; + } + break; + } + + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* + * The IR controller on this board only returns pulse widths. + * Any other mode setting will fail to set up the device. + */ + params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + params.enable = true; + params.interrupt_enable = true; + params.shutdown = false; + + /* Setup for baseband compatible with both RC-5 and RC-6A */ + params.modulation = false; + /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/ + /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/ + params.max_pulse_width = 3333333; /* ns */ + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + params.noise_filter_min_width = 333333; /* ns */ + /* + * This board has inverted receive sense: + * mark is received as low logic level; + * falling edges are detected as rising edges; etc. + */ + params.invert = true; + break; + } + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); +} + +static void cx23885_input_ir_stop(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + struct v4l2_subdev_ir_parameters params; + + if (dev->sd_ir == NULL) + return; + + /* + * Stop the sd_ir subdevice from generating notifications and + * scheduling work. + * It is shutdown this way in order to mitigate a race with + * cx23885_input_rx_work_handler() in the overrun case, which could + * re-enable the subdevice. + */ + atomic_set(&dev->ir_input_stopping, 1); + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + while (params.shutdown == false) { + params.enable = false; + params.interrupt_enable = false; + params.shutdown = true; + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + } + + flush_scheduled_work(); + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + del_timer_sync(&ir_input->timer_keyup); + break; + } +} + +int cx23885_input_init(struct cx23885_dev *dev) +{ + struct card_ir *ir; + struct input_dev *input_dev; + struct ir_scancode_table *ir_codes = NULL; + int ir_type, ir_addr, ir_start; + int ret; + + /* + * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't + * encapsulated in a v4l2_subdev, then I'm not going to deal with it. + */ + if (dev->sd_ir == NULL) + return -ENODEV; + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* Parameters for the grey Hauppauge remote for the HVR-1850 */ + ir_codes = &ir_codes_hauppauge_new_table; + ir_type = IR_TYPE_RC5; + ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */ + ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */ + break; + } + if (ir_codes == NULL) + return -ENODEV; + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ir || !input_dev) { + ret = -ENOMEM; + goto err_out_free; + } + + ir->dev = input_dev; + ir->addr = ir_addr; + ir->start = ir_start; + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)", + cx23885_boards[dev->board].name); + snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); + + ret = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (ret < 0) + goto err_out_free; + + input_dev->name = ir->name; + input_dev->phys = ir->phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.version = 1; + if (dev->pci->subsystem_vendor) { + input_dev->id.vendor = dev->pci->subsystem_vendor; + input_dev->id.product = dev->pci->subsystem_device; + } else { + input_dev->id.vendor = dev->pci->vendor; + input_dev->id.product = dev->pci->device; + } + input_dev->dev.parent = &dev->pci->dev; + + dev->ir_input = ir; + cx23885_input_ir_start(dev); + + ret = input_register_device(ir->dev); + if (ret) + goto err_out_stop; + + return 0; + +err_out_stop: + cx23885_input_ir_stop(dev); + dev->ir_input = NULL; +err_out_free: + ir_input_free(input_dev); + input_free_device(input_dev); + kfree(ir); + return ret; +} + +void cx23885_input_fini(struct cx23885_dev *dev) +{ + /* Always stop the IR hardware from generating interrupts */ + cx23885_input_ir_stop(dev); + + if (dev->ir_input == NULL) + return; + ir_input_free(dev->ir_input->dev); + input_unregister_device(dev->ir_input->dev); + kfree(dev->ir_input); + dev->ir_input = NULL; +} diff --git a/drivers/media/video/cx23885/cx23885-input.h b/drivers/media/video/cx23885/cx23885-input.h new file mode 100644 index 000000000000..3572cb1ecfc2 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-input.h @@ -0,0 +1,30 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared remote control input device + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23885_INPUT_H_ +#define _CX23885_INPUT_H_ +int cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events); + +int cx23885_input_init(struct cx23885_dev *dev); +void cx23885_input_fini(struct cx23885_dev *dev); +#endif diff --git a/drivers/media/video/cx23885/cx23885-ioctl.c b/drivers/media/video/cx23885/cx23885-ioctl.c new file mode 100644 index 000000000000..dfb4627fb340 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ioctl.c @@ -0,0 +1,208 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Various common ioctl() support functions + * + * Copyright (c) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx23885.h" +#include <media/v4l2-chip-ident.h> + +int cx23885_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + int err = 0; + u8 rev; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + switch (chip->match.addr) { + case 0: + rev = cx_read(RDR_CFG2) & 0xff; + switch (dev->pci->device) { + case 0x8852: + /* rev 0x04 could be '885 or '888. Pick '888. */ + if (rev == 0x04) + chip->ident = V4L2_IDENT_CX23888; + else + chip->ident = V4L2_IDENT_CX23885; + break; + case 0x8880: + if (rev == 0x0e || rev == 0x0f) + chip->ident = V4L2_IDENT_CX23887; + else + chip->ident = V4L2_IDENT_CX23888; + break; + default: + chip->ident = V4L2_IDENT_UNKNOWN; + break; + } + chip->revision = (dev->pci->device << 16) | (rev << 8) | + (dev->hwrevision & 0xff); + break; + case 1: + if (dev->v4l_device != NULL) { + chip->ident = V4L2_IDENT_CX23417; + chip->revision = 0; + } + break; + case 2: + /* + * The integrated IR controller on the CX23888 is + * host chip 2. It may not be used/initialized or sd_ir + * may be pointing at the cx25840 subdevice for the + * IR controller on the CX23885. Thus we find it + * without using the dev->sd_ir pointer. + */ + call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident, + chip); + break; + default: + err = -EINVAL; /* per V4L2 spec */ + break; + } + break; + case V4L2_CHIP_MATCH_I2C_DRIVER: + /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */ + call_all(dev, core, g_chip_ident, chip); + break; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* + * We could return V4L2_IDENT_UNKNOWN, but we don't do the work + * to look if a chip is at the address with no driver. That's a + * dangerous thing to do with EEPROMs anyway. + */ + call_all(dev, core, g_chip_ident, chip); + break; + default: + err = -EINVAL; + break; + } + return err; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx23885_g_host_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) + return -EINVAL; + + reg->size = 4; + reg->val = cx_read(reg->reg); + return 0; +} + +static int cx23417_g_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + u32 value; + + if (dev->v4l_device == NULL) + return -EINVAL; + + if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000) + return -EINVAL; + + if (mc417_register_read(dev, (u16) reg->reg, &value)) + return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ + + reg->size = 4; + reg->val = value; + return 0; +} + +int cx23885_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (reg->match.type == V4L2_CHIP_MATCH_HOST) { + switch (reg->match.addr) { + case 0: + return cx23885_g_host_register(dev, reg); + case 1: + return cx23417_g_register(dev, reg); + default: + break; + } + } + + /* FIXME - any error returns should not be ignored */ + call_all(dev, core, g_register, reg); + return 0; +} + +static int cx23885_s_host_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) + return -EINVAL; + + reg->size = 4; + cx_write(reg->reg, reg->val); + return 0; +} + +static int cx23417_s_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if (dev->v4l_device == NULL) + return -EINVAL; + + if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000) + return -EINVAL; + + if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val)) + return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ + + reg->size = 4; + return 0; +} + +int cx23885_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (reg->match.type == V4L2_CHIP_MATCH_HOST) { + switch (reg->match.addr) { + case 0: + return cx23885_s_host_register(dev, reg); + case 1: + return cx23417_s_register(dev, reg); + default: + break; + } + } + + /* FIXME - any error returns should not be ignored */ + call_all(dev, core, s_register, reg); + return 0; +} +#endif diff --git a/drivers/media/video/cx23885/cx23885-ioctl.h b/drivers/media/video/cx23885/cx23885-ioctl.h new file mode 100644 index 000000000000..80b0f4923c6a --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ioctl.h @@ -0,0 +1,39 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Various common ioctl() support functions + * + * Copyright (c) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _CX23885_IOCTL_H_ +#define _CX23885_IOCTL_H_ + +int cx23885_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip); + +#ifdef CONFIG_VIDEO_ADV_DEBUG +int cx23885_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); + + +int cx23885_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); + +#endif +#endif diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c new file mode 100644 index 000000000000..6ae982cc9856 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ir.c @@ -0,0 +1,101 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared device support routines - non-input, non-vl42_subdev routines + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <media/v4l2-device.h> + +#include "cx23885.h" +#include "cx23885-input.h" + +#define CX23885_IR_RX_FIFO_SERVICE_REQ 0 +#define CX23885_IR_RX_END_OF_RX_DETECTED 1 +#define CX23885_IR_RX_HW_FIFO_OVERRUN 2 +#define CX23885_IR_RX_SW_FIFO_OVERRUN 3 + +#define CX23885_IR_TX_FIFO_SERVICE_REQ 0 + + +void cx23885_ir_rx_work_handler(struct work_struct *work) +{ + struct cx23885_dev *dev = + container_of(work, struct cx23885_dev, ir_rx_work); + u32 events = 0; + unsigned long *notifications = &dev->ir_rx_notifications; + + if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications)) + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications)) + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications)) + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications)) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + + if (events == 0) + return; + + if (dev->ir_input) + cx23885_input_rx_work_handler(dev, events); +} + +void cx23885_ir_tx_work_handler(struct work_struct *work) +{ + struct cx23885_dev *dev = + container_of(work, struct cx23885_dev, ir_tx_work); + u32 events = 0; + unsigned long *notifications = &dev->ir_tx_notifications; + + if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications)) + events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + + if (events == 0) + return; + +} + +/* Called in an IRQ context */ +void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) +{ + struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); + unsigned long *notifications = &dev->ir_rx_notifications; + + if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ) + set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications); + if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED) + set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications); + if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN) + set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications); + if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN) + set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications); + schedule_work(&dev->ir_rx_work); +} + +/* Called in an IRQ context */ +void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) +{ + struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); + unsigned long *notifications = &dev->ir_tx_notifications; + + if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ) + set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications); + schedule_work(&dev->ir_tx_work); +} diff --git a/drivers/media/video/cx23885/cx23885-ir.h b/drivers/media/video/cx23885/cx23885-ir.h new file mode 100644 index 000000000000..9b8a6d5d1ef6 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ir.h @@ -0,0 +1,31 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared device support routines - non-input, non-vl42_subdev routines + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23885_IR_H_ +#define _CX23885_IR_H_ +void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events); +void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events); + +void cx23885_ir_rx_work_handler(struct work_struct *work); +void cx23885_ir_tx_work_handler(struct work_struct *work); +#endif diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h index eafbe5226bae..c0bc9a068954 100644 --- a/drivers/media/video/cx23885/cx23885-reg.h +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -212,8 +212,9 @@ Channel manager Data Structure entry = 20 DWORD #define DEV_CNTRL2 0x00040000 -#define PCI_MSK_GPIO1 (1 << 24) -#define PCI_MSK_GPIO0 (1 << 23) +#define PCI_MSK_IR (1 << 28) +#define PCI_MSK_GPIO1 (1 << 24) +#define PCI_MSK_GPIO0 (1 << 23) #define PCI_MSK_APB_DMA (1 << 12) #define PCI_MSK_AL_WR (1 << 11) #define PCI_MSK_AL_RD (1 << 10) diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 654cc253cd50..8b372b4f0de2 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -35,6 +35,7 @@ #include "cx23885.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include "cx23885-ioctl.h" MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); @@ -401,6 +402,13 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) INPUT(input)->gpio2, INPUT(input)->gpio3); dev->input = input; + if (dev->board == CX23885_BOARD_MYGICA_X8506 || + dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2) { + /* Select Analog TV */ + if (INPUT(input)->type == CX23885_VMUX_TELEVISION) + cx23885_gpio_clear(dev, GPIO_0); + } + /* Tell the internal A/V decoder */ v4l2_subdev_call(dev->sd_cx25840, video, s_routing, INPUT(input)->vmux, 0, 0); @@ -1144,6 +1152,7 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) [CX23885_VMUX_COMPOSITE3] = "Composite3", [CX23885_VMUX_COMPOSITE4] = "Composite4", [CX23885_VMUX_SVIDEO] = "S-Video", + [CX23885_VMUX_COMPONENT] = "Component", [CX23885_VMUX_TELEVISION] = "Television", [CX23885_VMUX_CABLE] = "Cable TV", [CX23885_VMUX_DVB] = "DVB", @@ -1312,34 +1321,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, cx23885_set_freq(dev, f); } -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vidioc_g_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - - call_all(dev, core, g_register, reg); - - return 0; -} - -static int vidioc_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - - call_all(dev, core, s_register, reg); - - return 0; -} -#endif - /* ----------------------------------------------------------- */ static void cx23885_vid_timeout(unsigned long data) @@ -1449,9 +1430,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_chip_ident = cx23885_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx23885_g_register, + .vidioc_s_register = cx23885_s_register, #endif }; @@ -1529,9 +1511,11 @@ int cx23885_video_register(struct cx23885_dev *dev) if (sd) { struct tuner_setup tun_setup; + memset(&tun_setup, 0, sizeof(tun_setup)); tun_setup.mode_mask = T_ANALOG_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = v4l2_i2c_subdev_addr(sd); + tun_setup.tuner_callback = cx23885_tuner_callback; v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup); } diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index cc7a165561ff..fa744764dc8b 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -79,6 +79,8 @@ #define CX23885_BOARD_MAGICPRO_PROHDTVE2 23 #define CX23885_BOARD_HAUPPAUGE_HVR1850 24 #define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25 +#define CX23885_BOARD_HAUPPAUGE_HVR1290 26 +#define CX23885_BOARD_MYGICA_X8558PRO 27 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -157,6 +159,7 @@ enum cx23885_itype { CX23885_VMUX_COMPOSITE3, CX23885_VMUX_COMPOSITE4, CX23885_VMUX_SVIDEO, + CX23885_VMUX_COMPONENT, CX23885_VMUX_TELEVISION, CX23885_VMUX_CABLE, CX23885_VMUX_DVB, @@ -297,10 +300,6 @@ struct cx23885_tsport { /* Allow a single tsport to have multiple frontends */ u32 num_frontends; void *port_priv; - - /* FIXME: temporary hack */ - int (*set_frontend_save) (struct dvb_frontend *, - struct dvb_frontend_parameters *); }; struct cx23885_dev { @@ -356,6 +355,16 @@ struct cx23885_dev { unsigned int has_radio; struct v4l2_subdev *sd_cx25840; + /* Infrared */ + struct v4l2_subdev *sd_ir; + struct work_struct ir_rx_work; + unsigned long ir_rx_notifications; + struct work_struct ir_tx_work; + unsigned long ir_tx_notifications; + + struct card_ir *ir_input; + atomic_t ir_input_stopping; + /* V4l */ u32 freq; struct video_device *video_dev; @@ -383,6 +392,13 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) #define call_all(dev, o, f, args...) \ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) +#define CX23885_HW_888_IR (1 << 0) + +#define call_hw(dev, grpid, o, f, args...) \ + v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args) + +extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw); + extern struct list_head cx23885_devlist; #define SRAM_CH01 0 /* Video A */ @@ -455,6 +471,7 @@ extern void cx23885_wakeup(struct cx23885_tsport *port, extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask); +extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); @@ -471,6 +488,8 @@ extern int cx23885_tuner_callback(void *priv, int component, int command, int arg); extern void cx23885_card_list(struct cx23885_dev *dev); extern int cx23885_ir_init(struct cx23885_dev *dev); +extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev); +extern void cx23885_ir_fini(struct cx23885_dev *dev); extern void cx23885_gpio_setup(struct cx23885_dev *dev); extern void cx23885_card_setup(struct cx23885_dev *dev); extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev); @@ -515,6 +534,10 @@ extern void cx23885_417_check_encoder(struct cx23885_dev *dev); extern void cx23885_mc417_init(struct cx23885_dev *dev); extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value); extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value); +extern int mc417_register_read(struct cx23885_dev *dev, + u16 address, u32 *value); +extern int mc417_register_write(struct cx23885_dev *dev, + u16 address, u32 value); extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c new file mode 100644 index 000000000000..3ccc8afeccf3 --- /dev/null +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -0,0 +1,1239 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * CX23888 Integrated Consumer Infrared Controller + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/kfifo.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> + +#include "cx23885.h" + +static unsigned int ir_888_debug; +module_param(ir_888_debug, int, 0644); +MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]"); + +#define CX23888_IR_REG_BASE 0x170000 +/* + * These CX23888 register offsets have a straightforward one to one mapping + * to the CX23885 register offsets of 0x200 through 0x218 + */ +#define CX23888_IR_CNTRL_REG 0x170000 +#define CNTRL_WIN_3_3 0x00000000 +#define CNTRL_WIN_4_3 0x00000001 +#define CNTRL_WIN_3_4 0x00000002 +#define CNTRL_WIN_4_4 0x00000003 +#define CNTRL_WIN 0x00000003 +#define CNTRL_EDG_NONE 0x00000000 +#define CNTRL_EDG_FALL 0x00000004 +#define CNTRL_EDG_RISE 0x00000008 +#define CNTRL_EDG_BOTH 0x0000000C +#define CNTRL_EDG 0x0000000C +#define CNTRL_DMD 0x00000010 +#define CNTRL_MOD 0x00000020 +#define CNTRL_RFE 0x00000040 +#define CNTRL_TFE 0x00000080 +#define CNTRL_RXE 0x00000100 +#define CNTRL_TXE 0x00000200 +#define CNTRL_RIC 0x00000400 +#define CNTRL_TIC 0x00000800 +#define CNTRL_CPL 0x00001000 +#define CNTRL_LBM 0x00002000 +#define CNTRL_R 0x00004000 + +#define CX23888_IR_TXCLK_REG 0x170004 +#define TXCLK_TCD 0x0000FFFF + +#define CX23888_IR_RXCLK_REG 0x170008 +#define RXCLK_RCD 0x0000FFFF + +#define CX23888_IR_CDUTY_REG 0x17000C +#define CDUTY_CDC 0x0000000F + +#define CX23888_IR_STATS_REG 0x170010 +#define STATS_RTO 0x00000001 +#define STATS_ROR 0x00000002 +#define STATS_RBY 0x00000004 +#define STATS_TBY 0x00000008 +#define STATS_RSR 0x00000010 +#define STATS_TSR 0x00000020 + +#define CX23888_IR_IRQEN_REG 0x170014 +#define IRQEN_RTE 0x00000001 +#define IRQEN_ROE 0x00000002 +#define IRQEN_RSE 0x00000010 +#define IRQEN_TSE 0x00000020 + +#define CX23888_IR_FILTR_REG 0x170018 +#define FILTR_LPF 0x0000FFFF + +/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */ +#define CX23888_IR_FIFO_REG 0x170040 +#define FIFO_RXTX 0x0000FFFF +#define FIFO_RXTX_LVL 0x00010000 +#define FIFO_RXTX_RTO 0x0001FFFF +#define FIFO_RX_NDV 0x00020000 +#define FIFO_RX_DEPTH 8 +#define FIFO_TX_DEPTH 8 + +/* CX23888 unique registers */ +#define CX23888_IR_SEEDP_REG 0x17001C +#define CX23888_IR_TIMOL_REG 0x170020 +#define CX23888_IR_WAKE0_REG 0x170024 +#define CX23888_IR_WAKE1_REG 0x170028 +#define CX23888_IR_WAKE2_REG 0x17002C +#define CX23888_IR_MASK0_REG 0x170030 +#define CX23888_IR_MASK1_REG 0x170034 +#define CX23888_IR_MAKS2_REG 0x170038 +#define CX23888_IR_DPIPG_REG 0x17003C +#define CX23888_IR_LEARN_REG 0x170044 + +#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ +#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2) + +#define CX23888_IR_RX_KFIFO_SIZE (512 * sizeof(u32)) +#define CX23888_IR_TX_KFIFO_SIZE (512 * sizeof(u32)) + +struct cx23888_ir_state { + struct v4l2_subdev sd; + struct cx23885_dev *dev; + u32 id; + u32 rev; + + struct v4l2_subdev_ir_parameters rx_params; + struct mutex rx_params_lock; + atomic_t rxclk_divider; + atomic_t rx_invert; + + struct kfifo *rx_kfifo; + spinlock_t rx_kfifo_lock; + + struct v4l2_subdev_ir_parameters tx_params; + struct mutex tx_params_lock; + atomic_t txclk_divider; + + struct kfifo *tx_kfifo; + spinlock_t tx_kfifo_lock; +}; + +static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd) +{ + return v4l2_get_subdevdata(sd); +} + +/* + * IR register block read and write functions + */ +static +inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value) +{ + cx_write(addr, value); + return 0; +} + +static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr) +{ + return cx_read(addr); +} + +static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr, + u32 and_mask, u32 or_value) +{ + cx_andor(addr, ~and_mask, or_value); + return 0; +} + +/* + * Rx and Tx Clock Divider register computations + * + * Note the largest clock divider value of 0xffff corresponds to: + * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_clock_divider(unsigned int d) +{ + if (d > RXCLK_RCD + 1) + d = RXCLK_RCD; + else if (d < 2) + d = 1; + else + d--; + return (u16) d; +} + +static inline u16 ns_to_clock_divider(unsigned int ns) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int clock_divider_to_ns(unsigned int divider) +{ + /* Period of the Rx or Tx clock in ns */ + return DIV_ROUND_CLOSEST((divider + 1) * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static inline u16 carrier_freq_to_clock_divider(unsigned int freq) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * 16)); +} + +static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) +{ + return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16); +} + +static inline u16 freq_to_clock_divider(unsigned int freq, + unsigned int rollovers) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers)); +} + +static inline unsigned int clock_divider_to_freq(unsigned int divider, + unsigned int rollovers) +{ + return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, + (divider + 1) * rollovers); +} + +/* + * Low Pass Filter register calculations + * + * Note the largest count value of 0xffff corresponds to: + * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_lpf_count(unsigned int d) +{ + if (d > FILTR_LPF) + d = FILTR_LPF; + else if (d < 4) + d = 0; + return (u16) d; +} + +static inline u16 ns_to_lpf_count(unsigned int ns) +{ + return count_to_lpf_count( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int lpf_count_to_ns(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in ns */ + return DIV_ROUND_CLOSEST(count * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static inline unsigned int lpf_count_to_us(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in us */ + return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000); +} + +/* + * FIFO register pulse width count compuations + */ +static u32 clock_divider_to_resolution(u16 divider) +{ + /* + * Resolution is the duration of 1 tick of the readable portion of + * of the pulse width counter as read from the FIFO. The two lsb's are + * not readable, hence the << 2. This function returns ns. + */ + return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static u64 pulse_width_count_to_ns(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ + rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ + if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return n; +} + +static unsigned int pulse_width_count_to_us(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ + rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ + if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return (unsigned int) n; +} + +/* + * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts + * + * The total pulse clock count is an 18 bit pulse width timer count as the most + * significant part and (up to) 16 bit clock divider count as a modulus. + * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse + * width timer count's least significant bit. + */ +static u64 ns_to_pulse_clocks(u32 ns) +{ + u64 clocks; + u32 rem; + clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ + rem = do_div(clocks, 1000); /* /1000 = cycles */ + if (rem >= 1000 / 2) + clocks++; + return clocks; +} + +static u16 pulse_clocks_to_clock_divider(u64 count) +{ + u32 rem; + + rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + + /* net result needs to be rounded down and decremented by 1 */ + if (count > RXCLK_RCD + 1) + count = RXCLK_RCD; + else if (count < 2) + count = 1; + else + count--; + return (u16) count; +} + +/* + * IR Control Register helpers + */ +enum tx_fifo_watermark { + TX_FIFO_HALF_EMPTY = 0, + TX_FIFO_EMPTY = CNTRL_TIC, +}; + +enum rx_fifo_watermark { + RX_FIFO_HALF_FULL = 0, + RX_FIFO_NOT_EMPTY = CNTRL_RIC, +}; + +static inline void control_tx_irq_watermark(struct cx23885_dev *dev, + enum tx_fifo_watermark level) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_TIC, level); +} + +static inline void control_rx_irq_watermark(struct cx23885_dev *dev, + enum rx_fifo_watermark level) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_RIC, level); +} + +static inline void control_tx_enable(struct cx23885_dev *dev, bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), + enable ? (CNTRL_TXE | CNTRL_TFE) : 0); +} + +static inline void control_rx_enable(struct cx23885_dev *dev, bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), + enable ? (CNTRL_RXE | CNTRL_RFE) : 0); +} + +static inline void control_tx_modulation_enable(struct cx23885_dev *dev, + bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_MOD, + enable ? CNTRL_MOD : 0); +} + +static inline void control_rx_demodulation_enable(struct cx23885_dev *dev, + bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_DMD, + enable ? CNTRL_DMD : 0); +} + +static inline void control_rx_s_edge_detection(struct cx23885_dev *dev, + u32 edge_types) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, + edge_types & CNTRL_EDG_BOTH); +} + +static void control_rx_s_carrier_window(struct cx23885_dev *dev, + unsigned int carrier, + unsigned int *carrier_range_low, + unsigned int *carrier_range_high) +{ + u32 v; + unsigned int c16 = carrier * 16; + + if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { + v = CNTRL_WIN_3_4; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); + } else { + v = CNTRL_WIN_3_3; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); + } + + if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { + v |= CNTRL_WIN_4_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); + } else { + v |= CNTRL_WIN_3_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); + } + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_WIN, v); +} + +static inline void control_tx_polarity_invert(struct cx23885_dev *dev, + bool invert) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_CPL, + invert ? CNTRL_CPL : 0); +} + +/* + * IR Rx & Tx Clock Register helpers + */ +static unsigned int txclk_tx_s_carrier(struct cx23885_dev *dev, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +/* + * IR Tx Carrier Duty Cycle register helpers + */ +static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev, + unsigned int duty_cycle) +{ + u32 n; + n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ + if (n != 0) + n--; + if (n > 15) + n = 15; + cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n); + return DIV_ROUND_CLOSEST((n + 1) * 100, 16); +} + +/* + * IR Filter Register helpers + */ +static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns) +{ + u32 count = ns_to_lpf_count(min_width_ns); + cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count); + return lpf_count_to_ns(count); +} + +/* + * IR IRQ Enable Register helpers + */ +static inline void irqenable_rx(struct cx23885_dev *dev, u32 mask) +{ + mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); + cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, + ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); +} + +static inline void irqenable_tx(struct cx23885_dev *dev, u32 mask) +{ + mask &= IRQEN_TSE; + cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ~IRQEN_TSE, mask); +} + +/* + * V4L2 Subdevice IR Ops + */ +static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); + u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); + u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); + + u32 rx_data[FIFO_RX_DEPTH]; + int i, j, k; + u32 events, v; + int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; + + tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ + rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ + rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ + ror = stats & STATS_ROR; /* Rx FIFO Over Run */ + + tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ + rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ + rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ + roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ + + *handled = false; + v4l2_dbg(2, ir_888_debug, sd, "IRQ Status: %s %s %s %s %s %s\n", + tsr ? "tsr" : " ", rsr ? "rsr" : " ", + rto ? "rto" : " ", ror ? "ror" : " ", + stats & STATS_TBY ? "tby" : " ", + stats & STATS_RBY ? "rby" : " "); + + v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n", + tse ? "tse" : " ", rse ? "rse" : " ", + rte ? "rte" : " ", roe ? "roe" : " "); + + /* + * Transmitter interrupt service + */ + if (tse && tsr) { + /* + * TODO: + * Check the watermark threshold setting + * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo + * Push the data to the hardware FIFO. + * If there was nothing more to send in the tx_kfifo, disable + * the TSR IRQ and notify the v4l2_device. + * If there was something in the tx_kfifo, check the tx_kfifo + * level and notify the v4l2_device, if it is low. + */ + /* For now, inhibit TSR interrupt until Tx is implemented */ + irqenable_tx(dev, 0); + events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); + *handled = true; + } + + /* + * Receiver interrupt service + */ + kror = 0; + if ((rse && rsr) || (rte && rto)) { + /* + * Receive data on RSR to clear the STATS_RSR. + * Receive data on RTO, since we may not have yet hit the RSR + * watermark when we receive the RTO. + */ + for (i = 0, v = FIFO_RX_NDV; + (v & FIFO_RX_NDV) && !kror; i = 0) { + for (j = 0; + (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { + v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG); + rx_data[i++] = v & ~FIFO_RX_NDV; + } + if (i == 0) + break; + j = i * sizeof(u32); + k = kfifo_put(state->rx_kfifo, + (unsigned char *) rx_data, j); + if (k != j) + kror++; /* rx_kfifo over run */ + } + *handled = true; + } + + events = 0; + v = 0; + if (kror) { + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver software FIFO overrun\n"); + } + if (roe && ror) { + /* + * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear + * the Rx FIFO Over Run status (STATS_ROR) + */ + v |= CNTRL_RFE; + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); + } + if (rte && rto) { + /* + * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear + * the Rx Pulse Width Timer Time Out (STATS_RTO) + */ + v |= CNTRL_RXE; + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + } + if (v) { + /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ + cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v); + cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl); + *handled = true; + } + if (kfifo_len(state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + + if (events) + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); + return 0; +} + +/* Receiver */ +static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx23888_ir_state *state = to_state(sd); + bool invert = (bool) atomic_read(&state->rx_invert); + u16 divider = (u16) atomic_read(&state->rxclk_divider); + + unsigned int i, n; + u32 *p; + u32 u, v; + + n = count / sizeof(u32) * sizeof(u32); + if (n == 0) { + *num = 0; + return 0; + } + + n = kfifo_get(state->rx_kfifo, buf, n); + + n /= sizeof(u32); + *num = n * sizeof(u32); + + for (p = (u32 *) buf, i = 0; i < n; p++, i++) { + if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { + *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; + v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n"); + continue; + } + + u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0; + if (invert) + u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK; + + v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX), + divider); + if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1; + + *p = u | v; + + v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n", + v, u ? "mark" : "space"); + } + return 0; +} + +static int cx23888_ir_rx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + mutex_lock(&state->rx_params_lock); + memcpy(p, &state->rx_params, sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&state->rx_params_lock); + return 0; +} + +static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + mutex_lock(&state->rx_params_lock); + + /* Disable or slow down all IR Rx circuits and counters */ + irqenable_rx(dev, 0); + control_rx_enable(dev, false); + control_rx_demodulation_enable(dev, false); + control_rx_s_edge_detection(dev, CNTRL_EDG_NONE); + filter_rx_s_min_width(dev, 0); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD); + + state->rx_params.shutdown = true; + + mutex_unlock(&state->rx_params_lock); + return 0; +} + +static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + struct v4l2_subdev_ir_parameters *o = &state->rx_params; + u16 rxclk_divider; + + if (p->shutdown) + return cx23888_ir_rx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + mutex_lock(&state->rx_params_lock); + + o->shutdown = p->shutdown; + + o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + + o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32); + + /* Before we tweak the hardware, we have to disable the receiver */ + irqenable_rx(dev, 0); + control_rx_enable(dev, false); + + control_rx_demodulation_enable(dev, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = rxclk_rx_s_carrier(dev, p->carrier_freq, + &rxclk_divider); + + o->carrier_freq = p->carrier_freq; + + o->duty_cycle = p->duty_cycle = 50; + + control_rx_s_carrier_window(dev, p->carrier_freq, + &p->carrier_range_lower, + &p->carrier_range_upper); + o->carrier_range_lower = p->carrier_range_lower; + o->carrier_range_upper = p->carrier_range_upper; + } else { + p->max_pulse_width = + rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width, + &rxclk_divider); + o->max_pulse_width = p->max_pulse_width; + } + atomic_set(&state->rxclk_divider, rxclk_divider); + + p->noise_filter_min_width = + filter_rx_s_min_width(dev, p->noise_filter_min_width); + o->noise_filter_min_width = p->noise_filter_min_width; + + p->resolution = clock_divider_to_resolution(rxclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_rx_irq_watermark(dev, RX_FIFO_HALF_FULL); + + control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH); + + o->invert = p->invert; + atomic_set(&state->rx_invert, p->invert); + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + kfifo_reset(state->rx_kfifo); + if (p->interrupt_enable) + irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); + control_rx_enable(dev, p->enable); + } + + mutex_unlock(&state->rx_params_lock); + return 0; +} + +/* Transmitter */ +static int cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + /* For now enable the Tx FIFO Service interrupt & pretend we did work */ + irqenable_tx(dev, IRQEN_TSE); + *num = count; + return 0; +} + +static int cx23888_ir_tx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + mutex_lock(&state->tx_params_lock); + memcpy(p, &state->tx_params, sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&state->tx_params_lock); + return 0; +} + +static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + mutex_lock(&state->tx_params_lock); + + /* Disable or slow down all IR Tx circuits and counters */ + irqenable_tx(dev, 0); + control_tx_enable(dev, false); + control_tx_modulation_enable(dev, false); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD); + + state->tx_params.shutdown = true; + + mutex_unlock(&state->tx_params_lock); + return 0; +} + +static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + struct v4l2_subdev_ir_parameters *o = &state->tx_params; + u16 txclk_divider; + + if (p->shutdown) + return cx23888_ir_tx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + mutex_lock(&state->tx_params_lock); + + o->shutdown = p->shutdown; + + o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + + o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32); + + /* Before we tweak the hardware, we have to disable the transmitter */ + irqenable_tx(dev, 0); + control_tx_enable(dev, false); + + control_tx_modulation_enable(dev, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = txclk_tx_s_carrier(dev, p->carrier_freq, + &txclk_divider); + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle); + o->duty_cycle = p->duty_cycle; + } else { + p->max_pulse_width = + txclk_tx_s_max_pulse_width(dev, p->max_pulse_width, + &txclk_divider); + o->max_pulse_width = p->max_pulse_width; + } + atomic_set(&state->txclk_divider, txclk_divider); + + p->resolution = clock_divider_to_resolution(txclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY); + + control_tx_polarity_invert(dev, p->invert); + o->invert = p->invert; + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + kfifo_reset(state->tx_kfifo); + if (p->interrupt_enable) + irqenable_tx(dev, IRQEN_TSE); + control_tx_enable(dev, p->enable); + } + + mutex_unlock(&state->tx_params_lock); + return 0; +} + + +/* + * V4L2 Subdevice Core Ops + */ +static int cx23888_ir_log_status(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + char *s; + int i, j; + + u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); + u32 txclk = cx23888_ir_read4(dev, CX23888_IR_TXCLK_REG) & TXCLK_TCD; + u32 rxclk = cx23888_ir_read4(dev, CX23888_IR_RXCLK_REG) & RXCLK_RCD; + u32 cduty = cx23888_ir_read4(dev, CX23888_IR_CDUTY_REG) & CDUTY_CDC; + u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); + u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); + u32 filtr = cx23888_ir_read4(dev, CX23888_IR_FILTR_REG) & FILTR_LPF; + + v4l2_info(sd, "IR Receiver:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_RXE ? "yes" : "no"); + v4l2_info(sd, "\tDemodulation from a carrier: %s\n", + cntrl & CNTRL_DMD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_RFE ? "enabled" : "disabled"); + switch (cntrl & CNTRL_EDG) { + case CNTRL_EDG_NONE: + s = "disabled"; + break; + case CNTRL_EDG_FALL: + s = "falling edge"; + break; + case CNTRL_EDG_RISE: + s = "rising edge"; + break; + case CNTRL_EDG_BOTH: + s = "rising & falling edges"; + break; + default: + s = "??? edge"; + break; + } + v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); + v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", + cntrl & CNTRL_R ? "not loaded" : "overflow marker"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); + v4l2_info(sd, "\tLoopback mode: %s\n", + cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); + if (cntrl & CNTRL_DMD) { + v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(rxclk)); + switch (cntrl & CNTRL_WIN) { + case CNTRL_WIN_3_3: + i = 3; + j = 3; + break; + case CNTRL_WIN_4_3: + i = 4; + j = 3; + break; + case CNTRL_WIN_3_4: + i = 3; + j = 4; + break; + case CNTRL_WIN_4_4: + i = 4; + j = 4; + break; + default: + i = 0; + j = 0; + break; + } + v4l2_info(sd, "\tNext carrier edge window: 16 clocks " + "-%1d/+%1d, %u to %u Hz\n", i, j, + clock_divider_to_freq(rxclk, 16 + j), + clock_divider_to_freq(rxclk, 16 - i)); + } else { + v4l2_info(sd, "\tMax measurable pulse width: %u us, " + "%llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, rxclk), + pulse_width_count_to_ns(FIFO_RXTX, rxclk)); + } + v4l2_info(sd, "\tLow pass filter: %s\n", + filtr ? "enabled" : "disabled"); + if (filtr) + v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " + "%u ns\n", + lpf_count_to_us(filtr), + lpf_count_to_ns(filtr)); + v4l2_info(sd, "\tPulse width timer timed-out: %s\n", + stats & STATS_RTO ? "yes" : "no"); + v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", + irqen & IRQEN_RTE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO overrun: %s\n", + stats & STATS_ROR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", + irqen & IRQEN_ROE ? "enabled" : "disabled"); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_RBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_RSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_RSE ? "enabled" : "disabled"); + + v4l2_info(sd, "IR Transmitter:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_TXE ? "yes" : "no"); + v4l2_info(sd, "\tModulation onto a carrier: %s\n", + cntrl & CNTRL_MOD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_TFE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_TIC ? "not empty" : "half full or less"); + v4l2_info(sd, "\tSignal polarity: %s\n", + cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark"); + if (cntrl & CNTRL_MOD) { + v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(txclk)); + v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", + cduty + 1); + } else { + v4l2_info(sd, "\tMax pulse width: %u us, " + "%llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, txclk), + pulse_width_count_to_ns(FIFO_RXTX, txclk)); + } + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_TBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_TSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_TSE ? "enabled" : "disabled"); + + return 0; +} + +static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match) +{ + return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2; +} + +static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct cx23888_ir_state *state = to_state(sd); + + if (cx23888_ir_dbg_match(&chip->match)) { + chip->ident = state->id; + chip->revision = state->rev; + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx23888_ir_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct cx23888_ir_state *state = to_state(sd); + u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; + + if (!cx23888_ir_dbg_match(®->match)) + return -EINVAL; + if ((addr & 0x3) != 0) + return -EINVAL; + if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->size = 4; + reg->val = cx23888_ir_read4(state->dev, addr); + return 0; +} + +static int cx23888_ir_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct cx23888_ir_state *state = to_state(sd); + u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; + + if (!cx23888_ir_dbg_match(®->match)) + return -EINVAL; + if ((addr & 0x3) != 0) + return -EINVAL; + if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx23888_ir_write4(state->dev, addr, reg->val); + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = { + .g_chip_ident = cx23888_ir_g_chip_ident, + .log_status = cx23888_ir_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx23888_ir_g_register, + .s_register = cx23888_ir_s_register, +#endif +}; + +static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = { + .interrupt_service_routine = cx23888_ir_irq_handler, + + .rx_read = cx23888_ir_rx_read, + .rx_g_parameters = cx23888_ir_rx_g_parameters, + .rx_s_parameters = cx23888_ir_rx_s_parameters, + + .tx_write = cx23888_ir_tx_write, + .tx_g_parameters = cx23888_ir_tx_g_parameters, + .tx_s_parameters = cx23888_ir_tx_s_parameters, +}; + +static const struct v4l2_subdev_ops cx23888_ir_controller_ops = { + .core = &cx23888_ir_core_ops, + .ir = &cx23888_ir_ir_ops, +}; + +static const struct v4l2_subdev_ir_parameters default_rx_params = { + .bytes_per_data_element = sizeof(u32), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5, RC-6, and RC-6A carrier */ + + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + .noise_filter_min_width = 333333, /* ns */ + .carrier_range_lower = 35000, + .carrier_range_upper = 37000, + .invert = false, +}; + +static const struct v4l2_subdev_ir_parameters default_tx_params = { + .bytes_per_data_element = sizeof(u32), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ + .duty_cycle = 25, /* 25 % - RC-5 carrier */ + .invert = false, +}; + +int cx23888_ir_probe(struct cx23885_dev *dev) +{ + struct cx23888_ir_state *state; + struct v4l2_subdev *sd; + struct v4l2_subdev_ir_parameters default_params; + int ret; + + state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + spin_lock_init(&state->rx_kfifo_lock); + state->rx_kfifo = kfifo_alloc(CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL, + &state->rx_kfifo_lock); + if (state->rx_kfifo == NULL) + return -ENOMEM; + + spin_lock_init(&state->tx_kfifo_lock); + state->tx_kfifo = kfifo_alloc(CX23888_IR_TX_KFIFO_SIZE, GFP_KERNEL, + &state->tx_kfifo_lock); + if (state->tx_kfifo == NULL) { + kfifo_free(state->rx_kfifo); + return -ENOMEM; + } + + state->dev = dev; + state->id = V4L2_IDENT_CX23888_IR; + state->rev = 0; + sd = &state->sd; + + v4l2_subdev_init(sd, &cx23888_ir_controller_ops); + v4l2_set_subdevdata(sd, state); + /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */ + snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name); + sd->grp_id = CX23885_HW_888_IR; + + ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd); + if (ret == 0) { + /* + * Ensure no interrupts arrive from '888 specific conditions, + * since we ignore them in this driver to have commonality with + * similar IR controller cores. + */ + cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0); + + mutex_init(&state->rx_params_lock); + memcpy(&default_params, &default_rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); + + mutex_init(&state->tx_params_lock); + memcpy(&default_params, &default_tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); + } else { + kfifo_free(state->rx_kfifo); + kfifo_free(state->tx_kfifo); + } + return ret; +} + +int cx23888_ir_remove(struct cx23885_dev *dev) +{ + struct v4l2_subdev *sd; + struct cx23888_ir_state *state; + + sd = cx23885_find_hw(dev, CX23885_HW_888_IR); + if (sd == NULL) + return -ENODEV; + + cx23888_ir_rx_shutdown(sd); + cx23888_ir_tx_shutdown(sd); + + state = to_state(sd); + v4l2_device_unregister_subdev(sd); + kfifo_free(state->rx_kfifo); + kfifo_free(state->tx_kfifo); + kfree(state); + /* Nothing more to free() as state held the actual v4l2_subdev object */ + return 0; +} diff --git a/drivers/media/video/cx23885/cx23888-ir.h b/drivers/media/video/cx23885/cx23888-ir.h new file mode 100644 index 000000000000..3d446f9eb94b --- /dev/null +++ b/drivers/media/video/cx23885/cx23888-ir.h @@ -0,0 +1,28 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * CX23888 Integrated Consumer Infrared Controller + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23888_IR_H_ +#define _CX23888_IR_H_ +int cx23888_ir_probe(struct cx23885_dev *dev); +int cx23888_ir_remove(struct cx23885_dev *dev); +#endif diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index 2f846f5e0f9f..45608d50529c 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -23,87 +23,137 @@ #include "cx25840-core.h" -static int set_audclk_freq(struct i2c_client *client, u32 freq) +/* + * Note: The PLL and SRC parameters are based on a reference frequency that + * would ideally be: + * + * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz + * + * However, it's not the exact reference frequency that matters, only that the + * firmware and modules that comprise the driver for a particular board all + * use the same value (close to the ideal value). + * + * Comments below will note which reference frequency is assumed for various + * parameters. They will usually be one of + * + * ref_freq = 28.636360 MHz + * or + * ref_freq = 28.636363 MHz + */ + +static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - if (freq != 32000 && freq != 44100 && freq != 48000) - return -EINVAL; - - /* common for all inputs and rates */ - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ - if (!state->is_cx23885 && !state->is_cx231xx) - cx25840_write(client, 0x127, 0x50); - if (state->aud_input != CX25840_AUDIO_SERIAL) { switch (freq) { case 32000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1006040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x01bb39ee); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1006040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x1bb39ee + * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 + * 196.6 MHz pre-postdivide + * FIXME < 200 MHz is out of specified valid range + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x01bb39ee); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src3/4/6_ctl = 0x0801f77f */ + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ cx25840_write4(client, 0x900, 0x0801f77f); cx25840_write4(client, 0x904, 0x0801f77f); cx25840_write4(client, 0x90c, 0x0801f77f); break; case 44100: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1009040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1009040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - } - if (state->is_cx25836) - break; - - /* src3/4/6_ctl = 0x08016d59 */ + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ cx25840_write4(client, 0x900, 0x08016d59); cx25840_write4(client, 0x904, 0x08016d59); cx25840_write4(client, 0x90c, 0x08016d59); break; case 48000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x100a040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x0098d6e5); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x100a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src3/4/6_ctl = 0x08014faa */ + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ cx25840_write4(client, 0x900, 0x08014faa); cx25840_write4(client, 0x904, 0x08014faa); cx25840_write4(client, 0x90c, 0x08014faa); @@ -112,91 +162,249 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) } else { switch (freq) { case 32000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1e08040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x012a0869); - } + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e + */ + cx25840_write4(client, 0x108, 0x1e08040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x12a0869 + * 28636363 * 0x8.9504348/0x1e = 32000 * 256 + * 246 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x012a0869); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x54); - if (state->is_cx25836) + if (is_cx2583x(state)) break; - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ cx25840_write4(client, 0x8f8, 0x08010000); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ cx25840_write4(client, 0x900, 0x08020000); cx25840_write4(client, 0x904, 0x08020000); cx25840_write4(client, 0x90c, 0x08020000); - - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ - cx25840_write(client, 0x127, 0x54); break; case 44100: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1809040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x1809040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ cx25840_write4(client, 0x8f8, 0x080160cd); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ cx25840_write4(client, 0x900, 0x08017385); cx25840_write4(client, 0x904, 0x08017385); cx25840_write4(client, 0x90c, 0x08017385); break; case 48000: - if (!state->is_cx23885 && !state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x180a040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x180a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x0098d6e5); - } + /* src1_ctl */ + /* 0x1.8000 = 48000/32000 */ + cx25840_write4(client, 0x8f8, 0x08018000); - if (state->is_cx25836) - break; + /* src3/4/6_ctl */ + /* 0x1.5555 = 2 * (32000/48000) */ + cx25840_write4(client, 0x900, 0x08015555); + cx25840_write4(client, 0x904, 0x08015555); + cx25840_write4(client, 0x90c, 0x08015555); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + return cx25840_set_audclk_freq(client, freq); +} + +static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + case 44100: + case 48000: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + } + } else { + switch (freq) { + case 32000: + case 44100: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ + cx25840_write4(client, 0x900, 0x0801f77f); + cx25840_write4(client, 0x904, 0x0801f77f); + cx25840_write4(client, 0x90c, 0x0801f77f); + break; + + case 44100: + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ + cx25840_write4(client, 0x900, 0x08016d59); + cx25840_write4(client, 0x904, 0x08016d59); + cx25840_write4(client, 0x90c, 0x08016d59); + break; + + case 48000: + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } else { + switch (freq) { + /* FIXME These cases make different assumptions about audclk */ + case 32000: + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ + cx25840_write4(client, 0x8f8, 0x08010000); - if (!state->is_cx23885 && !state->is_cx231xx) { - /* src1_ctl */ - cx25840_write4(client, 0x8f8, 0x08018000); + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ + cx25840_write4(client, 0x900, 0x08020000); + cx25840_write4(client, 0x904, 0x08020000); + cx25840_write4(client, 0x90c, 0x08020000); + break; - /* src3/4/6_ctl */ - cx25840_write4(client, 0x900, 0x08015555); - cx25840_write4(client, 0x904, 0x08015555); - cx25840_write4(client, 0x90c, 0x08015555); - } else { + case 44100: + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ + cx25840_write4(client, 0x8f8, 0x080160cd); - cx25840_write4(client, 0x8f8, 0x0801867c); + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ + cx25840_write4(client, 0x900, 0x08017385); + cx25840_write4(client, 0x904, 0x08017385); + cx25840_write4(client, 0x90c, 0x08017385); + break; - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - } + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); break; } } @@ -206,6 +414,25 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) return 0; } +static int set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (freq != 32000 && freq != 44100 && freq != 48000) + return -EINVAL; + + if (is_cx231xx(state)) + return cx231xx_set_audclk_freq(client, freq); + + if (is_cx2388x(state)) + return cx23885_set_audclk_freq(client, freq); + + if (is_cx2583x(state)) + return cx25836_set_audclk_freq(client, freq); + + return cx25840_set_audclk_freq(client, freq); +} + void cx25840_audio_set_path(struct i2c_client *client) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); @@ -243,7 +470,7 @@ void cx25840_audio_set_path(struct i2c_client *client) cx25840_and_or(client, 0x810, ~0x1, 0x00); /* Ensure the controller is running when we exit */ - if (state->is_cx23885 || state->is_cx231xx) + if (is_cx2388x(state) || is_cx231xx(state)) cx25840_and_or(client, 0x803, ~0x10, 0x10); } @@ -383,7 +610,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) struct cx25840_state *state = to_state(sd); int retval; - if (!state->is_cx25836) + if (!is_cx2583x(state)) cx25840_and_or(client, 0x810, ~0x1, 1); if (state->aud_input != CX25840_AUDIO_SERIAL) { cx25840_and_or(client, 0x803, ~0x10, 0); @@ -392,7 +619,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) retval = set_audclk_freq(client, freq); if (state->aud_input != CX25840_AUDIO_SERIAL) cx25840_and_or(client, 0x803, ~0x10, 0x10); - if (!state->is_cx25836) + if (!is_cx2583x(state)) cx25840_and_or(client, 0x810, ~0x1, 0); return retval; } diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 1aeaf18a9bea..385ecd58f1c0 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -259,6 +259,13 @@ static void cx23885_initialize(struct i2c_client *client) struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; + /* + * Come out of digital power down + * The CX23888, at least, needs this, otherwise registers aside from + * 0x0-0x2 can't be read or written. + */ + cx25840_write(client, 0x000, 0); + /* Internal Reset */ cx25840_and_or(client, 0x102, ~0x01, 0x01); cx25840_and_or(client, 0x102, ~0x01, 0x00); @@ -269,18 +276,45 @@ static void cx23885_initialize(struct i2c_client *client) /* DIF in reset? */ cx25840_write(client, 0x398, 0); - /* Trust the default xtal, no division */ - /* This changes for the cx23888 products */ + /* + * Trust the default xtal, no division + * '885: 28.636363... MHz + * '887: 25.000000 MHz + * '888: 50.000000 MHz + */ cx25840_write(client, 0x2, 0x76); - /* Bring down the regulator for AUX clk */ + /* Power up all the PLL's and DLL */ cx25840_write(client, 0x1, 0x40); - /* Sys PLL frac */ - cx25840_write4(client, 0x11c, 0x01d1744c); - - /* Sys PLL int */ - cx25840_write4(client, 0x118, 0x00000416); + /* Sys PLL */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x00e8ba26); + cx25840_write4(client, 0x118, 0x0000040b); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x01d1744c); + cx25840_write4(client, 0x118, 0x00000416); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x00000000); + cx25840_write4(client, 0x118, 0x00000414); + break; + } /* Disable DIF bypass */ cx25840_write4(client, 0x33c, 0x00000001); @@ -288,11 +322,15 @@ static void cx23885_initialize(struct i2c_client *client) /* DIF Src phase inc */ cx25840_write4(client, 0x340, 0x0df7df83); - /* Vid PLL frac */ - cx25840_write4(client, 0x10c, 0x01b6db7b); - - /* Vid PLL int */ - cx25840_write4(client, 0x108, 0x00000512); + /* + * Vid PLL + * Setup for a BT.656 pixel clock of 13.5 Mpixels/second + * + * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz + * 432.0 MHz before post divide + */ + cx25840_write4(client, 0x10c, 0x002be2c9); + cx25840_write4(client, 0x108, 0x0000040f); /* Luma */ cx25840_write4(client, 0x414, 0x00107d12); @@ -300,11 +338,43 @@ static void cx23885_initialize(struct i2c_client *client) /* Chroma */ cx25840_write4(client, 0x420, 0x3d008282); - /* Aux PLL frac */ - cx25840_write4(client, 0x114, 0x017dbf48); - - /* Aux PLL int */ - cx25840_write4(client, 0x110, 0x000a030e); + /* + * Aux PLL + * Initial setup for audio sample clock: + * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz + * Intial I2S output/master clock(?): + * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz + */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x00bedfa4); + cx25840_write4(client, 0x110, 0x000a0307); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x017dbf48); + cx25840_write4(client, 0x110, 0x000a030e); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x01bf0c9e); + cx25840_write4(client, 0x110, 0x000a030c); + break; + }; /* ADC2 input select */ cx25840_write(client, 0x102, 0x10); @@ -494,7 +564,7 @@ void cx25840_std_setup(struct i2c_client *client) } /* DEBUG: Displays configured PLL frequency */ - if (!state->is_cx231xx) { + if (!is_cx231xx(state)) { pll_int = cx25840_read(client, 0x108); pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; pll_post = cx25840_read(client, 0x109); @@ -615,13 +685,30 @@ static void input_change(struct i2c_client *client) } cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_PAL) { - /* Follow tuner change procedure for PAL */ + /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); - cx25840_write(client, 0x80b, 0x10); + /* Since system PAL-L is pretty much non-existant and + not used by any public broadcast network, force + 6.5 MHz carrier to be interpreted as System DK, + this avoids DK audio detection instability */ + cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_SECAM) { - /* Select autodetect for SECAM */ + /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); - cx25840_write(client, 0x80b, 0x10); + /* If only one of SECAM-DK / SECAM-L is required, then force + 6.5MHz carrier, else autodetect it */ + if ((std & V4L2_STD_SECAM_DK) && + !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System DK */ + cx25840_write(client, 0x80b, 0x00); + } else if (!(std & V4L2_STD_SECAM_DK) && + (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System L */ + cx25840_write(client, 0x80b, 0x08); + } else { + /* 6.5 MHz carrier to be autodetected */ + cx25840_write(client, 0x80b, 0x10); + } } cx25840_and_or(client, 0x810, ~0x01, 0); @@ -633,6 +720,10 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8); + u8 is_component = (vid_input & CX25840_COMPONENT_ON) == + CX25840_COMPONENT_ON; + int luma = vid_input & 0xf0; + int chroma = vid_input & 0xf00; u8 reg; v4l_dbg(1, cx25840_debug, client, @@ -645,18 +736,14 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp reg = vid_input & 0xff; if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON) is_composite = 0; - else + else if ((vid_input & CX25840_COMPONENT_ON) == 0) is_composite = 1; v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", reg, is_composite); - } else - if (is_composite) { + } else if (is_composite) { reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); } else { - int luma = vid_input & 0xf0; - int chroma = vid_input & 0xf00; - if ((vid_input & ~0xff0) || luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { @@ -678,7 +765,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp * configuration in reg (for the cx23885) so we have no * need to attempt to flip bits for earlier av decoders. */ - if (!state->is_cx23885 && !state->is_cx231xx) { + if (!is_cx2388x(state) && !is_cx231xx(state)) { switch (aud_input) { case CX25840_AUDIO_SERIAL: /* do nothing, use serial audio input */ @@ -698,10 +785,13 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_write(client, 0x103, reg); - /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); + /* Set INPUT_MODE to Composite, S-Video or Component */ + if (is_component) + cx25840_and_or(client, 0x401, ~0x6, 0x6); + else + cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); - if (!state->is_cx23885 && !state->is_cx231xx) { + if (!is_cx2388x(state) && !is_cx231xx(state)) { /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ @@ -710,22 +800,31 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp else cx25840_and_or(client, 0x102, ~0x4, 0); } else { - if (is_composite) + /* Set DUAL_MODE_ADC2 to 1 if component*/ + cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0); + if (is_composite) { /* ADC2 input select channel 2 */ cx25840_and_or(client, 0x102, ~0x2, 0); - else - /* ADC2 input select channel 3 */ - cx25840_and_or(client, 0x102, ~0x2, 2); + } else if (!is_component) { + /* S-Video */ + if (chroma >= CX25840_SVIDEO_CHROMA7) { + /* ADC2 input select channel 3 */ + cx25840_and_or(client, 0x102, ~0x2, 2); + } else { + /* ADC2 input select channel 2 */ + cx25840_and_or(client, 0x102, ~0x2, 0); + } + } } state->vid_input = vid_input; state->aud_input = aud_input; - if (!state->is_cx25836) { + if (!is_cx2583x(state)) { cx25840_audio_set_path(client); input_change(client); } - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Audio channel 1 src : Parallel 1 */ cx25840_write(client, 0x124, 0x03); @@ -741,7 +840,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp */ cx25840_write(client, 0x918, 0xa0); cx25840_write(client, 0x919, 0x01); - } else if (state->is_cx231xx) { + } else if (is_cx231xx(state)) { /* Audio channel 1 src : Parallel 1 */ cx25840_write(client, 0x124, 0x03); @@ -805,7 +904,7 @@ static int set_v4lstd(struct i2c_client *client) cx25840_and_or(client, 0x400, ~0xf, fmt); cx25840_and_or(client, 0x403, ~0x3, pal_m); cx25840_std_setup(client); - if (!state->is_cx25836) + if (!is_cx2583x(state)) input_change(client); return 0; } @@ -868,7 +967,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return cx25840_audio_s_ctrl(sd, ctrl); @@ -905,7 +1004,7 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return cx25840_audio_g_ctrl(sd, ctrl); default: @@ -1209,11 +1308,11 @@ static int cx25840_load_fw(struct v4l2_subdev *sd) if (!state->is_initialized) { /* initialize and load firmware */ state->is_initialized = 1; - if (state->is_cx25836) + if (is_cx2583x(state)) cx25836_initialize(client); - else if (state->is_cx23885) + else if (is_cx2388x(state)) cx23885_initialize(client); - else if (state->is_cx231xx) + else if (is_cx231xx(state)) cx231xx_initialize(client); else cx25840_initialize(client); @@ -1256,17 +1355,17 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) v4l_dbg(1, cx25840_debug, client, "%s output\n", enable ? "enable" : "disable"); if (enable) { - if (state->is_cx23885 || state->is_cx231xx) { + if (is_cx2388x(state) || is_cx231xx(state)) { u8 v = (cx25840_read(client, 0x421) | 0x0b); cx25840_write(client, 0x421, v); } else { cx25840_write(client, 0x115, - state->is_cx25836 ? 0x0c : 0x8c); + is_cx2583x(state) ? 0x0c : 0x8c); cx25840_write(client, 0x116, - state->is_cx25836 ? 0x04 : 0x07); + is_cx2583x(state) ? 0x04 : 0x07); } } else { - if (state->is_cx23885 || state->is_cx231xx) { + if (is_cx2388x(state) || is_cx231xx(state)) { u8 v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); } else { @@ -1292,7 +1391,7 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) default: break; } - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; switch (qc->id) { @@ -1346,7 +1445,7 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd, struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return set_input(client, state->vid_input, input); } @@ -1356,7 +1455,7 @@ static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!state->is_cx25836) + if (!is_cx2583x(state)) input_change(client); return 0; } @@ -1373,7 +1472,7 @@ static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; vt->signal = vpres ? 0xffff : 0x0; - if (state->is_cx25836) + if (is_cx2583x(state)) return 0; vt->capability |= @@ -1404,7 +1503,7 @@ static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio || state->is_cx25836) + if (state->radio || is_cx2583x(state)) return 0; switch (vt->audmode) { @@ -1445,11 +1544,11 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val) struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->is_cx25836) + if (is_cx2583x(state)) cx25836_initialize(client); - else if (state->is_cx23885) + else if (is_cx2388x(state)) cx23885_initialize(client); - else if (state->is_cx231xx) + else if (is_cx231xx(state)) cx231xx_initialize(client); else cx25840_initialize(client); @@ -1470,7 +1569,7 @@ static int cx25840_log_status(struct v4l2_subdev *sd) struct i2c_client *client = v4l2_get_subdevdata(sd); log_video_status(client); - if (!state->is_cx25836) + if (!is_cx2583x(state)) log_audio_status(client); return 0; } @@ -1521,12 +1620,50 @@ static const struct v4l2_subdev_ops cx25840_ops = { /* ----------------------------------------------------------------------- */ +static u32 get_cx2388x_ident(struct i2c_client *client) +{ + u32 ret; + + /* Come out of digital power down */ + cx25840_write(client, 0x000, 0); + + /* Detecting whether the part is cx23885/7/8 is more + * difficult than it needs to be. No ID register. Instead we + * probe certain registers indicated in the datasheets to look + * for specific defaults that differ between the silicon designs. */ + + /* It's either 885/7 if the IR Tx Clk Divider register exists */ + if (cx25840_read4(client, 0x204) & 0xffff) { + /* CX23885 returns bogus repetitive byte values for the DIF, + * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ + ret = cx25840_read4(client, 0x300); + if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { + /* No DIF */ + ret = V4L2_IDENT_CX23885_AV; + } else { + /* CX23887 has a broken DIF, but the registers + * appear valid (but unsed), good enough to detect. */ + ret = V4L2_IDENT_CX23887_AV; + } + } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { + /* DIF PLL Freq Word reg exists; chip must be a CX23888 */ + ret = V4L2_IDENT_CX23888_AV; + } else { + v4l_err(client, "Unable to detect h/w, assuming cx23887\n"); + ret = V4L2_IDENT_CX23887_AV; + } + + /* Back into digital power down */ + cx25840_write(client, 0x000, 2); + return ret; +} + static int cx25840_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct cx25840_state *state; struct v4l2_subdev *sd; - u32 id; + u32 id = V4L2_IDENT_NONE; u16 device_id; /* Check if the adapter supports the needed features */ @@ -1543,17 +1680,22 @@ static int cx25840_probe(struct i2c_client *client, * 0x83 for the cx2583x and 0x84 for the cx2584x */ if ((device_id & 0xff00) == 0x8300) { id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; - } - else if ((device_id & 0xff00) == 0x8400) { + } else if ((device_id & 0xff00) == 0x8400) { id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); } else if (device_id == 0x0000) { - id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; - } else if (device_id == 0x1313) { - id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; + id = get_cx2388x_ident(client); } else if ((device_id & 0xfff0) == 0x5A30) { - id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); - } - else { + /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */ + id = V4L2_IDENT_CX2310X_AV; + } else if ((device_id & 0xff) == (device_id >> 8)) { + v4l_err(client, + "likely a confused/unresponsive cx2388[578] A/V decoder" + " found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_err(client, "A method to reset it from the cx25840 driver" + " software is not known at this time\n"); + return -ENODEV; + } else { v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); return -ENODEV; } @@ -1564,17 +1706,45 @@ static int cx25840_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &cx25840_ops); - /* Note: revision '(device_id & 0x0f) == 2' was never built. The - marking skips from 0x1 == 22 to 0x3 == 23. */ - v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", - (device_id & 0xfff0) >> 4, - (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), - client->addr << 1, client->adapter->name); + switch (id) { + case V4L2_IDENT_CX23885_AV: + v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23887_AV: + v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23888_AV: + v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX2310X_AV: + v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n", + device_id, client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25840: + case V4L2_IDENT_CX25841: + case V4L2_IDENT_CX25842: + case V4L2_IDENT_CX25843: + /* Note: revision '(device_id & 0x0f) == 2' was never built. The + marking skips from 0x1 == 22 to 0x3 == 23. */ + v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, + (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 + : (device_id & 0x0f), + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25836: + case V4L2_IDENT_CX25837: + default: + v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, device_id & 0x0f, + client->addr << 1, client->adapter->name); + break; + } state->c = client; - state->is_cx25836 = ((device_id & 0xff00) == 0x8300); - state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); - state->is_cx231xx = (device_id == 0x5a3e); state->vid_input = CX25840_COMPOSITE7; state->aud_input = CX25840_AUDIO8; state->audclk_freq = 48000; diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 814b56536994..55345444417f 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -23,6 +23,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> #include <linux/i2c.h> /* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is @@ -48,9 +49,6 @@ struct cx25840_state { int vbi_line_offset; u32 id; u32 rev; - int is_cx25836; - int is_cx23885; - int is_cx231xx; int is_initialized; wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ struct work_struct fw_work; /* work entry for fw load */ @@ -61,6 +59,24 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct cx25840_state, sd); } +static inline bool is_cx2583x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX25836 || + state->id == V4L2_IDENT_CX25837; +} + +static inline bool is_cx231xx(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX2310X_AV; +} + +static inline bool is_cx2388x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23885_AV || + state->id == V4L2_IDENT_CX23887_AV || + state->id == V4L2_IDENT_CX23888_AV; +} + /* ----------------------------------------------------------------------- */ /* cx25850-core.c */ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 1f483c1d0dbe..8150200511da 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -67,9 +67,9 @@ static const char *get_fw_name(struct i2c_client *client) if (firmware[0]) return firmware; - if (state->is_cx23885) + if (is_cx2388x(state)) return "v4l-cx23885-avcore-01.fw"; - if (state->is_cx231xx) + if (is_cx231xx(state)) return "v4l-cx231xx-avcore-01.fw"; return "v4l-cx25840.fw"; } @@ -112,13 +112,13 @@ int cx25840_loadfw(struct i2c_client *client) int MAX_BUF_SIZE = FWSEND; u32 gpio_oe = 0, gpio_da = 0; - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Preserve the GPIO OE and output bits */ gpio_oe = cx25840_read(client, 0x160); gpio_da = cx25840_read(client, 0x164); } - if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) { + if (is_cx231xx(state) && MAX_BUF_SIZE > 16) { v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ } @@ -156,7 +156,7 @@ int cx25840_loadfw(struct i2c_client *client) size = fw->size; release_firmware(fw); - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Restore GPIO configuration after f/w load */ cx25840_write(client, 0x160, gpio_oe); cx25840_write(client, 0x164, gpio_da); diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 49952980dab3..c7e5851d3486 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -61,6 +61,8 @@ config VIDEO_CX88_DVB select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_STB6000 if !DVB_FE_CUSTOMISE + select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 33be6369871a..d844f2aaa01d 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -2075,6 +2075,18 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_PROF_7301] = { + .name = "Prof 7301 DVB-S/S2", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = { { + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -2535,6 +2547,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x107d, .subdevice = 0x6618, .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, + }, { + .subvendor = 0xb034, + .subdevice = 0x3034, + .card = CX88_BOARD_PROF_7301, }, }; @@ -3211,6 +3227,7 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_TBS_8920: case CX88_BOARD_PROF_6200: case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: case CX88_BOARD_SATTRADE_ST4200: cx_write(MO_GP0_IO, 0x8000); msleep(100); @@ -3267,7 +3284,7 @@ static void cx88_card_setup(struct cx88_core *core) ctl.fname); call_all(core, tuner, s_config, &xc2028_cfg); } - call_all(core, tuner, s_standby); + call_all(core, core, s_power, 0); } /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index cf634606ba9a..b35411160f04 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -624,7 +624,7 @@ int cx88_reset(struct cx88_core *core) /* setup image format */ cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000); - /* setup FIFO Threshholds */ + /* setup FIFO Thresholds */ cx_write(MO_PDMA_STHRSH, 0x0807); cx_write(MO_PDMA_DTHRSH, 0x0807); diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 518bcfe18bcb..b14296923250 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -53,6 +53,9 @@ #include "stv0288.h" #include "stb6000.h" #include "cx24116.h" +#include "stv0900.h" +#include "stb6100.h" +#include "stb6100_proc.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); @@ -573,6 +576,15 @@ static int cx24116_set_ts_param(struct dvb_frontend *fe, return 0; } +static int stv0900_set_ts_param(struct dvb_frontend *fe, + int is_punctured) +{ + struct cx8802_dev *dev = fe->dvb->priv; + dev->ts_gen_cntrl = 0; + + return 0; +} + static int cx24116_reset_device(struct dvb_frontend *fe) { struct cx8802_dev *dev = fe->dvb->priv; @@ -601,6 +613,23 @@ static struct cx24116_config tevii_s460_config = { .reset_device = cx24116_reset_device, }; +static struct stv0900_config prof_7301_stv0900_config = { + .demod_address = 0x6a, +/* demod_mode = 0,*/ + .xtal = 27000000, + .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ + .diseqc_mode = 2,/* 2/3 PWM */ + .tun1_maddress = 0,/* 0x60 */ + .tun1_adc = 0,/* 2 Vpp */ + .path1_mode = 3, + .set_ts_params = stv0900_set_ts_param, +}; + +static struct stb6100_config prof_7301_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; + static struct stv0299_config tevii_tuner_sharp_config = { .demod_address = 0x68, .inittab = sharp_z0194a_inittab, @@ -1149,6 +1178,31 @@ static int dvb_register(struct cx8802_dev *dev) goto frontend_detach; } break; + case CX88_BOARD_PROF_7301:{ + struct dvb_tuner_ops *tuner_ops = NULL; + + fe0->dvb.frontend = dvb_attach(stv0900_attach, + &prof_7301_stv0900_config, + &core->i2c_adap, 0); + if (fe0->dvb.frontend != NULL) { + if (!dvb_attach(stb6100_attach, fe0->dvb.frontend, + &prof_7301_stb6100_config, + &core->i2c_adap)) + goto frontend_detach; + + tuner_ops = &fe0->dvb.frontend->ops.tuner_ops; + tuner_ops->set_frequency = stb6100_set_freq; + tuner_ops->get_frequency = stb6100_get_freq; + tuner_ops->set_bandwidth = stb6100_set_bandw; + tuner_ops->get_bandwidth = stb6100_get_bandw; + + core->prev_set_voltage = + fe0->dvb.frontend->ops.set_voltage; + fe0->dvb.frontend->ops.set_voltage = + tevii_dvbs_set_voltage; + } + break; + } default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", core->name); @@ -1170,11 +1224,11 @@ static int dvb_register(struct cx8802_dev *dev) fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ - call_all(core, tuner, s_standby); + call_all(core, core, s_power, 0); /* register everything */ return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, - &dev->pci->dev, adapter_nr, mfe_shared); + &dev->pci->dev, adapter_nr, mfe_shared, NULL); frontend_detach: core->gate_ctrl = NULL; diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 78b3635178af..92b8cdf9fb81 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -118,13 +118,13 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) data = (data << 4) | ((gpio_key & 0xf0) >> 4); - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); ir_input_nokey(ir->input, &ir->ir); } else if (ir->mask_keydown) { /* bit set on keydown */ if (gpio & ir->mask_keydown) { - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); } else { ir_input_nokey(ir->input, &ir->ir); } @@ -132,14 +132,14 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) } else if (ir->mask_keyup) { /* bit cleared on keydown */ if (0 == (gpio & ir->mask_keyup)) { - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); } else { ir_input_nokey(ir->input, &ir->ir); } } else { /* can't distinguish keydown/up :-/ */ - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); ir_input_nokey(ir->input, &ir->ir); } } @@ -303,6 +303,23 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->mask_keydown = 0x02; ir->polling = 50; /* ms */ break; + case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_SATTRADE_ST4200: + case CX88_BOARD_TBS_8920: + case CX88_BOARD_TBS_8910: + case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: + case CX88_BOARD_PROF_6200: + ir_codes = &ir_codes_tbs_nec_table; + ir_type = IR_TYPE_PD; + ir->sampling = 0xff00; /* address */ + break; + case CX88_BOARD_TEVII_S460: + case CX88_BOARD_TEVII_S420: + ir_codes = &ir_codes_tevii_nec_table; + ir_type = IR_TYPE_PD; + ir->sampling = 0xff00; /* address */ + break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: ir_codes = &ir_codes_dntv_live_dvbt_pro_table; ir_type = IR_TYPE_PD; @@ -343,7 +360,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -373,6 +393,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) cx88_ir_stop(core, ir); core->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -387,6 +408,7 @@ int cx88_ir_fini(struct cx88_core *core) return 0; cx88_ir_stop(core, ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); @@ -432,8 +454,17 @@ void cx88_ir_irq(struct cx88_core *core) /* decode it */ switch (core->boardnr) { + case CX88_BOARD_TEVII_S460: + case CX88_BOARD_TEVII_S420: case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: + case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_SATTRADE_ST4200: + case CX88_BOARD_TBS_8920: + case CX88_BOARD_TBS_8910: + case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: + case CX88_BOARD_PROF_6200: ircode = ir_decode_pulsedistance(ir->samples, ir->scount, 1, 4); if (ircode == 0xffffffff) { /* decoding error */ @@ -461,7 +492,7 @@ void cx88_ir_irq(struct cx88_core *core) ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f); - ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f, (ircode >> 16) & 0xff); + ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f); ir->release = jiffies + msecs_to_jiffies(120); break; case CX88_BOARD_HAUPPAUGE: @@ -498,7 +529,7 @@ void cx88_ir_irq(struct cx88_core *core) if ( dev != 0x1e && dev != 0x1f ) /* not a hauppauge remote */ break; - ir_input_keydown(ir->input, &ir->ir, code, ircode); + ir_input_keydown(ir->input, &ir->ir, code); ir->release = jiffies + msecs_to_jiffies(120); break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: @@ -506,7 +537,7 @@ void cx88_ir_irq(struct cx88_core *core) ir_dprintk("biphase decoded: %x\n", ircode); if ((ircode & 0xfffff000) != 0x3000) break; - ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f, ircode); + ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f); ir->release = jiffies + msecs_to_jiffies(120); break; } diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 57e6b1241090..d7e8fcee559c 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -935,7 +935,7 @@ static int video_release(struct file *file) mutex_lock(&dev->core->lock); if(atomic_dec_and_test(&dev->core->users)) - call_all(dev->core, tuner, s_standby); + call_all(dev->core, core, s_power, 0); mutex_unlock(&dev->core->lock); return 0; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index d5cea41f4207..e1c521710103 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -238,6 +238,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_HAUPPAUGE_IRONLY 80 #define CX88_BOARD_WINFAST_DTV1800H 81 #define CX88_BOARD_WINFAST_DTV2000H_J 82 +#define CX88_BOARD_PROF_7301 83 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c index 4629cabe3f28..314390016370 100644 --- a/drivers/media/video/davinci/dm355_ccdc.c +++ b/drivers/media/video/davinci/dm355_ccdc.c @@ -285,7 +285,7 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) if ((ccdcparam->med_filt_thres < 0) || (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { - dev_dbg(dev, "Invalid value of median filter thresold\n"); + dev_dbg(dev, "Invalid value of median filter threshold\n"); return -EINVAL; } @@ -959,7 +959,7 @@ static struct ccdc_hw_device ccdc_hw_dev = { }, }; -static int dm355_ccdc_init(void) +static int __init dm355_ccdc_init(void) { printk(KERN_NOTICE "dm355_ccdc_init\n"); if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) @@ -969,7 +969,7 @@ static int dm355_ccdc_init(void) return 0; } -static void dm355_ccdc_exit(void) +static void __exit dm355_ccdc_exit(void) { vpfe_unregister_ccdc_device(&ccdc_hw_dev); } diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c index 2f19a919f477..d5fa193f32d2 100644 --- a/drivers/media/video/davinci/dm644x_ccdc.c +++ b/drivers/media/video/davinci/dm644x_ccdc.c @@ -859,7 +859,7 @@ static struct ccdc_hw_device ccdc_hw_dev = { }, }; -static int dm644x_ccdc_init(void) +static int __init dm644x_ccdc_init(void) { printk(KERN_NOTICE "dm644x_ccdc_init\n"); if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) @@ -869,7 +869,7 @@ static int dm644x_ccdc_init(void) return 0; } -static void dm644x_ccdc_exit(void) +static void __exit dm644x_ccdc_exit(void) { vpfe_unregister_ccdc_device(&ccdc_hw_dev); } diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 402ce43ef38e..12a1b3d7132d 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -660,7 +660,7 @@ static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) frame_format = ccdc_dev->hw_ops.get_frame_format(); if (frame_format == CCDC_FRMFMT_PROGRESSIVE) - free_irq(IRQ_VDINT1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); } static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) @@ -1338,7 +1338,7 @@ static int vpfe_reqbufs(struct file *file, void *priv, vpfe_dev->memory = req_buf->memory; videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, &vpfe_videobuf_qops, - NULL, + vpfe_dev->pdev, &vpfe_dev->irqlock, req_buf->type, vpfe_dev->fmt.fmt.pix.field, @@ -1413,6 +1413,41 @@ static int vpfe_dqbuf(struct file *file, void *priv, buf, file->f_flags & O_NONBLOCK); } +static int vpfe_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, queryctrl, qctrl); + +} + +static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, g_ctrl, ctrl); +} + +static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_ctrl, ctrl); +} + /* * vpfe_calculate_offsets : This function calculates buffers offset * for top and bottom field @@ -1577,7 +1612,7 @@ static int vpfe_cropcap(struct file *file, void *priv, v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); - if (vpfe_dev->std_index > ARRAY_SIZE(vpfe_standards)) + if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) return -EINVAL; memset(crop, 0, sizeof(struct v4l2_cropcap)); @@ -1710,6 +1745,9 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_querystd = vpfe_querystd, .vidioc_s_std = vpfe_s_std, .vidioc_g_std = vpfe_g_std, + .vidioc_queryctrl = vpfe_queryctrl, + .vidioc_g_ctrl = vpfe_g_ctrl, + .vidioc_s_ctrl = vpfe_s_ctrl, .vidioc_reqbufs = vpfe_reqbufs, .vidioc_querybuf = vpfe_querybuf, .vidioc_qbuf = vpfe_qbuf, @@ -1978,8 +2016,7 @@ static __init int vpfe_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vpfe_dev); /* set driver private data */ video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); - i2c_adap = i2c_get_adapter(1); - vpfe_cfg = pdev->dev.platform_data; + i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); num_subdevs = vpfe_cfg->num_subdevs; vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, GFP_KERNEL); diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c index 6d709ca8cfb0..453236bd7559 100644 --- a/drivers/media/video/davinci/vpss.c +++ b/drivers/media/video/davinci/vpss.c @@ -53,7 +53,7 @@ struct vpss_hw_ops { int (*enable_clock)(enum vpss_clock_sel clock_sel, int en); /* select input to ccdc */ void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); - /* clear wbl overlflow bit */ + /* clear wbl overflow bit */ int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); }; diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index ac947aecb9c3..bd783387b37d 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -293,7 +293,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("opening device and trying to acquire exclusive lock\n"); if (!dev) { - printk(KERN_ERR "BUG: em28xx can't find device struct." + em28xx_err("BUG: em28xx can't find device struct." " Can't proceed with open\n"); return -ENODEV; } @@ -325,7 +325,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) return 0; err: - printk(KERN_ERR "Error while configuring em28xx mixer\n"); + em28xx_err("Error while configuring em28xx mixer\n"); return ret; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index c0fd5c6feeac..82da205047be 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -225,6 +225,14 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { { -1, -1, -1, -1}, }; +static struct em28xx_reg_seq vc211a_enable[] = { + {EM28XX_R08_GPIO, 0xff, 0x07, 10}, + {EM28XX_R08_GPIO, 0xff, 0x0f, 10}, + {EM28XX_R08_GPIO, 0xff, 0x0b, 10}, + { -1, -1, -1, -1}, +}; + + /* * Board definitions */ @@ -829,7 +837,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = &ir_codes_rc5_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1009,6 +1017,23 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2800_BOARD_VC211A] = { + .name = "Actionmaster/LinXcel/Digitus VC211A", + .is_em2800 = 1, + .tuner_type = TUNER_ABSENT, /* Capture-only board */ + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = vc211a_enable, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = vc211a_enable, + } }, + }, [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { .name = "Leadtek Winfast USB II", .is_em2800 = 1, @@ -1381,10 +1406,14 @@ struct em28xx_board em28xx_boards[] = { }, [EM2882_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS (em2882)", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1608,6 +1637,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2861), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2862), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2870), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2881), @@ -2050,6 +2081,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) switch (dev->model) { case EM2880_BOARD_EMPIRE_DUAL_TV: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_TERRATEC_HYBRID_XS: @@ -2227,6 +2259,7 @@ static int em28xx_hint_board(struct em28xx *dev) /* ----------------------------------------------------------------------- */ void em28xx_register_i2c_ir(struct em28xx *dev) { + struct i2c_board_info info; const unsigned short addr_list[] = { 0x30, 0x47, I2C_CLIENT_END }; @@ -2234,9 +2267,9 @@ void em28xx_register_i2c_ir(struct em28xx *dev) if (disable_ir) return; - memset(&dev->info, 0, sizeof(&dev->info)); + memset(&info, 0, sizeof(struct i2c_board_info)); memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); /* detect & configure */ switch (dev->model) { @@ -2259,8 +2292,8 @@ void em28xx_register_i2c_ir(struct em28xx *dev) } if (dev->init_data.name) - dev->info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); + info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); } void em28xx_card_setup(struct em28xx *dev) @@ -2524,6 +2557,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->chip_id = retval; switch (dev->chip_id) { + case CHIP_ID_EM2800: + em28xx_info("chip ID is em2800\n"); + break; case CHIP_ID_EM2710: em28xx_info("chip ID is em2710\n"); break; @@ -2650,7 +2686,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_init_extension(dev); /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); return 0; diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index a88257a7d94f..3f86d36dff2b 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -50,7 +50,7 @@ MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); printk(KERN_INFO "%s %s :"fmt, \ dev->name, __func__ , ##arg); } while (0) -static int alt = EM28XX_PINOUT; +static int alt; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); @@ -533,8 +533,15 @@ int em28xx_audio_setup(struct em28xx *dev) vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1); if (vid1 < 0) { - /* Device likely doesn't support AC97 */ + /* + * Device likely doesn't support AC97 + * Note: (some) em2800 devices without eeprom reports 0x91 on + * CHIPCFG register, even not having an AC97 chip + */ em28xx_warn("AC97 chip type couldn't be determined\n"); + dev->audio_mode.ac97 = EM28XX_NO_AC97; + dev->has_alsa_audio = 0; + dev->audio_mode.has_audio = 0; goto init_audio; } @@ -778,6 +785,16 @@ int em28xx_set_alternate(struct em28xx *dev) int i; unsigned int min_pkt_size = dev->width * 2 + 4; + /* + * alt = 0 is used only for control messages, so, only values + * greater than 0 can be used for streaming. + */ + if (alt && alt < dev->num_alt) { + em28xx_coredbg("alternate forced to %d\n", dev->alt); + dev->alt = alt; + goto set_alt; + } + /* When image size is bigger than a certain value, the frame size should be increased, otherwise, only green screen will be received. @@ -798,6 +815,7 @@ int em28xx_set_alternate(struct em28xx *dev) dev->alt = i; } +set_alt: if (dev->alt != prev_alt) { em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", min_pkt_size, dev->alt); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index db749461e5c6..cc0505eb900f 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -313,22 +313,20 @@ static int attach_xc3028(u8 addr, struct em28xx *dev) cfg.i2c_addr = addr; if (!dev->dvb->frontend) { - printk(KERN_ERR "%s/2: dvb frontend not attached. " - "Can't attach xc3028\n", - dev->name); + em28xx_errdev("/2: dvb frontend not attached. " + "Can't attach xc3028\n"); return -EINVAL; } fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg); if (!fe) { - printk(KERN_ERR "%s/2: xc3028 attach failed\n", - dev->name); + em28xx_errdev("/2: xc3028 attach failed\n"); dvb_frontend_detach(dev->dvb->frontend); dev->dvb->frontend = NULL; return -EINVAL; } - printk(KERN_INFO "%s/2: xc3028 attached\n", dev->name); + em28xx_info("%s/2: xc3028 attached\n", dev->name); return 0; } @@ -463,7 +461,7 @@ static int dvb_init(struct em28xx *dev) dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); if (dvb == NULL) { - printk(KERN_INFO "em28xx_dvb: memory allocation failed\n"); + em28xx_info("em28xx_dvb: memory allocation failed\n"); return -ENOMEM; } dev->dvb = dvb; @@ -493,6 +491,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, @@ -569,15 +568,12 @@ static int dvb_init(struct em28xx *dev) } break; default: - printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" - " isn't supported yet\n", - dev->name); + em28xx_errdev("/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n"); break; } if (NULL == dvb->frontend) { - printk(KERN_ERR - "%s/2: frontend initialization failed\n", - dev->name); + em28xx_errdev("/2: frontend initialization failed\n"); result = -EINVAL; goto out_free; } @@ -591,7 +587,7 @@ static int dvb_init(struct em28xx *dev) goto out_free; em28xx_set_mode(dev, EM28XX_SUSPEND); - printk(KERN_INFO "Successfully loaded em28xx-dvb\n"); + em28xx_info("Successfully loaded em28xx-dvb\n"); return 0; out_free: diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 7a0fe3816e3d..d96ec7c09dca 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -70,6 +70,7 @@ struct em28xx_IR { int polling; struct delayed_work work; unsigned int last_toggle:1; + unsigned int full_code:1; unsigned int last_readcount; unsigned int repeat_interval; @@ -246,9 +247,10 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) return; } - dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n", + dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n", poll_result.toggle_bit, poll_result.read_count, - ir->last_readcount, poll_result.rc_data[0]); + ir->last_readcount, poll_result.rc_address, + poll_result.rc_data[0]); if (ir->dev->chip_id == CHIP_ID_EM2874) { /* The em2874 clears the readcount field every time the @@ -282,8 +284,15 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) if (do_sendkey) { dprintk("sending keypress\n"); - ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], - poll_result.rc_data[0]); + + if (ir->full_code) + ir_input_keydown(ir->input, &ir->ir, + poll_result.rc_address << 8 | + poll_result.rc_data[0]); + else + ir_input_keydown(ir->input, &ir->ir, + poll_result.rc_data[0]); + ir_input_nokey(ir->input, &ir->ir); } @@ -333,6 +342,8 @@ int em28xx_ir_init(struct em28xx *dev) switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: + if (dev->model == EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950) + ir->full_code = 1; ir->get_key = default_polling_getkey; break; case CHIP_ID_EM2874: @@ -356,7 +367,11 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, + dev->board.ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; @@ -381,6 +396,7 @@ int em28xx_ir_init(struct em28xx *dev) em28xx_ir_stop(ir); dev->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -395,6 +411,7 @@ int em28xx_ir_fini(struct em28xx *dev) return 0; em28xx_ir_stop(ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index ed12e7ffcbd0..058ac87639ce 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -192,6 +192,7 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { + CHIP_ID_EM2800 = 7, CHIP_ID_EM2710 = 17, CHIP_ID_EM2820 = 18, /* Also used by some em2710 */ CHIP_ID_EM2840 = 20, diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 3a1dfb7726f8..7ad65370f274 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1060,12 +1060,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; - /* According to empiatech support the MaxPacketSize is too small - * to support framesizes larger than 640x480 @ 30 fps or 640x576 - * @ 25 fps. As this would cut of a part of the image we prefer - * 360x576 or 360x480 for now */ - if (width == maxw && height == maxh) - width /= 2; } else { /* width must even because of the YUYV format height must be even because of interlacing */ @@ -2225,7 +2219,7 @@ static int em28xx_v4l2_close(struct file *filp) } /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ em28xx_uninit_isoc(dev); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 0a73e8bf0d6e..441df644ddbe 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -110,6 +110,7 @@ #define EM2820_BOARD_SILVERCREST_WEBCAM 71 #define EM2861_BOARD_GADMEI_UTV330PLUS 72 #define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 +#define EM2800_BOARD_VC211A 74 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -143,9 +144,6 @@ */ #define EM28XX_NUM_PACKETS 40 -/* default alternate; 0 means choose the best */ -#define EM28XX_PINOUT 0 - #define EM28XX_INTERLACED_DEFAULT 1 /* @@ -615,7 +613,6 @@ struct em28xx { struct em28xx_dvb *dvb; /* I2C keyboard data */ - struct i2c_board_info info; struct IR_i2c_init_data init_data; }; @@ -800,7 +797,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev) if (dev->board.is_webcam) return dev->sensor_xres; - if (dev->board.max_range_640_480) + if (dev->board.max_range_640_480 || dev->board.is_em2800) return 640; return 720; diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index fe2e490ebc52..609d65b0b10d 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -76,10 +76,11 @@ config USB_GSPCA_MR97310A module will be called gspca_mr97310a. config USB_GSPCA_OV519 - tristate "OV519 USB Camera Driver" + tristate "OV51x / OVFX2 / W996xCF USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the OV519 chip. + Say Y here if you want support for cameras based on one of these: + OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF To compile this driver as a module, choose M here: the module will be called gspca_ov519. @@ -103,6 +104,15 @@ config USB_GSPCA_PAC207 To compile this driver as a module, choose M here: the module will be called gspca_pac207. +config USB_GSPCA_PAC7302 + tristate "Pixart PAC7302 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the PAC7302 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_pac7302. + config USB_GSPCA_PAC7311 tristate "Pixart PAC7311 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -229,6 +239,15 @@ config USB_GSPCA_STK014 To compile this driver as a module, choose M here: the module will be called gspca_stk014. +config USB_GSPCA_STV0680 + tristate "STV0680 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the STV0680 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stv0680. + config USB_GSPCA_SUNPLUS tristate "SUNPLUS USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index b7420818037e..ff2c7279d82e 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o +obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o @@ -22,6 +23,7 @@ obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o @@ -37,6 +39,7 @@ gspca_mr97310a-objs := mr97310a.o gspca_ov519-objs := ov519.o gspca_ov534-objs := ov534.o gspca_pac207-objs := pac207.o +gspca_pac7302-objs := pac7302.o gspca_pac7311-objs := pac7311.o gspca_sn9c20x-objs := sn9c20x.o gspca_sonixb-objs := sonixb.o @@ -50,6 +53,7 @@ gspca_spca561-objs := spca561.o gspca_sq905-objs := sq905.o gspca_sq905c-objs := sq905c.o gspca_stk014-objs := stk014.o +gspca_stv0680-objs := stv0680.o gspca_sunplus-objs := sunplus.o gspca_t613-objs := t613.o gspca_tv8532-objs := tv8532.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index eca003566ae3..2f0b8d621e00 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -888,8 +888,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -897,16 +896,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); data += 2; len -= 2; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev*gspca_dev) diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index c1461e63647f..9de86419ae1e 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -752,8 +752,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) #undef LIMIT static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { int seqframe; @@ -767,14 +766,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data[2], data[3], data[4], data[5]); data += 30; /* don't change datalength as the chips provided it */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } if (len) { data += 8; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else { /* Drop Packet */ gspca_dev->last_packet_type = DISCARD_PACKET; } diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 480ec5c87d0e..5d90e7448579 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -82,7 +82,6 @@ static void dostream(struct work_struct *work) struct gspca_dev *gspca_dev = &dev->gspca_dev; struct urb *urb = gspca_dev->urb[0]; u8 *data = urb->transfer_buffer; - struct gspca_frame *frame; int ret = 0; int len; @@ -118,10 +117,6 @@ again: } if (!gspca_dev->present || !gspca_dev->streaming) goto out; - frame = gspca_get_i_frame(&dev->gspca_dev); - if (frame == NULL) - gspca_dev->last_packet_type = DISCARD_PACKET; - if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && data[len - 1] == 0xd9)) { @@ -132,21 +127,17 @@ again: * but there's nothing we can do. We also end * here if the the jpeg ends right at the end * of the frame. */ - if (frame) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - data, len); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, len); break; } /* got a partial image */ - if (frame) - gspca_frame_add(gspca_dev, - gspca_dev->last_packet_type - == LAST_PACKET - ? FIRST_PACKET : INTER_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, + gspca_dev->last_packet_type + == LAST_PACKET + ? FIRST_PACKET : INTER_PACKET, + data, len); } /* We must wait before trying reading the next diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c index 39f6261c1a0c..1355e526ee84 100644 --- a/drivers/media/video/gspca/gl860/gl860-mi1320.c +++ b/drivers/media/video/gspca/gl860/gl860-mi1320.c @@ -1,6 +1,5 @@ -/* @file gl860-mi1320.c - * @author Olivier LORIN from my logs - * @date 2009-08-27 +/* Subdriver for the GL860 chip with the MI1320 sensor + * Author Olivier LORIN from own logs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,49 +126,49 @@ static u8 dat_wbalBL[] = static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00}; -static u8 s000[] = +static u8 dat_common00[] = "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42" "\xd8\x04\x58\x00\x04\x02"; -static u8 s001[] = +static u8 dat_common01[] = "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d" "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0"; -static u8 s002[] = +static u8 dat_common02[] = "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e" "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00" "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff"; -static u8 s003[] = +static u8 dat_common03[] = "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda" "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c" "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c"; -static u8 s004[] = +static u8 dat_common04[] = "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43"; -static u8 s005[] = +static u8 dat_common05[] = "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68" "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82" "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b"; -static u8 s006[] = +static u8 dat_common06[] = "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06" "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4" "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f"; -static u8 s007[] = +static u8 dat_common07[] = "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72" "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03" "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea" "\xe1\xff\xf1\x00"; -static u8 s008[] = +static u8 dat_common08[] = "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7" "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06" "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a"; -static u8 s009[] = +static u8 dat_common09[] = "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03" "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa" "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14"; -static u8 s010[] = +static u8 dat_common10[] = "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00" "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f" "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01" "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10"; -static u8 s011[] = +static u8 dat_common11[] = "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10" "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00" "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81"; @@ -222,26 +221,26 @@ void mi1320_init_settings(struct gspca_dev *gspca_dev) static void common(struct gspca_dev *gspca_dev) { - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00); ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01); n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); } diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c index ffb09fed3e8c..80cb3f1b36f7 100644 --- a/drivers/media/video/gspca/gl860/gl860-mi2020.c +++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c @@ -1,7 +1,6 @@ -/* @file gl860-mi2020.c - * @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's +/* Subdriver for the GL860 chip with the MI2020 sensor + * Author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist. - * @date 2009-08-27 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +40,7 @@ static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; -static struct validx tbl_common_a[] = { +static struct validx tbl_common1[] = { {0x0000, 0x0000}, {1, 0xffff}, /* msleep(35); */ {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, @@ -49,7 +48,7 @@ static struct validx tbl_common_a[] = { {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000}, }; -static struct validx tbl_common_b[] = { +static struct validx tbl_common2[] = { {0x006a, 0x0007}, {35, 0xffff}, {0x00ef, 0x0006}, @@ -60,7 +59,7 @@ static struct validx tbl_common_b[] = { {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, }; -static struct idxdata tbl_common_c[] = { +static struct idxdata tbl_common3[] = { {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, {6, "\xff\xff\xff"}, /* 12 */ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, @@ -109,7 +108,7 @@ static struct idxdata tbl_common_c[] = { {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, }; -static struct idxdata tbl_common_d[] = { +static struct idxdata tbl_common4[] = { {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, @@ -118,7 +117,7 @@ static struct idxdata tbl_common_d[] = { {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"}, }; -static struct idxdata tbl_common_e[] = { +static struct idxdata tbl_common5[] = { {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, @@ -180,7 +179,7 @@ static struct validx tbl_init_at_startup[] = { {53, 0xffff}, }; -static struct idxdata tbl_init_post_alt_low_a[] = { +static struct idxdata tbl_init_post_alt_low1[] = { {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"}, @@ -189,7 +188,7 @@ static struct idxdata tbl_init_post_alt_low_a[] = { {0x33, "\x90\x00\x9b"}, }; -static struct idxdata tbl_init_post_alt_low_b[] = { +static struct idxdata tbl_init_post_alt_low2[] = { {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, @@ -197,7 +196,7 @@ static struct idxdata tbl_init_post_alt_low_b[] = { {2, "\xff\xff\xff"}, }; -static struct idxdata tbl_init_post_alt_low_c[] = { +static struct idxdata tbl_init_post_alt_low3[] = { {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, {2, "\xff\xff\xff"}, {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"}, @@ -221,7 +220,7 @@ static struct idxdata tbl_init_post_alt_low_c[] = { {1, "\xff\xff\xff"}, }; -static struct idxdata tbl_init_post_alt_low_d[] = { +static struct idxdata tbl_init_post_alt_low4[] = { {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, @@ -267,7 +266,7 @@ static struct idxdata tbl_init_post_alt_low_d[] = { {0x32, "\x6c\x14\x08"}, }; -static struct idxdata tbl_init_post_alt_big_a[] = { +static struct idxdata tbl_init_post_alt_big1[] = { {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, @@ -288,7 +287,7 @@ static struct idxdata tbl_init_post_alt_big_a[] = { {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, }; -static struct idxdata tbl_init_post_alt_big_b[] = { +static struct idxdata tbl_init_post_alt_big2[] = { {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, @@ -317,7 +316,7 @@ static struct idxdata tbl_init_post_alt_big_b[] = { {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, }; -static struct idxdata tbl_init_post_alt_big_c[] = { +static struct idxdata tbl_init_post_alt_big3[] = { {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, @@ -388,14 +387,14 @@ static void common(struct gspca_dev *gspca_dev) s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; if (_MI2020b_) { - fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a)); + fetch_validx(gspca_dev, tbl_common1, ARRAY_SIZE(tbl_common1)); } else { if (_MI2020_) ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL); else ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL); msleep(35); - fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b)); + fetch_validx(gspca_dev, tbl_common2, ARRAY_SIZE(tbl_common2)); } ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01"); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00"); @@ -403,13 +402,13 @@ static void common(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc"); if (reso == IMAGE_1600) msleep(2); /* 1600 */ - fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c)); + fetch_idxdata(gspca_dev, tbl_common3, ARRAY_SIZE(tbl_common3)); if (_MI2020b_ || _MI2020_) - fetch_idxdata(gspca_dev, tbl_common_d, - ARRAY_SIZE(tbl_common_d)); + fetch_idxdata(gspca_dev, tbl_common4, + ARRAY_SIZE(tbl_common4)); - fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e)); + fetch_idxdata(gspca_dev, tbl_common5, ARRAY_SIZE(tbl_common5)); if (_MI2020b_ || _MI2020_) { /* Different from fret */ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78"); @@ -525,15 +524,15 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) 12, dat_800); if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a, - ARRAY_SIZE(tbl_init_post_alt_low_a)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, + ARRAY_SIZE(tbl_init_post_alt_low1)); if (reso == IMAGE_800) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b, - ARRAY_SIZE(tbl_init_post_alt_low_b)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low2, + ARRAY_SIZE(tbl_init_post_alt_low2)); - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c, - ARRAY_SIZE(tbl_init_post_alt_low_c)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low3, + ARRAY_SIZE(tbl_init_post_alt_low3)); if (_MI2020b_) { ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); @@ -574,8 +573,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) msleep(5);/* " */ if (_MI2020c_) { - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d, - ARRAY_SIZE(tbl_init_post_alt_low_d)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low4, + ARRAY_SIZE(tbl_init_post_alt_low4)); } else { ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); msleep(14); /* 0xd8 */ @@ -644,8 +643,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) 3, "\x90\x04\xb0"); } - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a, - ARRAY_SIZE(tbl_init_post_alt_big_a)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big1, + ARRAY_SIZE(tbl_init_post_alt_big1)); if (reso == IMAGE_1600) msleep(13); /* 1600 */ @@ -708,8 +707,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) msleep(14); if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b, - ARRAY_SIZE(tbl_init_post_alt_big_b)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big2, + ARRAY_SIZE(tbl_init_post_alt_big2)); /* flip/mirror */ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); @@ -738,8 +737,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) sd->nbIm = 0; if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c, - ARRAY_SIZE(tbl_init_post_alt_big_c)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big3, + ARRAY_SIZE(tbl_init_post_alt_big3)); } sd->vold.mirror = mirror; diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/video/gspca/gl860/gl860-ov2640.c index 14b9c373f9f7..768cac5cd72b 100644 --- a/drivers/media/video/gspca/gl860/gl860-ov2640.c +++ b/drivers/media/video/gspca/gl860/gl860-ov2640.c @@ -1,6 +1,5 @@ -/* @file gl860-ov2640.c - * @author Olivier LORIN, from Malmostoso's logs - * @date 2009-08-27 +/* Subdriver for the GL860 chip with the OV2640 sensor + * Author Olivier LORIN, from Malmostoso's logs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,8 +20,12 @@ #include "gl860.h" static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01"; -static u8 dat_init2[] = {0x61}; /* expected */ -static u8 dat_init3[] = {0x51}; /* expected */ + +static u8 c61[] = {0x61}; /* expected */ +static u8 c51[] = {0x51}; /* expected */ +static u8 c50[] = {0x50}; /* expected */ +static u8 c28[] = {0x28}; /* expected */ +static u8 ca8[] = {0xa8}; /* expected */ static u8 dat_post[] = "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01"; @@ -32,10 +35,6 @@ static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21"; static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01"; static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41"; -static u8 c50[] = {0x50}; /* expected */ -static u8 c28[] = {0x28}; /* expected */ -static u8 ca8[] = {0xa8}; /* expected */ - static struct validx tbl_init_at_startup[] = { {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, @@ -92,7 +91,7 @@ static struct validx tbl_common[] = { {0x6000, 0x0010}, }; -static struct validx tbl_sensor_settings_common_a[] = { +static struct validx tbl_sensor_settings_common1[] = { {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000}, @@ -104,40 +103,10 @@ static struct validx tbl_sensor_settings_common_a[] = { {0x0040, 0x0000}, }; -static struct validx tbl_sensor_settings_common_b[] = { +static struct validx tbl_sensor_settings_common2[] = { {0x6001, 0x00ff}, {0x6038, 0x000c}, {10, 0xffff}, {0x6000, 0x0011}, - /* backlight=31/64 */ - {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, - /* bright=0/256 */ - {0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d}, - /* wbal=64/128 */ - {0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d}, - /* cntr=0/256 */ - {0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d}, - /* sat=128/256 */ - {0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d}, - /* sharpness=0/32 */ - {0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093}, - /* hue=0/256 */ - {0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d}, - /* gam=32/64 */ - {0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d}, - /* image right up */ - {0xffff, 0xffff}, - {15, 0xffff}, - {0x6001, 0x00ff}, {0x6000, 0x8004}, - {0xffff, 0xffff}, - {0x60a8, 0x0004}, - {15, 0xffff}, - {0x6001, 0x00ff}, {0x6000, 0x8004}, - {0xffff, 0xffff}, - {0x60f8, 0x0004}, - /* image right up */ - {0xffff, 0xffff}, - /* backlight=31/64 */ - {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, }; static struct validx tbl_640[] = { @@ -166,7 +135,7 @@ static struct validx tbl_800[] = { {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018}, }; -static struct validx tbl_big_a[] = { +static struct validx tbl_big1[] = { {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018}, @@ -176,14 +145,14 @@ static struct validx tbl_big_a[] = { {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, }; -static struct validx tbl_big_b[] = { +static struct validx tbl_big2[] = { {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3}, {0x6000, 0x008e}, }; -static struct validx tbl_big_c[] = { +static struct validx tbl_big3[] = { {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd}, {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7}, @@ -223,17 +192,19 @@ void ov2640_init_settings(struct gspca_dev *gspca_dev) sd->vcur.hue = 0; sd->vcur.saturation = 128; sd->vcur.whitebal = 64; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; sd->vmax.backlight = 64; sd->vmax.brightness = 255; sd->vmax.sharpness = 31; sd->vmax.contrast = 255; sd->vmax.gamma = 64; - sd->vmax.hue = 255 + 1; + sd->vmax.hue = 254 + 2; sd->vmax.saturation = 255; sd->vmax.whitebal = 128; - sd->vmax.mirror = 0; - sd->vmax.flip = 0; + sd->vmax.mirror = 1; + sd->vmax.flip = 1; sd->vmax.AC50Hz = 0; sd->dev_camera_settings = ov2640_camera_settings; @@ -259,11 +230,11 @@ static int ov2640_init_at_startup(struct gspca_dev *gspca_dev) common(gspca_dev); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61); ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51); ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL); /* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ @@ -275,6 +246,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + sd->mirrorMask = 0; + sd->vold.backlight = -1; sd->vold.brightness = -1; sd->vold.sharpness = -1; @@ -283,6 +256,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) sd->vold.gamma = -1; sd->vold.hue = -1; sd->vold.whitebal = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; ov2640_init_post_alt(gspca_dev); @@ -292,16 +267,16 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) { s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a, - ARRAY_SIZE(tbl_sensor_settings_common_a)); + n = fetch_validx(gspca_dev, tbl_sensor_settings_common1, + ARRAY_SIZE(tbl_sensor_settings_common1)); ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post); common(gspca_dev); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a, - ARRAY_SIZE(tbl_sensor_settings_common_a), n); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1, + ARRAY_SIZE(tbl_sensor_settings_common1), n); switch (reso) { case IMAGE_640: @@ -316,18 +291,18 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) case IMAGE_1600: case IMAGE_1280: - n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a)); + n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1)); if (reso == IMAGE_1280) { - n = fetch_validx(gspca_dev, tbl_big_b, - ARRAY_SIZE(tbl_big_b)); + n = fetch_validx(gspca_dev, tbl_big2, + ARRAY_SIZE(tbl_big2)); } else { ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL); } - n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c)); + n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3)); if (reso == IMAGE_1280) { ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); @@ -343,20 +318,8 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) break; } - n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b)); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); + n = fetch_validx(gspca_dev, tbl_sensor_settings_common2, + ARRAY_SIZE(tbl_sensor_settings_common2)); ov2640_camera_settings(gspca_dev); @@ -393,18 +356,20 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) s32 sat = sd->vcur.saturation; s32 hue = sd->vcur.hue; s32 wbal = sd->vcur.whitebal; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) == 0); if (backlight != sd->vold.backlight) { + /* No sd->vold.backlight=backlight; (to be done again later) */ if (backlight < 0 || backlight > sd->vmax.backlight) backlight = 0; ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, 0, NULL); - /* No sd->vold.backlight=backlight; (to be done again later) */ } if (bright != sd->vold.brightness) { @@ -466,7 +431,7 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d, 0, NULL); - if (hue >= sd->vmax.hue) + if (hue >= 255) sd->swapRB = 1; else sd->swapRB = 0; @@ -482,14 +447,33 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL); } + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + mirror = 0x80 * mirror; + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); + ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL); + + flip = 0x50 * flip + mirror; + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); + ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); + } + if (backlight != sd->vold.backlight) { sd->vold.backlight = backlight; ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, 0, NULL); } diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c index eda3346f939c..d412694c50af 100644 --- a/drivers/media/video/gspca/gl860/gl860-ov9655.c +++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c @@ -1,7 +1,6 @@ -/* @file gl860-ov9655.c - * @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt +/* Subdriver for the GL860 chip with the OV9655 sensor + * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt * on dsd's weblog - * @date 2009-08-27 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -104,14 +103,14 @@ static u8 *tbl_800[] = { }; static u8 c04[] = {0x04}; -static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; -static u8 dat_post_2[] = "\x10\x10\xc1\x02"; -static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; -static u8 dat_post_4[] = "\x10\x02\xc1\x06"; -static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; -static u8 dat_post_6[] = "\x10\x10\xc1\x05"; -static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; -static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; +static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; +static u8 dat_post2[] = "\x10\x10\xc1\x02"; +static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; +static u8 dat_post4[] = "\x10\x02\xc1\x06"; +static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; +static u8 dat_post6[] = "\x10\x10\xc1\x05"; +static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; +static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; static struct validx tbl_init_post_alt[] = { {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff}, @@ -212,7 +211,7 @@ static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev) static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) { s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ s32 i; u8 **tbl; @@ -243,7 +242,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); @@ -259,7 +258,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); @@ -270,18 +269,18 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8); ov9655_camera_settings(gspca_dev); diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index 6ef59ac7f502..a695e0ae13c2 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -1,9 +1,7 @@ -/* @file gl860.c - * @date 2009-08-27 +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver core * - * Genesys Logic webcam with gl860 subdrivers - * - * Driver by Olivier Lorin <o.lorin@laposte.net> + * 2009/09/24 Olivier Lorin <o.lorin@laposte.net> * GSPCA by Jean-Francois Moine <http://moinejf.free.fr> * Thanks BUGabundo and Malmostoso for your amazing help! * @@ -23,8 +21,8 @@ #include "gspca.h" #include "gl860.h" -MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>"); -MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver"); +MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>"); +MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver"); MODULE_LICENSE("GPL"); /*======================== static function declarations ====================*/ @@ -38,7 +36,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev); static int sd_start(struct gspca_dev *gspca_dev); static void sd_stop0(struct gspca_dev *gspca_dev); static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, u8 *data, s32 len); + u8 *data, int len); static void sd_callback(struct gspca_dev *gspca_dev); static int gl860_guess_sensor(struct gspca_dev *gspca_dev, @@ -53,7 +51,7 @@ MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); static char sensor[7]; module_param_string(sensor, sensor, sizeof(sensor), 0644); MODULE_PARM_DESC(sensor, - " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')"); + " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')"); /*============================ webcam controls =============================*/ @@ -156,7 +154,7 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev) SET_MY_CTRL(V4L2_CID_VFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz) + V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz) return nCtrls; } @@ -435,7 +433,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) /* This function is called when an image is being received */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, u8 *data, s32 len) + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; static s32 nSkipped; @@ -447,11 +445,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Test only against 0202h, so endianess does not matter */ switch (*(s16 *) data) { case 0x0202: /* End of frame, start a new one */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); nSkipped = 0; if (sd->nbIm >= 0 && sd->nbIm < 10) sd->nbIm++; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); break; default: @@ -466,7 +464,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, nSkipped = nToSkip + 1; } gspca_frame_add(gspca_dev, - INTER_PACKET, frame, data, len); + INTER_PACKET, data, len); } break; } @@ -702,6 +700,7 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); msleep(56); + PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX"); nOV = 0; for (ntry = 0; ntry < 4; ntry++) { ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); @@ -711,14 +710,14 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); msleep(10); ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); - PDEBUG(D_PROBE, "1st probe=%02x", probe); + PDEBUG(D_PROBE, "probe=0x%02x", probe); if (probe == 0xff) nOV++; } if (nOV) { - PDEBUG(D_PROBE, "0xff -> sensor OVXXXX"); - PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655"); + PDEBUG(D_PROBE, "0xff -> OVXXXX"); + PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655"); nb26 = nb96 = 0; for (ntry = 0; ntry < 4; ntry++) { @@ -728,40 +727,38 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, 0, NULL); msleep(10); + /* Wait for 26(OV2640) or 96(OV9655) */ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, 1, &probe); - PDEBUG(D_PROBE, "2nd probe=%02x", probe); - if (probe == 0x00) - nb26++; if (probe == 0x26 || probe == 0x40) { + PDEBUG(D_PROBE, + "probe=0x%02x -> OV2640", + probe); sd->sensor = ID_OV2640; nb26 += 4; break; } if (probe == 0x96 || probe == 0x55) { + PDEBUG(D_PROBE, + "probe=0x%02x -> OV9655", + probe); sd->sensor = ID_OV9655; nb96 += 4; break; } + PDEBUG(D_PROBE, "probe=0x%02x", probe); + if (probe == 0x00) + nb26++; if (probe == 0xff) nb96++; msleep(3); } - if (nb26 < 4 && nb96 < 4) { - PDEBUG(D_PROBE, "No relevant answer "); - PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655"); - PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640"); - PDEBUG(D_PROBE, - "To force a sensor, add that line to " - "/etc/modprobe.d/options.conf:"); - PDEBUG(D_PROBE, "options gspca_gl860 " - "sensor=\"OV2640\" or \"OV9655\""); + if (nb26 < 4 && nb96 < 4) return -1; - } - } else { /* probe = 0 */ - PDEBUG(D_PROBE, "No 0xff -> sensor MI2020"); + } else { + PDEBUG(D_PROBE, "Not any 0xff -> MI2020"); sd->sensor = ID_MI2020; } } diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h index cef4e24c1e61..305061ff8387 100644 --- a/drivers/media/video/gspca/gl860/gl860.h +++ b/drivers/media/video/gspca/gl860/gl860.h @@ -1,6 +1,7 @@ -/* @file gl860.h - * @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN - * @date 2009-08-27 +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver declarations + * + * 2009/10/14 Olivier LORIN <o.lorin@laposte.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 23d3fb776918..4076f8e5a6fc 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 7, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 8, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -74,7 +74,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) #define PDEBUG_MODE(txt, pixfmt, w, h) #endif -/* specific memory types - !! should different from V4L2_MEMORY_xxx */ +/* specific memory types - !! should be different from V4L2_MEMORY_xxx */ #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ #define GSPCA_MEMORY_READ 7 @@ -126,7 +126,6 @@ EXPORT_SYMBOL(gspca_get_i_frame); static void fill_frame(struct gspca_dev *gspca_dev, struct urb *urb) { - struct gspca_frame *frame; u8 *data; /* address of data in the iso message */ int i, len, st; cam_pkt_op pkt_scan; @@ -135,21 +134,16 @@ static void fill_frame(struct gspca_dev *gspca_dev, if (urb->status == -ESHUTDOWN) return; /* disconnection */ #ifdef CONFIG_PM - if (!gspca_dev->frozen) + if (gspca_dev->frozen) + return; #endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - return; + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + urb->status = 0; + goto resubmit; } pkt_scan = gspca_dev->sd_desc->pkt_scan; for (i = 0; i < urb->number_of_packets; i++) { - /* check the availability of the frame buffer */ - frame = gspca_get_i_frame(gspca_dev); - if (!frame) { - gspca_dev->last_packet_type = DISCARD_PACKET; - break; - } - /* check the packet status and length */ len = urb->iso_frame_desc[i].actual_length; if (len == 0) { @@ -171,9 +165,10 @@ static void fill_frame(struct gspca_dev *gspca_dev, i, urb->iso_frame_desc[i].offset, len); data = (u8 *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; - pkt_scan(gspca_dev, frame, data, len); + pkt_scan(gspca_dev, data, len); } +resubmit: /* resubmit the URB */ st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) @@ -201,7 +196,6 @@ static void isoc_irq(struct urb *urb) static void bulk_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; - struct gspca_frame *frame; int st; PDEBUG(D_PACK, "bulk irq"); @@ -212,29 +206,22 @@ static void bulk_irq(struct urb *urb) break; case -ESHUTDOWN: return; /* disconnection */ - case -ECONNRESET: - urb->status = 0; - break; default: #ifdef CONFIG_PM - if (!gspca_dev->frozen) + if (gspca_dev->frozen) + return; #endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - return; + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + urb->status = 0; + goto resubmit; } - /* check the availability of the frame buffer */ - frame = gspca_get_i_frame(gspca_dev); - if (!frame) { - gspca_dev->last_packet_type = DISCARD_PACKET; - } else { - PDEBUG(D_PACK, "packet l:%d", urb->actual_length); - gspca_dev->sd_desc->pkt_scan(gspca_dev, - frame, - urb->transfer_buffer, - urb->actual_length); - } + PDEBUG(D_PACK, "packet l:%d", urb->actual_length); + gspca_dev->sd_desc->pkt_scan(gspca_dev, + urb->transfer_buffer, + urb->actual_length); +resubmit: /* resubmit the URB */ if (gspca_dev->cam.bulk_nurbs != 0) { st = usb_submit_urb(urb, GFP_ATOMIC); @@ -255,24 +242,27 @@ static void bulk_irq(struct urb *urb) * DISCARD_PACKET invalidates the whole frame. * On LAST_PACKET, a new frame is returned. */ -struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, - enum gspca_packet_type packet_type, - struct gspca_frame *frame, - const __u8 *data, - int len) +void gspca_frame_add(struct gspca_dev *gspca_dev, + enum gspca_packet_type packet_type, + const u8 *data, + int len) { + struct gspca_frame *frame; int i, j; PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); + /* check the availability of the frame buffer */ + frame = gspca_dev->cur_frame; + if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) + != V4L2_BUF_FLAG_QUEUED) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + /* when start of a new frame, if the current frame buffer * is not queued, discard the whole frame */ if (packet_type == FIRST_PACKET) { - if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) - != V4L2_BUF_FLAG_QUEUED) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return frame; - } frame->data_end = frame->data; jiffies_to_timeval(get_jiffies_64(), &frame->v4l2_buf.timestamp); @@ -280,7 +270,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, } else if (gspca_dev->last_packet_type == DISCARD_PACKET) { if (packet_type == LAST_PACKET) gspca_dev->last_packet_type = packet_type; - return frame; + return; } /* append the packet to the frame buffer */ @@ -312,9 +302,9 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, i, gspca_dev->fr_o); j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; + gspca_dev->cur_frame = &gspca_dev->frame[j]; } - return frame; + return; } EXPORT_SYMBOL(gspca_frame_add); @@ -395,6 +385,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, frame->v4l2_buf.m.offset = i * frsz; } gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; + gspca_dev->cur_frame = &gspca_dev->frame[0]; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; return 0; @@ -475,10 +466,18 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK : USB_ENDPOINT_XFER_ISOC; i = gspca_dev->alt; /* previous alt setting */ - while (--i >= 0) { - ep = alt_xfer(&intf->altsetting[i], xfer); - if (ep) - break; + if (gspca_dev->cam.reverse_alts) { + while (++i < gspca_dev->nbalt) { + ep = alt_xfer(&intf->altsetting[i], xfer); + if (ep) + break; + } + } else { + while (--i >= 0) { + ep = alt_xfer(&intf->altsetting[i], xfer); + if (ep) + break; + } } if (ep == NULL) { err("no transfer endpoint found"); @@ -599,7 +598,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* set the higher alternate setting and * loop until urb submit succeeds */ - gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->cam.reverse_alts) + gspca_dev->alt = 0; + else + gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->sd_desc->isoc_init) { ret = gspca_dev->sd_desc->isoc_init(gspca_dev); if (ret < 0) @@ -641,15 +644,19 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) } if (ret >= 0) break; - PDEBUG(D_ERR|D_STREAM, - "usb_submit_urb alt %d err %d", gspca_dev->alt, ret); gspca_dev->streaming = 0; destroy_urbs(gspca_dev); - if (ret != -ENOSPC) + if (ret != -ENOSPC) { + PDEBUG(D_ERR|D_STREAM, + "usb_submit_urb alt %d err %d", + gspca_dev->alt, ret); goto out; + } /* the bandwidth is not wide enough * negociate or try a lower alternate setting */ + PDEBUG(D_ERR|D_STREAM, + "bandwidth not wide enough - trying again"); msleep(20); /* wait for kill complete */ if (gspca_dev->sd_desc->isoc_nego) { ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); @@ -980,7 +987,7 @@ static void gspca_release(struct video_device *vfd) { struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); - PDEBUG(D_STREAM, "device released"); + PDEBUG(D_PROBE, "/dev/video%d released", gspca_dev->vdev.num); kfree(gspca_dev->usb_buf); kfree(gspca_dev); @@ -991,7 +998,7 @@ static int dev_open(struct file *file) struct gspca_dev *gspca_dev; int ret; - PDEBUG(D_STREAM, "%s open", current->comm); + PDEBUG(D_STREAM, "[%s] open", current->comm); gspca_dev = (struct gspca_dev *) video_devdata(file); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; @@ -1037,7 +1044,7 @@ static int dev_close(struct file *file) { struct gspca_dev *gspca_dev = file->private_data; - PDEBUG(D_STREAM, "%s close", current->comm); + PDEBUG(D_STREAM, "[%s] close", current->comm); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; gspca_dev->users--; @@ -1138,10 +1145,13 @@ static int vidioc_queryctrl(struct file *file, void *priv, } } else { ctrls = get_ctrl(gspca_dev, id); + i = ctrls - gspca_dev->sd_desc->ctrls; } if (ctrls == NULL) return -EINVAL; memcpy(q_ctrl, ctrls, sizeof *q_ctrl); + if (gspca_dev->ctrl_inac & (1 << i)) + q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return 0; } @@ -1603,7 +1613,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) size -= PAGE_SIZE; } - vma->vm_ops = &gspca_vm_ops; + vma->vm_ops = (struct vm_operations_struct *) &gspca_vm_ops; vma->vm_private_data = frame; gspca_vm_open(vma); ret = 0; @@ -2001,11 +2011,15 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); /* we don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) + if (dev->descriptor.bNumConfigurations != 1) { + PDEBUG(D_ERR, "Too many config"); return -ENODEV; + } interface = &intf->cur_altsetting->desc; - if (interface->bInterfaceNumber > 0) + if (interface->bInterfaceNumber > 0) { + PDEBUG(D_ERR, "intf != 0"); return -ENODEV; + } /* create the device */ if (dev_size < sizeof *gspca_dev) @@ -2059,7 +2073,7 @@ int gspca_dev_probe(struct usb_interface *intf, } usb_set_intfdata(intf, gspca_dev); - PDEBUG(D_PROBE, "probe ok"); + PDEBUG(D_PROBE, "/dev/video%d created", gspca_dev->vdev.num); return 0; out: kfree(gspca_dev->usb_buf); @@ -2078,6 +2092,7 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + PDEBUG(D_PROBE, "/dev/video%d disconnect", gspca_dev->vdev.num); mutex_lock(&gspca_dev->usb_lock); gspca_dev->present = 0; @@ -2096,7 +2111,7 @@ void gspca_disconnect(struct usb_interface *intf) /* (this will call gspca_release() immediatly or on last close) */ video_unregister_device(&gspca_dev->vdev); - PDEBUG(D_PROBE, "disconnect complete"); +/* PDEBUG(D_PROBE, "disconnect complete"); */ } EXPORT_SYMBOL(gspca_disconnect); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 70b1fd830876..181617355ec3 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -58,6 +58,7 @@ struct cam { u8 npkt; /* number of packets in an ISOC message * 0 is the default value: 32 packets */ u32 input_flags; /* value for ENUM_INPUT status flags */ + char reverse_alts; /* Alt settings are in high to low order */ }; struct gspca_dev; @@ -78,8 +79,7 @@ typedef int (*cam_streamparm_op) (struct gspca_dev *, typedef int (*cam_qmnu_op) (struct gspca_dev *, struct v4l2_querymenu *); typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len); struct ctrl { @@ -142,6 +142,7 @@ struct gspca_dev { struct cam cam; /* device information */ const struct sd_desc *sd_desc; /* subdriver description */ unsigned ctrl_dis; /* disabled controls (bit map) */ + unsigned ctrl_inac; /* inactive controls (bit map) */ #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ @@ -149,6 +150,7 @@ struct gspca_dev { __u8 *frbuf; /* buffer for nframes */ struct gspca_frame frame[GSPCA_MAX_FRAMES]; + struct gspca_frame *cur_frame; /* frame beeing filled */ __u32 frsz; /* frame size */ char nframes; /* number of frames */ char fr_i; /* frame being filled */ @@ -189,11 +191,10 @@ int gspca_dev_probe(struct usb_interface *intf, int dev_size, struct module *module); void gspca_disconnect(struct usb_interface *intf); -struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, - enum gspca_packet_type packet_type, - struct gspca_frame *frame, - const __u8 *data, - int len); +void gspca_frame_add(struct gspca_dev *gspca_dev, + enum gspca_packet_type packet_type, + const u8 *data, + int len); struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev); #ifdef CONFIG_PM int gspca_suspend(struct usb_interface *intf, pm_message_t message); diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index a11c97ebeb0f..2019b04f9235 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -181,11 +181,9 @@ static void jlj_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int blocks_left; /* 0x200-sized blocks remaining in current frame. */ int size_in_blocks; int act_len; - int discarding = 0; /* true if we failed to get space for frame. */ int packet_type; int ret; u8 *buffer; @@ -196,15 +194,6 @@ static void jlj_dostream(struct work_struct *work) goto quit_stream; } while (gspca_dev->present && gspca_dev->streaming) { - if (!gspca_dev->present) - goto quit_stream; - /* Start a new frame, and add the JPEG header, first thing */ - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - dev->jpeg_hdr, JPEG_HDR_SZ); - else - discarding = 1; /* * Now request data block 0. Line 0 reports the size * to download, in blocks of size 0x200, and also tells the @@ -222,14 +211,15 @@ static void jlj_dostream(struct work_struct *work) size_in_blocks = buffer[0x0a]; blocks_left = buffer[0x0a] - 1; PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left); - packet_type = INTER_PACKET; - if (frame && !discarding) - /* Toss line 0 of data block 0, keep the rest. */ - gspca_frame_add(gspca_dev, packet_type, - frame, buffer + FRAME_HEADER_LEN, + + /* Start a new frame, and add the JPEG header, first thing */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + dev->jpeg_hdr, JPEG_HDR_SZ); + /* Toss line 0 of data block 0, keep the rest. */ + gspca_frame_add(gspca_dev, INTER_PACKET, + buffer + FRAME_HEADER_LEN, JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); - else - discarding = 1; + while (blocks_left > 0) { if (!gspca_dev->present) goto quit_stream; @@ -246,12 +236,8 @@ static void jlj_dostream(struct work_struct *work) packet_type = LAST_PACKET; else packet_type = INTER_PACKET; - if (frame && !discarding) - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, - JEILINJ_MAX_TRANSFER); - else - discarding = 1; + gspca_frame_add(gspca_dev, packet_type, + buffer, JEILINJ_MAX_TRANSFER); } } quit_stream: diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index 7f1e5415850b..844fc1d886d1 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -274,8 +274,7 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev) } static void m5602_urb_complete(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, int len) + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; @@ -295,19 +294,27 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, len -= 6; /* Complete the last frame (if any) */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); sd->frame_count++; /* Create a new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); PDEBUG(D_FRAM, "Starting new frame %d", sd->frame_count); } else { - int cur_frame_len = frame->data_end - frame->data; + struct gspca_frame *frame; + int cur_frame_len; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + cur_frame_len = frame->data_end - frame->data; /* Remove urb header */ data += 4; len -= 4; @@ -316,12 +323,12 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes", sd->frame_count, len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else if (frame->v4l2_buf.length - cur_frame_len > 0) { /* Add the remaining data up to frame size */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, - frame->v4l2_buf.length - cur_frame_len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, + frame->v4l2_buf.length - cur_frame_len); } } } diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index de769caf013d..9cf8d68c71bf 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -325,8 +325,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -348,11 +347,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, || data[5 + p] == 0x67) { PDEBUG(D_PACK, "sof offset: %d len: %d", p, len); - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, p); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, p); /* put the JPEG header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); data += p + 16; len -= p + 16; @@ -360,7 +359,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index f8328b9efae5..126d968dd9e0 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -1,23 +1,30 @@ /* * Mars MR97310A library * + * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com> * * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ * and for the routines for detecting and classifying these various cameras, + * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> * + * Support for the control settings for the CIF cameras is + * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com> and + * Thomas Kaiser <thomas@kaiser-linux.li> + * + * Support for the control settings for the VGA cameras is * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> * - * Acknowledgements: + * Several previously unsupported cameras are owned and have been tested by + * Hans de Goede <hdgoede@redhat.com> and + * Thomas Kaiser <thomas@kaiser-linux.li> and + * Theodore Kilgore <kilgota@auburn.edu> and + * Edmond Rodriguez <erodrig_97@yahoo.com> and + * Aurelien Jacobs <aurel@gnuage.org> * * The MR97311A support in gspca/mars.c has been helpful in understanding some * of the registers in these cameras. * - * Hans de Goede <hdgoede@redhat.com> and - * Thomas Kaiser <thomas@kaiser-linux.li> - * have assisted with their experience. Each of them has also helped by - * testing a previously unsupported camera. - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -40,11 +47,9 @@ #define CAM_TYPE_CIF 0 #define CAM_TYPE_VGA 1 -#define MR97310A_BRIGHTNESS_MIN -254 -#define MR97310A_BRIGHTNESS_MAX 255 #define MR97310A_BRIGHTNESS_DEFAULT 0 -#define MR97310A_EXPOSURE_MIN 300 +#define MR97310A_EXPOSURE_MIN 0 #define MR97310A_EXPOSURE_MAX 4095 #define MR97310A_EXPOSURE_DEFAULT 1000 @@ -52,6 +57,10 @@ #define MR97310A_GAIN_MAX 31 #define MR97310A_GAIN_DEFAULT 25 +#define MR97310A_MIN_CLOCKDIV_MIN 3 +#define MR97310A_MIN_CLOCKDIV_MAX 8 +#define MR97310A_MIN_CLOCKDIV_DEFAULT 3 + MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>," "Theodore Kilgore <kilgota@auburn.edu>"); MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); @@ -69,10 +78,12 @@ struct sd { u8 cam_type; /* 0 is CIF and 1 is VGA */ u8 sensor_type; /* We use 0 and 1 here, too. */ u8 do_lcd_stop; + u8 adj_colors; int brightness; u16 exposure; u8 gain; + u8 min_clockdiv; }; struct sensor_w_data { @@ -82,26 +93,31 @@ struct sensor_w_data { int len; }; +static void sd_stopN(struct gspca_dev *gspca_dev); static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val); static void setbrightness(struct gspca_dev *gspca_dev); static void setexposure(struct gspca_dev *gspca_dev); static void setgain(struct gspca_dev *gspca_dev); /* V4L2 controls supported by the driver */ static struct ctrl sd_ctrls[] = { +/* Separate brightness control description for Argus QuickClix as it has + different limits from the other mr97310a cameras */ { -#define BRIGHTNESS_IDX 0 +#define NORM_BRIGHTNESS_IDX 0 { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", - .minimum = MR97310A_BRIGHTNESS_MIN, - .maximum = MR97310A_BRIGHTNESS_MAX, + .minimum = -254, + .maximum = 255, .step = 1, .default_value = MR97310A_BRIGHTNESS_DEFAULT, .flags = 0, @@ -110,7 +126,22 @@ static struct ctrl sd_ctrls[] = { .get = sd_getbrightness, }, { -#define EXPOSURE_IDX 1 +#define ARGUS_QC_BRIGHTNESS_IDX 1 + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = MR97310A_BRIGHTNESS_DEFAULT, + .flags = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { +#define EXPOSURE_IDX 2 { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -125,7 +156,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getexposure, }, { -#define GAIN_IDX 2 +#define GAIN_IDX 3 { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -139,6 +170,21 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgain, .get = sd_getgain, }, + { +#define MIN_CLOCKDIV_IDX 4 + { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Minimum Clock Divider", + .minimum = MR97310A_MIN_CLOCKDIV_MIN, + .maximum = MR97310A_MIN_CLOCKDIV_MAX, + .step = 1, + .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT, + .flags = 0, + }, + .set = sd_setmin_clockdiv, + .get = sd_getmin_clockdiv, + }, }; static const struct v4l2_pix_format vga_mode[] = { @@ -230,12 +276,17 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) int rc; buf = data; - rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + if (sd->cam_type == CAM_TYPE_CIF) { + rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + confirm_reg = sd->sensor_type ? 0x13 : 0x11; + } else { + rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1); + confirm_reg = 0x11; + } if (rc < 0) return rc; buf = 0x01; - confirm_reg = sd->sensor_type ? 0x13 : 0x11; rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); if (rc < 0) return rc; @@ -243,18 +294,26 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) return 0; } -static int cam_get_response16(struct gspca_dev *gspca_dev) +static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose) { - __u8 *data = gspca_dev->usb_buf; int err_code; - data[0] = 0x21; + gspca_dev->usb_buf[0] = reg; err_code = mr_write(gspca_dev, 1); if (err_code < 0) return err_code; err_code = mr_read(gspca_dev, 16); - return err_code; + if (err_code < 0) + return err_code; + + if (verbose) + PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg, + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2]); + + return 0; } static int zero_the_pointer(struct gspca_dev *gspca_dev) @@ -264,7 +323,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) u8 status = 0; int tries = 0; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -275,7 +334,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -285,7 +344,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -295,7 +354,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -306,7 +365,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) return err_code; while (status != 0x0a && tries < 256) { - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); status = data[0]; tries++; if (err_code < 0) @@ -323,7 +382,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); status = data[0]; tries++; if (err_code < 0) @@ -342,89 +401,202 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) return 0; } -static u8 get_sensor_id(struct gspca_dev *gspca_dev) +static int stream_start(struct gspca_dev *gspca_dev) { - int err_code; - - gspca_dev->usb_buf[0] = 0x1e; - err_code = mr_write(gspca_dev, 1); - if (err_code < 0) - return err_code; + gspca_dev->usb_buf[0] = 0x01; + gspca_dev->usb_buf[1] = 0x01; + return mr_write(gspca_dev, 2); +} - err_code = mr_read(gspca_dev, 16); - if (err_code < 0) - return err_code; +static void stream_stop(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x01; + gspca_dev->usb_buf[1] = 0x00; + if (mr_write(gspca_dev, 2) < 0) + PDEBUG(D_ERR, "Stream Stop failed"); +} - PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[0]); +static void lcd_stop(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x19; + gspca_dev->usb_buf[1] = 0x54; + if (mr_write(gspca_dev, 2) < 0) + PDEBUG(D_ERR, "LCD Stop failed"); +} - return gspca_dev->usb_buf[0]; +static int isoc_enable(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x00; + gspca_dev->usb_buf[1] = 0x4d; /* ISOC transfering enable... */ + return mr_write(gspca_dev, 2); } -/* this function is called at probe time */ +/* This function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u8 *data = gspca_dev->usb_buf; int err_code; cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + sd->do_lcd_stop = 0; + + /* Several of the supported CIF cameras share the same USB ID but + * require different initializations and different control settings. + * The same is true of the VGA cameras. Therefore, we are forced + * to start the initialization process in order to determine which + * camera is present. Some of the supported cameras require the + * memory pointer to be set to 0 as the very first item of business + * or else they will not stream. So we do that immediately. + */ + err_code = zero_the_pointer(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = stream_start(gspca_dev); + if (err_code < 0) + return err_code; - if (id->idProduct == 0x010e) { + if (id->idProduct == 0x0110 || id->idProduct == 0x010e) { sd->cam_type = CAM_TYPE_CIF; cam->nmodes--; - - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); + err_code = cam_get_response16(gspca_dev, 0x06, 1); if (err_code < 0) return err_code; - - msleep(200); - data[0] = get_sensor_id(gspca_dev); /* - * Known CIF cameras. If you have another to report, please do + * All but one of the known CIF cameras share the same USB ID, + * but two different init routines are in use, and the control + * settings are different, too. We need to detect which camera + * of the two known varieties is connected! * - * Name byte just read sd->sensor_type - * reported by - * Sakar Spy-shot 0x28 T. Kilgore 0 - * Innovage 0xf5 (unstable) T. Kilgore 0 - * Vivitar Mini 0x53 H. De Goede 0 - * Vivitar Mini 0x04 / 0x24 E. Rodriguez 0 - * Vivitar Mini 0x08 T. Kilgore 1 - * Elta-Media 8212dc 0x23 T. Kaiser 1 - * Philips dig. keych. 0x37 T. Kilgore 1 + * A list of known CIF cameras follows. They all report either + * 0002 for type 0 or 0003 for type 1. + * If you have another to report, please do + * + * Name sd->sensor_type reported by + * + * Sakar Spy-shot 0 T. Kilgore + * Innovage 0 T. Kilgore + * Vivitar Mini 0 H. De Goede + * Vivitar Mini 0 E. Rodriguez + * Vivitar Mini 1 T. Kilgore + * Elta-Media 8212dc 1 T. Kaiser + * Philips dig. keych. 1 T. Kilgore + * Trust Spyc@m 100 1 A. Jacobs */ - if ((data[0] & 0x78) == 8 || - ((data[0] & 0x2) == 0x2 && data[0] != 0x53)) - sd->sensor_type = 1; - else + switch (gspca_dev->usb_buf[1]) { + case 2: sd->sensor_type = 0; - + break; + case 3: + sd->sensor_type = 1; + break; + default: + PDEBUG(D_ERR, "Unknown CIF Sensor id : %02x", + gspca_dev->usb_buf[1]); + return -ENODEV; + } PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d", sd->sensor_type); + } else { + sd->cam_type = CAM_TYPE_VGA; - if (force_sensor_type != -1) { - sd->sensor_type = !! force_sensor_type; - PDEBUG(D_PROBE, "Forcing sensor type to: %d", - sd->sensor_type); + err_code = cam_get_response16(gspca_dev, 0x07, 1); + if (err_code < 0) + return err_code; + + /* + * Here is a table of the responses to the previous command + * from the known MR97310A VGA cameras. + * + * Name gspca_dev->usb_buf[] sd->sensor_type + * sd->do_lcd_stop + * Aiptek Pencam VGA+ 0300 0 1 + * ION digital 0350 0 1 + * Argus DC-1620 0450 1 0 + * Argus QuickClix 0420 1 1 + * + * Based upon these results, we assume default settings + * and then correct as necessary, as follows. + * + */ + + sd->sensor_type = 1; + sd->do_lcd_stop = 0; + sd->adj_colors = 0; + if ((gspca_dev->usb_buf[0] != 0x03) && + (gspca_dev->usb_buf[0] != 0x04)) { + PDEBUG(D_ERR, "Unknown VGA Sensor id Byte 0: %02x", + gspca_dev->usb_buf[1]); + PDEBUG(D_ERR, "Defaults assumed, may not work"); + PDEBUG(D_ERR, "Please report this"); + } + /* Sakar Digital color needs to be adjusted. */ + if ((gspca_dev->usb_buf[0] == 0x03) && + (gspca_dev->usb_buf[1] == 0x50)) + sd->adj_colors = 1; + if (gspca_dev->usb_buf[0] == 0x04) { + sd->do_lcd_stop = 1; + switch (gspca_dev->usb_buf[1]) { + case 0x50: + sd->sensor_type = 0; + PDEBUG(D_PROBE, "sensor_type corrected to 0"); + break; + case 0x20: + /* Nothing to do here. */ + break; + default: + PDEBUG(D_ERR, + "Unknown VGA Sensor id Byte 1: %02x", + gspca_dev->usb_buf[1]); + PDEBUG(D_ERR, + "Defaults assumed, may not work"); + PDEBUG(D_ERR, "Please report this"); + } } + PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d", + sd->sensor_type); + } + /* Stop streaming as we've started it to probe the sensor type. */ + sd_stopN(gspca_dev); + + if (force_sensor_type != -1) { + sd->sensor_type = !!force_sensor_type; + PDEBUG(D_PROBE, "Forcing sensor type to: %d", + sd->sensor_type); + } + /* Setup controls depending on camera type */ + if (sd->cam_type == CAM_TYPE_CIF) { + /* No brightness for sensor_type 0 */ if (sd->sensor_type == 0) - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | + (1 << ARGUS_QC_BRIGHTNESS_IDX); + else + gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << MIN_CLOCKDIV_IDX); } else { - sd->cam_type = CAM_TYPE_VGA; - PDEBUG(D_PROBE, "MR97310A VGA camera detected"); - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) | - (1 << EXPOSURE_IDX) | (1 << GAIN_IDX); + /* All controls need to be disabled if VGA sensor_type is 0 */ + if (sd->sensor_type == 0) + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | + (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << EXPOSURE_IDX) | + (1 << GAIN_IDX) | + (1 << MIN_CLOCKDIV_IDX); + else if (sd->do_lcd_stop) + /* Argus QuickClix has different brightness limits */ + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX); + else + gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX); } sd->brightness = MR97310A_BRIGHTNESS_DEFAULT; sd->exposure = MR97310A_EXPOSURE_DEFAULT; sd->gain = MR97310A_GAIN_DEFAULT; + sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT; return 0; } @@ -455,11 +627,6 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) }; /* Note: Some of the above descriptions guessed from MR97113A driver */ - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; memcpy(data, startup_string, 11); if (sd->sensor_type) @@ -533,22 +700,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, ARRAY_SIZE(cif_sensor1_init_data)); } - if (err_code < 0) - return err_code; - - setbrightness(gspca_dev); - setexposure(gspca_dev); - setgain(gspca_dev); - - msleep(200); - - data[0] = 0x00; - data[1] = 0x4d; /* ISOC transfering enable... */ - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - - return 0; + return err_code; } static int start_vga_cam(struct gspca_dev *gspca_dev) @@ -558,84 +710,8 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) int err_code; const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x50, 0xc0}; - /* What some of these mean is explained in start_cif_cam(), above */ - sd->sof_read = 0; - - /* - * We have to know which camera we have, because the register writes - * depend upon the camera. This test, run before we actually enter - * the initialization routine, distinguishes most of the cameras, If - * needed, another routine is done later, too. - */ - memset(data, 0, 16); - data[0] = 0x20; - err_code = mr_write(gspca_dev, 1); - if (err_code < 0) - return err_code; - - err_code = mr_read(gspca_dev, 16); - if (err_code < 0) - return err_code; - - PDEBUG(D_PROBE, "Byte reported is %02x", data[0]); - - msleep(200); - /* - * Known VGA cameras. If you have another to report, please do - * - * Name byte just read sd->sensor_type - * sd->do_lcd_stop - * Aiptek Pencam VGA+ 0x31 0 1 - * ION digital 0x31 0 1 - * Argus DC-1620 0x30 1 0 - * Argus QuickClix 0x30 1 1 (not caught here) - */ - sd->sensor_type = data[0] & 1; - sd->do_lcd_stop = (~data[0]) & 1; - - - - /* Streaming setup begins here. */ - - - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - /* - * A second test can now resolve any remaining ambiguity in the - * identification of the camera type, - */ - if (!sd->sensor_type) { - data[0] = get_sensor_id(gspca_dev); - if (data[0] == 0x7f) { - sd->sensor_type = 1; - PDEBUG(D_PROBE, "sensor_type corrected to 1"); - } - msleep(200); - } - - if (force_sensor_type != -1) { - sd->sensor_type = !! force_sensor_type; - PDEBUG(D_PROBE, "Forcing sensor type to: %d", - sd->sensor_type); - } - - /* - * Known VGA cameras. - * This test is only run if the previous test returned 0x30, but - * here is the information for all others, too, just for reference. - * - * Name byte just read sd->sensor_type - * - * Aiptek Pencam VGA+ 0xfb (this test not run) 1 - * ION digital 0xbd (this test not run) 1 - * Argus DC-1620 0xe5 (no change) 0 - * Argus QuickClix 0x7f (reclassified) 1 - */ memcpy(data, startup_string, 11); if (!sd->sensor_type) { data[5] = 0x00; @@ -689,29 +765,44 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, ARRAY_SIZE(vga_sensor0_init_data)); } else { /* sd->sensor_type = 1 */ - const struct sensor_w_data vga_sensor1_init_data[] = { + const struct sensor_w_data color_adj[] = { {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, - 0x07, 0x00, 0x01}, 8}, + /* adjusted blue, green, red gain correct + too much blue from the Sakar Digital */ + 0x05, 0x01, 0x04}, 8} + }; + + const struct sensor_w_data color_no_adj[] = { + {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, + /* default blue, green, red gain settings */ + 0x07, 0x00, 0x01}, 8} + }; + + const struct sensor_w_data vga_sensor1_init_data[] = { {0x11, 0x04, {0x01}, 1}, - /*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */ - {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, + {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, + /* These settings may be better for some cameras */ + /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */ 0x00, 0x0a}, 7}, {0x11, 0x04, {0x01}, 1}, {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, {0x11, 0x04, {0x01}, 1}, {0, 0, {0}, 0} }; + + if (sd->adj_colors) + err_code = sensor_write_regs(gspca_dev, color_adj, + ARRAY_SIZE(color_adj)); + else + err_code = sensor_write_regs(gspca_dev, color_no_adj, + ARRAY_SIZE(color_no_adj)); + + if (err_code < 0) + return err_code; + err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, ARRAY_SIZE(vga_sensor1_init_data)); } - if (err_code < 0) - return err_code; - - msleep(200); - data[0] = 0x00; - data[1] = 0x4d; /* ISOC transfering enable... */ - err_code = mr_write(gspca_dev, 2); - return err_code; } @@ -719,97 +810,120 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int err_code; - struct cam *cam; - cam = &gspca_dev->cam; sd->sof_read = 0; - /* - * Some of the supported cameras require the memory pointer to be - * set to 0, or else they will not stream. - */ - zero_the_pointer(gspca_dev); - msleep(200); + + /* Some of the VGA cameras require the memory pointer + * to be set to 0 again. We have been forced to start the + * stream in sd_config() to detect the hardware, and closed it. + * Thus, we need here to do a completely fresh and clean start. */ + err_code = zero_the_pointer(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = stream_start(gspca_dev); + if (err_code < 0) + return err_code; + if (sd->cam_type == CAM_TYPE_CIF) { err_code = start_cif_cam(gspca_dev); } else { err_code = start_vga_cam(gspca_dev); } - return err_code; + if (err_code < 0) + return err_code; + + setbrightness(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); + + return isoc_enable(gspca_dev); } static void sd_stopN(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int result; - - gspca_dev->usb_buf[0] = 1; - gspca_dev->usb_buf[1] = 0; - result = mr_write(gspca_dev, 2); - if (result < 0) - PDEBUG(D_ERR, "Camera Stop failed"); + stream_stop(gspca_dev); /* Not all the cams need this, but even if not, probably a good idea */ zero_the_pointer(gspca_dev); - if (sd->do_lcd_stop) { - gspca_dev->usb_buf[0] = 0x19; - gspca_dev->usb_buf[1] = 0x54; - result = mr_write(gspca_dev, 2); - if (result < 0) - PDEBUG(D_ERR, "Camera Stop failed"); - } + if (sd->do_lcd_stop) + lcd_stop(gspca_dev); } static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; - - if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX)) + u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */ + u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */ + const u8 quick_clix_table[] = + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15}; + /* + * This control is disabled for CIF type 1 and VGA type 0 cameras. + * It does not quite act linearly for the Argus QuickClix camera, + * but it does control brightness. The values are 0 - 15 only, and + * the table above makes them act consecutively. + */ + if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) && + (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX))) return; - /* Note register 7 is also seen as 0x8x or 0xCx in dumps */ + if (sd->cam_type == CAM_TYPE_VGA) { + sign_reg += 4; + value_reg += 4; + } + + /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */ if (sd->brightness > 0) { - sensor_write1(gspca_dev, 7, 0x00); + sensor_write1(gspca_dev, sign_reg, 0x00); val = sd->brightness; } else { - sensor_write1(gspca_dev, 7, 0x01); - val = 257 - sd->brightness; + sensor_write1(gspca_dev, sign_reg, 0x01); + val = (257 - sd->brightness); } - sensor_write1(gspca_dev, 8, val); + /* Use lookup table for funky Argus QuickClix brightness */ + if (sd->do_lcd_stop) + val = quick_clix_table[val]; + + sensor_write1(gspca_dev, value_reg, val); } static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 val; + int exposure; + u8 buf[2]; if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX)) return; - if (sd->sensor_type) { - val = sd->exposure >> 4; - sensor_write1(gspca_dev, 3, val); - val = sd->exposure & 0xf; - sensor_write1(gspca_dev, 4, val); + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { + /* This cam does not like exposure settings < 300, + so scale 0 - 4095 to 300 - 4095 */ + exposure = (sd->exposure * 9267) / 10000 + 300; + sensor_write1(gspca_dev, 3, exposure >> 4); + sensor_write1(gspca_dev, 4, exposure & 0x0f); } else { - u8 clockdiv; - int exposure; - /* We have both a clock divider and an exposure register. We first calculate the clock divider, as that determines - the maximum exposure and then we calculayte the exposure + the maximum exposure and then we calculate the exposure register setting (which goes from 0 - 511). Note our 0 - 4095 exposure is mapped to 0 - 511 milliseconds exposure time */ - clockdiv = (60 * sd->exposure + 7999) / 8000; + u8 clockdiv = (60 * sd->exposure + 7999) / 8000; /* Limit framerate to not exceed usb bandwidth */ - if (clockdiv < 3 && gspca_dev->width >= 320) - clockdiv = 3; + if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320) + clockdiv = sd->min_clockdiv; else if (clockdiv < 2) clockdiv = 2; + if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4) + clockdiv = 4; + /* Frame exposure time in ms = 1000 * clockdiv / 60 -> exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv); @@ -819,9 +933,10 @@ static void setexposure(struct gspca_dev *gspca_dev) /* exposure register value is reversed! */ exposure = 511 - exposure; + buf[0] = exposure & 0xff; + buf[1] = exposure >> 8; + sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2); sensor_write1(gspca_dev, 0x02, clockdiv); - sensor_write1(gspca_dev, 0x0e, exposure & 0xff); - sensor_write1(gspca_dev, 0x0f, exposure >> 8); } } @@ -832,7 +947,7 @@ static void setgain(struct gspca_dev *gspca_dev) if (gspca_dev->ctrl_dis & (1 << GAIN_IDX)) return; - if (sd->sensor_type) { + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { sensor_write1(gspca_dev, 0x0e, sd->gain); } else { sensor_write1(gspca_dev, 0x10, sd->gain); @@ -893,17 +1008,35 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->min_clockdiv = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->min_clockdiv; + return 0; +} + /* Include pac common sof detection functions */ #include "pac_common.h" static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ - int len) /* iso packet length */ + u8 *data, /* isoc packet */ + int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n; @@ -913,15 +1046,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n -= sizeof pac_sof_marker; else n = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, data, n); /* Start next frame. */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, pac_sof_marker, sizeof pac_sof_marker); len -= sof - data; data = sof; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } /* sub-driver description */ @@ -938,6 +1071,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */ {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index a5c190e93799..ad9ec339981d 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2,14 +2,19 @@ * OV519 driver * * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> * * This module is adapted from the ov51x-jpeg package, which itself * was adapted from the ov511 driver. * * Original copyright for the ov511 driver is: * - * Copyright (c) 1999-2004 Mark W. McClelland + * Copyright (c) 1999-2006 Mark W. McClelland * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach + * Many improvements by Bret Wallach <bwallac1@san.rr.com> + * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) + * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org> + * Changes by Claudio Matsuoka <claudio@conectiva.com> * * ov51x-jpeg original copyright is: * @@ -58,6 +63,8 @@ struct sd { #define BRIDGE_OV518 2 #define BRIDGE_OV518PLUS 3 #define BRIDGE_OV519 4 +#define BRIDGE_OVFX2 5 +#define BRIDGE_W9968CF 6 #define BRIDGE_MASK 7 char invert_led; @@ -73,6 +80,10 @@ struct sd { __u8 vflip; __u8 autobrightness; __u8 freq; + __u8 quality; +#define QUALITY_MIN 50 +#define QUALITY_MAX 70 +#define QUALITY_DEF 50 __u8 stopped; /* Streaming is temporarily paused */ @@ -81,17 +92,31 @@ struct sd { char sensor; /* Type of image sensor chip (SEN_*) */ #define SEN_UNKNOWN 0 -#define SEN_OV6620 1 -#define SEN_OV6630 2 -#define SEN_OV66308AF 3 -#define SEN_OV7610 4 -#define SEN_OV7620 5 -#define SEN_OV7640 6 -#define SEN_OV7670 7 -#define SEN_OV76BE 8 -#define SEN_OV8610 9 +#define SEN_OV2610 1 +#define SEN_OV3610 2 +#define SEN_OV6620 3 +#define SEN_OV6630 4 +#define SEN_OV66308AF 5 +#define SEN_OV7610 6 +#define SEN_OV7620 7 +#define SEN_OV7640 8 +#define SEN_OV7670 9 +#define SEN_OV76BE 10 +#define SEN_OV8610 11 + + u8 sensor_addr; + int sensor_width; + int sensor_height; + int sensor_reg_cache[256]; + + u8 *jpeg_hdr; }; +/* Note this is a bit of a hack, but the w9968cf driver needs the code for all + the ov sensors which is already present here. When we have the time we + really should move the sensor drivers to v4l2 sub drivers. */ +#include "w996Xcf.c" + /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); @@ -345,6 +370,75 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { .priv = 0}, }; +static const struct v4l2_pix_format ovfx2_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_cif_mode[] = { + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_ov2610_mode[] = { + {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; +static const struct v4l2_pix_format ovfx2_ov3610_mode[] = { + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 2048, + .sizeimage = 2048 * 1536, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + + /* Registers common to OV511 / OV518 */ #define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ #define R51x_SYS_RESET 0x50 @@ -406,6 +500,30 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { #define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ +/* + * The FX2 chip does not give us a zero length read at end of frame. + * It does, however, give a short read at the end of a frame, if + * neccessary, rather than run two frames together. + * + * By choosing the right bulk transfer size, we are guaranteed to always + * get a short read for the last read of each frame. Frame sizes are + * always a composite number (width * height, or a multiple) so if we + * choose a prime number, we are guaranteed that the last read of a + * frame will be short. + * + * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB, + * otherwise EOVERFLOW "babbling" errors occur. I have not been able + * to figure out why. [PMiller] + * + * The constant (13 * 4096) is the largest "prime enough" number less than 64KB. + * + * It isn't enough to know the number of bytes per frame, in case we + * have data dropouts or buffer overruns (even though the FX2 double + * buffers, there are some pretty strict real time constraints for + * isochronous transfer for larger frame sizes). + */ +#define OVFX2_BULK_SIZE (13 * 4096) + /* I2C registers */ #define R51x_I2C_W_SID 0x41 #define R51x_I2C_SADDR_3 0x42 @@ -413,9 +531,11 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { #define R51x_I2C_R_SID 0x44 #define R51x_I2C_DATA 0x45 #define R518_I2C_CTL 0x47 /* OV518(+) only */ +#define OVFX2_I2C_ADDR 0x00 /* I2C ADDRESSES */ #define OV7xx0_SID 0x42 +#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */ #define OV8xx0_SID 0xa0 #define OV6xx0_SID 0xc0 @@ -508,6 +628,696 @@ struct ov_i2c_regvals { __u8 val; }; +/* Settings for OV2610 camera chip */ +static const struct ov_i2c_regvals norm_2610[] = +{ + { 0x12, 0x80 }, /* reset */ +}; + +static const struct ov_i2c_regvals norm_3620b[] = +{ + /* + * From the datasheet: "Note that after writing to register COMH + * (0x12) to change the sensor mode, registers related to the + * sensor’s cropping window will be reset back to their default + * values." + * + * "wait 4096 external clock ... to make sure the sensor is + * stable and ready to access registers" i.e. 160us at 24MHz + */ + + { 0x12, 0x80 }, /* COMH reset */ + { 0x12, 0x00 }, /* QXGA, master */ + + /* + * 11 CLKRC "Clock Rate Control" + * [7] internal frequency doublers: on + * [6] video port mode: master + * [5:0] clock divider: 1 + */ + { 0x11, 0x80 }, + + /* + * 13 COMI "Common Control I" + * = 192 (0xC0) 11000000 + * COMI[7] "AEC speed selection" + * = 1 (0x01) 1....... "Faster AEC correction" + * COMI[6] "AEC speed step selection" + * = 1 (0x01) .1...... "Big steps, fast" + * COMI[5] "Banding filter on off" + * = 0 (0x00) ..0..... "Off" + * COMI[4] "Banding filter option" + * = 0 (0x00) ...0.... "Main clock is 48 MHz and + * the PLL is ON" + * COMI[3] "Reserved" + * = 0 (0x00) ....0... + * COMI[2] "AGC auto manual control selection" + * = 0 (0x00) .....0.. "Manual" + * COMI[1] "AWB auto manual control selection" + * = 0 (0x00) ......0. "Manual" + * COMI[0] "Exposure control" + * = 0 (0x00) .......0 "Manual" + */ + { 0x13, 0xC0 }, + + /* + * 09 COMC "Common Control C" + * = 8 (0x08) 00001000 + * COMC[7:5] "Reserved" + * = 0 (0x00) 000..... + * COMC[4] "Sleep Mode Enable" + * = 0 (0x00) ...0.... "Normal mode" + * COMC[3:2] "Sensor sampling reset timing selection" + * = 2 (0x02) ....10.. "Longer reset time" + * COMC[1:0] "Output drive current select" + * = 0 (0x00) ......00 "Weakest" + */ + { 0x09, 0x08 }, + + /* + * 0C COMD "Common Control D" + * = 8 (0x08) 00001000 + * COMD[7] "Reserved" + * = 0 (0x00) 0....... + * COMD[6] "Swap MSB and LSB at the output port" + * = 0 (0x00) .0...... "False" + * COMD[5:3] "Reserved" + * = 1 (0x01) ..001... + * COMD[2] "Output Average On Off" + * = 0 (0x00) .....0.. "Output Normal" + * COMD[1] "Sensor precharge voltage selection" + * = 0 (0x00) ......0. "Selects internal + * reference precharge + * voltage" + * COMD[0] "Snapshot option" + * = 0 (0x00) .......0 "Enable live video output + * after snapshot sequence" + */ + { 0x0c, 0x08 }, + + /* + * 0D COME "Common Control E" + * = 161 (0xA1) 10100001 + * COME[7] "Output average option" + * = 1 (0x01) 1....... "Output average of 4 pixels" + * COME[6] "Anti-blooming control" + * = 0 (0x00) .0...... "Off" + * COME[5:3] "Reserved" + * = 4 (0x04) ..100... + * COME[2] "Clock output power down pin status" + * = 0 (0x00) .....0.. "Tri-state data output pin + * on power down" + * COME[1] "Data output pin status selection at power down" + * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK, + * HREF, and CHSYNC pins on + * power down" + * COME[0] "Auto zero circuit select" + * = 1 (0x01) .......1 "On" + */ + { 0x0d, 0xA1 }, + + /* + * 0E COMF "Common Control F" + * = 112 (0x70) 01110000 + * COMF[7] "System clock selection" + * = 0 (0x00) 0....... "Use 24 MHz system clock" + * COMF[6:4] "Reserved" + * = 7 (0x07) .111.... + * COMF[3] "Manual auto negative offset canceling selection" + * = 0 (0x00) ....0... "Auto detect negative + * offset and cancel it" + * COMF[2:0] "Reserved" + * = 0 (0x00) .....000 + */ + { 0x0e, 0x70 }, + + /* + * 0F COMG "Common Control G" + * = 66 (0x42) 01000010 + * COMG[7] "Optical black output selection" + * = 0 (0x00) 0....... "Disable" + * COMG[6] "Black level calibrate selection" + * = 1 (0x01) .1...... "Use optical black pixels + * to calibrate" + * COMG[5:4] "Reserved" + * = 0 (0x00) ..00.... + * COMG[3] "Channel offset adjustment" + * = 0 (0x00) ....0... "Disable offset adjustment" + * COMG[2] "ADC black level calibration option" + * = 0 (0x00) .....0.. "Use B/G line and G/R + * line to calibrate each + * channel's black level" + * COMG[1] "Reserved" + * = 1 (0x01) ......1. + * COMG[0] "ADC black level calibration enable" + * = 0 (0x00) .......0 "Disable" + */ + { 0x0f, 0x42 }, + + /* + * 14 COMJ "Common Control J" + * = 198 (0xC6) 11000110 + * COMJ[7:6] "AGC gain ceiling" + * = 3 (0x03) 11...... "8x" + * COMJ[5:4] "Reserved" + * = 0 (0x00) ..00.... + * COMJ[3] "Auto banding filter" + * = 0 (0x00) ....0... "Banding filter is always + * on off depending on + * COMI[5] setting" + * COMJ[2] "VSYNC drop option" + * = 1 (0x01) .....1.. "SYNC is dropped if frame + * data is dropped" + * COMJ[1] "Frame data drop" + * = 1 (0x01) ......1. "Drop frame data if + * exposure is not within + * tolerance. In AEC mode, + * data is normally dropped + * when data is out of + * range." + * COMJ[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x14, 0xC6 }, + + /* + * 15 COMK "Common Control K" + * = 2 (0x02) 00000010 + * COMK[7] "CHSYNC pin output swap" + * = 0 (0x00) 0....... "CHSYNC" + * COMK[6] "HREF pin output swap" + * = 0 (0x00) .0...... "HREF" + * COMK[5] "PCLK output selection" + * = 0 (0x00) ..0..... "PCLK always output" + * COMK[4] "PCLK edge selection" + * = 0 (0x00) ...0.... "Data valid on falling edge" + * COMK[3] "HREF output polarity" + * = 0 (0x00) ....0... "positive" + * COMK[2] "Reserved" + * = 0 (0x00) .....0.. + * COMK[1] "VSYNC polarity" + * = 1 (0x01) ......1. "negative" + * COMK[0] "HSYNC polarity" + * = 0 (0x00) .......0 "positive" + */ + { 0x15, 0x02 }, + + /* + * 33 CHLF "Current Control" + * = 9 (0x09) 00001001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 0 (0x00) ...0.... "normal current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x09 }, + + /* + * 34 VBLM "Blooming Control" + * = 80 (0x50) 01010000 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 0 (0x00) ....0000 + */ + { 0x34, 0x50 }, + + /* + * 36 VCHG "Sensor Precharge Voltage Control" + * = 0 (0x00) 00000000 + * VCHG[7] "Reserved" + * = 0 (0x00) 0....... + * VCHG[6:4] "Sensor precharge voltage control" + * = 0 (0x00) .000.... + * VCHG[3:0] "Sensor array common reference" + * = 0 (0x00) ....0000 + */ + { 0x36, 0x00 }, + + /* + * 37 ADC "ADC Reference Control" + * = 4 (0x04) 00000100 + * ADC[7:4] "Reserved" + * = 0 (0x00) 0000.... + * ADC[3] "ADC input signal range" + * = 0 (0x00) ....0... "Input signal 1.0x" + * ADC[2:0] "ADC range control" + * = 4 (0x04) .....100 + */ + { 0x37, 0x04 }, + + /* + * 38 ACOM "Analog Common Ground" + * = 82 (0x52) 01010010 + * ACOM[7] "Analog gain control" + * = 0 (0x00) 0....... "Gain 1x" + * ACOM[6] "Analog black level calibration" + * = 1 (0x01) .1...... "On" + * ACOM[5:0] "Reserved" + * = 18 (0x12) ..010010 + */ + { 0x38, 0x52 }, + + /* + * 3A FREFA "Internal Reference Adjustment" + * = 0 (0x00) 00000000 + * FREFA[7:0] "Range" + * = 0 (0x00) 00000000 + */ + { 0x3a, 0x00 }, + + /* + * 3C FVOPT "Internal Reference Adjustment" + * = 31 (0x1F) 00011111 + * FVOPT[7:0] "Range" + * = 31 (0x1F) 00011111 + */ + { 0x3c, 0x1F }, + + /* + * 44 Undocumented = 0 (0x00) 00000000 + * 44[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x44, 0x00 }, + + /* + * 40 Undocumented = 0 (0x00) 00000000 + * 40[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x40, 0x00 }, + + /* + * 41 Undocumented = 0 (0x00) 00000000 + * 41[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x41, 0x00 }, + + /* + * 42 Undocumented = 0 (0x00) 00000000 + * 42[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x42, 0x00 }, + + /* + * 43 Undocumented = 0 (0x00) 00000000 + * 43[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x43, 0x00 }, + + /* + * 45 Undocumented = 128 (0x80) 10000000 + * 45[7:0] "It's a secret" + * = 128 (0x80) 10000000 + */ + { 0x45, 0x80 }, + + /* + * 48 Undocumented = 192 (0xC0) 11000000 + * 48[7:0] "It's a secret" + * = 192 (0xC0) 11000000 + */ + { 0x48, 0xC0 }, + + /* + * 49 Undocumented = 25 (0x19) 00011001 + * 49[7:0] "It's a secret" + * = 25 (0x19) 00011001 + */ + { 0x49, 0x19 }, + + /* + * 4B Undocumented = 128 (0x80) 10000000 + * 4B[7:0] "It's a secret" + * = 128 (0x80) 10000000 + */ + { 0x4B, 0x80 }, + + /* + * 4D Undocumented = 196 (0xC4) 11000100 + * 4D[7:0] "It's a secret" + * = 196 (0xC4) 11000100 + */ + { 0x4D, 0xC4 }, + + /* + * 35 VREF "Reference Voltage Control" + * = 76 (0x4C) 01001100 + * VREF[7:5] "Column high reference control" + * = 2 (0x02) 010..... "higher voltage" + * VREF[4:2] "Column low reference control" + * = 3 (0x03) ...011.. "Highest voltage" + * VREF[1:0] "Reserved" + * = 0 (0x00) ......00 + */ + { 0x35, 0x4C }, + + /* + * 3D Undocumented = 0 (0x00) 00000000 + * 3D[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x3D, 0x00 }, + + /* + * 3E Undocumented = 0 (0x00) 00000000 + * 3E[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x3E, 0x00 }, + + /* + * 3B FREFB "Internal Reference Adjustment" + * = 24 (0x18) 00011000 + * FREFB[7:0] "Range" + * = 24 (0x18) 00011000 + */ + { 0x3b, 0x18 }, + + /* + * 33 CHLF "Current Control" + * = 25 (0x19) 00011001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 1 (0x01) ...1.... "double current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x19 }, + + /* + * 34 VBLM "Blooming Control" + * = 90 (0x5A) 01011010 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 10 (0x0A) ....1010 + */ + { 0x34, 0x5A }, + + /* + * 3B FREFB "Internal Reference Adjustment" + * = 0 (0x00) 00000000 + * FREFB[7:0] "Range" + * = 0 (0x00) 00000000 + */ + { 0x3b, 0x00 }, + + /* + * 33 CHLF "Current Control" + * = 9 (0x09) 00001001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 0 (0x00) ...0.... "normal current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x09 }, + + /* + * 34 VBLM "Blooming Control" + * = 80 (0x50) 01010000 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 0 (0x00) ....0000 + */ + { 0x34, 0x50 }, + + /* + * 12 COMH "Common Control H" + * = 64 (0x40) 01000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 4 (0x04) .100.... "XGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x40 }, + + /* + * 17 HREFST "Horizontal window start" + * = 31 (0x1F) 00011111 + * HREFST[7:0] "Horizontal window start, 8 MSBs" + * = 31 (0x1F) 00011111 + */ + { 0x17, 0x1F }, + + /* + * 18 HREFEND "Horizontal window end" + * = 95 (0x5F) 01011111 + * HREFEND[7:0] "Horizontal Window End, 8 MSBs" + * = 95 (0x5F) 01011111 + */ + { 0x18, 0x5F }, + + /* + * 19 VSTRT "Vertical window start" + * = 0 (0x00) 00000000 + * VSTRT[7:0] "Vertical Window Start, 8 MSBs" + * = 0 (0x00) 00000000 + */ + { 0x19, 0x00 }, + + /* + * 1A VEND "Vertical window end" + * = 96 (0x60) 01100000 + * VEND[7:0] "Vertical Window End, 8 MSBs" + * = 96 (0x60) 01100000 + */ + { 0x1a, 0x60 }, + + /* + * 32 COMM "Common Control M" + * = 18 (0x12) 00010010 + * COMM[7:6] "Pixel clock divide option" + * = 0 (0x00) 00...... "/1" + * COMM[5:3] "Horizontal window end position, 3 LSBs" + * = 2 (0x02) ..010... + * COMM[2:0] "Horizontal window start position, 3 LSBs" + * = 2 (0x02) .....010 + */ + { 0x32, 0x12 }, + + /* + * 03 COMA "Common Control A" + * = 74 (0x4A) 01001010 + * COMA[7:4] "AWB Update Threshold" + * = 4 (0x04) 0100.... + * COMA[3:2] "Vertical window end line control 2 LSBs" + * = 2 (0x02) ....10.. + * COMA[1:0] "Vertical window start line control 2 LSBs" + * = 2 (0x02) ......10 + */ + { 0x03, 0x4A }, + + /* + * 11 CLKRC "Clock Rate Control" + * = 128 (0x80) 10000000 + * CLKRC[7] "Internal frequency doublers on off seclection" + * = 1 (0x01) 1....... "On" + * CLKRC[6] "Digital video master slave selection" + * = 0 (0x00) .0...... "Master mode, sensor + * provides PCLK" + * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }" + * = 0 (0x00) ..000000 + */ + { 0x11, 0x80 }, + + /* + * 12 COMH "Common Control H" + * = 0 (0x00) 00000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 0 (0x00) .000.... "QXGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x00 }, + + /* + * 12 COMH "Common Control H" + * = 64 (0x40) 01000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 4 (0x04) .100.... "XGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x40 }, + + /* + * 17 HREFST "Horizontal window start" + * = 31 (0x1F) 00011111 + * HREFST[7:0] "Horizontal window start, 8 MSBs" + * = 31 (0x1F) 00011111 + */ + { 0x17, 0x1F }, + + /* + * 18 HREFEND "Horizontal window end" + * = 95 (0x5F) 01011111 + * HREFEND[7:0] "Horizontal Window End, 8 MSBs" + * = 95 (0x5F) 01011111 + */ + { 0x18, 0x5F }, + + /* + * 19 VSTRT "Vertical window start" + * = 0 (0x00) 00000000 + * VSTRT[7:0] "Vertical Window Start, 8 MSBs" + * = 0 (0x00) 00000000 + */ + { 0x19, 0x00 }, + + /* + * 1A VEND "Vertical window end" + * = 96 (0x60) 01100000 + * VEND[7:0] "Vertical Window End, 8 MSBs" + * = 96 (0x60) 01100000 + */ + { 0x1a, 0x60 }, + + /* + * 32 COMM "Common Control M" + * = 18 (0x12) 00010010 + * COMM[7:6] "Pixel clock divide option" + * = 0 (0x00) 00...... "/1" + * COMM[5:3] "Horizontal window end position, 3 LSBs" + * = 2 (0x02) ..010... + * COMM[2:0] "Horizontal window start position, 3 LSBs" + * = 2 (0x02) .....010 + */ + { 0x32, 0x12 }, + + /* + * 03 COMA "Common Control A" + * = 74 (0x4A) 01001010 + * COMA[7:4] "AWB Update Threshold" + * = 4 (0x04) 0100.... + * COMA[3:2] "Vertical window end line control 2 LSBs" + * = 2 (0x02) ....10.. + * COMA[1:0] "Vertical window start line control 2 LSBs" + * = 2 (0x02) ......10 + */ + { 0x03, 0x4A }, + + /* + * 02 RED "Red Gain Control" + * = 175 (0xAF) 10101111 + * RED[7] "Action" + * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" + * RED[6:0] "Value" + * = 47 (0x2F) .0101111 + */ + { 0x02, 0xAF }, + + /* + * 2D ADDVSL "VSYNC Pulse Width" + * = 210 (0xD2) 11010010 + * ADDVSL[7:0] "VSYNC pulse width, LSB" + * = 210 (0xD2) 11010010 + */ + { 0x2d, 0xD2 }, + + /* + * 00 GAIN = 24 (0x18) 00011000 + * GAIN[7:6] "Reserved" + * = 0 (0x00) 00...... + * GAIN[5] "Double" + * = 0 (0x00) ..0..... "False" + * GAIN[4] "Double" + * = 1 (0x01) ...1.... "True" + * GAIN[3:0] "Range" + * = 8 (0x08) ....1000 + */ + { 0x00, 0x18 }, + + /* + * 01 BLUE "Blue Gain Control" + * = 240 (0xF0) 11110000 + * BLUE[7] "Action" + * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" + * BLUE[6:0] "Value" + * = 112 (0x70) .1110000 + */ + { 0x01, 0xF0 }, + + /* + * 10 AEC "Automatic Exposure Control" + * = 10 (0x0A) 00001010 + * AEC[7:0] "Automatic Exposure Control, 8 MSBs" + * = 10 (0x0A) 00001010 + */ + { 0x10, 0x0A }, + + { 0xE1, 0x67 }, + { 0xE3, 0x03 }, + { 0xE4, 0x26 }, + { 0xE5, 0x3E }, + { 0xF8, 0x01 }, + { 0xFF, 0x01 }, +}; + static const struct ov_i2c_regvals norm_6x20[] = { { 0x12, 0x80 }, /* reset */ { 0x11, 0x01 }, @@ -678,6 +1488,7 @@ static const struct ov_i2c_regvals norm_7610[] = { }; static const struct ov_i2c_regvals norm_7620[] = { + { 0x12, 0x80 }, /* reset */ { 0x00, 0x00 }, /* gain */ { 0x01, 0x80 }, /* blue gain */ { 0x02, 0x80 }, /* red gain */ @@ -1042,10 +1853,28 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) } /* Write a OV519 register */ -static int reg_w(struct sd *sd, __u16 index, __u8 value) +static int reg_w(struct sd *sd, __u16 index, __u16 value) { - int ret; - int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 2 : 1; + int ret, req = 0; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 2; + break; + case BRIDGE_OVFX2: + req = 0x0a; + /* fall through */ + case BRIDGE_W9968CF: + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + goto leave; + default: + req = 1; + } sd->gspca_dev.usb_buf[0] = value; ret = usb_control_msg(sd->gspca_dev.dev, @@ -1054,17 +1883,35 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, 1, 500); - if (ret < 0) - PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value); - return ret; +leave: + if (ret < 0) { + PDEBUG(D_ERR, "Write reg 0x%04x -> [0x%02x] failed", + value, index); + return ret; + } + + PDEBUG(D_USBO, "Write reg 0x%04x -> [0x%02x]", value, index); + return 0; } -/* Read from a OV519 register */ +/* Read from a OV519 register, note not valid for the w9968cf!! */ /* returns: negative is error, pos or zero is data */ static int reg_r(struct sd *sd, __u16 index) { int ret; - int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 3 : 1; + int req; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 3; + break; + case BRIDGE_OVFX2: + req = 0x0b; + break; + default: + req = 1; + } ret = usb_control_msg(sd->gspca_dev.dev, usb_rcvctrlpipe(sd->gspca_dev.dev, 0), @@ -1072,10 +1919,12 @@ static int reg_r(struct sd *sd, __u16 index) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, 1, 500); - if (ret >= 0) + if (ret >= 0) { ret = sd->gspca_dev.usb_buf[0]; - else + PDEBUG(D_USBI, "Read reg [0x%02X] -> 0x%04X", index, ret); + } else PDEBUG(D_ERR, "Read reg [0x%02x] failed", index); + return ret; } @@ -1095,6 +1944,7 @@ static int reg_r8(struct sd *sd, ret = sd->gspca_dev.usb_buf[0]; else PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index); + return ret; } @@ -1140,9 +1990,12 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, n, 500); - if (ret < 0) + if (ret < 0) { PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value); - return ret; + return ret; + } + + return 0; } static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value) @@ -1324,32 +2177,110 @@ static int ov518_i2c_r(struct sd *sd, __u8 reg) return value; } +static int ovfx2_i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + int ret; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (__u16)value, (__u16)reg, NULL, 0, 500); + + if (ret < 0) { + PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg); + return ret; + } + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + return 0; +} + +static int ovfx2_i2c_r(struct sd *sd, __u8 reg) +{ + int ret; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 0x03, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, (__u16)reg, sd->gspca_dev.usb_buf, 1, 500); + + if (ret >= 0) { + ret = sd->gspca_dev.usb_buf[0]; + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, ret); + } else + PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); + + return ret; +} + static int i2c_w(struct sd *sd, __u8 reg, __u8 value) { + int ret = -1; + + if (sd->sensor_reg_cache[reg] == value) + return 0; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - return ov511_i2c_w(sd, reg, value); + ret = ov511_i2c_w(sd, reg, value); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: case BRIDGE_OV519: - return ov518_i2c_w(sd, reg, value); + ret = ov518_i2c_w(sd, reg, value); + break; + case BRIDGE_OVFX2: + ret = ovfx2_i2c_w(sd, reg, value); + break; + case BRIDGE_W9968CF: + ret = w9968cf_i2c_w(sd, reg, value); + break; } - return -1; /* Should never happen */ + + if (ret >= 0) { + /* Up on sensor reset empty the register cache */ + if (reg == 0x12 && (value & 0x80)) + memset(sd->sensor_reg_cache, -1, + sizeof(sd->sensor_reg_cache)); + else + sd->sensor_reg_cache[reg] = value; + } + + return ret; } static int i2c_r(struct sd *sd, __u8 reg) { + int ret = -1; + + if (sd->sensor_reg_cache[reg] != -1) + return sd->sensor_reg_cache[reg]; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - return ov511_i2c_r(sd, reg); + ret = ov511_i2c_r(sd, reg); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: case BRIDGE_OV519: - return ov518_i2c_r(sd, reg); + ret = ov518_i2c_r(sd, reg); + break; + case BRIDGE_OVFX2: + ret = ovfx2_i2c_r(sd, reg); + break; + case BRIDGE_W9968CF: + ret = w9968cf_i2c_r(sd, reg); + break; } - return -1; /* Should never happen */ + + if (ret >= 0) + sd->sensor_reg_cache[reg] = ret; + + return ret; } /* Writes bits at positions specified by mask to an I2C reg. Bits that are in @@ -1389,6 +2320,10 @@ static inline int ov51x_stop(struct sd *sd) return reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a); case BRIDGE_OV519: return reg_w(sd, OV519_SYS_RESET1, 0x0f); + case BRIDGE_OVFX2: + return reg_w_mask(sd, 0x0f, 0x00, 0x02); + case BRIDGE_W9968CF: + return reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */ } return 0; @@ -1418,18 +2353,27 @@ static inline int ov51x_restart(struct sd *sd) return reg_w(sd, R51x_SYS_RESET, 0x00); case BRIDGE_OV519: return reg_w(sd, OV519_SYS_RESET1, 0x00); + case BRIDGE_OVFX2: + return reg_w_mask(sd, 0x0f, 0x02, 0x02); + case BRIDGE_W9968CF: + return reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */ } return 0; } +static int ov51x_set_slave_ids(struct sd *sd, __u8 slave); + /* This does an initial reset of an OmniVision sensor and ensures that I2C * is synchronized. Returns <0 on failure. */ -static int init_ov_sensor(struct sd *sd) +static int init_ov_sensor(struct sd *sd, __u8 slave) { int i; + if (ov51x_set_slave_ids(sd, slave) < 0) + return -EIO; + /* Reset the sensor */ if (i2c_w(sd, 0x12, 0x80) < 0) return -EIO; @@ -1466,6 +2410,14 @@ static int ov51x_set_slave_ids(struct sd *sd, { int rc; + switch (sd->bridge) { + case BRIDGE_OVFX2: + return reg_w(sd, OVFX2_I2C_ADDR, slave); + case BRIDGE_W9968CF: + sd->sensor_addr = slave; + return 0; + } + rc = reg_w(sd, R51x_I2C_W_SID, slave); if (rc < 0) return rc; @@ -1508,6 +2460,39 @@ static int write_i2c_regvals(struct sd *sd, * ***************************************************************************/ +/* This initializes the OV2x10 / OV3610 / OV3620 */ +static int ov_hires_configure(struct sd *sd) +{ + int high, low; + + if (sd->bridge != BRIDGE_OVFX2) { + PDEBUG(D_ERR, "error hires sensors only supported with ovfx2"); + return -1; + } + + PDEBUG(D_PROBE, "starting ov hires configuration"); + + /* Detect sensor (sub)type */ + high = i2c_r(sd, 0x0a); + low = i2c_r(sd, 0x0b); + /* info("%x, %x", high, low); */ + if (high == 0x96 && low == 0x40) { + PDEBUG(D_PROBE, "Sensor is an OV2610"); + sd->sensor = SEN_OV2610; + } else if (high == 0x36 && (low & 0x0f) == 0x00) { + PDEBUG(D_PROBE, "Sensor is an OV3610"); + sd->sensor = SEN_OV3610; + } else { + PDEBUG(D_ERR, "Error unknown sensor type: 0x%02x%02x", + high, low); + return -1; + } + + /* Set sensor-specific vars */ + return 0; +} + + /* This initializes the OV8110, OV8610 sensor. The OV8110 uses * the same register settings as the OV8610, since they are very similar. */ @@ -1966,12 +2951,29 @@ static int ov519_configure(struct sd *sd) return write_regvals(sd, init_519, ARRAY_SIZE(init_519)); } +static int ovfx2_configure(struct sd *sd) +{ + static const struct ov_regvals init_fx2[] = { + { 0x00, 0x60 }, + { 0x02, 0x01 }, + { 0x0f, 0x1d }, + { 0xe9, 0x82 }, + { 0xea, 0xc7 }, + { 0xeb, 0x10 }, + { 0xec, 0xf6 }, + }; + + sd->stopped = 1; + + return write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2)); +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; + struct cam *cam = &gspca_dev->cam; int ret = 0; sd->bridge = id->driver_info & BRIDGE_MASK; @@ -1989,6 +2991,16 @@ static int sd_config(struct gspca_dev *gspca_dev, case BRIDGE_OV519: ret = ov519_configure(sd); break; + case BRIDGE_OVFX2: + ret = ovfx2_configure(sd); + cam->bulk_size = OVFX2_BULK_SIZE; + cam->bulk_nurbs = MAX_NURBS; + cam->bulk = 1; + break; + case BRIDGE_W9968CF: + ret = w9968cf_configure(sd); + cam->reverse_alts = 1; + break; } if (ret) @@ -1996,49 +3008,39 @@ static int sd_config(struct gspca_dev *gspca_dev, ov51x_led_control(sd, 0); /* turn LED off */ - /* Test for 76xx */ - if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0) - goto error; - /* The OV519 must be more aggressive about sensor detection since * I2C write will never fail if the sensor is not present. We have * to try to initialize the sensor to detect its presence */ - if (init_ov_sensor(sd) >= 0) { + + /* Test for 76xx */ + if (init_ov_sensor(sd, OV7xx0_SID) >= 0) { if (ov7xx0_configure(sd) < 0) { PDEBUG(D_ERR, "Failed to configure OV7xx0"); goto error; } - } else { - - /* Test for 6xx0 */ - if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0) + /* Test for 6xx0 */ + } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) { + if (ov6xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV6xx0"); + goto error; + } + /* Test for 8xx0 */ + } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) { + if (ov8xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV8xx0"); goto error; - - if (init_ov_sensor(sd) >= 0) { - if (ov6xx0_configure(sd) < 0) { - PDEBUG(D_ERR, "Failed to configure OV6xx0"); - goto error; - } - } else { - - /* Test for 8xx0 */ - if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0) - goto error; - - if (init_ov_sensor(sd) < 0) { - PDEBUG(D_ERR, - "Can't determine sensor slave IDs"); - goto error; - } - if (ov8xx0_configure(sd) < 0) { - PDEBUG(D_ERR, - "Failed to configure OV8xx0 sensor"); - goto error; - } } + /* Test for 3xxx / 2xxx */ + } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) { + if (ov_hires_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure high res OV"); + goto error; + } + } else { + PDEBUG(D_ERR, "Can't determine sensor slave IDs"); + goto error; } - cam = &gspca_dev->cam; switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: @@ -2069,6 +3071,31 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(ov519_sif_mode); } break; + case BRIDGE_OVFX2: + if (sd->sensor == SEN_OV2610) { + cam->cam_mode = ovfx2_ov2610_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode); + } else if (sd->sensor == SEN_OV3610) { + cam->cam_mode = ovfx2_ov3610_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode); + } else if (!sd->sif) { + cam->cam_mode = ov519_vga_mode; + cam->nmodes = ARRAY_SIZE(ov519_vga_mode); + } else { + cam->cam_mode = ov519_sif_mode; + cam->nmodes = ARRAY_SIZE(ov519_sif_mode); + } + break; + case BRIDGE_W9968CF: + cam->cam_mode = w9968cf_vga_mode; + cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode); + if (sd->sif) + cam->nmodes--; + + /* w9968cf needs initialisation once the sensor is known */ + if (w9968cf_init(sd) < 0) + goto error; + break; } sd->brightness = BRIGHTNESS_DEF; if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF) @@ -2087,11 +3114,15 @@ static int sd_config(struct gspca_dev *gspca_dev, gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << OV7670_FREQ_IDX); } + sd->quality = QUALITY_DEF; if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX; /* OV8610 Frequency filter control should work but needs testing */ if (sd->sensor == SEN_OV8610) gspca_dev->ctrl_dis |= 1 << FREQ_IDX; + /* No controls for the OV2610/OV3610 */ + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) + gspca_dev->ctrl_dis |= 0xFF; return 0; error: @@ -2106,6 +3137,20 @@ static int sd_init(struct gspca_dev *gspca_dev) /* initialize the sensor */ switch (sd->sensor) { + case SEN_OV2610: + if (write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610))) + return -EIO; + /* Enable autogain, autoexpo, awb, bandfilter */ + if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0) + return -EIO; + break; + case SEN_OV3610: + if (write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b))) + return -EIO; + /* Enable autogain, autoexpo, awb, bandfilter */ + if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0) + return -EIO; + break; case SEN_OV6620: if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20))) return -EIO; @@ -2548,19 +3593,60 @@ static int ov519_mode_init_regs(struct sd *sd) static int mode_init_ov_sensor_regs(struct sd *sd) { struct gspca_dev *gspca_dev; - int qvga; + int qvga, xstart, xend, ystart, yend; + __u8 v; gspca_dev = &sd->gspca_dev; qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; /******** Mode (VGA/QVGA) and sensor specific regs ********/ switch (sd->sensor) { + case SEN_OV2610: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); + i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); + i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + return 0; + case SEN_OV3610: + if (qvga) { + xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4); + ystart = (776 - gspca_dev->height) / 2; + } else { + xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4); + ystart = (1544 - gspca_dev->height) / 2; + } + xend = xstart + gspca_dev->width; + yend = ystart + gspca_dev->height; + /* Writing to the COMH register resets the other windowing regs + to their default values, so we must do this first. */ + i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0); + i2c_w_mask(sd, 0x32, + (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7), + 0x3f); + i2c_w_mask(sd, 0x03, + (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3), + 0x0f); + i2c_w(sd, 0x17, xstart >> 4); + i2c_w(sd, 0x18, xend >> 4); + i2c_w(sd, 0x19, ystart >> 3); + i2c_w(sd, 0x1a, yend >> 3); + return 0; case SEN_OV8610: /* For OV8610 qvga means qsvga */ i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */ + i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */ break; case SEN_OV7610: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w(sd, 0x35, qvga?0x1e:0x9e); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ break; case SEN_OV7620: case SEN_OV76BE: @@ -2571,6 +3657,10 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0); i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + if (sd->sensor == SEN_OV76BE) + i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e); break; case SEN_OV7640: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); @@ -2580,6 +3670,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) /* i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */ /* i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */ /* i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */ + i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */ break; case SEN_OV7670: /* set COM7_FMT_VGA or COM7_FMT_QVGA @@ -2588,55 +3679,56 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, OV7670_REG_COM7, qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA, OV7670_COM7_FMT_MASK); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, + OV7670_COM8_AWB); + if (qvga) { /* QVGA from ov7670.c by + * Jonathan Corbet */ + xstart = 164; + xend = 28; + ystart = 14; + yend = 494; + } else { /* VGA */ + xstart = 158; + xend = 14; + ystart = 10; + yend = 490; + } + /* OV7670 hardware window registers are split across + * multiple locations */ + i2c_w(sd, OV7670_REG_HSTART, xstart >> 3); + i2c_w(sd, OV7670_REG_HSTOP, xend >> 3); + v = i2c_r(sd, OV7670_REG_HREF); + v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_HREF, v); + + i2c_w(sd, OV7670_REG_VSTART, ystart >> 2); + i2c_w(sd, OV7670_REG_VSTOP, yend >> 2); + v = i2c_r(sd, OV7670_REG_VREF); + v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_VREF, v); break; case SEN_OV6620: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + break; case SEN_OV6630: case SEN_OV66308AF: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ break; default: return -EINVAL; } - /******** Palette-specific regs ********/ - - /* The OV518 needs special treatment. Although both the OV518 - * and the OV6630 support a 16-bit video bus, only the 8 bit Y - * bus is actually used. The UV bus is tied to ground. - * Therefore, the OV6630 needs to be in 8-bit multiplexed - * output mode */ - - /* OV7640 is 8-bit only */ - - if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF && - sd->sensor != SEN_OV7640) - i2c_w_mask(sd, 0x13, 0x00, 0x20); - /******** Clock programming ********/ i2c_w(sd, 0x11, sd->clockdiv); - /******** Special Features ********/ -/* no evidence this is possible with OV7670, either */ - /* Test Pattern */ - if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670) - i2c_w_mask(sd, 0x12, 0x00, 0x02); - - /* Enable auto white balance */ - if (sd->sensor == SEN_OV7670) - i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, - OV7670_COM8_AWB); - else - i2c_w_mask(sd, 0x12, 0x04, 0x04); - - /* This will go away as soon as ov51x_mode_init_sensor_regs() */ - /* is fully tested. */ - /* 7620/6620/6630? don't have register 0x35, so play it safe */ - if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { - if (!qvga) - i2c_w(sd, 0x35, 0x9e); - else - i2c_w(sd, 0x35, 0x1e); - } return 0; } @@ -2659,8 +3751,12 @@ static int set_ov_sensor_window(struct sd *sd) struct gspca_dev *gspca_dev; int qvga, crop; int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; - int ret, hstart, hstop, vstop, vstart; - __u8 v; + int ret; + + /* mode setup is fully handled in mode_init_ov_sensor_regs for these */ + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610 || + sd->sensor == SEN_OV7670) + return mode_init_ov_sensor_regs(sd); gspca_dev = &sd->gspca_dev; qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; @@ -2708,11 +3804,6 @@ static int set_ov_sensor_window(struct sd *sd) hwebase = 0x1a; vwsbase = vwebase = 0x03; break; - case SEN_OV7670: - /*handling of OV7670 hardware sensor start and stop values - * is very odd, compared to the other OV sensors */ - vwsbase = vwebase = hwebase = hwsbase = 0x00; - break; default: return -EINVAL; } @@ -2753,58 +3844,11 @@ static int set_ov_sensor_window(struct sd *sd) if (ret < 0) return ret; - if (sd->sensor == SEN_OV8610) { - i2c_w_mask(sd, 0x2d, 0x05, 0x40); - /* old 0x95, new 0x05 from windrv 090403 */ - /* bits 5-7: reserved */ - i2c_w_mask(sd, 0x28, 0x20, 0x20); - /* bit 5: progressive mode on */ - } - - /* The below is wrong for OV7670s because their window registers - * only store the high bits in 0x17 to 0x1a */ - - /* SRH Use sd->max values instead of requested win values */ - /* SCS Since we're sticking with only the max hardware widths - * for a given mode */ - /* I can hard code this for OV7670s */ - /* Yes, these numbers do look odd, but they're tested and work! */ - if (sd->sensor == SEN_OV7670) { - if (qvga) { /* QVGA from ov7670.c by - * Jonathan Corbet */ - hstart = 164; - hstop = 28; - vstart = 14; - vstop = 494; - } else { /* VGA */ - hstart = 158; - hstop = 14; - vstart = 10; - vstop = 490; - } - /* OV7670 hardware window registers are split across - * multiple locations */ - i2c_w(sd, OV7670_REG_HSTART, hstart >> 3); - i2c_w(sd, OV7670_REG_HSTOP, hstop >> 3); - v = i2c_r(sd, OV7670_REG_HREF); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07); - msleep(10); /* need to sleep between read and write to - * same reg! */ - i2c_w(sd, OV7670_REG_HREF, v); + i2c_w(sd, 0x17, hwsbase); + i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale)); + i2c_w(sd, 0x19, vwsbase); + i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale)); - i2c_w(sd, OV7670_REG_VSTART, vstart >> 2); - i2c_w(sd, OV7670_REG_VSTOP, vstop >> 2); - v = i2c_r(sd, OV7670_REG_VREF); - v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03); - msleep(10); /* need to sleep between read and write to - * same reg! */ - i2c_w(sd, OV7670_REG_VREF, v); - } else { - i2c_w(sd, 0x17, hwsbase); - i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale)); - i2c_w(sd, 0x19, vwsbase); - i2c_w(sd, 0x1a, vwebase + (sd->gspca_dev.height >> vwscale)); - } return 0; } @@ -2814,6 +3858,10 @@ static int sd_start(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int ret = 0; + /* Default for most bridges, allow bridge_mode_init_regs to override */ + sd->sensor_width = sd->gspca_dev.width; + sd->sensor_height = sd->gspca_dev.height; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: @@ -2826,6 +3874,10 @@ static int sd_start(struct gspca_dev *gspca_dev) case BRIDGE_OV519: ret = ov519_mode_init_regs(sd); break; + /* case BRIDGE_OVFX2: nothing to do */ + case BRIDGE_W9968CF: + ret = w9968cf_mode_init_regs(sd); + break; } if (ret < 0) goto out; @@ -2859,10 +3911,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov51x_led_control(sd, 0); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge == BRIDGE_W9968CF) + w9968cf_stop0(sd); +} + static void ov511_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *in, /* isoc packet */ - int len) /* iso packet length */ + u8 *in, /* isoc packet */ + int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -2893,11 +3952,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev, return; } /* Add 11 byte footer to frame, might be usefull */ - gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11); + gspca_frame_add(gspca_dev, LAST_PACKET, in, 11); return; } else { /* Frame start */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0); sd->packet_nr = 0; } } @@ -2906,12 +3965,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev, len--; /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len); + gspca_frame_add(gspca_dev, INTER_PACKET, in, len); } static void ov518_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -2919,8 +3977,8 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, /* A false positive here is likely, until OVT gives me * the definitive SOF/EOF format */ if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) { - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); sd->packet_nr = 0; } @@ -2944,12 +4002,11 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, } /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void ov519_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { /* Header of ov519 is 16 bytes: @@ -2972,7 +4029,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, len -= HDRSZ; #undef HDRSZ if (data[0] == 0xff || data[1] == 0xd8) - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); else gspca_dev->last_packet_type = DISCARD_PACKET; @@ -2980,20 +4037,31 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, case 0x51: /* end of frame */ if (data[9] != 0) gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); return; } } /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* A short read signals EOF */ + if (len < OVFX2_BULK_SIZE) { + gspca_frame_add(gspca_dev, LAST_PACKET, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + return; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -3001,14 +4069,20 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - ov511_pkt_scan(gspca_dev, frame, data, len); + ov511_pkt_scan(gspca_dev, data, len); break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: - ov518_pkt_scan(gspca_dev, frame, data, len); + ov518_pkt_scan(gspca_dev, data, len); break; case BRIDGE_OV519: - ov519_pkt_scan(gspca_dev, frame, data, len); + ov519_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_OVFX2: + ovfx2_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_W9968CF: + w9968cf_pkt_scan(gspca_dev, data, len); break; } } @@ -3124,7 +4198,8 @@ static void setcolors(struct gspca_dev *gspca_dev) static void setautobrightness(struct sd *sd) { - if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670 || + sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) return; i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10); @@ -3132,6 +4207,9 @@ static void setautobrightness(struct sd *sd) static void setfreq(struct sd *sd) { + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) + return; + if (sd->sensor == SEN_OV7670) { switch (sd->freq) { case 0: /* Banding filter disabled */ @@ -3301,8 +4379,12 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->freq = val; - if (gspca_dev->streaming) + if (gspca_dev->streaming) { setfreq(sd); + /* Ugly but necessary */ + if (sd->bridge == BRIDGE_W9968CF) + w9968cf_set_crop_window(sd); + } return 0; } @@ -3343,6 +4425,45 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge != BRIDGE_W9968CF) + return -EINVAL; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT | + V4L2_JPEG_MARKER_DRI; + return 0; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge != BRIDGE_W9968CF) + return -EINVAL; + + if (gspca_dev->streaming) + return -EBUSY; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + + /* Return resulting jcomp params to app */ + sd_get_jcomp(gspca_dev, jcomp); + + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -3352,18 +4473,23 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF }, {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 }, @@ -3373,11 +4499,16 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 }, {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS }, {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS }, {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS }, + {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF }, + {USB_DEVICE(0x8020, 0xEF04), .driver_info = BRIDGE_OVFX2 }, {} }; diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 4b528b372911..4dbb882c83dc 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -1,5 +1,6 @@ /* * ov534 gspca driver + * * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> * Copyright (C) 2008 Jim Paris <jim@jtan.com> * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr @@ -8,6 +9,10 @@ * USB protocol reverse engineered by Jim Paris <jim@jtan.com> * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ * + * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr + * PS3 Eye camera, brightness, contrast, hue, AWB control added + * by Max Thrun <bear24rw@gmail.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -51,16 +56,335 @@ struct sd { u16 last_fid; u8 frame_rate; + u8 brightness; + u8 contrast; + u8 gain; + u8 exposure; + u8 redblc; + u8 blueblc; + u8 hue; + u8 autogain; + u8 awb; + s8 sharpness; + u8 hflip; + u8 vflip; + u8 satur; + u8 lightfreq; + u8 sensor; #define SENSOR_OV772X 0 #define SENSOR_OV965X 1 }; /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls_ov772x[] = { + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_77_DEF 20 + .default_value = BRIGHTNESS_77_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { /* 1 */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_77_DEF 37 + .default_value = CONTRAST_77_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { /* 2 */ + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Main Gain", + .minimum = 0, + .maximum = 63, + .step = 1, +#define GAIN_DEF 20 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 255, + .step = 1, +#define EXPO_77_DEF 120 + .default_value = EXPO_77_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { /* 4 */ + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define RED_BALANCE_DEF 128 + .default_value = RED_BALANCE_DEF, + }, + .set = sd_setredblc, + .get = sd_getredblc, + }, + { /* 5 */ + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BLUE_BALANCE_DEF 128 + .default_value = BLUE_BALANCE_DEF, + }, + .set = sd_setblueblc, + .get = sd_getblueblc, + }, + { /* 6 */ + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 255, + .step = 1, +#define HUE_DEF 143 + .default_value = HUE_DEF, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { /* 7 */ + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Autogain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_77_DEF 0 + .default_value = AUTOGAIN_77_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define AWB_77_IDX 8 + { /* 8 */ + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AWB_DEF 0 + .default_value = AWB_DEF, + }, + .set = sd_setawb, + .get = sd_getawb, + }, + { /* 9 */ + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 63, + .step = 1, +#define SHARPNESS_77_DEF 0 + .default_value = SHARPNESS_77_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, + { /* 10 */ + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "HFlip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { /* 11 */ + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "VFlip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +}; +static struct ctrl sd_ctrls_ov965x[] = { + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 15, + .step = 1, +#define BRIGHTNESS_96_DEF 7 + .default_value = BRIGHTNESS_96_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { /* 1 */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 15, + .step = 1, +#define CONTRAST_96_DEF 3 + .default_value = CONTRAST_96_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { /* 2 */ + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Autogain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_96_DEF 1 + .default_value = AUTOGAIN_96_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define EXPO_96_IDX 3 + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 3, + .step = 1, +#define EXPO_96_DEF 0 + .default_value = EXPO_96_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { /* 4 */ + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = -1, /* -1 = auto */ + .maximum = 4, + .step = 1, +#define SHARPNESS_96_DEF -1 + .default_value = SHARPNESS_96_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, + { /* 5 */ + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 4, + .step = 1, +#define SATUR_DEF 2 + .default_value = SATUR_DEF, + }, + .set = sd_setsatur, + .get = sd_getsatur, + }, + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 0 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; -static const struct v4l2_pix_format vga_yuyv_mode[] = { +static const struct v4l2_pix_format ov772x_mode[] = { + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, .bytesperline = 640 * 2, .sizeimage = 640 * 480 * 2, @@ -68,20 +392,35 @@ static const struct v4l2_pix_format vga_yuyv_mode[] = { .priv = 0}, }; -static const struct v4l2_pix_format vga_jpeg_mode[] = { +static const struct v4l2_pix_format ov965x_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, + .priv = 4}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; -static const u8 bridge_init_ov722x[][2] = { +static const u8 bridge_init_ov772x[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, { 0xc3, 0x69 }, @@ -122,6 +461,7 @@ static const u8 bridge_init_ov722x[][2] = { { 0x1d, 0x40 }, { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ { 0x1d, 0x00 }, /* payload size */ + { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ { 0x1d, 0x58 }, /* frame size */ { 0x1d, 0x00 }, /* frame size */ @@ -138,10 +478,20 @@ static const u8 bridge_init_ov722x[][2] = { { 0xc1, 0x3c }, { 0xc2, 0x0c }, }; - -static const u8 sensor_init_ov722x[][2] = { +static const u8 sensor_init_ov772x[][2] = { { 0x12, 0x80 }, { 0x11, 0x01 }, +/*fixme: better have a delay?*/ + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, { 0x3d, 0x03 }, { 0x17, 0x26 }, @@ -154,10 +504,10 @@ static const u8 sensor_init_ov722x[][2] = { { 0x65, 0x20 }, { 0x11, 0x01 }, { 0x42, 0x7f }, - { 0x63, 0xe0 }, + { 0x63, 0xaa }, /* AWB - was e0 */ { 0x64, 0xff }, { 0x66, 0x00 }, - { 0x13, 0xf0 }, + { 0x13, 0xf0 }, /* com8 */ { 0x0d, 0x41 }, { 0x0f, 0xc5 }, { 0x14, 0x11 }, @@ -170,7 +520,7 @@ static const u8 sensor_init_ov722x[][2] = { { 0x2a, 0x00 }, { 0x2b, 0x00 }, { 0x6b, 0xaa }, - { 0x13, 0xff }, + { 0x13, 0xff }, /* AWB */ { 0x90, 0x05 }, { 0x91, 0x01 }, @@ -218,9 +568,51 @@ static const u8 sensor_init_ov722x[][2] = { { 0x14, 0x41 }, { 0x0e, 0xcd }, { 0xac, 0xbf }, - { 0x8e, 0x00 }, + { 0x8e, 0x00 }, /* De-noise threshold */ { 0x0c, 0xd0 } }; +static const u8 bridge_start_ov772x_vga[][2] = { + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x58}, + {0x1d, 0x00}, + {0xc0, 0x50}, + {0xc1, 0x3c}, +}; +static const u8 sensor_start_ov772x_vga[][2] = { + {0x12, 0x00}, + {0x17, 0x26}, + {0x18, 0xa0}, + {0x19, 0x07}, + {0x1a, 0xf0}, + {0x29, 0xa0}, + {0x2c, 0xf0}, + {0x65, 0x20}, +}; +static const u8 bridge_start_ov772x_qvga[][2] = { + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x01}, + {0x1d, 0x4b}, + {0x1d, 0x00}, + {0xc0, 0x28}, + {0xc1, 0x1e}, +}; +static const u8 sensor_start_ov772x_qvga[][2] = { + {0x12, 0x40}, + {0x17, 0x3f}, + {0x18, 0x50}, + {0x19, 0x03}, + {0x1a, 0x78}, + {0x29, 0x50}, + {0x2c, 0x78}, + {0x65, 0x2f}, +}; static const u8 bridge_init_ov965x[][2] = { {0x88, 0xf8}, @@ -403,7 +795,7 @@ static const u8 sensor_init_ov965x[][2] = { {0xcb, 0xf0}, {0xcc, 0xd8}, {0xcd, 0xf1}, - {0x4f, 0x98}, + {0x4f, 0x98}, /* matrix */ {0x50, 0x98}, {0x51, 0x00}, {0x52, 0x28}, @@ -412,6 +804,7 @@ static const u8 sensor_init_ov965x[][2] = { {0x58, 0x1a}, {0xff, 0x41}, /* read 41, write ff 00 */ {0x41, 0x40}, /* com16 */ + {0xc5, 0x03}, /* 60 Hz banding filter */ {0x6a, 0x02}, /* 50 Hz banding filter */ @@ -455,8 +848,8 @@ static const u8 bridge_init_ov965x_2[][2] = { {0x52, 0x3c}, {0x53, 0x00}, {0x54, 0x00}, - {0x55, 0x00}, /* brightness */ - {0x57, 0x00}, /* contrast 2 */ + {0x55, 0x00}, + {0x57, 0x00}, {0x5c, 0x00}, {0x5a, 0xa0}, {0x5b, 0x78}, @@ -479,14 +872,16 @@ static const u8 sensor_init_ov965x_2[][2] = { {0xa3, 0x3e}, {0x2d, 0x00}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc0}, + {0x42, 0xc0}, /* com17 */ {0x2d, 0x00}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, + {0x42, 0xc1}, /* com17 */ +/* sharpness */ {0x3f, 0x01}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, - {0x4f, 0x98}, + {0x42, 0xc1}, /* com17 */ +/* saturation */ + {0x4f, 0x98}, /* matrix */ {0x50, 0x98}, {0x51, 0x00}, {0x52, 0x28}, @@ -495,14 +890,17 @@ static const u8 sensor_init_ov965x_2[][2] = { {0x58, 0x1a}, {0xff, 0x41}, /* read 41, write ff 00 */ {0x41, 0x40}, /* com16 */ +/* contrast */ {0x56, 0x40}, +/* brightness */ {0x55, 0x8f}, +/* expo */ {0x10, 0x25}, /* aech - exposure high bits */ {0xff, 0x13}, /* read 13, write ff 00 */ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ }; -static const u8 sensor_start_ov965x[][2] = { +static const u8 sensor_start_ov965x_1_vga[][2] = { /* same for qvga */ {0x12, 0x62}, /* com7 - 30fps VGA YUV */ {0x36, 0xfa}, /* aref3 */ {0x69, 0x0a}, /* hv */ @@ -523,10 +921,77 @@ static const u8 sensor_start_ov965x[][2] = { {0x1a, 0x3d}, /* vstop */ {0x32, 0xff}, /* href */ {0xc0, 0xaa}, - {} }; -static const u8 bridge_start_ov965x[][2] = { +static const u8 sensor_start_ov965x_1_svga[][2] = { + {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x0d}, /* com22 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 sensor_start_ov965x_1_xga[][2] = { + {0x12, 0x02}, /* com7 */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 sensor_start_ov965x_1_sxga[][2] = { + {0x12, 0x02}, /* com7 */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 bridge_start_ov965x_qvga[][2] = { {0x94, 0xaa}, {0xf1, 0x60}, {0xe5, 0x04}, @@ -535,10 +1000,34 @@ static const u8 bridge_start_ov965x[][2] = { {0x8c, 0x00}, {0x8d, 0x1c}, {0x34, 0x05}, - {} + + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x78}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0x50}, + {0x5b, 0x3c}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, }; static const u8 bridge_start_ov965x_vga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, {0xc2, 0x0c}, {0xc3, 0xf9}, {0xda, 0x01}, @@ -555,30 +1044,98 @@ static const u8 bridge_start_ov965x_vga[][2] = { {0x35, 0x02}, {0xd9, 0x10}, {0x94, 0x11}, - {} }; -static const u8 bridge_start_ov965x_cif[][2] = { +static const u8 bridge_start_ov965x_svga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, {0xc2, 0x4c}, {0xc3, 0xf9}, - {0xda, 0x00}, {0x50, 0x00}, - {0x51, 0xa0}, - {0x52, 0x78}, + {0x51, 0x40}, + {0x52, 0x00}, {0x53, 0x00}, {0x54, 0x00}, - {0x55, 0x00}, + {0x55, 0x88}, {0x57, 0x00}, {0x5c, 0x00}, - {0x5a, 0x50}, - {0x5b, 0x3c}, + {0x5a, 0xc8}, + {0x5b, 0x96}, {0x35, 0x02}, {0xd9, 0x10}, + {0xda, 0x00}, {0x94, 0x11}, - {} }; -static const u8 sensor_start_ov965x_vga[][2] = { +static const u8 bridge_start_ov965x_xga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0x50, 0x00}, + {0x51, 0x40}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x88}, + {0x57, 0x00}, + {0x5c, 0x01}, + {0x5a, 0x00}, + {0x5b, 0xc0}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0xda, 0x01}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_ov965x_sxga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 sensor_start_ov965x_2_qvga[][2] = { + {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x02}, /* 50 Hz banding filter */ + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0xa2, 0x96}, /* bd50 */ + {0xa3, 0x7d}, /* bd60 */ + + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, + {0x3a, 0x80}, /* tslb - yuyv */ +}; + +static const u8 sensor_start_ov965x_2_vga[][2] = { {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ @@ -592,35 +1149,36 @@ static const u8 sensor_start_ov965x_vga[][2] = { {0xa3, 0x3e}, /* bd60 */ {0x2d, 0x00}, /* advfl */ - {} }; -static const u8 sensor_start_ov965x_cif[][2] = { - {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ +static const u8 sensor_start_ov965x_2_svga[][2] = { /* same for xga */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ {0x00, 0x00}, {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ {0x11, 0x01}, /* clkrc */ {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x02}, /* 50 Hz banding filter */ - {0xc5, 0x03}, /* 60 Hz banding filter */ - {0xa2, 0x96}, /* bd50 */ - {0xa3, 0x7d}, /* bd60 */ - - {0xff, 0x13}, /* read 13, write ff 00 */ - {0x13, 0xe7}, - {0x3a, 0x80}, /* tslb - yuyv */ - {} + {0x6a, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ }; -static const u8 sensor_start_ov965x_2[][2] = { - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, /* com17 - 50 Hz filter */ - {} +static const u8 sensor_start_ov965x_2_sxga[][2] = { + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ }; - static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) { struct usb_device *udev = gspca_dev->dev; @@ -753,39 +1311,310 @@ static void sccb_w_array(struct gspca_dev *gspca_dev, } } -/* set framerate */ -static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) +/* ov772x specific controls */ +static void set_frame_rate(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + struct rate_s { + u8 fps; + u8 r11; + u8 r0d; + u8 re5; + }; + const struct rate_s *r; + static const struct rate_s rate_0[] = { /* 640x480 */ + {60, 0x01, 0xc1, 0x04}, + {50, 0x01, 0x41, 0x02}, + {40, 0x02, 0xc1, 0x04}, + {30, 0x04, 0x81, 0x02}, + {15, 0x03, 0x41, 0x04}, + }; + static const struct rate_s rate_1[] = { /* 320x240 */ + {125, 0x02, 0x81, 0x02}, + {100, 0x02, 0xc1, 0x04}, + {75, 0x03, 0xc1, 0x04}, + {60, 0x04, 0xc1, 0x04}, + {50, 0x02, 0x41, 0x04}, + {40, 0x03, 0x41, 0x04}, + {30, 0x04, 0x41, 0x04}, + }; + + if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) { + r = rate_0; + i = ARRAY_SIZE(rate_0); + } else { + r = rate_1; + i = ARRAY_SIZE(rate_1); + } + while (--i > 0) { + if (sd->frame_rate >= r->fps) + break; + r++; + } + + sccb_reg_write(gspca_dev, 0x11, r->r11); + sccb_reg_write(gspca_dev, 0x0d, r->r0d); + ov534_reg_write(gspca_dev, 0xe5, r->re5); + + PDEBUG(D_PROBE, "frame_rate: %d", r->fps); +} + +static void setbrightness_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x9B, sd->brightness); +} + +static void setcontrast_77(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int fr = sd->frame_rate; - switch (fr) { - case 50: - sccb_reg_write(gspca_dev, 0x11, 0x01); - sccb_reg_write(gspca_dev, 0x0d, 0x41); - ov534_reg_write(gspca_dev, 0xe5, 0x02); + sccb_reg_write(gspca_dev, 0x9C, sd->contrast); +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->gain; + switch (val & 0x30) { + case 0x00: + val &= 0x0f; break; - case 40: - sccb_reg_write(gspca_dev, 0x11, 0x02); - sccb_reg_write(gspca_dev, 0x0d, 0xc1); - ov534_reg_write(gspca_dev, 0xe5, 0x04); + case 0x10: + val &= 0x0f; + val |= 0x30; break; -/* case 30: */ - default: - fr = 30; - sccb_reg_write(gspca_dev, 0x11, 0x04); - sccb_reg_write(gspca_dev, 0x0d, 0x81); - ov534_reg_write(gspca_dev, 0xe5, 0x02); + case 0x20: + val &= 0x0f; + val |= 0x70; break; - case 15: - sccb_reg_write(gspca_dev, 0x11, 0x03); - sccb_reg_write(gspca_dev, 0x0d, 0x41); - ov534_reg_write(gspca_dev, 0xe5, 0x04); + default: +/* case 0x30: */ + val &= 0x0f; + val |= 0xf0; break; } + sccb_reg_write(gspca_dev, 0x00, val); +} + +static void setexposure_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->exposure; + sccb_reg_write(gspca_dev, 0x08, val >> 7); + sccb_reg_write(gspca_dev, 0x10, val << 1); +} + +static void setredblc(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x43, sd->redblc); +} + +static void setblueblc(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x42, sd->blueblc); +} + +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x01, sd->hue); +} + +static void setautogain_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->autogain) { + sccb_reg_write(gspca_dev, 0x13, 0xf7); /* AGC,AEC,AWB ON */ + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) | 0x03); + } else { + sccb_reg_write(gspca_dev, 0x13, 0xf0); /* AGC,AEC,AWB OFF */ + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) & 0xfc); + } +} + +static void setawb(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->awb) + sccb_reg_write(gspca_dev, 0x63, 0xe0); /* AWB on */ + else + sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */ +} + +static void setsharpness_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->sharpness; + sccb_reg_write(gspca_dev, 0x91, val); /* vga noise */ + sccb_reg_write(gspca_dev, 0x8e, val); /* qvga noise */ +} + +static void sethflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->hflip == 0) + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) | 0x40); + else + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) & 0xbf); +} + +static void setvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->vflip == 0) + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) | 0x80); + else + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) & 0x7f); +} + +/* ov965x specific controls */ +static void setbrightness_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->brightness; + if (val < 8) + val = 15 - val; /* f .. 8 */ + else + val = val - 8; /* 0 .. 7 */ + sccb_reg_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ + 0x0f | (val << 4)); +} + +static void setcontrast_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */ + sd->contrast << 4); +} + +static void setexposure_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; + + sccb_reg_write(gspca_dev, 0x10, /* aec[9:2] */ + expo[sd->exposure]); + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x13, val); + val = sccb_reg_read(gspca_dev, 0xa1); /* aech */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */ +} + +static void setsharpness_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->sharpness; + if (val < 0) { /* auto */ + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x42, val | 0x40); + /* Edge enhancement strength auto adjust */ + return; + } + if (val != 0) + val = 1 << (val - 1); + sccb_reg_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */ + val); + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x42, val & 0xbf); +} + +static void setautogain_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + +/*fixme: should adjust agc/awb/aec by different controls */ + val = sd->autogain; + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->autogain) + val |= 0x05; /* agc & aec */ + else + val &= 0xfa; + sccb_reg_write(gspca_dev, 0x13, val); +} + +static void setsatur(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val1, val2, val3; + static const u8 matrix[5][2] = { + {0x14, 0x38}, + {0x1e, 0x54}, + {0x28, 0x70}, + {0x32, 0x8c}, + {0x48, 0x90} + }; + + val1 = matrix[sd->satur][0]; + val2 = matrix[sd->satur][1]; + val3 = val1 + val2; + sccb_reg_write(gspca_dev, 0x4f, val3); /* matrix coeff */ + sccb_reg_write(gspca_dev, 0x50, val3); + sccb_reg_write(gspca_dev, 0x51, 0x00); + sccb_reg_write(gspca_dev, 0x52, val1); + sccb_reg_write(gspca_dev, 0x53, val2); + sccb_reg_write(gspca_dev, 0x54, val3); + sccb_reg_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */ + val1 = sccb_reg_read(gspca_dev, 0x41); /* com16 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x41, val1); +} + +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->lightfreq == 0) { + sccb_reg_write(gspca_dev, 0x13, val & 0xdf); + return; + } + sccb_reg_write(gspca_dev, 0x13, val | 0x20); - sd->frame_rate = fr; - PDEBUG(D_PROBE, "frame_rate: %d", fr); + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->lightfreq == 1) + val |= 0x01; + else + val &= 0xfe; + sccb_reg_write(gspca_dev, 0x42, val); } /* this function is called at probe time */ @@ -800,17 +1629,60 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; if (sd->sensor == SENSOR_OV772X) { - cam->cam_mode = vga_yuyv_mode; - cam->nmodes = ARRAY_SIZE(vga_yuyv_mode); + cam->cam_mode = ov772x_mode; + cam->nmodes = ARRAY_SIZE(ov772x_mode); cam->bulk = 1; cam->bulk_size = 16384; cam->bulk_nurbs = 2; } else { /* ov965x */ - cam->cam_mode = vga_jpeg_mode; - cam->nmodes = ARRAY_SIZE(vga_jpeg_mode); + cam->cam_mode = ov965x_mode; + cam->nmodes = ARRAY_SIZE(ov965x_mode); } + sd->frame_rate = 30; + + if (sd->sensor == SENSOR_OV772X) { + sd->brightness = BRIGHTNESS_77_DEF; + sd->contrast = CONTRAST_77_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPO_77_DEF; + sd->redblc = RED_BALANCE_DEF; + sd->blueblc = BLUE_BALANCE_DEF; + sd->hue = HUE_DEF; +#if AUTOGAIN_77_DEF != 0 + sd->autogain = AUTOGAIN_77_DEF; +#else + gspca_dev->ctrl_inac |= (1 << AWB_77_IDX); +#endif +#if AWB_DEF != 0 + sd->awb = AWB_DEF +#endif +#if SHARPNESS_77_DEF != 0 + sd->sharpness = SHARPNESS_77_DEF; +#endif +#if HFLIP_DEF != 0 + sd->hflip = HFLIP_DEF; +#endif +#if VFLIP_DEF != 0 + sd->vflip = VFLIP_DEF; +#endif + } else { + sd->brightness = BRIGHTNESS_96_DEF; + sd->contrast = CONTRAST_96_DEF; +#if AUTOGAIN_96_DEF != 0 + sd->autogain = AUTOGAIN_96_DEF; + gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX); +#endif +#if EXPO_96_DEF != 0 + sd->exposure = EXPO_96_DEF; +#endif +#if SHARPNESS_96_DEF != 0 + sd->sharpness = SHARPNESS_96_DEF; +#endif + sd->satur = SATUR_DEF; + sd->lightfreq = FREQ_DEF; + } return 0; } @@ -847,14 +1719,14 @@ static int sd_init(struct gspca_dev *gspca_dev) /* initialize */ switch (sd->sensor) { case SENSOR_OV772X: - reg_w_array(gspca_dev, bridge_init_ov722x, - ARRAY_SIZE(bridge_init_ov722x)); + reg_w_array(gspca_dev, bridge_init_ov772x, + ARRAY_SIZE(bridge_init_ov772x)); ov534_set_led(gspca_dev, 1); - sccb_w_array(gspca_dev, sensor_init_ov722x, - ARRAY_SIZE(sensor_init_ov722x)); + sccb_w_array(gspca_dev, sensor_init_ov772x, + ARRAY_SIZE(sensor_init_ov772x)); ov534_reg_write(gspca_dev, 0xe0, 0x09); ov534_set_led(gspca_dev, 0); - ov534_set_frame_rate(gspca_dev); + set_frame_rate(gspca_dev); break; default: /* case SENSOR_OV965X: */ @@ -875,60 +1747,115 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static int sd_start(struct gspca_dev *gspca_dev) +static int sd_start_ov772x(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; int mode; - switch (sd->sensor) { - case SENSOR_OV772X: - ov534_set_led(gspca_dev, 1); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - break; - default: -/* case SENSOR_OV965X: */ - - sccb_w_array(gspca_dev, sensor_start_ov965x, - ARRAY_SIZE(sensor_start_ov965x)); - reg_w_array(gspca_dev, bridge_start_ov965x, - ARRAY_SIZE(bridge_start_ov965x)); - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - if (mode != 0) { /* 320x240 */ - reg_w_array(gspca_dev, bridge_start_ov965x_cif, - ARRAY_SIZE(bridge_start_ov965x_cif)); - sccb_w_array(gspca_dev, sensor_start_ov965x_cif, - ARRAY_SIZE(sensor_start_ov965x_cif)); - } else { /* 640x480 */ - reg_w_array(gspca_dev, bridge_start_ov965x_vga, - ARRAY_SIZE(bridge_start_ov965x_vga)); - sccb_w_array(gspca_dev, sensor_start_ov965x_vga, - ARRAY_SIZE(sensor_start_ov965x_vga)); - } - sccb_w_array(gspca_dev, sensor_start_ov965x_2, - ARRAY_SIZE(sensor_start_ov965x_2)); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - ov534_set_led(gspca_dev, 1); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + if (mode != 0) { /* 320x240 */ + reg_w_array(gspca_dev, bridge_start_ov772x_qvga, + ARRAY_SIZE(bridge_start_ov772x_qvga)); + sccb_w_array(gspca_dev, sensor_start_ov772x_qvga, + ARRAY_SIZE(sensor_start_ov772x_qvga)); + } else { /* 640x480 */ + reg_w_array(gspca_dev, bridge_start_ov772x_vga, + ARRAY_SIZE(bridge_start_ov772x_vga)); + sccb_w_array(gspca_dev, sensor_start_ov772x_vga, + ARRAY_SIZE(sensor_start_ov772x_vga)); } + set_frame_rate(gspca_dev); + + setautogain_77(gspca_dev); + setawb(gspca_dev); + setgain(gspca_dev); + setredblc(gspca_dev); + setblueblc(gspca_dev); + sethue(gspca_dev); + setexposure_77(gspca_dev); + setbrightness_77(gspca_dev); + setcontrast_77(gspca_dev); + setsharpness_77(gspca_dev); + setvflip(gspca_dev); + sethflip(gspca_dev); + + ov534_set_led(gspca_dev, 1); + ov534_reg_write(gspca_dev, 0xe0, 0x00); return 0; } -static void sd_stopN(struct gspca_dev *gspca_dev) +static int sd_start_ov965x(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; + int mode; - switch (sd->sensor) { - case SENSOR_OV772X: - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); - break; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + switch (mode) { default: -/* case SENSOR_OV965X: */ - ov534_reg_write(gspca_dev, 0xe0, 0x01); - ov534_set_led(gspca_dev, 0); - ov534_reg_write(gspca_dev, 0xe0, 0x00); +/* case 4: * 320x240 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga, + ARRAY_SIZE(sensor_start_ov965x_1_vga)); + reg_w_array(gspca_dev, bridge_start_ov965x_qvga, + ARRAY_SIZE(bridge_start_ov965x_qvga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_qvga, + ARRAY_SIZE(sensor_start_ov965x_2_qvga)); + break; + case 3: /* 640x480 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga, + ARRAY_SIZE(sensor_start_ov965x_1_vga)); + reg_w_array(gspca_dev, bridge_start_ov965x_vga, + ARRAY_SIZE(bridge_start_ov965x_vga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_vga, + ARRAY_SIZE(sensor_start_ov965x_2_vga)); + break; + case 2: /* 800x600 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_svga, + ARRAY_SIZE(sensor_start_ov965x_1_svga)); + reg_w_array(gspca_dev, bridge_start_ov965x_svga, + ARRAY_SIZE(bridge_start_ov965x_svga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga, + ARRAY_SIZE(sensor_start_ov965x_2_svga)); + break; + case 1: /* 1024x768 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_xga, + ARRAY_SIZE(sensor_start_ov965x_1_xga)); + reg_w_array(gspca_dev, bridge_start_ov965x_xga, + ARRAY_SIZE(bridge_start_ov965x_xga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga, + ARRAY_SIZE(sensor_start_ov965x_2_svga)); + break; + case 0: /* 1280x1024 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_sxga, + ARRAY_SIZE(sensor_start_ov965x_1_sxga)); + reg_w_array(gspca_dev, bridge_start_ov965x_sxga, + ARRAY_SIZE(bridge_start_ov965x_sxga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_sxga, + ARRAY_SIZE(sensor_start_ov965x_2_sxga)); break; } + setfreq(gspca_dev); + setautogain_96(gspca_dev); + setbrightness_96(gspca_dev); + setcontrast_96(gspca_dev); + setexposure_96(gspca_dev); + setsharpness_96(gspca_dev); + setsatur(gspca_dev); + + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_set_led(gspca_dev, 1); + return 0; +} + +static void sd_stopN_ov772x(struct gspca_dev *gspca_dev) +{ + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); +} + +static void sd_stopN_ov965x(struct gspca_dev *gspca_dev) +{ + ov534_reg_write(gspca_dev, 0xe0, 0x01); + ov534_set_led(gspca_dev, 0); + ov534_reg_write(gspca_dev, 0xe0, 0x00); } /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ @@ -941,8 +1868,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) #define UVC_STREAM_EOF (1 << 1) #define UVC_STREAM_FID (1 << 0) -static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, - __u8 *data, int len) +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; __u32 this_pts; @@ -983,32 +1910,30 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* If PTS or FID has changed, start a new frame. */ if (this_pts != sd->last_pts || this_fid != sd->last_fid) { if (gspca_dev->last_packet_type == INTER_PACKET) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, frame, - NULL, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); sd->last_pts = this_pts; sd->last_fid = this_fid; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data + 12, len - 12); /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { sd->last_pts = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data + 12, len - 12); + gspca_frame_add(gspca_dev, LAST_PACKET, + data + 12, len - 12); } else { /* Add the data from this payload */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data + 12, len - 12); + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 12, len - 12); } - /* Done this payload */ goto scan_next; discard: /* Discard data until a new frame starts. */ - gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); + gspca_dev->last_packet_type = DISCARD_PACKET; scan_next: remaining_len -= len; @@ -1016,6 +1941,291 @@ scan_next: } while (remaining_len > 0); } +/* controls */ +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setexposure_77(gspca_dev); + else + setexposure_96(gspca_dev); + } + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setbrightness_77(gspca_dev); + else + setbrightness_96(gspca_dev); + } + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setcontrast_77(gspca_dev); + else + setcontrast_96(gspca_dev); + } + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->satur = val; + if (gspca_dev->streaming) + setsatur(gspca_dev); + return 0; +} + +static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->satur; + return 0; +} +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->redblc = val; + if (gspca_dev->streaming) + setredblc(gspca_dev); + return 0; +} + +static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->redblc; + return 0; +} + +static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blueblc = val; + if (gspca_dev->streaming) + setblueblc(gspca_dev); + return 0; +} + +static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blueblc; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hue; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) { + + /* the auto white balance control works only + * when auto gain is set */ + if (val) + gspca_dev->ctrl_inac &= ~(1 << AWB_77_IDX); + else + gspca_dev->ctrl_inac |= (1 << AWB_77_IDX); + setautogain_77(gspca_dev); + } else { + if (val) + gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX); + else + gspca_dev->ctrl_inac &= ~(1 << EXPO_96_IDX); + setautogain_96(gspca_dev); + } + } + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->awb = val; + if (gspca_dev->streaming) + setawb(gspca_dev); + return 0; +} + +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->awb; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setsharpness_77(gspca_dev); + else + setsharpness_96(gspca_dev); + } + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + setvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + /* get stream parameters (framerate) */ static int sd_get_streamparm(struct gspca_dev *gspca_dev, struct v4l2_streamparm *parm) @@ -1047,7 +2257,8 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev, /* Set requested framerate */ sd->frame_rate = tpf->denominator / tpf->numerator; - ov534_set_frame_rate(gspca_dev); + if (gspca_dev->streaming && sd->sensor == SENSOR_OV772X) + set_frame_rate(gspca_dev); /* Return the actual framerate */ tpf->numerator = 1; @@ -1056,20 +2267,53 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev, return 0; } +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ -static const struct sd_desc sd_desc = { +static const struct sd_desc sd_desc_ov772x = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), + .ctrls = sd_ctrls_ov772x, + .nctrls = ARRAY_SIZE(sd_ctrls_ov772x), .config = sd_config, .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, + .start = sd_start_ov772x, + .stopN = sd_stopN_ov772x, .pkt_scan = sd_pkt_scan, .get_streamparm = sd_get_streamparm, .set_streamparm = sd_set_streamparm, }; +static const struct sd_desc sd_desc_ov965x = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov965x, + .nctrls = ARRAY_SIZE(sd_ctrls_ov965x), + .config = sd_config, + .init = sd_init, + .start = sd_start_ov965x, + .stopN = sd_stopN_ov965x, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X}, @@ -1082,8 +2326,12 @@ MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); + return gspca_dev_probe(intf, id, + id->driver_info == SENSOR_OV772X + ? &sd_desc_ov772x + : &sd_desc_ov965x, + sizeof(struct sd), + THIS_MODULE); } static struct usb_driver sd_driver = { @@ -1101,6 +2349,7 @@ static struct usb_driver sd_driver = { static int __init sd_mod_init(void) { int ret; + ret = usb_register(&sd_driver); if (ret < 0) return ret; diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 96659433d248..4706a823add0 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -337,14 +337,13 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n; @@ -354,10 +353,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n -= sizeof pac_sof_marker; else n = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, n); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, n); sd->header_read = 0; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); len -= sof - data; data = sof; } @@ -381,7 +380,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, sd->header_read = 11; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c new file mode 100644 index 000000000000..74acceea8094 --- /dev/null +++ b/drivers/media/video/gspca/pac7302.c @@ -0,0 +1,1272 @@ +/* + * Pixart PAC7302 library + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * Separated from Pixart PAC7311 library by Márton Németh <nm127@freemail.hu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Some documentation about various registers as determined by trial and error. + When the register addresses differ between the 7202 and the 7311 the 2 + different addresses are written as 7302addr/7311addr, when one of the 2 + addresses is a - sign that register description is not valid for the + matching IC. + + Register page 1: + + Address Description + -/0x08 Unknown compressor related, must always be 8 except when not + in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! + -/0x1b Auto white balance related, bit 0 is AWB enable (inverted) + bits 345 seem to toggle per color gains on/off (inverted) + 0x78 Global control, bit 6 controls the LED (inverted) + -/0x80 JPEG compression ratio ? Best not touched + + Register page 3/4: + + Address Description + 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + -/0x0f Master gain 1-245, low value = high gain + 0x10/- Master gain 0-31 + -/0x10 Another gain 0-15, limited influence (1-2x gain I guess) + 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + completely disable the analog amplification block. Set to 0x68 + for max gain, 0x14 for minimal gain. + + The registers are accessed in the following functions: + + Page | Register | Function + -----+------------+--------------------------------------------------- + 0 | 0x0f..0x20 | setcolors() + 0 | 0xa2..0xab | setbrightcont() + 0 | 0xc5 | setredbalance() + 0 | 0xc6 | setwhitebalance() + 0 | 0xc7 | setbluebalance() + 0 | 0xdc | setbrightcont(), setcolors() + 3 | 0x02 | setexposure() + 3 | 0x10 | setgain() + 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() + 3 | 0x21 | sethvflip() +*/ + +#define MODULE_NAME "pac7302" + +#include <media/v4l2-chip-ident.h> +#include "gspca.h" + +MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_DESCRIPTION("Pixart PAC7302"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor for pac7302 */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char white_balance; + unsigned char red_balance; + unsigned char blue_balance; + unsigned char gain; + unsigned char exposure; + unsigned char autogain; + __u8 hflip; + __u8 vflip; + + u8 sof_read; + u8 autogain_ignore_frames; + + atomic_t avg_lum; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +/* This control is pac7302 only */ + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, +#define BRIGHTNESS_MAX 0x20 + .maximum = BRIGHTNESS_MAX, + .step = 1, +#define BRIGHTNESS_DEF 0x10 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +/* This control is for both the 7302 and the 7311 */ + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, +#define CONTRAST_MAX 255 + .maximum = CONTRAST_MAX, + .step = 1, +#define CONTRAST_DEF 127 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +/* This control is pac7302 only */ + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, +#define COLOR_MAX 255 + .maximum = COLOR_MAX, + .step = 1, +#define COLOR_DEF 127 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, + { + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "White Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define WHITEBALANCE_DEF 4 + .default_value = WHITEBALANCE_DEF, + }, + .set = sd_setwhitebalance, + .get = sd_getwhitebalance, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red", + .minimum = 0, + .maximum = 3, + .step = 1, +#define REDBALANCE_DEF 1 + .default_value = REDBALANCE_DEF, + }, + .set = sd_setredbalance, + .get = sd_getredbalance, + }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue", + .minimum = 0, + .maximum = 3, + .step = 1, +#define BLUEBALANCE_DEF 1 + .default_value = BLUEBALANCE_DEF, + }, + .set = sd_setbluebalance, + .get = sd_getbluebalance, + }, +/* All controls below are for both the 7302 and the 7311 */ + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, +#define GAIN_MAX 255 + .maximum = GAIN_MAX, + .step = 1, +#define GAIN_DEF 127 +#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, +#define EXPOSURE_MAX 255 + .maximum = EXPOSURE_MAX, + .step = 1, +#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ +#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +}; + +static const struct v4l2_pix_format vga_mode[] = { + {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +#define LOAD_PAGE3 255 +#define LOAD_PAGE4 254 +#define END_OF_SEQUENCE 0 + +/* pac 7302 */ +static const __u8 init_7302[] = { +/* index,value */ + 0xff, 0x01, /* page 1 */ + 0x78, 0x00, /* deactivate */ + 0xff, 0x01, + 0x78, 0x40, /* led off */ +}; +static const __u8 start_7302[] = { +/* index, len, [value]* */ + 0xff, 1, 0x00, /* page 0 */ + 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, + 0x00, 0x00, 0x00, 0x00, + 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, + 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, + 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, + 0x26, 2, 0xaa, 0xaa, + 0x2e, 1, 0x31, + 0x38, 1, 0x01, + 0x3a, 3, 0x14, 0xff, 0x5a, + 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, + 0x00, 0x54, 0x11, + 0x55, 1, 0x00, + 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, + 0x6b, 1, 0x00, + 0x6e, 3, 0x08, 0x06, 0x00, + 0x72, 3, 0x00, 0xff, 0x00, + 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, + 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, + 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, + 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, + 0xd2, 0xeb, + 0xaf, 1, 0x02, + 0xb5, 2, 0x08, 0x08, + 0xb8, 2, 0x08, 0x88, + 0xc4, 4, 0xae, 0x01, 0x04, 0x01, + 0xcc, 1, 0x00, + 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, + 0xc1, 0xd7, 0xec, + 0xdc, 1, 0x01, + 0xff, 1, 0x01, /* page 1 */ + 0x12, 3, 0x02, 0x00, 0x01, + 0x3e, 2, 0x00, 0x00, + 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, + 0x7c, 1, 0x00, + 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, + 0x02, 0x00, + 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, + 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, + 0xd8, 1, 0x01, + 0xdb, 2, 0x00, 0x01, + 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, + 0xe6, 4, 0x00, 0x00, 0x00, 0x01, + 0xeb, 1, 0x00, + 0xff, 1, 0x02, /* page 2 */ + 0x22, 1, 0x00, + 0xff, 1, 0x03, /* page 3 */ + 0, LOAD_PAGE3, /* load the page 3 */ + 0x11, 1, 0x01, + 0xff, 1, 0x02, /* page 2 */ + 0x13, 1, 0x00, + 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, + 0x27, 2, 0x14, 0x0c, + 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, + 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, + 0x6e, 1, 0x08, + 0xff, 1, 0x01, /* page 1 */ + 0x78, 1, 0x00, + 0, END_OF_SEQUENCE /* end of sequence */ +}; + +#define SKIP 0xaa +/* page 3 - the value SKIP says skip the index - see reg_w_page() */ +static const __u8 page3_7302[] = { + 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, + 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, + 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, + 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, + 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, + 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, + 0x00 +}; + +static int reg_w_buf(struct gspca_dev *gspca_dev, + __u8 index, + const char *buffer, int len) +{ + int ret; + + memcpy(gspca_dev->usb_buf, buffer, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 1, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, + 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w_buf(): " + "Failed to write registers to index 0x%x, error %i", + index, ret); + return ret; +} + + +static int reg_w(struct gspca_dev *gspca_dev, + __u8 index, + __u8 value) +{ + int ret; + + gspca_dev->usb_buf[0] = value; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w(): " + "Failed to write register to index 0x%x, value 0x%x, error %i", + index, value, ret); + return ret; +} + +static int reg_w_seq(struct gspca_dev *gspca_dev, + const __u8 *seq, int len) +{ + int ret = 0; + while (--len >= 0) { + if (0 <= ret) + ret = reg_w(gspca_dev, seq[0], seq[1]); + seq += 2; + } + return ret; +} + +/* load the beginning of a page */ +static int reg_w_page(struct gspca_dev *gspca_dev, + const __u8 *page, int len) +{ + int index; + int ret = 0; + + for (index = 0; index < len; index++) { + if (page[index] == SKIP) /* skip this index */ + continue; + gspca_dev->usb_buf[0] = page[index]; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w_page(): " + "Failed to write register to index 0x%x, " + "value 0x%x, error %i", + index, page[index], ret); + break; + } + } + return ret; +} + +/* output a variable sequence */ +static int reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq, + const __u8 *page3, unsigned int page3_len, + const __u8 *page4, unsigned int page4_len) +{ + int index, len; + int ret = 0; + + for (;;) { + index = *seq++; + len = *seq++; + switch (len) { + case END_OF_SEQUENCE: + return ret; + case LOAD_PAGE4: + ret = reg_w_page(gspca_dev, page4, page4_len); + break; + case LOAD_PAGE3: + ret = reg_w_page(gspca_dev, page3, page3_len); + break; + default: + if (len > USB_BUF_SZ) { + PDEBUG(D_ERR|D_STREAM, + "Incorrect variable sequence"); + return -EINVAL; + } + while (len > 0) { + if (len < 8) { + ret = reg_w_buf(gspca_dev, + index, seq, len); + if (ret < 0) + return ret; + seq += len; + break; + } + ret = reg_w_buf(gspca_dev, index, seq, 8); + seq += 8; + index += 8; + len -= 8; + } + } + if (ret < 0) + return ret; + } + /* not reached */ +} + +/* this function is called at probe time for pac7302 */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + + PDEBUG(D_CONF, "Find Sensor PAC7302"); + cam->cam_mode = vga_mode; /* only 640x480 */ + cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->white_balance = WHITEBALANCE_DEF; + sd->red_balance = REDBALANCE_DEF; + sd->blue_balance = BLUEBALANCE_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPOSURE_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; + return 0; +} + +/* This function is used by pac7302 only */ +static int setbrightcont(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + int ret; + static const __u8 max[10] = + {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, + 0xd4, 0xec}; + static const __u8 delta[10] = + {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, + 0x11, 0x0b}; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 10; i++) { + v = max[i]; + v += (sd->brightness - BRIGHTNESS_MAX) + * 150 / BRIGHTNESS_MAX; /* 200 ? */ + v -= delta[i] * sd->contrast / CONTRAST_MAX; + if (v < 0) + v = 0; + else if (v > 0xff) + v = 0xff; + if (0 <= ret) + ret = reg_w(gspca_dev, 0xa2 + i, v); + } + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + return ret; +} + +/* This function is used by pac7302 only */ +static int setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + int ret; + static const int a[9] = + {217, -212, 0, -101, 170, -67, -38, -315, 355}; + static const int b[9] = + {19, 106, 0, 19, 106, 1, 19, 106, 1}; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 9; i++) { + v = a[i] * sd->colors / COLOR_MAX + b[i]; + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f + 2 * i + 1, v); + } + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); + return ret; +} + +static int setwhitebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc6, sd->white_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance); + return ret; +} + +static int setredbalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc5, sd->red_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance); + return ret; +} + +static int setbluebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc7, sd->blue_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance); + return ret; +} + +static int setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x10, sd->gain >> 3); + + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +static int setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + __u8 reg; + + /* register 2 of frame 3/4 contains the clock divider configuring the + no fps according to the formula: 60 / reg. sd->exposure is the + desired exposure time in ms. */ + reg = 120 * sd->exposure / 1000; + if (reg < 2) + reg = 2; + else if (reg > 63) + reg = 63; + + /* On the pac7302 reg2 MUST be a multiple of 3, so round it to + the nearest multiple of 3, except when between 6 and 12? */ + if (reg < 6 || reg > 12) + reg = ((reg + 1) / 3) * 3; + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x02, reg); + + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +static int sethvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + __u8 data; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + data = (sd->hflip ? 0x08 : 0x00) | (sd->vflip ? 0x04 : 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x21, data); + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +/* this function is called at probe and resume time for pac7302 */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->sof_read = 0; + + ret = reg_w_var(gspca_dev, start_7302, + page3_7302, sizeof(page3_7302), + NULL, 0); + if (0 <= ret) + ret = setbrightcont(gspca_dev); + if (0 <= ret) + ret = setcolors(gspca_dev); + if (0 <= ret) + ret = setwhitebalance(gspca_dev); + if (0 <= ret) + ret = setredbalance(gspca_dev); + if (0 <= ret) + ret = setbluebalance(gspca_dev); + if (0 <= ret) + ret = setgain(gspca_dev); + if (0 <= ret) + ret = setexposure(gspca_dev); + if (0 <= ret) + ret = sethvflip(gspca_dev); + + /* only resolution 640x480 is supported for pac7302 */ + + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); + + /* start stream */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x01); + + return ret; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int ret; + + /* stop stream */ + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x00); +} + +/* called on streamoff with alt 0 and on disconnect for pac7302 */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + int ret; + + if (!gspca_dev->present) + return; + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x40); +} + +/* Include pac common sof detection functions */ +#include "pac_common.h" + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum = atomic_read(&sd->avg_lum); + int desired_lum, deadzone; + + if (avg_lum == -1) + return; + + desired_lum = 270 + sd->brightness * 4; + /* Hack hack, with the 7202 the first exposure step is + pretty large, so if we're about to make the first + exposure increase make the deadzone large to avoid + oscilating */ + if (desired_lum > avg_lum && sd->gain == GAIN_DEF && + sd->exposure > EXPOSURE_DEF && + sd->exposure < 42) + deadzone = 90; + else + deadzone = 30; + + if (sd->autogain_ignore_frames > 0) + sd->autogain_ignore_frames--; + else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; +} + +/* JPEG header, part 1 */ +static const unsigned char pac_jpeg_header1[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08 /* Precision: 8 */ + /* 2 bytes is placed here: number of image lines */ + /* 2 bytes is placed here: samples per line */ +}; + +/* JPEG header, continued */ +static const unsigned char pac_jpeg_header2[] = { + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ +}; + +static void pac_start_frame(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u16 lines, __u16 samples_per_line) +{ + unsigned char tmpbuf[4]; + + gspca_frame_add(gspca_dev, FIRST_PACKET, + pac_jpeg_header1, sizeof(pac_jpeg_header1)); + + tmpbuf[0] = lines >> 8; + tmpbuf[1] = lines & 0xff; + tmpbuf[2] = samples_per_line >> 8; + tmpbuf[3] = samples_per_line & 0xff; + + gspca_frame_add(gspca_dev, INTER_PACKET, + tmpbuf, sizeof(tmpbuf)); + gspca_frame_add(gspca_dev, INTER_PACKET, + pac_jpeg_header2, sizeof(pac_jpeg_header2)); +} + +/* this function is run at interrupt level */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + struct gspca_frame *frame; + unsigned char *sof; + + sof = pac_find_sof(&sd->sof_read, data, len); + if (sof) { + int n, lum_offset, footer_length; + + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* 6 bytes after the FF D9 EOF marker a number of lumination + bytes are send corresponding to different parts of the + image, the 14th and 15th byte after the EOF seem to + correspond to the center of the image */ + lum_offset = 61 + sizeof pac_sof_marker; + footer_length = 74; + + /* Finish decoding current frame */ + n = (sof - data) - (footer_length + sizeof pac_sof_marker); + if (n < 0) { + frame->data_end += n; + n = 0; + } + gspca_frame_add(gspca_dev, INTER_PACKET, + data, n); + if (gspca_dev->last_packet_type != DISCARD_PACKET && + frame->data_end[-2] == 0xff && + frame->data_end[-1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + + n = sof - data; + len -= n; + data = sof; + + /* Get average lumination */ + if (gspca_dev->last_packet_type == LAST_PACKET && + n >= lum_offset) + atomic_set(&sd->avg_lum, data[-lum_offset] + + data[-lum_offset + 1]); + else + atomic_set(&sd->avg_lum, -1); + + /* Start the new frame with the jpeg header */ + /* The PAC7302 has the image rotated 90 degrees */ + pac_start_frame(gspca_dev, frame, + gspca_dev->width, gspca_dev->height); + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightcont(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) { + setbrightcont(gspca_dev); + } + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->white_balance = val; + if (gspca_dev->streaming) + ret = setwhitebalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->white_balance; + return 0; +} + +static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->red_balance = val; + if (gspca_dev->streaming) + ret = setredbalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red_balance; + return 0; +} + +static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->blue_balance = val; + if (gspca_dev->streaming) + ret = setbluebalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue_balance; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->autogain) { + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; + if (gspca_dev->streaming) { + sd->autogain_ignore_frames = + PAC_AUTOGAIN_IGNORE_FRAMES; + setexposure(gspca_dev); + setgain(gspca_dev); + } + } + + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sd_dbg_s_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + int ret = -EINVAL; + __u8 index; + __u8 value; + + /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit + long on the USB bus) + */ + if (reg->match.type == V4L2_CHIP_MATCH_HOST && + reg->match.addr == 0 && + (reg->reg < 0x000000ff) && + (reg->val <= 0x000000ff) + ) { + /* Currently writing to page 0 is only supported. */ + /* reg_w() only supports 8bit index */ + index = reg->reg & 0x000000ff; + value = reg->val & 0x000000ff; + + /* Note that there shall be no access to other page + by any other function between the page swith and + the actual register write */ + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, index, value); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + } + return ret; +} + +static int sd_chip_ident(struct gspca_dev *gspca_dev, + struct v4l2_dbg_chip_ident *chip) +{ + int ret = -EINVAL; + + if (chip->match.type == V4L2_CHIP_MATCH_HOST && + chip->match.addr == 0) { + chip->revision = 0; + chip->ident = V4L2_IDENT_UNKNOWN; + ret = 0; + } + return ret; +} +#endif + +/* sub-driver description for pac7302 */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .set_register = sd_dbg_s_register, + .get_chip_ident = sd_chip_ident, +#endif +}; + +/* -- module initialisation -- */ +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3009)}, + {USB_DEVICE(0x093a, 0x2620)}, + {USB_DEVICE(0x093a, 0x2621)}, + {USB_DEVICE(0x093a, 0x2622)}, + {USB_DEVICE(0x093a, 0x2624)}, + {USB_DEVICE(0x093a, 0x2626)}, + {USB_DEVICE(0x093a, 0x2628)}, + {USB_DEVICE(0x093a, 0x2629)}, + {USB_DEVICE(0x093a, 0x262a)}, + {USB_DEVICE(0x093a, 0x262c)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 052714484e83..e5697a6345e8 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -57,23 +57,17 @@ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); -/* specific webcam descriptor */ +/* specific webcam descriptor for pac7311 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; unsigned char contrast; - unsigned char colors; unsigned char gain; unsigned char exposure; unsigned char autogain; __u8 hflip; __u8 vflip; - __u8 sensor; -#define SENSOR_PAC7302 0 -#define SENSOR_PAC7311 1 - u8 sof_read; u8 autogain_ignore_frames; @@ -81,12 +75,8 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); @@ -99,23 +89,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -/* This control is pac7302 only */ -#define BRIGHTNESS_IDX 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, -#define BRIGHTNESS_MAX 0x20 - .maximum = BRIGHTNESS_MAX, - .step = 1, -#define BRIGHTNESS_DEF 0x10 - .default_value = BRIGHTNESS_DEF, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, /* This control is for both the 7302 and the 7311 */ { { @@ -132,23 +105,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, -/* This control is pac7302 only */ -#define SATURATION_IDX 2 - { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, -#define COLOR_MAX 255 - .maximum = COLOR_MAX, - .step = 1, -#define COLOR_DEF 127 - .default_value = COLOR_DEF, - }, - .set = sd_setcolors, - .get = sd_getcolors, - }, /* All controls below are for both the 7302 and the 7311 */ { { @@ -244,101 +200,9 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -/* pac 7302 */ -static const __u8 init_7302[] = { -/* index,value */ - 0xff, 0x01, /* page 1 */ - 0x78, 0x00, /* deactivate */ - 0xff, 0x01, - 0x78, 0x40, /* led off */ -}; -static const __u8 start_7302[] = { -/* index, len, [value]* */ - 0xff, 1, 0x00, /* page 0 */ - 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, - 0x00, 0x00, 0x00, 0x00, - 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, - 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, - 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, - 0x26, 2, 0xaa, 0xaa, - 0x2e, 1, 0x31, - 0x38, 1, 0x01, - 0x3a, 3, 0x14, 0xff, 0x5a, - 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, - 0x00, 0x54, 0x11, - 0x55, 1, 0x00, - 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, - 0x6b, 1, 0x00, - 0x6e, 3, 0x08, 0x06, 0x00, - 0x72, 3, 0x00, 0xff, 0x00, - 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, - 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, - 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, - 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, - 0xd2, 0xeb, - 0xaf, 1, 0x02, - 0xb5, 2, 0x08, 0x08, - 0xb8, 2, 0x08, 0x88, - 0xc4, 4, 0xae, 0x01, 0x04, 0x01, - 0xcc, 1, 0x00, - 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, - 0xc1, 0xd7, 0xec, - 0xdc, 1, 0x01, - 0xff, 1, 0x01, /* page 1 */ - 0x12, 3, 0x02, 0x00, 0x01, - 0x3e, 2, 0x00, 0x00, - 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, - 0x7c, 1, 0x00, - 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, - 0x02, 0x00, - 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, - 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, - 0xd8, 1, 0x01, - 0xdb, 2, 0x00, 0x01, - 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, - 0xe6, 4, 0x00, 0x00, 0x00, 0x01, - 0xeb, 1, 0x00, - 0xff, 1, 0x02, /* page 2 */ - 0x22, 1, 0x00, - 0xff, 1, 0x03, /* page 3 */ - 0x00, 255, /* load the page 3 */ - 0x11, 1, 0x01, - 0xff, 1, 0x02, /* page 2 */ - 0x13, 1, 0x00, - 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, - 0x27, 2, 0x14, 0x0c, - 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, - 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, - 0x6e, 1, 0x08, - 0xff, 1, 0x01, /* page 1 */ - 0x78, 1, 0x00, - 0, 0 /* end of sequence */ -}; - -/* page 3 - the value 0xaa says skip the index - see reg_w_page() */ -static const __u8 page3_7302[] = { - 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, - 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, - 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, - 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, - 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, - 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, - 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, - 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, - 0x00 -}; +#define LOAD_PAGE3 255 +#define LOAD_PAGE4 254 +#define END_OF_SEQUENCE 0 /* pac 7311 */ static const __u8 init_7311[] = { @@ -378,119 +242,154 @@ static const __u8 start_7311[] = { 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00, 0x3f, 0x00, 0x0a, 0x01, 0x00, 0xff, 1, 0x04, /* page 4 */ - 0x00, 254, /* load the page 4 */ + 0, LOAD_PAGE4, /* load the page 4 */ 0x11, 1, 0x01, - 0, 0 /* end of sequence */ + 0, END_OF_SEQUENCE /* end of sequence */ }; -/* page 4 - the value 0xaa says skip the index - see reg_w_page() */ +#define SKIP 0xaa +/* page 4 - the value SKIP says skip the index - see reg_w_page() */ static const __u8 page4_7311[] = { - 0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, - 0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62, - 0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa, - 0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68, + SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, + 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62, + 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP, + SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68, 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00, 0x23, 0x28, 0x04, 0x11, 0x00, 0x00 }; -static void reg_w_buf(struct gspca_dev *gspca_dev, +static int reg_w_buf(struct gspca_dev *gspca_dev, __u8 index, const char *buffer, int len) { + int ret; + memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 1, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, len, 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w_buf(): " + "Failed to write registers to index 0x%x, error %i", + index, ret); + return ret; } -static void reg_w(struct gspca_dev *gspca_dev, +static int reg_w(struct gspca_dev *gspca_dev, __u8 index, __u8 value) { + int ret; + gspca_dev->usb_buf[0] = value; - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w(): " + "Failed to write register to index 0x%x, value 0x%x, error %i", + index, value, ret); + return ret; } -static void reg_w_seq(struct gspca_dev *gspca_dev, +static int reg_w_seq(struct gspca_dev *gspca_dev, const __u8 *seq, int len) { + int ret = 0; while (--len >= 0) { - reg_w(gspca_dev, seq[0], seq[1]); + if (0 <= ret) + ret = reg_w(gspca_dev, seq[0], seq[1]); seq += 2; } + return ret; } /* load the beginning of a page */ -static void reg_w_page(struct gspca_dev *gspca_dev, +static int reg_w_page(struct gspca_dev *gspca_dev, const __u8 *page, int len) { int index; + int ret = 0; for (index = 0; index < len; index++) { - if (page[index] == 0xaa) /* skip this index */ + if (page[index] == SKIP) /* skip this index */ continue; gspca_dev->usb_buf[0] = page[index]; - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w_page(): " + "Failed to write register to index 0x%x, " + "value 0x%x, error %i", + index, page[index], ret); + break; + } } + return ret; } /* output a variable sequence */ -static void reg_w_var(struct gspca_dev *gspca_dev, - const __u8 *seq) +static int reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq, + const __u8 *page3, unsigned int page3_len, + const __u8 *page4, unsigned int page4_len) { int index, len; + int ret = 0; for (;;) { index = *seq++; len = *seq++; switch (len) { - case 0: - return; - case 254: - reg_w_page(gspca_dev, page4_7311, sizeof page4_7311); + case END_OF_SEQUENCE: + return ret; + case LOAD_PAGE4: + ret = reg_w_page(gspca_dev, page4, page4_len); break; - case 255: - reg_w_page(gspca_dev, page3_7302, sizeof page3_7302); + case LOAD_PAGE3: + ret = reg_w_page(gspca_dev, page3, page3_len); break; default: - if (len > 64) { + if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_STREAM, "Incorrect variable sequence"); - return; + return -EINVAL; } while (len > 0) { if (len < 8) { - reg_w_buf(gspca_dev, index, seq, len); + ret = reg_w_buf(gspca_dev, + index, seq, len); + if (ret < 0) + return ret; seq += len; break; } - reg_w_buf(gspca_dev, index, seq, 8); + ret = reg_w_buf(gspca_dev, index, seq, 8); seq += 8; index += 8; len -= 8; } } + if (ret < 0) + return ret; } /* not reached */ } -/* this function is called at probe time */ +/* this function is called at probe time for pac7311 */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { @@ -499,22 +398,11 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; - sd->sensor = id->driver_info; - if (sd->sensor == SENSOR_PAC7302) { - PDEBUG(D_CONF, "Find Sensor PAC7302"); - cam->cam_mode = &vga_mode[2]; /* only 640x480 */ - cam->nmodes = 1; - } else { - PDEBUG(D_CONF, "Find Sensor PAC7311"); - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) - | (1 << SATURATION_IDX); - } + PDEBUG(D_CONF, "Find Sensor PAC7311"); + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); - sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; - sd->colors = COLOR_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPOSURE_DEF; sd->autogain = AUTOGAIN_DEF; @@ -523,91 +411,47 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* This function is used by pac7302 only */ -static void setbrightcont(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, v; - static const __u8 max[10] = - {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, - 0xd4, 0xec}; - static const __u8 delta[10] = - {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, - 0x11, 0x0b}; - - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - for (i = 0; i < 10; i++) { - v = max[i]; - v += (sd->brightness - BRIGHTNESS_MAX) - * 150 / BRIGHTNESS_MAX; /* 200 ? */ - v -= delta[i] * sd->contrast / CONTRAST_MAX; - if (v < 0) - v = 0; - else if (v > 0xff) - v = 0xff; - reg_w(gspca_dev, 0xa2 + i, v); - } - reg_w(gspca_dev, 0xdc, 0x01); -} - /* This function is used by pac7311 only */ -static void setcontrast(struct gspca_dev *gspca_dev) +static int setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, sd->contrast >> 4); + ret = reg_w(gspca_dev, 0xff, 0x04); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x10, sd->contrast >> 4); /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -/* This function is used by pac7302 only */ -static void setcolors(struct gspca_dev *gspca_dev) +static int setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i, v; - static const int a[9] = - {217, -212, 0, -101, 170, -67, -38, -315, 355}; - static const int b[9] = - {19, 106, 0, 19, 106, 1, 19, 106, 1}; - - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x11, 0x01); - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - for (i = 0; i < 9; i++) { - v = a[i] * sd->colors / COLOR_MAX + b[i]; - reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); - reg_w(gspca_dev, 0x0f + 2 * i + 1, v); - } - reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); -} + int gain = GAIN_MAX - sd->gain; + int ret; -static void setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; + if (gain < 1) + gain = 1; + else if (gain > 245) + gain = 245; + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0e, 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f, gain); - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, sd->gain >> 3); - } else { - int gain = GAIN_MAX - sd->gain; - if (gain < 1) - gain = 1; - else if (gain > 245) - gain = 245; - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, gain); - } /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -static void setexposure(struct gspca_dev *gspca_dev) +static int setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; __u8 reg; /* register 2 of frame 3/4 contains the clock divider configuring the @@ -619,97 +463,94 @@ static void setexposure(struct gspca_dev *gspca_dev) else if (reg > 63) reg = 63; - if (sd->sensor == SENSOR_PAC7302) { - /* On the pac7302 reg2 MUST be a multiple of 3, so round it to - the nearest multiple of 3, except when between 6 and 12? */ - if (reg < 6 || reg > 12) - reg = ((reg + 1) / 3) * 3; - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x02, reg); + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x02, reg); + /* Page 1 register 8 must always be 0x08 except when not in + 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && + reg <= 3) { + if (0 <= ret) + ret = reg_w(gspca_dev, 0x08, 0x09); } else { - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, reg); - /* Page 1 register 8 must always be 0x08 except when not in - 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ - reg_w(gspca_dev, 0xff, 0x01); - if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && - reg <= 3) - reg_w(gspca_dev, 0x08, 0x09); - else - reg_w(gspca_dev, 0x08, 0x08); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x08, 0x08); } + /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -static void sethvflip(struct gspca_dev *gspca_dev) +static int sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; __u8 data; - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - data = (sd->hflip ? 0x08 : 0x00) - | (sd->vflip ? 0x04 : 0x00); - } else { - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - data = (sd->hflip ? 0x04 : 0x00) - | (sd->vflip ? 0x08 : 0x00); - } - reg_w(gspca_dev, 0x21, data); + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x21, data); /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -/* this function is called at probe and resume time */ +/* this function is called at probe and resume time for pac7311 */ static int sd_init(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAC7302) - reg_w_seq(gspca_dev, init_7302, sizeof init_7302); - else - reg_w_seq(gspca_dev, init_7311, sizeof init_7311); - - return 0; + return reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2); } static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; sd->sof_read = 0; - if (sd->sensor == SENSOR_PAC7302) { - reg_w_var(gspca_dev, start_7302); - setbrightcont(gspca_dev); - setcolors(gspca_dev); - } else { - reg_w_var(gspca_dev, start_7311); - setcontrast(gspca_dev); - } - setgain(gspca_dev); - setexposure(gspca_dev); - sethvflip(gspca_dev); + ret = reg_w_var(gspca_dev, start_7311, + NULL, 0, + page4_7311, sizeof(page4_7311)); + if (0 <= ret) + ret = setcontrast(gspca_dev); + if (0 <= ret) + ret = setgain(gspca_dev); + if (0 <= ret) + ret = setexposure(gspca_dev); + if (0 <= ret) + ret = sethvflip(gspca_dev); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { case 2: /* 160x120 pac7311 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x20); - reg_w(gspca_dev, 0x87, 0x10); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x20); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x10); break; case 1: /* 320x240 pac7311 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x30); - reg_w(gspca_dev, 0x87, 0x11); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x30); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x11); break; case 0: /* 640x480 */ - if (sd->sensor == SENSOR_PAC7302) - break; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x00); - reg_w(gspca_dev, 0x87, 0x12); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x12); break; } @@ -718,47 +559,42 @@ static int sd_start(struct gspca_dev *gspca_dev) atomic_set(&sd->avg_lum, -1); /* start stream */ - reg_w(gspca_dev, 0xff, 0x01); - if (sd->sensor == SENSOR_PAC7302) - reg_w(gspca_dev, 0x78, 0x01); - else - reg_w(gspca_dev, 0x78, 0x05); - return 0; + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x05); + + return ret; } static void sd_stopN(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x00); - reg_w(gspca_dev, 0x78, 0x00); - return; - } - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x27, 0x80); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x53); - reg_w(gspca_dev, 0x2a, 0x0e); - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x3e, 0x20); - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ -} + int ret; -/* called on streamoff with alt 0 and on disconnect */ + ret = reg_w(gspca_dev, 0xff, 0x04); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x27, 0x80); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x28, 0xca); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x29, 0x53); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x2a, 0x0e); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x3e, 0x20); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ +} + +/* called on streamoff with alt 0 and on disconnect for 7311 */ static void sd_stop0(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (!gspca_dev->present) - return; - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x40); - } } /* Include pac common sof detection functions */ @@ -773,22 +609,8 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (avg_lum == -1) return; - if (sd->sensor == SENSOR_PAC7302) { - desired_lum = 270 + sd->brightness * 4; - /* Hack hack, with the 7202 the first exposure step is - pretty large, so if we're about to make the first - exposure increase make the deadzone large to avoid - oscilating */ - if (desired_lum > avg_lum && sd->gain == GAIN_DEF && - sd->exposure > EXPOSURE_DEF && - sd->exposure < 42) - deadzone = 90; - else - deadzone = 30; - } else { - desired_lum = 200; - deadzone = 20; - } + desired_lum = 200; + deadzone = 20; if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; @@ -797,53 +619,92 @@ static void do_autogain(struct gspca_dev *gspca_dev) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } -static const unsigned char pac7311_jpeg_header1[] = { - 0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08 +/* JPEG header, part 1 */ +static const unsigned char pac_jpeg_header1[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08 /* Precision: 8 */ + /* 2 bytes is placed here: number of image lines */ + /* 2 bytes is placed here: samples per line */ }; -static const unsigned char pac7311_jpeg_header2[] = { - 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, - 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +/* JPEG header, continued */ +static const unsigned char pac_jpeg_header2[] = { + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ }; +static void pac_start_frame(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u16 lines, __u16 samples_per_line) +{ + unsigned char tmpbuf[4]; + + gspca_frame_add(gspca_dev, FIRST_PACKET, + pac_jpeg_header1, sizeof(pac_jpeg_header1)); + + tmpbuf[0] = lines >> 8; + tmpbuf[1] = lines & 0xff; + tmpbuf[2] = samples_per_line >> 8; + tmpbuf[3] = samples_per_line & 0xff; + + gspca_frame_add(gspca_dev, INTER_PACKET, + tmpbuf, sizeof(tmpbuf)); + gspca_frame_add(gspca_dev, INTER_PACKET, + pac_jpeg_header2, sizeof(pac_jpeg_header2)); +} + /* this function is run at interrupt level */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; + struct gspca_frame *frame; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { - unsigned char tmpbuf[4]; int n, lum_offset, footer_length; - if (sd->sensor == SENSOR_PAC7302) { - /* 6 bytes after the FF D9 EOF marker a number of lumination - bytes are send corresponding to different parts of the - image, the 14th and 15th byte after the EOF seem to - correspond to the center of the image */ - lum_offset = 61 + sizeof pac_sof_marker; - footer_length = 74; - } else { - lum_offset = 24 + sizeof pac_sof_marker; - footer_length = 26; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; } + /* 6 bytes after the FF D9 EOF marker a number of lumination + bytes are send corresponding to different parts of the + image, the 14th and 15th byte after the EOF seem to + correspond to the center of the image */ + lum_offset = 24 + sizeof pac_sof_marker; + footer_length = 26; + /* Finish decoding current frame */ n = (sof - data) - (footer_length + sizeof pac_sof_marker); if (n < 0) { frame->data_end += n; n = 0; } - frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); if (gspca_dev->last_packet_type != DISCARD_PACKET && frame->data_end[-2] == 0xff && frame->data_end[-1] == 0xd9) - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); n = sof - data; @@ -859,43 +720,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, atomic_set(&sd->avg_lum, -1); /* Start the new frame with the jpeg header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1)); - if (sd->sensor == SENSOR_PAC7302) { - /* The PAC7302 has the image rotated 90 degrees */ - tmpbuf[0] = gspca_dev->width >> 8; - tmpbuf[1] = gspca_dev->width & 0xff; - tmpbuf[2] = gspca_dev->height >> 8; - tmpbuf[3] = gspca_dev->height & 0xff; - } else { - tmpbuf[0] = gspca_dev->height >> 8; - tmpbuf[1] = gspca_dev->height & 0xff; - tmpbuf[2] = gspca_dev->width >> 8; - tmpbuf[3] = gspca_dev->width & 0xff; - } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2)); + pac_start_frame(gspca_dev, frame, + gspca_dev->height, gspca_dev->width); } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); -} - -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightcont(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) @@ -904,10 +732,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) sd->contrast = val; if (gspca_dev->streaming) { - if (sd->sensor == SENSOR_PAC7302) - setbrightcont(gspca_dev); - else - setcontrast(gspca_dev); + setcontrast(gspca_dev); } return 0; } @@ -920,24 +745,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->colors = val; - if (gspca_dev->streaming) - setcolors(gspca_dev); - return 0; -} - -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->colors; - return 0; -} - static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1041,7 +848,7 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -/* sub-driver description */ +/* sub-driver description for pac7311 */ static struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -1057,21 +864,12 @@ static struct sd_desc sd_desc = { /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2600)}, + {USB_DEVICE(0x093a, 0x2601)}, + {USB_DEVICE(0x093a, 0x2603)}, + {USB_DEVICE(0x093a, 0x2608)}, + {USB_DEVICE(0x093a, 0x260e)}, + {USB_DEVICE(0x093a, 0x260f)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h index 34d4b1494cd5..20f67d9b8c06 100644 --- a/drivers/media/video/gspca/pac_common.h +++ b/drivers/media/video/gspca/pac_common.h @@ -33,26 +33,101 @@ static const unsigned char pac_sof_marker[5] = { 0xff, 0xff, 0x00, 0xff, 0x96 }; -static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev, +/* + The following state machine finds the SOF marker sequence + 0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream. + + +----------+ + | 0: START |<---------------\ + +----------+<-\ | + | \---/otherwise | + v 0xff | + +----------+ otherwise | + | 1 |--------------->* + | | ^ + +----------+ | + | | + v 0xff | + +----------+<-\0xff | + /->| |--/ | + | | 2 |--------------->* + | | | otherwise ^ + | +----------+ | + | | | + | v 0x00 | + | +----------+ | + | | 3 | | + | | |--------------->* + | +----------+ otherwise ^ + | | | + 0xff | v 0xff | + | +----------+ | + \--| 4 | | + | |----------------/ + +----------+ otherwise + | + v 0x96 + +----------+ + | FOUND | + +----------+ +*/ + +static unsigned char *pac_find_sof(u8 *sof_read, unsigned char *m, int len) { - struct sd *sd = (struct sd *) gspca_dev; int i; /* Search for the SOF marker (fixed part) in the header */ for (i = 0; i < len; i++) { - if (m[i] == pac_sof_marker[sd->sof_read]) { - sd->sof_read++; - if (sd->sof_read == sizeof(pac_sof_marker)) { + switch (*sof_read) { + case 0: + if (m[i] == 0xff) + *sof_read = 1; + break; + case 1: + if (m[i] == 0xff) + *sof_read = 2; + else + *sof_read = 0; + break; + case 2: + switch (m[i]) { + case 0x00: + *sof_read = 3; + break; + case 0xff: + /* stay in this state */ + break; + default: + *sof_read = 0; + } + break; + case 3: + if (m[i] == 0xff) + *sof_read = 4; + else + *sof_read = 0; + break; + case 4: + switch (m[i]) { + case 0x96: + /* Pattern found */ PDEBUG(D_FRAM, "SOF found, bytes to analyze: %u." " Frame starts at byte #%u", len, i + 1); - sd->sof_read = 0; + *sof_read = 0; return m + i + 1; + break; + case 0xff: + *sof_read = 2; + break; + default: + *sof_read = 0; } - } else { - sd->sof_read = 0; + break; + default: + *sof_read = 0; } } diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index cdad3db33367..b1944a7cbb0f 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -2342,7 +2342,6 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -2378,22 +2377,22 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, avg_lum >>= 9; atomic_set(&sd->avg_lum, avg_lum); gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, len); + data, len); return; } if (gspca_dev->last_packet_type == LAST_PACKET) { if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & MODE_JPEG) { - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else { - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } } else { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } } diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index cf3af8de6e97..5be95bc65138 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -304,7 +304,7 @@ static const __u8 initOv6650[] = { }; static const __u8 ov6650_sensor_init[][8] = { - /* Bright, contrast, etc are set througth SCBB interface. + /* Bright, contrast, etc are set through SCBB interface. * AVCAP on win2 do not send any data on this controls. */ /* Anyway, some registers appears to alter bright and constrat */ @@ -995,8 +995,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - unsigned char *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { int i; @@ -1054,12 +1053,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, pkt_type = DISCARD_PACKET; } - frame = gspca_frame_add(gspca_dev, pkt_type, - frame, data, 0); + gspca_frame_add(gspca_dev, pkt_type, + NULL, 0); data += i + fr_h_sz; len -= i + fr_h_sz; gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + data, len); return; } } @@ -1068,15 +1067,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { /* In raw mode we sometimes get some garbage after the frame ignore this */ - int used = frame->data_end - frame->data; + struct gspca_frame *frame; + int used; int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + used = frame->data_end - frame->data; if (used + len > size) len = size - used; } - gspca_frame_add(gspca_dev, INTER_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 33f4d0a1f6fd..0bd36a00dd2a 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1,8 +1,8 @@ /* - * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library - * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,8 +72,9 @@ struct sd { #define SENSOR_OV7630 5 #define SENSOR_OV7648 6 #define SENSOR_OV7660 7 -#define SENSOR_SP80708 8 - u8 i2c_base; +#define SENSOR_PO1030 8 +#define SENSOR_SP80708 9 + u8 i2c_addr; u8 *jpeg_hdr; }; @@ -250,7 +251,7 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ .step = 1, -#define FREQ_DEF 2 +#define FREQ_DEF 1 .default_value = FREQ_DEF, }, .set = sd_setfreq, @@ -277,7 +278,9 @@ static __u32 ctrl_dis[] = { (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_OV7660 7 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_SP80708 8 */ + (1 << FREQ_IDX), /* SENSOR_PO1030 8 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | + (1 << FREQ_IDX), /* SENSOR_SP80708 9 */ }; static const struct v4l2_pix_format vga_mode[] = { @@ -304,7 +307,7 @@ static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, /* reg18 reg19 reg1a reg1b */ @@ -315,7 +318,7 @@ static const u8 sn_mi0360[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, /* reg18 reg19 reg1a reg1b */ @@ -337,7 +340,7 @@ static const u8 sn_mt9v111[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x5c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40, /* reg18 reg19 reg1a reg1b */ @@ -346,7 +349,7 @@ static const u8 sn_mt9v111[0x1c] = { static const u8 sn_om6802[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20, + 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19, /* reg8 reg9 rega regb regc regd rege regf */ 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -359,7 +362,7 @@ static const u8 sn_ov7630[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2, /* reg18 reg19 reg1a reg1b */ @@ -370,7 +373,7 @@ static const u8 sn_ov7648[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00, /* reg18 reg19 reg1a reg1b */ @@ -388,11 +391,22 @@ static const u8 sn_ov7660[0x1c] = { 0x07, 0x00, 0x00, 0x00 }; +static const u8 sn_po1030[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + static const u8 sn_sp80708[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00, /* reg18 reg19 reg1a reg1b */ @@ -409,6 +423,7 @@ static const u8 *sn_tb[] = { sn_ov7630, sn_ov7648, sn_ov7660, + sn_po1030, sn_sp80708 }; @@ -455,7 +470,7 @@ static const u8 hv7131r_sensor_init[][8] = { {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, @@ -464,6 +479,8 @@ static const u8 hv7131r_sensor_init[][8] = { {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10}, + /* set sensor clock */ {} }; static const u8 mi0360_sensor_init[][8] = { @@ -545,7 +562,7 @@ static const u8 mo4000_sensor_init[][8] = { }; static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */ - /* delay 20 ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */ {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */ @@ -572,7 +589,9 @@ static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - /*******/ + {} +}; +static const u8 mt9v111_sensor_param1[][8] = { {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10}, @@ -585,14 +604,20 @@ static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */ {} }; +static const u8 om6802_init0[2][8] = { +/*fixme: variable*/ + {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10}, +}; static const u8 om6802_sensor_init[][8] = { - {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10}, + /* factory mode */ {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10}, + /* output raw RGB */ + {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, /* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */ {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10}, - /* white balance & auto-exposure */ + /* auto-exposure speed (0) / white balance mode (auto RGB) */ /* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10}, * set color mode */ /* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10}, @@ -606,26 +631,29 @@ static const u8 om6802_sensor_init[][8] = { /* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10}, * preset gamma */ {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10}, - /* luminance mode (0x4f = AE) */ + /* luminance mode (0x4f -> AutoExpo on) */ {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10}, /* preset shutter */ /* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10}, * auto frame rate */ /* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */ - -/* {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */ + {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 om6802_sensor_param1[][8] = { + {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, {} }; static const u8 ov7630_sensor_init[][8] = { {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, -/* win: delay 20ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, -/* win: delay 20ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, /* win: i2c_r from 00 to 80 */ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, @@ -677,6 +705,7 @@ static const u8 ov7630_sensor_init[][8] = { static const u8 ov7648_sensor_init[][8] = { {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10}, {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10}, @@ -701,7 +730,9 @@ static const u8 ov7648_sensor_init[][8] = { /* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ /* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ /* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */ -/*...*/ + {} +}; +static const u8 ov7648_sensor_param1[][8] = { /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ /* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN * set by setvflip */ @@ -723,7 +754,7 @@ static const u8 ov7648_sensor_init[][8] = { static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ -/* (delay 20ms) */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, /* Outformat = rawRGB */ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ @@ -783,8 +814,11 @@ static const u8 ov7660_sensor_init[][8] = { {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */ +/* not in all ms-win traces*/ {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10}, -/****** (some exchanges in the win trace) ******/ + {} +}; +static const u8 ov7660_sensor_param1[][8] = { {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */ /* bits[3..0]reserved */ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, @@ -797,6 +831,7 @@ static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */ /* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */ /****** (some exchanges in the win trace) ******/ +/*fixme:param2*/ {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */ {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */ @@ -804,6 +839,7 @@ static const u8 ov7660_sensor_init[][8] = { /* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */ /****** (some exchanges in the win trace) ******/ /******!! startsensor KO if changed !!****/ +/*fixme: param3*/ {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, @@ -811,6 +847,60 @@ static const u8 ov7660_sensor_init[][8] = { {} }; +static const u8 po1030_sensor_init[][8] = { +/* the sensor registers are described in m5602/m5602_po1030.h */ + {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10}, + {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10}, + {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, + {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */ + {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10}, + {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */ + {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10}, + {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */ + {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10}, + {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10}, + {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, + {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10}, + {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10}, + {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10}, + {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */ + {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10}, + + {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10}, + {} +}; +static const u8 po1030_sensor_param1[][8] = { +/* from ms-win traces - these values change with auto gain/expo/wb.. */ + {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, +/* mean values */ + {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */ + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */ + {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */ + + {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */ + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */ + {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10}, +/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */ + {} +}; + static const u8 sp80708_sensor_init[][8] = { {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10}, @@ -883,7 +973,9 @@ static const u8 sp80708_sensor_init[][8] = { {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10}, - /********/ + {} +}; +static const u8 sp80708_sensor_param1[][8] = { {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10}, @@ -894,6 +986,19 @@ static const u8 sp80708_sensor_init[][8] = { {} }; +static const u8 (*sensor_init[10])[8] = { + hv7131r_sensor_init, /* HV7131R 0 */ + mi0360_sensor_init, /* MI0360 1 */ + mo4000_sensor_init, /* MO4000 2 */ + mt9v111_sensor_init, /* MT9V111 3 */ + om6802_sensor_init, /* OM6802 4 */ + ov7630_sensor_init, /* OV7630 5 */ + ov7648_sensor_init, /* OV7648 6 */ + ov7660_sensor_init, /* OV7660 7 */ + po1030_sensor_init, /* PO1030 8 */ + sp80708_sensor_init, /* SP80708 9 */ +}; + /* read <len> bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, u16 value, int len) @@ -958,8 +1063,15 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) struct sd *sd = (struct sd *) gspca_dev; PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val); - gspca_dev->usb_buf[0] = 0x81 | (2 << 4); /* = a1 */ - gspca_dev->usb_buf[1] = sd->i2c_base; + switch (sd->sensor) { + case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */ + gspca_dev->usb_buf[0] = 0x80 | (2 << 4); + break; + default: /* i2c command = a1 (400 kHz) */ + gspca_dev->usb_buf[0] = 0x81 | (2 << 4); + break; + } + gspca_dev->usb_buf[1] = sd->i2c_addr; gspca_dev->usb_buf[2] = reg; gspca_dev->usb_buf[3] = val; gspca_dev->usb_buf[4] = 0; @@ -991,14 +1103,21 @@ static void i2c_w8(struct gspca_dev *gspca_dev, msleep(2); } -/* read 5 bytes in gspca_dev->usb_buf */ -static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg) +/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */ +static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len) { struct sd *sd = (struct sd *) gspca_dev; u8 mode[8]; - mode[0] = 0x81 | 0x10; - mode[1] = sd->i2c_base; + switch (sd->sensor) { + case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */ + mode[0] = 0x80 | 0x10; + break; + default: /* i2c command = 91 (400 kHz) */ + mode[0] = 0x81 | 0x10; + break; + } + mode[1] = sd->i2c_addr; mode[2] = reg; mode[3] = 0; mode[4] = 0; @@ -1007,33 +1126,43 @@ static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg) mode[7] = 0x10; i2c_w8(gspca_dev, mode); msleep(2); - mode[0] = 0x81 | (5 << 4) | 0x02; + mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02; mode[2] = 0; i2c_w8(gspca_dev, mode); msleep(2); reg_r(gspca_dev, 0x0a, 5); } -static int hv7131r_probe(struct gspca_dev *gspca_dev) +static void i2c_w_seq(struct gspca_dev *gspca_dev, + const u8 (*data)[8]) +{ + while ((*data)[0] != 0) { + if ((*data)[0] != 0xdd) + i2c_w8(gspca_dev, *data); + else + msleep((*data)[1]); + data++; + } +} + +static void hv7131r_probe(struct gspca_dev *gspca_dev) { i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ msleep(10); reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */ msleep(10); - i2c_r5(gspca_dev, 0); /* read sensor id */ + i2c_r(gspca_dev, 0, 5); /* read sensor id */ if (gspca_dev->usb_buf[0] == 0x02 && gspca_dev->usb_buf[1] == 0x09 && gspca_dev->usb_buf[2] == 0x01 && gspca_dev->usb_buf[3] == 0x00 && gspca_dev->usb_buf[4] == 0x00) { - PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R"); - return 0; + PDEBUG(D_PROBE, "Sensor sn9c102P HV7131R found"); + return; } - PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x", + PDEBUG(D_PROBE, "Sensor 0x%02x 0x%02x 0x%02x - sn9c102P not found", gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], gspca_dev->usb_buf[2]); - PDEBUG(D_PROBE, "Sensor sn9c102P Not found"); - return -ENODEV; } static void mi0360_probe(struct gspca_dev *gspca_dev) @@ -1075,7 +1204,6 @@ static void mi0360_probe(struct gspca_dev *gspca_dev) case 0x823a: PDEBUG(D_PROBE, "Sensor mt9v111"); sd->sensor = SENSOR_MT9V111; - sd->i2c_base = 0x5c; break; case 0x8243: PDEBUG(D_PROBE, "Sensor mi0360"); @@ -1086,7 +1214,42 @@ static void mi0360_probe(struct gspca_dev *gspca_dev) } } -static int configure_gpio(struct gspca_dev *gspca_dev, +static void ov7648_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* check ov76xx */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x0a, 2); + if (gspca_dev->usb_buf[3] == 0x76) { /* ov76xx */ + PDEBUG(D_PROBE, "Sensor ov%02x%02x", + gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); + return; + } + + /* reset */ + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + + /* check po1030 */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x6e; + i2c_r(gspca_dev, 0x00, 2); + if (gspca_dev->usb_buf[3] == 0x10 /* po1030 */ + && gspca_dev->usb_buf[4] == 0x30) { + PDEBUG(D_PROBE, "Sensor po1030"); + sd->sensor = SENSOR_PO1030; + return; + } + + PDEBUG(D_PROBE, "Unknown sensor %02x%02x", + gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); +} + +static void bridge_init(struct gspca_dev *gspca_dev, const u8 *sn9c1xx) { struct sd *sd = (struct sd *) gspca_dev; @@ -1103,9 +1266,10 @@ static int configure_gpio(struct gspca_dev *gspca_dev, /* configure gpio */ reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); - reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm len was 3 */ + reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); switch (sd->sensor) { case SENSOR_OV7660: + case SENSOR_PO1030: case SENSOR_SP80708: reg9a = reg9a_spec; break; @@ -1115,7 +1279,7 @@ static int configure_gpio(struct gspca_dev *gspca_dev, } reg_w(gspca_dev, 0x9a, reg9a, 6); - reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); /*fixme:jfm was 60 only*/ + reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); @@ -1127,10 +1291,22 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x40); break; case SENSOR_OM6802: - reg_w1(gspca_dev, 0x02, 0x71); - reg_w1(gspca_dev, 0x01, 0x42); + msleep(10); + reg_w1(gspca_dev, 0x02, 0x73); + reg_w1(gspca_dev, 0x17, 0x60); + reg_w1(gspca_dev, 0x01, 0x22); + msleep(100); + reg_w1(gspca_dev, 0x01, 0x62); + reg_w1(gspca_dev, 0x17, 0x64); reg_w1(gspca_dev, 0x17, 0x64); reg_w1(gspca_dev, 0x01, 0x42); + msleep(10); + reg_w1(gspca_dev, 0x01, 0x42); + i2c_w8(gspca_dev, om6802_init0[0]); + i2c_w8(gspca_dev, om6802_init0[1]); + msleep(15); + reg_w1(gspca_dev, 0x02, 0x71); + msleep(150); break; case SENSOR_OV7630: reg_w1(gspca_dev, 0x01, 0x61); @@ -1144,7 +1320,14 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x62); reg_w1(gspca_dev, 0x01, 0x42); break; + case SENSOR_PO1030: + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + break; case SENSOR_OV7660: + /* fall thru */ case SENSOR_SP80708: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); @@ -1153,143 +1336,18 @@ static int configure_gpio(struct gspca_dev *gspca_dev, msleep(100); reg_w1(gspca_dev, 0x02, 0x62); break; + default: /* case SENSOR_HV7131R: */ /* case SENSOR_MI0360: */ /* case SENSOR_MO4000: */ - default: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x61); reg_w1(gspca_dev, 0x01, 0x42); - if (sd->sensor == SENSOR_HV7131R) { - if (hv7131r_probe(gspca_dev) < 0) - return -ENODEV; - } + if (sd->sensor == SENSOR_HV7131R + && sd->bridge == BRIDGE_SN9C102P) + hv7131r_probe(gspca_dev); break; } - return 0; -} - -static void hv7131R_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - static const u8 SetSensorClk[] = /* 0x08 Mclk */ - { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 }; - - while (hv7131r_sensor_init[i][0]) { - i2c_w8(gspca_dev, hv7131r_sensor_init[i]); - i++; - } - i2c_w8(gspca_dev, SetSensorClk); -} - -static void mi0360_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (mi0360_sensor_init[i][0]) { - i2c_w8(gspca_dev, mi0360_sensor_init[i]); - i++; - } -} - -static void mo4000_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (mo4000_sensor_init[i][0]) { - i2c_w8(gspca_dev, mo4000_sensor_init[i]); - i++; - } -} - -static void mt9v111_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, mt9v111_sensor_init[i]); - i++; - msleep(20); - while (mt9v111_sensor_init[i][0]) { - i2c_w8(gspca_dev, mt9v111_sensor_init[i]); - i++; - } -} - -static void om6802_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (om6802_sensor_init[i][0]) { - i2c_w8(gspca_dev, om6802_sensor_init[i]); - i++; - } -} - -static void ov7630_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 76 01 */ - i++; - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 (RGB+SRST) */ - i++; - msleep(20); - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ - i++; - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 */ - i++; - msleep(20); - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ - i++; -/*jfm:win i2c_r from 00 to 80*/ - - while (ov7630_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7630_sensor_init[i]); - i++; - } -} - -static void ov7648_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7648_sensor_init[i]); - i++; -/* win: dble reset */ - i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */ - i++; - msleep(20); -/* win: i2c reg read 00..7f */ - while (ov7648_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7648_sensor_init[i]); - i++; - } -} - -static void ov7660_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7660_sensor_init[i]); /* reset SCCB */ - i++; - msleep(20); - while (ov7660_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7660_sensor_init[i]); - i++; - } -} - -static void sp80708_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, sp80708_sensor_init[i]); /* reset SCCB */ - i++; - msleep(20); - while (sp80708_sensor_init[i][0]) { - i2c_w8(gspca_dev, sp80708_sensor_init[i]); - i++; - } } /* this function is called at probe time */ @@ -1305,8 +1363,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->npkt = 24; /* 24 packets per ISOC message */ sd->bridge = id->driver_info >> 16; - sd->sensor = id->driver_info >> 8; - sd->i2c_base = id->driver_info; + sd->sensor = id->driver_info; sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; @@ -1322,7 +1379,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->quality = QUALITY_DEF; sd->jpegqual = 80; - gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; return 0; } @@ -1330,6 +1386,7 @@ static int sd_config(struct gspca_dev *gspca_dev, static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + const u8 *sn9c1xx; u8 regGpio[] = { 0x29, 0x74 }; u8 regF1; @@ -1356,8 +1413,14 @@ static int sd_init(struct gspca_dev *gspca_dev) case BRIDGE_SN9C120: if (regF1 != 0x12) return -ENODEV; - if (sd->sensor == SENSOR_MI0360) + switch (sd->sensor) { + case SENSOR_MI0360: mi0360_probe(gspca_dev); + break; + case SENSOR_OV7648: + ov7648_probe(gspca_dev); + break; + } regGpio[1] = 0x70; reg_w(gspca_dev, 0x01, regGpio, 2); break; @@ -1372,6 +1435,12 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xf1, 0x01); + /* set the i2c address */ + sn9c1xx = sn_tb[sd->sensor]; + sd->i2c_addr = sn9c1xx[9]; + + gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; + return 0; } @@ -1383,7 +1452,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, switch (sd->sensor) { case SENSOR_HV7131R: { u8 Expodoit[] = - { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 }; + { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 }; Expodoit[3] = expo >> 16; Expodoit[4] = expo >> 8; @@ -1393,7 +1462,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, } case SENSOR_MI0360: { u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ - { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 }; + { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 }; static const u8 doit[] = /* update sensor */ { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; static const u8 sensorgo[] = /* sensor on */ @@ -1412,9 +1481,9 @@ static u32 setexposure(struct gspca_dev *gspca_dev, } case SENSOR_MO4000: { u8 expoMof[] = - { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 }; + { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 }; u8 expoMo10[] = - { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 }; + { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 }; static const u8 gainMo[] = { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; @@ -1450,6 +1519,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, case SENSOR_OM6802: { u8 gainOm[] = { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 }; + /* preset AGC - works when AutoExpo = off */ if (expo > 0x03ff) expo = 0x03ff; @@ -1457,7 +1527,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, expo = 0x0001; gainOm[3] = expo >> 2; i2c_w8(gspca_dev, gainOm); - reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f); + reg_w1(gspca_dev, 0x96, expo >> 5); PDEBUG(D_FRAM, "set exposure %d", gainOm[3]); break; } @@ -1489,7 +1559,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SENSOR_MT9V111: expo = sd->brightness >> 8; sd->exposure = setexposure(gspca_dev, expo); - break; + return; /* don't set the Y offset */ case SENSOR_OM6802: expo = sd->brightness >> 6; sd->exposure = setexposure(gspca_dev, expo); @@ -1497,8 +1567,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) break; } - if (sd->sensor != SENSOR_MT9V111) - reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ + reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1526,6 +1595,7 @@ static void setcolors(struct gspca_dev *gspca_dev) -24, -38, 64, /* UR UG UB */ 62, -51, -9 /* VR VG VB */ }; + for (i = 0; i < 6; i++) { v = uv[i] * sd->colors / COLOR_DEF; reg8a[i * 2] = v; @@ -1605,6 +1675,8 @@ static void setvflip(struct sd *sd) { u8 comn; + if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX)) + return; if (sd->sensor == SENSOR_OV7630) { comn = 0x02; if (!sd->vflip) @@ -1726,8 +1798,9 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - u8 reg1, reg17; + u8 reg1, reg2, reg17; const u8 *sn9c1xx; + const u8 (*init)[8]; int mode; static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; @@ -1743,8 +1816,26 @@ static int sd_start(struct gspca_dev *gspca_dev) 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); - sn9c1xx = sn_tb[(int) sd->sensor]; - configure_gpio(gspca_dev, sn9c1xx); + /* initialize the bridge */ + sn9c1xx = sn_tb[sd->sensor]; + bridge_init(gspca_dev, sn9c1xx); + + /* initialize the sensor */ + i2c_w_seq(gspca_dev, sensor_init[sd->sensor]); + + switch (sd->sensor) { + case SENSOR_OM6802: + reg2 = 0x71; + break; + case SENSOR_SP80708: + reg2 = 0x62; + break; + default: + reg2 = 0x40; + break; + } + reg_w1(gspca_dev, 0x02, reg2); + reg_w1(gspca_dev, 0x02, reg2); reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); @@ -1771,6 +1862,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7660: reg17 = 0xa0; break; + case SENSOR_PO1030: + reg17 = 0xa0; + break; default: reg17 = 0x60; break; @@ -1791,6 +1885,10 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x9a, 0x07); reg_w1(gspca_dev, 0x99, 0x59); break; + case SENSOR_OM6802: + reg_w1(gspca_dev, 0x9a, 0x08); + reg_w1(gspca_dev, 0x99, 0x10); + break; case SENSOR_OV7648: reg_w1(gspca_dev, 0x9a, 0x0a); reg_w1(gspca_dev, 0x99, 0x60); @@ -1806,21 +1904,20 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg_w(gspca_dev, 0x84, reg84, sizeof reg84); + reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */ + reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */ + reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */ + + init = NULL; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; if (mode) reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */ else reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */ reg17 = 0x61; /* 0x:20: enable sensor clock */ switch (sd->sensor) { - case SENSOR_HV7131R: - hv7131R_InitSensor(gspca_dev); - break; - case SENSOR_MI0360: - mi0360_InitSensor(gspca_dev); - break; case SENSOR_MO4000: - mo4000_InitSensor(gspca_dev); if (mode) { /* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ reg1 = 0x06; /* clk 24Mz */ @@ -1830,7 +1927,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; case SENSOR_MT9V111: - mt9v111_InitSensor(gspca_dev); + init = mt9v111_sensor_param1; if (mode) { reg1 = 0x04; /* 320 clk 48Mhz */ } else { @@ -1839,22 +1936,21 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; case SENSOR_OM6802: - om6802_InitSensor(gspca_dev); + init = om6802_sensor_param1; reg17 = 0x64; /* 640 MCKSIZE */ break; case SENSOR_OV7630: - ov7630_InitSensor(gspca_dev); setvflip(sd); reg17 = 0xe2; reg1 = 0x44; break; case SENSOR_OV7648: - ov7648_InitSensor(gspca_dev); + init = ov7648_sensor_param1; reg17 = 0x21; /* reg1 = 0x42; * 42 - 46? */ break; case SENSOR_OV7660: - ov7660_InitSensor(gspca_dev); + init = ov7660_sensor_param1; if (sd->bridge == BRIDGE_SN9C120) { if (mode) { /* 320x240 - 160x120 */ reg17 = 0xa2; @@ -1866,9 +1962,14 @@ static int sd_start(struct gspca_dev *gspca_dev) * inverse power down */ } break; + case SENSOR_PO1030: + init = po1030_sensor_param1; + reg17 = 0xa2; + reg1 = 0x44; + break; default: /* case SENSOR_SP80708: */ - sp80708_InitSensor(gspca_dev); + init = sp80708_sensor_param1; if (mode) { /*?? reg1 = 0x04; * 320 clk 48Mhz */ } else { @@ -1877,6 +1978,13 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; } + + /* more sensor initialization - param1 */ + if (init != NULL) { + i2c_w_seq(gspca_dev, init); +/* init = NULL; */ + } + reg_w(gspca_dev, 0xc0, C0, 6); reg_w(gspca_dev, 0xca, CA, 4); switch (sd->sensor) { @@ -1891,6 +1999,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } + /* here change size mode 0 -> VGA; 1 -> CIF */ sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40; reg_w1(gspca_dev, 0x18, sd->reg18); @@ -1898,6 +2007,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x17, reg17); reg_w1(gspca_dev, 0x01, reg1); + switch (sd->sensor) { case SENSOR_OV7630: setvflip(sd); @@ -1937,14 +2047,11 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* fall thru */ case SENSOR_MT9V111: case SENSOR_OV7630: + case SENSOR_PO1030: data = 0x29; break; - default: -/* case SENSOR_MO4000: */ -/* case SENSOR_OV7660: */ - break; } - sn9c1xx = sn_tb[(int) sd->sensor]; + sn9c1xx = sn_tb[sd->sensor]; reg_w1(gspca_dev, 0x01, sn9c1xx[1]); reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]); reg_w1(gspca_dev, 0x01, sn9c1xx[1]); @@ -1987,11 +2094,19 @@ static void do_autogain(struct gspca_dev *gspca_dev) sd->exposure = setexposure(gspca_dev, (unsigned int) (expotimes << 8)); break; + case SENSOR_OM6802: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 2; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) expotimes); + setredblue(gspca_dev); + break; default: /* case SENSOR_MO4000: */ /* case SENSOR_MI0360: */ /* case SENSOR_MT9V111: */ -/* case SENSOR_OM6802: */ expotimes = sd->exposure; expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) @@ -2007,7 +2122,6 @@ static void do_autogain(struct gspca_dev *gspca_dev) /* scan the URB packets */ /* This function is run at interrupt level. */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -2019,7 +2133,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* end of frame */ gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, sof + 2); + data, sof + 2); if (sd->ag_cnt < 0) return; /* w1 w2 w3 */ @@ -2042,10 +2156,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (gspca_dev->last_packet_type == LAST_PACKET) { /* put the JPEG 422 header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) @@ -2295,69 +2409,69 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define BSI(bridge, sensor, i2c_addr) \ +#define BS(bridge, sensor) \ .driver_info = (BRIDGE_ ## bridge << 16) \ - | (SENSOR_ ## sensor << 8) \ - | (i2c_addr) + | SENSOR_ ## sensor static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)}, - {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)}, + {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)}, + {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)}, #endif - {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, - {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, - {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)}, - {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x06f8, 0x3008), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)}, -/* bw600.inf: - {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */ -/* {USB_DEVICE(0x0c45, 0x603a), BSI(SN9C102P, OV7648, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x607a), BSI(SN9C102P, OV7648, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)}, -/* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)}, -/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6802, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)}, + {USB_DEVICE(0x045e, 0x00f5), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x045e, 0x00f7), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)}, +/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */ + {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)}, +/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */ +/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */ + {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)}, +/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */ + {USB_DEVICE(0x0c45, 0x60c0), BS(SN9C105, MI0360)}, +/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */ +/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */ +/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */ + {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)}, +/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */ +/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */ + {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, - {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, + {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)}, + {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, #endif - {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */ -/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */ - {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610e), BSI(SN9C120, OV7630, 0x21)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ +/* {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, P1030xC)}, */ +/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */ + {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/ +/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */ +/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */ +/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */ + {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/ /*bw600.inf:*/ - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/ - {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)}, - {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)}, + {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)}, +/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, + {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)}, #endif -/* {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */ - {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, - {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, +/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */ + {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)}, + {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, + {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)}, #endif - {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, - {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/ - {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/ - {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)}, + {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)}, +/* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index fab7ef85a6c1..fe46868a87f2 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -589,7 +589,7 @@ static void spca500_reinit(struct gspca_dev *gspca_dev) int err; __u8 Data; - /* some unknow command from Aiptek pocket dv and family300 */ + /* some unknown command from Aiptek pocket dv and family300 */ reg_w(gspca_dev, 0x00, 0x0d01, 0x01); reg_w(gspca_dev, 0x00, 0x0d03, 0x00); @@ -899,8 +899,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -913,11 +912,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* gspca_dev->last_packet_type = DISCARD_PACKET; */ return; } - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, ffd9, 2); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); data += SPCA500_OFFSET_DATA; @@ -931,7 +930,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, i = 0; do { if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, i + 1); len -= i; data += i; @@ -940,7 +939,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } i++; } while (i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index b74a34218da0..6761a3048a98 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -1636,7 +1636,7 @@ static const __u16 spca501c_arowana_init_data[][3] = { {} }; -/* Unknow camera from Ori Usbid 0x0000:0x0000 */ +/* Unknown camera from Ori Usbid 0x0000:0x0000 */ /* Based on snoops from Ori Cohen */ static const __u16 spca501c_mysterious_open_data[][3] = { {0x02, 0x000f, 0x0005}, @@ -1945,7 +1945,7 @@ static int sd_init(struct gspca_dev *gspca_dev) goto error; break; case MystFromOriUnknownCamera: - /* UnKnow Ori CMOS Camera data */ + /* Unknown Ori CMOS Camera data */ if (write_vector(gspca_dev, spca501c_mysterious_open_data)) goto error; break; @@ -1978,7 +1978,7 @@ static int sd_start(struct gspca_dev *gspca_dev) write_vector(gspca_dev, spca501c_arowana_open_data); break; case MystFromOriUnknownCamera: - /* UnKnow CMOS Camera data */ + /* Unknown CMOS Camera data */ write_vector(gspca_dev, spca501c_mysterious_init_data); break; default: @@ -2032,20 +2032,15 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA501_OFFSET_DATA; len -= SPCA501_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ @@ -2053,8 +2048,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } data++; len--; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index ea8c9fe2e961..0f9232ff1281 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -739,26 +739,22 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ break; default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index a199298a6419..ab28cc23e415 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -543,18 +543,15 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ @@ -562,8 +559,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 9696c4caf5c9..4d8e6cf75d55 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -1447,26 +1447,22 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA508_OFFSET_DATA; len -= SPCA508_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ break; default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index 27e82b35f3e7..58c2f0039af1 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -779,8 +779,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -788,12 +787,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, len--; switch (*data++) { /* sequence number */ case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); if (data[1] & 0x10) { /* compressed bayer */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } else { /* raw bayer (with a header, which we skip) */ if (sd->chip_revision == Rev012A) { @@ -803,14 +800,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += 16; len -= 16; } - gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } return; case 0xff: /* drop (empty mpackets) */ return; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } /* rev 72a only */ diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 715a68f0156e..1fcaca6a87f7 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -168,18 +168,22 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev) * request and read a block of data - see warning on sq905_command. */ static int -sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size) +sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock) { int ret; int act_len; gspca_dev->usb_buf[0] = '\0'; + if (need_lock) + mutex_lock(&gspca_dev->usb_lock); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), USB_REQ_SYNCH_FRAME, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, SQ905_BULK_READ, size, gspca_dev->usb_buf, 1, SQ905_CMD_TIMEOUT); + if (need_lock) + mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) { PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret); return ret; @@ -210,11 +214,9 @@ static void sq905_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int bytes_left; /* bytes remaining in current frame. */ int data_len; /* size to use for the next read. */ int header_read; /* true if we have already read the frame header. */ - int discarding; /* true if we failed to get space for frame. */ int packet_type; int frame_sz; int ret; @@ -222,7 +224,6 @@ static void sq905_dostream(struct work_struct *work) u8 *buffer; buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); - mutex_lock(&gspca_dev->usb_lock); if (!buffer) { PDEBUG(D_ERR, "Couldn't allocate USB buffer"); goto quit_stream; @@ -232,28 +233,22 @@ static void sq905_dostream(struct work_struct *work) + FRAME_HEADER_LEN; while (gspca_dev->present && gspca_dev->streaming) { - /* Need a short delay to ensure streaming flag was set by - * gspca and to make sure gspca can grab the mutex. */ - mutex_unlock(&gspca_dev->usb_lock); - msleep(1); - /* request some data and then read it until we have * a complete frame. */ bytes_left = frame_sz; header_read = 0; - discarding = 0; - while (bytes_left > 0) { + /* Note we do not check for gspca_dev->streaming here, as + we must finish reading an entire frame, otherwise the + next time we stream we start reading in the middle of a + frame. */ + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > SQ905_MAX_TRANSFER ? SQ905_MAX_TRANSFER : bytes_left; - mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present) - goto quit_stream; - ret = sq905_read_data(gspca_dev, buffer, data_len); + ret = sq905_read_data(gspca_dev, buffer, data_len, 1); if (ret < 0) goto quit_stream; - mutex_unlock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, + PDEBUG(D_PACK, "Got %d bytes out of %d for frame", data_len, bytes_left); bytes_left -= data_len; @@ -270,34 +265,30 @@ static void sq905_dostream(struct work_struct *work) } else { packet_type = INTER_PACKET; } - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) { - frame = gspca_frame_add(gspca_dev, packet_type, - frame, data, data_len); - /* If entire frame fits in one packet we still - need to add a LAST_PACKET */ - if (packet_type == FIRST_PACKET && - bytes_left == 0) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, data, 0); - } else { - discarding = 1; - } + gspca_frame_add(gspca_dev, packet_type, + data, data_len); + /* If entire frame fits in one packet we still + need to add a LAST_PACKET */ + if (packet_type == FIRST_PACKET && + bytes_left == 0) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + } + if (gspca_dev->present) { + /* acknowledge the frame */ + mutex_lock(&gspca_dev->usb_lock); + ret = sq905_ack_frame(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + goto quit_stream; } - /* acknowledge the frame */ - mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present) - goto quit_stream; - ret = sq905_ack_frame(gspca_dev); - if (ret < 0) - goto quit_stream; } quit_stream: - /* the usb_lock is already acquired */ - if (gspca_dev->present) + if (gspca_dev->present) { + mutex_lock(&gspca_dev->usb_lock); sq905_command(gspca_dev, SQ905_CLEAR); - mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + } kfree(buffer); } @@ -346,7 +337,7 @@ static int sd_init(struct gspca_dev *gspca_dev) ret = sq905_command(gspca_dev, SQ905_ID); if (ret < 0) return ret; - ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4); + ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0); if (ret < 0) return ret; /* usb_buf is allocated with kmalloc so is aligned. diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index 916892505432..d70b156872d6 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -115,11 +115,9 @@ static void sq905c_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int bytes_left; /* bytes remaining in current frame. */ int data_len; /* size to use for the next read. */ int act_len; - int discarding = 0; /* true if we failed to get space for frame. */ int packet_type; int ret; u8 *buffer; @@ -131,8 +129,6 @@ static void sq905c_dostream(struct work_struct *work) } while (gspca_dev->present && gspca_dev->streaming) { - if (!gspca_dev->present) - goto quit_stream; /* Request the header, which tells the size to download */ ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), @@ -149,17 +145,11 @@ static void sq905c_dostream(struct work_struct *work) PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left); /* We keep the header. It has other information, too. */ packet_type = FIRST_PACKET; - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) { - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, FRAME_HEADER_LEN); - } else - discarding = 1; - while (bytes_left > 0) { + gspca_frame_add(gspca_dev, packet_type, + buffer, FRAME_HEADER_LEN); + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > SQ905C_MAX_TRANSFER ? SQ905C_MAX_TRANSFER : bytes_left; - if (!gspca_dev->present) - goto quit_stream; ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), buffer, data_len, &act_len, @@ -174,19 +164,16 @@ static void sq905c_dostream(struct work_struct *work) packet_type = LAST_PACKET; else packet_type = INTER_PACKET; - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, data_len); - else - discarding = 1; + gspca_frame_add(gspca_dev, packet_type, + buffer, data_len); } } quit_stream: - mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->present) + if (gspca_dev->present) { + mutex_lock(&gspca_dev->usb_lock); sq905c_command(gspca_dev, SQ905C_CLEAR, 0); - mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + } kfree(buffer); } diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 47628964801e..8e23320d7ab7 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -418,8 +418,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -435,11 +434,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * (without ending - ff d9) */ if (data[0] == 0xff && data[1] == 0xfe) { - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - ffd9, 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); /* put the JPEG 411 header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); /* beginning of the frame */ @@ -447,7 +446,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += STKHDRSZ; len -= STKHDRSZ; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c new file mode 100644 index 000000000000..2a69d7ccb50d --- /dev/null +++ b/drivers/media/video/gspca/stv0680.c @@ -0,0 +1,364 @@ +/* + * STV0680 USB Camera Driver + * + * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com> + * + * This module is adapted from the in kernel v4l1 stv680 driver: + * + * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) + * + * Thanks to STMicroelectronics for information on the usb commands, and + * to Steve Miller at STM for his help and encouragement while I was + * writing this driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "stv0680" + +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>"); +MODULE_DESCRIPTION("STV0680 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_pix_format mode; + u8 orig_mode; + u8 video_mode; + u8 current_mode; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, + int size) +{ + int ret = -1; + u8 req_type = 0; + + switch (set) { + case 0: /* 0xc1 */ + req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + break; + case 1: /* 0x41 */ + req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + break; + case 2: /* 0x80 */ + req_type = USB_DIR_IN | USB_RECIP_DEVICE; + break; + case 3: /* 0x40 */ + req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + break; + } + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, req_type, + val, 0, gspca_dev->usb_buf, size, 500); + + if ((ret < 0) && (req != 0x0a)) + PDEBUG(D_ERR, + "usb_control_msg error %i, request = 0x%x, error = %i", + set, req, ret); + + return ret; +} + +static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret) +{ + stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */ + PDEBUG(D_ERR, "last error: %i, command = 0x%x", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + return ret; +} + +static int stv0680_get_video_mode(struct gspca_dev *gspca_dev) +{ + /* Note not sure if this init of usb_buf is really necessary */ + memset(gspca_dev->usb_buf, 0, 8); + gspca_dev->usb_buf[0] = 0x0f; + + if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) { + PDEBUG(D_ERR, "Get_Camera_Mode failed"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ +} + +static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->current_mode == mode) + return 0; + + memset(gspca_dev->usb_buf, 0, 8); + gspca_dev->usb_buf[0] = mode; + + if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) { + PDEBUG(D_ERR, "Set_Camera_Mode failed"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + /* Verify we got what we've asked for */ + if (stv0680_get_video_mode(gspca_dev) != mode) { + PDEBUG(D_ERR, "Error setting camera video mode!"); + return -EIO; + } + + sd->current_mode = mode; + + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + int ret; + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + /* ping camera to be sure STV0680 is present */ + if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 || + gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) { + PDEBUG(D_ERR, "STV(e): camera ping failed!!"); + return stv0680_handle_error(gspca_dev, -ENODEV); + } + + /* get camera descriptor */ + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09) + return stv0680_handle_error(gspca_dev, -ENODEV); + + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 || + gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) { + PDEBUG(D_ERR, "Could not get descriptor 0200."); + return stv0680_handle_error(gspca_dev, -ENODEV); + } + if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02) + return stv0680_handle_error(gspca_dev, -ENODEV); + if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24) + return stv0680_handle_error(gspca_dev, -ENODEV); + if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -ENODEV); + + if (!(gspca_dev->usb_buf[7] & 0x09)) { + PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode"); + return -ENODEV; + } + if (gspca_dev->usb_buf[7] & 0x01) + PDEBUG(D_PROBE, "Camera supports CIF mode"); + if (gspca_dev->usb_buf[7] & 0x02) + PDEBUG(D_PROBE, "Camera supports VGA mode"); + if (gspca_dev->usb_buf[7] & 0x08) + PDEBUG(D_PROBE, "Camera supports QVGA mode"); + + if (gspca_dev->usb_buf[7] & 0x01) + sd->video_mode = 0x00; /* CIF */ + else + sd->video_mode = 0x03; /* QVGA */ + + /* FW rev, ASIC rev, sensor ID */ + PDEBUG(D_PROBE, "Firmware rev is %i.%i", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + PDEBUG(D_PROBE, "ASIC rev is %i.%i", + gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]); + PDEBUG(D_PROBE, "Sensor ID is %i", + (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4)); + + + ret = stv0680_get_video_mode(gspca_dev); + if (ret < 0) + return ret; + sd->current_mode = sd->orig_mode = ret; + + ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); + if (ret < 0) + return ret; + + /* Get mode details */ + if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -EIO); + + cam->bulk = 1; + cam->bulk_nurbs = 1; /* The cam cannot handle more */ + cam->bulk_size = (gspca_dev->usb_buf[0] << 24) | + (gspca_dev->usb_buf[1] << 16) | + (gspca_dev->usb_buf[2] << 8) | + (gspca_dev->usb_buf[3]); + sd->mode.width = (gspca_dev->usb_buf[4] << 8) | + (gspca_dev->usb_buf[5]); /* 322, 356, 644 */ + sd->mode.height = (gspca_dev->usb_buf[6] << 8) | + (gspca_dev->usb_buf[7]); /* 242, 292, 484 */ + sd->mode.pixelformat = V4L2_PIX_FMT_STV0680; + sd->mode.field = V4L2_FIELD_NONE; + sd->mode.bytesperline = sd->mode.width; + sd->mode.sizeimage = cam->bulk_size; + sd->mode.colorspace = V4L2_COLORSPACE_SRGB; + + /* origGain = gspca_dev->usb_buf[12]; */ + + cam->cam_mode = &sd->mode; + cam->nmodes = 1; + + + ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode); + if (ret < 0) + return ret; + + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 || + gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) { + PDEBUG(D_ERR, "Could not get descriptor 0100."); + return stv0680_handle_error(gspca_dev, -EIO); + } + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + int ret; + struct sd *sd = (struct sd *) gspca_dev; + + ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); + if (ret < 0) + return ret; + + if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -EIO); + + /* Start stream at: + 0x0000 = CIF (352x288) + 0x0100 = VGA (640x480) + 0x0300 = QVGA (320x240) */ + if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0) + return stv0680_handle_error(gspca_dev, -EIO); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* This is a high priority command; it stops all lower order cmds */ + if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0) + stv0680_handle_error(gspca_dev, -EIO); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->gspca_dev.present) + return; + + stv0680_set_video_mode(gspca_dev, sd->orig_mode); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Every now and then the camera sends a 16 byte packet, no idea + what it contains, but it is not image data, when this + happens the frame received before this packet is corrupt, + so discard it. */ + if (len != sd->mode.sizeimage) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* Finish the previous frame, we do this upon reception of the next + packet, even though it is already complete so that the strange 16 + byte packets send after a corrupt frame can discard it. */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + /* Store the just received frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0553, 0x0202)}, + {USB_DEVICE(0x041e, 0x4007)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index bfae63f5584c..5d0241bb1611 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -312,8 +312,7 @@ out: * The 0005 and 0100 chunks seem to appear only in compressed stream. */ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -366,7 +365,7 @@ frame_data: sd->to_skip -= skip; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, chunk_len); break; @@ -378,7 +377,7 @@ frame_data: /* Create a new frame, chunk length should be zero */ gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, 0); + NULL, 0); if (sd->bridge == BRIDGE_ST6422) sd->to_skip = gspca_dev->width * 4; @@ -394,8 +393,8 @@ frame_data: PDEBUG(D_PACK, "End of frame detected"); /* Complete the last frame (if any) */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); if (chunk_len) PDEBUG(D_ERR, "Chunk length is " diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index aa8f995ce04e..72bf3b4f0a31 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -631,7 +631,7 @@ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, count = 200; while (--count > 0) { msleep(10); - /* gsmart mini2 write a each wait setting 1 ms is enought */ + /* gsmart mini2 write a each wait setting 1 ms is enough */ /* reg_w_riv(dev, req, idx, val); */ status = reg_r_12(gspca_dev, 0x01, 0x0001, 1); if (status == endcode) { @@ -1116,7 +1116,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -1186,11 +1185,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, break; } if (sof) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - ffd9, 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); } @@ -1198,7 +1197,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, i = 0; do { if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, i + 1); len -= i; data += i; @@ -1207,7 +1206,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } i++; } while (i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 1d321c30d22f..55ef6a744427 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -938,7 +938,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -956,9 +955,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* extra bytes....., could be processed too but would be * a waste of time, right now leave the application and * libjpeg do it for ourserlves.. */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, ffd9, 2); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } @@ -967,7 +966,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * other's do not include it... */ len -= 2; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 4b44dde9f8b8..b74a3b6489c7 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -398,8 +398,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -424,9 +423,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * - 4 bytes */ gspca_frame_add(gspca_dev, packet_type0, - frame, data + 2, gspca_dev->width); + data + 2, gspca_dev->width); gspca_frame_add(gspca_dev, packet_type1, - frame, data + gspca_dev->width + 5, gspca_dev->width); + data + gspca_dev->width + 5, gspca_dev->width); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 589042f6adbe..c090efcd8045 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -2987,7 +2987,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso pkt length */ { @@ -2996,21 +2995,25 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (data[0] == 0xff && data[1] == 0xd8) { PDEBUG(D_PACK, "vc032x header packet found len %d", len); - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += sd->image_offset; len -= sd->image_offset; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } /* The vc0321 sends some additional data after sending the complete * frame, we ignore this. */ - if (sd->bridge == BRIDGE_VC0321 - && len > frame->v4l2_buf.length - (frame->data_end - frame->data)) - len = frame->v4l2_buf.length - (frame->data_end - frame->data); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + if (sd->bridge == BRIDGE_VC0321) { + struct gspca_frame *frame; + int l; + + frame = gspca_get_i_frame(gspca_dev); + l = frame->data_end - frame->data; + if (len > frame->v4l2_buf.length - l) + len = frame->v4l2_buf.length - l; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) @@ -3092,6 +3095,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: + if (menu->index >= ARRAY_SIZE(freq_nm)) + break; strcpy((char *) menu->name, freq_nm[menu->index]); return 0; } diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c new file mode 100644 index 000000000000..2fffe203bed8 --- /dev/null +++ b/drivers/media/video/gspca/w996Xcf.c @@ -0,0 +1,609 @@ +/** + * + * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip. + * + * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> + * + * This module is adapted from the in kernel v4l1 w9968cf driver: + * + * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Note this is not a stand alone driver, it gets included in ov519.c, this + is a bit of a hack, but it needs the driver code for a lot of different + ov sensors which is already present in ov519.c (the old v4l1 driver used + the ovchipcam framework). When we have the time we really should move + the sensor drivers to v4l2 sub drivers, and properly split of this + driver from ov519.c */ + +/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ +#define CONEX_CAM +#include "jpeg.h" + +#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ + +#define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET) +#define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET) + +static const struct v4l2_pix_format w9968cf_vga_mode[] = { + {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 160 * 2, + .sizeimage = 160 * 120 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 176 * 2, + .sizeimage = 176 * 144 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352 * 2, + .sizeimage = 352 * 288 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static int reg_w(struct sd *sd, __u16 index, __u16 value); + +/*-------------------------------------------------------------------------- + Write 64-bit data to the fast serial bus registers. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_fsb(struct sd *sd, u16* data) +{ + struct usb_device* udev = sd->gspca_dev.dev; + u16 value; + int ret; + + value = *data++; + memcpy(sd->gspca_dev.usb_buf, data, 6); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, 0x06, sd->gspca_dev.usb_buf, 6, 500); + if (ret < 0) { + PDEBUG(D_ERR, "Write FSB registers failed (%d)", ret); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------- + Write data to the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_sb(struct sd *sd, u16 value) +{ + int ret; + + /* We don't use reg_w here, as that would cause all writes when + bitbanging i2c to be logged, making the logs impossible to read */ + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0x01, NULL, 0, 500); + + udelay(W9968CF_I2C_BUS_DELAY); + + if (ret < 0) { + PDEBUG(D_ERR, "Write SB reg [01] %04x failed", value); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------- + Read data from the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_sb(struct sd *sd) +{ + int ret; + + /* We don't use reg_r here, as the w9968cf is special and has 16 + bit registers instead of 8 bit */ + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0x01, sd->gspca_dev.usb_buf, 2, 500); + if (ret >= 0) + ret = sd->gspca_dev.usb_buf[0] | + (sd->gspca_dev.usb_buf[1] << 8); + else + PDEBUG(D_ERR, "Read SB reg [01] failed"); + + udelay(W9968CF_I2C_BUS_DELAY); + + return ret; +} + +/*-------------------------------------------------------------------------- + Upload quantization tables for the JPEG compression. + This function is called by w9968cf_start_transfer(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_upload_quantizationtables(struct sd *sd) +{ + u16 a, b; + int ret = 0, i, j; + + ret += reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */ + + for (i = 0, j = 0; i < 32; i++, j += 2) { + a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8); + b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8); + ret += reg_w(sd, 0x40+i, a); + ret += reg_w(sd, 0x60+i, b); + } + ret += reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */ + + return ret; +} + +/**************************************************************************** + * Low-level I2C I/O functions. * + * The adapter supports the following I2C transfer functions: * + * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * + * i2c_adap_read_byte_data() * + * i2c_adap_read_byte() * + ****************************************************************************/ + +static int w9968cf_smbus_start(struct sd *sd) +{ + int ret = 0; + + ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return ret; +} + +static int w9968cf_smbus_stop(struct sd *sd) +{ + int ret = 0; + + ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + + return ret; +} + +static int w9968cf_smbus_write_byte(struct sd *sd, u8 v) +{ + u8 bit; + int ret = 0, sda; + + for (bit = 0 ; bit < 8 ; bit++) { + sda = (v & 0x80) ? 2 : 0; + v <<= 1; + /* SDE=1, SDA=sda, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x10 | sda); + /* SDE=1, SDA=sda, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x11 | sda); + /* SDE=1, SDA=sda, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x10 | sda); + } + + return ret; +} + +static int w9968cf_smbus_read_byte(struct sd *sd, u8* v) +{ + u8 bit; + int ret = 0; + + /* No need to ensure SDA is high as we are always called after + read_ack which ends with SDA high */ + *v = 0; + for (bit = 0 ; bit < 8 ; bit++) { + *v <<= 1; + /* SDE=1, SDA=1, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0013); + *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0; + /* SDE=1, SDA=1, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0012); + } + + return ret; +} + +static int w9968cf_smbus_write_nack(struct sd *sd) +{ + int ret = 0; + + /* No need to ensure SDA is high as we are always called after + read_byte which ends with SDA high */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + + return ret; +} + +static int w9968cf_smbus_read_ack(struct sd *sd) +{ + int ret = 0, sda; + + /* Ensure SDA is high before raising clock to avoid a spurious stop */ + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + sda = w9968cf_read_sb(sd); + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + if (sda < 0) + ret += sda; + else if (sda & 0x08) { + PDEBUG(D_USBI, "Did not receive i2c ACK"); + ret += -1; + } + + return ret; +} + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ +static int w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value) +{ + u16* data = (u16 *)sd->gspca_dev.usb_buf; + int ret = 0; + + data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0); + data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0; + data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0); + data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0; + data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0; + data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0); + data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0; + data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0; + data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0); + data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0; + + ret += w9968cf_write_fsb(sd, data); + + data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0); + data[0] |= (reg & 0x40) ? 0x0540 : 0x0; + data[0] |= (reg & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0); + data[1] |= (reg & 0x10) ? 0x0054 : 0x0; + data[1] |= (reg & 0x08) ? 0x1500 : 0x0; + data[1] |= (reg & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0); + data[2] |= (reg & 0x02) ? 0x0150 : 0x0; + data[2] |= (reg & 0x01) ? 0x5400 : 0x0; + data[3] = 0x001d; + + ret += w9968cf_write_fsb(sd, data); + + data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); + data[0] |= (value & 0x40) ? 0x0540 : 0x0; + data[0] |= (value & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); + data[1] |= (value & 0x10) ? 0x0054 : 0x0; + data[1] |= (value & 0x08) ? 0x1500 : 0x0; + data[1] |= (value & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); + data[2] |= (value & 0x02) ? 0x0150 : 0x0; + data[2] |= (value & 0x01) ? 0x5400 : 0x0; + data[3] = 0xfe1d; + + ret += w9968cf_write_fsb(sd, data); + + if (!ret) + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + else + PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg); + + return ret; +} + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ +static int w9968cf_i2c_r(struct sd *sd, u8 reg) +{ + int ret = 0; + u8 value; + + /* Fast serial bus data control disable */ + ret += w9968cf_write_sb(sd, 0x0013); /* don't change ! */ + + ret += w9968cf_smbus_start(sd); + ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_write_byte(sd, reg); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_stop(sd); + ret += w9968cf_smbus_start(sd); + ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_read_byte(sd, &value); + /* signal we don't want to read anymore, the v4l1 driver used to + send an ack here which is very wrong! (and then fixed + the issues this gave by retrying reads) */ + ret += w9968cf_smbus_write_nack(sd); + ret += w9968cf_smbus_stop(sd); + + /* Fast serial bus data control re-enable */ + ret += w9968cf_write_sb(sd, 0x0030); + + if (!ret) { + ret = value; + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + } else + PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); + + return ret; +} + + +/*-------------------------------------------------------------------------- + Turn on the LED on some webcams. A beep should be heard too. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_configure(struct sd *sd) +{ + int ret = 0; + + ret += reg_w(sd, 0x00, 0xff00); /* power-down */ + ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */ + ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */ + ret += reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */ + ret += reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */ + ret += reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */ + ret += reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */ + + if (ret) + PDEBUG(D_ERR, "Couldn't turn on the LED"); + + sd->stopped = 1; + + return ret; +} + +static int w9968cf_init(struct sd *sd) +{ + int ret = 0; + unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2), + y0 = 0x0000, + u0 = y0 + hw_bufsize/2, + v0 = u0 + hw_bufsize/4, + y1 = v0 + hw_bufsize/4, + u1 = y1 + hw_bufsize/2, + v1 = u1 + hw_bufsize/4; + + ret += reg_w(sd, 0x00, 0xff00); /* power off */ + ret += reg_w(sd, 0x00, 0xbf10); /* power on */ + + ret += reg_w(sd, 0x03, 0x405d); /* DRAM timings */ + ret += reg_w(sd, 0x04, 0x0030); /* SDRAM timings */ + + ret += reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */ + ret += reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */ + ret += reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */ + ret += reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */ + ret += reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */ + ret += reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */ + + ret += reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */ + ret += reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */ + ret += reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */ + ret += reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */ + ret += reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */ + ret += reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */ + + ret += reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */ + ret += reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */ + + ret += reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */ + ret += reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */ + + ret += reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */ + ret += reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/ + ret += reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */ + ret += reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */ + + return ret; +} + +static int w9968cf_set_crop_window(struct sd *sd) +{ + int ret = 0, start_cropx, start_cropy, x, y, fw, fh, cw, ch, + max_width, max_height; + + if (sd->sif) { + max_width = 352; + max_height = 288; + } else { + max_width = 640; + max_height = 480; + } + + if (sd->sensor == SEN_OV7620) { + /* Sigh, this is dependend on the clock / framerate changes + made by the frequency control, sick. */ + if (sd->freq == 1) { + start_cropx = 277; + start_cropy = 37; + } else { + start_cropx = 105; + start_cropy = 37; + } + } else { + start_cropx = 320; + start_cropy = 35; + } + + /* Work around to avoid FP arithmetics */ + #define SC(x) ((x) << 10) + + /* Scaling factors */ + fw = SC(sd->gspca_dev.width) / max_width; + fh = SC(sd->gspca_dev.height) / max_height; + + cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width)/fh; + ch = (fw >= fh) ? SC(sd->gspca_dev.height)/fw : max_height; + + sd->sensor_width = max_width; + sd->sensor_height = max_height; + + x = (max_width - cw) / 2; + y = (max_height - ch) / 2; + + ret += reg_w(sd, 0x10, start_cropx + x); + ret += reg_w(sd, 0x11, start_cropy + y); + ret += reg_w(sd, 0x12, start_cropx + x + cw); + ret += reg_w(sd, 0x13, start_cropy + y + ch); + + return ret; +} + +static int w9968cf_mode_init_regs(struct sd *sd) +{ + int ret = 0, val, vs_polarity, hs_polarity; + + ret += w9968cf_set_crop_window(sd); + + ret += reg_w(sd, 0x14, sd->gspca_dev.width); + ret += reg_w(sd, 0x15, sd->gspca_dev.height); + + /* JPEG width & height */ + ret += reg_w(sd, 0x30, sd->gspca_dev.width); + ret += reg_w(sd, 0x31, sd->gspca_dev.height); + + /* Y & UV frame buffer strides (in WORD) */ + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + ret += reg_w(sd, 0x2c, sd->gspca_dev.width/2); + ret += reg_w(sd, 0x2d, sd->gspca_dev.width/4); + } else + ret += reg_w(sd, 0x2c, sd->gspca_dev.width); + + ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */ + ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */ + + /* Transfer size in WORDS (for UYVY format only) */ + val = sd->gspca_dev.width * sd->gspca_dev.height; + ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */ + ret += reg_w(sd, 0x3e, val >> 16); /* high bits */ + + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + /* We may get called multiple times (usb isoc bw negotiat.) */ + if (!sd->jpeg_hdr) + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; + + jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height, + sd->gspca_dev.width, 0x22); /* JPEG 420 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + ret += w9968cf_upload_quantizationtables(sd); + } + + /* Video Capture Control Register */ + if (sd->sensor == SEN_OV7620) { + /* Seems to work around a bug in the image sensor */ + vs_polarity = 1; + hs_polarity = 1; + } else { + vs_polarity = 1; + hs_polarity = 0; + } + + val = (vs_polarity << 12) | (hs_polarity << 11); + + /* NOTE: We may not have enough memory to do double buffering while + doing compression (amount of memory differs per model cam). + So we use the second image buffer also as jpeg stream buffer + (see w9968cf_init), and disable double buffering. */ + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + /* val |= 0x0002; YUV422P */ + val |= 0x0003; /* YUV420P */ + } else + val |= 0x0080; /* Enable HW double buffering */ + + /* val |= 0x0020; enable clamping */ + /* val |= 0x0008; enable (1-2-1) filter */ + /* val |= 0x000c; enable (2-3-6-3-2) filter */ + + val |= 0x8000; /* capt. enable */ + + ret += reg_w(sd, 0x16, val); + + sd->gspca_dev.empty_packet = 0; + + return ret; +} + +static void w9968cf_stop0(struct sd *sd) +{ + if (sd->gspca_dev.present) { + reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */ + reg_w(sd, 0x16, 0x0000); /* stop video capture */ + } + + kfree(sd->jpeg_hdr); + sd->jpeg_hdr = NULL; +} + +/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF + for the next frame). This seems to simply not be true when operating + in JPEG mode, in this case there may be empty packets within the + frame. So in JPEG mode use the JPEG SOI marker to detect SOF. + + Note to make things even more interesting the w9968cf sends *PLANAR* jpeg, + to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS, + V-data, EOI. */ +static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + if (len >= 2 && + data[0] == 0xff && + data[1] == 0xd8) { + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + /* Strip the ff d8, our own header (which adds + huffman and quantization tables) already has this */ + len -= 2; + data += 2; + } + } else { + /* In UYVY mode an empty packet signals EOF */ + if (gspca_dev->empty_packet) { + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + NULL, 0); + gspca_dev->empty_packet = 0; + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index cdf3357b4c9f..69e5dc4fc9de 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -61,17 +61,18 @@ struct sd { #define SENSOR_HV7131C 6 #define SENSOR_ICM105A 7 #define SENSOR_MC501CB 8 -#define SENSOR_OV7620 9 -/*#define SENSOR_OV7648 9 - same values */ -#define SENSOR_OV7630C 10 -#define SENSOR_PAS106 11 -#define SENSOR_PAS202B 12 -#define SENSOR_PB0330 13 -#define SENSOR_PO2030 14 -#define SENSOR_TAS5130CK 15 -#define SENSOR_TAS5130CXX 16 -#define SENSOR_TAS5130C_VF0250 17 -#define SENSOR_MAX 18 +#define SENSOR_MI0360SOC 9 +#define SENSOR_OV7620 10 +/*#define SENSOR_OV7648 10 - same values */ +#define SENSOR_OV7630C 11 +#define SENSOR_PAS106 12 +#define SENSOR_PAS202B 13 +#define SENSOR_PB0330 14 /* (MI0360) */ +#define SENSOR_PO2030 15 +#define SENSOR_TAS5130CK 16 +#define SENSOR_TAS5130CXX 17 +#define SENSOR_TAS5130C_VF0250 18 +#define SENSOR_MAX 19 unsigned short chip_revision; u8 *jpeg_hdr; @@ -420,9 +421,7 @@ static const struct usb_action adcm2700_NoFliker[] = { {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ {} }; -static const struct usb_action cs2102_Initial[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action cs2102_Initial[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -471,88 +470,10 @@ static const struct usb_action cs2102_Initial[] = { {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x68, ZC3XX_R18D_YTARGET}, {0xa0, 0x00, 0x01ad}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */ - {0xa0, 0x44, ZC3XX_R121_GAMMA01}, - {0xa0, 0x64, ZC3XX_R122_GAMMA02}, - {0xa0, 0x84, ZC3XX_R123_GAMMA03}, - {0xa0, 0x9d, ZC3XX_R124_GAMMA04}, - {0xa0, 0xb2, ZC3XX_R125_GAMMA05}, - {0xa0, 0xc4, ZC3XX_R126_GAMMA06}, - {0xa0, 0xd3, ZC3XX_R127_GAMMA07}, - {0xa0, 0xe0, ZC3XX_R128_GAMMA08}, - {0xa0, 0xeb, ZC3XX_R129_GAMMA09}, - {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x18, ZC3XX_R130_GAMMA10}, - {0xa0, 0x20, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0e, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0001}, - {0xaa, 0x24, 0x0055}, - {0xaa, 0x25, 0x00cc}, - {0xaa, 0x21, 0x003f}, - {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action cs2102_InitialScale[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action cs2102_InitialScale[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -601,57 +522,75 @@ static const struct usb_action cs2102_InitialScale[] = { {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x68, ZC3XX_R18D_YTARGET}, {0xa0, 0x00, 0x01ad}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */ - {0xa0, 0x44, ZC3XX_R121_GAMMA01}, - {0xa0, 0x64, ZC3XX_R122_GAMMA02}, - {0xa0, 0x84, ZC3XX_R123_GAMMA03}, - {0xa0, 0x9d, ZC3XX_R124_GAMMA04}, - {0xa0, 0xb2, ZC3XX_R125_GAMMA05}, - {0xa0, 0xc4, ZC3XX_R126_GAMMA06}, - {0xa0, 0xd3, ZC3XX_R127_GAMMA07}, - {0xa0, 0xe0, ZC3XX_R128_GAMMA08}, - {0xa0, 0xeb, ZC3XX_R129_GAMMA09}, - {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x18, ZC3XX_R130_GAMMA10}, - {0xa0, 0x20, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0e, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action cs2102_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x005f}, + {0xaa, 0x25, 0x0090}, + {0xaa, 0x21, 0x00dd}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00af}, + {0xaa, 0x25, 0x00c8}, + {0xaa, 0x21, 0x0068}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x0055}, + {0xaa, 0x25, 0x00cc}, + {0xaa, 0x21, 0x003f}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_60HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0000}, {0xaa, 0x24, 0x00aa}, @@ -671,162 +610,50 @@ static const struct usb_action cs2102_InitialScale[] = { {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1}, {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action cs2102_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x008c}, /* 00,0f,8c,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00ac}, /* 00,04,ac,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00ac}, /* 00,11,ac,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00ac}, /* 00,1d,ac,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x42, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,42,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x8c, ZC3XX_R01D_HSYNC_0}, /* 00,1d,8c,cc */ - {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */ - {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */ - {} -}; -static const struct usb_action cs2102_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0093}, /* 00,0f,93,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00a1}, /* 00,04,a1,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00a1}, /* 00,11,a1,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00a1}, /* 00,1d,a1,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf7, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f7,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x93, ZC3XX_R01D_HSYNC_0}, /* 00,1d,93,cc */ - {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */ - {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */ - {} -}; -static const struct usb_action cs2102_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x005d}, /* 00,0f,5d,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00aa}, /* 00,04,aa,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00aa}, /* 00,11,aa,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00aa}, /* 00,1d,aa,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xe4, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e4,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x5d, ZC3XX_R01D_HSYNC_0}, /* 00,1d,5d,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */ - {} -}; -static const struct usb_action cs2102_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x00b7}, /* 00,0f,b7,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00be}, /* 00,04,be,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00be}, /* 00,11,be,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00be}, /* 00,1d,be,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xfc, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,fc,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x69, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,69,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0xb7, ZC3XX_R01D_HSYNC_0}, /* 00,1d,b7,cc */ - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ - {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ {} }; static const struct usb_action cs2102_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x005f}, + {0xaa, 0x25, 0x0000}, + {0xaa, 0x21, 0x0001}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action cs2102_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00af}, + {0xaa, 0x25, 0x0080}, + {0xaa, 0x21, 0x0001}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; @@ -4409,170 +4236,80 @@ static const struct usb_action pas202b_NoFlikerScale[] = { {} }; -static const struct usb_action pb03303x_Initial[] = { +/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */ +static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */ + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/ +/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, {0xaa, 0x01, 0x0001}, {0xaa, 0x06, 0x0000}, {0xaa, 0x08, 0x0483}, {0xaa, 0x01, 0x0004}, {0xaa, 0x08, 0x0006}, {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, + {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/ + {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x35, 0x0050}, + {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/ + {0xaa, 0x35, 0x507f}, /*jfm: was 0050*/ {0xaa, 0x30, 0x0005}, {0xaa, 0x31, 0x0000}, {0xaa, 0x58, 0x0078}, {0xaa, 0x62, 0x0411}, {0xaa, 0x2b, 0x0028}, - {0xaa, 0x2c, 0x0030}, - {0xaa, 0x2d, 0x0030}, - {0xaa, 0x2e, 0x0028}, + {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/ + {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/ + {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /* jfm: was 78 */ {0xa0, 0x61, ZC3XX_R116_RGAIN}, {0xa0, 0x65, ZC3XX_R118_BGAIN}, - - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x0d, 0x003a}, - {0xa0, 0x02, 0x003b}, - {0xa0, 0x00, 0x0038}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x0134}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; - -static const struct usb_action pb03303x_InitialScale[] = { +static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */ + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/ +/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, {0xaa, 0x01, 0x0001}, {0xaa, 0x06, 0x0000}, {0xaa, 0x08, 0x0483}, @@ -4582,111 +4319,111 @@ static const struct usb_action pb03303x_InitialScale[] = { {0xaa, 0x03, 0x01e7}, {0xaa, 0x04, 0x0287}, {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x35, 0x0050}, + {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/ + {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/ {0xaa, 0x30, 0x0005}, {0xaa, 0x31, 0x0000}, {0xaa, 0x58, 0x0078}, {0xaa, 0x62, 0x0411}, - {0xaa, 0x2b, 0x0028}, - {0xaa, 0x2c, 0x0030}, - {0xaa, 0x2d, 0x0030}, - {0xaa, 0x2e, 0x0028}, + {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/ + {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/ + {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/ + {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /*jfm: was 78*/ {0xa0, 0x61, ZC3XX_R116_RGAIN}, {0xa0, 0x65, ZC3XX_R118_BGAIN}, - - {0xa1, 0x01, 0x0002}, - - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - - {0xa0, 0x0d, 0x003a}, - {0xa0, 0x02, 0x003b}, - {0xa0, 0x00, 0x0038}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, + {} +}; +static const struct usb_action mi360soc_AE50HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0562}, + {0xbb, 0x01, 0x09aa}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE50HZScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0934}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE60HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x053d}, + {0xbb, 0x01, 0x096e}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE60HZScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x0134}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0983}, {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, @@ -4696,20 +4433,60 @@ static const struct usb_action pb03303x_InitialScale[] = { {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, + {} +}; +static const struct usb_action mi360soc_AENoFliker[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0960}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action pb0330xx_Initial[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action mi360soc_AENoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0534}, + {0xbb, 0x02, 0x0960}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action pb0330_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, @@ -4721,11 +4498,12 @@ static const struct usb_action pb0330xx_Initial[] = { {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xdd, 0x00, 0x0200}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x0006}, {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, + {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/ + {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ {0xaa, 0x06, 0x0003}, {0xaa, 0x07, 0x3002}, {0xaa, 0x20, 0x1100}, @@ -4743,88 +4521,21 @@ static const struct usb_action pb0330xx_Initial[] = { {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */ + {0xa0, 0x15, 0x01ae}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0066}, - {0xaa, 0x09, 0x02b2}, - {0xaa, 0x10, 0x0002}, - - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0007}, -/* {0xa0, 0x30, 0x0007}, */ -/* {0xa0, 0x00, 0x0007}, */ + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ {} }; - -static const struct usb_action pb0330xx_InitialScale[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 10 */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, @@ -4836,6 +4547,7 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xdd, 0x00, 0x0200}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x0006}, {0xaa, 0x02, 0x0011}, @@ -4858,53 +4570,43 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ + {} +}; +static const struct usb_action pb0330_50HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0066}, - {0xaa, 0x09, 0x02b2}, - {0xaa, 0x10, 0x0002}, + {0xbb, 0x00, 0x055c}, + {0xbb, 0x01, 0x09aa}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action pb0330_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0566}, + {0xbb, 0x02, 0x09b2}, + {0xbb, 0x00, 0x1002}, {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, @@ -4912,124 +4614,102 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0007}, -/* {0xa0, 0x30, 0x0007}, */ -/* {0xa0, 0x00, 0x0007}, */ - {} -}; -static const struct usb_action pb0330_50HZ[] = { - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,ee,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, /* 00,1d,68,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ - {} -}; -static const struct usb_action pb0330_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0xe5, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e5,cc */ - {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f0,cc */ - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ {} }; static const struct usb_action pb0330_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,dd,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x43, ZC3XX_R01D_HSYNC_0}, /* 00,1d,43,cc */ - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x01, 0x0974}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x02, 0x096c}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */ - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x02, 0x0940}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */ - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x01, 0x0980}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, {} }; @@ -5655,7 +5335,7 @@ static const struct usb_action tas5130CK_InitialScale[] = { {} }; -static const struct usb_action tas5130cxx_Initial[] = { +static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -5684,74 +5364,19 @@ static const struct usb_action tas5130cxx_Initial[] = { {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x95, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xec, ZC3XX_R10B_RGB01}, - {0xa0, 0xec, ZC3XX_R10C_RGB02}, - {0xa0, 0xec, ZC3XX_R10D_RGB10}, - {0xa0, 0x68, ZC3XX_R10E_RGB11}, - {0xa0, 0xec, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0xec, ZC3XX_R111_RGB21}, - {0xa0, 0x68, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x018d}, - {0xa0, 0x90, ZC3XX_R18D_YTARGET}, /* 90 */ - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - - {0xaa, 0xa3, 0x0001}, - {0xaa, 0xa4, 0x0077}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, - - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 00 */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 03 */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* e8 */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 0 */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 0 */ - {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 7d */ - - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 08 */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 24 */ - {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, - {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 50 */ - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action tas5130cxx_InitialScale[] = { -/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */ +static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, @@ -5775,63 +5400,13 @@ static const struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x95, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa1, 0x01, 0x0008}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xec, ZC3XX_R10B_RGB01}, - {0xa0, 0xec, ZC3XX_R10C_RGB02}, - {0xa0, 0xec, ZC3XX_R10D_RGB10}, - {0xa0, 0x68, ZC3XX_R10E_RGB11}, - {0xa0, 0xec, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0xec, ZC3XX_R111_RGB21}, - {0xa0, 0x68, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x018d}, - {0xa0, 0x90, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0xa3, 0x0001}, - {0xaa, 0xa4, 0x0063}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, - {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; static const struct usb_action tas5130cxx_50HZ[] = { @@ -5841,20 +5416,22 @@ static const struct usb_action tas5130cxx_50HZ[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ - {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,38,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */ {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_50HZScale[] = { @@ -5864,20 +5441,22 @@ static const struct usb_action tas5130cxx_50HZScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ - {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_60HZ[] = { @@ -5887,20 +5466,22 @@ static const struct usb_action tas5130cxx_60HZ[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_60HZScale[] = { @@ -5910,20 +5491,22 @@ static const struct usb_action tas5130cxx_60HZScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */ + {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ - {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_NoFliker[] = { @@ -5933,13 +5516,13 @@ static const struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ @@ -5947,6 +5530,8 @@ static const struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; @@ -5957,13 +5542,13 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ @@ -5971,6 +5556,8 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = { {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; @@ -6303,8 +5890,10 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, reg, 0x0092); reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */ - msleep(25); + msleep(20); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + if (retbyte != 0x00) + err("i2c_r status error %02x", retbyte); retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */ PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)", @@ -6323,8 +5912,10 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, valL, 0x93); reg_w_i(gspca_dev->dev, valH, 0x94); reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */ - msleep(15); + msleep(1); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + if (retbyte != 0x00) + err("i2c_w status error %02x", retbyte); PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", reg, valH, valL, retbyte); return retbyte; @@ -6359,7 +5950,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev, break; } action++; -/* msleep(1); */ + msleep(1); } } @@ -6380,11 +5971,13 @@ static void setmatrix(struct gspca_dev *gspca_dev) {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f}; static const __u8 po2030_matrix[9] = {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; + static const u8 tas5130c_matrix[9] = + {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68}; static const __u8 vf0250_matrix[9] = {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; static const __u8 *matrix_tb[SENSOR_MAX] = { adcm2700_matrix, /* SENSOR_ADCM2700 0 */ - NULL, /* SENSOR_CS2102 1 */ + ov7620_matrix, /* SENSOR_CS2102 1 */ NULL, /* SENSOR_CS2102K 2 */ gc0305_matrix, /* SENSOR_GC0305 3 */ NULL, /* SENSOR_HDCS2020b 4 */ @@ -6392,15 +5985,16 @@ static void setmatrix(struct gspca_dev *gspca_dev) NULL, /* SENSOR_HV7131C 6 */ NULL, /* SENSOR_ICM105A 7 */ NULL, /* SENSOR_MC501CB 8 */ - ov7620_matrix, /* SENSOR_OV7620 9 */ - NULL, /* SENSOR_OV7630C 10 */ - NULL, /* SENSOR_PAS106 11 */ - pas202b_matrix, /* SENSOR_PAS202B 12 */ - NULL, /* SENSOR_PB0330 13 */ - po2030_matrix, /* SENSOR_PO2030 14 */ - NULL, /* SENSOR_TAS5130CK 15 */ - NULL, /* SENSOR_TAS5130CXX 16 */ - vf0250_matrix, /* SENSOR_TAS5130C_VF0250 17 */ + gc0305_matrix, /* SENSOR_MI0360SOC 9 */ + ov7620_matrix, /* SENSOR_OV7620 10 */ + NULL, /* SENSOR_OV7630C 11 */ + NULL, /* SENSOR_PAS106 12 */ + pas202b_matrix, /* SENSOR_PAS202B 13 */ + gc0305_matrix, /* SENSOR_PB0330 14 */ + po2030_matrix, /* SENSOR_PO2030 15 */ + NULL, /* SENSOR_TAS5130CK 16 */ + tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */ + vf0250_matrix, /* SENSOR_TAS5130C_VF0250 18 */ }; matrix = matrix_tb[sd->sensor]; @@ -6640,39 +6234,43 @@ static int setlightfreq(struct gspca_dev *gspca_dev) {MC501CB_NoFliker, MC501CB_NoFlikerScale, MC501CB_50HZ, MC501CB_50HZScale, MC501CB_60HZ, MC501CB_60HZScale}, -/* SENSOR_OV7620 9 */ +/* SENSOR_MI0360SOC 9 */ + {mi360soc_AENoFlikerScale, mi360soc_AENoFliker, + mi360soc_AE50HZScale, mi360soc_AE50HZ, + mi360soc_AE60HZScale, mi360soc_AE60HZ}, +/* SENSOR_OV7620 10 */ {OV7620_NoFliker, OV7620_NoFliker, OV7620_50HZ, OV7620_50HZ, OV7620_60HZ, OV7620_60HZ}, -/* SENSOR_OV7630C 10 */ +/* SENSOR_OV7630C 11 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_PAS106 11 */ +/* SENSOR_PAS106 12 */ {pas106b_NoFliker, pas106b_NoFliker, pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, -/* SENSOR_PAS202B 12 */ +/* SENSOR_PAS202B 13 */ {pas202b_NoFlikerScale, pas202b_NoFliker, pas202b_50HZScale, pas202b_50HZ, pas202b_60HZScale, pas202b_60HZ}, -/* SENSOR_PB0330 13 */ - {pb0330_NoFliker, pb0330_NoFlikerScale, - pb0330_50HZ, pb0330_50HZScale, - pb0330_60HZ, pb0330_60HZScale}, -/* SENSOR_PO2030 14 */ +/* SENSOR_PB0330 14 */ + {pb0330_NoFlikerScale, pb0330_NoFliker, + pb0330_50HZScale, pb0330_50HZ, + pb0330_60HZScale, pb0330_60HZ}, +/* SENSOR_PO2030 15 */ {PO2030_NoFliker, PO2030_NoFliker, PO2030_50HZ, PO2030_50HZ, PO2030_60HZ, PO2030_60HZ}, -/* SENSOR_TAS5130CK 15 */ - {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, - tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130CXX 16 */ - {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, - tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130C_VF0250 17 */ +/* SENSOR_TAS5130CK 16 */ + {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker, + tas5130cxx_50HZScale, tas5130cxx_50HZ, + tas5130cxx_60HZScale, tas5130cxx_60HZ}, +/* SENSOR_TAS5130CXX 17 */ + {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker, + tas5130cxx_50HZScale, tas5130cxx_50HZ, + tas5130cxx_60HZScale, tas5130cxx_60HZ}, +/* SENSOR_TAS5130C_VF0250 18 */ {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale}, @@ -6729,6 +6327,7 @@ static void send_unknown(struct usb_device *dev, int sensor) case SENSOR_ADCM2700: case SENSOR_GC0305: case SENSOR_OV7620: + case SENSOR_MI0360SOC: case SENSOR_PB0330: case SENSOR_PO2030: reg_w(dev, 0x0d, 0x003a); @@ -6820,7 +6419,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) start_2wr_probe(dev, 0x0e); /* PAS202BCB */ reg_w(dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x03, 0xaa, 0x00); - msleep(500); + msleep(50); retword = i2c_read(gspca_dev, 0x03); if (retword != 0) return 0x0e; /* PAS202BCB */ @@ -6863,7 +6462,8 @@ struct sensor_by_chipset_revision { __u8 internal_sensor_id; }; static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { - {0xc001, 0x13}, /* MI0360 */ + {0xc000, 0x12}, /* TAS5130C */ + {0xc001, 0x13}, /* MI0360SOC */ {0xe001, 0x13}, {0x8001, 0x13}, {0x8000, 0x14}, /* CS2102K */ @@ -6963,7 +6563,6 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x01, 0x0001); reg_w(dev, 0xee, 0x008b); reg_w(dev, 0x03, 0x0012); -/* msleep(150); */ reg_w(dev, 0x01, 0x0012); reg_w(dev, 0x05, 0x0012); retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */ @@ -7025,7 +6624,7 @@ static int sd_config(struct gspca_dev *gspca_dev, int vga = 1; /* 1: vga, 0: sif */ static const __u8 gamma[SENSOR_MAX] = { 4, /* SENSOR_ADCM2700 0 */ - 5, /* SENSOR_CS2102 1 */ + 4, /* SENSOR_CS2102 1 */ 5, /* SENSOR_CS2102K 2 */ 4, /* SENSOR_GC0305 3 */ 4, /* SENSOR_HDCS2020b 4 */ @@ -7033,15 +6632,16 @@ static int sd_config(struct gspca_dev *gspca_dev, 4, /* SENSOR_HV7131C 6 */ 4, /* SENSOR_ICM105A 7 */ 4, /* SENSOR_MC501CB 8 */ - 3, /* SENSOR_OV7620 9 */ - 4, /* SENSOR_OV7630C 10 */ - 4, /* SENSOR_PAS106 11 */ - 4, /* SENSOR_PAS202B 12 */ - 4, /* SENSOR_PB0330 13 */ - 4, /* SENSOR_PO2030 14 */ - 4, /* SENSOR_TAS5130CK 15 */ - 4, /* SENSOR_TAS5130CXX 16 */ - 3, /* SENSOR_TAS5130C_VF0250 17 */ + 4, /* SENSOR_MI0360SOC 9 */ + 3, /* SENSOR_OV7620 10 */ + 4, /* SENSOR_OV7630C 11 */ + 4, /* SENSOR_PAS106 12 */ + 4, /* SENSOR_PAS202B 13 */ + 4, /* SENSOR_PB0330 14 */ + 4, /* SENSOR_PO2030 15 */ + 4, /* SENSOR_TAS5130CK 16 */ + 3, /* SENSOR_TAS5130CXX 17 */ + 3, /* SENSOR_TAS5130C_VF0250 18 */ }; /* define some sensors from the vendor/product */ @@ -7065,7 +6665,7 @@ static int sd_config(struct gspca_dev *gspca_dev, break; default: PDEBUG(D_PROBE, - "Sensor UNKNOW_0 force Tas5130"); + "Sensor UNKNOWN_0 force Tas5130"); sd->sensor = SENSOR_TAS5130CXX; } break; @@ -7103,7 +6703,7 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case 0x10: case 0x12: - PDEBUG(D_PROBE, "Find Sensor TAS5130"); + PDEBUG(D_PROBE, "Find Sensor TAS5130C"); sd->sensor = SENSOR_TAS5130CXX; break; case 0x11: @@ -7112,9 +6712,9 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case 0x13: PDEBUG(D_PROBE, - "Find Sensor MI0360. Chip revision %x", + "Find Sensor MI0360SOC. Chip revision %x", sd->chip_revision); - sd->sensor = SENSOR_PB0330; + sd->sensor = SENSOR_MI0360SOC; break; case 0x14: PDEBUG(D_PROBE, @@ -7228,17 +6828,17 @@ static int sd_start(struct gspca_dev *gspca_dev) {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */ {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */ {MC501CB_InitialScale, MC501CB_Initial}, /* 8 */ - {OV7620_mode0, OV7620_mode1}, /* 9 */ - {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ - {pas106b_InitialScale, pas106b_Initial}, /* 11 */ - {pas202b_Initial, pas202b_InitialScale}, /* 12 */ - {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */ -/* or {pb03303x_InitialScale, pb03303x_Initial}, */ - {PO2030_mode0, PO2030_mode1}, /* 14 */ - {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */ - {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */ + {mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */ + {OV7620_mode0, OV7620_mode1}, /* 10 */ + {ov7630c_InitialScale, ov7630c_Initial}, /* 11 */ + {pas106b_InitialScale, pas106b_Initial}, /* 12 */ + {pas202b_Initial, pas202b_InitialScale}, /* 13 */ + {pb0330_Initial, pb0330_InitialScale}, /* 14 */ + {PO2030_mode0, PO2030_mode1}, /* 15 */ + {tas5130CK_InitialScale, tas5130CK_Initial}, /* 16 */ + {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */ {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial}, - /* 17 */ + /* 18 */ }; /* create the JPEG header */ @@ -7258,19 +6858,6 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_PAS106: usb_exchange(gspca_dev, pas106b_Initial_com); break; - case SENSOR_PB0330: - if (mode) { - if (sd->chip_revision == 0xc001 - || sd->chip_revision == 0xe001 - || sd->chip_revision == 0x8001) - zc3_init = pb03303x_Initial; - } else { - if (sd->chip_revision == 0xc001 - || sd->chip_revision == 0xe001 - || sd->chip_revision == 0x8001) - zc3_init = pb03303x_InitialScale; - } - break; } usb_exchange(gspca_dev, zc3_init); @@ -7310,10 +6897,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* set the gamma tables when not set */ switch (sd->sensor) { - case SENSOR_CS2102: /* gamma set in xxx_Initial */ - case SENSOR_CS2102K: + case SENSOR_CS2102K: /* gamma set in xxx_Initial */ case SENSOR_HDCS2020b: - case SENSOR_PB0330: /* pb with chip_revision - see above */ case SENSOR_OV7630C: case SENSOR_TAS5130CK: break; @@ -7365,7 +6950,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); switch (sd->sensor) { case SENSOR_PO2030: - msleep(500); + msleep(50); reg_r(gspca_dev, 0x0008); reg_r(gspca_dev, 0x0007); /*fall thru*/ @@ -7389,17 +6974,16 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); /* remove the webcam's header: @@ -7411,7 +6995,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += 18; len -= 18; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 2eb9dc2ebe59..b5439cabb381 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -139,7 +139,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n"); - goto exit; + goto exit_urb; } buf->urb = urb; @@ -148,7 +148,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) if (!mem) { v4l2_err(&dev->v4l2_dev, "cannot allocate usb transfer buffer\n"); - goto exit; + goto exit_urb_buffer; } usb_fill_bulk_urb(buf->urb, dev->udev, @@ -161,6 +161,10 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) list_add_tail(&buf->buff_list, &dev->free_buff_list); } return 0; +exit_urb_buffer: + usb_free_urb(urb); +exit_urb: + kfree(buf); exit: hdpvr_free_buffers(dev); return retval; diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index 71c211402eb5..60d992ee2589 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -251,7 +251,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (input < 0 || input >= HEXIUM_INPUTS) + if (input >= HEXIUM_INPUTS) return -EINVAL; hexium->cur_input = input; diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 39d65ca41c62..938a1f8f880a 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -350,7 +350,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct hexium *hexium = (struct hexium *) dev->ext_priv; - if (input < 0 || input >= HEXIUM_INPUTS) + if (input >= HEXIUM_INPUTS) return -EINVAL; hexium->cur_input = input; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 247d3115a9b7..64360d26b32d 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -275,7 +275,7 @@ static void ir_key_poll(struct IR_i2c *ir) if (0 == rc) { ir_input_nokey(ir->input, &ir->ir); } else { - ir_input_keydown(ir->input, &ir->ir, ir_key, ir_raw); + ir_input_keydown(ir->input, &ir->ir, ir_key); } } @@ -299,7 +299,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ir_scancode_table *ir_codes = NULL; const char *name = NULL; - int ir_type; + int ir_type = 0; struct IR_i2c *ir; struct input_dev *input_dev; struct i2c_adapter *adap = client->adapter; @@ -353,10 +353,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_type = IR_TYPE_RC5; ir_codes = &ir_codes_fusionhdtv_mce_table; break; - case 0x7a: case 0x47: case 0x71: - case 0x2d: if (adap->id == I2C_HW_B_CX2388x || adap->id == I2C_HW_B_CX2341X) { /* Handled by cx88-input */ @@ -381,10 +379,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_type = IR_TYPE_OTHER; ir_codes = &ir_codes_avermedia_cardbus_table; break; - default: - dprintk(1, DEVNAME ": Unsupported i2c address 0x%02x\n", addr); - err = -ENODEV; - goto err_out_free; } /* Let the caller override settings */ @@ -427,7 +421,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* Make sure we are all setup before going on */ - if (!name || !ir->get_key || !ir_codes) { + if (!name || !ir->get_key || !ir_type || !ir_codes) { dprintk(1, DEVNAME ": Unsupported device at address 0x%02x\n", addr); err = -ENODEV; @@ -443,7 +437,10 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_name(&client->dev)); /* init + register input device */ - ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); + if (err < 0) + goto err_out_free; + input_dev->id.bustype = BUS_I2C; input_dev->name = ir->name; input_dev->phys = ir->phys; @@ -462,6 +459,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) return 0; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -475,6 +473,7 @@ static int ir_remove(struct i2c_client *client) cancel_delayed_work_sync(&ir->work); /* unregister device */ + ir_input_free(ir->input); input_unregister_device(ir->input); /* free memory */ diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 4873b6ca5801..79d0fe4990d6 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -136,7 +136,8 @@ static const struct ivtv_card ivtv_card_pvr350 = { .hw_audio = IVTV_HW_MSP34XX, .hw_audio_ctrl = IVTV_HW_MSP34XX, .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | - IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER | + IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, @@ -199,7 +200,9 @@ static const struct ivtv_card ivtv_card_pvr150 = { .hw_audio_ctrl = IVTV_HW_CX25840, .hw_muxer = IVTV_HW_WM8775, .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 | - IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + IVTV_HW_TVEEPROM | IVTV_HW_TUNER | + IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT | + IVTV_HW_Z8F0811_IR_HAUP, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 }, { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 }, @@ -955,7 +958,8 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, .hw_audio_ctrl = IVTV_HW_CX25840, - .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739 | + IVTV_HW_I2C_IR_RX_AVER, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, @@ -965,6 +969,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, /* enable line-in */ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, .xceive_pin = 10, @@ -1025,13 +1030,15 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = { /* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */ static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = { - { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, /* NTSC */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc01b }, /* PAL/SECAM */ { 0, 0, 0 } }; static const struct ivtv_card ivtv_card_aver_ultra1500mce = { .type = IVTV_CARD_AVER_ULTRA1500MCE, .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner", + .comment = "For non-NTSC tuners, use the pal= or secam= module options", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, @@ -1058,6 +1065,7 @@ static const struct ivtv_card ivtv_card_aver_ultra1500mce = { .tuners = { /* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216MK5 }, }, .pci_list = ivtv_pci_aver_ultra1500mce, .i2c = &ivtv_i2c_std, diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h index e99a0a255578..6148827ec885 100644 --- a/drivers/media/video/ivtv/ivtv-cards.h +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -87,26 +87,43 @@ #define IVTV_PCI_ID_GOTVIEW1 0xffac #define IVTV_PCI_ID_GOTVIEW2 0xffad -/* hardware flags, no gaps allowed, IVTV_HW_GPIO must always be last */ -#define IVTV_HW_CX25840 (1 << 0) -#define IVTV_HW_SAA7115 (1 << 1) -#define IVTV_HW_SAA7127 (1 << 2) -#define IVTV_HW_MSP34XX (1 << 3) -#define IVTV_HW_TUNER (1 << 4) -#define IVTV_HW_WM8775 (1 << 5) -#define IVTV_HW_CS53L32A (1 << 6) -#define IVTV_HW_TVEEPROM (1 << 7) -#define IVTV_HW_SAA7114 (1 << 8) -#define IVTV_HW_UPD64031A (1 << 9) -#define IVTV_HW_UPD6408X (1 << 10) -#define IVTV_HW_SAA717X (1 << 11) -#define IVTV_HW_WM8739 (1 << 12) -#define IVTV_HW_VP27SMPX (1 << 13) -#define IVTV_HW_M52790 (1 << 14) -#define IVTV_HW_GPIO (1 << 15) +/* hardware flags, no gaps allowed */ +#define IVTV_HW_CX25840 (1 << 0) +#define IVTV_HW_SAA7115 (1 << 1) +#define IVTV_HW_SAA7127 (1 << 2) +#define IVTV_HW_MSP34XX (1 << 3) +#define IVTV_HW_TUNER (1 << 4) +#define IVTV_HW_WM8775 (1 << 5) +#define IVTV_HW_CS53L32A (1 << 6) +#define IVTV_HW_TVEEPROM (1 << 7) +#define IVTV_HW_SAA7114 (1 << 8) +#define IVTV_HW_UPD64031A (1 << 9) +#define IVTV_HW_UPD6408X (1 << 10) +#define IVTV_HW_SAA717X (1 << 11) +#define IVTV_HW_WM8739 (1 << 12) +#define IVTV_HW_VP27SMPX (1 << 13) +#define IVTV_HW_M52790 (1 << 14) +#define IVTV_HW_GPIO (1 << 15) +#define IVTV_HW_I2C_IR_RX_AVER (1 << 16) +#define IVTV_HW_I2C_IR_RX_HAUP_EXT (1 << 17) /* External before internal */ +#define IVTV_HW_I2C_IR_RX_HAUP_INT (1 << 18) +#define IVTV_HW_Z8F0811_IR_TX_HAUP (1 << 19) +#define IVTV_HW_Z8F0811_IR_RX_HAUP (1 << 20) + +#define IVTV_HW_Z8F0811_IR_HAUP (IVTV_HW_Z8F0811_IR_RX_HAUP | \ + IVTV_HW_Z8F0811_IR_TX_HAUP) #define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) +#define IVTV_HW_IR_RX_ANY (IVTV_HW_I2C_IR_RX_AVER | \ + IVTV_HW_I2C_IR_RX_HAUP_EXT | \ + IVTV_HW_I2C_IR_RX_HAUP_INT | \ + IVTV_HW_Z8F0811_IR_RX_HAUP) + +#define IVTV_HW_IR_TX_ANY (IVTV_HW_Z8F0811_IR_TX_HAUP) + +#define IVTV_HW_IR_ANY (IVTV_HW_IR_RX_ANY | IVTV_HW_IR_TX_ANY) + /* video inputs */ #define IVTV_CARD_INPUT_VID_TUNER 1 #define IVTV_CARD_INPUT_SVIDEO1 2 diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 463ec3457d7b..347c3344f56d 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -91,10 +91,15 @@ static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; static unsigned int cardtype_c = 1; static unsigned int tuner_c = 1; static unsigned int radio_c = 1; +static unsigned int i2c_clock_period_c = 1; static char pal[] = "---"; static char secam[] = "--"; static char ntsc[] = "-"; @@ -151,6 +156,7 @@ module_param(dec_vbi_buffers, int, 0644); module_param(tunertype, int, 0644); module_param(newi2c, int, 0644); +module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644); MODULE_PARM_DESC(tuner, "Tuner type selection,\n" "\t\t\tsee tuner.h for values"); @@ -245,6 +251,10 @@ MODULE_PARM_DESC(newi2c, "Use new I2C implementation\n" "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" "\t\t\tDefault is autodetect"); +MODULE_PARM_DESC(i2c_clock_period, + "Period of SCL for the I2C bus controlled by the CX23415/6\n" + "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD)); MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card"); @@ -600,6 +610,15 @@ static void ivtv_process_options(struct ivtv *itv) itv->options.cardtype = cardtype[itv->instance]; itv->options.tuner = tuner[itv->instance]; itv->options.radio = radio[itv->instance]; + + itv->options.i2c_clock_period = i2c_clock_period[itv->instance]; + if (itv->options.i2c_clock_period == -1) + itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD; + else if (itv->options.i2c_clock_period < 10) + itv->options.i2c_clock_period = 10; + else if (itv->options.i2c_clock_period > 4500) + itv->options.i2c_clock_period = 4500; + itv->options.newi2c = newi2c; if (tunertype < -1 || tunertype > 1) { IVTV_WARN("Invalid tunertype argument, will autodetect instead\n"); @@ -865,6 +884,10 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) itv->hw_flags |= device; } + /* probe for legacy IR controllers that aren't in card definitions */ + if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0) + ivtv_i2c_new_ir_legacy(itv); + if (itv->card->hw_all & IVTV_HW_CX25840) itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840); else if (itv->card->hw_all & IVTV_HW_SAA717X) @@ -1361,7 +1384,7 @@ static struct pci_driver ivtv_pci_driver = { .remove = ivtv_remove, }; -static int module_start(void) +static int __init module_start(void) { printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION); @@ -1385,7 +1408,7 @@ static int module_start(void) return 0; } -static void module_cleanup(void) +static void __exit module_cleanup(void) { pci_unregister_driver(&ivtv_pci_driver); } diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 440f7328a7ed..e4816da6482b 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -64,6 +64,7 @@ #include <media/v4l2-device.h> #include <media/tuner.h> #include <media/cx2341x.h> +#include <media/ir-kbd-i2c.h> #include <linux/ivtv.h> @@ -176,12 +177,16 @@ extern int ivtv_debug; #define IVTV_MAX_PGM_INDEX (400) +/* Default I2C SCL period in microseconds */ +#define IVTV_DEFAULT_I2C_CLOCK_PERIOD 20 + struct ivtv_options { int kilobytes[IVTV_MAX_STREAMS]; /* size in kilobytes of each stream */ int cardtype; /* force card type on load */ int tuner; /* set tuner on load */ int radio; /* enable/disable radio */ int newi2c; /* new I2C algorithm */ + int i2c_clock_period; /* period of SCL for I2C bus */ }; /* ivtv-specific mailbox template */ @@ -677,6 +682,7 @@ struct ivtv { int i2c_state; /* i2c bit state */ struct mutex i2c_bus_lock; /* lock i2c bus */ + struct IR_i2c_init_data ir_i2c_init_data; /* Program Index information */ u32 pgm_info_offset; /* start of pgm info in encoder memory */ diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index b9c71e61f7d6..2ee03c2a1b58 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -88,6 +88,11 @@ #define IVTV_UPD64083_I2C_ADDR 0x5c #define IVTV_VP27SMPX_I2C_ADDR 0x5b #define IVTV_M52790_I2C_ADDR 0x48 +#define IVTV_AVERMEDIA_IR_RX_I2C_ADDR 0x40 +#define IVTV_HAUP_EXT_IR_RX_I2C_ADDR 0x1a +#define IVTV_HAUP_INT_IR_RX_I2C_ADDR 0x18 +#define IVTV_Z8F0811_IR_TX_I2C_ADDR 0x70 +#define IVTV_Z8F0811_IR_RX_I2C_ADDR 0x71 /* This array should match the IVTV_HW_ defines */ static const u8 hw_addrs[] = { @@ -106,7 +111,12 @@ static const u8 hw_addrs[] = { IVTV_WM8739_I2C_ADDR, IVTV_VP27SMPX_I2C_ADDR, IVTV_M52790_I2C_ADDR, - 0 /* IVTV_HW_GPIO dummy driver ID */ + 0, /* IVTV_HW_GPIO dummy driver ID */ + IVTV_AVERMEDIA_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_AVER */ + IVTV_HAUP_EXT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + IVTV_HAUP_INT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + IVTV_Z8F0811_IR_TX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + IVTV_Z8F0811_IR_RX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the IVTV_HW_ defines */ @@ -126,7 +136,12 @@ static const char *hw_modules[] = { "wm8739", "vp27smpx", "m52790", - NULL + NULL, + NULL, /* IVTV_HW_I2C_IR_RX_AVER */ + NULL, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + NULL, /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + NULL, /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + NULL, /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the IVTV_HW_ defines */ @@ -147,8 +162,95 @@ static const char * const hw_devicenames[] = { "vp27smpx", "m52790", "gpio", + "ir_video", /* IVTV_HW_I2C_IR_RX_AVER */ + "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + "ir_tx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + "ir_rx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; +static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) +{ + struct i2c_board_info info; + struct i2c_adapter *adap = &itv->i2c_adap; + struct IR_i2c_init_data *init_data = &itv->ir_i2c_init_data; + unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; + + /* Only allow one IR transmitter to be registered per board */ + if (hw & IVTV_HW_IR_TX_ANY) { + if (itv->hw_flags & IVTV_HW_IR_TX_ANY) + return -1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, type, I2C_NAME_SIZE); + return i2c_new_probed_device(adap, &info, addr_list) == NULL + ? -1 : 0; + } + + /* Only allow one IR receiver to be registered per board */ + if (itv->hw_flags & IVTV_HW_IR_RX_ANY) + return -1; + + /* Our default information for ir-kbd-i2c.c to use */ + switch (hw) { + case IVTV_HW_I2C_IR_RX_AVER: + init_data->ir_codes = &ir_codes_avermedia_cardbus_table; + init_data->internal_get_key_func = + IR_KBD_GET_KEY_AVERMEDIA_CARDBUS; + init_data->type = IR_TYPE_OTHER; + init_data->name = "AVerMedia AVerTV card"; + break; + case IVTV_HW_I2C_IR_RX_HAUP_EXT: + case IVTV_HW_I2C_IR_RX_HAUP_INT: + /* Default to old black remote */ + init_data->ir_codes = &ir_codes_rc5_tv_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; + init_data->type = IR_TYPE_RC5; + init_data->name = itv->card_name; + break; + case IVTV_HW_Z8F0811_IR_RX_HAUP: + /* Default to grey remote */ + init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = IR_TYPE_RC5; + init_data->name = itv->card_name; + break; + } + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.platform_data = init_data; + strlcpy(info.type, type, I2C_NAME_SIZE); + + return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; +} + +/* Instantiate the IR receiver device using probing -- undesirable */ +struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv) +{ + struct i2c_board_info info; + /* + * The external IR receiver is at i2c address 0x34. + * The internal IR receiver is at i2c address 0x30. + * + * In theory, both can be fitted, and Hauppauge suggests an external + * overrides an internal. That's why we probe 0x1a (~0x34) first. CB + * + * Some of these addresses we probe may collide with other i2c address + * allocations, so this function must be called after all other i2c + * devices we care about are registered. + */ + const unsigned short addr_list[] = { + 0x1a, /* Hauppauge IR external - collides with WM8739 */ + 0x18, /* Hauppauge IR internal */ + 0x71, /* Hauppauge IR (PVR150) */ + 0x6b, /* Adaptec IR */ + I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list); +} + int ivtv_i2c_register(struct ivtv *itv, unsigned idx) { struct v4l2_subdev *sd; @@ -178,8 +280,15 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) sd->grp_id = 1 << idx; return sd ? 0 : -1; } + + if (hw & IVTV_HW_IR_ANY) + return ivtv_i2c_new_ir(itv, hw, type, hw_addrs[idx]); + + /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) return -1; + + /* It's an I2C device other than an analog tuner or IR chip */ if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx])); @@ -564,20 +673,22 @@ static struct i2c_adapter ivtv_i2c_adap_template = { .owner = THIS_MODULE, }; +#define IVTV_ALGO_BIT_TIMEOUT (2) /* seconds */ + static const struct i2c_algo_bit_data ivtv_i2c_algo_template = { .setsda = ivtv_setsda_old, .setscl = ivtv_setscl_old, .getsda = ivtv_getsda_old, .getscl = ivtv_getscl_old, - .udelay = 10, - .timeout = 200, + .udelay = IVTV_DEFAULT_I2C_CLOCK_PERIOD / 2, /* microseconds */ + .timeout = IVTV_ALGO_BIT_TIMEOUT * HZ, /* jiffies */ }; static struct i2c_client ivtv_i2c_client_template = { .name = "ivtv internal", }; -/* init + register i2c adapter + instantiate IR receiver */ +/* init + register i2c adapter */ int init_ivtv_i2c(struct ivtv *itv) { int retval; @@ -585,11 +696,10 @@ int init_ivtv_i2c(struct ivtv *itv) IVTV_DEBUG_I2C("i2c init\n"); /* Sanity checks for the I2C hardware arrays. They must be the - * same size and GPIO must be the last entry. + * same size. */ if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || - ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) || - IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) { + ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules)) { IVTV_ERR("Mismatched I2C hardware arrays\n"); return -ENODEV; } @@ -602,6 +712,7 @@ int init_ivtv_i2c(struct ivtv *itv) memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, sizeof(struct i2c_algo_bit_data)); } + itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2; itv->i2c_algo.data = itv; itv->i2c_adap.algo_data = &itv->i2c_algo; @@ -623,32 +734,6 @@ int init_ivtv_i2c(struct ivtv *itv) else retval = i2c_bit_add_bus(&itv->i2c_adap); - /* Instantiate the IR receiver device, if present */ - if (retval == 0) { - struct i2c_board_info info; - /* The external IR receiver is at i2c address 0x34 (0x35 for - reads). Future Hauppauge cards will have an internal - receiver at 0x30 (0x31 for reads). In theory, both can be - fitted, and Hauppauge suggest an external overrides an - internal. - - That's why we probe 0x1a (~0x34) first. CB - */ - const unsigned short addr_list[] = { - 0x1a, /* Hauppauge IR external */ - 0x18, /* Hauppauge IR internal */ - 0x71, /* Hauppauge IR (PVR150) */ - 0x64, /* Pixelview IR */ - 0x30, /* KNC ONE IR */ - 0x6b, /* Adaptec IR */ - I2C_CLIENT_END - }; - - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - i2c_new_probed_device(&itv->i2c_adap, &info, addr_list); - } - return retval; } diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h index 396928a06a54..9332920ca4ff 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.h +++ b/drivers/media/video/ivtv/ivtv-i2c.h @@ -21,6 +21,7 @@ #ifndef IVTV_I2C_H #define IVTV_I2C_H +struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv); int ivtv_i2c_register(struct ivtv *itv, unsigned idx); struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw); diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 3454070e63f0..c1fc6dc776f5 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -478,7 +478,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (input < 0 || input >= MXB_INPUTS) + if (input >= MXB_INPUTS) return -EINVAL; mxb->cur_input = input; diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index eccb40ab7fec..205229333466 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -247,7 +247,7 @@ /* COM5 */ #define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ -#define AFR_SPPED 0x40 /* Auto frame rate control speed slection */ +#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */ /* Auto frame rate max rate control */ #define AFR_NO_RATE 0x00 /* No reduction of frame rate */ #define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c new file mode 100644 index 000000000000..c81ae2192887 --- /dev/null +++ b/drivers/media/video/ov9640.c @@ -0,0 +1,801 @@ +/* + * OmniVision OV96xx Camera Driver + * + * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> + * + * Based on ov772x camera driver: + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on ov7670 and soc_camera_platform driver, + * + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * 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/i2c.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include <media/soc_camera.h> + +#include "ov9640.h" + +/* default register setup */ +static const struct ov9640_reg ov9640_regs_dflt[] = { + { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, + { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | + OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, + { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, + { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, + { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, + { OV9640_COM16, OV9640_COM16_RB_AVG }, + + /* Gamma curve P */ + { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, + { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, + { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, + { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, + + /* Gamma curve T */ + { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, + { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, + { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, + { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, +}; + +/* Configurations + * NOTE: for YUV, alter the following registers: + * COM12 |= OV9640_COM12_YUV_AVG + * + * for RGB, alter the following registers: + * COM7 |= OV9640_COM7_RGB + * COM13 |= OV9640_COM13_RGB_AVG + * COM15 |= proper RGB color encoding mode + */ +static const struct ov9640_reg ov9640_regs_qqcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qqvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_cif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_CIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_vga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_VGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_sxga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, 0 }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_yuv[] = { + { OV9640_MTX1, 0x58 }, + { OV9640_MTX2, 0x48 }, + { OV9640_MTX3, 0x10 }, + { OV9640_MTX4, 0x28 }, + { OV9640_MTX5, 0x48 }, + { OV9640_MTX6, 0x70 }, + { OV9640_MTX7, 0x40 }, + { OV9640_MTX8, 0x40 }, + { OV9640_MTX9, 0x40 }, + { OV9640_MTXS, 0x0f }, +}; + +static const struct ov9640_reg ov9640_regs_rgb[] = { + { OV9640_MTX1, 0x71 }, + { OV9640_MTX2, 0x3e }, + { OV9640_MTX3, 0x0c }, + { OV9640_MTX4, 0x33 }, + { OV9640_MTX5, 0x72 }, + { OV9640_MTX6, 0x00 }, + { OV9640_MTX7, 0x2b }, + { OV9640_MTX8, 0x66 }, + { OV9640_MTX9, 0xd2 }, + { OV9640_MTXS, 0x65 }, +}; + +/* + * TODO: this sensor also supports RGB555 and RGB565 formats, but support for + * them has not yet been sufficiently tested and so it is not included with + * this version of the driver. To test and debug these formats add two entries + * to the below array, see ov722x.c for an example. + */ +static const struct soc_camera_data_format ov9640_fmt_lists[] = { + { + .name = "UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static const struct v4l2_queryctrl ov9640_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + +/* read a register */ +static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) +{ + int ret; + u8 data = reg; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + *val = data; + return 0; + +err: + dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); + return ret; +} + +/* write a register */ +static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + u8 _val; + unsigned char data[2] = { reg, val }; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); + return ret; + } + + /* we have to read the register back ... no idea why, maybe HW bug */ + ret = ov9640_reg_read(client, reg, &_val); + if (ret) + dev_err(&client->dev, + "Failed reading back register 0x%02x!\n", reg); + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov9640_reg_read(client, reg, &val); + if (ret) { + dev_err(&client->dev, + "[Read]-Modify-Write of register %02x failed!\n", reg); + return val; + } + + val |= set; + val &= ~unset; + + ret = ov9640_reg_write(client, reg, val); + if (ret) + dev_err(&client->dev, + "Read-Modify-[Write] of register %02x failed!\n", reg); + + return ret; +} + +/* Soft reset the camera. This has nothing to do with the RESET pin! */ +static int ov9640_reset(struct i2c_client *client) +{ + int ret; + + ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); + if (ret) + dev_err(&client->dev, + "An error occured while entering soft reset!\n"); + + return ret; +} + +/* Start/Stop streaming from the device */ +static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + +/* Alter bus settings on camera side */ +static int ov9640_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +/* Request bus settings on camera side */ +static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + + /* + * REVISIT: the camera probably can do 10 bit transfers, but I don't + * have those pins connected on my hardware. + */ + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +/* Get status of additional camera capabilities */ +static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + ctrl->value = priv->flag_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = priv->flag_hflip; + break; + } + return 0; +} + +/* Set status of additional camera capabilities */ +static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + priv->flag_vflip = ctrl->value; + if (ctrl->value) + ret = ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_V, 0); + else + ret = ov9640_reg_rmw(client, OV9640_MVFP, + 0, OV9640_MVFP_V); + break; + case V4L2_CID_HFLIP: + priv->flag_hflip = ctrl->value; + if (ctrl->value) + ret = ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_H, 0); + else + ret = ov9640_reg_rmw(client, OV9640_MVFP, + 0, OV9640_MVFP_H); + break; + } + + return ret; +} + +/* Get chip identification */ +static int ov9640_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + id->ident = priv->model; + id->revision = priv->revision; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9640_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + int ret; + u8 val; + + if (reg->reg & ~0xff) + return -EINVAL; + + reg->size = 1; + + ret = ov9640_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return 0; +} + +static int ov9640_set_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->reg & ~0xff || reg->val & ~0xff) + return -EINVAL; + + return ov9640_reg_write(client, reg->reg, reg->val); +} +#endif + +/* select nearest higher resolution for capture */ +static void ov9640_res_roundup(u32 *width, u32 *height) +{ + int i; + enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; + int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; + int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; + + for (i = 0; i < ARRAY_SIZE(res_x); i++) { + if (res_x[i] >= *width && res_y[i] >= *height) { + *width = res_x[i]; + *height = res_y[i]; + return; + } + } + + *width = res_x[SXGA]; + *height = res_y[SXGA]; +} + +/* Prepare necessary register changes depending on color encoding */ +static void ov9640_alter_regs(u32 pixfmt, struct ov9640_reg_alt *alt) +{ + switch (pixfmt) { + case V4L2_PIX_FMT_UYVY: + alt->com12 = OV9640_COM12_YUV_AVG; + alt->com13 = OV9640_COM13_Y_DELAY_EN | + OV9640_COM13_YUV_DLY(0x01); + break; + case V4L2_PIX_FMT_RGB555: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_555; + break; + case V4L2_PIX_FMT_RGB565: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_565; + break; + }; +} + +/* Setup registers according to resolution and color encoding */ +static int ov9640_write_regs(struct i2c_client *client, + u32 width, u32 pixfmt, struct ov9640_reg_alt *alts) +{ + const struct ov9640_reg *ov9640_regs, *matrix_regs; + int ov9640_regs_len, matrix_regs_len; + int i, ret; + u8 val; + + /* select register configuration for given resolution */ + switch (width) { + case W_QQCIF: + ov9640_regs = ov9640_regs_qqcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); + break; + case W_QQVGA: + ov9640_regs = ov9640_regs_qqvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); + break; + case W_QCIF: + ov9640_regs = ov9640_regs_qcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); + break; + case W_QVGA: + ov9640_regs = ov9640_regs_qvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); + break; + case W_CIF: + ov9640_regs = ov9640_regs_cif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); + break; + case W_VGA: + ov9640_regs = ov9640_regs_vga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); + break; + case W_SXGA: + ov9640_regs = ov9640_regs_sxga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); + break; + default: + dev_err(&client->dev, "Failed to select resolution!\n"); + return -EINVAL; + } + + /* select color matrix configuration for given color encoding */ + if (pixfmt == V4L2_PIX_FMT_UYVY) { + matrix_regs = ov9640_regs_yuv; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); + } else { + matrix_regs = ov9640_regs_rgb; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); + } + + /* write register settings into the module */ + for (i = 0; i < ov9640_regs_len; i++) { + val = ov9640_regs[i].val; + + switch (ov9640_regs[i].reg) { + case OV9640_COM7: + val |= alts->com7; + break; + case OV9640_COM12: + val |= alts->com12; + break; + case OV9640_COM13: + val |= alts->com13; + break; + case OV9640_COM15: + val |= alts->com15; + break; + } + + ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); + if (ret) + return ret; + } + + /* write color matrix configuration into the module */ + for (i = 0; i < matrix_regs_len; i++) { + ret = ov9640_reg_write(client, matrix_regs[i].reg, + matrix_regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/* program default register values */ +static int ov9640_prog_dflt(struct i2c_client *client) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { + ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, + ov9640_regs_dflt[i].val); + if (ret) + return ret; + } + + /* wait for the changes to actually happen, 140ms are not enough yet */ + mdelay(150); + + return 0; +} + +/* set the format we will capture in */ +static int ov9640_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct ov9640_reg_alt alts = {0}; + int ret; + + ov9640_res_roundup(&pix->width, &pix->height); + ov9640_alter_regs(pix->pixelformat, &alts); + + ov9640_reset(client); + + ret = ov9640_prog_dflt(client); + if (ret) + return ret; + + return ov9640_write_regs(client, pix->width, pix->pixelformat, &alts); +} + +static int ov9640_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + ov9640_res_roundup(&pix->width, &pix->height); + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + a->c.left = 0; + a->c.top = 0; + a->c.width = W_SXGA; + a->c.height = H_SXGA; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = W_SXGA; + a->bounds.height = H_SXGA; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + + + +static int ov9640_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + struct ov9640_priv *priv = i2c_get_clientdata(client); + u8 pid, ver, midh, midl; + const char *devname; + int ret = 0; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { + dev_err(&client->dev, "Parent missing or invalid!\n"); + ret = -ENODEV; + goto err; + } + + icd->formats = ov9640_fmt_lists; + icd->num_formats = ARRAY_SIZE(ov9640_fmt_lists); + + /* + * check and show product ID and manufacturer ID + */ + + ret = ov9640_reg_read(client, OV9640_PID, &pid); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_VER, &ver); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_MIDH, &midh); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_MIDL, &midl); + if (ret) + goto err; + + switch (VERSION(pid, ver)) { + case OV9640_V2: + devname = "ov9640"; + priv->model = V4L2_IDENT_OV9640; + priv->revision = 2; + case OV9640_V3: + devname = "ov9640"; + priv->model = V4L2_IDENT_OV9640; + priv->revision = 3; + break; + default: + dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); + ret = -ENODEV; + goto err; + } + + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, pid, ver, midh, midl); + +err: + return ret; +} + +static struct soc_camera_ops ov9640_ops = { + .set_bus_param = ov9640_set_bus_param, + .query_bus_param = ov9640_query_bus_param, + .controls = ov9640_controls, + .num_controls = ARRAY_SIZE(ov9640_controls), +}; + +static struct v4l2_subdev_core_ops ov9640_core_ops = { + .g_ctrl = ov9640_g_ctrl, + .s_ctrl = ov9640_s_ctrl, + .g_chip_ident = ov9640_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9640_get_register, + .s_register = ov9640_set_register, +#endif + +}; + +static struct v4l2_subdev_video_ops ov9640_video_ops = { + .s_stream = ov9640_s_stream, + .s_fmt = ov9640_s_fmt, + .try_fmt = ov9640_try_fmt, + .cropcap = ov9640_cropcap, + .g_crop = ov9640_g_crop, + +}; + +static struct v4l2_subdev_ops ov9640_subdev_ops = { + .core = &ov9640_core_ops, + .video = &ov9640_video_ops, +}; + +/* + * i2c_driver function + */ +static int ov9640_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov9640_priv *priv; + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "Missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, + "Failed to allocate memory for private data!\n"); + return -ENOMEM; + } + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); + + icd->ops = &ov9640_ops; + + ret = ov9640_video_probe(icd, client); + + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(priv); + } + + return ret; +} + +static int ov9640_remove(struct i2c_client *client) +{ + struct ov9640_priv *priv = i2c_get_clientdata(client); + + i2c_set_clientdata(client, NULL); + kfree(priv); + return 0; +} + +static const struct i2c_device_id ov9640_id[] = { + { "ov9640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9640_id); + +static struct i2c_driver ov9640_i2c_driver = { + .driver = { + .name = "ov9640", + }, + .probe = ov9640_probe, + .remove = ov9640_remove, + .id_table = ov9640_id, +}; + +static int __init ov9640_module_init(void) +{ + return i2c_add_driver(&ov9640_i2c_driver); +} + +static void __exit ov9640_module_exit(void) +{ + i2c_del_driver(&ov9640_i2c_driver); +} + +module_init(ov9640_module_init); +module_exit(ov9640_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h new file mode 100644 index 000000000000..f8a51b70792e --- /dev/null +++ b/drivers/media/video/ov9640.h @@ -0,0 +1,209 @@ +/* + * OmniVision OV96xx Camera Header File + * + * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> + * + * 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. + */ + +#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ +#define __DRIVERS_MEDIA_VIDEO_OV9640_H__ + +/* Register definitions */ +#define OV9640_GAIN 0x00 +#define OV9640_BLUE 0x01 +#define OV9640_RED 0x02 +#define OV9640_VFER 0x03 +#define OV9640_COM1 0x04 +#define OV9640_BAVE 0x05 +#define OV9640_GEAVE 0x06 +#define OV9640_RSID 0x07 +#define OV9640_RAVE 0x08 +#define OV9640_COM2 0x09 +#define OV9640_PID 0x0a +#define OV9640_VER 0x0b +#define OV9640_COM3 0x0c +#define OV9640_COM4 0x0d +#define OV9640_COM5 0x0e +#define OV9640_COM6 0x0f +#define OV9640_AECH 0x10 +#define OV9640_CLKRC 0x11 +#define OV9640_COM7 0x12 +#define OV9640_COM8 0x13 +#define OV9640_COM9 0x14 +#define OV9640_COM10 0x15 +/* 0x16 - RESERVED */ +#define OV9640_HSTART 0x17 +#define OV9640_HSTOP 0x18 +#define OV9640_VSTART 0x19 +#define OV9640_VSTOP 0x1a +#define OV9640_PSHFT 0x1b +#define OV9640_MIDH 0x1c +#define OV9640_MIDL 0x1d +#define OV9640_MVFP 0x1e +#define OV9640_LAEC 0x1f +#define OV9640_BOS 0x20 +#define OV9640_GBOS 0x21 +#define OV9640_GROS 0x22 +#define OV9640_ROS 0x23 +#define OV9640_AEW 0x24 +#define OV9640_AEB 0x25 +#define OV9640_VPT 0x26 +#define OV9640_BBIAS 0x27 +#define OV9640_GBBIAS 0x28 +/* 0x29 - RESERVED */ +#define OV9640_EXHCH 0x2a +#define OV9640_EXHCL 0x2b +#define OV9640_RBIAS 0x2c +#define OV9640_ADVFL 0x2d +#define OV9640_ADVFH 0x2e +#define OV9640_YAVE 0x2f +#define OV9640_HSYST 0x30 +#define OV9640_HSYEN 0x31 +#define OV9640_HREF 0x32 +#define OV9640_CHLF 0x33 +#define OV9640_ARBLM 0x34 +/* 0x35..0x36 - RESERVED */ +#define OV9640_ADC 0x37 +#define OV9640_ACOM 0x38 +#define OV9640_OFON 0x39 +#define OV9640_TSLB 0x3a +#define OV9640_COM11 0x3b +#define OV9640_COM12 0x3c +#define OV9640_COM13 0x3d +#define OV9640_COM14 0x3e +#define OV9640_EDGE 0x3f +#define OV9640_COM15 0x40 +#define OV9640_COM16 0x41 +#define OV9640_COM17 0x42 +/* 0x43..0x4e - RESERVED */ +#define OV9640_MTX1 0x4f +#define OV9640_MTX2 0x50 +#define OV9640_MTX3 0x51 +#define OV9640_MTX4 0x52 +#define OV9640_MTX5 0x53 +#define OV9640_MTX6 0x54 +#define OV9640_MTX7 0x55 +#define OV9640_MTX8 0x56 +#define OV9640_MTX9 0x57 +#define OV9640_MTXS 0x58 +/* 0x59..0x61 - RESERVED */ +#define OV9640_LCC1 0x62 +#define OV9640_LCC2 0x63 +#define OV9640_LCC3 0x64 +#define OV9640_LCC4 0x65 +#define OV9640_LCC5 0x66 +#define OV9640_MANU 0x67 +#define OV9640_MANV 0x68 +#define OV9640_HV 0x69 +#define OV9640_MBD 0x6a +#define OV9640_DBLV 0x6b +#define OV9640_GSP 0x6c /* ... till 0x7b */ +#define OV9640_GST 0x7c /* ... till 0x8a */ + +#define OV9640_CLKRC_DPLL_EN 0x80 +#define OV9640_CLKRC_DIRECT 0x40 +#define OV9640_CLKRC_DIV(x) ((x) & 0x3f) + +#define OV9640_PSHFT_VAL(x) ((x) & 0xff) + +#define OV9640_ACOM_2X_ANALOG 0x80 +#define OV9640_ACOM_RSVD 0x12 + +#define OV9640_MVFP_V 0x10 +#define OV9640_MVFP_H 0x20 + +#define OV9640_COM1_HREF_NOSKIP 0x00 +#define OV9640_COM1_HREF_2SKIP 0x04 +#define OV9640_COM1_HREF_3SKIP 0x08 +#define OV9640_COM1_QQFMT 0x20 + +#define OV9640_COM2_SSM 0x10 + +#define OV9640_COM3_VP 0x04 + +#define OV9640_COM4_QQ_VP 0x80 +#define OV9640_COM4_RSVD 0x40 + +#define OV9640_COM5_SYSCLK 0x80 +#define OV9640_COM5_LONGEXP 0x01 + +#define OV9640_COM6_OPT_BLC 0x40 +#define OV9640_COM6_ADBLC_BIAS 0x08 +#define OV9640_COM6_FMT_RST 0x82 +#define OV9640_COM6_ADBLC_OPTEN 0x01 + +#define OV9640_COM7_RAW_RGB 0x01 +#define OV9640_COM7_RGB 0x04 +#define OV9640_COM7_QCIF 0x08 +#define OV9640_COM7_QVGA 0x10 +#define OV9640_COM7_CIF 0x20 +#define OV9640_COM7_VGA 0x40 +#define OV9640_COM7_SCCB_RESET 0x80 + +#define OV9640_TSLB_YVYU_YUYV 0x04 +#define OV9640_TSLB_YUYV_UYVY 0x08 + +#define OV9640_COM12_YUV_AVG 0x04 +#define OV9640_COM12_RSVD 0x40 + +#define OV9640_COM13_GAMMA_NONE 0x00 +#define OV9640_COM13_GAMMA_Y 0x40 +#define OV9640_COM13_GAMMA_RAW 0x80 +#define OV9640_COM13_RGB_AVG 0x20 +#define OV9640_COM13_MATRIX_EN 0x10 +#define OV9640_COM13_Y_DELAY_EN 0x08 +#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) + +#define OV9640_COM15_OR_00FF 0x00 +#define OV9640_COM15_OR_01FE 0x40 +#define OV9640_COM15_OR_10F0 0xc0 +#define OV9640_COM15_RGB_NORM 0x00 +#define OV9640_COM15_RGB_565 0x10 +#define OV9640_COM15_RGB_555 0x30 + +#define OV9640_COM16_RB_AVG 0x01 + +/* IDs */ +#define OV9640_V2 0x9648 +#define OV9640_V3 0x9649 +#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) + +/* supported resolutions */ +enum { + W_QQCIF = 88, + W_QQVGA = 160, + W_QCIF = 176, + W_QVGA = 320, + W_CIF = 352, + W_VGA = 640, + W_SXGA = 1280 +}; +#define H_SXGA 960 + +/* Misc. structures */ +struct ov9640_reg_alt { + u8 com7; + u8 com12; + u8 com13; + u8 com15; +}; + +struct ov9640_reg { + u8 reg; + u8 val; +}; + +struct ov9640_priv { + struct v4l2_subdev subdev; + + int model; + int revision; + + bool flag_vflip; + bool flag_hflip; +}; + +#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index a1ad38fc49c1..73ec970ca5ca 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -14,8 +14,10 @@ * unless the userspace driver also doesn't work for you... * * Changes: - * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> - * - pms_capture: report back -EFAULT + * 25-11-2009 Hans Verkuil <hverkuil@xs4all.nl> + * - converted to version 2 of the V4L API. + * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> + * - pms_capture: report back -EFAULT */ #include <linux/module.h> @@ -27,175 +29,183 @@ #include <linux/mm.h> #include <linux/ioport.h> #include <linux/init.h> +#include <linux/version.h> +#include <linux/mutex.h> +#include <asm/uaccess.h> #include <asm/io.h> -#include <linux/videodev.h> + +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#include <linux/mutex.h> +#include <media/v4l2-device.h> -#include <asm/uaccess.h> +MODULE_LICENSE("GPL"); #define MOTOROLA 1 -#define PHILIPS2 2 +#define PHILIPS2 2 /* SAA7191 */ #define PHILIPS1 3 #define MVVMEMORYWIDTH 0x40 /* 512 bytes */ -struct pms_device -{ - struct video_device v; - struct video_picture picture; - int height; - int width; - unsigned long in_use; - struct mutex lock; -}; - -struct i2c_info -{ +struct i2c_info { u8 slave; u8 sub; u8 data; u8 hits; }; -static int i2c_count; -static struct i2c_info i2cinfo[64]; +struct pms { + struct v4l2_device v4l2_dev; + struct video_device vdev; + int height; + int width; + int depth; + int input; + s32 brightness, saturation, hue, contrast; + unsigned long in_use; + struct mutex lock; + int i2c_count; + struct i2c_info i2cinfo[64]; + + int decoder; + int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ + v4l2_std_id std; + int io; + int data; + void __iomem *mem; +}; -static int decoder = PHILIPS2; -static int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ +static struct pms pms_card; /* * I/O ports and Shared Memory */ -static int io_port = 0x250; -static int data_port = 0x251; -static int mem_base = 0xC8000; -static void __iomem *mem; -static int video_nr = -1; +static int io_port = 0x250; +module_param(io_port, int, 0); +static int mem_base = 0xc8000; +module_param(mem_base, int, 0); +static int video_nr = -1; +module_param(video_nr, int, 0); -static inline void mvv_write(u8 index, u8 value) + +static inline void mvv_write(struct pms *dev, u8 index, u8 value) { - outw(index|(value<<8), io_port); + outw(index | (value << 8), dev->io); } -static inline u8 mvv_read(u8 index) +static inline u8 mvv_read(struct pms *dev, u8 index) { - outb(index, io_port); - return inb(data_port); + outb(index, dev->io); + return inb(dev->data); } -static int pms_i2c_stat(u8 slave) +static int pms_i2c_stat(struct pms *dev, u8 slave) { - int counter; + int counter = 0; int i; - outb(0x28, io_port); + outb(0x28, dev->io); - counter=0; - while((inb(data_port)&0x01)==0) - if(counter++==256) + while ((inb(dev->data) & 0x01) == 0) + if (counter++ == 256) break; - while((inb(data_port)&0x01)!=0) - if(counter++==256) + while ((inb(dev->data) & 0x01) != 0) + if (counter++ == 256) break; - outb(slave, io_port); + outb(slave, dev->io); - counter=0; - while((inb(data_port)&0x01)==0) - if(counter++==256) + counter = 0; + while ((inb(dev->data) & 0x01) == 0) + if (counter++ == 256) break; - while((inb(data_port)&0x01)!=0) - if(counter++==256) + while ((inb(dev->data) & 0x01) != 0) + if (counter++ == 256) break; - for(i=0;i<12;i++) - { - char st=inb(data_port); - if((st&2)!=0) + for (i = 0; i < 12; i++) { + char st = inb(dev->data); + + if ((st & 2) != 0) return -1; - if((st&1)==0) + if ((st & 1) == 0) break; } - outb(0x29, io_port); - return inb(data_port); + outb(0x29, dev->io); + return inb(dev->data); } -static int pms_i2c_write(u16 slave, u16 sub, u16 data) +static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data) { - int skip=0; + int skip = 0; int count; int i; - for(i=0;i<i2c_count;i++) - { - if((i2cinfo[i].slave==slave) && - (i2cinfo[i].sub == sub)) - { - if(i2cinfo[i].data==data) - skip=1; - i2cinfo[i].data=data; - i=i2c_count+1; + for (i = 0; i < dev->i2c_count; i++) { + if ((dev->i2cinfo[i].slave == slave) && + (dev->i2cinfo[i].sub == sub)) { + if (dev->i2cinfo[i].data == data) + skip = 1; + dev->i2cinfo[i].data = data; + i = dev->i2c_count + 1; } } - if(i==i2c_count && i2c_count<64) - { - i2cinfo[i2c_count].slave=slave; - i2cinfo[i2c_count].sub=sub; - i2cinfo[i2c_count].data=data; - i2c_count++; + if (i == dev->i2c_count && dev->i2c_count < 64) { + dev->i2cinfo[dev->i2c_count].slave = slave; + dev->i2cinfo[dev->i2c_count].sub = sub; + dev->i2cinfo[dev->i2c_count].data = data; + dev->i2c_count++; } - if(skip) + if (skip) return 0; - mvv_write(0x29, sub); - mvv_write(0x2A, data); - mvv_write(0x28, slave); + mvv_write(dev, 0x29, sub); + mvv_write(dev, 0x2A, data); + mvv_write(dev, 0x28, slave); - outb(0x28, io_port); + outb(0x28, dev->io); - count=0; - while((inb(data_port)&1)==0) - if(count>255) + count = 0; + while ((inb(dev->data) & 1) == 0) + if (count > 255) break; - while((inb(data_port)&1)!=0) - if(count>255) + while ((inb(dev->data) & 1) != 0) + if (count > 255) break; - count=inb(data_port); + count = inb(dev->data); - if(count&2) + if (count & 2) return -1; return count; } -static int pms_i2c_read(int slave, int sub) +static int pms_i2c_read(struct pms *dev, int slave, int sub) { - int i=0; - for(i=0;i<i2c_count;i++) - { - if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub) - return i2cinfo[i].data; + int i; + + for (i = 0; i < dev->i2c_count; i++) { + if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub) + return dev->i2cinfo[i].data; } return 0; } -static void pms_i2c_andor(int slave, int sub, int and, int or) +static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or) { u8 tmp; - tmp=pms_i2c_read(slave, sub); - tmp = (tmp&and)|or; - pms_i2c_write(slave, sub, tmp); + tmp = pms_i2c_read(dev, slave, sub); + tmp = (tmp & and) | or; + pms_i2c_write(dev, slave, sub, tmp); } /* @@ -203,100 +213,108 @@ static void pms_i2c_andor(int slave, int sub, int and, int or) */ -static void pms_videosource(short source) +static void pms_videosource(struct pms *dev, short source) { - mvv_write(0x2E, source?0x31:0x30); + switch (dev->decoder) { + case MOTOROLA: + break; + case PHILIPS2: + pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0); + break; + case PHILIPS1: + break; + } + mvv_write(dev, 0x2E, 0x31); + /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30); + But could not make this work correctly. Only Composite input + worked for me. */ } -static void pms_hue(short hue) +static void pms_hue(struct pms *dev, short hue) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, hue); - break; - case PHILIPS2: - pms_i2c_write(0x8A, 0x07, hue); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x07, hue); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, hue); + break; + case PHILIPS2: + pms_i2c_write(dev, 0x8a, 0x07, hue); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x07, hue); + break; } } -static void pms_colour(short colour) +static void pms_saturation(struct pms *dev, short sat) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, colour); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x12, colour); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, sat); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x12, sat); + break; } } -static void pms_contrast(short contrast) +static void pms_contrast(struct pms *dev, short contrast) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, contrast); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x13, contrast); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, contrast); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x13, contrast); + break; } } -static void pms_brightness(short brightness) +static void pms_brightness(struct pms *dev, short brightness) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, brightness); - pms_i2c_write(0x8A, 0x00, brightness); - pms_i2c_write(0x8A, 0x00, brightness); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x19, brightness); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, brightness); + pms_i2c_write(dev, 0x8a, 0x00, brightness); + pms_i2c_write(dev, 0x8a, 0x00, brightness); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x19, brightness); + break; } } -static void pms_format(short format) +static void pms_format(struct pms *dev, short format) { int target; - standard = format; - if(decoder==PHILIPS1) - target=0x42; - else if(decoder==PHILIPS2) - target=0x8A; + dev->standard = format; + + if (dev->decoder == PHILIPS1) + target = 0x42; + else if (dev->decoder == PHILIPS2) + target = 0x8a; else return; - switch(format) - { - case 0: /* Auto */ - pms_i2c_andor(target, 0x0D, 0xFE,0x00); - pms_i2c_andor(target, 0x0F, 0x3F,0x80); - break; - case 1: /* NTSC */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x00); - pms_i2c_andor(target, 0x0F, 0x3F, 0x40); - break; - case 2: /* PAL */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x00); - pms_i2c_andor(target, 0x0F, 0x3F, 0x00); - break; - case 3: /* SECAM */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x01); - pms_i2c_andor(target, 0x0F, 0x3F, 0x00); - break; + switch (format) { + case 0: /* Auto */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80); + break; + case 1: /* NTSC */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40); + break; + case 2: /* PAL */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); + break; + case 3: /* SECAM */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); + break; } } @@ -308,18 +326,17 @@ static void pms_format(short format) * people need it. We also don't yet use the PMS interrupt. */ -static void pms_hstart(short start) +static void pms_hstart(struct pms *dev, short start) { - switch(decoder) - { - case PHILIPS1: - pms_i2c_write(0x8A, 0x05, start); - pms_i2c_write(0x8A, 0x18, start); - break; - case PHILIPS2: - pms_i2c_write(0x42, 0x05, start); - pms_i2c_write(0x42, 0x18, start); - break; + switch (dev->decoder) { + case PHILIPS1: + pms_i2c_write(dev, 0x8a, 0x05, start); + pms_i2c_write(dev, 0x8a, 0x18, start); + break; + case PHILIPS2: + pms_i2c_write(dev, 0x42, 0x05, start); + pms_i2c_write(dev, 0x42, 0x18, start); + break; } } @@ -327,293 +344,271 @@ static void pms_hstart(short start) * Bandpass filters */ -static void pms_bandpass(short pass) +static void pms_bandpass(struct pms *dev, short pass) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4); } -static void pms_antisnow(short snow) +static void pms_antisnow(struct pms *dev, short snow) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2); } -static void pms_sharpness(short sharp) +static void pms_sharpness(struct pms *dev, short sharp) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03); } -static void pms_chromaagc(short agc) +static void pms_chromaagc(struct pms *dev, short agc) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5); } -static void pms_vertnoise(short noise) +static void pms_vertnoise(struct pms *dev, short noise) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x10, 0xFC, noise&3); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3); } -static void pms_forcecolour(short colour) +static void pms_forcecolour(struct pms *dev, short colour) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7); } -static void pms_antigamma(short gamma) +static void pms_antigamma(struct pms *dev, short gamma) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7); } -static void pms_prefilter(short filter) +static void pms_prefilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6); } -static void pms_hfilter(short filter) +static void pms_hfilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5); } -static void pms_vfilter(short filter) +static void pms_vfilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5); } -static void pms_killcolour(short colour) +static void pms_killcolour(struct pms *dev, short colour) { - if(decoder==PHILIPS2) - { - pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3); - pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3); - } - else if(decoder==PHILIPS1) - { - pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3); - pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3); + if (dev->decoder == PHILIPS2) { + pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3); + pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3); + } else if (dev->decoder == PHILIPS1) { + pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3); + pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3); } } -static void pms_chromagain(short chroma) +static void pms_chromagain(struct pms *dev, short chroma) { - if(decoder==PHILIPS2) - { - pms_i2c_write(0x8A, 0x11, chroma); - } - else if(decoder==PHILIPS1) - { - pms_i2c_write(0x42, 0x11, chroma); - } + if (dev->decoder == PHILIPS2) + pms_i2c_write(dev, 0x8a, 0x11, chroma); + else if (dev->decoder == PHILIPS1) + pms_i2c_write(dev, 0x42, 0x11, chroma); } -static void pms_spacialcompl(short data) +static void pms_spacialcompl(struct pms *dev, short data) { - mvv_write(0x3B, data); + mvv_write(dev, 0x3b, data); } -static void pms_spacialcomph(short data) +static void pms_spacialcomph(struct pms *dev, short data) { - mvv_write(0x3A, data); + mvv_write(dev, 0x3a, data); } -static void pms_vstart(short start) +static void pms_vstart(struct pms *dev, short start) { - mvv_write(0x16, start); - mvv_write(0x17, (start>>8)&0x01); + mvv_write(dev, 0x16, start); + mvv_write(dev, 0x17, (start >> 8) & 0x01); } #endif -static void pms_secamcross(short cross) +static void pms_secamcross(struct pms *dev, short cross) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5); } -static void pms_swsense(short sense) +static void pms_swsense(struct pms *dev, short sense) { - if(decoder==PHILIPS2) - { - pms_i2c_write(0x8A, 0x0A, sense); - pms_i2c_write(0x8A, 0x0B, sense); - } - else if(decoder==PHILIPS1) - { - pms_i2c_write(0x42, 0x0A, sense); - pms_i2c_write(0x42, 0x0B, sense); + if (dev->decoder == PHILIPS2) { + pms_i2c_write(dev, 0x8a, 0x0a, sense); + pms_i2c_write(dev, 0x8a, 0x0b, sense); + } else if (dev->decoder == PHILIPS1) { + pms_i2c_write(dev, 0x42, 0x0a, sense); + pms_i2c_write(dev, 0x42, 0x0b, sense); } } -static void pms_framerate(short frr) +static void pms_framerate(struct pms *dev, short frr) { - int fps=(standard==1)?30:25; - if(frr==0) + int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25; + + if (frr == 0) return; - fps=fps/frr; - mvv_write(0x14,0x80|fps); - mvv_write(0x15,1); + fps = fps/frr; + mvv_write(dev, 0x14, 0x80 | fps); + mvv_write(dev, 0x15, 1); } -static void pms_vert(u8 deciden, u8 decinum) +static void pms_vert(struct pms *dev, u8 deciden, u8 decinum) { - mvv_write(0x1C, deciden); /* Denominator */ - mvv_write(0x1D, decinum); /* Numerator */ + mvv_write(dev, 0x1c, deciden); /* Denominator */ + mvv_write(dev, 0x1d, decinum); /* Numerator */ } /* * Turn 16bit ratios into best small ratio the chipset can grok */ -static void pms_vertdeci(unsigned short decinum, unsigned short deciden) +static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden) { - /* Knock it down by /5 once */ - if(decinum%5==0) - { - deciden/=5; - decinum/=5; + /* Knock it down by / 5 once */ + if (decinum % 5 == 0) { + deciden /= 5; + decinum /= 5; } /* * 3's */ - while(decinum%3==0 && deciden%3==0) - { - deciden/=3; - decinum/=3; + while (decinum % 3 == 0 && deciden % 3 == 0) { + deciden /= 3; + decinum /= 3; } /* * 2's */ - while(decinum%2==0 && deciden%2==0) - { - decinum/=2; - deciden/=2; + while (decinum % 2 == 0 && deciden % 2 == 0) { + decinum /= 2; + deciden /= 2; } /* * Fudgyify */ - while(deciden>32) - { - deciden/=2; - decinum=(decinum+1)/2; + while (deciden > 32) { + deciden /= 2; + decinum = (decinum + 1) / 2; } - if(deciden==32) + if (deciden == 32) deciden--; - pms_vert(deciden,decinum); + pms_vert(dev, deciden, decinum); } -static void pms_horzdeci(short decinum, short deciden) +static void pms_horzdeci(struct pms *dev, short decinum, short deciden) { - if(decinum<=512) - { - if(decinum%5==0) - { - decinum/=5; - deciden/=5; + if (decinum <= 512) { + if (decinum % 5 == 0) { + decinum /= 5; + deciden /= 5; } - } - else - { - decinum=512; - deciden=640; /* 768 would be ideal */ + } else { + decinum = 512; + deciden = 640; /* 768 would be ideal */ } - while(((decinum|deciden)&1)==0) - { - decinum>>=1; - deciden>>=1; + while (((decinum | deciden) & 1) == 0) { + decinum >>= 1; + deciden >>= 1; } - while(deciden>32) - { - deciden>>=1; - decinum=(decinum+1)>>1; + while (deciden > 32) { + deciden >>= 1; + decinum = (decinum + 1) >> 1; } - if(deciden==32) + if (deciden == 32) deciden--; - mvv_write(0x24, 0x80|deciden); - mvv_write(0x25, decinum); + mvv_write(dev, 0x24, 0x80 | deciden); + mvv_write(dev, 0x25, decinum); } -static void pms_resolution(short width, short height) +static void pms_resolution(struct pms *dev, short width, short height) { int fg_height; - fg_height=height; - if(fg_height>280) - fg_height=280; + fg_height = height; + if (fg_height > 280) + fg_height = 280; - mvv_write(0x18, fg_height); - mvv_write(0x19, fg_height>>8); + mvv_write(dev, 0x18, fg_height); + mvv_write(dev, 0x19, fg_height >> 8); - if(standard==1) - { - mvv_write(0x1A, 0xFC); - mvv_write(0x1B, 0x00); - if(height>fg_height) - pms_vertdeci(240,240); + if (dev->std & V4L2_STD_525_60) { + mvv_write(dev, 0x1a, 0xfc); + mvv_write(dev, 0x1b, 0x00); + if (height > fg_height) + pms_vertdeci(dev, 240, 240); else - pms_vertdeci(fg_height,240); - } - else - { - mvv_write(0x1A, 0x1A); - mvv_write(0x1B, 0x01); - if(fg_height>256) - pms_vertdeci(270,270); + pms_vertdeci(dev, fg_height, 240); + } else { + mvv_write(dev, 0x1a, 0x1a); + mvv_write(dev, 0x1b, 0x01); + if (fg_height > 256) + pms_vertdeci(dev, 270, 270); else - pms_vertdeci(fg_height, 270); + pms_vertdeci(dev, fg_height, 270); } - mvv_write(0x12,0); - mvv_write(0x13, MVVMEMORYWIDTH); - mvv_write(0x42, 0x00); - mvv_write(0x43, 0x00); - mvv_write(0x44, MVVMEMORYWIDTH); + mvv_write(dev, 0x12, 0); + mvv_write(dev, 0x13, MVVMEMORYWIDTH); + mvv_write(dev, 0x42, 0x00); + mvv_write(dev, 0x43, 0x00); + mvv_write(dev, 0x44, MVVMEMORYWIDTH); - mvv_write(0x22, width+8); - mvv_write(0x23, (width+8)>> 8); + mvv_write(dev, 0x22, width + 8); + mvv_write(dev, 0x23, (width + 8) >> 8); - if(standard==1) - pms_horzdeci(width,640); + if (dev->std & V4L2_STD_525_60) + pms_horzdeci(dev, width, 640); else - pms_horzdeci(width+8, 768); + pms_horzdeci(dev, width + 8, 768); - mvv_write(0x30, mvv_read(0x30)&0xFE); - mvv_write(0x08, mvv_read(0x08)|0x01); - mvv_write(0x01, mvv_read(0x01)&0xFD); - mvv_write(0x32, 0x00); - mvv_write(0x33, MVVMEMORYWIDTH); + mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe); + mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01); + mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd); + mvv_write(dev, 0x32, 0x00); + mvv_write(dev, 0x33, MVVMEMORYWIDTH); } @@ -621,52 +616,49 @@ static void pms_resolution(short width, short height) * Set Input */ -static void pms_vcrinput(short input) +static void pms_vcrinput(struct pms *dev, short input) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7); } -static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count) +static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count) { int y; - int dw = 2*dev->width; - - char tmp[dw+32]; /* using a temp buffer is faster than direct */ + int dw = 2 * dev->width; + char tmp[dw + 32]; /* using a temp buffer is faster than direct */ int cnt = 0; - int len=0; + int len = 0; unsigned char r8 = 0x5; /* value for reg8 */ if (rgb555) r8 |= 0x20; /* else use untranslated rgb = 565 */ - mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */ + mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */ /* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */ - for (y = 0; y < dev->height; y++ ) - { - writeb(0, mem); /* synchronisiert neue Zeile */ + for (y = 0; y < dev->height; y++) { + writeb(0, dev->mem); /* synchronisiert neue Zeile */ /* * This is in truth a fifo, be very careful as if you * forgot this odd things will occur 8) */ - memcpy_fromio(tmp, mem, dw+32); /* discard 16 word */ + memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */ cnt -= dev->height; - while (cnt <= 0) - { + while (cnt <= 0) { /* * Don't copy too far */ - int dt=dw; - if(dt+len>count) - dt=count-len; + int dt = dw; + if (dt + len > count) + dt = count - len; cnt += dev->height; - if (copy_to_user(buf, tmp+32, dt)) + if (copy_to_user(buf, tmp + 32, dt)) return len ? len : -EFAULT; buf += dt; len += dt; @@ -680,221 +672,278 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int * Video4linux interfacing */ -static long pms_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int pms_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) { - struct video_device *dev = video_devdata(file); - struct pms_device *pd=(struct pms_device *)dev; - - switch(cmd) - { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - strcpy(b->name, "Mediavision PMS"); - b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; - b->channels = 4; - b->audios = 0; - b->maxwidth = 640; - b->maxheight = 480; - b->minwidth = 16; - b->minheight = 16; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - if(v->channel<0 || v->channel>3) - return -EINVAL; - v->flags=0; - v->tuners=1; - /* Good question.. its composite or SVHS so.. */ - v->type = VIDEO_TYPE_CAMERA; - switch(v->channel) - { - case 0: - strcpy(v->name, "Composite");break; - case 1: - strcpy(v->name, "SVideo");break; - case 2: - strcpy(v->name, "Composite(VCR)");break; - case 3: - strcpy(v->name, "SVideo(VCR)");break; - } - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - if(v->channel<0 || v->channel>3) - return -EINVAL; - mutex_lock(&pd->lock); - pms_videosource(v->channel&1); - pms_vcrinput(v->channel>>1); - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - strcpy(v->name, "Format"); - v->rangelow=0; - v->rangehigh=0; - v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; - switch(standard) - { - case 0: - v->mode = VIDEO_MODE_AUTO; - break; - case 1: - v->mode = VIDEO_MODE_NTSC; - break; - case 2: - v->mode = VIDEO_MODE_PAL; - break; - case 3: - v->mode = VIDEO_MODE_SECAM; - break; - } - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - mutex_lock(&pd->lock); - switch(v->mode) - { - case VIDEO_MODE_AUTO: - pms_framerate(25); - pms_secamcross(0); - pms_format(0); - break; - case VIDEO_MODE_NTSC: - pms_framerate(30); - pms_secamcross(0); - pms_format(1); - break; - case VIDEO_MODE_PAL: - pms_framerate(25); - pms_secamcross(0); - pms_format(2); - break; - case VIDEO_MODE_SECAM: - pms_framerate(25); - pms_secamcross(1); - pms_format(2); - break; - default: - mutex_unlock(&pd->lock); - return -EINVAL; - } - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - *p = pd->picture; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16) - ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15))) - return -EINVAL; - pd->picture= *p; + struct pms *dev = video_drvdata(file); - /* - * Now load the card. - */ + strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card)); + strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info)); + vcap->version = KERNEL_VERSION(0, 0, 3); + vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; +} - mutex_lock(&pd->lock); - pms_brightness(p->brightness>>8); - pms_hue(p->hue>>8); - pms_colour(p->colour>>8); - pms_contrast(p->contrast>>8); - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - if(vw->flags) - return -EINVAL; - if(vw->clipcount) - return -EINVAL; - if(vw->height<16||vw->height>480) - return -EINVAL; - if(vw->width<16||vw->width>640) - return -EINVAL; - pd->width=vw->width; - pd->height=vw->height; - mutex_lock(&pd->lock); - pms_resolution(pd->width, pd->height); - mutex_unlock(&pd->lock); /* Ok we figured out what to use from our wide choice */ - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - memset(vw,0,sizeof(*vw)); - vw->width=pd->width; - vw->height=pd->height; - return 0; - } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; +static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin) +{ + static const char *inputs[4] = { + "Composite", + "S-Video", + "Composite (VCR)", + "S-Video (VCR)" + }; + + if (vin->index > 3) + return -EINVAL; + strlcpy(vin->name, inputs[vin->index], sizeof(vin->name)); + vin->type = V4L2_INPUT_TYPE_CAMERA; + vin->audioset = 0; + vin->tuner = 0; + vin->std = V4L2_STD_ALL; + vin->status = 0; + return 0; +} + +static int pms_g_input(struct file *file, void *fh, unsigned int *inp) +{ + struct pms *dev = video_drvdata(file); + + *inp = dev->input; + return 0; +} + +static int pms_s_input(struct file *file, void *fh, unsigned int inp) +{ + struct pms *dev = video_drvdata(file); + + if (inp > 3) + return -EINVAL; + + mutex_lock(&dev->lock); + dev->input = inp; + pms_videosource(dev, inp & 1); + pms_vcrinput(dev, inp >> 1); + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct pms *dev = video_drvdata(file); + + *std = dev->std; + return 0; +} + +static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + dev->std = *std; + mutex_lock(&dev->lock); + if (dev->std & V4L2_STD_NTSC) { + pms_framerate(dev, 30); + pms_secamcross(dev, 0); + pms_format(dev, 1); + } else if (dev->std & V4L2_STD_PAL) { + pms_framerate(dev, 25); + pms_secamcross(dev, 0); + pms_format(dev, 2); + } else if (dev->std & V4L2_STD_SECAM) { + pms_framerate(dev, 25); + pms_secamcross(dev, 1); + pms_format(dev, 2); + } else { + ret = -EINVAL; + } + /* + switch (v->mode) { + case VIDEO_MODE_AUTO: + pms_framerate(dev, 25); + pms_secamcross(dev, 0); + pms_format(dev, 0); + break; + }*/ + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0); + } + return -EINVAL; +} + +static int pms_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dev->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dev->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dev->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dev->hue; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int pms_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + mutex_lock(&dev->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + dev->brightness = ctrl->value; + pms_brightness(dev, dev->brightness); + break; + case V4L2_CID_CONTRAST: + dev->contrast = ctrl->value; + pms_contrast(dev, dev->contrast); + break; + case V4L2_CID_SATURATION: + dev->saturation = ctrl->value; + pms_saturation(dev, dev->saturation); + break; + case V4L2_CID_HUE: + dev->hue = ctrl->value; + pms_hue(dev, dev->hue); + break; + default: + ret = -EINVAL; + break; } + mutex_unlock(&dev->lock); + return ret; +} + +static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct pms *dev = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + pix->width = dev->width; + pix->height = dev->height; + pix->pixelformat = dev->width == 15 ? + V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * dev->width; + pix->sizeimage = 2 * dev->width * dev->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SRGB; return 0; } -static long pms_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - return video_usercopy(file, cmd, arg, pms_do_ioctl); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + if (pix->height < 16 || pix->height > 480) + return -EINVAL; + if (pix->width < 16 || pix->width > 640) + return -EINVAL; + if (pix->pixelformat != V4L2_PIX_FMT_RGB555 && + pix->pixelformat != V4L2_PIX_FMT_RGB565) + return -EINVAL; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * pix->width; + pix->sizeimage = 2 * pix->width * pix->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SRGB; + return 0; +} + +static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct pms *dev = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int ret = pms_try_fmt_vid_cap(file, fh, fmt); + + if (ret) + return ret; + mutex_lock(&dev->lock); + dev->width = pix->width; + dev->height = pix->height; + dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16; + pms_resolution(dev, dev->width, dev->height); + /* Ok we figured out what to use from our wide choice */ + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) +{ + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "RGB 5:5:5", V4L2_PIX_FMT_RGB555, + { 0, 0, 0, 0 } + }, + { 0, 0, 0, + "RGB 5:6:5", V4L2_PIX_FMT_RGB565, + { 0, 0, 0, 0 } + }, + }; + enum v4l2_buf_type type = fmt->type; + + if (fmt->index > 1) + return -EINVAL; + + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; } static ssize_t pms_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct video_device *v = video_devdata(file); - struct pms_device *pd=(struct pms_device *)v; + struct pms *dev = video_drvdata(file); int len; - mutex_lock(&pd->lock); - len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count); - mutex_unlock(&pd->lock); + mutex_lock(&dev->lock); + len = pms_capture(dev, buf, (dev->depth == 15), count); + mutex_unlock(&dev->lock); return len; } static int pms_exclusive_open(struct file *file) { - struct video_device *v = video_devdata(file); - struct pms_device *pd = (struct pms_device *)v; + struct pms *dev = video_drvdata(file); - return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0; + return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0; } static int pms_exclusive_release(struct file *file) { - struct video_device *v = video_devdata(file); - struct pms_device *pd = (struct pms_device *)v; + struct pms *dev = video_drvdata(file); - clear_bit(0, &pd->in_use); + clear_bit(0, &dev->in_use); return 0; } @@ -902,78 +951,81 @@ static const struct v4l2_file_operations pms_fops = { .owner = THIS_MODULE, .open = pms_exclusive_open, .release = pms_exclusive_release, - .ioctl = pms_ioctl, + .ioctl = video_ioctl2, .read = pms_read, }; -static struct video_device pms_template= -{ - .name = "Mediavision PMS", - .fops = &pms_fops, - .release = video_device_release_empty, +static const struct v4l2_ioctl_ops pms_ioctl_ops = { + .vidioc_querycap = pms_querycap, + .vidioc_g_input = pms_g_input, + .vidioc_s_input = pms_s_input, + .vidioc_enum_input = pms_enum_input, + .vidioc_g_std = pms_g_std, + .vidioc_s_std = pms_s_std, + .vidioc_queryctrl = pms_queryctrl, + .vidioc_g_ctrl = pms_g_ctrl, + .vidioc_s_ctrl = pms_s_ctrl, + .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, }; -static struct pms_device pms_device; - - /* * Probe for and initialise the Mediavision PMS */ -static int init_mediavision(void) +static int init_mediavision(struct pms *dev) { int id; int idec, decst; int i; - - unsigned char i2c_defs[]={ - 0x4C,0x30,0x00,0xE8, - 0xB6,0xE2,0x00,0x00, - 0xFF,0xFF,0x00,0x00, - 0x00,0x00,0x78,0x98, - 0x00,0x00,0x00,0x00, - 0x34,0x0A,0xF4,0xCE, - 0xE4 + static const unsigned char i2c_defs[] = { + 0x4c, 0x30, 0x00, 0xe8, + 0xb6, 0xe2, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x98, + 0x00, 0x00, 0x00, 0x00, + 0x34, 0x0a, 0xf4, 0xce, + 0xe4 }; - mem = ioremap(mem_base, 0x800); - if (!mem) + dev->mem = ioremap(mem_base, 0x800); + if (!dev->mem) return -ENOMEM; - if (!request_region(0x9A01, 1, "Mediavision PMS config")) - { - printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n"); - iounmap(mem); + if (!request_region(0x9a01, 1, "Mediavision PMS config")) { + printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n"); + iounmap(dev->mem); return -EBUSY; } - if (!request_region(io_port, 3, "Mediavision PMS")) - { - printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port); - release_region(0x9A01, 1); - iounmap(mem); + if (!request_region(dev->io, 3, "Mediavision PMS")) { + printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io); + release_region(0x9a01, 1); + iounmap(dev->mem); return -EBUSY; } - outb(0xB8, 0x9A01); /* Unlock */ - outb(io_port>>4, 0x9A01); /* Set IO port */ + outb(0xb8, 0x9a01); /* Unlock */ + outb(dev->io >> 4, 0x9a01); /* Set IO port */ - id=mvv_read(3); - decst=pms_i2c_stat(0x43); + id = mvv_read(dev, 3); + decst = pms_i2c_stat(dev, 0x43); - if(decst!=-1) - idec=2; - else if(pms_i2c_stat(0xb9)!=-1) - idec=3; - else if(pms_i2c_stat(0x8b)!=-1) - idec=1; + if (decst != -1) + idec = 2; + else if (pms_i2c_stat(dev, 0xb9) != -1) + idec = 3; + else if (pms_i2c_stat(dev, 0x8b) != -1) + idec = 1; else - idec=0; + idec = 0; printk(KERN_INFO "PMS type is %d\n", idec); - if(idec == 0) { - release_region(io_port, 3); - release_region(0x9A01, 1); - iounmap(mem); + if (idec == 0) { + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); return -ENODEV; } @@ -981,51 +1033,50 @@ static int init_mediavision(void) * Ok we have a PMS of some sort */ - mvv_write(0x04, mem_base>>12); /* Set the memory area */ + mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */ /* Ok now load the defaults */ - for(i=0;i<0x19;i++) - { - if(i2c_defs[i]==0xFF) - pms_i2c_andor(0x8A, i, 0x07,0x00); + for (i = 0; i < 0x19; i++) { + if (i2c_defs[i] == 0xff) + pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00); else - pms_i2c_write(0x8A, i, i2c_defs[i]); + pms_i2c_write(dev, 0x8a, i, i2c_defs[i]); } - pms_i2c_write(0xB8,0x00,0x12); - pms_i2c_write(0xB8,0x04,0x00); - pms_i2c_write(0xB8,0x07,0x00); - pms_i2c_write(0xB8,0x08,0x00); - pms_i2c_write(0xB8,0x09,0xFF); - pms_i2c_write(0xB8,0x0A,0x00); - pms_i2c_write(0xB8,0x0B,0x10); - pms_i2c_write(0xB8,0x10,0x03); - - mvv_write(0x01, 0x00); - mvv_write(0x05, 0xA0); - mvv_write(0x08, 0x25); - mvv_write(0x09, 0x00); - mvv_write(0x0A, 0x20|MVVMEMORYWIDTH); - - mvv_write(0x10, 0x02); - mvv_write(0x1E, 0x0C); - mvv_write(0x1F, 0x03); - mvv_write(0x26, 0x06); - - mvv_write(0x2B, 0x00); - mvv_write(0x2C, 0x20); - mvv_write(0x2D, 0x00); - mvv_write(0x2F, 0x70); - mvv_write(0x32, 0x00); - mvv_write(0x33, MVVMEMORYWIDTH); - mvv_write(0x34, 0x00); - mvv_write(0x35, 0x00); - mvv_write(0x3A, 0x80); - mvv_write(0x3B, 0x10); - mvv_write(0x20, 0x00); - mvv_write(0x21, 0x00); - mvv_write(0x30, 0x22); + pms_i2c_write(dev, 0xb8, 0x00, 0x12); + pms_i2c_write(dev, 0xb8, 0x04, 0x00); + pms_i2c_write(dev, 0xb8, 0x07, 0x00); + pms_i2c_write(dev, 0xb8, 0x08, 0x00); + pms_i2c_write(dev, 0xb8, 0x09, 0xff); + pms_i2c_write(dev, 0xb8, 0x0a, 0x00); + pms_i2c_write(dev, 0xb8, 0x0b, 0x10); + pms_i2c_write(dev, 0xb8, 0x10, 0x03); + + mvv_write(dev, 0x01, 0x00); + mvv_write(dev, 0x05, 0xa0); + mvv_write(dev, 0x08, 0x25); + mvv_write(dev, 0x09, 0x00); + mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH); + + mvv_write(dev, 0x10, 0x02); + mvv_write(dev, 0x1e, 0x0c); + mvv_write(dev, 0x1f, 0x03); + mvv_write(dev, 0x26, 0x06); + + mvv_write(dev, 0x2b, 0x00); + mvv_write(dev, 0x2c, 0x20); + mvv_write(dev, 0x2d, 0x00); + mvv_write(dev, 0x2f, 0x70); + mvv_write(dev, 0x32, 0x00); + mvv_write(dev, 0x33, MVVMEMORYWIDTH); + mvv_write(dev, 0x34, 0x00); + mvv_write(dev, 0x35, 0x00); + mvv_write(dev, 0x3a, 0x80); + mvv_write(dev, 0x3b, 0x10); + mvv_write(dev, 0x20, 0x00); + mvv_write(dev, 0x21, 0x00); + mvv_write(dev, 0x30, 0x22); return 0; } @@ -1038,53 +1089,77 @@ static int enable; module_param(enable, int, 0); #endif -static int __init init_pms_cards(void) +static int __init pms_init(void) { - printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); + struct pms *dev = &pms_card; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + int res; + + strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name)); + + v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n"); #ifndef MODULE if (!enable) { - printk(KERN_INFO "PMS: not enabled, use pms.enable=1 to " - "probe\n"); + v4l2_err(v4l2_dev, + "PMS: not enabled, use pms.enable=1 to probe\n"); return -ENODEV; } #endif - data_port = io_port +1; + dev->decoder = PHILIPS2; + dev->io = io_port; + dev->data = io_port + 1; - if(init_mediavision()) - { - printk(KERN_INFO "Board not found.\n"); + if (init_mediavision(dev)) { + v4l2_err(v4l2_dev, "Board not found.\n"); return -ENODEV; } - memcpy(&pms_device, &pms_template, sizeof(pms_template)); - mutex_init(&pms_device.lock); - pms_device.height=240; - pms_device.width=320; - pms_swsense(75); - pms_resolution(320,240); - return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr); -} - -module_param(io_port, int, 0); -module_param(mem_base, int, 0); -module_param(video_nr, int, 0); -MODULE_LICENSE("GPL"); + res = v4l2_device_register(NULL, v4l2_dev); + if (res < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return res; + } -static void __exit shutdown_mediavision(void) -{ - release_region(io_port,3); - release_region(0x9A01, 1); + strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); + dev->vdev.v4l2_dev = v4l2_dev; + dev->vdev.fops = &pms_fops; + dev->vdev.ioctl_ops = &pms_ioctl_ops; + dev->vdev.release = video_device_release_empty; + video_set_drvdata(&dev->vdev, dev); + mutex_init(&dev->lock); + dev->std = V4L2_STD_NTSC_M; + dev->height = 240; + dev->width = 320; + dev->depth = 15; + dev->brightness = 139; + dev->contrast = 70; + dev->hue = 0; + dev->saturation = 64; + pms_swsense(dev, 75); + pms_resolution(dev, 320, 240); + pms_videosource(dev, 0); + pms_vcrinput(dev, 0); + if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + v4l2_device_unregister(&dev->v4l2_dev); + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); + return -EINVAL; + } + return 0; } -static void __exit cleanup_pms_module(void) +static void __exit pms_exit(void) { - shutdown_mediavision(); - video_unregister_device((struct video_device *)&pms_device); - iounmap(mem); -} + struct pms *dev = &pms_card; -module_init(init_pms_cards); -module_exit(cleanup_pms_module); + video_unregister_device(&dev->vdev); + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); +} +module_init(pms_init); +module_exit(pms_exit); diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index fbe3856bdca6..ae977668c496 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -142,6 +142,9 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) { int bcnt = 0; int ccnt; + ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n", + pvr2_hdw_get_desc(hdw)); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = scnprintf(buf,acnt,"Driver state info:\n"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = pvr2_hdw_state_report(hdw,buf,acnt); @@ -249,11 +252,15 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); if (scnt && wptr) { count -= scnt; buf += scnt; - if (debugifc_match_keyword(wptr,wlen,"prom")) { - pvr2_hdw_cpufw_set_enabled(hdw,!0,!0); - } else if (debugifc_match_keyword(wptr,wlen, - "ram")) { - pvr2_hdw_cpufw_set_enabled(hdw,0,!0); + if (debugifc_match_keyword(wptr, wlen, + "prom")) { + pvr2_hdw_cpufw_set_enabled(hdw, 2, !0); + } else if (debugifc_match_keyword(wptr, wlen, + "ram8k")) { + pvr2_hdw_cpufw_set_enabled(hdw, 0, !0); + } else if (debugifc_match_keyword(wptr, wlen, + "ram16k")) { + pvr2_hdw_cpufw_set_enabled(hdw, 1, !0); } else { return -EINVAL; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index e4d7c13cab87..6bc16c13ccef 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -58,7 +58,7 @@ static const char *pvr2_fw1_names_29xxx[] = { }; static const struct pvr2_device_desc pvr2_device_29xxx = { - .description = "WinTV PVR USB2 Model Category 29xxx", + .description = "WinTV PVR USB2 Model 29xxx", .shortname = "29xxx", .client_table.lst = pvr2_cli_29xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx), @@ -91,7 +91,7 @@ static const char *pvr2_fw1_names_24xxx[] = { }; static const struct pvr2_device_desc pvr2_device_24xxx = { - .description = "WinTV PVR USB2 Model Category 24xxx", + .description = "WinTV PVR USB2 Model 24xxx", .shortname = "24xxx", .client_table.lst = pvr2_cli_24xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx), @@ -340,7 +340,7 @@ static const char *pvr2_fw1_names_73xxx[] = { }; static const struct pvr2_device_desc pvr2_device_73xxx = { - .description = "WinTV HVR-1900 Model Category 73xxx", + .description = "WinTV HVR-1900 Model 73xxx", .shortname = "73xxx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -351,6 +351,7 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, @@ -445,7 +446,7 @@ static const char *pvr2_fw1_names_75xxx[] = { }; static const struct pvr2_device_desc pvr2_device_750xx = { - .description = "WinTV HVR-1950 Model Category 750xx", + .description = "WinTV HVR-1950 Model 750xx", .shortname = "750xx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -456,6 +457,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, @@ -467,7 +469,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = { }; static const struct pvr2_device_desc pvr2_device_751xx = { - .description = "WinTV HVR-1950 Model Category 751xx", + .description = "WinTV HVR-1950 Model 751xx", .shortname = "751xx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -478,6 +480,7 @@ static const struct pvr2_device_desc pvr2_device_751xx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h index ea04ecf8aa39..e5b9594eb5f6 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -176,6 +176,7 @@ struct pvr2_device_desc { unsigned int flag_has_analogtuner:1; /* Has analog tuner */ unsigned int flag_has_composite:1; /* Has composite input */ unsigned int flag_has_svideo:1; /* Has s-video input */ + unsigned int flag_fx2_16kb:1; /* 16KB FX2 firmware OK here */ }; extern struct usb_device_id pvr2_device_table[]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 54ac5349dee2..e046fdaec5ae 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -294,7 +294,10 @@ static int pvr2_encoder_cmd(void *ctxt, pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." - " This is normally recovered by the driver."); + " This is normally recovered via a firmware" + " reload and re-initialization; concern" + " is only warranted if this happens repeatedly" + " and rapidly."); break; } wrData[0] = 0x7; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 5b152ff20bd0..de5485f506b1 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -270,6 +270,7 @@ struct pvr2_hdw { int force_dirty; /* consider all controls dirty if true */ int flag_ok; /* device in known good state */ + int flag_modulefail; /* true if at least one module failed to load */ int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ int fw1_state; /* current situation with fw1 */ @@ -332,7 +333,7 @@ struct pvr2_hdw { /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */ unsigned int input_avail_mask; - /* Bit mask of PVR2_CVAL_INPUT choices which are currenly allowed */ + /* Bit mask of PVR2_CVAL_INPUT choices which are currently allowed */ unsigned int input_allowed_mask; /* Location of eeprom or a negative number if none */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 13639b302700..1bbdab08fe0e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1447,6 +1447,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) const struct firmware *fw_entry = NULL; void *fw_ptr; unsigned int pipe; + unsigned int fwsize; int ret; u16 address; @@ -1473,9 +1474,21 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f)); pipe = usb_sndctrlpipe(hdw->usb_dev, 0); + fwsize = fw_entry->size; - if (fw_entry->size != 0x2000){ - pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size"); + if ((fwsize != 0x2000) && + (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) { + if (hdw->hdw_desc->flag_fx2_16kb) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Wrong fx2 firmware size" + " (expected 8192 or 16384, got %u)", + fwsize); + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Wrong fx2 firmware size" + " (expected 8192, got %u)", + fwsize); + } release_firmware(fw_entry); return -ENOMEM; } @@ -1493,7 +1506,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) chunk. */ ret = 0; - for(address = 0; address < fw_entry->size; address += 0x800) { + for (address = 0; address < fwsize; address += 0x800) { memcpy(fw_ptr, fw_entry->data + address, 0x800); ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address, 0, fw_ptr, 0x800, HZ); @@ -1509,8 +1522,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) trace_firmware("Upload done (%d bytes sent)",ret); - /* We should have written 8192 bytes */ - if (ret == 8192) { + /* We should have written fwsize bytes */ + if (ret == fwsize) { hdw->fw1_state = FW1_STATE_RELOAD; return 0; } @@ -2030,7 +2043,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL; if (!fname) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u for device %s has no name", + "Module ID %u for device %s has no name?" + " The driver might have a configuration problem.", mid, hdw->hdw_desc->description); return -EINVAL; @@ -2058,7 +2072,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, if (!i2ccnt) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Module ID %u (%s) for device %s:" - " No i2c addresses", + " No i2c addresses." + " The driver might have a configuration problem.", mid, fname, hdw->hdw_desc->description); return -EINVAL; } @@ -2090,7 +2105,9 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, if (!sd) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u (%s) for device %s failed to load", + "Module ID %u (%s) for device %s failed to load." + " Possible missing sub-device kernel module or" + " initialization failure within module.", mid, fname, hdw->hdw_desc->description); return -EIO; } @@ -2132,7 +2149,10 @@ static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw) for (idx = 0; idx < ct->cnt; idx++) { if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0; } - if (!okFl) pvr2_hdw_render_useless(hdw); + if (!okFl) { + hdw->flag_modulefail = !0; + pvr2_hdw_render_useless(hdw); + } } @@ -2334,6 +2354,20 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw) break; } } + if (hdw->flag_modulefail) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "***WARNING*** pvrusb2 driver initialization" + " failed due to the failure of one or more" + " sub-device kernel modules."); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "You need to resolve the failing condition" + " before this driver can function. There" + " should be some earlier messages giving more" + " information about the problem."); + break; + } if (procreload) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, @@ -2419,6 +2453,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", hdw,hdw_desc->description); + pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s", + hdw_desc->description); if (!hdw) goto fail; init_timer(&hdw->quiescent_timer); @@ -3480,7 +3516,7 @@ static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw) void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, - int prom_flag, + int mode, int enable_flag) { int ret; @@ -3503,11 +3539,12 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, break; } - hdw->fw_cpu_flag = (prom_flag == 0); + hdw->fw_cpu_flag = (mode != 2); if (hdw->fw_cpu_flag) { + hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000; pvr2_trace(PVR2_TRACE_FIRMWARE, - "Preparing to suck out CPU firmware"); - hdw->fw_size = 0x2000; + "Preparing to suck out CPU firmware" + " (size=%u)", hdw->fw_size); hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL); if (!hdw->fw_buffer) { hdw->fw_size = 0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 7b6940554e9a..56e70eae20c1 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -219,7 +219,7 @@ int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, this may prevent the device from running (and leaving this mode may imply a device reset). */ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, - int prom_flag, + int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */ int enable_flag); /* Return true if we're in a mode for retrieval CPU firmware */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index a334b1a966a2..7cbe18c4ca95 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -50,6 +50,7 @@ MODULE_PARM_DESC(disable_autoload_ir_video, /* Mapping of IR schemes to known I2C addresses - if any */ static const unsigned char ir_video_addresses[] = { + [PVR2_IR_SCHEME_ZILOG] = 0x71, [PVR2_IR_SCHEME_29XXX] = 0x18, [PVR2_IR_SCHEME_24XXX] = 0x18, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 2d8825e5b1be..6aa48e0ae731 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -913,6 +913,15 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) } +static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip) +{ + if (!dip) return; + if (!dip->devbase.parent) return; + dip->devbase.parent = NULL; + device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE); +} + + static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) { if (vp->dev_video) { @@ -943,6 +952,8 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp) struct pvr2_v4l2 *vp; vp = container_of(chp,struct pvr2_v4l2,channel); if (!vp->channel.mc_head->disconnect_flag) return; + pvr2_v4l2_dev_disassociate_parent(vp->dev_video); + pvr2_v4l2_dev_disassociate_parent(vp->dev_radio); if (vp->vfirst) return; pvr2_v4l2_destroy_no_lock(vp); } @@ -1250,12 +1261,13 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct pvr2_v4l2 *vp, int v4l_type) { + struct usb_device *usbdev; int mindevnum; int unit_number; int *nr_ptr = NULL; dip->v4lp = vp; - + usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw); dip->v4l_type = v4l_type; switch (v4l_type) { case VFL_TYPE_GRABBER: @@ -1296,6 +1308,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { mindevnum = nr_ptr[unit_number]; } + dip->devbase.parent = &usbdev->dev; if ((video_register_device(&dip->devbase, dip->v4l_type, mindevnum) < 0) && (video_register_device(&dip->devbase, diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index f976df452a34..89b620f6db7b 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -68,6 +68,7 @@ #endif #include <linux/vmalloc.h> #include <asm/io.h> +#include <linux/kernel.h> /* simple_strtol() */ #include "pwc.h" #include "pwc-kiara.h" @@ -1916,19 +1917,6 @@ disconnect_out: unlock_kernel(); } -/* *grunt* We have to do atoi ourselves :-( */ -static int pwc_atoi(const char *s) -{ - int k = 0; - - k = 0; - while (*s != '\0' && *s >= '0' && *s <= '9') { - k = 10 * k + (*s - '0'); - s++; - } - return k; -} - /* * Initialization code & module stuff @@ -2078,13 +2066,16 @@ static int __init usb_pwc_init(void) } else { /* No type or serial number specified, just a number. */ - device_hint[i].device_node = pwc_atoi(s); + device_hint[i].device_node = + simple_strtol(s, NULL, 10); } } else { /* There's a colon, so we have at least a type and a device node */ - device_hint[i].type = pwc_atoi(s); - device_hint[i].device_node = pwc_atoi(colon + 1); + device_hint[i].type = + simple_strtol(s, NULL, 10); + device_hint[i].device_node = + simple_strtol(colon + 1, NULL, 10); if (*dot != '\0') { /* There's a serial number as well */ int k; diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c new file mode 100644 index 000000000000..373f2a30a677 --- /dev/null +++ b/drivers/media/video/rj54n1cb0c.c @@ -0,0 +1,1219 @@ +/* + * Driver for RJ54N1CB0C CMOS Image Sensor from Micron + * + * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/v4l2-subdev.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#define RJ54N1_DEV_CODE 0x0400 +#define RJ54N1_DEV_CODE2 0x0401 +#define RJ54N1_OUT_SEL 0x0403 +#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 +#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 +#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 +#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 +#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 +#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 +#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a +#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b +#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c +#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d +#define RJ54N1_RESIZE_N 0x040e +#define RJ54N1_RESIZE_N_STEP 0x040f +#define RJ54N1_RESIZE_STEP 0x0410 +#define RJ54N1_RESIZE_HOLD_H 0x0411 +#define RJ54N1_RESIZE_HOLD_L 0x0412 +#define RJ54N1_H_OBEN_OFS 0x0413 +#define RJ54N1_V_OBEN_OFS 0x0414 +#define RJ54N1_RESIZE_CONTROL 0x0415 +#define RJ54N1_INC_USE_SEL_H 0x0425 +#define RJ54N1_INC_USE_SEL_L 0x0426 +#define RJ54N1_MIRROR_STILL_MODE 0x0427 +#define RJ54N1_INIT_START 0x0428 +#define RJ54N1_SCALE_1_2_LEV 0x0429 +#define RJ54N1_SCALE_4_LEV 0x042a +#define RJ54N1_Y_GAIN 0x04d8 +#define RJ54N1_APT_GAIN_UP 0x04fa +#define RJ54N1_RA_SEL_UL 0x0530 +#define RJ54N1_BYTE_SWAP 0x0531 +#define RJ54N1_OUT_SIGPO 0x053b +#define RJ54N1_FRAME_LENGTH_S_H 0x0595 +#define RJ54N1_FRAME_LENGTH_S_L 0x0596 +#define RJ54N1_FRAME_LENGTH_P_H 0x0597 +#define RJ54N1_FRAME_LENGTH_P_L 0x0598 +#define RJ54N1_IOC 0x05ef +#define RJ54N1_TG_BYPASS 0x0700 +#define RJ54N1_PLL_L 0x0701 +#define RJ54N1_PLL_N 0x0702 +#define RJ54N1_PLL_EN 0x0704 +#define RJ54N1_RATIO_TG 0x0706 +#define RJ54N1_RATIO_T 0x0707 +#define RJ54N1_RATIO_R 0x0708 +#define RJ54N1_RAMP_TGCLK_EN 0x0709 +#define RJ54N1_OCLK_DSP 0x0710 +#define RJ54N1_RATIO_OP 0x0711 +#define RJ54N1_RATIO_O 0x0712 +#define RJ54N1_OCLK_SEL_EN 0x0713 +#define RJ54N1_CLK_RST 0x0717 +#define RJ54N1_RESET_STANDBY 0x0718 + +#define E_EXCLK (1 << 7) +#define SOFT_STDBY (1 << 4) +#define SEN_RSTX (1 << 2) +#define TG_RSTX (1 << 1) +#define DSP_RSTX (1 << 0) + +#define RESIZE_HOLD_SEL (1 << 2) +#define RESIZE_GO (1 << 1) + +#define RJ54N1_COLUMN_SKIP 0 +#define RJ54N1_ROW_SKIP 0 +#define RJ54N1_MAX_WIDTH 1600 +#define RJ54N1_MAX_HEIGHT 1200 + +/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ + +static const struct soc_camera_data_format rj54n1_colour_formats[] = { + { + .name = "YUYV", + .depth = 16, + .fourcc = V4L2_PIX_FMT_YUYV, + .colorspace = V4L2_COLORSPACE_JPEG, + }, { + .name = "RGB565", + .depth = 16, + .fourcc = V4L2_PIX_FMT_RGB565, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +struct rj54n1_clock_div { + u8 ratio_tg; + u8 ratio_t; + u8 ratio_r; + u8 ratio_op; + u8 ratio_o; +}; + +struct rj54n1 { + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + unsigned short width; /* Output window */ + unsigned short height; + unsigned short resize; /* Sensor * 1024 / resize = Output */ + struct rj54n1_clock_div clk_div; + u32 fourcc; + unsigned short scale; + u8 bank; +}; + +struct rj54n1_reg_val { + u16 reg; + u8 val; +}; + +const static struct rj54n1_reg_val bank_4[] = { + {0x417, 0}, + {0x42c, 0}, + {0x42d, 0xf0}, + {0x42e, 0}, + {0x42f, 0x50}, + {0x430, 0xf5}, + {0x431, 0x16}, + {0x432, 0x20}, + {0x433, 0}, + {0x434, 0xc8}, + {0x43c, 8}, + {0x43e, 0x90}, + {0x445, 0x83}, + {0x4ba, 0x58}, + {0x4bb, 4}, + {0x4bc, 0x20}, + {0x4db, 4}, + {0x4fe, 2}, +}; + +const static struct rj54n1_reg_val bank_5[] = { + {0x514, 0}, + {0x516, 0}, + {0x518, 0}, + {0x51a, 0}, + {0x51d, 0xff}, + {0x56f, 0x28}, + {0x575, 0x40}, + {0x5bc, 0x48}, + {0x5c1, 6}, + {0x5e5, 0x11}, + {0x5e6, 0x43}, + {0x5e7, 0x33}, + {0x5e8, 0x21}, + {0x5e9, 0x30}, + {0x5ea, 0x0}, + {0x5eb, 0xa5}, + {0x5ec, 0xff}, + {0x5fe, 2}, +}; + +const static struct rj54n1_reg_val bank_7[] = { + {0x70a, 0}, + {0x714, 0xff}, + {0x715, 0xff}, + {0x716, 0x1f}, + {0x7FE, 0x02}, +}; + +const static struct rj54n1_reg_val bank_8[] = { + {0x800, 0x00}, + {0x801, 0x01}, + {0x802, 0x61}, + {0x805, 0x00}, + {0x806, 0x00}, + {0x807, 0x00}, + {0x808, 0x00}, + {0x809, 0x01}, + {0x80A, 0x61}, + {0x80B, 0x00}, + {0x80C, 0x01}, + {0x80D, 0x00}, + {0x80E, 0x00}, + {0x80F, 0x00}, + {0x810, 0x00}, + {0x811, 0x01}, + {0x812, 0x61}, + {0x813, 0x00}, + {0x814, 0x11}, + {0x815, 0x00}, + {0x816, 0x41}, + {0x817, 0x00}, + {0x818, 0x51}, + {0x819, 0x01}, + {0x81A, 0x1F}, + {0x81B, 0x00}, + {0x81C, 0x01}, + {0x81D, 0x00}, + {0x81E, 0x11}, + {0x81F, 0x00}, + {0x820, 0x41}, + {0x821, 0x00}, + {0x822, 0x51}, + {0x823, 0x00}, + {0x824, 0x00}, + {0x825, 0x00}, + {0x826, 0x47}, + {0x827, 0x01}, + {0x828, 0x4F}, + {0x829, 0x00}, + {0x82A, 0x00}, + {0x82B, 0x00}, + {0x82C, 0x30}, + {0x82D, 0x00}, + {0x82E, 0x40}, + {0x82F, 0x00}, + {0x830, 0xB3}, + {0x831, 0x00}, + {0x832, 0xE3}, + {0x833, 0x00}, + {0x834, 0x00}, + {0x835, 0x00}, + {0x836, 0x00}, + {0x837, 0x00}, + {0x838, 0x00}, + {0x839, 0x01}, + {0x83A, 0x61}, + {0x83B, 0x00}, + {0x83C, 0x01}, + {0x83D, 0x00}, + {0x83E, 0x00}, + {0x83F, 0x00}, + {0x840, 0x00}, + {0x841, 0x01}, + {0x842, 0x61}, + {0x843, 0x00}, + {0x844, 0x1D}, + {0x845, 0x00}, + {0x846, 0x00}, + {0x847, 0x00}, + {0x848, 0x00}, + {0x849, 0x01}, + {0x84A, 0x1F}, + {0x84B, 0x00}, + {0x84C, 0x05}, + {0x84D, 0x00}, + {0x84E, 0x19}, + {0x84F, 0x01}, + {0x850, 0x21}, + {0x851, 0x01}, + {0x852, 0x5D}, + {0x853, 0x00}, + {0x854, 0x00}, + {0x855, 0x00}, + {0x856, 0x19}, + {0x857, 0x01}, + {0x858, 0x21}, + {0x859, 0x00}, + {0x85A, 0x00}, + {0x85B, 0x00}, + {0x85C, 0x00}, + {0x85D, 0x00}, + {0x85E, 0x00}, + {0x85F, 0x00}, + {0x860, 0xB3}, + {0x861, 0x00}, + {0x862, 0xE3}, + {0x863, 0x00}, + {0x864, 0x00}, + {0x865, 0x00}, + {0x866, 0x00}, + {0x867, 0x00}, + {0x868, 0x00}, + {0x869, 0xE2}, + {0x86A, 0x00}, + {0x86B, 0x01}, + {0x86C, 0x06}, + {0x86D, 0x00}, + {0x86E, 0x00}, + {0x86F, 0x00}, + {0x870, 0x60}, + {0x871, 0x8C}, + {0x872, 0x10}, + {0x873, 0x00}, + {0x874, 0xE0}, + {0x875, 0x00}, + {0x876, 0x27}, + {0x877, 0x01}, + {0x878, 0x00}, + {0x879, 0x00}, + {0x87A, 0x00}, + {0x87B, 0x03}, + {0x87C, 0x00}, + {0x87D, 0x00}, + {0x87E, 0x00}, + {0x87F, 0x00}, + {0x880, 0x00}, + {0x881, 0x00}, + {0x882, 0x00}, + {0x883, 0x00}, + {0x884, 0x00}, + {0x885, 0x00}, + {0x886, 0xF8}, + {0x887, 0x00}, + {0x888, 0x03}, + {0x889, 0x00}, + {0x88A, 0x64}, + {0x88B, 0x00}, + {0x88C, 0x03}, + {0x88D, 0x00}, + {0x88E, 0xB1}, + {0x88F, 0x00}, + {0x890, 0x03}, + {0x891, 0x01}, + {0x892, 0x1D}, + {0x893, 0x00}, + {0x894, 0x03}, + {0x895, 0x01}, + {0x896, 0x4B}, + {0x897, 0x00}, + {0x898, 0xE5}, + {0x899, 0x00}, + {0x89A, 0x01}, + {0x89B, 0x00}, + {0x89C, 0x01}, + {0x89D, 0x04}, + {0x89E, 0xC8}, + {0x89F, 0x00}, + {0x8A0, 0x01}, + {0x8A1, 0x01}, + {0x8A2, 0x61}, + {0x8A3, 0x00}, + {0x8A4, 0x01}, + {0x8A5, 0x00}, + {0x8A6, 0x00}, + {0x8A7, 0x00}, + {0x8A8, 0x00}, + {0x8A9, 0x00}, + {0x8AA, 0x7F}, + {0x8AB, 0x03}, + {0x8AC, 0x00}, + {0x8AD, 0x00}, + {0x8AE, 0x00}, + {0x8AF, 0x00}, + {0x8B0, 0x00}, + {0x8B1, 0x00}, + {0x8B6, 0x00}, + {0x8B7, 0x01}, + {0x8B8, 0x00}, + {0x8B9, 0x00}, + {0x8BA, 0x02}, + {0x8BB, 0x00}, + {0x8BC, 0xFF}, + {0x8BD, 0x00}, + {0x8FE, 0x02}, +}; + +const static struct rj54n1_reg_val bank_10[] = { + {0x10bf, 0x69} +}; + +/* Clock dividers - these are default register values, divider = register + 1 */ +const static struct rj54n1_clock_div clk_div = { + .ratio_tg = 3 /* default: 5 */, + .ratio_t = 4 /* default: 1 */, + .ratio_r = 4 /* default: 0 */, + .ratio_op = 1 /* default: 5 */, + .ratio_o = 9 /* default: 0 */, +}; + +static struct rj54n1 *to_rj54n1(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); +} + +static int reg_read(struct i2c_client *client, const u16 reg) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + return i2c_smbus_read_byte_data(client, reg & 0xff); +} + +static int reg_write(struct i2c_client *client, const u16 reg, + const u8 data) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); + return i2c_smbus_write_byte_data(client, reg & 0xff, data); +} + +static int reg_set(struct i2c_client *client, const u16 reg, + const u8 data, const u8 mask) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static int reg_write_multiple(struct i2c_client *client, + const struct rj54n1_reg_val *rv, const int n) +{ + int i, ret; + + for (i = 0; i < n; i++) { + ret = reg_write(client, rv->reg, rv->val); + if (ret < 0) + return ret; + rv++; + } + + return 0; +} + +static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) +{ + /* TODO: start / stop streaming */ + return 0; +} + +static int rj54n1_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct i2c_client *client = sd->priv; + /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ + + if (flags & SOCAM_PCLK_SAMPLE_RISING) + return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); + else + return reg_write(client, RJ54N1_OUT_SIGPO, 0); +} + +static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + const unsigned long flags = + SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | + SOCAM_MASTER | SOCAM_DATAWIDTH_8 | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int rj54n1_set_rect(struct i2c_client *client, + u16 reg_x, u16 reg_y, u16 reg_xy, + u32 width, u32 height) +{ + int ret; + + ret = reg_write(client, reg_xy, + ((width >> 4) & 0x70) | + ((height >> 8) & 7)); + + if (!ret) + ret = reg_write(client, reg_x, width & 0xff); + if (!ret) + ret = reg_write(client, reg_y, height & 0xff); + + return ret; +} + +/* + * Some commands, specifically certain initialisation sequences, require + * a commit operation. + */ +static int rj54n1_commit(struct i2c_client *client) +{ + int ret = reg_write(client, RJ54N1_INIT_START, 1); + msleep(10); + if (!ret) + ret = reg_write(client, RJ54N1_INIT_START, 0); + return ret; +} + +static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + + a->c = rj54n1->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = RJ54N1_COLUMN_SKIP; + a->bounds.top = RJ54N1_ROW_SKIP; + a->bounds.width = RJ54N1_MAX_WIDTH; + a->bounds.height = RJ54N1_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int rj54n1_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->pixelformat = rj54n1->fourcc; + pix->field = V4L2_FIELD_NONE; + pix->width = rj54n1->width; + pix->height = rj54n1->height; + + return 0; +} + +/* + * The actual geometry configuration routine. It scales the input window into + * the output one, updates the window sizes and returns an error or the resize + * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. + */ +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, + u32 *out_w, u32 *out_h) +{ + struct i2c_client *client = sd->priv; + unsigned int skip, resize, input_w = *in_w, input_h = *in_h, + output_w = *out_w, output_h = *out_h; + u16 inc_sel; + int ret; + + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, + RJ54N1_Y_OUTPUT_SIZE_S_L, + RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); + if (!ret) + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, + RJ54N1_Y_OUTPUT_SIZE_P_L, + RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); + + if (ret < 0) + return ret; + + if (output_w > input_w || output_h > input_h) { + input_w = output_w; + input_h = output_h; + + resize = 1024; + } else { + unsigned int resize_x, resize_y; + resize_x = input_w * 1024 / output_w; + resize_y = input_h * 1024 / output_h; + + resize = min(resize_x, resize_y); + + /* Prohibited value ranges */ + switch (resize) { + case 2040 ... 2047: + resize = 2039; + break; + case 4080 ... 4095: + resize = 4079; + break; + case 8160 ... 8191: + resize = 8159; + break; + case 16320 ... 16383: + resize = 16319; + } + + input_w = output_w * resize / 1024; + input_h = output_h * resize / 1024; + } + + /* Set scaling */ + ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); + + if (ret < 0) + return ret; + + /* + * Configure a skipping bitmask. The sensor will select a skipping value + * among set bits automatically. + */ + skip = min(resize / 1024, (unsigned)15); + inc_sel = 1 << skip; + + if (inc_sel <= 2) + inc_sel = 0xc; + else if (resize & 1023 && skip < 15) + inc_sel |= 1 << (skip + 1); + + ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); + if (!ret) + ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); + + /* Start resizing */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | RESIZE_GO | 1); + + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "resize %u, skip %u\n", resize, skip); + + /* Constant taken from manufacturer's example */ + msleep(230); + + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); + if (ret < 0) + return ret; + + *in_w = input_w; + *in_h = input_h; + *out_w = output_w; + *out_h = output_h; + + return resize; +} + +static int rj54n1_set_clock(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* Enable external clock */ + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); + /* Leave stand-by */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); + + if (!ret) + ret = reg_write(client, RJ54N1_PLL_L, 2); + if (!ret) + ret = reg_write(client, RJ54N1_PLL_N, 0x31); + + /* TGCLK dividers */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_TG, + rj54n1->clk_div.ratio_tg); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_T, + rj54n1->clk_div.ratio_t); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_R, + rj54n1->clk_div.ratio_r); + + /* Enable TGCLK & RAMP */ + if (!ret) + ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); + + /* Disable clock output */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_DSP, 0); + + /* Set divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_OP, + rj54n1->clk_div.ratio_op); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_O, + rj54n1->clk_div.ratio_o); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + /* Use PLL for Timing Generator, write 2 to reserved bits */ + if (!ret) + ret = reg_write(client, RJ54N1_TG_BYPASS, 2); + + /* Take sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | SEN_RSTX); + /* Enable PLL */ + if (!ret) + ret = reg_write(client, RJ54N1_PLL_EN, 1); + + /* Wait for PLL to stabilise */ + msleep(10); + + /* Enable clock to frequency divider */ + if (!ret) + ret = reg_write(client, RJ54N1_CLK_RST, 1); + + if (!ret) + ret = reg_read(client, RJ54N1_CLK_RST); + if (ret != 1) { + dev_err(&client->dev, + "Resetting RJ54N1CB0C clock failed: %d!\n", ret); + return -EIO; + } + /* Start the PLL */ + ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + return ret; +} + +static int rj54n1_reg_init(struct i2c_client *client) +{ + int ret = rj54n1_set_clock(client); + + if (!ret) + ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); + if (!ret) + ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); + + /* Set binning divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); + + /* Switch to fixed resize mode */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | 1); + + /* Set gain */ + if (!ret) + ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); + + /* Mirror the image back: default is upside down and left-to-right... */ + if (!ret) + ret = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 3, 3); + + if (!ret) + ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); + if (!ret) + ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); + if (!ret) + ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); + + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | SEN_RSTX); + + /* Commit init */ + if (!ret) + ret = rj54n1_commit(client); + + /* Take DSP, TG, sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); + + if (!ret) + ret = reg_write(client, 0x7fe, 2); + + /* Constant taken from manufacturer's example */ + msleep(700); + + return ret; +} + +/* FIXME: streaming output only up to 800x600 is functional */ +static int rj54n1_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->field = V4L2_FIELD_NONE; + + if (pix->width > 800) + pix->width = 800; + if (pix->height > 600) + pix->height = 600; + + return 0; +} + +static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int output_w, output_h, + input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; + int ret; + + /* + * The host driver can call us without .try_fmt(), so, we have to take + * care ourseleves + */ + ret = rj54n1_try_fmt(sd, f); + + /* + * Verify if the sensor has just been powered on. TODO: replace this + * with proper PM, when a suitable API is available. + */ + if (!ret) + ret = reg_read(client, RJ54N1_RESET_STANDBY); + if (ret < 0) + return ret; + + if (!(ret & E_EXCLK)) { + ret = rj54n1_reg_init(client); + if (ret < 0) + return ret; + } + + /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + ret = reg_write(client, RJ54N1_OUT_SEL, 0); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + case V4L2_PIX_FMT_RGB565: + ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + /* Supported scales 1:1 - 1:16 */ + if (pix->width < input_w / 16) + pix->width = input_w / 16; + if (pix->height < input_h / 16) + pix->height = input_h / 16; + + output_w = pix->width; + output_h = pix->height; + + ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); + if (ret < 0) + return ret; + + rj54n1->fourcc = pix->pixelformat; + rj54n1->resize = ret; + rj54n1->rect.width = input_w; + rj54n1->rect.height = input_h; + rj54n1->width = output_w; + rj54n1->height = output_h; + + pix->width = output_w; + pix->height = output_h; + pix->field = V4L2_FIELD_NONE; + + return ret; +} + +static int rj54n1_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = sd->priv; + + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match.addr != client->addr) + return -ENODEV; + + id->ident = V4L2_IDENT_RJ54N1CB0C; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int rj54n1_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || + reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers > 0x0800 are only available from Sharp support */ + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + reg->size = 1; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xff) + return -EIO; + + return 0; +} + +static int rj54n1_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || + reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers >= 0x0800 are only available from Sharp support */ + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static const struct v4l2_queryctrl rj54n1_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 66, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, +}; + +static struct soc_camera_ops rj54n1_ops = { + .set_bus_param = rj54n1_set_bus_param, + .query_bus_param = rj54n1_query_bus_param, + .controls = rj54n1_controls, + .num_controls = ARRAY_SIZE(rj54n1_controls), +}; + +static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(client, RJ54N1_MIRROR_STILL_MODE); + if (data < 0) + return -EIO; + ctrl->value = !(data & 1); + break; + case V4L2_CID_HFLIP: + data = reg_read(client, RJ54N1_MIRROR_STILL_MODE); + if (data < 0) + return -EIO; + ctrl->value = !(data & 2); + break; + case V4L2_CID_GAIN: + data = reg_read(client, RJ54N1_Y_GAIN); + if (data < 0) + return -EIO; + + ctrl->value = data / 2; + break; + } + + return 0; +} + +static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + int data; + struct i2c_client *client = sd->priv; + const struct v4l2_queryctrl *qctrl; + + qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id); + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); + if (data < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + if (ctrl->value > qctrl->maximum || + ctrl->value < qctrl->minimum) + return -EINVAL; + else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0) + return -EIO; + break; + } + + return 0; +} + +static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { + .g_ctrl = rj54n1_g_ctrl, + .s_ctrl = rj54n1_s_ctrl, + .g_chip_ident = rj54n1_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = rj54n1_g_register, + .s_register = rj54n1_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { + .s_stream = rj54n1_s_stream, + .s_fmt = rj54n1_s_fmt, + .g_fmt = rj54n1_g_fmt, + .try_fmt = rj54n1_try_fmt, + .g_crop = rj54n1_g_crop, + .cropcap = rj54n1_cropcap, +}; + +static struct v4l2_subdev_ops rj54n1_subdev_ops = { + .core = &rj54n1_subdev_core_ops, + .video = &rj54n1_subdev_video_ops, +}; + +static int rj54n1_pin_config(struct i2c_client *client) +{ + /* + * Experimentally found out IOCTRL wired to 0. TODO: add to platform + * data: 0 or 1 << 7. + */ + return reg_write(client, RJ54N1_IOC, 0); +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int rj54n1_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + int data1, data2; + int ret; + + /* This could be a BUG_ON() or a WARN_ON(), or remove it completely */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Read out the chip version register */ + data1 = reg_read(client, RJ54N1_DEV_CODE); + data2 = reg_read(client, RJ54N1_DEV_CODE2); + + if (data1 != 0x51 || data2 != 0x10) { + ret = -ENODEV; + dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", + data1, data2); + goto ei2c; + } + + ret = rj54n1_pin_config(client); + if (ret < 0) + goto ei2c; + + dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", + data1, data2); + +ei2c: + return ret; +} + +static int rj54n1_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct rj54n1 *rj54n1; + struct soc_camera_device *icd = client->dev.platform_data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); + return -EIO; + } + + rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL); + if (!rj54n1) + return -ENOMEM; + + v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); + + icd->ops = &rj54n1_ops; + + rj54n1->clk_div = clk_div; + rj54n1->rect.left = RJ54N1_COLUMN_SKIP; + rj54n1->rect.top = RJ54N1_ROW_SKIP; + rj54n1->rect.width = RJ54N1_MAX_WIDTH; + rj54n1->rect.height = RJ54N1_MAX_HEIGHT; + rj54n1->width = RJ54N1_MAX_WIDTH; + rj54n1->height = RJ54N1_MAX_HEIGHT; + rj54n1->fourcc = V4L2_PIX_FMT_YUYV; + rj54n1->resize = 1024; + + ret = rj54n1_video_probe(icd, client); + if (ret < 0) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(rj54n1); + return ret; + } + + icd->formats = rj54n1_colour_formats; + icd->num_formats = ARRAY_SIZE(rj54n1_colour_formats); + + return ret; +} + +static int rj54n1_remove(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); + + icd->ops = NULL; + if (icl->free_bus) + icl->free_bus(icl); + i2c_set_clientdata(client, NULL); + client->driver = NULL; + kfree(rj54n1); + + return 0; +} + +static const struct i2c_device_id rj54n1_id[] = { + { "rj54n1cb0c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rj54n1_id); + +static struct i2c_driver rj54n1_i2c_driver = { + .driver = { + .name = "rj54n1cb0c", + }, + .probe = rj54n1_probe, + .remove = rj54n1_remove, + .id_table = rj54n1_id, +}; + +static int __init rj54n1_mod_init(void) +{ + return i2c_add_driver(&rj54n1_i2c_driver); +} + +static void __exit rj54n1_mod_exit(void) +{ + i2c_del_driver(&rj54n1_i2c_driver); +} + +module_init(rj54n1_mod_init); +module_exit(rj54n1_mod_exit); + +MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 2c0bb06cab3b..41765f3c7c28 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -1958,7 +1958,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if (pdword[1] >= MAX_CHANNELS) break; cc = G_chnmap[pdword[1]]; - if (!(cc >= 0 && cc < MAX_CHANNELS)) + if (cc >= MAX_CHANNELS) break; switch (pdword[2]) { case S2255_RESPONSE_SETMODE: @@ -1980,7 +1980,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) wake_up(&dev->fw_data->wait_fw); break; default: - printk(KERN_INFO "s2255 unknwn resp\n"); + printk(KERN_INFO "s2255 unknown resp\n"); } default: pdata++; diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index 5c24c993ac16..3bca744e43af 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -304,7 +304,7 @@ static int saa7110_s_routing(struct v4l2_subdev *sd, { struct saa7110 *decoder = to_saa7110(sd); - if (input < 0 || input >= SAA7110_MAX_INPUT) { + if (input >= SAA7110_MAX_INPUT) { v4l2_dbg(1, debug, sd, "input=%d not available\n", input); return -EINVAL; } diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 09013229d4aa..7e40d6d99dd0 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5239,6 +5239,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, .inputs = { { .name = name_tv, .vmux = 2, @@ -5279,6 +5280,46 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, }, }, + [SAA7134_BOARD_ASUS_EUROPA_HYBRID] = { + .name = "Asus Europa Hybrid OEM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TD1316, + .radio_type = UNSET, + .tuner_addr = 0x61, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 4, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, + [SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = { + .name = "Leadtek Winfast DTV1000S", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_comp1, + .vmux = 3, + }, { + .name = name_svideo, + .vmux = 8, + } }, + }, }; @@ -6418,6 +6459,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x2004, .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1043, + .subdevice = 0x4847, + .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x107d, + .subdevice = 0x6655, + .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -6748,6 +6801,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -7079,6 +7133,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) /* break intentionally omitted */ case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: + case SAA7134_BOARD_ASUS_EUROPA_HYBRID: { /* The Philips EUROPA based hybrid boards have the tuner diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index f87757fccc72..0ba7f5af0fc3 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -1032,7 +1032,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, saa7134_irq_video_signalchange(dev); if (TUNER_ABSENT != dev->tuner_type) - saa_call_all(dev, tuner, s_standby); + saa_call_all(dev, core, s_power, 0); /* register v4l devices */ if (saa7134_no_overlay > 0) @@ -1319,7 +1319,7 @@ static struct pci_driver saa7134_pci_driver = { #endif }; -static int saa7134_init(void) +static int __init saa7134_init(void) { INIT_LIST_HEAD(&saa7134_devlist); printk(KERN_INFO "saa7130/34: v4l2 driver version %d.%d.%d loaded\n", @@ -1333,7 +1333,7 @@ static int saa7134_init(void) return pci_register_driver(&saa7134_pci_driver); } -static void saa7134_fini(void) +static void __exit saa7134_fini(void) { pci_unregister_driver(&saa7134_pci_driver); } diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index a26e997a9ce6..73739d2a63dd 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -40,6 +40,7 @@ #include "tda1004x.h" #include "nxt200x.h" #include "tuner-xc2028.h" +#include "xc5000.h" #include "tda10086.h" #include "tda826x.h" @@ -871,6 +872,20 @@ static struct zl10353_config behold_h6_config = { .disable_i2c_gate_ctrl = 1, }; +static struct xc5000_config behold_x7_tunerconfig = { + .i2c_address = 0xc2>>1, + .if_khz = 4560, + .radio_input = XC5000_RADIO_FM1, +}; + +static struct zl10353_config behold_x7_config = { + .demod_address = 0x1e>>1, + .if2 = 45600, + .no_tuner = 1, + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, +}; + /* ================================================================== * tda10086 based DVB-S cards, helper functions */ @@ -1030,6 +1045,32 @@ static struct tda18271_config zolid_tda18271_config = { .gate = TDA18271_GATE_ANALOG, }; +static struct tda10048_config dtv1000s_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3800, + .dtv8_if_freq_khz = TDA10048_IF_4300, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, +}; + +static struct tda18271_std_map dtv1000s_tda18271_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config dtv1000s_tda18271_config = { + .std_map = &dtv1000s_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + /* ================================================================== * Core code */ @@ -1116,6 +1157,7 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_PHILIPS_EUROPA: case SAA7134_BOARD_VIDEOMATE_DVBT_300: + case SAA7134_BOARD_ASUS_EUROPA_HYBRID: fe0->dvb.frontend = dvb_attach(tda10046_attach, &philips_europa_config, &dev->i2c_adap); @@ -1482,6 +1524,15 @@ static int dvb_init(struct saa7134_dev *dev) TUNER_PHILIPS_FMD1216MEX_MK3); } break; + case SAA7134_BOARD_BEHOLD_X7: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &behold_x7_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(xc5000_attach, fe0->dvb.frontend, + &dev->i2c_adap, &behold_x7_tunerconfig); + } + break; case SAA7134_BOARD_AVERMEDIA_A700_PRO: case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: /* Zarlink ZL10313 */ @@ -1518,6 +1569,19 @@ static int dvb_init(struct saa7134_dev *dev) &zolid_tda18271_config); } break; + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &dtv1000s_tda10048_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &dtv1000s_tda18271_config); + } + break; default: wprintk("Huh? unknown DVB card?\n"); break; @@ -1550,7 +1614,7 @@ static int dvb_init(struct saa7134_dev *dev) /* register everything else */ ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, - &dev->pci->dev, adapter_nr, 0); + &dev->pci->dev, adapter_nr, 0, NULL); /* this sequence is necessary to make the tda1004x load its firmware * and to enter analog mode of hybrid boards diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index a0e8c62e6ae1..744918b1cd47 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -102,14 +102,14 @@ static int build_key(struct saa7134_dev *dev) if (data == ir->mask_keycode) ir_input_nokey(ir->dev, &ir->ir); else - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); return 0; } if (ir->polling) { if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); } else { ir_input_nokey(ir->dev, &ir->ir); } @@ -117,7 +117,7 @@ static int build_key(struct saa7134_dev *dev) else { /* IRQ driven mode - handle key press and release in one go */ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); ir_input_nokey(ir->dev, &ir->ir); } } @@ -616,6 +616,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x003f00; mask_keydown = 0x040000; break; + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + ir_codes = &ir_codes_winfast_table; + mask_keycode = 0x5f00; + mask_keyup = 0x020000; + polling = 50; /* ms */ + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", @@ -646,7 +652,10 @@ int saa7134_input_init1(struct saa7134_dev *dev) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -677,6 +686,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa7134_ir_stop(dev); dev->remote = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -688,6 +698,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) return; saa7134_ir_stop(dev); + ir_input_free(dev->remote->dev); input_unregister_device(dev->remote->dev); kfree(dev->remote); dev->remote = NULL; @@ -695,10 +706,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) void saa7134_probe_i2c_ir(struct saa7134_dev *dev) { - const unsigned short addr_list[] = { - 0x7a, 0x47, 0x71, 0x2d, - I2C_CLIENT_END - }; + struct i2c_board_info info; struct i2c_msg msg_msi = { .addr = 0x50, @@ -714,9 +722,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) return; } - memset(&dev->info, 0, sizeof(dev->info)); + memset(&info, 0, sizeof(struct i2c_board_info)); memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); switch (dev->board) { case SAA7134_BOARD_PINNACLE_PCTV_110i: @@ -725,23 +733,24 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) if (pinnacle_remote == 0) { dev->init_data.get_key = get_key_pinnacle_color; dev->init_data.ir_codes = &ir_codes_pinnacle_color_table; - dev->info.addr = 0x47; + info.addr = 0x47; } else { dev->init_data.get_key = get_key_pinnacle_grey; dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; - dev->info.addr = 0x47; + info.addr = 0x47; } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: dev->init_data.name = "Purple TV"; dev->init_data.get_key = get_key_purpletv; dev->init_data.ir_codes = &ir_codes_purpletv_table; + info.addr = 0x7a; break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: dev->init_data.name = "MSI TV@nywhere Plus"; dev->init_data.get_key = get_key_msi_tvanywhere_plus; dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table; - dev->info.addr = 0x30; + info.addr = 0x30; /* MSI TV@nywhere Plus controller doesn't seem to respond to probes unless we read something from an existing device. Weird... @@ -755,6 +764,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "HVR 1110"; dev->init_data.get_key = get_key_hvr1110; dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; + info.addr = 0x71; break; case SAA7134_BOARD_BEHOLD_607FM_MK3: case SAA7134_BOARD_BEHOLD_607FM_MK5: @@ -772,23 +782,20 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "BeholdTV"; dev->init_data.get_key = get_key_beholdm6xx; dev->init_data.ir_codes = &ir_codes_behold_table; + info.addr = 0x2d; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: - dev->info.addr = 0x40; + info.addr = 0x40; break; - } - - if (dev->init_data.name) - dev->info.platform_data = &dev->init_data; - /* No need to probe if address is known */ - if (dev->info.addr) { - i2c_new_device(&dev->i2c_adap, &dev->info); + default: + dprintk("No I2C IR support for board %x\n", dev->board); return; } - /* Address not known, fallback to probing */ - i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_device(&dev->i2c_adap, &info); } static int saa7134_rc5_irq(struct saa7134_dev *dev) @@ -936,7 +943,7 @@ static void nec_task(unsigned long data) dprintk("scancode = 0x%02x (code = 0x%02x, notcode= 0x%02x)\n", ir->code, ircode, not_code); - ir_input_keydown(ir->dev, &ir->ir, ir->code, ir->code); + ir_input_keydown(ir->dev, &ir->ir, ir->code); } else dprintk("Repeat last key\n"); diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index da26f476a302..35f8daa3a359 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1499,7 +1499,7 @@ static int video_release(struct file *file) saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); - saa_call_all(dev, tuner, s_standby); + saa_call_all(dev, core, s_power, 0); if (fh->radio) saa_call_all(dev, core, ioctl, RDS_CMD_CLOSE, &cmd); diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index f8697d46ff5f..53b7e0b8a2fb 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -297,6 +297,8 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_X7 171 #define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172 #define SAA7134_BOARD_ZOLID_HYBRID_PCI 173 +#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174 +#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -592,7 +594,6 @@ struct saa7134_dev { unsigned int insuspend; /* I2C keyboard data */ - struct i2c_board_info info; struct IR_i2c_init_data init_data; /* SAA7134_MPEG_* */ diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c index 709affc31042..e6aa0fbd1e91 100644 --- a/drivers/media/video/saa7164/saa7164-core.c +++ b/drivers/media/video/saa7164/saa7164-core.c @@ -724,13 +724,13 @@ static struct pci_driver saa7164_pci_driver = { .resume = NULL, }; -static int saa7164_init(void) +static int __init saa7164_init(void) { printk(KERN_INFO "saa7164 driver loaded\n"); return pci_register_driver(&saa7164_pci_driver); } -static void saa7164_fini(void) +static void __exit saa7164_fini(void) { pci_unregister_driver(&saa7164_pci_driver); } diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c index 6a2d847d6a88..cf099c59b38e 100644 --- a/drivers/media/video/saa7164/saa7164-dvb.c +++ b/drivers/media/video/saa7164/saa7164-dvb.c @@ -68,6 +68,7 @@ static struct tda18271_config hauppauge_hvr22x0s_tuner_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, .role = TDA18271_SLAVE, + .output_opt = TDA18271_OUTPUT_LT_OFF, .rf_cal_on_startup = 1 }; diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index b15c40908e84..6818df571168 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -1115,7 +1115,7 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); /* inputs from 0-9 are available*/ /* saa717x have mode0-mode9 but mode5 is reserved. */ - if (input < 0 || input > 9 || input == 5) + if (input > 9 || input == 5) return -EINVAL; if (decoder->input != input) { @@ -1312,7 +1312,7 @@ static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) "MONO", "STEREO", "LANG1", "LANG2/SAP" }; - audio_mode = V4L2_TUNER_MODE_STEREO; + audio_mode = TUNER_AUDIO_STEREO; switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 9c8b7c7b89ee..a4f3472d4db8 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -153,6 +153,40 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs) return ioread32(priv->base + reg_offs); } +static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) +{ + int i, success = 0; + struct soc_camera_device *icd = pcdev->icd; + + ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ + + /* wait CSTSR.CPTON bit */ + for (i = 0; i < 1000; i++) { + if (!(ceu_read(pcdev, CSTSR) & 1)) { + success++; + break; + } + udelay(1); + } + + /* wait CAPSR.CPKIL bit */ + for (i = 0; i < 1000; i++) { + if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) { + success++; + break; + } + udelay(1); + } + + + if (2 != success) { + dev_warn(&icd->dev, "soft reset time out\n"); + return -EIO; + } + + return 0; +} + /* * Videobuf operations */ @@ -202,26 +236,45 @@ static void free_buffer(struct videobuf_queue *vq, #define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ #define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ #define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ +#define CEU_CEIER_VBP (1 << 20) /* vbp error */ #define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ +#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP) -static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) +/* + * return value doesn't reflex the success/failure to queue the new buffer, + * but rather the status of the previous buffer. + */ +static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) { struct soc_camera_device *icd = pcdev->icd; dma_addr_t phys_addr_top, phys_addr_bottom; + u32 status; + int ret = 0; /* The hardware is _very_ picky about this sequence. Especially * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge * several not-so-well documented interrupt sources in CETCR. */ - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE); - ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC); - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE); + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); + status = ceu_read(pcdev, CETCR); + ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); + /* + * When a VBP interrupt occurs, a capture end interrupt does not occur + * and the image of that frame is not captured correctly. So, soft reset + * is needed here. + */ + if (status & CEU_CEIER_VBP) { + sh_mobile_ceu_soft_reset(pcdev); + ret = -EIO; + } + if (!pcdev->active) - return; + return ret; phys_addr_top = videobuf_to_dma_contig(pcdev->active); ceu_write(pcdev, CDAYR, phys_addr_top); @@ -247,6 +300,8 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) pcdev->active->state = VIDEOBUF_ACTIVE; ceu_write(pcdev, CAPSR, 0x1); /* start capture */ + + return ret; } static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, @@ -319,6 +374,11 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, list_add_tail(&vb->queue, &pcdev->capture); if (!pcdev->active) { + /* + * Because there were no active buffer at this moment, + * we are not interested in the return value of + * sh_mobile_ceu_capture here. + */ pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } @@ -379,9 +439,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) else pcdev->active = NULL; - sh_mobile_ceu_capture(pcdev); - - vb->state = VIDEOBUF_DONE; + vb->state = (sh_mobile_ceu_capture(pcdev) < 0) ? + VIDEOBUF_ERROR : VIDEOBUF_DONE; do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); @@ -407,13 +466,9 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) pm_runtime_get_sync(ici->v4l2_dev.dev); - ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ - while (ceu_read(pcdev, CSTSR) & 1) - msleep(1); - pcdev->icd = icd; - return 0; + return sh_mobile_ceu_soft_reset(pcdev); } /* Called with .video_lock held */ @@ -427,7 +482,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) /* disable capture, disable interrupts */ ceu_write(pcdev, CEIER, 0); - ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ + sh_mobile_ceu_soft_reset(pcdev); /* make sure active buffer is canceled */ spin_lock_irqsave(&pcdev->lock, flags); diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index aba92e2313d8..5b3eaa16afd2 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -320,6 +320,7 @@ static void set_type(struct i2c_client *c, unsigned int type, struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; unsigned char buffer[4]; + int tune_now = 1; if (type == UNSET || type == TUNER_ABSENT) { tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr); @@ -328,7 +329,7 @@ static void set_type(struct i2c_client *c, unsigned int type, t->type = type; /* prevent invalid config values */ - t->config = ((new_config >= 0) && (new_config < 256)) ? new_config : 0; + t->config = new_config < 256 ? new_config : 0; if (tuner_callback != NULL) { tuner_dbg("defining GPIO callback\n"); t->fe.callback = tuner_callback; @@ -404,6 +405,7 @@ static void set_type(struct i2c_client *c, unsigned int type, }; if (!dvb_attach(xc2028_attach, &t->fe, &cfg)) goto attach_failed; + tune_now = 0; break; } case TUNER_TDA9887: @@ -419,6 +421,7 @@ static void set_type(struct i2c_client *c, unsigned int type, if (!dvb_attach(xc5000_attach, &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; + tune_now = 0; break; } case TUNER_NXP_TDA18271: @@ -430,6 +433,7 @@ static void set_type(struct i2c_client *c, unsigned int type, if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr, t->i2c->adapter, &cfg)) goto attach_failed; + tune_now = 0; break; } default: @@ -458,12 +462,13 @@ static void set_type(struct i2c_client *c, unsigned int type, if (t->mode_mask == T_UNINITIALIZED) t->mode_mask = new_mode_mask; - /* xc2028/3028 and xc5000 requires a firmware to be set-up later + /* Some tuners require more initialization setup before use, + such as firmware download or device calibration. trying to set a frequency here will just fail FIXME: better to move set_freq to the tuner code. This is needed on analog tuners for PLL to properly work */ - if (t->type != TUNER_XC2028 && t->type != TUNER_XC5000) + if (tune_now) set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq); @@ -752,14 +757,17 @@ static int tuner_s_radio(struct v4l2_subdev *sd) return 0; } -static int tuner_s_standby(struct v4l2_subdev *sd) +static int tuner_s_power(struct v4l2_subdev *sd, int on) { struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + if (on) + return 0; + tuner_dbg("Putting tuner to sleep\n"); - if (check_mode(t, "s_standby") == -EINVAL) + if (check_mode(t, "s_power") == -EINVAL) return 0; t->mode = T_STANDBY; if (analog_ops->standby) @@ -961,6 +969,7 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, .s_std = tuner_s_std, + .s_power = tuner_s_power, }; static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { @@ -971,7 +980,6 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { .g_frequency = tuner_g_frequency, .s_type_addr = tuner_s_type_addr, .s_config = tuner_s_config, - .s_standby = tuner_s_standby, }; static const struct v4l2_subdev_ops tuner_ops = { diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 0869bafc2b56..800fc1b111ef 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1919,7 +1919,7 @@ static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { .s_radio = tvaudio_s_radio, .s_frequency = tvaudio_s_frequency, .s_tuner = tvaudio_s_tuner, - .s_tuner = tvaudio_g_tuner, + .g_tuner = tvaudio_g_tuner, }; static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 244372627df2..26b4e718cd6d 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -272,7 +272,7 @@ static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) read_again: err = i2c_smbus_read_byte_data(client, reg); - if (err == -1) { + if (err < 0) { if (retry <= I2C_RETRY_COUNT) { v4l2_warn(sd, "Read: retry ... %d\n", retry); retry++; diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index 31d57f2d09e1..a0addcb04295 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -225,7 +225,7 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); - strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname)); cam->input = input_dev = input_allocate_device(); if (!input_dev) { diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index 803d3e4e29a2..c4d1b96b5cee 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -89,7 +89,7 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev) int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); - strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname)); cam->input = input_dev = input_allocate_device(); if (!input_dev) { diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index a2a50d608a3f..c07b0ac452ab 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -601,7 +601,7 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int input) { struct usb_usbvision *usbvision = video_drvdata(file); - if ((input >= usbvision->video_inputs) || (input < 0) ) + if (input >= usbvision->video_inputs) return -EINVAL; mutex_lock(&usbvision->lock); diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 1b89735e62fd..0469d7a876a8 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -742,17 +742,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, v4l2_id &= V4L2_CTRL_ID_MASK; /* Find the control. */ - __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - - list_for_each_entry(entity, &chain->iterms, chain) { - __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - } - - list_for_each_entry(entity, &chain->extensions, chain) { + list_for_each_entry(entity, &chain->entities, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; @@ -826,6 +816,13 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, ret = 0; goto out; + case V4L2_CTRL_TYPE_BUTTON: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = 0; + v4l2_ctrl->step = 0; + ret = 0; + goto out; + default: break; } @@ -944,17 +941,7 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) int ret = 0; /* Find the control. */ - ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback); - if (ret < 0) - goto done; - - list_for_each_entry(entity, &chain->iterms, chain) { - ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); - if (ret < 0) - goto done; - } - - list_for_each_entry(entity, &chain->extensions, chain) { + list_for_each_entry(entity, &chain->entities, chain) { ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; @@ -1068,8 +1055,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, int ret; /* Find the extension unit. */ - list_for_each_entry(entity, &chain->extensions, chain) { - if (entity->id == xctrl->unit) + list_for_each_entry(entity, &chain->entities, chain) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && + entity->id == xctrl->unit) break; } diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 8756be569154..c31bc50113bc 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -46,6 +46,7 @@ unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param; unsigned int uvc_trace_param; +unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; /* ------------------------------------------------------------------------ * Video formats @@ -248,29 +249,9 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, entity = list_entry(&dev->entities, struct uvc_entity, list); list_for_each_entry_continue(entity, &dev->entities, list) { - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_TT_STREAMING: - if (entity->output.bSourceID == id) - return entity; - break; - - case UVC_VC_PROCESSING_UNIT: - if (entity->processing.bSourceID == id) + for (i = 0; i < entity->bNrInPins; ++i) + if (entity->baSourceID[i] == id) return entity; - break; - - case UVC_VC_SELECTOR_UNIT: - for (i = 0; i < entity->selector.bNrInPins; ++i) - if (entity->selector.baSourceID[i] == id) - return entity; - break; - - case UVC_VC_EXTENSION_UNIT: - for (i = 0; i < entity->extension.bNrInPins; ++i) - if (entity->extension.baSourceID[i] == id) - return entity; - break; - } } return NULL; @@ -426,7 +407,8 @@ static int uvc_parse_format(struct uvc_device *dev, /* Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ - while (buflen > 2 && buffer[2] == ftype) { + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == ftype) { frame = &format->frame[format->nframes]; if (ftype != UVC_VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; @@ -503,12 +485,14 @@ static int uvc_parse_format(struct uvc_device *dev, buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { buflen -= buffer[0]; buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) { + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_COLORFORMAT) { if (buflen < 6) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d COLORFORMAT error\n", @@ -749,6 +733,11 @@ static int uvc_parse_streaming(struct uvc_device *dev, buffer += buffer[0]; } + if (buflen) + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has %u bytes of trailing descriptor garbage.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); + /* Parse the alternate settings to find the maximum bandwidth. */ for (i = 0; i < intf->num_altsetting; ++i) { struct usb_host_endpoint *ep; @@ -776,6 +765,28 @@ error: return ret; } +static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, + unsigned int num_pads, unsigned int extra_size) +{ + struct uvc_entity *entity; + unsigned int num_inputs; + unsigned int size; + + num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; + size = sizeof(*entity) + extra_size + num_inputs; + entity = kzalloc(size, GFP_KERNEL); + if (entity == NULL) + return NULL; + + entity->id = id; + entity->type = type; + + entity->bNrInPins = num_inputs; + entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size; + + return entity; +} + /* Parse vendor-specific extensions. */ static int uvc_parse_vendor_control(struct uvc_device *dev, const unsigned char *buffer, int buflen) @@ -827,21 +838,18 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, break; } - unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL); + unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], + p + 1, 2*n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = UVC_VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); - unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.baSourceID, &buffer[22], p); + memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; - unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit - + p + n; + unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); + unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) + + n; memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); if (buffer[24+p+2*n] != 0) @@ -938,13 +946,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - term = kzalloc(sizeof *term + n + p, GFP_KERNEL); + term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], + 1, n + p); if (term == NULL) return -ENOMEM; - term->id = buffer[3]; - term->type = type | UVC_TERM_INPUT; - if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; @@ -999,13 +1005,12 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return 0; } - term = kzalloc(sizeof *term, GFP_KERNEL); + term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], + 1, 0); if (term == NULL) return -ENOMEM; - term->id = buffer[3]; - term->type = type | UVC_TERM_OUTPUT; - term->output.bSourceID = buffer[7]; + memcpy(term->baSourceID, &buffer[7], 1); if (buffer[8] != 0) usb_string(udev, buffer[8], term->name, @@ -1026,15 +1031,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + p, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; - unit->selector.bNrInPins = buffer[4]; - unit->selector.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->selector.baSourceID, &buffer[5], p); + memcpy(unit->baSourceID, &buffer[5], p); if (buffer[5+p] != 0) usb_string(udev, buffer[5+p], unit->name, @@ -1056,13 +1057,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + n, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; - unit->processing.bSourceID = buffer[4]; + memcpy(unit->baSourceID, &buffer[4], 1); unit->processing.wMaxMultiplier = get_unaligned_le16(&buffer[5]); unit->processing.bControlSize = buffer[7]; @@ -1091,19 +1090,15 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); - unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.baSourceID, &buffer[22], p); + memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.bmControls, &buffer[23+p], n); if (buffer[23+p+n] != 0) @@ -1209,13 +1204,12 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- XU %d", entity->id); - if (entity->extension.bNrInPins != 1) { + if (entity->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " "than 1 input pin.\n", entity->id); return -1; } - list_add_tail(&entity->chain, &chain->extensions); break; case UVC_VC_PROCESSING_UNIT: @@ -1236,7 +1230,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, printk(" <- SU %d", entity->id); /* Single-input selector units are ignored. */ - if (entity->selector.bNrInPins == 1) + if (entity->bNrInPins == 1) break; if (chain->selector != NULL) { @@ -1254,20 +1248,17 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); - list_add_tail(&entity->chain, &chain->iterms); break; case UVC_TT_STREAMING: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- IT %d\n", entity->id); - - if (!UVC_ENTITY_IS_ITERM(entity)) { - uvc_trace(UVC_TRACE_DESCR, "Unsupported input " - "terminal %u.\n", entity->id); - return -1; + if (UVC_ENTITY_IS_ITERM(entity)) { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + } else { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); } - list_add_tail(&entity->chain, &chain->iterms); break; default: @@ -1276,6 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, return -1; } + list_add_tail(&entity->chain, &chain->entities); return 0; } @@ -1299,14 +1291,14 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, switch (UVC_ENTITY_TYPE(forward)) { case UVC_VC_EXTENSION_UNIT: - if (forward->extension.bNrInPins != 1) { + if (forward->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " "has more than 1 input pin.\n", entity->id); return -EINVAL; } - list_add_tail(&forward->chain, &chain->extensions); + list_add_tail(&forward->chain, &chain->entities); if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); @@ -1326,7 +1318,7 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, return -EINVAL; } - list_add_tail(&forward->chain, &chain->oterms); + list_add_tail(&forward->chain, &chain->entities); if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); @@ -1344,24 +1336,22 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, } static int uvc_scan_chain_backward(struct uvc_video_chain *chain, - struct uvc_entity *entity) + struct uvc_entity **_entity) { + struct uvc_entity *entity = *_entity; struct uvc_entity *term; - int id = -1, i; + int id = -EINVAL, i; switch (UVC_ENTITY_TYPE(entity)) { case UVC_VC_EXTENSION_UNIT: - id = entity->extension.baSourceID[0]; - break; - case UVC_VC_PROCESSING_UNIT: - id = entity->processing.bSourceID; + id = entity->baSourceID[0]; break; case UVC_VC_SELECTOR_UNIT: /* Single-input selector units are ignored. */ - if (entity->selector.bNrInPins == 1) { - id = entity->selector.baSourceID[0]; + if (entity->bNrInPins == 1) { + id = entity->baSourceID[0]; break; } @@ -1369,8 +1359,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, printk(" <- IT"); chain->selector = entity; - for (i = 0; i < entity->selector.bNrInPins; ++i) { - id = entity->selector.baSourceID[i]; + for (i = 0; i < entity->bNrInPins; ++i) { + id = entity->baSourceID[i]; term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " @@ -1382,7 +1372,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" %d", term->id); - list_add_tail(&term->chain, &chain->iterms); + list_add_tail(&term->chain, &chain->entities); uvc_scan_chain_forward(chain, term, entity); } @@ -1391,34 +1381,49 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, id = 0; break; + + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_CAMERA: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; + break; + } + + if (id <= 0) { + *_entity = NULL; + return id; } - return id; + entity = uvc_entity_by_id(chain->dev, id); + if (entity == NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "unknown entity %d.\n", id); + return -EINVAL; + } + + *_entity = entity; + return 0; } static int uvc_scan_chain(struct uvc_video_chain *chain, - struct uvc_entity *oterm) + struct uvc_entity *term) { struct uvc_entity *entity, *prev; - int id; - entity = oterm; - list_add_tail(&entity->chain, &chain->oterms); - uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); + uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); - id = entity->output.bSourceID; - while (id != 0) { - prev = entity; - entity = uvc_entity_by_id(chain->dev, id); - if (entity == NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "unknown entity %d.\n", id); - return -EINVAL; - } + entity = term; + prev = NULL; + while (entity != NULL) { + /* Entity must not be part of an existing chain */ if (entity->chain.next || entity->chain.prev) { uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "entity %d already in chain.\n", id); + "entity %d already in chain.\n", entity->id); return -EINVAL; } @@ -1430,34 +1435,34 @@ static int uvc_scan_chain(struct uvc_video_chain *chain, if (uvc_scan_chain_forward(chain, entity, prev) < 0) return -EINVAL; - /* Stop when a terminal is found. */ - if (UVC_ENTITY_IS_TERM(entity)) - break; - /* Backward scan */ - id = uvc_scan_chain_backward(chain, entity); - if (id < 0) - return id; + prev = entity; + if (uvc_scan_chain_backward(chain, &entity) < 0) + return -EINVAL; } return 0; } -static unsigned int uvc_print_terms(struct list_head *terms, char *buffer) +static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, + char *buffer) { struct uvc_entity *term; unsigned int nterms = 0; char *p = buffer; list_for_each_entry(term, terms, chain) { - p += sprintf(p, "%u", term->id); - if (term->chain.next != terms) { + if (!UVC_ENTITY_IS_TERM(term) || + UVC_TERM_DIRECTION(term) != dir) + continue; + + if (nterms) p += sprintf(p, ","); - if (++nterms >= 4) { - p += sprintf(p, "..."); - break; - } + if (++nterms >= 4) { + p += sprintf(p, "..."); + break; } + p += sprintf(p, "%u", term->id); } return p - buffer; @@ -1468,9 +1473,9 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain) static char buffer[43]; char *p = buffer; - p += uvc_print_terms(&chain->iterms, p); + p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); p += sprintf(p, " -> "); - uvc_print_terms(&chain->oterms, p); + uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); return buffer; } @@ -1501,9 +1506,7 @@ static int uvc_scan_device(struct uvc_device *dev) if (chain == NULL) return -ENOMEM; - INIT_LIST_HEAD(&chain->iterms); - INIT_LIST_HEAD(&chain->oterms); - INIT_LIST_HEAD(&chain->extensions); + INIT_LIST_HEAD(&chain->entities); mutex_init(&chain->ctrl_mutex); chain->dev = dev; @@ -1531,22 +1534,92 @@ static int uvc_scan_device(struct uvc_device *dev) */ /* + * Delete the UVC device. + * + * Called by the kernel when the last reference to the uvc_device structure + * is released. + * + * As this function is called after or during disconnect(), all URBs have + * already been canceled by the USB core. There is no need to kill the + * interrupt URB manually. + */ +static void uvc_delete(struct uvc_device *dev) +{ + struct list_head *p, *n; + + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + + uvc_status_cleanup(dev); + uvc_ctrl_cleanup_device(dev); + + list_for_each_safe(p, n, &dev->chains) { + struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); + kfree(chain); + } + + list_for_each_safe(p, n, &dev->entities) { + struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); + kfree(entity); + } + + list_for_each_safe(p, n, &dev->streams) { + struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); + usb_driver_release_interface(&uvc_driver.driver, + streaming->intf); + usb_put_intf(streaming->intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + } + + kfree(dev); +} + +static void uvc_release(struct video_device *vdev) +{ + struct uvc_streaming *stream = video_get_drvdata(vdev); + struct uvc_device *dev = stream->dev; + + video_device_release(vdev); + + /* Decrement the registered streams count and delete the device when it + * reaches zero. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); +} + +/* * Unregister the video devices. */ static void uvc_unregister_video(struct uvc_device *dev) { struct uvc_streaming *stream; + /* Unregistering all video devices might result in uvc_delete() being + * called from inside the loop if there's no open file handle. To avoid + * that, increment the stream count before iterating over the streams + * and decrement it when done. + */ + atomic_inc(&dev->nstreams); + list_for_each_entry(stream, &dev->streams, list) { if (stream->vdev == NULL) continue; - if (stream->vdev->minor == -1) - video_device_release(stream->vdev); - else - video_unregister_device(stream->vdev); + video_unregister_device(stream->vdev); stream->vdev = NULL; } + + /* Decrement the stream count and call uvc_delete explicitly if there + * are no stream left. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); } static int uvc_register_video(struct uvc_device *dev, @@ -1580,7 +1653,7 @@ static int uvc_register_video(struct uvc_device *dev, vdev->parent = &dev->intf->dev; vdev->minor = -1; vdev->fops = &uvc_fops; - vdev->release = video_device_release; + vdev->release = uvc_release; strlcpy(vdev->name, dev->name, sizeof vdev->name); /* Set the driver data before calling video_register_device, otherwise @@ -1598,6 +1671,7 @@ static int uvc_register_video(struct uvc_device *dev, return ret; } + atomic_inc(&dev->nstreams); return 0; } @@ -1605,13 +1679,13 @@ static int uvc_register_video(struct uvc_device *dev, * Register all video devices in all chains. */ static int uvc_register_terms(struct uvc_device *dev, - struct uvc_video_chain *chain, struct list_head *terms) + struct uvc_video_chain *chain) { struct uvc_streaming *stream; struct uvc_entity *term; int ret; - list_for_each_entry(term, terms, chain) { + list_for_each_entry(term, &chain->entities, chain) { if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) continue; @@ -1637,11 +1711,7 @@ static int uvc_register_chains(struct uvc_device *dev) int ret; list_for_each_entry(chain, &dev->chains, list) { - ret = uvc_register_terms(dev, chain, &chain->iterms); - if (ret < 0) - return ret; - - ret = uvc_register_terms(dev, chain, &chain->oterms); + ret = uvc_register_terms(dev, chain); if (ret < 0) return ret; } @@ -1653,61 +1723,6 @@ static int uvc_register_chains(struct uvc_device *dev) * USB probe, disconnect, suspend and resume */ -/* - * Delete the UVC device. - * - * Called by the kernel when the last reference to the uvc_device structure - * is released. - * - * Unregistering the video devices is done here because every opened instance - * must be closed before the device can be unregistered. An alternative would - * have been to use another reference count for uvc_v4l2_open/uvc_release, and - * unregister the video devices on disconnect when that reference count drops - * to zero. - * - * As this function is called after or during disconnect(), all URBs have - * already been canceled by the USB core. There is no need to kill the - * interrupt URB manually. - */ -void uvc_delete(struct kref *kref) -{ - struct uvc_device *dev = container_of(kref, struct uvc_device, kref); - struct list_head *p, *n; - - /* Unregister the video devices. */ - uvc_unregister_video(dev); - usb_put_intf(dev->intf); - usb_put_dev(dev->udev); - - uvc_status_cleanup(dev); - uvc_ctrl_cleanup_device(dev); - - list_for_each_safe(p, n, &dev->chains) { - struct uvc_video_chain *chain; - chain = list_entry(p, struct uvc_video_chain, list); - kfree(chain); - } - - list_for_each_safe(p, n, &dev->entities) { - struct uvc_entity *entity; - entity = list_entry(p, struct uvc_entity, list); - kfree(entity); - } - - list_for_each_safe(p, n, &dev->streams) { - struct uvc_streaming *streaming; - streaming = list_entry(p, struct uvc_streaming, list); - usb_driver_release_interface(&uvc_driver.driver, - streaming->intf); - usb_put_intf(streaming->intf); - kfree(streaming->format); - kfree(streaming->header.bmaControls); - kfree(streaming); - } - - kfree(dev); -} - static int uvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1730,7 +1745,7 @@ static int uvc_probe(struct usb_interface *intf, INIT_LIST_HEAD(&dev->entities); INIT_LIST_HEAD(&dev->chains); INIT_LIST_HEAD(&dev->streams); - kref_init(&dev->kref); + atomic_set(&dev->nstreams, 0); atomic_set(&dev->users, 0); dev->udev = usb_get_dev(udev); @@ -1792,7 +1807,7 @@ static int uvc_probe(struct usb_interface *intf, return 0; error: - kref_put(&dev->kref, uvc_delete); + uvc_unregister_video(dev); return -ENODEV; } @@ -1809,21 +1824,9 @@ static void uvc_disconnect(struct usb_interface *intf) UVC_SC_VIDEOSTREAMING) return; - /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide - * lock is needed to prevent uvc_disconnect from releasing its - * reference to the uvc_device instance after uvc_v4l2_open() received - * the pointer to the device (video_devdata) but before it got the - * chance to increase the reference count (kref_get). - * - * Note that the reference can't be released with the lock held, - * otherwise a AB-BA deadlock can occur with videodev_lock that - * videodev acquires in videodev_open() and video_unregister_device(). - */ - mutex_lock(&uvc_driver.open_mutex); dev->state |= UVC_DEV_DISCONNECTED; - mutex_unlock(&uvc_driver.open_mutex); - kref_put(&dev->kref, uvc_delete); + uvc_unregister_video(dev); } static int uvc_suspend(struct usb_interface *intf, pm_message_t message) @@ -1899,6 +1902,15 @@ static int uvc_reset_resume(struct usb_interface *intf) * though they are compliant. */ static struct usb_device_id uvc_ids[] = { + /* Genius eFace 2025 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0458, + .idProduct = 0x706e, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Microsoft Lifecam NX-6000 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2123,6 +2135,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STATUS_INTERVAL }, + /* MSI StarCam 370i */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1b3b, + .idProduct = 0x2951, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* SiGma Micro USB Web Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2159,7 +2180,6 @@ static int __init uvc_init(void) INIT_LIST_HEAD(&uvc_driver.devices); INIT_LIST_HEAD(&uvc_driver.controls); - mutex_init(&uvc_driver.open_mutex); mutex_init(&uvc_driver.ctrl_mutex); uvc_ctrl_init(); @@ -2184,6 +2204,8 @@ module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(trace, "Trace level bitmask"); +module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index a2bdd806efab..23239a4adefe 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -364,37 +364,30 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, * unprivileged state. Only a single instance can be in a privileged state at * a given time. Trying to perform an operation that requires privileges will * automatically acquire the required privileges if possible, or return -EBUSY - * otherwise. Privileges are dismissed when closing the instance. + * otherwise. Privileges are dismissed when closing the instance or when + * freeing the video buffers using VIDIOC_REQBUFS. * * Operations that require privileges are: * * - VIDIOC_S_INPUT * - VIDIOC_S_PARM * - VIDIOC_S_FMT - * - VIDIOC_TRY_FMT * - VIDIOC_REQBUFS */ static int uvc_acquire_privileges(struct uvc_fh *handle) { - int ret = 0; - /* Always succeed if the handle is already privileged. */ if (handle->state == UVC_HANDLE_ACTIVE) return 0; /* Check if the device already has a privileged handle. */ - mutex_lock(&uvc_driver.open_mutex); if (atomic_inc_return(&handle->stream->active) != 1) { atomic_dec(&handle->stream->active); - ret = -EBUSY; - goto done; + return -EBUSY; } handle->state = UVC_HANDLE_ACTIVE; - -done: - mutex_unlock(&uvc_driver.open_mutex); - return ret; + return 0; } static void uvc_dismiss_privileges(struct uvc_fh *handle) @@ -421,24 +414,20 @@ static int uvc_v4l2_open(struct file *file) int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); - mutex_lock(&uvc_driver.open_mutex); stream = video_drvdata(file); - if (stream->dev->state & UVC_DEV_DISCONNECTED) { - ret = -ENODEV; - goto done; - } + if (stream->dev->state & UVC_DEV_DISCONNECTED) + return -ENODEV; ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) - goto done; + return ret; /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { usb_autopm_put_interface(stream->dev->intf); - ret = -ENOMEM; - goto done; + return -ENOMEM; } if (atomic_inc_return(&stream->dev->users) == 1) { @@ -447,7 +436,7 @@ static int uvc_v4l2_open(struct file *file) usb_autopm_put_interface(stream->dev->intf); atomic_dec(&stream->dev->users); kfree(handle); - goto done; + return ret; } } @@ -456,11 +445,7 @@ static int uvc_v4l2_open(struct file *file) handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; - kref_get(&stream->dev->kref); - -done: - mutex_unlock(&uvc_driver.open_mutex); - return ret; + return 0; } static int uvc_v4l2_release(struct file *file) @@ -490,7 +475,6 @@ static int uvc_v4l2_release(struct file *file) uvc_status_stop(stream->dev); usb_autopm_put_interface(stream->dev->intf); - kref_put(&stream->dev->kref, uvc_delete); return 0; } @@ -636,12 +620,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (index != 0) return -EINVAL; - iterm = list_first_entry(&chain->iterms, - struct uvc_entity, chain); + list_for_each_entry(iterm, &chain->entities, chain) { + if (UVC_ENTITY_IS_ITERM(iterm)) + break; + } pin = iterm->id; - } else if (pin < selector->selector.bNrInPins) { - pin = selector->selector.baSourceID[index]; - list_for_each_entry(iterm, chain->iterms.next, chain) { + } else if (pin < selector->bNrInPins) { + pin = selector->baSourceID[index]; + list_for_each_entry(iterm, &chain->entities, chain) { + if (!UVC_ENTITY_IS_ITERM(iterm)) + continue; if (iterm->id == pin) break; } @@ -692,7 +680,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } - if (input == 0 || input > chain->selector->selector.bNrInPins) + if (input == 0 || input > chain->selector->bNrInPins) return -EINVAL; return uvc_query_ctrl(chain->dev, UVC_SET_CUR, @@ -731,9 +719,6 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct uvc_streaming_control probe; - if ((ret = uvc_acquire_privileges(handle)) < 0) - return ret; - return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); } @@ -888,6 +873,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (ret < 0) return ret; + if (ret == 0) + uvc_dismiss_privileges(handle); + rb->count = ret; ret = 0; break; @@ -1051,7 +1039,7 @@ static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); - return -ENODEV; + return -EINVAL; } /* diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index a6e41d12b221..05139a4f14f6 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -135,7 +135,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, UVC_CTRL_STREAMING_TIMEOUT); + size, uvc_timeout_param); if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { /* Some cameras, mostly based on Bison Electronics chipsets, @@ -239,7 +239,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream, ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, UVC_CTRL_STREAMING_TIMEOUT); + size, uvc_timeout_param); if (ret != size) { uvc_printk(KERN_ERR, "Failed to set UVC %s control : " "%d (exp. %u).\n", probe ? "probe" : "commit", @@ -770,8 +770,9 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, /* Retry allocations until one succeed. */ for (; npackets > 1; npackets /= 2) { for (i = 0; i < UVC_URBS; ++i) { + stream->urb_size = psize * npackets; stream->urb_buffer[i] = usb_buffer_alloc( - stream->dev->udev, psize * npackets, + stream->dev->udev, stream->urb_size, gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); if (!stream->urb_buffer[i]) { uvc_free_urb_buffers(stream); @@ -780,11 +781,15 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, } if (i == UVC_URBS) { - stream->urb_size = psize * npackets; + uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers " + "of %ux%u bytes each.\n", UVC_URBS, npackets, + psize); return npackets; } } + uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes " + "per packet).\n", psize); return 0; } @@ -935,10 +940,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { - uvc_printk(KERN_WARNING, "device %s requested null " - "bandwidth, defaulting to lowest.\n", - stream->dev->name); + uvc_trace(UVC_TRACE_VIDEO, "Device requested null " + "bandwidth, defaulting to lowest.\n"); bandwidth = 1; + } else { + uvc_trace(UVC_TRACE_VIDEO, "Device requested %u " + "B/frame bandwidth.\n", bandwidth); } for (i = 0; i < intf->num_altsetting; ++i) { @@ -955,8 +962,11 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) break; } - if (i >= intf->num_altsetting) + if (i >= intf->num_altsetting) { + uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting " + "for requested bandwidth.\n"); return -EIO; + } ret = usb_set_interface(stream->dev->udev, intfnum, i); if (ret < 0) diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index e7958aa454ce..7ec9a04ced50 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -75,6 +75,7 @@ struct uvc_xu_control { #define UVC_TERM_INPUT 0x0000 #define UVC_TERM_OUTPUT 0x8000 +#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000) #define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) #define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) @@ -148,7 +149,7 @@ struct uvc_xu_control { #define UVC_MAX_STATUS_SIZE 16 #define UVC_CTRL_CONTROL_TIMEOUT 300 -#define UVC_CTRL_STREAMING_TIMEOUT 1000 +#define UVC_CTRL_STREAMING_TIMEOUT 3000 /* Devices quirks */ #define UVC_QUIRK_STATUS_INTERVAL 0x00000001 @@ -292,11 +293,9 @@ struct uvc_entity { } media; struct { - __u8 bSourceID; } output; struct { - __u8 bSourceID; __u16 wMaxMultiplier; __u8 bControlSize; __u8 *bmControls; @@ -304,21 +303,20 @@ struct uvc_entity { } processing; struct { - __u8 bNrInPins; - __u8 *baSourceID; } selector; struct { __u8 guidExtensionCode[16]; __u8 bNumControls; - __u8 bNrInPins; - __u8 *baSourceID; __u8 bControlSize; __u8 *bmControls; __u8 *bmControlsType; } extension; }; + __u8 bNrInPins; + __u8 *baSourceID; + unsigned int ncontrols; struct uvc_control *controls; }; @@ -408,11 +406,9 @@ struct uvc_video_chain { struct uvc_device *dev; struct list_head list; - struct list_head iterms; /* Input terminals */ - struct list_head oterms; /* Output terminals */ + struct list_head entities; /* All entities */ struct uvc_entity *processing; /* Processing unit */ struct uvc_entity *selector; /* Selector unit */ - struct list_head extensions; /* Extension units */ struct mutex ctrl_mutex; }; @@ -475,7 +471,6 @@ struct uvc_device { char name[32]; enum uvc_device_state state; - struct kref kref; struct list_head list; atomic_t users; @@ -488,6 +483,7 @@ struct uvc_device { /* Video Streaming interfaces */ struct list_head streams; + atomic_t nstreams; /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; @@ -511,8 +507,6 @@ struct uvc_fh { struct uvc_driver { struct usb_driver driver; - struct mutex open_mutex; /* protects from open/disconnect race */ - struct list_head devices; /* struct uvc_device list */ struct list_head controls; /* struct uvc_control_info list */ struct mutex ctrl_mutex; /* protects controls and devices @@ -533,12 +527,14 @@ struct uvc_driver { #define UVC_TRACE_FRAME (1 << 7) #define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_STATUS (1 << 9) +#define UVC_TRACE_VIDEO (1 << 10) #define UVC_WARN_MINMAX 0 #define UVC_WARN_PROBE_DEF 1 extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; +extern unsigned int uvc_timeout_param; #define uvc_trace(flag, msg...) \ do { \ @@ -571,7 +567,6 @@ extern unsigned int uvc_trace_param; /* Core driver */ extern struct uvc_driver uvc_driver; -extern void uvc_delete(struct kref *kref); /* Video buffers queue management. */ extern void uvc_queue_init(struct uvc_video_queue *queue, diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index f5a93ae3cdf9..e8e5affbabce 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -431,6 +431,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; case V4L2_CID_COLOR_KILLER: return "Color Killer"; case V4L2_CID_COLORFX: return "Color Effects"; + case V4L2_CID_ROTATE: return "Rotate"; + case V4L2_CID_BG_COLOR: return "Background color"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -587,6 +589,13 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; min = max = step = def = 0; break; + case V4L2_CID_BG_COLOR: + qctrl->type = V4L2_CTRL_TYPE_INTEGER; + step = 1; + min = 0; + /* Max is calculated as RGB888 that is 2^24 */ + max = 0xFFFFFF; + break; default: qctrl->type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 8e93c6f25c83..bb0a1c8de414 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -110,7 +110,7 @@ EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc); void videobuf_queue_core_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, @@ -360,7 +360,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, q->bufs[i]->bsize = bsize; switch (memory) { case V4L2_MEMORY_MMAP: - q->bufs[i]->boff = bsize * i; + q->bufs[i]->boff = PAGE_ALIGN(bsize) * i; break; case V4L2_MEMORY_USERPTR: case V4L2_MEMORY_OVERLAY: @@ -430,9 +430,9 @@ int videobuf_reqbufs(struct videobuf_queue *q, count = VIDEO_MAX_FRAME; size = 0; q->ops->buf_setup(q, &count, &size); - size = PAGE_ALIGN(size); - dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n", - count, size, (count*size)>>PAGE_SHIFT); + dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n", + count, size, + (unsigned int)((count*PAGE_ALIGN(size))>>PAGE_SHIFT) ); retval = __videobuf_mmap_setup(q, count, size, req->memory); if (retval < 0) { @@ -1099,7 +1099,7 @@ int videobuf_cgmbuf(struct videobuf_queue *q, mbuf->size = 0; for (i = 0; i < mbuf->frames; i++) { mbuf->offsets[i] = q->bufs[i]->boff; - mbuf->size += q->bufs[i]->bsize; + mbuf->size += PAGE_ALIGN(q->bufs[i]->bsize); } return 0; diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index c3065c4bcba9..d25f28461da1 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -429,7 +429,7 @@ static struct videobuf_qtype_ops qops = { }; void videobuf_queue_dma_contig_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index 032ebae0134a..fa78555b118b 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -588,7 +588,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, retval = -EBUSY; goto done; } - size += q->bufs[last]->bsize; + size += PAGE_ALIGN(q->bufs[last]->bsize); if (size == (vma->vm_end - vma->vm_start)) break; } @@ -610,7 +610,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, continue; q->bufs[i]->map = map; q->bufs[i]->baddr = vma->vm_start + size; - size += q->bufs[i]->bsize; + size += PAGE_ALIGN(q->bufs[i]->bsize); } map->count = 1; @@ -702,7 +702,7 @@ void *videobuf_sg_alloc(size_t size) } void videobuf_queue_sg_init(struct videobuf_queue* q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 0e7dcba8e4ae..a56cf0d3a6d6 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -139,7 +139,9 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe, struct device *device, char *adapter_name, short *adapter_nr, - int mfe_shared) + int mfe_shared, + int (*fe_ioctl_override)(struct dvb_frontend *, + unsigned int, void *, unsigned int)) { int result; @@ -154,6 +156,7 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe, } fe->adapter.priv = adapter_priv; fe->adapter.mfe_shared = mfe_shared; + fe->adapter.fe_ioctl_override = fe_ioctl_override; return result; } @@ -253,7 +256,9 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, void *adapter_priv, struct device *device, short *adapter_nr, - int mfe_shared) + int mfe_shared, + int (*fe_ioctl_override)(struct dvb_frontend *, + unsigned int, void *, unsigned int)) { struct list_head *list, *q; struct videobuf_dvb_frontend *fe; @@ -267,7 +272,7 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, /* Bring up the adapter */ res = videobuf_dvb_register_adapter(f, module, adapter_priv, device, - fe->dvb.name, adapter_nr, mfe_shared); + fe->dvb.name, adapter_nr, mfe_shared, fe_ioctl_override); if (res < 0) { printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res); return res; diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 35f3900c5633..d6e6a28fb6b8 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -391,8 +391,8 @@ static struct videobuf_qtype_ops qops = { }; void videobuf_queue_vmalloc_init(struct videobuf_queue* q, - struct videobuf_queue_ops *ops, - void *dev, + const struct videobuf_queue_ops *ops, + struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index 97e0ce28ff18..33205d7537d8 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -391,7 +391,7 @@ static int vpx3220_s_routing(struct v4l2_subdev *sd, {0x0e, 1} }; - if (input < 0 || input > 2) + if (input > 2) return -EINVAL; v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h index d439c76b27e1..cb1de7ea197a 100644 --- a/drivers/media/video/zoran/zoran.h +++ b/drivers/media/video/zoran/zoran.h @@ -106,7 +106,7 @@ struct zoran_params { unsigned long jpeg_markers; /* Which markers should go into the JPEG output. * Unless you exactly know what you do, leave them untouched. * Inluding less markers will make the resulting code - * smaller, but there will be fewer aplications + * smaller, but there will be fewer applications * which can read it. * The presence of the APP and COM marker is * influenced by APP0_len and COM_len ONLY! */ diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 47137deafcfd..e9f72ca458f1 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -2764,7 +2764,7 @@ static int zoran_enum_input(struct file *file, void *__fh, struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - if (inp->index < 0 || inp->index >= zr->card.inputs) + if (inp->index >= zr->card.inputs) return -EINVAL; else { int id = inp->index; diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 9aae011d92ab..2ef110b5221b 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -115,6 +115,7 @@ static struct usb_device_id device_table[] = { {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 }, + {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, {} /* Terminating entry */ }; diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 8dd4d219e433..b4948671eb92 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -76,8 +76,8 @@ #define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "3.04.12" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.12" +#define MPT_LINUX_VERSION_COMMON "3.04.13" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.13" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index 9b2e2198aee9..352acd05c46b 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -621,11 +621,8 @@ __mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ iocnumX = khdr.iocnum & 0xFF; if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) || - (iocp == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_ioctl() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnumX); + (iocp == NULL)) return -ENODEV; - } if (!iocp->active) { printk(KERN_DEBUG MYNAM "%s::mptctl_ioctl() @%d - Controller disabled.\n", diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index c29578614504..57752751712b 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -792,11 +792,36 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) * precedence! */ sc->result = (DID_OK << 16) | scsi_status; - if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { - /* Have already saved the status and sense data + if (!(scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)) { + + /* + * For an Errata on LSI53C1030 + * When the length of request data + * and transfer data are different + * with result of command (READ or VERIFY), + * DID_SOFT_ERROR is set. */ - ; - } else { + if (ioc->bus_type == SPI) { + if (pScsiReq->CDB[0] == READ_6 || + pScsiReq->CDB[0] == READ_10 || + pScsiReq->CDB[0] == READ_12 || + pScsiReq->CDB[0] == READ_16 || + pScsiReq->CDB[0] == VERIFY || + pScsiReq->CDB[0] == VERIFY_16) { + if (scsi_bufflen(sc) != + xfer_cnt) { + sc->result = + DID_SOFT_ERROR << 16; + printk(KERN_WARNING "Errata" + "on LSI53C1030 occurred." + "sc->req_bufflen=0x%02x," + "xfer_cnt=0x%02x\n", + scsi_bufflen(sc), + xfer_cnt); + } + } + } + if (xfer_cnt < sc->underflow) { if (scsi_status == SAM_STAT_BUSY) sc->result = SAM_STAT_BUSY; @@ -835,7 +860,58 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) sc->result = (DID_OK << 16) | scsi_status; if (scsi_state == 0) { ; - } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { + } else if (scsi_state & + MPI_SCSI_STATE_AUTOSENSE_VALID) { + + /* + * For potential trouble on LSI53C1030. + * (date:2007.xx.) + * It is checked whether the length of + * request data is equal to + * the length of transfer and residual. + * MEDIUM_ERROR is set by incorrect data. + */ + if ((ioc->bus_type == SPI) && + (sc->sense_buffer[2] & 0x20)) { + u32 difftransfer; + difftransfer = + sc->sense_buffer[3] << 24 | + sc->sense_buffer[4] << 16 | + sc->sense_buffer[5] << 8 | + sc->sense_buffer[6]; + if (((sc->sense_buffer[3] & 0x80) == + 0x80) && (scsi_bufflen(sc) + != xfer_cnt)) { + sc->sense_buffer[2] = + MEDIUM_ERROR; + sc->sense_buffer[12] = 0xff; + sc->sense_buffer[13] = 0xff; + printk(KERN_WARNING"Errata" + "on LSI53C1030 occurred." + "sc->req_bufflen=0x%02x," + "xfer_cnt=0x%02x\n" , + scsi_bufflen(sc), + xfer_cnt); + } + if (((sc->sense_buffer[3] & 0x80) + != 0x80) && + (scsi_bufflen(sc) != + xfer_cnt + difftransfer)) { + sc->sense_buffer[2] = + MEDIUM_ERROR; + sc->sense_buffer[12] = 0xff; + sc->sense_buffer[13] = 0xff; + printk(KERN_WARNING + "Errata on LSI53C1030 occurred" + "sc->req_bufflen=0x%02x," + " xfer_cnt=0x%02x," + "difftransfer=0x%02x\n", + scsi_bufflen(sc), + xfer_cnt, + difftransfer); + } + } + /* * If running against circa 200003dd 909 MPT f/w, * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL @@ -2275,11 +2351,12 @@ mptscsih_slave_destroy(struct scsi_device *sdev) * mptscsih_change_queue_depth - This function will set a devices queue depth * @sdev: per scsi_device pointer * @qdepth: requested queue depth + * @reason: calling context * * Adding support for new 'change_queue_depth' api. */ int -mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth) +mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) { MPT_SCSI_HOST *hd = shost_priv(sdev->host); VirtTarget *vtarget; @@ -2291,6 +2368,9 @@ mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth) starget = scsi_target(sdev); vtarget = starget->hostdata; + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (ioc->bus_type == SPI) { if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)) max_depth = 1; @@ -2357,7 +2437,8 @@ mptscsih_slave_configure(struct scsi_device *sdev) ioc->name, vtarget->negoFlags, vtarget->maxOffset, vtarget->minSyncFactor)); - mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH); + mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH, + SCSI_QDEPTH_DEFAULT); dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "tagged %d, simple %d, ordered %d\n", ioc->name,sdev->tagged_supported, sdev->simple_tags, diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h index e0b33e04a33b..45a5ff3eff61 100644 --- a/drivers/message/fusion/mptscsih.h +++ b/drivers/message/fusion/mptscsih.h @@ -128,7 +128,8 @@ extern int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_F extern int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); extern int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset); -extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth); +extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, + int reason); extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id); extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id); extern struct device_attribute *mptscsih_host_attrs[]; diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index d505b68cd372..e39986a78273 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -940,7 +940,7 @@ static const struct block_device_operations i2o_block_fops = { * Allocate memory for the i2o_block_device struct, gendisk and request * queue and initialize them as far as no additional information is needed. * - * Returns a pointer to the allocated I2O Block device on succes or a + * Returns a pointer to the allocated I2O Block device on success or a * negative error code on failure. */ static struct i2o_block_device *i2o_block_device_alloc(void) diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c index 27cf4af0e13d..e5ab62141503 100644 --- a/drivers/message/i2o/iop.c +++ b/drivers/message/i2o/iop.c @@ -132,7 +132,7 @@ u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr) * Removes a previously added pointer from the context list and returns * the matching context id. * - * Returns context id on succes or 0 on failure. + * Returns context id on success or 0 on failure. */ u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr) { @@ -198,7 +198,7 @@ void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context) * @c: controller to which the context list belong * @ptr: pointer to which the context id should be fetched * - * Returns context id which matches to the pointer on succes or 0 on + * Returns context id which matches to the pointer on success or 0 on * failure. */ u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 570be139f9df..a296e717e86e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -35,6 +35,14 @@ config MFD_ASIC3 This driver supports the ASIC3 multifunction chip found on many PDAs (mainly iPAQ and HTC based ones) +config MFD_SH_MOBILE_SDHI + bool "Support for SuperH Mobile SDHI" + depends on SUPERH + select MFD_CORE + ---help--- + This driver supports the SDHI hardware block found in many + SuperH Mobile SoCs. + config MFD_DM355EVM_MSP bool "DaVinci DM355 EVM microcontroller" depends on I2C && MACH_DAVINCI_DM355_EVM @@ -121,6 +129,12 @@ config TWL4030_POWER and load scripts controling which resources are switched off/on or reset when a sleep, wakeup or warm reset event occurs. +config TWL4030_CODEC + bool + depends on TWL4030_CORE + select MFD_CORE + default n + config MFD_TMIO bool default n diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f3b277b90d40..11350c1d9301 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o +obj-$(CONFIG_MFD_SH_MOBILE_SDHI) += sh_mobile_sdhi.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o @@ -26,6 +27,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o +obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o obj-$(CONFIG_MFD_MC13783) += mc13783-core.o diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 57271cb3b316..84815f9ef636 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -17,11 +17,11 @@ #include <linux/device.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/mfd/mcp.h> #include <mach/dma.h> #include <asm/system.h> -#include "mcp.h" #define to_mcp(d) container_of(d, struct mcp, attached_device) #define to_mcp_driver(d) container_of(d, struct mcp_driver, drv) diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 62b32dabf629..258427232728 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -19,6 +19,7 @@ #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/platform_device.h> +#include <linux/mfd/mcp.h> #include <mach/dma.h> #include <mach/hardware.h> @@ -28,7 +29,6 @@ #include <mach/assabet.h> -#include "mcp.h" struct mcp_sa11x0 { u32 mccr0; @@ -163,6 +163,7 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->dma_audio_wr = DMA_Ser4MCP0Wr; mcp->dma_telco_rd = DMA_Ser4MCP1Rd; mcp->dma_telco_wr = DMA_Ser4MCP1Wr; + mcp->gpio_base = data->gpio_base; platform_set_drvdata(pdev, mcp); diff --git a/drivers/mfd/mcp.h b/drivers/mfd/mcp.h deleted file mode 100644 index c093a93b8808..000000000000 --- a/drivers/mfd/mcp.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * linux/drivers/mfd/mcp.h - * - * Copyright (C) 2001 Russell King, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - */ -#ifndef MCP_H -#define MCP_H - -struct mcp_ops; - -struct mcp { - struct module *owner; - struct mcp_ops *ops; - spinlock_t lock; - int use_count; - unsigned int sclk_rate; - unsigned int rw_timeout; - dma_device_t dma_audio_rd; - dma_device_t dma_audio_wr; - dma_device_t dma_telco_rd; - dma_device_t dma_telco_wr; - struct device attached_device; -}; - -struct mcp_ops { - void (*set_telecom_divisor)(struct mcp *, unsigned int); - void (*set_audio_divisor)(struct mcp *, unsigned int); - void (*reg_write)(struct mcp *, unsigned int, unsigned int); - unsigned int (*reg_read)(struct mcp *, unsigned int); - void (*enable)(struct mcp *); - void (*disable)(struct mcp *); -}; - -void mcp_set_telecom_divisor(struct mcp *, unsigned int); -void mcp_set_audio_divisor(struct mcp *, unsigned int); -void mcp_reg_write(struct mcp *, unsigned int, unsigned int); -unsigned int mcp_reg_read(struct mcp *, unsigned int); -void mcp_enable(struct mcp *); -void mcp_disable(struct mcp *); -#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) - -struct mcp *mcp_host_alloc(struct device *, size_t); -int mcp_host_register(struct mcp *); -void mcp_host_unregister(struct mcp *); - -struct mcp_driver { - struct device_driver drv; - int (*probe)(struct mcp *); - void (*remove)(struct mcp *); - int (*suspend)(struct mcp *, pm_message_t); - int (*resume)(struct mcp *); -}; - -int mcp_driver_register(struct mcp_driver *); -void mcp_driver_unregister(struct mcp_driver *); - -#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device) -#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d) - -#define mcp_priv(mcp) ((void *)((mcp)+1)) - -#endif diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index 4b364bae6b3e..970afa103261 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -44,7 +44,7 @@ #include <asm/mach/irq.h> #include <mach/gpio.h> -#include <mach/menelaus.h> +#include <plat/menelaus.h> #define DRIVER_NAME "menelaus" diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c new file mode 100644 index 000000000000..03efae8041ab --- /dev/null +++ b/drivers/mfd/sh_mobile_sdhi.c @@ -0,0 +1,156 @@ +/* + * SuperH Mobile SDHI + * + * Copyright (C) 2009 Magnus Damm + * + * 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. + * + * Based on "Compaq ASIC3 support": + * + * Copyright 2001 Compaq Computer Corporation. + * Copyright 2004-2005 Phil Blundell + * Copyright 2007-2008 OpenedHand Ltd. + * + * Authors: Phil Blundell <pb@handhelds.org>, + * Samuel Ortiz <sameo@openedhand.com> + * + */ + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/platform_device.h> + +#include <linux/mfd/core.h> +#include <linux/mfd/tmio.h> +#include <linux/mfd/sh_mobile_sdhi.h> + +struct sh_mobile_sdhi { + struct clk *clk; + struct tmio_mmc_data mmc_data; + struct mfd_cell cell_mmc; +}; + +static struct resource sh_mobile_sdhi_resources[] = { + { + .start = 0x000, + .end = 0x1ff, + .flags = IORESOURCE_MEM, + }, + { + .start = 0, + .end = 0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell sh_mobile_sdhi_cell = { + .name = "tmio-mmc", + .num_resources = ARRAY_SIZE(sh_mobile_sdhi_resources), + .resources = sh_mobile_sdhi_resources, +}; + +static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state) +{ + struct platform_device *pdev = to_platform_device(tmio->dev.parent); + struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; + + if (p && p->set_pwr) + p->set_pwr(pdev, state); +} + +static int __init sh_mobile_sdhi_probe(struct platform_device *pdev) +{ + struct sh_mobile_sdhi *priv; + struct resource *mem; + char clk_name[8]; + int ret, irq; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + dev_err(&pdev->dev, "missing MEM resource\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + dev_err(&pdev->dev, "missing IRQ resource\n"); + + if (!mem || (irq < 0)) + return -EINVAL; + + priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL); + if (priv == NULL) { + dev_err(&pdev->dev, "kzalloc failed\n"); + return -ENOMEM; + } + + snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); + priv->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); + ret = PTR_ERR(priv->clk); + kfree(priv); + return ret; + } + + clk_enable(priv->clk); + + /* FIXME: silly const unsigned int hclk */ + *(unsigned int *)&priv->mmc_data.hclk = clk_get_rate(priv->clk); + priv->mmc_data.set_pwr = sh_mobile_sdhi_set_pwr; + + memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc)); + priv->cell_mmc.driver_data = &priv->mmc_data; + priv->cell_mmc.platform_data = &priv->cell_mmc; + priv->cell_mmc.data_size = sizeof(priv->cell_mmc); + + platform_set_drvdata(pdev, priv); + + ret = mfd_add_devices(&pdev->dev, pdev->id, + &priv->cell_mmc, 1, mem, irq); + if (ret) { + clk_disable(priv->clk); + clk_put(priv->clk); + kfree(priv); + } + + return ret; +} + +static int sh_mobile_sdhi_remove(struct platform_device *pdev) +{ + struct sh_mobile_sdhi *priv = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + clk_disable(priv->clk); + clk_put(priv->clk); + kfree(priv); + + return 0; +} + +static struct platform_driver sh_mobile_sdhi_driver = { + .driver = { + .name = "sh_mobile_sdhi", + .owner = THIS_MODULE, + }, + .probe = sh_mobile_sdhi_probe, + .remove = __devexit_p(sh_mobile_sdhi_remove), +}; + +static int __init sh_mobile_sdhi_init(void) +{ + return platform_driver_register(&sh_mobile_sdhi_driver); +} + +static void __exit sh_mobile_sdhi_exit(void) +{ + platform_driver_unregister(&sh_mobile_sdhi_driver); +} + +module_init(sh_mobile_sdhi_init); +module_exit(sh_mobile_sdhi_exit); + +MODULE_DESCRIPTION("SuperH Mobile SDHI driver"); +MODULE_AUTHOR("Magnus Damm"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c new file mode 100644 index 000000000000..77b914907d7c --- /dev/null +++ b/drivers/mfd/twl4030-codec.c @@ -0,0 +1,276 @@ +/* + * MFD driver for twl4030 codec submodule + * + * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com> + * + * Copyright: (C) 2009 Nokia 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> +#include <linux/mfd/core.h> +#include <linux/mfd/twl4030-codec.h> + +#define TWL4030_CODEC_CELLS 2 + +static struct platform_device *twl4030_codec_dev; + +struct twl4030_codec_resource { + int request_count; + u8 reg; + u8 mask; +}; + +struct twl4030_codec { + unsigned int audio_mclk; + struct mutex mutex; + struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX]; + struct mfd_cell cells[TWL4030_CODEC_CELLS]; +}; + +/* + * Modify the resource, the function returns the content of the register + * after the modification. + */ +static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + u8 val; + + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + codec->resource[id].reg); + + if (enable) + val |= codec->resource[id].mask; + else + val &= ~codec->resource[id].mask; + + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, codec->resource[id].reg); + + return val; +} + +static inline int twl4030_codec_get_resource(enum twl4030_codec_res id) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + u8 val; + + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + codec->resource[id].reg); + + return val; +} + +/* + * Enable the resource. + * The function returns with error or the content of the register + */ +int twl4030_codec_enable_resource(enum twl4030_codec_res id) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + int val; + + if (id >= TWL4030_CODEC_RES_MAX) { + dev_err(&twl4030_codec_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&codec->mutex); + if (!codec->resource[id].request_count) + /* Resource was disabled, enable it */ + val = twl4030_codec_set_resource(id, 1); + else + val = twl4030_codec_get_resource(id); + + codec->resource[id].request_count++; + mutex_unlock(&codec->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource); + +/* + * Disable the resource. + * The function returns with error or the content of the register + */ +int twl4030_codec_disable_resource(unsigned id) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + int val; + + if (id >= TWL4030_CODEC_RES_MAX) { + dev_err(&twl4030_codec_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&codec->mutex); + if (!codec->resource[id].request_count) { + dev_err(&twl4030_codec_dev->dev, + "Resource has been disabled already (%u)\n", id); + mutex_unlock(&codec->mutex); + return -EPERM; + } + codec->resource[id].request_count--; + + if (!codec->resource[id].request_count) + /* Resource can be disabled now */ + val = twl4030_codec_set_resource(id, 0); + else + val = twl4030_codec_get_resource(id); + + mutex_unlock(&codec->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource); + +unsigned int twl4030_codec_get_mclk(void) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + + return codec->audio_mclk; +} +EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk); + +static int __devinit twl4030_codec_probe(struct platform_device *pdev) +{ + struct twl4030_codec *codec; + struct twl4030_codec_data *pdata = pdev->dev.platform_data; + struct mfd_cell *cell = NULL; + int ret, childs = 0; + u8 val; + + if (!pdata) { + dev_err(&pdev->dev, "Platform data is missing\n"); + return -EINVAL; + } + + /* Configure APLL_INFREQ and disable APLL if enabled */ + val = 0; + switch (pdata->audio_mclk) { + case 19200000: + val |= TWL4030_APLL_INFREQ_19200KHZ; + break; + case 26000000: + val |= TWL4030_APLL_INFREQ_26000KHZ; + break; + case 38400000: + val |= TWL4030_APLL_INFREQ_38400KHZ; + break; + default: + dev_err(&pdev->dev, "Invalid audio_mclk\n"); + return -EINVAL; + } + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, TWL4030_REG_APLL_CTL); + + codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + platform_set_drvdata(pdev, codec); + + twl4030_codec_dev = pdev; + mutex_init(&codec->mutex); + codec->audio_mclk = pdata->audio_mclk; + + /* Codec power */ + codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; + codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ; + + /* PLL */ + codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL; + codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN; + + if (pdata->audio) { + cell = &codec->cells[childs]; + cell->name = "twl4030_codec_audio"; + cell->platform_data = pdata->audio; + cell->data_size = sizeof(*pdata->audio); + childs++; + } + if (pdata->vibra) { + cell = &codec->cells[childs]; + cell->name = "twl4030_codec_vibra"; + cell->platform_data = pdata->vibra; + cell->data_size = sizeof(*pdata->vibra); + childs++; + } + + if (childs) + ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells, + childs, NULL, 0); + else { + dev_err(&pdev->dev, "No platform data found for childs\n"); + ret = -ENODEV; + } + + if (!ret) + return 0; + + platform_set_drvdata(pdev, NULL); + kfree(codec); + twl4030_codec_dev = NULL; + return ret; +} + +static int __devexit twl4030_codec_remove(struct platform_device *pdev) +{ + struct twl4030_codec *codec = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(codec); + twl4030_codec_dev = NULL; + + return 0; +} + +MODULE_ALIAS("platform:twl4030_codec"); + +static struct platform_driver twl4030_codec_driver = { + .probe = twl4030_codec_probe, + .remove = __devexit_p(twl4030_codec_remove), + .driver = { + .owner = THIS_MODULE, + .name = "twl4030_codec", + }, +}; + +static int __devinit twl4030_codec_init(void) +{ + return platform_driver_register(&twl4030_codec_driver); +} +module_init(twl4030_codec_init); + +static void __devexit twl4030_codec_exit(void) +{ + platform_driver_unregister(&twl4030_codec_driver); +} +module_exit(twl4030_codec_exit); + +MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index a1c47ee95c0e..40449cdf09db 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -39,7 +39,7 @@ #include <linux/i2c/twl4030.h> #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) -#include <mach/cpu.h> +#include <plat/cpu.h> #endif /* @@ -114,6 +114,12 @@ #define twl_has_watchdog() false #endif +#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) +#define twl_has_codec() true +#else +#define twl_has_codec() false +#endif + /* Triton Core internal information (BEGIN) */ /* Last - for index max*/ @@ -601,6 +607,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); } + if (twl_has_codec() && pdata->codec) { + child = add_child(1, "twl4030_codec", + pdata->codec, sizeof(*pdata->codec), + false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + if (twl_has_regulator()) { /* child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); @@ -763,7 +777,7 @@ static int twl4030_remove(struct i2c_client *client) } /* NOTE: this driver only handles a single twl4030/tps659x0 chip */ -static int +static int __init twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id) { int status; diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index fa294b6d600a..85fd9421be94 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -51,6 +51,7 @@ static int ucb1400_core_probe(struct device *dev) struct ucb1400_ts ucb_ts; struct ucb1400_gpio ucb_gpio; struct snd_ac97 *ac97; + struct ucb1400_pdata *pdata = dev->platform_data; memset(&ucb_ts, 0, sizeof(ucb_ts)); memset(&ucb_gpio, 0, sizeof(ucb_gpio)); @@ -88,6 +89,12 @@ static int ucb1400_core_probe(struct device *dev) /* TOUCHSCREEN */ ucb_ts.ac97 = ac97; + + if (pdata != NULL && pdata->irq >= 0) + ucb_ts.irq = pdata->irq; + else + ucb_ts.irq = -1; + ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1); if (!ucb->ucb1400_ts) { err = -ENOMEM; diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index 86fed4870f93..cea9da60850d 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -14,10 +14,10 @@ #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/device.h> +#include <linux/mfd/ucb1x00.h> #include <mach/dma.h> -#include "ucb1x00.h" #define UCB1X00_ATTR(name,input)\ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 60c3988f3cf3..252b74188ec2 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -25,12 +25,12 @@ #include <linux/interrupt.h> #include <linux/device.h> #include <linux/mutex.h> +#include <linux/mfd/ucb1x00.h> +#include <linux/gpio.h> #include <mach/dma.h> #include <mach/hardware.h> -#include "ucb1x00.h" - static DEFINE_MUTEX(ucb1x00_mutex); static LIST_HEAD(ucb1x00_drivers); static LIST_HEAD(ucb1x00_devices); @@ -108,6 +108,60 @@ unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) return ucb1x00_reg_read(ucb, UCB_IO_DATA); } +static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + if (value) + ucb->io_out |= 1 << offset; + else + ucb->io_out &= ~(1 << offset); + + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); + return ucb1x00_reg_read(ucb, UCB_IO_DATA) & (1 << offset); +} + +static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_dir &= ~(1 << offset); + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + spin_unlock_irqrestore(&ucb->io_lock, flags); + + return 0; +} + +static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset + , int value) +{ + struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_dir |= (1 << offset); + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + + if (value) + ucb->io_out |= 1 << offset; + else + ucb->io_out &= ~(1 << offset); + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + spin_unlock_irqrestore(&ucb->io_lock, flags); + + return 0; +} + /* * UCB1300 data sheet says we must: * 1. enable ADC => 5us (including reference startup time) @@ -476,6 +530,7 @@ static int ucb1x00_probe(struct mcp *mcp) struct ucb1x00_driver *drv; unsigned int id; int ret = -ENODEV; + int temp; mcp_enable(mcp); id = mcp_reg_read(mcp, UCB_ID); @@ -508,12 +563,27 @@ static int ucb1x00_probe(struct mcp *mcp) goto err_free; } + ucb->gpio.base = -1; + if (mcp->gpio_base != 0) { + ucb->gpio.label = dev_name(&ucb->dev); + ucb->gpio.base = mcp->gpio_base; + ucb->gpio.ngpio = 10; + ucb->gpio.set = ucb1x00_gpio_set; + ucb->gpio.get = ucb1x00_gpio_get; + ucb->gpio.direction_input = ucb1x00_gpio_direction_input; + ucb->gpio.direction_output = ucb1x00_gpio_direction_output; + ret = gpiochip_add(&ucb->gpio); + if (ret) + goto err_free; + } else + dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); + ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING, "UCB1x00", ucb); if (ret) { printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", ucb->irq, ret); - goto err_free; + goto err_gpio; } mcp_set_drvdata(mcp, ucb); @@ -522,6 +592,7 @@ static int ucb1x00_probe(struct mcp *mcp) if (ret) goto err_irq; + INIT_LIST_HEAD(&ucb->devs); mutex_lock(&ucb1x00_mutex); list_add(&ucb->node, &ucb1x00_devices); @@ -529,10 +600,14 @@ static int ucb1x00_probe(struct mcp *mcp) ucb1x00_add_dev(ucb, drv); } mutex_unlock(&ucb1x00_mutex); + goto out; err_irq: free_irq(ucb->irq, ucb); + err_gpio: + if (ucb->gpio.base != -1) + temp = gpiochip_remove(&ucb->gpio); err_free: kfree(ucb); err_disable: @@ -545,6 +620,7 @@ static void ucb1x00_remove(struct mcp *mcp) { struct ucb1x00 *ucb = mcp_get_drvdata(mcp); struct list_head *l, *n; + int ret; mutex_lock(&ucb1x00_mutex); list_del(&ucb->node); @@ -554,6 +630,12 @@ static void ucb1x00_remove(struct mcp *mcp) } mutex_unlock(&ucb1x00_mutex); + if (ucb->gpio.base != -1) { + ret = gpiochip_remove(&ucb->gpio); + if (ret) + dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret); + } + free_irq(ucb->irq, ucb); device_unregister(&ucb->dev); } @@ -604,6 +686,7 @@ static int ucb1x00_resume(struct mcp *mcp) struct ucb1x00 *ucb = mcp_get_drvdata(mcp); struct ucb1x00_dev *dev; + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); mutex_lock(&ucb1x00_mutex); list_for_each_entry(dev, &ucb->devs, dev_node) { if (dev->drv->resume) diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 61b7d3eb9a2f..000cb414a78a 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -30,12 +30,12 @@ #include <linux/freezer.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <linux/mfd/ucb1x00.h> #include <mach/dma.h> #include <mach/collie.h> #include <asm/mach-types.h> -#include "ucb1x00.h" struct ucb1x00_ts { diff --git a/drivers/mfd/ucb1x00.h b/drivers/mfd/ucb1x00.h deleted file mode 100644 index a8ad8a0ed5db..000000000000 --- a/drivers/mfd/ucb1x00.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * linux/drivers/mfd/ucb1x00.h - * - * Copyright (C) 2001 Russell King, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - */ -#ifndef UCB1200_H -#define UCB1200_H - -#define UCB_IO_DATA 0x00 -#define UCB_IO_DIR 0x01 - -#define UCB_IO_0 (1 << 0) -#define UCB_IO_1 (1 << 1) -#define UCB_IO_2 (1 << 2) -#define UCB_IO_3 (1 << 3) -#define UCB_IO_4 (1 << 4) -#define UCB_IO_5 (1 << 5) -#define UCB_IO_6 (1 << 6) -#define UCB_IO_7 (1 << 7) -#define UCB_IO_8 (1 << 8) -#define UCB_IO_9 (1 << 9) - -#define UCB_IE_RIS 0x02 -#define UCB_IE_FAL 0x03 -#define UCB_IE_STATUS 0x04 -#define UCB_IE_CLEAR 0x04 -#define UCB_IE_ADC (1 << 11) -#define UCB_IE_TSPX (1 << 12) -#define UCB_IE_TSMX (1 << 13) -#define UCB_IE_TCLIP (1 << 14) -#define UCB_IE_ACLIP (1 << 15) - -#define UCB_IRQ_TSPX 12 - -#define UCB_TC_A 0x05 -#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ -#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ - -#define UCB_TC_B 0x06 -#define UCB_TC_B_VOICE_ENA (1 << 3) -#define UCB_TC_B_CLIP (1 << 4) -#define UCB_TC_B_ATT (1 << 6) -#define UCB_TC_B_SIDE_ENA (1 << 11) -#define UCB_TC_B_MUTE (1 << 13) -#define UCB_TC_B_IN_ENA (1 << 14) -#define UCB_TC_B_OUT_ENA (1 << 15) - -#define UCB_AC_A 0x07 -#define UCB_AC_B 0x08 -#define UCB_AC_B_LOOP (1 << 8) -#define UCB_AC_B_MUTE (1 << 13) -#define UCB_AC_B_IN_ENA (1 << 14) -#define UCB_AC_B_OUT_ENA (1 << 15) - -#define UCB_TS_CR 0x09 -#define UCB_TS_CR_TSMX_POW (1 << 0) -#define UCB_TS_CR_TSPX_POW (1 << 1) -#define UCB_TS_CR_TSMY_POW (1 << 2) -#define UCB_TS_CR_TSPY_POW (1 << 3) -#define UCB_TS_CR_TSMX_GND (1 << 4) -#define UCB_TS_CR_TSPX_GND (1 << 5) -#define UCB_TS_CR_TSMY_GND (1 << 6) -#define UCB_TS_CR_TSPY_GND (1 << 7) -#define UCB_TS_CR_MODE_INT (0 << 8) -#define UCB_TS_CR_MODE_PRES (1 << 8) -#define UCB_TS_CR_MODE_POS (2 << 8) -#define UCB_TS_CR_BIAS_ENA (1 << 11) -#define UCB_TS_CR_TSPX_LOW (1 << 12) -#define UCB_TS_CR_TSMX_LOW (1 << 13) - -#define UCB_ADC_CR 0x0a -#define UCB_ADC_SYNC_ENA (1 << 0) -#define UCB_ADC_VREFBYP_CON (1 << 1) -#define UCB_ADC_INP_TSPX (0 << 2) -#define UCB_ADC_INP_TSMX (1 << 2) -#define UCB_ADC_INP_TSPY (2 << 2) -#define UCB_ADC_INP_TSMY (3 << 2) -#define UCB_ADC_INP_AD0 (4 << 2) -#define UCB_ADC_INP_AD1 (5 << 2) -#define UCB_ADC_INP_AD2 (6 << 2) -#define UCB_ADC_INP_AD3 (7 << 2) -#define UCB_ADC_EXT_REF (1 << 5) -#define UCB_ADC_START (1 << 7) -#define UCB_ADC_ENA (1 << 15) - -#define UCB_ADC_DATA 0x0b -#define UCB_ADC_DAT_VAL (1 << 15) -#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) - -#define UCB_ID 0x0c -#define UCB_ID_1200 0x1004 -#define UCB_ID_1300 0x1005 -#define UCB_ID_TC35143 0x9712 - -#define UCB_MODE 0x0d -#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) -#define UCB_MODE_AUD_OFF_CAN (1 << 13) - -#include "mcp.h" - -struct ucb1x00_irq { - void *devid; - void (*fn)(int, void *); -}; - -struct ucb1x00 { - spinlock_t lock; - struct mcp *mcp; - unsigned int irq; - struct semaphore adc_sem; - spinlock_t io_lock; - u16 id; - u16 io_dir; - u16 io_out; - u16 adc_cr; - u16 irq_fal_enbl; - u16 irq_ris_enbl; - struct ucb1x00_irq irq_handler[16]; - struct device dev; - struct list_head node; - struct list_head devs; -}; - -struct ucb1x00_driver; - -struct ucb1x00_dev { - struct list_head dev_node; - struct list_head drv_node; - struct ucb1x00 *ucb; - struct ucb1x00_driver *drv; - void *priv; -}; - -struct ucb1x00_driver { - struct list_head node; - struct list_head devs; - int (*add)(struct ucb1x00_dev *dev); - void (*remove)(struct ucb1x00_dev *dev); - int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state); - int (*resume)(struct ucb1x00_dev *dev); -}; - -#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, dev) - -int ucb1x00_register_driver(struct ucb1x00_driver *); -void ucb1x00_unregister_driver(struct ucb1x00_driver *); - -/** - * ucb1x00_clkrate - return the UCB1x00 SIB clock rate - * @ucb: UCB1x00 structure describing chip - * - * Return the SIB clock rate in Hz. - */ -static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) -{ - return mcp_get_sclk_rate(ucb->mcp); -} - -/** - * ucb1x00_enable - enable the UCB1x00 SIB clock - * @ucb: UCB1x00 structure describing chip - * - * Enable the SIB clock. This can be called multiple times. - */ -static inline void ucb1x00_enable(struct ucb1x00 *ucb) -{ - mcp_enable(ucb->mcp); -} - -/** - * ucb1x00_disable - disable the UCB1x00 SIB clock - * @ucb: UCB1x00 structure describing chip - * - * Disable the SIB clock. The SIB clock will only be disabled - * when the number of ucb1x00_enable calls match the number of - * ucb1x00_disable calls. - */ -static inline void ucb1x00_disable(struct ucb1x00 *ucb) -{ - mcp_disable(ucb->mcp); -} - -/** - * ucb1x00_reg_write - write a UCB1x00 register - * @ucb: UCB1x00 structure describing chip - * @reg: UCB1x00 4-bit register index to write - * @val: UCB1x00 16-bit value to write - * - * Write the UCB1x00 register @reg with value @val. The SIB - * clock must be running for this function to return. - */ -static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) -{ - mcp_reg_write(ucb->mcp, reg, val); -} - -/** - * ucb1x00_reg_read - read a UCB1x00 register - * @ucb: UCB1x00 structure describing chip - * @reg: UCB1x00 4-bit register index to write - * - * Read the UCB1x00 register @reg and return its value. The SIB - * clock must be running for this function to return. - */ -static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) -{ - return mcp_reg_read(ucb->mcp, reg); -} -/** - * ucb1x00_set_audio_divisor - - * @ucb: UCB1x00 structure describing chip - * @div: SIB clock divisor - */ -static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) -{ - mcp_set_audio_divisor(ucb->mcp, div); -} - -/** - * ucb1x00_set_telecom_divisor - - * @ucb: UCB1x00 structure describing chip - * @div: SIB clock divisor - */ -static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) -{ - mcp_set_telecom_divisor(ucb->mcp, div); -} - -void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); -void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); -unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); - -#define UCB_NOSYNC (0) -#define UCB_SYNC (1) - -unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); -void ucb1x00_adc_enable(struct ucb1x00 *ucb); -void ucb1x00_adc_disable(struct ucb1x00 *ucb); - -/* - * Which edges of the IRQ do you want to control today? - */ -#define UCB_RISING (1 << 0) -#define UCB_FALLING (1 << 1) - -int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); -void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); -void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); -int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); - -#endif diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index df1f86b5c83e..2c16ca6501d5 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -246,8 +246,19 @@ config EP93XX_PWM To compile this driver as a module, choose M here: the module will be called ep93xx_pwm. +config DS1682 + tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for Dallas Semiconductor + DS1682 Total Elapsed Time Recorder. + + This driver can also be built as a module. If so, the module + will be called ds1682. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" +source "drivers/misc/iwmc3200top/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index f982d2ecfde7..906a0edcea40 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -20,6 +20,8 @@ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_ISL29003) += isl29003.o obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o +obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_C2PORT) += c2port/ +obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ obj-y += eeprom/ obj-y += cb710/ diff --git a/drivers/i2c/chips/ds1682.c b/drivers/misc/ds1682.c index f3ee4a1abb77..f3ee4a1abb77 100644 --- a/drivers/i2c/chips/ds1682.c +++ b/drivers/misc/ds1682.c diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 6e43ab4231ae..4bb7a3af9ad9 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -417,32 +417,25 @@ static int ics932s401_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - int vendor, device, revision; - - vendor = i2c_smbus_read_word_data(client, - ICS932S401_REG_VENDOR_REV); - vendor >>= 8; - revision = vendor >> ICS932S401_REV_SHIFT; - vendor &= ICS932S401_VENDOR_MASK; - if (vendor != ICS932S401_VENDOR) - return -ENODEV; - - device = i2c_smbus_read_word_data(client, - ICS932S401_REG_DEVICE); - device >>= 8; - if (device != ICS932S401_DEVICE) - return -ENODEV; - - if (revision != ICS932S401_REV) - dev_info(&adapter->dev, "Unknown revision %d\n", - revision); - } else - dev_dbg(&adapter->dev, "detection forced\n"); + vendor = i2c_smbus_read_word_data(client, ICS932S401_REG_VENDOR_REV); + vendor >>= 8; + revision = vendor >> ICS932S401_REV_SHIFT; + vendor &= ICS932S401_VENDOR_MASK; + if (vendor != ICS932S401_VENDOR) + return -ENODEV; + + device = i2c_smbus_read_word_data(client, ICS932S401_REG_DEVICE); + device >>= 8; + if (device != ICS932S401_DEVICE) + return -ENODEV; + + if (revision != ICS932S401_REV) + dev_info(&adapter->dev, "Unknown revision %d\n", revision); strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); diff --git a/drivers/misc/iwmc3200top/Kconfig b/drivers/misc/iwmc3200top/Kconfig new file mode 100644 index 000000000000..9e4b88fb57f1 --- /dev/null +++ b/drivers/misc/iwmc3200top/Kconfig @@ -0,0 +1,20 @@ +config IWMC3200TOP + tristate "Intel Wireless MultiCom Top Driver" + depends on MMC && EXPERIMENTAL + select FW_LOADER + ---help--- + Intel Wireless MultiCom 3200 Top driver is responsible for + for firmware load and enabled coms enumeration + +config IWMC3200TOP_DEBUG + bool "Enable full debug output of iwmc3200top Driver" + depends on IWMC3200TOP + ---help--- + Enable full debug output of iwmc3200top Driver + +config IWMC3200TOP_DEBUGFS + bool "Enable Debugfs debugging interface for iwmc3200top" + depends on IWMC3200TOP + ---help--- + Enable creation of debugfs files for iwmc3200top + diff --git a/drivers/misc/iwmc3200top/Makefile b/drivers/misc/iwmc3200top/Makefile new file mode 100644 index 000000000000..fbf53fb4634e --- /dev/null +++ b/drivers/misc/iwmc3200top/Makefile @@ -0,0 +1,29 @@ +# iwmc3200top - Intel Wireless MultiCom 3200 Top Driver +# drivers/misc/iwmc3200top/Makefile +# +# Copyright (C) 2009 Intel Corporation. All rights reserved. +# +# 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. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# +# Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> +# - +# +# + +obj-$(CONFIG_IWMC3200TOP) += iwmc3200top.o +iwmc3200top-objs := main.o fw-download.o +iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUG) += log.o +iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUGFS) += debugfs.o diff --git a/drivers/misc/iwmc3200top/debugfs.c b/drivers/misc/iwmc3200top/debugfs.c new file mode 100644 index 000000000000..0c8ea0a1c8a3 --- /dev/null +++ b/drivers/misc/iwmc3200top/debugfs.c @@ -0,0 +1,133 @@ +/* + * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver + * drivers/misc/iwmc3200top/debufs.c + * + * Copyright (C) 2009 Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> + * - + * + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> +#include <linux/debugfs.h> + +#include "iwmc3200top.h" +#include "fw-msg.h" +#include "log.h" +#include "debugfs.h" + + + +/* Constants definition */ +#define HEXADECIMAL_RADIX 16 + +/* Functions definition */ + + +#define DEBUGFS_ADD(name, parent) do { \ + dbgfs->dbgfs_##parent##_files.file_##name = \ + debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \ + &iwmct_dbgfs_##name##_ops); \ +} while (0) + +#define DEBUGFS_RM(name) do { \ + debugfs_remove(name); \ + name = NULL; \ +} while (0) + +#define DEBUGFS_READ_FUNC(name) \ +ssize_t iwmct_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos); + +#define DEBUGFS_WRITE_FUNC(name) \ +ssize_t iwmct_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos); + +#define DEBUGFS_READ_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name) \ + static const struct file_operations iwmct_dbgfs_##name##_ops = { \ + .read = iwmct_dbgfs_##name##_read, \ + .open = iwmct_dbgfs_open_file_generic, \ + }; + +#define DEBUGFS_WRITE_FILE_OPS(name) \ + DEBUGFS_WRITE_FUNC(name) \ + static const struct file_operations iwmct_dbgfs_##name##_ops = { \ + .write = iwmct_dbgfs_##name##_write, \ + .open = iwmct_dbgfs_open_file_generic, \ + }; + +#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name) \ + DEBUGFS_WRITE_FUNC(name) \ + static const struct file_operations iwmct_dbgfs_##name##_ops = {\ + .write = iwmct_dbgfs_##name##_write, \ + .read = iwmct_dbgfs_##name##_read, \ + .open = iwmct_dbgfs_open_file_generic, \ + }; + + +/* Debugfs file ops definitions */ + +/* + * Create the debugfs files and directories + * + */ +void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name) +{ + struct iwmct_debugfs *dbgfs; + + dbgfs = kzalloc(sizeof(struct iwmct_debugfs), GFP_KERNEL); + if (!dbgfs) { + LOG_ERROR(priv, DEBUGFS, "failed to allocate %zd bytes\n", + sizeof(struct iwmct_debugfs)); + return; + } + + priv->dbgfs = dbgfs; + dbgfs->name = name; + dbgfs->dir_drv = debugfs_create_dir(name, NULL); + if (!dbgfs->dir_drv) { + LOG_ERROR(priv, DEBUGFS, "failed to create debugfs dir\n"); + return; + } + + return; +} + +/** + * Remove the debugfs files and directories + * + */ +void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs) +{ + if (!dbgfs) + return; + + DEBUGFS_RM(dbgfs->dir_drv); + kfree(dbgfs); + dbgfs = NULL; +} + diff --git a/drivers/misc/iwmc3200top/debugfs.h b/drivers/misc/iwmc3200top/debugfs.h new file mode 100644 index 000000000000..71d45759b40f --- /dev/null +++ b/drivers/misc/iwmc3200top/debugfs.h @@ -0,0 +1,58 @@ +/* + * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver + * drivers/misc/iwmc3200top/debufs.h + * + * Copyright (C) 2009 Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> + * - + * + */ + +#ifndef __DEBUGFS_H__ +#define __DEBUGFS_H__ + + +#ifdef CONFIG_IWMC3200TOP_DEBUGFS + +struct iwmct_debugfs { + const char *name; + struct dentry *dir_drv; + struct dir_drv_files { + } dbgfs_drv_files; +}; + +void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name); +void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs); + +#else /* CONFIG_IWMC3200TOP_DEBUGFS */ + +struct iwmct_debugfs; + +static inline void +iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name) +{} + +static inline void +iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs) +{} + +#endif /* CONFIG_IWMC3200TOP_DEBUGFS */ + +#endif /* __DEBUGFS_H__ */ + diff --git a/drivers/misc/iwmc3200top/fw-download.c b/drivers/misc/iwmc3200top/fw-download.c new file mode 100644 index 000000000000..50d431e469f5 --- /dev/null +++ b/drivers/misc/iwmc3200top/fw-download.c @@ -0,0 +1,355 @@ +/* + * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver + * drivers/misc/iwmc3200top/fw-download.c + * + * Copyright (C) 2009 Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> + * - + * + */ + +#include <linux/firmware.h> +#include <linux/mmc/sdio_func.h> +#include <asm/unaligned.h> + +#include "iwmc3200top.h" +#include "log.h" +#include "fw-msg.h" + +#define CHECKSUM_BYTES_NUM sizeof(u32) + +/** + init parser struct with file + */ +static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file, + size_t file_size, size_t block_size) +{ + struct iwmct_parser *parser = &priv->parser; + struct iwmct_fw_hdr *fw_hdr = &parser->versions; + + LOG_INFOEX(priv, INIT, "-->\n"); + + LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size); + + parser->file = file; + parser->file_size = file_size; + parser->cur_pos = 0; + parser->buf = NULL; + + parser->buf = kzalloc(block_size, GFP_KERNEL); + if (!parser->buf) { + LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n"); + return -ENOMEM; + } + parser->buf_size = block_size; + + /* extract fw versions */ + memcpy(fw_hdr, parser->file, sizeof(struct iwmct_fw_hdr)); + LOG_INFO(priv, FW_DOWNLOAD, "fw versions are:\n" + "top %u.%u.%u gps %u.%u.%u bt %u.%u.%u tic %s\n", + fw_hdr->top_major, fw_hdr->top_minor, fw_hdr->top_revision, + fw_hdr->gps_major, fw_hdr->gps_minor, fw_hdr->gps_revision, + fw_hdr->bt_major, fw_hdr->bt_minor, fw_hdr->bt_revision, + fw_hdr->tic_name); + + parser->cur_pos += sizeof(struct iwmct_fw_hdr); + + LOG_INFOEX(priv, INIT, "<--\n"); + return 0; +} + +static bool iwmct_checksum(struct iwmct_priv *priv) +{ + struct iwmct_parser *parser = &priv->parser; + __le32 *file = (__le32 *)parser->file; + int i, pad, steps; + u32 accum = 0; + u32 checksum; + u32 mask = 0xffffffff; + + pad = (parser->file_size - CHECKSUM_BYTES_NUM) % 4; + steps = (parser->file_size - CHECKSUM_BYTES_NUM) / 4; + + LOG_INFO(priv, FW_DOWNLOAD, "pad=%d steps=%d\n", pad, steps); + + for (i = 0; i < steps; i++) + accum += le32_to_cpu(file[i]); + + if (pad) { + mask <<= 8 * (4 - pad); + accum += le32_to_cpu(file[steps]) & mask; + } + + checksum = get_unaligned_le32((__le32 *)(parser->file + + parser->file_size - CHECKSUM_BYTES_NUM)); + + LOG_INFO(priv, FW_DOWNLOAD, + "compare checksum accum=0x%x to checksum=0x%x\n", + accum, checksum); + + return checksum == accum; +} + +static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec, + size_t *sec_size, __le32 *sec_addr) +{ + struct iwmct_parser *parser = &priv->parser; + struct iwmct_dbg *dbg = &priv->dbg; + struct iwmct_fw_sec_hdr *sec_hdr; + + LOG_INFOEX(priv, INIT, "-->\n"); + + while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr) + <= parser->file_size) { + + sec_hdr = (struct iwmct_fw_sec_hdr *) + (parser->file + parser->cur_pos); + parser->cur_pos += sizeof(struct iwmct_fw_sec_hdr); + + LOG_INFO(priv, FW_DOWNLOAD, + "sec hdr: type=%s addr=0x%x size=%d\n", + sec_hdr->type, sec_hdr->target_addr, + sec_hdr->data_size); + + if (strcmp(sec_hdr->type, "ENT") == 0) + parser->entry_point = le32_to_cpu(sec_hdr->target_addr); + else if (strcmp(sec_hdr->type, "LBL") == 0) + strcpy(dbg->label_fw, parser->file + parser->cur_pos); + else if (((strcmp(sec_hdr->type, "TOP") == 0) && + (priv->barker & BARKER_DNLOAD_TOP_MSK)) || + ((strcmp(sec_hdr->type, "GPS") == 0) && + (priv->barker & BARKER_DNLOAD_GPS_MSK)) || + ((strcmp(sec_hdr->type, "BTH") == 0) && + (priv->barker & BARKER_DNLOAD_BT_MSK))) { + *sec_addr = sec_hdr->target_addr; + *sec_size = le32_to_cpu(sec_hdr->data_size); + *p_sec = parser->file + parser->cur_pos; + parser->cur_pos += le32_to_cpu(sec_hdr->data_size); + return 1; + } else if (strcmp(sec_hdr->type, "LOG") != 0) + LOG_WARNING(priv, FW_DOWNLOAD, + "skipping section type %s\n", + sec_hdr->type); + + parser->cur_pos += le32_to_cpu(sec_hdr->data_size); + LOG_INFO(priv, FW_DOWNLOAD, + "finished with section cur_pos=%zd\n", parser->cur_pos); + } + + LOG_INFOEX(priv, INIT, "<--\n"); + return 0; +} + +static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec, + size_t sec_size, __le32 addr) +{ + struct iwmct_parser *parser = &priv->parser; + struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf; + const u8 *cur_block = p_sec; + size_t sent = 0; + int cnt = 0; + int ret = 0; + u32 cmd = 0; + + LOG_INFOEX(priv, INIT, "-->\n"); + LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n", + addr, sec_size); + + while (sent < sec_size) { + int i; + u32 chksm = 0; + u32 reset = atomic_read(&priv->reset); + /* actual FW data */ + u32 data_size = min(parser->buf_size - sizeof(*hdr), + sec_size - sent); + /* Pad to block size */ + u32 trans_size = (data_size + sizeof(*hdr) + + IWMC_SDIO_BLK_SIZE - 1) & + ~(IWMC_SDIO_BLK_SIZE - 1); + ++cnt; + + /* in case of reset, interrupt FW DOWNLAOD */ + if (reset) { + LOG_INFO(priv, FW_DOWNLOAD, + "Reset detected. Abort FW download!!!"); + ret = -ECANCELED; + goto exit; + } + + memset(parser->buf, 0, parser->buf_size); + cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS; + cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; + cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS; + cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS; + hdr->data_size = cpu_to_le32(data_size); + hdr->target_addr = addr; + + /* checksum is allowed for sizes divisible by 4 */ + if (data_size & 0x3) + cmd &= ~CMD_HDR_USE_CHECKSUM_MSK; + + memcpy(hdr->data, cur_block, data_size); + + + if (cmd & CMD_HDR_USE_CHECKSUM_MSK) { + + chksm = data_size + le32_to_cpu(addr) + cmd; + for (i = 0; i < data_size >> 2; i++) + chksm += ((u32 *)cur_block)[i]; + + hdr->block_chksm = cpu_to_le32(chksm); + LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n", + hdr->block_chksm); + } + + LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, " + "sec_size=%zd, startAddress 0x%X\n", + cnt, trans_size, sent, sec_size, addr); + + if (priv->dbg.dump) + LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size); + + + hdr->cmd = cpu_to_le32(cmd); + /* send it down */ + /* TODO: add more proper sending and error checking */ + ret = iwmct_tx(priv, 0, parser->buf, trans_size); + if (ret != 0) { + LOG_INFO(priv, FW_DOWNLOAD, + "iwmct_tx returned %d\n", ret); + goto exit; + } + + addr = cpu_to_le32(le32_to_cpu(addr) + data_size); + sent += data_size; + cur_block = p_sec + sent; + + if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) { + LOG_INFO(priv, FW_DOWNLOAD, + "Block number limit is reached [%d]\n", + priv->dbg.blocks); + break; + } + } + + if (sent < sec_size) + ret = -EINVAL; +exit: + LOG_INFOEX(priv, INIT, "<--\n"); + return ret; +} + +static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump) +{ + struct iwmct_parser *parser = &priv->parser; + struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf; + int ret; + u32 cmd; + + LOG_INFOEX(priv, INIT, "-->\n"); + + memset(parser->buf, 0, parser->buf_size); + cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; + if (jump) { + cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS; + hdr->target_addr = cpu_to_le32(parser->entry_point); + LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n", + parser->entry_point); + } else { + cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS; + LOG_INFO(priv, FW_DOWNLOAD, "last command\n"); + } + + hdr->cmd = cpu_to_le32(cmd); + + LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr)); + /* send it down */ + /* TODO: add more proper sending and error checking */ + ret = iwmct_tx(priv, 0, parser->buf, IWMC_SDIO_BLK_SIZE); + if (ret) + LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret); + + LOG_INFOEX(priv, INIT, "<--\n"); + return 0; +} + +int iwmct_fw_load(struct iwmct_priv *priv) +{ + const u8 *fw_name = FW_NAME(FW_API_VER); + const struct firmware *raw; + const u8 *pdata; + size_t len; + __le32 addr; + int ret; + + /* clear parser struct */ + memset(&priv->parser, 0, sizeof(struct iwmct_parser)); + + /* get the firmware */ + ret = request_firmware(&raw, fw_name, &priv->func->dev); + if (ret < 0) { + LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n", + fw_name, ret); + goto exit; + } + + if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) { + LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n", + fw_name, sizeof(struct iwmct_fw_sec_hdr), raw->size); + goto exit; + } + + LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", fw_name); + + ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len); + if (ret < 0) { + LOG_ERROR(priv, FW_DOWNLOAD, + "iwmct_parser_init failed: Reason %d\n", ret); + goto exit; + } + + /* checksum */ + if (!iwmct_checksum(priv)) { + LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n"); + ret = -EINVAL; + goto exit; + } + + /* download firmware to device */ + while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) { + if (iwmct_download_section(priv, pdata, len, addr)) { + LOG_ERROR(priv, FW_DOWNLOAD, + "%s download section failed\n", fw_name); + ret = -EIO; + goto exit; + } + } + + iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK)); + +exit: + kfree(priv->parser.buf); + + if (raw) + release_firmware(raw); + + raw = NULL; + + return ret; +} diff --git a/drivers/misc/iwmc3200top/fw-msg.h b/drivers/misc/iwmc3200top/fw-msg.h new file mode 100644 index 000000000000..9e26b75bd482 --- /dev/null +++ b/drivers/misc/iwmc3200top/fw-msg.h @@ -0,0 +1,113 @@ +/* + * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver + * drivers/misc/iwmc3200top/fw-msg.h + * + * Copyright (C) 2009 Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> + * - + * + */ + +#ifndef __FWMSG_H__ +#define __FWMSG_H__ + +#define COMM_TYPE_D2H 0xFF +#define COMM_TYPE_H2D 0xEE + +#define COMM_CATEGORY_OPERATIONAL 0x00 +#define COMM_CATEGORY_DEBUG 0x01 +#define COMM_CATEGORY_TESTABILITY 0x02 +#define COMM_CATEGORY_DIAGNOSTICS 0x03 + +#define OP_DBG_ZSTR_MSG cpu_to_le16(0x1A) + +#define FW_LOG_SRC_MAX 32 +#define FW_LOG_SRC_ALL 255 + +#define FW_STRING_TABLE_ADDR cpu_to_le32(0x0C000000) + +#define CMD_DBG_LOG_LEVEL cpu_to_le16(0x0001) +#define CMD_TST_DEV_RESET cpu_to_le16(0x0060) +#define CMD_TST_FUNC_RESET cpu_to_le16(0x0062) +#define CMD_TST_IFACE_RESET cpu_to_le16(0x0064) +#define CMD_TST_CPU_UTILIZATION cpu_to_le16(0x0065) +#define CMD_TST_TOP_DEEP_SLEEP cpu_to_le16(0x0080) +#define CMD_TST_WAKEUP cpu_to_le16(0x0081) +#define CMD_TST_FUNC_WAKEUP cpu_to_le16(0x0082) +#define CMD_TST_FUNC_DEEP_SLEEP_REQUEST cpu_to_le16(0x0083) +#define CMD_TST_GET_MEM_DUMP cpu_to_le16(0x0096) + +#define OP_OPR_ALIVE cpu_to_le16(0x0010) +#define OP_OPR_CMD_ACK cpu_to_le16(0x001F) +#define OP_OPR_CMD_NACK cpu_to_le16(0x0020) +#define OP_TST_MEM_DUMP cpu_to_le16(0x0043) + +#define CMD_FLAG_PADDING_256 0x80 + +#define FW_HCMD_BLOCK_SIZE 256 + +struct msg_hdr { + u8 type; + u8 category; + __le16 opcode; + u8 seqnum; + u8 flags; + __le16 length; +} __attribute__((__packed__)); + +struct log_hdr { + __le32 timestamp; + u8 severity; + u8 logsource; + __le16 reserved; +} __attribute__((__packed__)); + +struct mdump_hdr { + u8 dmpid; + u8 frag; + __le16 size; + __le32 addr; +} __attribute__((__packed__)); + +struct top_msg { + struct msg_hdr hdr; + union { + /* D2H messages */ + struct { + struct log_hdr log_hdr; + u8 data[1]; + } __attribute__((__packed__)) log; + + struct { + struct log_hdr log_hdr; + struct mdump_hdr md_hdr; + u8 data[1]; + } __attribute__((__packed__)) mdump; + + /* H2D messages */ + struct { + u8 logsource; + u8 sevmask; + } __attribute__((__packed__)) logdefs[FW_LOG_SRC_MAX]; + struct mdump_hdr mdump_req; + } u; +} __attribute__((__packed__)); + + +#endif /* __FWMSG_H__ */ diff --git a/drivers/misc/iwmc3200top/iwmc3200top.h b/drivers/misc/iwmc3200top/iwmc3200top.h new file mode 100644 index 000000000000..43bd510e1872 --- /dev/null +++ b/drivers/misc/iwmc3200top/iwmc3200top.h @@ -0,0 +1,209 @@ +/* + * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver + * drivers/misc/iwmc3200top/iwmc3200top.h + * + * Copyright (C) 2009 Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> + * - + * + */ + +#ifndef __IWMC3200TOP_H__ +#define __IWMC3200TOP_H__ + +#include <linux/workqueue.h> + +#define DRV_NAME "iwmc3200top" +#define FW_API_VER 1 +#define _FW_NAME(api) DRV_NAME "." #api ".fw" +#define FW_NAME(api) _FW_NAME(api) + +#define IWMC_SDIO_BLK_SIZE 256 +#define IWMC_DEFAULT_TR_BLK 64 +#define IWMC_SDIO_DATA_ADDR 0x0 +#define IWMC_SDIO_INTR_ENABLE_ADDR 0x14 +#define IWMC_SDIO_INTR_STATUS_ADDR 0x13 +#define IWMC_SDIO_INTR_CLEAR_ADDR 0x13 +#define IWMC_SDIO_INTR_GET_SIZE_ADDR 0x2C + +#define COMM_HUB_HEADER_LENGTH 16 +#define LOGGER_HEADER_LENGTH 10 + + +#define BARKER_DNLOAD_BT_POS 0 +#define BARKER_DNLOAD_BT_MSK BIT(BARKER_DNLOAD_BT_POS) +#define BARKER_DNLOAD_GPS_POS 1 +#define BARKER_DNLOAD_GPS_MSK BIT(BARKER_DNLOAD_GPS_POS) +#define BARKER_DNLOAD_TOP_POS 2 +#define BARKER_DNLOAD_TOP_MSK BIT(BARKER_DNLOAD_TOP_POS) +#define BARKER_DNLOAD_RESERVED1_POS 3 +#define BARKER_DNLOAD_RESERVED1_MSK BIT(BARKER_DNLOAD_RESERVED1_POS) +#define BARKER_DNLOAD_JUMP_POS 4 +#define BARKER_DNLOAD_JUMP_MSK BIT(BARKER_DNLOAD_JUMP_POS) +#define BARKER_DNLOAD_SYNC_POS 5 +#define BARKER_DNLOAD_SYNC_MSK BIT(BARKER_DNLOAD_SYNC_POS) +#define BARKER_DNLOAD_RESERVED2_POS 6 +#define BARKER_DNLOAD_RESERVED2_MSK (0x3 << BARKER_DNLOAD_RESERVED2_POS) +#define BARKER_DNLOAD_BARKER_POS 8 +#define BARKER_DNLOAD_BARKER_MSK (0xffffff << BARKER_DNLOAD_BARKER_POS) + +#define IWMC_BARKER_REBOOT (0xdeadbe << BARKER_DNLOAD_BARKER_POS) +/* whole field barker */ +#define IWMC_BARKER_ACK 0xfeedbabe + +#define IWMC_CMD_SIGNATURE 0xcbbc + +#define CMD_HDR_OPCODE_POS 0 +#define CMD_HDR_OPCODE_MSK_MSK (0xf << CMD_HDR_OPCODE_MSK_POS) +#define CMD_HDR_RESPONSE_CODE_POS 4 +#define CMD_HDR_RESPONSE_CODE_MSK (0xf << CMD_HDR_RESPONSE_CODE_POS) +#define CMD_HDR_USE_CHECKSUM_POS 8 +#define CMD_HDR_USE_CHECKSUM_MSK BIT(CMD_HDR_USE_CHECKSUM_POS) +#define CMD_HDR_RESPONSE_REQUIRED_POS 9 +#define CMD_HDR_RESPONSE_REQUIRED_MSK BIT(CMD_HDR_RESPONSE_REQUIRED_POS) +#define CMD_HDR_DIRECT_ACCESS_POS 10 +#define CMD_HDR_DIRECT_ACCESS_MSK BIT(CMD_HDR_DIRECT_ACCESS_POS) +#define CMD_HDR_RESERVED_POS 11 +#define CMD_HDR_RESERVED_MSK BIT(0x1f << CMD_HDR_RESERVED_POS) +#define CMD_HDR_SIGNATURE_POS 16 +#define CMD_HDR_SIGNATURE_MSK BIT(0xffff << CMD_HDR_SIGNATURE_POS) + +enum { + IWMC_OPCODE_PING = 0, + IWMC_OPCODE_READ = 1, + IWMC_OPCODE_WRITE = 2, + IWMC_OPCODE_JUMP = 3, + IWMC_OPCODE_REBOOT = 4, + IWMC_OPCODE_PERSISTENT_WRITE = 5, + IWMC_OPCODE_PERSISTENT_READ = 6, + IWMC_OPCODE_READ_MODIFY_WRITE = 7, + IWMC_OPCODE_LAST_COMMAND = 15 +}; + +struct iwmct_fw_load_hdr { + __le32 cmd; + __le32 target_addr; + __le32 data_size; + __le32 block_chksm; + u8 data[0]; +}; + +/** + * struct iwmct_fw_hdr + * holds all sw components versions + */ +struct iwmct_fw_hdr { + u8 top_major; + u8 top_minor; + u8 top_revision; + u8 gps_major; + u8 gps_minor; + u8 gps_revision; + u8 bt_major; + u8 bt_minor; + u8 bt_revision; + u8 tic_name[31]; +}; + +/** + * struct iwmct_fw_sec_hdr + * @type: function type + * @data_size: section's data size + * @target_addr: download address + */ +struct iwmct_fw_sec_hdr { + u8 type[4]; + __le32 data_size; + __le32 target_addr; +}; + +/** + * struct iwmct_parser + * @file: fw image + * @file_size: fw size + * @cur_pos: position in file + * @buf: temp buf for download + * @buf_size: size of buf + * @entry_point: address to jump in fw kick-off + */ +struct iwmct_parser { + const u8 *file; + size_t file_size; + size_t cur_pos; + u8 *buf; + size_t buf_size; + u32 entry_point; + struct iwmct_fw_hdr versions; +}; + + +struct iwmct_work_struct { + struct list_head list; + ssize_t iosize; +}; + +struct iwmct_dbg { + int blocks; + bool dump; + bool jump; + bool direct; + bool checksum; + bool fw_download; + int block_size; + int download_trans_blks; + + char label_fw[256]; +}; + +struct iwmct_debugfs; + +struct iwmct_priv { + struct sdio_func *func; + struct iwmct_debugfs *dbgfs; + struct iwmct_parser parser; + atomic_t reset; + atomic_t dev_sync; + u32 trans_len; + u32 barker; + struct iwmct_dbg dbg; + + /* drivers work queue */ + struct workqueue_struct *wq; + struct workqueue_struct *bus_rescan_wq; + struct work_struct bus_rescan_worker; + struct work_struct isr_worker; + + /* drivers wait queue */ + wait_queue_head_t wait_q; + + /* rx request list */ + struct list_head read_req_list; +}; + +extern int iwmct_tx(struct iwmct_priv *priv, unsigned int addr, + void *src, int count); + +extern int iwmct_fw_load(struct iwmct_priv *priv); + +extern void iwmct_dbg_init_params(struct iwmct_priv *drv); +extern void iwmct_dbg_init_drv_attrs(struct device_driver *drv); +extern void iwmct_dbg_remove_drv_attrs(struct device_driver *drv); +extern int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len); + +#endif /* __IWMC3200TOP_H__ */ diff --git a/drivers/misc/iwmc3200top/log.c b/drivers/misc/iwmc3200top/log.c new file mode 100644 index 000000000000..d569279698f6 --- /dev/null +++ b/drivers/misc/iwmc3200top/log.c @@ -0,0 +1,347 @@ +/* + * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver + * drivers/misc/iwmc3200top/log.c + * + * Copyright (C) 2009 Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> + * - + * + */ + +#include <linux/kernel.h> +#include <linux/mmc/sdio_func.h> +#include <linux/ctype.h> +#include "fw-msg.h" +#include "iwmc3200top.h" +#include "log.h" + +/* Maximal hexadecimal string size of the FW memdump message */ +#define LOG_MSG_SIZE_MAX 12400 + +/* iwmct_logdefs is a global used by log macros */ +u8 iwmct_logdefs[LOG_SRC_MAX]; +static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX]; + + +static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask) +{ + int i; + + if (src < size) + logdefs[src] = logmask; + else if (src == LOG_SRC_ALL) + for (i = 0; i < size; i++) + logdefs[i] = logmask; + else + return -1; + + return 0; +} + + +int iwmct_log_set_filter(u8 src, u8 logmask) +{ + return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask); +} + + +int iwmct_log_set_fw_filter(u8 src, u8 logmask) +{ + return _log_set_log_filter(iwmct_fw_logdefs, + FW_LOG_SRC_MAX, src, logmask); +} + + +static int log_msg_format_hex(char *str, int slen, u8 *ibuf, + int ilen, char *pref) +{ + int pos = 0; + int i; + int len; + + for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++) + str[pos] = pref[i]; + + for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++) + len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]); + + if (i < ilen) + return -1; + + return 0; +} + +/* NOTE: This function is not thread safe. + Currently it's called only from sdio rx worker - no race there +*/ +void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len) +{ + struct top_msg *msg; + static char logbuf[LOG_MSG_SIZE_MAX]; + + msg = (struct top_msg *)buf; + + if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) { + LOG_ERROR(priv, FW_MSG, "Log message from TOP " + "is too short %d (expected %zd)\n", + len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)); + return; + } + + if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] & + BIT(msg->u.log.log_hdr.severity)) || + !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity))) + return; + + switch (msg->hdr.category) { + case COMM_CATEGORY_TESTABILITY: + if (!(iwmct_logdefs[LOG_SRC_TST] & + BIT(msg->u.log.log_hdr.severity))) + return; + if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf, + le16_to_cpu(msg->hdr.length) + + sizeof(msg->hdr), "<TST>")) + LOG_WARNING(priv, TST, + "TOP TST message is too long, truncating..."); + LOG_WARNING(priv, TST, "%s\n", logbuf); + break; + case COMM_CATEGORY_DEBUG: + if (msg->hdr.opcode == OP_DBG_ZSTR_MSG) + LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>", + ((u8 *)msg) + sizeof(msg->hdr) + + sizeof(msg->u.log.log_hdr)); + else { + if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf, + le16_to_cpu(msg->hdr.length) + + sizeof(msg->hdr), + "<DBG>")) + LOG_WARNING(priv, FW_MSG, + "TOP DBG message is too long," + "truncating..."); + LOG_WARNING(priv, FW_MSG, "%s\n", logbuf); + } + break; + default: + break; + } +} + +static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size) +{ + int i, pos, len; + for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) { + len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,", + i, logdefs[i]); + pos += len; + } + buf[pos-1] = '\n'; + buf[pos] = '\0'; + + if (i < logdefsz) + return -1; + return 0; +} + +int log_get_filter_str(char *buf, int size) +{ + return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size); +} + +int log_get_fw_filter_str(char *buf, int size) +{ + return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size); +} + +#define HEXADECIMAL_RADIX 16 +#define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */ + +ssize_t show_iwmct_log_level(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwmct_priv *priv = dev_get_drvdata(d); + char *str_buf; + int buf_size; + ssize_t ret; + + buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1; + str_buf = kzalloc(buf_size, GFP_KERNEL); + if (!str_buf) { + LOG_ERROR(priv, DEBUGFS, + "failed to allocate %d bytes\n", buf_size); + ret = -ENOMEM; + goto exit; + } + + if (log_get_filter_str(str_buf, buf_size) < 0) { + ret = -EINVAL; + goto exit; + } + + ret = sprintf(buf, "%s", str_buf); + +exit: + kfree(str_buf); + return ret; +} + +ssize_t store_iwmct_log_level(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwmct_priv *priv = dev_get_drvdata(d); + char *token, *str_buf = NULL; + long val; + ssize_t ret = count; + u8 src, mask; + + if (!count) + goto exit; + + str_buf = kzalloc(count, GFP_KERNEL); + if (!str_buf) { + LOG_ERROR(priv, DEBUGFS, + "failed to allocate %zd bytes\n", count); + ret = -ENOMEM; + goto exit; + } + + memcpy(str_buf, buf, count); + + while ((token = strsep(&str_buf, ",")) != NULL) { + while (isspace(*token)) + ++token; + if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) { + LOG_ERROR(priv, DEBUGFS, + "failed to convert string to long %s\n", + token); + ret = -EINVAL; + goto exit; + } + + mask = val & 0xFF; + src = (val & 0XFF00) >> 8; + iwmct_log_set_filter(src, mask); + } + +exit: + kfree(str_buf); + return ret; +} + +ssize_t show_iwmct_log_level_fw(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwmct_priv *priv = dev_get_drvdata(d); + char *str_buf; + int buf_size; + ssize_t ret; + + buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2; + + str_buf = kzalloc(buf_size, GFP_KERNEL); + if (!str_buf) { + LOG_ERROR(priv, DEBUGFS, + "failed to allocate %d bytes\n", buf_size); + ret = -ENOMEM; + goto exit; + } + + if (log_get_fw_filter_str(str_buf, buf_size) < 0) { + ret = -EINVAL; + goto exit; + } + + ret = sprintf(buf, "%s", str_buf); + +exit: + kfree(str_buf); + return ret; +} + +ssize_t store_iwmct_log_level_fw(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwmct_priv *priv = dev_get_drvdata(d); + struct top_msg cmd; + char *token, *str_buf = NULL; + ssize_t ret = count; + u16 cmdlen = 0; + int i; + long val; + u8 src, mask; + + if (!count) + goto exit; + + str_buf = kzalloc(count, GFP_KERNEL); + if (!str_buf) { + LOG_ERROR(priv, DEBUGFS, + "failed to allocate %zd bytes\n", count); + ret = -ENOMEM; + goto exit; + } + + memcpy(str_buf, buf, count); + + cmd.hdr.type = COMM_TYPE_H2D; + cmd.hdr.category = COMM_CATEGORY_DEBUG; + cmd.hdr.opcode = CMD_DBG_LOG_LEVEL; + + for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) && + (i < FW_LOG_SRC_MAX); i++) { + + while (isspace(*token)) + ++token; + + if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) { + LOG_ERROR(priv, DEBUGFS, + "failed to convert string to long %s\n", + token); + ret = -EINVAL; + goto exit; + } + + mask = val & 0xFF; /* LSB */ + src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */ + iwmct_log_set_fw_filter(src, mask); + + cmd.u.logdefs[i].logsource = src; + cmd.u.logdefs[i].sevmask = mask; + } + + cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0])); + cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr)); + + ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen); + if (ret) { + LOG_ERROR(priv, DEBUGFS, + "Failed to send %d bytes of fwcmd, ret=%zd\n", + cmdlen, ret); + goto exit; + } else + LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen); + + ret = count; + +exit: + kfree(str_buf); + return ret; +} + diff --git a/drivers/misc/iwmc3200top/log.h b/drivers/misc/iwmc3200top/log.h new file mode 100644 index 000000000000..aba8121f978c --- /dev/null +++ b/drivers/misc/iwmc3200top/log.h @@ -0,0 +1,158 @@ +/* + * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver + * drivers/misc/iwmc3200top/log.h + * + * Copyright (C) 2009 Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> + * - + * + */ + +#ifndef __LOG_H__ +#define __LOG_H__ + + +/* log severity: + * The log levels here match FW log levels + * so values need to stay as is */ +#define LOG_SEV_CRITICAL 0 +#define LOG_SEV_ERROR 1 +#define LOG_SEV_WARNING 2 +#define LOG_SEV_INFO 3 +#define LOG_SEV_INFOEX 4 + +#define LOG_SEV_FILTER_ALL \ + (BIT(LOG_SEV_CRITICAL) | \ + BIT(LOG_SEV_ERROR) | \ + BIT(LOG_SEV_WARNING) | \ + BIT(LOG_SEV_INFO) | \ + BIT(LOG_SEV_INFOEX)) + +/* log source */ +#define LOG_SRC_INIT 0 +#define LOG_SRC_DEBUGFS 1 +#define LOG_SRC_FW_DOWNLOAD 2 +#define LOG_SRC_FW_MSG 3 +#define LOG_SRC_TST 4 +#define LOG_SRC_IRQ 5 + +#define LOG_SRC_MAX 6 +#define LOG_SRC_ALL 0xFF + +/** + * Default intitialization runtime log level + */ +#ifndef LOG_SEV_FILTER_RUNTIME +#define LOG_SEV_FILTER_RUNTIME \ + (BIT(LOG_SEV_CRITICAL) | \ + BIT(LOG_SEV_ERROR) | \ + BIT(LOG_SEV_WARNING)) +#endif + +#ifndef FW_LOG_SEV_FILTER_RUNTIME +#define FW_LOG_SEV_FILTER_RUNTIME LOG_SEV_FILTER_ALL +#endif + +#ifdef CONFIG_IWMC3200TOP_DEBUG +/** + * Log macros + */ + +#define priv2dev(priv) (&(priv->func)->dev) + +#define LOG_CRITICAL(priv, src, fmt, args...) \ +do { \ + if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_CRITICAL)) \ + dev_crit(priv2dev(priv), "%s %d: " fmt, \ + __func__, __LINE__, ##args); \ +} while (0) + +#define LOG_ERROR(priv, src, fmt, args...) \ +do { \ + if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_ERROR)) \ + dev_err(priv2dev(priv), "%s %d: " fmt, \ + __func__, __LINE__, ##args); \ +} while (0) + +#define LOG_WARNING(priv, src, fmt, args...) \ +do { \ + if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_WARNING)) \ + dev_warn(priv2dev(priv), "%s %d: " fmt, \ + __func__, __LINE__, ##args); \ +} while (0) + +#define LOG_INFO(priv, src, fmt, args...) \ +do { \ + if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFO)) \ + dev_info(priv2dev(priv), "%s %d: " fmt, \ + __func__, __LINE__, ##args); \ +} while (0) + +#define LOG_INFOEX(priv, src, fmt, args...) \ +do { \ + if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \ + dev_dbg(priv2dev(priv), "%s %d: " fmt, \ + __func__, __LINE__, ##args); \ +} while (0) + +#define LOG_HEXDUMP(src, ptr, len) \ +do { \ + if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \ + 16, 1, ptr, len, false); \ +} while (0) + +void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len); + +extern u8 iwmct_logdefs[]; + +int iwmct_log_set_filter(u8 src, u8 logmask); +int iwmct_log_set_fw_filter(u8 src, u8 logmask); + +ssize_t show_iwmct_log_level(struct device *d, + struct device_attribute *attr, char *buf); +ssize_t store_iwmct_log_level(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count); +ssize_t show_iwmct_log_level_fw(struct device *d, + struct device_attribute *attr, char *buf); +ssize_t store_iwmct_log_level_fw(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count); + +#else + +#define LOG_CRITICAL(priv, src, fmt, args...) +#define LOG_ERROR(priv, src, fmt, args...) +#define LOG_WARNING(priv, src, fmt, args...) +#define LOG_INFO(priv, src, fmt, args...) +#define LOG_INFOEX(priv, src, fmt, args...) +#define LOG_HEXDUMP(src, ptr, len) + +static inline void iwmct_log_top_message(struct iwmct_priv *priv, + u8 *buf, int len) {} +static inline int iwmct_log_set_filter(u8 src, u8 logmask) { return 0; } +static inline int iwmct_log_set_fw_filter(u8 src, u8 logmask) { return 0; } + +#endif /* CONFIG_IWMC3200TOP_DEBUG */ + +int log_get_filter_str(char *buf, int size); +int log_get_fw_filter_str(char *buf, int size); + +#endif /* __LOG_H__ */ diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c new file mode 100644 index 000000000000..fafcaa481d74 --- /dev/null +++ b/drivers/misc/iwmc3200top/main.c @@ -0,0 +1,678 @@ +/* + * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver + * drivers/misc/iwmc3200top/main.c + * + * Copyright (C) 2009 Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com> + * - + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/debugfs.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> + +#include "iwmc3200top.h" +#include "log.h" +#include "fw-msg.h" +#include "debugfs.h" + + +#define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver" +#define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation." + +#define DRIVER_VERSION "0.1.62" + +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_COPYRIGHT); +MODULE_FIRMWARE(FW_NAME(FW_API_VER)); + +/* + * This workers main task is to wait for OP_OPR_ALIVE + * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed. + * When OP_OPR_ALIVE received it will issue + * a call to "bus_rescan_devices". + */ +static void iwmct_rescan_worker(struct work_struct *ws) +{ + struct iwmct_priv *priv; + int ret; + + priv = container_of(ws, struct iwmct_priv, bus_rescan_worker); + + LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n"); + + ret = bus_rescan_devices(priv->func->dev.bus); + if (ret < 0) + LOG_INFO(priv, FW_DOWNLOAD, "bus_rescan_devices FAILED!!!\n"); +} + +static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg) +{ + switch (msg->hdr.opcode) { + case OP_OPR_ALIVE: + LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n"); + queue_work(priv->bus_rescan_wq, &priv->bus_rescan_worker); + break; + default: + LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n", + msg->hdr.opcode); + break; + } +} + + +static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len) +{ + struct top_msg *msg; + + msg = (struct top_msg *)buf; + + if (msg->hdr.type != COMM_TYPE_D2H) { + LOG_ERROR(priv, FW_MSG, + "Message from TOP with invalid message type 0x%X\n", + msg->hdr.type); + return; + } + + if (len < sizeof(msg->hdr)) { + LOG_ERROR(priv, FW_MSG, + "Message from TOP is too short for message header " + "received %d bytes, expected at least %zd bytes\n", + len, sizeof(msg->hdr)); + return; + } + + if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) { + LOG_ERROR(priv, FW_MSG, + "Message length (%d bytes) is shorter than " + "in header (%d bytes)\n", + len, le16_to_cpu(msg->hdr.length)); + return; + } + + switch (msg->hdr.category) { + case COMM_CATEGORY_OPERATIONAL: + op_top_message(priv, (struct top_msg *)buf); + break; + + case COMM_CATEGORY_DEBUG: + case COMM_CATEGORY_TESTABILITY: + case COMM_CATEGORY_DIAGNOSTICS: + iwmct_log_top_message(priv, buf, len); + break; + + default: + LOG_ERROR(priv, FW_MSG, + "Message from TOP with unknown category 0x%X\n", + msg->hdr.category); + break; + } +} + +int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len) +{ + int ret; + u8 *buf; + + LOG_INFOEX(priv, FW_MSG, "Sending hcmd:\n"); + + /* add padding to 256 for IWMC */ + ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256; + + LOG_HEXDUMP(FW_MSG, cmd, len); + + if (len > FW_HCMD_BLOCK_SIZE) { + LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n", + len, FW_HCMD_BLOCK_SIZE); + return -1; + } + + buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL); + if (!buf) { + LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n", + FW_HCMD_BLOCK_SIZE); + return -1; + } + + memcpy(buf, cmd, len); + + sdio_claim_host(priv->func); + ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, buf, + FW_HCMD_BLOCK_SIZE); + sdio_release_host(priv->func); + + kfree(buf); + return ret; +} + +int iwmct_tx(struct iwmct_priv *priv, unsigned int addr, + void *src, int count) +{ + int ret; + + sdio_claim_host(priv->func); + ret = sdio_memcpy_toio(priv->func, addr, src, count); + sdio_release_host(priv->func); + + return ret; +} + +static void iwmct_irq_read_worker(struct work_struct *ws) +{ + struct iwmct_priv *priv; + struct iwmct_work_struct *read_req; + __le32 *buf = NULL; + int ret; + int iosize; + u32 barker; + bool is_barker; + + priv = container_of(ws, struct iwmct_priv, isr_worker); + + LOG_INFO(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws); + + /* --------------------- Handshake with device -------------------- */ + sdio_claim_host(priv->func); + + /* all list manipulations have to be protected by + * sdio_claim_host/sdio_release_host */ + if (list_empty(&priv->read_req_list)) { + LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n"); + goto exit_release; + } + + read_req = list_entry(priv->read_req_list.next, + struct iwmct_work_struct, list); + + list_del(&read_req->list); + iosize = read_req->iosize; + kfree(read_req); + + buf = kzalloc(iosize, GFP_KERNEL); + if (!buf) { + LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize); + goto exit_release; + } + + LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n", + iosize, buf, priv->func->num); + + /* read from device */ + ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize); + if (ret) { + LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret); + goto exit_release; + } + + LOG_HEXDUMP(IRQ, (u8 *)buf, iosize); + + barker = le32_to_cpu(buf[0]); + + /* Verify whether it's a barker and if not - treat as regular Rx */ + if (barker == IWMC_BARKER_ACK || + (barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) { + + /* Valid Barker is equal on first 4 dwords */ + is_barker = (buf[1] == buf[0]) && + (buf[2] == buf[0]) && + (buf[3] == buf[0]); + + if (!is_barker) { + LOG_WARNING(priv, IRQ, + "Potentially inconsistent barker " + "%08X_%08X_%08X_%08X\n", + le32_to_cpu(buf[0]), le32_to_cpu(buf[1]), + le32_to_cpu(buf[2]), le32_to_cpu(buf[3])); + } + } else { + is_barker = false; + } + + /* Handle Top CommHub message */ + if (!is_barker) { + sdio_release_host(priv->func); + handle_top_message(priv, (u8 *)buf, iosize); + goto exit; + } else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */ + if (atomic_read(&priv->dev_sync) == 0) { + LOG_ERROR(priv, IRQ, + "ACK barker arrived out-of-sync\n"); + goto exit_release; + } + + /* Continuing to FW download (after Sync is completed)*/ + atomic_set(&priv->dev_sync, 0); + LOG_INFO(priv, IRQ, "ACK barker arrived " + "- starting FW download\n"); + } else { /* REBOOT barker */ + LOG_INFO(priv, IRQ, "Recieved reboot barker: %x\n", barker); + priv->barker = barker; + + if (barker & BARKER_DNLOAD_SYNC_MSK) { + /* Send the same barker back */ + ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, + buf, iosize); + if (ret) { + LOG_ERROR(priv, IRQ, + "error %d echoing barker\n", ret); + goto exit_release; + } + LOG_INFO(priv, IRQ, "Echoing barker to device\n"); + atomic_set(&priv->dev_sync, 1); + goto exit_release; + } + + /* Continuing to FW download (without Sync) */ + LOG_INFO(priv, IRQ, "No sync requested " + "- starting FW download\n"); + } + + sdio_release_host(priv->func); + + + LOG_INFO(priv, IRQ, "barker download request 0x%x is:\n", priv->barker); + LOG_INFO(priv, IRQ, "******* Top FW %s requested ********\n", + (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not"); + LOG_INFO(priv, IRQ, "******* GPS FW %s requested ********\n", + (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not"); + LOG_INFO(priv, IRQ, "******* BT FW %s requested ********\n", + (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not"); + + if (priv->dbg.fw_download) + iwmct_fw_load(priv); + else + LOG_ERROR(priv, IRQ, "FW download not allowed\n"); + + goto exit; + +exit_release: + sdio_release_host(priv->func); +exit: + kfree(buf); + LOG_INFO(priv, IRQ, "exit iwmct_irq_read_worker\n"); +} + +static void iwmct_irq(struct sdio_func *func) +{ + struct iwmct_priv *priv; + int val, ret; + int iosize; + int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR; + struct iwmct_work_struct *read_req; + + priv = sdio_get_drvdata(func); + + LOG_INFO(priv, IRQ, "enter iwmct_irq\n"); + + /* read the function's status register */ + val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret); + + LOG_INFO(priv, IRQ, "iir value = %d, ret=%d\n", val, ret); + + if (!val) { + LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n"); + goto exit_clear_intr; + } + + + /* + * read 2 bytes of the transaction size + * IMPORTANT: sdio transaction size has to be read before clearing + * sdio interrupt!!! + */ + val = sdio_readb(priv->func, addr++, &ret); + iosize = val; + val = sdio_readb(priv->func, addr++, &ret); + iosize += val << 8; + + LOG_INFO(priv, IRQ, "READ size %d\n", iosize); + + if (iosize == 0) { + LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize); + goto exit_clear_intr; + } + + /* allocate a work structure to pass iosize to the worker */ + read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL); + if (!read_req) { + LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n"); + goto exit_clear_intr; + } + + INIT_LIST_HEAD(&read_req->list); + read_req->iosize = iosize; + + list_add_tail(&priv->read_req_list, &read_req->list); + + /* clear the function's interrupt request bit (write 1 to clear) */ + sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret); + + queue_work(priv->wq, &priv->isr_worker); + + LOG_INFO(priv, IRQ, "exit iwmct_irq\n"); + + return; + +exit_clear_intr: + /* clear the function's interrupt request bit (write 1 to clear) */ + sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret); +} + + +static int blocks; +module_param(blocks, int, 0604); +MODULE_PARM_DESC(blocks, "max_blocks_to_send"); + +static int dump; +module_param(dump, bool, 0604); +MODULE_PARM_DESC(dump, "dump_hex_content"); + +static int jump = 1; +module_param(jump, bool, 0604); + +static int direct = 1; +module_param(direct, bool, 0604); + +static int checksum = 1; +module_param(checksum, bool, 0604); + +static int fw_download = 1; +module_param(fw_download, bool, 0604); + +static int block_size = IWMC_SDIO_BLK_SIZE; +module_param(block_size, int, 0404); + +static int download_trans_blks = IWMC_DEFAULT_TR_BLK; +module_param(download_trans_blks, int, 0604); + +static int rubbish_barker; +module_param(rubbish_barker, bool, 0604); + +#ifdef CONFIG_IWMC3200TOP_DEBUG +static int log_level[LOG_SRC_MAX]; +static unsigned int log_level_argc; +module_param_array(log_level, int, &log_level_argc, 0604); +MODULE_PARM_DESC(log_level, "log_level"); + +static int log_level_fw[FW_LOG_SRC_MAX]; +static unsigned int log_level_fw_argc; +module_param_array(log_level_fw, int, &log_level_fw_argc, 0604); +MODULE_PARM_DESC(log_level_fw, "log_level_fw"); +#endif + +void iwmct_dbg_init_params(struct iwmct_priv *priv) +{ +#ifdef CONFIG_IWMC3200TOP_DEBUG + int i; + + for (i = 0; i < log_level_argc; i++) { + dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n", + i, log_level[i]); + iwmct_log_set_filter((log_level[i] >> 8) & 0xFF, + log_level[i] & 0xFF); + } + for (i = 0; i < log_level_fw_argc; i++) { + dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n", + i, log_level_fw[i]); + iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF, + log_level_fw[i] & 0xFF); + } +#endif + + priv->dbg.blocks = blocks; + LOG_INFO(priv, INIT, "blocks=%d\n", blocks); + priv->dbg.dump = (bool)dump; + LOG_INFO(priv, INIT, "dump=%d\n", dump); + priv->dbg.jump = (bool)jump; + LOG_INFO(priv, INIT, "jump=%d\n", jump); + priv->dbg.direct = (bool)direct; + LOG_INFO(priv, INIT, "direct=%d\n", direct); + priv->dbg.checksum = (bool)checksum; + LOG_INFO(priv, INIT, "checksum=%d\n", checksum); + priv->dbg.fw_download = (bool)fw_download; + LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download); + priv->dbg.block_size = block_size; + LOG_INFO(priv, INIT, "block_size=%d\n", block_size); + priv->dbg.download_trans_blks = download_trans_blks; + LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks); +} + +/***************************************************************************** + * + * sysfs attributes + * + *****************************************************************************/ +static ssize_t show_iwmct_fw_version(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwmct_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "%s\n", priv->dbg.label_fw); +} +static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL); + +#ifdef CONFIG_IWMC3200TOP_DEBUG +static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO, + show_iwmct_log_level, store_iwmct_log_level); +static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO, + show_iwmct_log_level_fw, store_iwmct_log_level_fw); +#endif + +static struct attribute *iwmct_sysfs_entries[] = { + &dev_attr_cc_label_fw.attr, +#ifdef CONFIG_IWMC3200TOP_DEBUG + &dev_attr_log_level.attr, + &dev_attr_log_level_fw.attr, +#endif + NULL +}; + +static struct attribute_group iwmct_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = iwmct_sysfs_entries, +}; + + +static int iwmct_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct iwmct_priv *priv; + int ret; + int val = 1; + int addr = IWMC_SDIO_INTR_ENABLE_ADDR; + + dev_dbg(&func->dev, "enter iwmct_probe\n"); + + dev_dbg(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n", + jiffies_to_msecs(2147483647), HZ); + + priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL); + if (!priv) { + dev_err(&func->dev, "kzalloc error\n"); + return -ENOMEM; + } + priv->func = func; + sdio_set_drvdata(func, priv); + + + /* create drivers work queue */ + priv->wq = create_workqueue(DRV_NAME "_wq"); + priv->bus_rescan_wq = create_workqueue(DRV_NAME "_rescan_wq"); + INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker); + INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker); + + init_waitqueue_head(&priv->wait_q); + + sdio_claim_host(func); + /* FIXME: Remove after it is fixed in the Boot ROM upgrade */ + func->enable_timeout = 10; + + /* In our HW, setting the block size also wakes up the boot rom. */ + ret = sdio_set_block_size(func, priv->dbg.block_size); + if (ret) { + LOG_ERROR(priv, INIT, + "sdio_set_block_size() failure: %d\n", ret); + goto error_sdio_enable; + } + + ret = sdio_enable_func(func); + if (ret) { + LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret); + goto error_sdio_enable; + } + + /* init reset and dev_sync states */ + atomic_set(&priv->reset, 0); + atomic_set(&priv->dev_sync, 0); + + /* init read req queue */ + INIT_LIST_HEAD(&priv->read_req_list); + + /* process configurable parameters */ + iwmct_dbg_init_params(priv); + ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group); + if (ret) { + LOG_ERROR(priv, INIT, "Failed to register attributes and " + "initialize module_params\n"); + goto error_dev_attrs; + } + + iwmct_dbgfs_register(priv, DRV_NAME); + + if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) { + LOG_INFO(priv, INIT, + "Reducing transaction to 8 blocks = 2K (from %d)\n", + priv->dbg.download_trans_blks); + priv->dbg.download_trans_blks = 8; + } + priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size; + LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len); + + ret = sdio_claim_irq(func, iwmct_irq); + if (ret) { + LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret); + goto error_claim_irq; + } + + + /* Enable function's interrupt */ + sdio_writeb(priv->func, val, addr, &ret); + if (ret) { + LOG_ERROR(priv, INIT, "Failure writing to " + "Interrupt Enable Register (%d): %d\n", addr, ret); + goto error_enable_int; + } + + sdio_release_host(func); + + LOG_INFO(priv, INIT, "exit iwmct_probe\n"); + + return ret; + +error_enable_int: + sdio_release_irq(func); +error_claim_irq: + sdio_disable_func(func); +error_dev_attrs: + iwmct_dbgfs_unregister(priv->dbgfs); + sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group); +error_sdio_enable: + sdio_release_host(func); + return ret; +} + +static void iwmct_remove(struct sdio_func *func) +{ + struct iwmct_work_struct *read_req; + struct iwmct_priv *priv = sdio_get_drvdata(func); + + priv = sdio_get_drvdata(func); + + LOG_INFO(priv, INIT, "enter\n"); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); + + /* Safely destroy osc workqueue */ + destroy_workqueue(priv->bus_rescan_wq); + destroy_workqueue(priv->wq); + + sdio_claim_host(func); + sdio_disable_func(func); + sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group); + iwmct_dbgfs_unregister(priv->dbgfs); + sdio_release_host(func); + + /* free read requests */ + while (!list_empty(&priv->read_req_list)) { + read_req = list_entry(priv->read_req_list.next, + struct iwmct_work_struct, list); + + list_del(&read_req->list); + kfree(read_req); + } + + kfree(priv); +} + + +static const struct sdio_device_id iwmct_ids[] = { + /* Intel Wireless MultiCom 3200 Top Driver */ + { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1404)}, + { }, /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(sdio, iwmct_ids); + +static struct sdio_driver iwmct_driver = { + .probe = iwmct_probe, + .remove = iwmct_remove, + .name = DRV_NAME, + .id_table = iwmct_ids, +}; + +static int __init iwmct_init(void) +{ + int rc; + + /* Default log filter settings */ + iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME); + iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FILTER_ALL); + iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME); + + rc = sdio_register_driver(&iwmct_driver); + + return rc; +} + +static void __exit iwmct_exit(void) +{ + sdio_unregister_driver(&iwmct_driver); +} + +module_init(iwmct_init); +module_exit(iwmct_exit); + diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c index 41c8fe2a928c..ce5eda985ab0 100644 --- a/drivers/misc/sgi-gru/grufile.c +++ b/drivers/misc/sgi-gru/grufile.c @@ -92,7 +92,7 @@ static void gru_vma_close(struct vm_area_struct *vma) /* * gru_file_mmap * - * Called when mmaping the device. Initializes the vma with a fault handler + * Called when mmapping the device. Initializes the vma with a fault handler * and private data structure necessary to allocate, track, and free the * underlying pages. */ diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index fd3688a3e23f..832ed4c88cf7 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c @@ -89,48 +89,40 @@ static int xpc_disengage_max_timelimit = 120; static ctl_table xpc_sys_xpc_hb_dir[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "hb_interval", .data = &xpc_hb_interval, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &xpc_hb_min_interval, .extra2 = &xpc_hb_max_interval}, { - .ctl_name = CTL_UNNUMBERED, .procname = "hb_check_interval", .data = &xpc_hb_check_interval, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &xpc_hb_check_min_interval, .extra2 = &xpc_hb_check_max_interval}, {} }; static ctl_table xpc_sys_xpc_dir[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "hb", .mode = 0555, .child = xpc_sys_xpc_hb_dir}, { - .ctl_name = CTL_UNNUMBERED, .procname = "disengage_timelimit", .data = &xpc_disengage_timelimit, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &xpc_disengage_min_timelimit, .extra2 = &xpc_disengage_max_timelimit}, {} }; static ctl_table xpc_sys_dir[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "xpc", .mode = 0555, .child = xpc_sys_xpc_dir}, diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 36a8d53ad2a2..b8e7c5ae981e 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -231,7 +231,8 @@ static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port) return ret; } -static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl) +static void sdio_uart_write_mctrl(struct sdio_uart_port *port, + unsigned int mctrl) { unsigned char mcr = 0; @@ -387,7 +388,8 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port) sdio_out(port, UART_IER, port->ier); } -static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status) +static void sdio_uart_receive_chars(struct sdio_uart_port *port, + unsigned int *status) { struct tty_struct *tty = port->tty; unsigned int ch, flag; @@ -399,7 +401,7 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *s port->icount.rx++; if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { + UART_LSR_FE | UART_LSR_OE))) { /* * For statistics only */ @@ -417,9 +419,9 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *s * Mask off conditions which should be ignored. */ *status &= port->read_status_mask; - if (*status & UART_LSR_BI) { + if (*status & UART_LSR_BI) flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) + else if (*status & UART_LSR_PE) flag = TTY_PARITY; else if (*status & UART_LSR_FE) flag = TTY_FRAME; @@ -574,7 +576,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port) */ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO); sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); sdio_out(port, UART_FCR, 0); /* @@ -635,7 +637,7 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port) if (port->tty->termios->c_cflag & HUPCL) sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); - /* Disable interrupts from this port */ + /* Disable interrupts from this port */ sdio_release_irq(port->func); port->ier = 0; sdio_out(port, UART_IER, 0); @@ -659,7 +661,7 @@ skip: free_page((unsigned long)port->xmit.buf); } -static int sdio_uart_open (struct tty_struct *tty, struct file * filp) +static int sdio_uart_open(struct tty_struct *tty, struct file *filp) { struct sdio_uart_port *port; int ret; @@ -846,7 +848,7 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t struct sdio_uart_port *port = tty->driver_data; unsigned int cflag = tty->termios->c_cflag; -#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) if ((cflag ^ old_termios->c_cflag) == 0 && RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) @@ -925,7 +927,7 @@ static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file, struct sdio_uart_port *port = tty->driver_data; int result; - result =sdio_uart_claim_func(port); + result = sdio_uart_claim_func(port); if(!result) { sdio_uart_update_mctrl(port, set, clear); sdio_uart_release_func(port); @@ -946,31 +948,31 @@ static int sdio_uart_proc_show(struct seq_file *m, void *v) seq_printf(m, "%d: uart:SDIO", i); if(capable(CAP_SYS_ADMIN)) { seq_printf(m, " tx:%d rx:%d", - port->icount.tx, port->icount.rx); + port->icount.tx, port->icount.rx); if (port->icount.frame) seq_printf(m, " fe:%d", - port->icount.frame); + port->icount.frame); if (port->icount.parity) seq_printf(m, " pe:%d", - port->icount.parity); + port->icount.parity); if (port->icount.brk) seq_printf(m, " brk:%d", - port->icount.brk); + port->icount.brk); if (port->icount.overrun) seq_printf(m, " oe:%d", - port->icount.overrun); + port->icount.overrun); if (port->icount.cts) seq_printf(m, " cts:%d", - port->icount.cts); + port->icount.cts); if (port->icount.dsr) seq_printf(m, " dsr:%d", - port->icount.dsr); + port->icount.dsr); if (port->icount.rng) seq_printf(m, " rng:%d", - port->icount.rng); + port->icount.rng); if (port->icount.dcd) seq_printf(m, " dcd:%d", - port->icount.dcd); + port->icount.dcd); } sdio_uart_port_put(port); seq_putc(m, '\n'); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 432ae8358c86..e04b751680d0 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -329,7 +329,7 @@ config MMC_SDRICOH_CS config MMC_TMIO tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support" - depends on MFD_TMIO || MFD_ASIC3 + depends on MFD_TMIO || MFD_ASIC3 || SUPERH help This provides support for the SD/MMC cell found in TC6393XB, T7L66XB and also HTC ASIC3 diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 705a5894a6bb..90d168ad03b6 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -56,7 +56,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) clk = 255; host->cclk = host->mclk / (2 * (clk + 1)); } - if (host->hw_designer == 0x80) + if (host->hw_designer == AMBA_VENDOR_ST) clk |= MCI_FCEN; /* Bug fix in ST IP block */ clk |= MCI_CLK_ENABLE; /* This hasn't proven to be worthwhile */ diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index b8fd7af1ceeb..5f970e253e50 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -30,12 +30,12 @@ #include <asm/io.h> #include <asm/irq.h> -#include <mach/board.h> -#include <mach/mmc.h> +#include <plat/board.h> +#include <plat/mmc.h> #include <mach/gpio.h> -#include <mach/dma.h> -#include <mach/mux.h> -#include <mach/fpga.h> +#include <plat/dma.h> +#include <plat/mux.h> +#include <plat/fpga.h> #define OMAP_MMC_REG_CMD 0x00 #define OMAP_MMC_REG_ARGL 0x04 diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 0aecaaebef3d..4b2322518909 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -30,11 +30,11 @@ #include <linux/mmc/core.h> #include <linux/io.h> #include <linux/semaphore.h> -#include <mach/dma.h> +#include <plat/dma.h> #include <mach/hardware.h> -#include <mach/board.h> -#include <mach/mmc.h> -#include <mach/cpu.h> +#include <plat/board.h> +#include <plat/mmc.h> +#include <plat/cpu.h> /* OMAP HSMMC Host Controller Registers */ #define OMAP_HSMMC_SYSCONFIG 0x0010 diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 9fb480bb0e0a..bb47ff465c04 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -43,6 +43,9 @@ #define NR_SG 1 #define CLKRT_OFF (~0) +#define mmc_has_26MHz() (cpu_is_pxa300() || cpu_is_pxa310() \ + || cpu_is_pxa935()) + struct pxamci_host { struct mmc_host *mmc; spinlock_t lock; @@ -457,7 +460,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clk_enable(host->clk); if (ios->clock == 26000000) { - /* to support 26MHz on pxa300/pxa310 */ + /* to support 26MHz */ host->clkrt = 7; } else { /* to handle (19.5MHz, 26MHz) */ @@ -608,8 +611,7 @@ static int pxamci_probe(struct platform_device *pdev) * Calculate minimum clock rate, rounding up. */ mmc->f_min = (host->clkrate + 63) / 64; - mmc->f_max = (cpu_is_pxa300() || cpu_is_pxa310()) ? 26000000 - : host->clkrate; + mmc->f_max = (mmc_has_26MHz()) ? 26000000 : host->clkrate; pxamci_init_ocr(host); @@ -618,7 +620,7 @@ static int pxamci_probe(struct platform_device *pdev) if (!cpu_is_pxa25x()) { mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; host->cmdat |= CMDAT_SDIO_INT_EN; - if (cpu_is_pxa300() || cpu_is_pxa310()) + if (mmc_has_26MHz()) mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; } diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 99b74a351020..941a4d35ef8d 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1360,7 +1360,7 @@ static struct mmc_host_ops s3cmci_ops = { static struct s3c24xx_mci_pdata s3cmci_def_pdata = { /* This is currently here to avoid a number of if (host->pdata) - * checks. Any zero fields to ensure reaonable defaults are picked. */ + * checks. Any zero fields to ensure reasonable defaults are picked. */ }; #ifdef CONFIG_CPU_FREQ diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index 9408099eec48..35c6a23b183b 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -1,5 +1,3 @@ -# drivers/mtd/chips/Kconfig - menu "RAM/ROM/Flash chip drivers" depends on MTD!=n @@ -242,4 +240,3 @@ config MTD_XIP then say N. endmenu - diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index c222514bb70d..35081ce77fbd 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,3 @@ -# drivers/mtd/maps/Kconfig - menu "Self-contained MTD device drivers" depends on MTD!=n @@ -308,4 +306,3 @@ config MTD_DOCPROBE_55AA you have managed to wipe the first block. endmenu - diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 3aa05cd18ea1..592016a0668f 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -18,7 +18,7 @@ to specify the offset instead of the absolute address NOTE: - With slram it's only possible to map a contigous memory region. Therfore + With slram it's only possible to map a contiguous memory region. Therefore if there's a device mapped somewhere in the region specified slram will fail to load (see kernel log if modprobe fails). diff --git a/drivers/mtd/lpddr/Kconfig b/drivers/mtd/lpddr/Kconfig index 5a401d8047ab..265f969817e3 100644 --- a/drivers/mtd/lpddr/Kconfig +++ b/drivers/mtd/lpddr/Kconfig @@ -1,5 +1,3 @@ -# drivers/mtd/chips/Kconfig - menu "LPDDR flash memory drivers" depends on MTD!=n @@ -20,4 +18,3 @@ config MTD_QINFO_PROBE families of devices. This serves similar purpose of CFI on legacy Flash products endmenu - diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 14be0755d7cd..847e214ade59 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,5 +1,3 @@ -# drivers/mtd/maps/Kconfig - menu "Mapping drivers for chip access" depends on MTD!=n @@ -389,9 +387,9 @@ config MTD_IXP2000 depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP2000 help This enables MTD access to flash devices on platforms based - on Intel's IXP2000 family of network processors such as the - IXDP425 and Coyote. If you have an IXP2000 based board and - would like to use the flash chips on it, say 'Y'. + on Intel's IXP2000 family of network processors. If you have an + IXP2000 based board and would like to use the flash chips on it, + say 'Y'. config MTD_FORTUNET tristate "CFI Flash device mapped on the FortuNet board" diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index a24478102b11..ead0b2fab670 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c @@ -45,7 +45,7 @@ #include <asm/io.h> #include <mach/hardware.h> #include <asm/mach/flash.h> -#include <mach/tc.h> +#include <plat/tc.h> #ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { /* "RedBoot", */ "cmdlinepart", NULL }; diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 8ca17a3e96ea..64e2b379a350 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -59,12 +59,14 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->readsect(dev, block, buf)) return -EIO; + rq_flush_dcache_pages(req); return 0; case WRITE: if (!tr->writesect) return -EIO; + rq_flush_dcache_pages(req); for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->writesect(dev, block, buf)) return -EIO; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 2fda0b615246..0e35e1aefd22 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,3 @@ -# drivers/mtd/nand/Kconfig - menuconfig MTD_NAND tristate "NAND Device Support" depends on MTD @@ -358,7 +356,7 @@ endchoice config MTD_NAND_PXA3xx tristate "Support for NAND flash devices on PXA3xx" - depends on MTD_NAND && PXA3xx + depends on MTD_NAND && (PXA3xx || ARCH_MMP) help This enables the driver for the NAND flash device found on PXA3xx processors diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 005b91f096f2..2548e1065bf8 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -25,7 +25,7 @@ #include <mach/hardware.h> #include <asm/sizes.h> #include <mach/gpio.h> -#include <mach/board-ams-delta.h> +#include <plat/board-ams-delta.h> /* * MTD structure for E3 (Delta) diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e51c1ed7ac18..b126cf887476 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1056,7 +1056,7 @@ static struct nand_ecclayout doc200x_oobinfo = { }; /* Find the (I)NFTL Media Header, and optionally also the mirror media header. - On sucessful return, buf will contain a copy of the media header for + On successful return, buf will contain a copy of the media header for further processing. id is the string to scan for, and will presumably be either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media header. The page #s of the found media headers are placed in mh0_page and diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index db7ae9d6a296..92320a643275 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -475,7 +475,7 @@ int __nand_correct_data(unsigned char *buf, * * The b2 shift is there to get rid of the lowest two bits. * We could also do addressbits[b2] >> 1 but for the - * performace it does not make any difference + * performance it does not make any difference */ if (eccsize_mult == 1) byte_addr = (addressbits[b1] << 4) + addressbits[b0]; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 090ab87086b5..1bb799f0125c 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -18,9 +18,9 @@ #include <linux/mtd/partitions.h> #include <linux/io.h> -#include <mach/dma.h> -#include <mach/gpmc.h> -#include <mach/nand.h> +#include <plat/dma.h> +#include <plat/gpmc.h> +#include <plat/nand.h> #define GPMC_IRQ_STATUS 0x18 #define GPMC_ECC_CONFIG 0x1F4 diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 6ea520ae2410..1a5a0365c983 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/platform_device.h> @@ -22,7 +23,7 @@ #include <linux/irq.h> #include <mach/dma.h> -#include <mach/pxa3xx_nand.h> +#include <plat/pxa3xx_nand.h> #define CHIP_DELAY_TIMEOUT (2 * HZ/10) @@ -84,10 +85,6 @@ #define NDCB0_CMD1_MASK (0xff) #define NDCB0_ADDR_CYC_SHIFT (16) -/* dma-able I/O address for the NAND data and commands */ -#define NDCB0_DMA_ADDR (0x43100048) -#define NDDB_DMA_ADDR (0x43100040) - /* macros for registers read/write */ #define nand_writel(info, off, val) \ __raw_writel((val), (info)->mmio_base + (off)) @@ -123,6 +120,7 @@ struct pxa3xx_nand_info { struct clk *clk; void __iomem *mmio_base; + unsigned long mmio_phys; unsigned int buf_start; unsigned int buf_count; @@ -228,13 +226,35 @@ static struct pxa3xx_nand_flash samsung512MbX16 = { .chip_id = 0x46ec, }; +static struct pxa3xx_nand_flash samsung2GbX8 = { + .timing = &samsung512MbX16_timing, + .cmdset = &smallpage_cmdset, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 2048, + .chip_id = 0xdaec, +}; + +static struct pxa3xx_nand_flash samsung32GbX8 = { + .timing = &samsung512MbX16_timing, + .cmdset = &smallpage_cmdset, + .page_per_block = 128, + .page_size = 4096, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 8192, + .chip_id = 0xd7ec, +}; + static struct pxa3xx_nand_timing micron_timing = { .tCH = 10, .tCS = 25, .tWH = 15, .tWP = 25, .tRH = 15, - .tRP = 25, + .tRP = 30, .tR = 25000, .tWHR = 60, .tAR = 10, @@ -262,6 +282,28 @@ static struct pxa3xx_nand_flash micron1GbX16 = { .chip_id = 0xb12c, }; +static struct pxa3xx_nand_flash micron4GbX8 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 4096, + .chip_id = 0xdc2c, +}; + +static struct pxa3xx_nand_flash micron4GbX16 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 4096, + .chip_id = 0xcc2c, +}; + static struct pxa3xx_nand_timing stm2GbX16_timing = { .tCH = 10, .tCS = 35, @@ -287,8 +329,12 @@ static struct pxa3xx_nand_flash stm2GbX16 = { static struct pxa3xx_nand_flash *builtin_flash_types[] = { &samsung512MbX16, + &samsung2GbX8, + &samsung32GbX8, µn1GbX8, µn1GbX16, + µn4GbX8, + µn4GbX16, &stm2GbX16, }; #endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */ @@ -489,7 +535,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info) switch (info->state) { case STATE_PIO_WRITING: __raw_writesl(info->mmio_base + NDDB, info->data_buff, - info->data_size << 2); + DIV_ROUND_UP(info->data_size, 4)); enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); @@ -501,7 +547,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info) break; case STATE_PIO_READING: __raw_readsl(info->mmio_base + NDDB, info->data_buff, - info->data_size << 2); + DIV_ROUND_UP(info->data_size, 4)); break; default: printk(KERN_ERR "%s: invalid state %d\n", __func__, @@ -523,11 +569,11 @@ static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) if (dir_out) { desc->dsadr = info->data_buff_phys; - desc->dtadr = NDDB_DMA_ADDR; + desc->dtadr = info->mmio_phys + NDDB; desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; } else { desc->dtadr = info->data_buff_phys; - desc->dsadr = NDDB_DMA_ADDR; + desc->dsadr = info->mmio_phys + NDDB; desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC; } @@ -669,6 +715,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, /* disable HW ECC to get all the OOB data */ info->buf_count = mtd->writesize + mtd->oobsize; info->buf_start = mtd->writesize + column; + memset(info->data_buff, 0xFF, info->buf_count); if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) break; @@ -1239,13 +1286,17 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) ret = -ENODEV; goto fail_free_res; } + info->mmio_phys = r->start; ret = pxa3xx_nand_init_buff(info); if (ret) goto fail_free_io; - ret = request_irq(IRQ_NAND, pxa3xx_nand_irq, IRQF_DISABLED, - pdev->name, info); + /* initialize all interrupts to be disabled */ + disable_int(info, NDSR_MASK); + + ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED, + pdev->name, info); if (ret < 0) { dev_err(&pdev->dev, "failed to request IRQ\n"); goto fail_free_buf; @@ -1271,7 +1322,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); fail_free_irq: - free_irq(IRQ_NAND, info); + free_irq(irq, info); fail_free_buf: if (use_dma) { pxa_free_dma(info->data_dma_ch); @@ -1296,12 +1347,15 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) struct mtd_info *mtd = platform_get_drvdata(pdev); struct pxa3xx_nand_info *info = mtd->priv; struct resource *r; + int irq; platform_set_drvdata(pdev, NULL); del_mtd_device(mtd); del_mtd_partitions(mtd); - free_irq(IRQ_NAND, info); + irq = platform_get_irq(pdev, 0); + if (irq >= 0) + free_irq(irq, info); if (use_dma) { pxa_free_dma(info->data_dma_ch); dma_free_writecombine(&pdev->dev, info->data_buff_size, diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 11dc7e69c4fb..68b5b3a486a9 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -875,7 +875,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, * @info: The controller instance. * @nmtd: The driver version of the MTD instance. * - * This routine is called after the chip probe has succesfully completed + * This routine is called after the chip probe has successfully completed * and the relevant per-chip information updated. This call ensure that * we update the internal state accordingly. * diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index a38f580c2bb3..3a9f15784600 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -1,7 +1,3 @@ -# -# linux/drivers/mtd/onenand/Kconfig -# - menuconfig MTD_ONENAND tristate "OneNAND Device Support" depends on MTD diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 0108ed42e877..86c4f6dcdc65 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -36,13 +36,13 @@ #include <linux/io.h> #include <asm/mach/flash.h> -#include <mach/gpmc.h> -#include <mach/onenand.h> +#include <plat/gpmc.h> +#include <plat/onenand.h> #include <mach/gpio.h> -#include <mach/dma.h> +#include <plat/dma.h> -#include <mach/board.h> +#include <plat/board.h> #define DRIVER_NAME "omap2-onenand" diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index b1cd7a1a2191..0a8c7ea764ae 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -1,5 +1,3 @@ -# drivers/mtd/ubi/Kconfig - menu "UBI - Unsorted block images" depends on MTD diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 88a72e9c8beb..277786ebaa2c 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -22,6 +22,8 @@ #include <linux/module.h> #include <linux/err.h> +#include <linux/namei.h> +#include <linux/fs.h> #include <asm/div64.h> #include "ubi.h" @@ -280,6 +282,44 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, EXPORT_SYMBOL_GPL(ubi_open_volume_nm); /** + * ubi_open_volume_path - open UBI volume by its character device node path. + * @pathname: volume character device node path + * @mode: open mode + * + * This function is similar to 'ubi_open_volume()', but opens a volume the path + * to its character device node. + */ +struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode) +{ + int error, ubi_num, vol_id; + struct ubi_volume_desc *ret; + struct inode *inode; + struct path path; + + dbg_gen("open volume %s, mode %d", pathname, mode); + + if (!pathname || !*pathname) + return ERR_PTR(-EINVAL); + + error = kern_path(pathname, LOOKUP_FOLLOW, &path); + if (error) + return ERR_PTR(error); + + inode = path.dentry->d_inode; + ubi_num = ubi_major2num(imajor(inode)); + vol_id = iminor(inode) - 1; + + if (vol_id >= 0 && ubi_num >= 0) + ret = ubi_open_volume(ubi_num, vol_id, mode); + else + ret = ERR_PTR(-ENODEV); + + path_put(&path); + return ret; +} +EXPORT_SYMBOL_GPL(ubi_open_volume_path); + +/** * ubi_close_volume - close UBI volume. * @desc: volume descriptor */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 74fdc40c8627..c1d7b880c795 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -147,12 +147,14 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, } if (bytes == 0) { + err = ubi_wl_flush(ubi); + if (err) + return err; + err = clear_update_marker(ubi, vol, 0); if (err) return err; - err = ubi_wl_flush(ubi); - if (!err) - vol->updating = 0; + vol->updating = 0; } vol->upd_buf = vmalloc(ubi->leb_size); @@ -362,16 +364,16 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, ubi_assert(vol->upd_received <= vol->upd_bytes); if (vol->upd_received == vol->upd_bytes) { + err = ubi_wl_flush(ubi); + if (err) + return err; /* The update is finished, clear the update marker */ err = clear_update_marker(ubi, vol, vol->upd_bytes); if (err) return err; - err = ubi_wl_flush(ubi); - if (err == 0) { - vol->updating = 0; - err = to_write; - vfree(vol->upd_buf); - } + vol->updating = 0; + err = to_write; + vfree(vol->upd_buf); } return err; diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c index f60309175ef5..4d4cad393dce 100644 --- a/drivers/net/3c501.c +++ b/drivers/net/3c501.c @@ -249,11 +249,11 @@ static int __init el1_probe1(struct net_device *dev, int ioaddr) * for the Sager NP943 prefix. */ - if (station_addr[0] == 0x02 && station_addr[1] == 0x60 - && station_addr[2] == 0x8c) + if (station_addr[0] == 0x02 && station_addr[1] == 0x60 && + station_addr[2] == 0x8c) mname = "3c501"; - else if (station_addr[0] == 0x00 && station_addr[1] == 0x80 - && station_addr[2] == 0xC8) + else if (station_addr[0] == 0x00 && station_addr[1] == 0x80 && + station_addr[2] == 0xC8) mname = "NP943"; else { release_region(ioaddr, EL1_IO_EXTENT); @@ -345,7 +345,7 @@ static int el_open(struct net_device *dev) if (el_debug > 2) pr_debug("%s: Doing el_open()...\n", dev->name); - retval = request_irq(dev->irq, &el_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, el_interrupt, 0, dev->name, dev); if (retval) return retval; diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c index c71e12d05f6e..66e0323c1839 100644 --- a/drivers/net/3c503.c +++ b/drivers/net/3c503.c @@ -214,8 +214,8 @@ el2_probe1(struct net_device *dev, int ioaddr) iobase_reg = inb(ioaddr+0x403); membase_reg = inb(ioaddr+0x404); /* ASIC location registers should be 0 or have only a single bit set. */ - if ( (iobase_reg & (iobase_reg - 1)) - || (membase_reg & (membase_reg - 1))) { + if ((iobase_reg & (iobase_reg - 1)) || + (membase_reg & (membase_reg - 1))) { retval = -ENODEV; goto out1; } @@ -291,8 +291,8 @@ el2_probe1(struct net_device *dev, int ioaddr) writel(0xba5eba5e, mem_base); for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) { writel(test_val, mem_base + i); - if (readl(mem_base) != 0xba5eba5e - || readl(mem_base + i) != test_val) { + if (readl(mem_base) != 0xba5eba5e || + readl(mem_base + i) != test_val) { pr_warning("3c503: memory failure or memory address conflict.\n"); dev->mem_start = 0; ei_status.name = "3c503-PIO"; @@ -397,9 +397,10 @@ el2_open(struct net_device *dev) unsigned long cookie = probe_irq_on(); outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR); outb_p(0x00, E33G_IDCFR); - if (*irqp == probe_irq_off(cookie) /* It's a good IRQ line! */ - && ((retval = request_irq(dev->irq = *irqp, - eip_interrupt, 0, dev->name, dev)) == 0)) + if (*irqp == probe_irq_off(cookie) && /* It's a good IRQ line! */ + ((retval = request_irq(dev->irq = *irqp, + eip_interrupt, 0, + dev->name, dev)) == 0)) break; } else { if (retval != -EBUSY) diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c index a21c9d15ef8a..9257d7ce0378 100644 --- a/drivers/net/3c505.c +++ b/drivers/net/3c505.c @@ -886,7 +886,7 @@ static int elp_open(struct net_device *dev) /* * install our interrupt service routine */ - if ((retval = request_irq(dev->irq, &elp_interrupt, 0, dev->name, dev))) { + if ((retval = request_irq(dev->irq, elp_interrupt, 0, dev->name, dev))) { pr_err("%s: could not allocate IRQ%d\n", dev->name, dev->irq); return retval; } diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c index a6dc8bcbc7df..fbc231153e55 100644 --- a/drivers/net/3c507.c +++ b/drivers/net/3c507.c @@ -399,7 +399,7 @@ static int __init el16_probe1(struct net_device *dev, int ioaddr) irq = inb(ioaddr + IRQ_CONFIG) & 0x0f; - irqval = request_irq(irq, &el16_interrupt, 0, DRV_NAME, dev); + irqval = request_irq(irq, el16_interrupt, 0, DRV_NAME, dev); if (irqval) { pr_cont("\n"); pr_err("3c507: unable to get IRQ %d (irqval=%d).\n", irq, irqval); @@ -836,8 +836,8 @@ static void el16_rx(struct net_device *dev) void __iomem *data_frame = lp->base + data_buffer_addr; ushort pkt_len = readw(data_frame); - if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 - || (pkt_len & 0xC000) != 0xC000) { + if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 || + (pkt_len & 0xC000) != 0xC000) { pr_err("%s: Rx frame at %#x corrupted, " "status %04x cmd %04x next %04x " "data-buf @%04x %04x.\n", diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index 3b00a4e927aa..9d85efce5916 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -253,9 +253,9 @@ static int el3_isa_id_sequence(__be16 *phys_addr) This check is needed in order not to register them twice. */ for (i = 0; i < el3_cards; i++) { struct el3_private *lp = netdev_priv(el3_devs[i]); - if (lp->type == EL3_PNP - && !memcmp(phys_addr, el3_devs[i]->dev_addr, - ETH_ALEN)) { + if (lp->type == EL3_PNP && + !memcmp(phys_addr, el3_devs[i]->dev_addr, + ETH_ALEN)) { if (el3_debug > 3) pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n", phys_addr[0] & 0xff, phys_addr[0] >> 8, @@ -780,7 +780,7 @@ el3_open(struct net_device *dev) outw(RxReset, ioaddr + EL3_CMD); outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - i = request_irq(dev->irq, &el3_interrupt, 0, dev->name, dev); + i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev); if (i) return i; @@ -835,8 +835,8 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev) #ifndef final_version { /* Error-checking code, delete someday. */ ushort status = inw(ioaddr + EL3_STATUS); - if (status & 0x0001 /* IRQ line active, missed one. */ - && inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */ + if (status & 0x0001 && /* IRQ line active, missed one. */ + inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */ pr_debug("%s: Missed interrupt, status then %04x now %04x" " Tx %2.2x Rx %4.4x.\n", dev->name, status, inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS), diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c index 4adcb950f5f1..063b049ffe55 100644 --- a/drivers/net/3c515.c +++ b/drivers/net/3c515.c @@ -764,13 +764,14 @@ static int corkscrew_open(struct net_device *dev) /* Use the now-standard shared IRQ implementation. */ if (vp->capabilities == 0x11c7) { /* Corkscrew: Cannot share ISA resources. */ - if (dev->irq == 0 - || dev->dma == 0 - || request_irq(dev->irq, &corkscrew_interrupt, 0, - vp->product_name, dev)) return -EAGAIN; + if (dev->irq == 0 || + dev->dma == 0 || + request_irq(dev->irq, corkscrew_interrupt, 0, + vp->product_name, dev)) + return -EAGAIN; enable_dma(dev->dma); set_dma_mode(dev->dma, DMA_MODE_CASCADE); - } else if (request_irq(dev->irq, &corkscrew_interrupt, IRQF_SHARED, + } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED, vp->product_name, dev)) { return -EAGAIN; } @@ -1368,8 +1369,8 @@ static int boomerang_rx(struct net_device *dev) /* Check if the packet is long enough to just accept without copying to a properly sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 4)) != NULL) { + if (pkt_len < rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 4)) != NULL) { skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ memcpy(skb_put(skb, pkt_len), diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c index cb0b730799ba..27d80ca5e4c0 100644 --- a/drivers/net/3c523.c +++ b/drivers/net/3c523.c @@ -288,7 +288,7 @@ static int elmc_open(struct net_device *dev) elmc_id_attn586(); /* disable interrupts */ - ret = request_irq(dev->irq, &elmc_interrupt, IRQF_SHARED | IRQF_SAMPLE_RANDOM, + ret = request_irq(dev->irq, elmc_interrupt, IRQF_SHARED | IRQF_SAMPLE_RANDOM, dev->name, dev); if (ret) { pr_err("%s: couldn't get irq %d\n", dev->name, dev->irq); diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index 6021e6dded8f..36c4191e7bca 100644 --- a/drivers/net/3c527.c +++ b/drivers/net/3c527.c @@ -443,7 +443,7 @@ static int __init mc32_probe1(struct net_device *dev, int slot) * Grab the IRQ */ - err = request_irq(dev->irq, &mc32_interrupt, IRQF_SHARED | IRQF_SAMPLE_RANDOM, DRV_NAME, dev); + err = request_irq(dev->irq, mc32_interrupt, IRQF_SHARED | IRQF_SAMPLE_RANDOM, DRV_NAME, dev); if (err) { release_region(dev->base_addr, MC32_IO_EXTENT); pr_err("%s: unable to get IRQ %d.\n", DRV_NAME, dev->irq); @@ -1168,8 +1168,8 @@ static void mc32_rx_ring(struct net_device *dev) /* Try to save time by avoiding a copy on big frames */ - if ((length > RX_COPYBREAK) - && ((newskb=dev_alloc_skb(1532)) != NULL)) + if ((length > RX_COPYBREAK) && + ((newskb=dev_alloc_skb(1532)) != NULL)) { skb=lp->rx_ring[rx_ring_tail].skb; skb_put(skb, length); diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 975e25b19ebe..78b7167a8ce3 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -1942,8 +1942,8 @@ vortex_error(struct net_device *dev, int status) if (status & TxComplete) { /* Really "TxError" for us. */ tx_status = ioread8(ioaddr + TxStatus); /* Presumably a tx-timeout. We must merely re-enable. */ - if (vortex_debug > 2 - || (tx_status != 0x88 && vortex_debug > 0)) { + if (vortex_debug > 2 || + (tx_status != 0x88 && vortex_debug > 0)) { pr_err("%s: Transmit error, Tx status register %2.2x.\n", dev->name, tx_status); if (tx_status == 0x82) { @@ -2560,7 +2560,7 @@ boomerang_rx(struct net_device *dev) struct sk_buff *skb; entry = vp->dirty_rx % RX_RING_SIZE; if (vp->rx_skbuff[entry] == NULL) { - skb = netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(dev, PKT_BUF_SZ); if (skb == NULL) { static unsigned long last_jif; if (time_after(jiffies, last_jif + 10 * HZ)) { @@ -2572,7 +2572,6 @@ boomerang_rx(struct net_device *dev) break; /* Bad news! */ } - skb_reserve(skb, NET_IP_ALIGN); vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->data, PKT_BUF_SZ, PCI_DMA_FROMDEVICE)); vp->rx_skbuff[entry] = skb; } diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 83a1922e68e0..3f452bcbfb9e 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -549,14 +549,12 @@ rx_status_loop: pr_debug("%s: rx slot %d status 0x%x len %d\n", dev->name, rx_tail, status, len); - new_skb = netdev_alloc_skb(dev, buflen + NET_IP_ALIGN); + new_skb = netdev_alloc_skb_ip_align(dev, buflen); if (!new_skb) { dev->stats.rx_dropped++; goto rx_next; } - skb_reserve(new_skb, NET_IP_ALIGN); - dma_unmap_single(&cp->pdev->dev, mapping, buflen, PCI_DMA_FROMDEVICE); @@ -911,8 +909,8 @@ static void __cp_set_rx_mode (struct net_device *dev) AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; @@ -1057,12 +1055,10 @@ static int cp_refill_rx(struct cp_private *cp) struct sk_buff *skb; dma_addr_t mapping; - skb = netdev_alloc_skb(dev, cp->rx_buf_sz + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(dev, cp->rx_buf_sz); if (!skb) goto err_out; - skb_reserve(skb, NET_IP_ALIGN); - mapping = dma_map_single(&cp->pdev->dev, skb->data, cp->rx_buf_sz, PCI_DMA_FROMDEVICE); cp->rx_skb[i] = skb; diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 4a3628755026..25f7339daabd 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -1549,8 +1549,8 @@ static inline void rtl8139_thread_iter (struct net_device *dev, mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); if (!tp->mii.force_media && mii_lpa != 0xffff) { - int duplex = (mii_lpa & LPA_100FULL) - || (mii_lpa & 0x01C0) == 0x0040; + int duplex = ((mii_lpa & LPA_100FULL) || + (mii_lpa & 0x01C0) == 0x0040); if (tp->mii.full_duplex != duplex) { tp->mii.full_duplex = duplex; @@ -1936,8 +1936,8 @@ static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, RTL_R16 (RxBufAddr), RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); - while (netif_running(dev) && received < budget - && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { + while (netif_running(dev) && received < budget && + (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { u32 ring_offset = cur_rx % RX_BUF_LEN; u32 rx_status; unsigned int pkt_size; @@ -2004,9 +2004,8 @@ no_early_rx: /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ - skb = netdev_alloc_skb(dev, pkt_size + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(dev, pkt_size); if (likely(skb)) { - skb_reserve (skb, NET_IP_ALIGN); /* 16 byte align the IP fields. */ #if RX_BUF_IDX == 3 wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); #else @@ -2522,8 +2521,8 @@ static void __set_rx_mode (struct net_device *dev) AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; diff --git a/drivers/net/82596.c b/drivers/net/82596.c index ea6b139b812c..1663bc9e45de 100644 --- a/drivers/net/82596.c +++ b/drivers/net/82596.c @@ -19,7 +19,7 @@ TBD: * look at deferring rx frames rather than discarding (as per tulip) * handle tx ring full as per tulip - * performace test to tune rx_copybreak + * performance test to tune rx_copybreak Most of my modifications relate to the braindead big-endian implementation by Intel. When the i596 is operating in diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index b2f71f79baaf..0bbd5ae49862 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1001,7 +1001,7 @@ config SMC911X config SMSC911X tristate "SMSC LAN911x/LAN921x families embedded ethernet support" - depends on ARM || SUPERH || BLACKFIN + depends on ARM || SUPERH || BLACKFIN || MIPS select CRC32 select MII select PHYLIB @@ -3235,7 +3235,7 @@ config VIRTIO_NET config VMXNET3 tristate "VMware VMXNET3 ethernet driver" - depends on PCI && X86 && INET + depends on PCI && INET help This driver supports VMware's vmxnet3 virtual ethernet NIC. To compile this driver as a module, choose M here: the diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c index 4e6359fff0e1..766aabfdfc75 100644 --- a/drivers/net/amd8111e.c +++ b/drivers/net/amd8111e.c @@ -1633,8 +1633,13 @@ static int amd8111e_enable_link_change(struct amd8111e_priv* lp) readl(lp->mmio + CMD7); return 0; } -/* This function is called when a packet transmission fails to complete within a resonable period, on the assumption that an interrupts have been failed or the interface is locked up. This function will reinitialize the hardware */ +/* + * This function is called when a packet transmission fails to complete + * within a reasonable period, on the assumption that an interrupt have + * failed or the interface is locked up. This function will reinitialize + * the hardware. + */ static void amd8111e_tx_timeout(struct net_device *dev) { struct amd8111e_priv* lp = netdev_priv(dev); diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index b5dc7f550725..73b38c204eb9 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -120,7 +120,7 @@ static int irq = 5; /* Default IRQ */ * DAYNA driver mode: * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, * Farallon PhoneNET PC III, Farallon PhoneNET PC II - * Other cards possibly supported mode unkown though: + * Other cards possibly supported mode unknown though: * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel) * * Cards NOT supported by this driver but supported by the ltpc.c @@ -328,7 +328,7 @@ static int __init cops_probe1(struct net_device *dev, int ioaddr) /* Reserve any actual interrupt. */ if (dev->irq) { - retval = request_irq(dev->irq, &cops_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, cops_interrupt, 0, dev->name, dev); if (retval) goto err_out; } diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index aaf14d306a4a..eb0448b03f41 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -230,9 +230,9 @@ static int ipddp_delete(struct ipddp_route *rt) spin_lock_bh(&ipddp_route_lock); while((tmp = *r) != NULL) { - if(tmp->ip == rt->ip - && tmp->at.s_net == rt->at.s_net - && tmp->at.s_node == rt->at.s_node) + if(tmp->ip == rt->ip && + tmp->at.s_net == rt->at.s_net && + tmp->at.s_node == rt->at.s_node) { *r = tmp->next; spin_unlock_bh(&ipddp_route_lock); @@ -255,9 +255,9 @@ static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) for(f = ipddp_route_list; f != NULL; f = f->next) { - if(f->ip == rt->ip - && f->at.s_net == rt->at.s_net - && f->at.s_node == rt->at.s_node) + if(f->ip == rt->ip && + f->at.s_net == rt->at.s_net && + f->at.s_node == rt->at.s_node) return (f); } diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c index 08760baece7a..dbfbd3b7ff86 100644 --- a/drivers/net/appletalk/ltpc.c +++ b/drivers/net/appletalk/ltpc.c @@ -1158,7 +1158,7 @@ struct net_device * __init ltpc_probe(void) } /* grab it and don't let go :-) */ - if (irq && request_irq( irq, <pc_interrupt, 0, "ltpc", dev) >= 0) + if (irq && request_irq( irq, ltpc_interrupt, 0, "ltpc", dev) >= 0) { (void) inb_p(io+7); /* enable interrupts from board */ (void) inb_p(io+7); /* and reset irq line */ diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c index e3082a9350fc..e6afab2455b1 100644 --- a/drivers/net/arcnet/arc-rimi.c +++ b/drivers/net/arcnet/arc-rimi.c @@ -156,7 +156,7 @@ static int __init arcrimi_found(struct net_device *dev) } /* reserve the irq */ - if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet (RIM I)", dev)) { + if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) { iounmap(p); release_mem_region(dev->mem_start, MIRROR_SIZE); BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); @@ -174,9 +174,9 @@ static int __init arcrimi_found(struct net_device *dev) * 2k (or there are no mirrors at all) but on some, it's 4k. */ mirror_size = MIRROR_SIZE; - if (readb(p) == TESTvalue - && check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 - && check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1) + if (readb(p) == TESTvalue && + check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 && + check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1) mirror_size = 2 * MIRROR_SIZE; first_mirror = shmem - mirror_size; diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 75a572560909..d8f029303754 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -301,8 +301,8 @@ static int choose_mtu(void) /* choose the smallest MTU of all available encaps */ for (count = 0; count < 256; count++) { - if (arc_proto_map[count] != &arc_proto_null - && arc_proto_map[count]->mtu < mtu) { + if (arc_proto_map[count] != &arc_proto_null && + arc_proto_map[count]->mtu < mtu) { mtu = arc_proto_map[count]->mtu; } } @@ -953,13 +953,13 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) * > RECON_THRESHOLD/min; * then print a warning message. */ - if (!lp->network_down - && (lp->last_recon - lp->first_recon) <= HZ * 60 - && lp->num_recons >= RECON_THRESHOLD) { + if (!lp->network_down && + (lp->last_recon - lp->first_recon) <= HZ * 60 && + lp->num_recons >= RECON_THRESHOLD) { lp->network_down = 1; BUGMSG(D_NORMAL, "many reconfigurations detected: cabling problem?\n"); - } else if (!lp->network_down - && lp->last_recon - lp->first_recon > HZ * 60) { + } else if (!lp->network_down && + lp->last_recon - lp->first_recon > HZ * 60) { /* reset counters if we've gone for over a minute. */ lp->first_recon = lp->last_recon; lp->num_recons = 1; diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index 651275a5f3d2..0a74f21409c5 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -200,7 +200,7 @@ int com20020_found(struct net_device *dev, int shared) outb(dev->dev_addr[0], _XREG); /* reserve the irq */ - if (request_irq(dev->irq, &arcnet_interrupt, shared, + if (request_irq(dev->irq, arcnet_interrupt, shared, "arcnet (COM20020)", dev)) { BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c index 89de29b3b1dc..28dea518d554 100644 --- a/drivers/net/arcnet/com90io.c +++ b/drivers/net/arcnet/com90io.c @@ -238,7 +238,7 @@ static int __init com90io_found(struct net_device *dev) int err; /* Reserve the irq */ - if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) { + if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) { BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; } diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c index d762fe46251e..112e230cb13d 100644 --- a/drivers/net/arcnet/com90xx.c +++ b/drivers/net/arcnet/com90xx.c @@ -501,7 +501,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem goto err_free_dev; /* reserve the irq */ - if (request_irq(airq, &arcnet_interrupt, 0, "arcnet (90xx)", dev)) { + if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) { BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", airq); goto err_release_mem; } diff --git a/drivers/net/ariadne.h b/drivers/net/ariadne.h index bb613f292e04..727be5cdd1ea 100644 --- a/drivers/net/ariadne.h +++ b/drivers/net/ariadne.h @@ -244,7 +244,7 @@ struct Am79C960 { #define DLNKTST 0x0010 /* Disable Link Status */ #define DAPC 0x0008 /* Disable Automatic Polarity Correction */ #define MENDECL 0x0004 /* MENDEC Loopback Mode */ -#define LRTTSEL 0x0002 /* Low Receive Treshold/Transmit Mode Select */ +#define LRTTSEL 0x0002 /* Low Receive Threshold/Transmit Mode Select */ #define PORTSEL1 0x0001 /* Port Select Bits */ #define PORTSEL2 0x8000 /* Port Select Bits */ #define INTL 0x4000 /* Internal Loopback */ diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c index 2a7b7745cc55..be256b34cea8 100644 --- a/drivers/net/arm/ks8695net.c +++ b/drivers/net/arm/ks8695net.c @@ -35,11 +35,13 @@ #include <mach/regs-switch.h> #include <mach/regs-misc.h> +#include <asm/mach/irq.h> +#include <mach/regs-irq.h> #include "ks8695net.h" #define MODULENAME "ks8695_ether" -#define MODULEVERSION "1.01" +#define MODULEVERSION "1.02" /* * Transmit and device reset timeout, default 5 seconds. @@ -95,6 +97,9 @@ struct ks8695_skbuff { #define MAX_RX_DESC 16 #define MAX_RX_DESC_MASK 0xf +/*napi_weight have better more than rx DMA buffers*/ +#define NAPI_WEIGHT 64 + #define MAX_RXBUF_SIZE 0x700 #define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC) @@ -120,6 +125,7 @@ enum ks8695_dtype { * @dev: The platform device object for this interface * @dtype: The type of this device * @io_regs: The ioremapped registers for this interface + * @napi : Add support NAPI for Rx * @rx_irq_name: The textual name of the RX IRQ from the platform data * @tx_irq_name: The textual name of the TX IRQ from the platform data * @link_irq_name: The textual name of the link IRQ from the @@ -143,6 +149,7 @@ enum ks8695_dtype { * @rx_ring_dma: The DMA mapped equivalent of rx_ring * @rx_buffers: The sk_buff mappings for the RX ring * @next_rx_desc_read: The next RX descriptor to read from on IRQ + * @rx_lock: A lock to protect Rx irq function * @msg_enable: The flags for which messages to emit */ struct ks8695_priv { @@ -152,6 +159,8 @@ struct ks8695_priv { enum ks8695_dtype dtype; void __iomem *io_regs; + struct napi_struct napi; + const char *rx_irq_name, *tx_irq_name, *link_irq_name; int rx_irq, tx_irq, link_irq; @@ -172,6 +181,7 @@ struct ks8695_priv { dma_addr_t rx_ring_dma; struct ks8695_skbuff rx_buffers[MAX_RX_DESC]; int next_rx_desc_read; + spinlock_t rx_lock; int msg_enable; }; @@ -392,29 +402,74 @@ ks8695_tx_irq(int irq, void *dev_id) } /** + * ks8695_get_rx_enable_bit - Get rx interrupt enable/status bit + * @ksp: Private data for the KS8695 Ethernet + * + * For KS8695 document: + * Interrupt Enable Register (offset 0xE204) + * Bit29 : WAN MAC Receive Interrupt Enable + * Bit16 : LAN MAC Receive Interrupt Enable + * Interrupt Status Register (Offset 0xF208) + * Bit29: WAN MAC Receive Status + * Bit16: LAN MAC Receive Status + * So, this Rx interrrupt enable/status bit number is equal + * as Rx IRQ number. + */ +static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp) +{ + return ksp->rx_irq; +} + +/** * ks8695_rx_irq - Receive IRQ handler * @irq: The IRQ which went off (ignored) * @dev_id: The net_device for the interrupt * - * Process the RX ring, passing any received packets up to the - * host. If we received anything other than errors, we then - * refill the ring. + * Inform NAPI that packet reception needs to be scheduled */ + static irqreturn_t ks8695_rx_irq(int irq, void *dev_id) { struct net_device *ndev = (struct net_device *)dev_id; struct ks8695_priv *ksp = netdev_priv(ndev); + + spin_lock(&ksp->rx_lock); + + if (napi_schedule_prep(&ksp->napi)) { + unsigned long status = readl(KS8695_IRQ_VA + KS8695_INTEN); + unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp); + /*disable rx interrupt*/ + status &= ~mask_bit; + writel(status , KS8695_IRQ_VA + KS8695_INTEN); + __napi_schedule(&ksp->napi); + } + + spin_unlock(&ksp->rx_lock); + return IRQ_HANDLED; +} + +/** + * ks8695_rx - Receive packets called by NAPI poll method + * @ksp: Private data for the KS8695 Ethernet + * @budget: The max packets would be receive + */ + +static int ks8695_rx(struct ks8695_priv *ksp, int budget) +{ + struct net_device *ndev = ksp->ndev; struct sk_buff *skb; int buff_n; u32 flags; int pktlen; int last_rx_processed = -1; + int received = 0; buff_n = ksp->next_rx_desc_read; - do { - if (ksp->rx_buffers[buff_n].skb && - !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) { + while (received < budget + && ksp->rx_buffers[buff_n].skb + && (!(ksp->rx_ring[buff_n].status & + cpu_to_le32(RDES_OWN)))) { rmb(); flags = le32_to_cpu(ksp->rx_ring[buff_n].status); /* Found an SKB which we own, this means we @@ -464,7 +519,7 @@ ks8695_rx_irq(int irq, void *dev_id) /* Relinquish the SKB to the network layer */ skb_put(skb, pktlen); skb->protocol = eth_type_trans(skb, ndev); - netif_rx(skb); + netif_receive_skb(skb); /* Record stats */ ndev->stats.rx_packets++; @@ -478,29 +533,55 @@ rx_failure: /* Give the ring entry back to the hardware */ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN); rx_finished: + received++; /* And note this as processed so we can start * from here next time */ last_rx_processed = buff_n; - } else { - /* Ran out of things to process, stop now */ - break; - } - buff_n = (buff_n + 1) & MAX_RX_DESC_MASK; - } while (buff_n != ksp->next_rx_desc_read); - - /* And note which RX descriptor we last did anything with */ - if (likely(last_rx_processed != -1)) - ksp->next_rx_desc_read = - (last_rx_processed + 1) & MAX_RX_DESC_MASK; - + buff_n = (buff_n + 1) & MAX_RX_DESC_MASK; + /*And note which RX descriptor we last did */ + if (likely(last_rx_processed != -1)) + ksp->next_rx_desc_read = + (last_rx_processed + 1) & + MAX_RX_DESC_MASK; + } /* And refill the buffers */ ks8695_refill_rxbuffers(ksp); - /* Kick the RX DMA engine, in case it became suspended */ + /* Kick the RX DMA engine, in case it became + * suspended */ ks8695_writereg(ksp, KS8695_DRSC, 0); + return received; +} - return IRQ_HANDLED; + +/** + * ks8695_poll - Receive packet by NAPI poll method + * @ksp: Private data for the KS8695 Ethernet + * @budget: The remaining number packets for network subsystem + * + * Invoked by the network core when it requests for new + * packets from the driver + */ +static int ks8695_poll(struct napi_struct *napi, int budget) +{ + struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi); + unsigned long work_done; + + unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN); + unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp); + + work_done = ks8695_rx(ksp, budget); + + if (work_done < budget) { + unsigned long flags; + spin_lock_irqsave(&ksp->rx_lock, flags); + /*enable rx interrupt*/ + writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN); + __napi_complete(napi); + spin_unlock_irqrestore(&ksp->rx_lock, flags); + } + return work_done; } /** @@ -1253,6 +1334,7 @@ ks8695_stop(struct net_device *ndev) struct ks8695_priv *ksp = netdev_priv(ndev); netif_stop_queue(ndev); + napi_disable(&ksp->napi); netif_carrier_off(ndev); ks8695_shutdown(ksp); @@ -1287,6 +1369,7 @@ ks8695_open(struct net_device *ndev) return ret; } + napi_enable(&ksp->napi); netif_start_queue(ndev); return 0; @@ -1472,6 +1555,8 @@ ks8695_probe(struct platform_device *pdev) SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops); ndev->watchdog_timeo = msecs_to_jiffies(watchdog); + netif_napi_add(ndev, &ksp->napi, ks8695_poll, NAPI_WEIGHT); + /* Retrieve the default MAC addr from the chip. */ /* The bootloader should have left it in there for us. */ @@ -1505,6 +1590,7 @@ ks8695_probe(struct platform_device *pdev) /* And initialise the queue's lock */ spin_lock_init(&ksp->txq_lock); + spin_lock_init(&ksp->rx_lock); /* Specify the RX DMA ring buffer */ ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE; @@ -1626,6 +1712,7 @@ ks8695_drv_remove(struct platform_device *pdev) struct ks8695_priv *ksp = netdev_priv(ndev); platform_set_drvdata(pdev, NULL); + netif_napi_del(&ksp->napi); unregister_netdev(ndev); ks8695_release_device(ksp); diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/arm/w90p910_ether.c index 25e2627eb118..b7f3866d546f 100644 --- a/drivers/net/arm/w90p910_ether.c +++ b/drivers/net/arm/w90p910_ether.c @@ -160,8 +160,8 @@ struct w90p910_ether { struct mii_if_info mii; struct timer_list check_timer; void __iomem *reg; - unsigned int rxirq; - unsigned int txirq; + int rxirq; + int txirq; unsigned int cur_tx; unsigned int cur_rx; unsigned int finish_tx; diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c index 544d5af6950e..b14f4799d5d1 100644 --- a/drivers/net/at1700.c +++ b/drivers/net/at1700.c @@ -350,13 +350,13 @@ static int __init at1700_probe1(struct net_device *dev, int ioaddr) slot = -1; /* We must check for the EEPROM-config boards first, else accessing IOCONFIG0 will move the board! */ - if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr - && read_eeprom(ioaddr, 4) == 0x0000 - && (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400) + if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr && + read_eeprom(ioaddr, 4) == 0x0000 && + (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400) is_at1700 = 1; - else if (inb(ioaddr + SAPROM ) == 0x00 - && inb(ioaddr + SAPROM + 1) == 0x00 - && inb(ioaddr + SAPROM + 2) == 0x0e) + else if (inb(ioaddr + SAPROM ) == 0x00 && + inb(ioaddr + SAPROM + 1) == 0x00 && + inb(ioaddr + SAPROM + 2) == 0x0e) is_fmv18x = 1; else { goto err_out; @@ -468,7 +468,7 @@ found: lp->jumpered = is_fmv18x; lp->mca_slot = slot; /* Snarf the interrupt vector now. */ - ret = request_irq(irq, &net_interrupt, 0, DRV_NAME, dev); + ret = request_irq(irq, net_interrupt, 0, DRV_NAME, dev); if (ret) { printk(KERN_ERR "AT1700 at %#3x is unusable due to a " "conflict on IRQ %d.\n", @@ -839,8 +839,8 @@ set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) { memset(mc_filter, 0xff, sizeof(mc_filter)); outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ - } else if (dev->mc_count > MC_FILTERBREAK - || (dev->flags & IFF_ALLMULTI)) { + } else if (dev->mc_count > MC_FILTERBREAK || + (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); outb(2, ioaddr + RX_MODE); /* Use normal mode. */ diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c index 0c0deceb6938..c5721cb38265 100644 --- a/drivers/net/atarilance.c +++ b/drivers/net/atarilance.c @@ -930,8 +930,8 @@ static irqreturn_t lance_interrupt( int irq, void *dev_id ) } #endif - if (lp->tx_full && (netif_queue_stopped(dev)) - && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { + if (lp->tx_full && (netif_queue_stopped(dev)) && + dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { /* The ring is no longer full, clear tbusy. */ lp->tx_full = 0; netif_wake_queue (dev); diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h index 2a1120ad2e74..a348a22551d9 100644 --- a/drivers/net/atl1c/atl1c.h +++ b/drivers/net/atl1c/atl1c.h @@ -470,12 +470,28 @@ struct atl1c_ring_header { struct atl1c_buffer { struct sk_buff *skb; /* socket buffer */ u16 length; /* rx buffer length */ - u16 state; /* state of buffer */ -#define ATL1_BUFFER_FREE 0 -#define ATL1_BUFFER_BUSY 1 + u16 flags; /* information of buffer */ +#define ATL1C_BUFFER_FREE 0x0001 +#define ATL1C_BUFFER_BUSY 0x0002 +#define ATL1C_BUFFER_STATE_MASK 0x0003 + +#define ATL1C_PCIMAP_SINGLE 0x0004 +#define ATL1C_PCIMAP_PAGE 0x0008 +#define ATL1C_PCIMAP_TYPE_MASK 0x000C + dma_addr_t dma; }; +#define ATL1C_SET_BUFFER_STATE(buff, state) do { \ + ((buff)->flags) &= ~ATL1C_BUFFER_STATE_MASK; \ + ((buff)->flags) |= (state); \ + } while (0) + +#define ATL1C_SET_PCIMAP_TYPE(buff, type) do { \ + ((buff)->flags) &= ~ATL1C_PCIMAP_TYPE_MASK; \ + ((buff)->flags) |= (type); \ + } while (0) + /* transimit packet descriptor (tpd) ring */ struct atl1c_tpd_ring { void *desc; /* descriptor ring virtual address */ diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index 1372e9a99f5b..6eb9241cee0a 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -710,6 +710,29 @@ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter) return 0; } +static inline void atl1c_clean_buffer(struct pci_dev *pdev, + struct atl1c_buffer *buffer_info, int in_irq) +{ + if (buffer_info->flags & ATL1C_BUFFER_FREE) + return; + if (buffer_info->dma) { + if (buffer_info->flags & ATL1C_PCIMAP_SINGLE) + pci_unmap_single(pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + else if (buffer_info->flags & ATL1C_PCIMAP_PAGE) + pci_unmap_page(pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + } + if (buffer_info->skb) { + if (in_irq) + dev_kfree_skb_irq(buffer_info->skb); + else + dev_kfree_skb(buffer_info->skb); + } + buffer_info->dma = 0; + buffer_info->skb = NULL; + ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE); +} /* * atl1c_clean_tx_ring - Free Tx-skb * @adapter: board private structure @@ -725,22 +748,12 @@ static void atl1c_clean_tx_ring(struct atl1c_adapter *adapter, ring_count = tpd_ring->count; for (index = 0; index < ring_count; index++) { buffer_info = &tpd_ring->buffer_info[index]; - if (buffer_info->state == ATL1_BUFFER_FREE) - continue; - if (buffer_info->dma) - pci_unmap_single(pdev, buffer_info->dma, - buffer_info->length, - PCI_DMA_TODEVICE); - if (buffer_info->skb) - dev_kfree_skb(buffer_info->skb); - buffer_info->dma = 0; - buffer_info->skb = NULL; - buffer_info->state = ATL1_BUFFER_FREE; + atl1c_clean_buffer(pdev, buffer_info, 0); } /* Zero out Tx-buffers */ memset(tpd_ring->desc, 0, sizeof(struct atl1c_tpd_desc) * - ring_count); + ring_count); atomic_set(&tpd_ring->next_to_clean, 0); tpd_ring->next_to_use = 0; } @@ -760,16 +773,7 @@ static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter) for (i = 0; i < adapter->num_rx_queues; i++) { for (j = 0; j < rfd_ring[i].count; j++) { buffer_info = &rfd_ring[i].buffer_info[j]; - if (buffer_info->state == ATL1_BUFFER_FREE) - continue; - if (buffer_info->dma) - pci_unmap_single(pdev, buffer_info->dma, - buffer_info->length, - PCI_DMA_FROMDEVICE); - if (buffer_info->skb) - dev_kfree_skb(buffer_info->skb); - buffer_info->state = ATL1_BUFFER_FREE; - buffer_info->skb = NULL; + atl1c_clean_buffer(pdev, buffer_info, 0); } /* zero out the descriptor ring */ memset(rfd_ring[i].desc, 0, rfd_ring[i].size); @@ -796,7 +800,8 @@ static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter) atomic_set(&tpd_ring[i].next_to_clean, 0); buffer_info = tpd_ring[i].buffer_info; for (j = 0; j < tpd_ring->count; j++) - buffer_info[i].state = ATL1_BUFFER_FREE; + ATL1C_SET_BUFFER_STATE(&buffer_info[i], + ATL1C_BUFFER_FREE); } for (i = 0; i < adapter->num_rx_queues; i++) { rfd_ring[i].next_to_use = 0; @@ -805,7 +810,7 @@ static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter) rrd_ring[i].next_to_clean = 0; for (j = 0; j < rfd_ring[i].count; j++) { buffer_info = &rfd_ring[i].buffer_info[j]; - buffer_info->state = ATL1_BUFFER_FREE; + ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE); } } } @@ -1447,6 +1452,7 @@ static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter, struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *) &adapter->tpd_ring[type]; struct atl1c_buffer *buffer_info; + struct pci_dev *pdev = adapter->pdev; u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); u16 hw_next_to_clean; u16 shift; @@ -1462,16 +1468,7 @@ static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter, while (next_to_clean != hw_next_to_clean) { buffer_info = &tpd_ring->buffer_info[next_to_clean]; - if (buffer_info->state == ATL1_BUFFER_BUSY) { - pci_unmap_page(adapter->pdev, buffer_info->dma, - buffer_info->length, PCI_DMA_TODEVICE); - buffer_info->dma = 0; - if (buffer_info->skb) { - dev_kfree_skb_irq(buffer_info->skb); - buffer_info->skb = NULL; - } - buffer_info->state = ATL1_BUFFER_FREE; - } + atl1c_clean_buffer(pdev, buffer_info, 1); if (++next_to_clean == tpd_ring->count) next_to_clean = 0; atomic_set(&tpd_ring->next_to_clean, next_to_clean); @@ -1543,7 +1540,7 @@ static irqreturn_t atl1c_intr(int irq, void *data) if (status & ISR_OVER) if (netif_msg_intr(adapter)) dev_warn(&pdev->dev, - "TX/RX over flow (status = 0x%x)\n", + "TX/RX overflow (status = 0x%x)\n", status & ISR_OVER); /* link event */ @@ -1587,7 +1584,7 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; next_info = &rfd_ring->buffer_info[next_next]; - while (next_info->state == ATL1_BUFFER_FREE) { + while (next_info->flags & ATL1C_BUFFER_FREE) { rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use); skb = dev_alloc_skb(adapter->rx_buffer_len); @@ -1603,12 +1600,13 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid * the 14 byte MAC header is removed */ vir_addr = skb->data; - buffer_info->state = ATL1_BUFFER_BUSY; + ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); buffer_info->skb = skb; buffer_info->length = adapter->rx_buffer_len; buffer_info->dma = pci_map_single(pdev, vir_addr, buffer_info->length, PCI_DMA_FROMDEVICE); + ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE); rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); rfd_next_to_use = next_next; if (++next_next == rfd_ring->count) @@ -1653,7 +1651,8 @@ static void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring, RRS_RX_RFD_INDEX_MASK; for (i = 0; i < num; i++) { buffer_info[rfd_index].skb = NULL; - buffer_info[rfd_index].state = ATL1_BUFFER_FREE; + ATL1C_SET_BUFFER_STATE(&buffer_info[rfd_index], + ATL1C_BUFFER_FREE); if (++rfd_index == rfd_ring->count) rfd_index = 0; } @@ -1967,7 +1966,8 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter, buffer_info->length = map_len; buffer_info->dma = pci_map_single(adapter->pdev, skb->data, hdr_len, PCI_DMA_TODEVICE); - buffer_info->state = ATL1_BUFFER_BUSY; + ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); + ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE); mapped_len += map_len; use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); @@ -1981,16 +1981,14 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter, else { use_tpd = atl1c_get_tpd(adapter, type); memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); - use_tpd = atl1c_get_tpd(adapter, type); - memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); } buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); buffer_info->length = buf_len - mapped_len; buffer_info->dma = pci_map_single(adapter->pdev, skb->data + mapped_len, buffer_info->length, PCI_DMA_TODEVICE); - buffer_info->state = ATL1_BUFFER_BUSY; - + ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); + ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE); use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); } @@ -2010,8 +2008,8 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter, frag->page_offset, buffer_info->length, PCI_DMA_TODEVICE); - buffer_info->state = ATL1_BUFFER_BUSY; - + ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); + ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE); use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); } @@ -2137,7 +2135,7 @@ static int atl1c_request_irq(struct atl1c_adapter *adapter) if (!adapter->have_msi) flags |= IRQF_SHARED; - err = request_irq(adapter->pdev->irq, &atl1c_intr, flags, + err = request_irq(adapter->pdev->irq, atl1c_intr, flags, netdev->name, netdev); if (err) { if (netif_msg_ifup(adapter)) diff --git a/drivers/net/atl1e/atl1e_ethtool.c b/drivers/net/atl1e/atl1e_ethtool.c index 60edb9f232bb..a76006c1bc6b 100644 --- a/drivers/net/atl1e/atl1e_ethtool.c +++ b/drivers/net/atl1e/atl1e_ethtool.c @@ -131,11 +131,6 @@ static int atl1e_set_settings(struct net_device *netdev, return 0; } -static u32 atl1e_get_tx_csum(struct net_device *netdev) -{ - return (netdev->features & NETIF_F_HW_CSUM) != 0; -} - static u32 atl1e_get_msglevel(struct net_device *netdev) { #ifdef DBG @@ -145,10 +140,6 @@ static u32 atl1e_get_msglevel(struct net_device *netdev) #endif } -static void atl1e_set_msglevel(struct net_device *netdev, u32 data) -{ -} - static int atl1e_get_regs_len(struct net_device *netdev) { return AT_REGS_LEN * sizeof(u32); @@ -387,18 +378,14 @@ static const struct ethtool_ops atl1e_ethtool_ops = { .get_wol = atl1e_get_wol, .set_wol = atl1e_set_wol, .get_msglevel = atl1e_get_msglevel, - .set_msglevel = atl1e_set_msglevel, .nway_reset = atl1e_nway_reset, .get_link = ethtool_op_get_link, .get_eeprom_len = atl1e_get_eeprom_len, .get_eeprom = atl1e_get_eeprom, .set_eeprom = atl1e_set_eeprom, - .get_tx_csum = atl1e_get_tx_csum, - .get_sg = ethtool_op_get_sg, + .set_tx_csum = ethtool_op_set_tx_hw_csum, .set_sg = ethtool_op_set_sg, -#ifdef NETIF_F_TSO - .get_tso = ethtool_op_get_tso, -#endif + .set_tso = ethtool_op_set_tso, }; void atl1e_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c index 955da733c2ad..08f8c0969e9b 100644 --- a/drivers/net/atl1e/atl1e_main.c +++ b/drivers/net/atl1e/atl1e_main.c @@ -1433,14 +1433,12 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que, packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) & RRS_PKT_SIZE_MASK) - 4; /* CRC */ - skb = netdev_alloc_skb(netdev, - packet_size + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(netdev, packet_size); if (skb == NULL) { dev_warn(&pdev->dev, "%s: Memory squeeze," "deferring packet.\n", netdev->name); goto skip_pkt; } - skb_reserve(skb, NET_IP_ALIGN); skb->dev = netdev; memcpy(skb->data, (u8 *)(prrs + 1), packet_size); skb_put(skb, packet_size); @@ -1666,41 +1664,6 @@ static int atl1e_tso_csum(struct atl1e_adapter *adapter, } return 0; } - - if (offload_type & SKB_GSO_TCPV6) { - real_len = (((unsigned char *)ipv6_hdr(skb) - skb->data) - + ntohs(ipv6_hdr(skb)->payload_len)); - if (real_len < skb->len) - pskb_trim(skb, real_len); - - /* check payload == 0 byte ? */ - hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); - if (unlikely(skb->len == hdr_len)) { - /* only xsum need */ - dev_warn(&pdev->dev, - "IPV6 tso with zero data??\n"); - goto check_sum; - } else { - tcp_hdr(skb)->check = ~csum_ipv6_magic( - &ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - 0, IPPROTO_TCP, 0); - tpd->word3 |= 1 << TPD_IP_VERSION_SHIFT; - hdr_len >>= 1; - tpd->word3 |= (hdr_len & TPD_V6_IPHLLO_MASK) << - TPD_V6_IPHLLO_SHIFT; - tpd->word3 |= ((hdr_len >> 3) & - TPD_V6_IPHLHI_MASK) << - TPD_V6_IPHLHI_SHIFT; - tpd->word3 |= (tcp_hdrlen(skb) >> 2 & - TPD_TCPHDRLEN_MASK) << - TPD_TCPHDRLEN_SHIFT; - tpd->word3 |= ((skb_shinfo(skb)->gso_size) & - TPD_MSS_MASK) << TPD_MSS_SHIFT; - tpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT; - } - } - return 0; } check_sum: @@ -1932,7 +1895,7 @@ static int atl1e_request_irq(struct atl1e_adapter *adapter) if (!adapter->have_msi) flags |= IRQF_SHARED; - err = request_irq(adapter->pdev->irq, &atl1e_intr, flags, + err = request_irq(adapter->pdev->irq, atl1e_intr, flags, netdev->name, netdev); if (err) { dev_dbg(&pdev->dev, @@ -2289,7 +2252,6 @@ static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev) NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; netdev->features |= NETIF_F_LLTX; netdev->features |= NETIF_F_TSO; - netdev->features |= NETIF_F_TSO6; return 0; } diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c index 00569dc1313c..b6cf3263127c 100644 --- a/drivers/net/atlx/atl1.c +++ b/drivers/net/atlx/atl1.c @@ -1344,8 +1344,8 @@ static u32 atl1_check_link(struct atl1_adapter *adapter) /* link result is our setting */ if (!reconfig) { - if (adapter->link_speed != speed - || adapter->link_duplex != duplex) { + if (adapter->link_speed != speed || + adapter->link_duplex != duplex) { adapter->link_speed = speed; adapter->link_duplex = duplex; atl1_setup_mac_ctrl(adapter); @@ -1864,21 +1864,14 @@ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) rfd_desc = ATL1_RFD_DESC(rfd_ring, rfd_next_to_use); - skb = netdev_alloc_skb(adapter->netdev, - adapter->rx_buffer_len + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(adapter->netdev, + adapter->rx_buffer_len); if (unlikely(!skb)) { /* Better luck next round */ adapter->netdev->stats.rx_dropped++; break; } - /* - * Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->alloced = 1; buffer_info->skb = skb; buffer_info->length = (u16) adapter->rx_buffer_len; @@ -2094,8 +2087,8 @@ static void atl1_intr_tx(struct atl1_adapter *adapter) } atomic_set(&tpd_ring->next_to_clean, sw_tpd_next_to_clean); - if (netif_queue_stopped(adapter->netdev) - && netif_carrier_ok(adapter->netdev)) + if (netif_queue_stopped(adapter->netdev) && + netif_carrier_ok(adapter->netdev)) netif_wake_queue(adapter->netdev); } @@ -2596,7 +2589,7 @@ static s32 atl1_up(struct atl1_adapter *adapter) irq_flags |= IRQF_SHARED; } - err = request_irq(adapter->pdev->irq, &atl1_intr, irq_flags, + err = request_irq(adapter->pdev->irq, atl1_intr, irq_flags, netdev->name, netdev); if (unlikely(err)) goto err_up; diff --git a/drivers/net/atlx/atl2.c b/drivers/net/atlx/atl2.c index ab688862093f..c0451d75cdcf 100644 --- a/drivers/net/atlx/atl2.c +++ b/drivers/net/atlx/atl2.c @@ -409,7 +409,7 @@ static void atl2_intr_rx(struct atl2_adapter *adapter) if (rxd->status.ok && rxd->status.pkt_size >= 60) { int rx_size = (int)(rxd->status.pkt_size - 4); /* alloc new buffer */ - skb = netdev_alloc_skb(netdev, rx_size + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(netdev, rx_size); if (NULL == skb) { printk(KERN_WARNING "%s: Mem squeeze, deferring packet.\n", @@ -421,7 +421,6 @@ static void atl2_intr_rx(struct atl2_adapter *adapter) netdev->stats.rx_dropped++; break; } - skb_reserve(skb, NET_IP_ALIGN); skb->dev = netdev; memcpy(skb->data, rxd->packet, rx_size); skb_put(skb, rx_size); @@ -652,7 +651,7 @@ static int atl2_request_irq(struct atl2_adapter *adapter) if (adapter->have_msi) flags &= ~IRQF_SHARED; - return request_irq(adapter->pdev->irq, &atl2_intr, flags, netdev->name, + return request_irq(adapter->pdev->irq, atl2_intr, flags, netdev->name, netdev); } diff --git a/drivers/net/atp.c b/drivers/net/atp.c index 9043294fe617..2f8261c9614a 100644 --- a/drivers/net/atp.c +++ b/drivers/net/atp.c @@ -437,7 +437,7 @@ static int net_open(struct net_device *dev) /* The interrupt line is turned off (tri-stated) when the device isn't in use. That's especially important for "attached" interfaces where the port or interrupt may be shared. */ - ret = request_irq(dev->irq, &atp_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, atp_interrupt, 0, dev->name, dev); if (ret) return ret; @@ -673,8 +673,8 @@ static irqreturn_t atp_interrupt(int irq, void *dev_instance) netif_wake_queue(dev); /* Inform upper layers. */ } num_tx_since_rx++; - } else if (num_tx_since_rx > 8 - && time_after(jiffies, dev->last_rx + HZ)) { + } else if (num_tx_since_rx > 8 && + time_after(jiffies, dev->last_rx + HZ)) { if (net_debug > 2) printk(KERN_DEBUG "%s: Missed packet? No Rx after %d Tx and " "%ld jiffies status %02x CMR1 %02x.\n", dev->name, diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 3f4b4300f533..6bac04603a88 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -881,7 +881,7 @@ static int au1000_open(struct net_device *dev) if (au1000_debug > 4) printk("%s: open: dev=%p\n", dev->name, dev); - if ((retval = request_irq(dev->irq, &au1000_interrupt, 0, + if ((retval = request_irq(dev->irq, au1000_interrupt, 0, dev->name, dev))) { printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); diff --git a/drivers/net/bcm63xx_enet.c b/drivers/net/bcm63xx_enet.c index ba29dc319b34..1f6c5486d715 100644 --- a/drivers/net/bcm63xx_enet.c +++ b/drivers/net/bcm63xx_enet.c @@ -320,16 +320,13 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) if (len < copybreak) { struct sk_buff *nskb; - nskb = netdev_alloc_skb(dev, len + NET_IP_ALIGN); + nskb = netdev_alloc_skb_ip_align(dev, len); if (!nskb) { /* forget packet, just rearm desc */ priv->stats.rx_dropped++; continue; } - /* since we're copying the data, we can align - * them properly */ - skb_reserve(nskb, NET_IP_ALIGN); dma_sync_single_for_cpu(kdev, desc->address, len, DMA_FROM_DEVICE); memcpy(nskb->data, skb->data, len); diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h index 3b79a225628a..9e56014d27ed 100644 --- a/drivers/net/benet/be.h +++ b/drivers/net/benet/be.h @@ -32,23 +32,34 @@ #include "be_hw.h" -#define DRV_VER "2.101.205" +#define DRV_VER "2.101.346u" #define DRV_NAME "be2net" #define BE_NAME "ServerEngines BladeEngine2 10Gbps NIC" +#define BE3_NAME "ServerEngines BladeEngine3 10Gbps NIC" #define OC_NAME "Emulex OneConnect 10Gbps NIC" +#define OC_NAME1 "Emulex OneConnect 10Gbps NIC (be3)" #define DRV_DESC BE_NAME "Driver" #define BE_VENDOR_ID 0x19a2 #define BE_DEVICE_ID1 0x211 +#define BE_DEVICE_ID2 0x221 #define OC_DEVICE_ID1 0x700 #define OC_DEVICE_ID2 0x701 +#define OC_DEVICE_ID3 0x710 static inline char *nic_name(struct pci_dev *pdev) { - if (pdev->device == OC_DEVICE_ID1 || pdev->device == OC_DEVICE_ID2) + switch (pdev->device) { + case OC_DEVICE_ID1: + case OC_DEVICE_ID2: return OC_NAME; - else + case OC_DEVICE_ID3: + return OC_NAME1; + case BE_DEVICE_ID2: + return BE3_NAME; + default: return BE_NAME; + } } /* Number of bytes of an RX frame that are copied to skb->data */ @@ -159,7 +170,7 @@ struct be_drvr_stats { u32 cache_barrier[16]; u32 be_ethrx_post_fail;/* number of ethrx buffer alloc failures */ - u32 be_polls; /* number of times NAPI called poll function */ + u32 be_rx_polls; /* number of times NAPI called poll function */ u32 be_rx_events; /* number of ucast rx completion events */ u32 be_rx_compl; /* number of rx completion entries processed */ ulong be_rx_jiffies; @@ -181,7 +192,6 @@ struct be_drvr_stats { struct be_stats_obj { struct be_drvr_stats drvr_stats; - struct net_device_stats net_stats; struct be_dma_mem cmd; }; @@ -244,6 +254,7 @@ struct be_adapter { struct vlan_group *vlan_grp; u16 num_vlans; u8 vlan_tag[VLAN_GROUP_ARRAY_LEN]; + struct be_dma_mem mc_cmd_mem; struct be_stats_obj stats; /* Work queue used to perform periodic tasks like getting statistics */ @@ -258,9 +269,12 @@ struct be_adapter { bool link_up; u32 port_num; bool promiscuous; + bool wol; u32 cap; u32 rx_fc; /* Rx flow control */ u32 tx_fc; /* Tx flow control */ + int link_speed; + u8 port_type; }; extern const struct ethtool_ops be_ethtool_ops; diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index 28a0eda92680..1b68bd98dc0c 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -71,8 +71,8 @@ static int be_mcc_compl_process(struct be_adapter *adapter, extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) & CQE_STATUS_EXTD_MASK; dev_warn(&adapter->pdev->dev, - "Error in cmd completion: status(compl/extd)=%d/%d\n", - compl_status, extd_status); + "Error in cmd completion - opcode %d, compl %d, extd %d\n", + compl->tag0, compl_status, extd_status); } return compl_status; } @@ -277,7 +277,7 @@ static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb) /* Don't touch the hdr after it's prepared */ static void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, - bool embedded, u8 sge_cnt) + bool embedded, u8 sge_cnt, u32 opcode) { if (embedded) wrb->embedded |= MCC_WRB_EMBEDDED_MASK; @@ -285,6 +285,7 @@ static void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, wrb->embedded |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) << MCC_WRB_SGE_CNT_SHIFT; wrb->payload_length = payload_len; + wrb->tag0 = opcode; be_dws_cpu_to_le(wrb, 20); } @@ -349,7 +350,11 @@ static struct be_mcc_wrb *wrb_from_mccq(struct be_adapter *adapter) struct be_queue_info *mccq = &adapter->mcc_obj.q; struct be_mcc_wrb *wrb; - BUG_ON(atomic_read(&mccq->used) >= mccq->len); + if (atomic_read(&mccq->used) >= mccq->len) { + dev_err(&adapter->pdev->dev, "Out of MCCQ wrbs\n"); + return NULL; + } + wrb = queue_head_node(mccq); queue_head_inc(mccq); atomic_inc(&mccq->used); @@ -357,6 +362,57 @@ static struct be_mcc_wrb *wrb_from_mccq(struct be_adapter *adapter) return wrb; } +/* Tell fw we're about to start firing cmds by writing a + * special pattern across the wrb hdr; uses mbox + */ +int be_cmd_fw_init(struct be_adapter *adapter) +{ + u8 *wrb; + int status; + + spin_lock(&adapter->mbox_lock); + + wrb = (u8 *)wrb_from_mbox(adapter); + *wrb++ = 0xFF; + *wrb++ = 0x12; + *wrb++ = 0x34; + *wrb++ = 0xFF; + *wrb++ = 0xFF; + *wrb++ = 0x56; + *wrb++ = 0x78; + *wrb = 0xFF; + + status = be_mbox_notify_wait(adapter); + + spin_unlock(&adapter->mbox_lock); + return status; +} + +/* Tell fw we're done with firing cmds by writing a + * special pattern across the wrb hdr; uses mbox + */ +int be_cmd_fw_clean(struct be_adapter *adapter) +{ + u8 *wrb; + int status; + + spin_lock(&adapter->mbox_lock); + + wrb = (u8 *)wrb_from_mbox(adapter); + *wrb++ = 0xFF; + *wrb++ = 0xAA; + *wrb++ = 0xBB; + *wrb++ = 0xFF; + *wrb++ = 0xFF; + *wrb++ = 0xCC; + *wrb++ = 0xDD; + *wrb = 0xFF; + + status = be_mbox_notify_wait(adapter); + + spin_unlock(&adapter->mbox_lock); + return status; +} int be_cmd_eq_create(struct be_adapter *adapter, struct be_queue_info *eq, int eq_delay) { @@ -370,7 +426,7 @@ int be_cmd_eq_create(struct be_adapter *adapter, wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, OPCODE_COMMON_EQ_CREATE); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_EQ_CREATE, sizeof(*req)); @@ -414,7 +470,8 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr, wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_NTWK_MAC_QUERY); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req)); @@ -448,9 +505,14 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr, spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_NTWK_PMAC_ADD); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req)); @@ -464,6 +526,7 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr, *pmac_id = le32_to_cpu(resp->pmac_id); } +err: spin_unlock_bh(&adapter->mcc_lock); return status; } @@ -478,9 +541,14 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, u32 pmac_id) spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_NTWK_PMAC_DEL); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_PMAC_DEL, sizeof(*req)); @@ -490,8 +558,8 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, u32 pmac_id) status = be_mcc_notify_wait(adapter); +err: spin_unlock_bh(&adapter->mcc_lock); - return status; } @@ -512,7 +580,8 @@ int be_cmd_cq_create(struct be_adapter *adapter, req = embedded_payload(wrb); ctxt = &req->context; - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_CQ_CREATE); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_CQ_CREATE, sizeof(*req)); @@ -569,7 +638,8 @@ int be_cmd_mccq_create(struct be_adapter *adapter, req = embedded_payload(wrb); ctxt = &req->context; - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_MCC_CREATE); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_MCC_CREATE, sizeof(*req)); @@ -613,7 +683,8 @@ int be_cmd_txq_create(struct be_adapter *adapter, req = embedded_payload(wrb); ctxt = &req->context; - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_ETH_TX_CREATE); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_TX_CREATE, sizeof(*req)); @@ -660,7 +731,8 @@ int be_cmd_rxq_create(struct be_adapter *adapter, wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_ETH_RX_CREATE); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_RX_CREATE, sizeof(*req)); @@ -701,8 +773,6 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q, wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - switch (queue_type) { case QTYPE_EQ: subsys = CMD_SUBSYSTEM_COMMON; @@ -727,6 +797,9 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q, default: BUG(); } + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, opcode); + be_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req)); req->id = cpu_to_le16(q->id); @@ -752,7 +825,8 @@ int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags, wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_NTWK_INTERFACE_CREATE); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req)); @@ -787,7 +861,8 @@ int be_cmd_if_destroy(struct be_adapter *adapter, u32 interface_id) wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_NTWK_INTERFACE_DESTROY); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_INTERFACE_DESTROY, sizeof(*req)); @@ -810,15 +885,20 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd) struct be_mcc_wrb *wrb; struct be_cmd_req_get_stats *req; struct be_sge *sge; + int status = 0; spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = nonemb_cmd->va; sge = nonembedded_sgl(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); - wrb->tag0 = OPCODE_ETH_GET_STATISTICS; + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1, + OPCODE_ETH_GET_STATISTICS); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_GET_STATISTICS, sizeof(*req)); @@ -828,13 +908,14 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd) be_mcc_notify(adapter); +err: spin_unlock_bh(&adapter->mcc_lock); - return 0; + return status; } /* Uses synchronous mcc */ int be_cmd_link_status_query(struct be_adapter *adapter, - bool *link_up) + bool *link_up, u8 *mac_speed, u16 *link_speed) { struct be_mcc_wrb *wrb; struct be_cmd_req_link_status *req; @@ -843,11 +924,16 @@ int be_cmd_link_status_query(struct be_adapter *adapter, spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = embedded_payload(wrb); *link_up = false; - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_NTWK_LINK_STATUS_QUERY); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_LINK_STATUS_QUERY, sizeof(*req)); @@ -855,10 +941,14 @@ int be_cmd_link_status_query(struct be_adapter *adapter, status = be_mcc_notify_wait(adapter); if (!status) { struct be_cmd_resp_link_status *resp = embedded_payload(wrb); - if (resp->mac_speed != PHY_LINK_SPEED_ZERO) + if (resp->mac_speed != PHY_LINK_SPEED_ZERO) { *link_up = true; + *link_speed = le16_to_cpu(resp->link_speed); + *mac_speed = resp->mac_speed; + } } +err: spin_unlock_bh(&adapter->mcc_lock); return status; } @@ -875,7 +965,8 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver) wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_GET_FW_VERSION); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_GET_FW_VERSION, sizeof(*req)); @@ -897,13 +988,19 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd) { struct be_mcc_wrb *wrb; struct be_cmd_req_modify_eq_delay *req; + int status = 0; spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_MODIFY_EQ_DELAY); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req)); @@ -915,8 +1012,9 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd) be_mcc_notify(adapter); +err: spin_unlock_bh(&adapter->mcc_lock); - return 0; + return status; } /* Uses sycnhronous mcc */ @@ -930,9 +1028,14 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array, spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_NTWK_VLAN_CONFIG); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req)); @@ -948,6 +1051,7 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array, status = be_mcc_notify_wait(adapter); +err: spin_unlock_bh(&adapter->mcc_lock); return status; } @@ -964,9 +1068,13 @@ int be_cmd_promiscuous_config(struct be_adapter *adapter, u8 port_num, bool en) spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, OPCODE_ETH_PROMISCUOUS); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_PROMISCUOUS, sizeof(*req)); @@ -978,6 +1086,7 @@ int be_cmd_promiscuous_config(struct be_adapter *adapter, u8 port_num, bool en) status = be_mcc_notify_wait(adapter); +err: spin_unlock_bh(&adapter->mcc_lock); return status; } @@ -987,24 +1096,35 @@ int be_cmd_promiscuous_config(struct be_adapter *adapter, u8 port_num, bool en) * (mc == NULL) => multicast promiscous */ int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id, - struct dev_mc_list *mc_list, u32 mc_count) + struct dev_mc_list *mc_list, u32 mc_count, + struct be_dma_mem *mem) { -#define BE_MAX_MC 32 /* set mcast promisc if > 32 */ struct be_mcc_wrb *wrb; - struct be_cmd_req_mcast_mac_config *req; + struct be_cmd_req_mcast_mac_config *req = mem->va; + struct be_sge *sge; + int status; spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); - req = embedded_payload(wrb); + if (!wrb) { + status = -EBUSY; + goto err; + } + sge = nonembedded_sgl(wrb); + memset(req, 0, sizeof(*req)); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1, + OPCODE_COMMON_NTWK_MULTICAST_SET); + sge->pa_hi = cpu_to_le32(upper_32_bits(mem->dma)); + sge->pa_lo = cpu_to_le32(mem->dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(mem->size); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_MULTICAST_SET, sizeof(*req)); req->interface_id = if_id; - if (mc_list && mc_count <= BE_MAX_MC) { + if (mc_list) { int i; struct dev_mc_list *mc; @@ -1016,11 +1136,11 @@ int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id, req->promiscuous = 1; } - be_mcc_notify_wait(adapter); + status = be_mcc_notify_wait(adapter); +err: spin_unlock_bh(&adapter->mcc_lock); - - return 0; + return status; } /* Uses synchrounous mcc */ @@ -1033,9 +1153,14 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc) spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_SET_FLOW_CONTROL); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req)); @@ -1045,6 +1170,7 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc) status = be_mcc_notify_wait(adapter); +err: spin_unlock_bh(&adapter->mcc_lock); return status; } @@ -1059,9 +1185,14 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc) spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_GET_FLOW_CONTROL); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req)); @@ -1074,6 +1205,7 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc) *rx_fc = le16_to_cpu(resp->rx_flow_control); } +err: spin_unlock_bh(&adapter->mcc_lock); return status; } @@ -1090,7 +1222,8 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num, u32 *cap) wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_QUERY_FIRMWARE_CONFIG); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req)); @@ -1118,7 +1251,8 @@ int be_cmd_reset_function(struct be_adapter *adapter) wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_FUNCTION_RESET); be_cmd_hdr_prepare(req, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_FUNCTION_RESET, sizeof(*req)); @@ -1129,6 +1263,113 @@ int be_cmd_reset_function(struct be_adapter *adapter) return status; } +/* Uses sync mcc */ +int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num, + u8 bcn, u8 sts, u8 state) +{ + struct be_mcc_wrb *wrb; + struct be_cmd_req_enable_disable_beacon *req; + int status; + + spin_lock_bh(&adapter->mcc_lock); + + wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } + req = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_ENABLE_DISABLE_BEACON); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_ENABLE_DISABLE_BEACON, sizeof(*req)); + + req->port_num = port_num; + req->beacon_state = state; + req->beacon_duration = bcn; + req->status_duration = sts; + + status = be_mcc_notify_wait(adapter); + +err: + spin_unlock_bh(&adapter->mcc_lock); + return status; +} + +/* Uses sync mcc */ +int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, u32 *state) +{ + struct be_mcc_wrb *wrb; + struct be_cmd_req_get_beacon_state *req; + int status; + + spin_lock_bh(&adapter->mcc_lock); + + wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } + req = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_COMMON_GET_BEACON_STATE); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req)); + + req->port_num = port_num; + + status = be_mcc_notify_wait(adapter); + if (!status) { + struct be_cmd_resp_get_beacon_state *resp = + embedded_payload(wrb); + *state = resp->beacon_state; + } + +err: + spin_unlock_bh(&adapter->mcc_lock); + return status; +} + +/* Uses sync mcc */ +int be_cmd_read_port_type(struct be_adapter *adapter, u32 port, + u8 *connector) +{ + struct be_mcc_wrb *wrb; + struct be_cmd_req_port_type *req; + int status; + + spin_lock_bh(&adapter->mcc_lock); + + wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } + req = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(struct be_cmd_resp_port_type), true, 0, + OPCODE_COMMON_READ_TRANSRECV_DATA); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_READ_TRANSRECV_DATA, sizeof(*req)); + + req->port = cpu_to_le32(port); + req->page_num = cpu_to_le32(TR_PAGE_A0); + status = be_mcc_notify_wait(adapter); + if (!status) { + struct be_cmd_resp_port_type *resp = embedded_payload(wrb); + *connector = resp->data.connector; + } + +err: + spin_unlock_bh(&adapter->mcc_lock); + return status; +} + int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, u32 flash_type, u32 flash_opcode, u32 buf_size) { @@ -1140,9 +1381,15 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } + req = cmd->va; sge = nonembedded_sgl(wrb); - be_wrb_hdr_prepare(wrb, cmd->size, false, 1); + be_wrb_hdr_prepare(wrb, cmd->size, false, 1, + OPCODE_COMMON_WRITE_FLASHROM); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_WRITE_FLASHROM, cmd->size); @@ -1156,6 +1403,171 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, status = be_mcc_notify_wait(adapter); +err: + spin_unlock_bh(&adapter->mcc_lock); + return status; +} + +int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc) +{ + struct be_mcc_wrb *wrb; + struct be_cmd_write_flashrom *req; + int status; + + spin_lock_bh(&adapter->mcc_lock); + + wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } + req = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*req)+4, true, 0, + OPCODE_COMMON_READ_FLASHROM); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_READ_FLASHROM, sizeof(*req)+4); + + req->params.op_type = cpu_to_le32(FLASHROM_TYPE_REDBOOT); + req->params.op_code = cpu_to_le32(FLASHROM_OPER_REPORT); + req->params.offset = 0x3FFFC; + req->params.data_buf_size = 0x4; + + status = be_mcc_notify_wait(adapter); + if (!status) + memcpy(flashed_crc, req->params.data_buf, 4); + +err: + spin_unlock_bh(&adapter->mcc_lock); + return status; +} + +extern int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac, + struct be_dma_mem *nonemb_cmd) +{ + struct be_mcc_wrb *wrb; + struct be_cmd_req_acpi_wol_magic_config *req; + struct be_sge *sge; + int status; + + spin_lock_bh(&adapter->mcc_lock); + + wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } + req = nonemb_cmd->va; + sge = nonembedded_sgl(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1, + OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, + OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req)); + memcpy(req->magic_mac, mac, ETH_ALEN); + + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd->size); + + status = be_mcc_notify_wait(adapter); + +err: + spin_unlock_bh(&adapter->mcc_lock); + return status; +} + +int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num, + u32 loopback_type, u32 pkt_size, u32 num_pkts, u64 pattern) +{ + struct be_mcc_wrb *wrb; + struct be_cmd_req_loopback_test *req; + int status; + + spin_lock_bh(&adapter->mcc_lock); + + wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } + + req = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, + OPCODE_LOWLEVEL_LOOPBACK_TEST); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL, + OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req)); + + req->pattern = cpu_to_le64(pattern); + req->src_port = cpu_to_le32(port_num); + req->dest_port = cpu_to_le32(port_num); + req->pkt_size = cpu_to_le32(pkt_size); + req->num_pkts = cpu_to_le32(num_pkts); + req->loopback_type = cpu_to_le32(loopback_type); + + status = be_mcc_notify_wait(adapter); + if (!status) { + struct be_cmd_resp_loopback_test *resp = embedded_payload(wrb); + status = le32_to_cpu(resp->status); + } + +err: + spin_unlock_bh(&adapter->mcc_lock); + return status; +} + +int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern, + u32 byte_cnt, struct be_dma_mem *cmd) +{ + struct be_mcc_wrb *wrb; + struct be_cmd_req_ddrdma_test *req; + struct be_sge *sge; + int status; + int i, j = 0; + + spin_lock_bh(&adapter->mcc_lock); + + wrb = wrb_from_mccq(adapter); + if (!wrb) { + status = -EBUSY; + goto err; + } + req = cmd->va; + sge = nonembedded_sgl(wrb); + be_wrb_hdr_prepare(wrb, cmd->size, false, 1, + OPCODE_LOWLEVEL_HOST_DDR_DMA); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL, + OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size); + + sge->pa_hi = cpu_to_le32(upper_32_bits(cmd->dma)); + sge->pa_lo = cpu_to_le32(cmd->dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(cmd->size); + + req->pattern = cpu_to_le64(pattern); + req->byte_count = cpu_to_le32(byte_cnt); + for (i = 0; i < byte_cnt; i++) { + req->snd_buff[i] = (u8)(pattern >> (j*8)); + j++; + if (j > 7) + j = 0; + } + + status = be_mcc_notify_wait(adapter); + + if (!status) { + struct be_cmd_resp_ddrdma_test *resp; + resp = cmd->va; + if ((memcmp(resp->rcv_buff, req->snd_buff, byte_cnt) != 0) || + resp->snd_err) { + status = -1; + } + } + +err: spin_unlock_bh(&adapter->mcc_lock); return status; } diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h index e5f9676cf1bc..92b87ef156ed 100644 --- a/drivers/net/benet/be_cmds.h +++ b/drivers/net/benet/be_cmds.h @@ -112,12 +112,14 @@ struct be_mcc_mailbox { #define CMD_SUBSYSTEM_COMMON 0x1 #define CMD_SUBSYSTEM_ETH 0x3 +#define CMD_SUBSYSTEM_LOWLEVEL 0xb #define OPCODE_COMMON_NTWK_MAC_QUERY 1 #define OPCODE_COMMON_NTWK_MAC_SET 2 #define OPCODE_COMMON_NTWK_MULTICAST_SET 3 #define OPCODE_COMMON_NTWK_VLAN_CONFIG 4 #define OPCODE_COMMON_NTWK_LINK_STATUS_QUERY 5 +#define OPCODE_COMMON_READ_FLASHROM 6 #define OPCODE_COMMON_WRITE_FLASHROM 7 #define OPCODE_COMMON_CQ_CREATE 12 #define OPCODE_COMMON_EQ_CREATE 13 @@ -138,6 +140,9 @@ struct be_mcc_mailbox { #define OPCODE_COMMON_NTWK_PMAC_ADD 59 #define OPCODE_COMMON_NTWK_PMAC_DEL 60 #define OPCODE_COMMON_FUNCTION_RESET 61 +#define OPCODE_COMMON_ENABLE_DISABLE_BEACON 69 +#define OPCODE_COMMON_GET_BEACON_STATE 70 +#define OPCODE_COMMON_READ_TRANSRECV_DATA 73 #define OPCODE_ETH_ACPI_CONFIG 2 #define OPCODE_ETH_PROMISCUOUS 3 @@ -146,6 +151,10 @@ struct be_mcc_mailbox { #define OPCODE_ETH_RX_CREATE 8 #define OPCODE_ETH_TX_DESTROY 9 #define OPCODE_ETH_RX_DESTROY 10 +#define OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG 12 + +#define OPCODE_LOWLEVEL_HOST_DDR_DMA 17 +#define OPCODE_LOWLEVEL_LOOPBACK_TEST 18 struct be_cmd_req_hdr { u8 opcode; /* dword 0 */ @@ -435,7 +444,7 @@ enum be_if_flags { * filtering capabilities. */ struct be_cmd_req_if_create { struct be_cmd_req_hdr hdr; - u32 version; /* ignore currntly */ + u32 version; /* ignore currently */ u32 capability_flags; u32 enable_flags; u8 mac_addr[ETH_ALEN]; @@ -587,6 +596,8 @@ struct be_cmd_req_promiscuous_config { u16 rsvd0; } __packed; +/******************** Multicast MAC Config *******************/ +#define BE_MAX_MC 64 /* set mcast promisc if > 64 */ struct macaddr { u8 byte[ETH_ALEN]; }; @@ -596,7 +607,7 @@ struct be_cmd_req_mcast_mac_config { u16 num_mac; u8 promiscuous; u8 interface_id; - struct macaddr mac[32]; + struct macaddr mac[BE_MAX_MC]; } __packed; static inline struct be_hw_stats * @@ -633,9 +644,47 @@ struct be_cmd_resp_link_status { u8 mac_fault; u8 mgmt_mac_duplex; u8 mgmt_mac_speed; - u16 rsvd0; + u16 link_speed; + u32 rsvd0; } __packed; +/******************** Port Identification ***************************/ +/* Identifies the type of port attached to NIC */ +struct be_cmd_req_port_type { + struct be_cmd_req_hdr hdr; + u32 page_num; + u32 port; +}; + +enum { + TR_PAGE_A0 = 0xa0, + TR_PAGE_A2 = 0xa2 +}; + +struct be_cmd_resp_port_type { + struct be_cmd_resp_hdr hdr; + u32 page_num; + u32 port; + struct data { + u8 identifier; + u8 identifier_ext; + u8 connector; + u8 transceiver[8]; + u8 rsvd0[3]; + u8 length_km; + u8 length_hm; + u8 length_om1; + u8 length_om2; + u8 length_cu; + u8 length_cu_m; + u8 vendor_name[16]; + u8 rsvd; + u8 vendor_oui[3]; + u8 vendor_pn[16]; + u8 vendor_rev[4]; + } data; +}; + /******************** Get FW Version *******************/ struct be_cmd_req_get_fw_version { struct be_cmd_req_hdr hdr; @@ -699,6 +748,37 @@ struct be_cmd_resp_query_fw_cfg { u32 rsvd[26]; }; +/******************** Port Beacon ***************************/ + +#define BEACON_STATE_ENABLED 0x1 +#define BEACON_STATE_DISABLED 0x0 + +struct be_cmd_req_enable_disable_beacon { + struct be_cmd_req_hdr hdr; + u8 port_num; + u8 beacon_state; + u8 beacon_duration; + u8 status_duration; +} __packed; + +struct be_cmd_resp_enable_disable_beacon { + struct be_cmd_resp_hdr resp_hdr; + u32 rsvd0; +} __packed; + +struct be_cmd_req_get_beacon_state { + struct be_cmd_req_hdr hdr; + u8 port_num; + u8 rsvd0; + u16 rsvd1; +} __packed; + +struct be_cmd_resp_get_beacon_state { + struct be_cmd_resp_hdr resp_hdr; + u8 beacon_state; + u8 rsvd0[3]; +} __packed; + /****************** Firmware Flash ******************/ struct flashrom_params { u32 op_code; @@ -713,6 +793,53 @@ struct be_cmd_write_flashrom { struct flashrom_params params; }; +/************************ WOL *******************************/ +struct be_cmd_req_acpi_wol_magic_config{ + struct be_cmd_req_hdr hdr; + u32 rsvd0[145]; + u8 magic_mac[6]; + u8 rsvd2[2]; +} __packed; + +/********************** LoopBack test *********************/ +struct be_cmd_req_loopback_test { + struct be_cmd_req_hdr hdr; + u32 loopback_type; + u32 num_pkts; + u64 pattern; + u32 src_port; + u32 dest_port; + u32 pkt_size; +}; + +struct be_cmd_resp_loopback_test { + struct be_cmd_resp_hdr resp_hdr; + u32 status; + u32 num_txfer; + u32 num_rx; + u32 miscomp_off; + u32 ticks_compl; +}; + +/********************** DDR DMA test *********************/ +struct be_cmd_req_ddrdma_test { + struct be_cmd_req_hdr hdr; + u64 pattern; + u32 byte_count; + u32 rsvd0; + u8 snd_buff[4096]; + u8 rsvd1[4096]; +}; + +struct be_cmd_resp_ddrdma_test { + struct be_cmd_resp_hdr hdr; + u64 pattern; + u32 byte_cnt; + u32 snd_err; + u8 rsvd0[4096]; + u8 rcv_buff[4096]; +}; + extern int be_pci_fnum_get(struct be_adapter *adapter); extern int be_cmd_POST(struct be_adapter *adapter); extern int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr, @@ -743,7 +870,7 @@ extern int be_cmd_rxq_create(struct be_adapter *adapter, extern int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q, int type); extern int be_cmd_link_status_query(struct be_adapter *adapter, - bool *link_up); + bool *link_up, u8 *mac_speed, u16 *link_speed); extern int be_cmd_reset(struct be_adapter *adapter); extern int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd); @@ -756,7 +883,8 @@ extern int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, extern int be_cmd_promiscuous_config(struct be_adapter *adapter, u8 port_num, bool en); extern int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id, - struct dev_mc_list *mc_list, u32 mc_count); + struct dev_mc_list *mc_list, u32 mc_count, + struct be_dma_mem *mem); extern int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc); extern int be_cmd_get_flow_control(struct be_adapter *adapter, @@ -765,6 +893,22 @@ extern int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num, u32 *cap); extern int be_cmd_reset_function(struct be_adapter *adapter); extern int be_process_mcc(struct be_adapter *adapter); +extern int be_cmd_set_beacon_state(struct be_adapter *adapter, + u8 port_num, u8 beacon, u8 status, u8 state); +extern int be_cmd_get_beacon_state(struct be_adapter *adapter, + u8 port_num, u32 *state); +extern int be_cmd_read_port_type(struct be_adapter *adapter, u32 port, + u8 *connector); extern int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, u32 flash_oper, u32 flash_opcode, u32 buf_size); +extern int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc); +extern int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac, + struct be_dma_mem *nonemb_cmd); +extern int be_cmd_fw_init(struct be_adapter *adapter); +extern int be_cmd_fw_clean(struct be_adapter *adapter); +extern int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num, + u32 loopback_type, u32 pkt_size, + u32 num_pkts, u64 pattern); +extern int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern, + u32 byte_cnt, struct be_dma_mem *cmd); diff --git a/drivers/net/benet/be_ethtool.c b/drivers/net/benet/be_ethtool.c index f0fd95b43c07..298b92cbd689 100644 --- a/drivers/net/benet/be_ethtool.c +++ b/drivers/net/benet/be_ethtool.c @@ -55,7 +55,7 @@ static const struct be_ethtool_stat et_stats[] = { {DRVSTAT_INFO(be_tx_stops)}, {DRVSTAT_INFO(be_fwd_reqs)}, {DRVSTAT_INFO(be_tx_wrbs)}, - {DRVSTAT_INFO(be_polls)}, + {DRVSTAT_INFO(be_rx_polls)}, {DRVSTAT_INFO(be_tx_events)}, {DRVSTAT_INFO(be_rx_events)}, {DRVSTAT_INFO(be_tx_compl)}, @@ -107,6 +107,18 @@ static const struct be_ethtool_stat et_stats[] = { }; #define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats) +static const char et_self_tests[][ETH_GSTRING_LEN] = { + "MAC Loopback test", + "PHY Loopback test", + "External Loopback test", + "DDR DMA test" +}; + +#define ETHTOOL_TESTS_NUM ARRAY_SIZE(et_self_tests) +#define BE_MAC_LOOPBACK 0x0 +#define BE_PHY_LOOPBACK 0x1 +#define BE_ONE_PORT_EXT_LOOPBACK 0x2 + static void be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { @@ -234,7 +246,7 @@ be_get_ethtool_stats(struct net_device *netdev, struct be_rxf_stats *rxf_stats = &hw_stats->rxf; struct be_port_rxf_stats *port_stats = &rxf_stats->port[adapter->port_num]; - struct net_device_stats *net_stats = &adapter->stats.net_stats; + struct net_device_stats *net_stats = &netdev->stats; struct be_erx_stats *erx_stats = &hw_stats->erx; void *p = NULL; int i; @@ -278,19 +290,78 @@ be_get_stat_strings(struct net_device *netdev, uint32_t stringset, data += ETH_GSTRING_LEN; } break; + case ETH_SS_TEST: + for (i = 0; i < ETHTOOL_TESTS_NUM; i++) { + memcpy(data, et_self_tests[i], ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + break; } } -static int be_get_stats_count(struct net_device *netdev) +static int be_get_sset_count(struct net_device *netdev, int stringset) { - return ETHTOOL_STATS_NUM; + switch (stringset) { + case ETH_SS_TEST: + return ETHTOOL_TESTS_NUM; + case ETH_SS_STATS: + return ETHTOOL_STATS_NUM; + default: + return -EINVAL; + } } static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { - ecmd->speed = SPEED_10000; + struct be_adapter *adapter = netdev_priv(netdev); + u8 mac_speed = 0, connector = 0; + u16 link_speed = 0; + bool link_up = false; + int status; + + if (adapter->link_speed < 0) { + status = be_cmd_link_status_query(adapter, &link_up, + &mac_speed, &link_speed); + + /* link_speed is in units of 10 Mbps */ + if (link_speed) { + ecmd->speed = link_speed*10; + } else { + switch (mac_speed) { + case PHY_LINK_SPEED_1GBPS: + ecmd->speed = SPEED_1000; + break; + case PHY_LINK_SPEED_10GBPS: + ecmd->speed = SPEED_10000; + break; + } + } + + status = be_cmd_read_port_type(adapter, adapter->port_num, + &connector); + switch (connector) { + case 7: + ecmd->port = PORT_FIBRE; + break; + default: + ecmd->port = PORT_TP; + break; + } + + /* Save for future use */ + adapter->link_speed = ecmd->speed; + adapter->port_type = ecmd->port; + } else { + ecmd->speed = adapter->link_speed; + ecmd->port = adapter->port_type; + } + ecmd->duplex = DUPLEX_FULL; ecmd->autoneg = AUTONEG_DISABLE; + ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_TP); + ecmd->phy_address = adapter->port_num; + ecmd->transceiver = XCVR_INTERNAL; + return 0; } @@ -335,6 +406,123 @@ be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd) } static int +be_phys_id(struct net_device *netdev, u32 data) +{ + struct be_adapter *adapter = netdev_priv(netdev); + int status; + u32 cur; + + be_cmd_get_beacon_state(adapter, adapter->port_num, &cur); + + if (cur == BEACON_STATE_ENABLED) + return 0; + + if (data < 2) + data = 2; + + status = be_cmd_set_beacon_state(adapter, adapter->port_num, 0, 0, + BEACON_STATE_ENABLED); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(data*HZ); + + status = be_cmd_set_beacon_state(adapter, adapter->port_num, 0, 0, + BEACON_STATE_DISABLED); + + return status; +} + +static void +be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct be_adapter *adapter = netdev_priv(netdev); + + wol->supported = WAKE_MAGIC; + if (adapter->wol) + wol->wolopts = WAKE_MAGIC; + else + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); + return; +} + +static int +be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct be_adapter *adapter = netdev_priv(netdev); + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + if (wol->wolopts & WAKE_MAGIC) + adapter->wol = true; + else + adapter->wol = false; + + return 0; +} + +static int +be_test_ddr_dma(struct be_adapter *adapter) +{ + int ret, i; + struct be_dma_mem ddrdma_cmd; + u64 pattern[2] = {0x5a5a5a5a5a5a5a5a, 0xa5a5a5a5a5a5a5a5}; + + ddrdma_cmd.size = sizeof(struct be_cmd_req_ddrdma_test); + ddrdma_cmd.va = pci_alloc_consistent(adapter->pdev, ddrdma_cmd.size, + &ddrdma_cmd.dma); + if (!ddrdma_cmd.va) { + dev_err(&adapter->pdev->dev, "Memory allocation failure \n"); + return -ENOMEM; + } + + for (i = 0; i < 2; i++) { + ret = be_cmd_ddr_dma_test(adapter, pattern[i], + 4096, &ddrdma_cmd); + if (ret != 0) + goto err; + } + +err: + pci_free_consistent(adapter->pdev, ddrdma_cmd.size, + ddrdma_cmd.va, ddrdma_cmd.dma); + return ret; +} + +static void +be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data) +{ + struct be_adapter *adapter = netdev_priv(netdev); + + memset(data, 0, sizeof(u64) * ETHTOOL_TESTS_NUM); + + if (test->flags & ETH_TEST_FL_OFFLINE) { + data[0] = be_cmd_loopback_test(adapter, adapter->port_num, + BE_MAC_LOOPBACK, 1500, + 2, 0xabc); + if (data[0] != 0) + test->flags |= ETH_TEST_FL_FAILED; + + data[1] = be_cmd_loopback_test(adapter, adapter->port_num, + BE_PHY_LOOPBACK, 1500, + 2, 0xabc); + if (data[1] != 0) + test->flags |= ETH_TEST_FL_FAILED; + + data[2] = be_cmd_loopback_test(adapter, adapter->port_num, + BE_ONE_PORT_EXT_LOOPBACK, + 1500, 2, 0xabc); + if (data[2] != 0) + test->flags |= ETH_TEST_FL_FAILED; + + data[3] = be_test_ddr_dma(adapter); + if (data[3] != 0) + test->flags |= ETH_TEST_FL_FAILED; + } + +} + +static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl) { struct be_adapter *adapter = netdev_priv(netdev); @@ -351,6 +539,8 @@ be_do_flash(struct net_device *netdev, struct ethtool_flash *efl) const struct ethtool_ops be_ethtool_ops = { .get_settings = be_get_settings, .get_drvinfo = be_get_drvinfo, + .get_wol = be_get_wol, + .set_wol = be_set_wol, .get_link = ethtool_op_get_link, .get_coalesce = be_get_coalesce, .set_coalesce = be_set_coalesce, @@ -366,7 +556,9 @@ const struct ethtool_ops be_ethtool_ops = { .get_tso = ethtool_op_get_tso, .set_tso = ethtool_op_set_tso, .get_strings = be_get_stat_strings, - .get_stats_count = be_get_stats_count, + .phys_id = be_phys_id, + .get_sset_count = be_get_sset_count, .get_ethtool_stats = be_get_ethtool_stats, .flash_device = be_do_flash, + .self_test = be_self_test, }; diff --git a/drivers/net/benet/be_hw.h b/drivers/net/benet/be_hw.h index a3394b4aa14a..e2b3beffd49d 100644 --- a/drivers/net/benet/be_hw.h +++ b/drivers/net/benet/be_hw.h @@ -52,6 +52,10 @@ */ #define MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK (1 << 29) /* bit 29 */ +/********* Power managment (WOL) **********/ +#define PCICFG_PM_CONTROL_OFFSET 0x44 +#define PCICFG_PM_CONTROL_MASK 0x108 /* bits 3 & 8 */ + /********* ISR0 Register offset **********/ #define CEV_ISR0_OFFSET 0xC18 #define CEV_ISR_SIZE 4 @@ -225,6 +229,7 @@ struct be_eth_rx_compl { #define NUM_FLASHDIR_ENTRIES 32 #define FLASHROM_TYPE_ISCSI_ACTIVE 0 +#define FLASHROM_TYPE_REDBOOT 1 #define FLASHROM_TYPE_BIOS 2 #define FLASHROM_TYPE_PXE_BIOS 3 #define FLASHROM_TYPE_FCOE_BIOS 8 @@ -234,9 +239,11 @@ struct be_eth_rx_compl { #define FLASHROM_OPER_FLASH 1 #define FLASHROM_OPER_SAVE 2 +#define FLASHROM_OPER_REPORT 4 #define FLASH_IMAGE_MAX_SIZE (1310720) /* Max firmware image size */ #define FLASH_BIOS_IMAGE_MAX_SIZE (262144) /* Max OPTION ROM image sz */ +#define FLASH_REDBOOT_IMAGE_MAX_SIZE (262144) /* Max redboot image sz */ /* Offsets for components on Flash. */ #define FLASH_iSCSI_PRIMARY_IMAGE_START (1048576) @@ -246,6 +253,8 @@ struct be_eth_rx_compl { #define FLASH_iSCSI_BIOS_START (7340032) #define FLASH_PXE_BIOS_START (7864320) #define FLASH_FCoE_BIOS_START (524288) +#define FLASH_REDBOOT_START (32768) +#define FLASH_REDBOOT_ISM_START (0) struct controller_id { u32 vendor; diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index 876b357101fa..24c7d9900baa 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -31,8 +31,10 @@ MODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data."); static DEFINE_PCI_DEVICE_TABLE(be_dev_ids) = { { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, + { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) }, { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) }, { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID3) }, { 0 } }; MODULE_DEVICE_TABLE(pci, be_dev_ids); @@ -123,6 +125,9 @@ static int be_mac_addr_set(struct net_device *netdev, void *p) struct sockaddr *addr = p; int status = 0; + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + status = be_cmd_pmac_del(adapter, adapter->if_handle, adapter->pmac_id); if (status) return status; @@ -141,7 +146,7 @@ void netdev_stats_update(struct be_adapter *adapter) struct be_rxf_stats *rxf_stats = &hw_stats->rxf; struct be_port_rxf_stats *port_stats = &rxf_stats->port[adapter->port_num]; - struct net_device_stats *dev_stats = &adapter->stats.net_stats; + struct net_device_stats *dev_stats = &adapter->netdev->stats; struct be_erx_stats *erx_stats = &hw_stats->erx; dev_stats->rx_packets = port_stats->rx_total_frames; @@ -168,7 +173,8 @@ void netdev_stats_update(struct be_adapter *adapter) port_stats->rx_udp_checksum_errs; /* no space in linux buffers: best possible approximation */ - dev_stats->rx_dropped = erx_stats->rx_drops_no_fragments[0]; + dev_stats->rx_dropped = + erx_stats->rx_drops_no_fragments[adapter->rx_obj.q.id]; /* detailed rx errors */ dev_stats->rx_length_errors = port_stats->rx_in_range_errors + @@ -214,6 +220,7 @@ void be_link_status_update(struct be_adapter *adapter, bool link_up) /* If link came up or went down */ if (adapter->link_up != link_up) { + adapter->link_speed = -1; if (link_up) { netif_start_queue(netdev); netif_carrier_on(netdev); @@ -269,9 +276,7 @@ static void be_rx_eqd_update(struct be_adapter *adapter) static struct net_device_stats *be_get_stats(struct net_device *dev) { - struct be_adapter *adapter = netdev_priv(dev); - - return &adapter->stats.net_stats; + return &dev->stats; } static u32 be_calc_rate(u64 bytes, unsigned long ticks) @@ -389,15 +394,11 @@ static int make_tx_wrbs(struct be_adapter *adapter, atomic_add(wrb_cnt, &txq->used); queue_head_inc(txq); - if (skb_dma_map(&pdev->dev, skb, DMA_TO_DEVICE)) { - dev_err(&pdev->dev, "TX DMA mapping failed\n"); - return 0; - } - if (skb->len > skb->data_len) { int len = skb->len - skb->data_len; + busaddr = pci_map_single(pdev, skb->data, len, + PCI_DMA_TODEVICE); wrb = queue_head_node(txq); - busaddr = skb_shinfo(skb)->dma_head; wrb_fill(wrb, busaddr, len); be_dws_cpu_to_le(wrb, sizeof(*wrb)); queue_head_inc(txq); @@ -407,8 +408,9 @@ static int make_tx_wrbs(struct be_adapter *adapter, for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i]; - - busaddr = skb_shinfo(skb)->dma_maps[i]; + busaddr = pci_map_page(pdev, frag->page, + frag->page_offset, + frag->size, PCI_DMA_TODEVICE); wrb = queue_head_node(txq); wrb_fill(wrb, busaddr, frag->size); be_dws_cpu_to_le(wrb, sizeof(*wrb)); @@ -562,13 +564,15 @@ static void be_set_multicast_list(struct net_device *netdev) be_cmd_promiscuous_config(adapter, adapter->port_num, 0); } - if (netdev->flags & IFF_ALLMULTI) { - be_cmd_multicast_set(adapter, adapter->if_handle, NULL, 0); + /* Enable multicast promisc if num configured exceeds what we support */ + if (netdev->flags & IFF_ALLMULTI || netdev->mc_count > BE_MAX_MC) { + be_cmd_multicast_set(adapter, adapter->if_handle, NULL, 0, + &adapter->mc_cmd_mem); goto done; } be_cmd_multicast_set(adapter, adapter->if_handle, netdev->mc_list, - netdev->mc_count); + netdev->mc_count, &adapter->mc_cmd_mem); done: return; } @@ -758,7 +762,7 @@ static void be_rx_compl_process(struct be_adapter *adapter, if ((adapter->cap == 0x400) && !vtm) vlanf = 0; - skb = netdev_alloc_skb(adapter->netdev, BE_HDR_LEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(adapter->netdev, BE_HDR_LEN); if (!skb) { if (net_ratelimit()) dev_warn(&adapter->pdev->dev, "skb alloc failed\n"); @@ -766,8 +770,6 @@ static void be_rx_compl_process(struct be_adapter *adapter, return; } - skb_reserve(skb, NET_IP_ALIGN); - skb_fill_rx_data(adapter, skb, rxcp); if (do_pkt_csum(rxcp, adapter->rx_csum)) @@ -981,23 +983,41 @@ static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq) static void be_tx_compl_process(struct be_adapter *adapter, u16 last_index) { struct be_queue_info *txq = &adapter->tx_obj.q; + struct be_eth_wrb *wrb; struct sk_buff **sent_skbs = adapter->tx_obj.sent_skb_list; struct sk_buff *sent_skb; + u64 busaddr; u16 cur_index, num_wrbs = 0; cur_index = txq->tail; sent_skb = sent_skbs[cur_index]; BUG_ON(!sent_skb); sent_skbs[cur_index] = NULL; + wrb = queue_tail_node(txq); + be_dws_le_to_cpu(wrb, sizeof(*wrb)); + busaddr = ((u64)wrb->frag_pa_hi << 32) | (u64)wrb->frag_pa_lo; + if (busaddr != 0) { + pci_unmap_single(adapter->pdev, busaddr, + wrb->frag_len, PCI_DMA_TODEVICE); + } + num_wrbs++; + queue_tail_inc(txq); - do { + while (cur_index != last_index) { cur_index = txq->tail; + wrb = queue_tail_node(txq); + be_dws_le_to_cpu(wrb, sizeof(*wrb)); + busaddr = ((u64)wrb->frag_pa_hi << 32) | (u64)wrb->frag_pa_lo; + if (busaddr != 0) { + pci_unmap_page(adapter->pdev, busaddr, + wrb->frag_len, PCI_DMA_TODEVICE); + } num_wrbs++; queue_tail_inc(txq); - } while (cur_index != last_index); + } atomic_sub(num_wrbs, &txq->used); - skb_dma_unmap(&adapter->pdev->dev, sent_skb, DMA_TO_DEVICE); + kfree_skb(sent_skb); } @@ -1377,6 +1397,7 @@ int be_poll_rx(struct napi_struct *napi, int budget) struct be_eth_rx_compl *rxcp; u32 work_done; + adapter->stats.drvr_stats.be_rx_polls++; for (work_done = 0; work_done < budget; work_done++) { rxcp = be_rx_compl_get(adapter); if (!rxcp) @@ -1475,6 +1496,14 @@ static void be_worker(struct work_struct *work) schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000)); } +static void be_msix_disable(struct be_adapter *adapter) +{ + if (adapter->msix_enabled) { + pci_disable_msix(adapter->pdev); + adapter->msix_enabled = false; + } +} + static void be_msix_enable(struct be_adapter *adapter) { int i, status; @@ -1590,6 +1619,8 @@ static int be_open(struct net_device *netdev) struct be_eq_obj *tx_eq = &adapter->tx_eq; bool link_up; int status; + u8 mac_speed; + u16 link_speed; /* First time posting */ be_post_rx_frags(adapter); @@ -1608,7 +1639,8 @@ static int be_open(struct net_device *netdev) /* Rx compl queue may be in unarmed state; rearm it */ be_cq_notify(adapter, adapter->rx_obj.cq.id, true, 0); - status = be_cmd_link_status_query(adapter, &link_up); + status = be_cmd_link_status_query(adapter, &link_up, &mac_speed, + &link_speed); if (status) goto ret_sts; be_link_status_update(adapter, link_up); @@ -1627,6 +1659,44 @@ ret_sts: return status; } +static int be_setup_wol(struct be_adapter *adapter, bool enable) +{ + struct be_dma_mem cmd; + int status = 0; + u8 mac[ETH_ALEN]; + + memset(mac, 0, ETH_ALEN); + + cmd.size = sizeof(struct be_cmd_req_acpi_wol_magic_config); + cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma); + if (cmd.va == NULL) + return -1; + memset(cmd.va, 0, cmd.size); + + if (enable) { + status = pci_write_config_dword(adapter->pdev, + PCICFG_PM_CONTROL_OFFSET, PCICFG_PM_CONTROL_MASK); + if (status) { + dev_err(&adapter->pdev->dev, + "Could not enable Wake-on-lan \n"); + pci_free_consistent(adapter->pdev, cmd.size, cmd.va, + cmd.dma); + return status; + } + status = be_cmd_enable_magic_wol(adapter, + adapter->netdev->dev_addr, &cmd); + pci_enable_wake(adapter->pdev, PCI_D3hot, 1); + pci_enable_wake(adapter->pdev, PCI_D3cold, 1); + } else { + status = be_cmd_enable_magic_wol(adapter, mac, &cmd); + pci_enable_wake(adapter->pdev, PCI_D3hot, 0); + pci_enable_wake(adapter->pdev, PCI_D3cold, 0); + } + + pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma); + return status; +} + static int be_setup(struct be_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -1658,6 +1728,8 @@ static int be_setup(struct be_adapter *adapter) if (status != 0) goto rx_qs_destroy; + adapter->link_speed = -1; + return 0; rx_qs_destroy: @@ -1678,6 +1750,8 @@ static int be_clear(struct be_adapter *adapter) be_cmd_if_destroy(adapter, adapter->if_handle); + /* tell fw we're done with firing cmds */ + be_cmd_fw_clean(adapter); return 0; } @@ -1720,6 +1794,31 @@ static int be_close(struct net_device *netdev) #define FW_FILE_HDR_SIGN "ServerEngines Corp. " char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "}; + +static bool be_flash_redboot(struct be_adapter *adapter, + const u8 *p) +{ + u32 crc_offset; + u8 flashed_crc[4]; + int status; + crc_offset = FLASH_REDBOOT_START + FLASH_REDBOOT_IMAGE_MAX_SIZE - 4 + + sizeof(struct flash_file_hdr) - 32*1024; + p += crc_offset; + status = be_cmd_get_flash_crc(adapter, flashed_crc); + if (status) { + dev_err(&adapter->pdev->dev, + "could not get crc from flash, not flashing redboot\n"); + return false; + } + + /*update redboot only if crc does not match*/ + if (!memcmp(flashed_crc, p, 4)) + return false; + else + return true; + +} + static int be_flash_image(struct be_adapter *adapter, const struct firmware *fw, struct be_dma_mem *flash_cmd, u32 flash_type) @@ -1759,6 +1858,12 @@ static int be_flash_image(struct be_adapter *adapter, image_offset = FLASH_PXE_BIOS_START; image_size = FLASH_BIOS_IMAGE_MAX_SIZE; break; + case FLASHROM_TYPE_REDBOOT: + if (!be_flash_redboot(adapter, fw->data)) + return 0; + image_offset = FLASH_REDBOOT_ISM_START; + image_size = FLASH_REDBOOT_IMAGE_MAX_SIZE; + break; default: return 0; } @@ -1877,7 +1982,7 @@ int be_load_fw(struct be_adapter *adapter, u8 *func) goto fw_exit; } - dev_info(&adapter->pdev->dev, "Firmware flashed succesfully\n"); + dev_info(&adapter->pdev->dev, "Firmware flashed successfully\n"); fw_exit: release_firmware(fw); @@ -1906,6 +2011,8 @@ static void be_netdev_init(struct net_device *netdev) NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_HW_CSUM | NETIF_F_GRO; + netdev->vlan_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_CSUM; + netdev->flags |= IFF_MULTICAST; adapter->rx_csum = true; @@ -1977,34 +2084,61 @@ static void be_ctrl_cleanup(struct be_adapter *adapter) if (mem->va) pci_free_consistent(adapter->pdev, mem->size, mem->va, mem->dma); + + mem = &adapter->mc_cmd_mem; + if (mem->va) + pci_free_consistent(adapter->pdev, mem->size, + mem->va, mem->dma); } static int be_ctrl_init(struct be_adapter *adapter) { struct be_dma_mem *mbox_mem_alloc = &adapter->mbox_mem_alloced; struct be_dma_mem *mbox_mem_align = &adapter->mbox_mem; + struct be_dma_mem *mc_cmd_mem = &adapter->mc_cmd_mem; int status; status = be_map_pci_bars(adapter); if (status) - return status; + goto done; mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16; mbox_mem_alloc->va = pci_alloc_consistent(adapter->pdev, mbox_mem_alloc->size, &mbox_mem_alloc->dma); if (!mbox_mem_alloc->va) { - be_unmap_pci_bars(adapter); - return -1; + status = -ENOMEM; + goto unmap_pci_bars; } + mbox_mem_align->size = sizeof(struct be_mcc_mailbox); mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16); mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16); memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox)); + + mc_cmd_mem->size = sizeof(struct be_cmd_req_mcast_mac_config); + mc_cmd_mem->va = pci_alloc_consistent(adapter->pdev, mc_cmd_mem->size, + &mc_cmd_mem->dma); + if (mc_cmd_mem->va == NULL) { + status = -ENOMEM; + goto free_mbox; + } + memset(mc_cmd_mem->va, 0, mc_cmd_mem->size); + spin_lock_init(&adapter->mbox_lock); spin_lock_init(&adapter->mcc_lock); spin_lock_init(&adapter->mcc_cq_lock); return 0; + +free_mbox: + pci_free_consistent(adapter->pdev, mbox_mem_alloc->size, + mbox_mem_alloc->va, mbox_mem_alloc->dma); + +unmap_pci_bars: + be_unmap_pci_bars(adapter); + +done: + return status; } static void be_stats_cleanup(struct be_adapter *adapter) @@ -2032,6 +2166,7 @@ static int be_stats_init(struct be_adapter *adapter) static void __devexit be_remove(struct pci_dev *pdev) { struct be_adapter *adapter = pci_get_drvdata(pdev); + if (!adapter) return; @@ -2043,10 +2178,7 @@ static void __devexit be_remove(struct pci_dev *pdev) be_ctrl_cleanup(adapter); - if (adapter->msix_enabled) { - pci_disable_msix(adapter->pdev); - adapter->msix_enabled = false; - } + be_msix_disable(adapter); pci_set_drvdata(pdev, NULL); pci_release_regions(pdev); @@ -2055,25 +2187,33 @@ static void __devexit be_remove(struct pci_dev *pdev) free_netdev(adapter->netdev); } -static int be_hw_up(struct be_adapter *adapter) +static int be_get_config(struct be_adapter *adapter) { int status; + u8 mac[ETH_ALEN]; - status = be_cmd_POST(adapter); + status = be_cmd_get_fw_ver(adapter, adapter->fw_ver); if (status) return status; - status = be_cmd_reset_function(adapter); + status = be_cmd_query_fw_cfg(adapter, + &adapter->port_num, &adapter->cap); if (status) return status; - status = be_cmd_get_fw_ver(adapter, adapter->fw_ver); + memset(mac, 0, ETH_ALEN); + status = be_cmd_mac_addr_query(adapter, mac, + MAC_ADDRESS_TYPE_NETWORK, true /*permanent */, 0); if (status) return status; - status = be_cmd_query_fw_cfg(adapter, - &adapter->port_num, &adapter->cap); - return status; + if (!is_valid_ether_addr(mac)) + return -EADDRNOTAVAIL; + + memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN); + memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN); + + return 0; } static int __devinit be_probe(struct pci_dev *pdev, @@ -2082,7 +2222,6 @@ static int __devinit be_probe(struct pci_dev *pdev, int status = 0; struct be_adapter *adapter; struct net_device *netdev; - u8 mac[ETH_ALEN]; status = pci_enable_device(pdev); if (status) @@ -2102,6 +2241,8 @@ static int __devinit be_probe(struct pci_dev *pdev, adapter->pdev = pdev; pci_set_drvdata(pdev, adapter); adapter->netdev = netdev; + be_netdev_init(netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); be_msix_enable(adapter); @@ -2120,27 +2261,34 @@ static int __devinit be_probe(struct pci_dev *pdev, if (status) goto free_netdev; - status = be_stats_init(adapter); + /* sync up with fw's ready state */ + status = be_cmd_POST(adapter); if (status) goto ctrl_clean; - status = be_hw_up(adapter); + /* tell fw we're ready to fire cmds */ + status = be_cmd_fw_init(adapter); if (status) - goto stats_clean; + goto ctrl_clean; - status = be_cmd_mac_addr_query(adapter, mac, MAC_ADDRESS_TYPE_NETWORK, - true /* permanent */, 0); + status = be_cmd_reset_function(adapter); + if (status) + goto ctrl_clean; + + status = be_stats_init(adapter); + if (status) + goto ctrl_clean; + + status = be_get_config(adapter); if (status) goto stats_clean; - memcpy(netdev->dev_addr, mac, ETH_ALEN); INIT_DELAYED_WORK(&adapter->work, be_worker); - be_netdev_init(netdev); - SET_NETDEV_DEV(netdev, &adapter->pdev->dev); status = be_setup(adapter); if (status) goto stats_clean; + status = register_netdev(netdev); if (status != 0) goto unsetup; @@ -2155,7 +2303,9 @@ stats_clean: ctrl_clean: be_ctrl_cleanup(adapter); free_netdev: + be_msix_disable(adapter); free_netdev(adapter->netdev); + pci_set_drvdata(pdev, NULL); rel_reg: pci_release_regions(pdev); disable_dev: @@ -2170,6 +2320,9 @@ static int be_suspend(struct pci_dev *pdev, pm_message_t state) struct be_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev = adapter->netdev; + if (adapter->wol) + be_setup_wol(adapter, true); + netif_device_detach(netdev); if (netif_running(netdev)) { rtnl_lock(); @@ -2200,6 +2353,11 @@ static int be_resume(struct pci_dev *pdev) pci_set_power_state(pdev, 0); pci_restore_state(pdev); + /* tell fw we're ready to fire cmds */ + status = be_cmd_fw_init(adapter); + if (status) + return status; + be_setup(adapter); if (netif_running(netdev)) { rtnl_lock(); @@ -2207,6 +2365,9 @@ static int be_resume(struct pci_dev *pdev) rtnl_unlock(); } netif_device_attach(netdev); + + if (adapter->wol) + be_setup_wol(adapter, false); return 0; } @@ -2221,8 +2382,8 @@ static struct pci_driver be_driver = { static int __init be_init_module(void) { - if (rx_frag_size != 8192 && rx_frag_size != 4096 - && rx_frag_size != 2048) { + if (rx_frag_size != 8192 && rx_frag_size != 4096 && + rx_frag_size != 2048) { printk(KERN_WARNING DRV_NAME " : Module param rx_frag_size must be 2048/4096/8192." " Using 2048\n"); diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index 14bd3801f7d3..8ffea3990d07 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -554,8 +554,8 @@ static void adjust_tx_list(void) { int timeout_cnt = MAX_TIMEOUT_CNT; - if (tx_list_head->status.status_word != 0 - && current_tx_ptr != tx_list_head) { + if (tx_list_head->status.status_word != 0 && + current_tx_ptr != tx_list_head) { goto adjust_head; /* released something, just return; */ } @@ -567,8 +567,8 @@ static void adjust_tx_list(void) if (current_tx_ptr->next->next == tx_list_head) { while (tx_list_head->status.status_word == 0) { udelay(10); - if (tx_list_head->status.status_word != 0 - || !(bfin_read_DMA2_IRQ_STATUS() & DMA_RUN)) { + if (tx_list_head->status.status_word != 0 || + !(bfin_read_DMA2_IRQ_STATUS() & DMA_RUN)) { goto adjust_head; } if (timeout_cnt-- < 0) { @@ -596,8 +596,8 @@ adjust_head: ": no sk_buff in a transmitted frame!\n"); } tx_list_head = tx_list_head->next; - } while (tx_list_head->status.status_word != 0 - && current_tx_ptr != tx_list_head); + } while (tx_list_head->status.status_word != 0 && + current_tx_ptr != tx_list_head); return; } diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c index 406f06424251..9b587c344194 100644 --- a/drivers/net/bmac.c +++ b/drivers/net/bmac.c @@ -438,8 +438,8 @@ bmac_init_phy(struct net_device *dev) ctrl = bmac_mif_read(dev, 0); capable = ((bmac_mif_read(dev, 1) & 0xf800) >> 6) | 1; - if (bmac_mif_read(dev, 4) != capable - || (ctrl & 0x1000) == 0) { + if (bmac_mif_read(dev, 4) != capable || + (ctrl & 0x1000) == 0) { bmac_mif_write(dev, 4, capable); bmac_mif_write(dev, 0, 0x1200); } else diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 08cddb6ff740..4bfc80812926 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -59,8 +59,8 @@ #define DRV_MODULE_NAME "bnx2" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "2.0.2" -#define DRV_MODULE_RELDATE "Aug 21, 2009" +#define DRV_MODULE_VERSION "2.0.3" +#define DRV_MODULE_RELDATE "Dec 03, 2009" #define FW_MIPS_FILE_06 "bnx2/bnx2-mips-06-5.0.0.j3.fw" #define FW_RV2P_FILE_06 "bnx2/bnx2-rv2p-06-5.0.0.j3.fw" #define FW_MIPS_FILE_09 "bnx2/bnx2-mips-09-5.0.0.j3.fw" @@ -1466,6 +1466,8 @@ bnx2_enable_forced_2g5(struct bnx2 *bp) } else if (CHIP_NUM(bp) == CHIP_NUM_5708) { bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr |= BCM5708S_BMCR_FORCE_2500; + } else { + return; } if (bp->autoneg & AUTONEG_SPEED) { @@ -1500,6 +1502,8 @@ bnx2_disable_forced_2g5(struct bnx2 *bp) } else if (CHIP_NUM(bp) == CHIP_NUM_5708) { bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr &= ~BCM5708S_BMCR_FORCE_2500; + } else { + return; } if (bp->autoneg & AUTONEG_SPEED) @@ -2811,13 +2815,21 @@ bnx2_tx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget) } } - skb_dma_unmap(&bp->pdev->dev, skb, DMA_TO_DEVICE); + pci_unmap_single(bp->pdev, pci_unmap_addr(tx_buf, mapping), + skb_headlen(skb), PCI_DMA_TODEVICE); tx_buf->skb = NULL; last = tx_buf->nr_frags; for (i = 0; i < last; i++) { sw_cons = NEXT_TX_BD(sw_cons); + + pci_unmap_page(bp->pdev, + pci_unmap_addr( + &txr->tx_buf_ring[TX_RING_IDX(sw_cons)], + mapping), + skb_shinfo(skb)->frags[i].size, + PCI_DMA_TODEVICE); } sw_cons = NEXT_TX_BD(sw_cons); @@ -5146,8 +5158,12 @@ bnx2_init_rx_ring(struct bnx2 *bp, int ring_num) ring_prod = prod = rxr->rx_pg_prod; for (i = 0; i < bp->rx_pg_ring_size; i++) { - if (bnx2_alloc_rx_page(bp, rxr, ring_prod) < 0) + if (bnx2_alloc_rx_page(bp, rxr, ring_prod) < 0) { + printk(KERN_WARNING PFX "%s: init'ed rx page ring %d " + "with %d/%d pages only\n", + bp->dev->name, ring_num, i, bp->rx_pg_ring_size); break; + } prod = NEXT_RX_BD(prod); ring_prod = RX_PG_RING_IDX(prod); } @@ -5155,8 +5171,12 @@ bnx2_init_rx_ring(struct bnx2 *bp, int ring_num) ring_prod = prod = rxr->rx_prod; for (i = 0; i < bp->rx_ring_size; i++) { - if (bnx2_alloc_rx_skb(bp, rxr, ring_prod) < 0) + if (bnx2_alloc_rx_skb(bp, rxr, ring_prod) < 0) { + printk(KERN_WARNING PFX "%s: init'ed rx ring %d with " + "%d/%d skbs only\n", + bp->dev->name, ring_num, i, bp->rx_ring_size); break; + } prod = NEXT_RX_BD(prod); ring_prod = RX_RING_IDX(prod); } @@ -5291,17 +5311,29 @@ bnx2_free_tx_skbs(struct bnx2 *bp) for (j = 0; j < TX_DESC_CNT; ) { struct sw_tx_bd *tx_buf = &txr->tx_buf_ring[j]; struct sk_buff *skb = tx_buf->skb; + int k, last; if (skb == NULL) { j++; continue; } - skb_dma_unmap(&bp->pdev->dev, skb, DMA_TO_DEVICE); + pci_unmap_single(bp->pdev, + pci_unmap_addr(tx_buf, mapping), + skb_headlen(skb), + PCI_DMA_TODEVICE); tx_buf->skb = NULL; - j += skb_shinfo(skb)->nr_frags + 1; + last = tx_buf->nr_frags; + j++; + for (k = 0; k < last; k++, j++) { + tx_buf = &txr->tx_buf_ring[TX_RING_IDX(j)]; + pci_unmap_page(bp->pdev, + pci_unmap_addr(tx_buf, mapping), + skb_shinfo(skb)->frags[k].size, + PCI_DMA_TODEVICE); + } dev_kfree_skb(skb); } } @@ -5680,11 +5712,12 @@ bnx2_run_loopback(struct bnx2 *bp, int loopback_mode) for (i = 14; i < pkt_size; i++) packet[i] = (unsigned char) (i & 0xff); - if (skb_dma_map(&bp->pdev->dev, skb, DMA_TO_DEVICE)) { + map = pci_map_single(bp->pdev, skb->data, pkt_size, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(bp->pdev, map)) { dev_kfree_skb(skb); return -EIO; } - map = skb_shinfo(skb)->dma_head; REG_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT); @@ -5719,7 +5752,7 @@ bnx2_run_loopback(struct bnx2 *bp, int loopback_mode) udelay(5); - skb_dma_unmap(&bp->pdev->dev, skb, DMA_TO_DEVICE); + pci_unmap_single(bp->pdev, map, pkt_size, PCI_DMA_TODEVICE); dev_kfree_skb(skb); if (bnx2_get_hw_tx_cons(tx_napi) != txr->tx_prod) @@ -6238,8 +6271,11 @@ bnx2_reset_task(struct work_struct *work) { struct bnx2 *bp = container_of(work, struct bnx2, reset_task); - if (!netif_running(bp->dev)) + rtnl_lock(); + if (!netif_running(bp->dev)) { + rtnl_unlock(); return; + } bnx2_netif_stop(bp); @@ -6247,6 +6283,28 @@ bnx2_reset_task(struct work_struct *work) atomic_set(&bp->intr_sem, 1); bnx2_netif_start(bp); + rtnl_unlock(); +} + +static void +bnx2_dump_state(struct bnx2 *bp) +{ + struct net_device *dev = bp->dev; + + printk(KERN_ERR PFX "%s DEBUG: intr_sem[%x]\n", dev->name, + atomic_read(&bp->intr_sem)); + printk(KERN_ERR PFX "%s DEBUG: EMAC_TX_STATUS[%08x] " + "RPM_MGMT_PKT_CTRL[%08x]\n", dev->name, + REG_RD(bp, BNX2_EMAC_TX_STATUS), + REG_RD(bp, BNX2_RPM_MGMT_PKT_CTRL)); + printk(KERN_ERR PFX "%s DEBUG: MCP_STATE_P0[%08x] MCP_STATE_P1[%08x]\n", + dev->name, bnx2_reg_rd_ind(bp, BNX2_MCP_STATE_P0), + bnx2_reg_rd_ind(bp, BNX2_MCP_STATE_P1)); + printk(KERN_ERR PFX "%s DEBUG: HC_STATS_INTERRUPT_STATUS[%08x]\n", + dev->name, REG_RD(bp, BNX2_HC_STATS_INTERRUPT_STATUS)); + if (bp->flags & BNX2_FLAG_USING_MSIX) + printk(KERN_ERR PFX "%s DEBUG: PBA[%08x]\n", dev->name, + REG_RD(bp, BNX2_PCI_GRC_WINDOW3_BASE)); } static void @@ -6254,6 +6312,8 @@ bnx2_tx_timeout(struct net_device *dev) { struct bnx2 *bp = netdev_priv(dev); + bnx2_dump_state(bp); + /* This allows the netif to be shutdown gracefully before resetting */ schedule_work(&bp->reset_task); } @@ -6298,7 +6358,6 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) struct bnx2_napi *bnapi; struct bnx2_tx_ring_info *txr; struct netdev_queue *txq; - struct skb_shared_info *sp; /* Determine which tx ring we will be placed on */ i = skb_get_queue_mapping(skb); @@ -6363,16 +6422,15 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) } else mss = 0; - if (skb_dma_map(&bp->pdev->dev, skb, DMA_TO_DEVICE)) { + mapping = pci_map_single(bp->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(bp->pdev, mapping)) { dev_kfree_skb(skb); return NETDEV_TX_OK; } - sp = skb_shinfo(skb); - mapping = sp->dma_head; - tx_buf = &txr->tx_buf_ring[ring_prod]; tx_buf->skb = skb; + pci_unmap_addr_set(tx_buf, mapping, mapping); txbd = &txr->tx_desc_ring[ring_prod]; @@ -6393,7 +6451,12 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) txbd = &txr->tx_desc_ring[ring_prod]; len = frag->size; - mapping = sp->dma_maps[i]; + mapping = pci_map_page(bp->pdev, frag->page, frag->page_offset, + len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(bp->pdev, mapping)) + goto dma_error; + pci_unmap_addr_set(&txr->tx_buf_ring[ring_prod], mapping, + mapping); txbd->tx_bd_haddr_hi = (u64) mapping >> 32; txbd->tx_bd_haddr_lo = (u64) mapping & 0xffffffff; @@ -6420,6 +6483,30 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) } return NETDEV_TX_OK; +dma_error: + /* save value of frag that failed */ + last_frag = i; + + /* start back at beginning and unmap skb */ + prod = txr->tx_prod; + ring_prod = TX_RING_IDX(prod); + tx_buf = &txr->tx_buf_ring[ring_prod]; + tx_buf->skb = NULL; + pci_unmap_single(bp->pdev, pci_unmap_addr(tx_buf, mapping), + skb_headlen(skb), PCI_DMA_TODEVICE); + + /* unmap remaining mapped pages */ + for (i = 0; i < last_frag; i++) { + prod = NEXT_TX_BD(prod); + ring_prod = TX_RING_IDX(prod); + tx_buf = &txr->tx_buf_ring[ring_prod]; + pci_unmap_page(bp->pdev, pci_unmap_addr(tx_buf, mapping), + skb_shinfo(skb)->frags[i].size, + PCI_DMA_TODEVICE); + } + + dev_kfree_skb(skb); + return NETDEV_TX_OK; } /* Called with rtnl_lock */ @@ -7635,6 +7722,86 @@ bnx2_get_pci_speed(struct bnx2 *bp) } +static void __devinit +bnx2_read_vpd_fw_ver(struct bnx2 *bp) +{ + int rc, i, v0_len = 0; + u8 *data; + u8 *v0_str = NULL; + bool mn_match = false; + +#define BNX2_VPD_NVRAM_OFFSET 0x300 +#define BNX2_VPD_LEN 128 +#define BNX2_MAX_VER_SLEN 30 + + data = kmalloc(256, GFP_KERNEL); + if (!data) + return; + + rc = bnx2_nvram_read(bp, BNX2_VPD_NVRAM_OFFSET, data + BNX2_VPD_LEN, + BNX2_VPD_LEN); + if (rc) + goto vpd_done; + + for (i = 0; i < BNX2_VPD_LEN; i += 4) { + data[i] = data[i + BNX2_VPD_LEN + 3]; + data[i + 1] = data[i + BNX2_VPD_LEN + 2]; + data[i + 2] = data[i + BNX2_VPD_LEN + 1]; + data[i + 3] = data[i + BNX2_VPD_LEN]; + } + + for (i = 0; i <= BNX2_VPD_LEN - 3; ) { + unsigned char val = data[i]; + unsigned int block_end; + + if (val == 0x82 || val == 0x91) { + i = (i + 3 + (data[i + 1] + (data[i + 2] << 8))); + continue; + } + + if (val != 0x90) + goto vpd_done; + + block_end = (i + 3 + (data[i + 1] + (data[i + 2] << 8))); + i += 3; + + if (block_end > BNX2_VPD_LEN) + goto vpd_done; + + while (i < (block_end - 2)) { + int len = data[i + 2]; + + if (i + 3 + len > block_end) + goto vpd_done; + + if (data[i] == 'M' && data[i + 1] == 'N') { + if (len != 4 || + memcmp(&data[i + 3], "1028", 4)) + goto vpd_done; + mn_match = true; + + } else if (data[i] == 'V' && data[i + 1] == '0') { + if (len > BNX2_MAX_VER_SLEN) + goto vpd_done; + + v0_len = len; + v0_str = &data[i + 3]; + } + i += 3 + len; + + if (mn_match && v0_str) { + memcpy(bp->fw_version, v0_str, v0_len); + bp->fw_version[v0_len] = ' '; + goto vpd_done; + } + } + goto vpd_done; + } + +vpd_done: + kfree(data); +} + static int __devinit bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) { @@ -7808,10 +7975,18 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) goto err_out_unmap; } + bnx2_read_vpd_fw_ver(bp); + + j = strlen(bp->fw_version); reg = bnx2_shmem_rd(bp, BNX2_DEV_INFO_BC_REV); - for (i = 0, j = 0; i < 3; i++) { + for (i = 0; i < 3 && j < 24; i++) { u8 num, k, skip0; + if (i == 0) { + bp->fw_version[j++] = 'b'; + bp->fw_version[j++] = 'c'; + bp->fw_version[j++] = ' '; + } num = (u8) (reg >> (24 - (i * 8))); for (k = 100, skip0 = 1; k >= 1; num %= k, k /= 10) { if (num >= k || !skip0 || k == 1) { @@ -7842,8 +8017,9 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) reg != BNX2_CONDITION_MFW_RUN_NONE) { u32 addr = bnx2_shmem_rd(bp, BNX2_MFW_VER_PTR); - bp->fw_version[j++] = ' '; - for (i = 0; i < 3; i++) { + if (j < 32) + bp->fw_version[j++] = ' '; + for (i = 0; i < 3 && j < 28; i++) { reg = bnx2_reg_rd_ind(bp, addr + i * 4); reg = swab32(reg); memcpy(&bp->fw_version[j], ®, 4); @@ -8264,6 +8440,7 @@ static pci_ers_result_t bnx2_io_slot_reset(struct pci_dev *pdev) } pci_set_master(pdev); pci_restore_state(pdev); + pci_save_state(pdev); if (netif_running(dev)) { bnx2_set_power_state(bp, PCI_D0); diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h index a4d83409f205..939dc44d50a0 100644 --- a/drivers/net/bnx2.h +++ b/drivers/net/bnx2.h @@ -6345,6 +6345,8 @@ struct l2_fhdr { #define BNX2_MCP_ROM 0x00150000 #define BNX2_MCP_SCRATCH 0x00160000 +#define BNX2_MCP_STATE_P1 0x0016f9c8 +#define BNX2_MCP_STATE_P0 0x0016fdc8 #define BNX2_SHM_HDR_SIGNATURE BNX2_MCP_SCRATCH #define BNX2_SHM_HDR_SIGNATURE_SIG_MASK 0xffff0000 @@ -6559,6 +6561,7 @@ struct sw_pg { struct sw_tx_bd { struct sk_buff *skb; + DECLARE_PCI_UNMAP_ADDR(mapping) unsigned short is_gso; unsigned short nr_frags; }; diff --git a/drivers/net/bnx2x.h b/drivers/net/bnx2x.h index bbf842284ebb..602ab86b6392 100644 --- a/drivers/net/bnx2x.h +++ b/drivers/net/bnx2x.h @@ -24,6 +24,10 @@ #define BCM_VLAN 1 #endif +#if defined(CONFIG_CNIC) || defined(CONFIG_CNIC_MODULE) +#define BCM_CNIC 1 +#include "cnic_if.h" +#endif #define BNX2X_MULTI_QUEUE @@ -255,9 +259,6 @@ struct bnx2x_eth_q_stats { struct bnx2x_fastpath { struct napi_struct napi; - - u8 is_rx_queue; - struct host_status_block *status_blk; dma_addr_t status_blk_mapping; @@ -762,7 +763,11 @@ struct bnx2x_eth_stats { (offsetof(struct bnx2x_eth_stats, stat_name) / 4) +#ifdef BCM_CNIC +#define MAX_CONTEXT 15 +#else #define MAX_CONTEXT 16 +#endif union cdu_context { struct eth_context eth; @@ -811,13 +816,21 @@ struct bnx2x { struct bnx2x_fastpath fp[MAX_CONTEXT]; void __iomem *regview; void __iomem *doorbells; +#ifdef BCM_CNIC +#define BNX2X_DB_SIZE (18*BCM_PAGE_SIZE) +#else #define BNX2X_DB_SIZE (16*BCM_PAGE_SIZE) +#endif struct net_device *dev; struct pci_dev *pdev; atomic_t intr_sem; +#ifdef BCM_CNIC + struct msix_entry msix_table[MAX_CONTEXT+2]; +#else struct msix_entry msix_table[MAX_CONTEXT+1]; +#endif #define INT_MODE_INTx 1 #define INT_MODE_MSI 2 #define INT_MODE_MSIX 3 @@ -863,8 +876,8 @@ struct bnx2x { /* Flags for marking that there is a STAT_QUERY or SET_MAC ramrod pending */ - u8 stats_pending; - u8 set_mac_pending; + int stats_pending; + int set_mac_pending; /* End of fields used in the performance code paths */ @@ -884,6 +897,7 @@ struct bnx2x { #define BP_NOMCP(bp) (bp->flags & NO_MCP_FLAG) #define HW_VLAN_TX_FLAG 0x400 #define HW_VLAN_RX_FLAG 0x800 +#define MF_FUNC_DIS 0x1000 int func; #define BP_PORT(bp) (bp->func % PORT_MAX) @@ -891,6 +905,11 @@ struct bnx2x { #define BP_E1HVN(bp) (bp->func >> 1) #define BP_L_ID(bp) (BP_E1HVN(bp) << 2) +#ifdef BCM_CNIC +#define BCM_CNIC_CID_START 16 +#define BCM_ISCSI_ETH_CL_ID 17 +#endif + int pm_cap; int pcie_cap; int mrrs; @@ -944,13 +963,11 @@ struct bnx2x { #define BNX2X_STATE_CLOSING_WAIT4_HALT 0x4000 #define BNX2X_STATE_CLOSING_WAIT4_DELETE 0x5000 #define BNX2X_STATE_CLOSING_WAIT4_UNLOAD 0x6000 -#define BNX2X_STATE_DISABLED 0xd000 #define BNX2X_STATE_DIAG 0xe000 #define BNX2X_STATE_ERROR 0xf000 int multi_mode; - int num_rx_queues; - int num_tx_queues; + int num_queues; u32 rx_mode; #define BNX2X_RX_MODE_NONE 0 @@ -960,28 +977,51 @@ struct bnx2x { #define BNX2X_MAX_MULTICAST 64 #define BNX2X_MAX_EMUL_MULTI 16 + u32 rx_mode_cl_mask; + dma_addr_t def_status_blk_mapping; struct bnx2x_slowpath *slowpath; dma_addr_t slowpath_mapping; -#ifdef BCM_ISCSI - void *t1; - dma_addr_t t1_mapping; - void *t2; - dma_addr_t t2_mapping; - void *timers; - dma_addr_t timers_mapping; - void *qm; - dma_addr_t qm_mapping; -#endif - int dropless_fc; +#ifdef BCM_CNIC + u32 cnic_flags; +#define BNX2X_CNIC_FLAG_MAC_SET 1 + + void *t1; + dma_addr_t t1_mapping; + void *t2; + dma_addr_t t2_mapping; + void *timers; + dma_addr_t timers_mapping; + void *qm; + dma_addr_t qm_mapping; + struct cnic_ops *cnic_ops; + void *cnic_data; + u32 cnic_tag; + struct cnic_eth_dev cnic_eth_dev; + struct host_status_block *cnic_sb; + dma_addr_t cnic_sb_mapping; +#define CNIC_SB_ID(bp) BP_L_ID(bp) + struct eth_spe *cnic_kwq; + struct eth_spe *cnic_kwq_prod; + struct eth_spe *cnic_kwq_cons; + struct eth_spe *cnic_kwq_last; + u16 cnic_kwq_pending; + u16 cnic_spq_pending; + struct mutex cnic_mutex; + u8 iscsi_mac[6]; +#endif + int dmae_ready; /* used to synchronize dmae accesses */ struct mutex dmae_mutex; + /* used to protect the FW mail box */ + struct mutex fw_mb_mutex; + /* used to synchronize stats collecting */ int stats_state; /* used by dmae command loader */ @@ -1030,20 +1070,15 @@ struct bnx2x { }; -#define BNX2X_MAX_QUEUES(bp) (IS_E1HMF(bp) ? (MAX_CONTEXT/(2 * E1HVN_MAX)) \ - : (MAX_CONTEXT/2)) -#define BNX2X_NUM_QUEUES(bp) (bp->num_rx_queues + bp->num_tx_queues) -#define is_multi(bp) (BNX2X_NUM_QUEUES(bp) > 2) +#define BNX2X_MAX_QUEUES(bp) (IS_E1HMF(bp) ? (MAX_CONTEXT/E1HVN_MAX) \ + : MAX_CONTEXT) +#define BNX2X_NUM_QUEUES(bp) (bp->num_queues) +#define is_multi(bp) (BNX2X_NUM_QUEUES(bp) > 1) -#define for_each_rx_queue(bp, var) \ - for (var = 0; var < bp->num_rx_queues; var++) -#define for_each_tx_queue(bp, var) \ - for (var = bp->num_rx_queues; \ - var < BNX2X_NUM_QUEUES(bp); var++) #define for_each_queue(bp, var) \ for (var = 0; var < BNX2X_NUM_QUEUES(bp); var++) #define for_each_nondefault_queue(bp, var) \ - for (var = 1; var < bp->num_rx_queues; var++) + for (var = 1; var < BNX2X_NUM_QUEUES(bp); var++) void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32); @@ -1147,7 +1182,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, #define MAX_SP_DESC_CNT (SP_DESC_CNT - 1) -#define BNX2X_BTR 3 +#define BNX2X_BTR 1 #define MAX_SPQ_PENDING 8 diff --git a/drivers/net/bnx2x_hsi.h b/drivers/net/bnx2x_hsi.h index 8e2261fad485..52585338ada8 100644 --- a/drivers/net/bnx2x_hsi.h +++ b/drivers/net/bnx2x_hsi.h @@ -7,6 +7,20 @@ * the Free Software Foundation. */ +struct license_key { + u32 reserved[6]; + +#if defined(__BIG_ENDIAN) + u16 max_iscsi_init_conn; + u16 max_iscsi_trgt_conn; +#elif defined(__LITTLE_ENDIAN) + u16 max_iscsi_trgt_conn; + u16 max_iscsi_init_conn; +#endif + + u32 reserved_a[6]; +}; + #define PORT_0 0 #define PORT_1 1 @@ -250,6 +264,7 @@ struct port_hw_cfg { /* port 0: 0x12c port 1: 0x2bc */ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101 0x00000800 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727 0x00000900 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC 0x00000a00 +#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823 0x00000b00 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE 0x0000fd00 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN 0x0000ff00 @@ -881,7 +896,7 @@ struct shmem_region { /* SharedMem Offset (size) */ struct shm_dev_info dev_info; /* 0x8 (0x438) */ - u8 reserved[52*PORT_MAX]; + struct license_key drv_lic_key[PORT_MAX]; /* 0x440 (52*2=0x68) */ /* FW information (for internal FW use) */ u32 fw_info_fio_offset; /* 0x4a8 (0x4) */ @@ -1245,8 +1260,8 @@ struct host_func_stats { #define BCM_5710_FW_MAJOR_VERSION 5 -#define BCM_5710_FW_MINOR_VERSION 0 -#define BCM_5710_FW_REVISION_VERSION 21 +#define BCM_5710_FW_MINOR_VERSION 2 +#define BCM_5710_FW_REVISION_VERSION 7 #define BCM_5710_FW_ENGINEERING_VERSION 0 #define BCM_5710_FW_COMPILE_FLAGS 1 diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index e32d3370862e..cf5778919b4b 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -1107,18 +1107,21 @@ static void bnx2x_set_parallel_detection(struct link_params *params, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2, &control2); - - - control2 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN; - - + if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) + control2 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN; + else + control2 &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN; + DP(NETIF_MSG_LINK, "params->speed_cap_mask = 0x%x, control2 = 0x%x\n", + params->speed_cap_mask, control2); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2, control2); - if (phy_flags & PHY_XGXS_FLAG) { + if ((phy_flags & PHY_XGXS_FLAG) && + (params->speed_cap_mask & + PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) { DP(NETIF_MSG_LINK, "XGXS\n"); CL45_WR_OVER_CL22(bp, params->port, @@ -1225,7 +1228,7 @@ static void bnx2x_set_autoneg(struct link_params *params, params->phy_addr, MDIO_REG_BANK_CL73_USERB0, MDIO_CL73_USERB0_CL73_UCTRL, - MDIO_CL73_USERB0_CL73_UCTRL_USTAT1_MUXSEL); + 0xe); /* Enable BAM Station Manager*/ CL45_WR_OVER_CL22(bp, params->port, @@ -1236,29 +1239,25 @@ static void bnx2x_set_autoneg(struct link_params *params, MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_STATION_MNGR_EN | MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_NP_AFTER_BP_EN); - /* Merge CL73 and CL37 aneg resolution */ - CL45_RD_OVER_CL22(bp, params->port, - params->phy_addr, - MDIO_REG_BANK_CL73_USERB0, - MDIO_CL73_USERB0_CL73_BAM_CTRL3, - ®_val); - - if (params->speed_cap_mask & - PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) { - /* Set the CL73 AN speed */ + /* Advertise CL73 link speeds */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB1, MDIO_CL73_IEEEB1_AN_ADV2, ®_val); + if (params->speed_cap_mask & + PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) + reg_val |= MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4; + if (params->speed_cap_mask & + PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) + reg_val |= MDIO_CL73_IEEEB1_AN_ADV2_ADVR_1000M_KX; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB1, MDIO_CL73_IEEEB1_AN_ADV2, - reg_val | MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4); + reg_val); - } /* CL73 Autoneg Enabled */ reg_val = MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN; @@ -1351,6 +1350,7 @@ static void bnx2x_set_brcm_cl37_advertisment(struct link_params *params) static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u16 *ieee_fc) { + struct bnx2x *bp = params->bp; *ieee_fc = MDIO_COMBO_IEEE0_AUTO_NEG_ADV_FULL_DUPLEX; /* resolve pause mode and advertisement * Please refer to Table 28B-3 of the 802.3ab-1999 spec */ @@ -1380,18 +1380,30 @@ static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u16 *ieee_fc) *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE; break; } + DP(NETIF_MSG_LINK, "ieee_fc = 0x%x\n", *ieee_fc); } static void bnx2x_set_ieee_aneg_advertisment(struct link_params *params, u16 ieee_fc) { struct bnx2x *bp = params->bp; + u16 val; /* for AN, we are always publishing full duplex */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_ADV, ieee_fc); + CL45_RD_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_CL73_IEEEB1, + MDIO_CL73_IEEEB1_AN_ADV1, &val); + val &= ~MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_BOTH; + val |= ((ieee_fc<<3) & MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_MASK); + CL45_WR_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_CL73_IEEEB1, + MDIO_CL73_IEEEB1_AN_ADV1, val); } static void bnx2x_restart_autoneg(struct link_params *params, u8 enable_cl73) @@ -1609,6 +1621,39 @@ static u8 bnx2x_ext_phy_resolve_fc(struct link_params *params, return ret; } +static u8 bnx2x_direct_parallel_detect_used(struct link_params *params) +{ + struct bnx2x *bp = params->bp; + u16 pd_10g, status2_1000x; + CL45_RD_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_SERDES_DIGITAL, + MDIO_SERDES_DIGITAL_A_1000X_STATUS2, + &status2_1000x); + CL45_RD_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_SERDES_DIGITAL, + MDIO_SERDES_DIGITAL_A_1000X_STATUS2, + &status2_1000x); + if (status2_1000x & MDIO_SERDES_DIGITAL_A_1000X_STATUS2_AN_DISABLED) { + DP(NETIF_MSG_LINK, "1G parallel detect link on port %d\n", + params->port); + return 1; + } + + CL45_RD_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_10G_PARALLEL_DETECT, + MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_STATUS, + &pd_10g); + + if (pd_10g & MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_STATUS_PD_LINK) { + DP(NETIF_MSG_LINK, "10G parallel detect link on port %d\n", + params->port); + return 1; + } + return 0; +} static void bnx2x_flow_ctrl_resolve(struct link_params *params, struct link_vars *vars, @@ -1627,21 +1672,53 @@ static void bnx2x_flow_ctrl_resolve(struct link_params *params, (!(vars->phy_flags & PHY_SGMII_FLAG)) && (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)) { - CL45_RD_OVER_CL22(bp, params->port, - params->phy_addr, - MDIO_REG_BANK_COMBO_IEEE0, - MDIO_COMBO_IEEE0_AUTO_NEG_ADV, - &ld_pause); - CL45_RD_OVER_CL22(bp, params->port, - params->phy_addr, - MDIO_REG_BANK_COMBO_IEEE0, - MDIO_COMBO_IEEE0_AUTO_NEG_LINK_PARTNER_ABILITY1, - &lp_pause); - pause_result = (ld_pause & + if (bnx2x_direct_parallel_detect_used(params)) { + vars->flow_ctrl = params->req_fc_auto_adv; + return; + } + if ((gp_status & + (MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_AUTONEG_COMPLETE | + MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_MR_LP_NP_AN_ABLE)) == + (MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_AUTONEG_COMPLETE | + MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_MR_LP_NP_AN_ABLE)) { + + CL45_RD_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_CL73_IEEEB1, + MDIO_CL73_IEEEB1_AN_ADV1, + &ld_pause); + CL45_RD_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_CL73_IEEEB1, + MDIO_CL73_IEEEB1_AN_LP_ADV1, + &lp_pause); + pause_result = (ld_pause & + MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_MASK) + >> 8; + pause_result |= (lp_pause & + MDIO_CL73_IEEEB1_AN_LP_ADV1_PAUSE_MASK) + >> 10; + DP(NETIF_MSG_LINK, "pause_result CL73 0x%x\n", + pause_result); + } else { + + CL45_RD_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_COMBO_IEEE0, + MDIO_COMBO_IEEE0_AUTO_NEG_ADV, + &ld_pause); + CL45_RD_OVER_CL22(bp, params->port, + params->phy_addr, + MDIO_REG_BANK_COMBO_IEEE0, + MDIO_COMBO_IEEE0_AUTO_NEG_LINK_PARTNER_ABILITY1, + &lp_pause); + pause_result = (ld_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>5; - pause_result |= (lp_pause & + pause_result |= (lp_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>7; - DP(NETIF_MSG_LINK, "pause_result 0x%x\n", pause_result); + DP(NETIF_MSG_LINK, "pause_result CL37 0x%x\n", + pause_result); + } bnx2x_pause_resolve(vars, pause_result); } else if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) && (bnx2x_ext_phy_resolve_fc(params, vars))) { @@ -1853,6 +1930,8 @@ static u8 bnx2x_link_settings_status(struct link_params *params, (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) || (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == + PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706) || + (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726))) { vars->autoneg = AUTO_NEG_ENABLED; @@ -1987,8 +2066,7 @@ static u8 bnx2x_emac_program(struct link_params *params, GRCBASE_EMAC0 + port*0x400 + EMAC_REG_EMAC_MODE, mode); - bnx2x_set_led(bp, params->port, LED_MODE_OPER, - line_speed, params->hw_led_mode, params->chip_id); + bnx2x_set_led(params, LED_MODE_OPER, line_speed); return 0; } @@ -2122,6 +2200,8 @@ static void bnx2x_ext_phy_reset(struct link_params *params, MDIO_PMA_REG_CTRL, 1<<15); break; + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823: + break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "XGXS PHY Failure detected\n"); break; @@ -2512,16 +2592,11 @@ static void bnx2x_bcm8726_external_rom_boot(struct link_params *params) /* Need to wait 100ms after reset */ msleep(100); - /* Set serial boot control for external load */ - bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_MISC_CTRL1, 0x0001); - /* Micro controller re-boot */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, - MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); + 0x018B); /* Set soft reset */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, @@ -2529,14 +2604,10 @@ static void bnx2x_bcm8726_external_rom_boot(struct link_params *params) MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); - /* Set PLL register value to be same like in P13 ver */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, - MDIO_PMA_REG_PLL_CTRL, - 0x73A0); + MDIO_PMA_REG_MISC_CTRL1, 0x0001); - /* Clear soft reset. - Will automatically reset micro-controller re-boot */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, @@ -3462,8 +3533,8 @@ static void bnx2x_8481_set_10G_led_mode(struct link_params *params, MDIO_PMA_REG_8481_LINK_SIGNAL, &val1); /* Set bit 2 to 0, and bits [1:0] to 10 */ - val1 &= ~((1<<0) | (1<<2)); /* Clear bits 0,2*/ - val1 |= (1<<1); /* Set bit 1 */ + val1 &= ~((1<<0) | (1<<2) | (1<<7)); /* Clear bits 0,2,7*/ + val1 |= ((1<<1) | (1<<6)); /* Set bit 1, 6 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, @@ -3497,36 +3568,19 @@ static void bnx2x_8481_set_10G_led_mode(struct link_params *params, MDIO_PMA_REG_8481_LED2_MASK, 0); - /* LED3 (10G/1G/100/10G Activity) */ - bnx2x_cl45_read(bp, params->port, - ext_phy_type, - ext_phy_addr, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_8481_LINK_SIGNAL, - &val1); - /* Enable blink based on source 4(Activity) */ - val1 &= ~((1<<7) | (1<<8)); /* Clear bits 7,8 */ - val1 |= (1<<6); /* Set only bit 6 */ + /* Unmask LED3 for 10G link */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, - MDIO_PMA_REG_8481_LINK_SIGNAL, - val1); - - bnx2x_cl45_read(bp, params->port, - ext_phy_type, - ext_phy_addr, - MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED3_MASK, - &val1); - val1 |= (1<<4); /* Unmask LED3 for 10G link */ + 0x6); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, - MDIO_PMA_REG_8481_LED3_MASK, - val1); + MDIO_PMA_REG_8481_LED3_BLINK, + 0); } @@ -3544,7 +3598,10 @@ static void bnx2x_init_internal_phy(struct link_params *params, bnx2x_set_preemphasis(params); /* forced speed requested? */ - if (vars->line_speed != SPEED_AUTO_NEG) { + if (vars->line_speed != SPEED_AUTO_NEG || + ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) == + PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && + params->loopback_mode == LOOPBACK_EXT)) { DP(NETIF_MSG_LINK, "not SGMII, no AN\n"); /* disable autoneg */ @@ -3693,19 +3750,6 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) } } /* Force speed */ - /* First enable LASI */ - bnx2x_cl45_write(bp, params->port, - ext_phy_type, - ext_phy_addr, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_RX_ALARM_CTRL, - 0x0400); - bnx2x_cl45_write(bp, params->port, - ext_phy_type, - ext_phy_addr, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_LASI_CTRL, 0x0004); - if (params->req_line_speed == SPEED_10000) { DP(NETIF_MSG_LINK, "XGXS 8706 force 10Gbps\n"); @@ -3715,6 +3759,9 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) MDIO_PMA_DEVAD, MDIO_PMA_REG_DIGITAL_CTRL, 0x400); + bnx2x_cl45_write(bp, params->port, ext_phy_type, + ext_phy_addr, MDIO_PMA_DEVAD, + MDIO_PMA_REG_LASI_CTRL, 1); } else { /* Force 1Gbps using autoneg with 1G advertisment */ @@ -3756,6 +3803,17 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); + bnx2x_cl45_write(bp, params->port, + ext_phy_type, + ext_phy_addr, + MDIO_PMA_DEVAD, + MDIO_PMA_REG_RX_ALARM_CTRL, + 0x0400); + bnx2x_cl45_write(bp, params->port, + ext_phy_type, + ext_phy_addr, + MDIO_PMA_DEVAD, + MDIO_PMA_REG_LASI_CTRL, 0x0004); } bnx2x_save_bcm_spirom_ver(bp, params->port, @@ -4291,6 +4349,7 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823: /* This phy uses the NIG latch mechanism since link indication arrives through its LED4 and not via its LASI signal, so we get steady signal @@ -4298,6 +4357,12 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) bnx2x_bits_en(bp, NIG_REG_LATCH_BC_0 + params->port*4, 1 << NIG_LATCH_BC_ENABLE_MI_INT); + bnx2x_cl45_write(bp, params->port, + PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, + ext_phy_addr, + MDIO_PMA_DEVAD, + MDIO_PMA_REG_CTRL, 0x0000); + bnx2x_8481_set_led4(params, ext_phy_type, ext_phy_addr); if (params->req_line_speed == SPEED_AUTO_NEG) { @@ -4394,17 +4459,12 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) { DP(NETIF_MSG_LINK, "Advertising 10G\n"); /* Restart autoneg for 10G*/ - bnx2x_cl45_read(bp, params->port, - ext_phy_type, - ext_phy_addr, - MDIO_AN_DEVAD, - MDIO_AN_REG_CTRL, &val); - val |= 0x200; + bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, - MDIO_AN_REG_CTRL, val); + MDIO_AN_REG_CTRL, 0x3200); } } else { /* Force speed */ @@ -4657,8 +4717,8 @@ static u8 bnx2x_ext_phy_is_link_up(struct link_params *params, 0xc809, &val1); DP(NETIF_MSG_LINK, "8705 1.c809 val=0x%x\n", val1); - ext_phy_link_up = ((rx_sd & 0x1) && (val1 & (1<<9)) - && ((val1 & (1<<8)) == 0)); + ext_phy_link_up = ((rx_sd & 0x1) && (val1 & (1<<9)) && + ((val1 & (1<<8)) == 0)); if (ext_phy_link_up) vars->line_speed = SPEED_10000; break; @@ -5148,6 +5208,7 @@ static u8 bnx2x_ext_phy_is_link_up(struct link_params *params, } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823: /* Check 10G-BaseT link status */ /* Check PMD signal ok */ bnx2x_cl45_read(bp, params->port, ext_phy_type, @@ -5363,8 +5424,10 @@ static void bnx2x_link_int_ack(struct link_params *params, (NIG_STATUS_XGXS0_LINK10G | NIG_STATUS_XGXS0_LINK_STATUS | NIG_STATUS_SERDES0_LINK_STATUS)); - if (XGXS_EXT_PHY_TYPE(params->ext_phy_config) - == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481) { + if ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) + == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481) || + (XGXS_EXT_PHY_TYPE(params->ext_phy_config) + == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823)) { bnx2x_8481_rearm_latch_signal(bp, port, is_mi_int); } if (vars->phy_link_up) { @@ -5477,6 +5540,7 @@ u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded, status = bnx2x_format_ver(spirom_ver, version, len); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823: spirom_ver = ((spirom_ver & 0xF80) >> 7) << 16 | (spirom_ver & 0x7F); status = bnx2x_format_ver(spirom_ver, version, len); @@ -5728,13 +5792,15 @@ u8 bnx2x_override_led_value(struct bnx2x *bp, u8 port, } -u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, - u16 hw_led_mode, u32 chip_id) +u8 bnx2x_set_led(struct link_params *params, u8 mode, u32 speed) { + u8 port = params->port; + u16 hw_led_mode = params->hw_led_mode; u8 rc = 0; u32 tmp; u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; - + u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); + struct bnx2x *bp = params->bp; DP(NETIF_MSG_LINK, "bnx2x_set_led: port %x, mode %d\n", port, mode); DP(NETIF_MSG_LINK, "speed 0x%x, hw_led_mode 0x%x\n", speed, hw_led_mode); @@ -5749,7 +5815,14 @@ u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, break; case LED_MODE_OPER: - REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode); + if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) { + REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0); + REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 1); + } else { + REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, + hw_led_mode); + } + REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0); /* Set blinking rate to ~15.9Hz */ @@ -5761,7 +5834,7 @@ u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp & (~EMAC_LED_OVERRIDE))); - if (!CHIP_IS_E1H(bp) && + if (CHIP_IS_E1(bp) && ((speed == SPEED_2500) || (speed == SPEED_1000) || (speed == SPEED_100) || @@ -5864,6 +5937,7 @@ static u8 bnx2x_link_initialize(struct link_params *params, if (non_ext_phy || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) || + (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706) || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) || (params->loopback_mode == LOOPBACK_EXT_PHY)) { if (params->req_line_speed == SPEED_AUTO_NEG) @@ -6030,10 +6104,7 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars) REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); - bnx2x_set_led(bp, params->port, LED_MODE_OPER, - vars->line_speed, params->hw_led_mode, - params->chip_id); - + bnx2x_set_led(params, LED_MODE_OPER, vars->line_speed); } else /* No loopback */ { @@ -6091,15 +6162,13 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars, { struct bnx2x *bp = params->bp; u32 ext_phy_config = params->ext_phy_config; - u16 hw_led_mode = params->hw_led_mode; - u32 chip_id = params->chip_id; u8 port = params->port; u32 ext_phy_type = XGXS_EXT_PHY_TYPE(ext_phy_config); u32 val = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, dev_info. port_feature_config[params->port]. config)); - + DP(NETIF_MSG_LINK, "Resetting the link of port %d\n", port); /* disable attentions */ vars->link_status = 0; bnx2x_update_mng(params, vars->link_status); @@ -6127,7 +6196,7 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars, * Hold it as vars low */ /* clear link led */ - bnx2x_set_led(bp, port, LED_MODE_OFF, 0, hw_led_mode, chip_id); + bnx2x_set_led(params, LED_MODE_OFF, 0); if (reset_ext_phy) { switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: @@ -6163,6 +6232,22 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars, bnx2x_8726_reset_phy(bp, params->port, ext_phy_addr); break; } + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823: + { + u8 ext_phy_addr = + XGXS_EXT_PHY_ADDR(params->ext_phy_config); + bnx2x_cl45_write(bp, port, + PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, + ext_phy_addr, + MDIO_AN_DEVAD, + MDIO_AN_REG_CTRL, 0x0000); + bnx2x_cl45_write(bp, port, + PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, + ext_phy_addr, + MDIO_PMA_DEVAD, + MDIO_PMA_REG_CTRL, 1); + break; + } default: /* HW reset */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, @@ -6198,9 +6283,7 @@ static u8 bnx2x_update_link_down(struct link_params *params, u8 port = params->port; DP(NETIF_MSG_LINK, "Port %x: Link is down\n", port); - bnx2x_set_led(bp, port, LED_MODE_OFF, - 0, params->hw_led_mode, - params->chip_id); + bnx2x_set_led(params, LED_MODE_OFF, 0); /* indicate no mac active */ vars->mac_type = MAC_TYPE_NONE; @@ -6237,15 +6320,13 @@ static u8 bnx2x_update_link_up(struct link_params *params, vars->link_status |= LINK_STATUS_LINK_UP; if (link_10g) { bnx2x_bmac_enable(params, vars, 0); - bnx2x_set_led(bp, port, LED_MODE_OPER, - SPEED_10000, params->hw_led_mode, - params->chip_id); - + bnx2x_set_led(params, LED_MODE_OPER, SPEED_10000); } else { - bnx2x_emac_enable(params, vars, 0); rc = bnx2x_emac_program(params, vars->line_speed, vars->duplex); + bnx2x_emac_enable(params, vars, 0); + /* AN complete? */ if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) { if (!(vars->phy_flags & @@ -6343,6 +6424,7 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars) if ((ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) && + (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) && (ext_phy_link_up && !vars->phy_link_up)) bnx2x_init_internal_phy(params, vars, 0); @@ -6578,6 +6660,13 @@ static u8 bnx2x_8726_common_init_phy(struct bnx2x *bp, u32 shmem_base) return 0; } + +static u8 bnx2x_84823_common_init_phy(struct bnx2x *bp, u32 shmem_base) +{ + /* HW reset */ + bnx2x_ext_phy_hw_reset(bp, 1); + return 0; +} u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base) { u8 rc = 0; @@ -6607,7 +6696,9 @@ u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base) /* GPIO1 affects both ports, so there's need to pull it for single port alone */ rc = bnx2x_8726_common_init_phy(bp, shmem_base); - + break; + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823: + rc = bnx2x_84823_common_init_phy(bp, shmem_base); break; default: DP(NETIF_MSG_LINK, diff --git a/drivers/net/bnx2x_link.h b/drivers/net/bnx2x_link.h index f3e252264e1b..40c2981de8ed 100644 --- a/drivers/net/bnx2x_link.h +++ b/drivers/net/bnx2x_link.h @@ -178,8 +178,7 @@ u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded, Basically, the CLC takes care of the led for the link, but in case one needs to set/unset the led unnaturally, set the "mode" to LED_MODE_OPER to blink the led, and LED_MODE_OFF to set the led off.*/ -u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, - u16 hw_led_mode, u32 chip_id); +u8 bnx2x_set_led(struct link_params *params, u8 mode, u32 speed); #define LED_MODE_OFF 0 #define LED_MODE_OPER 2 diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 20f0ed956df2..77ba13520d87 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -49,6 +49,7 @@ #include <linux/prefetch.h> #include <linux/zlib.h> #include <linux/io.h> +#include <linux/stringify.h> #include "bnx2x.h" @@ -56,15 +57,20 @@ #include "bnx2x_init_ops.h" #include "bnx2x_dump.h" -#define DRV_MODULE_VERSION "1.52.1" -#define DRV_MODULE_RELDATE "2009/08/12" +#define DRV_MODULE_VERSION "1.52.1-5" +#define DRV_MODULE_RELDATE "2009/11/09" #define BNX2X_BC_VER 0x040200 #include <linux/firmware.h> #include "bnx2x_fw_file_hdr.h" /* FW files */ -#define FW_FILE_PREFIX_E1 "bnx2x-e1-" -#define FW_FILE_PREFIX_E1H "bnx2x-e1h-" +#define FW_FILE_VERSION \ + __stringify(BCM_5710_FW_MAJOR_VERSION) "." \ + __stringify(BCM_5710_FW_MINOR_VERSION) "." \ + __stringify(BCM_5710_FW_REVISION_VERSION) "." \ + __stringify(BCM_5710_FW_ENGINEERING_VERSION) +#define FW_FILE_NAME_E1 "bnx2x-e1-" FW_FILE_VERSION ".fw" +#define FW_FILE_NAME_E1H "bnx2x-e1h-" FW_FILE_VERSION ".fw" /* Time in jiffies before concluding the transmitter is hung */ #define TX_TIMEOUT (5*HZ) @@ -77,21 +83,18 @@ MODULE_AUTHOR("Eliezer Tamir"); MODULE_DESCRIPTION("Broadcom NetXtreme II BCM57710/57711/57711E Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); +MODULE_FIRMWARE(FW_FILE_NAME_E1); +MODULE_FIRMWARE(FW_FILE_NAME_E1H); static int multi_mode = 1; module_param(multi_mode, int, 0); MODULE_PARM_DESC(multi_mode, " Multi queue mode " "(0 Disable; 1 Enable (default))"); -static int num_rx_queues; -module_param(num_rx_queues, int, 0); -MODULE_PARM_DESC(num_rx_queues, " Number of Rx queues for multi_mode=1" - " (default is half number of CPUs)"); - -static int num_tx_queues; -module_param(num_tx_queues, int, 0); -MODULE_PARM_DESC(num_tx_queues, " Number of Tx queues for multi_mode=1" - " (default is half number of CPUs)"); +static int num_queues; +module_param(num_queues, int, 0); +MODULE_PARM_DESC(num_queues, " Number of queues for multi_mode=1" + " (default is as a number of CPUs)"); static int disable_tpa; module_param(disable_tpa, int, 0); @@ -550,7 +553,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) bp->def_att_idx, bp->attn_state, bp->spq_prod_idx); /* Rx */ - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; BNX2X_ERR("fp%d: rx_bd_prod(%x) rx_bd_cons(%x)" @@ -567,7 +570,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) } /* Tx */ - for_each_tx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; BNX2X_ERR("fp%d: tx_pkt_prod(%x) tx_pkt_cons(%x)" @@ -582,7 +585,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) /* Rings */ /* Rx */ - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; start = RX_BD(le16_to_cpu(*fp->rx_cons_sb) - 10); @@ -616,7 +619,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) } /* Tx */ - for_each_tx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; start = TX_BD(le16_to_cpu(*fp->tx_cons_sb) - 10); @@ -742,6 +745,9 @@ static void bnx2x_int_disable_sync(struct bnx2x *bp, int disable_hw) if (msix) { synchronize_irq(bp->msix_table[0].vector); offset = 1; +#ifdef BCM_CNIC + offset++; +#endif for_each_queue(bp, i) synchronize_irq(bp->msix_table[i + offset].vector); } else @@ -781,21 +787,13 @@ static inline void bnx2x_ack_sb(struct bnx2x *bp, u8 sb_id, barrier(); } -static inline u16 bnx2x_update_fpsb_idx(struct bnx2x_fastpath *fp) +static inline void bnx2x_update_fpsb_idx(struct bnx2x_fastpath *fp) { struct host_status_block *fpsb = fp->status_blk; - u16 rc = 0; barrier(); /* status block is written to by the chip */ - if (fp->fp_c_idx != fpsb->c_status_block.status_block_index) { - fp->fp_c_idx = fpsb->c_status_block.status_block_index; - rc |= 1; - } - if (fp->fp_u_idx != fpsb->u_status_block.status_block_index) { - fp->fp_u_idx = fpsb->u_status_block.status_block_index; - rc |= 2; - } - return rc; + fp->fp_c_idx = fpsb->c_status_block.status_block_index; + fp->fp_u_idx = fpsb->u_status_block.status_block_index; } static u16 bnx2x_ack_int(struct bnx2x *bp) @@ -835,6 +833,9 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, u16 bd_idx = TX_BD(tx_buf->first_bd), new_cons; int nbd; + /* prefetch skb end pointer to speedup dev_kfree_skb() */ + prefetch(&skb->end); + DP(BNX2X_MSG_OFF, "pkt_idx %d buff @(%p)->skb %p\n", idx, tx_buf, skb); @@ -879,7 +880,7 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, /* release skb */ WARN_ON(!skb); - dev_kfree_skb_any(skb); + dev_kfree_skb(skb); tx_buf->first_bd = 0; tx_buf->skb = NULL; @@ -909,19 +910,28 @@ static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp) return (s16)(fp->bp->tx_ring_size) - used; } -static void bnx2x_tx_int(struct bnx2x_fastpath *fp) +static inline int bnx2x_has_tx_work(struct bnx2x_fastpath *fp) +{ + u16 hw_cons; + + /* Tell compiler that status block fields can change */ + barrier(); + hw_cons = le16_to_cpu(*fp->tx_cons_sb); + return hw_cons != fp->tx_pkt_cons; +} + +static int bnx2x_tx_int(struct bnx2x_fastpath *fp) { struct bnx2x *bp = fp->bp; struct netdev_queue *txq; u16 hw_cons, sw_cons, bd_cons = fp->tx_bd_cons; - int done = 0; #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) - return; + return -1; #endif - txq = netdev_get_tx_queue(bp->dev, fp->index - bp->num_rx_queues); + txq = netdev_get_tx_queue(bp->dev, fp->index); hw_cons = le16_to_cpu(*fp->tx_cons_sb); sw_cons = fp->tx_pkt_cons; @@ -942,7 +952,6 @@ static void bnx2x_tx_int(struct bnx2x_fastpath *fp) */ bd_cons = bnx2x_free_tx_pkt(bp, fp, pkt_cons); sw_cons++; - done++; } fp->tx_pkt_cons = sw_cons; @@ -964,8 +973,12 @@ static void bnx2x_tx_int(struct bnx2x_fastpath *fp) (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3)) netif_tx_wake_queue(txq); } + return 0; } +#ifdef BCM_CNIC +static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid); +#endif static void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe) @@ -1022,16 +1035,24 @@ static void bnx2x_sp_event(struct bnx2x_fastpath *fp, bnx2x_fp(bp, cid, state) = BNX2X_FP_STATE_CLOSED; break; +#ifdef BCM_CNIC + case (RAMROD_CMD_ID_ETH_CFC_DEL | BNX2X_STATE_OPEN): + DP(NETIF_MSG_IFDOWN, "got delete ramrod for CID %d\n", cid); + bnx2x_cnic_cfc_comp(bp, cid); + break; +#endif case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_OPEN): case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_DIAG): DP(NETIF_MSG_IFUP, "got set mac ramrod\n"); - bp->set_mac_pending = 0; + bp->set_mac_pending--; + smp_wmb(); break; case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_CLOSING_WAIT4_HALT): - case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_DISABLED): DP(NETIF_MSG_IFDOWN, "got (un)set mac ramrod\n"); + bp->set_mac_pending--; + smp_wmb(); break; default: @@ -1539,6 +1560,8 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) } else { rx_buf = &fp->rx_buf_ring[bd_cons]; skb = rx_buf->skb; + prefetch(skb); + prefetch((u8 *)skb + 256); len = le16_to_cpu(cqe->fast_path_cqe.pkt_len); pad = cqe->fast_path_cqe.placement_offset; @@ -1720,27 +1743,13 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie) if (unlikely(bp->panic)) return IRQ_HANDLED; #endif - /* Handle Rx or Tx according to MSI-X vector */ - if (fp->is_rx_queue) { - prefetch(fp->rx_cons_sb); - prefetch(&fp->status_blk->u_status_block.status_block_index); - napi_schedule(&bnx2x_fp(bp, fp->index, napi)); - - } else { - prefetch(fp->tx_cons_sb); - prefetch(&fp->status_blk->c_status_block.status_block_index); - - bnx2x_update_fpsb_idx(fp); - rmb(); - bnx2x_tx_int(fp); - - /* Re-enable interrupts */ - bnx2x_ack_sb(bp, fp->sb_id, USTORM_ID, - le16_to_cpu(fp->fp_u_idx), IGU_INT_NOP, 1); - bnx2x_ack_sb(bp, fp->sb_id, CSTORM_ID, - le16_to_cpu(fp->fp_c_idx), IGU_INT_ENABLE, 1); - } + /* Handle Rx and Tx according to MSI-X vector */ + prefetch(fp->rx_cons_sb); + prefetch(fp->tx_cons_sb); + prefetch(&fp->status_blk->u_status_block.status_block_index); + prefetch(&fp->status_blk->c_status_block.status_block_index); + napi_schedule(&bnx2x_fp(bp, fp->index, napi)); return IRQ_HANDLED; } @@ -1775,35 +1784,32 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) mask = 0x2 << fp->sb_id; if (status & mask) { - /* Handle Rx or Tx according to SB id */ - if (fp->is_rx_queue) { - prefetch(fp->rx_cons_sb); - prefetch(&fp->status_blk->u_status_block. - status_block_index); - - napi_schedule(&bnx2x_fp(bp, fp->index, napi)); - - } else { - prefetch(fp->tx_cons_sb); - prefetch(&fp->status_blk->c_status_block. - status_block_index); - - bnx2x_update_fpsb_idx(fp); - rmb(); - bnx2x_tx_int(fp); - - /* Re-enable interrupts */ - bnx2x_ack_sb(bp, fp->sb_id, USTORM_ID, - le16_to_cpu(fp->fp_u_idx), - IGU_INT_NOP, 1); - bnx2x_ack_sb(bp, fp->sb_id, CSTORM_ID, - le16_to_cpu(fp->fp_c_idx), - IGU_INT_ENABLE, 1); - } + /* Handle Rx and Tx according to SB id */ + prefetch(fp->rx_cons_sb); + prefetch(&fp->status_blk->u_status_block. + status_block_index); + prefetch(fp->tx_cons_sb); + prefetch(&fp->status_blk->c_status_block. + status_block_index); + napi_schedule(&bnx2x_fp(bp, fp->index, napi)); status &= ~mask; } } +#ifdef BCM_CNIC + mask = 0x2 << CNIC_SB_ID(bp); + if (status & (mask | 0x1)) { + struct cnic_ops *c_ops = NULL; + + rcu_read_lock(); + c_ops = rcu_dereference(bp->cnic_ops); + if (c_ops) + c_ops->cnic_handler(bp->cnic_data, NULL); + rcu_read_unlock(); + + status &= ~mask; + } +#endif if (unlikely(status & 0x1)) { queue_delayed_work(bnx2x_wq, &bp->sp_task, 0); @@ -2128,18 +2134,30 @@ static void bnx2x_calc_fc_adv(struct bnx2x *bp) static void bnx2x_link_report(struct bnx2x *bp) { - if (bp->state == BNX2X_STATE_DISABLED) { + if (bp->flags & MF_FUNC_DIS) { netif_carrier_off(bp->dev); printk(KERN_ERR PFX "%s NIC Link is Down\n", bp->dev->name); return; } if (bp->link_vars.link_up) { + u16 line_speed; + if (bp->state == BNX2X_STATE_OPEN) netif_carrier_on(bp->dev); printk(KERN_INFO PFX "%s NIC Link is Up, ", bp->dev->name); - printk("%d Mbps ", bp->link_vars.line_speed); + line_speed = bp->link_vars.line_speed; + if (IS_E1HMF(bp)) { + u16 vn_max_rate; + + vn_max_rate = + ((bp->mf_config & FUNC_MF_CFG_MAX_BW_MASK) >> + FUNC_MF_CFG_MAX_BW_SHIFT) * 100; + if (vn_max_rate < line_speed) + line_speed = vn_max_rate; + } + printk("%d Mbps ", line_speed); if (bp->link_vars.duplex == DUPLEX_FULL) printk("full duplex"); @@ -2304,8 +2322,14 @@ static void bnx2x_calc_vn_weight_sum(struct bnx2x *bp) } /* ... only if all min rates are zeros - disable fairness */ - if (all_zero) - bp->vn_weight_sum = 0; + if (all_zero) { + bp->cmng.flags.cmng_enables &= + ~CMNG_FLAGS_PER_PORT_FAIRNESS_VN; + DP(NETIF_MSG_IFUP, "All MIN values are zeroes" + " fairness will be disabled\n"); + } else + bp->cmng.flags.cmng_enables |= + CMNG_FLAGS_PER_PORT_FAIRNESS_VN; } static void bnx2x_init_vn_minmax(struct bnx2x *bp, int func) @@ -2324,17 +2348,14 @@ static void bnx2x_init_vn_minmax(struct bnx2x *bp, int func) } else { vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> FUNC_MF_CFG_MIN_BW_SHIFT) * 100; - /* If fairness is enabled (not all min rates are zeroes) and - if current min rate is zero - set it to 1. - This is a requirement of the algorithm. */ - if (bp->vn_weight_sum && (vn_min_rate == 0)) + /* If min rate is zero - set it to 1 */ + if (!vn_min_rate) vn_min_rate = DEF_MIN_RATE; vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >> FUNC_MF_CFG_MAX_BW_SHIFT) * 100; } - DP(NETIF_MSG_IFUP, - "func %d: vn_min_rate=%d vn_max_rate=%d vn_weight_sum=%d\n", + "func %d: vn_min_rate %d vn_max_rate %d vn_weight_sum %d\n", func, vn_min_rate, vn_max_rate, bp->vn_weight_sum); memset(&m_rs_vn, 0, sizeof(struct rate_shaping_vars_per_vn)); @@ -2405,8 +2426,7 @@ static void bnx2x_link_attn(struct bnx2x *bp) memset(&(pstats->mac_stx[0]), 0, sizeof(struct mac_stx)); } - if ((bp->state == BNX2X_STATE_OPEN) || - (bp->state == BNX2X_STATE_DISABLED)) + if (bp->state == BNX2X_STATE_OPEN) bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP); } @@ -2449,9 +2469,7 @@ static void bnx2x_link_attn(struct bnx2x *bp) static void bnx2x__link_status_update(struct bnx2x *bp) { - int func = BP_FUNC(bp); - - if (bp->state != BNX2X_STATE_OPEN) + if ((bp->state != BNX2X_STATE_OPEN) || (bp->flags & MF_FUNC_DIS)) return; bnx2x_link_status_update(&bp->link_params, &bp->link_vars); @@ -2461,7 +2479,6 @@ static void bnx2x__link_status_update(struct bnx2x *bp) else bnx2x_stats_handle(bp, STATS_EVENT_STOP); - bp->mf_config = SHMEM_RD(bp, mf_cfg.func_mf_config[func].config); bnx2x_calc_vn_weight_sum(bp); /* indicate link status */ @@ -2501,6 +2518,7 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command) u32 cnt = 1; u8 delay = CHIP_REV_IS_SLOW(bp) ? 100 : 10; + mutex_lock(&bp->fw_mb_mutex); SHMEM_WR(bp, func_mb[func].drv_mb_header, (command | seq)); DP(BNX2X_MSG_MCP, "wrote command (%x) to FW MB\n", (command | seq)); @@ -2510,8 +2528,8 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command) rc = SHMEM_RD(bp, func_mb[func].fw_mb_header); - /* Give the FW up to 2 second (200*10ms) */ - } while ((seq != (rc & FW_MSG_SEQ_NUMBER_MASK)) && (cnt++ < 200)); + /* Give the FW up to 5 second (500*10ms) */ + } while ((seq != (rc & FW_MSG_SEQ_NUMBER_MASK)) && (cnt++ < 500)); DP(BNX2X_MSG_MCP, "[after %d ms] read (%x) seq is (%x) from FW MB\n", cnt*delay, rc, seq); @@ -2525,32 +2543,23 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command) bnx2x_fw_dump(bp); rc = 0; } + mutex_unlock(&bp->fw_mb_mutex); return rc; } static void bnx2x_set_storm_rx_mode(struct bnx2x *bp); -static void bnx2x_set_mac_addr_e1h(struct bnx2x *bp, int set); +static void bnx2x_set_eth_mac_addr_e1h(struct bnx2x *bp, int set); static void bnx2x_set_rx_mode(struct net_device *dev); static void bnx2x_e1h_disable(struct bnx2x *bp) { int port = BP_PORT(bp); - int i; - - bp->rx_mode = BNX2X_RX_MODE_NONE; - bnx2x_set_storm_rx_mode(bp); netif_tx_disable(bp->dev); - bp->dev->trans_start = jiffies; /* prevent tx timeout */ REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0); - bnx2x_set_mac_addr_e1h(bp, 0); - - for (i = 0; i < MC_HASH_SIZE; i++) - REG_WR(bp, MC_HASH_OFFSET(bp, i), 0); - netif_carrier_off(bp->dev); } @@ -2560,13 +2569,13 @@ static void bnx2x_e1h_enable(struct bnx2x *bp) REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 1); - bnx2x_set_mac_addr_e1h(bp, 1); - /* Tx queue should be only reenabled */ netif_tx_wake_all_queues(bp->dev); - /* Initialize the receive filter. */ - bnx2x_set_rx_mode(bp->dev); + /* + * Should not call netif_carrier_on since it will be called if the link + * is up when checking for link state + */ } static void bnx2x_update_min_max(struct bnx2x *bp) @@ -2605,21 +2614,23 @@ static void bnx2x_update_min_max(struct bnx2x *bp) static void bnx2x_dcc_event(struct bnx2x *bp, u32 dcc_event) { - int func = BP_FUNC(bp); - DP(BNX2X_MSG_MCP, "dcc_event 0x%x\n", dcc_event); - bp->mf_config = SHMEM_RD(bp, mf_cfg.func_mf_config[func].config); if (dcc_event & DRV_STATUS_DCC_DISABLE_ENABLE_PF) { + /* + * This is the only place besides the function initialization + * where the bp->flags can change so it is done without any + * locks + */ if (bp->mf_config & FUNC_MF_CFG_FUNC_DISABLED) { DP(NETIF_MSG_IFDOWN, "mf_cfg function disabled\n"); - bp->state = BNX2X_STATE_DISABLED; + bp->flags |= MF_FUNC_DIS; bnx2x_e1h_disable(bp); } else { DP(NETIF_MSG_IFUP, "mf_cfg function enabled\n"); - bp->state = BNX2X_STATE_OPEN; + bp->flags &= ~MF_FUNC_DIS; bnx2x_e1h_enable(bp); } @@ -2638,11 +2649,40 @@ static void bnx2x_dcc_event(struct bnx2x *bp, u32 dcc_event) bnx2x_fw_command(bp, DRV_MSG_CODE_DCC_OK); } +/* must be called under the spq lock */ +static inline struct eth_spe *bnx2x_sp_get_next(struct bnx2x *bp) +{ + struct eth_spe *next_spe = bp->spq_prod_bd; + + if (bp->spq_prod_bd == bp->spq_last_bd) { + bp->spq_prod_bd = bp->spq; + bp->spq_prod_idx = 0; + DP(NETIF_MSG_TIMER, "end of spq\n"); + } else { + bp->spq_prod_bd++; + bp->spq_prod_idx++; + } + return next_spe; +} + +/* must be called under the spq lock */ +static inline void bnx2x_sp_prod_update(struct bnx2x *bp) +{ + int func = BP_FUNC(bp); + + /* Make sure that BD data is updated before writing the producer */ + wmb(); + + REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func), + bp->spq_prod_idx); + mmiowb(); +} + /* the slow path queue is odd since completions arrive on the fastpath ring */ static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, u32 data_hi, u32 data_lo, int common) { - int func = BP_FUNC(bp); + struct eth_spe *spe; DP(BNX2X_MSG_SP/*NETIF_MSG_TIMER*/, "SPQE (%x:%x) command %d hw_cid %x data (%x:%x) left %x\n", @@ -2664,38 +2704,23 @@ static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, return -EBUSY; } + spe = bnx2x_sp_get_next(bp); + /* CID needs port number to be encoded int it */ - bp->spq_prod_bd->hdr.conn_and_cmd_data = + spe->hdr.conn_and_cmd_data = cpu_to_le32(((command << SPE_HDR_CMD_ID_SHIFT) | HW_CID(bp, cid))); - bp->spq_prod_bd->hdr.type = cpu_to_le16(ETH_CONNECTION_TYPE); + spe->hdr.type = cpu_to_le16(ETH_CONNECTION_TYPE); if (common) - bp->spq_prod_bd->hdr.type |= + spe->hdr.type |= cpu_to_le16((1 << SPE_HDR_COMMON_RAMROD_SHIFT)); - bp->spq_prod_bd->data.mac_config_addr.hi = cpu_to_le32(data_hi); - bp->spq_prod_bd->data.mac_config_addr.lo = cpu_to_le32(data_lo); + spe->data.mac_config_addr.hi = cpu_to_le32(data_hi); + spe->data.mac_config_addr.lo = cpu_to_le32(data_lo); bp->spq_left--; - if (bp->spq_prod_bd == bp->spq_last_bd) { - bp->spq_prod_bd = bp->spq; - bp->spq_prod_idx = 0; - DP(NETIF_MSG_TIMER, "end of spq\n"); - - } else { - bp->spq_prod_bd++; - bp->spq_prod_idx++; - } - - /* Make sure that BD data is updated before writing the producer */ - wmb(); - - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func), - bp->spq_prod_idx); - - mmiowb(); - + bnx2x_sp_prod_update(bp); spin_unlock_bh(&bp->spq_lock); return 0; } @@ -3024,6 +3049,8 @@ static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn) int func = BP_FUNC(bp); REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0); + bp->mf_config = SHMEM_RD(bp, + mf_cfg.func_mf_config[func].config); val = SHMEM_RD(bp, func_mb[func].drv_status); if (val & DRV_STATUS_DCC_EVENT_MASK) bnx2x_dcc_event(bp, @@ -3227,6 +3254,17 @@ static irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance) return IRQ_HANDLED; #endif +#ifdef BCM_CNIC + { + struct cnic_ops *c_ops; + + rcu_read_lock(); + c_ops = rcu_dereference(bp->cnic_ops); + if (c_ops) + c_ops->cnic_handler(bp->cnic_data, NULL); + rcu_read_unlock(); + } +#endif queue_delayed_work(bnx2x_wq, &bp->sp_task, 0); return IRQ_HANDLED; @@ -3958,7 +3996,7 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp) estats->no_buff_discard_hi = 0; estats->no_buff_discard_lo = 0; - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; int cl_id = fp->cl_id; struct tstorm_per_client_stats *tclient = @@ -4175,7 +4213,7 @@ static void bnx2x_net_stats_update(struct bnx2x *bp) nstats->tx_bytes = bnx2x_hilo(&estats->total_bytes_transmitted_hi); nstats->rx_dropped = estats->mac_discard; - for_each_rx_queue(bp, i) + for_each_queue(bp, i) nstats->rx_dropped += le32_to_cpu(bp->fp[i].old_tclient.checksum_discard); @@ -4229,7 +4267,7 @@ static void bnx2x_drv_stats_update(struct bnx2x *bp) estats->rx_err_discard_pkt = 0; estats->rx_skb_alloc_failed = 0; estats->hw_csum_err = 0; - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_eth_q_stats *qstats = &bp->fp[i].eth_q_stats; estats->driver_xoff += qstats->driver_xoff; @@ -4260,7 +4298,7 @@ static void bnx2x_stats_update(struct bnx2x *bp) if (bp->msglevel & NETIF_MSG_TIMER) { struct bnx2x_fastpath *fp0_rx = bp->fp; - struct bnx2x_fastpath *fp0_tx = &(bp->fp[bp->num_rx_queues]); + struct bnx2x_fastpath *fp0_tx = bp->fp; struct tstorm_per_client_stats *old_tclient = &bp->fp->old_tclient; struct bnx2x_eth_q_stats *qstats = &bp->fp->eth_q_stats; @@ -4640,8 +4678,7 @@ static void bnx2x_timer(unsigned long data) } } - if ((bp->state == BNX2X_STATE_OPEN) || - (bp->state == BNX2X_STATE_DISABLED)) + if (bp->state == BNX2X_STATE_OPEN) bnx2x_stats_handle(bp, STATS_EVENT_UPDATE); timer_restart: @@ -4860,21 +4897,21 @@ static void bnx2x_update_coalesce(struct bnx2x *bp) REG_WR8(bp, BAR_CSTRORM_INTMEM + CSTORM_SB_HC_TIMEOUT_U_OFFSET(port, sb_id, U_SB_ETH_RX_CQ_INDEX), - bp->rx_ticks/12); + bp->rx_ticks/(4 * BNX2X_BTR)); REG_WR16(bp, BAR_CSTRORM_INTMEM + CSTORM_SB_HC_DISABLE_U_OFFSET(port, sb_id, U_SB_ETH_RX_CQ_INDEX), - (bp->rx_ticks/12) ? 0 : 1); + (bp->rx_ticks/(4 * BNX2X_BTR)) ? 0 : 1); /* HC_INDEX_C_ETH_TX_CQ_CONS */ REG_WR8(bp, BAR_CSTRORM_INTMEM + CSTORM_SB_HC_TIMEOUT_C_OFFSET(port, sb_id, C_SB_ETH_TX_CQ_INDEX), - bp->tx_ticks/12); + bp->tx_ticks/(4 * BNX2X_BTR)); REG_WR16(bp, BAR_CSTRORM_INTMEM + CSTORM_SB_HC_DISABLE_C_OFFSET(port, sb_id, C_SB_ETH_TX_CQ_INDEX), - (bp->tx_ticks/12) ? 0 : 1); + (bp->tx_ticks/(4 * BNX2X_BTR)) ? 0 : 1); } } @@ -4916,7 +4953,7 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) if (bp->flags & TPA_ENABLE_FLAG) { - for_each_rx_queue(bp, j) { + for_each_queue(bp, j) { struct bnx2x_fastpath *fp = &bp->fp[j]; for (i = 0; i < max_agg_queues; i++) { @@ -4939,16 +4976,13 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) } } - for_each_rx_queue(bp, j) { + for_each_queue(bp, j) { struct bnx2x_fastpath *fp = &bp->fp[j]; fp->rx_bd_cons = 0; fp->rx_cons_sb = BNX2X_RX_SB_INDEX; fp->rx_bd_cons_sb = BNX2X_RX_SB_BD_INDEX; - /* Mark queue as Rx */ - fp->is_rx_queue = 1; - /* "next page" elements initialization */ /* SGE ring */ for (i = 1; i <= NUM_RX_SGE_PAGES; i++) { @@ -5054,7 +5088,7 @@ static void bnx2x_init_tx_ring(struct bnx2x *bp) { int i, j; - for_each_tx_queue(bp, j) { + for_each_queue(bp, j) { struct bnx2x_fastpath *fp = &bp->fp[j]; for (i = 1; i <= NUM_TX_RINGS; i++) { @@ -5080,10 +5114,6 @@ static void bnx2x_init_tx_ring(struct bnx2x *bp) fp->tx_cons_sb = BNX2X_TX_SB_INDEX; fp->tx_pkt = 0; } - - /* clean tx statistics */ - for_each_rx_queue(bp, i) - bnx2x_fp(bp, i, tx_pkt) = 0; } static void bnx2x_init_sp_ring(struct bnx2x *bp) @@ -5112,7 +5142,8 @@ static void bnx2x_init_context(struct bnx2x *bp) { int i; - for_each_rx_queue(bp, i) { + /* Rx */ + for_each_queue(bp, i) { struct eth_context *context = bnx2x_sp(bp, context[i].eth); struct bnx2x_fastpath *fp = &bp->fp[i]; u8 cl_id = fp->cl_id; @@ -5164,10 +5195,11 @@ static void bnx2x_init_context(struct bnx2x *bp) ETH_CONNECTION_TYPE); } - for_each_tx_queue(bp, i) { + /* Tx */ + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; struct eth_context *context = - bnx2x_sp(bp, context[i - bp->num_rx_queues].eth); + bnx2x_sp(bp, context[i].eth); context->cstorm_st_context.sb_index_number = C_SB_ETH_TX_CQ_INDEX; @@ -5195,7 +5227,7 @@ static void bnx2x_init_ind_table(struct bnx2x *bp) for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++) REG_WR8(bp, BAR_TSTRORM_INTMEM + TSTORM_INDIRECTION_TABLE_OFFSET(func) + i, - bp->fp->cl_id + (i % bp->num_rx_queues)); + bp->fp->cl_id + (i % bp->num_queues)); } static void bnx2x_set_client_config(struct bnx2x *bp) @@ -5235,7 +5267,7 @@ static void bnx2x_set_storm_rx_mode(struct bnx2x *bp) { struct tstorm_eth_mac_filter_config tstorm_mac_filter = {0}; int mode = bp->rx_mode; - int mask = (1 << BP_L_ID(bp)); + int mask = bp->rx_mode_cl_mask; int func = BP_FUNC(bp); int port = BP_PORT(bp); int i; @@ -5348,6 +5380,7 @@ static void bnx2x_init_internal_func(struct bnx2x *bp) (*(u32 *)&tstorm_config)); bp->rx_mode = BNX2X_RX_MODE_NONE; /* no rx until link is up */ + bp->rx_mode_cl_mask = (1 << BP_L_ID(bp)); bnx2x_set_storm_rx_mode(bp); for_each_queue(bp, i) { @@ -5438,7 +5471,7 @@ static void bnx2x_init_internal_func(struct bnx2x *bp) min((u32)(min((u32)8, (u32)MAX_SKB_FRAGS) * SGE_PAGE_SIZE * PAGES_PER_SGE), (u32)0xffff); - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; REG_WR(bp, BAR_USTRORM_INTMEM + @@ -5473,7 +5506,7 @@ static void bnx2x_init_internal_func(struct bnx2x *bp) rx_pause.cqe_thr_high = 350; rx_pause.sge_thr_high = 0; - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; if (!fp->disable_tpa) { @@ -5504,20 +5537,18 @@ static void bnx2x_init_internal_func(struct bnx2x *bp) bp->link_vars.line_speed = SPEED_10000; bnx2x_init_port_minmax(bp); + if (!BP_NOMCP(bp)) + bp->mf_config = + SHMEM_RD(bp, mf_cfg.func_mf_config[func].config); bnx2x_calc_vn_weight_sum(bp); for (vn = VN_0; vn < E1HVN_MAX; vn++) bnx2x_init_vn_minmax(bp, 2*vn + port); /* Enable rate shaping and fairness */ - bp->cmng.flags.cmng_enables = + bp->cmng.flags.cmng_enables |= CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN; - if (bp->vn_weight_sum) - bp->cmng.flags.cmng_enables |= - CMNG_FLAGS_PER_PORT_FAIRNESS_VN; - else - DP(NETIF_MSG_IFUP, "All MIN values are zeroes" - " fairness will be disabled\n"); + } else { /* rate shaping and fairness are disabled */ DP(NETIF_MSG_IFUP, @@ -5565,10 +5596,11 @@ static void bnx2x_nic_init(struct bnx2x *bp, u32 load_code) fp->state = BNX2X_FP_STATE_CLOSED; fp->index = i; fp->cl_id = BP_L_ID(bp) + i; +#ifdef BCM_CNIC + fp->sb_id = fp->cl_id + 1; +#else fp->sb_id = fp->cl_id; - /* Suitable Rx and Tx SBs are served by the same client */ - if (i >= bp->num_rx_queues) - fp->cl_id -= bp->num_rx_queues; +#endif DP(NETIF_MSG_IFUP, "queue[%d]: bnx2x_init_sb(%p,%p) cl_id %d sb %d\n", i, bp, fp->status_blk, fp->cl_id, fp->sb_id); @@ -5867,7 +5899,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) msleep(50); bnx2x_init_block(bp, BRB1_BLOCK, COMMON_STAGE); bnx2x_init_block(bp, PRS_BLOCK, COMMON_STAGE); -#ifndef BCM_ISCSI +#ifndef BCM_CNIC /* set NIC mode */ REG_WR(bp, PRS_REG_NIC_MODE, 1); #endif @@ -6006,6 +6038,9 @@ static void bnx2x_setup_fan_failure_detection(struct bnx2x *bp) static int bnx2x_init_common(struct bnx2x *bp) { u32 val, i; +#ifdef BCM_CNIC + u32 wb_write[2]; +#endif DP(BNX2X_MSG_MCP, "starting common init func %d\n", BP_FUNC(bp)); @@ -6048,7 +6083,7 @@ static int bnx2x_init_common(struct bnx2x *bp) #endif REG_WR(bp, PXP2_REG_RQ_CDU_P_SIZE, 2); -#ifdef BCM_ISCSI +#ifdef BCM_CNIC REG_WR(bp, PXP2_REG_RQ_TM_P_SIZE, 5); REG_WR(bp, PXP2_REG_RQ_QM_P_SIZE, 5); REG_WR(bp, PXP2_REG_RQ_SRC_P_SIZE, 5); @@ -6091,11 +6126,26 @@ static int bnx2x_init_common(struct bnx2x *bp) bnx2x_read_dmae(bp, USEM_REG_PASSIVE_BUFFER, 3); bnx2x_init_block(bp, QM_BLOCK, COMMON_STAGE); + +#ifdef BCM_CNIC + wb_write[0] = 0; + wb_write[1] = 0; + for (i = 0; i < 64; i++) { + REG_WR(bp, QM_REG_BASEADDR + i*4, 1024 * 4 * (i%16)); + bnx2x_init_ind_wr(bp, QM_REG_PTRTBL + i*8, wb_write, 2); + + if (CHIP_IS_E1H(bp)) { + REG_WR(bp, QM_REG_BASEADDR_EXT_A + i*4, 1024*4*(i%16)); + bnx2x_init_ind_wr(bp, QM_REG_PTRTBL_EXT_A + i*8, + wb_write, 2); + } + } +#endif /* soft reset pulse */ REG_WR(bp, QM_REG_SOFT_RESET, 1); REG_WR(bp, QM_REG_SOFT_RESET, 0); -#ifdef BCM_ISCSI +#ifdef BCM_CNIC bnx2x_init_block(bp, TIMERS_BLOCK, COMMON_STAGE); #endif @@ -6109,8 +6159,10 @@ static int bnx2x_init_common(struct bnx2x *bp) bnx2x_init_block(bp, BRB1_BLOCK, COMMON_STAGE); bnx2x_init_block(bp, PRS_BLOCK, COMMON_STAGE); REG_WR(bp, PRS_REG_A_PRSU_20, 0xf); +#ifndef BCM_CNIC /* set NIC mode */ REG_WR(bp, PRS_REG_NIC_MODE, 1); +#endif if (CHIP_IS_E1H(bp)) REG_WR(bp, PRS_REG_E1HOV_MODE, IS_E1HMF(bp)); @@ -6145,6 +6197,18 @@ static int bnx2x_init_common(struct bnx2x *bp) /* TODO: replace with something meaningful */ } bnx2x_init_block(bp, SRCH_BLOCK, COMMON_STAGE); +#ifdef BCM_CNIC + REG_WR(bp, SRC_REG_KEYSEARCH_0, 0x63285672); + REG_WR(bp, SRC_REG_KEYSEARCH_1, 0x24b8f2cc); + REG_WR(bp, SRC_REG_KEYSEARCH_2, 0x223aef9b); + REG_WR(bp, SRC_REG_KEYSEARCH_3, 0x26001e3a); + REG_WR(bp, SRC_REG_KEYSEARCH_4, 0x7ae91116); + REG_WR(bp, SRC_REG_KEYSEARCH_5, 0x5ce5230b); + REG_WR(bp, SRC_REG_KEYSEARCH_6, 0x298d8adf); + REG_WR(bp, SRC_REG_KEYSEARCH_7, 0x6eb0ff09); + REG_WR(bp, SRC_REG_KEYSEARCH_8, 0x1830f82f); + REG_WR(bp, SRC_REG_KEYSEARCH_9, 0x01e46be7); +#endif REG_WR(bp, SRC_REG_SOFT_RST, 0); if (sizeof(union cdu_context) != 1024) @@ -6261,38 +6325,14 @@ static int bnx2x_init_port(struct bnx2x *bp) bnx2x_init_block(bp, TCM_BLOCK, init_stage); bnx2x_init_block(bp, UCM_BLOCK, init_stage); bnx2x_init_block(bp, CCM_BLOCK, init_stage); -#ifdef BCM_ISCSI - /* Port0 1 - * Port1 385 */ - i++; - wb_write[0] = ONCHIP_ADDR1(bp->timers_mapping); - wb_write[1] = ONCHIP_ADDR2(bp->timers_mapping); - REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); - REG_WR(bp, PXP2_REG_PSWRQ_TM0_L2P + func*4, PXP_ONE_ILT(i)); - - /* Port0 2 - * Port1 386 */ - i++; - wb_write[0] = ONCHIP_ADDR1(bp->qm_mapping); - wb_write[1] = ONCHIP_ADDR2(bp->qm_mapping); - REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); - REG_WR(bp, PXP2_REG_PSWRQ_QM0_L2P + func*4, PXP_ONE_ILT(i)); - - /* Port0 3 - * Port1 387 */ - i++; - wb_write[0] = ONCHIP_ADDR1(bp->t1_mapping); - wb_write[1] = ONCHIP_ADDR2(bp->t1_mapping); - REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); - REG_WR(bp, PXP2_REG_PSWRQ_SRC0_L2P + func*4, PXP_ONE_ILT(i)); -#endif bnx2x_init_block(bp, XCM_BLOCK, init_stage); -#ifdef BCM_ISCSI - REG_WR(bp, TM_REG_LIN0_SCAN_TIME + func*4, 1024/64*20); - REG_WR(bp, TM_REG_LIN0_MAX_ACTIVE_CID + func*4, 31); +#ifdef BCM_CNIC + REG_WR(bp, QM_REG_CONNNUM_0 + port*4, 1024/16 - 1); bnx2x_init_block(bp, TIMERS_BLOCK, init_stage); + REG_WR(bp, TM_REG_LIN0_SCAN_TIME + port*4, 20); + REG_WR(bp, TM_REG_LIN0_MAX_ACTIVE_CID + port*4, 31); #endif bnx2x_init_block(bp, DQ_BLOCK, init_stage); @@ -6350,18 +6390,8 @@ static int bnx2x_init_port(struct bnx2x *bp) msleep(5); REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0); -#ifdef BCM_ISCSI - /* tell the searcher where the T2 table is */ - REG_WR(bp, SRC_REG_COUNTFREE0 + func*4, 16*1024/64); - - wb_write[0] = U64_LO(bp->t2_mapping); - wb_write[1] = U64_HI(bp->t2_mapping); - REG_WR_DMAE(bp, SRC_REG_FIRSTFREE0 + func*4, wb_write, 2); - wb_write[0] = U64_LO((u64)bp->t2_mapping + 16*1024 - 64); - wb_write[1] = U64_HI((u64)bp->t2_mapping + 16*1024 - 64); - REG_WR_DMAE(bp, SRC_REG_LASTFREE0 + func*4, wb_write, 2); - - REG_WR(bp, SRC_REG_NUMBER_HASH_BITS0 + func*4, 10); +#ifdef BCM_CNIC + bnx2x_init_block(bp, SRCH_BLOCK, init_stage); #endif bnx2x_init_block(bp, CDU_BLOCK, init_stage); bnx2x_init_block(bp, CFC_BLOCK, init_stage); @@ -6470,7 +6500,12 @@ static int bnx2x_init_port(struct bnx2x *bp) #define PXP_ONE_ILT(x) (((x) << 10) | x) #define PXP_ILT_RANGE(f, l) (((l) << 10) | f) +#ifdef BCM_CNIC +#define CNIC_ILT_LINES 127 +#define CNIC_CTX_PER_ILT 16 +#else #define CNIC_ILT_LINES 0 +#endif static void bnx2x_ilt_wr(struct bnx2x *bp, u32 index, dma_addr_t addr) { @@ -6509,6 +6544,46 @@ static int bnx2x_init_func(struct bnx2x *bp) REG_WR(bp, PXP2_REG_PSWRQ_CDU0_L2P + func*4, PXP_ILT_RANGE(i, i + CNIC_ILT_LINES)); +#ifdef BCM_CNIC + i += 1 + CNIC_ILT_LINES; + bnx2x_ilt_wr(bp, i, bp->timers_mapping); + if (CHIP_IS_E1(bp)) + REG_WR(bp, PXP2_REG_PSWRQ_TM0_L2P + func*4, PXP_ONE_ILT(i)); + else { + REG_WR(bp, PXP2_REG_RQ_TM_FIRST_ILT, i); + REG_WR(bp, PXP2_REG_RQ_TM_LAST_ILT, i); + } + + i++; + bnx2x_ilt_wr(bp, i, bp->qm_mapping); + if (CHIP_IS_E1(bp)) + REG_WR(bp, PXP2_REG_PSWRQ_QM0_L2P + func*4, PXP_ONE_ILT(i)); + else { + REG_WR(bp, PXP2_REG_RQ_QM_FIRST_ILT, i); + REG_WR(bp, PXP2_REG_RQ_QM_LAST_ILT, i); + } + + i++; + bnx2x_ilt_wr(bp, i, bp->t1_mapping); + if (CHIP_IS_E1(bp)) + REG_WR(bp, PXP2_REG_PSWRQ_SRC0_L2P + func*4, PXP_ONE_ILT(i)); + else { + REG_WR(bp, PXP2_REG_RQ_SRC_FIRST_ILT, i); + REG_WR(bp, PXP2_REG_RQ_SRC_LAST_ILT, i); + } + + /* tell the searcher where the T2 table is */ + REG_WR(bp, SRC_REG_COUNTFREE0 + port*4, 16*1024/64); + + bnx2x_wb_wr(bp, SRC_REG_FIRSTFREE0 + port*16, + U64_LO(bp->t2_mapping), U64_HI(bp->t2_mapping)); + + bnx2x_wb_wr(bp, SRC_REG_LASTFREE0 + port*16, + U64_LO((u64)bp->t2_mapping + 16*1024 - 64), + U64_HI((u64)bp->t2_mapping + 16*1024 - 64)); + + REG_WR(bp, SRC_REG_NUMBER_HASH_BITS0 + port*4, 10); +#endif if (CHIP_IS_E1H(bp)) { bnx2x_init_block(bp, MISC_BLOCK, FUNC0_STAGE + func); @@ -6593,6 +6668,9 @@ static int bnx2x_init_hw(struct bnx2x *bp, u32 load_code) bnx2x_zero_def_sb(bp); for_each_queue(bp, i) bnx2x_zero_sb(bp, BP_L_ID(bp) + i); +#ifdef BCM_CNIC + bnx2x_zero_sb(bp, BP_L_ID(bp) + i); +#endif init_hw_err: bnx2x_gunzip_end(bp); @@ -6632,7 +6710,7 @@ static void bnx2x_free_mem(struct bnx2x *bp) sizeof(struct host_status_block)); } /* Rx */ - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { /* fastpath rx rings: rx_buf rx_desc rx_comp */ BNX2X_FREE(bnx2x_fp(bp, i, rx_buf_ring)); @@ -6652,7 +6730,7 @@ static void bnx2x_free_mem(struct bnx2x *bp) BCM_PAGE_SIZE * NUM_RX_SGE_PAGES); } /* Tx */ - for_each_tx_queue(bp, i) { + for_each_queue(bp, i) { /* fastpath tx rings: tx_buf tx_desc */ BNX2X_FREE(bnx2x_fp(bp, i, tx_buf_ring)); @@ -6668,11 +6746,13 @@ static void bnx2x_free_mem(struct bnx2x *bp) BNX2X_PCI_FREE(bp->slowpath, bp->slowpath_mapping, sizeof(struct bnx2x_slowpath)); -#ifdef BCM_ISCSI +#ifdef BCM_CNIC BNX2X_PCI_FREE(bp->t1, bp->t1_mapping, 64*1024); BNX2X_PCI_FREE(bp->t2, bp->t2_mapping, 16*1024); BNX2X_PCI_FREE(bp->timers, bp->timers_mapping, 8*1024); BNX2X_PCI_FREE(bp->qm, bp->qm_mapping, 128*1024); + BNX2X_PCI_FREE(bp->cnic_sb, bp->cnic_sb_mapping, + sizeof(struct host_status_block)); #endif BNX2X_PCI_FREE(bp->spq, bp->spq_mapping, BCM_PAGE_SIZE); @@ -6712,7 +6792,7 @@ static int bnx2x_alloc_mem(struct bnx2x *bp) sizeof(struct host_status_block)); } /* Rx */ - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { /* fastpath rx rings: rx_buf rx_desc rx_comp */ BNX2X_ALLOC(bnx2x_fp(bp, i, rx_buf_ring), @@ -6734,7 +6814,7 @@ static int bnx2x_alloc_mem(struct bnx2x *bp) BCM_PAGE_SIZE * NUM_RX_SGE_PAGES); } /* Tx */ - for_each_tx_queue(bp, i) { + for_each_queue(bp, i) { /* fastpath tx rings: tx_buf tx_desc */ BNX2X_ALLOC(bnx2x_fp(bp, i, tx_buf_ring), @@ -6751,32 +6831,26 @@ static int bnx2x_alloc_mem(struct bnx2x *bp) BNX2X_PCI_ALLOC(bp->slowpath, &bp->slowpath_mapping, sizeof(struct bnx2x_slowpath)); -#ifdef BCM_ISCSI +#ifdef BCM_CNIC BNX2X_PCI_ALLOC(bp->t1, &bp->t1_mapping, 64*1024); - /* Initialize T1 */ - for (i = 0; i < 64*1024; i += 64) { - *(u64 *)((char *)bp->t1 + i + 56) = 0x0UL; - *(u64 *)((char *)bp->t1 + i + 3) = 0x0UL; - } - /* allocate searcher T2 table we allocate 1/4 of alloc num for T2 (which is not entered into the ILT) */ BNX2X_PCI_ALLOC(bp->t2, &bp->t2_mapping, 16*1024); - /* Initialize T2 */ + /* Initialize T2 (for 1024 connections) */ for (i = 0; i < 16*1024; i += 64) - * (u64 *)((char *)bp->t2 + i + 56) = bp->t2_mapping + i + 64; - - /* now fixup the last line in the block to point to the next block */ - *(u64 *)((char *)bp->t2 + 1024*16-8) = bp->t2_mapping; + *(u64 *)((char *)bp->t2 + i + 56) = bp->t2_mapping + i + 64; - /* Timer block array (MAX_CONN*8) phys uncached for now 1024 conns */ + /* Timer block array (8*MAX_CONN) phys uncached for now 1024 conns */ BNX2X_PCI_ALLOC(bp->timers, &bp->timers_mapping, 8*1024); /* QM queues (128*MAX_CONN) */ BNX2X_PCI_ALLOC(bp->qm, &bp->qm_mapping, 128*1024); + + BNX2X_PCI_ALLOC(bp->cnic_sb, &bp->cnic_sb_mapping, + sizeof(struct host_status_block)); #endif /* Slow path ring */ @@ -6796,7 +6870,7 @@ static void bnx2x_free_tx_skbs(struct bnx2x *bp) { int i; - for_each_tx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; u16 bd_cons = fp->tx_bd_cons; @@ -6814,7 +6888,7 @@ static void bnx2x_free_rx_skbs(struct bnx2x *bp) { int i, j; - for_each_rx_queue(bp, j) { + for_each_queue(bp, j) { struct bnx2x_fastpath *fp = &bp->fp[j]; for (i = 0; i < NUM_RX_BD; i++) { @@ -6852,6 +6926,9 @@ static void bnx2x_free_msix_irqs(struct bnx2x *bp) DP(NETIF_MSG_IFDOWN, "released sp irq (%d)\n", bp->msix_table[0].vector); +#ifdef BCM_CNIC + offset++; +#endif for_each_queue(bp, i) { DP(NETIF_MSG_IFDOWN, "about to release fp #%d->%d irq " "state %x\n", i, bp->msix_table[i + offset].vector, @@ -6885,6 +6962,12 @@ static int bnx2x_enable_msix(struct bnx2x *bp) bp->msix_table[0].entry = igu_vec; DP(NETIF_MSG_IFUP, "msix_table[0].entry = %d (slowpath)\n", igu_vec); +#ifdef BCM_CNIC + igu_vec = BP_L_ID(bp) + offset; + bp->msix_table[1].entry = igu_vec; + DP(NETIF_MSG_IFUP, "msix_table[1].entry = %d (CNIC)\n", igu_vec); + offset++; +#endif for_each_queue(bp, i) { igu_vec = BP_L_ID(bp) + offset + i; bp->msix_table[i + offset].entry = igu_vec; @@ -6915,14 +6998,13 @@ static int bnx2x_req_msix_irqs(struct bnx2x *bp) return -EBUSY; } +#ifdef BCM_CNIC + offset++; +#endif for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; - - if (i < bp->num_rx_queues) - sprintf(fp->name, "%s-rx-%d", bp->dev->name, i); - else - sprintf(fp->name, "%s-tx-%d", - bp->dev->name, i - bp->num_rx_queues); + snprintf(fp->name, sizeof(fp->name), "%s-fp-%d", + bp->dev->name, i); rc = request_irq(bp->msix_table[i + offset].vector, bnx2x_msix_fp_int, 0, fp->name, fp); @@ -6981,7 +7063,7 @@ static void bnx2x_napi_enable(struct bnx2x *bp) { int i; - for_each_rx_queue(bp, i) + for_each_queue(bp, i) napi_enable(&bnx2x_fp(bp, i, napi)); } @@ -6989,7 +7071,7 @@ static void bnx2x_napi_disable(struct bnx2x *bp) { int i; - for_each_rx_queue(bp, i) + for_each_queue(bp, i) napi_disable(&bnx2x_fp(bp, i, napi)); } @@ -7015,14 +7097,25 @@ static void bnx2x_netif_stop(struct bnx2x *bp, int disable_hw) bnx2x_int_disable_sync(bp, disable_hw); bnx2x_napi_disable(bp); netif_tx_disable(bp->dev); - bp->dev->trans_start = jiffies; /* prevent tx timeout */ } /* * Init service functions */ -static void bnx2x_set_mac_addr_e1(struct bnx2x *bp, int set) +/** + * Sets a MAC in a CAM for a few L2 Clients for E1 chip + * + * @param bp driver descriptor + * @param set set or clear an entry (1 or 0) + * @param mac pointer to a buffer containing a MAC + * @param cl_bit_vec bit vector of clients to register a MAC for + * @param cam_offset offset in a CAM to use + * @param with_bcast set broadcast MAC as well + */ +static void bnx2x_set_mac_addr_e1_gen(struct bnx2x *bp, int set, u8 *mac, + u32 cl_bit_vec, u8 cam_offset, + u8 with_bcast) { struct mac_configuration_cmd *config = bnx2x_sp(bp, mac_config); int port = BP_PORT(bp); @@ -7031,25 +7124,25 @@ static void bnx2x_set_mac_addr_e1(struct bnx2x *bp, int set) * unicasts 0-31:port0 32-63:port1 * multicast 64-127:port0 128-191:port1 */ - config->hdr.length = 2; - config->hdr.offset = port ? 32 : 0; - config->hdr.client_id = bp->fp->cl_id; + config->hdr.length = 1 + (with_bcast ? 1 : 0); + config->hdr.offset = cam_offset; + config->hdr.client_id = 0xff; config->hdr.reserved1 = 0; /* primary MAC */ config->config_table[0].cam_entry.msb_mac_addr = - swab16(*(u16 *)&bp->dev->dev_addr[0]); + swab16(*(u16 *)&mac[0]); config->config_table[0].cam_entry.middle_mac_addr = - swab16(*(u16 *)&bp->dev->dev_addr[2]); + swab16(*(u16 *)&mac[2]); config->config_table[0].cam_entry.lsb_mac_addr = - swab16(*(u16 *)&bp->dev->dev_addr[4]); + swab16(*(u16 *)&mac[4]); config->config_table[0].cam_entry.flags = cpu_to_le16(port); if (set) config->config_table[0].target_table_entry.flags = 0; else CAM_INVALIDATE(config->config_table[0]); config->config_table[0].target_table_entry.clients_bit_vector = - cpu_to_le32(1 << BP_L_ID(bp)); + cpu_to_le32(cl_bit_vec); config->config_table[0].target_table_entry.vlan_id = 0; DP(NETIF_MSG_IFUP, "%s MAC (%04x:%04x:%04x)\n", @@ -7059,47 +7152,58 @@ static void bnx2x_set_mac_addr_e1(struct bnx2x *bp, int set) config->config_table[0].cam_entry.lsb_mac_addr); /* broadcast */ - config->config_table[1].cam_entry.msb_mac_addr = cpu_to_le16(0xffff); - config->config_table[1].cam_entry.middle_mac_addr = cpu_to_le16(0xffff); - config->config_table[1].cam_entry.lsb_mac_addr = cpu_to_le16(0xffff); - config->config_table[1].cam_entry.flags = cpu_to_le16(port); - if (set) - config->config_table[1].target_table_entry.flags = - TSTORM_CAM_TARGET_TABLE_ENTRY_BROADCAST; - else - CAM_INVALIDATE(config->config_table[1]); - config->config_table[1].target_table_entry.clients_bit_vector = - cpu_to_le32(1 << BP_L_ID(bp)); - config->config_table[1].target_table_entry.vlan_id = 0; + if (with_bcast) { + config->config_table[1].cam_entry.msb_mac_addr = + cpu_to_le16(0xffff); + config->config_table[1].cam_entry.middle_mac_addr = + cpu_to_le16(0xffff); + config->config_table[1].cam_entry.lsb_mac_addr = + cpu_to_le16(0xffff); + config->config_table[1].cam_entry.flags = cpu_to_le16(port); + if (set) + config->config_table[1].target_table_entry.flags = + TSTORM_CAM_TARGET_TABLE_ENTRY_BROADCAST; + else + CAM_INVALIDATE(config->config_table[1]); + config->config_table[1].target_table_entry.clients_bit_vector = + cpu_to_le32(cl_bit_vec); + config->config_table[1].target_table_entry.vlan_id = 0; + } bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(bnx2x_sp_mapping(bp, mac_config)), U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0); } -static void bnx2x_set_mac_addr_e1h(struct bnx2x *bp, int set) +/** + * Sets a MAC in a CAM for a few L2 Clients for E1H chip + * + * @param bp driver descriptor + * @param set set or clear an entry (1 or 0) + * @param mac pointer to a buffer containing a MAC + * @param cl_bit_vec bit vector of clients to register a MAC for + * @param cam_offset offset in a CAM to use + */ +static void bnx2x_set_mac_addr_e1h_gen(struct bnx2x *bp, int set, u8 *mac, + u32 cl_bit_vec, u8 cam_offset) { struct mac_configuration_cmd_e1h *config = (struct mac_configuration_cmd_e1h *)bnx2x_sp(bp, mac_config); - /* CAM allocation for E1H - * unicasts: by func number - * multicast: 20+FUNC*20, 20 each - */ config->hdr.length = 1; - config->hdr.offset = BP_FUNC(bp); - config->hdr.client_id = bp->fp->cl_id; + config->hdr.offset = cam_offset; + config->hdr.client_id = 0xff; config->hdr.reserved1 = 0; /* primary MAC */ config->config_table[0].msb_mac_addr = - swab16(*(u16 *)&bp->dev->dev_addr[0]); + swab16(*(u16 *)&mac[0]); config->config_table[0].middle_mac_addr = - swab16(*(u16 *)&bp->dev->dev_addr[2]); + swab16(*(u16 *)&mac[2]); config->config_table[0].lsb_mac_addr = - swab16(*(u16 *)&bp->dev->dev_addr[4]); + swab16(*(u16 *)&mac[4]); config->config_table[0].clients_bit_vector = - cpu_to_le32(1 << BP_L_ID(bp)); + cpu_to_le32(cl_bit_vec); config->config_table[0].vlan_id = 0; config->config_table[0].e1hov_id = cpu_to_le16(bp->e1hov); if (set) @@ -7108,11 +7212,11 @@ static void bnx2x_set_mac_addr_e1h(struct bnx2x *bp, int set) config->config_table[0].flags = MAC_CONFIGURATION_ENTRY_E1H_ACTION_TYPE; - DP(NETIF_MSG_IFUP, "%s MAC (%04x:%04x:%04x) E1HOV %d CLID %d\n", + DP(NETIF_MSG_IFUP, "%s MAC (%04x:%04x:%04x) E1HOV %d CLID mask %d\n", (set ? "setting" : "clearing"), config->config_table[0].msb_mac_addr, config->config_table[0].middle_mac_addr, - config->config_table[0].lsb_mac_addr, bp->e1hov, BP_L_ID(bp)); + config->config_table[0].lsb_mac_addr, bp->e1hov, cl_bit_vec); bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(bnx2x_sp_mapping(bp, mac_config)), @@ -7164,6 +7268,69 @@ static int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx, return -EBUSY; } +static void bnx2x_set_eth_mac_addr_e1h(struct bnx2x *bp, int set) +{ + bp->set_mac_pending++; + smp_wmb(); + + bnx2x_set_mac_addr_e1h_gen(bp, set, bp->dev->dev_addr, + (1 << bp->fp->cl_id), BP_FUNC(bp)); + + /* Wait for a completion */ + bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending, set ? 0 : 1); +} + +static void bnx2x_set_eth_mac_addr_e1(struct bnx2x *bp, int set) +{ + bp->set_mac_pending++; + smp_wmb(); + + bnx2x_set_mac_addr_e1_gen(bp, set, bp->dev->dev_addr, + (1 << bp->fp->cl_id), (BP_PORT(bp) ? 32 : 0), + 1); + + /* Wait for a completion */ + bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending, set ? 0 : 1); +} + +#ifdef BCM_CNIC +/** + * Set iSCSI MAC(s) at the next enties in the CAM after the ETH + * MAC(s). This function will wait until the ramdord completion + * returns. + * + * @param bp driver handle + * @param set set or clear the CAM entry + * + * @return 0 if cussess, -ENODEV if ramrod doesn't return. + */ +static int bnx2x_set_iscsi_eth_mac_addr(struct bnx2x *bp, int set) +{ + u32 cl_bit_vec = (1 << BCM_ISCSI_ETH_CL_ID); + + bp->set_mac_pending++; + smp_wmb(); + + /* Send a SET_MAC ramrod */ + if (CHIP_IS_E1(bp)) + bnx2x_set_mac_addr_e1_gen(bp, set, bp->iscsi_mac, + cl_bit_vec, (BP_PORT(bp) ? 32 : 0) + 2, + 1); + else + /* CAM allocation for E1H + * unicasts: by func number + * multicast: 20+FUNC*20, 20 each + */ + bnx2x_set_mac_addr_e1h_gen(bp, set, bp->iscsi_mac, + cl_bit_vec, E1H_FUNC_MAX + BP_FUNC(bp)); + + /* Wait for a completion when setting */ + bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending, set ? 0 : 1); + + return 0; +} +#endif + static int bnx2x_setup_leading(struct bnx2x *bp) { int rc; @@ -7199,96 +7366,67 @@ static int bnx2x_setup_multi(struct bnx2x *bp, int index) static int bnx2x_poll(struct napi_struct *napi, int budget); -static void bnx2x_set_int_mode_msix(struct bnx2x *bp, int *num_rx_queues_out, - int *num_tx_queues_out) +static void bnx2x_set_num_queues_msix(struct bnx2x *bp) { - int _num_rx_queues = 0, _num_tx_queues = 0; switch (bp->multi_mode) { case ETH_RSS_MODE_DISABLED: - _num_rx_queues = 1; - _num_tx_queues = 1; + bp->num_queues = 1; break; case ETH_RSS_MODE_REGULAR: - if (num_rx_queues) - _num_rx_queues = min_t(u32, num_rx_queues, - BNX2X_MAX_QUEUES(bp)); + if (num_queues) + bp->num_queues = min_t(u32, num_queues, + BNX2X_MAX_QUEUES(bp)); else - _num_rx_queues = min_t(u32, num_online_cpus(), - BNX2X_MAX_QUEUES(bp)); - - if (num_tx_queues) - _num_tx_queues = min_t(u32, num_tx_queues, - BNX2X_MAX_QUEUES(bp)); - else - _num_tx_queues = min_t(u32, num_online_cpus(), - BNX2X_MAX_QUEUES(bp)); - - /* There must be not more Tx queues than Rx queues */ - if (_num_tx_queues > _num_rx_queues) { - BNX2X_ERR("number of tx queues (%d) > " - "number of rx queues (%d)" - " defaulting to %d\n", - _num_tx_queues, _num_rx_queues, - _num_rx_queues); - _num_tx_queues = _num_rx_queues; - } + bp->num_queues = min_t(u32, num_online_cpus(), + BNX2X_MAX_QUEUES(bp)); break; default: - _num_rx_queues = 1; - _num_tx_queues = 1; + bp->num_queues = 1; break; } - - *num_rx_queues_out = _num_rx_queues; - *num_tx_queues_out = _num_tx_queues; } -static int bnx2x_set_int_mode(struct bnx2x *bp) +static int bnx2x_set_num_queues(struct bnx2x *bp) { int rc = 0; switch (int_mode) { case INT_MODE_INTx: case INT_MODE_MSI: - bp->num_rx_queues = 1; - bp->num_tx_queues = 1; + bp->num_queues = 1; DP(NETIF_MSG_IFUP, "set number of queues to 1\n"); break; case INT_MODE_MSIX: default: - /* Set interrupt mode according to bp->multi_mode value */ - bnx2x_set_int_mode_msix(bp, &bp->num_rx_queues, - &bp->num_tx_queues); + /* Set number of queues according to bp->multi_mode value */ + bnx2x_set_num_queues_msix(bp); - DP(NETIF_MSG_IFUP, "set number of queues to: rx %d tx %d\n", - bp->num_rx_queues, bp->num_tx_queues); + DP(NETIF_MSG_IFUP, "set number of queues to %d\n", + bp->num_queues); /* if we can't use MSI-X we only need one fp, * so try to enable MSI-X with the requested number of fp's * and fallback to MSI or legacy INTx with one fp */ rc = bnx2x_enable_msix(bp); - if (rc) { + if (rc) /* failed to enable MSI-X */ - if (bp->multi_mode) - BNX2X_ERR("Multi requested but failed to " - "enable MSI-X (rx %d tx %d), " - "set number of queues to 1\n", - bp->num_rx_queues, bp->num_tx_queues); - bp->num_rx_queues = 1; - bp->num_tx_queues = 1; - } + bp->num_queues = 1; break; } - bp->dev->real_num_tx_queues = bp->num_tx_queues; + bp->dev->real_num_tx_queues = bp->num_queues; return rc; } +#ifdef BCM_CNIC +static int bnx2x_cnic_notify(struct bnx2x *bp, int cmd); +static void bnx2x_setup_cnic_irq_info(struct bnx2x *bp); +#endif /* must be called with rtnl_lock */ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) @@ -7303,16 +7441,16 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD; - rc = bnx2x_set_int_mode(bp); + rc = bnx2x_set_num_queues(bp); if (bnx2x_alloc_mem(bp)) return -ENOMEM; - for_each_rx_queue(bp, i) + for_each_queue(bp, i) bnx2x_fp(bp, i, disable_tpa) = ((bp->flags & TPA_ENABLE_FLAG) == 0); - for_each_rx_queue(bp, i) + for_each_queue(bp, i) netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll, 128); @@ -7326,7 +7464,7 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) } } else { /* Fall to INTx if failed to enable MSI-X due to lack of - memory (in bnx2x_set_int_mode()) */ + memory (in bnx2x_set_num_queues()) */ if ((rc != -ENOMEM) && (int_mode != INT_MODE_INTx)) bnx2x_enable_msi(bp); bnx2x_ack_int(bp); @@ -7427,20 +7565,37 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) if (CHIP_IS_E1H(bp)) if (bp->mf_config & FUNC_MF_CFG_FUNC_DISABLED) { DP(NETIF_MSG_IFUP, "mf_cfg function disabled\n"); - bp->state = BNX2X_STATE_DISABLED; + bp->flags |= MF_FUNC_DIS; } if (bp->state == BNX2X_STATE_OPEN) { +#ifdef BCM_CNIC + /* Enable Timer scan */ + REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + BP_PORT(bp)*4, 1); +#endif for_each_nondefault_queue(bp, i) { rc = bnx2x_setup_multi(bp, i); if (rc) +#ifdef BCM_CNIC + goto load_error4; +#else goto load_error3; +#endif } if (CHIP_IS_E1(bp)) - bnx2x_set_mac_addr_e1(bp, 1); + bnx2x_set_eth_mac_addr_e1(bp, 1); else - bnx2x_set_mac_addr_e1h(bp, 1); + bnx2x_set_eth_mac_addr_e1h(bp, 1); +#ifdef BCM_CNIC + /* Set iSCSI L2 MAC */ + mutex_lock(&bp->cnic_mutex); + if (bp->cnic_eth_dev.drv_state & CNIC_DRV_STATE_REGD) { + bnx2x_set_iscsi_eth_mac_addr(bp, 1); + bp->cnic_flags |= BNX2X_CNIC_FLAG_MAC_SET; + } + mutex_unlock(&bp->cnic_mutex); +#endif } if (bp->port.pmf) @@ -7481,9 +7636,19 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) /* start the timer */ mod_timer(&bp->timer, jiffies + bp->current_interval); +#ifdef BCM_CNIC + bnx2x_setup_cnic_irq_info(bp); + if (bp->state == BNX2X_STATE_OPEN) + bnx2x_cnic_notify(bp, CNIC_CTL_START_CMD); +#endif return 0; +#ifdef BCM_CNIC +load_error4: + /* Disable Timer scan */ + REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + BP_PORT(bp)*4, 0); +#endif load_error3: bnx2x_int_disable_sync(bp, 1); if (!BP_NOMCP(bp)) { @@ -7493,14 +7658,14 @@ load_error3: bp->port.pmf = 0; /* Free SKBs, SGEs, TPA pool and driver internals */ bnx2x_free_skbs(bp); - for_each_rx_queue(bp, i) + for_each_queue(bp, i) bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE); load_error2: /* Release IRQs */ bnx2x_free_irq(bp); load_error1: bnx2x_napi_disable(bp); - for_each_rx_queue(bp, i) + for_each_queue(bp, i) netif_napi_del(&bnx2x_fp(bp, i, napi)); bnx2x_free_mem(bp); @@ -7591,6 +7756,19 @@ static void bnx2x_reset_func(struct bnx2x *bp) REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0); REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0); +#ifdef BCM_CNIC + /* Disable Timer scan */ + REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + port*4, 0); + /* + * Wait for at least 10ms and up to 2 second for the timers scan to + * complete + */ + for (i = 0; i < 200; i++) { + msleep(10); + if (!REG_RD(bp, TM_REG_LIN0_SCAN_ON + port*4)) + break; + } +#endif /* Clear ILT */ base = FUNC_ILT_BASE(func); for (i = base; i < base + ILT_PER_FUNC; i++) @@ -7657,6 +7835,9 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) u32 reset_code = 0; int i, cnt, rc; +#ifdef BCM_CNIC + bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD); +#endif bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT; /* Set "drop all" */ @@ -7675,7 +7856,7 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) bnx2x_free_irq(bp); /* Wait until tx fastpath tasks complete */ - for_each_tx_queue(bp, i) { + for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; cnt = 1000; @@ -7703,7 +7884,7 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) struct mac_configuration_cmd *config = bnx2x_sp(bp, mcast_config); - bnx2x_set_mac_addr_e1(bp, 0); + bnx2x_set_eth_mac_addr_e1(bp, 0); for (i = 0; i < config->hdr.length; i++) CAM_INVALIDATE(config->config_table[i]); @@ -7716,6 +7897,9 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) config->hdr.client_id = bp->fp->cl_id; config->hdr.reserved1 = 0; + bp->set_mac_pending++; + smp_wmb(); + bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(bnx2x_sp_mapping(bp, mcast_config)), U64_LO(bnx2x_sp_mapping(bp, mcast_config)), 0); @@ -7723,13 +7907,22 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) } else { /* E1H */ REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0); - bnx2x_set_mac_addr_e1h(bp, 0); + bnx2x_set_eth_mac_addr_e1h(bp, 0); for (i = 0; i < MC_HASH_SIZE; i++) REG_WR(bp, MC_HASH_OFFSET(bp, i), 0); REG_WR(bp, MISC_REG_E1HMF_MODE, 0); } +#ifdef BCM_CNIC + /* Clear iSCSI L2 MAC */ + mutex_lock(&bp->cnic_mutex); + if (bp->cnic_flags & BNX2X_CNIC_FLAG_MAC_SET) { + bnx2x_set_iscsi_eth_mac_addr(bp, 0); + bp->cnic_flags &= ~BNX2X_CNIC_FLAG_MAC_SET; + } + mutex_unlock(&bp->cnic_mutex); +#endif if (unload_mode == UNLOAD_NORMAL) reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; @@ -7806,9 +7999,9 @@ unload_error: /* Free SKBs, SGEs, TPA pool and driver internals */ bnx2x_free_skbs(bp); - for_each_rx_queue(bp, i) + for_each_queue(bp, i) bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE); - for_each_rx_queue(bp, i) + for_each_queue(bp, i) netif_napi_del(&bnx2x_fp(bp, i, napi)); bnx2x_free_mem(bp); @@ -8506,6 +8699,14 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp) bp->link_params.req_flow_ctrl, bp->port.advertising); } +static void __devinit bnx2x_set_mac_buf(u8 *mac_buf, u32 mac_lo, u16 mac_hi) +{ + mac_hi = cpu_to_be16(mac_hi); + mac_lo = cpu_to_be32(mac_lo); + memcpy(mac_buf, &mac_hi, sizeof(mac_hi)); + memcpy(mac_buf + sizeof(mac_hi), &mac_lo, sizeof(mac_lo)); +} + static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp) { int port = BP_PORT(bp); @@ -8587,14 +8788,15 @@ static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp) val2 = SHMEM_RD(bp, dev_info.port_hw_config[port].mac_upper); val = SHMEM_RD(bp, dev_info.port_hw_config[port].mac_lower); - bp->dev->dev_addr[0] = (u8)(val2 >> 8 & 0xff); - bp->dev->dev_addr[1] = (u8)(val2 & 0xff); - bp->dev->dev_addr[2] = (u8)(val >> 24 & 0xff); - bp->dev->dev_addr[3] = (u8)(val >> 16 & 0xff); - bp->dev->dev_addr[4] = (u8)(val >> 8 & 0xff); - bp->dev->dev_addr[5] = (u8)(val & 0xff); + bnx2x_set_mac_buf(bp->dev->dev_addr, val, val2); memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, ETH_ALEN); memcpy(bp->dev->perm_addr, bp->dev->dev_addr, ETH_ALEN); + +#ifdef BCM_CNIC + val2 = SHMEM_RD(bp, dev_info.port_hw_config[port].iscsi_mac_upper); + val = SHMEM_RD(bp, dev_info.port_hw_config[port].iscsi_mac_lower); + bnx2x_set_mac_buf(bp->iscsi_mac, val, val2); +#endif } static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp) @@ -8690,6 +8892,10 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp) smp_wmb(); /* Ensure that bp->intr_sem update is SMP-safe */ mutex_init(&bp->port.phy_mutex); + mutex_init(&bp->fw_mb_mutex); +#ifdef BCM_CNIC + mutex_init(&bp->cnic_mutex); +#endif INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task); INIT_WORK(&bp->reset_task, bnx2x_reset_task); @@ -8738,8 +8944,9 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp) bp->rx_csum = 1; - bp->tx_ticks = 50; - bp->rx_ticks = 25; + /* make sure that the numbers are in the right granularity */ + bp->tx_ticks = (50 / (4 * BNX2X_BTR)) * (4 * BNX2X_BTR); + bp->rx_ticks = (25 / (4 * BNX2X_BTR)) * (4 * BNX2X_BTR); timer_interval = (CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ); bp->current_interval = (poll ? poll : timer_interval); @@ -8765,20 +8972,23 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) cmd->supported = bp->port.supported; cmd->advertising = bp->port.advertising; - if (netif_carrier_ok(dev)) { + if ((bp->state == BNX2X_STATE_OPEN) && + !(bp->flags & MF_FUNC_DIS) && + (bp->link_vars.link_up)) { cmd->speed = bp->link_vars.line_speed; cmd->duplex = bp->link_vars.duplex; - } else { - cmd->speed = bp->link_params.req_line_speed; - cmd->duplex = bp->link_params.req_duplex; - } - if (IS_E1HMF(bp)) { - u16 vn_max_rate; + if (IS_E1HMF(bp)) { + u16 vn_max_rate; - vn_max_rate = ((bp->mf_config & FUNC_MF_CFG_MAX_BW_MASK) >> + vn_max_rate = + ((bp->mf_config & FUNC_MF_CFG_MAX_BW_MASK) >> FUNC_MF_CFG_MAX_BW_SHIFT) * 100; - if (vn_max_rate < cmd->speed) - cmd->speed = vn_max_rate; + if (vn_max_rate < cmd->speed) + cmd->speed = vn_max_rate; + } + } else { + cmd->speed = -1; + cmd->duplex = -1; } if (bp->link_params.switch_cfg == SWITCH_CFG_10G) { @@ -9163,6 +9373,9 @@ static u32 bnx2x_get_link(struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); + if (bp->flags & MF_FUNC_DIS) + return 0; + return bp->link_vars.link_up; } @@ -9567,8 +9780,7 @@ static int bnx2x_set_eeprom(struct net_device *dev, } else if (eeprom->magic == 0x50485952) { /* 'PHYR' (0x50485952): re-init link after FW upgrade */ - if ((bp->state == BNX2X_STATE_OPEN) || - (bp->state == BNX2X_STATE_DISABLED)) { + if (bp->state == BNX2X_STATE_OPEN) { bnx2x_acquire_phy_lock(bp); rc |= bnx2x_link_reset(&bp->link_params, &bp->link_vars, 1); @@ -9818,11 +10030,6 @@ static const struct { { "idle check (online)" } }; -static int bnx2x_self_test_count(struct net_device *dev) -{ - return BNX2X_NUM_TESTS; -} - static int bnx2x_test_registers(struct bnx2x *bp) { int idx, i, rc = -ENODEV; @@ -9990,7 +10197,7 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up) struct sk_buff *skb; unsigned char *packet; struct bnx2x_fastpath *fp_rx = &bp->fp[0]; - struct bnx2x_fastpath *fp_tx = &bp->fp[bp->num_rx_queues]; + struct bnx2x_fastpath *fp_tx = &bp->fp[0]; u16 tx_start_idx, tx_idx; u16 rx_start_idx, rx_idx; u16 pkt_prod, bd_prod; @@ -10067,13 +10274,12 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up) fp_tx->tx_db.data.prod += 2; barrier(); - DOORBELL(bp, fp_tx->index - bp->num_rx_queues, fp_tx->tx_db.raw); + DOORBELL(bp, fp_tx->index, fp_tx->tx_db.raw); mmiowb(); num_pkts++; fp_tx->tx_bd_prod += 2; /* start + pbd */ - bp->dev->trans_start = jiffies; udelay(100); @@ -10223,14 +10429,16 @@ static int bnx2x_test_intr(struct bnx2x *bp) config->hdr.client_id = bp->fp->cl_id; config->hdr.reserved1 = 0; + bp->set_mac_pending++; + smp_wmb(); rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(bnx2x_sp_mapping(bp, mac_config)), U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0); if (rc == 0) { - bp->set_mac_pending++; for (i = 0; i < 10; i++) { if (!bp->set_mac_pending) break; + smp_rmb(); msleep_interruptible(10); } if (i == 10) @@ -10264,7 +10472,7 @@ static void bnx2x_self_test(struct net_device *dev, /* disable input for TX port IF */ REG_WR(bp, NIG_REG_EGRESS_UMP0_IN_EN + port*4, 0); - link_up = bp->link_vars.link_up; + link_up = (bnx2x_link_test(bp) == 0); bnx2x_nic_unload(bp, UNLOAD_NORMAL); bnx2x_nic_load(bp, LOAD_DIAG); /* wait until link state is restored */ @@ -10436,6 +10644,36 @@ static const struct { #define IS_E1HMF_MODE_STAT(bp) \ (IS_E1HMF(bp) && !(bp->msglevel & BNX2X_MSG_STATS)) +static int bnx2x_get_sset_count(struct net_device *dev, int stringset) +{ + struct bnx2x *bp = netdev_priv(dev); + int i, num_stats; + + switch(stringset) { + case ETH_SS_STATS: + if (is_multi(bp)) { + num_stats = BNX2X_NUM_Q_STATS * bp->num_queues; + if (!IS_E1HMF_MODE_STAT(bp)) + num_stats += BNX2X_NUM_STATS; + } else { + if (IS_E1HMF_MODE_STAT(bp)) { + num_stats = 0; + for (i = 0; i < BNX2X_NUM_STATS; i++) + if (IS_FUNC_STAT(i)) + num_stats++; + } else + num_stats = BNX2X_NUM_STATS; + } + return num_stats; + + case ETH_SS_TEST: + return BNX2X_NUM_TESTS; + + default: + return -EINVAL; + } +} + static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) { struct bnx2x *bp = netdev_priv(dev); @@ -10445,7 +10683,7 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) case ETH_SS_STATS: if (is_multi(bp)) { k = 0; - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { for (j = 0; j < BNX2X_NUM_Q_STATS; j++) sprintf(buf + (k + j)*ETH_GSTRING_LEN, bnx2x_q_stats_arr[j].string, i); @@ -10473,28 +10711,6 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) } } -static int bnx2x_get_stats_count(struct net_device *dev) -{ - struct bnx2x *bp = netdev_priv(dev); - int i, num_stats; - - if (is_multi(bp)) { - num_stats = BNX2X_NUM_Q_STATS * bp->num_rx_queues; - if (!IS_E1HMF_MODE_STAT(bp)) - num_stats += BNX2X_NUM_STATS; - } else { - if (IS_E1HMF_MODE_STAT(bp)) { - num_stats = 0; - for (i = 0; i < BNX2X_NUM_STATS; i++) - if (IS_FUNC_STAT(i)) - num_stats++; - } else - num_stats = BNX2X_NUM_STATS; - } - - return num_stats; -} - static void bnx2x_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *buf) { @@ -10504,7 +10720,7 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev, if (is_multi(bp)) { k = 0; - for_each_rx_queue(bp, i) { + for_each_queue(bp, i) { hw_stats = (u32 *)&bp->fp[i].eth_q_stats; for (j = 0; j < BNX2X_NUM_Q_STATS; j++) { if (bnx2x_q_stats_arr[j].size == 0) { @@ -10570,7 +10786,6 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev, static int bnx2x_phys_id(struct net_device *dev, u32 data) { struct bnx2x *bp = netdev_priv(dev); - int port = BP_PORT(bp); int i; if (!netif_running(dev)) @@ -10584,13 +10799,10 @@ static int bnx2x_phys_id(struct net_device *dev, u32 data) for (i = 0; i < (data * 2); i++) { if ((i % 2) == 0) - bnx2x_set_led(bp, port, LED_MODE_OPER, SPEED_1000, - bp->link_params.hw_led_mode, - bp->link_params.chip_id); + bnx2x_set_led(&bp->link_params, LED_MODE_OPER, + SPEED_1000); else - bnx2x_set_led(bp, port, LED_MODE_OFF, 0, - bp->link_params.hw_led_mode, - bp->link_params.chip_id); + bnx2x_set_led(&bp->link_params, LED_MODE_OFF, 0); msleep_interruptible(500); if (signal_pending(current)) @@ -10598,10 +10810,8 @@ static int bnx2x_phys_id(struct net_device *dev, u32 data) } if (bp->link_vars.link_up) - bnx2x_set_led(bp, port, LED_MODE_OPER, - bp->link_vars.line_speed, - bp->link_params.hw_led_mode, - bp->link_params.chip_id); + bnx2x_set_led(&bp->link_params, LED_MODE_OPER, + bp->link_vars.line_speed); return 0; } @@ -10637,11 +10847,10 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { .set_sg = ethtool_op_set_sg, .get_tso = ethtool_op_get_tso, .set_tso = bnx2x_set_tso, - .self_test_count = bnx2x_self_test_count, .self_test = bnx2x_self_test, + .get_sset_count = bnx2x_get_sset_count, .get_strings = bnx2x_get_strings, .phys_id = bnx2x_phys_id, - .get_stats_count = bnx2x_get_stats_count, .get_ethtool_stats = bnx2x_get_ethtool_stats, }; @@ -10707,54 +10916,60 @@ static inline int bnx2x_has_rx_work(struct bnx2x_fastpath *fp) static int bnx2x_poll(struct napi_struct *napi, int budget) { + int work_done = 0; struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath, napi); struct bnx2x *bp = fp->bp; - int work_done = 0; + while (1) { #ifdef BNX2X_STOP_ON_ERROR - if (unlikely(bp->panic)) - goto poll_panic; + if (unlikely(bp->panic)) { + napi_complete(napi); + return 0; + } #endif - prefetch(fp->rx_buf_ring[RX_BD(fp->rx_bd_cons)].skb); - prefetch((char *)(fp->rx_buf_ring[RX_BD(fp->rx_bd_cons)].skb) + 256); - - bnx2x_update_fpsb_idx(fp); - - if (bnx2x_has_rx_work(fp)) { - work_done = bnx2x_rx_int(fp, budget); + if (bnx2x_has_tx_work(fp)) + bnx2x_tx_int(fp); - /* must not complete if we consumed full budget */ - if (work_done >= budget) - goto poll_again; - } + if (bnx2x_has_rx_work(fp)) { + work_done += bnx2x_rx_int(fp, budget - work_done); - /* bnx2x_has_rx_work() reads the status block, thus we need to - * ensure that status block indices have been actually read - * (bnx2x_update_fpsb_idx) prior to this check (bnx2x_has_rx_work) - * so that we won't write the "newer" value of the status block to IGU - * (if there was a DMA right after bnx2x_has_rx_work and - * if there is no rmb, the memory reading (bnx2x_update_fpsb_idx) - * may be postponed to right before bnx2x_ack_sb). In this case - * there will never be another interrupt until there is another update - * of the status block, while there is still unhandled work. - */ - rmb(); + /* must not complete if we consumed full budget */ + if (work_done >= budget) + break; + } - if (!bnx2x_has_rx_work(fp)) { -#ifdef BNX2X_STOP_ON_ERROR -poll_panic: -#endif - napi_complete(napi); + /* Fall out from the NAPI loop if needed */ + if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) { + bnx2x_update_fpsb_idx(fp); + /* bnx2x_has_rx_work() reads the status block, thus we need + * to ensure that status block indices have been actually read + * (bnx2x_update_fpsb_idx) prior to this check + * (bnx2x_has_rx_work) so that we won't write the "newer" + * value of the status block to IGU (if there was a DMA right + * after bnx2x_has_rx_work and if there is no rmb, the memory + * reading (bnx2x_update_fpsb_idx) may be postponed to right + * before bnx2x_ack_sb). In this case there will never be + * another interrupt until there is another update of the + * status block, while there is still unhandled work. + */ + rmb(); - bnx2x_ack_sb(bp, fp->sb_id, USTORM_ID, - le16_to_cpu(fp->fp_u_idx), IGU_INT_NOP, 1); - bnx2x_ack_sb(bp, fp->sb_id, CSTORM_ID, - le16_to_cpu(fp->fp_c_idx), IGU_INT_ENABLE, 1); + if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) { + napi_complete(napi); + /* Re-enable interrupts */ + bnx2x_ack_sb(bp, fp->sb_id, CSTORM_ID, + le16_to_cpu(fp->fp_c_idx), + IGU_INT_NOP, 1); + bnx2x_ack_sb(bp, fp->sb_id, USTORM_ID, + le16_to_cpu(fp->fp_u_idx), + IGU_INT_ENABLE, 1); + break; + } + } } -poll_again: return work_done; } @@ -10843,10 +11058,10 @@ static inline u32 bnx2x_xmit_type(struct bnx2x *bp, struct sk_buff *skb) } if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) - rc |= XMIT_GSO_V4; + rc |= (XMIT_GSO_V4 | XMIT_CSUM_V4 | XMIT_CSUM_TCP); else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) - rc |= XMIT_GSO_V6; + rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP | XMIT_CSUM_V6); return rc; } @@ -10939,7 +11154,7 @@ exit_lbl: static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); - struct bnx2x_fastpath *fp, *fp_stat; + struct bnx2x_fastpath *fp; struct netdev_queue *txq; struct sw_tx_bd *tx_buf; struct eth_tx_start_bd *tx_start_bd; @@ -10961,11 +11176,10 @@ static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) fp_index = skb_get_queue_mapping(skb); txq = netdev_get_tx_queue(dev, fp_index); - fp = &bp->fp[fp_index + bp->num_rx_queues]; - fp_stat = &bp->fp[fp_index]; + fp = &bp->fp[fp_index]; if (unlikely(bnx2x_tx_avail(fp) < (skb_shinfo(skb)->nr_frags + 3))) { - fp_stat->eth_q_stats.driver_xoff++; + fp->eth_q_stats.driver_xoff++; netif_tx_stop_queue(txq); BNX2X_ERR("BUG! Tx ring full when queue awake!\n"); return NETDEV_TX_BUSY; @@ -11191,7 +11405,7 @@ static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) fp->tx_db.data.prod += nbd; barrier(); - DOORBELL(bp, fp->index - bp->num_rx_queues, fp->tx_db.raw); + DOORBELL(bp, fp->index, fp->tx_db.raw); mmiowb(); @@ -11202,11 +11416,11 @@ static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) /* We want bnx2x_tx_int to "see" the updated tx_bd_prod if we put Tx into XOFF state. */ smp_mb(); - fp_stat->eth_q_stats.driver_xoff++; + fp->eth_q_stats.driver_xoff++; if (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3) netif_tx_wake_queue(txq); } - fp_stat->tx_pkt++; + fp->tx_pkt++; return NETDEV_TX_OK; } @@ -11321,6 +11535,9 @@ static void bnx2x_set_rx_mode(struct net_device *dev) config->hdr.client_id = bp->fp->cl_id; config->hdr.reserved1 = 0; + bp->set_mac_pending++; + smp_wmb(); + bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(bnx2x_sp_mapping(bp, mcast_config)), U64_LO(bnx2x_sp_mapping(bp, mcast_config)), @@ -11370,9 +11587,9 @@ static int bnx2x_change_mac_addr(struct net_device *dev, void *p) memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); if (netif_running(dev)) { if (CHIP_IS_E1(bp)) - bnx2x_set_mac_addr_e1(bp, 1); + bnx2x_set_eth_mac_addr_e1(bp, 1); else - bnx2x_set_mac_addr_e1h(bp, 1); + bnx2x_set_eth_mac_addr_e1h(bp, 1); } return 0; @@ -11830,21 +12047,14 @@ static inline void be16_to_cpu_n(const u8 *_source, u8 *_target, u32 n) static int __devinit bnx2x_init_firmware(struct bnx2x *bp, struct device *dev) { - char fw_file_name[40] = {0}; + const char *fw_file_name; struct bnx2x_fw_file_hdr *fw_hdr; - int rc, offset; + int rc; - /* Create a FW file name */ if (CHIP_IS_E1(bp)) - offset = sprintf(fw_file_name, FW_FILE_PREFIX_E1); + fw_file_name = FW_FILE_NAME_E1; else - offset = sprintf(fw_file_name, FW_FILE_PREFIX_E1H); - - sprintf(fw_file_name + offset, "%d.%d.%d.%d.fw", - BCM_5710_FW_MAJOR_VERSION, - BCM_5710_FW_MINOR_VERSION, - BCM_5710_FW_REVISION_VERSION, - BCM_5710_FW_ENGINEERING_VERSION); + fw_file_name = FW_FILE_NAME_E1H; printk(KERN_INFO PFX "Loading %s\n", fw_file_name); @@ -12098,9 +12308,9 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp) /* Free SKBs, SGEs, TPA pool and driver internals */ bnx2x_free_skbs(bp); - for_each_rx_queue(bp, i) + for_each_queue(bp, i) bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE); - for_each_rx_queue(bp, i) + for_each_queue(bp, i) netif_napi_del(&bnx2x_fp(bp, i, napi)); bnx2x_free_mem(bp); @@ -12276,4 +12486,287 @@ static void __exit bnx2x_cleanup(void) module_init(bnx2x_init); module_exit(bnx2x_cleanup); +#ifdef BCM_CNIC + +/* count denotes the number of new completions we have seen */ +static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count) +{ + struct eth_spe *spe; + +#ifdef BNX2X_STOP_ON_ERROR + if (unlikely(bp->panic)) + return; +#endif + + spin_lock_bh(&bp->spq_lock); + bp->cnic_spq_pending -= count; + + for (; bp->cnic_spq_pending < bp->cnic_eth_dev.max_kwqe_pending; + bp->cnic_spq_pending++) { + + if (!bp->cnic_kwq_pending) + break; + + spe = bnx2x_sp_get_next(bp); + *spe = *bp->cnic_kwq_cons; + + bp->cnic_kwq_pending--; + + DP(NETIF_MSG_TIMER, "pending on SPQ %d, on KWQ %d count %d\n", + bp->cnic_spq_pending, bp->cnic_kwq_pending, count); + + if (bp->cnic_kwq_cons == bp->cnic_kwq_last) + bp->cnic_kwq_cons = bp->cnic_kwq; + else + bp->cnic_kwq_cons++; + } + bnx2x_sp_prod_update(bp); + spin_unlock_bh(&bp->spq_lock); +} + +static int bnx2x_cnic_sp_queue(struct net_device *dev, + struct kwqe_16 *kwqes[], u32 count) +{ + struct bnx2x *bp = netdev_priv(dev); + int i; + +#ifdef BNX2X_STOP_ON_ERROR + if (unlikely(bp->panic)) + return -EIO; +#endif + + spin_lock_bh(&bp->spq_lock); + + for (i = 0; i < count; i++) { + struct eth_spe *spe = (struct eth_spe *)kwqes[i]; + + if (bp->cnic_kwq_pending == MAX_SP_DESC_CNT) + break; + + *bp->cnic_kwq_prod = *spe; + + bp->cnic_kwq_pending++; + + DP(NETIF_MSG_TIMER, "L5 SPQE %x %x %x:%x pos %d\n", + spe->hdr.conn_and_cmd_data, spe->hdr.type, + spe->data.mac_config_addr.hi, + spe->data.mac_config_addr.lo, + bp->cnic_kwq_pending); + + if (bp->cnic_kwq_prod == bp->cnic_kwq_last) + bp->cnic_kwq_prod = bp->cnic_kwq; + else + bp->cnic_kwq_prod++; + } + + spin_unlock_bh(&bp->spq_lock); + + if (bp->cnic_spq_pending < bp->cnic_eth_dev.max_kwqe_pending) + bnx2x_cnic_sp_post(bp, 0); + + return i; +} + +static int bnx2x_cnic_ctl_send(struct bnx2x *bp, struct cnic_ctl_info *ctl) +{ + struct cnic_ops *c_ops; + int rc = 0; + + mutex_lock(&bp->cnic_mutex); + c_ops = bp->cnic_ops; + if (c_ops) + rc = c_ops->cnic_ctl(bp->cnic_data, ctl); + mutex_unlock(&bp->cnic_mutex); + + return rc; +} + +static int bnx2x_cnic_ctl_send_bh(struct bnx2x *bp, struct cnic_ctl_info *ctl) +{ + struct cnic_ops *c_ops; + int rc = 0; + + rcu_read_lock(); + c_ops = rcu_dereference(bp->cnic_ops); + if (c_ops) + rc = c_ops->cnic_ctl(bp->cnic_data, ctl); + rcu_read_unlock(); + + return rc; +} + +/* + * for commands that have no data + */ +static int bnx2x_cnic_notify(struct bnx2x *bp, int cmd) +{ + struct cnic_ctl_info ctl = {0}; + + ctl.cmd = cmd; + + return bnx2x_cnic_ctl_send(bp, &ctl); +} + +static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid) +{ + struct cnic_ctl_info ctl; + + /* first we tell CNIC and only then we count this as a completion */ + ctl.cmd = CNIC_CTL_COMPLETION_CMD; + ctl.data.comp.cid = cid; + + bnx2x_cnic_ctl_send_bh(bp, &ctl); + bnx2x_cnic_sp_post(bp, 1); +} + +static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl) +{ + struct bnx2x *bp = netdev_priv(dev); + int rc = 0; + + switch (ctl->cmd) { + case DRV_CTL_CTXTBL_WR_CMD: { + u32 index = ctl->data.io.offset; + dma_addr_t addr = ctl->data.io.dma_addr; + + bnx2x_ilt_wr(bp, index, addr); + break; + } + + case DRV_CTL_COMPLETION_CMD: { + int count = ctl->data.comp.comp_count; + + bnx2x_cnic_sp_post(bp, count); + break; + } + + /* rtnl_lock is held. */ + case DRV_CTL_START_L2_CMD: { + u32 cli = ctl->data.ring.client_id; + + bp->rx_mode_cl_mask |= (1 << cli); + bnx2x_set_storm_rx_mode(bp); + break; + } + + /* rtnl_lock is held. */ + case DRV_CTL_STOP_L2_CMD: { + u32 cli = ctl->data.ring.client_id; + + bp->rx_mode_cl_mask &= ~(1 << cli); + bnx2x_set_storm_rx_mode(bp); + break; + } + + default: + BNX2X_ERR("unknown command %x\n", ctl->cmd); + rc = -EINVAL; + } + + return rc; +} + +static void bnx2x_setup_cnic_irq_info(struct bnx2x *bp) +{ + struct cnic_eth_dev *cp = &bp->cnic_eth_dev; + + if (bp->flags & USING_MSIX_FLAG) { + cp->drv_state |= CNIC_DRV_STATE_USING_MSIX; + cp->irq_arr[0].irq_flags |= CNIC_IRQ_FL_MSIX; + cp->irq_arr[0].vector = bp->msix_table[1].vector; + } else { + cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX; + cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX; + } + cp->irq_arr[0].status_blk = bp->cnic_sb; + cp->irq_arr[0].status_blk_num = CNIC_SB_ID(bp); + cp->irq_arr[1].status_blk = bp->def_status_blk; + cp->irq_arr[1].status_blk_num = DEF_SB_ID; + + cp->num_irq = 2; +} + +static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops, + void *data) +{ + struct bnx2x *bp = netdev_priv(dev); + struct cnic_eth_dev *cp = &bp->cnic_eth_dev; + + if (ops == NULL) + return -EINVAL; + + if (atomic_read(&bp->intr_sem) != 0) + return -EBUSY; + + bp->cnic_kwq = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!bp->cnic_kwq) + return -ENOMEM; + + bp->cnic_kwq_cons = bp->cnic_kwq; + bp->cnic_kwq_prod = bp->cnic_kwq; + bp->cnic_kwq_last = bp->cnic_kwq + MAX_SP_DESC_CNT; + + bp->cnic_spq_pending = 0; + bp->cnic_kwq_pending = 0; + + bp->cnic_data = data; + + cp->num_irq = 0; + cp->drv_state = CNIC_DRV_STATE_REGD; + + bnx2x_init_sb(bp, bp->cnic_sb, bp->cnic_sb_mapping, CNIC_SB_ID(bp)); + + bnx2x_setup_cnic_irq_info(bp); + bnx2x_set_iscsi_eth_mac_addr(bp, 1); + bp->cnic_flags |= BNX2X_CNIC_FLAG_MAC_SET; + rcu_assign_pointer(bp->cnic_ops, ops); + + return 0; +} + +static int bnx2x_unregister_cnic(struct net_device *dev) +{ + struct bnx2x *bp = netdev_priv(dev); + struct cnic_eth_dev *cp = &bp->cnic_eth_dev; + + mutex_lock(&bp->cnic_mutex); + if (bp->cnic_flags & BNX2X_CNIC_FLAG_MAC_SET) { + bp->cnic_flags &= ~BNX2X_CNIC_FLAG_MAC_SET; + bnx2x_set_iscsi_eth_mac_addr(bp, 0); + } + cp->drv_state = 0; + rcu_assign_pointer(bp->cnic_ops, NULL); + mutex_unlock(&bp->cnic_mutex); + synchronize_rcu(); + kfree(bp->cnic_kwq); + bp->cnic_kwq = NULL; + + return 0; +} + +struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev) +{ + struct bnx2x *bp = netdev_priv(dev); + struct cnic_eth_dev *cp = &bp->cnic_eth_dev; + + cp->drv_owner = THIS_MODULE; + cp->chip_id = CHIP_ID(bp); + cp->pdev = bp->pdev; + cp->io_base = bp->regview; + cp->io_base2 = bp->doorbells; + cp->max_kwqe_pending = 8; + cp->ctx_blk_size = CNIC_CTX_PER_ILT * sizeof(union cdu_context); + cp->ctx_tbl_offset = FUNC_ILT_BASE(BP_FUNC(bp)) + 1; + cp->ctx_tbl_len = CNIC_ILT_LINES; + cp->starting_cid = BCM_CNIC_CID_START; + cp->drv_submit_kwqes_16 = bnx2x_cnic_sp_queue; + cp->drv_ctl = bnx2x_drv_ctl; + cp->drv_register_cnic = bnx2x_register_cnic; + cp->drv_unregister_cnic = bnx2x_unregister_cnic; + + return cp; +} +EXPORT_SYMBOL(bnx2x_cnic_probe); + +#endif /* BCM_CNIC */ diff --git a/drivers/net/bnx2x_reg.h b/drivers/net/bnx2x_reg.h index aa76cbada5e2..944964e78c81 100644 --- a/drivers/net/bnx2x_reg.h +++ b/drivers/net/bnx2x_reg.h @@ -2536,7 +2536,7 @@ /* [RC 1] A flag to indicate that overflow error occurred in one of the queues. */ #define QM_REG_OVFERROR 0x16805c -/* [RC 7] the Q were the qverflow occurs */ +/* [RC 7] the Q where the overflow occurs */ #define QM_REG_OVFQNUM 0x168058 /* [R 16] Pause state for physical queues 15-0 */ #define QM_REG_PAUSESTATE0 0x168410 @@ -4772,18 +4772,28 @@ #define PCI_ID_VAL2 0x438 -#define MDIO_REG_BANK_CL73_IEEEB0 0x0 -#define MDIO_CL73_IEEEB0_CL73_AN_CONTROL 0x0 +#define MDIO_REG_BANK_CL73_IEEEB0 0x0 +#define MDIO_CL73_IEEEB0_CL73_AN_CONTROL 0x0 #define MDIO_CL73_IEEEB0_CL73_AN_CONTROL_RESTART_AN 0x0200 #define MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN 0x1000 #define MDIO_CL73_IEEEB0_CL73_AN_CONTROL_MAIN_RST 0x8000 -#define MDIO_REG_BANK_CL73_IEEEB1 0x10 -#define MDIO_CL73_IEEEB1_AN_ADV2 0x01 +#define MDIO_REG_BANK_CL73_IEEEB1 0x10 +#define MDIO_CL73_IEEEB1_AN_ADV1 0x00 +#define MDIO_CL73_IEEEB1_AN_ADV1_PAUSE 0x0400 +#define MDIO_CL73_IEEEB1_AN_ADV1_ASYMMETRIC 0x0800 +#define MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_BOTH 0x0C00 +#define MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_MASK 0x0C00 +#define MDIO_CL73_IEEEB1_AN_ADV2 0x01 #define MDIO_CL73_IEEEB1_AN_ADV2_ADVR_1000M 0x0000 #define MDIO_CL73_IEEEB1_AN_ADV2_ADVR_1000M_KX 0x0020 #define MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4 0x0040 #define MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KR 0x0080 +#define MDIO_CL73_IEEEB1_AN_LP_ADV1 0x03 +#define MDIO_CL73_IEEEB1_AN_LP_ADV1_PAUSE 0x0400 +#define MDIO_CL73_IEEEB1_AN_LP_ADV1_ASYMMETRIC 0x0800 +#define MDIO_CL73_IEEEB1_AN_LP_ADV1_PAUSE_BOTH 0x0C00 +#define MDIO_CL73_IEEEB1_AN_LP_ADV1_PAUSE_MASK 0x0C00 #define MDIO_REG_BANK_RX0 0x80b0 #define MDIO_RX0_RX_STATUS 0x10 @@ -4910,6 +4920,8 @@ #define MDIO_REG_BANK_10G_PARALLEL_DETECT 0x8130 +#define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_STATUS 0x10 +#define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_STATUS_PD_LINK 0x8000 #define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL 0x11 #define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL_PARDET10G_EN 0x1 #define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK 0x13 @@ -4934,6 +4946,8 @@ #define MDIO_SERDES_DIGITAL_A_1000X_STATUS1_SPEED_1G 0x0010 #define MDIO_SERDES_DIGITAL_A_1000X_STATUS1_SPEED_100M 0x0008 #define MDIO_SERDES_DIGITAL_A_1000X_STATUS1_SPEED_10M 0x0000 +#define MDIO_SERDES_DIGITAL_A_1000X_STATUS2 0x15 +#define MDIO_SERDES_DIGITAL_A_1000X_STATUS2_AN_DISABLED 0x0002 #define MDIO_SERDES_DIGITAL_MISC1 0x18 #define MDIO_SERDES_DIGITAL_MISC1_REFCLK_SEL_MASK 0xE000 #define MDIO_SERDES_DIGITAL_MISC1_REFCLK_SEL_25M 0x0000 @@ -5115,6 +5129,7 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_PMA_REG_8481_LED1_MASK 0xa82c #define MDIO_PMA_REG_8481_LED2_MASK 0xa82f #define MDIO_PMA_REG_8481_LED3_MASK 0xa832 +#define MDIO_PMA_REG_8481_LED3_BLINK 0xa834 #define MDIO_PMA_REG_8481_SIGNAL_MASK 0xa835 #define MDIO_PMA_REG_8481_LINK_SIGNAL 0xa83b diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index c3fa31c9f2a7..d69e6838f21e 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -446,6 +446,48 @@ static u16 __ad_timer_to_ticks(u16 timer_type, u16 par) ///////////////////////////////////////////////////////////////////////////////// /** + * __choose_matched - update a port's matched variable from a received lacpdu + * @lacpdu: the lacpdu we've received + * @port: the port we're looking at + * + * Update the value of the matched variable, using parameter values from a + * newly received lacpdu. Parameter values for the partner carried in the + * received PDU are compared with the corresponding operational parameter + * values for the actor. Matched is set to TRUE if all of these parameters + * match and the PDU parameter partner_state.aggregation has the same value as + * actor_oper_port_state.aggregation and lacp will actively maintain the link + * in the aggregation. Matched is also set to TRUE if the value of + * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates + * an individual link and lacp will actively maintain the link. Otherwise, + * matched is set to FALSE. LACP is considered to be actively maintaining the + * link if either the PDU's actor_state.lacp_activity variable is TRUE or both + * the actor's actor_oper_port_state.lacp_activity and the PDU's + * partner_state.lacp_activity variables are TRUE. + * + * Note: the AD_PORT_MATCHED "variable" is not specified by 802.3ad; it is + * used here to implement the language from 802.3ad 43.4.9 that requires + * recordPDU to "match" the LACPDU parameters to the stored values. + */ +static void __choose_matched(struct lacpdu *lacpdu, struct port *port) +{ + // check if all parameters are alike + if (((ntohs(lacpdu->partner_port) == port->actor_port_number) && + (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) && + !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) && + (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) && + (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) && + ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) || + // or this is individual link(aggregation == FALSE) + ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0) + ) { + // update the state machine Matched variable + port->sm_vars |= AD_PORT_MATCHED; + } else { + port->sm_vars &= ~AD_PORT_MATCHED; + } +} + +/** * __record_pdu - record parameters from a received lacpdu * @lacpdu: the lacpdu we've received * @port: the port we're looking at @@ -459,6 +501,7 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port) if (lacpdu && port) { struct port_params *partner = &port->partner_oper; + __choose_matched(lacpdu, port); // record the new parameter values for the partner operational partner->port_number = ntohs(lacpdu->actor_port); partner->port_priority = ntohs(lacpdu->actor_port_priority); @@ -518,12 +561,12 @@ static void __update_selected(struct lacpdu *lacpdu, struct port *port) const struct port_params *partner = &port->partner_oper; // check if any parameter is different - if (ntohs(lacpdu->actor_port) != partner->port_number - || ntohs(lacpdu->actor_port_priority) != partner->port_priority - || MAC_ADDRESS_COMPARE(&lacpdu->actor_system, &partner->system) - || ntohs(lacpdu->actor_system_priority) != partner->system_priority - || ntohs(lacpdu->actor_key) != partner->key - || (lacpdu->actor_state & AD_STATE_AGGREGATION) != (partner->port_state & AD_STATE_AGGREGATION)) { + if (ntohs(lacpdu->actor_port) != partner->port_number || + ntohs(lacpdu->actor_port_priority) != partner->port_priority || + MAC_ADDRESS_COMPARE(&lacpdu->actor_system, &partner->system) || + ntohs(lacpdu->actor_system_priority) != partner->system_priority || + ntohs(lacpdu->actor_key) != partner->key || + (lacpdu->actor_state & AD_STATE_AGGREGATION) != (partner->port_state & AD_STATE_AGGREGATION)) { // update the state machine Selected variable port->sm_vars &= ~AD_PORT_SELECTED; } @@ -549,12 +592,12 @@ static void __update_default_selected(struct port *port) const struct port_params *oper = &port->partner_oper; // check if any parameter is different - if (admin->port_number != oper->port_number - || admin->port_priority != oper->port_priority - || MAC_ADDRESS_COMPARE(&admin->system, &oper->system) - || admin->system_priority != oper->system_priority - || admin->key != oper->key - || (admin->port_state & AD_STATE_AGGREGATION) + if (admin->port_number != oper->port_number || + admin->port_priority != oper->port_priority || + MAC_ADDRESS_COMPARE(&admin->system, &oper->system) || + admin->system_priority != oper->system_priority || + admin->key != oper->key || + (admin->port_state & AD_STATE_AGGREGATION) != (oper->port_state & AD_STATE_AGGREGATION)) { // update the state machine Selected variable port->sm_vars &= ~AD_PORT_SELECTED; @@ -563,47 +606,6 @@ static void __update_default_selected(struct port *port) } /** - * __choose_matched - update a port's matched variable from a received lacpdu - * @lacpdu: the lacpdu we've received - * @port: the port we're looking at - * - * Update the value of the matched variable, using parameter values from a - * newly received lacpdu. Parameter values for the partner carried in the - * received PDU are compared with the corresponding operational parameter - * values for the actor. Matched is set to TRUE if all of these parameters - * match and the PDU parameter partner_state.aggregation has the same value as - * actor_oper_port_state.aggregation and lacp will actively maintain the link - * in the aggregation. Matched is also set to TRUE if the value of - * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates - * an individual link and lacp will actively maintain the link. Otherwise, - * matched is set to FALSE. LACP is considered to be actively maintaining the - * link if either the PDU's actor_state.lacp_activity variable is TRUE or both - * the actor's actor_oper_port_state.lacp_activity and the PDU's - * partner_state.lacp_activity variables are TRUE. - */ -static void __choose_matched(struct lacpdu *lacpdu, struct port *port) -{ - // validate lacpdu and port - if (lacpdu && port) { - // check if all parameters are alike - if (((ntohs(lacpdu->partner_port) == port->actor_port_number) && - (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) && - !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) && - (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) && - (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) && - ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) || - // or this is individual link(aggregation == FALSE) - ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0) - ) { - // update the state machine Matched variable - port->sm_vars |= AD_PORT_MATCHED; - } else { - port->sm_vars &= ~AD_PORT_MATCHED; - } - } -} - -/** * __update_ntt - update a port's ntt variable from a received lacpdu * @lacpdu: the lacpdu we've received * @port: the port we're looking at @@ -1134,7 +1136,6 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) __update_selected(lacpdu, port); __update_ntt(lacpdu, port); __record_pdu(lacpdu, port); - __choose_matched(lacpdu, port); port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)); port->actor_oper_port_state &= ~AD_STATE_EXPIRED; // verify that if the aggregator is enabled, the port is enabled too. @@ -1956,7 +1957,7 @@ void bond_3ad_unbind_slave(struct slave *slave) struct port *port, *prev_port, *temp_port; struct aggregator *aggregator, *new_aggregator, *temp_aggregator; int select_new_active_agg = 0; - + // find the aggregator related to this slave aggregator = &(SLAVE_AD_INFO(slave).aggregator); @@ -2024,7 +2025,7 @@ void bond_3ad_unbind_slave(struct slave *slave) // clear the aggregator ad_clear_agg(aggregator); - + if (select_new_active_agg) { ad_agg_selection_logic(__get_first_agg(port)); } @@ -2075,7 +2076,7 @@ void bond_3ad_unbind_slave(struct slave *slave) } } } - port->slave=NULL; + port->slave=NULL; } /** @@ -2301,7 +2302,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) } /* - * set link state for bonding master: if we have an active + * set link state for bonding master: if we have an active * aggregator, we're up, if not, we're down. Presumes that we cannot * have an active aggregator if there are no slaves with link up. * @@ -2395,7 +2396,7 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) goto out; } - slave_agg_no = bond->xmit_hash_policy(skb, dev, slaves_in_agg); + slave_agg_no = bond->xmit_hash_policy(skb, slaves_in_agg); bond_for_each_slave(bond, slave, i) { struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator; @@ -2445,9 +2446,6 @@ int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct pac struct slave *slave = NULL; int ret = NET_RX_DROP; - if (dev_net(dev) != &init_net) - goto out; - if (!(dev->flags & IFF_MASTER)) goto out; @@ -2468,4 +2466,3 @@ out: return ret; } - diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 9b5936f072dc..00ab51ef3129 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -355,9 +355,6 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct struct arp_pkt *arp = (struct arp_pkt *)skb->data; int res = NET_RX_DROP; - if (dev_net(bond_dev) != &init_net) - goto out; - while (bond_dev->priv_flags & IFF_802_1Q_VLAN) bond_dev = vlan_dev_real_dev(bond_dev); @@ -559,7 +556,7 @@ static void rlb_update_rx_clients(struct bonding *bond) } } - /* do not update the entries again untill this counter is zero so that + /* do not update the entries again until this counter is zero so that * not to confuse the clients. */ bond_info->rlb_update_delay_counter = RLB_UPDATE_DELAY; diff --git a/drivers/net/bonding/bond_ipv6.c b/drivers/net/bonding/bond_ipv6.c index 83921abae12d..b72e1dc8cf8f 100644 --- a/drivers/net/bonding/bond_ipv6.c +++ b/drivers/net/bonding/bond_ipv6.c @@ -25,6 +25,7 @@ #include <net/ipv6.h> #include <net/ndisc.h> #include <net/addrconf.h> +#include <net/netns/generic.h> #include "bonding.h" /* @@ -152,11 +153,9 @@ static int bond_inet6addr_event(struct notifier_block *this, struct net_device *vlan_dev, *event_dev = ifa->idev->dev; struct bonding *bond; struct vlan_entry *vlan; + struct bond_net *bn = net_generic(dev_net(event_dev), bond_net_id); - if (dev_net(event_dev) != &init_net) - return NOTIFY_DONE; - - list_for_each_entry(bond, &bond_dev_list, bond_list) { + list_for_each_entry(bond, &bn->dev_list, bond_list) { if (bond->dev == event_dev) { switch (event) { case NETDEV_UP: diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 40fb5eefc72e..af9b9c4eb496 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -75,6 +75,7 @@ #include <linux/jiffies.h> #include <net/route.h> #include <net/net_namespace.h> +#include <net/netns/generic.h> #include "bonding.h" #include "bond_3ad.h" #include "bond_alb.h" @@ -94,6 +95,7 @@ static int downdelay; static int use_carrier = 1; static char *mode; static char *primary; +static char *primary_reselect; static char *lacp_rate; static char *ad_select; static char *xmit_hash_policy; @@ -126,6 +128,14 @@ MODULE_PARM_DESC(mode, "Mode of operation : 0 for balance-rr, " "6 for balance-alb"); module_param(primary, charp, 0); MODULE_PARM_DESC(primary, "Primary network device to use"); +module_param(primary_reselect, charp, 0); +MODULE_PARM_DESC(primary_reselect, "Reselect primary slave " + "once it comes up; " + "0 for always (default), " + "1 for only if speed of primary is " + "better, " + "2 for only on active slave " + "failure"); module_param(lacp_rate, charp, 0); MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner " "(slow/fast)"); @@ -148,11 +158,7 @@ MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to the static const char * const version = DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n"; -LIST_HEAD(bond_dev_list); - -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *bond_proc_dir; -#endif +int bond_net_id __read_mostly; static __be32 arp_target[BOND_MAX_ARP_TARGETS]; static int arp_ip_count; @@ -200,6 +206,13 @@ const struct bond_parm_tbl fail_over_mac_tbl[] = { { NULL, -1}, }; +const struct bond_parm_tbl pri_reselect_tbl[] = { +{ "always", BOND_PRI_RESELECT_ALWAYS}, +{ "better", BOND_PRI_RESELECT_BETTER}, +{ "failure", BOND_PRI_RESELECT_FAILURE}, +{ NULL, -1}, +}; + struct bond_parm_tbl ad_select_tbl[] = { { "stable", BOND_AD_STABLE}, { "bandwidth", BOND_AD_BANDWIDTH}, @@ -211,7 +224,7 @@ struct bond_parm_tbl ad_select_tbl[] = { static void bond_send_gratuitous_arp(struct bonding *bond); static int bond_init(struct net_device *bond_dev); -static void bond_deinit(struct net_device *bond_dev); +static void bond_uninit(struct net_device *bond_dev); /*---------------------------- General routines -----------------------------*/ @@ -1070,6 +1083,25 @@ out: } +static bool bond_should_change_active(struct bonding *bond) +{ + struct slave *prim = bond->primary_slave; + struct slave *curr = bond->curr_active_slave; + + if (!prim || !curr || curr->link != BOND_LINK_UP) + return true; + if (bond->force_primary) { + bond->force_primary = false; + return true; + } + if (bond->params.primary_reselect == BOND_PRI_RESELECT_BETTER && + (prim->speed < curr->speed || + (prim->speed == curr->speed && prim->duplex <= curr->duplex))) + return false; + if (bond->params.primary_reselect == BOND_PRI_RESELECT_FAILURE) + return false; + return true; +} /** * find_best_interface - select the best available slave to be the active one @@ -1084,7 +1116,7 @@ static struct slave *bond_find_best_slave(struct bonding *bond) int mintime = bond->params.updelay; int i; - new_active = old_active = bond->curr_active_slave; + new_active = bond->curr_active_slave; if (!new_active) { /* there were no active slaves left */ if (bond->slave_cnt > 0) /* found one slave */ @@ -1094,7 +1126,8 @@ static struct slave *bond_find_best_slave(struct bonding *bond) } if ((bond->primary_slave) && - bond->primary_slave->link == BOND_LINK_UP) { + bond->primary_slave->link == BOND_LINK_UP && + bond_should_change_active(bond)) { new_active = bond->primary_slave; } @@ -1678,8 +1711,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) { /* if there is a primary slave, remember it */ - if (strcmp(bond->params.primary, new_slave->dev->name) == 0) + if (strcmp(bond->params.primary, new_slave->dev->name) == 0) { bond->primary_slave = new_slave; + bond->force_primary = true; + } } write_lock_bh(&bond->curr_slave_lock); @@ -1817,8 +1852,8 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) } if (!bond->params.fail_over_mac) { - if (!compare_ether_addr(bond_dev->dev_addr, slave->perm_hwaddr) - && bond->slave_cnt > 1) + if (!compare_ether_addr(bond_dev->dev_addr, slave->perm_hwaddr) && + bond->slave_cnt > 1) pr_warning(DRV_NAME ": %s: Warning: the permanent HWaddr of %s - " "%pM - is still in use by %s. " @@ -1965,25 +2000,6 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) } /* -* Destroy a bonding device. -* Must be under rtnl_lock when this function is called. -*/ -static void bond_uninit(struct net_device *bond_dev) -{ - struct bonding *bond = netdev_priv(bond_dev); - - bond_deinit(bond_dev); - bond_destroy_sysfs_entry(bond); - - if (bond->wq) - destroy_workqueue(bond->wq); - - netif_addr_lock_bh(bond_dev); - bond_mc_list_destroy(bond); - netif_addr_unlock_bh(bond_dev); -} - -/* * First release a slave and than destroy the bond if no more slaves are left. * Must be under rtnl_lock when this function is called. */ @@ -2567,7 +2583,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) fl.fl4_dst = targets[i]; fl.fl4_tos = RTO_ONLINK; - rv = ip_route_output_key(&init_net, &rt, &fl); + rv = ip_route_output_key(dev_net(bond->dev), &rt, &fl); if (rv) { if (net_ratelimit()) { pr_warning(DRV_NAME @@ -2675,9 +2691,6 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack unsigned char *arp_ptr; __be32 sip, tip; - if (dev_net(dev) != &init_net) - goto out; - if (!(dev->priv_flags & IFF_BONDING) || !(dev->flags & IFF_MASTER)) goto out; @@ -3201,11 +3214,14 @@ static void bond_info_show_master(struct seq_file *seq) } if (USES_PRIMARY(bond->params.mode)) { - seq_printf(seq, "Primary Slave: %s\n", + seq_printf(seq, "Primary Slave: %s", (bond->primary_slave) ? bond->primary_slave->dev->name : "None"); + if (bond->primary_slave) + seq_printf(seq, " (primary_reselect %s)", + pri_reselect_tbl[bond->params.primary_reselect].modename); - seq_printf(seq, "Currently Active Slave: %s\n", + seq_printf(seq, "\nCurrently Active Slave: %s\n", (curr) ? curr->dev->name : "None"); } @@ -3334,13 +3350,14 @@ static const struct file_operations bond_info_fops = { .release = seq_release, }; -static int bond_create_proc_entry(struct bonding *bond) +static void bond_create_proc_entry(struct bonding *bond) { struct net_device *bond_dev = bond->dev; + struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id); - if (bond_proc_dir) { + if (bn->proc_dir) { bond->proc_entry = proc_create_data(bond_dev->name, - S_IRUGO, bond_proc_dir, + S_IRUGO, bn->proc_dir, &bond_info_fops, bond); if (bond->proc_entry == NULL) pr_warning(DRV_NAME @@ -3349,14 +3366,15 @@ static int bond_create_proc_entry(struct bonding *bond) else memcpy(bond->proc_file_name, bond_dev->name, IFNAMSIZ); } - - return 0; } static void bond_remove_proc_entry(struct bonding *bond) { - if (bond_proc_dir && bond->proc_entry) { - remove_proc_entry(bond->proc_file_name, bond_proc_dir); + struct net_device *bond_dev = bond->dev; + struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id); + + if (bn->proc_dir && bond->proc_entry) { + remove_proc_entry(bond->proc_file_name, bn->proc_dir); memset(bond->proc_file_name, 0, IFNAMSIZ); bond->proc_entry = NULL; } @@ -3365,11 +3383,11 @@ static void bond_remove_proc_entry(struct bonding *bond) /* Create the bonding directory under /proc/net, if doesn't exist yet. * Caller must hold rtnl_lock. */ -static void bond_create_proc_dir(void) +static void bond_create_proc_dir(struct bond_net *bn) { - if (!bond_proc_dir) { - bond_proc_dir = proc_mkdir(DRV_NAME, init_net.proc_net); - if (!bond_proc_dir) + if (!bn->proc_dir) { + bn->proc_dir = proc_mkdir(DRV_NAME, bn->net->proc_net); + if (!bn->proc_dir) pr_warning(DRV_NAME ": Warning: cannot create /proc/net/%s\n", DRV_NAME); @@ -3379,17 +3397,17 @@ static void bond_create_proc_dir(void) /* Destroy the bonding directory under /proc/net, if empty. * Caller must hold rtnl_lock. */ -static void bond_destroy_proc_dir(void) +static void bond_destroy_proc_dir(struct bond_net *bn) { - if (bond_proc_dir) { - remove_proc_entry(DRV_NAME, init_net.proc_net); - bond_proc_dir = NULL; + if (bn->proc_dir) { + remove_proc_entry(DRV_NAME, bn->net->proc_net); + bn->proc_dir = NULL; } } #else /* !CONFIG_PROC_FS */ -static int bond_create_proc_entry(struct bonding *bond) +static void bond_create_proc_entry(struct bonding *bond) { } @@ -3397,11 +3415,11 @@ static void bond_remove_proc_entry(struct bonding *bond) { } -static void bond_create_proc_dir(void) +static void bond_create_proc_dir(struct bond_net *bn) { } -static void bond_destroy_proc_dir(void) +static void bond_destroy_proc_dir(struct bond_net *bn) { } @@ -3418,9 +3436,6 @@ static int bond_event_changename(struct bonding *bond) bond_remove_proc_entry(bond); bond_create_proc_entry(bond); - bond_destroy_sysfs_entry(bond); - bond_create_sysfs_entry(bond); - return NOTIFY_DONE; } @@ -3432,9 +3447,6 @@ static int bond_master_netdev_event(unsigned long event, switch (event) { case NETDEV_CHANGENAME: return bond_event_changename(event_bond); - case NETDEV_UNREGISTER: - bond_release_all(event_bond->dev); - break; default: break; } @@ -3526,9 +3538,6 @@ static int bond_netdev_event(struct notifier_block *this, { struct net_device *event_dev = (struct net_device *)ptr; - if (dev_net(event_dev) != &init_net) - return NOTIFY_DONE; - pr_debug("event_dev: %s, event: %lx\n", (event_dev ? event_dev->name : "None"), event); @@ -3561,13 +3570,11 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, { struct in_ifaddr *ifa = ptr; struct net_device *vlan_dev, *event_dev = ifa->ifa_dev->dev; + struct bond_net *bn = net_generic(dev_net(event_dev), bond_net_id); struct bonding *bond; struct vlan_entry *vlan; - if (dev_net(ifa->ifa_dev->dev) != &init_net) - return NOTIFY_DONE; - - list_for_each_entry(bond, &bond_dev_list, bond_list) { + list_for_each_entry(bond, &bn->dev_list, bond_list) { if (bond->dev == event_dev) { switch (event) { case NETDEV_UP: @@ -3657,8 +3664,7 @@ void bond_unregister_arp(struct bonding *bond) * Hash for the output device based upon layer 2 and layer 3 data. If * the packet is not IP mimic bond_xmit_hash_policy_l2() */ -static int bond_xmit_hash_policy_l23(struct sk_buff *skb, - struct net_device *bond_dev, int count) +static int bond_xmit_hash_policy_l23(struct sk_buff *skb, int count) { struct ethhdr *data = (struct ethhdr *)skb->data; struct iphdr *iph = ip_hdr(skb); @@ -3676,8 +3682,7 @@ static int bond_xmit_hash_policy_l23(struct sk_buff *skb, * the packet is a frag or not TCP or UDP, just use layer 3 data. If it is * altogether not IP, mimic bond_xmit_hash_policy_l2() */ -static int bond_xmit_hash_policy_l34(struct sk_buff *skb, - struct net_device *bond_dev, int count) +static int bond_xmit_hash_policy_l34(struct sk_buff *skb, int count) { struct ethhdr *data = (struct ethhdr *)skb->data; struct iphdr *iph = ip_hdr(skb); @@ -3701,8 +3706,7 @@ static int bond_xmit_hash_policy_l34(struct sk_buff *skb, /* * Hash for the output device based upon layer 2 data */ -static int bond_xmit_hash_policy_l2(struct sk_buff *skb, - struct net_device *bond_dev, int count) +static int bond_xmit_hash_policy_l2(struct sk_buff *skb, int count) { struct ethhdr *data = (struct ethhdr *)skb->data; @@ -3939,7 +3943,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd if (!capable(CAP_NET_ADMIN)) return -EPERM; - slave_dev = dev_get_by_name(&init_net, ifr->ifr_slave); + slave_dev = dev_get_by_name(dev_net(bond_dev), ifr->ifr_slave); pr_debug("slave_dev=%p: \n", slave_dev); @@ -4295,7 +4299,7 @@ static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev) if (!BOND_IS_OK(bond)) goto out; - slave_no = bond->xmit_hash_policy(skb, bond_dev, bond->slave_cnt); + slave_no = bond->xmit_hash_policy(skb, bond->slave_cnt); bond_for_each_slave(bond, slave, i) { slave_no--; @@ -4576,37 +4580,29 @@ static void bond_work_cancel_all(struct bonding *bond) cancel_delayed_work(&bond->ad_work); } -/* De-initialize device specific data. - * Caller must hold rtnl_lock. - */ -static void bond_deinit(struct net_device *bond_dev) +/* +* Destroy a bonding device. +* Must be under rtnl_lock when this function is called. +*/ +static void bond_uninit(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + /* Release the bonded slaves */ + bond_release_all(bond_dev); + list_del(&bond->bond_list); bond_work_cancel_all(bond); bond_remove_proc_entry(bond); -} - -/* Unregister and free all bond devices. - * Caller must hold rtnl_lock. - */ -static void bond_free_all(void) -{ - struct bonding *bond, *nxt; - list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) { - struct net_device *bond_dev = bond->dev; - - bond_work_cancel_all(bond); - /* Release the bonded slaves */ - bond_release_all(bond_dev); - unregister_netdevice(bond_dev); - } + if (bond->wq) + destroy_workqueue(bond->wq); - bond_destroy_proc_dir(); + netif_addr_lock_bh(bond_dev); + bond_mc_list_destroy(bond); + netif_addr_unlock_bh(bond_dev); } /*------------------------- Module initialization ---------------------------*/ @@ -4646,7 +4642,7 @@ int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl) static int bond_check_params(struct bond_params *params) { - int arp_validate_value, fail_over_mac_value; + int arp_validate_value, fail_over_mac_value, primary_reselect_value; /* * Convert string parameters. @@ -4665,7 +4661,8 @@ static int bond_check_params(struct bond_params *params) if ((bond_mode != BOND_MODE_XOR) && (bond_mode != BOND_MODE_8023AD)) { pr_info(DRV_NAME - ": xor_mode param is irrelevant in mode %s\n", + ": xmit_hash_policy param is irrelevant in" + " mode %s\n", bond_mode_name(bond_mode)); } else { xmit_hashtype = bond_parse_parm(xmit_hash_policy, @@ -4945,6 +4942,20 @@ static int bond_check_params(struct bond_params *params) primary = NULL; } + if (primary && primary_reselect) { + primary_reselect_value = bond_parse_parm(primary_reselect, + pri_reselect_tbl); + if (primary_reselect_value == -1) { + pr_err(DRV_NAME + ": Error: Invalid primary_reselect \"%s\"\n", + primary_reselect == + NULL ? "NULL" : primary_reselect); + return -EINVAL; + } + } else { + primary_reselect_value = BOND_PRI_RESELECT_ALWAYS; + } + if (fail_over_mac) { fail_over_mac_value = bond_parse_parm(fail_over_mac, fail_over_mac_tbl); @@ -4976,6 +4987,7 @@ static int bond_check_params(struct bond_params *params) params->use_carrier = use_carrier; params->lacp_fast = lacp_fast; params->primary[0] = 0; + params->primary_reselect = primary_reselect_value; params->fail_over_mac = fail_over_mac_value; if (primary) { @@ -5012,6 +5024,7 @@ static void bond_set_lockdep_class(struct net_device *dev) static int bond_init(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id); pr_debug("Begin bond_init for %s\n", bond_dev->name); @@ -5024,30 +5037,41 @@ static int bond_init(struct net_device *bond_dev) netif_carrier_off(bond_dev); bond_create_proc_entry(bond); - list_add_tail(&bond->bond_list, &bond_dev_list); + list_add_tail(&bond->bond_list, &bn->dev_list); + bond_prepare_sysfs_group(bond); + return 0; +} + +static int bond_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } return 0; } +static struct rtnl_link_ops bond_link_ops __read_mostly = { + .kind = "bond", + .priv_size = sizeof(struct bonding), + .setup = bond_setup, + .validate = bond_validate, +}; + /* Create a new bond based on the specified name and bonding parameters. * If name is NULL, obtain a suitable "bond%d" name for us. * Caller must NOT hold rtnl_lock; we need to release it here before we * set up our sysfs entries. */ -int bond_create(const char *name) +int bond_create(struct net *net, const char *name) { struct net_device *bond_dev; int res; rtnl_lock(); - /* Check to see if the bond already exists. */ - /* FIXME: pass netns from caller */ - if (name && __dev_get_by_name(&init_net, name)) { - pr_err(DRV_NAME ": cannot add bond %s; already exists\n", - name); - res = -EEXIST; - goto out_rtnl; - } bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "", bond_setup); @@ -5055,9 +5079,12 @@ int bond_create(const char *name) pr_err(DRV_NAME ": %s: eek! can't alloc netdev!\n", name); res = -ENOMEM; - goto out_rtnl; + goto out; } + dev_net_set(bond_dev, net); + bond_dev->rtnl_link_ops = &bond_link_ops; + if (!name) { res = dev_alloc_name(bond_dev, "bond%d"); if (res < 0) @@ -5065,27 +5092,41 @@ int bond_create(const char *name) } res = register_netdevice(bond_dev); - if (res < 0) - goto out_bond; - - res = bond_create_sysfs_entry(netdev_priv(bond_dev)); - if (res < 0) - goto out_unreg; +out: rtnl_unlock(); - return 0; - -out_unreg: - unregister_netdevice(bond_dev); -out_bond: - bond_deinit(bond_dev); + return res; out_netdev: free_netdev(bond_dev); -out_rtnl: - rtnl_unlock(); - return res; + goto out; +} + +static int bond_net_init(struct net *net) +{ + struct bond_net *bn = net_generic(net, bond_net_id); + + bn->net = net; + INIT_LIST_HEAD(&bn->dev_list); + + bond_create_proc_dir(bn); + + return 0; } +static void bond_net_exit(struct net *net) +{ + struct bond_net *bn = net_generic(net, bond_net_id); + + bond_destroy_proc_dir(bn); +} + +static struct pernet_operations bond_net_ops = { + .init = bond_net_init, + .exit = bond_net_exit, + .id = &bond_net_id, + .size = sizeof(struct bond_net), +}; + static int __init bonding_init(void) { int i; @@ -5097,10 +5138,16 @@ static int __init bonding_init(void) if (res) goto out; - bond_create_proc_dir(); + res = register_pernet_subsys(&bond_net_ops); + if (res) + goto out; + + res = rtnl_link_register(&bond_link_ops); + if (res) + goto err_link; for (i = 0; i < max_bonds; i++) { - res = bond_create(NULL); + res = bond_create(&init_net, NULL); if (res) goto err; } @@ -5112,14 +5159,13 @@ static int __init bonding_init(void) register_netdevice_notifier(&bond_netdev_notifier); register_inetaddr_notifier(&bond_inetaddr_notifier); bond_register_ipv6_notifier(); - - goto out; -err: - rtnl_lock(); - bond_free_all(); - rtnl_unlock(); out: return res; +err: + rtnl_link_unregister(&bond_link_ops); +err_link: + unregister_pernet_subsys(&bond_net_ops); + goto out; } @@ -5131,9 +5177,8 @@ static void __exit bonding_exit(void) bond_destroy_sysfs(); - rtnl_lock(); - bond_free_all(); - rtnl_unlock(); + rtnl_link_unregister(&bond_link_ops); + unregister_pernet_subsys(&bond_net_ops); } module_init(bonding_init); @@ -5142,3 +5187,4 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION); MODULE_AUTHOR("Thomas Davis, tadavis@lbl.gov and many others"); +MODULE_ALIAS_RTNL_LINK("bond"); diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 8762a27a2a18..4e00b4f83641 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -36,6 +36,8 @@ #include <linux/rtnetlink.h> #include <linux/etherdevice.h> #include <net/net_namespace.h> +#include <net/netns/generic.h> +#include <linux/nsproxy.h> #include "bonding.h" @@ -48,12 +50,14 @@ */ static ssize_t bonding_show_bonds(struct class *cls, char *buf) { + struct net *net = current->nsproxy->net_ns; + struct bond_net *bn = net_generic(net, bond_net_id); int res = 0; struct bonding *bond; rtnl_lock(); - list_for_each_entry(bond, &bond_dev_list, bond_list) { + list_for_each_entry(bond, &bn->dev_list, bond_list) { if (res > (PAGE_SIZE - IFNAMSIZ)) { /* not enough space for another interface name */ if ((PAGE_SIZE - res) > 10) @@ -70,11 +74,12 @@ static ssize_t bonding_show_bonds(struct class *cls, char *buf) return res; } -static struct net_device *bond_get_by_name(const char *ifname) +static struct net_device *bond_get_by_name(struct net *net, const char *ifname) { + struct bond_net *bn = net_generic(net, bond_net_id); struct bonding *bond; - list_for_each_entry(bond, &bond_dev_list, bond_list) { + list_for_each_entry(bond, &bn->dev_list, bond_list) { if (strncmp(bond->dev->name, ifname, IFNAMSIZ) == 0) return bond->dev; } @@ -92,6 +97,7 @@ static struct net_device *bond_get_by_name(const char *ifname) static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t count) { + struct net *net = current->nsproxy->net_ns; char command[IFNAMSIZ + 1] = {0, }; char *ifname; int rv, res = count; @@ -105,7 +111,7 @@ static ssize_t bonding_store_bonds(struct class *cls, if (command[0] == '+') { pr_info(DRV_NAME ": %s is being created...\n", ifname); - rv = bond_create(ifname); + rv = bond_create(net, ifname); if (rv) { pr_info(DRV_NAME ": Bond creation failed.\n"); res = rv; @@ -114,7 +120,7 @@ static ssize_t bonding_store_bonds(struct class *cls, struct net_device *bond_dev; rtnl_lock(); - bond_dev = bond_get_by_name(ifname); + bond_dev = bond_get_by_name(net, ifname); if (bond_dev) { pr_info(DRV_NAME ": %s is being deleted...\n", ifname); @@ -239,8 +245,7 @@ static ssize_t bonding_store_slaves(struct device *d, /* Got a slave name in ifname. Is it already in the list? */ found = 0; - /* FIXME: get netns from sysfs object */ - dev = __dev_get_by_name(&init_net, ifname); + dev = __dev_get_by_name(dev_net(bond->dev), ifname); if (!dev) { pr_info(DRV_NAME ": %s: Interface %s does not exist!\n", @@ -1214,6 +1219,58 @@ static DEVICE_ATTR(primary, S_IRUGO | S_IWUSR, bonding_show_primary, bonding_store_primary); /* + * Show and set the primary_reselect flag. + */ +static ssize_t bonding_show_primary_reselect(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct bonding *bond = to_bond(d); + + return sprintf(buf, "%s %d\n", + pri_reselect_tbl[bond->params.primary_reselect].modename, + bond->params.primary_reselect); +} + +static ssize_t bonding_store_primary_reselect(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int new_value, ret = count; + struct bonding *bond = to_bond(d); + + if (!rtnl_trylock()) + return restart_syscall(); + + new_value = bond_parse_parm(buf, pri_reselect_tbl); + if (new_value < 0) { + pr_err(DRV_NAME + ": %s: Ignoring invalid primary_reselect value %.*s.\n", + bond->dev->name, + (int) strlen(buf) - 1, buf); + ret = -EINVAL; + goto out; + } + + bond->params.primary_reselect = new_value; + pr_info(DRV_NAME ": %s: setting primary_reselect to %s (%d).\n", + bond->dev->name, pri_reselect_tbl[new_value].modename, + new_value); + + read_lock(&bond->lock); + write_lock_bh(&bond->curr_slave_lock); + bond_select_active_slave(bond); + write_unlock_bh(&bond->curr_slave_lock); + read_unlock(&bond->lock); +out: + rtnl_unlock(); + return ret; +} +static DEVICE_ATTR(primary_reselect, S_IRUGO | S_IWUSR, + bonding_show_primary_reselect, + bonding_store_primary_reselect); + +/* * Show and set the use_carrier flag. */ static ssize_t bonding_show_carrier(struct device *d, @@ -1502,6 +1559,7 @@ static struct attribute *per_bond_attrs[] = { &dev_attr_num_unsol_na.attr, &dev_attr_miimon.attr, &dev_attr_primary.attr, + &dev_attr_primary_reselect.attr, &dev_attr_use_carrier.attr, &dev_attr_active_slave.attr, &dev_attr_mii_status.attr, @@ -1564,24 +1622,8 @@ void bond_destroy_sysfs(void) * Initialize sysfs for each bond. This sets up and registers * the 'bondctl' directory for each individual bond under /sys/class/net. */ -int bond_create_sysfs_entry(struct bonding *bond) +void bond_prepare_sysfs_group(struct bonding *bond) { - struct net_device *dev = bond->dev; - int err; - - err = sysfs_create_group(&(dev->dev.kobj), &bonding_group); - if (err) - pr_emerg("eek! didn't create group!\n"); - - return err; -} -/* - * Remove sysfs entries for each bond. - */ -void bond_destroy_sysfs_entry(struct bonding *bond) -{ - struct net_device *dev = bond->dev; - - sysfs_remove_group(&(dev->dev.kobj), &bonding_group); + bond->dev->sysfs_groups[0] = &bonding_group; } diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 68247714466f..558ec1352527 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -23,15 +23,13 @@ #include "bond_3ad.h" #include "bond_alb.h" -#define DRV_VERSION "3.5.0" -#define DRV_RELDATE "November 4, 2008" +#define DRV_VERSION "3.6.0" +#define DRV_RELDATE "September 26, 2009" #define DRV_NAME "bonding" #define DRV_DESCRIPTION "Ethernet Channel Bonding Driver" #define BOND_MAX_ARP_TARGETS 16 -extern struct list_head bond_dev_list; - #define IS_UP(dev) \ ((((dev)->flags & IFF_UP) == IFF_UP) && \ netif_running(dev) && \ @@ -131,6 +129,7 @@ struct bond_params { int lacp_fast; int ad_select; char primary[IFNAMSIZ]; + int primary_reselect; __be32 arp_targets[BOND_MAX_ARP_TARGETS]; }; @@ -190,6 +189,7 @@ struct bonding { struct slave *curr_active_slave; struct slave *current_arp_slave; struct slave *primary_slave; + bool force_primary; s32 slave_cnt; /* never change this value outside the attach/detach wrappers */ rwlock_t lock; rwlock_t curr_slave_lock; @@ -204,7 +204,7 @@ struct bonding { #endif /* CONFIG_PROC_FS */ struct list_head bond_list; struct dev_mc_list *mc_list; - int (*xmit_hash_policy)(struct sk_buff *, struct net_device *, int); + int (*xmit_hash_policy)(struct sk_buff *, int); __be32 master_ip; u16 flags; u16 rr_tx_counter; @@ -254,10 +254,14 @@ static inline struct bonding *bond_get_bond_by_slave(struct slave *slave) static inline bool bond_is_lb(const struct bonding *bond) { - return bond->params.mode == BOND_MODE_TLB - || bond->params.mode == BOND_MODE_ALB; + return (bond->params.mode == BOND_MODE_TLB || + bond->params.mode == BOND_MODE_ALB); } +#define BOND_PRI_RESELECT_ALWAYS 0 +#define BOND_PRI_RESELECT_BETTER 1 +#define BOND_PRI_RESELECT_FAILURE 2 + #define BOND_FOM_NONE 0 #define BOND_FOM_ACTIVE 1 #define BOND_FOM_FOLLOW 2 @@ -321,12 +325,11 @@ static inline void bond_unset_master_alb_flags(struct bonding *bond) struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr); int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); -int bond_create(const char *name); +int bond_create(struct net *net, const char *name); int bond_release_and_destroy(struct net_device *bond_dev, struct net_device *slave_dev); int bond_create_sysfs(void); void bond_destroy_sysfs(void); -void bond_destroy_sysfs_entry(struct bonding *bond); -int bond_create_sysfs_entry(struct bonding *bond); +void bond_prepare_sysfs_group(struct bonding *bond); int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave); void bond_destroy_slave_symlinks(struct net_device *master, struct net_device *slave); int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev); @@ -341,13 +344,22 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active); void bond_register_arp(struct bonding *); void bond_unregister_arp(struct bonding *); +struct bond_net { + struct net * net; /* Associated network namespace */ + struct list_head dev_list; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry * proc_dir; +#endif +}; + /* exported from bond_main.c */ -extern struct list_head bond_dev_list; +extern int bond_net_id; extern const struct bond_parm_tbl bond_lacp_tbl[]; extern const struct bond_parm_tbl bond_mode_tbl[]; extern const struct bond_parm_tbl xmit_hashtype_tbl[]; extern const struct bond_parm_tbl arp_validate_tbl[]; extern const struct bond_parm_tbl fail_over_mac_tbl[]; +extern const struct bond_parm_tbl pri_reselect_tbl[]; extern struct bond_parm_tbl ad_select_tbl[]; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -370,4 +382,3 @@ static inline void bond_unregister_ipv6_notifier(void) #endif #endif /* _LINUX_BONDING_H */ - diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 772f6d2489ce..bb803fa1e6a7 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -41,6 +41,21 @@ config CAN_AT91 ---help--- This is a driver for the SoC CAN controller in Atmel's AT91SAM9263. +config CAN_TI_HECC + depends on CAN_DEV && ARCH_OMAP3 + tristate "TI High End CAN Controller" + ---help--- + Driver for TI HECC (High End CAN Controller) module found on many + TI devices. The device specifications are available from www.ti.com + +config CAN_MCP251X + tristate "Microchip MCP251x SPI CAN controllers" + depends on CAN_DEV && SPI + ---help--- + Driver for the Microchip MCP251x SPI CAN controllers. + +source "drivers/net/can/mscan/Kconfig" + source "drivers/net/can/sja1000/Kconfig" source "drivers/net/can/usb/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 0dea62721f2f..56899fef1c6a 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -10,6 +10,9 @@ can-dev-y := dev.o obj-y += usb/ obj-$(CONFIG_CAN_SJA1000) += sja1000/ +obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_AT91) += at91_can.o +obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o +obj-$(CONFIG_CAN_MCP251X) += mcp251x.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index f67ae285a35a..cbe3fce53e3b 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -221,38 +221,6 @@ static inline void set_mb_mode(const struct at91_priv *priv, unsigned int mb, set_mb_mode_prio(priv, mb, mode, 0); } -static struct sk_buff *alloc_can_skb(struct net_device *dev, - struct can_frame **cf) -{ - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, sizeof(struct can_frame)); - if (unlikely(!skb)) - return NULL; - - skb->protocol = htons(ETH_P_CAN); - skb->ip_summed = CHECKSUM_UNNECESSARY; - *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); - - return skb; -} - -static struct sk_buff *alloc_can_err_skb(struct net_device *dev, - struct can_frame **cf) -{ - struct sk_buff *skb; - - skb = alloc_can_skb(dev, cf); - if (unlikely(!skb)) - return NULL; - - memset(*cf, 0, sizeof(struct can_frame)); - (*cf)->can_id = CAN_ERR_FLAG; - (*cf)->can_dlc = CAN_ERR_DLC; - - return skb; -} - /* * Swtich transceiver on or off */ @@ -1087,7 +1055,7 @@ static int __init at91_can_probe(struct platform_device *pdev) goto exit_release; } - dev = alloc_candev(sizeof(struct at91_priv)); + dev = alloc_candev(sizeof(struct at91_priv), AT91_MB_TX_NUM); if (!dev) { err = -ENOMEM; goto exit_iounmap; diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 2868fe842a41..c1bb29f0322b 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -245,7 +245,7 @@ static void can_flush_echo_skb(struct net_device *dev) struct net_device_stats *stats = &dev->stats; int i; - for (i = 0; i < CAN_ECHO_SKB_MAX; i++) { + for (i = 0; i < priv->echo_skb_max; i++) { if (priv->echo_skb[i]) { kfree_skb(priv->echo_skb[i]); priv->echo_skb[i] = NULL; @@ -262,10 +262,13 @@ static void can_flush_echo_skb(struct net_device *dev) * of the device driver. The driver must protect access to * priv->echo_skb, if necessary. */ -void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx) +void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, + unsigned int idx) { struct can_priv *priv = netdev_priv(dev); + BUG_ON(idx >= priv->echo_skb_max); + /* check flag whether this packet has to be looped back */ if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) { kfree_skb(skb); @@ -311,10 +314,12 @@ EXPORT_SYMBOL_GPL(can_put_echo_skb); * is handled in the device driver. The driver must protect * access to priv->echo_skb, if necessary. */ -void can_get_echo_skb(struct net_device *dev, int idx) +void can_get_echo_skb(struct net_device *dev, unsigned int idx) { struct can_priv *priv = netdev_priv(dev); + BUG_ON(idx >= priv->echo_skb_max); + if (priv->echo_skb[idx]) { netif_rx(priv->echo_skb[idx]); priv->echo_skb[idx] = NULL; @@ -327,10 +332,12 @@ EXPORT_SYMBOL_GPL(can_get_echo_skb); * * The function is typically called when TX failed. */ -void can_free_echo_skb(struct net_device *dev, int idx) +void can_free_echo_skb(struct net_device *dev, unsigned int idx) { struct can_priv *priv = netdev_priv(dev); + BUG_ON(idx >= priv->echo_skb_max); + if (priv->echo_skb[idx]) { kfree_skb(priv->echo_skb[idx]); priv->echo_skb[idx] = NULL; @@ -359,17 +366,12 @@ void can_restart(unsigned long data) can_flush_echo_skb(dev); /* send restart message upstream */ - skb = dev_alloc_skb(sizeof(struct can_frame)); + skb = alloc_can_err_skb(dev, &cf); if (skb == NULL) { err = -ENOMEM; goto restart; } - skb->dev = dev; - skb->protocol = htons(ETH_P_CAN); - cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); - memset(cf, 0, sizeof(struct can_frame)); - cf->can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED; - cf->can_dlc = CAN_ERR_DLC; + cf->can_id |= CAN_ERR_RESTARTED; netif_rx(skb); @@ -442,20 +444,66 @@ static void can_setup(struct net_device *dev) dev->features = NETIF_F_NO_CSUM; } +struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, sizeof(struct can_frame)); + if (unlikely(!skb)) + return NULL; + + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + memset(*cf, 0, sizeof(struct can_frame)); + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_can_skb); + +struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf) +{ + struct sk_buff *skb; + + skb = alloc_can_skb(dev, cf); + if (unlikely(!skb)) + return NULL; + + (*cf)->can_id = CAN_ERR_FLAG; + (*cf)->can_dlc = CAN_ERR_DLC; + + return skb; +} +EXPORT_SYMBOL_GPL(alloc_can_err_skb); + /* * Allocate and setup space for the CAN network device */ -struct net_device *alloc_candev(int sizeof_priv) +struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max) { struct net_device *dev; struct can_priv *priv; + int size; - dev = alloc_netdev(sizeof_priv, "can%d", can_setup); + if (echo_skb_max) + size = ALIGN(sizeof_priv, sizeof(struct sk_buff *)) + + echo_skb_max * sizeof(struct sk_buff *); + else + size = sizeof_priv; + + dev = alloc_netdev(size, "can%d", can_setup); if (!dev) return NULL; priv = netdev_priv(dev); + if (echo_skb_max) { + priv->echo_skb_max = echo_skb_max; + priv->echo_skb = (void *)priv + + ALIGN(sizeof_priv, sizeof(struct sk_buff *)); + } + priv->state = CAN_STATE_STOPPED; init_timer(&priv->restart_timer); @@ -647,7 +695,7 @@ nla_put_failure: return -EMSGSIZE; } -static int can_newlink(struct net_device *dev, +static int can_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { return -EOPNOTSUPP; diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c new file mode 100644 index 000000000000..78b1b69b2921 --- /dev/null +++ b/drivers/net/can/mcp251x.c @@ -0,0 +1,1166 @@ +/* + * CAN bus driver for Microchip 251x CAN Controller with SPI Interface + * + * MCP2510 support and bug fixes by Christian Pellegrin + * <chripell@evolware.org> + * + * Copyright 2009 Christian Pellegrin EVOL S.r.l. + * + * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved. + * Written under contract by: + * Chris Elston, Katalix Systems, Ltd. + * + * Based on Microchip MCP251x CAN controller driver written by + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd. + * + * Based on CAN bus driver for the CCAN controller written by + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix + * - Simon Kallweit, intefo AG + * Copyright 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * + * Your platform definition file should specify something like: + * + * static struct mcp251x_platform_data mcp251x_info = { + * .oscillator_frequency = 8000000, + * .board_specific_setup = &mcp251x_setup, + * .model = CAN_MCP251X_MCP2510, + * .power_enable = mcp251x_power_enable, + * .transceiver_enable = NULL, + * }; + * + * static struct spi_board_info spi_board_info[] = { + * { + * .modalias = "mcp251x", + * .platform_data = &mcp251x_info, + * .irq = IRQ_EINT13, + * .max_speed_hz = 2*1000*1000, + * .chip_select = 2, + * }, + * }; + * + * Please see mcp251x.h for a description of the fields in + * struct mcp251x_platform_data. + * + */ + +#include <linux/can.h> +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/can/platform/mcp251x.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/freezer.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/uaccess.h> + +/* SPI interface instruction set */ +#define INSTRUCTION_WRITE 0x02 +#define INSTRUCTION_READ 0x03 +#define INSTRUCTION_BIT_MODIFY 0x05 +#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n)) +#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94) +#define INSTRUCTION_RESET 0xC0 + +/* MPC251x registers */ +#define CANSTAT 0x0e +#define CANCTRL 0x0f +# define CANCTRL_REQOP_MASK 0xe0 +# define CANCTRL_REQOP_CONF 0x80 +# define CANCTRL_REQOP_LISTEN_ONLY 0x60 +# define CANCTRL_REQOP_LOOPBACK 0x40 +# define CANCTRL_REQOP_SLEEP 0x20 +# define CANCTRL_REQOP_NORMAL 0x00 +# define CANCTRL_OSM 0x08 +# define CANCTRL_ABAT 0x10 +#define TEC 0x1c +#define REC 0x1d +#define CNF1 0x2a +# define CNF1_SJW_SHIFT 6 +#define CNF2 0x29 +# define CNF2_BTLMODE 0x80 +# define CNF2_SAM 0x40 +# define CNF2_PS1_SHIFT 3 +#define CNF3 0x28 +# define CNF3_SOF 0x08 +# define CNF3_WAKFIL 0x04 +# define CNF3_PHSEG2_MASK 0x07 +#define CANINTE 0x2b +# define CANINTE_MERRE 0x80 +# define CANINTE_WAKIE 0x40 +# define CANINTE_ERRIE 0x20 +# define CANINTE_TX2IE 0x10 +# define CANINTE_TX1IE 0x08 +# define CANINTE_TX0IE 0x04 +# define CANINTE_RX1IE 0x02 +# define CANINTE_RX0IE 0x01 +#define CANINTF 0x2c +# define CANINTF_MERRF 0x80 +# define CANINTF_WAKIF 0x40 +# define CANINTF_ERRIF 0x20 +# define CANINTF_TX2IF 0x10 +# define CANINTF_TX1IF 0x08 +# define CANINTF_TX0IF 0x04 +# define CANINTF_RX1IF 0x02 +# define CANINTF_RX0IF 0x01 +#define EFLG 0x2d +# define EFLG_EWARN 0x01 +# define EFLG_RXWAR 0x02 +# define EFLG_TXWAR 0x04 +# define EFLG_RXEP 0x08 +# define EFLG_TXEP 0x10 +# define EFLG_TXBO 0x20 +# define EFLG_RX0OVR 0x40 +# define EFLG_RX1OVR 0x80 +#define TXBCTRL(n) (((n) * 0x10) + 0x30 + TXBCTRL_OFF) +# define TXBCTRL_ABTF 0x40 +# define TXBCTRL_MLOA 0x20 +# define TXBCTRL_TXERR 0x10 +# define TXBCTRL_TXREQ 0x08 +#define TXBSIDH(n) (((n) * 0x10) + 0x30 + TXBSIDH_OFF) +# define SIDH_SHIFT 3 +#define TXBSIDL(n) (((n) * 0x10) + 0x30 + TXBSIDL_OFF) +# define SIDL_SID_MASK 7 +# define SIDL_SID_SHIFT 5 +# define SIDL_EXIDE_SHIFT 3 +# define SIDL_EID_SHIFT 16 +# define SIDL_EID_MASK 3 +#define TXBEID8(n) (((n) * 0x10) + 0x30 + TXBEID8_OFF) +#define TXBEID0(n) (((n) * 0x10) + 0x30 + TXBEID0_OFF) +#define TXBDLC(n) (((n) * 0x10) + 0x30 + TXBDLC_OFF) +# define DLC_RTR_SHIFT 6 +#define TXBCTRL_OFF 0 +#define TXBSIDH_OFF 1 +#define TXBSIDL_OFF 2 +#define TXBEID8_OFF 3 +#define TXBEID0_OFF 4 +#define TXBDLC_OFF 5 +#define TXBDAT_OFF 6 +#define RXBCTRL(n) (((n) * 0x10) + 0x60 + RXBCTRL_OFF) +# define RXBCTRL_BUKT 0x04 +# define RXBCTRL_RXM0 0x20 +# define RXBCTRL_RXM1 0x40 +#define RXBSIDH(n) (((n) * 0x10) + 0x60 + RXBSIDH_OFF) +# define RXBSIDH_SHIFT 3 +#define RXBSIDL(n) (((n) * 0x10) + 0x60 + RXBSIDL_OFF) +# define RXBSIDL_IDE 0x08 +# define RXBSIDL_EID 3 +# define RXBSIDL_SHIFT 5 +#define RXBEID8(n) (((n) * 0x10) + 0x60 + RXBEID8_OFF) +#define RXBEID0(n) (((n) * 0x10) + 0x60 + RXBEID0_OFF) +#define RXBDLC(n) (((n) * 0x10) + 0x60 + RXBDLC_OFF) +# define RXBDLC_LEN_MASK 0x0f +# define RXBDLC_RTR 0x40 +#define RXBCTRL_OFF 0 +#define RXBSIDH_OFF 1 +#define RXBSIDL_OFF 2 +#define RXBEID8_OFF 3 +#define RXBEID0_OFF 4 +#define RXBDLC_OFF 5 +#define RXBDAT_OFF 6 + +#define GET_BYTE(val, byte) \ + (((val) >> ((byte) * 8)) & 0xff) +#define SET_BYTE(val, byte) \ + (((val) & 0xff) << ((byte) * 8)) + +/* + * Buffer size required for the largest SPI transfer (i.e., reading a + * frame) + */ +#define CAN_FRAME_MAX_DATA_LEN 8 +#define SPI_TRANSFER_BUF_LEN (6 + CAN_FRAME_MAX_DATA_LEN) +#define CAN_FRAME_MAX_BITS 128 + +#define TX_ECHO_SKB_MAX 1 + +#define DEVICE_NAME "mcp251x" + +static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */ +module_param(mcp251x_enable_dma, int, S_IRUGO); +MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)"); + +static struct can_bittiming_const mcp251x_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 3, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +struct mcp251x_priv { + struct can_priv can; + struct net_device *net; + struct spi_device *spi; + + struct mutex spi_lock; /* SPI buffer lock */ + u8 *spi_tx_buf; + u8 *spi_rx_buf; + dma_addr_t spi_tx_dma; + dma_addr_t spi_rx_dma; + + struct sk_buff *tx_skb; + int tx_len; + struct workqueue_struct *wq; + struct work_struct tx_work; + struct work_struct irq_work; + struct completion awake; + int wake; + int force_quit; + int after_suspend; +#define AFTER_SUSPEND_UP 1 +#define AFTER_SUSPEND_DOWN 2 +#define AFTER_SUSPEND_POWER 4 +#define AFTER_SUSPEND_RESTART 8 + int restart_tx; +}; + +static void mcp251x_clean(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + + net->stats.tx_errors++; + if (priv->tx_skb) + dev_kfree_skb(priv->tx_skb); + if (priv->tx_len) + can_free_echo_skb(priv->net, 0); + priv->tx_skb = NULL; + priv->tx_len = 0; +} + +/* + * Note about handling of error return of mcp251x_spi_trans: accessing + * registers via SPI is not really different conceptually than using + * normal I/O assembler instructions, although it's much more + * complicated from a practical POV. So it's not advisable to always + * check the return value of this function. Imagine that every + * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0) + * error();", it would be a great mess (well there are some situation + * when exception handling C++ like could be useful after all). So we + * just check that transfers are OK at the beginning of our + * conversation with the chip and to avoid doing really nasty things + * (like injecting bogus packets in the network stack). + */ +static int mcp251x_spi_trans(struct spi_device *spi, int len) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct spi_transfer t = { + .tx_buf = priv->spi_tx_buf, + .rx_buf = priv->spi_rx_buf, + .len = len, + .cs_change = 0, + }; + struct spi_message m; + int ret; + + spi_message_init(&m); + + if (mcp251x_enable_dma) { + t.tx_dma = priv->spi_tx_dma; + t.rx_dma = priv->spi_rx_dma; + m.is_dma_mapped = 1; + } + + spi_message_add_tail(&t, &m); + + ret = spi_sync(spi, &m); + if (ret) + dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret); + return ret; +} + +static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + u8 val = 0; + + mutex_lock(&priv->spi_lock); + + priv->spi_tx_buf[0] = INSTRUCTION_READ; + priv->spi_tx_buf[1] = reg; + + mcp251x_spi_trans(spi, 3); + val = priv->spi_rx_buf[2]; + + mutex_unlock(&priv->spi_lock); + + return val; +} + +static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + mutex_lock(&priv->spi_lock); + + priv->spi_tx_buf[0] = INSTRUCTION_WRITE; + priv->spi_tx_buf[1] = reg; + priv->spi_tx_buf[2] = val; + + mcp251x_spi_trans(spi, 3); + + mutex_unlock(&priv->spi_lock); +} + +static void mcp251x_write_bits(struct spi_device *spi, u8 reg, + u8 mask, uint8_t val) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + mutex_lock(&priv->spi_lock); + + priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY; + priv->spi_tx_buf[1] = reg; + priv->spi_tx_buf[2] = mask; + priv->spi_tx_buf[3] = val; + + mcp251x_spi_trans(spi, 4); + + mutex_unlock(&priv->spi_lock); +} + +static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf, + int len, int tx_buf_idx) +{ + struct mcp251x_platform_data *pdata = spi->dev.platform_data; + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + if (pdata->model == CAN_MCP251X_MCP2510) { + int i; + + for (i = 1; i < TXBDAT_OFF + len; i++) + mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + i, + buf[i]); + } else { + mutex_lock(&priv->spi_lock); + memcpy(priv->spi_tx_buf, buf, TXBDAT_OFF + len); + mcp251x_spi_trans(spi, TXBDAT_OFF + len); + mutex_unlock(&priv->spi_lock); + } +} + +static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame, + int tx_buf_idx) +{ + u32 sid, eid, exide, rtr; + u8 buf[SPI_TRANSFER_BUF_LEN]; + + exide = (frame->can_id & CAN_EFF_FLAG) ? 1 : 0; /* Extended ID Enable */ + if (exide) + sid = (frame->can_id & CAN_EFF_MASK) >> 18; + else + sid = frame->can_id & CAN_SFF_MASK; /* Standard ID */ + eid = frame->can_id & CAN_EFF_MASK; /* Extended ID */ + rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0; /* Remote transmission */ + + buf[TXBCTRL_OFF] = INSTRUCTION_LOAD_TXB(tx_buf_idx); + buf[TXBSIDH_OFF] = sid >> SIDH_SHIFT; + buf[TXBSIDL_OFF] = ((sid & SIDL_SID_MASK) << SIDL_SID_SHIFT) | + (exide << SIDL_EXIDE_SHIFT) | + ((eid >> SIDL_EID_SHIFT) & SIDL_EID_MASK); + buf[TXBEID8_OFF] = GET_BYTE(eid, 1); + buf[TXBEID0_OFF] = GET_BYTE(eid, 0); + buf[TXBDLC_OFF] = (rtr << DLC_RTR_SHIFT) | frame->can_dlc; + memcpy(buf + TXBDAT_OFF, frame->data, frame->can_dlc); + mcp251x_hw_tx_frame(spi, buf, frame->can_dlc, tx_buf_idx); + mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ); +} + +static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf, + int buf_idx) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct mcp251x_platform_data *pdata = spi->dev.platform_data; + + if (pdata->model == CAN_MCP251X_MCP2510) { + int i, len; + + for (i = 1; i < RXBDAT_OFF; i++) + buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); + len = buf[RXBDLC_OFF] & RXBDLC_LEN_MASK; + if (len > 8) + len = 8; + for (; i < (RXBDAT_OFF + len); i++) + buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); + } else { + mutex_lock(&priv->spi_lock); + + priv->spi_tx_buf[RXBCTRL_OFF] = INSTRUCTION_READ_RXB(buf_idx); + mcp251x_spi_trans(spi, SPI_TRANSFER_BUF_LEN); + memcpy(buf, priv->spi_rx_buf, SPI_TRANSFER_BUF_LEN); + + mutex_unlock(&priv->spi_lock); + } +} + +static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct sk_buff *skb; + struct can_frame *frame; + u8 buf[SPI_TRANSFER_BUF_LEN]; + + skb = alloc_can_skb(priv->net, &frame); + if (!skb) { + dev_err(&spi->dev, "cannot allocate RX skb\n"); + priv->net->stats.rx_dropped++; + return; + } + + mcp251x_hw_rx_frame(spi, buf, buf_idx); + if (buf[RXBSIDL_OFF] & RXBSIDL_IDE) { + /* Extended ID format */ + frame->can_id = CAN_EFF_FLAG; + frame->can_id |= + /* Extended ID part */ + SET_BYTE(buf[RXBSIDL_OFF] & RXBSIDL_EID, 2) | + SET_BYTE(buf[RXBEID8_OFF], 1) | + SET_BYTE(buf[RXBEID0_OFF], 0) | + /* Standard ID part */ + (((buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) | + (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT)) << 18); + /* Remote transmission request */ + if (buf[RXBDLC_OFF] & RXBDLC_RTR) + frame->can_id |= CAN_RTR_FLAG; + } else { + /* Standard ID format */ + frame->can_id = + (buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) | + (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT); + } + /* Data length */ + frame->can_dlc = buf[RXBDLC_OFF] & RXBDLC_LEN_MASK; + if (frame->can_dlc > 8) { + dev_warn(&spi->dev, "invalid frame recevied\n"); + priv->net->stats.rx_errors++; + dev_kfree_skb(skb); + return; + } + memcpy(frame->data, buf + RXBDAT_OFF, frame->can_dlc); + + priv->net->stats.rx_packets++; + priv->net->stats.rx_bytes += frame->can_dlc; + netif_rx(skb); +} + +static void mcp251x_hw_sleep(struct spi_device *spi) +{ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP); +} + +static void mcp251x_hw_wakeup(struct spi_device *spi) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + priv->wake = 1; + + /* Can only wake up by generating a wake-up interrupt. */ + mcp251x_write_bits(spi, CANINTE, CANINTE_WAKIE, CANINTE_WAKIE); + mcp251x_write_bits(spi, CANINTF, CANINTF_WAKIF, CANINTF_WAKIF); + + /* Wait until the device is awake */ + if (!wait_for_completion_timeout(&priv->awake, HZ)) + dev_err(&spi->dev, "MCP251x didn't wake-up\n"); +} + +static netdev_tx_t mcp251x_hard_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + if (priv->tx_skb || priv->tx_len) { + dev_warn(&spi->dev, "hard_xmit called while tx busy\n"); + netif_stop_queue(net); + return NETDEV_TX_BUSY; + } + + if (skb->len != sizeof(struct can_frame)) { + dev_err(&spi->dev, "dropping packet - bad length\n"); + dev_kfree_skb(skb); + net->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + netif_stop_queue(net); + priv->tx_skb = skb; + net->trans_start = jiffies; + queue_work(priv->wq, &priv->tx_work); + + return NETDEV_TX_OK; +} + +static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode) +{ + struct mcp251x_priv *priv = netdev_priv(net); + + switch (mode) { + case CAN_MODE_START: + /* We have to delay work since SPI I/O may sleep */ + priv->can.state = CAN_STATE_ERROR_ACTIVE; + priv->restart_tx = 1; + if (priv->can.restart_ms == 0) + priv->after_suspend = AFTER_SUSPEND_RESTART; + queue_work(priv->wq, &priv->irq_work); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static void mcp251x_set_normal_mode(struct spi_device *spi) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + unsigned long timeout; + + /* Enable interrupts */ + mcp251x_write_reg(spi, CANINTE, + CANINTE_ERRIE | CANINTE_TX2IE | CANINTE_TX1IE | + CANINTE_TX0IE | CANINTE_RX1IE | CANINTE_RX0IE | + CANINTF_MERRF); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + /* Put device into loopback mode */ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LOOPBACK); + } else { + /* Put device into normal mode */ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL); + + /* Wait for the device to enter normal mode */ + timeout = jiffies + HZ; + while (mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) { + schedule(); + if (time_after(jiffies, timeout)) { + dev_err(&spi->dev, "MCP251x didn't" + " enter in normal mode\n"); + return; + } + } + } + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static int mcp251x_do_set_bittiming(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct can_bittiming *bt = &priv->can.bittiming; + struct spi_device *spi = priv->spi; + + mcp251x_write_reg(spi, CNF1, ((bt->sjw - 1) << CNF1_SJW_SHIFT) | + (bt->brp - 1)); + mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE | + (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES ? + CNF2_SAM : 0) | + ((bt->phase_seg1 - 1) << CNF2_PS1_SHIFT) | + (bt->prop_seg - 1)); + mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK, + (bt->phase_seg2 - 1)); + dev_info(&spi->dev, "CNF: 0x%02x 0x%02x 0x%02x\n", + mcp251x_read_reg(spi, CNF1), + mcp251x_read_reg(spi, CNF2), + mcp251x_read_reg(spi, CNF3)); + + return 0; +} + +static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv, + struct spi_device *spi) +{ + mcp251x_do_set_bittiming(net); + + /* Enable RX0->RX1 buffer roll over and disable filters */ + mcp251x_write_bits(spi, RXBCTRL(0), + RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1, + RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1); + mcp251x_write_bits(spi, RXBCTRL(1), + RXBCTRL_RXM0 | RXBCTRL_RXM1, + RXBCTRL_RXM0 | RXBCTRL_RXM1); + return 0; +} + +static void mcp251x_hw_reset(struct spi_device *spi) +{ + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + int ret; + + mutex_lock(&priv->spi_lock); + + priv->spi_tx_buf[0] = INSTRUCTION_RESET; + + ret = spi_write(spi, priv->spi_tx_buf, 1); + + mutex_unlock(&priv->spi_lock); + + if (ret) + dev_err(&spi->dev, "reset failed: ret = %d\n", ret); + /* Wait for reset to finish */ + mdelay(10); +} + +static int mcp251x_hw_probe(struct spi_device *spi) +{ + int st1, st2; + + mcp251x_hw_reset(spi); + + /* + * Please note that these are "magic values" based on after + * reset defaults taken from data sheet which allows us to see + * if we really have a chip on the bus (we avoid common all + * zeroes or all ones situations) + */ + st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE; + st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17; + + dev_dbg(&spi->dev, "CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2); + + /* Check for power up default values */ + return (st1 == 0x80 && st2 == 0x07) ? 1 : 0; +} + +static irqreturn_t mcp251x_can_isr(int irq, void *dev_id) +{ + struct net_device *net = (struct net_device *)dev_id; + struct mcp251x_priv *priv = netdev_priv(net); + + /* Schedule bottom half */ + if (!work_pending(&priv->irq_work)) + queue_work(priv->wq, &priv->irq_work); + + return IRQ_HANDLED; +} + +static int mcp251x_open(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + struct mcp251x_platform_data *pdata = spi->dev.platform_data; + int ret; + + ret = open_candev(net); + if (ret) { + dev_err(&spi->dev, "unable to set initial baudrate!\n"); + return ret; + } + + if (pdata->transceiver_enable) + pdata->transceiver_enable(1); + + priv->force_quit = 0; + priv->tx_skb = NULL; + priv->tx_len = 0; + + ret = request_irq(spi->irq, mcp251x_can_isr, + IRQF_TRIGGER_FALLING, DEVICE_NAME, net); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); + if (pdata->transceiver_enable) + pdata->transceiver_enable(0); + close_candev(net); + return ret; + } + + mcp251x_hw_wakeup(spi); + mcp251x_hw_reset(spi); + ret = mcp251x_setup(net, priv, spi); + if (ret) { + free_irq(spi->irq, net); + mcp251x_hw_sleep(spi); + if (pdata->transceiver_enable) + pdata->transceiver_enable(0); + close_candev(net); + return ret; + } + mcp251x_set_normal_mode(spi); + netif_wake_queue(net); + + return 0; +} + +static int mcp251x_stop(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + struct mcp251x_platform_data *pdata = spi->dev.platform_data; + + close_candev(net); + + /* Disable and clear pending interrupts */ + mcp251x_write_reg(spi, CANINTE, 0x00); + mcp251x_write_reg(spi, CANINTF, 0x00); + + priv->force_quit = 1; + free_irq(spi->irq, net); + flush_workqueue(priv->wq); + + mcp251x_write_reg(spi, TXBCTRL(0), 0); + if (priv->tx_skb || priv->tx_len) + mcp251x_clean(net); + + mcp251x_hw_sleep(spi); + + if (pdata->transceiver_enable) + pdata->transceiver_enable(0); + + priv->can.state = CAN_STATE_STOPPED; + + return 0; +} + +static void mcp251x_tx_work_handler(struct work_struct *ws) +{ + struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv, + tx_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + struct can_frame *frame; + + if (priv->tx_skb) { + frame = (struct can_frame *)priv->tx_skb->data; + + if (priv->can.state == CAN_STATE_BUS_OFF) { + mcp251x_clean(net); + netif_wake_queue(net); + return; + } + if (frame->can_dlc > CAN_FRAME_MAX_DATA_LEN) + frame->can_dlc = CAN_FRAME_MAX_DATA_LEN; + mcp251x_hw_tx(spi, frame, 0); + priv->tx_len = 1 + frame->can_dlc; + can_put_echo_skb(priv->tx_skb, net, 0); + priv->tx_skb = NULL; + } +} + +static void mcp251x_irq_work_handler(struct work_struct *ws) +{ + struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv, + irq_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + u8 txbnctrl; + u8 intf; + enum can_state new_state; + + if (priv->after_suspend) { + mdelay(10); + mcp251x_hw_reset(spi); + mcp251x_setup(net, priv, spi); + if (priv->after_suspend & AFTER_SUSPEND_RESTART) { + mcp251x_set_normal_mode(spi); + } else if (priv->after_suspend & AFTER_SUSPEND_UP) { + netif_device_attach(net); + /* Clean since we lost tx buffer */ + if (priv->tx_skb || priv->tx_len) { + mcp251x_clean(net); + netif_wake_queue(net); + } + mcp251x_set_normal_mode(spi); + } else { + mcp251x_hw_sleep(spi); + } + priv->after_suspend = 0; + } + + if (priv->can.restart_ms == 0 && priv->can.state == CAN_STATE_BUS_OFF) + return; + + while (!priv->force_quit && !freezing(current)) { + u8 eflag = mcp251x_read_reg(spi, EFLG); + int can_id = 0, data1 = 0; + + mcp251x_write_reg(spi, EFLG, 0x00); + + if (priv->restart_tx) { + priv->restart_tx = 0; + mcp251x_write_reg(spi, TXBCTRL(0), 0); + if (priv->tx_skb || priv->tx_len) + mcp251x_clean(net); + netif_wake_queue(net); + can_id |= CAN_ERR_RESTARTED; + } + + if (priv->wake) { + /* Wait whilst the device wakes up */ + mdelay(10); + priv->wake = 0; + } + + intf = mcp251x_read_reg(spi, CANINTF); + mcp251x_write_bits(spi, CANINTF, intf, 0x00); + + /* Update can state */ + if (eflag & EFLG_TXBO) { + new_state = CAN_STATE_BUS_OFF; + can_id |= CAN_ERR_BUSOFF; + } else if (eflag & EFLG_TXEP) { + new_state = CAN_STATE_ERROR_PASSIVE; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_TX_PASSIVE; + } else if (eflag & EFLG_RXEP) { + new_state = CAN_STATE_ERROR_PASSIVE; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_RX_PASSIVE; + } else if (eflag & EFLG_TXWAR) { + new_state = CAN_STATE_ERROR_WARNING; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_TX_WARNING; + } else if (eflag & EFLG_RXWAR) { + new_state = CAN_STATE_ERROR_WARNING; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_RX_WARNING; + } else { + new_state = CAN_STATE_ERROR_ACTIVE; + } + + /* Update can state statistics */ + switch (priv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (new_state >= CAN_STATE_ERROR_WARNING && + new_state <= CAN_STATE_BUS_OFF) + priv->can.can_stats.error_warning++; + case CAN_STATE_ERROR_WARNING: /* fallthrough */ + if (new_state >= CAN_STATE_ERROR_PASSIVE && + new_state <= CAN_STATE_BUS_OFF) + priv->can.can_stats.error_passive++; + break; + default: + break; + } + priv->can.state = new_state; + + if ((intf & CANINTF_ERRIF) || (can_id & CAN_ERR_RESTARTED)) { + struct sk_buff *skb; + struct can_frame *frame; + + /* Create error frame */ + skb = alloc_can_err_skb(net, &frame); + if (skb) { + /* Set error frame flags based on bus state */ + frame->can_id = can_id; + frame->data[1] = data1; + + /* Update net stats for overflows */ + if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) { + if (eflag & EFLG_RX0OVR) + net->stats.rx_over_errors++; + if (eflag & EFLG_RX1OVR) + net->stats.rx_over_errors++; + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] |= + CAN_ERR_CRTL_RX_OVERFLOW; + } + + netif_rx(skb); + } else { + dev_info(&spi->dev, + "cannot allocate error skb\n"); + } + } + + if (priv->can.state == CAN_STATE_BUS_OFF) { + if (priv->can.restart_ms == 0) { + can_bus_off(net); + mcp251x_hw_sleep(spi); + return; + } + } + + if (intf == 0) + break; + + if (intf & CANINTF_WAKIF) + complete(&priv->awake); + + if (intf & CANINTF_MERRF) { + /* If there are pending Tx buffers, restart queue */ + txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0)); + if (!(txbnctrl & TXBCTRL_TXREQ)) { + if (priv->tx_skb || priv->tx_len) + mcp251x_clean(net); + netif_wake_queue(net); + } + } + + if (intf & (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)) { + net->stats.tx_packets++; + net->stats.tx_bytes += priv->tx_len - 1; + if (priv->tx_len) { + can_get_echo_skb(net, 0); + priv->tx_len = 0; + } + netif_wake_queue(net); + } + + if (intf & CANINTF_RX0IF) + mcp251x_hw_rx(spi, 0); + + if (intf & CANINTF_RX1IF) + mcp251x_hw_rx(spi, 1); + } +} + +static const struct net_device_ops mcp251x_netdev_ops = { + .ndo_open = mcp251x_open, + .ndo_stop = mcp251x_stop, + .ndo_start_xmit = mcp251x_hard_start_xmit, +}; + +static int __devinit mcp251x_can_probe(struct spi_device *spi) +{ + struct net_device *net; + struct mcp251x_priv *priv; + struct mcp251x_platform_data *pdata = spi->dev.platform_data; + int ret = -ENODEV; + + if (!pdata) + /* Platform data is required for osc freq */ + goto error_out; + + /* Allocate can/net device */ + net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX); + if (!net) { + ret = -ENOMEM; + goto error_alloc; + } + + net->netdev_ops = &mcp251x_netdev_ops; + net->flags |= IFF_ECHO; + + priv = netdev_priv(net); + priv->can.bittiming_const = &mcp251x_bittiming_const; + priv->can.do_set_mode = mcp251x_do_set_mode; + priv->can.clock.freq = pdata->oscillator_frequency / 2; + priv->net = net; + dev_set_drvdata(&spi->dev, priv); + + priv->spi = spi; + mutex_init(&priv->spi_lock); + + /* If requested, allocate DMA buffers */ + if (mcp251x_enable_dma) { + spi->dev.coherent_dma_mask = ~0; + + /* + * Minimum coherent DMA allocation is PAGE_SIZE, so allocate + * that much and share it between Tx and Rx DMA buffers. + */ + priv->spi_tx_buf = dma_alloc_coherent(&spi->dev, + PAGE_SIZE, + &priv->spi_tx_dma, + GFP_DMA); + + if (priv->spi_tx_buf) { + priv->spi_rx_buf = (u8 *)(priv->spi_tx_buf + + (PAGE_SIZE / 2)); + priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma + + (PAGE_SIZE / 2)); + } else { + /* Fall back to non-DMA */ + mcp251x_enable_dma = 0; + } + } + + /* Allocate non-DMA buffers */ + if (!mcp251x_enable_dma) { + priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL); + if (!priv->spi_tx_buf) { + ret = -ENOMEM; + goto error_tx_buf; + } + priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL); + if (!priv->spi_tx_buf) { + ret = -ENOMEM; + goto error_rx_buf; + } + } + + if (pdata->power_enable) + pdata->power_enable(1); + + /* Call out to platform specific setup */ + if (pdata->board_specific_setup) + pdata->board_specific_setup(spi); + + SET_NETDEV_DEV(net, &spi->dev); + + priv->wq = create_freezeable_workqueue("mcp251x_wq"); + + INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler); + INIT_WORK(&priv->irq_work, mcp251x_irq_work_handler); + + init_completion(&priv->awake); + + /* Configure the SPI bus */ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + spi_setup(spi); + + if (!mcp251x_hw_probe(spi)) { + dev_info(&spi->dev, "Probe failed\n"); + goto error_probe; + } + mcp251x_hw_sleep(spi); + + if (pdata->transceiver_enable) + pdata->transceiver_enable(0); + + ret = register_candev(net); + if (!ret) { + dev_info(&spi->dev, "probed\n"); + return ret; + } +error_probe: + if (!mcp251x_enable_dma) + kfree(priv->spi_rx_buf); +error_rx_buf: + if (!mcp251x_enable_dma) + kfree(priv->spi_tx_buf); +error_tx_buf: + free_candev(net); + if (mcp251x_enable_dma) + dma_free_coherent(&spi->dev, PAGE_SIZE, + priv->spi_tx_buf, priv->spi_tx_dma); +error_alloc: + if (pdata->power_enable) + pdata->power_enable(0); + dev_err(&spi->dev, "probe failed\n"); +error_out: + return ret; +} + +static int __devexit mcp251x_can_remove(struct spi_device *spi) +{ + struct mcp251x_platform_data *pdata = spi->dev.platform_data; + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct net_device *net = priv->net; + + unregister_candev(net); + free_candev(net); + + priv->force_quit = 1; + flush_workqueue(priv->wq); + destroy_workqueue(priv->wq); + + if (mcp251x_enable_dma) { + dma_free_coherent(&spi->dev, PAGE_SIZE, + priv->spi_tx_buf, priv->spi_tx_dma); + } else { + kfree(priv->spi_tx_buf); + kfree(priv->spi_rx_buf); + } + + if (pdata->power_enable) + pdata->power_enable(0); + + return 0; +} + +#ifdef CONFIG_PM +static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state) +{ + struct mcp251x_platform_data *pdata = spi->dev.platform_data; + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + struct net_device *net = priv->net; + + if (netif_running(net)) { + netif_device_detach(net); + + mcp251x_hw_sleep(spi); + if (pdata->transceiver_enable) + pdata->transceiver_enable(0); + priv->after_suspend = AFTER_SUSPEND_UP; + } else { + priv->after_suspend = AFTER_SUSPEND_DOWN; + } + + if (pdata->power_enable) { + pdata->power_enable(0); + priv->after_suspend |= AFTER_SUSPEND_POWER; + } + + return 0; +} + +static int mcp251x_can_resume(struct spi_device *spi) +{ + struct mcp251x_platform_data *pdata = spi->dev.platform_data; + struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev); + + if (priv->after_suspend & AFTER_SUSPEND_POWER) { + pdata->power_enable(1); + queue_work(priv->wq, &priv->irq_work); + } else { + if (priv->after_suspend & AFTER_SUSPEND_UP) { + if (pdata->transceiver_enable) + pdata->transceiver_enable(1); + queue_work(priv->wq, &priv->irq_work); + } else { + priv->after_suspend = 0; + } + } + return 0; +} +#else +#define mcp251x_can_suspend NULL +#define mcp251x_can_resume NULL +#endif + +static struct spi_driver mcp251x_can_driver = { + .driver = { + .name = DEVICE_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = mcp251x_can_probe, + .remove = __devexit_p(mcp251x_can_remove), + .suspend = mcp251x_can_suspend, + .resume = mcp251x_can_resume, +}; + +static int __init mcp251x_can_init(void) +{ + return spi_register_driver(&mcp251x_can_driver); +} + +static void __exit mcp251x_can_exit(void) +{ + spi_unregister_driver(&mcp251x_can_driver); +} + +module_init(mcp251x_can_init); +module_exit(mcp251x_can_exit); + +MODULE_AUTHOR("Chris Elston <celston@katalix.com>, " + "Christian Pellegrin <chripell@evolware.org>"); +MODULE_DESCRIPTION("Microchip 251x CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/mscan/Kconfig b/drivers/net/can/mscan/Kconfig new file mode 100644 index 000000000000..cd0f2d6f375d --- /dev/null +++ b/drivers/net/can/mscan/Kconfig @@ -0,0 +1,23 @@ +config CAN_MSCAN + depends on CAN_DEV && (PPC || M68K || M68KNOMMU) + tristate "Support for Freescale MSCAN based chips" + ---help--- + The Motorola Scalable Controller Area Network (MSCAN) definition + is based on the MSCAN12 definition which is the specific + implementation of the Motorola Scalable CAN concept targeted for + the Motorola MC68HC12 Microcontroller Family. + +if CAN_MSCAN + +config CAN_MPC5XXX + tristate "Freescale MPC5xxx onboard CAN controller" + depends on PPC_MPC52xx + ---help--- + If you say yes here you get support for Freescale's MPC5xxx + onboard CAN controller. + + This driver can also be built as a module. If so, the module + will be called mscan-mpc5xxx.ko. + +endif + diff --git a/drivers/net/can/mscan/Makefile b/drivers/net/can/mscan/Makefile new file mode 100644 index 000000000000..c9fab17cd8b4 --- /dev/null +++ b/drivers/net/can/mscan/Makefile @@ -0,0 +1,5 @@ + +obj-$(CONFIG_CAN_MPC5XXX) += mscan-mpc5xxx.o +mscan-mpc5xxx-objs := mscan.o mpc5xxx_can.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c new file mode 100644 index 000000000000..1de6f6349b16 --- /dev/null +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -0,0 +1,259 @@ +/* + * CAN bus driver for the Freescale MPC5xxx embedded CPU. + * + * Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2009 Wolfram Sang, Pengutronix <w.sang@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/of_platform.h> +#include <sysdev/fsl_soc.h> +#include <linux/io.h> +#include <asm/mpc52xx.h> + +#include "mscan.h" + +#define DRV_NAME "mpc5xxx_can" + +static struct of_device_id mpc52xx_cdm_ids[] __devinitdata = { + { .compatible = "fsl,mpc5200-cdm", }, + {} +}; + +/* + * Get frequency of the MSCAN clock source + * + * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock (IP_CLK) + * can be selected. According to the MPC5200 user's manual, the oscillator + * clock is the better choice as it has less jitter but due to a hardware + * bug, it can not be selected for the old MPC5200 Rev. A chips. + */ + +static unsigned int __devinit mpc52xx_can_clock_freq(struct of_device *of, + int clock_src) +{ + unsigned int pvr; + struct mpc52xx_cdm __iomem *cdm; + struct device_node *np_cdm; + unsigned int freq; + u32 val; + + pvr = mfspr(SPRN_PVR); + + freq = mpc5xxx_get_bus_frequency(of->node); + if (!freq) + return 0; + + if (clock_src == MSCAN_CLKSRC_BUS || pvr == 0x80822011) + return freq; + + /* Determine SYS_XTAL_IN frequency from the clock domain settings */ + np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids); + if (!np_cdm) { + dev_err(&of->dev, "can't get clock node!\n"); + return 0; + } + cdm = of_iomap(np_cdm, 0); + of_node_put(np_cdm); + + if (in_8(&cdm->ipb_clk_sel) & 0x1) + freq *= 2; + val = in_be32(&cdm->rstcfg); + + freq *= (val & (1 << 5)) ? 8 : 4; + freq /= (val & (1 << 6)) ? 12 : 16; + + iounmap(cdm); + + return freq; +} + +static int __devinit mpc5xxx_can_probe(struct of_device *ofdev, + const struct of_device_id *id) +{ + struct device_node *np = ofdev->node; + struct net_device *dev; + struct mscan_priv *priv; + void __iomem *base; + const char *clk_src; + int err, irq, clock_src; + + base = of_iomap(ofdev->node, 0); + if (!base) { + dev_err(&ofdev->dev, "couldn't ioremap\n"); + err = -ENOMEM; + goto exit_release_mem; + } + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + dev_err(&ofdev->dev, "no irq found\n"); + err = -ENODEV; + goto exit_unmap_mem; + } + + dev = alloc_mscandev(); + if (!dev) { + err = -ENOMEM; + goto exit_dispose_irq; + } + + priv = netdev_priv(dev); + priv->reg_base = base; + dev->irq = irq; + + /* + * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock + * (IP_CLK) can be selected as MSCAN clock source. According to + * the MPC5200 user's manual, the oscillator clock is the better + * choice as it has less jitter. For this reason, it is selected + * by default. + */ + clk_src = of_get_property(np, "fsl,mscan-clock-source", NULL); + if (clk_src && strcmp(clk_src, "ip") == 0) + clock_src = MSCAN_CLKSRC_BUS; + else + clock_src = MSCAN_CLKSRC_XTAL; + priv->can.clock.freq = mpc52xx_can_clock_freq(ofdev, clock_src); + if (!priv->can.clock.freq) { + dev_err(&ofdev->dev, "couldn't get MSCAN clock frequency\n"); + err = -ENODEV; + goto exit_free_mscan; + } + + SET_NETDEV_DEV(dev, &ofdev->dev); + + err = register_mscandev(dev, clock_src); + if (err) { + dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free_mscan; + } + + dev_set_drvdata(&ofdev->dev, dev); + + dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n", + priv->reg_base, dev->irq, priv->can.clock.freq); + + return 0; + +exit_free_mscan: + free_candev(dev); +exit_dispose_irq: + irq_dispose_mapping(irq); +exit_unmap_mem: + iounmap(base); +exit_release_mem: + return err; +} + +static int __devexit mpc5xxx_can_remove(struct of_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct mscan_priv *priv = netdev_priv(dev); + + dev_set_drvdata(&ofdev->dev, NULL); + + unregister_mscandev(dev); + iounmap(priv->reg_base); + irq_dispose_mapping(dev->irq); + free_candev(dev); + + return 0; +} + +#ifdef CONFIG_PM +static struct mscan_regs saved_regs; +static int mpc5xxx_can_suspend(struct of_device *ofdev, pm_message_t state) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + + _memcpy_fromio(&saved_regs, regs, sizeof(*regs)); + + return 0; +} + +static int mpc5xxx_can_resume(struct of_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + + regs->canctl0 |= MSCAN_INITRQ; + while (!(regs->canctl1 & MSCAN_INITAK)) + udelay(10); + + regs->canctl1 = saved_regs.canctl1; + regs->canbtr0 = saved_regs.canbtr0; + regs->canbtr1 = saved_regs.canbtr1; + regs->canidac = saved_regs.canidac; + + /* restore masks, buffers etc. */ + _memcpy_toio(®s->canidar1_0, (void *)&saved_regs.canidar1_0, + sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0)); + + regs->canctl0 &= ~MSCAN_INITRQ; + regs->cantbsel = saved_regs.cantbsel; + regs->canrier = saved_regs.canrier; + regs->cantier = saved_regs.cantier; + regs->canctl0 = saved_regs.canctl0; + + return 0; +} +#endif + +static struct of_device_id __devinitdata mpc5xxx_can_table[] = { + {.compatible = "fsl,mpc5200-mscan"}, + {}, +}; + +static struct of_platform_driver mpc5xxx_can_driver = { + .owner = THIS_MODULE, + .name = "mpc5xxx_can", + .probe = mpc5xxx_can_probe, + .remove = __devexit_p(mpc5xxx_can_remove), +#ifdef CONFIG_PM + .suspend = mpc5xxx_can_suspend, + .resume = mpc5xxx_can_resume, +#endif + .match_table = mpc5xxx_can_table, +}; + +static int __init mpc5xxx_can_init(void) +{ + return of_register_platform_driver(&mpc5xxx_can_driver); +} +module_init(mpc5xxx_can_init); + +static void __exit mpc5xxx_can_exit(void) +{ + return of_unregister_platform_driver(&mpc5xxx_can_driver); +}; +module_exit(mpc5xxx_can_exit); + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Freescale MPC5200 CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c new file mode 100644 index 000000000000..bb06dfb58f25 --- /dev/null +++ b/drivers/net/can/mscan/mscan.c @@ -0,0 +1,668 @@ +/* + * CAN bus driver for the alone generic (as possible as) MSCAN controller. + * + * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + * Copytight (C) 2008-2009 Pengutronix <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/list.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/io.h> + +#include "mscan.h" + +static struct can_bittiming_const mscan_bittiming_const = { + .name = "mscan", + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +struct mscan_state { + u8 mode; + u8 canrier; + u8 cantier; +}; + +static enum can_state state_map[] = { + CAN_STATE_ERROR_ACTIVE, + CAN_STATE_ERROR_WARNING, + CAN_STATE_ERROR_PASSIVE, + CAN_STATE_BUS_OFF +}; + +static int mscan_set_mode(struct net_device *dev, u8 mode) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + int ret = 0; + int i; + u8 canctl1; + + if (mode != MSCAN_NORMAL_MODE) { + if (priv->tx_active) { + /* Abort transfers before going to sleep */# + out_8(®s->cantarq, priv->tx_active); + /* Suppress TX done interrupts */ + out_8(®s->cantier, 0); + } + + canctl1 = in_8(®s->canctl1); + if ((mode & MSCAN_SLPRQ) && !(canctl1 & MSCAN_SLPAK)) { + setbits8(®s->canctl0, MSCAN_SLPRQ); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + if (in_8(®s->canctl1) & MSCAN_SLPAK) + break; + udelay(100); + } + /* + * The mscan controller will fail to enter sleep mode, + * while there are irregular activities on bus, like + * somebody keeps retransmitting. This behavior is + * undocumented and seems to differ between mscan built + * in mpc5200b and mpc5200. We proceed in that case, + * since otherwise the slprq will be kept set and the + * controller will get stuck. NOTE: INITRQ or CSWAI + * will abort all active transmit actions, if still + * any, at once. + */ + if (i >= MSCAN_SET_MODE_RETRIES) + dev_dbg(dev->dev.parent, + "device failed to enter sleep mode. " + "We proceed anyhow.\n"); + else + priv->can.state = CAN_STATE_SLEEPING; + } + + if ((mode & MSCAN_INITRQ) && !(canctl1 & MSCAN_INITAK)) { + setbits8(®s->canctl0, MSCAN_INITRQ); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + if (in_8(®s->canctl1) & MSCAN_INITAK) + break; + } + if (i >= MSCAN_SET_MODE_RETRIES) + ret = -ENODEV; + } + if (!ret) + priv->can.state = CAN_STATE_STOPPED; + + if (mode & MSCAN_CSWAI) + setbits8(®s->canctl0, MSCAN_CSWAI); + + } else { + canctl1 = in_8(®s->canctl1); + if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) { + clrbits8(®s->canctl0, MSCAN_SLPRQ | MSCAN_INITRQ); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + canctl1 = in_8(®s->canctl1); + if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK))) + break; + } + if (i >= MSCAN_SET_MODE_RETRIES) + ret = -ENODEV; + else + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + } + return ret; +} + +static int mscan_start(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + u8 canrflg; + int err; + + out_8(®s->canrier, 0); + + INIT_LIST_HEAD(&priv->tx_head); + priv->prev_buf_id = 0; + priv->cur_pri = 0; + priv->tx_active = 0; + priv->shadow_canrier = 0; + priv->flags = 0; + + err = mscan_set_mode(dev, MSCAN_NORMAL_MODE); + if (err) + return err; + + canrflg = in_8(®s->canrflg); + priv->shadow_statflg = canrflg & MSCAN_STAT_MSK; + priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg), + MSCAN_STATE_TX(canrflg))]; + out_8(®s->cantier, 0); + + /* Enable receive interrupts. */ + out_8(®s->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE | + MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0); + + return 0; +} + +static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct can_frame *frame = (struct can_frame *)skb->data; + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + int i, rtr, buf_id; + u32 can_id; + + if (frame->can_dlc > 8) + return -EINVAL; + + out_8(®s->cantier, 0); + + i = ~priv->tx_active & MSCAN_TXE; + buf_id = ffs(i) - 1; + switch (hweight8(i)) { + case 0: + netif_stop_queue(dev); + dev_err(dev->dev.parent, "Tx Ring full when queue awake!\n"); + return NETDEV_TX_BUSY; + case 1: + /* + * if buf_id < 3, then current frame will be send out of order, + * since buffer with lower id have higher priority (hell..) + */ + netif_stop_queue(dev); + case 2: + if (buf_id < priv->prev_buf_id) { + priv->cur_pri++; + if (priv->cur_pri == 0xff) { + set_bit(F_TX_WAIT_ALL, &priv->flags); + netif_stop_queue(dev); + } + } + set_bit(F_TX_PROGRESS, &priv->flags); + break; + } + priv->prev_buf_id = buf_id; + out_8(®s->cantbsel, i); + + rtr = frame->can_id & CAN_RTR_FLAG; + + /* RTR is always the lowest bit of interest, then IDs follow */ + if (frame->can_id & CAN_EFF_FLAG) { + can_id = (frame->can_id & CAN_EFF_MASK) + << (MSCAN_EFF_RTR_SHIFT + 1); + if (rtr) + can_id |= 1 << MSCAN_EFF_RTR_SHIFT; + out_be16(®s->tx.idr3_2, can_id); + + can_id >>= 16; + /* EFF_FLAGS are inbetween the IDs :( */ + can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) + | MSCAN_EFF_FLAGS; + } else { + can_id = (frame->can_id & CAN_SFF_MASK) + << (MSCAN_SFF_RTR_SHIFT + 1); + if (rtr) + can_id |= 1 << MSCAN_SFF_RTR_SHIFT; + } + out_be16(®s->tx.idr1_0, can_id); + + if (!rtr) { + void __iomem *data = ®s->tx.dsr1_0; + u16 *payload = (u16 *)frame->data; + + /* It is safe to write into dsr[dlc+1] */ + for (i = 0; i < (frame->can_dlc + 1) / 2; i++) { + out_be16(data, *payload++); + data += 2 + _MSCAN_RESERVED_DSR_SIZE; + } + } + + out_8(®s->tx.dlr, frame->can_dlc); + out_8(®s->tx.tbpr, priv->cur_pri); + + /* Start transmission. */ + out_8(®s->cantflg, 1 << buf_id); + + if (!test_bit(F_TX_PROGRESS, &priv->flags)) + dev->trans_start = jiffies; + + list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head); + + can_put_echo_skb(skb, dev, buf_id); + + /* Enable interrupt. */ + priv->tx_active |= 1 << buf_id; + out_8(®s->cantier, priv->tx_active); + + return NETDEV_TX_OK; +} + +/* This function returns the old state to see where we came from */ +static enum can_state check_set_state(struct net_device *dev, u8 canrflg) +{ + struct mscan_priv *priv = netdev_priv(dev); + enum can_state state, old_state = priv->can.state; + + if (canrflg & MSCAN_CSCIF && old_state <= CAN_STATE_BUS_OFF) { + state = state_map[max(MSCAN_STATE_RX(canrflg), + MSCAN_STATE_TX(canrflg))]; + priv->can.state = state; + } + return old_state; +} + +static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + u32 can_id; + int i; + + can_id = in_be16(®s->rx.idr1_0); + if (can_id & (1 << 3)) { + frame->can_id = CAN_EFF_FLAG; + can_id = ((can_id << 16) | in_be16(®s->rx.idr3_2)); + can_id = ((can_id & 0xffe00000) | + ((can_id & 0x7ffff) << 2)) >> 2; + } else { + can_id >>= 4; + frame->can_id = 0; + } + + frame->can_id |= can_id >> 1; + if (can_id & 1) + frame->can_id |= CAN_RTR_FLAG; + frame->can_dlc = in_8(®s->rx.dlr) & 0xf; + + if (!(frame->can_id & CAN_RTR_FLAG)) { + void __iomem *data = ®s->rx.dsr1_0; + u16 *payload = (u16 *)frame->data; + + for (i = 0; i < (frame->can_dlc + 1) / 2; i++) { + *payload++ = in_be16(data); + data += 2 + _MSCAN_RESERVED_DSR_SIZE; + } + } + + out_8(®s->canrflg, MSCAN_RXF); +} + +static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame, + u8 canrflg) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct net_device_stats *stats = &dev->stats; + enum can_state old_state; + + dev_dbg(dev->dev.parent, "error interrupt (canrflg=%#x)\n", canrflg); + frame->can_id = CAN_ERR_FLAG; + + if (canrflg & MSCAN_OVRIF) { + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + stats->rx_errors++; + } else { + frame->data[1] = 0; + } + + old_state = check_set_state(dev, canrflg); + /* State changed */ + if (old_state != priv->can.state) { + switch (priv->can.state) { + case CAN_STATE_ERROR_WARNING: + frame->can_id |= CAN_ERR_CRTL; + priv->can.can_stats.error_warning++; + if ((priv->shadow_statflg & MSCAN_RSTAT_MSK) < + (canrflg & MSCAN_RSTAT_MSK)) + frame->data[1] |= CAN_ERR_CRTL_RX_WARNING; + if ((priv->shadow_statflg & MSCAN_TSTAT_MSK) < + (canrflg & MSCAN_TSTAT_MSK)) + frame->data[1] |= CAN_ERR_CRTL_TX_WARNING; + break; + case CAN_STATE_ERROR_PASSIVE: + frame->can_id |= CAN_ERR_CRTL; + priv->can.can_stats.error_passive++; + frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + frame->can_id |= CAN_ERR_BUSOFF; + /* + * The MSCAN on the MPC5200 does recover from bus-off + * automatically. To avoid that we stop the chip doing + * a light-weight stop (we are in irq-context). + */ + out_8(®s->cantier, 0); + out_8(®s->canrier, 0); + setbits8(®s->canctl0, MSCAN_SLPRQ | MSCAN_INITRQ); + can_bus_off(dev); + break; + default: + break; + } + } + priv->shadow_statflg = canrflg & MSCAN_STAT_MSK; + frame->can_dlc = CAN_ERR_DLC; + out_8(®s->canrflg, MSCAN_ERR_IF); +} + +static int mscan_rx_poll(struct napi_struct *napi, int quota) +{ + struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi); + struct net_device *dev = napi->dev; + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct net_device_stats *stats = &dev->stats; + int npackets = 0; + int ret = 1; + struct sk_buff *skb; + struct can_frame *frame; + u8 canrflg; + + while (npackets < quota) { + canrflg = in_8(®s->canrflg); + if (!(canrflg & (MSCAN_RXF | MSCAN_ERR_IF))) + break; + + skb = alloc_can_skb(dev, &frame); + if (!skb) { + if (printk_ratelimit()) + dev_notice(dev->dev.parent, "packet dropped\n"); + stats->rx_dropped++; + out_8(®s->canrflg, canrflg); + continue; + } + + if (canrflg & MSCAN_RXF) + mscan_get_rx_frame(dev, frame); + else if (canrflg & MSCAN_ERR_IF) + mscan_get_err_frame(dev, frame, canrflg); + + stats->rx_packets++; + stats->rx_bytes += frame->can_dlc; + npackets++; + netif_receive_skb(skb); + } + + if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) { + napi_complete(&priv->napi); + clear_bit(F_RX_PROGRESS, &priv->flags); + if (priv->can.state < CAN_STATE_BUS_OFF) + out_8(®s->canrier, priv->shadow_canrier); + ret = 0; + } + return ret; +} + +static irqreturn_t mscan_isr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct net_device_stats *stats = &dev->stats; + u8 cantier, cantflg, canrflg; + irqreturn_t ret = IRQ_NONE; + + cantier = in_8(®s->cantier) & MSCAN_TXE; + cantflg = in_8(®s->cantflg) & cantier; + + if (cantier && cantflg) { + struct list_head *tmp, *pos; + + list_for_each_safe(pos, tmp, &priv->tx_head) { + struct tx_queue_entry *entry = + list_entry(pos, struct tx_queue_entry, list); + u8 mask = entry->mask; + + if (!(cantflg & mask)) + continue; + + out_8(®s->cantbsel, mask); + stats->tx_bytes += in_8(®s->tx.dlr); + stats->tx_packets++; + can_get_echo_skb(dev, entry->id); + priv->tx_active &= ~mask; + list_del(pos); + } + + if (list_empty(&priv->tx_head)) { + clear_bit(F_TX_WAIT_ALL, &priv->flags); + clear_bit(F_TX_PROGRESS, &priv->flags); + priv->cur_pri = 0; + } else { + dev->trans_start = jiffies; + } + + if (!test_bit(F_TX_WAIT_ALL, &priv->flags)) + netif_wake_queue(dev); + + out_8(®s->cantier, priv->tx_active); + ret = IRQ_HANDLED; + } + + canrflg = in_8(®s->canrflg); + if ((canrflg & ~MSCAN_STAT_MSK) && + !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) { + if (canrflg & ~MSCAN_STAT_MSK) { + priv->shadow_canrier = in_8(®s->canrier); + out_8(®s->canrier, 0); + napi_schedule(&priv->napi); + ret = IRQ_HANDLED; + } else { + clear_bit(F_RX_PROGRESS, &priv->flags); + } + } + return ret; +} + +static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct mscan_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!priv->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + if (priv->can.state <= CAN_STATE_BUS_OFF) + mscan_set_mode(dev, MSCAN_INIT_MODE); + ret = mscan_start(dev); + if (ret) + break; + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + +static int mscan_do_set_bittiming(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + + btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw); + btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) | + BTR1_SET_TSEG2(bt->phase_seg2) | + BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)); + + dev_info(dev->dev.parent, "setting BTR0=0x%02x BTR1=0x%02x\n", + btr0, btr1); + + out_8(®s->canbtr0, btr0); + out_8(®s->canbtr1, btr1); + + return 0; +} + +static int mscan_open(struct net_device *dev) +{ + int ret; + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + + /* common open */ + ret = open_candev(dev); + if (ret) + return ret; + + napi_enable(&priv->napi); + + ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev); + if (ret < 0) { + dev_err(dev->dev.parent, "failed to attach interrupt\n"); + goto exit_napi_disable; + } + + priv->open_time = jiffies; + + clrbits8(®s->canctl1, MSCAN_LISTEN); + + ret = mscan_start(dev); + if (ret) + goto exit_free_irq; + + netif_start_queue(dev); + + return 0; + +exit_free_irq: + priv->open_time = 0; + free_irq(dev->irq, dev); +exit_napi_disable: + napi_disable(&priv->napi); + close_candev(dev); + return ret; +} + +static int mscan_close(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + + netif_stop_queue(dev); + napi_disable(&priv->napi); + + out_8(®s->cantier, 0); + out_8(®s->canrier, 0); + mscan_set_mode(dev, MSCAN_INIT_MODE); + close_candev(dev); + free_irq(dev->irq, dev); + priv->open_time = 0; + + return 0; +} + +static const struct net_device_ops mscan_netdev_ops = { + .ndo_open = mscan_open, + .ndo_stop = mscan_close, + .ndo_start_xmit = mscan_start_xmit, +}; + +int register_mscandev(struct net_device *dev, int clock_src) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + u8 ctl1; + + ctl1 = in_8(®s->canctl1); + if (clock_src) + ctl1 |= MSCAN_CLKSRC; + else + ctl1 &= ~MSCAN_CLKSRC; + + ctl1 |= MSCAN_CANE; + out_8(®s->canctl1, ctl1); + udelay(100); + + /* acceptance mask/acceptance code (accept everything) */ + out_be16(®s->canidar1_0, 0); + out_be16(®s->canidar3_2, 0); + out_be16(®s->canidar5_4, 0); + out_be16(®s->canidar7_6, 0); + + out_be16(®s->canidmr1_0, 0xffff); + out_be16(®s->canidmr3_2, 0xffff); + out_be16(®s->canidmr5_4, 0xffff); + out_be16(®s->canidmr7_6, 0xffff); + /* Two 32 bit Acceptance Filters */ + out_8(®s->canidac, MSCAN_AF_32BIT); + + mscan_set_mode(dev, MSCAN_INIT_MODE); + + return register_candev(dev); +} + +void unregister_mscandev(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; + mscan_set_mode(dev, MSCAN_INIT_MODE); + clrbits8(®s->canctl1, MSCAN_CANE); + unregister_candev(dev); +} + +struct net_device *alloc_mscandev(void) +{ + struct net_device *dev; + struct mscan_priv *priv; + int i; + + dev = alloc_candev(sizeof(struct mscan_priv), MSCAN_ECHO_SKB_MAX); + if (!dev) + return NULL; + priv = netdev_priv(dev); + + dev->netdev_ops = &mscan_netdev_ops; + + dev->flags |= IFF_ECHO; /* we support local echo */ + + netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8); + + priv->can.bittiming_const = &mscan_bittiming_const; + priv->can.do_set_bittiming = mscan_do_set_bittiming; + priv->can.do_set_mode = mscan_do_set_mode; + + for (i = 0; i < TX_QUEUE_SIZE; i++) { + priv->tx_queue[i].id = i; + priv->tx_queue[i].mask = 1 << i; + } + + return dev; +} + +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN port driver for a MSCAN based chips"); diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h new file mode 100644 index 000000000000..00fc4aaf1ed8 --- /dev/null +++ b/drivers/net/can/mscan/mscan.h @@ -0,0 +1,296 @@ +/* + * Definitions of consts/structs to drive the Freescale MSCAN. + * + * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MSCAN_H__ +#define __MSCAN_H__ + +#include <linux/types.h> + +/* MSCAN control register 0 (CANCTL0) bits */ +#define MSCAN_RXFRM 0x80 +#define MSCAN_RXACT 0x40 +#define MSCAN_CSWAI 0x20 +#define MSCAN_SYNCH 0x10 +#define MSCAN_TIME 0x08 +#define MSCAN_WUPE 0x04 +#define MSCAN_SLPRQ 0x02 +#define MSCAN_INITRQ 0x01 + +/* MSCAN control register 1 (CANCTL1) bits */ +#define MSCAN_CANE 0x80 +#define MSCAN_CLKSRC 0x40 +#define MSCAN_LOOPB 0x20 +#define MSCAN_LISTEN 0x10 +#define MSCAN_WUPM 0x04 +#define MSCAN_SLPAK 0x02 +#define MSCAN_INITAK 0x01 + +/* Use the MPC5200 MSCAN variant? */ +#ifdef CONFIG_PPC +#define MSCAN_FOR_MPC5200 +#endif + +#ifdef MSCAN_FOR_MPC5200 +#define MSCAN_CLKSRC_BUS 0 +#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC +#else +#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC +#define MSCAN_CLKSRC_XTAL 0 +#endif + +/* MSCAN receiver flag register (CANRFLG) bits */ +#define MSCAN_WUPIF 0x80 +#define MSCAN_CSCIF 0x40 +#define MSCAN_RSTAT1 0x20 +#define MSCAN_RSTAT0 0x10 +#define MSCAN_TSTAT1 0x08 +#define MSCAN_TSTAT0 0x04 +#define MSCAN_OVRIF 0x02 +#define MSCAN_RXF 0x01 +#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF) +#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0) +#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0) +#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK) + +#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \ + MSCAN_TSTAT1 | MSCAN_TSTAT0) +#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2) +#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4) +#define MSCAN_STATE_ACTIVE 0 +#define MSCAN_STATE_WARNING 1 +#define MSCAN_STATE_PASSIVE 2 +#define MSCAN_STATE_BUSOFF 3 + +/* MSCAN receiver interrupt enable register (CANRIER) bits */ +#define MSCAN_WUPIE 0x80 +#define MSCAN_CSCIE 0x40 +#define MSCAN_RSTATE1 0x20 +#define MSCAN_RSTATE0 0x10 +#define MSCAN_TSTATE1 0x08 +#define MSCAN_TSTATE0 0x04 +#define MSCAN_OVRIE 0x02 +#define MSCAN_RXFIE 0x01 + +/* MSCAN transmitter flag register (CANTFLG) bits */ +#define MSCAN_TXE2 0x04 +#define MSCAN_TXE1 0x02 +#define MSCAN_TXE0 0x01 +#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0) + +/* MSCAN transmitter interrupt enable register (CANTIER) bits */ +#define MSCAN_TXIE2 0x04 +#define MSCAN_TXIE1 0x02 +#define MSCAN_TXIE0 0x01 +#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0) + +/* MSCAN transmitter message abort request (CANTARQ) bits */ +#define MSCAN_ABTRQ2 0x04 +#define MSCAN_ABTRQ1 0x02 +#define MSCAN_ABTRQ0 0x01 + +/* MSCAN transmitter message abort ack (CANTAAK) bits */ +#define MSCAN_ABTAK2 0x04 +#define MSCAN_ABTAK1 0x02 +#define MSCAN_ABTAK0 0x01 + +/* MSCAN transmit buffer selection (CANTBSEL) bits */ +#define MSCAN_TX2 0x04 +#define MSCAN_TX1 0x02 +#define MSCAN_TX0 0x01 + +/* MSCAN ID acceptance control register (CANIDAC) bits */ +#define MSCAN_IDAM1 0x20 +#define MSCAN_IDAM0 0x10 +#define MSCAN_IDHIT2 0x04 +#define MSCAN_IDHIT1 0x02 +#define MSCAN_IDHIT0 0x01 + +#define MSCAN_AF_32BIT 0x00 +#define MSCAN_AF_16BIT MSCAN_IDAM0 +#define MSCAN_AF_8BIT MSCAN_IDAM1 +#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1) +#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1)) + +/* MSCAN Miscellaneous Register (CANMISC) bits */ +#define MSCAN_BOHOLD 0x01 + +/* MSCAN Identifier Register (IDR) bits */ +#define MSCAN_SFF_RTR_SHIFT 4 +#define MSCAN_EFF_RTR_SHIFT 0 +#define MSCAN_EFF_FLAGS 0x18 /* IDE + SRR */ + +#ifdef MSCAN_FOR_MPC5200 +#define _MSCAN_RESERVED_(n, num) u8 _res##n[num] +#define _MSCAN_RESERVED_DSR_SIZE 2 +#else +#define _MSCAN_RESERVED_(n, num) +#define _MSCAN_RESERVED_DSR_SIZE 0 +#endif + +/* Structure of the hardware registers */ +struct mscan_regs { + /* (see doc S12MSCANV3/D) MPC5200 MSCAN */ + u8 canctl0; /* + 0x00 0x00 */ + u8 canctl1; /* + 0x01 0x01 */ + _MSCAN_RESERVED_(1, 2); /* + 0x02 */ + u8 canbtr0; /* + 0x04 0x02 */ + u8 canbtr1; /* + 0x05 0x03 */ + _MSCAN_RESERVED_(2, 2); /* + 0x06 */ + u8 canrflg; /* + 0x08 0x04 */ + u8 canrier; /* + 0x09 0x05 */ + _MSCAN_RESERVED_(3, 2); /* + 0x0a */ + u8 cantflg; /* + 0x0c 0x06 */ + u8 cantier; /* + 0x0d 0x07 */ + _MSCAN_RESERVED_(4, 2); /* + 0x0e */ + u8 cantarq; /* + 0x10 0x08 */ + u8 cantaak; /* + 0x11 0x09 */ + _MSCAN_RESERVED_(5, 2); /* + 0x12 */ + u8 cantbsel; /* + 0x14 0x0a */ + u8 canidac; /* + 0x15 0x0b */ + u8 reserved; /* + 0x16 0x0c */ + _MSCAN_RESERVED_(6, 5); /* + 0x17 */ +#ifndef MSCAN_FOR_MPC5200 + u8 canmisc; /* 0x0d */ +#endif + u8 canrxerr; /* + 0x1c 0x0e */ + u8 cantxerr; /* + 0x1d 0x0f */ + _MSCAN_RESERVED_(7, 2); /* + 0x1e */ + u16 canidar1_0; /* + 0x20 0x10 */ + _MSCAN_RESERVED_(8, 2); /* + 0x22 */ + u16 canidar3_2; /* + 0x24 0x12 */ + _MSCAN_RESERVED_(9, 2); /* + 0x26 */ + u16 canidmr1_0; /* + 0x28 0x14 */ + _MSCAN_RESERVED_(10, 2); /* + 0x2a */ + u16 canidmr3_2; /* + 0x2c 0x16 */ + _MSCAN_RESERVED_(11, 2); /* + 0x2e */ + u16 canidar5_4; /* + 0x30 0x18 */ + _MSCAN_RESERVED_(12, 2); /* + 0x32 */ + u16 canidar7_6; /* + 0x34 0x1a */ + _MSCAN_RESERVED_(13, 2); /* + 0x36 */ + u16 canidmr5_4; /* + 0x38 0x1c */ + _MSCAN_RESERVED_(14, 2); /* + 0x3a */ + u16 canidmr7_6; /* + 0x3c 0x1e */ + _MSCAN_RESERVED_(15, 2); /* + 0x3e */ + struct { + u16 idr1_0; /* + 0x40 0x20 */ + _MSCAN_RESERVED_(16, 2); /* + 0x42 */ + u16 idr3_2; /* + 0x44 0x22 */ + _MSCAN_RESERVED_(17, 2); /* + 0x46 */ + u16 dsr1_0; /* + 0x48 0x24 */ + _MSCAN_RESERVED_(18, 2); /* + 0x4a */ + u16 dsr3_2; /* + 0x4c 0x26 */ + _MSCAN_RESERVED_(19, 2); /* + 0x4e */ + u16 dsr5_4; /* + 0x50 0x28 */ + _MSCAN_RESERVED_(20, 2); /* + 0x52 */ + u16 dsr7_6; /* + 0x54 0x2a */ + _MSCAN_RESERVED_(21, 2); /* + 0x56 */ + u8 dlr; /* + 0x58 0x2c */ + u8:8; /* + 0x59 0x2d */ + _MSCAN_RESERVED_(22, 2); /* + 0x5a */ + u16 time; /* + 0x5c 0x2e */ + } rx; + _MSCAN_RESERVED_(23, 2); /* + 0x5e */ + struct { + u16 idr1_0; /* + 0x60 0x30 */ + _MSCAN_RESERVED_(24, 2); /* + 0x62 */ + u16 idr3_2; /* + 0x64 0x32 */ + _MSCAN_RESERVED_(25, 2); /* + 0x66 */ + u16 dsr1_0; /* + 0x68 0x34 */ + _MSCAN_RESERVED_(26, 2); /* + 0x6a */ + u16 dsr3_2; /* + 0x6c 0x36 */ + _MSCAN_RESERVED_(27, 2); /* + 0x6e */ + u16 dsr5_4; /* + 0x70 0x38 */ + _MSCAN_RESERVED_(28, 2); /* + 0x72 */ + u16 dsr7_6; /* + 0x74 0x3a */ + _MSCAN_RESERVED_(29, 2); /* + 0x76 */ + u8 dlr; /* + 0x78 0x3c */ + u8 tbpr; /* + 0x79 0x3d */ + _MSCAN_RESERVED_(30, 2); /* + 0x7a */ + u16 time; /* + 0x7c 0x3e */ + } tx; + _MSCAN_RESERVED_(31, 2); /* + 0x7e */ +} __attribute__ ((packed)); + +#undef _MSCAN_RESERVED_ +#define MSCAN_REGION sizeof(struct mscan) + +#define MSCAN_NORMAL_MODE 0 +#define MSCAN_SLEEP_MODE MSCAN_SLPRQ +#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ) +#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ) +#define MSCAN_SET_MODE_RETRIES 255 +#define MSCAN_ECHO_SKB_MAX 3 + +#define BTR0_BRP_MASK 0x3f +#define BTR0_SJW_SHIFT 6 +#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT) + +#define BTR1_TSEG1_MASK 0xf +#define BTR1_TSEG2_SHIFT 4 +#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT) +#define BTR1_SAM_SHIFT 7 + +#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK) +#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \ + BTR0_SJW_MASK) + +#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK) +#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \ + BTR1_TSEG2_MASK) +#define BTR1_SET_SAM(sam) ((sam) ? 1 << BTR1_SAM_SHIFT : 0) + +#define F_RX_PROGRESS 0 +#define F_TX_PROGRESS 1 +#define F_TX_WAIT_ALL 2 + +#define TX_QUEUE_SIZE 3 + +struct tx_queue_entry { + struct list_head list; + u8 mask; + u8 id; +}; + +struct mscan_priv { + struct can_priv can; /* must be the first member */ + long open_time; + unsigned long flags; + void __iomem *reg_base; /* ioremap'ed address to registers */ + u8 shadow_statflg; + u8 shadow_canrier; + u8 cur_pri; + u8 prev_buf_id; + u8 tx_active; + + struct list_head tx_head; + struct tx_queue_entry tx_queue[TX_QUEUE_SIZE]; + struct napi_struct napi; +}; + +extern struct net_device *alloc_mscandev(void); +/* + * clock_src: + * 1 = The MSCAN clock source is the onchip Bus Clock. + * 0 = The MSCAN clock source is the chip Oscillator Clock. + */ +extern int register_mscandev(struct net_device *dev, int clock_src); +extern void unregister_mscandev(struct net_device *dev); + +#endif /* __MSCAN_H__ */ diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 16d2ecd2a3b7..b4ba88a31075 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -296,11 +296,9 @@ static void sja1000_rx(struct net_device *dev) uint8_t dlc; int i; - skb = dev_alloc_skb(sizeof(struct can_frame)); + skb = alloc_can_skb(dev, &cf); if (skb == NULL) return; - skb->dev = dev; - skb->protocol = htons(ETH_P_CAN); fi = priv->read_reg(priv, REG_FI); dlc = fi & 0x0F; @@ -323,8 +321,6 @@ static void sja1000_rx(struct net_device *dev) if (fi & FI_RTR) id |= CAN_RTR_FLAG; - cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); - memset(cf, 0, sizeof(struct can_frame)); cf->can_id = id; cf->can_dlc = dlc; for (i = 0; i < dlc; i++) @@ -351,15 +347,9 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) enum can_state state = priv->can.state; uint8_t ecc, alc; - skb = dev_alloc_skb(sizeof(struct can_frame)); + skb = alloc_can_err_skb(dev, &cf); if (skb == NULL) return -ENOMEM; - skb->dev = dev; - skb->protocol = htons(ETH_P_CAN); - cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); - memset(cf, 0, sizeof(struct can_frame)); - cf->can_id = CAN_ERR_FLAG; - cf->can_dlc = CAN_ERR_DLC; if (isrc & IRQ_DOI) { /* data overrun interrupt */ @@ -526,7 +516,7 @@ static int sja1000_open(struct net_device *dev) /* register interrupt handler, if not done by the device driver */ if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) { - err = request_irq(dev->irq, &sja1000_interrupt, priv->irq_flags, + err = request_irq(dev->irq, sja1000_interrupt, priv->irq_flags, dev->name, (void *)dev); if (err) { close_candev(dev); @@ -565,7 +555,8 @@ struct net_device *alloc_sja1000dev(int sizeof_priv) struct net_device *dev; struct sja1000_priv *priv; - dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv); + dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv, + SJA1000_ECHO_SKB_MAX); if (!dev) return NULL; diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h index 302d2c763ad7..97a622b9302f 100644 --- a/drivers/net/can/sja1000/sja1000.h +++ b/drivers/net/can/sja1000/sja1000.h @@ -50,6 +50,8 @@ #include <linux/can/dev.h> #include <linux/can/platform/sja1000.h> +#define SJA1000_ECHO_SKB_MAX 1 /* the SJA1000 has one TX buffer object */ + #define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */ /* SJA1000 registers - manual section 6.4 (Pelican Mode) */ diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c new file mode 100644 index 000000000000..07e8016b17ec --- /dev/null +++ b/drivers/net/can/ti_hecc.c @@ -0,0 +1,993 @@ +/* + * TI HECC (CAN) device driver + * + * This driver supports TI's HECC (High End CAN Controller module) and the + * specs for the same is available at <http://www.ti.com> + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed as is WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Your platform definitions should specify module ram offsets and interrupt + * number to use as follows: + * + * static struct ti_hecc_platform_data am3517_evm_hecc_pdata = { + * .scc_hecc_offset = 0, + * .scc_ram_offset = 0x3000, + * .hecc_ram_offset = 0x3000, + * .mbx_offset = 0x2000, + * .int_line = 0, + * .revision = 1, + * }; + * + * Please see include/can/platform/ti_hecc.h for description of above fields + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/platform/ti_hecc.h> + +#define DRV_NAME "ti_hecc" +#define HECC_MODULE_VERSION "0.7" +MODULE_VERSION(HECC_MODULE_VERSION); +#define DRV_DESC "TI High End CAN Controller Driver " HECC_MODULE_VERSION + +/* TX / RX Mailbox Configuration */ +#define HECC_MAX_MAILBOXES 32 /* hardware mailboxes - do not change */ +#define MAX_TX_PRIO 0x3F /* hardware value - do not change */ + +/* + * Important Note: TX mailbox configuration + * TX mailboxes should be restricted to the number of SKB buffers to avoid + * maintaining SKB buffers separately. TX mailboxes should be a power of 2 + * for the mailbox logic to work. Top mailbox numbers are reserved for RX + * and lower mailboxes for TX. + * + * HECC_MAX_TX_MBOX HECC_MB_TX_SHIFT + * 4 (default) 2 + * 8 3 + * 16 4 + */ +#define HECC_MB_TX_SHIFT 2 /* as per table above */ +#define HECC_MAX_TX_MBOX BIT(HECC_MB_TX_SHIFT) + +#define HECC_TX_PRIO_SHIFT (HECC_MB_TX_SHIFT) +#define HECC_TX_PRIO_MASK (MAX_TX_PRIO << HECC_MB_TX_SHIFT) +#define HECC_TX_MB_MASK (HECC_MAX_TX_MBOX - 1) +#define HECC_TX_MASK ((HECC_MAX_TX_MBOX - 1) | HECC_TX_PRIO_MASK) +#define HECC_TX_MBOX_MASK (~(BIT(HECC_MAX_TX_MBOX) - 1)) +#define HECC_DEF_NAPI_WEIGHT HECC_MAX_RX_MBOX + +/* + * Important Note: RX mailbox configuration + * RX mailboxes are further logically split into two - main and buffer + * mailboxes. The goal is to get all packets into main mailboxes as + * driven by mailbox number and receive priority (higher to lower) and + * buffer mailboxes are used to receive pkts while main mailboxes are being + * processed. This ensures in-order packet reception. + * + * Here are the recommended values for buffer mailbox. Note that RX mailboxes + * start after TX mailboxes: + * + * HECC_MAX_RX_MBOX HECC_RX_BUFFER_MBOX No of buffer mailboxes + * 28 12 8 + * 16 20 4 + */ + +#define HECC_MAX_RX_MBOX (HECC_MAX_MAILBOXES - HECC_MAX_TX_MBOX) +#define HECC_RX_BUFFER_MBOX 12 /* as per table above */ +#define HECC_RX_FIRST_MBOX (HECC_MAX_MAILBOXES - 1) +#define HECC_RX_HIGH_MBOX_MASK (~(BIT(HECC_RX_BUFFER_MBOX) - 1)) + +/* TI HECC module registers */ +#define HECC_CANME 0x0 /* Mailbox enable */ +#define HECC_CANMD 0x4 /* Mailbox direction */ +#define HECC_CANTRS 0x8 /* Transmit request set */ +#define HECC_CANTRR 0xC /* Transmit request */ +#define HECC_CANTA 0x10 /* Transmission acknowledge */ +#define HECC_CANAA 0x14 /* Abort acknowledge */ +#define HECC_CANRMP 0x18 /* Receive message pending */ +#define HECC_CANRML 0x1C /* Remote message lost */ +#define HECC_CANRFP 0x20 /* Remote frame pending */ +#define HECC_CANGAM 0x24 /* SECC only:Global acceptance mask */ +#define HECC_CANMC 0x28 /* Master control */ +#define HECC_CANBTC 0x2C /* Bit timing configuration */ +#define HECC_CANES 0x30 /* Error and status */ +#define HECC_CANTEC 0x34 /* Transmit error counter */ +#define HECC_CANREC 0x38 /* Receive error counter */ +#define HECC_CANGIF0 0x3C /* Global interrupt flag 0 */ +#define HECC_CANGIM 0x40 /* Global interrupt mask */ +#define HECC_CANGIF1 0x44 /* Global interrupt flag 1 */ +#define HECC_CANMIM 0x48 /* Mailbox interrupt mask */ +#define HECC_CANMIL 0x4C /* Mailbox interrupt level */ +#define HECC_CANOPC 0x50 /* Overwrite protection control */ +#define HECC_CANTIOC 0x54 /* Transmit I/O control */ +#define HECC_CANRIOC 0x58 /* Receive I/O control */ +#define HECC_CANLNT 0x5C /* HECC only: Local network time */ +#define HECC_CANTOC 0x60 /* HECC only: Time-out control */ +#define HECC_CANTOS 0x64 /* HECC only: Time-out status */ +#define HECC_CANTIOCE 0x68 /* SCC only:Enhanced TX I/O control */ +#define HECC_CANRIOCE 0x6C /* SCC only:Enhanced RX I/O control */ + +/* Mailbox registers */ +#define HECC_CANMID 0x0 +#define HECC_CANMCF 0x4 +#define HECC_CANMDL 0x8 +#define HECC_CANMDH 0xC + +#define HECC_SET_REG 0xFFFFFFFF +#define HECC_CANID_MASK 0x3FF /* 18 bits mask for extended id's */ +#define HECC_CCE_WAIT_COUNT 100 /* Wait for ~1 sec for CCE bit */ + +#define HECC_CANMC_SCM BIT(13) /* SCC compat mode */ +#define HECC_CANMC_CCR BIT(12) /* Change config request */ +#define HECC_CANMC_PDR BIT(11) /* Local Power down - for sleep mode */ +#define HECC_CANMC_ABO BIT(7) /* Auto Bus On */ +#define HECC_CANMC_STM BIT(6) /* Self test mode - loopback */ +#define HECC_CANMC_SRES BIT(5) /* Software reset */ + +#define HECC_CANTIOC_EN BIT(3) /* Enable CAN TX I/O pin */ +#define HECC_CANRIOC_EN BIT(3) /* Enable CAN RX I/O pin */ + +#define HECC_CANMID_IDE BIT(31) /* Extended frame format */ +#define HECC_CANMID_AME BIT(30) /* Acceptance mask enable */ +#define HECC_CANMID_AAM BIT(29) /* Auto answer mode */ + +#define HECC_CANES_FE BIT(24) /* form error */ +#define HECC_CANES_BE BIT(23) /* bit error */ +#define HECC_CANES_SA1 BIT(22) /* stuck at dominant error */ +#define HECC_CANES_CRCE BIT(21) /* CRC error */ +#define HECC_CANES_SE BIT(20) /* stuff bit error */ +#define HECC_CANES_ACKE BIT(19) /* ack error */ +#define HECC_CANES_BO BIT(18) /* Bus off status */ +#define HECC_CANES_EP BIT(17) /* Error passive status */ +#define HECC_CANES_EW BIT(16) /* Error warning status */ +#define HECC_CANES_SMA BIT(5) /* suspend mode ack */ +#define HECC_CANES_CCE BIT(4) /* Change config enabled */ +#define HECC_CANES_PDA BIT(3) /* Power down mode ack */ + +#define HECC_CANBTC_SAM BIT(7) /* sample points */ + +#define HECC_BUS_ERROR (HECC_CANES_FE | HECC_CANES_BE |\ + HECC_CANES_CRCE | HECC_CANES_SE |\ + HECC_CANES_ACKE) + +#define HECC_CANMCF_RTR BIT(4) /* Remote transmit request */ + +#define HECC_CANGIF_MAIF BIT(17) /* Message alarm interrupt */ +#define HECC_CANGIF_TCOIF BIT(16) /* Timer counter overflow int */ +#define HECC_CANGIF_GMIF BIT(15) /* Global mailbox interrupt */ +#define HECC_CANGIF_AAIF BIT(14) /* Abort ack interrupt */ +#define HECC_CANGIF_WDIF BIT(13) /* Write denied interrupt */ +#define HECC_CANGIF_WUIF BIT(12) /* Wake up interrupt */ +#define HECC_CANGIF_RMLIF BIT(11) /* Receive message lost interrupt */ +#define HECC_CANGIF_BOIF BIT(10) /* Bus off interrupt */ +#define HECC_CANGIF_EPIF BIT(9) /* Error passive interrupt */ +#define HECC_CANGIF_WLIF BIT(8) /* Warning level interrupt */ +#define HECC_CANGIF_MBOX_MASK 0x1F /* Mailbox number mask */ +#define HECC_CANGIM_I1EN BIT(1) /* Int line 1 enable */ +#define HECC_CANGIM_I0EN BIT(0) /* Int line 0 enable */ +#define HECC_CANGIM_DEF_MASK 0x700 /* only busoff/warning/passive */ +#define HECC_CANGIM_SIL BIT(2) /* system interrupts to int line 1 */ + +/* CAN Bittiming constants as per HECC specs */ +static struct can_bittiming_const ti_hecc_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +struct ti_hecc_priv { + struct can_priv can; /* MUST be first member/field */ + struct napi_struct napi; + struct net_device *ndev; + struct clk *clk; + void __iomem *base; + u32 scc_ram_offset; + u32 hecc_ram_offset; + u32 mbx_offset; + u32 int_line; + spinlock_t mbx_lock; /* CANME register needs protection */ + u32 tx_head; + u32 tx_tail; + u32 rx_next; +}; + +static inline int get_tx_head_mb(struct ti_hecc_priv *priv) +{ + return priv->tx_head & HECC_TX_MB_MASK; +} + +static inline int get_tx_tail_mb(struct ti_hecc_priv *priv) +{ + return priv->tx_tail & HECC_TX_MB_MASK; +} + +static inline int get_tx_head_prio(struct ti_hecc_priv *priv) +{ + return (priv->tx_head >> HECC_TX_PRIO_SHIFT) & MAX_TX_PRIO; +} + +static inline void hecc_write_lam(struct ti_hecc_priv *priv, u32 mbxno, u32 val) +{ + __raw_writel(val, priv->base + priv->hecc_ram_offset + mbxno * 4); +} + +static inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno, + u32 reg, u32 val) +{ + __raw_writel(val, priv->base + priv->mbx_offset + mbxno * 0x10 + + reg); +} + +static inline u32 hecc_read_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg) +{ + return __raw_readl(priv->base + priv->mbx_offset + mbxno * 0x10 + + reg); +} + +static inline void hecc_write(struct ti_hecc_priv *priv, u32 reg, u32 val) +{ + __raw_writel(val, priv->base + reg); +} + +static inline u32 hecc_read(struct ti_hecc_priv *priv, int reg) +{ + return __raw_readl(priv->base + reg); +} + +static inline void hecc_set_bit(struct ti_hecc_priv *priv, int reg, + u32 bit_mask) +{ + hecc_write(priv, reg, hecc_read(priv, reg) | bit_mask); +} + +static inline void hecc_clear_bit(struct ti_hecc_priv *priv, int reg, + u32 bit_mask) +{ + hecc_write(priv, reg, hecc_read(priv, reg) & ~bit_mask); +} + +static inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask) +{ + return (hecc_read(priv, reg) & bit_mask) ? 1 : 0; +} + +static int ti_hecc_get_state(const struct net_device *ndev, + enum can_state *state) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + *state = priv->can.state; + return 0; +} + +static int ti_hecc_set_btc(struct ti_hecc_priv *priv) +{ + struct can_bittiming *bit_timing = &priv->can.bittiming; + u32 can_btc; + + can_btc = (bit_timing->phase_seg2 - 1) & 0x7; + can_btc |= ((bit_timing->phase_seg1 + bit_timing->prop_seg - 1) + & 0xF) << 3; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) { + if (bit_timing->brp > 4) + can_btc |= HECC_CANBTC_SAM; + else + dev_warn(priv->ndev->dev.parent, "WARN: Triple" \ + "sampling not set due to h/w limitations"); + } + can_btc |= ((bit_timing->sjw - 1) & 0x3) << 8; + can_btc |= ((bit_timing->brp - 1) & 0xFF) << 16; + + /* ERM being set to 0 by default meaning resync at falling edge */ + + hecc_write(priv, HECC_CANBTC, can_btc); + dev_info(priv->ndev->dev.parent, "setting CANBTC=%#x\n", can_btc); + + return 0; +} + +static void ti_hecc_reset(struct net_device *ndev) +{ + u32 cnt; + struct ti_hecc_priv *priv = netdev_priv(ndev); + + dev_dbg(ndev->dev.parent, "resetting hecc ...\n"); + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SRES); + + /* Set change control request and wait till enabled */ + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + + /* + * INFO: It has been observed that at times CCE bit may not be + * set and hw seems to be ok even if this bit is not set so + * timing out with a timing of 1ms to respect the specs + */ + cnt = HECC_CCE_WAIT_COUNT; + while (!hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) { + --cnt; + udelay(10); + } + + /* + * Note: On HECC, BTC can be programmed only in initialization mode, so + * it is expected that the can bittiming parameters are set via ip + * utility before the device is opened + */ + ti_hecc_set_btc(priv); + + /* Clear CCR (and CANMC register) and wait for CCE = 0 enable */ + hecc_write(priv, HECC_CANMC, 0); + + /* + * INFO: CAN net stack handles bus off and hence disabling auto-bus-on + * hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_ABO); + */ + + /* + * INFO: It has been observed that at times CCE bit may not be + * set and hw seems to be ok even if this bit is not set so + */ + cnt = HECC_CCE_WAIT_COUNT; + while (hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) { + --cnt; + udelay(10); + } + + /* Enable TX and RX I/O Control pins */ + hecc_write(priv, HECC_CANTIOC, HECC_CANTIOC_EN); + hecc_write(priv, HECC_CANRIOC, HECC_CANRIOC_EN); + + /* Clear registers for clean operation */ + hecc_write(priv, HECC_CANTA, HECC_SET_REG); + hecc_write(priv, HECC_CANRMP, HECC_SET_REG); + hecc_write(priv, HECC_CANGIF0, HECC_SET_REG); + hecc_write(priv, HECC_CANGIF1, HECC_SET_REG); + hecc_write(priv, HECC_CANME, 0); + hecc_write(priv, HECC_CANMD, 0); + + /* SCC compat mode NOT supported (and not needed too) */ + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SCM); +} + +static void ti_hecc_start(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + u32 cnt, mbxno, mbx_mask; + + /* put HECC in initialization mode and set btc */ + ti_hecc_reset(ndev); + + priv->tx_head = priv->tx_tail = HECC_TX_MASK; + priv->rx_next = HECC_RX_FIRST_MBOX; + + /* Enable local and global acceptance mask registers */ + hecc_write(priv, HECC_CANGAM, HECC_SET_REG); + + /* Prepare configured mailboxes to receive messages */ + for (cnt = 0; cnt < HECC_MAX_RX_MBOX; cnt++) { + mbxno = HECC_MAX_MAILBOXES - 1 - cnt; + mbx_mask = BIT(mbxno); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + hecc_write_mbx(priv, mbxno, HECC_CANMID, HECC_CANMID_AME); + hecc_write_lam(priv, mbxno, HECC_SET_REG); + hecc_set_bit(priv, HECC_CANMD, mbx_mask); + hecc_set_bit(priv, HECC_CANME, mbx_mask); + hecc_set_bit(priv, HECC_CANMIM, mbx_mask); + } + + /* Prevent message over-write & Enable interrupts */ + hecc_write(priv, HECC_CANOPC, HECC_SET_REG); + if (priv->int_line) { + hecc_write(priv, HECC_CANMIL, HECC_SET_REG); + hecc_write(priv, HECC_CANGIM, HECC_CANGIM_DEF_MASK | + HECC_CANGIM_I1EN | HECC_CANGIM_SIL); + } else { + hecc_write(priv, HECC_CANMIL, 0); + hecc_write(priv, HECC_CANGIM, + HECC_CANGIM_DEF_MASK | HECC_CANGIM_I0EN); + } + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static void ti_hecc_stop(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + /* Disable interrupts and disable mailboxes */ + hecc_write(priv, HECC_CANGIM, 0); + hecc_write(priv, HECC_CANMIM, 0); + hecc_write(priv, HECC_CANME, 0); + priv->can.state = CAN_STATE_STOPPED; +} + +static int ti_hecc_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + ti_hecc_start(ndev); + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +/* + * ti_hecc_xmit: HECC Transmit + * + * The transmit mailboxes start from 0 to HECC_MAX_TX_MBOX. In HECC the + * priority of the mailbox for tranmission is dependent upon priority setting + * field in mailbox registers. The mailbox with highest value in priority field + * is transmitted first. Only when two mailboxes have the same value in + * priority field the highest numbered mailbox is transmitted first. + * + * To utilize the HECC priority feature as described above we start with the + * highest numbered mailbox with highest priority level and move on to the next + * mailbox with the same priority level and so on. Once we loop through all the + * transmit mailboxes we choose the next priority level (lower) and so on + * until we reach the lowest priority level on the lowest numbered mailbox + * when we stop transmission until all mailboxes are transmitted and then + * restart at highest numbered mailbox with highest priority. + * + * Two counters (head and tail) are used to track the next mailbox to transmit + * and to track the echo buffer for already transmitted mailbox. The queue + * is stopped when all the mailboxes are busy or when there is a priority + * value roll-over happens. + */ +static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + u32 mbxno, mbx_mask, data; + unsigned long flags; + + mbxno = get_tx_head_mb(priv); + mbx_mask = BIT(mbxno); + spin_lock_irqsave(&priv->mbx_lock, flags); + if (unlikely(hecc_read(priv, HECC_CANME) & mbx_mask)) { + spin_unlock_irqrestore(&priv->mbx_lock, flags); + netif_stop_queue(ndev); + dev_err(priv->ndev->dev.parent, + "BUG: TX mbx not ready tx_head=%08X, tx_tail=%08X\n", + priv->tx_head, priv->tx_tail); + return NETDEV_TX_BUSY; + } + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + /* Prepare mailbox for transmission */ + data = min_t(u8, cf->can_dlc, 8); + if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ + data |= HECC_CANMCF_RTR; + data |= get_tx_head_prio(priv) << 8; + hecc_write_mbx(priv, mbxno, HECC_CANMCF, data); + + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + data = (cf->can_id & CAN_EFF_MASK) | HECC_CANMID_IDE; + else /* Standard frame format */ + data = (cf->can_id & CAN_SFF_MASK) << 18; + hecc_write_mbx(priv, mbxno, HECC_CANMID, data); + hecc_write_mbx(priv, mbxno, HECC_CANMDL, + be32_to_cpu(*(u32 *)(cf->data))); + if (cf->can_dlc > 4) + hecc_write_mbx(priv, mbxno, HECC_CANMDH, + be32_to_cpu(*(u32 *)(cf->data + 4))); + else + *(u32 *)(cf->data + 4) = 0; + can_put_echo_skb(skb, ndev, mbxno); + + spin_lock_irqsave(&priv->mbx_lock, flags); + --priv->tx_head; + if ((hecc_read(priv, HECC_CANME) & BIT(get_tx_head_mb(priv))) || + (priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK) { + netif_stop_queue(ndev); + } + hecc_set_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + hecc_clear_bit(priv, HECC_CANMD, mbx_mask); + hecc_set_bit(priv, HECC_CANMIM, mbx_mask); + hecc_write(priv, HECC_CANTRS, mbx_mask); + + return NETDEV_TX_OK; +} + +static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 data, mbx_mask; + unsigned long flags; + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->ndev->dev.parent, + "ti_hecc_rx_pkt: alloc_can_skb() failed\n"); + return -ENOMEM; + } + + mbx_mask = BIT(mbxno); + data = hecc_read_mbx(priv, mbxno, HECC_CANMID); + if (data & HECC_CANMID_IDE) + cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (data >> 18) & CAN_SFF_MASK; + data = hecc_read_mbx(priv, mbxno, HECC_CANMCF); + if (data & HECC_CANMCF_RTR) + cf->can_id |= CAN_RTR_FLAG; + cf->can_dlc = data & 0xF; + data = hecc_read_mbx(priv, mbxno, HECC_CANMDL); + *(u32 *)(cf->data) = cpu_to_be32(data); + if (cf->can_dlc > 4) { + data = hecc_read_mbx(priv, mbxno, HECC_CANMDH); + *(u32 *)(cf->data + 4) = cpu_to_be32(data); + } else { + *(u32 *)(cf->data + 4) = 0; + } + spin_lock_irqsave(&priv->mbx_lock, flags); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + hecc_write(priv, HECC_CANRMP, mbx_mask); + /* enable mailbox only if it is part of rx buffer mailboxes */ + if (priv->rx_next < HECC_RX_BUFFER_MBOX) + hecc_set_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + stats->rx_packets++; + + return 0; +} + +/* + * ti_hecc_rx_poll - HECC receive pkts + * + * The receive mailboxes start from highest numbered mailbox till last xmit + * mailbox. On CAN frame reception the hardware places the data into highest + * numbered mailbox that matches the CAN ID filter. Since all receive mailboxes + * have same filtering (ALL CAN frames) packets will arrive in the highest + * available RX mailbox and we need to ensure in-order packet reception. + * + * To ensure the packets are received in the right order we logically divide + * the RX mailboxes into main and buffer mailboxes. Packets are received as per + * mailbox priotity (higher to lower) in the main bank and once it is full we + * disable further reception into main mailboxes. While the main mailboxes are + * processed in NAPI, further packets are received in buffer mailboxes. + * + * We maintain a RX next mailbox counter to process packets and once all main + * mailboxe packets are passed to the upper stack we enable all of them but + * continue to process packets received in buffer mailboxes. With each packet + * received from buffer mailbox we enable it immediately so as to handle the + * overflow from higher mailboxes. + */ +static int ti_hecc_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct ti_hecc_priv *priv = netdev_priv(ndev); + u32 num_pkts = 0; + u32 mbx_mask; + unsigned long pending_pkts, flags; + + if (!netif_running(ndev)) + return 0; + + while ((pending_pkts = hecc_read(priv, HECC_CANRMP)) && + num_pkts < quota) { + mbx_mask = BIT(priv->rx_next); /* next rx mailbox to process */ + if (mbx_mask & pending_pkts) { + if (ti_hecc_rx_pkt(priv, priv->rx_next) < 0) + return num_pkts; + ++num_pkts; + } else if (priv->rx_next > HECC_RX_BUFFER_MBOX) { + break; /* pkt not received yet */ + } + --priv->rx_next; + if (priv->rx_next == HECC_RX_BUFFER_MBOX) { + /* enable high bank mailboxes */ + spin_lock_irqsave(&priv->mbx_lock, flags); + mbx_mask = hecc_read(priv, HECC_CANME); + mbx_mask |= HECC_RX_HIGH_MBOX_MASK; + hecc_write(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + } else if (priv->rx_next == HECC_MAX_TX_MBOX - 1) { + priv->rx_next = HECC_RX_FIRST_MBOX; + break; + } + } + + /* Enable packet interrupt if all pkts are handled */ + if (hecc_read(priv, HECC_CANRMP) == 0) { + napi_complete(napi); + /* Re-enable RX mailbox interrupts */ + mbx_mask = hecc_read(priv, HECC_CANMIM); + mbx_mask |= HECC_TX_MBOX_MASK; + hecc_write(priv, HECC_CANMIM, mbx_mask); + } + + return num_pkts; +} + +static int ti_hecc_error(struct net_device *ndev, int int_status, + int err_status) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* propogate the error condition to the can stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->ndev->dev.parent, + "ti_hecc_error: alloc_can_err_skb() failed\n"); + return -ENOMEM; + } + + if (int_status & HECC_CANGIF_WLIF) { /* warning level int */ + if ((int_status & HECC_CANGIF_BOIF) == 0) { + priv->can.state = CAN_STATE_ERROR_WARNING; + ++priv->can.can_stats.error_warning; + cf->can_id |= CAN_ERR_CRTL; + if (hecc_read(priv, HECC_CANTEC) > 96) + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; + if (hecc_read(priv, HECC_CANREC) > 96) + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; + } + hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW); + dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n"); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + } + + if (int_status & HECC_CANGIF_EPIF) { /* error passive int */ + if ((int_status & HECC_CANGIF_BOIF) == 0) { + priv->can.state = CAN_STATE_ERROR_PASSIVE; + ++priv->can.can_stats.error_passive; + cf->can_id |= CAN_ERR_CRTL; + if (hecc_read(priv, HECC_CANTEC) > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (hecc_read(priv, HECC_CANREC) > 127) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + } + hecc_set_bit(priv, HECC_CANES, HECC_CANES_EP); + dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n"); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + } + + /* + * Need to check busoff condition in error status register too to + * ensure warning interrupts don't hog the system + */ + if ((int_status & HECC_CANGIF_BOIF) || (err_status & HECC_CANES_BO)) { + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + hecc_set_bit(priv, HECC_CANES, HECC_CANES_BO); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + /* Disable all interrupts in bus-off to avoid int hog */ + hecc_write(priv, HECC_CANGIM, 0); + can_bus_off(ndev); + } + + if (err_status & HECC_BUS_ERROR) { + ++priv->can.can_stats.bus_error; + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + if (err_status & HECC_CANES_FE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_FE); + cf->data[2] |= CAN_ERR_PROT_FORM; + } + if (err_status & HECC_CANES_BE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_BE); + cf->data[2] |= CAN_ERR_PROT_BIT; + } + if (err_status & HECC_CANES_SE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_SE); + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + if (err_status & HECC_CANES_CRCE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE); + cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL; + } + if (err_status & HECC_CANES_ACKE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE); + cf->data[2] |= CAN_ERR_PROT_LOC_ACK | + CAN_ERR_PROT_LOC_ACK_DEL; + } + } + + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + return 0; +} + +static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 mbxno, mbx_mask, int_status, err_status; + unsigned long ack, flags; + + int_status = hecc_read(priv, + (priv->int_line) ? HECC_CANGIF1 : HECC_CANGIF0); + + if (!int_status) + return IRQ_NONE; + + err_status = hecc_read(priv, HECC_CANES); + if (err_status & (HECC_BUS_ERROR | HECC_CANES_BO | + HECC_CANES_EP | HECC_CANES_EW)) + ti_hecc_error(ndev, int_status, err_status); + + if (int_status & HECC_CANGIF_GMIF) { + while (priv->tx_tail - priv->tx_head > 0) { + mbxno = get_tx_tail_mb(priv); + mbx_mask = BIT(mbxno); + if (!(mbx_mask & hecc_read(priv, HECC_CANTA))) + break; + hecc_clear_bit(priv, HECC_CANMIM, mbx_mask); + hecc_write(priv, HECC_CANTA, mbx_mask); + spin_lock_irqsave(&priv->mbx_lock, flags); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + stats->tx_bytes += hecc_read_mbx(priv, mbxno, + HECC_CANMCF) & 0xF; + stats->tx_packets++; + can_get_echo_skb(ndev, mbxno); + --priv->tx_tail; + } + + /* restart queue if wrap-up or if queue stalled on last pkt */ + if (((priv->tx_head == priv->tx_tail) && + ((priv->tx_head & HECC_TX_MASK) != HECC_TX_MASK)) || + (((priv->tx_tail & HECC_TX_MASK) == HECC_TX_MASK) && + ((priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK))) + netif_wake_queue(ndev); + + /* Disable RX mailbox interrupts and let NAPI reenable them */ + if (hecc_read(priv, HECC_CANRMP)) { + ack = hecc_read(priv, HECC_CANMIM); + ack &= BIT(HECC_MAX_TX_MBOX) - 1; + hecc_write(priv, HECC_CANMIM, ack); + napi_schedule(&priv->napi); + } + } + + /* clear all interrupt conditions - read back to avoid spurious ints */ + if (priv->int_line) { + hecc_write(priv, HECC_CANGIF1, HECC_SET_REG); + int_status = hecc_read(priv, HECC_CANGIF1); + } else { + hecc_write(priv, HECC_CANGIF0, HECC_SET_REG); + int_status = hecc_read(priv, HECC_CANGIF0); + } + + return IRQ_HANDLED; +} + +static int ti_hecc_open(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + int err; + + err = request_irq(ndev->irq, ti_hecc_interrupt, IRQF_SHARED, + ndev->name, ndev); + if (err) { + dev_err(ndev->dev.parent, "error requesting interrupt\n"); + return err; + } + + /* Open common can device */ + err = open_candev(ndev); + if (err) { + dev_err(ndev->dev.parent, "open_candev() failed %d\n", err); + free_irq(ndev->irq, ndev); + return err; + } + + clk_enable(priv->clk); + ti_hecc_start(ndev); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; +} + +static int ti_hecc_close(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + ti_hecc_stop(ndev); + free_irq(ndev->irq, ndev); + clk_disable(priv->clk); + close_candev(ndev); + + return 0; +} + +static const struct net_device_ops ti_hecc_netdev_ops = { + .ndo_open = ti_hecc_open, + .ndo_stop = ti_hecc_close, + .ndo_start_xmit = ti_hecc_xmit, +}; + +static int ti_hecc_probe(struct platform_device *pdev) +{ + struct net_device *ndev = (struct net_device *)0; + struct ti_hecc_priv *priv; + struct ti_hecc_platform_data *pdata; + struct resource *mem, *irq; + void __iomem *addr; + int err = -ENODEV; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data\n"); + goto probe_exit; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No mem resources\n"); + goto probe_exit; + } + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "No irq resource\n"); + goto probe_exit; + } + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { + dev_err(&pdev->dev, "HECC region already claimed\n"); + err = -EBUSY; + goto probe_exit; + } + addr = ioremap(mem->start, resource_size(mem)); + if (!addr) { + dev_err(&pdev->dev, "ioremap failed\n"); + err = -ENOMEM; + goto probe_exit_free_region; + } + + ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + err = -ENOMEM; + goto probe_exit_iounmap; + } + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->base = addr; + priv->scc_ram_offset = pdata->scc_ram_offset; + priv->hecc_ram_offset = pdata->hecc_ram_offset; + priv->mbx_offset = pdata->mbx_offset; + priv->int_line = pdata->int_line; + + priv->can.bittiming_const = &ti_hecc_bittiming_const; + priv->can.do_set_mode = ti_hecc_do_set_mode; + priv->can.do_get_state = ti_hecc_get_state; + + ndev->irq = irq->start; + ndev->flags |= IFF_ECHO; + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &ti_hecc_netdev_ops; + + priv->clk = clk_get(&pdev->dev, "hecc_ck"); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "No clock available\n"); + err = PTR_ERR(priv->clk); + priv->clk = NULL; + goto probe_exit_candev; + } + priv->can.clock.freq = clk_get_rate(priv->clk); + netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll, + HECC_DEF_NAPI_WEIGHT); + + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed\n"); + goto probe_exit_clk; + } + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n", + priv->base, (u32) ndev->irq); + + return 0; + +probe_exit_clk: + clk_put(priv->clk); +probe_exit_candev: + free_candev(ndev); +probe_exit_iounmap: + iounmap(addr); +probe_exit_free_region: + release_mem_region(mem->start, resource_size(mem)); +probe_exit: + return err; +} + +static int __devexit ti_hecc_remove(struct platform_device *pdev) +{ + struct resource *res; + struct net_device *ndev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(ndev); + + clk_put(priv->clk); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(priv->base); + release_mem_region(res->start, resource_size(res)); + unregister_candev(ndev); + free_candev(ndev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +/* TI HECC netdevice driver: platform driver structure */ +static struct platform_driver ti_hecc_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ti_hecc_probe, + .remove = __devexit_p(ti_hecc_remove), +}; + +static int __init ti_hecc_init_driver(void) +{ + printk(KERN_INFO DRV_DESC "\n"); + return platform_driver_register(&ti_hecc_driver); +} +module_init(ti_hecc_init_driver); + +static void __exit ti_hecc_exit_driver(void) +{ + printk(KERN_INFO DRV_DESC " unloaded\n"); + platform_driver_unregister(&ti_hecc_driver); +} +module_exit(ti_hecc_exit_driver); + +MODULE_AUTHOR("Anant Gole <anantgole@ti.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DRV_DESC); diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index abdbd9c2b788..591eb0eb1c2b 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -232,7 +232,7 @@ MODULE_DEVICE_TABLE(usb, ems_usb_table); #define INTR_IN_BUFFER_SIZE 4 #define MAX_RX_URBS 10 -#define MAX_TX_URBS CAN_ECHO_SKB_MAX +#define MAX_TX_URBS 10 struct ems_usb; @@ -311,23 +311,19 @@ static void ems_usb_rx_can_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) int i; struct net_device_stats *stats = &dev->netdev->stats; - skb = netdev_alloc_skb(dev->netdev, sizeof(struct can_frame)); + skb = alloc_can_skb(dev->netdev, &cf); if (skb == NULL) return; - skb->protocol = htons(ETH_P_CAN); - - cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); - cf->can_id = le32_to_cpu(msg->msg.can_msg.id); cf->can_dlc = min_t(u8, msg->msg.can_msg.length, 8); - if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME - || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) + if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME || + msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) cf->can_id |= CAN_EFF_FLAG; - if (msg->type == CPC_MSG_TYPE_RTR_FRAME - || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) { + if (msg->type == CPC_MSG_TYPE_RTR_FRAME || + msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) { cf->can_id |= CAN_RTR_FLAG; } else { for (i = 0; i < cf->can_dlc; i++) @@ -346,18 +342,10 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) struct sk_buff *skb; struct net_device_stats *stats = &dev->netdev->stats; - skb = netdev_alloc_skb(dev->netdev, sizeof(struct can_frame)); + skb = alloc_can_err_skb(dev->netdev, &cf); if (skb == NULL) return; - skb->protocol = htons(ETH_P_CAN); - - cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); - memset(cf, 0, sizeof(struct can_frame)); - - cf->can_id = CAN_ERR_FLAG; - cf->can_dlc = CAN_ERR_DLC; - if (msg->type == CPC_MSG_TYPE_CAN_STATE) { u8 state = msg->msg.can_state; @@ -1015,7 +1003,7 @@ static int ems_usb_probe(struct usb_interface *intf, struct ems_usb *dev; int i, err = -ENOMEM; - netdev = alloc_candev(sizeof(struct ems_usb)); + netdev = alloc_candev(sizeof(struct ems_usb), MAX_TX_URBS); if (!netdev) { dev_err(netdev->dev.parent, "Couldn't alloc candev\n"); return -ENOMEM; diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index 3bf1b04f2cab..d4c6e7fcff53 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -33,10 +33,16 @@ #include <net/route.h> #include <net/ipv6.h> #include <net/ip6_route.h> +#include <net/ip6_checksum.h> #include <scsi/iscsi_if.h> #include "cnic_if.h" #include "bnx2.h" +#include "bnx2x_reg.h" +#include "bnx2x_fw_defs.h" +#include "bnx2x_hsi.h" +#include "../scsi/bnx2i/57xx_iscsi_constants.h" +#include "../scsi/bnx2i/57xx_iscsi_hsi.h" #include "cnic.h" #include "cnic_defs.h" @@ -59,6 +65,7 @@ static DEFINE_MUTEX(cnic_lock); static struct cnic_ulp_ops *cnic_ulp_tbl[MAX_CNIC_ULP_TYPE]; static int cnic_service_bnx2(void *, void *); +static int cnic_service_bnx2x(void *, void *); static int cnic_ctl(void *, struct cnic_ctl_info *); static struct cnic_ops cnic_bnx2_ops = { @@ -67,9 +74,14 @@ static struct cnic_ops cnic_bnx2_ops = { .cnic_ctl = cnic_ctl, }; -static void cnic_shutdown_bnx2_rx_ring(struct cnic_dev *); -static void cnic_init_bnx2_tx_ring(struct cnic_dev *); -static void cnic_init_bnx2_rx_ring(struct cnic_dev *); +static struct cnic_ops cnic_bnx2x_ops = { + .cnic_owner = THIS_MODULE, + .cnic_handler = cnic_service_bnx2x, + .cnic_ctl = cnic_ctl, +}; + +static void cnic_shutdown_rings(struct cnic_dev *); +static void cnic_init_rings(struct cnic_dev *); static int cnic_cm_set_pg(struct cnic_sock *); static int cnic_uio_open(struct uio_info *uinfo, struct inode *inode) @@ -83,10 +95,16 @@ static int cnic_uio_open(struct uio_info *uinfo, struct inode *inode) if (cp->uio_dev != -1) return -EBUSY; + rtnl_lock(); + if (!test_bit(CNIC_F_CNIC_UP, &dev->flags)) { + rtnl_unlock(); + return -ENODEV; + } + cp->uio_dev = iminor(inode); - cnic_init_bnx2_tx_ring(dev); - cnic_init_bnx2_rx_ring(dev); + cnic_init_rings(dev); + rtnl_unlock(); return 0; } @@ -96,7 +114,7 @@ static int cnic_uio_close(struct uio_info *uinfo, struct inode *inode) struct cnic_dev *dev = uinfo->priv; struct cnic_local *cp = dev->cnic_priv; - cnic_shutdown_bnx2_rx_ring(dev); + cnic_shutdown_rings(dev); cp->uio_dev = -1; return 0; @@ -162,6 +180,36 @@ static void cnic_ctx_wr(struct cnic_dev *dev, u32 cid_addr, u32 off, u32 val) ethdev->drv_ctl(dev->netdev, &info); } +static void cnic_ctx_tbl_wr(struct cnic_dev *dev, u32 off, dma_addr_t addr) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_eth_dev *ethdev = cp->ethdev; + struct drv_ctl_info info; + struct drv_ctl_io *io = &info.data.io; + + info.cmd = DRV_CTL_CTXTBL_WR_CMD; + io->offset = off; + io->dma_addr = addr; + ethdev->drv_ctl(dev->netdev, &info); +} + +static void cnic_ring_ctl(struct cnic_dev *dev, u32 cid, u32 cl_id, int start) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_eth_dev *ethdev = cp->ethdev; + struct drv_ctl_info info; + struct drv_ctl_l2_ring *ring = &info.data.ring; + + if (start) + info.cmd = DRV_CTL_START_L2_CMD; + else + info.cmd = DRV_CTL_STOP_L2_CMD; + + ring->cid = cid; + ring->client_id = cl_id; + ethdev->drv_ctl(dev->netdev, &info); +} + static void cnic_reg_wr_ind(struct cnic_dev *dev, u32 off, u32 val) { struct cnic_local *cp = dev->cnic_priv; @@ -204,6 +252,19 @@ static void cnic_kwq_completion(struct cnic_dev *dev, u32 count) ethdev->drv_ctl(dev->netdev, &info); } +static int cnic_get_l5_cid(struct cnic_local *cp, u32 cid, u32 *l5_cid) +{ + u32 i; + + for (i = 0; i < MAX_ISCSI_TBL_SZ; i++) { + if (cp->ctx_tbl[i].cid == cid) { + *l5_cid = i; + return 0; + } + } + return -EINVAL; +} + static int cnic_send_nlmsg(struct cnic_local *cp, u32 type, struct cnic_sock *csk) { @@ -347,7 +408,7 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops) { struct cnic_dev *dev; - if (ulp_type >= MAX_CNIC_ULP_TYPE) { + if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) { printk(KERN_ERR PFX "cnic_register_driver: Bad type %d\n", ulp_type); return -EINVAL; @@ -393,7 +454,7 @@ int cnic_unregister_driver(int ulp_type) struct cnic_ulp_ops *ulp_ops; int i = 0; - if (ulp_type >= MAX_CNIC_ULP_TYPE) { + if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) { printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n", ulp_type); return -EINVAL; @@ -449,7 +510,7 @@ static int cnic_register_device(struct cnic_dev *dev, int ulp_type, struct cnic_local *cp = dev->cnic_priv; struct cnic_ulp_ops *ulp_ops; - if (ulp_type >= MAX_CNIC_ULP_TYPE) { + if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) { printk(KERN_ERR PFX "cnic_register_device: Bad type %d\n", ulp_type); return -EINVAL; @@ -490,7 +551,7 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) struct cnic_local *cp = dev->cnic_priv; int i = 0; - if (ulp_type >= MAX_CNIC_ULP_TYPE) { + if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) { printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n", ulp_type); return -EINVAL; @@ -606,14 +667,14 @@ static void cnic_free_dma(struct cnic_dev *dev, struct cnic_dma *dma) for (i = 0; i < dma->num_pages; i++) { if (dma->pg_arr[i]) { - pci_free_consistent(dev->pcidev, BCM_PAGE_SIZE, - dma->pg_arr[i], dma->pg_map_arr[i]); + dma_free_coherent(&dev->pcidev->dev, BCM_PAGE_SIZE, + dma->pg_arr[i], dma->pg_map_arr[i]); dma->pg_arr[i] = NULL; } } if (dma->pgtbl) { - pci_free_consistent(dev->pcidev, dma->pgtbl_size, - dma->pgtbl, dma->pgtbl_map); + dma_free_coherent(&dev->pcidev->dev, dma->pgtbl_size, + dma->pgtbl, dma->pgtbl_map); dma->pgtbl = NULL; } kfree(dma->pg_arr); @@ -635,6 +696,20 @@ static void cnic_setup_page_tbl(struct cnic_dev *dev, struct cnic_dma *dma) } } +static void cnic_setup_page_tbl_le(struct cnic_dev *dev, struct cnic_dma *dma) +{ + int i; + u32 *page_table = dma->pgtbl; + + for (i = 0; i < dma->num_pages; i++) { + /* Each entry needs to be in little endian format. */ + *page_table = dma->pg_map_arr[i] & 0xffffffff; + page_table++; + *page_table = (u32) ((u64) dma->pg_map_arr[i] >> 32); + page_table++; + } +} + static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma, int pages, int use_pg_tbl) { @@ -650,9 +725,10 @@ static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma, dma->num_pages = pages; for (i = 0; i < pages; i++) { - dma->pg_arr[i] = pci_alloc_consistent(dev->pcidev, - BCM_PAGE_SIZE, - &dma->pg_map_arr[i]); + dma->pg_arr[i] = dma_alloc_coherent(&dev->pcidev->dev, + BCM_PAGE_SIZE, + &dma->pg_map_arr[i], + GFP_ATOMIC); if (dma->pg_arr[i] == NULL) goto error; } @@ -661,8 +737,8 @@ static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma, dma->pgtbl_size = ((pages * 8) + BCM_PAGE_SIZE - 1) & ~(BCM_PAGE_SIZE - 1); - dma->pgtbl = pci_alloc_consistent(dev->pcidev, dma->pgtbl_size, - &dma->pgtbl_map); + dma->pgtbl = dma_alloc_coherent(&dev->pcidev->dev, dma->pgtbl_size, + &dma->pgtbl_map, GFP_ATOMIC); if (dma->pgtbl == NULL) goto error; @@ -675,6 +751,21 @@ error: return -ENOMEM; } +static void cnic_free_context(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int i; + + for (i = 0; i < cp->ctx_blks; i++) { + if (cp->ctx_arr[i].ctx) { + dma_free_coherent(&dev->pcidev->dev, cp->ctx_blk_size, + cp->ctx_arr[i].ctx, + cp->ctx_arr[i].mapping); + cp->ctx_arr[i].ctx = NULL; + } + } +} + static void cnic_free_resc(struct cnic_dev *dev) { struct cnic_local *cp = dev->cnic_priv; @@ -691,25 +782,18 @@ static void cnic_free_resc(struct cnic_dev *dev) } if (cp->l2_buf) { - pci_free_consistent(dev->pcidev, cp->l2_buf_size, - cp->l2_buf, cp->l2_buf_map); + dma_free_coherent(&dev->pcidev->dev, cp->l2_buf_size, + cp->l2_buf, cp->l2_buf_map); cp->l2_buf = NULL; } if (cp->l2_ring) { - pci_free_consistent(dev->pcidev, cp->l2_ring_size, - cp->l2_ring, cp->l2_ring_map); + dma_free_coherent(&dev->pcidev->dev, cp->l2_ring_size, + cp->l2_ring, cp->l2_ring_map); cp->l2_ring = NULL; } - for (i = 0; i < cp->ctx_blks; i++) { - if (cp->ctx_arr[i].ctx) { - pci_free_consistent(dev->pcidev, cp->ctx_blk_size, - cp->ctx_arr[i].ctx, - cp->ctx_arr[i].mapping); - cp->ctx_arr[i].ctx = NULL; - } - } + cnic_free_context(dev); kfree(cp->ctx_arr); cp->ctx_arr = NULL; cp->ctx_blks = 0; @@ -717,6 +801,7 @@ static void cnic_free_resc(struct cnic_dev *dev) cnic_free_dma(dev, &cp->gbl_buf_info); cnic_free_dma(dev, &cp->conn_buf_info); cnic_free_dma(dev, &cp->kwq_info); + cnic_free_dma(dev, &cp->kwq_16_data_info); cnic_free_dma(dev, &cp->kcq_info); kfree(cp->iscsi_tbl); cp->iscsi_tbl = NULL; @@ -765,8 +850,10 @@ static int cnic_alloc_context(struct cnic_dev *dev) for (i = 0; i < cp->ctx_blks; i++) { cp->ctx_arr[i].ctx = - pci_alloc_consistent(dev->pcidev, BCM_PAGE_SIZE, - &cp->ctx_arr[i].mapping); + dma_alloc_coherent(&dev->pcidev->dev, + BCM_PAGE_SIZE, + &cp->ctx_arr[i].mapping, + GFP_KERNEL); if (cp->ctx_arr[i].ctx == NULL) return -ENOMEM; } @@ -779,15 +866,17 @@ static int cnic_alloc_l2_rings(struct cnic_dev *dev, int pages) struct cnic_local *cp = dev->cnic_priv; cp->l2_ring_size = pages * BCM_PAGE_SIZE; - cp->l2_ring = pci_alloc_consistent(dev->pcidev, cp->l2_ring_size, - &cp->l2_ring_map); + cp->l2_ring = dma_alloc_coherent(&dev->pcidev->dev, cp->l2_ring_size, + &cp->l2_ring_map, + GFP_KERNEL | __GFP_COMP); if (!cp->l2_ring) return -ENOMEM; cp->l2_buf_size = (cp->l2_rx_ring_size + 1) * cp->l2_single_buf_size; cp->l2_buf_size = PAGE_ALIGN(cp->l2_buf_size); - cp->l2_buf = pci_alloc_consistent(dev->pcidev, cp->l2_buf_size, - &cp->l2_buf_map); + cp->l2_buf = dma_alloc_coherent(&dev->pcidev->dev, cp->l2_buf_size, + &cp->l2_buf_map, + GFP_KERNEL | __GFP_COMP); if (!cp->l2_buf) return -ENOMEM; @@ -808,14 +897,20 @@ static int cnic_alloc_uio(struct cnic_dev *dev) { uinfo->mem[0].size = dev->netdev->mem_end - dev->netdev->mem_start; uinfo->mem[0].memtype = UIO_MEM_PHYS; - uinfo->mem[1].addr = (unsigned long) cp->status_blk & PAGE_MASK; if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) { + uinfo->mem[1].addr = (unsigned long) cp->status_blk & PAGE_MASK; if (cp->ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE * 9; else uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE; uinfo->name = "bnx2_cnic"; + } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) { + uinfo->mem[1].addr = (unsigned long) cp->bnx2x_def_status_blk & + PAGE_MASK; + uinfo->mem[1].size = sizeof(struct host_def_status_block); + + uinfo->name = "bnx2x_cnic"; } uinfo->mem[1].memtype = UIO_MEM_LOGICAL; @@ -880,6 +975,152 @@ error: return ret; } +static int cnic_alloc_bnx2x_context(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_eth_dev *ethdev = cp->ethdev; + int ctx_blk_size = cp->ethdev->ctx_blk_size; + int total_mem, blks, i, cid_space; + + if (BNX2X_ISCSI_START_CID < ethdev->starting_cid) + return -EINVAL; + + cid_space = MAX_ISCSI_TBL_SZ + + (BNX2X_ISCSI_START_CID - ethdev->starting_cid); + + total_mem = BNX2X_CONTEXT_MEM_SIZE * cid_space; + blks = total_mem / ctx_blk_size; + if (total_mem % ctx_blk_size) + blks++; + + if (blks > cp->ethdev->ctx_tbl_len) + return -ENOMEM; + + cp->ctx_arr = kzalloc(blks * sizeof(struct cnic_ctx), GFP_KERNEL); + if (cp->ctx_arr == NULL) + return -ENOMEM; + + cp->ctx_blks = blks; + cp->ctx_blk_size = ctx_blk_size; + if (BNX2X_CHIP_IS_E1H(cp->chip_id)) + cp->ctx_align = 0; + else + cp->ctx_align = ctx_blk_size; + + cp->cids_per_blk = ctx_blk_size / BNX2X_CONTEXT_MEM_SIZE; + + for (i = 0; i < blks; i++) { + cp->ctx_arr[i].ctx = + dma_alloc_coherent(&dev->pcidev->dev, cp->ctx_blk_size, + &cp->ctx_arr[i].mapping, + GFP_KERNEL); + if (cp->ctx_arr[i].ctx == NULL) + return -ENOMEM; + + if (cp->ctx_align && cp->ctx_blk_size == ctx_blk_size) { + if (cp->ctx_arr[i].mapping & (cp->ctx_align - 1)) { + cnic_free_context(dev); + cp->ctx_blk_size += cp->ctx_align; + i = -1; + continue; + } + } + } + return 0; +} + +static int cnic_alloc_bnx2x_resc(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int i, j, n, ret, pages; + struct cnic_dma *kwq_16_dma = &cp->kwq_16_data_info; + + cp->iscsi_tbl = kzalloc(sizeof(struct cnic_iscsi) * MAX_ISCSI_TBL_SZ, + GFP_KERNEL); + if (!cp->iscsi_tbl) + goto error; + + cp->ctx_tbl = kzalloc(sizeof(struct cnic_context) * + MAX_CNIC_L5_CONTEXT, GFP_KERNEL); + if (!cp->ctx_tbl) + goto error; + + for (i = 0; i < MAX_ISCSI_TBL_SZ; i++) { + cp->ctx_tbl[i].proto.iscsi = &cp->iscsi_tbl[i]; + cp->ctx_tbl[i].ulp_proto_id = CNIC_ULP_ISCSI; + } + + pages = PAGE_ALIGN(MAX_CNIC_L5_CONTEXT * CNIC_KWQ16_DATA_SIZE) / + PAGE_SIZE; + + ret = cnic_alloc_dma(dev, kwq_16_dma, pages, 0); + if (ret) + return -ENOMEM; + + n = PAGE_SIZE / CNIC_KWQ16_DATA_SIZE; + for (i = 0, j = 0; i < MAX_ISCSI_TBL_SZ; i++) { + long off = CNIC_KWQ16_DATA_SIZE * (i % n); + + cp->ctx_tbl[i].kwqe_data = kwq_16_dma->pg_arr[j] + off; + cp->ctx_tbl[i].kwqe_data_mapping = kwq_16_dma->pg_map_arr[j] + + off; + + if ((i % n) == (n - 1)) + j++; + } + + ret = cnic_alloc_dma(dev, &cp->kcq_info, KCQ_PAGE_CNT, 0); + if (ret) + goto error; + cp->kcq = (struct kcqe **) cp->kcq_info.pg_arr; + + for (i = 0; i < KCQ_PAGE_CNT; i++) { + struct bnx2x_bd_chain_next *next = + (struct bnx2x_bd_chain_next *) + &cp->kcq[i][MAX_KCQE_CNT]; + int j = i + 1; + + if (j >= KCQ_PAGE_CNT) + j = 0; + next->addr_hi = (u64) cp->kcq_info.pg_map_arr[j] >> 32; + next->addr_lo = cp->kcq_info.pg_map_arr[j] & 0xffffffff; + } + + pages = PAGE_ALIGN(BNX2X_ISCSI_NUM_CONNECTIONS * + BNX2X_ISCSI_CONN_BUF_SIZE) / PAGE_SIZE; + ret = cnic_alloc_dma(dev, &cp->conn_buf_info, pages, 1); + if (ret) + goto error; + + pages = PAGE_ALIGN(BNX2X_ISCSI_GLB_BUF_SIZE) / PAGE_SIZE; + ret = cnic_alloc_dma(dev, &cp->gbl_buf_info, pages, 0); + if (ret) + goto error; + + ret = cnic_alloc_bnx2x_context(dev); + if (ret) + goto error; + + cp->bnx2x_status_blk = cp->status_blk; + cp->bnx2x_def_status_blk = cp->ethdev->irq_arr[1].status_blk; + + cp->l2_rx_ring_size = 15; + + ret = cnic_alloc_l2_rings(dev, 4); + if (ret) + goto error; + + ret = cnic_alloc_uio(dev); + if (ret) + goto error; + + return 0; + +error: + cnic_free_resc(dev); + return -ENOMEM; +} + static inline u32 cnic_kwq_avail(struct cnic_local *cp) { return cp->max_kwq_idx - @@ -921,6 +1162,880 @@ static int cnic_submit_bnx2_kwqes(struct cnic_dev *dev, struct kwqe *wqes[], return 0; } +static void *cnic_get_kwqe_16_data(struct cnic_local *cp, u32 l5_cid, + union l5cm_specific_data *l5_data) +{ + struct cnic_context *ctx = &cp->ctx_tbl[l5_cid]; + dma_addr_t map; + + map = ctx->kwqe_data_mapping; + l5_data->phy_address.lo = (u64) map & 0xffffffff; + l5_data->phy_address.hi = (u64) map >> 32; + return ctx->kwqe_data; +} + +static int cnic_submit_kwqe_16(struct cnic_dev *dev, u32 cmd, u32 cid, + u32 type, union l5cm_specific_data *l5_data) +{ + struct cnic_local *cp = dev->cnic_priv; + struct l5cm_spe kwqe; + struct kwqe_16 *kwq[1]; + int ret; + + kwqe.hdr.conn_and_cmd_data = + cpu_to_le32(((cmd << SPE_HDR_CMD_ID_SHIFT) | + BNX2X_HW_CID(cid, cp->func))); + kwqe.hdr.type = cpu_to_le16(type); + kwqe.hdr.reserved = 0; + kwqe.data.phy_address.lo = cpu_to_le32(l5_data->phy_address.lo); + kwqe.data.phy_address.hi = cpu_to_le32(l5_data->phy_address.hi); + + kwq[0] = (struct kwqe_16 *) &kwqe; + + spin_lock_bh(&cp->cnic_ulp_lock); + ret = cp->ethdev->drv_submit_kwqes_16(dev->netdev, kwq, 1); + spin_unlock_bh(&cp->cnic_ulp_lock); + + if (ret == 1) + return 0; + + return -EBUSY; +} + +static void cnic_reply_bnx2x_kcqes(struct cnic_dev *dev, int ulp_type, + struct kcqe *cqes[], u32 num_cqes) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_ulp_ops *ulp_ops; + + rcu_read_lock(); + ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]); + if (likely(ulp_ops)) { + ulp_ops->indicate_kcqes(cp->ulp_handle[ulp_type], + cqes, num_cqes); + } + rcu_read_unlock(); +} + +static int cnic_bnx2x_iscsi_init1(struct cnic_dev *dev, struct kwqe *kwqe) +{ + struct cnic_local *cp = dev->cnic_priv; + struct iscsi_kwqe_init1 *req1 = (struct iscsi_kwqe_init1 *) kwqe; + int func = cp->func, pages; + int hq_bds; + + cp->num_iscsi_tasks = req1->num_tasks_per_conn; + cp->num_ccells = req1->num_ccells_per_conn; + cp->task_array_size = BNX2X_ISCSI_TASK_CONTEXT_SIZE * + cp->num_iscsi_tasks; + cp->r2tq_size = cp->num_iscsi_tasks * BNX2X_ISCSI_MAX_PENDING_R2TS * + BNX2X_ISCSI_R2TQE_SIZE; + cp->hq_size = cp->num_ccells * BNX2X_ISCSI_HQ_BD_SIZE; + pages = PAGE_ALIGN(cp->hq_size) / PAGE_SIZE; + hq_bds = pages * (PAGE_SIZE / BNX2X_ISCSI_HQ_BD_SIZE); + cp->num_cqs = req1->num_cqs; + + if (!dev->max_iscsi_conn) + return 0; + + /* init Tstorm RAM */ + CNIC_WR16(dev, BAR_TSTRORM_INTMEM + TSTORM_ISCSI_RQ_SIZE_OFFSET(func), + req1->rq_num_wqes); + CNIC_WR16(dev, BAR_TSTRORM_INTMEM + TSTORM_ISCSI_PAGE_SIZE_OFFSET(func), + PAGE_SIZE); + CNIC_WR8(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), PAGE_SHIFT); + CNIC_WR16(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func), + req1->num_tasks_per_conn); + + /* init Ustorm RAM */ + CNIC_WR16(dev, BAR_USTRORM_INTMEM + + USTORM_ISCSI_RQ_BUFFER_SIZE_OFFSET(func), + req1->rq_buffer_size); + CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_PAGE_SIZE_OFFSET(func), + PAGE_SIZE); + CNIC_WR8(dev, BAR_USTRORM_INTMEM + + USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), PAGE_SHIFT); + CNIC_WR16(dev, BAR_USTRORM_INTMEM + + USTORM_ISCSI_NUM_OF_TASKS_OFFSET(func), + req1->num_tasks_per_conn); + CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_RQ_SIZE_OFFSET(func), + req1->rq_num_wqes); + CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_CQ_SIZE_OFFSET(func), + req1->cq_num_wqes); + CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_R2TQ_SIZE_OFFSET(func), + cp->num_iscsi_tasks * BNX2X_ISCSI_MAX_PENDING_R2TS); + + /* init Xstorm RAM */ + CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_PAGE_SIZE_OFFSET(func), + PAGE_SIZE); + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), PAGE_SHIFT); + CNIC_WR16(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func), + req1->num_tasks_per_conn); + CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_HQ_SIZE_OFFSET(func), + hq_bds); + CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_SQ_SIZE_OFFSET(func), + req1->num_tasks_per_conn); + CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_R2TQ_SIZE_OFFSET(func), + cp->num_iscsi_tasks * BNX2X_ISCSI_MAX_PENDING_R2TS); + + /* init Cstorm RAM */ + CNIC_WR16(dev, BAR_CSTRORM_INTMEM + CSTORM_ISCSI_PAGE_SIZE_OFFSET(func), + PAGE_SIZE); + CNIC_WR8(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), PAGE_SHIFT); + CNIC_WR16(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func), + req1->num_tasks_per_conn); + CNIC_WR16(dev, BAR_CSTRORM_INTMEM + CSTORM_ISCSI_CQ_SIZE_OFFSET(func), + req1->cq_num_wqes); + CNIC_WR16(dev, BAR_CSTRORM_INTMEM + CSTORM_ISCSI_HQ_SIZE_OFFSET(func), + hq_bds); + + return 0; +} + +static int cnic_bnx2x_iscsi_init2(struct cnic_dev *dev, struct kwqe *kwqe) +{ + struct iscsi_kwqe_init2 *req2 = (struct iscsi_kwqe_init2 *) kwqe; + struct cnic_local *cp = dev->cnic_priv; + int func = cp->func; + struct iscsi_kcqe kcqe; + struct kcqe *cqes[1]; + + memset(&kcqe, 0, sizeof(kcqe)); + if (!dev->max_iscsi_conn) { + kcqe.completion_status = + ISCSI_KCQE_COMPLETION_STATUS_ISCSI_NOT_SUPPORTED; + goto done; + } + + CNIC_WR(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_ERROR_BITMAP_OFFSET(func), req2->error_bit_map[0]); + CNIC_WR(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_ERROR_BITMAP_OFFSET(func) + 4, + req2->error_bit_map[1]); + + CNIC_WR16(dev, BAR_USTRORM_INTMEM + + USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(func), req2->max_cq_sqn); + CNIC_WR(dev, BAR_USTRORM_INTMEM + + USTORM_ISCSI_ERROR_BITMAP_OFFSET(func), req2->error_bit_map[0]); + CNIC_WR(dev, BAR_USTRORM_INTMEM + + USTORM_ISCSI_ERROR_BITMAP_OFFSET(func) + 4, + req2->error_bit_map[1]); + + CNIC_WR16(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(func), req2->max_cq_sqn); + + kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_SUCCESS; + +done: + kcqe.op_code = ISCSI_KCQE_OPCODE_INIT; + cqes[0] = (struct kcqe *) &kcqe; + cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_ISCSI, cqes, 1); + + return 0; +} + +static void cnic_free_bnx2x_conn_resc(struct cnic_dev *dev, u32 l5_cid) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_context *ctx = &cp->ctx_tbl[l5_cid]; + + if (ctx->ulp_proto_id == CNIC_ULP_ISCSI) { + struct cnic_iscsi *iscsi = ctx->proto.iscsi; + + cnic_free_dma(dev, &iscsi->hq_info); + cnic_free_dma(dev, &iscsi->r2tq_info); + cnic_free_dma(dev, &iscsi->task_array_info); + } + cnic_free_id(&cp->cid_tbl, ctx->cid); + ctx->cid = 0; +} + +static int cnic_alloc_bnx2x_conn_resc(struct cnic_dev *dev, u32 l5_cid) +{ + u32 cid; + int ret, pages; + struct cnic_local *cp = dev->cnic_priv; + struct cnic_context *ctx = &cp->ctx_tbl[l5_cid]; + struct cnic_iscsi *iscsi = ctx->proto.iscsi; + + cid = cnic_alloc_new_id(&cp->cid_tbl); + if (cid == -1) { + ret = -ENOMEM; + goto error; + } + + ctx->cid = cid; + pages = PAGE_ALIGN(cp->task_array_size) / PAGE_SIZE; + + ret = cnic_alloc_dma(dev, &iscsi->task_array_info, pages, 1); + if (ret) + goto error; + + pages = PAGE_ALIGN(cp->r2tq_size) / PAGE_SIZE; + ret = cnic_alloc_dma(dev, &iscsi->r2tq_info, pages, 1); + if (ret) + goto error; + + pages = PAGE_ALIGN(cp->hq_size) / PAGE_SIZE; + ret = cnic_alloc_dma(dev, &iscsi->hq_info, pages, 1); + if (ret) + goto error; + + return 0; + +error: + cnic_free_bnx2x_conn_resc(dev, l5_cid); + return ret; +} + +static void *cnic_get_bnx2x_ctx(struct cnic_dev *dev, u32 cid, int init, + struct regpair *ctx_addr) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_eth_dev *ethdev = cp->ethdev; + int blk = (cid - ethdev->starting_cid) / cp->cids_per_blk; + int off = (cid - ethdev->starting_cid) % cp->cids_per_blk; + unsigned long align_off = 0; + dma_addr_t ctx_map; + void *ctx; + + if (cp->ctx_align) { + unsigned long mask = cp->ctx_align - 1; + + if (cp->ctx_arr[blk].mapping & mask) + align_off = cp->ctx_align - + (cp->ctx_arr[blk].mapping & mask); + } + ctx_map = cp->ctx_arr[blk].mapping + align_off + + (off * BNX2X_CONTEXT_MEM_SIZE); + ctx = cp->ctx_arr[blk].ctx + align_off + + (off * BNX2X_CONTEXT_MEM_SIZE); + if (init) + memset(ctx, 0, BNX2X_CONTEXT_MEM_SIZE); + + ctx_addr->lo = ctx_map & 0xffffffff; + ctx_addr->hi = (u64) ctx_map >> 32; + return ctx; +} + +static int cnic_setup_bnx2x_ctx(struct cnic_dev *dev, struct kwqe *wqes[], + u32 num) +{ + struct cnic_local *cp = dev->cnic_priv; + struct iscsi_kwqe_conn_offload1 *req1 = + (struct iscsi_kwqe_conn_offload1 *) wqes[0]; + struct iscsi_kwqe_conn_offload2 *req2 = + (struct iscsi_kwqe_conn_offload2 *) wqes[1]; + struct iscsi_kwqe_conn_offload3 *req3; + struct cnic_context *ctx = &cp->ctx_tbl[req1->iscsi_conn_id]; + struct cnic_iscsi *iscsi = ctx->proto.iscsi; + u32 cid = ctx->cid; + u32 hw_cid = BNX2X_HW_CID(cid, cp->func); + struct iscsi_context *ictx; + struct regpair context_addr; + int i, j, n = 2, n_max; + + ctx->ctx_flags = 0; + if (!req2->num_additional_wqes) + return -EINVAL; + + n_max = req2->num_additional_wqes + 2; + + ictx = cnic_get_bnx2x_ctx(dev, cid, 1, &context_addr); + if (ictx == NULL) + return -ENOMEM; + + req3 = (struct iscsi_kwqe_conn_offload3 *) wqes[n++]; + + ictx->xstorm_ag_context.hq_prod = 1; + + ictx->xstorm_st_context.iscsi.first_burst_length = + ISCSI_DEF_FIRST_BURST_LEN; + ictx->xstorm_st_context.iscsi.max_send_pdu_length = + ISCSI_DEF_MAX_RECV_SEG_LEN; + ictx->xstorm_st_context.iscsi.sq_pbl_base.lo = + req1->sq_page_table_addr_lo; + ictx->xstorm_st_context.iscsi.sq_pbl_base.hi = + req1->sq_page_table_addr_hi; + ictx->xstorm_st_context.iscsi.sq_curr_pbe.lo = req2->sq_first_pte.hi; + ictx->xstorm_st_context.iscsi.sq_curr_pbe.hi = req2->sq_first_pte.lo; + ictx->xstorm_st_context.iscsi.hq_pbl_base.lo = + iscsi->hq_info.pgtbl_map & 0xffffffff; + ictx->xstorm_st_context.iscsi.hq_pbl_base.hi = + (u64) iscsi->hq_info.pgtbl_map >> 32; + ictx->xstorm_st_context.iscsi.hq_curr_pbe_base.lo = + iscsi->hq_info.pgtbl[0]; + ictx->xstorm_st_context.iscsi.hq_curr_pbe_base.hi = + iscsi->hq_info.pgtbl[1]; + ictx->xstorm_st_context.iscsi.r2tq_pbl_base.lo = + iscsi->r2tq_info.pgtbl_map & 0xffffffff; + ictx->xstorm_st_context.iscsi.r2tq_pbl_base.hi = + (u64) iscsi->r2tq_info.pgtbl_map >> 32; + ictx->xstorm_st_context.iscsi.r2tq_curr_pbe_base.lo = + iscsi->r2tq_info.pgtbl[0]; + ictx->xstorm_st_context.iscsi.r2tq_curr_pbe_base.hi = + iscsi->r2tq_info.pgtbl[1]; + ictx->xstorm_st_context.iscsi.task_pbl_base.lo = + iscsi->task_array_info.pgtbl_map & 0xffffffff; + ictx->xstorm_st_context.iscsi.task_pbl_base.hi = + (u64) iscsi->task_array_info.pgtbl_map >> 32; + ictx->xstorm_st_context.iscsi.task_pbl_cache_idx = + BNX2X_ISCSI_PBL_NOT_CACHED; + ictx->xstorm_st_context.iscsi.flags.flags |= + XSTORM_ISCSI_CONTEXT_FLAGS_B_IMMEDIATE_DATA; + ictx->xstorm_st_context.iscsi.flags.flags |= + XSTORM_ISCSI_CONTEXT_FLAGS_B_INITIAL_R2T; + + ictx->tstorm_st_context.iscsi.hdr_bytes_2_fetch = ISCSI_HEADER_SIZE; + /* TSTORM requires the base address of RQ DB & not PTE */ + ictx->tstorm_st_context.iscsi.rq_db_phy_addr.lo = + req2->rq_page_table_addr_lo & PAGE_MASK; + ictx->tstorm_st_context.iscsi.rq_db_phy_addr.hi = + req2->rq_page_table_addr_hi; + ictx->tstorm_st_context.iscsi.iscsi_conn_id = req1->iscsi_conn_id; + ictx->tstorm_st_context.tcp.cwnd = 0x5A8; + ictx->tstorm_st_context.tcp.flags2 |= + TSTORM_TCP_ST_CONTEXT_SECTION_DA_EN; + + ictx->timers_context.flags |= ISCSI_TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG; + + ictx->ustorm_st_context.ring.rq.pbl_base.lo = + req2->rq_page_table_addr_lo; + ictx->ustorm_st_context.ring.rq.pbl_base.hi = + req2->rq_page_table_addr_hi; + ictx->ustorm_st_context.ring.rq.curr_pbe.lo = req3->qp_first_pte[0].hi; + ictx->ustorm_st_context.ring.rq.curr_pbe.hi = req3->qp_first_pte[0].lo; + ictx->ustorm_st_context.ring.r2tq.pbl_base.lo = + iscsi->r2tq_info.pgtbl_map & 0xffffffff; + ictx->ustorm_st_context.ring.r2tq.pbl_base.hi = + (u64) iscsi->r2tq_info.pgtbl_map >> 32; + ictx->ustorm_st_context.ring.r2tq.curr_pbe.lo = + iscsi->r2tq_info.pgtbl[0]; + ictx->ustorm_st_context.ring.r2tq.curr_pbe.hi = + iscsi->r2tq_info.pgtbl[1]; + ictx->ustorm_st_context.ring.cq_pbl_base.lo = + req1->cq_page_table_addr_lo; + ictx->ustorm_st_context.ring.cq_pbl_base.hi = + req1->cq_page_table_addr_hi; + ictx->ustorm_st_context.ring.cq[0].cq_sn = ISCSI_INITIAL_SN; + ictx->ustorm_st_context.ring.cq[0].curr_pbe.lo = req2->cq_first_pte.hi; + ictx->ustorm_st_context.ring.cq[0].curr_pbe.hi = req2->cq_first_pte.lo; + ictx->ustorm_st_context.task_pbe_cache_index = + BNX2X_ISCSI_PBL_NOT_CACHED; + ictx->ustorm_st_context.task_pdu_cache_index = + BNX2X_ISCSI_PDU_HEADER_NOT_CACHED; + + for (i = 1, j = 1; i < cp->num_cqs; i++, j++) { + if (j == 3) { + if (n >= n_max) + break; + req3 = (struct iscsi_kwqe_conn_offload3 *) wqes[n++]; + j = 0; + } + ictx->ustorm_st_context.ring.cq[i].cq_sn = ISCSI_INITIAL_SN; + ictx->ustorm_st_context.ring.cq[i].curr_pbe.lo = + req3->qp_first_pte[j].hi; + ictx->ustorm_st_context.ring.cq[i].curr_pbe.hi = + req3->qp_first_pte[j].lo; + } + + ictx->ustorm_st_context.task_pbl_base.lo = + iscsi->task_array_info.pgtbl_map & 0xffffffff; + ictx->ustorm_st_context.task_pbl_base.hi = + (u64) iscsi->task_array_info.pgtbl_map >> 32; + ictx->ustorm_st_context.tce_phy_addr.lo = + iscsi->task_array_info.pgtbl[0]; + ictx->ustorm_st_context.tce_phy_addr.hi = + iscsi->task_array_info.pgtbl[1]; + ictx->ustorm_st_context.iscsi_conn_id = req1->iscsi_conn_id; + ictx->ustorm_st_context.num_cqs = cp->num_cqs; + ictx->ustorm_st_context.negotiated_rx |= ISCSI_DEF_MAX_RECV_SEG_LEN; + ictx->ustorm_st_context.negotiated_rx_and_flags |= + ISCSI_DEF_MAX_BURST_LEN; + ictx->ustorm_st_context.negotiated_rx |= + ISCSI_DEFAULT_MAX_OUTSTANDING_R2T << + USTORM_ISCSI_ST_CONTEXT_MAX_OUTSTANDING_R2TS_SHIFT; + + ictx->cstorm_st_context.hq_pbl_base.lo = + iscsi->hq_info.pgtbl_map & 0xffffffff; + ictx->cstorm_st_context.hq_pbl_base.hi = + (u64) iscsi->hq_info.pgtbl_map >> 32; + ictx->cstorm_st_context.hq_curr_pbe.lo = iscsi->hq_info.pgtbl[0]; + ictx->cstorm_st_context.hq_curr_pbe.hi = iscsi->hq_info.pgtbl[1]; + ictx->cstorm_st_context.task_pbl_base.lo = + iscsi->task_array_info.pgtbl_map & 0xffffffff; + ictx->cstorm_st_context.task_pbl_base.hi = + (u64) iscsi->task_array_info.pgtbl_map >> 32; + /* CSTORM and USTORM initialization is different, CSTORM requires + * CQ DB base & not PTE addr */ + ictx->cstorm_st_context.cq_db_base.lo = + req1->cq_page_table_addr_lo & PAGE_MASK; + ictx->cstorm_st_context.cq_db_base.hi = req1->cq_page_table_addr_hi; + ictx->cstorm_st_context.iscsi_conn_id = req1->iscsi_conn_id; + ictx->cstorm_st_context.cq_proc_en_bit_map = (1 << cp->num_cqs) - 1; + for (i = 0; i < cp->num_cqs; i++) { + ictx->cstorm_st_context.cq_c_prod_sqn_arr.sqn[i] = + ISCSI_INITIAL_SN; + ictx->cstorm_st_context.cq_c_sqn_2_notify_arr.sqn[i] = + ISCSI_INITIAL_SN; + } + + ictx->xstorm_ag_context.cdu_reserved = + CDU_RSRVD_VALUE_TYPE_A(hw_cid, CDU_REGION_NUMBER_XCM_AG, + ISCSI_CONNECTION_TYPE); + ictx->ustorm_ag_context.cdu_usage = + CDU_RSRVD_VALUE_TYPE_A(hw_cid, CDU_REGION_NUMBER_UCM_AG, + ISCSI_CONNECTION_TYPE); + return 0; + +} + +static int cnic_bnx2x_iscsi_ofld1(struct cnic_dev *dev, struct kwqe *wqes[], + u32 num, int *work) +{ + struct iscsi_kwqe_conn_offload1 *req1; + struct iscsi_kwqe_conn_offload2 *req2; + struct cnic_local *cp = dev->cnic_priv; + struct iscsi_kcqe kcqe; + struct kcqe *cqes[1]; + u32 l5_cid; + int ret; + + if (num < 2) { + *work = num; + return -EINVAL; + } + + req1 = (struct iscsi_kwqe_conn_offload1 *) wqes[0]; + req2 = (struct iscsi_kwqe_conn_offload2 *) wqes[1]; + if ((num - 2) < req2->num_additional_wqes) { + *work = num; + return -EINVAL; + } + *work = 2 + req2->num_additional_wqes;; + + l5_cid = req1->iscsi_conn_id; + if (l5_cid >= MAX_ISCSI_TBL_SZ) + return -EINVAL; + + memset(&kcqe, 0, sizeof(kcqe)); + kcqe.op_code = ISCSI_KCQE_OPCODE_OFFLOAD_CONN; + kcqe.iscsi_conn_id = l5_cid; + kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE; + + if (atomic_inc_return(&cp->iscsi_conn) > dev->max_iscsi_conn) { + atomic_dec(&cp->iscsi_conn); + ret = 0; + goto done; + } + ret = cnic_alloc_bnx2x_conn_resc(dev, l5_cid); + if (ret) { + atomic_dec(&cp->iscsi_conn); + ret = 0; + goto done; + } + ret = cnic_setup_bnx2x_ctx(dev, wqes, num); + if (ret < 0) { + cnic_free_bnx2x_conn_resc(dev, l5_cid); + atomic_dec(&cp->iscsi_conn); + goto done; + } + + kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_SUCCESS; + kcqe.iscsi_conn_context_id = BNX2X_HW_CID(cp->ctx_tbl[l5_cid].cid, + cp->func); + +done: + cqes[0] = (struct kcqe *) &kcqe; + cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_ISCSI, cqes, 1); + return ret; +} + + +static int cnic_bnx2x_iscsi_update(struct cnic_dev *dev, struct kwqe *kwqe) +{ + struct cnic_local *cp = dev->cnic_priv; + struct iscsi_kwqe_conn_update *req = + (struct iscsi_kwqe_conn_update *) kwqe; + void *data; + union l5cm_specific_data l5_data; + u32 l5_cid, cid = BNX2X_SW_CID(req->context_id); + int ret; + + if (cnic_get_l5_cid(cp, cid, &l5_cid) != 0) + return -EINVAL; + + data = cnic_get_kwqe_16_data(cp, l5_cid, &l5_data); + if (!data) + return -ENOMEM; + + memcpy(data, kwqe, sizeof(struct kwqe)); + + ret = cnic_submit_kwqe_16(dev, ISCSI_RAMROD_CMD_ID_UPDATE_CONN, + req->context_id, ISCSI_CONNECTION_TYPE, &l5_data); + return ret; +} + +static int cnic_bnx2x_iscsi_destroy(struct cnic_dev *dev, struct kwqe *kwqe) +{ + struct cnic_local *cp = dev->cnic_priv; + struct iscsi_kwqe_conn_destroy *req = + (struct iscsi_kwqe_conn_destroy *) kwqe; + union l5cm_specific_data l5_data; + u32 l5_cid = req->reserved0; + struct cnic_context *ctx = &cp->ctx_tbl[l5_cid]; + int ret = 0; + struct iscsi_kcqe kcqe; + struct kcqe *cqes[1]; + + if (!(ctx->ctx_flags & CTX_FL_OFFLD_START)) + goto skip_cfc_delete; + + while (!time_after(jiffies, ctx->timestamp + (2 * HZ))) + msleep(250); + + init_waitqueue_head(&ctx->waitq); + ctx->wait_cond = 0; + memset(&l5_data, 0, sizeof(l5_data)); + ret = cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_ETH_CFC_DEL, + req->context_id, + ETH_CONNECTION_TYPE | + (1 << SPE_HDR_COMMON_RAMROD_SHIFT), + &l5_data); + if (ret == 0) + wait_event(ctx->waitq, ctx->wait_cond); + +skip_cfc_delete: + cnic_free_bnx2x_conn_resc(dev, l5_cid); + + atomic_dec(&cp->iscsi_conn); + + memset(&kcqe, 0, sizeof(kcqe)); + kcqe.op_code = ISCSI_KCQE_OPCODE_DESTROY_CONN; + kcqe.iscsi_conn_id = l5_cid; + kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_SUCCESS; + kcqe.iscsi_conn_context_id = req->context_id; + + cqes[0] = (struct kcqe *) &kcqe; + cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_ISCSI, cqes, 1); + + return ret; +} + +static void cnic_init_storm_conn_bufs(struct cnic_dev *dev, + struct l4_kwq_connect_req1 *kwqe1, + struct l4_kwq_connect_req3 *kwqe3, + struct l5cm_active_conn_buffer *conn_buf) +{ + struct l5cm_conn_addr_params *conn_addr = &conn_buf->conn_addr_buf; + struct l5cm_xstorm_conn_buffer *xstorm_buf = + &conn_buf->xstorm_conn_buffer; + struct l5cm_tstorm_conn_buffer *tstorm_buf = + &conn_buf->tstorm_conn_buffer; + struct regpair context_addr; + u32 cid = BNX2X_SW_CID(kwqe1->cid); + struct in6_addr src_ip, dst_ip; + int i; + u32 *addrp; + + addrp = (u32 *) &conn_addr->local_ip_addr; + for (i = 0; i < 4; i++, addrp++) + src_ip.in6_u.u6_addr32[i] = cpu_to_be32(*addrp); + + addrp = (u32 *) &conn_addr->remote_ip_addr; + for (i = 0; i < 4; i++, addrp++) + dst_ip.in6_u.u6_addr32[i] = cpu_to_be32(*addrp); + + cnic_get_bnx2x_ctx(dev, cid, 0, &context_addr); + + xstorm_buf->context_addr.hi = context_addr.hi; + xstorm_buf->context_addr.lo = context_addr.lo; + xstorm_buf->mss = 0xffff; + xstorm_buf->rcv_buf = kwqe3->rcv_buf; + if (kwqe1->tcp_flags & L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE) + xstorm_buf->params |= L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE; + xstorm_buf->pseudo_header_checksum = + swab16(~csum_ipv6_magic(&src_ip, &dst_ip, 0, IPPROTO_TCP, 0)); + + if (!(kwqe1->tcp_flags & L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK)) + tstorm_buf->params |= + L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE; + if (kwqe3->ka_timeout) { + tstorm_buf->ka_enable = 1; + tstorm_buf->ka_timeout = kwqe3->ka_timeout; + tstorm_buf->ka_interval = kwqe3->ka_interval; + tstorm_buf->ka_max_probe_count = kwqe3->ka_max_probe_count; + } + tstorm_buf->rcv_buf = kwqe3->rcv_buf; + tstorm_buf->snd_buf = kwqe3->snd_buf; + tstorm_buf->max_rt_time = 0xffffffff; +} + +static void cnic_init_bnx2x_mac(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int func = CNIC_FUNC(cp); + u8 *mac = dev->mac_addr; + + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_LOCAL_MAC_ADDR0_OFFSET(func), mac[0]); + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_LOCAL_MAC_ADDR1_OFFSET(func), mac[1]); + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_LOCAL_MAC_ADDR2_OFFSET(func), mac[2]); + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_LOCAL_MAC_ADDR3_OFFSET(func), mac[3]); + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_LOCAL_MAC_ADDR4_OFFSET(func), mac[4]); + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_LOCAL_MAC_ADDR5_OFFSET(func), mac[5]); + + CNIC_WR8(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(func), mac[5]); + CNIC_WR8(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(func) + 1, + mac[4]); + CNIC_WR8(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(func), mac[3]); + CNIC_WR8(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(func) + 1, + mac[2]); + CNIC_WR8(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(func) + 2, + mac[1]); + CNIC_WR8(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(func) + 3, + mac[0]); +} + +static void cnic_bnx2x_set_tcp_timestamp(struct cnic_dev *dev, int tcp_ts) +{ + struct cnic_local *cp = dev->cnic_priv; + u8 xstorm_flags = XSTORM_L5CM_TCP_FLAGS_WND_SCL_EN; + u16 tstorm_flags = 0; + + if (tcp_ts) { + xstorm_flags |= XSTORM_L5CM_TCP_FLAGS_TS_ENABLED; + tstorm_flags |= TSTORM_L5CM_TCP_FLAGS_TS_ENABLED; + } + + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(cp->func), xstorm_flags); + + CNIC_WR16(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(cp->func), tstorm_flags); +} + +static int cnic_bnx2x_connect(struct cnic_dev *dev, struct kwqe *wqes[], + u32 num, int *work) +{ + struct cnic_local *cp = dev->cnic_priv; + struct l4_kwq_connect_req1 *kwqe1 = + (struct l4_kwq_connect_req1 *) wqes[0]; + struct l4_kwq_connect_req3 *kwqe3; + struct l5cm_active_conn_buffer *conn_buf; + struct l5cm_conn_addr_params *conn_addr; + union l5cm_specific_data l5_data; + u32 l5_cid = kwqe1->pg_cid; + struct cnic_sock *csk = &cp->csk_tbl[l5_cid]; + struct cnic_context *ctx = &cp->ctx_tbl[l5_cid]; + int ret; + + if (num < 2) { + *work = num; + return -EINVAL; + } + + if (kwqe1->conn_flags & L4_KWQ_CONNECT_REQ1_IP_V6) + *work = 3; + else + *work = 2; + + if (num < *work) { + *work = num; + return -EINVAL; + } + + if (sizeof(*conn_buf) > CNIC_KWQ16_DATA_SIZE) { + printk(KERN_ERR PFX "%s: conn_buf size too big\n", + dev->netdev->name); + return -ENOMEM; + } + conn_buf = cnic_get_kwqe_16_data(cp, l5_cid, &l5_data); + if (!conn_buf) + return -ENOMEM; + + memset(conn_buf, 0, sizeof(*conn_buf)); + + conn_addr = &conn_buf->conn_addr_buf; + conn_addr->remote_addr_0 = csk->ha[0]; + conn_addr->remote_addr_1 = csk->ha[1]; + conn_addr->remote_addr_2 = csk->ha[2]; + conn_addr->remote_addr_3 = csk->ha[3]; + conn_addr->remote_addr_4 = csk->ha[4]; + conn_addr->remote_addr_5 = csk->ha[5]; + + if (kwqe1->conn_flags & L4_KWQ_CONNECT_REQ1_IP_V6) { + struct l4_kwq_connect_req2 *kwqe2 = + (struct l4_kwq_connect_req2 *) wqes[1]; + + conn_addr->local_ip_addr.ip_addr_hi_hi = kwqe2->src_ip_v6_4; + conn_addr->local_ip_addr.ip_addr_hi_lo = kwqe2->src_ip_v6_3; + conn_addr->local_ip_addr.ip_addr_lo_hi = kwqe2->src_ip_v6_2; + + conn_addr->remote_ip_addr.ip_addr_hi_hi = kwqe2->dst_ip_v6_4; + conn_addr->remote_ip_addr.ip_addr_hi_lo = kwqe2->dst_ip_v6_3; + conn_addr->remote_ip_addr.ip_addr_lo_hi = kwqe2->dst_ip_v6_2; + conn_addr->params |= L5CM_CONN_ADDR_PARAMS_IP_VERSION; + } + kwqe3 = (struct l4_kwq_connect_req3 *) wqes[*work - 1]; + + conn_addr->local_ip_addr.ip_addr_lo_lo = kwqe1->src_ip; + conn_addr->remote_ip_addr.ip_addr_lo_lo = kwqe1->dst_ip; + conn_addr->local_tcp_port = kwqe1->src_port; + conn_addr->remote_tcp_port = kwqe1->dst_port; + + conn_addr->pmtu = kwqe3->pmtu; + cnic_init_storm_conn_bufs(dev, kwqe1, kwqe3, conn_buf); + + CNIC_WR16(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_LOCAL_VLAN_OFFSET(cp->func), csk->vlan_id); + + cnic_bnx2x_set_tcp_timestamp(dev, + kwqe1->tcp_flags & L4_KWQ_CONNECT_REQ1_TIME_STAMP); + + ret = cnic_submit_kwqe_16(dev, L5CM_RAMROD_CMD_ID_TCP_CONNECT, + kwqe1->cid, ISCSI_CONNECTION_TYPE, &l5_data); + if (!ret) + ctx->ctx_flags |= CTX_FL_OFFLD_START; + + return ret; +} + +static int cnic_bnx2x_close(struct cnic_dev *dev, struct kwqe *kwqe) +{ + struct l4_kwq_close_req *req = (struct l4_kwq_close_req *) kwqe; + union l5cm_specific_data l5_data; + int ret; + + memset(&l5_data, 0, sizeof(l5_data)); + ret = cnic_submit_kwqe_16(dev, L5CM_RAMROD_CMD_ID_CLOSE, + req->cid, ISCSI_CONNECTION_TYPE, &l5_data); + return ret; +} + +static int cnic_bnx2x_reset(struct cnic_dev *dev, struct kwqe *kwqe) +{ + struct l4_kwq_reset_req *req = (struct l4_kwq_reset_req *) kwqe; + union l5cm_specific_data l5_data; + int ret; + + memset(&l5_data, 0, sizeof(l5_data)); + ret = cnic_submit_kwqe_16(dev, L5CM_RAMROD_CMD_ID_ABORT, + req->cid, ISCSI_CONNECTION_TYPE, &l5_data); + return ret; +} +static int cnic_bnx2x_offload_pg(struct cnic_dev *dev, struct kwqe *kwqe) +{ + struct l4_kwq_offload_pg *req = (struct l4_kwq_offload_pg *) kwqe; + struct l4_kcq kcqe; + struct kcqe *cqes[1]; + + memset(&kcqe, 0, sizeof(kcqe)); + kcqe.pg_host_opaque = req->host_opaque; + kcqe.pg_cid = req->host_opaque; + kcqe.op_code = L4_KCQE_OPCODE_VALUE_OFFLOAD_PG; + cqes[0] = (struct kcqe *) &kcqe; + cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_L4, cqes, 1); + return 0; +} + +static int cnic_bnx2x_update_pg(struct cnic_dev *dev, struct kwqe *kwqe) +{ + struct l4_kwq_update_pg *req = (struct l4_kwq_update_pg *) kwqe; + struct l4_kcq kcqe; + struct kcqe *cqes[1]; + + memset(&kcqe, 0, sizeof(kcqe)); + kcqe.pg_host_opaque = req->pg_host_opaque; + kcqe.pg_cid = req->pg_cid; + kcqe.op_code = L4_KCQE_OPCODE_VALUE_UPDATE_PG; + cqes[0] = (struct kcqe *) &kcqe; + cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_L4, cqes, 1); + return 0; +} + +static int cnic_submit_bnx2x_kwqes(struct cnic_dev *dev, struct kwqe *wqes[], + u32 num_wqes) +{ + int i, work, ret; + u32 opcode; + struct kwqe *kwqe; + + if (!test_bit(CNIC_F_CNIC_UP, &dev->flags)) + return -EAGAIN; /* bnx2 is down */ + + for (i = 0; i < num_wqes; ) { + kwqe = wqes[i]; + opcode = KWQE_OPCODE(kwqe->kwqe_op_flag); + work = 1; + + switch (opcode) { + case ISCSI_KWQE_OPCODE_INIT1: + ret = cnic_bnx2x_iscsi_init1(dev, kwqe); + break; + case ISCSI_KWQE_OPCODE_INIT2: + ret = cnic_bnx2x_iscsi_init2(dev, kwqe); + break; + case ISCSI_KWQE_OPCODE_OFFLOAD_CONN1: + ret = cnic_bnx2x_iscsi_ofld1(dev, &wqes[i], + num_wqes - i, &work); + break; + case ISCSI_KWQE_OPCODE_UPDATE_CONN: + ret = cnic_bnx2x_iscsi_update(dev, kwqe); + break; + case ISCSI_KWQE_OPCODE_DESTROY_CONN: + ret = cnic_bnx2x_iscsi_destroy(dev, kwqe); + break; + case L4_KWQE_OPCODE_VALUE_CONNECT1: + ret = cnic_bnx2x_connect(dev, &wqes[i], num_wqes - i, + &work); + break; + case L4_KWQE_OPCODE_VALUE_CLOSE: + ret = cnic_bnx2x_close(dev, kwqe); + break; + case L4_KWQE_OPCODE_VALUE_RESET: + ret = cnic_bnx2x_reset(dev, kwqe); + break; + case L4_KWQE_OPCODE_VALUE_OFFLOAD_PG: + ret = cnic_bnx2x_offload_pg(dev, kwqe); + break; + case L4_KWQE_OPCODE_VALUE_UPDATE_PG: + ret = cnic_bnx2x_update_pg(dev, kwqe); + break; + case L4_KWQE_OPCODE_VALUE_UPLOAD_PG: + ret = 0; + break; + default: + ret = 0; + printk(KERN_ERR PFX "%s: Unknown type of KWQE(0x%x)\n", + dev->netdev->name, opcode); + break; + } + if (ret < 0) + printk(KERN_ERR PFX "%s: KWQE(0x%x) failed\n", + dev->netdev->name, opcode); + i += work; + } + return 0; +} + static void service_kcqes(struct cnic_dev *dev, int num_cqes) { struct cnic_local *cp = dev->cnic_priv; @@ -987,6 +2102,22 @@ static u16 cnic_bnx2_hw_idx(u16 idx) return idx; } +static u16 cnic_bnx2x_next_idx(u16 idx) +{ + idx++; + if ((idx & MAX_KCQE_CNT) == MAX_KCQE_CNT) + idx++; + + return idx; +} + +static u16 cnic_bnx2x_hw_idx(u16 idx) +{ + if ((idx & MAX_KCQE_CNT) == MAX_KCQE_CNT) + idx++; + return idx; +} + static int cnic_get_kcqes(struct cnic_dev *dev, u16 hw_prod, u16 *sw_prod) { struct cnic_local *cp = dev->cnic_priv; @@ -1012,7 +2143,7 @@ static int cnic_get_kcqes(struct cnic_dev *dev, u16 hw_prod, u16 *sw_prod) return last_cnt; } -static void cnic_chk_bnx2_pkt_rings(struct cnic_local *cp) +static void cnic_chk_pkt_rings(struct cnic_local *cp) { u16 rx_cons = *cp->rx_cons_ptr; u16 tx_cons = *cp->tx_cons_ptr; @@ -1020,6 +2151,7 @@ static void cnic_chk_bnx2_pkt_rings(struct cnic_local *cp) if (cp->tx_cons != tx_cons || cp->rx_cons != rx_cons) { cp->tx_cons = tx_cons; cp->rx_cons = rx_cons; + uio_event_notify(cp->cnic_uinfo); } } @@ -1062,7 +2194,7 @@ done: cp->kcq_prod_idx = sw_prod; - cnic_chk_bnx2_pkt_rings(cp); + cnic_chk_pkt_rings(cp); return status_idx; } @@ -1100,7 +2232,7 @@ done: CNIC_WR16(dev, cp->kcq_io_addr, sw_prod); cp->kcq_prod_idx = sw_prod; - cnic_chk_bnx2_pkt_rings(cp); + cnic_chk_pkt_rings(cp); cp->last_status_idx = status_idx; CNIC_WR(dev, BNX2_PCICFG_INT_ACK_CMD, cp->int_num | @@ -1125,6 +2257,91 @@ static irqreturn_t cnic_irq(int irq, void *dev_instance) return IRQ_HANDLED; } +static inline void cnic_ack_bnx2x_int(struct cnic_dev *dev, u8 id, u8 storm, + u16 index, u8 op, u8 update) +{ + struct cnic_local *cp = dev->cnic_priv; + u32 hc_addr = (HC_REG_COMMAND_REG + CNIC_PORT(cp) * 32 + + COMMAND_REG_INT_ACK); + struct igu_ack_register igu_ack; + + igu_ack.status_block_index = index; + igu_ack.sb_id_and_flags = + ((id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) | + (storm << IGU_ACK_REGISTER_STORM_ID_SHIFT) | + (update << IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT) | + (op << IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT)); + + CNIC_WR(dev, hc_addr, (*(u32 *)&igu_ack)); +} + +static void cnic_ack_bnx2x_msix(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + + cnic_ack_bnx2x_int(dev, cp->status_blk_num, CSTORM_ID, 0, + IGU_INT_DISABLE, 0); +} + +static void cnic_service_bnx2x_bh(unsigned long data) +{ + struct cnic_dev *dev = (struct cnic_dev *) data; + struct cnic_local *cp = dev->cnic_priv; + u16 hw_prod, sw_prod; + struct cstorm_status_block_c *sblk = + &cp->bnx2x_status_blk->c_status_block; + u32 status_idx = sblk->status_block_index; + int kcqe_cnt; + + if (unlikely(!test_bit(CNIC_F_CNIC_UP, &dev->flags))) + return; + + hw_prod = sblk->index_values[HC_INDEX_C_ISCSI_EQ_CONS]; + hw_prod = cp->hw_idx(hw_prod); + sw_prod = cp->kcq_prod_idx; + while (sw_prod != hw_prod) { + kcqe_cnt = cnic_get_kcqes(dev, hw_prod, &sw_prod); + if (kcqe_cnt == 0) + goto done; + + service_kcqes(dev, kcqe_cnt); + + /* Tell compiler that sblk fields can change. */ + barrier(); + if (status_idx == sblk->status_block_index) + break; + + status_idx = sblk->status_block_index; + hw_prod = sblk->index_values[HC_INDEX_C_ISCSI_EQ_CONS]; + hw_prod = cp->hw_idx(hw_prod); + } + +done: + CNIC_WR16(dev, cp->kcq_io_addr, sw_prod + MAX_KCQ_IDX); + cnic_ack_bnx2x_int(dev, cp->status_blk_num, CSTORM_ID, + status_idx, IGU_INT_ENABLE, 1); + + cp->kcq_prod_idx = sw_prod; + return; +} + +static int cnic_service_bnx2x(void *data, void *status_blk) +{ + struct cnic_dev *dev = data; + struct cnic_local *cp = dev->cnic_priv; + u16 prod = cp->kcq_prod_idx & MAX_KCQ_IDX; + + prefetch(cp->status_blk); + prefetch(&cp->kcq[KCQ_PG(prod)][KCQ_IDX(prod)]); + + if (likely(test_bit(CNIC_F_CNIC_UP, &dev->flags))) + tasklet_schedule(&cp->cnic_irq_task); + + cnic_chk_pkt_rings(cp); + + return 0; +} + static void cnic_ulp_stop(struct cnic_dev *dev) { struct cnic_local *cp = dev->cnic_priv; @@ -1197,6 +2414,19 @@ static int cnic_ctl(void *data, struct cnic_ctl_info *info) cnic_put(dev); break; + case CNIC_CTL_COMPLETION_CMD: { + u32 cid = BNX2X_SW_CID(info->data.comp.cid); + u32 l5_cid; + struct cnic_local *cp = dev->cnic_priv; + + if (cnic_get_l5_cid(cp, cid, &l5_cid) == 0) { + struct cnic_context *ctx = &cp->ctx_tbl[l5_cid]; + + ctx->wait_cond = 1; + wake_up(&ctx->waitq); + } + break; + } default: return -EINVAL; } @@ -1872,6 +3102,8 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe) /* fall through */ case L4_KCQE_OPCODE_VALUE_CLOSE_COMP: case L4_KCQE_OPCODE_VALUE_RESET_COMP: + case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE: + case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD: cp->close_conn(csk, opcode); break; @@ -1957,6 +3189,76 @@ static int cnic_cm_init_bnx2_hw(struct cnic_dev *dev) return 0; } +static void cnic_close_bnx2x_conn(struct cnic_sock *csk, u32 opcode) +{ + struct cnic_dev *dev = csk->dev; + struct cnic_local *cp = dev->cnic_priv; + struct cnic_context *ctx = &cp->ctx_tbl[csk->l5_cid]; + union l5cm_specific_data l5_data; + u32 cmd = 0; + int close_complete = 0; + + switch (opcode) { + case L4_KCQE_OPCODE_VALUE_RESET_RECEIVED: + case L4_KCQE_OPCODE_VALUE_CLOSE_COMP: + case L4_KCQE_OPCODE_VALUE_RESET_COMP: + if (cnic_ready_to_close(csk, opcode)) + cmd = L5CM_RAMROD_CMD_ID_SEARCHER_DELETE; + break; + case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE: + cmd = L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD; + break; + case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD: + close_complete = 1; + break; + } + if (cmd) { + memset(&l5_data, 0, sizeof(l5_data)); + + cnic_submit_kwqe_16(dev, cmd, csk->cid, ISCSI_CONNECTION_TYPE, + &l5_data); + } else if (close_complete) { + ctx->timestamp = jiffies; + cnic_close_conn(csk); + cnic_cm_upcall(cp, csk, csk->state); + } +} + +static void cnic_cm_stop_bnx2x_hw(struct cnic_dev *dev) +{ +} + +static int cnic_cm_init_bnx2x_hw(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int func = CNIC_FUNC(cp); + + cnic_init_bnx2x_mac(dev); + cnic_bnx2x_set_tcp_timestamp(dev, 1); + + CNIC_WR16(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_LOCAL_VLAN_OFFSET(func), 0); + + CNIC_WR(dev, BAR_XSTRORM_INTMEM + + XSTORM_TCP_GLOBAL_DEL_ACK_COUNTER_ENABLED_OFFSET(func), 1); + CNIC_WR(dev, BAR_XSTRORM_INTMEM + + XSTORM_TCP_GLOBAL_DEL_ACK_COUNTER_MAX_COUNT_OFFSET(func), + DEF_MAX_DA_COUNT); + + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_TCP_VARS_TTL_OFFSET(func), DEF_TTL); + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_TCP_VARS_TOS_OFFSET(func), DEF_TOS); + CNIC_WR8(dev, BAR_XSTRORM_INTMEM + + XSTORM_ISCSI_TCP_VARS_ADV_WND_SCL_OFFSET(func), 2); + CNIC_WR(dev, BAR_XSTRORM_INTMEM + + XSTORM_TCP_TX_SWS_TIMER_VAL_OFFSET(func), DEF_SWS_TIMER); + + CNIC_WR(dev, BAR_TSTRORM_INTMEM + TSTORM_TCP_MAX_CWND_OFFSET(func), + DEF_MAX_CWND); + return 0; +} + static int cnic_cm_open(struct cnic_dev *dev) { struct cnic_local *cp = dev->cnic_priv; @@ -2091,7 +3393,7 @@ static int cnic_init_bnx2_irq(struct cnic_dev *dev) cp->bnx2_status_blk = cp->status_blk; cp->last_status_idx = cp->bnx2_status_blk->status_idx; - tasklet_init(&cp->cnic_irq_task, &cnic_service_bnx2_msix, + tasklet_init(&cp->cnic_irq_task, cnic_service_bnx2_msix, (unsigned long) dev); err = request_irq(ethdev->irq_arr[0].vector, cnic_irq, 0, "cnic", dev); @@ -2464,6 +3766,426 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev) return 0; } +static void cnic_setup_bnx2x_context(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_eth_dev *ethdev = cp->ethdev; + u32 start_offset = ethdev->ctx_tbl_offset; + int i; + + for (i = 0; i < cp->ctx_blks; i++) { + struct cnic_ctx *ctx = &cp->ctx_arr[i]; + dma_addr_t map = ctx->mapping; + + if (cp->ctx_align) { + unsigned long mask = cp->ctx_align - 1; + + map = (map + mask) & ~mask; + } + + cnic_ctx_tbl_wr(dev, start_offset + i, map); + } +} + +static int cnic_init_bnx2x_irq(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_eth_dev *ethdev = cp->ethdev; + int err = 0; + + tasklet_init(&cp->cnic_irq_task, cnic_service_bnx2x_bh, + (unsigned long) dev); + if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) { + err = request_irq(ethdev->irq_arr[0].vector, cnic_irq, 0, + "cnic", dev); + if (err) + tasklet_disable(&cp->cnic_irq_task); + } + return err; +} + +static void cnic_enable_bnx2x_int(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + u8 sb_id = cp->status_blk_num; + int port = CNIC_PORT(cp); + + CNIC_WR8(dev, BAR_CSTRORM_INTMEM + + CSTORM_SB_HC_TIMEOUT_C_OFFSET(port, sb_id, + HC_INDEX_C_ISCSI_EQ_CONS), + 64 / 12); + CNIC_WR16(dev, BAR_CSTRORM_INTMEM + + CSTORM_SB_HC_DISABLE_C_OFFSET(port, sb_id, + HC_INDEX_C_ISCSI_EQ_CONS), 0); +} + +static void cnic_disable_bnx2x_int_sync(struct cnic_dev *dev) +{ +} + +static void cnic_init_bnx2x_tx_ring(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + union eth_tx_bd_types *txbd = (union eth_tx_bd_types *) cp->l2_ring; + struct eth_context *context; + struct regpair context_addr; + dma_addr_t buf_map; + int func = CNIC_FUNC(cp); + int port = CNIC_PORT(cp); + int i; + int cli = BNX2X_ISCSI_CL_ID(CNIC_E1HVN(cp)); + u32 val; + + memset(txbd, 0, BCM_PAGE_SIZE); + + buf_map = cp->l2_buf_map; + for (i = 0; i < MAX_TX_DESC_CNT; i += 3, txbd += 3) { + struct eth_tx_start_bd *start_bd = &txbd->start_bd; + struct eth_tx_bd *reg_bd = &((txbd + 2)->reg_bd); + + start_bd->addr_hi = cpu_to_le32((u64) buf_map >> 32); + start_bd->addr_lo = cpu_to_le32(buf_map & 0xffffffff); + reg_bd->addr_hi = start_bd->addr_hi; + reg_bd->addr_lo = start_bd->addr_lo + 0x10; + start_bd->nbytes = cpu_to_le16(0x10); + start_bd->nbd = cpu_to_le16(3); + start_bd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD; + start_bd->general_data = (UNICAST_ADDRESS << + ETH_TX_START_BD_ETH_ADDR_TYPE_SHIFT); + start_bd->general_data |= (1 << ETH_TX_START_BD_HDR_NBDS_SHIFT); + + } + context = cnic_get_bnx2x_ctx(dev, BNX2X_ISCSI_L2_CID, 1, &context_addr); + + val = (u64) cp->l2_ring_map >> 32; + txbd->next_bd.addr_hi = cpu_to_le32(val); + + context->xstorm_st_context.tx_bd_page_base_hi = val; + + val = (u64) cp->l2_ring_map & 0xffffffff; + txbd->next_bd.addr_lo = cpu_to_le32(val); + + context->xstorm_st_context.tx_bd_page_base_lo = val; + + context->cstorm_st_context.sb_index_number = + HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS; + context->cstorm_st_context.status_block_id = BNX2X_DEF_SB_ID; + + context->xstorm_st_context.statistics_data = (cli | + XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE); + + context->xstorm_ag_context.cdu_reserved = + CDU_RSRVD_VALUE_TYPE_A(BNX2X_HW_CID(BNX2X_ISCSI_L2_CID, func), + CDU_REGION_NUMBER_XCM_AG, + ETH_CONNECTION_TYPE); + + /* reset xstorm per client statistics */ + val = BAR_XSTRORM_INTMEM + + XSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli); + for (i = 0; i < sizeof(struct xstorm_per_client_stats) / 4; i++) + CNIC_WR(dev, val + i * 4, 0); + + cp->tx_cons_ptr = + &cp->bnx2x_def_status_blk->c_def_status_block.index_values[ + HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS]; +} + +static void cnic_init_bnx2x_rx_ring(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + struct eth_rx_bd *rxbd = (struct eth_rx_bd *) (cp->l2_ring + + BCM_PAGE_SIZE); + struct eth_rx_cqe_next_page *rxcqe = (struct eth_rx_cqe_next_page *) + (cp->l2_ring + (2 * BCM_PAGE_SIZE)); + struct eth_context *context; + struct regpair context_addr; + int i; + int port = CNIC_PORT(cp); + int func = CNIC_FUNC(cp); + int cli = BNX2X_ISCSI_CL_ID(CNIC_E1HVN(cp)); + u32 val; + struct tstorm_eth_client_config tstorm_client = {0}; + + for (i = 0; i < BNX2X_MAX_RX_DESC_CNT; i++, rxbd++) { + dma_addr_t buf_map; + int n = (i % cp->l2_rx_ring_size) + 1; + + buf_map = cp->l2_buf_map + (n * cp->l2_single_buf_size); + rxbd->addr_hi = cpu_to_le32((u64) buf_map >> 32); + rxbd->addr_lo = cpu_to_le32(buf_map & 0xffffffff); + } + context = cnic_get_bnx2x_ctx(dev, BNX2X_ISCSI_L2_CID, 0, &context_addr); + + val = (u64) (cp->l2_ring_map + BCM_PAGE_SIZE) >> 32; + rxbd->addr_hi = cpu_to_le32(val); + + context->ustorm_st_context.common.bd_page_base_hi = val; + + val = (u64) (cp->l2_ring_map + BCM_PAGE_SIZE) & 0xffffffff; + rxbd->addr_lo = cpu_to_le32(val); + + context->ustorm_st_context.common.bd_page_base_lo = val; + + context->ustorm_st_context.common.sb_index_numbers = + BNX2X_ISCSI_RX_SB_INDEX_NUM; + context->ustorm_st_context.common.clientId = cli; + context->ustorm_st_context.common.status_block_id = BNX2X_DEF_SB_ID; + context->ustorm_st_context.common.flags = + USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_STATISTICS; + context->ustorm_st_context.common.statistics_counter_id = cli; + context->ustorm_st_context.common.mc_alignment_log_size = 0; + context->ustorm_st_context.common.bd_buff_size = + cp->l2_single_buf_size; + + context->ustorm_ag_context.cdu_usage = + CDU_RSRVD_VALUE_TYPE_A(BNX2X_HW_CID(BNX2X_ISCSI_L2_CID, func), + CDU_REGION_NUMBER_UCM_AG, + ETH_CONNECTION_TYPE); + + rxcqe += BNX2X_MAX_RCQ_DESC_CNT; + val = (u64) (cp->l2_ring_map + (2 * BCM_PAGE_SIZE)) >> 32; + rxcqe->addr_hi = cpu_to_le32(val); + + CNIC_WR(dev, BAR_USTRORM_INTMEM + + USTORM_CQE_PAGE_BASE_OFFSET(port, cli) + 4, val); + + CNIC_WR(dev, BAR_USTRORM_INTMEM + + USTORM_CQE_PAGE_NEXT_OFFSET(port, cli) + 4, val); + + val = (u64) (cp->l2_ring_map + (2 * BCM_PAGE_SIZE)) & 0xffffffff; + rxcqe->addr_lo = cpu_to_le32(val); + + CNIC_WR(dev, BAR_USTRORM_INTMEM + + USTORM_CQE_PAGE_BASE_OFFSET(port, cli), val); + + CNIC_WR(dev, BAR_USTRORM_INTMEM + + USTORM_CQE_PAGE_NEXT_OFFSET(port, cli), val); + + /* client tstorm info */ + tstorm_client.mtu = cp->l2_single_buf_size - 14; + tstorm_client.config_flags = + (TSTORM_ETH_CLIENT_CONFIG_E1HOV_REM_ENABLE | + TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE); + tstorm_client.statistics_counter_id = cli; + + CNIC_WR(dev, BAR_TSTRORM_INTMEM + + TSTORM_CLIENT_CONFIG_OFFSET(port, cli), + ((u32 *)&tstorm_client)[0]); + CNIC_WR(dev, BAR_TSTRORM_INTMEM + + TSTORM_CLIENT_CONFIG_OFFSET(port, cli) + 4, + ((u32 *)&tstorm_client)[1]); + + /* reset tstorm per client statistics */ + val = BAR_TSTRORM_INTMEM + + TSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli); + for (i = 0; i < sizeof(struct tstorm_per_client_stats) / 4; i++) + CNIC_WR(dev, val + i * 4, 0); + + /* reset ustorm per client statistics */ + val = BAR_USTRORM_INTMEM + + USTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli); + for (i = 0; i < sizeof(struct ustorm_per_client_stats) / 4; i++) + CNIC_WR(dev, val + i * 4, 0); + + cp->rx_cons_ptr = + &cp->bnx2x_def_status_blk->u_def_status_block.index_values[ + HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS]; +} + +static void cnic_get_bnx2x_iscsi_info(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + u32 base, addr, val; + int port = CNIC_PORT(cp); + + dev->max_iscsi_conn = 0; + base = CNIC_RD(dev, MISC_REG_SHARED_MEM_ADDR); + if (base < 0xa0000 || base >= 0xc0000) + return; + + addr = BNX2X_SHMEM_ADDR(base, + dev_info.port_hw_config[port].iscsi_mac_upper); + + val = CNIC_RD(dev, addr); + + dev->mac_addr[0] = (u8) (val >> 8); + dev->mac_addr[1] = (u8) val; + + addr = BNX2X_SHMEM_ADDR(base, + dev_info.port_hw_config[port].iscsi_mac_lower); + + val = CNIC_RD(dev, addr); + + dev->mac_addr[2] = (u8) (val >> 24); + dev->mac_addr[3] = (u8) (val >> 16); + dev->mac_addr[4] = (u8) (val >> 8); + dev->mac_addr[5] = (u8) val; + + addr = BNX2X_SHMEM_ADDR(base, validity_map[port]); + val = CNIC_RD(dev, addr); + + if (!(val & SHR_MEM_VALIDITY_LIC_NO_KEY_IN_EFFECT)) { + u16 val16; + + addr = BNX2X_SHMEM_ADDR(base, + drv_lic_key[port].max_iscsi_init_conn); + val16 = CNIC_RD16(dev, addr); + + if (val16) + val16 ^= 0x1e1e; + dev->max_iscsi_conn = val16; + } + if (BNX2X_CHIP_IS_E1H(cp->chip_id)) { + int func = CNIC_FUNC(cp); + + addr = BNX2X_SHMEM_ADDR(base, + mf_cfg.func_mf_config[func].e1hov_tag); + val = CNIC_RD(dev, addr); + val &= FUNC_MF_CFG_E1HOV_TAG_MASK; + if (val != FUNC_MF_CFG_E1HOV_TAG_DEFAULT) { + addr = BNX2X_SHMEM_ADDR(base, + mf_cfg.func_mf_config[func].config); + val = CNIC_RD(dev, addr); + val &= FUNC_MF_CFG_PROTOCOL_MASK; + if (val != FUNC_MF_CFG_PROTOCOL_ISCSI) + dev->max_iscsi_conn = 0; + } + } +} + +static int cnic_start_bnx2x_hw(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int func = CNIC_FUNC(cp), ret, i; + int port = CNIC_PORT(cp); + u16 eq_idx; + u8 sb_id = cp->status_blk_num; + + ret = cnic_init_id_tbl(&cp->cid_tbl, MAX_ISCSI_TBL_SZ, + BNX2X_ISCSI_START_CID); + + if (ret) + return -ENOMEM; + + cp->kcq_io_addr = BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_PROD_OFFSET(func, 0); + cp->kcq_prod_idx = 0; + + cnic_get_bnx2x_iscsi_info(dev); + + /* Only 1 EQ */ + CNIC_WR16(dev, cp->kcq_io_addr, MAX_KCQ_IDX); + CNIC_WR(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_CONS_OFFSET(func, 0), 0); + CNIC_WR(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(func, 0), + cp->kcq_info.pg_map_arr[1] & 0xffffffff); + CNIC_WR(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(func, 0) + 4, + (u64) cp->kcq_info.pg_map_arr[1] >> 32); + CNIC_WR(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(func, 0), + cp->kcq_info.pg_map_arr[0] & 0xffffffff); + CNIC_WR(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(func, 0) + 4, + (u64) cp->kcq_info.pg_map_arr[0] >> 32); + CNIC_WR8(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_VALID_OFFSET(func, 0), 1); + CNIC_WR16(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_SB_NUM_OFFSET(func, 0), cp->status_blk_num); + CNIC_WR8(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_SB_INDEX_OFFSET(func, 0), + HC_INDEX_C_ISCSI_EQ_CONS); + + for (i = 0; i < cp->conn_buf_info.num_pages; i++) { + CNIC_WR(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_CONN_BUF_PBL_OFFSET(func, i), + cp->conn_buf_info.pgtbl[2 * i]); + CNIC_WR(dev, BAR_TSTRORM_INTMEM + + TSTORM_ISCSI_CONN_BUF_PBL_OFFSET(func, i) + 4, + cp->conn_buf_info.pgtbl[(2 * i) + 1]); + } + + CNIC_WR(dev, BAR_USTRORM_INTMEM + + USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(func), + cp->gbl_buf_info.pg_map_arr[0] & 0xffffffff); + CNIC_WR(dev, BAR_USTRORM_INTMEM + + USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(func) + 4, + (u64) cp->gbl_buf_info.pg_map_arr[0] >> 32); + + cnic_setup_bnx2x_context(dev); + + eq_idx = CNIC_RD16(dev, BAR_CSTRORM_INTMEM + + CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id) + + offsetof(struct cstorm_status_block_c, + index_values[HC_INDEX_C_ISCSI_EQ_CONS])); + if (eq_idx != 0) { + printk(KERN_ERR PFX "%s: EQ cons index %x != 0\n", + dev->netdev->name, eq_idx); + return -EBUSY; + } + ret = cnic_init_bnx2x_irq(dev); + if (ret) + return ret; + + cnic_init_bnx2x_tx_ring(dev); + cnic_init_bnx2x_rx_ring(dev); + + return 0; +} + +static void cnic_init_rings(struct cnic_dev *dev) +{ + if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) { + cnic_init_bnx2_tx_ring(dev); + cnic_init_bnx2_rx_ring(dev); + } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) { + struct cnic_local *cp = dev->cnic_priv; + u32 cli = BNX2X_ISCSI_CL_ID(CNIC_E1HVN(cp)); + union l5cm_specific_data l5_data; + struct ustorm_eth_rx_producers rx_prods = {0}; + u32 off, i; + + rx_prods.bd_prod = 0; + rx_prods.cqe_prod = BNX2X_MAX_RCQ_DESC_CNT; + barrier(); + + off = BAR_USTRORM_INTMEM + + USTORM_RX_PRODS_OFFSET(CNIC_PORT(cp), cli); + + for (i = 0; i < sizeof(struct ustorm_eth_rx_producers) / 4; i++) + CNIC_WR(dev, off + i * 4, ((u32 *) &rx_prods)[i]); + + cnic_init_bnx2x_tx_ring(dev); + cnic_init_bnx2x_rx_ring(dev); + + l5_data.phy_address.lo = cli; + l5_data.phy_address.hi = 0; + cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_ETH_CLIENT_SETUP, + BNX2X_ISCSI_L2_CID, ETH_CONNECTION_TYPE, &l5_data); + cnic_ring_ctl(dev, BNX2X_ISCSI_L2_CID, cli, 1); + } +} + +static void cnic_shutdown_rings(struct cnic_dev *dev) +{ + if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) { + cnic_shutdown_bnx2_rx_ring(dev); + } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) { + struct cnic_local *cp = dev->cnic_priv; + u32 cli = BNX2X_ISCSI_CL_ID(CNIC_E1HVN(cp)); + union l5cm_specific_data l5_data; + + cnic_ring_ctl(dev, BNX2X_ISCSI_L2_CID, cli, 0); + + l5_data.phy_address.lo = cli; + l5_data.phy_address.hi = 0; + cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_ETH_HALT, + BNX2X_ISCSI_L2_CID, ETH_CONNECTION_TYPE, &l5_data); + msleep(10); + } +} + static int cnic_register_netdev(struct cnic_dev *dev) { struct cnic_local *cp = dev->cnic_priv; @@ -2554,6 +4276,22 @@ static void cnic_stop_bnx2_hw(struct cnic_dev *dev) cnic_free_resc(dev); } + +static void cnic_stop_bnx2x_hw(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + u8 sb_id = cp->status_blk_num; + int port = CNIC_PORT(cp); + + cnic_free_irq(dev); + CNIC_WR16(dev, BAR_CSTRORM_INTMEM + + CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id) + + offsetof(struct cstorm_status_block_c, + index_values[HC_INDEX_C_ISCSI_EQ_CONS]), + 0); + cnic_free_resc(dev); +} + static void cnic_stop_hw(struct cnic_dev *dev) { if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) { @@ -2685,6 +4423,57 @@ cnic_err: return NULL; } +static struct cnic_dev *init_bnx2x_cnic(struct net_device *dev) +{ + struct pci_dev *pdev; + struct cnic_dev *cdev; + struct cnic_local *cp; + struct cnic_eth_dev *ethdev = NULL; + struct cnic_eth_dev *(*probe)(struct net_device *) = NULL; + + probe = symbol_get(bnx2x_cnic_probe); + if (probe) { + ethdev = (*probe)(dev); + symbol_put(bnx2x_cnic_probe); + } + if (!ethdev) + return NULL; + + pdev = ethdev->pdev; + if (!pdev) + return NULL; + + dev_hold(dev); + cdev = cnic_alloc_dev(dev, pdev); + if (cdev == NULL) { + dev_put(dev); + return NULL; + } + + set_bit(CNIC_F_BNX2X_CLASS, &cdev->flags); + cdev->submit_kwqes = cnic_submit_bnx2x_kwqes; + + cp = cdev->cnic_priv; + cp->ethdev = ethdev; + cdev->pcidev = pdev; + + cp->cnic_ops = &cnic_bnx2x_ops; + cp->start_hw = cnic_start_bnx2x_hw; + cp->stop_hw = cnic_stop_bnx2x_hw; + cp->setup_pgtbl = cnic_setup_page_tbl_le; + cp->alloc_resc = cnic_alloc_bnx2x_resc; + cp->free_resc = cnic_free_resc; + cp->start_cm = cnic_cm_init_bnx2x_hw; + cp->stop_cm = cnic_cm_stop_bnx2x_hw; + cp->enable_int = cnic_enable_bnx2x_int; + cp->disable_int_sync = cnic_disable_bnx2x_int_sync; + cp->ack_int = cnic_ack_bnx2x_msix; + cp->close_conn = cnic_close_bnx2x_conn; + cp->next_idx = cnic_bnx2x_next_idx; + cp->hw_idx = cnic_bnx2x_hw_idx; + return cdev; +} + static struct cnic_dev *is_cnic_dev(struct net_device *dev) { struct ethtool_drvinfo drvinfo; @@ -2696,6 +4485,8 @@ static struct cnic_dev *is_cnic_dev(struct net_device *dev) if (!strcmp(drvinfo.driver, "bnx2")) cdev = init_bnx2_cnic(dev); + if (!strcmp(drvinfo.driver, "bnx2x")) + cdev = init_bnx2x_cnic(dev); if (cdev) { write_lock(&cnic_dev_lock); list_add(&cdev->list, &cnic_dev_list); diff --git a/drivers/net/cnic.h b/drivers/net/cnic.h index a94b302bb464..241d09acc0d4 100644 --- a/drivers/net/cnic.h +++ b/drivers/net/cnic.h @@ -227,6 +227,7 @@ struct cnic_local { void *status_blk; struct status_block_msix *bnx2_status_blk; struct host_status_block *bnx2x_status_blk; + struct host_def_status_block *bnx2x_def_status_blk; u32 status_blk_num; u32 int_num; @@ -258,6 +259,7 @@ struct cnic_local { struct cnic_ctx *ctx_arr; int ctx_blks; int ctx_blk_size; + unsigned long ctx_align; int cids_per_blk; u32 chip_id; @@ -290,11 +292,73 @@ struct bnx2x_bd_chain_next { u8 reserved[8]; }; +#define ISCSI_DEFAULT_MAX_OUTSTANDING_R2T (1) + #define ISCSI_RAMROD_CMD_ID_UPDATE_CONN (ISCSI_KCQE_OPCODE_UPDATE_CONN) #define ISCSI_RAMROD_CMD_ID_INIT (ISCSI_KCQE_OPCODE_INIT) #define CDU_REGION_NUMBER_XCM_AG 2 #define CDU_REGION_NUMBER_UCM_AG 4 +#define CDU_VALID_DATA(_cid, _region, _type) \ + (((_cid) << 8) | (((_region)&0xf)<<4) | (((_type)&0xf))) + +#define CDU_CRC8(_cid, _region, _type) \ + (calc_crc8(CDU_VALID_DATA(_cid, _region, _type), 0xff)) + +#define CDU_RSRVD_VALUE_TYPE_A(_cid, _region, _type) \ + (0x80 | ((CDU_CRC8(_cid, _region, _type)) & 0x7f)) + +#define BNX2X_CONTEXT_MEM_SIZE 1024 +#define BNX2X_FCOE_CID 16 + +/* iSCSI client IDs are 17, 19, 21, 23 */ +#define BNX2X_ISCSI_BASE_CL_ID 17 +#define BNX2X_ISCSI_CL_ID(vn) (BNX2X_ISCSI_BASE_CL_ID + ((vn) << 1)) + +#define BNX2X_ISCSI_L2_CID 17 +#define BNX2X_ISCSI_START_CID 18 +#define BNX2X_ISCSI_NUM_CONNECTIONS 128 +#define BNX2X_ISCSI_TASK_CONTEXT_SIZE 128 +#define BNX2X_ISCSI_MAX_PENDING_R2TS 4 +#define BNX2X_ISCSI_R2TQE_SIZE 8 +#define BNX2X_ISCSI_HQ_BD_SIZE 64 +#define BNX2X_ISCSI_CONN_BUF_SIZE 64 +#define BNX2X_ISCSI_GLB_BUF_SIZE 64 +#define BNX2X_ISCSI_PBL_NOT_CACHED 0xff +#define BNX2X_ISCSI_PDU_HEADER_NOT_CACHED 0xff +#define BNX2X_HW_CID(x, func) ((x) | (((func) % PORT_MAX) << 23) | \ + (((func) >> 1) << 17)) +#define BNX2X_SW_CID(x) (x & 0x1ffff) +#define BNX2X_CHIP_NUM_57711 0x164f +#define BNX2X_CHIP_NUM_57711E 0x1650 +#define BNX2X_CHIP_NUM(x) (x >> 16) +#define BNX2X_CHIP_IS_57711(x) \ + (BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57711) +#define BNX2X_CHIP_IS_57711E(x) \ + (BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57711E) +#define BNX2X_CHIP_IS_E1H(x) \ + (BNX2X_CHIP_IS_57711(x) || BNX2X_CHIP_IS_57711E(x)) +#define IS_E1H_OFFSET BNX2X_CHIP_IS_E1H(cp->chip_id) + +#define BNX2X_RX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_rx_bd)) +#define BNX2X_MAX_RX_DESC_CNT (BNX2X_RX_DESC_CNT - 2) +#define BNX2X_RCQ_DESC_CNT (BCM_PAGE_SIZE / sizeof(union eth_rx_cqe)) +#define BNX2X_MAX_RCQ_DESC_CNT (BNX2X_RCQ_DESC_CNT - 1) + +#define BNX2X_DEF_SB_ID 16 + +#define BNX2X_ISCSI_RX_SB_INDEX_NUM \ + ((HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS << \ + USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER_SHIFT) & \ + USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER) + +#define BNX2X_SHMEM_ADDR(base, field) (base + \ + offsetof(struct shmem_region, field)) + +#define CNIC_PORT(cp) ((cp)->func % PORT_MAX) +#define CNIC_FUNC(cp) ((cp)->func) +#define CNIC_E1HVN(cp) ((cp)->func >> 1) + #endif diff --git a/drivers/net/cnic_defs.h b/drivers/net/cnic_defs.h index cee80f694457..9827b278dc7c 100644 --- a/drivers/net/cnic_defs.h +++ b/drivers/net/cnic_defs.h @@ -51,6 +51,9 @@ #define L4_KCQE_COMPLETION_STATUS_SUCCESS (0) #define L4_KCQE_COMPLETION_STATUS_TIMEOUT (0x93) +#define L4_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAIL (0x83) +#define L4_KCQE_COMPLETION_STATUS_OFFLOADED_PG (0x89) + #define L4_LAYER_CODE (4) #define L2_LAYER_CODE (2) @@ -577,4 +580,1918 @@ struct l4_kwq_upload { u32 reserved2[6]; }; +/* + * bnx2x structures + */ + +/* + * iSCSI context region, used only in iSCSI + */ +struct ustorm_iscsi_rq_db { + struct regpair pbl_base; + struct regpair curr_pbe; +}; + +/* + * iSCSI context region, used only in iSCSI + */ +struct ustorm_iscsi_r2tq_db { + struct regpair pbl_base; + struct regpair curr_pbe; +}; + +/* + * iSCSI context region, used only in iSCSI + */ +struct ustorm_iscsi_cq_db { +#if defined(__BIG_ENDIAN) + u16 cq_sn; + u16 prod; +#elif defined(__LITTLE_ENDIAN) + u16 prod; + u16 cq_sn; +#endif + struct regpair curr_pbe; +}; + +/* + * iSCSI context region, used only in iSCSI + */ +struct rings_db { + struct ustorm_iscsi_rq_db rq; + struct ustorm_iscsi_r2tq_db r2tq; + struct ustorm_iscsi_cq_db cq[8]; +#if defined(__BIG_ENDIAN) + u16 rq_prod; + u16 r2tq_prod; +#elif defined(__LITTLE_ENDIAN) + u16 r2tq_prod; + u16 rq_prod; +#endif + struct regpair cq_pbl_base; +}; + +/* + * iSCSI context region, used only in iSCSI + */ +struct ustorm_iscsi_placement_db { + u32 sgl_base_lo; + u32 sgl_base_hi; + u32 local_sge_0_address_hi; + u32 local_sge_0_address_lo; +#if defined(__BIG_ENDIAN) + u16 curr_sge_offset; + u16 local_sge_0_size; +#elif defined(__LITTLE_ENDIAN) + u16 local_sge_0_size; + u16 curr_sge_offset; +#endif + u32 local_sge_1_address_hi; + u32 local_sge_1_address_lo; +#if defined(__BIG_ENDIAN) + u16 reserved6; + u16 local_sge_1_size; +#elif defined(__LITTLE_ENDIAN) + u16 local_sge_1_size; + u16 reserved6; +#endif +#if defined(__BIG_ENDIAN) + u8 sgl_size; + u8 local_sge_index_2b; + u16 reserved7; +#elif defined(__LITTLE_ENDIAN) + u16 reserved7; + u8 local_sge_index_2b; + u8 sgl_size; +#endif + u32 rem_pdu; + u32 place_db_bitfield_1; +#define USTORM_ISCSI_PLACEMENT_DB_REM_PDU_PAYLOAD (0xFFFFFF<<0) +#define USTORM_ISCSI_PLACEMENT_DB_REM_PDU_PAYLOAD_SHIFT 0 +#define USTORM_ISCSI_PLACEMENT_DB_CQ_ID (0xFF<<24) +#define USTORM_ISCSI_PLACEMENT_DB_CQ_ID_SHIFT 24 + u32 place_db_bitfield_2; +#define USTORM_ISCSI_PLACEMENT_DB_BYTES_2_TRUNCATE (0xFFFFFF<<0) +#define USTORM_ISCSI_PLACEMENT_DB_BYTES_2_TRUNCATE_SHIFT 0 +#define USTORM_ISCSI_PLACEMENT_DB_HOST_SGE_INDEX (0xFF<<24) +#define USTORM_ISCSI_PLACEMENT_DB_HOST_SGE_INDEX_SHIFT 24 + u32 nal; +#define USTORM_ISCSI_PLACEMENT_DB_REM_SGE_SIZE (0xFFFFFF<<0) +#define USTORM_ISCSI_PLACEMENT_DB_REM_SGE_SIZE_SHIFT 0 +#define USTORM_ISCSI_PLACEMENT_DB_EXP_PADDING_2B (0x3<<24) +#define USTORM_ISCSI_PLACEMENT_DB_EXP_PADDING_2B_SHIFT 24 +#define USTORM_ISCSI_PLACEMENT_DB_EXP_DIGEST_3B (0x7<<26) +#define USTORM_ISCSI_PLACEMENT_DB_EXP_DIGEST_3B_SHIFT 26 +#define USTORM_ISCSI_PLACEMENT_DB_NAL_LEN_3B (0x7<<29) +#define USTORM_ISCSI_PLACEMENT_DB_NAL_LEN_3B_SHIFT 29 +}; + +/* + * Ustorm iSCSI Storm Context + */ +struct ustorm_iscsi_st_context { + u32 exp_stat_sn; + u32 exp_data_sn; + struct rings_db ring; + struct regpair task_pbl_base; + struct regpair tce_phy_addr; + struct ustorm_iscsi_placement_db place_db; + u32 data_rcv_seq; + u32 rem_rcv_len; +#if defined(__BIG_ENDIAN) + u16 hdr_itt; + u16 iscsi_conn_id; +#elif defined(__LITTLE_ENDIAN) + u16 iscsi_conn_id; + u16 hdr_itt; +#endif + u32 nal_bytes; +#if defined(__BIG_ENDIAN) + u8 hdr_second_byte_union; + u8 bitfield_0; +#define USTORM_ISCSI_ST_CONTEXT_BMIDDLEOFPDU (0x1<<0) +#define USTORM_ISCSI_ST_CONTEXT_BMIDDLEOFPDU_SHIFT 0 +#define USTORM_ISCSI_ST_CONTEXT_BFENCECQE (0x1<<1) +#define USTORM_ISCSI_ST_CONTEXT_BFENCECQE_SHIFT 1 +#define USTORM_ISCSI_ST_CONTEXT_RESERVED1 (0x3F<<2) +#define USTORM_ISCSI_ST_CONTEXT_RESERVED1_SHIFT 2 + u8 task_pdu_cache_index; + u8 task_pbe_cache_index; +#elif defined(__LITTLE_ENDIAN) + u8 task_pbe_cache_index; + u8 task_pdu_cache_index; + u8 bitfield_0; +#define USTORM_ISCSI_ST_CONTEXT_BMIDDLEOFPDU (0x1<<0) +#define USTORM_ISCSI_ST_CONTEXT_BMIDDLEOFPDU_SHIFT 0 +#define USTORM_ISCSI_ST_CONTEXT_BFENCECQE (0x1<<1) +#define USTORM_ISCSI_ST_CONTEXT_BFENCECQE_SHIFT 1 +#define USTORM_ISCSI_ST_CONTEXT_RESERVED1 (0x3F<<2) +#define USTORM_ISCSI_ST_CONTEXT_RESERVED1_SHIFT 2 + u8 hdr_second_byte_union; +#endif +#if defined(__BIG_ENDIAN) + u16 reserved3; + u8 reserved2; + u8 acDecrement; +#elif defined(__LITTLE_ENDIAN) + u8 acDecrement; + u8 reserved2; + u16 reserved3; +#endif + u32 task_stat; +#if defined(__BIG_ENDIAN) + u8 hdr_opcode; + u8 num_cqs; + u16 reserved5; +#elif defined(__LITTLE_ENDIAN) + u16 reserved5; + u8 num_cqs; + u8 hdr_opcode; +#endif + u32 negotiated_rx; +#define USTORM_ISCSI_ST_CONTEXT_MAX_RECV_PDU_LENGTH (0xFFFFFF<<0) +#define USTORM_ISCSI_ST_CONTEXT_MAX_RECV_PDU_LENGTH_SHIFT 0 +#define USTORM_ISCSI_ST_CONTEXT_MAX_OUTSTANDING_R2TS (0xFF<<24) +#define USTORM_ISCSI_ST_CONTEXT_MAX_OUTSTANDING_R2TS_SHIFT 24 + u32 negotiated_rx_and_flags; +#define USTORM_ISCSI_ST_CONTEXT_MAX_BURST_LENGTH (0xFFFFFF<<0) +#define USTORM_ISCSI_ST_CONTEXT_MAX_BURST_LENGTH_SHIFT 0 +#define USTORM_ISCSI_ST_CONTEXT_B_CQE_POSTED_OR_HEADER_CACHED (0x1<<24) +#define USTORM_ISCSI_ST_CONTEXT_B_CQE_POSTED_OR_HEADER_CACHED_SHIFT 24 +#define USTORM_ISCSI_ST_CONTEXT_B_HDR_DIGEST_EN (0x1<<25) +#define USTORM_ISCSI_ST_CONTEXT_B_HDR_DIGEST_EN_SHIFT 25 +#define USTORM_ISCSI_ST_CONTEXT_B_DATA_DIGEST_EN (0x1<<26) +#define USTORM_ISCSI_ST_CONTEXT_B_DATA_DIGEST_EN_SHIFT 26 +#define USTORM_ISCSI_ST_CONTEXT_B_PROTOCOL_ERROR (0x1<<27) +#define USTORM_ISCSI_ST_CONTEXT_B_PROTOCOL_ERROR_SHIFT 27 +#define USTORM_ISCSI_ST_CONTEXT_B_TASK_VALID (0x1<<28) +#define USTORM_ISCSI_ST_CONTEXT_B_TASK_VALID_SHIFT 28 +#define USTORM_ISCSI_ST_CONTEXT_TASK_TYPE (0x3<<29) +#define USTORM_ISCSI_ST_CONTEXT_TASK_TYPE_SHIFT 29 +#define USTORM_ISCSI_ST_CONTEXT_B_ALL_DATA_ACKED (0x1<<31) +#define USTORM_ISCSI_ST_CONTEXT_B_ALL_DATA_ACKED_SHIFT 31 +}; + +/* + * TCP context region, shared in TOE, RDMA and ISCSI + */ +struct tstorm_tcp_st_context_section { + u32 flags1; +#define TSTORM_TCP_ST_CONTEXT_SECTION_RTT_SRTT_20B (0xFFFFFF<<0) +#define TSTORM_TCP_ST_CONTEXT_SECTION_RTT_SRTT_20B_SHIFT 0 +#define TSTORM_TCP_ST_CONTEXT_SECTION_PAWS_INVALID (0x1<<24) +#define TSTORM_TCP_ST_CONTEXT_SECTION_PAWS_INVALID_SHIFT 24 +#define TSTORM_TCP_ST_CONTEXT_SECTION_TIMESTAMP_EXISTS (0x1<<25) +#define TSTORM_TCP_ST_CONTEXT_SECTION_TIMESTAMP_EXISTS_SHIFT 25 +#define TSTORM_TCP_ST_CONTEXT_SECTION_ISLE_EXISTS (0x1<<26) +#define TSTORM_TCP_ST_CONTEXT_SECTION_ISLE_EXISTS_SHIFT 26 +#define TSTORM_TCP_ST_CONTEXT_SECTION_STOP_RX_PAYLOAD (0x1<<27) +#define TSTORM_TCP_ST_CONTEXT_SECTION_STOP_RX_PAYLOAD_SHIFT 27 +#define TSTORM_TCP_ST_CONTEXT_SECTION_KA_ENABLED (0x1<<28) +#define TSTORM_TCP_ST_CONTEXT_SECTION_KA_ENABLED_SHIFT 28 +#define TSTORM_TCP_ST_CONTEXT_SECTION_FIRST_RTO_ESTIMATE (0x1<<29) +#define TSTORM_TCP_ST_CONTEXT_SECTION_FIRST_RTO_ESTIMATE_SHIFT 29 +#define TSTORM_TCP_ST_CONTEXT_SECTION_MAX_SEG_RETRANSMIT_EN (0x1<<30) +#define TSTORM_TCP_ST_CONTEXT_SECTION_MAX_SEG_RETRANSMIT_EN_SHIFT 30 +#define TSTORM_TCP_ST_CONTEXT_SECTION_RESERVED3 (0x1<<31) +#define TSTORM_TCP_ST_CONTEXT_SECTION_RESERVED3_SHIFT 31 + u32 flags2; +#define TSTORM_TCP_ST_CONTEXT_SECTION_RTT_VARIATION_20B (0xFFFFFF<<0) +#define TSTORM_TCP_ST_CONTEXT_SECTION_RTT_VARIATION_20B_SHIFT 0 +#define TSTORM_TCP_ST_CONTEXT_SECTION_DA_EN (0x1<<24) +#define TSTORM_TCP_ST_CONTEXT_SECTION_DA_EN_SHIFT 24 +#define TSTORM_TCP_ST_CONTEXT_SECTION_DA_COUNTER_EN (0x1<<25) +#define TSTORM_TCP_ST_CONTEXT_SECTION_DA_COUNTER_EN_SHIFT 25 +#define __TSTORM_TCP_ST_CONTEXT_SECTION_KA_PROBE_SENT (0x1<<26) +#define __TSTORM_TCP_ST_CONTEXT_SECTION_KA_PROBE_SENT_SHIFT 26 +#define __TSTORM_TCP_ST_CONTEXT_SECTION_PERSIST_PROBE_SENT (0x1<<27) +#define __TSTORM_TCP_ST_CONTEXT_SECTION_PERSIST_PROBE_SENT_SHIFT 27 +#define TSTORM_TCP_ST_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<28) +#define TSTORM_TCP_ST_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 28 +#define TSTORM_TCP_ST_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<29) +#define TSTORM_TCP_ST_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 29 +#define __TSTORM_TCP_ST_CONTEXT_SECTION_SECOND_ISLE_DROPPED (0x1<<30) +#define __TSTORM_TCP_ST_CONTEXT_SECTION_SECOND_ISLE_DROPPED_SHIFT 30 +#define __TSTORM_TCP_ST_CONTEXT_SECTION_DONT_SUPPORT_OOO (0x1<<31) +#define __TSTORM_TCP_ST_CONTEXT_SECTION_DONT_SUPPORT_OOO_SHIFT 31 +#if defined(__BIG_ENDIAN) + u16 reserved_slowpath; + u8 tcp_sm_state_3b; + u8 rto_exp_3b; +#elif defined(__LITTLE_ENDIAN) + u8 rto_exp_3b; + u8 tcp_sm_state_3b; + u16 reserved_slowpath; +#endif + u32 rcv_nxt; + u32 timestamp_recent; + u32 timestamp_recent_time; + u32 cwnd; + u32 ss_thresh; + u32 cwnd_accum; + u32 prev_seg_seq; + u32 expected_rel_seq; + u32 recover; +#if defined(__BIG_ENDIAN) + u8 retransmit_count; + u8 ka_max_probe_count; + u8 persist_probe_count; + u8 ka_probe_count; +#elif defined(__LITTLE_ENDIAN) + u8 ka_probe_count; + u8 persist_probe_count; + u8 ka_max_probe_count; + u8 retransmit_count; +#endif +#if defined(__BIG_ENDIAN) + u8 statistics_counter_id; + u8 ooo_support_mode; + u8 snd_wnd_scale_4b; + u8 dup_ack_count; +#elif defined(__LITTLE_ENDIAN) + u8 dup_ack_count; + u8 snd_wnd_scale_4b; + u8 ooo_support_mode; + u8 statistics_counter_id; +#endif + u32 retransmit_start_time; + u32 ka_timeout; + u32 ka_interval; + u32 isle_start_seq; + u32 isle_end_seq; +#if defined(__BIG_ENDIAN) + u16 mss; + u16 recent_seg_wnd; +#elif defined(__LITTLE_ENDIAN) + u16 recent_seg_wnd; + u16 mss; +#endif + u32 reserved4; + u32 max_rt_time; +#if defined(__BIG_ENDIAN) + u16 lsb_mac_address; + u16 vlan_id; +#elif defined(__LITTLE_ENDIAN) + u16 vlan_id; + u16 lsb_mac_address; +#endif + u32 msb_mac_address; + u32 reserved2; +}; + +/* + * Termination variables + */ +struct iscsi_term_vars { + u8 BitMap; +#define ISCSI_TERM_VARS_TCP_STATE (0xF<<0) +#define ISCSI_TERM_VARS_TCP_STATE_SHIFT 0 +#define ISCSI_TERM_VARS_FIN_RECEIVED_SBIT (0x1<<4) +#define ISCSI_TERM_VARS_FIN_RECEIVED_SBIT_SHIFT 4 +#define ISCSI_TERM_VARS_ACK_ON_FIN_RECEIVED_SBIT (0x1<<5) +#define ISCSI_TERM_VARS_ACK_ON_FIN_RECEIVED_SBIT_SHIFT 5 +#define ISCSI_TERM_VARS_TERM_ON_CHIP (0x1<<6) +#define ISCSI_TERM_VARS_TERM_ON_CHIP_SHIFT 6 +#define ISCSI_TERM_VARS_RSRV (0x1<<7) +#define ISCSI_TERM_VARS_RSRV_SHIFT 7 +}; + +/* + * iSCSI context region, used only in iSCSI + */ +struct tstorm_iscsi_st_context_section { +#if defined(__BIG_ENDIAN) + u16 rem_tcp_data_len; + u16 brb_offset; +#elif defined(__LITTLE_ENDIAN) + u16 brb_offset; + u16 rem_tcp_data_len; +#endif + u32 b2nh; +#if defined(__BIG_ENDIAN) + u16 rq_cons; + u8 flags; +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN (0x1<<0) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN_SHIFT 0 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN (0x1<<1) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN_SHIFT 1 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_PARTIAL_HEADER (0x1<<2) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_PARTIAL_HEADER_SHIFT 2 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE (0x1<<3) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE_SHIFT 3 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS (0x1<<4) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS_SHIFT 4 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV (0x7<<5) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV_SHIFT 5 + u8 hdr_bytes_2_fetch; +#elif defined(__LITTLE_ENDIAN) + u8 hdr_bytes_2_fetch; + u8 flags; +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN (0x1<<0) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN_SHIFT 0 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN (0x1<<1) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN_SHIFT 1 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_PARTIAL_HEADER (0x1<<2) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_PARTIAL_HEADER_SHIFT 2 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE (0x1<<3) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE_SHIFT 3 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS (0x1<<4) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS_SHIFT 4 +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV (0x7<<5) +#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV_SHIFT 5 + u16 rq_cons; +#endif + struct regpair rq_db_phy_addr; +#if defined(__BIG_ENDIAN) + struct iscsi_term_vars term_vars; + u8 scratchpad_idx; + u16 iscsi_conn_id; +#elif defined(__LITTLE_ENDIAN) + u16 iscsi_conn_id; + u8 scratchpad_idx; + struct iscsi_term_vars term_vars; +#endif + u32 reserved2; +}; + +/* + * The iSCSI non-aggregative context of Tstorm + */ +struct tstorm_iscsi_st_context { + struct tstorm_tcp_st_context_section tcp; + struct tstorm_iscsi_st_context_section iscsi; +}; + +/* + * The tcp aggregative context section of Xstorm + */ +struct xstorm_tcp_tcp_ag_context_section { +#if defined(__BIG_ENDIAN) + u8 __tcp_agg_vars1; + u8 __da_cnt; + u16 mss; +#elif defined(__LITTLE_ENDIAN) + u16 mss; + u8 __da_cnt; + u8 __tcp_agg_vars1; +#endif + u32 snd_nxt; + u32 tx_wnd; + u32 snd_una; + u32 local_adv_wnd; +#if defined(__BIG_ENDIAN) + u8 __agg_val8_th; + u8 __agg_val8; + u16 tcp_agg_vars2; +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG (0x1<<0) +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_SHIFT 0 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED (0x1<<1) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED_SHIFT 1 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE (0x1<<2) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE_SHIFT 2 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG (0x1<<3) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG_SHIFT 3 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG (0x1<<4) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG_SHIFT 4 +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE (0x1<<5) +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE_SHIFT 5 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN (0x1<<6) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN_SHIFT 6 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN (0x1<<7) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN_SHIFT 7 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN (0x1<<8) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN_SHIFT 8 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 9 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF (0x3<<10) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF (0x3<<14) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 tcp_agg_vars2; +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG (0x1<<0) +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_SHIFT 0 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED (0x1<<1) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED_SHIFT 1 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE (0x1<<2) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE_SHIFT 2 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG (0x1<<3) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG_SHIFT 3 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG (0x1<<4) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG_SHIFT 4 +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE (0x1<<5) +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE_SHIFT 5 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN (0x1<<6) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN_SHIFT 6 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN (0x1<<7) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN_SHIFT 7 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN (0x1<<8) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN_SHIFT 8 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 9 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF (0x3<<10) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF (0x3<<14) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_SHIFT 14 + u8 __agg_val8; + u8 __agg_val8_th; +#endif + u32 ack_to_far_end; + u32 rto_timer; + u32 ka_timer; + u32 ts_to_echo; +#if defined(__BIG_ENDIAN) + u16 __agg_val7_th; + u16 __agg_val7; +#elif defined(__LITTLE_ENDIAN) + u16 __agg_val7; + u16 __agg_val7_th; +#endif +#if defined(__BIG_ENDIAN) + u8 __tcp_agg_vars5; + u8 __tcp_agg_vars4; + u8 __tcp_agg_vars3; + u8 __force_pure_ack_cnt; +#elif defined(__LITTLE_ENDIAN) + u8 __force_pure_ack_cnt; + u8 __tcp_agg_vars3; + u8 __tcp_agg_vars4; + u8 __tcp_agg_vars5; +#endif + u32 tcp_agg_vars6; +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_CF_EN (0x1<<0) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_CF_EN_SHIFT 0 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_EN (0x1<<1) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_EN_SHIFT 1 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_EN (0x1<<2) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_EN_SHIFT 2 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<3) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 3 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX6_FLAG (0x1<<4) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX6_FLAG_SHIFT 4 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX7_FLAG (0x1<<5) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX7_FLAG_SHIFT 5 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX5_CF (0x3<<6) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX5_CF_SHIFT 6 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF (0x3<<8) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_SHIFT 8 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF (0x3<<10) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_SHIFT 10 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF (0x3<<12) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_SHIFT 12 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF (0x3<<14) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_SHIFT 14 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX13_CF (0x3<<16) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX13_CF_SHIFT 16 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX14_CF (0x3<<18) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX14_CF_SHIFT 18 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX15_CF (0x3<<20) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX15_CF_SHIFT 20 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX16_CF (0x3<<22) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX16_CF_SHIFT 22 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX17_CF (0x3<<24) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX17_CF_SHIFT 24 +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ECE_FLAG (0x1<<26) +#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ECE_FLAG_SHIFT 26 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED71 (0x1<<27) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED71_SHIFT 27 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_FORCE_PURE_ACK_CNT_DIRTY (0x1<<28) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_FORCE_PURE_ACK_CNT_DIRTY_SHIFT 28 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TCP_AUTO_STOP_FLAG (0x1<<29) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TCP_AUTO_STOP_FLAG_SHIFT 29 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DO_TS_UPDATE_FLAG (0x1<<30) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DO_TS_UPDATE_FLAG_SHIFT 30 +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CANCEL_RETRANSMIT_FLAG (0x1<<31) +#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CANCEL_RETRANSMIT_FLAG_SHIFT 31 +#if defined(__BIG_ENDIAN) + u16 __agg_misc6; + u16 __tcp_agg_vars7; +#elif defined(__LITTLE_ENDIAN) + u16 __tcp_agg_vars7; + u16 __agg_misc6; +#endif + u32 __agg_val10; + u32 __agg_val10_th; +#if defined(__BIG_ENDIAN) + u16 __reserved3; + u8 __reserved2; + u8 __da_only_cnt; +#elif defined(__LITTLE_ENDIAN) + u8 __da_only_cnt; + u8 __reserved2; + u16 __reserved3; +#endif +}; + +/* + * The iscsi aggregative context of Xstorm + */ +struct xstorm_iscsi_ag_context { +#if defined(__BIG_ENDIAN) + u16 agg_val1; + u8 agg_vars1; +#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0) +#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0 +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1) +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1 +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2) +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2 +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3) +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3 +#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4) +#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4 +#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN (0x1<<5) +#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN_SHIFT 5 +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6) +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6 +#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7) +#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7 + u8 state; +#elif defined(__LITTLE_ENDIAN) + u8 state; + u8 agg_vars1; +#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0) +#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0 +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1) +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1 +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2) +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2 +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3) +#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3 +#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4) +#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4 +#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN (0x1<<5) +#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN_SHIFT 5 +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6) +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6 +#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7) +#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7 + u16 agg_val1; +#endif +#if defined(__BIG_ENDIAN) + u8 cdu_reserved; + u8 agg_vars4; +#define XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF (0x3<<0) +#define XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_SHIFT 0 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF (0x3<<2) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_SHIFT 2 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_EN (0x1<<4) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_EN_SHIFT 4 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_EN (0x1<<5) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_EN_SHIFT 5 +#define __XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_EN (0x1<<6) +#define __XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_EN_SHIFT 6 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_EN (0x1<<7) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_EN_SHIFT 7 + u8 agg_vars3; +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0) +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF (0x3<<6) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_SHIFT 6 + u8 agg_vars2; +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF (0x3<<0) +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_SHIFT 0 +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2) +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG (0x1<<3) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG_SHIFT 3 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG (0x1<<4) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG_SHIFT 4 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1 (0x3<<5) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1_SHIFT 5 +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7) +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7 +#elif defined(__LITTLE_ENDIAN) + u8 agg_vars2; +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF (0x3<<0) +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_SHIFT 0 +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2) +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG (0x1<<3) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG_SHIFT 3 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG (0x1<<4) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG_SHIFT 4 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1 (0x3<<5) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1_SHIFT 5 +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7) +#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7 + u8 agg_vars3; +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0) +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF (0x3<<6) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_SHIFT 6 + u8 agg_vars4; +#define XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF (0x3<<0) +#define XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_SHIFT 0 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF (0x3<<2) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_SHIFT 2 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_EN (0x1<<4) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_EN_SHIFT 4 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_EN (0x1<<5) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_EN_SHIFT 5 +#define __XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_EN (0x1<<6) +#define __XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_EN_SHIFT 6 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_EN (0x1<<7) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_EN_SHIFT 7 + u8 cdu_reserved; +#endif + u32 more_to_send; +#if defined(__BIG_ENDIAN) + u16 agg_vars5; +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5 (0x3<<0) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5_SHIFT 0 +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2) +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2 +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8) +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2 (0x3<<14) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2_SHIFT 14 + u16 sq_cons; +#elif defined(__LITTLE_ENDIAN) + u16 sq_cons; + u16 agg_vars5; +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5 (0x3<<0) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5_SHIFT 0 +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2) +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2 +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8) +#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2 (0x3<<14) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2_SHIFT 14 +#endif + struct xstorm_tcp_tcp_ag_context_section tcp; +#if defined(__BIG_ENDIAN) + u16 agg_vars7; +#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0) +#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG (0x1<<3) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG_SHIFT 3 +#define XSTORM_ISCSI_AG_CONTEXT_AUX18_CF (0x3<<4) +#define XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_SHIFT 4 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3 (0x3<<6) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3_SHIFT 6 +#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF (0x3<<8) +#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_SHIFT 8 +#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10) +#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<11) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 11 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG (0x1<<12) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG_SHIFT 12 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG (0x1<<13) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG_SHIFT 13 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG (0x1<<14) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG_SHIFT 14 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX2_FLAG (0x1<<15) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX2_FLAG_SHIFT 15 + u8 agg_val3_th; + u8 agg_vars6; +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6 (0x7<<0) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6_SHIFT 0 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7 (0x7<<3) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7_SHIFT 3 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4 (0x3<<6) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4_SHIFT 6 +#elif defined(__LITTLE_ENDIAN) + u8 agg_vars6; +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6 (0x7<<0) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6_SHIFT 0 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7 (0x7<<3) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7_SHIFT 3 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4 (0x3<<6) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4_SHIFT 6 + u8 agg_val3_th; + u16 agg_vars7; +#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0) +#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG (0x1<<3) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG_SHIFT 3 +#define XSTORM_ISCSI_AG_CONTEXT_AUX18_CF (0x3<<4) +#define XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_SHIFT 4 +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3 (0x3<<6) +#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3_SHIFT 6 +#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF (0x3<<8) +#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_SHIFT 8 +#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10) +#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<11) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 11 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG (0x1<<12) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG_SHIFT 12 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG (0x1<<13) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG_SHIFT 13 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG (0x1<<14) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG_SHIFT 14 +#define __XSTORM_ISCSI_AG_CONTEXT_AUX2_FLAG (0x1<<15) +#define __XSTORM_ISCSI_AG_CONTEXT_AUX2_FLAG_SHIFT 15 +#endif +#if defined(__BIG_ENDIAN) + u16 __agg_val11_th; + u16 __agg_val11; +#elif defined(__LITTLE_ENDIAN) + u16 __agg_val11; + u16 __agg_val11_th; +#endif +#if defined(__BIG_ENDIAN) + u8 __reserved1; + u8 __agg_val6_th; + u16 __agg_val9; +#elif defined(__LITTLE_ENDIAN) + u16 __agg_val9; + u8 __agg_val6_th; + u8 __reserved1; +#endif +#if defined(__BIG_ENDIAN) + u16 hq_prod; + u16 hq_cons; +#elif defined(__LITTLE_ENDIAN) + u16 hq_cons; + u16 hq_prod; +#endif + u32 agg_vars8; +#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC2 (0xFFFFFF<<0) +#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC2_SHIFT 0 +#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC3 (0xFF<<24) +#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC3_SHIFT 24 +#if defined(__BIG_ENDIAN) + u16 r2tq_prod; + u16 sq_prod; +#elif defined(__LITTLE_ENDIAN) + u16 sq_prod; + u16 r2tq_prod; +#endif +#if defined(__BIG_ENDIAN) + u8 agg_val3; + u8 agg_val6; + u8 agg_val5_th; + u8 agg_val5; +#elif defined(__LITTLE_ENDIAN) + u8 agg_val5; + u8 agg_val5_th; + u8 agg_val6; + u8 agg_val3; +#endif +#if defined(__BIG_ENDIAN) + u16 __agg_misc1; + u16 agg_limit1; +#elif defined(__LITTLE_ENDIAN) + u16 agg_limit1; + u16 __agg_misc1; +#endif + u32 hq_cons_tcp_seq; + u32 exp_stat_sn; + u32 agg_misc5; +}; + +/* + * The tcp aggregative context section of Tstorm + */ +struct tstorm_tcp_tcp_ag_context_section { + u32 __agg_val1; +#if defined(__BIG_ENDIAN) + u8 __tcp_agg_vars2; + u8 __agg_val3; + u16 __agg_val2; +#elif defined(__LITTLE_ENDIAN) + u16 __agg_val2; + u8 __agg_val3; + u8 __tcp_agg_vars2; +#endif +#if defined(__BIG_ENDIAN) + u16 __agg_val5; + u8 __agg_val6; + u8 __tcp_agg_vars3; +#elif defined(__LITTLE_ENDIAN) + u8 __tcp_agg_vars3; + u8 __agg_val6; + u16 __agg_val5; +#endif + u32 snd_nxt; + u32 rtt_seq; + u32 rtt_time; + u32 __reserved66; + u32 wnd_right_edge; + u32 tcp_agg_vars1; +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<0) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 0 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG (0x1<<1) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG_SHIFT 1 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF (0x3<<2) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_SHIFT 2 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF (0x3<<4) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_SHIFT 4 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_EN (0x1<<6) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_EN_SHIFT 6 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_EN (0x1<<7) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_EN_SHIFT 7 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN (0x1<<8) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN_SHIFT 8 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_SND_NXT_EN (0x1<<9) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_SND_NXT_EN_SHIFT 9 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<10) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 10 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_FLAG (0x1<<11) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_FLAG_SHIFT 11 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_EN (0x1<<12) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_EN_SHIFT 12 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_EN (0x1<<13) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_EN_SHIFT 13 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF (0x3<<14) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_SHIFT 14 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF (0x3<<16) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_SHIFT 16 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_BLOCKED (0x1<<18) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_BLOCKED_SHIFT 18 +#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<19) +#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 19 +#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_EN (0x1<<20) +#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_EN_SHIFT 20 +#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_EN (0x1<<21) +#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_EN_SHIFT 21 +#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED1 (0x3<<22) +#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED1_SHIFT 22 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ (0xF<<24) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ_SHIFT 24 +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ (0xF<<28) +#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ_SHIFT 28 + u32 snd_max; + u32 snd_una; + u32 __reserved2; +}; + +/* + * The iscsi aggregative context of Tstorm + */ +struct tstorm_iscsi_ag_context { +#if defined(__BIG_ENDIAN) + u16 ulp_credit; + u8 agg_vars1; +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0) +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0 +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1) +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1 +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2) +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2 +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3) +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_CF (0x3<<4) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_SHIFT 4 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG (0x1<<6) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG_SHIFT 6 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_FLAG (0x1<<7) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_FLAG_SHIFT 7 + u8 state; +#elif defined(__LITTLE_ENDIAN) + u8 state; + u8 agg_vars1; +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0) +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0 +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1) +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1 +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2) +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2 +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3) +#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_CF (0x3<<4) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_SHIFT 4 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG (0x1<<6) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG_SHIFT 6 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_FLAG (0x1<<7) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_FLAG_SHIFT 7 + u16 ulp_credit; +#endif +#if defined(__BIG_ENDIAN) + u16 __agg_val4; + u16 agg_vars2; +#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_FLAG (0x1<<0) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_FLAG_SHIFT 0 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_FLAG (0x1<<1) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_FLAG_SHIFT 1 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_CF (0x3<<2) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_SHIFT 2 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_CF (0x3<<4) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_SHIFT 4 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF (0x3<<6) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_SHIFT 6 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF (0x3<<8) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_SHIFT 8 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG (0x1<<10) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG_SHIFT 10 +#define TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<11) +#define TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 11 +#define TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN (0x1<<12) +#define TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN_SHIFT 12 +#define TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_EN (0x1<<13) +#define TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_EN_SHIFT 13 +#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN (0x1<<14) +#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN_SHIFT 14 +#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN (0x1<<15) +#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN_SHIFT 15 +#elif defined(__LITTLE_ENDIAN) + u16 agg_vars2; +#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_FLAG (0x1<<0) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_FLAG_SHIFT 0 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_FLAG (0x1<<1) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_FLAG_SHIFT 1 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_CF (0x3<<2) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_SHIFT 2 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_CF (0x3<<4) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_SHIFT 4 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF (0x3<<6) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_SHIFT 6 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF (0x3<<8) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_SHIFT 8 +#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG (0x1<<10) +#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG_SHIFT 10 +#define TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<11) +#define TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 11 +#define TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN (0x1<<12) +#define TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN_SHIFT 12 +#define TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_EN (0x1<<13) +#define TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_EN_SHIFT 13 +#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN (0x1<<14) +#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN_SHIFT 14 +#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN (0x1<<15) +#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN_SHIFT 15 + u16 __agg_val4; +#endif + struct tstorm_tcp_tcp_ag_context_section tcp; +}; + +/* + * The iscsi aggregative context of Cstorm + */ +struct cstorm_iscsi_ag_context { + u32 agg_vars1; +#define CSTORM_ISCSI_AG_CONTEXT_STATE (0xFF<<0) +#define CSTORM_ISCSI_AG_CONTEXT_STATE_SHIFT 0 +#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<8) +#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 8 +#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<9) +#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 9 +#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<10) +#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 10 +#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<11) +#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 11 +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_SE_CF_EN (0x1<<12) +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_SE_CF_EN_SHIFT 12 +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_INV_CF_EN (0x1<<13) +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_INV_CF_EN_SHIFT 13 +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF (0x3<<14) +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_SHIFT 14 +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED66 (0x3<<16) +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED66_SHIFT 16 +#define __CSTORM_ISCSI_AG_CONTEXT_FIN_RECEIVED_CF_EN (0x1<<18) +#define __CSTORM_ISCSI_AG_CONTEXT_FIN_RECEIVED_CF_EN_SHIFT 18 +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION0_CF_EN (0x1<<19) +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION0_CF_EN_SHIFT 19 +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION1_CF_EN (0x1<<20) +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION1_CF_EN_SHIFT 20 +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION2_CF_EN (0x1<<21) +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION2_CF_EN_SHIFT 21 +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_EN (0x1<<22) +#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_EN_SHIFT 22 +#define __CSTORM_ISCSI_AG_CONTEXT_REL_SEQ_RULE (0x7<<23) +#define __CSTORM_ISCSI_AG_CONTEXT_REL_SEQ_RULE_SHIFT 23 +#define CSTORM_ISCSI_AG_CONTEXT_HQ_PROD_RULE (0x3<<26) +#define CSTORM_ISCSI_AG_CONTEXT_HQ_PROD_RULE_SHIFT 26 +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED52 (0x3<<28) +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED52_SHIFT 28 +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED53 (0x3<<30) +#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED53_SHIFT 30 +#if defined(__BIG_ENDIAN) + u8 __aux1_th; + u8 __aux1_val; + u16 __agg_vars2; +#elif defined(__LITTLE_ENDIAN) + u16 __agg_vars2; + u8 __aux1_val; + u8 __aux1_th; +#endif + u32 rel_seq; + u32 rel_seq_th; +#if defined(__BIG_ENDIAN) + u16 hq_cons; + u16 hq_prod; +#elif defined(__LITTLE_ENDIAN) + u16 hq_prod; + u16 hq_cons; +#endif +#if defined(__BIG_ENDIAN) + u8 __reserved62; + u8 __reserved61; + u8 __reserved60; + u8 __reserved59; +#elif defined(__LITTLE_ENDIAN) + u8 __reserved59; + u8 __reserved60; + u8 __reserved61; + u8 __reserved62; +#endif +#if defined(__BIG_ENDIAN) + u16 __reserved64; + u16 __cq_u_prod0; +#elif defined(__LITTLE_ENDIAN) + u16 __cq_u_prod0; + u16 __reserved64; +#endif + u32 __cq_u_prod1; +#if defined(__BIG_ENDIAN) + u16 __agg_vars3; + u16 __cq_u_prod2; +#elif defined(__LITTLE_ENDIAN) + u16 __cq_u_prod2; + u16 __agg_vars3; +#endif +#if defined(__BIG_ENDIAN) + u16 __aux2_th; + u16 __cq_u_prod3; +#elif defined(__LITTLE_ENDIAN) + u16 __cq_u_prod3; + u16 __aux2_th; +#endif +}; + +/* + * The iscsi aggregative context of Ustorm + */ +struct ustorm_iscsi_ag_context { +#if defined(__BIG_ENDIAN) + u8 __aux_counter_flags; + u8 agg_vars2; +#define USTORM_ISCSI_AG_CONTEXT_TX_CF (0x3<<0) +#define USTORM_ISCSI_AG_CONTEXT_TX_CF_SHIFT 0 +#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF (0x3<<2) +#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_SHIFT 2 +#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4) +#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4 +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7) +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7 + u8 agg_vars1; +#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0) +#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0 +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1) +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1 +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2) +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2 +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3) +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3 +#define USTORM_ISCSI_AG_CONTEXT_INV_CF (0x3<<4) +#define USTORM_ISCSI_AG_CONTEXT_INV_CF_SHIFT 4 +#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF (0x3<<6) +#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_SHIFT 6 + u8 state; +#elif defined(__LITTLE_ENDIAN) + u8 state; + u8 agg_vars1; +#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0) +#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0 +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1) +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1 +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2) +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2 +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3) +#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3 +#define USTORM_ISCSI_AG_CONTEXT_INV_CF (0x3<<4) +#define USTORM_ISCSI_AG_CONTEXT_INV_CF_SHIFT 4 +#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF (0x3<<6) +#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_SHIFT 6 + u8 agg_vars2; +#define USTORM_ISCSI_AG_CONTEXT_TX_CF (0x3<<0) +#define USTORM_ISCSI_AG_CONTEXT_TX_CF_SHIFT 0 +#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF (0x3<<2) +#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_SHIFT 2 +#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4) +#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4 +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7) +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7 + u8 __aux_counter_flags; +#endif +#if defined(__BIG_ENDIAN) + u8 cdu_usage; + u8 agg_misc2; + u16 __cq_local_comp_itt_val; +#elif defined(__LITTLE_ENDIAN) + u16 __cq_local_comp_itt_val; + u8 agg_misc2; + u8 cdu_usage; +#endif + u32 agg_misc4; +#if defined(__BIG_ENDIAN) + u8 agg_val3_th; + u8 agg_val3; + u16 agg_misc3; +#elif defined(__LITTLE_ENDIAN) + u16 agg_misc3; + u8 agg_val3; + u8 agg_val3_th; +#endif + u32 agg_val1; + u32 agg_misc4_th; +#if defined(__BIG_ENDIAN) + u16 agg_val2_th; + u16 agg_val2; +#elif defined(__LITTLE_ENDIAN) + u16 agg_val2; + u16 agg_val2_th; +#endif +#if defined(__BIG_ENDIAN) + u16 __reserved2; + u8 decision_rules; +#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE (0x7<<0) +#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE_SHIFT 0 +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3) +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3 +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG (0x1<<6) +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG_SHIFT 6 +#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1 (0x1<<7) +#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1_SHIFT 7 + u8 decision_rule_enable_bits; +#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN (0x1<<0) +#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN_SHIFT 0 +#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1) +#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1 +#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN (0x1<<2) +#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN_SHIFT 2 +#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN (0x1<<3) +#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN_SHIFT 3 +#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN (0x1<<4) +#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN_SHIFT 4 +#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<5) +#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 5 +#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<6) +#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 6 +#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7) +#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7 +#elif defined(__LITTLE_ENDIAN) + u8 decision_rule_enable_bits; +#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN (0x1<<0) +#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN_SHIFT 0 +#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1) +#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1 +#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN (0x1<<2) +#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN_SHIFT 2 +#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN (0x1<<3) +#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN_SHIFT 3 +#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN (0x1<<4) +#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN_SHIFT 4 +#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<5) +#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 5 +#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<6) +#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 6 +#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7) +#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7 + u8 decision_rules; +#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE (0x7<<0) +#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE_SHIFT 0 +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3) +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3 +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG (0x1<<6) +#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG_SHIFT 6 +#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1 (0x1<<7) +#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1_SHIFT 7 + u16 __reserved2; +#endif +}; + +/* + * Timers connection context + */ +struct iscsi_timers_block_context { + u32 __reserved_0; + u32 __reserved_1; + u32 __reserved_2; + u32 flags; +#define __ISCSI_TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS (0x3<<0) +#define __ISCSI_TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS_SHIFT 0 +#define ISCSI_TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG (0x1<<2) +#define ISCSI_TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG_SHIFT 2 +#define __ISCSI_TIMERS_BLOCK_CONTEXT_RESERVED0 (0x1FFFFFFF<<3) +#define __ISCSI_TIMERS_BLOCK_CONTEXT_RESERVED0_SHIFT 3 +}; + +/* + * Ethernet context section, shared in TOE, RDMA and ISCSI + */ +struct xstorm_eth_context_section { +#if defined(__BIG_ENDIAN) + u8 remote_addr_4; + u8 remote_addr_5; + u8 local_addr_0; + u8 local_addr_1; +#elif defined(__LITTLE_ENDIAN) + u8 local_addr_1; + u8 local_addr_0; + u8 remote_addr_5; + u8 remote_addr_4; +#endif +#if defined(__BIG_ENDIAN) + u8 remote_addr_0; + u8 remote_addr_1; + u8 remote_addr_2; + u8 remote_addr_3; +#elif defined(__LITTLE_ENDIAN) + u8 remote_addr_3; + u8 remote_addr_2; + u8 remote_addr_1; + u8 remote_addr_0; +#endif +#if defined(__BIG_ENDIAN) + u16 reserved_vlan_type; + u16 params; +#define XSTORM_ETH_CONTEXT_SECTION_VLAN_ID (0xFFF<<0) +#define XSTORM_ETH_CONTEXT_SECTION_VLAN_ID_SHIFT 0 +#define XSTORM_ETH_CONTEXT_SECTION_CFI (0x1<<12) +#define XSTORM_ETH_CONTEXT_SECTION_CFI_SHIFT 12 +#define XSTORM_ETH_CONTEXT_SECTION_PRIORITY (0x7<<13) +#define XSTORM_ETH_CONTEXT_SECTION_PRIORITY_SHIFT 13 +#elif defined(__LITTLE_ENDIAN) + u16 params; +#define XSTORM_ETH_CONTEXT_SECTION_VLAN_ID (0xFFF<<0) +#define XSTORM_ETH_CONTEXT_SECTION_VLAN_ID_SHIFT 0 +#define XSTORM_ETH_CONTEXT_SECTION_CFI (0x1<<12) +#define XSTORM_ETH_CONTEXT_SECTION_CFI_SHIFT 12 +#define XSTORM_ETH_CONTEXT_SECTION_PRIORITY (0x7<<13) +#define XSTORM_ETH_CONTEXT_SECTION_PRIORITY_SHIFT 13 + u16 reserved_vlan_type; +#endif +#if defined(__BIG_ENDIAN) + u8 local_addr_2; + u8 local_addr_3; + u8 local_addr_4; + u8 local_addr_5; +#elif defined(__LITTLE_ENDIAN) + u8 local_addr_5; + u8 local_addr_4; + u8 local_addr_3; + u8 local_addr_2; +#endif +}; + +/* + * IpV4 context section, shared in TOE, RDMA and ISCSI + */ +struct xstorm_ip_v4_context_section { +#if defined(__BIG_ENDIAN) + u16 __pbf_hdr_cmd_rsvd_id; + u16 __pbf_hdr_cmd_rsvd_flags_offset; +#elif defined(__LITTLE_ENDIAN) + u16 __pbf_hdr_cmd_rsvd_flags_offset; + u16 __pbf_hdr_cmd_rsvd_id; +#endif +#if defined(__BIG_ENDIAN) + u8 __pbf_hdr_cmd_rsvd_ver_ihl; + u8 tos; + u16 __pbf_hdr_cmd_rsvd_length; +#elif defined(__LITTLE_ENDIAN) + u16 __pbf_hdr_cmd_rsvd_length; + u8 tos; + u8 __pbf_hdr_cmd_rsvd_ver_ihl; +#endif + u32 ip_local_addr; +#if defined(__BIG_ENDIAN) + u8 ttl; + u8 __pbf_hdr_cmd_rsvd_protocol; + u16 __pbf_hdr_cmd_rsvd_csum; +#elif defined(__LITTLE_ENDIAN) + u16 __pbf_hdr_cmd_rsvd_csum; + u8 __pbf_hdr_cmd_rsvd_protocol; + u8 ttl; +#endif + u32 __pbf_hdr_cmd_rsvd_1; + u32 ip_remote_addr; +}; + +/* + * context section, shared in TOE, RDMA and ISCSI + */ +struct xstorm_padded_ip_v4_context_section { + struct xstorm_ip_v4_context_section ip_v4; + u32 reserved1[4]; +}; + +/* + * IpV6 context section, shared in TOE, RDMA and ISCSI + */ +struct xstorm_ip_v6_context_section { +#if defined(__BIG_ENDIAN) + u16 pbf_hdr_cmd_rsvd_payload_len; + u8 pbf_hdr_cmd_rsvd_nxt_hdr; + u8 hop_limit; +#elif defined(__LITTLE_ENDIAN) + u8 hop_limit; + u8 pbf_hdr_cmd_rsvd_nxt_hdr; + u16 pbf_hdr_cmd_rsvd_payload_len; +#endif + u32 priority_flow_label; +#define XSTORM_IP_V6_CONTEXT_SECTION_FLOW_LABEL (0xFFFFF<<0) +#define XSTORM_IP_V6_CONTEXT_SECTION_FLOW_LABEL_SHIFT 0 +#define XSTORM_IP_V6_CONTEXT_SECTION_TRAFFIC_CLASS (0xFF<<20) +#define XSTORM_IP_V6_CONTEXT_SECTION_TRAFFIC_CLASS_SHIFT 20 +#define XSTORM_IP_V6_CONTEXT_SECTION_PBF_HDR_CMD_RSVD_VER (0xF<<28) +#define XSTORM_IP_V6_CONTEXT_SECTION_PBF_HDR_CMD_RSVD_VER_SHIFT 28 + u32 ip_local_addr_lo_hi; + u32 ip_local_addr_lo_lo; + u32 ip_local_addr_hi_hi; + u32 ip_local_addr_hi_lo; + u32 ip_remote_addr_lo_hi; + u32 ip_remote_addr_lo_lo; + u32 ip_remote_addr_hi_hi; + u32 ip_remote_addr_hi_lo; +}; + +union xstorm_ip_context_section_types { + struct xstorm_padded_ip_v4_context_section padded_ip_v4; + struct xstorm_ip_v6_context_section ip_v6; +}; + +/* + * TCP context section, shared in TOE, RDMA and ISCSI + */ +struct xstorm_tcp_context_section { + u32 snd_max; +#if defined(__BIG_ENDIAN) + u16 remote_port; + u16 local_port; +#elif defined(__LITTLE_ENDIAN) + u16 local_port; + u16 remote_port; +#endif +#if defined(__BIG_ENDIAN) + u8 original_nagle_1b; + u8 ts_enabled_1b; + u16 tcp_params; +#define XSTORM_TCP_CONTEXT_SECTION_TOTAL_HEADER_SIZE (0xFF<<0) +#define XSTORM_TCP_CONTEXT_SECTION_TOTAL_HEADER_SIZE_SHIFT 0 +#define __XSTORM_TCP_CONTEXT_SECTION_ECT_BIT (0x1<<8) +#define __XSTORM_TCP_CONTEXT_SECTION_ECT_BIT_SHIFT 8 +#define __XSTORM_TCP_CONTEXT_SECTION_ECN_ENABLED (0x1<<9) +#define __XSTORM_TCP_CONTEXT_SECTION_ECN_ENABLED_SHIFT 9 +#define XSTORM_TCP_CONTEXT_SECTION_SACK_ENABLED (0x1<<10) +#define XSTORM_TCP_CONTEXT_SECTION_SACK_ENABLED_SHIFT 10 +#define XSTORM_TCP_CONTEXT_SECTION_KA_STATE (0x1<<11) +#define XSTORM_TCP_CONTEXT_SECTION_KA_STATE_SHIFT 11 +#define XSTORM_TCP_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<12) +#define XSTORM_TCP_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 12 +#define XSTORM_TCP_CONTEXT_SECTION_WINDOW_SATURATED (0x1<<13) +#define XSTORM_TCP_CONTEXT_SECTION_WINDOW_SATURATED_SHIFT 13 +#define XSTORM_TCP_CONTEXT_SECTION_SLOWPATH_QUEUES_FLUSH_COUNTER (0x3<<14) +#define XSTORM_TCP_CONTEXT_SECTION_SLOWPATH_QUEUES_FLUSH_COUNTER_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 tcp_params; +#define XSTORM_TCP_CONTEXT_SECTION_TOTAL_HEADER_SIZE (0xFF<<0) +#define XSTORM_TCP_CONTEXT_SECTION_TOTAL_HEADER_SIZE_SHIFT 0 +#define __XSTORM_TCP_CONTEXT_SECTION_ECT_BIT (0x1<<8) +#define __XSTORM_TCP_CONTEXT_SECTION_ECT_BIT_SHIFT 8 +#define __XSTORM_TCP_CONTEXT_SECTION_ECN_ENABLED (0x1<<9) +#define __XSTORM_TCP_CONTEXT_SECTION_ECN_ENABLED_SHIFT 9 +#define XSTORM_TCP_CONTEXT_SECTION_SACK_ENABLED (0x1<<10) +#define XSTORM_TCP_CONTEXT_SECTION_SACK_ENABLED_SHIFT 10 +#define XSTORM_TCP_CONTEXT_SECTION_KA_STATE (0x1<<11) +#define XSTORM_TCP_CONTEXT_SECTION_KA_STATE_SHIFT 11 +#define XSTORM_TCP_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<12) +#define XSTORM_TCP_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 12 +#define XSTORM_TCP_CONTEXT_SECTION_WINDOW_SATURATED (0x1<<13) +#define XSTORM_TCP_CONTEXT_SECTION_WINDOW_SATURATED_SHIFT 13 +#define XSTORM_TCP_CONTEXT_SECTION_SLOWPATH_QUEUES_FLUSH_COUNTER (0x3<<14) +#define XSTORM_TCP_CONTEXT_SECTION_SLOWPATH_QUEUES_FLUSH_COUNTER_SHIFT 14 + u8 ts_enabled_1b; + u8 original_nagle_1b; +#endif +#if defined(__BIG_ENDIAN) + u16 pseudo_csum; + u16 window_scaling_factor; +#elif defined(__LITTLE_ENDIAN) + u16 window_scaling_factor; + u16 pseudo_csum; +#endif + u32 reserved2; + u32 ts_time_diff; + u32 __next_timer_expir; +}; + +/* + * Common context section, shared in TOE, RDMA and ISCSI + */ +struct xstorm_common_context_section { + struct xstorm_eth_context_section ethernet; + union xstorm_ip_context_section_types ip_union; + struct xstorm_tcp_context_section tcp; +#if defined(__BIG_ENDIAN) + u16 reserved; + u8 statistics_params; +#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<0) +#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 0 +#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<1) +#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 1 +#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID (0x1F<<2) +#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID_SHIFT 2 +#define XSTORM_COMMON_CONTEXT_SECTION_RESERVED0 (0x1<<7) +#define XSTORM_COMMON_CONTEXT_SECTION_RESERVED0_SHIFT 7 + u8 ip_version_1b; +#elif defined(__LITTLE_ENDIAN) + u8 ip_version_1b; + u8 statistics_params; +#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<0) +#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 0 +#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<1) +#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 1 +#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID (0x1F<<2) +#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID_SHIFT 2 +#define XSTORM_COMMON_CONTEXT_SECTION_RESERVED0 (0x1<<7) +#define XSTORM_COMMON_CONTEXT_SECTION_RESERVED0_SHIFT 7 + u16 reserved; +#endif +}; + +/* + * Flags used in ISCSI context section + */ +struct xstorm_iscsi_context_flags { + u8 flags; +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_IMMEDIATE_DATA (0x1<<0) +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_IMMEDIATE_DATA_SHIFT 0 +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_INITIAL_R2T (0x1<<1) +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_INITIAL_R2T_SHIFT 1 +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_HEADER_DIGEST (0x1<<2) +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_HEADER_DIGEST_SHIFT 2 +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_DATA_DIGEST (0x1<<3) +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_DATA_DIGEST_SHIFT 3 +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_HQ_BD_WRITTEN (0x1<<4) +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_HQ_BD_WRITTEN_SHIFT 4 +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_LAST_OP_SQ (0x1<<5) +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_LAST_OP_SQ_SHIFT 5 +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_UPDATE_SND_NXT (0x1<<6) +#define XSTORM_ISCSI_CONTEXT_FLAGS_B_UPDATE_SND_NXT_SHIFT 6 +#define XSTORM_ISCSI_CONTEXT_FLAGS_RESERVED4 (0x1<<7) +#define XSTORM_ISCSI_CONTEXT_FLAGS_RESERVED4_SHIFT 7 +}; + +struct iscsi_task_context_entry_x { + u32 data_out_buffer_offset; + u32 itt; + u32 data_sn; +}; + +struct iscsi_task_context_entry_xuc_x_write_only { + u32 tx_r2t_sn; +}; + +struct iscsi_task_context_entry_xuc_xu_write_both { + u32 sgl_base_lo; + u32 sgl_base_hi; +#if defined(__BIG_ENDIAN) + u8 sgl_size; + u8 sge_index; + u16 sge_offset; +#elif defined(__LITTLE_ENDIAN) + u16 sge_offset; + u8 sge_index; + u8 sgl_size; +#endif +}; + +/* + * iSCSI context section + */ +struct xstorm_iscsi_context_section { + u32 first_burst_length; + u32 max_send_pdu_length; + struct regpair sq_pbl_base; + struct regpair sq_curr_pbe; + struct regpair hq_pbl_base; + struct regpair hq_curr_pbe_base; + struct regpair r2tq_pbl_base; + struct regpair r2tq_curr_pbe_base; + struct regpair task_pbl_base; +#if defined(__BIG_ENDIAN) + u16 data_out_count; + struct xstorm_iscsi_context_flags flags; + u8 task_pbl_cache_idx; +#elif defined(__LITTLE_ENDIAN) + u8 task_pbl_cache_idx; + struct xstorm_iscsi_context_flags flags; + u16 data_out_count; +#endif + u32 seq_more_2_send; + u32 pdu_more_2_send; + struct iscsi_task_context_entry_x temp_tce_x; + struct iscsi_task_context_entry_xuc_x_write_only temp_tce_x_wr; + struct iscsi_task_context_entry_xuc_xu_write_both temp_tce_xu_wr; + struct regpair lun; + u32 exp_data_transfer_len_ttt; + u32 pdu_data_2_rxmit; + u32 rxmit_bytes_2_dr; +#if defined(__BIG_ENDIAN) + u16 rxmit_sge_offset; + u16 hq_rxmit_cons; +#elif defined(__LITTLE_ENDIAN) + u16 hq_rxmit_cons; + u16 rxmit_sge_offset; +#endif +#if defined(__BIG_ENDIAN) + u16 r2tq_cons; + u8 rxmit_flags; +#define XSTORM_ISCSI_CONTEXT_SECTION_B_NEW_HQ_BD (0x1<<0) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_NEW_HQ_BD_SHIFT 0 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PDU_HDR (0x1<<1) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PDU_HDR_SHIFT 1 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_END_PDU (0x1<<2) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_END_PDU_SHIFT 2 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_DR (0x1<<3) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_DR_SHIFT 3 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_START_DR (0x1<<4) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_START_DR_SHIFT 4 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PADDING (0x3<<5) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PADDING_SHIFT 5 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_ISCSI_CONT_FAST_RXMIT (0x1<<7) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_ISCSI_CONT_FAST_RXMIT_SHIFT 7 + u8 rxmit_sge_idx; +#elif defined(__LITTLE_ENDIAN) + u8 rxmit_sge_idx; + u8 rxmit_flags; +#define XSTORM_ISCSI_CONTEXT_SECTION_B_NEW_HQ_BD (0x1<<0) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_NEW_HQ_BD_SHIFT 0 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PDU_HDR (0x1<<1) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PDU_HDR_SHIFT 1 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_END_PDU (0x1<<2) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_END_PDU_SHIFT 2 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_DR (0x1<<3) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_DR_SHIFT 3 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_START_DR (0x1<<4) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_START_DR_SHIFT 4 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PADDING (0x3<<5) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PADDING_SHIFT 5 +#define XSTORM_ISCSI_CONTEXT_SECTION_B_ISCSI_CONT_FAST_RXMIT (0x1<<7) +#define XSTORM_ISCSI_CONTEXT_SECTION_B_ISCSI_CONT_FAST_RXMIT_SHIFT 7 + u16 r2tq_cons; +#endif + u32 hq_rxmit_tcp_seq; +}; + +/* + * Xstorm iSCSI Storm Context + */ +struct xstorm_iscsi_st_context { + struct xstorm_common_context_section common; + struct xstorm_iscsi_context_section iscsi; +}; + +/* + * CQ DB CQ producer and pending completion counter + */ +struct iscsi_cq_db_prod_pnd_cmpltn_cnt { +#if defined(__BIG_ENDIAN) + u16 cntr; + u16 prod; +#elif defined(__LITTLE_ENDIAN) + u16 prod; + u16 cntr; +#endif +}; + +/* + * CQ DB pending completion ITT array + */ +struct iscsi_cq_db_prod_pnd_cmpltn_cnt_arr { + struct iscsi_cq_db_prod_pnd_cmpltn_cnt prod_pend_comp[8]; +}; + +/* + * Cstorm CQ sequence to notify array, updated by driver + */ +struct iscsi_cq_db_sqn_2_notify_arr { + u16 sqn[8]; +}; + +/* + * Cstorm iSCSI Storm Context + */ +struct cstorm_iscsi_st_context { + struct iscsi_cq_db_prod_pnd_cmpltn_cnt_arr cq_c_prod_pend_comp_ctr_arr; + struct iscsi_cq_db_sqn_2_notify_arr cq_c_prod_sqn_arr; + struct iscsi_cq_db_sqn_2_notify_arr cq_c_sqn_2_notify_arr; + struct regpair hq_pbl_base; + struct regpair hq_curr_pbe; + struct regpair task_pbl_base; + struct regpair cq_db_base; +#if defined(__BIG_ENDIAN) + u16 hq_bd_itt; + u16 iscsi_conn_id; +#elif defined(__LITTLE_ENDIAN) + u16 iscsi_conn_id; + u16 hq_bd_itt; +#endif + u32 hq_bd_data_segment_len; + u32 hq_bd_buffer_offset; +#if defined(__BIG_ENDIAN) + u8 timer_entry_idx; + u8 cq_proc_en_bit_map; + u8 cq_pend_comp_itt_valid_bit_map; + u8 hq_bd_opcode; +#elif defined(__LITTLE_ENDIAN) + u8 hq_bd_opcode; + u8 cq_pend_comp_itt_valid_bit_map; + u8 cq_proc_en_bit_map; + u8 timer_entry_idx; +#endif + u32 hq_tcp_seq; +#if defined(__BIG_ENDIAN) + u16 flags; +#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN (0x1<<0) +#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN_SHIFT 0 +#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN (0x1<<1) +#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN_SHIFT 1 +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID (0x1<<2) +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID_SHIFT 2 +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG (0x1<<3) +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG_SHIFT 3 +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK (0x1<<4) +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK_SHIFT 4 +#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV (0x7FF<<5) +#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV_SHIFT 5 + u16 hq_cons; +#elif defined(__LITTLE_ENDIAN) + u16 hq_cons; + u16 flags; +#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN (0x1<<0) +#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN_SHIFT 0 +#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN (0x1<<1) +#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN_SHIFT 1 +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID (0x1<<2) +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID_SHIFT 2 +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG (0x1<<3) +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG_SHIFT 3 +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK (0x1<<4) +#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK_SHIFT 4 +#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV (0x7FF<<5) +#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV_SHIFT 5 +#endif + struct regpair rsrv1; +}; + +/* + * Iscsi connection context + */ +struct iscsi_context { + struct ustorm_iscsi_st_context ustorm_st_context; + struct tstorm_iscsi_st_context tstorm_st_context; + struct xstorm_iscsi_ag_context xstorm_ag_context; + struct tstorm_iscsi_ag_context tstorm_ag_context; + struct cstorm_iscsi_ag_context cstorm_ag_context; + struct ustorm_iscsi_ag_context ustorm_ag_context; + struct iscsi_timers_block_context timers_context; + struct regpair upb_context; + struct xstorm_iscsi_st_context xstorm_st_context; + struct regpair xpb_context; + struct cstorm_iscsi_st_context cstorm_st_context; +}; + +/* + * Buffer per connection, used in Tstorm + */ +struct iscsi_conn_buf { + struct regpair reserved[8]; +}; + +/* + * ipv6 structure + */ +struct ip_v6_addr { + u32 ip_addr_lo_lo; + u32 ip_addr_lo_hi; + u32 ip_addr_hi_lo; + u32 ip_addr_hi_hi; +}; + +/* + * l5cm- connection identification params + */ +struct l5cm_conn_addr_params { + u32 pmtu; +#if defined(__BIG_ENDIAN) + u8 remote_addr_3; + u8 remote_addr_2; + u8 remote_addr_1; + u8 remote_addr_0; +#elif defined(__LITTLE_ENDIAN) + u8 remote_addr_0; + u8 remote_addr_1; + u8 remote_addr_2; + u8 remote_addr_3; +#endif +#if defined(__BIG_ENDIAN) + u16 params; +#define L5CM_CONN_ADDR_PARAMS_IP_VERSION (0x1<<0) +#define L5CM_CONN_ADDR_PARAMS_IP_VERSION_SHIFT 0 +#define L5CM_CONN_ADDR_PARAMS_RSRV (0x7FFF<<1) +#define L5CM_CONN_ADDR_PARAMS_RSRV_SHIFT 1 + u8 remote_addr_5; + u8 remote_addr_4; +#elif defined(__LITTLE_ENDIAN) + u8 remote_addr_4; + u8 remote_addr_5; + u16 params; +#define L5CM_CONN_ADDR_PARAMS_IP_VERSION (0x1<<0) +#define L5CM_CONN_ADDR_PARAMS_IP_VERSION_SHIFT 0 +#define L5CM_CONN_ADDR_PARAMS_RSRV (0x7FFF<<1) +#define L5CM_CONN_ADDR_PARAMS_RSRV_SHIFT 1 +#endif + struct ip_v6_addr local_ip_addr; + struct ip_v6_addr remote_ip_addr; + u32 ipv6_flow_label_20b; + u32 reserved1; +#if defined(__BIG_ENDIAN) + u16 remote_tcp_port; + u16 local_tcp_port; +#elif defined(__LITTLE_ENDIAN) + u16 local_tcp_port; + u16 remote_tcp_port; +#endif +}; + +/* + * l5cm-xstorm connection buffer + */ +struct l5cm_xstorm_conn_buffer { +#if defined(__BIG_ENDIAN) + u16 rsrv1; + u16 params; +#define L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE (0x1<<0) +#define L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE_SHIFT 0 +#define L5CM_XSTORM_CONN_BUFFER_RSRV (0x7FFF<<1) +#define L5CM_XSTORM_CONN_BUFFER_RSRV_SHIFT 1 +#elif defined(__LITTLE_ENDIAN) + u16 params; +#define L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE (0x1<<0) +#define L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE_SHIFT 0 +#define L5CM_XSTORM_CONN_BUFFER_RSRV (0x7FFF<<1) +#define L5CM_XSTORM_CONN_BUFFER_RSRV_SHIFT 1 + u16 rsrv1; +#endif +#if defined(__BIG_ENDIAN) + u16 mss; + u16 pseudo_header_checksum; +#elif defined(__LITTLE_ENDIAN) + u16 pseudo_header_checksum; + u16 mss; +#endif + u32 rcv_buf; + u32 rsrv2; + struct regpair context_addr; +}; + +/* + * l5cm-tstorm connection buffer + */ +struct l5cm_tstorm_conn_buffer { + u32 snd_buf; + u32 rcv_buf; +#if defined(__BIG_ENDIAN) + u16 params; +#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE (0x1<<0) +#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE_SHIFT 0 +#define L5CM_TSTORM_CONN_BUFFER_RSRV (0x7FFF<<1) +#define L5CM_TSTORM_CONN_BUFFER_RSRV_SHIFT 1 + u8 ka_max_probe_count; + u8 ka_enable; +#elif defined(__LITTLE_ENDIAN) + u8 ka_enable; + u8 ka_max_probe_count; + u16 params; +#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE (0x1<<0) +#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE_SHIFT 0 +#define L5CM_TSTORM_CONN_BUFFER_RSRV (0x7FFF<<1) +#define L5CM_TSTORM_CONN_BUFFER_RSRV_SHIFT 1 +#endif + u32 ka_timeout; + u32 ka_interval; + u32 max_rt_time; +}; + +/* + * l5cm connection buffer for active side + */ +struct l5cm_active_conn_buffer { + struct l5cm_conn_addr_params conn_addr_buf; + struct l5cm_xstorm_conn_buffer xstorm_conn_buffer; + struct l5cm_tstorm_conn_buffer tstorm_conn_buffer; +}; + +/* + * l5cm slow path element + */ +struct l5cm_packet_size { + u32 size; + u32 rsrv; +}; + +/* + * l5cm connection parameters + */ +union l5cm_reduce_param_union { + u32 passive_side_scramble_key; + u32 pcs_id; +}; + +/* + * l5cm connection parameters + */ +struct l5cm_reduce_conn { + union l5cm_reduce_param_union param; + u32 isn; +}; + +/* + * l5cm slow path element + */ +union l5cm_specific_data { + u8 protocol_data[8]; + struct regpair phy_address; + struct l5cm_packet_size packet_size; + struct l5cm_reduce_conn reduced_conn; +}; + +/* + * l5 slow path element + */ +struct l5cm_spe { + struct spe_hdr hdr; + union l5cm_specific_data data; +}; + +/* + * Tstorm Tcp flags + */ +struct tstorm_l5cm_tcp_flags { + u16 flags; +#define TSTORM_L5CM_TCP_FLAGS_VLAN_ID (0xFFF<<0) +#define TSTORM_L5CM_TCP_FLAGS_VLAN_ID_SHIFT 0 +#define TSTORM_L5CM_TCP_FLAGS_RSRV0 (0x1<<12) +#define TSTORM_L5CM_TCP_FLAGS_RSRV0_SHIFT 12 +#define TSTORM_L5CM_TCP_FLAGS_TS_ENABLED (0x1<<13) +#define TSTORM_L5CM_TCP_FLAGS_TS_ENABLED_SHIFT 13 +#define TSTORM_L5CM_TCP_FLAGS_RSRV1 (0x3<<14) +#define TSTORM_L5CM_TCP_FLAGS_RSRV1_SHIFT 14 +}; + +/* + * Xstorm Tcp flags + */ +struct xstorm_l5cm_tcp_flags { + u8 flags; +#define XSTORM_L5CM_TCP_FLAGS_ENC_ENABLED (0x1<<0) +#define XSTORM_L5CM_TCP_FLAGS_ENC_ENABLED_SHIFT 0 +#define XSTORM_L5CM_TCP_FLAGS_TS_ENABLED (0x1<<1) +#define XSTORM_L5CM_TCP_FLAGS_TS_ENABLED_SHIFT 1 +#define XSTORM_L5CM_TCP_FLAGS_WND_SCL_EN (0x1<<2) +#define XSTORM_L5CM_TCP_FLAGS_WND_SCL_EN_SHIFT 2 +#define XSTORM_L5CM_TCP_FLAGS_RSRV (0x1F<<3) +#define XSTORM_L5CM_TCP_FLAGS_RSRV_SHIFT 3 +}; + #endif /* CNIC_DEFS_H */ diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h index d8b09efdcb52..8aaf98bdd4f7 100644 --- a/drivers/net/cnic_if.h +++ b/drivers/net/cnic_if.h @@ -12,8 +12,8 @@ #ifndef CNIC_IF_H #define CNIC_IF_H -#define CNIC_MODULE_VERSION "2.0.1" -#define CNIC_MODULE_RELDATE "Oct 01, 2009" +#define CNIC_MODULE_VERSION "2.1.0" +#define CNIC_MODULE_RELDATE "Oct 10, 2009" #define CNIC_ULP_RDMA 0 #define CNIC_ULP_ISCSI 1 @@ -81,6 +81,8 @@ struct kcqe { #define DRV_CTL_CTX_WR_CMD 0x103 #define DRV_CTL_CTXTBL_WR_CMD 0x104 #define DRV_CTL_COMPLETION_CMD 0x105 +#define DRV_CTL_START_L2_CMD 0x106 +#define DRV_CTL_STOP_L2_CMD 0x107 struct cnic_ctl_completion { u32 cid; @@ -105,11 +107,17 @@ struct drv_ctl_io { dma_addr_t dma_addr; }; +struct drv_ctl_l2_ring { + u32 client_id; + u32 cid; +}; + struct drv_ctl_info { int cmd; union { struct drv_ctl_completion comp; struct drv_ctl_io io; + struct drv_ctl_l2_ring ring; char bytes[MAX_DRV_CTL_DATA]; } data; }; @@ -143,6 +151,7 @@ struct cnic_eth_dev { u32 max_kwqe_pending; struct pci_dev *pdev; void __iomem *io_base; + void __iomem *io_base2; u32 ctx_tbl_offset; u32 ctx_tbl_len; @@ -298,5 +307,6 @@ extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops); extern int cnic_unregister_driver(int ulp_type); extern struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev); +extern struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev); #endif diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 61f9da2b4943..678222389407 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -380,9 +380,8 @@ static struct sk_buff *cpmac_rx_one(struct cpmac_priv *priv, return NULL; } - skb = netdev_alloc_skb(priv->dev, CPMAC_SKB_SIZE); + skb = netdev_alloc_skb_ip_align(priv->dev, CPMAC_SKB_SIZE); if (likely(skb)) { - skb_reserve(skb, 2); skb_put(desc->skb, desc->datalen); desc->skb->protocol = eth_type_trans(desc->skb, priv->dev); desc->skb->ip_summed = CHECKSUM_NONE; @@ -991,12 +990,11 @@ static int cpmac_open(struct net_device *dev) priv->rx_head = &priv->desc_ring[CPMAC_QUEUES]; for (i = 0, desc = priv->rx_head; i < priv->ring_size; i++, desc++) { - skb = netdev_alloc_skb(dev, CPMAC_SKB_SIZE); + skb = netdev_alloc_skb_ip_align(dev, CPMAC_SKB_SIZE); if (unlikely(!skb)) { res = -ENOMEM; goto fail_desc; } - skb_reserve(skb, 2); desc->skb = skb; desc->data_mapping = dma_map_single(&dev->dev, skb->data, CPMAC_SKB_SIZE, diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 0c54219960e2..af9321617ce4 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -1323,7 +1323,7 @@ net_open(struct net_device *dev) writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); #endif write_irq(dev, lp->chip_type, dev->irq); - ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, net_interrupt, 0, dev->name, dev); if (ret) { if (net_debug) printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq); diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h index 2b1aea6aa558..3e8618b4efbc 100644 --- a/drivers/net/cxgb3/adapter.h +++ b/drivers/net/cxgb3/adapter.h @@ -48,12 +48,27 @@ struct vlan_group; struct adapter; struct sge_qset; +struct port_info; enum { /* rx_offload flags */ T3_RX_CSUM = 1 << 0, T3_LRO = 1 << 1, }; +enum mac_idx_types { + LAN_MAC_IDX = 0, + SAN_MAC_IDX, + + MAX_MAC_IDX +}; + +struct iscsi_config { + __u8 mac_addr[ETH_ALEN]; + __u32 flags; + int (*send)(struct port_info *pi, struct sk_buff **skb); + int (*recv)(struct port_info *pi, struct sk_buff *skb); +}; + struct port_info { struct adapter *adapter; struct vlan_group *vlan_grp; @@ -68,6 +83,7 @@ struct port_info { struct net_device_stats netstats; int activity; __be32 iscsi_ipv4addr; + struct iscsi_config iscsic; int link_fault; /* link fault was detected */ }; diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h index 1b2c305fb82b..6ff356d4c7ab 100644 --- a/drivers/net/cxgb3/common.h +++ b/drivers/net/cxgb3/common.h @@ -125,11 +125,9 @@ enum { /* adapter interrupt-maintained statistics */ IRQ_NUM_STATS /* keep last */ }; -enum { - TP_VERSION_MAJOR = 1, - TP_VERSION_MINOR = 1, - TP_VERSION_MICRO = 0 -}; +#define TP_VERSION_MAJOR 1 +#define TP_VERSION_MINOR 1 +#define TP_VERSION_MICRO 0 #define S_TP_VERSION_MAJOR 16 #define M_TP_VERSION_MAJOR 0xFF diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 34e776c5f06b..cef3f882e2b6 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -44,6 +44,7 @@ #include <linux/rtnetlink.h> #include <linux/firmware.h> #include <linux/log2.h> +#include <linux/stringify.h> #include <asm/uaccess.h> #include "common.h" @@ -344,8 +345,10 @@ static void link_start(struct net_device *dev) init_rx_mode(&rm, dev, dev->mc_list); t3_mac_reset(mac); + t3_mac_set_num_ucast(mac, MAX_MAC_IDX); t3_mac_set_mtu(mac, dev->mtu); - t3_mac_set_address(mac, 0, dev->dev_addr); + t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr); + t3_mac_set_address(mac, SAN_MAC_IDX, pi->iscsic.mac_addr); t3_mac_set_rx_mode(mac, &rm); t3_link_start(&pi->phy, mac, &pi->link_config); t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); @@ -903,6 +906,7 @@ static inline int offload_tx(struct t3cdev *tdev, struct sk_buff *skb) static int write_smt_entry(struct adapter *adapter, int idx) { struct cpl_smt_write_req *req; + struct port_info *pi = netdev_priv(adapter->port[idx]); struct sk_buff *skb = alloc_skb(sizeof(*req), GFP_KERNEL); if (!skb) @@ -913,8 +917,8 @@ static int write_smt_entry(struct adapter *adapter, int idx) OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, idx)); req->mtu_idx = NMTUS - 1; /* should be 0 but there's a T3 bug */ req->iff = idx; - memset(req->src_mac1, 0, sizeof(req->src_mac1)); memcpy(req->src_mac0, adapter->port[idx]->dev_addr, ETH_ALEN); + memcpy(req->src_mac1, pi->iscsic.mac_addr, ETH_ALEN); skb->priority = 1; offload_tx(&adapter->tdev, skb); return 0; @@ -989,11 +993,21 @@ static int bind_qsets(struct adapter *adap) return err; } -#define FW_FNAME "cxgb3/t3fw-%d.%d.%d.bin" -#define TPSRAM_NAME "cxgb3/t3%c_psram-%d.%d.%d.bin" +#define FW_VERSION __stringify(FW_VERSION_MAJOR) "." \ + __stringify(FW_VERSION_MINOR) "." __stringify(FW_VERSION_MICRO) +#define FW_FNAME "cxgb3/t3fw-" FW_VERSION ".bin" +#define TPSRAM_VERSION __stringify(TP_VERSION_MAJOR) "." \ + __stringify(TP_VERSION_MINOR) "." __stringify(TP_VERSION_MICRO) +#define TPSRAM_NAME "cxgb3/t3%c_psram-" TPSRAM_VERSION ".bin" #define AEL2005_OPT_EDC_NAME "cxgb3/ael2005_opt_edc.bin" #define AEL2005_TWX_EDC_NAME "cxgb3/ael2005_twx_edc.bin" #define AEL2020_TWX_EDC_NAME "cxgb3/ael2020_twx_edc.bin" +MODULE_FIRMWARE(FW_FNAME); +MODULE_FIRMWARE("cxgb3/t3b_psram-" TPSRAM_VERSION ".bin"); +MODULE_FIRMWARE("cxgb3/t3c_psram-" TPSRAM_VERSION ".bin"); +MODULE_FIRMWARE(AEL2005_OPT_EDC_NAME); +MODULE_FIRMWARE(AEL2005_TWX_EDC_NAME); +MODULE_FIRMWARE(AEL2020_TWX_EDC_NAME); static inline const char *get_edc_fw_name(int edc_idx) { @@ -1064,16 +1078,13 @@ int t3_get_edc_fw(struct cphy *phy, int edc_idx, int size) static int upgrade_fw(struct adapter *adap) { int ret; - char buf[64]; const struct firmware *fw; struct device *dev = &adap->pdev->dev; - snprintf(buf, sizeof(buf), FW_FNAME, FW_VERSION_MAJOR, - FW_VERSION_MINOR, FW_VERSION_MICRO); - ret = request_firmware(&fw, buf, dev); + ret = request_firmware(&fw, FW_FNAME, dev); if (ret < 0) { dev_err(dev, "could not upgrade firmware: unable to load %s\n", - buf); + FW_FNAME); return ret; } ret = t3_load_fw(adap, fw->data, fw->size); @@ -1117,8 +1128,7 @@ static int update_tpsram(struct adapter *adap) if (!rev) return 0; - snprintf(buf, sizeof(buf), TPSRAM_NAME, rev, - TP_VERSION_MAJOR, TP_VERSION_MINOR, TP_VERSION_MICRO); + snprintf(buf, sizeof(buf), TPSRAM_NAME, rev); ret = request_firmware(&tpsram, buf, dev); if (ret < 0) { @@ -2107,19 +2117,19 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) if (t.qset_idx >= SGE_QSETS) return -EINVAL; if (!in_range(t.intr_lat, 0, M_NEWTIMER) || - !in_range(t.cong_thres, 0, 255) || - !in_range(t.txq_size[0], MIN_TXQ_ENTRIES, - MAX_TXQ_ENTRIES) || - !in_range(t.txq_size[1], MIN_TXQ_ENTRIES, - MAX_TXQ_ENTRIES) || - !in_range(t.txq_size[2], MIN_CTRL_TXQ_ENTRIES, - MAX_CTRL_TXQ_ENTRIES) || - !in_range(t.fl_size[0], MIN_FL_ENTRIES, - MAX_RX_BUFFERS) - || !in_range(t.fl_size[1], MIN_FL_ENTRIES, - MAX_RX_JUMBO_BUFFERS) - || !in_range(t.rspq_size, MIN_RSPQ_ENTRIES, - MAX_RSPQ_ENTRIES)) + !in_range(t.cong_thres, 0, 255) || + !in_range(t.txq_size[0], MIN_TXQ_ENTRIES, + MAX_TXQ_ENTRIES) || + !in_range(t.txq_size[1], MIN_TXQ_ENTRIES, + MAX_TXQ_ENTRIES) || + !in_range(t.txq_size[2], MIN_CTRL_TXQ_ENTRIES, + MAX_CTRL_TXQ_ENTRIES) || + !in_range(t.fl_size[0], MIN_FL_ENTRIES, + MAX_RX_BUFFERS) || + !in_range(t.fl_size[1], MIN_FL_ENTRIES, + MAX_RX_JUMBO_BUFFERS) || + !in_range(t.rspq_size, MIN_RSPQ_ENTRIES, + MAX_RSPQ_ENTRIES)) return -EINVAL; if ((adapter->flags & FULL_INIT_DONE) && t.lro > 0) @@ -2516,7 +2526,7 @@ static int cxgb_set_mac_addr(struct net_device *dev, void *p) return -EINVAL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - t3_mac_set_address(&pi->mac, 0, dev->dev_addr); + t3_mac_set_address(&pi->mac, LAN_MAC_IDX, dev->dev_addr); if (offload_running(adapter)) write_smt_entry(adapter, pi->port_id); return 0; @@ -2654,7 +2664,7 @@ static void check_t3b2_mac(struct adapter *adapter) struct cmac *mac = &p->mac; t3_mac_set_mtu(mac, dev->mtu); - t3_mac_set_address(mac, 0, dev->dev_addr); + t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr); cxgb_set_rxmode(dev); t3_link_start(&p->phy, mac, &p->link_config); t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); @@ -3112,6 +3122,14 @@ static const struct net_device_ops cxgb_netdev_ops = { #endif }; +static void __devinit cxgb3_init_iscsi_mac(struct net_device *dev) +{ + struct port_info *pi = netdev_priv(dev); + + memcpy(pi->iscsic.mac_addr, dev->dev_addr, ETH_ALEN); + pi->iscsic.mac_addr[3] |= 0x80; +} + static int __devinit init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -3270,6 +3288,9 @@ static int __devinit init_one(struct pci_dev *pdev, goto out_free_dev; } + for_each_port(adapter, i) + cxgb3_init_iscsi_mac(adapter->port[i]); + /* Driver's ready. Reflect it on LEDs */ t3_led_ready(adapter); diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 6366061712f4..bdbd14727e4b 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -1260,7 +1260,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) if (should_restart_tx(q) && test_and_clear_bit(TXQ_ETH, &qs->txq_stopped)) { q->restarts++; - netif_tx_wake_queue(txq); + netif_tx_start_queue(txq); } } @@ -1285,7 +1285,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) /* * We do not use Tx completion interrupts to free DMAd Tx packets. - * This is good for performamce but means that we rely on new Tx + * This is good for performance but means that we rely on new Tx * packets arriving to run the destructors of completed packets, * which open up space in their sockets' send queues. Sometimes * we do not get such new packets causing Tx to stall. A single @@ -1946,10 +1946,9 @@ static void restart_tx(struct sge_qset *qs) * Check if the ARP request is probing the private IP address * dedicated to iSCSI, generate an ARP reply if so. */ -static void cxgb3_arp_process(struct adapter *adapter, struct sk_buff *skb) +static void cxgb3_arp_process(struct port_info *pi, struct sk_buff *skb) { struct net_device *dev = skb->dev; - struct port_info *pi; struct arphdr *arp; unsigned char *arp_ptr; unsigned char *sha; @@ -1972,12 +1971,11 @@ static void cxgb3_arp_process(struct adapter *adapter, struct sk_buff *skb) arp_ptr += dev->addr_len; memcpy(&tip, arp_ptr, sizeof(tip)); - pi = netdev_priv(dev); if (tip != pi->iscsi_ipv4addr) return; arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha, - dev->dev_addr, sha); + pi->iscsic.mac_addr, sha); } @@ -1986,6 +1984,19 @@ static inline int is_arp(struct sk_buff *skb) return skb->protocol == htons(ETH_P_ARP); } +static void cxgb3_process_iscsi_prov_pack(struct port_info *pi, + struct sk_buff *skb) +{ + if (is_arp(skb)) { + cxgb3_arp_process(pi, skb); + return; + } + + if (pi->iscsic.recv) + pi->iscsic.recv(pi, skb); + +} + /** * rx_eth - process an ingress ethernet packet * @adap: the adapter @@ -2024,13 +2035,12 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq, vlan_gro_receive(&qs->napi, grp, ntohs(p->vlan), skb); else { - if (unlikely(pi->iscsi_ipv4addr && - is_arp(skb))) { + if (unlikely(pi->iscsic.flags)) { unsigned short vtag = ntohs(p->vlan) & VLAN_VID_MASK; skb->dev = vlan_group_get_device(grp, vtag); - cxgb3_arp_process(adap, skb); + cxgb3_process_iscsi_prov_pack(pi, skb); } __vlan_hwaccel_rx(skb, grp, ntohs(p->vlan), rq->polling); @@ -2041,8 +2051,8 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq, if (lro) napi_gro_receive(&qs->napi, skb); else { - if (unlikely(pi->iscsi_ipv4addr && is_arp(skb))) - cxgb3_arp_process(adap, skb); + if (unlikely(pi->iscsic.flags)) + cxgb3_process_iscsi_prov_pack(pi, skb); netif_receive_skb(skb); } } else @@ -2125,6 +2135,7 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs, if (!complete) return; + skb_record_rx_queue(skb, qs - &adap->sge.qs[0]); skb->ip_summed = CHECKSUM_UNNECESSARY; cpl = qs->lro_va; diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index e3478314c002..8edac8915ea8 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -2803,11 +2803,33 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev) return 0; } +static +int davinci_emac_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + if (netif_running(dev)) + emac_dev_stop(dev); + + clk_disable(emac_clk); + + return 0; +} + +static int davinci_emac_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + clk_enable(emac_clk); + + if (netif_running(dev)) + emac_dev_open(dev); + + return 0; +} + /** * davinci_emac_driver: EMAC platform driver structure - * - * We implement only probe and remove functions - suspend/resume and - * others not supported by this module */ static struct platform_driver davinci_emac_driver = { .driver = { @@ -2816,6 +2838,8 @@ static struct platform_driver davinci_emac_driver = { }, .probe = davinci_emac_probe, .remove = __devexit_p(davinci_emac_remove), + .suspend = davinci_emac_suspend, + .resume = davinci_emac_resume, }; /** diff --git a/drivers/net/declance.c b/drivers/net/declance.c index a31696a3928e..be9590253aa1 100644 --- a/drivers/net/declance.c +++ b/drivers/net/declance.c @@ -801,14 +801,14 @@ static int lance_open(struct net_device *dev) netif_start_queue(dev); /* Associate IRQ with lance_interrupt */ - if (request_irq(dev->irq, &lance_interrupt, 0, "lance", dev)) { + if (request_irq(dev->irq, lance_interrupt, 0, "lance", dev)) { printk("%s: Can't get IRQ %d\n", dev->name, dev->irq); return -EAGAIN; } if (lp->dma_irq >= 0) { unsigned long flags; - if (request_irq(lp->dma_irq, &lance_dma_merr_int, 0, + if (request_irq(lp->dma_irq, lance_dma_merr_int, 0, "lance error", dev)) { free_irq(dev->irq, dev); printk("%s: Can't get DMA IRQ %d\n", dev->name, diff --git a/drivers/net/depca.c b/drivers/net/depca.c index 7a3bdac84abe..0c1f491d20bf 100644 --- a/drivers/net/depca.c +++ b/drivers/net/depca.c @@ -849,7 +849,7 @@ static int depca_open(struct net_device *dev) depca_dbg_open(dev); - if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name, dev)) { + if (request_irq(dev->irq, depca_interrupt, 0, lp->adapter_name, dev)) { printk("depca_open(): Requested IRQ%d is busy\n", dev->irq); status = -EAGAIN; } else { diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 7fa7a907f134..2a8b6a7c0b87 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -163,8 +163,8 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) strcmp (media[card_idx], "4") == 0) { np->speed = 100; np->full_duplex = 1; - } else if (strcmp (media[card_idx], "100mbps_hd") == 0 - || strcmp (media[card_idx], "3") == 0) { + } else if (strcmp (media[card_idx], "100mbps_hd") == 0 || + strcmp (media[card_idx], "3") == 0) { np->speed = 100; np->full_duplex = 0; } else if (strcmp (media[card_idx], "10mbps_fd") == 0 || @@ -411,7 +411,7 @@ rio_open (struct net_device *dev) int i; u16 macctrl; - i = request_irq (dev->irq, &rio_interrupt, IRQF_SHARED, dev->name, dev); + i = request_irq (dev->irq, rio_interrupt, IRQF_SHARED, dev->name, dev); if (i) return i; @@ -505,7 +505,8 @@ rio_timer (unsigned long data) entry = np->old_rx % RX_RING_SIZE; /* Dropped packets don't need to re-allocate */ if (np->rx_skbuff[entry] == NULL) { - skb = netdev_alloc_skb (dev, np->rx_buf_sz); + skb = netdev_alloc_skb_ip_align(dev, + np->rx_buf_sz); if (skb == NULL) { np->rx_ring[entry].fraginfo = 0; printk (KERN_INFO @@ -514,8 +515,6 @@ rio_timer (unsigned long data) break; } np->rx_skbuff[entry] = skb; - /* 16 byte align the IP header */ - skb_reserve (skb, 2); np->rx_ring[entry].fraginfo = cpu_to_le64 (pci_map_single (np->pdev, skb->data, np->rx_buf_sz, @@ -576,7 +575,9 @@ alloc_list (struct net_device *dev) /* Allocate the rx buffers */ for (i = 0; i < RX_RING_SIZE; i++) { /* Allocated fixed size of skbuff */ - struct sk_buff *skb = netdev_alloc_skb (dev, np->rx_buf_sz); + struct sk_buff *skb; + + skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz); np->rx_skbuff[i] = skb; if (skb == NULL) { printk (KERN_ERR @@ -584,7 +585,6 @@ alloc_list (struct net_device *dev) dev->name); break; } - skb_reserve (skb, 2); /* 16 byte align the IP header. */ /* Rubicon now supports 40 bits of addressing space. */ np->rx_ring[i].fraginfo = cpu_to_le64 ( pci_map_single ( @@ -871,13 +871,11 @@ receive_packet (struct net_device *dev) PCI_DMA_FROMDEVICE); skb_put (skb = np->rx_skbuff[entry], pkt_len); np->rx_skbuff[entry] = NULL; - } else if ((skb = netdev_alloc_skb(dev, pkt_len + 2))) { + } else if ((skb = netdev_alloc_skb_ip_align(dev, pkt_len))) { pci_dma_sync_single_for_cpu(np->pdev, desc_to_dma(desc), np->rx_buf_sz, PCI_DMA_FROMDEVICE); - /* 16 byte align the IP header */ - skb_reserve (skb, 2); skb_copy_to_linear_data (skb, np->rx_skbuff[entry]->data, pkt_len); @@ -907,7 +905,7 @@ receive_packet (struct net_device *dev) struct sk_buff *skb; /* Dropped packets don't need to re-allocate */ if (np->rx_skbuff[entry] == NULL) { - skb = netdev_alloc_skb(dev, np->rx_buf_sz); + skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz); if (skb == NULL) { np->rx_ring[entry].fraginfo = 0; printk (KERN_INFO @@ -917,8 +915,6 @@ receive_packet (struct net_device *dev) break; } np->rx_skbuff[entry] = skb; - /* 16 byte align the IP header */ - skb_reserve (skb, 2); np->rx_ring[entry].fraginfo = cpu_to_le64 (pci_map_single (np->pdev, skb->data, np->rx_buf_sz, diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index 31b8bef49d2e..0cbe3c0e7c06 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -100,6 +100,7 @@ typedef struct board_info { unsigned int flags; unsigned int in_suspend :1; + unsigned int wake_supported :1; int debug_level; enum dm9000_type type; @@ -116,6 +117,8 @@ typedef struct board_info { struct resource *data_req; struct resource *irq_res; + int irq_wake; + struct mutex addr_lock; /* phy and eeprom access lock */ struct delayed_work phy_poll; @@ -125,6 +128,7 @@ typedef struct board_info { struct mii_if_info mii; u32 msg_enable; + u32 wake_state; int rx_csum; int can_csum; @@ -568,6 +572,54 @@ static int dm9000_set_eeprom(struct net_device *dev, return 0; } +static void dm9000_get_wol(struct net_device *dev, struct ethtool_wolinfo *w) +{ + board_info_t *dm = to_dm9000_board(dev); + + memset(w, 0, sizeof(struct ethtool_wolinfo)); + + /* note, we could probably support wake-phy too */ + w->supported = dm->wake_supported ? WAKE_MAGIC : 0; + w->wolopts = dm->wake_state; +} + +static int dm9000_set_wol(struct net_device *dev, struct ethtool_wolinfo *w) +{ + board_info_t *dm = to_dm9000_board(dev); + unsigned long flags; + u32 opts = w->wolopts; + u32 wcr = 0; + + if (!dm->wake_supported) + return -EOPNOTSUPP; + + if (opts & ~WAKE_MAGIC) + return -EINVAL; + + if (opts & WAKE_MAGIC) + wcr |= WCR_MAGICEN; + + mutex_lock(&dm->addr_lock); + + spin_lock_irqsave(&dm->lock, flags); + iow(dm, DM9000_WCR, wcr); + spin_unlock_irqrestore(&dm->lock, flags); + + mutex_unlock(&dm->addr_lock); + + if (dm->wake_state != opts) { + /* change in wol state, update IRQ state */ + + if (!dm->wake_state) + set_irq_wake(dm->irq_wake, 1); + else if (dm->wake_state & !opts) + set_irq_wake(dm->irq_wake, 0); + } + + dm->wake_state = opts; + return 0; +} + static const struct ethtool_ops dm9000_ethtool_ops = { .get_drvinfo = dm9000_get_drvinfo, .get_settings = dm9000_get_settings, @@ -576,6 +628,8 @@ static const struct ethtool_ops dm9000_ethtool_ops = { .set_msglevel = dm9000_set_msglevel, .nway_reset = dm9000_nway_reset, .get_link = dm9000_get_link, + .get_wol = dm9000_get_wol, + .set_wol = dm9000_set_wol, .get_eeprom_len = dm9000_get_eeprom_len, .get_eeprom = dm9000_get_eeprom, .set_eeprom = dm9000_set_eeprom, @@ -722,6 +776,7 @@ dm9000_init_dm9000(struct net_device *dev) { board_info_t *db = netdev_priv(dev); unsigned int imr; + unsigned int ncr; dm9000_dbg(db, 1, "entering %s\n", __func__); @@ -736,8 +791,15 @@ dm9000_init_dm9000(struct net_device *dev) iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */ iow(db, DM9000_GPR, 0); /* Enable PHY */ - if (db->flags & DM9000_PLATF_EXT_PHY) - iow(db, DM9000_NCR, NCR_EXT_PHY); + ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0; + + /* if wol is needed, then always set NCR_WAKEEN otherwise we end + * up dumping the wake events if we disable this. There is already + * a wake-mask in DM9000_WCR */ + if (db->wake_supported) + ncr |= NCR_WAKEEN; + + iow(db, DM9000_NCR, ncr); /* Program operating register */ iow(db, DM9000_TCR, 0); /* TX Polling clear */ @@ -962,8 +1024,8 @@ dm9000_rx(struct net_device *dev) } /* Move data from DM9000 */ - if (GoodPacket - && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) { + if (GoodPacket && + ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) { skb_reserve(skb, 2); rdptr = (u8 *) skb_put(skb, RxLen - 4); @@ -1045,6 +1107,41 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + board_info_t *db = netdev_priv(dev); + unsigned long flags; + unsigned nsr, wcr; + + spin_lock_irqsave(&db->lock, flags); + + nsr = ior(db, DM9000_NSR); + wcr = ior(db, DM9000_WCR); + + dev_dbg(db->dev, "%s: NSR=0x%02x, WCR=0x%02x\n", __func__, nsr, wcr); + + if (nsr & NSR_WAKEST) { + /* clear, so we can avoid */ + iow(db, DM9000_NSR, NSR_WAKEST); + + if (wcr & WCR_LINKST) + dev_info(db->dev, "wake by link status change\n"); + if (wcr & WCR_SAMPLEST) + dev_info(db->dev, "wake by sample packet\n"); + if (wcr & WCR_MAGICST ) + dev_info(db->dev, "wake by magic packet\n"); + if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST))) + dev_err(db->dev, "wake signalled with no reason? " + "NSR=0x%02x, WSR=0x%02x\n", nsr, wcr); + + } + + spin_unlock_irqrestore(&db->lock, flags); + + return (nsr & NSR_WAKEST) ? IRQ_HANDLED : IRQ_NONE; +} + #ifdef CONFIG_NET_POLL_CONTROLLER /* *Used by netconsole @@ -1078,7 +1175,7 @@ dm9000_open(struct net_device *dev) irqflags |= IRQF_SHARED; - if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev)) + if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev)) return -EAGAIN; /* Initialize DM9000 board */ @@ -1299,6 +1396,29 @@ dm9000_probe(struct platform_device *pdev) goto out; } + db->irq_wake = platform_get_irq(pdev, 1); + if (db->irq_wake >= 0) { + dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake); + + ret = request_irq(db->irq_wake, dm9000_wol_interrupt, + IRQF_SHARED, dev_name(db->dev), ndev); + if (ret) { + dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret); + } else { + + /* test to see if irq is really wakeup capable */ + ret = set_irq_wake(db->irq_wake, 1); + if (ret) { + dev_err(db->dev, "irq %d cannot set wakeup (%d)\n", + db->irq_wake, ret); + ret = 0; + } else { + set_irq_wake(db->irq_wake, 0); + db->wake_supported = 1; + } + } + } + iosize = resource_size(db->addr_res); db->addr_req = request_mem_region(db->addr_res->start, iosize, pdev->name); @@ -1490,10 +1610,14 @@ dm9000_drv_suspend(struct device *dev) db = netdev_priv(ndev); db->in_suspend = 1; - if (netif_running(ndev)) { - netif_device_detach(ndev); + if (!netif_running(ndev)) + return 0; + + netif_device_detach(ndev); + + /* only shutdown if not using WoL */ + if (!db->wake_state) dm9000_shutdown(ndev); - } } return 0; } @@ -1506,10 +1630,13 @@ dm9000_drv_resume(struct device *dev) board_info_t *db = netdev_priv(ndev); if (ndev) { - if (netif_running(ndev)) { - dm9000_reset(db); - dm9000_init_dm9000(ndev); + /* reset if we were not in wake mode to ensure if + * the device was powered off it is in a known state */ + if (!db->wake_state) { + dm9000_reset(db); + dm9000_init_dm9000(ndev); + } netif_device_attach(ndev); } diff --git a/drivers/net/dm9000.h b/drivers/net/dm9000.h index fb1c924d79b4..55688bd1a3ef 100644 --- a/drivers/net/dm9000.h +++ b/drivers/net/dm9000.h @@ -111,6 +111,13 @@ #define RSR_CE (1<<1) #define RSR_FOE (1<<0) +#define WCR_LINKEN (1 << 5) +#define WCR_SAMPLEEN (1 << 4) +#define WCR_MAGICEN (1 << 3) +#define WCR_LINKST (1 << 2) +#define WCR_SAMPLEST (1 << 1) +#define WCR_MAGICST (1 << 0) + #define FCTR_HWOT(ot) (( ot & 0xf ) << 4 ) #define FCTR_LWOT(ot) ( ot & 0xf ) diff --git a/drivers/net/e100.c b/drivers/net/e100.c index d269a68ce354..929701ca07d3 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -624,6 +624,7 @@ struct nic { u16 eeprom_wc; __le16 eeprom[256]; spinlock_t mdio_lock; + const struct firmware *fw; }; static inline void e100_write_flush(struct nic *nic) @@ -1225,9 +1226,9 @@ static void e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb) static const struct firmware *e100_request_firmware(struct nic *nic) { const char *fw_name; - const struct firmware *fw; + const struct firmware *fw = nic->fw; u8 timer, bundle, min_size; - int err; + int err = 0; /* do not load u-code for ICH devices */ if (nic->flags & ich) @@ -1243,12 +1244,20 @@ static const struct firmware *e100_request_firmware(struct nic *nic) else /* No ucode on other devices */ return NULL; - err = request_firmware(&fw, fw_name, &nic->pdev->dev); + /* If the firmware has not previously been loaded, request a pointer + * to it. If it was previously loaded, we are reinitializing the + * adapter, possibly in a resume from hibernate, in which case + * request_firmware() cannot be used. + */ + if (!fw) + err = request_firmware(&fw, fw_name, &nic->pdev->dev); + if (err) { DPRINTK(PROBE, ERR, "Failed to load firmware \"%s\": %d\n", fw_name, err); return ERR_PTR(err); } + /* Firmware should be precisely UCODE_SIZE (words) plus three bytes indicating the offsets for BUNDLESMALL, BUNDLEMAX, INTDELAY */ if (fw->size != UCODE_SIZE * 4 + 3) { @@ -1271,7 +1280,10 @@ static const struct firmware *e100_request_firmware(struct nic *nic) release_firmware(fw); return ERR_PTR(-EINVAL); } - /* OK, firmware is validated and ready to use... */ + + /* OK, firmware is validated and ready to use. Save a pointer + * to it in the nic */ + nic->fw = fw; return fw; } @@ -1852,11 +1864,10 @@ static inline void e100_start_receiver(struct nic *nic, struct rx *rx) #define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN) static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) { - if (!(rx->skb = netdev_alloc_skb(nic->netdev, RFD_BUF_LEN + NET_IP_ALIGN))) + if (!(rx->skb = netdev_alloc_skb_ip_align(nic->netdev, RFD_BUF_LEN))) return -ENOMEM; - /* Align, init, and map the RFD. */ - skb_reserve(rx->skb, NET_IP_ALIGN); + /* Init, and map the RFD. */ skb_copy_to_linear_data(rx->skb, &nic->blank_rfd, sizeof(struct rfd)); rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 42e2b7e21c29..2a567df3ea71 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -167,6 +167,7 @@ struct e1000_buffer { unsigned long time_stamp; u16 length; u16 next_to_watch; + u16 mapped_as_page; }; struct e1000_tx_ring { @@ -302,7 +303,6 @@ struct e1000_adapter { /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; - struct net_device_stats net_stats; /* structs defined in e1000_hw.h */ struct e1000_hw hw; diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 490b2b7cd3ab..13e9ece16889 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -31,14 +31,22 @@ #include "e1000.h" #include <asm/uaccess.h> +enum {NETDEV_STATS, E1000_STATS}; + struct e1000_stats { char stat_string[ETH_GSTRING_LEN]; + int type; int sizeof_stat; int stat_offset; }; -#define E1000_STAT(m) FIELD_SIZEOF(struct e1000_adapter, m), \ - offsetof(struct e1000_adapter, m) +#define E1000_STAT(m) E1000_STATS, \ + sizeof(((struct e1000_adapter *)0)->m), \ + offsetof(struct e1000_adapter, m) +#define E1000_NETDEV_STAT(m) NETDEV_STATS, \ + sizeof(((struct net_device *)0)->m), \ + offsetof(struct net_device, m) + static const struct e1000_stats e1000_gstrings_stats[] = { { "rx_packets", E1000_STAT(stats.gprc) }, { "tx_packets", E1000_STAT(stats.gptc) }, @@ -50,19 +58,19 @@ static const struct e1000_stats e1000_gstrings_stats[] = { { "tx_multicast", E1000_STAT(stats.mptc) }, { "rx_errors", E1000_STAT(stats.rxerrc) }, { "tx_errors", E1000_STAT(stats.txerrc) }, - { "tx_dropped", E1000_STAT(net_stats.tx_dropped) }, + { "tx_dropped", E1000_NETDEV_STAT(stats.tx_dropped) }, { "multicast", E1000_STAT(stats.mprc) }, { "collisions", E1000_STAT(stats.colc) }, { "rx_length_errors", E1000_STAT(stats.rlerrc) }, - { "rx_over_errors", E1000_STAT(net_stats.rx_over_errors) }, + { "rx_over_errors", E1000_NETDEV_STAT(stats.rx_over_errors) }, { "rx_crc_errors", E1000_STAT(stats.crcerrs) }, - { "rx_frame_errors", E1000_STAT(net_stats.rx_frame_errors) }, + { "rx_frame_errors", E1000_NETDEV_STAT(stats.rx_frame_errors) }, { "rx_no_buffer_count", E1000_STAT(stats.rnbc) }, { "rx_missed_errors", E1000_STAT(stats.mpc) }, { "tx_aborted_errors", E1000_STAT(stats.ecol) }, { "tx_carrier_errors", E1000_STAT(stats.tncrs) }, - { "tx_fifo_errors", E1000_STAT(net_stats.tx_fifo_errors) }, - { "tx_heartbeat_errors", E1000_STAT(net_stats.tx_heartbeat_errors) }, + { "tx_fifo_errors", E1000_NETDEV_STAT(stats.tx_fifo_errors) }, + { "tx_heartbeat_errors", E1000_NETDEV_STAT(stats.tx_heartbeat_errors) }, { "tx_window_errors", E1000_STAT(stats.latecol) }, { "tx_abort_late_coll", E1000_STAT(stats.latecol) }, { "tx_deferred_ok", E1000_STAT(stats.dc) }, @@ -861,10 +869,10 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data) /* NOTE: we don't test MSI interrupts here, yet */ /* Hook up test interrupt handler just for this test */ - if (!request_irq(irq, &e1000_test_intr, IRQF_PROBE_SHARED, netdev->name, + if (!request_irq(irq, e1000_test_intr, IRQF_PROBE_SHARED, netdev->name, netdev)) shared_int = false; - else if (request_irq(irq, &e1000_test_intr, IRQF_SHARED, + else if (request_irq(irq, e1000_test_intr, IRQF_SHARED, netdev->name, netdev)) { *data = 1; return -1; @@ -1830,10 +1838,21 @@ static void e1000_get_ethtool_stats(struct net_device *netdev, { struct e1000_adapter *adapter = netdev_priv(netdev); int i; + char *p = NULL; e1000_update_stats(adapter); for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) { - char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset; + switch (e1000_gstrings_stats[i].type) { + case NETDEV_STATS: + p = (char *) netdev + + e1000_gstrings_stats[i].stat_offset; + break; + case E1000_STATS: + p = (char *) adapter + + e1000_gstrings_stats[i].stat_offset; + break; + } + data[i] = (e1000_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index bcd192ca47b0..7e855f9bbd97 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -1839,10 +1839,17 @@ void e1000_free_all_tx_resources(struct e1000_adapter *adapter) static void e1000_unmap_and_free_tx_resource(struct e1000_adapter *adapter, struct e1000_buffer *buffer_info) { - buffer_info->dma = 0; + if (buffer_info->dma) { + if (buffer_info->mapped_as_page) + pci_unmap_page(adapter->pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + else + pci_unmap_single(adapter->pdev, buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + buffer_info->dma = 0; + } if (buffer_info->skb) { - skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb, - DMA_TO_DEVICE); dev_kfree_skb_any(buffer_info->skb); buffer_info->skb = NULL; } @@ -2683,22 +2690,14 @@ static int e1000_tx_map(struct e1000_adapter *adapter, unsigned int mss) { struct e1000_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; struct e1000_buffer *buffer_info; unsigned int len = skb_headlen(skb); - unsigned int offset, size, count = 0, i; + unsigned int offset = 0, size, count = 0, i; unsigned int f; - dma_addr_t *map; i = tx_ring->next_to_use; - if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) { - dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); - return 0; - } - - map = skb_shinfo(skb)->dma_maps; - offset = 0; - while (len) { buffer_info = &tx_ring->buffer_info[i]; size = min(len, max_per_txd); @@ -2735,7 +2734,11 @@ static int e1000_tx_map(struct e1000_adapter *adapter, buffer_info->length = size; /* set time_stamp *before* dma to help avoid a possible race */ buffer_info->time_stamp = jiffies; - buffer_info->dma = skb_shinfo(skb)->dma_head + offset; + buffer_info->mapped_as_page = false; + buffer_info->dma = pci_map_single(pdev, skb->data + offset, + size, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; buffer_info->next_to_watch = i; len -= size; @@ -2753,7 +2756,7 @@ static int e1000_tx_map(struct e1000_adapter *adapter, frag = &skb_shinfo(skb)->frags[f]; len = frag->size; - offset = 0; + offset = frag->page_offset; while (len) { i++; @@ -2777,7 +2780,12 @@ static int e1000_tx_map(struct e1000_adapter *adapter, buffer_info->length = size; buffer_info->time_stamp = jiffies; - buffer_info->dma = map[f] + offset; + buffer_info->mapped_as_page = true; + buffer_info->dma = pci_map_page(pdev, frag->page, + offset, size, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; buffer_info->next_to_watch = i; len -= size; @@ -2790,6 +2798,22 @@ static int e1000_tx_map(struct e1000_adapter *adapter, tx_ring->buffer_info[first].next_to_watch = i; return count; + +dma_error: + dev_err(&pdev->dev, "TX DMA map failed\n"); + buffer_info->dma = 0; + count--; + + while (count >= 0) { + count--; + i--; + if (i < 0) + i += tx_ring->count; + buffer_info = &tx_ring->buffer_info[i]; + e1000_unmap_and_free_tx_resource(adapter, buffer_info); + } + + return 0; } static void e1000_tx_queue(struct e1000_adapter *adapter, @@ -3101,10 +3125,8 @@ static void e1000_reset_task(struct work_struct *work) static struct net_device_stats *e1000_get_stats(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev_priv(netdev); - /* only return the current stats */ - return &adapter->net_stats; + return &netdev->stats; } /** @@ -3196,6 +3218,7 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) void e1000_update_stats(struct e1000_adapter *adapter) { + struct net_device *netdev = adapter->netdev; struct e1000_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; unsigned long flags; @@ -3288,32 +3311,32 @@ void e1000_update_stats(struct e1000_adapter *adapter) } /* Fill out the OS statistics structure */ - adapter->net_stats.multicast = adapter->stats.mprc; - adapter->net_stats.collisions = adapter->stats.colc; + netdev->stats.multicast = adapter->stats.mprc; + netdev->stats.collisions = adapter->stats.colc; /* Rx Errors */ /* RLEC on some newer hardware can be incorrect so build * our own version based on RUC and ROC */ - adapter->net_stats.rx_errors = adapter->stats.rxerrc + + netdev->stats.rx_errors = adapter->stats.rxerrc + adapter->stats.crcerrs + adapter->stats.algnerrc + adapter->stats.ruc + adapter->stats.roc + adapter->stats.cexterr; adapter->stats.rlerrc = adapter->stats.ruc + adapter->stats.roc; - adapter->net_stats.rx_length_errors = adapter->stats.rlerrc; - adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs; - adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc; - adapter->net_stats.rx_missed_errors = adapter->stats.mpc; + netdev->stats.rx_length_errors = adapter->stats.rlerrc; + netdev->stats.rx_crc_errors = adapter->stats.crcerrs; + netdev->stats.rx_frame_errors = adapter->stats.algnerrc; + netdev->stats.rx_missed_errors = adapter->stats.mpc; /* Tx Errors */ adapter->stats.txerrc = adapter->stats.ecol + adapter->stats.latecol; - adapter->net_stats.tx_errors = adapter->stats.txerrc; - adapter->net_stats.tx_aborted_errors = adapter->stats.ecol; - adapter->net_stats.tx_window_errors = adapter->stats.latecol; - adapter->net_stats.tx_carrier_errors = adapter->stats.tncrs; + netdev->stats.tx_errors = adapter->stats.txerrc; + netdev->stats.tx_aborted_errors = adapter->stats.ecol; + netdev->stats.tx_window_errors = adapter->stats.latecol; + netdev->stats.tx_carrier_errors = adapter->stats.tncrs; if (hw->bad_tx_carr_stats_fd && adapter->link_duplex == FULL_DUPLEX) { - adapter->net_stats.tx_carrier_errors = 0; + netdev->stats.tx_carrier_errors = 0; adapter->stats.tncrs = 0; } @@ -3484,8 +3507,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, adapter->detect_tx_hung = false; if (tx_ring->buffer_info[eop].time_stamp && time_after(jiffies, tx_ring->buffer_info[eop].time_stamp + - (adapter->tx_timeout_factor * HZ)) - && !(er32(STATUS) & E1000_STATUS_TXOFF)) { + (adapter->tx_timeout_factor * HZ)) && + !(er32(STATUS) & E1000_STATUS_TXOFF)) { /* detected Tx unit hang */ DPRINTK(DRV, ERR, "Detected Tx Unit Hang\n" @@ -3514,8 +3537,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, } adapter->total_tx_bytes += total_tx_bytes; adapter->total_tx_packets += total_tx_packets; - adapter->net_stats.tx_bytes += total_tx_bytes; - adapter->net_stats.tx_packets += total_tx_packets; + netdev->stats.tx_bytes += total_tx_bytes; + netdev->stats.tx_packets += total_tx_packets; return (count < tx_ring->count); } @@ -3767,8 +3790,8 @@ next_desc: adapter->total_rx_packets += total_rx_packets; adapter->total_rx_bytes += total_rx_bytes; - adapter->net_stats.rx_bytes += total_rx_bytes; - adapter->net_stats.rx_packets += total_rx_packets; + netdev->stats.rx_bytes += total_rx_bytes; + netdev->stats.rx_packets += total_rx_packets; return cleaned; } @@ -3867,9 +3890,8 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, * of reassembly being done in the stack */ if (length < copybreak) { struct sk_buff *new_skb = - netdev_alloc_skb(netdev, length + NET_IP_ALIGN); + netdev_alloc_skb_ip_align(netdev, length); if (new_skb) { - skb_reserve(new_skb, NET_IP_ALIGN); skb_copy_to_linear_data_offset(new_skb, -NET_IP_ALIGN, (skb->data - @@ -3916,8 +3938,8 @@ next_desc: adapter->total_rx_packets += total_rx_packets; adapter->total_rx_bytes += total_rx_bytes; - adapter->net_stats.rx_bytes += total_rx_bytes; - adapter->net_stats.rx_packets += total_rx_packets; + netdev->stats.rx_bytes += total_rx_bytes; + netdev->stats.rx_packets += total_rx_packets; return cleaned; } @@ -3938,9 +3960,7 @@ e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter, struct e1000_buffer *buffer_info; struct sk_buff *skb; unsigned int i; - unsigned int bufsz = 256 - - 16 /*for skb_reserve */ - - NET_IP_ALIGN; + unsigned int bufsz = 256 - 16 /*for skb_reserve */ ; i = rx_ring->next_to_use; buffer_info = &rx_ring->buffer_info[i]; @@ -3952,7 +3972,7 @@ e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter, goto check_page; } - skb = netdev_alloc_skb(netdev, bufsz); + skb = netdev_alloc_skb_ip_align(netdev, bufsz); if (unlikely(!skb)) { /* Better luck next round */ adapter->alloc_rx_buff_failed++; @@ -3965,7 +3985,7 @@ e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter, DPRINTK(PROBE, ERR, "skb align check failed: %u bytes " "at %p\n", bufsz, skb->data); /* Try again, without freeing the previous */ - skb = netdev_alloc_skb(netdev, bufsz); + skb = netdev_alloc_skb_ip_align(netdev, bufsz); /* Failed allocation, critical failure */ if (!skb) { dev_kfree_skb(oldskb); @@ -3983,12 +4003,6 @@ e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter, /* Use new allocation */ dev_kfree_skb(oldskb); } - /* Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; buffer_info->length = adapter->rx_buffer_len; check_page: @@ -4045,7 +4059,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter, struct e1000_buffer *buffer_info; struct sk_buff *skb; unsigned int i; - unsigned int bufsz = adapter->rx_buffer_len + NET_IP_ALIGN; + unsigned int bufsz = adapter->rx_buffer_len; i = rx_ring->next_to_use; buffer_info = &rx_ring->buffer_info[i]; @@ -4057,7 +4071,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter, goto map_skb; } - skb = netdev_alloc_skb(netdev, bufsz); + skb = netdev_alloc_skb_ip_align(netdev, bufsz); if (unlikely(!skb)) { /* Better luck next round */ adapter->alloc_rx_buff_failed++; @@ -4070,7 +4084,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter, DPRINTK(RX_ERR, ERR, "skb align check failed: %u bytes " "at %p\n", bufsz, skb->data); /* Try again, without freeing the previous */ - skb = netdev_alloc_skb(netdev, bufsz); + skb = netdev_alloc_skb_ip_align(netdev, bufsz); /* Failed allocation, critical failure */ if (!skb) { dev_kfree_skb(oldskb); @@ -4089,12 +4103,6 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter, /* Use new allocation */ dev_kfree_skb(oldskb); } - /* Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; buffer_info->length = adapter->rx_buffer_len; map_skb: diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c index d1e0563a67df..c1a42cfc80ba 100644 --- a/drivers/net/e1000e/82571.c +++ b/drivers/net/e1000e/82571.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -43,10 +43,6 @@ * 82583V Gigabit Network Connection */ -#include <linux/netdevice.h> -#include <linux/delay.h> -#include <linux/pci.h> - #include "e1000.h" #define ID_LED_RESERVED_F746 0xF746 @@ -69,15 +65,15 @@ static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw); static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw); static s32 e1000_setup_link_82571(struct e1000_hw *hw); static void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw); +static void e1000_clear_vfta_82571(struct e1000_hw *hw); static bool e1000_check_mng_mode_82574(struct e1000_hw *hw); static s32 e1000_led_on_82574(struct e1000_hw *hw); static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw); +static void e1000_power_down_phy_copper_82571(struct e1000_hw *hw); /** * e1000_init_phy_params_82571 - Init PHY func ptrs. * @hw: pointer to the HW structure - * - * This is a function pointer entry point called by the api module. **/ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw) { @@ -93,6 +89,9 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw) phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; phy->reset_delay_us = 100; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_82571; + switch (hw->mac.type) { case e1000_82571: case e1000_82572: @@ -140,8 +139,6 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw) /** * e1000_init_nvm_params_82571 - Init NVM func ptrs. * @hw: pointer to the HW structure - * - * This is a function pointer entry point called by the api module. **/ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw) { @@ -205,8 +202,6 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw) /** * e1000_init_mac_params_82571 - Init MAC func ptrs. * @hw: pointer to the HW structure - * - * This is a function pointer entry point called by the api module. **/ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter) { @@ -240,7 +235,8 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter) /* Set rar entry count */ mac->rar_entry_count = E1000_RAR_ENTRIES; /* Set if manageability features are enabled. */ - mac->arc_subsystem_valid = (er32(FWSM) & E1000_FWSM_MODE_MASK) ? 1 : 0; + mac->arc_subsystem_valid = (er32(FWSM) & E1000_FWSM_MODE_MASK) + ? true : false; /* check for link */ switch (hw->phy.media_type) { @@ -313,7 +309,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter) * indicates that the bootagent or EFI code has * improperly left this bit enabled */ - hw_dbg(hw, "Please update your 82571 Bootagent\n"); + e_dbg("Please update your 82571 Bootagent\n"); } ew32(SWSM, swsm & ~E1000_SWSM_SMBI); } @@ -487,7 +483,7 @@ static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw) } if (i == sw_timeout) { - hw_dbg(hw, "Driver can't access device - SMBI bit is set.\n"); + e_dbg("Driver can't access device - SMBI bit is set.\n"); hw->dev_spec.e82571.smb_counter++; } /* Get the FW semaphore. */ @@ -505,7 +501,7 @@ static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw) if (i == fw_timeout) { /* Release semaphores */ e1000_put_hw_semaphore_82571(hw); - hw_dbg(hw, "Driver can't access the NVM\n"); + e_dbg("Driver can't access the NVM\n"); return -E1000_ERR_NVM; } @@ -702,8 +698,7 @@ static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { struct e1000_nvm_info *nvm = &hw->nvm; - u32 i; - u32 eewr = 0; + u32 i, eewr = 0; s32 ret_val = 0; /* @@ -712,7 +707,7 @@ static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset, */ if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || (words == 0)) { - hw_dbg(hw, "nvm parameter(s) out of bounds\n"); + e_dbg("nvm parameter(s) out of bounds\n"); return -E1000_ERR_NVM; } @@ -753,7 +748,7 @@ static s32 e1000_get_cfg_done_82571(struct e1000_hw *hw) timeout--; } if (!timeout) { - hw_dbg(hw, "MNG configuration cycle has not completed.\n"); + e_dbg("MNG configuration cycle has not completed.\n"); return -E1000_ERR_RESET; } @@ -763,7 +758,7 @@ static s32 e1000_get_cfg_done_82571(struct e1000_hw *hw) /** * e1000_set_d0_lplu_state_82571 - Set Low Power Linkup D0 state * @hw: pointer to the HW structure - * @active: TRUE to enable LPLU, FALSE to disable + * @active: true to enable LPLU, false to disable * * Sets the LPLU D0 state according to the active flag. When activating LPLU * this function also disables smart speed and vice versa. LPLU will not be @@ -834,15 +829,11 @@ static s32 e1000_set_d0_lplu_state_82571(struct e1000_hw *hw, bool active) * e1000_reset_hw_82571 - Reset hardware * @hw: pointer to the HW structure * - * This resets the hardware into a known state. This is a - * function pointer entry point called by the api module. + * This resets the hardware into a known state. **/ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) { - u32 ctrl; - u32 extcnf_ctrl; - u32 ctrl_ext; - u32 icr; + u32 ctrl, extcnf_ctrl, ctrl_ext, icr; s32 ret_val; u16 i = 0; @@ -852,9 +843,9 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) */ ret_val = e1000e_disable_pcie_master(hw); if (ret_val) - hw_dbg(hw, "PCI-E Master disable polling has failed.\n"); + e_dbg("PCI-E Master disable polling has failed.\n"); - hw_dbg(hw, "Masking off all interrupts\n"); + e_dbg("Masking off all interrupts\n"); ew32(IMC, 0xffffffff); ew32(RCTL, 0); @@ -893,7 +884,7 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) ctrl = er32(CTRL); - hw_dbg(hw, "Issuing a global reset to MAC\n"); + e_dbg("Issuing a global reset to MAC\n"); ew32(CTRL, ctrl | E1000_CTRL_RST); if (hw->nvm.type == e1000_nvm_flash_hw) { @@ -951,21 +942,19 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw) struct e1000_mac_info *mac = &hw->mac; u32 reg_data; s32 ret_val; - u16 i; - u16 rar_count = mac->rar_entry_count; + u16 i, rar_count = mac->rar_entry_count; e1000_initialize_hw_bits_82571(hw); /* Initialize identification LED */ ret_val = e1000e_id_led_init(hw); - if (ret_val) { - hw_dbg(hw, "Error initializing identification LED\n"); - return ret_val; - } + if (ret_val) + e_dbg("Error initializing identification LED\n"); + /* This is not fatal and we should not stop init due to this */ /* Disabling VLAN filtering */ - hw_dbg(hw, "Initializing the IEEE VLAN\n"); - e1000e_clear_vfta(hw); + e_dbg("Initializing the IEEE VLAN\n"); + mac->ops.clear_vfta(hw); /* Setup the receive address. */ /* @@ -978,7 +967,7 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw) e1000e_init_rx_addrs(hw, rar_count); /* Zero out the Multicast HASH table */ - hw_dbg(hw, "Zeroing the MTA\n"); + e_dbg("Zeroing the MTA\n"); for (i = 0; i < mac->mta_reg_count; i++) E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); @@ -1125,6 +1114,13 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) reg |= (1 << 22); ew32(GCR, reg); + /* + * Workaround for hardware errata. + * apply workaround for hardware errata documented in errata + * docs Fixes issue where some error prone or unreliable PCIe + * completions are occurring, particularly with ASPM enabled. + * Without fix, issue can cause tx timeouts. + */ reg = er32(GCR2); reg |= 1; ew32(GCR2, reg); @@ -1137,13 +1133,13 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) } /** - * e1000e_clear_vfta - Clear VLAN filter table + * e1000_clear_vfta_82571 - Clear VLAN filter table * @hw: pointer to the HW structure * * Clears the register array which contains the VLAN filter table by * setting all the values to 0. **/ -void e1000e_clear_vfta(struct e1000_hw *hw) +static void e1000_clear_vfta_82571(struct e1000_hw *hw) { u32 offset; u32 vfta_value = 0; @@ -1360,8 +1356,20 @@ static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw) * e1000_check_for_serdes_link_82571 - Check for link (Serdes) * @hw: pointer to the HW structure * - * Checks for link up on the hardware. If link is not up and we have - * a signal, then we need to force link up. + * Reports the link state as up or down. + * + * If autonegotiation is supported by the link partner, the link state is + * determined by the result of autonegotiation. This is the most likely case. + * If autonegotiation is not supported by the link partner, and the link + * has a valid signal, force the link up. + * + * The link state is represented internally here by 4 states: + * + * 1) down + * 2) autoneg_progress + * 3) autoneg_complete (the link sucessfully autonegotiated) + * 4) forced_up (the link has been forced up, it did not autonegotiate) + * **/ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) { @@ -1387,7 +1395,8 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) */ mac->serdes_link_state = e1000_serdes_link_autoneg_progress; - hw_dbg(hw, "AN_UP -> AN_PROG\n"); + mac->serdes_has_link = false; + e_dbg("AN_UP -> AN_PROG\n"); } break; @@ -1401,79 +1410,86 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) if (rxcw & E1000_RXCW_C) { /* Enable autoneg, and unforce link up */ ew32(TXCW, mac->txcw); - ew32(CTRL, - (ctrl & ~E1000_CTRL_SLU)); + ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); mac->serdes_link_state = e1000_serdes_link_autoneg_progress; - hw_dbg(hw, "FORCED_UP -> AN_PROG\n"); + mac->serdes_has_link = false; + e_dbg("FORCED_UP -> AN_PROG\n"); } break; case e1000_serdes_link_autoneg_progress: - /* - * If the LU bit is set in the STATUS register, - * autoneg has completed sucessfully. If not, - * try foring the link because the far end may be - * available but not capable of autonegotiation. - */ - if (status & E1000_STATUS_LU) { - mac->serdes_link_state = - e1000_serdes_link_autoneg_complete; - hw_dbg(hw, "AN_PROG -> AN_UP\n"); + if (rxcw & E1000_RXCW_C) { + /* + * We received /C/ ordered sets, meaning the + * link partner has autonegotiated, and we can + * trust the Link Up (LU) status bit. + */ + if (status & E1000_STATUS_LU) { + mac->serdes_link_state = + e1000_serdes_link_autoneg_complete; + e_dbg("AN_PROG -> AN_UP\n"); + mac->serdes_has_link = true; + } else { + /* Autoneg completed, but failed. */ + mac->serdes_link_state = + e1000_serdes_link_down; + e_dbg("AN_PROG -> DOWN\n"); + } } else { /* - * Disable autoneg, force link up and - * full duplex, and change state to forced + * The link partner did not autoneg. + * Force link up and full duplex, and change + * state to forced. */ - ew32(TXCW, - (mac->txcw & ~E1000_TXCW_ANE)); + ew32(TXCW, (mac->txcw & ~E1000_TXCW_ANE)); ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); ew32(CTRL, ctrl); /* Configure Flow Control after link up. */ - ret_val = - e1000e_config_fc_after_link_up(hw); + ret_val = e1000e_config_fc_after_link_up(hw); if (ret_val) { - hw_dbg(hw, "Error config flow control\n"); + e_dbg("Error config flow control\n"); break; } mac->serdes_link_state = e1000_serdes_link_forced_up; - hw_dbg(hw, "AN_PROG -> FORCED_UP\n"); + mac->serdes_has_link = true; + e_dbg("AN_PROG -> FORCED_UP\n"); } - mac->serdes_has_link = true; break; case e1000_serdes_link_down: default: - /* The link was down but the receiver has now gained + /* + * The link was down but the receiver has now gained * valid sync, so lets see if we can bring the link - * up. */ + * up. + */ ew32(TXCW, mac->txcw); - ew32(CTRL, - (ctrl & ~E1000_CTRL_SLU)); + ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); mac->serdes_link_state = e1000_serdes_link_autoneg_progress; - hw_dbg(hw, "DOWN -> AN_PROG\n"); + e_dbg("DOWN -> AN_PROG\n"); break; } } else { if (!(rxcw & E1000_RXCW_SYNCH)) { mac->serdes_has_link = false; mac->serdes_link_state = e1000_serdes_link_down; - hw_dbg(hw, "ANYSTATE -> DOWN\n"); + e_dbg("ANYSTATE -> DOWN\n"); } else { /* - * We have sync, and can tolerate one - * invalid (IV) codeword before declaring - * link down, so reread to look again + * We have sync, and can tolerate one invalid (IV) + * codeword before declaring link down, so reread + * to look again. */ udelay(10); rxcw = er32(RXCW); if (rxcw & E1000_RXCW_IV) { mac->serdes_link_state = e1000_serdes_link_down; mac->serdes_has_link = false; - hw_dbg(hw, "ANYSTATE -> DOWN\n"); + e_dbg("ANYSTATE -> DOWN\n"); } } } @@ -1495,7 +1511,7 @@ static s32 e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data) ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } @@ -1525,7 +1541,7 @@ static s32 e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data) bool e1000e_get_laa_state_82571(struct e1000_hw *hw) { if (hw->mac.type != e1000_82571) - return 0; + return false; return hw->dev_spec.e82571.laa_is_present; } @@ -1535,7 +1551,7 @@ bool e1000e_get_laa_state_82571(struct e1000_hw *hw) * @hw: pointer to the HW structure * @state: enable/disable locally administered address * - * Enable/Disable the current locally administers address state. + * Enable/Disable the current locally administered address state. **/ void e1000e_set_laa_state_82571(struct e1000_hw *hw, bool state) { @@ -1609,6 +1625,28 @@ static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw) } /** + * e1000_power_down_phy_copper_82571 - Remove link during PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, remove the link. + **/ +static void e1000_power_down_phy_copper_82571(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_mac_info *mac = &hw->mac; + + if (!(phy->ops.check_reset_block)) + return; + + /* If the management interface is not enabled, then power down */ + if (!(mac->ops.check_mng_mode(hw) || phy->ops.check_reset_block(hw))) + e1000_power_down_phy_copper(hw); + + return; +} + +/** * e1000_clear_hw_cntrs_82571 - Clear device specific hardware counters * @hw: pointer to the HW structure * @@ -1616,44 +1654,42 @@ static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw) **/ static void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw) { - u32 temp; - e1000e_clear_hw_cntrs_base(hw); - temp = er32(PRC64); - temp = er32(PRC127); - temp = er32(PRC255); - temp = er32(PRC511); - temp = er32(PRC1023); - temp = er32(PRC1522); - temp = er32(PTC64); - temp = er32(PTC127); - temp = er32(PTC255); - temp = er32(PTC511); - temp = er32(PTC1023); - temp = er32(PTC1522); - - temp = er32(ALGNERRC); - temp = er32(RXERRC); - temp = er32(TNCRS); - temp = er32(CEXTERR); - temp = er32(TSCTC); - temp = er32(TSCTFC); - - temp = er32(MGTPRC); - temp = er32(MGTPDC); - temp = er32(MGTPTC); - - temp = er32(IAC); - temp = er32(ICRXOC); - - temp = er32(ICRXPTC); - temp = er32(ICRXATC); - temp = er32(ICTXPTC); - temp = er32(ICTXATC); - temp = er32(ICTXQEC); - temp = er32(ICTXQMTC); - temp = er32(ICRXDMTC); + er32(PRC64); + er32(PRC127); + er32(PRC255); + er32(PRC511); + er32(PRC1023); + er32(PRC1522); + er32(PTC64); + er32(PTC127); + er32(PTC255); + er32(PTC511); + er32(PTC1023); + er32(PTC1522); + + er32(ALGNERRC); + er32(RXERRC); + er32(TNCRS); + er32(CEXTERR); + er32(TSCTC); + er32(TSCTFC); + + er32(MGTPRC); + er32(MGTPDC); + er32(MGTPTC); + + er32(IAC); + er32(ICRXOC); + + er32(ICRXPTC); + er32(ICRXATC); + er32(ICTXPTC); + er32(ICTXATC); + er32(ICTXQEC); + er32(ICTXQMTC); + er32(ICRXDMTC); } static struct e1000_mac_operations e82571_mac_ops = { @@ -1667,6 +1703,8 @@ static struct e1000_mac_operations e82571_mac_ops = { /* .led_on: mac type dependent */ .led_off = e1000e_led_off_generic, .update_mc_addr_list = e1000_update_mc_addr_list_82571, + .write_vfta = e1000_write_vfta_generic, + .clear_vfta = e1000_clear_vfta_82571, .reset_hw = e1000_reset_hw_82571, .init_hw = e1000_init_hw_82571, .setup_link = e1000_setup_link_82571, @@ -1675,64 +1713,67 @@ static struct e1000_mac_operations e82571_mac_ops = { }; static struct e1000_phy_operations e82_phy_ops_igp = { - .acquire_phy = e1000_get_hw_semaphore_82571, + .acquire = e1000_get_hw_semaphore_82571, + .check_polarity = e1000_check_polarity_igp, .check_reset_block = e1000e_check_reset_block_generic, - .commit_phy = NULL, + .commit = NULL, .force_speed_duplex = e1000e_phy_force_speed_duplex_igp, .get_cfg_done = e1000_get_cfg_done_82571, .get_cable_length = e1000e_get_cable_length_igp_2, - .get_phy_info = e1000e_get_phy_info_igp, - .read_phy_reg = e1000e_read_phy_reg_igp, - .release_phy = e1000_put_hw_semaphore_82571, - .reset_phy = e1000e_phy_hw_reset_generic, + .get_info = e1000e_get_phy_info_igp, + .read_reg = e1000e_read_phy_reg_igp, + .release = e1000_put_hw_semaphore_82571, + .reset = e1000e_phy_hw_reset_generic, .set_d0_lplu_state = e1000_set_d0_lplu_state_82571, .set_d3_lplu_state = e1000e_set_d3_lplu_state, - .write_phy_reg = e1000e_write_phy_reg_igp, + .write_reg = e1000e_write_phy_reg_igp, .cfg_on_link_up = NULL, }; static struct e1000_phy_operations e82_phy_ops_m88 = { - .acquire_phy = e1000_get_hw_semaphore_82571, + .acquire = e1000_get_hw_semaphore_82571, + .check_polarity = e1000_check_polarity_m88, .check_reset_block = e1000e_check_reset_block_generic, - .commit_phy = e1000e_phy_sw_reset, + .commit = e1000e_phy_sw_reset, .force_speed_duplex = e1000e_phy_force_speed_duplex_m88, .get_cfg_done = e1000e_get_cfg_done, .get_cable_length = e1000e_get_cable_length_m88, - .get_phy_info = e1000e_get_phy_info_m88, - .read_phy_reg = e1000e_read_phy_reg_m88, - .release_phy = e1000_put_hw_semaphore_82571, - .reset_phy = e1000e_phy_hw_reset_generic, + .get_info = e1000e_get_phy_info_m88, + .read_reg = e1000e_read_phy_reg_m88, + .release = e1000_put_hw_semaphore_82571, + .reset = e1000e_phy_hw_reset_generic, .set_d0_lplu_state = e1000_set_d0_lplu_state_82571, .set_d3_lplu_state = e1000e_set_d3_lplu_state, - .write_phy_reg = e1000e_write_phy_reg_m88, + .write_reg = e1000e_write_phy_reg_m88, .cfg_on_link_up = NULL, }; static struct e1000_phy_operations e82_phy_ops_bm = { - .acquire_phy = e1000_get_hw_semaphore_82571, + .acquire = e1000_get_hw_semaphore_82571, + .check_polarity = e1000_check_polarity_m88, .check_reset_block = e1000e_check_reset_block_generic, - .commit_phy = e1000e_phy_sw_reset, + .commit = e1000e_phy_sw_reset, .force_speed_duplex = e1000e_phy_force_speed_duplex_m88, .get_cfg_done = e1000e_get_cfg_done, .get_cable_length = e1000e_get_cable_length_m88, - .get_phy_info = e1000e_get_phy_info_m88, - .read_phy_reg = e1000e_read_phy_reg_bm2, - .release_phy = e1000_put_hw_semaphore_82571, - .reset_phy = e1000e_phy_hw_reset_generic, + .get_info = e1000e_get_phy_info_m88, + .read_reg = e1000e_read_phy_reg_bm2, + .release = e1000_put_hw_semaphore_82571, + .reset = e1000e_phy_hw_reset_generic, .set_d0_lplu_state = e1000_set_d0_lplu_state_82571, .set_d3_lplu_state = e1000e_set_d3_lplu_state, - .write_phy_reg = e1000e_write_phy_reg_bm2, + .write_reg = e1000e_write_phy_reg_bm2, .cfg_on_link_up = NULL, }; static struct e1000_nvm_operations e82571_nvm_ops = { - .acquire_nvm = e1000_acquire_nvm_82571, - .read_nvm = e1000e_read_nvm_eerd, - .release_nvm = e1000_release_nvm_82571, - .update_nvm = e1000_update_nvm_checksum_82571, + .acquire = e1000_acquire_nvm_82571, + .read = e1000e_read_nvm_eerd, + .release = e1000_release_nvm_82571, + .update = e1000_update_nvm_checksum_82571, .valid_led_default = e1000_valid_led_default_82571, - .validate_nvm = e1000_validate_nvm_checksum_82571, - .write_nvm = e1000_write_nvm_82571, + .validate = e1000_validate_nvm_checksum_82571, + .write = e1000_write_nvm_82571, }; struct e1000_info e1000_82571_info = { diff --git a/drivers/net/e1000e/defines.h b/drivers/net/e1000e/defines.h index 1190167a8b3d..86d2809763c3 100644 --- a/drivers/net/e1000e/defines.h +++ b/drivers/net/e1000e/defines.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h index 3e187b0e4203..cebbd9079d53 100644 --- a/drivers/net/e1000e/e1000.h +++ b/drivers/net/e1000e/e1000.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -36,6 +36,7 @@ #include <linux/workqueue.h> #include <linux/io.h> #include <linux/netdevice.h> +#include <linux/pci.h> #include "hw.h" @@ -47,9 +48,9 @@ struct e1000_info; #ifdef DEBUG #define e_dbg(format, arg...) \ - e_printk(KERN_DEBUG , adapter, format, ## arg) + e_printk(KERN_DEBUG , hw->adapter, format, ## arg) #else -#define e_dbg(format, arg...) do { (void)(adapter); } while (0) +#define e_dbg(format, arg...) do { (void)(hw); } while (0) #endif #define e_err(format, arg...) \ @@ -193,12 +194,15 @@ struct e1000_buffer { unsigned long time_stamp; u16 length; u16 next_to_watch; + u16 mapped_as_page; }; /* Rx */ - /* arrays of page information for packet split */ - struct e1000_ps_page *ps_pages; + struct { + /* arrays of page information for packet split */ + struct e1000_ps_page *ps_pages; + struct page *page; + }; }; - struct page *page; }; struct e1000_ring { @@ -331,7 +335,6 @@ struct e1000_adapter { /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; - struct net_device_stats net_stats; /* structs defined in e1000_hw.h */ struct e1000_hw hw; @@ -366,6 +369,7 @@ struct e1000_adapter { struct work_struct downshift_task; struct work_struct update_phy_task; struct work_struct led_blink_task; + struct work_struct print_hang_task; }; struct e1000_info { @@ -488,6 +492,7 @@ extern void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw, extern void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw); extern void e1000e_gig_downshift_workaround_ich8lan(struct e1000_hw *hw); extern void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw); +extern s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable); extern s32 e1000e_check_for_copper_link(struct e1000_hw *hw); extern s32 e1000e_check_for_fiber_link(struct e1000_hw *hw); @@ -507,7 +512,7 @@ extern s32 e1000e_setup_fiber_serdes_link(struct e1000_hw *hw); extern s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw); extern s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw); extern s32 e1000e_setup_link(struct e1000_hw *hw); -extern void e1000e_clear_vfta(struct e1000_hw *hw); +extern void e1000_clear_vfta_generic(struct e1000_hw *hw); extern void e1000e_init_rx_addrs(struct e1000_hw *hw, u16 rar_count); extern void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw, u8 *mc_addr_list, @@ -523,7 +528,7 @@ extern void e1000e_config_collision_dist(struct e1000_hw *hw); extern s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw); extern s32 e1000e_force_mac_fc(struct e1000_hw *hw); extern s32 e1000e_blink_led(struct e1000_hw *hw); -extern void e1000e_write_vfta(struct e1000_hw *hw, u32 offset, u32 value); +extern void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value); extern void e1000e_reset_adaptive(struct e1000_hw *hw); extern void e1000e_update_adaptive(struct e1000_hw *hw); @@ -566,6 +571,8 @@ extern s32 e1000e_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, extern s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, u32 usec_interval, bool *success); extern s32 e1000e_phy_reset_dsp(struct e1000_hw *hw); +extern void e1000_power_up_phy_copper(struct e1000_hw *hw); +extern void e1000_power_down_phy_copper(struct e1000_hw *hw); extern s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); extern s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); extern s32 e1000e_check_downshift(struct e1000_hw *hw); @@ -583,9 +590,15 @@ extern s32 e1000_get_phy_info_82577(struct e1000_hw *hw); extern s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw); extern s32 e1000_get_cable_length_82577(struct e1000_hw *hw); +extern s32 e1000_check_polarity_m88(struct e1000_hw *hw); +extern s32 e1000_get_phy_info_ife(struct e1000_hw *hw); +extern s32 e1000_check_polarity_ife(struct e1000_hw *hw); +extern s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw); +extern s32 e1000_check_polarity_igp(struct e1000_hw *hw); + static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw) { - return hw->phy.ops.reset_phy(hw); + return hw->phy.ops.reset(hw); } static inline s32 e1000_check_reset_block(struct e1000_hw *hw) @@ -595,12 +608,12 @@ static inline s32 e1000_check_reset_block(struct e1000_hw *hw) static inline s32 e1e_rphy(struct e1000_hw *hw, u32 offset, u16 *data) { - return hw->phy.ops.read_phy_reg(hw, offset, data); + return hw->phy.ops.read_reg(hw, offset, data); } static inline s32 e1e_wphy(struct e1000_hw *hw, u32 offset, u16 data) { - return hw->phy.ops.write_phy_reg(hw, offset, data); + return hw->phy.ops.write_reg(hw, offset, data); } static inline s32 e1000_get_cable_length(struct e1000_hw *hw) @@ -620,27 +633,27 @@ extern s32 e1000e_read_mac_addr(struct e1000_hw *hw); static inline s32 e1000_validate_nvm_checksum(struct e1000_hw *hw) { - return hw->nvm.ops.validate_nvm(hw); + return hw->nvm.ops.validate(hw); } static inline s32 e1000e_update_nvm_checksum(struct e1000_hw *hw) { - return hw->nvm.ops.update_nvm(hw); + return hw->nvm.ops.update(hw); } static inline s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { - return hw->nvm.ops.read_nvm(hw, offset, words, data); + return hw->nvm.ops.read(hw, offset, words, data); } static inline s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { - return hw->nvm.ops.write_nvm(hw, offset, words, data); + return hw->nvm.ops.write(hw, offset, words, data); } static inline s32 e1000_get_phy_info(struct e1000_hw *hw) { - return hw->phy.ops.get_phy_info(hw); + return hw->phy.ops.get_info(hw); } static inline s32 e1000e_check_mng_mode(struct e1000_hw *hw) diff --git a/drivers/net/e1000e/es2lan.c b/drivers/net/e1000e/es2lan.c index ae5d73689353..d2a104794609 100644 --- a/drivers/net/e1000e/es2lan.c +++ b/drivers/net/e1000e/es2lan.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -31,11 +31,6 @@ * 80003ES2LAN Gigabit Ethernet Controller (Serdes) */ -#include <linux/netdevice.h> -#include <linux/ethtool.h> -#include <linux/delay.h> -#include <linux/pci.h> - #include "e1000.h" #define E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL 0x00 @@ -104,6 +99,8 @@ */ static const u16 e1000_gg82563_cable_length_table[] = { 0, 60, 115, 150, 150, 60, 115, 150, 180, 180, 0xFF }; +#define GG82563_CABLE_LENGTH_TABLE_SIZE \ + ARRAY_SIZE(e1000_gg82563_cable_length_table) static s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw); static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask); @@ -117,12 +114,11 @@ static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, u16 *data); static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, u16 data); +static void e1000_power_down_phy_copper_80003es2lan(struct e1000_hw *hw); /** * e1000_init_phy_params_80003es2lan - Init ESB2 PHY func ptrs. * @hw: pointer to the HW structure - * - * This is a function pointer entry point called by the api module. **/ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw) { @@ -132,6 +128,9 @@ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw) if (hw->phy.media_type != e1000_media_type_copper) { phy->type = e1000_phy_none; return 0; + } else { + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_80003es2lan; } phy->addr = 1; @@ -152,8 +151,6 @@ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw) /** * e1000_init_nvm_params_80003es2lan - Init ESB2 NVM func ptrs. * @hw: pointer to the HW structure - * - * This is a function pointer entry point called by the api module. **/ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) { @@ -200,8 +197,6 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) /** * e1000_init_mac_params_80003es2lan - Init ESB2 MAC func ptrs. * @hw: pointer to the HW structure - * - * This is a function pointer entry point called by the api module. **/ static s32 e1000_init_mac_params_80003es2lan(struct e1000_adapter *adapter) { @@ -224,7 +219,8 @@ static s32 e1000_init_mac_params_80003es2lan(struct e1000_adapter *adapter) /* Set rar entry count */ mac->rar_entry_count = E1000_RAR_ENTRIES; /* Set if manageability features are enabled. */ - mac->arc_subsystem_valid = (er32(FWSM) & E1000_FWSM_MODE_MASK) ? 1 : 0; + mac->arc_subsystem_valid = (er32(FWSM) & E1000_FWSM_MODE_MASK) + ? true : false; /* check for link */ switch (hw->phy.media_type) { @@ -272,8 +268,7 @@ static s32 e1000_get_variants_80003es2lan(struct e1000_adapter *adapter) * e1000_acquire_phy_80003es2lan - Acquire rights to access PHY * @hw: pointer to the HW structure * - * A wrapper to acquire access rights to the correct PHY. This is a - * function pointer entry point called by the api module. + * A wrapper to acquire access rights to the correct PHY. **/ static s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw) { @@ -287,8 +282,7 @@ static s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw) * e1000_release_phy_80003es2lan - Release rights to access PHY * @hw: pointer to the HW structure * - * A wrapper to release access rights to the correct PHY. This is a - * function pointer entry point called by the api module. + * A wrapper to release access rights to the correct PHY. **/ static void e1000_release_phy_80003es2lan(struct e1000_hw *hw) { @@ -333,8 +327,7 @@ static void e1000_release_mac_csr_80003es2lan(struct e1000_hw *hw) * e1000_acquire_nvm_80003es2lan - Acquire rights to access NVM * @hw: pointer to the HW structure * - * Acquire the semaphore to access the EEPROM. This is a function - * pointer entry point called by the api module. + * Acquire the semaphore to access the EEPROM. **/ static s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw) { @@ -356,8 +349,7 @@ static s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw) * e1000_release_nvm_80003es2lan - Relinquish rights to access NVM * @hw: pointer to the HW structure * - * Release the semaphore used to access the EEPROM. This is a - * function pointer entry point called by the api module. + * Release the semaphore used to access the EEPROM. **/ static void e1000_release_nvm_80003es2lan(struct e1000_hw *hw) { @@ -399,8 +391,7 @@ static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask) } if (i == timeout) { - hw_dbg(hw, - "Driver can't access resource, SW_FW_SYNC timeout.\n"); + e_dbg("Driver can't access resource, SW_FW_SYNC timeout.\n"); return -E1000_ERR_SWFW_SYNC; } @@ -440,8 +431,7 @@ static void e1000_release_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask) * @offset: offset of the register to read * @data: pointer to the data returned from the operation * - * Read the GG82563 PHY register. This is a function pointer entry - * point called by the api module. + * Read the GG82563 PHY register. **/ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, u32 offset, u16 *data) @@ -505,8 +495,7 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, * @offset: offset of the register to read * @data: value to write to the register * - * Write to the GG82563 PHY register. This is a function pointer entry - * point called by the api module. + * Write to the GG82563 PHY register. **/ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, u32 offset, u16 data) @@ -571,8 +560,7 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, * @words: number of words to write * @data: buffer of data to write to the NVM * - * Write "words" of data to the ESB2 NVM. This is a function - * pointer entry point called by the api module. + * Write "words" of data to the ESB2 NVM. **/ static s32 e1000_write_nvm_80003es2lan(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) @@ -602,7 +590,7 @@ static s32 e1000_get_cfg_done_80003es2lan(struct e1000_hw *hw) timeout--; } if (!timeout) { - hw_dbg(hw, "MNG configuration cycle has not completed.\n"); + e_dbg("MNG configuration cycle has not completed.\n"); return -E1000_ERR_RESET; } @@ -635,7 +623,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) if (ret_val) return ret_val; - hw_dbg(hw, "GG82563 PSCR: %X\n", phy_data); + e_dbg("GG82563 PSCR: %X\n", phy_data); ret_val = e1e_rphy(hw, PHY_CONTROL, &phy_data); if (ret_val) @@ -653,7 +641,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) udelay(1); if (hw->phy.autoneg_wait_to_complete) { - hw_dbg(hw, "Waiting for forced speed/duplex link " + e_dbg("Waiting for forced speed/duplex link " "on GG82563 phy.\n"); ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT, @@ -712,21 +700,27 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) static s32 e1000_get_cable_length_80003es2lan(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val; - u16 phy_data; - u16 index; + s32 ret_val = 0; + u16 phy_data, index; ret_val = e1e_rphy(hw, GG82563_PHY_DSP_DISTANCE, &phy_data); if (ret_val) - return ret_val; + goto out; index = phy_data & GG82563_DSPD_CABLE_LENGTH; + + if (index >= GG82563_CABLE_LENGTH_TABLE_SIZE - 5) { + ret_val = -E1000_ERR_PHY; + goto out; + } + phy->min_cable_length = e1000_gg82563_cable_length_table[index]; - phy->max_cable_length = e1000_gg82563_cable_length_table[index+5]; + phy->max_cable_length = e1000_gg82563_cable_length_table[index + 5]; phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; - return 0; +out: + return ret_val; } /** @@ -736,7 +730,6 @@ static s32 e1000_get_cable_length_80003es2lan(struct e1000_hw *hw) * @duplex: pointer to duplex buffer * * Retrieve the current speed and duplex configuration. - * This is a function pointer entry point called by the api module. **/ static s32 e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed, u16 *duplex) @@ -762,12 +755,10 @@ static s32 e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed, * @hw: pointer to the HW structure * * Perform a global reset to the ESB2 controller. - * This is a function pointer entry point called by the api module. **/ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw) { - u32 ctrl; - u32 icr; + u32 ctrl, icr; s32 ret_val; /* @@ -776,9 +767,9 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw) */ ret_val = e1000e_disable_pcie_master(hw); if (ret_val) - hw_dbg(hw, "PCI-E Master disable polling has failed.\n"); + e_dbg("PCI-E Master disable polling has failed.\n"); - hw_dbg(hw, "Masking off all interrupts\n"); + e_dbg("Masking off all interrupts\n"); ew32(IMC, 0xffffffff); ew32(RCTL, 0); @@ -790,7 +781,7 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw) ctrl = er32(CTRL); ret_val = e1000_acquire_phy_80003es2lan(hw); - hw_dbg(hw, "Issuing a global reset to MAC\n"); + e_dbg("Issuing a global reset to MAC\n"); ew32(CTRL, ctrl | E1000_CTRL_RST); e1000_release_phy_80003es2lan(hw); @@ -811,7 +802,6 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw) * @hw: pointer to the HW structure * * Initialize the hw bits, LED, VFTA, MTA, link and hw counters. - * This is a function pointer entry point called by the api module. **/ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw) { @@ -824,20 +814,19 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw) /* Initialize identification LED */ ret_val = e1000e_id_led_init(hw); - if (ret_val) { - hw_dbg(hw, "Error initializing identification LED\n"); - return ret_val; - } + if (ret_val) + e_dbg("Error initializing identification LED\n"); + /* This is not fatal and we should not stop init due to this */ /* Disabling VLAN filtering */ - hw_dbg(hw, "Initializing the IEEE VLAN\n"); - e1000e_clear_vfta(hw); + e_dbg("Initializing the IEEE VLAN\n"); + mac->ops.clear_vfta(hw); /* Setup the receive address. */ e1000e_init_rx_addrs(hw, mac->rar_entry_count); /* Zero out the Multicast HASH table */ - hw_dbg(hw, "Zeroing the MTA\n"); + e_dbg("Zeroing the MTA\n"); for (i = 0; i < mac->mta_reg_count; i++) E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); @@ -994,7 +983,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) /* SW Reset the PHY so all changes take effect */ ret_val = e1000e_commit_phy(hw); if (ret_val) { - hw_dbg(hw, "Error Resetting the PHY\n"); + e_dbg("Error Resetting the PHY\n"); return ret_val; } @@ -1318,6 +1307,23 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, } /** + * e1000_power_down_phy_copper_80003es2lan - Remove link during PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, remove the link. + **/ +static void e1000_power_down_phy_copper_80003es2lan(struct e1000_hw *hw) +{ + /* If the management interface is not enabled, then power down */ + if (!(hw->mac.ops.check_mng_mode(hw) || + hw->phy.ops.check_reset_block(hw))) + e1000_power_down_phy_copper(hw); + + return; +} + +/** * e1000_clear_hw_cntrs_80003es2lan - Clear device specific hardware counters * @hw: pointer to the HW structure * @@ -1325,44 +1331,42 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, **/ static void e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw) { - u32 temp; - e1000e_clear_hw_cntrs_base(hw); - temp = er32(PRC64); - temp = er32(PRC127); - temp = er32(PRC255); - temp = er32(PRC511); - temp = er32(PRC1023); - temp = er32(PRC1522); - temp = er32(PTC64); - temp = er32(PTC127); - temp = er32(PTC255); - temp = er32(PTC511); - temp = er32(PTC1023); - temp = er32(PTC1522); - - temp = er32(ALGNERRC); - temp = er32(RXERRC); - temp = er32(TNCRS); - temp = er32(CEXTERR); - temp = er32(TSCTC); - temp = er32(TSCTFC); - - temp = er32(MGTPRC); - temp = er32(MGTPDC); - temp = er32(MGTPTC); - - temp = er32(IAC); - temp = er32(ICRXOC); - - temp = er32(ICRXPTC); - temp = er32(ICRXATC); - temp = er32(ICTXPTC); - temp = er32(ICTXATC); - temp = er32(ICTXQEC); - temp = er32(ICTXQMTC); - temp = er32(ICRXDMTC); + er32(PRC64); + er32(PRC127); + er32(PRC255); + er32(PRC511); + er32(PRC1023); + er32(PRC1522); + er32(PTC64); + er32(PTC127); + er32(PTC255); + er32(PTC511); + er32(PTC1023); + er32(PTC1522); + + er32(ALGNERRC); + er32(RXERRC); + er32(TNCRS); + er32(CEXTERR); + er32(TSCTC); + er32(TSCTFC); + + er32(MGTPRC); + er32(MGTPDC); + er32(MGTPTC); + + er32(IAC); + er32(ICRXOC); + + er32(ICRXPTC); + er32(ICRXATC); + er32(ICTXPTC); + er32(ICTXATC); + er32(ICTXQEC); + er32(ICTXQMTC); + er32(ICRXDMTC); } static struct e1000_mac_operations es2_mac_ops = { @@ -1376,6 +1380,8 @@ static struct e1000_mac_operations es2_mac_ops = { .led_on = e1000e_led_on_generic, .led_off = e1000e_led_off_generic, .update_mc_addr_list = e1000e_update_mc_addr_list_generic, + .write_vfta = e1000_write_vfta_generic, + .clear_vfta = e1000_clear_vfta_generic, .reset_hw = e1000_reset_hw_80003es2lan, .init_hw = e1000_init_hw_80003es2lan, .setup_link = e1000e_setup_link, @@ -1384,30 +1390,31 @@ static struct e1000_mac_operations es2_mac_ops = { }; static struct e1000_phy_operations es2_phy_ops = { - .acquire_phy = e1000_acquire_phy_80003es2lan, + .acquire = e1000_acquire_phy_80003es2lan, + .check_polarity = e1000_check_polarity_m88, .check_reset_block = e1000e_check_reset_block_generic, - .commit_phy = e1000e_phy_sw_reset, + .commit = e1000e_phy_sw_reset, .force_speed_duplex = e1000_phy_force_speed_duplex_80003es2lan, .get_cfg_done = e1000_get_cfg_done_80003es2lan, .get_cable_length = e1000_get_cable_length_80003es2lan, - .get_phy_info = e1000e_get_phy_info_m88, - .read_phy_reg = e1000_read_phy_reg_gg82563_80003es2lan, - .release_phy = e1000_release_phy_80003es2lan, - .reset_phy = e1000e_phy_hw_reset_generic, + .get_info = e1000e_get_phy_info_m88, + .read_reg = e1000_read_phy_reg_gg82563_80003es2lan, + .release = e1000_release_phy_80003es2lan, + .reset = e1000e_phy_hw_reset_generic, .set_d0_lplu_state = NULL, .set_d3_lplu_state = e1000e_set_d3_lplu_state, - .write_phy_reg = e1000_write_phy_reg_gg82563_80003es2lan, + .write_reg = e1000_write_phy_reg_gg82563_80003es2lan, .cfg_on_link_up = e1000_cfg_on_link_up_80003es2lan, }; static struct e1000_nvm_operations es2_nvm_ops = { - .acquire_nvm = e1000_acquire_nvm_80003es2lan, - .read_nvm = e1000e_read_nvm_eerd, - .release_nvm = e1000_release_nvm_80003es2lan, - .update_nvm = e1000e_update_nvm_checksum_generic, + .acquire = e1000_acquire_nvm_80003es2lan, + .read = e1000e_read_nvm_eerd, + .release = e1000_release_nvm_80003es2lan, + .update = e1000e_update_nvm_checksum_generic, .valid_led_default = e1000e_valid_led_default, - .validate_nvm = e1000e_validate_nvm_checksum_generic, - .write_nvm = e1000_write_nvm_80003es2lan, + .validate = e1000e_validate_nvm_checksum_generic, + .write = e1000_write_nvm_80003es2lan, }; struct e1000_info e1000_es2_info = { diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index e82638ecae88..0aa50c229c79 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -35,14 +35,22 @@ #include "e1000.h" +enum {NETDEV_STATS, E1000_STATS}; + struct e1000_stats { char stat_string[ETH_GSTRING_LEN]; + int type; int sizeof_stat; int stat_offset; }; -#define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \ - offsetof(struct e1000_adapter, m) +#define E1000_STAT(m) E1000_STATS, \ + sizeof(((struct e1000_adapter *)0)->m), \ + offsetof(struct e1000_adapter, m) +#define E1000_NETDEV_STAT(m) NETDEV_STATS, \ + sizeof(((struct net_device *)0)->m), \ + offsetof(struct net_device, m) + static const struct e1000_stats e1000_gstrings_stats[] = { { "rx_packets", E1000_STAT(stats.gprc) }, { "tx_packets", E1000_STAT(stats.gptc) }, @@ -52,21 +60,21 @@ static const struct e1000_stats e1000_gstrings_stats[] = { { "tx_broadcast", E1000_STAT(stats.bptc) }, { "rx_multicast", E1000_STAT(stats.mprc) }, { "tx_multicast", E1000_STAT(stats.mptc) }, - { "rx_errors", E1000_STAT(net_stats.rx_errors) }, - { "tx_errors", E1000_STAT(net_stats.tx_errors) }, - { "tx_dropped", E1000_STAT(net_stats.tx_dropped) }, + { "rx_errors", E1000_NETDEV_STAT(stats.rx_errors) }, + { "tx_errors", E1000_NETDEV_STAT(stats.tx_errors) }, + { "tx_dropped", E1000_NETDEV_STAT(stats.tx_dropped) }, { "multicast", E1000_STAT(stats.mprc) }, { "collisions", E1000_STAT(stats.colc) }, - { "rx_length_errors", E1000_STAT(net_stats.rx_length_errors) }, - { "rx_over_errors", E1000_STAT(net_stats.rx_over_errors) }, + { "rx_length_errors", E1000_NETDEV_STAT(stats.rx_length_errors) }, + { "rx_over_errors", E1000_NETDEV_STAT(stats.rx_over_errors) }, { "rx_crc_errors", E1000_STAT(stats.crcerrs) }, - { "rx_frame_errors", E1000_STAT(net_stats.rx_frame_errors) }, + { "rx_frame_errors", E1000_NETDEV_STAT(stats.rx_frame_errors) }, { "rx_no_buffer_count", E1000_STAT(stats.rnbc) }, { "rx_missed_errors", E1000_STAT(stats.mpc) }, { "tx_aborted_errors", E1000_STAT(stats.ecol) }, { "tx_carrier_errors", E1000_STAT(stats.tncrs) }, - { "tx_fifo_errors", E1000_STAT(net_stats.tx_fifo_errors) }, - { "tx_heartbeat_errors", E1000_STAT(net_stats.tx_heartbeat_errors) }, + { "tx_fifo_errors", E1000_NETDEV_STAT(stats.tx_fifo_errors) }, + { "tx_heartbeat_errors", E1000_NETDEV_STAT(stats.tx_heartbeat_errors) }, { "tx_window_errors", E1000_STAT(stats.latecol) }, { "tx_abort_late_coll", E1000_STAT(stats.latecol) }, { "tx_deferred_ok", E1000_STAT(stats.dc) }, @@ -182,6 +190,17 @@ static int e1000_get_settings(struct net_device *netdev, static u32 e1000_get_link(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_mac_info *mac = &adapter->hw.mac; + + /* + * If the link is not reported up to netdev, interrupts are disabled, + * and so the physical link state may have changed since we last + * looked. Set get_link_status to make sure that the true link + * state is interrogated, rather than pulling a cached and possibly + * stale link state from the driver. + */ + if (!netif_carrier_ok(netdev)) + mac->get_link_status = 1; return e1000_has_link(adapter); } @@ -516,7 +535,8 @@ static int e1000_get_eeprom(struct net_device *netdev, if (ret_val) { /* a read error occurred, throw away the result */ - memset(eeprom_buff, 0xff, sizeof(eeprom_buff)); + memset(eeprom_buff, 0xff, sizeof(u16) * + (last_word - first_word + 1)); } else { /* Device's eeprom is always little-endian, word addressable */ for (i = 0; i < last_word - first_word + 1; i++) @@ -596,7 +616,9 @@ static int e1000_set_eeprom(struct net_device *netdev, * and flush shadow RAM for applicable controllers */ if ((first_word <= NVM_CHECKSUM_REG) || - (hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82573)) + (hw->mac.type == e1000_82583) || + (hw->mac.type == e1000_82574) || + (hw->mac.type == e1000_82573)) ret_val = e1000e_update_nvm_checksum(hw); out: @@ -929,10 +951,10 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data) e1000e_set_interrupt_capability(adapter); } /* Hook up test interrupt handler just for this test */ - if (!request_irq(irq, &e1000_test_intr, IRQF_PROBE_SHARED, netdev->name, + if (!request_irq(irq, e1000_test_intr, IRQF_PROBE_SHARED, netdev->name, netdev)) { shared_int = 0; - } else if (request_irq(irq, &e1000_test_intr, IRQF_SHARED, + } else if (request_irq(irq, e1000_test_intr, IRQF_SHARED, netdev->name, netdev)) { *data = 1; ret_val = -1; @@ -1239,6 +1261,10 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) hw->mac.autoneg = 0; + /* Workaround: K1 must be disabled for stable 1Gbps operation */ + if (hw->mac.type == e1000_pchlan) + e1000_configure_k1_ich8lan(hw, false); + if (hw->phy.type == e1000_phy_m88) { /* Auto-MDI/MDIX Off */ e1e_wphy(hw, M88E1000_PHY_SPEC_CTRL, 0x0808); @@ -1769,12 +1795,11 @@ static int e1000_set_wol(struct net_device *netdev, { struct e1000_adapter *adapter = netdev_priv(netdev); - if (wol->wolopts & WAKE_MAGICSECURE) - return -EOPNOTSUPP; - if (!(adapter->flags & FLAG_HAS_WOL) || - !device_can_wakeup(&adapter->pdev->dev)) - return wol->wolopts ? -EOPNOTSUPP : 0; + !device_can_wakeup(&adapter->pdev->dev) || + (wol->wolopts & ~(WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | + WAKE_MAGIC | WAKE_PHY | WAKE_ARP))) + return -EOPNOTSUPP; /* these settings will always override what we currently have */ adapter->wol = 0; @@ -1832,6 +1857,7 @@ static int e1000_phys_id(struct net_device *netdev, u32 data) if ((hw->phy.type == e1000_phy_ife) || (hw->mac.type == e1000_pchlan) || + (hw->mac.type == e1000_82583) || (hw->mac.type == e1000_82574)) { INIT_WORK(&adapter->led_blink_task, e1000e_led_blink_task); if (!adapter->blink_timer.function) { @@ -1912,10 +1938,21 @@ static void e1000_get_ethtool_stats(struct net_device *netdev, { struct e1000_adapter *adapter = netdev_priv(netdev); int i; + char *p = NULL; e1000e_update_stats(adapter); for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) { - char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset; + switch (e1000_gstrings_stats[i].type) { + case NETDEV_STATS: + p = (char *) netdev + + e1000_gstrings_stats[i].stat_offset; + break; + case E1000_STATS: + p = (char *) adapter + + e1000_gstrings_stats[i].stat_offset; + break; + } + data[i] = (e1000_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } @@ -1975,6 +2012,8 @@ static const struct ethtool_ops e1000_ethtool_ops = { .get_sset_count = e1000e_get_sset_count, .get_coalesce = e1000_get_coalesce, .set_coalesce = e1000_set_coalesce, + .get_flags = ethtool_op_get_flags, + .set_flags = ethtool_op_set_flags, }; void e1000e_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h index aaea41ef794d..a7d08dae79c4 100644 --- a/drivers/net/e1000e/hw.h +++ b/drivers/net/e1000e/hw.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -219,7 +219,7 @@ enum e1e_registers { E1000_HICR = 0x08F00, /* Host Interface Control */ }; -/* RSS registers */ +#define E1000_MAX_PHY_ADDR 4 /* IGP01E1000 Specific Registers */ #define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */ @@ -356,6 +356,7 @@ enum e1e_registers { #define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA #define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB +#define E1000_DEV_ID_ICH8_82567V_3 0x1501 #define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 #define E1000_DEV_ID_ICH8_IGP_AMT 0x104A #define E1000_DEV_ID_ICH8_IGP_C 0x104B @@ -741,6 +742,7 @@ struct e1000_mac_operations { s32 (*check_for_link)(struct e1000_hw *); s32 (*cleanup_led)(struct e1000_hw *); void (*clear_hw_cntrs)(struct e1000_hw *); + void (*clear_vfta)(struct e1000_hw *); s32 (*get_bus_info)(struct e1000_hw *); s32 (*get_link_up_info)(struct e1000_hw *, u16 *, u16 *); s32 (*led_on)(struct e1000_hw *); @@ -751,38 +753,41 @@ struct e1000_mac_operations { s32 (*setup_link)(struct e1000_hw *); s32 (*setup_physical_interface)(struct e1000_hw *); s32 (*setup_led)(struct e1000_hw *); + void (*write_vfta)(struct e1000_hw *, u32, u32); }; /* Function pointers for the PHY. */ struct e1000_phy_operations { - s32 (*acquire_phy)(struct e1000_hw *); + s32 (*acquire)(struct e1000_hw *); + s32 (*cfg_on_link_up)(struct e1000_hw *); s32 (*check_polarity)(struct e1000_hw *); s32 (*check_reset_block)(struct e1000_hw *); - s32 (*commit_phy)(struct e1000_hw *); + s32 (*commit)(struct e1000_hw *); s32 (*force_speed_duplex)(struct e1000_hw *); s32 (*get_cfg_done)(struct e1000_hw *hw); s32 (*get_cable_length)(struct e1000_hw *); - s32 (*get_phy_info)(struct e1000_hw *); - s32 (*read_phy_reg)(struct e1000_hw *, u32, u16 *); - s32 (*read_phy_reg_locked)(struct e1000_hw *, u32, u16 *); - void (*release_phy)(struct e1000_hw *); - s32 (*reset_phy)(struct e1000_hw *); + s32 (*get_info)(struct e1000_hw *); + s32 (*read_reg)(struct e1000_hw *, u32, u16 *); + s32 (*read_reg_locked)(struct e1000_hw *, u32, u16 *); + void (*release)(struct e1000_hw *); + s32 (*reset)(struct e1000_hw *); s32 (*set_d0_lplu_state)(struct e1000_hw *, bool); s32 (*set_d3_lplu_state)(struct e1000_hw *, bool); - s32 (*write_phy_reg)(struct e1000_hw *, u32, u16); - s32 (*write_phy_reg_locked)(struct e1000_hw *, u32, u16); - s32 (*cfg_on_link_up)(struct e1000_hw *); + s32 (*write_reg)(struct e1000_hw *, u32, u16); + s32 (*write_reg_locked)(struct e1000_hw *, u32, u16); + void (*power_up)(struct e1000_hw *); + void (*power_down)(struct e1000_hw *); }; /* Function pointers for the NVM. */ struct e1000_nvm_operations { - s32 (*acquire_nvm)(struct e1000_hw *); - s32 (*read_nvm)(struct e1000_hw *, u16, u16, u16 *); - void (*release_nvm)(struct e1000_hw *); - s32 (*update_nvm)(struct e1000_hw *); + s32 (*acquire)(struct e1000_hw *); + s32 (*read)(struct e1000_hw *, u16, u16, u16 *); + void (*release)(struct e1000_hw *); + s32 (*update)(struct e1000_hw *); s32 (*valid_led_default)(struct e1000_hw *, u16 *); - s32 (*validate_nvm)(struct e1000_hw *); - s32 (*write_nvm)(struct e1000_hw *, u16, u16, u16 *); + s32 (*validate)(struct e1000_hw *); + s32 (*write)(struct e1000_hw *, u16, u16, u16 *); }; struct e1000_mac_info { @@ -925,15 +930,4 @@ struct e1000_hw { } dev_spec; }; -#ifdef DEBUG -#define hw_dbg(hw, format, arg...) \ - printk(KERN_DEBUG "%s: " format, e1000e_get_hw_dev_name(hw), ##arg) -#else -static inline int __attribute__ ((format (printf, 2, 3))) -hw_dbg(struct e1000_hw *hw, const char *format, ...) -{ - return 0; -} -#endif - #endif diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c index eff3f4783655..7b33be98a2ca 100644 --- a/drivers/net/e1000e/ich8lan.c +++ b/drivers/net/e1000e/ich8lan.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -54,11 +54,6 @@ * 82578DC Gigabit Network Connection */ -#include <linux/netdevice.h> -#include <linux/ethtool.h> -#include <linux/delay.h> -#include <linux/pci.h> - #include "e1000.h" #define ICH_FLASH_GFPREG 0x0000 @@ -200,7 +195,6 @@ union ich8_flash_protected_range { static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw); static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw); static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw); -static s32 e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw); static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank); static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw, u32 offset, u8 byte); @@ -222,9 +216,9 @@ static s32 e1000_cleanup_led_pchlan(struct e1000_hw *hw); static s32 e1000_led_on_pchlan(struct e1000_hw *hw); static s32 e1000_led_off_pchlan(struct e1000_hw *hw); static s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active); +static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw); static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw); static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link); -static s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable); static inline u16 __er16flash(struct e1000_hw *hw, unsigned long reg) { @@ -265,26 +259,37 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) phy->addr = 1; phy->reset_delay_us = 100; - phy->ops.check_polarity = e1000_check_polarity_ife_ich8lan; - phy->ops.read_phy_reg = e1000_read_phy_reg_hv; - phy->ops.read_phy_reg_locked = e1000_read_phy_reg_hv_locked; + phy->ops.read_reg = e1000_read_phy_reg_hv; + phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked; phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan; phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan; - phy->ops.write_phy_reg = e1000_write_phy_reg_hv; - phy->ops.write_phy_reg_locked = e1000_write_phy_reg_hv_locked; + phy->ops.write_reg = e1000_write_phy_reg_hv; + phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; phy->id = e1000_phy_unknown; e1000e_get_phy_id(hw); phy->type = e1000e_get_phy_type_from_id(phy->id); - if (phy->type == e1000_phy_82577) { + switch (phy->type) { + case e1000_phy_82577: phy->ops.check_polarity = e1000_check_polarity_82577; phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_82577; - phy->ops.get_cable_length = e1000_get_cable_length_82577; - phy->ops.get_phy_info = e1000_get_phy_info_82577; - phy->ops.commit_phy = e1000e_phy_sw_reset; + phy->ops.get_cable_length = e1000_get_cable_length_82577; + phy->ops.get_info = e1000_get_phy_info_82577; + phy->ops.commit = e1000e_phy_sw_reset; + case e1000_phy_82578: + phy->ops.check_polarity = e1000_check_polarity_m88; + phy->ops.force_speed_duplex = e1000e_phy_force_speed_duplex_m88; + phy->ops.get_cable_length = e1000e_get_cable_length_m88; + phy->ops.get_info = e1000e_get_phy_info_m88; + break; + default: + ret_val = -E1000_ERR_PHY; + break; } return ret_val; @@ -305,17 +310,22 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw) phy->addr = 1; phy->reset_delay_us = 100; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; + /* * We may need to do this twice - once for IGP and if that fails, * we'll set BM func pointers and try again */ ret_val = e1000e_determine_phy_address(hw); if (ret_val) { - hw->phy.ops.write_phy_reg = e1000e_write_phy_reg_bm; - hw->phy.ops.read_phy_reg = e1000e_read_phy_reg_bm; + phy->ops.write_reg = e1000e_write_phy_reg_bm; + phy->ops.read_reg = e1000e_read_phy_reg_bm; ret_val = e1000e_determine_phy_address(hw); - if (ret_val) + if (ret_val) { + e_dbg("Cannot determine PHY addr. Erroring out\n"); return ret_val; + } } phy->id = 0; @@ -332,29 +342,36 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw) case IGP03E1000_E_PHY_ID: phy->type = e1000_phy_igp_3; phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; - phy->ops.read_phy_reg_locked = e1000e_read_phy_reg_igp_locked; - phy->ops.write_phy_reg_locked = e1000e_write_phy_reg_igp_locked; + phy->ops.read_reg_locked = e1000e_read_phy_reg_igp_locked; + phy->ops.write_reg_locked = e1000e_write_phy_reg_igp_locked; + phy->ops.get_info = e1000e_get_phy_info_igp; + phy->ops.check_polarity = e1000_check_polarity_igp; + phy->ops.force_speed_duplex = e1000e_phy_force_speed_duplex_igp; break; case IFE_E_PHY_ID: case IFE_PLUS_E_PHY_ID: case IFE_C_E_PHY_ID: phy->type = e1000_phy_ife; phy->autoneg_mask = E1000_ALL_NOT_GIG; + phy->ops.get_info = e1000_get_phy_info_ife; + phy->ops.check_polarity = e1000_check_polarity_ife; + phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife; break; case BME1000_E_PHY_ID: phy->type = e1000_phy_bm; phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; - hw->phy.ops.read_phy_reg = e1000e_read_phy_reg_bm; - hw->phy.ops.write_phy_reg = e1000e_write_phy_reg_bm; - hw->phy.ops.commit_phy = e1000e_phy_sw_reset; + phy->ops.read_reg = e1000e_read_phy_reg_bm; + phy->ops.write_reg = e1000e_write_phy_reg_bm; + phy->ops.commit = e1000e_phy_sw_reset; + phy->ops.get_info = e1000e_get_phy_info_m88; + phy->ops.check_polarity = e1000_check_polarity_m88; + phy->ops.force_speed_duplex = e1000e_phy_force_speed_duplex_m88; break; default: return -E1000_ERR_PHY; break; } - phy->ops.check_polarity = e1000_check_polarity_ife_ich8lan; - return 0; } @@ -374,7 +391,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) /* Can't read flash registers if the register set isn't mapped. */ if (!hw->flash_address) { - hw_dbg(hw, "ERROR: Flash registers not mapped\n"); + e_dbg("ERROR: Flash registers not mapped\n"); return -E1000_ERR_CONFIG; } @@ -407,7 +424,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) /* Clear shadow ram */ for (i = 0; i < nvm->word_size; i++) { - dev_spec->shadow_ram[i].modified = 0; + dev_spec->shadow_ram[i].modified = false; dev_spec->shadow_ram[i].value = 0xFFFF; } @@ -436,7 +453,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_adapter *adapter) if (mac->type == e1000_ich8lan) mac->rar_entry_count--; /* Set if manageability features are enabled. */ - mac->arc_subsystem_valid = 1; + mac->arc_subsystem_valid = true; /* LED operations */ switch (mac->type) { @@ -470,7 +487,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_adapter *adapter) /* Enable PCS Lock-loss workaround for ICH8 */ if (mac->type == e1000_ich8lan) - e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw, 1); + e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw, true); return 0; } @@ -556,7 +573,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) */ ret_val = e1000e_config_fc_after_link_up(hw); if (ret_val) - hw_dbg(hw, "Error configuring flow control\n"); + e_dbg("Error configuring flow control\n"); out: return ret_val; @@ -636,8 +653,6 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) u32 extcnf_ctrl, timeout = PHY_CFG_TIMEOUT; s32 ret_val = 0; - might_sleep(); - mutex_lock(&swflag_mutex); while (timeout) { @@ -650,7 +665,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) } if (!timeout) { - hw_dbg(hw, "SW/FW/HW has locked the resource for too long.\n"); + e_dbg("SW/FW/HW has locked the resource for too long.\n"); ret_val = -E1000_ERR_CONFIG; goto out; } @@ -670,7 +685,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) } if (!timeout) { - hw_dbg(hw, "Failed to acquire the semaphore.\n"); + e_dbg("Failed to acquire the semaphore.\n"); extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG; ew32(EXTCNF_CTRL, extcnf_ctrl); ret_val = -E1000_ERR_CONFIG; @@ -714,7 +729,9 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw) **/ static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw) { - u32 fwsm = er32(FWSM); + u32 fwsm; + + fwsm = er32(FWSM); return (fwsm & E1000_FWSM_MODE_MASK) == (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT); @@ -738,77 +755,6 @@ static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw) } /** - * e1000_phy_force_speed_duplex_ich8lan - Force PHY speed & duplex - * @hw: pointer to the HW structure - * - * Forces the speed and duplex settings of the PHY. - * This is a function pointer entry point only called by - * PHY setup routines. - **/ -static s32 e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw) -{ - struct e1000_phy_info *phy = &hw->phy; - s32 ret_val; - u16 data; - bool link; - - if (phy->type != e1000_phy_ife) { - ret_val = e1000e_phy_force_speed_duplex_igp(hw); - return ret_val; - } - - ret_val = e1e_rphy(hw, PHY_CONTROL, &data); - if (ret_val) - return ret_val; - - e1000e_phy_force_speed_duplex_setup(hw, &data); - - ret_val = e1e_wphy(hw, PHY_CONTROL, data); - if (ret_val) - return ret_val; - - /* Disable MDI-X support for 10/100 */ - ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data); - if (ret_val) - return ret_val; - - data &= ~IFE_PMC_AUTO_MDIX; - data &= ~IFE_PMC_FORCE_MDIX; - - ret_val = e1e_wphy(hw, IFE_PHY_MDIX_CONTROL, data); - if (ret_val) - return ret_val; - - hw_dbg(hw, "IFE PMC: %X\n", data); - - udelay(1); - - if (phy->autoneg_wait_to_complete) { - hw_dbg(hw, "Waiting for forced speed/duplex link on IFE phy.\n"); - - ret_val = e1000e_phy_has_link_generic(hw, - PHY_FORCE_LIMIT, - 100000, - &link); - if (ret_val) - return ret_val; - - if (!link) - hw_dbg(hw, "Link taking longer than expected.\n"); - - /* Try once more */ - ret_val = e1000e_phy_has_link_generic(hw, - PHY_FORCE_LIMIT, - 100000, - &link); - if (ret_val) - return ret_val; - } - - return 0; -} - -/** * e1000_sw_lcd_config_ich8lan - SW-based LCD Configuration * @hw: pointer to the HW structure * @@ -822,7 +768,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) s32 ret_val; u16 word_addr, reg_data, reg_addr, phy_page = 0; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; @@ -918,7 +864,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) reg_addr &= PHY_REG_MASK; reg_addr |= phy_page; - ret_val = phy->ops.write_phy_reg_locked(hw, + ret_val = phy->ops.write_reg_locked(hw, (u32)reg_addr, reg_data); if (ret_val) @@ -927,7 +873,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) } out: - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -951,15 +897,14 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link) goto out; /* Wrap the whole flow with the sw flag */ - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) goto out; /* Disable K1 when link is 1Gbps, otherwise use the NVM setting */ if (link) { if (hw->phy.type == e1000_phy_82578) { - ret_val = hw->phy.ops.read_phy_reg_locked(hw, - BM_CS_STATUS, + ret_val = hw->phy.ops.read_reg_locked(hw, BM_CS_STATUS, &status_reg); if (ret_val) goto release; @@ -975,8 +920,7 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link) } if (hw->phy.type == e1000_phy_82577) { - ret_val = hw->phy.ops.read_phy_reg_locked(hw, - HV_M_STATUS, + ret_val = hw->phy.ops.read_reg_locked(hw, HV_M_STATUS, &status_reg); if (ret_val) goto release; @@ -992,14 +936,14 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link) } /* Link stall fix for link up */ - ret_val = hw->phy.ops.write_phy_reg_locked(hw, PHY_REG(770, 19), + ret_val = hw->phy.ops.write_reg_locked(hw, PHY_REG(770, 19), 0x0100); if (ret_val) goto release; } else { /* Link stall fix for link down */ - ret_val = hw->phy.ops.write_phy_reg_locked(hw, PHY_REG(770, 19), + ret_val = hw->phy.ops.write_reg_locked(hw, PHY_REG(770, 19), 0x4100); if (ret_val) goto release; @@ -1008,7 +952,7 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link) ret_val = e1000_configure_k1_ich8lan(hw, k1_enable); release: - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); out: return ret_val; } @@ -1023,7 +967,7 @@ out: * * Success returns 0, Failure returns -E1000_ERR_PHY (-2) **/ -static s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable) +s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable) { s32 ret_val = 0; u32 ctrl_reg = 0; @@ -1084,7 +1028,7 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state) if (hw->mac.type != e1000_pchlan) return ret_val; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; @@ -1098,7 +1042,7 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state) mac_reg = er32(PHY_CTRL); - ret_val = hw->phy.ops.read_phy_reg_locked(hw, HV_OEM_BITS, &oem_reg); + ret_val = hw->phy.ops.read_reg_locked(hw, HV_OEM_BITS, &oem_reg); if (ret_val) goto out; @@ -1120,10 +1064,10 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state) /* Restart auto-neg to activate the bits */ if (!e1000_check_reset_block(hw)) oem_reg |= HV_OEM_BITS_RESTART_AN; - ret_val = hw->phy.ops.write_phy_reg_locked(hw, HV_OEM_BITS, oem_reg); + ret_val = hw->phy.ops.write_reg_locked(hw, HV_OEM_BITS, oem_reg); out: - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -1166,7 +1110,7 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw) } /* Select page 0 */ - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; @@ -1174,7 +1118,7 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw) ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0); if (ret_val) goto out; - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); /* * Configure the K1 Si workaround during phy reset assuming there is @@ -1210,7 +1154,7 @@ static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw) * leave the PHY in a bad state possibly resulting in no link. */ if (loop == 0) - hw_dbg(hw, "LAN_INIT_DONE not set, increase timeout\n"); + e_dbg("LAN_INIT_DONE not set, increase timeout\n"); /* Clear the Init Done bit for the next init event */ data = er32(STATUS); @@ -1262,122 +1206,6 @@ out: } /** - * e1000_get_phy_info_ife_ich8lan - Retrieves various IFE PHY states - * @hw: pointer to the HW structure - * - * Populates "phy" structure with various feature states. - * This function is only called by other family-specific - * routines. - **/ -static s32 e1000_get_phy_info_ife_ich8lan(struct e1000_hw *hw) -{ - struct e1000_phy_info *phy = &hw->phy; - s32 ret_val; - u16 data; - bool link; - - ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link); - if (ret_val) - return ret_val; - - if (!link) { - hw_dbg(hw, "Phy info is only valid if link is up\n"); - return -E1000_ERR_CONFIG; - } - - ret_val = e1e_rphy(hw, IFE_PHY_SPECIAL_CONTROL, &data); - if (ret_val) - return ret_val; - phy->polarity_correction = (!(data & IFE_PSC_AUTO_POLARITY_DISABLE)); - - if (phy->polarity_correction) { - ret_val = phy->ops.check_polarity(hw); - if (ret_val) - return ret_val; - } else { - /* Polarity is forced */ - phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY) - ? e1000_rev_polarity_reversed - : e1000_rev_polarity_normal; - } - - ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data); - if (ret_val) - return ret_val; - - phy->is_mdix = (data & IFE_PMC_MDIX_STATUS); - - /* The following parameters are undefined for 10/100 operation. */ - phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; - phy->local_rx = e1000_1000t_rx_status_undefined; - phy->remote_rx = e1000_1000t_rx_status_undefined; - - return 0; -} - -/** - * e1000_get_phy_info_ich8lan - Calls appropriate PHY type get_phy_info - * @hw: pointer to the HW structure - * - * Wrapper for calling the get_phy_info routines for the appropriate phy type. - * This is a function pointer entry point called by drivers - * or other shared routines. - **/ -static s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw) -{ - switch (hw->phy.type) { - case e1000_phy_ife: - return e1000_get_phy_info_ife_ich8lan(hw); - break; - case e1000_phy_igp_3: - case e1000_phy_bm: - case e1000_phy_82578: - case e1000_phy_82577: - return e1000e_get_phy_info_igp(hw); - break; - default: - break; - } - - return -E1000_ERR_PHY_TYPE; -} - -/** - * e1000_check_polarity_ife_ich8lan - Check cable polarity for IFE PHY - * @hw: pointer to the HW structure - * - * Polarity is determined on the polarity reversal feature being enabled. - * This function is only called by other family-specific - * routines. - **/ -static s32 e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw) -{ - struct e1000_phy_info *phy = &hw->phy; - s32 ret_val; - u16 phy_data, offset, mask; - - /* - * Polarity is determined based on the reversal feature being enabled. - */ - if (phy->polarity_correction) { - offset = IFE_PHY_EXTENDED_STATUS_CONTROL; - mask = IFE_PESC_POLARITY_REVERSED; - } else { - offset = IFE_PHY_SPECIAL_CONTROL; - mask = IFE_PSC_FORCE_POLARITY; - } - - ret_val = e1e_rphy(hw, offset, &phy_data); - - if (!ret_val) - phy->cable_polarity = (phy_data & mask) - ? e1000_rev_polarity_reversed - : e1000_rev_polarity_normal; - - return ret_val; -} - -/** * e1000_set_lplu_state_pchlan - Set Low Power Link Up state * @hw: pointer to the HW structure * @active: true to enable LPLU, false to disable @@ -1412,7 +1240,7 @@ out: /** * e1000_set_d0_lplu_state_ich8lan - Set Low Power Linkup D0 state * @hw: pointer to the HW structure - * @active: TRUE to enable LPLU, FALSE to disable + * @active: true to enable LPLU, false to disable * * Sets the LPLU D0 state according to the active flag. When * activating LPLU this function also disables smart speed @@ -1498,7 +1326,7 @@ static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active) /** * e1000_set_d3_lplu_state_ich8lan - Set Low Power Linkup D3 state * @hw: pointer to the HW structure - * @active: TRUE to enable LPLU, FALSE to disable + * @active: true to enable LPLU, false to disable * * Sets the LPLU D3 state according to the active flag. When * activating LPLU this function also disables smart speed @@ -1611,7 +1439,7 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank) return 0; } - hw_dbg(hw, "Unable to determine valid NVM bank via EEC - " + e_dbg("Unable to determine valid NVM bank via EEC - " "reading flash signature\n"); /* fall-thru */ default: @@ -1641,7 +1469,7 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank) return 0; } - hw_dbg(hw, "ERROR: No valid NVM bank present\n"); + e_dbg("ERROR: No valid NVM bank present\n"); return -E1000_ERR_NVM; } @@ -1669,16 +1497,16 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) || (words == 0)) { - hw_dbg(hw, "nvm parameter(s) out of bounds\n"); + e_dbg("nvm parameter(s) out of bounds\n"); ret_val = -E1000_ERR_NVM; goto out; } - nvm->ops.acquire_nvm(hw); + nvm->ops.acquire(hw); ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); if (ret_val) { - hw_dbg(hw, "Could not detect valid bank, assuming bank 0\n"); + e_dbg("Could not detect valid bank, assuming bank 0\n"); bank = 0; } @@ -1700,11 +1528,11 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, } } - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); out: if (ret_val) - hw_dbg(hw, "NVM read error: %d\n", ret_val); + e_dbg("NVM read error: %d\n", ret_val); return ret_val; } @@ -1726,7 +1554,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) /* Check if the flash descriptor is valid */ if (hsfsts.hsf_status.fldesvalid == 0) { - hw_dbg(hw, "Flash descriptor invalid. " + e_dbg("Flash descriptor invalid. " "SW Sequencing must be used."); return -E1000_ERR_NVM; } @@ -1749,7 +1577,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) if (hsfsts.hsf_status.flcinprog == 0) { /* * There is no cycle running at present, - * so we can start a cycle + * so we can start a cycle. * Begin by setting Flash Cycle Done. */ hsfsts.hsf_status.flcdone = 1; @@ -1757,7 +1585,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) ret_val = 0; } else { /* - * otherwise poll for sometime so the current + * Otherwise poll for sometime so the current * cycle has a chance to end before giving up. */ for (i = 0; i < ICH_FLASH_READ_COMMAND_TIMEOUT; i++) { @@ -1776,7 +1604,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) hsfsts.hsf_status.flcdone = 1; ew16flash(ICH_FLASH_HSFSTS, hsfsts.regval); } else { - hw_dbg(hw, "Flash controller busy, cannot get access"); + e_dbg("Flash controller busy, cannot get access"); } } @@ -1926,7 +1754,7 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, /* Repeat for some time before giving up. */ continue; } else if (hsfsts.hsf_status.flcdone == 0) { - hw_dbg(hw, "Timeout error - flash cycle " + e_dbg("Timeout error - flash cycle " "did not complete."); break; } @@ -1954,18 +1782,18 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) || (words == 0)) { - hw_dbg(hw, "nvm parameter(s) out of bounds\n"); + e_dbg("nvm parameter(s) out of bounds\n"); return -E1000_ERR_NVM; } - nvm->ops.acquire_nvm(hw); + nvm->ops.acquire(hw); for (i = 0; i < words; i++) { - dev_spec->shadow_ram[offset+i].modified = 1; + dev_spec->shadow_ram[offset+i].modified = true; dev_spec->shadow_ram[offset+i].value = data[i]; } - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); return 0; } @@ -1996,7 +1824,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) if (nvm->type != e1000_nvm_flash_sw) goto out; - nvm->ops.acquire_nvm(hw); + nvm->ops.acquire(hw); /* * We're writing to the opposite bank so if we're on bank 1, @@ -2005,7 +1833,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) */ ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); if (ret_val) { - hw_dbg(hw, "Could not detect valid bank, assuming bank 0\n"); + e_dbg("Could not detect valid bank, assuming bank 0\n"); bank = 0; } @@ -2014,7 +1842,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) old_bank_offset = 0; ret_val = e1000_erase_flash_bank_ich8lan(hw, 1); if (ret_val) { - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); goto out; } } else { @@ -2022,7 +1850,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) new_bank_offset = 0; ret_val = e1000_erase_flash_bank_ich8lan(hw, 0); if (ret_val) { - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); goto out; } } @@ -2079,8 +1907,8 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) */ if (ret_val) { /* Possibly read-only, see e1000e_write_protect_nvm_ich8lan() */ - hw_dbg(hw, "Flash commit failed.\n"); - nvm->ops.release_nvm(hw); + e_dbg("Flash commit failed.\n"); + nvm->ops.release(hw); goto out; } @@ -2093,7 +1921,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD; ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data); if (ret_val) { - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); goto out; } data &= 0xBFFF; @@ -2101,7 +1929,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) act_offset * 2 + 1, (u8)(data >> 8)); if (ret_val) { - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); goto out; } @@ -2114,17 +1942,17 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) act_offset = (old_bank_offset + E1000_ICH_NVM_SIG_WORD) * 2 + 1; ret_val = e1000_retry_write_flash_byte_ich8lan(hw, act_offset, 0); if (ret_val) { - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); goto out; } /* Great! Everything worked, we can now clear the cached entries. */ for (i = 0; i < E1000_ICH8_SHADOW_RAM_WORDS; i++) { - dev_spec->shadow_ram[i].modified = 0; + dev_spec->shadow_ram[i].modified = false; dev_spec->shadow_ram[i].value = 0xFFFF; } - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); /* * Reload the EEPROM, or else modifications will not appear @@ -2135,7 +1963,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) out: if (ret_val) - hw_dbg(hw, "NVM update error: %d\n", ret_val); + e_dbg("NVM update error: %d\n", ret_val); return ret_val; } @@ -2193,7 +2021,7 @@ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw) union ich8_hws_flash_status hsfsts; u32 gfpreg; - nvm->ops.acquire_nvm(hw); + nvm->ops.acquire(hw); gfpreg = er32flash(ICH_FLASH_GFPREG); @@ -2214,7 +2042,7 @@ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw) hsfsts.hsf_status.flockdn = true; ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval); - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); } /** @@ -2285,7 +2113,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, /* Repeat for some time before giving up. */ continue; if (hsfsts.hsf_status.flcdone == 0) { - hw_dbg(hw, "Timeout error - flash cycle " + e_dbg("Timeout error - flash cycle " "did not complete."); break; } @@ -2330,7 +2158,7 @@ static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw, return ret_val; for (program_retries = 0; program_retries < 100; program_retries++) { - hw_dbg(hw, "Retrying Byte %2.2X at offset %u\n", byte, offset); + e_dbg("Retrying Byte %2.2X at offset %u\n", byte, offset); udelay(100); ret_val = e1000_write_flash_byte_ich8lan(hw, offset, byte); if (!ret_val) @@ -2360,9 +2188,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) u32 flash_bank_size = nvm->flash_bank_size * 2; s32 ret_val; s32 count = 0; - s32 iteration; - s32 sector_size; - s32 j; + s32 j, iteration, sector_size; hsfsts.regval = er16flash(ICH_FLASH_HSFSTS); @@ -2465,7 +2291,7 @@ static s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw, u16 *data) ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } @@ -2595,10 +2421,10 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) */ ret_val = e1000e_disable_pcie_master(hw); if (ret_val) { - hw_dbg(hw, "PCI-E Master disable polling has failed.\n"); + e_dbg("PCI-E Master disable polling has failed.\n"); } - hw_dbg(hw, "Masking off all interrupts\n"); + e_dbg("Masking off all interrupts\n"); ew32(IMC, 0xffffffff); /* @@ -2649,8 +2475,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) ctrl |= E1000_CTRL_PHY_RST; } ret_val = e1000_acquire_swflag_ich8lan(hw); - /* Whether or not the swflag was acquired, we need to reset the part */ - hw_dbg(hw, "Issuing a global reset to ich8lan\n"); + e_dbg("Issuing a global reset to ich8lan\n"); ew32(CTRL, (ctrl | E1000_CTRL_RST)); msleep(20); @@ -2670,7 +2495,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) * return with an error. This can happen in situations * where there is no eeprom and prevents getting link. */ - hw_dbg(hw, "Auto Read Done did not complete\n"); + e_dbg("Auto Read Done did not complete\n"); } } /* Dummy read to clear the phy wakeup bit after lcd reset */ @@ -2731,16 +2556,15 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw) /* Initialize identification LED */ ret_val = mac->ops.id_led_init(hw); - if (ret_val) { - hw_dbg(hw, "Error initializing identification LED\n"); - return ret_val; - } + if (ret_val) + e_dbg("Error initializing identification LED\n"); + /* This is not fatal and we should not stop init due to this */ /* Setup the receive address. */ e1000e_init_rx_addrs(hw, mac->rar_entry_count); /* Zero out the Multicast HASH table */ - hw_dbg(hw, "Zeroing the MTA\n"); + e_dbg("Zeroing the MTA\n"); for (i = 0; i < mac->mta_reg_count; i++) E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); @@ -2750,7 +2574,7 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw) * Reset the phy after disabling host wakeup to reset the Rx buffer. */ if (hw->phy.type == e1000_phy_82578) { - hw->phy.ops.read_phy_reg(hw, BM_WUC, &i); + hw->phy.ops.read_reg(hw, BM_WUC, &i); ret_val = e1000_phy_hw_reset_ich8lan(hw); if (ret_val) return ret_val; @@ -2886,7 +2710,7 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw) */ hw->fc.current_mode = hw->fc.requested_mode; - hw_dbg(hw, "After fix-ups FlowControl is now = %x\n", + e_dbg("After fix-ups FlowControl is now = %x\n", hw->fc.current_mode); /* Continue to configure the copper link. */ @@ -2897,7 +2721,7 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw) ew32(FCTTV, hw->fc.pause_time); if ((hw->phy.type == e1000_phy_82578) || (hw->phy.type == e1000_phy_82577)) { - ret_val = hw->phy.ops.write_phy_reg(hw, + ret_val = hw->phy.ops.write_reg(hw, PHY_REG(BM_PORT_CTRL_PAGE, 27), hw->fc.pause_time); if (ret_val) @@ -2960,7 +2784,7 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw) return ret_val; break; case e1000_phy_ife: - ret_val = hw->phy.ops.read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, + ret_val = hw->phy.ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, ®_data); if (ret_val) return ret_val; @@ -2979,7 +2803,7 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw) reg_data |= IFE_PMC_AUTO_MDIX; break; } - ret_val = hw->phy.ops.write_phy_reg(hw, IFE_PHY_MDIX_CONTROL, + ret_val = hw->phy.ops.write_reg(hw, IFE_PHY_MDIX_CONTROL, reg_data); if (ret_val) return ret_val; @@ -3092,8 +2916,8 @@ static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw) * @hw: pointer to the HW structure * @state: boolean value used to set the current Kumeran workaround state * - * If ICH8, set the current Kumeran workaround state (enabled - TRUE - * /disabled - FALSE). + * If ICH8, set the current Kumeran workaround state (enabled - true + * /disabled - false). **/ void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw, bool state) @@ -3101,7 +2925,7 @@ void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw, struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan; if (hw->mac.type != e1000_ich8lan) { - hw_dbg(hw, "Workaround applies to ICH8 only.\n"); + e_dbg("Workaround applies to ICH8 only.\n"); return; } @@ -3209,6 +3033,7 @@ void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw) u32 phy_ctrl; switch (hw->mac.type) { + case e1000_ich8lan: case e1000_ich9lan: case e1000_ich10lan: case e1000_pchlan: @@ -3281,7 +3106,7 @@ static s32 e1000_led_off_ich8lan(struct e1000_hw *hw) **/ static s32 e1000_setup_led_pchlan(struct e1000_hw *hw) { - return hw->phy.ops.write_phy_reg(hw, HV_LED_CONFIG, + return hw->phy.ops.write_reg(hw, HV_LED_CONFIG, (u16)hw->mac.ledctl_mode1); } @@ -3293,7 +3118,7 @@ static s32 e1000_setup_led_pchlan(struct e1000_hw *hw) **/ static s32 e1000_cleanup_led_pchlan(struct e1000_hw *hw) { - return hw->phy.ops.write_phy_reg(hw, HV_LED_CONFIG, + return hw->phy.ops.write_reg(hw, HV_LED_CONFIG, (u16)hw->mac.ledctl_default); } @@ -3325,7 +3150,7 @@ static s32 e1000_led_on_pchlan(struct e1000_hw *hw) } } - return hw->phy.ops.write_phy_reg(hw, HV_LED_CONFIG, data); + return hw->phy.ops.write_reg(hw, HV_LED_CONFIG, data); } /** @@ -3356,7 +3181,7 @@ static s32 e1000_led_off_pchlan(struct e1000_hw *hw) } } - return hw->phy.ops.write_phy_reg(hw, HV_LED_CONFIG, data); + return hw->phy.ops.write_reg(hw, HV_LED_CONFIG, data); } /** @@ -3379,8 +3204,7 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw) if (status & E1000_STATUS_PHYRA) ew32(STATUS, status & ~E1000_STATUS_PHYRA); else - hw_dbg(hw, - "PHY Reset Asserted not set - needs delay\n"); + e_dbg("PHY Reset Asserted not set - needs delay\n"); } e1000e_get_cfg_done(hw); @@ -3395,7 +3219,7 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw) } else { if (e1000_valid_nvm_bank_detect_ich8lan(hw, &bank)) { /* Maybe we should do a basic PHY config */ - hw_dbg(hw, "EEPROM not present\n"); + e_dbg("EEPROM not present\n"); return -E1000_ERR_CONFIG; } } @@ -3404,6 +3228,23 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw) } /** + * e1000_power_down_phy_copper_ich8lan - Remove link during PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, remove the link. + **/ +static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw) +{ + /* If the management interface is not enabled, then power down */ + if (!(hw->mac.ops.check_mng_mode(hw) || + hw->phy.ops.check_reset_block(hw))) + e1000_power_down_phy_copper(hw); + + return; +} + +/** * e1000_clear_hw_cntrs_ich8lan - Clear statistical counters * @hw: pointer to the HW structure * @@ -3412,42 +3253,41 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw) **/ static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw) { - u32 temp; u16 phy_data; e1000e_clear_hw_cntrs_base(hw); - temp = er32(ALGNERRC); - temp = er32(RXERRC); - temp = er32(TNCRS); - temp = er32(CEXTERR); - temp = er32(TSCTC); - temp = er32(TSCTFC); + er32(ALGNERRC); + er32(RXERRC); + er32(TNCRS); + er32(CEXTERR); + er32(TSCTC); + er32(TSCTFC); - temp = er32(MGTPRC); - temp = er32(MGTPDC); - temp = er32(MGTPTC); + er32(MGTPRC); + er32(MGTPDC); + er32(MGTPTC); - temp = er32(IAC); - temp = er32(ICRXOC); + er32(IAC); + er32(ICRXOC); /* Clear PHY statistics registers */ if ((hw->phy.type == e1000_phy_82578) || (hw->phy.type == e1000_phy_82577)) { - hw->phy.ops.read_phy_reg(hw, HV_SCC_UPPER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_SCC_LOWER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_ECOL_UPPER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_ECOL_LOWER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_MCC_UPPER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_MCC_LOWER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_LATECOL_UPPER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_LATECOL_LOWER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_COLC_UPPER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_COLC_LOWER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_DC_UPPER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_DC_LOWER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_TNCRS_UPPER, &phy_data); - hw->phy.ops.read_phy_reg(hw, HV_TNCRS_LOWER, &phy_data); + hw->phy.ops.read_reg(hw, HV_SCC_UPPER, &phy_data); + hw->phy.ops.read_reg(hw, HV_SCC_LOWER, &phy_data); + hw->phy.ops.read_reg(hw, HV_ECOL_UPPER, &phy_data); + hw->phy.ops.read_reg(hw, HV_ECOL_LOWER, &phy_data); + hw->phy.ops.read_reg(hw, HV_MCC_UPPER, &phy_data); + hw->phy.ops.read_reg(hw, HV_MCC_LOWER, &phy_data); + hw->phy.ops.read_reg(hw, HV_LATECOL_UPPER, &phy_data); + hw->phy.ops.read_reg(hw, HV_LATECOL_LOWER, &phy_data); + hw->phy.ops.read_reg(hw, HV_COLC_UPPER, &phy_data); + hw->phy.ops.read_reg(hw, HV_COLC_LOWER, &phy_data); + hw->phy.ops.read_reg(hw, HV_DC_UPPER, &phy_data); + hw->phy.ops.read_reg(hw, HV_DC_LOWER, &phy_data); + hw->phy.ops.read_reg(hw, HV_TNCRS_UPPER, &phy_data); + hw->phy.ops.read_reg(hw, HV_TNCRS_LOWER, &phy_data); } } @@ -3470,29 +3310,27 @@ static struct e1000_mac_operations ich8_mac_ops = { }; static struct e1000_phy_operations ich8_phy_ops = { - .acquire_phy = e1000_acquire_swflag_ich8lan, + .acquire = e1000_acquire_swflag_ich8lan, .check_reset_block = e1000_check_reset_block_ich8lan, - .commit_phy = NULL, - .force_speed_duplex = e1000_phy_force_speed_duplex_ich8lan, + .commit = NULL, .get_cfg_done = e1000_get_cfg_done_ich8lan, .get_cable_length = e1000e_get_cable_length_igp_2, - .get_phy_info = e1000_get_phy_info_ich8lan, - .read_phy_reg = e1000e_read_phy_reg_igp, - .release_phy = e1000_release_swflag_ich8lan, - .reset_phy = e1000_phy_hw_reset_ich8lan, + .read_reg = e1000e_read_phy_reg_igp, + .release = e1000_release_swflag_ich8lan, + .reset = e1000_phy_hw_reset_ich8lan, .set_d0_lplu_state = e1000_set_d0_lplu_state_ich8lan, .set_d3_lplu_state = e1000_set_d3_lplu_state_ich8lan, - .write_phy_reg = e1000e_write_phy_reg_igp, + .write_reg = e1000e_write_phy_reg_igp, }; static struct e1000_nvm_operations ich8_nvm_ops = { - .acquire_nvm = e1000_acquire_nvm_ich8lan, - .read_nvm = e1000_read_nvm_ich8lan, - .release_nvm = e1000_release_nvm_ich8lan, - .update_nvm = e1000_update_nvm_checksum_ich8lan, + .acquire = e1000_acquire_nvm_ich8lan, + .read = e1000_read_nvm_ich8lan, + .release = e1000_release_nvm_ich8lan, + .update = e1000_update_nvm_checksum_ich8lan, .valid_led_default = e1000_valid_led_default_ich8lan, - .validate_nvm = e1000_validate_nvm_checksum_ich8lan, - .write_nvm = e1000_write_nvm_ich8lan, + .validate = e1000_validate_nvm_checksum_ich8lan, + .write = e1000_write_nvm_ich8lan, }; struct e1000_info e1000_ich8_info = { diff --git a/drivers/net/e1000e/lib.c b/drivers/net/e1000e/lib.c index 99ba2b8a2a05..a86c17548c1e 100644 --- a/drivers/net/e1000e/lib.c +++ b/drivers/net/e1000e/lib.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -26,11 +26,6 @@ *******************************************************************************/ -#include <linux/netdevice.h> -#include <linux/ethtool.h> -#include <linux/delay.h> -#include <linux/pci.h> - #include "e1000.h" enum e1000_mng_mode { @@ -87,7 +82,24 @@ s32 e1000e_get_bus_info_pcie(struct e1000_hw *hw) } /** - * e1000e_write_vfta - Write value to VLAN filter table + * e1000_clear_vfta_generic - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * Clears the register array which contains the VLAN filter table by + * setting all the values to 0. + **/ +void e1000_clear_vfta_generic(struct e1000_hw *hw) +{ + u32 offset; + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, 0); + e1e_flush(); + } +} + +/** + * e1000_write_vfta_generic - Write value to VLAN filter table * @hw: pointer to the HW structure * @offset: register offset in VLAN filter table * @value: register value written to VLAN filter table @@ -95,7 +107,7 @@ s32 e1000e_get_bus_info_pcie(struct e1000_hw *hw) * Writes value at the given offset in the register array which stores * the VLAN filter table. **/ -void e1000e_write_vfta(struct e1000_hw *hw, u32 offset, u32 value) +void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value) { E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value); e1e_flush(); @@ -115,12 +127,12 @@ void e1000e_init_rx_addrs(struct e1000_hw *hw, u16 rar_count) u32 i; /* Setup the receive address */ - hw_dbg(hw, "Programming MAC Address into RAR[0]\n"); + e_dbg("Programming MAC Address into RAR[0]\n"); e1000e_rar_set(hw, hw->mac.addr, 0); /* Zero out the other (rar_entry_count - 1) receive addresses */ - hw_dbg(hw, "Clearing RAR[1-%u]\n", rar_count-1); + e_dbg("Clearing RAR[1-%u]\n", rar_count-1); for (i = 1; i < rar_count; i++) { E1000_WRITE_REG_ARRAY(hw, E1000_RA, (i << 1), 0); e1e_flush(); @@ -276,7 +288,7 @@ void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw, for (; mc_addr_count > 0; mc_addr_count--) { u32 hash_value, hash_reg, hash_bit, mta; hash_value = e1000_hash_mc_addr(hw, mc_addr_list); - hw_dbg(hw, "Hash value = 0x%03X\n", hash_value); + e_dbg("Hash value = 0x%03X\n", hash_value); hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); hash_bit = hash_value & 0x1F; mta = (1 << hash_bit); @@ -300,45 +312,43 @@ void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw, **/ void e1000e_clear_hw_cntrs_base(struct e1000_hw *hw) { - u32 temp; - - temp = er32(CRCERRS); - temp = er32(SYMERRS); - temp = er32(MPC); - temp = er32(SCC); - temp = er32(ECOL); - temp = er32(MCC); - temp = er32(LATECOL); - temp = er32(COLC); - temp = er32(DC); - temp = er32(SEC); - temp = er32(RLEC); - temp = er32(XONRXC); - temp = er32(XONTXC); - temp = er32(XOFFRXC); - temp = er32(XOFFTXC); - temp = er32(FCRUC); - temp = er32(GPRC); - temp = er32(BPRC); - temp = er32(MPRC); - temp = er32(GPTC); - temp = er32(GORCL); - temp = er32(GORCH); - temp = er32(GOTCL); - temp = er32(GOTCH); - temp = er32(RNBC); - temp = er32(RUC); - temp = er32(RFC); - temp = er32(ROC); - temp = er32(RJC); - temp = er32(TORL); - temp = er32(TORH); - temp = er32(TOTL); - temp = er32(TOTH); - temp = er32(TPR); - temp = er32(TPT); - temp = er32(MPTC); - temp = er32(BPTC); + er32(CRCERRS); + er32(SYMERRS); + er32(MPC); + er32(SCC); + er32(ECOL); + er32(MCC); + er32(LATECOL); + er32(COLC); + er32(DC); + er32(SEC); + er32(RLEC); + er32(XONRXC); + er32(XONTXC); + er32(XOFFRXC); + er32(XOFFTXC); + er32(FCRUC); + er32(GPRC); + er32(BPRC); + er32(MPRC); + er32(GPTC); + er32(GORCL); + er32(GORCH); + er32(GOTCL); + er32(GOTCH); + er32(RNBC); + er32(RUC); + er32(RFC); + er32(ROC); + er32(RJC); + er32(TORL); + er32(TORH); + er32(TOTL); + er32(TOTH); + er32(TPR); + er32(TPT); + er32(MPTC); + er32(BPTC); } /** @@ -376,7 +386,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) if (!link) return ret_val; /* No link detected */ - mac->get_link_status = 0; + mac->get_link_status = false; /* * Check if there was DownShift, must be checked @@ -408,7 +418,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) */ ret_val = e1000e_config_fc_after_link_up(hw); if (ret_val) { - hw_dbg(hw, "Error configuring flow control\n"); + e_dbg("Error configuring flow control\n"); } return ret_val; @@ -448,7 +458,7 @@ s32 e1000e_check_for_fiber_link(struct e1000_hw *hw) mac->autoneg_failed = 1; return 0; } - hw_dbg(hw, "NOT RXing /C/, disable AutoNeg and force link.\n"); + e_dbg("NOT RXing /C/, disable AutoNeg and force link.\n"); /* Disable auto-negotiation in the TXCW register */ ew32(TXCW, (mac->txcw & ~E1000_TXCW_ANE)); @@ -461,7 +471,7 @@ s32 e1000e_check_for_fiber_link(struct e1000_hw *hw) /* Configure Flow Control after forcing link up. */ ret_val = e1000e_config_fc_after_link_up(hw); if (ret_val) { - hw_dbg(hw, "Error configuring flow control\n"); + e_dbg("Error configuring flow control\n"); return ret_val; } } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { @@ -471,7 +481,7 @@ s32 e1000e_check_for_fiber_link(struct e1000_hw *hw) * and disable forced link in the Device Control register * in an attempt to auto-negotiate with our link partner. */ - hw_dbg(hw, "RXing /C/, enable AutoNeg and stop forcing link.\n"); + e_dbg("RXing /C/, enable AutoNeg and stop forcing link.\n"); ew32(TXCW, mac->txcw); ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); @@ -513,7 +523,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw) mac->autoneg_failed = 1; return 0; } - hw_dbg(hw, "NOT RXing /C/, disable AutoNeg and force link.\n"); + e_dbg("NOT RXing /C/, disable AutoNeg and force link.\n"); /* Disable auto-negotiation in the TXCW register */ ew32(TXCW, (mac->txcw & ~E1000_TXCW_ANE)); @@ -526,7 +536,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw) /* Configure Flow Control after forcing link up. */ ret_val = e1000e_config_fc_after_link_up(hw); if (ret_val) { - hw_dbg(hw, "Error configuring flow control\n"); + e_dbg("Error configuring flow control\n"); return ret_val; } } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { @@ -536,7 +546,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw) * and disable forced link in the Device Control register * in an attempt to auto-negotiate with our link partner. */ - hw_dbg(hw, "RXing /C/, enable AutoNeg and stop forcing link.\n"); + e_dbg("RXing /C/, enable AutoNeg and stop forcing link.\n"); ew32(TXCW, mac->txcw); ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); @@ -553,11 +563,11 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw) if (rxcw & E1000_RXCW_SYNCH) { if (!(rxcw & E1000_RXCW_IV)) { mac->serdes_has_link = true; - hw_dbg(hw, "SERDES: Link up - forced.\n"); + e_dbg("SERDES: Link up - forced.\n"); } } else { mac->serdes_has_link = false; - hw_dbg(hw, "SERDES: Link down - force failed.\n"); + e_dbg("SERDES: Link down - force failed.\n"); } } @@ -570,20 +580,20 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw) if (rxcw & E1000_RXCW_SYNCH) { if (!(rxcw & E1000_RXCW_IV)) { mac->serdes_has_link = true; - hw_dbg(hw, "SERDES: Link up - autoneg " + e_dbg("SERDES: Link up - autoneg " "completed sucessfully.\n"); } else { mac->serdes_has_link = false; - hw_dbg(hw, "SERDES: Link down - invalid" + e_dbg("SERDES: Link down - invalid" "codewords detected in autoneg.\n"); } } else { mac->serdes_has_link = false; - hw_dbg(hw, "SERDES: Link down - no sync.\n"); + e_dbg("SERDES: Link down - no sync.\n"); } } else { mac->serdes_has_link = false; - hw_dbg(hw, "SERDES: Link down - autoneg failed\n"); + e_dbg("SERDES: Link down - autoneg failed\n"); } } @@ -614,7 +624,7 @@ static s32 e1000_set_default_fc_generic(struct e1000_hw *hw) ret_val = e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &nvm_data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } @@ -667,7 +677,7 @@ s32 e1000e_setup_link(struct e1000_hw *hw) */ hw->fc.current_mode = hw->fc.requested_mode; - hw_dbg(hw, "After fix-ups FlowControl is now = %x\n", + e_dbg("After fix-ups FlowControl is now = %x\n", hw->fc.current_mode); /* Call the necessary media_type subroutine to configure the link. */ @@ -681,7 +691,7 @@ s32 e1000e_setup_link(struct e1000_hw *hw) * control is disabled, because it does not hurt anything to * initialize these registers. */ - hw_dbg(hw, "Initializing the Flow Control address, type and timer regs\n"); + e_dbg("Initializing the Flow Control address, type and timer regs\n"); ew32(FCT, FLOW_CONTROL_TYPE); ew32(FCAH, FLOW_CONTROL_ADDRESS_HIGH); ew32(FCAL, FLOW_CONTROL_ADDRESS_LOW); @@ -751,7 +761,7 @@ static s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw) txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); break; default: - hw_dbg(hw, "Flow control param set incorrectly\n"); + e_dbg("Flow control param set incorrectly\n"); return -E1000_ERR_CONFIG; break; } @@ -789,7 +799,7 @@ static s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) break; } if (i == FIBER_LINK_UP_LIMIT) { - hw_dbg(hw, "Never got a valid link from auto-neg!!!\n"); + e_dbg("Never got a valid link from auto-neg!!!\n"); mac->autoneg_failed = 1; /* * AutoNeg failed to achieve a link, so we'll call @@ -799,13 +809,13 @@ static s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) */ ret_val = mac->ops.check_for_link(hw); if (ret_val) { - hw_dbg(hw, "Error while checking for link\n"); + e_dbg("Error while checking for link\n"); return ret_val; } mac->autoneg_failed = 0; } else { mac->autoneg_failed = 0; - hw_dbg(hw, "Valid Link Found\n"); + e_dbg("Valid Link Found\n"); } return 0; @@ -841,7 +851,7 @@ s32 e1000e_setup_fiber_serdes_link(struct e1000_hw *hw) * then the link-up status bit will be set and the flow control enable * bits (RFCE and TFCE) will be set according to their negotiated value. */ - hw_dbg(hw, "Auto-negotiation enabled\n"); + e_dbg("Auto-negotiation enabled\n"); ew32(CTRL, ctrl); e1e_flush(); @@ -856,7 +866,7 @@ s32 e1000e_setup_fiber_serdes_link(struct e1000_hw *hw) (er32(CTRL) & E1000_CTRL_SWDPIN1)) { ret_val = e1000_poll_fiber_serdes_link_generic(hw); } else { - hw_dbg(hw, "No signal detected\n"); + e_dbg("No signal detected\n"); } return 0; @@ -952,7 +962,7 @@ s32 e1000e_force_mac_fc(struct e1000_hw *hw) * 3: Both Rx and Tx flow control (symmetric) is enabled. * other: No other values should be possible at this point. */ - hw_dbg(hw, "hw->fc.current_mode = %u\n", hw->fc.current_mode); + e_dbg("hw->fc.current_mode = %u\n", hw->fc.current_mode); switch (hw->fc.current_mode) { case e1000_fc_none: @@ -970,7 +980,7 @@ s32 e1000e_force_mac_fc(struct e1000_hw *hw) ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); break; default: - hw_dbg(hw, "Flow control param set incorrectly\n"); + e_dbg("Flow control param set incorrectly\n"); return -E1000_ERR_CONFIG; } @@ -1011,7 +1021,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) } if (ret_val) { - hw_dbg(hw, "Error forcing flow control settings\n"); + e_dbg("Error forcing flow control settings\n"); return ret_val; } @@ -1035,7 +1045,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) return ret_val; if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) { - hw_dbg(hw, "Copper PHY and Auto Neg " + e_dbg("Copper PHY and Auto Neg " "has not completed.\n"); return ret_val; } @@ -1076,7 +1086,6 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) * 1 | 1 | 0 | 0 | e1000_fc_none * 1 | 1 | 0 | 1 | e1000_fc_rx_pause * - * * Are both PAUSE bits set to 1? If so, this implies * Symmetric Flow Control is enabled at both ends. The * ASM_DIR bits are irrelevant per the spec. @@ -1100,10 +1109,10 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) */ if (hw->fc.requested_mode == e1000_fc_full) { hw->fc.current_mode = e1000_fc_full; - hw_dbg(hw, "Flow Control = FULL.\r\n"); + e_dbg("Flow Control = FULL.\r\n"); } else { hw->fc.current_mode = e1000_fc_rx_pause; - hw_dbg(hw, "Flow Control = " + e_dbg("Flow Control = " "RX PAUSE frames only.\r\n"); } } @@ -1114,14 +1123,13 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result *-------|---------|-------|---------|-------------------- * 0 | 1 | 1 | 1 | e1000_fc_tx_pause - * */ else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { hw->fc.current_mode = e1000_fc_tx_pause; - hw_dbg(hw, "Flow Control = Tx PAUSE frames only.\r\n"); + e_dbg("Flow Control = Tx PAUSE frames only.\r\n"); } /* * For transmitting PAUSE frames ONLY. @@ -1130,21 +1138,20 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result *-------|---------|-------|---------|-------------------- * 1 | 1 | 0 | 1 | e1000_fc_rx_pause - * */ else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { hw->fc.current_mode = e1000_fc_rx_pause; - hw_dbg(hw, "Flow Control = Rx PAUSE frames only.\r\n"); + e_dbg("Flow Control = Rx PAUSE frames only.\r\n"); } else { /* * Per the IEEE spec, at this point flow control * should be disabled. */ hw->fc.current_mode = e1000_fc_none; - hw_dbg(hw, "Flow Control = NONE.\r\n"); + e_dbg("Flow Control = NONE.\r\n"); } /* @@ -1154,7 +1161,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) */ ret_val = mac->ops.get_link_up_info(hw, &speed, &duplex); if (ret_val) { - hw_dbg(hw, "Error getting link speed and duplex\n"); + e_dbg("Error getting link speed and duplex\n"); return ret_val; } @@ -1167,7 +1174,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) */ ret_val = e1000e_force_mac_fc(hw); if (ret_val) { - hw_dbg(hw, "Error forcing flow control settings\n"); + e_dbg("Error forcing flow control settings\n"); return ret_val; } } @@ -1191,21 +1198,21 @@ s32 e1000e_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed, u16 *dup status = er32(STATUS); if (status & E1000_STATUS_SPEED_1000) { *speed = SPEED_1000; - hw_dbg(hw, "1000 Mbs, "); + e_dbg("1000 Mbs, "); } else if (status & E1000_STATUS_SPEED_100) { *speed = SPEED_100; - hw_dbg(hw, "100 Mbs, "); + e_dbg("100 Mbs, "); } else { *speed = SPEED_10; - hw_dbg(hw, "10 Mbs, "); + e_dbg("10 Mbs, "); } if (status & E1000_STATUS_FD) { *duplex = FULL_DUPLEX; - hw_dbg(hw, "Full Duplex\n"); + e_dbg("Full Duplex\n"); } else { *duplex = HALF_DUPLEX; - hw_dbg(hw, "Half Duplex\n"); + e_dbg("Half Duplex\n"); } return 0; @@ -1251,7 +1258,7 @@ s32 e1000e_get_hw_semaphore(struct e1000_hw *hw) } if (i == timeout) { - hw_dbg(hw, "Driver can't access device - SMBI bit is set.\n"); + e_dbg("Driver can't access device - SMBI bit is set.\n"); return -E1000_ERR_NVM; } @@ -1270,7 +1277,7 @@ s32 e1000e_get_hw_semaphore(struct e1000_hw *hw) if (i == timeout) { /* Release semaphores */ e1000e_put_hw_semaphore(hw); - hw_dbg(hw, "Driver can't access the NVM\n"); + e_dbg("Driver can't access the NVM\n"); return -E1000_ERR_NVM; } @@ -1310,7 +1317,7 @@ s32 e1000e_get_auto_rd_done(struct e1000_hw *hw) } if (i == AUTO_READ_DONE_TIMEOUT) { - hw_dbg(hw, "Auto read by HW from NVM has not completed.\n"); + e_dbg("Auto read by HW from NVM has not completed.\n"); return -E1000_ERR_RESET; } @@ -1331,7 +1338,7 @@ s32 e1000e_valid_led_default(struct e1000_hw *hw, u16 *data) ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } @@ -1585,7 +1592,7 @@ s32 e1000e_disable_pcie_master(struct e1000_hw *hw) } if (!timeout) { - hw_dbg(hw, "Master requests are pending.\n"); + e_dbg("Master requests are pending.\n"); return -E1000_ERR_MASTER_REQUESTS_PENDING; } @@ -1608,7 +1615,7 @@ void e1000e_reset_adaptive(struct e1000_hw *hw) mac->ifs_step_size = IFS_STEP; mac->ifs_ratio = IFS_RATIO; - mac->in_ifs_mode = 0; + mac->in_ifs_mode = false; ew32(AIT, 0); } @@ -1625,7 +1632,7 @@ void e1000e_update_adaptive(struct e1000_hw *hw) if ((mac->collision_delta * mac->ifs_ratio) > mac->tx_packet_delta) { if (mac->tx_packet_delta > MIN_NUM_XMITS) { - mac->in_ifs_mode = 1; + mac->in_ifs_mode = true; if (mac->current_ifs_val < mac->ifs_max_val) { if (!mac->current_ifs_val) mac->current_ifs_val = mac->ifs_min_val; @@ -1639,7 +1646,7 @@ void e1000e_update_adaptive(struct e1000_hw *hw) if (mac->in_ifs_mode && (mac->tx_packet_delta <= MIN_NUM_XMITS)) { mac->current_ifs_val = 0; - mac->in_ifs_mode = 0; + mac->in_ifs_mode = false; ew32(AIT, 0); } } @@ -1809,7 +1816,7 @@ s32 e1000e_acquire_nvm(struct e1000_hw *hw) if (!timeout) { eecd &= ~E1000_EECD_REQ; ew32(EECD, eecd); - hw_dbg(hw, "Could not acquire NVM grant\n"); + e_dbg("Could not acquire NVM grant\n"); return -E1000_ERR_NVM; } @@ -1914,7 +1921,7 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw) } if (!timeout) { - hw_dbg(hw, "SPI NVM Status error\n"); + e_dbg("SPI NVM Status error\n"); return -E1000_ERR_NVM; } } @@ -1943,7 +1950,7 @@ s32 e1000e_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) */ if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || (words == 0)) { - hw_dbg(hw, "nvm parameter(s) out of bounds\n"); + e_dbg("nvm parameter(s) out of bounds\n"); return -E1000_ERR_NVM; } @@ -1986,11 +1993,11 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) */ if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || (words == 0)) { - hw_dbg(hw, "nvm parameter(s) out of bounds\n"); + e_dbg("nvm parameter(s) out of bounds\n"); return -E1000_ERR_NVM; } - ret_val = nvm->ops.acquire_nvm(hw); + ret_val = nvm->ops.acquire(hw); if (ret_val) return ret_val; @@ -2001,7 +2008,7 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) ret_val = e1000_ready_nvm_eeprom(hw); if (ret_val) { - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); return ret_val; } @@ -2040,7 +2047,7 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) } msleep(10); - nvm->ops.release_nvm(hw); + nvm->ops.release(hw); return 0; } @@ -2066,7 +2073,7 @@ s32 e1000e_read_mac_addr(struct e1000_hw *hw) ret_val = e1000_read_nvm(hw, NVM_ALT_MAC_ADDR_PTR, 1, &mac_addr_offset); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } if (mac_addr_offset == 0xFFFF) @@ -2081,7 +2088,7 @@ s32 e1000e_read_mac_addr(struct e1000_hw *hw) ret_val = e1000_read_nvm(hw, mac_addr_offset, 1, &nvm_data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } if (nvm_data & 0x0001) @@ -2096,7 +2103,7 @@ s32 e1000e_read_mac_addr(struct e1000_hw *hw) offset = mac_addr_offset + (i >> 1); ret_val = e1000_read_nvm(hw, offset, 1, &nvm_data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF); @@ -2129,14 +2136,14 @@ s32 e1000e_validate_nvm_checksum_generic(struct e1000_hw *hw) for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) { ret_val = e1000_read_nvm(hw, i, 1, &nvm_data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } checksum += nvm_data; } if (checksum != (u16) NVM_SUM) { - hw_dbg(hw, "NVM Checksum Invalid\n"); + e_dbg("NVM Checksum Invalid\n"); return -E1000_ERR_NVM; } @@ -2160,7 +2167,7 @@ s32 e1000e_update_nvm_checksum_generic(struct e1000_hw *hw) for (i = 0; i < NVM_CHECKSUM_REG; i++) { ret_val = e1000_read_nvm(hw, i, 1, &nvm_data); if (ret_val) { - hw_dbg(hw, "NVM Read Error while updating checksum.\n"); + e_dbg("NVM Read Error while updating checksum.\n"); return ret_val; } checksum += nvm_data; @@ -2168,7 +2175,7 @@ s32 e1000e_update_nvm_checksum_generic(struct e1000_hw *hw) checksum = (u16) NVM_SUM - checksum; ret_val = e1000_write_nvm(hw, NVM_CHECKSUM_REG, 1, &checksum); if (ret_val) - hw_dbg(hw, "NVM Write Error while updating checksum.\n"); + e_dbg("NVM Write Error while updating checksum.\n"); return ret_val; } @@ -2231,7 +2238,7 @@ static s32 e1000_mng_enable_host_if(struct e1000_hw *hw) /* Check that the host interface is enabled. */ hicr = er32(HICR); if ((hicr & E1000_HICR_EN) == 0) { - hw_dbg(hw, "E1000_HOST_EN bit disabled.\n"); + e_dbg("E1000_HOST_EN bit disabled.\n"); return -E1000_ERR_HOST_INTERFACE_COMMAND; } /* check the previous command is completed */ @@ -2243,7 +2250,7 @@ static s32 e1000_mng_enable_host_if(struct e1000_hw *hw) } if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) { - hw_dbg(hw, "Previous command timeout failed .\n"); + e_dbg("Previous command timeout failed .\n"); return -E1000_ERR_HOST_INTERFACE_COMMAND; } @@ -2282,7 +2289,7 @@ bool e1000e_enable_tx_pkt_filtering(struct e1000_hw *hw) /* No manageability, no filtering */ if (!e1000e_check_mng_mode(hw)) { - hw->mac.tx_pkt_filtering = 0; + hw->mac.tx_pkt_filtering = false; return 0; } @@ -2292,7 +2299,7 @@ bool e1000e_enable_tx_pkt_filtering(struct e1000_hw *hw) */ ret_val = e1000_mng_enable_host_if(hw); if (ret_val != 0) { - hw->mac.tx_pkt_filtering = 0; + hw->mac.tx_pkt_filtering = false; return ret_val; } @@ -2311,17 +2318,17 @@ bool e1000e_enable_tx_pkt_filtering(struct e1000_hw *hw) * take the safe route of assuming Tx filtering is enabled. */ if ((hdr_csum != csum) || (hdr->signature != E1000_IAMT_SIGNATURE)) { - hw->mac.tx_pkt_filtering = 1; + hw->mac.tx_pkt_filtering = true; return 1; } /* Cookie area is valid, make the final check for filtering. */ if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING)) { - hw->mac.tx_pkt_filtering = 0; + hw->mac.tx_pkt_filtering = false; return 0; } - hw->mac.tx_pkt_filtering = 1; + hw->mac.tx_pkt_filtering = true; return 1; } @@ -2353,7 +2360,7 @@ static s32 e1000_mng_write_cmd_header(struct e1000_hw *hw, } /** - * e1000_mng_host_if_write - Writes to the manageability host interface + * e1000_mng_host_if_write - Write to the manageability host interface * @hw: pointer to the HW structure * @buffer: pointer to the host interface buffer * @length: size of the buffer @@ -2478,7 +2485,7 @@ bool e1000e_enable_mng_pass_thru(struct e1000_hw *hw) { u32 manc; u32 fwsm, factps; - bool ret_val = 0; + bool ret_val = false; manc = er32(MANC); @@ -2493,13 +2500,13 @@ bool e1000e_enable_mng_pass_thru(struct e1000_hw *hw) if (!(factps & E1000_FACTPS_MNGCG) && ((fwsm & E1000_FWSM_MODE_MASK) == (e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT))) { - ret_val = 1; + ret_val = true; return ret_val; } } else { if ((manc & E1000_MANC_SMBUS_EN) && !(manc & E1000_MANC_ASF_EN)) { - ret_val = 1; + ret_val = true; return ret_val; } } @@ -2514,14 +2521,14 @@ s32 e1000e_read_pba_num(struct e1000_hw *hw, u32 *pba_num) ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_0, 1, &nvm_data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } *pba_num = (u32)(nvm_data << 16); ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_1, 1, &nvm_data); if (ret_val) { - hw_dbg(hw, "NVM Read Error\n"); + e_dbg("NVM Read Error\n"); return ret_val; } *pba_num |= nvm_data; diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index fad8f9ea0043..c3105c5087e0 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -65,17 +65,6 @@ static const struct e1000_info *e1000_info_tbl[] = { [board_pchlan] = &e1000_pch_info, }; -#ifdef DEBUG -/** - * e1000_get_hw_dev_name - return device name string - * used by hardware layer to print debugging information - **/ -char *e1000e_get_hw_dev_name(struct e1000_hw *hw) -{ - return hw->adapter->netdev->name; -} -#endif - /** * e1000_desc_unused - calculate if we have unused descriptors **/ @@ -167,7 +156,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter, struct e1000_buffer *buffer_info; struct sk_buff *skb; unsigned int i; - unsigned int bufsz = adapter->rx_buffer_len + NET_IP_ALIGN; + unsigned int bufsz = adapter->rx_buffer_len; i = rx_ring->next_to_use; buffer_info = &rx_ring->buffer_info[i]; @@ -179,20 +168,13 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter, goto map_skb; } - skb = netdev_alloc_skb(netdev, bufsz); + skb = netdev_alloc_skb_ip_align(netdev, bufsz); if (!skb) { /* Better luck next round */ adapter->alloc_rx_buff_failed++; break; } - /* - * Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; map_skb: buffer_info->dma = pci_map_single(pdev, skb->data, @@ -284,21 +266,14 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, cpu_to_le64(ps_page->dma); } - skb = netdev_alloc_skb(netdev, - adapter->rx_ps_bsize0 + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(netdev, + adapter->rx_ps_bsize0); if (!skb) { adapter->alloc_rx_buff_failed++; break; } - /* - * Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; buffer_info->dma = pci_map_single(pdev, skb->data, adapter->rx_ps_bsize0, @@ -359,9 +334,7 @@ static void e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter, struct e1000_buffer *buffer_info; struct sk_buff *skb; unsigned int i; - unsigned int bufsz = 256 - - 16 /* for skb_reserve */ - - NET_IP_ALIGN; + unsigned int bufsz = 256 - 16 /* for skb_reserve */; i = rx_ring->next_to_use; buffer_info = &rx_ring->buffer_info[i]; @@ -373,19 +346,13 @@ static void e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter, goto check_page; } - skb = netdev_alloc_skb(netdev, bufsz); + skb = netdev_alloc_skb_ip_align(netdev, bufsz); if (unlikely(!skb)) { /* Better luck next round */ adapter->alloc_rx_buff_failed++; break; } - /* Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; check_page: /* allocate a new page if necessary */ @@ -437,6 +404,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, { struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; + struct e1000_hw *hw = &adapter->hw; struct e1000_ring *rx_ring = adapter->rx_ring; struct e1000_rx_desc *rx_desc, *next_rxd; struct e1000_buffer *buffer_info, *next_buffer; @@ -486,8 +454,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, * packet, also make sure the frame isn't just CRC only */ if (!(status & E1000_RXD_STAT_EOP) || (length <= 4)) { /* All receives must fit into a single buffer */ - e_dbg("%s: Receive packet consumed multiple buffers\n", - netdev->name); + e_dbg("Receive packet consumed multiple buffers\n"); /* recycle */ buffer_info->skb = skb; goto next_desc; @@ -513,9 +480,8 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, */ if (length < copybreak) { struct sk_buff *new_skb = - netdev_alloc_skb(netdev, length + NET_IP_ALIGN); + netdev_alloc_skb_ip_align(netdev, length); if (new_skb) { - skb_reserve(new_skb, NET_IP_ALIGN); skb_copy_to_linear_data_offset(new_skb, -NET_IP_ALIGN, (skb->data - @@ -560,33 +526,52 @@ next_desc: adapter->total_rx_bytes += total_rx_bytes; adapter->total_rx_packets += total_rx_packets; - adapter->net_stats.rx_bytes += total_rx_bytes; - adapter->net_stats.rx_packets += total_rx_packets; + netdev->stats.rx_bytes += total_rx_bytes; + netdev->stats.rx_packets += total_rx_packets; return cleaned; } static void e1000_put_txbuf(struct e1000_adapter *adapter, struct e1000_buffer *buffer_info) { - buffer_info->dma = 0; + if (buffer_info->dma) { + if (buffer_info->mapped_as_page) + pci_unmap_page(adapter->pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + else + pci_unmap_single(adapter->pdev, buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + buffer_info->dma = 0; + } if (buffer_info->skb) { - skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb, - DMA_TO_DEVICE); dev_kfree_skb_any(buffer_info->skb); buffer_info->skb = NULL; } buffer_info->time_stamp = 0; } -static void e1000_print_tx_hang(struct e1000_adapter *adapter) +static void e1000_print_hw_hang(struct work_struct *work) { + struct e1000_adapter *adapter = container_of(work, + struct e1000_adapter, + print_hang_task); struct e1000_ring *tx_ring = adapter->tx_ring; unsigned int i = tx_ring->next_to_clean; unsigned int eop = tx_ring->buffer_info[i].next_to_watch; struct e1000_tx_desc *eop_desc = E1000_TX_DESC(*tx_ring, eop); + struct e1000_hw *hw = &adapter->hw; + u16 phy_status, phy_1000t_status, phy_ext_status; + u16 pci_status; - /* detected Tx unit hang */ - e_err("Detected Tx Unit Hang:\n" + e1e_rphy(hw, PHY_STATUS, &phy_status); + e1e_rphy(hw, PHY_1000T_STATUS, &phy_1000t_status); + e1e_rphy(hw, PHY_EXT_STATUS, &phy_ext_status); + + pci_read_config_word(adapter->pdev, PCI_STATUS, &pci_status); + + /* detected Hardware unit hang */ + e_err("Detected Hardware Unit Hang:\n" " TDH <%x>\n" " TDT <%x>\n" " next_to_use <%x>\n" @@ -595,7 +580,12 @@ static void e1000_print_tx_hang(struct e1000_adapter *adapter) " time_stamp <%lx>\n" " next_to_watch <%x>\n" " jiffies <%lx>\n" - " next_to_watch.status <%x>\n", + " next_to_watch.status <%x>\n" + "MAC Status <%x>\n" + "PHY Status <%x>\n" + "PHY 1000BASE-T Status <%x>\n" + "PHY Extended Status <%x>\n" + "PCI Status <%x>\n", readl(adapter->hw.hw_addr + tx_ring->head), readl(adapter->hw.hw_addr + tx_ring->tail), tx_ring->next_to_use, @@ -603,7 +593,12 @@ static void e1000_print_tx_hang(struct e1000_adapter *adapter) tx_ring->buffer_info[eop].time_stamp, eop, jiffies, - eop_desc->upper.fields.status); + eop_desc->upper.fields.status, + er32(STATUS), + phy_status, + phy_1000t_status, + phy_ext_status, + pci_status); } /** @@ -677,21 +672,23 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter) } if (adapter->detect_tx_hung) { - /* Detect a transmit hang in hardware, this serializes the - * check with the clearing of time_stamp and movement of i */ + /* + * Detect a transmit hang in hardware, this serializes the + * check with the clearing of time_stamp and movement of i + */ adapter->detect_tx_hung = 0; if (tx_ring->buffer_info[i].time_stamp && time_after(jiffies, tx_ring->buffer_info[i].time_stamp - + (adapter->tx_timeout_factor * HZ)) - && !(er32(STATUS) & E1000_STATUS_TXOFF)) { - e1000_print_tx_hang(adapter); + + (adapter->tx_timeout_factor * HZ)) && + !(er32(STATUS) & E1000_STATUS_TXOFF)) { + schedule_work(&adapter->print_hang_task); netif_stop_queue(netdev); } } adapter->total_tx_bytes += total_tx_bytes; adapter->total_tx_packets += total_tx_packets; - adapter->net_stats.tx_bytes += total_tx_bytes; - adapter->net_stats.tx_packets += total_tx_packets; + netdev->stats.tx_bytes += total_tx_bytes; + netdev->stats.tx_packets += total_tx_packets; return (count < tx_ring->count); } @@ -705,6 +702,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter) static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, int *work_done, int work_to_do) { + struct e1000_hw *hw = &adapter->hw; union e1000_rx_desc_packet_split *rx_desc, *next_rxd; struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; @@ -748,8 +746,8 @@ static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, buffer_info->dma = 0; if (!(staterr & E1000_RXD_STAT_EOP)) { - e_dbg("%s: Packet Split buffers didn't pick up the " - "full packet\n", netdev->name); + e_dbg("Packet Split buffers didn't pick up the full " + "packet\n"); dev_kfree_skb_irq(skb); goto next_desc; } @@ -762,8 +760,8 @@ static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, length = le16_to_cpu(rx_desc->wb.middle.length0); if (!length) { - e_dbg("%s: Last part of the packet spanning multiple " - "descriptors\n", netdev->name); + e_dbg("Last part of the packet spanning multiple " + "descriptors\n"); dev_kfree_skb_irq(skb); goto next_desc; } @@ -871,8 +869,8 @@ next_desc: adapter->total_rx_bytes += total_rx_bytes; adapter->total_rx_packets += total_rx_packets; - adapter->net_stats.rx_bytes += total_rx_bytes; - adapter->net_stats.rx_packets += total_rx_packets; + netdev->stats.rx_bytes += total_rx_bytes; + netdev->stats.rx_packets += total_rx_packets; return cleaned; } @@ -1051,8 +1049,8 @@ next_desc: adapter->total_rx_bytes += total_rx_bytes; adapter->total_rx_packets += total_rx_packets; - adapter->net_stats.rx_bytes += total_rx_bytes; - adapter->net_stats.rx_packets += total_rx_packets; + netdev->stats.rx_bytes += total_rx_bytes; + netdev->stats.rx_packets += total_rx_packets; return cleaned; } @@ -1199,7 +1197,7 @@ static irqreturn_t e1000_intr(int irq, void *data) struct e1000_hw *hw = &adapter->hw; u32 rctl, icr = er32(ICR); - if (!icr) + if (!icr || test_bit(__E1000_DOWN, &adapter->state)) return IRQ_NONE; /* Not our interrupt */ /* @@ -1481,7 +1479,7 @@ static int e1000_request_msix(struct e1000_adapter *adapter) else memcpy(adapter->rx_ring->name, netdev->name, IFNAMSIZ); err = request_irq(adapter->msix_entries[vector].vector, - &e1000_intr_msix_rx, 0, adapter->rx_ring->name, + e1000_intr_msix_rx, 0, adapter->rx_ring->name, netdev); if (err) goto out; @@ -1494,7 +1492,7 @@ static int e1000_request_msix(struct e1000_adapter *adapter) else memcpy(adapter->tx_ring->name, netdev->name, IFNAMSIZ); err = request_irq(adapter->msix_entries[vector].vector, - &e1000_intr_msix_tx, 0, adapter->tx_ring->name, + e1000_intr_msix_tx, 0, adapter->tx_ring->name, netdev); if (err) goto out; @@ -1503,7 +1501,7 @@ static int e1000_request_msix(struct e1000_adapter *adapter) vector++; err = request_irq(adapter->msix_entries[vector].vector, - &e1000_msix_other, 0, netdev->name, netdev); + e1000_msix_other, 0, netdev->name, netdev); if (err) goto out; @@ -1534,7 +1532,7 @@ static int e1000_request_irq(struct e1000_adapter *adapter) e1000e_set_interrupt_capability(adapter); } if (adapter->flags & FLAG_MSI_ENABLED) { - err = request_irq(adapter->pdev->irq, &e1000_intr_msi, 0, + err = request_irq(adapter->pdev->irq, e1000_intr_msi, 0, netdev->name, netdev); if (!err) return err; @@ -1544,7 +1542,7 @@ static int e1000_request_irq(struct e1000_adapter *adapter) adapter->int_mode = E1000E_INT_MODE_LEGACY; } - err = request_irq(adapter->pdev->irq, &e1000_intr, IRQF_SHARED, + err = request_irq(adapter->pdev->irq, e1000_intr, IRQF_SHARED, netdev->name, netdev); if (err) e_err("Unable to allocate interrupt, Error: %d\n", err); @@ -2040,11 +2038,14 @@ static void e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid) E1000_MNG_DHCP_COOKIE_STATUS_VLAN) && (vid == adapter->mng_vlan_id)) return; + /* add VID to filter table */ - index = (vid >> 5) & 0x7F; - vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index); - vfta |= (1 << (vid & 0x1F)); - e1000e_write_vfta(hw, index, vfta); + if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER) { + index = (vid >> 5) & 0x7F; + vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index); + vfta |= (1 << (vid & 0x1F)); + hw->mac.ops.write_vfta(hw, index, vfta); + } } static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) @@ -2069,10 +2070,12 @@ static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) } /* remove VID from filter table */ - index = (vid >> 5) & 0x7F; - vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index); - vfta &= ~(1 << (vid & 0x1F)); - e1000e_write_vfta(hw, index, vfta); + if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER) { + index = (vid >> 5) & 0x7F; + vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index); + vfta &= ~(1 << (vid & 0x1F)); + hw->mac.ops.write_vfta(hw, index, vfta); + } } static void e1000_update_mng_vlan(struct e1000_adapter *adapter) @@ -2464,8 +2467,6 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) ew32(ITR, 1000000000 / (adapter->itr * 256)); ctrl_ext = er32(CTRL_EXT); - /* Reset delay timers after every interrupt */ - ctrl_ext |= E1000_CTRL_EXT_INT_TIMER_CLR; /* Auto-Mask interrupts upon ICR access */ ctrl_ext |= E1000_CTRL_EXT_IAME; ew32(IAM, 0xffffffff); @@ -2507,21 +2508,23 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) * packet size is equal or larger than the specified value (in 8 byte * units), e.g. using jumbo frames when setting to E1000_ERT_2048 */ - if ((adapter->flags & FLAG_HAS_ERT) && - (adapter->netdev->mtu > ETH_DATA_LEN)) { - u32 rxdctl = er32(RXDCTL(0)); - ew32(RXDCTL(0), rxdctl | 0x3); - ew32(ERT, E1000_ERT_2048 | (1 << 13)); - /* - * With jumbo frames and early-receive enabled, excessive - * C4->C2 latencies result in dropped transactions. - */ - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, - e1000e_driver_name, 55); - } else { - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, - e1000e_driver_name, - PM_QOS_DEFAULT_VALUE); + if (adapter->flags & FLAG_HAS_ERT) { + if (adapter->netdev->mtu > ETH_DATA_LEN) { + u32 rxdctl = er32(RXDCTL(0)); + ew32(RXDCTL(0), rxdctl | 0x3); + ew32(ERT, E1000_ERT_2048 | (1 << 13)); + /* + * With jumbo frames and early-receive enabled, + * excessive C-state transition latencies result in + * dropped transactions. + */ + pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, + adapter->netdev->name, 55); + } else { + pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, + adapter->netdev->name, + PM_QOS_DEFAULT_VALUE); + } } /* Enable Receives */ @@ -2645,18 +2648,8 @@ static void e1000_configure(struct e1000_adapter *adapter) **/ void e1000e_power_up_phy(struct e1000_adapter *adapter) { - u16 mii_reg = 0; - - /* Just clear the power down bit to wake the phy back up */ - if (adapter->hw.phy.media_type == e1000_media_type_copper) { - /* - * According to the manual, the phy will retain its - * settings across a power-down/up cycle - */ - e1e_rphy(&adapter->hw, PHY_CONTROL, &mii_reg); - mii_reg &= ~MII_CR_POWER_DOWN; - e1e_wphy(&adapter->hw, PHY_CONTROL, mii_reg); - } + if (adapter->hw.phy.ops.power_up) + adapter->hw.phy.ops.power_up(&adapter->hw); adapter->hw.mac.ops.setup_link(&adapter->hw); } @@ -2664,35 +2657,17 @@ void e1000e_power_up_phy(struct e1000_adapter *adapter) /** * e1000_power_down_phy - Power down the PHY * - * Power down the PHY so no link is implied when interface is down - * The PHY cannot be powered down is management or WoL is active + * Power down the PHY so no link is implied when interface is down. + * The PHY cannot be powered down if management or WoL is active. */ static void e1000_power_down_phy(struct e1000_adapter *adapter) { - struct e1000_hw *hw = &adapter->hw; - u16 mii_reg; - /* WoL is enabled */ if (adapter->wol) return; - /* non-copper PHY? */ - if (adapter->hw.phy.media_type != e1000_media_type_copper) - return; - - /* reset is blocked because of a SoL/IDER session */ - if (e1000e_check_mng_mode(hw) || e1000_check_reset_block(hw)) - return; - - /* manageability (AMT) is enabled */ - if (er32(MANC) & E1000_MANC_SMBUS_EN) - return; - - /* power down the PHY */ - e1e_rphy(hw, PHY_CONTROL, &mii_reg); - mii_reg |= MII_CR_POWER_DOWN; - e1e_wphy(hw, PHY_CONTROL, mii_reg); - mdelay(1); + if (adapter->hw.phy.ops.power_down) + adapter->hw.phy.ops.power_down(&adapter->hw); } /** @@ -2856,6 +2831,12 @@ int e1000e_up(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; + /* DMA latency requirement to workaround early-receive/jumbo issue */ + if (adapter->flags & FLAG_HAS_ERT) + pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, + adapter->netdev->name, + PM_QOS_DEFAULT_VALUE); + /* hardware has been reset, we need to reload some things */ e1000_configure(adapter); @@ -2916,6 +2897,10 @@ void e1000e_down(struct e1000_adapter *adapter) e1000_clean_tx_ring(adapter); e1000_clean_rx_ring(adapter); + if (adapter->flags & FLAG_HAS_ERT) + pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, + adapter->netdev->name); + /* * TODO: for power management, we could drop the link and * pci_disable_device here. @@ -2973,7 +2958,7 @@ static irqreturn_t e1000_intr_msi_test(int irq, void *data) struct e1000_hw *hw = &adapter->hw; u32 icr = er32(ICR); - e_dbg("%s: icr is %08X\n", netdev->name, icr); + e_dbg("icr is %08X\n", icr); if (icr & E1000_ICR_RXSEQ) { adapter->flags &= ~FLAG_MSI_TEST_FAILED; wmb(); @@ -3010,7 +2995,7 @@ static int e1000_test_msi_interrupt(struct e1000_adapter *adapter) if (err) goto msi_test_failed; - err = request_irq(adapter->pdev->irq, &e1000_intr_msi_test, 0, + err = request_irq(adapter->pdev->irq, e1000_intr_msi_test, 0, netdev->name, netdev); if (err) { pci_disable_msi(adapter->pdev); @@ -3043,7 +3028,7 @@ static int e1000_test_msi_interrupt(struct e1000_adapter *adapter) goto msi_test_failed; /* okay so the test worked, restore settings */ - e_dbg("%s: MSI interrupt test succeeded!\n", netdev->name); + e_dbg("MSI interrupt test succeeded!\n"); msi_test_failed: e1000e_set_interrupt_capability(adapter); e1000_request_irq(adapter); @@ -3304,6 +3289,7 @@ static void e1000_update_phy_info(unsigned long data) **/ void e1000e_update_stats(struct e1000_adapter *adapter) { + struct net_device *netdev = adapter->netdev; struct e1000_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; u16 phy_data; @@ -3398,8 +3384,8 @@ void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.tsctfc += er32(TSCTFC); /* Fill out the OS statistics structure */ - adapter->net_stats.multicast = adapter->stats.mprc; - adapter->net_stats.collisions = adapter->stats.colc; + netdev->stats.multicast = adapter->stats.mprc; + netdev->stats.collisions = adapter->stats.colc; /* Rx Errors */ @@ -3407,22 +3393,22 @@ void e1000e_update_stats(struct e1000_adapter *adapter) * RLEC on some newer hardware can be incorrect so build * our own version based on RUC and ROC */ - adapter->net_stats.rx_errors = adapter->stats.rxerrc + + netdev->stats.rx_errors = adapter->stats.rxerrc + adapter->stats.crcerrs + adapter->stats.algnerrc + adapter->stats.ruc + adapter->stats.roc + adapter->stats.cexterr; - adapter->net_stats.rx_length_errors = adapter->stats.ruc + + netdev->stats.rx_length_errors = adapter->stats.ruc + adapter->stats.roc; - adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs; - adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc; - adapter->net_stats.rx_missed_errors = adapter->stats.mpc; + netdev->stats.rx_crc_errors = adapter->stats.crcerrs; + netdev->stats.rx_frame_errors = adapter->stats.algnerrc; + netdev->stats.rx_missed_errors = adapter->stats.mpc; /* Tx Errors */ - adapter->net_stats.tx_errors = adapter->stats.ecol + + netdev->stats.tx_errors = adapter->stats.ecol + adapter->stats.latecol; - adapter->net_stats.tx_aborted_errors = adapter->stats.ecol; - adapter->net_stats.tx_window_errors = adapter->stats.latecol; - adapter->net_stats.tx_carrier_errors = adapter->stats.tncrs; + netdev->stats.tx_aborted_errors = adapter->stats.ecol; + netdev->stats.tx_window_errors = adapter->stats.latecol; + netdev->stats.tx_carrier_errors = adapter->stats.tncrs; /* Tx Dropped needs to be maintained elsewhere */ @@ -3776,68 +3762,64 @@ static int e1000_tso(struct e1000_adapter *adapter, u8 ipcss, ipcso, tucss, tucso, hdr_len; int err; - if (skb_is_gso(skb)) { - if (skb_header_cloned(skb)) { - err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); - if (err) - return err; - } + if (!skb_is_gso(skb)) + return 0; - hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); - mss = skb_shinfo(skb)->gso_size; - if (skb->protocol == htons(ETH_P_IP)) { - struct iphdr *iph = ip_hdr(skb); - iph->tot_len = 0; - iph->check = 0; - tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, - iph->daddr, 0, - IPPROTO_TCP, - 0); - cmd_length = E1000_TXD_CMD_IP; - ipcse = skb_transport_offset(skb) - 1; - } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) { - ipv6_hdr(skb)->payload_len = 0; - tcp_hdr(skb)->check = - ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - 0, IPPROTO_TCP, 0); - ipcse = 0; - } - ipcss = skb_network_offset(skb); - ipcso = (void *)&(ip_hdr(skb)->check) - (void *)skb->data; - tucss = skb_transport_offset(skb); - tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data; - tucse = 0; + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (err) + return err; + } - cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE | - E1000_TXD_CMD_TCP | (skb->len - (hdr_len))); + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + mss = skb_shinfo(skb)->gso_size; + if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *iph = ip_hdr(skb); + iph->tot_len = 0; + iph->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, + 0, IPPROTO_TCP, 0); + cmd_length = E1000_TXD_CMD_IP; + ipcse = skb_transport_offset(skb) - 1; + } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) { + ipv6_hdr(skb)->payload_len = 0; + tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + ipcse = 0; + } + ipcss = skb_network_offset(skb); + ipcso = (void *)&(ip_hdr(skb)->check) - (void *)skb->data; + tucss = skb_transport_offset(skb); + tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data; + tucse = 0; - i = tx_ring->next_to_use; - context_desc = E1000_CONTEXT_DESC(*tx_ring, i); - buffer_info = &tx_ring->buffer_info[i]; + cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE | + E1000_TXD_CMD_TCP | (skb->len - (hdr_len))); - context_desc->lower_setup.ip_fields.ipcss = ipcss; - context_desc->lower_setup.ip_fields.ipcso = ipcso; - context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse); - context_desc->upper_setup.tcp_fields.tucss = tucss; - context_desc->upper_setup.tcp_fields.tucso = tucso; - context_desc->upper_setup.tcp_fields.tucse = cpu_to_le16(tucse); - context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss); - context_desc->tcp_seg_setup.fields.hdr_len = hdr_len; - context_desc->cmd_and_length = cpu_to_le32(cmd_length); + i = tx_ring->next_to_use; + context_desc = E1000_CONTEXT_DESC(*tx_ring, i); + buffer_info = &tx_ring->buffer_info[i]; - buffer_info->time_stamp = jiffies; - buffer_info->next_to_watch = i; + context_desc->lower_setup.ip_fields.ipcss = ipcss; + context_desc->lower_setup.ip_fields.ipcso = ipcso; + context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse); + context_desc->upper_setup.tcp_fields.tucss = tucss; + context_desc->upper_setup.tcp_fields.tucso = tucso; + context_desc->upper_setup.tcp_fields.tucse = cpu_to_le16(tucse); + context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss); + context_desc->tcp_seg_setup.fields.hdr_len = hdr_len; + context_desc->cmd_and_length = cpu_to_le32(cmd_length); - i++; - if (i == tx_ring->count) - i = 0; - tx_ring->next_to_use = i; + buffer_info->time_stamp = jiffies; + buffer_info->next_to_watch = i; - return 1; - } + i++; + if (i == tx_ring->count) + i = 0; + tx_ring->next_to_use = i; - return 0; + return 1; } static bool e1000_tx_csum(struct e1000_adapter *adapter, struct sk_buff *skb) @@ -3909,23 +3891,14 @@ static int e1000_tx_map(struct e1000_adapter *adapter, unsigned int mss) { struct e1000_ring *tx_ring = adapter->tx_ring; + struct pci_dev *pdev = adapter->pdev; struct e1000_buffer *buffer_info; unsigned int len = skb_headlen(skb); - unsigned int offset, size, count = 0, i; + unsigned int offset = 0, size, count = 0, i; unsigned int f; - dma_addr_t *map; i = tx_ring->next_to_use; - if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) { - dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); - adapter->tx_dma_failed++; - return 0; - } - - map = skb_shinfo(skb)->dma_maps; - offset = 0; - while (len) { buffer_info = &tx_ring->buffer_info[i]; size = min(len, max_per_txd); @@ -3933,11 +3906,15 @@ static int e1000_tx_map(struct e1000_adapter *adapter, buffer_info->length = size; buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; - buffer_info->dma = skb_shinfo(skb)->dma_head + offset; - count++; + buffer_info->dma = pci_map_single(pdev, skb->data + offset, + size, PCI_DMA_TODEVICE); + buffer_info->mapped_as_page = false; + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; len -= size; offset += size; + count++; if (len) { i++; @@ -3951,7 +3928,7 @@ static int e1000_tx_map(struct e1000_adapter *adapter, frag = &skb_shinfo(skb)->frags[f]; len = frag->size; - offset = 0; + offset = frag->page_offset; while (len) { i++; @@ -3964,7 +3941,12 @@ static int e1000_tx_map(struct e1000_adapter *adapter, buffer_info->length = size; buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; - buffer_info->dma = map[f] + offset; + buffer_info->dma = pci_map_page(pdev, frag->page, + offset, size, + PCI_DMA_TODEVICE); + buffer_info->mapped_as_page = true; + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; len -= size; offset += size; @@ -3976,6 +3958,22 @@ static int e1000_tx_map(struct e1000_adapter *adapter, tx_ring->buffer_info[first].next_to_watch = i; return count; + +dma_error: + dev_err(&pdev->dev, "TX DMA map failed\n"); + buffer_info->dma = 0; + count--; + + while (count >= 0) { + count--; + i--; + if (i < 0) + i += tx_ring->count; + buffer_info = &tx_ring->buffer_info[i]; + e1000_put_txbuf(adapter, buffer_info);; + } + + return 0; } static void e1000_tx_queue(struct e1000_adapter *adapter, @@ -4048,8 +4046,8 @@ static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter, u16 length, offset; if (vlan_tx_tag_present(skb)) { - if (!((vlan_tx_tag_get(skb) == adapter->hw.mng_cookie.vlan_id) - && (adapter->hw.mng_cookie.status & + if (!((vlan_tx_tag_get(skb) == adapter->hw.mng_cookie.vlan_id) && + (adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN))) return 0; } @@ -4271,10 +4269,8 @@ static void e1000_reset_task(struct work_struct *work) **/ static struct net_device_stats *e1000_get_stats(struct net_device *netdev) { - struct e1000_adapter *adapter = netdev_priv(netdev); - /* only return the current stats */ - return &adapter->net_stats; + return &netdev->stats; } /** @@ -4362,6 +4358,8 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, data->phy_id = adapter->hw.phy.addr; break; case SIOCGMIIREG: + e1000_phy_read_status(adapter); + switch (data->reg_num & 0x1F) { case MII_BMCR: data->val_out = adapter->phy_regs.bmcr; @@ -4469,7 +4467,7 @@ static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc) e1e_wphy(&adapter->hw, BM_WUC, E1000_WUC_PME_EN); /* activate PHY wakeup */ - retval = hw->phy.ops.acquire_phy(hw); + retval = hw->phy.ops.acquire(hw); if (retval) { e_err("Could not acquire PHY\n"); return retval; @@ -4486,7 +4484,7 @@ static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc) if (retval) e_err("Could not set PHY Host Wakeup bit\n"); out: - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return retval; } @@ -5160,6 +5158,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task); INIT_WORK(&adapter->downshift_task, e1000e_downshift_workaround); INIT_WORK(&adapter->update_phy_task, e1000e_update_phy_task); + INIT_WORK(&adapter->print_hang_task, e1000_print_hw_hang); /* Initialize link parameters. User can change them with ethtool */ adapter->hw.mac.autoneg = 1; @@ -5283,19 +5282,24 @@ static void __devexit e1000_remove(struct pci_dev *pdev) del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_info_timer); + cancel_work_sync(&adapter->reset_task); + cancel_work_sync(&adapter->watchdog_task); + cancel_work_sync(&adapter->downshift_task); + cancel_work_sync(&adapter->update_phy_task); + cancel_work_sync(&adapter->print_hang_task); flush_scheduled_work(); + if (!(netdev->flags & IFF_UP)) + e1000_power_down_phy(adapter); + + unregister_netdev(netdev); + /* * Release control of h/w to f/w. If f/w is AMT enabled, this * would have already happened in close and is redundant. */ e1000_release_hw_control(adapter); - unregister_netdev(netdev); - - if (!e1000_check_reset_block(&adapter->hw)) - e1000_phy_hw_reset(&adapter->hw); - e1000e_reset_interrupt_capability(adapter); kfree(adapter->tx_ring); kfree(adapter->rx_ring); @@ -5361,6 +5365,7 @@ static struct pci_device_id e1000_pci_tbl[] = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_C), board_ich8lan }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_M), board_ich8lan }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_M_AMT), board_ich8lan }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_82567V_3), board_ich8lan }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE), board_ich9lan }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE_G), board_ich9lan }, @@ -5414,12 +5419,10 @@ static int __init e1000_init_module(void) int ret; printk(KERN_INFO "%s: Intel(R) PRO/1000 Network Driver - %s\n", e1000e_driver_name, e1000e_driver_version); - printk(KERN_INFO "%s: Copyright (c) 1999-2008 Intel Corporation.\n", + printk(KERN_INFO "%s: Copyright (c) 1999 - 2009 Intel Corporation.\n", e1000e_driver_name); ret = pci_register_driver(&e1000_driver); - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, e1000e_driver_name, - PM_QOS_DEFAULT_VALUE); - + return ret; } module_init(e1000_init_module); @@ -5433,7 +5436,6 @@ module_init(e1000_init_module); static void __exit e1000_exit_module(void) { pci_unregister_driver(&e1000_driver); - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, e1000e_driver_name); } module_exit(e1000_exit_module); diff --git a/drivers/net/e1000e/param.c b/drivers/net/e1000e/param.c index 1342e0b1815c..2e399778cae5 100644 --- a/drivers/net/e1000e/param.c +++ b/drivers/net/e1000e/param.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c index 85f955f70417..55a2c0acfee7 100644 --- a/drivers/net/e1000e/phy.c +++ b/drivers/net/e1000e/phy.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2008 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -44,6 +44,8 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, /* Cable length tables */ static const u16 e1000_m88_cable_length_table[] = { 0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED }; +#define M88E1000_CABLE_LENGTH_TABLE_SIZE \ + ARRAY_SIZE(e1000_m88_cable_length_table) static const u16 e1000_igp_2_cable_length_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21, 0, 0, 0, 3, @@ -130,7 +132,7 @@ s32 e1000e_get_phy_id(struct e1000_hw *hw) u16 phy_id; u16 retry_count = 0; - if (!(phy->ops.read_phy_reg)) + if (!(phy->ops.read_reg)) goto out; while (retry_count < 2) { @@ -151,29 +153,29 @@ s32 e1000e_get_phy_id(struct e1000_hw *hw) goto out; /* - * If the PHY ID is still unknown, we may have an 82577i - * without link. We will try again after setting Slow - * MDIC mode. No harm in trying again in this case since - * the PHY ID is unknown at this point anyway + * If the PHY ID is still unknown, we may have an 82577 + * without link. We will try again after setting Slow MDIC + * mode. No harm in trying again in this case since the PHY + * ID is unknown at this point anyway. */ - ret_val = phy->ops.acquire_phy(hw); + ret_val = phy->ops.acquire(hw); if (ret_val) goto out; ret_val = e1000_set_mdio_slow_mode_hv(hw, true); if (ret_val) goto out; - phy->ops.release_phy(hw); + phy->ops.release(hw); retry_count++; } out: /* Revert to MDIO fast mode, if applicable */ if (retry_count) { - ret_val = phy->ops.acquire_phy(hw); + ret_val = phy->ops.acquire(hw); if (ret_val) return ret_val; ret_val = e1000_set_mdio_slow_mode_hv(hw, false); - phy->ops.release_phy(hw); + phy->ops.release(hw); } return ret_val; @@ -211,7 +213,7 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) u32 i, mdic = 0; if (offset > MAX_PHY_REG_ADDRESS) { - hw_dbg(hw, "PHY Address %d is out of range\n", offset); + e_dbg("PHY Address %d is out of range\n", offset); return -E1000_ERR_PARAM; } @@ -238,11 +240,11 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) break; } if (!(mdic & E1000_MDIC_READY)) { - hw_dbg(hw, "MDI Read did not complete\n"); + e_dbg("MDI Read did not complete\n"); return -E1000_ERR_PHY; } if (mdic & E1000_MDIC_ERROR) { - hw_dbg(hw, "MDI Error\n"); + e_dbg("MDI Error\n"); return -E1000_ERR_PHY; } *data = (u16) mdic; @@ -264,7 +266,7 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) u32 i, mdic = 0; if (offset > MAX_PHY_REG_ADDRESS) { - hw_dbg(hw, "PHY Address %d is out of range\n", offset); + e_dbg("PHY Address %d is out of range\n", offset); return -E1000_ERR_PARAM; } @@ -292,11 +294,11 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) break; } if (!(mdic & E1000_MDIC_READY)) { - hw_dbg(hw, "MDI Write did not complete\n"); + e_dbg("MDI Write did not complete\n"); return -E1000_ERR_PHY; } if (mdic & E1000_MDIC_ERROR) { - hw_dbg(hw, "MDI Error\n"); + e_dbg("MDI Error\n"); return -E1000_ERR_PHY; } @@ -317,14 +319,14 @@ s32 e1000e_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data) { s32 ret_val; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -342,14 +344,14 @@ s32 e1000e_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data) { s32 ret_val; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -371,10 +373,10 @@ static s32 __e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data, s32 ret_val = 0; if (!locked) { - if (!(hw->phy.ops.acquire_phy)) + if (!(hw->phy.ops.acquire)) goto out; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) goto out; } @@ -392,7 +394,7 @@ static s32 __e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data, release: if (!locked) - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); out: return ret_val; } @@ -442,10 +444,10 @@ static s32 __e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data, s32 ret_val = 0; if (!locked) { - if (!(hw->phy.ops.acquire_phy)) + if (!(hw->phy.ops.acquire)) goto out; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) goto out; } @@ -463,7 +465,7 @@ static s32 __e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data, release: if (!locked) - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); out: return ret_val; @@ -515,10 +517,10 @@ static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data, s32 ret_val = 0; if (!locked) { - if (!(hw->phy.ops.acquire_phy)) + if (!(hw->phy.ops.acquire)) goto out; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) goto out; } @@ -533,7 +535,7 @@ static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data, *data = (u16)kmrnctrlsta; if (!locked) - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); out: return ret_val; @@ -587,10 +589,10 @@ static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data, s32 ret_val = 0; if (!locked) { - if (!(hw->phy.ops.acquire_phy)) + if (!(hw->phy.ops.acquire)) goto out; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) goto out; } @@ -602,7 +604,7 @@ static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data, udelay(2); if (!locked) - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); out: return ret_val; @@ -649,7 +651,7 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) u16 phy_data; /* Enable CRS on TX. This must be set for half-duplex operation. */ - ret_val = phy->ops.read_phy_reg(hw, I82577_CFG_REG, &phy_data); + ret_val = phy->ops.read_reg(hw, I82577_CFG_REG, &phy_data); if (ret_val) goto out; @@ -658,7 +660,7 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) /* Enable downshift */ phy_data |= I82577_CFG_ENABLE_DOWNSHIFT; - ret_val = phy->ops.write_phy_reg(hw, I82577_CFG_REG, phy_data); + ret_val = phy->ops.write_reg(hw, I82577_CFG_REG, phy_data); out: return ret_val; @@ -776,12 +778,12 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw) /* Commit the changes. */ ret_val = e1000e_commit_phy(hw); if (ret_val) { - hw_dbg(hw, "Error committing the PHY changes\n"); + e_dbg("Error committing the PHY changes\n"); return ret_val; } if (phy->type == e1000_phy_82578) { - ret_val = phy->ops.read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); if (ret_val) return ret_val; @@ -789,7 +791,7 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw) /* 82578 PHY - set the downshift count to 1x. */ phy_data |= I82578_EPSCR_DOWNSHIFT_ENABLE; phy_data &= ~I82578_EPSCR_DOWNSHIFT_COUNTER_MASK; - ret_val = phy->ops.write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); if (ret_val) return ret_val; @@ -813,7 +815,7 @@ s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw) ret_val = e1000_phy_hw_reset(hw); if (ret_val) { - hw_dbg(hw, "Error resetting the PHY.\n"); + e_dbg("Error resetting the PHY.\n"); return ret_val; } @@ -824,9 +826,9 @@ s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw) msleep(100); /* disable lplu d0 during driver init */ - ret_val = e1000_set_d0_lplu_state(hw, 0); + ret_val = e1000_set_d0_lplu_state(hw, false); if (ret_val) { - hw_dbg(hw, "Error Disabling LPLU D0\n"); + e_dbg("Error Disabling LPLU D0\n"); return ret_val; } /* Configure mdi-mdix settings */ @@ -962,39 +964,39 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) NWAY_AR_10T_HD_CAPS); mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS); - hw_dbg(hw, "autoneg_advertised %x\n", phy->autoneg_advertised); + e_dbg("autoneg_advertised %x\n", phy->autoneg_advertised); /* Do we want to advertise 10 Mb Half Duplex? */ if (phy->autoneg_advertised & ADVERTISE_10_HALF) { - hw_dbg(hw, "Advertise 10mb Half duplex\n"); + e_dbg("Advertise 10mb Half duplex\n"); mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; } /* Do we want to advertise 10 Mb Full Duplex? */ if (phy->autoneg_advertised & ADVERTISE_10_FULL) { - hw_dbg(hw, "Advertise 10mb Full duplex\n"); + e_dbg("Advertise 10mb Full duplex\n"); mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; } /* Do we want to advertise 100 Mb Half Duplex? */ if (phy->autoneg_advertised & ADVERTISE_100_HALF) { - hw_dbg(hw, "Advertise 100mb Half duplex\n"); + e_dbg("Advertise 100mb Half duplex\n"); mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; } /* Do we want to advertise 100 Mb Full Duplex? */ if (phy->autoneg_advertised & ADVERTISE_100_FULL) { - hw_dbg(hw, "Advertise 100mb Full duplex\n"); + e_dbg("Advertise 100mb Full duplex\n"); mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; } /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ if (phy->autoneg_advertised & ADVERTISE_1000_HALF) - hw_dbg(hw, "Advertise 1000mb Half duplex request denied!\n"); + e_dbg("Advertise 1000mb Half duplex request denied!\n"); /* Do we want to advertise 1000 Mb Full Duplex? */ if (phy->autoneg_advertised & ADVERTISE_1000_FULL) { - hw_dbg(hw, "Advertise 1000mb Full duplex\n"); + e_dbg("Advertise 1000mb Full duplex\n"); mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; } @@ -1053,7 +1055,7 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); break; default: - hw_dbg(hw, "Flow control param set incorrectly\n"); + e_dbg("Flow control param set incorrectly\n"); ret_val = -E1000_ERR_CONFIG; return ret_val; } @@ -1062,7 +1064,7 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) if (ret_val) return ret_val; - hw_dbg(hw, "Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + e_dbg("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); if (phy->autoneg_mask & ADVERTISE_1000_FULL) { ret_val = e1e_wphy(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); @@ -1099,13 +1101,13 @@ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) if (phy->autoneg_advertised == 0) phy->autoneg_advertised = phy->autoneg_mask; - hw_dbg(hw, "Reconfiguring auto-neg advertisement params\n"); + e_dbg("Reconfiguring auto-neg advertisement params\n"); ret_val = e1000_phy_setup_autoneg(hw); if (ret_val) { - hw_dbg(hw, "Error Setting up Auto-Negotiation\n"); + e_dbg("Error Setting up Auto-Negotiation\n"); return ret_val; } - hw_dbg(hw, "Restarting Auto-Neg\n"); + e_dbg("Restarting Auto-Neg\n"); /* * Restart auto-negotiation by setting the Auto Neg Enable bit and @@ -1127,7 +1129,7 @@ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) if (phy->autoneg_wait_to_complete) { ret_val = e1000_wait_autoneg(hw); if (ret_val) { - hw_dbg(hw, "Error while waiting for " + e_dbg("Error while waiting for " "autoneg to complete\n"); return ret_val; } @@ -1165,10 +1167,10 @@ s32 e1000e_setup_copper_link(struct e1000_hw *hw) * PHY will be set to 10H, 10F, 100H or 100F * depending on user settings. */ - hw_dbg(hw, "Forcing Speed and Duplex\n"); + e_dbg("Forcing Speed and Duplex\n"); ret_val = e1000_phy_force_speed_duplex(hw); if (ret_val) { - hw_dbg(hw, "Error Forcing Speed and Duplex\n"); + e_dbg("Error Forcing Speed and Duplex\n"); return ret_val; } } @@ -1185,11 +1187,11 @@ s32 e1000e_setup_copper_link(struct e1000_hw *hw) return ret_val; if (link) { - hw_dbg(hw, "Valid link established!!!\n"); + e_dbg("Valid link established!!!\n"); e1000e_config_collision_dist(hw); ret_val = e1000e_config_fc_after_link_up(hw); } else { - hw_dbg(hw, "Unable to establish link!!!\n"); + e_dbg("Unable to establish link!!!\n"); } return ret_val; @@ -1235,12 +1237,12 @@ s32 e1000e_phy_force_speed_duplex_igp(struct e1000_hw *hw) if (ret_val) return ret_val; - hw_dbg(hw, "IGP PSCR: %X\n", phy_data); + e_dbg("IGP PSCR: %X\n", phy_data); udelay(1); if (phy->autoneg_wait_to_complete) { - hw_dbg(hw, "Waiting for forced speed/duplex link on IGP phy.\n"); + e_dbg("Waiting for forced speed/duplex link on IGP phy.\n"); ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT, @@ -1250,7 +1252,7 @@ s32 e1000e_phy_force_speed_duplex_igp(struct e1000_hw *hw) return ret_val; if (!link) - hw_dbg(hw, "Link taking longer than expected.\n"); + e_dbg("Link taking longer than expected.\n"); /* Try once more */ ret_val = e1000e_phy_has_link_generic(hw, @@ -1294,7 +1296,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) if (ret_val) return ret_val; - hw_dbg(hw, "M88E1000 PSCR: %X\n", phy_data); + e_dbg("M88E1000 PSCR: %X\n", phy_data); ret_val = e1e_rphy(hw, PHY_CONTROL, &phy_data); if (ret_val) @@ -1312,7 +1314,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) return ret_val; if (phy->autoneg_wait_to_complete) { - hw_dbg(hw, "Waiting for forced speed/duplex link on M88 phy.\n"); + e_dbg("Waiting for forced speed/duplex link on M88 phy.\n"); ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); @@ -1320,17 +1322,22 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) return ret_val; if (!link) { - /* - * We didn't get link. - * Reset the DSP and cross our fingers. - */ - ret_val = e1e_wphy(hw, M88E1000_PHY_PAGE_SELECT, - 0x001d); - if (ret_val) - return ret_val; - ret_val = e1000e_phy_reset_dsp(hw); - if (ret_val) - return ret_val; + if (hw->phy.type != e1000_phy_m88) { + e_dbg("Link taking longer than expected.\n"); + } else { + /* + * We didn't get link. + * Reset the DSP and cross our fingers. + */ + ret_val = e1e_wphy(hw, + M88E1000_PHY_PAGE_SELECT, + 0x001d); + if (ret_val) + return ret_val; + ret_val = e1000e_phy_reset_dsp(hw); + if (ret_val) + return ret_val; + } } /* Try once more */ @@ -1340,6 +1347,9 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) return ret_val; } + if (hw->phy.type != e1000_phy_m88) + return 0; + ret_val = e1e_rphy(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); if (ret_val) return ret_val; @@ -1369,6 +1379,73 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) } /** + * e1000_phy_force_speed_duplex_ife - Force PHY speed & duplex + * @hw: pointer to the HW structure + * + * Forces the speed and duplex settings of the PHY. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + ret_val = e1e_rphy(hw, PHY_CONTROL, &data); + if (ret_val) + goto out; + + e1000e_phy_force_speed_duplex_setup(hw, &data); + + ret_val = e1e_wphy(hw, PHY_CONTROL, data); + if (ret_val) + goto out; + + /* Disable MDI-X support for 10/100 */ + ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data); + if (ret_val) + goto out; + + data &= ~IFE_PMC_AUTO_MDIX; + data &= ~IFE_PMC_FORCE_MDIX; + + ret_val = e1e_wphy(hw, IFE_PHY_MDIX_CONTROL, data); + if (ret_val) + goto out; + + e_dbg("IFE PMC: %X\n", data); + + udelay(1); + + if (phy->autoneg_wait_to_complete) { + e_dbg("Waiting for forced speed/duplex link on IFE phy.\n"); + + ret_val = e1000e_phy_has_link_generic(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + + if (!link) + e_dbg("Link taking longer than expected.\n"); + + /* Try once more */ + ret_val = e1000e_phy_has_link_generic(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** * e1000e_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex * @hw: pointer to the HW structure * @phy_ctrl: pointer to current value of PHY_CONTROL @@ -1403,11 +1480,11 @@ void e1000e_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl) if (mac->forced_speed_duplex & E1000_ALL_HALF_DUPLEX) { ctrl &= ~E1000_CTRL_FD; *phy_ctrl &= ~MII_CR_FULL_DUPLEX; - hw_dbg(hw, "Half Duplex\n"); + e_dbg("Half Duplex\n"); } else { ctrl |= E1000_CTRL_FD; *phy_ctrl |= MII_CR_FULL_DUPLEX; - hw_dbg(hw, "Full Duplex\n"); + e_dbg("Full Duplex\n"); } /* Forcing 10mb or 100mb? */ @@ -1415,12 +1492,12 @@ void e1000e_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl) ctrl |= E1000_CTRL_SPD_100; *phy_ctrl |= MII_CR_SPEED_100; *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10); - hw_dbg(hw, "Forcing 100mb\n"); + e_dbg("Forcing 100mb\n"); } else { ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); *phy_ctrl |= MII_CR_SPEED_10; *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); - hw_dbg(hw, "Forcing 10mb\n"); + e_dbg("Forcing 10mb\n"); } e1000e_config_collision_dist(hw); @@ -1523,8 +1600,8 @@ s32 e1000e_check_downshift(struct e1000_hw *hw) switch (phy->type) { case e1000_phy_m88: case e1000_phy_gg82563: + case e1000_phy_bm: case e1000_phy_82578: - case e1000_phy_82577: offset = M88E1000_PHY_SPEC_STATUS; mask = M88E1000_PSSR_DOWNSHIFT; break; @@ -1535,7 +1612,7 @@ s32 e1000e_check_downshift(struct e1000_hw *hw) break; default: /* speed downshift not supported */ - phy->speed_downgraded = 0; + phy->speed_downgraded = false; return 0; } @@ -1555,7 +1632,7 @@ s32 e1000e_check_downshift(struct e1000_hw *hw) * * Polarity is determined based on the PHY specific status register. **/ -static s32 e1000_check_polarity_m88(struct e1000_hw *hw) +s32 e1000_check_polarity_m88(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; s32 ret_val; @@ -1580,7 +1657,7 @@ static s32 e1000_check_polarity_m88(struct e1000_hw *hw) * Polarity is determined based on the PHY port status register, and the * current speed (since there is no polarity at 100Mbps). **/ -static s32 e1000_check_polarity_igp(struct e1000_hw *hw) +s32 e1000_check_polarity_igp(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; s32 ret_val; @@ -1618,6 +1695,39 @@ static s32 e1000_check_polarity_igp(struct e1000_hw *hw) } /** + * e1000_check_polarity_ife - Check cable polarity for IFE PHY + * @hw: pointer to the HW structure + * + * Polarity is determined on the polarity reversal feature being enabled. + **/ +s32 e1000_check_polarity_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, offset, mask; + + /* + * Polarity is determined based on the reversal feature being enabled. + */ + if (phy->polarity_correction) { + offset = IFE_PHY_EXTENDED_STATUS_CONTROL; + mask = IFE_PESC_POLARITY_REVERSED; + } else { + offset = IFE_PHY_SPECIAL_CONTROL; + mask = IFE_PSC_FORCE_POLARITY; + } + + ret_val = e1e_rphy(hw, offset, &phy_data); + + if (!ret_val) + phy->cable_polarity = (phy_data & mask) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal; + + return ret_val; +} + +/** * e1000_wait_autoneg - Wait for auto-neg completion * @hw: pointer to the HW structure * @@ -1717,15 +1827,21 @@ s32 e1000e_get_cable_length_m88(struct e1000_hw *hw) ret_val = e1e_rphy(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) - return ret_val; + goto out; index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> - M88E1000_PSSR_CABLE_LENGTH_SHIFT; + M88E1000_PSSR_CABLE_LENGTH_SHIFT; + if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) { + ret_val = -E1000_ERR_PHY; + goto out; + } + phy->min_cable_length = e1000_m88_cable_length_table[index]; - phy->max_cable_length = e1000_m88_cable_length_table[index+1]; + phy->max_cable_length = e1000_m88_cable_length_table[index + 1]; phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; +out: return ret_val; } @@ -1736,7 +1852,7 @@ s32 e1000e_get_cable_length_m88(struct e1000_hw *hw) * The automatic gain control (agc) normalizes the amplitude of the * received signal, adjusting for the attenuation produced by the * cable. By reading the AGC registers, which represent the - * combination of course and fine gain value, the value can be put + * combination of coarse and fine gain value, the value can be put * into a lookup table to obtain the approximate cable length * for each channel. **/ @@ -1761,7 +1877,7 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw) /* * Getting bits 15:9, which represent the combination of - * course and fine gain values. The result is a number + * coarse and fine gain values. The result is a number * that can be put into the lookup table to obtain the * approximate cable length. */ @@ -1815,8 +1931,8 @@ s32 e1000e_get_phy_info_m88(struct e1000_hw *hw) u16 phy_data; bool link; - if (hw->phy.media_type != e1000_media_type_copper) { - hw_dbg(hw, "Phy info is only valid for copper media\n"); + if (phy->media_type != e1000_media_type_copper) { + e_dbg("Phy info is only valid for copper media\n"); return -E1000_ERR_CONFIG; } @@ -1825,7 +1941,7 @@ s32 e1000e_get_phy_info_m88(struct e1000_hw *hw) return ret_val; if (!link) { - hw_dbg(hw, "Phy info is only valid if link is up\n"); + e_dbg("Phy info is only valid if link is up\n"); return -E1000_ERR_CONFIG; } @@ -1893,11 +2009,11 @@ s32 e1000e_get_phy_info_igp(struct e1000_hw *hw) return ret_val; if (!link) { - hw_dbg(hw, "Phy info is only valid if link is up\n"); + e_dbg("Phy info is only valid if link is up\n"); return -E1000_ERR_CONFIG; } - phy->polarity_correction = 1; + phy->polarity_correction = true; ret_val = e1000_check_polarity_igp(hw); if (ret_val) @@ -1936,6 +2052,61 @@ s32 e1000e_get_phy_info_igp(struct e1000_hw *hw) } /** + * e1000_get_phy_info_ife - Retrieves various IFE PHY states + * @hw: pointer to the HW structure + * + * Populates "phy" structure with various feature states. + **/ +s32 e1000_get_phy_info_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + goto out; + + if (!link) { + e_dbg("Phy info is only valid if link is up\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + ret_val = e1e_rphy(hw, IFE_PHY_SPECIAL_CONTROL, &data); + if (ret_val) + goto out; + phy->polarity_correction = (data & IFE_PSC_AUTO_POLARITY_DISABLE) + ? false : true; + + if (phy->polarity_correction) { + ret_val = e1000_check_polarity_ife(hw); + if (ret_val) + goto out; + } else { + /* Polarity is forced */ + phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal; + } + + ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data); + if (ret_val) + goto out; + + phy->is_mdix = (data & IFE_PMC_MDIX_STATUS) ? true : false; + + /* The following parameters are undefined for 10/100 operation. */ + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + +out: + return ret_val; +} + +/** * e1000e_phy_sw_reset - PHY software reset * @hw: pointer to the HW structure * @@ -1980,7 +2151,7 @@ s32 e1000e_phy_hw_reset_generic(struct e1000_hw *hw) if (ret_val) return 0; - ret_val = phy->ops.acquire_phy(hw); + ret_val = phy->ops.acquire(hw); if (ret_val) return ret_val; @@ -1995,7 +2166,7 @@ s32 e1000e_phy_hw_reset_generic(struct e1000_hw *hw) udelay(150); - phy->ops.release_phy(hw); + phy->ops.release(hw); return e1000_get_phy_cfg_done(hw); } @@ -2021,7 +2192,7 @@ s32 e1000e_get_cfg_done(struct e1000_hw *hw) **/ s32 e1000e_phy_init_script_igp3(struct e1000_hw *hw) { - hw_dbg(hw, "Running IGP 3 PHY init script\n"); + e_dbg("Running IGP 3 PHY init script\n"); /* PHY init IGP 3 */ /* Enable rise/fall, 10-mode work in class-A */ @@ -2189,28 +2360,34 @@ enum e1000_phy_type e1000e_get_phy_type_from_id(u32 phy_id) s32 e1000e_determine_phy_address(struct e1000_hw *hw) { s32 ret_val = -E1000_ERR_PHY_TYPE; - u32 phy_addr= 0; - u32 i = 0; + u32 phy_addr = 0; + u32 i; enum e1000_phy_type phy_type = e1000_phy_unknown; - do { - for (phy_addr = 0; phy_addr < 4; phy_addr++) { - hw->phy.addr = phy_addr; + hw->phy.id = phy_type; + + for (phy_addr = 0; phy_addr < E1000_MAX_PHY_ADDR; phy_addr++) { + hw->phy.addr = phy_addr; + i = 0; + + do { e1000e_get_phy_id(hw); phy_type = e1000e_get_phy_type_from_id(hw->phy.id); - /* + /* * If phy_type is valid, break - we found our * PHY address */ if (phy_type != e1000_phy_unknown) { ret_val = 0; - break; + goto out; } - } - i++; - } while ((ret_val != 0) && (i < 100)); + msleep(1); + i++; + } while (i < 10); + } +out: return ret_val; } @@ -2246,7 +2423,7 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) u32 page = offset >> IGP_PAGE_SHIFT; u32 page_shift = 0; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; @@ -2284,7 +2461,7 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) data); out: - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -2305,7 +2482,7 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) u32 page = offset >> IGP_PAGE_SHIFT; u32 page_shift = 0; - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; @@ -2342,7 +2519,7 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); out: - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -2361,7 +2538,7 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data) s32 ret_val; u16 page = (u16)(offset >> IGP_PAGE_SHIFT); - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; @@ -2387,7 +2564,7 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data) ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); out: - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -2405,7 +2582,7 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data) s32 ret_val; u16 page = (u16)(offset >> IGP_PAGE_SHIFT); - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; @@ -2431,7 +2608,7 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data) data); out: - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -2464,7 +2641,7 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, /* Gig must be disabled for MDIO accesses to page 800 */ if ((hw->mac.type == e1000_pchlan) && (!(er32(PHY_CTRL) & E1000_PHY_CTRL_GBE_DISABLE))) - hw_dbg(hw, "Attempting to access page 800 while gig enabled\n"); + e_dbg("Attempting to access page 800 while gig enabled.\n"); /* All operations in this function are phy address 1 */ hw->phy.addr = 1; @@ -2474,20 +2651,26 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT)); ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &phy_reg); - if (ret_val) + if (ret_val) { + e_dbg("Could not read PHY page 769\n"); goto out; + } /* First clear bit 4 to avoid a power state change */ phy_reg &= ~(BM_WUC_HOST_WU_BIT); ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg); - if (ret_val) + if (ret_val) { + e_dbg("Could not clear PHY page 769 bit 4\n"); goto out; + } /* Write bit 2 = 1, and clear bit 4 to 769_17 */ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg | BM_WUC_ENABLE_BIT); - if (ret_val) + if (ret_val) { + e_dbg("Could not write PHY page 769 bit 2\n"); goto out; + } /* Select page 800 */ ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, @@ -2495,21 +2678,25 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, /* Write the page 800 offset value using opcode 0x11 */ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ADDRESS_OPCODE, reg); - if (ret_val) + if (ret_val) { + e_dbg("Could not write address opcode to page 800\n"); goto out; + } if (read) { /* Read the page 800 value using opcode 0x12 */ ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE, data); } else { - /* Read the page 800 value using opcode 0x12 */ + /* Write the page 800 value using opcode 0x12 */ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE, *data); } - if (ret_val) + if (ret_val) { + e_dbg("Could not access data value from page 800\n"); goto out; + } /* * Restore 769_17.2 to its original value @@ -2520,12 +2707,53 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, /* Clear 769_17.2 */ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg); + if (ret_val) { + e_dbg("Could not clear PHY page 769 bit 2\n"); + goto out; + } out: return ret_val; } /** + * e1000_power_up_phy_copper - Restore copper link in case of PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, restore the link to previous + * settings. + **/ +void e1000_power_up_phy_copper(struct e1000_hw *hw) +{ + u16 mii_reg = 0; + + /* The PHY will retain its settings across a power down/up cycle */ + e1e_rphy(hw, PHY_CONTROL, &mii_reg); + mii_reg &= ~MII_CR_POWER_DOWN; + e1e_wphy(hw, PHY_CONTROL, mii_reg); +} + +/** + * e1000_power_down_phy_copper - Restore copper link in case of PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, restore the link to previous + * settings. + **/ +void e1000_power_down_phy_copper(struct e1000_hw *hw) +{ + u16 mii_reg = 0; + + /* The PHY will retain its settings across a power down/up cycle */ + e1e_rphy(hw, PHY_CONTROL, &mii_reg); + mii_reg |= MII_CR_POWER_DOWN; + e1e_wphy(hw, PHY_CONTROL, mii_reg); + msleep(1); +} + +/** * e1000e_commit_phy - Soft PHY reset * @hw: pointer to the HW structure * @@ -2534,8 +2762,8 @@ out: **/ s32 e1000e_commit_phy(struct e1000_hw *hw) { - if (hw->phy.ops.commit_phy) - return hw->phy.ops.commit_phy(hw); + if (hw->phy.ops.commit) + return hw->phy.ops.commit(hw); return 0; } @@ -2614,7 +2842,7 @@ static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data, bool in_slow_mode = false; if (!locked) { - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; } @@ -2670,7 +2898,7 @@ out: ret_val |= e1000_set_mdio_slow_mode_hv(hw, false); if (!locked) - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -2723,7 +2951,7 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data, bool in_slow_mode = false; if (!locked) { - ret_val = hw->phy.ops.acquire_phy(hw); + ret_val = hw->phy.ops.acquire(hw); if (ret_val) return ret_val; } @@ -2796,7 +3024,7 @@ out: ret_val |= e1000_set_mdio_slow_mode_hv(hw, false); if (!locked) - hw->phy.ops.release_phy(hw); + hw->phy.ops.release(hw); return ret_val; } @@ -2872,7 +3100,7 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, /* masking with 0x3F to remove the page from offset */ ret_val = e1000e_write_phy_reg_mdic(hw, addr_reg, (u16)offset & 0x3F); if (ret_val) { - hw_dbg(hw, "Could not write PHY the HV address register\n"); + e_dbg("Could not write PHY the HV address register\n"); goto out; } @@ -2883,7 +3111,7 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, ret_val = e1000e_write_phy_reg_mdic(hw, data_reg, *data); if (ret_val) { - hw_dbg(hw, "Could not read data value from HV data register\n"); + e_dbg("Could not read data value from HV data register\n"); goto out; } @@ -2911,12 +3139,12 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw) goto out; /* Do not apply workaround if in PHY loopback bit 14 set */ - hw->phy.ops.read_phy_reg(hw, PHY_CONTROL, &data); + hw->phy.ops.read_reg(hw, PHY_CONTROL, &data); if (data & PHY_CONTROL_LB) goto out; /* check if link is up and at 1Gbps */ - ret_val = hw->phy.ops.read_phy_reg(hw, BM_CS_STATUS, &data); + ret_val = hw->phy.ops.read_reg(hw, BM_CS_STATUS, &data); if (ret_val) goto out; @@ -2932,13 +3160,13 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw) mdelay(200); /* flush the packets in the fifo buffer */ - ret_val = hw->phy.ops.write_phy_reg(hw, HV_MUX_DATA_CTRL, + ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL, HV_MUX_DATA_CTRL_GEN_TO_MAC | HV_MUX_DATA_CTRL_FORCE_SPEED); if (ret_val) goto out; - ret_val = hw->phy.ops.write_phy_reg(hw, HV_MUX_DATA_CTRL, + ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL, HV_MUX_DATA_CTRL_GEN_TO_MAC); out: @@ -2959,7 +3187,7 @@ s32 e1000_check_polarity_82577(struct e1000_hw *hw) s32 ret_val; u16 data; - ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_STATUS_2, &data); + ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data); if (!ret_val) phy->cable_polarity = (data & I82577_PHY_STATUS2_REV_POLARITY) @@ -2984,13 +3212,13 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw) u16 phy_data; bool link; - ret_val = phy->ops.read_phy_reg(hw, PHY_CONTROL, &phy_data); + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); if (ret_val) goto out; e1000e_phy_force_speed_duplex_setup(hw, &phy_data); - ret_val = phy->ops.write_phy_reg(hw, PHY_CONTROL, phy_data); + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); if (ret_val) goto out; @@ -2998,23 +3226,23 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw) * Clear Auto-Crossover to force MDI manually. 82577 requires MDI * forced whenever speed and duplex are forced. */ - ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_CTRL_2, &phy_data); + ret_val = phy->ops.read_reg(hw, I82577_PHY_CTRL_2, &phy_data); if (ret_val) goto out; phy_data &= ~I82577_PHY_CTRL2_AUTO_MDIX; phy_data &= ~I82577_PHY_CTRL2_FORCE_MDI_MDIX; - ret_val = phy->ops.write_phy_reg(hw, I82577_PHY_CTRL_2, phy_data); + ret_val = phy->ops.write_reg(hw, I82577_PHY_CTRL_2, phy_data); if (ret_val) goto out; - hw_dbg(hw, "I82577_PHY_CTRL_2: %X\n", phy_data); + e_dbg("I82577_PHY_CTRL_2: %X\n", phy_data); udelay(1); if (phy->autoneg_wait_to_complete) { - hw_dbg(hw, "Waiting for forced speed/duplex link on 82577 phy\n"); + e_dbg("Waiting for forced speed/duplex link on 82577 phy\n"); ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT, @@ -3024,7 +3252,7 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw) goto out; if (!link) - hw_dbg(hw, "Link taking longer than expected.\n"); + e_dbg("Link taking longer than expected.\n"); /* Try once more */ ret_val = e1000e_phy_has_link_generic(hw, @@ -3060,7 +3288,7 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw) goto out; if (!link) { - hw_dbg(hw, "Phy info is only valid if link is up\n"); + e_dbg("Phy info is only valid if link is up\n"); ret_val = -E1000_ERR_CONFIG; goto out; } @@ -3071,7 +3299,7 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw) if (ret_val) goto out; - ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_STATUS_2, &data); + ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data); if (ret_val) goto out; @@ -3083,7 +3311,7 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw) if (ret_val) goto out; - ret_val = phy->ops.read_phy_reg(hw, PHY_1000T_STATUS, &data); + ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data); if (ret_val) goto out; @@ -3117,7 +3345,7 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw) s32 ret_val; u16 phy_data, length; - ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data); + ret_val = phy->ops.read_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data); if (ret_val) goto out; @@ -3125,7 +3353,7 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw) I82577_DSTATUS_CABLE_LENGTH_SHIFT; if (length == E1000_CABLE_LENGTH_UNDEFINED) - ret_val = E1000_ERR_PHY; + ret_val = -E1000_ERR_PHY; phy->cable_length = length; diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c index d2f6ee1a6290..ca93c9a9d372 100644 --- a/drivers/net/e2100.c +++ b/drivers/net/e2100.c @@ -186,9 +186,9 @@ static int __init e21_probe1(struct net_device *dev, int ioaddr) return -EBUSY; /* First check the station address for the Ctron prefix. */ - if (inb(ioaddr + E21_SAPROM + 0) != 0x00 - || inb(ioaddr + E21_SAPROM + 1) != 0x00 - || inb(ioaddr + E21_SAPROM + 2) != 0x1d) { + if (inb(ioaddr + E21_SAPROM + 0) != 0x00 || + inb(ioaddr + E21_SAPROM + 1) != 0x00 || + inb(ioaddr + E21_SAPROM + 2) != 0x1d) { retval = -ENODEV; goto out; } diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index 1e934160062c..94c59498cdb6 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -990,7 +990,7 @@ static int eepro_open(struct net_device *dev) return -EAGAIN; } - if (request_irq(dev->irq , &eepro_interrupt, 0, dev->name, dev)) { + if (request_irq(dev->irq , eepro_interrupt, 0, dev->name, dev)) { printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq); return -EAGAIN; } diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index 592de8f1668a..6fbfc8eee632 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -457,7 +457,7 @@ static int eexp_open(struct net_device *dev) if (!dev->irq || !irqrmap[dev->irq]) return -ENXIO; - ret = request_irq(dev->irq, &eexp_irq, 0, dev->name, dev); + ret = request_irq(dev->irq, eexp_irq, 0, dev->name, dev); if (ret) return ret; diff --git a/drivers/net/ehea/ehea_ethtool.c b/drivers/net/ehea/ehea_ethtool.c index d76885223366..75b099ce49c9 100644 --- a/drivers/net/ehea/ehea_ethtool.c +++ b/drivers/net/ehea/ehea_ethtool.c @@ -118,7 +118,7 @@ doit: ret = ehea_set_portspeed(port, sp); if (!ret) - ehea_info("%s: Port speed succesfully set: %dMbps " + ehea_info("%s: Port speed successfully set: %dMbps " "%s Duplex", port->netdev->name, port->port_speed, port->full_duplex == 1 ? "Full" : "Half"); @@ -134,7 +134,7 @@ static int ehea_nway_reset(struct net_device *dev) ret = ehea_set_portspeed(port, EHEA_SPEED_AUTONEG); if (!ret) - ehea_info("%s: Port speed succesfully set: %dMbps " + ehea_info("%s: Port speed successfully set: %dMbps " "%s Duplex", port->netdev->name, port->port_speed, port->full_duplex == 1 ? "Full" : "Half"); diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index 41bd7aeafd82..7b62336e6736 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -189,8 +189,8 @@ static void ehea_update_firmware_handles(void) for (k = 0; k < EHEA_MAX_PORTS; k++) { struct ehea_port *port = adapter->port[k]; - if (!port || (port->state != EHEA_PORT_UP) - || (num_ports == 0)) + if (!port || (port->state != EHEA_PORT_UP) || + (num_ports == 0)) continue; for (l = 0; @@ -447,7 +447,9 @@ static int ehea_refill_rq_def(struct ehea_port_res *pr, max_index_mask = q_skba->len - 1; for (i = 0; i < fill_wqes; i++) { u64 tmp_addr; - struct sk_buff *skb = netdev_alloc_skb(dev, packet_size); + struct sk_buff *skb; + + skb = netdev_alloc_skb_ip_align(dev, packet_size); if (!skb) { q_skba->os_skbs = fill_wqes - i; if (q_skba->os_skbs == q_skba->len - 2) { @@ -457,7 +459,6 @@ static int ehea_refill_rq_def(struct ehea_port_res *pr, } break; } - skb_reserve(skb, NET_IP_ALIGN); skb_arr[index] = skb; tmp_addr = ehea_map_vaddr(skb->data); @@ -500,7 +501,7 @@ static int ehea_refill_rq2(struct ehea_port_res *pr, int nr_of_wqes) { return ehea_refill_rq_def(pr, &pr->rq2_skba, 2, nr_of_wqes, EHEA_RWQE2_TYPE, - EHEA_RQ2_PKT_SIZE + NET_IP_ALIGN); + EHEA_RQ2_PKT_SIZE); } @@ -508,7 +509,7 @@ static int ehea_refill_rq3(struct ehea_port_res *pr, int nr_of_wqes) { return ehea_refill_rq_def(pr, &pr->rq3_skba, 3, nr_of_wqes, EHEA_RWQE3_TYPE, - EHEA_MAX_PACKET_SIZE + NET_IP_ALIGN); + EHEA_MAX_PACKET_SIZE); } static inline int ehea_check_cqe(struct ehea_cqe *cqe, int *rq_num) @@ -656,8 +657,8 @@ static int get_skb_hdr(struct sk_buff *skb, void **iphdr, static void ehea_proc_skb(struct ehea_port_res *pr, struct ehea_cqe *cqe, struct sk_buff *skb) { - int vlan_extracted = (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT) - && pr->port->vgrp; + int vlan_extracted = ((cqe->status & EHEA_CQE_VLAN_TAG_XTRACT) && + pr->port->vgrp); if (use_lro) { if (vlan_extracted) @@ -1388,8 +1389,8 @@ out: int ehea_rem_smrs(struct ehea_port_res *pr) { - if ((ehea_rem_mr(&pr->send_mr)) - || (ehea_rem_mr(&pr->recv_mr))) + if ((ehea_rem_mr(&pr->send_mr)) || + (ehea_rem_mr(&pr->recv_mr))) return -EIO; else return 0; @@ -2030,8 +2031,8 @@ static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev, write_ip_start_end(swqe, skb); if (iph->protocol == IPPROTO_UDP) { - if ((iph->frag_off & IP_MF) - || (iph->frag_off & IP_OFFSET)) + if ((iph->frag_off & IP_MF) || + (iph->frag_off & IP_OFFSET)) /* IP fragment, so don't change cs */ swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM; else @@ -2076,8 +2077,8 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev, write_tcp_offset_end(swqe, skb); } else if (iph->protocol == IPPROTO_UDP) { - if ((iph->frag_off & IP_MF) - || (iph->frag_off & IP_OFFSET)) + if ((iph->frag_off & IP_MF) || + (iph->frag_off & IP_OFFSET)) /* IP fragment, so don't change cs */ swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IMM_DATA_PRESENT; diff --git a/drivers/net/ehea/ehea_qmr.c b/drivers/net/ehea/ehea_qmr.c index bc7c5b7abb88..18d405f78c0f 100644 --- a/drivers/net/ehea/ehea_qmr.c +++ b/drivers/net/ehea/ehea_qmr.c @@ -837,8 +837,8 @@ static u64 ehea_reg_mr_section(int top, int dir, int idx, u64 *pt, hret = ehea_h_register_rpage_mr(adapter->handle, mr->handle, 0, 0, pt_abs, EHEA_MAX_RPAGE); - if ((hret != H_SUCCESS) - && (hret != H_PAGE_REGISTERED)) { + if ((hret != H_SUCCESS) && + (hret != H_PAGE_REGISTERED)) { ehea_h_free_resource(adapter->handle, mr->handle, FORCE_FREE); ehea_error("register_rpage_mr failed"); diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index d69d52ed7726..f875751af15e 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c @@ -870,19 +870,6 @@ static void enic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) dev_kfree_skb_any(buf->os_buf); } -static inline struct sk_buff *enic_rq_alloc_skb(struct net_device *netdev, - unsigned int size) -{ - struct sk_buff *skb; - - skb = netdev_alloc_skb(netdev, size + NET_IP_ALIGN); - - if (skb) - skb_reserve(skb, NET_IP_ALIGN); - - return skb; -} - static int enic_rq_alloc_buf(struct vnic_rq *rq) { struct enic *enic = vnic_dev_priv(rq->vdev); @@ -892,7 +879,7 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq) unsigned int os_buf_index = 0; dma_addr_t dma_addr; - skb = enic_rq_alloc_skb(netdev, len); + skb = netdev_alloc_skb_ip_align(netdev, len); if (!skb) return -ENOMEM; diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 641a10d2e843..41494f7b2ec8 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -630,8 +630,8 @@ static int mdio_read(struct net_device *dev, int phy_id, int location) barrier(); if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0) { /* Work around read failure bug. */ - if (phy_id == 1 && location < 6 - && inw(ioaddr + MIIData) == 0xffff) { + if (phy_id == 1 && location < 6 && + inw(ioaddr + MIIData) == 0xffff) { outl(read_cmd, ioaddr + MIICtrl); continue; } @@ -668,7 +668,7 @@ static int epic_open(struct net_device *dev) outl(0x4001, ioaddr + GENCTL); napi_enable(&ep->napi); - if ((retval = request_irq(dev->irq, &epic_interrupt, IRQF_SHARED, dev->name, dev))) { + if ((retval = request_irq(dev->irq, epic_interrupt, IRQF_SHARED, dev->name, dev))) { napi_disable(&ep->napi); return retval; } @@ -1205,8 +1205,8 @@ static int epic_rx(struct net_device *dev, int budget) } /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + if (pkt_len < rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(ep->pci_dev, ep->rx_ring[entry].bufaddr, diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index f7d9ac8324cb..bd1db92aec1b 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -406,10 +406,10 @@ static int ethoc_rx(struct net_device *dev, int limit) if (ethoc_update_rx_stats(priv, &bd) == 0) { int size = bd.stat >> 16; - struct sk_buff *skb = netdev_alloc_skb(dev, size); + struct sk_buff *skb; size -= 4; /* strip the CRC */ - skb_reserve(skb, 2); /* align TCP/IP header */ + skb = netdev_alloc_skb_ip_align(dev, size); if (likely(skb)) { void *src = phys_to_virt(bd.addr); @@ -641,7 +641,7 @@ static int ethoc_mdio_probe(struct net_device *dev) return -ENXIO; } - phy = phy_connect(dev, dev_name(&phy->dev), ðoc_mdio_poll, 0, + phy = phy_connect(dev, dev_name(&phy->dev), ethoc_mdio_poll, 0, PHY_INTERFACE_MODE_GMII); if (IS_ERR(phy)) { dev_err(&dev->dev, "could not attach to PHY\n"); diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index 18d5fbb9673e..dac4e595589e 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -839,7 +839,7 @@ static int netdev_open(struct net_device *dev) iowrite32(0x00000001, ioaddr + BCR); /* Reset */ - if (request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev)) + if (request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev)) return -EAGAIN; for (i = 0; i < 3; i++) @@ -1629,8 +1629,8 @@ static int netdev_rx(struct net_device *dev) if (debug) printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", rx_status); - if ((!((rx_status & RXFSD) && (rx_status & RXLSD))) - || (rx_status & ErrorSummary)) { + if ((!((rx_status & RXFSD) && (rx_status & RXLSD))) || + (rx_status & ErrorSummary)) { if (rx_status & ErrorSummary) { /* there was a fatal error */ if (debug) printk(KERN_DEBUG @@ -1655,8 +1655,8 @@ static int netdev_rx(struct net_device *dev) cur = np->cur_rx; while (desno <= np->really_rx_count) { ++desno; - if ((!(cur->status & RXOWN)) - && (cur->status & RXLSD)) + if ((!(cur->status & RXOWN)) && + (cur->status & RXLSD)) break; /* goto next rx descriptor */ cur = cur->next_desc_logical; @@ -1786,8 +1786,8 @@ static void __set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = CR_W_AB | CR_W_AM; diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index 66dace6d324f..6407672b28e9 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -226,17 +226,17 @@ static int mpc52xx_fec_open(struct net_device *dev) phy_start(priv->phydev); } - if (request_irq(dev->irq, &mpc52xx_fec_interrupt, IRQF_SHARED, + if (request_irq(dev->irq, mpc52xx_fec_interrupt, IRQF_SHARED, DRIVER_NAME "_ctrl", dev)) { dev_err(&dev->dev, "ctrl interrupt request failed\n"); goto free_phy; } - if (request_irq(priv->r_irq, &mpc52xx_fec_rx_interrupt, 0, + if (request_irq(priv->r_irq, mpc52xx_fec_rx_interrupt, 0, DRIVER_NAME "_rx", dev)) { dev_err(&dev->dev, "rx interrupt request failed\n"); goto free_ctrl_irq; } - if (request_irq(priv->t_irq, &mpc52xx_fec_tx_interrupt, 0, + if (request_irq(priv->t_irq, mpc52xx_fec_tx_interrupt, 0, DRIVER_NAME "_tx", dev)) { dev_err(&dev->dev, "tx interrupt request failed\n"); goto free_2irqs; diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 3116601dbfea..3c340489804a 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -4004,7 +4004,7 @@ static int nv_request_irq(struct net_device *dev, int intr_test) /* Request irq for rx handling */ sprintf(np->name_rx, "%s-rx", dev->name); if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector, - &nv_nic_irq_rx, IRQF_SHARED, np->name_rx, dev) != 0) { + nv_nic_irq_rx, IRQF_SHARED, np->name_rx, dev) != 0) { printk(KERN_INFO "forcedeth: request_irq failed for rx %d\n", ret); pci_disable_msix(np->pci_dev); np->msi_flags &= ~NV_MSI_X_ENABLED; @@ -4013,7 +4013,7 @@ static int nv_request_irq(struct net_device *dev, int intr_test) /* Request irq for tx handling */ sprintf(np->name_tx, "%s-tx", dev->name); if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector, - &nv_nic_irq_tx, IRQF_SHARED, np->name_tx, dev) != 0) { + nv_nic_irq_tx, IRQF_SHARED, np->name_tx, dev) != 0) { printk(KERN_INFO "forcedeth: request_irq failed for tx %d\n", ret); pci_disable_msix(np->pci_dev); np->msi_flags &= ~NV_MSI_X_ENABLED; @@ -4022,7 +4022,7 @@ static int nv_request_irq(struct net_device *dev, int intr_test) /* Request irq for link and timer handling */ sprintf(np->name_other, "%s-other", dev->name); if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector, - &nv_nic_irq_other, IRQF_SHARED, np->name_other, dev) != 0) { + nv_nic_irq_other, IRQF_SHARED, np->name_other, dev) != 0) { printk(KERN_INFO "forcedeth: request_irq failed for link %d\n", ret); pci_disable_msix(np->pci_dev); np->msi_flags &= ~NV_MSI_X_ENABLED; diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c index efbf67689eca..25fabb3eedc5 100644 --- a/drivers/net/fsl_pq_mdio.c +++ b/drivers/net/fsl_pq_mdio.c @@ -3,8 +3,9 @@ * Provides Bus interface for MIIM regs * * Author: Andy Fleming <afleming@freescale.com> + * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com> * - * Copyright (c) 2002-2004,2008 Freescale Semiconductor, Inc. + * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc. * * Based on gianfar_mii.c and ucc_geth_mii.c (Li Yang, Kim Phillips) * @@ -102,13 +103,18 @@ int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs, return value; } +static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus) +{ + return (void __iomem __force *)bus->priv; +} + /* * Write value to the PHY at mii_id at register regnum, * on the bus, waiting until the write is done before returning. */ int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) { - struct fsl_pq_mdio __iomem *regs = (void __iomem *)bus->priv; + struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus); /* Write to the local MII regs */ return(fsl_pq_local_mdio_write(regs, mii_id, regnum, value)); @@ -120,7 +126,7 @@ int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) */ int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { - struct fsl_pq_mdio __iomem *regs = (void __iomem *)bus->priv; + struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus); /* Read the local MII regs */ return(fsl_pq_local_mdio_read(regs, mii_id, regnum)); @@ -129,7 +135,7 @@ int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum) /* Reset the MIIM registers, and wait for the bus to free */ static int fsl_pq_mdio_reset(struct mii_bus *bus) { - struct fsl_pq_mdio __iomem *regs = (void __iomem *)bus->priv; + struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus); int timeout = PHY_INIT_TIMEOUT; mutex_lock(&bus->mdio_lock); @@ -189,19 +195,29 @@ static int fsl_pq_mdio_find_free(struct mii_bus *new_bus) #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) -static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs) +static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs, struct device_node *np) { struct gfar __iomem *enet_regs; + u32 __iomem *ioremap_tbipa; + u64 addr, size; /* * This is mildly evil, but so is our hardware for doing this. * Also, we have to cast back to struct gfar because of * definition weirdness done in gianfar.h. */ - enet_regs = (struct gfar __iomem *) - ((char __iomem *)regs - offsetof(struct gfar, gfar_mii_regs)); - - return &enet_regs->tbipa; + if(of_device_is_compatible(np, "fsl,gianfar-mdio") || + of_device_is_compatible(np, "fsl,gianfar-tbi") || + of_device_is_compatible(np, "gianfar")) { + enet_regs = (struct gfar __iomem *)regs; + return &enet_regs->tbipa; + } else if (of_device_is_compatible(np, "fsl,etsec2-mdio") || + of_device_is_compatible(np, "fsl,etsec2-tbi")) { + addr = of_translate_address(np, of_get_address(np, 1, &size, NULL)); + ioremap_tbipa = ioremap(addr, size); + return ioremap_tbipa; + } else + return NULL; } #endif @@ -250,11 +266,12 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev, { struct device_node *np = ofdev->node; struct device_node *tbi; - struct fsl_pq_mdio __iomem *regs; + struct fsl_pq_mdio __iomem *regs = NULL; + void __iomem *map; u32 __iomem *tbipa; struct mii_bus *new_bus; int tbiaddr = -1; - u64 addr, size; + u64 addr = 0, size = 0; int err = 0; new_bus = mdiobus_alloc(); @@ -269,13 +286,19 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev, /* Set the PHY base address */ addr = of_translate_address(np, of_get_address(np, 0, &size, NULL)); - regs = ioremap(addr, size); - - if (NULL == regs) { + map = ioremap(addr, size); + if (!map) { err = -ENOMEM; goto err_free_bus; } + if (of_device_is_compatible(np, "fsl,gianfar-mdio") || + of_device_is_compatible(np, "fsl,gianfar-tbi") || + of_device_is_compatible(np, "fsl,ucc-mdio") || + of_device_is_compatible(np, "ucc_geth_phy")) + map -= offsetof(struct fsl_pq_mdio, miimcfg); + regs = map; + new_bus->priv = (void __force *)regs; new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); @@ -290,9 +313,15 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev, if (of_device_is_compatible(np, "fsl,gianfar-mdio") || of_device_is_compatible(np, "fsl,gianfar-tbi") || + of_device_is_compatible(np, "fsl,etsec2-mdio") || + of_device_is_compatible(np, "fsl,etsec2-tbi") || of_device_is_compatible(np, "gianfar")) { #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) - tbipa = get_gfar_tbipa(regs); + tbipa = get_gfar_tbipa(regs, np); + if (!tbipa) { + err = -EINVAL; + goto err_free_irqs; + } #else err = -ENODEV; goto err_free_irqs; @@ -380,7 +409,7 @@ static int fsl_pq_mdio_remove(struct of_device *ofdev) dev_set_drvdata(device, NULL); - iounmap((void __iomem *)bus->priv); + iounmap(fsl_pq_mdio_get_regs(bus)); bus->priv = NULL; mdiobus_free(bus); @@ -405,6 +434,12 @@ static struct of_device_id fsl_pq_mdio_match[] = { { .compatible = "fsl,gianfar-mdio", }, + { + .compatible = "fsl,etsec2-tbi", + }, + { + .compatible = "fsl,etsec2-mdio", + }, {}, }; MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match); diff --git a/drivers/net/fsl_pq_mdio.h b/drivers/net/fsl_pq_mdio.h index 36dad527410b..1f7d865cedb6 100644 --- a/drivers/net/fsl_pq_mdio.h +++ b/drivers/net/fsl_pq_mdio.h @@ -3,8 +3,9 @@ * Driver for the MDIO bus controller on Freescale PowerQUICC processors * * Author: Andy Fleming + * Modifier: Sandeep Gopalpet * - * Copyright (c) 2002-2004,2008 Freescale Semiconductor, Inc. + * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -23,6 +24,12 @@ #define MII_READ_COMMAND 0x00000001 struct fsl_pq_mdio { + u8 res1[16]; + u32 ieventm; /* MDIO Interrupt event register (for etsec2)*/ + u32 imaskm; /* MDIO Interrupt mask register (for etsec2)*/ + u8 res2[4]; + u32 emapm; /* MDIO Event mapping register (for etsec2)*/ + u8 res3[1280]; u32 miimcfg; /* MII management configuration reg */ u32 miimcom; /* MII management command reg */ u32 miimadd; /* MII management address reg */ @@ -31,9 +38,9 @@ struct fsl_pq_mdio { u32 miimind; /* MII management indication reg */ u8 reserved[28]; /* Space holder */ u32 utbipar; /* TBI phy address reg (only on UCC) */ + u8 res4[2728]; } __attribute__ ((packed)); - int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum); int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value); int fsl_pq_local_mdio_write(struct fsl_pq_mdio __iomem *regs, int mii_id, diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 5bf31f1509c9..16def131c390 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -8,9 +8,10 @@ * * Author: Andy Fleming * Maintainer: Kumar Gala + * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com> * - * Copyright (c) 2002-2006 Freescale Semiconductor, Inc. - * Copyright (c) 2007 MontaVista Software, Inc. + * Copyright 2002-2009 Freescale Semiconductor, Inc. + * Copyright 2007 MontaVista Software, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -109,7 +110,7 @@ static void gfar_reset_task(struct work_struct *work); static void gfar_timeout(struct net_device *dev); static int gfar_close(struct net_device *dev); struct sk_buff *gfar_new_skb(struct net_device *dev); -static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp, +static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp, struct sk_buff *skb); static int gfar_set_mac_address(struct net_device *dev); static int gfar_change_mtu(struct net_device *dev, int new_mtu); @@ -130,8 +131,8 @@ static int gfar_poll(struct napi_struct *napi, int budget); #ifdef CONFIG_NET_POLL_CONTROLLER static void gfar_netpoll(struct net_device *dev); #endif -int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit); -static int gfar_clean_tx_ring(struct net_device *dev); +int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit); +static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue); static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int amount_pull); static void gfar_vlan_rx_register(struct net_device *netdev, @@ -142,11 +143,277 @@ void gfar_start(struct net_device *dev); static void gfar_clear_exact_match(struct net_device *dev); static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr); static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +u16 gfar_select_queue(struct net_device *dev, struct sk_buff *skb); MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_DESCRIPTION("Gianfar Ethernet Driver"); MODULE_LICENSE("GPL"); +static void gfar_init_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp, + dma_addr_t buf) +{ + u32 lstatus; + + bdp->bufPtr = buf; + + lstatus = BD_LFLAG(RXBD_EMPTY | RXBD_INTERRUPT); + if (bdp == rx_queue->rx_bd_base + rx_queue->rx_ring_size - 1) + lstatus |= BD_LFLAG(RXBD_WRAP); + + eieio(); + + bdp->lstatus = lstatus; +} + +static int gfar_init_bds(struct net_device *ndev) +{ + struct gfar_private *priv = netdev_priv(ndev); + struct gfar_priv_tx_q *tx_queue = NULL; + struct gfar_priv_rx_q *rx_queue = NULL; + struct txbd8 *txbdp; + struct rxbd8 *rxbdp; + int i, j; + + for (i = 0; i < priv->num_tx_queues; i++) { + tx_queue = priv->tx_queue[i]; + /* Initialize some variables in our dev structure */ + tx_queue->num_txbdfree = tx_queue->tx_ring_size; + tx_queue->dirty_tx = tx_queue->tx_bd_base; + tx_queue->cur_tx = tx_queue->tx_bd_base; + tx_queue->skb_curtx = 0; + tx_queue->skb_dirtytx = 0; + + /* Initialize Transmit Descriptor Ring */ + txbdp = tx_queue->tx_bd_base; + for (j = 0; j < tx_queue->tx_ring_size; j++) { + txbdp->lstatus = 0; + txbdp->bufPtr = 0; + txbdp++; + } + + /* Set the last descriptor in the ring to indicate wrap */ + txbdp--; + txbdp->status |= TXBD_WRAP; + } + + for (i = 0; i < priv->num_rx_queues; i++) { + rx_queue = priv->rx_queue[i]; + rx_queue->cur_rx = rx_queue->rx_bd_base; + rx_queue->skb_currx = 0; + rxbdp = rx_queue->rx_bd_base; + + for (j = 0; j < rx_queue->rx_ring_size; j++) { + struct sk_buff *skb = rx_queue->rx_skbuff[j]; + + if (skb) { + gfar_init_rxbdp(rx_queue, rxbdp, + rxbdp->bufPtr); + } else { + skb = gfar_new_skb(ndev); + if (!skb) { + pr_err("%s: Can't allocate RX buffers\n", + ndev->name); + goto err_rxalloc_fail; + } + rx_queue->rx_skbuff[j] = skb; + + gfar_new_rxbdp(rx_queue, rxbdp, skb); + } + + rxbdp++; + } + + } + + return 0; + +err_rxalloc_fail: + free_skb_resources(priv); + return -ENOMEM; +} + +static int gfar_alloc_skb_resources(struct net_device *ndev) +{ + void *vaddr; + dma_addr_t addr; + int i, j, k; + struct gfar_private *priv = netdev_priv(ndev); + struct device *dev = &priv->ofdev->dev; + struct gfar_priv_tx_q *tx_queue = NULL; + struct gfar_priv_rx_q *rx_queue = NULL; + + priv->total_tx_ring_size = 0; + for (i = 0; i < priv->num_tx_queues; i++) + priv->total_tx_ring_size += priv->tx_queue[i]->tx_ring_size; + + priv->total_rx_ring_size = 0; + for (i = 0; i < priv->num_rx_queues; i++) + priv->total_rx_ring_size += priv->rx_queue[i]->rx_ring_size; + + /* Allocate memory for the buffer descriptors */ + vaddr = dma_alloc_coherent(dev, + sizeof(struct txbd8) * priv->total_tx_ring_size + + sizeof(struct rxbd8) * priv->total_rx_ring_size, + &addr, GFP_KERNEL); + if (!vaddr) { + if (netif_msg_ifup(priv)) + pr_err("%s: Could not allocate buffer descriptors!\n", + ndev->name); + return -ENOMEM; + } + + for (i = 0; i < priv->num_tx_queues; i++) { + tx_queue = priv->tx_queue[i]; + tx_queue->tx_bd_base = (struct txbd8 *) vaddr; + tx_queue->tx_bd_dma_base = addr; + tx_queue->dev = ndev; + /* enet DMA only understands physical addresses */ + addr += sizeof(struct txbd8) *tx_queue->tx_ring_size; + vaddr += sizeof(struct txbd8) *tx_queue->tx_ring_size; + } + + /* Start the rx descriptor ring where the tx ring leaves off */ + for (i = 0; i < priv->num_rx_queues; i++) { + rx_queue = priv->rx_queue[i]; + rx_queue->rx_bd_base = (struct rxbd8 *) vaddr; + rx_queue->rx_bd_dma_base = addr; + rx_queue->dev = ndev; + addr += sizeof (struct rxbd8) * rx_queue->rx_ring_size; + vaddr += sizeof (struct rxbd8) * rx_queue->rx_ring_size; + } + + /* Setup the skbuff rings */ + for (i = 0; i < priv->num_tx_queues; i++) { + tx_queue = priv->tx_queue[i]; + tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) * + tx_queue->tx_ring_size, GFP_KERNEL); + if (!tx_queue->tx_skbuff) { + if (netif_msg_ifup(priv)) + pr_err("%s: Could not allocate tx_skbuff\n", + ndev->name); + goto cleanup; + } + + for (k = 0; k < tx_queue->tx_ring_size; k++) + tx_queue->tx_skbuff[k] = NULL; + } + + for (i = 0; i < priv->num_rx_queues; i++) { + rx_queue = priv->rx_queue[i]; + rx_queue->rx_skbuff = kmalloc(sizeof(*rx_queue->rx_skbuff) * + rx_queue->rx_ring_size, GFP_KERNEL); + + if (!rx_queue->rx_skbuff) { + if (netif_msg_ifup(priv)) + pr_err("%s: Could not allocate rx_skbuff\n", + ndev->name); + goto cleanup; + } + + for (j = 0; j < rx_queue->rx_ring_size; j++) + rx_queue->rx_skbuff[j] = NULL; + } + + if (gfar_init_bds(ndev)) + goto cleanup; + + return 0; + +cleanup: + free_skb_resources(priv); + return -ENOMEM; +} + +static void gfar_init_tx_rx_base(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 __iomem *baddr; + int i; + + baddr = ®s->tbase0; + for(i = 0; i < priv->num_tx_queues; i++) { + gfar_write(baddr, priv->tx_queue[i]->tx_bd_dma_base); + baddr += 2; + } + + baddr = ®s->rbase0; + for(i = 0; i < priv->num_rx_queues; i++) { + gfar_write(baddr, priv->rx_queue[i]->rx_bd_dma_base); + baddr += 2; + } +} + +static void gfar_init_mac(struct net_device *ndev) +{ + struct gfar_private *priv = netdev_priv(ndev); + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 rctrl = 0; + u32 tctrl = 0; + u32 attrs = 0; + + /* write the tx/rx base registers */ + gfar_init_tx_rx_base(priv); + + /* Configure the coalescing support */ + gfar_configure_coalescing(priv, 0xFF, 0xFF); + + if (priv->rx_filer_enable) + rctrl |= RCTRL_FILREN; + + if (priv->rx_csum_enable) + rctrl |= RCTRL_CHECKSUMMING; + + if (priv->extended_hash) { + rctrl |= RCTRL_EXTHASH; + + gfar_clear_exact_match(ndev); + rctrl |= RCTRL_EMEN; + } + + if (priv->padding) { + rctrl &= ~RCTRL_PAL_MASK; + rctrl |= RCTRL_PADDING(priv->padding); + } + + /* keep vlan related bits if it's enabled */ + if (priv->vlgrp) { + rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT; + tctrl |= TCTRL_VLINS; + } + + /* Init rctrl based on our settings */ + gfar_write(®s->rctrl, rctrl); + + if (ndev->features & NETIF_F_IP_CSUM) + tctrl |= TCTRL_INIT_CSUM; + + tctrl |= TCTRL_TXSCHED_PRIO; + + gfar_write(®s->tctrl, tctrl); + + /* Set the extraction length and index */ + attrs = ATTRELI_EL(priv->rx_stash_size) | + ATTRELI_EI(priv->rx_stash_index); + + gfar_write(®s->attreli, attrs); + + /* Start with defaults, and add stashing or locking + * depending on the approprate variables */ + attrs = ATTR_INIT_SETTINGS; + + if (priv->bd_stash_en) + attrs |= ATTR_BDSTASH; + + if (priv->rx_stash_size != 0) + attrs |= ATTR_BUFSTASH; + + gfar_write(®s->attr, attrs); + + gfar_write(®s->fifo_tx_thr, priv->fifo_threshold); + gfar_write(®s->fifo_tx_starve, priv->fifo_starve); + gfar_write(®s->fifo_tx_starve_shutoff, priv->fifo_starve_off); +} + static const struct net_device_ops gfar_netdev_ops = { .ndo_open = gfar_enet_open, .ndo_start_xmit = gfar_start_xmit, @@ -155,6 +422,7 @@ static const struct net_device_ops gfar_netdev_ops = { .ndo_set_multicast_list = gfar_set_multi, .ndo_tx_timeout = gfar_timeout, .ndo_do_ioctl = gfar_ioctl, + .ndo_select_queue = gfar_select_queue, .ndo_vlan_rx_register = gfar_vlan_rx_register, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, @@ -163,56 +431,252 @@ static const struct net_device_ops gfar_netdev_ops = { #endif }; +unsigned int ftp_rqfpr[MAX_FILER_IDX + 1]; +unsigned int ftp_rqfcr[MAX_FILER_IDX + 1]; + +void lock_rx_qs(struct gfar_private *priv) +{ + int i = 0x0; + + for (i = 0; i < priv->num_rx_queues; i++) + spin_lock(&priv->rx_queue[i]->rxlock); +} + +void lock_tx_qs(struct gfar_private *priv) +{ + int i = 0x0; + + for (i = 0; i < priv->num_tx_queues; i++) + spin_lock(&priv->tx_queue[i]->txlock); +} + +void unlock_rx_qs(struct gfar_private *priv) +{ + int i = 0x0; + + for (i = 0; i < priv->num_rx_queues; i++) + spin_unlock(&priv->rx_queue[i]->rxlock); +} + +void unlock_tx_qs(struct gfar_private *priv) +{ + int i = 0x0; + + for (i = 0; i < priv->num_tx_queues; i++) + spin_unlock(&priv->tx_queue[i]->txlock); +} + /* Returns 1 if incoming frames use an FCB */ static inline int gfar_uses_fcb(struct gfar_private *priv) { return priv->vlgrp || priv->rx_csum_enable; } -static int gfar_of_init(struct net_device *dev) +u16 gfar_select_queue(struct net_device *dev, struct sk_buff *skb) +{ + return skb_get_queue_mapping(skb); +} +static void free_tx_pointers(struct gfar_private *priv) +{ + int i = 0; + + for (i = 0; i < priv->num_tx_queues; i++) + kfree(priv->tx_queue[i]); +} + +static void free_rx_pointers(struct gfar_private *priv) +{ + int i = 0; + + for (i = 0; i < priv->num_rx_queues; i++) + kfree(priv->rx_queue[i]); +} + +static void unmap_group_regs(struct gfar_private *priv) +{ + int i = 0; + + for (i = 0; i < MAXGROUPS; i++) + if (priv->gfargrp[i].regs) + iounmap(priv->gfargrp[i].regs); +} + +static void disable_napi(struct gfar_private *priv) +{ + int i = 0; + + for (i = 0; i < priv->num_grps; i++) + napi_disable(&priv->gfargrp[i].napi); +} + +static void enable_napi(struct gfar_private *priv) +{ + int i = 0; + + for (i = 0; i < priv->num_grps; i++) + napi_enable(&priv->gfargrp[i].napi); +} + +static int gfar_parse_group(struct device_node *np, + struct gfar_private *priv, const char *model) +{ + u32 *queue_mask; + u64 addr, size; + + addr = of_translate_address(np, + of_get_address(np, 0, &size, NULL)); + priv->gfargrp[priv->num_grps].regs = ioremap(addr, size); + + if (!priv->gfargrp[priv->num_grps].regs) + return -ENOMEM; + + priv->gfargrp[priv->num_grps].interruptTransmit = + irq_of_parse_and_map(np, 0); + + /* If we aren't the FEC we have multiple interrupts */ + if (model && strcasecmp(model, "FEC")) { + priv->gfargrp[priv->num_grps].interruptReceive = + irq_of_parse_and_map(np, 1); + priv->gfargrp[priv->num_grps].interruptError = + irq_of_parse_and_map(np,2); + if (priv->gfargrp[priv->num_grps].interruptTransmit < 0 || + priv->gfargrp[priv->num_grps].interruptReceive < 0 || + priv->gfargrp[priv->num_grps].interruptError < 0) { + return -EINVAL; + } + } + + priv->gfargrp[priv->num_grps].grp_id = priv->num_grps; + priv->gfargrp[priv->num_grps].priv = priv; + spin_lock_init(&priv->gfargrp[priv->num_grps].grplock); + if(priv->mode == MQ_MG_MODE) { + queue_mask = (u32 *)of_get_property(np, + "fsl,rx-bit-map", NULL); + priv->gfargrp[priv->num_grps].rx_bit_map = + queue_mask ? *queue_mask :(DEFAULT_MAPPING >> priv->num_grps); + queue_mask = (u32 *)of_get_property(np, + "fsl,tx-bit-map", NULL); + priv->gfargrp[priv->num_grps].tx_bit_map = + queue_mask ? *queue_mask : (DEFAULT_MAPPING >> priv->num_grps); + } else { + priv->gfargrp[priv->num_grps].rx_bit_map = 0xFF; + priv->gfargrp[priv->num_grps].tx_bit_map = 0xFF; + } + priv->num_grps++; + + return 0; +} + +static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev) { const char *model; const char *ctype; const void *mac_addr; - u64 addr, size; - int err = 0; - struct gfar_private *priv = netdev_priv(dev); - struct device_node *np = priv->node; + int err = 0, i; + struct net_device *dev = NULL; + struct gfar_private *priv = NULL; + struct device_node *np = ofdev->node; + struct device_node *child = NULL; const u32 *stash; const u32 *stash_len; const u32 *stash_idx; + unsigned int num_tx_qs, num_rx_qs; + u32 *tx_queues, *rx_queues; if (!np || !of_device_is_available(np)) return -ENODEV; - /* get a pointer to the register memory */ - addr = of_translate_address(np, of_get_address(np, 0, &size, NULL)); - priv->regs = ioremap(addr, size); + /* parse the num of tx and rx queues */ + tx_queues = (u32 *)of_get_property(np, "fsl,num_tx_queues", NULL); + num_tx_qs = tx_queues ? *tx_queues : 1; + + if (num_tx_qs > MAX_TX_QS) { + printk(KERN_ERR "num_tx_qs(=%d) greater than MAX_TX_QS(=%d)\n", + num_tx_qs, MAX_TX_QS); + printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n"); + return -EINVAL; + } + + rx_queues = (u32 *)of_get_property(np, "fsl,num_rx_queues", NULL); + num_rx_qs = rx_queues ? *rx_queues : 1; - if (priv->regs == NULL) + if (num_rx_qs > MAX_RX_QS) { + printk(KERN_ERR "num_rx_qs(=%d) greater than MAX_RX_QS(=%d)\n", + num_tx_qs, MAX_TX_QS); + printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n"); + return -EINVAL; + } + + *pdev = alloc_etherdev_mq(sizeof(*priv), num_tx_qs); + dev = *pdev; + if (NULL == dev) return -ENOMEM; - priv->interruptTransmit = irq_of_parse_and_map(np, 0); + priv = netdev_priv(dev); + priv->node = ofdev->node; + priv->ndev = dev; + + dev->num_tx_queues = num_tx_qs; + dev->real_num_tx_queues = num_tx_qs; + priv->num_tx_queues = num_tx_qs; + priv->num_rx_queues = num_rx_qs; + priv->num_grps = 0x0; model = of_get_property(np, "model", NULL); - /* If we aren't the FEC we have multiple interrupts */ - if (model && strcasecmp(model, "FEC")) { - priv->interruptReceive = irq_of_parse_and_map(np, 1); + for (i = 0; i < MAXGROUPS; i++) + priv->gfargrp[i].regs = NULL; + + /* Parse and initialize group specific information */ + if (of_device_is_compatible(np, "fsl,etsec2")) { + priv->mode = MQ_MG_MODE; + for_each_child_of_node(np, child) { + err = gfar_parse_group(child, priv, model); + if (err) + goto err_grp_init; + } + } else { + priv->mode = SQ_SG_MODE; + err = gfar_parse_group(np, priv, model); + if(err) + goto err_grp_init; + } - priv->interruptError = irq_of_parse_and_map(np, 2); + for (i = 0; i < priv->num_tx_queues; i++) + priv->tx_queue[i] = NULL; + for (i = 0; i < priv->num_rx_queues; i++) + priv->rx_queue[i] = NULL; + + for (i = 0; i < priv->num_tx_queues; i++) { + priv->tx_queue[i] = (struct gfar_priv_tx_q *)kmalloc( + sizeof (struct gfar_priv_tx_q), GFP_KERNEL); + if (!priv->tx_queue[i]) { + err = -ENOMEM; + goto tx_alloc_failed; + } + priv->tx_queue[i]->tx_skbuff = NULL; + priv->tx_queue[i]->qindex = i; + priv->tx_queue[i]->dev = dev; + spin_lock_init(&(priv->tx_queue[i]->txlock)); + } - if (priv->interruptTransmit < 0 || - priv->interruptReceive < 0 || - priv->interruptError < 0) { - err = -EINVAL; - goto err_out; + for (i = 0; i < priv->num_rx_queues; i++) { + priv->rx_queue[i] = (struct gfar_priv_rx_q *)kmalloc( + sizeof (struct gfar_priv_rx_q), GFP_KERNEL); + if (!priv->rx_queue[i]) { + err = -ENOMEM; + goto rx_alloc_failed; } + priv->rx_queue[i]->rx_skbuff = NULL; + priv->rx_queue[i]->qindex = i; + priv->rx_queue[i]->dev = dev; + spin_lock_init(&(priv->rx_queue[i]->rxlock)); } + stash = of_get_property(np, "bd-stash", NULL); - if(stash) { + if (stash) { priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING; priv->bd_stash_en = 1; } @@ -270,8 +734,13 @@ static int gfar_of_init(struct net_device *dev) return 0; -err_out: - iounmap(priv->regs); +rx_alloc_failed: + free_rx_pointers(priv); +tx_alloc_failed: + free_tx_pointers(priv); +err_grp_init: + unmap_group_regs(priv); + free_netdev(dev); return err; } @@ -289,6 +758,85 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd); } +static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs) +{ + unsigned int new_bit_map = 0x0; + int mask = 0x1 << (max_qs - 1), i; + for (i = 0; i < max_qs; i++) { + if (bit_map & mask) + new_bit_map = new_bit_map + (1 << i); + mask = mask >> 0x1; + } + return new_bit_map; +} + +static u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar, + u32 class) +{ + u32 rqfpr = FPR_FILER_MASK; + u32 rqfcr = 0x0; + + rqfar--; + rqfcr = RQFCR_CLE | RQFCR_PID_MASK | RQFCR_CMP_EXACT; + ftp_rqfpr[rqfar] = rqfpr; + ftp_rqfcr[rqfar] = rqfcr; + gfar_write_filer(priv, rqfar, rqfcr, rqfpr); + + rqfar--; + rqfcr = RQFCR_CMP_NOMATCH; + ftp_rqfpr[rqfar] = rqfpr; + ftp_rqfcr[rqfar] = rqfcr; + gfar_write_filer(priv, rqfar, rqfcr, rqfpr); + + rqfar--; + rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_PARSE | RQFCR_CLE | RQFCR_AND; + rqfpr = class; + ftp_rqfcr[rqfar] = rqfcr; + ftp_rqfpr[rqfar] = rqfpr; + gfar_write_filer(priv, rqfar, rqfcr, rqfpr); + + rqfar--; + rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_MASK | RQFCR_AND; + rqfpr = class; + ftp_rqfcr[rqfar] = rqfcr; + ftp_rqfpr[rqfar] = rqfpr; + gfar_write_filer(priv, rqfar, rqfcr, rqfpr); + + return rqfar; +} + +static void gfar_init_filer_table(struct gfar_private *priv) +{ + int i = 0x0; + u32 rqfar = MAX_FILER_IDX; + u32 rqfcr = 0x0; + u32 rqfpr = FPR_FILER_MASK; + + /* Default rule */ + rqfcr = RQFCR_CMP_MATCH; + ftp_rqfcr[rqfar] = rqfcr; + ftp_rqfpr[rqfar] = rqfpr; + gfar_write_filer(priv, rqfar, rqfcr, rqfpr); + + rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6); + rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_UDP); + rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_TCP); + rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4); + rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_UDP); + rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_TCP); + + /* cur_filer_idx indicated the fisrt non-masked rule */ + priv->cur_filer_idx = rqfar; + + /* Rest are masked rules */ + rqfcr = RQFCR_CMP_NOMATCH; + for (i = 0; i < rqfar; i++) { + ftp_rqfcr[i] = rqfcr; + ftp_rqfpr[i] = rqfpr; + gfar_write_filer(priv, i, rqfcr, rqfpr); + } +} + /* Set up the ethernet device structure, private data, * and anything else we need before we start */ static int gfar_probe(struct of_device *ofdev, @@ -297,14 +845,17 @@ static int gfar_probe(struct of_device *ofdev, u32 tempval; struct net_device *dev = NULL; struct gfar_private *priv = NULL; - int err = 0; + struct gfar __iomem *regs = NULL; + int err = 0, i, grp_idx = 0; int len_devname; + u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0; + u32 isrg = 0; + u32 __iomem *baddr; - /* Create an ethernet device instance */ - dev = alloc_etherdev(sizeof (*priv)); + err = gfar_of_init(ofdev, &dev); - if (NULL == dev) - return -ENOMEM; + if (err) + return err; priv = netdev_priv(dev); priv->ndev = dev; @@ -312,50 +863,46 @@ static int gfar_probe(struct of_device *ofdev, priv->node = ofdev->node; SET_NETDEV_DEV(dev, &ofdev->dev); - err = gfar_of_init(dev); - - if (err) - goto regs_fail; - - spin_lock_init(&priv->txlock); - spin_lock_init(&priv->rxlock); spin_lock_init(&priv->bflock); INIT_WORK(&priv->reset_task, gfar_reset_task); dev_set_drvdata(&ofdev->dev, priv); + regs = priv->gfargrp[0].regs; /* Stop the DMA engine now, in case it was running before */ /* (The firmware could have used it, and left it running). */ gfar_halt(dev); /* Reset MAC layer */ - gfar_write(&priv->regs->maccfg1, MACCFG1_SOFT_RESET); + gfar_write(®s->maccfg1, MACCFG1_SOFT_RESET); /* We need to delay at least 3 TX clocks */ udelay(2); tempval = (MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); - gfar_write(&priv->regs->maccfg1, tempval); + gfar_write(®s->maccfg1, tempval); /* Initialize MACCFG2. */ - gfar_write(&priv->regs->maccfg2, MACCFG2_INIT_SETTINGS); + gfar_write(®s->maccfg2, MACCFG2_INIT_SETTINGS); /* Initialize ECNTRL */ - gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS); + gfar_write(®s->ecntrl, ECNTRL_INIT_SETTINGS); /* Set the dev->base_addr to the gfar reg region */ - dev->base_addr = (unsigned long) (priv->regs); + dev->base_addr = (unsigned long) regs; SET_NETDEV_DEV(dev, &ofdev->dev); /* Fill in the dev structure */ dev->watchdog_timeo = TX_TIMEOUT; - netif_napi_add(dev, &priv->napi, gfar_poll, GFAR_DEV_WEIGHT); dev->mtu = 1500; - dev->netdev_ops = &gfar_netdev_ops; dev->ethtool_ops = &gfar_ethtool_ops; + /* Register for napi ...We are registering NAPI for each grp */ + for (i = 0; i < priv->num_grps; i++) + netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, GFAR_DEV_WEIGHT); + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) { priv->rx_csum_enable = 1; dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA; @@ -371,35 +918,35 @@ static int gfar_probe(struct of_device *ofdev, priv->extended_hash = 1; priv->hash_width = 9; - priv->hash_regs[0] = &priv->regs->igaddr0; - priv->hash_regs[1] = &priv->regs->igaddr1; - priv->hash_regs[2] = &priv->regs->igaddr2; - priv->hash_regs[3] = &priv->regs->igaddr3; - priv->hash_regs[4] = &priv->regs->igaddr4; - priv->hash_regs[5] = &priv->regs->igaddr5; - priv->hash_regs[6] = &priv->regs->igaddr6; - priv->hash_regs[7] = &priv->regs->igaddr7; - priv->hash_regs[8] = &priv->regs->gaddr0; - priv->hash_regs[9] = &priv->regs->gaddr1; - priv->hash_regs[10] = &priv->regs->gaddr2; - priv->hash_regs[11] = &priv->regs->gaddr3; - priv->hash_regs[12] = &priv->regs->gaddr4; - priv->hash_regs[13] = &priv->regs->gaddr5; - priv->hash_regs[14] = &priv->regs->gaddr6; - priv->hash_regs[15] = &priv->regs->gaddr7; + priv->hash_regs[0] = ®s->igaddr0; + priv->hash_regs[1] = ®s->igaddr1; + priv->hash_regs[2] = ®s->igaddr2; + priv->hash_regs[3] = ®s->igaddr3; + priv->hash_regs[4] = ®s->igaddr4; + priv->hash_regs[5] = ®s->igaddr5; + priv->hash_regs[6] = ®s->igaddr6; + priv->hash_regs[7] = ®s->igaddr7; + priv->hash_regs[8] = ®s->gaddr0; + priv->hash_regs[9] = ®s->gaddr1; + priv->hash_regs[10] = ®s->gaddr2; + priv->hash_regs[11] = ®s->gaddr3; + priv->hash_regs[12] = ®s->gaddr4; + priv->hash_regs[13] = ®s->gaddr5; + priv->hash_regs[14] = ®s->gaddr6; + priv->hash_regs[15] = ®s->gaddr7; } else { priv->extended_hash = 0; priv->hash_width = 8; - priv->hash_regs[0] = &priv->regs->gaddr0; - priv->hash_regs[1] = &priv->regs->gaddr1; - priv->hash_regs[2] = &priv->regs->gaddr2; - priv->hash_regs[3] = &priv->regs->gaddr3; - priv->hash_regs[4] = &priv->regs->gaddr4; - priv->hash_regs[5] = &priv->regs->gaddr5; - priv->hash_regs[6] = &priv->regs->gaddr6; - priv->hash_regs[7] = &priv->regs->gaddr7; + priv->hash_regs[0] = ®s->gaddr0; + priv->hash_regs[1] = ®s->gaddr1; + priv->hash_regs[2] = ®s->gaddr2; + priv->hash_regs[3] = ®s->gaddr3; + priv->hash_regs[4] = ®s->gaddr4; + priv->hash_regs[5] = ®s->gaddr5; + priv->hash_regs[6] = ®s->gaddr6; + priv->hash_regs[7] = ®s->gaddr7; } if (priv->device_flags & FSL_GIANFAR_DEV_HAS_PADDING) @@ -410,15 +957,70 @@ static int gfar_probe(struct of_device *ofdev, if (dev->features & NETIF_F_IP_CSUM) dev->hard_header_len += GMAC_FCB_LEN; + /* Program the isrg regs only if number of grps > 1 */ + if (priv->num_grps > 1) { + baddr = ®s->isrg0; + for (i = 0; i < priv->num_grps; i++) { + isrg |= (priv->gfargrp[i].rx_bit_map << ISRG_SHIFT_RX); + isrg |= (priv->gfargrp[i].tx_bit_map << ISRG_SHIFT_TX); + gfar_write(baddr, isrg); + baddr++; + isrg = 0x0; + } + } + + /* Need to reverse the bit maps as bit_map's MSB is q0 + * but, for_each_bit parses from right to left, which + * basically reverses the queue numbers */ + for (i = 0; i< priv->num_grps; i++) { + priv->gfargrp[i].tx_bit_map = reverse_bitmap( + priv->gfargrp[i].tx_bit_map, MAX_TX_QS); + priv->gfargrp[i].rx_bit_map = reverse_bitmap( + priv->gfargrp[i].rx_bit_map, MAX_RX_QS); + } + + /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values, + * also assign queues to groups */ + for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) { + priv->gfargrp[grp_idx].num_rx_queues = 0x0; + for_each_bit(i, &priv->gfargrp[grp_idx].rx_bit_map, + priv->num_rx_queues) { + priv->gfargrp[grp_idx].num_rx_queues++; + priv->rx_queue[i]->grp = &priv->gfargrp[grp_idx]; + rstat = rstat | (RSTAT_CLEAR_RHALT >> i); + rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i); + } + priv->gfargrp[grp_idx].num_tx_queues = 0x0; + for_each_bit (i, &priv->gfargrp[grp_idx].tx_bit_map, + priv->num_tx_queues) { + priv->gfargrp[grp_idx].num_tx_queues++; + priv->tx_queue[i]->grp = &priv->gfargrp[grp_idx]; + tstat = tstat | (TSTAT_CLEAR_THALT >> i); + tqueue = tqueue | (TQUEUE_EN0 >> i); + } + priv->gfargrp[grp_idx].rstat = rstat; + priv->gfargrp[grp_idx].tstat = tstat; + rstat = tstat =0; + } + + gfar_write(®s->rqueue, rqueue); + gfar_write(®s->tqueue, tqueue); + priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE; - priv->tx_ring_size = DEFAULT_TX_RING_SIZE; - priv->rx_ring_size = DEFAULT_RX_RING_SIZE; - priv->num_txbdfree = DEFAULT_TX_RING_SIZE; - priv->txcoalescing = DEFAULT_TX_COALESCE; - priv->txic = DEFAULT_TXIC; - priv->rxcoalescing = DEFAULT_RX_COALESCE; - priv->rxic = DEFAULT_RXIC; + /* Initializing some of the rx/tx queue level parameters */ + for (i = 0; i < priv->num_tx_queues; i++) { + priv->tx_queue[i]->tx_ring_size = DEFAULT_TX_RING_SIZE; + priv->tx_queue[i]->num_txbdfree = DEFAULT_TX_RING_SIZE; + priv->tx_queue[i]->txcoalescing = DEFAULT_TX_COALESCE; + priv->tx_queue[i]->txic = DEFAULT_TXIC; + } + + for (i = 0; i < priv->num_rx_queues; i++) { + priv->rx_queue[i]->rx_ring_size = DEFAULT_RX_RING_SIZE; + priv->rx_queue[i]->rxcoalescing = DEFAULT_RX_COALESCE; + priv->rx_queue[i]->rxic = DEFAULT_RXIC; + } /* Enable most messages by default */ priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; @@ -439,20 +1041,43 @@ static int gfar_probe(struct of_device *ofdev, /* fill out IRQ number and name fields */ len_devname = strlen(dev->name); - strncpy(&priv->int_name_tx[0], dev->name, len_devname); - if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { - strncpy(&priv->int_name_tx[len_devname], - "_tx", sizeof("_tx") + 1); - - strncpy(&priv->int_name_rx[0], dev->name, len_devname); - strncpy(&priv->int_name_rx[len_devname], - "_rx", sizeof("_rx") + 1); + for (i = 0; i < priv->num_grps; i++) { + strncpy(&priv->gfargrp[i].int_name_tx[0], dev->name, + len_devname); + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { + strncpy(&priv->gfargrp[i].int_name_tx[len_devname], + "_g", sizeof("_g")); + priv->gfargrp[i].int_name_tx[ + strlen(priv->gfargrp[i].int_name_tx)] = i+48; + strncpy(&priv->gfargrp[i].int_name_tx[strlen( + priv->gfargrp[i].int_name_tx)], + "_tx", sizeof("_tx") + 1); + + strncpy(&priv->gfargrp[i].int_name_rx[0], dev->name, + len_devname); + strncpy(&priv->gfargrp[i].int_name_rx[len_devname], + "_g", sizeof("_g")); + priv->gfargrp[i].int_name_rx[ + strlen(priv->gfargrp[i].int_name_rx)] = i+48; + strncpy(&priv->gfargrp[i].int_name_rx[strlen( + priv->gfargrp[i].int_name_rx)], + "_rx", sizeof("_rx") + 1); + + strncpy(&priv->gfargrp[i].int_name_er[0], dev->name, + len_devname); + strncpy(&priv->gfargrp[i].int_name_er[len_devname], + "_g", sizeof("_g")); + priv->gfargrp[i].int_name_er[strlen( + priv->gfargrp[i].int_name_er)] = i+48; + strncpy(&priv->gfargrp[i].int_name_er[strlen(\ + priv->gfargrp[i].int_name_er)], + "_er", sizeof("_er") + 1); + } else + priv->gfargrp[i].int_name_tx[len_devname] = '\0'; + } - strncpy(&priv->int_name_er[0], dev->name, len_devname); - strncpy(&priv->int_name_er[len_devname], - "_er", sizeof("_er") + 1); - } else - priv->int_name_tx[len_devname] = '\0'; + /* Initialize the filer table */ + gfar_init_filer_table(priv); /* Create all the sysfs files */ gfar_init_sysfs(dev); @@ -463,14 +1088,19 @@ static int gfar_probe(struct of_device *ofdev, /* Even more device info helps when determining which kernel */ /* provided which set of benchmarks. */ printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name); - printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n", - dev->name, priv->rx_ring_size, priv->tx_ring_size); + for (i = 0; i < priv->num_rx_queues; i++) + printk(KERN_INFO "%s: :RX BD ring size for Q[%d]: %d\n", + dev->name, i, priv->rx_queue[i]->rx_ring_size); + for(i = 0; i < priv->num_tx_queues; i++) + printk(KERN_INFO "%s:TX BD ring size for Q[%d]: %d\n", + dev->name, i, priv->tx_queue[i]->tx_ring_size); return 0; register_fail: - iounmap(priv->regs); -regs_fail: + unmap_group_regs(priv); + free_tx_pointers(priv); + free_rx_pointers(priv); if (priv->phy_node) of_node_put(priv->phy_node); if (priv->tbi_node) @@ -491,54 +1121,59 @@ static int gfar_remove(struct of_device *ofdev) dev_set_drvdata(&ofdev->dev, NULL); unregister_netdev(priv->ndev); - iounmap(priv->regs); + unmap_group_regs(priv); free_netdev(priv->ndev); return 0; } #ifdef CONFIG_PM -static int gfar_suspend(struct of_device *ofdev, pm_message_t state) + +static int gfar_suspend(struct device *dev) { - struct gfar_private *priv = dev_get_drvdata(&ofdev->dev); - struct net_device *dev = priv->ndev; + struct gfar_private *priv = dev_get_drvdata(dev); + struct net_device *ndev = priv->ndev; + struct gfar __iomem *regs = priv->gfargrp[0].regs; unsigned long flags; u32 tempval; int magic_packet = priv->wol_en && (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); - netif_device_detach(dev); + netif_device_detach(ndev); - if (netif_running(dev)) { - spin_lock_irqsave(&priv->txlock, flags); - spin_lock(&priv->rxlock); + if (netif_running(ndev)) { - gfar_halt_nodisable(dev); + local_irq_save(flags); + lock_tx_qs(priv); + lock_rx_qs(priv); + + gfar_halt_nodisable(ndev); /* Disable Tx, and Rx if wake-on-LAN is disabled. */ - tempval = gfar_read(&priv->regs->maccfg1); + tempval = gfar_read(®s->maccfg1); tempval &= ~MACCFG1_TX_EN; if (!magic_packet) tempval &= ~MACCFG1_RX_EN; - gfar_write(&priv->regs->maccfg1, tempval); + gfar_write(®s->maccfg1, tempval); - spin_unlock(&priv->rxlock); - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_rx_qs(priv); + unlock_tx_qs(priv); + local_irq_restore(flags); - napi_disable(&priv->napi); + disable_napi(priv); if (magic_packet) { /* Enable interrupt on Magic Packet */ - gfar_write(&priv->regs->imask, IMASK_MAG); + gfar_write(®s->imask, IMASK_MAG); /* Enable Magic Packet mode */ - tempval = gfar_read(&priv->regs->maccfg2); + tempval = gfar_read(®s->maccfg2); tempval |= MACCFG2_MPEN; - gfar_write(&priv->regs->maccfg2, tempval); + gfar_write(®s->maccfg2, tempval); } else { phy_stop(priv->phydev); } @@ -547,17 +1182,18 @@ static int gfar_suspend(struct of_device *ofdev, pm_message_t state) return 0; } -static int gfar_resume(struct of_device *ofdev) +static int gfar_resume(struct device *dev) { - struct gfar_private *priv = dev_get_drvdata(&ofdev->dev); - struct net_device *dev = priv->ndev; + struct gfar_private *priv = dev_get_drvdata(dev); + struct net_device *ndev = priv->ndev; + struct gfar __iomem *regs = priv->gfargrp[0].regs; unsigned long flags; u32 tempval; int magic_packet = priv->wol_en && (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); - if (!netif_running(dev)) { - netif_device_attach(dev); + if (!netif_running(ndev)) { + netif_device_attach(ndev); return 0; } @@ -567,28 +1203,80 @@ static int gfar_resume(struct of_device *ofdev) /* Disable Magic Packet mode, in case something * else woke us up. */ + local_irq_save(flags); + lock_tx_qs(priv); + lock_rx_qs(priv); - spin_lock_irqsave(&priv->txlock, flags); - spin_lock(&priv->rxlock); - - tempval = gfar_read(&priv->regs->maccfg2); + tempval = gfar_read(®s->maccfg2); tempval &= ~MACCFG2_MPEN; - gfar_write(&priv->regs->maccfg2, tempval); + gfar_write(®s->maccfg2, tempval); - gfar_start(dev); + gfar_start(ndev); - spin_unlock(&priv->rxlock); - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_rx_qs(priv); + unlock_tx_qs(priv); + local_irq_restore(flags); - netif_device_attach(dev); + netif_device_attach(ndev); - napi_enable(&priv->napi); + enable_napi(priv); return 0; } + +static int gfar_restore(struct device *dev) +{ + struct gfar_private *priv = dev_get_drvdata(dev); + struct net_device *ndev = priv->ndev; + + if (!netif_running(ndev)) + return 0; + + gfar_init_bds(ndev); + init_registers(ndev); + gfar_set_mac_address(ndev); + gfar_init_mac(ndev); + gfar_start(ndev); + + priv->oldlink = 0; + priv->oldspeed = 0; + priv->oldduplex = -1; + + if (priv->phydev) + phy_start(priv->phydev); + + netif_device_attach(ndev); + enable_napi(priv); + + return 0; +} + +static struct dev_pm_ops gfar_pm_ops = { + .suspend = gfar_suspend, + .resume = gfar_resume, + .freeze = gfar_suspend, + .thaw = gfar_resume, + .restore = gfar_restore, +}; + +#define GFAR_PM_OPS (&gfar_pm_ops) + +static int gfar_legacy_suspend(struct of_device *ofdev, pm_message_t state) +{ + return gfar_suspend(&ofdev->dev); +} + +static int gfar_legacy_resume(struct of_device *ofdev) +{ + return gfar_resume(&ofdev->dev); +} + #else -#define gfar_suspend NULL -#define gfar_resume NULL + +#define GFAR_PM_OPS NULL +#define gfar_legacy_suspend NULL +#define gfar_legacy_resume NULL + #endif /* Reads the controller's registers to determine what interface @@ -597,7 +1285,10 @@ static int gfar_resume(struct of_device *ofdev) static phy_interface_t gfar_get_interface(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - u32 ecntrl = gfar_read(&priv->regs->ecntrl); + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 ecntrl; + + ecntrl = gfar_read(®s->ecntrl); if (ecntrl & ECNTRL_SGMII_MODE) return PHY_INTERFACE_MODE_SGMII; @@ -719,46 +1410,52 @@ static void gfar_configure_serdes(struct net_device *dev) static void init_registers(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); + struct gfar __iomem *regs = NULL; + int i = 0; - /* Clear IEVENT */ - gfar_write(&priv->regs->ievent, IEVENT_INIT_CLEAR); + for (i = 0; i < priv->num_grps; i++) { + regs = priv->gfargrp[i].regs; + /* Clear IEVENT */ + gfar_write(®s->ievent, IEVENT_INIT_CLEAR); - /* Initialize IMASK */ - gfar_write(&priv->regs->imask, IMASK_INIT_CLEAR); + /* Initialize IMASK */ + gfar_write(®s->imask, IMASK_INIT_CLEAR); + } + regs = priv->gfargrp[0].regs; /* Init hash registers to zero */ - gfar_write(&priv->regs->igaddr0, 0); - gfar_write(&priv->regs->igaddr1, 0); - gfar_write(&priv->regs->igaddr2, 0); - gfar_write(&priv->regs->igaddr3, 0); - gfar_write(&priv->regs->igaddr4, 0); - gfar_write(&priv->regs->igaddr5, 0); - gfar_write(&priv->regs->igaddr6, 0); - gfar_write(&priv->regs->igaddr7, 0); - - gfar_write(&priv->regs->gaddr0, 0); - gfar_write(&priv->regs->gaddr1, 0); - gfar_write(&priv->regs->gaddr2, 0); - gfar_write(&priv->regs->gaddr3, 0); - gfar_write(&priv->regs->gaddr4, 0); - gfar_write(&priv->regs->gaddr5, 0); - gfar_write(&priv->regs->gaddr6, 0); - gfar_write(&priv->regs->gaddr7, 0); + gfar_write(®s->igaddr0, 0); + gfar_write(®s->igaddr1, 0); + gfar_write(®s->igaddr2, 0); + gfar_write(®s->igaddr3, 0); + gfar_write(®s->igaddr4, 0); + gfar_write(®s->igaddr5, 0); + gfar_write(®s->igaddr6, 0); + gfar_write(®s->igaddr7, 0); + + gfar_write(®s->gaddr0, 0); + gfar_write(®s->gaddr1, 0); + gfar_write(®s->gaddr2, 0); + gfar_write(®s->gaddr3, 0); + gfar_write(®s->gaddr4, 0); + gfar_write(®s->gaddr5, 0); + gfar_write(®s->gaddr6, 0); + gfar_write(®s->gaddr7, 0); /* Zero out the rmon mib registers if it has them */ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) { - memset_io(&(priv->regs->rmon), 0, sizeof (struct rmon_mib)); + memset_io(&(regs->rmon), 0, sizeof (struct rmon_mib)); /* Mask off the CAM interrupts */ - gfar_write(&priv->regs->rmon.cam1, 0xffffffff); - gfar_write(&priv->regs->rmon.cam2, 0xffffffff); + gfar_write(®s->rmon.cam1, 0xffffffff); + gfar_write(®s->rmon.cam2, 0xffffffff); } /* Initialize the max receive buffer length */ - gfar_write(&priv->regs->mrblr, priv->rx_buffer_size); + gfar_write(®s->mrblr, priv->rx_buffer_size); /* Initialize the Minimum Frame Length Register */ - gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS); + gfar_write(®s->minflr, MINFLR_INIT_SETTINGS); } @@ -766,23 +1463,28 @@ static void init_registers(struct net_device *dev) static void gfar_halt_nodisable(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - struct gfar __iomem *regs = priv->regs; + struct gfar __iomem *regs = NULL; u32 tempval; + int i = 0; - /* Mask all interrupts */ - gfar_write(®s->imask, IMASK_INIT_CLEAR); + for (i = 0; i < priv->num_grps; i++) { + regs = priv->gfargrp[i].regs; + /* Mask all interrupts */ + gfar_write(®s->imask, IMASK_INIT_CLEAR); - /* Clear all interrupts */ - gfar_write(®s->ievent, IEVENT_INIT_CLEAR); + /* Clear all interrupts */ + gfar_write(®s->ievent, IEVENT_INIT_CLEAR); + } + regs = priv->gfargrp[0].regs; /* Stop the DMA, and wait for it to stop */ - tempval = gfar_read(&priv->regs->dmactrl); + tempval = gfar_read(®s->dmactrl); if ((tempval & (DMACTRL_GRS | DMACTRL_GTS)) != (DMACTRL_GRS | DMACTRL_GTS)) { tempval |= (DMACTRL_GRS | DMACTRL_GTS); - gfar_write(&priv->regs->dmactrl, tempval); + gfar_write(®s->dmactrl, tempval); - while (!(gfar_read(&priv->regs->ievent) & + while (!(gfar_read(®s->ievent) & (IEVENT_GRSC | IEVENT_GTSC))) cpu_relax(); } @@ -792,7 +1494,7 @@ static void gfar_halt_nodisable(struct net_device *dev) void gfar_halt(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - struct gfar __iomem *regs = priv->regs; + struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; gfar_halt_nodisable(dev); @@ -803,101 +1505,131 @@ void gfar_halt(struct net_device *dev) gfar_write(®s->maccfg1, tempval); } +static void free_grp_irqs(struct gfar_priv_grp *grp) +{ + free_irq(grp->interruptError, grp); + free_irq(grp->interruptTransmit, grp); + free_irq(grp->interruptReceive, grp); +} + void stop_gfar(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - struct gfar __iomem *regs = priv->regs; unsigned long flags; + int i; phy_stop(priv->phydev); + /* Lock it down */ - spin_lock_irqsave(&priv->txlock, flags); - spin_lock(&priv->rxlock); + local_irq_save(flags); + lock_tx_qs(priv); + lock_rx_qs(priv); gfar_halt(dev); - spin_unlock(&priv->rxlock); - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_rx_qs(priv); + unlock_tx_qs(priv); + local_irq_restore(flags); /* Free the IRQs */ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { - free_irq(priv->interruptError, dev); - free_irq(priv->interruptTransmit, dev); - free_irq(priv->interruptReceive, dev); + for (i = 0; i < priv->num_grps; i++) + free_grp_irqs(&priv->gfargrp[i]); } else { - free_irq(priv->interruptTransmit, dev); + for (i = 0; i < priv->num_grps; i++) + free_irq(priv->gfargrp[i].interruptTransmit, + &priv->gfargrp[i]); } free_skb_resources(priv); - - dma_free_coherent(&priv->ofdev->dev, - sizeof(struct txbd8)*priv->tx_ring_size - + sizeof(struct rxbd8)*priv->rx_ring_size, - priv->tx_bd_base, - gfar_read(®s->tbase0)); } -/* If there are any tx skbs or rx skbs still around, free them. - * Then free tx_skbuff and rx_skbuff */ -static void free_skb_resources(struct gfar_private *priv) +static void free_skb_tx_queue(struct gfar_priv_tx_q *tx_queue) { - struct rxbd8 *rxbdp; struct txbd8 *txbdp; + struct gfar_private *priv = netdev_priv(tx_queue->dev); int i, j; - /* Go through all the buffer descriptors and free their data buffers */ - txbdp = priv->tx_bd_base; + txbdp = tx_queue->tx_bd_base; - for (i = 0; i < priv->tx_ring_size; i++) { - if (!priv->tx_skbuff[i]) + for (i = 0; i < tx_queue->tx_ring_size; i++) { + if (!tx_queue->tx_skbuff[i]) continue; dma_unmap_single(&priv->ofdev->dev, txbdp->bufPtr, txbdp->length, DMA_TO_DEVICE); txbdp->lstatus = 0; - for (j = 0; j < skb_shinfo(priv->tx_skbuff[i])->nr_frags; j++) { + for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags; + j++) { txbdp++; dma_unmap_page(&priv->ofdev->dev, txbdp->bufPtr, txbdp->length, DMA_TO_DEVICE); } txbdp++; - dev_kfree_skb_any(priv->tx_skbuff[i]); - priv->tx_skbuff[i] = NULL; + dev_kfree_skb_any(tx_queue->tx_skbuff[i]); + tx_queue->tx_skbuff[i] = NULL; } + kfree(tx_queue->tx_skbuff); +} - kfree(priv->tx_skbuff); - - rxbdp = priv->rx_bd_base; +static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue) +{ + struct rxbd8 *rxbdp; + struct gfar_private *priv = netdev_priv(rx_queue->dev); + int i; - /* rx_skbuff is not guaranteed to be allocated, so only - * free it and its contents if it is allocated */ - if(priv->rx_skbuff != NULL) { - for (i = 0; i < priv->rx_ring_size; i++) { - if (priv->rx_skbuff[i]) { - dma_unmap_single(&priv->ofdev->dev, rxbdp->bufPtr, - priv->rx_buffer_size, - DMA_FROM_DEVICE); + rxbdp = rx_queue->rx_bd_base; - dev_kfree_skb_any(priv->rx_skbuff[i]); - priv->rx_skbuff[i] = NULL; - } + for (i = 0; i < rx_queue->rx_ring_size; i++) { + if (rx_queue->rx_skbuff[i]) { + dma_unmap_single(&priv->ofdev->dev, + rxbdp->bufPtr, priv->rx_buffer_size, + DMA_FROM_DEVICE); + dev_kfree_skb_any(rx_queue->rx_skbuff[i]); + rx_queue->rx_skbuff[i] = NULL; + } + rxbdp->lstatus = 0; + rxbdp->bufPtr = 0; + rxbdp++; + } + kfree(rx_queue->rx_skbuff); +} - rxbdp->lstatus = 0; - rxbdp->bufPtr = 0; +/* If there are any tx skbs or rx skbs still around, free them. + * Then free tx_skbuff and rx_skbuff */ +static void free_skb_resources(struct gfar_private *priv) +{ + struct gfar_priv_tx_q *tx_queue = NULL; + struct gfar_priv_rx_q *rx_queue = NULL; + int i; - rxbdp++; - } + /* Go through all the buffer descriptors and free their data buffers */ + for (i = 0; i < priv->num_tx_queues; i++) { + tx_queue = priv->tx_queue[i]; + if(!tx_queue->tx_skbuff) + free_skb_tx_queue(tx_queue); + } - kfree(priv->rx_skbuff); + for (i = 0; i < priv->num_rx_queues; i++) { + rx_queue = priv->rx_queue[i]; + if(!rx_queue->rx_skbuff) + free_skb_rx_queue(rx_queue); } + + dma_free_coherent(&priv->ofdev->dev, + sizeof(struct txbd8) * priv->total_tx_ring_size + + sizeof(struct rxbd8) * priv->total_rx_ring_size, + priv->tx_queue[0]->tx_bd_base, + priv->tx_queue[0]->tx_bd_dma_base); } void gfar_start(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - struct gfar __iomem *regs = priv->regs; + struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; + int i = 0; /* Enable Rx and Tx in MACCFG1 */ tempval = gfar_read(®s->maccfg1); @@ -905,269 +1637,159 @@ void gfar_start(struct net_device *dev) gfar_write(®s->maccfg1, tempval); /* Initialize DMACTRL to have WWR and WOP */ - tempval = gfar_read(&priv->regs->dmactrl); + tempval = gfar_read(®s->dmactrl); tempval |= DMACTRL_INIT_SETTINGS; - gfar_write(&priv->regs->dmactrl, tempval); + gfar_write(®s->dmactrl, tempval); /* Make sure we aren't stopped */ - tempval = gfar_read(&priv->regs->dmactrl); + tempval = gfar_read(®s->dmactrl); tempval &= ~(DMACTRL_GRS | DMACTRL_GTS); - gfar_write(&priv->regs->dmactrl, tempval); - - /* Clear THLT/RHLT, so that the DMA starts polling now */ - gfar_write(®s->tstat, TSTAT_CLEAR_THALT); - gfar_write(®s->rstat, RSTAT_CLEAR_RHALT); - - /* Unmask the interrupts we look for */ - gfar_write(®s->imask, IMASK_DEFAULT); + gfar_write(®s->dmactrl, tempval); + + for (i = 0; i < priv->num_grps; i++) { + regs = priv->gfargrp[i].regs; + /* Clear THLT/RHLT, so that the DMA starts polling now */ + gfar_write(®s->tstat, priv->gfargrp[i].tstat); + gfar_write(®s->rstat, priv->gfargrp[i].rstat); + /* Unmask the interrupts we look for */ + gfar_write(®s->imask, IMASK_DEFAULT); + } dev->trans_start = jiffies; } -/* Bring the controller up and running */ -int startup_gfar(struct net_device *dev) +void gfar_configure_coalescing(struct gfar_private *priv, + unsigned long tx_mask, unsigned long rx_mask) { - struct txbd8 *txbdp; - struct rxbd8 *rxbdp; - dma_addr_t addr = 0; - unsigned long vaddr; - int i; - struct gfar_private *priv = netdev_priv(dev); - struct gfar __iomem *regs = priv->regs; - int err = 0; - u32 rctrl = 0; - u32 tctrl = 0; - u32 attrs = 0; - - gfar_write(®s->imask, IMASK_INIT_CLEAR); + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 __iomem *baddr; + int i = 0; - /* Allocate memory for the buffer descriptors */ - vaddr = (unsigned long) dma_alloc_coherent(&priv->ofdev->dev, - sizeof (struct txbd8) * priv->tx_ring_size + - sizeof (struct rxbd8) * priv->rx_ring_size, - &addr, GFP_KERNEL); - - if (vaddr == 0) { - if (netif_msg_ifup(priv)) - printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n", - dev->name); - return -ENOMEM; - } - - priv->tx_bd_base = (struct txbd8 *) vaddr; - - /* enet DMA only understands physical addresses */ - gfar_write(®s->tbase0, addr); - - /* Start the rx descriptor ring where the tx ring leaves off */ - addr = addr + sizeof (struct txbd8) * priv->tx_ring_size; - vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size; - priv->rx_bd_base = (struct rxbd8 *) vaddr; - gfar_write(®s->rbase0, addr); - - /* Setup the skbuff rings */ - priv->tx_skbuff = - (struct sk_buff **) kmalloc(sizeof (struct sk_buff *) * - priv->tx_ring_size, GFP_KERNEL); - - if (NULL == priv->tx_skbuff) { - if (netif_msg_ifup(priv)) - printk(KERN_ERR "%s: Could not allocate tx_skbuff\n", - dev->name); - err = -ENOMEM; - goto tx_skb_fail; - } - - for (i = 0; i < priv->tx_ring_size; i++) - priv->tx_skbuff[i] = NULL; - - priv->rx_skbuff = - (struct sk_buff **) kmalloc(sizeof (struct sk_buff *) * - priv->rx_ring_size, GFP_KERNEL); - - if (NULL == priv->rx_skbuff) { - if (netif_msg_ifup(priv)) - printk(KERN_ERR "%s: Could not allocate rx_skbuff\n", - dev->name); - err = -ENOMEM; - goto rx_skb_fail; - } - - for (i = 0; i < priv->rx_ring_size; i++) - priv->rx_skbuff[i] = NULL; - - /* Initialize some variables in our dev structure */ - priv->num_txbdfree = priv->tx_ring_size; - priv->dirty_tx = priv->cur_tx = priv->tx_bd_base; - priv->cur_rx = priv->rx_bd_base; - priv->skb_curtx = priv->skb_dirtytx = 0; - priv->skb_currx = 0; - - /* Initialize Transmit Descriptor Ring */ - txbdp = priv->tx_bd_base; - for (i = 0; i < priv->tx_ring_size; i++) { - txbdp->lstatus = 0; - txbdp->bufPtr = 0; - txbdp++; - } - - /* Set the last descriptor in the ring to indicate wrap */ - txbdp--; - txbdp->status |= TXBD_WRAP; - - rxbdp = priv->rx_bd_base; - for (i = 0; i < priv->rx_ring_size; i++) { - struct sk_buff *skb; - - skb = gfar_new_skb(dev); - - if (!skb) { - printk(KERN_ERR "%s: Can't allocate RX buffers\n", - dev->name); + /* Backward compatible case ---- even if we enable + * multiple queues, there's only single reg to program + */ + gfar_write(®s->txic, 0); + if(likely(priv->tx_queue[0]->txcoalescing)) + gfar_write(®s->txic, priv->tx_queue[0]->txic); - goto err_rxalloc_fail; + gfar_write(®s->rxic, 0); + if(unlikely(priv->rx_queue[0]->rxcoalescing)) + gfar_write(®s->rxic, priv->rx_queue[0]->rxic); + + if (priv->mode == MQ_MG_MODE) { + baddr = ®s->txic0; + for_each_bit (i, &tx_mask, priv->num_tx_queues) { + if (likely(priv->tx_queue[i]->txcoalescing)) { + gfar_write(baddr + i, 0); + gfar_write(baddr + i, priv->tx_queue[i]->txic); + } } - priv->rx_skbuff[i] = skb; - - gfar_new_rxbdp(dev, rxbdp, skb); - - rxbdp++; + baddr = ®s->rxic0; + for_each_bit (i, &rx_mask, priv->num_rx_queues) { + if (likely(priv->rx_queue[i]->rxcoalescing)) { + gfar_write(baddr + i, 0); + gfar_write(baddr + i, priv->rx_queue[i]->rxic); + } + } } +} - /* Set the last descriptor in the ring to wrap */ - rxbdp--; - rxbdp->status |= RXBD_WRAP; +static int register_grp_irqs(struct gfar_priv_grp *grp) +{ + struct gfar_private *priv = grp->priv; + struct net_device *dev = priv->ndev; + int err; /* If the device has multiple interrupts, register for * them. Otherwise, only register for the one */ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { /* Install our interrupt handlers for Error, * Transmit, and Receive */ - if (request_irq(priv->interruptError, gfar_error, - 0, priv->int_name_er, dev) < 0) { + if ((err = request_irq(grp->interruptError, gfar_error, 0, + grp->int_name_er,grp)) < 0) { if (netif_msg_intr(priv)) printk(KERN_ERR "%s: Can't get IRQ %d\n", - dev->name, priv->interruptError); + dev->name, grp->interruptError); - err = -1; - goto err_irq_fail; + goto err_irq_fail; } - if (request_irq(priv->interruptTransmit, gfar_transmit, - 0, priv->int_name_tx, dev) < 0) { + if ((err = request_irq(grp->interruptTransmit, gfar_transmit, + 0, grp->int_name_tx, grp)) < 0) { if (netif_msg_intr(priv)) printk(KERN_ERR "%s: Can't get IRQ %d\n", - dev->name, priv->interruptTransmit); - - err = -1; - + dev->name, grp->interruptTransmit); goto tx_irq_fail; } - if (request_irq(priv->interruptReceive, gfar_receive, - 0, priv->int_name_rx, dev) < 0) { + if ((err = request_irq(grp->interruptReceive, gfar_receive, 0, + grp->int_name_rx, grp)) < 0) { if (netif_msg_intr(priv)) - printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n", - dev->name, priv->interruptReceive); - - err = -1; + printk(KERN_ERR "%s: Can't get IRQ %d\n", + dev->name, grp->interruptReceive); goto rx_irq_fail; } } else { - if (request_irq(priv->interruptTransmit, gfar_interrupt, - 0, priv->int_name_tx, dev) < 0) { + if ((err = request_irq(grp->interruptTransmit, gfar_interrupt, 0, + grp->int_name_tx, grp)) < 0) { if (netif_msg_intr(priv)) printk(KERN_ERR "%s: Can't get IRQ %d\n", - dev->name, priv->interruptTransmit); - - err = -1; + dev->name, grp->interruptTransmit); goto err_irq_fail; } } - phy_start(priv->phydev); - - /* Configure the coalescing support */ - gfar_write(®s->txic, 0); - if (priv->txcoalescing) - gfar_write(®s->txic, priv->txic); - - gfar_write(®s->rxic, 0); - if (priv->rxcoalescing) - gfar_write(®s->rxic, priv->rxic); - - if (priv->rx_csum_enable) - rctrl |= RCTRL_CHECKSUMMING; + return 0; - if (priv->extended_hash) { - rctrl |= RCTRL_EXTHASH; +rx_irq_fail: + free_irq(grp->interruptTransmit, grp); +tx_irq_fail: + free_irq(grp->interruptError, grp); +err_irq_fail: + return err; - gfar_clear_exact_match(dev); - rctrl |= RCTRL_EMEN; - } +} - if (priv->padding) { - rctrl &= ~RCTRL_PAL_MASK; - rctrl |= RCTRL_PADDING(priv->padding); - } +/* Bring the controller up and running */ +int startup_gfar(struct net_device *ndev) +{ + struct gfar_private *priv = netdev_priv(ndev); + struct gfar __iomem *regs = NULL; + int err, i, j; - /* keep vlan related bits if it's enabled */ - if (priv->vlgrp) { - rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT; - tctrl |= TCTRL_VLINS; + for (i = 0; i < priv->num_grps; i++) { + regs= priv->gfargrp[i].regs; + gfar_write(®s->imask, IMASK_INIT_CLEAR); } - /* Init rctrl based on our settings */ - gfar_write(&priv->regs->rctrl, rctrl); - - if (dev->features & NETIF_F_IP_CSUM) - tctrl |= TCTRL_INIT_CSUM; - - gfar_write(&priv->regs->tctrl, tctrl); - - /* Set the extraction length and index */ - attrs = ATTRELI_EL(priv->rx_stash_size) | - ATTRELI_EI(priv->rx_stash_index); - - gfar_write(&priv->regs->attreli, attrs); - - /* Start with defaults, and add stashing or locking - * depending on the approprate variables */ - attrs = ATTR_INIT_SETTINGS; + regs= priv->gfargrp[0].regs; + err = gfar_alloc_skb_resources(ndev); + if (err) + return err; - if (priv->bd_stash_en) - attrs |= ATTR_BDSTASH; + gfar_init_mac(ndev); - if (priv->rx_stash_size != 0) - attrs |= ATTR_BUFSTASH; + for (i = 0; i < priv->num_grps; i++) { + err = register_grp_irqs(&priv->gfargrp[i]); + if (err) { + for (j = 0; j < i; j++) + free_grp_irqs(&priv->gfargrp[j]); + goto irq_fail; + } + } - gfar_write(&priv->regs->attr, attrs); + /* Start the controller */ + gfar_start(ndev); - gfar_write(&priv->regs->fifo_tx_thr, priv->fifo_threshold); - gfar_write(&priv->regs->fifo_tx_starve, priv->fifo_starve); - gfar_write(&priv->regs->fifo_tx_starve_shutoff, priv->fifo_starve_off); + phy_start(priv->phydev); - /* Start the controller */ - gfar_start(dev); + gfar_configure_coalescing(priv, 0xFF, 0xFF); return 0; -rx_irq_fail: - free_irq(priv->interruptTransmit, dev); -tx_irq_fail: - free_irq(priv->interruptError, dev); -err_irq_fail: -err_rxalloc_fail: -rx_skb_fail: +irq_fail: free_skb_resources(priv); -tx_skb_fail: - dma_free_coherent(&priv->ofdev->dev, - sizeof(struct txbd8)*priv->tx_ring_size - + sizeof(struct rxbd8)*priv->rx_ring_size, - priv->tx_bd_base, - gfar_read(®s->tbase0)); - return err; } @@ -1178,7 +1800,7 @@ static int gfar_enet_open(struct net_device *dev) struct gfar_private *priv = netdev_priv(dev); int err; - napi_enable(&priv->napi); + enable_napi(priv); skb_queue_head_init(&priv->rx_recycle); @@ -1189,18 +1811,18 @@ static int gfar_enet_open(struct net_device *dev) err = init_phy(dev); - if(err) { - napi_disable(&priv->napi); + if (err) { + disable_napi(priv); return err; } err = startup_gfar(dev); if (err) { - napi_disable(&priv->napi); + disable_napi(priv); return err; } - netif_start_queue(dev); + netif_tx_start_all_queues(dev); device_set_wakeup_enable(&dev->dev, priv->wol_en); @@ -1269,15 +1891,23 @@ static inline struct txbd8 *next_txbd(struct txbd8 *bdp, struct txbd8 *base, static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); + struct gfar_priv_tx_q *tx_queue = NULL; + struct netdev_queue *txq; + struct gfar __iomem *regs = NULL; struct txfcb *fcb = NULL; struct txbd8 *txbdp, *txbdp_start, *base; u32 lstatus; - int i; + int i, rq = 0; u32 bufaddr; unsigned long flags; unsigned int nr_frags, length; - base = priv->tx_bd_base; + + rq = skb->queue_mapping; + tx_queue = priv->tx_queue[rq]; + txq = netdev_get_tx_queue(dev, rq); + base = tx_queue->tx_bd_base; + regs = tx_queue->grp->regs; /* make space for additional header when fcb is needed */ if (((skb->ip_summed == CHECKSUM_PARTIAL) || @@ -1298,21 +1928,18 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* total number of fragments in the SKB */ nr_frags = skb_shinfo(skb)->nr_frags; - spin_lock_irqsave(&priv->txlock, flags); - /* check if there is space to queue this packet */ - if ((nr_frags+1) > priv->num_txbdfree) { + if ((nr_frags+1) > tx_queue->num_txbdfree) { /* no space, stop the queue */ - netif_stop_queue(dev); + netif_tx_stop_queue(txq); dev->stats.tx_fifo_errors++; - spin_unlock_irqrestore(&priv->txlock, flags); return NETDEV_TX_BUSY; } /* Update transmit stats */ dev->stats.tx_bytes += skb->len; - txbdp = txbdp_start = priv->cur_tx; + txbdp = txbdp_start = tx_queue->cur_tx; if (nr_frags == 0) { lstatus = txbdp->lstatus | BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT); @@ -1320,7 +1947,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Place the fragment addresses and lengths into the TxBDs */ for (i = 0; i < nr_frags; i++) { /* Point at the next BD, wrapping as needed */ - txbdp = next_txbd(txbdp, base, priv->tx_ring_size); + txbdp = next_txbd(txbdp, base, tx_queue->tx_ring_size); length = skb_shinfo(skb)->frags[i].size; @@ -1362,13 +1989,27 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) } /* setup the TxBD length and buffer pointer for the first BD */ - priv->tx_skbuff[priv->skb_curtx] = skb; + tx_queue->tx_skbuff[tx_queue->skb_curtx] = skb; txbdp_start->bufPtr = dma_map_single(&priv->ofdev->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb); /* + * We can work in parallel with gfar_clean_tx_ring(), except + * when modifying num_txbdfree. Note that we didn't grab the lock + * when we were reading the num_txbdfree and checking for available + * space, that's because outside of this function it can only grow, + * and once we've got needed space, it cannot suddenly disappear. + * + * The lock also protects us from gfar_error(), which can modify + * regs->tstat and thus retrigger the transfers, which is why we + * also must grab the lock before setting ready bit for the first + * to be transmitted BD. + */ + spin_lock_irqsave(&tx_queue->txlock, flags); + + /* * The powerpc-specific eieio() is used, as wmb() has too strong * semantics (it requires synchronization between cacheable and * uncacheable mappings, which eieio doesn't provide and which we @@ -1382,29 +2023,29 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Update the current skb pointer to the next entry we will use * (wrapping if necessary) */ - priv->skb_curtx = (priv->skb_curtx + 1) & - TX_RING_MOD_MASK(priv->tx_ring_size); + tx_queue->skb_curtx = (tx_queue->skb_curtx + 1) & + TX_RING_MOD_MASK(tx_queue->tx_ring_size); - priv->cur_tx = next_txbd(txbdp, base, priv->tx_ring_size); + tx_queue->cur_tx = next_txbd(txbdp, base, tx_queue->tx_ring_size); /* reduce TxBD free count */ - priv->num_txbdfree -= (nr_frags + 1); + tx_queue->num_txbdfree -= (nr_frags + 1); dev->trans_start = jiffies; /* If the next BD still needs to be cleaned up, then the bds are full. We need to tell the kernel to stop sending us stuff. */ - if (!priv->num_txbdfree) { - netif_stop_queue(dev); + if (!tx_queue->num_txbdfree) { + netif_tx_stop_queue(txq); dev->stats.tx_fifo_errors++; } /* Tell the DMA to go go go */ - gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); + gfar_write(®s->tstat, TSTAT_CLEAR_THALT >> tx_queue->qindex); /* Unlock priv */ - spin_unlock_irqrestore(&priv->txlock, flags); + spin_unlock_irqrestore(&tx_queue->txlock, flags); return NETDEV_TX_OK; } @@ -1414,7 +2055,7 @@ static int gfar_close(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - napi_disable(&priv->napi); + disable_napi(priv); skb_queue_purge(&priv->rx_recycle); cancel_work_sync(&priv->reset_task); @@ -1424,7 +2065,7 @@ static int gfar_close(struct net_device *dev) phy_disconnect(priv->phydev); priv->phydev = NULL; - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); return 0; } @@ -1443,50 +2084,55 @@ static void gfar_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) { struct gfar_private *priv = netdev_priv(dev); + struct gfar __iomem *regs = NULL; unsigned long flags; u32 tempval; - spin_lock_irqsave(&priv->rxlock, flags); + regs = priv->gfargrp[0].regs; + local_irq_save(flags); + lock_rx_qs(priv); priv->vlgrp = grp; if (grp) { /* Enable VLAN tag insertion */ - tempval = gfar_read(&priv->regs->tctrl); + tempval = gfar_read(®s->tctrl); tempval |= TCTRL_VLINS; - gfar_write(&priv->regs->tctrl, tempval); + gfar_write(®s->tctrl, tempval); /* Enable VLAN tag extraction */ - tempval = gfar_read(&priv->regs->rctrl); + tempval = gfar_read(®s->rctrl); tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT); - gfar_write(&priv->regs->rctrl, tempval); + gfar_write(®s->rctrl, tempval); } else { /* Disable VLAN tag insertion */ - tempval = gfar_read(&priv->regs->tctrl); + tempval = gfar_read(®s->tctrl); tempval &= ~TCTRL_VLINS; - gfar_write(&priv->regs->tctrl, tempval); + gfar_write(®s->tctrl, tempval); /* Disable VLAN tag extraction */ - tempval = gfar_read(&priv->regs->rctrl); + tempval = gfar_read(®s->rctrl); tempval &= ~RCTRL_VLEX; /* If parse is no longer required, then disable parser */ if (tempval & RCTRL_REQ_PARSER) tempval |= RCTRL_PRSDEP_INIT; else tempval &= ~RCTRL_PRSDEP_INIT; - gfar_write(&priv->regs->rctrl, tempval); + gfar_write(®s->rctrl, tempval); } gfar_change_mtu(dev, dev->mtu); - spin_unlock_irqrestore(&priv->rxlock, flags); + unlock_rx_qs(priv); + local_irq_restore(flags); } static int gfar_change_mtu(struct net_device *dev, int new_mtu) { int tempsize, tempval; struct gfar_private *priv = netdev_priv(dev); + struct gfar __iomem *regs = priv->gfargrp[0].regs; int oldsize = priv->rx_buffer_size; int frame_size = new_mtu + ETH_HLEN; @@ -1518,20 +2164,20 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu) dev->mtu = new_mtu; - gfar_write(&priv->regs->mrblr, priv->rx_buffer_size); - gfar_write(&priv->regs->maxfrm, priv->rx_buffer_size); + gfar_write(®s->mrblr, priv->rx_buffer_size); + gfar_write(®s->maxfrm, priv->rx_buffer_size); /* If the mtu is larger than the max size for standard * ethernet frames (ie, a jumbo frame), then set maccfg2 * to allow huge frames, and to check the length */ - tempval = gfar_read(&priv->regs->maccfg2); + tempval = gfar_read(®s->maccfg2); if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE) tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK); else tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK); - gfar_write(&priv->regs->maccfg2, tempval); + gfar_write(®s->maccfg2, tempval); if ((oldsize != tempsize) && (dev->flags & IFF_UP)) startup_gfar(dev); @@ -1551,10 +2197,10 @@ static void gfar_reset_task(struct work_struct *work) struct net_device *dev = priv->ndev; if (dev->flags & IFF_UP) { - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); stop_gfar(dev); startup_gfar(dev); - netif_start_queue(dev); + netif_tx_start_all_queues(dev); } netif_tx_schedule_all(dev); @@ -1569,24 +2215,29 @@ static void gfar_timeout(struct net_device *dev) } /* Interrupt Handler for Transmit complete */ -static int gfar_clean_tx_ring(struct net_device *dev) +static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) { + struct net_device *dev = tx_queue->dev; struct gfar_private *priv = netdev_priv(dev); + struct gfar_priv_rx_q *rx_queue = NULL; struct txbd8 *bdp; struct txbd8 *lbdp = NULL; - struct txbd8 *base = priv->tx_bd_base; + struct txbd8 *base = tx_queue->tx_bd_base; struct sk_buff *skb; int skb_dirtytx; - int tx_ring_size = priv->tx_ring_size; + int tx_ring_size = tx_queue->tx_ring_size; int frags = 0; int i; int howmany = 0; u32 lstatus; - bdp = priv->dirty_tx; - skb_dirtytx = priv->skb_dirtytx; + rx_queue = priv->rx_queue[tx_queue->qindex]; + bdp = tx_queue->dirty_tx; + skb_dirtytx = tx_queue->skb_dirtytx; + + while ((skb = tx_queue->tx_skbuff[skb_dirtytx])) { + unsigned long flags; - while ((skb = priv->tx_skbuff[skb_dirtytx])) { frags = skb_shinfo(skb)->nr_frags; lbdp = skip_txbd(bdp, frags, base, tx_ring_size); @@ -1618,82 +2269,73 @@ static int gfar_clean_tx_ring(struct net_device *dev) * If there's room in the queue (limit it to rx_buffer_size) * we add this skb back into the pool, if it's the right size */ - if (skb_queue_len(&priv->rx_recycle) < priv->rx_ring_size && + if (skb_queue_len(&priv->rx_recycle) < rx_queue->rx_ring_size && skb_recycle_check(skb, priv->rx_buffer_size + RXBUF_ALIGNMENT)) __skb_queue_head(&priv->rx_recycle, skb); else dev_kfree_skb_any(skb); - priv->tx_skbuff[skb_dirtytx] = NULL; + tx_queue->tx_skbuff[skb_dirtytx] = NULL; skb_dirtytx = (skb_dirtytx + 1) & TX_RING_MOD_MASK(tx_ring_size); howmany++; - priv->num_txbdfree += frags + 1; + spin_lock_irqsave(&tx_queue->txlock, flags); + tx_queue->num_txbdfree += frags + 1; + spin_unlock_irqrestore(&tx_queue->txlock, flags); } /* If we freed a buffer, we can restart transmission, if necessary */ - if (netif_queue_stopped(dev) && priv->num_txbdfree) - netif_wake_queue(dev); + if (__netif_subqueue_stopped(dev, tx_queue->qindex) && tx_queue->num_txbdfree) + netif_wake_subqueue(dev, tx_queue->qindex); /* Update dirty indicators */ - priv->skb_dirtytx = skb_dirtytx; - priv->dirty_tx = bdp; + tx_queue->skb_dirtytx = skb_dirtytx; + tx_queue->dirty_tx = bdp; dev->stats.tx_packets += howmany; return howmany; } -static void gfar_schedule_cleanup(struct net_device *dev) +static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp) { - struct gfar_private *priv = netdev_priv(dev); unsigned long flags; - spin_lock_irqsave(&priv->txlock, flags); - spin_lock(&priv->rxlock); - - if (napi_schedule_prep(&priv->napi)) { - gfar_write(&priv->regs->imask, IMASK_RTX_DISABLED); - __napi_schedule(&priv->napi); + spin_lock_irqsave(&gfargrp->grplock, flags); + if (napi_schedule_prep(&gfargrp->napi)) { + gfar_write(&gfargrp->regs->imask, IMASK_RTX_DISABLED); + __napi_schedule(&gfargrp->napi); } else { /* * Clear IEVENT, so interrupts aren't called again * because of the packets that have already arrived. */ - gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK); + gfar_write(&gfargrp->regs->ievent, IEVENT_RTX_MASK); } + spin_unlock_irqrestore(&gfargrp->grplock, flags); - spin_unlock(&priv->rxlock); - spin_unlock_irqrestore(&priv->txlock, flags); } /* Interrupt Handler for Transmit complete */ -static irqreturn_t gfar_transmit(int irq, void *dev_id) +static irqreturn_t gfar_transmit(int irq, void *grp_id) { - gfar_schedule_cleanup((struct net_device *)dev_id); + gfar_schedule_cleanup((struct gfar_priv_grp *)grp_id); return IRQ_HANDLED; } -static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp, +static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp, struct sk_buff *skb) { + struct net_device *dev = rx_queue->dev; struct gfar_private *priv = netdev_priv(dev); - u32 lstatus; - - bdp->bufPtr = dma_map_single(&priv->ofdev->dev, skb->data, - priv->rx_buffer_size, DMA_FROM_DEVICE); - - lstatus = BD_LFLAG(RXBD_EMPTY | RXBD_INTERRUPT); - - if (bdp == priv->rx_bd_base + priv->rx_ring_size - 1) - lstatus |= BD_LFLAG(RXBD_WRAP); - - eieio(); + dma_addr_t buf; - bdp->lstatus = lstatus; + buf = dma_map_single(&priv->ofdev->dev, skb->data, + priv->rx_buffer_size, DMA_FROM_DEVICE); + gfar_init_rxbdp(rx_queue, bdp, buf); } @@ -1760,9 +2402,9 @@ static inline void count_errors(unsigned short status, struct net_device *dev) } } -irqreturn_t gfar_receive(int irq, void *dev_id) +irqreturn_t gfar_receive(int irq, void *grp_id) { - gfar_schedule_cleanup((struct net_device *)dev_id); + gfar_schedule_cleanup((struct gfar_priv_grp *)grp_id); return IRQ_HANDLED; } @@ -1792,6 +2434,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, fcb = (struct rxfcb *)skb->data; /* Remove the FCB from the skb */ + skb_set_queue_mapping(skb, fcb->rq); /* Remove the padded bytes, if there are any */ if (amount_pull) skb_pull(skb, amount_pull); @@ -1818,8 +2461,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, * until the budget/quota has been reached. Returns the number * of frames handled */ -int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) +int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) { + struct net_device *dev = rx_queue->dev; struct rxbd8 *bdp, *base; struct sk_buff *skb; int pkt_len; @@ -1828,8 +2472,8 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) struct gfar_private *priv = netdev_priv(dev); /* Get the first full descriptor */ - bdp = priv->cur_rx; - base = priv->rx_bd_base; + bdp = rx_queue->cur_rx; + base = rx_queue->rx_bd_base; amount_pull = (gfar_uses_fcb(priv) ? GMAC_FCB_LEN : 0) + priv->padding; @@ -1841,7 +2485,7 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) /* Add another skb for the future */ newskb = gfar_new_skb(dev); - skb = priv->rx_skbuff[priv->skb_currx]; + skb = rx_queue->rx_skbuff[rx_queue->skb_currx]; dma_unmap_single(&priv->ofdev->dev, bdp->bufPtr, priv->rx_buffer_size, DMA_FROM_DEVICE); @@ -1875,8 +2519,6 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) skb_put(skb, pkt_len); dev->stats.rx_bytes += pkt_len; - if (in_irq() || irqs_disabled()) - printk("Interrupt problem!\n"); gfar_process_frame(dev, skb, amount_pull); } else { @@ -1889,46 +2531,70 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) } - priv->rx_skbuff[priv->skb_currx] = newskb; + rx_queue->rx_skbuff[rx_queue->skb_currx] = newskb; /* Setup the new bdp */ - gfar_new_rxbdp(dev, bdp, newskb); + gfar_new_rxbdp(rx_queue, bdp, newskb); /* Update to the next pointer */ - bdp = next_bd(bdp, base, priv->rx_ring_size); + bdp = next_bd(bdp, base, rx_queue->rx_ring_size); /* update to point at the next skb */ - priv->skb_currx = - (priv->skb_currx + 1) & - RX_RING_MOD_MASK(priv->rx_ring_size); + rx_queue->skb_currx = + (rx_queue->skb_currx + 1) & + RX_RING_MOD_MASK(rx_queue->rx_ring_size); } /* Update the current rxbd pointer to be the next one */ - priv->cur_rx = bdp; + rx_queue->cur_rx = bdp; return howmany; } static int gfar_poll(struct napi_struct *napi, int budget) { - struct gfar_private *priv = container_of(napi, struct gfar_private, napi); - struct net_device *dev = priv->ndev; - int tx_cleaned = 0; - int rx_cleaned = 0; - unsigned long flags; + struct gfar_priv_grp *gfargrp = container_of(napi, + struct gfar_priv_grp, napi); + struct gfar_private *priv = gfargrp->priv; + struct gfar __iomem *regs = gfargrp->regs; + struct gfar_priv_tx_q *tx_queue = NULL; + struct gfar_priv_rx_q *rx_queue = NULL; + int rx_cleaned = 0, budget_per_queue = 0, rx_cleaned_per_queue = 0; + int tx_cleaned = 0, i, left_over_budget = budget; + unsigned long serviced_queues = 0; + int num_queues = 0; + + num_queues = gfargrp->num_rx_queues; + budget_per_queue = budget/num_queues; /* Clear IEVENT, so interrupts aren't called again * because of the packets that have already arrived */ - gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK); - - /* If we fail to get the lock, don't bother with the TX BDs */ - if (spin_trylock_irqsave(&priv->txlock, flags)) { - tx_cleaned = gfar_clean_tx_ring(dev); - spin_unlock_irqrestore(&priv->txlock, flags); + gfar_write(®s->ievent, IEVENT_RTX_MASK); + + while (num_queues && left_over_budget) { + + budget_per_queue = left_over_budget/num_queues; + left_over_budget = 0; + + for_each_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) { + if (test_bit(i, &serviced_queues)) + continue; + rx_queue = priv->rx_queue[i]; + tx_queue = priv->tx_queue[rx_queue->qindex]; + + tx_cleaned += gfar_clean_tx_ring(tx_queue); + rx_cleaned_per_queue = gfar_clean_rx_ring(rx_queue, + budget_per_queue); + rx_cleaned += rx_cleaned_per_queue; + if(rx_cleaned_per_queue < budget_per_queue) { + left_over_budget = left_over_budget + + (budget_per_queue - rx_cleaned_per_queue); + set_bit(i, &serviced_queues); + num_queues--; + } + } } - rx_cleaned = gfar_clean_rx_ring(dev, budget); - if (tx_cleaned) return budget; @@ -1936,20 +2602,14 @@ static int gfar_poll(struct napi_struct *napi, int budget) napi_complete(napi); /* Clear the halt bit in RSTAT */ - gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT); + gfar_write(®s->rstat, gfargrp->rstat); - gfar_write(&priv->regs->imask, IMASK_DEFAULT); + gfar_write(®s->imask, IMASK_DEFAULT); /* If we are coalescing interrupts, update the timer */ /* Otherwise, clear it */ - if (likely(priv->rxcoalescing)) { - gfar_write(&priv->regs->rxic, 0); - gfar_write(&priv->regs->rxic, priv->rxic); - } - if (likely(priv->txcoalescing)) { - gfar_write(&priv->regs->txic, 0); - gfar_write(&priv->regs->txic, priv->txic); - } + gfar_configure_coalescing(priv, + gfargrp->rx_bit_map, gfargrp->tx_bit_map); } return rx_cleaned; @@ -1964,44 +2624,49 @@ static int gfar_poll(struct napi_struct *napi, int budget) static void gfar_netpoll(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); + int i = 0; /* If the device has multiple interrupts, run tx/rx */ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { - disable_irq(priv->interruptTransmit); - disable_irq(priv->interruptReceive); - disable_irq(priv->interruptError); - gfar_interrupt(priv->interruptTransmit, dev); - enable_irq(priv->interruptError); - enable_irq(priv->interruptReceive); - enable_irq(priv->interruptTransmit); + for (i = 0; i < priv->num_grps; i++) { + disable_irq(priv->gfargrp[i].interruptTransmit); + disable_irq(priv->gfargrp[i].interruptReceive); + disable_irq(priv->gfargrp[i].interruptError); + gfar_interrupt(priv->gfargrp[i].interruptTransmit, + &priv->gfargrp[i]); + enable_irq(priv->gfargrp[i].interruptError); + enable_irq(priv->gfargrp[i].interruptReceive); + enable_irq(priv->gfargrp[i].interruptTransmit); + } } else { - disable_irq(priv->interruptTransmit); - gfar_interrupt(priv->interruptTransmit, dev); - enable_irq(priv->interruptTransmit); + for (i = 0; i < priv->num_grps; i++) { + disable_irq(priv->gfargrp[i].interruptTransmit); + gfar_interrupt(priv->gfargrp[i].interruptTransmit, + &priv->gfargrp[i]); + enable_irq(priv->gfargrp[i].interruptTransmit); } } #endif /* The interrupt handler for devices with one interrupt */ -static irqreturn_t gfar_interrupt(int irq, void *dev_id) +static irqreturn_t gfar_interrupt(int irq, void *grp_id) { - struct net_device *dev = dev_id; - struct gfar_private *priv = netdev_priv(dev); + struct gfar_priv_grp *gfargrp = grp_id; /* Save ievent for future reference */ - u32 events = gfar_read(&priv->regs->ievent); + u32 events = gfar_read(&gfargrp->regs->ievent); /* Check for reception */ if (events & IEVENT_RX_MASK) - gfar_receive(irq, dev_id); + gfar_receive(irq, grp_id); /* Check for transmit completion */ if (events & IEVENT_TX_MASK) - gfar_transmit(irq, dev_id); + gfar_transmit(irq, grp_id); /* Check for errors */ if (events & IEVENT_ERR_MASK) - gfar_error(irq, dev_id); + gfar_error(irq, grp_id); return IRQ_HANDLED; } @@ -2015,12 +2680,14 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id) static void adjust_link(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - struct gfar __iomem *regs = priv->regs; + struct gfar __iomem *regs = priv->gfargrp[0].regs; unsigned long flags; struct phy_device *phydev = priv->phydev; int new_state = 0; - spin_lock_irqsave(&priv->txlock, flags); + local_irq_save(flags); + lock_tx_qs(priv); + if (phydev->link) { u32 tempval = gfar_read(®s->maccfg2); u32 ecntrl = gfar_read(®s->ecntrl); @@ -2085,8 +2752,8 @@ static void adjust_link(struct net_device *dev) if (new_state && netif_msg_link(priv)) phy_print_status(phydev); - - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_tx_qs(priv); + local_irq_restore(flags); } /* Update the hash table based on the current list of multicast @@ -2097,10 +2764,10 @@ static void gfar_set_multi(struct net_device *dev) { struct dev_mc_list *mc_ptr; struct gfar_private *priv = netdev_priv(dev); - struct gfar __iomem *regs = priv->regs; + struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; - if(dev->flags & IFF_PROMISC) { + if (dev->flags & IFF_PROMISC) { /* Set RCTRL to PROM */ tempval = gfar_read(®s->rctrl); tempval |= RCTRL_PROM; @@ -2112,7 +2779,7 @@ static void gfar_set_multi(struct net_device *dev) gfar_write(®s->rctrl, tempval); } - if(dev->flags & IFF_ALLMULTI) { + if (dev->flags & IFF_ALLMULTI) { /* Set the hash to rx all multicast frames */ gfar_write(®s->igaddr0, 0xffffffff); gfar_write(®s->igaddr1, 0xffffffff); @@ -2164,7 +2831,7 @@ static void gfar_set_multi(struct net_device *dev) em_num = 0; } - if(dev->mc_count == 0) + if (dev->mc_count == 0) return; /* Parse the list, and set the appropriate bits */ @@ -2230,10 +2897,11 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr) static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr) { struct gfar_private *priv = netdev_priv(dev); + struct gfar __iomem *regs = priv->gfargrp[0].regs; int idx; char tmpbuf[MAC_ADDR_LEN]; u32 tempval; - u32 __iomem *macptr = &priv->regs->macstnaddr1; + u32 __iomem *macptr = ®s->macstnaddr1; macptr += num*2; @@ -2250,16 +2918,18 @@ static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr) } /* GFAR error interrupt handler */ -static irqreturn_t gfar_error(int irq, void *dev_id) +static irqreturn_t gfar_error(int irq, void *grp_id) { - struct net_device *dev = dev_id; - struct gfar_private *priv = netdev_priv(dev); + struct gfar_priv_grp *gfargrp = grp_id; + struct gfar __iomem *regs = gfargrp->regs; + struct gfar_private *priv= gfargrp->priv; + struct net_device *dev = priv->ndev; /* Save ievent for future reference */ - u32 events = gfar_read(&priv->regs->ievent); + u32 events = gfar_read(®s->ievent); /* Clear IEVENT */ - gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK); + gfar_write(®s->ievent, events & IEVENT_ERR_MASK); /* Magic Packet is not an error. */ if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && @@ -2269,7 +2939,7 @@ static irqreturn_t gfar_error(int irq, void *dev_id) /* Hmm... */ if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n", - dev->name, events, gfar_read(&priv->regs->imask)); + dev->name, events, gfar_read(®s->imask)); /* Update the error counters */ if (events & IEVENT_TXE) { @@ -2280,14 +2950,22 @@ static irqreturn_t gfar_error(int irq, void *dev_id) if (events & IEVENT_CRL) dev->stats.tx_aborted_errors++; if (events & IEVENT_XFUN) { + unsigned long flags; + if (netif_msg_tx_err(priv)) printk(KERN_DEBUG "%s: TX FIFO underrun, " "packet dropped.\n", dev->name); dev->stats.tx_dropped++; priv->extra_stats.tx_underrun++; + local_irq_save(flags); + lock_tx_qs(priv); + /* Reactivate the Tx Queues */ - gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); + gfar_write(®s->tstat, gfargrp->tstat); + + unlock_tx_qs(priv); + local_irq_restore(flags); } if (netif_msg_tx_err(priv)) printk(KERN_DEBUG "%s: Transmit Error\n", dev->name); @@ -2296,11 +2974,11 @@ static irqreturn_t gfar_error(int irq, void *dev_id) dev->stats.rx_errors++; priv->extra_stats.rx_bsy++; - gfar_receive(irq, dev_id); + gfar_receive(irq, grp_id); if (netif_msg_rx_err(priv)) printk(KERN_DEBUG "%s: busy error (rstat: %x)\n", - dev->name, gfar_read(&priv->regs->rstat)); + dev->name, gfar_read(®s->rstat)); } if (events & IEVENT_BABR) { dev->stats.rx_errors++; @@ -2331,6 +3009,9 @@ static struct of_device_id gfar_match[] = .type = "network", .compatible = "gianfar", }, + { + .compatible = "fsl,etsec2", + }, {}, }; MODULE_DEVICE_TABLE(of, gfar_match); @@ -2342,8 +3023,9 @@ static struct of_platform_driver gfar_driver = { .probe = gfar_probe, .remove = gfar_remove, - .suspend = gfar_suspend, - .resume = gfar_resume, + .suspend = gfar_legacy_suspend, + .resume = gfar_legacy_resume, + .driver.pm = GFAR_PM_OPS, }; static int __init gfar_init(void) diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 2cd94338b5d3..cbb451011cb5 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -7,8 +7,9 @@ * * Author: Andy Fleming * Maintainer: Kumar Gala + * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com> * - * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. + * Copyright 2002-2009 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -74,6 +75,13 @@ extern const char gfar_driver_name[]; extern const char gfar_driver_version[]; +/* MAXIMUM NUMBER OF QUEUES SUPPORTED */ +#define MAX_TX_QS 0x8 +#define MAX_RX_QS 0x8 + +/* MAXIMUM NUMBER OF GROUPS SUPPORTED */ +#define MAXGROUPS 0x2 + /* These need to be powers of 2 for this driver */ #define DEFAULT_TX_RING_SIZE 256 #define DEFAULT_RX_RING_SIZE 256 @@ -171,12 +179,63 @@ extern const char gfar_driver_version[]; #define MINFLR_INIT_SETTINGS 0x00000040 +/* Tqueue control */ +#define TQUEUE_EN0 0x00008000 +#define TQUEUE_EN1 0x00004000 +#define TQUEUE_EN2 0x00002000 +#define TQUEUE_EN3 0x00001000 +#define TQUEUE_EN4 0x00000800 +#define TQUEUE_EN5 0x00000400 +#define TQUEUE_EN6 0x00000200 +#define TQUEUE_EN7 0x00000100 +#define TQUEUE_EN_ALL 0x0000FF00 + +#define TR03WT_WT0_MASK 0xFF000000 +#define TR03WT_WT1_MASK 0x00FF0000 +#define TR03WT_WT2_MASK 0x0000FF00 +#define TR03WT_WT3_MASK 0x000000FF + +#define TR47WT_WT4_MASK 0xFF000000 +#define TR47WT_WT5_MASK 0x00FF0000 +#define TR47WT_WT6_MASK 0x0000FF00 +#define TR47WT_WT7_MASK 0x000000FF + +/* Rqueue control */ +#define RQUEUE_EX0 0x00800000 +#define RQUEUE_EX1 0x00400000 +#define RQUEUE_EX2 0x00200000 +#define RQUEUE_EX3 0x00100000 +#define RQUEUE_EX4 0x00080000 +#define RQUEUE_EX5 0x00040000 +#define RQUEUE_EX6 0x00020000 +#define RQUEUE_EX7 0x00010000 +#define RQUEUE_EX_ALL 0x00FF0000 + +#define RQUEUE_EN0 0x00000080 +#define RQUEUE_EN1 0x00000040 +#define RQUEUE_EN2 0x00000020 +#define RQUEUE_EN3 0x00000010 +#define RQUEUE_EN4 0x00000008 +#define RQUEUE_EN5 0x00000004 +#define RQUEUE_EN6 0x00000002 +#define RQUEUE_EN7 0x00000001 +#define RQUEUE_EN_ALL 0x000000FF + /* Init to do tx snooping for buffers and descriptors */ #define DMACTRL_INIT_SETTINGS 0x000000c3 #define DMACTRL_GRS 0x00000010 #define DMACTRL_GTS 0x00000008 -#define TSTAT_CLEAR_THALT 0x80000000 +#define TSTAT_CLEAR_THALT_ALL 0xFF000000 +#define TSTAT_CLEAR_THALT 0x80000000 +#define TSTAT_CLEAR_THALT0 0x80000000 +#define TSTAT_CLEAR_THALT1 0x40000000 +#define TSTAT_CLEAR_THALT2 0x20000000 +#define TSTAT_CLEAR_THALT3 0x10000000 +#define TSTAT_CLEAR_THALT4 0x08000000 +#define TSTAT_CLEAR_THALT5 0x04000000 +#define TSTAT_CLEAR_THALT6 0x02000000 +#define TSTAT_CLEAR_THALT7 0x01000000 /* Interrupt coalescing macros */ #define IC_ICEN 0x80000000 @@ -227,6 +286,13 @@ extern const char gfar_driver_version[]; #define TCTRL_IPCSEN 0x00004000 #define TCTRL_TUCSEN 0x00002000 #define TCTRL_VLINS 0x00001000 +#define TCTRL_THDF 0x00000800 +#define TCTRL_RFCPAUSE 0x00000010 +#define TCTRL_TFCPAUSE 0x00000008 +#define TCTRL_TXSCHED_MASK 0x00000006 +#define TCTRL_TXSCHED_INIT 0x00000000 +#define TCTRL_TXSCHED_PRIO 0x00000002 +#define TCTRL_TXSCHED_WRRS 0x00000004 #define TCTRL_INIT_CSUM (TCTRL_TUCSEN | TCTRL_IPCSEN) #define IEVENT_INIT_CLEAR 0xffffffff @@ -315,6 +381,84 @@ extern const char gfar_driver_version[]; #define BD_LFLAG(flags) ((flags) << 16) #define BD_LENGTH_MASK 0x0000ffff +#define CLASS_CODE_UNRECOG 0x00 +#define CLASS_CODE_DUMMY1 0x01 +#define CLASS_CODE_ETHERTYPE1 0x02 +#define CLASS_CODE_ETHERTYPE2 0x03 +#define CLASS_CODE_USER_PROG1 0x04 +#define CLASS_CODE_USER_PROG2 0x05 +#define CLASS_CODE_USER_PROG3 0x06 +#define CLASS_CODE_USER_PROG4 0x07 +#define CLASS_CODE_TCP_IPV4 0x08 +#define CLASS_CODE_UDP_IPV4 0x09 +#define CLASS_CODE_AH_ESP_IPV4 0x0a +#define CLASS_CODE_SCTP_IPV4 0x0b +#define CLASS_CODE_TCP_IPV6 0x0c +#define CLASS_CODE_UDP_IPV6 0x0d +#define CLASS_CODE_AH_ESP_IPV6 0x0e +#define CLASS_CODE_SCTP_IPV6 0x0f + +#define FPR_FILER_MASK 0xFFFFFFFF +#define MAX_FILER_IDX 0xFF + +/* RQFCR register bits */ +#define RQFCR_GPI 0x80000000 +#define RQFCR_HASHTBL_Q 0x00000000 +#define RQFCR_HASHTBL_0 0x00020000 +#define RQFCR_HASHTBL_1 0x00040000 +#define RQFCR_HASHTBL_2 0x00060000 +#define RQFCR_HASHTBL_3 0x00080000 +#define RQFCR_HASH 0x00010000 +#define RQFCR_CLE 0x00000200 +#define RQFCR_RJE 0x00000100 +#define RQFCR_AND 0x00000080 +#define RQFCR_CMP_EXACT 0x00000000 +#define RQFCR_CMP_MATCH 0x00000020 +#define RQFCR_CMP_NOEXACT 0x00000040 +#define RQFCR_CMP_NOMATCH 0x00000060 + +/* RQFCR PID values */ +#define RQFCR_PID_MASK 0x00000000 +#define RQFCR_PID_PARSE 0x00000001 +#define RQFCR_PID_ARB 0x00000002 +#define RQFCR_PID_DAH 0x00000003 +#define RQFCR_PID_DAL 0x00000004 +#define RQFCR_PID_SAH 0x00000005 +#define RQFCR_PID_SAL 0x00000006 +#define RQFCR_PID_ETY 0x00000007 +#define RQFCR_PID_VID 0x00000008 +#define RQFCR_PID_PRI 0x00000009 +#define RQFCR_PID_TOS 0x0000000A +#define RQFCR_PID_L4P 0x0000000B +#define RQFCR_PID_DIA 0x0000000C +#define RQFCR_PID_SIA 0x0000000D +#define RQFCR_PID_DPT 0x0000000E +#define RQFCR_PID_SPT 0x0000000F + +/* RQFPR when PID is 0x0001 */ +#define RQFPR_HDR_GE_512 0x00200000 +#define RQFPR_LERR 0x00100000 +#define RQFPR_RAR 0x00080000 +#define RQFPR_RARQ 0x00040000 +#define RQFPR_AR 0x00020000 +#define RQFPR_ARQ 0x00010000 +#define RQFPR_EBC 0x00008000 +#define RQFPR_VLN 0x00004000 +#define RQFPR_CFI 0x00002000 +#define RQFPR_JUM 0x00001000 +#define RQFPR_IPF 0x00000800 +#define RQFPR_FIF 0x00000400 +#define RQFPR_IPV4 0x00000200 +#define RQFPR_IPV6 0x00000100 +#define RQFPR_ICC 0x00000080 +#define RQFPR_ICV 0x00000040 +#define RQFPR_TCP 0x00000020 +#define RQFPR_UDP 0x00000010 +#define RQFPR_TUC 0x00000008 +#define RQFPR_TUV 0x00000004 +#define RQFPR_PER 0x00000002 +#define RQFPR_EER 0x00000001 + /* TxBD status field bits */ #define TXBD_READY 0x8000 #define TXBD_PADCRC 0x4000 @@ -503,25 +647,32 @@ struct gfar_stats { struct gfar { u32 tsec_id; /* 0x.000 - Controller ID register */ - u8 res1[12]; + u32 tsec_id2; /* 0x.004 - Controller ID2 register */ + u8 res1[8]; u32 ievent; /* 0x.010 - Interrupt Event Register */ u32 imask; /* 0x.014 - Interrupt Mask Register */ u32 edis; /* 0x.018 - Error Disabled Register */ - u8 res2[4]; + u32 emapg; /* 0x.01c - Group Error mapping register */ u32 ecntrl; /* 0x.020 - Ethernet Control Register */ u32 minflr; /* 0x.024 - Minimum Frame Length Register */ u32 ptv; /* 0x.028 - Pause Time Value Register */ u32 dmactrl; /* 0x.02c - DMA Control Register */ u32 tbipa; /* 0x.030 - TBI PHY Address Register */ - u8 res3[88]; + u8 res2[28]; + u32 fifo_rx_pause; /* 0x.050 - FIFO receive pause start threshold + register */ + u32 fifo_rx_pause_shutoff; /* x.054 - FIFO receive starve shutoff + register */ + u32 fifo_rx_alarm; /* 0x.058 - FIFO receive alarm start threshold + register */ + u32 fifo_rx_alarm_shutoff; /*0x.05c - FIFO receive alarm starve + shutoff register */ + u8 res3[44]; u32 fifo_tx_thr; /* 0x.08c - FIFO transmit threshold register */ u8 res4[8]; u32 fifo_tx_starve; /* 0x.098 - FIFO transmit starve register */ u32 fifo_tx_starve_shutoff; /* 0x.09c - FIFO transmit starve shutoff register */ - u8 res5[4]; - u32 fifo_rx_pause; /* 0x.0a4 - FIFO receive pause threshold register */ - u32 fifo_rx_alarm; /* 0x.0a8 - FIFO receive alarm threshold register */ - u8 res6[84]; + u8 res5[96]; u32 tctrl; /* 0x.100 - Transmit Control Register */ u32 tstat; /* 0x.104 - Transmit Status Register */ u32 dfvlan; /* 0x.108 - Default VLAN Control word */ @@ -572,7 +723,11 @@ struct gfar { u8 res12[8]; u32 rxic; /* 0x.310 - Receive Interrupt Coalescing Configuration Register */ u32 rqueue; /* 0x.314 - Receive queue control register */ - u8 res13[24]; + u32 rir0; /* 0x.318 - Ring mapping register 0 */ + u32 rir1; /* 0x.31c - Ring mapping register 1 */ + u32 rir2; /* 0x.320 - Ring mapping register 2 */ + u32 rir3; /* 0x.324 - Ring mapping register 3 */ + u8 res13[8]; u32 rbifx; /* 0x.330 - Receive bit field extract control register */ u32 rqfar; /* 0x.334 - Receive queue filing table address register */ u32 rqfcr; /* 0x.338 - Receive queue filing table control register */ @@ -621,7 +776,7 @@ struct gfar { u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */ u8 res18[12]; u8 gfar_mii_regs[24]; /* See gianfar_phy.h */ - u8 res19[4]; + u32 ifctrl; /* 0x.538 - Interface control register */ u32 ifstat; /* 0x.53c - Interface Status Register */ u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */ u32 macstnaddr2; /* 0x.544 - Station Address Part 2 Register */ @@ -682,8 +837,30 @@ struct gfar { u8 res23c[248]; u32 attr; /* 0x.bf8 - Attributes Register */ u32 attreli; /* 0x.bfc - Attributes Extract Length and Extract Index Register */ - u8 res24[1024]; - + u8 res24[688]; + u32 isrg0; /* 0x.eb0 - Interrupt steering group 0 register */ + u32 isrg1; /* 0x.eb4 - Interrupt steering group 1 register */ + u32 isrg2; /* 0x.eb8 - Interrupt steering group 2 register */ + u32 isrg3; /* 0x.ebc - Interrupt steering group 3 register */ + u8 res25[16]; + u32 rxic0; /* 0x.ed0 - Ring 0 Rx interrupt coalescing */ + u32 rxic1; /* 0x.ed4 - Ring 1 Rx interrupt coalescing */ + u32 rxic2; /* 0x.ed8 - Ring 2 Rx interrupt coalescing */ + u32 rxic3; /* 0x.edc - Ring 3 Rx interrupt coalescing */ + u32 rxic4; /* 0x.ee0 - Ring 4 Rx interrupt coalescing */ + u32 rxic5; /* 0x.ee4 - Ring 5 Rx interrupt coalescing */ + u32 rxic6; /* 0x.ee8 - Ring 6 Rx interrupt coalescing */ + u32 rxic7; /* 0x.eec - Ring 7 Rx interrupt coalescing */ + u8 res26[32]; + u32 txic0; /* 0x.f10 - Ring 0 Tx interrupt coalescing */ + u32 txic1; /* 0x.f14 - Ring 1 Tx interrupt coalescing */ + u32 txic2; /* 0x.f18 - Ring 2 Tx interrupt coalescing */ + u32 txic3; /* 0x.f1c - Ring 3 Tx interrupt coalescing */ + u32 txic4; /* 0x.f20 - Ring 4 Tx interrupt coalescing */ + u32 txic5; /* 0x.f24 - Ring 5 Tx interrupt coalescing */ + u32 txic6; /* 0x.f28 - Ring 6 Tx interrupt coalescing */ + u32 txic7; /* 0x.f2c - Ring 7 Tx interrupt coalescing */ + u8 res27[208]; }; /* Flags related to gianfar device features */ @@ -699,6 +876,133 @@ struct gfar { #define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200 #define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400 +#if (MAXGROUPS == 2) +#define DEFAULT_MAPPING 0xAA +#else +#define DEFAULT_MAPPING 0xFF +#endif + +#define ISRG_SHIFT_TX 0x10 +#define ISRG_SHIFT_RX 0x18 + +/* The same driver can operate in two modes */ +/* SQ_SG_MODE: Single Queue Single Group Mode + * (Backward compatible mode) + * MQ_MG_MODE: Multi Queue Multi Group mode + */ +enum { + SQ_SG_MODE = 0, + MQ_MG_MODE +}; + +/** + * struct gfar_priv_tx_q - per tx queue structure + * @txlock: per queue tx spin lock + * @tx_skbuff:skb pointers + * @skb_curtx: to be used skb pointer + * @skb_dirtytx:the last used skb pointer + * @qindex: index of this queue + * @dev: back pointer to the dev structure + * @grp: back pointer to the group to which this queue belongs + * @tx_bd_base: First tx buffer descriptor + * @cur_tx: Next free ring entry + * @dirty_tx: First buffer in line to be transmitted + * @tx_ring_size: Tx ring size + * @num_txbdfree: number of free TxBds + * @txcoalescing: enable/disable tx coalescing + * @txic: transmit interrupt coalescing value + * @txcount: coalescing value if based on tx frame count + * @txtime: coalescing value if based on time + */ +struct gfar_priv_tx_q { + spinlock_t txlock __attribute__ ((aligned (SMP_CACHE_BYTES))); + struct sk_buff ** tx_skbuff; + /* Buffer descriptor pointers */ + dma_addr_t tx_bd_dma_base; + struct txbd8 *tx_bd_base; + struct txbd8 *cur_tx; + struct txbd8 *dirty_tx; + struct net_device *dev; + struct gfar_priv_grp *grp; + u16 skb_curtx; + u16 skb_dirtytx; + u16 qindex; + unsigned int tx_ring_size; + unsigned int num_txbdfree; + /* Configuration info for the coalescing features */ + unsigned char txcoalescing; + unsigned long txic; + unsigned short txcount; + unsigned short txtime; +}; + +/** + * struct gfar_priv_rx_q - per rx queue structure + * @rxlock: per queue rx spin lock + * @rx_skbuff: skb pointers + * @skb_currx: currently use skb pointer + * @rx_bd_base: First rx buffer descriptor + * @cur_rx: Next free rx ring entry + * @qindex: index of this queue + * @dev: back pointer to the dev structure + * @rx_ring_size: Rx ring size + * @rxcoalescing: enable/disable rx-coalescing + * @rxic: receive interrupt coalescing vlaue + */ + +struct gfar_priv_rx_q { + spinlock_t rxlock __attribute__ ((aligned (SMP_CACHE_BYTES))); + struct sk_buff ** rx_skbuff; + dma_addr_t rx_bd_dma_base; + struct rxbd8 *rx_bd_base; + struct rxbd8 *cur_rx; + struct net_device *dev; + struct gfar_priv_grp *grp; + u16 skb_currx; + u16 qindex; + unsigned int rx_ring_size; + /* RX Coalescing values */ + unsigned char rxcoalescing; + unsigned long rxic; +}; + +/** + * struct gfar_priv_grp - per group structure + * @napi: the napi poll function + * @priv: back pointer to the priv structure + * @regs: the ioremapped register space for this group + * @grp_id: group id for this group + * @interruptTransmit: The TX interrupt number for this group + * @interruptReceive: The RX interrupt number for this group + * @interruptError: The ERROR interrupt number for this group + * @int_name_tx: tx interrupt name for this group + * @int_name_rx: rx interrupt name for this group + * @int_name_er: er interrupt name for this group + */ + +struct gfar_priv_grp { + spinlock_t grplock __attribute__ ((aligned (SMP_CACHE_BYTES))); + struct napi_struct napi; + struct gfar_private *priv; + struct gfar __iomem *regs; + unsigned int grp_id; + unsigned long rx_bit_map; + unsigned long tx_bit_map; + unsigned long num_tx_queues; + unsigned long num_rx_queues; + unsigned int rstat; + unsigned int tstat; + unsigned int imask; + unsigned int ievent; + unsigned int interruptTransmit; + unsigned int interruptReceive; + unsigned int interruptError; + + char int_name_tx[GFAR_INT_NAME_MAX]; + char int_name_rx[GFAR_INT_NAME_MAX]; + char int_name_er[GFAR_INT_NAME_MAX]; +}; + /* Struct stolen almost completely (and shamelessly) from the FCC enet source * (Ok, that's not so true anymore, but there is a family resemblence) * The GFAR buffer descriptors track the ring buffers. The rx_bd_base @@ -709,62 +1013,36 @@ struct gfar { * the buffer descriptor determines the actual condition. */ struct gfar_private { - /* Fields controlled by TX lock */ - spinlock_t txlock; - /* Pointer to the array of skbuffs */ - struct sk_buff ** tx_skbuff; + /* Indicates how many tx, rx queues are enabled */ + unsigned int num_tx_queues; + unsigned int num_rx_queues; + unsigned int num_grps; + unsigned int mode; - /* next free skb in the array */ - u16 skb_curtx; - - /* First skb in line to be transmitted */ - u16 skb_dirtytx; - - /* Configuration info for the coalescing features */ - unsigned char txcoalescing; - unsigned long txic; - - /* Buffer descriptor pointers */ - struct txbd8 *tx_bd_base; /* First tx buffer descriptor */ - struct txbd8 *cur_tx; /* Next free ring entry */ - struct txbd8 *dirty_tx; /* First buffer in line - to be transmitted */ - unsigned int tx_ring_size; - unsigned int num_txbdfree; /* number of TxBDs free */ - - /* RX Locked fields */ - spinlock_t rxlock; + /* The total tx and rx ring size for the enabled queues */ + unsigned int total_tx_ring_size; + unsigned int total_rx_ring_size; struct device_node *node; struct net_device *ndev; struct of_device *ofdev; - struct napi_struct napi; - - /* skb array and index */ - struct sk_buff ** rx_skbuff; - u16 skb_currx; - - /* RX Coalescing values */ - unsigned char rxcoalescing; - unsigned long rxic; - struct rxbd8 *rx_bd_base; /* First Rx buffers */ - struct rxbd8 *cur_rx; /* Next free rx ring entry */ + struct gfar_priv_grp gfargrp[MAXGROUPS]; + struct gfar_priv_tx_q *tx_queue[MAX_TX_QS]; + struct gfar_priv_rx_q *rx_queue[MAX_RX_QS]; - /* RX parameters */ - unsigned int rx_ring_size; + /* RX per device parameters */ unsigned int rx_buffer_size; unsigned int rx_stash_size; unsigned int rx_stash_index; + u32 cur_filer_idx; + struct sk_buff_head rx_recycle; struct vlan_group *vlgrp; - /* Unprotected fields */ - /* Pointer to the GFAR memory mapped Registers */ - struct gfar __iomem *regs; /* Hash registers and their width */ u32 __iomem *hash_regs[16]; @@ -785,13 +1063,10 @@ struct gfar_private { unsigned char rx_csum_enable:1, extended_hash:1, bd_stash_en:1, + rx_filer_enable:1, wol_en:1; /* Wake-on-LAN enabled */ unsigned short padding; - unsigned int interruptTransmit; - unsigned int interruptReceive; - unsigned int interruptError; - /* PHY stuff */ struct phy_device *phydev; struct mii_bus *mii_bus; @@ -803,14 +1078,13 @@ struct gfar_private { struct work_struct reset_task; - char int_name_tx[GFAR_INT_NAME_MAX]; - char int_name_rx[GFAR_INT_NAME_MAX]; - char int_name_er[GFAR_INT_NAME_MAX]; - /* Network Statistics */ struct gfar_extra_stats extra_stats; }; +extern unsigned int ftp_rqfpr[MAX_FILER_IDX + 1]; +extern unsigned int ftp_rqfcr[MAX_FILER_IDX + 1]; + static inline u32 gfar_read(volatile unsigned __iomem *addr) { u32 val; @@ -823,12 +1097,28 @@ static inline void gfar_write(volatile unsigned __iomem *addr, u32 val) out_be32(addr, val); } +static inline void gfar_write_filer(struct gfar_private *priv, + unsigned int far, unsigned int fcr, unsigned int fpr) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + + gfar_write(®s->rqfar, far); + gfar_write(®s->rqfcr, fcr); + gfar_write(®s->rqfpr, fpr); +} + +extern void lock_rx_qs(struct gfar_private *priv); +extern void lock_tx_qs(struct gfar_private *priv); +extern void unlock_rx_qs(struct gfar_private *priv); +extern void unlock_tx_qs(struct gfar_private *priv); extern irqreturn_t gfar_receive(int irq, void *dev_id); extern int startup_gfar(struct net_device *dev); extern void stop_gfar(struct net_device *dev); extern void gfar_halt(struct net_device *dev); extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable, u32 regnum, u32 read); +extern void gfar_configure_coalescing(struct gfar_private *priv, + unsigned long tx_mask, unsigned long rx_mask); void gfar_init_sysfs(struct net_device *dev); extern const struct ethtool_ops gfar_ethtool_ops; diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 6c144b525b47..1010367695e4 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -7,8 +7,9 @@ * * Author: Andy Fleming * Maintainer: Kumar Gala + * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com> * - * Copyright (c) 2003,2004 Freescale Semiconductor, Inc. + * Copyright 2003-2006, 2008-2009 Freescale Semiconductor, Inc. * * This software may be used and distributed according to * the terms of the GNU Public License, Version 2, incorporated herein @@ -41,7 +42,7 @@ #include "gianfar.h" extern void gfar_start(struct net_device *dev); -extern int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit); +extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit); #define GFAR_MAX_COAL_USECS 0xffff #define GFAR_MAX_COAL_FRAMES 0xff @@ -136,10 +137,11 @@ static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, { int i; struct gfar_private *priv = netdev_priv(dev); + struct gfar __iomem *regs = priv->gfargrp[0].regs; u64 *extra = (u64 *) & priv->extra_stats; if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) { - u32 __iomem *rmon = (u32 __iomem *) & priv->regs->rmon; + u32 __iomem *rmon = (u32 __iomem *) ®s->rmon; struct gfar_stats *stats = (struct gfar_stats *) buf; for (i = 0; i < GFAR_RMON_LEN; i++) @@ -197,12 +199,18 @@ static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) { struct gfar_private *priv = netdev_priv(dev); struct phy_device *phydev = priv->phydev; + struct gfar_priv_rx_q *rx_queue = NULL; + struct gfar_priv_tx_q *tx_queue = NULL; if (NULL == phydev) return -ENODEV; + tx_queue = priv->tx_queue[0]; + rx_queue = priv->rx_queue[0]; - cmd->maxtxpkt = get_icft_value(priv->txic); - cmd->maxrxpkt = get_icft_value(priv->rxic); + /* etsec-1.7 and older versions have only one txic + * and rxic regs although they support multiple queues */ + cmd->maxtxpkt = get_icft_value(tx_queue->txic); + cmd->maxrxpkt = get_icft_value(rx_queue->rxic); return phy_ethtool_gset(phydev, cmd); } @@ -218,7 +226,7 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, voi { int i; struct gfar_private *priv = netdev_priv(dev); - u32 __iomem *theregs = (u32 __iomem *) priv->regs; + u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp[0].regs; u32 *buf = (u32 *) regbuf; for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++) @@ -279,6 +287,8 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) { struct gfar_private *priv = netdev_priv(dev); + struct gfar_priv_rx_q *rx_queue = NULL; + struct gfar_priv_tx_q *tx_queue = NULL; unsigned long rxtime; unsigned long rxcount; unsigned long txtime; @@ -290,10 +300,13 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals if (NULL == priv->phydev) return -ENODEV; - rxtime = get_ictt_value(priv->rxic); - rxcount = get_icft_value(priv->rxic); - txtime = get_ictt_value(priv->txic); - txcount = get_icft_value(priv->txic); + rx_queue = priv->rx_queue[0]; + tx_queue = priv->tx_queue[0]; + + rxtime = get_ictt_value(rx_queue->rxic); + rxcount = get_icft_value(rx_queue->rxic); + txtime = get_ictt_value(tx_queue->txic); + txcount = get_icft_value(tx_queue->txic); cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime); cvals->rx_max_coalesced_frames = rxcount; @@ -339,16 +352,23 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) { struct gfar_private *priv = netdev_priv(dev); + int i = 0; if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) return -EOPNOTSUPP; /* Set up rx coalescing */ + /* As of now, we will enable/disable coalescing for all + * queues together in case of eTSEC2, this will be modified + * along with the ethtool interface */ if ((cvals->rx_coalesce_usecs == 0) || - (cvals->rx_max_coalesced_frames == 0)) - priv->rxcoalescing = 0; - else - priv->rxcoalescing = 1; + (cvals->rx_max_coalesced_frames == 0)) { + for (i = 0; i < priv->num_rx_queues; i++) + priv->rx_queue[i]->rxcoalescing = 0; + } else { + for (i = 0; i < priv->num_rx_queues; i++) + priv->rx_queue[i]->rxcoalescing = 1; + } if (NULL == priv->phydev) return -ENODEV; @@ -366,15 +386,21 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals return -EINVAL; } - priv->rxic = mk_ic_value(cvals->rx_max_coalesced_frames, - gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs)); + for (i = 0; i < priv->num_rx_queues; i++) { + priv->rx_queue[i]->rxic = mk_ic_value( + cvals->rx_max_coalesced_frames, + gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs)); + } /* Set up tx coalescing */ if ((cvals->tx_coalesce_usecs == 0) || - (cvals->tx_max_coalesced_frames == 0)) - priv->txcoalescing = 0; - else - priv->txcoalescing = 1; + (cvals->tx_max_coalesced_frames == 0)) { + for (i = 0; i < priv->num_tx_queues; i++) + priv->tx_queue[i]->txcoalescing = 0; + } else { + for (i = 0; i < priv->num_tx_queues; i++) + priv->tx_queue[i]->txcoalescing = 1; + } /* Check the bounds of the values */ if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) { @@ -389,16 +415,13 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals return -EINVAL; } - priv->txic = mk_ic_value(cvals->tx_max_coalesced_frames, - gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs)); - - gfar_write(&priv->regs->rxic, 0); - if (priv->rxcoalescing) - gfar_write(&priv->regs->rxic, priv->rxic); + for (i = 0; i < priv->num_tx_queues; i++) { + priv->tx_queue[i]->txic = mk_ic_value( + cvals->tx_max_coalesced_frames, + gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs)); + } - gfar_write(&priv->regs->txic, 0); - if (priv->txcoalescing) - gfar_write(&priv->regs->txic, priv->txic); + gfar_configure_coalescing(priv, 0xFF, 0xFF); return 0; } @@ -409,6 +432,11 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals) { struct gfar_private *priv = netdev_priv(dev); + struct gfar_priv_tx_q *tx_queue = NULL; + struct gfar_priv_rx_q *rx_queue = NULL; + + tx_queue = priv->tx_queue[0]; + rx_queue = priv->rx_queue[0]; rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE; rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE; @@ -418,10 +446,10 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv /* Values changeable by the user. The valid values are * in the range 1 to the "*_max_pending" counterpart above. */ - rvals->rx_pending = priv->rx_ring_size; - rvals->rx_mini_pending = priv->rx_ring_size; - rvals->rx_jumbo_pending = priv->rx_ring_size; - rvals->tx_pending = priv->tx_ring_size; + rvals->rx_pending = rx_queue->rx_ring_size; + rvals->rx_mini_pending = rx_queue->rx_ring_size; + rvals->rx_jumbo_pending = rx_queue->rx_ring_size; + rvals->tx_pending = tx_queue->tx_ring_size; } /* Change the current ring parameters, stopping the controller if @@ -431,7 +459,7 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) { struct gfar_private *priv = netdev_priv(dev); - int err = 0; + int err = 0, i = 0; if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE) return -EINVAL; @@ -451,34 +479,41 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva return -EINVAL; } + if (dev->flags & IFF_UP) { unsigned long flags; /* Halt TX and RX, and process the frames which * have already been received */ - spin_lock_irqsave(&priv->txlock, flags); - spin_lock(&priv->rxlock); + local_irq_save(flags); + lock_tx_qs(priv); + lock_rx_qs(priv); gfar_halt(dev); - spin_unlock(&priv->rxlock); - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_rx_qs(priv); + unlock_tx_qs(priv); + local_irq_restore(flags); - gfar_clean_rx_ring(dev, priv->rx_ring_size); + for (i = 0; i < priv->num_rx_queues; i++) + gfar_clean_rx_ring(priv->rx_queue[i], + priv->rx_queue[i]->rx_ring_size); /* Now we take down the rings to rebuild them */ stop_gfar(dev); } /* Change the size */ - priv->rx_ring_size = rvals->rx_pending; - priv->tx_ring_size = rvals->tx_pending; - priv->num_txbdfree = priv->tx_ring_size; + for (i = 0; i < priv->num_rx_queues; i++) { + priv->rx_queue[i]->rx_ring_size = rvals->rx_pending; + priv->tx_queue[i]->tx_ring_size = rvals->tx_pending; + priv->tx_queue[i]->num_txbdfree = priv->tx_queue[i]->tx_ring_size; + } /* Rebuild the rings with the new size */ if (dev->flags & IFF_UP) { err = startup_gfar(dev); - netif_wake_queue(dev); + netif_tx_wake_all_queues(dev); } return err; } @@ -487,23 +522,28 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) { struct gfar_private *priv = netdev_priv(dev); unsigned long flags; - int err = 0; + int err = 0, i = 0; if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return -EOPNOTSUPP; + if (dev->flags & IFF_UP) { /* Halt TX and RX, and process the frames which * have already been received */ - spin_lock_irqsave(&priv->txlock, flags); - spin_lock(&priv->rxlock); + local_irq_save(flags); + lock_tx_qs(priv); + lock_rx_qs(priv); gfar_halt(dev); - spin_unlock(&priv->rxlock); - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_tx_qs(priv); + unlock_rx_qs(priv); + local_irq_save(flags); - gfar_clean_rx_ring(dev, priv->rx_ring_size); + for (i = 0; i < priv->num_rx_queues; i++) + gfar_clean_rx_ring(priv->rx_queue[i], + priv->rx_queue[i]->rx_ring_size); /* Now we take down the rings to rebuild them */ stop_gfar(dev); @@ -515,7 +555,7 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) if (dev->flags & IFF_UP) { err = startup_gfar(dev); - netif_wake_queue(dev); + netif_tx_wake_all_queues(dev); } return err; } @@ -605,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) } #endif +static int gfar_ethflow_to_class(int flow_type, u64 *class) +{ + switch (flow_type) { + case TCP_V4_FLOW: + *class = CLASS_CODE_TCP_IPV4; + break; + case UDP_V4_FLOW: + *class = CLASS_CODE_UDP_IPV4; + break; + case AH_V4_FLOW: + case ESP_V4_FLOW: + *class = CLASS_CODE_AH_ESP_IPV4; + break; + case SCTP_V4_FLOW: + *class = CLASS_CODE_SCTP_IPV4; + break; + case TCP_V6_FLOW: + *class = CLASS_CODE_TCP_IPV6; + break; + case UDP_V6_FLOW: + *class = CLASS_CODE_UDP_IPV6; + break; + case AH_V6_FLOW: + case ESP_V6_FLOW: + *class = CLASS_CODE_AH_ESP_IPV6; + break; + case SCTP_V6_FLOW: + *class = CLASS_CODE_SCTP_IPV6; + break; + default: + return 0; + } + + return 1; +} + +static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) +{ + u32 fcr = 0x0, fpr = FPR_FILER_MASK; + + if (ethflow & RXH_L2DA) { + fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH | + RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; + ftp_rqfpr[priv->cur_filer_idx] = fpr; + ftp_rqfcr[priv->cur_filer_idx] = fcr; + gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); + priv->cur_filer_idx = priv->cur_filer_idx - 1; + + fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH | + RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; + ftp_rqfpr[priv->cur_filer_idx] = fpr; + ftp_rqfcr[priv->cur_filer_idx] = fcr; + gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); + priv->cur_filer_idx = priv->cur_filer_idx - 1; + } + + if (ethflow & RXH_VLAN) { + fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH | + RQFCR_AND | RQFCR_HASHTBL_0; + gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); + ftp_rqfpr[priv->cur_filer_idx] = fpr; + ftp_rqfcr[priv->cur_filer_idx] = fcr; + priv->cur_filer_idx = priv->cur_filer_idx - 1; + } + + if (ethflow & RXH_IP_SRC) { + fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | + RQFCR_AND | RQFCR_HASHTBL_0; + ftp_rqfpr[priv->cur_filer_idx] = fpr; + ftp_rqfcr[priv->cur_filer_idx] = fcr; + gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); + priv->cur_filer_idx = priv->cur_filer_idx - 1; + } + + if (ethflow & (RXH_IP_DST)) { + fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | + RQFCR_AND | RQFCR_HASHTBL_0; + ftp_rqfpr[priv->cur_filer_idx] = fpr; + ftp_rqfcr[priv->cur_filer_idx] = fcr; + gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); + priv->cur_filer_idx = priv->cur_filer_idx - 1; + } + + if (ethflow & RXH_L3_PROTO) { + fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH | + RQFCR_AND | RQFCR_HASHTBL_0; + ftp_rqfpr[priv->cur_filer_idx] = fpr; + ftp_rqfcr[priv->cur_filer_idx] = fcr; + gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); + priv->cur_filer_idx = priv->cur_filer_idx - 1; + } + + if (ethflow & RXH_L4_B_0_1) { + fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | + RQFCR_AND | RQFCR_HASHTBL_0; + ftp_rqfpr[priv->cur_filer_idx] = fpr; + ftp_rqfcr[priv->cur_filer_idx] = fcr; + gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); + priv->cur_filer_idx = priv->cur_filer_idx - 1; + } + + if (ethflow & RXH_L4_B_2_3) { + fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | + RQFCR_AND | RQFCR_HASHTBL_0; + ftp_rqfpr[priv->cur_filer_idx] = fpr; + ftp_rqfcr[priv->cur_filer_idx] = fcr; + gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); + priv->cur_filer_idx = priv->cur_filer_idx - 1; + } +} + +static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class) +{ + unsigned int last_rule_idx = priv->cur_filer_idx; + unsigned int cmp_rqfpr; + unsigned int local_rqfpr[MAX_FILER_IDX + 1]; + unsigned int local_rqfcr[MAX_FILER_IDX + 1]; + int i = 0x0, k = 0x0; + int j = MAX_FILER_IDX, l = 0x0; + + switch (class) { + case TCP_V4_FLOW: + cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP; + break; + case UDP_V4_FLOW: + cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP; + break; + case TCP_V6_FLOW: + cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP; + break; + case UDP_V6_FLOW: + cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP; + break; + case IPV4_FLOW: + cmp_rqfpr = RQFPR_IPV4; + case IPV6_FLOW: + cmp_rqfpr = RQFPR_IPV6; + break; + default: + printk(KERN_ERR "Right now this class is not supported\n"); + return 0; + } + + for (i = 0; i < MAX_FILER_IDX + 1; i++) { + local_rqfpr[j] = ftp_rqfpr[i]; + local_rqfcr[j] = ftp_rqfcr[i]; + j--; + if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE | + RQFCR_CLE |RQFCR_AND)) && + (ftp_rqfpr[i] == cmp_rqfpr)) + break; + } + + if (i == MAX_FILER_IDX + 1) { + printk(KERN_ERR "No parse rule found, "); + printk(KERN_ERR "can't create hash rules\n"); + return 0; + } + + /* If a match was found, then it begins the starting of a cluster rule + * if it was already programmed, we need to overwrite these rules + */ + for (l = i+1; l < MAX_FILER_IDX; l++) { + if ((ftp_rqfcr[l] & RQFCR_CLE) && + !(ftp_rqfcr[l] & RQFCR_AND)) { + ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT | + RQFCR_HASHTBL_0 | RQFCR_PID_MASK; + ftp_rqfpr[l] = FPR_FILER_MASK; + gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]); + break; + } + + if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND)) + continue; + else { + local_rqfpr[j] = ftp_rqfpr[l]; + local_rqfcr[j] = ftp_rqfcr[l]; + j--; + } + } + + priv->cur_filer_idx = l - 1; + last_rule_idx = l; + + /* hash rules */ + ethflow_to_filer_rules(priv, ethflow); + + /* Write back the popped out rules again */ + for (k = j+1; k < MAX_FILER_IDX; k++) { + ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k]; + ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k]; + gfar_write_filer(priv, priv->cur_filer_idx, + local_rqfcr[k], local_rqfpr[k]); + if (!priv->cur_filer_idx) + break; + priv->cur_filer_idx = priv->cur_filer_idx - 1; + } + + return 1; +} + +static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd) +{ + u64 class; + + if (!gfar_ethflow_to_class(cmd->flow_type, &class)) + return -EINVAL; + + if (class < CLASS_CODE_USER_PROG1 || + class > CLASS_CODE_SCTP_IPV6) + return -EINVAL; + + /* write the filer rules here */ + if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type)) + return -1; + + return 0; +} + +static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + struct gfar_private *priv = netdev_priv(dev); + int ret = 0; + + switch(cmd->cmd) { + case ETHTOOL_SRXFH: + ret = gfar_set_hash_opts(priv, cmd); + break; + default: + ret = -EINVAL; + } + + return ret; +} + const struct ethtool_ops gfar_ethtool_ops = { .get_settings = gfar_gsettings, .set_settings = gfar_ssettings, @@ -630,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = { .get_wol = gfar_get_wol, .set_wol = gfar_set_wol, #endif + .set_rxnfc = gfar_set_nfc, }; diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c index dd26da74f27a..b98c6c512299 100644 --- a/drivers/net/gianfar_sysfs.c +++ b/drivers/net/gianfar_sysfs.c @@ -8,8 +8,9 @@ * * Author: Andy Fleming * Maintainer: Kumar Gala (galak@kernel.crashing.org) + * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com> * - * Copyright (c) 2002-2005 Freescale Semiconductor, Inc. + * Copyright 2002-2009 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -49,6 +50,7 @@ static ssize_t gfar_set_bd_stash(struct device *dev, const char *buf, size_t count) { struct gfar_private *priv = netdev_priv(to_net_dev(dev)); + struct gfar __iomem *regs = priv->gfargrp[0].regs; int new_setting = 0; u32 temp; unsigned long flags; @@ -56,30 +58,34 @@ static ssize_t gfar_set_bd_stash(struct device *dev, if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BD_STASHING)) return count; + /* Find out the new setting */ if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1)) new_setting = 1; - else if (!strncmp("off", buf, count - 1) - || !strncmp("0", buf, count - 1)) + else if (!strncmp("off", buf, count - 1) || + !strncmp("0", buf, count - 1)) new_setting = 0; else return count; - spin_lock_irqsave(&priv->rxlock, flags); + + local_irq_save(flags); + lock_rx_qs(priv); /* Set the new stashing value */ priv->bd_stash_en = new_setting; - temp = gfar_read(&priv->regs->attr); + temp = gfar_read(®s->attr); if (new_setting) temp |= ATTR_BDSTASH; else temp &= ~(ATTR_BDSTASH); - gfar_write(&priv->regs->attr, temp); + gfar_write(®s->attr, temp); - spin_unlock_irqrestore(&priv->rxlock, flags); + unlock_rx_qs(priv); + local_irq_restore(flags); return count; } @@ -99,6 +105,7 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev, const char *buf, size_t count) { struct gfar_private *priv = netdev_priv(to_net_dev(dev)); + struct gfar __iomem *regs = priv->gfargrp[0].regs; unsigned int length = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -106,7 +113,9 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev, if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING)) return count; - spin_lock_irqsave(&priv->rxlock, flags); + local_irq_save(flags); + lock_rx_qs(priv); + if (length > priv->rx_buffer_size) goto out; @@ -115,23 +124,24 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev, priv->rx_stash_size = length; - temp = gfar_read(&priv->regs->attreli); + temp = gfar_read(®s->attreli); temp &= ~ATTRELI_EL_MASK; temp |= ATTRELI_EL(length); - gfar_write(&priv->regs->attreli, temp); + gfar_write(®s->attreli, temp); /* Turn stashing on/off as appropriate */ - temp = gfar_read(&priv->regs->attr); + temp = gfar_read(®s->attr); if (length) temp |= ATTR_BUFSTASH; else temp &= ~(ATTR_BUFSTASH); - gfar_write(&priv->regs->attr, temp); + gfar_write(®s->attr, temp); out: - spin_unlock_irqrestore(&priv->rxlock, flags); + unlock_rx_qs(priv); + local_irq_restore(flags); return count; } @@ -154,6 +164,7 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev, const char *buf, size_t count) { struct gfar_private *priv = netdev_priv(to_net_dev(dev)); + struct gfar __iomem *regs = priv->gfargrp[0].regs; unsigned short index = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -161,7 +172,9 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev, if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING)) return count; - spin_lock_irqsave(&priv->rxlock, flags); + local_irq_save(flags); + lock_rx_qs(priv); + if (index > priv->rx_stash_size) goto out; @@ -170,13 +183,14 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev, priv->rx_stash_index = index; - temp = gfar_read(&priv->regs->attreli); + temp = gfar_read(®s->attreli); temp &= ~ATTRELI_EI_MASK; temp |= ATTRELI_EI(index); - gfar_write(&priv->regs->attreli, flags); + gfar_write(®s->attreli, temp); out: - spin_unlock_irqrestore(&priv->rxlock, flags); + unlock_rx_qs(priv); + local_irq_restore(flags); return count; } @@ -198,6 +212,7 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev, const char *buf, size_t count) { struct gfar_private *priv = netdev_priv(to_net_dev(dev)); + struct gfar __iomem *regs = priv->gfargrp[0].regs; unsigned int length = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -205,16 +220,18 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev, if (length > GFAR_MAX_FIFO_THRESHOLD) return count; - spin_lock_irqsave(&priv->txlock, flags); + local_irq_save(flags); + lock_tx_qs(priv); priv->fifo_threshold = length; - temp = gfar_read(&priv->regs->fifo_tx_thr); + temp = gfar_read(®s->fifo_tx_thr); temp &= ~FIFO_TX_THR_MASK; temp |= length; - gfar_write(&priv->regs->fifo_tx_thr, temp); + gfar_write(®s->fifo_tx_thr, temp); - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_tx_qs(priv); + local_irq_restore(flags); return count; } @@ -235,6 +252,7 @@ static ssize_t gfar_set_fifo_starve(struct device *dev, const char *buf, size_t count) { struct gfar_private *priv = netdev_priv(to_net_dev(dev)); + struct gfar __iomem *regs = priv->gfargrp[0].regs; unsigned int num = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -242,16 +260,18 @@ static ssize_t gfar_set_fifo_starve(struct device *dev, if (num > GFAR_MAX_FIFO_STARVE) return count; - spin_lock_irqsave(&priv->txlock, flags); + local_irq_save(flags); + lock_tx_qs(priv); priv->fifo_starve = num; - temp = gfar_read(&priv->regs->fifo_tx_starve); + temp = gfar_read(®s->fifo_tx_starve); temp &= ~FIFO_TX_STARVE_MASK; temp |= num; - gfar_write(&priv->regs->fifo_tx_starve, temp); + gfar_write(®s->fifo_tx_starve, temp); - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_tx_qs(priv); + local_irq_restore(flags); return count; } @@ -273,6 +293,7 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev, const char *buf, size_t count) { struct gfar_private *priv = netdev_priv(to_net_dev(dev)); + struct gfar __iomem *regs = priv->gfargrp[0].regs; unsigned int num = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -280,16 +301,18 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev, if (num > GFAR_MAX_FIFO_STARVE_OFF) return count; - spin_lock_irqsave(&priv->txlock, flags); + local_irq_save(flags); + lock_tx_qs(priv); priv->fifo_starve_off = num; - temp = gfar_read(&priv->regs->fifo_tx_starve_shutoff); + temp = gfar_read(®s->fifo_tx_starve_shutoff); temp &= ~FIFO_TX_STARVE_OFF_MASK; temp |= num; - gfar_write(&priv->regs->fifo_tx_starve_shutoff, temp); + gfar_write(®s->fifo_tx_starve_shutoff, temp); - spin_unlock_irqrestore(&priv->txlock, flags); + unlock_tx_qs(priv); + local_irq_restore(flags); return count; } diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index f7519a594945..ea85075a89a2 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -407,10 +407,9 @@ that case. /* A few values that may be tweaked. */ /* Size of each temporary Rx buffer, calculated as: * 1518 bytes (ethernet packet) + 2 bytes (to get 8 byte alignment for - * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum + - * 2 more because we use skb_reserve. + * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum */ -#define PKT_BUF_SZ 1538 +#define PKT_BUF_SZ 1536 /* For now, this is going to be set to the maximum size of an ethernet * packet. Eventually, we may want to make it a variable that is @@ -873,7 +872,7 @@ static int hamachi_open(struct net_device *dev) u32 rx_int_var, tx_int_var; u16 fifo_info; - i = request_irq(dev->irq, &hamachi_interrupt, IRQF_SHARED, dev->name, dev); + i = request_irq(dev->irq, hamachi_interrupt, IRQF_SHARED, dev->name, dev); if (i) return i; @@ -1152,12 +1151,13 @@ static void hamachi_tx_timeout(struct net_device *dev) } /* Fill in the Rx buffers. Handle allocation failure gracefully. */ for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = netdev_alloc_skb(dev, hmp->rx_buf_sz); + struct sk_buff *skb; + + skb = netdev_alloc_skb_ip_align(dev, hmp->rx_buf_sz); hmp->rx_skbuff[i] = skb; if (skb == NULL) break; - skb_reserve(skb, 2); /* 16 byte align the IP header. */ hmp->rx_ring[i].addr = cpu_to_leXX(pci_map_single(hmp->pci_dev, skb->data, hmp->rx_buf_sz, PCI_DMA_FROMDEVICE)); hmp->rx_ring[i].status_n_length = cpu_to_le32(DescOwn | @@ -1196,7 +1196,7 @@ static void hamachi_init_ring(struct net_device *dev) * card. -KDU */ hmp->rx_buf_sz = (dev->mtu <= 1492 ? PKT_BUF_SZ : - (((dev->mtu+26+7) & ~7) + 2 + 16)); + (((dev->mtu+26+7) & ~7) + 16)); /* Initialize all Rx descriptors. */ for (i = 0; i < RX_RING_SIZE; i++) { @@ -1566,8 +1566,8 @@ static int hamachi_rx(struct net_device *dev) #endif /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + if (pkt_len < rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { #ifdef RX_CHECKSUM printk(KERN_ERR "%s: rx_copybreak non-zero " "not good with RX_CHECKSUM\n", dev->name); @@ -1722,10 +1722,10 @@ static void hamachi_error(struct net_device *dev, int intr_status) readl(ioaddr + 0x370); readl(ioaddr + 0x3F0); } - if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone)) - && hamachi_debug) + if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone)) && + hamachi_debug) printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", - dev->name, intr_status); + dev->name, intr_status); /* Hmmmmm, it's not clear how to recover from PCI faults. */ if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) hmp->stats.tx_fifo_errors++; diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index fb588301a05d..689b9bd377a5 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -34,6 +34,7 @@ #include <linux/ip.h> #include <linux/tcp.h> #include <linux/semaphore.h> +#include <linux/compat.h> #include <asm/atomic.h> #define SIXPACK_VERSION "Revision: 0.3.0" @@ -777,6 +778,23 @@ static int sixpack_ioctl(struct tty_struct *tty, struct file *file, return err; } +#ifdef CONFIG_COMPAT +static long sixpack_compat_ioctl(struct tty_struct * tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCGIFNAME: + case SIOCGIFENCAP: + case SIOCSIFENCAP: + case SIOCSIFHWADDR: + return sixpack_ioctl(tty, file, cmd, + (unsigned long)compat_ptr(arg)); + } + + return -ENOIOCTLCMD; +} +#endif + static struct tty_ldisc_ops sp_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, @@ -784,6 +802,9 @@ static struct tty_ldisc_ops sp_ldisc = { .open = sixpack_open, .close = sixpack_close, .ioctl = sixpack_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sixpack_compat_ioctl, +#endif .receive_buf = sixpack_receive_buf, .write_wakeup = sixpack_write_wakeup, }; diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index e344c84c0ef9..a3c0dc9d8b98 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -596,16 +596,16 @@ static int receive(struct net_device *dev, int cnt) if (!(notbitstream & (0x1fc << j))) state = 0; - /* not flag received */ - else if (!(bitstream & (0x1fe << j)) != (0x0fc << j)) { + /* flag received */ + else if ((bitstream & (0x1fe << j)) == (0x0fc << j)) { if (state) do_rxpacket(dev); bc->hdlcrx.bufcnt = 0; bc->hdlcrx.bufptr = bc->hdlcrx.buf; state = 1; numbits = 7-j; - } } + } /* stuffed bit */ else if (unlikely((bitstream & (0x1f8 << j)) == (0xf8 << j))) { diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index ed60fd664273..0cab992b3d1a 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -35,7 +35,7 @@ * driver only supports standard serial hardware (8250, 16450, 16550A) * * This modem usually draws its supply current out of the otherwise unused - * TXD pin of the serial port. Thus a contignuous stream of 0x00-bytes + * TXD pin of the serial port. Thus a contiguous stream of 0x00-bytes * is transmitted to achieve a positive supply voltage. * * hsk: This is a 4800 baud FSK modem, designed for TNC use. It works fine diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index fe893c91a01b..ae5f11c8fc13 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -167,10 +167,7 @@ static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev) static inline int dev_is_ethdev(struct net_device *dev) { - return ( - dev->type == ARPHRD_ETHER - && strncmp(dev->name, "dummy", 5) - ); + return (dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5)); } /* ------------------------------------------------------------------------ */ @@ -186,7 +183,7 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty struct ethhdr *eth; struct bpqdev *bpq; - if (dev_net(dev) != &init_net) + if (!net_eq(dev_net(dev), &init_net)) goto drop; if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) @@ -552,7 +549,7 @@ static int bpq_device_event(struct notifier_block *this,unsigned long event, voi { struct net_device *dev = (struct net_device *)ptr; - if (dev_net(dev) != &init_net) + if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; if (!dev_is_ethdev(dev)) diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 950f3bb21f9d..9ee76b42668f 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -331,8 +331,8 @@ static int __init dmascc_init(void) for (i = 0; i < MAX_NUM_DEVS && io[i]; i++) { j = (io[i] - hw[h].io_region) / hw[h].io_delta; - if (j >= 0 && j < hw[h].num_devs - && hw[h].io_region + + if (j >= 0 && j < hw[h].num_devs && + hw[h].io_region + j * hw[h].io_delta == io[i]) { base[j] = io[i]; } @@ -396,8 +396,8 @@ static int __init dmascc_init(void) t_val = inb(t1[i]) + (inb(t1[i]) << 8); /* Also check whether counter did wrap */ - if (t_val == 0 - || t_val > TMR_0_HZ / HZ * 10) + if (t_val == 0 || + t_val > TMR_0_HZ / HZ * 10) counting[i] = 0; delay[i] = jiffies - start[i]; } diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index db4b7f1603f6..7db0a1c3216c 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -36,6 +36,7 @@ #include <linux/skbuff.h> #include <linux/if_arp.h> #include <linux/jiffies.h> +#include <linux/compat.h> #include <net/ax25.h> @@ -898,6 +899,23 @@ static int mkiss_ioctl(struct tty_struct *tty, struct file *file, return err; } +#ifdef CONFIG_COMPAT +static long mkiss_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCGIFNAME: + case SIOCGIFENCAP: + case SIOCSIFENCAP: + case SIOCSIFHWADDR: + return mkiss_ioctl(tty, file, cmd, + (unsigned long)compat_ptr(arg)); + } + + return -ENOIOCTLCMD; +} +#endif + /* * Handle the 'receiver data ready' interrupt. * This function is called by the 'tty_io' module in the kernel when @@ -972,6 +990,9 @@ static struct tty_ldisc_ops ax_ldisc = { .open = mkiss_open, .close = mkiss_close, .ioctl = mkiss_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mkiss_compat_ioctl, +#endif .receive_buf = mkiss_receive_buf, .write_wakeup = mkiss_write_wakeup }; diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c index 0486cbe01adb..efdbcad63c67 100644 --- a/drivers/net/hp-plus.c +++ b/drivers/net/hp-plus.c @@ -187,8 +187,8 @@ static int __init hpp_probe1(struct net_device *dev, int ioaddr) return -EBUSY; /* Check for the HP+ signature, 50 48 0x 53. */ - if (inw(ioaddr + HP_ID) != 0x4850 - || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) { + if (inw(ioaddr + HP_ID) != 0x4850 || + (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) { retval = -ENODEV; goto out; } diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index dd8665138062..90f890e7c5e1 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -993,8 +993,8 @@ static void hp100_mmuinit(struct net_device *dev) if (lp->mode == 1) { /* only needed for Busmaster */ int xmit_stop, recv_stop; - if ((lp->chip == HP100_CHIPID_RAINIER) - || (lp->chip == HP100_CHIPID_SHASTA)) { + if ((lp->chip == HP100_CHIPID_RAINIER) || + (lp->chip == HP100_CHIPID_SHASTA)) { int pdl_stop; /* diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index 3fae87559791..fb5e019169ee 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -1976,27 +1976,27 @@ static int emac_ethtool_set_settings(struct net_device *ndev, if (cmd->autoneg == AUTONEG_DISABLE) { switch (cmd->speed) { case SPEED_10: - if (cmd->duplex == DUPLEX_HALF - && !(f & SUPPORTED_10baseT_Half)) + if (cmd->duplex == DUPLEX_HALF && + !(f & SUPPORTED_10baseT_Half)) return -EINVAL; - if (cmd->duplex == DUPLEX_FULL - && !(f & SUPPORTED_10baseT_Full)) + if (cmd->duplex == DUPLEX_FULL && + !(f & SUPPORTED_10baseT_Full)) return -EINVAL; break; case SPEED_100: - if (cmd->duplex == DUPLEX_HALF - && !(f & SUPPORTED_100baseT_Half)) + if (cmd->duplex == DUPLEX_HALF && + !(f & SUPPORTED_100baseT_Half)) return -EINVAL; - if (cmd->duplex == DUPLEX_FULL - && !(f & SUPPORTED_100baseT_Full)) + if (cmd->duplex == DUPLEX_FULL && + !(f & SUPPORTED_100baseT_Full)) return -EINVAL; break; case SPEED_1000: - if (cmd->duplex == DUPLEX_HALF - && !(f & SUPPORTED_1000baseT_Half)) + if (cmd->duplex == DUPLEX_HALF && + !(f & SUPPORTED_1000baseT_Half)) return -EINVAL; - if (cmd->duplex == DUPLEX_FULL - && !(f & SUPPORTED_1000baseT_Full)) + if (cmd->duplex == DUPLEX_FULL && + !(f & SUPPORTED_1000baseT_Full)) return -EINVAL; break; default: @@ -2149,9 +2149,12 @@ static int emac_ethtool_nway_reset(struct net_device *ndev) return res; } -static int emac_ethtool_get_stats_count(struct net_device *ndev) +static int emac_ethtool_get_sset_count(struct net_device *ndev, int stringset) { - return EMAC_ETHTOOL_STATS_COUNT; + if (stringset == ETH_SS_STATS) + return EMAC_ETHTOOL_STATS_COUNT; + else + return -EINVAL; } static void emac_ethtool_get_strings(struct net_device *ndev, u32 stringset, @@ -2182,7 +2185,6 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev, info->fw_version[0] = '\0'; sprintf(info->bus_info, "PPC 4xx EMAC-%d %s", dev->cell_index, dev->ofdev->node->full_name); - info->n_stats = emac_ethtool_get_stats_count(ndev); info->regdump_len = emac_ethtool_get_regs_len(ndev); } @@ -2202,7 +2204,7 @@ static const struct ethtool_ops emac_ethtool_ops = { .get_rx_csum = emac_ethtool_get_rx_csum, .get_strings = emac_ethtool_get_strings, - .get_stats_count = emac_ethtool_get_stats_count, + .get_sset_count = emac_ethtool_get_sset_count, .get_ethtool_stats = emac_ethtool_get_ethtool_stats, .get_link = ethtool_op_get_link, diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 5862282ab2fe..a86693906ac8 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -625,7 +625,7 @@ static int ibmveth_open(struct net_device *netdev) } ibmveth_debug_printk("registering irq 0x%x\n", netdev->irq); - if((rc = request_irq(netdev->irq, &ibmveth_interrupt, 0, netdev->name, netdev)) != 0) { + if((rc = request_irq(netdev->irq, ibmveth_interrupt, 0, netdev->name, netdev)) != 0) { ibmveth_error_printk("unable to request irq 0x%x, rc %d\n", netdev->irq, rc); do { rc = h_free_logical_lan(adapter->vdev->unit_address); diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 030913f8bd26..f4081c0a2d9c 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -98,14 +98,16 @@ static void ri_tasklet(unsigned long dev) stats->tx_packets++; stats->tx_bytes +=skb->len; - skb->dev = dev_get_by_index(&init_net, skb->iif); + rcu_read_lock(); + skb->dev = dev_get_by_index_rcu(&init_net, skb->skb_iif); if (!skb->dev) { + rcu_read_unlock(); dev_kfree_skb(skb); stats->tx_dropped++; break; } - dev_put(skb->dev); - skb->iif = _dev->ifindex; + rcu_read_unlock(); + skb->skb_iif = _dev->ifindex; if (from & AT_EGRESS) { dp->st_rx_frm_egr++; @@ -170,7 +172,7 @@ static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev) stats->rx_packets++; stats->rx_bytes+=skb->len; - if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) { + if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) { dev_kfree_skb(skb); stats->rx_dropped++; return NETDEV_TX_OK; diff --git a/drivers/net/igb/e1000_82575.c b/drivers/net/igb/e1000_82575.c index f8f5772557ce..e8e9e9194a88 100644 --- a/drivers/net/igb/e1000_82575.c +++ b/drivers/net/igb/e1000_82575.c @@ -46,7 +46,10 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *); static s32 igb_init_hw_82575(struct e1000_hw *); static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *); static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *, u32, u16 *); +static s32 igb_read_phy_reg_82580(struct e1000_hw *, u32, u16 *); +static s32 igb_write_phy_reg_82580(struct e1000_hw *, u32, u16); static s32 igb_reset_hw_82575(struct e1000_hw *); +static s32 igb_reset_hw_82580(struct e1000_hw *); static s32 igb_set_d0_lplu_state_82575(struct e1000_hw *, bool); static s32 igb_setup_copper_link_82575(struct e1000_hw *); static s32 igb_setup_serdes_link_82575(struct e1000_hw *); @@ -62,6 +65,12 @@ static s32 igb_reset_init_script_82575(struct e1000_hw *); static s32 igb_read_mac_addr_82575(struct e1000_hw *); static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw); +static const u16 e1000_82580_rxpbs_table[] = + { 36, 72, 144, 1, 2, 4, 8, 16, + 35, 70, 140 }; +#define E1000_82580_RXPBS_TABLE_SIZE \ + (sizeof(e1000_82580_rxpbs_table)/sizeof(u16)) + static s32 igb_get_invariants_82575(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; @@ -81,12 +90,20 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) break; case E1000_DEV_ID_82576: case E1000_DEV_ID_82576_NS: + case E1000_DEV_ID_82576_NS_SERDES: case E1000_DEV_ID_82576_FIBER: case E1000_DEV_ID_82576_SERDES: case E1000_DEV_ID_82576_QUAD_COPPER: case E1000_DEV_ID_82576_SERDES_QUAD: mac->type = e1000_82576; break; + case E1000_DEV_ID_82580_COPPER: + case E1000_DEV_ID_82580_FIBER: + case E1000_DEV_ID_82580_SERDES: + case E1000_DEV_ID_82580_SGMII: + case E1000_DEV_ID_82580_COPPER_DUAL: + mac->type = e1000_82580; + break; default: return -E1000_ERR_MAC_INIT; break; @@ -109,6 +126,7 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) dev_spec->sgmii_active = true; ctrl_ext |= E1000_CTRL_I2C_ENA; break; + case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: hw->phy.media_type = e1000_media_type_internal_serdes; ctrl_ext |= E1000_CTRL_I2C_ENA; @@ -120,12 +138,26 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) wr32(E1000_CTRL_EXT, ctrl_ext); + /* + * if using i2c make certain the MDICNFG register is cleared to prevent + * communications from being misrouted to the mdic registers + */ + if ((ctrl_ext & E1000_CTRL_I2C_ENA) && (hw->mac.type == e1000_82580)) + wr32(E1000_MDICNFG, 0); + /* Set mta register count */ mac->mta_reg_count = 128; /* Set rar entry count */ mac->rar_entry_count = E1000_RAR_ENTRIES_82575; if (mac->type == e1000_82576) mac->rar_entry_count = E1000_RAR_ENTRIES_82576; + if (mac->type == e1000_82580) + mac->rar_entry_count = E1000_RAR_ENTRIES_82580; + /* reset */ + if (mac->type == e1000_82580) + mac->ops.reset_hw = igb_reset_hw_82580; + else + mac->ops.reset_hw = igb_reset_hw_82575; /* Set if part includes ASF firmware */ mac->asf_firmware_present = true; /* Set if manageability features are enabled. */ @@ -193,6 +225,10 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) phy->ops.reset = igb_phy_hw_reset_sgmii_82575; phy->ops.read_reg = igb_read_phy_reg_sgmii_82575; phy->ops.write_reg = igb_write_phy_reg_sgmii_82575; + } else if (hw->mac.type == e1000_82580) { + phy->ops.reset = igb_phy_hw_reset; + phy->ops.read_reg = igb_read_phy_reg_82580; + phy->ops.write_reg = igb_write_phy_reg_82580; } else { phy->ops.reset = igb_phy_hw_reset; phy->ops.read_reg = igb_read_phy_reg_igp; @@ -224,6 +260,12 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) phy->ops.set_d0_lplu_state = igb_set_d0_lplu_state_82575; phy->ops.set_d3_lplu_state = igb_set_d3_lplu_state; break; + case I82580_I_PHY_ID: + phy->type = e1000_phy_82580; + phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_82580; + phy->ops.get_cable_length = igb_get_cable_length_82580; + phy->ops.get_phy_info = igb_get_phy_info_82580; + break; default: return -E1000_ERR_PHY; } @@ -240,9 +282,10 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) **/ static s32 igb_acquire_phy_82575(struct e1000_hw *hw) { - u16 mask; + u16 mask = E1000_SWFW_PHY0_SM; - mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM; + if (hw->bus.func == E1000_FUNC_1) + mask = E1000_SWFW_PHY1_SM; return igb_acquire_swfw_sync_82575(hw, mask); } @@ -256,9 +299,11 @@ static s32 igb_acquire_phy_82575(struct e1000_hw *hw) **/ static void igb_release_phy_82575(struct e1000_hw *hw) { - u16 mask; + u16 mask = E1000_SWFW_PHY0_SM; + + if (hw->bus.func == E1000_FUNC_1) + mask = E1000_SWFW_PHY1_SM; - mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM; igb_release_swfw_sync_82575(hw, mask); } @@ -274,45 +319,23 @@ static void igb_release_phy_82575(struct e1000_hw *hw) static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, u16 *data) { - struct e1000_phy_info *phy = &hw->phy; - u32 i, i2ccmd = 0; + s32 ret_val = -E1000_ERR_PARAM; if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { hw_dbg("PHY Address %u is out of range\n", offset); - return -E1000_ERR_PARAM; + goto out; } - /* - * Set up Op-code, Phy Address, and register address in the I2CCMD - * register. The MAC will take care of interfacing with the - * PHY to retrieve the desired data. - */ - i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | - (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | - (E1000_I2CCMD_OPCODE_READ)); - - wr32(E1000_I2CCMD, i2ccmd); + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; - /* Poll the ready bit to see if the I2C read completed */ - for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { - udelay(50); - i2ccmd = rd32(E1000_I2CCMD); - if (i2ccmd & E1000_I2CCMD_READY) - break; - } - if (!(i2ccmd & E1000_I2CCMD_READY)) { - hw_dbg("I2CCMD Read did not complete\n"); - return -E1000_ERR_PHY; - } - if (i2ccmd & E1000_I2CCMD_ERROR) { - hw_dbg("I2CCMD Error bit set\n"); - return -E1000_ERR_PHY; - } + ret_val = igb_read_phy_reg_i2c(hw, offset, data); - /* Need to byte-swap the 16-bit value. */ - *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00); + hw->phy.ops.release(hw); - return 0; +out: + return ret_val; } /** @@ -327,47 +350,24 @@ static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, static s32 igb_write_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, u16 data) { - struct e1000_phy_info *phy = &hw->phy; - u32 i, i2ccmd = 0; - u16 phy_data_swapped; + s32 ret_val = -E1000_ERR_PARAM; + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { hw_dbg("PHY Address %d is out of range\n", offset); - return -E1000_ERR_PARAM; + goto out; } - /* Swap the data bytes for the I2C interface */ - phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00); + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; - /* - * Set up Op-code, Phy Address, and register address in the I2CCMD - * register. The MAC will take care of interfacing with the - * PHY to retrieve the desired data. - */ - i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | - (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | - E1000_I2CCMD_OPCODE_WRITE | - phy_data_swapped); - - wr32(E1000_I2CCMD, i2ccmd); - - /* Poll the ready bit to see if the I2C read completed */ - for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { - udelay(50); - i2ccmd = rd32(E1000_I2CCMD); - if (i2ccmd & E1000_I2CCMD_READY) - break; - } - if (!(i2ccmd & E1000_I2CCMD_READY)) { - hw_dbg("I2CCMD Write did not complete\n"); - return -E1000_ERR_PHY; - } - if (i2ccmd & E1000_I2CCMD_ERROR) { - hw_dbg("I2CCMD Error bit set\n"); - return -E1000_ERR_PHY; - } + ret_val = igb_write_phy_reg_i2c(hw, offset, data); - return 0; + hw->phy.ops.release(hw); + +out: + return ret_val; } /** @@ -676,6 +676,10 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw) if (hw->bus.func == 1) mask = E1000_NVM_CFG_DONE_PORT_1; + else if (hw->bus.func == E1000_FUNC_2) + mask = E1000_NVM_CFG_DONE_PORT_2; + else if (hw->bus.func == E1000_FUNC_3) + mask = E1000_NVM_CFG_DONE_PORT_3; while (timeout) { if (rd32(E1000_EEMNGCTL) & mask) @@ -706,9 +710,7 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw) s32 ret_val; u16 speed, duplex; - /* SGMII link check is done through the PCS register. */ - if ((hw->phy.media_type != e1000_media_type_copper) || - (igb_sgmii_active_82575(hw))) { + if (hw->phy.media_type != e1000_media_type_copper) { ret_val = igb_get_pcs_speed_and_duplex_82575(hw, &speed, &duplex); /* @@ -723,6 +725,7 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw) return ret_val; } + /** * igb_get_pcs_speed_and_duplex_82575 - Retrieve current speed/duplex * @hw: pointer to the HW structure @@ -788,13 +791,27 @@ static s32 igb_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, u16 *speed, void igb_shutdown_serdes_link_82575(struct e1000_hw *hw) { u32 reg; + u16 eeprom_data = 0; if (hw->phy.media_type != e1000_media_type_internal_serdes || igb_sgmii_active_82575(hw)) return; - /* if the management interface is not enabled, then power down */ - if (!igb_enable_mng_pass_thru(hw)) { + if (hw->bus.func == E1000_FUNC_0) + hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data); + else if (hw->mac.type == e1000_82580) + hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A + + NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1, + &eeprom_data); + else if (hw->bus.func == E1000_FUNC_1) + hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); + + /* + * If APM is not enabled in the EEPROM and management interface is + * not enabled, then power down. + */ + if (!(eeprom_data & E1000_NVM_APME_82575) && + !igb_enable_mng_pass_thru(hw)) { /* Disable PCS to turn off link */ reg = rd32(E1000_PCS_CFG0); reg &= ~E1000_PCS_CFG_PCS_EN; @@ -908,6 +925,11 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw) for (i = 0; i < mac->mta_reg_count; i++) array_wr32(E1000_MTA, i, 0); + /* Zero out the Unicast HASH table */ + hw_dbg("Zeroing the UTA\n"); + for (i = 0; i < mac->uta_reg_count; i++) + array_wr32(E1000_UTA, i, 0); + /* Setup link and flow control */ ret_val = igb_setup_link(hw); @@ -934,7 +956,6 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) { u32 ctrl; s32 ret_val; - bool link; ctrl = rd32(E1000_CTRL); ctrl |= E1000_CTRL_SLU; @@ -946,6 +967,9 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) goto out; if (igb_sgmii_active_82575(hw) && !hw->phy.reset_disable) { + /* allow time for SFP cage time to power up phy */ + msleep(300); + ret_val = hw->phy.ops.reset(hw); if (ret_val) { hw_dbg("Error resetting the PHY.\n"); @@ -959,6 +983,9 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) case e1000_phy_igp_3: ret_val = igb_copper_link_setup_igp(hw); break; + case e1000_phy_82580: + ret_val = igb_copper_link_setup_82580(hw); + break; default: ret_val = -E1000_ERR_PHY; break; @@ -967,57 +994,24 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) if (ret_val) goto out; - if (hw->mac.autoneg) { - /* - * Setup autoneg and flow control advertisement - * and perform autonegotiation. - */ - ret_val = igb_copper_link_autoneg(hw); - if (ret_val) - goto out; - } else { - /* - * PHY will be set to 10H, 10F, 100H or 100F - * depending on user settings. - */ - hw_dbg("Forcing Speed and Duplex\n"); - ret_val = hw->phy.ops.force_speed_duplex(hw); - if (ret_val) { - hw_dbg("Error Forcing Speed and Duplex\n"); - goto out; - } - } - - /* - * Check link status. Wait up to 100 microseconds for link to become - * valid. - */ - ret_val = igb_phy_has_link(hw, COPPER_LINK_UP_LIMIT, 10, &link); - if (ret_val) - goto out; - - if (link) { - hw_dbg("Valid link established!!!\n"); - /* Config the MAC and PHY after link is up */ - igb_config_collision_dist(hw); - ret_val = igb_config_fc_after_link_up(hw); - } else { - hw_dbg("Unable to establish link!!!\n"); - } - + ret_val = igb_setup_copper_link(hw); out: return ret_val; } /** - * igb_setup_serdes_link_82575 - Setup link for fiber/serdes + * igb_setup_serdes_link_82575 - Setup link for serdes * @hw: pointer to the HW structure * - * Configures speed and duplex for fiber and serdes links. + * Configure the physical coding sub-layer (PCS) link. The PCS link is + * used on copper connections where the serialized gigabit media independent + * interface (sgmii), or serdes fiber is being used. Configures the link + * for auto-negotiation or forces speed/duplex. **/ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw) { - u32 ctrl_reg, reg; + u32 ctrl_ext, ctrl_reg, reg; + bool pcs_autoneg; if ((hw->phy.media_type != e1000_media_type_internal_serdes) && !igb_sgmii_active_82575(hw)) @@ -1032,9 +1026,9 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw) wr32(E1000_SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK); /* power on the sfp cage if present */ - reg = rd32(E1000_CTRL_EXT); - reg &= ~E1000_CTRL_EXT_SDP3_DATA; - wr32(E1000_CTRL_EXT, reg); + ctrl_ext = rd32(E1000_CTRL_EXT); + ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA; + wr32(E1000_CTRL_EXT, ctrl_ext); ctrl_reg = rd32(E1000_CTRL); ctrl_reg |= E1000_CTRL_SLU; @@ -1051,15 +1045,31 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw) reg = rd32(E1000_PCS_LCTL); - if (igb_sgmii_active_82575(hw)) { - /* allow time for SFP cage to power up phy */ - msleep(300); + /* default pcs_autoneg to the same setting as mac autoneg */ + pcs_autoneg = hw->mac.autoneg; - /* AN time out should be disabled for SGMII mode */ + switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) { + case E1000_CTRL_EXT_LINK_MODE_SGMII: + /* sgmii mode lets the phy handle forcing speed/duplex */ + pcs_autoneg = true; + /* autoneg time out should be disabled for SGMII mode */ reg &= ~(E1000_PCS_LCTL_AN_TIMEOUT); - } else { + break; + case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: + /* disable PCS autoneg and support parallel detect only */ + pcs_autoneg = false; + default: + /* + * non-SGMII modes only supports a speed of 1000/Full for the + * link so it is best to just force the MAC and let the pcs + * link either autoneg or be forced to 1000/Full + */ ctrl_reg |= E1000_CTRL_SPD_1000 | E1000_CTRL_FRCSPD | E1000_CTRL_FD | E1000_CTRL_FRCDPX; + + /* set speed of 1000/Full if speed/duplex is forced */ + reg |= E1000_PCS_LCTL_FSV_1000 | E1000_PCS_LCTL_FDV_FULL; + break; } wr32(E1000_CTRL, ctrl_reg); @@ -1070,7 +1080,6 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw) * mode that will be compatible with older link partners and switches. * However, both are supported by the hardware and some drivers/tools. */ - reg &= ~(E1000_PCS_LCTL_AN_ENABLE | E1000_PCS_LCTL_FLV_LINK_UP | E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK); @@ -1080,25 +1089,18 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw) */ reg |= E1000_PCS_LCTL_FORCE_FCTRL; - /* - * we always set sgmii to autoneg since it is the phy that will be - * forcing the link and the serdes is just a go-between - */ - if (hw->mac.autoneg || igb_sgmii_active_82575(hw)) { + if (pcs_autoneg) { /* Set PCS register for autoneg */ - reg |= E1000_PCS_LCTL_FSV_1000 | /* Force 1000 */ - E1000_PCS_LCTL_FDV_FULL | /* SerDes Full duplex */ - E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */ - E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */ - hw_dbg("Configuring Autoneg; PCS_LCTL = 0x%08X\n", reg); + reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */ + E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */ + hw_dbg("Configuring Autoneg:PCS_LCTL=0x%08X\n", reg); } else { - /* Set PCS register for forced speed */ - reg |= E1000_PCS_LCTL_FLV_LINK_UP | /* Force link up */ - E1000_PCS_LCTL_FSV_1000 | /* Force 1000 */ - E1000_PCS_LCTL_FDV_FULL | /* SerDes Full duplex */ - E1000_PCS_LCTL_FSD | /* Force Speed */ - E1000_PCS_LCTL_FORCE_LINK; /* Force Link */ - hw_dbg("Configuring Forced Link; PCS_LCTL = 0x%08X\n", reg); + /* Set PCS register for forced link */ + reg |= E1000_PCS_LCTL_FSD | /* Force Speed */ + E1000_PCS_LCTL_FORCE_LINK | /* Force Link */ + E1000_PCS_LCTL_FLV_LINK_UP; /* Force link value up */ + + hw_dbg("Configuring Forced Link:PCS_LCTL=0x%08X\n", reg); } wr32(E1000_PCS_LCTL, reg); @@ -1167,9 +1169,18 @@ static s32 igb_read_mac_addr_82575(struct e1000_hw *hw) { s32 ret_val = 0; - if (igb_check_alt_mac_addr(hw)) - ret_val = igb_read_mac_addr(hw); + /* + * If there's an alternate MAC address place it in RAR0 + * so that it will override the Si installed default perm + * address. + */ + ret_val = igb_check_alt_mac_addr(hw); + if (ret_val) + goto out; + + ret_val = igb_read_mac_addr(hw); +out: return ret_val; } @@ -1181,61 +1192,59 @@ static s32 igb_read_mac_addr_82575(struct e1000_hw *hw) **/ static void igb_clear_hw_cntrs_82575(struct e1000_hw *hw) { - u32 temp; - igb_clear_hw_cntrs_base(hw); - temp = rd32(E1000_PRC64); - temp = rd32(E1000_PRC127); - temp = rd32(E1000_PRC255); - temp = rd32(E1000_PRC511); - temp = rd32(E1000_PRC1023); - temp = rd32(E1000_PRC1522); - temp = rd32(E1000_PTC64); - temp = rd32(E1000_PTC127); - temp = rd32(E1000_PTC255); - temp = rd32(E1000_PTC511); - temp = rd32(E1000_PTC1023); - temp = rd32(E1000_PTC1522); - - temp = rd32(E1000_ALGNERRC); - temp = rd32(E1000_RXERRC); - temp = rd32(E1000_TNCRS); - temp = rd32(E1000_CEXTERR); - temp = rd32(E1000_TSCTC); - temp = rd32(E1000_TSCTFC); - - temp = rd32(E1000_MGTPRC); - temp = rd32(E1000_MGTPDC); - temp = rd32(E1000_MGTPTC); - - temp = rd32(E1000_IAC); - temp = rd32(E1000_ICRXOC); - - temp = rd32(E1000_ICRXPTC); - temp = rd32(E1000_ICRXATC); - temp = rd32(E1000_ICTXPTC); - temp = rd32(E1000_ICTXATC); - temp = rd32(E1000_ICTXQEC); - temp = rd32(E1000_ICTXQMTC); - temp = rd32(E1000_ICRXDMTC); - - temp = rd32(E1000_CBTMPC); - temp = rd32(E1000_HTDPMC); - temp = rd32(E1000_CBRMPC); - temp = rd32(E1000_RPTHC); - temp = rd32(E1000_HGPTC); - temp = rd32(E1000_HTCBDPC); - temp = rd32(E1000_HGORCL); - temp = rd32(E1000_HGORCH); - temp = rd32(E1000_HGOTCL); - temp = rd32(E1000_HGOTCH); - temp = rd32(E1000_LENERRS); + rd32(E1000_PRC64); + rd32(E1000_PRC127); + rd32(E1000_PRC255); + rd32(E1000_PRC511); + rd32(E1000_PRC1023); + rd32(E1000_PRC1522); + rd32(E1000_PTC64); + rd32(E1000_PTC127); + rd32(E1000_PTC255); + rd32(E1000_PTC511); + rd32(E1000_PTC1023); + rd32(E1000_PTC1522); + + rd32(E1000_ALGNERRC); + rd32(E1000_RXERRC); + rd32(E1000_TNCRS); + rd32(E1000_CEXTERR); + rd32(E1000_TSCTC); + rd32(E1000_TSCTFC); + + rd32(E1000_MGTPRC); + rd32(E1000_MGTPDC); + rd32(E1000_MGTPTC); + + rd32(E1000_IAC); + rd32(E1000_ICRXOC); + + rd32(E1000_ICRXPTC); + rd32(E1000_ICRXATC); + rd32(E1000_ICTXPTC); + rd32(E1000_ICTXATC); + rd32(E1000_ICTXQEC); + rd32(E1000_ICTXQMTC); + rd32(E1000_ICRXDMTC); + + rd32(E1000_CBTMPC); + rd32(E1000_HTDPMC); + rd32(E1000_CBRMPC); + rd32(E1000_RPTHC); + rd32(E1000_HGPTC); + rd32(E1000_HTCBDPC); + rd32(E1000_HGORCL); + rd32(E1000_HGORCH); + rd32(E1000_HGOTCL); + rd32(E1000_HGOTCH); + rd32(E1000_LENERRS); /* This register should not be read in copper configurations */ if (hw->phy.media_type == e1000_media_type_internal_serdes || igb_sgmii_active_82575(hw)) - temp = rd32(E1000_SCVPC); + rd32(E1000_SCVPC); } /** @@ -1400,8 +1409,183 @@ void igb_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable) wr32(E1000_VT_CTL, vt_ctl); } +/** + * igb_read_phy_reg_82580 - Read 82580 MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the MDI control register in the PHY at offset and stores the + * information read to data. + **/ +static s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data) +{ + u32 mdicnfg = 0; + s32 ret_val; + + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + /* + * We config the phy address in MDICNFG register now. Same bits + * as before. The values in MDIC can be written but will be + * ignored. This allows us to call the old function after + * configuring the PHY address in the new register + */ + mdicnfg = (hw->phy.addr << E1000_MDIC_PHY_SHIFT); + wr32(E1000_MDICNFG, mdicnfg); + + ret_val = igb_read_phy_reg_mdic(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * igb_write_phy_reg_82580 - Write 82580 MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write to register at offset + * + * Writes data to MDI control register in the PHY at offset. + **/ +static s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data) +{ + u32 mdicnfg = 0; + s32 ret_val; + + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + /* + * We config the phy address in MDICNFG register now. Same bits + * as before. The values in MDIC can be written but will be + * ignored. This allows us to call the old function after + * configuring the PHY address in the new register + */ + mdicnfg = (hw->phy.addr << E1000_MDIC_PHY_SHIFT); + wr32(E1000_MDICNFG, mdicnfg); + + ret_val = igb_write_phy_reg_mdic(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * igb_reset_hw_82580 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets function or entire device (all ports, etc.) + * to a known state. + **/ +static s32 igb_reset_hw_82580(struct e1000_hw *hw) +{ + s32 ret_val = 0; + /* BH SW mailbox bit in SW_FW_SYNC */ + u16 swmbsw_mask = E1000_SW_SYNCH_MB; + u32 ctrl, icr; + bool global_device_reset = hw->dev_spec._82575.global_device_reset; + + + hw->dev_spec._82575.global_device_reset = false; + + /* Get current control state. */ + ctrl = rd32(E1000_CTRL); + + /* + * Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = igb_disable_pcie_master(hw); + if (ret_val) + hw_dbg("PCI-E Master disable polling has failed.\n"); + + hw_dbg("Masking off all interrupts\n"); + wr32(E1000_IMC, 0xffffffff); + wr32(E1000_RCTL, 0); + wr32(E1000_TCTL, E1000_TCTL_PSP); + wrfl(); + + msleep(10); + + /* Determine whether or not a global dev reset is requested */ + if (global_device_reset && + igb_acquire_swfw_sync_82575(hw, swmbsw_mask)) + global_device_reset = false; + + if (global_device_reset && + !(rd32(E1000_STATUS) & E1000_STAT_DEV_RST_SET)) + ctrl |= E1000_CTRL_DEV_RST; + else + ctrl |= E1000_CTRL_RST; + + wr32(E1000_CTRL, ctrl); + + /* Add delay to insure DEV_RST has time to complete */ + if (global_device_reset) + msleep(5); + + ret_val = igb_get_auto_rd_done(hw); + if (ret_val) { + /* + * When auto config read does not complete, do not + * return with an error. This can happen in situations + * where there is no eeprom and prevents getting link. + */ + hw_dbg("Auto Read Done did not complete\n"); + } + + /* If EEPROM is not present, run manual init scripts */ + if ((rd32(E1000_EECD) & E1000_EECD_PRES) == 0) + igb_reset_init_script_82575(hw); + + /* clear global device reset status bit */ + wr32(E1000_STATUS, E1000_STAT_DEV_RST_SET); + + /* Clear any pending interrupt events. */ + wr32(E1000_IMC, 0xffffffff); + icr = rd32(E1000_ICR); + + /* Install any alternate MAC address into RAR0 */ + ret_val = igb_check_alt_mac_addr(hw); + + /* Release semaphore */ + if (global_device_reset) + igb_release_swfw_sync_82575(hw, swmbsw_mask); + + return ret_val; +} + +/** + * igb_rxpbs_adjust_82580 - adjust RXPBS value to reflect actual RX PBA size + * @data: data received by reading RXPBS register + * + * The 82580 uses a table based approach for packet buffer allocation sizes. + * This function converts the retrieved value into the correct table value + * 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 + * 0x0 36 72 144 1 2 4 8 16 + * 0x8 35 70 140 rsv rsv rsv rsv rsv + */ +u16 igb_rxpbs_adjust_82580(u32 data) +{ + u16 ret_val = 0; + + if (data < E1000_82580_RXPBS_TABLE_SIZE) + ret_val = e1000_82580_rxpbs_table[data]; + + return ret_val; +} + static struct e1000_mac_operations e1000_mac_ops_82575 = { - .reset_hw = igb_reset_hw_82575, .init_hw = igb_init_hw_82575, .check_for_link = igb_check_for_link_82575, .rar_set = igb_rar_set, diff --git a/drivers/net/igb/e1000_82575.h b/drivers/net/igb/e1000_82575.h index ebd146fd4e15..d51c9927c819 100644 --- a/drivers/net/igb/e1000_82575.h +++ b/drivers/net/igb/e1000_82575.h @@ -38,6 +38,11 @@ extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw); #define E1000_RAR_ENTRIES_82575 16 #define E1000_RAR_ENTRIES_82576 24 +#define E1000_RAR_ENTRIES_82580 24 + +#define E1000_SW_SYNCH_MB 0x00000100 +#define E1000_STAT_DEV_RST_SET 0x00100000 +#define E1000_CTRL_DEV_RST 0x20000000 /* SRRCTL bit definitions */ #define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */ @@ -66,6 +71,8 @@ extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw); E1000_EICR_RX_QUEUE3) /* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */ +#define E1000_IMIREXT_SIZE_BP 0x00001000 /* Packet size bypass */ +#define E1000_IMIREXT_CTRL_BP 0x00080000 /* Bypass check of ctrl bits */ /* Receive Descriptor - Advanced */ union e1000_adv_rx_desc { @@ -98,6 +105,7 @@ union e1000_adv_rx_desc { #define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0 #define E1000_RXDADV_HDRBUFLEN_SHIFT 5 +#define E1000_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */ /* Transmit Descriptor - Advanced */ union e1000_adv_tx_desc { @@ -167,6 +175,18 @@ struct e1000_adv_tx_context_desc { #define E1000_DCA_TXCTRL_CPUID_SHIFT 24 /* Tx CPUID now in the last byte */ #define E1000_DCA_RXCTRL_CPUID_SHIFT 24 /* Rx CPUID now in the last byte */ +/* ETQF register bit definitions */ +#define E1000_ETQF_FILTER_ENABLE (1 << 26) +#define E1000_ETQF_1588 (1 << 30) + +/* FTQF register bit definitions */ +#define E1000_FTQF_VF_BP 0x00008000 +#define E1000_FTQF_1588_TIME_STAMP 0x08000000 +#define E1000_FTQF_MASK 0xF0000000 +#define E1000_FTQF_MASK_PROTO_BP 0x10000000 +#define E1000_FTQF_MASK_SOURCE_PORT_BP 0x80000000 + +#define E1000_NVM_APME_82575 0x0400 #define MAX_NUM_VFS 8 #define E1000_DTXSWC_VMDQ_LOOPBACK_EN (1 << 31) /* global VF LB enable */ @@ -202,9 +222,21 @@ struct e1000_adv_tx_context_desc { #define E1000_IOVCTL 0x05BBC #define E1000_IOVCTL_REUSE_VFQ 0x00000001 +#define E1000_RPLOLR_STRVLAN 0x40000000 +#define E1000_RPLOLR_STRCRC 0x80000000 + +#define E1000_DTXCTL_8023LL 0x0004 +#define E1000_DTXCTL_VLAN_ADDED 0x0008 +#define E1000_DTXCTL_OOS_ENABLE 0x0010 +#define E1000_DTXCTL_MDP_EN 0x0020 +#define E1000_DTXCTL_SPOOF_INT 0x0040 + #define ALL_QUEUES 0xFFFF +/* RX packet buffer size defines */ +#define E1000_RXPBS_SIZE_MASK_82576 0x0000007F void igb_vmdq_set_loopback_pf(struct e1000_hw *, bool); void igb_vmdq_set_replication_pf(struct e1000_hw *, bool); +u16 igb_rxpbs_adjust_82580(u32 data); #endif diff --git a/drivers/net/igb/e1000_defines.h b/drivers/net/igb/e1000_defines.h index cb916833f303..6e036ae3138f 100644 --- a/drivers/net/igb/e1000_defines.h +++ b/drivers/net/igb/e1000_defines.h @@ -49,6 +49,7 @@ #define E1000_CTRL_EXT_PFRSTD 0x00004000 #define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 #define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_1000BASE_KX 0x00400000 #define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000 #define E1000_CTRL_EXT_EIAME 0x01000000 #define E1000_CTRL_EXT_IRCA 0x00000001 @@ -329,6 +330,7 @@ #define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ #define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ #define E1000_ICR_VMMB 0x00000100 /* VM MB event */ +#define E1000_ICR_DRSTA 0x40000000 /* Device Reset Asserted */ /* If this bit asserted, the driver should claim the interrupt */ #define E1000_ICR_INT_ASSERTED 0x80000000 /* LAN connected device generates an interrupt */ @@ -370,6 +372,7 @@ #define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ #define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ #define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_DRSTA E1000_ICR_DRSTA /* Device Reset Asserted */ #define E1000_IMS_DOUTSYNC E1000_ICR_DOUTSYNC /* NIC DMA out of sync */ /* Extended Interrupt Mask Set */ @@ -378,6 +381,7 @@ /* Interrupt Cause Set */ #define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ #define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_ICS_DRSTA E1000_ICR_DRSTA /* Device Reset Aserted */ /* Extended Interrupt Cause Set */ @@ -435,6 +439,39 @@ /* Flow Control */ #define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ +#define E1000_TSYNCTXCTL_VALID 0x00000001 /* tx timestamp valid */ +#define E1000_TSYNCTXCTL_ENABLED 0x00000010 /* enable tx timestampping */ + +#define E1000_TSYNCRXCTL_VALID 0x00000001 /* rx timestamp valid */ +#define E1000_TSYNCRXCTL_TYPE_MASK 0x0000000E /* rx type mask */ +#define E1000_TSYNCRXCTL_TYPE_L2_V2 0x00 +#define E1000_TSYNCRXCTL_TYPE_L4_V1 0x02 +#define E1000_TSYNCRXCTL_TYPE_L2_L4_V2 0x04 +#define E1000_TSYNCRXCTL_TYPE_ALL 0x08 +#define E1000_TSYNCRXCTL_TYPE_EVENT_V2 0x0A +#define E1000_TSYNCRXCTL_ENABLED 0x00000010 /* enable rx timestampping */ + +#define E1000_TSYNCRXCFG_PTP_V1_CTRLT_MASK 0x000000FF +#define E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE 0x00 +#define E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE 0x01 +#define E1000_TSYNCRXCFG_PTP_V1_FOLLOWUP_MESSAGE 0x02 +#define E1000_TSYNCRXCFG_PTP_V1_DELAY_RESP_MESSAGE 0x03 +#define E1000_TSYNCRXCFG_PTP_V1_MANAGEMENT_MESSAGE 0x04 + +#define E1000_TSYNCRXCFG_PTP_V2_MSGID_MASK 0x00000F00 +#define E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE 0x0000 +#define E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE 0x0100 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_REQ_MESSAGE 0x0200 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_RESP_MESSAGE 0x0300 +#define E1000_TSYNCRXCFG_PTP_V2_FOLLOWUP_MESSAGE 0x0800 +#define E1000_TSYNCRXCFG_PTP_V2_DELAY_RESP_MESSAGE 0x0900 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_FOLLOWUP_MESSAGE 0x0A00 +#define E1000_TSYNCRXCFG_PTP_V2_ANNOUNCE_MESSAGE 0x0B00 +#define E1000_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE 0x0C00 +#define E1000_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE 0x0D00 + +#define E1000_TIMINCA_16NS_SHIFT 24 + /* PCI Express Control */ #define E1000_GCR_CMPL_TMOUT_MASK 0x0000F000 #define E1000_GCR_CMPL_TMOUT_10ms 0x00001000 @@ -524,8 +561,12 @@ #define NVM_ALT_MAC_ADDR_PTR 0x0037 #define NVM_CHECKSUM_REG 0x003F -#define E1000_NVM_CFG_DONE_PORT_0 0x40000 /* MNG config cycle done */ -#define E1000_NVM_CFG_DONE_PORT_1 0x80000 /* ...for second port */ +#define E1000_NVM_CFG_DONE_PORT_0 0x040000 /* MNG config cycle done */ +#define E1000_NVM_CFG_DONE_PORT_1 0x080000 /* ...for second port */ +#define E1000_NVM_CFG_DONE_PORT_2 0x100000 /* ...for third port */ +#define E1000_NVM_CFG_DONE_PORT_3 0x200000 /* ...for fourth port */ + +#define NVM_82580_LAN_FUNC_OFFSET(a) (a ? (0x40 + (0x40 * a)) : 0) /* Mask bits for fields in Word 0x0f of the NVM */ #define NVM_WORD0F_PAUSE_MASK 0x3000 @@ -592,6 +633,7 @@ */ #define M88E1111_I_PHY_ID 0x01410CC0 #define IGP03E1000_E_PHY_ID 0x02A80390 +#define I82580_I_PHY_ID 0x015403A0 #define M88_VENDOR 0x0141 /* M88E1000 Specific Registers */ @@ -678,4 +720,8 @@ #define E1000_VFTA_ENTRY_MASK 0x7F #define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F +/* DMA Coalescing register fields */ +#define E1000_PCIEMISC_LX_DECISION 0x00000080 /* Lx power decision based + on DMA coal */ + #endif diff --git a/drivers/net/igb/e1000_hw.h b/drivers/net/igb/e1000_hw.h index 119869b1124d..dbaeb5f5e0c7 100644 --- a/drivers/net/igb/e1000_hw.h +++ b/drivers/net/igb/e1000_hw.h @@ -42,20 +42,35 @@ struct e1000_hw; #define E1000_DEV_ID_82576_SERDES 0x10E7 #define E1000_DEV_ID_82576_QUAD_COPPER 0x10E8 #define E1000_DEV_ID_82576_NS 0x150A +#define E1000_DEV_ID_82576_NS_SERDES 0x1518 #define E1000_DEV_ID_82576_SERDES_QUAD 0x150D #define E1000_DEV_ID_82575EB_COPPER 0x10A7 #define E1000_DEV_ID_82575EB_FIBER_SERDES 0x10A9 #define E1000_DEV_ID_82575GB_QUAD_COPPER 0x10D6 +#define E1000_DEV_ID_82580_COPPER 0x150E +#define E1000_DEV_ID_82580_FIBER 0x150F +#define E1000_DEV_ID_82580_SERDES 0x1510 +#define E1000_DEV_ID_82580_SGMII 0x1511 +#define E1000_DEV_ID_82580_COPPER_DUAL 0x1516 #define E1000_REVISION_2 2 #define E1000_REVISION_4 4 +#define E1000_FUNC_0 0 #define E1000_FUNC_1 1 +#define E1000_FUNC_2 2 +#define E1000_FUNC_3 3 + +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN0 0 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN1 3 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN2 6 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN3 9 enum e1000_mac_type { e1000_undefined = 0, e1000_82575, e1000_82576, + e1000_82580, e1000_num_macs /* List is 1-based, so subtract 1 for true count. */ }; @@ -70,7 +85,6 @@ enum e1000_nvm_type { e1000_nvm_unknown = 0, e1000_nvm_none, e1000_nvm_eeprom_spi, - e1000_nvm_eeprom_microwire, e1000_nvm_flash_hw, e1000_nvm_flash_sw }; @@ -79,8 +93,6 @@ enum e1000_nvm_override { e1000_nvm_override_none = 0, e1000_nvm_override_spi_small, e1000_nvm_override_spi_large, - e1000_nvm_override_microwire_small, - e1000_nvm_override_microwire_large }; enum e1000_phy_type { @@ -92,6 +104,7 @@ enum e1000_phy_type { e1000_phy_gg82563, e1000_phy_igp_3, e1000_phy_ife, + e1000_phy_82580, }; enum e1000_bus_type { @@ -288,6 +301,7 @@ struct e1000_mac_operations { struct e1000_phy_operations { s32 (*acquire)(struct e1000_hw *); + s32 (*check_polarity)(struct e1000_hw *); s32 (*check_reset_block)(struct e1000_hw *); s32 (*force_speed_duplex)(struct e1000_hw *); s32 (*get_cfg_done)(struct e1000_hw *hw); @@ -339,6 +353,7 @@ struct e1000_mac_info { u16 ifs_ratio; u16 ifs_step_size; u16 mta_reg_count; + u16 uta_reg_count; /* Maximum size of the MTA register table in all supported adapters */ #define MAX_MTA_REG 128 @@ -463,6 +478,7 @@ struct e1000_mbx_info { struct e1000_dev_spec_82575 { bool sgmii_active; + bool global_device_reset; }; struct e1000_hw { diff --git a/drivers/net/igb/e1000_mac.c b/drivers/net/igb/e1000_mac.c index 7d76bb085e10..2ad358a240bf 100644 --- a/drivers/net/igb/e1000_mac.c +++ b/drivers/net/igb/e1000_mac.c @@ -185,13 +185,12 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw) } if (nvm_alt_mac_addr_offset == 0xFFFF) { - ret_val = -(E1000_NOT_IMPLEMENTED); + /* There is no Alternate MAC Address */ goto out; } if (hw->bus.func == E1000_FUNC_1) - nvm_alt_mac_addr_offset += ETH_ALEN/sizeof(u16); - + nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1; for (i = 0; i < ETH_ALEN; i += 2) { offset = nvm_alt_mac_addr_offset + (i >> 1); ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data); @@ -206,14 +205,16 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw) /* if multicast bit is set, the alternate address will not be used */ if (alt_mac_addr[0] & 0x01) { - ret_val = -(E1000_NOT_IMPLEMENTED); + hw_dbg("Ignoring Alternate Mac Address with MC bit set\n"); goto out; } - for (i = 0; i < ETH_ALEN; i++) - hw->mac.addr[i] = hw->mac.perm_addr[i] = alt_mac_addr[i]; - - hw->mac.ops.rar_set(hw, hw->mac.perm_addr, 0); + /* + * We have a valid alternate MAC address, and we want to treat it the + * same as the normal permanent MAC address stored by the HW into the + * RAR. Do this by mapping this address into RAR0. + */ + hw->mac.ops.rar_set(hw, alt_mac_addr, 0); out: return ret_val; @@ -246,8 +247,15 @@ void igb_rar_set(struct e1000_hw *hw, u8 *addr, u32 index) if (rar_low || rar_high) rar_high |= E1000_RAH_AV; + /* + * Some bridges will combine consecutive 32-bit writes into + * a single burst write, which will malfunction on some parts. + * The flushes avoid this. + */ wr32(E1000_RAL(index), rar_low); + wrfl(); wr32(E1000_RAH(index), rar_high); + wrfl(); } /** @@ -399,45 +407,43 @@ void igb_update_mc_addr_list(struct e1000_hw *hw, **/ void igb_clear_hw_cntrs_base(struct e1000_hw *hw) { - u32 temp; - - temp = rd32(E1000_CRCERRS); - temp = rd32(E1000_SYMERRS); - temp = rd32(E1000_MPC); - temp = rd32(E1000_SCC); - temp = rd32(E1000_ECOL); - temp = rd32(E1000_MCC); - temp = rd32(E1000_LATECOL); - temp = rd32(E1000_COLC); - temp = rd32(E1000_DC); - temp = rd32(E1000_SEC); - temp = rd32(E1000_RLEC); - temp = rd32(E1000_XONRXC); - temp = rd32(E1000_XONTXC); - temp = rd32(E1000_XOFFRXC); - temp = rd32(E1000_XOFFTXC); - temp = rd32(E1000_FCRUC); - temp = rd32(E1000_GPRC); - temp = rd32(E1000_BPRC); - temp = rd32(E1000_MPRC); - temp = rd32(E1000_GPTC); - temp = rd32(E1000_GORCL); - temp = rd32(E1000_GORCH); - temp = rd32(E1000_GOTCL); - temp = rd32(E1000_GOTCH); - temp = rd32(E1000_RNBC); - temp = rd32(E1000_RUC); - temp = rd32(E1000_RFC); - temp = rd32(E1000_ROC); - temp = rd32(E1000_RJC); - temp = rd32(E1000_TORL); - temp = rd32(E1000_TORH); - temp = rd32(E1000_TOTL); - temp = rd32(E1000_TOTH); - temp = rd32(E1000_TPR); - temp = rd32(E1000_TPT); - temp = rd32(E1000_MPTC); - temp = rd32(E1000_BPTC); + rd32(E1000_CRCERRS); + rd32(E1000_SYMERRS); + rd32(E1000_MPC); + rd32(E1000_SCC); + rd32(E1000_ECOL); + rd32(E1000_MCC); + rd32(E1000_LATECOL); + rd32(E1000_COLC); + rd32(E1000_DC); + rd32(E1000_SEC); + rd32(E1000_RLEC); + rd32(E1000_XONRXC); + rd32(E1000_XONTXC); + rd32(E1000_XOFFRXC); + rd32(E1000_XOFFTXC); + rd32(E1000_FCRUC); + rd32(E1000_GPRC); + rd32(E1000_BPRC); + rd32(E1000_MPRC); + rd32(E1000_GPTC); + rd32(E1000_GORCL); + rd32(E1000_GORCH); + rd32(E1000_GOTCL); + rd32(E1000_GOTCH); + rd32(E1000_RNBC); + rd32(E1000_RUC); + rd32(E1000_RFC); + rd32(E1000_ROC); + rd32(E1000_RJC); + rd32(E1000_TORL); + rd32(E1000_TORH); + rd32(E1000_TOTL); + rd32(E1000_TOTH); + rd32(E1000_TPR); + rd32(E1000_TPT); + rd32(E1000_MPTC); + rd32(E1000_BPTC); } /** diff --git a/drivers/net/igb/e1000_mbx.c b/drivers/net/igb/e1000_mbx.c index ed9058eca45c..c474cdb70047 100644 --- a/drivers/net/igb/e1000_mbx.c +++ b/drivers/net/igb/e1000_mbx.c @@ -143,12 +143,16 @@ static s32 igb_poll_for_msg(struct e1000_hw *hw, u16 mbx_id) if (!countdown || !mbx->ops.check_for_msg) goto out; - while (mbx->ops.check_for_msg(hw, mbx_id)) { + while (countdown && mbx->ops.check_for_msg(hw, mbx_id)) { countdown--; if (!countdown) break; udelay(mbx->usec_delay); } + + /* if we failed, all future posted messages fail until reset */ + if (!countdown) + mbx->timeout = 0; out: return countdown ? 0 : -E1000_ERR_MBX; } @@ -168,12 +172,16 @@ static s32 igb_poll_for_ack(struct e1000_hw *hw, u16 mbx_id) if (!countdown || !mbx->ops.check_for_ack) goto out; - while (mbx->ops.check_for_ack(hw, mbx_id)) { + while (countdown && mbx->ops.check_for_ack(hw, mbx_id)) { countdown--; if (!countdown) break; udelay(mbx->usec_delay); } + + /* if we failed, all future posted messages fail until reset */ + if (!countdown) + mbx->timeout = 0; out: return countdown ? 0 : -E1000_ERR_MBX; } @@ -217,12 +225,13 @@ out: static s32 igb_write_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) { struct e1000_mbx_info *mbx = &hw->mbx; - s32 ret_val = 0; + s32 ret_val = -E1000_ERR_MBX; - if (!mbx->ops.write) + /* exit if either we can't write or there isn't a defined timeout */ + if (!mbx->ops.write || !mbx->timeout) goto out; - /* send msg*/ + /* send msg */ ret_val = mbx->ops.write(hw, msg, size, mbx_id); /* if msg sent wait until we receive an ack */ @@ -305,6 +314,30 @@ static s32 igb_check_for_rst_pf(struct e1000_hw *hw, u16 vf_number) } /** + * igb_obtain_mbx_lock_pf - obtain mailbox lock + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * return SUCCESS if we obtained the mailbox lock + **/ +static s32 igb_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number) +{ + s32 ret_val = -E1000_ERR_MBX; + u32 p2v_mailbox; + + + /* Take ownership of the buffer */ + wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU); + + /* reserve mailbox for vf use */ + p2v_mailbox = rd32(E1000_P2VMAILBOX(vf_number)); + if (p2v_mailbox & E1000_P2VMAILBOX_PFU) + ret_val = 0; + + return ret_val; +} + +/** * igb_write_mbx_pf - Places a message in the mailbox * @hw: pointer to the HW structure * @msg: The message buffer @@ -316,27 +349,17 @@ static s32 igb_check_for_rst_pf(struct e1000_hw *hw, u16 vf_number) static s32 igb_write_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, u16 vf_number) { - u32 p2v_mailbox; - s32 ret_val = 0; + s32 ret_val; u16 i; - /* Take ownership of the buffer */ - wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU); - - /* Make sure we have ownership now... */ - p2v_mailbox = rd32(E1000_P2VMAILBOX(vf_number)); - if (!(p2v_mailbox & E1000_P2VMAILBOX_PFU)) { - /* failed to grab ownership */ - ret_val = -E1000_ERR_MBX; + /* lock the mailbox to prevent pf/vf race condition */ + ret_val = igb_obtain_mbx_lock_pf(hw, vf_number); + if (ret_val) goto out_no_write; - } - /* - * flush any ack or msg which may already be in the queue - * as they are likely the result of an error - */ - igb_check_for_ack_pf(hw, vf_number); + /* flush msg and acks as we are overwriting the message buffer */ igb_check_for_msg_pf(hw, vf_number); + igb_check_for_ack_pf(hw, vf_number); /* copy the caller specified message to the mailbox memory buffer */ for (i = 0; i < size; i++) @@ -367,20 +390,13 @@ out_no_write: static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, u16 vf_number) { - u32 p2v_mailbox; - s32 ret_val = 0; + s32 ret_val; u16 i; - /* Take ownership of the buffer */ - wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU); - - /* Make sure we have ownership now... */ - p2v_mailbox = rd32(E1000_P2VMAILBOX(vf_number)); - if (!(p2v_mailbox & E1000_P2VMAILBOX_PFU)) { - /* failed to grab ownership */ - ret_val = -E1000_ERR_MBX; + /* lock the mailbox to prevent pf/vf race condition */ + ret_val = igb_obtain_mbx_lock_pf(hw, vf_number); + if (ret_val) goto out_no_read; - } /* copy the message to the mailbox memory buffer */ for (i = 0; i < size; i++) @@ -392,8 +408,6 @@ static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, /* update stats */ hw->mbx.stats.msgs_rx++; - ret_val = 0; - out_no_read: return ret_val; } diff --git a/drivers/net/igb/e1000_mbx.h b/drivers/net/igb/e1000_mbx.h index ebc02ea3f198..bb112fb6c3a1 100644 --- a/drivers/net/igb/e1000_mbx.h +++ b/drivers/net/igb/e1000_mbx.h @@ -58,10 +58,12 @@ #define E1000_VT_MSGINFO_MASK (0xFF << E1000_VT_MSGINFO_SHIFT) #define E1000_VF_RESET 0x01 /* VF requests reset */ -#define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests PF to set MAC addr */ -#define E1000_VF_SET_MULTICAST 0x03 /* VF requests PF to set MC addr */ -#define E1000_VF_SET_VLAN 0x04 /* VF requests PF to set VLAN */ -#define E1000_VF_SET_LPE 0x05 /* VF requests PF to set VMOLR.LPE */ +#define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests to set MAC addr */ +#define E1000_VF_SET_MULTICAST 0x03 /* VF requests to set MC addr */ +#define E1000_VF_SET_VLAN 0x04 /* VF requests to set VLAN */ +#define E1000_VF_SET_LPE 0x05 /* VF requests to set VMOLR.LPE */ +#define E1000_VF_SET_PROMISC 0x06 /*VF requests to clear VMOLR.ROPE/MPME*/ +#define E1000_VF_SET_PROMISC_MULTICAST (0x02 << E1000_VT_MSGINFO_SHIFT) #define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */ diff --git a/drivers/net/igb/e1000_nvm.c b/drivers/net/igb/e1000_nvm.c index a88bfe2f1e8f..d83b77fa4038 100644 --- a/drivers/net/igb/e1000_nvm.c +++ b/drivers/net/igb/e1000_nvm.c @@ -78,9 +78,7 @@ static void igb_shift_out_eec_bits(struct e1000_hw *hw, u16 data, u16 count) u32 mask; mask = 0x01 << (count - 1); - if (nvm->type == e1000_nvm_eeprom_microwire) - eecd &= ~E1000_EECD_DO; - else if (nvm->type == e1000_nvm_eeprom_spi) + if (nvm->type == e1000_nvm_eeprom_spi) eecd |= E1000_EECD_DO; do { @@ -220,22 +218,7 @@ static void igb_standby_nvm(struct e1000_hw *hw) struct e1000_nvm_info *nvm = &hw->nvm; u32 eecd = rd32(E1000_EECD); - if (nvm->type == e1000_nvm_eeprom_microwire) { - eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); - wr32(E1000_EECD, eecd); - wrfl(); - udelay(nvm->delay_usec); - - igb_raise_eec_clk(hw, &eecd); - - /* Select EEPROM */ - eecd |= E1000_EECD_CS; - wr32(E1000_EECD, eecd); - wrfl(); - udelay(nvm->delay_usec); - - igb_lower_eec_clk(hw, &eecd); - } else if (nvm->type == e1000_nvm_eeprom_spi) { + if (nvm->type == e1000_nvm_eeprom_spi) { /* Toggle CS to flush commands */ eecd |= E1000_EECD_CS; wr32(E1000_EECD, eecd); @@ -263,12 +246,6 @@ static void e1000_stop_nvm(struct e1000_hw *hw) /* Pull CS high */ eecd |= E1000_EECD_CS; igb_lower_eec_clk(hw, &eecd); - } else if (hw->nvm.type == e1000_nvm_eeprom_microwire) { - /* CS on Microcwire is active-high */ - eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); - wr32(E1000_EECD, eecd); - igb_raise_eec_clk(hw, &eecd); - igb_lower_eec_clk(hw, &eecd); } } @@ -304,14 +281,7 @@ static s32 igb_ready_nvm_eeprom(struct e1000_hw *hw) u8 spi_stat_reg; - if (nvm->type == e1000_nvm_eeprom_microwire) { - /* Clear SK and DI */ - eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); - wr32(E1000_EECD, eecd); - /* Set CS */ - eecd |= E1000_EECD_CS; - wr32(E1000_EECD, eecd); - } else if (nvm->type == e1000_nvm_eeprom_spi) { + if (nvm->type == e1000_nvm_eeprom_spi) { /* Clear SK and CS */ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); wr32(E1000_EECD, eecd); diff --git a/drivers/net/igb/e1000_phy.c b/drivers/net/igb/e1000_phy.c index ee460600e74b..5c9d73e9bb8d 100644 --- a/drivers/net/igb/e1000_phy.c +++ b/drivers/net/igb/e1000_phy.c @@ -39,6 +39,9 @@ static s32 igb_wait_autoneg(struct e1000_hw *hw); /* Cable length tables */ static const u16 e1000_m88_cable_length_table[] = { 0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED }; +#define M88E1000_CABLE_LENGTH_TABLE_SIZE \ + (sizeof(e1000_m88_cable_length_table) / \ + sizeof(e1000_m88_cable_length_table[0])) static const u16 e1000_igp_2_cable_length_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21, @@ -109,7 +112,10 @@ out: **/ static s32 igb_phy_reset_dsp(struct e1000_hw *hw) { - s32 ret_val; + s32 ret_val = 0; + + if (!(hw->phy.ops.write_reg)) + goto out; ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xC1); if (ret_val) @@ -130,7 +136,7 @@ out: * Reads the MDI control regsiter in the PHY at offset and stores the * information read to data. **/ -static s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) +s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) { struct e1000_phy_info *phy = &hw->phy; u32 i, mdic = 0; @@ -188,7 +194,7 @@ out: * * Writes data to MDI control register in the PHY at offset. **/ -static s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) +s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) { struct e1000_phy_info *phy = &hw->phy; u32 i, mdic = 0; @@ -239,6 +245,103 @@ out: } /** + * igb_read_phy_reg_i2c - Read PHY register using i2c + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the i2c interface and stores the + * retrieved information in data. + **/ +s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, i2ccmd = 0; + + + /* + * Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | + (E1000_I2CCMD_OPCODE_READ)); + + wr32(E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + udelay(50); + i2ccmd = rd32(E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + hw_dbg("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + hw_dbg("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + /* Need to byte-swap the 16-bit value. */ + *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00); + + return 0; +} + +/** + * igb_write_phy_reg_i2c - Write PHY register using i2c + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset using the i2c interface. + **/ +s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, i2ccmd = 0; + u16 phy_data_swapped; + + + /* Swap the data bytes for the I2C interface */ + phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00); + + /* + * Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_WRITE | + phy_data_swapped); + + wr32(E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + udelay(50); + i2ccmd = rd32(E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + hw_dbg("I2CCMD Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + hw_dbg("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + return 0; +} + +/** * igb_read_phy_reg_igp - Read igp PHY register * @hw: pointer to the HW structure * @offset: register offset to be read @@ -318,6 +421,57 @@ out: } /** + * igb_copper_link_setup_82580 - Setup 82580 PHY for copper link + * @hw: pointer to the HW structure + * + * Sets up Carrier-sense on Transmit and downshift values. + **/ +s32 igb_copper_link_setup_82580(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + + + if (phy->reset_disable) { + ret_val = 0; + goto out; + } + + if (phy->type == e1000_phy_82580) { + ret_val = hw->phy.ops.reset(hw); + if (ret_val) { + hw_dbg("Error resetting the PHY.\n"); + goto out; + } + } + + /* Enable CRS on TX. This must be set for half-duplex operation. */ + ret_val = phy->ops.read_reg(hw, I82580_CFG_REG, &phy_data); + if (ret_val) + goto out; + + phy_data |= I82580_CFG_ASSERT_CRS_ON_TX; + + /* Enable downshift */ + phy_data |= I82580_CFG_ENABLE_DOWNSHIFT; + + ret_val = phy->ops.write_reg(hw, I82580_CFG_REG, phy_data); + if (ret_val) + goto out; + + /* Set number of link attempts before downshift */ + ret_val = phy->ops.read_reg(hw, I82580_CTRL_REG, &phy_data); + if (ret_val) + goto out; + phy_data &= ~I82580_CTRL_DOWNSHIFT_MASK; + ret_val = phy->ops.write_reg(hw, I82580_CTRL_REG, phy_data); + +out: + return ret_val; +} + +/** * igb_copper_link_setup_m88 - Setup m88 PHY's for copper link * @hw: pointer to the HW structure * @@ -572,7 +726,7 @@ out: * and restart the negotiation process between the link partner. If * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. **/ -s32 igb_copper_link_autoneg(struct e1000_hw *hw) +static s32 igb_copper_link_autoneg(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; s32 ret_val; @@ -796,6 +950,65 @@ out: } /** + * igb_setup_copper_link - Configure copper link settings + * @hw: pointer to the HW structure + * + * Calls the appropriate function to configure the link for auto-neg or forced + * speed and duplex. Then we check for link, once link is established calls + * to configure collision distance and flow control are called. If link is + * not established, we return -E1000_ERR_PHY (-2). + **/ +s32 igb_setup_copper_link(struct e1000_hw *hw) +{ + s32 ret_val; + bool link; + + + if (hw->mac.autoneg) { + /* + * Setup autoneg and flow control advertisement and perform + * autonegotiation. + */ + ret_val = igb_copper_link_autoneg(hw); + if (ret_val) + goto out; + } else { + /* + * PHY will be set to 10H, 10F, 100H or 100F + * depending on user settings. + */ + hw_dbg("Forcing Speed and Duplex\n"); + ret_val = hw->phy.ops.force_speed_duplex(hw); + if (ret_val) { + hw_dbg("Error Forcing Speed and Duplex\n"); + goto out; + } + } + + /* + * Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + ret_val = igb_phy_has_link(hw, + COPPER_LINK_UP_LIMIT, + 10, + &link); + if (ret_val) + goto out; + + if (link) { + hw_dbg("Valid link established!!!\n"); + igb_config_collision_dist(hw); + ret_val = igb_config_fc_after_link_up(hw); + } else { + hw_dbg("Unable to establish link!!!\n"); + } + +out: + return ret_val; +} + +/** * igb_phy_force_speed_duplex_igp - Force speed/duplex for igp PHY * @hw: pointer to the HW structure * @@ -903,22 +1116,19 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw) igb_phy_force_speed_duplex_setup(hw, &phy_data); - /* Reset the phy to commit changes. */ - phy_data |= MII_CR_RESET; - ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); if (ret_val) goto out; - udelay(1); + /* Reset the phy to commit changes. */ + ret_val = igb_phy_sw_reset(hw); + if (ret_val) + goto out; if (phy->autoneg_wait_to_complete) { hw_dbg("Waiting for forced speed/duplex link on M88 phy.\n"); - ret_val = igb_phy_has_link(hw, - PHY_FORCE_LIMIT, - 100000, - &link); + ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) goto out; @@ -928,8 +1138,8 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw) * Reset the DSP and cross our fingers. */ ret_val = phy->ops.write_reg(hw, - M88E1000_PHY_PAGE_SELECT, - 0x001d); + M88E1000_PHY_PAGE_SELECT, + 0x001d); if (ret_val) goto out; ret_val = igb_phy_reset_dsp(hw); @@ -939,7 +1149,7 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw) /* Try once more */ ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, - 100000, &link); + 100000, &link); if (ret_val) goto out; } @@ -1051,9 +1261,12 @@ static void igb_phy_force_speed_duplex_setup(struct e1000_hw *hw, s32 igb_set_d3_lplu_state(struct e1000_hw *hw, bool active) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val; + s32 ret_val = 0; u16 data; + if (!(hw->phy.ops.read_reg)) + goto out; + ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); if (ret_val) goto out; @@ -1288,8 +1501,14 @@ s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations, * it across the board. */ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); - if (ret_val) - break; + if (ret_val) { + /* + * If the first read fails, another entity may have + * ownership of the resources, wait and try again to + * see if they have relinquished the resources yet. + */ + udelay(usec_interval); + } ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); if (ret_val) break; @@ -1333,8 +1552,13 @@ s32 igb_get_cable_length_m88(struct e1000_hw *hw) index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> M88E1000_PSSR_CABLE_LENGTH_SHIFT; + if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) { + ret_val = -E1000_ERR_PHY; + goto out; + } + phy->min_cable_length = e1000_m88_cable_length_table[index]; - phy->max_cable_length = e1000_m88_cable_length_table[index+1]; + phy->max_cable_length = e1000_m88_cable_length_table[index + 1]; phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; @@ -1715,3 +1939,194 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw) return 0; } +/** + * igb_check_polarity_82580 - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY specific status register. + **/ +static s32 igb_check_polarity_82580(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + + ret_val = phy->ops.read_reg(hw, I82580_PHY_STATUS_2, &data); + + if (!ret_val) + phy->cable_polarity = (data & I82580_PHY_STATUS2_REV_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal; + + return ret_val; +} + +/** + * igb_phy_force_speed_duplex_82580 - Force speed/duplex for I82580 PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. Clears the + * auto-crossover to force MDI manually. Waits for link and returns + * successful if link up is successful, else -E1000_ERR_PHY (-2). + **/ +s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + goto out; + + igb_phy_force_speed_duplex_setup(hw, &phy_data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + goto out; + + /* + * Clear Auto-Crossover to force MDI manually. 82580 requires MDI + * forced whenever speed and duplex are forced. + */ + ret_val = phy->ops.read_reg(hw, I82580_PHY_CTRL_2, &phy_data); + if (ret_val) + goto out; + + phy_data &= ~I82580_PHY_CTRL2_AUTO_MDIX; + phy_data &= ~I82580_PHY_CTRL2_FORCE_MDI_MDIX; + + ret_val = phy->ops.write_reg(hw, I82580_PHY_CTRL_2, phy_data); + if (ret_val) + goto out; + + hw_dbg("I82580_PHY_CTRL_2: %X\n", phy_data); + + udelay(1); + + if (phy->autoneg_wait_to_complete) { + hw_dbg("Waiting for forced speed/duplex link on 82580 phy\n"); + + ret_val = igb_phy_has_link(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + + if (!link) + hw_dbg("Link taking longer than expected.\n"); + + /* Try once more */ + ret_val = igb_phy_has_link(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * igb_get_phy_info_82580 - Retrieve I82580 PHY information + * @hw: pointer to the HW structure + * + * Read PHY status to determine if link is up. If link is up, then + * set/determine 10base-T extended distance and polarity correction. Read + * PHY port status to determine MDI/MDIx and speed. Based on the speed, + * determine on the cable length, local and remote receiver. + **/ +s32 igb_get_phy_info_82580(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + + ret_val = igb_phy_has_link(hw, 1, 0, &link); + if (ret_val) + goto out; + + if (!link) { + hw_dbg("Phy info is only valid if link is up\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + phy->polarity_correction = true; + + ret_val = igb_check_polarity_82580(hw); + if (ret_val) + goto out; + + ret_val = phy->ops.read_reg(hw, I82580_PHY_STATUS_2, &data); + if (ret_val) + goto out; + + phy->is_mdix = (data & I82580_PHY_STATUS2_MDIX) ? true : false; + + if ((data & I82580_PHY_STATUS2_SPEED_MASK) == + I82580_PHY_STATUS2_SPEED_1000MBPS) { + ret_val = hw->phy.ops.get_cable_length(hw); + if (ret_val) + goto out; + + ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data); + if (ret_val) + goto out; + + phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + +out: + return ret_val; +} + +/** + * igb_get_cable_length_82580 - Determine cable length for 82580 PHY + * @hw: pointer to the HW structure + * + * Reads the diagnostic status register and verifies result is valid before + * placing it in the phy_cable_length field. + **/ +s32 igb_get_cable_length_82580(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, length; + + + ret_val = phy->ops.read_reg(hw, I82580_PHY_DIAG_STATUS, &phy_data); + if (ret_val) + goto out; + + length = (phy_data & I82580_DSTATUS_CABLE_LENGTH) >> + I82580_DSTATUS_CABLE_LENGTH_SHIFT; + + if (length == E1000_CABLE_LENGTH_UNDEFINED) + ret_val = -E1000_ERR_PHY; + + phy->cable_length = length; + +out: + return ret_val; +} diff --git a/drivers/net/igb/e1000_phy.h b/drivers/net/igb/e1000_phy.h index ebe4b616db8a..555eb54bb6ed 100644 --- a/drivers/net/igb/e1000_phy.h +++ b/drivers/net/igb/e1000_phy.h @@ -43,7 +43,6 @@ enum e1000_smart_speed { s32 igb_check_downshift(struct e1000_hw *hw); s32 igb_check_reset_block(struct e1000_hw *hw); -s32 igb_copper_link_autoneg(struct e1000_hw *hw); s32 igb_copper_link_setup_igp(struct e1000_hw *hw); s32 igb_copper_link_setup_m88(struct e1000_hw *hw); s32 igb_phy_force_speed_duplex_igp(struct e1000_hw *hw); @@ -57,10 +56,19 @@ s32 igb_phy_sw_reset(struct e1000_hw *hw); s32 igb_phy_hw_reset(struct e1000_hw *hw); s32 igb_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data); s32 igb_set_d3_lplu_state(struct e1000_hw *hw, bool active); +s32 igb_setup_copper_link(struct e1000_hw *hw); s32 igb_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data); s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations, u32 usec_interval, bool *success); s32 igb_phy_init_script_igp3(struct e1000_hw *hw); +s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); +s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); +s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data); +s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data); +s32 igb_copper_link_setup_82580(struct e1000_hw *hw); +s32 igb_get_phy_info_82580(struct e1000_hw *hw); +s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw); +s32 igb_get_cable_length_82580(struct e1000_hw *hw); /* IGP01E1000 Specific Registers */ #define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */ @@ -75,6 +83,33 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw); #define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0=MDI, 1=MDIX */ #define IGP01E1000_PSCFR_SMART_SPEED 0x0080 +#define I82580_ADDR_REG 16 +#define I82580_CFG_REG 22 +#define I82580_CFG_ASSERT_CRS_ON_TX (1 << 15) +#define I82580_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift 100/10 */ +#define I82580_CTRL_REG 23 +#define I82580_CTRL_DOWNSHIFT_MASK (7 << 10) + +/* 82580 specific PHY registers */ +#define I82580_PHY_CTRL_2 18 +#define I82580_PHY_LBK_CTRL 19 +#define I82580_PHY_STATUS_2 26 +#define I82580_PHY_DIAG_STATUS 31 + +/* I82580 PHY Status 2 */ +#define I82580_PHY_STATUS2_REV_POLARITY 0x0400 +#define I82580_PHY_STATUS2_MDIX 0x0800 +#define I82580_PHY_STATUS2_SPEED_MASK 0x0300 +#define I82580_PHY_STATUS2_SPEED_1000MBPS 0x0200 +#define I82580_PHY_STATUS2_SPEED_100MBPS 0x0100 + +/* I82580 PHY Control 2 */ +#define I82580_PHY_CTRL2_AUTO_MDIX 0x0400 +#define I82580_PHY_CTRL2_FORCE_MDI_MDIX 0x0200 + +/* I82580 PHY Diagnostics Status */ +#define I82580_DSTATUS_CABLE_LENGTH 0x03FC +#define I82580_DSTATUS_CABLE_LENGTH_SHIFT 2 /* Enable flexible speed on link-up */ #define IGP02E1000_PM_D0_LPLU 0x0002 /* For D0a states */ #define IGP02E1000_PM_D3_LPLU 0x0004 /* For all other states */ diff --git a/drivers/net/igb/e1000_regs.h b/drivers/net/igb/e1000_regs.h index 345d1442d6d6..dd4e6ffd29f5 100644 --- a/drivers/net/igb/e1000_regs.h +++ b/drivers/net/igb/e1000_regs.h @@ -34,6 +34,7 @@ #define E1000_EERD 0x00014 /* EEPROM Read - RW */ #define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ #define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_MDICNFG 0x00E04 /* MDI Config - RW */ #define E1000_SCTL 0x00024 /* SerDes Control - RW */ #define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ #define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ @@ -76,59 +77,20 @@ #define E1000_FCRTV 0x02460 /* Flow Control Refresh Timer Value - RW */ /* IEEE 1588 TIMESYNCH */ -#define E1000_TSYNCTXCTL 0x0B614 -#define E1000_TSYNCTXCTL_VALID (1<<0) -#define E1000_TSYNCTXCTL_ENABLED (1<<4) -#define E1000_TSYNCRXCTL 0x0B620 -#define E1000_TSYNCRXCTL_VALID (1<<0) -#define E1000_TSYNCRXCTL_ENABLED (1<<4) -enum { - E1000_TSYNCRXCTL_TYPE_L2_V2 = 0, - E1000_TSYNCRXCTL_TYPE_L4_V1 = (1<<1), - E1000_TSYNCRXCTL_TYPE_L2_L4_V2 = (1<<2), - E1000_TSYNCRXCTL_TYPE_ALL = (1<<3), - E1000_TSYNCRXCTL_TYPE_EVENT_V2 = (1<<3) | (1<<1), -}; -#define E1000_TSYNCRXCFG 0x05F50 -enum { - E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE = 0<<0, - E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE = 1<<0, - E1000_TSYNCRXCFG_PTP_V1_FOLLOWUP_MESSAGE = 2<<0, - E1000_TSYNCRXCFG_PTP_V1_DELAY_RESP_MESSAGE = 3<<0, - E1000_TSYNCRXCFG_PTP_V1_MANAGEMENT_MESSAGE = 4<<0, - - E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE = 0<<8, - E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE = 1<<8, - E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_REQ_MESSAGE = 2<<8, - E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_RESP_MESSAGE = 3<<8, - E1000_TSYNCRXCFG_PTP_V2_FOLLOWUP_MESSAGE = 8<<8, - E1000_TSYNCRXCFG_PTP_V2_DELAY_RESP_MESSAGE = 9<<8, - E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_FOLLOWUP_MESSAGE = 0xA<<8, - E1000_TSYNCRXCFG_PTP_V2_ANNOUNCE_MESSAGE = 0xB<<8, - E1000_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE = 0xC<<8, - E1000_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE = 0xD<<8, -}; -#define E1000_SYSTIML 0x0B600 -#define E1000_SYSTIMH 0x0B604 -#define E1000_TIMINCA 0x0B608 - -#define E1000_RXMTRL 0x0B634 -#define E1000_RXSTMPL 0x0B624 -#define E1000_RXSTMPH 0x0B628 -#define E1000_RXSATRL 0x0B62C -#define E1000_RXSATRH 0x0B630 - -#define E1000_TXSTMPL 0x0B618 -#define E1000_TXSTMPH 0x0B61C - -#define E1000_ETQF0 0x05CB0 -#define E1000_ETQF1 0x05CB4 -#define E1000_ETQF2 0x05CB8 -#define E1000_ETQF3 0x05CBC -#define E1000_ETQF4 0x05CC0 -#define E1000_ETQF5 0x05CC4 -#define E1000_ETQF6 0x05CC8 -#define E1000_ETQF7 0x05CCC +#define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */ +#define E1000_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */ +#define E1000_TSYNCRXCFG 0x05F50 /* Time Sync Rx Configuration - RW */ +#define E1000_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */ +#define E1000_RXSTMPH 0x0B628 /* Rx timestamp High - RO */ +#define E1000_RXSATRL 0x0B62C /* Rx timestamp attribute low - RO */ +#define E1000_RXSATRH 0x0B630 /* Rx timestamp attribute high - RO */ +#define E1000_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */ +#define E1000_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */ +#define E1000_SYSTIML 0x0B600 /* System time register Low - RO */ +#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */ +#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ +#define E1000_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */ +#define E1000_SYSTIMR 0x0B6F8 /* System time register Residue */ /* Filtering Registers */ #define E1000_SAQF(_n) (0x5980 + 4 * (_n)) @@ -143,7 +105,9 @@ enum { #define E1000_ETQF(_n) (0x05CB0 + (4 * (_n))) /* EType Queue Fltr */ #define E1000_RQDPC(_n) (0x0C030 + ((_n) * 0x40)) + /* Split and Replication RX Control - RW */ +#define E1000_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */ /* * Convenience macros * @@ -288,10 +252,17 @@ enum { #define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ #define E1000_RA 0x05400 /* Receive Address - RW Array */ #define E1000_RA2 0x054E0 /* 2nd half of receive address array - RW Array */ +#define E1000_PSRTYPE(_i) (0x05480 + ((_i) * 4)) #define E1000_RAL(_i) (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \ (0x054E0 + ((_i - 16) * 8))) #define E1000_RAH(_i) (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \ (0x054E4 + ((_i - 16) * 8))) +#define E1000_IP4AT_REG(_i) (0x05840 + ((_i) * 8)) +#define E1000_IP6AT_REG(_i) (0x05880 + ((_i) * 4)) +#define E1000_WUPM_REG(_i) (0x05A00 + ((_i) * 4)) +#define E1000_FFMT_REG(_i) (0x09000 + ((_i) * 8)) +#define E1000_FFVT_REG(_i) (0x09800 + ((_i) * 8)) +#define E1000_FFLT_REG(_i) (0x05F00 + ((_i) * 8)) #define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ #define E1000_VT_CTL 0x0581C /* VMDq Control - RW */ #define E1000_WUC 0x05800 /* Wakeup Control - RW */ @@ -331,6 +302,7 @@ enum { #define E1000_QDE 0x02408 /* Queue Drop Enable - RW */ #define E1000_DTXSWC 0x03500 /* DMA Tx Switch Control - RW */ #define E1000_RPLOLR 0x05AF0 /* Replication Offload - RW */ +#define E1000_UTA 0x0A000 /* Unicast Table Array - RW */ #define E1000_IOVTCL 0x05BBC /* IOV Control Register */ /* These act per VF so an array friendly macro is used */ #define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n))) @@ -348,4 +320,6 @@ enum { #define array_rd32(reg, offset) \ (readl(hw->hw_addr + reg + ((offset) << 2))) +/* DMA Coalescing registers */ +#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */ #endif diff --git a/drivers/net/igb/igb.h b/drivers/net/igb/igb.h index 7126fea26fec..b1c1eb88893f 100644 --- a/drivers/net/igb/igb.h +++ b/drivers/net/igb/igb.h @@ -55,12 +55,14 @@ struct igb_adapter; #define IGB_DEFAULT_ITR 3 /* dynamic */ #define IGB_MAX_ITR_USECS 10000 #define IGB_MIN_ITR_USECS 10 +#define NON_Q_VECTORS 1 +#define MAX_Q_VECTORS 8 /* Transmit and receive queues */ -#define IGB_MAX_RX_QUEUES (adapter->vfs_allocated_count ? \ - (adapter->vfs_allocated_count > 6 ? 1 : 2) : 4) -#define IGB_MAX_TX_QUEUES IGB_MAX_RX_QUEUES -#define IGB_ABS_MAX_TX_QUEUES 4 +#define IGB_MAX_RX_QUEUES (adapter->vfs_allocated_count ? 2 : \ + (hw->mac.type > e1000_82575 ? 8 : 4)) +#define IGB_ABS_MAX_TX_QUEUES 8 +#define IGB_MAX_TX_QUEUES IGB_MAX_RX_QUEUES #define IGB_MAX_VF_MC_ENTRIES 30 #define IGB_MAX_VF_FUNCTIONS 8 @@ -71,9 +73,14 @@ struct vf_data_storage { u16 vf_mc_hashes[IGB_MAX_VF_MC_ENTRIES]; u16 num_vf_mc_hashes; u16 vlans_enabled; - bool clear_to_send; + u32 flags; + unsigned long last_nack; }; +#define IGB_VF_FLAG_CTS 0x00000001 /* VF is clear to send data */ +#define IGB_VF_FLAG_UNI_PROMISC 0x00000002 /* VF has unicast promisc */ +#define IGB_VF_FLAG_MULTI_PROMISC 0x00000004 /* VF has multicast promisc */ + /* RX descriptor control thresholds. * PTHRESH - MAC will consider prefetch if it has fewer than this number of * descriptors available in its onboard memory. @@ -85,17 +92,19 @@ struct vf_data_storage { * descriptors until either it has this many to write back, or the * ITR timer expires. */ -#define IGB_RX_PTHRESH 16 +#define IGB_RX_PTHRESH (hw->mac.type <= e1000_82576 ? 16 : 8) #define IGB_RX_HTHRESH 8 #define IGB_RX_WTHRESH 1 +#define IGB_TX_PTHRESH 8 +#define IGB_TX_HTHRESH 1 +#define IGB_TX_WTHRESH ((hw->mac.type == e1000_82576 && \ + adapter->msix_entries) ? 0 : 16) /* this is the size past which hardware will drop packets when setting LPE=0 */ #define MAXIMUM_ETHERNET_VLAN_SIZE 1522 /* Supported Rx Buffer Sizes */ #define IGB_RXBUFFER_128 128 /* Used for packet split */ -#define IGB_RXBUFFER_256 256 /* Used for packet split */ -#define IGB_RXBUFFER_512 512 #define IGB_RXBUFFER_1024 1024 #define IGB_RXBUFFER_2048 2048 #define IGB_RXBUFFER_16384 16384 @@ -128,12 +137,13 @@ struct igb_buffer { unsigned long time_stamp; u16 length; u16 next_to_watch; + u16 mapped_as_page; }; /* RX */ struct { struct page *page; - u64 page_dma; - unsigned int page_offset; + dma_addr_t page_dma; + u16 page_offset; }; }; }; @@ -141,36 +151,55 @@ struct igb_buffer { struct igb_tx_queue_stats { u64 packets; u64 bytes; + u64 restart_queue; }; struct igb_rx_queue_stats { u64 packets; u64 bytes; u64 drops; + u64 csum_err; + u64 alloc_failed; }; -struct igb_ring { +struct igb_q_vector { struct igb_adapter *adapter; /* backlink */ - void *desc; /* descriptor ring memory */ - dma_addr_t dma; /* phys address of the ring */ - unsigned int size; /* length of desc. ring in bytes */ - unsigned int count; /* number of desc. in the ring */ - u16 next_to_use; - u16 next_to_clean; - u16 head; - u16 tail; - struct igb_buffer *buffer_info; /* array of buffer info structs */ + struct igb_ring *rx_ring; + struct igb_ring *tx_ring; + struct napi_struct napi; u32 eims_value; - u32 itr_val; - u16 itr_register; u16 cpu; - u16 queue_index; - u16 reg_idx; + u16 itr_val; + u8 set_itr; + u8 itr_shift; + void __iomem *itr_register; + + char name[IFNAMSIZ + 9]; +}; + +struct igb_ring { + struct igb_q_vector *q_vector; /* backlink to q_vector */ + struct net_device *netdev; /* back pointer to net_device */ + struct pci_dev *pdev; /* pci device for dma mapping */ + dma_addr_t dma; /* phys address of the ring */ + void *desc; /* descriptor ring memory */ + unsigned int size; /* length of desc. ring in bytes */ + u16 count; /* number of desc. in the ring */ + u16 next_to_use; + u16 next_to_clean; + u8 queue_index; + u8 reg_idx; + void __iomem *head; + void __iomem *tail; + struct igb_buffer *buffer_info; /* array of buffer info structs */ + unsigned int total_bytes; unsigned int total_packets; + u32 flags; + union { /* TX */ struct { @@ -180,16 +209,18 @@ struct igb_ring { /* RX */ struct { struct igb_rx_queue_stats rx_stats; - u64 rx_queue_drops; - struct napi_struct napi; - int set_itr; - struct igb_ring *buddy; + u32 rx_buffer_len; }; }; - - char name[IFNAMSIZ + 5]; }; +#define IGB_RING_FLAG_RX_CSUM 0x00000001 /* RX CSUM enabled */ +#define IGB_RING_FLAG_RX_SCTP_CSUM 0x00000002 /* SCTP CSUM offload enabled */ + +#define IGB_RING_FLAG_TX_CTX_IDX 0x00000001 /* HW requires context index */ + +#define IGB_ADVTXD_DCMD (E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS) + #define E1000_RX_DESC_ADV(R, i) \ (&(((union e1000_adv_rx_desc *)((R).desc))[i])) #define E1000_TX_DESC_ADV(R, i) \ @@ -197,6 +228,15 @@ struct igb_ring { #define E1000_TX_CTXTDESC_ADV(R, i) \ (&(((struct e1000_adv_tx_context_desc *)((R).desc))[i])) +/* igb_desc_unused - calculate if we have unused descriptors */ +static inline int igb_desc_unused(struct igb_ring *ring) +{ + if (ring->next_to_clean > ring->next_to_use) + return ring->next_to_clean - ring->next_to_use - 1; + + return ring->count + ring->next_to_clean - ring->next_to_use - 1; +} + /* board specific private data structure */ struct igb_adapter { @@ -205,18 +245,14 @@ struct igb_adapter { struct vlan_group *vlgrp; u16 mng_vlan_id; u32 bd_number; - u32 rx_buffer_len; u32 wol; u32 en_mng_pt; u16 link_speed; u16 link_duplex; - unsigned int total_tx_bytes; - unsigned int total_tx_packets; - unsigned int total_rx_bytes; - unsigned int total_rx_packets; + /* Interrupt Throttle Rate */ - u32 itr; - u32 itr_setting; + u32 rx_itr_setting; + u32 tx_itr_setting; u16 tx_itr; u16 rx_itr; @@ -229,13 +265,7 @@ struct igb_adapter { /* TX */ struct igb_ring *tx_ring; /* One per active queue */ - unsigned int restart_queue; unsigned long tx_queue_len; - u32 txd_cmd; - u32 gotc; - u64 gotc_old; - u64 tpt_old; - u64 colc_old; u32 tx_timeout_count; /* RX */ @@ -243,20 +273,12 @@ struct igb_adapter { int num_tx_queues; int num_rx_queues; - u64 hw_csum_err; - u64 hw_csum_good; - u32 alloc_rx_buff_failed; - u32 gorc; - u64 gorc_old; - u16 rx_ps_hdr_size; u32 max_frame_size; u32 min_frame_size; /* OS defined structs */ struct net_device *netdev; - struct napi_struct napi; struct pci_dev *pdev; - struct net_device_stats net_stats; struct cyclecounter cycles; struct timecounter clock; struct timecompare compare; @@ -273,6 +295,9 @@ struct igb_adapter { struct igb_ring test_rx_ring; int msg_enable; + + unsigned int num_q_vectors; + struct igb_q_vector *q_vector[MAX_Q_VECTORS]; struct msix_entry *msix_entries; u32 eims_enable_mask; u32 eims_other; @@ -283,18 +308,20 @@ struct igb_adapter { u32 eeprom_wol; struct igb_ring *multi_tx_table[IGB_ABS_MAX_TX_QUEUES]; - unsigned int tx_ring_count; - unsigned int rx_ring_count; + u16 tx_ring_count; + u16 rx_ring_count; unsigned int vfs_allocated_count; struct vf_data_storage *vf_data; + u32 rss_queues; }; #define IGB_FLAG_HAS_MSI (1 << 0) #define IGB_FLAG_DCA_ENABLED (1 << 1) #define IGB_FLAG_QUAD_PORT_A (1 << 2) -#define IGB_FLAG_NEED_CTX_IDX (1 << 3) -#define IGB_FLAG_RX_CSUM_DISABLED (1 << 4) +#define IGB_FLAG_QUEUE_PAIRS (1 << 3) +#define IGB_82576_TSYNC_SHIFT 19 +#define IGB_82580_TSYNC_SHIFT 24 enum e1000_state_t { __IGB_TESTING, __IGB_RESETTING, @@ -314,10 +341,18 @@ extern void igb_down(struct igb_adapter *); extern void igb_reinit_locked(struct igb_adapter *); extern void igb_reset(struct igb_adapter *); extern int igb_set_spd_dplx(struct igb_adapter *, u16); -extern int igb_setup_tx_resources(struct igb_adapter *, struct igb_ring *); -extern int igb_setup_rx_resources(struct igb_adapter *, struct igb_ring *); +extern int igb_setup_tx_resources(struct igb_ring *); +extern int igb_setup_rx_resources(struct igb_ring *); extern void igb_free_tx_resources(struct igb_ring *); extern void igb_free_rx_resources(struct igb_ring *); +extern void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *); +extern void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *); +extern void igb_setup_tctl(struct igb_adapter *); +extern void igb_setup_rctl(struct igb_adapter *); +extern netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *, struct igb_ring *); +extern void igb_unmap_and_free_tx_resource(struct igb_ring *, + struct igb_buffer *); +extern void igb_alloc_rx_buffers_adv(struct igb_ring *, int); extern void igb_update_stats(struct igb_adapter *); extern void igb_set_ethtool_ops(struct net_device *); diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c index b243ed3b0c36..ac9d5272650d 100644 --- a/drivers/net/igb/igb_ethtool.c +++ b/drivers/net/igb/igb_ethtool.c @@ -44,78 +44,94 @@ struct igb_stats { int stat_offset; }; -#define IGB_STAT(m) FIELD_SIZEOF(struct igb_adapter, m), \ - offsetof(struct igb_adapter, m) +#define IGB_STAT(_name, _stat) { \ + .stat_string = _name, \ + .sizeof_stat = FIELD_SIZEOF(struct igb_adapter, _stat), \ + .stat_offset = offsetof(struct igb_adapter, _stat) \ +} static const struct igb_stats igb_gstrings_stats[] = { - { "rx_packets", IGB_STAT(stats.gprc) }, - { "tx_packets", IGB_STAT(stats.gptc) }, - { "rx_bytes", IGB_STAT(stats.gorc) }, - { "tx_bytes", IGB_STAT(stats.gotc) }, - { "rx_broadcast", IGB_STAT(stats.bprc) }, - { "tx_broadcast", IGB_STAT(stats.bptc) }, - { "rx_multicast", IGB_STAT(stats.mprc) }, - { "tx_multicast", IGB_STAT(stats.mptc) }, - { "rx_errors", IGB_STAT(net_stats.rx_errors) }, - { "tx_errors", IGB_STAT(net_stats.tx_errors) }, - { "tx_dropped", IGB_STAT(net_stats.tx_dropped) }, - { "multicast", IGB_STAT(stats.mprc) }, - { "collisions", IGB_STAT(stats.colc) }, - { "rx_length_errors", IGB_STAT(net_stats.rx_length_errors) }, - { "rx_over_errors", IGB_STAT(net_stats.rx_over_errors) }, - { "rx_crc_errors", IGB_STAT(stats.crcerrs) }, - { "rx_frame_errors", IGB_STAT(net_stats.rx_frame_errors) }, - { "rx_no_buffer_count", IGB_STAT(stats.rnbc) }, - { "rx_queue_drop_packet_count", IGB_STAT(net_stats.rx_fifo_errors) }, - { "rx_missed_errors", IGB_STAT(stats.mpc) }, - { "tx_aborted_errors", IGB_STAT(stats.ecol) }, - { "tx_carrier_errors", IGB_STAT(stats.tncrs) }, - { "tx_fifo_errors", IGB_STAT(net_stats.tx_fifo_errors) }, - { "tx_heartbeat_errors", IGB_STAT(net_stats.tx_heartbeat_errors) }, - { "tx_window_errors", IGB_STAT(stats.latecol) }, - { "tx_abort_late_coll", IGB_STAT(stats.latecol) }, - { "tx_deferred_ok", IGB_STAT(stats.dc) }, - { "tx_single_coll_ok", IGB_STAT(stats.scc) }, - { "tx_multi_coll_ok", IGB_STAT(stats.mcc) }, - { "tx_timeout_count", IGB_STAT(tx_timeout_count) }, - { "tx_restart_queue", IGB_STAT(restart_queue) }, - { "rx_long_length_errors", IGB_STAT(stats.roc) }, - { "rx_short_length_errors", IGB_STAT(stats.ruc) }, - { "rx_align_errors", IGB_STAT(stats.algnerrc) }, - { "tx_tcp_seg_good", IGB_STAT(stats.tsctc) }, - { "tx_tcp_seg_failed", IGB_STAT(stats.tsctfc) }, - { "rx_flow_control_xon", IGB_STAT(stats.xonrxc) }, - { "rx_flow_control_xoff", IGB_STAT(stats.xoffrxc) }, - { "tx_flow_control_xon", IGB_STAT(stats.xontxc) }, - { "tx_flow_control_xoff", IGB_STAT(stats.xofftxc) }, - { "rx_long_byte_count", IGB_STAT(stats.gorc) }, - { "rx_csum_offload_good", IGB_STAT(hw_csum_good) }, - { "rx_csum_offload_errors", IGB_STAT(hw_csum_err) }, - { "tx_dma_out_of_sync", IGB_STAT(stats.doosync) }, - { "alloc_rx_buff_failed", IGB_STAT(alloc_rx_buff_failed) }, - { "tx_smbus", IGB_STAT(stats.mgptc) }, - { "rx_smbus", IGB_STAT(stats.mgprc) }, - { "dropped_smbus", IGB_STAT(stats.mgpdc) }, + IGB_STAT("rx_packets", stats.gprc), + IGB_STAT("tx_packets", stats.gptc), + IGB_STAT("rx_bytes", stats.gorc), + IGB_STAT("tx_bytes", stats.gotc), + IGB_STAT("rx_broadcast", stats.bprc), + IGB_STAT("tx_broadcast", stats.bptc), + IGB_STAT("rx_multicast", stats.mprc), + IGB_STAT("tx_multicast", stats.mptc), + IGB_STAT("multicast", stats.mprc), + IGB_STAT("collisions", stats.colc), + IGB_STAT("rx_crc_errors", stats.crcerrs), + IGB_STAT("rx_no_buffer_count", stats.rnbc), + IGB_STAT("rx_missed_errors", stats.mpc), + IGB_STAT("tx_aborted_errors", stats.ecol), + IGB_STAT("tx_carrier_errors", stats.tncrs), + IGB_STAT("tx_window_errors", stats.latecol), + IGB_STAT("tx_abort_late_coll", stats.latecol), + IGB_STAT("tx_deferred_ok", stats.dc), + IGB_STAT("tx_single_coll_ok", stats.scc), + IGB_STAT("tx_multi_coll_ok", stats.mcc), + IGB_STAT("tx_timeout_count", tx_timeout_count), + IGB_STAT("rx_long_length_errors", stats.roc), + IGB_STAT("rx_short_length_errors", stats.ruc), + IGB_STAT("rx_align_errors", stats.algnerrc), + IGB_STAT("tx_tcp_seg_good", stats.tsctc), + IGB_STAT("tx_tcp_seg_failed", stats.tsctfc), + IGB_STAT("rx_flow_control_xon", stats.xonrxc), + IGB_STAT("rx_flow_control_xoff", stats.xoffrxc), + IGB_STAT("tx_flow_control_xon", stats.xontxc), + IGB_STAT("tx_flow_control_xoff", stats.xofftxc), + IGB_STAT("rx_long_byte_count", stats.gorc), + IGB_STAT("tx_dma_out_of_sync", stats.doosync), + IGB_STAT("tx_smbus", stats.mgptc), + IGB_STAT("rx_smbus", stats.mgprc), + IGB_STAT("dropped_smbus", stats.mgpdc), +}; + +#define IGB_NETDEV_STAT(_net_stat) { \ + .stat_string = __stringify(_net_stat), \ + .sizeof_stat = FIELD_SIZEOF(struct net_device_stats, _net_stat), \ + .stat_offset = offsetof(struct net_device_stats, _net_stat) \ +} +static const struct igb_stats igb_gstrings_net_stats[] = { + IGB_NETDEV_STAT(rx_errors), + IGB_NETDEV_STAT(tx_errors), + IGB_NETDEV_STAT(tx_dropped), + IGB_NETDEV_STAT(rx_length_errors), + IGB_NETDEV_STAT(rx_over_errors), + IGB_NETDEV_STAT(rx_frame_errors), + IGB_NETDEV_STAT(rx_fifo_errors), + IGB_NETDEV_STAT(tx_fifo_errors), + IGB_NETDEV_STAT(tx_heartbeat_errors) }; -#define IGB_QUEUE_STATS_LEN \ - (((((struct igb_adapter *)netdev_priv(netdev))->num_rx_queues)* \ - (sizeof(struct igb_rx_queue_stats) / sizeof(u64))) + \ - ((((struct igb_adapter *)netdev_priv(netdev))->num_tx_queues) * \ - (sizeof(struct igb_tx_queue_stats) / sizeof(u64)))) #define IGB_GLOBAL_STATS_LEN \ - sizeof(igb_gstrings_stats) / sizeof(struct igb_stats) -#define IGB_STATS_LEN (IGB_GLOBAL_STATS_LEN + IGB_QUEUE_STATS_LEN) + (sizeof(igb_gstrings_stats) / sizeof(struct igb_stats)) +#define IGB_NETDEV_STATS_LEN \ + (sizeof(igb_gstrings_net_stats) / sizeof(struct igb_stats)) +#define IGB_RX_QUEUE_STATS_LEN \ + (sizeof(struct igb_rx_queue_stats) / sizeof(u64)) +#define IGB_TX_QUEUE_STATS_LEN \ + (sizeof(struct igb_tx_queue_stats) / sizeof(u64)) +#define IGB_QUEUE_STATS_LEN \ + ((((struct igb_adapter *)netdev_priv(netdev))->num_rx_queues * \ + IGB_RX_QUEUE_STATS_LEN) + \ + (((struct igb_adapter *)netdev_priv(netdev))->num_tx_queues * \ + IGB_TX_QUEUE_STATS_LEN)) +#define IGB_STATS_LEN \ + (IGB_GLOBAL_STATS_LEN + IGB_NETDEV_STATS_LEN + IGB_QUEUE_STATS_LEN) + static const char igb_gstrings_test[][ETH_GSTRING_LEN] = { "Register test (offline)", "Eeprom test (offline)", "Interrupt test (offline)", "Loopback test (offline)", "Link test (on/offline)" }; -#define IGB_TEST_LEN sizeof(igb_gstrings_test) / ETH_GSTRING_LEN +#define IGB_TEST_LEN (sizeof(igb_gstrings_test) / ETH_GSTRING_LEN) static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + u32 status; if (hw->phy.media_type == e1000_media_type_copper) { @@ -150,17 +166,20 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->transceiver = XCVR_INTERNAL; - if (rd32(E1000_STATUS) & E1000_STATUS_LU) { + status = rd32(E1000_STATUS); - adapter->hw.mac.ops.get_speed_and_duplex(hw, - &adapter->link_speed, - &adapter->link_duplex); - ecmd->speed = adapter->link_speed; + if (status & E1000_STATUS_LU) { - /* unfortunately FULL_DUPLEX != DUPLEX_FULL - * and HALF_DUPLEX != DUPLEX_HALF */ + if ((status & E1000_STATUS_SPEED_1000) || + hw->phy.media_type != e1000_media_type_copper) + ecmd->speed = SPEED_1000; + else if (status & E1000_STATUS_SPEED_100) + ecmd->speed = SPEED_100; + else + ecmd->speed = SPEED_10; - if (adapter->link_duplex == FULL_DUPLEX) + if ((status & E1000_STATUS_FD) || + hw->phy.media_type != e1000_media_type_copper) ecmd->duplex = DUPLEX_FULL; else ecmd->duplex = DUPLEX_HALF; @@ -251,8 +270,9 @@ static int igb_set_pauseparam(struct net_device *netdev, if (netif_running(adapter->netdev)) { igb_down(adapter); igb_up(adapter); - } else + } else { igb_reset(adapter); + } } else { if (pause->rx_pause && pause->tx_pause) hw->fc.requested_mode = e1000_fc_full; @@ -276,17 +296,20 @@ static int igb_set_pauseparam(struct net_device *netdev, static u32 igb_get_rx_csum(struct net_device *netdev) { struct igb_adapter *adapter = netdev_priv(netdev); - return !(adapter->flags & IGB_FLAG_RX_CSUM_DISABLED); + return !!(adapter->rx_ring[0].flags & IGB_RING_FLAG_RX_CSUM); } static int igb_set_rx_csum(struct net_device *netdev, u32 data) { struct igb_adapter *adapter = netdev_priv(netdev); + int i; - if (data) - adapter->flags &= ~IGB_FLAG_RX_CSUM_DISABLED; - else - adapter->flags |= IGB_FLAG_RX_CSUM_DISABLED; + for (i = 0; i < adapter->num_rx_queues; i++) { + if (data) + adapter->rx_ring[i].flags |= IGB_RING_FLAG_RX_CSUM; + else + adapter->rx_ring[i].flags &= ~IGB_RING_FLAG_RX_CSUM; + } return 0; } @@ -302,7 +325,7 @@ static int igb_set_tx_csum(struct net_device *netdev, u32 data) if (data) { netdev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); - if (adapter->hw.mac.type == e1000_82576) + if (adapter->hw.mac.type >= e1000_82576) netdev->features |= NETIF_F_SCTP_CSUM; } else { netdev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | @@ -496,19 +519,10 @@ static void igb_get_regs(struct net_device *netdev, regs_buff[119] = adapter->stats.scvpc; regs_buff[120] = adapter->stats.hrmpc; - /* These should probably be added to e1000_regs.h instead */ - #define E1000_PSRTYPE_REG(_i) (0x05480 + ((_i) * 4)) - #define E1000_IP4AT_REG(_i) (0x05840 + ((_i) * 8)) - #define E1000_IP6AT_REG(_i) (0x05880 + ((_i) * 4)) - #define E1000_WUPM_REG(_i) (0x05A00 + ((_i) * 4)) - #define E1000_FFMT_REG(_i) (0x09000 + ((_i) * 8)) - #define E1000_FFVT_REG(_i) (0x09800 + ((_i) * 8)) - #define E1000_FFLT_REG(_i) (0x05F00 + ((_i) * 8)) - for (i = 0; i < 4; i++) regs_buff[121 + i] = rd32(E1000_SRRCTL(i)); for (i = 0; i < 4; i++) - regs_buff[125 + i] = rd32(E1000_PSRTYPE_REG(i)); + regs_buff[125 + i] = rd32(E1000_PSRTYPE(i)); for (i = 0; i < 4; i++) regs_buff[129 + i] = rd32(E1000_RDBAL(i)); for (i = 0; i < 4; i++) @@ -733,17 +747,17 @@ static int igb_set_ringparam(struct net_device *netdev, struct igb_adapter *adapter = netdev_priv(netdev); struct igb_ring *temp_ring; int i, err = 0; - u32 new_rx_count, new_tx_count; + u16 new_rx_count, new_tx_count; if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) return -EINVAL; - new_rx_count = max(ring->rx_pending, (u32)IGB_MIN_RXD); - new_rx_count = min(new_rx_count, (u32)IGB_MAX_RXD); + new_rx_count = min_t(u32, ring->rx_pending, IGB_MAX_RXD); + new_rx_count = max_t(u16, new_rx_count, IGB_MIN_RXD); new_rx_count = ALIGN(new_rx_count, REQ_RX_DESCRIPTOR_MULTIPLE); - new_tx_count = max(ring->tx_pending, (u32)IGB_MIN_TXD); - new_tx_count = min(new_tx_count, (u32)IGB_MAX_TXD); + new_tx_count = min_t(u32, ring->tx_pending, IGB_MAX_TXD); + new_tx_count = max_t(u16, new_tx_count, IGB_MIN_TXD); new_tx_count = ALIGN(new_tx_count, REQ_TX_DESCRIPTOR_MULTIPLE); if ((new_tx_count == adapter->tx_ring_count) && @@ -788,7 +802,7 @@ static int igb_set_ringparam(struct net_device *netdev, for (i = 0; i < adapter->num_tx_queues; i++) { temp_ring[i].count = new_tx_count; - err = igb_setup_tx_resources(adapter, &temp_ring[i]); + err = igb_setup_tx_resources(&temp_ring[i]); if (err) { while (i) { i--; @@ -813,7 +827,7 @@ static int igb_set_ringparam(struct net_device *netdev, for (i = 0; i < adapter->num_rx_queues; i++) { temp_ring[i].count = new_rx_count; - err = igb_setup_rx_resources(adapter, &temp_ring[i]); + err = igb_setup_rx_resources(&temp_ring[i]); if (err) { while (i) { i--; @@ -867,6 +881,49 @@ struct igb_reg_test { #define TABLE64_TEST_LO 5 #define TABLE64_TEST_HI 6 +/* 82580 reg test */ +static struct igb_reg_test reg_test_82580[] = { + { E1000_FCAL, 0x100, 1, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF }, + { E1000_FCAH, 0x100, 1, PATTERN_TEST, 0x0000FFFF, 0xFFFFFFFF }, + { E1000_FCT, 0x100, 1, PATTERN_TEST, 0x0000FFFF, 0xFFFFFFFF }, + { E1000_VET, 0x100, 1, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF }, + { E1000_RDBAL(0), 0x100, 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFFFF }, + { E1000_RDBAH(0), 0x100, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF }, + { E1000_RDLEN(0), 0x100, 4, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF }, + { E1000_RDBAL(4), 0x40, 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFFFF }, + { E1000_RDBAH(4), 0x40, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF }, + { E1000_RDLEN(4), 0x40, 4, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF }, + /* RDH is read-only for 82580, only test RDT. */ + { E1000_RDT(0), 0x100, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF }, + { E1000_RDT(4), 0x40, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF }, + { E1000_FCRTH, 0x100, 1, PATTERN_TEST, 0x0000FFF0, 0x0000FFF0 }, + { E1000_FCTTV, 0x100, 1, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF }, + { E1000_TIPG, 0x100, 1, PATTERN_TEST, 0x3FFFFFFF, 0x3FFFFFFF }, + { E1000_TDBAL(0), 0x100, 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFFFF }, + { E1000_TDBAH(0), 0x100, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF }, + { E1000_TDLEN(0), 0x100, 4, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF }, + { E1000_TDBAL(4), 0x40, 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFFFF }, + { E1000_TDBAH(4), 0x40, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF }, + { E1000_TDLEN(4), 0x40, 4, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF }, + { E1000_TDT(0), 0x100, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF }, + { E1000_TDT(4), 0x40, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF }, + { E1000_RCTL, 0x100, 1, SET_READ_TEST, 0xFFFFFFFF, 0x00000000 }, + { E1000_RCTL, 0x100, 1, SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB }, + { E1000_RCTL, 0x100, 1, SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF }, + { E1000_TCTL, 0x100, 1, SET_READ_TEST, 0xFFFFFFFF, 0x00000000 }, + { E1000_RA, 0, 16, TABLE64_TEST_LO, + 0xFFFFFFFF, 0xFFFFFFFF }, + { E1000_RA, 0, 16, TABLE64_TEST_HI, + 0x83FFFFFF, 0xFFFFFFFF }, + { E1000_RA2, 0, 8, TABLE64_TEST_LO, + 0xFFFFFFFF, 0xFFFFFFFF }, + { E1000_RA2, 0, 8, TABLE64_TEST_HI, + 0x83FFFFFF, 0xFFFFFFFF }, + { E1000_MTA, 0, 128, TABLE32_TEST, + 0xFFFFFFFF, 0xFFFFFFFF }, + { 0, 0, 0, 0 } +}; + /* 82576 reg test */ static struct igb_reg_test reg_test_82576[] = { { E1000_FCAL, 0x100, 1, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF }, @@ -944,7 +1001,7 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data, { struct e1000_hw *hw = &adapter->hw; u32 pat, val; - u32 _test[] = + static const u32 _test[] = {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF}; for (pat = 0; pat < ARRAY_SIZE(_test); pat++) { wr32(reg, (_test[pat] & write)); @@ -957,6 +1014,7 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data, return 1; } } + return 0; } @@ -974,6 +1032,7 @@ static bool reg_set_and_check(struct igb_adapter *adapter, u64 *data, *data = reg; return 1; } + return 0; } @@ -996,14 +1055,18 @@ static int igb_reg_test(struct igb_adapter *adapter, u64 *data) u32 value, before, after; u32 i, toggle; - toggle = 0x7FFFF3FF; - switch (adapter->hw.mac.type) { + case e1000_82580: + test = reg_test_82580; + toggle = 0x7FEFF3FF; + break; case e1000_82576: test = reg_test_82576; + toggle = 0x7FFFF3FF; break; default: test = reg_test_82575; + toggle = 0x7FFFF3FF; break; } @@ -1081,8 +1144,7 @@ static int igb_eeprom_test(struct igb_adapter *adapter, u64 *data) *data = 0; /* Read and add up the contents of the EEPROM */ for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) { - if ((adapter->hw.nvm.ops.read(&adapter->hw, i, 1, &temp)) - < 0) { + if ((adapter->hw.nvm.ops.read(&adapter->hw, i, 1, &temp)) < 0) { *data = 1; break; } @@ -1098,8 +1160,7 @@ static int igb_eeprom_test(struct igb_adapter *adapter, u64 *data) static irqreturn_t igb_test_intr(int irq, void *data) { - struct net_device *netdev = (struct net_device *) data; - struct igb_adapter *adapter = netdev_priv(netdev); + struct igb_adapter *adapter = (struct igb_adapter *) data; struct e1000_hw *hw = &adapter->hw; adapter->test_icr |= rd32(E1000_ICR); @@ -1117,38 +1178,45 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data) *data = 0; /* Hook up test interrupt handler just for this test */ - if (adapter->msix_entries) - /* NOTE: we don't test MSI-X interrupts here, yet */ - return 0; - - if (adapter->flags & IGB_FLAG_HAS_MSI) { + if (adapter->msix_entries) { + if (request_irq(adapter->msix_entries[0].vector, + igb_test_intr, 0, netdev->name, adapter)) { + *data = 1; + return -1; + } + } else if (adapter->flags & IGB_FLAG_HAS_MSI) { shared_int = false; - if (request_irq(irq, &igb_test_intr, 0, netdev->name, netdev)) { + if (request_irq(irq, + igb_test_intr, 0, netdev->name, adapter)) { *data = 1; return -1; } - } else if (!request_irq(irq, &igb_test_intr, IRQF_PROBE_SHARED, - netdev->name, netdev)) { + } else if (!request_irq(irq, igb_test_intr, IRQF_PROBE_SHARED, + netdev->name, adapter)) { shared_int = false; - } else if (request_irq(irq, &igb_test_intr, IRQF_SHARED, - netdev->name, netdev)) { + } else if (request_irq(irq, igb_test_intr, IRQF_SHARED, + netdev->name, adapter)) { *data = 1; return -1; } dev_info(&adapter->pdev->dev, "testing %s interrupt\n", (shared_int ? "shared" : "unshared")); + /* Disable all the interrupts */ - wr32(E1000_IMC, 0xFFFFFFFF); + wr32(E1000_IMC, ~0); msleep(10); /* Define all writable bits for ICS */ - switch(hw->mac.type) { + switch (hw->mac.type) { case e1000_82575: ics_mask = 0x37F47EDD; break; case e1000_82576: ics_mask = 0x77D4FBFD; break; + case e1000_82580: + ics_mask = 0x77DCFED5; + break; default: ics_mask = 0x7FFFFFFF; break; @@ -1232,190 +1300,61 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data) msleep(10); /* Unhook test interrupt handler */ - free_irq(irq, netdev); + if (adapter->msix_entries) + free_irq(adapter->msix_entries[0].vector, adapter); + else + free_irq(irq, adapter); return *data; } static void igb_free_desc_rings(struct igb_adapter *adapter) { - struct igb_ring *tx_ring = &adapter->test_tx_ring; - struct igb_ring *rx_ring = &adapter->test_rx_ring; - struct pci_dev *pdev = adapter->pdev; - int i; - - if (tx_ring->desc && tx_ring->buffer_info) { - for (i = 0; i < tx_ring->count; i++) { - struct igb_buffer *buf = &(tx_ring->buffer_info[i]); - if (buf->dma) - pci_unmap_single(pdev, buf->dma, buf->length, - PCI_DMA_TODEVICE); - if (buf->skb) - dev_kfree_skb(buf->skb); - } - } - - if (rx_ring->desc && rx_ring->buffer_info) { - for (i = 0; i < rx_ring->count; i++) { - struct igb_buffer *buf = &(rx_ring->buffer_info[i]); - if (buf->dma) - pci_unmap_single(pdev, buf->dma, - IGB_RXBUFFER_2048, - PCI_DMA_FROMDEVICE); - if (buf->skb) - dev_kfree_skb(buf->skb); - } - } - - if (tx_ring->desc) { - pci_free_consistent(pdev, tx_ring->size, tx_ring->desc, - tx_ring->dma); - tx_ring->desc = NULL; - } - if (rx_ring->desc) { - pci_free_consistent(pdev, rx_ring->size, rx_ring->desc, - rx_ring->dma); - rx_ring->desc = NULL; - } - - kfree(tx_ring->buffer_info); - tx_ring->buffer_info = NULL; - kfree(rx_ring->buffer_info); - rx_ring->buffer_info = NULL; - - return; + igb_free_tx_resources(&adapter->test_tx_ring); + igb_free_rx_resources(&adapter->test_rx_ring); } static int igb_setup_desc_rings(struct igb_adapter *adapter) { - struct e1000_hw *hw = &adapter->hw; struct igb_ring *tx_ring = &adapter->test_tx_ring; struct igb_ring *rx_ring = &adapter->test_rx_ring; - struct pci_dev *pdev = adapter->pdev; - struct igb_buffer *buffer_info; - u32 rctl; - int i, ret_val; + struct e1000_hw *hw = &adapter->hw; + int ret_val; /* Setup Tx descriptor ring and Tx buffers */ + tx_ring->count = IGB_DEFAULT_TXD; + tx_ring->pdev = adapter->pdev; + tx_ring->netdev = adapter->netdev; + tx_ring->reg_idx = adapter->vfs_allocated_count; - if (!tx_ring->count) - tx_ring->count = IGB_DEFAULT_TXD; - - tx_ring->buffer_info = kcalloc(tx_ring->count, - sizeof(struct igb_buffer), - GFP_KERNEL); - if (!tx_ring->buffer_info) { + if (igb_setup_tx_resources(tx_ring)) { ret_val = 1; goto err_nomem; } - tx_ring->size = tx_ring->count * sizeof(union e1000_adv_tx_desc); - tx_ring->size = ALIGN(tx_ring->size, 4096); - tx_ring->desc = pci_alloc_consistent(pdev, tx_ring->size, - &tx_ring->dma); - if (!tx_ring->desc) { - ret_val = 2; - goto err_nomem; - } - tx_ring->next_to_use = tx_ring->next_to_clean = 0; - - wr32(E1000_TDBAL(0), - ((u64) tx_ring->dma & 0x00000000FFFFFFFF)); - wr32(E1000_TDBAH(0), ((u64) tx_ring->dma >> 32)); - wr32(E1000_TDLEN(0), - tx_ring->count * sizeof(union e1000_adv_tx_desc)); - wr32(E1000_TDH(0), 0); - wr32(E1000_TDT(0), 0); - wr32(E1000_TCTL, - E1000_TCTL_PSP | E1000_TCTL_EN | - E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT | - E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT); - - for (i = 0; i < tx_ring->count; i++) { - union e1000_adv_tx_desc *tx_desc; - struct sk_buff *skb; - unsigned int size = 1024; - - tx_desc = E1000_TX_DESC_ADV(*tx_ring, i); - skb = alloc_skb(size, GFP_KERNEL); - if (!skb) { - ret_val = 3; - goto err_nomem; - } - skb_put(skb, size); - buffer_info = &tx_ring->buffer_info[i]; - buffer_info->skb = skb; - buffer_info->length = skb->len; - buffer_info->dma = pci_map_single(pdev, skb->data, skb->len, - PCI_DMA_TODEVICE); - tx_desc->read.buffer_addr = cpu_to_le64(buffer_info->dma); - tx_desc->read.olinfo_status = cpu_to_le32(skb->len) << - E1000_ADVTXD_PAYLEN_SHIFT; - tx_desc->read.cmd_type_len = cpu_to_le32(skb->len); - tx_desc->read.cmd_type_len |= cpu_to_le32(E1000_TXD_CMD_EOP | - E1000_TXD_CMD_IFCS | - E1000_TXD_CMD_RS | - E1000_ADVTXD_DTYP_DATA | - E1000_ADVTXD_DCMD_DEXT); - } + igb_setup_tctl(adapter); + igb_configure_tx_ring(adapter, tx_ring); /* Setup Rx descriptor ring and Rx buffers */ - - if (!rx_ring->count) - rx_ring->count = IGB_DEFAULT_RXD; - - rx_ring->buffer_info = kcalloc(rx_ring->count, - sizeof(struct igb_buffer), - GFP_KERNEL); - if (!rx_ring->buffer_info) { - ret_val = 4; + rx_ring->count = IGB_DEFAULT_RXD; + rx_ring->pdev = adapter->pdev; + rx_ring->netdev = adapter->netdev; + rx_ring->rx_buffer_len = IGB_RXBUFFER_2048; + rx_ring->reg_idx = adapter->vfs_allocated_count; + + if (igb_setup_rx_resources(rx_ring)) { + ret_val = 3; goto err_nomem; } - rx_ring->size = rx_ring->count * sizeof(union e1000_adv_rx_desc); - rx_ring->desc = pci_alloc_consistent(pdev, rx_ring->size, - &rx_ring->dma); - if (!rx_ring->desc) { - ret_val = 5; - goto err_nomem; - } - rx_ring->next_to_use = rx_ring->next_to_clean = 0; + /* set the default queue to queue 0 of PF */ + wr32(E1000_MRQC, adapter->vfs_allocated_count << 3); - rctl = rd32(E1000_RCTL); - wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN); - wr32(E1000_RDBAL(0), - ((u64) rx_ring->dma & 0xFFFFFFFF)); - wr32(E1000_RDBAH(0), - ((u64) rx_ring->dma >> 32)); - wr32(E1000_RDLEN(0), rx_ring->size); - wr32(E1000_RDH(0), 0); - wr32(E1000_RDT(0), 0); - rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC); - rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_RDMTS_HALF | - (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT); - wr32(E1000_RCTL, rctl); - wr32(E1000_SRRCTL(0), E1000_SRRCTL_DESCTYPE_ADV_ONEBUF); - - for (i = 0; i < rx_ring->count; i++) { - union e1000_adv_rx_desc *rx_desc; - struct sk_buff *skb; - - buffer_info = &rx_ring->buffer_info[i]; - rx_desc = E1000_RX_DESC_ADV(*rx_ring, i); - skb = alloc_skb(IGB_RXBUFFER_2048 + NET_IP_ALIGN, - GFP_KERNEL); - if (!skb) { - ret_val = 6; - goto err_nomem; - } - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; - buffer_info->dma = pci_map_single(pdev, skb->data, - IGB_RXBUFFER_2048, - PCI_DMA_FROMDEVICE); - rx_desc->read.pkt_addr = cpu_to_le64(buffer_info->dma); - memset(skb->data, 0x00, skb->len); - } + /* enable receive ring */ + igb_setup_rctl(adapter); + igb_configure_rx_ring(adapter, rx_ring); + + igb_alloc_rx_buffers_adv(rx_ring, igb_desc_unused(rx_ring)); return 0; @@ -1449,6 +1388,9 @@ static int igb_integrated_phy_loopback(struct igb_adapter *adapter) igb_write_phy_reg(hw, PHY_CONTROL, 0x9140); /* autoneg off */ igb_write_phy_reg(hw, PHY_CONTROL, 0x8140); + } else if (hw->phy.type == e1000_phy_82580) { + /* enable MII loopback */ + igb_write_phy_reg(hw, I82580_PHY_LBK_CTRL, 0x8041); } ctrl_reg = rd32(E1000_CTRL); @@ -1491,7 +1433,10 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter) struct e1000_hw *hw = &adapter->hw; u32 reg; - if (hw->phy.media_type == e1000_media_type_internal_serdes) { + reg = rd32(E1000_CTRL_EXT); + + /* use CTRL_EXT to identify link type as SGMII can appear as copper */ + if (reg & E1000_CTRL_EXT_LINK_MODE_MASK) { reg = rd32(E1000_RCTL); reg |= E1000_RCTL_LBM_TCVR; wr32(E1000_RCTL, reg); @@ -1522,11 +1467,9 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter) wr32(E1000_PCS_LCTL, reg); return 0; - } else if (hw->phy.media_type == e1000_media_type_copper) { - return igb_set_phy_loopback(adapter); } - return 7; + return igb_set_phy_loopback(adapter); } static void igb_loopback_cleanup(struct igb_adapter *adapter) @@ -1552,35 +1495,99 @@ static void igb_create_lbtest_frame(struct sk_buff *skb, unsigned int frame_size) { memset(skb->data, 0xFF, frame_size); - frame_size &= ~1; - memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1); - memset(&skb->data[frame_size / 2 + 10], 0xBE, 1); - memset(&skb->data[frame_size / 2 + 12], 0xAF, 1); + frame_size /= 2; + memset(&skb->data[frame_size], 0xAA, frame_size - 1); + memset(&skb->data[frame_size + 10], 0xBE, 1); + memset(&skb->data[frame_size + 12], 0xAF, 1); } static int igb_check_lbtest_frame(struct sk_buff *skb, unsigned int frame_size) { - frame_size &= ~1; - if (*(skb->data + 3) == 0xFF) - if ((*(skb->data + frame_size / 2 + 10) == 0xBE) && - (*(skb->data + frame_size / 2 + 12) == 0xAF)) + frame_size /= 2; + if (*(skb->data + 3) == 0xFF) { + if ((*(skb->data + frame_size + 10) == 0xBE) && + (*(skb->data + frame_size + 12) == 0xAF)) { return 0; + } + } return 13; } +static int igb_clean_test_rings(struct igb_ring *rx_ring, + struct igb_ring *tx_ring, + unsigned int size) +{ + union e1000_adv_rx_desc *rx_desc; + struct igb_buffer *buffer_info; + int rx_ntc, tx_ntc, count = 0; + u32 staterr; + + /* initialize next to clean and descriptor values */ + rx_ntc = rx_ring->next_to_clean; + tx_ntc = tx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_ADV(*rx_ring, rx_ntc); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); + + while (staterr & E1000_RXD_STAT_DD) { + /* check rx buffer */ + buffer_info = &rx_ring->buffer_info[rx_ntc]; + + /* unmap rx buffer, will be remapped by alloc_rx_buffers */ + pci_unmap_single(rx_ring->pdev, + buffer_info->dma, + rx_ring->rx_buffer_len, + PCI_DMA_FROMDEVICE); + buffer_info->dma = 0; + + /* verify contents of skb */ + if (!igb_check_lbtest_frame(buffer_info->skb, size)) + count++; + + /* unmap buffer on tx side */ + buffer_info = &tx_ring->buffer_info[tx_ntc]; + igb_unmap_and_free_tx_resource(tx_ring, buffer_info); + + /* increment rx/tx next to clean counters */ + rx_ntc++; + if (rx_ntc == rx_ring->count) + rx_ntc = 0; + tx_ntc++; + if (tx_ntc == tx_ring->count) + tx_ntc = 0; + + /* fetch next descriptor */ + rx_desc = E1000_RX_DESC_ADV(*rx_ring, rx_ntc); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); + } + + /* re-map buffers to ring, store next to clean values */ + igb_alloc_rx_buffers_adv(rx_ring, count); + rx_ring->next_to_clean = rx_ntc; + tx_ring->next_to_clean = tx_ntc; + + return count; +} + static int igb_run_loopback_test(struct igb_adapter *adapter) { - struct e1000_hw *hw = &adapter->hw; struct igb_ring *tx_ring = &adapter->test_tx_ring; struct igb_ring *rx_ring = &adapter->test_rx_ring; - struct pci_dev *pdev = adapter->pdev; - int i, j, k, l, lc, good_cnt; - int ret_val = 0; - unsigned long time; + int i, j, lc, good_cnt, ret_val = 0; + unsigned int size = 1024; + netdev_tx_t tx_ret_val; + struct sk_buff *skb; - wr32(E1000_RDT(0), rx_ring->count - 1); + /* allocate test skb */ + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return 11; - /* Calculate the loop count based on the largest descriptor ring + /* place data into test skb */ + igb_create_lbtest_frame(skb, size); + skb_put(skb, size); + + /* + * Calculate the loop count based on the largest descriptor ring * The idea is to wrap the largest ring a number of times using 64 * send/receive pairs during each loop */ @@ -1590,50 +1597,36 @@ static int igb_run_loopback_test(struct igb_adapter *adapter) else lc = ((rx_ring->count / 64) * 2) + 1; - k = l = 0; for (j = 0; j <= lc; j++) { /* loop count loop */ - for (i = 0; i < 64; i++) { /* send the packets */ - igb_create_lbtest_frame(tx_ring->buffer_info[k].skb, - 1024); - pci_dma_sync_single_for_device(pdev, - tx_ring->buffer_info[k].dma, - tx_ring->buffer_info[k].length, - PCI_DMA_TODEVICE); - k++; - if (k == tx_ring->count) - k = 0; - } - wr32(E1000_TDT(0), k); - msleep(200); - time = jiffies; /* set the start time for the receive */ + /* reset count of good packets */ good_cnt = 0; - do { /* receive the sent packets */ - pci_dma_sync_single_for_cpu(pdev, - rx_ring->buffer_info[l].dma, - IGB_RXBUFFER_2048, - PCI_DMA_FROMDEVICE); - - ret_val = igb_check_lbtest_frame( - rx_ring->buffer_info[l].skb, 1024); - if (!ret_val) + + /* place 64 packets on the transmit queue*/ + for (i = 0; i < 64; i++) { + skb_get(skb); + tx_ret_val = igb_xmit_frame_ring_adv(skb, tx_ring); + if (tx_ret_val == NETDEV_TX_OK) good_cnt++; - l++; - if (l == rx_ring->count) - l = 0; - /* time + 20 msecs (200 msecs on 2.4) is more than - * enough time to complete the receives, if it's - * exceeded, break and error off - */ - } while (good_cnt < 64 && jiffies < (time + 20)); + } + if (good_cnt != 64) { - ret_val = 13; /* ret_val is the same as mis-compare */ + ret_val = 12; break; } - if (jiffies >= (time + 20)) { - ret_val = 14; /* error code for time out error */ + + /* allow 200 milliseconds for packets to go from tx to rx */ + msleep(200); + + good_cnt = igb_clean_test_rings(rx_ring, tx_ring, size); + if (good_cnt != 64) { + ret_val = 13; break; } } /* end loop count loop */ + + /* free the original skb */ + kfree_skb(skb); + return ret_val; } @@ -1686,8 +1679,7 @@ static int igb_link_test(struct igb_adapter *adapter, u64 *data) if (hw->mac.autoneg) msleep(4000); - if (!(rd32(E1000_STATUS) & - E1000_STATUS_LU)) + if (!(rd32(E1000_STATUS) & E1000_STATUS_LU)) *data = 1; } return *data; @@ -1869,7 +1861,6 @@ static int igb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) adapter->wol |= E1000_WUFC_BC; if (wol->wolopts & WAKE_MAGIC) adapter->wol |= E1000_WUFC_MAG; - device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); return 0; @@ -1882,12 +1873,19 @@ static int igb_phys_id(struct net_device *netdev, u32 data) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + unsigned long timeout; - if (!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ)) - data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ); + timeout = data * 1000; + + /* + * msleep_interruptable only accepts unsigned int so we are limited + * in how long a duration we can wait + */ + if (!timeout || timeout > UINT_MAX) + timeout = UINT_MAX; igb_blink_led(hw); - msleep_interruptible(data * 1000); + msleep_interruptible(timeout); igb_led_off(hw); clear_bit(IGB_LED_ON, &adapter->led_status); @@ -1900,7 +1898,6 @@ static int igb_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) { struct igb_adapter *adapter = netdev_priv(netdev); - struct e1000_hw *hw = &adapter->hw; int i; if ((ec->rx_coalesce_usecs > IGB_MAX_ITR_USECS) || @@ -1909,17 +1906,39 @@ static int igb_set_coalesce(struct net_device *netdev, (ec->rx_coalesce_usecs == 2)) return -EINVAL; + if ((ec->tx_coalesce_usecs > IGB_MAX_ITR_USECS) || + ((ec->tx_coalesce_usecs > 3) && + (ec->tx_coalesce_usecs < IGB_MIN_ITR_USECS)) || + (ec->tx_coalesce_usecs == 2)) + return -EINVAL; + + if ((adapter->flags & IGB_FLAG_QUEUE_PAIRS) && ec->tx_coalesce_usecs) + return -EINVAL; + /* convert to rate of irq's per second */ - if (ec->rx_coalesce_usecs && ec->rx_coalesce_usecs <= 3) { - adapter->itr_setting = ec->rx_coalesce_usecs; - adapter->itr = IGB_START_ITR; - } else { - adapter->itr_setting = ec->rx_coalesce_usecs << 2; - adapter->itr = adapter->itr_setting; - } + if (ec->rx_coalesce_usecs && ec->rx_coalesce_usecs <= 3) + adapter->rx_itr_setting = ec->rx_coalesce_usecs; + else + adapter->rx_itr_setting = ec->rx_coalesce_usecs << 2; - for (i = 0; i < adapter->num_rx_queues; i++) - wr32(adapter->rx_ring[i].itr_register, adapter->itr); + /* convert to rate of irq's per second */ + if (adapter->flags & IGB_FLAG_QUEUE_PAIRS) + adapter->tx_itr_setting = adapter->rx_itr_setting; + else if (ec->tx_coalesce_usecs && ec->tx_coalesce_usecs <= 3) + adapter->tx_itr_setting = ec->tx_coalesce_usecs; + else + adapter->tx_itr_setting = ec->tx_coalesce_usecs << 2; + + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + if (q_vector->rx_ring) + q_vector->itr_val = adapter->rx_itr_setting; + else + q_vector->itr_val = adapter->tx_itr_setting; + if (q_vector->itr_val && q_vector->itr_val <= 3) + q_vector->itr_val = IGB_START_ITR; + q_vector->set_itr = 1; + } return 0; } @@ -1929,15 +1948,21 @@ static int igb_get_coalesce(struct net_device *netdev, { struct igb_adapter *adapter = netdev_priv(netdev); - if (adapter->itr_setting <= 3) - ec->rx_coalesce_usecs = adapter->itr_setting; + if (adapter->rx_itr_setting <= 3) + ec->rx_coalesce_usecs = adapter->rx_itr_setting; else - ec->rx_coalesce_usecs = adapter->itr_setting >> 2; + ec->rx_coalesce_usecs = adapter->rx_itr_setting >> 2; + + if (!(adapter->flags & IGB_FLAG_QUEUE_PAIRS)) { + if (adapter->tx_itr_setting <= 3) + ec->tx_coalesce_usecs = adapter->tx_itr_setting; + else + ec->tx_coalesce_usecs = adapter->tx_itr_setting >> 2; + } return 0; } - static int igb_nway_reset(struct net_device *netdev) { struct igb_adapter *adapter = netdev_priv(netdev); @@ -1962,31 +1987,32 @@ static void igb_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct igb_adapter *adapter = netdev_priv(netdev); + struct net_device_stats *net_stats = &netdev->stats; u64 *queue_stat; - int stat_count_tx = sizeof(struct igb_tx_queue_stats) / sizeof(u64); - int stat_count_rx = sizeof(struct igb_rx_queue_stats) / sizeof(u64); - int j; - int i; + int i, j, k; + char *p; igb_update_stats(adapter); + for (i = 0; i < IGB_GLOBAL_STATS_LEN; i++) { - char *p = (char *)adapter+igb_gstrings_stats[i].stat_offset; + p = (char *)adapter + igb_gstrings_stats[i].stat_offset; data[i] = (igb_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } + for (j = 0; j < IGB_NETDEV_STATS_LEN; j++, i++) { + p = (char *)net_stats + igb_gstrings_net_stats[j].stat_offset; + data[i] = (igb_gstrings_net_stats[j].sizeof_stat == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } for (j = 0; j < adapter->num_tx_queues; j++) { - int k; queue_stat = (u64 *)&adapter->tx_ring[j].tx_stats; - for (k = 0; k < stat_count_tx; k++) - data[i + k] = queue_stat[k]; - i += k; + for (k = 0; k < IGB_TX_QUEUE_STATS_LEN; k++, i++) + data[i] = queue_stat[k]; } for (j = 0; j < adapter->num_rx_queues; j++) { - int k; queue_stat = (u64 *)&adapter->rx_ring[j].rx_stats; - for (k = 0; k < stat_count_rx; k++) - data[i + k] = queue_stat[k]; - i += k; + for (k = 0; k < IGB_RX_QUEUE_STATS_LEN; k++, i++) + data[i] = queue_stat[k]; } } @@ -2007,11 +2033,18 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data) ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } + for (i = 0; i < IGB_NETDEV_STATS_LEN; i++) { + memcpy(p, igb_gstrings_net_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } for (i = 0; i < adapter->num_tx_queues; i++) { sprintf(p, "tx_queue_%u_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "tx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; + sprintf(p, "tx_queue_%u_restart", i); + p += ETH_GSTRING_LEN; } for (i = 0; i < adapter->num_rx_queues; i++) { sprintf(p, "rx_queue_%u_packets", i); @@ -2020,6 +2053,10 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data) p += ETH_GSTRING_LEN; sprintf(p, "rx_queue_%u_drops", i); p += ETH_GSTRING_LEN; + sprintf(p, "rx_queue_%u_csum_err", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_queue_%u_alloc_failed", i); + p += ETH_GSTRING_LEN; } /* BUG_ON(p - data != IGB_STATS_LEN * ETH_GSTRING_LEN); */ break; diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 714c3a4a44ef..16349ba68736 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -49,7 +49,7 @@ #endif #include "igb.h" -#define DRV_VERSION "1.3.16-k2" +#define DRV_VERSION "2.1.0-k2" char igb_driver_name[] = "igb"; char igb_driver_version[] = DRV_VERSION; static const char igb_driver_string[] = @@ -61,8 +61,14 @@ static const struct e1000_info *igb_info_tbl[] = { }; static struct pci_device_id igb_pci_tbl[] = { + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER), board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_FIBER), board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SERDES), board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SGMII), board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER_DUAL), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS), board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS_SERDES), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_FIBER), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES_QUAD), board_82575 }, @@ -81,6 +87,7 @@ static int igb_setup_all_tx_resources(struct igb_adapter *); static int igb_setup_all_rx_resources(struct igb_adapter *); static void igb_free_all_tx_resources(struct igb_adapter *); static void igb_free_all_rx_resources(struct igb_adapter *); +static void igb_setup_mrqc(struct igb_adapter *); void igb_update_stats(struct igb_adapter *); static int igb_probe(struct pci_dev *, const struct pci_device_id *); static void __devexit igb_remove(struct pci_dev *pdev); @@ -89,7 +96,6 @@ static int igb_open(struct net_device *); static int igb_close(struct net_device *); static void igb_configure_tx(struct igb_adapter *); static void igb_configure_rx(struct igb_adapter *); -static void igb_setup_rctl(struct igb_adapter *); static void igb_clean_all_tx_rings(struct igb_adapter *); static void igb_clean_all_rx_rings(struct igb_adapter *); static void igb_clean_tx_ring(struct igb_ring *); @@ -98,28 +104,22 @@ static void igb_set_rx_mode(struct net_device *); static void igb_update_phy_info(unsigned long); static void igb_watchdog(unsigned long); static void igb_watchdog_task(struct work_struct *); -static netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *, - struct net_device *, - struct igb_ring *); -static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb, - struct net_device *); +static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb, struct net_device *); static struct net_device_stats *igb_get_stats(struct net_device *); static int igb_change_mtu(struct net_device *, int); static int igb_set_mac(struct net_device *, void *); +static void igb_set_uta(struct igb_adapter *adapter); static irqreturn_t igb_intr(int irq, void *); static irqreturn_t igb_intr_msi(int irq, void *); static irqreturn_t igb_msix_other(int irq, void *); -static irqreturn_t igb_msix_rx(int irq, void *); -static irqreturn_t igb_msix_tx(int irq, void *); +static irqreturn_t igb_msix_ring(int irq, void *); #ifdef CONFIG_IGB_DCA -static void igb_update_rx_dca(struct igb_ring *); -static void igb_update_tx_dca(struct igb_ring *); +static void igb_update_dca(struct igb_q_vector *); static void igb_setup_dca(struct igb_adapter *); #endif /* CONFIG_IGB_DCA */ -static bool igb_clean_tx_irq(struct igb_ring *); +static bool igb_clean_tx_irq(struct igb_q_vector *); static int igb_poll(struct napi_struct *, int); -static bool igb_clean_rx_irq_adv(struct igb_ring *, int *, int); -static void igb_alloc_rx_buffers_adv(struct igb_ring *, int); +static bool igb_clean_rx_irq_adv(struct igb_q_vector *, int *, int); static int igb_ioctl(struct net_device *, struct ifreq *, int cmd); static void igb_tx_timeout(struct net_device *); static void igb_reset_task(struct work_struct *); @@ -127,57 +127,13 @@ static void igb_vlan_rx_register(struct net_device *, struct vlan_group *); static void igb_vlan_rx_add_vid(struct net_device *, u16); static void igb_vlan_rx_kill_vid(struct net_device *, u16); static void igb_restore_vlan(struct igb_adapter *); +static void igb_rar_set_qsel(struct igb_adapter *, u8 *, u32 , u8); static void igb_ping_all_vfs(struct igb_adapter *); static void igb_msg_task(struct igb_adapter *); -static int igb_rcv_msg_from_vf(struct igb_adapter *, u32); -static inline void igb_set_rah_pool(struct e1000_hw *, int , int); static void igb_vmm_control(struct igb_adapter *); -static int igb_set_vf_mac(struct igb_adapter *adapter, int, unsigned char *); +static int igb_set_vf_mac(struct igb_adapter *, int, unsigned char *); static void igb_restore_vf_multicasts(struct igb_adapter *adapter); -static inline void igb_set_vmolr(struct e1000_hw *hw, int vfn) -{ - u32 reg_data; - - reg_data = rd32(E1000_VMOLR(vfn)); - reg_data |= E1000_VMOLR_BAM | /* Accept broadcast */ - E1000_VMOLR_ROPE | /* Accept packets matched in UTA */ - E1000_VMOLR_ROMPE | /* Accept packets matched in MTA */ - E1000_VMOLR_AUPE | /* Accept untagged packets */ - E1000_VMOLR_STRVLAN; /* Strip vlan tags */ - wr32(E1000_VMOLR(vfn), reg_data); -} - -static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size, - int vfn) -{ - struct e1000_hw *hw = &adapter->hw; - u32 vmolr; - - /* if it isn't the PF check to see if VFs are enabled and - * increase the size to support vlan tags */ - if (vfn < adapter->vfs_allocated_count && - adapter->vf_data[vfn].vlans_enabled) - size += VLAN_TAG_SIZE; - - vmolr = rd32(E1000_VMOLR(vfn)); - vmolr &= ~E1000_VMOLR_RLPML_MASK; - vmolr |= size | E1000_VMOLR_LPE; - wr32(E1000_VMOLR(vfn), vmolr); - - return 0; -} - -static inline void igb_set_rah_pool(struct e1000_hw *hw, int pool, int entry) -{ - u32 reg_data; - - reg_data = rd32(E1000_RAH(entry)); - reg_data &= ~E1000_RAH_POOL_MASK; - reg_data |= E1000_RAH_POOL_1 << pool;; - wr32(E1000_RAH(entry), reg_data); -} - #ifdef CONFIG_PM static int igb_suspend(struct pci_dev *, pm_message_t); static int igb_resume(struct pci_dev *); @@ -228,46 +184,12 @@ static struct pci_driver igb_driver = { .err_handler = &igb_err_handler }; -static int global_quad_port_a; /* global quad port a indication */ - MODULE_AUTHOR("Intel Corporation, <e1000-devel@lists.sourceforge.net>"); MODULE_DESCRIPTION("Intel(R) Gigabit Ethernet Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); /** - * Scale the NIC clock cycle by a large factor so that - * relatively small clock corrections can be added or - * substracted at each clock tick. The drawbacks of a - * large factor are a) that the clock register overflows - * more quickly (not such a big deal) and b) that the - * increment per tick has to fit into 24 bits. - * - * Note that - * TIMINCA = IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS * - * IGB_TSYNC_SCALE - * TIMINCA += TIMINCA * adjustment [ppm] / 1e9 - * - * The base scale factor is intentionally a power of two - * so that the division in %struct timecounter can be done with - * a shift. - */ -#define IGB_TSYNC_SHIFT (19) -#define IGB_TSYNC_SCALE (1<<IGB_TSYNC_SHIFT) - -/** - * The duration of one clock cycle of the NIC. - * - * @todo This hard-coded value is part of the specification and might change - * in future hardware revisions. Add revision check. - */ -#define IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS 16 - -#if (IGB_TSYNC_SCALE * IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS) >= (1<<24) -# error IGB_TSYNC_SCALE and/or IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS are too large to fit into TIMINCA -#endif - -/** * igb_read_clock - read raw cycle counter (to be used by time counter) */ static cycle_t igb_read_clock(const struct cyclecounter *tc) @@ -275,11 +197,21 @@ static cycle_t igb_read_clock(const struct cyclecounter *tc) struct igb_adapter *adapter = container_of(tc, struct igb_adapter, cycles); struct e1000_hw *hw = &adapter->hw; - u64 stamp; + u64 stamp = 0; + int shift = 0; - stamp = rd32(E1000_SYSTIML); - stamp |= (u64)rd32(E1000_SYSTIMH) << 32ULL; + /* + * The timestamp latches on lowest register read. For the 82580 + * the lowest register is SYSTIMR instead of SYSTIML. However we never + * adjusted TIMINCA so SYSTIMR will just read as all 0s so ignore it. + */ + if (hw->mac.type == e1000_82580) { + stamp = rd32(E1000_SYSTIMR) >> 8; + shift = IGB_82580_TSYNC_SHIFT; + } + stamp |= (u64)rd32(E1000_SYSTIML) << shift; + stamp |= (u64)rd32(E1000_SYSTIMH) << (shift + 32); return stamp; } @@ -320,17 +252,6 @@ static char *igb_get_time_str(struct igb_adapter *adapter, #endif /** - * igb_desc_unused - calculate if we have unused descriptors - **/ -static int igb_desc_unused(struct igb_ring *ring) -{ - if (ring->next_to_clean > ring->next_to_use) - return ring->next_to_clean - ring->next_to_use - 1; - - return ring->count + ring->next_to_clean - ring->next_to_use - 1; -} - -/** * igb_init_module - Driver Registration Routine * * igb_init_module is the first routine called when the driver is @@ -344,12 +265,9 @@ static int __init igb_init_module(void) printk(KERN_INFO "%s\n", igb_copyright); - global_quad_port_a = 0; - #ifdef CONFIG_IGB_DCA dca_register_notify(&dca_notifier); #endif - ret = pci_register_driver(&igb_driver); return ret; } @@ -382,8 +300,8 @@ module_exit(igb_exit_module); **/ static void igb_cache_ring_register(struct igb_adapter *adapter) { - int i; - unsigned int rbase_offset = adapter->vfs_allocated_count; + int i = 0, j = 0; + u32 rbase_offset = adapter->vfs_allocated_count; switch (adapter->hw.mac.type) { case e1000_82576: @@ -392,23 +310,37 @@ static void igb_cache_ring_register(struct igb_adapter *adapter) * In order to avoid collision we start at the first free queue * and continue consuming queues in the same sequence */ - for (i = 0; i < adapter->num_rx_queues; i++) - adapter->rx_ring[i].reg_idx = rbase_offset + - Q_IDX_82576(i); - for (i = 0; i < adapter->num_tx_queues; i++) - adapter->tx_ring[i].reg_idx = rbase_offset + - Q_IDX_82576(i); - break; + if (adapter->vfs_allocated_count) { + for (; i < adapter->rss_queues; i++) + adapter->rx_ring[i].reg_idx = rbase_offset + + Q_IDX_82576(i); + for (; j < adapter->rss_queues; j++) + adapter->tx_ring[j].reg_idx = rbase_offset + + Q_IDX_82576(j); + } case e1000_82575: + case e1000_82580: default: - for (i = 0; i < adapter->num_rx_queues; i++) - adapter->rx_ring[i].reg_idx = i; - for (i = 0; i < adapter->num_tx_queues; i++) - adapter->tx_ring[i].reg_idx = i; + for (; i < adapter->num_rx_queues; i++) + adapter->rx_ring[i].reg_idx = rbase_offset + i; + for (; j < adapter->num_tx_queues; j++) + adapter->tx_ring[j].reg_idx = rbase_offset + j; break; } } +static void igb_free_queues(struct igb_adapter *adapter) +{ + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + + adapter->tx_ring = NULL; + adapter->rx_ring = NULL; + + adapter->num_rx_queues = 0; + adapter->num_tx_queues = 0; +} + /** * igb_alloc_queues - Allocate memory for all rings * @adapter: board private structure to initialize @@ -423,59 +355,61 @@ static int igb_alloc_queues(struct igb_adapter *adapter) adapter->tx_ring = kcalloc(adapter->num_tx_queues, sizeof(struct igb_ring), GFP_KERNEL); if (!adapter->tx_ring) - return -ENOMEM; + goto err; adapter->rx_ring = kcalloc(adapter->num_rx_queues, sizeof(struct igb_ring), GFP_KERNEL); - if (!adapter->rx_ring) { - kfree(adapter->tx_ring); - return -ENOMEM; - } - - adapter->rx_ring->buddy = adapter->tx_ring; + if (!adapter->rx_ring) + goto err; for (i = 0; i < adapter->num_tx_queues; i++) { struct igb_ring *ring = &(adapter->tx_ring[i]); ring->count = adapter->tx_ring_count; - ring->adapter = adapter; ring->queue_index = i; + ring->pdev = adapter->pdev; + ring->netdev = adapter->netdev; + /* For 82575, context index must be unique per ring. */ + if (adapter->hw.mac.type == e1000_82575) + ring->flags = IGB_RING_FLAG_TX_CTX_IDX; } + for (i = 0; i < adapter->num_rx_queues; i++) { struct igb_ring *ring = &(adapter->rx_ring[i]); ring->count = adapter->rx_ring_count; - ring->adapter = adapter; ring->queue_index = i; - ring->itr_register = E1000_ITR; - - /* set a default napi handler for each rx_ring */ - netif_napi_add(adapter->netdev, &ring->napi, igb_poll, 64); + ring->pdev = adapter->pdev; + ring->netdev = adapter->netdev; + ring->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE; + ring->flags = IGB_RING_FLAG_RX_CSUM; /* enable rx checksum */ + /* set flag indicating ring supports SCTP checksum offload */ + if (adapter->hw.mac.type >= e1000_82576) + ring->flags |= IGB_RING_FLAG_RX_SCTP_CSUM; } igb_cache_ring_register(adapter); - return 0; -} - -static void igb_free_queues(struct igb_adapter *adapter) -{ - int i; - for (i = 0; i < adapter->num_rx_queues; i++) - netif_napi_del(&adapter->rx_ring[i].napi); + return 0; - adapter->num_rx_queues = 0; - adapter->num_tx_queues = 0; +err: + igb_free_queues(adapter); - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); + return -ENOMEM; } #define IGB_N0_QUEUE -1 -static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue, - int tx_queue, int msix_vector) +static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector) { u32 msixbm = 0; + struct igb_adapter *adapter = q_vector->adapter; struct e1000_hw *hw = &adapter->hw; u32 ivar, index; + int rx_queue = IGB_N0_QUEUE; + int tx_queue = IGB_N0_QUEUE; + + if (q_vector->rx_ring) + rx_queue = q_vector->rx_ring->reg_idx; + if (q_vector->tx_ring) + tx_queue = q_vector->tx_ring->reg_idx; switch (hw->mac.type) { case e1000_82575: @@ -483,16 +417,12 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue, bitmask for the EICR/EIMS/EIMC registers. To assign one or more queues to a vector, we write the appropriate bits into the MSIXBM register for that vector. */ - if (rx_queue > IGB_N0_QUEUE) { + if (rx_queue > IGB_N0_QUEUE) msixbm = E1000_EICR_RX_QUEUE0 << rx_queue; - adapter->rx_ring[rx_queue].eims_value = msixbm; - } - if (tx_queue > IGB_N0_QUEUE) { + if (tx_queue > IGB_N0_QUEUE) msixbm |= E1000_EICR_TX_QUEUE0 << tx_queue; - adapter->tx_ring[tx_queue].eims_value = - E1000_EICR_TX_QUEUE0 << tx_queue; - } array_wr32(E1000_MSIXBM(0), msix_vector, msixbm); + q_vector->eims_value = msixbm; break; case e1000_82576: /* 82576 uses a table-based method for assigning vectors. @@ -500,7 +430,40 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue, a vector number along with a "valid" bit. Sadly, the layout of the table is somewhat counterintuitive. */ if (rx_queue > IGB_N0_QUEUE) { - index = (rx_queue >> 1) + adapter->vfs_allocated_count; + index = (rx_queue & 0x7); + ivar = array_rd32(E1000_IVAR0, index); + if (rx_queue < 8) { + /* vector goes into low byte of register */ + ivar = ivar & 0xFFFFFF00; + ivar |= msix_vector | E1000_IVAR_VALID; + } else { + /* vector goes into third byte of register */ + ivar = ivar & 0xFF00FFFF; + ivar |= (msix_vector | E1000_IVAR_VALID) << 16; + } + array_wr32(E1000_IVAR0, index, ivar); + } + if (tx_queue > IGB_N0_QUEUE) { + index = (tx_queue & 0x7); + ivar = array_rd32(E1000_IVAR0, index); + if (tx_queue < 8) { + /* vector goes into second byte of register */ + ivar = ivar & 0xFFFF00FF; + ivar |= (msix_vector | E1000_IVAR_VALID) << 8; + } else { + /* vector goes into high byte of register */ + ivar = ivar & 0x00FFFFFF; + ivar |= (msix_vector | E1000_IVAR_VALID) << 24; + } + array_wr32(E1000_IVAR0, index, ivar); + } + q_vector->eims_value = 1 << msix_vector; + break; + case e1000_82580: + /* 82580 uses the same table-based approach as 82576 but has fewer + entries as a result we carry over for queues greater than 4. */ + if (rx_queue > IGB_N0_QUEUE) { + index = (rx_queue >> 1); ivar = array_rd32(E1000_IVAR0, index); if (rx_queue & 0x1) { /* vector goes into third byte of register */ @@ -511,11 +474,10 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue, ivar = ivar & 0xFFFFFF00; ivar |= msix_vector | E1000_IVAR_VALID; } - adapter->rx_ring[rx_queue].eims_value= 1 << msix_vector; array_wr32(E1000_IVAR0, index, ivar); } if (tx_queue > IGB_N0_QUEUE) { - index = (tx_queue >> 1) + adapter->vfs_allocated_count; + index = (tx_queue >> 1); ivar = array_rd32(E1000_IVAR0, index); if (tx_queue & 0x1) { /* vector goes into high byte of register */ @@ -526,9 +488,9 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue, ivar = ivar & 0xFFFF00FF; ivar |= (msix_vector | E1000_IVAR_VALID) << 8; } - adapter->tx_ring[tx_queue].eims_value= 1 << msix_vector; array_wr32(E1000_IVAR0, index, ivar); } + q_vector->eims_value = 1 << msix_vector; break; default: BUG(); @@ -549,43 +511,10 @@ static void igb_configure_msix(struct igb_adapter *adapter) struct e1000_hw *hw = &adapter->hw; adapter->eims_enable_mask = 0; - if (hw->mac.type == e1000_82576) - /* Turn on MSI-X capability first, or our settings - * won't stick. And it will take days to debug. */ - wr32(E1000_GPIE, E1000_GPIE_MSIX_MODE | - E1000_GPIE_PBA | E1000_GPIE_EIAME | - E1000_GPIE_NSICR); - - for (i = 0; i < adapter->num_tx_queues; i++) { - struct igb_ring *tx_ring = &adapter->tx_ring[i]; - igb_assign_vector(adapter, IGB_N0_QUEUE, i, vector++); - adapter->eims_enable_mask |= tx_ring->eims_value; - if (tx_ring->itr_val) - writel(tx_ring->itr_val, - hw->hw_addr + tx_ring->itr_register); - else - writel(1, hw->hw_addr + tx_ring->itr_register); - } - - for (i = 0; i < adapter->num_rx_queues; i++) { - struct igb_ring *rx_ring = &adapter->rx_ring[i]; - rx_ring->buddy = NULL; - igb_assign_vector(adapter, i, IGB_N0_QUEUE, vector++); - adapter->eims_enable_mask |= rx_ring->eims_value; - if (rx_ring->itr_val) - writel(rx_ring->itr_val, - hw->hw_addr + rx_ring->itr_register); - else - writel(1, hw->hw_addr + rx_ring->itr_register); - } - /* set vector for other causes, i.e. link changes */ switch (hw->mac.type) { case e1000_82575: - array_wr32(E1000_MSIXBM(0), vector++, - E1000_EIMS_OTHER); - tmp = rd32(E1000_CTRL_EXT); /* enable MSI-X PBA support*/ tmp |= E1000_CTRL_EXT_PBA_CLR; @@ -595,22 +524,41 @@ static void igb_configure_msix(struct igb_adapter *adapter) tmp |= E1000_CTRL_EXT_IRCA; wr32(E1000_CTRL_EXT, tmp); - adapter->eims_enable_mask |= E1000_EIMS_OTHER; + + /* enable msix_other interrupt */ + array_wr32(E1000_MSIXBM(0), vector++, + E1000_EIMS_OTHER); adapter->eims_other = E1000_EIMS_OTHER; break; case e1000_82576: + case e1000_82580: + /* Turn on MSI-X capability first, or our settings + * won't stick. And it will take days to debug. */ + wr32(E1000_GPIE, E1000_GPIE_MSIX_MODE | + E1000_GPIE_PBA | E1000_GPIE_EIAME | + E1000_GPIE_NSICR); + + /* enable msix_other interrupt */ + adapter->eims_other = 1 << vector; tmp = (vector++ | E1000_IVAR_VALID) << 8; - wr32(E1000_IVAR_MISC, tmp); - adapter->eims_enable_mask = (1 << (vector)) - 1; - adapter->eims_other = 1 << (vector - 1); + wr32(E1000_IVAR_MISC, tmp); break; default: /* do nothing, since nothing else supports MSI-X */ break; } /* switch (hw->mac.type) */ + + adapter->eims_enable_mask |= adapter->eims_other; + + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + igb_assign_vector(q_vector, vector++); + adapter->eims_enable_mask |= q_vector->eims_value; + } + wrfl(); } @@ -623,43 +571,40 @@ static void igb_configure_msix(struct igb_adapter *adapter) static int igb_request_msix(struct igb_adapter *adapter) { struct net_device *netdev = adapter->netdev; + struct e1000_hw *hw = &adapter->hw; int i, err = 0, vector = 0; - vector = 0; - - for (i = 0; i < adapter->num_tx_queues; i++) { - struct igb_ring *ring = &(adapter->tx_ring[i]); - sprintf(ring->name, "%s-tx-%d", netdev->name, i); - err = request_irq(adapter->msix_entries[vector].vector, - &igb_msix_tx, 0, ring->name, - &(adapter->tx_ring[i])); - if (err) - goto out; - ring->itr_register = E1000_EITR(0) + (vector << 2); - ring->itr_val = 976; /* ~4000 ints/sec */ - vector++; - } - for (i = 0; i < adapter->num_rx_queues; i++) { - struct igb_ring *ring = &(adapter->rx_ring[i]); - if (strlen(netdev->name) < (IFNAMSIZ - 5)) - sprintf(ring->name, "%s-rx-%d", netdev->name, i); + err = request_irq(adapter->msix_entries[vector].vector, + igb_msix_other, 0, netdev->name, adapter); + if (err) + goto out; + vector++; + + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + + q_vector->itr_register = hw->hw_addr + E1000_EITR(vector); + + if (q_vector->rx_ring && q_vector->tx_ring) + sprintf(q_vector->name, "%s-TxRx-%u", netdev->name, + q_vector->rx_ring->queue_index); + else if (q_vector->tx_ring) + sprintf(q_vector->name, "%s-tx-%u", netdev->name, + q_vector->tx_ring->queue_index); + else if (q_vector->rx_ring) + sprintf(q_vector->name, "%s-rx-%u", netdev->name, + q_vector->rx_ring->queue_index); else - memcpy(ring->name, netdev->name, IFNAMSIZ); + sprintf(q_vector->name, "%s-unused", netdev->name); + err = request_irq(adapter->msix_entries[vector].vector, - &igb_msix_rx, 0, ring->name, - &(adapter->rx_ring[i])); + igb_msix_ring, 0, q_vector->name, + q_vector); if (err) goto out; - ring->itr_register = E1000_EITR(0) + (vector << 2); - ring->itr_val = adapter->itr; vector++; } - err = request_irq(adapter->msix_entries[vector].vector, - &igb_msix_other, 0, netdev->name, netdev); - if (err) - goto out; - igb_configure_msix(adapter); return 0; out: @@ -672,11 +617,44 @@ static void igb_reset_interrupt_capability(struct igb_adapter *adapter) pci_disable_msix(adapter->pdev); kfree(adapter->msix_entries); adapter->msix_entries = NULL; - } else if (adapter->flags & IGB_FLAG_HAS_MSI) + } else if (adapter->flags & IGB_FLAG_HAS_MSI) { pci_disable_msi(adapter->pdev); - return; + } +} + +/** + * igb_free_q_vectors - Free memory allocated for interrupt vectors + * @adapter: board private structure to initialize + * + * This function frees the memory allocated to the q_vectors. In addition if + * NAPI is enabled it will delete any references to the NAPI struct prior + * to freeing the q_vector. + **/ +static void igb_free_q_vectors(struct igb_adapter *adapter) +{ + int v_idx; + + for (v_idx = 0; v_idx < adapter->num_q_vectors; v_idx++) { + struct igb_q_vector *q_vector = adapter->q_vector[v_idx]; + adapter->q_vector[v_idx] = NULL; + netif_napi_del(&q_vector->napi); + kfree(q_vector); + } + adapter->num_q_vectors = 0; } +/** + * igb_clear_interrupt_scheme - reset the device to a state of no interrupts + * + * This function resets the device so that it has 0 rx queues, tx queues, and + * MSI-X interrupts allocated. + */ +static void igb_clear_interrupt_scheme(struct igb_adapter *adapter) +{ + igb_free_queues(adapter); + igb_free_q_vectors(adapter); + igb_reset_interrupt_capability(adapter); +} /** * igb_set_interrupt_capability - set MSI or MSI-X if supported @@ -690,11 +668,21 @@ static void igb_set_interrupt_capability(struct igb_adapter *adapter) int numvecs, i; /* Number of supported queues. */ - /* Having more queues than CPUs doesn't make sense. */ - adapter->num_rx_queues = min_t(u32, IGB_MAX_RX_QUEUES, num_online_cpus()); - adapter->num_tx_queues = min_t(u32, IGB_MAX_TX_QUEUES, num_online_cpus()); + adapter->num_rx_queues = adapter->rss_queues; + adapter->num_tx_queues = adapter->rss_queues; + + /* start with one vector for every rx queue */ + numvecs = adapter->num_rx_queues; + + /* if tx handler is seperate add 1 for every tx queue */ + if (!(adapter->flags & IGB_FLAG_QUEUE_PAIRS)) + numvecs += adapter->num_tx_queues; + + /* store the number of vectors reserved for queues */ + adapter->num_q_vectors = numvecs; - numvecs = adapter->num_tx_queues + adapter->num_rx_queues + 1; + /* add 1 vector for link status interrupts */ + numvecs++; adapter->msix_entries = kcalloc(numvecs, sizeof(struct msix_entry), GFP_KERNEL); if (!adapter->msix_entries) @@ -728,8 +716,12 @@ msi_only: dev_info(&adapter->pdev->dev, "IOV Disabled\n"); } #endif + adapter->vfs_allocated_count = 0; + adapter->rss_queues = 1; + adapter->flags |= IGB_FLAG_QUEUE_PAIRS; adapter->num_rx_queues = 1; adapter->num_tx_queues = 1; + adapter->num_q_vectors = 1; if (!pci_enable_msi(adapter->pdev)) adapter->flags |= IGB_FLAG_HAS_MSI; out: @@ -739,6 +731,143 @@ out: } /** + * igb_alloc_q_vectors - Allocate memory for interrupt vectors + * @adapter: board private structure to initialize + * + * We allocate one q_vector per queue interrupt. If allocation fails we + * return -ENOMEM. + **/ +static int igb_alloc_q_vectors(struct igb_adapter *adapter) +{ + struct igb_q_vector *q_vector; + struct e1000_hw *hw = &adapter->hw; + int v_idx; + + for (v_idx = 0; v_idx < adapter->num_q_vectors; v_idx++) { + q_vector = kzalloc(sizeof(struct igb_q_vector), GFP_KERNEL); + if (!q_vector) + goto err_out; + q_vector->adapter = adapter; + q_vector->itr_shift = (hw->mac.type == e1000_82575) ? 16 : 0; + q_vector->itr_register = hw->hw_addr + E1000_EITR(0); + q_vector->itr_val = IGB_START_ITR; + q_vector->set_itr = 1; + netif_napi_add(adapter->netdev, &q_vector->napi, igb_poll, 64); + adapter->q_vector[v_idx] = q_vector; + } + return 0; + +err_out: + while (v_idx) { + v_idx--; + q_vector = adapter->q_vector[v_idx]; + netif_napi_del(&q_vector->napi); + kfree(q_vector); + adapter->q_vector[v_idx] = NULL; + } + return -ENOMEM; +} + +static void igb_map_rx_ring_to_vector(struct igb_adapter *adapter, + int ring_idx, int v_idx) +{ + struct igb_q_vector *q_vector; + + q_vector = adapter->q_vector[v_idx]; + q_vector->rx_ring = &adapter->rx_ring[ring_idx]; + q_vector->rx_ring->q_vector = q_vector; + q_vector->itr_val = adapter->rx_itr_setting; + if (q_vector->itr_val && q_vector->itr_val <= 3) + q_vector->itr_val = IGB_START_ITR; +} + +static void igb_map_tx_ring_to_vector(struct igb_adapter *adapter, + int ring_idx, int v_idx) +{ + struct igb_q_vector *q_vector; + + q_vector = adapter->q_vector[v_idx]; + q_vector->tx_ring = &adapter->tx_ring[ring_idx]; + q_vector->tx_ring->q_vector = q_vector; + q_vector->itr_val = adapter->tx_itr_setting; + if (q_vector->itr_val && q_vector->itr_val <= 3) + q_vector->itr_val = IGB_START_ITR; +} + +/** + * igb_map_ring_to_vector - maps allocated queues to vectors + * + * This function maps the recently allocated queues to vectors. + **/ +static int igb_map_ring_to_vector(struct igb_adapter *adapter) +{ + int i; + int v_idx = 0; + + if ((adapter->num_q_vectors < adapter->num_rx_queues) || + (adapter->num_q_vectors < adapter->num_tx_queues)) + return -ENOMEM; + + if (adapter->num_q_vectors >= + (adapter->num_rx_queues + adapter->num_tx_queues)) { + for (i = 0; i < adapter->num_rx_queues; i++) + igb_map_rx_ring_to_vector(adapter, i, v_idx++); + for (i = 0; i < adapter->num_tx_queues; i++) + igb_map_tx_ring_to_vector(adapter, i, v_idx++); + } else { + for (i = 0; i < adapter->num_rx_queues; i++) { + if (i < adapter->num_tx_queues) + igb_map_tx_ring_to_vector(adapter, i, v_idx); + igb_map_rx_ring_to_vector(adapter, i, v_idx++); + } + for (; i < adapter->num_tx_queues; i++) + igb_map_tx_ring_to_vector(adapter, i, v_idx++); + } + return 0; +} + +/** + * igb_init_interrupt_scheme - initialize interrupts, allocate queues/vectors + * + * This function initializes the interrupts and allocates all of the queues. + **/ +static int igb_init_interrupt_scheme(struct igb_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + int err; + + igb_set_interrupt_capability(adapter); + + err = igb_alloc_q_vectors(adapter); + if (err) { + dev_err(&pdev->dev, "Unable to allocate memory for vectors\n"); + goto err_alloc_q_vectors; + } + + err = igb_alloc_queues(adapter); + if (err) { + dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); + goto err_alloc_queues; + } + + err = igb_map_ring_to_vector(adapter); + if (err) { + dev_err(&pdev->dev, "Invalid q_vector to ring mapping\n"); + goto err_map_queues; + } + + + return 0; +err_map_queues: + igb_free_queues(adapter); +err_alloc_queues: + igb_free_q_vectors(adapter); +err_alloc_q_vectors: + igb_reset_interrupt_capability(adapter); + return err; +} + +/** * igb_request_irq - initialize interrupts * * Attempts to configure interrupts using the best available @@ -747,6 +876,7 @@ out: static int igb_request_irq(struct igb_adapter *adapter) { struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; struct e1000_hw *hw = &adapter->hw; int err = 0; @@ -755,19 +885,38 @@ static int igb_request_irq(struct igb_adapter *adapter) if (!err) goto request_done; /* fall back to MSI */ - igb_reset_interrupt_capability(adapter); + igb_clear_interrupt_scheme(adapter); if (!pci_enable_msi(adapter->pdev)) adapter->flags |= IGB_FLAG_HAS_MSI; igb_free_all_tx_resources(adapter); igb_free_all_rx_resources(adapter); + adapter->num_tx_queues = 1; adapter->num_rx_queues = 1; - igb_alloc_queues(adapter); + adapter->num_q_vectors = 1; + err = igb_alloc_q_vectors(adapter); + if (err) { + dev_err(&pdev->dev, + "Unable to allocate memory for vectors\n"); + goto request_done; + } + err = igb_alloc_queues(adapter); + if (err) { + dev_err(&pdev->dev, + "Unable to allocate memory for queues\n"); + igb_free_q_vectors(adapter); + goto request_done; + } + igb_setup_all_tx_resources(adapter); + igb_setup_all_rx_resources(adapter); } else { switch (hw->mac.type) { case e1000_82575: wr32(E1000_MSIXBM(0), - (E1000_EICR_RX_QUEUE0 | E1000_EIMS_OTHER)); + (E1000_EICR_RX_QUEUE0 | + E1000_EICR_TX_QUEUE0 | + E1000_EIMS_OTHER)); break; + case e1000_82580: case e1000_82576: wr32(E1000_IVAR0, E1000_IVAR_VALID); break; @@ -777,17 +926,18 @@ static int igb_request_irq(struct igb_adapter *adapter) } if (adapter->flags & IGB_FLAG_HAS_MSI) { - err = request_irq(adapter->pdev->irq, &igb_intr_msi, 0, - netdev->name, netdev); + err = request_irq(adapter->pdev->irq, igb_intr_msi, 0, + netdev->name, adapter); if (!err) goto request_done; + /* fall back to legacy interrupts */ igb_reset_interrupt_capability(adapter); adapter->flags &= ~IGB_FLAG_HAS_MSI; } - err = request_irq(adapter->pdev->irq, &igb_intr, IRQF_SHARED, - netdev->name, netdev); + err = request_irq(adapter->pdev->irq, igb_intr, IRQF_SHARED, + netdev->name, adapter); if (err) dev_err(&adapter->pdev->dev, "Error %d getting interrupt\n", @@ -799,23 +949,19 @@ request_done: static void igb_free_irq(struct igb_adapter *adapter) { - struct net_device *netdev = adapter->netdev; - if (adapter->msix_entries) { int vector = 0, i; - for (i = 0; i < adapter->num_tx_queues; i++) - free_irq(adapter->msix_entries[vector++].vector, - &(adapter->tx_ring[i])); - for (i = 0; i < adapter->num_rx_queues; i++) - free_irq(adapter->msix_entries[vector++].vector, - &(adapter->rx_ring[i])); + free_irq(adapter->msix_entries[vector++].vector, adapter); - free_irq(adapter->msix_entries[vector++].vector, netdev); - return; + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + free_irq(adapter->msix_entries[vector++].vector, + q_vector); + } + } else { + free_irq(adapter->pdev->irq, adapter); } - - free_irq(adapter->pdev->irq, netdev); } /** @@ -826,6 +972,11 @@ static void igb_irq_disable(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; + /* + * we need to be careful when disabling interrupts. The VFs are also + * mapped into these registers and so clearing the bits can cause + * issues on the VF drivers so we only need to clear what we set + */ if (adapter->msix_entries) { u32 regval = rd32(E1000_EIAM); wr32(E1000_EIAM, regval & ~adapter->eims_enable_mask); @@ -849,41 +1000,47 @@ static void igb_irq_enable(struct igb_adapter *adapter) struct e1000_hw *hw = &adapter->hw; if (adapter->msix_entries) { + u32 ims = E1000_IMS_LSC | E1000_IMS_DOUTSYNC; u32 regval = rd32(E1000_EIAC); wr32(E1000_EIAC, regval | adapter->eims_enable_mask); regval = rd32(E1000_EIAM); wr32(E1000_EIAM, regval | adapter->eims_enable_mask); wr32(E1000_EIMS, adapter->eims_enable_mask); - if (adapter->vfs_allocated_count) + if (adapter->vfs_allocated_count) { wr32(E1000_MBVFIMR, 0xFF); - wr32(E1000_IMS, (E1000_IMS_LSC | E1000_IMS_VMMB | - E1000_IMS_DOUTSYNC)); + ims |= E1000_IMS_VMMB; + } + if (adapter->hw.mac.type == e1000_82580) + ims |= E1000_IMS_DRSTA; + + wr32(E1000_IMS, ims); } else { - wr32(E1000_IMS, IMS_ENABLE_MASK); - wr32(E1000_IAM, IMS_ENABLE_MASK); + wr32(E1000_IMS, IMS_ENABLE_MASK | + E1000_IMS_DRSTA); + wr32(E1000_IAM, IMS_ENABLE_MASK | + E1000_IMS_DRSTA); } } static void igb_update_mng_vlan(struct igb_adapter *adapter) { - struct net_device *netdev = adapter->netdev; + struct e1000_hw *hw = &adapter->hw; u16 vid = adapter->hw.mng_cookie.vlan_id; u16 old_vid = adapter->mng_vlan_id; - if (adapter->vlgrp) { - if (!vlan_group_get_device(adapter->vlgrp, vid)) { - if (adapter->hw.mng_cookie.status & - E1000_MNG_DHCP_COOKIE_STATUS_VLAN) { - igb_vlan_rx_add_vid(netdev, vid); - adapter->mng_vlan_id = vid; - } else - adapter->mng_vlan_id = IGB_MNG_VLAN_NONE; - if ((old_vid != (u16)IGB_MNG_VLAN_NONE) && - (vid != old_vid) && - !vlan_group_get_device(adapter->vlgrp, old_vid)) - igb_vlan_rx_kill_vid(netdev, old_vid); - } else - adapter->mng_vlan_id = vid; + if (hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN) { + /* add VID to filter table */ + igb_vfta_set(hw, vid, true); + adapter->mng_vlan_id = vid; + } else { + adapter->mng_vlan_id = IGB_MNG_VLAN_NONE; + } + + if ((old_vid != (u16)IGB_MNG_VLAN_NONE) && + (vid != old_vid) && + !vlan_group_get_device(adapter->vlgrp, old_vid)) { + /* remove VID from filter table */ + igb_vfta_set(hw, old_vid, false); } } @@ -907,7 +1064,6 @@ static void igb_release_hw_control(struct igb_adapter *adapter) ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); } - /** * igb_get_hw_control - get control of the h/w from f/w * @adapter: address of board private structure @@ -942,8 +1098,11 @@ static void igb_configure(struct igb_adapter *adapter) igb_restore_vlan(adapter); - igb_configure_tx(adapter); + igb_setup_tctl(adapter); + igb_setup_mrqc(adapter); igb_setup_rctl(adapter); + + igb_configure_tx(adapter); igb_configure_rx(adapter); igb_rx_fifo_flush_82575(&adapter->hw); @@ -965,7 +1124,6 @@ static void igb_configure(struct igb_adapter *adapter) * igb_up - Open the interface and prepare it to handle traffic * @adapter: board private structure **/ - int igb_up(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; @@ -976,30 +1134,37 @@ int igb_up(struct igb_adapter *adapter) clear_bit(__IGB_DOWN, &adapter->state); - for (i = 0; i < adapter->num_rx_queues; i++) - napi_enable(&adapter->rx_ring[i].napi); + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + napi_enable(&q_vector->napi); + } if (adapter->msix_entries) igb_configure_msix(adapter); - igb_vmm_control(adapter); - igb_set_rah_pool(hw, adapter->vfs_allocated_count, 0); - igb_set_vmolr(hw, adapter->vfs_allocated_count); - /* Clear any pending interrupts. */ rd32(E1000_ICR); igb_irq_enable(adapter); + /* notify VFs that reset has been completed */ + if (adapter->vfs_allocated_count) { + u32 reg_data = rd32(E1000_CTRL_EXT); + reg_data |= E1000_CTRL_EXT_PFRSTD; + wr32(E1000_CTRL_EXT, reg_data); + } + netif_tx_start_all_queues(adapter->netdev); - /* Fire a link change interrupt to start the watchdog. */ - wr32(E1000_ICS, E1000_ICS_LSC); + /* start the watchdog. */ + hw->mac.get_link_status = 1; + schedule_work(&adapter->watchdog_task); + return 0; } void igb_down(struct igb_adapter *adapter) { - struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; + struct e1000_hw *hw = &adapter->hw; u32 tctl, rctl; int i; @@ -1022,8 +1187,10 @@ void igb_down(struct igb_adapter *adapter) wrfl(); msleep(10); - for (i = 0; i < adapter->num_rx_queues; i++) - napi_disable(&adapter->rx_ring[i].napi); + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + napi_disable(&q_vector->napi); + } igb_irq_disable(adapter); @@ -1062,6 +1229,7 @@ void igb_reinit_locked(struct igb_adapter *adapter) void igb_reset(struct igb_adapter *adapter) { + struct pci_dev *pdev = adapter->pdev; struct e1000_hw *hw = &adapter->hw; struct e1000_mac_info *mac = &hw->mac; struct e1000_fc_info *fc = &hw->fc; @@ -1072,8 +1240,13 @@ void igb_reset(struct igb_adapter *adapter) * To take effect CTRL.RST is required. */ switch (mac->type) { + case e1000_82580: + pba = rd32(E1000_RXPBS); + pba = igb_rxpbs_adjust_82580(pba); + break; case e1000_82576: - pba = E1000_PBA_64K; + pba = rd32(E1000_RXPBS); + pba &= E1000_RXPBS_SIZE_MASK_82576; break; case e1000_82575: default: @@ -1148,10 +1321,10 @@ void igb_reset(struct igb_adapter *adapter) if (adapter->vfs_allocated_count) { int i; for (i = 0 ; i < adapter->vfs_allocated_count; i++) - adapter->vf_data[i].clear_to_send = false; + adapter->vf_data[i].flags = 0; /* ping all the active vfs to let them know we are going down */ - igb_ping_all_vfs(adapter); + igb_ping_all_vfs(adapter); /* disable transmits and receives */ wr32(E1000_VFRE, 0); @@ -1159,23 +1332,28 @@ void igb_reset(struct igb_adapter *adapter) } /* Allow time for pending master requests to run */ - adapter->hw.mac.ops.reset_hw(&adapter->hw); + hw->mac.ops.reset_hw(hw); wr32(E1000_WUC, 0); - if (adapter->hw.mac.ops.init_hw(&adapter->hw)) - dev_err(&adapter->pdev->dev, "Hardware Error\n"); + if (hw->mac.ops.init_hw(hw)) + dev_err(&pdev->dev, "Hardware Error\n"); + if (hw->mac.type == e1000_82580) { + u32 reg = rd32(E1000_PCIEMISC); + wr32(E1000_PCIEMISC, + reg & ~E1000_PCIEMISC_LX_DECISION); + } igb_update_mng_vlan(adapter); /* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */ wr32(E1000_VET, ETHERNET_IEEE_VLAN_TYPE); - igb_reset_adaptive(&adapter->hw); - igb_get_phy_info(&adapter->hw); + igb_reset_adaptive(hw); + igb_get_phy_info(hw); } static const struct net_device_ops igb_netdev_ops = { - .ndo_open = igb_open, + .ndo_open = igb_open, .ndo_stop = igb_close, .ndo_start_xmit = igb_xmit_frame_adv, .ndo_get_stats = igb_get_stats, @@ -1211,10 +1389,11 @@ static int __devinit igb_probe(struct pci_dev *pdev, struct net_device *netdev; struct igb_adapter *adapter; struct e1000_hw *hw; + u16 eeprom_data = 0; + static int global_quad_port_a; /* global quad port a indication */ const struct e1000_info *ei = igb_info_tbl[ent->driver_data]; unsigned long mmio_start, mmio_len; int err, pci_using_dac; - u16 eeprom_data = 0; u16 eeprom_apme_mask = IGB_EEPROM_APME; u32 part_num; @@ -1291,8 +1470,6 @@ static int __devinit igb_probe(struct pci_dev *pdev, hw->subsystem_vendor_id = pdev->subsystem_vendor; hw->subsystem_device_id = pdev->subsystem_device; - /* setup the private structure */ - hw->back = adapter; /* Copy the default MAC, PHY and NVM function pointers */ memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops)); memcpy(&hw->phy.ops, ei->phy_ops, sizeof(hw->phy.ops)); @@ -1302,46 +1479,6 @@ static int __devinit igb_probe(struct pci_dev *pdev, if (err) goto err_sw_init; -#ifdef CONFIG_PCI_IOV - /* since iov functionality isn't critical to base device function we - * can accept failure. If it fails we don't allow iov to be enabled */ - if (hw->mac.type == e1000_82576) { - /* 82576 supports a maximum of 7 VFs in addition to the PF */ - unsigned int num_vfs = (max_vfs > 7) ? 7 : max_vfs; - int i; - unsigned char mac_addr[ETH_ALEN]; - - if (num_vfs) { - adapter->vf_data = kcalloc(num_vfs, - sizeof(struct vf_data_storage), - GFP_KERNEL); - if (!adapter->vf_data) { - dev_err(&pdev->dev, - "Could not allocate VF private data - " - "IOV enable failed\n"); - } else { - err = pci_enable_sriov(pdev, num_vfs); - if (!err) { - adapter->vfs_allocated_count = num_vfs; - dev_info(&pdev->dev, - "%d vfs allocated\n", - num_vfs); - for (i = 0; - i < adapter->vfs_allocated_count; - i++) { - random_ether_addr(mac_addr); - igb_set_vf_mac(adapter, i, - mac_addr); - } - } else { - kfree(adapter->vf_data); - adapter->vf_data = NULL; - } - } - } - } - -#endif /* setup the private structure */ err = igb_sw_init(adapter); if (err) @@ -1349,16 +1486,6 @@ static int __devinit igb_probe(struct pci_dev *pdev, igb_get_bus_info_pcie(hw); - /* set flags */ - switch (hw->mac.type) { - case e1000_82575: - adapter->flags |= IGB_FLAG_NEED_CTX_IDX; - break; - case e1000_82576: - default: - break; - } - hw->phy.autoneg_wait_to_complete = false; hw->mac.adaptive_ifs = true; @@ -1382,7 +1509,6 @@ static int __devinit igb_probe(struct pci_dev *pdev, netdev->features |= NETIF_F_IPV6_CSUM; netdev->features |= NETIF_F_TSO; netdev->features |= NETIF_F_TSO6; - netdev->features |= NETIF_F_GRO; netdev->vlan_features |= NETIF_F_TSO; @@ -1394,10 +1520,10 @@ static int __devinit igb_probe(struct pci_dev *pdev, if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; - if (adapter->hw.mac.type == e1000_82576) + if (hw->mac.type >= e1000_82576) netdev->features |= NETIF_F_SCTP_CSUM; - adapter->en_mng_pt = igb_enable_mng_pass_thru(&adapter->hw); + adapter->en_mng_pt = igb_enable_mng_pass_thru(hw); /* before reading the NVM, reset the controller to put the device in a * known good starting state */ @@ -1439,9 +1565,6 @@ static int __devinit igb_probe(struct pci_dev *pdev, hw->fc.requested_mode = e1000_fc_default; hw->fc.current_mode = e1000_fc_default; - adapter->itr_setting = IGB_DEFAULT_ITR; - adapter->itr = IGB_START_ITR; - igb_validate_mdi_setting(hw); /* Initial Wake on LAN setting If APM wake is enabled in the EEPROM, @@ -1450,6 +1573,10 @@ static int __devinit igb_probe(struct pci_dev *pdev, if (hw->bus.func == 0) hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data); + else if (hw->mac.type == e1000_82580) + hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A + + NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1, + &eeprom_data); else if (hw->bus.func == 1) hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); @@ -1508,66 +1635,14 @@ static int __devinit igb_probe(struct pci_dev *pdev, dev_info(&pdev->dev, "DCA enabled\n"); igb_setup_dca(adapter); } -#endif - - /* - * Initialize hardware timer: we keep it running just in case - * that some program needs it later on. - */ - memset(&adapter->cycles, 0, sizeof(adapter->cycles)); - adapter->cycles.read = igb_read_clock; - adapter->cycles.mask = CLOCKSOURCE_MASK(64); - adapter->cycles.mult = 1; - adapter->cycles.shift = IGB_TSYNC_SHIFT; - wr32(E1000_TIMINCA, - (1<<24) | - IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS * IGB_TSYNC_SCALE); -#if 0 - /* - * Avoid rollover while we initialize by resetting the time counter. - */ - wr32(E1000_SYSTIML, 0x00000000); - wr32(E1000_SYSTIMH, 0x00000000); -#else - /* - * Set registers so that rollover occurs soon to test this. - */ - wr32(E1000_SYSTIML, 0x00000000); - wr32(E1000_SYSTIMH, 0xFF800000); -#endif - wrfl(); - timecounter_init(&adapter->clock, - &adapter->cycles, - ktime_to_ns(ktime_get_real())); - /* - * Synchronize our NIC clock against system wall clock. NIC - * time stamp reading requires ~3us per sample, each sample - * was pretty stable even under load => only require 10 - * samples for each offset comparison. - */ - memset(&adapter->compare, 0, sizeof(adapter->compare)); - adapter->compare.source = &adapter->clock; - adapter->compare.target = ktime_get_real; - adapter->compare.num_samples = 10; - timecompare_update(&adapter->compare, 0); - -#ifdef DEBUG - { - char buffer[160]; - printk(KERN_DEBUG - "igb: %s: hw %p initialized timer\n", - igb_get_time_str(adapter, buffer), - &adapter->hw); - } #endif - dev_info(&pdev->dev, "Intel(R) Gigabit Ethernet Network Connection\n"); /* print bus type/speed/width info */ dev_info(&pdev->dev, "%s: (PCIe:%s:%s) %pM\n", netdev->name, - ((hw->bus.speed == e1000_bus_speed_2500) - ? "2.5Gb/s" : "unknown"), + ((hw->bus.speed == e1000_bus_speed_2500) ? "2.5Gb/s" : + "unknown"), ((hw->bus.width == e1000_bus_width_pcie_x4) ? "Width x4" : (hw->bus.width == e1000_bus_width_pcie_x2) ? "Width x2" : (hw->bus.width == e1000_bus_width_pcie_x1) ? "Width x1" : @@ -1594,15 +1669,14 @@ err_eeprom: if (hw->flash_address) iounmap(hw->flash_address); - - igb_free_queues(adapter); err_sw_init: + igb_clear_interrupt_scheme(adapter); iounmap(hw->hw_addr); err_ioremap: free_netdev(netdev); err_alloc_etherdev: - pci_release_selected_regions(pdev, pci_select_bars(pdev, - IORESOURCE_MEM)); + pci_release_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM)); err_pci_reg: err_dma: pci_disable_device(pdev); @@ -1647,12 +1721,10 @@ static void __devexit igb_remove(struct pci_dev *pdev) unregister_netdev(netdev); - if (!igb_check_reset_block(&adapter->hw)) - igb_reset_phy(&adapter->hw); - - igb_reset_interrupt_capability(adapter); + if (!igb_check_reset_block(hw)) + igb_reset_phy(hw); - igb_free_queues(adapter); + igb_clear_interrupt_scheme(adapter); #ifdef CONFIG_PCI_IOV /* reclaim resources allocated to VFs */ @@ -1668,11 +1740,12 @@ static void __devexit igb_remove(struct pci_dev *pdev) dev_info(&pdev->dev, "IOV Disabled\n"); } #endif + iounmap(hw->hw_addr); if (hw->flash_address) iounmap(hw->flash_address); - pci_release_selected_regions(pdev, pci_select_bars(pdev, - IORESOURCE_MEM)); + pci_release_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM)); free_netdev(netdev); @@ -1682,6 +1755,160 @@ static void __devexit igb_remove(struct pci_dev *pdev) } /** + * igb_probe_vfs - Initialize vf data storage and add VFs to pci config space + * @adapter: board private structure to initialize + * + * This function initializes the vf specific data storage and then attempts to + * allocate the VFs. The reason for ordering it this way is because it is much + * mor expensive time wise to disable SR-IOV than it is to allocate and free + * the memory for the VFs. + **/ +static void __devinit igb_probe_vfs(struct igb_adapter * adapter) +{ +#ifdef CONFIG_PCI_IOV + struct pci_dev *pdev = adapter->pdev; + + if (adapter->vfs_allocated_count > 7) + adapter->vfs_allocated_count = 7; + + if (adapter->vfs_allocated_count) { + adapter->vf_data = kcalloc(adapter->vfs_allocated_count, + sizeof(struct vf_data_storage), + GFP_KERNEL); + /* if allocation failed then we do not support SR-IOV */ + if (!adapter->vf_data) { + adapter->vfs_allocated_count = 0; + dev_err(&pdev->dev, "Unable to allocate memory for VF " + "Data Storage\n"); + } + } + + if (pci_enable_sriov(pdev, adapter->vfs_allocated_count)) { + kfree(adapter->vf_data); + adapter->vf_data = NULL; +#endif /* CONFIG_PCI_IOV */ + adapter->vfs_allocated_count = 0; +#ifdef CONFIG_PCI_IOV + } else { + unsigned char mac_addr[ETH_ALEN]; + int i; + dev_info(&pdev->dev, "%d vfs allocated\n", + adapter->vfs_allocated_count); + for (i = 0; i < adapter->vfs_allocated_count; i++) { + random_ether_addr(mac_addr); + igb_set_vf_mac(adapter, i, mac_addr); + } + } +#endif /* CONFIG_PCI_IOV */ +} + + +/** + * igb_init_hw_timer - Initialize hardware timer used with IEEE 1588 timestamp + * @adapter: board private structure to initialize + * + * igb_init_hw_timer initializes the function pointer and values for the hw + * timer found in hardware. + **/ +static void igb_init_hw_timer(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + + switch (hw->mac.type) { + case e1000_82580: + memset(&adapter->cycles, 0, sizeof(adapter->cycles)); + adapter->cycles.read = igb_read_clock; + adapter->cycles.mask = CLOCKSOURCE_MASK(64); + adapter->cycles.mult = 1; + /* + * The 82580 timesync updates the system timer every 8ns by 8ns + * and the value cannot be shifted. Instead we need to shift + * the registers to generate a 64bit timer value. As a result + * SYSTIMR/L/H, TXSTMPL/H, RXSTMPL/H all have to be shifted by + * 24 in order to generate a larger value for synchronization. + */ + adapter->cycles.shift = IGB_82580_TSYNC_SHIFT; + /* disable system timer temporarily by setting bit 31 */ + wr32(E1000_TSAUXC, 0x80000000); + wrfl(); + + /* Set registers so that rollover occurs soon to test this. */ + wr32(E1000_SYSTIMR, 0x00000000); + wr32(E1000_SYSTIML, 0x80000000); + wr32(E1000_SYSTIMH, 0x000000FF); + wrfl(); + + /* enable system timer by clearing bit 31 */ + wr32(E1000_TSAUXC, 0x0); + wrfl(); + + timecounter_init(&adapter->clock, + &adapter->cycles, + ktime_to_ns(ktime_get_real())); + /* + * Synchronize our NIC clock against system wall clock. NIC + * time stamp reading requires ~3us per sample, each sample + * was pretty stable even under load => only require 10 + * samples for each offset comparison. + */ + memset(&adapter->compare, 0, sizeof(adapter->compare)); + adapter->compare.source = &adapter->clock; + adapter->compare.target = ktime_get_real; + adapter->compare.num_samples = 10; + timecompare_update(&adapter->compare, 0); + break; + case e1000_82576: + /* + * Initialize hardware timer: we keep it running just in case + * that some program needs it later on. + */ + memset(&adapter->cycles, 0, sizeof(adapter->cycles)); + adapter->cycles.read = igb_read_clock; + adapter->cycles.mask = CLOCKSOURCE_MASK(64); + adapter->cycles.mult = 1; + /** + * Scale the NIC clock cycle by a large factor so that + * relatively small clock corrections can be added or + * substracted at each clock tick. The drawbacks of a large + * factor are a) that the clock register overflows more quickly + * (not such a big deal) and b) that the increment per tick has + * to fit into 24 bits. As a result we need to use a shift of + * 19 so we can fit a value of 16 into the TIMINCA register. + */ + adapter->cycles.shift = IGB_82576_TSYNC_SHIFT; + wr32(E1000_TIMINCA, + (1 << E1000_TIMINCA_16NS_SHIFT) | + (16 << IGB_82576_TSYNC_SHIFT)); + + /* Set registers so that rollover occurs soon to test this. */ + wr32(E1000_SYSTIML, 0x00000000); + wr32(E1000_SYSTIMH, 0xFF800000); + wrfl(); + + timecounter_init(&adapter->clock, + &adapter->cycles, + ktime_to_ns(ktime_get_real())); + /* + * Synchronize our NIC clock against system wall clock. NIC + * time stamp reading requires ~3us per sample, each sample + * was pretty stable even under load => only require 10 + * samples for each offset comparison. + */ + memset(&adapter->compare, 0, sizeof(adapter->compare)); + adapter->compare.source = &adapter->clock; + adapter->compare.target = ktime_get_real; + adapter->compare.num_samples = 10; + timecompare_update(&adapter->compare, 0); + break; + case e1000_82575: + /* 82575 does not support timesync */ + default: + break; + } + +} + +/** * igb_sw_init - Initialize general software structures (struct igb_adapter) * @adapter: board private structure to initialize * @@ -1699,20 +1926,37 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter) adapter->tx_ring_count = IGB_DEFAULT_TXD; adapter->rx_ring_count = IGB_DEFAULT_RXD; - adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE; - adapter->rx_ps_hdr_size = 0; /* disable packet split */ + adapter->rx_itr_setting = IGB_DEFAULT_ITR; + adapter->tx_itr_setting = IGB_DEFAULT_ITR; + adapter->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN; adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN; - /* This call may decrease the number of queues depending on - * interrupt mode. */ - igb_set_interrupt_capability(adapter); +#ifdef CONFIG_PCI_IOV + if (hw->mac.type == e1000_82576) + adapter->vfs_allocated_count = max_vfs; - if (igb_alloc_queues(adapter)) { +#endif /* CONFIG_PCI_IOV */ + adapter->rss_queues = min_t(u32, IGB_MAX_RX_QUEUES, num_online_cpus()); + + /* + * if rss_queues > 4 or vfs are going to be allocated with rss_queues + * then we should combine the queues into a queue pair in order to + * conserve interrupts due to limited supply + */ + if ((adapter->rss_queues > 4) || + ((adapter->rss_queues > 1) && (adapter->vfs_allocated_count > 6))) + adapter->flags |= IGB_FLAG_QUEUE_PAIRS; + + /* This call may decrease the number of queues */ + if (igb_init_interrupt_scheme(adapter)) { dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); return -ENOMEM; } + igb_init_hw_timer(adapter); + igb_probe_vfs(adapter); + /* Explicitly disable IRQ since the NIC can be in any state. */ igb_irq_disable(adapter); @@ -1757,21 +2001,12 @@ static int igb_open(struct net_device *netdev) /* e1000_power_up_phy(adapter); */ - adapter->mng_vlan_id = IGB_MNG_VLAN_NONE; - if ((adapter->hw.mng_cookie.status & - E1000_MNG_DHCP_COOKIE_STATUS_VLAN)) - igb_update_mng_vlan(adapter); - /* before we allocate an interrupt, we must be ready to handle it. * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt * as soon as we call pci_request_irq, so we have to setup our * clean_rx handler before we do so. */ igb_configure(adapter); - igb_vmm_control(adapter); - igb_set_rah_pool(hw, adapter->vfs_allocated_count, 0); - igb_set_vmolr(hw, adapter->vfs_allocated_count); - err = igb_request_irq(adapter); if (err) goto err_req_irq; @@ -1779,18 +2014,28 @@ static int igb_open(struct net_device *netdev) /* From here on the code is the same as igb_up() */ clear_bit(__IGB_DOWN, &adapter->state); - for (i = 0; i < adapter->num_rx_queues; i++) - napi_enable(&adapter->rx_ring[i].napi); + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + napi_enable(&q_vector->napi); + } /* Clear any pending interrupts. */ rd32(E1000_ICR); igb_irq_enable(adapter); + /* notify VFs that reset has been completed */ + if (adapter->vfs_allocated_count) { + u32 reg_data = rd32(E1000_CTRL_EXT); + reg_data |= E1000_CTRL_EXT_PFRSTD; + wr32(E1000_CTRL_EXT, reg_data); + } + netif_tx_start_all_queues(netdev); - /* Fire a link status change interrupt to start the watchdog. */ - wr32(E1000_ICS, E1000_ICS_LSC); + /* start the watchdog. */ + hw->mac.get_link_status = 1; + schedule_work(&adapter->watchdog_task); return 0; @@ -1829,28 +2074,18 @@ static int igb_close(struct net_device *netdev) igb_free_all_tx_resources(adapter); igb_free_all_rx_resources(adapter); - /* kill manageability vlan ID if supported, but not if a vlan with - * the same ID is registered on the host OS (let 8021q kill it) */ - if ((adapter->hw.mng_cookie.status & - E1000_MNG_DHCP_COOKIE_STATUS_VLAN) && - !(adapter->vlgrp && - vlan_group_get_device(adapter->vlgrp, adapter->mng_vlan_id))) - igb_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id); - return 0; } /** * igb_setup_tx_resources - allocate Tx resources (Descriptors) - * @adapter: board private structure * @tx_ring: tx descriptor ring (for a specific queue) to setup * * Return 0 on success, negative on failure **/ -int igb_setup_tx_resources(struct igb_adapter *adapter, - struct igb_ring *tx_ring) +int igb_setup_tx_resources(struct igb_ring *tx_ring) { - struct pci_dev *pdev = adapter->pdev; + struct pci_dev *pdev = tx_ring->pdev; int size; size = sizeof(struct igb_buffer) * tx_ring->count; @@ -1863,20 +2098,20 @@ int igb_setup_tx_resources(struct igb_adapter *adapter, tx_ring->size = tx_ring->count * sizeof(union e1000_adv_tx_desc); tx_ring->size = ALIGN(tx_ring->size, 4096); - tx_ring->desc = pci_alloc_consistent(pdev, tx_ring->size, + tx_ring->desc = pci_alloc_consistent(pdev, + tx_ring->size, &tx_ring->dma); if (!tx_ring->desc) goto err; - tx_ring->adapter = adapter; tx_ring->next_to_use = 0; tx_ring->next_to_clean = 0; return 0; err: vfree(tx_ring->buffer_info); - dev_err(&adapter->pdev->dev, + dev_err(&pdev->dev, "Unable to allocate memory for the transmit descriptor ring\n"); return -ENOMEM; } @@ -1890,13 +2125,13 @@ err: **/ static int igb_setup_all_tx_resources(struct igb_adapter *adapter) { + struct pci_dev *pdev = adapter->pdev; int i, err = 0; - int r_idx; for (i = 0; i < adapter->num_tx_queues; i++) { - err = igb_setup_tx_resources(adapter, &adapter->tx_ring[i]); + err = igb_setup_tx_resources(&adapter->tx_ring[i]); if (err) { - dev_err(&adapter->pdev->dev, + dev_err(&pdev->dev, "Allocation for Tx Queue %u failed\n", i); for (i--; i >= 0; i--) igb_free_tx_resources(&adapter->tx_ring[i]); @@ -1904,57 +2139,24 @@ static int igb_setup_all_tx_resources(struct igb_adapter *adapter) } } - for (i = 0; i < IGB_MAX_TX_QUEUES; i++) { - r_idx = i % adapter->num_tx_queues; + for (i = 0; i < IGB_ABS_MAX_TX_QUEUES; i++) { + int r_idx = i % adapter->num_tx_queues; adapter->multi_tx_table[i] = &adapter->tx_ring[r_idx]; } return err; } /** - * igb_configure_tx - Configure transmit Unit after Reset - * @adapter: board private structure - * - * Configure the Tx unit of the MAC after a reset. + * igb_setup_tctl - configure the transmit control registers + * @adapter: Board private structure **/ -static void igb_configure_tx(struct igb_adapter *adapter) +void igb_setup_tctl(struct igb_adapter *adapter) { - u64 tdba; struct e1000_hw *hw = &adapter->hw; u32 tctl; - u32 txdctl, txctrl; - int i, j; - - for (i = 0; i < adapter->num_tx_queues; i++) { - struct igb_ring *ring = &adapter->tx_ring[i]; - j = ring->reg_idx; - wr32(E1000_TDLEN(j), - ring->count * sizeof(union e1000_adv_tx_desc)); - tdba = ring->dma; - wr32(E1000_TDBAL(j), - tdba & 0x00000000ffffffffULL); - wr32(E1000_TDBAH(j), tdba >> 32); - - ring->head = E1000_TDH(j); - ring->tail = E1000_TDT(j); - writel(0, hw->hw_addr + ring->tail); - writel(0, hw->hw_addr + ring->head); - txdctl = rd32(E1000_TXDCTL(j)); - txdctl |= E1000_TXDCTL_QUEUE_ENABLE; - wr32(E1000_TXDCTL(j), txdctl); - - /* Turn off Relaxed Ordering on head write-backs. The - * writebacks MUST be delivered in order or it will - * completely screw up our bookeeping. - */ - txctrl = rd32(E1000_DCA_TXCTRL(j)); - txctrl &= ~E1000_DCA_TXCTRL_TX_WB_RO_EN; - wr32(E1000_DCA_TXCTRL(j), txctrl); - } - /* disable queue 0 to prevent tail bump w/o re-configuration */ - if (adapter->vfs_allocated_count) - wr32(E1000_TXDCTL(0), 0); + /* disable queue 0 which is enabled by default on 82575 and 82576 */ + wr32(E1000_TXDCTL(0), 0); /* Program the Transmit Control Register */ tctl = rd32(E1000_TCTL); @@ -1964,9 +2166,6 @@ static void igb_configure_tx(struct igb_adapter *adapter) igb_config_collision_dist(hw); - /* Setup Transmit Descriptor Settings for eop descriptor */ - adapter->txd_cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS; - /* Enable transmits */ tctl |= E1000_TCTL_EN; @@ -1974,16 +2173,69 @@ static void igb_configure_tx(struct igb_adapter *adapter) } /** - * igb_setup_rx_resources - allocate Rx resources (Descriptors) + * igb_configure_tx_ring - Configure transmit ring after Reset + * @adapter: board private structure + * @ring: tx ring to configure + * + * Configure a transmit ring after a reset. + **/ +void igb_configure_tx_ring(struct igb_adapter *adapter, + struct igb_ring *ring) +{ + struct e1000_hw *hw = &adapter->hw; + u32 txdctl; + u64 tdba = ring->dma; + int reg_idx = ring->reg_idx; + + /* disable the queue */ + txdctl = rd32(E1000_TXDCTL(reg_idx)); + wr32(E1000_TXDCTL(reg_idx), + txdctl & ~E1000_TXDCTL_QUEUE_ENABLE); + wrfl(); + mdelay(10); + + wr32(E1000_TDLEN(reg_idx), + ring->count * sizeof(union e1000_adv_tx_desc)); + wr32(E1000_TDBAL(reg_idx), + tdba & 0x00000000ffffffffULL); + wr32(E1000_TDBAH(reg_idx), tdba >> 32); + + ring->head = hw->hw_addr + E1000_TDH(reg_idx); + ring->tail = hw->hw_addr + E1000_TDT(reg_idx); + writel(0, ring->head); + writel(0, ring->tail); + + txdctl |= IGB_TX_PTHRESH; + txdctl |= IGB_TX_HTHRESH << 8; + txdctl |= IGB_TX_WTHRESH << 16; + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + wr32(E1000_TXDCTL(reg_idx), txdctl); +} + +/** + * igb_configure_tx - Configure transmit Unit after Reset * @adapter: board private structure + * + * Configure the Tx unit of the MAC after a reset. + **/ +static void igb_configure_tx(struct igb_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->num_tx_queues; i++) + igb_configure_tx_ring(adapter, &adapter->tx_ring[i]); +} + +/** + * igb_setup_rx_resources - allocate Rx resources (Descriptors) * @rx_ring: rx descriptor ring (for a specific queue) to setup * * Returns 0 on success, negative on failure **/ -int igb_setup_rx_resources(struct igb_adapter *adapter, - struct igb_ring *rx_ring) +int igb_setup_rx_resources(struct igb_ring *rx_ring) { - struct pci_dev *pdev = adapter->pdev; + struct pci_dev *pdev = rx_ring->pdev; int size, desc_len; size = sizeof(struct igb_buffer) * rx_ring->count; @@ -2007,13 +2259,12 @@ int igb_setup_rx_resources(struct igb_adapter *adapter, rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; - rx_ring->adapter = adapter; - return 0; err: vfree(rx_ring->buffer_info); - dev_err(&adapter->pdev->dev, "Unable to allocate memory for " + rx_ring->buffer_info = NULL; + dev_err(&pdev->dev, "Unable to allocate memory for " "the receive descriptor ring\n"); return -ENOMEM; } @@ -2027,12 +2278,13 @@ err: **/ static int igb_setup_all_rx_resources(struct igb_adapter *adapter) { + struct pci_dev *pdev = adapter->pdev; int i, err = 0; for (i = 0; i < adapter->num_rx_queues; i++) { - err = igb_setup_rx_resources(adapter, &adapter->rx_ring[i]); + err = igb_setup_rx_resources(&adapter->rx_ring[i]); if (err) { - dev_err(&adapter->pdev->dev, + dev_err(&pdev->dev, "Allocation for Rx Queue %u failed\n", i); for (i--; i >= 0; i--) igb_free_rx_resources(&adapter->rx_ring[i]); @@ -2044,15 +2296,122 @@ static int igb_setup_all_rx_resources(struct igb_adapter *adapter) } /** + * igb_setup_mrqc - configure the multiple receive queue control registers + * @adapter: Board private structure + **/ +static void igb_setup_mrqc(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + u32 mrqc, rxcsum; + u32 j, num_rx_queues, shift = 0, shift2 = 0; + union e1000_reta { + u32 dword; + u8 bytes[4]; + } reta; + static const u8 rsshash[40] = { + 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 0x41, 0x67, + 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 0xd0, 0xca, 0x2b, 0xcb, + 0xae, 0x7b, 0x30, 0xb4, 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, + 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa }; + + /* Fill out hash function seeds */ + for (j = 0; j < 10; j++) { + u32 rsskey = rsshash[(j * 4)]; + rsskey |= rsshash[(j * 4) + 1] << 8; + rsskey |= rsshash[(j * 4) + 2] << 16; + rsskey |= rsshash[(j * 4) + 3] << 24; + array_wr32(E1000_RSSRK(0), j, rsskey); + } + + num_rx_queues = adapter->rss_queues; + + if (adapter->vfs_allocated_count) { + /* 82575 and 82576 supports 2 RSS queues for VMDq */ + switch (hw->mac.type) { + case e1000_82580: + num_rx_queues = 1; + shift = 0; + break; + case e1000_82576: + shift = 3; + num_rx_queues = 2; + break; + case e1000_82575: + shift = 2; + shift2 = 6; + default: + break; + } + } else { + if (hw->mac.type == e1000_82575) + shift = 6; + } + + for (j = 0; j < (32 * 4); j++) { + reta.bytes[j & 3] = (j % num_rx_queues) << shift; + if (shift2) + reta.bytes[j & 3] |= num_rx_queues << shift2; + if ((j & 3) == 3) + wr32(E1000_RETA(j >> 2), reta.dword); + } + + /* + * Disable raw packet checksumming so that RSS hash is placed in + * descriptor on writeback. No need to enable TCP/UDP/IP checksum + * offloads as they are enabled by default + */ + rxcsum = rd32(E1000_RXCSUM); + rxcsum |= E1000_RXCSUM_PCSD; + + if (adapter->hw.mac.type >= e1000_82576) + /* Enable Receive Checksum Offload for SCTP */ + rxcsum |= E1000_RXCSUM_CRCOFL; + + /* Don't need to set TUOFL or IPOFL, they default to 1 */ + wr32(E1000_RXCSUM, rxcsum); + + /* If VMDq is enabled then we set the appropriate mode for that, else + * we default to RSS so that an RSS hash is calculated per packet even + * if we are only using one queue */ + if (adapter->vfs_allocated_count) { + if (hw->mac.type > e1000_82575) { + /* Set the default pool for the PF's first queue */ + u32 vtctl = rd32(E1000_VT_CTL); + vtctl &= ~(E1000_VT_CTL_DEFAULT_POOL_MASK | + E1000_VT_CTL_DISABLE_DEF_POOL); + vtctl |= adapter->vfs_allocated_count << + E1000_VT_CTL_DEFAULT_POOL_SHIFT; + wr32(E1000_VT_CTL, vtctl); + } + if (adapter->rss_queues > 1) + mrqc = E1000_MRQC_ENABLE_VMDQ_RSS_2Q; + else + mrqc = E1000_MRQC_ENABLE_VMDQ; + } else { + mrqc = E1000_MRQC_ENABLE_RSS_4Q; + } + igb_vmm_control(adapter); + + mrqc |= (E1000_MRQC_RSS_FIELD_IPV4 | + E1000_MRQC_RSS_FIELD_IPV4_TCP); + mrqc |= (E1000_MRQC_RSS_FIELD_IPV6 | + E1000_MRQC_RSS_FIELD_IPV6_TCP); + mrqc |= (E1000_MRQC_RSS_FIELD_IPV4_UDP | + E1000_MRQC_RSS_FIELD_IPV6_UDP); + mrqc |= (E1000_MRQC_RSS_FIELD_IPV6_UDP_EX | + E1000_MRQC_RSS_FIELD_IPV6_TCP_EX); + + wr32(E1000_MRQC, mrqc); +} + +/** * igb_setup_rctl - configure the receive control registers * @adapter: Board private structure **/ -static void igb_setup_rctl(struct igb_adapter *adapter) +void igb_setup_rctl(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; u32 rctl; - u32 srrctl = 0; - int i; rctl = rd32(E1000_RCTL); @@ -2069,75 +2428,45 @@ static void igb_setup_rctl(struct igb_adapter *adapter) */ rctl |= E1000_RCTL_SECRC; - /* - * disable store bad packets and clear size bits. - */ + /* disable store bad packets and clear size bits. */ rctl &= ~(E1000_RCTL_SBP | E1000_RCTL_SZ_256); - /* enable LPE when to prevent packets larger than max_frame_size */ - rctl |= E1000_RCTL_LPE; - - /* Setup buffer sizes */ - switch (adapter->rx_buffer_len) { - case IGB_RXBUFFER_256: - rctl |= E1000_RCTL_SZ_256; - break; - case IGB_RXBUFFER_512: - rctl |= E1000_RCTL_SZ_512; - break; - default: - srrctl = ALIGN(adapter->rx_buffer_len, 1024) - >> E1000_SRRCTL_BSIZEPKT_SHIFT; - break; - } + /* enable LPE to prevent packets larger than max_frame_size */ + rctl |= E1000_RCTL_LPE; - /* 82575 and greater support packet-split where the protocol - * header is placed in skb->data and the packet data is - * placed in pages hanging off of skb_shinfo(skb)->nr_frags. - * In the case of a non-split, skb->data is linearly filled, - * followed by the page buffers. Therefore, skb->data is - * sized to hold the largest protocol header. - */ - /* allocations using alloc_page take too long for regular MTU - * so only enable packet split for jumbo frames */ - if (adapter->netdev->mtu > ETH_DATA_LEN) { - adapter->rx_ps_hdr_size = IGB_RXBUFFER_128; - srrctl |= adapter->rx_ps_hdr_size << - E1000_SRRCTL_BSIZEHDRSIZE_SHIFT; - srrctl |= E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS; - } else { - adapter->rx_ps_hdr_size = 0; - srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; - } + /* disable queue 0 to prevent tail write w/o re-config */ + wr32(E1000_RXDCTL(0), 0); /* Attention!!! For SR-IOV PF driver operations you must enable * queue drop for all VF and PF queues to prevent head of line blocking * if an un-trusted VF does not provide descriptors to hardware. */ if (adapter->vfs_allocated_count) { - u32 vmolr; - /* set all queue drop enable bits */ wr32(E1000_QDE, ALL_QUEUES); - srrctl |= E1000_SRRCTL_DROP_EN; + } - /* disable queue 0 to prevent tail write w/o re-config */ - wr32(E1000_RXDCTL(0), 0); + wr32(E1000_RCTL, rctl); +} - vmolr = rd32(E1000_VMOLR(adapter->vfs_allocated_count)); - if (rctl & E1000_RCTL_LPE) - vmolr |= E1000_VMOLR_LPE; - if (adapter->num_rx_queues > 1) - vmolr |= E1000_VMOLR_RSSE; - wr32(E1000_VMOLR(adapter->vfs_allocated_count), vmolr); - } +static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size, + int vfn) +{ + struct e1000_hw *hw = &adapter->hw; + u32 vmolr; - for (i = 0; i < adapter->num_rx_queues; i++) { - int j = adapter->rx_ring[i].reg_idx; - wr32(E1000_SRRCTL(j), srrctl); - } + /* if it isn't the PF check to see if VFs are enabled and + * increase the size to support vlan tags */ + if (vfn < adapter->vfs_allocated_count && + adapter->vf_data[vfn].vlans_enabled) + size += VLAN_TAG_SIZE; - wr32(E1000_RCTL, rctl); + vmolr = rd32(E1000_VMOLR(vfn)); + vmolr &= ~E1000_VMOLR_RLPML_MASK; + vmolr |= size | E1000_VMOLR_LPE; + wr32(E1000_VMOLR(vfn), vmolr); + + return 0; } /** @@ -2159,33 +2488,107 @@ static void igb_rlpml_set(struct igb_adapter *adapter) * size and set the VMOLR RLPML to the size we need */ if (pf_id) { igb_set_vf_rlpml(adapter, max_frame_size, pf_id); - max_frame_size = MAX_STD_JUMBO_FRAME_SIZE + VLAN_TAG_SIZE; + max_frame_size = MAX_JUMBO_FRAME_SIZE; } wr32(E1000_RLPML, max_frame_size); } +static inline void igb_set_vmolr(struct igb_adapter *adapter, int vfn) +{ + struct e1000_hw *hw = &adapter->hw; + u32 vmolr; + + /* + * This register exists only on 82576 and newer so if we are older then + * we should exit and do nothing + */ + if (hw->mac.type < e1000_82576) + return; + + vmolr = rd32(E1000_VMOLR(vfn)); + vmolr |= E1000_VMOLR_AUPE | /* Accept untagged packets */ + E1000_VMOLR_STRVLAN; /* Strip vlan tags */ + + /* clear all bits that might not be set */ + vmolr &= ~(E1000_VMOLR_BAM | E1000_VMOLR_RSSE); + + if (adapter->rss_queues > 1 && vfn == adapter->vfs_allocated_count) + vmolr |= E1000_VMOLR_RSSE; /* enable RSS */ + /* + * for VMDq only allow the VFs and pool 0 to accept broadcast and + * multicast packets + */ + if (vfn <= adapter->vfs_allocated_count) + vmolr |= E1000_VMOLR_BAM; /* Accept broadcast */ + + wr32(E1000_VMOLR(vfn), vmolr); +} + /** - * igb_configure_vt_default_pool - Configure VT default pool + * igb_configure_rx_ring - Configure a receive ring after Reset * @adapter: board private structure + * @ring: receive ring to be configured * - * Configure the default pool + * Configure the Rx unit of the MAC after a reset. **/ -static void igb_configure_vt_default_pool(struct igb_adapter *adapter) +void igb_configure_rx_ring(struct igb_adapter *adapter, + struct igb_ring *ring) { struct e1000_hw *hw = &adapter->hw; - u16 pf_id = adapter->vfs_allocated_count; - u32 vtctl; + u64 rdba = ring->dma; + int reg_idx = ring->reg_idx; + u32 srrctl, rxdctl; + + /* disable the queue */ + rxdctl = rd32(E1000_RXDCTL(reg_idx)); + wr32(E1000_RXDCTL(reg_idx), + rxdctl & ~E1000_RXDCTL_QUEUE_ENABLE); + + /* Set DMA base address registers */ + wr32(E1000_RDBAL(reg_idx), + rdba & 0x00000000ffffffffULL); + wr32(E1000_RDBAH(reg_idx), rdba >> 32); + wr32(E1000_RDLEN(reg_idx), + ring->count * sizeof(union e1000_adv_rx_desc)); + + /* initialize head and tail */ + ring->head = hw->hw_addr + E1000_RDH(reg_idx); + ring->tail = hw->hw_addr + E1000_RDT(reg_idx); + writel(0, ring->head); + writel(0, ring->tail); + + /* set descriptor configuration */ + if (ring->rx_buffer_len < IGB_RXBUFFER_1024) { + srrctl = ALIGN(ring->rx_buffer_len, 64) << + E1000_SRRCTL_BSIZEHDRSIZE_SHIFT; +#if (PAGE_SIZE / 2) > IGB_RXBUFFER_16384 + srrctl |= IGB_RXBUFFER_16384 >> + E1000_SRRCTL_BSIZEPKT_SHIFT; +#else + srrctl |= (PAGE_SIZE / 2) >> + E1000_SRRCTL_BSIZEPKT_SHIFT; +#endif + srrctl |= E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS; + } else { + srrctl = ALIGN(ring->rx_buffer_len, 1024) >> + E1000_SRRCTL_BSIZEPKT_SHIFT; + srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; + } - /* not in sr-iov mode - do nothing */ - if (!pf_id) - return; + wr32(E1000_SRRCTL(reg_idx), srrctl); + + /* set filtering for VMDQ pools */ + igb_set_vmolr(adapter, reg_idx & 0x7); - vtctl = rd32(E1000_VT_CTL); - vtctl &= ~(E1000_VT_CTL_DEFAULT_POOL_MASK | - E1000_VT_CTL_DISABLE_DEF_POOL); - vtctl |= pf_id << E1000_VT_CTL_DEFAULT_POOL_SHIFT; - wr32(E1000_VT_CTL, vtctl); + /* enable receive descriptor fetching */ + rxdctl = rd32(E1000_RXDCTL(reg_idx)); + rxdctl |= E1000_RXDCTL_QUEUE_ENABLE; + rxdctl &= 0xFFF00000; + rxdctl |= IGB_RX_PTHRESH; + rxdctl |= IGB_RX_HTHRESH << 8; + rxdctl |= IGB_RX_WTHRESH << 16; + wr32(E1000_RXDCTL(reg_idx), rxdctl); } /** @@ -2196,112 +2599,19 @@ static void igb_configure_vt_default_pool(struct igb_adapter *adapter) **/ static void igb_configure_rx(struct igb_adapter *adapter) { - u64 rdba; - struct e1000_hw *hw = &adapter->hw; - u32 rctl, rxcsum; - u32 rxdctl; int i; - /* disable receives while setting up the descriptors */ - rctl = rd32(E1000_RCTL); - wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN); - wrfl(); - mdelay(10); + /* set UTA to appropriate mode */ + igb_set_uta(adapter); - if (adapter->itr_setting > 3) - wr32(E1000_ITR, adapter->itr); + /* set the correct pool for the PF default MAC address in entry 0 */ + igb_rar_set_qsel(adapter, adapter->hw.mac.addr, 0, + adapter->vfs_allocated_count); /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ - for (i = 0; i < adapter->num_rx_queues; i++) { - struct igb_ring *ring = &adapter->rx_ring[i]; - int j = ring->reg_idx; - rdba = ring->dma; - wr32(E1000_RDBAL(j), - rdba & 0x00000000ffffffffULL); - wr32(E1000_RDBAH(j), rdba >> 32); - wr32(E1000_RDLEN(j), - ring->count * sizeof(union e1000_adv_rx_desc)); - - ring->head = E1000_RDH(j); - ring->tail = E1000_RDT(j); - writel(0, hw->hw_addr + ring->tail); - writel(0, hw->hw_addr + ring->head); - - rxdctl = rd32(E1000_RXDCTL(j)); - rxdctl |= E1000_RXDCTL_QUEUE_ENABLE; - rxdctl &= 0xFFF00000; - rxdctl |= IGB_RX_PTHRESH; - rxdctl |= IGB_RX_HTHRESH << 8; - rxdctl |= IGB_RX_WTHRESH << 16; - wr32(E1000_RXDCTL(j), rxdctl); - } - - if (adapter->num_rx_queues > 1) { - u32 random[10]; - u32 mrqc; - u32 j, shift; - union e1000_reta { - u32 dword; - u8 bytes[4]; - } reta; - - get_random_bytes(&random[0], 40); - - if (hw->mac.type >= e1000_82576) - shift = 0; - else - shift = 6; - for (j = 0; j < (32 * 4); j++) { - reta.bytes[j & 3] = - adapter->rx_ring[(j % adapter->num_rx_queues)].reg_idx << shift; - if ((j & 3) == 3) - writel(reta.dword, - hw->hw_addr + E1000_RETA(0) + (j & ~3)); - } - if (adapter->vfs_allocated_count) - mrqc = E1000_MRQC_ENABLE_VMDQ_RSS_2Q; - else - mrqc = E1000_MRQC_ENABLE_RSS_4Q; - - /* Fill out hash function seeds */ - for (j = 0; j < 10; j++) - array_wr32(E1000_RSSRK(0), j, random[j]); - - mrqc |= (E1000_MRQC_RSS_FIELD_IPV4 | - E1000_MRQC_RSS_FIELD_IPV4_TCP); - mrqc |= (E1000_MRQC_RSS_FIELD_IPV6 | - E1000_MRQC_RSS_FIELD_IPV6_TCP); - mrqc |= (E1000_MRQC_RSS_FIELD_IPV4_UDP | - E1000_MRQC_RSS_FIELD_IPV6_UDP); - mrqc |= (E1000_MRQC_RSS_FIELD_IPV6_UDP_EX | - E1000_MRQC_RSS_FIELD_IPV6_TCP_EX); - - wr32(E1000_MRQC, mrqc); - } else if (adapter->vfs_allocated_count) { - /* Enable multi-queue for sr-iov */ - wr32(E1000_MRQC, E1000_MRQC_ENABLE_VMDQ); - } - - /* Enable Receive Checksum Offload for TCP and UDP */ - rxcsum = rd32(E1000_RXCSUM); - /* Disable raw packet checksumming */ - rxcsum |= E1000_RXCSUM_PCSD; - - if (adapter->hw.mac.type == e1000_82576) - /* Enable Receive Checksum Offload for SCTP */ - rxcsum |= E1000_RXCSUM_CRCOFL; - - /* Don't need to set TUOFL or IPOFL, they default to 1 */ - wr32(E1000_RXCSUM, rxcsum); - - /* Set the default pool for the PF's first queue */ - igb_configure_vt_default_pool(adapter); - - igb_rlpml_set(adapter); - - /* Enable Receives */ - wr32(E1000_RCTL, rctl); + for (i = 0; i < adapter->num_rx_queues; i++) + igb_configure_rx_ring(adapter, &adapter->rx_ring[i]); } /** @@ -2312,14 +2622,17 @@ static void igb_configure_rx(struct igb_adapter *adapter) **/ void igb_free_tx_resources(struct igb_ring *tx_ring) { - struct pci_dev *pdev = tx_ring->adapter->pdev; - igb_clean_tx_ring(tx_ring); vfree(tx_ring->buffer_info); tx_ring->buffer_info = NULL; - pci_free_consistent(pdev, tx_ring->size, tx_ring->desc, tx_ring->dma); + /* if not set, then don't free */ + if (!tx_ring->desc) + return; + + pci_free_consistent(tx_ring->pdev, tx_ring->size, + tx_ring->desc, tx_ring->dma); tx_ring->desc = NULL; } @@ -2338,18 +2651,30 @@ static void igb_free_all_tx_resources(struct igb_adapter *adapter) igb_free_tx_resources(&adapter->tx_ring[i]); } -static void igb_unmap_and_free_tx_resource(struct igb_adapter *adapter, - struct igb_buffer *buffer_info) +void igb_unmap_and_free_tx_resource(struct igb_ring *tx_ring, + struct igb_buffer *buffer_info) { - buffer_info->dma = 0; + if (buffer_info->dma) { + if (buffer_info->mapped_as_page) + pci_unmap_page(tx_ring->pdev, + buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + else + pci_unmap_single(tx_ring->pdev, + buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + buffer_info->dma = 0; + } if (buffer_info->skb) { - skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb, - DMA_TO_DEVICE); dev_kfree_skb_any(buffer_info->skb); buffer_info->skb = NULL; } buffer_info->time_stamp = 0; - /* buffer_info must be completely set up in the transmit path */ + buffer_info->length = 0; + buffer_info->next_to_watch = 0; + buffer_info->mapped_as_page = false; } /** @@ -2358,7 +2683,6 @@ static void igb_unmap_and_free_tx_resource(struct igb_adapter *adapter, **/ static void igb_clean_tx_ring(struct igb_ring *tx_ring) { - struct igb_adapter *adapter = tx_ring->adapter; struct igb_buffer *buffer_info; unsigned long size; unsigned int i; @@ -2369,21 +2693,17 @@ static void igb_clean_tx_ring(struct igb_ring *tx_ring) for (i = 0; i < tx_ring->count; i++) { buffer_info = &tx_ring->buffer_info[i]; - igb_unmap_and_free_tx_resource(adapter, buffer_info); + igb_unmap_and_free_tx_resource(tx_ring, buffer_info); } size = sizeof(struct igb_buffer) * tx_ring->count; memset(tx_ring->buffer_info, 0, size); /* Zero out the descriptor ring */ - memset(tx_ring->desc, 0, tx_ring->size); tx_ring->next_to_use = 0; tx_ring->next_to_clean = 0; - - writel(0, adapter->hw.hw_addr + tx_ring->head); - writel(0, adapter->hw.hw_addr + tx_ring->tail); } /** @@ -2406,14 +2726,17 @@ static void igb_clean_all_tx_rings(struct igb_adapter *adapter) **/ void igb_free_rx_resources(struct igb_ring *rx_ring) { - struct pci_dev *pdev = rx_ring->adapter->pdev; - igb_clean_rx_ring(rx_ring); vfree(rx_ring->buffer_info); rx_ring->buffer_info = NULL; - pci_free_consistent(pdev, rx_ring->size, rx_ring->desc, rx_ring->dma); + /* if not set, then don't free */ + if (!rx_ring->desc) + return; + + pci_free_consistent(rx_ring->pdev, rx_ring->size, + rx_ring->desc, rx_ring->dma); rx_ring->desc = NULL; } @@ -2438,26 +2761,21 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter) **/ static void igb_clean_rx_ring(struct igb_ring *rx_ring) { - struct igb_adapter *adapter = rx_ring->adapter; struct igb_buffer *buffer_info; - struct pci_dev *pdev = adapter->pdev; unsigned long size; unsigned int i; if (!rx_ring->buffer_info) return; + /* Free all the Rx ring sk_buffs */ for (i = 0; i < rx_ring->count; i++) { buffer_info = &rx_ring->buffer_info[i]; if (buffer_info->dma) { - if (adapter->rx_ps_hdr_size) - pci_unmap_single(pdev, buffer_info->dma, - adapter->rx_ps_hdr_size, - PCI_DMA_FROMDEVICE); - else - pci_unmap_single(pdev, buffer_info->dma, - adapter->rx_buffer_len, - PCI_DMA_FROMDEVICE); + pci_unmap_single(rx_ring->pdev, + buffer_info->dma, + rx_ring->rx_buffer_len, + PCI_DMA_FROMDEVICE); buffer_info->dma = 0; } @@ -2465,14 +2783,16 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring) dev_kfree_skb(buffer_info->skb); buffer_info->skb = NULL; } + if (buffer_info->page_dma) { + pci_unmap_page(rx_ring->pdev, + buffer_info->page_dma, + PAGE_SIZE / 2, + PCI_DMA_FROMDEVICE); + buffer_info->page_dma = 0; + } if (buffer_info->page) { - if (buffer_info->page_dma) - pci_unmap_page(pdev, buffer_info->page_dma, - PAGE_SIZE / 2, - PCI_DMA_FROMDEVICE); put_page(buffer_info->page); buffer_info->page = NULL; - buffer_info->page_dma = 0; buffer_info->page_offset = 0; } } @@ -2485,9 +2805,6 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring) rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; - - writel(0, adapter->hw.hw_addr + rx_ring->head); - writel(0, adapter->hw.hw_addr + rx_ring->tail); } /** @@ -2521,61 +2838,90 @@ static int igb_set_mac(struct net_device *netdev, void *p) memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len); - igb_rar_set(hw, hw->mac.addr, 0); - igb_set_rah_pool(hw, adapter->vfs_allocated_count, 0); + /* set the correct pool for the new PF MAC address in entry 0 */ + igb_rar_set_qsel(adapter, hw->mac.addr, 0, + adapter->vfs_allocated_count); return 0; } /** - * igb_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set + * igb_write_mc_addr_list - write multicast addresses to MTA * @netdev: network interface device structure * - * The set_rx_mode entry point is called whenever the unicast or multicast - * address lists or the network interface flags are updated. This routine is - * responsible for configuring the hardware for proper unicast, multicast, - * promiscuous mode, and all-multi behavior. + * Writes multicast address list to the MTA hash table. + * Returns: -ENOMEM on failure + * 0 on no addresses written + * X on writing X addresses to MTA **/ -static void igb_set_rx_mode(struct net_device *netdev) +static int igb_write_mc_addr_list(struct net_device *netdev) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - unsigned int rar_entries = hw->mac.rar_entry_count - - (adapter->vfs_allocated_count + 1); struct dev_mc_list *mc_ptr = netdev->mc_list; - u8 *mta_list = NULL; - u32 rctl; + u8 *mta_list; + u32 vmolr = 0; int i; - /* Check for Promiscuous and All Multicast modes */ - rctl = rd32(E1000_RCTL); + if (!netdev->mc_count) { + /* nothing to program, so clear mc list */ + igb_update_mc_addr_list(hw, NULL, 0); + igb_restore_vf_multicasts(adapter); + return 0; + } - if (netdev->flags & IFF_PROMISC) { - rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); - rctl &= ~E1000_RCTL_VFE; - } else { - if (netdev->flags & IFF_ALLMULTI) - rctl |= E1000_RCTL_MPE; - else - rctl &= ~E1000_RCTL_MPE; + mta_list = kzalloc(netdev->mc_count * 6, GFP_ATOMIC); + if (!mta_list) + return -ENOMEM; - if (netdev->uc.count > rar_entries) - rctl |= E1000_RCTL_UPE; - else - rctl &= ~E1000_RCTL_UPE; - rctl |= E1000_RCTL_VFE; + /* set vmolr receive overflow multicast bit */ + vmolr |= E1000_VMOLR_ROMPE; + + /* The shared function expects a packed array of only addresses. */ + mc_ptr = netdev->mc_list; + + for (i = 0; i < netdev->mc_count; i++) { + if (!mc_ptr) + break; + memcpy(mta_list + (i*ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN); + mc_ptr = mc_ptr->next; } - wr32(E1000_RCTL, rctl); + igb_update_mc_addr_list(hw, mta_list, i); + kfree(mta_list); + + return netdev->mc_count; +} + +/** + * igb_write_uc_addr_list - write unicast addresses to RAR table + * @netdev: network interface device structure + * + * Writes unicast address list to the RAR table. + * Returns: -ENOMEM on failure/insufficient address space + * 0 on no addresses written + * X on writing X addresses to the RAR table + **/ +static int igb_write_uc_addr_list(struct net_device *netdev) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + unsigned int vfn = adapter->vfs_allocated_count; + unsigned int rar_entries = hw->mac.rar_entry_count - (vfn + 1); + int count = 0; + + /* return ENOMEM indicating insufficient memory for addresses */ + if (netdev->uc.count > rar_entries) + return -ENOMEM; if (netdev->uc.count && rar_entries) { struct netdev_hw_addr *ha; list_for_each_entry(ha, &netdev->uc.list, list) { if (!rar_entries) break; - igb_rar_set(hw, ha->addr, rar_entries); - igb_set_rah_pool(hw, adapter->vfs_allocated_count, - rar_entries); - rar_entries--; + igb_rar_set_qsel(adapter, ha->addr, + rar_entries--, + vfn); + count++; } } /* write the addresses in reverse order to avoid write combining */ @@ -2585,29 +2931,79 @@ static void igb_set_rx_mode(struct net_device *netdev) } wrfl(); - if (!netdev->mc_count) { - /* nothing to program, so clear mc list */ - igb_update_mc_addr_list(hw, NULL, 0); - igb_restore_vf_multicasts(adapter); - return; + return count; +} + +/** + * igb_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_rx_mode entry point is called whenever the unicast or multicast + * address lists or the network interface flags are updated. This routine is + * responsible for configuring the hardware for proper unicast, multicast, + * promiscuous mode, and all-multi behavior. + **/ +static void igb_set_rx_mode(struct net_device *netdev) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + unsigned int vfn = adapter->vfs_allocated_count; + u32 rctl, vmolr = 0; + int count; + + /* Check for Promiscuous and All Multicast modes */ + rctl = rd32(E1000_RCTL); + + /* clear the effected bits */ + rctl &= ~(E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_VFE); + + if (netdev->flags & IFF_PROMISC) { + rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); + vmolr |= (E1000_VMOLR_ROPE | E1000_VMOLR_MPME); + } else { + if (netdev->flags & IFF_ALLMULTI) { + rctl |= E1000_RCTL_MPE; + vmolr |= E1000_VMOLR_MPME; + } else { + /* + * Write addresses to the MTA, if the attempt fails + * then we should just turn on promiscous mode so + * that we can at least receive multicast traffic + */ + count = igb_write_mc_addr_list(netdev); + if (count < 0) { + rctl |= E1000_RCTL_MPE; + vmolr |= E1000_VMOLR_MPME; + } else if (count) { + vmolr |= E1000_VMOLR_ROMPE; + } + } + /* + * Write addresses to available RAR registers, if there is not + * sufficient space to store all the addresses then enable + * unicast promiscous mode + */ + count = igb_write_uc_addr_list(netdev); + if (count < 0) { + rctl |= E1000_RCTL_UPE; + vmolr |= E1000_VMOLR_ROPE; + } + rctl |= E1000_RCTL_VFE; } + wr32(E1000_RCTL, rctl); - mta_list = kzalloc(netdev->mc_count * 6, GFP_ATOMIC); - if (!mta_list) { - dev_err(&adapter->pdev->dev, - "failed to allocate multicast filter list\n"); + /* + * In order to support SR-IOV and eventually VMDq it is necessary to set + * the VMOLR to enable the appropriate modes. Without this workaround + * we will have issues with VLAN tag stripping not being done for frames + * that are only arriving because we are the default pool + */ + if (hw->mac.type < e1000_82576) return; - } - /* The shared function expects a packed array of only addresses. */ - for (i = 0; i < netdev->mc_count; i++) { - if (!mc_ptr) - break; - memcpy(mta_list + (i*ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN); - mc_ptr = mc_ptr->next; - } - igb_update_mc_addr_list(hw, mta_list, i); - kfree(mta_list); + vmolr |= rd32(E1000_VMOLR(vfn)) & + ~(E1000_VMOLR_ROPE | E1000_VMOLR_MPME | E1000_VMOLR_ROMPE); + wr32(E1000_VMOLR(vfn), vmolr); igb_restore_vf_multicasts(adapter); } @@ -2669,37 +3065,33 @@ static void igb_watchdog(unsigned long data) static void igb_watchdog_task(struct work_struct *work) { struct igb_adapter *adapter = container_of(work, - struct igb_adapter, watchdog_task); + struct igb_adapter, + watchdog_task); struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; - struct igb_ring *tx_ring = adapter->tx_ring; u32 link; - u32 eics = 0; int i; link = igb_has_link(adapter); - if ((netif_carrier_ok(netdev)) && link) - goto link_up; - if (link) { if (!netif_carrier_ok(netdev)) { u32 ctrl; - hw->mac.ops.get_speed_and_duplex(&adapter->hw, - &adapter->link_speed, - &adapter->link_duplex); + hw->mac.ops.get_speed_and_duplex(hw, + &adapter->link_speed, + &adapter->link_duplex); ctrl = rd32(E1000_CTRL); /* Links status message must follow this format */ printk(KERN_INFO "igb: %s NIC Link is Up %d Mbps %s, " "Flow Control: %s\n", - netdev->name, - adapter->link_speed, - adapter->link_duplex == FULL_DUPLEX ? + netdev->name, + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? "Full Duplex" : "Half Duplex", - ((ctrl & E1000_CTRL_TFCE) && (ctrl & - E1000_CTRL_RFCE)) ? "RX/TX" : ((ctrl & - E1000_CTRL_RFCE) ? "RX" : ((ctrl & - E1000_CTRL_TFCE) ? "TX" : "None"))); + ((ctrl & E1000_CTRL_TFCE) && + (ctrl & E1000_CTRL_RFCE)) ? "RX/TX" : + ((ctrl & E1000_CTRL_RFCE) ? "RX" : + ((ctrl & E1000_CTRL_TFCE) ? "TX" : "None"))); /* tweak tx_queue_len according to speed/duplex and * adjust the timeout factor */ @@ -2743,46 +3135,40 @@ static void igb_watchdog_task(struct work_struct *work) } } -link_up: igb_update_stats(adapter); + igb_update_adaptive(hw); - hw->mac.tx_packet_delta = adapter->stats.tpt - adapter->tpt_old; - adapter->tpt_old = adapter->stats.tpt; - hw->mac.collision_delta = adapter->stats.colc - adapter->colc_old; - adapter->colc_old = adapter->stats.colc; - - adapter->gorc = adapter->stats.gorc - adapter->gorc_old; - adapter->gorc_old = adapter->stats.gorc; - adapter->gotc = adapter->stats.gotc - adapter->gotc_old; - adapter->gotc_old = adapter->stats.gotc; - - igb_update_adaptive(&adapter->hw); - - if (!netif_carrier_ok(netdev)) { - if (igb_desc_unused(tx_ring) + 1 < tx_ring->count) { + for (i = 0; i < adapter->num_tx_queues; i++) { + struct igb_ring *tx_ring = &adapter->tx_ring[i]; + if (!netif_carrier_ok(netdev)) { /* We've lost link, so the controller stops DMA, * but we've got queued Tx work that's never going * to get done, so reset controller to flush Tx. * (Do the reset outside of interrupt context). */ - adapter->tx_timeout_count++; - schedule_work(&adapter->reset_task); - /* return immediately since reset is imminent */ - return; + if (igb_desc_unused(tx_ring) + 1 < tx_ring->count) { + adapter->tx_timeout_count++; + schedule_work(&adapter->reset_task); + /* return immediately since reset is imminent */ + return; + } } + + /* Force detection of hung controller every watchdog period */ + tx_ring->detect_tx_hung = true; } /* Cause software interrupt to ensure rx ring is cleaned */ if (adapter->msix_entries) { - for (i = 0; i < adapter->num_rx_queues; i++) - eics |= adapter->rx_ring[i].eims_value; + u32 eics = 0; + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + eics |= q_vector->eims_value; + } wr32(E1000_EICS, eics); } else { wr32(E1000_ICS, E1000_ICS_RXDMT0); } - /* Force detection of hung controller every watchdog period */ - tx_ring->detect_tx_hung = true; - /* Reset the timer */ if (!test_bit(__IGB_DOWN, &adapter->state)) mod_timer(&adapter->watchdog_timer, @@ -2796,7 +3182,6 @@ enum latency_range { latency_invalid = 255 }; - /** * igb_update_ring_itr - update the dynamic ITR value based on packet size * @@ -2811,25 +3196,37 @@ enum latency_range { * parameter (see igb_param.c) * NOTE: This function is called only when operating in a multiqueue * receive environment. - * @rx_ring: pointer to ring + * @q_vector: pointer to q_vector **/ -static void igb_update_ring_itr(struct igb_ring *rx_ring) +static void igb_update_ring_itr(struct igb_q_vector *q_vector) { - int new_val = rx_ring->itr_val; + int new_val = q_vector->itr_val; int avg_wire_size = 0; - struct igb_adapter *adapter = rx_ring->adapter; - - if (!rx_ring->total_packets) - goto clear_counts; /* no packets, so don't do anything */ + struct igb_adapter *adapter = q_vector->adapter; /* For non-gigabit speeds, just fix the interrupt rate at 4000 * ints/sec - ITR timer value of 120 ticks. */ if (adapter->link_speed != SPEED_1000) { - new_val = 120; + new_val = 976; goto set_itr_val; } - avg_wire_size = rx_ring->total_bytes / rx_ring->total_packets; + + if (q_vector->rx_ring && q_vector->rx_ring->total_packets) { + struct igb_ring *ring = q_vector->rx_ring; + avg_wire_size = ring->total_bytes / ring->total_packets; + } + + if (q_vector->tx_ring && q_vector->tx_ring->total_packets) { + struct igb_ring *ring = q_vector->tx_ring; + avg_wire_size = max_t(u32, avg_wire_size, + (ring->total_bytes / + ring->total_packets)); + } + + /* if avg_wire_size isn't set no work was done */ + if (!avg_wire_size) + goto clear_counts; /* Add 24 bytes to size to account for CRC, preamble, and gap */ avg_wire_size += 24; @@ -2844,13 +3241,19 @@ static void igb_update_ring_itr(struct igb_ring *rx_ring) new_val = avg_wire_size / 2; set_itr_val: - if (new_val != rx_ring->itr_val) { - rx_ring->itr_val = new_val; - rx_ring->set_itr = 1; + if (new_val != q_vector->itr_val) { + q_vector->itr_val = new_val; + q_vector->set_itr = 1; } clear_counts: - rx_ring->total_bytes = 0; - rx_ring->total_packets = 0; + if (q_vector->rx_ring) { + q_vector->rx_ring->total_bytes = 0; + q_vector->rx_ring->total_packets = 0; + } + if (q_vector->tx_ring) { + q_vector->tx_ring->total_bytes = 0; + q_vector->tx_ring->total_packets = 0; + } } /** @@ -2867,7 +3270,7 @@ clear_counts: * NOTE: These calculations are only valid when operating in a single- * queue environment. * @adapter: pointer to adapter - * @itr_setting: current adapter->itr + * @itr_setting: current q_vector->itr_val * @packets: the number of packets during this measurement interval * @bytes: the number of bytes during this measurement interval **/ @@ -2919,8 +3322,9 @@ update_itr_done: static void igb_set_itr(struct igb_adapter *adapter) { + struct igb_q_vector *q_vector = adapter->q_vector[0]; u16 current_itr; - u32 new_itr = adapter->itr; + u32 new_itr = q_vector->itr_val; /* for non-gigabit speeds, just fix the interrupt rate at 4000 */ if (adapter->link_speed != SPEED_1000) { @@ -2934,18 +3338,14 @@ static void igb_set_itr(struct igb_adapter *adapter) adapter->rx_ring->total_packets, adapter->rx_ring->total_bytes); - if (adapter->rx_ring->buddy) { - adapter->tx_itr = igb_update_itr(adapter, - adapter->tx_itr, - adapter->tx_ring->total_packets, - adapter->tx_ring->total_bytes); - current_itr = max(adapter->rx_itr, adapter->tx_itr); - } else { - current_itr = adapter->rx_itr; - } + adapter->tx_itr = igb_update_itr(adapter, + adapter->tx_itr, + adapter->tx_ring->total_packets, + adapter->tx_ring->total_bytes); + current_itr = max(adapter->rx_itr, adapter->tx_itr); /* conservative mode (itr 3) eliminates the lowest_latency setting */ - if (adapter->itr_setting == 3 && current_itr == lowest_latency) + if (adapter->rx_itr_setting == 3 && current_itr == lowest_latency) current_itr = low_latency; switch (current_itr) { @@ -2966,18 +3366,17 @@ static void igb_set_itr(struct igb_adapter *adapter) set_itr_now: adapter->rx_ring->total_bytes = 0; adapter->rx_ring->total_packets = 0; - if (adapter->rx_ring->buddy) { - adapter->rx_ring->buddy->total_bytes = 0; - adapter->rx_ring->buddy->total_packets = 0; - } + adapter->tx_ring->total_bytes = 0; + adapter->tx_ring->total_packets = 0; - if (new_itr != adapter->itr) { + if (new_itr != q_vector->itr_val) { /* this attempts to bias the interrupt rate towards Bulk * by adding intermediate steps when interrupt rate is * increasing */ - new_itr = new_itr > adapter->itr ? - max((new_itr * adapter->itr) / - (new_itr + (adapter->itr >> 2)), new_itr) : + new_itr = new_itr > q_vector->itr_val ? + max((new_itr * q_vector->itr_val) / + (new_itr + (q_vector->itr_val >> 2)), + new_itr) : new_itr; /* Don't write the value here; it resets the adapter's * internal timer, and causes us to delay far longer than @@ -2985,25 +3384,22 @@ set_itr_now: * value at the beginning of the next interrupt so the timing * ends up being correct. */ - adapter->itr = new_itr; - adapter->rx_ring->itr_val = new_itr; - adapter->rx_ring->set_itr = 1; + q_vector->itr_val = new_itr; + q_vector->set_itr = 1; } return; } - #define IGB_TX_FLAGS_CSUM 0x00000001 #define IGB_TX_FLAGS_VLAN 0x00000002 #define IGB_TX_FLAGS_TSO 0x00000004 #define IGB_TX_FLAGS_IPV4 0x00000008 -#define IGB_TX_FLAGS_TSTAMP 0x00000010 -#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000 -#define IGB_TX_FLAGS_VLAN_SHIFT 16 +#define IGB_TX_FLAGS_TSTAMP 0x00000010 +#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000 +#define IGB_TX_FLAGS_VLAN_SHIFT 16 -static inline int igb_tso_adv(struct igb_adapter *adapter, - struct igb_ring *tx_ring, +static inline int igb_tso_adv(struct igb_ring *tx_ring, struct sk_buff *skb, u32 tx_flags, u8 *hdr_len) { struct e1000_adv_tx_context_desc *context_desc; @@ -3065,8 +3461,8 @@ static inline int igb_tso_adv(struct igb_adapter *adapter, mss_l4len_idx |= (l4len << E1000_ADVTXD_L4LEN_SHIFT); /* For 82575, context index must be unique per ring. */ - if (adapter->flags & IGB_FLAG_NEED_CTX_IDX) - mss_l4len_idx |= tx_ring->queue_index << 4; + if (tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX) + mss_l4len_idx |= tx_ring->reg_idx << 4; context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx); context_desc->seqnum_seed = 0; @@ -3083,14 +3479,14 @@ static inline int igb_tso_adv(struct igb_adapter *adapter, return true; } -static inline bool igb_tx_csum_adv(struct igb_adapter *adapter, - struct igb_ring *tx_ring, - struct sk_buff *skb, u32 tx_flags) +static inline bool igb_tx_csum_adv(struct igb_ring *tx_ring, + struct sk_buff *skb, u32 tx_flags) { struct e1000_adv_tx_context_desc *context_desc; - unsigned int i; + struct pci_dev *pdev = tx_ring->pdev; struct igb_buffer *buffer_info; u32 info = 0, tu_cmd = 0; + unsigned int i; if ((skb->ip_summed == CHECKSUM_PARTIAL) || (tx_flags & IGB_TX_FLAGS_VLAN)) { @@ -3100,6 +3496,7 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter, if (tx_flags & IGB_TX_FLAGS_VLAN) info |= (tx_flags & IGB_TX_FLAGS_VLAN_MASK); + info |= (skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT); if (skb->ip_summed == CHECKSUM_PARTIAL) info |= skb_network_header_len(skb); @@ -3137,7 +3534,7 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter, break; default: if (unlikely(net_ratelimit())) - dev_warn(&adapter->pdev->dev, + dev_warn(&pdev->dev, "partial checksum but proto=%x!\n", skb->protocol); break; @@ -3146,11 +3543,9 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter, context_desc->type_tucmd_mlhl = cpu_to_le32(tu_cmd); context_desc->seqnum_seed = 0; - if (adapter->flags & IGB_FLAG_NEED_CTX_IDX) + if (tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX) context_desc->mss_l4len_idx = - cpu_to_le32(tx_ring->queue_index << 4); - else - context_desc->mss_l4len_idx = 0; + cpu_to_le32(tx_ring->reg_idx << 4); buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; @@ -3169,32 +3564,27 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter, #define IGB_MAX_TXD_PWR 16 #define IGB_MAX_DATA_PER_TXD (1<<IGB_MAX_TXD_PWR) -static inline int igb_tx_map_adv(struct igb_adapter *adapter, - struct igb_ring *tx_ring, struct sk_buff *skb, +static inline int igb_tx_map_adv(struct igb_ring *tx_ring, struct sk_buff *skb, unsigned int first) { struct igb_buffer *buffer_info; + struct pci_dev *pdev = tx_ring->pdev; unsigned int len = skb_headlen(skb); unsigned int count = 0, i; unsigned int f; - dma_addr_t *map; i = tx_ring->next_to_use; - if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) { - dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); - return 0; - } - - map = skb_shinfo(skb)->dma_maps; - buffer_info = &tx_ring->buffer_info[i]; BUG_ON(len >= IGB_MAX_DATA_PER_TXD); buffer_info->length = len; /* set time_stamp *before* dma to help avoid a possible race */ buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; - buffer_info->dma = skb_shinfo(skb)->dma_head; + buffer_info->dma = pci_map_single(pdev, skb->data, len, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) { struct skb_frag_struct *frag; @@ -3211,25 +3601,55 @@ static inline int igb_tx_map_adv(struct igb_adapter *adapter, buffer_info->length = len; buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; - buffer_info->dma = map[count]; + buffer_info->mapped_as_page = true; + buffer_info->dma = pci_map_page(pdev, + frag->page, + frag->page_offset, + len, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; + count++; } tx_ring->buffer_info[i].skb = skb; tx_ring->buffer_info[first].next_to_watch = i; - return count + 1; + return ++count; + +dma_error: + dev_err(&pdev->dev, "TX DMA map failed\n"); + + /* clear timestamp and dma mappings for failed buffer_info mapping */ + buffer_info->dma = 0; + buffer_info->time_stamp = 0; + buffer_info->length = 0; + buffer_info->next_to_watch = 0; + buffer_info->mapped_as_page = false; + count--; + + /* clear timestamp and dma mappings for remaining portion of packet */ + while (count >= 0) { + count--; + i--; + if (i < 0) + i += tx_ring->count; + buffer_info = &tx_ring->buffer_info[i]; + igb_unmap_and_free_tx_resource(tx_ring, buffer_info); + } + + return 0; } -static inline void igb_tx_queue_adv(struct igb_adapter *adapter, - struct igb_ring *tx_ring, +static inline void igb_tx_queue_adv(struct igb_ring *tx_ring, int tx_flags, int count, u32 paylen, u8 hdr_len) { - union e1000_adv_tx_desc *tx_desc = NULL; + union e1000_adv_tx_desc *tx_desc; struct igb_buffer *buffer_info; u32 olinfo_status = 0, cmd_type_len; - unsigned int i; + unsigned int i = tx_ring->next_to_use; cmd_type_len = (E1000_ADVTXD_DTYP_DATA | E1000_ADVTXD_DCMD_IFCS | E1000_ADVTXD_DCMD_DEXT); @@ -3254,27 +3674,28 @@ static inline void igb_tx_queue_adv(struct igb_adapter *adapter, olinfo_status |= E1000_TXD_POPTS_TXSM << 8; } - if ((adapter->flags & IGB_FLAG_NEED_CTX_IDX) && - (tx_flags & (IGB_TX_FLAGS_CSUM | IGB_TX_FLAGS_TSO | + if ((tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX) && + (tx_flags & (IGB_TX_FLAGS_CSUM | + IGB_TX_FLAGS_TSO | IGB_TX_FLAGS_VLAN))) - olinfo_status |= tx_ring->queue_index << 4; + olinfo_status |= tx_ring->reg_idx << 4; olinfo_status |= ((paylen - hdr_len) << E1000_ADVTXD_PAYLEN_SHIFT); - i = tx_ring->next_to_use; - while (count--) { + do { buffer_info = &tx_ring->buffer_info[i]; tx_desc = E1000_TX_DESC_ADV(*tx_ring, i); tx_desc->read.buffer_addr = cpu_to_le64(buffer_info->dma); tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type_len | buffer_info->length); tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status); + count--; i++; if (i == tx_ring->count) i = 0; - } + } while (count > 0); - tx_desc->read.cmd_type_len |= cpu_to_le32(adapter->txd_cmd); + tx_desc->read.cmd_type_len |= cpu_to_le32(IGB_ADVTXD_DCMD); /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, @@ -3282,16 +3703,15 @@ static inline void igb_tx_queue_adv(struct igb_adapter *adapter, wmb(); tx_ring->next_to_use = i; - writel(i, adapter->hw.hw_addr + tx_ring->tail); + writel(i, tx_ring->tail); /* we need this if more than one processor can write to our tail * at a time, it syncronizes IO on IA64/Altix systems */ mmiowb(); } -static int __igb_maybe_stop_tx(struct net_device *netdev, - struct igb_ring *tx_ring, int size) +static int __igb_maybe_stop_tx(struct igb_ring *tx_ring, int size) { - struct igb_adapter *adapter = netdev_priv(netdev); + struct net_device *netdev = tx_ring->netdev; netif_stop_subqueue(netdev, tx_ring->queue_index); @@ -3307,66 +3727,43 @@ static int __igb_maybe_stop_tx(struct net_device *netdev, /* A reprieve! */ netif_wake_subqueue(netdev, tx_ring->queue_index); - ++adapter->restart_queue; + tx_ring->tx_stats.restart_queue++; return 0; } -static int igb_maybe_stop_tx(struct net_device *netdev, - struct igb_ring *tx_ring, int size) +static int igb_maybe_stop_tx(struct igb_ring *tx_ring, int size) { if (igb_desc_unused(tx_ring) >= size) return 0; - return __igb_maybe_stop_tx(netdev, tx_ring, size); + return __igb_maybe_stop_tx(tx_ring, size); } -static netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb, - struct net_device *netdev, - struct igb_ring *tx_ring) +netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb, + struct igb_ring *tx_ring) { - struct igb_adapter *adapter = netdev_priv(netdev); + struct igb_adapter *adapter = netdev_priv(tx_ring->netdev); unsigned int first; unsigned int tx_flags = 0; u8 hdr_len = 0; - int count = 0; - int tso = 0; - union skb_shared_tx *shtx; - - if (test_bit(__IGB_DOWN, &adapter->state)) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - - if (skb->len <= 0) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } + int tso = 0, count; + union skb_shared_tx *shtx = skb_tx(skb); /* need: 1 descriptor per page, * + 2 desc gap to keep tail from touching head, * + 1 desc for skb->data, * + 1 desc for context descriptor, * otherwise try next time */ - if (igb_maybe_stop_tx(netdev, tx_ring, skb_shinfo(skb)->nr_frags + 4)) { + if (igb_maybe_stop_tx(tx_ring, skb_shinfo(skb)->nr_frags + 4)) { /* this is a hard error */ return NETDEV_TX_BUSY; } - /* - * TODO: check that there currently is no other packet with - * time stamping in the queue - * - * When doing time stamping, keep the connection to the socket - * a while longer: it is still needed by skb_hwtstamp_tx(), - * called either in igb_tx_hwtstamp() or by our caller when - * doing software time stamping. - */ - shtx = skb_tx(skb); if (unlikely(shtx->hardware)) { shtx->in_progress = 1; tx_flags |= IGB_TX_FLAGS_TSTAMP; } - if (adapter->vlgrp && vlan_tx_tag_present(skb)) { + if (vlan_tx_tag_present(skb) && adapter->vlgrp) { tx_flags |= IGB_TX_FLAGS_VLAN; tx_flags |= (vlan_tx_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT); } @@ -3375,37 +3772,38 @@ static netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb, tx_flags |= IGB_TX_FLAGS_IPV4; first = tx_ring->next_to_use; - tso = skb_is_gso(skb) ? igb_tso_adv(adapter, tx_ring, skb, tx_flags, - &hdr_len) : 0; + if (skb_is_gso(skb)) { + tso = igb_tso_adv(tx_ring, skb, tx_flags, &hdr_len); - if (tso < 0) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + if (tso < 0) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } } if (tso) tx_flags |= IGB_TX_FLAGS_TSO; - else if (igb_tx_csum_adv(adapter, tx_ring, skb, tx_flags) && + else if (igb_tx_csum_adv(tx_ring, skb, tx_flags) && (skb->ip_summed == CHECKSUM_PARTIAL)) tx_flags |= IGB_TX_FLAGS_CSUM; /* - * count reflects descriptors mapped, if 0 then mapping error + * count reflects descriptors mapped, if 0 or less then mapping error * has occured and we need to rewind the descriptor queue */ - count = igb_tx_map_adv(adapter, tx_ring, skb, first); - - if (count) { - igb_tx_queue_adv(adapter, tx_ring, tx_flags, count, - skb->len, hdr_len); - /* Make sure there is space in the ring for the next send. */ - igb_maybe_stop_tx(netdev, tx_ring, MAX_SKB_FRAGS + 4); - } else { + count = igb_tx_map_adv(tx_ring, skb, first); + if (!count) { dev_kfree_skb_any(skb); tx_ring->buffer_info[first].time_stamp = 0; tx_ring->next_to_use = first; + return NETDEV_TX_OK; } + igb_tx_queue_adv(tx_ring, tx_flags, count, skb->len, hdr_len); + + /* Make sure there is space in the ring for the next send. */ + igb_maybe_stop_tx(tx_ring, MAX_SKB_FRAGS + 4); + return NETDEV_TX_OK; } @@ -3414,8 +3812,18 @@ static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb, { struct igb_adapter *adapter = netdev_priv(netdev); struct igb_ring *tx_ring; - int r_idx = 0; + + if (test_bit(__IGB_DOWN, &adapter->state)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (skb->len <= 0) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + r_idx = skb->queue_mapping & (IGB_ABS_MAX_TX_QUEUES - 1); tx_ring = adapter->multi_tx_table[r_idx]; @@ -3423,7 +3831,7 @@ static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb, * to a flow. Right now, performance is impacted slightly negatively * if using multiple tx queues. If the stack breaks away from a * single qdisc implementation, we can look at this again. */ - return igb_xmit_frame_ring_adv(skb, netdev, tx_ring); + return igb_xmit_frame_ring_adv(skb, tx_ring); } /** @@ -3437,6 +3845,10 @@ static void igb_tx_timeout(struct net_device *netdev) /* Do the reset outside of interrupt context */ adapter->tx_timeout_count++; + + if (hw->mac.type == e1000_82580) + hw->dev_spec._82575.global_device_reset = true; + schedule_work(&adapter->reset_task); wr32(E1000_EICS, (adapter->eims_enable_mask & ~adapter->eims_other)); @@ -3459,10 +3871,8 @@ static void igb_reset_task(struct work_struct *work) **/ static struct net_device_stats *igb_get_stats(struct net_device *netdev) { - struct igb_adapter *adapter = netdev_priv(netdev); - /* only return the current stats */ - return &adapter->net_stats; + return &netdev->stats; } /** @@ -3475,16 +3885,17 @@ static struct net_device_stats *igb_get_stats(struct net_device *netdev) static int igb_change_mtu(struct net_device *netdev, int new_mtu) { struct igb_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; + u32 rx_buffer_len, i; - if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || - (max_frame > MAX_JUMBO_FRAME_SIZE)) { - dev_err(&adapter->pdev->dev, "Invalid MTU setting\n"); + if ((new_mtu < 68) || (max_frame > MAX_JUMBO_FRAME_SIZE)) { + dev_err(&pdev->dev, "Invalid MTU setting\n"); return -EINVAL; } if (max_frame > MAX_STD_JUMBO_FRAME_SIZE) { - dev_err(&adapter->pdev->dev, "MTU > 9216 not supported.\n"); + dev_err(&pdev->dev, "MTU > 9216 not supported.\n"); return -EINVAL; } @@ -3493,8 +3904,6 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu) /* igb_down has a dependency on max_frame_size */ adapter->max_frame_size = max_frame; - if (netif_running(netdev)) - igb_down(adapter); /* NOTE: netdev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN * means we reserve 2 more, this pushes us to allocate from the next @@ -3502,35 +3911,23 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu) * i.e. RXBUFFER_2048 --> size-4096 slab */ - if (max_frame <= IGB_RXBUFFER_256) - adapter->rx_buffer_len = IGB_RXBUFFER_256; - else if (max_frame <= IGB_RXBUFFER_512) - adapter->rx_buffer_len = IGB_RXBUFFER_512; - else if (max_frame <= IGB_RXBUFFER_1024) - adapter->rx_buffer_len = IGB_RXBUFFER_1024; - else if (max_frame <= IGB_RXBUFFER_2048) - adapter->rx_buffer_len = IGB_RXBUFFER_2048; + if (max_frame <= IGB_RXBUFFER_1024) + rx_buffer_len = IGB_RXBUFFER_1024; + else if (max_frame <= MAXIMUM_ETHERNET_VLAN_SIZE) + rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE; else -#if (PAGE_SIZE / 2) > IGB_RXBUFFER_16384 - adapter->rx_buffer_len = IGB_RXBUFFER_16384; -#else - adapter->rx_buffer_len = PAGE_SIZE / 2; -#endif + rx_buffer_len = IGB_RXBUFFER_128; - /* if sr-iov is enabled we need to force buffer size to 1K or larger */ - if (adapter->vfs_allocated_count && - (adapter->rx_buffer_len < IGB_RXBUFFER_1024)) - adapter->rx_buffer_len = IGB_RXBUFFER_1024; - - /* adjust allocation if LPE protects us, and we aren't using SBP */ - if ((max_frame == ETH_FRAME_LEN + ETH_FCS_LEN) || - (max_frame == MAXIMUM_ETHERNET_VLAN_SIZE)) - adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE; + if (netif_running(netdev)) + igb_down(adapter); - dev_info(&adapter->pdev->dev, "changing MTU from %d to %d\n", + dev_info(&pdev->dev, "changing MTU from %d to %d\n", netdev->mtu, new_mtu); netdev->mtu = new_mtu; + for (i = 0; i < adapter->num_rx_queues; i++) + adapter->rx_ring[i].rx_buffer_len = rx_buffer_len; + if (netif_running(netdev)) igb_up(adapter); else @@ -3548,9 +3945,13 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu) void igb_update_stats(struct igb_adapter *adapter) { + struct net_device_stats *net_stats = igb_get_stats(adapter->netdev); struct e1000_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; + u32 rnbc; u16 phy_tmp; + int i; + u64 bytes, packets; #define PHY_IDLE_ERROR_COUNT_MASK 0x00FF @@ -3563,6 +3964,29 @@ void igb_update_stats(struct igb_adapter *adapter) if (pci_channel_offline(pdev)) return; + bytes = 0; + packets = 0; + for (i = 0; i < adapter->num_rx_queues; i++) { + u32 rqdpc_tmp = rd32(E1000_RQDPC(i)) & 0x0FFF; + adapter->rx_ring[i].rx_stats.drops += rqdpc_tmp; + net_stats->rx_fifo_errors += rqdpc_tmp; + bytes += adapter->rx_ring[i].rx_stats.bytes; + packets += adapter->rx_ring[i].rx_stats.packets; + } + + net_stats->rx_bytes = bytes; + net_stats->rx_packets = packets; + + bytes = 0; + packets = 0; + for (i = 0; i < adapter->num_tx_queues; i++) { + bytes += adapter->tx_ring[i].tx_stats.bytes; + packets += adapter->tx_ring[i].tx_stats.packets; + } + net_stats->tx_bytes = bytes; + net_stats->tx_packets = packets; + + /* read stats registers */ adapter->stats.crcerrs += rd32(E1000_CRCERRS); adapter->stats.gprc += rd32(E1000_GPRC); adapter->stats.gorc += rd32(E1000_GORCL); @@ -3595,7 +4019,9 @@ void igb_update_stats(struct igb_adapter *adapter) adapter->stats.gptc += rd32(E1000_GPTC); adapter->stats.gotc += rd32(E1000_GOTCL); rd32(E1000_GOTCH); /* clear GOTCL */ - adapter->stats.rnbc += rd32(E1000_RNBC); + rnbc = rd32(E1000_RNBC); + adapter->stats.rnbc += rnbc; + net_stats->rx_fifo_errors += rnbc; adapter->stats.ruc += rd32(E1000_RUC); adapter->stats.rfc += rd32(E1000_RFC); adapter->stats.rjc += rd32(E1000_RJC); @@ -3614,7 +4040,6 @@ void igb_update_stats(struct igb_adapter *adapter) adapter->stats.bptc += rd32(E1000_BPTC); /* used for adaptive IFS */ - hw->mac.tx_packet_delta = rd32(E1000_TPT); adapter->stats.tpt += hw->mac.tx_packet_delta; hw->mac.collision_delta = rd32(E1000_COLC); @@ -3637,56 +4062,29 @@ void igb_update_stats(struct igb_adapter *adapter) adapter->stats.icrxdmtc += rd32(E1000_ICRXDMTC); /* Fill out the OS statistics structure */ - adapter->net_stats.multicast = adapter->stats.mprc; - adapter->net_stats.collisions = adapter->stats.colc; + net_stats->multicast = adapter->stats.mprc; + net_stats->collisions = adapter->stats.colc; /* Rx Errors */ - if (hw->mac.type != e1000_82575) { - u32 rqdpc_tmp; - u64 rqdpc_total = 0; - int i; - /* Read out drops stats per RX queue. Notice RQDPC (Receive - * Queue Drop Packet Count) stats only gets incremented, if - * the DROP_EN but it set (in the SRRCTL register for that - * queue). If DROP_EN bit is NOT set, then the some what - * equivalent count is stored in RNBC (not per queue basis). - * Also note the drop count is due to lack of available - * descriptors. - */ - for (i = 0; i < adapter->num_rx_queues; i++) { - rqdpc_tmp = rd32(E1000_RQDPC(i)) & 0xFFF; - adapter->rx_ring[i].rx_stats.drops += rqdpc_tmp; - rqdpc_total += adapter->rx_ring[i].rx_stats.drops; - } - adapter->net_stats.rx_fifo_errors = rqdpc_total; - } - - /* Note RNBC (Receive No Buffers Count) is an not an exact - * drop count as the hardware FIFO might save the day. Thats - * one of the reason for saving it in rx_fifo_errors, as its - * potentially not a true drop. - */ - adapter->net_stats.rx_fifo_errors += adapter->stats.rnbc; - /* RLEC on some newer hardware can be incorrect so build * our own version based on RUC and ROC */ - adapter->net_stats.rx_errors = adapter->stats.rxerrc + + net_stats->rx_errors = adapter->stats.rxerrc + adapter->stats.crcerrs + adapter->stats.algnerrc + adapter->stats.ruc + adapter->stats.roc + adapter->stats.cexterr; - adapter->net_stats.rx_length_errors = adapter->stats.ruc + - adapter->stats.roc; - adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs; - adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc; - adapter->net_stats.rx_missed_errors = adapter->stats.mpc; + net_stats->rx_length_errors = adapter->stats.ruc + + adapter->stats.roc; + net_stats->rx_crc_errors = adapter->stats.crcerrs; + net_stats->rx_frame_errors = adapter->stats.algnerrc; + net_stats->rx_missed_errors = adapter->stats.mpc; /* Tx Errors */ - adapter->net_stats.tx_errors = adapter->stats.ecol + - adapter->stats.latecol; - adapter->net_stats.tx_aborted_errors = adapter->stats.ecol; - adapter->net_stats.tx_window_errors = adapter->stats.latecol; - adapter->net_stats.tx_carrier_errors = adapter->stats.tncrs; + net_stats->tx_errors = adapter->stats.ecol + + adapter->stats.latecol; + net_stats->tx_aborted_errors = adapter->stats.ecol; + net_stats->tx_window_errors = adapter->stats.latecol; + net_stats->tx_carrier_errors = adapter->stats.tncrs; /* Tx Dropped needs to be maintained elsewhere */ @@ -3707,14 +4105,12 @@ void igb_update_stats(struct igb_adapter *adapter) static irqreturn_t igb_msix_other(int irq, void *data) { - struct net_device *netdev = data; - struct igb_adapter *adapter = netdev_priv(netdev); + struct igb_adapter *adapter = data; struct e1000_hw *hw = &adapter->hw; u32 icr = rd32(E1000_ICR); - /* reading ICR causes bit 31 of EICR to be cleared */ - if(icr & E1000_ICR_DOUTSYNC) { + if (icr & E1000_ICR_DOUTSYNC) { /* HW is reporting DMA is out of sync */ adapter->stats.doosync++; } @@ -3730,125 +4126,90 @@ static irqreturn_t igb_msix_other(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC | E1000_IMS_VMMB); + if (adapter->vfs_allocated_count) + wr32(E1000_IMS, E1000_IMS_LSC | + E1000_IMS_VMMB | + E1000_IMS_DOUTSYNC); + else + wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC); wr32(E1000_EIMS, adapter->eims_other); return IRQ_HANDLED; } -static irqreturn_t igb_msix_tx(int irq, void *data) +static void igb_write_itr(struct igb_q_vector *q_vector) { - struct igb_ring *tx_ring = data; - struct igb_adapter *adapter = tx_ring->adapter; - struct e1000_hw *hw = &adapter->hw; + u32 itr_val = q_vector->itr_val & 0x7FFC; -#ifdef CONFIG_IGB_DCA - if (adapter->flags & IGB_FLAG_DCA_ENABLED) - igb_update_tx_dca(tx_ring); -#endif + if (!q_vector->set_itr) + return; - tx_ring->total_bytes = 0; - tx_ring->total_packets = 0; + if (!itr_val) + itr_val = 0x4; - /* auto mask will automatically reenable the interrupt when we write - * EICS */ - if (!igb_clean_tx_irq(tx_ring)) - /* Ring was not completely cleaned, so fire another interrupt */ - wr32(E1000_EICS, tx_ring->eims_value); + if (q_vector->itr_shift) + itr_val |= itr_val << q_vector->itr_shift; else - wr32(E1000_EIMS, tx_ring->eims_value); + itr_val |= 0x8000000; - return IRQ_HANDLED; -} - -static void igb_write_itr(struct igb_ring *ring) -{ - struct e1000_hw *hw = &ring->adapter->hw; - if ((ring->adapter->itr_setting & 3) && ring->set_itr) { - switch (hw->mac.type) { - case e1000_82576: - wr32(ring->itr_register, ring->itr_val | - 0x80000000); - break; - default: - wr32(ring->itr_register, ring->itr_val | - (ring->itr_val << 16)); - break; - } - ring->set_itr = 0; - } + writel(itr_val, q_vector->itr_register); + q_vector->set_itr = 0; } -static irqreturn_t igb_msix_rx(int irq, void *data) +static irqreturn_t igb_msix_ring(int irq, void *data) { - struct igb_ring *rx_ring = data; - - /* Write the ITR value calculated at the end of the - * previous interrupt. - */ + struct igb_q_vector *q_vector = data; - igb_write_itr(rx_ring); + /* Write the ITR value calculated from the previous interrupt. */ + igb_write_itr(q_vector); - if (napi_schedule_prep(&rx_ring->napi)) - __napi_schedule(&rx_ring->napi); + napi_schedule(&q_vector->napi); -#ifdef CONFIG_IGB_DCA - if (rx_ring->adapter->flags & IGB_FLAG_DCA_ENABLED) - igb_update_rx_dca(rx_ring); -#endif - return IRQ_HANDLED; + return IRQ_HANDLED; } #ifdef CONFIG_IGB_DCA -static void igb_update_rx_dca(struct igb_ring *rx_ring) +static void igb_update_dca(struct igb_q_vector *q_vector) { - u32 dca_rxctrl; - struct igb_adapter *adapter = rx_ring->adapter; + struct igb_adapter *adapter = q_vector->adapter; struct e1000_hw *hw = &adapter->hw; int cpu = get_cpu(); - int q = rx_ring->reg_idx; - if (rx_ring->cpu != cpu) { - dca_rxctrl = rd32(E1000_DCA_RXCTRL(q)); - if (hw->mac.type == e1000_82576) { - dca_rxctrl &= ~E1000_DCA_RXCTRL_CPUID_MASK_82576; - dca_rxctrl |= dca3_get_tag(&adapter->pdev->dev, cpu) << - E1000_DCA_RXCTRL_CPUID_SHIFT; + if (q_vector->cpu == cpu) + goto out_no_update; + + if (q_vector->tx_ring) { + int q = q_vector->tx_ring->reg_idx; + u32 dca_txctrl = rd32(E1000_DCA_TXCTRL(q)); + if (hw->mac.type == e1000_82575) { + dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK; + dca_txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu); } else { + dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK_82576; + dca_txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu) << + E1000_DCA_TXCTRL_CPUID_SHIFT; + } + dca_txctrl |= E1000_DCA_TXCTRL_DESC_DCA_EN; + wr32(E1000_DCA_TXCTRL(q), dca_txctrl); + } + if (q_vector->rx_ring) { + int q = q_vector->rx_ring->reg_idx; + u32 dca_rxctrl = rd32(E1000_DCA_RXCTRL(q)); + if (hw->mac.type == e1000_82575) { dca_rxctrl &= ~E1000_DCA_RXCTRL_CPUID_MASK; dca_rxctrl |= dca3_get_tag(&adapter->pdev->dev, cpu); + } else { + dca_rxctrl &= ~E1000_DCA_RXCTRL_CPUID_MASK_82576; + dca_rxctrl |= dca3_get_tag(&adapter->pdev->dev, cpu) << + E1000_DCA_RXCTRL_CPUID_SHIFT; } dca_rxctrl |= E1000_DCA_RXCTRL_DESC_DCA_EN; dca_rxctrl |= E1000_DCA_RXCTRL_HEAD_DCA_EN; dca_rxctrl |= E1000_DCA_RXCTRL_DATA_DCA_EN; wr32(E1000_DCA_RXCTRL(q), dca_rxctrl); - rx_ring->cpu = cpu; - } - put_cpu(); -} - -static void igb_update_tx_dca(struct igb_ring *tx_ring) -{ - u32 dca_txctrl; - struct igb_adapter *adapter = tx_ring->adapter; - struct e1000_hw *hw = &adapter->hw; - int cpu = get_cpu(); - int q = tx_ring->reg_idx; - - if (tx_ring->cpu != cpu) { - dca_txctrl = rd32(E1000_DCA_TXCTRL(q)); - if (hw->mac.type == e1000_82576) { - dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK_82576; - dca_txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu) << - E1000_DCA_TXCTRL_CPUID_SHIFT; - } else { - dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK; - dca_txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu); - } - dca_txctrl |= E1000_DCA_TXCTRL_DESC_DCA_EN; - wr32(E1000_DCA_TXCTRL(q), dca_txctrl); - tx_ring->cpu = cpu; } + q_vector->cpu = cpu; +out_no_update: put_cpu(); } @@ -3863,13 +4224,10 @@ static void igb_setup_dca(struct igb_adapter *adapter) /* Always use CB2 mode, difference is masked in the CB driver. */ wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_CB2); - for (i = 0; i < adapter->num_tx_queues; i++) { - adapter->tx_ring[i].cpu = -1; - igb_update_tx_dca(&adapter->tx_ring[i]); - } - for (i = 0; i < adapter->num_rx_queues; i++) { - adapter->rx_ring[i].cpu = -1; - igb_update_rx_dca(&adapter->rx_ring[i]); + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + q_vector->cpu = -1; + igb_update_dca(q_vector); } } @@ -3877,6 +4235,7 @@ static int __igb_notify_dca(struct device *dev, void *data) { struct net_device *netdev = dev_get_drvdata(dev); struct igb_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; struct e1000_hw *hw = &adapter->hw; unsigned long event = *(unsigned long *)data; @@ -3885,12 +4244,9 @@ static int __igb_notify_dca(struct device *dev, void *data) /* if already enabled, don't do it again */ if (adapter->flags & IGB_FLAG_DCA_ENABLED) break; - /* Always use CB2 mode, difference is masked - * in the CB driver. */ - wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_CB2); if (dca_add_requester(dev) == 0) { adapter->flags |= IGB_FLAG_DCA_ENABLED; - dev_info(&adapter->pdev->dev, "DCA enabled\n"); + dev_info(&pdev->dev, "DCA enabled\n"); igb_setup_dca(adapter); break; } @@ -3898,9 +4254,9 @@ static int __igb_notify_dca(struct device *dev, void *data) case DCA_PROVIDER_REMOVE: if (adapter->flags & IGB_FLAG_DCA_ENABLED) { /* without this a class_device is left - * hanging around in the sysfs model */ + * hanging around in the sysfs model */ dca_remove_requester(dev); - dev_info(&adapter->pdev->dev, "DCA disabled\n"); + dev_info(&pdev->dev, "DCA disabled\n"); adapter->flags &= ~IGB_FLAG_DCA_ENABLED; wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_DISABLE); } @@ -3930,12 +4286,51 @@ static void igb_ping_all_vfs(struct igb_adapter *adapter) for (i = 0 ; i < adapter->vfs_allocated_count; i++) { ping = E1000_PF_CONTROL_MSG; - if (adapter->vf_data[i].clear_to_send) + if (adapter->vf_data[i].flags & IGB_VF_FLAG_CTS) ping |= E1000_VT_MSGTYPE_CTS; igb_write_mbx(hw, &ping, 1, i); } } +static int igb_set_vf_promisc(struct igb_adapter *adapter, u32 *msgbuf, u32 vf) +{ + struct e1000_hw *hw = &adapter->hw; + u32 vmolr = rd32(E1000_VMOLR(vf)); + struct vf_data_storage *vf_data = &adapter->vf_data[vf]; + + vf_data->flags |= ~(IGB_VF_FLAG_UNI_PROMISC | + IGB_VF_FLAG_MULTI_PROMISC); + vmolr &= ~(E1000_VMOLR_ROPE | E1000_VMOLR_ROMPE | E1000_VMOLR_MPME); + + if (*msgbuf & E1000_VF_SET_PROMISC_MULTICAST) { + vmolr |= E1000_VMOLR_MPME; + *msgbuf &= ~E1000_VF_SET_PROMISC_MULTICAST; + } else { + /* + * if we have hashes and we are clearing a multicast promisc + * flag we need to write the hashes to the MTA as this step + * was previously skipped + */ + if (vf_data->num_vf_mc_hashes > 30) { + vmolr |= E1000_VMOLR_MPME; + } else if (vf_data->num_vf_mc_hashes) { + int j; + vmolr |= E1000_VMOLR_ROMPE; + for (j = 0; j < vf_data->num_vf_mc_hashes; j++) + igb_mta_set(hw, vf_data->vf_mc_hashes[j]); + } + } + + wr32(E1000_VMOLR(vf), vmolr); + + /* there are flags left unprocessed, likely not supported */ + if (*msgbuf & E1000_VT_MSGINFO_MASK) + return -EINVAL; + + return 0; + +} + static int igb_set_vf_multicasts(struct igb_adapter *adapter, u32 *msgbuf, u32 vf) { @@ -3944,18 +4339,17 @@ static int igb_set_vf_multicasts(struct igb_adapter *adapter, struct vf_data_storage *vf_data = &adapter->vf_data[vf]; int i; - /* only up to 30 hash values supported */ - if (n > 30) - n = 30; - - /* salt away the number of multi cast addresses assigned + /* salt away the number of multicast addresses assigned * to this VF for later use to restore when the PF multi cast * list changes */ vf_data->num_vf_mc_hashes = n; - /* VFs are limited to using the MTA hash table for their multicast - * addresses */ + /* only up to 30 hash values supported */ + if (n > 30) + n = 30; + + /* store the hashes for later use */ for (i = 0; i < n; i++) vf_data->vf_mc_hashes[i] = hash_list[i]; @@ -3972,9 +4366,20 @@ static void igb_restore_vf_multicasts(struct igb_adapter *adapter) int i, j; for (i = 0; i < adapter->vfs_allocated_count; i++) { + u32 vmolr = rd32(E1000_VMOLR(i)); + vmolr &= ~(E1000_VMOLR_ROMPE | E1000_VMOLR_MPME); + vf_data = &adapter->vf_data[i]; - for (j = 0; j < vf_data->num_vf_mc_hashes; j++) - igb_mta_set(hw, vf_data->vf_mc_hashes[j]); + + if ((vf_data->num_vf_mc_hashes > 30) || + (vf_data->flags & IGB_VF_FLAG_MULTI_PROMISC)) { + vmolr |= E1000_VMOLR_MPME; + } else if (vf_data->num_vf_mc_hashes) { + vmolr |= E1000_VMOLR_ROMPE; + for (j = 0; j < vf_data->num_vf_mc_hashes; j++) + igb_mta_set(hw, vf_data->vf_mc_hashes[j]); + } + wr32(E1000_VMOLR(i), vmolr); } } @@ -4012,7 +4417,11 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf) struct e1000_hw *hw = &adapter->hw; u32 reg, i; - /* It is an error to call this function when VFs are not enabled */ + /* The vlvf table only exists on 82576 hardware and newer */ + if (hw->mac.type < e1000_82576) + return -1; + + /* we only need to do this if VMDq is enabled */ if (!adapter->vfs_allocated_count) return -1; @@ -4042,16 +4451,12 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf) /* if !enabled we need to set this up in vfta */ if (!(reg & E1000_VLVF_VLANID_ENABLE)) { - /* add VID to filter table, if bit already set - * PF must have added it outside of table */ - if (igb_vfta_set(hw, vid, true)) - reg |= 1 << (E1000_VLVF_POOLSEL_SHIFT + - adapter->vfs_allocated_count); + /* add VID to filter table */ + igb_vfta_set(hw, vid, true); reg |= E1000_VLVF_VLANID_ENABLE; } reg &= ~E1000_VLVF_VLANID_MASK; reg |= vid; - wr32(E1000_VLVF(i), reg); /* do not modify RLPML for PF devices */ @@ -4067,8 +4472,8 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf) reg |= size; wr32(E1000_VMOLR(vf), reg); } - adapter->vf_data[vf].vlans_enabled++; + adapter->vf_data[vf].vlans_enabled++; return 0; } } else { @@ -4110,15 +4515,14 @@ static int igb_set_vf_vlan(struct igb_adapter *adapter, u32 *msgbuf, u32 vf) return igb_vlvf_set(adapter, vid, add, vf); } -static inline void igb_vf_reset_event(struct igb_adapter *adapter, u32 vf) +static inline void igb_vf_reset(struct igb_adapter *adapter, u32 vf) { - struct e1000_hw *hw = &adapter->hw; - - /* disable mailbox functionality for vf */ - adapter->vf_data[vf].clear_to_send = false; + /* clear all flags */ + adapter->vf_data[vf].flags = 0; + adapter->vf_data[vf].last_nack = jiffies; /* reset offloads to defaults */ - igb_set_vmolr(hw, vf); + igb_set_vmolr(adapter, vf); /* reset vlans for device */ igb_clear_vf_vfta(adapter, vf); @@ -4130,7 +4534,18 @@ static inline void igb_vf_reset_event(struct igb_adapter *adapter, u32 vf) igb_set_rx_mode(adapter->netdev); } -static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) +static void igb_vf_reset_event(struct igb_adapter *adapter, u32 vf) +{ + unsigned char *vf_mac = adapter->vf_data[vf].vf_mac_addresses; + + /* generate a new mac address as we were hotplug removed/added */ + random_ether_addr(vf_mac); + + /* process remaining reset events */ + igb_vf_reset(adapter, vf); +} + +static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) { struct e1000_hw *hw = &adapter->hw; unsigned char *vf_mac = adapter->vf_data[vf].vf_mac_addresses; @@ -4139,11 +4554,10 @@ static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) u8 *addr = (u8 *)(&msgbuf[1]); /* process all the same items cleared in a function level reset */ - igb_vf_reset_event(adapter, vf); + igb_vf_reset(adapter, vf); /* set vf mac address */ - igb_rar_set(hw, vf_mac, rar_entry); - igb_set_rah_pool(hw, vf, rar_entry); + igb_rar_set_qsel(adapter, vf_mac, rar_entry, vf); /* enable transmit and receive for vf */ reg = rd32(E1000_VFTE); @@ -4151,8 +4565,7 @@ static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) reg = rd32(E1000_VFRE); wr32(E1000_VFRE, reg | (1 << vf)); - /* enable mailbox functionality for vf */ - adapter->vf_data[vf].clear_to_send = true; + adapter->vf_data[vf].flags = IGB_VF_FLAG_CTS; /* reply to reset with ack and vf mac address */ msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK; @@ -4162,66 +4575,45 @@ static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) static int igb_set_vf_mac_addr(struct igb_adapter *adapter, u32 *msg, int vf) { - unsigned char *addr = (char *)&msg[1]; - int err = -1; + unsigned char *addr = (char *)&msg[1]; + int err = -1; - if (is_valid_ether_addr(addr)) - err = igb_set_vf_mac(adapter, vf, addr); - - return err; + if (is_valid_ether_addr(addr)) + err = igb_set_vf_mac(adapter, vf, addr); + return err; } static void igb_rcv_ack_from_vf(struct igb_adapter *adapter, u32 vf) { struct e1000_hw *hw = &adapter->hw; + struct vf_data_storage *vf_data = &adapter->vf_data[vf]; u32 msg = E1000_VT_MSGTYPE_NACK; /* if device isn't clear to send it shouldn't be reading either */ - if (!adapter->vf_data[vf].clear_to_send) + if (!(vf_data->flags & IGB_VF_FLAG_CTS) && + time_after(jiffies, vf_data->last_nack + (2 * HZ))) { igb_write_mbx(hw, &msg, 1, vf); -} - - -static void igb_msg_task(struct igb_adapter *adapter) -{ - struct e1000_hw *hw = &adapter->hw; - u32 vf; - - for (vf = 0; vf < adapter->vfs_allocated_count; vf++) { - /* process any reset requests */ - if (!igb_check_for_rst(hw, vf)) { - adapter->vf_data[vf].clear_to_send = false; - igb_vf_reset_event(adapter, vf); - } - - /* process any messages pending */ - if (!igb_check_for_msg(hw, vf)) - igb_rcv_msg_from_vf(adapter, vf); - - /* process any acks */ - if (!igb_check_for_ack(hw, vf)) - igb_rcv_ack_from_vf(adapter, vf); - + vf_data->last_nack = jiffies; } } -static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) +static void igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) { - u32 mbx_size = E1000_VFMAILBOX_SIZE; - u32 msgbuf[mbx_size]; + struct pci_dev *pdev = adapter->pdev; + u32 msgbuf[E1000_VFMAILBOX_SIZE]; struct e1000_hw *hw = &adapter->hw; + struct vf_data_storage *vf_data = &adapter->vf_data[vf]; s32 retval; - retval = igb_read_mbx(hw, msgbuf, mbx_size, vf); + retval = igb_read_mbx(hw, msgbuf, E1000_VFMAILBOX_SIZE, vf); if (retval) - dev_err(&adapter->pdev->dev, - "Error receiving message from VF\n"); + dev_err(&pdev->dev, "Error receiving message from VF\n"); /* this is a message we already processed, do nothing */ if (msgbuf[0] & (E1000_VT_MSGTYPE_ACK | E1000_VT_MSGTYPE_NACK)) - return retval; + return; /* * until the vf completes a reset it should not be @@ -4230,20 +4622,25 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) if (msgbuf[0] == E1000_VF_RESET) { igb_vf_reset_msg(adapter, vf); - - return retval; + return; } - if (!adapter->vf_data[vf].clear_to_send) { - msgbuf[0] |= E1000_VT_MSGTYPE_NACK; - igb_write_mbx(hw, msgbuf, 1, vf); - return retval; + if (!(vf_data->flags & IGB_VF_FLAG_CTS)) { + msgbuf[0] = E1000_VT_MSGTYPE_NACK; + if (time_after(jiffies, vf_data->last_nack + (2 * HZ))) { + igb_write_mbx(hw, msgbuf, 1, vf); + vf_data->last_nack = jiffies; + } + return; } switch ((msgbuf[0] & 0xFFFF)) { case E1000_VF_SET_MAC_ADDR: retval = igb_set_vf_mac_addr(adapter, msgbuf, vf); break; + case E1000_VF_SET_PROMISC: + retval = igb_set_vf_promisc(adapter, msgbuf, vf); + break; case E1000_VF_SET_MULTICAST: retval = igb_set_vf_multicasts(adapter, msgbuf, vf); break; @@ -4254,7 +4651,7 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) retval = igb_set_vf_vlan(adapter, msgbuf, vf); break; default: - dev_err(&adapter->pdev->dev, "Unhandled Msg %08x\n", msgbuf[0]); + dev_err(&pdev->dev, "Unhandled Msg %08x\n", msgbuf[0]); retval = -1; break; } @@ -4268,8 +4665,53 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) msgbuf[0] |= E1000_VT_MSGTYPE_CTS; igb_write_mbx(hw, msgbuf, 1, vf); +} - return retval; +static void igb_msg_task(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + u32 vf; + + for (vf = 0; vf < adapter->vfs_allocated_count; vf++) { + /* process any reset requests */ + if (!igb_check_for_rst(hw, vf)) + igb_vf_reset_event(adapter, vf); + + /* process any messages pending */ + if (!igb_check_for_msg(hw, vf)) + igb_rcv_msg_from_vf(adapter, vf); + + /* process any acks */ + if (!igb_check_for_ack(hw, vf)) + igb_rcv_ack_from_vf(adapter, vf); + } +} + +/** + * igb_set_uta - Set unicast filter table address + * @adapter: board private structure + * + * The unicast table address is a register array of 32-bit registers. + * The table is meant to be used in a way similar to how the MTA is used + * however due to certain limitations in the hardware it is necessary to + * set all the hash bits to 1 and use the VMOLR ROPE bit as a promiscous + * enable bit to allow vlan tag stripping when promiscous mode is enabled + **/ +static void igb_set_uta(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + int i; + + /* The UTA table only exists on 82576 hardware and newer */ + if (hw->mac.type < e1000_82576) + return; + + /* we only need to do this if VMDq is enabled */ + if (!adapter->vfs_allocated_count) + return; + + for (i = 0; i < hw->mac.uta_reg_count; i++) + array_wr32(E1000_UTA, i, ~0); } /** @@ -4279,15 +4721,15 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) **/ static irqreturn_t igb_intr_msi(int irq, void *data) { - struct net_device *netdev = data; - struct igb_adapter *adapter = netdev_priv(netdev); + struct igb_adapter *adapter = data; + struct igb_q_vector *q_vector = adapter->q_vector[0]; struct e1000_hw *hw = &adapter->hw; /* read ICR disables interrupts using IAM */ u32 icr = rd32(E1000_ICR); - igb_write_itr(adapter->rx_ring); + igb_write_itr(q_vector); - if(icr & E1000_ICR_DOUTSYNC) { + if (icr & E1000_ICR_DOUTSYNC) { /* HW is reporting DMA is out of sync */ adapter->stats.doosync++; } @@ -4298,7 +4740,7 @@ static irqreturn_t igb_intr_msi(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - napi_schedule(&adapter->rx_ring[0].napi); + napi_schedule(&q_vector->napi); return IRQ_HANDLED; } @@ -4310,8 +4752,8 @@ static irqreturn_t igb_intr_msi(int irq, void *data) **/ static irqreturn_t igb_intr(int irq, void *data) { - struct net_device *netdev = data; - struct igb_adapter *adapter = netdev_priv(netdev); + struct igb_adapter *adapter = data; + struct igb_q_vector *q_vector = adapter->q_vector[0]; struct e1000_hw *hw = &adapter->hw; /* Interrupt Auto-Mask...upon reading ICR, interrupts are masked. No * need for the IMC write */ @@ -4319,14 +4761,14 @@ static irqreturn_t igb_intr(int irq, void *data) if (!icr) return IRQ_NONE; /* Not our interrupt */ - igb_write_itr(adapter->rx_ring); + igb_write_itr(q_vector); /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is * not set, then the adapter didn't send an interrupt */ if (!(icr & E1000_ICR_INT_ASSERTED)) return IRQ_NONE; - if(icr & E1000_ICR_DOUTSYNC) { + if (icr & E1000_ICR_DOUTSYNC) { /* HW is reporting DMA is out of sync */ adapter->stats.doosync++; } @@ -4338,26 +4780,27 @@ static irqreturn_t igb_intr(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - napi_schedule(&adapter->rx_ring[0].napi); + napi_schedule(&q_vector->napi); return IRQ_HANDLED; } -static inline void igb_rx_irq_enable(struct igb_ring *rx_ring) +static inline void igb_ring_irq_enable(struct igb_q_vector *q_vector) { - struct igb_adapter *adapter = rx_ring->adapter; + struct igb_adapter *adapter = q_vector->adapter; struct e1000_hw *hw = &adapter->hw; - if (adapter->itr_setting & 3) { - if (adapter->num_rx_queues == 1) + if ((q_vector->rx_ring && (adapter->rx_itr_setting & 3)) || + (!q_vector->rx_ring && (adapter->tx_itr_setting & 3))) { + if (!adapter->msix_entries) igb_set_itr(adapter); else - igb_update_ring_itr(rx_ring); + igb_update_ring_itr(q_vector); } if (!test_bit(__IGB_DOWN, &adapter->state)) { if (adapter->msix_entries) - wr32(E1000_EIMS, rx_ring->eims_value); + wr32(E1000_EIMS, q_vector->eims_value); else igb_irq_enable(adapter); } @@ -4370,76 +4813,101 @@ static inline void igb_rx_irq_enable(struct igb_ring *rx_ring) **/ static int igb_poll(struct napi_struct *napi, int budget) { - struct igb_ring *rx_ring = container_of(napi, struct igb_ring, napi); - int work_done = 0; + struct igb_q_vector *q_vector = container_of(napi, + struct igb_q_vector, + napi); + int tx_clean_complete = 1, work_done = 0; #ifdef CONFIG_IGB_DCA - if (rx_ring->adapter->flags & IGB_FLAG_DCA_ENABLED) - igb_update_rx_dca(rx_ring); + if (q_vector->adapter->flags & IGB_FLAG_DCA_ENABLED) + igb_update_dca(q_vector); #endif - igb_clean_rx_irq_adv(rx_ring, &work_done, budget); + if (q_vector->tx_ring) + tx_clean_complete = igb_clean_tx_irq(q_vector); - if (rx_ring->buddy) { -#ifdef CONFIG_IGB_DCA - if (rx_ring->adapter->flags & IGB_FLAG_DCA_ENABLED) - igb_update_tx_dca(rx_ring->buddy); -#endif - if (!igb_clean_tx_irq(rx_ring->buddy)) - work_done = budget; - } + if (q_vector->rx_ring) + igb_clean_rx_irq_adv(q_vector, &work_done, budget); + + if (!tx_clean_complete) + work_done = budget; /* If not enough Rx work done, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - igb_rx_irq_enable(rx_ring); + igb_ring_irq_enable(q_vector); } return work_done; } /** - * igb_hwtstamp - utility function which checks for TX time stamp + * igb_systim_to_hwtstamp - convert system time value to hw timestamp * @adapter: board private structure + * @shhwtstamps: timestamp structure to update + * @regval: unsigned 64bit system time value. + * + * We need to convert the system time value stored in the RX/TXSTMP registers + * into a hwtstamp which can be used by the upper level timestamping functions + */ +static void igb_systim_to_hwtstamp(struct igb_adapter *adapter, + struct skb_shared_hwtstamps *shhwtstamps, + u64 regval) +{ + u64 ns; + + /* + * The 82580 starts with 1ns at bit 0 in RX/TXSTMPL, shift this up to + * 24 to match clock shift we setup earlier. + */ + if (adapter->hw.mac.type == e1000_82580) + regval <<= IGB_82580_TSYNC_SHIFT; + + ns = timecounter_cyc2time(&adapter->clock, regval); + timecompare_update(&adapter->compare, ns); + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = ns_to_ktime(ns); + shhwtstamps->syststamp = timecompare_transform(&adapter->compare, ns); +} + +/** + * igb_tx_hwtstamp - utility function which checks for TX time stamp + * @q_vector: pointer to q_vector containing needed info * @skb: packet that was just sent * * If we were asked to do hardware stamping and such a time stamp is * available, then it must have been for this skb here because we only * allow only one such packet into the queue. */ -static void igb_tx_hwtstamp(struct igb_adapter *adapter, struct sk_buff *skb) +static void igb_tx_hwtstamp(struct igb_q_vector *q_vector, struct sk_buff *skb) { + struct igb_adapter *adapter = q_vector->adapter; union skb_shared_tx *shtx = skb_tx(skb); struct e1000_hw *hw = &adapter->hw; + struct skb_shared_hwtstamps shhwtstamps; + u64 regval; - if (unlikely(shtx->hardware)) { - u32 valid = rd32(E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID; - if (valid) { - u64 regval = rd32(E1000_TXSTMPL); - u64 ns; - struct skb_shared_hwtstamps shhwtstamps; - - memset(&shhwtstamps, 0, sizeof(shhwtstamps)); - regval |= (u64)rd32(E1000_TXSTMPH) << 32; - ns = timecounter_cyc2time(&adapter->clock, - regval); - timecompare_update(&adapter->compare, ns); - shhwtstamps.hwtstamp = ns_to_ktime(ns); - shhwtstamps.syststamp = - timecompare_transform(&adapter->compare, ns); - skb_tstamp_tx(skb, &shhwtstamps); - } - } + /* if skb does not support hw timestamp or TX stamp not valid exit */ + if (likely(!shtx->hardware) || + !(rd32(E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID)) + return; + + regval = rd32(E1000_TXSTMPL); + regval |= (u64)rd32(E1000_TXSTMPH) << 32; + + igb_systim_to_hwtstamp(adapter, &shhwtstamps, regval); + skb_tstamp_tx(skb, &shhwtstamps); } /** * igb_clean_tx_irq - Reclaim resources after transmit completes - * @adapter: board private structure + * @q_vector: pointer to q_vector containing needed info * returns true if ring is completely cleaned **/ -static bool igb_clean_tx_irq(struct igb_ring *tx_ring) +static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) { - struct igb_adapter *adapter = tx_ring->adapter; - struct net_device *netdev = adapter->netdev; + struct igb_adapter *adapter = q_vector->adapter; + struct igb_ring *tx_ring = q_vector->tx_ring; + struct net_device *netdev = tx_ring->netdev; struct e1000_hw *hw = &adapter->hw; struct igb_buffer *buffer_info; struct sk_buff *skb; @@ -4470,10 +4938,10 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring) total_packets += segs; total_bytes += bytecount; - igb_tx_hwtstamp(adapter, skb); + igb_tx_hwtstamp(q_vector, skb); } - igb_unmap_and_free_tx_resource(adapter, buffer_info); + igb_unmap_and_free_tx_resource(tx_ring, buffer_info); tx_desc->wb.status = 0; i++; @@ -4496,7 +4964,7 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring) if (__netif_subqueue_stopped(netdev, tx_ring->queue_index) && !(test_bit(__IGB_DOWN, &adapter->state))) { netif_wake_subqueue(netdev, tx_ring->queue_index); - ++adapter->restart_queue; + tx_ring->tx_stats.restart_queue++; } } @@ -4506,12 +4974,11 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring) tx_ring->detect_tx_hung = false; if (tx_ring->buffer_info[i].time_stamp && time_after(jiffies, tx_ring->buffer_info[i].time_stamp + - (adapter->tx_timeout_factor * HZ)) - && !(rd32(E1000_STATUS) & - E1000_STATUS_TXOFF)) { + (adapter->tx_timeout_factor * HZ)) && + !(rd32(E1000_STATUS) & E1000_STATUS_TXOFF)) { /* detected Tx unit hang */ - dev_err(&adapter->pdev->dev, + dev_err(&tx_ring->pdev->dev, "Detected Tx Unit Hang\n" " Tx Queue <%d>\n" " TDH <%x>\n" @@ -4524,11 +4991,11 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring) " jiffies <%lx>\n" " desc.status <%x>\n", tx_ring->queue_index, - readl(adapter->hw.hw_addr + tx_ring->head), - readl(adapter->hw.hw_addr + tx_ring->tail), + readl(tx_ring->head), + readl(tx_ring->tail), tx_ring->next_to_use, tx_ring->next_to_clean, - tx_ring->buffer_info[i].time_stamp, + tx_ring->buffer_info[eop].time_stamp, eop, jiffies, eop_desc->wb.status); @@ -4539,43 +5006,38 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring) tx_ring->total_packets += total_packets; tx_ring->tx_stats.bytes += total_bytes; tx_ring->tx_stats.packets += total_packets; - adapter->net_stats.tx_bytes += total_bytes; - adapter->net_stats.tx_packets += total_packets; return (count < tx_ring->count); } /** * igb_receive_skb - helper function to handle rx indications - * @ring: pointer to receive ring receving this packet - * @status: descriptor status field as written by hardware - * @rx_desc: receive descriptor containing vlan and type information. - * @skb: pointer to sk_buff to be indicated to stack + * @q_vector: structure containing interrupt and ring information + * @skb: packet to send up + * @vlan_tag: vlan tag for packet **/ -static void igb_receive_skb(struct igb_ring *ring, u8 status, - union e1000_adv_rx_desc * rx_desc, - struct sk_buff *skb) -{ - struct igb_adapter * adapter = ring->adapter; - bool vlan_extracted = (adapter->vlgrp && (status & E1000_RXD_STAT_VP)); - - skb_record_rx_queue(skb, ring->queue_index); - if (vlan_extracted) - vlan_gro_receive(&ring->napi, adapter->vlgrp, - le16_to_cpu(rx_desc->wb.upper.vlan), - skb); +static void igb_receive_skb(struct igb_q_vector *q_vector, + struct sk_buff *skb, + u16 vlan_tag) +{ + struct igb_adapter *adapter = q_vector->adapter; + + if (vlan_tag) + vlan_gro_receive(&q_vector->napi, adapter->vlgrp, + vlan_tag, skb); else - napi_gro_receive(&ring->napi, skb); + napi_gro_receive(&q_vector->napi, skb); } -static inline void igb_rx_checksum_adv(struct igb_adapter *adapter, +static inline void igb_rx_checksum_adv(struct igb_ring *ring, u32 status_err, struct sk_buff *skb) { skb->ip_summed = CHECKSUM_NONE; /* Ignore Checksum bit is set or checksum is disabled through ethtool */ - if ((status_err & E1000_RXD_STAT_IXSM) || - (adapter->flags & IGB_FLAG_RX_CSUM_DISABLED)) + if (!(ring->flags & IGB_RING_FLAG_RX_CSUM) || + (status_err & E1000_RXD_STAT_IXSM)) return; + /* TCP/UDP checksum error bit is set */ if (status_err & (E1000_RXDEXT_STATERR_TCPE | E1000_RXDEXT_STATERR_IPE)) { @@ -4584,9 +5046,10 @@ static inline void igb_rx_checksum_adv(struct igb_adapter *adapter, * L4E bit is set incorrectly on 64 byte (60 byte w/o crc) * packets, (aka let the stack check the crc32c) */ - if (!((adapter->hw.mac.type == e1000_82576) && - (skb->len == 60))) - adapter->hw_csum_err++; + if ((skb->len == 60) && + (ring->flags & IGB_RING_FLAG_RX_SCTP_CSUM)) + ring->rx_stats.csum_err++; + /* let the stack verify checksum errors */ return; } @@ -4594,11 +5057,38 @@ static inline void igb_rx_checksum_adv(struct igb_adapter *adapter, if (status_err & (E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS)) skb->ip_summed = CHECKSUM_UNNECESSARY; - dev_dbg(&adapter->pdev->dev, "cksum success: bits %08X\n", status_err); - adapter->hw_csum_good++; + dev_dbg(&ring->pdev->dev, "cksum success: bits %08X\n", status_err); } -static inline u16 igb_get_hlen(struct igb_adapter *adapter, +static inline void igb_rx_hwtstamp(struct igb_q_vector *q_vector, u32 staterr, + struct sk_buff *skb) +{ + struct igb_adapter *adapter = q_vector->adapter; + struct e1000_hw *hw = &adapter->hw; + u64 regval; + + /* + * If this bit is set, then the RX registers contain the time stamp. No + * other packet will be time stamped until we read these registers, so + * read the registers to make them available again. Because only one + * packet can be time stamped at a time, we know that the register + * values must belong to this one here and therefore we don't need to + * compare any of the additional attributes stored for it. + * + * If nothing went wrong, then it should have a skb_shared_tx that we + * can turn into a skb_shared_hwtstamps. + */ + if (likely(!(staterr & E1000_RXDADV_STAT_TS))) + return; + if (!(rd32(E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID)) + return; + + regval = rd32(E1000_RXSTMPL); + regval |= (u64)rd32(E1000_RXSTMPH) << 32; + + igb_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval); +} +static inline u16 igb_get_hlen(struct igb_ring *rx_ring, union e1000_adv_rx_desc *rx_desc) { /* HW will not DMA in data larger than the given buffer, even if it @@ -4607,27 +5097,28 @@ static inline u16 igb_get_hlen(struct igb_adapter *adapter, */ u16 hlen = (le16_to_cpu(rx_desc->wb.lower.lo_dword.hdr_info) & E1000_RXDADV_HDRBUFLEN_MASK) >> E1000_RXDADV_HDRBUFLEN_SHIFT; - if (hlen > adapter->rx_ps_hdr_size) - hlen = adapter->rx_ps_hdr_size; + if (hlen > rx_ring->rx_buffer_len) + hlen = rx_ring->rx_buffer_len; return hlen; } -static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring, - int *work_done, int budget) +static bool igb_clean_rx_irq_adv(struct igb_q_vector *q_vector, + int *work_done, int budget) { - struct igb_adapter *adapter = rx_ring->adapter; - struct net_device *netdev = adapter->netdev; - struct e1000_hw *hw = &adapter->hw; - struct pci_dev *pdev = adapter->pdev; + struct igb_ring *rx_ring = q_vector->rx_ring; + struct net_device *netdev = rx_ring->netdev; + struct pci_dev *pdev = rx_ring->pdev; union e1000_adv_rx_desc *rx_desc , *next_rxd; struct igb_buffer *buffer_info , *next_buffer; struct sk_buff *skb; bool cleaned = false; int cleaned_count = 0; + int current_node = numa_node_id(); unsigned int total_bytes = 0, total_packets = 0; unsigned int i; u32 staterr; u16 length; + u16 vlan_tag; i = rx_ring->next_to_clean; buffer_info = &rx_ring->buffer_info[i]; @@ -4646,6 +5137,7 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring, i++; if (i == rx_ring->count) i = 0; + next_rxd = E1000_RX_DESC_ADV(*rx_ring, i); prefetch(next_rxd); next_buffer = &rx_ring->buffer_info[i]; @@ -4654,23 +5146,16 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring, cleaned = true; cleaned_count++; - /* this is the fast path for the non-packet split case */ - if (!adapter->rx_ps_hdr_size) { - pci_unmap_single(pdev, buffer_info->dma, - adapter->rx_buffer_len, - PCI_DMA_FROMDEVICE); - buffer_info->dma = 0; - skb_put(skb, length); - goto send_up; - } - if (buffer_info->dma) { - u16 hlen = igb_get_hlen(adapter, rx_desc); pci_unmap_single(pdev, buffer_info->dma, - adapter->rx_ps_hdr_size, + rx_ring->rx_buffer_len, PCI_DMA_FROMDEVICE); buffer_info->dma = 0; - skb_put(skb, hlen); + if (rx_ring->rx_buffer_len >= IGB_RXBUFFER_1024) { + skb_put(skb, length); + goto send_up; + } + skb_put(skb, igb_get_hlen(rx_ring, rx_desc)); } if (length) { @@ -4683,15 +5168,14 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring, buffer_info->page_offset, length); - if ((adapter->rx_buffer_len > (PAGE_SIZE / 2)) || - (page_count(buffer_info->page) != 1)) + if ((page_count(buffer_info->page) != 1) || + (page_to_nid(buffer_info->page) != current_node)) buffer_info->page = NULL; else get_page(buffer_info->page); skb->len += length; skb->data_len += length; - skb->truesize += length; } @@ -4703,60 +5187,24 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring, goto next_desc; } send_up: - /* - * If this bit is set, then the RX registers contain - * the time stamp. No other packet will be time - * stamped until we read these registers, so read the - * registers to make them available again. Because - * only one packet can be time stamped at a time, we - * know that the register values must belong to this - * one here and therefore we don't need to compare - * any of the additional attributes stored for it. - * - * If nothing went wrong, then it should have a - * skb_shared_tx that we can turn into a - * skb_shared_hwtstamps. - * - * TODO: can time stamping be triggered (thus locking - * the registers) without the packet reaching this point - * here? In that case RX time stamping would get stuck. - * - * TODO: in "time stamp all packets" mode this bit is - * not set. Need a global flag for this mode and then - * always read the registers. Cannot be done without - * a race condition. - */ - if (unlikely(staterr & E1000_RXD_STAT_TS)) { - u64 regval; - u64 ns; - struct skb_shared_hwtstamps *shhwtstamps = - skb_hwtstamps(skb); - - WARN(!(rd32(E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID), - "igb: no RX time stamp available for time stamped packet"); - regval = rd32(E1000_RXSTMPL); - regval |= (u64)rd32(E1000_RXSTMPH) << 32; - ns = timecounter_cyc2time(&adapter->clock, regval); - timecompare_update(&adapter->compare, ns); - memset(shhwtstamps, 0, sizeof(*shhwtstamps)); - shhwtstamps->hwtstamp = ns_to_ktime(ns); - shhwtstamps->syststamp = - timecompare_transform(&adapter->compare, ns); - } - if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) { dev_kfree_skb_irq(skb); goto next_desc; } + igb_rx_hwtstamp(q_vector, staterr, skb); total_bytes += skb->len; total_packets++; - igb_rx_checksum_adv(adapter, staterr, skb); + igb_rx_checksum_adv(rx_ring, staterr, skb); skb->protocol = eth_type_trans(skb, netdev); + skb_record_rx_queue(skb, rx_ring->queue_index); - igb_receive_skb(rx_ring, staterr, rx_desc, skb); + vlan_tag = ((staterr & E1000_RXD_STAT_VP) ? + le16_to_cpu(rx_desc->wb.upper.vlan) : 0); + + igb_receive_skb(q_vector, skb, vlan_tag); next_desc: rx_desc->wb.upper.status_error = 0; @@ -4783,8 +5231,6 @@ next_desc: rx_ring->total_bytes += total_bytes; rx_ring->rx_stats.packets += total_packets; rx_ring->rx_stats.bytes += total_bytes; - adapter->net_stats.rx_bytes += total_bytes; - adapter->net_stats.rx_packets += total_packets; return cleaned; } @@ -4792,12 +5238,9 @@ next_desc: * igb_alloc_rx_buffers_adv - Replace used receive buffers; packet split * @adapter: address of board private structure **/ -static void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring, - int cleaned_count) +void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring, int cleaned_count) { - struct igb_adapter *adapter = rx_ring->adapter; - struct net_device *netdev = adapter->netdev; - struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = rx_ring->netdev; union e1000_adv_rx_desc *rx_desc; struct igb_buffer *buffer_info; struct sk_buff *skb; @@ -4807,19 +5250,16 @@ static void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring, i = rx_ring->next_to_use; buffer_info = &rx_ring->buffer_info[i]; - if (adapter->rx_ps_hdr_size) - bufsz = adapter->rx_ps_hdr_size; - else - bufsz = adapter->rx_buffer_len; + bufsz = rx_ring->rx_buffer_len; while (cleaned_count--) { rx_desc = E1000_RX_DESC_ADV(*rx_ring, i); - if (adapter->rx_ps_hdr_size && !buffer_info->page_dma) { + if ((bufsz < IGB_RXBUFFER_1024) && !buffer_info->page_dma) { if (!buffer_info->page) { - buffer_info->page = alloc_page(GFP_ATOMIC); + buffer_info->page = netdev_alloc_page(netdev); if (!buffer_info->page) { - adapter->alloc_rx_buff_failed++; + rx_ring->rx_stats.alloc_failed++; goto no_buffers; } buffer_info->page_offset = 0; @@ -4827,39 +5267,48 @@ static void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring, buffer_info->page_offset ^= PAGE_SIZE / 2; } buffer_info->page_dma = - pci_map_page(pdev, buffer_info->page, + pci_map_page(rx_ring->pdev, buffer_info->page, buffer_info->page_offset, PAGE_SIZE / 2, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(rx_ring->pdev, + buffer_info->page_dma)) { + buffer_info->page_dma = 0; + rx_ring->rx_stats.alloc_failed++; + goto no_buffers; + } } - if (!buffer_info->skb) { - skb = netdev_alloc_skb(netdev, bufsz + NET_IP_ALIGN); + skb = buffer_info->skb; + if (!skb) { + skb = netdev_alloc_skb_ip_align(netdev, bufsz); if (!skb) { - adapter->alloc_rx_buff_failed++; + rx_ring->rx_stats.alloc_failed++; goto no_buffers; } - /* Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; - buffer_info->dma = pci_map_single(pdev, skb->data, + } + if (!buffer_info->dma) { + buffer_info->dma = pci_map_single(rx_ring->pdev, + skb->data, bufsz, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(rx_ring->pdev, + buffer_info->dma)) { + buffer_info->dma = 0; + rx_ring->rx_stats.alloc_failed++; + goto no_buffers; + } } /* Refresh the desc even if buffer_addrs didn't change because * each write-back erases this info. */ - if (adapter->rx_ps_hdr_size) { + if (bufsz < IGB_RXBUFFER_1024) { rx_desc->read.pkt_addr = cpu_to_le64(buffer_info->page_dma); rx_desc->read.hdr_addr = cpu_to_le64(buffer_info->dma); } else { - rx_desc->read.pkt_addr = - cpu_to_le64(buffer_info->dma); + rx_desc->read.pkt_addr = cpu_to_le64(buffer_info->dma); rx_desc->read.hdr_addr = 0; } @@ -4882,7 +5331,7 @@ no_buffers: * applicable for weak-ordered memory model archs, * such as IA-64). */ wmb(); - writel(i, adapter->hw.hw_addr + rx_ring->tail); + writel(i, rx_ring->tail); } } @@ -4941,13 +5390,11 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev, struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; struct hwtstamp_config config; - u32 tsync_tx_ctl_bit = E1000_TSYNCTXCTL_ENABLED; - u32 tsync_rx_ctl_bit = E1000_TSYNCRXCTL_ENABLED; - u32 tsync_rx_ctl_type = 0; + u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED; + u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED; u32 tsync_rx_cfg = 0; - int is_l4 = 0; - int is_l2 = 0; - short port = 319; /* PTP */ + bool is_l4 = false; + bool is_l2 = false; u32 regval; if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) @@ -4959,10 +5406,8 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev, switch (config.tx_type) { case HWTSTAMP_TX_OFF: - tsync_tx_ctl_bit = 0; - break; + tsync_tx_ctl = 0; case HWTSTAMP_TX_ON: - tsync_tx_ctl_bit = E1000_TSYNCTXCTL_ENABLED; break; default: return -ERANGE; @@ -4970,7 +5415,7 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev, switch (config.rx_filter) { case HWTSTAMP_FILTER_NONE: - tsync_rx_ctl_bit = 0; + tsync_rx_ctl = 0; break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: @@ -4981,86 +5426,97 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev, * possible to time stamp both Sync and Delay_Req messages * => fall back to time stamping all packets */ - tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_ALL; + tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL; config.rx_filter = HWTSTAMP_FILTER_ALL; break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: - tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L4_V1; + tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1; tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE; - is_l4 = 1; + is_l4 = true; break; case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: - tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L4_V1; + tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1; tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE; - is_l4 = 1; + is_l4 = true; break; case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: - tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L2_L4_V2; + tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2; tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE; - is_l2 = 1; - is_l4 = 1; + is_l2 = true; + is_l4 = true; config.rx_filter = HWTSTAMP_FILTER_SOME; break; case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: - tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L2_L4_V2; + tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2; tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE; - is_l2 = 1; - is_l4 = 1; + is_l2 = true; + is_l4 = true; config.rx_filter = HWTSTAMP_FILTER_SOME; break; case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: - tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_EVENT_V2; + tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_EVENT_V2; config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; - is_l2 = 1; + is_l2 = true; break; default: return -ERANGE; } + if (hw->mac.type == e1000_82575) { + if (tsync_rx_ctl | tsync_tx_ctl) + return -EINVAL; + return 0; + } + /* enable/disable TX */ regval = rd32(E1000_TSYNCTXCTL); - regval = (regval & ~E1000_TSYNCTXCTL_ENABLED) | tsync_tx_ctl_bit; + regval &= ~E1000_TSYNCTXCTL_ENABLED; + regval |= tsync_tx_ctl; wr32(E1000_TSYNCTXCTL, regval); - /* enable/disable RX, define which PTP packets are time stamped */ + /* enable/disable RX */ regval = rd32(E1000_TSYNCRXCTL); - regval = (regval & ~E1000_TSYNCRXCTL_ENABLED) | tsync_rx_ctl_bit; - regval = (regval & ~0xE) | tsync_rx_ctl_type; + regval &= ~(E1000_TSYNCRXCTL_ENABLED | E1000_TSYNCRXCTL_TYPE_MASK); + regval |= tsync_rx_ctl; wr32(E1000_TSYNCRXCTL, regval); - wr32(E1000_TSYNCRXCFG, tsync_rx_cfg); - /* - * Ethertype Filter Queue Filter[0][15:0] = 0x88F7 - * (Ethertype to filter on) - * Ethertype Filter Queue Filter[0][26] = 0x1 (Enable filter) - * Ethertype Filter Queue Filter[0][30] = 0x1 (Enable Timestamping) - */ - wr32(E1000_ETQF0, is_l2 ? 0x440088f7 : 0); - - /* L4 Queue Filter[0]: only filter by source and destination port */ - wr32(E1000_SPQF0, htons(port)); - wr32(E1000_IMIREXT(0), is_l4 ? - ((1<<12) | (1<<19) /* bypass size and control flags */) : 0); - wr32(E1000_IMIR(0), is_l4 ? - (htons(port) - | (0<<16) /* immediate interrupt disabled */ - | 0 /* (1<<17) bit cleared: do not bypass - destination port check */) - : 0); - wr32(E1000_FTQF0, is_l4 ? - (0x11 /* UDP */ - | (1<<15) /* VF not compared */ - | (1<<27) /* Enable Timestamping */ - | (7<<28) /* only source port filter enabled, - source/target address and protocol - masked */) - : ((1<<15) | (15<<28) /* all mask bits set = filter not - enabled */)); + /* define which PTP packets are time stamped */ + wr32(E1000_TSYNCRXCFG, tsync_rx_cfg); + /* define ethertype filter for timestamped packets */ + if (is_l2) + wr32(E1000_ETQF(3), + (E1000_ETQF_FILTER_ENABLE | /* enable filter */ + E1000_ETQF_1588 | /* enable timestamping */ + ETH_P_1588)); /* 1588 eth protocol type */ + else + wr32(E1000_ETQF(3), 0); + +#define PTP_PORT 319 + /* L4 Queue Filter[3]: filter by destination port and protocol */ + if (is_l4) { + u32 ftqf = (IPPROTO_UDP /* UDP */ + | E1000_FTQF_VF_BP /* VF not compared */ + | E1000_FTQF_1588_TIME_STAMP /* Enable Timestamping */ + | E1000_FTQF_MASK); /* mask all inputs */ + ftqf &= ~E1000_FTQF_MASK_PROTO_BP; /* enable protocol check */ + + wr32(E1000_IMIR(3), htons(PTP_PORT)); + wr32(E1000_IMIREXT(3), + (E1000_IMIREXT_SIZE_BP | E1000_IMIREXT_CTRL_BP)); + if (hw->mac.type == e1000_82576) { + /* enable source port check */ + wr32(E1000_SPQF(3), htons(PTP_PORT)); + ftqf &= ~E1000_FTQF_MASK_SOURCE_PORT_BP; + } + wr32(E1000_FTQF(3), ftqf); + } else { + wr32(E1000_FTQF(3), E1000_FTQF_MASK); + } wrfl(); adapter->hwtstamp_config = config; @@ -5137,21 +5593,15 @@ static void igb_vlan_rx_register(struct net_device *netdev, ctrl |= E1000_CTRL_VME; wr32(E1000_CTRL, ctrl); - /* enable VLAN receive filtering */ + /* Disable CFI check */ rctl = rd32(E1000_RCTL); rctl &= ~E1000_RCTL_CFIEN; wr32(E1000_RCTL, rctl); - igb_update_mng_vlan(adapter); } else { /* disable VLAN tag insert/strip */ ctrl = rd32(E1000_CTRL); ctrl &= ~E1000_CTRL_VME; wr32(E1000_CTRL, ctrl); - - if (adapter->mng_vlan_id != (u16)IGB_MNG_VLAN_NONE) { - igb_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id); - adapter->mng_vlan_id = IGB_MNG_VLAN_NONE; - } } igb_rlpml_set(adapter); @@ -5166,16 +5616,11 @@ static void igb_vlan_rx_add_vid(struct net_device *netdev, u16 vid) struct e1000_hw *hw = &adapter->hw; int pf_id = adapter->vfs_allocated_count; - if ((hw->mng_cookie.status & - E1000_MNG_DHCP_COOKIE_STATUS_VLAN) && - (vid == adapter->mng_vlan_id)) - return; - - /* add vid to vlvf if sr-iov is enabled, - * if that fails add directly to filter table */ - if (igb_vlvf_set(adapter, vid, true, pf_id)) - igb_vfta_set(hw, vid, true); + /* attempt to add filter to vlvf array */ + igb_vlvf_set(adapter, vid, true, pf_id); + /* add the filter since PF can receive vlans w/o entry in vlvf */ + igb_vfta_set(hw, vid, true); } static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) @@ -5183,6 +5628,7 @@ static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; int pf_id = adapter->vfs_allocated_count; + s32 err; igb_irq_disable(adapter); vlan_group_set_device(adapter->vlgrp, vid, NULL); @@ -5190,17 +5636,11 @@ static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) if (!test_bit(__IGB_DOWN, &adapter->state)) igb_irq_enable(adapter); - if ((adapter->hw.mng_cookie.status & - E1000_MNG_DHCP_COOKIE_STATUS_VLAN) && - (vid == adapter->mng_vlan_id)) { - /* release control to f/w */ - igb_release_hw_control(adapter); - return; - } + /* remove vlan from VLVF table array */ + err = igb_vlvf_set(adapter, vid, false, pf_id); - /* remove vid from vlvf if sr-iov is enabled, - * if not in vlvf remove from vfta */ - if (igb_vlvf_set(adapter, vid, false, pf_id)) + /* if vid was not present in VLVF just remove it from table */ + if (err) igb_vfta_set(hw, vid, false); } @@ -5220,6 +5660,7 @@ static void igb_restore_vlan(struct igb_adapter *adapter) int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx) { + struct pci_dev *pdev = adapter->pdev; struct e1000_mac_info *mac = &adapter->hw.mac; mac->autoneg = 0; @@ -5243,8 +5684,7 @@ int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx) break; case SPEED_1000 + DUPLEX_HALF: /* not supported */ default: - dev_err(&adapter->pdev->dev, - "Unsupported Speed/Duplex configuration\n"); + dev_err(&pdev->dev, "Unsupported Speed/Duplex configuration\n"); return -EINVAL; } return 0; @@ -5266,9 +5706,7 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake) if (netif_running(netdev)) igb_close(netdev); - igb_reset_interrupt_capability(adapter); - - igb_free_queues(adapter); + igb_clear_interrupt_scheme(adapter); #ifdef CONFIG_PM retval = pci_save_state(pdev); @@ -5300,7 +5738,7 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake) wr32(E1000_CTRL, ctrl); /* Allow time for pending master requests to run */ - igb_disable_pcie_master(&adapter->hw); + igb_disable_pcie_master(hw); wr32(E1000_WUC, E1000_WUC_PME_EN); wr32(E1000_WUFC, wufc); @@ -5363,9 +5801,7 @@ static int igb_resume(struct pci_dev *pdev) pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); - igb_set_interrupt_capability(adapter); - - if (igb_alloc_queues(adapter)) { + if (igb_init_interrupt_scheme(adapter)) { dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); return -ENOMEM; } @@ -5417,22 +5853,16 @@ static void igb_netpoll(struct net_device *netdev) int i; if (!adapter->msix_entries) { + struct igb_q_vector *q_vector = adapter->q_vector[0]; igb_irq_disable(adapter); - napi_schedule(&adapter->rx_ring[0].napi); + napi_schedule(&q_vector->napi); return; } - for (i = 0; i < adapter->num_tx_queues; i++) { - struct igb_ring *tx_ring = &adapter->tx_ring[i]; - wr32(E1000_EIMC, tx_ring->eims_value); - igb_clean_tx_irq(tx_ring); - wr32(E1000_EIMS, tx_ring->eims_value); - } - - for (i = 0; i < adapter->num_rx_queues; i++) { - struct igb_ring *rx_ring = &adapter->rx_ring[i]; - wr32(E1000_EIMC, rx_ring->eims_value); - napi_schedule(&rx_ring->napi); + for (i = 0; i < adapter->num_q_vectors; i++) { + struct igb_q_vector *q_vector = adapter->q_vector[i]; + wr32(E1000_EIMC, q_vector->eims_value); + napi_schedule(&q_vector->napi); } } #endif /* CONFIG_NET_POLL_CONTROLLER */ @@ -5532,6 +5962,33 @@ static void igb_io_resume(struct pci_dev *pdev) igb_get_hw_control(adapter); } +static void igb_rar_set_qsel(struct igb_adapter *adapter, u8 *addr, u32 index, + u8 qsel) +{ + u32 rar_low, rar_high; + struct e1000_hw *hw = &adapter->hw; + + /* HW expects these in little endian so we reverse the byte order + * from network order (big endian) to little endian + */ + rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) | + ((u32) addr[2] << 16) | ((u32) addr[3] << 24)); + rar_high = ((u32) addr[4] | ((u32) addr[5] << 8)); + + /* Indicate to hardware the Address is Valid. */ + rar_high |= E1000_RAH_AV; + + if (hw->mac.type == e1000_82575) + rar_high |= E1000_RAH_POOL_1 * qsel; + else + rar_high |= E1000_RAH_POOL_1 << qsel; + + wr32(E1000_RAL(index), rar_low); + wrfl(); + wr32(E1000_RAH(index), rar_high); + wrfl(); +} + static int igb_set_vf_mac(struct igb_adapter *adapter, int vf, unsigned char *mac_addr) { @@ -5542,8 +5999,7 @@ static int igb_set_vf_mac(struct igb_adapter *adapter, memcpy(adapter->vf_data[vf].vf_mac_addresses, mac_addr, ETH_ALEN); - igb_rar_set(hw, mac_addr, rar_entry); - igb_set_rah_pool(hw, vf, rar_entry); + igb_rar_set_qsel(adapter, mac_addr, rar_entry, vf); return 0; } @@ -5551,19 +6007,29 @@ static int igb_set_vf_mac(struct igb_adapter *adapter, static void igb_vmm_control(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; - u32 reg_data; + u32 reg; - if (!adapter->vfs_allocated_count) + /* replication is not supported for 82575 */ + if (hw->mac.type == e1000_82575) return; - /* VF's need PF reset indication before they - * can send/receive mail */ - reg_data = rd32(E1000_CTRL_EXT); - reg_data |= E1000_CTRL_EXT_PFRSTD; - wr32(E1000_CTRL_EXT, reg_data); + /* enable replication vlan tag stripping */ + reg = rd32(E1000_RPLOLR); + reg |= E1000_RPLOLR_STRVLAN; + wr32(E1000_RPLOLR, reg); - igb_vmdq_set_loopback_pf(hw, true); - igb_vmdq_set_replication_pf(hw, true); + /* notify HW that the MAC is adding vlan tags */ + reg = rd32(E1000_DTXCTL); + reg |= E1000_DTXCTL_VLAN_ADDED; + wr32(E1000_DTXCTL, reg); + + if (adapter->vfs_allocated_count) { + igb_vmdq_set_loopback_pf(hw, true); + igb_vmdq_set_replication_pf(hw, true); + } else { + igb_vmdq_set_loopback_pf(hw, false); + igb_vmdq_set_replication_pf(hw, false); + } } /* igb_main.c */ diff --git a/drivers/net/igbvf/ethtool.c b/drivers/net/igbvf/ethtool.c index c68265bd0d1a..8afff07ff559 100644 --- a/drivers/net/igbvf/ethtool.c +++ b/drivers/net/igbvf/ethtool.c @@ -367,16 +367,6 @@ static int igbvf_link_test(struct igbvf_adapter *adapter, u64 *data) return *data; } -static int igbvf_get_self_test_count(struct net_device *netdev) -{ - return IGBVF_TEST_LEN; -} - -static int igbvf_get_stats_count(struct net_device *netdev) -{ - return IGBVF_GLOBAL_STATS_LEN; -} - static void igbvf_diag_test(struct net_device *netdev, struct ethtool_test *eth_test, u64 *data) { @@ -484,6 +474,18 @@ static void igbvf_get_ethtool_stats(struct net_device *netdev, } +static int igbvf_get_sset_count(struct net_device *dev, int stringset) +{ + switch(stringset) { + case ETH_SS_TEST: + return IGBVF_TEST_LEN; + case ETH_SS_STATS: + return IGBVF_GLOBAL_STATS_LEN; + default: + return -EINVAL; + } +} + static void igbvf_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { @@ -532,11 +534,10 @@ static const struct ethtool_ops igbvf_ethtool_ops = { .get_tso = ethtool_op_get_tso, .set_tso = igbvf_set_tso, .self_test = igbvf_diag_test, + .get_sset_count = igbvf_get_sset_count, .get_strings = igbvf_get_strings, .phys_id = igbvf_phys_id, .get_ethtool_stats = igbvf_get_ethtool_stats, - .self_test_count = igbvf_get_self_test_count, - .get_stats_count = igbvf_get_stats_count, .get_coalesce = igbvf_get_coalesce, .set_coalesce = igbvf_set_coalesce, }; diff --git a/drivers/net/igbvf/igbvf.h b/drivers/net/igbvf/igbvf.h index 8e9b67ebbf8b..3d1ee7a8478e 100644 --- a/drivers/net/igbvf/igbvf.h +++ b/drivers/net/igbvf/igbvf.h @@ -117,6 +117,7 @@ struct igbvf_buffer { unsigned long time_stamp; u16 length; u16 next_to_watch; + u16 mapped_as_page; }; /* Rx */ struct { diff --git a/drivers/net/igbvf/netdev.c b/drivers/net/igbvf/netdev.c index 91024a3cdad3..a127620dc653 100644 --- a/drivers/net/igbvf/netdev.c +++ b/drivers/net/igbvf/netdev.c @@ -170,18 +170,12 @@ static void igbvf_alloc_rx_buffers(struct igbvf_ring *rx_ring, } if (!buffer_info->skb) { - skb = netdev_alloc_skb(netdev, bufsz + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(netdev, bufsz); if (!skb) { adapter->alloc_rx_buff_failed++; goto no_buffers; } - /* Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; buffer_info->dma = pci_map_single(pdev, skb->data, bufsz, @@ -372,10 +366,20 @@ next_desc: static void igbvf_put_txbuf(struct igbvf_adapter *adapter, struct igbvf_buffer *buffer_info) { - buffer_info->dma = 0; + if (buffer_info->dma) { + if (buffer_info->mapped_as_page) + pci_unmap_page(adapter->pdev, + buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + else + pci_unmap_single(adapter->pdev, + buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + buffer_info->dma = 0; + } if (buffer_info->skb) { - skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb, - DMA_TO_DEVICE); dev_kfree_skb_any(buffer_info->skb); buffer_info->skb = NULL; } @@ -823,8 +827,8 @@ static bool igbvf_clean_tx_irq(struct igbvf_ring *tx_ring) adapter->detect_tx_hung = false; if (tx_ring->buffer_info[i].time_stamp && time_after(jiffies, tx_ring->buffer_info[i].time_stamp + - (adapter->tx_timeout_factor * HZ)) - && !(er32(STATUS) & E1000_STATUS_TXOFF)) { + (adapter->tx_timeout_factor * HZ)) && + !(er32(STATUS) & E1000_STATUS_TXOFF)) { tx_desc = IGBVF_TX_DESC_ADV(*tx_ring, i); /* detected Tx unit hang */ @@ -1049,7 +1053,7 @@ static int igbvf_request_msix(struct igbvf_adapter *adapter) } err = request_irq(adapter->msix_entries[vector].vector, - &igbvf_intr_msix_tx, 0, adapter->tx_ring->name, + igbvf_intr_msix_tx, 0, adapter->tx_ring->name, netdev); if (err) goto out; @@ -1059,7 +1063,7 @@ static int igbvf_request_msix(struct igbvf_adapter *adapter) vector++; err = request_irq(adapter->msix_entries[vector].vector, - &igbvf_intr_msix_rx, 0, adapter->rx_ring->name, + igbvf_intr_msix_rx, 0, adapter->rx_ring->name, netdev); if (err) goto out; @@ -1069,7 +1073,7 @@ static int igbvf_request_msix(struct igbvf_adapter *adapter) vector++; err = request_irq(adapter->msix_entries[vector].vector, - &igbvf_msix_other, 0, netdev->name, netdev); + igbvf_msix_other, 0, netdev->name, netdev); if (err) goto out; @@ -2094,27 +2098,24 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter, unsigned int first) { struct igbvf_buffer *buffer_info; + struct pci_dev *pdev = adapter->pdev; unsigned int len = skb_headlen(skb); unsigned int count = 0, i; unsigned int f; - dma_addr_t *map; i = tx_ring->next_to_use; - if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) { - dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); - return 0; - } - - map = skb_shinfo(skb)->dma_maps; - buffer_info = &tx_ring->buffer_info[i]; BUG_ON(len >= IGBVF_MAX_DATA_PER_TXD); buffer_info->length = len; /* set time_stamp *before* dma to help avoid a possible race */ buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; - buffer_info->dma = skb_shinfo(skb)->dma_head; + buffer_info->dma = pci_map_single(pdev, skb->data, len, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; + for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) { struct skb_frag_struct *frag; @@ -2131,14 +2132,44 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter, buffer_info->length = len; buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; - buffer_info->dma = map[count]; + buffer_info->mapped_as_page = true; + buffer_info->dma = pci_map_page(pdev, + frag->page, + frag->page_offset, + len, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; count++; } tx_ring->buffer_info[i].skb = skb; tx_ring->buffer_info[first].next_to_watch = i; - return count + 1; + return ++count; + +dma_error: + dev_err(&pdev->dev, "TX DMA map failed\n"); + + /* clear timestamp and dma mappings for failed buffer_info mapping */ + buffer_info->dma = 0; + buffer_info->time_stamp = 0; + buffer_info->length = 0; + buffer_info->next_to_watch = 0; + buffer_info->mapped_as_page = false; + count--; + + /* clear timestamp and dma mappings for remaining portion of packet */ + while (count >= 0) { + count--; + i--; + if (i < 0) + i += tx_ring->count; + buffer_info = &tx_ring->buffer_info[i]; + igbvf_put_txbuf(adapter, buffer_info); + } + + return 0; } static inline void igbvf_tx_queue_adv(struct igbvf_adapter *adapter, diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index 9f7b5d4172b8..ba8d246d05a0 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -738,17 +738,12 @@ static int ipg_get_rxbuff(struct net_device *dev, int entry) IPG_DEBUG_MSG("_get_rxbuff\n"); - skb = netdev_alloc_skb(dev, sp->rxsupport_size + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(dev, sp->rxsupport_size); if (!skb) { sp->rx_buff[entry] = NULL; return -ENOMEM; } - /* Adjust the data start location within the buffer to - * align IP address field to a 16 byte boundary. - */ - skb_reserve(skb, NET_IP_ALIGN); - /* Associate the receive buffer with the IPG NIC. */ skb->dev = dev; @@ -1756,7 +1751,7 @@ static int ipg_nic_open(struct net_device *dev) /* Register the interrupt line to be used by the IPG within * the Linux system. */ - rc = request_irq(pdev->irq, &ipg_interrupt_handler, IRQF_SHARED, + rc = request_irq(pdev->irq, ipg_interrupt_handler, IRQF_SHARED, dev->name, dev); if (rc < 0) { printk(KERN_INFO "%s: Error when requesting interrupt.\n", diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c index eb424681202d..9b2eebdbb25b 100644 --- a/drivers/net/irda/au1k_ir.c +++ b/drivers/net/irda/au1k_ir.c @@ -353,13 +353,13 @@ static int au1k_irda_start(struct net_device *dev) return retval; } - if ((retval = request_irq(AU1000_IRDA_TX_INT, &au1k_irda_interrupt, + if ((retval = request_irq(AU1000_IRDA_TX_INT, au1k_irda_interrupt, 0, dev->name, dev))) { printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); return retval; } - if ((retval = request_irq(AU1000_IRDA_RX_INT, &au1k_irda_interrupt, + if ((retval = request_irq(AU1000_IRDA_RX_INT, au1k_irda_interrupt, 0, dev->name, dev))) { free_irq(AU1000_IRDA_TX_INT, dev); printk(KERN_ERR "%s: unable to get IRQ %d\n", diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 215adf6377d0..e8e33bb9d876 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -852,7 +852,7 @@ static void irda_usb_receive(struct urb *urb) * hot unplug of the dongle... * Lowest effective timer is 10ms... * Jean II */ - self->rx_defer_timer.function = &irda_usb_rx_defer_expired; + self->rx_defer_timer.function = irda_usb_rx_defer_expired; self->rx_defer_timer.data = (unsigned long) urb; mod_timer(&self->rx_defer_timer, jiffies + (10 * HZ / 1000)); return; @@ -1124,11 +1124,11 @@ static int stir421x_patch_device(struct irda_usb_cb *self) * The actual image starts after the "STMP" keyword * so forward to the firmware header tag */ - for (i = 0; (fw->data[i] != STIR421X_PATCH_END_OF_HDR_TAG) - && (i < fw->size); i++) ; + for (i = 0; (fw->data[i] != STIR421X_PATCH_END_OF_HDR_TAG) && + (i < fw->size); i++) ; /* here we check for the out of buffer case */ - if ((STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i]) - && (i < STIR421X_PATCH_CODE_OFFSET)) { + if ((STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i]) && + (i < STIR421X_PATCH_CODE_OFFSET)) { if (!memcmp(fw->data + i + 1, STIR421X_PATCH_STMP_TAG, sizeof(STIR421X_PATCH_STMP_TAG) - 1)) { diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index 1e8dd8c74a64..8f7d0d146f24 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -115,7 +115,7 @@ struct smsc_ircc_subsystem_configuration { unsigned short vendor; /* PCI vendor ID */ unsigned short device; /* PCI vendor ID */ unsigned short subvendor; /* PCI subsystem vendor ID */ - unsigned short subdevice; /* PCI sybsystem device ID */ + unsigned short subdevice; /* PCI subsystem device ID */ unsigned short sir_io; /* I/O port for SIR */ unsigned short fir_io; /* I/O port for FIR */ unsigned char fir_irq; /* FIR IRQ */ diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 528767dec9d7..e5698fa30a4f 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -612,16 +612,16 @@ static int fifo_txwait(struct stir_cb *stir, int space) pr_debug("fifo status 0x%lx count %lu\n", status, count); /* is fifo receiving already, or empty */ - if (!(status & FIFOCTL_DIR) - || (status & FIFOCTL_EMPTY)) + if (!(status & FIFOCTL_DIR) || + (status & FIFOCTL_EMPTY)) return 0; if (signal_pending(current)) return -EINTR; /* shutting down? */ - if (!netif_running(stir->netdev) - || !netif_device_present(stir->netdev)) + if (!netif_running(stir->netdev) || + !netif_device_present(stir->netdev)) return -ESHUTDOWN; /* only waiting for some space */ @@ -776,8 +776,8 @@ static int stir_transmit_thread(void *arg) } /* nothing to send? start receiving */ - if (!stir->receiving - && irda_device_txqueue_empty(dev)) { + if (!stir->receiving && + irda_device_txqueue_empty(dev)) { /* Wait otherwise chip gets confused. */ if (fifo_txwait(stir, -1)) break; diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index a5ca71cec028..fddb4efd5453 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -1185,8 +1185,8 @@ F01_E */ * if frame size,data ptr,or skb ptr are wrong ,the get next * entry. */ - if ((skb == NULL) || (skb->data == NULL) - || (self->rx_buff.data == NULL) || (len < 6)) { + if ((skb == NULL) || (skb->data == NULL) || + (self->rx_buff.data == NULL) || (len < 6)) { self->netdev->stats.rx_dropped++; return TRUE; } @@ -1284,8 +1284,8 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase) self->RetryCount++; if ((self->RetryCount >= 1) || - ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize) - || (st_fifo->len >= (MAX_RX_WINDOW))) { + ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize) || + (st_fifo->len >= (MAX_RX_WINDOW))) { while (st_fifo->len > 0) { //upload frame // Put this entry back in fifo if (st_fifo->head > MAX_RX_WINDOW) @@ -1300,8 +1300,8 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase) * if frame size, data ptr, or skb ptr are wrong, * then get next entry. */ - if ((skb == NULL) || (skb->data == NULL) - || (self->rx_buff.data == NULL) || (len < 6)) { + if ((skb == NULL) || (skb->data == NULL) || + (self->rx_buff.data == NULL) || (len < 6)) { self->netdev->stats.rx_dropped++; continue; } @@ -1332,8 +1332,8 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase) * if frame is receive complete at this routine ,then upload * frame. */ - if ((GetRXStatus(iobase) & 0x10) - && (RxCurCount(iobase, self) != self->RxLastCount)) { + if ((GetRXStatus(iobase) & 0x10) && + (RxCurCount(iobase, self) != self->RxLastCount)) { upload_rxdata(self, iobase); if (irda_device_txqueue_empty(self->netdev)) via_ircc_dma_receive(self); diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index 7cfb8b6593c6..bd3c6b5ee76a 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -431,8 +431,8 @@ static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr memset(rd, 0, sizeof(*rd)); rd->hw = hwmap + i; rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA); - if (rd->buf == NULL - || !(busaddr = pci_map_single(pdev, rd->buf, len, dir))) { + if (rd->buf == NULL || + !(busaddr = pci_map_single(pdev, rd->buf, len, dir))) { if (rd->buf) { IRDA_ERROR("%s: failed to create PCI-MAP for %p", __func__, rd->buf); @@ -955,8 +955,8 @@ static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb, } for(;;) { do_gettimeofday(&now); - if (now.tv_sec > ready.tv_sec - || (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec)) + if (now.tv_sec > ready.tv_sec || + (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec)) break; udelay(100); /* must not sleep here - called under netif_tx_lock! */ @@ -1594,8 +1594,8 @@ static int vlsi_irda_init(struct net_device *ndev) * see include file for details why we need these 2 masks, in this order! */ - if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW) - || pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) { + if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW) || + pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) { IRDA_ERROR("%s: aborting due to PCI BM-DMA address limitations\n", __func__); return -1; } @@ -1641,8 +1641,8 @@ vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id) IRDA_MESSAGE("%s: IrDA PCI controller %s detected\n", drivername, pci_name(pdev)); - if ( !pci_resource_start(pdev,0) - || !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) { + if ( !pci_resource_start(pdev,0) || + !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) { IRDA_ERROR("%s: bar 0 invalid", __func__); goto out_disable; } diff --git a/drivers/net/isa-skeleton.c b/drivers/net/isa-skeleton.c index 9706e64e367b..04d0502726c0 100644 --- a/drivers/net/isa-skeleton.c +++ b/drivers/net/isa-skeleton.c @@ -214,9 +214,9 @@ static int __init netcard_probe1(struct net_device *dev, int ioaddr) * contains the manufacturer's unique code. That might be a good probe * method. Ideally you would add additional checks. */ - if (inb(ioaddr + 0) != SA_ADDR0 - || inb(ioaddr + 1) != SA_ADDR1 - || inb(ioaddr + 2) != SA_ADDR2) + if (inb(ioaddr + 0) != SA_ADDR0 || + inb(ioaddr + 1) != SA_ADDR1 || + inb(ioaddr + 2) != SA_ADDR2) goto out; if (net_debug && version_printed++ == 0) @@ -260,7 +260,7 @@ static int __init netcard_probe1(struct net_device *dev, int ioaddr) dev->irq = 9; { - int irqval = request_irq(dev->irq, &net_interrupt, 0, cardname, dev); + int irqval = request_irq(dev->irq, net_interrupt, 0, cardname, dev); if (irqval) { printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval); @@ -378,7 +378,7 @@ net_open(struct net_device *dev) * This is used if the interrupt line can turned off (shared). * See 3c503.c for an example of selecting the IRQ at config-time. */ - if (request_irq(dev->irq, &net_interrupt, 0, cardname, dev)) { + if (request_irq(dev->irq, net_interrupt, 0, cardname, dev)) { return -EAGAIN; } /* diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index aa7286bc4364..16c91910d6c1 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -604,10 +604,10 @@ static int veth_process_caps(struct veth_lpar_connection *cnx) /* Convert timer to jiffies */ cnx->ack_timeout = remote_caps->ack_timeout * HZ / 1000000; - if ( (remote_caps->num_buffers == 0) - || (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG) - || (remote_caps->ack_threshold == 0) - || (cnx->ack_timeout == 0) ) { + if ( (remote_caps->num_buffers == 0) || + (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG) || + (remote_caps->ack_threshold == 0) || + (cnx->ack_timeout == 0) ) { veth_error("Received incompatible capabilities from LPAR %d.\n", cnx->remote_lp); return HvLpEvent_Rc_InvalidSubtypeData; @@ -714,8 +714,8 @@ static void veth_statemachine(struct work_struct *work) cnx->state |= VETH_STATE_OPEN; } - if ( (cnx->state & VETH_STATE_OPEN) - && !(cnx->state & VETH_STATE_SENTMON) ) { + if ( (cnx->state & VETH_STATE_OPEN) && + !(cnx->state & VETH_STATE_SENTMON) ) { rc = veth_signalevent(cnx, VETH_EVENT_MONITOR, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_DeferredAck, @@ -724,8 +724,8 @@ static void veth_statemachine(struct work_struct *work) if (rc == HvLpEvent_Rc_Good) { cnx->state |= VETH_STATE_SENTMON; } else { - if ( (rc != HvLpEvent_Rc_PartitionDead) - && (rc != HvLpEvent_Rc_PathClosed) ) + if ( (rc != HvLpEvent_Rc_PartitionDead) && + (rc != HvLpEvent_Rc_PathClosed) ) veth_error("Error sending monitor to LPAR %d, " "rc = %d\n", rlp, rc); @@ -735,8 +735,8 @@ static void veth_statemachine(struct work_struct *work) } } - if ( (cnx->state & VETH_STATE_OPEN) - && !(cnx->state & VETH_STATE_SENTCAPS)) { + if ( (cnx->state & VETH_STATE_OPEN) && + !(cnx->state & VETH_STATE_SENTCAPS)) { u64 *rawcap = (u64 *)&cnx->local_caps; rc = veth_signalevent(cnx, VETH_EVENT_CAP, @@ -748,8 +748,8 @@ static void veth_statemachine(struct work_struct *work) if (rc == HvLpEvent_Rc_Good) { cnx->state |= VETH_STATE_SENTCAPS; } else { - if ( (rc != HvLpEvent_Rc_PartitionDead) - && (rc != HvLpEvent_Rc_PathClosed) ) + if ( (rc != HvLpEvent_Rc_PartitionDead) && + (rc != HvLpEvent_Rc_PathClosed) ) veth_error("Error sending caps to LPAR %d, " "rc = %d\n", rlp, rc); @@ -759,8 +759,8 @@ static void veth_statemachine(struct work_struct *work) } } - if ((cnx->state & VETH_STATE_GOTCAPS) - && !(cnx->state & VETH_STATE_SENTCAPACK)) { + if ((cnx->state & VETH_STATE_GOTCAPS) && + !(cnx->state & VETH_STATE_SENTCAPACK)) { struct veth_cap_data *remote_caps = &cnx->remote_caps; memcpy(remote_caps, &cnx->cap_event.u.caps_data, @@ -783,9 +783,9 @@ static void veth_statemachine(struct work_struct *work) goto cant_cope; } - if ((cnx->state & VETH_STATE_GOTCAPACK) - && (cnx->state & VETH_STATE_GOTCAPS) - && !(cnx->state & VETH_STATE_READY)) { + if ((cnx->state & VETH_STATE_GOTCAPACK) && + (cnx->state & VETH_STATE_GOTCAPS) && + !(cnx->state & VETH_STATE_READY)) { if (cnx->cap_ack_event.base_event.xRc == HvLpEvent_Rc_Good) { /* Start the ACK timer */ cnx->ack_timer.expires = jiffies + cnx->ack_timeout; @@ -818,8 +818,8 @@ static int veth_init_connection(u8 rlp) struct veth_msg *msgs; int i; - if ( (rlp == this_lp) - || ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) ) + if ( (rlp == this_lp) || + ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) ) return 0; cnx = kzalloc(sizeof(*cnx), GFP_KERNEL); @@ -1384,7 +1384,7 @@ static inline void veth_build_dma_list(struct dma_chunk *list, unsigned long done; int i = 1; - /* FIXME: skbs are continguous in real addresses. Do we + /* FIXME: skbs are contiguous in real addresses. Do we * really need to break it into PAGE_SIZE chunks, or can we do * it just at the granularity of iSeries real->absolute * mapping? Indeed, given the way the allocator works, can we @@ -1538,8 +1538,8 @@ static void veth_receive(struct veth_lpar_connection *cnx, cnx->pending_acks[cnx->num_pending_acks++] = event->base_event.xCorrelationToken; - if ( (cnx->num_pending_acks >= cnx->remote_caps.ack_threshold) - || (cnx->num_pending_acks >= VETH_MAX_ACKS_PER_MSG) ) + if ( (cnx->num_pending_acks >= cnx->remote_caps.ack_threshold) || + (cnx->num_pending_acks >= VETH_MAX_ACKS_PER_MSG) ) veth_flush_acks(cnx); spin_unlock_irqrestore(&cnx->lock, flags); diff --git a/drivers/net/ixgb/ixgb.h b/drivers/net/ixgb/ixgb.h index d85717e3022a..5257ae08b9f9 100644 --- a/drivers/net/ixgb/ixgb.h +++ b/drivers/net/ixgb/ixgb.h @@ -117,6 +117,7 @@ struct ixgb_buffer { unsigned long time_stamp; u16 length; u16 next_to_watch; + u16 mapped_as_page; }; struct ixgb_desc_ring { @@ -183,7 +184,6 @@ struct ixgb_adapter { struct napi_struct napi; struct net_device *netdev; struct pci_dev *pdev; - struct net_device_stats net_stats; /* structs defined in ixgb_hw.h */ struct ixgb_hw hw; diff --git a/drivers/net/ixgb/ixgb_ethtool.c b/drivers/net/ixgb/ixgb_ethtool.c index 288ee1d0f431..a4ed96caae69 100644 --- a/drivers/net/ixgb/ixgb_ethtool.c +++ b/drivers/net/ixgb/ixgb_ethtool.c @@ -34,38 +34,46 @@ #define IXGB_ALL_RAR_ENTRIES 16 +enum {NETDEV_STATS, IXGB_STATS}; + struct ixgb_stats { char stat_string[ETH_GSTRING_LEN]; + int type; int sizeof_stat; int stat_offset; }; -#define IXGB_STAT(m) FIELD_SIZEOF(struct ixgb_adapter, m), \ - offsetof(struct ixgb_adapter, m) +#define IXGB_STAT(m) IXGB_STATS, \ + FIELD_SIZEOF(struct ixgb_adapter, m), \ + offsetof(struct ixgb_adapter, m) +#define IXGB_NETDEV_STAT(m) NETDEV_STATS, \ + FIELD_SIZEOF(struct net_device, m), \ + offsetof(struct net_device, m) + static struct ixgb_stats ixgb_gstrings_stats[] = { - {"rx_packets", IXGB_STAT(net_stats.rx_packets)}, - {"tx_packets", IXGB_STAT(net_stats.tx_packets)}, - {"rx_bytes", IXGB_STAT(net_stats.rx_bytes)}, - {"tx_bytes", IXGB_STAT(net_stats.tx_bytes)}, - {"rx_errors", IXGB_STAT(net_stats.rx_errors)}, - {"tx_errors", IXGB_STAT(net_stats.tx_errors)}, - {"rx_dropped", IXGB_STAT(net_stats.rx_dropped)}, - {"tx_dropped", IXGB_STAT(net_stats.tx_dropped)}, - {"multicast", IXGB_STAT(net_stats.multicast)}, - {"collisions", IXGB_STAT(net_stats.collisions)}, - -/* { "rx_length_errors", IXGB_STAT(net_stats.rx_length_errors) }, */ - {"rx_over_errors", IXGB_STAT(net_stats.rx_over_errors)}, - {"rx_crc_errors", IXGB_STAT(net_stats.rx_crc_errors)}, - {"rx_frame_errors", IXGB_STAT(net_stats.rx_frame_errors)}, + {"rx_packets", IXGB_NETDEV_STAT(stats.rx_packets)}, + {"tx_packets", IXGB_NETDEV_STAT(stats.tx_packets)}, + {"rx_bytes", IXGB_NETDEV_STAT(stats.rx_bytes)}, + {"tx_bytes", IXGB_NETDEV_STAT(stats.tx_bytes)}, + {"rx_errors", IXGB_NETDEV_STAT(stats.rx_errors)}, + {"tx_errors", IXGB_NETDEV_STAT(stats.tx_errors)}, + {"rx_dropped", IXGB_NETDEV_STAT(stats.rx_dropped)}, + {"tx_dropped", IXGB_NETDEV_STAT(stats.tx_dropped)}, + {"multicast", IXGB_NETDEV_STAT(stats.multicast)}, + {"collisions", IXGB_NETDEV_STAT(stats.collisions)}, + +/* { "rx_length_errors", IXGB_NETDEV_STAT(stats.rx_length_errors) }, */ + {"rx_over_errors", IXGB_NETDEV_STAT(stats.rx_over_errors)}, + {"rx_crc_errors", IXGB_NETDEV_STAT(stats.rx_crc_errors)}, + {"rx_frame_errors", IXGB_NETDEV_STAT(stats.rx_frame_errors)}, {"rx_no_buffer_count", IXGB_STAT(stats.rnbc)}, - {"rx_fifo_errors", IXGB_STAT(net_stats.rx_fifo_errors)}, - {"rx_missed_errors", IXGB_STAT(net_stats.rx_missed_errors)}, - {"tx_aborted_errors", IXGB_STAT(net_stats.tx_aborted_errors)}, - {"tx_carrier_errors", IXGB_STAT(net_stats.tx_carrier_errors)}, - {"tx_fifo_errors", IXGB_STAT(net_stats.tx_fifo_errors)}, - {"tx_heartbeat_errors", IXGB_STAT(net_stats.tx_heartbeat_errors)}, - {"tx_window_errors", IXGB_STAT(net_stats.tx_window_errors)}, + {"rx_fifo_errors", IXGB_NETDEV_STAT(stats.rx_fifo_errors)}, + {"rx_missed_errors", IXGB_NETDEV_STAT(stats.rx_missed_errors)}, + {"tx_aborted_errors", IXGB_NETDEV_STAT(stats.tx_aborted_errors)}, + {"tx_carrier_errors", IXGB_NETDEV_STAT(stats.tx_carrier_errors)}, + {"tx_fifo_errors", IXGB_NETDEV_STAT(stats.tx_fifo_errors)}, + {"tx_heartbeat_errors", IXGB_NETDEV_STAT(stats.tx_heartbeat_errors)}, + {"tx_window_errors", IXGB_NETDEV_STAT(stats.tx_window_errors)}, {"tx_deferred_ok", IXGB_STAT(stats.dc)}, {"tx_timeout_count", IXGB_STAT(tx_timeout_count) }, {"tx_restart_queue", IXGB_STAT(restart_queue) }, @@ -662,10 +670,21 @@ ixgb_get_ethtool_stats(struct net_device *netdev, { struct ixgb_adapter *adapter = netdev_priv(netdev); int i; + char *p = NULL; ixgb_update_stats(adapter); for (i = 0; i < IXGB_STATS_LEN; i++) { - char *p = (char *)adapter+ixgb_gstrings_stats[i].stat_offset; + switch (ixgb_gstrings_stats[i].type) { + case NETDEV_STATS: + p = (char *) netdev + + ixgb_gstrings_stats[i].stat_offset; + break; + case IXGB_STATS: + p = (char *) adapter + + ixgb_gstrings_stats[i].stat_offset; + break; + } + data[i] = (ixgb_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c index 8aa44dca57eb..bcd0f01d5feb 100644 --- a/drivers/net/ixgb/ixgb_main.c +++ b/drivers/net/ixgb/ixgb_main.c @@ -233,7 +233,7 @@ ixgb_up(struct ixgb_adapter *adapter) /* proceed to try to request regular interrupt */ } - err = request_irq(adapter->pdev->irq, &ixgb_intr, irq_flags, + err = request_irq(adapter->pdev->irq, ixgb_intr, irq_flags, netdev->name, netdev); if (err) { if (adapter->have_msi) @@ -892,10 +892,18 @@ static void ixgb_unmap_and_free_tx_resource(struct ixgb_adapter *adapter, struct ixgb_buffer *buffer_info) { - buffer_info->dma = 0; + if (buffer_info->dma) { + if (buffer_info->mapped_as_page) + pci_unmap_page(adapter->pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + else + pci_unmap_single(adapter->pdev, buffer_info->dma, + buffer_info->length, + PCI_DMA_TODEVICE); + buffer_info->dma = 0; + } + if (buffer_info->skb) { - skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb, - DMA_TO_DEVICE); dev_kfree_skb_any(buffer_info->skb); buffer_info->skb = NULL; } @@ -1272,24 +1280,16 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb, unsigned int first) { struct ixgb_desc_ring *tx_ring = &adapter->tx_ring; + struct pci_dev *pdev = adapter->pdev; struct ixgb_buffer *buffer_info; int len = skb_headlen(skb); unsigned int offset = 0, size, count = 0, i; unsigned int mss = skb_shinfo(skb)->gso_size; - unsigned int nr_frags = skb_shinfo(skb)->nr_frags; unsigned int f; - dma_addr_t *map; i = tx_ring->next_to_use; - if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) { - dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); - return 0; - } - - map = skb_shinfo(skb)->dma_maps; - while (len) { buffer_info = &tx_ring->buffer_info[i]; size = min(len, IXGB_MAX_DATA_PER_TXD); @@ -1301,11 +1301,11 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb, buffer_info->length = size; WARN_ON(buffer_info->dma != 0); buffer_info->time_stamp = jiffies; - buffer_info->dma = skb_shinfo(skb)->dma_head + offset; - pci_map_single(adapter->pdev, - skb->data + offset, - size, - PCI_DMA_TODEVICE); + buffer_info->mapped_as_page = false; + buffer_info->dma = pci_map_single(pdev, skb->data + offset, + size, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; buffer_info->next_to_watch = 0; len -= size; @@ -1323,7 +1323,7 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb, frag = &skb_shinfo(skb)->frags[f]; len = frag->size; - offset = 0; + offset = frag->page_offset; while (len) { i++; @@ -1341,7 +1341,13 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb, buffer_info->length = size; buffer_info->time_stamp = jiffies; - buffer_info->dma = map[f] + offset; + buffer_info->mapped_as_page = true; + buffer_info->dma = + pci_map_page(pdev, frag->page, + offset, size, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, buffer_info->dma)) + goto dma_error; buffer_info->next_to_watch = 0; len -= size; @@ -1353,6 +1359,22 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb, tx_ring->buffer_info[first].next_to_watch = i; return count; + +dma_error: + dev_err(&pdev->dev, "TX DMA map failed\n"); + buffer_info->dma = 0; + count--; + + while (count >= 0) { + count--; + i--; + if (i < 0) + i += tx_ring->count; + buffer_info = &tx_ring->buffer_info[i]; + ixgb_unmap_and_free_tx_resource(adapter, buffer_info); + } + + return 0; } static void @@ -1537,9 +1559,7 @@ ixgb_tx_timeout_task(struct work_struct *work) static struct net_device_stats * ixgb_get_stats(struct net_device *netdev) { - struct ixgb_adapter *adapter = netdev_priv(netdev); - - return &adapter->net_stats; + return &netdev->stats; } /** @@ -1676,16 +1696,16 @@ ixgb_update_stats(struct ixgb_adapter *adapter) /* Fill out the OS statistics structure */ - adapter->net_stats.rx_packets = adapter->stats.gprcl; - adapter->net_stats.tx_packets = adapter->stats.gptcl; - adapter->net_stats.rx_bytes = adapter->stats.gorcl; - adapter->net_stats.tx_bytes = adapter->stats.gotcl; - adapter->net_stats.multicast = adapter->stats.mprcl; - adapter->net_stats.collisions = 0; + netdev->stats.rx_packets = adapter->stats.gprcl; + netdev->stats.tx_packets = adapter->stats.gptcl; + netdev->stats.rx_bytes = adapter->stats.gorcl; + netdev->stats.tx_bytes = adapter->stats.gotcl; + netdev->stats.multicast = adapter->stats.mprcl; + netdev->stats.collisions = 0; /* ignore RLEC as it reports errors for padded (<64bytes) frames * with a length in the type/len field */ - adapter->net_stats.rx_errors = + netdev->stats.rx_errors = /* adapter->stats.rnbc + */ adapter->stats.crcerrs + adapter->stats.ruc + adapter->stats.roc /*+ adapter->stats.rlec */ + @@ -1693,21 +1713,21 @@ ixgb_update_stats(struct ixgb_adapter *adapter) adapter->stats.ecbc + adapter->stats.mpc; /* see above - * adapter->net_stats.rx_length_errors = adapter->stats.rlec; + * netdev->stats.rx_length_errors = adapter->stats.rlec; */ - adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs; - adapter->net_stats.rx_fifo_errors = adapter->stats.mpc; - adapter->net_stats.rx_missed_errors = adapter->stats.mpc; - adapter->net_stats.rx_over_errors = adapter->stats.mpc; - - adapter->net_stats.tx_errors = 0; - adapter->net_stats.rx_frame_errors = 0; - adapter->net_stats.tx_aborted_errors = 0; - adapter->net_stats.tx_carrier_errors = 0; - adapter->net_stats.tx_fifo_errors = 0; - adapter->net_stats.tx_heartbeat_errors = 0; - adapter->net_stats.tx_window_errors = 0; + netdev->stats.rx_crc_errors = adapter->stats.crcerrs; + netdev->stats.rx_fifo_errors = adapter->stats.mpc; + netdev->stats.rx_missed_errors = adapter->stats.mpc; + netdev->stats.rx_over_errors = adapter->stats.mpc; + + netdev->stats.tx_errors = 0; + netdev->stats.rx_frame_errors = 0; + netdev->stats.tx_aborted_errors = 0; + netdev->stats.tx_carrier_errors = 0; + netdev->stats.tx_fifo_errors = 0; + netdev->stats.tx_heartbeat_errors = 0; + netdev->stats.tx_window_errors = 0; } #define IXGB_MAX_INTR 10 @@ -1974,9 +1994,8 @@ ixgb_clean_rx_irq(struct ixgb_adapter *adapter, int *work_done, int work_to_do) * of reassembly being done in the stack */ if (length < copybreak) { struct sk_buff *new_skb = - netdev_alloc_skb(netdev, length + NET_IP_ALIGN); + netdev_alloc_skb_ip_align(netdev, length); if (new_skb) { - skb_reserve(new_skb, NET_IP_ALIGN); skb_copy_to_linear_data_offset(new_skb, -NET_IP_ALIGN, (skb->data - @@ -2059,20 +2078,13 @@ ixgb_alloc_rx_buffers(struct ixgb_adapter *adapter, int cleaned_count) goto map_skb; } - skb = netdev_alloc_skb(netdev, adapter->rx_buffer_len - + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(netdev, adapter->rx_buffer_len); if (unlikely(!skb)) { /* Better luck next round */ adapter->alloc_rx_buff_failed++; break; } - /* Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); - buffer_info->skb = skb; buffer_info->length = adapter->rx_buffer_len; map_skb: diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index 385be6016667..8da8eb535084 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -51,11 +51,11 @@ __func__ , ## args))) /* TX/RX descriptor defines */ -#define IXGBE_DEFAULT_TXD 1024 +#define IXGBE_DEFAULT_TXD 512 #define IXGBE_MAX_TXD 4096 #define IXGBE_MIN_TXD 64 -#define IXGBE_DEFAULT_RXD 1024 +#define IXGBE_DEFAULT_RXD 512 #define IXGBE_MAX_RXD 4096 #define IXGBE_MIN_RXD 64 @@ -106,6 +106,7 @@ struct ixgbe_tx_buffer { unsigned long time_stamp; u16 length; u16 next_to_watch; + u16 mapped_as_page; }; struct ixgbe_rx_buffer { @@ -159,10 +160,13 @@ struct ixgbe_ring { struct ixgbe_queue_stats stats; unsigned long reinit_state; u64 rsc_count; /* stat for coalesced packets */ + u64 rsc_flush; /* stats for flushed packets */ + u32 restart_queue; /* track tx queue restarts */ + u32 non_eop_descs; /* track hardware descriptor chaining */ unsigned int size; /* length in bytes */ dma_addr_t dma; /* phys. address of descriptor ring */ -}; +} ____cacheline_internodealigned_in_smp; enum ixgbe_ring_f_enum { RING_F_NONE = 0, @@ -187,7 +191,7 @@ enum ixgbe_ring_f_enum { struct ixgbe_ring_feature { int indices; int mask; -}; +} ____cacheline_internodealigned_in_smp; #define MAX_RX_QUEUES 128 #define MAX_TX_QUEUES 128 @@ -273,29 +277,25 @@ struct ixgbe_adapter { u16 eitr_high; /* TX */ - struct ixgbe_ring *tx_ring; /* One per active queue */ + struct ixgbe_ring *tx_ring ____cacheline_aligned_in_smp; /* One per active queue */ int num_tx_queues; - u64 restart_queue; - u64 hw_csum_tx_good; - u64 lsc_int; - u64 hw_tso_ctxt; - u64 hw_tso6_ctxt; u32 tx_timeout_count; bool detect_tx_hung; + u64 restart_queue; + u64 lsc_int; + /* RX */ - struct ixgbe_ring *rx_ring; /* One per active queue */ + struct ixgbe_ring *rx_ring ____cacheline_aligned_in_smp; /* One per active queue */ int num_rx_queues; u64 hw_csum_rx_error; u64 hw_rx_no_dma_resources; - u64 hw_csum_rx_good; u64 non_eop_descs; int num_msix_vectors; int max_msix_q_vectors; /* true count of q_vectors for device */ struct ixgbe_ring_feature ring_feature[RING_F_ARRAY_SIZE]; struct msix_entry *msix_entries; - u64 rx_hdr_split; u32 alloc_rx_page_failed; u32 alloc_rx_buff_failed; @@ -340,7 +340,6 @@ struct ixgbe_adapter { /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; - struct net_device_stats net_stats; u32 test_icr; struct ixgbe_ring test_tx_ring; @@ -376,7 +375,8 @@ struct ixgbe_adapter { #ifdef IXGBE_FCOE struct ixgbe_fcoe fcoe; #endif /* IXGBE_FCOE */ - u64 rsc_count; + u64 rsc_total_count; + u64 rsc_total_flush; u32 wol; u16 eeprom_version; }; @@ -397,7 +397,7 @@ enum ixgbe_boards { extern struct ixgbe_info ixgbe_82598_info; extern struct ixgbe_info ixgbe_82599_info; #ifdef CONFIG_IXGBE_DCB -extern struct dcbnl_rtnl_ops dcbnl_ops; +extern const struct dcbnl_rtnl_ops dcbnl_ops; extern int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg, struct ixgbe_dcb_config *dst_dcb_cfg, int tc_max); @@ -458,6 +458,7 @@ extern int ixgbe_fcoe_disable(struct net_device *netdev); extern u8 ixgbe_fcoe_getapp(struct ixgbe_adapter *adapter); extern u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up); #endif /* CONFIG_IXGBE_DCB */ +extern int ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type); #endif /* IXGBE_FCOE */ #endif /* _IXGBE_H_ */ diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c index 34b04924c8a1..72106898a5cb 100644 --- a/drivers/net/ixgbe/ixgbe_82599.c +++ b/drivers/net/ixgbe/ixgbe_82599.c @@ -42,6 +42,10 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg, bool autoneg_wait_to_complete); +static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + bool autoneg, + bool autoneg_wait_to_complete); s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw, bool autoneg_wait_to_complete); s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, @@ -64,7 +68,13 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw) /* Set up dual speed SFP+ support */ mac->ops.setup_link = &ixgbe_setup_mac_link_multispeed_fiber; } else { - mac->ops.setup_link = &ixgbe_setup_mac_link_82599; + if ((mac->ops.get_media_type(hw) == + ixgbe_media_type_backplane) && + (hw->phy.smart_speed == ixgbe_smart_speed_auto || + hw->phy.smart_speed == ixgbe_smart_speed_on)) + mac->ops.setup_link = &ixgbe_setup_mac_link_smartspeed; + else + mac->ops.setup_link = &ixgbe_setup_mac_link_82599; } } @@ -337,6 +347,7 @@ static enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw) media_type = ixgbe_media_type_backplane; break; case IXGBE_DEV_ID_82599_SFP: + case IXGBE_DEV_ID_82599_SFP_EM: media_type = ixgbe_media_type_fiber; break; case IXGBE_DEV_ID_82599_CX4: @@ -479,7 +490,12 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, hw->mac.autotry_restart = false; } - /* The controller may take up to 500ms at 10g to acquire link */ + /* + * Wait for the controller to acquire link. Per IEEE 802.3ap, + * Section 73.10.2, we may have to wait up to 500ms if KR is + * attempted. 82599 uses the same timing for 10g SFI. + */ + for (i = 0; i < 5; i++) { /* Wait for the link partner to also set speed */ msleep(100); @@ -567,6 +583,111 @@ out: } /** + * ixgbe_setup_mac_link_smartspeed - Set MAC link speed using SmartSpeed + * @hw: pointer to hardware structure + * @speed: new link speed + * @autoneg: true if autonegotiation enabled + * @autoneg_wait_to_complete: true when waiting for completion is needed + * + * Implements the Intel SmartSpeed algorithm. + **/ +static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, + ixgbe_link_speed speed, bool autoneg, + bool autoneg_wait_to_complete) +{ + s32 status = 0; + ixgbe_link_speed link_speed; + s32 i, j; + bool link_up = false; + u32 autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC); + + hw_dbg(hw, "ixgbe_setup_mac_link_smartspeed.\n"); + + /* Set autoneg_advertised value based on input link speed */ + hw->phy.autoneg_advertised = 0; + + if (speed & IXGBE_LINK_SPEED_10GB_FULL) + hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL; + + if (speed & IXGBE_LINK_SPEED_1GB_FULL) + hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL; + + if (speed & IXGBE_LINK_SPEED_100_FULL) + hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_100_FULL; + + /* + * Implement Intel SmartSpeed algorithm. SmartSpeed will reduce the + * autoneg advertisement if link is unable to be established at the + * highest negotiated rate. This can sometimes happen due to integrity + * issues with the physical media connection. + */ + + /* First, try to get link with full advertisement */ + hw->phy.smart_speed_active = false; + for (j = 0; j < IXGBE_SMARTSPEED_MAX_RETRIES; j++) { + status = ixgbe_setup_mac_link_82599(hw, speed, autoneg, + autoneg_wait_to_complete); + if (status) + goto out; + + /* + * Wait for the controller to acquire link. Per IEEE 802.3ap, + * Section 73.10.2, we may have to wait up to 500ms if KR is + * attempted, or 200ms if KX/KX4/BX/BX4 is attempted, per + * Table 9 in the AN MAS. + */ + for (i = 0; i < 5; i++) { + mdelay(100); + + /* If we have link, just jump out */ + hw->mac.ops.check_link(hw, &link_speed, + &link_up, false); + if (link_up) + goto out; + } + } + + /* + * We didn't get link. If we advertised KR plus one of KX4/KX + * (or BX4/BX), then disable KR and try again. + */ + if (((autoc_reg & IXGBE_AUTOC_KR_SUPP) == 0) || + ((autoc_reg & IXGBE_AUTOC_KX4_KX_SUPP_MASK) == 0)) + goto out; + + /* Turn SmartSpeed on to disable KR support */ + hw->phy.smart_speed_active = true; + status = ixgbe_setup_mac_link_82599(hw, speed, autoneg, + autoneg_wait_to_complete); + if (status) + goto out; + + /* + * Wait for the controller to acquire link. 600ms will allow for + * the AN link_fail_inhibit_timer as well for multiple cycles of + * parallel detect, both 10g and 1g. This allows for the maximum + * connect attempts as defined in the AN MAS table 73-7. + */ + for (i = 0; i < 6; i++) { + mdelay(100); + + /* If we have link, just jump out */ + hw->mac.ops.check_link(hw, &link_speed, + &link_up, false); + if (link_up) + goto out; + } + + /* We didn't get link. Turn SmartSpeed back off. */ + hw->phy.smart_speed_active = false; + status = ixgbe_setup_mac_link_82599(hw, speed, autoneg, + autoneg_wait_to_complete); + +out: + return status; +} + +/** * ixgbe_check_mac_link_82599 - Determine link and speed status * @hw: pointer to hardware structure * @speed: pointer to link speed @@ -669,7 +790,8 @@ s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, if (speed & IXGBE_LINK_SPEED_10GB_FULL) if (orig_autoc & IXGBE_AUTOC_KX4_SUPP) autoc |= IXGBE_AUTOC_KX4_SUPP; - if (orig_autoc & IXGBE_AUTOC_KR_SUPP) + if ((orig_autoc & IXGBE_AUTOC_KR_SUPP) && + (hw->phy.smart_speed_active == false)) autoc |= IXGBE_AUTOC_KR_SUPP; if (speed & IXGBE_LINK_SPEED_1GB_FULL) autoc |= IXGBE_AUTOC_KX_SUPP; @@ -878,6 +1000,10 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw) hw->mac.num_rar_entries--; } + /* Store the alternative WWNN/WWPN prefix */ + hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix, + &hw->mac.wwpn_prefix); + reset_hw_out: return status; } @@ -2414,6 +2540,51 @@ fw_version_out: return status; } +/** + * ixgbe_get_wwn_prefix_82599 - Get alternative WWNN/WWPN prefix from + * the EEPROM + * @hw: pointer to hardware structure + * @wwnn_prefix: the alternative WWNN prefix + * @wwpn_prefix: the alternative WWPN prefix + * + * This function will read the EEPROM from the alternative SAN MAC address + * block to check the support for the alternative WWNN/WWPN prefix support. + **/ +static s32 ixgbe_get_wwn_prefix_82599(struct ixgbe_hw *hw, u16 *wwnn_prefix, + u16 *wwpn_prefix) +{ + u16 offset, caps; + u16 alt_san_mac_blk_offset; + + /* clear output first */ + *wwnn_prefix = 0xFFFF; + *wwpn_prefix = 0xFFFF; + + /* check if alternative SAN MAC is supported */ + hw->eeprom.ops.read(hw, IXGBE_ALT_SAN_MAC_ADDR_BLK_PTR, + &alt_san_mac_blk_offset); + + if ((alt_san_mac_blk_offset == 0) || + (alt_san_mac_blk_offset == 0xFFFF)) + goto wwn_prefix_out; + + /* check capability in alternative san mac address block */ + offset = alt_san_mac_blk_offset + IXGBE_ALT_SAN_MAC_ADDR_CAPS_OFFSET; + hw->eeprom.ops.read(hw, offset, &caps); + if (!(caps & IXGBE_ALT_SAN_MAC_ADDR_CAPS_ALTWWN)) + goto wwn_prefix_out; + + /* get the corresponding prefix for WWNN/WWPN */ + offset = alt_san_mac_blk_offset + IXGBE_ALT_SAN_MAC_ADDR_WWNN_OFFSET; + hw->eeprom.ops.read(hw, offset, wwnn_prefix); + + offset = alt_san_mac_blk_offset + IXGBE_ALT_SAN_MAC_ADDR_WWPN_OFFSET; + hw->eeprom.ops.read(hw, offset, wwpn_prefix); + +wwn_prefix_out: + return 0; +} + static struct ixgbe_mac_operations mac_ops_82599 = { .init_hw = &ixgbe_init_hw_generic, .reset_hw = &ixgbe_reset_hw_82599, @@ -2425,6 +2596,7 @@ static struct ixgbe_mac_operations mac_ops_82599 = { .get_mac_addr = &ixgbe_get_mac_addr_generic, .get_san_mac_addr = &ixgbe_get_san_mac_addr_82599, .get_device_caps = &ixgbe_get_device_caps_82599, + .get_wwn_prefix = &ixgbe_get_wwn_prefix_82599, .stop_adapter = &ixgbe_stop_adapter_generic, .get_bus_info = &ixgbe_get_bus_info_generic, .set_lan_id = &ixgbe_set_lan_id_multi_port_pcie, diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c index 40ff120a9ad4..688b8ca5da32 100644 --- a/drivers/net/ixgbe/ixgbe_common.c +++ b/drivers/net/ixgbe/ixgbe_common.c @@ -1382,10 +1382,10 @@ s32 ixgbe_update_uc_addr_list_generic(struct ixgbe_hw *hw, hw->addr_ctrl.overflow_promisc = 0; /* Zero out the other receive addresses */ - hw_dbg(hw, "Clearing RAR[1-%d]\n", uc_addr_in_use); - for (i = 1; i <= uc_addr_in_use; i++) { - IXGBE_WRITE_REG(hw, IXGBE_RAL(i), 0); - IXGBE_WRITE_REG(hw, IXGBE_RAH(i), 0); + hw_dbg(hw, "Clearing RAR[1-%d]\n", uc_addr_in_use + 1); + for (i = 0; i < uc_addr_in_use; i++) { + IXGBE_WRITE_REG(hw, IXGBE_RAL(1+i), 0); + IXGBE_WRITE_REG(hw, IXGBE_RAH(1+i), 0); } /* Add the new addresses */ @@ -1755,17 +1755,24 @@ s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw) /* * On backplane, bail out if * - backplane autoneg was not completed, or if - * - link partner is not AN enabled + * - we are 82599 and link partner is not AN enabled */ if (hw->phy.media_type == ixgbe_media_type_backplane) { links = IXGBE_READ_REG(hw, IXGBE_LINKS); - links2 = IXGBE_READ_REG(hw, IXGBE_LINKS2); - if (((links & IXGBE_LINKS_KX_AN_COMP) == 0) || - ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0)) { + if ((links & IXGBE_LINKS_KX_AN_COMP) == 0) { hw->fc.fc_was_autonegged = false; hw->fc.current_mode = hw->fc.requested_mode; goto out; } + + if (hw->mac.type == ixgbe_mac_82599EB) { + links2 = IXGBE_READ_REG(hw, IXGBE_LINKS2); + if ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0) { + hw->fc.fc_was_autonegged = false; + hw->fc.current_mode = hw->fc.requested_mode; + goto out; + } + } } /* @@ -1784,6 +1791,20 @@ s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw) } /* + * Bail out on + * - copper or CX4 adapters + * - fiber adapters running at 10gig + */ + if ((hw->phy.media_type == ixgbe_media_type_copper) || + (hw->phy.media_type == ixgbe_media_type_cx4) || + ((hw->phy.media_type == ixgbe_media_type_fiber) && + (speed == IXGBE_LINK_SPEED_10GB_FULL))) { + hw->fc.fc_was_autonegged = false; + hw->fc.current_mode = hw->fc.requested_mode; + goto out; + } + + /* * Read the AN advertisement and LP ability registers and resolve * local flow control settings accordingly */ diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ixgbe/ixgbe_dcb_nl.c index a6bc1ef28f92..3c7a79a7d7c6 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ixgbe/ixgbe_dcb_nl.c @@ -563,7 +563,7 @@ static u8 ixgbe_dcbnl_setapp(struct net_device *netdev, return rval; } -struct dcbnl_rtnl_ops dcbnl_ops = { +const struct dcbnl_rtnl_ops dcbnl_ops = { .getstate = ixgbe_dcbnl_get_state, .setstate = ixgbe_dcbnl_set_state, .getpermhwaddr = ixgbe_dcbnl_get_perm_hw_addr, diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 856c18c207f3..06a9d18bbdbc 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -40,19 +40,27 @@ #define IXGBE_ALL_RAR_ENTRIES 16 +enum {NETDEV_STATS, IXGBE_STATS}; + struct ixgbe_stats { char stat_string[ETH_GSTRING_LEN]; + int type; int sizeof_stat; int stat_offset; }; -#define IXGBE_STAT(m) sizeof(((struct ixgbe_adapter *)0)->m), \ - offsetof(struct ixgbe_adapter, m) +#define IXGBE_STAT(m) IXGBE_STATS, \ + sizeof(((struct ixgbe_adapter *)0)->m), \ + offsetof(struct ixgbe_adapter, m) +#define IXGBE_NETDEV_STAT(m) NETDEV_STATS, \ + sizeof(((struct net_device *)0)->m), \ + offsetof(struct net_device, m) + static struct ixgbe_stats ixgbe_gstrings_stats[] = { - {"rx_packets", IXGBE_STAT(net_stats.rx_packets)}, - {"tx_packets", IXGBE_STAT(net_stats.tx_packets)}, - {"rx_bytes", IXGBE_STAT(net_stats.rx_bytes)}, - {"tx_bytes", IXGBE_STAT(net_stats.tx_bytes)}, + {"rx_packets", IXGBE_NETDEV_STAT(stats.rx_packets)}, + {"tx_packets", IXGBE_NETDEV_STAT(stats.tx_packets)}, + {"rx_bytes", IXGBE_NETDEV_STAT(stats.rx_bytes)}, + {"tx_bytes", IXGBE_NETDEV_STAT(stats.tx_bytes)}, {"rx_pkts_nic", IXGBE_STAT(stats.gprc)}, {"tx_pkts_nic", IXGBE_STAT(stats.gptc)}, {"rx_bytes_nic", IXGBE_STAT(stats.gorc)}, @@ -60,40 +68,36 @@ static struct ixgbe_stats ixgbe_gstrings_stats[] = { {"lsc_int", IXGBE_STAT(lsc_int)}, {"tx_busy", IXGBE_STAT(tx_busy)}, {"non_eop_descs", IXGBE_STAT(non_eop_descs)}, - {"rx_errors", IXGBE_STAT(net_stats.rx_errors)}, - {"tx_errors", IXGBE_STAT(net_stats.tx_errors)}, - {"rx_dropped", IXGBE_STAT(net_stats.rx_dropped)}, - {"tx_dropped", IXGBE_STAT(net_stats.tx_dropped)}, - {"multicast", IXGBE_STAT(net_stats.multicast)}, + {"rx_errors", IXGBE_NETDEV_STAT(stats.rx_errors)}, + {"tx_errors", IXGBE_NETDEV_STAT(stats.tx_errors)}, + {"rx_dropped", IXGBE_NETDEV_STAT(stats.rx_dropped)}, + {"tx_dropped", IXGBE_NETDEV_STAT(stats.tx_dropped)}, + {"multicast", IXGBE_NETDEV_STAT(stats.multicast)}, {"broadcast", IXGBE_STAT(stats.bprc)}, {"rx_no_buffer_count", IXGBE_STAT(stats.rnbc[0]) }, - {"collisions", IXGBE_STAT(net_stats.collisions)}, - {"rx_over_errors", IXGBE_STAT(net_stats.rx_over_errors)}, - {"rx_crc_errors", IXGBE_STAT(net_stats.rx_crc_errors)}, - {"rx_frame_errors", IXGBE_STAT(net_stats.rx_frame_errors)}, - {"hw_rsc_count", IXGBE_STAT(rsc_count)}, + {"collisions", IXGBE_NETDEV_STAT(stats.collisions)}, + {"rx_over_errors", IXGBE_NETDEV_STAT(stats.rx_over_errors)}, + {"rx_crc_errors", IXGBE_NETDEV_STAT(stats.rx_crc_errors)}, + {"rx_frame_errors", IXGBE_NETDEV_STAT(stats.rx_frame_errors)}, + {"hw_rsc_aggregated", IXGBE_STAT(rsc_total_count)}, + {"hw_rsc_flushed", IXGBE_STAT(rsc_total_flush)}, {"fdir_match", IXGBE_STAT(stats.fdirmatch)}, {"fdir_miss", IXGBE_STAT(stats.fdirmiss)}, - {"rx_fifo_errors", IXGBE_STAT(net_stats.rx_fifo_errors)}, - {"rx_missed_errors", IXGBE_STAT(net_stats.rx_missed_errors)}, - {"tx_aborted_errors", IXGBE_STAT(net_stats.tx_aborted_errors)}, - {"tx_carrier_errors", IXGBE_STAT(net_stats.tx_carrier_errors)}, - {"tx_fifo_errors", IXGBE_STAT(net_stats.tx_fifo_errors)}, - {"tx_heartbeat_errors", IXGBE_STAT(net_stats.tx_heartbeat_errors)}, + {"rx_fifo_errors", IXGBE_NETDEV_STAT(stats.rx_fifo_errors)}, + {"rx_missed_errors", IXGBE_NETDEV_STAT(stats.rx_missed_errors)}, + {"tx_aborted_errors", IXGBE_NETDEV_STAT(stats.tx_aborted_errors)}, + {"tx_carrier_errors", IXGBE_NETDEV_STAT(stats.tx_carrier_errors)}, + {"tx_fifo_errors", IXGBE_NETDEV_STAT(stats.tx_fifo_errors)}, + {"tx_heartbeat_errors", IXGBE_NETDEV_STAT(stats.tx_heartbeat_errors)}, {"tx_timeout_count", IXGBE_STAT(tx_timeout_count)}, {"tx_restart_queue", IXGBE_STAT(restart_queue)}, {"rx_long_length_errors", IXGBE_STAT(stats.roc)}, {"rx_short_length_errors", IXGBE_STAT(stats.ruc)}, - {"tx_tcp4_seg_ctxt", IXGBE_STAT(hw_tso_ctxt)}, - {"tx_tcp6_seg_ctxt", IXGBE_STAT(hw_tso6_ctxt)}, {"tx_flow_control_xon", IXGBE_STAT(stats.lxontxc)}, {"rx_flow_control_xon", IXGBE_STAT(stats.lxonrxc)}, {"tx_flow_control_xoff", IXGBE_STAT(stats.lxofftxc)}, {"rx_flow_control_xoff", IXGBE_STAT(stats.lxoffrxc)}, - {"rx_csum_offload_good", IXGBE_STAT(hw_csum_rx_good)}, {"rx_csum_offload_errors", IXGBE_STAT(hw_csum_rx_error)}, - {"tx_csum_offload_ctxt", IXGBE_STAT(hw_csum_tx_good)}, - {"rx_header_split", IXGBE_STAT(rx_hdr_split)}, {"alloc_rx_page_failed", IXGBE_STAT(alloc_rx_page_failed)}, {"alloc_rx_buff_failed", IXGBE_STAT(alloc_rx_buff_failed)}, {"rx_no_dma_resources", IXGBE_STAT(hw_rx_no_dma_resources)}, @@ -196,6 +200,56 @@ static int ixgbe_get_settings(struct net_device *netdev, ecmd->autoneg = AUTONEG_DISABLE; } + /* Get PHY type */ + switch (adapter->hw.phy.type) { + case ixgbe_phy_tn: + case ixgbe_phy_cu_unknown: + /* Copper 10G-BASET */ + ecmd->port = PORT_TP; + break; + case ixgbe_phy_qt: + ecmd->port = PORT_FIBRE; + break; + case ixgbe_phy_nl: + case ixgbe_phy_tw_tyco: + case ixgbe_phy_tw_unknown: + case ixgbe_phy_sfp_ftl: + case ixgbe_phy_sfp_avago: + case ixgbe_phy_sfp_intel: + case ixgbe_phy_sfp_unknown: + switch (adapter->hw.phy.sfp_type) { + /* SFP+ devices, further checking needed */ + case ixgbe_sfp_type_da_cu: + case ixgbe_sfp_type_da_cu_core0: + case ixgbe_sfp_type_da_cu_core1: + ecmd->port = PORT_DA; + break; + case ixgbe_sfp_type_sr: + case ixgbe_sfp_type_lr: + case ixgbe_sfp_type_srlr_core0: + case ixgbe_sfp_type_srlr_core1: + ecmd->port = PORT_FIBRE; + break; + case ixgbe_sfp_type_not_present: + ecmd->port = PORT_NONE; + break; + case ixgbe_sfp_type_unknown: + default: + ecmd->port = PORT_OTHER; + break; + } + break; + case ixgbe_phy_xaui: + ecmd->port = PORT_NONE; + break; + case ixgbe_phy_unknown: + case ixgbe_phy_generic: + case ixgbe_phy_sfp_unsupported: + default: + ecmd->port = PORT_OTHER; + break; + } + hw->mac.ops.check_link(hw, &link_speed, &link_up, false); if (link_up) { ecmd->speed = (link_speed == IXGBE_LINK_SPEED_10GB_FULL) ? @@ -933,10 +987,21 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, int stat_count = sizeof(struct ixgbe_queue_stats) / sizeof(u64); int j, k; int i; + char *p = NULL; ixgbe_update_stats(adapter); for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) { - char *p = (char *)adapter + ixgbe_gstrings_stats[i].stat_offset; + switch (ixgbe_gstrings_stats[i].type) { + case NETDEV_STATS: + p = (char *) netdev + + ixgbe_gstrings_stats[i].stat_offset; + break; + case IXGBE_STATS: + p = (char *) adapter + + ixgbe_gstrings_stats[i].stat_offset; + break; + } + data[i] = (ixgbe_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } @@ -1255,15 +1320,15 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data) return 0; } else if (adapter->flags & IXGBE_FLAG_MSI_ENABLED) { shared_int = false; - if (request_irq(irq, &ixgbe_test_intr, 0, netdev->name, + if (request_irq(irq, ixgbe_test_intr, 0, netdev->name, netdev)) { *data = 1; return -1; } - } else if (!request_irq(irq, &ixgbe_test_intr, IRQF_PROBE_SHARED, + } else if (!request_irq(irq, ixgbe_test_intr, IRQF_PROBE_SHARED, netdev->name, netdev)) { shared_int = false; - } else if (request_irq(irq, &ixgbe_test_intr, IRQF_SHARED, + } else if (request_irq(irq, ixgbe_test_intr, IRQF_SHARED, netdev->name, netdev)) { *data = 1; return -1; @@ -1952,6 +2017,10 @@ static int ixgbe_get_coalesce(struct net_device *netdev, break; } + /* if in mixed tx/rx queues per vector mode, report only rx settings */ + if (adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count) + return 0; + /* only valid if in constant ITR mode */ switch (adapter->tx_itr_setting) { case 0: @@ -1977,12 +2046,9 @@ static int ixgbe_set_coalesce(struct net_device *netdev, struct ixgbe_q_vector *q_vector; int i; - /* - * don't accept tx specific changes if we've got mixed RxTx vectors - * test and jump out here if needed before changing the rx numbers - */ - if ((1000000/ec->tx_coalesce_usecs) != adapter->tx_eitr_param && - adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count) + /* don't accept tx specific changes if we've got mixed RxTx vectors */ + if (adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count + && ec->tx_coalesce_usecs) return -EINVAL; if (ec->tx_max_coalesced_frames_irq) diff --git a/drivers/net/ixgbe/ixgbe_fcoe.c b/drivers/net/ixgbe/ixgbe_fcoe.c index a3c9f99515e2..da32a108a7b4 100644 --- a/drivers/net/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ixgbe/ixgbe_fcoe.c @@ -499,6 +499,10 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_fcoe *fcoe = &adapter->fcoe; struct ixgbe_ring_feature *f = &adapter->ring_feature[RING_F_FCOE]; +#ifdef CONFIG_IXGBE_DCB + u8 tc; + u32 up2tc; +#endif /* create the pool for ddp if not created yet */ if (!fcoe->pool) { @@ -540,6 +544,17 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter) IXGBE_FCRXCTRL_FCOELLI | IXGBE_FCRXCTRL_FCCRCBO | (FC_FCOE_VER << IXGBE_FCRXCTRL_FCOEVER_SHIFT)); +#ifdef CONFIG_IXGBE_DCB + up2tc = IXGBE_READ_REG(&adapter->hw, IXGBE_RTTUP2TC); + for (i = 0; i < MAX_USER_PRIORITY; i++) { + tc = (u8)(up2tc >> (i * IXGBE_RTTUP2TC_UP_SHIFT)); + tc &= (MAX_TRAFFIC_CLASS - 1); + if (fcoe->tc == tc) { + fcoe->up = i; + break; + } + } +#endif } /** @@ -671,19 +686,7 @@ out_disable: */ u8 ixgbe_fcoe_getapp(struct ixgbe_adapter *adapter) { - int i; - u8 tc; - u32 up2tc; - - up2tc = IXGBE_READ_REG(&adapter->hw, IXGBE_RTTUP2TC); - for (i = 0; i < MAX_USER_PRIORITY; i++) { - tc = (u8)(up2tc >> (i * IXGBE_RTTUP2TC_UP_SHIFT)); - tc &= (MAX_TRAFFIC_CLASS - 1); - if (adapter->fcoe.tc == tc) - return 1 << i; - } - - return 0; + return 1 << adapter->fcoe.up; } /** @@ -710,6 +713,7 @@ u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up) up2tc >>= (i * IXGBE_RTTUP2TC_UP_SHIFT); up2tc &= (MAX_TRAFFIC_CLASS - 1); adapter->fcoe.tc = (u8)up2tc; + adapter->fcoe.up = i; return 0; } } @@ -718,3 +722,49 @@ u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up) return 1; } #endif /* CONFIG_IXGBE_DCB */ + +/** + * ixgbe_fcoe_get_wwn - get world wide name for the node or the port + * @netdev : ixgbe adapter + * @wwn : the world wide name + * @type: the type of world wide name + * + * Returns the node or port world wide name if both the prefix and the san + * mac address are valid, then the wwn is formed based on the NAA-2 for + * IEEE Extended name identifier (ref. to T10 FC-LS Spec., Sec. 15.3). + * + * Returns : 0 on success + */ +int ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type) +{ + int rc = -EINVAL; + u16 prefix = 0xffff; + struct ixgbe_adapter *adapter = netdev_priv(netdev); + struct ixgbe_mac_info *mac = &adapter->hw.mac; + + switch (type) { + case NETDEV_FCOE_WWNN: + prefix = mac->wwnn_prefix; + break; + case NETDEV_FCOE_WWPN: + prefix = mac->wwpn_prefix; + break; + default: + break; + } + + if ((prefix != 0xffff) && + is_valid_ether_addr(mac->san_addr)) { + *wwn = ((u64) prefix << 48) | + ((u64) mac->san_addr[0] << 40) | + ((u64) mac->san_addr[1] << 32) | + ((u64) mac->san_addr[2] << 24) | + ((u64) mac->san_addr[3] << 16) | + ((u64) mac->san_addr[4] << 8) | + ((u64) mac->san_addr[5]); + rc = 0; + } + return rc; +} + + diff --git a/drivers/net/ixgbe/ixgbe_fcoe.h b/drivers/net/ixgbe/ixgbe_fcoe.h index b5dee7b3ef1c..de8ff53187da 100644 --- a/drivers/net/ixgbe/ixgbe_fcoe.h +++ b/drivers/net/ixgbe/ixgbe_fcoe.h @@ -62,7 +62,10 @@ struct ixgbe_fcoe_ddp { }; struct ixgbe_fcoe { +#ifdef CONFIG_IXGBE_DCB u8 tc; + u8 up; +#endif spinlock_t lock; struct pci_pool *pool; struct ixgbe_fcoe_ddp ddp[IXGBE_FCOE_DDP_MAX]; diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index a456578b8578..247ed2a24769 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -98,6 +98,8 @@ static struct pci_device_id ixgbe_pci_tbl[] = { board_82599 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP), board_82599 }, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_EM), + board_82599 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4_MEZZ), board_82599 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_CX4), @@ -216,10 +218,20 @@ static void ixgbe_unmap_and_free_tx_resource(struct ixgbe_adapter *adapter, struct ixgbe_tx_buffer *tx_buffer_info) { - tx_buffer_info->dma = 0; + if (tx_buffer_info->dma) { + if (tx_buffer_info->mapped_as_page) + pci_unmap_page(adapter->pdev, + tx_buffer_info->dma, + tx_buffer_info->length, + PCI_DMA_TODEVICE); + else + pci_unmap_single(adapter->pdev, + tx_buffer_info->dma, + tx_buffer_info->length, + PCI_DMA_TODEVICE); + tx_buffer_info->dma = 0; + } if (tx_buffer_info->skb) { - skb_dma_unmap(&adapter->pdev->dev, tx_buffer_info->skb, - DMA_TO_DEVICE); dev_kfree_skb_any(tx_buffer_info->skb); tx_buffer_info->skb = NULL; } @@ -401,7 +413,7 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, if (__netif_subqueue_stopped(netdev, tx_ring->queue_index) && !test_bit(__IXGBE_DOWN, &adapter->state)) { netif_wake_subqueue(netdev, tx_ring->queue_index); - ++adapter->restart_queue; + ++tx_ring->restart_queue; } } @@ -423,8 +435,8 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, tx_ring->total_packets += total_packets; tx_ring->stats.packets += total_packets; tx_ring->stats.bytes += total_bytes; - adapter->net_stats.tx_bytes += total_bytes; - adapter->net_stats.tx_packets += total_packets; + netdev->stats.tx_bytes += total_bytes; + netdev->stats.tx_packets += total_packets; return (count < tx_ring->work_limit); } @@ -612,7 +624,6 @@ static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter, /* It must be a TCP or UDP packet with a valid checksum */ skb->ip_summed = CHECKSUM_UNNECESSARY; - adapter->hw_csum_rx_good++; } static inline void ixgbe_release_rx_desc(struct ixgbe_hw *hw, @@ -669,21 +680,18 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter, if (!bi->skb) { struct sk_buff *skb; - skb = netdev_alloc_skb(adapter->netdev, - (rx_ring->rx_buf_len + - NET_IP_ALIGN)); + /* netdev_alloc_skb reserves 32 bytes up front!! */ + uint bufsz = rx_ring->rx_buf_len + SMP_CACHE_BYTES; + skb = netdev_alloc_skb(adapter->netdev, bufsz); if (!skb) { adapter->alloc_rx_buff_failed++; goto no_buffers; } - /* - * Make buffer alignment 2 beyond a 16 byte boundary - * this will result in a 16 byte aligned IP header after - * the 14 byte MAC header is removed - */ - skb_reserve(skb, NET_IP_ALIGN); + /* advance the data pointer to the next cache line */ + skb_reserve(skb, (PTR_ALIGN(skb->data, SMP_CACHE_BYTES) + - skb->data)); bi->skb = skb; bi->dma = pci_map_single(pdev, skb->data, @@ -735,12 +743,14 @@ static inline u32 ixgbe_get_rsc_count(union ixgbe_adv_rx_desc *rx_desc) /** * ixgbe_transform_rsc_queue - change rsc queue into a full packet * @skb: pointer to the last skb in the rsc queue + * @count: pointer to number of packets coalesced in this context * * This function changes a queue full of hw rsc buffers into a completed * packet. It uses the ->prev pointers to find the first packet and then * turns it into the frag list owner. **/ -static inline struct sk_buff *ixgbe_transform_rsc_queue(struct sk_buff *skb) +static inline struct sk_buff *ixgbe_transform_rsc_queue(struct sk_buff *skb, + u64 *count) { unsigned int frag_list_size = 0; @@ -749,6 +759,7 @@ static inline struct sk_buff *ixgbe_transform_rsc_queue(struct sk_buff *skb) frag_list_size += skb->len; skb->prev = NULL; skb = prev; + *count += 1; } skb_shinfo(skb)->frag_list = skb->next; @@ -764,6 +775,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, int *work_done, int work_to_do) { struct ixgbe_adapter *adapter = q_vector->adapter; + struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; union ixgbe_adv_rx_desc *rx_desc, *next_rxd; struct ixgbe_rx_buffer *rx_buffer_info, *next_buffer; @@ -793,8 +805,6 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, hdr_info = le16_to_cpu(ixgbe_get_hdr_info(rx_desc)); len = (hdr_info & IXGBE_RXDADV_HDRBUFLEN_MASK) >> IXGBE_RXDADV_HDRBUFLEN_SHIFT; - if (hdr_info & IXGBE_RXDADV_SPH) - adapter->rx_hdr_split++; if (len > IXGBE_RX_HDR_SIZE) len = IXGBE_RX_HDR_SIZE; upper_len = le16_to_cpu(rx_desc->wb.upper.length); @@ -804,7 +814,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, cleaned = true; skb = rx_buffer_info->skb; - prefetch(skb->data - NET_IP_ALIGN); + prefetch(skb->data); rx_buffer_info->skb = NULL; if (rx_buffer_info->dma) { @@ -850,14 +860,20 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, u32 nextp = (staterr & IXGBE_RXDADV_NEXTP_MASK) >> IXGBE_RXDADV_NEXTP_SHIFT; next_buffer = &rx_ring->rx_buffer_info[nextp]; - rx_ring->rsc_count += (rsc_count - 1); } else { next_buffer = &rx_ring->rx_buffer_info[i]; } if (staterr & IXGBE_RXD_STAT_EOP) { if (skb->prev) - skb = ixgbe_transform_rsc_queue(skb); + skb = ixgbe_transform_rsc_queue(skb, &(rx_ring->rsc_count)); + if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) { + if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) + rx_ring->rsc_count += skb_shinfo(skb)->nr_frags; + else + rx_ring->rsc_count++; + rx_ring->rsc_flush++; + } rx_ring->stats.packets++; rx_ring->stats.bytes += skb->len; } else { @@ -870,7 +886,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, skb->next = next_buffer->skb; skb->next->prev = skb; } - adapter->non_eop_descs++; + rx_ring->non_eop_descs++; goto next_desc; } @@ -935,8 +951,8 @@ next_desc: rx_ring->total_packets += total_rx_packets; rx_ring->total_bytes += total_rx_bytes; - adapter->net_stats.rx_bytes += total_rx_bytes; - adapter->net_stats.rx_packets += total_rx_packets; + netdev->stats.rx_bytes += total_rx_bytes; + netdev->stats.rx_packets += total_rx_packets; return cleaned; } @@ -1209,6 +1225,7 @@ static void ixgbe_check_lsc(struct ixgbe_adapter *adapter) adapter->link_check_timeout = jiffies; if (!test_bit(__IXGBE_DOWN, &adapter->state)) { IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_LSC); + IXGBE_WRITE_FLUSH(hw); schedule_work(&adapter->watchdog_task); } } @@ -1312,8 +1329,7 @@ static irqreturn_t ixgbe_msix_clean_tx(int irq, void *data) r_idx + 1); } - /* disable interrupts on this vector only */ - ixgbe_irq_disable_queues(adapter, ((u64)1 << q_vector->v_idx)); + /* EIAM disabled interrupts (on this vector) for us */ napi_schedule(&q_vector->napi); return IRQ_HANDLED; @@ -1344,10 +1360,8 @@ static irqreturn_t ixgbe_msix_clean_rx(int irq, void *data) if (!q_vector->rxr_count) return IRQ_HANDLED; - r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues); - rx_ring = &(adapter->rx_ring[r_idx]); /* disable interrupts on this vector only */ - ixgbe_irq_disable_queues(adapter, ((u64)1 << q_vector->v_idx)); + /* EIAM disabled interrupts (on this vector) for us */ napi_schedule(&q_vector->napi); return IRQ_HANDLED; @@ -1382,8 +1396,7 @@ static irqreturn_t ixgbe_msix_clean_many(int irq, void *data) r_idx + 1); } - /* disable interrupts on this vector only */ - ixgbe_irq_disable_queues(adapter, ((u64)1 << q_vector->v_idx)); + /* EIAM disabled interrupts (on this vector) for us */ napi_schedule(&q_vector->napi); return IRQ_HANDLED; @@ -1667,7 +1680,7 @@ static int ixgbe_request_msix_irqs(struct ixgbe_adapter *adapter) sprintf(adapter->name[vector], "%s:lsc", netdev->name); err = request_irq(adapter->msix_entries[vector].vector, - &ixgbe_msix_lsc, 0, adapter->name[vector], netdev); + ixgbe_msix_lsc, 0, adapter->name[vector], netdev); if (err) { DPRINTK(PROBE, ERR, "request_irq for msix_lsc failed: %d\n", err); @@ -1838,10 +1851,10 @@ static int ixgbe_request_irq(struct ixgbe_adapter *adapter) if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) { err = ixgbe_request_msix_irqs(adapter); } else if (adapter->flags & IXGBE_FLAG_MSI_ENABLED) { - err = request_irq(adapter->pdev->irq, &ixgbe_intr, 0, + err = request_irq(adapter->pdev->irq, ixgbe_intr, 0, netdev->name, netdev); } else { - err = request_irq(adapter->pdev->irq, &ixgbe_intr, IRQF_SHARED, + err = request_irq(adapter->pdev->irq, ixgbe_intr, IRQF_SHARED, netdev->name, netdev); } @@ -2063,18 +2076,18 @@ static u32 ixgbe_setup_mrqc(struct ixgbe_adapter *adapter) * ixgbe_configure_rscctl - enable RSC for the indicated ring * @adapter: address of board private structure * @index: index of ring to set - * @rx_buf_len: rx buffer length **/ -static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter, int index, - int rx_buf_len) +static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter, int index) { struct ixgbe_ring *rx_ring; struct ixgbe_hw *hw = &adapter->hw; int j; u32 rscctrl; + int rx_buf_len; rx_ring = &adapter->rx_ring[index]; j = rx_ring->reg_idx; + rx_buf_len = rx_ring->rx_buf_len; rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(j)); rscctrl |= IXGBE_RSCCTL_RSCEN; /* @@ -2282,7 +2295,7 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) { /* Enable 82599 HW-RSC */ for (i = 0; i < adapter->num_rx_queues; i++) - ixgbe_configure_rscctl(adapter, i, rx_buf_len); + ixgbe_configure_rscctl(adapter, i); /* Disable RSC for ACK packets */ IXGBE_WRITE_REG(hw, IXGBE_RSCDBU, @@ -2333,23 +2346,25 @@ static void ixgbe_vlan_rx_register(struct net_device *netdev, * not in DCB mode. */ ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL); + + /* Disable CFI check */ + ctrl &= ~IXGBE_VLNCTRL_CFIEN; + + /* enable VLAN tag stripping */ if (adapter->hw.mac.type == ixgbe_mac_82598EB) { - ctrl |= IXGBE_VLNCTRL_VME | IXGBE_VLNCTRL_VFE; - ctrl &= ~IXGBE_VLNCTRL_CFIEN; - IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl); + ctrl |= IXGBE_VLNCTRL_VME; } else if (adapter->hw.mac.type == ixgbe_mac_82599EB) { - ctrl |= IXGBE_VLNCTRL_VFE; - /* enable VLAN tag insert/strip */ - ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL); - ctrl &= ~IXGBE_VLNCTRL_CFIEN; - IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl); for (i = 0; i < adapter->num_rx_queues; i++) { + u32 ctrl; j = adapter->rx_ring[i].reg_idx; ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_RXDCTL(j)); ctrl |= IXGBE_RXDCTL_VME; IXGBE_WRITE_REG(&adapter->hw, IXGBE_RXDCTL(j), ctrl); } } + + IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl); + ixgbe_vlan_rx_add_vid(netdev, 0); if (!test_bit(__IXGBE_DOWN, &adapter->state)) @@ -2699,7 +2714,22 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie); } - if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED)) { + if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) { + /* + * use EIAM to auto-mask when MSI-X interrupt is asserted + * this saves a register write for every interrupt + */ + switch (hw->mac.type) { + case ixgbe_mac_82598EB: + IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE); + break; + default: + case ixgbe_mac_82599EB: + IXGBE_WRITE_REG(hw, IXGBE_EIAM_EX(0), 0xFFFFFFFF); + IXGBE_WRITE_REG(hw, IXGBE_EIAM_EX(1), 0xFFFFFFFF); + break; + } + } else { /* legacy interrupts, use EIAM to auto-mask when reading EICR, * specifically only auto mask tx and rx interrupts */ IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE); @@ -3632,10 +3662,10 @@ static int ixgbe_set_interrupt_capability(struct ixgbe_adapter *adapter) * It's easy to be greedy for MSI-X vectors, but it really * doesn't do us much good if we have a lot more vectors * than CPU's. So let's be conservative and only ask for - * (roughly) twice the number of vectors as there are CPU's. + * (roughly) the same number of vectors as there are CPU's. */ v_budget = min(adapter->num_rx_queues + adapter->num_tx_queues, - (int)(num_online_cpus() * 2)) + NON_Q_VECTORS; + (int)num_online_cpus()) + NON_Q_VECTORS; /* * At the same time, hardware can only support a maximum of @@ -3943,8 +3973,10 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter) adapter->flags |= IXGBE_FLAG_FCOE_CAPABLE; adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED; adapter->ring_feature[RING_F_FCOE].indices = 0; +#ifdef CONFIG_IXGBE_DCB /* Default traffic class to use for FCoE */ adapter->fcoe.tc = IXGBE_FCOE_DEFTC; +#endif #endif /* IXGBE_FCOE */ } @@ -4475,20 +4507,32 @@ static void ixgbe_shutdown(struct pci_dev *pdev) **/ void ixgbe_update_stats(struct ixgbe_adapter *adapter) { + struct net_device *netdev = adapter->netdev; struct ixgbe_hw *hw = &adapter->hw; u64 total_mpc = 0; u32 i, missed_rx = 0, mpc, bprc, lxon, lxoff, xon_off_tot; - if (hw->mac.type == ixgbe_mac_82599EB) { + if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) { u64 rsc_count = 0; + u64 rsc_flush = 0; for (i = 0; i < 16; i++) adapter->hw_rx_no_dma_resources += IXGBE_READ_REG(hw, IXGBE_QPRDC(i)); - for (i = 0; i < adapter->num_rx_queues; i++) + for (i = 0; i < adapter->num_rx_queues; i++) { rsc_count += adapter->rx_ring[i].rsc_count; - adapter->rsc_count = rsc_count; + rsc_flush += adapter->rx_ring[i].rsc_flush; + } + adapter->rsc_total_count = rsc_count; + adapter->rsc_total_flush = rsc_flush; } + /* gather some stats to the adapter struct that are per queue */ + for (i = 0; i < adapter->num_tx_queues; i++) + adapter->restart_queue += adapter->tx_ring[i].restart_queue; + + for (i = 0; i < adapter->num_rx_queues; i++) + adapter->non_eop_descs += adapter->tx_ring[i].non_eop_descs; + adapter->stats.crcerrs += IXGBE_READ_REG(hw, IXGBE_CRCERRS); for (i = 0; i < 8; i++) { /* for packet buffers not used, the register should read 0 */ @@ -4594,15 +4638,15 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter) adapter->stats.bptc += IXGBE_READ_REG(hw, IXGBE_BPTC); /* Fill out the OS statistics structure */ - adapter->net_stats.multicast = adapter->stats.mprc; + netdev->stats.multicast = adapter->stats.mprc; /* Rx Errors */ - adapter->net_stats.rx_errors = adapter->stats.crcerrs + + netdev->stats.rx_errors = adapter->stats.crcerrs + adapter->stats.rlec; - adapter->net_stats.rx_dropped = 0; - adapter->net_stats.rx_length_errors = adapter->stats.rlec; - adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs; - adapter->net_stats.rx_missed_errors = total_mpc; + netdev->stats.rx_dropped = 0; + netdev->stats.rx_length_errors = adapter->stats.rlec; + netdev->stats.rx_crc_errors = adapter->stats.crcerrs; + netdev->stats.rx_missed_errors = total_mpc; } /** @@ -4871,14 +4915,12 @@ static int ixgbe_tso(struct ixgbe_adapter *adapter, iph->daddr, 0, IPPROTO_TCP, 0); - adapter->hw_tso_ctxt++; } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) { ipv6_hdr(skb)->payload_len = 0; tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); - adapter->hw_tso6_ctxt++; } i = tx_ring->next_to_use; @@ -4997,7 +5039,6 @@ static bool ixgbe_tx_csum(struct ixgbe_adapter *adapter, tx_buffer_info->time_stamp = jiffies; tx_buffer_info->next_to_watch = i; - adapter->hw_csum_tx_good++; i++; if (i == tx_ring->count) i = 0; @@ -5014,23 +5055,16 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter, struct sk_buff *skb, u32 tx_flags, unsigned int first) { + struct pci_dev *pdev = adapter->pdev; struct ixgbe_tx_buffer *tx_buffer_info; unsigned int len; unsigned int total = skb->len; unsigned int offset = 0, size, count = 0, i; unsigned int nr_frags = skb_shinfo(skb)->nr_frags; unsigned int f; - dma_addr_t *map; i = tx_ring->next_to_use; - if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) { - dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); - return 0; - } - - map = skb_shinfo(skb)->dma_maps; - if (tx_flags & IXGBE_TX_FLAGS_FCOE) /* excluding fcoe_crc_eof for FCoE */ total -= sizeof(struct fcoe_crc_eof); @@ -5041,7 +5075,12 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter, size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD); tx_buffer_info->length = size; - tx_buffer_info->dma = skb_shinfo(skb)->dma_head + offset; + tx_buffer_info->mapped_as_page = false; + tx_buffer_info->dma = pci_map_single(pdev, + skb->data + offset, + size, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, tx_buffer_info->dma)) + goto dma_error; tx_buffer_info->time_stamp = jiffies; tx_buffer_info->next_to_watch = i; @@ -5062,7 +5101,7 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter, frag = &skb_shinfo(skb)->frags[f]; len = min((unsigned int)frag->size, total); - offset = 0; + offset = frag->page_offset; while (len) { i++; @@ -5073,7 +5112,13 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter, size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD); tx_buffer_info->length = size; - tx_buffer_info->dma = map[f] + offset; + tx_buffer_info->dma = pci_map_page(adapter->pdev, + frag->page, + offset, size, + PCI_DMA_TODEVICE); + tx_buffer_info->mapped_as_page = true; + if (pci_dma_mapping_error(pdev, tx_buffer_info->dma)) + goto dma_error; tx_buffer_info->time_stamp = jiffies; tx_buffer_info->next_to_watch = i; @@ -5090,6 +5135,27 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter, tx_ring->tx_buffer_info[first].next_to_watch = i; return count; + +dma_error: + dev_err(&pdev->dev, "TX DMA map failed\n"); + + /* clear timestamp and dma mappings for failed tx_buffer_info map */ + tx_buffer_info->dma = 0; + tx_buffer_info->time_stamp = 0; + tx_buffer_info->next_to_watch = 0; + count--; + + /* clear timestamp and dma mappings for remaining portion of packet */ + while (count >= 0) { + count--; + i--; + if (i < 0) + i += tx_ring->count; + tx_buffer_info = &tx_ring->tx_buffer_info[i]; + ixgbe_unmap_and_free_tx_resource(adapter, tx_buffer_info); + } + + return count; } static void ixgbe_tx_queue(struct ixgbe_adapter *adapter, @@ -5209,8 +5275,6 @@ static void ixgbe_atr(struct ixgbe_adapter *adapter, struct sk_buff *skb, static int __ixgbe_maybe_stop_tx(struct net_device *netdev, struct ixgbe_ring *tx_ring, int size) { - struct ixgbe_adapter *adapter = netdev_priv(netdev); - netif_stop_subqueue(netdev, tx_ring->queue_index); /* Herbert's original patch had: * smp_mb__after_netif_stop_queue(); @@ -5224,7 +5288,7 @@ static int __ixgbe_maybe_stop_tx(struct net_device *netdev, /* A reprieve! - use start_queue because it doesn't call schedule */ netif_start_subqueue(netdev, tx_ring->queue_index); - ++adapter->restart_queue; + ++tx_ring->restart_queue; return 0; } @@ -5239,10 +5303,19 @@ static int ixgbe_maybe_stop_tx(struct net_device *netdev, static u16 ixgbe_select_queue(struct net_device *dev, struct sk_buff *skb) { struct ixgbe_adapter *adapter = netdev_priv(dev); + int txq = smp_processor_id(); if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) - return smp_processor_id(); + return txq; +#ifdef IXGBE_FCOE + if ((adapter->flags & IXGBE_FLAG_FCOE_ENABLED) && + (skb->protocol == htons(ETH_P_FCOE))) { + txq &= (adapter->ring_feature[RING_F_FCOE].indices - 1); + txq += adapter->ring_feature[RING_F_FCOE].mask; + return txq; + } +#endif if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) return (skb->vlan_tci & IXGBE_TX_FLAGS_VLAN_PRIO_MASK) >> 13; @@ -5257,7 +5330,7 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, unsigned int first; unsigned int tx_flags = 0; u8 hdr_len = 0; - int r_idx = 0, tso; + int tso; int count = 0; unsigned int f; @@ -5265,13 +5338,13 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, tx_flags |= vlan_tx_tag_get(skb); if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { tx_flags &= ~IXGBE_TX_FLAGS_VLAN_PRIO_MASK; - tx_flags |= (skb->queue_mapping << 13); + tx_flags |= ((skb->queue_mapping & 0x7) << 13); } tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; tx_flags |= IXGBE_TX_FLAGS_VLAN; } else if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { if (skb->priority != TC_PRIO_CONTROL) { - tx_flags |= (skb->queue_mapping << 13); + tx_flags |= ((skb->queue_mapping & 0x7) << 13); tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; tx_flags |= IXGBE_TX_FLAGS_VLAN; } else { @@ -5280,17 +5353,18 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, } } - r_idx = skb->queue_mapping; - tx_ring = &adapter->tx_ring[r_idx]; + tx_ring = &adapter->tx_ring[skb->queue_mapping]; if ((adapter->flags & IXGBE_FLAG_FCOE_ENABLED) && (skb->protocol == htons(ETH_P_FCOE))) { tx_flags |= IXGBE_TX_FLAGS_FCOE; #ifdef IXGBE_FCOE - r_idx = smp_processor_id(); - r_idx &= (adapter->ring_feature[RING_F_FCOE].indices - 1); - r_idx += adapter->ring_feature[RING_F_FCOE].mask; - tx_ring = &adapter->tx_ring[r_idx]; +#ifdef CONFIG_IXGBE_DCB + tx_flags &= ~(IXGBE_TX_FLAGS_VLAN_PRIO_MASK + << IXGBE_TX_FLAGS_VLAN_SHIFT); + tx_flags |= ((adapter->fcoe.up << 13) + << IXGBE_TX_FLAGS_VLAN_SHIFT); +#endif #endif } /* four things can cause us to need a context descriptor */ @@ -5372,10 +5446,8 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, **/ static struct net_device_stats *ixgbe_get_stats(struct net_device *netdev) { - struct ixgbe_adapter *adapter = netdev_priv(netdev); - /* only return the current stats */ - return &adapter->net_stats; + return &netdev->stats; } /** @@ -5527,6 +5599,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_fcoe_ddp_done = ixgbe_fcoe_ddp_put, .ndo_fcoe_enable = ixgbe_fcoe_enable, .ndo_fcoe_disable = ixgbe_fcoe_disable, + .ndo_fcoe_get_wwn = ixgbe_fcoe_get_wwn, #endif /* IXGBE_FCOE */ }; diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h index ef4bdd58e016..21b6633da578 100644 --- a/drivers/net/ixgbe/ixgbe_type.h +++ b/drivers/net/ixgbe/ixgbe_type.h @@ -52,6 +52,7 @@ #define IXGBE_DEV_ID_82599_KX4_MEZZ 0x1514 #define IXGBE_DEV_ID_82599_CX4 0x10F9 #define IXGBE_DEV_ID_82599_SFP 0x10FB +#define IXGBE_DEV_ID_82599_SFP_EM 0x1507 #define IXGBE_DEV_ID_82599_XAUI_LOM 0x10FC #define IXGBE_DEV_ID_82599_COMBO_BACKPLANE 0x10F8 @@ -1538,6 +1539,16 @@ #define IXGBE_FW_PASSTHROUGH_PATCH_CONFIG_PTR 0x4 #define IXGBE_FW_PATCH_VERSION_4 0x7 +/* Alternative SAN MAC Address Block */ +#define IXGBE_ALT_SAN_MAC_ADDR_BLK_PTR 0x27 /* Alt. SAN MAC block */ +#define IXGBE_ALT_SAN_MAC_ADDR_CAPS_OFFSET 0x0 /* Alt. SAN MAC capability */ +#define IXGBE_ALT_SAN_MAC_ADDR_PORT0_OFFSET 0x1 /* Alt. SAN MAC 0 offset */ +#define IXGBE_ALT_SAN_MAC_ADDR_PORT1_OFFSET 0x4 /* Alt. SAN MAC 1 offset */ +#define IXGBE_ALT_SAN_MAC_ADDR_WWNN_OFFSET 0x7 /* Alt. WWNN prefix offset */ +#define IXGBE_ALT_SAN_MAC_ADDR_WWPN_OFFSET 0x8 /* Alt. WWPN prefix offset */ +#define IXGBE_ALT_SAN_MAC_ADDR_CAPS_SANMAC 0x0 /* Alt. SAN MAC exists */ +#define IXGBE_ALT_SAN_MAC_ADDR_CAPS_ALTWWN 0x1 /* Alt. WWN base exists */ + /* PCI Bus Info */ #define IXGBE_PCI_LINK_STATUS 0xB2 #define IXGBE_PCI_DEVICE_CONTROL2 0xC8 @@ -2171,6 +2182,14 @@ enum ixgbe_fc_mode { ixgbe_fc_default }; +/* Smart Speed Settings */ +#define IXGBE_SMARTSPEED_MAX_RETRIES 3 +enum ixgbe_smart_speed { + ixgbe_smart_speed_auto = 0, + ixgbe_smart_speed_on, + ixgbe_smart_speed_off +}; + /* PCI bus types */ enum ixgbe_bus_type { ixgbe_bus_type_unknown = 0, @@ -2336,6 +2355,7 @@ struct ixgbe_mac_operations { s32 (*get_mac_addr)(struct ixgbe_hw *, u8 *); s32 (*get_san_mac_addr)(struct ixgbe_hw *, u8 *); s32 (*get_device_caps)(struct ixgbe_hw *, u16 *); + s32 (*get_wwn_prefix)(struct ixgbe_hw *, u16 *, u16 *); s32 (*stop_adapter)(struct ixgbe_hw *); s32 (*get_bus_info)(struct ixgbe_hw *); void (*set_lan_id)(struct ixgbe_hw *); @@ -2407,6 +2427,10 @@ struct ixgbe_mac_info { u8 addr[IXGBE_ETH_LENGTH_OF_ADDRESS]; u8 perm_addr[IXGBE_ETH_LENGTH_OF_ADDRESS]; u8 san_addr[IXGBE_ETH_LENGTH_OF_ADDRESS]; + /* prefix for World Wide Node Name (WWNN) */ + u16 wwnn_prefix; + /* prefix for World Wide Port Name (WWPN) */ + u16 wwpn_prefix; s32 mc_filter_type; u32 mcft_size; u32 vft_size; @@ -2431,6 +2455,8 @@ struct ixgbe_phy_info { enum ixgbe_media_type media_type; bool reset_disable; ixgbe_autoneg_advertised autoneg_advertised; + enum ixgbe_smart_speed smart_speed; + bool smart_speed_active; bool multispeed_fiber; }; diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c index 9aee0cc922c9..e9d9d595e1b7 100644 --- a/drivers/net/ixp2000/ixpdev.c +++ b/drivers/net/ixp2000/ixpdev.c @@ -109,9 +109,8 @@ static int ixpdev_rx(struct net_device *dev, int processed, int budget) if (unlikely(!netif_running(nds[desc->channel]))) goto err; - skb = netdev_alloc_skb(dev, desc->pkt_length + 2); + skb = netdev_alloc_skb_ip_align(dev, desc->pkt_length); if (likely(skb != NULL)) { - skb_reserve(skb, 2); skb_copy_to_linear_data(skb, buf, desc->pkt_length); skb_put(skb, desc->pkt_length); skb->protocol = eth_type_trans(skb, nds[desc->channel]); diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c index 6e5b3f30527f..f47d4d663b19 100644 --- a/drivers/net/jazzsonic.c +++ b/drivers/net/jazzsonic.c @@ -81,7 +81,7 @@ static unsigned short known_revisions[] = static int jazzsonic_open(struct net_device* dev) { - if (request_irq(dev->irq, &sonic_interrupt, IRQF_DISABLED, "sonic", dev)) { + if (request_irq(dev->irq, sonic_interrupt, IRQF_DISABLED, "sonic", dev)) { printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq); return -EAGAIN; } @@ -130,8 +130,8 @@ static int __devinit sonic_probe1(struct net_device *dev) printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision); i = 0; - while (known_revisions[i] != 0xffff - && known_revisions[i] != silicon_revision) + while (known_revisions[i] != 0xffff && + known_revisions[i] != silicon_revision) i++; if (known_revisions[i] == 0xffff) { diff --git a/drivers/net/jme.c b/drivers/net/jme.c index 1d2a32544ed2..792b88fc3574 100644 --- a/drivers/net/jme.c +++ b/drivers/net/jme.c @@ -1050,8 +1050,8 @@ jme_dynamic_pcc(struct jme_adapter *jme) if ((NET_STAT(jme).rx_bytes - dpi->last_bytes) > PCC_P3_THRESHOLD) jme_attempt_pcc(dpi, PCC_P3); - else if ((NET_STAT(jme).rx_packets - dpi->last_pkts) > PCC_P2_THRESHOLD - || dpi->intr_cnt > PCC_INTR_THRESHOLD) + else if ((NET_STAT(jme).rx_packets - dpi->last_pkts) > PCC_P2_THRESHOLD || + dpi->intr_cnt > PCC_INTR_THRESHOLD) jme_attempt_pcc(dpi, PCC_P2); else jme_attempt_pcc(dpi, PCC_P1); @@ -2199,8 +2199,8 @@ jme_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd) if (netif_running(netdev)) return -EBUSY; - if (ecmd->use_adaptive_rx_coalesce - && test_bit(JME_FLAG_POLL, &jme->flags)) { + if (ecmd->use_adaptive_rx_coalesce && + test_bit(JME_FLAG_POLL, &jme->flags)) { clear_bit(JME_FLAG_POLL, &jme->flags); jme->jme_rx = netif_rx; jme->jme_vlan_rx = vlan_hwaccel_rx; @@ -2209,8 +2209,8 @@ jme_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd) dpi->cnt = 0; jme_set_rx_pcc(jme, PCC_P1); jme_interrupt_mode(jme); - } else if (!(ecmd->use_adaptive_rx_coalesce) - && !(test_bit(JME_FLAG_POLL, &jme->flags))) { + } else if (!(ecmd->use_adaptive_rx_coalesce) && + !(test_bit(JME_FLAG_POLL, &jme->flags))) { set_bit(JME_FLAG_POLL, &jme->flags); jme->jme_rx = netif_receive_skb; jme->jme_vlan_rx = vlan_hwaccel_receive_skb; @@ -2764,19 +2764,19 @@ jme_init_one(struct pci_dev *pdev, atomic_set(&jme->rx_empty, 1); tasklet_init(&jme->pcc_task, - &jme_pcc_tasklet, + jme_pcc_tasklet, (unsigned long) jme); tasklet_init(&jme->linkch_task, - &jme_link_change_tasklet, + jme_link_change_tasklet, (unsigned long) jme); tasklet_init(&jme->txclean_task, - &jme_tx_clean_tasklet, + jme_tx_clean_tasklet, (unsigned long) jme); tasklet_init(&jme->rxclean_task, - &jme_rx_clean_tasklet, + jme_rx_clean_tasklet, (unsigned long) jme); tasklet_init(&jme->rxempty_task, - &jme_rx_empty_tasklet, + jme_rx_empty_tasklet, (unsigned long) jme); tasklet_disable_nosync(&jme->linkch_task); tasklet_disable_nosync(&jme->txclean_task); diff --git a/drivers/net/korina.c b/drivers/net/korina.c index 03199fa10003..25e2af6997e4 100644 --- a/drivers/net/korina.c +++ b/drivers/net/korina.c @@ -400,7 +400,7 @@ static int korina_rx(struct net_device *dev, int limit) dma_cache_inv((unsigned long)pkt_buf, pkt_len - 4); /* Malloc up new buffer. */ - skb_new = netdev_alloc_skb(dev, KORINA_RBSIZE + 2); + skb_new = netdev_alloc_skb_ip_align(dev, KORINA_RBSIZE); if (!skb_new) break; @@ -417,9 +417,6 @@ static int korina_rx(struct net_device *dev, int limit) if (devcs & ETH_RX_MP) dev->stats.multicast++; - /* 16 bit align */ - skb_reserve(skb_new, 2); - lp->rx_skb[lp->rx_next_done] = skb_new; } @@ -1017,14 +1014,14 @@ static int korina_open(struct net_device *dev) /* Install the interrupt handler * that handles the Done Finished * Ovr and Und Events */ - ret = request_irq(lp->rx_irq, &korina_rx_dma_interrupt, + ret = request_irq(lp->rx_irq, korina_rx_dma_interrupt, IRQF_DISABLED, "Korina ethernet Rx", dev); if (ret < 0) { printk(KERN_ERR "%s: unable to get Rx DMA IRQ %d\n", dev->name, lp->rx_irq); goto err_release; } - ret = request_irq(lp->tx_irq, &korina_tx_dma_interrupt, + ret = request_irq(lp->tx_irq, korina_tx_dma_interrupt, IRQF_DISABLED, "Korina ethernet Tx", dev); if (ret < 0) { printk(KERN_ERR "%s: unable to get Tx DMA IRQ %d\n", @@ -1033,7 +1030,7 @@ static int korina_open(struct net_device *dev) } /* Install handler for overrun error. */ - ret = request_irq(lp->ovr_irq, &korina_ovr_interrupt, + ret = request_irq(lp->ovr_irq, korina_ovr_interrupt, IRQF_DISABLED, "Ethernet Overflow", dev); if (ret < 0) { printk(KERN_ERR "%s: unable to get OVR IRQ %d\n", @@ -1042,7 +1039,7 @@ static int korina_open(struct net_device *dev) } /* Install handler for underflow error. */ - ret = request_irq(lp->und_irq, &korina_und_interrupt, + ret = request_irq(lp->und_irq, korina_und_interrupt, IRQF_DISABLED, "Ethernet Underflow", dev); if (ret < 0) { printk(KERN_ERR "%s: unable to get UND IRQ %d\n", diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c index 99e954167fa6..5c45cb58d023 100644 --- a/drivers/net/ks8842.c +++ b/drivers/net/ks8842.c @@ -357,7 +357,7 @@ static void ks8842_rx_frame(struct net_device *netdev, /* check the status */ if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) { - struct sk_buff *skb = netdev_alloc_skb(netdev, len + 2); + struct sk_buff *skb = netdev_alloc_skb_ip_align(netdev, len); dev_dbg(&adapter->pdev->dev, "%s, got package, len: %d\n", __func__, len); @@ -369,9 +369,6 @@ static void ks8842_rx_frame(struct net_device *netdev, if (status & RXSR_MULTICAST) netdev->stats.multicast++; - /* Align socket buffer in 4-byte boundary for - better performance. */ - skb_reserve(skb, 2); data = (u32 *)skb_put(skb, len); ks8842_select_bank(adapter, 17); diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c index a23f739d222f..6d3ac65bc35c 100644 --- a/drivers/net/ks8851.c +++ b/drivers/net/ks8851.c @@ -1,4 +1,4 @@ -/* drivers/net/ks8651.c +/* drivers/net/ks8851.c * * Copyright 2009 Simtec Electronics * http://www.simtec.co.uk/ @@ -714,7 +714,7 @@ static void ks8851_tx_work(struct work_struct *work) { struct ks8851_net *ks = container_of(work, struct ks8851_net, tx_work); struct sk_buff *txb; - bool last = false; + bool last = skb_queue_empty(&ks->txq); mutex_lock(&ks->lock); diff --git a/drivers/net/lance.c b/drivers/net/lance.c index dcda30338b65..8d7d3d4625f6 100644 --- a/drivers/net/lance.c +++ b/drivers/net/lance.c @@ -493,14 +493,14 @@ static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360}; int hp_port = (readl(bios + 1) & 1) ? 0x499 : 0x99; /* We can have boards other than the built-in! Verify this is on-board. */ - if ((inb(hp_port) & 0xc0) == 0x80 - && ioaddr_table[inb(hp_port) & 3] == ioaddr) + if ((inb(hp_port) & 0xc0) == 0x80 && + ioaddr_table[inb(hp_port) & 3] == ioaddr) hp_builtin = hp_port; } iounmap(bios); /* We also recognize the HP Vectra on-board here, but check below. */ - hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 - && inb(ioaddr+2) == 0x09); + hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 && + inb(ioaddr+2) == 0x09); /* Reset the LANCE. */ reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */ @@ -755,7 +755,7 @@ lance_open(struct net_device *dev) int i; if (dev->irq == 0 || - request_irq(dev->irq, &lance_interrupt, 0, lp->name, dev)) { + request_irq(dev->irq, lance_interrupt, 0, lp->name, dev)) { return -EAGAIN; } @@ -1035,8 +1035,8 @@ static irqreturn_t lance_interrupt(int irq, void *dev_id) spin_lock (&lp->devlock); outw(0x00, dev->base_addr + LANCE_ADDR); - while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 - && --boguscnt >= 0) { + while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 && + --boguscnt >= 0) { /* Acknowledge all of the current interrupt sources ASAP. */ outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA); diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c index a0c578585a50..b77238dbafb8 100644 --- a/drivers/net/lasi_82596.c +++ b/drivers/net/lasi_82596.c @@ -47,7 +47,7 @@ TBD: * look at deferring rx frames rather than discarding (as per tulip) * handle tx ring full as per tulip - * performace test to tune rx_copybreak + * performance test to tune rx_copybreak Most of my modifications relate to the braindead big-endian implementation by Intel. When the i596 is operating in diff --git a/drivers/net/lib82596.c b/drivers/net/lib82596.c index 51e11c3e53e1..b117f7f8b194 100644 --- a/drivers/net/lib82596.c +++ b/drivers/net/lib82596.c @@ -47,7 +47,7 @@ TBD: * look at deferring rx frames rather than discarding (as per tulip) * handle tx ring full as per tulip - * performace test to tune rx_copybreak + * performance test to tune rx_copybreak Most of my modifications relate to the braindead big-endian implementation by Intel. When the i596 is operating in @@ -470,11 +470,11 @@ static inline int init_rx_bufs(struct net_device *dev) for (i = 0, rbd = dma->rbds; i < rx_ring_size; i++, rbd++) { dma_addr_t dma_addr; - struct sk_buff *skb = netdev_alloc_skb(dev, PKT_BUF_SZ + 4); + struct sk_buff *skb; + skb = netdev_alloc_skb_ip_align(dev, PKT_BUF_SZ); if (skb == NULL) return -1; - skb_reserve(skb, 2); dma_addr = dma_map_single(dev->dev.parent, skb->data, PKT_BUF_SZ, DMA_FROM_DEVICE); rbd->v_next = rbd+1; @@ -588,7 +588,7 @@ static int init_i596_mem(struct net_device *dev) "%s: i82596 initialization successful\n", dev->name)); - if (request_irq(dev->irq, &i596_interrupt, 0, "i82596", dev)) { + if (request_irq(dev->irq, i596_interrupt, 0, "i82596", dev)) { printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq); goto failed; } @@ -697,12 +697,12 @@ static inline int i596_rx(struct net_device *dev) (dma_addr_t)SWAP32(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE); /* Get fresh skbuff to replace filled one. */ - newskb = netdev_alloc_skb(dev, PKT_BUF_SZ + 4); + newskb = netdev_alloc_skb_ip_align(dev, + PKT_BUF_SZ); if (newskb == NULL) { skb = NULL; /* drop pkt */ goto memory_squeeze; } - skb_reserve(newskb, 2); /* Pass up the skb already on the Rx ring. */ skb_put(skb, pkt_len); @@ -716,7 +716,7 @@ static inline int i596_rx(struct net_device *dev) rbd->b_data = SWAP32(dma_addr); DMA_WBACK_INV(dev, rbd, sizeof(struct i596_rbd)); } else - skb = netdev_alloc_skb(dev, pkt_len + 2); + skb = netdev_alloc_skb_ip_align(dev, pkt_len); memory_squeeze: if (skb == NULL) { /* XXX tulip.c can defer packets here!! */ @@ -730,7 +730,6 @@ memory_squeeze: dma_sync_single_for_cpu(dev->dev.parent, (dma_addr_t)SWAP32(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE); - skb_reserve(skb, 2); memcpy(skb_put(skb, pkt_len), rbd->v_data, pkt_len); dma_sync_single_for_device(dev->dev.parent, (dma_addr_t)SWAP32(rbd->b_data), diff --git a/drivers/net/lib8390.c b/drivers/net/lib8390.c index 256119882b1e..57f25848fe80 100644 --- a/drivers/net/lib8390.c +++ b/drivers/net/lib8390.c @@ -464,8 +464,8 @@ static irqreturn_t __ei_interrupt(int irq, void *dev_id) ei_inb_p(e8390_base + EN0_ISR)); /* !!Assumption!! -- we stay in page 0. Don't break this. */ - while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 - && ++nr_serviced < MAX_SERVICE) + while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 && + ++nr_serviced < MAX_SERVICE) { if (!netif_running(dev)) { printk(KERN_WARNING "%s: interrupt from stopped card\n", dev->name); @@ -721,10 +721,10 @@ static void ei_receive(struct net_device *dev) /* Check for bogosity warned by 3c503 book: the status byte is never written. This happened a lot during testing! This code should be cleaned up someday. */ - if (rx_frame.next != next_frame - && rx_frame.next != next_frame + 1 - && rx_frame.next != next_frame - num_rx_pages - && rx_frame.next != next_frame + 1 - num_rx_pages) { + if (rx_frame.next != next_frame && + rx_frame.next != next_frame + 1 && + rx_frame.next != next_frame - num_rx_pages && + rx_frame.next != next_frame + 1 - num_rx_pages) { ei_local->current_page = rxing_page; ei_outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY); dev->stats.rx_errors++; diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c index f2a197fd47a5..336e7c7a9275 100644 --- a/drivers/net/ll_temac_main.c +++ b/drivers/net/ll_temac_main.c @@ -231,8 +231,8 @@ static void temac_set_multicast_list(struct net_device *ndev) int i; mutex_lock(&lp->indirect_mutex); - if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC) - || ndev->mc_count > MULTICAST_CAM_TABLE_NUM) { + if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC) || + ndev->mc_count > MULTICAST_CAM_TABLE_NUM) { /* * We must make the kernel realise we had to move * into promisc mode or we start all out war on diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 1bc654a73c47..eae4ad749e9d 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -207,20 +207,12 @@ static __net_init int loopback_net_init(struct net *net) out_free_netdev: free_netdev(dev); out: - if (net == &init_net) + if (net_eq(net, &init_net)) panic("loopback: Failed to register netdevice: %d\n", err); return err; } -static __net_exit void loopback_net_exit(struct net *net) -{ - struct net_device *dev = net->loopback_dev; - - unregister_netdev(dev); -} - /* Registered in net/core/dev.c */ struct pernet_operations __net_initdata loopback_net_ops = { .init = loopback_net_init, - .exit = loopback_net_exit, }; diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c index cc3ed9cf28be..e20fefc73c8b 100644 --- a/drivers/net/lp486e.c +++ b/drivers/net/lp486e.c @@ -845,7 +845,7 @@ static int i596_open(struct net_device *dev) { int i; - i = request_irq(dev->irq, &i596_interrupt, IRQF_SHARED, dev->name, dev); + i = request_irq(dev->irq, i596_interrupt, IRQF_SHARED, dev->name, dev); if (i) { printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq); return i; diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c index 149e0ed4a055..23b633e2ac42 100644 --- a/drivers/net/mac89x0.c +++ b/drivers/net/mac89x0.c @@ -222,8 +222,8 @@ struct net_device * __init mac89x0_probe(int unit) int card_present; local_irq_save(flags); - card_present = hwreg_present((void*) ioaddr+4) - && hwreg_present((void*) ioaddr + DATA_PORT); + card_present = (hwreg_present((void*) ioaddr+4) && + hwreg_present((void*) ioaddr + DATA_PORT)); local_irq_restore(flags); if (!card_present) @@ -337,7 +337,7 @@ net_open(struct net_device *dev) writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); /* Grab the interrupt */ - if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) + if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev)) return -EAGAIN; /* Set up the IRQ - Apparently magic */ diff --git a/drivers/net/mace.c b/drivers/net/mace.c index 7d7577b598ea..d9fbad386389 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -897,8 +897,8 @@ static irqreturn_t mace_rxdma_intr(int irq, void *dev_id) if (next >= N_RX_RING) next = 0; np = mp->rx_cmds + next; - if (next != mp->rx_fill - && (ld_le16(&np->xfer_status) & ACTIVE) != 0) { + if (next != mp->rx_fill && + (ld_le16(&np->xfer_status) & ACTIVE) != 0) { printk(KERN_DEBUG "mace: lost a status word\n"); ++mace_lost_status; } else diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c index b3d7d8d77f46..875d361fb79d 100644 --- a/drivers/net/macsonic.c +++ b/drivers/net/macsonic.c @@ -140,7 +140,7 @@ static irqreturn_t macsonic_interrupt(int irq, void *dev_id) static int macsonic_open(struct net_device* dev) { - if (request_irq(dev->irq, &sonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) { + if (request_irq(dev->irq, sonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) { printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq); return -EAGAIN; } @@ -149,7 +149,7 @@ static int macsonic_open(struct net_device* dev) * rupt as well, which must prevent re-entrance of the sonic handler. */ if (dev->irq == IRQ_AUTO_3) - if (request_irq(IRQ_NUBUS_9, &macsonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) { + if (request_irq(IRQ_NUBUS_9, macsonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) { printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, IRQ_NUBUS_9); free_irq(dev->irq, dev); return -EAGAIN; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 2490aa39804c..21a9c9ab4b34 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -29,6 +29,7 @@ #include <linux/if_link.h> #include <linux/if_macvlan.h> #include <net/rtnetlink.h> +#include <net/xfrm.h> #define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE) @@ -38,12 +39,28 @@ struct macvlan_port { struct list_head vlans; }; +/** + * struct macvlan_rx_stats - MACVLAN percpu rx stats + * @rx_packets: number of received packets + * @rx_bytes: number of received bytes + * @multicast: number of received multicast packets + * @rx_errors: number of errors + */ +struct macvlan_rx_stats { + unsigned long rx_packets; + unsigned long rx_bytes; + unsigned long multicast; + unsigned long rx_errors; +}; + struct macvlan_dev { struct net_device *dev; struct list_head list; struct hlist_node hlist; struct macvlan_port *port; struct net_device *lowerdev; + struct macvlan_rx_stats *rx_stats; + enum macvlan_mode mode; }; @@ -101,41 +118,67 @@ static int macvlan_addr_busy(const struct macvlan_port *port, return 0; } +static inline void macvlan_count_rx(const struct macvlan_dev *vlan, + unsigned int len, bool success, + bool multicast) +{ + struct macvlan_rx_stats *rx_stats; + + rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id()); + if (likely(success)) { + rx_stats->rx_packets++;; + rx_stats->rx_bytes += len; + if (multicast) + rx_stats->multicast++; + } else { + rx_stats->rx_errors++; + } +} + +static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev, + const struct ethhdr *eth, bool local) +{ + if (!skb) + return NET_RX_DROP; + + if (local) + return dev_forward_skb(dev, skb); + + skb->dev = dev; + if (!compare_ether_addr_64bits(eth->h_dest, + dev->broadcast)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + + return netif_rx(skb); +} + static void macvlan_broadcast(struct sk_buff *skb, - const struct macvlan_port *port) + const struct macvlan_port *port, + struct net_device *src, + enum macvlan_mode mode) { const struct ethhdr *eth = eth_hdr(skb); const struct macvlan_dev *vlan; struct hlist_node *n; - struct net_device *dev; struct sk_buff *nskb; unsigned int i; + int err; if (skb->protocol == htons(ETH_P_PAUSE)) return; for (i = 0; i < MACVLAN_HASH_SIZE; i++) { hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) { - dev = vlan->dev; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (nskb == NULL) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; + if (vlan->dev == src || !(vlan->mode & mode)) continue; - } - - dev->stats.rx_bytes += skb->len + ETH_HLEN; - dev->stats.rx_packets++; - dev->stats.multicast++; - - nskb->dev = dev; - if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast)) - nskb->pkt_type = PACKET_BROADCAST; - else - nskb->pkt_type = PACKET_MULTICAST; - netif_rx(nskb); + nskb = skb_clone(skb, GFP_ATOMIC); + err = macvlan_broadcast_one(nskb, vlan->dev, eth, + mode == MACVLAN_MODE_BRIDGE); + macvlan_count_rx(vlan, skb->len + ETH_HLEN, + err == NET_RX_SUCCESS, 1); } } } @@ -146,14 +189,34 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) const struct ethhdr *eth = eth_hdr(skb); const struct macvlan_port *port; const struct macvlan_dev *vlan; + const struct macvlan_dev *src; struct net_device *dev; + unsigned int len; port = rcu_dereference(skb->dev->macvlan_port); if (port == NULL) return skb; if (is_multicast_ether_addr(eth->h_dest)) { - macvlan_broadcast(skb, port); + src = macvlan_hash_lookup(port, eth->h_source); + if (!src) + /* frame comes from an external address */ + macvlan_broadcast(skb, port, NULL, + MACVLAN_MODE_PRIVATE | + MACVLAN_MODE_VEPA | + MACVLAN_MODE_BRIDGE); + else if (src->mode == MACVLAN_MODE_VEPA) + /* flood to everyone except source */ + macvlan_broadcast(skb, port, src->dev, + MACVLAN_MODE_VEPA | + MACVLAN_MODE_BRIDGE); + else if (src->mode == MACVLAN_MODE_BRIDGE) + /* + * flood only to VEPA ports, bridge ports + * already saw the frame on the way out. + */ + macvlan_broadcast(skb, port, src->dev, + MACVLAN_MODE_VEPA); return skb; } @@ -166,16 +229,11 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) kfree_skb(skb); return NULL; } - + len = skb->len + ETH_HLEN; skb = skb_share_check(skb, GFP_ATOMIC); - if (skb == NULL) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; + macvlan_count_rx(vlan, len, skb != NULL, 0); + if (!skb) return NULL; - } - - dev->stats.rx_bytes += skb->len + ETH_HLEN; - dev->stats.rx_packets++; skb->dev = dev; skb->pkt_type = PACKET_HOST; @@ -184,25 +242,53 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) return NULL; } +static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev) +{ + const struct macvlan_dev *vlan = netdev_priv(dev); + const struct macvlan_port *port = vlan->port; + const struct macvlan_dev *dest; + + if (vlan->mode == MACVLAN_MODE_BRIDGE) { + const struct ethhdr *eth = (void *)skb->data; + + /* send to other bridge ports directly */ + if (is_multicast_ether_addr(eth->h_dest)) { + macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE); + goto xmit_world; + } + + dest = macvlan_hash_lookup(port, eth->h_dest); + if (dest && dest->mode == MACVLAN_MODE_BRIDGE) { + unsigned int length = skb->len + ETH_HLEN; + int ret = dev_forward_skb(dest->dev, skb); + macvlan_count_rx(dest, length, + ret == NET_RX_SUCCESS, 0); + + return NET_XMIT_SUCCESS; + } + } + +xmit_world: + skb->dev = vlan->lowerdev; + return dev_queue_xmit(skb); +} + static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev) { int i = skb_get_queue_mapping(skb); struct netdev_queue *txq = netdev_get_tx_queue(dev, i); - const struct macvlan_dev *vlan = netdev_priv(dev); unsigned int len = skb->len; int ret; - skb->dev = vlan->lowerdev; - ret = dev_queue_xmit(skb); - + ret = macvlan_queue_xmit(skb, dev); if (likely(ret == NET_XMIT_SUCCESS)) { txq->tx_packets++; txq->tx_bytes += len; } else txq->tx_dropped++; - return NETDEV_TX_OK; + return ret; } static int macvlan_hard_header(struct sk_buff *skb, struct net_device *dev, @@ -366,9 +452,47 @@ static int macvlan_init(struct net_device *dev) macvlan_set_lockdep_class(dev); + vlan->rx_stats = alloc_percpu(struct macvlan_rx_stats); + if (!vlan->rx_stats) + return -ENOMEM; + return 0; } +static void macvlan_uninit(struct net_device *dev) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + + free_percpu(vlan->rx_stats); +} + +static struct net_device_stats *macvlan_dev_get_stats(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct macvlan_dev *vlan = netdev_priv(dev); + + dev_txq_stats_fold(dev, stats); + + if (vlan->rx_stats) { + struct macvlan_rx_stats *p, rx = {0}; + int i; + + for_each_possible_cpu(i) { + p = per_cpu_ptr(vlan->rx_stats, i); + rx.rx_packets += p->rx_packets; + rx.rx_bytes += p->rx_bytes; + rx.rx_errors += p->rx_errors; + rx.multicast += p->multicast; + } + stats->rx_packets = rx.rx_packets; + stats->rx_bytes = rx.rx_bytes; + stats->rx_errors = rx.rx_errors; + stats->rx_dropped = rx.rx_errors; + stats->multicast = rx.multicast; + } + return stats; +} + static void macvlan_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { @@ -405,6 +529,7 @@ static const struct ethtool_ops macvlan_ethtool_ops = { static const struct net_device_ops macvlan_netdev_ops = { .ndo_init = macvlan_init, + .ndo_uninit = macvlan_uninit, .ndo_open = macvlan_open, .ndo_stop = macvlan_stop, .ndo_start_xmit = macvlan_start_xmit, @@ -412,6 +537,7 @@ static const struct net_device_ops macvlan_netdev_ops = { .ndo_change_rx_flags = macvlan_change_rx_flags, .ndo_set_mac_address = macvlan_set_mac_address, .ndo_set_multicast_list = macvlan_set_multicast_list, + .ndo_get_stats = macvlan_dev_get_stats, .ndo_validate_addr = eth_validate_addr, }; @@ -456,25 +582,6 @@ static void macvlan_port_destroy(struct net_device *dev) kfree(port); } -static void macvlan_transfer_operstate(struct net_device *dev) -{ - struct macvlan_dev *vlan = netdev_priv(dev); - const struct net_device *lowerdev = vlan->lowerdev; - - if (lowerdev->operstate == IF_OPER_DORMANT) - netif_dormant_on(dev); - else - netif_dormant_off(dev); - - if (netif_carrier_ok(lowerdev)) { - if (!netif_carrier_ok(dev)) - netif_carrier_on(dev); - } else { - if (netif_carrier_ok(dev)) - netif_carrier_off(dev); - } -} - static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) { if (tb[IFLA_ADDRESS]) { @@ -483,6 +590,17 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) return -EADDRNOTAVAIL; } + + if (data && data[IFLA_MACVLAN_MODE]) { + switch (nla_get_u32(data[IFLA_MACVLAN_MODE])) { + case MACVLAN_MODE_PRIVATE: + case MACVLAN_MODE_VEPA: + case MACVLAN_MODE_BRIDGE: + break; + default: + return -EINVAL; + } + } return 0; } @@ -505,7 +623,7 @@ static int macvlan_get_tx_queues(struct net *net, return 0; } -static int macvlan_newlink(struct net_device *dev, +static int macvlan_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct macvlan_dev *vlan = netdev_priv(dev); @@ -516,7 +634,7 @@ static int macvlan_newlink(struct net_device *dev, if (!tb[IFLA_LINK]) return -EINVAL; - lowerdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK])); + lowerdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); if (lowerdev == NULL) return -ENODEV; @@ -547,27 +665,61 @@ static int macvlan_newlink(struct net_device *dev, vlan->dev = dev; vlan->port = port; + vlan->mode = MACVLAN_MODE_VEPA; + if (data && data[IFLA_MACVLAN_MODE]) + vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); + err = register_netdevice(dev); if (err < 0) return err; list_add_tail(&vlan->list, &port->vlans); - macvlan_transfer_operstate(dev); + netif_stacked_transfer_operstate(lowerdev, dev); return 0; } -static void macvlan_dellink(struct net_device *dev) +static void macvlan_dellink(struct net_device *dev, struct list_head *head) { struct macvlan_dev *vlan = netdev_priv(dev); struct macvlan_port *port = vlan->port; list_del(&vlan->list); - unregister_netdevice(dev); + unregister_netdevice_queue(dev, head); if (list_empty(&port->vlans)) macvlan_port_destroy(port->dev); } +static int macvlan_changelink(struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + if (data && data[IFLA_MACVLAN_MODE]) + vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); + return 0; +} + +static size_t macvlan_get_size(const struct net_device *dev) +{ + return nla_total_size(4); +} + +static int macvlan_fill_info(struct sk_buff *skb, + const struct net_device *dev) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + + NLA_PUT_U32(skb, IFLA_MACVLAN_MODE, vlan->mode); + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = { + [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, +}; + static struct rtnl_link_ops macvlan_link_ops __read_mostly = { .kind = "macvlan", .priv_size = sizeof(struct macvlan_dev), @@ -576,6 +728,11 @@ static struct rtnl_link_ops macvlan_link_ops __read_mostly = { .validate = macvlan_validate, .newlink = macvlan_newlink, .dellink = macvlan_dellink, + .maxtype = IFLA_MACVLAN_MAX, + .policy = macvlan_policy, + .changelink = macvlan_changelink, + .get_size = macvlan_get_size, + .fill_info = macvlan_fill_info, }; static int macvlan_device_event(struct notifier_block *unused, @@ -592,7 +749,8 @@ static int macvlan_device_event(struct notifier_block *unused, switch (event) { case NETDEV_CHANGE: list_for_each_entry(vlan, &port->vlans, list) - macvlan_transfer_operstate(vlan->dev); + netif_stacked_transfer_operstate(vlan->lowerdev, + vlan->dev); break; case NETDEV_FEAT_CHANGE: list_for_each_entry(vlan, &port->vlans, list) { @@ -603,7 +761,7 @@ static int macvlan_device_event(struct notifier_block *unused, break; case NETDEV_UNREGISTER: list_for_each_entry_safe(vlan, next, &port->vlans, list) - macvlan_dellink(vlan->dev); + macvlan_dellink(vlan->dev, NULL); break; } return NOTIFY_DONE; diff --git a/drivers/net/mdio.c b/drivers/net/mdio.c index 21f8754fcf4c..e85bf04cf813 100644 --- a/drivers/net/mdio.c +++ b/drivers/net/mdio.c @@ -162,6 +162,10 @@ static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr) result |= ADVERTISED_100baseT_Half; if (reg & ADVERTISE_100FULL) result |= ADVERTISED_100baseT_Full; + if (reg & ADVERTISE_PAUSE_CAP) + result |= ADVERTISED_Pause; + if (reg & ADVERTISE_PAUSE_ASYM) + result |= ADVERTISED_Asym_Pause; return result; } @@ -344,11 +348,9 @@ void mdio45_ethtool_spauseparam_an(const struct mdio_if_info *mdio, old_adv = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - adv = old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); - if (ecmd->autoneg) - adv |= mii_advertise_flowctrl( - (ecmd->rx_pause ? FLOW_CTRL_RX : 0) | - (ecmd->tx_pause ? FLOW_CTRL_TX : 0)); + adv = ((old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) | + mii_advertise_flowctrl((ecmd->rx_pause ? FLOW_CTRL_RX : 0) | + (ecmd->tx_pause ? FLOW_CTRL_TX : 0))); if (adv != old_adv) { mdio->mdio_write(mdio->dev, mdio->prtad, MDIO_MMD_AN, MDIO_AN_ADVERTISE, adv); diff --git a/drivers/net/mipsnet.c b/drivers/net/mipsnet.c index 8ea98bd89ff1..8e9704f5c122 100644 --- a/drivers/net/mipsnet.c +++ b/drivers/net/mipsnet.c @@ -211,7 +211,7 @@ static int mipsnet_open(struct net_device *dev) { int err; - err = request_irq(dev->irq, &mipsnet_interrupt, + err = request_irq(dev->irq, mipsnet_interrupt, IRQF_SHARED, dev->name, (void *) dev); if (err) { release_region(dev->base_addr, sizeof(struct mipsnet_regs)); diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c index 03b781a7a182..829b9ec9ff67 100644 --- a/drivers/net/mlx4/en_rx.c +++ b/drivers/net/mlx4/en_rx.c @@ -204,7 +204,7 @@ static void mlx4_en_free_rx_desc(struct mlx4_en_priv *priv, en_dbg(DRV, priv, "Freeing fragment:%d\n", nr); dma = be64_to_cpu(rx_desc->data[nr].addr); - en_dbg(DRV, priv, "Unmaping buffer at dma:0x%llx\n", (u64) dma); + en_dbg(DRV, priv, "Unmapping buffer at dma:0x%llx\n", (u64) dma); pci_unmap_single(mdev->pdev, dma, skb_frags[nr].size, PCI_DMA_FROMDEVICE); put_page(skb_frags[nr].page); diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index 8c7279965b44..3d1396af9462 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -47,7 +47,7 @@ enum { static int inline_thold __read_mostly = MAX_INLINE; module_param_named(inline_thold, inline_thold, int, 0444); -MODULE_PARM_DESC(inline_thold, "treshold for using inline data"); +MODULE_PARM_DESC(inline_thold, "threshold for using inline data"); int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, u32 size, diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index 4376147b0ea0..82c3ebc584e3 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h @@ -162,7 +162,7 @@ enum { #define MLX4_EN_DEF_RX_PAUSE 1 #define MLX4_EN_DEF_TX_PAUSE 1 -/* Interval between sucessive polls in the Tx routine when polling is used +/* Interval between successive polls in the Tx routine when polling is used instead of interrupts (in per-core Tx rings) - should be power of 2 */ #define MLX4_EN_TX_POLL_MODER 16 #define MLX4_EN_TX_POLL_TIMEOUT (HZ / 4) diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index b62e61d4ca3e..796a493f95ab 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -849,7 +849,7 @@ no_csum: return 0; } -static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); int queue; diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index f3624517cb0e..d38921906bb7 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -207,7 +207,6 @@ struct myri10ge_priv { int big_bytes; int max_intr_slots; struct net_device *dev; - struct net_device_stats stats; spinlock_t stats_lock; u8 __iomem *sram; int sram_size; @@ -264,6 +263,10 @@ static char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat"; static char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat"; static char *myri10ge_fw_rss_unaligned = "myri10ge_rss_ethp_z8e.dat"; static char *myri10ge_fw_rss_aligned = "myri10ge_rss_eth_z8e.dat"; +MODULE_FIRMWARE("myri10ge_ethp_z8e.dat"); +MODULE_FIRMWARE("myri10ge_eth_z8e.dat"); +MODULE_FIRMWARE("myri10ge_rss_ethp_z8e.dat"); +MODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat"); static char *myri10ge_fw_name = NULL; module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR); @@ -407,8 +410,8 @@ myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd, * and try to get the completion quickly * (1ms will be enough for those commands) */ for (sleep_total = 0; - sleep_total < 1000 - && response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT); + sleep_total < 1000 && + response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT); sleep_total += 10) { udelay(10); mb(); @@ -416,8 +419,8 @@ myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd, } else { /* use msleep for most command */ for (sleep_total = 0; - sleep_total < 15 - && response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT); + sleep_total < 15 && + response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT); sleep_total++) msleep(1); } @@ -554,8 +557,8 @@ myri10ge_validate_firmware(struct myri10ge_priv *mgp, sscanf(mgp->fw_version, "%d.%d.%d", &mgp->fw_ver_major, &mgp->fw_ver_minor, &mgp->fw_ver_tiny); - if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR - && mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) { + if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR && + mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) { dev_err(dev, "Found firmware version %s\n", mgp->fw_version); dev_err(dev, "Driver needs %d.%d\n", MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); @@ -1409,8 +1412,8 @@ myri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index) } /* start the queue if we've stopped it */ - if (netif_tx_queue_stopped(dev_queue) - && tx->req - tx->done < (tx->mask >> 1)) { + if (netif_tx_queue_stopped(dev_queue) && + tx->req - tx->done < (tx->mask >> 1)) { tx->wake_queue++; netif_tx_wake_queue(dev_queue); } @@ -1832,7 +1835,7 @@ myri10ge_get_ethtool_stats(struct net_device *netdev, /* force stats update */ (void)myri10ge_get_stats(netdev); for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++) - data[i] = ((unsigned long *)&mgp->stats)[i]; + data[i] = ((unsigned long *)&netdev->stats)[i]; data[i++] = (unsigned int)mgp->tx_boundary; data[i++] = (unsigned int)mgp->wc_enabled; @@ -3002,7 +3005,7 @@ static struct net_device_stats *myri10ge_get_stats(struct net_device *dev) { struct myri10ge_priv *mgp = netdev_priv(dev); struct myri10ge_slice_netstats *slice_stats; - struct net_device_stats *stats = &mgp->stats; + struct net_device_stats *stats = &dev->stats; int i; spin_lock(&mgp->stats_lock); diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c index 29ebebc6a95b..b3513ad3b703 100644 --- a/drivers/net/myri_sbus.c +++ b/drivers/net/myri_sbus.c @@ -1084,7 +1084,7 @@ static int __devinit myri_sbus_probe(struct of_device *op, const struct of_devic /* Register interrupt handler now. */ DET(("Requesting MYRIcom IRQ line.\n")); - if (request_irq(dev->irq, &myri_interrupt, + if (request_irq(dev->irq, myri_interrupt, IRQF_SHARED, "MyriCOM Ethernet", (void *) dev)) { printk("MyriCOM: Cannot register interrupt handler.\n"); goto err; diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index b2722c44337e..797fe164ce27 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -683,8 +683,8 @@ static ssize_t natsemi_set_dspcfg_workaround(struct device *dev, /* Find out the new setting */ if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1)) new_setting = 1; - else if (!strncmp("off", buf, count - 1) - || !strncmp("0", buf, count - 1)) + else if (!strncmp("off", buf, count - 1) || + !strncmp("0", buf, count - 1)) new_setting = 0; else return count; @@ -757,8 +757,8 @@ static void __devinit natsemi_init_media (struct net_device *dev) np->autoneg = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE; np->advertising= mdio_read(dev, MII_ADVERTISE); - if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL - && netif_msg_probe(np)) { + if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL && + netif_msg_probe(np)) { printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s " "10%s %s duplex.\n", pci_name(np->pci_dev), @@ -1153,8 +1153,8 @@ static void init_phy_fixup(struct net_device *dev) tmp = mdio_read(dev, MII_BMCR); if (np->autoneg == AUTONEG_ENABLE) { /* renegotiate if something changed */ - if ((tmp & BMCR_ANENABLE) == 0 - || np->advertising != mdio_read(dev, MII_ADVERTISE)) + if ((tmp & BMCR_ANENABLE) == 0 || + np->advertising != mdio_read(dev, MII_ADVERTISE)) { /* turn on autonegotiation and force negotiation */ tmp |= (BMCR_ANENABLE | BMCR_ANRESTART); @@ -1535,7 +1535,7 @@ static int netdev_open(struct net_device *dev) /* Reset the chip, just in case. */ natsemi_reset(dev); - i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev); + i = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev); if (i) return i; if (netif_msg_ifup(np)) @@ -2164,8 +2164,8 @@ static void netdev_tx_done(struct net_device *dev) dev_kfree_skb_irq(np->tx_skbuff[entry]); np->tx_skbuff[entry] = NULL; } - if (netif_queue_stopped(dev) - && np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { + if (netif_queue_stopped(dev) && + np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { /* The ring is no longer full, wake queue. */ netif_wake_queue(dev); } @@ -2343,8 +2343,8 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do) /* Omit CRC size. */ /* Check if the packet is long enough to accept * without copying to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) { + if (pkt_len < rx_copybreak && + (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) { /* 16 byte align the IP header */ skb_reserve(skb, RX_OFFSET); pci_dma_sync_single_for_cpu(np->pci_dev, @@ -2390,8 +2390,8 @@ static void netdev_error(struct net_device *dev, int intr_status) spin_lock(&np->lock); if (intr_status & LinkChange) { u16 lpa = mdio_read(dev, MII_LPA); - if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE - && netif_msg_link(np)) { + if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE && + netif_msg_link(np)) { printk(KERN_INFO "%s: Autonegotiation advertising" " %#04x partner %#04x.\n", dev->name, @@ -2488,8 +2488,8 @@ static void __set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ rx_mode = RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { rx_mode = RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys; } else { diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c index 9f4235466d59..64770298c4f7 100644 --- a/drivers/net/netx-eth.c +++ b/drivers/net/netx-eth.c @@ -212,7 +212,7 @@ static int netx_eth_open(struct net_device *ndev) struct netx_eth_priv *priv = netdev_priv(ndev); if (request_irq - (ndev->irq, &netx_eth_interrupt, IRQF_SHARED, ndev->name, ndev)) + (ndev->irq, netx_eth_interrupt, IRQF_SHARED, ndev->name, ndev)) return -EAGAIN; writel(ndev->dev_addr[0] | @@ -510,3 +510,6 @@ module_exit(netx_eth_cleanup); MODULE_AUTHOR("Sascha Hauer, Pengutronix"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" CARDNAME); +MODULE_FIRMWARE("xc0.bin"); +MODULE_FIRMWARE("xc1.bin"); +MODULE_FIRMWARE("xc2.bin"); diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index e1237b802872..76cd1f3e9fc8 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -53,8 +53,8 @@ #define _NETXEN_NIC_LINUX_MAJOR 4 #define _NETXEN_NIC_LINUX_MINOR 0 -#define _NETXEN_NIC_LINUX_SUBVERSION 50 -#define NETXEN_NIC_LINUX_VERSIONID "4.0.50" +#define _NETXEN_NIC_LINUX_SUBVERSION 65 +#define NETXEN_NIC_LINUX_VERSIONID "4.0.65" #define NETXEN_VERSION_CODE(a, b, c) (((a) << 24) + ((b) << 16) + (c)) #define _major(v) (((v) >> 24) & 0xff) @@ -74,8 +74,6 @@ #define NETXEN_FLASH_TOTAL_SIZE (NETXEN_NUM_FLASH_SECTORS \ * NETXEN_FLASH_SECTOR_SIZE) -#define PHAN_VENDOR_ID 0x4040 - #define RCV_DESC_RINGSIZE(rds_ring) \ (sizeof(struct rcv_desc) * (rds_ring)->num_desc) #define RCV_BUFF_RINGSIZE(rds_ring) \ @@ -117,9 +115,11 @@ #define NX_P3_B0 0x40 #define NX_P3_B1 0x41 #define NX_P3_B2 0x42 +#define NX_P3P_A0 0x50 #define NX_IS_REVISION_P2(REVISION) (REVISION <= NX_P2_C1) #define NX_IS_REVISION_P3(REVISION) (REVISION >= NX_P3_A0) +#define NX_IS_REVISION_P3P(REVISION) (REVISION >= NX_P3P_A0) #define FIRST_PAGE_GROUP_START 0 #define FIRST_PAGE_GROUP_END 0x100000 @@ -419,6 +419,34 @@ struct status_desc { __le64 status_desc_data[2]; } __attribute__ ((aligned(16))); +/* UNIFIED ROMIMAGE *************************/ +#define NX_UNI_FW_MIN_SIZE 0x3eb000 +#define NX_UNI_DIR_SECT_PRODUCT_TBL 0x0 +#define NX_UNI_DIR_SECT_BOOTLD 0x6 +#define NX_UNI_DIR_SECT_FW 0x7 + +/*Offsets */ +#define NX_UNI_CHIP_REV_OFF 10 +#define NX_UNI_FLAGS_OFF 11 +#define NX_UNI_BIOS_VERSION_OFF 12 +#define NX_UNI_BOOTLD_IDX_OFF 27 +#define NX_UNI_FIRMWARE_IDX_OFF 29 + +struct uni_table_desc{ + uint32_t findex; + uint32_t num_entries; + uint32_t entry_size; + uint32_t reserved[5]; +}; + +struct uni_data_desc{ + uint32_t findex; + uint32_t size; + uint32_t reserved[5]; +}; + +/* UNIFIED ROMIMAGE *************************/ + /* The version of the main data structure */ #define NETXEN_BDINFO_VERSION 1 @@ -485,7 +513,15 @@ struct status_desc { #define NX_P2_MN_ROMIMAGE 0 #define NX_P3_CT_ROMIMAGE 1 #define NX_P3_MN_ROMIMAGE 2 -#define NX_FLASH_ROMIMAGE 3 +#define NX_UNIFIED_ROMIMAGE 3 +#define NX_FLASH_ROMIMAGE 4 +#define NX_UNKNOWN_ROMIMAGE 0xff + +#define NX_P2_MN_ROMIMAGE_NAME "nxromimg.bin" +#define NX_P3_CT_ROMIMAGE_NAME "nx3fwct.bin" +#define NX_P3_MN_ROMIMAGE_NAME "nx3fwmn.bin" +#define NX_UNIFIED_ROMIMAGE_NAME "phanfw.bin" +#define NX_FLASH_ROMIMAGE_NAME "flash" extern char netxen_nic_driver_name[]; @@ -543,13 +579,16 @@ struct netxen_hardware_context { void __iomem *pci_base1; void __iomem *pci_base2; void __iomem *db_base; + void __iomem *ocm_win_crb; + unsigned long db_len; unsigned long pci_len0; - int qdr_sn_window; - int ddr_mn_window; - u32 mn_win_crb; - u32 ms_win_crb; + u32 ocm_win; + u32 crb_win; + + rwlock_t crb_lock; + spinlock_t mem_lock; u8 cut_through; u8 revision_id; @@ -1039,6 +1078,9 @@ typedef struct { #define LINKEVENT_LINKSPEED_MBPS 0 #define LINKEVENT_LINKSPEED_ENCODED 1 +#define AUTO_FW_RESET_ENABLED 0xEF10AF12 +#define AUTO_FW_RESET_DISABLED 0xDCBAAF12 + /* firmware response header: * 63:58 - message type * 57:56 - owner @@ -1086,6 +1128,7 @@ typedef struct { #define NETXEN_NIC_MSIX_ENABLED 0x04 #define NETXEN_NIC_LRO_ENABLED 0x08 #define NETXEN_NIC_BRIDGE_ENABLED 0X10 +#define NETXEN_NIC_DIAG_ENABLED 0x20 #define NETXEN_IS_MSI_FAMILY(adapter) \ ((adapter)->flags & (NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED)) @@ -1115,10 +1158,6 @@ struct netxen_adapter { struct pci_dev *pdev; struct list_head mac_list; - u32 curr_window; - u32 crb_win; - rwlock_t adapter_lock; - spinlock_t tx_clean_lock; u16 num_txd; @@ -1182,11 +1221,10 @@ struct netxen_adapter { u32 (*crb_read)(struct netxen_adapter *, ulong); int (*crb_write)(struct netxen_adapter *, ulong, u32); - int (*pci_mem_read)(struct netxen_adapter *, u64, void *, int); - int (*pci_mem_write)(struct netxen_adapter *, u64, void *, int); + int (*pci_mem_read)(struct netxen_adapter *, u64, u64 *); + int (*pci_mem_write)(struct netxen_adapter *, u64, u64); - unsigned long (*pci_set_window)(struct netxen_adapter *, - unsigned long long); + int (*pci_set_window)(struct netxen_adapter *, u64, u32 *); u32 (*io_read)(struct netxen_adapter *, void __iomem *); void (*io_write)(struct netxen_adapter *, void __iomem *, u32); @@ -1205,12 +1243,10 @@ struct netxen_adapter { struct work_struct tx_timeout_task; - struct net_device_stats net_stats; - nx_nic_intr_coalesce_t coal; unsigned long state; - u32 resv5; + __le32 file_prd_off; /*File fw product offset*/ u32 fw_version; const struct firmware *fw; }; @@ -1273,7 +1309,7 @@ int netxen_load_firmware(struct netxen_adapter *adapter); int netxen_need_fw_reset(struct netxen_adapter *adapter); void netxen_request_firmware(struct netxen_adapter *adapter); void netxen_release_firmware(struct netxen_adapter *adapter); -int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose); +int netxen_pinit_from_rom(struct netxen_adapter *adapter); int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp); int netxen_rom_fast_read_words(struct netxen_adapter *adapter, int addr, diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index 714f38791a9a..ddd704ae0188 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -85,11 +85,9 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) strncpy(drvinfo->driver, netxen_nic_driver_name, 32); strncpy(drvinfo->version, NETXEN_NIC_LINUX_VERSIONID, 32); - read_lock(&adapter->adapter_lock); fw_major = NXRD32(adapter, NETXEN_FW_VERSION_MAJOR); fw_minor = NXRD32(adapter, NETXEN_FW_VERSION_MINOR); fw_build = NXRD32(adapter, NETXEN_FW_VERSION_SUB); - read_unlock(&adapter->adapter_lock); sprintf(drvinfo->fw_version, "%d.%d.%d", fw_major, fw_minor, fw_build); strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); @@ -259,18 +257,18 @@ netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) /* read which mode */ if (adapter->ahw.port_type == NETXEN_NIC_GBE) { /* autonegotiation */ - if (adapter->phy_write - && adapter->phy_write(adapter, - NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, - ecmd->autoneg) != 0) + if (adapter->phy_write && + adapter->phy_write(adapter, + NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, + ecmd->autoneg) != 0) return -EIO; else adapter->link_autoneg = ecmd->autoneg; - if (adapter->phy_read - && adapter->phy_read(adapter, - NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, - &status) != 0) + if (adapter->phy_read && + adapter->phy_read(adapter, + NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, + &status) != 0) return -EIO; /* speed */ @@ -290,10 +288,10 @@ netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) netxen_clear_phy_duplex(status); if (ecmd->duplex == DUPLEX_FULL) netxen_set_phy_duplex(status); - if (adapter->phy_write - && adapter->phy_write(adapter, - NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, - *((int *)&status)) != 0) + if (adapter->phy_write && + adapter->phy_write(adapter, + NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, + *((int *)&status)) != 0) return -EIO; else { adapter->link_speed = ecmd->speed; @@ -444,10 +442,10 @@ static u32 netxen_nic_test_link(struct net_device *dev) /* read which mode */ if (adapter->ahw.port_type == NETXEN_NIC_GBE) { - if (adapter->phy_read - && adapter->phy_read(adapter, - NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, - &status) != 0) + if (adapter->phy_read && + adapter->phy_read(adapter, + NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, + &status) != 0) return -EIO; else { val = netxen_get_phy_link(status); @@ -690,8 +688,8 @@ static int netxen_nic_reg_test(struct net_device *dev) u32 data_read, data_written; data_read = NXRD32(adapter, NETXEN_PCIX_PH_REG(0)); - if ((data_read & 0xffff) != PHAN_VENDOR_ID) - return 1; + if ((data_read & 0xffff) != adapter->pdev->vendor) + return 1; data_written = (u32)0xa5a5a5a5; diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h index 17bb3818d84e..d138fc22927a 100644 --- a/drivers/net/netxen/netxen_nic_hdr.h +++ b/drivers/net/netxen/netxen_nic_hdr.h @@ -664,40 +664,51 @@ enum { #define NETXEN_NIU_AP_STATION_ADDR_0(I) (NETXEN_CRB_NIU+0xa0040+(I)*0x10000) #define NETXEN_NIU_AP_STATION_ADDR_1(I) (NETXEN_CRB_NIU+0xa0044+(I)*0x10000) + +#define TEST_AGT_CTRL (0x00) + +#define TA_CTL_START 1 +#define TA_CTL_ENABLE 2 +#define TA_CTL_WRITE 4 +#define TA_CTL_BUSY 8 + /* * Register offsets for MN */ -#define MIU_CONTROL (0x000) -#define MIU_TEST_AGT_CTRL (0x090) -#define MIU_TEST_AGT_ADDR_LO (0x094) -#define MIU_TEST_AGT_ADDR_HI (0x098) -#define MIU_TEST_AGT_WRDATA_LO (0x0a0) -#define MIU_TEST_AGT_WRDATA_HI (0x0a4) -#define MIU_TEST_AGT_WRDATA(i) (0x0a0+(4*(i))) -#define MIU_TEST_AGT_RDDATA_LO (0x0a8) -#define MIU_TEST_AGT_RDDATA_HI (0x0ac) -#define MIU_TEST_AGT_RDDATA(i) (0x0a8+(4*(i))) -#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8 -#define MIU_TEST_AGT_UPPER_ADDR(off) (0) - -/* MIU_TEST_AGT_CTRL flags. work for SIU as well */ -#define MIU_TA_CTL_START 1 -#define MIU_TA_CTL_ENABLE 2 -#define MIU_TA_CTL_WRITE 4 -#define MIU_TA_CTL_BUSY 8 - -#define SIU_TEST_AGT_CTRL (0x060) -#define SIU_TEST_AGT_ADDR_LO (0x064) -#define SIU_TEST_AGT_ADDR_HI (0x078) -#define SIU_TEST_AGT_WRDATA_LO (0x068) -#define SIU_TEST_AGT_WRDATA_HI (0x06c) -#define SIU_TEST_AGT_WRDATA(i) (0x068+(4*(i))) -#define SIU_TEST_AGT_RDDATA_LO (0x070) -#define SIU_TEST_AGT_RDDATA_HI (0x074) -#define SIU_TEST_AGT_RDDATA(i) (0x070+(4*(i))) - -#define SIU_TEST_AGT_ADDR_MASK 0x3ffff8 -#define SIU_TEST_AGT_UPPER_ADDR(off) ((off)>>22) +#define MIU_TEST_AGT_BASE (0x90) + +#define MIU_TEST_AGT_ADDR_LO (0x04) +#define MIU_TEST_AGT_ADDR_HI (0x08) +#define MIU_TEST_AGT_WRDATA_LO (0x10) +#define MIU_TEST_AGT_WRDATA_HI (0x14) +#define MIU_TEST_AGT_WRDATA_UPPER_LO (0x20) +#define MIU_TEST_AGT_WRDATA_UPPER_HI (0x24) +#define MIU_TEST_AGT_WRDATA(i) (0x10+(0x10*((i)>>1))+(4*((i)&1))) +#define MIU_TEST_AGT_RDDATA_LO (0x18) +#define MIU_TEST_AGT_RDDATA_HI (0x1c) +#define MIU_TEST_AGT_RDDATA_UPPER_LO (0x28) +#define MIU_TEST_AGT_RDDATA_UPPER_HI (0x2c) +#define MIU_TEST_AGT_RDDATA(i) (0x18+(0x10*((i)>>1))+(4*((i)&1))) + +#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8 +#define MIU_TEST_AGT_UPPER_ADDR(off) (0) + +/* + * Register offsets for MS + */ +#define SIU_TEST_AGT_BASE (0x60) + +#define SIU_TEST_AGT_ADDR_LO (0x04) +#define SIU_TEST_AGT_ADDR_HI (0x18) +#define SIU_TEST_AGT_WRDATA_LO (0x08) +#define SIU_TEST_AGT_WRDATA_HI (0x0c) +#define SIU_TEST_AGT_WRDATA(i) (0x08+(4*(i))) +#define SIU_TEST_AGT_RDDATA_LO (0x10) +#define SIU_TEST_AGT_RDDATA_HI (0x14) +#define SIU_TEST_AGT_RDDATA(i) (0x10+(4*(i))) + +#define SIU_TEST_AGT_ADDR_MASK 0x3ffff8 +#define SIU_TEST_AGT_UPPER_ADDR(off) ((off)>>22) /* XG Link status */ #define XG_LINK_UP 0x10 @@ -859,6 +870,9 @@ enum { (PCIX_SN_WINDOW_F0 + (0x20 * (func))) :\ (PCIX_SN_WINDOW_F4 + (0x10 * ((func)-4)))) +#define PCIX_OCM_WINDOW (0x10800) +#define PCIX_OCM_WINDOW_REG(func) (PCIX_OCM_WINDOW + 0x20 * (func)) + #define PCIX_TARGET_STATUS (0x10118) #define PCIX_TARGET_STATUS_F1 (0x10160) #define PCIX_TARGET_STATUS_F2 (0x10164) diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index 52a3798d8d94..2e364fee3cbb 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -31,6 +31,7 @@ #define MASK(n) ((1ULL<<(n))-1) #define MN_WIN(addr) (((addr & 0x1fc0000) >> 1) | ((addr >> 25) & 0x3ff)) #define OCM_WIN(addr) (((addr & 0x1ff0000) >> 1) | ((addr >> 25) & 0x3ff)) +#define OCM_WIN_P3P(addr) (addr & 0xffc0000) #define MS_WIN(addr) (addr & 0x0ffc0000) #define GET_MEM_OFFS_2M(addr) (addr & MASK(18)) @@ -41,6 +42,11 @@ #define CRB_HI(off) ((crb_hub_agt[CRB_BLK(off)] << 20) | ((off) & 0xf0000)) #define CRB_INDIRECT_2M (0x1e0000UL) +static void netxen_nic_io_write_128M(struct netxen_adapter *adapter, + void __iomem *addr, u32 data); +static u32 netxen_nic_io_read_128M(struct netxen_adapter *adapter, + void __iomem *addr); + #ifndef readq static inline u64 readq(void __iomem *addr) { @@ -326,7 +332,7 @@ netxen_pcie_sem_lock(struct netxen_adapter *adapter, int sem, u32 id_reg) if (done == 1) break; if (++timeout >= NETXEN_PCIE_SEM_TIMEOUT) - return -1; + return -EIO; msleep(1); } @@ -1073,89 +1079,71 @@ int netxen_p3_get_mac_addr(struct netxen_adapter *adapter, __le64 *mac) * Changes the CRB window to the specified window. */ static void -netxen_nic_pci_change_crbwindow_128M(struct netxen_adapter *adapter, u32 wndw) +netxen_nic_pci_set_crbwindow_128M(struct netxen_adapter *adapter, + u32 window) { void __iomem *offset; - u32 tmp; - int count = 0; - uint8_t func = adapter->ahw.pci_func; + int count = 10; + u8 func = adapter->ahw.pci_func; - if (adapter->curr_window == wndw) + if (adapter->ahw.crb_win == window) return; - /* - * Move the CRB window. - * We need to write to the "direct access" region of PCI - * to avoid a race condition where the window register has - * not been successfully written across CRB before the target - * register address is received by PCI. The direct region bypasses - * the CRB bus. - */ + offset = PCI_OFFSET_SECOND_RANGE(adapter, NETXEN_PCIX_PH_REG(PCIE_CRB_WINDOW_REG(func))); - if (wndw & 0x1) - wndw = NETXEN_WINDOW_ONE; + writel(window, offset); + do { + if (window == readl(offset)) + break; - writel(wndw, offset); + if (printk_ratelimit()) + dev_warn(&adapter->pdev->dev, + "failed to set CRB window to %d\n", + (window == NETXEN_WINDOW_ONE)); + udelay(1); - /* MUST make sure window is set before we forge on... */ - while ((tmp = readl(offset)) != wndw) { - printk(KERN_WARNING "%s: %s WARNING: CRB window value not " - "registered properly: 0x%08x.\n", - netxen_nic_driver_name, __func__, tmp); - mdelay(1); - if (count >= 10) - break; - count++; - } + } while (--count > 0); - if (wndw == NETXEN_WINDOW_ONE) - adapter->curr_window = 1; - else - adapter->curr_window = 0; + if (count > 0) + adapter->ahw.crb_win = window; } /* - * Return -1 if off is not valid, + * Returns < 0 if off is not valid, * 1 if window access is needed. 'off' is set to offset from * CRB space in 128M pci map * 0 if no window access is needed. 'off' is set to 2M addr * In: 'off' is offset from base in 128M pci map */ static int -netxen_nic_pci_get_crb_addr_2M(struct netxen_adapter *adapter, ulong *off) +netxen_nic_pci_get_crb_addr_2M(struct netxen_adapter *adapter, + ulong off, void __iomem **addr) { crb_128M_2M_sub_block_map_t *m; - if (*off >= NETXEN_CRB_MAX) - return -1; - - if (*off >= NETXEN_PCI_CAMQM && (*off < NETXEN_PCI_CAMQM_2M_END)) { - *off = (*off - NETXEN_PCI_CAMQM) + NETXEN_PCI_CAMQM_2M_BASE + - (ulong)adapter->ahw.pci_base0; - return 0; - } - - if (*off < NETXEN_PCI_CRBSPACE) - return -1; + if ((off >= NETXEN_CRB_MAX) || (off < NETXEN_PCI_CRBSPACE)) + return -EINVAL; - *off -= NETXEN_PCI_CRBSPACE; + off -= NETXEN_PCI_CRBSPACE; /* * Try direct map */ - m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)]; + m = &crb_128M_2M_map[CRB_BLK(off)].sub_block[CRB_SUBBLK(off)]; - if (m->valid && (m->start_128M <= *off) && (m->end_128M > *off)) { - *off = *off + m->start_2M - m->start_128M + - (ulong)adapter->ahw.pci_base0; + if (m->valid && (m->start_128M <= off) && (m->end_128M > off)) { + *addr = adapter->ahw.pci_base0 + m->start_2M + + (off - m->start_128M); return 0; } /* * Not in direct map, use crb window */ + *addr = adapter->ahw.pci_base0 + CRB_INDIRECT_2M + + (off & MASK(16)); return 1; } @@ -1165,52 +1153,78 @@ netxen_nic_pci_get_crb_addr_2M(struct netxen_adapter *adapter, ulong *off) * side effect: lock crb window */ static void -netxen_nic_pci_set_crbwindow_2M(struct netxen_adapter *adapter, ulong *off) +netxen_nic_pci_set_crbwindow_2M(struct netxen_adapter *adapter, ulong off) { - u32 win_read; + u32 window; + void __iomem *addr = adapter->ahw.pci_base0 + CRB_WINDOW_2M; - adapter->crb_win = CRB_HI(*off); - writel(adapter->crb_win, (adapter->ahw.pci_base0 + CRB_WINDOW_2M)); - /* - * Read back value to make sure write has gone through before trying - * to use it. - */ - win_read = readl(adapter->ahw.pci_base0 + CRB_WINDOW_2M); - if (win_read != adapter->crb_win) { - printk(KERN_ERR "%s: Written crbwin (0x%x) != " - "Read crbwin (0x%x), off=0x%lx\n", - __func__, adapter->crb_win, win_read, *off); + off -= NETXEN_PCI_CRBSPACE; + + window = CRB_HI(off); + + if (adapter->ahw.crb_win == window) + return; + + writel(window, addr); + if (readl(addr) != window) { + if (printk_ratelimit()) + dev_warn(&adapter->pdev->dev, + "failed to set CRB window to %d off 0x%lx\n", + window, off); } - *off = (*off & MASK(16)) + CRB_INDIRECT_2M + - (ulong)adapter->ahw.pci_base0; + adapter->ahw.crb_win = window; +} + +static void __iomem * +netxen_nic_map_indirect_address_128M(struct netxen_adapter *adapter, + ulong win_off, void __iomem **mem_ptr) +{ + ulong off = win_off; + void __iomem *addr; + resource_size_t mem_base; + + if (ADDR_IN_WINDOW1(win_off)) + off = NETXEN_CRB_NORMAL(win_off); + + addr = pci_base_offset(adapter, off); + if (addr) + return addr; + + if (adapter->ahw.pci_len0 == 0) + off -= NETXEN_PCI_CRBSPACE; + + mem_base = pci_resource_start(adapter->pdev, 0); + *mem_ptr = ioremap(mem_base + (off & PAGE_MASK), PAGE_SIZE); + if (*mem_ptr) + addr = *mem_ptr + (off & (PAGE_SIZE - 1)); + + return addr; } static int netxen_nic_hw_write_wx_128M(struct netxen_adapter *adapter, ulong off, u32 data) { unsigned long flags; - void __iomem *addr; - - if (ADDR_IN_WINDOW1(off)) - addr = NETXEN_CRB_NORMALIZE(adapter, off); - else - addr = pci_base_offset(adapter, off); + void __iomem *addr, *mem_ptr = NULL; - BUG_ON(!addr); + addr = netxen_nic_map_indirect_address_128M(adapter, off, &mem_ptr); + if (!addr) + return -EIO; - if (ADDR_IN_WINDOW1(off)) { /* Window 1 */ - read_lock(&adapter->adapter_lock); + if (ADDR_IN_WINDOW1(off)) { /* Window 1 */ + netxen_nic_io_write_128M(adapter, addr, data); + } else { /* Window 0 */ + write_lock_irqsave(&adapter->ahw.crb_lock, flags); + netxen_nic_pci_set_crbwindow_128M(adapter, 0); writel(data, addr); - read_unlock(&adapter->adapter_lock); - } else { /* Window 0 */ - write_lock_irqsave(&adapter->adapter_lock, flags); - addr = pci_base_offset(adapter, off); - netxen_nic_pci_change_crbwindow_128M(adapter, 0); - writel(data, addr); - netxen_nic_pci_change_crbwindow_128M(adapter, 1); - write_unlock_irqrestore(&adapter->adapter_lock, flags); + netxen_nic_pci_set_crbwindow_128M(adapter, + NETXEN_WINDOW_ONE); + write_unlock_irqrestore(&adapter->ahw.crb_lock, flags); } + if (mem_ptr) + iounmap(mem_ptr); + return 0; } @@ -1218,28 +1232,27 @@ static u32 netxen_nic_hw_read_wx_128M(struct netxen_adapter *adapter, ulong off) { unsigned long flags; - void __iomem *addr; + void __iomem *addr, *mem_ptr = NULL; u32 data; - if (ADDR_IN_WINDOW1(off)) - addr = NETXEN_CRB_NORMALIZE(adapter, off); - else - addr = pci_base_offset(adapter, off); - - BUG_ON(!addr); + addr = netxen_nic_map_indirect_address_128M(adapter, off, &mem_ptr); + if (!addr) + return -EIO; - if (ADDR_IN_WINDOW1(off)) { /* Window 1 */ - read_lock(&adapter->adapter_lock); - data = readl(addr); - read_unlock(&adapter->adapter_lock); - } else { /* Window 0 */ - write_lock_irqsave(&adapter->adapter_lock, flags); - netxen_nic_pci_change_crbwindow_128M(adapter, 0); + if (ADDR_IN_WINDOW1(off)) { /* Window 1 */ + data = netxen_nic_io_read_128M(adapter, addr); + } else { /* Window 0 */ + write_lock_irqsave(&adapter->ahw.crb_lock, flags); + netxen_nic_pci_set_crbwindow_128M(adapter, 0); data = readl(addr); - netxen_nic_pci_change_crbwindow_128M(adapter, 1); - write_unlock_irqrestore(&adapter->adapter_lock, flags); + netxen_nic_pci_set_crbwindow_128M(adapter, + NETXEN_WINDOW_ONE); + write_unlock_irqrestore(&adapter->ahw.crb_lock, flags); } + if (mem_ptr) + iounmap(mem_ptr); + return data; } @@ -1248,28 +1261,30 @@ netxen_nic_hw_write_wx_2M(struct netxen_adapter *adapter, ulong off, u32 data) { unsigned long flags; int rv; + void __iomem *addr = NULL; - rv = netxen_nic_pci_get_crb_addr_2M(adapter, &off); + rv = netxen_nic_pci_get_crb_addr_2M(adapter, off, &addr); - if (rv == -1) { - printk(KERN_ERR "%s: invalid offset: 0x%016lx\n", - __func__, off); - dump_stack(); - return -1; + if (rv == 0) { + writel(data, addr); + return 0; } - if (rv == 1) { - write_lock_irqsave(&adapter->adapter_lock, flags); + if (rv > 0) { + /* indirect access */ + write_lock_irqsave(&adapter->ahw.crb_lock, flags); crb_win_lock(adapter); - netxen_nic_pci_set_crbwindow_2M(adapter, &off); - writel(data, (void __iomem *)off); + netxen_nic_pci_set_crbwindow_2M(adapter, off); + writel(data, addr); crb_win_unlock(adapter); - write_unlock_irqrestore(&adapter->adapter_lock, flags); - } else - writel(data, (void __iomem *)off); - + write_unlock_irqrestore(&adapter->ahw.crb_lock, flags); + return 0; + } - return 0; + dev_err(&adapter->pdev->dev, + "%s: invalid offset: 0x%016lx\n", __func__, off); + dump_stack(); + return -EIO; } static u32 @@ -1278,102 +1293,37 @@ netxen_nic_hw_read_wx_2M(struct netxen_adapter *adapter, ulong off) unsigned long flags; int rv; u32 data; + void __iomem *addr = NULL; - rv = netxen_nic_pci_get_crb_addr_2M(adapter, &off); + rv = netxen_nic_pci_get_crb_addr_2M(adapter, off, &addr); - if (rv == -1) { - printk(KERN_ERR "%s: invalid offset: 0x%016lx\n", - __func__, off); - dump_stack(); - return -1; - } + if (rv == 0) + return readl(addr); - if (rv == 1) { - write_lock_irqsave(&adapter->adapter_lock, flags); + if (rv > 0) { + /* indirect access */ + write_lock_irqsave(&adapter->ahw.crb_lock, flags); crb_win_lock(adapter); - netxen_nic_pci_set_crbwindow_2M(adapter, &off); - data = readl((void __iomem *)off); + netxen_nic_pci_set_crbwindow_2M(adapter, off); + data = readl(addr); crb_win_unlock(adapter); - write_unlock_irqrestore(&adapter->adapter_lock, flags); - } else - data = readl((void __iomem *)off); - - return data; -} - -static int netxen_pci_set_window_warning_count; - -static unsigned long -netxen_nic_pci_set_window_128M(struct netxen_adapter *adapter, - unsigned long long addr) -{ - void __iomem *offset; - int window; - unsigned long long qdr_max; - uint8_t func = adapter->ahw.pci_func; - - if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { - qdr_max = NETXEN_ADDR_QDR_NET_MAX_P2; - } else { - qdr_max = NETXEN_ADDR_QDR_NET_MAX_P3; + write_unlock_irqrestore(&adapter->ahw.crb_lock, flags); + return data; } - if (ADDR_IN_RANGE(addr, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { - /* DDR network side */ - addr -= NETXEN_ADDR_DDR_NET; - window = (addr >> 25) & 0x3ff; - if (adapter->ahw.ddr_mn_window != window) { - adapter->ahw.ddr_mn_window = window; - offset = PCI_OFFSET_SECOND_RANGE(adapter, - NETXEN_PCIX_PH_REG(PCIE_MN_WINDOW_REG(func))); - writel(window, offset); - /* MUST make sure window is set before we forge on... */ - readl(offset); - } - addr -= (window * NETXEN_WINDOW_ONE); - addr += NETXEN_PCI_DDR_NET; - } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) { - addr -= NETXEN_ADDR_OCM0; - addr += NETXEN_PCI_OCM0; - } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) { - addr -= NETXEN_ADDR_OCM1; - addr += NETXEN_PCI_OCM1; - } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_QDR_NET, qdr_max)) { - /* QDR network side */ - addr -= NETXEN_ADDR_QDR_NET; - window = (addr >> 22) & 0x3f; - if (adapter->ahw.qdr_sn_window != window) { - adapter->ahw.qdr_sn_window = window; - offset = PCI_OFFSET_SECOND_RANGE(adapter, - NETXEN_PCIX_PH_REG(PCIE_SN_WINDOW_REG(func))); - writel((window << 22), offset); - /* MUST make sure window is set before we forge on... */ - readl(offset); - } - addr -= (window * 0x400000); - addr += NETXEN_PCI_QDR_NET; - } else { - /* - * peg gdb frequently accesses memory that doesn't exist, - * this limits the chit chat so debugging isn't slowed down. - */ - if ((netxen_pci_set_window_warning_count++ < 8) - || (netxen_pci_set_window_warning_count % 64 == 0)) - printk("%s: Warning:netxen_nic_pci_set_window()" - " Unknown address range!\n", - netxen_nic_driver_name); - addr = -1UL; - } - return addr; + dev_err(&adapter->pdev->dev, + "%s: invalid offset: 0x%016lx\n", __func__, off); + dump_stack(); + return -1; } /* window 1 registers only */ static void netxen_nic_io_write_128M(struct netxen_adapter *adapter, void __iomem *addr, u32 data) { - read_lock(&adapter->adapter_lock); + read_lock(&adapter->ahw.crb_lock); writel(data, addr); - read_unlock(&adapter->adapter_lock); + read_unlock(&adapter->ahw.crb_lock); } static u32 netxen_nic_io_read_128M(struct netxen_adapter *adapter, @@ -1381,9 +1331,9 @@ static u32 netxen_nic_io_read_128M(struct netxen_adapter *adapter, { u32 val; - read_lock(&adapter->adapter_lock); + read_lock(&adapter->ahw.crb_lock); val = readl(addr); - read_unlock(&adapter->adapter_lock); + read_unlock(&adapter->ahw.crb_lock); return val; } @@ -1403,488 +1353,437 @@ static u32 netxen_nic_io_read_2M(struct netxen_adapter *adapter, void __iomem * netxen_get_ioaddr(struct netxen_adapter *adapter, u32 offset) { - ulong off = offset; + void __iomem *addr = NULL; if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { - if (offset < NETXEN_CRB_PCIX_HOST2 && - offset > NETXEN_CRB_PCIX_HOST) - return PCI_OFFSET_SECOND_RANGE(adapter, offset); - return NETXEN_CRB_NORMALIZE(adapter, offset); + if ((offset < NETXEN_CRB_PCIX_HOST2) && + (offset > NETXEN_CRB_PCIX_HOST)) + addr = PCI_OFFSET_SECOND_RANGE(adapter, offset); + else + addr = NETXEN_CRB_NORMALIZE(adapter, offset); + } else { + WARN_ON(netxen_nic_pci_get_crb_addr_2M(adapter, + offset, &addr)); } - BUG_ON(netxen_nic_pci_get_crb_addr_2M(adapter, &off)); - return (void __iomem *)off; + return addr; } -static unsigned long -netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter, - unsigned long long addr) +static int +netxen_nic_pci_set_window_128M(struct netxen_adapter *adapter, + u64 addr, u32 *start) { - int window; - u32 win_read; - - if (ADDR_IN_RANGE(addr, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { - /* DDR network side */ - window = MN_WIN(addr); - adapter->ahw.ddr_mn_window = window; - NXWR32(adapter, adapter->ahw.mn_win_crb, window); - win_read = NXRD32(adapter, adapter->ahw.mn_win_crb); - if ((win_read << 17) != window) { - printk(KERN_INFO "Written MNwin (0x%x) != " - "Read MNwin (0x%x)\n", window, win_read); - } - addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_DDR_NET; + if (ADDR_IN_RANGE(addr, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) { + *start = (addr - NETXEN_ADDR_OCM0 + NETXEN_PCI_OCM0); + return 0; } else if (ADDR_IN_RANGE(addr, - NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) { - if ((addr & 0x00ff800) == 0xff800) { - printk("%s: QM access not handled.\n", __func__); - addr = -1UL; - } + NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) { + *start = (addr - NETXEN_ADDR_OCM1 + NETXEN_PCI_OCM1); + return 0; + } + + return -EIO; +} +static int +netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter, + u64 addr, u32 *start) +{ + u32 window; + struct pci_dev *pdev = adapter->pdev; + + if ((addr & 0x00ff800) == 0xff800) { + if (printk_ratelimit()) + dev_warn(&pdev->dev, "QM access not handled\n"); + return -EIO; + } + + if (NX_IS_REVISION_P3P(adapter->ahw.revision_id)) + window = OCM_WIN_P3P(addr); + else window = OCM_WIN(addr); - adapter->ahw.ddr_mn_window = window; - NXWR32(adapter, adapter->ahw.mn_win_crb, window); - win_read = NXRD32(adapter, adapter->ahw.mn_win_crb); - if ((win_read >> 7) != window) { - printk(KERN_INFO "%s: Written OCMwin (0x%x) != " - "Read OCMwin (0x%x)\n", - __func__, window, win_read); - } - addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_OCM0_2M; - } else if (ADDR_IN_RANGE(addr, - NETXEN_ADDR_QDR_NET, NETXEN_ADDR_QDR_NET_MAX_P3)) { - /* QDR network side */ - window = MS_WIN(addr); - adapter->ahw.qdr_sn_window = window; - NXWR32(adapter, adapter->ahw.ms_win_crb, window); - win_read = NXRD32(adapter, adapter->ahw.ms_win_crb); - if (win_read != window) { - printk(KERN_INFO "%s: Written MSwin (0x%x) != " - "Read MSwin (0x%x)\n", - __func__, window, win_read); - } - addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_QDR_NET; + writel(window, adapter->ahw.ocm_win_crb); + /* read back to flush */ + readl(adapter->ahw.ocm_win_crb); - } else { - /* - * peg gdb frequently accesses memory that doesn't exist, - * this limits the chit chat so debugging isn't slowed down. - */ - if ((netxen_pci_set_window_warning_count++ < 8) - || (netxen_pci_set_window_warning_count%64 == 0)) { - printk("%s: Warning:%s Unknown address range!\n", - __func__, netxen_nic_driver_name); + adapter->ahw.ocm_win = window; + *start = NETXEN_PCI_OCM0_2M + GET_MEM_OFFS_2M(addr); + return 0; } - addr = -1UL; + +static int +netxen_nic_pci_mem_access_direct(struct netxen_adapter *adapter, u64 off, + u64 *data, int op) +{ + void __iomem *addr, *mem_ptr = NULL; + resource_size_t mem_base; + int ret = -EIO; + u32 start; + + spin_lock(&adapter->ahw.mem_lock); + + ret = adapter->pci_set_window(adapter, off, &start); + if (ret != 0) + goto unlock; + + addr = pci_base_offset(adapter, start); + if (addr) + goto noremap; + + mem_base = pci_resource_start(adapter->pdev, 0) + (start & PAGE_MASK); + + mem_ptr = ioremap(mem_base, PAGE_SIZE); + if (mem_ptr == NULL) { + ret = -EIO; + goto unlock; } - return addr; + + addr = mem_ptr + (start & (PAGE_SIZE - 1)); + +noremap: + if (op == 0) /* read */ + *data = readq(addr); + else /* write */ + writeq(*data, addr); + +unlock: + spin_unlock(&adapter->ahw.mem_lock); + + if (mem_ptr) + iounmap(mem_ptr); + return ret; } #define MAX_CTL_CHECK 1000 static int netxen_nic_pci_mem_write_128M(struct netxen_adapter *adapter, - u64 off, void *data, int size) + u64 off, u64 data) { - unsigned long flags; - int i, j, ret = 0, loop, sz[2], off0; - uint32_t temp; - uint64_t off8, tmpw, word[2] = {0, 0}; + int j, ret; + u32 temp, off_lo, off_hi, addr_hi, data_hi, data_lo; void __iomem *mem_crb; - if (size != 8) + /* Only 64-bit aligned access */ + if (off & 7) return -EIO; + /* P2 has different SIU and MIU test agent base addr */ if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET, NETXEN_ADDR_QDR_NET_MAX_P2)) { - mem_crb = pci_base_offset(adapter, NETXEN_CRB_QDR_NET); + mem_crb = pci_base_offset(adapter, + NETXEN_CRB_QDR_NET+SIU_TEST_AGT_BASE); + addr_hi = SIU_TEST_AGT_ADDR_HI; + data_lo = SIU_TEST_AGT_WRDATA_LO; + data_hi = SIU_TEST_AGT_WRDATA_HI; + off_lo = off & SIU_TEST_AGT_ADDR_MASK; + off_hi = SIU_TEST_AGT_UPPER_ADDR(off); goto correct; } if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { - mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET); + mem_crb = pci_base_offset(adapter, + NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE); + addr_hi = MIU_TEST_AGT_ADDR_HI; + data_lo = MIU_TEST_AGT_WRDATA_LO; + data_hi = MIU_TEST_AGT_WRDATA_HI; + off_lo = off & MIU_TEST_AGT_ADDR_MASK; + off_hi = 0; goto correct; } - return -EIO; - -correct: - off8 = off & 0xfffffff8; - off0 = off & 0x7; - sz[0] = (size < (8 - off0)) ? size : (8 - off0); - sz[1] = size - sz[0]; - loop = ((off0 + size - 1) >> 3) + 1; - - if ((size != 8) || (off0 != 0)) { - for (i = 0; i < loop; i++) { - if (adapter->pci_mem_read(adapter, - off8 + (i << 3), &word[i], 8)) - return -1; + if (ADDR_IN_RANGE(off, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX) || + ADDR_IN_RANGE(off, NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) { + if (adapter->ahw.pci_len0 != 0) { + return netxen_nic_pci_mem_access_direct(adapter, + off, &data, 1); } } - switch (size) { - case 1: - tmpw = *((uint8_t *)data); - break; - case 2: - tmpw = *((uint16_t *)data); - break; - case 4: - tmpw = *((uint32_t *)data); - break; - case 8: - default: - tmpw = *((uint64_t *)data); - break; - } - word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); - word[0] |= tmpw << (off0 * 8); + return -EIO; - if (loop == 2) { - word[1] &= ~(~0ULL << (sz[1] * 8)); - word[1] |= tmpw >> (sz[0] * 8); +correct: + spin_lock(&adapter->ahw.mem_lock); + netxen_nic_pci_set_crbwindow_128M(adapter, 0); + + writel(off_lo, (mem_crb + MIU_TEST_AGT_ADDR_LO)); + writel(off_hi, (mem_crb + addr_hi)); + writel(data & 0xffffffff, (mem_crb + data_lo)); + writel((data >> 32) & 0xffffffff, (mem_crb + data_hi)); + writel((TA_CTL_ENABLE | TA_CTL_WRITE), (mem_crb + TEST_AGT_CTRL)); + writel((TA_CTL_START | TA_CTL_ENABLE | TA_CTL_WRITE), + (mem_crb + TEST_AGT_CTRL)); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = readl((mem_crb + TEST_AGT_CTRL)); + if ((temp & TA_CTL_BUSY) == 0) + break; } - write_lock_irqsave(&adapter->adapter_lock, flags); - netxen_nic_pci_change_crbwindow_128M(adapter, 0); - - for (i = 0; i < loop; i++) { - writel((uint32_t)(off8 + (i << 3)), - (mem_crb+MIU_TEST_AGT_ADDR_LO)); - writel(0, - (mem_crb+MIU_TEST_AGT_ADDR_HI)); - writel(word[i] & 0xffffffff, - (mem_crb+MIU_TEST_AGT_WRDATA_LO)); - writel((word[i] >> 32) & 0xffffffff, - (mem_crb+MIU_TEST_AGT_WRDATA_HI)); - writel(MIU_TA_CTL_ENABLE|MIU_TA_CTL_WRITE, - (mem_crb+MIU_TEST_AGT_CTRL)); - writel(MIU_TA_CTL_START|MIU_TA_CTL_ENABLE|MIU_TA_CTL_WRITE, - (mem_crb+MIU_TEST_AGT_CTRL)); - - for (j = 0; j < MAX_CTL_CHECK; j++) { - temp = readl( - (mem_crb+MIU_TEST_AGT_CTRL)); - if ((temp & MIU_TA_CTL_BUSY) == 0) - break; - } - - if (j >= MAX_CTL_CHECK) { - if (printk_ratelimit()) - dev_err(&adapter->pdev->dev, + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&adapter->pdev->dev, "failed to write through agent\n"); - ret = -1; - break; - } - } + ret = -EIO; + } else + ret = 0; - netxen_nic_pci_change_crbwindow_128M(adapter, 1); - write_unlock_irqrestore(&adapter->adapter_lock, flags); + netxen_nic_pci_set_crbwindow_128M(adapter, NETXEN_WINDOW_ONE); + spin_unlock(&adapter->ahw.mem_lock); return ret; } static int netxen_nic_pci_mem_read_128M(struct netxen_adapter *adapter, - u64 off, void *data, int size) + u64 off, u64 *data) { - unsigned long flags; - int i, j = 0, k, start, end, loop, sz[2], off0[2]; - uint32_t temp; - uint64_t off8, val, word[2] = {0, 0}; + int j, ret; + u32 temp, off_lo, off_hi, addr_hi, data_hi, data_lo; + u64 val; void __iomem *mem_crb; - if (size != 8) + /* Only 64-bit aligned access */ + if (off & 7) return -EIO; + /* P2 has different SIU and MIU test agent base addr */ if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET, NETXEN_ADDR_QDR_NET_MAX_P2)) { - mem_crb = pci_base_offset(adapter, NETXEN_CRB_QDR_NET); + mem_crb = pci_base_offset(adapter, + NETXEN_CRB_QDR_NET+SIU_TEST_AGT_BASE); + addr_hi = SIU_TEST_AGT_ADDR_HI; + data_lo = SIU_TEST_AGT_RDDATA_LO; + data_hi = SIU_TEST_AGT_RDDATA_HI; + off_lo = off & SIU_TEST_AGT_ADDR_MASK; + off_hi = SIU_TEST_AGT_UPPER_ADDR(off); goto correct; } if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { - mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET); + mem_crb = pci_base_offset(adapter, + NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE); + addr_hi = MIU_TEST_AGT_ADDR_HI; + data_lo = MIU_TEST_AGT_RDDATA_LO; + data_hi = MIU_TEST_AGT_RDDATA_HI; + off_lo = off & MIU_TEST_AGT_ADDR_MASK; + off_hi = 0; goto correct; } + if (ADDR_IN_RANGE(off, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX) || + ADDR_IN_RANGE(off, NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) { + if (adapter->ahw.pci_len0 != 0) { + return netxen_nic_pci_mem_access_direct(adapter, + off, data, 0); + } + } + return -EIO; correct: - off8 = off & 0xfffffff8; - off0[0] = off & 0x7; - off0[1] = 0; - sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]); - sz[1] = size - sz[0]; - loop = ((off0[0] + size - 1) >> 3) + 1; - - write_lock_irqsave(&adapter->adapter_lock, flags); - netxen_nic_pci_change_crbwindow_128M(adapter, 0); - - for (i = 0; i < loop; i++) { - writel((uint32_t)(off8 + (i << 3)), - (mem_crb+MIU_TEST_AGT_ADDR_LO)); - writel(0, - (mem_crb+MIU_TEST_AGT_ADDR_HI)); - writel(MIU_TA_CTL_ENABLE, - (mem_crb+MIU_TEST_AGT_CTRL)); - writel(MIU_TA_CTL_START|MIU_TA_CTL_ENABLE, - (mem_crb+MIU_TEST_AGT_CTRL)); + spin_lock(&adapter->ahw.mem_lock); + netxen_nic_pci_set_crbwindow_128M(adapter, 0); - for (j = 0; j < MAX_CTL_CHECK; j++) { - temp = readl( - (mem_crb+MIU_TEST_AGT_CTRL)); - if ((temp & MIU_TA_CTL_BUSY) == 0) - break; - } + writel(off_lo, (mem_crb + MIU_TEST_AGT_ADDR_LO)); + writel(off_hi, (mem_crb + addr_hi)); + writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL)); + writel((TA_CTL_START|TA_CTL_ENABLE), (mem_crb + TEST_AGT_CTRL)); - if (j >= MAX_CTL_CHECK) { - if (printk_ratelimit()) - dev_err(&adapter->pdev->dev, - "failed to read through agent\n"); + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = readl(mem_crb + TEST_AGT_CTRL); + if ((temp & TA_CTL_BUSY) == 0) break; - } - - start = off0[i] >> 2; - end = (off0[i] + sz[i] - 1) >> 2; - for (k = start; k <= end; k++) { - word[i] |= ((uint64_t) readl( - (mem_crb + - MIU_TEST_AGT_RDDATA(k))) << (32*k)); - } } - netxen_nic_pci_change_crbwindow_128M(adapter, 1); - write_unlock_irqrestore(&adapter->adapter_lock, flags); - - if (j >= MAX_CTL_CHECK) - return -1; - - if (sz[0] == 8) { - val = word[0]; + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&adapter->pdev->dev, + "failed to read through agent\n"); + ret = -EIO; } else { - val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) | - ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8)); - } - switch (size) { - case 1: - *(uint8_t *)data = val; - break; - case 2: - *(uint16_t *)data = val; - break; - case 4: - *(uint32_t *)data = val; - break; - case 8: - *(uint64_t *)data = val; - break; + temp = readl(mem_crb + data_hi); + val = ((u64)temp << 32); + val |= readl(mem_crb + data_lo); + *data = val; + ret = 0; } - return 0; + + netxen_nic_pci_set_crbwindow_128M(adapter, NETXEN_WINDOW_ONE); + spin_unlock(&adapter->ahw.mem_lock); + + return ret; } static int netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter, - u64 off, void *data, int size) + u64 off, u64 data) { - int i, j, ret = 0, loop, sz[2], off0; - uint32_t temp; - uint64_t off8, tmpw, word[2] = {0, 0}; + int i, j, ret; + u32 temp, off8; + u64 stride; void __iomem *mem_crb; - if (size != 8) + /* Only 64-bit aligned access */ + if (off & 7) return -EIO; + /* P3 onward, test agent base for MIU and SIU is same */ if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET, NETXEN_ADDR_QDR_NET_MAX_P3)) { - mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_QDR_NET); + mem_crb = netxen_get_ioaddr(adapter, + NETXEN_CRB_QDR_NET+MIU_TEST_AGT_BASE); goto correct; } if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { - mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_DDR_NET); + mem_crb = netxen_get_ioaddr(adapter, + NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE); goto correct; } + if (ADDR_IN_RANGE(off, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) + return netxen_nic_pci_mem_access_direct(adapter, off, &data, 1); + return -EIO; correct: - off8 = off & 0xfffffff8; - off0 = off & 0x7; - sz[0] = (size < (8 - off0)) ? size : (8 - off0); - sz[1] = size - sz[0]; - loop = ((off0 + size - 1) >> 3) + 1; - - if ((size != 8) || (off0 != 0)) { - for (i = 0; i < loop; i++) { - if (adapter->pci_mem_read(adapter, - off8 + (i << 3), &word[i], 8)) - return -1; - } - } - - switch (size) { - case 1: - tmpw = *((uint8_t *)data); - break; - case 2: - tmpw = *((uint16_t *)data); - break; - case 4: - tmpw = *((uint32_t *)data); - break; - case 8: - default: - tmpw = *((uint64_t *)data); - break; - } + stride = NX_IS_REVISION_P3P(adapter->ahw.revision_id) ? 16 : 8; - word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); - word[0] |= tmpw << (off0 * 8); + off8 = off & ~(stride-1); - if (loop == 2) { - word[1] &= ~(~0ULL << (sz[1] * 8)); - word[1] |= tmpw >> (sz[0] * 8); - } + spin_lock(&adapter->ahw.mem_lock); - /* - * don't lock here - write_wx gets the lock if each time - * write_lock_irqsave(&adapter->adapter_lock, flags); - * netxen_nic_pci_change_crbwindow_128M(adapter, 0); - */ + writel(off8, (mem_crb + MIU_TEST_AGT_ADDR_LO)); + writel(0, (mem_crb + MIU_TEST_AGT_ADDR_HI)); - for (i = 0; i < loop; i++) { - writel(off8 + (i << 3), mem_crb+MIU_TEST_AGT_ADDR_LO); - writel(0, mem_crb+MIU_TEST_AGT_ADDR_HI); - writel(word[i] & 0xffffffff, mem_crb+MIU_TEST_AGT_WRDATA_LO); - writel((word[i] >> 32) & 0xffffffff, - mem_crb+MIU_TEST_AGT_WRDATA_HI); - writel((MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE), - mem_crb+MIU_TEST_AGT_CTRL); - writel(MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE, - mem_crb+MIU_TEST_AGT_CTRL); + i = 0; + if (stride == 16) { + writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL)); + writel((TA_CTL_START | TA_CTL_ENABLE), + (mem_crb + TEST_AGT_CTRL)); for (j = 0; j < MAX_CTL_CHECK; j++) { - temp = readl(mem_crb + MIU_TEST_AGT_CTRL); - if ((temp & MIU_TA_CTL_BUSY) == 0) + temp = readl(mem_crb + TEST_AGT_CTRL); + if ((temp & TA_CTL_BUSY) == 0) break; } if (j >= MAX_CTL_CHECK) { - if (printk_ratelimit()) - dev_err(&adapter->pdev->dev, - "failed to write through agent\n"); - ret = -1; - break; + ret = -EIO; + goto done; } + + i = (off & 0xf) ? 0 : 2; + writel(readl(mem_crb + MIU_TEST_AGT_RDDATA(i)), + mem_crb + MIU_TEST_AGT_WRDATA(i)); + writel(readl(mem_crb + MIU_TEST_AGT_RDDATA(i+1)), + mem_crb + MIU_TEST_AGT_WRDATA(i+1)); + i = (off & 0xf) ? 2 : 0; } - /* - * netxen_nic_pci_change_crbwindow_128M(adapter, 1); - * write_unlock_irqrestore(&adapter->adapter_lock, flags); - */ + writel(data & 0xffffffff, + mem_crb + MIU_TEST_AGT_WRDATA(i)); + writel((data >> 32) & 0xffffffff, + mem_crb + MIU_TEST_AGT_WRDATA(i+1)); + + writel((TA_CTL_ENABLE | TA_CTL_WRITE), (mem_crb + TEST_AGT_CTRL)); + writel((TA_CTL_START | TA_CTL_ENABLE | TA_CTL_WRITE), + (mem_crb + TEST_AGT_CTRL)); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = readl(mem_crb + TEST_AGT_CTRL); + if ((temp & TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&adapter->pdev->dev, + "failed to write through agent\n"); + ret = -EIO; + } else + ret = 0; + +done: + spin_unlock(&adapter->ahw.mem_lock); + return ret; } static int netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter, - u64 off, void *data, int size) + u64 off, u64 *data) { - int i, j = 0, k, start, end, loop, sz[2], off0[2]; - uint32_t temp; - uint64_t off8, val, word[2] = {0, 0}; + int j, ret; + u32 temp, off8; + u64 val, stride; void __iomem *mem_crb; - if (size != 8) + /* Only 64-bit aligned access */ + if (off & 7) return -EIO; + /* P3 onward, test agent base for MIU and SIU is same */ if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET, NETXEN_ADDR_QDR_NET_MAX_P3)) { - mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_QDR_NET); + mem_crb = netxen_get_ioaddr(adapter, + NETXEN_CRB_QDR_NET+MIU_TEST_AGT_BASE); goto correct; } if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { - mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_DDR_NET); + mem_crb = netxen_get_ioaddr(adapter, + NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE); goto correct; } + if (ADDR_IN_RANGE(off, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) { + return netxen_nic_pci_mem_access_direct(adapter, + off, data, 0); + } + return -EIO; correct: - off8 = off & 0xfffffff8; - off0[0] = off & 0x7; - off0[1] = 0; - sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]); - sz[1] = size - sz[0]; - loop = ((off0[0] + size - 1) >> 3) + 1; + stride = NX_IS_REVISION_P3P(adapter->ahw.revision_id) ? 16 : 8; - /* - * don't lock here - write_wx gets the lock if each time - * write_lock_irqsave(&adapter->adapter_lock, flags); - * netxen_nic_pci_change_crbwindow_128M(adapter, 0); - */ + off8 = off & ~(stride-1); - for (i = 0; i < loop; i++) { - writel(off8 + (i << 3), mem_crb + MIU_TEST_AGT_ADDR_LO); - writel(0, mem_crb + MIU_TEST_AGT_ADDR_HI); - writel(MIU_TA_CTL_ENABLE, mem_crb + MIU_TEST_AGT_CTRL); - writel(MIU_TA_CTL_START | MIU_TA_CTL_ENABLE, - mem_crb + MIU_TEST_AGT_CTRL); + spin_lock(&adapter->ahw.mem_lock); - for (j = 0; j < MAX_CTL_CHECK; j++) { - temp = readl(mem_crb + MIU_TEST_AGT_CTRL); - if ((temp & MIU_TA_CTL_BUSY) == 0) - break; - } + writel(off8, (mem_crb + MIU_TEST_AGT_ADDR_LO)); + writel(0, (mem_crb + MIU_TEST_AGT_ADDR_HI)); + writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL)); + writel((TA_CTL_START | TA_CTL_ENABLE), (mem_crb + TEST_AGT_CTRL)); - if (j >= MAX_CTL_CHECK) { - if (printk_ratelimit()) - dev_err(&adapter->pdev->dev, - "failed to read through agent\n"); + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = readl(mem_crb + TEST_AGT_CTRL); + if ((temp & TA_CTL_BUSY) == 0) break; - } - - start = off0[i] >> 2; - end = (off0[i] + sz[i] - 1) >> 2; - for (k = start; k <= end; k++) { - temp = readl(mem_crb + MIU_TEST_AGT_RDDATA(k)); - word[i] |= ((uint64_t)temp << (32 * k)); - } } - /* - * netxen_nic_pci_change_crbwindow_128M(adapter, 1); - * write_unlock_irqrestore(&adapter->adapter_lock, flags); - */ - - if (j >= MAX_CTL_CHECK) - return -1; - - if (sz[0] == 8) { - val = word[0]; + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&adapter->pdev->dev, + "failed to read through agent\n"); + ret = -EIO; } else { - val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) | - ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8)); - } + off8 = MIU_TEST_AGT_RDDATA_LO; + if ((stride == 16) && (off & 0xf)) + off8 = MIU_TEST_AGT_RDDATA_UPPER_LO; - switch (size) { - case 1: - *(uint8_t *)data = val; - break; - case 2: - *(uint16_t *)data = val; - break; - case 4: - *(uint32_t *)data = val; - break; - case 8: - *(uint64_t *)data = val; - break; + temp = readl(mem_crb + off8 + 4); + val = (u64)temp << 32; + val |= readl(mem_crb + off8); + *data = val; + ret = 0; } - return 0; + + spin_unlock(&adapter->ahw.mem_lock); + + return ret; } void @@ -2037,10 +1936,10 @@ void netxen_nic_set_link_parameters(struct netxen_adapter *adapter) return; } - if (adapter->phy_read - && adapter->phy_read(adapter, - NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, - &status) == 0) { + if (adapter->phy_read && + adapter->phy_read(adapter, + NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, + &status) == 0) { if (netxen_get_phy_link(status)) { switch (netxen_get_phy_speed(status)) { case 0: @@ -2067,10 +1966,10 @@ void netxen_nic_set_link_parameters(struct netxen_adapter *adapter) adapter->link_duplex = -1; break; } - if (adapter->phy_read - && adapter->phy_read(adapter, - NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, - &autoneg) != 0) + if (adapter->phy_read && + adapter->phy_read(adapter, + NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, + &autoneg) != 0) adapter->link_autoneg = autoneg; } else goto link_down; diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index 8a0904368e08..80a667460514 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -46,6 +46,7 @@ static unsigned int crb_addr_xform[NETXEN_MAX_CRB_XFORM]; static void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter, struct nx_host_rds_ring *rds_ring); +static int netxen_p3_has_mn(struct netxen_adapter *adapter); static void crb_addr_transform_setup(void) { @@ -437,7 +438,7 @@ int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp) #define NETXEN_BOARDNUM 0x400c #define NETXEN_CHIPNUM 0x4010 -int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) +int netxen_pinit_from_rom(struct netxen_adapter *adapter) { int addr, val; int i, n, init_delay = 0; @@ -450,21 +451,6 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) NXWR32(adapter, NETXEN_ROMUSB_GLB_SW_RESET, 0xffffffff); netxen_rom_unlock(adapter); - if (verbose) { - if (netxen_rom_fast_read(adapter, NETXEN_BOARDTYPE, &val) == 0) - printk("P2 ROM board type: 0x%08x\n", val); - else - printk("Could not read board type\n"); - if (netxen_rom_fast_read(adapter, NETXEN_BOARDNUM, &val) == 0) - printk("P2 ROM board num: 0x%08x\n", val); - else - printk("Could not read board number\n"); - if (netxen_rom_fast_read(adapter, NETXEN_CHIPNUM, &val) == 0) - printk("P2 ROM chip num: 0x%08x\n", val); - else - printk("Could not read chip number\n"); - } - if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) { if (netxen_rom_fast_read(adapter, 0, &n) != 0 || (n != 0xcafecafe) || @@ -486,11 +472,7 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) n &= ~0x80000000; } - if (n < 1024) { - if (verbose) - printk(KERN_DEBUG "%s: %d CRB init values found" - " in ROM.\n", netxen_nic_driver_name, n); - } else { + if (n >= 1024) { printk(KERN_ERR "%s:n=0x%x Error! NetXen card flash not" " initialized.\n", __func__, n); return -EIO; @@ -502,6 +484,7 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) netxen_nic_driver_name); return -ENOMEM; } + for (i = 0; i < n; i++) { if (netxen_rom_fast_read(adapter, 8*i + 4*offset, &val) != 0 || netxen_rom_fast_read(adapter, 8*i + 4*offset + 4, &addr) != 0) { @@ -512,11 +495,8 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) buf[i].addr = addr; buf[i].data = val; - if (verbose) - printk(KERN_DEBUG "%s: PCI: 0x%08x == 0x%08x\n", - netxen_nic_driver_name, - (u32)netxen_decode_crb_addr(addr), val); } + for (i = 0; i < n; i++) { off = netxen_decode_crb_addr(buf[i].addr); @@ -526,6 +506,10 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) continue; } off += NETXEN_PCI_CRBSPACE; + + if (off & 1) + continue; + /* skipping cold reboot MAGIC */ if (off == NETXEN_CAM_RAM(0x1fc)) continue; @@ -546,7 +530,8 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) continue; if ((off & 0x0ff00000) == NETXEN_CRB_DDR_NET) continue; - if (off == (NETXEN_CRB_PEG_NET_1 + 0x18)) + if (off == (NETXEN_CRB_PEG_NET_1 + 0x18) && + !NX_IS_REVISION_P3P(adapter->ahw.revision_id)) buf[i].data = 0x1020; /* skip the function enable register */ if (off == NETXEN_PCIE_REG(PCIE_SETUP_FUNCTION)) @@ -607,6 +592,172 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) return 0; } +static struct uni_table_desc *nx_get_table_desc(const u8 *unirom, int section) +{ + uint32_t i; + struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0]; + __le32 entries = cpu_to_le32(directory->num_entries); + + for (i = 0; i < entries; i++) { + + __le32 offs = cpu_to_le32(directory->findex) + + (i * cpu_to_le32(directory->entry_size)); + __le32 tab_type = cpu_to_le32(*((u32 *)&unirom[offs] + 8)); + + if (tab_type == section) + return (struct uni_table_desc *) &unirom[offs]; + } + + return NULL; +} + +static int +nx_set_product_offs(struct netxen_adapter *adapter) +{ + struct uni_table_desc *ptab_descr; + const u8 *unirom = adapter->fw->data; + uint32_t i; + __le32 entries; + + ptab_descr = nx_get_table_desc(unirom, NX_UNI_DIR_SECT_PRODUCT_TBL); + if (ptab_descr == NULL) + return -1; + + entries = cpu_to_le32(ptab_descr->num_entries); + + for (i = 0; i < entries; i++) { + + __le32 flags, file_chiprev, offs; + u8 chiprev = adapter->ahw.revision_id; + int mn_present = netxen_p3_has_mn(adapter); + uint32_t flagbit; + + offs = cpu_to_le32(ptab_descr->findex) + + (i * cpu_to_le32(ptab_descr->entry_size)); + flags = cpu_to_le32(*((int *)&unirom[offs] + NX_UNI_FLAGS_OFF)); + file_chiprev = cpu_to_le32(*((int *)&unirom[offs] + + NX_UNI_CHIP_REV_OFF)); + + flagbit = mn_present ? 1 : 2; + + if ((chiprev == file_chiprev) && + ((1ULL << flagbit) & flags)) { + adapter->file_prd_off = offs; + return 0; + } + } + + return -1; +} + + +static struct uni_data_desc *nx_get_data_desc(struct netxen_adapter *adapter, + u32 section, u32 idx_offset) +{ + const u8 *unirom = adapter->fw->data; + int idx = cpu_to_le32(*((int *)&unirom[adapter->file_prd_off] + + idx_offset)); + struct uni_table_desc *tab_desc; + __le32 offs; + + tab_desc = nx_get_table_desc(unirom, section); + + if (tab_desc == NULL) + return NULL; + + offs = cpu_to_le32(tab_desc->findex) + + (cpu_to_le32(tab_desc->entry_size) * idx); + + return (struct uni_data_desc *)&unirom[offs]; +} + +static u8 * +nx_get_bootld_offs(struct netxen_adapter *adapter) +{ + u32 offs = NETXEN_BOOTLD_START; + + if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) + offs = cpu_to_le32((nx_get_data_desc(adapter, + NX_UNI_DIR_SECT_BOOTLD, + NX_UNI_BOOTLD_IDX_OFF))->findex); + + return (u8 *)&adapter->fw->data[offs]; +} + +static u8 * +nx_get_fw_offs(struct netxen_adapter *adapter) +{ + u32 offs = NETXEN_IMAGE_START; + + if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) + offs = cpu_to_le32((nx_get_data_desc(adapter, + NX_UNI_DIR_SECT_FW, + NX_UNI_FIRMWARE_IDX_OFF))->findex); + + return (u8 *)&adapter->fw->data[offs]; +} + +static __le32 +nx_get_fw_size(struct netxen_adapter *adapter) +{ + if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) + return cpu_to_le32((nx_get_data_desc(adapter, + NX_UNI_DIR_SECT_FW, + NX_UNI_FIRMWARE_IDX_OFF))->size); + else + return cpu_to_le32( + *(u32 *)&adapter->fw->data[NX_FW_SIZE_OFFSET]); +} + +static __le32 +nx_get_fw_version(struct netxen_adapter *adapter) +{ + struct uni_data_desc *fw_data_desc; + const struct firmware *fw = adapter->fw; + __le32 major, minor, sub; + const u8 *ver_str; + int i, ret = 0; + + if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) { + + fw_data_desc = nx_get_data_desc(adapter, + NX_UNI_DIR_SECT_FW, NX_UNI_FIRMWARE_IDX_OFF); + ver_str = fw->data + cpu_to_le32(fw_data_desc->findex) + + cpu_to_le32(fw_data_desc->size) - 17; + + for (i = 0; i < 12; i++) { + if (!strncmp(&ver_str[i], "REV=", 4)) { + ret = sscanf(&ver_str[i+4], "%u.%u.%u ", + &major, &minor, &sub); + break; + } + } + + if (ret != 3) + return 0; + + return major + (minor << 8) + (sub << 16); + + } else + return cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]); +} + +static __le32 +nx_get_bios_version(struct netxen_adapter *adapter) +{ + const struct firmware *fw = adapter->fw; + __le32 bios_ver, prd_off = adapter->file_prd_off; + + if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) { + bios_ver = cpu_to_le32(*((u32 *) (&fw->data[prd_off]) + + NX_UNI_BIOS_VERSION_OFF)); + return (bios_ver << 24) + ((bios_ver >> 8) & 0xff00) + + (bios_ver >> 24); + } else + return cpu_to_le32(*(u32 *)&fw->data[NX_BIOS_VERSION_OFFSET]); + +} + int netxen_need_fw_reset(struct netxen_adapter *adapter) { @@ -646,9 +797,8 @@ netxen_need_fw_reset(struct netxen_adapter *adapter) /* check if we have got newer or different file firmware */ if (adapter->fw) { - const struct firmware *fw = adapter->fw; + val = nx_get_fw_version(adapter); - val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]); version = NETXEN_DECODE_VERSION(val); major = NXRD32(adapter, NETXEN_FW_VERSION_MAJOR); @@ -658,7 +808,8 @@ netxen_need_fw_reset(struct netxen_adapter *adapter) if (version > NETXEN_VERSION_CODE(major, minor, build)) return 1; - if (version == NETXEN_VERSION_CODE(major, minor, build)) { + if (version == NETXEN_VERSION_CODE(major, minor, build) && + adapter->fw_type != NX_UNIFIED_ROMIMAGE) { val = NXRD32(adapter, NETXEN_MIU_MN_CONTROL); fw_type = (val & 0x4) ? @@ -673,7 +824,11 @@ netxen_need_fw_reset(struct netxen_adapter *adapter) } static char *fw_name[] = { - "nxromimg.bin", "nx3fwct.bin", "nx3fwmn.bin", "flash", + NX_P2_MN_ROMIMAGE_NAME, + NX_P3_CT_ROMIMAGE_NAME, + NX_P3_MN_ROMIMAGE_NAME, + NX_UNIFIED_ROMIMAGE_NAME, + NX_FLASH_ROMIMAGE_NAME, }; int @@ -695,26 +850,28 @@ netxen_load_firmware(struct netxen_adapter *adapter) size = (NETXEN_IMAGE_START - NETXEN_BOOTLD_START) / 8; - ptr64 = (u64 *)&fw->data[NETXEN_BOOTLD_START]; + ptr64 = (u64 *)nx_get_bootld_offs(adapter); flashaddr = NETXEN_BOOTLD_START; for (i = 0; i < size; i++) { data = cpu_to_le64(ptr64[i]); - adapter->pci_mem_write(adapter, flashaddr, &data, 8); + + if (adapter->pci_mem_write(adapter, flashaddr, data)) + return -EIO; + flashaddr += 8; } - size = *(u32 *)&fw->data[NX_FW_SIZE_OFFSET]; - size = (__force u32)cpu_to_le32(size) / 8; + size = (__force u32)nx_get_fw_size(adapter) / 8; - ptr64 = (u64 *)&fw->data[NETXEN_IMAGE_START]; + ptr64 = (u64 *)nx_get_fw_offs(adapter); flashaddr = NETXEN_IMAGE_START; for (i = 0; i < size; i++) { data = cpu_to_le64(ptr64[i]); if (adapter->pci_mem_write(adapter, - flashaddr, &data, 8)) + flashaddr, data)) return -EIO; flashaddr += 8; @@ -728,17 +885,17 @@ netxen_load_firmware(struct netxen_adapter *adapter) for (i = 0; i < size; i++) { if (netxen_rom_fast_read(adapter, - flashaddr, &lo) != 0) + flashaddr, (int *)&lo) != 0) return -EIO; if (netxen_rom_fast_read(adapter, - flashaddr + 4, &hi) != 0) + flashaddr + 4, (int *)&hi) != 0) return -EIO; /* hi, lo are already in host endian byteorder */ data = (((u64)hi << 32) | lo); if (adapter->pci_mem_write(adapter, - flashaddr, &data, 8)) + flashaddr, data)) return -EIO; flashaddr += 8; @@ -746,7 +903,10 @@ netxen_load_firmware(struct netxen_adapter *adapter) } msleep(1); - if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + if (NX_IS_REVISION_P3P(adapter->ahw.revision_id)) { + NXWR32(adapter, NETXEN_CRB_PEG_NET_0 + 0x18, 0x1020); + NXWR32(adapter, NETXEN_ROMUSB_GLB_SW_RESET, 0x80001e); + } else if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) NXWR32(adapter, NETXEN_ROMUSB_GLB_SW_RESET, 0x80001d); else { NXWR32(adapter, NETXEN_ROMUSB_GLB_CHIP_CLK_CTRL, 0x3fff); @@ -757,21 +917,31 @@ netxen_load_firmware(struct netxen_adapter *adapter) } static int -netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname) +netxen_validate_firmware(struct netxen_adapter *adapter) { __le32 val; - u32 ver, min_ver, bios; + u32 ver, min_ver, bios, min_size; struct pci_dev *pdev = adapter->pdev; const struct firmware *fw = adapter->fw; + u8 fw_type = adapter->fw_type; - if (fw->size < NX_FW_MIN_SIZE) - return -EINVAL; + if (fw_type == NX_UNIFIED_ROMIMAGE) { + if (nx_set_product_offs(adapter)) + return -EINVAL; + + min_size = NX_UNI_FW_MIN_SIZE; + } else { + val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_MAGIC_OFFSET]); + if ((__force u32)val != NETXEN_BDINFO_MAGIC) + return -EINVAL; + + min_size = NX_FW_MIN_SIZE; + } - val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_MAGIC_OFFSET]); - if ((__force u32)val != NETXEN_BDINFO_MAGIC) + if (fw->size < min_size) return -EINVAL; - val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]); + val = nx_get_fw_version(adapter); if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) min_ver = NETXEN_VERSION_CODE(4, 0, 216); @@ -783,15 +953,15 @@ netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname) if ((_major(ver) > _NETXEN_NIC_LINUX_MAJOR) || (ver < min_ver)) { dev_err(&pdev->dev, "%s: firmware version %d.%d.%d unsupported\n", - fwname, _major(ver), _minor(ver), _build(ver)); + fw_name[fw_type], _major(ver), _minor(ver), _build(ver)); return -EINVAL; } - val = cpu_to_le32(*(u32 *)&fw->data[NX_BIOS_VERSION_OFFSET]); + val = nx_get_bios_version(adapter); netxen_rom_fast_read(adapter, NX_BIOS_VERSION_OFFSET, (int *)&bios); if ((__force u32)val != bios) { dev_err(&pdev->dev, "%s: firmware bios is incompatible\n", - fwname); + fw_name[fw_type]); return -EINVAL; } @@ -802,7 +972,7 @@ netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname) val = NETXEN_DECODE_VERSION(val); if (val > ver) { dev_info(&pdev->dev, "%s: firmware is older than flash\n", - fwname); + fw_name[fw_type]); return -EINVAL; } @@ -810,6 +980,41 @@ netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname) return 0; } +static void +nx_get_next_fwtype(struct netxen_adapter *adapter) +{ + u8 fw_type; + + switch (adapter->fw_type) { + case NX_UNKNOWN_ROMIMAGE: + fw_type = NX_UNIFIED_ROMIMAGE; + break; + + case NX_UNIFIED_ROMIMAGE: + if (NX_IS_REVISION_P3P(adapter->ahw.revision_id)) + fw_type = NX_FLASH_ROMIMAGE; + else if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) + fw_type = NX_P2_MN_ROMIMAGE; + else if (netxen_p3_has_mn(adapter)) + fw_type = NX_P3_MN_ROMIMAGE; + else + fw_type = NX_P3_CT_ROMIMAGE; + break; + + case NX_P3_MN_ROMIMAGE: + fw_type = NX_P3_CT_ROMIMAGE; + break; + + case NX_P2_MN_ROMIMAGE: + case NX_P3_CT_ROMIMAGE: + default: + fw_type = NX_FLASH_ROMIMAGE; + break; + } + + adapter->fw_type = fw_type; +} + static int netxen_p3_has_mn(struct netxen_adapter *adapter) { @@ -831,49 +1036,29 @@ netxen_p3_has_mn(struct netxen_adapter *adapter) void netxen_request_firmware(struct netxen_adapter *adapter) { - u8 fw_type; struct pci_dev *pdev = adapter->pdev; int rc = 0; - if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { - fw_type = NX_P2_MN_ROMIMAGE; - goto request_fw; - } + adapter->fw_type = NX_UNKNOWN_ROMIMAGE; - fw_type = netxen_p3_has_mn(adapter) ? - NX_P3_MN_ROMIMAGE : NX_P3_CT_ROMIMAGE; +next: + nx_get_next_fwtype(adapter); -request_fw: - rc = request_firmware(&adapter->fw, fw_name[fw_type], &pdev->dev); - if (rc != 0) { - if (fw_type == NX_P3_MN_ROMIMAGE) { - msleep(1); - fw_type = NX_P3_CT_ROMIMAGE; - goto request_fw; - } - - fw_type = NX_FLASH_ROMIMAGE; + if (adapter->fw_type == NX_FLASH_ROMIMAGE) { adapter->fw = NULL; - goto done; - } - - rc = netxen_validate_firmware(adapter, fw_name[fw_type]); - if (rc != 0) { - release_firmware(adapter->fw); - - if (fw_type == NX_P3_MN_ROMIMAGE) { + } else { + rc = request_firmware(&adapter->fw, + fw_name[adapter->fw_type], &pdev->dev); + if (rc != 0) + goto next; + + rc = netxen_validate_firmware(adapter); + if (rc != 0) { + release_firmware(adapter->fw); msleep(1); - fw_type = NX_P3_CT_ROMIMAGE; - goto request_fw; + goto next; } - - fw_type = NX_FLASH_ROMIMAGE; - adapter->fw = NULL; - goto done; } - -done: - adapter->fw_type = fw_type; } @@ -1508,10 +1693,8 @@ netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ringid, (rds_ring->num_desc - 1))); netxen_set_msg_ctxid(msg, adapter->portnum); netxen_set_msg_opcode(msg, NETXEN_RCV_PRODUCER(ringid)); - read_lock(&adapter->adapter_lock); - writel(msg, DB_NORMALIZE(adapter, - NETXEN_RCV_PRODUCER_OFFSET)); - read_unlock(&adapter->adapter_lock); + NXWRIO(adapter, DB_NORMALIZE(adapter, + NETXEN_RCV_PRODUCER_OFFSET), msg); } } } diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 3bf78dbfbf0f..e5d187fce51b 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -34,13 +34,18 @@ #include <net/ip.h> #include <linux/ipv6.h> #include <linux/inetdevice.h> +#include <linux/sysfs.h> -MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver"); +MODULE_DESCRIPTION("QLogic/NetXen (1/10) GbE Converged Ethernet Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(NETXEN_NIC_LINUX_VERSIONID); +MODULE_FIRMWARE(NX_P2_MN_ROMIMAGE_NAME); +MODULE_FIRMWARE(NX_P3_CT_ROMIMAGE_NAME); +MODULE_FIRMWARE(NX_P3_MN_ROMIMAGE_NAME); +MODULE_FIRMWARE(NX_UNIFIED_ROMIMAGE_NAME); char netxen_nic_driver_name[] = "netxen_nic"; -static char netxen_nic_driver_string[] = "NetXen Network Driver version " +static char netxen_nic_driver_string[] = "QLogic/NetXen Network Driver v" NETXEN_NIC_LINUX_VERSIONID; static int port_mode = NETXEN_PORT_MODE_AUTO_NEG; @@ -52,7 +57,8 @@ static int use_msi = 1; static int use_msi_x = 1; -/* Local functions to NetXen NIC driver */ +static unsigned long auto_fw_reset = AUTO_FW_RESET_ENABLED; + static int __devinit netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void __devexit netxen_nic_remove(struct pci_dev *pdev); @@ -73,6 +79,8 @@ static void netxen_nic_poll_controller(struct net_device *netdev); static void netxen_create_sysfs_entries(struct netxen_adapter *adapter); static void netxen_remove_sysfs_entries(struct netxen_adapter *adapter); +static void netxen_create_diag_entries(struct netxen_adapter *adapter); +static void netxen_remove_diag_entries(struct netxen_adapter *adapter); static int nx_decr_dev_ref_cnt(struct netxen_adapter *adapter); static int netxen_can_start_firmware(struct netxen_adapter *adapter); @@ -609,14 +617,12 @@ netxen_setup_pci_map(struct netxen_adapter *adapter) * Set the CRB window to invalid. If any register in window 0 is * accessed it should set the window to 0 and then reset it to 1. */ - adapter->curr_window = 255; - adapter->ahw.qdr_sn_window = -1; - adapter->ahw.ddr_mn_window = -1; + adapter->ahw.crb_win = -1; + adapter->ahw.ocm_win = -1; /* remap phys address */ mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */ mem_len = pci_resource_len(pdev, 0); - pci_len0 = 0; /* 128 Meg of memory */ if (mem_len == NETXEN_PCI_128MB_SIZE) { @@ -625,6 +631,7 @@ netxen_setup_pci_map(struct netxen_adapter *adapter) SECOND_PAGE_GROUP_SIZE); mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE); + pci_len0 = FIRST_PAGE_GROUP_SIZE; } else if (mem_len == NETXEN_PCI_32MB_SIZE) { mem_ptr1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE); mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START - @@ -637,19 +644,6 @@ netxen_setup_pci_map(struct netxen_adapter *adapter) return -EIO; } pci_len0 = mem_len; - - adapter->ahw.ddr_mn_window = 0; - adapter->ahw.qdr_sn_window = 0; - - adapter->ahw.mn_win_crb = NETXEN_PCI_CRBSPACE + - 0x100000 + PCIX_MN_WINDOW + (pci_func * 0x20); - adapter->ahw.ms_win_crb = NETXEN_PCI_CRBSPACE + - 0x100000 + PCIX_SN_WINDOW; - if (pci_func < 4) - adapter->ahw.ms_win_crb += (pci_func * 0x20); - else - adapter->ahw.ms_win_crb += - 0xA0 + ((pci_func - 4) * 0x10); } else { return -EIO; } @@ -663,6 +657,15 @@ netxen_setup_pci_map(struct netxen_adapter *adapter) adapter->ahw.pci_base1 = mem_ptr1; adapter->ahw.pci_base2 = mem_ptr2; + if (NX_IS_REVISION_P3P(adapter->ahw.revision_id)) { + adapter->ahw.ocm_win_crb = netxen_get_ioaddr(adapter, + NETXEN_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(pci_func))); + + } else if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) { + adapter->ahw.ocm_win_crb = netxen_get_ioaddr(adapter, + NETXEN_PCIX_PS_REG(PCIE_MN_WINDOW_REG(pci_func))); + } + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) goto skip_doorbell; @@ -727,7 +730,8 @@ netxen_check_options(struct netxen_adapter *adapter) if (adapter->portnum == 0) { get_brd_name_by_type(adapter->ahw.board_type, brd_name); - printk(KERN_INFO "NetXen %s Board S/N %s Chip rev 0x%x\n", + pr_info("%s: %s Board S/N %s Chip rev 0x%x\n", + module_name(THIS_MODULE), brd_name, serial_num, adapter->ahw.revision_id); } @@ -815,11 +819,11 @@ netxen_start_firmware(struct netxen_adapter *adapter) if (err < 0) goto err_out; if (err == 0) - goto ready; + goto wait_init; if (first_boot != 0x55555555) { NXWR32(adapter, CRB_CMDPEG_STATE, 0); - netxen_pinit_from_rom(adapter, 0); + netxen_pinit_from_rom(adapter); msleep(1); } @@ -858,9 +862,6 @@ netxen_start_firmware(struct netxen_adapter *adapter) | (_NETXEN_NIC_LINUX_SUBVERSION); NXWR32(adapter, CRB_DRIVER_VERSION, val); -ready: - NXWR32(adapter, NX_CRB_DEV_STATE, NX_DEV_READY); - wait_init: /* Handshake with the card before we register the devices. */ err = netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); @@ -869,6 +870,8 @@ wait_init: goto err_out; } + NXWR32(adapter, NX_CRB_DEV_STATE, NX_DEV_READY); + nx_update_dma_mask(adapter); netxen_check_options(adapter); @@ -1209,16 +1212,10 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) int pci_func_id = PCI_FUNC(pdev->devfn); uint8_t revision_id; - if (pdev->class != 0x020000) { - printk(KERN_DEBUG "NetXen function %d, class %x will not " - "be enabled.\n",pci_func_id, pdev->class); - return -ENODEV; - } - if (pdev->revision >= NX_P3_A0 && pdev->revision < NX_P3_B1) { - printk(KERN_WARNING "NetXen chip revisions between 0x%x-0x%x" + pr_warning("%s: chip revisions between 0x%x-0x%x" "will not be enabled.\n", - NX_P3_A0, NX_P3_B1); + module_name(THIS_MODULE), NX_P3_A0, NX_P3_B1); return -ENODEV; } @@ -1252,7 +1249,9 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) revision_id = pdev->revision; adapter->ahw.revision_id = revision_id; - rwlock_init(&adapter->adapter_lock); + rwlock_init(&adapter->ahw.crb_lock); + spin_lock_init(&adapter->ahw.mem_lock); + spin_lock_init(&adapter->tx_clean_lock); INIT_LIST_HEAD(&adapter->mac_list); @@ -1282,7 +1281,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = netxen_start_firmware(adapter); if (err) - goto err_out_iounmap; + goto err_out_decr_ref; /* * See if the firmware gave us a virtual-physical port mapping. @@ -1317,6 +1316,8 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) break; } + netxen_create_diag_entries(adapter); + return 0; err_out_disable_msi: @@ -1324,6 +1325,7 @@ err_out_disable_msi: netxen_free_dummy_dma(adapter); +err_out_decr_ref: nx_decr_dev_ref_cnt(adapter); err_out_iounmap: @@ -1369,6 +1371,8 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) netxen_teardown_intr(adapter); + netxen_remove_diag_entries(adapter); + netxen_cleanup_pci_map(adapter); netxen_release_firmware(adapter); @@ -1449,7 +1453,8 @@ netxen_nic_resume(struct pci_dev *pdev) if (err) return err; - adapter->curr_window = 255; + adapter->ahw.crb_win = -1; + adapter->ahw.ocm_win = -1; err = netxen_start_firmware(adapter); if (err) { @@ -1927,7 +1932,7 @@ request_reset: struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev) { struct netxen_adapter *adapter = netdev_priv(netdev); - struct net_device_stats *stats = &adapter->net_stats; + struct net_device_stats *stats = &netdev->stats; memset(stats, 0, sizeof(*stats)); @@ -2184,14 +2189,13 @@ netxen_fwinit_work(struct work_struct *work) netxen_fwinit_work, 2 * FW_POLL_DELAY); return; } - break; case NX_DEV_FAILED: default: + nx_incr_dev_ref_cnt(adapter); break; } - nx_incr_dev_ref_cnt(adapter); clear_bit(__NX_RESETTING, &adapter->state); } @@ -2213,18 +2217,23 @@ netxen_detach_work(struct work_struct *work) status = NXRD32(adapter, NETXEN_PEG_HALT_STATUS1); - ref_cnt = nx_decr_dev_ref_cnt(adapter); - if (status & NX_RCODE_FATAL_ERROR) - return; + goto err_ret; if (adapter->temp == NX_TEMP_PANIC) - return; + goto err_ret; + + ref_cnt = nx_decr_dev_ref_cnt(adapter); delay = (ref_cnt == 0) ? 0 : (2 * FW_POLL_DELAY); adapter->fw_wait_cnt = 0; netxen_schedule_work(adapter, netxen_fwinit_work, delay); + + return; + +err_ret: + clear_bit(__NX_RESETTING, &adapter->state); } static int @@ -2263,7 +2272,8 @@ netxen_check_health(struct netxen_adapter *adapter) dev_info(&netdev->dev, "firmware hang detected\n"); detach: - if (!test_and_set_bit(__NX_RESETTING, &adapter->state)) + if ((auto_fw_reset == AUTO_FW_RESET_ENABLED) && + !test_and_set_bit(__NX_RESETTING, &adapter->state)) netxen_schedule_work(adapter, netxen_detach_work, 0); return 1; } @@ -2341,6 +2351,197 @@ static struct device_attribute dev_attr_bridged_mode = { .store = netxen_store_bridged_mode, }; +static ssize_t +netxen_store_diag_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct netxen_adapter *adapter = dev_get_drvdata(dev); + unsigned long new; + + if (strict_strtoul(buf, 2, &new)) + return -EINVAL; + + if (!!new != !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED)) + adapter->flags ^= NETXEN_NIC_DIAG_ENABLED; + + return len; +} + +static ssize_t +netxen_show_diag_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct netxen_adapter *adapter = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", + !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED)); +} + +static struct device_attribute dev_attr_diag_mode = { + .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)}, + .show = netxen_show_diag_mode, + .store = netxen_store_diag_mode, +}; + +static int +netxen_sysfs_validate_crb(struct netxen_adapter *adapter, + loff_t offset, size_t size) +{ + if (!(adapter->flags & NETXEN_NIC_DIAG_ENABLED)) + return -EIO; + + if ((size != 4) || (offset & 0x3)) + return -EINVAL; + + if (offset < NETXEN_PCI_CRBSPACE) + return -EINVAL; + + return 0; +} + +static ssize_t +netxen_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t offset, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct netxen_adapter *adapter = dev_get_drvdata(dev); + u32 data; + int ret; + + ret = netxen_sysfs_validate_crb(adapter, offset, size); + if (ret != 0) + return ret; + + data = NXRD32(adapter, offset); + memcpy(buf, &data, size); + return size; +} + +static ssize_t +netxen_sysfs_write_crb(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t offset, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct netxen_adapter *adapter = dev_get_drvdata(dev); + u32 data; + int ret; + + ret = netxen_sysfs_validate_crb(adapter, offset, size); + if (ret != 0) + return ret; + + memcpy(&data, buf, size); + NXWR32(adapter, offset, data); + return size; +} + +static int +netxen_sysfs_validate_mem(struct netxen_adapter *adapter, + loff_t offset, size_t size) +{ + if (!(adapter->flags & NETXEN_NIC_DIAG_ENABLED)) + return -EIO; + + if ((size != 8) || (offset & 0x7)) + return -EIO; + + return 0; +} + +static ssize_t +netxen_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t offset, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct netxen_adapter *adapter = dev_get_drvdata(dev); + u64 data; + int ret; + + ret = netxen_sysfs_validate_mem(adapter, offset, size); + if (ret != 0) + return ret; + + if (adapter->pci_mem_read(adapter, offset, &data)) + return -EIO; + + memcpy(buf, &data, size); + + return size; +} + +ssize_t netxen_sysfs_write_mem(struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t offset, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct netxen_adapter *adapter = dev_get_drvdata(dev); + u64 data; + int ret; + + ret = netxen_sysfs_validate_mem(adapter, offset, size); + if (ret != 0) + return ret; + + memcpy(&data, buf, size); + + if (adapter->pci_mem_write(adapter, offset, data)) + return -EIO; + + return size; +} + + +static struct bin_attribute bin_attr_crb = { + .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = netxen_sysfs_read_crb, + .write = netxen_sysfs_write_crb, +}; + +static struct bin_attribute bin_attr_mem = { + .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = netxen_sysfs_read_mem, + .write = netxen_sysfs_write_mem, +}; + +#ifdef CONFIG_MODULES +static ssize_t +netxen_store_auto_fw_reset(struct module_attribute *mattr, + struct module *mod, const char *buf, size_t count) + +{ + unsigned long new; + + if (strict_strtoul(buf, 16, &new)) + return -EINVAL; + + if ((new == AUTO_FW_RESET_ENABLED) || (new == AUTO_FW_RESET_DISABLED)) { + auto_fw_reset = new; + return count; + } + + return -EINVAL; +} + +static ssize_t +netxen_show_auto_fw_reset(struct module_attribute *mattr, + struct module *mod, char *buf) + +{ + if (auto_fw_reset == AUTO_FW_RESET_ENABLED) + return sprintf(buf, "enabled\n"); + else + return sprintf(buf, "disabled\n"); +} + +static struct module_attribute mod_attr_fw_reset = { + .attr = {.name = "auto_fw_reset", .mode = (S_IRUGO | S_IWUSR)}, + .show = netxen_show_auto_fw_reset, + .store = netxen_store_auto_fw_reset, +}; +#endif + static void netxen_create_sysfs_entries(struct netxen_adapter *adapter) { @@ -2366,6 +2567,33 @@ netxen_remove_sysfs_entries(struct netxen_adapter *adapter) device_remove_file(dev, &dev_attr_bridged_mode); } +static void +netxen_create_diag_entries(struct netxen_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct device *dev; + + dev = &pdev->dev; + if (device_create_file(dev, &dev_attr_diag_mode)) + dev_info(dev, "failed to create diag_mode sysfs entry\n"); + if (device_create_bin_file(dev, &bin_attr_crb)) + dev_info(dev, "failed to create crb sysfs entry\n"); + if (device_create_bin_file(dev, &bin_attr_mem)) + dev_info(dev, "failed to create mem sysfs entry\n"); +} + + +static void +netxen_remove_diag_entries(struct netxen_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct device *dev = &pdev->dev; + + device_remove_file(dev, &dev_attr_diag_mode); + device_remove_bin_file(dev, &bin_attr_crb); + device_remove_bin_file(dev, &bin_attr_mem); +} + #ifdef CONFIG_INET #define is_netxen_netdev(dev) (dev->netdev_ops == &netxen_netdev_ops) @@ -2518,6 +2746,10 @@ static struct pci_driver netxen_driver = { static int __init netxen_init_module(void) { +#ifdef CONFIG_MODULES + struct module *mod = THIS_MODULE; +#endif + printk(KERN_INFO "%s\n", netxen_nic_driver_string); #ifdef CONFIG_INET @@ -2525,6 +2757,12 @@ static int __init netxen_init_module(void) register_inetaddr_notifier(&netxen_inetaddr_cb); #endif +#ifdef CONFIG_MODULES + if (sysfs_create_file(&mod->mkobj.kobj, &mod_attr_fw_reset.attr)) + printk(KERN_ERR "%s: Failed to create auto_fw_reset " + "sysfs entry.", netxen_nic_driver_name); +#endif + return pci_register_driver(&netxen_driver); } @@ -2532,6 +2770,12 @@ module_init(netxen_init_module); static void __exit netxen_exit_module(void) { +#ifdef CONFIG_MODULES + struct module *mod = THIS_MODULE; + + sysfs_remove_file(&mod->mkobj.kobj, &mod_attr_fw_reset.attr); +#endif + pci_unregister_driver(&netxen_driver); #ifdef CONFIG_INET diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c index 462d20f26436..6a87d810e59d 100644 --- a/drivers/net/ni5010.c +++ b/drivers/net/ni5010.c @@ -377,7 +377,7 @@ static int ni5010_open(struct net_device *dev) PRINTK2((KERN_DEBUG "%s: entering ni5010_open()\n", dev->name)); - if (request_irq(dev->irq, &ni5010_interrupt, 0, boardname, dev)) { + if (request_irq(dev->irq, ni5010_interrupt, 0, boardname, dev)) { printk(KERN_WARNING "%s: Cannot get irq %#2x\n", dev->name, dev->irq); return -EAGAIN; } diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c index aad3b370c562..b42f5e522f90 100644 --- a/drivers/net/ni52.c +++ b/drivers/net/ni52.c @@ -284,7 +284,7 @@ static int ni52_open(struct net_device *dev) startrecv586(dev); ni_enaint(); - ret = request_irq(dev->irq, &ni52_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, ni52_interrupt, 0, dev->name, dev); if (ret) { ni_reset586(); return ret; @@ -477,8 +477,8 @@ static int __init ni52_probe1(struct net_device *dev, int ioaddr) for (i = 0; i < ETH_ALEN; i++) dev->dev_addr[i] = inb(dev->base_addr+i); - if (dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1 - || dev->dev_addr[2] != NI52_ADDR2) { + if (dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1 || + dev->dev_addr[2] != NI52_ADDR2) { retval = -ENODEV; goto out; } diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c index 752c2e4d9cf4..ae19aafd2c7e 100644 --- a/drivers/net/ni65.c +++ b/drivers/net/ni65.c @@ -294,7 +294,7 @@ static void ni65_set_performance(struct priv *p) static int ni65_open(struct net_device *dev) { struct priv *p = dev->ml_priv; - int irqval = request_irq(dev->irq, &ni65_interrupt,0, + int irqval = request_irq(dev->irq, ni65_interrupt,0, cards[p->cardno].cardname,dev); if (irqval) { printk(KERN_ERR "%s: unable to get IRQ %d (irqval=%d).\n", diff --git a/drivers/net/niu.c b/drivers/net/niu.c index d6c7ac68f6ea..8ce58c4c7dd3 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -45,10 +45,6 @@ MODULE_DESCRIPTION("NIU ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); -#ifndef DMA_44BIT_MASK -#define DMA_44BIT_MASK 0x00000fffffffffffULL -#endif - #ifndef readq static u64 readq(void __iomem *reg) { @@ -7855,10 +7851,13 @@ static void niu_get_strings(struct net_device *dev, u32 stringset, u8 *data) } } -static int niu_get_stats_count(struct net_device *dev) +static int niu_get_sset_count(struct net_device *dev, int stringset) { struct niu *np = netdev_priv(dev); + if (stringset != ETH_SS_STATS) + return -EINVAL; + return ((np->flags & NIU_FLAGS_XMAC ? NUM_XMAC_STAT_KEYS : NUM_BMAC_STAT_KEYS) + @@ -7978,7 +7977,7 @@ static const struct ethtool_ops niu_ethtool_ops = { .get_settings = niu_get_settings, .set_settings = niu_set_settings, .get_strings = niu_get_strings, - .get_stats_count = niu_get_stats_count, + .get_sset_count = niu_get_sset_count, .get_ethtool_stats = niu_get_ethtool_stats, .phys_id = niu_phys_id, .get_rxnfc = niu_get_nfc, @@ -8144,7 +8143,7 @@ static void __devinit niu_vpd_parse_version(struct niu *np) int i; for (i = 0; i < len - 5; i++) { - if (!strncmp(s + i, "FCode ", 5)) + if (!strncmp(s + i, "FCode ", 6)) break; } if (i >= len - 5) @@ -9915,7 +9914,7 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev, PCI_EXP_DEVCTL_RELAX_EN); pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16); - dma_mask = DMA_44BIT_MASK; + dma_mask = DMA_BIT_MASK(44); err = pci_set_dma_mask(pdev, dma_mask); if (!err) { dev->features |= NETIF_F_HIGHDMA; diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index 57fd483dbb1f..1f6327d41536 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -648,8 +648,8 @@ static void phy_intr(struct net_device *ndev) dprintk("phy_intr: tbisr=%08x, tanar=%08x, tanlpar=%08x\n", tbisr, tanar, tanlpar); - if ( (fullduplex = (tanlpar & TANAR_FULL_DUP) - && (tanar & TANAR_FULL_DUP)) ) { + if ( (fullduplex = (tanlpar & TANAR_FULL_DUP) && + (tanar & TANAR_FULL_DUP)) ) { /* both of us are full duplex */ writel(readl(dev->base + TXCFG) @@ -661,12 +661,12 @@ static void phy_intr(struct net_device *ndev) writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT, dev->base + GPIOR); - } else if(((tanlpar & TANAR_HALF_DUP) - && (tanar & TANAR_HALF_DUP)) - || ((tanlpar & TANAR_FULL_DUP) - && (tanar & TANAR_HALF_DUP)) - || ((tanlpar & TANAR_HALF_DUP) - && (tanar & TANAR_FULL_DUP))) { + } else if (((tanlpar & TANAR_HALF_DUP) && + (tanar & TANAR_HALF_DUP)) || + ((tanlpar & TANAR_FULL_DUP) && + (tanar & TANAR_HALF_DUP)) || + ((tanlpar & TANAR_HALF_DUP) && + (tanar & TANAR_FULL_DUP))) { /* one or both of us are half duplex */ writel((readl(dev->base + TXCFG) @@ -720,16 +720,16 @@ static void phy_intr(struct net_device *ndev) newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN; - if (newlinkstate & LINK_UP - && dev->linkstate != newlinkstate) { + if (newlinkstate & LINK_UP && + dev->linkstate != newlinkstate) { netif_start_queue(ndev); netif_wake_queue(ndev); printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n", ndev->name, speeds[speed], fullduplex ? "full" : "half"); - } else if (newlinkstate & LINK_DOWN - && dev->linkstate != newlinkstate) { + } else if (newlinkstate & LINK_DOWN && + dev->linkstate != newlinkstate) { netif_stop_queue(ndev); printk(KERN_INFO "%s: link now down.\n", ndev->name); } diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index c254a7f5b9f5..1673eb045e1e 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -1216,7 +1216,7 @@ static int pasemi_mac_open(struct net_device *dev) snprintf(mac->tx_irq_name, sizeof(mac->tx_irq_name), "%s tx", dev->name); - ret = request_irq(mac->tx->chan.irq, &pasemi_mac_tx_intr, IRQF_DISABLED, + ret = request_irq(mac->tx->chan.irq, pasemi_mac_tx_intr, IRQF_DISABLED, mac->tx_irq_name, mac->tx); if (ret) { dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n", @@ -1227,7 +1227,7 @@ static int pasemi_mac_open(struct net_device *dev) snprintf(mac->rx_irq_name, sizeof(mac->rx_irq_name), "%s rx", dev->name); - ret = request_irq(mac->rx->chan.irq, &pasemi_mac_rx_intr, IRQF_DISABLED, + ret = request_irq(mac->rx->chan.irq, pasemi_mac_rx_intr, IRQF_DISABLED, mac->rx_irq_name, mac->rx); if (ret) { dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n", diff --git a/drivers/net/pasemi_mac_ethtool.c b/drivers/net/pasemi_mac_ethtool.c index 28a86224879d..fefa79e34b95 100644 --- a/drivers/net/pasemi_mac_ethtool.c +++ b/drivers/net/pasemi_mac_ethtool.c @@ -77,6 +77,19 @@ pasemi_mac_ethtool_get_settings(struct net_device *netdev, return phy_ethtool_gset(phydev, cmd); } +static int +pasemi_mac_ethtool_set_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + struct pasemi_mac *mac = netdev_priv(netdev); + struct phy_device *phydev = mac->phydev; + + if (!phydev) + return -EOPNOTSUPP; + + return phy_ethtool_sset(phydev, cmd); +} + static void pasemi_mac_ethtool_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) @@ -150,6 +163,7 @@ static void pasemi_mac_get_strings(struct net_device *netdev, u32 stringset, const struct ethtool_ops pasemi_mac_ethtool_ops = { .get_settings = pasemi_mac_ethtool_get_settings, + .set_settings = pasemi_mac_ethtool_set_settings, .get_drvinfo = pasemi_mac_ethtool_get_drvinfo, .get_msglevel = pasemi_mac_ethtool_get_msglevel, .set_msglevel = pasemi_mac_ethtool_set_msglevel, diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c index 0c44b48f1384..480af402affd 100644 --- a/drivers/net/pci-skeleton.c +++ b/drivers/net/pci-skeleton.c @@ -1225,8 +1225,8 @@ static void netdrv_timer (unsigned long data) mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); if (!tp->duplex_lock && mii_lpa != 0xffff) { - int duplex = (mii_lpa & LPA_100FULL) - || (mii_lpa & 0x01C0) == 0x0040; + int duplex = ((mii_lpa & LPA_100FULL) || + (mii_lpa & 0x01C0) == 0x0040); if (tp->full_duplex != duplex) { tp->full_duplex = duplex; printk (KERN_INFO @@ -1612,8 +1612,8 @@ static void netdrv_weird_interrupt (struct net_device *dev, (tp->drv_flags & HAS_LNK_CHNG)) { /* Really link-change on new chips. */ int lpar = NETDRV_R16 (NWayLPAR); - int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040 - || tp->duplex_lock; + int duplex = ((lpar & 0x0100) || (lpar & 0x01C0) == 0x0040 || + tp->duplex_lock); if (tp->full_duplex != duplex) { tp->full_duplex = duplex; NETDRV_W8 (Cfg9346, Cfg9346_Unlock); @@ -1820,8 +1820,8 @@ static void netdrv_set_rx_mode (struct net_device *dev) AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 800597b82d18..81bafd578478 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -1214,8 +1214,8 @@ static irqreturn_t ax_interrupt(int irq, void *dev_id) ei_local->irqlock = 1; /* !!Assumption!! -- we stay in page 0. Don't break this. */ - while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 - && ++nr_serviced < MAX_SERVICE) + while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 && + ++nr_serviced < MAX_SERVICE) { if (!netif_running(dev) || (interrupts == 0xff)) { if (ei_debug > 1) diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index 6e3e1ced6db4..8ad8384fc1c0 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -256,7 +256,7 @@ static int fmvj18x_probe(struct pcmcia_device *link) /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = &fjn_interrupt; + link->irq.Handler = fjn_interrupt; /* General socket configuration */ link->conf.Attributes = CONF_ENABLE_IRQ; @@ -364,9 +364,9 @@ static int fmvj18x_config(struct pcmcia_device *link) switch (link->manf_id) { case MANFID_TDK: cardtype = TDK; - if (link->card_id == PRODID_TDK_GN3410 - || link->card_id == PRODID_TDK_NP9610 - || link->card_id == PRODID_TDK_MN3200) { + if (link->card_id == PRODID_TDK_GN3410 || + link->card_id == PRODID_TDK_NP9610 || + link->card_id == PRODID_TDK_MN3200) { /* MultiFunction Card */ link->conf.ConfigBase = 0x800; link->conf.ConfigIndex = 0x47; @@ -582,11 +582,11 @@ static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) */ for (i = 0; i < 0x200; i++) { if (readb(base+i*2) == 0x22) { - if (readb(base+(i-1)*2) == 0xff - && readb(base+(i+5)*2) == 0x04 - && readb(base+(i+6)*2) == 0x06 - && readb(base+(i+13)*2) == 0xff) - break; + if (readb(base+(i-1)*2) == 0xff && + readb(base+(i+5)*2) == 0x04 && + readb(base+(i+6)*2) == 0x06 && + readb(base+(i+13)*2) == 0xff) + break; } } @@ -1186,8 +1186,8 @@ static void set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) { memset(mc_filter, 0xff, sizeof(mc_filter)); outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ - } else if (dev->mc_count > MC_FILTERBREAK - || (dev->flags & IFF_ALLMULTI)) { + } else if (dev->mc_count > MC_FILTERBREAK || + (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); outb(2, ioaddr + RX_MODE); /* Use normal mode. */ diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index dae5ef6b2609..8a5ae3b182ed 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -464,7 +464,7 @@ static int nmclan_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 5; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.Handler = &mace_interrupt; + link->irq.Handler = mace_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index cbe462ed221f..2d26b6ca28b9 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -586,8 +586,8 @@ static int pcnet_config(struct pcmcia_device *link) dev->if_port = 0; } - if ((link->conf.ConfigBase == 0x03c0) - && (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) { + if ((link->conf.ConfigBase == 0x03c0) && + (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) { printk(KERN_INFO "pcnet_cs: this is an AX88190 card!\n"); printk(KERN_INFO "pcnet_cs: use axnet_cs instead.\n"); goto failed; @@ -1751,6 +1751,13 @@ static struct pcmcia_device_id pcnet_ids[] = { PCMCIA_DEVICE_NULL }; MODULE_DEVICE_TABLE(pcmcia, pcnet_ids); +MODULE_FIRMWARE("cis/PCMLM28.cis"); +MODULE_FIRMWARE("cis/DP83903.cis"); +MODULE_FIRMWARE("cis/LA-PCM.cis"); +MODULE_FIRMWARE("PE520.cis"); +MODULE_FIRMWARE("cis/NE2K.cis"); +MODULE_FIRMWARE("cis/PE-200.cis"); +MODULE_FIRMWARE("cis/tamarack.cis"); static struct pcmcia_driver pcnet_driver = { .drv = { diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index 9e0da370912e..cc4853bc0253 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -480,10 +480,10 @@ static int mhz_mfc_config(struct pcmcia_device *link) mem.CardOffset = link->conf.ConfigBase; i = pcmcia_map_mem_page(link, link->win, &mem); - if ((i == 0) - && (smc->manfid == MANFID_MEGAHERTZ) - && (smc->cardid == PRODID_MEGAHERTZ_EM3288)) - mhz_3288_power(link); + if ((i == 0) && + (smc->manfid == MANFID_MEGAHERTZ) && + (smc->cardid == PRODID_MEGAHERTZ_EM3288)) + mhz_3288_power(link); return 0; } diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index fe504b7f369f..a2eda28f903e 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -385,9 +385,9 @@ PrintRegisters(struct net_device *dev) printk("\n"); } for (page=0x40 ; page <= 0x5f; page++) { - if (page == 0x43 || (page >= 0x46 && page <= 0x4f) - || (page >= 0x51 && page <=0x5e)) - continue; + if (page == 0x43 || (page >= 0x46 && page <= 0x4f) || + (page >= 0x51 && page <=0x5e)) + continue; printk(KDBG_XIRC "Register page %2x: ", page); SelectPage(page); for (i = 8; i < 16; i++) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index c1b3f09f452c..dcc67a35e8f2 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -1515,8 +1515,8 @@ static void __devinit pcnet32_probe_vlbus(unsigned int *pcnet32_portlist) if (request_region (ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_vlbus")) { /* check if there is really a pcnet chip on that ioaddr */ - if ((inb(ioaddr + 14) == 0x57) - && (inb(ioaddr + 15) == 0x57)) { + if ((inb(ioaddr + 14) == 0x57) && + (inb(ioaddr + 15) == 0x57)) { pcnet32_probe1(ioaddr, 0, NULL); } else { release_region(ioaddr, PCNET32_TOTAL_SIZE); @@ -1610,8 +1610,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) a = &pcnet32_wio; } else { pcnet32_dwio_reset(ioaddr); - if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 - && pcnet32_dwio_check(ioaddr)) { + if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && + pcnet32_dwio_check(ioaddr)) { a = &pcnet32_dwio; } else { if (pcnet32_debug & NETIF_MSG_PROBE) @@ -1750,8 +1750,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) for (i = 0; i < 6; i++) promaddr[i] = inb(ioaddr + i); - if (memcmp(promaddr, dev->dev_addr, 6) - || !is_valid_ether_addr(dev->dev_addr)) { + if (memcmp(promaddr, dev->dev_addr, 6) || + !is_valid_ether_addr(dev->dev_addr)) { if (is_valid_ether_addr(promaddr)) { if (pcnet32_debug & NETIF_MSG_PROBE) { printk(" warning: CSR address invalid,\n"); @@ -1840,8 +1840,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) lp->mii = mii; lp->chip_version = chip_version; lp->msg_enable = pcnet32_debug; - if ((cards_found >= MAX_UNITS) - || (options[cards_found] >= sizeof(options_mapping))) + if ((cards_found >= MAX_UNITS) || + (options[cards_found] >= sizeof(options_mapping))) lp->options = PCNET32_PORT_ASEL; else lp->options = options_mapping[options[cards_found]]; @@ -1866,8 +1866,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) goto err_free_ring; } /* detect special T1/E1 WAN card by checking for MAC address */ - if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 - && dev->dev_addr[2] == 0x75) + if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && + dev->dev_addr[2] == 0x75) lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI; lp->init_block->mode = cpu_to_le16(0x0003); /* Disable Rx and Tx. */ @@ -2095,7 +2095,7 @@ static int pcnet32_open(struct net_device *dev) int rc; unsigned long flags; - if (request_irq(dev->irq, &pcnet32_interrupt, + if (request_irq(dev->irq, pcnet32_interrupt, lp->shared_irq ? IRQF_SHARED : 0, dev->name, (void *)dev)) { return -EAGAIN; diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index f81e53222230..f63c96a4ecb4 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/phy.h> +#include <linux/brcmphy.h> #define PHY_ID_BCM50610 0x0143bd60 #define PHY_ID_BCM50610M 0x0143bd70 @@ -24,6 +25,9 @@ #define BRCM_PHY_MODEL(phydev) \ ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask) +#define BRCM_PHY_REV(phydev) \ + ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask)) + #define MII_BCM54XX_ECR 0x10 /* BCM54xx extended control register */ #define MII_BCM54XX_ECR_IM 0x1000 /* Interrupt mask */ @@ -94,22 +98,35 @@ #define BCM_LED_SRC_OFF 0xe /* Tied high */ #define BCM_LED_SRC_ON 0xf /* Tied low */ + /* * BCM5482: Shadow registers * Shadow values go into bits [14:10] of register 0x1c to select a shadow * register to access. */ +/* 00101: Spare Control Register 3 */ +#define BCM54XX_SHD_SCR3 0x05 +#define BCM54XX_SHD_SCR3_DEF_CLK125 0x0001 +#define BCM54XX_SHD_SCR3_DLLAPD_DIS 0x0002 +#define BCM54XX_SHD_SCR3_TRDDAPD 0x0004 + +/* 01010: Auto Power-Down */ +#define BCM54XX_SHD_APD 0x0a +#define BCM54XX_SHD_APD_EN 0x0020 + #define BCM5482_SHD_LEDS1 0x0d /* 01101: LED Selector 1 */ /* LED3 / ~LINKSPD[2] selector */ #define BCM5482_SHD_LEDS1_LED3(src) ((src & 0xf) << 4) /* LED1 / ~LINKSPD[1] selector */ #define BCM5482_SHD_LEDS1_LED1(src) ((src & 0xf) << 0) +#define BCM54XX_SHD_RGMII_MODE 0x0b /* 01011: RGMII Mode Selector */ #define BCM5482_SHD_SSD 0x14 /* 10100: Secondary SerDes control */ #define BCM5482_SHD_SSD_LEDM 0x0008 /* SSD LED Mode enable */ #define BCM5482_SHD_SSD_EN 0x0001 /* SSD enable */ #define BCM5482_SHD_MODE 0x1f /* 11111: Mode Control Register */ #define BCM5482_SHD_MODE_1000BX 0x0001 /* Enable 1000BASE-X registers */ + /* * EXPANSION SHADOW ACCESS REGISTERS. (PHY REG 0x15, 0x16, and 0x17) */ @@ -138,16 +155,6 @@ #define BCM5482_SSD_SGMII_SLAVE_EN 0x0002 /* Slave mode enable */ #define BCM5482_SSD_SGMII_SLAVE_AD 0x0001 /* Slave auto-detection */ -/* - * Device flags for PHYs that can be configured for different operating - * modes. - */ -#define PHY_BCM_FLAGS_VALID 0x80000000 -#define PHY_BCM_FLAGS_INTF_XAUI 0x00000020 -#define PHY_BCM_FLAGS_INTF_SGMII 0x00000010 -#define PHY_BCM_FLAGS_MODE_1000BX 0x00000002 -#define PHY_BCM_FLAGS_MODE_COPPER 0x00000001 - /*****************************************************************************/ /* Fast Ethernet Transceiver definitions. */ @@ -237,53 +244,145 @@ static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); } +/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */ static int bcm50610_a0_workaround(struct phy_device *phydev) { int err; - err = bcm54xx_auxctl_write(phydev, - MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, - MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA | - MII_BCM54XX_AUXCTL_ACTL_TX_6DB); - if (err < 0) - return err; - - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08, - MII_BCM54XX_EXP_EXP08_RJCT_2MHZ | - MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE); - if (err < 0) - goto error; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0, MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN | MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF); if (err < 0) - goto error; + return err; err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3, MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); if (err < 0) - goto error; + return err; err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, MII_BCM54XX_EXP_EXP75_VDACCTRL); if (err < 0) - goto error; + return err; err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96, MII_BCM54XX_EXP_EXP96_MYST); if (err < 0) - goto error; + return err; err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97, MII_BCM54XX_EXP_EXP97_MYST); + return err; +} + +static int bcm54xx_phydsp_config(struct phy_device *phydev) +{ + int err, err2; + + /* Enable the SMDSP clock */ + err = bcm54xx_auxctl_write(phydev, + MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, + MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA | + MII_BCM54XX_AUXCTL_ACTL_TX_6DB); + if (err < 0) + return err; + + if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || + BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) { + /* Clear bit 9 to fix a phy interop issue. */ + err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08, + MII_BCM54XX_EXP_EXP08_RJCT_2MHZ); + if (err < 0) + goto error; + + if (phydev->drv->phy_id == PHY_ID_BCM50610) { + err = bcm50610_a0_workaround(phydev); + if (err < 0) + goto error; + } + } + + if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) { + int val; + + val = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75); + if (val < 0) + goto error; + + val |= MII_BCM54XX_EXP_EXP75_CM_OSC; + err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, val); + } + error: - bcm54xx_auxctl_write(phydev, - MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, - MII_BCM54XX_AUXCTL_ACTL_TX_6DB); + /* Disable the SMDSP clock */ + err2 = bcm54xx_auxctl_write(phydev, + MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, + MII_BCM54XX_AUXCTL_ACTL_TX_6DB); - return err; + /* Return the first error reported. */ + return err ? err : err2; +} + +static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) +{ + u32 val, orig; + bool clk125en = true; + + /* Abort if we are using an untested phy. */ + if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 || + BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 || + BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M) + return; + + val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); + if (val < 0) + return; + + orig = val; + + if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || + BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && + BRCM_PHY_REV(phydev) >= 0x3) { + /* + * Here, bit 0 _disables_ CLK125 when set. + * This bit is set by default. + */ + clk125en = false; + } else { + if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) { + /* Here, bit 0 _enables_ CLK125 when set */ + val &= ~BCM54XX_SHD_SCR3_DEF_CLK125; + clk125en = false; + } + } + + if (clk125en == false || + (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) + val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS; + else + val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; + + if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) + val |= BCM54XX_SHD_SCR3_TRDDAPD; + + if (orig != val) + bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); + + val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); + if (val < 0) + return; + + orig = val; + + if (clk125en == false || + (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) + val |= BCM54XX_SHD_APD_EN; + else + val &= ~BCM54XX_SHD_APD_EN; + + if (orig != val) + bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); } static int bcm54xx_config_init(struct phy_device *phydev) @@ -308,38 +407,17 @@ static int bcm54xx_config_init(struct phy_device *phydev) if (err < 0) return err; - if (phydev->drv->phy_id == PHY_ID_BCM50610) { - err = bcm50610_a0_workaround(phydev); - if (err < 0) - return err; - } - - if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) { - int err2; - - err = bcm54xx_auxctl_write(phydev, - MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, - MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA | - MII_BCM54XX_AUXCTL_ACTL_TX_6DB); - if (err < 0) - return err; - - reg = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75); - if (reg < 0) - goto error; + if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || + BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && + (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE)) + bcm54xx_shadow_write(phydev, BCM54XX_SHD_RGMII_MODE, 0); - reg |= MII_BCM54XX_EXP_EXP75_CM_OSC; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, reg); + if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) || + (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) || + (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) + bcm54xx_adjust_rxrefclk(phydev); -error: - err2 = bcm54xx_auxctl_write(phydev, - MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, - MII_BCM54XX_AUXCTL_ACTL_TX_6DB); - if (err) - return err; - if (err2) - return err2; - } + bcm54xx_phydsp_config(phydev); return 0; } @@ -564,9 +642,11 @@ static int brcm_fet_config_init(struct phy_device *phydev) if (err < 0) goto done; - /* Enable auto power down */ - err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2, - MII_BRCM_FET_SHDW_AS2_APDE); + if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) { + /* Enable auto power down */ + err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2, + MII_BRCM_FET_SHDW_AS2_APDE); + } done: /* Disable shadow register access */ diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 6b71b0034060..b0e9f9c51721 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -254,12 +254,12 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0) return -EINVAL; - if (cmd->autoneg == AUTONEG_DISABLE - && ((cmd->speed != SPEED_1000 - && cmd->speed != SPEED_100 - && cmd->speed != SPEED_10) - || (cmd->duplex != DUPLEX_HALF - && cmd->duplex != DUPLEX_FULL))) + if (cmd->autoneg == AUTONEG_DISABLE && + ((cmd->speed != SPEED_1000 && + cmd->speed != SPEED_100 && + cmd->speed != SPEED_10) || + (cmd->duplex != DUPLEX_HALF && + cmd->duplex != DUPLEX_FULL))) return -EINVAL; phydev->autoneg = cmd->autoneg; @@ -353,9 +353,9 @@ int phy_mii_ioctl(struct phy_device *phydev, phy_write(phydev, mii_data->reg_num, val); - if (mii_data->reg_num == MII_BMCR - && val & BMCR_RESET - && phydev->drv->config_init) { + if (mii_data->reg_num == MII_BMCR && + val & BMCR_RESET && + phydev->drv->config_init) { phy_scan_fixups(phydev); phydev->drv->config_init(phydev); } diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 00487f569cfd..3327e9fc7b51 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -372,8 +372,8 @@ plip_bh(struct work_struct *work) nl->is_deferred = 0; f = connection_state_table[nl->connection]; - if ((r = (*f)(nl->dev, nl, snd, rcv)) != OK - && (r = plip_bh_timeout_error(nl->dev, nl, snd, rcv, r)) != OK) { + if ((r = (*f)(nl->dev, nl, snd, rcv)) != OK && + (r = plip_bh_timeout_error(nl->dev, nl, snd, rcv, r)) != OK) { nl->is_deferred = 1; schedule_delayed_work(&nl->deferred, 1); } @@ -416,9 +416,8 @@ plip_bh_timeout_error(struct net_device *dev, struct net_local *nl, if (error != ERROR) { /* Timeout */ nl->timeout_count++; - if ((error == HS_TIMEOUT - && nl->timeout_count <= 10) - || nl->timeout_count <= 3) { + if ((error == HS_TIMEOUT && nl->timeout_count <= 10) || + nl->timeout_count <= 3) { spin_unlock_irq(&nl->lock); /* Try again later */ return TIMEOUT; @@ -624,8 +623,8 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl, if (plip_receive(nibble_timeout, dev, &rcv->nibble, &rcv->length.b.msb)) return TIMEOUT; - if (rcv->length.h > dev->mtu + dev->hard_header_len - || rcv->length.h < 8) { + if (rcv->length.h > dev->mtu + dev->hard_header_len || + rcv->length.h < 8) { printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h); return ERROR; } diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 6de8399d6dd9..6a375ea4947d 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -36,7 +36,7 @@ #define PPP_VERSION "2.4.2" -#define OBUFSIZE 256 +#define OBUFSIZE 4096 /* Structure for storing local state. */ struct asyncppp { @@ -337,10 +337,7 @@ ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) return 0; } -/* - * This can now be called from hard interrupt level as well - * as soft interrupt level or mainline. - */ +/* May sleep, don't call from interrupt level or with interrupts disabled */ static void ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) @@ -561,8 +558,8 @@ ppp_async_encode(struct asyncppp *ap) * Start of a new packet - insert the leading FLAG * character if necessary. */ - if (islcp || flag_time == 0 - || time_after_eq(jiffies, ap->last_xmit + flag_time)) + if (islcp || flag_time == 0 || + time_after_eq(jiffies, ap->last_xmit + flag_time)) *buf++ = PPP_FLAG; ap->last_xmit = jiffies; fcs = PPP_INITFCS; @@ -699,8 +696,8 @@ ppp_async_push(struct asyncppp *ap) */ clear_bit(XMIT_BUSY, &ap->xmit_flags); /* any more work to do? if not, exit the loop */ - if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) - || (!tty_stuffed && ap->tpkt))) + if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) || + (!tty_stuffed && ap->tpkt))) break; /* more work to do, see if we can do it now */ if (test_and_set_bit(XMIT_BUSY, &ap->xmit_flags)) @@ -757,8 +754,8 @@ scan_ordinary(struct asyncppp *ap, const unsigned char *buf, int count) for (i = 0; i < count; ++i) { c = buf[i]; - if (c == PPP_ESCAPE || c == PPP_FLAG - || (c < 0x20 && (ap->raccm & (1 << c)) != 0)) + if (c == PPP_ESCAPE || c == PPP_FLAG || + (c < 0x20 && (ap->raccm & (1 << c)) != 0)) break; } return i; diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c index 034c1c650bcb..695bc83e0cfd 100644 --- a/drivers/net/ppp_deflate.c +++ b/drivers/net/ppp_deflate.c @@ -111,11 +111,11 @@ static void *z_comp_alloc(unsigned char *options, int opt_len) struct ppp_deflate_state *state; int w_size; - if (opt_len != CILEN_DEFLATE - || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) - || options[1] != CILEN_DEFLATE - || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL - || options[3] != DEFLATE_CHK_SEQUENCE) + if (opt_len != CILEN_DEFLATE || + (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || + options[1] != CILEN_DEFLATE || + DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || + options[3] != DEFLATE_CHK_SEQUENCE) return NULL; w_size = DEFLATE_SIZE(options[2]); if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) @@ -163,12 +163,12 @@ static int z_comp_init(void *arg, unsigned char *options, int opt_len, { struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; - if (opt_len < CILEN_DEFLATE - || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) - || options[1] != CILEN_DEFLATE - || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL - || DEFLATE_SIZE(options[2]) != state->w_size - || options[3] != DEFLATE_CHK_SEQUENCE) + if (opt_len < CILEN_DEFLATE || + (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || + options[1] != CILEN_DEFLATE || + DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || + DEFLATE_SIZE(options[2]) != state->w_size || + options[3] != DEFLATE_CHK_SEQUENCE) return 0; state->seqno = 0; @@ -330,11 +330,11 @@ static void *z_decomp_alloc(unsigned char *options, int opt_len) struct ppp_deflate_state *state; int w_size; - if (opt_len != CILEN_DEFLATE - || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) - || options[1] != CILEN_DEFLATE - || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL - || options[3] != DEFLATE_CHK_SEQUENCE) + if (opt_len != CILEN_DEFLATE || + (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || + options[1] != CILEN_DEFLATE || + DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || + options[3] != DEFLATE_CHK_SEQUENCE) return NULL; w_size = DEFLATE_SIZE(options[2]); if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) @@ -381,12 +381,12 @@ static int z_decomp_init(void *arg, unsigned char *options, int opt_len, { struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; - if (opt_len < CILEN_DEFLATE - || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) - || options[1] != CILEN_DEFLATE - || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL - || DEFLATE_SIZE(options[2]) != state->w_size - || options[3] != DEFLATE_CHK_SEQUENCE) + if (opt_len < CILEN_DEFLATE || + (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || + options[1] != CILEN_DEFLATE || + DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || + DEFLATE_SIZE(options[2]) != state->w_size || + options[3] != DEFLATE_CHK_SEQUENCE) return 0; state->seqno = 0; diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 965adb6174c3..2282e729edbe 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -184,7 +184,7 @@ static atomic_t ppp_unit_count = ATOMIC_INIT(0); static atomic_t channel_count = ATOMIC_INIT(0); /* per-net private data for this module */ -static int ppp_net_id; +static int ppp_net_id __read_mostly; struct ppp_net { /* units to ppp mapping */ struct idr units_idr; @@ -425,8 +425,8 @@ static ssize_t ppp_read(struct file *file, char __user *buf, * network traffic (demand mode). */ struct ppp *ppp = PF_TO_PPP(pf); - if (ppp->n_channels == 0 - && (ppp->flags & SC_LOOP_TRAFFIC) == 0) + if (ppp->n_channels == 0 && + (ppp->flags & SC_LOOP_TRAFFIC) == 0) break; } ret = -EAGAIN; @@ -511,8 +511,8 @@ static unsigned int ppp_poll(struct file *file, poll_table *wait) else if (pf->kind == INTERFACE) { /* see comment in ppp_read */ struct ppp *ppp = PF_TO_PPP(pf); - if (ppp->n_channels == 0 - && (ppp->flags & SC_LOOP_TRAFFIC) == 0) + if (ppp->n_channels == 0 && + (ppp->flags & SC_LOOP_TRAFFIC) == 0) mask |= POLLIN | POLLRDNORM; } @@ -864,12 +864,7 @@ static const struct file_operations ppp_device_fops = { static __net_init int ppp_init_net(struct net *net) { - struct ppp_net *pn; - int err; - - pn = kzalloc(sizeof(*pn), GFP_KERNEL); - if (!pn) - return -ENOMEM; + struct ppp_net *pn = net_generic(net, ppp_net_id); idr_init(&pn->units_idr); mutex_init(&pn->all_ppp_mutex); @@ -879,32 +874,21 @@ static __net_init int ppp_init_net(struct net *net) spin_lock_init(&pn->all_channels_lock); - err = net_assign_generic(net, ppp_net_id, pn); - if (err) { - kfree(pn); - return err; - } - return 0; } static __net_exit void ppp_exit_net(struct net *net) { - struct ppp_net *pn; + struct ppp_net *pn = net_generic(net, ppp_net_id); - pn = net_generic(net, ppp_net_id); idr_destroy(&pn->units_idr); - /* - * if someone has cached our net then - * further net_generic call will return NULL - */ - net_assign_generic(net, ppp_net_id, NULL); - kfree(pn); } static struct pernet_operations ppp_net_ops = { .init = ppp_init_net, .exit = ppp_exit_net, + .id = &ppp_net_id, + .size = sizeof(struct ppp_net), }; #define PPP_MAJOR 108 @@ -917,7 +901,7 @@ static int __init ppp_init(void) printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); - err = register_pernet_gen_device(&ppp_net_id, &ppp_net_ops); + err = register_pernet_device(&ppp_net_ops); if (err) { printk(KERN_ERR "failed to register PPP pernet device (%d)\n", err); goto out; @@ -943,7 +927,7 @@ static int __init ppp_init(void) out_chrdev: unregister_chrdev(PPP_MAJOR, "ppp"); out_net: - unregister_pernet_gen_device(ppp_net_id, &ppp_net_ops); + unregister_pernet_device(&ppp_net_ops); out: return err; } @@ -1073,8 +1057,8 @@ ppp_xmit_process(struct ppp *ppp) ppp_xmit_lock(ppp); if (!ppp->closing) { ppp_push(ppp); - while (!ppp->xmit_pending - && (skb = skb_dequeue(&ppp->file.xq))) + while (!ppp->xmit_pending && + (skb = skb_dequeue(&ppp->file.xq))) ppp_send_frame(ppp, skb); /* If there's no work left to do, tell the core net code that we can accept some more. */ @@ -1153,18 +1137,18 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) /* the filter instructions are constructed assuming a four-byte PPP header on each packet */ *skb_push(skb, 2) = 1; - if (ppp->pass_filter - && sk_run_filter(skb, ppp->pass_filter, - ppp->pass_len) == 0) { + if (ppp->pass_filter && + sk_run_filter(skb, ppp->pass_filter, + ppp->pass_len) == 0) { if (ppp->debug & 1) printk(KERN_DEBUG "PPP: outbound frame not passed\n"); kfree_skb(skb); return; } /* if this packet passes the active filter, record the time */ - if (!(ppp->active_filter - && sk_run_filter(skb, ppp->active_filter, - ppp->active_len) == 0)) + if (!(ppp->active_filter && + sk_run_filter(skb, ppp->active_filter, + ppp->active_len) == 0)) ppp->last_xmit = jiffies; skb_pull(skb, 2); #else @@ -1218,8 +1202,8 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) } /* try to do packet compression */ - if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state - && proto != PPP_LCP && proto != PPP_CCP) { + if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state && + proto != PPP_LCP && proto != PPP_CCP) { if (!(ppp->flags & SC_CCP_UP) && (ppp->flags & SC_MUST_COMP)) { if (net_ratelimit()) printk(KERN_ERR "ppp: compression required but down - pkt dropped.\n"); @@ -1593,8 +1577,8 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) /* put it on the channel queue */ skb_queue_tail(&pch->file.rq, skb); /* drop old frames if queue too long */ - while (pch->file.rq.qlen > PPP_MAX_RQLEN - && (skb = skb_dequeue(&pch->file.rq))) + while (pch->file.rq.qlen > PPP_MAX_RQLEN && + (skb = skb_dequeue(&pch->file.rq))) kfree_skb(skb); wake_up_interruptible(&pch->file.rwait); } else { @@ -1670,8 +1654,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) * Note that some decompressors need to see uncompressed frames * that come in as well as compressed frames. */ - if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN) - && (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0) + if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN) && + (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0) skb = ppp_decompress_frame(ppp, skb); if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR) @@ -1742,8 +1726,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) /* control or unknown frame - pass it to pppd */ skb_queue_tail(&ppp->file.rq, skb); /* limit queue length by dropping old frames */ - while (ppp->file.rq.qlen > PPP_MAX_RQLEN - && (skb = skb_dequeue(&ppp->file.rq))) + while (ppp->file.rq.qlen > PPP_MAX_RQLEN && + (skb = skb_dequeue(&ppp->file.rq))) kfree_skb(skb); /* wake up any process polling or blocking on read */ wake_up_interruptible(&ppp->file.rwait); @@ -1761,26 +1745,26 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) goto err; *skb_push(skb, 2) = 0; - if (ppp->pass_filter - && sk_run_filter(skb, ppp->pass_filter, - ppp->pass_len) == 0) { + if (ppp->pass_filter && + sk_run_filter(skb, ppp->pass_filter, + ppp->pass_len) == 0) { if (ppp->debug & 1) printk(KERN_DEBUG "PPP: inbound frame " "not passed\n"); kfree_skb(skb); return; } - if (!(ppp->active_filter - && sk_run_filter(skb, ppp->active_filter, - ppp->active_len) == 0)) + if (!(ppp->active_filter && + sk_run_filter(skb, ppp->active_filter, + ppp->active_len) == 0)) ppp->last_recv = jiffies; __skb_pull(skb, 2); } else #endif /* CONFIG_PPP_FILTER */ ppp->last_recv = jiffies; - if ((ppp->dev->flags & IFF_UP) == 0 - || ppp->npmode[npi] != NPMODE_PASS) { + if ((ppp->dev->flags & IFF_UP) == 0 || + ppp->npmode[npi] != NPMODE_PASS) { kfree_skb(skb); } else { /* chop off protocol */ @@ -2244,13 +2228,13 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg) unsigned char ccp_option[CCP_MAX_OPTION_LENGTH]; err = -EFAULT; - if (copy_from_user(&data, (void __user *) arg, sizeof(data)) - || (data.length <= CCP_MAX_OPTION_LENGTH - && copy_from_user(ccp_option, (void __user *) data.ptr, data.length))) + if (copy_from_user(&data, (void __user *) arg, sizeof(data)) || + (data.length <= CCP_MAX_OPTION_LENGTH && + copy_from_user(ccp_option, (void __user *) data.ptr, data.length))) goto out; err = -EINVAL; - if (data.length > CCP_MAX_OPTION_LENGTH - || ccp_option[1] < 2 || ccp_option[1] > data.length) + if (data.length > CCP_MAX_OPTION_LENGTH || + ccp_option[1] < 2 || ccp_option[1] > data.length) goto out; cp = try_then_request_module( @@ -2835,7 +2819,7 @@ static void __exit ppp_cleanup(void) unregister_chrdev(PPP_MAJOR, "ppp"); device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0)); class_destroy(ppp_class); - unregister_pernet_gen_device(ppp_net_id, &ppp_net_ops); + unregister_pernet_device(&ppp_net_ops); } /* diff --git a/drivers/net/ppp_mppe.c b/drivers/net/ppp_mppe.c index 88f03c9e9403..6d1a1b80cc3e 100644 --- a/drivers/net/ppp_mppe.c +++ b/drivers/net/ppp_mppe.c @@ -195,8 +195,8 @@ static void *mppe_alloc(unsigned char *options, int optlen) struct ppp_mppe_state *state; unsigned int digestsize; - if (optlen != CILEN_MPPE + sizeof(state->master_key) - || options[0] != CI_MPPE || options[1] != CILEN_MPPE) + if (optlen != CILEN_MPPE + sizeof(state->master_key) || + options[0] != CI_MPPE || options[1] != CILEN_MPPE) goto out; state = kzalloc(sizeof(*state), GFP_KERNEL); @@ -276,8 +276,8 @@ mppe_init(void *arg, unsigned char *options, int optlen, int unit, int debug, struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg; unsigned char mppe_opts; - if (optlen != CILEN_MPPE - || options[0] != CI_MPPE || options[1] != CILEN_MPPE) + if (optlen != CILEN_MPPE || + options[0] != CI_MPPE || options[1] != CILEN_MPPE) return 0; MPPE_CI_TO_OPTS(&options[2], mppe_opts); diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index d2fa2db13586..3a13cecae3e2 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -378,10 +378,7 @@ ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait) return 0; } -/* - * This can now be called from hard interrupt level as well - * as soft interrupt level or mainline. - */ +/* May sleep, don't call from interrupt level or with interrupts disabled */ static void ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count) @@ -665,8 +662,8 @@ ppp_sync_push(struct syncppp *ap) } /* haven't made any progress */ spin_unlock_bh(&ap->xmit_lock); - if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) - || (!tty_stuffed && ap->tpkt))) + if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) || + (!tty_stuffed && ap->tpkt))) break; if (!spin_trylock_bh(&ap->xmit_lock)) break; diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 2559991eea6a..cdd11ba100ea 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -97,7 +97,7 @@ static const struct proto_ops pppoe_ops; static struct ppp_channel_ops pppoe_chan_ops; /* per-net private data for this module */ -static int pppoe_net_id; +static int pppoe_net_id __read_mostly; struct pppoe_net { /* * we could use _single_ hash table for all @@ -250,20 +250,19 @@ static inline struct pppox_sock *get_item_by_addr(struct net *net, { struct net_device *dev; struct pppoe_net *pn; - struct pppox_sock *pppox_sock; + struct pppox_sock *pppox_sock = NULL; int ifindex; - dev = dev_get_by_name(net, sp->sa_addr.pppoe.dev); - if (!dev) - return NULL; - - ifindex = dev->ifindex; - pn = net_generic(net, pppoe_net_id); - pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid, + rcu_read_lock(); + dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev); + if (dev) { + ifindex = dev->ifindex; + pn = net_generic(net, pppoe_net_id); + pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex); - dev_put(dev); - + } + rcu_read_unlock(); return pppox_sock; } @@ -324,8 +323,8 @@ static void pppoe_flush_dev(struct net_device *dev) write_unlock_bh(&pn->hash_lock); lock_sock(sk); - if (po->pppoe_dev == dev - && sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + if (po->pppoe_dev == dev && + sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { pppox_unbind_sock(sk); sk->sk_state = PPPOX_ZOMBIE; sk->sk_state_change(sk); @@ -1140,59 +1139,37 @@ static struct pppox_proto pppoe_proto = { static __net_init int pppoe_init_net(struct net *net) { - struct pppoe_net *pn; + struct pppoe_net *pn = pppoe_pernet(net); struct proc_dir_entry *pde; - int err; - - pn = kzalloc(sizeof(*pn), GFP_KERNEL); - if (!pn) - return -ENOMEM; rwlock_init(&pn->hash_lock); - err = net_assign_generic(net, pppoe_net_id, pn); - if (err) - goto out; - pde = proc_net_fops_create(net, "pppoe", S_IRUGO, &pppoe_seq_fops); #ifdef CONFIG_PROC_FS - if (!pde) { - err = -ENOMEM; - goto out; - } + if (!pde) + return -ENOMEM; #endif return 0; - -out: - kfree(pn); - return err; } static __net_exit void pppoe_exit_net(struct net *net) { - struct pppoe_net *pn; - proc_net_remove(net, "pppoe"); - pn = net_generic(net, pppoe_net_id); - /* - * if someone has cached our net then - * further net_generic call will return NULL - */ - net_assign_generic(net, pppoe_net_id, NULL); - kfree(pn); } static struct pernet_operations pppoe_net_ops = { .init = pppoe_init_net, .exit = pppoe_exit_net, + .id = &pppoe_net_id, + .size = sizeof(struct pppoe_net), }; static int __init pppoe_init(void) { int err; - err = register_pernet_gen_device(&pppoe_net_id, &pppoe_net_ops); + err = register_pernet_device(&pppoe_net_ops); if (err) goto out; @@ -1213,7 +1190,7 @@ static int __init pppoe_init(void) out_unregister_pppoe_proto: proto_unregister(&pppoe_sk_proto); out_unregister_net_ops: - unregister_pernet_gen_device(pppoe_net_id, &pppoe_net_ops); + unregister_pernet_device(&pppoe_net_ops); out: return err; } @@ -1225,7 +1202,7 @@ static void __exit pppoe_exit(void) dev_remove_pack(&pppoes_ptype); unregister_pppox_proto(PX_PROTO_OE); proto_unregister(&pppoe_sk_proto); - unregister_pernet_gen_device(pppoe_net_id, &pppoe_net_ops); + unregister_pernet_device(&pppoe_net_ops); } module_init(pppoe_init); diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 5910df60c93e..9fbb2eba9a06 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -232,7 +232,7 @@ static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL }; static const struct proto_ops pppol2tp_ops; /* per-net private data for this module */ -static int pppol2tp_net_id; +static int pppol2tp_net_id __read_mostly; struct pppol2tp_net { struct list_head pppol2tp_tunnel_list; rwlock_t pppol2tp_tunnel_list_lock; @@ -516,7 +516,7 @@ static inline int pppol2tp_verify_udp_checksum(struct sock *sk, return 0; inet = inet_sk(sk); - psum = csum_tcpudp_nofold(inet->saddr, inet->daddr, ulen, + psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, ulen, IPPROTO_UDP, 0); if ((skb->ip_summed == CHECKSUM_COMPLETE) && @@ -949,8 +949,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh inet = inet_sk(sk_tun); udp_len = hdr_len + sizeof(ppph) + total_len; uh = (struct udphdr *) skb->data; - uh->source = inet->sport; - uh->dest = inet->dport; + uh->source = inet->inet_sport; + uh->dest = inet->inet_dport; uh->len = htons(udp_len); uh->check = 0; skb_put(skb, sizeof(struct udphdr)); @@ -978,7 +978,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) { skb->ip_summed = CHECKSUM_COMPLETE; csum = skb_checksum(skb, 0, udp_len, 0); - uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr, + uh->check = csum_tcpudp_magic(inet->inet_saddr, + inet->inet_daddr, udp_len, IPPROTO_UDP, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; @@ -986,7 +987,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); - uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr, + uh->check = ~csum_tcpudp_magic(inet->inet_saddr, + inet->inet_daddr, udp_len, IPPROTO_UDP, 0); } @@ -1136,8 +1138,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) __skb_push(skb, sizeof(*uh)); skb_reset_transport_header(skb); uh = udp_hdr(skb); - uh->source = inet->sport; - uh->dest = inet->dport; + uh->source = inet->inet_sport; + uh->dest = inet->inet_dport; uh->len = htons(udp_len); uh->check = 0; @@ -1181,7 +1183,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) { skb->ip_summed = CHECKSUM_COMPLETE; csum = skb_checksum(skb, 0, udp_len, 0); - uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr, + uh->check = csum_tcpudp_magic(inet->inet_saddr, + inet->inet_daddr, udp_len, IPPROTO_UDP, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; @@ -1189,7 +1192,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); - uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr, + uh->check = ~csum_tcpudp_magic(inet->inet_saddr, + inet->inet_daddr, udp_len, IPPROTO_UDP, 0); } @@ -1533,7 +1537,7 @@ static struct sock *pppol2tp_prepare_tunnel_socket(struct net *net, * if the tunnel socket goes away. */ tunnel->old_sk_destruct = sk->sk_destruct; - sk->sk_destruct = &pppol2tp_tunnel_destruct; + sk->sk_destruct = pppol2tp_tunnel_destruct; tunnel->sock = sk; sk->sk_allocation = GFP_ATOMIC; @@ -2601,53 +2605,31 @@ static struct pppox_proto pppol2tp_proto = { static __net_init int pppol2tp_init_net(struct net *net) { - struct pppol2tp_net *pn; + struct pppol2tp_net *pn = pppol2tp_pernet(net); struct proc_dir_entry *pde; - int err; - - pn = kzalloc(sizeof(*pn), GFP_KERNEL); - if (!pn) - return -ENOMEM; INIT_LIST_HEAD(&pn->pppol2tp_tunnel_list); rwlock_init(&pn->pppol2tp_tunnel_list_lock); - err = net_assign_generic(net, pppol2tp_net_id, pn); - if (err) - goto out; - pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops); #ifdef CONFIG_PROC_FS - if (!pde) { - err = -ENOMEM; - goto out; - } + if (!pde) + return -ENOMEM; #endif return 0; - -out: - kfree(pn); - return err; } static __net_exit void pppol2tp_exit_net(struct net *net) { - struct pppoe_net *pn; - proc_net_remove(net, "pppol2tp"); - pn = net_generic(net, pppol2tp_net_id); - /* - * if someone has cached our net then - * further net_generic call will return NULL - */ - net_assign_generic(net, pppol2tp_net_id, NULL); - kfree(pn); } static struct pernet_operations pppol2tp_net_ops = { .init = pppol2tp_init_net, .exit = pppol2tp_exit_net, + .id = &pppol2tp_net_id, + .size = sizeof(struct pppol2tp_net), }; static int __init pppol2tp_init(void) @@ -2661,7 +2643,7 @@ static int __init pppol2tp_init(void) if (err) goto out_unregister_pppol2tp_proto; - err = register_pernet_gen_device(&pppol2tp_net_id, &pppol2tp_net_ops); + err = register_pernet_device(&pppol2tp_net_ops); if (err) goto out_unregister_pppox_proto; @@ -2680,7 +2662,7 @@ out_unregister_pppol2tp_proto: static void __exit pppol2tp_exit(void) { unregister_pppox_proto(PX_PROTO_OL2TP); - unregister_pernet_gen_device(pppol2tp_net_id, &pppol2tp_net_ops); + unregister_pernet_device(&pppol2tp_net_ops); proto_unregister(&pppol2tp_sk_proto); } diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c index 4f6d33fbc673..ac806b27c658 100644 --- a/drivers/net/pppox.c +++ b/drivers/net/pppox.c @@ -104,7 +104,8 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) EXPORT_SYMBOL(pppox_ioctl); -static int pppox_create(struct net *net, struct socket *sock, int protocol) +static int pppox_create(struct net *net, struct socket *sock, int protocol, + int kern) { int rc = -EPROTOTYPE; @@ -125,7 +126,7 @@ out: return rc; } -static struct net_proto_family pppox_proto_family = { +static const struct net_proto_family pppox_proto_family = { .family = PF_PPPOX, .create = pppox_create, .owner = THIS_MODULE, diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index b211613e9dbd..0c768593aad0 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c @@ -95,11 +95,11 @@ static void gelic_card_get_ether_port_status(struct gelic_card *card, lv1_net_control(bus_id(card), dev_id(card), GELIC_LV1_GET_ETH_PORT_STATUS, - GELIC_LV1_VLAN_TX_ETHERNET, 0, 0, + GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0, &card->ether_port_status, &v2); if (inform) { - ether_netdev = card->netdev[GELIC_PORT_ETHERNET]; + ether_netdev = card->netdev[GELIC_PORT_ETHERNET_0]; if (card->ether_port_status & GELIC_LV1_ETHER_LINK_UP) netif_carrier_on(ether_netdev); else @@ -107,6 +107,24 @@ static void gelic_card_get_ether_port_status(struct gelic_card *card, } } +static int gelic_card_set_link_mode(struct gelic_card *card, int mode) +{ + int status; + u64 v1, v2; + + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_SET_NEGOTIATION_MODE, + GELIC_LV1_PHY_ETHERNET_0, mode, 0, &v1, &v2); + if (status) { + pr_info("%s: failed setting negotiation mode %d\n", __func__, + status); + return -EBUSY; + } + + card->link_mode = mode; + return 0; +} + void gelic_card_up(struct gelic_card *card) { pr_debug("%s: called\n", __func__); @@ -296,7 +314,7 @@ static void gelic_card_reset_chain(struct gelic_card *card, * @card: card structure * @descr: descriptor to re-init * - * return 0 on succes, <0 on failure + * return 0 on success, <0 on failure * * allocates a new rx skb, iommu-maps it and attaches it to the descriptor. * Activate the descriptor state-wise @@ -451,14 +469,14 @@ static void gelic_descr_release_tx(struct gelic_card *card, static void gelic_card_stop_queues(struct gelic_card *card) { - netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET]); + netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET_0]); if (card->netdev[GELIC_PORT_WIRELESS]) netif_stop_queue(card->netdev[GELIC_PORT_WIRELESS]); } static void gelic_card_wake_queues(struct gelic_card *card) { - netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET]); + netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET_0]); if (card->netdev[GELIC_PORT_WIRELESS]) netif_wake_queue(card->netdev[GELIC_PORT_WIRELESS]); @@ -999,7 +1017,7 @@ static int gelic_card_decode_one_descr(struct gelic_card *card) goto refill; } } else - netdev = card->netdev[GELIC_PORT_ETHERNET]; + netdev = card->netdev[GELIC_PORT_ETHERNET_0]; if ((status == GELIC_DESCR_DMA_RESPONSE_ERROR) || (status == GELIC_DESCR_DMA_PROTECTION_ERROR) || @@ -1244,14 +1262,58 @@ static int gelic_ether_get_settings(struct net_device *netdev, cmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; + SUPPORTED_1000baseT_Full; cmd->advertising = cmd->supported; - cmd->autoneg = AUTONEG_ENABLE; /* always enabled */ + if (card->link_mode & GELIC_LV1_ETHER_AUTO_NEG) { + cmd->autoneg = AUTONEG_ENABLE; + } else { + cmd->autoneg = AUTONEG_DISABLE; + cmd->advertising &= ~ADVERTISED_Autoneg; + } cmd->port = PORT_TP; return 0; } +static int gelic_ether_set_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + struct gelic_card *card = netdev_card(netdev); + u64 mode; + int ret; + + if (cmd->autoneg == AUTONEG_ENABLE) { + mode = GELIC_LV1_ETHER_AUTO_NEG; + } else { + switch (cmd->speed) { + case SPEED_10: + mode = GELIC_LV1_ETHER_SPEED_10; + break; + case SPEED_100: + mode = GELIC_LV1_ETHER_SPEED_100; + break; + case SPEED_1000: + mode = GELIC_LV1_ETHER_SPEED_1000; + break; + default: + return -EINVAL; + } + if (cmd->duplex == DUPLEX_FULL) + mode |= GELIC_LV1_ETHER_FULL_DUPLEX; + else if (cmd->speed == SPEED_1000) { + pr_info("1000 half duplex is not supported.\n"); + return -EINVAL; + } + } + + ret = gelic_card_set_link_mode(card, mode); + + if (ret) + return ret; + + return 0; +} + u32 gelic_net_get_rx_csum(struct net_device *netdev) { struct gelic_card *card = netdev_card(netdev); @@ -1349,6 +1411,7 @@ done: static const struct ethtool_ops gelic_ether_ethtool_ops = { .get_drvinfo = gelic_net_get_drvinfo, .get_settings = gelic_ether_get_settings, + .set_settings = gelic_ether_set_settings, .get_link = ethtool_op_get_link, .get_tx_csum = ethtool_op_get_tx_csum, .set_tx_csum = ethtool_op_set_tx_csum, @@ -1369,7 +1432,7 @@ static void gelic_net_tx_timeout_task(struct work_struct *work) { struct gelic_card *card = container_of(work, struct gelic_card, tx_timeout_task); - struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET]; + struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET_0]; dev_info(ctodev(card), "%s:Timed out. Restarting... \n", __func__); @@ -1531,10 +1594,10 @@ static struct gelic_card * __devinit gelic_alloc_card_net(struct net_device **ne /* gelic_port */ port->netdev = *netdev; port->card = card; - port->type = GELIC_PORT_ETHERNET; + port->type = GELIC_PORT_ETHERNET_0; /* gelic_card */ - card->netdev[GELIC_PORT_ETHERNET] = *netdev; + card->netdev[GELIC_PORT_ETHERNET_0] = *netdev; INIT_WORK(&card->tx_timeout_task, gelic_net_tx_timeout_task); init_waitqueue_head(&card->waitq); @@ -1554,9 +1617,9 @@ static void __devinit gelic_card_get_vlan_info(struct gelic_card *card) int tx; int rx; } vlan_id_ix[2] = { - [GELIC_PORT_ETHERNET] = { - .tx = GELIC_LV1_VLAN_TX_ETHERNET, - .rx = GELIC_LV1_VLAN_RX_ETHERNET + [GELIC_PORT_ETHERNET_0] = { + .tx = GELIC_LV1_VLAN_TX_ETHERNET_0, + .rx = GELIC_LV1_VLAN_RX_ETHERNET_0 }, [GELIC_PORT_WIRELESS] = { .tx = GELIC_LV1_VLAN_TX_WIRELESS, @@ -1601,7 +1664,7 @@ static void __devinit gelic_card_get_vlan_info(struct gelic_card *card) i, card->vlan[i].tx, card->vlan[i].rx); } - if (card->vlan[GELIC_PORT_ETHERNET].tx) { + if (card->vlan[GELIC_PORT_ETHERNET_0].tx) { BUG_ON(!card->vlan[GELIC_PORT_WIRELESS].tx); card->vlan_required = 1; } else @@ -1657,6 +1720,8 @@ static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev) /* get internal vlan info */ gelic_card_get_vlan_info(card); + card->link_mode = GELIC_LV1_ETHER_AUTO_NEG; + /* setup interrupt */ result = lv1_net_set_interrupt_status_indicator(bus_id(card), dev_id(card), @@ -1773,6 +1838,9 @@ static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev) struct net_device *netdev0; pr_debug("%s: called\n", __func__); + /* set auto-negotiation */ + gelic_card_set_link_mode(card, GELIC_LV1_ETHER_AUTO_NEG); + #ifdef CONFIG_GELIC_WIRELESS gelic_wl_driver_remove(card); #endif @@ -1790,7 +1858,7 @@ static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev) gelic_card_free_chain(card, card->tx_top); gelic_card_free_chain(card, card->rx_top); - netdev0 = card->netdev[GELIC_PORT_ETHERNET]; + netdev0 = card->netdev[GELIC_PORT_ETHERNET_0]; /* disconnect event port */ free_irq(card->irq, card); netdev0->irq = NO_IRQ; diff --git a/drivers/net/ps3_gelic_net.h b/drivers/net/ps3_gelic_net.h index 8b413868bbe2..32521ae5e824 100644 --- a/drivers/net/ps3_gelic_net.h +++ b/drivers/net/ps3_gelic_net.h @@ -186,7 +186,7 @@ enum gelic_lv1_net_control_code { GELIC_LV1_GET_CHANNEL = 6, GELIC_LV1_POST_WLAN_CMD = 9, GELIC_LV1_GET_WLAN_CMD_RESULT = 10, - GELIC_LV1_GET_WLAN_EVENT = 11 + GELIC_LV1_GET_WLAN_EVENT = 11, }; /* for GELIC_LV1_SET_WOL */ @@ -217,24 +217,29 @@ enum gelic_lv1_ether_port_status { GELIC_LV1_ETHER_SPEED_10 = 0x0000000000000010L, GELIC_LV1_ETHER_SPEED_100 = 0x0000000000000020L, GELIC_LV1_ETHER_SPEED_1000 = 0x0000000000000040L, - GELIC_LV1_ETHER_SPEED_MASK = 0x0000000000000070L + GELIC_LV1_ETHER_SPEED_MASK = 0x0000000000000070L, }; enum gelic_lv1_vlan_index { /* for outgoing packets */ - GELIC_LV1_VLAN_TX_ETHERNET = 0x0000000000000002L, + GELIC_LV1_VLAN_TX_ETHERNET_0 = 0x0000000000000002L, GELIC_LV1_VLAN_TX_WIRELESS = 0x0000000000000003L, + /* for incoming packets */ - GELIC_LV1_VLAN_RX_ETHERNET = 0x0000000000000012L, - GELIC_LV1_VLAN_RX_WIRELESS = 0x0000000000000013L + GELIC_LV1_VLAN_RX_ETHERNET_0 = 0x0000000000000012L, + GELIC_LV1_VLAN_RX_WIRELESS = 0x0000000000000013L, +}; + +enum gelic_lv1_phy { + GELIC_LV1_PHY_ETHERNET_0 = 0x0000000000000002L, }; /* size of hardware part of gelic descriptor */ #define GELIC_DESCR_SIZE (32) enum gelic_port_type { - GELIC_PORT_ETHERNET = 0, - GELIC_PORT_WIRELESS = 1, + GELIC_PORT_ETHERNET_0 = 0, + GELIC_PORT_WIRELESS = 1, GELIC_PORT_MAX }; @@ -302,6 +307,8 @@ struct gelic_card { atomic_t users; u64 ether_port_status; + int link_mode; + /* original address returned by kzalloc */ void *unalign; diff --git a/drivers/net/ps3_gelic_wireless.h b/drivers/net/ps3_gelic_wireless.h index 5b631c6c9775..0a88b535197a 100644 --- a/drivers/net/ps3_gelic_wireless.h +++ b/drivers/net/ps3_gelic_wireless.h @@ -199,7 +199,7 @@ struct gelic_eurus_rssi_info { /* for 'stat' member of gelic_wl_info */ enum gelic_wl_info_status_bit { GELIC_WL_STAT_CONFIGURED, - GELIC_WL_STAT_CH_INFO, /* ch info aquired */ + GELIC_WL_STAT_CH_INFO, /* ch info acquired */ GELIC_WL_STAT_ESSID_SET, /* ESSID specified by userspace */ GELIC_WL_STAT_BSSID_SET, /* BSSID specified by userspace */ GELIC_WL_STAT_WPA_PSK_SET, /* PMK specified by userspace */ diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index 4c610511eb40..dd35066a7f8d 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -1969,8 +1969,8 @@ static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev) struct ql_rcv_buf_cb *lrg_buf_cb; struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; - if ((qdev->lrg_buf_free_count >= 8) - && (qdev->lrg_buf_release_cnt >= 16)) { + if ((qdev->lrg_buf_free_count >= 8) && + (qdev->lrg_buf_release_cnt >= 16)) { if (qdev->lrg_buf_skb_check) if (!ql_populate_free_queue(qdev)) @@ -1978,8 +1978,8 @@ static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev) lrg_buf_q_ele = qdev->lrg_buf_next_free; - while ((qdev->lrg_buf_release_cnt >= 16) - && (qdev->lrg_buf_free_count >= 8)) { + while ((qdev->lrg_buf_release_cnt >= 16) && + (qdev->lrg_buf_free_count >= 8)) { for (i = 0; i < 8; i++) { lrg_buf_cb = @@ -3651,7 +3651,7 @@ static int ql_adapter_up(struct ql3_adapter *qdev) ql_sem_unlock(qdev, QL_DRVR_SEM_MASK); } else { printk(KERN_ERR PFX - "%s: Could not aquire driver lock.\n", + "%s: Could not acquire driver lock.\n", ndev->name); goto err_lock; } diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h index c2383adcd527..862c1aaf3860 100644 --- a/drivers/net/qlge/qlge.h +++ b/drivers/net/qlge/qlge.h @@ -16,7 +16,7 @@ */ #define DRV_NAME "qlge" #define DRV_STRING "QLogic 10 Gigabit PCI-E Ethernet Driver " -#define DRV_VERSION "v1.00.00-b3" +#define DRV_VERSION "v1.00.00.23.00.00-01" #define PFX "qlge: " #define QPRINTK(qdev, nlevel, klevel, fmt, args...) \ @@ -54,8 +54,10 @@ #define RX_RING_SHADOW_SPACE (sizeof(u64) + \ MAX_DB_PAGES_PER_BQ(NUM_SMALL_BUFFERS) * sizeof(u64) + \ MAX_DB_PAGES_PER_BQ(NUM_LARGE_BUFFERS) * sizeof(u64)) -#define SMALL_BUFFER_SIZE 256 -#define LARGE_BUFFER_SIZE PAGE_SIZE +#define SMALL_BUFFER_SIZE 512 +#define SMALL_BUF_MAP_SIZE (SMALL_BUFFER_SIZE / 2) +#define LARGE_BUFFER_MAX_SIZE 8192 +#define LARGE_BUFFER_MIN_SIZE 2048 #define MAX_SPLIT_SIZE 1023 #define QLGE_SB_PAD 32 @@ -795,6 +797,7 @@ enum { MB_WOL_BCAST = (1 << 5), MB_WOL_LINK_UP = (1 << 6), MB_WOL_LINK_DOWN = (1 << 7), + MB_WOL_MODE_ON = (1 << 16), /* Wake on Lan Mode on */ MB_CMD_SET_WOL_FLTR = 0x00000111, /* Wake On Lan Filter */ MB_CMD_CLEAR_WOL_FLTR = 0x00000112, /* Wake On Lan Filter */ MB_CMD_SET_WOL_MAGIC = 0x00000113, /* Wake On Lan Magic Packet */ @@ -804,12 +807,27 @@ enum { MB_CMD_SET_PORT_CFG = 0x00000122, MB_CMD_GET_PORT_CFG = 0x00000123, MB_CMD_GET_LINK_STS = 0x00000124, + MB_CMD_SET_LED_CFG = 0x00000125, /* Set LED Configuration Register */ + QL_LED_BLINK = 0x03e803e8, + MB_CMD_GET_LED_CFG = 0x00000126, /* Get LED Configuration Register */ MB_CMD_SET_MGMNT_TFK_CTL = 0x00000160, /* Set Mgmnt Traffic Control */ MB_SET_MPI_TFK_STOP = (1 << 0), MB_SET_MPI_TFK_RESUME = (1 << 1), MB_CMD_GET_MGMNT_TFK_CTL = 0x00000161, /* Get Mgmnt Traffic Control */ MB_GET_MPI_TFK_STOPPED = (1 << 0), MB_GET_MPI_TFK_FIFO_EMPTY = (1 << 1), + /* Sub-commands for IDC request. + * This describes the reason for the + * IDC request. + */ + MB_CMD_IOP_NONE = 0x0000, + MB_CMD_IOP_PREP_UPDATE_MPI = 0x0001, + MB_CMD_IOP_COMP_UPDATE_MPI = 0x0002, + MB_CMD_IOP_PREP_LINK_DOWN = 0x0010, + MB_CMD_IOP_DVR_START = 0x0100, + MB_CMD_IOP_FLASH_ACC = 0x0101, + MB_CMD_IOP_RESTART_MPI = 0x0102, + MB_CMD_IOP_CORE_DUMP_MPI = 0x0103, /* Mailbox Command Status. */ MB_CMD_STS_GOOD = 0x00004000, /* Success. */ @@ -1201,9 +1219,17 @@ struct tx_ring_desc { struct tx_ring_desc *next; }; +struct page_chunk { + struct page *page; /* master page */ + char *va; /* virt addr for this chunk */ + u64 map; /* mapping for master */ + unsigned int offset; /* offset for this chunk */ + unsigned int last_flag; /* flag set for last chunk in page */ +}; + struct bq_desc { union { - struct page *lbq_page; + struct page_chunk pg_chunk; struct sk_buff *skb; } p; __le64 *addr; @@ -1237,6 +1263,9 @@ struct tx_ring { atomic_t queue_stopped; /* Turns queue off when full. */ struct delayed_work tx_work; struct ql_adapter *qdev; + u64 tx_packets; + u64 tx_bytes; + u64 tx_errors; }; /* @@ -1272,6 +1301,7 @@ struct rx_ring { dma_addr_t lbq_base_dma; void *lbq_base_indirect; dma_addr_t lbq_base_indirect_dma; + struct page_chunk pg_chunk; /* current page for chunks */ struct bq_desc *lbq; /* array of control blocks */ void __iomem *lbq_prod_idx_db_reg; /* PCI doorbell mem area + 0x18 */ u32 lbq_prod_idx; /* current sw prod idx */ @@ -1302,6 +1332,11 @@ struct rx_ring { struct napi_struct napi; u8 reserved; struct ql_adapter *qdev; + u64 rx_packets; + u64 rx_multicast; + u64 rx_bytes; + u64 rx_dropped; + u64 rx_errors; }; /* @@ -1363,6 +1398,174 @@ struct nic_stats { u64 rx_1024_to_1518_pkts; u64 rx_1519_to_max_pkts; u64 rx_len_err_pkts; + /* + * These stats come from offset 500h to 5C8h + * in the XGMAC register. + */ + u64 tx_cbfc_pause_frames0; + u64 tx_cbfc_pause_frames1; + u64 tx_cbfc_pause_frames2; + u64 tx_cbfc_pause_frames3; + u64 tx_cbfc_pause_frames4; + u64 tx_cbfc_pause_frames5; + u64 tx_cbfc_pause_frames6; + u64 tx_cbfc_pause_frames7; + u64 rx_cbfc_pause_frames0; + u64 rx_cbfc_pause_frames1; + u64 rx_cbfc_pause_frames2; + u64 rx_cbfc_pause_frames3; + u64 rx_cbfc_pause_frames4; + u64 rx_cbfc_pause_frames5; + u64 rx_cbfc_pause_frames6; + u64 rx_cbfc_pause_frames7; + u64 rx_nic_fifo_drop; +}; + +/* Address/Length pairs for the coredump. */ +enum { + MPI_CORE_REGS_ADDR = 0x00030000, + MPI_CORE_REGS_CNT = 127, + MPI_CORE_SH_REGS_CNT = 16, + TEST_REGS_ADDR = 0x00001000, + TEST_REGS_CNT = 23, + RMII_REGS_ADDR = 0x00001040, + RMII_REGS_CNT = 64, + FCMAC1_REGS_ADDR = 0x00001080, + FCMAC2_REGS_ADDR = 0x000010c0, + FCMAC_REGS_CNT = 64, + FC1_MBX_REGS_ADDR = 0x00001100, + FC2_MBX_REGS_ADDR = 0x00001240, + FC_MBX_REGS_CNT = 64, + IDE_REGS_ADDR = 0x00001140, + IDE_REGS_CNT = 64, + NIC1_MBX_REGS_ADDR = 0x00001180, + NIC2_MBX_REGS_ADDR = 0x00001280, + NIC_MBX_REGS_CNT = 64, + SMBUS_REGS_ADDR = 0x00001200, + SMBUS_REGS_CNT = 64, + I2C_REGS_ADDR = 0x00001fc0, + I2C_REGS_CNT = 64, + MEMC_REGS_ADDR = 0x00003000, + MEMC_REGS_CNT = 256, + PBUS_REGS_ADDR = 0x00007c00, + PBUS_REGS_CNT = 256, + MDE_REGS_ADDR = 0x00010000, + MDE_REGS_CNT = 6, + CODE_RAM_ADDR = 0x00020000, + CODE_RAM_CNT = 0x2000, + MEMC_RAM_ADDR = 0x00100000, + MEMC_RAM_CNT = 0x2000, +}; + +#define MPI_COREDUMP_COOKIE 0x5555aaaa +struct mpi_coredump_global_header { + u32 cookie; + u8 idString[16]; + u32 timeLo; + u32 timeHi; + u32 imageSize; + u32 headerSize; + u8 info[220]; +}; + +struct mpi_coredump_segment_header { + u32 cookie; + u32 segNum; + u32 segSize; + u32 extra; + u8 description[16]; +}; + +/* Reg dump segment numbers. */ +enum { + CORE_SEG_NUM = 1, + TEST_LOGIC_SEG_NUM = 2, + RMII_SEG_NUM = 3, + FCMAC1_SEG_NUM = 4, + FCMAC2_SEG_NUM = 5, + FC1_MBOX_SEG_NUM = 6, + IDE_SEG_NUM = 7, + NIC1_MBOX_SEG_NUM = 8, + SMBUS_SEG_NUM = 9, + FC2_MBOX_SEG_NUM = 10, + NIC2_MBOX_SEG_NUM = 11, + I2C_SEG_NUM = 12, + MEMC_SEG_NUM = 13, + PBUS_SEG_NUM = 14, + MDE_SEG_NUM = 15, + NIC1_CONTROL_SEG_NUM = 16, + NIC2_CONTROL_SEG_NUM = 17, + NIC1_XGMAC_SEG_NUM = 18, + NIC2_XGMAC_SEG_NUM = 19, + WCS_RAM_SEG_NUM = 20, + MEMC_RAM_SEG_NUM = 21, + XAUI_AN_SEG_NUM = 22, + XAUI_HSS_PCS_SEG_NUM = 23, + XFI_AN_SEG_NUM = 24, + XFI_TRAIN_SEG_NUM = 25, + XFI_HSS_PCS_SEG_NUM = 26, + XFI_HSS_TX_SEG_NUM = 27, + XFI_HSS_RX_SEG_NUM = 28, + XFI_HSS_PLL_SEG_NUM = 29, + MISC_NIC_INFO_SEG_NUM = 30, + INTR_STATES_SEG_NUM = 31, + CAM_ENTRIES_SEG_NUM = 32, + ROUTING_WORDS_SEG_NUM = 33, + ETS_SEG_NUM = 34, + PROBE_DUMP_SEG_NUM = 35, + ROUTING_INDEX_SEG_NUM = 36, + MAC_PROTOCOL_SEG_NUM = 37, + XAUI2_AN_SEG_NUM = 38, + XAUI2_HSS_PCS_SEG_NUM = 39, + XFI2_AN_SEG_NUM = 40, + XFI2_TRAIN_SEG_NUM = 41, + XFI2_HSS_PCS_SEG_NUM = 42, + XFI2_HSS_TX_SEG_NUM = 43, + XFI2_HSS_RX_SEG_NUM = 44, + XFI2_HSS_PLL_SEG_NUM = 45, + SEM_REGS_SEG_NUM = 50 + +}; + +struct ql_nic_misc { + u32 rx_ring_count; + u32 tx_ring_count; + u32 intr_count; + u32 function; +}; + +struct ql_reg_dump { + + /* segment 0 */ + struct mpi_coredump_global_header mpi_global_header; + + /* segment 16 */ + struct mpi_coredump_segment_header nic_regs_seg_hdr; + u32 nic_regs[64]; + + /* segment 30 */ + struct mpi_coredump_segment_header misc_nic_seg_hdr; + struct ql_nic_misc misc_nic_info; + + /* segment 31 */ + /* one interrupt state for each CQ */ + struct mpi_coredump_segment_header intr_states_seg_hdr; + u32 intr_states[MAX_CPUS]; + + /* segment 32 */ + /* 3 cam words each for 16 unicast, + * 2 cam words for each of 32 multicast. + */ + struct mpi_coredump_segment_header cam_entries_seg_hdr; + u32 cam_entries[(16 * 3) + (32 * 3)]; + + /* segment 33 */ + struct mpi_coredump_segment_header nic_routing_words_seg_hdr; + u32 nic_routing_words[16]; + + /* segment 34 */ + struct mpi_coredump_segment_header ets_seg_hdr; + u32 ets[8+2]; }; /* @@ -1398,6 +1601,8 @@ enum { QL_ALLMULTI = 6, QL_PORT_CFG = 7, QL_CAM_RT_SET = 8, + QL_SELFTEST = 9, + QL_LB_LINK_UP = 10, }; /* link_status bit definitions */ @@ -1505,6 +1710,7 @@ struct ql_adapter { struct rx_ring rx_ring[MAX_RX_RINGS]; struct tx_ring tx_ring[MAX_TX_RINGS]; + unsigned int lbq_buf_order; int rx_csum; u32 default_rx_queue; @@ -1519,11 +1725,11 @@ struct ql_adapter { u32 port_init; u32 link_status; u32 link_config; + u32 led_config; u32 max_frame_size; union flash_params flash; - struct net_device_stats stats; struct workqueue_struct *workqueue; struct delayed_work asic_reset_work; struct delayed_work mpi_reset_work; @@ -1533,6 +1739,7 @@ struct ql_adapter { struct completion ide_completion; struct nic_operations *nic_ops; u16 device_id; + atomic_t lb_count; }; /* @@ -1611,10 +1818,22 @@ int ql_mb_get_fw_state(struct ql_adapter *qdev); int ql_cam_route_initialize(struct ql_adapter *qdev); int ql_read_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 *data); int ql_mb_about_fw(struct ql_adapter *qdev); +int ql_wol(struct ql_adapter *qdev); +int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol); +int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol); +int ql_mb_set_led_cfg(struct ql_adapter *qdev, u32 led_config); +int ql_mb_get_led_cfg(struct ql_adapter *qdev); void ql_link_on(struct ql_adapter *qdev); void ql_link_off(struct ql_adapter *qdev); int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 control); +int ql_mb_get_port_cfg(struct ql_adapter *qdev); +int ql_mb_set_port_cfg(struct ql_adapter *qdev); int ql_wait_fifo_empty(struct ql_adapter *qdev); +void ql_gen_reg_dump(struct ql_adapter *qdev, + struct ql_reg_dump *mpi_coredump); +netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev); +void ql_check_lb_frame(struct ql_adapter *, struct sk_buff *); +int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget); #if 1 #define QL_ALL_DUMP diff --git a/drivers/net/qlge/qlge_dbg.c b/drivers/net/qlge/qlge_dbg.c index aa88cb3f41c7..9f58c4710761 100644 --- a/drivers/net/qlge/qlge_dbg.c +++ b/drivers/net/qlge/qlge_dbg.c @@ -1,5 +1,185 @@ #include "qlge.h" + +static int ql_get_ets_regs(struct ql_adapter *qdev, u32 * buf) +{ + int status = 0; + int i; + + for (i = 0; i < 8; i++, buf++) { + ql_write32(qdev, NIC_ETS, i << 29 | 0x08000000); + *buf = ql_read32(qdev, NIC_ETS); + } + + for (i = 0; i < 2; i++, buf++) { + ql_write32(qdev, CNA_ETS, i << 29 | 0x08000000); + *buf = ql_read32(qdev, CNA_ETS); + } + + return status; +} + +static void ql_get_intr_states(struct ql_adapter *qdev, u32 * buf) +{ + int i; + + for (i = 0; i < qdev->rx_ring_count; i++, buf++) { + ql_write32(qdev, INTR_EN, + qdev->intr_context[i].intr_read_mask); + *buf = ql_read32(qdev, INTR_EN); + } +} + +static int ql_get_cam_entries(struct ql_adapter *qdev, u32 * buf) +{ + int i, status; + u32 value[3]; + + status = ql_sem_spinlock(qdev, SEM_MAC_ADDR_MASK); + if (status) + return status; + + for (i = 0; i < 16; i++) { + status = ql_get_mac_addr_reg(qdev, + MAC_ADDR_TYPE_CAM_MAC, i, value); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Failed read of mac index register.\n"); + goto err; + } + *buf++ = value[0]; /* lower MAC address */ + *buf++ = value[1]; /* upper MAC address */ + *buf++ = value[2]; /* output */ + } + for (i = 0; i < 32; i++) { + status = ql_get_mac_addr_reg(qdev, + MAC_ADDR_TYPE_MULTI_MAC, i, value); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Failed read of mac index register.\n"); + goto err; + } + *buf++ = value[0]; /* lower Mcast address */ + *buf++ = value[1]; /* upper Mcast address */ + } +err: + ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK); + return status; +} + +static int ql_get_routing_entries(struct ql_adapter *qdev, u32 * buf) +{ + int status; + u32 value, i; + + status = ql_sem_spinlock(qdev, SEM_RT_IDX_MASK); + if (status) + return status; + + for (i = 0; i < 16; i++) { + status = ql_get_routing_reg(qdev, i, &value); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Failed read of routing index register.\n"); + goto err; + } else { + *buf++ = value; + } + } +err: + ql_sem_unlock(qdev, SEM_RT_IDX_MASK); + return status; +} + +/* Create a coredump segment header */ +static void ql_build_coredump_seg_header( + struct mpi_coredump_segment_header *seg_hdr, + u32 seg_number, u32 seg_size, u8 *desc) +{ + memset(seg_hdr, 0, sizeof(struct mpi_coredump_segment_header)); + seg_hdr->cookie = MPI_COREDUMP_COOKIE; + seg_hdr->segNum = seg_number; + seg_hdr->segSize = seg_size; + memcpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1); +} + +void ql_gen_reg_dump(struct ql_adapter *qdev, + struct ql_reg_dump *mpi_coredump) +{ + int i, status; + + + memset(&(mpi_coredump->mpi_global_header), 0, + sizeof(struct mpi_coredump_global_header)); + mpi_coredump->mpi_global_header.cookie = MPI_COREDUMP_COOKIE; + mpi_coredump->mpi_global_header.headerSize = + sizeof(struct mpi_coredump_global_header); + mpi_coredump->mpi_global_header.imageSize = + sizeof(struct ql_reg_dump); + memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump", + sizeof(mpi_coredump->mpi_global_header.idString)); + + + /* segment 16 */ + ql_build_coredump_seg_header(&mpi_coredump->misc_nic_seg_hdr, + MISC_NIC_INFO_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->misc_nic_info), + "MISC NIC INFO"); + mpi_coredump->misc_nic_info.rx_ring_count = qdev->rx_ring_count; + mpi_coredump->misc_nic_info.tx_ring_count = qdev->tx_ring_count; + mpi_coredump->misc_nic_info.intr_count = qdev->intr_count; + mpi_coredump->misc_nic_info.function = qdev->func; + + /* Segment 16, Rev C. Step 18 */ + ql_build_coredump_seg_header(&mpi_coredump->nic_regs_seg_hdr, + NIC1_CONTROL_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->nic_regs), + "NIC Registers"); + /* Get generic reg dump */ + for (i = 0; i < 64; i++) + mpi_coredump->nic_regs[i] = ql_read32(qdev, i * sizeof(u32)); + + /* Segment 31 */ + /* Get indexed register values. */ + ql_build_coredump_seg_header(&mpi_coredump->intr_states_seg_hdr, + INTR_STATES_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->intr_states), + "INTR States"); + ql_get_intr_states(qdev, &mpi_coredump->intr_states[0]); + + ql_build_coredump_seg_header(&mpi_coredump->cam_entries_seg_hdr, + CAM_ENTRIES_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->cam_entries), + "CAM Entries"); + status = ql_get_cam_entries(qdev, &mpi_coredump->cam_entries[0]); + if (status) + return; + + ql_build_coredump_seg_header(&mpi_coredump->nic_routing_words_seg_hdr, + ROUTING_WORDS_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->nic_routing_words), + "Routing Words"); + status = ql_get_routing_entries(qdev, + &mpi_coredump->nic_routing_words[0]); + if (status) + return; + + /* Segment 34 (Rev C. step 23) */ + ql_build_coredump_seg_header(&mpi_coredump->ets_seg_hdr, + ETS_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->ets), + "ETS Registers"); + status = ql_get_ets_regs(qdev, &mpi_coredump->ets[0]); + if (status) + return; +} + #ifdef QL_REG_DUMP static void ql_dump_intr_states(struct ql_adapter *qdev) { diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c index 52073946bce3..058fa0a48c6f 100644 --- a/drivers/net/qlge/qlge_ethtool.c +++ b/drivers/net/qlge/qlge_ethtool.c @@ -36,6 +36,11 @@ #include "qlge.h" +static const char ql_gstrings_test[][ETH_GSTRING_LEN] = { + "Loopback test (offline)" +}; +#define QLGE_TEST_LEN (sizeof(ql_gstrings_test) / ETH_GSTRING_LEN) + static int ql_update_ring_coalescing(struct ql_adapter *qdev) { int i, status = 0; @@ -132,6 +137,41 @@ static void ql_update_stats(struct ql_adapter *qdev) iter++; } + /* + * Get Per-priority TX pause frame counter statistics. + */ + for (i = 0x500; i < 0x540; i += 8) { + if (ql_read_xgmac_reg64(qdev, i, &data)) { + QPRINTK(qdev, DRV, ERR, + "Error reading status register 0x%.04x.\n", i); + goto end; + } else + *iter = data; + iter++; + } + + /* + * Get Per-priority RX pause frame counter statistics. + */ + for (i = 0x568; i < 0x5a8; i += 8) { + if (ql_read_xgmac_reg64(qdev, i, &data)) { + QPRINTK(qdev, DRV, ERR, + "Error reading status register 0x%.04x.\n", i); + goto end; + } else + *iter = data; + iter++; + } + + /* + * Get RX NIC FIFO DROP statistics. + */ + if (ql_read_xgmac_reg64(qdev, 0x5b8, &data)) { + QPRINTK(qdev, DRV, ERR, + "Error reading status register 0x%.04x.\n", i); + goto end; + } else + *iter = data; end: ql_sem_unlock(qdev, qdev->xg_sem_mask); quit: @@ -185,6 +225,23 @@ static char ql_stats_str_arr[][ETH_GSTRING_LEN] = { {"rx_1024_to_1518_pkts"}, {"rx_1519_to_max_pkts"}, {"rx_len_err_pkts"}, + {"tx_cbfc_pause_frames0"}, + {"tx_cbfc_pause_frames1"}, + {"tx_cbfc_pause_frames2"}, + {"tx_cbfc_pause_frames3"}, + {"tx_cbfc_pause_frames4"}, + {"tx_cbfc_pause_frames5"}, + {"tx_cbfc_pause_frames6"}, + {"tx_cbfc_pause_frames7"}, + {"rx_cbfc_pause_frames0"}, + {"rx_cbfc_pause_frames1"}, + {"rx_cbfc_pause_frames2"}, + {"rx_cbfc_pause_frames3"}, + {"rx_cbfc_pause_frames4"}, + {"rx_cbfc_pause_frames5"}, + {"rx_cbfc_pause_frames6"}, + {"rx_cbfc_pause_frames7"}, + {"rx_nic_fifo_drop"}, }; static void ql_get_strings(struct net_device *dev, u32 stringset, u8 *buf) @@ -199,6 +256,8 @@ static void ql_get_strings(struct net_device *dev, u32 stringset, u8 *buf) static int ql_get_sset_count(struct net_device *dev, int sset) { switch (sset) { + case ETH_SS_TEST: + return QLGE_TEST_LEN; case ETH_SS_STATS: return ARRAY_SIZE(ql_stats_str_arr); default: @@ -257,6 +316,23 @@ ql_get_ethtool_stats(struct net_device *ndev, *data++ = s->rx_1024_to_1518_pkts; *data++ = s->rx_1519_to_max_pkts; *data++ = s->rx_len_err_pkts; + *data++ = s->tx_cbfc_pause_frames0; + *data++ = s->tx_cbfc_pause_frames1; + *data++ = s->tx_cbfc_pause_frames2; + *data++ = s->tx_cbfc_pause_frames3; + *data++ = s->tx_cbfc_pause_frames4; + *data++ = s->tx_cbfc_pause_frames5; + *data++ = s->tx_cbfc_pause_frames6; + *data++ = s->tx_cbfc_pause_frames7; + *data++ = s->rx_cbfc_pause_frames0; + *data++ = s->rx_cbfc_pause_frames1; + *data++ = s->rx_cbfc_pause_frames2; + *data++ = s->rx_cbfc_pause_frames3; + *data++ = s->rx_cbfc_pause_frames4; + *data++ = s->rx_cbfc_pause_frames5; + *data++ = s->rx_cbfc_pause_frames6; + *data++ = s->rx_cbfc_pause_frames7; + *data++ = s->rx_nic_fifo_drop; } static int ql_get_settings(struct net_device *ndev, @@ -302,6 +378,181 @@ static void ql_get_drvinfo(struct net_device *ndev, drvinfo->eedump_len = 0; } +static void ql_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct ql_adapter *qdev = netdev_priv(ndev); + /* What we support. */ + wol->supported = WAKE_MAGIC; + /* What we've currently got set. */ + wol->wolopts = qdev->wol; +} + +static int ql_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct ql_adapter *qdev = netdev_priv(ndev); + int status; + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + qdev->wol = wol->wolopts; + + QPRINTK(qdev, DRV, INFO, "Set wol option 0x%x on %s\n", + qdev->wol, ndev->name); + if (!qdev->wol) { + u32 wol = 0; + status = ql_mb_wol_mode(qdev, wol); + QPRINTK(qdev, DRV, ERR, "WOL %s (wol code 0x%x) on %s\n", + (status == 0) ? "cleared sucessfully" : "clear failed", + wol, qdev->ndev->name); + } + + return 0; +} + +static int ql_phys_id(struct net_device *ndev, u32 data) +{ + struct ql_adapter *qdev = netdev_priv(ndev); + u32 led_reg, i; + int status; + + /* Save the current LED settings */ + status = ql_mb_get_led_cfg(qdev); + if (status) + return status; + led_reg = qdev->led_config; + + /* Start blinking the led */ + if (!data || data > 300) + data = 300; + + for (i = 0; i < (data * 10); i++) + ql_mb_set_led_cfg(qdev, QL_LED_BLINK); + + /* Restore LED settings */ + status = ql_mb_set_led_cfg(qdev, led_reg); + if (status) + return status; + + return 0; +} + +static int ql_start_loopback(struct ql_adapter *qdev) +{ + if (netif_carrier_ok(qdev->ndev)) { + set_bit(QL_LB_LINK_UP, &qdev->flags); + netif_carrier_off(qdev->ndev); + } else + clear_bit(QL_LB_LINK_UP, &qdev->flags); + qdev->link_config |= CFG_LOOPBACK_PCS; + return ql_mb_set_port_cfg(qdev); +} + +static void ql_stop_loopback(struct ql_adapter *qdev) +{ + qdev->link_config &= ~CFG_LOOPBACK_PCS; + ql_mb_set_port_cfg(qdev); + if (test_bit(QL_LB_LINK_UP, &qdev->flags)) { + netif_carrier_on(qdev->ndev); + clear_bit(QL_LB_LINK_UP, &qdev->flags); + } +} + +static void ql_create_lb_frame(struct sk_buff *skb, + unsigned int frame_size) +{ + memset(skb->data, 0xFF, frame_size); + frame_size &= ~1; + memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1); + memset(&skb->data[frame_size / 2 + 10], 0xBE, 1); + memset(&skb->data[frame_size / 2 + 12], 0xAF, 1); +} + +void ql_check_lb_frame(struct ql_adapter *qdev, + struct sk_buff *skb) +{ + unsigned int frame_size = skb->len; + + if ((*(skb->data + 3) == 0xFF) && + (*(skb->data + frame_size / 2 + 10) == 0xBE) && + (*(skb->data + frame_size / 2 + 12) == 0xAF)) { + atomic_dec(&qdev->lb_count); + return; + } +} + +static int ql_run_loopback_test(struct ql_adapter *qdev) +{ + int i; + netdev_tx_t rc; + struct sk_buff *skb; + unsigned int size = SMALL_BUF_MAP_SIZE; + + for (i = 0; i < 64; i++) { + skb = netdev_alloc_skb(qdev->ndev, size); + if (!skb) + return -ENOMEM; + + skb->queue_mapping = 0; + skb_put(skb, size); + ql_create_lb_frame(skb, size); + rc = ql_lb_send(skb, qdev->ndev); + if (rc != NETDEV_TX_OK) + return -EPIPE; + atomic_inc(&qdev->lb_count); + } + + ql_clean_lb_rx_ring(&qdev->rx_ring[0], 128); + return atomic_read(&qdev->lb_count) ? -EIO : 0; +} + +static int ql_loopback_test(struct ql_adapter *qdev, u64 *data) +{ + *data = ql_start_loopback(qdev); + if (*data) + goto out; + *data = ql_run_loopback_test(qdev); +out: + ql_stop_loopback(qdev); + return *data; +} + +static void ql_self_test(struct net_device *ndev, + struct ethtool_test *eth_test, u64 *data) +{ + struct ql_adapter *qdev = netdev_priv(ndev); + + if (netif_running(ndev)) { + set_bit(QL_SELFTEST, &qdev->flags); + if (eth_test->flags == ETH_TEST_FL_OFFLINE) { + /* Offline tests */ + if (ql_loopback_test(qdev, &data[0])) + eth_test->flags |= ETH_TEST_FL_FAILED; + + } else { + /* Online tests */ + data[0] = 0; + } + clear_bit(QL_SELFTEST, &qdev->flags); + } else { + QPRINTK(qdev, DRV, ERR, + "%s: is down, Loopback test will fail.\n", ndev->name); + eth_test->flags |= ETH_TEST_FL_FAILED; + } +} + +static int ql_get_regs_len(struct net_device *ndev) +{ + return sizeof(struct ql_reg_dump); +} + +static void ql_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, void *p) +{ + struct ql_adapter *qdev = netdev_priv(ndev); + + ql_gen_reg_dump(qdev, p); +} + static int ql_get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) { struct ql_adapter *qdev = netdev_priv(dev); @@ -355,6 +606,37 @@ static int ql_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *c) return ql_update_ring_coalescing(qdev); } +static void ql_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct ql_adapter *qdev = netdev_priv(netdev); + + ql_mb_get_port_cfg(qdev); + if (qdev->link_config & CFG_PAUSE_STD) { + pause->rx_pause = 1; + pause->tx_pause = 1; + } +} + +static int ql_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct ql_adapter *qdev = netdev_priv(netdev); + int status = 0; + + if ((pause->rx_pause) && (pause->tx_pause)) + qdev->link_config |= CFG_PAUSE_STD; + else if (!pause->rx_pause && !pause->tx_pause) + qdev->link_config &= ~CFG_PAUSE_STD; + else + return -EINVAL; + + status = ql_mb_set_port_cfg(qdev); + if (status) + return status; + return status; +} + static u32 ql_get_rx_csum(struct net_device *netdev) { struct ql_adapter *qdev = netdev_priv(netdev); @@ -396,9 +678,17 @@ static void ql_set_msglevel(struct net_device *ndev, u32 value) const struct ethtool_ops qlge_ethtool_ops = { .get_settings = ql_get_settings, .get_drvinfo = ql_get_drvinfo, + .get_wol = ql_get_wol, + .set_wol = ql_set_wol, + .get_regs_len = ql_get_regs_len, + .get_regs = ql_get_regs, .get_msglevel = ql_get_msglevel, .set_msglevel = ql_set_msglevel, .get_link = ethtool_op_get_link, + .phys_id = ql_phys_id, + .self_test = ql_self_test, + .get_pauseparam = ql_get_pauseparam, + .set_pauseparam = ql_set_pauseparam, .get_rx_csum = ql_get_rx_csum, .set_rx_csum = ql_set_rx_csum, .get_tx_csum = ethtool_op_get_tx_csum, diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index a2fc70a0d0cc..707b391afa02 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -69,9 +69,9 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); #define MSIX_IRQ 0 #define MSI_IRQ 1 #define LEG_IRQ 2 -static int irq_type = MSIX_IRQ; -module_param(irq_type, int, MSIX_IRQ); -MODULE_PARM_DESC(irq_type, "0 = MSI-X, 1 = MSI, 2 = Legacy."); +static int qlge_irq_type = MSIX_IRQ; +module_param(qlge_irq_type, int, MSIX_IRQ); +MODULE_PARM_DESC(qlge_irq_type, "0 = MSI-X, 1 = MSI, 2 = Legacy."); static struct pci_device_id qlge_pci_tbl[] __devinitdata = { {PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QLGE_DEVICE_ID_8012)}, @@ -1025,6 +1025,11 @@ end: return status; } +static inline unsigned int ql_lbq_block_size(struct ql_adapter *qdev) +{ + return PAGE_SIZE << qdev->lbq_buf_order; +} + /* Get the next large buffer. */ static struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring) { @@ -1036,6 +1041,28 @@ static struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring) return lbq_desc; } +static struct bq_desc *ql_get_curr_lchunk(struct ql_adapter *qdev, + struct rx_ring *rx_ring) +{ + struct bq_desc *lbq_desc = ql_get_curr_lbuf(rx_ring); + + pci_dma_sync_single_for_cpu(qdev->pdev, + pci_unmap_addr(lbq_desc, mapaddr), + rx_ring->lbq_buf_size, + PCI_DMA_FROMDEVICE); + + /* If it's the last chunk of our master page then + * we unmap it. + */ + if ((lbq_desc->p.pg_chunk.offset + rx_ring->lbq_buf_size) + == ql_lbq_block_size(qdev)) + pci_unmap_page(qdev->pdev, + lbq_desc->p.pg_chunk.map, + ql_lbq_block_size(qdev), + PCI_DMA_FROMDEVICE); + return lbq_desc; +} + /* Get the next small buffer. */ static struct bq_desc *ql_get_curr_sbuf(struct rx_ring *rx_ring) { @@ -1063,6 +1090,53 @@ static void ql_write_cq_idx(struct rx_ring *rx_ring) ql_write_db_reg(rx_ring->cnsmr_idx, rx_ring->cnsmr_idx_db_reg); } +static int ql_get_next_chunk(struct ql_adapter *qdev, struct rx_ring *rx_ring, + struct bq_desc *lbq_desc) +{ + if (!rx_ring->pg_chunk.page) { + u64 map; + rx_ring->pg_chunk.page = alloc_pages(__GFP_COLD | __GFP_COMP | + GFP_ATOMIC, + qdev->lbq_buf_order); + if (unlikely(!rx_ring->pg_chunk.page)) { + QPRINTK(qdev, DRV, ERR, + "page allocation failed.\n"); + return -ENOMEM; + } + rx_ring->pg_chunk.offset = 0; + map = pci_map_page(qdev->pdev, rx_ring->pg_chunk.page, + 0, ql_lbq_block_size(qdev), + PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(qdev->pdev, map)) { + __free_pages(rx_ring->pg_chunk.page, + qdev->lbq_buf_order); + QPRINTK(qdev, DRV, ERR, + "PCI mapping failed.\n"); + return -ENOMEM; + } + rx_ring->pg_chunk.map = map; + rx_ring->pg_chunk.va = page_address(rx_ring->pg_chunk.page); + } + + /* Copy the current master pg_chunk info + * to the current descriptor. + */ + lbq_desc->p.pg_chunk = rx_ring->pg_chunk; + + /* Adjust the master page chunk for next + * buffer get. + */ + rx_ring->pg_chunk.offset += rx_ring->lbq_buf_size; + if (rx_ring->pg_chunk.offset == ql_lbq_block_size(qdev)) { + rx_ring->pg_chunk.page = NULL; + lbq_desc->p.pg_chunk.last_flag = 1; + } else { + rx_ring->pg_chunk.va += rx_ring->lbq_buf_size; + get_page(rx_ring->pg_chunk.page); + lbq_desc->p.pg_chunk.last_flag = 0; + } + return 0; +} /* Process (refill) a large buffer queue. */ static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) { @@ -1072,39 +1146,28 @@ static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) u64 map; int i; - while (rx_ring->lbq_free_cnt > 16) { + while (rx_ring->lbq_free_cnt > 32) { for (i = 0; i < 16; i++) { QPRINTK(qdev, RX_STATUS, DEBUG, "lbq: try cleaning clean_idx = %d.\n", clean_idx); lbq_desc = &rx_ring->lbq[clean_idx]; - if (lbq_desc->p.lbq_page == NULL) { - QPRINTK(qdev, RX_STATUS, DEBUG, - "lbq: getting new page for index %d.\n", - lbq_desc->index); - lbq_desc->p.lbq_page = alloc_page(GFP_ATOMIC); - if (lbq_desc->p.lbq_page == NULL) { - rx_ring->lbq_clean_idx = clean_idx; - QPRINTK(qdev, RX_STATUS, ERR, - "Couldn't get a page.\n"); - return; - } - map = pci_map_page(qdev->pdev, - lbq_desc->p.lbq_page, - 0, PAGE_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(qdev->pdev, map)) { - rx_ring->lbq_clean_idx = clean_idx; - put_page(lbq_desc->p.lbq_page); - lbq_desc->p.lbq_page = NULL; - QPRINTK(qdev, RX_STATUS, ERR, - "PCI mapping failed.\n"); + if (ql_get_next_chunk(qdev, rx_ring, lbq_desc)) { + QPRINTK(qdev, IFUP, ERR, + "Could not get a page chunk.\n"); return; } + + map = lbq_desc->p.pg_chunk.map + + lbq_desc->p.pg_chunk.offset; pci_unmap_addr_set(lbq_desc, mapaddr, map); - pci_unmap_len_set(lbq_desc, maplen, PAGE_SIZE); + pci_unmap_len_set(lbq_desc, maplen, + rx_ring->lbq_buf_size); *lbq_desc->addr = cpu_to_le64(map); - } + + pci_dma_sync_single_for_device(qdev->pdev, map, + rx_ring->lbq_buf_size, + PCI_DMA_FROMDEVICE); clean_idx++; if (clean_idx == rx_ring->lbq_len) clean_idx = 0; @@ -1147,7 +1210,7 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) sbq_desc->index); sbq_desc->p.skb = netdev_alloc_skb(qdev->ndev, - rx_ring->sbq_buf_size); + SMALL_BUFFER_SIZE); if (sbq_desc->p.skb == NULL) { QPRINTK(qdev, PROBE, ERR, "Couldn't get an skb.\n"); @@ -1157,8 +1220,8 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) skb_reserve(sbq_desc->p.skb, QLGE_SB_PAD); map = pci_map_single(qdev->pdev, sbq_desc->p.skb->data, - rx_ring->sbq_buf_size / - 2, PCI_DMA_FROMDEVICE); + rx_ring->sbq_buf_size, + PCI_DMA_FROMDEVICE); if (pci_dma_mapping_error(qdev->pdev, map)) { QPRINTK(qdev, IFUP, ERR, "PCI mapping failed.\n"); rx_ring->sbq_clean_idx = clean_idx; @@ -1168,7 +1231,7 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) } pci_unmap_addr_set(sbq_desc, mapaddr, map); pci_unmap_len_set(sbq_desc, maplen, - rx_ring->sbq_buf_size / 2); + rx_ring->sbq_buf_size); *sbq_desc->addr = cpu_to_le64(map); } @@ -1480,27 +1543,24 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, * chain it to the header buffer's skb and let * it rip. */ - lbq_desc = ql_get_curr_lbuf(rx_ring); - pci_unmap_page(qdev->pdev, - pci_unmap_addr(lbq_desc, - mapaddr), - pci_unmap_len(lbq_desc, maplen), - PCI_DMA_FROMDEVICE); + lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); QPRINTK(qdev, RX_STATUS, DEBUG, - "Chaining page to skb.\n"); - skb_fill_page_desc(skb, 0, lbq_desc->p.lbq_page, - 0, length); + "Chaining page at offset = %d," + "for %d bytes to skb.\n", + lbq_desc->p.pg_chunk.offset, length); + skb_fill_page_desc(skb, 0, lbq_desc->p.pg_chunk.page, + lbq_desc->p.pg_chunk.offset, + length); skb->len += length; skb->data_len += length; skb->truesize += length; - lbq_desc->p.lbq_page = NULL; } else { /* * The headers and data are in a single large buffer. We * copy it to a new skb and let it go. This can happen with * jumbo mtu on a non-TCP/UDP frame. */ - lbq_desc = ql_get_curr_lbuf(rx_ring); + lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); skb = netdev_alloc_skb(qdev->ndev, length); if (skb == NULL) { QPRINTK(qdev, PROBE, DEBUG, @@ -1515,13 +1575,14 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, skb_reserve(skb, NET_IP_ALIGN); QPRINTK(qdev, RX_STATUS, DEBUG, "%d bytes of headers and data in large. Chain page to new skb and pull tail.\n", length); - skb_fill_page_desc(skb, 0, lbq_desc->p.lbq_page, - 0, length); + skb_fill_page_desc(skb, 0, + lbq_desc->p.pg_chunk.page, + lbq_desc->p.pg_chunk.offset, + length); skb->len += length; skb->data_len += length; skb->truesize += length; length -= length; - lbq_desc->p.lbq_page = NULL; __pskb_pull_tail(skb, (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ? VLAN_ETH_HLEN : ETH_HLEN); @@ -1538,8 +1599,7 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, * frames. If the MTU goes up we could * eventually be in trouble. */ - int size, offset, i = 0; - __le64 *bq, bq_array[8]; + int size, i = 0; sbq_desc = ql_get_curr_sbuf(rx_ring); pci_unmap_single(qdev->pdev, pci_unmap_addr(sbq_desc, mapaddr), @@ -1558,37 +1618,25 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, QPRINTK(qdev, RX_STATUS, DEBUG, "%d bytes of headers & data in chain of large.\n", length); skb = sbq_desc->p.skb; - bq = &bq_array[0]; - memcpy(bq, skb->data, sizeof(bq_array)); sbq_desc->p.skb = NULL; skb_reserve(skb, NET_IP_ALIGN); - } else { - QPRINTK(qdev, RX_STATUS, DEBUG, - "Headers in small, %d bytes of data in chain of large.\n", length); - bq = (__le64 *)sbq_desc->p.skb->data; } while (length > 0) { - lbq_desc = ql_get_curr_lbuf(rx_ring); - pci_unmap_page(qdev->pdev, - pci_unmap_addr(lbq_desc, - mapaddr), - pci_unmap_len(lbq_desc, - maplen), - PCI_DMA_FROMDEVICE); - size = (length < PAGE_SIZE) ? length : PAGE_SIZE; - offset = 0; + lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); + size = (length < rx_ring->lbq_buf_size) ? length : + rx_ring->lbq_buf_size; QPRINTK(qdev, RX_STATUS, DEBUG, "Adding page %d to skb for %d bytes.\n", i, size); - skb_fill_page_desc(skb, i, lbq_desc->p.lbq_page, - offset, size); + skb_fill_page_desc(skb, i, + lbq_desc->p.pg_chunk.page, + lbq_desc->p.pg_chunk.offset, + size); skb->len += size; skb->data_len += size; skb->truesize += size; length -= size; - lbq_desc->p.lbq_page = NULL; - bq++; i++; } __pskb_pull_tail(skb, (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ? @@ -1613,6 +1661,7 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev, if (unlikely(!skb)) { QPRINTK(qdev, RX_STATUS, DEBUG, "No skb available, drop packet.\n"); + rx_ring->rx_dropped++; return; } @@ -1621,6 +1670,7 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev, QPRINTK(qdev, DRV, ERR, "Receive error, flags2 = 0x%x\n", ib_mac_rsp->flags2); dev_kfree_skb_any(skb); + rx_ring->rx_errors++; return; } @@ -1629,6 +1679,14 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev, */ if (skb->len > ndev->mtu + ETH_HLEN) { dev_kfree_skb_any(skb); + rx_ring->rx_dropped++; + return; + } + + /* loopback self test for ethtool */ + if (test_bit(QL_SELFTEST, &qdev->flags)) { + ql_check_lb_frame(qdev, skb); + dev_kfree_skb_any(skb); return; } @@ -1642,6 +1700,7 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev, IB_MAC_IOCB_RSP_M_REG ? "Registered" : "", (ib_mac_rsp->flags1 & IB_MAC_IOCB_RSP_M_MASK) == IB_MAC_IOCB_RSP_M_PROM ? "Promiscuous" : ""); + rx_ring->rx_multicast++; } if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_P) { QPRINTK(qdev, RX_STATUS, DEBUG, "Promiscuous Packet.\n"); @@ -1673,8 +1732,8 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev, } } - qdev->stats.rx_packets++; - qdev->stats.rx_bytes += skb->len; + rx_ring->rx_packets++; + rx_ring->rx_bytes += skb->len; skb_record_rx_queue(skb, rx_ring->cq_id); if (skb->ip_summed == CHECKSUM_UNNECESSARY) { if (qdev->vlgrp && @@ -1705,8 +1764,8 @@ static void ql_process_mac_tx_intr(struct ql_adapter *qdev, tx_ring = &qdev->tx_ring[mac_rsp->txq_idx]; tx_ring_desc = &tx_ring->q[mac_rsp->tid]; ql_unmap_send(qdev, tx_ring_desc, tx_ring_desc->map_cnt); - qdev->stats.tx_bytes += (tx_ring_desc->skb)->len; - qdev->stats.tx_packets++; + tx_ring->tx_bytes += (tx_ring_desc->skb)->len; + tx_ring->tx_packets++; dev_kfree_skb(tx_ring_desc->skb); tx_ring_desc->skb = NULL; @@ -1929,7 +1988,7 @@ static int ql_napi_poll_msix(struct napi_struct *napi, int budget) return work_done; } -static void ql_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp) +static void qlge_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp) { struct ql_adapter *qdev = netdev_priv(ndev); @@ -1945,7 +2004,7 @@ static void ql_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp) } } -static void ql_vlan_rx_add_vid(struct net_device *ndev, u16 vid) +static void qlge_vlan_rx_add_vid(struct net_device *ndev, u16 vid) { struct ql_adapter *qdev = netdev_priv(ndev); u32 enable_bit = MAC_ADDR_E; @@ -1961,7 +2020,7 @@ static void ql_vlan_rx_add_vid(struct net_device *ndev, u16 vid) ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK); } -static void ql_vlan_rx_kill_vid(struct net_device *ndev, u16 vid) +static void qlge_vlan_rx_kill_vid(struct net_device *ndev, u16 vid) { struct ql_adapter *qdev = netdev_priv(ndev); u32 enable_bit = 0; @@ -2046,12 +2105,12 @@ static irqreturn_t qlge_isr(int irq, void *dev_id) */ var = ql_read32(qdev, ISR1); if (var & intr_context->irq_mask) { - QPRINTK(qdev, INTR, INFO, + QPRINTK(qdev, INTR, INFO, "Waking handler for rx_ring[0].\n"); ql_disable_completion_interrupt(qdev, intr_context->intr); - napi_schedule(&rx_ring->napi); - work_done++; - } + napi_schedule(&rx_ring->napi); + work_done++; + } ql_enable_completion_interrupt(qdev, intr_context->intr); return work_done ? IRQ_HANDLED : IRQ_NONE; } @@ -2149,6 +2208,7 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev) __func__, tx_ring_idx); netif_stop_subqueue(ndev, tx_ring->wq_id); atomic_inc(&tx_ring->queue_stopped); + tx_ring->tx_errors++; return NETDEV_TX_BUSY; } tx_ring_desc = &tx_ring->q[tx_ring->prod_idx]; @@ -2183,6 +2243,7 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev) NETDEV_TX_OK) { QPRINTK(qdev, TX_QUEUED, ERR, "Could not map the segments.\n"); + tx_ring->tx_errors++; return NETDEV_TX_BUSY; } QL_DUMP_OB_MAC_IOCB(mac_iocb_ptr); @@ -2199,6 +2260,7 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev) return NETDEV_TX_OK; } + static void ql_free_shadow_space(struct ql_adapter *qdev) { if (qdev->rx_ring_shadow_reg_area) { @@ -2285,8 +2347,8 @@ static int ql_alloc_tx_resources(struct ql_adapter *qdev, pci_alloc_consistent(qdev->pdev, tx_ring->wq_size, &tx_ring->wq_base_dma); - if ((tx_ring->wq_base == NULL) - || tx_ring->wq_base_dma & WQ_ADDR_ALIGN) { + if ((tx_ring->wq_base == NULL) || + tx_ring->wq_base_dma & WQ_ADDR_ALIGN) { QPRINTK(qdev, IFUP, ERR, "tx_ring alloc failed.\n"); return -ENOMEM; } @@ -2304,20 +2366,29 @@ err: static void ql_free_lbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring) { - int i; struct bq_desc *lbq_desc; - for (i = 0; i < rx_ring->lbq_len; i++) { - lbq_desc = &rx_ring->lbq[i]; - if (lbq_desc->p.lbq_page) { + uint32_t curr_idx, clean_idx; + + curr_idx = rx_ring->lbq_curr_idx; + clean_idx = rx_ring->lbq_clean_idx; + while (curr_idx != clean_idx) { + lbq_desc = &rx_ring->lbq[curr_idx]; + + if (lbq_desc->p.pg_chunk.last_flag) { pci_unmap_page(qdev->pdev, - pci_unmap_addr(lbq_desc, mapaddr), - pci_unmap_len(lbq_desc, maplen), + lbq_desc->p.pg_chunk.map, + ql_lbq_block_size(qdev), PCI_DMA_FROMDEVICE); - - put_page(lbq_desc->p.lbq_page); - lbq_desc->p.lbq_page = NULL; + lbq_desc->p.pg_chunk.last_flag = 0; } + + put_page(lbq_desc->p.pg_chunk.page); + lbq_desc->p.pg_chunk.page = NULL; + + if (++curr_idx == rx_ring->lbq_len) + curr_idx = 0; + } } @@ -2615,6 +2686,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) /* Set up the shadow registers for this ring. */ rx_ring->prod_idx_sh_reg = shadow_reg; rx_ring->prod_idx_sh_reg_dma = shadow_reg_dma; + *rx_ring->prod_idx_sh_reg = 0; shadow_reg += sizeof(u64); shadow_reg_dma += sizeof(u64); rx_ring->lbq_base_indirect = shadow_reg; @@ -2692,7 +2764,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) cqicb->sbq_addr = cpu_to_le64(rx_ring->sbq_base_indirect_dma); cqicb->sbq_buf_size = - cpu_to_le16((u16)(rx_ring->sbq_buf_size/2)); + cpu_to_le16((u16)(rx_ring->sbq_buf_size)); bq_len = (rx_ring->sbq_len == 65536) ? 0 : (u16) rx_ring->sbq_len; cqicb->sbq_len = cpu_to_le16(bq_len); @@ -2798,7 +2870,7 @@ static void ql_enable_msix(struct ql_adapter *qdev) int i, err; /* Get the MSIX vectors. */ - if (irq_type == MSIX_IRQ) { + if (qlge_irq_type == MSIX_IRQ) { /* Try to alloc space for the msix struct, * if it fails then go to MSI/legacy. */ @@ -2806,7 +2878,7 @@ static void ql_enable_msix(struct ql_adapter *qdev) sizeof(struct msix_entry), GFP_KERNEL); if (!qdev->msi_x_entry) { - irq_type = MSI_IRQ; + qlge_irq_type = MSI_IRQ; goto msi; } @@ -2829,7 +2901,7 @@ static void ql_enable_msix(struct ql_adapter *qdev) QPRINTK(qdev, IFUP, WARNING, "MSI-X Enable failed, trying MSI.\n"); qdev->intr_count = 1; - irq_type = MSI_IRQ; + qlge_irq_type = MSI_IRQ; } else if (err == 0) { set_bit(QL_MSIX_ENABLED, &qdev->flags); QPRINTK(qdev, IFUP, INFO, @@ -2840,7 +2912,7 @@ static void ql_enable_msix(struct ql_adapter *qdev) } msi: qdev->intr_count = 1; - if (irq_type == MSI_IRQ) { + if (qlge_irq_type == MSI_IRQ) { if (!pci_enable_msi(qdev->pdev)) { set_bit(QL_MSI_ENABLED, &qdev->flags); QPRINTK(qdev, IFUP, INFO, @@ -2848,7 +2920,7 @@ msi: return; } } - irq_type = LEG_IRQ; + qlge_irq_type = LEG_IRQ; QPRINTK(qdev, IFUP, DEBUG, "Running with legacy interrupts.\n"); } @@ -3268,7 +3340,7 @@ static int ql_adapter_initialize(struct ql_adapter *qdev) ql_write32(qdev, FSC, mask | value); ql_write32(qdev, SPLT_HDR, SPLT_HDR_EP | - min(SMALL_BUFFER_SIZE, MAX_SPLIT_SIZE)); + min(SMALL_BUF_MAP_SIZE, MAX_SPLIT_SIZE)); /* Set RX packet routing to use port/pci function on which the * packet arrived on in addition to usual frame routing. @@ -3276,6 +3348,22 @@ static int ql_adapter_initialize(struct ql_adapter *qdev) * the same MAC address. */ ql_write32(qdev, RST_FO, RST_FO_RR_MASK | RST_FO_RR_RCV_FUNC_CQ); + /* Reroute all packets to our Interface. + * They may have been routed to MPI firmware + * due to WOL. + */ + value = ql_read32(qdev, MGMT_RCV_CFG); + value &= ~MGMT_RCV_CFG_RM; + mask = 0xffff0000; + + /* Sticky reg needs clearing due to WOL. */ + ql_write32(qdev, MGMT_RCV_CFG, mask); + ql_write32(qdev, MGMT_RCV_CFG, mask | value); + + /* Default WOL is enable on Mezz cards */ + if (qdev->pdev->subsystem_device == 0x0068 || + qdev->pdev->subsystem_device == 0x0180) + qdev->wol = WAKE_MAGIC; /* Start up the rx queues. */ for (i = 0; i < qdev->rx_ring_count; i++) { @@ -3310,10 +3398,8 @@ static int ql_adapter_initialize(struct ql_adapter *qdev) /* Initialize the port and set the max framesize. */ status = qdev->nic_ops->port_initialize(qdev); - if (status) { - QPRINTK(qdev, IFUP, ERR, "Failed to start port.\n"); - return status; - } + if (status) + QPRINTK(qdev, IFUP, ERR, "Failed to start port.\n"); /* Set up the MAC address and frame routing filter. */ status = ql_cam_route_initialize(qdev); @@ -3392,6 +3478,52 @@ static void ql_display_dev_info(struct net_device *ndev) QPRINTK(qdev, PROBE, INFO, "MAC address %pM\n", ndev->dev_addr); } +int ql_wol(struct ql_adapter *qdev) +{ + int status = 0; + u32 wol = MB_WOL_DISABLE; + + /* The CAM is still intact after a reset, but if we + * are doing WOL, then we may need to program the + * routing regs. We would also need to issue the mailbox + * commands to instruct the MPI what to do per the ethtool + * settings. + */ + + if (qdev->wol & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_PHY | WAKE_UCAST | + WAKE_MCAST | WAKE_BCAST)) { + QPRINTK(qdev, IFDOWN, ERR, + "Unsupported WOL paramter. qdev->wol = 0x%x.\n", + qdev->wol); + return -EINVAL; + } + + if (qdev->wol & WAKE_MAGIC) { + status = ql_mb_wol_set_magic(qdev, 1); + if (status) { + QPRINTK(qdev, IFDOWN, ERR, + "Failed to set magic packet on %s.\n", + qdev->ndev->name); + return status; + } else + QPRINTK(qdev, DRV, INFO, + "Enabled magic packet successfully on %s.\n", + qdev->ndev->name); + + wol |= MB_WOL_MAGIC_PKT; + } + + if (qdev->wol) { + wol |= MB_WOL_MODE_ON; + status = ql_mb_wol_mode(qdev, wol); + QPRINTK(qdev, DRV, ERR, "WOL %s (wol code 0x%x) on %s\n", + (status == 0) ? "Sucessfully set" : "Failed", wol, + qdev->ndev->name); + } + + return status; +} + static int ql_adapter_down(struct ql_adapter *qdev) { int i, status = 0; @@ -3497,6 +3629,10 @@ static int ql_configure_rings(struct ql_adapter *qdev) struct rx_ring *rx_ring; struct tx_ring *tx_ring; int cpu_cnt = min(MAX_CPUS, (int)num_online_cpus()); + unsigned int lbq_buf_len = (qdev->ndev->mtu > 1500) ? + LARGE_BUFFER_MAX_SIZE : LARGE_BUFFER_MIN_SIZE; + + qdev->lbq_buf_order = get_order(lbq_buf_len); /* In a perfect world we have one RSS ring for each CPU * and each has it's own vector. To do that we ask for @@ -3544,11 +3680,14 @@ static int ql_configure_rings(struct ql_adapter *qdev) rx_ring->lbq_len = NUM_LARGE_BUFFERS; rx_ring->lbq_size = rx_ring->lbq_len * sizeof(__le64); - rx_ring->lbq_buf_size = LARGE_BUFFER_SIZE; + rx_ring->lbq_buf_size = (u16)lbq_buf_len; + QPRINTK(qdev, IFUP, DEBUG, + "lbq_buf_size %d, order = %d\n", + rx_ring->lbq_buf_size, qdev->lbq_buf_order); rx_ring->sbq_len = NUM_SMALL_BUFFERS; rx_ring->sbq_size = rx_ring->sbq_len * sizeof(__le64); - rx_ring->sbq_buf_size = SMALL_BUFFER_SIZE * 2; + rx_ring->sbq_buf_size = SMALL_BUF_MAP_SIZE; rx_ring->type = RX_Q; } else { /* @@ -3575,6 +3714,10 @@ static int qlge_open(struct net_device *ndev) int err = 0; struct ql_adapter *qdev = netdev_priv(ndev); + err = ql_adapter_reset(qdev); + if (err) + return err; + err = ql_configure_rings(qdev); if (err) return err; @@ -3594,14 +3737,63 @@ error_up: return err; } +static int ql_change_rx_buffers(struct ql_adapter *qdev) +{ + struct rx_ring *rx_ring; + int i, status; + u32 lbq_buf_len; + + /* Wait for an oustanding reset to complete. */ + if (!test_bit(QL_ADAPTER_UP, &qdev->flags)) { + int i = 3; + while (i-- && !test_bit(QL_ADAPTER_UP, &qdev->flags)) { + QPRINTK(qdev, IFUP, ERR, + "Waiting for adapter UP...\n"); + ssleep(1); + } + + if (!i) { + QPRINTK(qdev, IFUP, ERR, + "Timed out waiting for adapter UP\n"); + return -ETIMEDOUT; + } + } + + status = ql_adapter_down(qdev); + if (status) + goto error; + + /* Get the new rx buffer size. */ + lbq_buf_len = (qdev->ndev->mtu > 1500) ? + LARGE_BUFFER_MAX_SIZE : LARGE_BUFFER_MIN_SIZE; + qdev->lbq_buf_order = get_order(lbq_buf_len); + + for (i = 0; i < qdev->rss_ring_count; i++) { + rx_ring = &qdev->rx_ring[i]; + /* Set the new size. */ + rx_ring->lbq_buf_size = lbq_buf_len; + } + + status = ql_adapter_up(qdev); + if (status) + goto error; + + return status; +error: + QPRINTK(qdev, IFUP, ALERT, + "Driver up/down cycle failed, closing device.\n"); + set_bit(QL_ADAPTER_UP, &qdev->flags); + dev_close(qdev->ndev); + return status; +} + static int qlge_change_mtu(struct net_device *ndev, int new_mtu) { struct ql_adapter *qdev = netdev_priv(ndev); + int status; if (ndev->mtu == 1500 && new_mtu == 9000) { QPRINTK(qdev, IFUP, ERR, "Changing to jumbo MTU.\n"); - queue_delayed_work(qdev->workqueue, - &qdev->mpi_port_cfg_work, 0); } else if (ndev->mtu == 9000 && new_mtu == 1500) { QPRINTK(qdev, IFUP, ERR, "Changing to normal MTU.\n"); } else if ((ndev->mtu == 1500 && new_mtu == 1500) || @@ -3609,15 +3801,60 @@ static int qlge_change_mtu(struct net_device *ndev, int new_mtu) return 0; } else return -EINVAL; + + queue_delayed_work(qdev->workqueue, + &qdev->mpi_port_cfg_work, 3*HZ); + + if (!netif_running(qdev->ndev)) { + ndev->mtu = new_mtu; + return 0; + } + ndev->mtu = new_mtu; - return 0; + status = ql_change_rx_buffers(qdev); + if (status) { + QPRINTK(qdev, IFUP, ERR, + "Changing MTU failed.\n"); + } + + return status; } static struct net_device_stats *qlge_get_stats(struct net_device *ndev) { struct ql_adapter *qdev = netdev_priv(ndev); - return &qdev->stats; + struct rx_ring *rx_ring = &qdev->rx_ring[0]; + struct tx_ring *tx_ring = &qdev->tx_ring[0]; + unsigned long pkts, mcast, dropped, errors, bytes; + int i; + + /* Get RX stats. */ + pkts = mcast = dropped = errors = bytes = 0; + for (i = 0; i < qdev->rss_ring_count; i++, rx_ring++) { + pkts += rx_ring->rx_packets; + bytes += rx_ring->rx_bytes; + dropped += rx_ring->rx_dropped; + errors += rx_ring->rx_errors; + mcast += rx_ring->rx_multicast; + } + ndev->stats.rx_packets = pkts; + ndev->stats.rx_bytes = bytes; + ndev->stats.rx_dropped = dropped; + ndev->stats.rx_errors = errors; + ndev->stats.multicast = mcast; + + /* Get TX stats. */ + pkts = errors = bytes = 0; + for (i = 0; i < qdev->tx_ring_count; i++, tx_ring++) { + pkts += tx_ring->tx_packets; + bytes += tx_ring->tx_bytes; + errors += tx_ring->tx_errors; + } + ndev->stats.tx_packets = pkts; + ndev->stats.tx_bytes = bytes; + ndev->stats.tx_errors = errors; + return &ndev->stats; } static void qlge_set_multicast_list(struct net_device *ndev) @@ -3714,9 +3951,6 @@ static int qlge_set_mac_address(struct net_device *ndev, void *p) struct sockaddr *addr = p; int status; - if (netif_running(ndev)) - return -EBUSY; - if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); @@ -3868,8 +4102,7 @@ static int __devinit ql_init_device(struct pci_dev *pdev, struct net_device *ndev, int cards_found) { struct ql_adapter *qdev = netdev_priv(ndev); - int pos, err = 0; - u16 val16; + int err = 0; memset((void *)qdev, 0, sizeof(*qdev)); err = pci_enable_device(pdev); @@ -3881,18 +4114,12 @@ static int __devinit ql_init_device(struct pci_dev *pdev, qdev->ndev = ndev; qdev->pdev = pdev; pci_set_drvdata(pdev, ndev); - pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); - if (pos <= 0) { - dev_err(&pdev->dev, PFX "Cannot find PCI Express capability, " - "aborting.\n"); - return pos; - } else { - pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &val16); - val16 &= ~PCI_EXP_DEVCTL_NOSNOOP_EN; - val16 |= (PCI_EXP_DEVCTL_CERE | - PCI_EXP_DEVCTL_NFERE | - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); - pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16); + + /* Set PCIe read request size */ + err = pcie_set_readrq(pdev, 4096); + if (err) { + dev_err(&pdev->dev, "Set readrq failed.\n"); + goto err_out; } err = pci_request_regions(pdev, DRV_NAME); @@ -3991,7 +4218,6 @@ err_out: return err; } - static const struct net_device_ops qlge_netdev_ops = { .ndo_open = qlge_open, .ndo_stop = qlge_close, @@ -4002,9 +4228,9 @@ static const struct net_device_ops qlge_netdev_ops = { .ndo_set_mac_address = qlge_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = qlge_tx_timeout, - .ndo_vlan_rx_register = ql_vlan_rx_register, - .ndo_vlan_rx_add_vid = ql_vlan_rx_add_vid, - .ndo_vlan_rx_kill_vid = ql_vlan_rx_kill_vid, + .ndo_vlan_rx_register = qlge_vlan_rx_register, + .ndo_vlan_rx_add_vid = qlge_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = qlge_vlan_rx_kill_vid, }; static int __devinit qlge_probe(struct pci_dev *pdev, @@ -4060,10 +4286,21 @@ static int __devinit qlge_probe(struct pci_dev *pdev, } ql_link_off(qdev); ql_display_dev_info(ndev); + atomic_set(&qdev->lb_count, 0); cards_found++; return 0; } +netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev) +{ + return qlge_send(skb, ndev); +} + +int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget) +{ + return ql_clean_inbound_rx_ring(rx_ring, budget); +} + static void __devexit qlge_remove(struct pci_dev *pdev) { struct net_device *ndev = pci_get_drvdata(pdev); @@ -4193,6 +4430,7 @@ static int qlge_suspend(struct pci_dev *pdev, pm_message_t state) return err; } + ql_wol(qdev); err = pci_save_state(pdev); if (err) return err; diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c index aec05f266107..e2b2286102d4 100644 --- a/drivers/net/qlge/qlge_mpi.c +++ b/drivers/net/qlge/qlge_mpi.c @@ -1,25 +1,5 @@ #include "qlge.h" -static void ql_display_mb_sts(struct ql_adapter *qdev, - struct mbox_params *mbcp) -{ - int i; - static char *err_sts[] = { - "Command Complete", - "Command Not Supported", - "Host Interface Error", - "Checksum Error", - "Unused Completion Status", - "Test Failed", - "Command Parameter Error"}; - - QPRINTK(qdev, DRV, DEBUG, "%s.\n", - err_sts[mbcp->mbox_out[0] & 0x0000000f]); - for (i = 0; i < mbcp->out_count; i++) - QPRINTK(qdev, DRV, DEBUG, "mbox_out[%d] = 0x%.08x.\n", - i, mbcp->mbox_out[i]); -} - int ql_read_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 *data) { int status; @@ -317,6 +297,7 @@ static void ql_init_fw_done(struct ql_adapter *qdev, struct mbox_params *mbcp) } else { QPRINTK(qdev, DRV, ERR, "Firmware Revision = 0x%.08x.\n", mbcp->mbox_out[1]); + qdev->fw_rev_id = mbcp->mbox_out[1]; status = ql_cam_route_initialize(qdev); if (status) QPRINTK(qdev, IFUP, ERR, @@ -446,6 +427,9 @@ static int ql_mpi_handler(struct ql_adapter *qdev, struct mbox_params *mbcp) ql_aen_lost(qdev, mbcp); break; + case AEN_DCBX_CHG: + /* Need to support AEN 8110 */ + break; default: QPRINTK(qdev, DRV, ERR, "Unsupported AE %.08x.\n", mbcp->mbox_out[0]); @@ -537,7 +521,6 @@ done: MB_CMD_STS_GOOD) && ((mbcp->mbox_out[0] & 0x0000f000) != MB_CMD_STS_INTRMDT)) { - ql_display_mb_sts(qdev, mbcp); status = -EIO; } end: @@ -655,7 +638,7 @@ int ql_mb_idc_ack(struct ql_adapter *qdev) * for the current port. * Most likely will block. */ -static int ql_mb_set_port_cfg(struct ql_adapter *qdev) +int ql_mb_set_port_cfg(struct ql_adapter *qdev) { struct mbox_params mbc; struct mbox_params *mbcp = &mbc; @@ -690,7 +673,7 @@ static int ql_mb_set_port_cfg(struct ql_adapter *qdev) * for the current port. * Most likely will block. */ -static int ql_mb_get_port_cfg(struct ql_adapter *qdev) +int ql_mb_get_port_cfg(struct ql_adapter *qdev) { struct mbox_params mbc; struct mbox_params *mbcp = &mbc; @@ -720,6 +703,76 @@ static int ql_mb_get_port_cfg(struct ql_adapter *qdev) return status; } +int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 2; + mbcp->out_count = 1; + + mbcp->mbox_in[0] = MB_CMD_SET_WOL_MODE; + mbcp->mbox_in[1] = wol; + + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { + QPRINTK(qdev, DRV, ERR, + "Failed to set WOL mode.\n"); + status = -EIO; + } + return status; +} + +int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status; + u8 *addr = qdev->ndev->dev_addr; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 8; + mbcp->out_count = 1; + + mbcp->mbox_in[0] = MB_CMD_SET_WOL_MAGIC; + if (enable_wol) { + mbcp->mbox_in[1] = (u32)addr[0]; + mbcp->mbox_in[2] = (u32)addr[1]; + mbcp->mbox_in[3] = (u32)addr[2]; + mbcp->mbox_in[4] = (u32)addr[3]; + mbcp->mbox_in[5] = (u32)addr[4]; + mbcp->mbox_in[6] = (u32)addr[5]; + mbcp->mbox_in[7] = 0; + } else { + mbcp->mbox_in[1] = 0; + mbcp->mbox_in[2] = 1; + mbcp->mbox_in[3] = 1; + mbcp->mbox_in[4] = 1; + mbcp->mbox_in[5] = 1; + mbcp->mbox_in[6] = 1; + mbcp->mbox_in[7] = 0; + } + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { + QPRINTK(qdev, DRV, ERR, + "Failed to set WOL mode.\n"); + status = -EIO; + } + return status; +} + /* IDC - Inter Device Communication... * Some firmware commands require consent of adjacent FCOE * function. This function waits for the OK, or a @@ -769,6 +822,61 @@ static int ql_idc_wait(struct ql_adapter *qdev) return status; } +int ql_mb_set_led_cfg(struct ql_adapter *qdev, u32 led_config) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 2; + mbcp->out_count = 1; + + mbcp->mbox_in[0] = MB_CMD_SET_LED_CFG; + mbcp->mbox_in[1] = led_config; + + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { + QPRINTK(qdev, DRV, ERR, + "Failed to set LED Configuration.\n"); + status = -EIO; + } + + return status; +} + +int ql_mb_get_led_cfg(struct ql_adapter *qdev) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 1; + mbcp->out_count = 2; + + mbcp->mbox_in[0] = MB_CMD_GET_LED_CFG; + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { + QPRINTK(qdev, DRV, ERR, + "Failed to get LED Configuration.\n"); + status = -EIO; + } else + qdev->led_config = mbcp->mbox_out[1]; + + return status; +} + int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 control) { struct mbox_params mbc; @@ -930,8 +1038,11 @@ void ql_mpi_idc_work(struct work_struct *work) int status; struct mbox_params *mbcp = &qdev->idc_mbc; u32 aen; + int timeout; + rtnl_lock(); aen = mbcp->mbox_out[1] >> 16; + timeout = (mbcp->mbox_out[1] >> 8) & 0xf; switch (aen) { default: @@ -939,22 +1050,61 @@ void ql_mpi_idc_work(struct work_struct *work) "Bug: Unhandled IDC action.\n"); break; case MB_CMD_PORT_RESET: - case MB_CMD_SET_PORT_CFG: case MB_CMD_STOP_FW: ql_link_off(qdev); + case MB_CMD_SET_PORT_CFG: /* Signal the resulting link up AEN * that the frame routing and mac addr * needs to be set. * */ set_bit(QL_CAM_RT_SET, &qdev->flags); - rtnl_lock(); - status = ql_mb_idc_ack(qdev); - rtnl_unlock(); - if (status) { - QPRINTK(qdev, DRV, ERR, - "Bug: No pending IDC!\n"); + /* Do ACK if required */ + if (timeout) { + status = ql_mb_idc_ack(qdev); + if (status) + QPRINTK(qdev, DRV, ERR, + "Bug: No pending IDC!\n"); + } else { + QPRINTK(qdev, DRV, DEBUG, + "IDC ACK not required\n"); + status = 0; /* success */ } + break; + + /* These sub-commands issued by another (FCoE) + * function are requesting to do an operation + * on the shared resource (MPI environment). + * We currently don't issue these so we just + * ACK the request. + */ + case MB_CMD_IOP_RESTART_MPI: + case MB_CMD_IOP_PREP_LINK_DOWN: + /* Drop the link, reload the routing + * table when link comes up. + */ + ql_link_off(qdev); + set_bit(QL_CAM_RT_SET, &qdev->flags); + /* Fall through. */ + case MB_CMD_IOP_DVR_START: + case MB_CMD_IOP_FLASH_ACC: + case MB_CMD_IOP_CORE_DUMP_MPI: + case MB_CMD_IOP_PREP_UPDATE_MPI: + case MB_CMD_IOP_COMP_UPDATE_MPI: + case MB_CMD_IOP_NONE: /* an IDC without params */ + /* Do ACK if required */ + if (timeout) { + status = ql_mb_idc_ack(qdev); + if (status) + QPRINTK(qdev, DRV, ERR, + "Bug: No pending IDC!\n"); + } else { + QPRINTK(qdev, DRV, DEBUG, + "IDC ACK not required\n"); + status = 0; /* success */ + } + break; } + rtnl_unlock(); } void ql_mpi_work(struct work_struct *work) diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 8b14c6eda7c3..f03e2e4a15a8 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -842,7 +842,7 @@ static int r6040_open(struct net_device *dev) int ret; /* Request IRQ and Register interrupt handler */ - ret = request_irq(dev->irq, &r6040_interrupt, + ret = request_irq(dev->irq, r6040_interrupt, IRQF_SHARED, dev->name, dev); if (ret) return ret; @@ -958,8 +958,7 @@ static void r6040_multicast_list(struct net_device *dev) } /* Too many multicast addresses * accept all traffic */ - else if ((dev->mc_count > MCAST_MAX) - || (dev->flags & IFF_ALLMULTI)) + else if ((dev->mc_count > MCAST_MAX) || (dev->flags & IFF_ALLMULTI)) reg |= 0x0020; iowrite16(reg, ioaddr); diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 0fe2fc90f207..acfc5a3aa490 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -794,7 +794,7 @@ static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct rtl8169_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; unsigned int i; - static struct { + static const struct { u32 opt; u16 reg; u8 mask; @@ -1277,7 +1277,7 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp, * * (RTL_R32(TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec */ - const struct { + static const struct { u32 mask; u32 val; int mac_version; @@ -1351,7 +1351,7 @@ struct phy_reg { u16 val; }; -static void rtl_phy_write(void __iomem *ioaddr, struct phy_reg *regs, int len) +static void rtl_phy_write(void __iomem *ioaddr, const struct phy_reg *regs, int len) { while (len-- > 0) { mdio_write(ioaddr, regs->reg, regs->val); @@ -1361,7 +1361,7 @@ static void rtl_phy_write(void __iomem *ioaddr, struct phy_reg *regs, int len) static void rtl8169s_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, { 0x06, 0x006e }, { 0x08, 0x0708 }, @@ -1428,7 +1428,7 @@ static void rtl8169s_hw_phy_config(void __iomem *ioaddr) static void rtl8169sb_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0002 }, { 0x01, 0x90d0 }, { 0x1f, 0x0000 } @@ -1457,7 +1457,7 @@ static void rtl8169scd_hw_phy_config_quirk(struct rtl8169_private *tp, static void rtl8169scd_hw_phy_config(struct rtl8169_private *tp, void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, { 0x04, 0x0000 }, { 0x03, 0x00a1 }, @@ -1504,7 +1504,7 @@ static void rtl8169scd_hw_phy_config(struct rtl8169_private *tp, static void rtl8169sce_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, { 0x04, 0x0000 }, { 0x03, 0x00a1 }, @@ -1557,7 +1557,7 @@ static void rtl8169sce_hw_phy_config(void __iomem *ioaddr) static void rtl8168bb_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x10, 0xf41b }, { 0x1f, 0x0000 } }; @@ -1570,7 +1570,7 @@ static void rtl8168bb_hw_phy_config(void __iomem *ioaddr) static void rtl8168bef_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, { 0x10, 0xf41b }, { 0x1f, 0x0000 } @@ -1581,7 +1581,7 @@ static void rtl8168bef_hw_phy_config(void __iomem *ioaddr) static void rtl8168cp_1_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0000 }, { 0x1d, 0x0f00 }, { 0x1f, 0x0002 }, @@ -1594,7 +1594,7 @@ static void rtl8168cp_1_hw_phy_config(void __iomem *ioaddr) static void rtl8168cp_2_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, { 0x1d, 0x3d98 }, { 0x1f, 0x0000 } @@ -1609,7 +1609,7 @@ static void rtl8168cp_2_hw_phy_config(void __iomem *ioaddr) static void rtl8168c_1_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, { 0x12, 0x2300 }, { 0x1f, 0x0002 }, @@ -1638,7 +1638,7 @@ static void rtl8168c_1_hw_phy_config(void __iomem *ioaddr) static void rtl8168c_2_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, { 0x12, 0x2300 }, { 0x03, 0x802f }, @@ -1666,7 +1666,7 @@ static void rtl8168c_2_hw_phy_config(void __iomem *ioaddr) static void rtl8168c_3_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, { 0x12, 0x2300 }, { 0x1d, 0x3d98 }, @@ -1693,7 +1693,7 @@ static void rtl8168c_4_hw_phy_config(void __iomem *ioaddr) static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr) { - static struct phy_reg phy_reg_init_0[] = { + static const struct phy_reg phy_reg_init_0[] = { { 0x1f, 0x0001 }, { 0x06, 0x4064 }, { 0x07, 0x2863 }, @@ -1712,14 +1712,14 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr) { 0x1a, 0x05ad }, { 0x14, 0x94c0 } }; - static struct phy_reg phy_reg_init_1[] = { + static const struct phy_reg phy_reg_init_1[] = { { 0x1f, 0x0002 }, { 0x06, 0x5561 }, { 0x1f, 0x0005 }, { 0x05, 0x8332 }, { 0x06, 0x5561 } }; - static struct phy_reg phy_reg_init_2[] = { + static const struct phy_reg phy_reg_init_2[] = { { 0x1f, 0x0005 }, { 0x05, 0xffc2 }, { 0x1f, 0x0005 }, @@ -2084,7 +2084,7 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr) rtl_phy_write(ioaddr, phy_reg_init_1, ARRAY_SIZE(phy_reg_init_1)); if (rtl8168d_efuse_read(ioaddr, 0x01) == 0xb1) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0002 }, { 0x05, 0x669a }, { 0x1f, 0x0005 }, @@ -2099,7 +2099,7 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr) val = mdio_read(ioaddr, 0x0d); if ((val & 0x00ff) != 0x006c) { - u32 set[] = { + static const u32 set[] = { 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c }; @@ -2112,7 +2112,7 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr) mdio_write(ioaddr, 0x0d, val | set[i]); } } else { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0002 }, { 0x05, 0x6662 }, { 0x1f, 0x0005 }, @@ -2136,7 +2136,7 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr) static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr) { - static struct phy_reg phy_reg_init_0[] = { + static const struct phy_reg phy_reg_init_0[] = { { 0x1f, 0x0001 }, { 0x06, 0x4064 }, { 0x07, 0x2863 }, @@ -2161,7 +2161,7 @@ static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr) { 0x05, 0x8332 }, { 0x06, 0x5561 } }; - static struct phy_reg phy_reg_init_1[] = { + static const struct phy_reg phy_reg_init_1[] = { { 0x1f, 0x0005 }, { 0x05, 0xffc2 }, { 0x1f, 0x0005 }, @@ -2477,7 +2477,7 @@ static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr) rtl_phy_write(ioaddr, phy_reg_init_0, ARRAY_SIZE(phy_reg_init_0)); if (rtl8168d_efuse_read(ioaddr, 0x01) == 0xb1) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0002 }, { 0x05, 0x669a }, { 0x1f, 0x0005 }, @@ -2505,7 +2505,7 @@ static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr) mdio_write(ioaddr, 0x0d, val | set[i]); } } else { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0002 }, { 0x05, 0x2642 }, { 0x1f, 0x0005 }, @@ -2531,7 +2531,7 @@ static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr) static void rtl8168d_3_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0002 }, { 0x10, 0x0008 }, { 0x0d, 0x006c }, @@ -2592,7 +2592,7 @@ static void rtl8168d_3_hw_phy_config(void __iomem *ioaddr) static void rtl8102e_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init[] = { + static const struct phy_reg phy_reg_init[] = { { 0x1f, 0x0003 }, { 0x08, 0x441d }, { 0x01, 0x9100 }, @@ -3388,7 +3388,7 @@ static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz) static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version) { - struct { + static const struct { u32 mac_version; u32 clk; u32 val; @@ -3512,7 +3512,7 @@ struct ephy_info { u16 bits; }; -static void rtl_ephy_init(void __iomem *ioaddr, struct ephy_info *e, int len) +static void rtl_ephy_init(void __iomem *ioaddr, const struct ephy_info *e, int len) { u16 w; @@ -3583,7 +3583,7 @@ static void __rtl_hw_start_8168cp(void __iomem *ioaddr, struct pci_dev *pdev) static void rtl_hw_start_8168cp_1(void __iomem *ioaddr, struct pci_dev *pdev) { - static struct ephy_info e_info_8168cp[] = { + static const struct ephy_info e_info_8168cp[] = { { 0x01, 0, 0x0001 }, { 0x02, 0x0800, 0x1000 }, { 0x03, 0, 0x0042 }, @@ -3627,7 +3627,7 @@ static void rtl_hw_start_8168cp_3(void __iomem *ioaddr, struct pci_dev *pdev) static void rtl_hw_start_8168c_1(void __iomem *ioaddr, struct pci_dev *pdev) { - static struct ephy_info e_info_8168c_1[] = { + static const struct ephy_info e_info_8168c_1[] = { { 0x02, 0x0800, 0x1000 }, { 0x03, 0, 0x0002 }, { 0x06, 0x0080, 0x0000 } @@ -3644,7 +3644,7 @@ static void rtl_hw_start_8168c_1(void __iomem *ioaddr, struct pci_dev *pdev) static void rtl_hw_start_8168c_2(void __iomem *ioaddr, struct pci_dev *pdev) { - static struct ephy_info e_info_8168c_2[] = { + static const struct ephy_info e_info_8168c_2[] = { { 0x01, 0, 0x0001 }, { 0x03, 0x0400, 0x0220 } }; @@ -3787,7 +3787,7 @@ static void rtl_hw_start_8168(struct net_device *dev) static void rtl_hw_start_8102e_1(void __iomem *ioaddr, struct pci_dev *pdev) { - static struct ephy_info e_info_8102e_1[] = { + static const struct ephy_info e_info_8102e_1[] = { { 0x01, 0, 0x6e65 }, { 0x02, 0, 0x091f }, { 0x03, 0, 0xc2f9 }, @@ -4447,13 +4447,12 @@ static inline bool rtl8169_try_rx_copy(struct sk_buff **sk_buff, if (pkt_size >= rx_copybreak) goto out; - skb = netdev_alloc_skb(tp->dev, pkt_size + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(tp->dev, pkt_size); if (!skb) goto out; pci_dma_sync_single_for_cpu(tp->pci_dev, addr, pkt_size, PCI_DMA_FROMDEVICE); - skb_reserve(skb, NET_IP_ALIGN); skb_copy_from_linear_data(*sk_buff, skb->data, pkt_size); *sk_buff = skb; done = true; @@ -4764,8 +4763,8 @@ static void rtl_set_rx_mode(struct net_device *dev) AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 0dd7839322bc..cc4218667cba 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -3238,7 +3238,7 @@ static u64 s2io_mdio_read(u32 mmd_type, u64 addr, struct net_device *dev) /** * s2io_chk_xpak_counter - Function to check the status of the xpak counters - * @counter : couter value to be updated + * @counter : counter value to be updated * @flag : flag to indicate the status * @type : counter type * Description: diff --git a/drivers/net/s6gmac.c b/drivers/net/s6gmac.c index 4525cbe8dd69..45f26344b368 100644 --- a/drivers/net/s6gmac.c +++ b/drivers/net/s6gmac.c @@ -373,9 +373,9 @@ struct s6gmac { static void s6gmac_rx_fillfifo(struct s6gmac *pd) { struct sk_buff *skb; - while ((((u8)(pd->rx_skb_i - pd->rx_skb_o)) < S6_NUM_RX_SKB) - && (!s6dmac_fifo_full(pd->rx_dma, pd->rx_chan)) - && (skb = dev_alloc_skb(S6_MAX_FRLEN + 2))) { + while ((((u8)(pd->rx_skb_i - pd->rx_skb_o)) < S6_NUM_RX_SKB) && + (!s6dmac_fifo_full(pd->rx_dma, pd->rx_chan)) && + (skb = dev_alloc_skb(S6_MAX_FRLEN + 2))) { pd->rx_skb[(pd->rx_skb_i++) % S6_NUM_RX_SKB] = skb; s6dmac_put_fifo_cache(pd->rx_dma, pd->rx_chan, pd->io, (u32)skb->data, S6_MAX_FRLEN); @@ -984,7 +984,7 @@ static int __devinit s6gmac_probe(struct platform_device *pdev) pd->rx_dma = DMA_MASK_DMAC(i); pd->rx_chan = DMA_INDEX_CHNL(i); pd->io = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; - res = request_irq(dev->irq, &s6gmac_interrupt, 0, dev->name, dev); + res = request_irq(dev->irq, s6gmac_interrupt, 0, dev->name, dev); if (res) { printk(KERN_ERR DRV_PRMT "irq request failed: %d\n", dev->irq); goto errirq; diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index c9c70ab0cce0..9f83a1197375 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -973,7 +973,7 @@ sb1000_open(struct net_device *dev) lp->rx_frame_id[1] = 0; lp->rx_frame_id[2] = 0; lp->rx_frame_id[3] = 0; - if (request_irq(dev->irq, &sb1000_interrupt, 0, "sb1000", dev)) { + if (request_irq(dev->irq, sb1000_interrupt, 0, "sb1000", dev)) { return -EAGAIN; } diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 508551f1b3fc..564d4d7f855b 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -1476,7 +1476,6 @@ static void sbmac_channel_start(struct sbmac_softc *s) V_MAC_TX_RL_THRSH(4) | V_MAC_RX_PL_THRSH(4) | V_MAC_RX_RD_THRSH(4) | /* Must be '4' */ - V_MAC_RX_PL_THRSH(4) | V_MAC_RX_RL_THRSH(8) | 0; @@ -2411,7 +2410,7 @@ static int sbmac_open(struct net_device *dev) */ __raw_readq(sc->sbm_isr); - err = request_irq(dev->irq, &sbmac_intr, IRQF_SHARED, dev->name, dev); + err = request_irq(dev->irq, sbmac_intr, IRQF_SHARED, dev->name, dev); if (err) { printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c index 8d6030022d14..e35050322f97 100644 --- a/drivers/net/sc92031.c +++ b/drivers/net/sc92031.c @@ -428,9 +428,9 @@ static void _sc92031_set_mar(struct net_device *dev) void __iomem *port_base = priv->port_base; u32 mar0 = 0, mar1 = 0; - if ((dev->flags & IFF_PROMISC) - || dev->mc_count > multicast_filter_limit - || (dev->flags & IFF_ALLMULTI)) + if ((dev->flags & IFF_PROMISC) || + dev->mc_count > multicast_filter_limit || + (dev->flags & IFF_ALLMULTI)) mar0 = mar1 = 0xffffffff; else if (dev->flags & IFF_MULTICAST) { struct dev_mc_list *mc_list; @@ -777,10 +777,10 @@ static void _sc92031_rx_tasklet(struct net_device *dev) rx_ring_offset = (rx_ring_offset + 4) % RX_BUF_LEN; - if (unlikely(rx_status == 0 - || rx_size > (MAX_ETH_FRAME_SIZE + 4) - || rx_size < 16 - || !(rx_status & RxStatesOK))) { + if (unlikely(rx_status == 0 || + rx_size > (MAX_ETH_FRAME_SIZE + 4) || + rx_size < 16 || + !(rx_status & RxStatesOK))) { _sc92031_rx_tasklet_error(dev, rx_status, rx_size); break; } @@ -793,7 +793,7 @@ static void _sc92031_rx_tasklet(struct net_device *dev) rx_len -= rx_size_align + 4; - skb = netdev_alloc_skb(dev, pkt_size + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(dev, pkt_size); if (unlikely(!skb)) { if (printk_ratelimit()) printk(KERN_ERR "%s: Couldn't allocate a skb_buff for a packet of size %u\n", @@ -801,8 +801,6 @@ static void _sc92031_rx_tasklet(struct net_device *dev) goto next; } - skb_reserve(skb, NET_IP_ALIGN); - if ((rx_ring_offset + pkt_size) > RX_BUF_LEN) { memcpy(skb_put(skb, RX_BUF_LEN - rx_ring_offset), rx_ring + rx_ring_offset, RX_BUF_LEN - rx_ring_offset); diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c index 39246d457ac2..fe806bd9b95f 100644 --- a/drivers/net/seeq8005.c +++ b/drivers/net/seeq8005.c @@ -335,7 +335,7 @@ static int __init seeq8005_probe1(struct net_device *dev, int ioaddr) #if 0 { - int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", dev); + int irqval = request_irq(dev->irq, seeq8005_interrupt, 0, "seeq8005", dev); if (irqval) { printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval); @@ -367,7 +367,7 @@ static int seeq8005_open(struct net_device *dev) struct net_local *lp = netdev_priv(dev); { - int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", dev); + int irqval = request_irq(dev->irq, seeq8005_interrupt, 0, "seeq8005", dev); if (irqval) { printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval); diff --git a/drivers/net/sfc/Kconfig b/drivers/net/sfc/Kconfig index 260aafaac235..a65c98638398 100644 --- a/drivers/net/sfc/Kconfig +++ b/drivers/net/sfc/Kconfig @@ -1,5 +1,5 @@ config SFC - tristate "Solarflare Solarstorm SFC4000 support" + tristate "Solarflare Solarstorm SFC4000/SFC9000-family support" depends on PCI && INET select MDIO select CRC32 @@ -7,15 +7,16 @@ config SFC select I2C_ALGOBIT help This driver supports 10-gigabit Ethernet cards based on - the Solarflare Communications Solarstorm SFC4000 controller. + the Solarflare Communications Solarstorm SFC4000 and + SFC9000-family controllers. To compile this driver as a module, choose M here. The module will be called sfc. config SFC_MTD - bool "Solarflare Solarstorm SFC4000 flash MTD support" + bool "Solarflare Solarstorm SFC4000/SFC9000-family MTD support" depends on SFC && MTD && !(SFC=y && MTD=m) default y help - This exposes the on-board flash memory as an MTD device (e.g. - /dev/mtd1). This makes it possible to upload new boot code - to the NIC. + This exposes the on-board flash memory as MTD devices (e.g. + /dev/mtd1). This makes it possible to upload new firmware + to the NIC. diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile index b89f9be3cb13..1047b19c60a5 100644 --- a/drivers/net/sfc/Makefile +++ b/drivers/net/sfc/Makefile @@ -1,6 +1,7 @@ -sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.o \ - falcon_xmac.o selftest.o ethtool.o xfp_phy.o \ - mdio_10g.o tenxpress.o boards.o sfe4001.o +sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o \ + falcon_gmac.o falcon_xmac.o mcdi_mac.o \ + selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ + tenxpress.o falcon_boards.o mcdi.o mcdi_phy.o sfc-$(CONFIG_SFC_MTD) += mtd.o obj-$(CONFIG_SFC) += sfc.o diff --git a/drivers/net/sfc/bitfield.h b/drivers/net/sfc/bitfield.h index d54d84c267b9..098ac2ad757d 100644 --- a/drivers/net/sfc/bitfield.h +++ b/drivers/net/sfc/bitfield.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -37,6 +37,8 @@ #define EFX_DWORD_2_WIDTH 32 #define EFX_DWORD_3_LBN 96 #define EFX_DWORD_3_WIDTH 32 +#define EFX_QWORD_0_LBN 0 +#define EFX_QWORD_0_WIDTH 64 /* Specified attribute (e.g. LBN) of the specified field */ #define EFX_VAL(field, attribute) field ## _ ## attribute @@ -520,19 +522,6 @@ typedef union efx_oword { #define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32 #endif -#define EFX_SET_OWORD_FIELD_VER(efx, oword, field, value) do { \ - if (falcon_rev(efx) >= FALCON_REV_B0) { \ - EFX_SET_OWORD_FIELD((oword), field##_B0, (value)); \ - } else { \ - EFX_SET_OWORD_FIELD((oword), field##_A1, (value)); \ - } \ -} while (0) - -#define EFX_QWORD_FIELD_VER(efx, qword, field) \ - (falcon_rev(efx) >= FALCON_REV_B0 ? \ - EFX_QWORD_FIELD((qword), field##_B0) : \ - EFX_QWORD_FIELD((qword), field##_A1)) - /* Used to avoid compiler warnings about shift range exceeding width * of the data types when dma_addr_t is only 32 bits wide. */ diff --git a/drivers/net/sfc/boards.c b/drivers/net/sfc/boards.c deleted file mode 100644 index 4a4c74c891b7..000000000000 --- a/drivers/net/sfc/boards.c +++ /dev/null @@ -1,328 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#include "net_driver.h" -#include "phy.h" -#include "boards.h" -#include "efx.h" -#include "workarounds.h" - -/* Macros for unpacking the board revision */ -/* The revision info is in host byte order. */ -#define BOARD_TYPE(_rev) (_rev >> 8) -#define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) -#define BOARD_MINOR(_rev) (_rev & 0xf) - -/* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */ -#define BLINK_INTERVAL (HZ/2) - -static void blink_led_timer(unsigned long context) -{ - struct efx_nic *efx = (struct efx_nic *)context; - struct efx_blinker *bl = &efx->board_info.blinker; - efx->board_info.set_id_led(efx, bl->state); - bl->state = !bl->state; - if (bl->resubmit) - mod_timer(&bl->timer, jiffies + BLINK_INTERVAL); -} - -static void board_blink(struct efx_nic *efx, bool blink) -{ - struct efx_blinker *blinker = &efx->board_info.blinker; - - /* The rtnl mutex serialises all ethtool ioctls, so - * nothing special needs doing here. */ - if (blink) { - blinker->resubmit = true; - blinker->state = false; - setup_timer(&blinker->timer, blink_led_timer, - (unsigned long)efx); - mod_timer(&blinker->timer, jiffies + BLINK_INTERVAL); - } else { - blinker->resubmit = false; - if (blinker->timer.function) - del_timer_sync(&blinker->timer); - efx->board_info.init_leds(efx); - } -} - -/***************************************************************************** - * Support for LM87 sensor chip used on several boards - */ -#define LM87_REG_ALARMS1 0x41 -#define LM87_REG_ALARMS2 0x42 -#define LM87_IN_LIMITS(nr, _min, _max) \ - 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min -#define LM87_AIN_LIMITS(nr, _min, _max) \ - 0x3B + (nr), _max, 0x1A + (nr), _min -#define LM87_TEMP_INT_LIMITS(_min, _max) \ - 0x39, _max, 0x3A, _min -#define LM87_TEMP_EXT1_LIMITS(_min, _max) \ - 0x37, _max, 0x38, _min - -#define LM87_ALARM_TEMP_INT 0x10 -#define LM87_ALARM_TEMP_EXT1 0x20 - -#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE) - -static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info, - const u8 *reg_values) -{ - struct i2c_client *client = i2c_new_device(&efx->i2c_adap, info); - int rc; - - if (!client) - return -EIO; - - while (*reg_values) { - u8 reg = *reg_values++; - u8 value = *reg_values++; - rc = i2c_smbus_write_byte_data(client, reg, value); - if (rc) - goto err; - } - - efx->board_info.hwmon_client = client; - return 0; - -err: - i2c_unregister_device(client); - return rc; -} - -static void efx_fini_lm87(struct efx_nic *efx) -{ - i2c_unregister_device(efx->board_info.hwmon_client); -} - -static int efx_check_lm87(struct efx_nic *efx, unsigned mask) -{ - struct i2c_client *client = efx->board_info.hwmon_client; - s32 alarms1, alarms2; - - /* If link is up then do not monitor temperature */ - if (EFX_WORKAROUND_7884(efx) && efx->link_up) - return 0; - - alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1); - alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2); - if (alarms1 < 0) - return alarms1; - if (alarms2 < 0) - return alarms2; - alarms1 &= mask; - alarms2 &= mask >> 8; - if (alarms1 || alarms2) { - EFX_ERR(efx, - "LM87 detected a hardware failure (status %02x:%02x)" - "%s%s\n", - alarms1, alarms2, - (alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "", - (alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : ""); - return -ERANGE; - } - - return 0; -} - -#else /* !CONFIG_SENSORS_LM87 */ - -static inline int -efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info, - const u8 *reg_values) -{ - return 0; -} -static inline void efx_fini_lm87(struct efx_nic *efx) -{ -} -static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask) -{ - return 0; -} - -#endif /* CONFIG_SENSORS_LM87 */ - -/***************************************************************************** - * Support for the SFE4002 - * - */ -static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */ - -static const u8 sfe4002_lm87_regs[] = { - LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */ - LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */ - LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */ - LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */ - LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */ - LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */ - LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */ - LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */ - LM87_TEMP_INT_LIMITS(10, 60), /* board */ - LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */ - 0 -}; - -static struct i2c_board_info sfe4002_hwmon_info = { - I2C_BOARD_INFO("lm87", 0x2e), - .platform_data = &sfe4002_lm87_channel, -}; - -/****************************************************************************/ -/* LED allocations. Note that on rev A0 boards the schematic and the reality - * differ: red and green are swapped. Below is the fixed (A1) layout (there - * are only 3 A0 boards in existence, so no real reason to make this - * conditional). - */ -#define SFE4002_FAULT_LED (2) /* Red */ -#define SFE4002_RX_LED (0) /* Green */ -#define SFE4002_TX_LED (1) /* Amber */ - -static void sfe4002_init_leds(struct efx_nic *efx) -{ - /* Set the TX and RX LEDs to reflect status and activity, and the - * fault LED off */ - xfp_set_led(efx, SFE4002_TX_LED, - QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); - xfp_set_led(efx, SFE4002_RX_LED, - QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); - xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); -} - -static void sfe4002_set_id_led(struct efx_nic *efx, bool state) -{ - xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON : - QUAKE_LED_OFF); -} - -static int sfe4002_check_hw(struct efx_nic *efx) -{ - /* A0 board rev. 4002s report a temperature fault the whole time - * (bad sensor) so we mask it out. */ - unsigned alarm_mask = - (efx->board_info.major == 0 && efx->board_info.minor == 0) ? - ~LM87_ALARM_TEMP_EXT1 : ~0; - - return efx_check_lm87(efx, alarm_mask); -} - -static int sfe4002_init(struct efx_nic *efx) -{ - int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs); - if (rc) - return rc; - efx->board_info.monitor = sfe4002_check_hw; - efx->board_info.init_leds = sfe4002_init_leds; - efx->board_info.set_id_led = sfe4002_set_id_led; - efx->board_info.blink = board_blink; - efx->board_info.fini = efx_fini_lm87; - return 0; -} - -/***************************************************************************** - * Support for the SFN4112F - * - */ -static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */ - -static const u8 sfn4112f_lm87_regs[] = { - LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */ - LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */ - LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */ - LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */ - LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */ - LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */ - LM87_TEMP_INT_LIMITS(10, 60), /* board */ - LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */ - 0 -}; - -static struct i2c_board_info sfn4112f_hwmon_info = { - I2C_BOARD_INFO("lm87", 0x2e), - .platform_data = &sfn4112f_lm87_channel, -}; - -#define SFN4112F_ACT_LED 0 -#define SFN4112F_LINK_LED 1 - -static void sfn4112f_init_leds(struct efx_nic *efx) -{ - xfp_set_led(efx, SFN4112F_ACT_LED, - QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACT); - xfp_set_led(efx, SFN4112F_LINK_LED, - QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT); -} - -static void sfn4112f_set_id_led(struct efx_nic *efx, bool state) -{ - xfp_set_led(efx, SFN4112F_LINK_LED, - state ? QUAKE_LED_ON : QUAKE_LED_OFF); -} - -static int sfn4112f_check_hw(struct efx_nic *efx) -{ - /* Mask out unused sensors */ - return efx_check_lm87(efx, ~0x48); -} - -static int sfn4112f_init(struct efx_nic *efx) -{ - int rc = efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs); - if (rc) - return rc; - efx->board_info.monitor = sfn4112f_check_hw; - efx->board_info.init_leds = sfn4112f_init_leds; - efx->board_info.set_id_led = sfn4112f_set_id_led; - efx->board_info.blink = board_blink; - efx->board_info.fini = efx_fini_lm87; - return 0; -} - -/* This will get expanded as board-specific details get moved out of the - * PHY drivers. */ -struct efx_board_data { - enum efx_board_type type; - const char *ref_model; - const char *gen_type; - int (*init) (struct efx_nic *nic); -}; - - -static struct efx_board_data board_data[] = { - { EFX_BOARD_SFE4001, "SFE4001", "10GBASE-T adapter", sfe4001_init }, - { EFX_BOARD_SFE4002, "SFE4002", "XFP adapter", sfe4002_init }, - { EFX_BOARD_SFN4111T, "SFN4111T", "100/1000/10GBASE-T adapter", - sfn4111t_init }, - { EFX_BOARD_SFN4112F, "SFN4112F", "SFP+ adapter", - sfn4112f_init }, -}; - -void efx_set_board_info(struct efx_nic *efx, u16 revision_info) -{ - struct efx_board_data *data = NULL; - int i; - - efx->board_info.type = BOARD_TYPE(revision_info); - efx->board_info.major = BOARD_MAJOR(revision_info); - efx->board_info.minor = BOARD_MINOR(revision_info); - - for (i = 0; i < ARRAY_SIZE(board_data); i++) - if (board_data[i].type == efx->board_info.type) - data = &board_data[i]; - - if (data) { - EFX_INFO(efx, "board is %s rev %c%d\n", - (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC) - ? data->ref_model : data->gen_type, - 'A' + efx->board_info.major, efx->board_info.minor); - efx->board_info.init = data->init; - } else { - EFX_ERR(efx, "unknown board type %d\n", efx->board_info.type); - } -} diff --git a/drivers/net/sfc/boards.h b/drivers/net/sfc/boards.h deleted file mode 100644 index 44942de0e080..000000000000 --- a/drivers/net/sfc/boards.h +++ /dev/null @@ -1,28 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#ifndef EFX_BOARDS_H -#define EFX_BOARDS_H - -/* Board IDs (must fit in 8 bits) */ -enum efx_board_type { - EFX_BOARD_SFE4001 = 1, - EFX_BOARD_SFE4002 = 2, - EFX_BOARD_SFN4111T = 0x51, - EFX_BOARD_SFN4112F = 0x52, -}; - -extern void efx_set_board_info(struct efx_nic *efx, u16 revision_info); - -/* SFE4001 (10GBASE-T) */ -extern int sfe4001_init(struct efx_nic *efx); -/* SFN4111T (100/1000/10GBASE-T) */ -extern int sfn4111t_init(struct efx_nic *efx); - -#endif diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index cc4b2f99989d..f983e3b507cc 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2005-2008 Solarflare Communications Inc. + * Copyright 2005-2009 Solarflare Communications Inc. * * 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 @@ -21,12 +21,73 @@ #include <linux/ethtool.h> #include <linux/topology.h> #include "net_driver.h" -#include "ethtool.h" -#include "tx.h" -#include "rx.h" #include "efx.h" #include "mdio_10g.h" -#include "falcon.h" +#include "nic.h" + +#include "mcdi.h" + +/************************************************************************** + * + * Type name strings + * + ************************************************************************** + */ + +/* Loopback mode names (see LOOPBACK_MODE()) */ +const unsigned int efx_loopback_mode_max = LOOPBACK_MAX; +const char *efx_loopback_mode_names[] = { + [LOOPBACK_NONE] = "NONE", + [LOOPBACK_DATA] = "DATAPATH", + [LOOPBACK_GMAC] = "GMAC", + [LOOPBACK_XGMII] = "XGMII", + [LOOPBACK_XGXS] = "XGXS", + [LOOPBACK_XAUI] = "XAUI", + [LOOPBACK_GMII] = "GMII", + [LOOPBACK_SGMII] = "SGMII", + [LOOPBACK_XGBR] = "XGBR", + [LOOPBACK_XFI] = "XFI", + [LOOPBACK_XAUI_FAR] = "XAUI_FAR", + [LOOPBACK_GMII_FAR] = "GMII_FAR", + [LOOPBACK_SGMII_FAR] = "SGMII_FAR", + [LOOPBACK_XFI_FAR] = "XFI_FAR", + [LOOPBACK_GPHY] = "GPHY", + [LOOPBACK_PHYXS] = "PHYXS", + [LOOPBACK_PCS] = "PCS", + [LOOPBACK_PMAPMD] = "PMA/PMD", + [LOOPBACK_XPORT] = "XPORT", + [LOOPBACK_XGMII_WS] = "XGMII_WS", + [LOOPBACK_XAUI_WS] = "XAUI_WS", + [LOOPBACK_XAUI_WS_FAR] = "XAUI_WS_FAR", + [LOOPBACK_XAUI_WS_NEAR] = "XAUI_WS_NEAR", + [LOOPBACK_GMII_WS] = "GMII_WS", + [LOOPBACK_XFI_WS] = "XFI_WS", + [LOOPBACK_XFI_WS_FAR] = "XFI_WS_FAR", + [LOOPBACK_PHYXS_WS] = "PHYXS_WS", +}; + +/* Interrupt mode names (see INT_MODE())) */ +const unsigned int efx_interrupt_mode_max = EFX_INT_MODE_MAX; +const char *efx_interrupt_mode_names[] = { + [EFX_INT_MODE_MSIX] = "MSI-X", + [EFX_INT_MODE_MSI] = "MSI", + [EFX_INT_MODE_LEGACY] = "legacy", +}; + +const unsigned int efx_reset_type_max = RESET_TYPE_MAX; +const char *efx_reset_type_names[] = { + [RESET_TYPE_INVISIBLE] = "INVISIBLE", + [RESET_TYPE_ALL] = "ALL", + [RESET_TYPE_WORLD] = "WORLD", + [RESET_TYPE_DISABLE] = "DISABLE", + [RESET_TYPE_TX_WATCHDOG] = "TX_WATCHDOG", + [RESET_TYPE_INT_ERROR] = "INT_ERROR", + [RESET_TYPE_RX_RECOVERY] = "RX_RECOVERY", + [RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH", + [RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH", + [RESET_TYPE_TX_SKIP] = "TX_SKIP", + [RESET_TYPE_MC_FAILURE] = "MC_FAILURE", +}; #define EFX_MAX_MTU (9 * 1024) @@ -145,7 +206,8 @@ static void efx_fini_channels(struct efx_nic *efx); #define EFX_ASSERT_RESET_SERIALISED(efx) \ do { \ - if (efx->state == STATE_RUNNING) \ + if ((efx->state == STATE_RUNNING) || \ + (efx->state == STATE_DISABLED)) \ ASSERT_RTNL(); \ } while (0) @@ -171,7 +233,7 @@ static int efx_process_channel(struct efx_channel *channel, int rx_quota) !channel->enabled)) return 0; - rx_packets = falcon_process_eventq(channel, rx_quota); + rx_packets = efx_nic_process_eventq(channel, rx_quota); if (rx_packets == 0) return 0; @@ -203,7 +265,7 @@ static inline void efx_channel_processed(struct efx_channel *channel) channel->work_pending = false; smp_wmb(); - falcon_eventq_read_ack(channel); + efx_nic_eventq_read_ack(channel); } /* NAPI poll handler @@ -228,26 +290,20 @@ static int efx_poll(struct napi_struct *napi, int budget) if (channel->used_flags & EFX_USED_BY_RX && efx->irq_rx_adaptive && unlikely(++channel->irq_count == 1000)) { - unsigned old_irq_moderation = channel->irq_moderation; - if (unlikely(channel->irq_mod_score < irq_adapt_low_thresh)) { - channel->irq_moderation = - max_t(int, - channel->irq_moderation - - FALCON_IRQ_MOD_RESOLUTION, - FALCON_IRQ_MOD_RESOLUTION); + if (channel->irq_moderation > 1) { + channel->irq_moderation -= 1; + efx->type->push_irq_moderation(channel); + } } else if (unlikely(channel->irq_mod_score > irq_adapt_high_thresh)) { - channel->irq_moderation = - min(channel->irq_moderation + - FALCON_IRQ_MOD_RESOLUTION, - efx->irq_rx_moderation); + if (channel->irq_moderation < + efx->irq_rx_moderation) { + channel->irq_moderation += 1; + efx->type->push_irq_moderation(channel); + } } - - if (channel->irq_moderation != old_irq_moderation) - falcon_set_int_moderation(channel); - channel->irq_count = 0; channel->irq_mod_score = 0; } @@ -280,7 +336,7 @@ void efx_process_channel_now(struct efx_channel *channel) BUG_ON(!channel->enabled); /* Disable interrupts and wait for ISRs to complete */ - falcon_disable_interrupts(efx); + efx_nic_disable_interrupts(efx); if (efx->legacy_irq) synchronize_irq(efx->legacy_irq); if (channel->irq) @@ -290,14 +346,14 @@ void efx_process_channel_now(struct efx_channel *channel) napi_disable(&channel->napi_str); /* Poll the channel */ - efx_process_channel(channel, efx->type->evq_size); + efx_process_channel(channel, EFX_EVQ_SIZE); /* Ack the eventq. This may cause an interrupt to be generated * when they are reenabled */ efx_channel_processed(channel); napi_enable(&channel->napi_str); - falcon_enable_interrupts(efx); + efx_nic_enable_interrupts(efx); } /* Create event queue @@ -309,7 +365,7 @@ static int efx_probe_eventq(struct efx_channel *channel) { EFX_LOG(channel->efx, "chan %d create event queue\n", channel->channel); - return falcon_probe_eventq(channel); + return efx_nic_probe_eventq(channel); } /* Prepare channel's event queue */ @@ -319,21 +375,21 @@ static void efx_init_eventq(struct efx_channel *channel) channel->eventq_read_ptr = 0; - falcon_init_eventq(channel); + efx_nic_init_eventq(channel); } static void efx_fini_eventq(struct efx_channel *channel) { EFX_LOG(channel->efx, "chan %d fini event queue\n", channel->channel); - falcon_fini_eventq(channel); + efx_nic_fini_eventq(channel); } static void efx_remove_eventq(struct efx_channel *channel) { EFX_LOG(channel->efx, "chan %d remove event queue\n", channel->channel); - falcon_remove_eventq(channel); + efx_nic_remove_eventq(channel); } /************************************************************************** @@ -499,7 +555,7 @@ static void efx_fini_channels(struct efx_nic *efx) EFX_ASSERT_RESET_SERIALISED(efx); BUG_ON(efx->port_enabled); - rc = falcon_flush_queues(efx); + rc = efx_nic_flush_queues(efx); if (rc) EFX_ERR(efx, "failed to flush queues\n"); else @@ -547,8 +603,10 @@ void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay) * netif_carrier_on/off) of the link status, and also maintains the * link status's stop on the port's TX queue. */ -static void efx_link_status_changed(struct efx_nic *efx) +void efx_link_status_changed(struct efx_nic *efx) { + struct efx_link_state *link_state = &efx->link_state; + /* SFC Bug 5356: A net_dev notifier is registered, so we must ensure * that no events are triggered between unregister_netdev() and the * driver unloading. A more general condition is that NETDEV_CHANGE @@ -561,19 +619,19 @@ static void efx_link_status_changed(struct efx_nic *efx) return; } - if (efx->link_up != netif_carrier_ok(efx->net_dev)) { + if (link_state->up != netif_carrier_ok(efx->net_dev)) { efx->n_link_state_changes++; - if (efx->link_up) + if (link_state->up) netif_carrier_on(efx->net_dev); else netif_carrier_off(efx->net_dev); } /* Status message for kernel log */ - if (efx->link_up) { + if (link_state->up) { EFX_INFO(efx, "link up at %uMbps %s-duplex (MTU %d)%s\n", - efx->link_speed, efx->link_fd ? "full" : "half", + link_state->speed, link_state->fd ? "full" : "half", efx->net_dev->mtu, (efx->promiscuous ? " [PROMISC]" : "")); } else { @@ -582,16 +640,49 @@ static void efx_link_status_changed(struct efx_nic *efx) } +void efx_link_set_advertising(struct efx_nic *efx, u32 advertising) +{ + efx->link_advertising = advertising; + if (advertising) { + if (advertising & ADVERTISED_Pause) + efx->wanted_fc |= (EFX_FC_TX | EFX_FC_RX); + else + efx->wanted_fc &= ~(EFX_FC_TX | EFX_FC_RX); + if (advertising & ADVERTISED_Asym_Pause) + efx->wanted_fc ^= EFX_FC_TX; + } +} + +void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type wanted_fc) +{ + efx->wanted_fc = wanted_fc; + if (efx->link_advertising) { + if (wanted_fc & EFX_FC_RX) + efx->link_advertising |= (ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + else + efx->link_advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + if (wanted_fc & EFX_FC_TX) + efx->link_advertising ^= ADVERTISED_Asym_Pause; + } +} + static void efx_fini_port(struct efx_nic *efx); -/* This call reinitialises the MAC to pick up new PHY settings. The - * caller must hold the mac_lock */ -void __efx_reconfigure_port(struct efx_nic *efx) +/* Push loopback/power/transmit disable settings to the PHY, and reconfigure + * the MAC appropriately. All other PHY configuration changes are pushed + * through phy_op->set_settings(), and pushed asynchronously to the MAC + * through efx_monitor(). + * + * Callers must hold the mac_lock + */ +int __efx_reconfigure_port(struct efx_nic *efx) { - WARN_ON(!mutex_is_locked(&efx->mac_lock)); + enum efx_phy_mode phy_mode; + int rc; - EFX_LOG(efx, "reconfiguring MAC from PHY settings on CPU %d\n", - raw_smp_processor_id()); + WARN_ON(!mutex_is_locked(&efx->mac_lock)); /* Serialise the promiscuous flag with efx_set_multicast_list. */ if (efx_dev_registered(efx)) { @@ -599,61 +690,48 @@ void __efx_reconfigure_port(struct efx_nic *efx) netif_addr_unlock_bh(efx->net_dev); } - falcon_deconfigure_mac_wrapper(efx); - - /* Reconfigure the PHY, disabling transmit in mac level loopback. */ + /* Disable PHY transmit in mac level loopbacks */ + phy_mode = efx->phy_mode; if (LOOPBACK_INTERNAL(efx)) efx->phy_mode |= PHY_MODE_TX_DISABLED; else efx->phy_mode &= ~PHY_MODE_TX_DISABLED; - efx->phy_op->reconfigure(efx); - if (falcon_switch_mac(efx)) - goto fail; + rc = efx->type->reconfigure_port(efx); - efx->mac_op->reconfigure(efx); - - /* Inform kernel of loss/gain of carrier */ - efx_link_status_changed(efx); - return; + if (rc) + efx->phy_mode = phy_mode; -fail: - EFX_ERR(efx, "failed to reconfigure MAC\n"); - efx->port_enabled = false; - efx_fini_port(efx); + return rc; } /* Reinitialise the MAC to pick up new PHY settings, even if the port is * disabled. */ -void efx_reconfigure_port(struct efx_nic *efx) +int efx_reconfigure_port(struct efx_nic *efx) { + int rc; + EFX_ASSERT_RESET_SERIALISED(efx); mutex_lock(&efx->mac_lock); - __efx_reconfigure_port(efx); + rc = __efx_reconfigure_port(efx); mutex_unlock(&efx->mac_lock); -} - -/* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all() - * we don't efx_reconfigure_port() if the port is disabled. Care is taken - * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */ -static void efx_phy_work(struct work_struct *data) -{ - struct efx_nic *efx = container_of(data, struct efx_nic, phy_work); - mutex_lock(&efx->mac_lock); - if (efx->port_enabled) - __efx_reconfigure_port(efx); - mutex_unlock(&efx->mac_lock); + return rc; } +/* Asynchronous work item for changing MAC promiscuity and multicast + * hash. Avoid a drain/rx_ingress enable by reconfiguring the current + * MAC directly. */ static void efx_mac_work(struct work_struct *data) { struct efx_nic *efx = container_of(data, struct efx_nic, mac_work); mutex_lock(&efx->mac_lock); - if (efx->port_enabled) - efx->mac_op->irq(efx); + if (efx->port_enabled) { + efx->type->push_multicast_hash(efx); + efx->mac_op->reconfigure(efx); + } mutex_unlock(&efx->mac_lock); } @@ -663,8 +741,8 @@ static int efx_probe_port(struct efx_nic *efx) EFX_LOG(efx, "create port\n"); - /* Connect up MAC/PHY operations table and read MAC address */ - rc = falcon_probe_port(efx); + /* Connect up MAC/PHY operations table */ + rc = efx->type->probe_port(efx); if (rc) goto err; @@ -699,29 +777,33 @@ static int efx_init_port(struct efx_nic *efx) EFX_LOG(efx, "init port\n"); - rc = efx->phy_op->init(efx); - if (rc) - return rc; mutex_lock(&efx->mac_lock); - efx->phy_op->reconfigure(efx); - rc = falcon_switch_mac(efx); - mutex_unlock(&efx->mac_lock); + + rc = efx->phy_op->init(efx); if (rc) - goto fail; - efx->mac_op->reconfigure(efx); + goto fail1; efx->port_initialized = true; - efx_stats_enable(efx); + + /* Reconfigure the MAC before creating dma queues (required for + * Falcon/A1 where RX_INGR_EN/TX_DRAIN_EN isn't supported) */ + efx->mac_op->reconfigure(efx); + + /* Ensure the PHY advertises the correct flow control settings */ + rc = efx->phy_op->reconfigure(efx); + if (rc) + goto fail2; + + mutex_unlock(&efx->mac_lock); return 0; -fail: +fail2: efx->phy_op->fini(efx); +fail1: + mutex_unlock(&efx->mac_lock); return rc; } -/* Allow efx_reconfigure_port() to be scheduled, and close the window - * between efx_stop_port and efx_flush_all whereby a previously scheduled - * efx_phy_work()/efx_mac_work() may have been cancelled */ static void efx_start_port(struct efx_nic *efx) { EFX_LOG(efx, "start port\n"); @@ -729,15 +811,16 @@ static void efx_start_port(struct efx_nic *efx) mutex_lock(&efx->mac_lock); efx->port_enabled = true; - __efx_reconfigure_port(efx); - efx->mac_op->irq(efx); + + /* efx_mac_work() might have been scheduled after efx_stop_port(), + * and then cancelled by efx_flush_all() */ + efx->type->push_multicast_hash(efx); + efx->mac_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); } -/* Prevent efx_phy_work, efx_mac_work, and efx_monitor() from executing, - * and efx_set_multicast_list() from scheduling efx_phy_work. efx_phy_work - * and efx_mac_work may still be scheduled via NAPI processing until - * efx_flush_all() is called */ +/* Prevent efx_mac_work() and efx_monitor() from working */ static void efx_stop_port(struct efx_nic *efx) { EFX_LOG(efx, "stop port\n"); @@ -760,11 +843,10 @@ static void efx_fini_port(struct efx_nic *efx) if (!efx->port_initialized) return; - efx_stats_disable(efx); efx->phy_op->fini(efx); efx->port_initialized = false; - efx->link_up = false; + efx->link_state.up = false; efx_link_status_changed(efx); } @@ -772,7 +854,7 @@ static void efx_remove_port(struct efx_nic *efx) { EFX_LOG(efx, "destroying port\n"); - falcon_remove_port(efx); + efx->type->remove_port(efx); } /************************************************************************** @@ -824,9 +906,8 @@ static int efx_init_io(struct efx_nic *efx) goto fail2; } - efx->membase_phys = pci_resource_start(efx->pci_dev, - efx->type->mem_bar); - rc = pci_request_region(pci_dev, efx->type->mem_bar, "sfc"); + efx->membase_phys = pci_resource_start(efx->pci_dev, EFX_MEM_BAR); + rc = pci_request_region(pci_dev, EFX_MEM_BAR, "sfc"); if (rc) { EFX_ERR(efx, "request for memory BAR failed\n"); rc = -EIO; @@ -835,21 +916,20 @@ static int efx_init_io(struct efx_nic *efx) efx->membase = ioremap_nocache(efx->membase_phys, efx->type->mem_map_size); if (!efx->membase) { - EFX_ERR(efx, "could not map memory BAR %d at %llx+%x\n", - efx->type->mem_bar, + EFX_ERR(efx, "could not map memory BAR at %llx+%x\n", (unsigned long long)efx->membase_phys, efx->type->mem_map_size); rc = -ENOMEM; goto fail4; } - EFX_LOG(efx, "memory BAR %u at %llx+%x (virtual %p)\n", - efx->type->mem_bar, (unsigned long long)efx->membase_phys, + EFX_LOG(efx, "memory BAR at %llx+%x (virtual %p)\n", + (unsigned long long)efx->membase_phys, efx->type->mem_map_size, efx->membase); return 0; fail4: - pci_release_region(efx->pci_dev, efx->type->mem_bar); + pci_release_region(efx->pci_dev, EFX_MEM_BAR); fail3: efx->membase_phys = 0; fail2: @@ -868,7 +948,7 @@ static void efx_fini_io(struct efx_nic *efx) } if (efx->membase_phys) { - pci_release_region(efx->pci_dev, efx->type->mem_bar); + pci_release_region(efx->pci_dev, EFX_MEM_BAR); efx->membase_phys = 0; } @@ -1011,7 +1091,7 @@ static int efx_probe_nic(struct efx_nic *efx) EFX_LOG(efx, "creating NIC\n"); /* Carry out hardware-type specific initialisation */ - rc = falcon_probe_nic(efx); + rc = efx->type->probe(efx); if (rc) return rc; @@ -1032,7 +1112,7 @@ static void efx_remove_nic(struct efx_nic *efx) EFX_LOG(efx, "destroying NIC\n"); efx_remove_interrupts(efx); - falcon_remove_nic(efx); + efx->type->remove(efx); } /************************************************************************** @@ -1112,12 +1192,31 @@ static void efx_start_all(struct efx_nic *efx) efx_for_each_channel(channel, efx) efx_start_channel(channel); - falcon_enable_interrupts(efx); - - /* Start hardware monitor if we're in RUNNING */ - if (efx->state == STATE_RUNNING) + efx_nic_enable_interrupts(efx); + + /* Switch to event based MCDI completions after enabling interrupts. + * If a reset has been scheduled, then we need to stay in polled mode. + * Rather than serialising efx_mcdi_mode_event() [which sleeps] and + * reset_pending [modified from an atomic context], we instead guarantee + * that efx_mcdi_mode_poll() isn't reverted erroneously */ + efx_mcdi_mode_event(efx); + if (efx->reset_pending != RESET_TYPE_NONE) + efx_mcdi_mode_poll(efx); + + /* Start the hardware monitor if there is one. Otherwise (we're link + * event driven), we have to poll the PHY because after an event queue + * flush, we could have a missed a link state change */ + if (efx->type->monitor != NULL) { queue_delayed_work(efx->workqueue, &efx->monitor_work, efx_monitor_interval); + } else { + mutex_lock(&efx->mac_lock); + if (efx->phy_op->poll(efx)) + efx_link_status_changed(efx); + mutex_unlock(&efx->mac_lock); + } + + efx->type->start_stats(efx); } /* Flush all delayed work. Should only be called when no more delayed work @@ -1136,8 +1235,6 @@ static void efx_flush_all(struct efx_nic *efx) /* Stop scheduled port reconfigurations */ cancel_work_sync(&efx->mac_work); - cancel_work_sync(&efx->phy_work); - } /* Quiesce hardware and software without bringing the link down. @@ -1155,8 +1252,13 @@ static void efx_stop_all(struct efx_nic *efx) if (!efx->port_enabled) return; + efx->type->stop_stats(efx); + + /* Switch to MCDI polling on Siena before disabling interrupts */ + efx_mcdi_mode_poll(efx); + /* Disable interrupts and wait for ISR to complete */ - falcon_disable_interrupts(efx); + efx_nic_disable_interrupts(efx); if (efx->legacy_irq) synchronize_irq(efx->legacy_irq); efx_for_each_channel(channel, efx) { @@ -1173,15 +1275,9 @@ static void efx_stop_all(struct efx_nic *efx) * window to loose phy events */ efx_stop_port(efx); - /* Flush efx_phy_work, efx_mac_work, refill_workqueue, monitor_work */ + /* Flush efx_mac_work(), refill_workqueue, monitor_work */ efx_flush_all(efx); - /* Isolate the MAC from the TX and RX engines, so that queue - * flushes will complete in a timely fashion. */ - falcon_deconfigure_mac_wrapper(efx); - msleep(10); /* Let the Rx FIFO drain */ - falcon_drain_tx_fifo(efx); - /* Stop the kernel transmit interface late, so the watchdog * timer isn't ticking over the flush */ if (efx_dev_registered(efx)) { @@ -1201,41 +1297,39 @@ static void efx_remove_all(struct efx_nic *efx) efx_remove_nic(efx); } -/* A convinience function to safely flush all the queues */ -void efx_flush_queues(struct efx_nic *efx) -{ - EFX_ASSERT_RESET_SERIALISED(efx); - - efx_stop_all(efx); - - efx_fini_channels(efx); - efx_init_channels(efx); - - efx_start_all(efx); -} - /************************************************************************** * * Interrupt moderation * **************************************************************************/ +static unsigned irq_mod_ticks(int usecs, int resolution) +{ + if (usecs <= 0) + return 0; /* cannot receive interrupts ahead of time :-) */ + if (usecs < resolution) + return 1; /* never round down to 0 */ + return usecs / resolution; +} + /* Set interrupt moderation parameters */ void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs, bool rx_adaptive) { struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; + unsigned tx_ticks = irq_mod_ticks(tx_usecs, EFX_IRQ_MOD_RESOLUTION); + unsigned rx_ticks = irq_mod_ticks(rx_usecs, EFX_IRQ_MOD_RESOLUTION); EFX_ASSERT_RESET_SERIALISED(efx); efx_for_each_tx_queue(tx_queue, efx) - tx_queue->channel->irq_moderation = tx_usecs; + tx_queue->channel->irq_moderation = tx_ticks; efx->irq_rx_adaptive = rx_adaptive; - efx->irq_rx_moderation = rx_usecs; + efx->irq_rx_moderation = rx_ticks; efx_for_each_rx_queue(rx_queue, efx) - rx_queue->channel->irq_moderation = rx_usecs; + rx_queue->channel->irq_moderation = rx_ticks; } /************************************************************************** @@ -1250,10 +1344,10 @@ static void efx_monitor(struct work_struct *data) { struct efx_nic *efx = container_of(data, struct efx_nic, monitor_work.work); - int rc; EFX_TRACE(efx, "hardware monitor executing on CPU %d\n", raw_smp_processor_id()); + BUG_ON(efx->type->monitor == NULL); /* If the mac_lock is already held then it is likely a port * reconfiguration is already in place, which will likely do @@ -1262,15 +1356,7 @@ static void efx_monitor(struct work_struct *data) goto out_requeue; if (!efx->port_enabled) goto out_unlock; - rc = efx->board_info.monitor(efx); - if (rc) { - EFX_ERR(efx, "Board sensor %s; shutting down PHY\n", - (rc == -ERANGE) ? "reported fault" : "failed"); - efx->phy_mode |= PHY_MODE_LOW_POWER; - falcon_sim_phy_event(efx); - } - efx->phy_op->poll(efx); - efx->mac_op->poll(efx); + efx->type->monitor(efx); out_unlock: mutex_unlock(&efx->mac_lock); @@ -1374,6 +1460,12 @@ static int efx_net_open(struct net_device *net_dev) return -EIO; if (efx->phy_mode & PHY_MODE_SPECIAL) return -EBUSY; + if (efx_mcdi_poll_reboot(efx) && efx_reset(efx, RESET_TYPE_ALL)) + return -EIO; + + /* Notify the kernel of the link state polled during driver load, + * before the monitor starts running */ + efx_link_status_changed(efx); efx_start_all(efx); return 0; @@ -1400,20 +1492,6 @@ static int efx_net_stop(struct net_device *net_dev) return 0; } -void efx_stats_disable(struct efx_nic *efx) -{ - spin_lock(&efx->stats_lock); - ++efx->stats_disable_count; - spin_unlock(&efx->stats_lock); -} - -void efx_stats_enable(struct efx_nic *efx) -{ - spin_lock(&efx->stats_lock); - --efx->stats_disable_count; - spin_unlock(&efx->stats_lock); -} - /* Context: process, dev_base_lock or RTNL held, non-blocking. */ static struct net_device_stats *efx_net_stats(struct net_device *net_dev) { @@ -1421,17 +1499,9 @@ static struct net_device_stats *efx_net_stats(struct net_device *net_dev) struct efx_mac_stats *mac_stats = &efx->mac_stats; struct net_device_stats *stats = &net_dev->stats; - /* Update stats if possible, but do not wait if another thread - * is updating them or if MAC stats fetches are temporarily - * disabled; slightly stale stats are acceptable. - */ - if (!spin_trylock(&efx->stats_lock)) - return stats; - if (!efx->stats_disable_count) { - efx->mac_op->update_stats(efx); - falcon_update_nic_stats(efx); - } - spin_unlock(&efx->stats_lock); + spin_lock_bh(&efx->stats_lock); + efx->type->update_stats(efx); + spin_unlock_bh(&efx->stats_lock); stats->rx_packets = mac_stats->rx_packets; stats->tx_packets = mac_stats->tx_packets; @@ -1490,7 +1560,14 @@ static int efx_change_mtu(struct net_device *net_dev, int new_mtu) EFX_LOG(efx, "changing MTU to %d\n", new_mtu); efx_fini_channels(efx); + + mutex_lock(&efx->mac_lock); + /* Reconfigure the MAC before enabling the dma queues so that + * the RX buffers don't overflow */ net_dev->mtu = new_mtu; + efx->mac_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); + efx_init_channels(efx); efx_start_all(efx); @@ -1514,7 +1591,9 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data) memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len); /* Reconfigure the MAC */ - efx_reconfigure_port(efx); + mutex_lock(&efx->mac_lock); + efx->mac_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); return 0; } @@ -1525,16 +1604,14 @@ static void efx_set_multicast_list(struct net_device *net_dev) struct efx_nic *efx = netdev_priv(net_dev); struct dev_mc_list *mc_list = net_dev->mc_list; union efx_multicast_hash *mc_hash = &efx->multicast_hash; - bool promiscuous = !!(net_dev->flags & IFF_PROMISC); - bool changed = (efx->promiscuous != promiscuous); u32 crc; int bit; int i; - efx->promiscuous = promiscuous; + efx->promiscuous = !!(net_dev->flags & IFF_PROMISC); /* Build multicast hash table */ - if (promiscuous || (net_dev->flags & IFF_ALLMULTI)) { + if (efx->promiscuous || (net_dev->flags & IFF_ALLMULTI)) { memset(mc_hash, 0xff, sizeof(*mc_hash)); } else { memset(mc_hash, 0x00, sizeof(*mc_hash)); @@ -1544,17 +1621,17 @@ static void efx_set_multicast_list(struct net_device *net_dev) set_bit_le(bit, mc_hash->byte); mc_list = mc_list->next; } - } - - if (!efx->port_enabled) - /* Delay pushing settings until efx_start_port() */ - return; - if (changed) - queue_work(efx->workqueue, &efx->phy_work); + /* Broadcast packets go through the multicast hash filter. + * ether_crc_le() of the broadcast address is 0xbe2612ff + * so we always add bit 0xff to the mask. + */ + set_bit_le(0xff, mc_hash->byte); + } - /* Create and activate new global multicast hash table */ - falcon_set_multicast_hash(efx); + if (efx->port_enabled) + queue_work(efx->workqueue, &efx->mac_work); + /* Otherwise efx_start_port() will do this */ } static const struct net_device_ops efx_netdev_ops = { @@ -1683,21 +1760,18 @@ static void efx_unregister_netdev(struct efx_nic *efx) /* Tears down the entire software state and most of the hardware state * before reset. */ -void efx_reset_down(struct efx_nic *efx, enum reset_type method, - struct ethtool_cmd *ecmd) +void efx_reset_down(struct efx_nic *efx, enum reset_type method) { EFX_ASSERT_RESET_SERIALISED(efx); - efx_stats_disable(efx); efx_stop_all(efx); mutex_lock(&efx->mac_lock); mutex_lock(&efx->spi_lock); - efx->phy_op->get_settings(efx, ecmd); - efx_fini_channels(efx); if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) efx->phy_op->fini(efx); + efx->type->fini(efx); } /* This function will always ensure that the locks acquired in @@ -1705,79 +1779,67 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method, * that we were unable to reinitialise the hardware, and the * driver should be disabled. If ok is false, then the rx and tx * engines are not restarted, pending a RESET_DISABLE. */ -int efx_reset_up(struct efx_nic *efx, enum reset_type method, - struct ethtool_cmd *ecmd, bool ok) +int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok) { int rc; EFX_ASSERT_RESET_SERIALISED(efx); - rc = falcon_init_nic(efx); + rc = efx->type->init(efx); if (rc) { EFX_ERR(efx, "failed to initialise NIC\n"); - ok = false; + goto fail; } + if (!ok) + goto fail; + if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) { - if (ok) { - rc = efx->phy_op->init(efx); - if (rc) - ok = false; - } - if (!ok) - efx->port_initialized = false; + rc = efx->phy_op->init(efx); + if (rc) + goto fail; + if (efx->phy_op->reconfigure(efx)) + EFX_ERR(efx, "could not restore PHY settings\n"); } - if (ok) { - efx_init_channels(efx); + efx->mac_op->reconfigure(efx); - if (efx->phy_op->set_settings(efx, ecmd)) - EFX_ERR(efx, "could not restore PHY settings\n"); - } + efx_init_channels(efx); + + mutex_unlock(&efx->spi_lock); + mutex_unlock(&efx->mac_lock); + + efx_start_all(efx); + + return 0; + +fail: + efx->port_initialized = false; mutex_unlock(&efx->spi_lock); mutex_unlock(&efx->mac_lock); - if (ok) { - efx_start_all(efx); - efx_stats_enable(efx); - } return rc; } -/* Reset the NIC as transparently as possible. Do not reset the PHY - * Note that the reset may fail, in which case the card will be left - * in a most-probably-unusable state. +/* Reset the NIC using the specified method. Note that the reset may + * fail, in which case the card will be left in an unusable state. * - * This function will sleep. You cannot reset from within an atomic - * state; use efx_schedule_reset() instead. - * - * Grabs the rtnl_lock. + * Caller must hold the rtnl_lock. */ -static int efx_reset(struct efx_nic *efx) +int efx_reset(struct efx_nic *efx, enum reset_type method) { - struct ethtool_cmd ecmd; - enum reset_type method = efx->reset_pending; - int rc = 0; + int rc, rc2; + bool disabled; - /* Serialise with kernel interfaces */ - rtnl_lock(); + EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method)); - /* If we're not RUNNING then don't reset. Leave the reset_pending - * flag set so that efx_pci_probe_main will be retried */ - if (efx->state != STATE_RUNNING) { - EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); - goto out_unlock; - } + efx_reset_down(efx, method); - EFX_INFO(efx, "resetting (%d)\n", method); - - efx_reset_down(efx, method, &ecmd); - - rc = falcon_reset_hw(efx, method); + rc = efx->type->reset(efx, method); if (rc) { EFX_ERR(efx, "failed to reset hardware\n"); - goto out_disable; + goto out; } /* Allow resets to be rescheduled. */ @@ -1789,25 +1851,22 @@ static int efx_reset(struct efx_nic *efx) * can respond to requests. */ pci_set_master(efx->pci_dev); +out: /* Leave device stopped if necessary */ - if (method == RESET_TYPE_DISABLE) { - efx_reset_up(efx, method, &ecmd, false); - rc = -EIO; - } else { - rc = efx_reset_up(efx, method, &ecmd, true); + disabled = rc || method == RESET_TYPE_DISABLE; + rc2 = efx_reset_up(efx, method, !disabled); + if (rc2) { + disabled = true; + if (!rc) + rc = rc2; } -out_disable: - if (rc) { + if (disabled) { EFX_ERR(efx, "has been disabled\n"); efx->state = STATE_DISABLED; - dev_close(efx->net_dev); } else { EFX_LOG(efx, "reset complete\n"); } - -out_unlock: - rtnl_unlock(); return rc; } @@ -1816,9 +1875,19 @@ out_unlock: */ static void efx_reset_work(struct work_struct *data) { - struct efx_nic *nic = container_of(data, struct efx_nic, reset_work); + struct efx_nic *efx = container_of(data, struct efx_nic, reset_work); + + /* If we're not RUNNING then don't reset. Leave the reset_pending + * flag set so that efx_pci_probe_main will be retried */ + if (efx->state != STATE_RUNNING) { + EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); + return; + } - efx_reset(nic); + rtnl_lock(); + if (efx_reset(efx, efx->reset_pending)) + dev_close(efx->net_dev); + rtnl_unlock(); } void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) @@ -1843,18 +1912,24 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) case RESET_TYPE_TX_SKIP: method = RESET_TYPE_INVISIBLE; break; + case RESET_TYPE_MC_FAILURE: default: method = RESET_TYPE_ALL; break; } if (method != type) - EFX_LOG(efx, "scheduling reset (%d:%d)\n", type, method); + EFX_LOG(efx, "scheduling %s reset for %s\n", + RESET_TYPE(method), RESET_TYPE(type)); else - EFX_LOG(efx, "scheduling reset (%d)\n", method); + EFX_LOG(efx, "scheduling %s reset\n", RESET_TYPE(method)); efx->reset_pending = method; + /* efx_process_channel() will no longer read events once a + * reset is scheduled. So switch back to poll'd MCDI completions. */ + efx_mcdi_mode_poll(efx); + queue_work(reset_workqueue, &efx->reset_work); } @@ -1867,15 +1942,19 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) /* PCI device ID table */ static struct pci_device_id efx_pci_table[] __devinitdata = { {PCI_DEVICE(EFX_VENDID_SFC, FALCON_A_P_DEVID), - .driver_data = (unsigned long) &falcon_a_nic_type}, + .driver_data = (unsigned long) &falcon_a1_nic_type}, {PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID), - .driver_data = (unsigned long) &falcon_b_nic_type}, + .driver_data = (unsigned long) &falcon_b0_nic_type}, + {PCI_DEVICE(EFX_VENDID_SFC, BETHPAGE_A_P_DEVID), + .driver_data = (unsigned long) &siena_a0_nic_type}, + {PCI_DEVICE(EFX_VENDID_SFC, SIENA_A_P_DEVID), + .driver_data = (unsigned long) &siena_a0_nic_type}, {0} /* end of list */ }; /************************************************************************** * - * Dummy PHY/MAC/Board operations + * Dummy PHY/MAC operations * * Can be used for some unimplemented operations * Needed so all function pointers are valid and do not have to be tested @@ -1887,29 +1966,19 @@ int efx_port_dummy_op_int(struct efx_nic *efx) return 0; } void efx_port_dummy_op_void(struct efx_nic *efx) {} -void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink) {} - -static struct efx_mac_operations efx_dummy_mac_operations = { - .reconfigure = efx_port_dummy_op_void, - .poll = efx_port_dummy_op_void, - .irq = efx_port_dummy_op_void, -}; +void efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ +} +bool efx_port_dummy_op_poll(struct efx_nic *efx) +{ + return false; +} static struct efx_phy_operations efx_dummy_phy_operations = { .init = efx_port_dummy_op_int, - .reconfigure = efx_port_dummy_op_void, - .poll = efx_port_dummy_op_void, + .reconfigure = efx_port_dummy_op_int, + .poll = efx_port_dummy_op_poll, .fini = efx_port_dummy_op_void, - .clear_interrupt = efx_port_dummy_op_void, -}; - -static struct efx_board efx_dummy_board_info = { - .init = efx_port_dummy_op_int, - .init_leds = efx_port_dummy_op_void, - .set_id_led = efx_port_dummy_op_blink, - .monitor = efx_port_dummy_op_int, - .blink = efx_port_dummy_op_blink, - .fini = efx_port_dummy_op_void, }; /************************************************************************** @@ -1932,26 +2001,26 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, /* Initialise common structures */ memset(efx, 0, sizeof(*efx)); spin_lock_init(&efx->biu_lock); - spin_lock_init(&efx->phy_lock); + mutex_init(&efx->mdio_lock); mutex_init(&efx->spi_lock); +#ifdef CONFIG_SFC_MTD + INIT_LIST_HEAD(&efx->mtd_list); +#endif INIT_WORK(&efx->reset_work, efx_reset_work); INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor); efx->pci_dev = pci_dev; efx->state = STATE_INIT; efx->reset_pending = RESET_TYPE_NONE; strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); - efx->board_info = efx_dummy_board_info; efx->net_dev = net_dev; efx->rx_checksum_enabled = true; spin_lock_init(&efx->netif_stop_lock); spin_lock_init(&efx->stats_lock); - efx->stats_disable_count = 1; mutex_init(&efx->mac_lock); - efx->mac_op = &efx_dummy_mac_operations; + efx->mac_op = type->default_mac_ops; efx->phy_op = &efx_dummy_phy_operations; efx->mdio.dev = net_dev; - INIT_WORK(&efx->phy_work, efx_phy_work); INIT_WORK(&efx->mac_work, efx_mac_work); atomic_set(&efx->netif_stop_count, 1); @@ -1981,17 +2050,9 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, efx->type = type; - /* Sanity-check NIC type */ - EFX_BUG_ON_PARANOID(efx->type->txd_ring_mask & - (efx->type->txd_ring_mask + 1)); - EFX_BUG_ON_PARANOID(efx->type->rxd_ring_mask & - (efx->type->rxd_ring_mask + 1)); - EFX_BUG_ON_PARANOID(efx->type->evq_size & - (efx->type->evq_size - 1)); /* As close as we can get to guaranteeing that we don't overflow */ - EFX_BUG_ON_PARANOID(efx->type->evq_size < - (efx->type->txd_ring_mask + 1 + - efx->type->rxd_ring_mask + 1)); + BUILD_BUG_ON(EFX_EVQ_SIZE < EFX_TXQ_SIZE + EFX_RXQ_SIZE); + EFX_BUG_ON_PARANOID(efx->type->phys_addr_channels > EFX_MAX_CHANNELS); /* Higher numbered interrupt modes are less capable! */ @@ -2027,19 +2088,10 @@ static void efx_fini_struct(struct efx_nic *efx) */ static void efx_pci_remove_main(struct efx_nic *efx) { - EFX_ASSERT_RESET_SERIALISED(efx); - - /* Skip everything if we never obtained a valid membase */ - if (!efx->membase) - return; - + efx_nic_fini_interrupt(efx); efx_fini_channels(efx); efx_fini_port(efx); - - /* Shutdown the board, then the NIC and board state */ - efx->board_info.fini(efx); - falcon_fini_interrupt(efx); - + efx->type->fini(efx); efx_fini_napi(efx); efx_remove_all(efx); } @@ -2063,9 +2115,6 @@ static void efx_pci_remove(struct pci_dev *pci_dev) /* Allow any queued efx_resets() to complete */ rtnl_unlock(); - if (efx->membase == NULL) - goto out; - efx_unregister_netdev(efx); efx_mtd_remove(efx); @@ -2078,7 +2127,6 @@ static void efx_pci_remove(struct pci_dev *pci_dev) efx_pci_remove_main(efx); -out: efx_fini_io(efx); EFX_LOG(efx, "shutdown successful\n"); @@ -2103,39 +2151,31 @@ static int efx_pci_probe_main(struct efx_nic *efx) if (rc) goto fail2; - /* Initialise the board */ - rc = efx->board_info.init(efx); - if (rc) { - EFX_ERR(efx, "failed to initialise board\n"); - goto fail3; - } - - rc = falcon_init_nic(efx); + rc = efx->type->init(efx); if (rc) { EFX_ERR(efx, "failed to initialise NIC\n"); - goto fail4; + goto fail3; } rc = efx_init_port(efx); if (rc) { EFX_ERR(efx, "failed to initialise port\n"); - goto fail5; + goto fail4; } efx_init_channels(efx); - rc = falcon_init_interrupt(efx); + rc = efx_nic_init_interrupt(efx); if (rc) - goto fail6; + goto fail5; return 0; - fail6: + fail5: efx_fini_channels(efx); efx_fini_port(efx); - fail5: fail4: - efx->board_info.fini(efx); + efx->type->fini(efx); fail3: efx_fini_napi(efx); fail2: @@ -2165,9 +2205,11 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, net_dev = alloc_etherdev(sizeof(*efx)); if (!net_dev) return -ENOMEM; - net_dev->features |= (NETIF_F_IP_CSUM | NETIF_F_SG | + net_dev->features |= (type->offload_features | NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_GRO); + if (type->offload_features & NETIF_F_V6_CSUM) + net_dev->features |= NETIF_F_TSO6; /* Mask for features that also apply to VLAN devices */ net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_TSO); @@ -2219,18 +2261,19 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, goto fail4; } - /* Switch to the running state before we expose the device to - * the OS. This is to ensure that the initial gathering of - * MAC stats succeeds. */ + /* Switch to the running state before we expose the device to the OS, + * so that dev_open()|efx_start_all() will actually start the device */ efx->state = STATE_RUNNING; - efx_mtd_probe(efx); /* allowed to fail */ - rc = efx_register_netdev(efx); if (rc) goto fail5; EFX_LOG(efx, "initialisation successful\n"); + + rtnl_lock(); + efx_mtd_probe(efx); /* allowed to fail */ + rtnl_unlock(); return 0; fail5: @@ -2246,11 +2289,107 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, return rc; } +static int efx_pm_freeze(struct device *dev) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + + efx->state = STATE_FINI; + + netif_device_detach(efx->net_dev); + + efx_stop_all(efx); + efx_fini_channels(efx); + + return 0; +} + +static int efx_pm_thaw(struct device *dev) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + + efx->state = STATE_INIT; + + efx_init_channels(efx); + + mutex_lock(&efx->mac_lock); + efx->phy_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); + + efx_start_all(efx); + + netif_device_attach(efx->net_dev); + + efx->state = STATE_RUNNING; + + efx->type->resume_wol(efx); + + return 0; +} + +static int efx_pm_poweroff(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct efx_nic *efx = pci_get_drvdata(pci_dev); + + efx->type->fini(efx); + + efx->reset_pending = RESET_TYPE_NONE; + + pci_save_state(pci_dev); + return pci_set_power_state(pci_dev, PCI_D3hot); +} + +/* Used for both resume and restore */ +static int efx_pm_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct efx_nic *efx = pci_get_drvdata(pci_dev); + int rc; + + rc = pci_set_power_state(pci_dev, PCI_D0); + if (rc) + return rc; + pci_restore_state(pci_dev); + rc = pci_enable_device(pci_dev); + if (rc) + return rc; + pci_set_master(efx->pci_dev); + rc = efx->type->reset(efx, RESET_TYPE_ALL); + if (rc) + return rc; + rc = efx->type->init(efx); + if (rc) + return rc; + efx_pm_thaw(dev); + return 0; +} + +static int efx_pm_suspend(struct device *dev) +{ + int rc; + + efx_pm_freeze(dev); + rc = efx_pm_poweroff(dev); + if (rc) + efx_pm_resume(dev); + return rc; +} + +static struct dev_pm_ops efx_pm_ops = { + .suspend = efx_pm_suspend, + .resume = efx_pm_resume, + .freeze = efx_pm_freeze, + .thaw = efx_pm_thaw, + .poweroff = efx_pm_poweroff, + .restore = efx_pm_resume, +}; + static struct pci_driver efx_pci_driver = { .name = EFX_DRIVER_NAME, .id_table = efx_pci_table, .probe = efx_pci_probe, .remove = efx_pci_remove, + .driver.pm = &efx_pm_ops, }; /************************************************************************** @@ -2314,8 +2453,8 @@ static void __exit efx_exit_module(void) module_init(efx_init_module); module_exit(efx_exit_module); -MODULE_AUTHOR("Michael Brown <mbrown@fensystems.co.uk> and " - "Solarflare Communications"); +MODULE_AUTHOR("Solarflare Communications and " + "Michael Brown <mbrown@fensystems.co.uk>"); MODULE_DESCRIPTION("Solarflare Communications network driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, efx_pci_table); diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index aecaf62f4929..a615ac051530 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -18,35 +18,64 @@ #define FALCON_A_P_DEVID 0x0703 #define FALCON_A_S_DEVID 0x6703 #define FALCON_B_P_DEVID 0x0710 +#define BETHPAGE_A_P_DEVID 0x0803 +#define SIENA_A_P_DEVID 0x0813 + +/* Solarstorm controllers use BAR 0 for I/O space and BAR 2(&3) for memory */ +#define EFX_MEM_BAR 2 /* TX */ -extern netdev_tx_t efx_xmit(struct efx_nic *efx, - struct efx_tx_queue *tx_queue, - struct sk_buff *skb); +extern int efx_probe_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_init_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_fini_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_release_tx_buffers(struct efx_tx_queue *tx_queue); +extern netdev_tx_t +efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); +extern netdev_tx_t +efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); +extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); extern void efx_stop_queue(struct efx_nic *efx); extern void efx_wake_queue(struct efx_nic *efx); +#define EFX_TXQ_SIZE 1024 +#define EFX_TXQ_MASK (EFX_TXQ_SIZE - 1) /* RX */ -extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); +extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_remove_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_init_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_fini_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_rx_strategy(struct efx_channel *channel); +extern void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue); +extern void efx_rx_work(struct work_struct *data); +extern void __efx_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, bool checksummed); extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, unsigned int len, bool checksummed, bool discard); extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay); +#define EFX_RXQ_SIZE 1024 +#define EFX_RXQ_MASK (EFX_RXQ_SIZE - 1) /* Channels */ extern void efx_process_channel_now(struct efx_channel *channel); -extern void efx_flush_queues(struct efx_nic *efx); +#define EFX_EVQ_SIZE 4096 +#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1) /* Ports */ -extern void efx_stats_disable(struct efx_nic *efx); -extern void efx_stats_enable(struct efx_nic *efx); -extern void efx_reconfigure_port(struct efx_nic *efx); -extern void __efx_reconfigure_port(struct efx_nic *efx); +extern int efx_reconfigure_port(struct efx_nic *efx); +extern int __efx_reconfigure_port(struct efx_nic *efx); + +/* Ethtool support */ +extern int efx_ethtool_get_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd); +extern int efx_ethtool_set_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd); +extern const struct ethtool_ops efx_ethtool_ops; /* Reset handling */ -extern void efx_reset_down(struct efx_nic *efx, enum reset_type method, - struct ethtool_cmd *ecmd); -extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, - struct ethtool_cmd *ecmd, bool ok); +extern int efx_reset(struct efx_nic *efx, enum reset_type method); +extern void efx_reset_down(struct efx_nic *efx, enum reset_type method); +extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok); /* Global */ extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); @@ -60,7 +89,9 @@ extern void efx_hex_dump(const u8 *, unsigned int, const char *); /* Dummy PHY ops for PHY drivers */ extern int efx_port_dummy_op_int(struct efx_nic *efx); extern void efx_port_dummy_op_void(struct efx_nic *efx); -extern void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink); +extern void +efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); +extern bool efx_port_dummy_op_poll(struct efx_nic *efx); /* MTD */ #ifdef CONFIG_SFC_MTD @@ -84,4 +115,8 @@ static inline void efx_schedule_channel(struct efx_channel *channel) napi_schedule(&channel->napi_str); } +extern void efx_link_status_changed(struct efx_nic *efx); +extern void efx_link_set_advertising(struct efx_nic *efx, u32); +extern void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type); + #endif /* EFX_EFX_H */ diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h index 60cbc6e1e66b..384cfe3b1be1 100644 --- a/drivers/net/sfc/enum.h +++ b/drivers/net/sfc/enum.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. + * Copyright 2007-2009 Solarflare Communications Inc. * * 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 @@ -13,44 +13,101 @@ /** * enum efx_loopback_mode - loopback modes * @LOOPBACK_NONE: no loopback - * @LOOPBACK_GMAC: loopback within GMAC at unspecified level - * @LOOPBACK_XGMII: loopback within XMAC at XGMII level - * @LOOPBACK_XGXS: loopback within XMAC at XGXS level - * @LOOPBACK_XAUI: loopback within XMAC at XAUI level + * @LOOPBACK_DATA: data path loopback + * @LOOPBACK_GMAC: loopback within GMAC + * @LOOPBACK_XGMII: loopback after XMAC + * @LOOPBACK_XGXS: loopback within BPX after XGXS + * @LOOPBACK_XAUI: loopback within BPX before XAUI serdes + * @LOOPBACK_GMII: loopback within BPX after GMAC + * @LOOPBACK_SGMII: loopback within BPX within SGMII + * @LOOPBACK_XGBR: loopback within BPX within XGBR + * @LOOPBACK_XFI: loopback within BPX before XFI serdes + * @LOOPBACK_XAUI_FAR: loopback within BPX after XAUI serdes + * @LOOPBACK_GMII_FAR: loopback within BPX before SGMII + * @LOOPBACK_SGMII_FAR: loopback within BPX after SGMII + * @LOOPBACK_XFI_FAR: loopback after XFI serdes * @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level * @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level * @LOOPBACK_PCS: loopback within 10G PHY at PCS level * @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level - * @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!) + * @LOOPBACK_XPORT: cross port loopback + * @LOOPBACK_XGMII_WS: wireside loopback excluding XMAC + * @LOOPBACK_XAUI_WS: wireside loopback within BPX within XAUI serdes + * @LOOPBACK_XAUI_WS_FAR: wireside loopback within BPX including XAUI serdes + * @LOOPBACK_XAUI_WS_NEAR: wireside loopback within BPX excluding XAUI serdes + * @LOOPBACK_GMII_WS: wireside loopback excluding GMAC + * @LOOPBACK_XFI_WS: wireside loopback excluding XFI serdes + * @LOOPBACK_XFI_WS_FAR: wireside loopback including XFI serdes + * @LOOPBACK_PHYXS_WS: wireside loopback within 10G PHY at PHYXS level */ -/* Please keep in order and up-to-date w.r.t the following two #defines */ +/* Please keep up-to-date w.r.t the following two #defines */ enum efx_loopback_mode { LOOPBACK_NONE = 0, - LOOPBACK_GMAC = 1, - LOOPBACK_XGMII = 2, - LOOPBACK_XGXS = 3, - LOOPBACK_XAUI = 4, - LOOPBACK_GPHY = 5, - LOOPBACK_PHYXS = 6, - LOOPBACK_PCS = 7, - LOOPBACK_PMAPMD = 8, - LOOPBACK_NETWORK = 9, + LOOPBACK_DATA = 1, + LOOPBACK_GMAC = 2, + LOOPBACK_XGMII = 3, + LOOPBACK_XGXS = 4, + LOOPBACK_XAUI = 5, + LOOPBACK_GMII = 6, + LOOPBACK_SGMII = 7, + LOOPBACK_XGBR = 8, + LOOPBACK_XFI = 9, + LOOPBACK_XAUI_FAR = 10, + LOOPBACK_GMII_FAR = 11, + LOOPBACK_SGMII_FAR = 12, + LOOPBACK_XFI_FAR = 13, + LOOPBACK_GPHY = 14, + LOOPBACK_PHYXS = 15, + LOOPBACK_PCS = 16, + LOOPBACK_PMAPMD = 17, + LOOPBACK_XPORT = 18, + LOOPBACK_XGMII_WS = 19, + LOOPBACK_XAUI_WS = 20, + LOOPBACK_XAUI_WS_FAR = 21, + LOOPBACK_XAUI_WS_NEAR = 22, + LOOPBACK_GMII_WS = 23, + LOOPBACK_XFI_WS = 24, + LOOPBACK_XFI_WS_FAR = 25, + LOOPBACK_PHYXS_WS = 26, LOOPBACK_MAX }; - #define LOOPBACK_TEST_MAX LOOPBACK_PMAPMD -extern const char *efx_loopback_mode_names[]; -#define LOOPBACK_MODE_NAME(mode) \ - STRING_TABLE_LOOKUP(mode, efx_loopback_mode) -#define LOOPBACK_MODE(efx) \ - LOOPBACK_MODE_NAME(efx->loopback_mode) - /* These loopbacks occur within the controller */ -#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_GMAC) | \ - (1 << LOOPBACK_XGMII)| \ - (1 << LOOPBACK_XGXS) | \ - (1 << LOOPBACK_XAUI)) +#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_DATA) | \ + (1 << LOOPBACK_GMAC) | \ + (1 << LOOPBACK_XGMII)| \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI) | \ + (1 << LOOPBACK_GMII) | \ + (1 << LOOPBACK_SGMII) | \ + (1 << LOOPBACK_SGMII) | \ + (1 << LOOPBACK_XGBR) | \ + (1 << LOOPBACK_XFI) | \ + (1 << LOOPBACK_XAUI_FAR) | \ + (1 << LOOPBACK_GMII_FAR) | \ + (1 << LOOPBACK_SGMII_FAR) | \ + (1 << LOOPBACK_XFI_FAR) | \ + (1 << LOOPBACK_XGMII_WS) | \ + (1 << LOOPBACK_XAUI_WS) | \ + (1 << LOOPBACK_XAUI_WS_FAR) | \ + (1 << LOOPBACK_XAUI_WS_NEAR) | \ + (1 << LOOPBACK_GMII_WS) | \ + (1 << LOOPBACK_XFI_WS) | \ + (1 << LOOPBACK_XFI_WS_FAR)) + +#define LOOPBACKS_WS ((1 << LOOPBACK_XGMII_WS) | \ + (1 << LOOPBACK_XAUI_WS) | \ + (1 << LOOPBACK_XAUI_WS_FAR) | \ + (1 << LOOPBACK_XAUI_WS_NEAR) | \ + (1 << LOOPBACK_GMII_WS) | \ + (1 << LOOPBACK_XFI_WS) | \ + (1 << LOOPBACK_XFI_WS_FAR) | \ + (1 << LOOPBACK_PHYXS_WS)) + +#define LOOPBACKS_EXTERNAL(_efx) \ + ((_efx)->loopback_modes & ~LOOPBACKS_INTERNAL & \ + ~(1 << LOOPBACK_NONE)) #define LOOPBACK_MASK(_efx) \ (1 << (_efx)->loopback_mode) @@ -58,6 +115,9 @@ extern const char *efx_loopback_mode_names[]; #define LOOPBACK_INTERNAL(_efx) \ (!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx))) +#define LOOPBACK_EXTERNAL(_efx) \ + (!!(LOOPBACK_MASK(_efx) & LOOPBACKS_EXTERNAL(_efx))) + #define LOOPBACK_CHANGED(_from, _to, _mask) \ (!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask))) @@ -84,6 +144,7 @@ extern const char *efx_loopback_mode_names[]; * @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch * @RESET_TYPE_TX_DESC_FETCH: pcie error during tx descriptor fetch * @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors + * @RESET_TYPE_MC_FAILURE: MC reboot/assertion */ enum reset_type { RESET_TYPE_NONE = -1, @@ -98,6 +159,7 @@ enum reset_type { RESET_TYPE_RX_DESC_FETCH, RESET_TYPE_TX_DESC_FETCH, RESET_TYPE_TX_SKIP, + RESET_TYPE_MC_FAILURE, RESET_TYPE_MAX, }; diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 45018f283ffa..6c0bbed8c477 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -10,30 +10,15 @@ #include <linux/netdevice.h> #include <linux/ethtool.h> -#include <linux/mdio.h> #include <linux/rtnetlink.h> #include "net_driver.h" #include "workarounds.h" #include "selftest.h" #include "efx.h" -#include "ethtool.h" -#include "falcon.h" +#include "nic.h" #include "spi.h" #include "mdio_10g.h" -const char *efx_loopback_mode_names[] = { - [LOOPBACK_NONE] = "NONE", - [LOOPBACK_GMAC] = "GMAC", - [LOOPBACK_XGMII] = "XGMII", - [LOOPBACK_XGXS] = "XGXS", - [LOOPBACK_XAUI] = "XAUI", - [LOOPBACK_GPHY] = "GPHY", - [LOOPBACK_PHYXS] = "PHYXS", - [LOOPBACK_PCS] = "PCS", - [LOOPBACK_PMAPMD] = "PMA/PMD", - [LOOPBACK_NETWORK] = "NETWORK", -}; - struct ethtool_string { char name[ETH_GSTRING_LEN]; }; @@ -167,6 +152,7 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = { EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc), }; @@ -187,13 +173,15 @@ static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count) { struct efx_nic *efx = netdev_priv(net_dev); - efx->board_info.blink(efx, 1); - set_current_state(TASK_INTERRUPTIBLE); - if (count) - schedule_timeout(count * HZ); - else - schedule(); - efx->board_info.blink(efx, 0); + do { + efx->type->set_id_led(efx, EFX_LED_ON); + schedule_timeout_interruptible(HZ / 2); + + efx->type->set_id_led(efx, EFX_LED_OFF); + schedule_timeout_interruptible(HZ / 2); + } while (!signal_pending(current) && --count != 0); + + efx->type->set_id_led(efx, EFX_LED_DEFAULT); return 0; } @@ -202,6 +190,7 @@ int efx_ethtool_get_settings(struct net_device *net_dev, struct ethtool_cmd *ecmd) { struct efx_nic *efx = netdev_priv(net_dev); + struct efx_link_state *link_state = &efx->link_state; mutex_lock(&efx->mac_lock); efx->phy_op->get_settings(efx, ecmd); @@ -209,6 +198,13 @@ int efx_ethtool_get_settings(struct net_device *net_dev, /* Falcon GMAC does not support 1000Mbps HD */ ecmd->supported &= ~SUPPORTED_1000baseT_Half; + /* Both MACs support pause frames (bidirectional and respond-only) */ + ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + + if (LOOPBACK_INTERNAL(efx)) { + ecmd->speed = link_state->speed; + ecmd->duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF; + } return 0; } @@ -230,9 +226,6 @@ int efx_ethtool_set_settings(struct net_device *net_dev, mutex_lock(&efx->mac_lock); rc = efx->phy_op->set_settings(efx, ecmd); mutex_unlock(&efx->mac_lock); - if (!rc) - efx_reconfigure_port(efx); - return rc; } @@ -243,6 +236,9 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev, strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver)); strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) + siena_print_fwver(efx, info->fw_version, + sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); } @@ -289,7 +285,7 @@ static void efx_fill_test(unsigned int test_index, #define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue #define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue #define EFX_LOOPBACK_NAME(_mode, _counter) \ - "loopback.%s." _counter, LOOPBACK_MODE_NAME(mode) + "loopback.%s." _counter, STRING_TABLE_LOOKUP(_mode, efx_loopback_mode) /** * efx_fill_loopback_test - fill in a block of loopback self-test entries @@ -372,9 +368,21 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx, efx_fill_test(n++, strings, data, &tests->registers, "core", 0, "registers", NULL); - for (i = 0; i < efx->phy_op->num_tests; i++) - efx_fill_test(n++, strings, data, &tests->phy[i], - "phy", 0, efx->phy_op->test_names[i], NULL); + if (efx->phy_op->run_tests != NULL) { + EFX_BUG_ON_PARANOID(efx->phy_op->test_name == NULL); + + for (i = 0; true; ++i) { + const char *name; + + EFX_BUG_ON_PARANOID(i >= EFX_MAX_PHY_TESTS); + name = efx->phy_op->test_name(efx, i); + if (name == NULL) + break; + + efx_fill_test(n++, strings, data, &tests->phy[i], + "phy", 0, name, NULL); + } + } /* Loopback tests */ for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { @@ -463,6 +471,36 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, } } +static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable) +{ + struct efx_nic *efx __attribute__ ((unused)) = netdev_priv(net_dev); + unsigned long features; + + features = NETIF_F_TSO; + if (efx->type->offload_features & NETIF_F_V6_CSUM) + features |= NETIF_F_TSO6; + + if (enable) + net_dev->features |= features; + else + net_dev->features &= ~features; + + return 0; +} + +static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) +{ + struct efx_nic *efx = netdev_priv(net_dev); + unsigned long features = efx->type->offload_features & NETIF_F_ALL_CSUM; + + if (enable) + net_dev->features |= features; + else + net_dev->features &= ~features; + + return 0; +} + static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable) { struct efx_nic *efx = netdev_priv(net_dev); @@ -537,7 +575,7 @@ static u32 efx_ethtool_get_link(struct net_device *net_dev) { struct efx_nic *efx = netdev_priv(net_dev); - return efx->link_up; + return efx->link_state.up; } static int efx_ethtool_get_eeprom_len(struct net_device *net_dev) @@ -562,7 +600,8 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev, rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START, + rc = falcon_spi_read(efx, spi, + eeprom->offset + EFX_EEPROM_BOOTCONFIG_START, eeprom->len, &len, buf); mutex_unlock(&efx->spi_lock); @@ -585,7 +624,8 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev, rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START, + rc = falcon_spi_write(efx, spi, + eeprom->offset + EFX_EEPROM_BOOTCONFIG_START, eeprom->len, &len, buf); mutex_unlock(&efx->spi_lock); @@ -618,6 +658,9 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev, coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive; coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation; + coalesce->tx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; + coalesce->rx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; + return 0; } @@ -656,13 +699,8 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, } efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive); - - /* Reset channel to pick up new moderation value. Note that - * this may change the value of the irq_moderation field - * (e.g. to allow for hardware timer granularity). - */ efx_for_each_channel(channel, efx) - falcon_set_int_moderation(channel); + efx->type->push_irq_moderation(channel); return 0; } @@ -671,8 +709,12 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, struct ethtool_pauseparam *pause) { struct efx_nic *efx = netdev_priv(net_dev); - enum efx_fc_type wanted_fc; + enum efx_fc_type wanted_fc, old_fc; + u32 old_adv; bool reset; + int rc = 0; + + mutex_lock(&efx->mac_lock); wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | (pause->tx_pause ? EFX_FC_TX : 0) | @@ -680,14 +722,14 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) { EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } - if (!(efx->phy_op->mmds & MDIO_DEVS_AN) && - (wanted_fc & EFX_FC_AUTO)) { - EFX_LOG(efx, "PHY does not support flow control " - "autonegotiation\n"); - return -EINVAL; + if ((wanted_fc & EFX_FC_AUTO) && !efx->link_advertising) { + EFX_LOG(efx, "Autonegotiation is disabled\n"); + rc = -EINVAL; + goto out; } /* TX flow control may automatically turn itself off if the @@ -697,27 +739,40 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, * and fix it be cycling transmit flow control on this end. */ reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX); if (EFX_WORKAROUND_11482(efx) && reset) { - if (falcon_rev(efx) >= FALCON_REV_B0) { + if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { /* Recover by resetting the EM block */ - if (efx->link_up) - falcon_drain_tx_fifo(efx); + falcon_stop_nic_stats(efx); + falcon_drain_tx_fifo(efx); + efx->mac_op->reconfigure(efx); + falcon_start_nic_stats(efx); } else { /* Schedule a reset to recover */ efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); } } - /* Try to push the pause parameters */ - mutex_lock(&efx->mac_lock); + old_adv = efx->link_advertising; + old_fc = efx->wanted_fc; + efx_link_set_wanted_fc(efx, wanted_fc); + if (efx->link_advertising != old_adv || + (efx->wanted_fc ^ old_fc) & EFX_FC_AUTO) { + rc = efx->phy_op->reconfigure(efx); + if (rc) { + EFX_ERR(efx, "Unable to advertise requested flow " + "control setting\n"); + goto out; + } + } - efx->wanted_fc = wanted_fc; - if (efx->phy_op->mmds & MDIO_DEVS_AN) - mdio45_ethtool_spauseparam_an(&efx->mdio, pause); - __efx_reconfigure_port(efx); + /* Reconfigure the MAC. The PHY *may* generate a link state change event + * if the user just changed the advertised capabilities, but there's no + * harm doing this twice */ + efx->mac_op->reconfigure(efx); +out: mutex_unlock(&efx->mac_lock); - return 0; + return rc; } static void efx_ethtool_get_pauseparam(struct net_device *net_dev, @@ -731,6 +786,50 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev, } +static void efx_ethtool_get_wol(struct net_device *net_dev, + struct ethtool_wolinfo *wol) +{ + struct efx_nic *efx = netdev_priv(net_dev); + return efx->type->get_wol(efx, wol); +} + + +static int efx_ethtool_set_wol(struct net_device *net_dev, + struct ethtool_wolinfo *wol) +{ + struct efx_nic *efx = netdev_priv(net_dev); + return efx->type->set_wol(efx, wol->wolopts); +} + +extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) +{ + struct efx_nic *efx = netdev_priv(net_dev); + enum reset_type method; + enum { + ETH_RESET_EFX_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER | + ETH_RESET_OFFLOAD | ETH_RESET_MAC) + }; + + /* Check for minimal reset flags */ + if ((*flags & ETH_RESET_EFX_INVISIBLE) != ETH_RESET_EFX_INVISIBLE) + return -EINVAL; + *flags ^= ETH_RESET_EFX_INVISIBLE; + method = RESET_TYPE_INVISIBLE; + + if (*flags & ETH_RESET_PHY) { + *flags ^= ETH_RESET_PHY; + method = RESET_TYPE_ALL; + } + + if ((*flags & efx->type->reset_world_flags) == + efx->type->reset_world_flags) { + *flags ^= efx->type->reset_world_flags; + method = RESET_TYPE_WORLD; + } + + return efx_reset(efx, method); +} + const struct ethtool_ops efx_ethtool_ops = { .get_settings = efx_ethtool_get_settings, .set_settings = efx_ethtool_set_settings, @@ -747,11 +846,13 @@ const struct ethtool_ops efx_ethtool_ops = { .get_rx_csum = efx_ethtool_get_rx_csum, .set_rx_csum = efx_ethtool_set_rx_csum, .get_tx_csum = ethtool_op_get_tx_csum, - .set_tx_csum = ethtool_op_set_tx_csum, + /* Need to enable/disable IPv6 too */ + .set_tx_csum = efx_ethtool_set_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, .get_tso = ethtool_op_get_tso, - .set_tso = ethtool_op_set_tso, + /* Need to enable/disable TSO-IPv6 too */ + .set_tso = efx_ethtool_set_tso, .get_flags = ethtool_op_get_flags, .set_flags = ethtool_op_set_flags, .get_sset_count = efx_ethtool_get_sset_count, @@ -759,4 +860,7 @@ const struct ethtool_ops efx_ethtool_ops = { .get_strings = efx_ethtool_get_strings, .phys_id = efx_ethtool_phys_id, .get_ethtool_stats = efx_ethtool_get_stats, + .get_wol = efx_ethtool_get_wol, + .set_wol = efx_ethtool_set_wol, + .reset = efx_ethtool_reset, }; diff --git a/drivers/net/sfc/ethtool.h b/drivers/net/sfc/ethtool.h deleted file mode 100644 index 295ead403356..000000000000 --- a/drivers/net/sfc/ethtool.h +++ /dev/null @@ -1,27 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2005 Fen Systems Ltd. - * Copyright 2006 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#ifndef EFX_ETHTOOL_H -#define EFX_ETHTOOL_H - -#include "net_driver.h" - -/* - * Ethtool support - */ - -extern int efx_ethtool_get_settings(struct net_device *net_dev, - struct ethtool_cmd *ecmd); -extern int efx_ethtool_set_settings(struct net_device *net_dev, - struct ethtool_cmd *ecmd); - -extern const struct ethtool_ops efx_ethtool_ops; - -#endif /* EFX_ETHTOOL_H */ diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index c049364aec46..17afcd26e870 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -14,66 +14,20 @@ #include <linux/module.h> #include <linux/seq_file.h> #include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> #include <linux/mii.h> #include "net_driver.h" #include "bitfield.h" #include "efx.h" #include "mac.h" #include "spi.h" -#include "falcon.h" -#include "falcon_hwdefs.h" -#include "falcon_io.h" +#include "nic.h" +#include "regs.h" +#include "io.h" #include "mdio_10g.h" #include "phy.h" -#include "boards.h" #include "workarounds.h" -/* Falcon hardware control. - * Falcon is the internal codename for the SFC4000 controller that is - * present in SFE400X evaluation boards - */ - -/** - * struct falcon_nic_data - Falcon NIC state - * @next_buffer_table: First available buffer table id - * @pci_dev2: The secondary PCI device if present - * @i2c_data: Operations and state for I2C bit-bashing algorithm - * @int_error_count: Number of internal errors seen recently - * @int_error_expire: Time at which error count will be expired - */ -struct falcon_nic_data { - unsigned next_buffer_table; - struct pci_dev *pci_dev2; - struct i2c_algo_bit_data i2c_data; - - unsigned int_error_count; - unsigned long int_error_expire; -}; - -/************************************************************************** - * - * Configurable values - * - ************************************************************************** - */ - -static int disable_dma_stats; - -/* This is set to 16 for a good reason. In summary, if larger than - * 16, the descriptor cache holds more than a default socket - * buffer's worth of packets (for UDP we can only have at most one - * socket buffer's worth outstanding). This combined with the fact - * that we only get 1 TX event per descriptor cache means the NIC - * goes idle. - */ -#define TX_DC_ENTRIES 16 -#define TX_DC_ENTRIES_ORDER 0 -#define TX_DC_BASE 0x130000 - -#define RX_DC_ENTRIES 64 -#define RX_DC_ENTRIES_ORDER 2 -#define RX_DC_BASE 0x100000 +/* Hardware control for SFC4000 (aka Falcon). */ static const unsigned int /* "Large" EEPROM device: Atmel AT25640 or similar @@ -89,104 +43,6 @@ default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN) | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN) | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)); -/* RX FIFO XOFF watermark - * - * When the amount of the RX FIFO increases used increases past this - * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A) - * This also has an effect on RX/TX arbitration - */ -static int rx_xoff_thresh_bytes = -1; -module_param(rx_xoff_thresh_bytes, int, 0644); -MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold"); - -/* RX FIFO XON watermark - * - * When the amount of the RX FIFO used decreases below this - * watermark send XON. Only used if TX flow control is enabled (ethtool -A) - * This also has an effect on RX/TX arbitration - */ -static int rx_xon_thresh_bytes = -1; -module_param(rx_xon_thresh_bytes, int, 0644); -MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold"); - -/* TX descriptor ring size - min 512 max 4k */ -#define FALCON_TXD_RING_ORDER TX_DESCQ_SIZE_1K -#define FALCON_TXD_RING_SIZE 1024 -#define FALCON_TXD_RING_MASK (FALCON_TXD_RING_SIZE - 1) - -/* RX descriptor ring size - min 512 max 4k */ -#define FALCON_RXD_RING_ORDER RX_DESCQ_SIZE_1K -#define FALCON_RXD_RING_SIZE 1024 -#define FALCON_RXD_RING_MASK (FALCON_RXD_RING_SIZE - 1) - -/* Event queue size - max 32k */ -#define FALCON_EVQ_ORDER EVQ_SIZE_4K -#define FALCON_EVQ_SIZE 4096 -#define FALCON_EVQ_MASK (FALCON_EVQ_SIZE - 1) - -/* If FALCON_MAX_INT_ERRORS internal errors occur within - * FALCON_INT_ERROR_EXPIRE seconds, we consider the NIC broken and - * disable it. - */ -#define FALCON_INT_ERROR_EXPIRE 3600 -#define FALCON_MAX_INT_ERRORS 5 - -/* We poll for events every FLUSH_INTERVAL ms, and check FLUSH_POLL_COUNT times - */ -#define FALCON_FLUSH_INTERVAL 10 -#define FALCON_FLUSH_POLL_COUNT 100 - -/************************************************************************** - * - * Falcon constants - * - ************************************************************************** - */ - -/* DMA address mask */ -#define FALCON_DMA_MASK DMA_BIT_MASK(46) - -/* TX DMA length mask (13-bit) */ -#define FALCON_TX_DMA_MASK (4096 - 1) - -/* Size and alignment of special buffers (4KB) */ -#define FALCON_BUF_SIZE 4096 - -/* Dummy SRAM size code */ -#define SRM_NB_BSZ_ONCHIP_ONLY (-1) - -#define FALCON_IS_DUAL_FUNC(efx) \ - (falcon_rev(efx) < FALCON_REV_B0) - -/************************************************************************** - * - * Falcon hardware access - * - **************************************************************************/ - -/* Read the current event from the event queue */ -static inline efx_qword_t *falcon_event(struct efx_channel *channel, - unsigned int index) -{ - return (((efx_qword_t *) (channel->eventq.addr)) + index); -} - -/* See if an event is present - * - * We check both the high and low dword of the event for all ones. We - * wrote all ones when we cleared the event, and no valid event can - * have all ones in either its high or low dwords. This approach is - * robust against reordering. - * - * Note that using a single 64-bit comparison is incorrect; even - * though the CPU read will be atomic, the DMA write may not be. - */ -static inline int falcon_event_present(efx_qword_t *event) -{ - return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | - EFX_DWORD_IS_ALL_ONES(event->dword[1]))); -} - /************************************************************************** * * I2C bus - this is a bit-bashing interface using GPIO pins @@ -200,9 +56,9 @@ static void falcon_setsda(void *data, int state) struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(efx, ®, GPIO_CTL_REG_KER); - EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, !state); - falcon_write(efx, ®, GPIO_CTL_REG_KER); + efx_reado(efx, ®, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN, !state); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); } static void falcon_setscl(void *data, int state) @@ -210,9 +66,9 @@ static void falcon_setscl(void *data, int state) struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(efx, ®, GPIO_CTL_REG_KER); - EFX_SET_OWORD_FIELD(reg, GPIO0_OEN, !state); - falcon_write(efx, ®, GPIO_CTL_REG_KER); + efx_reado(efx, ®, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO0_OEN, !state); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); } static int falcon_getsda(void *data) @@ -220,8 +76,8 @@ static int falcon_getsda(void *data) struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(efx, ®, GPIO_CTL_REG_KER); - return EFX_OWORD_FIELD(reg, GPIO3_IN); + efx_reado(efx, ®, FR_AB_GPIO_CTL); + return EFX_OWORD_FIELD(reg, FRF_AB_GPIO3_IN); } static int falcon_getscl(void *data) @@ -229,8 +85,8 @@ static int falcon_getscl(void *data) struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(efx, ®, GPIO_CTL_REG_KER); - return EFX_OWORD_FIELD(reg, GPIO0_IN); + efx_reado(efx, ®, FR_AB_GPIO_CTL); + return EFX_OWORD_FIELD(reg, FRF_AB_GPIO0_IN); } static struct i2c_algo_bit_data falcon_i2c_bit_operations = { @@ -243,1115 +99,39 @@ static struct i2c_algo_bit_data falcon_i2c_bit_operations = { .timeout = DIV_ROUND_UP(HZ, 20), }; -/************************************************************************** - * - * Falcon special buffer handling - * Special buffers are used for event queues and the TX and RX - * descriptor rings. - * - *************************************************************************/ - -/* - * Initialise a Falcon special buffer - * - * This will define a buffer (previously allocated via - * falcon_alloc_special_buffer()) in Falcon's buffer table, allowing - * it to be used for event queues, descriptor rings etc. - */ -static void -falcon_init_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - efx_qword_t buf_desc; - int index; - dma_addr_t dma_addr; - int i; - - EFX_BUG_ON_PARANOID(!buffer->addr); - - /* Write buffer descriptors to NIC */ - for (i = 0; i < buffer->entries; i++) { - index = buffer->index + i; - dma_addr = buffer->dma_addr + (i * 4096); - EFX_LOG(efx, "mapping special buffer %d at %llx\n", - index, (unsigned long long)dma_addr); - EFX_POPULATE_QWORD_4(buf_desc, - IP_DAT_BUF_SIZE, IP_DAT_BUF_SIZE_4K, - BUF_ADR_REGION, 0, - BUF_ADR_FBUF, (dma_addr >> 12), - BUF_OWNER_ID_FBUF, 0); - falcon_write_sram(efx, &buf_desc, index); - } -} - -/* Unmaps a buffer from Falcon and clears the buffer table entries */ -static void -falcon_fini_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - efx_oword_t buf_tbl_upd; - unsigned int start = buffer->index; - unsigned int end = (buffer->index + buffer->entries - 1); - - if (!buffer->entries) - return; - - EFX_LOG(efx, "unmapping special buffers %d-%d\n", - buffer->index, buffer->index + buffer->entries - 1); - - EFX_POPULATE_OWORD_4(buf_tbl_upd, - BUF_UPD_CMD, 0, - BUF_CLR_CMD, 1, - BUF_CLR_END_ID, end, - BUF_CLR_START_ID, start); - falcon_write(efx, &buf_tbl_upd, BUF_TBL_UPD_REG_KER); -} - -/* - * Allocate a new Falcon special buffer - * - * This allocates memory for a new buffer, clears it and allocates a - * new buffer ID range. It does not write into Falcon's buffer table. - * - * This call will allocate 4KB buffers, since Falcon can't use 8KB - * buffers for event queues and descriptor rings. - */ -static int falcon_alloc_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer, - unsigned int len) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - - len = ALIGN(len, FALCON_BUF_SIZE); - - buffer->addr = pci_alloc_consistent(efx->pci_dev, len, - &buffer->dma_addr); - if (!buffer->addr) - return -ENOMEM; - buffer->len = len; - buffer->entries = len / FALCON_BUF_SIZE; - BUG_ON(buffer->dma_addr & (FALCON_BUF_SIZE - 1)); - - /* All zeros is a potentially valid event so memset to 0xff */ - memset(buffer->addr, 0xff, len); - - /* Select new buffer ID */ - buffer->index = nic_data->next_buffer_table; - nic_data->next_buffer_table += buffer->entries; - - EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " - "(virt %p phys %llx)\n", buffer->index, - buffer->index + buffer->entries - 1, - (u64)buffer->dma_addr, len, - buffer->addr, (u64)virt_to_phys(buffer->addr)); - - return 0; -} - -static void falcon_free_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - if (!buffer->addr) - return; - - EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x " - "(virt %p phys %llx)\n", buffer->index, - buffer->index + buffer->entries - 1, - (u64)buffer->dma_addr, buffer->len, - buffer->addr, (u64)virt_to_phys(buffer->addr)); - - pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr, - buffer->dma_addr); - buffer->addr = NULL; - buffer->entries = 0; -} - -/************************************************************************** - * - * Falcon generic buffer handling - * These buffers are used for interrupt status and MAC stats - * - **************************************************************************/ - -static int falcon_alloc_buffer(struct efx_nic *efx, - struct efx_buffer *buffer, unsigned int len) -{ - buffer->addr = pci_alloc_consistent(efx->pci_dev, len, - &buffer->dma_addr); - if (!buffer->addr) - return -ENOMEM; - buffer->len = len; - memset(buffer->addr, 0, len); - return 0; -} - -static void falcon_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer) -{ - if (buffer->addr) { - pci_free_consistent(efx->pci_dev, buffer->len, - buffer->addr, buffer->dma_addr); - buffer->addr = NULL; - } -} - -/************************************************************************** - * - * Falcon TX path - * - **************************************************************************/ - -/* Returns a pointer to the specified transmit descriptor in the TX - * descriptor queue belonging to the specified channel. - */ -static inline efx_qword_t *falcon_tx_desc(struct efx_tx_queue *tx_queue, - unsigned int index) -{ - return (((efx_qword_t *) (tx_queue->txd.addr)) + index); -} - -/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */ -static inline void falcon_notify_tx_desc(struct efx_tx_queue *tx_queue) -{ - unsigned write_ptr; - efx_dword_t reg; - - write_ptr = tx_queue->write_count & FALCON_TXD_RING_MASK; - EFX_POPULATE_DWORD_1(reg, TX_DESC_WPTR_DWORD, write_ptr); - falcon_writel_page(tx_queue->efx, ®, - TX_DESC_UPD_REG_KER_DWORD, tx_queue->queue); -} - - -/* For each entry inserted into the software descriptor ring, create a - * descriptor in the hardware TX descriptor ring (in host memory), and - * write a doorbell. - */ -void falcon_push_buffers(struct efx_tx_queue *tx_queue) -{ - - struct efx_tx_buffer *buffer; - efx_qword_t *txd; - unsigned write_ptr; - - BUG_ON(tx_queue->write_count == tx_queue->insert_count); - - do { - write_ptr = tx_queue->write_count & FALCON_TXD_RING_MASK; - buffer = &tx_queue->buffer[write_ptr]; - txd = falcon_tx_desc(tx_queue, write_ptr); - ++tx_queue->write_count; - - /* Create TX descriptor ring entry */ - EFX_POPULATE_QWORD_5(*txd, - TX_KER_PORT, 0, - TX_KER_CONT, buffer->continuation, - TX_KER_BYTE_CNT, buffer->len, - TX_KER_BUF_REGION, 0, - TX_KER_BUF_ADR, buffer->dma_addr); - } while (tx_queue->write_count != tx_queue->insert_count); - - wmb(); /* Ensure descriptors are written before they are fetched */ - falcon_notify_tx_desc(tx_queue); -} - -/* Allocate hardware resources for a TX queue */ -int falcon_probe_tx(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - return falcon_alloc_special_buffer(efx, &tx_queue->txd, - FALCON_TXD_RING_SIZE * - sizeof(efx_qword_t)); -} - -void falcon_init_tx(struct efx_tx_queue *tx_queue) -{ - efx_oword_t tx_desc_ptr; - struct efx_nic *efx = tx_queue->efx; - - tx_queue->flushed = false; - - /* Pin TX descriptor ring */ - falcon_init_special_buffer(efx, &tx_queue->txd); - - /* Push TX descriptor ring to card */ - EFX_POPULATE_OWORD_10(tx_desc_ptr, - TX_DESCQ_EN, 1, - TX_ISCSI_DDIG_EN, 0, - TX_ISCSI_HDIG_EN, 0, - TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, - TX_DESCQ_EVQ_ID, tx_queue->channel->channel, - TX_DESCQ_OWNER_ID, 0, - TX_DESCQ_LABEL, tx_queue->queue, - TX_DESCQ_SIZE, FALCON_TXD_RING_ORDER, - TX_DESCQ_TYPE, 0, - TX_NON_IP_DROP_DIS_B0, 1); - - if (falcon_rev(efx) >= FALCON_REV_B0) { - int csum = tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM; - EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_IP_CHKSM_DIS_B0, !csum); - EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_TCP_CHKSM_DIS_B0, !csum); - } - - falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, - tx_queue->queue); - - if (falcon_rev(efx) < FALCON_REV_B0) { - efx_oword_t reg; - - /* Only 128 bits in this register */ - BUILD_BUG_ON(EFX_TX_QUEUE_COUNT >= 128); - - falcon_read(efx, ®, TX_CHKSM_CFG_REG_KER_A1); - if (tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM) - clear_bit_le(tx_queue->queue, (void *)®); - else - set_bit_le(tx_queue->queue, (void *)®); - falcon_write(efx, ®, TX_CHKSM_CFG_REG_KER_A1); - } -} - -static void falcon_flush_tx_queue(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - efx_oword_t tx_flush_descq; - - /* Post a flush command */ - EFX_POPULATE_OWORD_2(tx_flush_descq, - TX_FLUSH_DESCQ_CMD, 1, - TX_FLUSH_DESCQ, tx_queue->queue); - falcon_write(efx, &tx_flush_descq, TX_FLUSH_DESCQ_REG_KER); -} - -void falcon_fini_tx(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - efx_oword_t tx_desc_ptr; - - /* The queue should have been flushed */ - WARN_ON(!tx_queue->flushed); - - /* Remove TX descriptor ring from card */ - EFX_ZERO_OWORD(tx_desc_ptr); - falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, - tx_queue->queue); - - /* Unpin TX descriptor ring */ - falcon_fini_special_buffer(efx, &tx_queue->txd); -} - -/* Free buffers backing TX queue */ -void falcon_remove_tx(struct efx_tx_queue *tx_queue) -{ - falcon_free_special_buffer(tx_queue->efx, &tx_queue->txd); -} - -/************************************************************************** - * - * Falcon RX path - * - **************************************************************************/ - -/* Returns a pointer to the specified descriptor in the RX descriptor queue */ -static inline efx_qword_t *falcon_rx_desc(struct efx_rx_queue *rx_queue, - unsigned int index) -{ - return (((efx_qword_t *) (rx_queue->rxd.addr)) + index); -} - -/* This creates an entry in the RX descriptor queue */ -static inline void falcon_build_rx_desc(struct efx_rx_queue *rx_queue, - unsigned index) -{ - struct efx_rx_buffer *rx_buf; - efx_qword_t *rxd; - - rxd = falcon_rx_desc(rx_queue, index); - rx_buf = efx_rx_buffer(rx_queue, index); - EFX_POPULATE_QWORD_3(*rxd, - RX_KER_BUF_SIZE, - rx_buf->len - - rx_queue->efx->type->rx_buffer_padding, - RX_KER_BUF_REGION, 0, - RX_KER_BUF_ADR, rx_buf->dma_addr); -} - -/* This writes to the RX_DESC_WPTR register for the specified receive - * descriptor ring. - */ -void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue) -{ - efx_dword_t reg; - unsigned write_ptr; - - while (rx_queue->notified_count != rx_queue->added_count) { - falcon_build_rx_desc(rx_queue, - rx_queue->notified_count & - FALCON_RXD_RING_MASK); - ++rx_queue->notified_count; - } - - wmb(); - write_ptr = rx_queue->added_count & FALCON_RXD_RING_MASK; - EFX_POPULATE_DWORD_1(reg, RX_DESC_WPTR_DWORD, write_ptr); - falcon_writel_page(rx_queue->efx, ®, - RX_DESC_UPD_REG_KER_DWORD, rx_queue->queue); -} - -int falcon_probe_rx(struct efx_rx_queue *rx_queue) -{ - struct efx_nic *efx = rx_queue->efx; - return falcon_alloc_special_buffer(efx, &rx_queue->rxd, - FALCON_RXD_RING_SIZE * - sizeof(efx_qword_t)); -} - -void falcon_init_rx(struct efx_rx_queue *rx_queue) -{ - efx_oword_t rx_desc_ptr; - struct efx_nic *efx = rx_queue->efx; - bool is_b0 = falcon_rev(efx) >= FALCON_REV_B0; - bool iscsi_digest_en = is_b0; - - EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n", - rx_queue->queue, rx_queue->rxd.index, - rx_queue->rxd.index + rx_queue->rxd.entries - 1); - - rx_queue->flushed = false; - - /* Pin RX descriptor ring */ - falcon_init_special_buffer(efx, &rx_queue->rxd); - - /* Push RX descriptor ring to card */ - EFX_POPULATE_OWORD_10(rx_desc_ptr, - RX_ISCSI_DDIG_EN, iscsi_digest_en, - RX_ISCSI_HDIG_EN, iscsi_digest_en, - RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, - RX_DESCQ_EVQ_ID, rx_queue->channel->channel, - RX_DESCQ_OWNER_ID, 0, - RX_DESCQ_LABEL, rx_queue->queue, - RX_DESCQ_SIZE, FALCON_RXD_RING_ORDER, - RX_DESCQ_TYPE, 0 /* kernel queue */ , - /* For >=B0 this is scatter so disable */ - RX_DESCQ_JUMBO, !is_b0, - RX_DESCQ_EN, 1); - falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); -} - -static void falcon_flush_rx_queue(struct efx_rx_queue *rx_queue) -{ - struct efx_nic *efx = rx_queue->efx; - efx_oword_t rx_flush_descq; - - /* Post a flush command */ - EFX_POPULATE_OWORD_2(rx_flush_descq, - RX_FLUSH_DESCQ_CMD, 1, - RX_FLUSH_DESCQ, rx_queue->queue); - falcon_write(efx, &rx_flush_descq, RX_FLUSH_DESCQ_REG_KER); -} - -void falcon_fini_rx(struct efx_rx_queue *rx_queue) -{ - efx_oword_t rx_desc_ptr; - struct efx_nic *efx = rx_queue->efx; - - /* The queue should already have been flushed */ - WARN_ON(!rx_queue->flushed); - - /* Remove RX descriptor ring from card */ - EFX_ZERO_OWORD(rx_desc_ptr); - falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); - - /* Unpin RX descriptor ring */ - falcon_fini_special_buffer(efx, &rx_queue->rxd); -} - -/* Free buffers backing RX queue */ -void falcon_remove_rx(struct efx_rx_queue *rx_queue) -{ - falcon_free_special_buffer(rx_queue->efx, &rx_queue->rxd); -} - -/************************************************************************** - * - * Falcon event queue processing - * Event queues are processed by per-channel tasklets. - * - **************************************************************************/ - -/* Update a channel's event queue's read pointer (RPTR) register - * - * This writes the EVQ_RPTR_REG register for the specified channel's - * event queue. - * - * Note that EVQ_RPTR_REG contains the index of the "last read" event, - * whereas channel->eventq_read_ptr contains the index of the "next to - * read" event. - */ -void falcon_eventq_read_ack(struct efx_channel *channel) -{ - efx_dword_t reg; - struct efx_nic *efx = channel->efx; - - EFX_POPULATE_DWORD_1(reg, EVQ_RPTR_DWORD, channel->eventq_read_ptr); - falcon_writel_table(efx, ®, efx->type->evq_rptr_tbl_base, - channel->channel); -} - -/* Use HW to insert a SW defined event */ -void falcon_generate_event(struct efx_channel *channel, efx_qword_t *event) -{ - efx_oword_t drv_ev_reg; - - EFX_POPULATE_OWORD_2(drv_ev_reg, - DRV_EV_QID, channel->channel, - DRV_EV_DATA, - EFX_QWORD_FIELD64(*event, WHOLE_EVENT)); - falcon_write(channel->efx, &drv_ev_reg, DRV_EV_REG_KER); -} - -/* Handle a transmit completion event - * - * Falcon batches TX completion events; the message we receive is of - * the form "complete all TX events up to this index". - */ -static void falcon_handle_tx_event(struct efx_channel *channel, - efx_qword_t *event) -{ - unsigned int tx_ev_desc_ptr; - unsigned int tx_ev_q_label; - struct efx_tx_queue *tx_queue; - struct efx_nic *efx = channel->efx; - - if (likely(EFX_QWORD_FIELD(*event, TX_EV_COMP))) { - /* Transmit completion */ - tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR); - tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); - tx_queue = &efx->tx_queue[tx_ev_q_label]; - channel->irq_mod_score += - (tx_ev_desc_ptr - tx_queue->read_count) & - efx->type->txd_ring_mask; - efx_xmit_done(tx_queue, tx_ev_desc_ptr); - } else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) { - /* Rewrite the FIFO write pointer */ - tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); - tx_queue = &efx->tx_queue[tx_ev_q_label]; - - if (efx_dev_registered(efx)) - netif_tx_lock(efx->net_dev); - falcon_notify_tx_desc(tx_queue); - if (efx_dev_registered(efx)) - netif_tx_unlock(efx->net_dev); - } else if (EFX_QWORD_FIELD(*event, TX_EV_PKT_ERR) && - EFX_WORKAROUND_10727(efx)) { - efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); - } else { - EFX_ERR(efx, "channel %d unexpected TX event " - EFX_QWORD_FMT"\n", channel->channel, - EFX_QWORD_VAL(*event)); - } -} - -/* Detect errors included in the rx_evt_pkt_ok bit. */ -static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue, - const efx_qword_t *event, - bool *rx_ev_pkt_ok, - bool *discard) -{ - struct efx_nic *efx = rx_queue->efx; - bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; - bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; - bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; - bool rx_ev_other_err, rx_ev_pause_frm; - bool rx_ev_ip_frag_err, rx_ev_hdr_type, rx_ev_mcast_pkt; - unsigned rx_ev_pkt_type; - - rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE); - rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT); - rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, RX_EV_TOBE_DISC); - rx_ev_pkt_type = EFX_QWORD_FIELD(*event, RX_EV_PKT_TYPE); - rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event, - RX_EV_BUF_OWNER_ID_ERR); - rx_ev_ip_frag_err = EFX_QWORD_FIELD(*event, RX_EV_IF_FRAG_ERR); - rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event, - RX_EV_IP_HDR_CHKSUM_ERR); - rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event, - RX_EV_TCP_UDP_CHKSUM_ERR); - rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, RX_EV_ETH_CRC_ERR); - rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, RX_EV_FRM_TRUNC); - rx_ev_drib_nib = ((falcon_rev(efx) >= FALCON_REV_B0) ? - 0 : EFX_QWORD_FIELD(*event, RX_EV_DRIB_NIB)); - rx_ev_pause_frm = EFX_QWORD_FIELD(*event, RX_EV_PAUSE_FRM_ERR); - - /* Every error apart from tobe_disc and pause_frm */ - rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | - rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | - rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); - - /* Count errors that are not in MAC stats. Ignore expected - * checksum errors during self-test. */ - if (rx_ev_frm_trunc) - ++rx_queue->channel->n_rx_frm_trunc; - else if (rx_ev_tobe_disc) - ++rx_queue->channel->n_rx_tobe_disc; - else if (!efx->loopback_selftest) { - if (rx_ev_ip_hdr_chksum_err) - ++rx_queue->channel->n_rx_ip_hdr_chksum_err; - else if (rx_ev_tcp_udp_chksum_err) - ++rx_queue->channel->n_rx_tcp_udp_chksum_err; - } - if (rx_ev_ip_frag_err) - ++rx_queue->channel->n_rx_ip_frag_err; - - /* The frame must be discarded if any of these are true. */ - *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | - rx_ev_tobe_disc | rx_ev_pause_frm); - - /* TOBE_DISC is expected on unicast mismatches; don't print out an - * error message. FRM_TRUNC indicates RXDP dropped the packet due - * to a FIFO overflow. - */ -#ifdef EFX_ENABLE_DEBUG - if (rx_ev_other_err) { - EFX_INFO_RL(efx, " RX queue %d unexpected RX event " - EFX_QWORD_FMT "%s%s%s%s%s%s%s%s\n", - rx_queue->queue, EFX_QWORD_VAL(*event), - rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", - rx_ev_ip_hdr_chksum_err ? - " [IP_HDR_CHKSUM_ERR]" : "", - rx_ev_tcp_udp_chksum_err ? - " [TCP_UDP_CHKSUM_ERR]" : "", - rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", - rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", - rx_ev_drib_nib ? " [DRIB_NIB]" : "", - rx_ev_tobe_disc ? " [TOBE_DISC]" : "", - rx_ev_pause_frm ? " [PAUSE]" : ""); - } -#endif -} - -/* Handle receive events that are not in-order. */ -static void falcon_handle_rx_bad_index(struct efx_rx_queue *rx_queue, - unsigned index) -{ - struct efx_nic *efx = rx_queue->efx; - unsigned expected, dropped; - - expected = rx_queue->removed_count & FALCON_RXD_RING_MASK; - dropped = ((index + FALCON_RXD_RING_SIZE - expected) & - FALCON_RXD_RING_MASK); - EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n", - dropped, index, expected); - - efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); -} - -/* Handle a packet received event - * - * Falcon silicon gives a "discard" flag if it's a unicast packet with the - * wrong destination address - * Also "is multicast" and "matches multicast filter" flags can be used to - * discard non-matching multicast packets. - */ -static void falcon_handle_rx_event(struct efx_channel *channel, - const efx_qword_t *event) -{ - unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt; - unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt; - unsigned expected_ptr; - bool rx_ev_pkt_ok, discard = false, checksummed; - struct efx_rx_queue *rx_queue; - struct efx_nic *efx = channel->efx; - - /* Basic packet information */ - rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, RX_EV_BYTE_CNT); - rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, RX_EV_PKT_OK); - rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE); - WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_JUMBO_CONT)); - WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_SOP) != 1); - WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_Q_LABEL) != channel->channel); - - rx_queue = &efx->rx_queue[channel->channel]; - - rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, RX_EV_DESC_PTR); - expected_ptr = rx_queue->removed_count & FALCON_RXD_RING_MASK; - if (unlikely(rx_ev_desc_ptr != expected_ptr)) - falcon_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr); - - if (likely(rx_ev_pkt_ok)) { - /* If packet is marked as OK and packet type is TCP/IPv4 or - * UDP/IPv4, then we can rely on the hardware checksum. - */ - checksummed = RX_EV_HDR_TYPE_HAS_CHECKSUMS(rx_ev_hdr_type); - } else { - falcon_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, - &discard); - checksummed = false; - } - - /* Detect multicast packets that didn't match the filter */ - rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT); - if (rx_ev_mcast_pkt) { - unsigned int rx_ev_mcast_hash_match = - EFX_QWORD_FIELD(*event, RX_EV_MCAST_HASH_MATCH); - - if (unlikely(!rx_ev_mcast_hash_match)) - discard = true; - } - - channel->irq_mod_score += 2; - - /* Handle received packet */ - efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, - checksummed, discard); -} - -/* Global events are basically PHY events */ -static void falcon_handle_global_event(struct efx_channel *channel, - efx_qword_t *event) -{ - struct efx_nic *efx = channel->efx; - bool handled = false; - - if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) || - EFX_QWORD_FIELD(*event, G_PHY1_INTR) || - EFX_QWORD_FIELD(*event, XG_PHY_INTR) || - EFX_QWORD_FIELD(*event, XFP_PHY_INTR)) { - efx->phy_op->clear_interrupt(efx); - queue_work(efx->workqueue, &efx->phy_work); - handled = true; - } - - if ((falcon_rev(efx) >= FALCON_REV_B0) && - EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0)) { - queue_work(efx->workqueue, &efx->mac_work); - handled = true; - } - - if (EFX_QWORD_FIELD_VER(efx, *event, RX_RECOVERY)) { - EFX_ERR(efx, "channel %d seen global RX_RESET " - "event. Resetting.\n", channel->channel); - - atomic_inc(&efx->rx_reset); - efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); - handled = true; - } - - if (!handled) - EFX_ERR(efx, "channel %d unknown global event " - EFX_QWORD_FMT "\n", channel->channel, - EFX_QWORD_VAL(*event)); -} - -static void falcon_handle_driver_event(struct efx_channel *channel, - efx_qword_t *event) -{ - struct efx_nic *efx = channel->efx; - unsigned int ev_sub_code; - unsigned int ev_sub_data; - - ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); - ev_sub_data = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_DATA); - - switch (ev_sub_code) { - case TX_DESCQ_FLS_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d TXQ %d flushed\n", - channel->channel, ev_sub_data); - break; - case RX_DESCQ_FLS_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d RXQ %d flushed\n", - channel->channel, ev_sub_data); - break; - case EVQ_INIT_DONE_EV_DECODE: - EFX_LOG(efx, "channel %d EVQ %d initialised\n", - channel->channel, ev_sub_data); - break; - case SRM_UPD_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d SRAM update done\n", - channel->channel); - break; - case WAKE_UP_EV_DECODE: - EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", - channel->channel, ev_sub_data); - break; - case TIMER_EV_DECODE: - EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", - channel->channel, ev_sub_data); - break; - case RX_RECOVERY_EV_DECODE: - EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " - "Resetting.\n", channel->channel); - atomic_inc(&efx->rx_reset); - efx_schedule_reset(efx, - EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : - RESET_TYPE_DISABLE); - break; - case RX_DSC_ERROR_EV_DECODE: - EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error." - " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data); - efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH); - break; - case TX_DSC_ERROR_EV_DECODE: - EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error." - " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data); - efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); - break; - default: - EFX_TRACE(efx, "channel %d unknown driver event code %d " - "data %04x\n", channel->channel, ev_sub_code, - ev_sub_data); - break; - } -} - -int falcon_process_eventq(struct efx_channel *channel, int rx_quota) -{ - unsigned int read_ptr; - efx_qword_t event, *p_event; - int ev_code; - int rx_packets = 0; - - read_ptr = channel->eventq_read_ptr; - - do { - p_event = falcon_event(channel, read_ptr); - event = *p_event; - - if (!falcon_event_present(&event)) - /* End of events */ - break; - - EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n", - channel->channel, EFX_QWORD_VAL(event)); - - /* Clear this event by marking it all ones */ - EFX_SET_QWORD(*p_event); - - ev_code = EFX_QWORD_FIELD(event, EV_CODE); - - switch (ev_code) { - case RX_IP_EV_DECODE: - falcon_handle_rx_event(channel, &event); - ++rx_packets; - break; - case TX_IP_EV_DECODE: - falcon_handle_tx_event(channel, &event); - break; - case DRV_GEN_EV_DECODE: - channel->eventq_magic - = EFX_QWORD_FIELD(event, EVQ_MAGIC); - EFX_LOG(channel->efx, "channel %d received generated " - "event "EFX_QWORD_FMT"\n", channel->channel, - EFX_QWORD_VAL(event)); - break; - case GLOBAL_EV_DECODE: - falcon_handle_global_event(channel, &event); - break; - case DRIVER_EV_DECODE: - falcon_handle_driver_event(channel, &event); - break; - default: - EFX_ERR(channel->efx, "channel %d unknown event type %d" - " (data " EFX_QWORD_FMT ")\n", channel->channel, - ev_code, EFX_QWORD_VAL(event)); - } - - /* Increment read pointer */ - read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; - - } while (rx_packets < rx_quota); - - channel->eventq_read_ptr = read_ptr; - return rx_packets; -} - -void falcon_set_int_moderation(struct efx_channel *channel) +static void falcon_push_irq_moderation(struct efx_channel *channel) { efx_dword_t timer_cmd; struct efx_nic *efx = channel->efx; /* Set timer register */ if (channel->irq_moderation) { - /* Round to resolution supported by hardware. The value we - * program is based at 0. So actual interrupt moderation - * achieved is ((x + 1) * res). - */ - channel->irq_moderation -= (channel->irq_moderation % - FALCON_IRQ_MOD_RESOLUTION); - if (channel->irq_moderation < FALCON_IRQ_MOD_RESOLUTION) - channel->irq_moderation = FALCON_IRQ_MOD_RESOLUTION; EFX_POPULATE_DWORD_2(timer_cmd, - TIMER_MODE, TIMER_MODE_INT_HLDOFF, - TIMER_VAL, - channel->irq_moderation / - FALCON_IRQ_MOD_RESOLUTION - 1); + FRF_AB_TC_TIMER_MODE, + FFE_BB_TIMER_MODE_INT_HLDOFF, + FRF_AB_TC_TIMER_VAL, + channel->irq_moderation - 1); } else { EFX_POPULATE_DWORD_2(timer_cmd, - TIMER_MODE, TIMER_MODE_DIS, - TIMER_VAL, 0); + FRF_AB_TC_TIMER_MODE, + FFE_BB_TIMER_MODE_DIS, + FRF_AB_TC_TIMER_VAL, 0); } - falcon_writel_page_locked(efx, &timer_cmd, TIMER_CMD_REG_KER, - channel->channel); - + BUILD_BUG_ON(FR_AA_TIMER_COMMAND_KER != FR_BZ_TIMER_COMMAND_P0); + efx_writed_page_locked(efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, + channel->channel); } -/* Allocate buffer table entries for event queue */ -int falcon_probe_eventq(struct efx_channel *channel) -{ - struct efx_nic *efx = channel->efx; - unsigned int evq_size; - - evq_size = FALCON_EVQ_SIZE * sizeof(efx_qword_t); - return falcon_alloc_special_buffer(efx, &channel->eventq, evq_size); -} +static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx); -void falcon_init_eventq(struct efx_channel *channel) +static void falcon_prepare_flush(struct efx_nic *efx) { - efx_oword_t evq_ptr; - struct efx_nic *efx = channel->efx; - - EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n", - channel->channel, channel->eventq.index, - channel->eventq.index + channel->eventq.entries - 1); - - /* Pin event queue buffer */ - falcon_init_special_buffer(efx, &channel->eventq); + falcon_deconfigure_mac_wrapper(efx); - /* Fill event queue with all ones (i.e. empty events) */ - memset(channel->eventq.addr, 0xff, channel->eventq.len); - - /* Push event queue to card */ - EFX_POPULATE_OWORD_3(evq_ptr, - EVQ_EN, 1, - EVQ_SIZE, FALCON_EVQ_ORDER, - EVQ_BUF_BASE_ID, channel->eventq.index); - falcon_write_table(efx, &evq_ptr, efx->type->evq_ptr_tbl_base, - channel->channel); - - falcon_set_int_moderation(channel); -} - -void falcon_fini_eventq(struct efx_channel *channel) -{ - efx_oword_t eventq_ptr; - struct efx_nic *efx = channel->efx; - - /* Remove event queue from card */ - EFX_ZERO_OWORD(eventq_ptr); - falcon_write_table(efx, &eventq_ptr, efx->type->evq_ptr_tbl_base, - channel->channel); - - /* Unpin event queue */ - falcon_fini_special_buffer(efx, &channel->eventq); -} - -/* Free buffers backing event queue */ -void falcon_remove_eventq(struct efx_channel *channel) -{ - falcon_free_special_buffer(channel->efx, &channel->eventq); -} - - -/* Generates a test event on the event queue. A subsequent call to - * process_eventq() should pick up the event and place the value of - * "magic" into channel->eventq_magic; - */ -void falcon_generate_test_event(struct efx_channel *channel, unsigned int magic) -{ - efx_qword_t test_event; - - EFX_POPULATE_QWORD_2(test_event, - EV_CODE, DRV_GEN_EV_DECODE, - EVQ_MAGIC, magic); - falcon_generate_event(channel, &test_event); -} - -void falcon_sim_phy_event(struct efx_nic *efx) -{ - efx_qword_t phy_event; - - EFX_POPULATE_QWORD_1(phy_event, EV_CODE, GLOBAL_EV_DECODE); - if (EFX_IS10G(efx)) - EFX_SET_QWORD_FIELD(phy_event, XG_PHY_INTR, 1); - else - EFX_SET_QWORD_FIELD(phy_event, G_PHY0_INTR, 1); - - falcon_generate_event(&efx->channel[0], &phy_event); -} - -/************************************************************************** - * - * Flush handling - * - **************************************************************************/ - - -static void falcon_poll_flush_events(struct efx_nic *efx) -{ - struct efx_channel *channel = &efx->channel[0]; - struct efx_tx_queue *tx_queue; - struct efx_rx_queue *rx_queue; - unsigned int read_ptr = channel->eventq_read_ptr; - unsigned int end_ptr = (read_ptr - 1) & FALCON_EVQ_MASK; - - do { - efx_qword_t *event = falcon_event(channel, read_ptr); - int ev_code, ev_sub_code, ev_queue; - bool ev_failed; - - if (!falcon_event_present(event)) - break; - - ev_code = EFX_QWORD_FIELD(*event, EV_CODE); - ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); - if (ev_code == DRIVER_EV_DECODE && - ev_sub_code == TX_DESCQ_FLS_DONE_EV_DECODE) { - ev_queue = EFX_QWORD_FIELD(*event, - DRIVER_EV_TX_DESCQ_ID); - if (ev_queue < EFX_TX_QUEUE_COUNT) { - tx_queue = efx->tx_queue + ev_queue; - tx_queue->flushed = true; - } - } else if (ev_code == DRIVER_EV_DECODE && - ev_sub_code == RX_DESCQ_FLS_DONE_EV_DECODE) { - ev_queue = EFX_QWORD_FIELD(*event, - DRIVER_EV_RX_DESCQ_ID); - ev_failed = EFX_QWORD_FIELD(*event, - DRIVER_EV_RX_FLUSH_FAIL); - if (ev_queue < efx->n_rx_queues) { - rx_queue = efx->rx_queue + ev_queue; - - /* retry the rx flush */ - if (ev_failed) - falcon_flush_rx_queue(rx_queue); - else - rx_queue->flushed = true; - } - } - - read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; - } while (read_ptr != end_ptr); -} - -/* Handle tx and rx flushes at the same time, since they run in - * parallel in the hardware and there's no reason for us to - * serialise them */ -int falcon_flush_queues(struct efx_nic *efx) -{ - struct efx_rx_queue *rx_queue; - struct efx_tx_queue *tx_queue; - int i; - bool outstanding; - - /* Issue flush requests */ - efx_for_each_tx_queue(tx_queue, efx) { - tx_queue->flushed = false; - falcon_flush_tx_queue(tx_queue); - } - efx_for_each_rx_queue(rx_queue, efx) { - rx_queue->flushed = false; - falcon_flush_rx_queue(rx_queue); - } - - /* Poll the evq looking for flush completions. Since we're not pushing - * any more rx or tx descriptors at this point, we're in no danger of - * overflowing the evq whilst we wait */ - for (i = 0; i < FALCON_FLUSH_POLL_COUNT; ++i) { - msleep(FALCON_FLUSH_INTERVAL); - falcon_poll_flush_events(efx); - - /* Check if every queue has been succesfully flushed */ - outstanding = false; - efx_for_each_tx_queue(tx_queue, efx) - outstanding |= !tx_queue->flushed; - efx_for_each_rx_queue(rx_queue, efx) - outstanding |= !rx_queue->flushed; - if (!outstanding) - return 0; - } - - /* Mark the queues as all flushed. We're going to return failure - * leading to a reset, or fake up success anyway. "flushed" now - * indicates that we tried to flush. */ - efx_for_each_tx_queue(tx_queue, efx) { - if (!tx_queue->flushed) - EFX_ERR(efx, "tx queue %d flush command timed out\n", - tx_queue->queue); - tx_queue->flushed = true; - } - efx_for_each_rx_queue(rx_queue, efx) { - if (!rx_queue->flushed) - EFX_ERR(efx, "rx queue %d flush command timed out\n", - rx_queue->queue); - rx_queue->flushed = true; - } - - if (EFX_WORKAROUND_7803(efx)) - return 0; - - return -ETIMEDOUT; -} - -/************************************************************************** - * - * Falcon hardware interrupts - * The hardware interrupt handler does very little work; all the event - * queue processing is carried out by per-channel tasklets. - * - **************************************************************************/ - -/* Enable/disable/generate Falcon interrupts */ -static inline void falcon_interrupts(struct efx_nic *efx, int enabled, - int force) -{ - efx_oword_t int_en_reg_ker; - - EFX_POPULATE_OWORD_2(int_en_reg_ker, - KER_INT_KER, force, - DRV_INT_EN_KER, enabled); - falcon_write(efx, &int_en_reg_ker, INT_EN_REG_KER); -} - -void falcon_enable_interrupts(struct efx_nic *efx) -{ - efx_oword_t int_adr_reg_ker; - struct efx_channel *channel; - - EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr)); - wmb(); /* Ensure interrupt vector is clear before interrupts enabled */ - - /* Program address */ - EFX_POPULATE_OWORD_2(int_adr_reg_ker, - NORM_INT_VEC_DIS_KER, EFX_INT_MODE_USE_MSI(efx), - INT_ADR_KER, efx->irq_status.dma_addr); - falcon_write(efx, &int_adr_reg_ker, INT_ADR_REG_KER); - - /* Enable interrupts */ - falcon_interrupts(efx, 1, 0); - - /* Force processing of all the channels to get the EVQ RPTRs up to - date */ - efx_for_each_channel(channel, efx) - efx_schedule_channel(channel); -} - -void falcon_disable_interrupts(struct efx_nic *efx) -{ - /* Disable interrupts */ - falcon_interrupts(efx, 0, 0); -} - -/* Generate a Falcon test interrupt - * Interrupt must already have been enabled, otherwise nasty things - * may happen. - */ -void falcon_generate_interrupt(struct efx_nic *efx) -{ - falcon_interrupts(efx, 1, 1); + /* Wait for the tx and rx fifo's to get to the next packet boundary + * (~1ms without back-pressure), then to drain the remainder of the + * fifo's at data path speeds (negligible), with a healthy margin. */ + msleep(10); } /* Acknowledge a legacy interrupt from Falcon @@ -1364,113 +144,17 @@ void falcon_generate_interrupt(struct efx_nic *efx) * * NB most hardware supports MSI interrupts */ -static inline void falcon_irq_ack_a1(struct efx_nic *efx) -{ - efx_dword_t reg; - - EFX_POPULATE_DWORD_1(reg, INT_ACK_DUMMY_DATA, 0xb7eb7e); - falcon_writel(efx, ®, INT_ACK_REG_KER_A1); - falcon_readl(efx, ®, WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1); -} - -/* Process a fatal interrupt - * Disable bus mastering ASAP and schedule a reset - */ -static irqreturn_t falcon_fatal_interrupt(struct efx_nic *efx) +inline void falcon_irq_ack_a1(struct efx_nic *efx) { - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t *int_ker = efx->irq_status.addr; - efx_oword_t fatal_intr; - int error, mem_perr; - - falcon_read(efx, &fatal_intr, FATAL_INTR_REG_KER); - error = EFX_OWORD_FIELD(fatal_intr, INT_KER_ERROR); - - EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status " - EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker), - EFX_OWORD_VAL(fatal_intr), - error ? "disabling bus mastering" : "no recognised error"); - if (error == 0) - goto out; - - /* If this is a memory parity error dump which blocks are offending */ - mem_perr = EFX_OWORD_FIELD(fatal_intr, MEM_PERR_INT_KER); - if (mem_perr) { - efx_oword_t reg; - falcon_read(efx, ®, MEM_STAT_REG_KER); - EFX_ERR(efx, "SYSTEM ERROR: memory parity error " - EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg)); - } - - /* Disable both devices */ - pci_clear_master(efx->pci_dev); - if (FALCON_IS_DUAL_FUNC(efx)) - pci_clear_master(nic_data->pci_dev2); - falcon_disable_interrupts(efx); - - /* Count errors and reset or disable the NIC accordingly */ - if (nic_data->int_error_count == 0 || - time_after(jiffies, nic_data->int_error_expire)) { - nic_data->int_error_count = 0; - nic_data->int_error_expire = - jiffies + FALCON_INT_ERROR_EXPIRE * HZ; - } - if (++nic_data->int_error_count < FALCON_MAX_INT_ERRORS) { - EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n"); - efx_schedule_reset(efx, RESET_TYPE_INT_ERROR); - } else { - EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen." - "NIC will be disabled\n"); - efx_schedule_reset(efx, RESET_TYPE_DISABLE); - } -out: - return IRQ_HANDLED; -} - -/* Handle a legacy interrupt from Falcon - * Acknowledges the interrupt and schedule event queue processing. - */ -static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id) -{ - struct efx_nic *efx = dev_id; - efx_oword_t *int_ker = efx->irq_status.addr; - irqreturn_t result = IRQ_NONE; - struct efx_channel *channel; efx_dword_t reg; - u32 queues; - int syserr; - /* Read the ISR which also ACKs the interrupts */ - falcon_readl(efx, ®, INT_ISR0_B0); - queues = EFX_EXTRACT_DWORD(reg, 0, 31); - - /* Check to see if we have a serious error condition */ - syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); - if (unlikely(syserr)) - return falcon_fatal_interrupt(efx); - - /* Schedule processing of any interrupting queues */ - efx_for_each_channel(channel, efx) { - if ((queues & 1) || - falcon_event_present( - falcon_event(channel, channel->eventq_read_ptr))) { - efx_schedule_channel(channel); - result = IRQ_HANDLED; - } - queues >>= 1; - } - - if (result == IRQ_HANDLED) { - efx->last_irq_cpu = raw_smp_processor_id(); - EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n", - irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg)); - } - - return result; + EFX_POPULATE_DWORD_1(reg, FRF_AA_INT_ACK_KER_FIELD, 0xb7eb7e); + efx_writed(efx, ®, FR_AA_INT_ACK_KER); + efx_readd(efx, ®, FR_AA_WORK_AROUND_BROKEN_PCI_READS); } -static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) +irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) { struct efx_nic *efx = dev_id; efx_oword_t *int_ker = efx->irq_status.addr; @@ -1491,15 +175,15 @@ static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); /* Check to see if we have a serious error condition */ - syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); + syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); if (unlikely(syserr)) - return falcon_fatal_interrupt(efx); + return efx_nic_fatal_interrupt(efx); /* Determine interrupting queues, clear interrupt status * register and acknowledge the device interrupt. */ - BUILD_BUG_ON(INT_EVQS_WIDTH > EFX_MAX_CHANNELS); - queues = EFX_OWORD_FIELD(*int_ker, INT_EVQS); + BUILD_BUG_ON(FSF_AZ_NET_IVEC_INT_Q_WIDTH > EFX_MAX_CHANNELS); + queues = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_INT_Q); EFX_ZERO_OWORD(*int_ker); wmb(); /* Ensure the vector is cleared before interrupt ack */ falcon_irq_ack_a1(efx); @@ -1515,126 +199,6 @@ static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) return IRQ_HANDLED; } - -/* Handle an MSI interrupt from Falcon - * - * Handle an MSI hardware interrupt. This routine schedules event - * queue processing. No interrupt acknowledgement cycle is necessary. - * Also, we never need to check that the interrupt is for us, since - * MSI interrupts cannot be shared. - */ -static irqreturn_t falcon_msi_interrupt(int irq, void *dev_id) -{ - struct efx_channel *channel = dev_id; - struct efx_nic *efx = channel->efx; - efx_oword_t *int_ker = efx->irq_status.addr; - int syserr; - - efx->last_irq_cpu = raw_smp_processor_id(); - EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", - irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); - - /* Check to see if we have a serious error condition */ - syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); - if (unlikely(syserr)) - return falcon_fatal_interrupt(efx); - - /* Schedule processing of the channel */ - efx_schedule_channel(channel); - - return IRQ_HANDLED; -} - - -/* Setup RSS indirection table. - * This maps from the hash value of the packet to RXQ - */ -static void falcon_setup_rss_indir_table(struct efx_nic *efx) -{ - int i = 0; - unsigned long offset; - efx_dword_t dword; - - if (falcon_rev(efx) < FALCON_REV_B0) - return; - - for (offset = RX_RSS_INDIR_TBL_B0; - offset < RX_RSS_INDIR_TBL_B0 + 0x800; - offset += 0x10) { - EFX_POPULATE_DWORD_1(dword, RX_RSS_INDIR_ENT_B0, - i % efx->n_rx_queues); - falcon_writel(efx, &dword, offset); - i++; - } -} - -/* Hook interrupt handler(s) - * Try MSI and then legacy interrupts. - */ -int falcon_init_interrupt(struct efx_nic *efx) -{ - struct efx_channel *channel; - int rc; - - if (!EFX_INT_MODE_USE_MSI(efx)) { - irq_handler_t handler; - if (falcon_rev(efx) >= FALCON_REV_B0) - handler = falcon_legacy_interrupt_b0; - else - handler = falcon_legacy_interrupt_a1; - - rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED, - efx->name, efx); - if (rc) { - EFX_ERR(efx, "failed to hook legacy IRQ %d\n", - efx->pci_dev->irq); - goto fail1; - } - return 0; - } - - /* Hook MSI or MSI-X interrupt */ - efx_for_each_channel(channel, efx) { - rc = request_irq(channel->irq, falcon_msi_interrupt, - IRQF_PROBE_SHARED, /* Not shared */ - channel->name, channel); - if (rc) { - EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq); - goto fail2; - } - } - - return 0; - - fail2: - efx_for_each_channel(channel, efx) - free_irq(channel->irq, channel); - fail1: - return rc; -} - -void falcon_fini_interrupt(struct efx_nic *efx) -{ - struct efx_channel *channel; - efx_oword_t reg; - - /* Disable MSI/MSI-X interrupts */ - efx_for_each_channel(channel, efx) { - if (channel->irq) - free_irq(channel->irq, channel); - } - - /* ACK legacy interrupt */ - if (falcon_rev(efx) >= FALCON_REV_B0) - falcon_read(efx, ®, INT_ISR0_B0); - else - falcon_irq_ack_a1(efx); - - /* Disable legacy interrupt */ - if (efx->legacy_irq) - free_irq(efx->legacy_irq, efx); -} - /************************************************************************** * * EEPROM/flash @@ -1647,8 +211,8 @@ void falcon_fini_interrupt(struct efx_nic *efx) static int falcon_spi_poll(struct efx_nic *efx) { efx_oword_t reg; - falcon_read(efx, ®, EE_SPI_HCMD_REG_KER); - return EFX_OWORD_FIELD(reg, EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0; + efx_reado(efx, ®, FR_AB_EE_SPI_HCMD); + return EFX_OWORD_FIELD(reg, FRF_AB_EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0; } /* Wait for SPI command completion */ @@ -1678,11 +242,10 @@ static int falcon_spi_wait(struct efx_nic *efx) } } -int falcon_spi_cmd(const struct efx_spi_device *spi, +int falcon_spi_cmd(struct efx_nic *efx, const struct efx_spi_device *spi, unsigned int command, int address, const void *in, void *out, size_t len) { - struct efx_nic *efx = spi->efx; bool addressed = (address >= 0); bool reading = (out != NULL); efx_oword_t reg; @@ -1700,27 +263,27 @@ int falcon_spi_cmd(const struct efx_spi_device *spi, /* Program address register, if we have an address */ if (addressed) { - EFX_POPULATE_OWORD_1(reg, EE_SPI_HADR_ADR, address); - falcon_write(efx, ®, EE_SPI_HADR_REG_KER); + EFX_POPULATE_OWORD_1(reg, FRF_AB_EE_SPI_HADR_ADR, address); + efx_writeo(efx, ®, FR_AB_EE_SPI_HADR); } /* Program data register, if we have data */ if (in != NULL) { memcpy(®, in, len); - falcon_write(efx, ®, EE_SPI_HDATA_REG_KER); + efx_writeo(efx, ®, FR_AB_EE_SPI_HDATA); } /* Issue read/write command */ EFX_POPULATE_OWORD_7(reg, - EE_SPI_HCMD_CMD_EN, 1, - EE_SPI_HCMD_SF_SEL, spi->device_id, - EE_SPI_HCMD_DABCNT, len, - EE_SPI_HCMD_READ, reading, - EE_SPI_HCMD_DUBCNT, 0, - EE_SPI_HCMD_ADBCNT, + FRF_AB_EE_SPI_HCMD_CMD_EN, 1, + FRF_AB_EE_SPI_HCMD_SF_SEL, spi->device_id, + FRF_AB_EE_SPI_HCMD_DABCNT, len, + FRF_AB_EE_SPI_HCMD_READ, reading, + FRF_AB_EE_SPI_HCMD_DUBCNT, 0, + FRF_AB_EE_SPI_HCMD_ADBCNT, (addressed ? spi->addr_len : 0), - EE_SPI_HCMD_ENC, command); - falcon_write(efx, ®, EE_SPI_HCMD_REG_KER); + FRF_AB_EE_SPI_HCMD_ENC, command); + efx_writeo(efx, ®, FR_AB_EE_SPI_HCMD); /* Wait for read/write to complete */ rc = falcon_spi_wait(efx); @@ -1729,7 +292,7 @@ int falcon_spi_cmd(const struct efx_spi_device *spi, /* Read data */ if (out != NULL) { - falcon_read(efx, ®, EE_SPI_HDATA_REG_KER); + efx_reado(efx, ®, FR_AB_EE_SPI_HDATA); memcpy(out, ®, len); } @@ -1751,15 +314,15 @@ efx_spi_munge_command(const struct efx_spi_device *spi, } /* Wait up to 10 ms for buffered write completion */ -int falcon_spi_wait_write(const struct efx_spi_device *spi) +int +falcon_spi_wait_write(struct efx_nic *efx, const struct efx_spi_device *spi) { - struct efx_nic *efx = spi->efx; unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100); u8 status; int rc; for (;;) { - rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); if (rc) return rc; @@ -1775,8 +338,8 @@ int falcon_spi_wait_write(const struct efx_spi_device *spi) } } -int falcon_spi_read(const struct efx_spi_device *spi, loff_t start, - size_t len, size_t *retlen, u8 *buffer) +int falcon_spi_read(struct efx_nic *efx, const struct efx_spi_device *spi, + loff_t start, size_t len, size_t *retlen, u8 *buffer) { size_t block_len, pos = 0; unsigned int command; @@ -1786,7 +349,7 @@ int falcon_spi_read(const struct efx_spi_device *spi, loff_t start, block_len = min(len - pos, FALCON_SPI_MAX_LEN); command = efx_spi_munge_command(spi, SPI_READ, start + pos); - rc = falcon_spi_cmd(spi, command, start + pos, NULL, + rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, buffer + pos, block_len); if (rc) break; @@ -1805,8 +368,9 @@ int falcon_spi_read(const struct efx_spi_device *spi, loff_t start, return rc; } -int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, - size_t len, size_t *retlen, const u8 *buffer) +int +falcon_spi_write(struct efx_nic *efx, const struct efx_spi_device *spi, + loff_t start, size_t len, size_t *retlen, const u8 *buffer) { u8 verify_buffer[FALCON_SPI_MAX_LEN]; size_t block_len, pos = 0; @@ -1814,24 +378,24 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, int rc = 0; while (pos < len) { - rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) break; block_len = min(len - pos, falcon_spi_write_limit(spi, start + pos)); command = efx_spi_munge_command(spi, SPI_WRITE, start + pos); - rc = falcon_spi_cmd(spi, command, start + pos, + rc = falcon_spi_cmd(efx, spi, command, start + pos, buffer + pos, NULL, block_len); if (rc) break; - rc = falcon_spi_wait_write(spi); + rc = falcon_spi_wait_write(efx, spi); if (rc) break; command = efx_spi_munge_command(spi, SPI_READ, start + pos); - rc = falcon_spi_cmd(spi, command, start + pos, + rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, verify_buffer, block_len); if (memcmp(verify_buffer, buffer + pos, block_len)) { rc = -EIO; @@ -1860,60 +424,70 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, ************************************************************************** */ -static int falcon_reset_macs(struct efx_nic *efx) +static void falcon_push_multicast_hash(struct efx_nic *efx) { - efx_oword_t reg; + union efx_multicast_hash *mc_hash = &efx->multicast_hash; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + efx_writeo(efx, &mc_hash->oword[0], FR_AB_MAC_MC_HASH_REG0); + efx_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1); +} + +static void falcon_reset_macs(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t reg, mac_ctrl; int count; - if (falcon_rev(efx) < FALCON_REV_B0) { + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { /* It's not safe to use GLB_CTL_REG to reset the * macs, so instead use the internal MAC resets */ if (!EFX_IS10G(efx)) { - EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 1); - falcon_write(efx, ®, GM_CFG1_REG); + EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 1); + efx_writeo(efx, ®, FR_AB_GM_CFG1); udelay(1000); - EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 0); - falcon_write(efx, ®, GM_CFG1_REG); + EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 0); + efx_writeo(efx, ®, FR_AB_GM_CFG1); udelay(1000); - return 0; + return; } else { - EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1); - falcon_write(efx, ®, XM_GLB_CFG_REG); + EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_CORE_RST, 1); + efx_writeo(efx, ®, FR_AB_XM_GLB_CFG); for (count = 0; count < 10000; count++) { - falcon_read(efx, ®, XM_GLB_CFG_REG); - if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0) - return 0; + efx_reado(efx, ®, FR_AB_XM_GLB_CFG); + if (EFX_OWORD_FIELD(reg, FRF_AB_XM_CORE_RST) == + 0) + return; udelay(10); } EFX_ERR(efx, "timed out waiting for XMAC core reset\n"); - return -ETIMEDOUT; } } - /* MAC stats will fail whilst the TX fifo is draining. Serialise - * the drain sequence with the statistics fetch */ - efx_stats_disable(efx); + /* Mac stats will fail whist the TX fifo is draining */ + WARN_ON(nic_data->stats_disable_count == 0); - falcon_read(efx, ®, MAC0_CTRL_REG_KER); - EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, 1); - falcon_write(efx, ®, MAC0_CTRL_REG_KER); + efx_reado(efx, &mac_ctrl, FR_AB_MAC_CTRL); + EFX_SET_OWORD_FIELD(mac_ctrl, FRF_BB_TXFIFO_DRAIN_EN, 1); + efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); - falcon_read(efx, ®, GLB_CTL_REG_KER); - EFX_SET_OWORD_FIELD(reg, RST_XGTX, 1); - EFX_SET_OWORD_FIELD(reg, RST_XGRX, 1); - EFX_SET_OWORD_FIELD(reg, RST_EM, 1); - falcon_write(efx, ®, GLB_CTL_REG_KER); + efx_reado(efx, ®, FR_AB_GLB_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGTX, 1); + EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGRX, 1); + EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_EM, 1); + efx_writeo(efx, ®, FR_AB_GLB_CTL); count = 0; while (1) { - falcon_read(efx, ®, GLB_CTL_REG_KER); - if (!EFX_OWORD_FIELD(reg, RST_XGTX) && - !EFX_OWORD_FIELD(reg, RST_XGRX) && - !EFX_OWORD_FIELD(reg, RST_EM)) { + efx_reado(efx, ®, FR_AB_GLB_CTL); + if (!EFX_OWORD_FIELD(reg, FRF_AB_RST_XGTX) && + !EFX_OWORD_FIELD(reg, FRF_AB_RST_XGRX) && + !EFX_OWORD_FIELD(reg, FRF_AB_RST_EM)) { EFX_LOG(efx, "Completed MAC reset after %d loops\n", count); break; @@ -1926,55 +500,50 @@ static int falcon_reset_macs(struct efx_nic *efx) udelay(10); } - efx_stats_enable(efx); - - /* If we've reset the EM block and the link is up, then - * we'll have to kick the XAUI link so the PHY can recover */ - if (efx->link_up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx)) - falcon_reset_xaui(efx); - - return 0; + /* Ensure the correct MAC is selected before statistics + * are re-enabled by the caller */ + efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); } void falcon_drain_tx_fifo(struct efx_nic *efx) { efx_oword_t reg; - if ((falcon_rev(efx) < FALCON_REV_B0) || + if ((efx_nic_rev(efx) < EFX_REV_FALCON_B0) || (efx->loopback_mode != LOOPBACK_NONE)) return; - falcon_read(efx, ®, MAC0_CTRL_REG_KER); + efx_reado(efx, ®, FR_AB_MAC_CTRL); /* There is no point in draining more than once */ - if (EFX_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0)) + if (EFX_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN)) return; falcon_reset_macs(efx); } -void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) +static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) { efx_oword_t reg; - if (falcon_rev(efx) < FALCON_REV_B0) + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) return; /* Isolate the MAC -> RX */ - falcon_read(efx, ®, RX_CFG_REG_KER); - EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 0); - falcon_write(efx, ®, RX_CFG_REG_KER); + efx_reado(efx, ®, FR_AZ_RX_CFG); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 0); + efx_writeo(efx, ®, FR_AZ_RX_CFG); - if (!efx->link_up) - falcon_drain_tx_fifo(efx); + /* Isolate TX -> MAC */ + falcon_drain_tx_fifo(efx); } void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) { + struct efx_link_state *link_state = &efx->link_state; efx_oword_t reg; int link_speed; - bool tx_fc; - switch (efx->link_speed) { + switch (link_state->speed) { case 10000: link_speed = 3; break; case 1000: link_speed = 2; break; case 100: link_speed = 1; break; @@ -1985,75 +554,139 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) * indefinitely held and TX queue can be flushed at any point * while the link is down. */ EFX_POPULATE_OWORD_5(reg, - MAC_XOFF_VAL, 0xffff /* max pause time */, - MAC_BCAD_ACPT, 1, - MAC_UC_PROM, efx->promiscuous, - MAC_LINK_STATUS, 1, /* always set */ - MAC_SPEED, link_speed); + FRF_AB_MAC_XOFF_VAL, 0xffff /* max pause time */, + FRF_AB_MAC_BCAD_ACPT, 1, + FRF_AB_MAC_UC_PROM, efx->promiscuous, + FRF_AB_MAC_LINK_STATUS, 1, /* always set */ + FRF_AB_MAC_SPEED, link_speed); /* On B0, MAC backpressure can be disabled and packets get * discarded. */ - if (falcon_rev(efx) >= FALCON_REV_B0) { - EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, - !efx->link_up); + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + EFX_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN, + !link_state->up); } - falcon_write(efx, ®, MAC0_CTRL_REG_KER); + efx_writeo(efx, ®, FR_AB_MAC_CTRL); /* Restore the multicast hash registers. */ - falcon_set_multicast_hash(efx); - - /* Transmission of pause frames when RX crosses the threshold is - * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL. - * Action on receipt of pause frames is controller by XM_DIS_FCNTL */ - tx_fc = !!(efx->link_fc & EFX_FC_TX); - falcon_read(efx, ®, RX_CFG_REG_KER); - EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc); + falcon_push_multicast_hash(efx); + efx_reado(efx, ®, FR_AZ_RX_CFG); + /* Enable XOFF signal from RX FIFO (we enabled it during NIC + * initialisation but it may read back as 0) */ + EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); /* Unisolate the MAC -> RX */ - if (falcon_rev(efx) >= FALCON_REV_B0) - EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 1); - falcon_write(efx, ®, RX_CFG_REG_KER); + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); + efx_writeo(efx, ®, FR_AZ_RX_CFG); } -int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset) +static void falcon_stats_request(struct efx_nic *efx) { + struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t reg; - u32 *dma_done; - int i; - if (disable_dma_stats) - return 0; + WARN_ON(nic_data->stats_pending); + WARN_ON(nic_data->stats_disable_count); - /* Statistics fetch will fail if the MAC is in TX drain */ - if (falcon_rev(efx) >= FALCON_REV_B0) { - efx_oword_t temp; - falcon_read(efx, &temp, MAC0_CTRL_REG_KER); - if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0)) - return 0; - } + if (nic_data->stats_dma_done == NULL) + return; /* no mac selected */ - dma_done = (efx->stats_buffer.addr + done_offset); - *dma_done = FALCON_STATS_NOT_DONE; + *nic_data->stats_dma_done = FALCON_STATS_NOT_DONE; + nic_data->stats_pending = true; wmb(); /* ensure done flag is clear */ /* Initiate DMA transfer of stats */ EFX_POPULATE_OWORD_2(reg, - MAC_STAT_DMA_CMD, 1, - MAC_STAT_DMA_ADR, + FRF_AB_MAC_STAT_DMA_CMD, 1, + FRF_AB_MAC_STAT_DMA_ADR, efx->stats_buffer.dma_addr); - falcon_write(efx, ®, MAC0_STAT_DMA_REG_KER); + efx_writeo(efx, ®, FR_AB_MAC_STAT_DMA); - /* Wait for transfer to complete */ - for (i = 0; i < 400; i++) { - if (*(volatile u32 *)dma_done == FALCON_STATS_DONE) { - rmb(); /* Ensure the stats are valid. */ - return 0; - } - udelay(10); + mod_timer(&nic_data->stats_timer, round_jiffies_up(jiffies + HZ / 2)); +} + +static void falcon_stats_complete(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + if (!nic_data->stats_pending) + return; + + nic_data->stats_pending = 0; + if (*nic_data->stats_dma_done == FALCON_STATS_DONE) { + rmb(); /* read the done flag before the stats */ + efx->mac_op->update_stats(efx); + } else { + EFX_ERR(efx, "timed out waiting for statistics\n"); } +} - EFX_ERR(efx, "timed out waiting for statistics\n"); - return -ETIMEDOUT; +static void falcon_stats_timer_func(unsigned long context) +{ + struct efx_nic *efx = (struct efx_nic *)context; + struct falcon_nic_data *nic_data = efx->nic_data; + + spin_lock(&efx->stats_lock); + + falcon_stats_complete(efx); + if (nic_data->stats_disable_count == 0) + falcon_stats_request(efx); + + spin_unlock(&efx->stats_lock); +} + +static void falcon_switch_mac(struct efx_nic *efx); + +static bool falcon_loopback_link_poll(struct efx_nic *efx) +{ + struct efx_link_state old_state = efx->link_state; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + WARN_ON(!LOOPBACK_INTERNAL(efx)); + + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + efx->link_state.up = true; + + if (efx->loopback_mode == LOOPBACK_GMAC) + efx->link_state.speed = 1000; + else + efx->link_state.speed = 10000; + + return !efx_link_state_equal(&efx->link_state, &old_state); +} + +static int falcon_reconfigure_port(struct efx_nic *efx) +{ + int rc; + + WARN_ON(efx_nic_rev(efx) > EFX_REV_FALCON_B0); + + /* Poll the PHY link state *before* reconfiguring it. This means we + * will pick up the correct speed (in loopback) to select the correct + * MAC. + */ + if (LOOPBACK_INTERNAL(efx)) + falcon_loopback_link_poll(efx); + else + efx->phy_op->poll(efx); + + falcon_stop_nic_stats(efx); + falcon_deconfigure_mac_wrapper(efx); + + falcon_switch_mac(efx); + + efx->phy_op->reconfigure(efx); + rc = efx->mac_op->reconfigure(efx); + BUG_ON(rc); + + falcon_start_nic_stats(efx); + + /* Synchronise efx->link_state with the kernel */ + efx_link_status_changed(efx); + + return 0; } /************************************************************************** @@ -2066,18 +699,18 @@ int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset) /* Wait for GMII access to complete */ static int falcon_gmii_wait(struct efx_nic *efx) { - efx_dword_t md_stat; + efx_oword_t md_stat; int count; /* wait upto 50ms - taken max from datasheet */ for (count = 0; count < 5000; count++) { - falcon_readl(efx, &md_stat, MD_STAT_REG_KER); - if (EFX_DWORD_FIELD(md_stat, MD_BSY) == 0) { - if (EFX_DWORD_FIELD(md_stat, MD_LNFL) != 0 || - EFX_DWORD_FIELD(md_stat, MD_BSERR) != 0) { + efx_reado(efx, &md_stat, FR_AB_MD_STAT); + if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSY) == 0) { + if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_LNFL) != 0 || + EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSERR) != 0) { EFX_ERR(efx, "error from GMII access " - EFX_DWORD_FMT"\n", - EFX_DWORD_VAL(md_stat)); + EFX_OWORD_FMT"\n", + EFX_OWORD_VAL(md_stat)); return -EIO; } return 0; @@ -2099,7 +732,7 @@ static int falcon_mdio_write(struct net_device *net_dev, EFX_REGDUMP(efx, "writing MDIO %d register %d.%d with 0x%04x\n", prtad, devad, addr, value); - spin_lock_bh(&efx->phy_lock); + mutex_lock(&efx->mdio_lock); /* Check MDIO not currently being accessed */ rc = falcon_gmii_wait(efx); @@ -2107,34 +740,35 @@ static int falcon_mdio_write(struct net_device *net_dev, goto out; /* Write the address/ID register */ - EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr); - falcon_write(efx, ®, MD_PHY_ADR_REG_KER); + EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr); + efx_writeo(efx, ®, FR_AB_MD_PHY_ADR); - EFX_POPULATE_OWORD_2(reg, MD_PRT_ADR, prtad, MD_DEV_ADR, devad); - falcon_write(efx, ®, MD_ID_REG_KER); + EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad, + FRF_AB_MD_DEV_ADR, devad); + efx_writeo(efx, ®, FR_AB_MD_ID); /* Write data */ - EFX_POPULATE_OWORD_1(reg, MD_TXD, value); - falcon_write(efx, ®, MD_TXD_REG_KER); + EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_TXD, value); + efx_writeo(efx, ®, FR_AB_MD_TXD); EFX_POPULATE_OWORD_2(reg, - MD_WRC, 1, - MD_GC, 0); - falcon_write(efx, ®, MD_CS_REG_KER); + FRF_AB_MD_WRC, 1, + FRF_AB_MD_GC, 0); + efx_writeo(efx, ®, FR_AB_MD_CS); /* Wait for data to be written */ rc = falcon_gmii_wait(efx); if (rc) { /* Abort the write operation */ EFX_POPULATE_OWORD_2(reg, - MD_WRC, 0, - MD_GC, 1); - falcon_write(efx, ®, MD_CS_REG_KER); + FRF_AB_MD_WRC, 0, + FRF_AB_MD_GC, 1); + efx_writeo(efx, ®, FR_AB_MD_CS); udelay(10); } - out: - spin_unlock_bh(&efx->phy_lock); +out: + mutex_unlock(&efx->mdio_lock); return rc; } @@ -2146,152 +780,139 @@ static int falcon_mdio_read(struct net_device *net_dev, efx_oword_t reg; int rc; - spin_lock_bh(&efx->phy_lock); + mutex_lock(&efx->mdio_lock); /* Check MDIO not currently being accessed */ rc = falcon_gmii_wait(efx); if (rc) goto out; - EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr); - falcon_write(efx, ®, MD_PHY_ADR_REG_KER); + EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr); + efx_writeo(efx, ®, FR_AB_MD_PHY_ADR); - EFX_POPULATE_OWORD_2(reg, MD_PRT_ADR, prtad, MD_DEV_ADR, devad); - falcon_write(efx, ®, MD_ID_REG_KER); + EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad, + FRF_AB_MD_DEV_ADR, devad); + efx_writeo(efx, ®, FR_AB_MD_ID); /* Request data to be read */ - EFX_POPULATE_OWORD_2(reg, MD_RDC, 1, MD_GC, 0); - falcon_write(efx, ®, MD_CS_REG_KER); + EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_RDC, 1, FRF_AB_MD_GC, 0); + efx_writeo(efx, ®, FR_AB_MD_CS); /* Wait for data to become available */ rc = falcon_gmii_wait(efx); if (rc == 0) { - falcon_read(efx, ®, MD_RXD_REG_KER); - rc = EFX_OWORD_FIELD(reg, MD_RXD); + efx_reado(efx, ®, FR_AB_MD_RXD); + rc = EFX_OWORD_FIELD(reg, FRF_AB_MD_RXD); EFX_REGDUMP(efx, "read from MDIO %d register %d.%d, got %04x\n", prtad, devad, addr, rc); } else { /* Abort the read operation */ EFX_POPULATE_OWORD_2(reg, - MD_RIC, 0, - MD_GC, 1); - falcon_write(efx, ®, MD_CS_REG_KER); + FRF_AB_MD_RIC, 0, + FRF_AB_MD_GC, 1); + efx_writeo(efx, ®, FR_AB_MD_CS); EFX_LOG(efx, "read from MDIO %d register %d.%d, got error %d\n", prtad, devad, addr, rc); } - out: - spin_unlock_bh(&efx->phy_lock); +out: + mutex_unlock(&efx->mdio_lock); return rc; } -static int falcon_probe_phy(struct efx_nic *efx) +static void falcon_clock_mac(struct efx_nic *efx) { - switch (efx->phy_type) { - case PHY_TYPE_SFX7101: - efx->phy_op = &falcon_sfx7101_phy_ops; - break; - case PHY_TYPE_SFT9001A: - case PHY_TYPE_SFT9001B: - efx->phy_op = &falcon_sft9001_phy_ops; - break; - case PHY_TYPE_QT2022C2: - case PHY_TYPE_QT2025C: - efx->phy_op = &falcon_xfp_phy_ops; - break; - default: - EFX_ERR(efx, "Unknown PHY type %d\n", - efx->phy_type); - return -1; - } - - if (efx->phy_op->macs & EFX_XMAC) - efx->loopback_modes |= ((1 << LOOPBACK_XGMII) | - (1 << LOOPBACK_XGXS) | - (1 << LOOPBACK_XAUI)); - if (efx->phy_op->macs & EFX_GMAC) - efx->loopback_modes |= (1 << LOOPBACK_GMAC); - efx->loopback_modes |= efx->phy_op->loopbacks; + unsigned strap_val; + efx_oword_t nic_stat; - return 0; + /* Configure the NIC generated MAC clock correctly */ + efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); + strap_val = EFX_IS10G(efx) ? 5 : 3; + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + EFX_SET_OWORD_FIELD(nic_stat, FRF_BB_EE_STRAP_EN, 1); + EFX_SET_OWORD_FIELD(nic_stat, FRF_BB_EE_STRAP, strap_val); + efx_writeo(efx, &nic_stat, FR_AB_NIC_STAT); + } else { + /* Falcon A1 does not support 1G/10G speed switching + * and must not be used with a PHY that does. */ + BUG_ON(EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_PINS) != + strap_val); + } } -int falcon_switch_mac(struct efx_nic *efx) +static void falcon_switch_mac(struct efx_nic *efx) { struct efx_mac_operations *old_mac_op = efx->mac_op; - efx_oword_t nic_stat; - unsigned strap_val; - int rc = 0; - - /* Don't try to fetch MAC stats while we're switching MACs */ - efx_stats_disable(efx); - - /* Internal loopbacks override the phy speed setting */ - if (efx->loopback_mode == LOOPBACK_GMAC) { - efx->link_speed = 1000; - efx->link_fd = true; - } else if (LOOPBACK_INTERNAL(efx)) { - efx->link_speed = 10000; - efx->link_fd = true; - } + struct falcon_nic_data *nic_data = efx->nic_data; + unsigned int stats_done_offset; WARN_ON(!mutex_is_locked(&efx->mac_lock)); + WARN_ON(nic_data->stats_disable_count == 0); + efx->mac_op = (EFX_IS10G(efx) ? &falcon_xmac_operations : &falcon_gmac_operations); - /* Always push the NIC_STAT_REG setting even if the mac hasn't - * changed, because this function is run post online reset */ - falcon_read(efx, &nic_stat, NIC_STAT_REG); - strap_val = EFX_IS10G(efx) ? 5 : 3; - if (falcon_rev(efx) >= FALCON_REV_B0) { - EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_EN, 1); - EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_OVR, strap_val); - falcon_write(efx, &nic_stat, NIC_STAT_REG); - } else { - /* Falcon A1 does not support 1G/10G speed switching - * and must not be used with a PHY that does. */ - BUG_ON(EFX_OWORD_FIELD(nic_stat, STRAP_PINS) != strap_val); - } + if (EFX_IS10G(efx)) + stats_done_offset = XgDmaDone_offset; + else + stats_done_offset = GDmaDone_offset; + nic_data->stats_dma_done = efx->stats_buffer.addr + stats_done_offset; if (old_mac_op == efx->mac_op) - goto out; + return; + + falcon_clock_mac(efx); EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G'); /* Not all macs support a mac-level link state */ - efx->mac_up = true; - - rc = falcon_reset_macs(efx); -out: - efx_stats_enable(efx); - return rc; + efx->xmac_poll_required = false; + falcon_reset_macs(efx); } /* This call is responsible for hooking in the MAC and PHY operations */ -int falcon_probe_port(struct efx_nic *efx) +static int falcon_probe_port(struct efx_nic *efx) { int rc; - /* Hook in PHY operations table */ - rc = falcon_probe_phy(efx); - if (rc) - return rc; + switch (efx->phy_type) { + case PHY_TYPE_SFX7101: + efx->phy_op = &falcon_sfx7101_phy_ops; + break; + case PHY_TYPE_SFT9001A: + case PHY_TYPE_SFT9001B: + efx->phy_op = &falcon_sft9001_phy_ops; + break; + case PHY_TYPE_QT2022C2: + case PHY_TYPE_QT2025C: + efx->phy_op = &falcon_qt202x_phy_ops; + break; + default: + EFX_ERR(efx, "Unknown PHY type %d\n", + efx->phy_type); + return -ENODEV; + } - /* Set up MDIO structure for PHY */ - efx->mdio.mmds = efx->phy_op->mmds; - efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + /* Fill out MDIO structure and loopback modes */ efx->mdio.mdio_read = falcon_mdio_read; efx->mdio.mdio_write = falcon_mdio_write; + rc = efx->phy_op->probe(efx); + if (rc != 0) + return rc; + + /* Initial assumption */ + efx->link_state.speed = 10000; + efx->link_state.fd = true; /* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */ - if (falcon_rev(efx) >= FALCON_REV_B0) + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; else efx->wanted_fc = EFX_FC_RX; /* Allocate buffer for stats */ - rc = falcon_alloc_buffer(efx, &efx->stats_buffer, - FALCON_MAC_STATS_SIZE); + rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer, + FALCON_MAC_STATS_SIZE); if (rc) return rc; EFX_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n", @@ -2302,40 +923,19 @@ int falcon_probe_port(struct efx_nic *efx) return 0; } -void falcon_remove_port(struct efx_nic *efx) +static void falcon_remove_port(struct efx_nic *efx) { - falcon_free_buffer(efx, &efx->stats_buffer); + efx_nic_free_buffer(efx, &efx->stats_buffer); } /************************************************************************** * - * Multicast filtering - * - ************************************************************************** - */ - -void falcon_set_multicast_hash(struct efx_nic *efx) -{ - union efx_multicast_hash *mc_hash = &efx->multicast_hash; - - /* Broadcast packets go through the multicast hash filter. - * ether_crc_le() of the broadcast address is 0xbe2612ff - * so we always add bit 0xff to the mask. - */ - set_bit_le(0xff, mc_hash->byte); - - falcon_write(efx, &mc_hash->oword[0], MAC_MCAST_HASH_REG0_KER); - falcon_write(efx, &mc_hash->oword[1], MAC_MCAST_HASH_REG1_KER); -} - - -/************************************************************************** - * * Falcon test code * **************************************************************************/ -int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) +static int +falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) { struct falcon_nvconfig *nvconfig; struct efx_spi_device *spi; @@ -2351,10 +951,10 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL); if (!region) return -ENOMEM; - nvconfig = region + NVCONFIG_OFFSET; + nvconfig = region + FALCON_NVCONFIG_OFFSET; mutex_lock(&efx->spi_lock); - rc = falcon_spi_read(spi, 0, FALCON_NVCONFIG_END, NULL, region); + rc = falcon_spi_read(efx, spi, 0, FALCON_NVCONFIG_END, NULL, region); mutex_unlock(&efx->spi_lock); if (rc) { EFX_ERR(efx, "Failed to read %s\n", @@ -2367,7 +967,7 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) struct_ver = le16_to_cpu(nvconfig->board_struct_ver); rc = -EINVAL; - if (magic_num != NVCONFIG_BOARD_MAGIC_NUM) { + if (magic_num != FALCON_NVCONFIG_BOARD_MAGIC_NUM) { EFX_ERR(efx, "NVRAM bad magic 0x%x\n", magic_num); goto out; } @@ -2398,107 +998,54 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) return rc; } -/* Registers tested in the falcon register test */ -static struct { - unsigned address; - efx_oword_t mask; -} efx_test_registers[] = { - { ADR_REGION_REG_KER, +static int falcon_test_nvram(struct efx_nic *efx) +{ + return falcon_read_nvram(efx, NULL); +} + +static const struct efx_nic_register_test falcon_b0_register_tests[] = { + { FR_AZ_ADR_REGION, EFX_OWORD32(0x0001FFFF, 0x0001FFFF, 0x0001FFFF, 0x0001FFFF) }, - { RX_CFG_REG_KER, + { FR_AZ_RX_CFG, EFX_OWORD32(0xFFFFFFFE, 0x00017FFF, 0x00000000, 0x00000000) }, - { TX_CFG_REG_KER, + { FR_AZ_TX_CFG, EFX_OWORD32(0x7FFF0037, 0x00000000, 0x00000000, 0x00000000) }, - { TX_CFG2_REG_KER, + { FR_AZ_TX_RESERVED, EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) }, - { MAC0_CTRL_REG_KER, + { FR_AB_MAC_CTRL, EFX_OWORD32(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000) }, - { SRM_TX_DC_CFG_REG_KER, + { FR_AZ_SRM_TX_DC_CFG, EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) }, - { RX_DC_CFG_REG_KER, + { FR_AZ_RX_DC_CFG, EFX_OWORD32(0x0000000F, 0x00000000, 0x00000000, 0x00000000) }, - { RX_DC_PF_WM_REG_KER, + { FR_AZ_RX_DC_PF_WM, EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) }, - { DP_CTRL_REG, + { FR_BZ_DP_CTRL, EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) }, - { GM_CFG2_REG, + { FR_AB_GM_CFG2, EFX_OWORD32(0x00007337, 0x00000000, 0x00000000, 0x00000000) }, - { GMF_CFG0_REG, + { FR_AB_GMF_CFG0, EFX_OWORD32(0x00001F1F, 0x00000000, 0x00000000, 0x00000000) }, - { XM_GLB_CFG_REG, + { FR_AB_XM_GLB_CFG, EFX_OWORD32(0x00000C68, 0x00000000, 0x00000000, 0x00000000) }, - { XM_TX_CFG_REG, + { FR_AB_XM_TX_CFG, EFX_OWORD32(0x00080164, 0x00000000, 0x00000000, 0x00000000) }, - { XM_RX_CFG_REG, + { FR_AB_XM_RX_CFG, EFX_OWORD32(0x07100A0C, 0x00000000, 0x00000000, 0x00000000) }, - { XM_RX_PARAM_REG, + { FR_AB_XM_RX_PARAM, EFX_OWORD32(0x00001FF8, 0x00000000, 0x00000000, 0x00000000) }, - { XM_FC_REG, + { FR_AB_XM_FC, EFX_OWORD32(0xFFFF0001, 0x00000000, 0x00000000, 0x00000000) }, - { XM_ADR_LO_REG, + { FR_AB_XM_ADR_LO, EFX_OWORD32(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000) }, - { XX_SD_CTL_REG, + { FR_AB_XX_SD_CTL, EFX_OWORD32(0x0003FF0F, 0x00000000, 0x00000000, 0x00000000) }, }; -static bool efx_masked_compare_oword(const efx_oword_t *a, const efx_oword_t *b, - const efx_oword_t *mask) +static int falcon_b0_test_registers(struct efx_nic *efx) { - return ((a->u64[0] ^ b->u64[0]) & mask->u64[0]) || - ((a->u64[1] ^ b->u64[1]) & mask->u64[1]); -} - -int falcon_test_registers(struct efx_nic *efx) -{ - unsigned address = 0, i, j; - efx_oword_t mask, imask, original, reg, buf; - - /* Falcon should be in loopback to isolate the XMAC from the PHY */ - WARN_ON(!LOOPBACK_INTERNAL(efx)); - - for (i = 0; i < ARRAY_SIZE(efx_test_registers); ++i) { - address = efx_test_registers[i].address; - mask = imask = efx_test_registers[i].mask; - EFX_INVERT_OWORD(imask); - - falcon_read(efx, &original, address); - - /* bit sweep on and off */ - for (j = 0; j < 128; j++) { - if (!EFX_EXTRACT_OWORD32(mask, j, j)) - continue; - - /* Test this testable bit can be set in isolation */ - EFX_AND_OWORD(reg, original, mask); - EFX_SET_OWORD32(reg, j, j, 1); - - falcon_write(efx, ®, address); - falcon_read(efx, &buf, address); - - if (efx_masked_compare_oword(®, &buf, &mask)) - goto fail; - - /* Test this testable bit can be cleared in isolation */ - EFX_OR_OWORD(reg, original, mask); - EFX_SET_OWORD32(reg, j, j, 0); - - falcon_write(efx, ®, address); - falcon_read(efx, &buf, address); - - if (efx_masked_compare_oword(®, &buf, &mask)) - goto fail; - } - - falcon_write(efx, &original, address); - } - - return 0; - -fail: - EFX_ERR(efx, "wrote "EFX_OWORD_FMT" read "EFX_OWORD_FMT - " at address 0x%x mask "EFX_OWORD_FMT"\n", EFX_OWORD_VAL(reg), - EFX_OWORD_VAL(buf), address, EFX_OWORD_VAL(mask)); - return -EIO; + return efx_nic_test_registers(efx, falcon_b0_register_tests, + ARRAY_SIZE(falcon_b0_register_tests)); } /************************************************************************** @@ -2510,13 +1057,13 @@ fail: /* Resets NIC to known state. This routine must be called in process * context and is allowed to sleep. */ -int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) +static int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) { struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t glb_ctl_reg_ker; int rc; - EFX_LOG(efx, "performing hardware reset (%d)\n", method); + EFX_LOG(efx, "performing %s hardware reset\n", RESET_TYPE(method)); /* Initiate device reset */ if (method == RESET_TYPE_WORLD) { @@ -2526,7 +1073,7 @@ int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) "function prior to hardware reset\n"); goto fail1; } - if (FALCON_IS_DUAL_FUNC(efx)) { + if (efx_nic_is_dual_func(efx)) { rc = pci_save_state(nic_data->pci_dev2); if (rc) { EFX_ERR(efx, "failed to backup PCI state of " @@ -2537,29 +1084,31 @@ int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) } EFX_POPULATE_OWORD_2(glb_ctl_reg_ker, - EXT_PHY_RST_DUR, 0x7, - SWRST, 1); + FRF_AB_EXT_PHY_RST_DUR, + FFE_AB_EXT_PHY_RST_DUR_10240US, + FRF_AB_SWRST, 1); } else { - int reset_phy = (method == RESET_TYPE_INVISIBLE ? - EXCLUDE_FROM_RESET : 0); - EFX_POPULATE_OWORD_7(glb_ctl_reg_ker, - EXT_PHY_RST_CTL, reset_phy, - PCIE_CORE_RST_CTL, EXCLUDE_FROM_RESET, - PCIE_NSTCK_RST_CTL, EXCLUDE_FROM_RESET, - PCIE_SD_RST_CTL, EXCLUDE_FROM_RESET, - EE_RST_CTL, EXCLUDE_FROM_RESET, - EXT_PHY_RST_DUR, 0x7 /* 10ms */, - SWRST, 1); - } - falcon_write(efx, &glb_ctl_reg_ker, GLB_CTL_REG_KER); + /* exclude PHY from "invisible" reset */ + FRF_AB_EXT_PHY_RST_CTL, + method == RESET_TYPE_INVISIBLE, + /* exclude EEPROM/flash and PCIe */ + FRF_AB_PCIE_CORE_RST_CTL, 1, + FRF_AB_PCIE_NSTKY_RST_CTL, 1, + FRF_AB_PCIE_SD_RST_CTL, 1, + FRF_AB_EE_RST_CTL, 1, + FRF_AB_EXT_PHY_RST_DUR, + FFE_AB_EXT_PHY_RST_DUR_10240US, + FRF_AB_SWRST, 1); + } + efx_writeo(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL); EFX_LOG(efx, "waiting for hardware reset\n"); schedule_timeout_uninterruptible(HZ / 20); /* Restore PCI configuration if needed */ if (method == RESET_TYPE_WORLD) { - if (FALCON_IS_DUAL_FUNC(efx)) { + if (efx_nic_is_dual_func(efx)) { rc = pci_restore_state(nic_data->pci_dev2); if (rc) { EFX_ERR(efx, "failed to restore PCI config for " @@ -2577,8 +1126,8 @@ int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) } /* Assert that reset complete */ - falcon_read(efx, &glb_ctl_reg_ker, GLB_CTL_REG_KER); - if (EFX_OWORD_FIELD(glb_ctl_reg_ker, SWRST) != 0) { + efx_reado(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL); + if (EFX_OWORD_FIELD(glb_ctl_reg_ker, FRF_AB_SWRST) != 0) { rc = -ETIMEDOUT; EFX_ERR(efx, "timed out waiting for hardware reset\n"); goto fail5; @@ -2597,6 +1146,44 @@ fail5: return rc; } +static void falcon_monitor(struct efx_nic *efx) +{ + bool link_changed; + int rc; + + BUG_ON(!mutex_is_locked(&efx->mac_lock)); + + rc = falcon_board(efx)->type->monitor(efx); + if (rc) { + EFX_ERR(efx, "Board sensor %s; shutting down PHY\n", + (rc == -ERANGE) ? "reported fault" : "failed"); + efx->phy_mode |= PHY_MODE_LOW_POWER; + rc = __efx_reconfigure_port(efx); + WARN_ON(rc); + } + + if (LOOPBACK_INTERNAL(efx)) + link_changed = falcon_loopback_link_poll(efx); + else + link_changed = efx->phy_op->poll(efx); + + if (link_changed) { + falcon_stop_nic_stats(efx); + falcon_deconfigure_mac_wrapper(efx); + + falcon_switch_mac(efx); + rc = efx->mac_op->reconfigure(efx); + BUG_ON(rc); + + falcon_start_nic_stats(efx); + + efx_link_status_changed(efx); + } + + if (EFX_IS10G(efx)) + falcon_poll_xmac(efx); +} + /* Zeroes out the SRAM contents. This routine must be called in * process context and is allowed to sleep. */ @@ -2606,16 +1193,16 @@ static int falcon_reset_sram(struct efx_nic *efx) int count; /* Set the SRAM wake/sleep GPIO appropriately. */ - falcon_read(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER); - EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OEN, 1); - EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OUT, 1); - falcon_write(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER); + efx_reado(efx, &gpio_cfg_reg_ker, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, FRF_AB_GPIO1_OEN, 1); + EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, FRF_AB_GPIO1_OUT, 1); + efx_writeo(efx, &gpio_cfg_reg_ker, FR_AB_GPIO_CTL); /* Initiate SRAM reset */ EFX_POPULATE_OWORD_2(srm_cfg_reg_ker, - SRAM_OOB_BT_INIT_EN, 1, - SRM_NUM_BANKS_AND_BANK_SIZE, 0); - falcon_write(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER); + FRF_AZ_SRM_INIT_EN, 1, + FRF_AZ_SRM_NB_SZ, 0); + efx_writeo(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG); /* Wait for SRAM reset to complete */ count = 0; @@ -2626,8 +1213,8 @@ static int falcon_reset_sram(struct efx_nic *efx) schedule_timeout_uninterruptible(HZ / 50); /* Check for reset complete */ - falcon_read(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER); - if (!EFX_OWORD_FIELD(srm_cfg_reg_ker, SRAM_OOB_BT_INIT_EN)) { + efx_reado(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG); + if (!EFX_OWORD_FIELD(srm_cfg_reg_ker, FRF_AZ_SRM_INIT_EN)) { EFX_LOG(efx, "SRAM reset complete\n"); return 0; @@ -2663,8 +1250,6 @@ static int falcon_spi_device_init(struct efx_nic *efx, spi_device->block_size = 1 << SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_BLOCK_SIZE); - - spi_device->efx = efx; } else { spi_device = NULL; } @@ -2674,7 +1259,6 @@ static int falcon_spi_device_init(struct efx_nic *efx, return 0; } - static void falcon_remove_spi_devices(struct efx_nic *efx) { kfree(efx->spi_eeprom); @@ -2712,16 +1296,16 @@ static int falcon_probe_nvconfig(struct efx_nic *efx) board_rev = le16_to_cpu(v2->board_revision); if (le16_to_cpu(nvconfig->board_struct_ver) >= 3) { - __le32 fl = v3->spi_device_type[EE_SPI_FLASH]; - __le32 ee = v3->spi_device_type[EE_SPI_EEPROM]; - rc = falcon_spi_device_init(efx, &efx->spi_flash, - EE_SPI_FLASH, - le32_to_cpu(fl)); + rc = falcon_spi_device_init( + efx, &efx->spi_flash, FFE_AB_SPI_DEVICE_FLASH, + le32_to_cpu(v3->spi_device_type + [FFE_AB_SPI_DEVICE_FLASH])); if (rc) goto fail2; - rc = falcon_spi_device_init(efx, &efx->spi_eeprom, - EE_SPI_EEPROM, - le32_to_cpu(ee)); + rc = falcon_spi_device_init( + efx, &efx->spi_eeprom, FFE_AB_SPI_DEVICE_EEPROM, + le32_to_cpu(v3->spi_device_type + [FFE_AB_SPI_DEVICE_EEPROM])); if (rc) goto fail2; } @@ -2732,7 +1316,7 @@ static int falcon_probe_nvconfig(struct efx_nic *efx) EFX_LOG(efx, "PHY is %d phy_id %d\n", efx->phy_type, efx->mdio.prtad); - efx_set_board_info(efx, board_rev); + falcon_probe_board(efx, board_rev); kfree(nvconfig); return 0; @@ -2744,89 +1328,49 @@ static int falcon_probe_nvconfig(struct efx_nic *efx) return rc; } -/* Probe the NIC variant (revision, ASIC vs FPGA, function count, port - * count, port speed). Set workaround and feature flags accordingly. - */ -static int falcon_probe_nic_variant(struct efx_nic *efx) -{ - efx_oword_t altera_build; - efx_oword_t nic_stat; - - falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER); - if (EFX_OWORD_FIELD(altera_build, VER_ALL)) { - EFX_ERR(efx, "Falcon FPGA not supported\n"); - return -ENODEV; - } - - falcon_read(efx, &nic_stat, NIC_STAT_REG); - - switch (falcon_rev(efx)) { - case FALCON_REV_A0: - case 0xff: - EFX_ERR(efx, "Falcon rev A0 not supported\n"); - return -ENODEV; - - case FALCON_REV_A1: - if (EFX_OWORD_FIELD(nic_stat, STRAP_PCIE) == 0) { - EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n"); - return -ENODEV; - } - break; - - case FALCON_REV_B0: - break; - - default: - EFX_ERR(efx, "Unknown Falcon rev %d\n", falcon_rev(efx)); - return -ENODEV; - } - - /* Initial assumed speed */ - efx->link_speed = EFX_OWORD_FIELD(nic_stat, STRAP_10G) ? 10000 : 1000; - - return 0; -} - /* Probe all SPI devices on the NIC */ static void falcon_probe_spi_devices(struct efx_nic *efx) { efx_oword_t nic_stat, gpio_ctl, ee_vpd_cfg; int boot_dev; - falcon_read(efx, &gpio_ctl, GPIO_CTL_REG_KER); - falcon_read(efx, &nic_stat, NIC_STAT_REG); - falcon_read(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER); + efx_reado(efx, &gpio_ctl, FR_AB_GPIO_CTL); + efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); + efx_reado(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0); - if (EFX_OWORD_FIELD(gpio_ctl, BOOTED_USING_NVDEVICE)) { - boot_dev = (EFX_OWORD_FIELD(nic_stat, SF_PRST) ? - EE_SPI_FLASH : EE_SPI_EEPROM); + if (EFX_OWORD_FIELD(gpio_ctl, FRF_AB_GPIO3_PWRUP_VALUE)) { + boot_dev = (EFX_OWORD_FIELD(nic_stat, FRF_AB_SF_PRST) ? + FFE_AB_SPI_DEVICE_FLASH : FFE_AB_SPI_DEVICE_EEPROM); EFX_LOG(efx, "Booted from %s\n", - boot_dev == EE_SPI_FLASH ? "flash" : "EEPROM"); + boot_dev == FFE_AB_SPI_DEVICE_FLASH ? "flash" : "EEPROM"); } else { /* Disable VPD and set clock dividers to safe * values for initial programming. */ boot_dev = -1; EFX_LOG(efx, "Booted from internal ASIC settings;" " setting SPI config\n"); - EFX_POPULATE_OWORD_3(ee_vpd_cfg, EE_VPD_EN, 0, + EFX_POPULATE_OWORD_3(ee_vpd_cfg, FRF_AB_EE_VPD_EN, 0, /* 125 MHz / 7 ~= 20 MHz */ - EE_SF_CLOCK_DIV, 7, + FRF_AB_EE_SF_CLOCK_DIV, 7, /* 125 MHz / 63 ~= 2 MHz */ - EE_EE_CLOCK_DIV, 63); - falcon_write(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER); + FRF_AB_EE_EE_CLOCK_DIV, 63); + efx_writeo(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0); } - if (boot_dev == EE_SPI_FLASH) - falcon_spi_device_init(efx, &efx->spi_flash, EE_SPI_FLASH, + if (boot_dev == FFE_AB_SPI_DEVICE_FLASH) + falcon_spi_device_init(efx, &efx->spi_flash, + FFE_AB_SPI_DEVICE_FLASH, default_flash_type); - if (boot_dev == EE_SPI_EEPROM) - falcon_spi_device_init(efx, &efx->spi_eeprom, EE_SPI_EEPROM, + if (boot_dev == FFE_AB_SPI_DEVICE_EEPROM) + falcon_spi_device_init(efx, &efx->spi_eeprom, + FFE_AB_SPI_DEVICE_EEPROM, large_eeprom_type); } -int falcon_probe_nic(struct efx_nic *efx) +static int falcon_probe_nic(struct efx_nic *efx) { struct falcon_nic_data *nic_data; + struct falcon_board *board; int rc; /* Allocate storage for hardware specific data */ @@ -2835,15 +1379,33 @@ int falcon_probe_nic(struct efx_nic *efx) return -ENOMEM; efx->nic_data = nic_data; - /* Determine number of ports etc. */ - rc = falcon_probe_nic_variant(efx); - if (rc) + rc = -ENODEV; + + if (efx_nic_fpga_ver(efx) != 0) { + EFX_ERR(efx, "Falcon FPGA not supported\n"); goto fail1; + } - /* Probe secondary function if expected */ - if (FALCON_IS_DUAL_FUNC(efx)) { - struct pci_dev *dev = pci_dev_get(efx->pci_dev); + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { + efx_oword_t nic_stat; + struct pci_dev *dev; + u8 pci_rev = efx->pci_dev->revision; + + if ((pci_rev == 0xff) || (pci_rev == 0)) { + EFX_ERR(efx, "Falcon rev A0 not supported\n"); + goto fail1; + } + efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); + if (EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_10G) == 0) { + EFX_ERR(efx, "Falcon rev A1 1G not supported\n"); + goto fail1; + } + if (EFX_OWORD_FIELD(nic_stat, FRF_AA_STRAP_PCIE) == 0) { + EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n"); + goto fail1; + } + dev = pci_dev_get(efx->pci_dev); while ((dev = pci_get_device(EFX_VENDID_SFC, FALCON_A_S_DEVID, dev))) { if (dev->bus == efx->pci_dev->bus && @@ -2867,7 +1429,7 @@ int falcon_probe_nic(struct efx_nic *efx) } /* Allocate memory for INT_KER */ - rc = falcon_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); + rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); if (rc) goto fail4; BUG_ON(efx->irq_status.dma_addr & 0x0f); @@ -2884,21 +1446,36 @@ int falcon_probe_nic(struct efx_nic *efx) goto fail5; /* Initialise I2C adapter */ - efx->i2c_adap.owner = THIS_MODULE; - nic_data->i2c_data = falcon_i2c_bit_operations; - nic_data->i2c_data.data = efx; - efx->i2c_adap.algo_data = &nic_data->i2c_data; - efx->i2c_adap.dev.parent = &efx->pci_dev->dev; - strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name)); - rc = i2c_bit_add_bus(&efx->i2c_adap); + board = falcon_board(efx); + board->i2c_adap.owner = THIS_MODULE; + board->i2c_data = falcon_i2c_bit_operations; + board->i2c_data.data = efx; + board->i2c_adap.algo_data = &board->i2c_data; + board->i2c_adap.dev.parent = &efx->pci_dev->dev; + strlcpy(board->i2c_adap.name, "SFC4000 GPIO", + sizeof(board->i2c_adap.name)); + rc = i2c_bit_add_bus(&board->i2c_adap); if (rc) goto fail5; + rc = falcon_board(efx)->type->init(efx); + if (rc) { + EFX_ERR(efx, "failed to initialise board\n"); + goto fail6; + } + + nic_data->stats_disable_count = 1; + setup_timer(&nic_data->stats_timer, &falcon_stats_timer_func, + (unsigned long)efx); + return 0; + fail6: + BUG_ON(i2c_del_adapter(&board->i2c_adap)); + memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); fail5: falcon_remove_spi_devices(efx); - falcon_free_buffer(efx, &efx->irq_status); + efx_nic_free_buffer(efx, &efx->irq_status); fail4: fail3: if (nic_data->pci_dev2) { @@ -2911,166 +1488,147 @@ int falcon_probe_nic(struct efx_nic *efx) return rc; } +static void falcon_init_rx_cfg(struct efx_nic *efx) +{ + /* Prior to Siena the RX DMA engine will split each frame at + * intervals of RX_USR_BUF_SIZE (32-byte units). We set it to + * be so large that that never happens. */ + const unsigned huge_buf_size = (3 * 4096) >> 5; + /* RX control FIFO thresholds (32 entries) */ + const unsigned ctrl_xon_thr = 20; + const unsigned ctrl_xoff_thr = 25; + /* RX data FIFO thresholds (256-byte units; size varies) */ + int data_xon_thr = efx_nic_rx_xon_thresh >> 8; + int data_xoff_thr = efx_nic_rx_xoff_thresh >> 8; + efx_oword_t reg; + + efx_reado(efx, ®, FR_AZ_RX_CFG); + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { + /* Data FIFO size is 5.5K */ + if (data_xon_thr < 0) + data_xon_thr = 512 >> 8; + if (data_xoff_thr < 0) + data_xoff_thr = 2048 >> 8; + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_DESC_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_USR_BUF_SIZE, + huge_buf_size); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_MAC_TH, data_xon_thr); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_MAC_TH, data_xoff_thr); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_TX_TH, ctrl_xon_thr); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_TX_TH, ctrl_xoff_thr); + } else { + /* Data FIFO size is 80K; register fields moved */ + if (data_xon_thr < 0) + data_xon_thr = 27648 >> 8; /* ~3*max MTU */ + if (data_xoff_thr < 0) + data_xoff_thr = 54272 >> 8; /* ~80Kb - 3*max MTU */ + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_DESC_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_USR_BUF_SIZE, + huge_buf_size); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_MAC_TH, data_xon_thr); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_MAC_TH, data_xoff_thr); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_TX_TH, ctrl_xon_thr); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_TX_TH, ctrl_xoff_thr); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); + } + /* Always enable XOFF signal from RX FIFO. We enable + * or disable transmission of pause frames at the MAC. */ + EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); + efx_writeo(efx, ®, FR_AZ_RX_CFG); +} + /* This call performs hardware-specific global initialisation, such as * defining the descriptor cache sizes and number of RSS channels. * It does not set up any buffers, descriptor rings or event queues. */ -int falcon_init_nic(struct efx_nic *efx) +static int falcon_init_nic(struct efx_nic *efx) { efx_oword_t temp; - unsigned thresh; int rc; /* Use on-chip SRAM */ - falcon_read(efx, &temp, NIC_STAT_REG); - EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, 1); - falcon_write(efx, &temp, NIC_STAT_REG); + efx_reado(efx, &temp, FR_AB_NIC_STAT); + EFX_SET_OWORD_FIELD(temp, FRF_AB_ONCHIP_SRAM, 1); + efx_writeo(efx, &temp, FR_AB_NIC_STAT); /* Set the source of the GMAC clock */ - if (falcon_rev(efx) == FALCON_REV_B0) { - falcon_read(efx, &temp, GPIO_CTL_REG_KER); - EFX_SET_OWORD_FIELD(temp, GPIO_USE_NIC_CLK, true); - falcon_write(efx, &temp, GPIO_CTL_REG_KER); + if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { + efx_reado(efx, &temp, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(temp, FRF_AB_USE_NIC_CLK, true); + efx_writeo(efx, &temp, FR_AB_GPIO_CTL); } - /* Set buffer table mode */ - EFX_POPULATE_OWORD_1(temp, BUF_TBL_MODE, BUF_TBL_MODE_FULL); - falcon_write(efx, &temp, BUF_TBL_CFG_REG_KER); + /* Select the correct MAC */ + falcon_clock_mac(efx); rc = falcon_reset_sram(efx); if (rc) return rc; - /* Set positions of descriptor caches in SRAM. */ - EFX_POPULATE_OWORD_1(temp, SRM_TX_DC_BASE_ADR, TX_DC_BASE / 8); - falcon_write(efx, &temp, SRM_TX_DC_CFG_REG_KER); - EFX_POPULATE_OWORD_1(temp, SRM_RX_DC_BASE_ADR, RX_DC_BASE / 8); - falcon_write(efx, &temp, SRM_RX_DC_CFG_REG_KER); - - /* Set TX descriptor cache size. */ - BUILD_BUG_ON(TX_DC_ENTRIES != (16 << TX_DC_ENTRIES_ORDER)); - EFX_POPULATE_OWORD_1(temp, TX_DC_SIZE, TX_DC_ENTRIES_ORDER); - falcon_write(efx, &temp, TX_DC_CFG_REG_KER); - - /* Set RX descriptor cache size. Set low watermark to size-8, as - * this allows most efficient prefetching. - */ - BUILD_BUG_ON(RX_DC_ENTRIES != (16 << RX_DC_ENTRIES_ORDER)); - EFX_POPULATE_OWORD_1(temp, RX_DC_SIZE, RX_DC_ENTRIES_ORDER); - falcon_write(efx, &temp, RX_DC_CFG_REG_KER); - EFX_POPULATE_OWORD_1(temp, RX_DC_PF_LWM, RX_DC_ENTRIES - 8); - falcon_write(efx, &temp, RX_DC_PF_WM_REG_KER); - /* Clear the parity enables on the TX data fifos as * they produce false parity errors because of timing issues */ if (EFX_WORKAROUND_5129(efx)) { - falcon_read(efx, &temp, SPARE_REG_KER); - EFX_SET_OWORD_FIELD(temp, MEM_PERR_EN_TX_DATA, 0); - falcon_write(efx, &temp, SPARE_REG_KER); + efx_reado(efx, &temp, FR_AZ_CSR_SPARE); + EFX_SET_OWORD_FIELD(temp, FRF_AB_MEM_PERR_EN_TX_DATA, 0); + efx_writeo(efx, &temp, FR_AZ_CSR_SPARE); } - /* Enable all the genuinely fatal interrupts. (They are still - * masked by the overall interrupt mask, controlled by - * falcon_interrupts()). - * - * Note: All other fatal interrupts are enabled - */ - EFX_POPULATE_OWORD_3(temp, - ILL_ADR_INT_KER_EN, 1, - RBUF_OWN_INT_KER_EN, 1, - TBUF_OWN_INT_KER_EN, 1); - EFX_INVERT_OWORD(temp); - falcon_write(efx, &temp, FATAL_INTR_REG_KER); - if (EFX_WORKAROUND_7244(efx)) { - falcon_read(efx, &temp, RX_FILTER_CTL_REG); - EFX_SET_OWORD_FIELD(temp, UDP_FULL_SRCH_LIMIT, 8); - EFX_SET_OWORD_FIELD(temp, UDP_WILD_SRCH_LIMIT, 8); - EFX_SET_OWORD_FIELD(temp, TCP_FULL_SRCH_LIMIT, 8); - EFX_SET_OWORD_FIELD(temp, TCP_WILD_SRCH_LIMIT, 8); - falcon_write(efx, &temp, RX_FILTER_CTL_REG); + efx_reado(efx, &temp, FR_BZ_RX_FILTER_CTL); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_FULL_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_WILD_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TCP_FULL_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TCP_WILD_SRCH_LIMIT, 8); + efx_writeo(efx, &temp, FR_BZ_RX_FILTER_CTL); } - falcon_setup_rss_indir_table(efx); - + /* XXX This is documented only for Falcon A0/A1 */ /* Setup RX. Wait for descriptor is broken and must * be disabled. RXDP recovery shouldn't be needed, but is. */ - falcon_read(efx, &temp, RX_SELF_RST_REG_KER); - EFX_SET_OWORD_FIELD(temp, RX_NODESC_WAIT_DIS, 1); - EFX_SET_OWORD_FIELD(temp, RX_RECOVERY_EN, 1); + efx_reado(efx, &temp, FR_AA_RX_SELF_RST); + EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_NODESC_WAIT_DIS, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_SELF_RST_EN, 1); if (EFX_WORKAROUND_5583(efx)) - EFX_SET_OWORD_FIELD(temp, RX_ISCSI_DIS, 1); - falcon_write(efx, &temp, RX_SELF_RST_REG_KER); - - /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be - * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q. - */ - falcon_read(efx, &temp, TX_CFG2_REG_KER); - EFX_SET_OWORD_FIELD(temp, TX_RX_SPACER, 0xfe); - EFX_SET_OWORD_FIELD(temp, TX_RX_SPACER_EN, 1); - EFX_SET_OWORD_FIELD(temp, TX_ONE_PKT_PER_Q, 1); - EFX_SET_OWORD_FIELD(temp, TX_CSR_PUSH_EN, 0); - EFX_SET_OWORD_FIELD(temp, TX_DIS_NON_IP_EV, 1); - /* Enable SW_EV to inherit in char driver - assume harmless here */ - EFX_SET_OWORD_FIELD(temp, TX_SW_EV_EN, 1); - /* Prefetch threshold 2 => fetch when descriptor cache half empty */ - EFX_SET_OWORD_FIELD(temp, TX_PREF_THRESHOLD, 2); - /* Squash TX of packets of 16 bytes or less */ - if (falcon_rev(efx) >= FALCON_REV_B0 && EFX_WORKAROUND_9141(efx)) - EFX_SET_OWORD_FIELD(temp, TX_FLUSH_MIN_LEN_EN_B0, 1); - falcon_write(efx, &temp, TX_CFG2_REG_KER); + EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_ISCSI_DIS, 1); + efx_writeo(efx, &temp, FR_AA_RX_SELF_RST); /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 * descriptors (which is bad). */ - falcon_read(efx, &temp, TX_CFG_REG_KER); - EFX_SET_OWORD_FIELD(temp, TX_NO_EOP_DISC_EN, 0); - falcon_write(efx, &temp, TX_CFG_REG_KER); - - /* RX config */ - falcon_read(efx, &temp, RX_CFG_REG_KER); - EFX_SET_OWORD_FIELD_VER(efx, temp, RX_DESC_PUSH_EN, 0); - if (EFX_WORKAROUND_7575(efx)) - EFX_SET_OWORD_FIELD_VER(efx, temp, RX_USR_BUF_SIZE, - (3 * 4096) / 32); - if (falcon_rev(efx) >= FALCON_REV_B0) - EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 1); - - /* RX FIFO flow control thresholds */ - thresh = ((rx_xon_thresh_bytes >= 0) ? - rx_xon_thresh_bytes : efx->type->rx_xon_thresh); - EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XON_MAC_TH, thresh / 256); - thresh = ((rx_xoff_thresh_bytes >= 0) ? - rx_xoff_thresh_bytes : efx->type->rx_xoff_thresh); - EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XOFF_MAC_TH, thresh / 256); - /* RX control FIFO thresholds [32 entries] */ - EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XON_TX_TH, 20); - EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XOFF_TX_TH, 25); - falcon_write(efx, &temp, RX_CFG_REG_KER); + efx_reado(efx, &temp, FR_AZ_TX_CFG); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0); + efx_writeo(efx, &temp, FR_AZ_TX_CFG); + + falcon_init_rx_cfg(efx); /* Set destination of both TX and RX Flush events */ - if (falcon_rev(efx) >= FALCON_REV_B0) { - EFX_POPULATE_OWORD_1(temp, FLS_EVQ_ID, 0); - falcon_write(efx, &temp, DP_CTRL_REG); + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); + efx_writeo(efx, &temp, FR_BZ_DP_CTRL); } + efx_nic_init_common(efx); + return 0; } -void falcon_remove_nic(struct efx_nic *efx) +static void falcon_remove_nic(struct efx_nic *efx) { struct falcon_nic_data *nic_data = efx->nic_data; + struct falcon_board *board = falcon_board(efx); int rc; + board->type->fini(efx); + /* Remove I2C adapter and clear it in preparation for a retry */ - rc = i2c_del_adapter(&efx->i2c_adap); + rc = i2c_del_adapter(&board->i2c_adap); BUG_ON(rc); - memset(&efx->i2c_adap, 0, sizeof(efx->i2c_adap)); + memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); falcon_remove_spi_devices(efx); - falcon_free_buffer(efx, &efx->irq_status); + efx_nic_free_buffer(efx, &efx->irq_status); falcon_reset_hw(efx, RESET_TYPE_ALL); @@ -3085,12 +1643,86 @@ void falcon_remove_nic(struct efx_nic *efx) efx->nic_data = NULL; } -void falcon_update_nic_stats(struct efx_nic *efx) +static void falcon_update_nic_stats(struct efx_nic *efx) { + struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t cnt; - falcon_read(efx, &cnt, RX_NODESC_DROP_REG_KER); - efx->n_rx_nodesc_drop_cnt += EFX_OWORD_FIELD(cnt, RX_NODESC_DROP_CNT); + if (nic_data->stats_disable_count) + return; + + efx_reado(efx, &cnt, FR_AZ_RX_NODESC_DROP); + efx->n_rx_nodesc_drop_cnt += + EFX_OWORD_FIELD(cnt, FRF_AB_RX_NODESC_DROP_CNT); + + if (nic_data->stats_pending && + *nic_data->stats_dma_done == FALCON_STATS_DONE) { + nic_data->stats_pending = false; + rmb(); /* read the done flag before the stats */ + efx->mac_op->update_stats(efx); + } +} + +void falcon_start_nic_stats(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + spin_lock_bh(&efx->stats_lock); + if (--nic_data->stats_disable_count == 0) + falcon_stats_request(efx); + spin_unlock_bh(&efx->stats_lock); +} + +void falcon_stop_nic_stats(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + int i; + + might_sleep(); + + spin_lock_bh(&efx->stats_lock); + ++nic_data->stats_disable_count; + spin_unlock_bh(&efx->stats_lock); + + del_timer_sync(&nic_data->stats_timer); + + /* Wait enough time for the most recent transfer to + * complete. */ + for (i = 0; i < 4 && nic_data->stats_pending; i++) { + if (*nic_data->stats_dma_done == FALCON_STATS_DONE) + break; + msleep(1); + } + + spin_lock_bh(&efx->stats_lock); + falcon_stats_complete(efx); + spin_unlock_bh(&efx->stats_lock); +} + +static void falcon_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + falcon_board(efx)->type->set_id_led(efx, mode); +} + +/************************************************************************** + * + * Wake on LAN + * + ************************************************************************** + */ + +static void falcon_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol) +{ + wol->supported = 0; + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + +static int falcon_set_wol(struct efx_nic *efx, u32 type) +{ + if (type != 0) + return -EINVAL; + return 0; } /************************************************************************** @@ -3100,50 +1732,91 @@ void falcon_update_nic_stats(struct efx_nic *efx) ************************************************************************** */ -struct efx_nic_type falcon_a_nic_type = { - .mem_bar = 2, +struct efx_nic_type falcon_a1_nic_type = { + .probe = falcon_probe_nic, + .remove = falcon_remove_nic, + .init = falcon_init_nic, + .fini = efx_port_dummy_op_void, + .monitor = falcon_monitor, + .reset = falcon_reset_hw, + .probe_port = falcon_probe_port, + .remove_port = falcon_remove_port, + .prepare_flush = falcon_prepare_flush, + .update_stats = falcon_update_nic_stats, + .start_stats = falcon_start_nic_stats, + .stop_stats = falcon_stop_nic_stats, + .set_id_led = falcon_set_id_led, + .push_irq_moderation = falcon_push_irq_moderation, + .push_multicast_hash = falcon_push_multicast_hash, + .reconfigure_port = falcon_reconfigure_port, + .get_wol = falcon_get_wol, + .set_wol = falcon_set_wol, + .resume_wol = efx_port_dummy_op_void, + .test_nvram = falcon_test_nvram, + .default_mac_ops = &falcon_xmac_operations, + + .revision = EFX_REV_FALCON_A1, .mem_map_size = 0x20000, - .txd_ptr_tbl_base = TX_DESC_PTR_TBL_KER_A1, - .rxd_ptr_tbl_base = RX_DESC_PTR_TBL_KER_A1, - .buf_tbl_base = BUF_TBL_KER_A1, - .evq_ptr_tbl_base = EVQ_PTR_TBL_KER_A1, - .evq_rptr_tbl_base = EVQ_RPTR_REG_KER_A1, - .txd_ring_mask = FALCON_TXD_RING_MASK, - .rxd_ring_mask = FALCON_RXD_RING_MASK, - .evq_size = FALCON_EVQ_SIZE, - .max_dma_mask = FALCON_DMA_MASK, - .tx_dma_mask = FALCON_TX_DMA_MASK, - .bug5391_mask = 0xf, - .rx_xoff_thresh = 2048, - .rx_xon_thresh = 512, + .txd_ptr_tbl_base = FR_AA_TX_DESC_PTR_TBL_KER, + .rxd_ptr_tbl_base = FR_AA_RX_DESC_PTR_TBL_KER, + .buf_tbl_base = FR_AA_BUF_FULL_TBL_KER, + .evq_ptr_tbl_base = FR_AA_EVQ_PTR_TBL_KER, + .evq_rptr_tbl_base = FR_AA_EVQ_RPTR_KER, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), .rx_buffer_padding = 0x24, .max_interrupt_mode = EFX_INT_MODE_MSI, .phys_addr_channels = 4, + .tx_dc_base = 0x130000, + .rx_dc_base = 0x100000, + .offload_features = NETIF_F_IP_CSUM, + .reset_world_flags = ETH_RESET_IRQ, }; -struct efx_nic_type falcon_b_nic_type = { - .mem_bar = 2, +struct efx_nic_type falcon_b0_nic_type = { + .probe = falcon_probe_nic, + .remove = falcon_remove_nic, + .init = falcon_init_nic, + .fini = efx_port_dummy_op_void, + .monitor = falcon_monitor, + .reset = falcon_reset_hw, + .probe_port = falcon_probe_port, + .remove_port = falcon_remove_port, + .prepare_flush = falcon_prepare_flush, + .update_stats = falcon_update_nic_stats, + .start_stats = falcon_start_nic_stats, + .stop_stats = falcon_stop_nic_stats, + .set_id_led = falcon_set_id_led, + .push_irq_moderation = falcon_push_irq_moderation, + .push_multicast_hash = falcon_push_multicast_hash, + .reconfigure_port = falcon_reconfigure_port, + .get_wol = falcon_get_wol, + .set_wol = falcon_set_wol, + .resume_wol = efx_port_dummy_op_void, + .test_registers = falcon_b0_test_registers, + .test_nvram = falcon_test_nvram, + .default_mac_ops = &falcon_xmac_operations, + + .revision = EFX_REV_FALCON_B0, /* Map everything up to and including the RSS indirection * table. Don't map MSI-X table, MSI-X PBA since Linux * requires that they not be mapped. */ - .mem_map_size = RX_RSS_INDIR_TBL_B0 + 0x800, - .txd_ptr_tbl_base = TX_DESC_PTR_TBL_KER_B0, - .rxd_ptr_tbl_base = RX_DESC_PTR_TBL_KER_B0, - .buf_tbl_base = BUF_TBL_KER_B0, - .evq_ptr_tbl_base = EVQ_PTR_TBL_KER_B0, - .evq_rptr_tbl_base = EVQ_RPTR_REG_KER_B0, - .txd_ring_mask = FALCON_TXD_RING_MASK, - .rxd_ring_mask = FALCON_RXD_RING_MASK, - .evq_size = FALCON_EVQ_SIZE, - .max_dma_mask = FALCON_DMA_MASK, - .tx_dma_mask = FALCON_TX_DMA_MASK, - .bug5391_mask = 0, - .rx_xoff_thresh = 54272, /* ~80Kb - 3*max MTU */ - .rx_xon_thresh = 27648, /* ~3*max MTU */ + .mem_map_size = (FR_BZ_RX_INDIRECTION_TBL + + FR_BZ_RX_INDIRECTION_TBL_STEP * + FR_BZ_RX_INDIRECTION_TBL_ROWS), + .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL, + .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL, + .buf_tbl_base = FR_BZ_BUF_FULL_TBL, + .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL, + .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), .rx_buffer_padding = 0, .max_interrupt_mode = EFX_INT_MODE_MSIX, .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy * interrupt handler only supports 32 * channels */ + .tx_dc_base = 0x130000, + .rx_dc_base = 0x100000, + .offload_features = NETIF_F_IP_CSUM, + .reset_world_flags = ETH_RESET_IRQ, }; diff --git a/drivers/net/sfc/falcon.h b/drivers/net/sfc/falcon.h deleted file mode 100644 index 77f2e0db7ca1..000000000000 --- a/drivers/net/sfc/falcon.h +++ /dev/null @@ -1,145 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#ifndef EFX_FALCON_H -#define EFX_FALCON_H - -#include "net_driver.h" -#include "efx.h" - -/* - * Falcon hardware control - */ - -enum falcon_revision { - FALCON_REV_A0 = 0, - FALCON_REV_A1 = 1, - FALCON_REV_B0 = 2, -}; - -static inline int falcon_rev(struct efx_nic *efx) -{ - return efx->pci_dev->revision; -} - -extern struct efx_nic_type falcon_a_nic_type; -extern struct efx_nic_type falcon_b_nic_type; - -/************************************************************************** - * - * Externs - * - ************************************************************************** - */ - -/* TX data path */ -extern int falcon_probe_tx(struct efx_tx_queue *tx_queue); -extern void falcon_init_tx(struct efx_tx_queue *tx_queue); -extern void falcon_fini_tx(struct efx_tx_queue *tx_queue); -extern void falcon_remove_tx(struct efx_tx_queue *tx_queue); -extern void falcon_push_buffers(struct efx_tx_queue *tx_queue); - -/* RX data path */ -extern int falcon_probe_rx(struct efx_rx_queue *rx_queue); -extern void falcon_init_rx(struct efx_rx_queue *rx_queue); -extern void falcon_fini_rx(struct efx_rx_queue *rx_queue); -extern void falcon_remove_rx(struct efx_rx_queue *rx_queue); -extern void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue); - -/* Event data path */ -extern int falcon_probe_eventq(struct efx_channel *channel); -extern void falcon_init_eventq(struct efx_channel *channel); -extern void falcon_fini_eventq(struct efx_channel *channel); -extern void falcon_remove_eventq(struct efx_channel *channel); -extern int falcon_process_eventq(struct efx_channel *channel, int rx_quota); -extern void falcon_eventq_read_ack(struct efx_channel *channel); - -/* Ports */ -extern int falcon_probe_port(struct efx_nic *efx); -extern void falcon_remove_port(struct efx_nic *efx); - -/* MAC/PHY */ -extern int falcon_switch_mac(struct efx_nic *efx); -extern bool falcon_xaui_link_ok(struct efx_nic *efx); -extern int falcon_dma_stats(struct efx_nic *efx, - unsigned int done_offset); -extern void falcon_drain_tx_fifo(struct efx_nic *efx); -extern void falcon_deconfigure_mac_wrapper(struct efx_nic *efx); -extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); - -/* Interrupts and test events */ -extern int falcon_init_interrupt(struct efx_nic *efx); -extern void falcon_enable_interrupts(struct efx_nic *efx); -extern void falcon_generate_test_event(struct efx_channel *channel, - unsigned int magic); -extern void falcon_sim_phy_event(struct efx_nic *efx); -extern void falcon_generate_interrupt(struct efx_nic *efx); -extern void falcon_set_int_moderation(struct efx_channel *channel); -extern void falcon_disable_interrupts(struct efx_nic *efx); -extern void falcon_fini_interrupt(struct efx_nic *efx); - -#define FALCON_IRQ_MOD_RESOLUTION 5 - -/* Global Resources */ -extern int falcon_probe_nic(struct efx_nic *efx); -extern int falcon_probe_resources(struct efx_nic *efx); -extern int falcon_init_nic(struct efx_nic *efx); -extern int falcon_flush_queues(struct efx_nic *efx); -extern int falcon_reset_hw(struct efx_nic *efx, enum reset_type method); -extern void falcon_remove_resources(struct efx_nic *efx); -extern void falcon_remove_nic(struct efx_nic *efx); -extern void falcon_update_nic_stats(struct efx_nic *efx); -extern void falcon_set_multicast_hash(struct efx_nic *efx); -extern int falcon_reset_xaui(struct efx_nic *efx); - -/* Tests */ -struct falcon_nvconfig; -extern int falcon_read_nvram(struct efx_nic *efx, - struct falcon_nvconfig *nvconfig); -extern int falcon_test_registers(struct efx_nic *efx); - -/************************************************************************** - * - * Falcon MAC stats - * - ************************************************************************** - */ - -#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset) -#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH) - -/* Retrieve statistic from statistics block */ -#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \ - if (FALCON_STAT_WIDTH(falcon_stat) == 16) \ - (efx)->mac_stats.efx_stat += le16_to_cpu( \ - *((__force __le16 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \ - (efx)->mac_stats.efx_stat += le32_to_cpu( \ - *((__force __le32 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - else \ - (efx)->mac_stats.efx_stat += le64_to_cpu( \ - *((__force __le64 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - } while (0) - -#define FALCON_MAC_STATS_SIZE 0x100 - -#define MAC_DATA_LBN 0 -#define MAC_DATA_WIDTH 32 - -extern void falcon_generate_event(struct efx_channel *channel, - efx_qword_t *event); - -#endif /* EFX_FALCON_H */ diff --git a/drivers/net/sfc/falcon_boards.c b/drivers/net/sfc/falcon_boards.c new file mode 100644 index 000000000000..bf0b96af5334 --- /dev/null +++ b/drivers/net/sfc/falcon_boards.c @@ -0,0 +1,752 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007-2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#include <linux/rtnetlink.h> + +#include "net_driver.h" +#include "phy.h" +#include "efx.h" +#include "nic.h" +#include "regs.h" +#include "io.h" +#include "workarounds.h" + +/* Macros for unpacking the board revision */ +/* The revision info is in host byte order. */ +#define FALCON_BOARD_TYPE(_rev) (_rev >> 8) +#define FALCON_BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) +#define FALCON_BOARD_MINOR(_rev) (_rev & 0xf) + +/* Board types */ +#define FALCON_BOARD_SFE4001 0x01 +#define FALCON_BOARD_SFE4002 0x02 +#define FALCON_BOARD_SFN4111T 0x51 +#define FALCON_BOARD_SFN4112F 0x52 + +/***************************************************************************** + * Support for LM87 sensor chip used on several boards + */ +#define LM87_REG_ALARMS1 0x41 +#define LM87_REG_ALARMS2 0x42 +#define LM87_IN_LIMITS(nr, _min, _max) \ + 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min +#define LM87_AIN_LIMITS(nr, _min, _max) \ + 0x3B + (nr), _max, 0x1A + (nr), _min +#define LM87_TEMP_INT_LIMITS(_min, _max) \ + 0x39, _max, 0x3A, _min +#define LM87_TEMP_EXT1_LIMITS(_min, _max) \ + 0x37, _max, 0x38, _min + +#define LM87_ALARM_TEMP_INT 0x10 +#define LM87_ALARM_TEMP_EXT1 0x20 + +#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE) + +static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info, + const u8 *reg_values) +{ + struct falcon_board *board = falcon_board(efx); + struct i2c_client *client = i2c_new_device(&board->i2c_adap, info); + int rc; + + if (!client) + return -EIO; + + while (*reg_values) { + u8 reg = *reg_values++; + u8 value = *reg_values++; + rc = i2c_smbus_write_byte_data(client, reg, value); + if (rc) + goto err; + } + + board->hwmon_client = client; + return 0; + +err: + i2c_unregister_device(client); + return rc; +} + +static void efx_fini_lm87(struct efx_nic *efx) +{ + i2c_unregister_device(falcon_board(efx)->hwmon_client); +} + +static int efx_check_lm87(struct efx_nic *efx, unsigned mask) +{ + struct i2c_client *client = falcon_board(efx)->hwmon_client; + s32 alarms1, alarms2; + + /* If link is up then do not monitor temperature */ + if (EFX_WORKAROUND_7884(efx) && efx->link_state.up) + return 0; + + alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1); + alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2); + if (alarms1 < 0) + return alarms1; + if (alarms2 < 0) + return alarms2; + alarms1 &= mask; + alarms2 &= mask >> 8; + if (alarms1 || alarms2) { + EFX_ERR(efx, + "LM87 detected a hardware failure (status %02x:%02x)" + "%s%s\n", + alarms1, alarms2, + (alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "", + (alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : ""); + return -ERANGE; + } + + return 0; +} + +#else /* !CONFIG_SENSORS_LM87 */ + +static inline int +efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info, + const u8 *reg_values) +{ + return 0; +} +static inline void efx_fini_lm87(struct efx_nic *efx) +{ +} +static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask) +{ + return 0; +} + +#endif /* CONFIG_SENSORS_LM87 */ + +/***************************************************************************** + * Support for the SFE4001 and SFN4111T NICs. + * + * The SFE4001 does not power-up fully at reset due to its high power + * consumption. We control its power via a PCA9539 I/O expander. + * Both boards have a MAX6647 temperature monitor which we expose to + * the lm90 driver. + * + * This also provides minimal support for reflashing the PHY, which is + * initiated by resetting it with the FLASH_CFG_1 pin pulled down. + * On SFE4001 rev A2 and later this is connected to the 3V3X output of + * the IO-expander; on the SFN4111T it is connected to Falcon's GPIO3. + * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually + * exclusive with the network device being open. + */ + +/************************************************************************** + * Support for I2C IO Expander device on SFE4001 + */ +#define PCA9539 0x74 + +#define P0_IN 0x00 +#define P0_OUT 0x02 +#define P0_INVERT 0x04 +#define P0_CONFIG 0x06 + +#define P0_EN_1V0X_LBN 0 +#define P0_EN_1V0X_WIDTH 1 +#define P0_EN_1V2_LBN 1 +#define P0_EN_1V2_WIDTH 1 +#define P0_EN_2V5_LBN 2 +#define P0_EN_2V5_WIDTH 1 +#define P0_EN_3V3X_LBN 3 +#define P0_EN_3V3X_WIDTH 1 +#define P0_EN_5V_LBN 4 +#define P0_EN_5V_WIDTH 1 +#define P0_SHORTEN_JTAG_LBN 5 +#define P0_SHORTEN_JTAG_WIDTH 1 +#define P0_X_TRST_LBN 6 +#define P0_X_TRST_WIDTH 1 +#define P0_DSP_RESET_LBN 7 +#define P0_DSP_RESET_WIDTH 1 + +#define P1_IN 0x01 +#define P1_OUT 0x03 +#define P1_INVERT 0x05 +#define P1_CONFIG 0x07 + +#define P1_AFE_PWD_LBN 0 +#define P1_AFE_PWD_WIDTH 1 +#define P1_DSP_PWD25_LBN 1 +#define P1_DSP_PWD25_WIDTH 1 +#define P1_RESERVED_LBN 2 +#define P1_RESERVED_WIDTH 2 +#define P1_SPARE_LBN 4 +#define P1_SPARE_WIDTH 4 + +/* Temperature Sensor */ +#define MAX664X_REG_RSL 0x02 +#define MAX664X_REG_WLHO 0x0B + +static void sfe4001_poweroff(struct efx_nic *efx) +{ + struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client; + struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client; + + /* Turn off all power rails and disable outputs */ + i2c_smbus_write_byte_data(ioexp_client, P0_OUT, 0xff); + i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, 0xff); + i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff); + + /* Clear any over-temperature alert */ + i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); +} + +static int sfe4001_poweron(struct efx_nic *efx) +{ + struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client; + struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client; + unsigned int i, j; + int rc; + u8 out; + + /* Clear any previous over-temperature alert */ + rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); + if (rc < 0) + return rc; + + /* Enable port 0 and port 1 outputs on IO expander */ + rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00); + if (rc) + return rc; + rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, + 0xff & ~(1 << P1_SPARE_LBN)); + if (rc) + goto fail_on; + + /* If PHY power is on, turn it all off and wait 1 second to + * ensure a full reset. + */ + rc = i2c_smbus_read_byte_data(ioexp_client, P0_OUT); + if (rc < 0) + goto fail_on; + out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) | + (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) | + (0 << P0_EN_1V0X_LBN)); + if (rc != out) { + EFX_INFO(efx, "power-cycling PHY\n"); + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + schedule_timeout_uninterruptible(HZ); + } + + for (i = 0; i < 20; ++i) { + /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */ + out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | + (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | + (1 << P0_X_TRST_LBN)); + if (efx->phy_mode & PHY_MODE_SPECIAL) + out |= 1 << P0_EN_3V3X_LBN; + + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + msleep(10); + + /* Turn on 1V power rail */ + out &= ~(1 << P0_EN_1V0X_LBN); + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + + EFX_INFO(efx, "waiting for DSP boot (attempt %d)...\n", i); + + /* In flash config mode, DSP does not turn on AFE, so + * just wait 1 second. + */ + if (efx->phy_mode & PHY_MODE_SPECIAL) { + schedule_timeout_uninterruptible(HZ); + return 0; + } + + for (j = 0; j < 10; ++j) { + msleep(100); + + /* Check DSP has asserted AFE power line */ + rc = i2c_smbus_read_byte_data(ioexp_client, P1_IN); + if (rc < 0) + goto fail_on; + if (rc & (1 << P1_AFE_PWD_LBN)) + return 0; + } + } + + EFX_INFO(efx, "timed out waiting for DSP boot\n"); + rc = -ETIMEDOUT; +fail_on: + sfe4001_poweroff(efx); + return rc; +} + +static int sfn4111t_reset(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + efx_oword_t reg; + + /* GPIO 3 and the GPIO register are shared with I2C, so block that */ + i2c_lock_adapter(&board->i2c_adap); + + /* Pull RST_N (GPIO 2) low then let it up again, setting the + * FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the + * output enables; the output levels should always be 0 (low) + * and we rely on external pull-ups. */ + efx_reado(efx, ®, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO2_OEN, true); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); + msleep(1000); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO2_OEN, false); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN, + !!(efx->phy_mode & PHY_MODE_SPECIAL)); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); + msleep(1); + + i2c_unlock_adapter(&board->i2c_adap); + + ssleep(1); + return 0; +} + +static ssize_t show_phy_flash_cfg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL)); +} + +static ssize_t set_phy_flash_cfg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + enum efx_phy_mode old_mode, new_mode; + int err; + + rtnl_lock(); + old_mode = efx->phy_mode; + if (count == 0 || *buf == '0') + new_mode = old_mode & ~PHY_MODE_SPECIAL; + else + new_mode = PHY_MODE_SPECIAL; + if (old_mode == new_mode) { + err = 0; + } else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) { + err = -EBUSY; + } else { + /* Reset the PHY, reconfigure the MAC and enable/disable + * MAC stats accordingly. */ + efx->phy_mode = new_mode; + if (new_mode & PHY_MODE_SPECIAL) + falcon_stop_nic_stats(efx); + if (falcon_board(efx)->type->id == FALCON_BOARD_SFE4001) + err = sfe4001_poweron(efx); + else + err = sfn4111t_reset(efx); + if (!err) + err = efx_reconfigure_port(efx); + if (!(new_mode & PHY_MODE_SPECIAL)) + falcon_start_nic_stats(efx); + } + rtnl_unlock(); + + return err ? err : count; +} + +static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg); + +static void sfe4001_fini(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + + EFX_INFO(efx, "%s\n", __func__); + + device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + sfe4001_poweroff(efx); + i2c_unregister_device(board->ioexp_client); + i2c_unregister_device(board->hwmon_client); +} + +static int sfe4001_check_hw(struct efx_nic *efx) +{ + s32 status; + + /* If XAUI link is up then do not monitor */ + if (EFX_WORKAROUND_7884(efx) && !efx->xmac_poll_required) + return 0; + + /* Check the powered status of the PHY. Lack of power implies that + * the MAX6647 has shut down power to it, probably due to a temp. + * alarm. Reading the power status rather than the MAX6647 status + * directly because the later is read-to-clear and would thus + * start to power up the PHY again when polled, causing us to blip + * the power undesirably. + * We know we can read from the IO expander because we did + * it during power-on. Assume failure now is bad news. */ + status = i2c_smbus_read_byte_data(falcon_board(efx)->ioexp_client, P1_IN); + if (status >= 0 && + (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0) + return 0; + + /* Use board power control, not PHY power control */ + sfe4001_poweroff(efx); + efx->phy_mode = PHY_MODE_OFF; + + return (status < 0) ? -EIO : -ERANGE; +} + +static struct i2c_board_info sfe4001_hwmon_info = { + I2C_BOARD_INFO("max6647", 0x4e), +}; + +/* This board uses an I2C expander to provider power to the PHY, which needs to + * be turned on before the PHY can be used. + * Context: Process context, rtnl lock held + */ +static int sfe4001_init(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + int rc; + +#if defined(CONFIG_SENSORS_LM90) || defined(CONFIG_SENSORS_LM90_MODULE) + board->hwmon_client = + i2c_new_device(&board->i2c_adap, &sfe4001_hwmon_info); +#else + board->hwmon_client = + i2c_new_dummy(&board->i2c_adap, sfe4001_hwmon_info.addr); +#endif + if (!board->hwmon_client) + return -EIO; + + /* Raise board/PHY high limit from 85 to 90 degrees Celsius */ + rc = i2c_smbus_write_byte_data(board->hwmon_client, + MAX664X_REG_WLHO, 90); + if (rc) + goto fail_hwmon; + + board->ioexp_client = i2c_new_dummy(&board->i2c_adap, PCA9539); + if (!board->ioexp_client) { + rc = -EIO; + goto fail_hwmon; + } + + if (efx->phy_mode & PHY_MODE_SPECIAL) { + /* PHY won't generate a 156.25 MHz clock and MAC stats fetch + * will fail. */ + falcon_stop_nic_stats(efx); + } + rc = sfe4001_poweron(efx); + if (rc) + goto fail_ioexp; + + rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + if (rc) + goto fail_on; + + EFX_INFO(efx, "PHY is powered on\n"); + return 0; + +fail_on: + sfe4001_poweroff(efx); +fail_ioexp: + i2c_unregister_device(board->ioexp_client); +fail_hwmon: + i2c_unregister_device(board->hwmon_client); + return rc; +} + +static int sfn4111t_check_hw(struct efx_nic *efx) +{ + s32 status; + + /* If XAUI link is up then do not monitor */ + if (EFX_WORKAROUND_7884(efx) && !efx->xmac_poll_required) + return 0; + + /* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */ + status = i2c_smbus_read_byte_data(falcon_board(efx)->hwmon_client, + MAX664X_REG_RSL); + if (status < 0) + return -EIO; + if (status & 0x57) + return -ERANGE; + return 0; +} + +static void sfn4111t_fini(struct efx_nic *efx) +{ + EFX_INFO(efx, "%s\n", __func__); + + device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + i2c_unregister_device(falcon_board(efx)->hwmon_client); +} + +static struct i2c_board_info sfn4111t_a0_hwmon_info = { + I2C_BOARD_INFO("max6647", 0x4e), +}; + +static struct i2c_board_info sfn4111t_r5_hwmon_info = { + I2C_BOARD_INFO("max6646", 0x4d), +}; + +static void sfn4111t_init_phy(struct efx_nic *efx) +{ + if (!(efx->phy_mode & PHY_MODE_SPECIAL)) { + if (sft9001_wait_boot(efx) != -EINVAL) + return; + + efx->phy_mode = PHY_MODE_SPECIAL; + falcon_stop_nic_stats(efx); + } + + sfn4111t_reset(efx); + sft9001_wait_boot(efx); +} + +static int sfn4111t_init(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + int rc; + + board->hwmon_client = + i2c_new_device(&board->i2c_adap, + (board->minor < 5) ? + &sfn4111t_a0_hwmon_info : + &sfn4111t_r5_hwmon_info); + if (!board->hwmon_client) + return -EIO; + + rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + if (rc) + goto fail_hwmon; + + if (efx->phy_mode & PHY_MODE_SPECIAL) + /* PHY may not generate a 156.25 MHz clock and MAC + * stats fetch will fail. */ + falcon_stop_nic_stats(efx); + + return 0; + +fail_hwmon: + i2c_unregister_device(board->hwmon_client); + return rc; +} + +/***************************************************************************** + * Support for the SFE4002 + * + */ +static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */ + +static const u8 sfe4002_lm87_regs[] = { + LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */ + LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */ + LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */ + LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */ + LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */ + LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */ + LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */ + LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */ + LM87_TEMP_INT_LIMITS(10, 60), /* board */ + LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */ + 0 +}; + +static struct i2c_board_info sfe4002_hwmon_info = { + I2C_BOARD_INFO("lm87", 0x2e), + .platform_data = &sfe4002_lm87_channel, +}; + +/****************************************************************************/ +/* LED allocations. Note that on rev A0 boards the schematic and the reality + * differ: red and green are swapped. Below is the fixed (A1) layout (there + * are only 3 A0 boards in existence, so no real reason to make this + * conditional). + */ +#define SFE4002_FAULT_LED (2) /* Red */ +#define SFE4002_RX_LED (0) /* Green */ +#define SFE4002_TX_LED (1) /* Amber */ + +static void sfe4002_init_phy(struct efx_nic *efx) +{ + /* Set the TX and RX LEDs to reflect status and activity, and the + * fault LED off */ + falcon_qt202x_set_led(efx, SFE4002_TX_LED, + QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); + falcon_qt202x_set_led(efx, SFE4002_RX_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); + falcon_qt202x_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); +} + +static void sfe4002_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + falcon_qt202x_set_led( + efx, SFE4002_FAULT_LED, + (mode == EFX_LED_ON) ? QUAKE_LED_ON : QUAKE_LED_OFF); +} + +static int sfe4002_check_hw(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + + /* A0 board rev. 4002s report a temperature fault the whole time + * (bad sensor) so we mask it out. */ + unsigned alarm_mask = + (board->major == 0 && board->minor == 0) ? + ~LM87_ALARM_TEMP_EXT1 : ~0; + + return efx_check_lm87(efx, alarm_mask); +} + +static int sfe4002_init(struct efx_nic *efx) +{ + return efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs); +} + +/***************************************************************************** + * Support for the SFN4112F + * + */ +static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */ + +static const u8 sfn4112f_lm87_regs[] = { + LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */ + LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */ + LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */ + LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */ + LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */ + LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */ + LM87_TEMP_INT_LIMITS(10, 60), /* board */ + LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */ + 0 +}; + +static struct i2c_board_info sfn4112f_hwmon_info = { + I2C_BOARD_INFO("lm87", 0x2e), + .platform_data = &sfn4112f_lm87_channel, +}; + +#define SFN4112F_ACT_LED 0 +#define SFN4112F_LINK_LED 1 + +static void sfn4112f_init_phy(struct efx_nic *efx) +{ + falcon_qt202x_set_led(efx, SFN4112F_ACT_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACT); + falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT); +} + +static void sfn4112f_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + int reg; + + switch (mode) { + case EFX_LED_OFF: + reg = QUAKE_LED_OFF; + break; + case EFX_LED_ON: + reg = QUAKE_LED_ON; + break; + default: + reg = QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT; + break; + } + + falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, reg); +} + +static int sfn4112f_check_hw(struct efx_nic *efx) +{ + /* Mask out unused sensors */ + return efx_check_lm87(efx, ~0x48); +} + +static int sfn4112f_init(struct efx_nic *efx) +{ + return efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs); +} + +static const struct falcon_board_type board_types[] = { + { + .id = FALCON_BOARD_SFE4001, + .ref_model = "SFE4001", + .gen_type = "10GBASE-T adapter", + .init = sfe4001_init, + .init_phy = efx_port_dummy_op_void, + .fini = sfe4001_fini, + .set_id_led = tenxpress_set_id_led, + .monitor = sfe4001_check_hw, + }, + { + .id = FALCON_BOARD_SFE4002, + .ref_model = "SFE4002", + .gen_type = "XFP adapter", + .init = sfe4002_init, + .init_phy = sfe4002_init_phy, + .fini = efx_fini_lm87, + .set_id_led = sfe4002_set_id_led, + .monitor = sfe4002_check_hw, + }, + { + .id = FALCON_BOARD_SFN4111T, + .ref_model = "SFN4111T", + .gen_type = "100/1000/10GBASE-T adapter", + .init = sfn4111t_init, + .init_phy = sfn4111t_init_phy, + .fini = sfn4111t_fini, + .set_id_led = tenxpress_set_id_led, + .monitor = sfn4111t_check_hw, + }, + { + .id = FALCON_BOARD_SFN4112F, + .ref_model = "SFN4112F", + .gen_type = "SFP+ adapter", + .init = sfn4112f_init, + .init_phy = sfn4112f_init_phy, + .fini = efx_fini_lm87, + .set_id_led = sfn4112f_set_id_led, + .monitor = sfn4112f_check_hw, + }, +}; + +static const struct falcon_board_type falcon_dummy_board = { + .init = efx_port_dummy_op_int, + .init_phy = efx_port_dummy_op_void, + .fini = efx_port_dummy_op_void, + .set_id_led = efx_port_dummy_op_set_id_led, + .monitor = efx_port_dummy_op_int, +}; + +void falcon_probe_board(struct efx_nic *efx, u16 revision_info) +{ + struct falcon_board *board = falcon_board(efx); + u8 type_id = FALCON_BOARD_TYPE(revision_info); + int i; + + board->major = FALCON_BOARD_MAJOR(revision_info); + board->minor = FALCON_BOARD_MINOR(revision_info); + + for (i = 0; i < ARRAY_SIZE(board_types); i++) + if (board_types[i].id == type_id) + board->type = &board_types[i]; + + if (board->type) { + EFX_INFO(efx, "board is %s rev %c%d\n", + (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC) + ? board->type->ref_model : board->type->gen_type, + 'A' + board->major, board->minor); + } else { + EFX_ERR(efx, "unknown board type %d\n", type_id); + board->type = &falcon_dummy_board; + } +} diff --git a/drivers/net/sfc/falcon_gmac.c b/drivers/net/sfc/falcon_gmac.c index 8865eae20ac5..7dadfcbd6ce7 100644 --- a/drivers/net/sfc/falcon_gmac.c +++ b/drivers/net/sfc/falcon_gmac.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -11,11 +11,10 @@ #include <linux/delay.h> #include "net_driver.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "mac.h" -#include "falcon_hwdefs.h" -#include "falcon_io.h" -#include "gmii.h" +#include "regs.h" +#include "io.h" /************************************************************************** * @@ -23,106 +22,109 @@ * *************************************************************************/ -static void falcon_reconfigure_gmac(struct efx_nic *efx) +static int falcon_reconfigure_gmac(struct efx_nic *efx) { + struct efx_link_state *link_state = &efx->link_state; bool loopback, tx_fc, rx_fc, bytemode; int if_mode; unsigned int max_frame_len; efx_oword_t reg; /* Configuration register 1 */ - tx_fc = (efx->link_fc & EFX_FC_TX) || !efx->link_fd; - rx_fc = !!(efx->link_fc & EFX_FC_RX); + tx_fc = (link_state->fc & EFX_FC_TX) || !link_state->fd; + rx_fc = !!(link_state->fc & EFX_FC_RX); loopback = (efx->loopback_mode == LOOPBACK_GMAC); - bytemode = (efx->link_speed == 1000); + bytemode = (link_state->speed == 1000); EFX_POPULATE_OWORD_5(reg, - GM_LOOP, loopback, - GM_TX_EN, 1, - GM_TX_FC_EN, tx_fc, - GM_RX_EN, 1, - GM_RX_FC_EN, rx_fc); - falcon_write(efx, ®, GM_CFG1_REG); + FRF_AB_GM_LOOP, loopback, + FRF_AB_GM_TX_EN, 1, + FRF_AB_GM_TX_FC_EN, tx_fc, + FRF_AB_GM_RX_EN, 1, + FRF_AB_GM_RX_FC_EN, rx_fc); + efx_writeo(efx, ®, FR_AB_GM_CFG1); udelay(10); /* Configuration register 2 */ if_mode = (bytemode) ? 2 : 1; EFX_POPULATE_OWORD_5(reg, - GM_IF_MODE, if_mode, - GM_PAD_CRC_EN, 1, - GM_LEN_CHK, 1, - GM_FD, efx->link_fd, - GM_PAMBL_LEN, 0x7/*datasheet recommended */); + FRF_AB_GM_IF_MODE, if_mode, + FRF_AB_GM_PAD_CRC_EN, 1, + FRF_AB_GM_LEN_CHK, 1, + FRF_AB_GM_FD, link_state->fd, + FRF_AB_GM_PAMBL_LEN, 0x7/*datasheet recommended */); - falcon_write(efx, ®, GM_CFG2_REG); + efx_writeo(efx, ®, FR_AB_GM_CFG2); udelay(10); /* Max frame len register */ max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); - EFX_POPULATE_OWORD_1(reg, GM_MAX_FLEN, max_frame_len); - falcon_write(efx, ®, GM_MAX_FLEN_REG); + EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_MAX_FLEN, max_frame_len); + efx_writeo(efx, ®, FR_AB_GM_MAX_FLEN); udelay(10); /* FIFO configuration register 0 */ EFX_POPULATE_OWORD_5(reg, - GMF_FTFENREQ, 1, - GMF_STFENREQ, 1, - GMF_FRFENREQ, 1, - GMF_SRFENREQ, 1, - GMF_WTMENREQ, 1); - falcon_write(efx, ®, GMF_CFG0_REG); + FRF_AB_GMF_FTFENREQ, 1, + FRF_AB_GMF_STFENREQ, 1, + FRF_AB_GMF_FRFENREQ, 1, + FRF_AB_GMF_SRFENREQ, 1, + FRF_AB_GMF_WTMENREQ, 1); + efx_writeo(efx, ®, FR_AB_GMF_CFG0); udelay(10); /* FIFO configuration register 1 */ EFX_POPULATE_OWORD_2(reg, - GMF_CFGFRTH, 0x12, - GMF_CFGXOFFRTX, 0xffff); - falcon_write(efx, ®, GMF_CFG1_REG); + FRF_AB_GMF_CFGFRTH, 0x12, + FRF_AB_GMF_CFGXOFFRTX, 0xffff); + efx_writeo(efx, ®, FR_AB_GMF_CFG1); udelay(10); /* FIFO configuration register 2 */ EFX_POPULATE_OWORD_2(reg, - GMF_CFGHWM, 0x3f, - GMF_CFGLWM, 0xa); - falcon_write(efx, ®, GMF_CFG2_REG); + FRF_AB_GMF_CFGHWM, 0x3f, + FRF_AB_GMF_CFGLWM, 0xa); + efx_writeo(efx, ®, FR_AB_GMF_CFG2); udelay(10); /* FIFO configuration register 3 */ EFX_POPULATE_OWORD_2(reg, - GMF_CFGHWMFT, 0x1c, - GMF_CFGFTTH, 0x08); - falcon_write(efx, ®, GMF_CFG3_REG); + FRF_AB_GMF_CFGHWMFT, 0x1c, + FRF_AB_GMF_CFGFTTH, 0x08); + efx_writeo(efx, ®, FR_AB_GMF_CFG3); udelay(10); /* FIFO configuration register 4 */ - EFX_POPULATE_OWORD_1(reg, GMF_HSTFLTRFRM_PAUSE, 1); - falcon_write(efx, ®, GMF_CFG4_REG); + EFX_POPULATE_OWORD_1(reg, FRF_AB_GMF_HSTFLTRFRM_PAUSE, 1); + efx_writeo(efx, ®, FR_AB_GMF_CFG4); udelay(10); /* FIFO configuration register 5 */ - falcon_read(efx, ®, GMF_CFG5_REG); - EFX_SET_OWORD_FIELD(reg, GMF_CFGBYTMODE, bytemode); - EFX_SET_OWORD_FIELD(reg, GMF_CFGHDPLX, !efx->link_fd); - EFX_SET_OWORD_FIELD(reg, GMF_HSTDRPLT64, !efx->link_fd); - EFX_SET_OWORD_FIELD(reg, GMF_HSTFLTRFRMDC_PAUSE, 0); - falcon_write(efx, ®, GMF_CFG5_REG); + efx_reado(efx, ®, FR_AB_GMF_CFG5); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_CFGBYTMODE, bytemode); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_CFGHDPLX, !link_state->fd); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_HSTDRPLT64, !link_state->fd); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_HSTFLTRFRMDC_PAUSE, 0); + efx_writeo(efx, ®, FR_AB_GMF_CFG5); udelay(10); /* MAC address */ EFX_POPULATE_OWORD_4(reg, - GM_HWADDR_5, efx->net_dev->dev_addr[5], - GM_HWADDR_4, efx->net_dev->dev_addr[4], - GM_HWADDR_3, efx->net_dev->dev_addr[3], - GM_HWADDR_2, efx->net_dev->dev_addr[2]); - falcon_write(efx, ®, GM_ADR1_REG); + FRF_AB_GM_ADR_B0, efx->net_dev->dev_addr[5], + FRF_AB_GM_ADR_B1, efx->net_dev->dev_addr[4], + FRF_AB_GM_ADR_B2, efx->net_dev->dev_addr[3], + FRF_AB_GM_ADR_B3, efx->net_dev->dev_addr[2]); + efx_writeo(efx, ®, FR_AB_GM_ADR1); udelay(10); EFX_POPULATE_OWORD_2(reg, - GM_HWADDR_1, efx->net_dev->dev_addr[1], - GM_HWADDR_0, efx->net_dev->dev_addr[0]); - falcon_write(efx, ®, GM_ADR2_REG); + FRF_AB_GM_ADR_B4, efx->net_dev->dev_addr[1], + FRF_AB_GM_ADR_B5, efx->net_dev->dev_addr[0]); + efx_writeo(efx, ®, FR_AB_GM_ADR2); udelay(10); falcon_reconfigure_mac_wrapper(efx); + + return 0; } static void falcon_update_stats_gmac(struct efx_nic *efx) @@ -130,11 +132,6 @@ static void falcon_update_stats_gmac(struct efx_nic *efx) struct efx_mac_stats *mac_stats = &efx->mac_stats; unsigned long old_rx_pause, old_tx_pause; unsigned long new_rx_pause, new_tx_pause; - int rc; - - rc = falcon_dma_stats(efx, GDmaDone_offset); - if (rc) - return; /* Pause frames are erroneously counted as errors (SFC bug 3269) */ old_rx_pause = mac_stats->rx_pause; @@ -221,9 +218,13 @@ static void falcon_update_stats_gmac(struct efx_nic *efx) mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64; } +static bool falcon_gmac_check_fault(struct efx_nic *efx) +{ + return false; +} + struct efx_mac_operations falcon_gmac_operations = { .reconfigure = falcon_reconfigure_gmac, .update_stats = falcon_update_stats_gmac, - .irq = efx_port_dummy_op_void, - .poll = efx_port_dummy_op_void, + .check_fault = falcon_gmac_check_fault, }; diff --git a/drivers/net/sfc/falcon_hwdefs.h b/drivers/net/sfc/falcon_hwdefs.h deleted file mode 100644 index 2d2261117ace..000000000000 --- a/drivers/net/sfc/falcon_hwdefs.h +++ /dev/null @@ -1,1333 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#ifndef EFX_FALCON_HWDEFS_H -#define EFX_FALCON_HWDEFS_H - -/* - * Falcon hardware value definitions. - * Falcon is the internal codename for the SFC4000 controller that is - * present in SFE400X evaluation boards - */ - -/************************************************************************** - * - * Falcon registers - * - ************************************************************************** - */ - -/* Address region register */ -#define ADR_REGION_REG_KER 0x00 -#define ADR_REGION0_LBN 0 -#define ADR_REGION0_WIDTH 18 -#define ADR_REGION1_LBN 32 -#define ADR_REGION1_WIDTH 18 -#define ADR_REGION2_LBN 64 -#define ADR_REGION2_WIDTH 18 -#define ADR_REGION3_LBN 96 -#define ADR_REGION3_WIDTH 18 - -/* Interrupt enable register */ -#define INT_EN_REG_KER 0x0010 -#define KER_INT_KER_LBN 3 -#define KER_INT_KER_WIDTH 1 -#define DRV_INT_EN_KER_LBN 0 -#define DRV_INT_EN_KER_WIDTH 1 - -/* Interrupt status address register */ -#define INT_ADR_REG_KER 0x0030 -#define NORM_INT_VEC_DIS_KER_LBN 64 -#define NORM_INT_VEC_DIS_KER_WIDTH 1 -#define INT_ADR_KER_LBN 0 -#define INT_ADR_KER_WIDTH EFX_DMA_TYPE_WIDTH(64) /* not 46 for this one */ - -/* Interrupt status register (B0 only) */ -#define INT_ISR0_B0 0x90 -#define INT_ISR1_B0 0xA0 - -/* Interrupt acknowledge register (A0/A1 only) */ -#define INT_ACK_REG_KER_A1 0x0050 -#define INT_ACK_DUMMY_DATA_LBN 0 -#define INT_ACK_DUMMY_DATA_WIDTH 32 - -/* Interrupt acknowledge work-around register (A0/A1 only )*/ -#define WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1 0x0070 - -/* SPI host command register */ -#define EE_SPI_HCMD_REG_KER 0x0100 -#define EE_SPI_HCMD_CMD_EN_LBN 31 -#define EE_SPI_HCMD_CMD_EN_WIDTH 1 -#define EE_WR_TIMER_ACTIVE_LBN 28 -#define EE_WR_TIMER_ACTIVE_WIDTH 1 -#define EE_SPI_HCMD_SF_SEL_LBN 24 -#define EE_SPI_HCMD_SF_SEL_WIDTH 1 -#define EE_SPI_EEPROM 0 -#define EE_SPI_FLASH 1 -#define EE_SPI_HCMD_DABCNT_LBN 16 -#define EE_SPI_HCMD_DABCNT_WIDTH 5 -#define EE_SPI_HCMD_READ_LBN 15 -#define EE_SPI_HCMD_READ_WIDTH 1 -#define EE_SPI_READ 1 -#define EE_SPI_WRITE 0 -#define EE_SPI_HCMD_DUBCNT_LBN 12 -#define EE_SPI_HCMD_DUBCNT_WIDTH 2 -#define EE_SPI_HCMD_ADBCNT_LBN 8 -#define EE_SPI_HCMD_ADBCNT_WIDTH 2 -#define EE_SPI_HCMD_ENC_LBN 0 -#define EE_SPI_HCMD_ENC_WIDTH 8 - -/* SPI host address register */ -#define EE_SPI_HADR_REG_KER 0x0110 -#define EE_SPI_HADR_ADR_LBN 0 -#define EE_SPI_HADR_ADR_WIDTH 24 - -/* SPI host data register */ -#define EE_SPI_HDATA_REG_KER 0x0120 - -/* SPI/VPD config register */ -#define EE_VPD_CFG_REG_KER 0x0140 -#define EE_VPD_EN_LBN 0 -#define EE_VPD_EN_WIDTH 1 -#define EE_VPD_EN_AD9_MODE_LBN 1 -#define EE_VPD_EN_AD9_MODE_WIDTH 1 -#define EE_EE_CLOCK_DIV_LBN 112 -#define EE_EE_CLOCK_DIV_WIDTH 7 -#define EE_SF_CLOCK_DIV_LBN 120 -#define EE_SF_CLOCK_DIV_WIDTH 7 - -/* PCIE CORE ACCESS REG */ -#define PCIE_CORE_ADDR_PCIE_DEVICE_CTRL_STAT 0x68 -#define PCIE_CORE_ADDR_PCIE_LINK_CTRL_STAT 0x70 -#define PCIE_CORE_ADDR_ACK_RPL_TIMER 0x700 -#define PCIE_CORE_ADDR_ACK_FREQ 0x70C - -/* NIC status register */ -#define NIC_STAT_REG 0x0200 -#define EE_STRAP_EN_LBN 31 -#define EE_STRAP_EN_WIDTH 1 -#define EE_STRAP_OVR_LBN 24 -#define EE_STRAP_OVR_WIDTH 4 -#define ONCHIP_SRAM_LBN 16 -#define ONCHIP_SRAM_WIDTH 1 -#define SF_PRST_LBN 9 -#define SF_PRST_WIDTH 1 -#define EE_PRST_LBN 8 -#define EE_PRST_WIDTH 1 -#define STRAP_PINS_LBN 0 -#define STRAP_PINS_WIDTH 3 -/* These bit definitions are extrapolated from the list of numerical - * values for STRAP_PINS. - */ -#define STRAP_10G_LBN 2 -#define STRAP_10G_WIDTH 1 -#define STRAP_PCIE_LBN 0 -#define STRAP_PCIE_WIDTH 1 - -#define BOOTED_USING_NVDEVICE_LBN 3 -#define BOOTED_USING_NVDEVICE_WIDTH 1 - -/* GPIO control register */ -#define GPIO_CTL_REG_KER 0x0210 -#define GPIO_USE_NIC_CLK_LBN (30) -#define GPIO_USE_NIC_CLK_WIDTH (1) -#define GPIO_OUTPUTS_LBN (16) -#define GPIO_OUTPUTS_WIDTH (4) -#define GPIO_INPUTS_LBN (8) -#define GPIO_DIRECTION_LBN (24) -#define GPIO_DIRECTION_WIDTH (4) -#define GPIO_DIRECTION_OUT (1) -#define GPIO_SRAM_SLEEP (1 << 1) - -#define GPIO3_OEN_LBN (GPIO_DIRECTION_LBN + 3) -#define GPIO3_OEN_WIDTH 1 -#define GPIO2_OEN_LBN (GPIO_DIRECTION_LBN + 2) -#define GPIO2_OEN_WIDTH 1 -#define GPIO1_OEN_LBN (GPIO_DIRECTION_LBN + 1) -#define GPIO1_OEN_WIDTH 1 -#define GPIO0_OEN_LBN (GPIO_DIRECTION_LBN + 0) -#define GPIO0_OEN_WIDTH 1 - -#define GPIO3_OUT_LBN (GPIO_OUTPUTS_LBN + 3) -#define GPIO3_OUT_WIDTH 1 -#define GPIO2_OUT_LBN (GPIO_OUTPUTS_LBN + 2) -#define GPIO2_OUT_WIDTH 1 -#define GPIO1_OUT_LBN (GPIO_OUTPUTS_LBN + 1) -#define GPIO1_OUT_WIDTH 1 -#define GPIO0_OUT_LBN (GPIO_OUTPUTS_LBN + 0) -#define GPIO0_OUT_WIDTH 1 - -#define GPIO3_IN_LBN (GPIO_INPUTS_LBN + 3) -#define GPIO3_IN_WIDTH 1 -#define GPIO2_IN_WIDTH 1 -#define GPIO1_IN_WIDTH 1 -#define GPIO0_IN_LBN (GPIO_INPUTS_LBN + 0) -#define GPIO0_IN_WIDTH 1 - -/* Global control register */ -#define GLB_CTL_REG_KER 0x0220 -#define EXT_PHY_RST_CTL_LBN 63 -#define EXT_PHY_RST_CTL_WIDTH 1 -#define PCIE_SD_RST_CTL_LBN 61 -#define PCIE_SD_RST_CTL_WIDTH 1 - -#define PCIE_NSTCK_RST_CTL_LBN 58 -#define PCIE_NSTCK_RST_CTL_WIDTH 1 -#define PCIE_CORE_RST_CTL_LBN 57 -#define PCIE_CORE_RST_CTL_WIDTH 1 -#define EE_RST_CTL_LBN 49 -#define EE_RST_CTL_WIDTH 1 -#define RST_XGRX_LBN 24 -#define RST_XGRX_WIDTH 1 -#define RST_XGTX_LBN 23 -#define RST_XGTX_WIDTH 1 -#define RST_EM_LBN 22 -#define RST_EM_WIDTH 1 -#define EXT_PHY_RST_DUR_LBN 1 -#define EXT_PHY_RST_DUR_WIDTH 3 -#define SWRST_LBN 0 -#define SWRST_WIDTH 1 -#define INCLUDE_IN_RESET 0 -#define EXCLUDE_FROM_RESET 1 - -/* Fatal interrupt register */ -#define FATAL_INTR_REG_KER 0x0230 -#define RBUF_OWN_INT_KER_EN_LBN 39 -#define RBUF_OWN_INT_KER_EN_WIDTH 1 -#define TBUF_OWN_INT_KER_EN_LBN 38 -#define TBUF_OWN_INT_KER_EN_WIDTH 1 -#define ILL_ADR_INT_KER_EN_LBN 33 -#define ILL_ADR_INT_KER_EN_WIDTH 1 -#define MEM_PERR_INT_KER_LBN 8 -#define MEM_PERR_INT_KER_WIDTH 1 -#define INT_KER_ERROR_LBN 0 -#define INT_KER_ERROR_WIDTH 12 - -#define DP_CTRL_REG 0x250 -#define FLS_EVQ_ID_LBN 0 -#define FLS_EVQ_ID_WIDTH 11 - -#define MEM_STAT_REG_KER 0x260 - -/* Debug probe register */ -#define DEBUG_BLK_SEL_MISC 7 -#define DEBUG_BLK_SEL_SERDES 6 -#define DEBUG_BLK_SEL_EM 5 -#define DEBUG_BLK_SEL_SR 4 -#define DEBUG_BLK_SEL_EV 3 -#define DEBUG_BLK_SEL_RX 2 -#define DEBUG_BLK_SEL_TX 1 -#define DEBUG_BLK_SEL_BIU 0 - -/* FPGA build version */ -#define ALTERA_BUILD_REG_KER 0x0300 -#define VER_ALL_LBN 0 -#define VER_ALL_WIDTH 32 - -/* Spare EEPROM bits register (flash 0x390) */ -#define SPARE_REG_KER 0x310 -#define MEM_PERR_EN_TX_DATA_LBN 72 -#define MEM_PERR_EN_TX_DATA_WIDTH 2 - -/* Timer table for kernel access */ -#define TIMER_CMD_REG_KER 0x420 -#define TIMER_MODE_LBN 12 -#define TIMER_MODE_WIDTH 2 -#define TIMER_MODE_DIS 0 -#define TIMER_MODE_INT_HLDOFF 2 -#define TIMER_VAL_LBN 0 -#define TIMER_VAL_WIDTH 12 - -/* Driver generated event register */ -#define DRV_EV_REG_KER 0x440 -#define DRV_EV_QID_LBN 64 -#define DRV_EV_QID_WIDTH 12 -#define DRV_EV_DATA_LBN 0 -#define DRV_EV_DATA_WIDTH 64 - -/* Buffer table configuration register */ -#define BUF_TBL_CFG_REG_KER 0x600 -#define BUF_TBL_MODE_LBN 3 -#define BUF_TBL_MODE_WIDTH 1 -#define BUF_TBL_MODE_HALF 0 -#define BUF_TBL_MODE_FULL 1 - -/* SRAM receive descriptor cache configuration register */ -#define SRM_RX_DC_CFG_REG_KER 0x610 -#define SRM_RX_DC_BASE_ADR_LBN 0 -#define SRM_RX_DC_BASE_ADR_WIDTH 21 - -/* SRAM transmit descriptor cache configuration register */ -#define SRM_TX_DC_CFG_REG_KER 0x620 -#define SRM_TX_DC_BASE_ADR_LBN 0 -#define SRM_TX_DC_BASE_ADR_WIDTH 21 - -/* SRAM configuration register */ -#define SRM_CFG_REG_KER 0x630 -#define SRAM_OOB_BT_INIT_EN_LBN 3 -#define SRAM_OOB_BT_INIT_EN_WIDTH 1 -#define SRM_NUM_BANKS_AND_BANK_SIZE_LBN 0 -#define SRM_NUM_BANKS_AND_BANK_SIZE_WIDTH 3 -#define SRM_NB_BSZ_1BANKS_2M 0 -#define SRM_NB_BSZ_1BANKS_4M 1 -#define SRM_NB_BSZ_1BANKS_8M 2 -#define SRM_NB_BSZ_DEFAULT 3 /* char driver will set the default */ -#define SRM_NB_BSZ_2BANKS_4M 4 -#define SRM_NB_BSZ_2BANKS_8M 5 -#define SRM_NB_BSZ_2BANKS_16M 6 -#define SRM_NB_BSZ_RESERVED 7 - -/* Special buffer table update register */ -#define BUF_TBL_UPD_REG_KER 0x0650 -#define BUF_UPD_CMD_LBN 63 -#define BUF_UPD_CMD_WIDTH 1 -#define BUF_CLR_CMD_LBN 62 -#define BUF_CLR_CMD_WIDTH 1 -#define BUF_CLR_END_ID_LBN 32 -#define BUF_CLR_END_ID_WIDTH 20 -#define BUF_CLR_START_ID_LBN 0 -#define BUF_CLR_START_ID_WIDTH 20 - -/* Receive configuration register */ -#define RX_CFG_REG_KER 0x800 - -/* B0 */ -#define RX_INGR_EN_B0_LBN 47 -#define RX_INGR_EN_B0_WIDTH 1 -#define RX_DESC_PUSH_EN_B0_LBN 43 -#define RX_DESC_PUSH_EN_B0_WIDTH 1 -#define RX_XON_TX_TH_B0_LBN 33 -#define RX_XON_TX_TH_B0_WIDTH 5 -#define RX_XOFF_TX_TH_B0_LBN 28 -#define RX_XOFF_TX_TH_B0_WIDTH 5 -#define RX_USR_BUF_SIZE_B0_LBN 19 -#define RX_USR_BUF_SIZE_B0_WIDTH 9 -#define RX_XON_MAC_TH_B0_LBN 10 -#define RX_XON_MAC_TH_B0_WIDTH 9 -#define RX_XOFF_MAC_TH_B0_LBN 1 -#define RX_XOFF_MAC_TH_B0_WIDTH 9 -#define RX_XOFF_MAC_EN_B0_LBN 0 -#define RX_XOFF_MAC_EN_B0_WIDTH 1 - -/* A1 */ -#define RX_DESC_PUSH_EN_A1_LBN 35 -#define RX_DESC_PUSH_EN_A1_WIDTH 1 -#define RX_XON_TX_TH_A1_LBN 25 -#define RX_XON_TX_TH_A1_WIDTH 5 -#define RX_XOFF_TX_TH_A1_LBN 20 -#define RX_XOFF_TX_TH_A1_WIDTH 5 -#define RX_USR_BUF_SIZE_A1_LBN 11 -#define RX_USR_BUF_SIZE_A1_WIDTH 9 -#define RX_XON_MAC_TH_A1_LBN 6 -#define RX_XON_MAC_TH_A1_WIDTH 5 -#define RX_XOFF_MAC_TH_A1_LBN 1 -#define RX_XOFF_MAC_TH_A1_WIDTH 5 -#define RX_XOFF_MAC_EN_A1_LBN 0 -#define RX_XOFF_MAC_EN_A1_WIDTH 1 - -/* Receive filter control register */ -#define RX_FILTER_CTL_REG 0x810 -#define UDP_FULL_SRCH_LIMIT_LBN 32 -#define UDP_FULL_SRCH_LIMIT_WIDTH 8 -#define NUM_KER_LBN 24 -#define NUM_KER_WIDTH 2 -#define UDP_WILD_SRCH_LIMIT_LBN 16 -#define UDP_WILD_SRCH_LIMIT_WIDTH 8 -#define TCP_WILD_SRCH_LIMIT_LBN 8 -#define TCP_WILD_SRCH_LIMIT_WIDTH 8 -#define TCP_FULL_SRCH_LIMIT_LBN 0 -#define TCP_FULL_SRCH_LIMIT_WIDTH 8 - -/* RX queue flush register */ -#define RX_FLUSH_DESCQ_REG_KER 0x0820 -#define RX_FLUSH_DESCQ_CMD_LBN 24 -#define RX_FLUSH_DESCQ_CMD_WIDTH 1 -#define RX_FLUSH_DESCQ_LBN 0 -#define RX_FLUSH_DESCQ_WIDTH 12 - -/* Receive descriptor update register */ -#define RX_DESC_UPD_REG_KER_DWORD (0x830 + 12) -#define RX_DESC_WPTR_DWORD_LBN 0 -#define RX_DESC_WPTR_DWORD_WIDTH 12 - -/* Receive descriptor cache configuration register */ -#define RX_DC_CFG_REG_KER 0x840 -#define RX_DC_SIZE_LBN 0 -#define RX_DC_SIZE_WIDTH 2 - -#define RX_DC_PF_WM_REG_KER 0x850 -#define RX_DC_PF_LWM_LBN 0 -#define RX_DC_PF_LWM_WIDTH 6 - -/* RX no descriptor drop counter */ -#define RX_NODESC_DROP_REG_KER 0x880 -#define RX_NODESC_DROP_CNT_LBN 0 -#define RX_NODESC_DROP_CNT_WIDTH 16 - -/* RX black magic register */ -#define RX_SELF_RST_REG_KER 0x890 -#define RX_ISCSI_DIS_LBN 17 -#define RX_ISCSI_DIS_WIDTH 1 -#define RX_NODESC_WAIT_DIS_LBN 9 -#define RX_NODESC_WAIT_DIS_WIDTH 1 -#define RX_RECOVERY_EN_LBN 8 -#define RX_RECOVERY_EN_WIDTH 1 - -/* TX queue flush register */ -#define TX_FLUSH_DESCQ_REG_KER 0x0a00 -#define TX_FLUSH_DESCQ_CMD_LBN 12 -#define TX_FLUSH_DESCQ_CMD_WIDTH 1 -#define TX_FLUSH_DESCQ_LBN 0 -#define TX_FLUSH_DESCQ_WIDTH 12 - -/* Transmit descriptor update register */ -#define TX_DESC_UPD_REG_KER_DWORD (0xa10 + 12) -#define TX_DESC_WPTR_DWORD_LBN 0 -#define TX_DESC_WPTR_DWORD_WIDTH 12 - -/* Transmit descriptor cache configuration register */ -#define TX_DC_CFG_REG_KER 0xa20 -#define TX_DC_SIZE_LBN 0 -#define TX_DC_SIZE_WIDTH 2 - -/* Transmit checksum configuration register (A0/A1 only) */ -#define TX_CHKSM_CFG_REG_KER_A1 0xa30 - -/* Transmit configuration register */ -#define TX_CFG_REG_KER 0xa50 -#define TX_NO_EOP_DISC_EN_LBN 5 -#define TX_NO_EOP_DISC_EN_WIDTH 1 - -/* Transmit configuration register 2 */ -#define TX_CFG2_REG_KER 0xa80 -#define TX_CSR_PUSH_EN_LBN 89 -#define TX_CSR_PUSH_EN_WIDTH 1 -#define TX_RX_SPACER_LBN 64 -#define TX_RX_SPACER_WIDTH 8 -#define TX_SW_EV_EN_LBN 59 -#define TX_SW_EV_EN_WIDTH 1 -#define TX_RX_SPACER_EN_LBN 57 -#define TX_RX_SPACER_EN_WIDTH 1 -#define TX_PREF_THRESHOLD_LBN 19 -#define TX_PREF_THRESHOLD_WIDTH 2 -#define TX_ONE_PKT_PER_Q_LBN 18 -#define TX_ONE_PKT_PER_Q_WIDTH 1 -#define TX_DIS_NON_IP_EV_LBN 17 -#define TX_DIS_NON_IP_EV_WIDTH 1 -#define TX_FLUSH_MIN_LEN_EN_B0_LBN 7 -#define TX_FLUSH_MIN_LEN_EN_B0_WIDTH 1 - -/* PHY management transmit data register */ -#define MD_TXD_REG_KER 0xc00 -#define MD_TXD_LBN 0 -#define MD_TXD_WIDTH 16 - -/* PHY management receive data register */ -#define MD_RXD_REG_KER 0xc10 -#define MD_RXD_LBN 0 -#define MD_RXD_WIDTH 16 - -/* PHY management configuration & status register */ -#define MD_CS_REG_KER 0xc20 -#define MD_GC_LBN 4 -#define MD_GC_WIDTH 1 -#define MD_RIC_LBN 2 -#define MD_RIC_WIDTH 1 -#define MD_RDC_LBN 1 -#define MD_RDC_WIDTH 1 -#define MD_WRC_LBN 0 -#define MD_WRC_WIDTH 1 - -/* PHY management PHY address register */ -#define MD_PHY_ADR_REG_KER 0xc30 -#define MD_PHY_ADR_LBN 0 -#define MD_PHY_ADR_WIDTH 16 - -/* PHY management ID register */ -#define MD_ID_REG_KER 0xc40 -#define MD_PRT_ADR_LBN 11 -#define MD_PRT_ADR_WIDTH 5 -#define MD_DEV_ADR_LBN 6 -#define MD_DEV_ADR_WIDTH 5 - -/* PHY management status & mask register (DWORD read only) */ -#define MD_STAT_REG_KER 0xc50 -#define MD_BSERR_LBN 2 -#define MD_BSERR_WIDTH 1 -#define MD_LNFL_LBN 1 -#define MD_LNFL_WIDTH 1 -#define MD_BSY_LBN 0 -#define MD_BSY_WIDTH 1 - -/* Port 0 and 1 MAC stats registers */ -#define MAC0_STAT_DMA_REG_KER 0xc60 -#define MAC_STAT_DMA_CMD_LBN 48 -#define MAC_STAT_DMA_CMD_WIDTH 1 -#define MAC_STAT_DMA_ADR_LBN 0 -#define MAC_STAT_DMA_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) - -/* Port 0 and 1 MAC control registers */ -#define MAC0_CTRL_REG_KER 0xc80 -#define MAC_XOFF_VAL_LBN 16 -#define MAC_XOFF_VAL_WIDTH 16 -#define TXFIFO_DRAIN_EN_B0_LBN 7 -#define TXFIFO_DRAIN_EN_B0_WIDTH 1 -#define MAC_BCAD_ACPT_LBN 4 -#define MAC_BCAD_ACPT_WIDTH 1 -#define MAC_UC_PROM_LBN 3 -#define MAC_UC_PROM_WIDTH 1 -#define MAC_LINK_STATUS_LBN 2 -#define MAC_LINK_STATUS_WIDTH 1 -#define MAC_SPEED_LBN 0 -#define MAC_SPEED_WIDTH 2 - -/* 10G XAUI XGXS default values */ -#define XX_TXDRV_DEQ_DEFAULT 0xe /* deq=.6 */ -#define XX_TXDRV_DTX_DEFAULT 0x5 /* 1.25 */ -#define XX_SD_CTL_DRV_DEFAULT 0 /* 20mA */ - -/* Multicast address hash table */ -#define MAC_MCAST_HASH_REG0_KER 0xca0 -#define MAC_MCAST_HASH_REG1_KER 0xcb0 - -/* GMAC configuration register 1 */ -#define GM_CFG1_REG 0xe00 -#define GM_SW_RST_LBN 31 -#define GM_SW_RST_WIDTH 1 -#define GM_LOOP_LBN 8 -#define GM_LOOP_WIDTH 1 -#define GM_RX_FC_EN_LBN 5 -#define GM_RX_FC_EN_WIDTH 1 -#define GM_TX_FC_EN_LBN 4 -#define GM_TX_FC_EN_WIDTH 1 -#define GM_RX_EN_LBN 2 -#define GM_RX_EN_WIDTH 1 -#define GM_TX_EN_LBN 0 -#define GM_TX_EN_WIDTH 1 - -/* GMAC configuration register 2 */ -#define GM_CFG2_REG 0xe10 -#define GM_PAMBL_LEN_LBN 12 -#define GM_PAMBL_LEN_WIDTH 4 -#define GM_IF_MODE_LBN 8 -#define GM_IF_MODE_WIDTH 2 -#define GM_LEN_CHK_LBN 4 -#define GM_LEN_CHK_WIDTH 1 -#define GM_PAD_CRC_EN_LBN 2 -#define GM_PAD_CRC_EN_WIDTH 1 -#define GM_FD_LBN 0 -#define GM_FD_WIDTH 1 - -/* GMAC maximum frame length register */ -#define GM_MAX_FLEN_REG 0xe40 -#define GM_MAX_FLEN_LBN 0 -#define GM_MAX_FLEN_WIDTH 16 - -/* GMAC station address register 1 */ -#define GM_ADR1_REG 0xf00 -#define GM_HWADDR_5_LBN 24 -#define GM_HWADDR_5_WIDTH 8 -#define GM_HWADDR_4_LBN 16 -#define GM_HWADDR_4_WIDTH 8 -#define GM_HWADDR_3_LBN 8 -#define GM_HWADDR_3_WIDTH 8 -#define GM_HWADDR_2_LBN 0 -#define GM_HWADDR_2_WIDTH 8 - -/* GMAC station address register 2 */ -#define GM_ADR2_REG 0xf10 -#define GM_HWADDR_1_LBN 24 -#define GM_HWADDR_1_WIDTH 8 -#define GM_HWADDR_0_LBN 16 -#define GM_HWADDR_0_WIDTH 8 - -/* GMAC FIFO configuration register 0 */ -#define GMF_CFG0_REG 0xf20 -#define GMF_FTFENREQ_LBN 12 -#define GMF_FTFENREQ_WIDTH 1 -#define GMF_STFENREQ_LBN 11 -#define GMF_STFENREQ_WIDTH 1 -#define GMF_FRFENREQ_LBN 10 -#define GMF_FRFENREQ_WIDTH 1 -#define GMF_SRFENREQ_LBN 9 -#define GMF_SRFENREQ_WIDTH 1 -#define GMF_WTMENREQ_LBN 8 -#define GMF_WTMENREQ_WIDTH 1 - -/* GMAC FIFO configuration register 1 */ -#define GMF_CFG1_REG 0xf30 -#define GMF_CFGFRTH_LBN 16 -#define GMF_CFGFRTH_WIDTH 5 -#define GMF_CFGXOFFRTX_LBN 0 -#define GMF_CFGXOFFRTX_WIDTH 16 - -/* GMAC FIFO configuration register 2 */ -#define GMF_CFG2_REG 0xf40 -#define GMF_CFGHWM_LBN 16 -#define GMF_CFGHWM_WIDTH 6 -#define GMF_CFGLWM_LBN 0 -#define GMF_CFGLWM_WIDTH 6 - -/* GMAC FIFO configuration register 3 */ -#define GMF_CFG3_REG 0xf50 -#define GMF_CFGHWMFT_LBN 16 -#define GMF_CFGHWMFT_WIDTH 6 -#define GMF_CFGFTTH_LBN 0 -#define GMF_CFGFTTH_WIDTH 6 - -/* GMAC FIFO configuration register 4 */ -#define GMF_CFG4_REG 0xf60 -#define GMF_HSTFLTRFRM_PAUSE_LBN 12 -#define GMF_HSTFLTRFRM_PAUSE_WIDTH 12 - -/* GMAC FIFO configuration register 5 */ -#define GMF_CFG5_REG 0xf70 -#define GMF_CFGHDPLX_LBN 22 -#define GMF_CFGHDPLX_WIDTH 1 -#define GMF_CFGBYTMODE_LBN 19 -#define GMF_CFGBYTMODE_WIDTH 1 -#define GMF_HSTDRPLT64_LBN 18 -#define GMF_HSTDRPLT64_WIDTH 1 -#define GMF_HSTFLTRFRMDC_PAUSE_LBN 12 -#define GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1 - -/* XGMAC address register low */ -#define XM_ADR_LO_REG 0x1200 -#define XM_ADR_3_LBN 24 -#define XM_ADR_3_WIDTH 8 -#define XM_ADR_2_LBN 16 -#define XM_ADR_2_WIDTH 8 -#define XM_ADR_1_LBN 8 -#define XM_ADR_1_WIDTH 8 -#define XM_ADR_0_LBN 0 -#define XM_ADR_0_WIDTH 8 - -/* XGMAC address register high */ -#define XM_ADR_HI_REG 0x1210 -#define XM_ADR_5_LBN 8 -#define XM_ADR_5_WIDTH 8 -#define XM_ADR_4_LBN 0 -#define XM_ADR_4_WIDTH 8 - -/* XGMAC global configuration */ -#define XM_GLB_CFG_REG 0x1220 -#define XM_RX_STAT_EN_LBN 11 -#define XM_RX_STAT_EN_WIDTH 1 -#define XM_TX_STAT_EN_LBN 10 -#define XM_TX_STAT_EN_WIDTH 1 -#define XM_RX_JUMBO_MODE_LBN 6 -#define XM_RX_JUMBO_MODE_WIDTH 1 -#define XM_INTCLR_MODE_LBN 3 -#define XM_INTCLR_MODE_WIDTH 1 -#define XM_CORE_RST_LBN 0 -#define XM_CORE_RST_WIDTH 1 - -/* XGMAC transmit configuration */ -#define XM_TX_CFG_REG 0x1230 -#define XM_IPG_LBN 16 -#define XM_IPG_WIDTH 4 -#define XM_FCNTL_LBN 10 -#define XM_FCNTL_WIDTH 1 -#define XM_TXCRC_LBN 8 -#define XM_TXCRC_WIDTH 1 -#define XM_AUTO_PAD_LBN 5 -#define XM_AUTO_PAD_WIDTH 1 -#define XM_TX_PRMBL_LBN 2 -#define XM_TX_PRMBL_WIDTH 1 -#define XM_TXEN_LBN 1 -#define XM_TXEN_WIDTH 1 - -/* XGMAC receive configuration */ -#define XM_RX_CFG_REG 0x1240 -#define XM_PASS_CRC_ERR_LBN 25 -#define XM_PASS_CRC_ERR_WIDTH 1 -#define XM_ACPT_ALL_MCAST_LBN 11 -#define XM_ACPT_ALL_MCAST_WIDTH 1 -#define XM_ACPT_ALL_UCAST_LBN 9 -#define XM_ACPT_ALL_UCAST_WIDTH 1 -#define XM_AUTO_DEPAD_LBN 8 -#define XM_AUTO_DEPAD_WIDTH 1 -#define XM_RXEN_LBN 1 -#define XM_RXEN_WIDTH 1 - -/* XGMAC management interrupt mask register */ -#define XM_MGT_INT_MSK_REG_B0 0x1250 -#define XM_MSK_PRMBLE_ERR_LBN 2 -#define XM_MSK_PRMBLE_ERR_WIDTH 1 -#define XM_MSK_RMTFLT_LBN 1 -#define XM_MSK_RMTFLT_WIDTH 1 -#define XM_MSK_LCLFLT_LBN 0 -#define XM_MSK_LCLFLT_WIDTH 1 - -/* XGMAC flow control register */ -#define XM_FC_REG 0x1270 -#define XM_PAUSE_TIME_LBN 16 -#define XM_PAUSE_TIME_WIDTH 16 -#define XM_DIS_FCNTL_LBN 0 -#define XM_DIS_FCNTL_WIDTH 1 - -/* XGMAC pause time count register */ -#define XM_PAUSE_TIME_REG 0x1290 - -/* XGMAC transmit parameter register */ -#define XM_TX_PARAM_REG 0x012d0 -#define XM_TX_JUMBO_MODE_LBN 31 -#define XM_TX_JUMBO_MODE_WIDTH 1 -#define XM_MAX_TX_FRM_SIZE_LBN 16 -#define XM_MAX_TX_FRM_SIZE_WIDTH 14 - -/* XGMAC receive parameter register */ -#define XM_RX_PARAM_REG 0x12e0 -#define XM_MAX_RX_FRM_SIZE_LBN 0 -#define XM_MAX_RX_FRM_SIZE_WIDTH 14 - -/* XGMAC management interrupt status register */ -#define XM_MGT_INT_REG_B0 0x12f0 -#define XM_PRMBLE_ERR 2 -#define XM_PRMBLE_WIDTH 1 -#define XM_RMTFLT_LBN 1 -#define XM_RMTFLT_WIDTH 1 -#define XM_LCLFLT_LBN 0 -#define XM_LCLFLT_WIDTH 1 - -/* XGXS/XAUI powerdown/reset register */ -#define XX_PWR_RST_REG 0x1300 - -#define XX_SD_RST_ACT_LBN 16 -#define XX_SD_RST_ACT_WIDTH 1 -#define XX_PWRDND_EN_LBN 15 -#define XX_PWRDND_EN_WIDTH 1 -#define XX_PWRDNC_EN_LBN 14 -#define XX_PWRDNC_EN_WIDTH 1 -#define XX_PWRDNB_EN_LBN 13 -#define XX_PWRDNB_EN_WIDTH 1 -#define XX_PWRDNA_EN_LBN 12 -#define XX_PWRDNA_EN_WIDTH 1 -#define XX_RSTPLLCD_EN_LBN 9 -#define XX_RSTPLLCD_EN_WIDTH 1 -#define XX_RSTPLLAB_EN_LBN 8 -#define XX_RSTPLLAB_EN_WIDTH 1 -#define XX_RESETD_EN_LBN 7 -#define XX_RESETD_EN_WIDTH 1 -#define XX_RESETC_EN_LBN 6 -#define XX_RESETC_EN_WIDTH 1 -#define XX_RESETB_EN_LBN 5 -#define XX_RESETB_EN_WIDTH 1 -#define XX_RESETA_EN_LBN 4 -#define XX_RESETA_EN_WIDTH 1 -#define XX_RSTXGXSRX_EN_LBN 2 -#define XX_RSTXGXSRX_EN_WIDTH 1 -#define XX_RSTXGXSTX_EN_LBN 1 -#define XX_RSTXGXSTX_EN_WIDTH 1 -#define XX_RST_XX_EN_LBN 0 -#define XX_RST_XX_EN_WIDTH 1 - -/* XGXS/XAUI powerdown/reset control register */ -#define XX_SD_CTL_REG 0x1310 -#define XX_HIDRVD_LBN 15 -#define XX_HIDRVD_WIDTH 1 -#define XX_LODRVD_LBN 14 -#define XX_LODRVD_WIDTH 1 -#define XX_HIDRVC_LBN 13 -#define XX_HIDRVC_WIDTH 1 -#define XX_LODRVC_LBN 12 -#define XX_LODRVC_WIDTH 1 -#define XX_HIDRVB_LBN 11 -#define XX_HIDRVB_WIDTH 1 -#define XX_LODRVB_LBN 10 -#define XX_LODRVB_WIDTH 1 -#define XX_HIDRVA_LBN 9 -#define XX_HIDRVA_WIDTH 1 -#define XX_LODRVA_LBN 8 -#define XX_LODRVA_WIDTH 1 -#define XX_LPBKD_LBN 3 -#define XX_LPBKD_WIDTH 1 -#define XX_LPBKC_LBN 2 -#define XX_LPBKC_WIDTH 1 -#define XX_LPBKB_LBN 1 -#define XX_LPBKB_WIDTH 1 -#define XX_LPBKA_LBN 0 -#define XX_LPBKA_WIDTH 1 - -#define XX_TXDRV_CTL_REG 0x1320 -#define XX_DEQD_LBN 28 -#define XX_DEQD_WIDTH 4 -#define XX_DEQC_LBN 24 -#define XX_DEQC_WIDTH 4 -#define XX_DEQB_LBN 20 -#define XX_DEQB_WIDTH 4 -#define XX_DEQA_LBN 16 -#define XX_DEQA_WIDTH 4 -#define XX_DTXD_LBN 12 -#define XX_DTXD_WIDTH 4 -#define XX_DTXC_LBN 8 -#define XX_DTXC_WIDTH 4 -#define XX_DTXB_LBN 4 -#define XX_DTXB_WIDTH 4 -#define XX_DTXA_LBN 0 -#define XX_DTXA_WIDTH 4 - -/* XAUI XGXS core status register */ -#define XX_CORE_STAT_REG 0x1360 -#define XX_FORCE_SIG_LBN 24 -#define XX_FORCE_SIG_WIDTH 8 -#define XX_FORCE_SIG_DECODE_FORCED 0xff -#define XX_XGXS_LB_EN_LBN 23 -#define XX_XGXS_LB_EN_WIDTH 1 -#define XX_XGMII_LB_EN_LBN 22 -#define XX_XGMII_LB_EN_WIDTH 1 -#define XX_ALIGN_DONE_LBN 20 -#define XX_ALIGN_DONE_WIDTH 1 -#define XX_SYNC_STAT_LBN 16 -#define XX_SYNC_STAT_WIDTH 4 -#define XX_SYNC_STAT_DECODE_SYNCED 0xf -#define XX_COMMA_DET_LBN 12 -#define XX_COMMA_DET_WIDTH 4 -#define XX_COMMA_DET_DECODE_DETECTED 0xf -#define XX_COMMA_DET_RESET 0xf -#define XX_CHARERR_LBN 4 -#define XX_CHARERR_WIDTH 4 -#define XX_CHARERR_RESET 0xf -#define XX_DISPERR_LBN 0 -#define XX_DISPERR_WIDTH 4 -#define XX_DISPERR_RESET 0xf - -/* Receive filter table */ -#define RX_FILTER_TBL0 0xF00000 - -/* Receive descriptor pointer table */ -#define RX_DESC_PTR_TBL_KER_A1 0x11800 -#define RX_DESC_PTR_TBL_KER_B0 0xF40000 -#define RX_DESC_PTR_TBL_KER_P0 0x900 -#define RX_ISCSI_DDIG_EN_LBN 88 -#define RX_ISCSI_DDIG_EN_WIDTH 1 -#define RX_ISCSI_HDIG_EN_LBN 87 -#define RX_ISCSI_HDIG_EN_WIDTH 1 -#define RX_DESCQ_BUF_BASE_ID_LBN 36 -#define RX_DESCQ_BUF_BASE_ID_WIDTH 20 -#define RX_DESCQ_EVQ_ID_LBN 24 -#define RX_DESCQ_EVQ_ID_WIDTH 12 -#define RX_DESCQ_OWNER_ID_LBN 10 -#define RX_DESCQ_OWNER_ID_WIDTH 14 -#define RX_DESCQ_LABEL_LBN 5 -#define RX_DESCQ_LABEL_WIDTH 5 -#define RX_DESCQ_SIZE_LBN 3 -#define RX_DESCQ_SIZE_WIDTH 2 -#define RX_DESCQ_SIZE_4K 3 -#define RX_DESCQ_SIZE_2K 2 -#define RX_DESCQ_SIZE_1K 1 -#define RX_DESCQ_SIZE_512 0 -#define RX_DESCQ_TYPE_LBN 2 -#define RX_DESCQ_TYPE_WIDTH 1 -#define RX_DESCQ_JUMBO_LBN 1 -#define RX_DESCQ_JUMBO_WIDTH 1 -#define RX_DESCQ_EN_LBN 0 -#define RX_DESCQ_EN_WIDTH 1 - -/* Transmit descriptor pointer table */ -#define TX_DESC_PTR_TBL_KER_A1 0x11900 -#define TX_DESC_PTR_TBL_KER_B0 0xF50000 -#define TX_DESC_PTR_TBL_KER_P0 0xa40 -#define TX_NON_IP_DROP_DIS_B0_LBN 91 -#define TX_NON_IP_DROP_DIS_B0_WIDTH 1 -#define TX_IP_CHKSM_DIS_B0_LBN 90 -#define TX_IP_CHKSM_DIS_B0_WIDTH 1 -#define TX_TCP_CHKSM_DIS_B0_LBN 89 -#define TX_TCP_CHKSM_DIS_B0_WIDTH 1 -#define TX_DESCQ_EN_LBN 88 -#define TX_DESCQ_EN_WIDTH 1 -#define TX_ISCSI_DDIG_EN_LBN 87 -#define TX_ISCSI_DDIG_EN_WIDTH 1 -#define TX_ISCSI_HDIG_EN_LBN 86 -#define TX_ISCSI_HDIG_EN_WIDTH 1 -#define TX_DESCQ_BUF_BASE_ID_LBN 36 -#define TX_DESCQ_BUF_BASE_ID_WIDTH 20 -#define TX_DESCQ_EVQ_ID_LBN 24 -#define TX_DESCQ_EVQ_ID_WIDTH 12 -#define TX_DESCQ_OWNER_ID_LBN 10 -#define TX_DESCQ_OWNER_ID_WIDTH 14 -#define TX_DESCQ_LABEL_LBN 5 -#define TX_DESCQ_LABEL_WIDTH 5 -#define TX_DESCQ_SIZE_LBN 3 -#define TX_DESCQ_SIZE_WIDTH 2 -#define TX_DESCQ_SIZE_4K 3 -#define TX_DESCQ_SIZE_2K 2 -#define TX_DESCQ_SIZE_1K 1 -#define TX_DESCQ_SIZE_512 0 -#define TX_DESCQ_TYPE_LBN 1 -#define TX_DESCQ_TYPE_WIDTH 2 - -/* Event queue pointer */ -#define EVQ_PTR_TBL_KER_A1 0x11a00 -#define EVQ_PTR_TBL_KER_B0 0xf60000 -#define EVQ_PTR_TBL_KER_P0 0x500 -#define EVQ_EN_LBN 23 -#define EVQ_EN_WIDTH 1 -#define EVQ_SIZE_LBN 20 -#define EVQ_SIZE_WIDTH 3 -#define EVQ_SIZE_32K 6 -#define EVQ_SIZE_16K 5 -#define EVQ_SIZE_8K 4 -#define EVQ_SIZE_4K 3 -#define EVQ_SIZE_2K 2 -#define EVQ_SIZE_1K 1 -#define EVQ_SIZE_512 0 -#define EVQ_BUF_BASE_ID_LBN 0 -#define EVQ_BUF_BASE_ID_WIDTH 20 - -/* Event queue read pointer */ -#define EVQ_RPTR_REG_KER_A1 0x11b00 -#define EVQ_RPTR_REG_KER_B0 0xfa0000 -#define EVQ_RPTR_REG_KER_DWORD (EVQ_RPTR_REG_KER + 0) -#define EVQ_RPTR_DWORD_LBN 0 -#define EVQ_RPTR_DWORD_WIDTH 14 - -/* RSS indirection table */ -#define RX_RSS_INDIR_TBL_B0 0xFB0000 -#define RX_RSS_INDIR_ENT_B0_LBN 0 -#define RX_RSS_INDIR_ENT_B0_WIDTH 6 - -/* Special buffer descriptors (full-mode) */ -#define BUF_FULL_TBL_KER_A1 0x8000 -#define BUF_FULL_TBL_KER_B0 0x800000 -#define IP_DAT_BUF_SIZE_LBN 50 -#define IP_DAT_BUF_SIZE_WIDTH 1 -#define IP_DAT_BUF_SIZE_8K 1 -#define IP_DAT_BUF_SIZE_4K 0 -#define BUF_ADR_REGION_LBN 48 -#define BUF_ADR_REGION_WIDTH 2 -#define BUF_ADR_FBUF_LBN 14 -#define BUF_ADR_FBUF_WIDTH 34 -#define BUF_OWNER_ID_FBUF_LBN 0 -#define BUF_OWNER_ID_FBUF_WIDTH 14 - -/* Transmit descriptor */ -#define TX_KER_PORT_LBN 63 -#define TX_KER_PORT_WIDTH 1 -#define TX_KER_CONT_LBN 62 -#define TX_KER_CONT_WIDTH 1 -#define TX_KER_BYTE_CNT_LBN 48 -#define TX_KER_BYTE_CNT_WIDTH 14 -#define TX_KER_BUF_REGION_LBN 46 -#define TX_KER_BUF_REGION_WIDTH 2 -#define TX_KER_BUF_REGION0_DECODE 0 -#define TX_KER_BUF_REGION1_DECODE 1 -#define TX_KER_BUF_REGION2_DECODE 2 -#define TX_KER_BUF_REGION3_DECODE 3 -#define TX_KER_BUF_ADR_LBN 0 -#define TX_KER_BUF_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) - -/* Receive descriptor */ -#define RX_KER_BUF_SIZE_LBN 48 -#define RX_KER_BUF_SIZE_WIDTH 14 -#define RX_KER_BUF_REGION_LBN 46 -#define RX_KER_BUF_REGION_WIDTH 2 -#define RX_KER_BUF_REGION0_DECODE 0 -#define RX_KER_BUF_REGION1_DECODE 1 -#define RX_KER_BUF_REGION2_DECODE 2 -#define RX_KER_BUF_REGION3_DECODE 3 -#define RX_KER_BUF_ADR_LBN 0 -#define RX_KER_BUF_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) - -/************************************************************************** - * - * Falcon events - * - ************************************************************************** - */ - -/* Event queue entries */ -#define EV_CODE_LBN 60 -#define EV_CODE_WIDTH 4 -#define RX_IP_EV_DECODE 0 -#define TX_IP_EV_DECODE 2 -#define DRIVER_EV_DECODE 5 -#define GLOBAL_EV_DECODE 6 -#define DRV_GEN_EV_DECODE 7 -#define WHOLE_EVENT_LBN 0 -#define WHOLE_EVENT_WIDTH 64 - -/* Receive events */ -#define RX_EV_PKT_OK_LBN 56 -#define RX_EV_PKT_OK_WIDTH 1 -#define RX_EV_PAUSE_FRM_ERR_LBN 55 -#define RX_EV_PAUSE_FRM_ERR_WIDTH 1 -#define RX_EV_BUF_OWNER_ID_ERR_LBN 54 -#define RX_EV_BUF_OWNER_ID_ERR_WIDTH 1 -#define RX_EV_IF_FRAG_ERR_LBN 53 -#define RX_EV_IF_FRAG_ERR_WIDTH 1 -#define RX_EV_IP_HDR_CHKSUM_ERR_LBN 52 -#define RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1 -#define RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51 -#define RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1 -#define RX_EV_ETH_CRC_ERR_LBN 50 -#define RX_EV_ETH_CRC_ERR_WIDTH 1 -#define RX_EV_FRM_TRUNC_LBN 49 -#define RX_EV_FRM_TRUNC_WIDTH 1 -#define RX_EV_DRIB_NIB_LBN 48 -#define RX_EV_DRIB_NIB_WIDTH 1 -#define RX_EV_TOBE_DISC_LBN 47 -#define RX_EV_TOBE_DISC_WIDTH 1 -#define RX_EV_PKT_TYPE_LBN 44 -#define RX_EV_PKT_TYPE_WIDTH 3 -#define RX_EV_PKT_TYPE_ETH_DECODE 0 -#define RX_EV_PKT_TYPE_LLC_DECODE 1 -#define RX_EV_PKT_TYPE_JUMBO_DECODE 2 -#define RX_EV_PKT_TYPE_VLAN_DECODE 3 -#define RX_EV_PKT_TYPE_VLAN_LLC_DECODE 4 -#define RX_EV_PKT_TYPE_VLAN_JUMBO_DECODE 5 -#define RX_EV_HDR_TYPE_LBN 42 -#define RX_EV_HDR_TYPE_WIDTH 2 -#define RX_EV_HDR_TYPE_TCP_IPV4_DECODE 0 -#define RX_EV_HDR_TYPE_UDP_IPV4_DECODE 1 -#define RX_EV_HDR_TYPE_OTHER_IP_DECODE 2 -#define RX_EV_HDR_TYPE_NON_IP_DECODE 3 -#define RX_EV_HDR_TYPE_HAS_CHECKSUMS(hdr_type) \ - ((hdr_type) <= RX_EV_HDR_TYPE_UDP_IPV4_DECODE) -#define RX_EV_MCAST_HASH_MATCH_LBN 40 -#define RX_EV_MCAST_HASH_MATCH_WIDTH 1 -#define RX_EV_MCAST_PKT_LBN 39 -#define RX_EV_MCAST_PKT_WIDTH 1 -#define RX_EV_Q_LABEL_LBN 32 -#define RX_EV_Q_LABEL_WIDTH 5 -#define RX_EV_JUMBO_CONT_LBN 31 -#define RX_EV_JUMBO_CONT_WIDTH 1 -#define RX_EV_BYTE_CNT_LBN 16 -#define RX_EV_BYTE_CNT_WIDTH 14 -#define RX_EV_SOP_LBN 15 -#define RX_EV_SOP_WIDTH 1 -#define RX_EV_DESC_PTR_LBN 0 -#define RX_EV_DESC_PTR_WIDTH 12 - -/* Transmit events */ -#define TX_EV_PKT_ERR_LBN 38 -#define TX_EV_PKT_ERR_WIDTH 1 -#define TX_EV_Q_LABEL_LBN 32 -#define TX_EV_Q_LABEL_WIDTH 5 -#define TX_EV_WQ_FF_FULL_LBN 15 -#define TX_EV_WQ_FF_FULL_WIDTH 1 -#define TX_EV_COMP_LBN 12 -#define TX_EV_COMP_WIDTH 1 -#define TX_EV_DESC_PTR_LBN 0 -#define TX_EV_DESC_PTR_WIDTH 12 - -/* Driver events */ -#define DRIVER_EV_SUB_CODE_LBN 56 -#define DRIVER_EV_SUB_CODE_WIDTH 4 -#define DRIVER_EV_SUB_DATA_LBN 0 -#define DRIVER_EV_SUB_DATA_WIDTH 14 -#define TX_DESCQ_FLS_DONE_EV_DECODE 0 -#define RX_DESCQ_FLS_DONE_EV_DECODE 1 -#define EVQ_INIT_DONE_EV_DECODE 2 -#define EVQ_NOT_EN_EV_DECODE 3 -#define RX_DESCQ_FLSFF_OVFL_EV_DECODE 4 -#define SRM_UPD_DONE_EV_DECODE 5 -#define WAKE_UP_EV_DECODE 6 -#define TX_PKT_NON_TCP_UDP_DECODE 9 -#define TIMER_EV_DECODE 10 -#define RX_RECOVERY_EV_DECODE 11 -#define RX_DSC_ERROR_EV_DECODE 14 -#define TX_DSC_ERROR_EV_DECODE 15 -#define DRIVER_EV_TX_DESCQ_ID_LBN 0 -#define DRIVER_EV_TX_DESCQ_ID_WIDTH 12 -#define DRIVER_EV_RX_FLUSH_FAIL_LBN 12 -#define DRIVER_EV_RX_FLUSH_FAIL_WIDTH 1 -#define DRIVER_EV_RX_DESCQ_ID_LBN 0 -#define DRIVER_EV_RX_DESCQ_ID_WIDTH 12 -#define SRM_CLR_EV_DECODE 0 -#define SRM_UPD_EV_DECODE 1 -#define SRM_ILLCLR_EV_DECODE 2 - -/* Global events */ -#define RX_RECOVERY_B0_LBN 12 -#define RX_RECOVERY_B0_WIDTH 1 -#define XG_MNT_INTR_B0_LBN 11 -#define XG_MNT_INTR_B0_WIDTH 1 -#define RX_RECOVERY_A1_LBN 11 -#define RX_RECOVERY_A1_WIDTH 1 -#define XFP_PHY_INTR_LBN 10 -#define XFP_PHY_INTR_WIDTH 1 -#define XG_PHY_INTR_LBN 9 -#define XG_PHY_INTR_WIDTH 1 -#define G_PHY1_INTR_LBN 8 -#define G_PHY1_INTR_WIDTH 1 -#define G_PHY0_INTR_LBN 7 -#define G_PHY0_INTR_WIDTH 1 - -/* Driver-generated test events */ -#define EVQ_MAGIC_LBN 0 -#define EVQ_MAGIC_WIDTH 32 - -/************************************************************************** - * - * Falcon MAC stats - * - ************************************************************************** - * - */ - -#define GRxGoodOct_offset 0x0 -#define GRxGoodOct_WIDTH 48 -#define GRxBadOct_offset 0x8 -#define GRxBadOct_WIDTH 48 -#define GRxMissPkt_offset 0x10 -#define GRxMissPkt_WIDTH 32 -#define GRxFalseCRS_offset 0x14 -#define GRxFalseCRS_WIDTH 32 -#define GRxPausePkt_offset 0x18 -#define GRxPausePkt_WIDTH 32 -#define GRxBadPkt_offset 0x1C -#define GRxBadPkt_WIDTH 32 -#define GRxUcastPkt_offset 0x20 -#define GRxUcastPkt_WIDTH 32 -#define GRxMcastPkt_offset 0x24 -#define GRxMcastPkt_WIDTH 32 -#define GRxBcastPkt_offset 0x28 -#define GRxBcastPkt_WIDTH 32 -#define GRxGoodLt64Pkt_offset 0x2C -#define GRxGoodLt64Pkt_WIDTH 32 -#define GRxBadLt64Pkt_offset 0x30 -#define GRxBadLt64Pkt_WIDTH 32 -#define GRx64Pkt_offset 0x34 -#define GRx64Pkt_WIDTH 32 -#define GRx65to127Pkt_offset 0x38 -#define GRx65to127Pkt_WIDTH 32 -#define GRx128to255Pkt_offset 0x3C -#define GRx128to255Pkt_WIDTH 32 -#define GRx256to511Pkt_offset 0x40 -#define GRx256to511Pkt_WIDTH 32 -#define GRx512to1023Pkt_offset 0x44 -#define GRx512to1023Pkt_WIDTH 32 -#define GRx1024to15xxPkt_offset 0x48 -#define GRx1024to15xxPkt_WIDTH 32 -#define GRx15xxtoJumboPkt_offset 0x4C -#define GRx15xxtoJumboPkt_WIDTH 32 -#define GRxGtJumboPkt_offset 0x50 -#define GRxGtJumboPkt_WIDTH 32 -#define GRxFcsErr64to15xxPkt_offset 0x54 -#define GRxFcsErr64to15xxPkt_WIDTH 32 -#define GRxFcsErr15xxtoJumboPkt_offset 0x58 -#define GRxFcsErr15xxtoJumboPkt_WIDTH 32 -#define GRxFcsErrGtJumboPkt_offset 0x5C -#define GRxFcsErrGtJumboPkt_WIDTH 32 -#define GTxGoodBadOct_offset 0x80 -#define GTxGoodBadOct_WIDTH 48 -#define GTxGoodOct_offset 0x88 -#define GTxGoodOct_WIDTH 48 -#define GTxSglColPkt_offset 0x90 -#define GTxSglColPkt_WIDTH 32 -#define GTxMultColPkt_offset 0x94 -#define GTxMultColPkt_WIDTH 32 -#define GTxExColPkt_offset 0x98 -#define GTxExColPkt_WIDTH 32 -#define GTxDefPkt_offset 0x9C -#define GTxDefPkt_WIDTH 32 -#define GTxLateCol_offset 0xA0 -#define GTxLateCol_WIDTH 32 -#define GTxExDefPkt_offset 0xA4 -#define GTxExDefPkt_WIDTH 32 -#define GTxPausePkt_offset 0xA8 -#define GTxPausePkt_WIDTH 32 -#define GTxBadPkt_offset 0xAC -#define GTxBadPkt_WIDTH 32 -#define GTxUcastPkt_offset 0xB0 -#define GTxUcastPkt_WIDTH 32 -#define GTxMcastPkt_offset 0xB4 -#define GTxMcastPkt_WIDTH 32 -#define GTxBcastPkt_offset 0xB8 -#define GTxBcastPkt_WIDTH 32 -#define GTxLt64Pkt_offset 0xBC -#define GTxLt64Pkt_WIDTH 32 -#define GTx64Pkt_offset 0xC0 -#define GTx64Pkt_WIDTH 32 -#define GTx65to127Pkt_offset 0xC4 -#define GTx65to127Pkt_WIDTH 32 -#define GTx128to255Pkt_offset 0xC8 -#define GTx128to255Pkt_WIDTH 32 -#define GTx256to511Pkt_offset 0xCC -#define GTx256to511Pkt_WIDTH 32 -#define GTx512to1023Pkt_offset 0xD0 -#define GTx512to1023Pkt_WIDTH 32 -#define GTx1024to15xxPkt_offset 0xD4 -#define GTx1024to15xxPkt_WIDTH 32 -#define GTx15xxtoJumboPkt_offset 0xD8 -#define GTx15xxtoJumboPkt_WIDTH 32 -#define GTxGtJumboPkt_offset 0xDC -#define GTxGtJumboPkt_WIDTH 32 -#define GTxNonTcpUdpPkt_offset 0xE0 -#define GTxNonTcpUdpPkt_WIDTH 16 -#define GTxMacSrcErrPkt_offset 0xE4 -#define GTxMacSrcErrPkt_WIDTH 16 -#define GTxIpSrcErrPkt_offset 0xE8 -#define GTxIpSrcErrPkt_WIDTH 16 -#define GDmaDone_offset 0xEC -#define GDmaDone_WIDTH 32 - -#define XgRxOctets_offset 0x0 -#define XgRxOctets_WIDTH 48 -#define XgRxOctetsOK_offset 0x8 -#define XgRxOctetsOK_WIDTH 48 -#define XgRxPkts_offset 0x10 -#define XgRxPkts_WIDTH 32 -#define XgRxPktsOK_offset 0x14 -#define XgRxPktsOK_WIDTH 32 -#define XgRxBroadcastPkts_offset 0x18 -#define XgRxBroadcastPkts_WIDTH 32 -#define XgRxMulticastPkts_offset 0x1C -#define XgRxMulticastPkts_WIDTH 32 -#define XgRxUnicastPkts_offset 0x20 -#define XgRxUnicastPkts_WIDTH 32 -#define XgRxUndersizePkts_offset 0x24 -#define XgRxUndersizePkts_WIDTH 32 -#define XgRxOversizePkts_offset 0x28 -#define XgRxOversizePkts_WIDTH 32 -#define XgRxJabberPkts_offset 0x2C -#define XgRxJabberPkts_WIDTH 32 -#define XgRxUndersizeFCSerrorPkts_offset 0x30 -#define XgRxUndersizeFCSerrorPkts_WIDTH 32 -#define XgRxDropEvents_offset 0x34 -#define XgRxDropEvents_WIDTH 32 -#define XgRxFCSerrorPkts_offset 0x38 -#define XgRxFCSerrorPkts_WIDTH 32 -#define XgRxAlignError_offset 0x3C -#define XgRxAlignError_WIDTH 32 -#define XgRxSymbolError_offset 0x40 -#define XgRxSymbolError_WIDTH 32 -#define XgRxInternalMACError_offset 0x44 -#define XgRxInternalMACError_WIDTH 32 -#define XgRxControlPkts_offset 0x48 -#define XgRxControlPkts_WIDTH 32 -#define XgRxPausePkts_offset 0x4C -#define XgRxPausePkts_WIDTH 32 -#define XgRxPkts64Octets_offset 0x50 -#define XgRxPkts64Octets_WIDTH 32 -#define XgRxPkts65to127Octets_offset 0x54 -#define XgRxPkts65to127Octets_WIDTH 32 -#define XgRxPkts128to255Octets_offset 0x58 -#define XgRxPkts128to255Octets_WIDTH 32 -#define XgRxPkts256to511Octets_offset 0x5C -#define XgRxPkts256to511Octets_WIDTH 32 -#define XgRxPkts512to1023Octets_offset 0x60 -#define XgRxPkts512to1023Octets_WIDTH 32 -#define XgRxPkts1024to15xxOctets_offset 0x64 -#define XgRxPkts1024to15xxOctets_WIDTH 32 -#define XgRxPkts15xxtoMaxOctets_offset 0x68 -#define XgRxPkts15xxtoMaxOctets_WIDTH 32 -#define XgRxLengthError_offset 0x6C -#define XgRxLengthError_WIDTH 32 -#define XgTxPkts_offset 0x80 -#define XgTxPkts_WIDTH 32 -#define XgTxOctets_offset 0x88 -#define XgTxOctets_WIDTH 48 -#define XgTxMulticastPkts_offset 0x90 -#define XgTxMulticastPkts_WIDTH 32 -#define XgTxBroadcastPkts_offset 0x94 -#define XgTxBroadcastPkts_WIDTH 32 -#define XgTxUnicastPkts_offset 0x98 -#define XgTxUnicastPkts_WIDTH 32 -#define XgTxControlPkts_offset 0x9C -#define XgTxControlPkts_WIDTH 32 -#define XgTxPausePkts_offset 0xA0 -#define XgTxPausePkts_WIDTH 32 -#define XgTxPkts64Octets_offset 0xA4 -#define XgTxPkts64Octets_WIDTH 32 -#define XgTxPkts65to127Octets_offset 0xA8 -#define XgTxPkts65to127Octets_WIDTH 32 -#define XgTxPkts128to255Octets_offset 0xAC -#define XgTxPkts128to255Octets_WIDTH 32 -#define XgTxPkts256to511Octets_offset 0xB0 -#define XgTxPkts256to511Octets_WIDTH 32 -#define XgTxPkts512to1023Octets_offset 0xB4 -#define XgTxPkts512to1023Octets_WIDTH 32 -#define XgTxPkts1024to15xxOctets_offset 0xB8 -#define XgTxPkts1024to15xxOctets_WIDTH 32 -#define XgTxPkts1519toMaxOctets_offset 0xBC -#define XgTxPkts1519toMaxOctets_WIDTH 32 -#define XgTxUndersizePkts_offset 0xC0 -#define XgTxUndersizePkts_WIDTH 32 -#define XgTxOversizePkts_offset 0xC4 -#define XgTxOversizePkts_WIDTH 32 -#define XgTxNonTcpUdpPkt_offset 0xC8 -#define XgTxNonTcpUdpPkt_WIDTH 16 -#define XgTxMacSrcErrPkt_offset 0xCC -#define XgTxMacSrcErrPkt_WIDTH 16 -#define XgTxIpSrcErrPkt_offset 0xD0 -#define XgTxIpSrcErrPkt_WIDTH 16 -#define XgDmaDone_offset 0xD4 - -#define FALCON_STATS_NOT_DONE 0x00000000 -#define FALCON_STATS_DONE 0xffffffff - -/* Interrupt status register bits */ -#define FATAL_INT_LBN 64 -#define FATAL_INT_WIDTH 1 -#define INT_EVQS_LBN 40 -#define INT_EVQS_WIDTH 4 - -/************************************************************************** - * - * Falcon non-volatile configuration - * - ************************************************************************** - */ - -/* Board configuration v2 (v1 is obsolete; later versions are compatible) */ -struct falcon_nvconfig_board_v2 { - __le16 nports; - u8 port0_phy_addr; - u8 port0_phy_type; - u8 port1_phy_addr; - u8 port1_phy_type; - __le16 asic_sub_revision; - __le16 board_revision; -} __packed; - -/* Board configuration v3 extra information */ -struct falcon_nvconfig_board_v3 { - __le32 spi_device_type[2]; -} __packed; - -/* Bit numbers for spi_device_type */ -#define SPI_DEV_TYPE_SIZE_LBN 0 -#define SPI_DEV_TYPE_SIZE_WIDTH 5 -#define SPI_DEV_TYPE_ADDR_LEN_LBN 6 -#define SPI_DEV_TYPE_ADDR_LEN_WIDTH 2 -#define SPI_DEV_TYPE_ERASE_CMD_LBN 8 -#define SPI_DEV_TYPE_ERASE_CMD_WIDTH 8 -#define SPI_DEV_TYPE_ERASE_SIZE_LBN 16 -#define SPI_DEV_TYPE_ERASE_SIZE_WIDTH 5 -#define SPI_DEV_TYPE_BLOCK_SIZE_LBN 24 -#define SPI_DEV_TYPE_BLOCK_SIZE_WIDTH 5 -#define SPI_DEV_TYPE_FIELD(type, field) \ - (((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(EFX_WIDTH(field))) - -#define NVCONFIG_OFFSET 0x300 - -#define NVCONFIG_BOARD_MAGIC_NUM 0xFA1C -struct falcon_nvconfig { - efx_oword_t ee_vpd_cfg_reg; /* 0x300 */ - u8 mac_address[2][8]; /* 0x310 */ - efx_oword_t pcie_sd_ctl0123_reg; /* 0x320 */ - efx_oword_t pcie_sd_ctl45_reg; /* 0x330 */ - efx_oword_t pcie_pcs_ctl_stat_reg; /* 0x340 */ - efx_oword_t hw_init_reg; /* 0x350 */ - efx_oword_t nic_stat_reg; /* 0x360 */ - efx_oword_t glb_ctl_reg; /* 0x370 */ - efx_oword_t srm_cfg_reg; /* 0x380 */ - efx_oword_t spare_reg; /* 0x390 */ - __le16 board_magic_num; /* 0x3A0 */ - __le16 board_struct_ver; - __le16 board_checksum; - struct falcon_nvconfig_board_v2 board_v2; - efx_oword_t ee_base_page_reg; /* 0x3B0 */ - struct falcon_nvconfig_board_v3 board_v3; -} __packed; - -#endif /* EFX_FALCON_HWDEFS_H */ diff --git a/drivers/net/sfc/falcon_io.h b/drivers/net/sfc/falcon_io.h deleted file mode 100644 index 8883092dae97..000000000000 --- a/drivers/net/sfc/falcon_io.h +++ /dev/null @@ -1,258 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#ifndef EFX_FALCON_IO_H -#define EFX_FALCON_IO_H - -#include <linux/io.h> -#include <linux/spinlock.h> - -/************************************************************************** - * - * Falcon hardware access - * - ************************************************************************** - * - * Notes on locking strategy: - * - * Most Falcon registers require 16-byte (or 8-byte, for SRAM - * registers) atomic writes which necessitates locking. - * Under normal operation few writes to the Falcon BAR are made and these - * registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and TX_DESC_UPD_REG) are special - * cased to allow 4-byte (hence lockless) accesses. - * - * It *is* safe to write to these 4-byte registers in the middle of an - * access to an 8-byte or 16-byte register. We therefore use a - * spinlock to protect accesses to the larger registers, but no locks - * for the 4-byte registers. - * - * A write barrier is needed to ensure that DW3 is written after DW0/1/2 - * due to the way the 16byte registers are "collected" in the Falcon BIU - * - * We also lock when carrying out reads, to ensure consistency of the - * data (made possible since the BIU reads all 128 bits into a cache). - * Reads are very rare, so this isn't a significant performance - * impact. (Most data transferred from NIC to host is DMAed directly - * into host memory). - * - * I/O BAR access uses locks for both reads and writes (but is only provided - * for testing purposes). - */ - -/* Special buffer descriptors (Falcon SRAM) */ -#define BUF_TBL_KER_A1 0x18000 -#define BUF_TBL_KER_B0 0x800000 - - -#if BITS_PER_LONG == 64 -#define FALCON_USE_QWORD_IO 1 -#endif - -#ifdef FALCON_USE_QWORD_IO -static inline void _falcon_writeq(struct efx_nic *efx, __le64 value, - unsigned int reg) -{ - __raw_writeq((__force u64)value, efx->membase + reg); -} -static inline __le64 _falcon_readq(struct efx_nic *efx, unsigned int reg) -{ - return (__force __le64)__raw_readq(efx->membase + reg); -} -#endif - -static inline void _falcon_writel(struct efx_nic *efx, __le32 value, - unsigned int reg) -{ - __raw_writel((__force u32)value, efx->membase + reg); -} -static inline __le32 _falcon_readl(struct efx_nic *efx, unsigned int reg) -{ - return (__force __le32)__raw_readl(efx->membase + reg); -} - -/* Writes to a normal 16-byte Falcon register, locking as appropriate. */ -static inline void falcon_write(struct efx_nic *efx, efx_oword_t *value, - unsigned int reg) -{ - unsigned long flags; - - EFX_REGDUMP(efx, "writing register %x with " EFX_OWORD_FMT "\n", reg, - EFX_OWORD_VAL(*value)); - - spin_lock_irqsave(&efx->biu_lock, flags); -#ifdef FALCON_USE_QWORD_IO - _falcon_writeq(efx, value->u64[0], reg + 0); - wmb(); - _falcon_writeq(efx, value->u64[1], reg + 8); -#else - _falcon_writel(efx, value->u32[0], reg + 0); - _falcon_writel(efx, value->u32[1], reg + 4); - _falcon_writel(efx, value->u32[2], reg + 8); - wmb(); - _falcon_writel(efx, value->u32[3], reg + 12); -#endif - mmiowb(); - spin_unlock_irqrestore(&efx->biu_lock, flags); -} - -/* Writes to an 8-byte Falcon SRAM register, locking as appropriate. */ -static inline void falcon_write_sram(struct efx_nic *efx, efx_qword_t *value, - unsigned int index) -{ - unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value)); - unsigned long flags; - - EFX_REGDUMP(efx, "writing SRAM register %x with " EFX_QWORD_FMT "\n", - reg, EFX_QWORD_VAL(*value)); - - spin_lock_irqsave(&efx->biu_lock, flags); -#ifdef FALCON_USE_QWORD_IO - _falcon_writeq(efx, value->u64[0], reg + 0); -#else - _falcon_writel(efx, value->u32[0], reg + 0); - wmb(); - _falcon_writel(efx, value->u32[1], reg + 4); -#endif - mmiowb(); - spin_unlock_irqrestore(&efx->biu_lock, flags); -} - -/* Write dword to Falcon register that allows partial writes - * - * Some Falcon registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and - * TX_DESC_UPD_REG) can be written to as a single dword. This allows - * for lockless writes. - */ -static inline void falcon_writel(struct efx_nic *efx, efx_dword_t *value, - unsigned int reg) -{ - EFX_REGDUMP(efx, "writing partial register %x with "EFX_DWORD_FMT"\n", - reg, EFX_DWORD_VAL(*value)); - - /* No lock required */ - _falcon_writel(efx, value->u32[0], reg); -} - -/* Read from a Falcon register - * - * This reads an entire 16-byte Falcon register in one go, locking as - * appropriate. It is essential to read the first dword first, as this - * prompts Falcon to load the current value into the shadow register. - */ -static inline void falcon_read(struct efx_nic *efx, efx_oword_t *value, - unsigned int reg) -{ - unsigned long flags; - - spin_lock_irqsave(&efx->biu_lock, flags); - value->u32[0] = _falcon_readl(efx, reg + 0); - rmb(); - value->u32[1] = _falcon_readl(efx, reg + 4); - value->u32[2] = _falcon_readl(efx, reg + 8); - value->u32[3] = _falcon_readl(efx, reg + 12); - spin_unlock_irqrestore(&efx->biu_lock, flags); - - EFX_REGDUMP(efx, "read from register %x, got " EFX_OWORD_FMT "\n", reg, - EFX_OWORD_VAL(*value)); -} - -/* This reads an 8-byte Falcon SRAM entry in one go. */ -static inline void falcon_read_sram(struct efx_nic *efx, efx_qword_t *value, - unsigned int index) -{ - unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value)); - unsigned long flags; - - spin_lock_irqsave(&efx->biu_lock, flags); -#ifdef FALCON_USE_QWORD_IO - value->u64[0] = _falcon_readq(efx, reg + 0); -#else - value->u32[0] = _falcon_readl(efx, reg + 0); - rmb(); - value->u32[1] = _falcon_readl(efx, reg + 4); -#endif - spin_unlock_irqrestore(&efx->biu_lock, flags); - - EFX_REGDUMP(efx, "read from SRAM register %x, got "EFX_QWORD_FMT"\n", - reg, EFX_QWORD_VAL(*value)); -} - -/* Read dword from Falcon register that allows partial writes (sic) */ -static inline void falcon_readl(struct efx_nic *efx, efx_dword_t *value, - unsigned int reg) -{ - value->u32[0] = _falcon_readl(efx, reg); - EFX_REGDUMP(efx, "read from register %x, got "EFX_DWORD_FMT"\n", - reg, EFX_DWORD_VAL(*value)); -} - -/* Write to a register forming part of a table */ -static inline void falcon_write_table(struct efx_nic *efx, efx_oword_t *value, - unsigned int reg, unsigned int index) -{ - falcon_write(efx, value, reg + index * sizeof(efx_oword_t)); -} - -/* Read to a register forming part of a table */ -static inline void falcon_read_table(struct efx_nic *efx, efx_oword_t *value, - unsigned int reg, unsigned int index) -{ - falcon_read(efx, value, reg + index * sizeof(efx_oword_t)); -} - -/* Write to a dword register forming part of a table */ -static inline void falcon_writel_table(struct efx_nic *efx, efx_dword_t *value, - unsigned int reg, unsigned int index) -{ - falcon_writel(efx, value, reg + index * sizeof(efx_oword_t)); -} - -/* Page-mapped register block size */ -#define FALCON_PAGE_BLOCK_SIZE 0x2000 - -/* Calculate offset to page-mapped register block */ -#define FALCON_PAGED_REG(page, reg) \ - ((page) * FALCON_PAGE_BLOCK_SIZE + (reg)) - -/* As for falcon_write(), but for a page-mapped register. */ -static inline void falcon_write_page(struct efx_nic *efx, efx_oword_t *value, - unsigned int reg, unsigned int page) -{ - falcon_write(efx, value, FALCON_PAGED_REG(page, reg)); -} - -/* As for falcon_writel(), but for a page-mapped register. */ -static inline void falcon_writel_page(struct efx_nic *efx, efx_dword_t *value, - unsigned int reg, unsigned int page) -{ - falcon_writel(efx, value, FALCON_PAGED_REG(page, reg)); -} - -/* Write dword to Falcon page-mapped register with an extra lock. - * - * As for falcon_writel_page(), but for a register that suffers from - * SFC bug 3181. If writing to page 0, take out a lock so the BIU - * collector cannot be confused. - */ -static inline void falcon_writel_page_locked(struct efx_nic *efx, - efx_dword_t *value, - unsigned int reg, - unsigned int page) -{ - unsigned long flags = 0; - - if (page == 0) - spin_lock_irqsave(&efx->biu_lock, flags); - falcon_writel(efx, value, FALCON_PAGED_REG(page, reg)); - if (page == 0) - spin_unlock_irqrestore(&efx->biu_lock, flags); -} - -#endif /* EFX_FALCON_IO_H */ diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c index bec52ca37eee..3da933f8f079 100644 --- a/drivers/net/sfc/falcon_xmac.c +++ b/drivers/net/sfc/falcon_xmac.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -11,13 +11,12 @@ #include <linux/delay.h> #include "net_driver.h" #include "efx.h" -#include "falcon.h" -#include "falcon_hwdefs.h" -#include "falcon_io.h" +#include "nic.h" +#include "regs.h" +#include "io.h" #include "mac.h" #include "mdio_10g.h" #include "phy.h" -#include "boards.h" #include "workarounds.h" /************************************************************************** @@ -36,43 +35,47 @@ static void falcon_setup_xaui(struct efx_nic *efx) if (efx->phy_type == PHY_TYPE_NONE) return; - falcon_read(efx, &sdctl, XX_SD_CTL_REG); - EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT); - EFX_SET_OWORD_FIELD(sdctl, XX_LODRVD, XX_SD_CTL_DRV_DEFAULT); - EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT); - EFX_SET_OWORD_FIELD(sdctl, XX_LODRVC, XX_SD_CTL_DRV_DEFAULT); - EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT); - EFX_SET_OWORD_FIELD(sdctl, XX_LODRVB, XX_SD_CTL_DRV_DEFAULT); - EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT); - EFX_SET_OWORD_FIELD(sdctl, XX_LODRVA, XX_SD_CTL_DRV_DEFAULT); - falcon_write(efx, &sdctl, XX_SD_CTL_REG); + efx_reado(efx, &sdctl, FR_AB_XX_SD_CTL); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVD, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVD, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVC, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVC, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVB, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVB, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVA, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVA, FFE_AB_XX_SD_CTL_DRV_DEF); + efx_writeo(efx, &sdctl, FR_AB_XX_SD_CTL); EFX_POPULATE_OWORD_8(txdrv, - XX_DEQD, XX_TXDRV_DEQ_DEFAULT, - XX_DEQC, XX_TXDRV_DEQ_DEFAULT, - XX_DEQB, XX_TXDRV_DEQ_DEFAULT, - XX_DEQA, XX_TXDRV_DEQ_DEFAULT, - XX_DTXD, XX_TXDRV_DTX_DEFAULT, - XX_DTXC, XX_TXDRV_DTX_DEFAULT, - XX_DTXB, XX_TXDRV_DTX_DEFAULT, - XX_DTXA, XX_TXDRV_DTX_DEFAULT); - falcon_write(efx, &txdrv, XX_TXDRV_CTL_REG); + FRF_AB_XX_DEQD, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQC, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQB, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQA, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DTXD, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXC, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXB, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXA, FFE_AB_XX_TXDRV_DTX_DEF); + efx_writeo(efx, &txdrv, FR_AB_XX_TXDRV_CTL); } int falcon_reset_xaui(struct efx_nic *efx) { + struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t reg; int count; + /* Don't fetch MAC statistics over an XMAC reset */ + WARN_ON(nic_data->stats_disable_count == 0); + /* Start reset sequence */ - EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1); - falcon_write(efx, ®, XX_PWR_RST_REG); + EFX_POPULATE_OWORD_1(reg, FRF_AB_XX_RST_XX_EN, 1); + efx_writeo(efx, ®, FR_AB_XX_PWR_RST); /* Wait up to 10 ms for completion, then reinitialise */ for (count = 0; count < 1000; count++) { - falcon_read(efx, ®, XX_PWR_RST_REG); - if (EFX_OWORD_FIELD(reg, XX_RST_XX_EN) == 0 && - EFX_OWORD_FIELD(reg, XX_SD_RST_ACT) == 0) { + efx_reado(efx, ®, FR_AB_XX_PWR_RST); + if (EFX_OWORD_FIELD(reg, FRF_AB_XX_RST_XX_EN) == 0 && + EFX_OWORD_FIELD(reg, FRF_AB_XX_SD_RST_ACT) == 0) { falcon_setup_xaui(efx); return 0; } @@ -86,30 +89,30 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) { efx_oword_t reg; - if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx)) + if ((efx_nic_rev(efx) != EFX_REV_FALCON_B0) || LOOPBACK_INTERNAL(efx)) return; /* We expect xgmii faults if the wireside link is up */ - if (!EFX_WORKAROUND_5147(efx) || !efx->link_up) + if (!EFX_WORKAROUND_5147(efx) || !efx->link_state.up) return; /* We can only use this interrupt to signal the negative edge of * xaui_align [we have to poll the positive edge]. */ - if (!efx->mac_up) + if (efx->xmac_poll_required) return; /* Flush the ISR */ if (enable) - falcon_read(efx, ®, XM_MGT_INT_REG_B0); + efx_reado(efx, ®, FR_AB_XM_MGT_INT_MSK); EFX_POPULATE_OWORD_2(reg, - XM_MSK_RMTFLT, !enable, - XM_MSK_LCLFLT, !enable); - falcon_write(efx, ®, XM_MGT_INT_MSK_REG_B0); + FRF_AB_XM_MSK_RMTFLT, !enable, + FRF_AB_XM_MSK_LCLFLT, !enable); + efx_writeo(efx, ®, FR_AB_XM_MGT_INT_MASK); } /* Get status of XAUI link */ -bool falcon_xaui_link_ok(struct efx_nic *efx) +static bool falcon_xaui_link_ok(struct efx_nic *efx) { efx_oword_t reg; bool align_done, link_ok = false; @@ -119,84 +122,79 @@ bool falcon_xaui_link_ok(struct efx_nic *efx) return true; /* Read link status */ - falcon_read(efx, ®, XX_CORE_STAT_REG); + efx_reado(efx, ®, FR_AB_XX_CORE_STAT); - align_done = EFX_OWORD_FIELD(reg, XX_ALIGN_DONE); - sync_status = EFX_OWORD_FIELD(reg, XX_SYNC_STAT); - if (align_done && (sync_status == XX_SYNC_STAT_DECODE_SYNCED)) + align_done = EFX_OWORD_FIELD(reg, FRF_AB_XX_ALIGN_DONE); + sync_status = EFX_OWORD_FIELD(reg, FRF_AB_XX_SYNC_STAT); + if (align_done && (sync_status == FFE_AB_XX_STAT_ALL_LANES)) link_ok = true; /* Clear link status ready for next read */ - EFX_SET_OWORD_FIELD(reg, XX_COMMA_DET, XX_COMMA_DET_RESET); - EFX_SET_OWORD_FIELD(reg, XX_CHARERR, XX_CHARERR_RESET); - EFX_SET_OWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET); - falcon_write(efx, ®, XX_CORE_STAT_REG); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_COMMA_DET, FFE_AB_XX_STAT_ALL_LANES); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_CHAR_ERR, FFE_AB_XX_STAT_ALL_LANES); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_DISPERR, FFE_AB_XX_STAT_ALL_LANES); + efx_writeo(efx, ®, FR_AB_XX_CORE_STAT); /* If the link is up, then check the phy side of the xaui link */ - if (efx->link_up && link_ok) - if (efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS)) + if (efx->link_state.up && link_ok) + if (efx->mdio.mmds & (1 << MDIO_MMD_PHYXS)) link_ok = efx_mdio_phyxgxs_lane_sync(efx); return link_ok; } -static void falcon_reconfigure_xmac_core(struct efx_nic *efx) +void falcon_reconfigure_xmac_core(struct efx_nic *efx) { unsigned int max_frame_len; efx_oword_t reg; - bool rx_fc = !!(efx->link_fc & EFX_FC_RX); + bool rx_fc = !!(efx->link_state.fc & EFX_FC_RX); + bool tx_fc = !!(efx->link_state.fc & EFX_FC_TX); /* Configure MAC - cut-thru mode is hard wired on */ - EFX_POPULATE_DWORD_3(reg, - XM_RX_JUMBO_MODE, 1, - XM_TX_STAT_EN, 1, - XM_RX_STAT_EN, 1); - falcon_write(efx, ®, XM_GLB_CFG_REG); + EFX_POPULATE_OWORD_3(reg, + FRF_AB_XM_RX_JUMBO_MODE, 1, + FRF_AB_XM_TX_STAT_EN, 1, + FRF_AB_XM_RX_STAT_EN, 1); + efx_writeo(efx, ®, FR_AB_XM_GLB_CFG); /* Configure TX */ - EFX_POPULATE_DWORD_6(reg, - XM_TXEN, 1, - XM_TX_PRMBL, 1, - XM_AUTO_PAD, 1, - XM_TXCRC, 1, - XM_FCNTL, 1, - XM_IPG, 0x3); - falcon_write(efx, ®, XM_TX_CFG_REG); + EFX_POPULATE_OWORD_6(reg, + FRF_AB_XM_TXEN, 1, + FRF_AB_XM_TX_PRMBL, 1, + FRF_AB_XM_AUTO_PAD, 1, + FRF_AB_XM_TXCRC, 1, + FRF_AB_XM_FCNTL, tx_fc, + FRF_AB_XM_IPG, 0x3); + efx_writeo(efx, ®, FR_AB_XM_TX_CFG); /* Configure RX */ - EFX_POPULATE_DWORD_5(reg, - XM_RXEN, 1, - XM_AUTO_DEPAD, 0, - XM_ACPT_ALL_MCAST, 1, - XM_ACPT_ALL_UCAST, efx->promiscuous, - XM_PASS_CRC_ERR, 1); - falcon_write(efx, ®, XM_RX_CFG_REG); + EFX_POPULATE_OWORD_5(reg, + FRF_AB_XM_RXEN, 1, + FRF_AB_XM_AUTO_DEPAD, 0, + FRF_AB_XM_ACPT_ALL_MCAST, 1, + FRF_AB_XM_ACPT_ALL_UCAST, efx->promiscuous, + FRF_AB_XM_PASS_CRC_ERR, 1); + efx_writeo(efx, ®, FR_AB_XM_RX_CFG); /* Set frame length */ max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); - EFX_POPULATE_DWORD_1(reg, XM_MAX_RX_FRM_SIZE, max_frame_len); - falcon_write(efx, ®, XM_RX_PARAM_REG); - EFX_POPULATE_DWORD_2(reg, - XM_MAX_TX_FRM_SIZE, max_frame_len, - XM_TX_JUMBO_MODE, 1); - falcon_write(efx, ®, XM_TX_PARAM_REG); - - EFX_POPULATE_DWORD_2(reg, - XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ - XM_DIS_FCNTL, !rx_fc); - falcon_write(efx, ®, XM_FC_REG); + EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_MAX_RX_FRM_SIZE, max_frame_len); + efx_writeo(efx, ®, FR_AB_XM_RX_PARAM); + EFX_POPULATE_OWORD_2(reg, + FRF_AB_XM_MAX_TX_FRM_SIZE, max_frame_len, + FRF_AB_XM_TX_JUMBO_MODE, 1); + efx_writeo(efx, ®, FR_AB_XM_TX_PARAM); + + EFX_POPULATE_OWORD_2(reg, + FRF_AB_XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ + FRF_AB_XM_DIS_FCNTL, !rx_fc); + efx_writeo(efx, ®, FR_AB_XM_FC); /* Set MAC address */ - EFX_POPULATE_DWORD_4(reg, - XM_ADR_0, efx->net_dev->dev_addr[0], - XM_ADR_1, efx->net_dev->dev_addr[1], - XM_ADR_2, efx->net_dev->dev_addr[2], - XM_ADR_3, efx->net_dev->dev_addr[3]); - falcon_write(efx, ®, XM_ADR_LO_REG); - EFX_POPULATE_DWORD_2(reg, - XM_ADR_4, efx->net_dev->dev_addr[4], - XM_ADR_5, efx->net_dev->dev_addr[5]); - falcon_write(efx, ®, XM_ADR_HI_REG); + memcpy(®, &efx->net_dev->dev_addr[0], 4); + efx_writeo(efx, ®, FR_AB_XM_ADR_LO); + memcpy(®, &efx->net_dev->dev_addr[4], 2); + efx_writeo(efx, ®, FR_AB_XM_ADR_HI); } static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) @@ -212,12 +210,13 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) bool old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback; bool reset_xgxs; - falcon_read(efx, ®, XX_CORE_STAT_REG); - old_xgxs_loopback = EFX_OWORD_FIELD(reg, XX_XGXS_LB_EN); - old_xgmii_loopback = EFX_OWORD_FIELD(reg, XX_XGMII_LB_EN); + efx_reado(efx, ®, FR_AB_XX_CORE_STAT); + old_xgxs_loopback = EFX_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN); + old_xgmii_loopback = + EFX_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN); - falcon_read(efx, ®, XX_SD_CTL_REG); - old_xaui_loopback = EFX_OWORD_FIELD(reg, XX_LPBKA); + efx_reado(efx, ®, FR_AB_XX_SD_CTL); + old_xaui_loopback = EFX_OWORD_FIELD(reg, FRF_AB_XX_LPBKA); /* The PHY driver may have turned XAUI off */ reset_xgxs = ((xgxs_loopback != old_xgxs_loopback) || @@ -228,45 +227,55 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) falcon_reset_xaui(efx); } - falcon_read(efx, ®, XX_CORE_STAT_REG); - EFX_SET_OWORD_FIELD(reg, XX_FORCE_SIG, + efx_reado(efx, ®, FR_AB_XX_CORE_STAT); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_FORCE_SIG, (xgxs_loopback || xaui_loopback) ? - XX_FORCE_SIG_DECODE_FORCED : 0); - EFX_SET_OWORD_FIELD(reg, XX_XGXS_LB_EN, xgxs_loopback); - EFX_SET_OWORD_FIELD(reg, XX_XGMII_LB_EN, xgmii_loopback); - falcon_write(efx, ®, XX_CORE_STAT_REG); - - falcon_read(efx, ®, XX_SD_CTL_REG); - EFX_SET_OWORD_FIELD(reg, XX_LPBKD, xaui_loopback); - EFX_SET_OWORD_FIELD(reg, XX_LPBKC, xaui_loopback); - EFX_SET_OWORD_FIELD(reg, XX_LPBKB, xaui_loopback); - EFX_SET_OWORD_FIELD(reg, XX_LPBKA, xaui_loopback); - falcon_write(efx, ®, XX_SD_CTL_REG); + FFE_AB_XX_FORCE_SIG_ALL_LANES : 0); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN, xgxs_loopback); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN, xgmii_loopback); + efx_writeo(efx, ®, FR_AB_XX_CORE_STAT); + + efx_reado(efx, ®, FR_AB_XX_SD_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKD, xaui_loopback); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKC, xaui_loopback); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKB, xaui_loopback); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKA, xaui_loopback); + efx_writeo(efx, ®, FR_AB_XX_SD_CTL); } -/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails - * to come back up. Bash it until it comes back up */ -static void falcon_check_xaui_link_up(struct efx_nic *efx, int tries) +/* Try to bring up the Falcon side of the Falcon-Phy XAUI link */ +static bool falcon_check_xaui_link_up(struct efx_nic *efx, int tries) { - efx->mac_up = falcon_xaui_link_ok(efx); + bool mac_up = falcon_xaui_link_ok(efx); - if ((efx->loopback_mode == LOOPBACK_NETWORK) || + if (LOOPBACK_MASK(efx) & LOOPBACKS_EXTERNAL(efx) & LOOPBACKS_WS || efx_phy_mode_disabled(efx->phy_mode)) /* XAUI link is expected to be down */ - return; + return mac_up; - while (!efx->mac_up && tries) { + falcon_stop_nic_stats(efx); + + while (!mac_up && tries) { EFX_LOG(efx, "bashing xaui\n"); falcon_reset_xaui(efx); udelay(200); - efx->mac_up = falcon_xaui_link_ok(efx); + mac_up = falcon_xaui_link_ok(efx); --tries; } + + falcon_start_nic_stats(efx); + + return mac_up; } -static void falcon_reconfigure_xmac(struct efx_nic *efx) +static bool falcon_xmac_check_fault(struct efx_nic *efx) +{ + return !falcon_check_xaui_link_up(efx, 5); +} + +static int falcon_reconfigure_xmac(struct efx_nic *efx) { falcon_mask_status_intr(efx, false); @@ -275,18 +284,15 @@ static void falcon_reconfigure_xmac(struct efx_nic *efx) falcon_reconfigure_mac_wrapper(efx); - falcon_check_xaui_link_up(efx, 5); + efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 5); falcon_mask_status_intr(efx, true); + + return 0; } static void falcon_update_stats_xmac(struct efx_nic *efx) { struct efx_mac_stats *mac_stats = &efx->mac_stats; - int rc; - - rc = falcon_dma_stats(efx, XgDmaDone_offset); - if (rc) - return; /* Update MAC stats from DMAed values */ FALCON_STAT(efx, XgRxOctets, rx_bytes); @@ -344,35 +350,19 @@ static void falcon_update_stats_xmac(struct efx_nic *efx) mac_stats->rx_control * 64); } -static void falcon_xmac_irq(struct efx_nic *efx) -{ - /* The XGMII link has a transient fault, which indicates either: - * - there's a transient xgmii fault - * - falcon's end of the xaui link may need a kick - * - the wire-side link may have gone down, but the lasi/poll() - * hasn't noticed yet. - * - * We only want to even bother polling XAUI if we're confident it's - * not (1) or (3). In both cases, the only reliable way to spot this - * is to wait a bit. We do this here by forcing the mac link state - * to down, and waiting for the mac poll to come round and check - */ - efx->mac_up = false; -} - -static void falcon_poll_xmac(struct efx_nic *efx) +void falcon_poll_xmac(struct efx_nic *efx) { - if (!EFX_WORKAROUND_5147(efx) || !efx->link_up || efx->mac_up) + if (!EFX_WORKAROUND_5147(efx) || !efx->link_state.up || + !efx->xmac_poll_required) return; falcon_mask_status_intr(efx, false); - falcon_check_xaui_link_up(efx, 1); + efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 1); falcon_mask_status_intr(efx, true); } struct efx_mac_operations falcon_xmac_operations = { .reconfigure = falcon_reconfigure_xmac, .update_stats = falcon_update_stats_xmac, - .irq = falcon_xmac_irq, - .poll = falcon_poll_xmac, + .check_fault = falcon_xmac_check_fault, }; diff --git a/drivers/net/sfc/gmii.h b/drivers/net/sfc/gmii.h deleted file mode 100644 index dfccaa7b573e..000000000000 --- a/drivers/net/sfc/gmii.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#ifndef EFX_GMII_H -#define EFX_GMII_H - -/* - * GMII interface - */ - -#include <linux/mii.h> - -/* GMII registers, excluding registers already defined as MII - * registers in mii.h - */ -#define GMII_IER 0x12 /* Interrupt enable register */ -#define GMII_ISR 0x13 /* Interrupt status register */ - -/* Interrupt enable register */ -#define IER_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */ -#define IER_SPEED_CHG 0x4000 /* Bit 14 - speed changed */ -#define IER_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */ -#define IER_PAGE_RCVD 0x1000 /* Bit 12 - page received */ -#define IER_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */ -#define IER_LINK_CHG 0x0400 /* Bit 10 - link status changed */ -#define IER_SYM_ERR 0x0200 /* Bit 9 - symbol error */ -#define IER_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */ -#define IER_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */ -#define IER_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */ -#define IER_DOWNSHIFT 0x0020 /* Bit 5 - downshift */ -#define IER_ENERGY 0x0010 /* Bit 4 - energy detect */ -#define IER_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */ -#define IER_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */ -#define IER_JABBER 0x0001 /* Bit 0 - jabber */ - -/* Interrupt status register */ -#define ISR_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */ -#define ISR_SPEED_CHG 0x4000 /* Bit 14 - speed changed */ -#define ISR_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */ -#define ISR_PAGE_RCVD 0x1000 /* Bit 12 - page received */ -#define ISR_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */ -#define ISR_LINK_CHG 0x0400 /* Bit 10 - link status changed */ -#define ISR_SYM_ERR 0x0200 /* Bit 9 - symbol error */ -#define ISR_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */ -#define ISR_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */ -#define ISR_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */ -#define ISR_DOWNSHIFT 0x0020 /* Bit 5 - downshift */ -#define ISR_ENERGY 0x0010 /* Bit 4 - energy detect */ -#define ISR_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */ -#define ISR_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */ -#define ISR_JABBER 0x0001 /* Bit 0 - jabber */ - -#endif /* EFX_GMII_H */ diff --git a/drivers/net/sfc/io.h b/drivers/net/sfc/io.h new file mode 100644 index 000000000000..b89177c27f4a --- /dev/null +++ b/drivers/net/sfc/io.h @@ -0,0 +1,256 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#ifndef EFX_IO_H +#define EFX_IO_H + +#include <linux/io.h> +#include <linux/spinlock.h> + +/************************************************************************** + * + * NIC register I/O + * + ************************************************************************** + * + * Notes on locking strategy: + * + * Most NIC registers require 16-byte (or 8-byte, for SRAM) atomic writes + * which necessitates locking. + * Under normal operation few writes to NIC registers are made and these + * registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and TX_DESC_UPD_REG) are special + * cased to allow 4-byte (hence lockless) accesses. + * + * It *is* safe to write to these 4-byte registers in the middle of an + * access to an 8-byte or 16-byte register. We therefore use a + * spinlock to protect accesses to the larger registers, but no locks + * for the 4-byte registers. + * + * A write barrier is needed to ensure that DW3 is written after DW0/1/2 + * due to the way the 16byte registers are "collected" in the BIU. + * + * We also lock when carrying out reads, to ensure consistency of the + * data (made possible since the BIU reads all 128 bits into a cache). + * Reads are very rare, so this isn't a significant performance + * impact. (Most data transferred from NIC to host is DMAed directly + * into host memory). + * + * I/O BAR access uses locks for both reads and writes (but is only provided + * for testing purposes). + */ + +#if BITS_PER_LONG == 64 +#define EFX_USE_QWORD_IO 1 +#endif + +#ifdef EFX_USE_QWORD_IO +static inline void _efx_writeq(struct efx_nic *efx, __le64 value, + unsigned int reg) +{ + __raw_writeq((__force u64)value, efx->membase + reg); +} +static inline __le64 _efx_readq(struct efx_nic *efx, unsigned int reg) +{ + return (__force __le64)__raw_readq(efx->membase + reg); +} +#endif + +static inline void _efx_writed(struct efx_nic *efx, __le32 value, + unsigned int reg) +{ + __raw_writel((__force u32)value, efx->membase + reg); +} +static inline __le32 _efx_readd(struct efx_nic *efx, unsigned int reg) +{ + return (__force __le32)__raw_readl(efx->membase + reg); +} + +/* Writes to a normal 16-byte Efx register, locking as appropriate. */ +static inline void efx_writeo(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg) +{ + unsigned long flags __attribute__ ((unused)); + + EFX_REGDUMP(efx, "writing register %x with " EFX_OWORD_FMT "\n", reg, + EFX_OWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef EFX_USE_QWORD_IO + _efx_writeq(efx, value->u64[0], reg + 0); + wmb(); + _efx_writeq(efx, value->u64[1], reg + 8); +#else + _efx_writed(efx, value->u32[0], reg + 0); + _efx_writed(efx, value->u32[1], reg + 4); + _efx_writed(efx, value->u32[2], reg + 8); + wmb(); + _efx_writed(efx, value->u32[3], reg + 12); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Write an 8-byte NIC SRAM entry through the supplied mapping, + * locking as appropriate. */ +static inline void efx_sram_writeq(struct efx_nic *efx, void __iomem *membase, + efx_qword_t *value, unsigned int index) +{ + unsigned int addr = index * sizeof(*value); + unsigned long flags __attribute__ ((unused)); + + EFX_REGDUMP(efx, "writing SRAM address %x with " EFX_QWORD_FMT "\n", + addr, EFX_QWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef EFX_USE_QWORD_IO + __raw_writeq((__force u64)value->u64[0], membase + addr); +#else + __raw_writel((__force u32)value->u32[0], membase + addr); + wmb(); + __raw_writel((__force u32)value->u32[1], membase + addr + 4); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Write dword to NIC register that allows partial writes + * + * Some registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and + * TX_DESC_UPD_REG) can be written to as a single dword. This allows + * for lockless writes. + */ +static inline void efx_writed(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg) +{ + EFX_REGDUMP(efx, "writing partial register %x with "EFX_DWORD_FMT"\n", + reg, EFX_DWORD_VAL(*value)); + + /* No lock required */ + _efx_writed(efx, value->u32[0], reg); +} + +/* Read from a NIC register + * + * This reads an entire 16-byte register in one go, locking as + * appropriate. It is essential to read the first dword first, as this + * prompts the NIC to load the current value into the shadow register. + */ +static inline void efx_reado(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg) +{ + unsigned long flags __attribute__ ((unused)); + + spin_lock_irqsave(&efx->biu_lock, flags); + value->u32[0] = _efx_readd(efx, reg + 0); + rmb(); + value->u32[1] = _efx_readd(efx, reg + 4); + value->u32[2] = _efx_readd(efx, reg + 8); + value->u32[3] = _efx_readd(efx, reg + 12); + spin_unlock_irqrestore(&efx->biu_lock, flags); + + EFX_REGDUMP(efx, "read from register %x, got " EFX_OWORD_FMT "\n", reg, + EFX_OWORD_VAL(*value)); +} + +/* Read an 8-byte SRAM entry through supplied mapping, + * locking as appropriate. */ +static inline void efx_sram_readq(struct efx_nic *efx, void __iomem *membase, + efx_qword_t *value, unsigned int index) +{ + unsigned int addr = index * sizeof(*value); + unsigned long flags __attribute__ ((unused)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef EFX_USE_QWORD_IO + value->u64[0] = (__force __le64)__raw_readq(membase + addr); +#else + value->u32[0] = (__force __le32)__raw_readl(membase + addr); + rmb(); + value->u32[1] = (__force __le32)__raw_readl(membase + addr + 4); +#endif + spin_unlock_irqrestore(&efx->biu_lock, flags); + + EFX_REGDUMP(efx, "read from SRAM address %x, got "EFX_QWORD_FMT"\n", + addr, EFX_QWORD_VAL(*value)); +} + +/* Read dword from register that allows partial writes (sic) */ +static inline void efx_readd(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg) +{ + value->u32[0] = _efx_readd(efx, reg); + EFX_REGDUMP(efx, "read from register %x, got "EFX_DWORD_FMT"\n", + reg, EFX_DWORD_VAL(*value)); +} + +/* Write to a register forming part of a table */ +static inline void efx_writeo_table(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int index) +{ + efx_writeo(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Read to a register forming part of a table */ +static inline void efx_reado_table(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int index) +{ + efx_reado(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Write to a dword register forming part of a table */ +static inline void efx_writed_table(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg, unsigned int index) +{ + efx_writed(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Page-mapped register block size */ +#define EFX_PAGE_BLOCK_SIZE 0x2000 + +/* Calculate offset to page-mapped register block */ +#define EFX_PAGED_REG(page, reg) \ + ((page) * EFX_PAGE_BLOCK_SIZE + (reg)) + +/* As for efx_writeo(), but for a page-mapped register. */ +static inline void efx_writeo_page(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int page) +{ + efx_writeo(efx, value, EFX_PAGED_REG(page, reg)); +} + +/* As for efx_writed(), but for a page-mapped register. */ +static inline void efx_writed_page(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg, unsigned int page) +{ + efx_writed(efx, value, EFX_PAGED_REG(page, reg)); +} + +/* Write dword to page-mapped register with an extra lock. + * + * As for efx_writed_page(), but for a register that suffers from + * SFC bug 3181. Take out a lock so the BIU collector cannot be + * confused. */ +static inline void efx_writed_page_locked(struct efx_nic *efx, + efx_dword_t *value, + unsigned int reg, + unsigned int page) +{ + unsigned long flags __attribute__ ((unused)); + + if (page == 0) { + spin_lock_irqsave(&efx->biu_lock, flags); + efx_writed(efx, value, EFX_PAGED_REG(page, reg)); + spin_unlock_irqrestore(&efx->biu_lock, flags); + } else { + efx_writed(efx, value, EFX_PAGED_REG(page, reg)); + } +} + +#endif /* EFX_IO_H */ diff --git a/drivers/net/sfc/mac.h b/drivers/net/sfc/mac.h index 4e7074278fe1..f1aa5f374890 100644 --- a/drivers/net/sfc/mac.h +++ b/drivers/net/sfc/mac.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -15,5 +15,9 @@ extern struct efx_mac_operations falcon_gmac_operations; extern struct efx_mac_operations falcon_xmac_operations; +extern struct efx_mac_operations efx_mcdi_mac_operations; +extern void falcon_reconfigure_xmac_core(struct efx_nic *efx); +extern int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr, + u32 dma_len, int enable, int clear); #endif diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c new file mode 100644 index 000000000000..683353b904c7 --- /dev/null +++ b/drivers/net/sfc/mcdi.c @@ -0,0 +1,1112 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2008-2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#include <linux/delay.h> +#include "net_driver.h" +#include "nic.h" +#include "io.h" +#include "regs.h" +#include "mcdi_pcol.h" +#include "phy.h" + +/************************************************************************** + * + * Management-Controller-to-Driver Interface + * + ************************************************************************** + */ + +/* Software-defined structure to the shared-memory */ +#define CMD_NOTIFY_PORT0 0 +#define CMD_NOTIFY_PORT1 4 +#define CMD_PDU_PORT0 0x008 +#define CMD_PDU_PORT1 0x108 +#define REBOOT_FLAG_PORT0 0x3f8 +#define REBOOT_FLAG_PORT1 0x3fc + +#define MCDI_RPC_TIMEOUT 10 /*seconds */ + +#define MCDI_PDU(efx) \ + (efx_port_num(efx) ? CMD_PDU_PORT1 : CMD_PDU_PORT0) +#define MCDI_DOORBELL(efx) \ + (efx_port_num(efx) ? CMD_NOTIFY_PORT1 : CMD_NOTIFY_PORT0) +#define MCDI_REBOOT_FLAG(efx) \ + (efx_port_num(efx) ? REBOOT_FLAG_PORT1 : REBOOT_FLAG_PORT0) + +#define SEQ_MASK \ + EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ)) + +static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data; + EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0); + nic_data = efx->nic_data; + return &nic_data->mcdi; +} + +void efx_mcdi_init(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + init_waitqueue_head(&mcdi->wq); + spin_lock_init(&mcdi->iface_lock); + atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT); + mcdi->mode = MCDI_MODE_POLL; + + (void) efx_mcdi_poll_reboot(efx); +} + +static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd, + const u8 *inbuf, size_t inlen) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx); + unsigned int i; + efx_dword_t hdr; + u32 xflags, seqno; + + BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); + BUG_ON(inlen & 3 || inlen >= 0x100); + + seqno = mcdi->seqno & SEQ_MASK; + xflags = 0; + if (mcdi->mode == MCDI_MODE_EVENTS) + xflags |= MCDI_HEADER_XFLAGS_EVREQ; + + EFX_POPULATE_DWORD_6(hdr, + MCDI_HEADER_RESPONSE, 0, + MCDI_HEADER_RESYNC, 1, + MCDI_HEADER_CODE, cmd, + MCDI_HEADER_DATALEN, inlen, + MCDI_HEADER_SEQ, seqno, + MCDI_HEADER_XFLAGS, xflags); + + efx_writed(efx, &hdr, pdu); + + for (i = 0; i < inlen; i += 4) + _efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i); + + /* Ensure the payload is written out before the header */ + wmb(); + + /* ring the doorbell with a distinctive value */ + _efx_writed(efx, (__force __le32) 0x45789abc, doorbell); +} + +static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + int i; + + BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); + BUG_ON(outlen & 3 || outlen >= 0x100); + + for (i = 0; i < outlen; i += 4) + *((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i); +} + +static int efx_mcdi_poll(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned int time, finish; + unsigned int respseq, respcmd, error; + unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + unsigned int rc, spins; + efx_dword_t reg; + + /* Check for a reboot atomically with respect to efx_mcdi_copyout() */ + rc = efx_mcdi_poll_reboot(efx); + if (rc) + goto out; + + /* Poll for completion. Poll quickly (once a us) for the 1st jiffy, + * because generally mcdi responses are fast. After that, back off + * and poll once a jiffy (approximately) + */ + spins = TICK_USEC; + finish = get_seconds() + MCDI_RPC_TIMEOUT; + + while (1) { + if (spins != 0) { + --spins; + udelay(1); + } else + schedule(); + + time = get_seconds(); + + rmb(); + efx_readd(efx, ®, pdu); + + /* All 1's indicates that shared memory is in reset (and is + * not a valid header). Wait for it to come out reset before + * completing the command */ + if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) != 0xffffffff && + EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE)) + break; + + if (time >= finish) + return -ETIMEDOUT; + } + + mcdi->resplen = EFX_DWORD_FIELD(reg, MCDI_HEADER_DATALEN); + respseq = EFX_DWORD_FIELD(reg, MCDI_HEADER_SEQ); + respcmd = EFX_DWORD_FIELD(reg, MCDI_HEADER_CODE); + error = EFX_DWORD_FIELD(reg, MCDI_HEADER_ERROR); + + if (error && mcdi->resplen == 0) { + EFX_ERR(efx, "MC rebooted\n"); + rc = EIO; + } else if ((respseq ^ mcdi->seqno) & SEQ_MASK) { + EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx seq 0x%x\n", + respseq, mcdi->seqno); + rc = EIO; + } else if (error) { + efx_readd(efx, ®, pdu + 4); + switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) { +#define TRANSLATE_ERROR(name) \ + case MC_CMD_ERR_ ## name: \ + rc = name; \ + break + TRANSLATE_ERROR(ENOENT); + TRANSLATE_ERROR(EINTR); + TRANSLATE_ERROR(EACCES); + TRANSLATE_ERROR(EBUSY); + TRANSLATE_ERROR(EINVAL); + TRANSLATE_ERROR(EDEADLK); + TRANSLATE_ERROR(ENOSYS); + TRANSLATE_ERROR(ETIME); +#undef TRANSLATE_ERROR + default: + rc = EIO; + break; + } + } else + rc = 0; + +out: + mcdi->resprc = rc; + if (rc) + mcdi->resplen = 0; + + /* Return rc=0 like wait_event_timeout() */ + return 0; +} + +/* Test and clear MC-rebooted flag for this port/function */ +int efx_mcdi_poll_reboot(struct efx_nic *efx) +{ + unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_REBOOT_FLAG(efx); + efx_dword_t reg; + uint32_t value; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return false; + + efx_readd(efx, ®, addr); + value = EFX_DWORD_FIELD(reg, EFX_DWORD_0); + + if (value == 0) + return 0; + + EFX_ZERO_DWORD(reg); + efx_writed(efx, ®, addr); + + if (value == MC_STATUS_DWORD_ASSERT) + return -EINTR; + else + return -EIO; +} + +static void efx_mcdi_acquire(struct efx_mcdi_iface *mcdi) +{ + /* Wait until the interface becomes QUIESCENT and we win the race + * to mark it RUNNING. */ + wait_event(mcdi->wq, + atomic_cmpxchg(&mcdi->state, + MCDI_STATE_QUIESCENT, + MCDI_STATE_RUNNING) + == MCDI_STATE_QUIESCENT); +} + +static int efx_mcdi_await_completion(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + if (wait_event_timeout( + mcdi->wq, + atomic_read(&mcdi->state) == MCDI_STATE_COMPLETED, + msecs_to_jiffies(MCDI_RPC_TIMEOUT * 1000)) == 0) + return -ETIMEDOUT; + + /* Check if efx_mcdi_set_mode() switched us back to polled completions. + * In which case, poll for completions directly. If efx_mcdi_ev_cpl() + * completed the request first, then we'll just end up completing the + * request again, which is safe. + * + * We need an smp_rmb() to synchronise with efx_mcdi_mode_poll(), which + * wait_event_timeout() implicitly provides. + */ + if (mcdi->mode == MCDI_MODE_POLL) + return efx_mcdi_poll(efx); + + return 0; +} + +static bool efx_mcdi_complete(struct efx_mcdi_iface *mcdi) +{ + /* If the interface is RUNNING, then move to COMPLETED and wake any + * waiters. If the interface isn't in RUNNING then we've received a + * duplicate completion after we've already transitioned back to + * QUIESCENT. [A subsequent invocation would increment seqno, so would + * have failed the seqno check]. + */ + if (atomic_cmpxchg(&mcdi->state, + MCDI_STATE_RUNNING, + MCDI_STATE_COMPLETED) == MCDI_STATE_RUNNING) { + wake_up(&mcdi->wq); + return true; + } + + return false; +} + +static void efx_mcdi_release(struct efx_mcdi_iface *mcdi) +{ + atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT); + wake_up(&mcdi->wq); +} + +static void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno, + unsigned int datalen, unsigned int errno) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + bool wake = false; + + spin_lock(&mcdi->iface_lock); + + if ((seqno ^ mcdi->seqno) & SEQ_MASK) { + if (mcdi->credits) + /* The request has been cancelled */ + --mcdi->credits; + else + EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx " + "seq 0x%x\n", seqno, mcdi->seqno); + } else { + mcdi->resprc = errno; + mcdi->resplen = datalen; + + wake = true; + } + + spin_unlock(&mcdi->iface_lock); + + if (wake) + efx_mcdi_complete(mcdi); +} + +/* Issue the given command by writing the data into the shared memory PDU, + * ring the doorbell and wait for completion. Copyout the result. */ +int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, + const u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen, + size_t *outlen_actual) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + int rc; + BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0); + + efx_mcdi_acquire(mcdi); + + /* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */ + spin_lock_bh(&mcdi->iface_lock); + ++mcdi->seqno; + spin_unlock_bh(&mcdi->iface_lock); + + efx_mcdi_copyin(efx, cmd, inbuf, inlen); + + if (mcdi->mode == MCDI_MODE_POLL) + rc = efx_mcdi_poll(efx); + else + rc = efx_mcdi_await_completion(efx); + + if (rc != 0) { + /* Close the race with efx_mcdi_ev_cpl() executing just too late + * and completing a request we've just cancelled, by ensuring + * that the seqno check therein fails. + */ + spin_lock_bh(&mcdi->iface_lock); + ++mcdi->seqno; + ++mcdi->credits; + spin_unlock_bh(&mcdi->iface_lock); + + EFX_ERR(efx, "MC command 0x%x inlen %d mode %d timed out\n", + cmd, (int)inlen, mcdi->mode); + } else { + size_t resplen; + + /* At the very least we need a memory barrier here to ensure + * we pick up changes from efx_mcdi_ev_cpl(). Protect against + * a spurious efx_mcdi_ev_cpl() running concurrently by + * acquiring the iface_lock. */ + spin_lock_bh(&mcdi->iface_lock); + rc = -mcdi->resprc; + resplen = mcdi->resplen; + spin_unlock_bh(&mcdi->iface_lock); + + if (rc == 0) { + efx_mcdi_copyout(efx, outbuf, + min(outlen, mcdi->resplen + 3) & ~0x3); + if (outlen_actual != NULL) + *outlen_actual = resplen; + } else if (cmd == MC_CMD_REBOOT && rc == -EIO) + ; /* Don't reset if MC_CMD_REBOOT returns EIO */ + else if (rc == -EIO || rc == -EINTR) { + EFX_ERR(efx, "MC fatal error %d\n", -rc); + efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); + } else + EFX_ERR(efx, "MC command 0x%x inlen %d failed rc=%d\n", + cmd, (int)inlen, -rc); + } + + efx_mcdi_release(mcdi); + return rc; +} + +void efx_mcdi_mode_poll(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + if (mcdi->mode == MCDI_MODE_POLL) + return; + + /* We can switch from event completion to polled completion, because + * mcdi requests are always completed in shared memory. We do this by + * switching the mode to POLL'd then completing the request. + * efx_mcdi_await_completion() will then call efx_mcdi_poll(). + * + * We need an smp_wmb() to synchronise with efx_mcdi_await_completion(), + * which efx_mcdi_complete() provides for us. + */ + mcdi->mode = MCDI_MODE_POLL; + + efx_mcdi_complete(mcdi); +} + +void efx_mcdi_mode_event(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + + if (mcdi->mode == MCDI_MODE_EVENTS) + return; + + /* We can't switch from polled to event completion in the middle of a + * request, because the completion method is specified in the request. + * So acquire the interface to serialise the requestors. We don't need + * to acquire the iface_lock to change the mode here, but we do need a + * write memory barrier ensure that efx_mcdi_rpc() sees it, which + * efx_mcdi_acquire() provides. + */ + efx_mcdi_acquire(mcdi); + mcdi->mode = MCDI_MODE_EVENTS; + efx_mcdi_release(mcdi); +} + +static void efx_mcdi_ev_death(struct efx_nic *efx, int rc) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + /* If there is an outstanding MCDI request, it has been terminated + * either by a BADASSERT or REBOOT event. If the mcdi interface is + * in polled mode, then do nothing because the MC reboot handler will + * set the header correctly. However, if the mcdi interface is waiting + * for a CMDDONE event it won't receive it [and since all MCDI events + * are sent to the same queue, we can't be racing with + * efx_mcdi_ev_cpl()] + * + * There's a race here with efx_mcdi_rpc(), because we might receive + * a REBOOT event *before* the request has been copied out. In polled + * mode (during startup) this is irrelevent, because efx_mcdi_complete() + * is ignored. In event mode, this condition is just an edge-case of + * receiving a REBOOT event after posting the MCDI request. Did the mc + * reboot before or after the copyout? The best we can do always is + * just return failure. + */ + spin_lock(&mcdi->iface_lock); + if (efx_mcdi_complete(mcdi)) { + if (mcdi->mode == MCDI_MODE_EVENTS) { + mcdi->resprc = rc; + mcdi->resplen = 0; + } + } else + /* Nobody was waiting for an MCDI request, so trigger a reset */ + efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); + + spin_unlock(&mcdi->iface_lock); +} + +static unsigned int efx_mcdi_event_link_speed[] = { + [MCDI_EVENT_LINKCHANGE_SPEED_100M] = 100, + [MCDI_EVENT_LINKCHANGE_SPEED_1G] = 1000, + [MCDI_EVENT_LINKCHANGE_SPEED_10G] = 10000, +}; + + +static void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev) +{ + u32 flags, fcntl, speed, lpa; + + speed = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_SPEED); + EFX_BUG_ON_PARANOID(speed >= ARRAY_SIZE(efx_mcdi_event_link_speed)); + speed = efx_mcdi_event_link_speed[speed]; + + flags = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LINK_FLAGS); + fcntl = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_FCNTL); + lpa = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LP_CAP); + + /* efx->link_state is only modified by efx_mcdi_phy_get_link(), + * which is only run after flushing the event queues. Therefore, it + * is safe to modify the link state outside of the mac_lock here. + */ + efx_mcdi_phy_decode_link(efx, &efx->link_state, speed, flags, fcntl); + + efx_mcdi_phy_check_fcntl(efx, lpa); + + efx_link_status_changed(efx); +} + +static const char *sensor_names[] = { + [MC_CMD_SENSOR_CONTROLLER_TEMP] = "Controller temp. sensor", + [MC_CMD_SENSOR_PHY_COMMON_TEMP] = "PHY shared temp. sensor", + [MC_CMD_SENSOR_CONTROLLER_COOLING] = "Controller cooling", + [MC_CMD_SENSOR_PHY0_TEMP] = "PHY 0 temp. sensor", + [MC_CMD_SENSOR_PHY0_COOLING] = "PHY 0 cooling", + [MC_CMD_SENSOR_PHY1_TEMP] = "PHY 1 temp. sensor", + [MC_CMD_SENSOR_PHY1_COOLING] = "PHY 1 cooling", + [MC_CMD_SENSOR_IN_1V0] = "1.0V supply sensor", + [MC_CMD_SENSOR_IN_1V2] = "1.2V supply sensor", + [MC_CMD_SENSOR_IN_1V8] = "1.8V supply sensor", + [MC_CMD_SENSOR_IN_2V5] = "2.5V supply sensor", + [MC_CMD_SENSOR_IN_3V3] = "3.3V supply sensor", + [MC_CMD_SENSOR_IN_12V0] = "12V supply sensor" +}; + +static const char *sensor_status_names[] = { + [MC_CMD_SENSOR_STATE_OK] = "OK", + [MC_CMD_SENSOR_STATE_WARNING] = "Warning", + [MC_CMD_SENSOR_STATE_FATAL] = "Fatal", + [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure", +}; + +static void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev) +{ + unsigned int monitor, state, value; + const char *name, *state_txt; + monitor = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR); + state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE); + value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE); + /* Deal gracefully with the board having more drivers than we + * know about, but do not expect new sensor states. */ + name = (monitor >= ARRAY_SIZE(sensor_names)) + ? "No sensor name available" : + sensor_names[monitor]; + EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names)); + state_txt = sensor_status_names[state]; + + EFX_ERR(efx, "Sensor %d (%s) reports condition '%s' for raw value %d\n", + monitor, name, state_txt, value); +} + +/* Called from falcon_process_eventq for MCDI events */ +void efx_mcdi_process_event(struct efx_channel *channel, + efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + int code = EFX_QWORD_FIELD(*event, MCDI_EVENT_CODE); + u32 data = EFX_QWORD_FIELD(*event, MCDI_EVENT_DATA); + + switch (code) { + case MCDI_EVENT_CODE_BADSSERT: + EFX_ERR(efx, "MC watchdog or assertion failure at 0x%x\n", data); + efx_mcdi_ev_death(efx, EINTR); + break; + + case MCDI_EVENT_CODE_PMNOTICE: + EFX_INFO(efx, "MCDI PM event.\n"); + break; + + case MCDI_EVENT_CODE_CMDDONE: + efx_mcdi_ev_cpl(efx, + MCDI_EVENT_FIELD(*event, CMDDONE_SEQ), + MCDI_EVENT_FIELD(*event, CMDDONE_DATALEN), + MCDI_EVENT_FIELD(*event, CMDDONE_ERRNO)); + break; + + case MCDI_EVENT_CODE_LINKCHANGE: + efx_mcdi_process_link_change(efx, event); + break; + case MCDI_EVENT_CODE_SENSOREVT: + efx_mcdi_sensor_event(efx, event); + break; + case MCDI_EVENT_CODE_SCHEDERR: + EFX_INFO(efx, "MC Scheduler error address=0x%x\n", data); + break; + case MCDI_EVENT_CODE_REBOOT: + EFX_INFO(efx, "MC Reboot\n"); + efx_mcdi_ev_death(efx, EIO); + break; + case MCDI_EVENT_CODE_MAC_STATS_DMA: + /* MAC stats are gather lazily. We can ignore this. */ + break; + + default: + EFX_ERR(efx, "Unknown MCDI event 0x%x\n", code); + } +} + +/************************************************************************** + * + * Specific request functions + * + ************************************************************************** + */ + +int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build) +{ + u8 outbuf[ALIGN(MC_CMD_GET_VERSION_V1_OUT_LEN, 4)]; + size_t outlength; + const __le16 *ver_words; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_VERSION_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_VERSION, NULL, 0, + outbuf, sizeof(outbuf), &outlength); + if (rc) + goto fail; + + if (outlength == MC_CMD_GET_VERSION_V0_OUT_LEN) { + *version = 0; + *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); + return 0; + } + + if (outlength < MC_CMD_GET_VERSION_V1_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION); + *version = (((u64)le16_to_cpu(ver_words[0]) << 48) | + ((u64)le16_to_cpu(ver_words[1]) << 32) | + ((u64)le16_to_cpu(ver_words[2]) << 16) | + le16_to_cpu(ver_words[3])); + *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, + bool *was_attached) +{ + u8 inbuf[MC_CMD_DRV_ATTACH_IN_LEN]; + u8 outbuf[MC_CMD_DRV_ATTACH_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_NEW_STATE, + driver_operating ? 1 : 0); + MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_UPDATE, 1); + + rc = efx_mcdi_rpc(efx, MC_CMD_DRV_ATTACH, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_DRV_ATTACH_OUT_LEN) + goto fail; + + if (was_attached != NULL) + *was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, + u16 *fw_subtype_list) +{ + uint8_t outbuf[MC_CMD_GET_BOARD_CFG_OUT_LEN]; + size_t outlen; + int port_num = efx_port_num(efx); + int offset; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_BOARD_CFG, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_BOARD_CFG_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + offset = (port_num) + ? MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST + : MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST; + if (mac_address) + memcpy(mac_address, outbuf + offset, ETH_ALEN); + if (fw_subtype_list) + memcpy(fw_subtype_list, + outbuf + MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST, + MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d len=%d\n", __func__, rc, (int)outlen); + + return rc; +} + +int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq) +{ + u8 inbuf[MC_CMD_LOG_CTRL_IN_LEN]; + u32 dest = 0; + int rc; + + if (uart) + dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_UART; + if (evq) + dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ; + + MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST, dest); + MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST_EVQ, dest_evq); + + BUILD_BUG_ON(MC_CMD_LOG_CTRL_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_LOG_CTRL, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out) +{ + u8 outbuf[MC_CMD_NVRAM_TYPES_OUT_LEN]; + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_NVRAM_TYPES_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TYPES, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_NVRAM_TYPES_OUT_LEN) + goto fail; + + *nvram_types_out = MCDI_DWORD(outbuf, NVRAM_TYPES_OUT_TYPES); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", + __func__, rc); + return rc; +} + +int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, + size_t *size_out, size_t *erase_size_out, + bool *protected_out) +{ + u8 inbuf[MC_CMD_NVRAM_INFO_IN_LEN]; + u8 outbuf[MC_CMD_NVRAM_INFO_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_INFO_IN_TYPE, type); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_INFO, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_NVRAM_INFO_OUT_LEN) + goto fail; + + *size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE); + *erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE); + *protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) & + (1 << MC_CMD_NVRAM_PROTECTED_LBN)); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type) +{ + u8 inbuf[MC_CMD_NVRAM_UPDATE_START_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type); + + BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, + loff_t offset, u8 *buffer, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_READ_IN_LEN]; + u8 outbuf[MC_CMD_NVRAM_READ_OUT_LEN(length)]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type, + loff_t offset, const u8 *buffer, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_WRITE_IN_LEN(length)]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length); + memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length); + + BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, + loff_t offset, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_ERASE_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length); + + BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type) +{ + u8 inbuf[MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type); + + BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_handle_assertion(struct efx_nic *efx) +{ + union { + u8 asserts[MC_CMD_GET_ASSERTS_IN_LEN]; + u8 reboot[MC_CMD_REBOOT_IN_LEN]; + } inbuf; + u8 assertion[MC_CMD_GET_ASSERTS_OUT_LEN]; + unsigned int flags, index, ofst; + const char *reason; + size_t outlen; + int retry; + int rc; + + /* Check if the MC is in the assertion handler, retrying twice. Once + * because a boot-time assertion might cause this command to fail + * with EINTR. And once again because GET_ASSERTS can race with + * MC_CMD_REBOOT running on the other port. */ + retry = 2; + do { + MCDI_SET_DWORD(inbuf.asserts, GET_ASSERTS_IN_CLEAR, 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, + inbuf.asserts, MC_CMD_GET_ASSERTS_IN_LEN, + assertion, sizeof(assertion), &outlen); + } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); + + if (rc) + return rc; + if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) + return -EINVAL; + + flags = MCDI_DWORD(assertion, GET_ASSERTS_OUT_GLOBAL_FLAGS); + if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) + return 0; + + /* Reset the hardware atomically such that only one port with succeed. + * This command will succeed if a reboot is no longer required (because + * the other port did it first), but fail with EIO if it succeeds. + */ + BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); + MCDI_SET_DWORD(inbuf.reboot, REBOOT_IN_FLAGS, + MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION); + efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf.reboot, MC_CMD_REBOOT_IN_LEN, + NULL, 0, NULL); + + /* Print out the assertion */ + reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) + ? "system-level assertion" + : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) + ? "thread-level assertion" + : (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED) + ? "watchdog reset" + : "unknown assertion"; + EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason, + MCDI_DWORD(assertion, GET_ASSERTS_OUT_SAVED_PC_OFFS), + MCDI_DWORD(assertion, GET_ASSERTS_OUT_THREAD_OFFS)); + + /* Print out the registers */ + ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; + for (index = 1; index < 32; index++) { + EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, + MCDI_DWORD2(assertion, ofst)); + ofst += sizeof(efx_dword_t); + } + + return 0; +} + +void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; + int rc; + + BUILD_BUG_ON(EFX_LED_OFF != MC_CMD_LED_OFF); + BUILD_BUG_ON(EFX_LED_ON != MC_CMD_LED_ON); + BUILD_BUG_ON(EFX_LED_DEFAULT != MC_CMD_LED_DEFAULT); + + BUILD_BUG_ON(MC_CMD_SET_ID_LED_OUT_LEN != 0); + + MCDI_SET_DWORD(inbuf, SET_ID_LED_IN_STATE, mode); + + rc = efx_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); +} + +int efx_mcdi_reset_port(struct efx_nic *efx) +{ + int rc = efx_mcdi_rpc(efx, MC_CMD_PORT_RESET, NULL, 0, NULL, 0, NULL); + if (rc) + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_reset_mc(struct efx_nic *efx) +{ + u8 inbuf[MC_CMD_REBOOT_IN_LEN]; + int rc; + + BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); + MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, 0); + rc = efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, sizeof(inbuf), + NULL, 0, NULL); + /* White is black, and up is down */ + if (rc == -EIO) + return 0; + if (rc == 0) + rc = -EIO; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type, + const u8 *mac, int *id_out) +{ + u8 inbuf[MC_CMD_WOL_FILTER_SET_IN_LEN]; + u8 outbuf[MC_CMD_WOL_FILTER_SET_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_WOL_TYPE, type); + MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_FILTER_MODE, + MC_CMD_FILTER_MODE_SIMPLE); + memcpy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac, ETH_ALEN); + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_SET, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_WOL_FILTER_SET_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_SET_OUT_FILTER_ID); + + return 0; + +fail: + *id_out = -1; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; + +} + + +int +efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, const u8 *mac, int *id_out) +{ + return efx_mcdi_wol_filter_set(efx, MC_CMD_WOL_TYPE_MAGIC, mac, id_out); +} + + +int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out) +{ + u8 outbuf[MC_CMD_WOL_FILTER_GET_OUT_LEN]; + size_t outlen; + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_GET, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_WOL_FILTER_GET_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_GET_OUT_FILTER_ID); + + return 0; + +fail: + *id_out = -1; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + + +int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id) +{ + u8 inbuf[MC_CMD_WOL_FILTER_REMOVE_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, WOL_FILTER_REMOVE_IN_FILTER_ID, (u32)id); + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_REMOVE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + + +int efx_mcdi_wol_filter_reset(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_RESET, NULL, 0, NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + diff --git a/drivers/net/sfc/mcdi.h b/drivers/net/sfc/mcdi.h new file mode 100644 index 000000000000..de916728c2e3 --- /dev/null +++ b/drivers/net/sfc/mcdi.h @@ -0,0 +1,130 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2008-2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#ifndef EFX_MCDI_H +#define EFX_MCDI_H + +/** + * enum efx_mcdi_state + * @MCDI_STATE_QUIESCENT: No pending MCDI requests. If the caller holds the + * mcdi_lock then they are able to move to MCDI_STATE_RUNNING + * @MCDI_STATE_RUNNING: There is an MCDI request pending. Only the thread that + * moved into this state is allowed to move out of it. + * @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread + * has not yet consumed the result. For all other threads, equivalent to + * MCDI_STATE_RUNNING. + */ +enum efx_mcdi_state { + MCDI_STATE_QUIESCENT, + MCDI_STATE_RUNNING, + MCDI_STATE_COMPLETED, +}; + +enum efx_mcdi_mode { + MCDI_MODE_POLL, + MCDI_MODE_EVENTS, +}; + +/** + * struct efx_mcdi_iface + * @state: Interface state. Waited for by mcdi_wq. + * @wq: Wait queue for threads waiting for state != STATE_RUNNING + * @iface_lock: Protects @credits, @seqno, @resprc, @resplen + * @mode: Poll for mcdi completion, or wait for an mcdi_event. + * Serialised by @lock + * @seqno: The next sequence number to use for mcdi requests. + * Serialised by @lock + * @credits: Number of spurious MCDI completion events allowed before we + * trigger a fatal error. Protected by @lock + * @resprc: Returned MCDI completion + * @resplen: Returned payload length + */ +struct efx_mcdi_iface { + atomic_t state; + wait_queue_head_t wq; + spinlock_t iface_lock; + enum efx_mcdi_mode mode; + unsigned int credits; + unsigned int seqno; + unsigned int resprc; + size_t resplen; +}; + +extern void efx_mcdi_init(struct efx_nic *efx); + +extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, + size_t inlen, u8 *outbuf, size_t outlen, + size_t *outlen_actual); + +extern int efx_mcdi_poll_reboot(struct efx_nic *efx); +extern void efx_mcdi_mode_poll(struct efx_nic *efx); +extern void efx_mcdi_mode_event(struct efx_nic *efx); + +extern void efx_mcdi_process_event(struct efx_channel *channel, + efx_qword_t *event); + +#define MCDI_PTR2(_buf, _ofst) \ + (((u8 *)_buf) + _ofst) +#define MCDI_SET_DWORD2(_buf, _ofst, _value) \ + EFX_POPULATE_DWORD_1(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_DWORD_0, _value) +#define MCDI_DWORD2(_buf, _ofst) \ + EFX_DWORD_FIELD(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_DWORD_0) +#define MCDI_QWORD2(_buf, _ofst) \ + EFX_QWORD_FIELD64(*((efx_qword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_QWORD_0) + +#define MCDI_PTR(_buf, _ofst) \ + MCDI_PTR2(_buf, MC_CMD_ ## _ofst ## _OFST) +#define MCDI_SET_DWORD(_buf, _ofst, _value) \ + MCDI_SET_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST, _value) +#define MCDI_DWORD(_buf, _ofst) \ + MCDI_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST) +#define MCDI_QWORD(_buf, _ofst) \ + MCDI_QWORD2(_buf, MC_CMD_ ## _ofst ## _OFST) + +#define MCDI_EVENT_FIELD(_ev, _field) \ + EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) + +extern int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build); +extern int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, + bool *was_attached_out); +extern int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, + u16 *fw_subtype_list); +extern int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, + u32 dest_evq); +extern int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out); +extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, + size_t *size_out, size_t *erase_size_out, + bool *protected_out); +extern int efx_mcdi_nvram_update_start(struct efx_nic *efx, + unsigned int type); +extern int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, + loff_t offset, u8 *buffer, size_t length); +extern int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type, + loff_t offset, const u8 *buffer, + size_t length); +extern int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, + loff_t offset, size_t length); +extern int efx_mcdi_nvram_update_finish(struct efx_nic *efx, + unsigned int type); +extern int efx_mcdi_handle_assertion(struct efx_nic *efx); +extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); +extern int efx_mcdi_reset_port(struct efx_nic *efx); +extern int efx_mcdi_reset_mc(struct efx_nic *efx); +extern int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type, + const u8 *mac, int *id_out); +extern int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, + const u8 *mac, int *id_out); +extern int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out); +extern int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id); +extern int efx_mcdi_wol_filter_reset(struct efx_nic *efx); + +#endif /* EFX_MCDI_H */ diff --git a/drivers/net/sfc/mcdi_mac.c b/drivers/net/sfc/mcdi_mac.c new file mode 100644 index 000000000000..06d24a1e412a --- /dev/null +++ b/drivers/net/sfc/mcdi_mac.c @@ -0,0 +1,152 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#include "net_driver.h" +#include "efx.h" +#include "mac.h" +#include "mcdi.h" +#include "mcdi_pcol.h" + +static int efx_mcdi_set_mac(struct efx_nic *efx) +{ + u32 reject, fcntl; + u8 cmdbytes[MC_CMD_SET_MAC_IN_LEN]; + + memcpy(cmdbytes + MC_CMD_SET_MAC_IN_ADDR_OFST, + efx->net_dev->dev_addr, ETH_ALEN); + + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_MTU, + EFX_MAX_FRAME_LEN(efx->net_dev->mtu)); + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_DRAIN, 0); + + /* The MCDI command provides for controlling accept/reject + * of broadcast packets too, but the driver doesn't currently + * expose this. */ + reject = (efx->promiscuous) ? 0 : + (1 << MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN); + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_REJECT, reject); + + switch (efx->wanted_fc) { + case EFX_FC_RX | EFX_FC_TX: + fcntl = MC_CMD_FCNTL_BIDIR; + break; + case EFX_FC_RX: + fcntl = MC_CMD_FCNTL_RESPOND; + break; + default: + fcntl = MC_CMD_FCNTL_OFF; + break; + } + if (efx->wanted_fc & EFX_FC_AUTO) + fcntl = MC_CMD_FCNTL_AUTO; + + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_FCNTL, fcntl); + + return efx_mcdi_rpc(efx, MC_CMD_SET_MAC, cmdbytes, sizeof(cmdbytes), + NULL, 0, NULL); +} + +static int efx_mcdi_get_mac_faults(struct efx_nic *efx, u32 *faults) +{ + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + size_t outlength; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), &outlength); + if (rc) + goto fail; + + *faults = MCDI_DWORD(outbuf, GET_LINK_OUT_MAC_FAULT); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", + __func__, rc); + return rc; +} + +int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr, + u32 dma_len, int enable, int clear) +{ + u8 inbuf[MC_CMD_MAC_STATS_IN_LEN]; + int rc; + efx_dword_t *cmd_ptr; + int period = 1000; + u32 addr_hi; + u32 addr_lo; + + BUILD_BUG_ON(MC_CMD_MAC_STATS_OUT_LEN != 0); + + addr_lo = ((u64)dma_addr) >> 0; + addr_hi = ((u64)dma_addr) >> 32; + + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_LO, addr_lo); + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_HI, addr_hi); + cmd_ptr = (efx_dword_t *)MCDI_PTR(inbuf, MAC_STATS_IN_CMD); + if (enable) + EFX_POPULATE_DWORD_6(*cmd_ptr, + MC_CMD_MAC_STATS_CMD_DMA, 1, + MC_CMD_MAC_STATS_CMD_CLEAR, clear, + MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0, + MC_CMD_MAC_STATS_CMD_PERIOD_MS, period); + else + EFX_POPULATE_DWORD_5(*cmd_ptr, + MC_CMD_MAC_STATS_CMD_DMA, 0, + MC_CMD_MAC_STATS_CMD_CLEAR, clear, + MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 0, + MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0); + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_LEN, dma_len); + + rc = efx_mcdi_rpc(efx, MC_CMD_MAC_STATS, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: %s failed rc=%d\n", + __func__, enable ? "enable" : "disable", rc); + return rc; +} + +static int efx_mcdi_mac_reconfigure(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_set_mac(efx); + if (rc != 0) + return rc; + + /* Restore the multicast hash registers. */ + efx->type->push_multicast_hash(efx); + + return 0; +} + + +static bool efx_mcdi_mac_check_fault(struct efx_nic *efx) +{ + u32 faults; + int rc = efx_mcdi_get_mac_faults(efx, &faults); + return (rc != 0) || (faults != 0); +} + + +struct efx_mac_operations efx_mcdi_mac_operations = { + .reconfigure = efx_mcdi_mac_reconfigure, + .update_stats = efx_port_dummy_op_void, + .check_fault = efx_mcdi_mac_check_fault, +}; diff --git a/drivers/net/sfc/mcdi_pcol.h b/drivers/net/sfc/mcdi_pcol.h new file mode 100644 index 000000000000..2a85360a46f0 --- /dev/null +++ b/drivers/net/sfc/mcdi_pcol.h @@ -0,0 +1,1578 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + + +#ifndef MCDI_PCOL_H +#define MCDI_PCOL_H + +/* Values to be written into FMCR_CZ_RESET_STATE_REG to control boot. */ +/* Power-on reset state */ +#define MC_FW_STATE_POR (1) +/* If this is set in MC_RESET_STATE_REG then it should be + * possible to jump into IMEM without loading code from flash. */ +#define MC_FW_WARM_BOOT_OK (2) +/* The MC main image has started to boot. */ +#define MC_FW_STATE_BOOTING (4) +/* The Scheduler has started. */ +#define MC_FW_STATE_SCHED (8) + +/* Values to be written to the per-port status dword in shared + * memory on reboot and assert */ +#define MC_STATUS_DWORD_REBOOT (0xb007b007) +#define MC_STATUS_DWORD_ASSERT (0xdeaddead) + +/* The current version of the MCDI protocol. + * + * Note that the ROM burnt into the card only talks V0, so at the very + * least every driver must support version 0 and MCDI_PCOL_VERSION + */ +#define MCDI_PCOL_VERSION 1 + +/** + * MCDI version 1 + * + * Each MCDI request starts with an MCDI_HEADER, which is a 32byte + * structure, filled in by the client. + * + * 0 7 8 16 20 22 23 24 31 + * | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS | + * | | | + * | | \--- Response + * | \------- Error + * \------------------------------ Resync (always set) + * + * The client writes it's request into MC shared memory, and rings the + * doorbell. Each request is completed by either by the MC writting + * back into shared memory, or by writting out an event. + * + * All MCDI commands support completion by shared memory response. Each + * request may also contain additional data (accounted for by HEADER.LEN), + * and some response's may also contain additional data (again, accounted + * for by HEADER.LEN). + * + * Some MCDI commands support completion by event, in which any associated + * response data is included in the event. + * + * The protocol requires one response to be delivered for every request, a + * request should not be sent unless the response for the previous request + * has been received (either by polling shared memory, or by receiving + * an event). + */ + +/** Request/Response structure */ +#define MCDI_HEADER_OFST 0 +#define MCDI_HEADER_CODE_LBN 0 +#define MCDI_HEADER_CODE_WIDTH 7 +#define MCDI_HEADER_RESYNC_LBN 7 +#define MCDI_HEADER_RESYNC_WIDTH 1 +#define MCDI_HEADER_DATALEN_LBN 8 +#define MCDI_HEADER_DATALEN_WIDTH 8 +#define MCDI_HEADER_SEQ_LBN 16 +#define MCDI_HEADER_RSVD_LBN 20 +#define MCDI_HEADER_RSVD_WIDTH 2 +#define MCDI_HEADER_SEQ_WIDTH 4 +#define MCDI_HEADER_ERROR_LBN 22 +#define MCDI_HEADER_ERROR_WIDTH 1 +#define MCDI_HEADER_RESPONSE_LBN 23 +#define MCDI_HEADER_RESPONSE_WIDTH 1 +#define MCDI_HEADER_XFLAGS_LBN 24 +#define MCDI_HEADER_XFLAGS_WIDTH 8 +/* Request response using event */ +#define MCDI_HEADER_XFLAGS_EVREQ 0x01 + +/* Maximum number of payload bytes */ +#define MCDI_CTL_SDU_LEN_MAX 0xfc + +/* The MC can generate events for two reasons: + * - To complete a shared memory request if XFLAGS_EVREQ was set + * - As a notification (link state, i2c event), controlled + * via MC_CMD_LOG_CTRL + * + * Both events share a common structure: + * + * 0 32 33 36 44 52 60 + * | Data | Cont | Level | Src | Code | Rsvd | + * | + * \ There is another event pending in this notification + * + * If Code==CMDDONE, then the fields are further interpreted as: + * + * - LEVEL==INFO Command succeded + * - LEVEL==ERR Command failed + * + * 0 8 16 24 32 + * | Seq | Datalen | Errno | Rsvd | + * + * These fields are taken directly out of the standard MCDI header, i.e., + * LEVEL==ERR, Datalen == 0 => Reboot + * + * Events can be squirted out of the UART (using LOG_CTRL) without a + * MCDI header. An event can be distinguished from a MCDI response by + * examining the first byte which is 0xc0. This corresponds to the + * non-existent MCDI command MC_CMD_DEBUG_LOG. + * + * 0 7 8 + * | command | Resync | = 0xc0 + * + * Since the event is written in big-endian byte order, this works + * providing bits 56-63 of the event are 0xc0. + * + * 56 60 63 + * | Rsvd | Code | = 0xc0 + * + * Which means for convenience the event code is 0xc for all MC + * generated events. + */ +#define FSE_AZ_EV_CODE_MCDI_EVRESPONSE 0xc + +#define MCDI_EVENT_DATA_LBN 0 +#define MCDI_EVENT_DATA_WIDTH 32 +#define MCDI_EVENT_CONT_LBN 32 +#define MCDI_EVENT_CONT_WIDTH 1 +#define MCDI_EVENT_LEVEL_LBN 33 +#define MCDI_EVENT_LEVEL_WIDTH 3 +#define MCDI_EVENT_LEVEL_INFO (0) +#define MCDI_EVENT_LEVEL_WARN (1) +#define MCDI_EVENT_LEVEL_ERR (2) +#define MCDI_EVENT_LEVEL_FATAL (3) +#define MCDI_EVENT_SRC_LBN 36 +#define MCDI_EVENT_SRC_WIDTH 8 +#define MCDI_EVENT_CODE_LBN 44 +#define MCDI_EVENT_CODE_WIDTH 8 +#define MCDI_EVENT_CODE_BADSSERT (1) +#define MCDI_EVENT_CODE_PMNOTICE (2) +#define MCDI_EVENT_CODE_CMDDONE (3) +#define MCDI_EVENT_CMDDONE_SEQ_LBN 0 +#define MCDI_EVENT_CMDDONE_SEQ_WIDTH 8 +#define MCDI_EVENT_CMDDONE_DATALEN_LBN 8 +#define MCDI_EVENT_CMDDONE_DATALEN_WIDTH 8 +#define MCDI_EVENT_CMDDONE_ERRNO_LBN 16 +#define MCDI_EVENT_CMDDONE_ERRNO_WIDTH 8 +#define MCDI_EVENT_CODE_LINKCHANGE (4) +#define MCDI_EVENT_LINKCHANGE_LP_CAP_LBN 0 +#define MCDI_EVENT_LINKCHANGE_LP_CAP_WIDTH 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_LBN 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_WIDTH 4 +#define MCDI_EVENT_LINKCHANGE_SPEED_100M 1 +#define MCDI_EVENT_LINKCHANGE_SPEED_1G 2 +#define MCDI_EVENT_LINKCHANGE_SPEED_10G 3 +#define MCDI_EVENT_LINKCHANGE_FCNTL_LBN 20 +#define MCDI_EVENT_LINKCHANGE_FCNTL_WIDTH 4 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_LBN 24 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_WIDTH 8 +#define MCDI_EVENT_CODE_SENSOREVT (5) +#define MCDI_EVENT_SENSOREVT_MONITOR_LBN 0 +#define MCDI_EVENT_SENSOREVT_MONITOR_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_STATE_LBN 8 +#define MCDI_EVENT_SENSOREVT_STATE_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_VALUE_LBN 16 +#define MCDI_EVENT_SENSOREVT_VALUE_WIDTH 16 +#define MCDI_EVENT_CODE_SCHEDERR (6) +#define MCDI_EVENT_CODE_REBOOT (7) +#define MCDI_EVENT_CODE_MAC_STATS_DMA (8) +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_LBN 0 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_WIDTH 32 + +/* Non-existent command target */ +#define MC_CMD_ERR_ENOENT 2 +/* assert() has killed the MC */ +#define MC_CMD_ERR_EINTR 4 +/* Caller does not hold required locks */ +#define MC_CMD_ERR_EACCES 13 +/* Resource is currently unavailable (e.g. lock contention) */ +#define MC_CMD_ERR_EBUSY 16 +/* Invalid argument to target */ +#define MC_CMD_ERR_EINVAL 22 +/* Non-recursive resource is already acquired */ +#define MC_CMD_ERR_EDEADLK 35 +/* Operation not implemented */ +#define MC_CMD_ERR_ENOSYS 38 +/* Operation timed out */ +#define MC_CMD_ERR_ETIME 62 + +#define MC_CMD_ERR_CODE_OFST 0 + + +/* MC_CMD_READ32: (debug, variadic out) + * Read multiple 32byte words from MC memory + */ +#define MC_CMD_READ32 0x01 +#define MC_CMD_READ32_IN_LEN 8 +#define MC_CMD_READ32_IN_ADDR_OFST 0 +#define MC_CMD_READ32_IN_NUMWORDS_OFST 4 +#define MC_CMD_READ32_OUT_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_READ32_OUT_BUFFER_OFST 0 + +/* MC_CMD_WRITE32: (debug, variadic in) + * Write multiple 32byte words to MC memory + */ +#define MC_CMD_WRITE32 0x02 +#define MC_CMD_WRITE32_IN_LEN(_numwords) (((_numwords) * 4) + 4) +#define MC_CMD_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_WRITE32_IN_BUFFER_OFST 4 +#define MC_CMD_WRITE32_OUT_LEN 0 + +/* MC_CMD_COPYCODE: (debug) + * Copy MC code between two locations and jump + */ +#define MC_CMD_COPYCODE 0x03 +#define MC_CMD_COPYCODE_IN_LEN 16 +#define MC_CMD_COPYCODE_IN_SRC_ADDR_OFST 0 +#define MC_CMD_COPYCODE_IN_DEST_ADDR_OFST 4 +#define MC_CMD_COPYCODE_IN_NUMWORDS_OFST 8 +#define MC_CMD_COPYCODE_IN_JUMP_OFST 12 +/* Control should return to the caller rather than jumping */ +#define MC_CMD_COPYCODE_JUMP_NONE 1 +#define MC_CMD_COPYCODE_OUT_LEN 0 + +/* MC_CMD_SET_FUNC: (debug) + * Select function for function-specific commands. + */ +#define MC_CMD_SET_FUNC 0x04 +#define MC_CMD_SET_FUNC_IN_LEN 4 +#define MC_CMD_SET_FUNC_IN_FUNC_OFST 0 +#define MC_CMD_SET_FUNC_OUT_LEN 0 + +/* MC_CMD_GET_BOOT_STATUS: + * Get the instruction address from which the MC booted. + */ +#define MC_CMD_GET_BOOT_STATUS 0x05 +#define MC_CMD_GET_BOOT_STATUS_IN_LEN 0 +#define MC_CMD_GET_BOOT_STATUS_OUT_LEN 8 +#define MC_CMD_GET_BOOT_STATUS_OUT_BOOT_OFFSET_OFST 0 +#define MC_CMD_GET_BOOT_STATUS_OUT_FLAGS_OFST 4 +/* Reboot caused by watchdog */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_WATCHDOG_LBN (0) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_WATCHDOG_WIDTH (1) +/* MC booted from primary flash partition */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_PRIMARY_LBN (1) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_PRIMARY_WIDTH (1) +/* MC booted from backup flash partition */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_BACKUP_LBN (2) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_BACKUP_WIDTH (1) + +/* MC_CMD_GET_ASSERTS: (debug, variadic out) + * Get (and optionally clear) the current assertion status. + * + * Only OUT.GLOBAL_FLAGS is guaranteed to exist in the completion + * payload. The other fields will only be present if + * OUT.GLOBAL_FLAGS != NO_FAILS + */ +#define MC_CMD_GET_ASSERTS 0x06 +#define MC_CMD_GET_ASSERTS_IN_LEN 4 +#define MC_CMD_GET_ASSERTS_IN_CLEAR_OFST 0 +#define MC_CMD_GET_ASSERTS_OUT_LEN 140 +/* Assertion status flag */ +#define MC_CMD_GET_ASSERTS_OUT_GLOBAL_FLAGS_OFST 0 +/*! No assertions have failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS 1 +/*! A system-level assertion has failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL 2 +/*! A thread-level assertion has failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL 3 +/*! The system was reset by the watchdog. */ +#define MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED 4 +/* Failing PC value */ +#define MC_CMD_GET_ASSERTS_OUT_SAVED_PC_OFFS_OFST 4 +/* Saved GP regs */ +#define MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST 8 +#define MC_CMD_GET_ASSERTS_OUT_GP_REGS_LEN 124 +/* Failing thread address */ +#define MC_CMD_GET_ASSERTS_OUT_THREAD_OFFS_OFST 132 + +/* MC_CMD_LOG_CTRL: + * Determine the output stream for various events and messages + */ +#define MC_CMD_LOG_CTRL 0x07 +#define MC_CMD_LOG_CTRL_IN_LEN 8 +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_OFST 0 +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_UART (1) +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ (2) +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ_OFST 4 +#define MC_CMD_LOG_CTRL_OUT_LEN 0 + +/* MC_CMD_GET_VERSION: + * Get version information about the MC firmware + */ +#define MC_CMD_GET_VERSION 0x08 +#define MC_CMD_GET_VERSION_IN_LEN 0 +#define MC_CMD_GET_VERSION_V0_OUT_LEN 4 +#define MC_CMD_GET_VERSION_V1_OUT_LEN 32 +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_OFST 0 +/* Reserved version number to indicate "any" version. */ +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_ANY 0xffffffff +/* The version response of a boot ROM awaiting rescue */ +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM 0xb0070000 +#define MC_CMD_GET_VERSION_V1_OUT_PCOL_OFST 4 +/* 128bit mask of functions supported by the current firmware */ +#define MC_CMD_GET_VERSION_V1_OUT_SUPPORTED_FUNCS_OFST 8 +/* The command set exported by the boot ROM (MCDI v0) */ +#define MC_CMD_GET_VERSION_V0_SUPPORTED_FUNCS { \ + (1 << MC_CMD_READ32) | \ + (1 << MC_CMD_WRITE32) | \ + (1 << MC_CMD_COPYCODE) | \ + (1 << MC_CMD_GET_VERSION), \ + 0, 0, 0 } +#define MC_CMD_GET_VERSION_OUT_VERSION_OFST 24 + +/* Vectors in the boot ROM */ +/* Point to the copycode entry point. */ +#define MC_BOOTROM_COPYCODE_VEC (0x7f4) +/* Points to the recovery mode entry point. */ +#define MC_BOOTROM_NOFLASH_VEC (0x7f8) + +/* Test execution limits */ +#define MC_TESTEXEC_VARIANT_COUNT 16 +#define MC_TESTEXEC_RESULT_COUNT 7 + +/* MC_CMD_SET_TESTVARS: (debug, variadic in) + * Write variant words for test. + * + * The user supplies a bitmap of the variants they wish to set. + * They must ensure that IN.LEN >= 4 + 4 * ffs(BITMAP) + */ +#define MC_CMD_SET_TESTVARS 0x09 +#define MC_CMD_SET_TESTVARS_IN_LEN(_numwords) \ + (4 + 4*(_numwords)) +#define MC_CMD_SET_TESTVARS_IN_ARGS_BITMAP_OFST 0 +/* Up to MC_TESTEXEC_VARIANT_COUNT of 32byte words start here */ +#define MC_CMD_SET_TESTVARS_IN_ARGS_BUFFER_OFST 4 +#define MC_CMD_SET_TESTVARS_OUT_LEN 0 + +/* MC_CMD_GET_TESTRCS: (debug, variadic out) + * Return result words from test. + */ +#define MC_CMD_GET_TESTRCS 0x0a +#define MC_CMD_GET_TESTRCS_IN_LEN 4 +#define MC_CMD_GET_TESTRCS_IN_NUMWORDS_OFST 0 +#define MC_CMD_GET_TESTRCS_OUT_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_GET_TESTRCS_OUT_BUFFER_OFST 0 + +/* MC_CMD_RUN_TEST: (debug) + * Run the test exported by this firmware image + */ +#define MC_CMD_RUN_TEST 0x0b +#define MC_CMD_RUN_TEST_IN_LEN 0 +#define MC_CMD_RUN_TEST_OUT_LEN 0 + +/* MC_CMD_CSR_READ32: (debug, variadic out) + * Read 32bit words from the indirect memory map + */ +#define MC_CMD_CSR_READ32 0x0c +#define MC_CMD_CSR_READ32_IN_LEN 12 +#define MC_CMD_CSR_READ32_IN_ADDR_OFST 0 +#define MC_CMD_CSR_READ32_IN_STEP_OFST 4 +#define MC_CMD_CSR_READ32_IN_NUMWORDS_OFST 8 +#define MC_CMD_CSR_READ32_OUT_LEN(_numwords) \ + (((_numwords) * 4) + 4) +/* IN.NUMWORDS of 32bit words start here */ +#define MC_CMD_CSR_READ32_OUT_BUFFER_OFST 0 +#define MC_CMD_CSR_READ32_OUT_IREG_STATUS_OFST(_numwords) \ + ((_numwords) * 4) + +/* MC_CMD_CSR_WRITE32: (debug, variadic in) + * Write 32bit dwords to the indirect memory map + */ +#define MC_CMD_CSR_WRITE32 0x0d +#define MC_CMD_CSR_WRITE32_IN_LEN(_numwords) \ + (((_numwords) * 4) + 8) +#define MC_CMD_CSR_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_CSR_WRITE32_IN_STEP_OFST 4 +/* Multiple 32bit words of data to write start here */ +#define MC_CMD_CSR_WRITE32_IN_BUFFER_OFST 8 +#define MC_CMD_CSR_WRITE32_OUT_LEN 4 +#define MC_CMD_CSR_WRITE32_OUT_STATUS_OFST 0 + +/* MC_CMD_JTAG_WORK: (debug, fpga only) + * Process JTAG work buffer for RBF acceleration. + * + * Host: bit count, (up to) 32 words of data to clock out to JTAG + * (bits 1,0=TMS,TDO for first bit; bits 3,2=TMS,TDO for second bit, etc.) + * MC: bit count, (up to) 32 words of data clocked in from JTAG + * (bit 0=TDI for first bit, bit 1=TDI for second bit, etc.; [31:16] unused) + */ +#define MC_CMD_JTAG_WORK 0x0e + +/* MC_CMD_STACKINFO: (debug, variadic out) + * Get stack information + * + * Host: nothing + * MC: (thread ptr, stack size, free space) for each thread in system + */ +#define MC_CMD_STACKINFO 0x0f + +/* MC_CMD_MDIO_READ: + * MDIO register read + */ +#define MC_CMD_MDIO_READ 0x10 +#define MC_CMD_MDIO_READ_IN_LEN 16 +#define MC_CMD_MDIO_READ_IN_BUS_OFST 0 +#define MC_CMD_MDIO_READ_IN_PRTAD_OFST 4 +#define MC_CMD_MDIO_READ_IN_DEVAD_OFST 8 +#define MC_CMD_MDIO_READ_IN_ADDR_OFST 12 +#define MC_CMD_MDIO_READ_OUT_LEN 8 +#define MC_CMD_MDIO_READ_OUT_VALUE_OFST 0 +#define MC_CMD_MDIO_READ_OUT_STATUS_OFST 4 + +/* MC_CMD_MDIO_WRITE: + * MDIO register write + */ +#define MC_CMD_MDIO_WRITE 0x11 +#define MC_CMD_MDIO_WRITE_IN_LEN 20 +#define MC_CMD_MDIO_WRITE_IN_BUS_OFST 0 +#define MC_CMD_MDIO_WRITE_IN_PRTAD_OFST 4 +#define MC_CMD_MDIO_WRITE_IN_DEVAD_OFST 8 +#define MC_CMD_MDIO_WRITE_IN_ADDR_OFST 12 +#define MC_CMD_MDIO_WRITE_IN_VALUE_OFST 16 +#define MC_CMD_MDIO_WRITE_OUT_LEN 4 +#define MC_CMD_MDIO_WRITE_OUT_STATUS_OFST 0 + +/* By default all the MCDI MDIO operations perform clause45 mode. + * If you want to use clause22 then set DEVAD = MC_CMD_MDIO_CLAUSE22. + */ +#define MC_CMD_MDIO_CLAUSE22 32 + +/* There are two MDIO buses: one for the internal PHY, and one for external + * devices. + */ +#define MC_CMD_MDIO_BUS_INTERNAL 0 +#define MC_CMD_MDIO_BUS_EXTERNAL 1 + +/* The MDIO commands return the raw status bits from the MDIO block. A "good" + * transaction should have the DONE bit set and all other bits clear. + */ +#define MC_CMD_MDIO_STATUS_GOOD 0x08 + + +/* MC_CMD_DBI_WRITE: (debug) + * Write DBI register(s) + * + * Host: address, byte-enables (and VF selection, and cs2 flag), + * value [,address ...] + * MC: nothing + */ +#define MC_CMD_DBI_WRITE 0x12 +#define MC_CMD_DBI_WRITE_IN_LEN(_numwords) \ + (12 * (_numwords)) +#define MC_CMD_DBI_WRITE_IN_ADDRESS_OFST(_word) \ + (((_word) * 12) + 0) +#define MC_CMD_DBI_WRITE_IN_BYTE_MASK_OFST(_word) \ + (((_word) * 12) + 4) +#define MC_CMD_DBI_WRITE_IN_VALUE_OFST(_word) \ + (((_word) * 12) + 8) +#define MC_CMD_DBI_WRITE_OUT_LEN 0 + +/* MC_CMD_DBI_READ: (debug) + * Read DBI register(s) + * + * Host: address, [,address ...] + * MC: value [,value ...] + * (note: this does not support reading from VFs, but is retained for backwards + * compatibility; see MC_CMD_DBI_READX below) + */ +#define MC_CMD_DBI_READ 0x13 +#define MC_CMD_DBI_READ_IN_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_DBI_READ_OUT_LEN(_numwords) \ + (4 * (_numwords)) + +/* MC_CMD_PORT_READ32: (debug) + * Read a 32-bit register from the indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_READ32 0x14 +#define MC_CMD_PORT_READ32_IN_LEN 4 +#define MC_CMD_PORT_READ32_IN_ADDR_OFST 0 +#define MC_CMD_PORT_READ32_OUT_LEN 8 +#define MC_CMD_PORT_READ32_OUT_VALUE_OFST 0 +#define MC_CMD_PORT_READ32_OUT_STATUS_OFST 4 + +/* MC_CMD_PORT_WRITE32: (debug) + * Write a 32-bit register to the indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_WRITE32 0x15 +#define MC_CMD_PORT_WRITE32_IN_LEN 8 +#define MC_CMD_PORT_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_PORT_WRITE32_IN_VALUE_OFST 4 +#define MC_CMD_PORT_WRITE32_OUT_LEN 4 +#define MC_CMD_PORT_WRITE32_OUT_STATUS_OFST 0 + +/* MC_CMD_PORT_READ128: (debug) + * Read a 128-bit register from indirect port register map + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_READ128 0x16 +#define MC_CMD_PORT_READ128_IN_LEN 4 +#define MC_CMD_PORT_READ128_IN_ADDR_OFST 0 +#define MC_CMD_PORT_READ128_OUT_LEN 20 +#define MC_CMD_PORT_READ128_OUT_VALUE_OFST 0 +#define MC_CMD_PORT_READ128_OUT_STATUS_OFST 16 + +/* MC_CMD_PORT_WRITE128: (debug) + * Write a 128-bit register to indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_WRITE128 0x17 +#define MC_CMD_PORT_WRITE128_IN_LEN 20 +#define MC_CMD_PORT_WRITE128_IN_ADDR_OFST 0 +#define MC_CMD_PORT_WRITE128_IN_VALUE_OFST 4 +#define MC_CMD_PORT_WRITE128_OUT_LEN 4 +#define MC_CMD_PORT_WRITE128_OUT_STATUS_OFST 0 + +/* MC_CMD_GET_BOARD_CFG: + * Returns the MC firmware configuration structure + * + * The FW_SUBTYPE_LIST contains a 16-bit value for each of the 12 types of + * NVRAM area. The values are defined in the firmware/mc/platform/<xxx>.c file + * for a specific board type, but otherwise have no meaning to the MC; they + * are used by the driver to manage selection of appropriate firmware updates. + */ +#define MC_CMD_GET_BOARD_CFG 0x18 +#define MC_CMD_GET_BOARD_CFG_IN_LEN 0 +#define MC_CMD_GET_BOARD_CFG_OUT_LEN 96 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_TYPE_OFST 0 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_NAME_OFST 4 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_NAME_LEN 32 +#define MC_CMD_GET_BOARD_CFG_OUT_CAPABILITIES_PORT0_OFST 36 +#define MC_CMD_GET_BOARD_CFG_OUT_CAPABILITIES_PORT1_OFST 40 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST 44 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_LEN 6 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST 50 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_LEN 6 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_COUNT_PORT0_OFST 56 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_COUNT_PORT1_OFST 60 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_STRIDE_PORT0_OFST 64 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_STRIDE_PORT1_OFST 68 +#define MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST 72 +#define MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN 24 + +/* MC_CMD_DBI_READX: (debug) + * Read DBI register(s) -- extended functionality + * + * Host: vf selection, address, [,vf selection ...] + * MC: value [,value ...] + */ +#define MC_CMD_DBI_READX 0x19 +#define MC_CMD_DBI_READX_IN_LEN(_numwords) \ + (8*(_numwords)) +#define MC_CMD_DBI_READX_OUT_LEN(_numwords) \ + (4*(_numwords)) + +/* MC_CMD_SET_RAND_SEED: + * Set the 16byte seed for the MC psuedo-random generator + */ +#define MC_CMD_SET_RAND_SEED 0x1a +#define MC_CMD_SET_RAND_SEED_IN_LEN 16 +#define MC_CMD_SET_RAND_SEED_IN_SEED_OFST 0 +#define MC_CMD_SET_RAND_SEED_OUT_LEN 0 + +/* MC_CMD_LTSSM_HIST: (debug) + * Retrieve the history of the LTSSM, if the build supports it. + * + * Host: nothing + * MC: variable number of LTSSM values, as bytes + * The history is read-to-clear. + */ +#define MC_CMD_LTSSM_HIST 0x1b + +/* MC_CMD_DRV_ATTACH: + * Inform MCPU that this port is managed on the host (i.e. driver active) + */ +#define MC_CMD_DRV_ATTACH 0x1c +#define MC_CMD_DRV_ATTACH_IN_LEN 8 +#define MC_CMD_DRV_ATTACH_IN_NEW_STATE_OFST 0 +#define MC_CMD_DRV_ATTACH_IN_UPDATE_OFST 4 +#define MC_CMD_DRV_ATTACH_OUT_LEN 4 +#define MC_CMD_DRV_ATTACH_OUT_OLD_STATE_OFST 0 + +/* MC_CMD_NCSI_PROD: (debug) + * Trigger an NC-SI event (and possibly an AEN in response) + */ +#define MC_CMD_NCSI_PROD 0x1d +#define MC_CMD_NCSI_PROD_IN_LEN 4 +#define MC_CMD_NCSI_PROD_IN_EVENTS_OFST 0 +#define MC_CMD_NCSI_PROD_LINKCHANGE_LBN 0 +#define MC_CMD_NCSI_PROD_LINKCHANGE_WIDTH 1 +#define MC_CMD_NCSI_PROD_RESET_LBN 1 +#define MC_CMD_NCSI_PROD_RESET_WIDTH 1 +#define MC_CMD_NCSI_PROD_DRVATTACH_LBN 2 +#define MC_CMD_NCSI_PROD_DRVATTACH_WIDTH 1 +#define MC_CMD_NCSI_PROD_OUT_LEN 0 + +/* Enumeration */ +#define MC_CMD_NCSI_PROD_LINKCHANGE 0 +#define MC_CMD_NCSI_PROD_RESET 1 +#define MC_CMD_NCSI_PROD_DRVATTACH 2 + +/* MC_CMD_DEVEL: (debug) + * Reserved for development + */ +#define MC_CMD_DEVEL 0x1e + +/* MC_CMD_SHMUART: (debug) + * Route UART output to circular buffer in shared memory instead. + */ +#define MC_CMD_SHMUART 0x1f +#define MC_CMD_SHMUART_IN_FLAG_OFST 0 +#define MC_CMD_SHMUART_IN_LEN 4 +#define MC_CMD_SHMUART_OUT_LEN 0 + +/* MC_CMD_PORT_RESET: + * Generic per-port reset. There is no equivalent for per-board reset. + * + * Locks required: None + * Return code: 0, ETIME + */ +#define MC_CMD_PORT_RESET 0x20 +#define MC_CMD_PORT_RESET_IN_LEN 0 +#define MC_CMD_PORT_RESET_OUT_LEN 0 + +/* MC_CMD_RESOURCE_LOCK: + * Generic resource lock/unlock interface. + * + * Locks required: None + * Return code: 0, + * EBUSY (if trylock is contended by other port), + * EDEADLK (if trylock is already acquired by this port) + * EINVAL (if unlock doesn't own the lock) + */ +#define MC_CMD_RESOURCE_LOCK 0x21 +#define MC_CMD_RESOURCE_LOCK_IN_LEN 8 +#define MC_CMD_RESOURCE_LOCK_IN_ACTION_OFST 0 +#define MC_CMD_RESOURCE_LOCK_ACTION_TRYLOCK 1 +#define MC_CMD_RESOURCE_LOCK_ACTION_UNLOCK 0 +#define MC_CMD_RESOURCE_LOCK_IN_RESOURCE_OFST 4 +#define MC_CMD_RESOURCE_LOCK_I2C 2 +#define MC_CMD_RESOURCE_LOCK_PHY 3 +#define MC_CMD_RESOURCE_LOCK_OUT_LEN 0 + +/* MC_CMD_SPI_COMMAND: (variadic in, variadic out) + * Read/Write to/from the SPI device. + * + * Locks required: SPI_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if SPI_LOCK is not held) + */ +#define MC_CMD_SPI_COMMAND 0x22 +#define MC_CMD_SPI_COMMAND_IN_LEN(_write_bytes) (12 + (_write_bytes)) +#define MC_CMD_SPI_COMMAND_IN_ARGS_OFST 0 +#define MC_CMD_SPI_COMMAND_IN_ARGS_ADDRESS_OFST 0 +#define MC_CMD_SPI_COMMAND_IN_ARGS_READ_BYTES_OFST 4 +#define MC_CMD_SPI_COMMAND_IN_ARGS_CHIP_SELECT_OFST 8 +/* Data to write here */ +#define MC_CMD_SPI_COMMAND_IN_WRITE_BUFFER_OFST 12 +#define MC_CMD_SPI_COMMAND_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_SPI_COMMAND_OUT_READ_BUFFER_OFST 0 + +/* MC_CMD_I2C_READ_WRITE: (variadic in, variadic out) + * Read/Write to/from the I2C bus. + * + * Locks required: I2C_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if I2C_LOCK is not held) + */ +#define MC_CMD_I2C_RW 0x23 +#define MC_CMD_I2C_RW_IN_LEN(_write_bytes) (8 + (_write_bytes)) +#define MC_CMD_I2C_RW_IN_ARGS_OFST 0 +#define MC_CMD_I2C_RW_IN_ARGS_ADDR_OFST 0 +#define MC_CMD_I2C_RW_IN_ARGS_READ_BYTES_OFST 4 +/* Data to write here */ +#define MC_CMD_I2C_RW_IN_WRITE_BUFFER_OFSET 8 +#define MC_CMD_I2C_RW_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_I2C_RW_OUT_READ_BUFFER_OFST 0 + +/* Generic phy capability bitmask */ +#define MC_CMD_PHY_CAP_10HDX_LBN 1 +#define MC_CMD_PHY_CAP_10HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10FDX_LBN 2 +#define MC_CMD_PHY_CAP_10FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100HDX_LBN 3 +#define MC_CMD_PHY_CAP_100HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100FDX_LBN 4 +#define MC_CMD_PHY_CAP_100FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000HDX_LBN 5 +#define MC_CMD_PHY_CAP_1000HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000FDX_LBN 6 +#define MC_CMD_PHY_CAP_1000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10000FDX_LBN 7 +#define MC_CMD_PHY_CAP_10000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_PAUSE_LBN 8 +#define MC_CMD_PHY_CAP_PAUSE_WIDTH 1 +#define MC_CMD_PHY_CAP_ASYM_LBN 9 +#define MC_CMD_PHY_CAP_ASYM_WIDTH 1 +#define MC_CMD_PHY_CAP_AN_LBN 10 +#define MC_CMD_PHY_CAP_AN_WIDTH 1 + +/* Generic loopback enumeration */ +#define MC_CMD_LOOPBACK_NONE 0 +#define MC_CMD_LOOPBACK_DATA 1 +#define MC_CMD_LOOPBACK_GMAC 2 +#define MC_CMD_LOOPBACK_XGMII 3 +#define MC_CMD_LOOPBACK_XGXS 4 +#define MC_CMD_LOOPBACK_XAUI 5 +#define MC_CMD_LOOPBACK_GMII 6 +#define MC_CMD_LOOPBACK_SGMII 7 +#define MC_CMD_LOOPBACK_XGBR 8 +#define MC_CMD_LOOPBACK_XFI 9 +#define MC_CMD_LOOPBACK_XAUI_FAR 10 +#define MC_CMD_LOOPBACK_GMII_FAR 11 +#define MC_CMD_LOOPBACK_SGMII_FAR 12 +#define MC_CMD_LOOPBACK_XFI_FAR 13 +#define MC_CMD_LOOPBACK_GPHY 14 +#define MC_CMD_LOOPBACK_PHYXS 15 +#define MC_CMD_LOOPBACK_PCS 16 +#define MC_CMD_LOOPBACK_PMAPMD 17 +#define MC_CMD_LOOPBACK_XPORT 18 +#define MC_CMD_LOOPBACK_XGMII_WS 19 +#define MC_CMD_LOOPBACK_XAUI_WS 20 +#define MC_CMD_LOOPBACK_XAUI_WS_FAR 21 +#define MC_CMD_LOOPBACK_XAUI_WS_NEAR 22 +#define MC_CMD_LOOPBACK_GMII_WS 23 +#define MC_CMD_LOOPBACK_XFI_WS 24 +#define MC_CMD_LOOPBACK_XFI_WS_FAR 25 +#define MC_CMD_LOOPBACK_PHYXS_WS 26 + +/* Generic PHY statistics enumeration */ +#define MC_CMD_OUI 0 +#define MC_CMD_PMA_PMD_LINK_UP 1 +#define MC_CMD_PMA_PMD_RX_FAULT 2 +#define MC_CMD_PMA_PMD_TX_FAULT 3 +#define MC_CMD_PMA_PMD_SIGNAL 4 +#define MC_CMD_PMA_PMD_SNR_A 5 +#define MC_CMD_PMA_PMD_SNR_B 6 +#define MC_CMD_PMA_PMD_SNR_C 7 +#define MC_CMD_PMA_PMD_SNR_D 8 +#define MC_CMD_PCS_LINK_UP 9 +#define MC_CMD_PCS_RX_FAULT 10 +#define MC_CMD_PCS_TX_FAULT 11 +#define MC_CMD_PCS_BER 12 +#define MC_CMD_PCS_BLOCK_ERRORS 13 +#define MC_CMD_PHYXS_LINK_UP 14 +#define MC_CMD_PHYXS_RX_FAULT 15 +#define MC_CMD_PHYXS_TX_FAULT 16 +#define MC_CMD_PHYXS_ALIGN 17 +#define MC_CMD_PHYXS_SYNC 18 +#define MC_CMD_AN_LINK_UP 19 +#define MC_CMD_AN_COMPLETE 20 +#define MC_CMD_AN_10GBT_STATUS 21 +#define MC_CMD_CL22_LINK_UP 22 +#define MC_CMD_PHY_NSTATS 23 + +/* MC_CMD_GET_PHY_CFG: + * Report PHY configuration. This guarantees to succeed even if the PHY is in + * a "zombie" state. + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_PHY_CFG 0x24 + +#define MC_CMD_GET_PHY_CFG_IN_LEN 0 +#define MC_CMD_GET_PHY_CFG_OUT_LEN 72 + +#define MC_CMD_GET_PHY_CFG_OUT_FLAGS_OFST 0 +#define MC_CMD_GET_PHY_CFG_PRESENT_LBN 0 +#define MC_CMD_GET_PHY_CFG_PRESENT_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_SHORTBIST_LBN 1 +#define MC_CMD_GET_PHY_CFG_SHORTBIST_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_LONGBIST_LBN 2 +#define MC_CMD_GET_PHY_CFG_LONGBIST_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_LOWPOWER_LBN 3 +#define MC_CMD_GET_PHY_CFG_LOWPOWER_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_POWEROFF_LBN 4 +#define MC_CMD_GET_PHY_CFG_POWEROFF_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_TXDIS_LBN 5 +#define MC_CMD_GET_PHY_CFG_TXDIS_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_TYPE_OFST 4 +/* Bitmask of supported capabilities */ +#define MC_CMD_GET_PHY_CFG_OUT_SUPPORTED_CAP_OFST 8 +#define MC_CMD_GET_PHY_CFG_OUT_CHANNEL_OFST 12 +#define MC_CMD_GET_PHY_CFG_OUT_PRT_OFST 16 +/* PHY statistics bitmap */ +#define MC_CMD_GET_PHY_CFG_OUT_STATS_MASK_OFST 20 +/* PHY type/name string */ +#define MC_CMD_GET_PHY_CFG_OUT_NAME_OFST 24 +#define MC_CMD_GET_PHY_CFG_OUT_NAME_LEN 20 +#define MC_CMD_GET_PHY_CFG_OUT_MEDIA_TYPE_OFST 44 +#define MC_CMD_MEDIA_XAUI 1 +#define MC_CMD_MEDIA_CX4 2 +#define MC_CMD_MEDIA_KX4 3 +#define MC_CMD_MEDIA_XFP 4 +#define MC_CMD_MEDIA_SFP_PLUS 5 +#define MC_CMD_MEDIA_BASE_T 6 +/* MDIO "MMDS" supported */ +#define MC_CMD_GET_PHY_CFG_OUT_MMD_MASK_OFST 48 +/* Native clause 22 */ +#define MC_CMD_MMD_CLAUSE22 0 +#define MC_CMD_MMD_CLAUSE45_PMAPMD 1 +#define MC_CMD_MMD_CLAUSE45_WIS 2 +#define MC_CMD_MMD_CLAUSE45_PCS 3 +#define MC_CMD_MMD_CLAUSE45_PHYXS 4 +#define MC_CMD_MMD_CLAUSE45_DTEXS 5 +#define MC_CMD_MMD_CLAUSE45_TC 6 +#define MC_CMD_MMD_CLAUSE45_AN 7 +/* Clause22 proxied over clause45 by PHY */ +#define MC_CMD_MMD_CLAUSE45_C22EXT 29 +#define MC_CMD_MMD_CLAUSE45_VEND1 30 +#define MC_CMD_MMD_CLAUSE45_VEND2 31 +/* PHY stepping version */ +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_OFST 52 +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN 20 + +/* MC_CMD_START_PHY_BIST: + * Start a BIST test on the PHY. + * + * Locks required: PHY_LOCK if doing a PHY BIST + * Return code: 0, EINVAL, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_START_BIST 0x25 +#define MC_CMD_START_BIST_IN_LEN 4 +#define MC_CMD_START_BIST_TYPE_OFST 0 + +/* Run the PHY's short BIST */ +#define MC_CMD_PHY_BIST_SHORT 1 +/* Run the PHY's long BIST */ +#define MC_CMD_PHY_BIST_LONG 2 +/* Run BIST on the currently selected BPX Serdes (XAUI or XFI) */ +#define MC_CMD_BPX_SERDES_BIST 3 + +/* MC_CMD_POLL_PHY_BIST: (variadic output) + * Poll for BIST completion + * + * Returns a single status code, and a binary blob of phy-specific + * bist output. If the driver can't succesfully parse the BIST output, + * it should still respect the Pass/Fail in OUT.RESULT. + * + * Locks required: PHY_LOCK if doing a PHY BIST + * Return code: 0, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_POLL_BIST 0x26 +#define MC_CMD_POLL_BIST_IN_LEN 0 +#define MC_CMD_POLL_BIST_OUT_LEN UNKNOWN +#define MC_CMD_POLL_BIST_OUT_RESULT_OFST 0 +#define MC_CMD_POLL_BIST_RUNNING 1 +#define MC_CMD_POLL_BIST_PASSED 2 +#define MC_CMD_POLL_BIST_FAILED 3 +#define MC_CMD_POLL_BIST_TIMEOUT 4 +#define MC_CMD_POLL_BIST_OUT_PRIVATE_OFST 4 + +/* MC_CMD_PHY_SPI: (variadic in, variadic out) + * Read/Write/Erase the PHY SPI device + * + * Locks required: PHY_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_PHY_SPI 0x27 +#define MC_CMD_PHY_SPI_IN_LEN(_write_bytes) (12 + (_write_bytes)) +#define MC_CMD_PHY_SPI_IN_ARGS_OFST 0 +#define MC_CMD_PHY_SPI_IN_ARGS_ADDR_OFST 0 +#define MC_CMD_PHY_SPI_IN_ARGS_READ_BYTES_OFST 4 +#define MC_CMD_PHY_SPI_IN_ARGS_ERASE_ALL_OFST 8 +/* Data to write here */ +#define MC_CMD_PHY_SPI_IN_WRITE_BUFFER_OFSET 12 +#define MC_CMD_PHY_SPI_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_PHY_SPI_OUT_READ_BUFFER_OFST 0 + + +/* MC_CMD_GET_LOOPBACK_MODES: + * Returns a bitmask of loopback modes evailable at each speed. + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_LOOPBACK_MODES 0x28 +#define MC_CMD_GET_LOOPBACK_MODES_IN_LEN 0 +#define MC_CMD_GET_LOOPBACK_MODES_OUT_LEN 32 +#define MC_CMD_GET_LOOPBACK_MODES_100M_OFST 0 +#define MC_CMD_GET_LOOPBACK_MODES_1G_OFST 8 +#define MC_CMD_GET_LOOPBACK_MODES_10G_OFST 16 +#define MC_CMD_GET_LOOPBACK_MODES_SUGGESTED_OFST 24 + +/* Flow control enumeration */ +#define MC_CMD_FCNTL_OFF 0 +#define MC_CMD_FCNTL_RESPOND 1 +#define MC_CMD_FCNTL_BIDIR 2 +/* Auto - Use what the link has autonegotiated + * - The driver should modify the advertised capabilities via SET_LINK.CAP + * to control the negotiated flow control mode. + * - Can only be set if the PHY supports PAUSE+ASYM capabilities + * - Never returned by GET_LINK as the value programmed into the MAC + */ +#define MC_CMD_FCNTL_AUTO 3 + +/* Generic mac fault bitmask */ +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_LBN 0 +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_WIDTH 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_LBN 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_WIDTH 1 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_LBN 2 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_WIDTH 1 + +/* MC_CMD_GET_LINK: + * Read the unified MAC/PHY link state + * + * Locks required: None + * Return code: 0, ETIME + */ +#define MC_CMD_GET_LINK 0x29 +#define MC_CMD_GET_LINK_IN_LEN 0 +#define MC_CMD_GET_LINK_OUT_LEN 28 +/* near-side and link-partner advertised capabilities */ +#define MC_CMD_GET_LINK_OUT_CAP_OFST 0 +#define MC_CMD_GET_LINK_OUT_LP_CAP_OFST 4 +/* Autonegotiated speed in mbit/s. The link may still be down + * even if this reads non-zero */ +#define MC_CMD_GET_LINK_OUT_LINK_SPEED_OFST 8 +#define MC_CMD_GET_LINK_OUT_LOOPBACK_MODE_OFST 12 +#define MC_CMD_GET_LINK_OUT_FLAGS_OFST 16 +/* Whether we have overall link up */ +#define MC_CMD_GET_LINK_LINK_UP_LBN 0 +#define MC_CMD_GET_LINK_LINK_UP_WIDTH 1 +#define MC_CMD_GET_LINK_FULL_DUPLEX_LBN 1 +#define MC_CMD_GET_LINK_FULL_DUPLEX_WIDTH 1 +/* Whether we have link at the layers provided by the BPX */ +#define MC_CMD_GET_LINK_BPX_LINK_LBN 2 +#define MC_CMD_GET_LINK_BPX_LINK_WIDTH 1 +/* Whether the PHY has external link */ +#define MC_CMD_GET_LINK_PHY_LINK_LBN 3 +#define MC_CMD_GET_LINK_PHY_LINK_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_FCNTL_OFST 20 +#define MC_CMD_GET_LINK_OUT_MAC_FAULT_OFST 24 + +/* MC_CMD_SET_LINK: + * Write the unified MAC/PHY link configuration + * + * A loopback speed of "0" is supported, and means + * (choose any available speed) + * + * Locks required: None + * Return code: 0, EINVAL, ETIME + */ +#define MC_CMD_SET_LINK 0x2a +#define MC_CMD_SET_LINK_IN_LEN 16 +#define MC_CMD_SET_LINK_IN_CAP_OFST 0 +#define MC_CMD_SET_LINK_IN_FLAGS_OFST 4 +#define MC_CMD_SET_LINK_LOWPOWER_LBN 0 +#define MC_CMD_SET_LINK_LOWPOWER_WIDTH 1 +#define MC_CMD_SET_LINK_POWEROFF_LBN 1 +#define MC_CMD_SET_LINK_POWEROFF_WIDTH 1 +#define MC_CMD_SET_LINK_TXDIS_LBN 2 +#define MC_CMD_SET_LINK_TXDIS_WIDTH 1 +#define MC_CMD_SET_LINK_IN_LOOPBACK_MODE_OFST 8 +#define MC_CMD_SET_LINK_IN_LOOPBACK_SPEED_OFST 12 +#define MC_CMD_SET_LINK_OUT_LEN 0 + +/* MC_CMD_SET_ID_LED: + * Set indentification LED state + * + * Locks required: None + * Return code: 0, EINVAL + */ +#define MC_CMD_SET_ID_LED 0x2b +#define MC_CMD_SET_ID_LED_IN_LEN 4 +#define MC_CMD_SET_ID_LED_IN_STATE_OFST 0 +#define MC_CMD_LED_OFF 0 +#define MC_CMD_LED_ON 1 +#define MC_CMD_LED_DEFAULT 2 +#define MC_CMD_SET_ID_LED_OUT_LEN 0 + +/* MC_CMD_SET_MAC: + * Set MAC configuration + * + * The MTU is the MTU programmed directly into the XMAC/GMAC + * (inclusive of EtherII, VLAN, bug16011 padding) + * + * Locks required: None + * Return code: 0, EINVAL + */ +#define MC_CMD_SET_MAC 0x2c +#define MC_CMD_SET_MAC_IN_LEN 24 +#define MC_CMD_SET_MAC_IN_MTU_OFST 0 +#define MC_CMD_SET_MAC_IN_DRAIN_OFST 4 +#define MC_CMD_SET_MAC_IN_ADDR_OFST 8 +#define MC_CMD_SET_MAC_IN_REJECT_OFST 16 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN 0 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_LBN 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_FCNTL_OFST 20 +#define MC_CMD_SET_MAC_OUT_LEN 0 + +/* MC_CMD_PHY_STATS: + * Get generic PHY statistics + * + * This call returns the statistics for a generic PHY, by direct DMA + * into host memory, in a sparse array (indexed by the enumerate). + * Each value is represented by a 32bit number. + * + * Locks required: None + * Returns: 0, ETIME + * Response methods: shared memory, event + */ +#define MC_CMD_PHY_STATS 0x2d +#define MC_CMD_PHY_STATS_IN_LEN 8 +#define MC_CMD_PHY_STATS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_PHY_STATS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_PHY_STATS_OUT_LEN 0 + +/* Unified MAC statistics enumeration */ +#define MC_CMD_MAC_GENERATION_START 0 +#define MC_CMD_MAC_TX_PKTS 1 +#define MC_CMD_MAC_TX_PAUSE_PKTS 2 +#define MC_CMD_MAC_TX_CONTROL_PKTS 3 +#define MC_CMD_MAC_TX_UNICAST_PKTS 4 +#define MC_CMD_MAC_TX_MULTICAST_PKTS 5 +#define MC_CMD_MAC_TX_BROADCAST_PKTS 6 +#define MC_CMD_MAC_TX_BYTES 7 +#define MC_CMD_MAC_TX_BAD_BYTES 8 +#define MC_CMD_MAC_TX_LT64_PKTS 9 +#define MC_CMD_MAC_TX_64_PKTS 10 +#define MC_CMD_MAC_TX_65_TO_127_PKTS 11 +#define MC_CMD_MAC_TX_128_TO_255_PKTS 12 +#define MC_CMD_MAC_TX_256_TO_511_PKTS 13 +#define MC_CMD_MAC_TX_512_TO_1023_PKTS 14 +#define MC_CMD_MAC_TX_1024_TO_15XX_PKTS 15 +#define MC_CMD_MAC_TX_15XX_TO_JUMBO_PKTS 16 +#define MC_CMD_MAC_TX_GTJUMBO_PKTS 17 +#define MC_CMD_MAC_TX_BAD_FCS_PKTS 18 +#define MC_CMD_MAC_TX_SINGLE_COLLISION_PKTS 19 +#define MC_CMD_MAC_TX_MULTIPLE_COLLISION_PKTS 20 +#define MC_CMD_MAC_TX_EXCESSIVE_COLLISION_PKTS 21 +#define MC_CMD_MAC_TX_LATE_COLLISION_PKTS 22 +#define MC_CMD_MAC_TX_DEFERRED_PKTS 23 +#define MC_CMD_MAC_TX_EXCESSIVE_DEFERRED_PKTS 24 +#define MC_CMD_MAC_TX_NON_TCPUDP_PKTS 25 +#define MC_CMD_MAC_TX_MAC_SRC_ERR_PKTS 26 +#define MC_CMD_MAC_TX_IP_SRC_ERR_PKTS 27 +#define MC_CMD_MAC_RX_PKTS 28 +#define MC_CMD_MAC_RX_PAUSE_PKTS 29 +#define MC_CMD_MAC_RX_GOOD_PKTS 30 +#define MC_CMD_MAC_RX_CONTROL_PKTS 31 +#define MC_CMD_MAC_RX_UNICAST_PKTS 32 +#define MC_CMD_MAC_RX_MULTICAST_PKTS 33 +#define MC_CMD_MAC_RX_BROADCAST_PKTS 34 +#define MC_CMD_MAC_RX_BYTES 35 +#define MC_CMD_MAC_RX_BAD_BYTES 36 +#define MC_CMD_MAC_RX_64_PKTS 37 +#define MC_CMD_MAC_RX_65_TO_127_PKTS 38 +#define MC_CMD_MAC_RX_128_TO_255_PKTS 39 +#define MC_CMD_MAC_RX_256_TO_511_PKTS 40 +#define MC_CMD_MAC_RX_512_TO_1023_PKTS 41 +#define MC_CMD_MAC_RX_1024_TO_15XX_PKTS 42 +#define MC_CMD_MAC_RX_15XX_TO_JUMBO_PKTS 43 +#define MC_CMD_MAC_RX_GTJUMBO_PKTS 44 +#define MC_CMD_MAC_RX_UNDERSIZE_PKTS 45 +#define MC_CMD_MAC_RX_BAD_FCS_PKTS 46 +#define MC_CMD_MAC_RX_OVERFLOW_PKTS 47 +#define MC_CMD_MAC_RX_FALSE_CARRIER_PKTS 48 +#define MC_CMD_MAC_RX_SYMBOL_ERROR_PKTS 49 +#define MC_CMD_MAC_RX_ALIGN_ERROR_PKTS 50 +#define MC_CMD_MAC_RX_LENGTH_ERROR_PKTS 51 +#define MC_CMD_MAC_RX_INTERNAL_ERROR_PKTS 52 +#define MC_CMD_MAC_RX_JABBER_PKTS 53 +#define MC_CMD_MAC_RX_NODESC_DROPS 54 +#define MC_CMD_MAC_RX_LANES01_CHAR_ERR 55 +#define MC_CMD_MAC_RX_LANES23_CHAR_ERR 56 +#define MC_CMD_MAC_RX_LANES01_DISP_ERR 57 +#define MC_CMD_MAC_RX_LANES23_DISP_ERR 58 +#define MC_CMD_MAC_RX_MATCH_FAULT 59 +/* Insert new members here. */ +#define MC_CMD_MAC_GENERATION_END 60 +#define MC_CMD_MAC_NSTATS (MC_CMD_MAC_GENERATION_END+1) + +/* MC_CMD_MAC_STATS: + * Get unified GMAC/XMAC statistics + * + * This call returns unified statistics maintained by the MC as it + * switches between the GMAC and XMAC. The MC will write out all + * supported stats. The driver should zero initialise the buffer to + * guarantee consistent results. + * + * Locks required: None + * Returns: 0 + * Response methods: shared memory, event + */ +#define MC_CMD_MAC_STATS 0x2e +#define MC_CMD_MAC_STATS_IN_LEN 16 +#define MC_CMD_MAC_STATS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_MAC_STATS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_MAC_STATS_IN_CMD_OFST 8 +#define MC_CMD_MAC_STATS_CMD_DMA_LBN 0 +#define MC_CMD_MAC_STATS_CMD_DMA_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_CLEAR_LBN 1 +#define MC_CMD_MAC_STATS_CMD_CLEAR_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE_LBN 2 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE_WIDTH 1 +/* Fields only relevent when PERIODIC_CHANGE is set */ +#define MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE_LBN 3 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR_LBN 4 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIOD_MS_LBN 16 +#define MC_CMD_MAC_STATS_CMD_PERIOD_MS_WIDTH 16 +#define MC_CMD_MAC_STATS_IN_DMA_LEN_OFST 12 + +#define MC_CMD_MAC_STATS_OUT_LEN 0 + +/* Callisto flags */ +#define MC_CMD_SFT9001_ROBUST_LBN 0 +#define MC_CMD_SFT9001_ROBUST_WIDTH 1 +#define MC_CMD_SFT9001_SHORT_REACH_LBN 1 +#define MC_CMD_SFT9001_SHORT_REACH_WIDTH 1 + +/* MC_CMD_SFT9001_GET: + * Read current callisto specific setting + * + * Locks required: None + * Returns: 0, ETIME + */ +#define MC_CMD_SFT9001_GET 0x30 +#define MC_CMD_SFT9001_GET_IN_LEN 0 +#define MC_CMD_SFT9001_GET_OUT_LEN 4 +#define MC_CMD_SFT9001_GET_OUT_FLAGS_OFST 0 + +/* MC_CMD_SFT9001_SET: + * Write current callisto specific setting + * + * Locks required: None + * Returns: 0, ETIME, EINVAL + */ +#define MC_CMD_SFT9001_SET 0x31 +#define MC_CMD_SFT9001_SET_IN_LEN 4 +#define MC_CMD_SFT9001_SET_IN_FLAGS_OFST 0 +#define MC_CMD_SFT9001_SET_OUT_LEN 0 + + +/* MC_CMD_WOL_FILTER_SET: + * Set a WoL filter + * + * Locks required: None + * Returns: 0, EBUSY, EINVAL, ENOSYS + */ +#define MC_CMD_WOL_FILTER_SET 0x32 +#define MC_CMD_WOL_FILTER_SET_IN_LEN 192 /* 190 rounded up to a word */ +#define MC_CMD_WOL_FILTER_SET_IN_FILTER_MODE_OFST 0 +#define MC_CMD_WOL_FILTER_SET_IN_WOL_TYPE_OFST 4 + +/* There is a union at offset 8, following defines overlap due to + * this */ +#define MC_CMD_WOL_FILTER_SET_IN_DATA_OFST 8 + +#define MC_CMD_WOL_FILTER_SET_IN_MAGIC_MAC_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_SRC_IP_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_DST_IP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 4) +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_SRC_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 8) +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_DST_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 10) + +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_SRC_IP_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_DST_IP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 16) +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_SRC_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 32) +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_DST_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 34) + +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_MASK_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 48) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LEN_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 176) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LAYER3_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 177) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LAYER4_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 178) + +#define MC_CMD_WOL_FILTER_SET_OUT_LEN 4 +#define MC_CMD_WOL_FILTER_SET_OUT_FILTER_ID_OFST 0 + +/* WOL Filter types enumeration */ +#define MC_CMD_WOL_TYPE_MAGIC 0x0 + /* unused 0x1 */ +#define MC_CMD_WOL_TYPE_WIN_MAGIC 0x2 +#define MC_CMD_WOL_TYPE_IPV4_SYN 0x3 +#define MC_CMD_WOL_TYPE_IPV6_SYN 0x4 +#define MC_CMD_WOL_TYPE_BITMAP 0x5 +#define MC_CMD_WOL_TYPE_MAX 0x6 + +#define MC_CMD_FILTER_MODE_SIMPLE 0x0 +#define MC_CMD_FILTER_MODE_STRUCTURED 0xffffffff + +/* MC_CMD_WOL_FILTER_REMOVE: + * Remove a WoL filter + * + * Locks required: None + * Returns: 0, EINVAL, ENOSYS + */ +#define MC_CMD_WOL_FILTER_REMOVE 0x33 +#define MC_CMD_WOL_FILTER_REMOVE_IN_LEN 4 +#define MC_CMD_WOL_FILTER_REMOVE_IN_FILTER_ID_OFST 0 +#define MC_CMD_WOL_FILTER_REMOVE_OUT_LEN 0 + + +/* MC_CMD_WOL_FILTER_RESET: + * Reset (i.e. remove all) WoL filters + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_WOL_FILTER_RESET 0x34 +#define MC_CMD_WOL_FILTER_RESET_IN_LEN 0 +#define MC_CMD_WOL_FILTER_RESET_OUT_LEN 0 + +/* MC_CMD_SET_MCAST_HASH: + * Set the MCASH hash value without otherwise + * reconfiguring the MAC + */ +#define MC_CMD_SET_MCAST_HASH 0x35 +#define MC_CMD_SET_MCAST_HASH_IN_LEN 32 +#define MC_CMD_SET_MCAST_HASH_IN_HASH0_OFST 0 +#define MC_CMD_SET_MCAST_HASH_IN_HASH1_OFST 16 +#define MC_CMD_SET_MCAST_HASH_OUT_LEN 0 + +/* MC_CMD_NVRAM_TYPES: + * Return bitfield indicating available types of virtual NVRAM partitions + * + * Locks required: none + * Returns: 0 + */ +#define MC_CMD_NVRAM_TYPES 0x36 +#define MC_CMD_NVRAM_TYPES_IN_LEN 0 +#define MC_CMD_NVRAM_TYPES_OUT_LEN 4 +#define MC_CMD_NVRAM_TYPES_OUT_TYPES_OFST 0 + +/* Supported NVRAM types */ +#define MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO 0 +#define MC_CMD_NVRAM_TYPE_MC_FW 1 +#define MC_CMD_NVRAM_TYPE_MC_FW_BACKUP 2 +#define MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 3 +#define MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1 4 +#define MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 5 +#define MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1 6 +#define MC_CMD_NVRAM_TYPE_EXP_ROM 7 +#define MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0 8 +#define MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1 9 +#define MC_CMD_NVRAM_TYPE_PHY_PORT0 10 +#define MC_CMD_NVRAM_TYPE_PHY_PORT1 11 +#define MC_CMD_NVRAM_TYPE_LOG 12 + +/* MC_CMD_NVRAM_INFO: + * Read info about a virtual NVRAM partition + * + * Locks required: none + * Returns: 0, EINVAL (bad type) + */ +#define MC_CMD_NVRAM_INFO 0x37 +#define MC_CMD_NVRAM_INFO_IN_LEN 4 +#define MC_CMD_NVRAM_INFO_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_INFO_OUT_LEN 24 +#define MC_CMD_NVRAM_INFO_OUT_TYPE_OFST 0 +#define MC_CMD_NVRAM_INFO_OUT_SIZE_OFST 4 +#define MC_CMD_NVRAM_INFO_OUT_ERASESIZE_OFST 8 +#define MC_CMD_NVRAM_INFO_OUT_FLAGS_OFST 12 +#define MC_CMD_NVRAM_PROTECTED_LBN 0 +#define MC_CMD_NVRAM_PROTECTED_WIDTH 1 +#define MC_CMD_NVRAM_INFO_OUT_PHYSDEV_OFST 16 +#define MC_CMD_NVRAM_INFO_OUT_PHYSADDR_OFST 20 + +/* MC_CMD_NVRAM_UPDATE_START: + * Start a group of update operations on a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_UPDATE_START 0x38 +#define MC_CMD_NVRAM_UPDATE_START_IN_LEN 4 +#define MC_CMD_NVRAM_UPDATE_START_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_UPDATE_START_OUT_LEN 0 + +/* MC_CMD_NVRAM_READ: + * Read data from a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_READ 0x39 +#define MC_CMD_NVRAM_READ_IN_LEN 12 +#define MC_CMD_NVRAM_READ_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_READ_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_READ_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_READ_OUT_LEN(_read_bytes) (_read_bytes) +#define MC_CMD_NVRAM_READ_OUT_READ_BUFFER_OFST 0 + +/* MC_CMD_NVRAM_WRITE: + * Write data to a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_WRITE 0x3a +#define MC_CMD_NVRAM_WRITE_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_WRITE_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_WRITE_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_OFST 12 +#define MC_CMD_NVRAM_WRITE_IN_LEN(_write_bytes) (12 + _write_bytes) +#define MC_CMD_NVRAM_WRITE_OUT_LEN 0 + +/* MC_CMD_NVRAM_ERASE: + * Erase sector(s) from a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_ERASE 0x3b +#define MC_CMD_NVRAM_ERASE_IN_LEN 12 +#define MC_CMD_NVRAM_ERASE_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_ERASE_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_ERASE_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_ERASE_OUT_LEN 0 + +/* MC_CMD_NVRAM_UPDATE_FINISH: + * Finish a group of update operations on a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_UPDATE_FINISH 0x3c +#define MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN 4 +#define MC_CMD_NVRAM_UPDATE_FINISH_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN 0 + +/* MC_CMD_REBOOT: + * Reboot the MC. The AFTER_ASSERTION flag is intended to be used + * when the driver notices an assertion failure, to allow two ports to + * both recover (semi-)gracefully. + * + * Locks required: NONE + * Returns: Nothing. You get back a response with ERR=1, DATALEN=0 + */ +#define MC_CMD_REBOOT 0x3d +#define MC_CMD_REBOOT_IN_LEN 4 +#define MC_CMD_REBOOT_IN_FLAGS_OFST 0 +#define MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION 1 +#define MC_CMD_REBOOT_OUT_LEN 0 + +/* MC_CMD_SCHEDINFO: + * Request scheduler info. from the MC. + * + * Locks required: NONE + * Returns: An array of (timeslice,maximum overrun), one for each thread, + * in ascending order of thread address.s + */ +#define MC_CMD_SCHEDINFO 0x3e +#define MC_CMD_SCHEDINFO_IN_LEN 0 + + +/* MC_CMD_SET_REBOOT_MODE: (debug) + * Set the mode for the next MC reboot. + * + * Locks required: NONE + * + * Sets the reboot mode to the specified value. Returns the old mode. + */ +#define MC_CMD_REBOOT_MODE 0x3f +#define MC_CMD_REBOOT_MODE_IN_LEN 4 +#define MC_CMD_REBOOT_MODE_IN_VALUE_OFST 0 +#define MC_CMD_REBOOT_MODE_OUT_LEN 4 +#define MC_CMD_REBOOT_MODE_OUT_VALUE_OFST 0 +#define MC_CMD_REBOOT_MODE_NORMAL 0 +#define MC_CMD_REBOOT_MODE_SNAPPER 3 + +/* MC_CMD_DEBUG_LOG: + * Null request/response command (debug) + * - sequence number is always zero + * - only supported on the UART interface + * (the same set of bytes is delivered as an + * event over PCI) + */ +#define MC_CMD_DEBUG_LOG 0x40 +#define MC_CMD_DEBUG_LOG_IN_LEN 0 +#define MC_CMD_DEBUG_LOG_OUT_LEN 0 + +/* Generic sensor enumeration. Note that a dual port NIC + * will EITHER expose PHY_COMMON_TEMP OR PHY0_TEMP and + * PHY1_TEMP depending on whether there is a single sensor + * in the vicinity of the two port, or one per port. + */ +#define MC_CMD_SENSOR_CONTROLLER_TEMP 0 /* degC */ +#define MC_CMD_SENSOR_PHY_COMMON_TEMP 1 /* degC */ +#define MC_CMD_SENSOR_CONTROLLER_COOLING 2 /* bool */ +#define MC_CMD_SENSOR_PHY0_TEMP 3 /* degC */ +#define MC_CMD_SENSOR_PHY0_COOLING 4 /* bool */ +#define MC_CMD_SENSOR_PHY1_TEMP 5 /* degC */ +#define MC_CMD_SENSOR_PHY1_COOLING 6 /* bool */ +#define MC_CMD_SENSOR_IN_1V0 7 /* mV */ +#define MC_CMD_SENSOR_IN_1V2 8 /* mV */ +#define MC_CMD_SENSOR_IN_1V8 9 /* mV */ +#define MC_CMD_SENSOR_IN_2V5 10 /* mV */ +#define MC_CMD_SENSOR_IN_3V3 11 /* mV */ +#define MC_CMD_SENSOR_IN_12V0 12 /* mV */ + + +/* Sensor state */ +#define MC_CMD_SENSOR_STATE_OK 0 +#define MC_CMD_SENSOR_STATE_WARNING 1 +#define MC_CMD_SENSOR_STATE_FATAL 2 +#define MC_CMD_SENSOR_STATE_BROKEN 3 + +/* MC_CMD_SENSOR_INFO: + * Returns information about every available sensor. + * + * Each sensor has a single (16bit) value, and a corresponding state. + * The mapping between value and sensor is nominally determined by the + * MC, but in practise is implemented as zero (BROKEN), one (TEMPERATURE), + * or two (VOLTAGE) ranges per sensor per state. + * + * This call returns a mask (32bit) of the sensors that are supported + * by this platform, then an array (indexed by MC_CMD_SENSOR) of byte + * offsets to the per-sensor arrays. Each sensor array has four 16bit + * numbers, min1, max1, min2, max2. + * + * Locks required: None + * Returns: 0 + */ +#define MC_CMD_SENSOR_INFO 0x41 +#define MC_CMD_SENSOR_INFO_IN_LEN 0 +#define MC_CMD_SENSOR_INFO_OUT_MASK_OFST 0 +#define MC_CMD_SENSOR_INFO_OUT_OFFSET_OFST(_x) \ + (4 + (_x)) +#define MC_CMD_SENSOR_INFO_OUT_MIN1_OFST(_ofst) \ + ((_ofst) + 0) +#define MC_CMD_SENSOR_INFO_OUT_MAX1_OFST(_ofst) \ + ((_ofst) + 2) +#define MC_CMD_SENSOR_INFO_OUT_MIN2_OFST(_ofst) \ + ((_ofst) + 4) +#define MC_CMD_SENSOR_INFO_OUT_MAX2_OFST(_ofst) \ + ((_ofst) + 6) + +/* MC_CMD_READ_SENSORS + * Returns the current (value, state) for each sensor + * + * Returns the current (value, state) [each 16bit] of each sensor supported by + * this board, by DMA'ing a sparse array (indexed by the sensor type) into host + * memory. + * + * The MC will send a SENSOREVT event every time any sensor changes state. The + * driver is responsible for ensuring that it doesn't miss any events. The board + * will function normally if all sensors are in STATE_OK or state_WARNING. + * Otherwise the board should not be expected to function. + */ +#define MC_CMD_READ_SENSORS 0x42 +#define MC_CMD_READ_SENSORS_IN_LEN 8 +#define MC_CMD_READ_SENSORS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_READ_SENSORS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_READ_SENSORS_OUT_LEN 0 + + +/* MC_CMD_GET_PHY_STATE: + * Report current state of PHY. A "zombie" PHY is a PHY that has failed to + * boot (e.g. due to missing or corrupted firmware). + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_PHY_STATE 0x43 + +#define MC_CMD_GET_PHY_STATE_IN_LEN 0 +#define MC_CMD_GET_PHY_STATE_OUT_LEN 4 +#define MC_CMD_GET_PHY_STATE_STATE_OFST 0 +/* PHY state enumeration: */ +#define MC_CMD_PHY_STATE_OK 1 +#define MC_CMD_PHY_STATE_ZOMBIE 2 + + +/* 802.1Qbb control. 8 Tx queues that map to priorities 0 - 7. Use all 1s to + * disable 802.Qbb for a given priority. */ +#define MC_CMD_SETUP_8021QBB 0x44 +#define MC_CMD_SETUP_8021QBB_IN_LEN 32 +#define MC_CMD_SETUP_8021QBB_OUT_LEN 0 +#define MC_CMD_SETUP_8021QBB_IN_TXQS_OFFST 0 + + +/* MC_CMD_WOL_FILTER_GET: + * Retrieve ID of any WoL filters + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_WOL_FILTER_GET 0x45 +#define MC_CMD_WOL_FILTER_GET_IN_LEN 0 +#define MC_CMD_WOL_FILTER_GET_OUT_LEN 4 +#define MC_CMD_WOL_FILTER_GET_OUT_FILTER_ID_OFST 0 + + +/* MC_CMD_ADD_LIGHTSOUT_OFFLOAD: + * Offload a protocol to NIC for lights-out state + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD 0x46 + +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_LEN 16 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0 + +/* There is a union at offset 4, following defines overlap due to + * this */ +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_DATA_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARPMAC_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARPIP_OFST 10 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSMAC_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSSNIPV6_OFST 10 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSIPV6_OFST 26 + +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID_OFST 0 + + +/* MC_CMD_REMOVE_LIGHTSOUT_PROTOCOL_OFFLOAD: + * Offload a protocol to NIC for lights-out state + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD 0x47 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN 8 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN 0 + +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID_OFST 4 + +/* Lights-out offload protocols enumeration */ +#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP 0x1 +#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS 0x2 + + +/* MC_CMD_MAC_RESET_RESTORE: + * Restore MAC after block reset + * + * Locks required: None + * Returns: 0 + */ + +#define MC_CMD_MAC_RESET_RESTORE 0x48 +#define MC_CMD_MAC_RESET_RESTORE_IN_LEN 0 +#define MC_CMD_MAC_RESET_RESTORE_OUT_LEN 0 + +#endif /* MCDI_PCOL_H */ diff --git a/drivers/net/sfc/mcdi_phy.c b/drivers/net/sfc/mcdi_phy.c new file mode 100644 index 000000000000..0e1bcc5a0d52 --- /dev/null +++ b/drivers/net/sfc/mcdi_phy.c @@ -0,0 +1,597 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +/* + * Driver for PHY related operations via MCDI. + */ + +#include "efx.h" +#include "phy.h" +#include "mcdi.h" +#include "mcdi_pcol.h" +#include "mdio_10g.h" + +struct efx_mcdi_phy_cfg { + u32 flags; + u32 type; + u32 supported_cap; + u32 channel; + u32 port; + u32 stats_mask; + u8 name[20]; + u32 media; + u32 mmd_mask; + u8 revision[20]; + u32 forced_cap; +}; + +static int +efx_mcdi_get_phy_cfg(struct efx_nic *efx, struct efx_mcdi_phy_cfg *cfg) +{ + u8 outbuf[MC_CMD_GET_PHY_CFG_OUT_LEN]; + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_IN_LEN != 0); + BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_OUT_NAME_LEN != sizeof(cfg->name)); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_CFG, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_PHY_CFG_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + cfg->flags = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_FLAGS); + cfg->type = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_TYPE); + cfg->supported_cap = + MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_SUPPORTED_CAP); + cfg->channel = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_CHANNEL); + cfg->port = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_PRT); + cfg->stats_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_STATS_MASK); + memcpy(cfg->name, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_NAME), + sizeof(cfg->name)); + cfg->media = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MEDIA_TYPE); + cfg->mmd_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MMD_MASK); + memcpy(cfg->revision, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_REVISION), + sizeof(cfg->revision)); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static int efx_mcdi_set_link(struct efx_nic *efx, u32 capabilities, + u32 flags, u32 loopback_mode, + u32 loopback_speed) +{ + u8 inbuf[MC_CMD_SET_LINK_IN_LEN]; + int rc; + + BUILD_BUG_ON(MC_CMD_SET_LINK_OUT_LEN != 0); + + MCDI_SET_DWORD(inbuf, SET_LINK_IN_CAP, capabilities); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_FLAGS, flags); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_MODE, loopback_mode); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_SPEED, loopback_speed); + + rc = efx_mcdi_rpc(efx, MC_CMD_SET_LINK, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static int efx_mcdi_loopback_modes(struct efx_nic *efx, u64 *loopback_modes) +{ + u8 outbuf[MC_CMD_GET_LOOPBACK_MODES_OUT_LEN]; + size_t outlen; + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LOOPBACK_MODES, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_LOOPBACK_MODES_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *loopback_modes = MCDI_QWORD(outbuf, GET_LOOPBACK_MODES_SUGGESTED); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, u16 addr, + u16 *value_out, u32 *status_out) +{ + u8 inbuf[MC_CMD_MDIO_READ_IN_LEN]; + u8 outbuf[MC_CMD_MDIO_READ_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, bus); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr); + + rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + *value_out = (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE); + *status_out = MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, u16 addr, + u16 value, u32 *status_out) +{ + u8 inbuf[MC_CMD_MDIO_WRITE_IN_LEN]; + u8 outbuf[MC_CMD_MDIO_WRITE_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, bus); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value); + + rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + *status_out = MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static u32 mcdi_to_ethtool_cap(u32 media, u32 cap) +{ + u32 result = 0; + + switch (media) { + case MC_CMD_MEDIA_KX4: + result |= SUPPORTED_Backplane; + if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) + result |= SUPPORTED_1000baseKX_Full; + if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) + result |= SUPPORTED_10000baseKX4_Full; + break; + + case MC_CMD_MEDIA_XFP: + case MC_CMD_MEDIA_SFP_PLUS: + result |= SUPPORTED_FIBRE; + break; + + case MC_CMD_MEDIA_BASE_T: + result |= SUPPORTED_TP; + if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN)) + result |= SUPPORTED_10baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN)) + result |= SUPPORTED_10baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN)) + result |= SUPPORTED_100baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN)) + result |= SUPPORTED_100baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN)) + result |= SUPPORTED_1000baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) + result |= SUPPORTED_1000baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) + result |= SUPPORTED_10000baseT_Full; + break; + } + + if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) + result |= SUPPORTED_Pause; + if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) + result |= SUPPORTED_Asym_Pause; + if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) + result |= SUPPORTED_Autoneg; + + return result; +} + +static u32 ethtool_to_mcdi_cap(u32 cap) +{ + u32 result = 0; + + if (cap & SUPPORTED_10baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN); + if (cap & SUPPORTED_10baseT_Full) + result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN); + if (cap & SUPPORTED_100baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN); + if (cap & SUPPORTED_100baseT_Full) + result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN); + if (cap & SUPPORTED_1000baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN); + if (cap & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseKX_Full)) + result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN); + if (cap & (SUPPORTED_10000baseT_Full | SUPPORTED_10000baseKX4_Full)) + result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN); + if (cap & SUPPORTED_Pause) + result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN); + if (cap & SUPPORTED_Asym_Pause) + result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN); + if (cap & SUPPORTED_Autoneg) + result |= (1 << MC_CMD_PHY_CAP_AN_LBN); + + return result; +} + +static u32 efx_get_mcdi_phy_flags(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + enum efx_phy_mode mode, supported; + u32 flags; + + /* TODO: Advertise the capabilities supported by this PHY */ + supported = 0; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_TXDIS_LBN)) + supported |= PHY_MODE_TX_DISABLED; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_LOWPOWER_LBN)) + supported |= PHY_MODE_LOW_POWER; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_POWEROFF_LBN)) + supported |= PHY_MODE_OFF; + + mode = efx->phy_mode & supported; + + flags = 0; + if (mode & PHY_MODE_TX_DISABLED) + flags |= (1 << MC_CMD_SET_LINK_TXDIS_LBN); + if (mode & PHY_MODE_LOW_POWER) + flags |= (1 << MC_CMD_SET_LINK_LOWPOWER_LBN); + if (mode & PHY_MODE_OFF) + flags |= (1 << MC_CMD_SET_LINK_POWEROFF_LBN); + + return flags; +} + +static u32 mcdi_to_ethtool_media(u32 media) +{ + switch (media) { + case MC_CMD_MEDIA_XAUI: + case MC_CMD_MEDIA_CX4: + case MC_CMD_MEDIA_KX4: + return PORT_OTHER; + + case MC_CMD_MEDIA_XFP: + case MC_CMD_MEDIA_SFP_PLUS: + return PORT_FIBRE; + + case MC_CMD_MEDIA_BASE_T: + return PORT_TP; + + default: + return PORT_OTHER; + } +} + +static int efx_mcdi_phy_probe(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_cfg; + int rc; + + /* TODO: Move phy_data initialisation to + * phy_op->probe/remove, rather than init/fini */ + phy_cfg = kzalloc(sizeof(*phy_cfg), GFP_KERNEL); + if (phy_cfg == NULL) { + rc = -ENOMEM; + goto fail_alloc; + } + rc = efx_mcdi_get_phy_cfg(efx, phy_cfg); + if (rc != 0) + goto fail; + + efx->phy_type = phy_cfg->type; + + efx->mdio_bus = phy_cfg->channel; + efx->mdio.prtad = phy_cfg->port; + efx->mdio.mmds = phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22); + efx->mdio.mode_support = 0; + if (phy_cfg->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22)) + efx->mdio.mode_support |= MDIO_SUPPORTS_C22; + if (phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22)) + efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + + /* Assert that we can map efx -> mcdi loopback modes */ + BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE); + BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA); + BUILD_BUG_ON(LOOPBACK_GMAC != MC_CMD_LOOPBACK_GMAC); + BUILD_BUG_ON(LOOPBACK_XGMII != MC_CMD_LOOPBACK_XGMII); + BUILD_BUG_ON(LOOPBACK_XGXS != MC_CMD_LOOPBACK_XGXS); + BUILD_BUG_ON(LOOPBACK_XAUI != MC_CMD_LOOPBACK_XAUI); + BUILD_BUG_ON(LOOPBACK_GMII != MC_CMD_LOOPBACK_GMII); + BUILD_BUG_ON(LOOPBACK_SGMII != MC_CMD_LOOPBACK_SGMII); + BUILD_BUG_ON(LOOPBACK_XGBR != MC_CMD_LOOPBACK_XGBR); + BUILD_BUG_ON(LOOPBACK_XFI != MC_CMD_LOOPBACK_XFI); + BUILD_BUG_ON(LOOPBACK_XAUI_FAR != MC_CMD_LOOPBACK_XAUI_FAR); + BUILD_BUG_ON(LOOPBACK_GMII_FAR != MC_CMD_LOOPBACK_GMII_FAR); + BUILD_BUG_ON(LOOPBACK_SGMII_FAR != MC_CMD_LOOPBACK_SGMII_FAR); + BUILD_BUG_ON(LOOPBACK_XFI_FAR != MC_CMD_LOOPBACK_XFI_FAR); + BUILD_BUG_ON(LOOPBACK_GPHY != MC_CMD_LOOPBACK_GPHY); + BUILD_BUG_ON(LOOPBACK_PHYXS != MC_CMD_LOOPBACK_PHYXS); + BUILD_BUG_ON(LOOPBACK_PCS != MC_CMD_LOOPBACK_PCS); + BUILD_BUG_ON(LOOPBACK_PMAPMD != MC_CMD_LOOPBACK_PMAPMD); + BUILD_BUG_ON(LOOPBACK_XPORT != MC_CMD_LOOPBACK_XPORT); + BUILD_BUG_ON(LOOPBACK_XGMII_WS != MC_CMD_LOOPBACK_XGMII_WS); + BUILD_BUG_ON(LOOPBACK_XAUI_WS != MC_CMD_LOOPBACK_XAUI_WS); + BUILD_BUG_ON(LOOPBACK_XAUI_WS_FAR != MC_CMD_LOOPBACK_XAUI_WS_FAR); + BUILD_BUG_ON(LOOPBACK_XAUI_WS_NEAR != MC_CMD_LOOPBACK_XAUI_WS_NEAR); + BUILD_BUG_ON(LOOPBACK_GMII_WS != MC_CMD_LOOPBACK_GMII_WS); + BUILD_BUG_ON(LOOPBACK_XFI_WS != MC_CMD_LOOPBACK_XFI_WS); + BUILD_BUG_ON(LOOPBACK_XFI_WS_FAR != MC_CMD_LOOPBACK_XFI_WS_FAR); + BUILD_BUG_ON(LOOPBACK_PHYXS_WS != MC_CMD_LOOPBACK_PHYXS_WS); + + rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes); + if (rc != 0) + goto fail; + /* The MC indicates that LOOPBACK_NONE is a valid loopback mode, + * but by convention we don't */ + efx->loopback_modes &= ~(1 << LOOPBACK_NONE); + + kfree(phy_cfg); + + return 0; + +fail: + kfree(phy_cfg); +fail_alloc: + return rc; +} + +static int efx_mcdi_phy_init(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_data; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + u32 caps; + int rc; + + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (phy_data == NULL) + return -ENOMEM; + + rc = efx_mcdi_get_phy_cfg(efx, phy_data); + if (rc != 0) + goto fail; + + efx->phy_data = phy_data; + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) + goto fail; + + caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP); + if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN)) + efx->link_advertising = + mcdi_to_ethtool_cap(phy_data->media, caps); + else + phy_data->forced_cap = caps; + + return 0; + +fail: + kfree(phy_data); + return rc; +} + +int efx_mcdi_phy_reconfigure(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 caps = (efx->link_advertising ? + ethtool_to_mcdi_cap(efx->link_advertising) : + phy_cfg->forced_cap); + + return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), + efx->loopback_mode, 0); +} + +void efx_mcdi_phy_decode_link(struct efx_nic *efx, + struct efx_link_state *link_state, + u32 speed, u32 flags, u32 fcntl) +{ + switch (fcntl) { + case MC_CMD_FCNTL_AUTO: + WARN_ON(1); /* This is not a link mode */ + link_state->fc = EFX_FC_AUTO | EFX_FC_TX | EFX_FC_RX; + break; + case MC_CMD_FCNTL_BIDIR: + link_state->fc = EFX_FC_TX | EFX_FC_RX; + break; + case MC_CMD_FCNTL_RESPOND: + link_state->fc = EFX_FC_RX; + break; + default: + WARN_ON(1); + case MC_CMD_FCNTL_OFF: + link_state->fc = 0; + break; + } + + link_state->up = !!(flags & (1 << MC_CMD_GET_LINK_LINK_UP_LBN)); + link_state->fd = !!(flags & (1 << MC_CMD_GET_LINK_FULL_DUPLEX_LBN)); + link_state->speed = speed; +} + +/* Verify that the forced flow control settings (!EFX_FC_AUTO) are + * supported by the link partner. Warn the user if this isn't the case + */ +void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 rmtadv; + + /* The link partner capabilities are only relevent if the + * link supports flow control autonegotiation */ + if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) + return; + + /* If flow control autoneg is supported and enabled, then fine */ + if (efx->wanted_fc & EFX_FC_AUTO) + return; + + rmtadv = 0; + if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) + rmtadv |= ADVERTISED_Pause; + if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) + rmtadv |= ADVERTISED_Asym_Pause; + + if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause) + EFX_ERR(efx, "warning: link partner doesn't support " + "pause frames"); +} + +static bool efx_mcdi_phy_poll(struct efx_nic *efx) +{ + struct efx_link_state old_state = efx->link_state; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + int rc; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) { + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + efx->link_state.up = false; + } else { + efx_mcdi_phy_decode_link( + efx, &efx->link_state, + MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED), + MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS), + MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL)); + } + + return !efx_link_state_equal(&efx->link_state, &old_state); +} + +static void efx_mcdi_phy_fini(struct efx_nic *efx) +{ + struct efx_mcdi_phy_data *phy_data = efx->phy_data; + + efx->phy_data = NULL; + kfree(phy_data); +} + +static void efx_mcdi_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + int rc; + + ecmd->supported = + mcdi_to_ethtool_cap(phy_cfg->media, phy_cfg->supported_cap); + ecmd->advertising = efx->link_advertising; + ecmd->speed = efx->link_state.speed; + ecmd->duplex = efx->link_state.fd; + ecmd->port = mcdi_to_ethtool_media(phy_cfg->media); + ecmd->phy_address = phy_cfg->port; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->autoneg = !!(efx->link_advertising & ADVERTISED_Autoneg); + ecmd->mdio_support = (efx->mdio.mode_support & + (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22)); + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) { + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return; + } + ecmd->lp_advertising = + mcdi_to_ethtool_cap(phy_cfg->media, + MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP)); +} + +static int efx_mcdi_phy_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 caps; + int rc; + + if (ecmd->autoneg) { + caps = (ethtool_to_mcdi_cap(ecmd->advertising) | + 1 << MC_CMD_PHY_CAP_AN_LBN); + } else if (ecmd->duplex) { + switch (ecmd->speed) { + case 10: caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN; break; + case 100: caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN; break; + case 1000: caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN; break; + case 10000: caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN; break; + default: return -EINVAL; + } + } else { + switch (ecmd->speed) { + case 10: caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN; break; + case 100: caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN; break; + case 1000: caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN; break; + default: return -EINVAL; + } + } + + rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), + efx->loopback_mode, 0); + if (rc) + return rc; + + if (ecmd->autoneg) { + efx_link_set_advertising( + efx, ecmd->advertising | ADVERTISED_Autoneg); + phy_cfg->forced_cap = 0; + } else { + efx_link_set_advertising(efx, 0); + phy_cfg->forced_cap = caps; + } + return 0; +} + +struct efx_phy_operations efx_mcdi_phy_ops = { + .probe = efx_mcdi_phy_probe, + .init = efx_mcdi_phy_init, + .reconfigure = efx_mcdi_phy_reconfigure, + .poll = efx_mcdi_phy_poll, + .fini = efx_mcdi_phy_fini, + .get_settings = efx_mcdi_phy_get_settings, + .set_settings = efx_mcdi_phy_set_settings, + .run_tests = NULL, + .test_name = NULL, +}; diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c index 6c33459f9ea9..1574e52f0594 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -14,8 +14,8 @@ #include <linux/delay.h> #include "net_driver.h" #include "mdio_10g.h" -#include "boards.h" #include "workarounds.h" +#include "nic.h" unsigned efx_mdio_id_oui(u32 id) { @@ -174,7 +174,7 @@ bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask) * of mmd's */ if (LOOPBACK_INTERNAL(efx)) return true; - else if (efx->loopback_mode == LOOPBACK_NETWORK) + else if (LOOPBACK_MASK(efx) & LOOPBACKS_WS) return false; else if (efx_phy_mode_disabled(efx->phy_mode)) return false; @@ -211,7 +211,7 @@ void efx_mdio_phy_reconfigure(struct efx_nic *efx) efx->loopback_mode == LOOPBACK_PCS); efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, MDIO_CTRL1, MDIO_PHYXS_CTRL1_LOOPBACK, - efx->loopback_mode == LOOPBACK_NETWORK); + efx->loopback_mode == LOOPBACK_PHYXS_WS); } static void efx_mdio_set_mmd_lpower(struct efx_nic *efx, @@ -249,8 +249,6 @@ void efx_mdio_set_mmds_lpower(struct efx_nic *efx, int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) { struct ethtool_cmd prev; - u32 required; - int reg; efx->phy_op->get_settings(efx, &prev); @@ -266,86 +264,74 @@ int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) return -EINVAL; /* Check that PHY supports these settings */ - if (ecmd->autoneg) { - required = SUPPORTED_Autoneg; - } else if (ecmd->duplex) { - switch (ecmd->speed) { - case SPEED_10: required = SUPPORTED_10baseT_Full; break; - case SPEED_100: required = SUPPORTED_100baseT_Full; break; - default: return -EINVAL; - } - } else { - switch (ecmd->speed) { - case SPEED_10: required = SUPPORTED_10baseT_Half; break; - case SPEED_100: required = SUPPORTED_100baseT_Half; break; - default: return -EINVAL; - } - } - required |= ecmd->advertising; - if (required & ~prev.supported) + if (!ecmd->autoneg || + (ecmd->advertising | SUPPORTED_Autoneg) & ~prev.supported) return -EINVAL; - if (ecmd->autoneg) { - bool xnp = (ecmd->advertising & ADVERTISED_10000baseT_Full - || EFX_WORKAROUND_13204(efx)); - - /* Set up the base page */ - reg = ADVERTISE_CSMA; - if (ecmd->advertising & ADVERTISED_10baseT_Half) - reg |= ADVERTISE_10HALF; - if (ecmd->advertising & ADVERTISED_10baseT_Full) - reg |= ADVERTISE_10FULL; - if (ecmd->advertising & ADVERTISED_100baseT_Half) - reg |= ADVERTISE_100HALF; - if (ecmd->advertising & ADVERTISED_100baseT_Full) - reg |= ADVERTISE_100FULL; - if (xnp) - reg |= ADVERTISE_RESV; - else if (ecmd->advertising & (ADVERTISED_1000baseT_Half | - ADVERTISED_1000baseT_Full)) - reg |= ADVERTISE_NPAGE; - reg |= mii_advertise_flowctrl(efx->wanted_fc); - efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); - - /* Set up the (extended) next page if necessary */ - if (efx->phy_op->set_npage_adv) - efx->phy_op->set_npage_adv(efx, ecmd->advertising); - - /* Enable and restart AN */ - reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1); - reg |= MDIO_AN_CTRL1_ENABLE; - if (!(EFX_WORKAROUND_15195(efx) && - LOOPBACK_MASK(efx) & efx->phy_op->loopbacks)) - reg |= MDIO_AN_CTRL1_RESTART; - if (xnp) - reg |= MDIO_AN_CTRL1_XNP; - else - reg &= ~MDIO_AN_CTRL1_XNP; - efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg); - } else { - /* Disable AN */ - efx_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_CTRL1, - MDIO_AN_CTRL1_ENABLE, false); - - /* Set the basic control bits */ - reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1); - reg &= ~(MDIO_CTRL1_SPEEDSEL | MDIO_CTRL1_FULLDPLX); - if (ecmd->speed == SPEED_100) - reg |= MDIO_PMA_CTRL1_SPEED100; - if (ecmd->duplex) - reg |= MDIO_CTRL1_FULLDPLX; - efx_mdio_write(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, reg); - } - + efx_link_set_advertising(efx, ecmd->advertising | ADVERTISED_Autoneg); + efx_mdio_an_reconfigure(efx); return 0; } +/** + * efx_mdio_an_reconfigure - Push advertising flags and restart autonegotiation + * @efx: Efx NIC + */ +void efx_mdio_an_reconfigure(struct efx_nic *efx) +{ + bool xnp = (efx->link_advertising & ADVERTISED_10000baseT_Full + || EFX_WORKAROUND_13204(efx)); + int reg; + + WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); + + /* Set up the base page */ + reg = ADVERTISE_CSMA; + if (efx->link_advertising & ADVERTISED_10baseT_Half) + reg |= ADVERTISE_10HALF; + if (efx->link_advertising & ADVERTISED_10baseT_Full) + reg |= ADVERTISE_10FULL; + if (efx->link_advertising & ADVERTISED_100baseT_Half) + reg |= ADVERTISE_100HALF; + if (efx->link_advertising & ADVERTISED_100baseT_Full) + reg |= ADVERTISE_100FULL; + if (xnp) + reg |= ADVERTISE_RESV; + else if (efx->link_advertising & (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full)) + reg |= ADVERTISE_NPAGE; + if (efx->link_advertising & ADVERTISED_Pause) + reg |= ADVERTISE_PAUSE_CAP; + if (efx->link_advertising & ADVERTISED_Asym_Pause) + reg |= ADVERTISE_PAUSE_ASYM; + efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); + + /* Set up the (extended) next page if necessary */ + if (efx->phy_op->set_npage_adv) + efx->phy_op->set_npage_adv(efx, efx->link_advertising); + + /* Enable and restart AN */ + reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1); + reg |= MDIO_AN_CTRL1_ENABLE; + if (!(EFX_WORKAROUND_15195(efx) && LOOPBACK_EXTERNAL(efx))) + reg |= MDIO_AN_CTRL1_RESTART; + if (xnp) + reg |= MDIO_AN_CTRL1_XNP; + else + reg &= ~MDIO_AN_CTRL1_XNP; + efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg); +} + enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx) { - int lpa; + BUILD_BUG_ON(EFX_FC_AUTO & (EFX_FC_RX | EFX_FC_TX)); - if (!(efx->phy_op->mmds & MDIO_DEVS_AN)) + if (!(efx->wanted_fc & EFX_FC_AUTO)) return efx->wanted_fc; - lpa = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA); - return efx_fc_resolve(efx->wanted_fc, lpa); + + WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); + + return mii_resolve_flowctrl_fdx( + mii_advertise_flowctrl(efx->wanted_fc), + efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA)); } diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h index 6b14421a7444..f6ac9503339d 100644 --- a/drivers/net/sfc/mdio_10g.h +++ b/drivers/net/sfc/mdio_10g.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -17,7 +17,6 @@ */ #include "efx.h" -#include "boards.h" static inline unsigned efx_mdio_id_rev(u32 id) { return id & 0xf; } static inline unsigned efx_mdio_id_model(u32 id) { return (id >> 4) & 0x3f; } @@ -87,6 +86,9 @@ extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx, /* Set (some of) the PHY settings over MDIO */ extern int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); +/* Push advertising flags and restart autonegotiation */ +extern void efx_mdio_an_reconfigure(struct efx_nic *efx); + /* Get pause parameters from AN if available (otherwise return * requested pause parameters) */ diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index 820c233c3ea0..3a464529a46b 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -1,36 +1,80 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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, incorporated herein by reference. */ +#include <linux/bitops.h> #include <linux/module.h> #include <linux/mtd/mtd.h> #include <linux/delay.h> +#include <linux/rtnetlink.h> #define EFX_DRIVER_NAME "sfc_mtd" #include "net_driver.h" #include "spi.h" #include "efx.h" +#include "nic.h" +#include "mcdi.h" +#include "mcdi_pcol.h" #define EFX_SPI_VERIFY_BUF_LEN 16 +#define EFX_MCDI_CHUNK_LEN 128 -struct efx_mtd { - const struct efx_spi_device *spi; +struct efx_mtd_partition { struct mtd_info mtd; + union { + struct { + bool updating; + u8 nvram_type; + u16 fw_subtype; + } mcdi; + size_t offset; + }; + const char *type_name; char name[IFNAMSIZ + 20]; }; +struct efx_mtd_ops { + int (*read)(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, u8 *buffer); + int (*erase)(struct mtd_info *mtd, loff_t start, size_t len); + int (*write)(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u8 *buffer); + int (*sync)(struct mtd_info *mtd); +}; + +struct efx_mtd { + struct list_head node; + struct efx_nic *efx; + const struct efx_spi_device *spi; + const char *name; + const struct efx_mtd_ops *ops; + size_t n_parts; + struct efx_mtd_partition part[0]; +}; + +#define efx_for_each_partition(part, efx_mtd) \ + for ((part) = &(efx_mtd)->part[0]; \ + (part) != &(efx_mtd)->part[(efx_mtd)->n_parts]; \ + (part)++) + +#define to_efx_mtd_partition(mtd) \ + container_of(mtd, struct efx_mtd_partition, mtd) + +static int falcon_mtd_probe(struct efx_nic *efx); +static int siena_mtd_probe(struct efx_nic *efx); + /* SPI utilities */ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) { const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; u8 status; int rc, i; @@ -39,7 +83,7 @@ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) __set_current_state(uninterruptible ? TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); schedule_timeout(HZ / 10); - rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); if (rc) return rc; @@ -52,32 +96,35 @@ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) return -ETIMEDOUT; } -static int efx_spi_unlock(const struct efx_spi_device *spi) +static int +efx_spi_unlock(struct efx_nic *efx, const struct efx_spi_device *spi) { const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 | SPI_STATUS_BP0); u8 status; int rc; - rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, + &status, sizeof(status)); if (rc) return rc; if (!(status & unlock_mask)) return 0; /* already unlocked */ - rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; - rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0); if (rc) return rc; status &= ~unlock_mask; - rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status)); + rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status, + NULL, sizeof(status)); if (rc) return rc; - rc = falcon_spi_wait_write(spi); + rc = falcon_spi_wait_write(efx, spi); if (rc) return rc; @@ -87,6 +134,7 @@ static int efx_spi_unlock(const struct efx_spi_device *spi) static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) { const struct efx_spi_device *spi = efx_mtd->spi; + struct efx_nic *efx = efx_mtd->efx; unsigned pos, block_len; u8 empty[EFX_SPI_VERIFY_BUF_LEN]; u8 buffer[EFX_SPI_VERIFY_BUF_LEN]; @@ -98,13 +146,14 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) if (spi->erase_command == 0) return -EOPNOTSUPP; - rc = efx_spi_unlock(spi); + rc = efx_spi_unlock(efx, spi); if (rc) return rc; - rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; - rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL, + NULL, 0); if (rc) return rc; rc = efx_spi_slow_wait(efx_mtd, false); @@ -113,7 +162,8 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) memset(empty, 0xff, sizeof(empty)); for (pos = 0; pos < len; pos += block_len) { block_len = min(len - pos, sizeof(buffer)); - rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer); + rc = falcon_spi_read(efx, spi, start + pos, block_len, + NULL, buffer); if (rc) return rc; if (memcmp(empty, buffer, block_len)) @@ -130,140 +180,473 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) /* MTD interface */ -static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len, - size_t *retlen, u8 *buffer) +static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) { struct efx_mtd *efx_mtd = mtd->priv; + int rc; + + rc = efx_mtd->ops->erase(mtd, erase->addr, erase->len); + if (rc == 0) { + erase->state = MTD_ERASE_DONE; + } else { + erase->state = MTD_ERASE_FAILED; + erase->fail_addr = 0xffffffff; + } + mtd_erase_callback(erase); + return rc; +} + +static void efx_mtd_sync(struct mtd_info *mtd) +{ + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc; + + rc = efx_mtd->ops->sync(mtd); + if (rc) + EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc); +} + +static void efx_mtd_remove_partition(struct efx_mtd_partition *part) +{ + int rc; + + for (;;) { + rc = del_mtd_device(&part->mtd); + if (rc != -EBUSY) + break; + ssleep(1); + } + WARN_ON(rc); +} + +static void efx_mtd_remove_device(struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_for_each_partition(part, efx_mtd) + efx_mtd_remove_partition(part); + list_del(&efx_mtd->node); + kfree(efx_mtd); +} + +static void efx_mtd_rename_device(struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_for_each_partition(part, efx_mtd) + if (efx_nic_rev(efx_mtd->efx) >= EFX_REV_SIENA_A0) + snprintf(part->name, sizeof(part->name), + "%s %s:%02x", efx_mtd->efx->name, + part->type_name, part->mcdi.fw_subtype); + else + snprintf(part->name, sizeof(part->name), + "%s %s", efx_mtd->efx->name, + part->type_name); +} + +static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_mtd->efx = efx; + + efx_mtd_rename_device(efx_mtd); + + efx_for_each_partition(part, efx_mtd) { + part->mtd.writesize = 1; + + part->mtd.owner = THIS_MODULE; + part->mtd.priv = efx_mtd; + part->mtd.name = part->name; + part->mtd.erase = efx_mtd_erase; + part->mtd.read = efx_mtd->ops->read; + part->mtd.write = efx_mtd->ops->write; + part->mtd.sync = efx_mtd_sync; + + if (add_mtd_device(&part->mtd)) + goto fail; + } + + list_add(&efx_mtd->node, &efx->mtd_list); + return 0; + +fail: + while (part != &efx_mtd->part[0]) { + --part; + efx_mtd_remove_partition(part); + } + /* add_mtd_device() returns 1 if the MTD table is full */ + return -ENOMEM; +} + +void efx_mtd_remove(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd, *next; + + WARN_ON(efx_dev_registered(efx)); + + list_for_each_entry_safe(efx_mtd, next, &efx->mtd_list, node) + efx_mtd_remove_device(efx_mtd); +} + +void efx_mtd_rename(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd; + + ASSERT_RTNL(); + + list_for_each_entry(efx_mtd, &efx->mtd_list, node) + efx_mtd_rename_device(efx_mtd); +} + +int efx_mtd_probe(struct efx_nic *efx) +{ + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) + return siena_mtd_probe(efx); + else + return falcon_mtd_probe(efx); +} + +/* Implementation of MTD operations for Falcon */ + +static int falcon_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start, - len, retlen, buffer); + rc = falcon_spi_read(efx, spi, part->offset + start, len, + retlen, buffer); mutex_unlock(&efx->spi_lock); return rc; } -static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) { + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); struct efx_mtd *efx_mtd = mtd->priv; - struct efx_nic *efx = efx_mtd->spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr, - erase->len); + rc = efx_spi_erase(efx_mtd, part->offset + start, len); mutex_unlock(&efx->spi_lock); - - if (rc == 0) { - erase->state = MTD_ERASE_DONE; - } else { - erase->state = MTD_ERASE_FAILED; - erase->fail_addr = 0xffffffff; - } - mtd_erase_callback(erase); return rc; } -static int efx_mtd_write(struct mtd_info *mtd, loff_t start, - size_t len, size_t *retlen, const u8 *buffer) +static int falcon_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) { + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); struct efx_mtd *efx_mtd = mtd->priv; const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start, - len, retlen, buffer); + rc = falcon_spi_write(efx, spi, part->offset + start, len, + retlen, buffer); mutex_unlock(&efx->spi_lock); return rc; } -static void efx_mtd_sync(struct mtd_info *mtd) +static int falcon_mtd_sync(struct mtd_info *mtd) { struct efx_mtd *efx_mtd = mtd->priv; - struct efx_nic *efx = efx_mtd->spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; mutex_lock(&efx->spi_lock); rc = efx_spi_slow_wait(efx_mtd, true); mutex_unlock(&efx->spi_lock); + return rc; +} + +static struct efx_mtd_ops falcon_mtd_ops = { + .read = falcon_mtd_read, + .erase = falcon_mtd_erase, + .write = falcon_mtd_write, + .sync = falcon_mtd_sync, +}; + +static int falcon_mtd_probe(struct efx_nic *efx) +{ + struct efx_spi_device *spi = efx->spi_flash; + struct efx_mtd *efx_mtd; + int rc; + + ASSERT_RTNL(); + if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START) + return -ENODEV; + + efx_mtd = kzalloc(sizeof(*efx_mtd) + sizeof(efx_mtd->part[0]), + GFP_KERNEL); + if (!efx_mtd) + return -ENOMEM; + + efx_mtd->spi = spi; + efx_mtd->name = "flash"; + efx_mtd->ops = &falcon_mtd_ops; + + efx_mtd->n_parts = 1; + efx_mtd->part[0].mtd.type = MTD_NORFLASH; + efx_mtd->part[0].mtd.flags = MTD_CAP_NORFLASH; + efx_mtd->part[0].mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; + efx_mtd->part[0].mtd.erasesize = spi->erase_size; + efx_mtd->part[0].offset = FALCON_FLASH_BOOTCODE_START; + efx_mtd->part[0].type_name = "sfc_flash_bootrom"; + + rc = efx_mtd_probe_device(efx, efx_mtd); if (rc) - EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc); - return; + kfree(efx_mtd); + return rc; } -void efx_mtd_remove(struct efx_nic *efx) +/* Implementation of MTD operations for Siena */ + +static int siena_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) { - if (efx->spi_flash && efx->spi_flash->mtd) { - struct efx_mtd *efx_mtd = efx->spi_flash->mtd; - int rc; - - for (;;) { - rc = del_mtd_device(&efx_mtd->mtd); - if (rc != -EBUSY) - break; - ssleep(1); - } - WARN_ON(rc); - kfree(efx_mtd); + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start; + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk; + int rc = 0; + + while (offset < end) { + chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); + rc = efx_mcdi_nvram_read(efx, part->mcdi.nvram_type, offset, + buffer, chunk); + if (rc) + goto out; + offset += chunk; + buffer += chunk; } +out: + *retlen = offset - start; + return rc; } -void efx_mtd_rename(struct efx_nic *efx) +static int siena_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) { - if (efx->spi_flash && efx->spi_flash->mtd) { - struct efx_mtd *efx_mtd = efx->spi_flash->mtd; - snprintf(efx_mtd->name, sizeof(efx_mtd->name), - "%s sfc_flash_bootrom", efx->name); + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start & ~((loff_t)(mtd->erasesize - 1)); + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk = part->mtd.erasesize; + int rc = 0; + + if (!part->mcdi.updating) { + rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); + if (rc) + goto out; + part->mcdi.updating = 1; + } + + /* The MCDI interface can in fact do multiple erase blocks at once; + * but erasing may be slow, so we make multiple calls here to avoid + * tripping the MCDI RPC timeout. */ + while (offset < end) { + rc = efx_mcdi_nvram_erase(efx, part->mcdi.nvram_type, offset, + chunk); + if (rc) + goto out; + offset += chunk; } +out: + return rc; } -int efx_mtd_probe(struct efx_nic *efx) +static int siena_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) { - struct efx_spi_device *spi = efx->spi_flash; - struct efx_mtd *efx_mtd; + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start; + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk; + int rc = 0; + + if (!part->mcdi.updating) { + rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); + if (rc) + goto out; + part->mcdi.updating = 1; + } - if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START) + while (offset < end) { + chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); + rc = efx_mcdi_nvram_write(efx, part->mcdi.nvram_type, offset, + buffer, chunk); + if (rc) + goto out; + offset += chunk; + buffer += chunk; + } +out: + *retlen = offset - start; + return rc; +} + +static int siena_mtd_sync(struct mtd_info *mtd) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc = 0; + + if (part->mcdi.updating) { + part->mcdi.updating = 0; + rc = efx_mcdi_nvram_update_finish(efx, part->mcdi.nvram_type); + } + + return rc; +} + +static struct efx_mtd_ops siena_mtd_ops = { + .read = siena_mtd_read, + .erase = siena_mtd_erase, + .write = siena_mtd_write, + .sync = siena_mtd_sync, +}; + +struct siena_nvram_type_info { + int port; + const char *name; +}; + +static struct siena_nvram_type_info siena_nvram_types[] = { + [MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO] = { 0, "sfc_dummy_phy" }, + [MC_CMD_NVRAM_TYPE_MC_FW] = { 0, "sfc_mcfw" }, + [MC_CMD_NVRAM_TYPE_MC_FW_BACKUP] = { 0, "sfc_mcfw_backup" }, + [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0] = { 0, "sfc_static_cfg" }, + [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1] = { 1, "sfc_static_cfg" }, + [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0] = { 0, "sfc_dynamic_cfg" }, + [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1] = { 1, "sfc_dynamic_cfg" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM] = { 0, "sfc_exp_rom" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0] = { 0, "sfc_exp_rom_cfg" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1] = { 1, "sfc_exp_rom_cfg" }, + [MC_CMD_NVRAM_TYPE_PHY_PORT0] = { 0, "sfc_phy_fw" }, + [MC_CMD_NVRAM_TYPE_PHY_PORT1] = { 1, "sfc_phy_fw" }, +}; + +static int siena_mtd_probe_partition(struct efx_nic *efx, + struct efx_mtd *efx_mtd, + unsigned int part_id, + unsigned int type) +{ + struct efx_mtd_partition *part = &efx_mtd->part[part_id]; + struct siena_nvram_type_info *info; + size_t size, erase_size; + bool protected; + int rc; + + if (type >= ARRAY_SIZE(siena_nvram_types)) return -ENODEV; - efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL); + info = &siena_nvram_types[type]; + + if (info->port != efx_port_num(efx)) + return -ENODEV; + + rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected); + if (rc) + return rc; + if (protected) + return -ENODEV; /* hide it */ + + part->mcdi.nvram_type = type; + part->type_name = info->name; + + part->mtd.type = MTD_NORFLASH; + part->mtd.flags = MTD_CAP_NORFLASH; + part->mtd.size = size; + part->mtd.erasesize = erase_size; + + return 0; +} + +static int siena_mtd_get_fw_subtypes(struct efx_nic *efx, + struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + uint16_t fw_subtype_list[MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN / + sizeof(uint16_t)]; + int rc; + + rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list); + if (rc) + return rc; + + efx_for_each_partition(part, efx_mtd) + part->mcdi.fw_subtype = fw_subtype_list[part->mcdi.nvram_type]; + + return 0; +} + +static int siena_mtd_probe(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd; + int rc = -ENODEV; + u32 nvram_types; + unsigned int type; + + ASSERT_RTNL(); + + rc = efx_mcdi_nvram_types(efx, &nvram_types); + if (rc) + return rc; + + efx_mtd = kzalloc(sizeof(*efx_mtd) + + hweight32(nvram_types) * sizeof(efx_mtd->part[0]), + GFP_KERNEL); if (!efx_mtd) return -ENOMEM; - efx_mtd->spi = spi; - spi->mtd = efx_mtd; - - efx_mtd->mtd.type = MTD_NORFLASH; - efx_mtd->mtd.flags = MTD_CAP_NORFLASH; - efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; - efx_mtd->mtd.erasesize = spi->erase_size; - efx_mtd->mtd.writesize = 1; - efx_mtd_rename(efx); - - efx_mtd->mtd.owner = THIS_MODULE; - efx_mtd->mtd.priv = efx_mtd; - efx_mtd->mtd.name = efx_mtd->name; - efx_mtd->mtd.erase = efx_mtd_erase; - efx_mtd->mtd.read = efx_mtd_read; - efx_mtd->mtd.write = efx_mtd_write; - efx_mtd->mtd.sync = efx_mtd_sync; - - if (add_mtd_device(&efx_mtd->mtd)) { - kfree(efx_mtd); - spi->mtd = NULL; - /* add_mtd_device() returns 1 if the MTD table is full */ - return -ENOMEM; + efx_mtd->name = "Siena NVRAM manager"; + + efx_mtd->ops = &siena_mtd_ops; + + type = 0; + efx_mtd->n_parts = 0; + + while (nvram_types != 0) { + if (nvram_types & 1) { + rc = siena_mtd_probe_partition(efx, efx_mtd, + efx_mtd->n_parts, type); + if (rc == 0) + efx_mtd->n_parts++; + else if (rc != -ENODEV) + goto fail; + } + type++; + nvram_types >>= 1; } - return 0; + rc = siena_mtd_get_fw_subtypes(efx, efx_mtd); + if (rc) + goto fail; + + rc = efx_mtd_probe_device(efx, efx_mtd); +fail: + if (rc) + kfree(efx_mtd); + return rc; } + diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 298566da638b..34c381f009b7 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2005-2008 Solarflare Communications Inc. + * Copyright 2005-2009 Solarflare Communications Inc. * * 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 @@ -38,7 +38,7 @@ #ifndef EFX_DRIVER_NAME #define EFX_DRIVER_NAME "sfc" #endif -#define EFX_DRIVER_VERSION "2.3" +#define EFX_DRIVER_VERSION "3.0" #ifdef EFX_ENABLE_DEBUG #define EFX_BUG_ON_PARANOID(x) BUG_ON(x) @@ -113,6 +113,13 @@ struct efx_special_buffer { int entries; }; +enum efx_flush_state { + FLUSH_NONE, + FLUSH_PENDING, + FLUSH_FAILED, + FLUSH_DONE, +}; + /** * struct efx_tx_buffer - An Efx TX buffer * @skb: The associated socket buffer. @@ -189,7 +196,7 @@ struct efx_tx_queue { struct efx_nic *nic; struct efx_tx_buffer *buffer; struct efx_special_buffer txd; - bool flushed; + enum efx_flush_state flushed; /* Members used mainly on the completion path */ unsigned int read_count ____cacheline_aligned_in_smp; @@ -284,7 +291,7 @@ struct efx_rx_queue { struct page *buf_page; dma_addr_t buf_dma_addr; char *buf_data; - bool flushed; + enum efx_flush_state flushed; }; /** @@ -327,7 +334,7 @@ enum efx_rx_alloc_method { * @used_flags: Channel is used by net driver * @enabled: Channel enabled indicator * @irq: IRQ number (MSI and MSI-X only) - * @irq_moderation: IRQ moderation value (in us) + * @irq_moderation: IRQ moderation value (in hardware ticks) * @napi_dev: Net device used with NAPI * @napi_str: NAPI control structure * @reset_work: Scheduled reset work thread @@ -343,9 +350,9 @@ enum efx_rx_alloc_method { * @rx_alloc_push_pages: RX allocation method currently in use for pushing * descriptors * @n_rx_tobe_disc: Count of RX_TOBE_DISC errors - * @n_rx_ip_frag_err: Count of RX IP fragment errors * @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors * @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors + * @n_rx_mcast_mismatch: Count of unmatched multicast frames * @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors * @n_rx_overlength: Count of RX_OVERLENGTH errors * @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun @@ -373,9 +380,9 @@ struct efx_channel { int rx_alloc_push_pages; unsigned n_rx_tobe_disc; - unsigned n_rx_ip_frag_err; unsigned n_rx_ip_hdr_chksum_err; unsigned n_rx_tcp_udp_chksum_err; + unsigned n_rx_mcast_mismatch; unsigned n_rx_frm_trunc; unsigned n_rx_overlength; unsigned n_skbuff_leaks; @@ -388,53 +395,29 @@ struct efx_channel { }; -/** - * struct efx_blinker - S/W LED blinking context - * @state: Current state - on or off - * @resubmit: Timer resubmission flag - * @timer: Control timer for blinking - */ -struct efx_blinker { - bool state; - bool resubmit; - struct timer_list timer; +enum efx_led_mode { + EFX_LED_OFF = 0, + EFX_LED_ON = 1, + EFX_LED_DEFAULT = 2 }; +#define STRING_TABLE_LOOKUP(val, member) \ + ((val) < member ## _max) ? member ## _names[val] : "(invalid)" -/** - * struct efx_board - board information - * @type: Board model type - * @major: Major rev. ('A', 'B' ...) - * @minor: Minor rev. (0, 1, ...) - * @init: Initialisation function - * @init_leds: Sets up board LEDs. May be called repeatedly. - * @set_id_led: Turns the identification LED on or off - * @blink: Starts/stops blinking - * @monitor: Board-specific health check function - * @fini: Cleanup function - * @blinker: used to blink LEDs in software - * @hwmon_client: I2C client for hardware monitor - * @ioexp_client: I2C client for power/port control - */ -struct efx_board { - int type; - int major; - int minor; - int (*init) (struct efx_nic *nic); - /* As the LEDs are typically attached to the PHY, LEDs - * have a separate init callback that happens later than - * board init. */ - void (*init_leds)(struct efx_nic *efx); - void (*set_id_led) (struct efx_nic *efx, bool state); - int (*monitor) (struct efx_nic *nic); - void (*blink) (struct efx_nic *efx, bool start); - void (*fini) (struct efx_nic *nic); - struct efx_blinker blinker; - struct i2c_client *hwmon_client, *ioexp_client; -}; +extern const char *efx_loopback_mode_names[]; +extern const unsigned int efx_loopback_mode_max; +#define LOOPBACK_MODE(efx) \ + STRING_TABLE_LOOKUP((efx)->loopback_mode, efx_loopback_mode) + +extern const char *efx_interrupt_mode_names[]; +extern const unsigned int efx_interrupt_mode_max; +#define INT_MODE(efx) \ + STRING_TABLE_LOOKUP(efx->interrupt_mode, efx_interrupt_mode) -#define STRING_TABLE_LOOKUP(val, member) \ - member ## _names[val] +extern const char *efx_reset_type_names[]; +extern const unsigned int efx_reset_type_max; +#define RESET_TYPE(type) \ + STRING_TABLE_LOOKUP(type, efx_reset_type) enum efx_int_mode { /* Be careful if altering to correct macro below */ @@ -445,20 +428,7 @@ enum efx_int_mode { }; #define EFX_INT_MODE_USE_MSI(x) (((x)->interrupt_mode) <= EFX_INT_MODE_MSI) -enum phy_type { - PHY_TYPE_NONE = 0, - PHY_TYPE_TXC43128 = 1, - PHY_TYPE_88E1111 = 2, - PHY_TYPE_SFX7101 = 3, - PHY_TYPE_QT2022C2 = 4, - PHY_TYPE_PM8358 = 6, - PHY_TYPE_SFT9001A = 8, - PHY_TYPE_QT2025C = 9, - PHY_TYPE_SFT9001B = 10, - PHY_TYPE_MAX /* Insert any new items before this */ -}; - -#define EFX_IS10G(efx) ((efx)->link_speed == 10000) +#define EFX_IS10G(efx) ((efx)->link_state.speed == 10000) enum nic_state { STATE_INIT = 0, @@ -500,73 +470,69 @@ enum efx_fc_type { EFX_FC_AUTO = 4, }; -/* Supported MAC bit-mask */ -enum efx_mac_type { - EFX_GMAC = 1, - EFX_XMAC = 2, +/** + * struct efx_link_state - Current state of the link + * @up: Link is up + * @fd: Link is full-duplex + * @fc: Actual flow control flags + * @speed: Link speed (Mbps) + */ +struct efx_link_state { + bool up; + bool fd; + enum efx_fc_type fc; + unsigned int speed; }; -static inline enum efx_fc_type efx_fc_resolve(enum efx_fc_type wanted_fc, - unsigned int lpa) +static inline bool efx_link_state_equal(const struct efx_link_state *left, + const struct efx_link_state *right) { - BUILD_BUG_ON(EFX_FC_AUTO & (EFX_FC_RX | EFX_FC_TX)); - - if (!(wanted_fc & EFX_FC_AUTO)) - return wanted_fc; - - return mii_resolve_flowctrl_fdx(mii_advertise_flowctrl(wanted_fc), lpa); + return left->up == right->up && left->fd == right->fd && + left->fc == right->fc && left->speed == right->speed; } /** * struct efx_mac_operations - Efx MAC operations table * @reconfigure: Reconfigure MAC. Serialised by the mac_lock * @update_stats: Update statistics - * @irq: Hardware MAC event callback. Serialised by the mac_lock - * @poll: Poll for hardware state. Serialised by the mac_lock + * @check_fault: Check fault state. True if fault present. */ struct efx_mac_operations { - void (*reconfigure) (struct efx_nic *efx); + int (*reconfigure) (struct efx_nic *efx); void (*update_stats) (struct efx_nic *efx); - void (*irq) (struct efx_nic *efx); - void (*poll) (struct efx_nic *efx); + bool (*check_fault)(struct efx_nic *efx); }; /** * struct efx_phy_operations - Efx PHY operations table + * @probe: Probe PHY and initialise efx->mdio.mode_support, efx->mdio.mmds, + * efx->loopback_modes. * @init: Initialise PHY * @fini: Shut down PHY * @reconfigure: Reconfigure PHY (e.g. for new link parameters) - * @clear_interrupt: Clear down interrupt - * @blink: Blink LEDs - * @poll: Poll for hardware state. Serialised by the mac_lock. + * @poll: Update @link_state and report whether it changed. + * Serialised by the mac_lock. * @get_settings: Get ethtool settings. Serialised by the mac_lock. * @set_settings: Set ethtool settings. Serialised by the mac_lock. * @set_npage_adv: Set abilities advertised in (Extended) Next Page * (only needed where AN bit is set in mmds) - * @num_tests: Number of PHY-specific tests/results - * @test_names: Names of the tests/results + * @test_name: Get the name of a PHY-specific test/result * @run_tests: Run tests and record results as appropriate. * Flags are the ethtool tests flags. - * @mmds: MMD presence mask - * @loopbacks: Supported loopback modes mask */ struct efx_phy_operations { - enum efx_mac_type macs; + int (*probe) (struct efx_nic *efx); int (*init) (struct efx_nic *efx); void (*fini) (struct efx_nic *efx); - void (*reconfigure) (struct efx_nic *efx); - void (*clear_interrupt) (struct efx_nic *efx); - void (*poll) (struct efx_nic *efx); + int (*reconfigure) (struct efx_nic *efx); + bool (*poll) (struct efx_nic *efx); void (*get_settings) (struct efx_nic *efx, struct ethtool_cmd *ecmd); int (*set_settings) (struct efx_nic *efx, struct ethtool_cmd *ecmd); void (*set_npage_adv) (struct efx_nic *efx, u32); - u32 num_tests; - const char *const *test_names; + const char *(*test_name) (struct efx_nic *efx, unsigned int index); int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags); - int mmds; - unsigned loopbacks; }; /** @@ -690,36 +656,38 @@ union efx_multicast_hash { * @interrupt_mode: Interrupt mode * @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues * @irq_rx_moderation: IRQ moderation time for RX event queues - * @i2c_adap: I2C adapter - * @board_info: Board-level information * @state: Device state flag. Serialised by the rtnl_lock. * @reset_pending: Pending reset method (normally RESET_TYPE_NONE) * @tx_queue: TX DMA queues * @rx_queue: RX DMA queues * @channel: Channels + * @next_buffer_table: First available buffer table id * @n_rx_queues: Number of RX queues * @n_channels: Number of channels in use * @rx_buffer_len: RX buffer length * @rx_buffer_order: Order (log2) of number of pages for each RX buffer + * @int_error_count: Number of internal errors seen recently + * @int_error_expire: Time at which error count will be expired * @irq_status: Interrupt status buffer * @last_irq_cpu: Last CPU to handle interrupt. * This register is written with the SMP processor ID whenever an * interrupt is handled. It is used by falcon_test_interrupt() * to verify that an interrupt has occurred. * @spi_flash: SPI flash device - * This field will be %NULL if no flash device is present. + * This field will be %NULL if no flash device is present (or for Siena). * @spi_eeprom: SPI EEPROM device - * This field will be %NULL if no EEPROM device is present. + * This field will be %NULL if no EEPROM device is present (or for Siena). * @spi_lock: SPI bus lock + * @mtd_list: List of MTDs attached to the NIC * @n_rx_nodesc_drop_cnt: RX no descriptor drop count * @nic_data: Hardware dependant state * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode, * @port_inhibited, efx_monitor() and efx_reconfigure_port() * @port_enabled: Port enabled indicator. - * Serialises efx_stop_all(), efx_start_all(), efx_monitor(), - * efx_phy_work(), and efx_mac_work() with kernel interfaces. Safe to read - * under any one of the rtnl_lock, mac_lock, or netif_tx_lock, but all - * three must be held to modify it. + * Serialises efx_stop_all(), efx_start_all(), efx_monitor() and + * efx_mac_work() with kernel interfaces. Safe to read under any + * one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must + * be held to modify it. * @port_inhibited: If set, the netif_carrier is always off. Hold the mac_lock * @port_initialized: Port initialized? * @net_dev: Operating system network device. Consider holding the rtnl lock @@ -731,26 +699,23 @@ union efx_multicast_hash { * &struct net_device_stats. * @stats_buffer: DMA buffer for statistics * @stats_lock: Statistics update lock. Serialises statistics fetches - * @stats_disable_count: Nest count for disabling statistics fetches * @mac_op: MAC interface * @mac_address: Permanent MAC address * @phy_type: PHY type - * @phy_lock: PHY access lock + * @mdio_lock: MDIO lock * @phy_op: PHY interface * @phy_data: PHY private data (including PHY-specific stats) * @mdio: PHY MDIO interface + * @mdio_bus: PHY MDIO bus ID (only used by Siena) * @phy_mode: PHY operating mode. Serialised by @mac_lock. - * @mac_up: MAC link state - * @link_up: Link status - * @link_fd: Link is full duplex - * @link_fc: Actualy flow control flags - * @link_speed: Link speed (Mbps) + * @xmac_poll_required: XMAC link state needs polling + * @link_advertising: Autonegotiation advertising flags + * @link_state: Current state of the link * @n_link_state_changes: Number of times the link has changed state * @promiscuous: Promiscuous flag. Protected by netif_tx_lock. * @multicast_hash: Multicast hash table * @wanted_fc: Wanted flow control flags - * @phy_work: work item for dealing with PHY events - * @mac_work: work item for dealing with MAC events + * @mac_work: Work item for changing MAC promiscuity and multicast hash * @loopback_mode: Loopback status * @loopback_modes: Supported loopback mode bitmask * @loopback_selftest: Offline self-test private state @@ -774,9 +739,6 @@ struct efx_nic { bool irq_rx_adaptive; unsigned int irq_rx_moderation; - struct i2c_adapter i2c_adap; - struct efx_board board_info; - enum nic_state state; enum reset_type reset_pending; @@ -784,21 +746,29 @@ struct efx_nic { struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES]; struct efx_channel channel[EFX_MAX_CHANNELS]; + unsigned next_buffer_table; int n_rx_queues; int n_channels; unsigned int rx_buffer_len; unsigned int rx_buffer_order; + unsigned int_error_count; + unsigned long int_error_expire; + struct efx_buffer irq_status; volatile signed int last_irq_cpu; + unsigned long irq_zero_count; struct efx_spi_device *spi_flash; struct efx_spi_device *spi_eeprom; struct mutex spi_lock; +#ifdef CONFIG_SFC_MTD + struct list_head mtd_list; +#endif unsigned n_rx_nodesc_drop_cnt; - struct falcon_nic_data *nic_data; + void *nic_data; struct mutex mac_lock; struct work_struct mac_work; @@ -815,24 +785,21 @@ struct efx_nic { struct efx_mac_stats mac_stats; struct efx_buffer stats_buffer; spinlock_t stats_lock; - unsigned int stats_disable_count; struct efx_mac_operations *mac_op; unsigned char mac_address[ETH_ALEN]; - enum phy_type phy_type; - spinlock_t phy_lock; - struct work_struct phy_work; + unsigned int phy_type; + struct mutex mdio_lock; struct efx_phy_operations *phy_op; void *phy_data; struct mdio_if_info mdio; + unsigned int mdio_bus; enum efx_phy_mode phy_mode; - bool mac_up; - bool link_up; - bool link_fd; - enum efx_fc_type link_fc; - unsigned int link_speed; + bool xmac_poll_required; + u32 link_advertising; + struct efx_link_state link_state; unsigned int n_link_state_changes; bool promiscuous; @@ -841,7 +808,7 @@ struct efx_nic { atomic_t rx_reset; enum efx_loopback_mode loopback_mode; - unsigned int loopback_modes; + u64 loopback_modes; void *loopback_selftest; }; @@ -860,50 +827,95 @@ static inline const char *efx_dev_name(struct efx_nic *efx) return efx_dev_registered(efx) ? efx->name : ""; } +static inline unsigned int efx_port_num(struct efx_nic *efx) +{ + return PCI_FUNC(efx->pci_dev->devfn); +} + /** * struct efx_nic_type - Efx device type definition - * @mem_bar: Memory BAR number + * @probe: Probe the controller + * @remove: Free resources allocated by probe() + * @init: Initialise the controller + * @fini: Shut down the controller + * @monitor: Periodic function for polling link state and hardware monitor + * @reset: Reset the controller hardware and possibly the PHY. This will + * be called while the controller is uninitialised. + * @probe_port: Probe the MAC and PHY + * @remove_port: Free resources allocated by probe_port() + * @prepare_flush: Prepare the hardware for flushing the DMA queues + * @update_stats: Update statistics not provided by event handling + * @start_stats: Start the regular fetching of statistics + * @stop_stats: Stop the regular fetching of statistics + * @set_id_led: Set state of identifying LED or revert to automatic function + * @push_irq_moderation: Apply interrupt moderation value + * @push_multicast_hash: Apply multicast hash table + * @reconfigure_port: Push loopback/power/txdis changes to the MAC and PHY + * @get_wol: Get WoL configuration from driver state + * @set_wol: Push WoL configuration to the NIC + * @resume_wol: Synchronise WoL state between driver and MC (e.g. after resume) + * @test_registers: Test read/write functionality of control registers + * @test_nvram: Test validity of NVRAM contents + * @default_mac_ops: efx_mac_operations to set at startup + * @revision: Hardware architecture revision * @mem_map_size: Memory BAR mapped size * @txd_ptr_tbl_base: TX descriptor ring base address * @rxd_ptr_tbl_base: RX descriptor ring base address * @buf_tbl_base: Buffer table base address * @evq_ptr_tbl_base: Event queue pointer table base address * @evq_rptr_tbl_base: Event queue read-pointer table base address - * @txd_ring_mask: TX descriptor ring size - 1 (must be a power of two - 1) - * @rxd_ring_mask: RX descriptor ring size - 1 (must be a power of two - 1) - * @evq_size: Event queue size (must be a power of two) * @max_dma_mask: Maximum possible DMA mask - * @tx_dma_mask: TX DMA mask - * @bug5391_mask: Address mask for bug 5391 workaround - * @rx_xoff_thresh: RX FIFO XOFF watermark (bytes) - * @rx_xon_thresh: RX FIFO XON watermark (bytes) * @rx_buffer_padding: Padding added to each RX buffer * @max_interrupt_mode: Highest capability interrupt mode supported * from &enum efx_init_mode. * @phys_addr_channels: Number of channels with physically addressed * descriptors + * @tx_dc_base: Base address in SRAM of TX queue descriptor caches + * @rx_dc_base: Base address in SRAM of RX queue descriptor caches + * @offload_features: net_device feature flags for protocol offload + * features implemented in hardware + * @reset_world_flags: Flags for additional components covered by + * reset method RESET_TYPE_WORLD */ struct efx_nic_type { - unsigned int mem_bar; + int (*probe)(struct efx_nic *efx); + void (*remove)(struct efx_nic *efx); + int (*init)(struct efx_nic *efx); + void (*fini)(struct efx_nic *efx); + void (*monitor)(struct efx_nic *efx); + int (*reset)(struct efx_nic *efx, enum reset_type method); + int (*probe_port)(struct efx_nic *efx); + void (*remove_port)(struct efx_nic *efx); + void (*prepare_flush)(struct efx_nic *efx); + void (*update_stats)(struct efx_nic *efx); + void (*start_stats)(struct efx_nic *efx); + void (*stop_stats)(struct efx_nic *efx); + void (*set_id_led)(struct efx_nic *efx, enum efx_led_mode mode); + void (*push_irq_moderation)(struct efx_channel *channel); + void (*push_multicast_hash)(struct efx_nic *efx); + int (*reconfigure_port)(struct efx_nic *efx); + void (*get_wol)(struct efx_nic *efx, struct ethtool_wolinfo *wol); + int (*set_wol)(struct efx_nic *efx, u32 type); + void (*resume_wol)(struct efx_nic *efx); + int (*test_registers)(struct efx_nic *efx); + int (*test_nvram)(struct efx_nic *efx); + struct efx_mac_operations *default_mac_ops; + + int revision; unsigned int mem_map_size; unsigned int txd_ptr_tbl_base; unsigned int rxd_ptr_tbl_base; unsigned int buf_tbl_base; unsigned int evq_ptr_tbl_base; unsigned int evq_rptr_tbl_base; - - unsigned int txd_ring_mask; - unsigned int rxd_ring_mask; - unsigned int evq_size; u64 max_dma_mask; - unsigned int tx_dma_mask; - unsigned bug5391_mask; - - int rx_xoff_thresh; - int rx_xon_thresh; unsigned int rx_buffer_padding; unsigned int max_interrupt_mode; unsigned int phys_addr_channels; + unsigned int tx_dc_base; + unsigned int rx_dc_base; + unsigned long offload_features; + u32 reset_world_flags; }; /************************************************************************** diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c new file mode 100644 index 000000000000..a577be227862 --- /dev/null +++ b/drivers/net/sfc/nic.c @@ -0,0 +1,1583 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "regs.h" +#include "io.h" +#include "workarounds.h" + +/************************************************************************** + * + * Configurable values + * + ************************************************************************** + */ + +/* This is set to 16 for a good reason. In summary, if larger than + * 16, the descriptor cache holds more than a default socket + * buffer's worth of packets (for UDP we can only have at most one + * socket buffer's worth outstanding). This combined with the fact + * that we only get 1 TX event per descriptor cache means the NIC + * goes idle. + */ +#define TX_DC_ENTRIES 16 +#define TX_DC_ENTRIES_ORDER 1 + +#define RX_DC_ENTRIES 64 +#define RX_DC_ENTRIES_ORDER 3 + +/* RX FIFO XOFF watermark + * + * When the amount of the RX FIFO increases used increases past this + * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A) + * This also has an effect on RX/TX arbitration + */ +int efx_nic_rx_xoff_thresh = -1; +module_param_named(rx_xoff_thresh_bytes, efx_nic_rx_xoff_thresh, int, 0644); +MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold"); + +/* RX FIFO XON watermark + * + * When the amount of the RX FIFO used decreases below this + * watermark send XON. Only used if TX flow control is enabled (ethtool -A) + * This also has an effect on RX/TX arbitration + */ +int efx_nic_rx_xon_thresh = -1; +module_param_named(rx_xon_thresh_bytes, efx_nic_rx_xon_thresh, int, 0644); +MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold"); + +/* If EFX_MAX_INT_ERRORS internal errors occur within + * EFX_INT_ERROR_EXPIRE seconds, we consider the NIC broken and + * disable it. + */ +#define EFX_INT_ERROR_EXPIRE 3600 +#define EFX_MAX_INT_ERRORS 5 + +/* We poll for events every FLUSH_INTERVAL ms, and check FLUSH_POLL_COUNT times + */ +#define EFX_FLUSH_INTERVAL 10 +#define EFX_FLUSH_POLL_COUNT 100 + +/* Size and alignment of special buffers (4KB) */ +#define EFX_BUF_SIZE 4096 + +/* Depth of RX flush request fifo */ +#define EFX_RX_FLUSH_COUNT 4 + +/************************************************************************** + * + * Solarstorm hardware access + * + **************************************************************************/ + +static inline void efx_write_buf_tbl(struct efx_nic *efx, efx_qword_t *value, + unsigned int index) +{ + efx_sram_writeq(efx, efx->membase + efx->type->buf_tbl_base, + value, index); +} + +/* Read the current event from the event queue */ +static inline efx_qword_t *efx_event(struct efx_channel *channel, + unsigned int index) +{ + return (((efx_qword_t *) (channel->eventq.addr)) + index); +} + +/* See if an event is present + * + * We check both the high and low dword of the event for all ones. We + * wrote all ones when we cleared the event, and no valid event can + * have all ones in either its high or low dwords. This approach is + * robust against reordering. + * + * Note that using a single 64-bit comparison is incorrect; even + * though the CPU read will be atomic, the DMA write may not be. + */ +static inline int efx_event_present(efx_qword_t *event) +{ + return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | + EFX_DWORD_IS_ALL_ONES(event->dword[1]))); +} + +static bool efx_masked_compare_oword(const efx_oword_t *a, const efx_oword_t *b, + const efx_oword_t *mask) +{ + return ((a->u64[0] ^ b->u64[0]) & mask->u64[0]) || + ((a->u64[1] ^ b->u64[1]) & mask->u64[1]); +} + +int efx_nic_test_registers(struct efx_nic *efx, + const struct efx_nic_register_test *regs, + size_t n_regs) +{ + unsigned address = 0, i, j; + efx_oword_t mask, imask, original, reg, buf; + + /* Falcon should be in loopback to isolate the XMAC from the PHY */ + WARN_ON(!LOOPBACK_INTERNAL(efx)); + + for (i = 0; i < n_regs; ++i) { + address = regs[i].address; + mask = imask = regs[i].mask; + EFX_INVERT_OWORD(imask); + + efx_reado(efx, &original, address); + + /* bit sweep on and off */ + for (j = 0; j < 128; j++) { + if (!EFX_EXTRACT_OWORD32(mask, j, j)) + continue; + + /* Test this testable bit can be set in isolation */ + EFX_AND_OWORD(reg, original, mask); + EFX_SET_OWORD32(reg, j, j, 1); + + efx_writeo(efx, ®, address); + efx_reado(efx, &buf, address); + + if (efx_masked_compare_oword(®, &buf, &mask)) + goto fail; + + /* Test this testable bit can be cleared in isolation */ + EFX_OR_OWORD(reg, original, mask); + EFX_SET_OWORD32(reg, j, j, 0); + + efx_writeo(efx, ®, address); + efx_reado(efx, &buf, address); + + if (efx_masked_compare_oword(®, &buf, &mask)) + goto fail; + } + + efx_writeo(efx, &original, address); + } + + return 0; + +fail: + EFX_ERR(efx, "wrote "EFX_OWORD_FMT" read "EFX_OWORD_FMT + " at address 0x%x mask "EFX_OWORD_FMT"\n", EFX_OWORD_VAL(reg), + EFX_OWORD_VAL(buf), address, EFX_OWORD_VAL(mask)); + return -EIO; +} + +/************************************************************************** + * + * Special buffer handling + * Special buffers are used for event queues and the TX and RX + * descriptor rings. + * + *************************************************************************/ + +/* + * Initialise a special buffer + * + * This will define a buffer (previously allocated via + * efx_alloc_special_buffer()) in the buffer table, allowing + * it to be used for event queues, descriptor rings etc. + */ +static void +efx_init_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + efx_qword_t buf_desc; + int index; + dma_addr_t dma_addr; + int i; + + EFX_BUG_ON_PARANOID(!buffer->addr); + + /* Write buffer descriptors to NIC */ + for (i = 0; i < buffer->entries; i++) { + index = buffer->index + i; + dma_addr = buffer->dma_addr + (i * 4096); + EFX_LOG(efx, "mapping special buffer %d at %llx\n", + index, (unsigned long long)dma_addr); + EFX_POPULATE_QWORD_3(buf_desc, + FRF_AZ_BUF_ADR_REGION, 0, + FRF_AZ_BUF_ADR_FBUF, dma_addr >> 12, + FRF_AZ_BUF_OWNER_ID_FBUF, 0); + efx_write_buf_tbl(efx, &buf_desc, index); + } +} + +/* Unmaps a buffer and clears the buffer table entries */ +static void +efx_fini_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + efx_oword_t buf_tbl_upd; + unsigned int start = buffer->index; + unsigned int end = (buffer->index + buffer->entries - 1); + + if (!buffer->entries) + return; + + EFX_LOG(efx, "unmapping special buffers %d-%d\n", + buffer->index, buffer->index + buffer->entries - 1); + + EFX_POPULATE_OWORD_4(buf_tbl_upd, + FRF_AZ_BUF_UPD_CMD, 0, + FRF_AZ_BUF_CLR_CMD, 1, + FRF_AZ_BUF_CLR_END_ID, end, + FRF_AZ_BUF_CLR_START_ID, start); + efx_writeo(efx, &buf_tbl_upd, FR_AZ_BUF_TBL_UPD); +} + +/* + * Allocate a new special buffer + * + * This allocates memory for a new buffer, clears it and allocates a + * new buffer ID range. It does not write into the buffer table. + * + * This call will allocate 4KB buffers, since 8KB buffers can't be + * used for event queues and descriptor rings. + */ +static int efx_alloc_special_buffer(struct efx_nic *efx, + struct efx_special_buffer *buffer, + unsigned int len) +{ + len = ALIGN(len, EFX_BUF_SIZE); + + buffer->addr = pci_alloc_consistent(efx->pci_dev, len, + &buffer->dma_addr); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + buffer->entries = len / EFX_BUF_SIZE; + BUG_ON(buffer->dma_addr & (EFX_BUF_SIZE - 1)); + + /* All zeros is a potentially valid event so memset to 0xff */ + memset(buffer->addr, 0xff, len); + + /* Select new buffer ID */ + buffer->index = efx->next_buffer_table; + efx->next_buffer_table += buffer->entries; + + EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (u64)buffer->dma_addr, len, + buffer->addr, (u64)virt_to_phys(buffer->addr)); + + return 0; +} + +static void +efx_free_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + if (!buffer->addr) + return; + + EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (u64)buffer->dma_addr, buffer->len, + buffer->addr, (u64)virt_to_phys(buffer->addr)); + + pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr, + buffer->dma_addr); + buffer->addr = NULL; + buffer->entries = 0; +} + +/************************************************************************** + * + * Generic buffer handling + * These buffers are used for interrupt status and MAC stats + * + **************************************************************************/ + +int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, + unsigned int len) +{ + buffer->addr = pci_alloc_consistent(efx->pci_dev, len, + &buffer->dma_addr); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + memset(buffer->addr, 0, len); + return 0; +} + +void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer) +{ + if (buffer->addr) { + pci_free_consistent(efx->pci_dev, buffer->len, + buffer->addr, buffer->dma_addr); + buffer->addr = NULL; + } +} + +/************************************************************************** + * + * TX path + * + **************************************************************************/ + +/* Returns a pointer to the specified transmit descriptor in the TX + * descriptor queue belonging to the specified channel. + */ +static inline efx_qword_t * +efx_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index) +{ + return (((efx_qword_t *) (tx_queue->txd.addr)) + index); +} + +/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */ +static inline void efx_notify_tx_desc(struct efx_tx_queue *tx_queue) +{ + unsigned write_ptr; + efx_dword_t reg; + + write_ptr = tx_queue->write_count & EFX_TXQ_MASK; + EFX_POPULATE_DWORD_1(reg, FRF_AZ_TX_DESC_WPTR_DWORD, write_ptr); + efx_writed_page(tx_queue->efx, ®, + FR_AZ_TX_DESC_UPD_DWORD_P0, tx_queue->queue); +} + + +/* For each entry inserted into the software descriptor ring, create a + * descriptor in the hardware TX descriptor ring (in host memory), and + * write a doorbell. + */ +void efx_nic_push_buffers(struct efx_tx_queue *tx_queue) +{ + + struct efx_tx_buffer *buffer; + efx_qword_t *txd; + unsigned write_ptr; + + BUG_ON(tx_queue->write_count == tx_queue->insert_count); + + do { + write_ptr = tx_queue->write_count & EFX_TXQ_MASK; + buffer = &tx_queue->buffer[write_ptr]; + txd = efx_tx_desc(tx_queue, write_ptr); + ++tx_queue->write_count; + + /* Create TX descriptor ring entry */ + EFX_POPULATE_QWORD_4(*txd, + FSF_AZ_TX_KER_CONT, buffer->continuation, + FSF_AZ_TX_KER_BYTE_COUNT, buffer->len, + FSF_AZ_TX_KER_BUF_REGION, 0, + FSF_AZ_TX_KER_BUF_ADDR, buffer->dma_addr); + } while (tx_queue->write_count != tx_queue->insert_count); + + wmb(); /* Ensure descriptors are written before they are fetched */ + efx_notify_tx_desc(tx_queue); +} + +/* Allocate hardware resources for a TX queue */ +int efx_nic_probe_tx(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + BUILD_BUG_ON(EFX_TXQ_SIZE < 512 || EFX_TXQ_SIZE > 4096 || + EFX_TXQ_SIZE & EFX_TXQ_MASK); + return efx_alloc_special_buffer(efx, &tx_queue->txd, + EFX_TXQ_SIZE * sizeof(efx_qword_t)); +} + +void efx_nic_init_tx(struct efx_tx_queue *tx_queue) +{ + efx_oword_t tx_desc_ptr; + struct efx_nic *efx = tx_queue->efx; + + tx_queue->flushed = FLUSH_NONE; + + /* Pin TX descriptor ring */ + efx_init_special_buffer(efx, &tx_queue->txd); + + /* Push TX descriptor ring to card */ + EFX_POPULATE_OWORD_10(tx_desc_ptr, + FRF_AZ_TX_DESCQ_EN, 1, + FRF_AZ_TX_ISCSI_DDIG_EN, 0, + FRF_AZ_TX_ISCSI_HDIG_EN, 0, + FRF_AZ_TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, + FRF_AZ_TX_DESCQ_EVQ_ID, + tx_queue->channel->channel, + FRF_AZ_TX_DESCQ_OWNER_ID, 0, + FRF_AZ_TX_DESCQ_LABEL, tx_queue->queue, + FRF_AZ_TX_DESCQ_SIZE, + __ffs(tx_queue->txd.entries), + FRF_AZ_TX_DESCQ_TYPE, 0, + FRF_BZ_TX_NON_IP_DROP_DIS, 1); + + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + int csum = tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM; + EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_IP_CHKSM_DIS, !csum); + EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_TCP_CHKSM_DIS, + !csum); + } + + efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { + efx_oword_t reg; + + /* Only 128 bits in this register */ + BUILD_BUG_ON(EFX_TX_QUEUE_COUNT >= 128); + + efx_reado(efx, ®, FR_AA_TX_CHKSM_CFG); + if (tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM) + clear_bit_le(tx_queue->queue, (void *)®); + else + set_bit_le(tx_queue->queue, (void *)®); + efx_writeo(efx, ®, FR_AA_TX_CHKSM_CFG); + } +} + +static void efx_flush_tx_queue(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + efx_oword_t tx_flush_descq; + + tx_queue->flushed = FLUSH_PENDING; + + /* Post a flush command */ + EFX_POPULATE_OWORD_2(tx_flush_descq, + FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, + FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); + efx_writeo(efx, &tx_flush_descq, FR_AZ_TX_FLUSH_DESCQ); +} + +void efx_nic_fini_tx(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + efx_oword_t tx_desc_ptr; + + /* The queue should have been flushed */ + WARN_ON(tx_queue->flushed != FLUSH_DONE); + + /* Remove TX descriptor ring from card */ + EFX_ZERO_OWORD(tx_desc_ptr); + efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + /* Unpin TX descriptor ring */ + efx_fini_special_buffer(efx, &tx_queue->txd); +} + +/* Free buffers backing TX queue */ +void efx_nic_remove_tx(struct efx_tx_queue *tx_queue) +{ + efx_free_special_buffer(tx_queue->efx, &tx_queue->txd); +} + +/************************************************************************** + * + * RX path + * + **************************************************************************/ + +/* Returns a pointer to the specified descriptor in the RX descriptor queue */ +static inline efx_qword_t * +efx_rx_desc(struct efx_rx_queue *rx_queue, unsigned int index) +{ + return (((efx_qword_t *) (rx_queue->rxd.addr)) + index); +} + +/* This creates an entry in the RX descriptor queue */ +static inline void +efx_build_rx_desc(struct efx_rx_queue *rx_queue, unsigned index) +{ + struct efx_rx_buffer *rx_buf; + efx_qword_t *rxd; + + rxd = efx_rx_desc(rx_queue, index); + rx_buf = efx_rx_buffer(rx_queue, index); + EFX_POPULATE_QWORD_3(*rxd, + FSF_AZ_RX_KER_BUF_SIZE, + rx_buf->len - + rx_queue->efx->type->rx_buffer_padding, + FSF_AZ_RX_KER_BUF_REGION, 0, + FSF_AZ_RX_KER_BUF_ADDR, rx_buf->dma_addr); +} + +/* This writes to the RX_DESC_WPTR register for the specified receive + * descriptor ring. + */ +void efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue) +{ + efx_dword_t reg; + unsigned write_ptr; + + while (rx_queue->notified_count != rx_queue->added_count) { + efx_build_rx_desc(rx_queue, + rx_queue->notified_count & + EFX_RXQ_MASK); + ++rx_queue->notified_count; + } + + wmb(); + write_ptr = rx_queue->added_count & EFX_RXQ_MASK; + EFX_POPULATE_DWORD_1(reg, FRF_AZ_RX_DESC_WPTR_DWORD, write_ptr); + efx_writed_page(rx_queue->efx, ®, + FR_AZ_RX_DESC_UPD_DWORD_P0, rx_queue->queue); +} + +int efx_nic_probe_rx(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + BUILD_BUG_ON(EFX_RXQ_SIZE < 512 || EFX_RXQ_SIZE > 4096 || + EFX_RXQ_SIZE & EFX_RXQ_MASK); + return efx_alloc_special_buffer(efx, &rx_queue->rxd, + EFX_RXQ_SIZE * sizeof(efx_qword_t)); +} + +void efx_nic_init_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + bool is_b0 = efx_nic_rev(efx) >= EFX_REV_FALCON_B0; + bool iscsi_digest_en = is_b0; + + EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n", + rx_queue->queue, rx_queue->rxd.index, + rx_queue->rxd.index + rx_queue->rxd.entries - 1); + + rx_queue->flushed = FLUSH_NONE; + + /* Pin RX descriptor ring */ + efx_init_special_buffer(efx, &rx_queue->rxd); + + /* Push RX descriptor ring to card */ + EFX_POPULATE_OWORD_10(rx_desc_ptr, + FRF_AZ_RX_ISCSI_DDIG_EN, iscsi_digest_en, + FRF_AZ_RX_ISCSI_HDIG_EN, iscsi_digest_en, + FRF_AZ_RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, + FRF_AZ_RX_DESCQ_EVQ_ID, + rx_queue->channel->channel, + FRF_AZ_RX_DESCQ_OWNER_ID, 0, + FRF_AZ_RX_DESCQ_LABEL, rx_queue->queue, + FRF_AZ_RX_DESCQ_SIZE, + __ffs(rx_queue->rxd.entries), + FRF_AZ_RX_DESCQ_TYPE, 0 /* kernel queue */ , + /* For >=B0 this is scatter so disable */ + FRF_AZ_RX_DESCQ_JUMBO, !is_b0, + FRF_AZ_RX_DESCQ_EN, 1); + efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); +} + +static void efx_flush_rx_queue(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + efx_oword_t rx_flush_descq; + + rx_queue->flushed = FLUSH_PENDING; + + /* Post a flush command */ + EFX_POPULATE_OWORD_2(rx_flush_descq, + FRF_AZ_RX_FLUSH_DESCQ_CMD, 1, + FRF_AZ_RX_FLUSH_DESCQ, rx_queue->queue); + efx_writeo(efx, &rx_flush_descq, FR_AZ_RX_FLUSH_DESCQ); +} + +void efx_nic_fini_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + + /* The queue should already have been flushed */ + WARN_ON(rx_queue->flushed != FLUSH_DONE); + + /* Remove RX descriptor ring from card */ + EFX_ZERO_OWORD(rx_desc_ptr); + efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); + + /* Unpin RX descriptor ring */ + efx_fini_special_buffer(efx, &rx_queue->rxd); +} + +/* Free buffers backing RX queue */ +void efx_nic_remove_rx(struct efx_rx_queue *rx_queue) +{ + efx_free_special_buffer(rx_queue->efx, &rx_queue->rxd); +} + +/************************************************************************** + * + * Event queue processing + * Event queues are processed by per-channel tasklets. + * + **************************************************************************/ + +/* Update a channel's event queue's read pointer (RPTR) register + * + * This writes the EVQ_RPTR_REG register for the specified channel's + * event queue. + * + * Note that EVQ_RPTR_REG contains the index of the "last read" event, + * whereas channel->eventq_read_ptr contains the index of the "next to + * read" event. + */ +void efx_nic_eventq_read_ack(struct efx_channel *channel) +{ + efx_dword_t reg; + struct efx_nic *efx = channel->efx; + + EFX_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR, channel->eventq_read_ptr); + efx_writed_table(efx, ®, efx->type->evq_rptr_tbl_base, + channel->channel); +} + +/* Use HW to insert a SW defined event */ +void efx_generate_event(struct efx_channel *channel, efx_qword_t *event) +{ + efx_oword_t drv_ev_reg; + + BUILD_BUG_ON(FRF_AZ_DRV_EV_DATA_LBN != 0 || + FRF_AZ_DRV_EV_DATA_WIDTH != 64); + drv_ev_reg.u32[0] = event->u32[0]; + drv_ev_reg.u32[1] = event->u32[1]; + drv_ev_reg.u32[2] = 0; + drv_ev_reg.u32[3] = 0; + EFX_SET_OWORD_FIELD(drv_ev_reg, FRF_AZ_DRV_EV_QID, channel->channel); + efx_writeo(channel->efx, &drv_ev_reg, FR_AZ_DRV_EV); +} + +/* Handle a transmit completion event + * + * The NIC batches TX completion events; the message we receive is of + * the form "complete all TX events up to this index". + */ +static void +efx_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) +{ + unsigned int tx_ev_desc_ptr; + unsigned int tx_ev_q_label; + struct efx_tx_queue *tx_queue; + struct efx_nic *efx = channel->efx; + + if (likely(EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_COMP))) { + /* Transmit completion */ + tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_DESC_PTR); + tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); + tx_queue = &efx->tx_queue[tx_ev_q_label]; + channel->irq_mod_score += + (tx_ev_desc_ptr - tx_queue->read_count) & + EFX_TXQ_MASK; + efx_xmit_done(tx_queue, tx_ev_desc_ptr); + } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_WQ_FF_FULL)) { + /* Rewrite the FIFO write pointer */ + tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); + tx_queue = &efx->tx_queue[tx_ev_q_label]; + + if (efx_dev_registered(efx)) + netif_tx_lock(efx->net_dev); + efx_notify_tx_desc(tx_queue); + if (efx_dev_registered(efx)) + netif_tx_unlock(efx->net_dev); + } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_PKT_ERR) && + EFX_WORKAROUND_10727(efx)) { + efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); + } else { + EFX_ERR(efx, "channel %d unexpected TX event " + EFX_QWORD_FMT"\n", channel->channel, + EFX_QWORD_VAL(*event)); + } +} + +/* Detect errors included in the rx_evt_pkt_ok bit. */ +static void efx_handle_rx_not_ok(struct efx_rx_queue *rx_queue, + const efx_qword_t *event, + bool *rx_ev_pkt_ok, + bool *discard) +{ + struct efx_nic *efx = rx_queue->efx; + bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; + bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; + bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; + bool rx_ev_other_err, rx_ev_pause_frm; + bool rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned rx_ev_pkt_type; + + rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); + rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); + rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_TOBE_DISC); + rx_ev_pkt_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_TYPE); + rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_BUF_OWNER_ID_ERR); + rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR); + rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR); + rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_ETH_CRC_ERR); + rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_FRM_TRUNC); + rx_ev_drib_nib = ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) ? + 0 : EFX_QWORD_FIELD(*event, FSF_AA_RX_EV_DRIB_NIB)); + rx_ev_pause_frm = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PAUSE_FRM_ERR); + + /* Every error apart from tobe_disc and pause_frm */ + rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | + rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | + rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); + + /* Count errors that are not in MAC stats. Ignore expected + * checksum errors during self-test. */ + if (rx_ev_frm_trunc) + ++rx_queue->channel->n_rx_frm_trunc; + else if (rx_ev_tobe_disc) + ++rx_queue->channel->n_rx_tobe_disc; + else if (!efx->loopback_selftest) { + if (rx_ev_ip_hdr_chksum_err) + ++rx_queue->channel->n_rx_ip_hdr_chksum_err; + else if (rx_ev_tcp_udp_chksum_err) + ++rx_queue->channel->n_rx_tcp_udp_chksum_err; + } + + /* The frame must be discarded if any of these are true. */ + *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | + rx_ev_tobe_disc | rx_ev_pause_frm); + + /* TOBE_DISC is expected on unicast mismatches; don't print out an + * error message. FRM_TRUNC indicates RXDP dropped the packet due + * to a FIFO overflow. + */ +#ifdef EFX_ENABLE_DEBUG + if (rx_ev_other_err) { + EFX_INFO_RL(efx, " RX queue %d unexpected RX event " + EFX_QWORD_FMT "%s%s%s%s%s%s%s%s\n", + rx_queue->queue, EFX_QWORD_VAL(*event), + rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", + rx_ev_ip_hdr_chksum_err ? + " [IP_HDR_CHKSUM_ERR]" : "", + rx_ev_tcp_udp_chksum_err ? + " [TCP_UDP_CHKSUM_ERR]" : "", + rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", + rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", + rx_ev_drib_nib ? " [DRIB_NIB]" : "", + rx_ev_tobe_disc ? " [TOBE_DISC]" : "", + rx_ev_pause_frm ? " [PAUSE]" : ""); + } +#endif +} + +/* Handle receive events that are not in-order. */ +static void +efx_handle_rx_bad_index(struct efx_rx_queue *rx_queue, unsigned index) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned expected, dropped; + + expected = rx_queue->removed_count & EFX_RXQ_MASK; + dropped = (index - expected) & EFX_RXQ_MASK; + EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n", + dropped, index, expected); + + efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); +} + +/* Handle a packet received event + * + * The NIC gives a "discard" flag if it's a unicast packet with the + * wrong destination address + * Also "is multicast" and "matches multicast filter" flags can be used to + * discard non-matching multicast packets. + */ +static void +efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event) +{ + unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt; + unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned expected_ptr; + bool rx_ev_pkt_ok, discard = false, checksummed; + struct efx_rx_queue *rx_queue; + struct efx_nic *efx = channel->efx; + + /* Basic packet information */ + rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_BYTE_CNT); + rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_OK); + rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_JUMBO_CONT)); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_SOP) != 1); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_Q_LABEL) != + channel->channel); + + rx_queue = &efx->rx_queue[channel->channel]; + + rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_DESC_PTR); + expected_ptr = rx_queue->removed_count & EFX_RXQ_MASK; + if (unlikely(rx_ev_desc_ptr != expected_ptr)) + efx_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr); + + if (likely(rx_ev_pkt_ok)) { + /* If packet is marked as OK and packet type is TCP/IP or + * UDP/IP, then we can rely on the hardware checksum. + */ + checksummed = + likely(efx->rx_checksum_enabled) && + (rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP || + rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP); + } else { + efx_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, &discard); + checksummed = false; + } + + /* Detect multicast packets that didn't match the filter */ + rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); + if (rx_ev_mcast_pkt) { + unsigned int rx_ev_mcast_hash_match = + EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_HASH_MATCH); + + if (unlikely(!rx_ev_mcast_hash_match)) { + ++channel->n_rx_mcast_mismatch; + discard = true; + } + } + + channel->irq_mod_score += 2; + + /* Handle received packet */ + efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, + checksummed, discard); +} + +/* Global events are basically PHY events */ +static void +efx_handle_global_event(struct efx_channel *channel, efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + bool handled = false; + + if (EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_G_PHY0_INTR) || + EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XG_PHY0_INTR) || + EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XFP_PHY0_INTR)) { + /* Ignored */ + handled = true; + } + + if ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) && + EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_XG_MGT_INTR)) { + efx->xmac_poll_required = true; + handled = true; + } + + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1 ? + EFX_QWORD_FIELD(*event, FSF_AA_GLB_EV_RX_RECOVERY) : + EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_RX_RECOVERY)) { + EFX_ERR(efx, "channel %d seen global RX_RESET " + "event. Resetting.\n", channel->channel); + + atomic_inc(&efx->rx_reset); + efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); + handled = true; + } + + if (!handled) + EFX_ERR(efx, "channel %d unknown global event " + EFX_QWORD_FMT "\n", channel->channel, + EFX_QWORD_VAL(*event)); +} + +static void +efx_handle_driver_event(struct efx_channel *channel, efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + unsigned int ev_sub_code; + unsigned int ev_sub_data; + + ev_sub_code = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBCODE); + ev_sub_data = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBDATA); + + switch (ev_sub_code) { + case FSE_AZ_TX_DESCQ_FLS_DONE_EV: + EFX_TRACE(efx, "channel %d TXQ %d flushed\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_RX_DESCQ_FLS_DONE_EV: + EFX_TRACE(efx, "channel %d RXQ %d flushed\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_EVQ_INIT_DONE_EV: + EFX_LOG(efx, "channel %d EVQ %d initialised\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_SRM_UPD_DONE_EV: + EFX_TRACE(efx, "channel %d SRAM update done\n", + channel->channel); + break; + case FSE_AZ_WAKE_UP_EV: + EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_TIMER_EV: + EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", + channel->channel, ev_sub_data); + break; + case FSE_AA_RX_RECOVER_EV: + EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " + "Resetting.\n", channel->channel); + atomic_inc(&efx->rx_reset); + efx_schedule_reset(efx, + EFX_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : + RESET_TYPE_DISABLE); + break; + case FSE_BZ_RX_DSC_ERROR_EV: + EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error." + " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data); + efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH); + break; + case FSE_BZ_TX_DSC_ERROR_EV: + EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error." + " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data); + efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); + break; + default: + EFX_TRACE(efx, "channel %d unknown driver event code %d " + "data %04x\n", channel->channel, ev_sub_code, + ev_sub_data); + break; + } +} + +int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota) +{ + unsigned int read_ptr; + efx_qword_t event, *p_event; + int ev_code; + int rx_packets = 0; + + read_ptr = channel->eventq_read_ptr; + + do { + p_event = efx_event(channel, read_ptr); + event = *p_event; + + if (!efx_event_present(&event)) + /* End of events */ + break; + + EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n", + channel->channel, EFX_QWORD_VAL(event)); + + /* Clear this event by marking it all ones */ + EFX_SET_QWORD(*p_event); + + ev_code = EFX_QWORD_FIELD(event, FSF_AZ_EV_CODE); + + switch (ev_code) { + case FSE_AZ_EV_CODE_RX_EV: + efx_handle_rx_event(channel, &event); + ++rx_packets; + break; + case FSE_AZ_EV_CODE_TX_EV: + efx_handle_tx_event(channel, &event); + break; + case FSE_AZ_EV_CODE_DRV_GEN_EV: + channel->eventq_magic = EFX_QWORD_FIELD( + event, FSF_AZ_DRV_GEN_EV_MAGIC); + EFX_LOG(channel->efx, "channel %d received generated " + "event "EFX_QWORD_FMT"\n", channel->channel, + EFX_QWORD_VAL(event)); + break; + case FSE_AZ_EV_CODE_GLOBAL_EV: + efx_handle_global_event(channel, &event); + break; + case FSE_AZ_EV_CODE_DRIVER_EV: + efx_handle_driver_event(channel, &event); + break; + case FSE_CZ_EV_CODE_MCDI_EV: + efx_mcdi_process_event(channel, &event); + break; + default: + EFX_ERR(channel->efx, "channel %d unknown event type %d" + " (data " EFX_QWORD_FMT ")\n", channel->channel, + ev_code, EFX_QWORD_VAL(event)); + } + + /* Increment read pointer */ + read_ptr = (read_ptr + 1) & EFX_EVQ_MASK; + + } while (rx_packets < rx_quota); + + channel->eventq_read_ptr = read_ptr; + return rx_packets; +} + + +/* Allocate buffer table entries for event queue */ +int efx_nic_probe_eventq(struct efx_channel *channel) +{ + struct efx_nic *efx = channel->efx; + BUILD_BUG_ON(EFX_EVQ_SIZE < 512 || EFX_EVQ_SIZE > 32768 || + EFX_EVQ_SIZE & EFX_EVQ_MASK); + return efx_alloc_special_buffer(efx, &channel->eventq, + EFX_EVQ_SIZE * sizeof(efx_qword_t)); +} + +void efx_nic_init_eventq(struct efx_channel *channel) +{ + efx_oword_t reg; + struct efx_nic *efx = channel->efx; + + EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n", + channel->channel, channel->eventq.index, + channel->eventq.index + channel->eventq.entries - 1); + + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { + EFX_POPULATE_OWORD_3(reg, + FRF_CZ_TIMER_Q_EN, 1, + FRF_CZ_HOST_NOTIFY_MODE, 0, + FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS); + efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); + } + + /* Pin event queue buffer */ + efx_init_special_buffer(efx, &channel->eventq); + + /* Fill event queue with all ones (i.e. empty events) */ + memset(channel->eventq.addr, 0xff, channel->eventq.len); + + /* Push event queue to card */ + EFX_POPULATE_OWORD_3(reg, + FRF_AZ_EVQ_EN, 1, + FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries), + FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index); + efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, + channel->channel); + + efx->type->push_irq_moderation(channel); +} + +void efx_nic_fini_eventq(struct efx_channel *channel) +{ + efx_oword_t reg; + struct efx_nic *efx = channel->efx; + + /* Remove event queue from card */ + EFX_ZERO_OWORD(reg); + efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, + channel->channel); + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) + efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); + + /* Unpin event queue */ + efx_fini_special_buffer(efx, &channel->eventq); +} + +/* Free buffers backing event queue */ +void efx_nic_remove_eventq(struct efx_channel *channel) +{ + efx_free_special_buffer(channel->efx, &channel->eventq); +} + + +/* Generates a test event on the event queue. A subsequent call to + * process_eventq() should pick up the event and place the value of + * "magic" into channel->eventq_magic; + */ +void efx_nic_generate_test_event(struct efx_channel *channel, unsigned int magic) +{ + efx_qword_t test_event; + + EFX_POPULATE_QWORD_2(test_event, FSF_AZ_EV_CODE, + FSE_AZ_EV_CODE_DRV_GEN_EV, + FSF_AZ_DRV_GEN_EV_MAGIC, magic); + efx_generate_event(channel, &test_event); +} + +/************************************************************************** + * + * Flush handling + * + **************************************************************************/ + + +static void efx_poll_flush_events(struct efx_nic *efx) +{ + struct efx_channel *channel = &efx->channel[0]; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + unsigned int read_ptr = channel->eventq_read_ptr; + unsigned int end_ptr = (read_ptr - 1) & EFX_EVQ_MASK; + + do { + efx_qword_t *event = efx_event(channel, read_ptr); + int ev_code, ev_sub_code, ev_queue; + bool ev_failed; + + if (!efx_event_present(event)) + break; + + ev_code = EFX_QWORD_FIELD(*event, FSF_AZ_EV_CODE); + ev_sub_code = EFX_QWORD_FIELD(*event, + FSF_AZ_DRIVER_EV_SUBCODE); + if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV && + ev_sub_code == FSE_AZ_TX_DESCQ_FLS_DONE_EV) { + ev_queue = EFX_QWORD_FIELD(*event, + FSF_AZ_DRIVER_EV_SUBDATA); + if (ev_queue < EFX_TX_QUEUE_COUNT) { + tx_queue = efx->tx_queue + ev_queue; + tx_queue->flushed = FLUSH_DONE; + } + } else if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV && + ev_sub_code == FSE_AZ_RX_DESCQ_FLS_DONE_EV) { + ev_queue = EFX_QWORD_FIELD( + *event, FSF_AZ_DRIVER_EV_RX_DESCQ_ID); + ev_failed = EFX_QWORD_FIELD( + *event, FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL); + if (ev_queue < efx->n_rx_queues) { + rx_queue = efx->rx_queue + ev_queue; + rx_queue->flushed = + ev_failed ? FLUSH_FAILED : FLUSH_DONE; + } + } + + /* We're about to destroy the queue anyway, so + * it's ok to throw away every non-flush event */ + EFX_SET_QWORD(*event); + + read_ptr = (read_ptr + 1) & EFX_EVQ_MASK; + } while (read_ptr != end_ptr); + + channel->eventq_read_ptr = read_ptr; +} + +/* Handle tx and rx flushes at the same time, since they run in + * parallel in the hardware and there's no reason for us to + * serialise them */ +int efx_nic_flush_queues(struct efx_nic *efx) +{ + struct efx_rx_queue *rx_queue; + struct efx_tx_queue *tx_queue; + int i, tx_pending, rx_pending; + + /* If necessary prepare the hardware for flushing */ + efx->type->prepare_flush(efx); + + /* Flush all tx queues in parallel */ + efx_for_each_tx_queue(tx_queue, efx) + efx_flush_tx_queue(tx_queue); + + /* The hardware supports four concurrent rx flushes, each of which may + * need to be retried if there is an outstanding descriptor fetch */ + for (i = 0; i < EFX_FLUSH_POLL_COUNT; ++i) { + rx_pending = tx_pending = 0; + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_queue->flushed == FLUSH_PENDING) + ++rx_pending; + } + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_pending == EFX_RX_FLUSH_COUNT) + break; + if (rx_queue->flushed == FLUSH_FAILED || + rx_queue->flushed == FLUSH_NONE) { + efx_flush_rx_queue(rx_queue); + ++rx_pending; + } + } + efx_for_each_tx_queue(tx_queue, efx) { + if (tx_queue->flushed != FLUSH_DONE) + ++tx_pending; + } + + if (rx_pending == 0 && tx_pending == 0) + return 0; + + msleep(EFX_FLUSH_INTERVAL); + efx_poll_flush_events(efx); + } + + /* Mark the queues as all flushed. We're going to return failure + * leading to a reset, or fake up success anyway */ + efx_for_each_tx_queue(tx_queue, efx) { + if (tx_queue->flushed != FLUSH_DONE) + EFX_ERR(efx, "tx queue %d flush command timed out\n", + tx_queue->queue); + tx_queue->flushed = FLUSH_DONE; + } + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_queue->flushed != FLUSH_DONE) + EFX_ERR(efx, "rx queue %d flush command timed out\n", + rx_queue->queue); + rx_queue->flushed = FLUSH_DONE; + } + + if (EFX_WORKAROUND_7803(efx)) + return 0; + + return -ETIMEDOUT; +} + +/************************************************************************** + * + * Hardware interrupts + * The hardware interrupt handler does very little work; all the event + * queue processing is carried out by per-channel tasklets. + * + **************************************************************************/ + +/* Enable/disable/generate interrupts */ +static inline void efx_nic_interrupts(struct efx_nic *efx, + bool enabled, bool force) +{ + efx_oword_t int_en_reg_ker; + unsigned int level = 0; + + if (EFX_WORKAROUND_17213(efx) && !EFX_INT_MODE_USE_MSI(efx)) + /* Set the level always even if we're generating a test + * interrupt, because our legacy interrupt handler is safe */ + level = 0x1f; + + EFX_POPULATE_OWORD_3(int_en_reg_ker, + FRF_AZ_KER_INT_LEVE_SEL, level, + FRF_AZ_KER_INT_KER, force, + FRF_AZ_DRV_INT_EN_KER, enabled); + efx_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER); +} + +void efx_nic_enable_interrupts(struct efx_nic *efx) +{ + struct efx_channel *channel; + + EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr)); + wmb(); /* Ensure interrupt vector is clear before interrupts enabled */ + + /* Enable interrupts */ + efx_nic_interrupts(efx, true, false); + + /* Force processing of all the channels to get the EVQ RPTRs up to + date */ + efx_for_each_channel(channel, efx) + efx_schedule_channel(channel); +} + +void efx_nic_disable_interrupts(struct efx_nic *efx) +{ + /* Disable interrupts */ + efx_nic_interrupts(efx, false, false); +} + +/* Generate a test interrupt + * Interrupt must already have been enabled, otherwise nasty things + * may happen. + */ +void efx_nic_generate_interrupt(struct efx_nic *efx) +{ + efx_nic_interrupts(efx, true, true); +} + +/* Process a fatal interrupt + * Disable bus mastering ASAP and schedule a reset + */ +irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t *int_ker = efx->irq_status.addr; + efx_oword_t fatal_intr; + int error, mem_perr; + + efx_reado(efx, &fatal_intr, FR_AZ_FATAL_INTR_KER); + error = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_FATAL_INTR); + + EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status " + EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker), + EFX_OWORD_VAL(fatal_intr), + error ? "disabling bus mastering" : "no recognised error"); + if (error == 0) + goto out; + + /* If this is a memory parity error dump which blocks are offending */ + mem_perr = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_MEM_PERR_INT_KER); + if (mem_perr) { + efx_oword_t reg; + efx_reado(efx, ®, FR_AZ_MEM_STAT); + EFX_ERR(efx, "SYSTEM ERROR: memory parity error " + EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg)); + } + + /* Disable both devices */ + pci_clear_master(efx->pci_dev); + if (efx_nic_is_dual_func(efx)) + pci_clear_master(nic_data->pci_dev2); + efx_nic_disable_interrupts(efx); + + /* Count errors and reset or disable the NIC accordingly */ + if (efx->int_error_count == 0 || + time_after(jiffies, efx->int_error_expire)) { + efx->int_error_count = 0; + efx->int_error_expire = + jiffies + EFX_INT_ERROR_EXPIRE * HZ; + } + if (++efx->int_error_count < EFX_MAX_INT_ERRORS) { + EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n"); + efx_schedule_reset(efx, RESET_TYPE_INT_ERROR); + } else { + EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen." + "NIC will be disabled\n"); + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + } +out: + return IRQ_HANDLED; +} + +/* Handle a legacy interrupt + * Acknowledges the interrupt and schedule event queue processing. + */ +static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id) +{ + struct efx_nic *efx = dev_id; + efx_oword_t *int_ker = efx->irq_status.addr; + irqreturn_t result = IRQ_NONE; + struct efx_channel *channel; + efx_dword_t reg; + u32 queues; + int syserr; + + /* Read the ISR which also ACKs the interrupts */ + efx_readd(efx, ®, FR_BZ_INT_ISR0); + queues = EFX_EXTRACT_DWORD(reg, 0, 31); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return efx_nic_fatal_interrupt(efx); + + if (queues != 0) { + if (EFX_WORKAROUND_15783(efx)) + efx->irq_zero_count = 0; + + /* Schedule processing of any interrupting queues */ + efx_for_each_channel(channel, efx) { + if (queues & 1) + efx_schedule_channel(channel); + queues >>= 1; + } + result = IRQ_HANDLED; + + } else if (EFX_WORKAROUND_15783(efx) && + efx->irq_zero_count++ == 0) { + efx_qword_t *event; + + /* Ensure we rearm all event queues */ + efx_for_each_channel(channel, efx) { + event = efx_event(channel, channel->eventq_read_ptr); + if (efx_event_present(event)) + efx_schedule_channel(channel); + } + + result = IRQ_HANDLED; + } + + if (result == IRQ_HANDLED) { + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg)); + } + + return result; +} + +/* Handle an MSI interrupt + * + * Handle an MSI hardware interrupt. This routine schedules event + * queue processing. No interrupt acknowledgement cycle is necessary. + * Also, we never need to check that the interrupt is for us, since + * MSI interrupts cannot be shared. + */ +static irqreturn_t efx_msi_interrupt(int irq, void *dev_id) +{ + struct efx_channel *channel = dev_id; + struct efx_nic *efx = channel->efx; + efx_oword_t *int_ker = efx->irq_status.addr; + int syserr; + + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return efx_nic_fatal_interrupt(efx); + + /* Schedule processing of the channel */ + efx_schedule_channel(channel); + + return IRQ_HANDLED; +} + + +/* Setup RSS indirection table. + * This maps from the hash value of the packet to RXQ + */ +static void efx_setup_rss_indir_table(struct efx_nic *efx) +{ + int i = 0; + unsigned long offset; + efx_dword_t dword; + + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) + return; + + for (offset = FR_BZ_RX_INDIRECTION_TBL; + offset < FR_BZ_RX_INDIRECTION_TBL + 0x800; + offset += 0x10) { + EFX_POPULATE_DWORD_1(dword, FRF_BZ_IT_QUEUE, + i % efx->n_rx_queues); + efx_writed(efx, &dword, offset); + i++; + } +} + +/* Hook interrupt handler(s) + * Try MSI and then legacy interrupts. + */ +int efx_nic_init_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + int rc; + + if (!EFX_INT_MODE_USE_MSI(efx)) { + irq_handler_t handler; + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + handler = efx_legacy_interrupt; + else + handler = falcon_legacy_interrupt_a1; + + rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED, + efx->name, efx); + if (rc) { + EFX_ERR(efx, "failed to hook legacy IRQ %d\n", + efx->pci_dev->irq); + goto fail1; + } + return 0; + } + + /* Hook MSI or MSI-X interrupt */ + efx_for_each_channel(channel, efx) { + rc = request_irq(channel->irq, efx_msi_interrupt, + IRQF_PROBE_SHARED, /* Not shared */ + channel->name, channel); + if (rc) { + EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq); + goto fail2; + } + } + + return 0; + + fail2: + efx_for_each_channel(channel, efx) + free_irq(channel->irq, channel); + fail1: + return rc; +} + +void efx_nic_fini_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + efx_oword_t reg; + + /* Disable MSI/MSI-X interrupts */ + efx_for_each_channel(channel, efx) { + if (channel->irq) + free_irq(channel->irq, channel); + } + + /* ACK legacy interrupt */ + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + efx_reado(efx, ®, FR_BZ_INT_ISR0); + else + falcon_irq_ack_a1(efx); + + /* Disable legacy interrupt */ + if (efx->legacy_irq) + free_irq(efx->legacy_irq, efx); +} + +u32 efx_nic_fpga_ver(struct efx_nic *efx) +{ + efx_oword_t altera_build; + efx_reado(efx, &altera_build, FR_AZ_ALTERA_BUILD); + return EFX_OWORD_FIELD(altera_build, FRF_AZ_ALTERA_BUILD_VER); +} + +void efx_nic_init_common(struct efx_nic *efx) +{ + efx_oword_t temp; + + /* Set positions of descriptor caches in SRAM. */ + EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_TX_DC_BASE_ADR, + efx->type->tx_dc_base / 8); + efx_writeo(efx, &temp, FR_AZ_SRM_TX_DC_CFG); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_RX_DC_BASE_ADR, + efx->type->rx_dc_base / 8); + efx_writeo(efx, &temp, FR_AZ_SRM_RX_DC_CFG); + + /* Set TX descriptor cache size. */ + BUILD_BUG_ON(TX_DC_ENTRIES != (8 << TX_DC_ENTRIES_ORDER)); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_TX_DC_SIZE, TX_DC_ENTRIES_ORDER); + efx_writeo(efx, &temp, FR_AZ_TX_DC_CFG); + + /* Set RX descriptor cache size. Set low watermark to size-8, as + * this allows most efficient prefetching. + */ + BUILD_BUG_ON(RX_DC_ENTRIES != (8 << RX_DC_ENTRIES_ORDER)); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_SIZE, RX_DC_ENTRIES_ORDER); + efx_writeo(efx, &temp, FR_AZ_RX_DC_CFG); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_PF_LWM, RX_DC_ENTRIES - 8); + efx_writeo(efx, &temp, FR_AZ_RX_DC_PF_WM); + + /* Program INT_KER address */ + EFX_POPULATE_OWORD_2(temp, + FRF_AZ_NORM_INT_VEC_DIS_KER, + EFX_INT_MODE_USE_MSI(efx), + FRF_AZ_INT_ADR_KER, efx->irq_status.dma_addr); + efx_writeo(efx, &temp, FR_AZ_INT_ADR_KER); + + /* Enable all the genuinely fatal interrupts. (They are still + * masked by the overall interrupt mask, controlled by + * falcon_interrupts()). + * + * Note: All other fatal interrupts are enabled + */ + EFX_POPULATE_OWORD_3(temp, + FRF_AZ_ILL_ADR_INT_KER_EN, 1, + FRF_AZ_RBUF_OWN_INT_KER_EN, 1, + FRF_AZ_TBUF_OWN_INT_KER_EN, 1); + EFX_INVERT_OWORD(temp); + efx_writeo(efx, &temp, FR_AZ_FATAL_INTR_KER); + + efx_setup_rss_indir_table(efx); + + /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be + * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q. + */ + efx_reado(efx, &temp, FR_AZ_TX_RESERVED); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER, 0xfe); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER_EN, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_ONE_PKT_PER_Q, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_DIS_NON_IP_EV, 1); + /* Enable SW_EV to inherit in char driver - assume harmless here */ + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1); + /* Prefetch threshold 2 => fetch when descriptor cache half empty */ + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_THRESHOLD, 2); + /* Squash TX of packets of 16 bytes or less */ + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); + efx_writeo(efx, &temp, FR_AZ_TX_RESERVED); +} diff --git a/drivers/net/sfc/nic.h b/drivers/net/sfc/nic.h new file mode 100644 index 000000000000..9351c0331a47 --- /dev/null +++ b/drivers/net/sfc/nic.h @@ -0,0 +1,261 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#ifndef EFX_NIC_H +#define EFX_NIC_H + +#include <linux/i2c-algo-bit.h> +#include "net_driver.h" +#include "efx.h" +#include "mcdi.h" + +/* + * Falcon hardware control + */ + +enum { + EFX_REV_FALCON_A0 = 0, + EFX_REV_FALCON_A1 = 1, + EFX_REV_FALCON_B0 = 2, + EFX_REV_SIENA_A0 = 3, +}; + +static inline int efx_nic_rev(struct efx_nic *efx) +{ + return efx->type->revision; +} + +extern u32 efx_nic_fpga_ver(struct efx_nic *efx); + +static inline bool efx_nic_has_mc(struct efx_nic *efx) +{ + return efx_nic_rev(efx) >= EFX_REV_SIENA_A0; +} +/* NIC has two interlinked PCI functions for the same port. */ +static inline bool efx_nic_is_dual_func(struct efx_nic *efx) +{ + return efx_nic_rev(efx) < EFX_REV_FALCON_B0; +} + +enum { + PHY_TYPE_NONE = 0, + PHY_TYPE_TXC43128 = 1, + PHY_TYPE_88E1111 = 2, + PHY_TYPE_SFX7101 = 3, + PHY_TYPE_QT2022C2 = 4, + PHY_TYPE_PM8358 = 6, + PHY_TYPE_SFT9001A = 8, + PHY_TYPE_QT2025C = 9, + PHY_TYPE_SFT9001B = 10, +}; + +#define FALCON_XMAC_LOOPBACKS \ + ((1 << LOOPBACK_XGMII) | \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI)) + +#define FALCON_GMAC_LOOPBACKS \ + (1 << LOOPBACK_GMAC) + +/** + * struct falcon_board_type - board operations and type information + * @id: Board type id, as found in NVRAM + * @ref_model: Model number of Solarflare reference design + * @gen_type: Generic board type description + * @init: Allocate resources and initialise peripheral hardware + * @init_phy: Do board-specific PHY initialisation + * @fini: Shut down hardware and free resources + * @set_id_led: Set state of identifying LED or revert to automatic function + * @monitor: Board-specific health check function + */ +struct falcon_board_type { + u8 id; + const char *ref_model; + const char *gen_type; + int (*init) (struct efx_nic *nic); + void (*init_phy) (struct efx_nic *efx); + void (*fini) (struct efx_nic *nic); + void (*set_id_led) (struct efx_nic *efx, enum efx_led_mode mode); + int (*monitor) (struct efx_nic *nic); +}; + +/** + * struct falcon_board - board information + * @type: Type of board + * @major: Major rev. ('A', 'B' ...) + * @minor: Minor rev. (0, 1, ...) + * @i2c_adap: I2C adapter for on-board peripherals + * @i2c_data: Data for bit-banging algorithm + * @hwmon_client: I2C client for hardware monitor + * @ioexp_client: I2C client for power/port control + */ +struct falcon_board { + const struct falcon_board_type *type; + int major; + int minor; + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_data; + struct i2c_client *hwmon_client, *ioexp_client; +}; + +/** + * struct falcon_nic_data - Falcon NIC state + * @pci_dev2: Secondary function of Falcon A + * @board: Board state and functions + * @stats_disable_count: Nest count for disabling statistics fetches + * @stats_pending: Is there a pending DMA of MAC statistics. + * @stats_timer: A timer for regularly fetching MAC statistics. + * @stats_dma_done: Pointer to the flag which indicates DMA completion. + */ +struct falcon_nic_data { + struct pci_dev *pci_dev2; + struct falcon_board board; + unsigned int stats_disable_count; + bool stats_pending; + struct timer_list stats_timer; + u32 *stats_dma_done; +}; + +static inline struct falcon_board *falcon_board(struct efx_nic *efx) +{ + struct falcon_nic_data *data = efx->nic_data; + return &data->board; +} + +/** + * struct siena_nic_data - Siena NIC state + * @fw_version: Management controller firmware version + * @fw_build: Firmware build number + * @mcdi: Management-Controller-to-Driver Interface + * @wol_filter_id: Wake-on-LAN packet filter id + */ +struct siena_nic_data { + u64 fw_version; + u32 fw_build; + struct efx_mcdi_iface mcdi; + int wol_filter_id; +}; + +extern void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len); + +extern struct efx_nic_type falcon_a1_nic_type; +extern struct efx_nic_type falcon_b0_nic_type; +extern struct efx_nic_type siena_a0_nic_type; + +/************************************************************************** + * + * Externs + * + ************************************************************************** + */ + +extern void falcon_probe_board(struct efx_nic *efx, u16 revision_info); + +/* TX data path */ +extern int efx_nic_probe_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_init_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_fini_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_remove_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_push_buffers(struct efx_tx_queue *tx_queue); + +/* RX data path */ +extern int efx_nic_probe_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_init_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_fini_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_remove_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue); + +/* Event data path */ +extern int efx_nic_probe_eventq(struct efx_channel *channel); +extern void efx_nic_init_eventq(struct efx_channel *channel); +extern void efx_nic_fini_eventq(struct efx_channel *channel); +extern void efx_nic_remove_eventq(struct efx_channel *channel); +extern int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota); +extern void efx_nic_eventq_read_ack(struct efx_channel *channel); + +/* MAC/PHY */ +extern void falcon_drain_tx_fifo(struct efx_nic *efx); +extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); +extern int efx_nic_rx_xoff_thresh, efx_nic_rx_xon_thresh; + +/* Interrupts and test events */ +extern int efx_nic_init_interrupt(struct efx_nic *efx); +extern void efx_nic_enable_interrupts(struct efx_nic *efx); +extern void efx_nic_generate_test_event(struct efx_channel *channel, + unsigned int magic); +extern void efx_nic_generate_interrupt(struct efx_nic *efx); +extern void efx_nic_disable_interrupts(struct efx_nic *efx); +extern void efx_nic_fini_interrupt(struct efx_nic *efx); +extern irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx); +extern irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id); +extern void falcon_irq_ack_a1(struct efx_nic *efx); + +#define EFX_IRQ_MOD_RESOLUTION 5 + +/* Global Resources */ +extern int efx_nic_flush_queues(struct efx_nic *efx); +extern void falcon_start_nic_stats(struct efx_nic *efx); +extern void falcon_stop_nic_stats(struct efx_nic *efx); +extern int falcon_reset_xaui(struct efx_nic *efx); +extern void efx_nic_init_common(struct efx_nic *efx); + +int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, + unsigned int len); +void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer); + +/* Tests */ +struct efx_nic_register_test { + unsigned address; + efx_oword_t mask; +}; +extern int efx_nic_test_registers(struct efx_nic *efx, + const struct efx_nic_register_test *regs, + size_t n_regs); + +/************************************************************************** + * + * Falcon MAC stats + * + ************************************************************************** + */ + +#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset) +#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH) + +/* Retrieve statistic from statistics block */ +#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \ + if (FALCON_STAT_WIDTH(falcon_stat) == 16) \ + (efx)->mac_stats.efx_stat += le16_to_cpu( \ + *((__force __le16 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \ + (efx)->mac_stats.efx_stat += le32_to_cpu( \ + *((__force __le32 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else \ + (efx)->mac_stats.efx_stat += le64_to_cpu( \ + *((__force __le64 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + } while (0) + +#define FALCON_MAC_STATS_SIZE 0x100 + +#define MAC_DATA_LBN 0 +#define MAC_DATA_WIDTH 32 + +extern void efx_nic_generate_event(struct efx_channel *channel, + efx_qword_t *event); + +extern void falcon_poll_xmac(struct efx_nic *efx); + +#endif /* EFX_NIC_H */ diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h index c1cff9c0c173..5bc26137257b 100644 --- a/drivers/net/sfc/phy.h +++ b/drivers/net/sfc/phy.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. + * Copyright 2007-2009 Solarflare Communications Inc. * * 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 @@ -16,16 +16,16 @@ extern struct efx_phy_operations falcon_sfx7101_phy_ops; extern struct efx_phy_operations falcon_sft9001_phy_ops; -extern void tenxpress_phy_blink(struct efx_nic *efx, bool blink); +extern void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); /* Wait for the PHY to boot. Return 0 on success, -EINVAL if the PHY failed * to boot due to corrupt flash, or some other negative error code. */ extern int sft9001_wait_boot(struct efx_nic *efx); /**************************************************************************** - * AMCC/Quake QT20xx PHYs + * AMCC/Quake QT202x PHYs */ -extern struct efx_phy_operations falcon_xfp_phy_ops; +extern struct efx_phy_operations falcon_qt202x_phy_ops; /* These PHYs provide various H/W control states for LEDs */ #define QUAKE_LED_LINK_INVAL (0) @@ -39,6 +39,23 @@ extern struct efx_phy_operations falcon_xfp_phy_ops; #define QUAKE_LED_TXLINK (0) #define QUAKE_LED_RXLINK (8) -extern void xfp_set_led(struct efx_nic *p, int led, int state); +extern void falcon_qt202x_set_led(struct efx_nic *p, int led, int state); + +/**************************************************************************** + * Siena managed PHYs + */ +extern struct efx_phy_operations efx_mcdi_phy_ops; + +extern int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, + u16 addr, u16 *value_out, u32 *status_out); +extern int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, + u16 addr, u16 value, u32 *status_out); +extern void efx_mcdi_phy_decode_link(struct efx_nic *efx, + struct efx_link_state *link_state, + u32 speed, u32 flags, u32 fcntl); +extern int efx_mcdi_phy_reconfigure(struct efx_nic *efx); +extern void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa); #endif diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/qt202x_phy.c index e6b3d5eaddba..3800fc791b2f 100644 --- a/drivers/net/sfc/xfp_phy.c +++ b/drivers/net/sfc/qt202x_phy.c @@ -1,14 +1,13 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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, incorporated herein by reference. */ /* - * Driver for SFP+ and XFP optical PHYs plus some support specific to the - * AMCC QT20xx adapters; see www.amcc.com for details + * Driver for AMCC QT202x SFP+ and XFP adapters; see www.amcc.com for details */ #include <linux/timer.h> @@ -16,15 +15,15 @@ #include "efx.h" #include "mdio_10g.h" #include "phy.h" -#include "falcon.h" +#include "nic.h" -#define XFP_REQUIRED_DEVS (MDIO_DEVS_PCS | \ - MDIO_DEVS_PMAPMD | \ - MDIO_DEVS_PHYXS) +#define QT202X_REQUIRED_DEVS (MDIO_DEVS_PCS | \ + MDIO_DEVS_PMAPMD | \ + MDIO_DEVS_PHYXS) -#define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \ - (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) +#define QT202X_LOOPBACKS ((1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_PHYXS_WS)) /****************************************************************************/ /* Quake-specific MDIO registers */ @@ -45,18 +44,18 @@ #define PCS_VEND1_REG 0xc000 #define PCS_VEND1_LBTXD_LBN 5 -void xfp_set_led(struct efx_nic *p, int led, int mode) +void falcon_qt202x_set_led(struct efx_nic *p, int led, int mode) { int addr = MDIO_QUAKE_LED0_REG + led; efx_mdio_write(p, MDIO_MMD_PMAPMD, addr, mode); } -struct xfp_phy_data { +struct qt202x_phy_data { enum efx_phy_mode phy_mode; }; -#define XFP_MAX_RESET_TIME 500 -#define XFP_RESET_WAIT 10 +#define QT2022C2_MAX_RESET_TIME 500 +#define QT2022C2_RESET_WAIT 10 static int qt2025c_wait_reset(struct efx_nic *efx) { @@ -97,7 +96,7 @@ static int qt2025c_wait_reset(struct efx_nic *efx) return 0; } -static int xfp_reset_phy(struct efx_nic *efx) +static int qt202x_reset_phy(struct efx_nic *efx) { int rc; @@ -111,8 +110,9 @@ static int xfp_reset_phy(struct efx_nic *efx) /* Reset the PHYXS MMD. This is documented as doing * a complete soft reset. */ rc = efx_mdio_reset_mmd(efx, MDIO_MMD_PHYXS, - XFP_MAX_RESET_TIME / XFP_RESET_WAIT, - XFP_RESET_WAIT); + QT2022C2_MAX_RESET_TIME / + QT2022C2_RESET_WAIT, + QT2022C2_RESET_WAIT); if (rc < 0) goto fail; } @@ -122,11 +122,11 @@ static int xfp_reset_phy(struct efx_nic *efx) /* Check that all the MMDs we expect are present and responding. We * expect faults on some if the link is down, but not on the PHY XS */ - rc = efx_mdio_check_mmds(efx, XFP_REQUIRED_DEVS, MDIO_DEVS_PHYXS); + rc = efx_mdio_check_mmds(efx, QT202X_REQUIRED_DEVS, MDIO_DEVS_PHYXS); if (rc < 0) goto fail; - efx->board_info.init_leds(efx); + falcon_board(efx)->type->init_phy(efx); return rc; @@ -135,60 +135,60 @@ static int xfp_reset_phy(struct efx_nic *efx) return rc; } -static int xfp_phy_init(struct efx_nic *efx) +static int qt202x_phy_probe(struct efx_nic *efx) { - struct xfp_phy_data *phy_data; - u32 devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); + efx->mdio.mmds = QT202X_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->loopback_modes = QT202X_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + return 0; +} + +static int qt202x_phy_init(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data; + u32 devid; int rc; - phy_data = kzalloc(sizeof(struct xfp_phy_data), GFP_KERNEL); + rc = qt202x_reset_phy(efx); + if (rc) { + EFX_ERR(efx, "PHY init failed\n"); + return rc; + } + + phy_data = kzalloc(sizeof(struct qt202x_phy_data), GFP_KERNEL); if (!phy_data) return -ENOMEM; efx->phy_data = phy_data; + devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); EFX_INFO(efx, "PHY ID reg %x (OUI %06x model %02x revision %x)\n", devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), efx_mdio_id_rev(devid)); phy_data->phy_mode = efx->phy_mode; - - rc = xfp_reset_phy(efx); - - EFX_INFO(efx, "PHY init %s.\n", - rc ? "failed" : "successful"); - if (rc < 0) - goto fail; - return 0; - - fail: - kfree(efx->phy_data); - efx->phy_data = NULL; - return rc; } -static void xfp_phy_clear_interrupt(struct efx_nic *efx) +static int qt202x_link_ok(struct efx_nic *efx) { - /* Read to clear link status alarm */ - efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT); + return efx_mdio_links_ok(efx, QT202X_REQUIRED_DEVS); } -static int xfp_link_ok(struct efx_nic *efx) +static bool qt202x_phy_poll(struct efx_nic *efx) { - return efx_mdio_links_ok(efx, XFP_REQUIRED_DEVS); -} + bool was_up = efx->link_state.up; -static void xfp_phy_poll(struct efx_nic *efx) -{ - int link_up = xfp_link_ok(efx); - /* Simulate a PHY event if link state has changed */ - if (link_up != efx->link_up) - falcon_sim_phy_event(efx); + efx->link_state.up = qt202x_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + + return efx->link_state.up != was_up; } -static void xfp_phy_reconfigure(struct efx_nic *efx) +static int qt202x_phy_reconfigure(struct efx_nic *efx) { - struct xfp_phy_data *phy_data = efx->phy_data; + struct qt202x_phy_data *phy_data = efx->phy_data; if (efx->phy_type == PHY_TYPE_QT2025C) { /* There are several different register bits which can @@ -207,7 +207,7 @@ static void xfp_phy_reconfigure(struct efx_nic *efx) /* Reset the PHY when moving from tx off to tx on */ if (!(efx->phy_mode & PHY_MODE_TX_DISABLED) && (phy_data->phy_mode & PHY_MODE_TX_DISABLED)) - xfp_reset_phy(efx); + qt202x_reset_phy(efx); efx_mdio_transmit_disable(efx); } @@ -215,36 +215,28 @@ static void xfp_phy_reconfigure(struct efx_nic *efx) efx_mdio_phy_reconfigure(efx); phy_data->phy_mode = efx->phy_mode; - efx->link_up = xfp_link_ok(efx); - efx->link_speed = 10000; - efx->link_fd = true; - efx->link_fc = efx->wanted_fc; + + return 0; } -static void xfp_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +static void qt202x_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) { mdio45_ethtool_gset(&efx->mdio, ecmd); } -static void xfp_phy_fini(struct efx_nic *efx) +static void qt202x_phy_fini(struct efx_nic *efx) { - /* Clobber the LED if it was blinking */ - efx->board_info.blink(efx, false); - /* Free the context block */ kfree(efx->phy_data); efx->phy_data = NULL; } -struct efx_phy_operations falcon_xfp_phy_ops = { - .macs = EFX_XMAC, - .init = xfp_phy_init, - .reconfigure = xfp_phy_reconfigure, - .poll = xfp_phy_poll, - .fini = xfp_phy_fini, - .clear_interrupt = xfp_phy_clear_interrupt, - .get_settings = xfp_phy_get_settings, +struct efx_phy_operations falcon_qt202x_phy_ops = { + .probe = qt202x_phy_probe, + .init = qt202x_phy_init, + .reconfigure = qt202x_phy_reconfigure, + .poll = qt202x_phy_poll, + .fini = qt202x_phy_fini, + .get_settings = qt202x_phy_get_settings, .set_settings = efx_mdio_set_settings, - .mmds = XFP_REQUIRED_DEVS, - .loopbacks = XFP_LOOPBACKS, }; diff --git a/drivers/net/sfc/regs.h b/drivers/net/sfc/regs.h new file mode 100644 index 000000000000..89d606fe9248 --- /dev/null +++ b/drivers/net/sfc/regs.h @@ -0,0 +1,3168 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#ifndef EFX_REGS_H +#define EFX_REGS_H + +/* + * Falcon hardware architecture definitions have a name prefix following + * the format: + * + * F<type>_<min-rev><max-rev>_ + * + * The following <type> strings are used: + * + * MMIO register MC register Host memory structure + * ------------------------------------------------------------- + * Address R MCR + * Bitfield RF MCRF SF + * Enumerator FE MCFE SE + * + * <min-rev> is the first revision to which the definition applies: + * + * A: Falcon A1 (SFC4000AB) + * B: Falcon B0 (SFC4000BA) + * C: Siena A0 (SFL9021AA) + * + * If the definition has been changed or removed in later revisions + * then <max-rev> is the last revision to which the definition applies; + * otherwise it is "Z". + */ + +/************************************************************************** + * + * Falcon/Siena registers and descriptors + * + ************************************************************************** + */ + +/* ADR_REGION_REG: Address region register */ +#define FR_AZ_ADR_REGION 0x00000000 +#define FRF_AZ_ADR_REGION3_LBN 96 +#define FRF_AZ_ADR_REGION3_WIDTH 18 +#define FRF_AZ_ADR_REGION2_LBN 64 +#define FRF_AZ_ADR_REGION2_WIDTH 18 +#define FRF_AZ_ADR_REGION1_LBN 32 +#define FRF_AZ_ADR_REGION1_WIDTH 18 +#define FRF_AZ_ADR_REGION0_LBN 0 +#define FRF_AZ_ADR_REGION0_WIDTH 18 + +/* INT_EN_REG_KER: Kernel driver Interrupt enable register */ +#define FR_AZ_INT_EN_KER 0x00000010 +#define FRF_AZ_KER_INT_LEVE_SEL_LBN 8 +#define FRF_AZ_KER_INT_LEVE_SEL_WIDTH 6 +#define FRF_AZ_KER_INT_CHAR_LBN 4 +#define FRF_AZ_KER_INT_CHAR_WIDTH 1 +#define FRF_AZ_KER_INT_KER_LBN 3 +#define FRF_AZ_KER_INT_KER_WIDTH 1 +#define FRF_AZ_DRV_INT_EN_KER_LBN 0 +#define FRF_AZ_DRV_INT_EN_KER_WIDTH 1 + +/* INT_EN_REG_CHAR: Char Driver interrupt enable register */ +#define FR_BZ_INT_EN_CHAR 0x00000020 +#define FRF_BZ_CHAR_INT_LEVE_SEL_LBN 8 +#define FRF_BZ_CHAR_INT_LEVE_SEL_WIDTH 6 +#define FRF_BZ_CHAR_INT_CHAR_LBN 4 +#define FRF_BZ_CHAR_INT_CHAR_WIDTH 1 +#define FRF_BZ_CHAR_INT_KER_LBN 3 +#define FRF_BZ_CHAR_INT_KER_WIDTH 1 +#define FRF_BZ_DRV_INT_EN_CHAR_LBN 0 +#define FRF_BZ_DRV_INT_EN_CHAR_WIDTH 1 + +/* INT_ADR_REG_KER: Interrupt host address for Kernel driver */ +#define FR_AZ_INT_ADR_KER 0x00000030 +#define FRF_AZ_NORM_INT_VEC_DIS_KER_LBN 64 +#define FRF_AZ_NORM_INT_VEC_DIS_KER_WIDTH 1 +#define FRF_AZ_INT_ADR_KER_LBN 0 +#define FRF_AZ_INT_ADR_KER_WIDTH 64 + +/* INT_ADR_REG_CHAR: Interrupt host address for Char driver */ +#define FR_BZ_INT_ADR_CHAR 0x00000040 +#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_LBN 64 +#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_WIDTH 1 +#define FRF_BZ_INT_ADR_CHAR_LBN 0 +#define FRF_BZ_INT_ADR_CHAR_WIDTH 64 + +/* INT_ACK_KER: Kernel interrupt acknowledge register */ +#define FR_AA_INT_ACK_KER 0x00000050 +#define FRF_AA_INT_ACK_KER_FIELD_LBN 0 +#define FRF_AA_INT_ACK_KER_FIELD_WIDTH 32 + +/* INT_ISR0_REG: Function 0 Interrupt Acknowlege Status register */ +#define FR_BZ_INT_ISR0 0x00000090 +#define FRF_BZ_INT_ISR_REG_LBN 0 +#define FRF_BZ_INT_ISR_REG_WIDTH 64 + +/* HW_INIT_REG: Hardware initialization register */ +#define FR_AZ_HW_INIT 0x000000c0 +#define FRF_BB_BDMRD_CPLF_FULL_LBN 124 +#define FRF_BB_BDMRD_CPLF_FULL_WIDTH 1 +#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_LBN 121 +#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_WIDTH 3 +#define FRF_CZ_TX_MRG_TAGS_LBN 120 +#define FRF_CZ_TX_MRG_TAGS_WIDTH 1 +#define FRF_AB_TRGT_MASK_ALL_LBN 100 +#define FRF_AB_TRGT_MASK_ALL_WIDTH 1 +#define FRF_AZ_DOORBELL_DROP_LBN 92 +#define FRF_AZ_DOORBELL_DROP_WIDTH 8 +#define FRF_AB_TX_RREQ_MASK_EN_LBN 76 +#define FRF_AB_TX_RREQ_MASK_EN_WIDTH 1 +#define FRF_AB_PE_EIDLE_DIS_LBN 75 +#define FRF_AB_PE_EIDLE_DIS_WIDTH 1 +#define FRF_AA_FC_BLOCKING_EN_LBN 45 +#define FRF_AA_FC_BLOCKING_EN_WIDTH 1 +#define FRF_BZ_B2B_REQ_EN_LBN 45 +#define FRF_BZ_B2B_REQ_EN_WIDTH 1 +#define FRF_AA_B2B_REQ_EN_LBN 44 +#define FRF_AA_B2B_REQ_EN_WIDTH 1 +#define FRF_BB_FC_BLOCKING_EN_LBN 44 +#define FRF_BB_FC_BLOCKING_EN_WIDTH 1 +#define FRF_AZ_POST_WR_MASK_LBN 40 +#define FRF_AZ_POST_WR_MASK_WIDTH 4 +#define FRF_AZ_TLP_TC_LBN 34 +#define FRF_AZ_TLP_TC_WIDTH 3 +#define FRF_AZ_TLP_ATTR_LBN 32 +#define FRF_AZ_TLP_ATTR_WIDTH 2 +#define FRF_AB_INTB_VEC_LBN 24 +#define FRF_AB_INTB_VEC_WIDTH 5 +#define FRF_AB_INTA_VEC_LBN 16 +#define FRF_AB_INTA_VEC_WIDTH 5 +#define FRF_AZ_WD_TIMER_LBN 8 +#define FRF_AZ_WD_TIMER_WIDTH 8 +#define FRF_AZ_US_DISABLE_LBN 5 +#define FRF_AZ_US_DISABLE_WIDTH 1 +#define FRF_AZ_TLP_EP_LBN 4 +#define FRF_AZ_TLP_EP_WIDTH 1 +#define FRF_AZ_ATTR_SEL_LBN 3 +#define FRF_AZ_ATTR_SEL_WIDTH 1 +#define FRF_AZ_TD_SEL_LBN 1 +#define FRF_AZ_TD_SEL_WIDTH 1 +#define FRF_AZ_TLP_TD_LBN 0 +#define FRF_AZ_TLP_TD_WIDTH 1 + +/* EE_SPI_HCMD_REG: SPI host command register */ +#define FR_AB_EE_SPI_HCMD 0x00000100 +#define FRF_AB_EE_SPI_HCMD_CMD_EN_LBN 31 +#define FRF_AB_EE_SPI_HCMD_CMD_EN_WIDTH 1 +#define FRF_AB_EE_WR_TIMER_ACTIVE_LBN 28 +#define FRF_AB_EE_WR_TIMER_ACTIVE_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_SF_SEL_LBN 24 +#define FRF_AB_EE_SPI_HCMD_SF_SEL_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_DABCNT_LBN 16 +#define FRF_AB_EE_SPI_HCMD_DABCNT_WIDTH 5 +#define FRF_AB_EE_SPI_HCMD_READ_LBN 15 +#define FRF_AB_EE_SPI_HCMD_READ_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_DUBCNT_LBN 12 +#define FRF_AB_EE_SPI_HCMD_DUBCNT_WIDTH 2 +#define FRF_AB_EE_SPI_HCMD_ADBCNT_LBN 8 +#define FRF_AB_EE_SPI_HCMD_ADBCNT_WIDTH 2 +#define FRF_AB_EE_SPI_HCMD_ENC_LBN 0 +#define FRF_AB_EE_SPI_HCMD_ENC_WIDTH 8 + +/* USR_EV_CFG: User Level Event Configuration register */ +#define FR_CZ_USR_EV_CFG 0x00000100 +#define FRF_CZ_USREV_DIS_LBN 16 +#define FRF_CZ_USREV_DIS_WIDTH 1 +#define FRF_CZ_DFLT_EVQ_LBN 0 +#define FRF_CZ_DFLT_EVQ_WIDTH 10 + +/* EE_SPI_HADR_REG: SPI host address register */ +#define FR_AB_EE_SPI_HADR 0x00000110 +#define FRF_AB_EE_SPI_HADR_DUBYTE_LBN 24 +#define FRF_AB_EE_SPI_HADR_DUBYTE_WIDTH 8 +#define FRF_AB_EE_SPI_HADR_ADR_LBN 0 +#define FRF_AB_EE_SPI_HADR_ADR_WIDTH 24 + +/* EE_SPI_HDATA_REG: SPI host data register */ +#define FR_AB_EE_SPI_HDATA 0x00000120 +#define FRF_AB_EE_SPI_HDATA3_LBN 96 +#define FRF_AB_EE_SPI_HDATA3_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA2_LBN 64 +#define FRF_AB_EE_SPI_HDATA2_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA1_LBN 32 +#define FRF_AB_EE_SPI_HDATA1_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA0_LBN 0 +#define FRF_AB_EE_SPI_HDATA0_WIDTH 32 + +/* EE_BASE_PAGE_REG: Expansion ROM base mirror register */ +#define FR_AB_EE_BASE_PAGE 0x00000130 +#define FRF_AB_EE_EXPROM_MASK_LBN 16 +#define FRF_AB_EE_EXPROM_MASK_WIDTH 13 +#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_LBN 0 +#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_WIDTH 13 + +/* EE_VPD_CFG0_REG: SPI/VPD configuration register 0 */ +#define FR_AB_EE_VPD_CFG0 0x00000140 +#define FRF_AB_EE_SF_FASTRD_EN_LBN 127 +#define FRF_AB_EE_SF_FASTRD_EN_WIDTH 1 +#define FRF_AB_EE_SF_CLOCK_DIV_LBN 120 +#define FRF_AB_EE_SF_CLOCK_DIV_WIDTH 7 +#define FRF_AB_EE_VPD_WIP_POLL_LBN 119 +#define FRF_AB_EE_VPD_WIP_POLL_WIDTH 1 +#define FRF_AB_EE_EE_CLOCK_DIV_LBN 112 +#define FRF_AB_EE_EE_CLOCK_DIV_WIDTH 7 +#define FRF_AB_EE_EE_WR_TMR_VALUE_LBN 96 +#define FRF_AB_EE_EE_WR_TMR_VALUE_WIDTH 16 +#define FRF_AB_EE_VPDW_LENGTH_LBN 80 +#define FRF_AB_EE_VPDW_LENGTH_WIDTH 15 +#define FRF_AB_EE_VPDW_BASE_LBN 64 +#define FRF_AB_EE_VPDW_BASE_WIDTH 15 +#define FRF_AB_EE_VPD_WR_CMD_EN_LBN 56 +#define FRF_AB_EE_VPD_WR_CMD_EN_WIDTH 8 +#define FRF_AB_EE_VPD_BASE_LBN 32 +#define FRF_AB_EE_VPD_BASE_WIDTH 24 +#define FRF_AB_EE_VPD_LENGTH_LBN 16 +#define FRF_AB_EE_VPD_LENGTH_WIDTH 15 +#define FRF_AB_EE_VPD_AD_SIZE_LBN 8 +#define FRF_AB_EE_VPD_AD_SIZE_WIDTH 5 +#define FRF_AB_EE_VPD_ACCESS_ON_LBN 5 +#define FRF_AB_EE_VPD_ACCESS_ON_WIDTH 1 +#define FRF_AB_EE_VPD_ACCESS_BLOCK_LBN 4 +#define FRF_AB_EE_VPD_ACCESS_BLOCK_WIDTH 1 +#define FRF_AB_EE_VPD_DEV_SF_SEL_LBN 2 +#define FRF_AB_EE_VPD_DEV_SF_SEL_WIDTH 1 +#define FRF_AB_EE_VPD_EN_AD9_MODE_LBN 1 +#define FRF_AB_EE_VPD_EN_AD9_MODE_WIDTH 1 +#define FRF_AB_EE_VPD_EN_LBN 0 +#define FRF_AB_EE_VPD_EN_WIDTH 1 + +/* EE_VPD_SW_CNTL_REG: VPD access SW control register */ +#define FR_AB_EE_VPD_SW_CNTL 0x00000150 +#define FRF_AB_EE_VPD_CYCLE_PENDING_LBN 31 +#define FRF_AB_EE_VPD_CYCLE_PENDING_WIDTH 1 +#define FRF_AB_EE_VPD_CYC_WRITE_LBN 28 +#define FRF_AB_EE_VPD_CYC_WRITE_WIDTH 1 +#define FRF_AB_EE_VPD_CYC_ADR_LBN 0 +#define FRF_AB_EE_VPD_CYC_ADR_WIDTH 15 + +/* EE_VPD_SW_DATA_REG: VPD access SW data register */ +#define FR_AB_EE_VPD_SW_DATA 0x00000160 +#define FRF_AB_EE_VPD_CYC_DAT_LBN 0 +#define FRF_AB_EE_VPD_CYC_DAT_WIDTH 32 + +/* PBMX_DBG_IADDR_REG: Capture Module address register */ +#define FR_CZ_PBMX_DBG_IADDR 0x000001f0 +#define FRF_CZ_PBMX_DBG_IADDR_LBN 0 +#define FRF_CZ_PBMX_DBG_IADDR_WIDTH 32 + +/* PCIE_CORE_INDIRECT_REG: Indirect Access to PCIE Core registers */ +#define FR_BB_PCIE_CORE_INDIRECT 0x000001f0 +#define FRF_BB_PCIE_CORE_TARGET_DATA_LBN 32 +#define FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH 32 +#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_LBN 15 +#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_WIDTH 1 +#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_LBN 0 +#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_WIDTH 12 + +/* PBMX_DBG_IDATA_REG: Capture Module data register */ +#define FR_CZ_PBMX_DBG_IDATA 0x000001f8 +#define FRF_CZ_PBMX_DBG_IDATA_LBN 0 +#define FRF_CZ_PBMX_DBG_IDATA_WIDTH 64 + +/* NIC_STAT_REG: NIC status register */ +#define FR_AB_NIC_STAT 0x00000200 +#define FRF_BB_AER_DIS_LBN 34 +#define FRF_BB_AER_DIS_WIDTH 1 +#define FRF_BB_EE_STRAP_EN_LBN 31 +#define FRF_BB_EE_STRAP_EN_WIDTH 1 +#define FRF_BB_EE_STRAP_LBN 24 +#define FRF_BB_EE_STRAP_WIDTH 4 +#define FRF_BB_REVISION_ID_LBN 17 +#define FRF_BB_REVISION_ID_WIDTH 7 +#define FRF_AB_ONCHIP_SRAM_LBN 16 +#define FRF_AB_ONCHIP_SRAM_WIDTH 1 +#define FRF_AB_SF_PRST_LBN 9 +#define FRF_AB_SF_PRST_WIDTH 1 +#define FRF_AB_EE_PRST_LBN 8 +#define FRF_AB_EE_PRST_WIDTH 1 +#define FRF_AB_ATE_MODE_LBN 3 +#define FRF_AB_ATE_MODE_WIDTH 1 +#define FRF_AB_STRAP_PINS_LBN 0 +#define FRF_AB_STRAP_PINS_WIDTH 3 + +/* GPIO_CTL_REG: GPIO control register */ +#define FR_AB_GPIO_CTL 0x00000210 +#define FRF_AB_GPIO_OUT3_LBN 112 +#define FRF_AB_GPIO_OUT3_WIDTH 16 +#define FRF_AB_GPIO_IN3_LBN 104 +#define FRF_AB_GPIO_IN3_WIDTH 8 +#define FRF_AB_GPIO_PWRUP_VALUE3_LBN 96 +#define FRF_AB_GPIO_PWRUP_VALUE3_WIDTH 8 +#define FRF_AB_GPIO_OUT2_LBN 80 +#define FRF_AB_GPIO_OUT2_WIDTH 16 +#define FRF_AB_GPIO_IN2_LBN 72 +#define FRF_AB_GPIO_IN2_WIDTH 8 +#define FRF_AB_GPIO_PWRUP_VALUE2_LBN 64 +#define FRF_AB_GPIO_PWRUP_VALUE2_WIDTH 8 +#define FRF_AB_GPIO15_OEN_LBN 63 +#define FRF_AB_GPIO15_OEN_WIDTH 1 +#define FRF_AB_GPIO14_OEN_LBN 62 +#define FRF_AB_GPIO14_OEN_WIDTH 1 +#define FRF_AB_GPIO13_OEN_LBN 61 +#define FRF_AB_GPIO13_OEN_WIDTH 1 +#define FRF_AB_GPIO12_OEN_LBN 60 +#define FRF_AB_GPIO12_OEN_WIDTH 1 +#define FRF_AB_GPIO11_OEN_LBN 59 +#define FRF_AB_GPIO11_OEN_WIDTH 1 +#define FRF_AB_GPIO10_OEN_LBN 58 +#define FRF_AB_GPIO10_OEN_WIDTH 1 +#define FRF_AB_GPIO9_OEN_LBN 57 +#define FRF_AB_GPIO9_OEN_WIDTH 1 +#define FRF_AB_GPIO8_OEN_LBN 56 +#define FRF_AB_GPIO8_OEN_WIDTH 1 +#define FRF_AB_GPIO15_OUT_LBN 55 +#define FRF_AB_GPIO15_OUT_WIDTH 1 +#define FRF_AB_GPIO14_OUT_LBN 54 +#define FRF_AB_GPIO14_OUT_WIDTH 1 +#define FRF_AB_GPIO13_OUT_LBN 53 +#define FRF_AB_GPIO13_OUT_WIDTH 1 +#define FRF_AB_GPIO12_OUT_LBN 52 +#define FRF_AB_GPIO12_OUT_WIDTH 1 +#define FRF_AB_GPIO11_OUT_LBN 51 +#define FRF_AB_GPIO11_OUT_WIDTH 1 +#define FRF_AB_GPIO10_OUT_LBN 50 +#define FRF_AB_GPIO10_OUT_WIDTH 1 +#define FRF_AB_GPIO9_OUT_LBN 49 +#define FRF_AB_GPIO9_OUT_WIDTH 1 +#define FRF_AB_GPIO8_OUT_LBN 48 +#define FRF_AB_GPIO8_OUT_WIDTH 1 +#define FRF_AB_GPIO15_IN_LBN 47 +#define FRF_AB_GPIO15_IN_WIDTH 1 +#define FRF_AB_GPIO14_IN_LBN 46 +#define FRF_AB_GPIO14_IN_WIDTH 1 +#define FRF_AB_GPIO13_IN_LBN 45 +#define FRF_AB_GPIO13_IN_WIDTH 1 +#define FRF_AB_GPIO12_IN_LBN 44 +#define FRF_AB_GPIO12_IN_WIDTH 1 +#define FRF_AB_GPIO11_IN_LBN 43 +#define FRF_AB_GPIO11_IN_WIDTH 1 +#define FRF_AB_GPIO10_IN_LBN 42 +#define FRF_AB_GPIO10_IN_WIDTH 1 +#define FRF_AB_GPIO9_IN_LBN 41 +#define FRF_AB_GPIO9_IN_WIDTH 1 +#define FRF_AB_GPIO8_IN_LBN 40 +#define FRF_AB_GPIO8_IN_WIDTH 1 +#define FRF_AB_GPIO15_PWRUP_VALUE_LBN 39 +#define FRF_AB_GPIO15_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO14_PWRUP_VALUE_LBN 38 +#define FRF_AB_GPIO14_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO13_PWRUP_VALUE_LBN 37 +#define FRF_AB_GPIO13_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO12_PWRUP_VALUE_LBN 36 +#define FRF_AB_GPIO12_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO11_PWRUP_VALUE_LBN 35 +#define FRF_AB_GPIO11_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO10_PWRUP_VALUE_LBN 34 +#define FRF_AB_GPIO10_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO9_PWRUP_VALUE_LBN 33 +#define FRF_AB_GPIO9_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO8_PWRUP_VALUE_LBN 32 +#define FRF_AB_GPIO8_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_CLK156_OUT_EN_LBN 31 +#define FRF_AB_CLK156_OUT_EN_WIDTH 1 +#define FRF_AB_USE_NIC_CLK_LBN 30 +#define FRF_AB_USE_NIC_CLK_WIDTH 1 +#define FRF_AB_GPIO5_OEN_LBN 29 +#define FRF_AB_GPIO5_OEN_WIDTH 1 +#define FRF_AB_GPIO4_OEN_LBN 28 +#define FRF_AB_GPIO4_OEN_WIDTH 1 +#define FRF_AB_GPIO3_OEN_LBN 27 +#define FRF_AB_GPIO3_OEN_WIDTH 1 +#define FRF_AB_GPIO2_OEN_LBN 26 +#define FRF_AB_GPIO2_OEN_WIDTH 1 +#define FRF_AB_GPIO1_OEN_LBN 25 +#define FRF_AB_GPIO1_OEN_WIDTH 1 +#define FRF_AB_GPIO0_OEN_LBN 24 +#define FRF_AB_GPIO0_OEN_WIDTH 1 +#define FRF_AB_GPIO7_OUT_LBN 23 +#define FRF_AB_GPIO7_OUT_WIDTH 1 +#define FRF_AB_GPIO6_OUT_LBN 22 +#define FRF_AB_GPIO6_OUT_WIDTH 1 +#define FRF_AB_GPIO5_OUT_LBN 21 +#define FRF_AB_GPIO5_OUT_WIDTH 1 +#define FRF_AB_GPIO4_OUT_LBN 20 +#define FRF_AB_GPIO4_OUT_WIDTH 1 +#define FRF_AB_GPIO3_OUT_LBN 19 +#define FRF_AB_GPIO3_OUT_WIDTH 1 +#define FRF_AB_GPIO2_OUT_LBN 18 +#define FRF_AB_GPIO2_OUT_WIDTH 1 +#define FRF_AB_GPIO1_OUT_LBN 17 +#define FRF_AB_GPIO1_OUT_WIDTH 1 +#define FRF_AB_GPIO0_OUT_LBN 16 +#define FRF_AB_GPIO0_OUT_WIDTH 1 +#define FRF_AB_GPIO7_IN_LBN 15 +#define FRF_AB_GPIO7_IN_WIDTH 1 +#define FRF_AB_GPIO6_IN_LBN 14 +#define FRF_AB_GPIO6_IN_WIDTH 1 +#define FRF_AB_GPIO5_IN_LBN 13 +#define FRF_AB_GPIO5_IN_WIDTH 1 +#define FRF_AB_GPIO4_IN_LBN 12 +#define FRF_AB_GPIO4_IN_WIDTH 1 +#define FRF_AB_GPIO3_IN_LBN 11 +#define FRF_AB_GPIO3_IN_WIDTH 1 +#define FRF_AB_GPIO2_IN_LBN 10 +#define FRF_AB_GPIO2_IN_WIDTH 1 +#define FRF_AB_GPIO1_IN_LBN 9 +#define FRF_AB_GPIO1_IN_WIDTH 1 +#define FRF_AB_GPIO0_IN_LBN 8 +#define FRF_AB_GPIO0_IN_WIDTH 1 +#define FRF_AB_GPIO7_PWRUP_VALUE_LBN 7 +#define FRF_AB_GPIO7_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO6_PWRUP_VALUE_LBN 6 +#define FRF_AB_GPIO6_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO5_PWRUP_VALUE_LBN 5 +#define FRF_AB_GPIO5_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO4_PWRUP_VALUE_LBN 4 +#define FRF_AB_GPIO4_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO3_PWRUP_VALUE_LBN 3 +#define FRF_AB_GPIO3_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO2_PWRUP_VALUE_LBN 2 +#define FRF_AB_GPIO2_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO1_PWRUP_VALUE_LBN 1 +#define FRF_AB_GPIO1_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO0_PWRUP_VALUE_LBN 0 +#define FRF_AB_GPIO0_PWRUP_VALUE_WIDTH 1 + +/* GLB_CTL_REG: Global control register */ +#define FR_AB_GLB_CTL 0x00000220 +#define FRF_AB_EXT_PHY_RST_CTL_LBN 63 +#define FRF_AB_EXT_PHY_RST_CTL_WIDTH 1 +#define FRF_AB_XAUI_SD_RST_CTL_LBN 62 +#define FRF_AB_XAUI_SD_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_SD_RST_CTL_LBN 61 +#define FRF_AB_PCIE_SD_RST_CTL_WIDTH 1 +#define FRF_AA_PCIX_RST_CTL_LBN 60 +#define FRF_AA_PCIX_RST_CTL_WIDTH 1 +#define FRF_BB_BIU_RST_CTL_LBN 60 +#define FRF_BB_BIU_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_STKY_RST_CTL_LBN 59 +#define FRF_AB_PCIE_STKY_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_NSTKY_RST_CTL_LBN 58 +#define FRF_AB_PCIE_NSTKY_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_CORE_RST_CTL_LBN 57 +#define FRF_AB_PCIE_CORE_RST_CTL_WIDTH 1 +#define FRF_AB_XGRX_RST_CTL_LBN 56 +#define FRF_AB_XGRX_RST_CTL_WIDTH 1 +#define FRF_AB_XGTX_RST_CTL_LBN 55 +#define FRF_AB_XGTX_RST_CTL_WIDTH 1 +#define FRF_AB_EM_RST_CTL_LBN 54 +#define FRF_AB_EM_RST_CTL_WIDTH 1 +#define FRF_AB_EV_RST_CTL_LBN 53 +#define FRF_AB_EV_RST_CTL_WIDTH 1 +#define FRF_AB_SR_RST_CTL_LBN 52 +#define FRF_AB_SR_RST_CTL_WIDTH 1 +#define FRF_AB_RX_RST_CTL_LBN 51 +#define FRF_AB_RX_RST_CTL_WIDTH 1 +#define FRF_AB_TX_RST_CTL_LBN 50 +#define FRF_AB_TX_RST_CTL_WIDTH 1 +#define FRF_AB_EE_RST_CTL_LBN 49 +#define FRF_AB_EE_RST_CTL_WIDTH 1 +#define FRF_AB_CS_RST_CTL_LBN 48 +#define FRF_AB_CS_RST_CTL_WIDTH 1 +#define FRF_AB_HOT_RST_CTL_LBN 40 +#define FRF_AB_HOT_RST_CTL_WIDTH 2 +#define FRF_AB_RST_EXT_PHY_LBN 31 +#define FRF_AB_RST_EXT_PHY_WIDTH 1 +#define FRF_AB_RST_XAUI_SD_LBN 30 +#define FRF_AB_RST_XAUI_SD_WIDTH 1 +#define FRF_AB_RST_PCIE_SD_LBN 29 +#define FRF_AB_RST_PCIE_SD_WIDTH 1 +#define FRF_AA_RST_PCIX_LBN 28 +#define FRF_AA_RST_PCIX_WIDTH 1 +#define FRF_BB_RST_BIU_LBN 28 +#define FRF_BB_RST_BIU_WIDTH 1 +#define FRF_AB_RST_PCIE_STKY_LBN 27 +#define FRF_AB_RST_PCIE_STKY_WIDTH 1 +#define FRF_AB_RST_PCIE_NSTKY_LBN 26 +#define FRF_AB_RST_PCIE_NSTKY_WIDTH 1 +#define FRF_AB_RST_PCIE_CORE_LBN 25 +#define FRF_AB_RST_PCIE_CORE_WIDTH 1 +#define FRF_AB_RST_XGRX_LBN 24 +#define FRF_AB_RST_XGRX_WIDTH 1 +#define FRF_AB_RST_XGTX_LBN 23 +#define FRF_AB_RST_XGTX_WIDTH 1 +#define FRF_AB_RST_EM_LBN 22 +#define FRF_AB_RST_EM_WIDTH 1 +#define FRF_AB_RST_EV_LBN 21 +#define FRF_AB_RST_EV_WIDTH 1 +#define FRF_AB_RST_SR_LBN 20 +#define FRF_AB_RST_SR_WIDTH 1 +#define FRF_AB_RST_RX_LBN 19 +#define FRF_AB_RST_RX_WIDTH 1 +#define FRF_AB_RST_TX_LBN 18 +#define FRF_AB_RST_TX_WIDTH 1 +#define FRF_AB_RST_SF_LBN 17 +#define FRF_AB_RST_SF_WIDTH 1 +#define FRF_AB_RST_CS_LBN 16 +#define FRF_AB_RST_CS_WIDTH 1 +#define FRF_AB_INT_RST_DUR_LBN 4 +#define FRF_AB_INT_RST_DUR_WIDTH 3 +#define FRF_AB_EXT_PHY_RST_DUR_LBN 1 +#define FRF_AB_EXT_PHY_RST_DUR_WIDTH 3 +#define FFE_AB_EXT_PHY_RST_DUR_10240US 7 +#define FFE_AB_EXT_PHY_RST_DUR_5120US 6 +#define FFE_AB_EXT_PHY_RST_DUR_2560US 5 +#define FFE_AB_EXT_PHY_RST_DUR_1280US 4 +#define FFE_AB_EXT_PHY_RST_DUR_640US 3 +#define FFE_AB_EXT_PHY_RST_DUR_320US 2 +#define FFE_AB_EXT_PHY_RST_DUR_160US 1 +#define FFE_AB_EXT_PHY_RST_DUR_80US 0 +#define FRF_AB_SWRST_LBN 0 +#define FRF_AB_SWRST_WIDTH 1 + +/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */ +#define FR_AZ_FATAL_INTR_KER 0x00000230 +#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_LBN 44 +#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_WIDTH 1 +#define FRF_AB_PCI_BUSERR_INT_KER_EN_LBN 43 +#define FRF_AB_PCI_BUSERR_INT_KER_EN_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_KER_EN_LBN 43 +#define FRF_CZ_MBU_PERR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_SRAM_OOB_INT_KER_EN_LBN 42 +#define FRF_AZ_SRAM_OOB_INT_KER_EN_WIDTH 1 +#define FRF_AZ_BUFID_OOB_INT_KER_EN_LBN 41 +#define FRF_AZ_BUFID_OOB_INT_KER_EN_WIDTH 1 +#define FRF_AZ_MEM_PERR_INT_KER_EN_LBN 40 +#define FRF_AZ_MEM_PERR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_RBUF_OWN_INT_KER_EN_LBN 39 +#define FRF_AZ_RBUF_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_TBUF_OWN_INT_KER_EN_LBN 38 +#define FRF_AZ_TBUF_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_LBN 37 +#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_LBN 36 +#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_EVQ_OWN_INT_KER_EN_LBN 35 +#define FRF_AZ_EVQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_EVF_OFLO_INT_KER_EN_LBN 34 +#define FRF_AZ_EVF_OFLO_INT_KER_EN_WIDTH 1 +#define FRF_AZ_ILL_ADR_INT_KER_EN_LBN 33 +#define FRF_AZ_ILL_ADR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_SRM_PERR_INT_KER_EN_LBN 32 +#define FRF_AZ_SRM_PERR_INT_KER_EN_WIDTH 1 +#define FRF_CZ_SRAM_PERR_INT_P_KER_LBN 12 +#define FRF_CZ_SRAM_PERR_INT_P_KER_WIDTH 1 +#define FRF_AB_PCI_BUSERR_INT_KER_LBN 11 +#define FRF_AB_PCI_BUSERR_INT_KER_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_KER_LBN 11 +#define FRF_CZ_MBU_PERR_INT_KER_WIDTH 1 +#define FRF_AZ_SRAM_OOB_INT_KER_LBN 10 +#define FRF_AZ_SRAM_OOB_INT_KER_WIDTH 1 +#define FRF_AZ_BUFID_DC_OOB_INT_KER_LBN 9 +#define FRF_AZ_BUFID_DC_OOB_INT_KER_WIDTH 1 +#define FRF_AZ_MEM_PERR_INT_KER_LBN 8 +#define FRF_AZ_MEM_PERR_INT_KER_WIDTH 1 +#define FRF_AZ_RBUF_OWN_INT_KER_LBN 7 +#define FRF_AZ_RBUF_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_TBUF_OWN_INT_KER_LBN 6 +#define FRF_AZ_TBUF_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_RDESCQ_OWN_INT_KER_LBN 5 +#define FRF_AZ_RDESCQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_TDESCQ_OWN_INT_KER_LBN 4 +#define FRF_AZ_TDESCQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_EVQ_OWN_INT_KER_LBN 3 +#define FRF_AZ_EVQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_EVF_OFLO_INT_KER_LBN 2 +#define FRF_AZ_EVF_OFLO_INT_KER_WIDTH 1 +#define FRF_AZ_ILL_ADR_INT_KER_LBN 1 +#define FRF_AZ_ILL_ADR_INT_KER_WIDTH 1 +#define FRF_AZ_SRM_PERR_INT_KER_LBN 0 +#define FRF_AZ_SRM_PERR_INT_KER_WIDTH 1 + +/* FATAL_INTR_REG_CHAR: Fatal interrupt register for Char */ +#define FR_BZ_FATAL_INTR_CHAR 0x00000240 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_LBN 44 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_WIDTH 1 +#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_LBN 43 +#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_CHAR_EN_LBN 43 +#define FRF_CZ_MBU_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_LBN 42 +#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_LBN 41 +#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_MEM_PERR_INT_CHAR_EN_LBN 40 +#define FRF_BZ_MEM_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_LBN 39 +#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_LBN 38 +#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_LBN 37 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_LBN 36 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_LBN 35 +#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_LBN 34 +#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_EN_LBN 33 +#define FRF_BZ_ILL_ADR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_SRM_PERR_INT_CHAR_EN_LBN 32 +#define FRF_BZ_SRM_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_LBN 12 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_WIDTH 1 +#define FRF_BB_PCI_BUSERR_INT_CHAR_LBN 11 +#define FRF_BB_PCI_BUSERR_INT_CHAR_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_CHAR_LBN 11 +#define FRF_CZ_MBU_PERR_INT_CHAR_WIDTH 1 +#define FRF_BZ_SRAM_OOB_INT_CHAR_LBN 10 +#define FRF_BZ_SRAM_OOB_INT_CHAR_WIDTH 1 +#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_LBN 9 +#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_WIDTH 1 +#define FRF_BZ_MEM_PERR_INT_CHAR_LBN 8 +#define FRF_BZ_MEM_PERR_INT_CHAR_WIDTH 1 +#define FRF_BZ_RBUF_OWN_INT_CHAR_LBN 7 +#define FRF_BZ_RBUF_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_TBUF_OWN_INT_CHAR_LBN 6 +#define FRF_BZ_TBUF_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_LBN 5 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_LBN 4 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_EVQ_OWN_INT_CHAR_LBN 3 +#define FRF_BZ_EVQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_EVF_OFLO_INT_CHAR_LBN 2 +#define FRF_BZ_EVF_OFLO_INT_CHAR_WIDTH 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_LBN 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_WIDTH 1 +#define FRF_BZ_SRM_PERR_INT_CHAR_LBN 0 +#define FRF_BZ_SRM_PERR_INT_CHAR_WIDTH 1 + +/* DP_CTRL_REG: Datapath control register */ +#define FR_BZ_DP_CTRL 0x00000250 +#define FRF_BZ_FLS_EVQ_ID_LBN 0 +#define FRF_BZ_FLS_EVQ_ID_WIDTH 12 + +/* MEM_STAT_REG: Memory status register */ +#define FR_AZ_MEM_STAT 0x00000260 +#define FRF_AB_MEM_PERR_VEC_LBN 53 +#define FRF_AB_MEM_PERR_VEC_WIDTH 38 +#define FRF_AB_MBIST_CORR_LBN 38 +#define FRF_AB_MBIST_CORR_WIDTH 15 +#define FRF_AB_MBIST_ERR_LBN 0 +#define FRF_AB_MBIST_ERR_WIDTH 40 +#define FRF_CZ_MEM_PERR_VEC_LBN 0 +#define FRF_CZ_MEM_PERR_VEC_WIDTH 35 + +/* CS_DEBUG_REG: Debug register */ +#define FR_AZ_CS_DEBUG 0x00000270 +#define FRF_AB_GLB_DEBUG2_SEL_LBN 50 +#define FRF_AB_GLB_DEBUG2_SEL_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL2_LBN 47 +#define FRF_AB_DEBUG_BLK_SEL2_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL1_LBN 44 +#define FRF_AB_DEBUG_BLK_SEL1_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL0_LBN 41 +#define FRF_AB_DEBUG_BLK_SEL0_WIDTH 3 +#define FRF_CZ_CS_PORT_NUM_LBN 40 +#define FRF_CZ_CS_PORT_NUM_WIDTH 2 +#define FRF_AB_MISC_DEBUG_ADDR_LBN 36 +#define FRF_AB_MISC_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_SERDES_DEBUG_ADDR_LBN 31 +#define FRF_AB_SERDES_DEBUG_ADDR_WIDTH 5 +#define FRF_CZ_CS_PORT_FPE_LBN 1 +#define FRF_CZ_CS_PORT_FPE_WIDTH 35 +#define FRF_AB_EM_DEBUG_ADDR_LBN 26 +#define FRF_AB_EM_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_SR_DEBUG_ADDR_LBN 21 +#define FRF_AB_SR_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_EV_DEBUG_ADDR_LBN 16 +#define FRF_AB_EV_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_RX_DEBUG_ADDR_LBN 11 +#define FRF_AB_RX_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_TX_DEBUG_ADDR_LBN 6 +#define FRF_AB_TX_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_CS_BIU_DEBUG_ADDR_LBN 1 +#define FRF_AB_CS_BIU_DEBUG_ADDR_WIDTH 5 +#define FRF_AZ_CS_DEBUG_EN_LBN 0 +#define FRF_AZ_CS_DEBUG_EN_WIDTH 1 + +/* DRIVER_REG: Driver scratch register [0-7] */ +#define FR_AZ_DRIVER 0x00000280 +#define FR_AZ_DRIVER_STEP 16 +#define FR_AZ_DRIVER_ROWS 8 +#define FRF_AZ_DRIVER_DW0_LBN 0 +#define FRF_AZ_DRIVER_DW0_WIDTH 32 + +/* ALTERA_BUILD_REG: Altera build register */ +#define FR_AZ_ALTERA_BUILD 0x00000300 +#define FRF_AZ_ALTERA_BUILD_VER_LBN 0 +#define FRF_AZ_ALTERA_BUILD_VER_WIDTH 32 + +/* CSR_SPARE_REG: Spare register */ +#define FR_AZ_CSR_SPARE 0x00000310 +#define FRF_AB_MEM_PERR_EN_LBN 64 +#define FRF_AB_MEM_PERR_EN_WIDTH 38 +#define FRF_CZ_MEM_PERR_EN_LBN 64 +#define FRF_CZ_MEM_PERR_EN_WIDTH 35 +#define FRF_AB_MEM_PERR_EN_TX_DATA_LBN 72 +#define FRF_AB_MEM_PERR_EN_TX_DATA_WIDTH 2 +#define FRF_AZ_CSR_SPARE_BITS_LBN 0 +#define FRF_AZ_CSR_SPARE_BITS_WIDTH 32 + +/* PCIE_SD_CTL0123_REG: PCIE SerDes control register 0 to 3 */ +#define FR_AB_PCIE_SD_CTL0123 0x00000320 +#define FRF_AB_PCIE_TESTSIG_H_LBN 96 +#define FRF_AB_PCIE_TESTSIG_H_WIDTH 19 +#define FRF_AB_PCIE_TESTSIG_L_LBN 64 +#define FRF_AB_PCIE_TESTSIG_L_WIDTH 19 +#define FRF_AB_PCIE_OFFSET_LBN 56 +#define FRF_AB_PCIE_OFFSET_WIDTH 8 +#define FRF_AB_PCIE_OFFSETEN_H_LBN 55 +#define FRF_AB_PCIE_OFFSETEN_H_WIDTH 1 +#define FRF_AB_PCIE_OFFSETEN_L_LBN 54 +#define FRF_AB_PCIE_OFFSETEN_L_WIDTH 1 +#define FRF_AB_PCIE_HIVMODE_H_LBN 53 +#define FRF_AB_PCIE_HIVMODE_H_WIDTH 1 +#define FRF_AB_PCIE_HIVMODE_L_LBN 52 +#define FRF_AB_PCIE_HIVMODE_L_WIDTH 1 +#define FRF_AB_PCIE_PARRESET_H_LBN 51 +#define FRF_AB_PCIE_PARRESET_H_WIDTH 1 +#define FRF_AB_PCIE_PARRESET_L_LBN 50 +#define FRF_AB_PCIE_PARRESET_L_WIDTH 1 +#define FRF_AB_PCIE_LPBKWDRV_H_LBN 49 +#define FRF_AB_PCIE_LPBKWDRV_H_WIDTH 1 +#define FRF_AB_PCIE_LPBKWDRV_L_LBN 48 +#define FRF_AB_PCIE_LPBKWDRV_L_WIDTH 1 +#define FRF_AB_PCIE_LPBK_LBN 40 +#define FRF_AB_PCIE_LPBK_WIDTH 8 +#define FRF_AB_PCIE_PARLPBK_LBN 32 +#define FRF_AB_PCIE_PARLPBK_WIDTH 8 +#define FRF_AB_PCIE_RXTERMADJ_H_LBN 30 +#define FRF_AB_PCIE_RXTERMADJ_H_WIDTH 2 +#define FRF_AB_PCIE_RXTERMADJ_L_LBN 28 +#define FRF_AB_PCIE_RXTERMADJ_L_WIDTH 2 +#define FFE_AB_PCIE_RXTERMADJ_MIN15PCNT 3 +#define FFE_AB_PCIE_RXTERMADJ_PL10PCNT 2 +#define FFE_AB_PCIE_RXTERMADJ_MIN17PCNT 1 +#define FFE_AB_PCIE_RXTERMADJ_NOMNL 0 +#define FRF_AB_PCIE_TXTERMADJ_H_LBN 26 +#define FRF_AB_PCIE_TXTERMADJ_H_WIDTH 2 +#define FRF_AB_PCIE_TXTERMADJ_L_LBN 24 +#define FRF_AB_PCIE_TXTERMADJ_L_WIDTH 2 +#define FFE_AB_PCIE_TXTERMADJ_MIN15PCNT 3 +#define FFE_AB_PCIE_TXTERMADJ_PL10PCNT 2 +#define FFE_AB_PCIE_TXTERMADJ_MIN17PCNT 1 +#define FFE_AB_PCIE_TXTERMADJ_NOMNL 0 +#define FRF_AB_PCIE_RXEQCTL_H_LBN 18 +#define FRF_AB_PCIE_RXEQCTL_H_WIDTH 2 +#define FRF_AB_PCIE_RXEQCTL_L_LBN 16 +#define FRF_AB_PCIE_RXEQCTL_L_WIDTH 2 +#define FFE_AB_PCIE_RXEQCTL_OFF_ALT 3 +#define FFE_AB_PCIE_RXEQCTL_OFF 2 +#define FFE_AB_PCIE_RXEQCTL_MIN 1 +#define FFE_AB_PCIE_RXEQCTL_MAX 0 +#define FRF_AB_PCIE_HIDRV_LBN 8 +#define FRF_AB_PCIE_HIDRV_WIDTH 8 +#define FRF_AB_PCIE_LODRV_LBN 0 +#define FRF_AB_PCIE_LODRV_WIDTH 8 + +/* PCIE_SD_CTL45_REG: PCIE SerDes control register 4 and 5 */ +#define FR_AB_PCIE_SD_CTL45 0x00000330 +#define FRF_AB_PCIE_DTX7_LBN 60 +#define FRF_AB_PCIE_DTX7_WIDTH 4 +#define FRF_AB_PCIE_DTX6_LBN 56 +#define FRF_AB_PCIE_DTX6_WIDTH 4 +#define FRF_AB_PCIE_DTX5_LBN 52 +#define FRF_AB_PCIE_DTX5_WIDTH 4 +#define FRF_AB_PCIE_DTX4_LBN 48 +#define FRF_AB_PCIE_DTX4_WIDTH 4 +#define FRF_AB_PCIE_DTX3_LBN 44 +#define FRF_AB_PCIE_DTX3_WIDTH 4 +#define FRF_AB_PCIE_DTX2_LBN 40 +#define FRF_AB_PCIE_DTX2_WIDTH 4 +#define FRF_AB_PCIE_DTX1_LBN 36 +#define FRF_AB_PCIE_DTX1_WIDTH 4 +#define FRF_AB_PCIE_DTX0_LBN 32 +#define FRF_AB_PCIE_DTX0_WIDTH 4 +#define FRF_AB_PCIE_DEQ7_LBN 28 +#define FRF_AB_PCIE_DEQ7_WIDTH 4 +#define FRF_AB_PCIE_DEQ6_LBN 24 +#define FRF_AB_PCIE_DEQ6_WIDTH 4 +#define FRF_AB_PCIE_DEQ5_LBN 20 +#define FRF_AB_PCIE_DEQ5_WIDTH 4 +#define FRF_AB_PCIE_DEQ4_LBN 16 +#define FRF_AB_PCIE_DEQ4_WIDTH 4 +#define FRF_AB_PCIE_DEQ3_LBN 12 +#define FRF_AB_PCIE_DEQ3_WIDTH 4 +#define FRF_AB_PCIE_DEQ2_LBN 8 +#define FRF_AB_PCIE_DEQ2_WIDTH 4 +#define FRF_AB_PCIE_DEQ1_LBN 4 +#define FRF_AB_PCIE_DEQ1_WIDTH 4 +#define FRF_AB_PCIE_DEQ0_LBN 0 +#define FRF_AB_PCIE_DEQ0_WIDTH 4 + +/* PCIE_PCS_CTL_STAT_REG: PCIE PCS control and status register */ +#define FR_AB_PCIE_PCS_CTL_STAT 0x00000340 +#define FRF_AB_PCIE_PRBSERRCOUNT0_H_LBN 52 +#define FRF_AB_PCIE_PRBSERRCOUNT0_H_WIDTH 4 +#define FRF_AB_PCIE_PRBSERRCOUNT0_L_LBN 48 +#define FRF_AB_PCIE_PRBSERRCOUNT0_L_WIDTH 4 +#define FRF_AB_PCIE_PRBSERR_LBN 40 +#define FRF_AB_PCIE_PRBSERR_WIDTH 8 +#define FRF_AB_PCIE_PRBSERRH0_LBN 32 +#define FRF_AB_PCIE_PRBSERRH0_WIDTH 8 +#define FRF_AB_PCIE_FASTINIT_H_LBN 15 +#define FRF_AB_PCIE_FASTINIT_H_WIDTH 1 +#define FRF_AB_PCIE_FASTINIT_L_LBN 14 +#define FRF_AB_PCIE_FASTINIT_L_WIDTH 1 +#define FRF_AB_PCIE_CTCDISABLE_H_LBN 13 +#define FRF_AB_PCIE_CTCDISABLE_H_WIDTH 1 +#define FRF_AB_PCIE_CTCDISABLE_L_LBN 12 +#define FRF_AB_PCIE_CTCDISABLE_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSSYNC_H_LBN 11 +#define FRF_AB_PCIE_PRBSSYNC_H_WIDTH 1 +#define FRF_AB_PCIE_PRBSSYNC_L_LBN 10 +#define FRF_AB_PCIE_PRBSSYNC_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSERRACK_H_LBN 9 +#define FRF_AB_PCIE_PRBSERRACK_H_WIDTH 1 +#define FRF_AB_PCIE_PRBSERRACK_L_LBN 8 +#define FRF_AB_PCIE_PRBSERRACK_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSSEL_LBN 0 +#define FRF_AB_PCIE_PRBSSEL_WIDTH 8 + +/* DEBUG_DATA_OUT_REG: Live Debug and Debug 2 out ports */ +#define FR_BB_DEBUG_DATA_OUT 0x00000350 +#define FRF_BB_DEBUG2_PORT_LBN 25 +#define FRF_BB_DEBUG2_PORT_WIDTH 15 +#define FRF_BB_DEBUG1_PORT_LBN 0 +#define FRF_BB_DEBUG1_PORT_WIDTH 25 + +/* EVQ_RPTR_REGP0: Event queue read pointer register */ +#define FR_BZ_EVQ_RPTR_P0 0x00000400 +#define FR_BZ_EVQ_RPTR_P0_STEP 8192 +#define FR_BZ_EVQ_RPTR_P0_ROWS 1024 +/* EVQ_RPTR_REG_KER: Event queue read pointer register */ +#define FR_AA_EVQ_RPTR_KER 0x00011b00 +#define FR_AA_EVQ_RPTR_KER_STEP 4 +#define FR_AA_EVQ_RPTR_KER_ROWS 4 +/* EVQ_RPTR_REG: Event queue read pointer register */ +#define FR_BZ_EVQ_RPTR 0x00fa0000 +#define FR_BZ_EVQ_RPTR_STEP 16 +#define FR_BB_EVQ_RPTR_ROWS 4096 +#define FR_CZ_EVQ_RPTR_ROWS 1024 +/* EVQ_RPTR_REGP123: Event queue read pointer register */ +#define FR_BB_EVQ_RPTR_P123 0x01000400 +#define FR_BB_EVQ_RPTR_P123_STEP 8192 +#define FR_BB_EVQ_RPTR_P123_ROWS 3072 +#define FRF_AZ_EVQ_RPTR_VLD_LBN 15 +#define FRF_AZ_EVQ_RPTR_VLD_WIDTH 1 +#define FRF_AZ_EVQ_RPTR_LBN 0 +#define FRF_AZ_EVQ_RPTR_WIDTH 15 + +/* TIMER_COMMAND_REGP0: Timer Command Registers */ +#define FR_BZ_TIMER_COMMAND_P0 0x00000420 +#define FR_BZ_TIMER_COMMAND_P0_STEP 8192 +#define FR_BZ_TIMER_COMMAND_P0_ROWS 1024 +/* TIMER_COMMAND_REG_KER: Timer Command Registers */ +#define FR_AA_TIMER_COMMAND_KER 0x00000420 +#define FR_AA_TIMER_COMMAND_KER_STEP 8192 +#define FR_AA_TIMER_COMMAND_KER_ROWS 4 +/* TIMER_COMMAND_REGP123: Timer Command Registers */ +#define FR_BB_TIMER_COMMAND_P123 0x01000420 +#define FR_BB_TIMER_COMMAND_P123_STEP 8192 +#define FR_BB_TIMER_COMMAND_P123_ROWS 3072 +#define FRF_CZ_TC_TIMER_MODE_LBN 14 +#define FRF_CZ_TC_TIMER_MODE_WIDTH 2 +#define FRF_AB_TC_TIMER_MODE_LBN 12 +#define FRF_AB_TC_TIMER_MODE_WIDTH 2 +#define FRF_CZ_TC_TIMER_VAL_LBN 0 +#define FRF_CZ_TC_TIMER_VAL_WIDTH 14 +#define FRF_AB_TC_TIMER_VAL_LBN 0 +#define FRF_AB_TC_TIMER_VAL_WIDTH 12 + +/* DRV_EV_REG: Driver generated event register */ +#define FR_AZ_DRV_EV 0x00000440 +#define FRF_AZ_DRV_EV_QID_LBN 64 +#define FRF_AZ_DRV_EV_QID_WIDTH 12 +#define FRF_AZ_DRV_EV_DATA_LBN 0 +#define FRF_AZ_DRV_EV_DATA_WIDTH 64 + +/* EVQ_CTL_REG: Event queue control register */ +#define FR_AZ_EVQ_CTL 0x00000450 +#define FRF_CZ_RX_EVQ_WAKEUP_MASK_LBN 15 +#define FRF_CZ_RX_EVQ_WAKEUP_MASK_WIDTH 10 +#define FRF_BB_RX_EVQ_WAKEUP_MASK_LBN 15 +#define FRF_BB_RX_EVQ_WAKEUP_MASK_WIDTH 6 +#define FRF_AZ_EVQ_OWNERR_CTL_LBN 14 +#define FRF_AZ_EVQ_OWNERR_CTL_WIDTH 1 +#define FRF_AZ_EVQ_FIFO_AF_TH_LBN 7 +#define FRF_AZ_EVQ_FIFO_AF_TH_WIDTH 7 +#define FRF_AZ_EVQ_FIFO_NOTAF_TH_LBN 0 +#define FRF_AZ_EVQ_FIFO_NOTAF_TH_WIDTH 7 + +/* EVQ_CNT1_REG: Event counter 1 register */ +#define FR_AZ_EVQ_CNT1 0x00000460 +#define FRF_AZ_EVQ_CNT_PRE_FIFO_LBN 120 +#define FRF_AZ_EVQ_CNT_PRE_FIFO_WIDTH 7 +#define FRF_AZ_EVQ_CNT_TOBIU_LBN 100 +#define FRF_AZ_EVQ_CNT_TOBIU_WIDTH 20 +#define FRF_AZ_EVQ_TX_REQ_CNT_LBN 80 +#define FRF_AZ_EVQ_TX_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_RX_REQ_CNT_LBN 60 +#define FRF_AZ_EVQ_RX_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_EM_REQ_CNT_LBN 40 +#define FRF_AZ_EVQ_EM_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_CSR_REQ_CNT_LBN 20 +#define FRF_AZ_EVQ_CSR_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_ERR_REQ_CNT_LBN 0 +#define FRF_AZ_EVQ_ERR_REQ_CNT_WIDTH 20 + +/* EVQ_CNT2_REG: Event counter 2 register */ +#define FR_AZ_EVQ_CNT2 0x00000470 +#define FRF_AZ_EVQ_UPD_REQ_CNT_LBN 104 +#define FRF_AZ_EVQ_UPD_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_CLR_REQ_CNT_LBN 84 +#define FRF_AZ_EVQ_CLR_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_RDY_CNT_LBN 80 +#define FRF_AZ_EVQ_RDY_CNT_WIDTH 4 +#define FRF_AZ_EVQ_WU_REQ_CNT_LBN 60 +#define FRF_AZ_EVQ_WU_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_WET_REQ_CNT_LBN 40 +#define FRF_AZ_EVQ_WET_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_INIT_REQ_CNT_LBN 20 +#define FRF_AZ_EVQ_INIT_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_TM_REQ_CNT_LBN 0 +#define FRF_AZ_EVQ_TM_REQ_CNT_WIDTH 20 + +/* USR_EV_REG: Event mailbox register */ +#define FR_CZ_USR_EV 0x00000540 +#define FR_CZ_USR_EV_STEP 8192 +#define FR_CZ_USR_EV_ROWS 1024 +#define FRF_CZ_USR_EV_DATA_LBN 0 +#define FRF_CZ_USR_EV_DATA_WIDTH 32 + +/* BUF_TBL_CFG_REG: Buffer table configuration register */ +#define FR_AZ_BUF_TBL_CFG 0x00000600 +#define FRF_AZ_BUF_TBL_MODE_LBN 3 +#define FRF_AZ_BUF_TBL_MODE_WIDTH 1 + +/* SRM_RX_DC_CFG_REG: SRAM receive descriptor cache configuration register */ +#define FR_AZ_SRM_RX_DC_CFG 0x00000610 +#define FRF_AZ_SRM_CLK_TMP_EN_LBN 21 +#define FRF_AZ_SRM_CLK_TMP_EN_WIDTH 1 +#define FRF_AZ_SRM_RX_DC_BASE_ADR_LBN 0 +#define FRF_AZ_SRM_RX_DC_BASE_ADR_WIDTH 21 + +/* SRM_TX_DC_CFG_REG: SRAM transmit descriptor cache configuration register */ +#define FR_AZ_SRM_TX_DC_CFG 0x00000620 +#define FRF_AZ_SRM_TX_DC_BASE_ADR_LBN 0 +#define FRF_AZ_SRM_TX_DC_BASE_ADR_WIDTH 21 + +/* SRM_CFG_REG: SRAM configuration register */ +#define FR_AZ_SRM_CFG 0x00000630 +#define FRF_AZ_SRM_OOB_ADR_INTEN_LBN 5 +#define FRF_AZ_SRM_OOB_ADR_INTEN_WIDTH 1 +#define FRF_AZ_SRM_OOB_BUF_INTEN_LBN 4 +#define FRF_AZ_SRM_OOB_BUF_INTEN_WIDTH 1 +#define FRF_AZ_SRM_INIT_EN_LBN 3 +#define FRF_AZ_SRM_INIT_EN_WIDTH 1 +#define FRF_AZ_SRM_NUM_BANK_LBN 2 +#define FRF_AZ_SRM_NUM_BANK_WIDTH 1 +#define FRF_AZ_SRM_BANK_SIZE_LBN 0 +#define FRF_AZ_SRM_BANK_SIZE_WIDTH 2 + +/* BUF_TBL_UPD_REG: Buffer table update register */ +#define FR_AZ_BUF_TBL_UPD 0x00000650 +#define FRF_AZ_BUF_UPD_CMD_LBN 63 +#define FRF_AZ_BUF_UPD_CMD_WIDTH 1 +#define FRF_AZ_BUF_CLR_CMD_LBN 62 +#define FRF_AZ_BUF_CLR_CMD_WIDTH 1 +#define FRF_AZ_BUF_CLR_END_ID_LBN 32 +#define FRF_AZ_BUF_CLR_END_ID_WIDTH 20 +#define FRF_AZ_BUF_CLR_START_ID_LBN 0 +#define FRF_AZ_BUF_CLR_START_ID_WIDTH 20 + +/* SRM_UPD_EVQ_REG: Buffer table update register */ +#define FR_AZ_SRM_UPD_EVQ 0x00000660 +#define FRF_AZ_SRM_UPD_EVQ_ID_LBN 0 +#define FRF_AZ_SRM_UPD_EVQ_ID_WIDTH 12 + +/* SRAM_PARITY_REG: SRAM parity register. */ +#define FR_AZ_SRAM_PARITY 0x00000670 +#define FRF_CZ_BYPASS_ECC_LBN 3 +#define FRF_CZ_BYPASS_ECC_WIDTH 1 +#define FRF_CZ_SEC_INT_LBN 2 +#define FRF_CZ_SEC_INT_WIDTH 1 +#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_LBN 1 +#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_WIDTH 1 +#define FRF_AB_FORCE_SRAM_PERR_LBN 0 +#define FRF_AB_FORCE_SRAM_PERR_WIDTH 1 +#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_LBN 0 +#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_WIDTH 1 + +/* RX_CFG_REG: Receive configuration register */ +#define FR_AZ_RX_CFG 0x00000800 +#define FRF_CZ_RX_MIN_KBUF_SIZE_LBN 72 +#define FRF_CZ_RX_MIN_KBUF_SIZE_WIDTH 14 +#define FRF_CZ_RX_HDR_SPLIT_EN_LBN 71 +#define FRF_CZ_RX_HDR_SPLIT_EN_WIDTH 1 +#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_LBN 62 +#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_WIDTH 9 +#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_LBN 53 +#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_WIDTH 9 +#define FRF_CZ_RX_PRE_RFF_IPG_LBN 49 +#define FRF_CZ_RX_PRE_RFF_IPG_WIDTH 4 +#define FRF_BZ_RX_TCP_SUP_LBN 48 +#define FRF_BZ_RX_TCP_SUP_WIDTH 1 +#define FRF_BZ_RX_INGR_EN_LBN 47 +#define FRF_BZ_RX_INGR_EN_WIDTH 1 +#define FRF_BZ_RX_IP_HASH_LBN 46 +#define FRF_BZ_RX_IP_HASH_WIDTH 1 +#define FRF_BZ_RX_HASH_ALG_LBN 45 +#define FRF_BZ_RX_HASH_ALG_WIDTH 1 +#define FRF_BZ_RX_HASH_INSRT_HDR_LBN 44 +#define FRF_BZ_RX_HASH_INSRT_HDR_WIDTH 1 +#define FRF_BZ_RX_DESC_PUSH_EN_LBN 43 +#define FRF_BZ_RX_DESC_PUSH_EN_WIDTH 1 +#define FRF_BZ_RX_RDW_PATCH_EN_LBN 42 +#define FRF_BZ_RX_RDW_PATCH_EN_WIDTH 1 +#define FRF_BB_RX_PCI_BURST_SIZE_LBN 39 +#define FRF_BB_RX_PCI_BURST_SIZE_WIDTH 3 +#define FRF_BZ_RX_OWNERR_CTL_LBN 38 +#define FRF_BZ_RX_OWNERR_CTL_WIDTH 1 +#define FRF_BZ_RX_XON_TX_TH_LBN 33 +#define FRF_BZ_RX_XON_TX_TH_WIDTH 5 +#define FRF_AA_RX_DESC_PUSH_EN_LBN 35 +#define FRF_AA_RX_DESC_PUSH_EN_WIDTH 1 +#define FRF_AA_RX_RDW_PATCH_EN_LBN 34 +#define FRF_AA_RX_RDW_PATCH_EN_WIDTH 1 +#define FRF_AA_RX_PCI_BURST_SIZE_LBN 31 +#define FRF_AA_RX_PCI_BURST_SIZE_WIDTH 3 +#define FRF_BZ_RX_XOFF_TX_TH_LBN 28 +#define FRF_BZ_RX_XOFF_TX_TH_WIDTH 5 +#define FRF_AA_RX_OWNERR_CTL_LBN 30 +#define FRF_AA_RX_OWNERR_CTL_WIDTH 1 +#define FRF_AA_RX_XON_TX_TH_LBN 25 +#define FRF_AA_RX_XON_TX_TH_WIDTH 5 +#define FRF_BZ_RX_USR_BUF_SIZE_LBN 19 +#define FRF_BZ_RX_USR_BUF_SIZE_WIDTH 9 +#define FRF_AA_RX_XOFF_TX_TH_LBN 20 +#define FRF_AA_RX_XOFF_TX_TH_WIDTH 5 +#define FRF_AA_RX_USR_BUF_SIZE_LBN 11 +#define FRF_AA_RX_USR_BUF_SIZE_WIDTH 9 +#define FRF_BZ_RX_XON_MAC_TH_LBN 10 +#define FRF_BZ_RX_XON_MAC_TH_WIDTH 9 +#define FRF_AA_RX_XON_MAC_TH_LBN 6 +#define FRF_AA_RX_XON_MAC_TH_WIDTH 5 +#define FRF_BZ_RX_XOFF_MAC_TH_LBN 1 +#define FRF_BZ_RX_XOFF_MAC_TH_WIDTH 9 +#define FRF_AA_RX_XOFF_MAC_TH_LBN 1 +#define FRF_AA_RX_XOFF_MAC_TH_WIDTH 5 +#define FRF_AZ_RX_XOFF_MAC_EN_LBN 0 +#define FRF_AZ_RX_XOFF_MAC_EN_WIDTH 1 + +/* RX_FILTER_CTL_REG: Receive filter control registers */ +#define FR_BZ_RX_FILTER_CTL 0x00000810 +#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_LBN 94 +#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_WIDTH 8 +#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_LBN 86 +#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_WIDTH 8 +#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_LBN 85 +#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_WIDTH 1 +#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_LBN 69 +#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_WIDTH 16 +#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_LBN 57 +#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_WIDTH 12 +#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_LBN 56 +#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_WIDTH 1 +#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_LBN 55 +#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_WIDTH 1 +#define FRF_CZ_UNICAST_NOMATCH_Q_ID_LBN 43 +#define FRF_CZ_UNICAST_NOMATCH_Q_ID_WIDTH 12 +#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_LBN 42 +#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_WIDTH 1 +#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_LBN 41 +#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_WIDTH 1 +#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_LBN 40 +#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_WIDTH 1 +#define FRF_BZ_UDP_FULL_SRCH_LIMIT_LBN 32 +#define FRF_BZ_UDP_FULL_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_NUM_KER_LBN 24 +#define FRF_BZ_NUM_KER_WIDTH 2 +#define FRF_BZ_UDP_WILD_SRCH_LIMIT_LBN 16 +#define FRF_BZ_UDP_WILD_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_TCP_WILD_SRCH_LIMIT_LBN 8 +#define FRF_BZ_TCP_WILD_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_TCP_FULL_SRCH_LIMIT_LBN 0 +#define FRF_BZ_TCP_FULL_SRCH_LIMIT_WIDTH 8 + +/* RX_FLUSH_DESCQ_REG: Receive flush descriptor queue register */ +#define FR_AZ_RX_FLUSH_DESCQ 0x00000820 +#define FRF_AZ_RX_FLUSH_DESCQ_CMD_LBN 24 +#define FRF_AZ_RX_FLUSH_DESCQ_CMD_WIDTH 1 +#define FRF_AZ_RX_FLUSH_DESCQ_LBN 0 +#define FRF_AZ_RX_FLUSH_DESCQ_WIDTH 12 + +/* RX_DESC_UPD_REGP0: Receive descriptor update register. */ +#define FR_BZ_RX_DESC_UPD_P0 0x00000830 +#define FR_BZ_RX_DESC_UPD_P0_STEP 8192 +#define FR_BZ_RX_DESC_UPD_P0_ROWS 1024 +/* RX_DESC_UPD_REG_KER: Receive descriptor update register. */ +#define FR_AA_RX_DESC_UPD_KER 0x00000830 +#define FR_AA_RX_DESC_UPD_KER_STEP 8192 +#define FR_AA_RX_DESC_UPD_KER_ROWS 4 +/* RX_DESC_UPD_REGP123: Receive descriptor update register. */ +#define FR_BB_RX_DESC_UPD_P123 0x01000830 +#define FR_BB_RX_DESC_UPD_P123_STEP 8192 +#define FR_BB_RX_DESC_UPD_P123_ROWS 3072 +#define FRF_AZ_RX_DESC_WPTR_LBN 96 +#define FRF_AZ_RX_DESC_WPTR_WIDTH 12 +#define FRF_AZ_RX_DESC_PUSH_CMD_LBN 95 +#define FRF_AZ_RX_DESC_PUSH_CMD_WIDTH 1 +#define FRF_AZ_RX_DESC_LBN 0 +#define FRF_AZ_RX_DESC_WIDTH 64 + +/* RX_DC_CFG_REG: Receive descriptor cache configuration register */ +#define FR_AZ_RX_DC_CFG 0x00000840 +#define FRF_AB_RX_MAX_PF_LBN 2 +#define FRF_AB_RX_MAX_PF_WIDTH 2 +#define FRF_AZ_RX_DC_SIZE_LBN 0 +#define FRF_AZ_RX_DC_SIZE_WIDTH 2 +#define FFE_AZ_RX_DC_SIZE_64 3 +#define FFE_AZ_RX_DC_SIZE_32 2 +#define FFE_AZ_RX_DC_SIZE_16 1 +#define FFE_AZ_RX_DC_SIZE_8 0 + +/* RX_DC_PF_WM_REG: Receive descriptor cache pre-fetch watermark register */ +#define FR_AZ_RX_DC_PF_WM 0x00000850 +#define FRF_AZ_RX_DC_PF_HWM_LBN 6 +#define FRF_AZ_RX_DC_PF_HWM_WIDTH 6 +#define FRF_AZ_RX_DC_PF_LWM_LBN 0 +#define FRF_AZ_RX_DC_PF_LWM_WIDTH 6 + +/* RX_RSS_TKEY_REG: RSS Toeplitz hash key */ +#define FR_BZ_RX_RSS_TKEY 0x00000860 +#define FRF_BZ_RX_RSS_TKEY_HI_LBN 64 +#define FRF_BZ_RX_RSS_TKEY_HI_WIDTH 64 +#define FRF_BZ_RX_RSS_TKEY_LO_LBN 0 +#define FRF_BZ_RX_RSS_TKEY_LO_WIDTH 64 + +/* RX_NODESC_DROP_REG: Receive dropped packet counter register */ +#define FR_AZ_RX_NODESC_DROP 0x00000880 +#define FRF_CZ_RX_NODESC_DROP_CNT_LBN 0 +#define FRF_CZ_RX_NODESC_DROP_CNT_WIDTH 32 +#define FRF_AB_RX_NODESC_DROP_CNT_LBN 0 +#define FRF_AB_RX_NODESC_DROP_CNT_WIDTH 16 + +/* RX_SELF_RST_REG: Receive self reset register */ +#define FR_AA_RX_SELF_RST 0x00000890 +#define FRF_AA_RX_ISCSI_DIS_LBN 17 +#define FRF_AA_RX_ISCSI_DIS_WIDTH 1 +#define FRF_AA_RX_SW_RST_REG_LBN 16 +#define FRF_AA_RX_SW_RST_REG_WIDTH 1 +#define FRF_AA_RX_NODESC_WAIT_DIS_LBN 9 +#define FRF_AA_RX_NODESC_WAIT_DIS_WIDTH 1 +#define FRF_AA_RX_SELF_RST_EN_LBN 8 +#define FRF_AA_RX_SELF_RST_EN_WIDTH 1 +#define FRF_AA_RX_MAX_PF_LAT_LBN 4 +#define FRF_AA_RX_MAX_PF_LAT_WIDTH 4 +#define FRF_AA_RX_MAX_LU_LAT_LBN 0 +#define FRF_AA_RX_MAX_LU_LAT_WIDTH 4 + +/* RX_DEBUG_REG: undocumented register */ +#define FR_AZ_RX_DEBUG 0x000008a0 +#define FRF_AZ_RX_DEBUG_LBN 0 +#define FRF_AZ_RX_DEBUG_WIDTH 64 + +/* RX_PUSH_DROP_REG: Receive descriptor push dropped counter register */ +#define FR_AZ_RX_PUSH_DROP 0x000008b0 +#define FRF_AZ_RX_PUSH_DROP_CNT_LBN 0 +#define FRF_AZ_RX_PUSH_DROP_CNT_WIDTH 32 + +/* RX_RSS_IPV6_REG1: IPv6 RSS Toeplitz hash key low bytes */ +#define FR_CZ_RX_RSS_IPV6_REG1 0x000008d0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_WIDTH 128 + +/* RX_RSS_IPV6_REG2: IPv6 RSS Toeplitz hash key middle bytes */ +#define FR_CZ_RX_RSS_IPV6_REG2 0x000008e0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_WIDTH 128 + +/* RX_RSS_IPV6_REG3: IPv6 RSS Toeplitz hash key upper bytes and IPv6 RSS settings */ +#define FR_CZ_RX_RSS_IPV6_REG3 0x000008f0 +#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_LBN 66 +#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_LBN 65 +#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_LBN 64 +#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH 64 + +/* TX_FLUSH_DESCQ_REG: Transmit flush descriptor queue register */ +#define FR_AZ_TX_FLUSH_DESCQ 0x00000a00 +#define FRF_AZ_TX_FLUSH_DESCQ_CMD_LBN 12 +#define FRF_AZ_TX_FLUSH_DESCQ_CMD_WIDTH 1 +#define FRF_AZ_TX_FLUSH_DESCQ_LBN 0 +#define FRF_AZ_TX_FLUSH_DESCQ_WIDTH 12 + +/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */ +#define FR_BZ_TX_DESC_UPD_P0 0x00000a10 +#define FR_BZ_TX_DESC_UPD_P0_STEP 8192 +#define FR_BZ_TX_DESC_UPD_P0_ROWS 1024 +/* TX_DESC_UPD_REG_KER: Transmit descriptor update register. */ +#define FR_AA_TX_DESC_UPD_KER 0x00000a10 +#define FR_AA_TX_DESC_UPD_KER_STEP 8192 +#define FR_AA_TX_DESC_UPD_KER_ROWS 8 +/* TX_DESC_UPD_REGP123: Transmit descriptor update register. */ +#define FR_BB_TX_DESC_UPD_P123 0x01000a10 +#define FR_BB_TX_DESC_UPD_P123_STEP 8192 +#define FR_BB_TX_DESC_UPD_P123_ROWS 3072 +#define FRF_AZ_TX_DESC_WPTR_LBN 96 +#define FRF_AZ_TX_DESC_WPTR_WIDTH 12 +#define FRF_AZ_TX_DESC_PUSH_CMD_LBN 95 +#define FRF_AZ_TX_DESC_PUSH_CMD_WIDTH 1 +#define FRF_AZ_TX_DESC_LBN 0 +#define FRF_AZ_TX_DESC_WIDTH 95 + +/* TX_DC_CFG_REG: Transmit descriptor cache configuration register */ +#define FR_AZ_TX_DC_CFG 0x00000a20 +#define FRF_AZ_TX_DC_SIZE_LBN 0 +#define FRF_AZ_TX_DC_SIZE_WIDTH 2 +#define FFE_AZ_TX_DC_SIZE_32 2 +#define FFE_AZ_TX_DC_SIZE_16 1 +#define FFE_AZ_TX_DC_SIZE_8 0 + +/* TX_CHKSM_CFG_REG: Transmit checksum configuration register */ +#define FR_AA_TX_CHKSM_CFG 0x00000a30 +#define FRF_AA_TX_Q_CHKSM_DIS_96_127_LBN 96 +#define FRF_AA_TX_Q_CHKSM_DIS_96_127_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_64_95_LBN 64 +#define FRF_AA_TX_Q_CHKSM_DIS_64_95_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_32_63_LBN 32 +#define FRF_AA_TX_Q_CHKSM_DIS_32_63_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_0_31_LBN 0 +#define FRF_AA_TX_Q_CHKSM_DIS_0_31_WIDTH 32 + +/* TX_CFG_REG: Transmit configuration register */ +#define FR_AZ_TX_CFG 0x00000a50 +#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_LBN 114 +#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_WIDTH 8 +#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_LBN 113 +#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_WIDTH 1 +#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_LBN 105 +#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_LBN 97 +#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_LBN 89 +#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_LBN 81 +#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_LBN 73 +#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_LBN 65 +#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_LBN 64 +#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_WIDTH 1 +#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_LBN 48 +#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_WIDTH 16 +#define FRF_CZ_TX_FILTER_EN_BIT_LBN 47 +#define FRF_CZ_TX_FILTER_EN_BIT_WIDTH 1 +#define FRF_AZ_TX_IP_ID_P0_OFS_LBN 16 +#define FRF_AZ_TX_IP_ID_P0_OFS_WIDTH 15 +#define FRF_AZ_TX_NO_EOP_DISC_EN_LBN 5 +#define FRF_AZ_TX_NO_EOP_DISC_EN_WIDTH 1 +#define FRF_AZ_TX_P1_PRI_EN_LBN 4 +#define FRF_AZ_TX_P1_PRI_EN_WIDTH 1 +#define FRF_AZ_TX_OWNERR_CTL_LBN 2 +#define FRF_AZ_TX_OWNERR_CTL_WIDTH 1 +#define FRF_AA_TX_NON_IP_DROP_DIS_LBN 1 +#define FRF_AA_TX_NON_IP_DROP_DIS_WIDTH 1 +#define FRF_AZ_TX_IP_ID_REP_EN_LBN 0 +#define FRF_AZ_TX_IP_ID_REP_EN_WIDTH 1 + +/* TX_PUSH_DROP_REG: Transmit push dropped register */ +#define FR_AZ_TX_PUSH_DROP 0x00000a60 +#define FRF_AZ_TX_PUSH_DROP_CNT_LBN 0 +#define FRF_AZ_TX_PUSH_DROP_CNT_WIDTH 32 + +/* TX_RESERVED_REG: Transmit configuration register */ +#define FR_AZ_TX_RESERVED 0x00000a80 +#define FRF_AZ_TX_EVT_CNT_LBN 121 +#define FRF_AZ_TX_EVT_CNT_WIDTH 7 +#define FRF_AZ_TX_PREF_AGE_CNT_LBN 119 +#define FRF_AZ_TX_PREF_AGE_CNT_WIDTH 2 +#define FRF_AZ_TX_RD_COMP_TMR_LBN 96 +#define FRF_AZ_TX_RD_COMP_TMR_WIDTH 23 +#define FRF_AZ_TX_PUSH_EN_LBN 89 +#define FRF_AZ_TX_PUSH_EN_WIDTH 1 +#define FRF_AZ_TX_PUSH_CHK_DIS_LBN 88 +#define FRF_AZ_TX_PUSH_CHK_DIS_WIDTH 1 +#define FRF_AZ_TX_D_FF_FULL_P0_LBN 85 +#define FRF_AZ_TX_D_FF_FULL_P0_WIDTH 1 +#define FRF_AZ_TX_DMAR_ST_P0_LBN 81 +#define FRF_AZ_TX_DMAR_ST_P0_WIDTH 1 +#define FRF_AZ_TX_DMAQ_ST_LBN 78 +#define FRF_AZ_TX_DMAQ_ST_WIDTH 1 +#define FRF_AZ_TX_RX_SPACER_LBN 64 +#define FRF_AZ_TX_RX_SPACER_WIDTH 8 +#define FRF_AZ_TX_DROP_ABORT_EN_LBN 60 +#define FRF_AZ_TX_DROP_ABORT_EN_WIDTH 1 +#define FRF_AZ_TX_SOFT_EVT_EN_LBN 59 +#define FRF_AZ_TX_SOFT_EVT_EN_WIDTH 1 +#define FRF_AZ_TX_PS_EVT_DIS_LBN 58 +#define FRF_AZ_TX_PS_EVT_DIS_WIDTH 1 +#define FRF_AZ_TX_RX_SPACER_EN_LBN 57 +#define FRF_AZ_TX_RX_SPACER_EN_WIDTH 1 +#define FRF_AZ_TX_XP_TIMER_LBN 52 +#define FRF_AZ_TX_XP_TIMER_WIDTH 5 +#define FRF_AZ_TX_PREF_SPACER_LBN 44 +#define FRF_AZ_TX_PREF_SPACER_WIDTH 8 +#define FRF_AZ_TX_PREF_WD_TMR_LBN 22 +#define FRF_AZ_TX_PREF_WD_TMR_WIDTH 22 +#define FRF_AZ_TX_ONLY1TAG_LBN 21 +#define FRF_AZ_TX_ONLY1TAG_WIDTH 1 +#define FRF_AZ_TX_PREF_THRESHOLD_LBN 19 +#define FRF_AZ_TX_PREF_THRESHOLD_WIDTH 2 +#define FRF_AZ_TX_ONE_PKT_PER_Q_LBN 18 +#define FRF_AZ_TX_ONE_PKT_PER_Q_WIDTH 1 +#define FRF_AZ_TX_DIS_NON_IP_EV_LBN 17 +#define FRF_AZ_TX_DIS_NON_IP_EV_WIDTH 1 +#define FRF_AA_TX_DMA_FF_THR_LBN 16 +#define FRF_AA_TX_DMA_FF_THR_WIDTH 1 +#define FRF_AZ_TX_DMA_SPACER_LBN 8 +#define FRF_AZ_TX_DMA_SPACER_WIDTH 8 +#define FRF_AA_TX_TCP_DIS_LBN 7 +#define FRF_AA_TX_TCP_DIS_WIDTH 1 +#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_LBN 7 +#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_WIDTH 1 +#define FRF_AA_TX_IP_DIS_LBN 6 +#define FRF_AA_TX_IP_DIS_WIDTH 1 +#define FRF_AZ_TX_MAX_CPL_LBN 2 +#define FRF_AZ_TX_MAX_CPL_WIDTH 2 +#define FFE_AZ_TX_MAX_CPL_16 3 +#define FFE_AZ_TX_MAX_CPL_8 2 +#define FFE_AZ_TX_MAX_CPL_4 1 +#define FFE_AZ_TX_MAX_CPL_NOLIMIT 0 +#define FRF_AZ_TX_MAX_PREF_LBN 0 +#define FRF_AZ_TX_MAX_PREF_WIDTH 2 +#define FFE_AZ_TX_MAX_PREF_32 3 +#define FFE_AZ_TX_MAX_PREF_16 2 +#define FFE_AZ_TX_MAX_PREF_8 1 +#define FFE_AZ_TX_MAX_PREF_OFF 0 + +/* TX_PACE_REG: Transmit pace control register */ +#define FR_BZ_TX_PACE 0x00000a90 +#define FRF_BZ_TX_PACE_SB_NOT_AF_LBN 19 +#define FRF_BZ_TX_PACE_SB_NOT_AF_WIDTH 10 +#define FRF_BZ_TX_PACE_SB_AF_LBN 9 +#define FRF_BZ_TX_PACE_SB_AF_WIDTH 10 +#define FRF_BZ_TX_PACE_FB_BASE_LBN 5 +#define FRF_BZ_TX_PACE_FB_BASE_WIDTH 4 +#define FRF_BZ_TX_PACE_BIN_TH_LBN 0 +#define FRF_BZ_TX_PACE_BIN_TH_WIDTH 5 + +/* TX_PACE_DROP_QID_REG: PACE Drop QID Counter */ +#define FR_BZ_TX_PACE_DROP_QID 0x00000aa0 +#define FRF_BZ_TX_PACE_QID_DRP_CNT_LBN 0 +#define FRF_BZ_TX_PACE_QID_DRP_CNT_WIDTH 16 + +/* TX_VLAN_REG: Transmit VLAN tag register */ +#define FR_BB_TX_VLAN 0x00000ae0 +#define FRF_BB_TX_VLAN_EN_LBN 127 +#define FRF_BB_TX_VLAN_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_PORT1_EN_LBN 125 +#define FRF_BB_TX_VLAN7_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_PORT0_EN_LBN 124 +#define FRF_BB_TX_VLAN7_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_LBN 112 +#define FRF_BB_TX_VLAN7_WIDTH 12 +#define FRF_BB_TX_VLAN6_PORT1_EN_LBN 109 +#define FRF_BB_TX_VLAN6_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN6_PORT0_EN_LBN 108 +#define FRF_BB_TX_VLAN6_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN6_LBN 96 +#define FRF_BB_TX_VLAN6_WIDTH 12 +#define FRF_BB_TX_VLAN5_PORT1_EN_LBN 93 +#define FRF_BB_TX_VLAN5_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN5_PORT0_EN_LBN 92 +#define FRF_BB_TX_VLAN5_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN5_LBN 80 +#define FRF_BB_TX_VLAN5_WIDTH 12 +#define FRF_BB_TX_VLAN4_PORT1_EN_LBN 77 +#define FRF_BB_TX_VLAN4_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN4_PORT0_EN_LBN 76 +#define FRF_BB_TX_VLAN4_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN4_LBN 64 +#define FRF_BB_TX_VLAN4_WIDTH 12 +#define FRF_BB_TX_VLAN3_PORT1_EN_LBN 61 +#define FRF_BB_TX_VLAN3_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN3_PORT0_EN_LBN 60 +#define FRF_BB_TX_VLAN3_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN3_LBN 48 +#define FRF_BB_TX_VLAN3_WIDTH 12 +#define FRF_BB_TX_VLAN2_PORT1_EN_LBN 45 +#define FRF_BB_TX_VLAN2_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN2_PORT0_EN_LBN 44 +#define FRF_BB_TX_VLAN2_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN2_LBN 32 +#define FRF_BB_TX_VLAN2_WIDTH 12 +#define FRF_BB_TX_VLAN1_PORT1_EN_LBN 29 +#define FRF_BB_TX_VLAN1_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN1_PORT0_EN_LBN 28 +#define FRF_BB_TX_VLAN1_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN1_LBN 16 +#define FRF_BB_TX_VLAN1_WIDTH 12 +#define FRF_BB_TX_VLAN0_PORT1_EN_LBN 13 +#define FRF_BB_TX_VLAN0_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN0_PORT0_EN_LBN 12 +#define FRF_BB_TX_VLAN0_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN0_LBN 0 +#define FRF_BB_TX_VLAN0_WIDTH 12 + +/* TX_IPFIL_PORTEN_REG: Transmit filter control register */ +#define FR_BZ_TX_IPFIL_PORTEN 0x00000af0 +#define FRF_BZ_TX_MADR0_FIL_EN_LBN 64 +#define FRF_BZ_TX_MADR0_FIL_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL31_PORT_EN_LBN 62 +#define FRF_BB_TX_IPFIL31_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL30_PORT_EN_LBN 60 +#define FRF_BB_TX_IPFIL30_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL29_PORT_EN_LBN 58 +#define FRF_BB_TX_IPFIL29_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL28_PORT_EN_LBN 56 +#define FRF_BB_TX_IPFIL28_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL27_PORT_EN_LBN 54 +#define FRF_BB_TX_IPFIL27_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL26_PORT_EN_LBN 52 +#define FRF_BB_TX_IPFIL26_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL25_PORT_EN_LBN 50 +#define FRF_BB_TX_IPFIL25_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL24_PORT_EN_LBN 48 +#define FRF_BB_TX_IPFIL24_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL23_PORT_EN_LBN 46 +#define FRF_BB_TX_IPFIL23_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL22_PORT_EN_LBN 44 +#define FRF_BB_TX_IPFIL22_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL21_PORT_EN_LBN 42 +#define FRF_BB_TX_IPFIL21_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL20_PORT_EN_LBN 40 +#define FRF_BB_TX_IPFIL20_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL19_PORT_EN_LBN 38 +#define FRF_BB_TX_IPFIL19_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL18_PORT_EN_LBN 36 +#define FRF_BB_TX_IPFIL18_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL17_PORT_EN_LBN 34 +#define FRF_BB_TX_IPFIL17_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL16_PORT_EN_LBN 32 +#define FRF_BB_TX_IPFIL16_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL15_PORT_EN_LBN 30 +#define FRF_BB_TX_IPFIL15_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL14_PORT_EN_LBN 28 +#define FRF_BB_TX_IPFIL14_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL13_PORT_EN_LBN 26 +#define FRF_BB_TX_IPFIL13_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL12_PORT_EN_LBN 24 +#define FRF_BB_TX_IPFIL12_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL11_PORT_EN_LBN 22 +#define FRF_BB_TX_IPFIL11_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL10_PORT_EN_LBN 20 +#define FRF_BB_TX_IPFIL10_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL9_PORT_EN_LBN 18 +#define FRF_BB_TX_IPFIL9_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL8_PORT_EN_LBN 16 +#define FRF_BB_TX_IPFIL8_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL7_PORT_EN_LBN 14 +#define FRF_BB_TX_IPFIL7_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL6_PORT_EN_LBN 12 +#define FRF_BB_TX_IPFIL6_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL5_PORT_EN_LBN 10 +#define FRF_BB_TX_IPFIL5_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL4_PORT_EN_LBN 8 +#define FRF_BB_TX_IPFIL4_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL3_PORT_EN_LBN 6 +#define FRF_BB_TX_IPFIL3_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL2_PORT_EN_LBN 4 +#define FRF_BB_TX_IPFIL2_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL1_PORT_EN_LBN 2 +#define FRF_BB_TX_IPFIL1_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL0_PORT_EN_LBN 0 +#define FRF_BB_TX_IPFIL0_PORT_EN_WIDTH 1 + +/* TX_IPFIL_TBL: Transmit IP source address filter table */ +#define FR_BB_TX_IPFIL_TBL 0x00000b00 +#define FR_BB_TX_IPFIL_TBL_STEP 16 +#define FR_BB_TX_IPFIL_TBL_ROWS 16 +#define FRF_BB_TX_IPFIL_MASK_1_LBN 96 +#define FRF_BB_TX_IPFIL_MASK_1_WIDTH 32 +#define FRF_BB_TX_IP_SRC_ADR_1_LBN 64 +#define FRF_BB_TX_IP_SRC_ADR_1_WIDTH 32 +#define FRF_BB_TX_IPFIL_MASK_0_LBN 32 +#define FRF_BB_TX_IPFIL_MASK_0_WIDTH 32 +#define FRF_BB_TX_IP_SRC_ADR_0_LBN 0 +#define FRF_BB_TX_IP_SRC_ADR_0_WIDTH 32 + +/* MD_TXD_REG: PHY management transmit data register */ +#define FR_AB_MD_TXD 0x00000c00 +#define FRF_AB_MD_TXD_LBN 0 +#define FRF_AB_MD_TXD_WIDTH 16 + +/* MD_RXD_REG: PHY management receive data register */ +#define FR_AB_MD_RXD 0x00000c10 +#define FRF_AB_MD_RXD_LBN 0 +#define FRF_AB_MD_RXD_WIDTH 16 + +/* MD_CS_REG: PHY management configuration & status register */ +#define FR_AB_MD_CS 0x00000c20 +#define FRF_AB_MD_RD_EN_CMD_LBN 15 +#define FRF_AB_MD_RD_EN_CMD_WIDTH 1 +#define FRF_AB_MD_WR_EN_CMD_LBN 14 +#define FRF_AB_MD_WR_EN_CMD_WIDTH 1 +#define FRF_AB_MD_ADDR_CMD_LBN 13 +#define FRF_AB_MD_ADDR_CMD_WIDTH 1 +#define FRF_AB_MD_PT_LBN 7 +#define FRF_AB_MD_PT_WIDTH 3 +#define FRF_AB_MD_PL_LBN 6 +#define FRF_AB_MD_PL_WIDTH 1 +#define FRF_AB_MD_INT_CLR_LBN 5 +#define FRF_AB_MD_INT_CLR_WIDTH 1 +#define FRF_AB_MD_GC_LBN 4 +#define FRF_AB_MD_GC_WIDTH 1 +#define FRF_AB_MD_PRSP_LBN 3 +#define FRF_AB_MD_PRSP_WIDTH 1 +#define FRF_AB_MD_RIC_LBN 2 +#define FRF_AB_MD_RIC_WIDTH 1 +#define FRF_AB_MD_RDC_LBN 1 +#define FRF_AB_MD_RDC_WIDTH 1 +#define FRF_AB_MD_WRC_LBN 0 +#define FRF_AB_MD_WRC_WIDTH 1 + +/* MD_PHY_ADR_REG: PHY management PHY address register */ +#define FR_AB_MD_PHY_ADR 0x00000c30 +#define FRF_AB_MD_PHY_ADR_LBN 0 +#define FRF_AB_MD_PHY_ADR_WIDTH 16 + +/* MD_ID_REG: PHY management ID register */ +#define FR_AB_MD_ID 0x00000c40 +#define FRF_AB_MD_PRT_ADR_LBN 11 +#define FRF_AB_MD_PRT_ADR_WIDTH 5 +#define FRF_AB_MD_DEV_ADR_LBN 6 +#define FRF_AB_MD_DEV_ADR_WIDTH 5 + +/* MD_STAT_REG: PHY management status & mask register */ +#define FR_AB_MD_STAT 0x00000c50 +#define FRF_AB_MD_PINT_LBN 4 +#define FRF_AB_MD_PINT_WIDTH 1 +#define FRF_AB_MD_DONE_LBN 3 +#define FRF_AB_MD_DONE_WIDTH 1 +#define FRF_AB_MD_BSERR_LBN 2 +#define FRF_AB_MD_BSERR_WIDTH 1 +#define FRF_AB_MD_LNFL_LBN 1 +#define FRF_AB_MD_LNFL_WIDTH 1 +#define FRF_AB_MD_BSY_LBN 0 +#define FRF_AB_MD_BSY_WIDTH 1 + +/* MAC_STAT_DMA_REG: Port MAC statistical counter DMA register */ +#define FR_AB_MAC_STAT_DMA 0x00000c60 +#define FRF_AB_MAC_STAT_DMA_CMD_LBN 48 +#define FRF_AB_MAC_STAT_DMA_CMD_WIDTH 1 +#define FRF_AB_MAC_STAT_DMA_ADR_LBN 0 +#define FRF_AB_MAC_STAT_DMA_ADR_WIDTH 48 + +/* MAC_CTRL_REG: Port MAC control register */ +#define FR_AB_MAC_CTRL 0x00000c80 +#define FRF_AB_MAC_XOFF_VAL_LBN 16 +#define FRF_AB_MAC_XOFF_VAL_WIDTH 16 +#define FRF_BB_TXFIFO_DRAIN_EN_LBN 7 +#define FRF_BB_TXFIFO_DRAIN_EN_WIDTH 1 +#define FRF_AB_MAC_XG_DISTXCRC_LBN 5 +#define FRF_AB_MAC_XG_DISTXCRC_WIDTH 1 +#define FRF_AB_MAC_BCAD_ACPT_LBN 4 +#define FRF_AB_MAC_BCAD_ACPT_WIDTH 1 +#define FRF_AB_MAC_UC_PROM_LBN 3 +#define FRF_AB_MAC_UC_PROM_WIDTH 1 +#define FRF_AB_MAC_LINK_STATUS_LBN 2 +#define FRF_AB_MAC_LINK_STATUS_WIDTH 1 +#define FRF_AB_MAC_SPEED_LBN 0 +#define FRF_AB_MAC_SPEED_WIDTH 2 +#define FFE_AB_MAC_SPEED_10G 3 +#define FFE_AB_MAC_SPEED_1G 2 +#define FFE_AB_MAC_SPEED_100M 1 +#define FFE_AB_MAC_SPEED_10M 0 + +/* GEN_MODE_REG: General Purpose mode register (external interrupt mask) */ +#define FR_BB_GEN_MODE 0x00000c90 +#define FRF_BB_XFP_PHY_INT_POL_SEL_LBN 3 +#define FRF_BB_XFP_PHY_INT_POL_SEL_WIDTH 1 +#define FRF_BB_XG_PHY_INT_POL_SEL_LBN 2 +#define FRF_BB_XG_PHY_INT_POL_SEL_WIDTH 1 +#define FRF_BB_XFP_PHY_INT_MASK_LBN 1 +#define FRF_BB_XFP_PHY_INT_MASK_WIDTH 1 +#define FRF_BB_XG_PHY_INT_MASK_LBN 0 +#define FRF_BB_XG_PHY_INT_MASK_WIDTH 1 + +/* MAC_MC_HASH_REG0: Multicast address hash table */ +#define FR_AB_MAC_MC_HASH_REG0 0x00000ca0 +#define FRF_AB_MAC_MCAST_HASH0_LBN 0 +#define FRF_AB_MAC_MCAST_HASH0_WIDTH 128 + +/* MAC_MC_HASH_REG1: Multicast address hash table */ +#define FR_AB_MAC_MC_HASH_REG1 0x00000cb0 +#define FRF_AB_MAC_MCAST_HASH1_LBN 0 +#define FRF_AB_MAC_MCAST_HASH1_WIDTH 128 + +/* GM_CFG1_REG: GMAC configuration register 1 */ +#define FR_AB_GM_CFG1 0x00000e00 +#define FRF_AB_GM_SW_RST_LBN 31 +#define FRF_AB_GM_SW_RST_WIDTH 1 +#define FRF_AB_GM_SIM_RST_LBN 30 +#define FRF_AB_GM_SIM_RST_WIDTH 1 +#define FRF_AB_GM_RST_RX_MAC_CTL_LBN 19 +#define FRF_AB_GM_RST_RX_MAC_CTL_WIDTH 1 +#define FRF_AB_GM_RST_TX_MAC_CTL_LBN 18 +#define FRF_AB_GM_RST_TX_MAC_CTL_WIDTH 1 +#define FRF_AB_GM_RST_RX_FUNC_LBN 17 +#define FRF_AB_GM_RST_RX_FUNC_WIDTH 1 +#define FRF_AB_GM_RST_TX_FUNC_LBN 16 +#define FRF_AB_GM_RST_TX_FUNC_WIDTH 1 +#define FRF_AB_GM_LOOP_LBN 8 +#define FRF_AB_GM_LOOP_WIDTH 1 +#define FRF_AB_GM_RX_FC_EN_LBN 5 +#define FRF_AB_GM_RX_FC_EN_WIDTH 1 +#define FRF_AB_GM_TX_FC_EN_LBN 4 +#define FRF_AB_GM_TX_FC_EN_WIDTH 1 +#define FRF_AB_GM_SYNC_RXEN_LBN 3 +#define FRF_AB_GM_SYNC_RXEN_WIDTH 1 +#define FRF_AB_GM_RX_EN_LBN 2 +#define FRF_AB_GM_RX_EN_WIDTH 1 +#define FRF_AB_GM_SYNC_TXEN_LBN 1 +#define FRF_AB_GM_SYNC_TXEN_WIDTH 1 +#define FRF_AB_GM_TX_EN_LBN 0 +#define FRF_AB_GM_TX_EN_WIDTH 1 + +/* GM_CFG2_REG: GMAC configuration register 2 */ +#define FR_AB_GM_CFG2 0x00000e10 +#define FRF_AB_GM_PAMBL_LEN_LBN 12 +#define FRF_AB_GM_PAMBL_LEN_WIDTH 4 +#define FRF_AB_GM_IF_MODE_LBN 8 +#define FRF_AB_GM_IF_MODE_WIDTH 2 +#define FFE_AB_IF_MODE_BYTE_MODE 2 +#define FFE_AB_IF_MODE_NIBBLE_MODE 1 +#define FRF_AB_GM_HUGE_FRM_EN_LBN 5 +#define FRF_AB_GM_HUGE_FRM_EN_WIDTH 1 +#define FRF_AB_GM_LEN_CHK_LBN 4 +#define FRF_AB_GM_LEN_CHK_WIDTH 1 +#define FRF_AB_GM_PAD_CRC_EN_LBN 2 +#define FRF_AB_GM_PAD_CRC_EN_WIDTH 1 +#define FRF_AB_GM_CRC_EN_LBN 1 +#define FRF_AB_GM_CRC_EN_WIDTH 1 +#define FRF_AB_GM_FD_LBN 0 +#define FRF_AB_GM_FD_WIDTH 1 + +/* GM_IPG_REG: GMAC IPG register */ +#define FR_AB_GM_IPG 0x00000e20 +#define FRF_AB_GM_NONB2B_IPG1_LBN 24 +#define FRF_AB_GM_NONB2B_IPG1_WIDTH 7 +#define FRF_AB_GM_NONB2B_IPG2_LBN 16 +#define FRF_AB_GM_NONB2B_IPG2_WIDTH 7 +#define FRF_AB_GM_MIN_IPG_ENF_LBN 8 +#define FRF_AB_GM_MIN_IPG_ENF_WIDTH 8 +#define FRF_AB_GM_B2B_IPG_LBN 0 +#define FRF_AB_GM_B2B_IPG_WIDTH 7 + +/* GM_HD_REG: GMAC half duplex register */ +#define FR_AB_GM_HD 0x00000e30 +#define FRF_AB_GM_ALT_BOFF_VAL_LBN 20 +#define FRF_AB_GM_ALT_BOFF_VAL_WIDTH 4 +#define FRF_AB_GM_ALT_BOFF_EN_LBN 19 +#define FRF_AB_GM_ALT_BOFF_EN_WIDTH 1 +#define FRF_AB_GM_BP_NO_BOFF_LBN 18 +#define FRF_AB_GM_BP_NO_BOFF_WIDTH 1 +#define FRF_AB_GM_DIS_BOFF_LBN 17 +#define FRF_AB_GM_DIS_BOFF_WIDTH 1 +#define FRF_AB_GM_EXDEF_TX_EN_LBN 16 +#define FRF_AB_GM_EXDEF_TX_EN_WIDTH 1 +#define FRF_AB_GM_RTRY_LIMIT_LBN 12 +#define FRF_AB_GM_RTRY_LIMIT_WIDTH 4 +#define FRF_AB_GM_COL_WIN_LBN 0 +#define FRF_AB_GM_COL_WIN_WIDTH 10 + +/* GM_MAX_FLEN_REG: GMAC maximum frame length register */ +#define FR_AB_GM_MAX_FLEN 0x00000e40 +#define FRF_AB_GM_MAX_FLEN_LBN 0 +#define FRF_AB_GM_MAX_FLEN_WIDTH 16 + +/* GM_TEST_REG: GMAC test register */ +#define FR_AB_GM_TEST 0x00000e70 +#define FRF_AB_GM_MAX_BOFF_LBN 3 +#define FRF_AB_GM_MAX_BOFF_WIDTH 1 +#define FRF_AB_GM_REG_TX_FLOW_EN_LBN 2 +#define FRF_AB_GM_REG_TX_FLOW_EN_WIDTH 1 +#define FRF_AB_GM_TEST_PAUSE_LBN 1 +#define FRF_AB_GM_TEST_PAUSE_WIDTH 1 +#define FRF_AB_GM_SHORT_SLOT_LBN 0 +#define FRF_AB_GM_SHORT_SLOT_WIDTH 1 + +/* GM_ADR1_REG: GMAC station address register 1 */ +#define FR_AB_GM_ADR1 0x00000f00 +#define FRF_AB_GM_ADR_B0_LBN 24 +#define FRF_AB_GM_ADR_B0_WIDTH 8 +#define FRF_AB_GM_ADR_B1_LBN 16 +#define FRF_AB_GM_ADR_B1_WIDTH 8 +#define FRF_AB_GM_ADR_B2_LBN 8 +#define FRF_AB_GM_ADR_B2_WIDTH 8 +#define FRF_AB_GM_ADR_B3_LBN 0 +#define FRF_AB_GM_ADR_B3_WIDTH 8 + +/* GM_ADR2_REG: GMAC station address register 2 */ +#define FR_AB_GM_ADR2 0x00000f10 +#define FRF_AB_GM_ADR_B4_LBN 24 +#define FRF_AB_GM_ADR_B4_WIDTH 8 +#define FRF_AB_GM_ADR_B5_LBN 16 +#define FRF_AB_GM_ADR_B5_WIDTH 8 + +/* GMF_CFG0_REG: GMAC FIFO configuration register 0 */ +#define FR_AB_GMF_CFG0 0x00000f20 +#define FRF_AB_GMF_FTFENRPLY_LBN 20 +#define FRF_AB_GMF_FTFENRPLY_WIDTH 1 +#define FRF_AB_GMF_STFENRPLY_LBN 19 +#define FRF_AB_GMF_STFENRPLY_WIDTH 1 +#define FRF_AB_GMF_FRFENRPLY_LBN 18 +#define FRF_AB_GMF_FRFENRPLY_WIDTH 1 +#define FRF_AB_GMF_SRFENRPLY_LBN 17 +#define FRF_AB_GMF_SRFENRPLY_WIDTH 1 +#define FRF_AB_GMF_WTMENRPLY_LBN 16 +#define FRF_AB_GMF_WTMENRPLY_WIDTH 1 +#define FRF_AB_GMF_FTFENREQ_LBN 12 +#define FRF_AB_GMF_FTFENREQ_WIDTH 1 +#define FRF_AB_GMF_STFENREQ_LBN 11 +#define FRF_AB_GMF_STFENREQ_WIDTH 1 +#define FRF_AB_GMF_FRFENREQ_LBN 10 +#define FRF_AB_GMF_FRFENREQ_WIDTH 1 +#define FRF_AB_GMF_SRFENREQ_LBN 9 +#define FRF_AB_GMF_SRFENREQ_WIDTH 1 +#define FRF_AB_GMF_WTMENREQ_LBN 8 +#define FRF_AB_GMF_WTMENREQ_WIDTH 1 +#define FRF_AB_GMF_HSTRSTFT_LBN 4 +#define FRF_AB_GMF_HSTRSTFT_WIDTH 1 +#define FRF_AB_GMF_HSTRSTST_LBN 3 +#define FRF_AB_GMF_HSTRSTST_WIDTH 1 +#define FRF_AB_GMF_HSTRSTFR_LBN 2 +#define FRF_AB_GMF_HSTRSTFR_WIDTH 1 +#define FRF_AB_GMF_HSTRSTSR_LBN 1 +#define FRF_AB_GMF_HSTRSTSR_WIDTH 1 +#define FRF_AB_GMF_HSTRSTWT_LBN 0 +#define FRF_AB_GMF_HSTRSTWT_WIDTH 1 + +/* GMF_CFG1_REG: GMAC FIFO configuration register 1 */ +#define FR_AB_GMF_CFG1 0x00000f30 +#define FRF_AB_GMF_CFGFRTH_LBN 16 +#define FRF_AB_GMF_CFGFRTH_WIDTH 5 +#define FRF_AB_GMF_CFGXOFFRTX_LBN 0 +#define FRF_AB_GMF_CFGXOFFRTX_WIDTH 16 + +/* GMF_CFG2_REG: GMAC FIFO configuration register 2 */ +#define FR_AB_GMF_CFG2 0x00000f40 +#define FRF_AB_GMF_CFGHWM_LBN 16 +#define FRF_AB_GMF_CFGHWM_WIDTH 6 +#define FRF_AB_GMF_CFGLWM_LBN 0 +#define FRF_AB_GMF_CFGLWM_WIDTH 6 + +/* GMF_CFG3_REG: GMAC FIFO configuration register 3 */ +#define FR_AB_GMF_CFG3 0x00000f50 +#define FRF_AB_GMF_CFGHWMFT_LBN 16 +#define FRF_AB_GMF_CFGHWMFT_WIDTH 6 +#define FRF_AB_GMF_CFGFTTH_LBN 0 +#define FRF_AB_GMF_CFGFTTH_WIDTH 6 + +/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */ +#define FR_AB_GMF_CFG4 0x00000f60 +#define FRF_AB_GMF_HSTFLTRFRM_LBN 0 +#define FRF_AB_GMF_HSTFLTRFRM_WIDTH 18 + +/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */ +#define FR_AB_GMF_CFG5 0x00000f70 +#define FRF_AB_GMF_CFGHDPLX_LBN 22 +#define FRF_AB_GMF_CFGHDPLX_WIDTH 1 +#define FRF_AB_GMF_SRFULL_LBN 21 +#define FRF_AB_GMF_SRFULL_WIDTH 1 +#define FRF_AB_GMF_HSTSRFULLCLR_LBN 20 +#define FRF_AB_GMF_HSTSRFULLCLR_WIDTH 1 +#define FRF_AB_GMF_CFGBYTMODE_LBN 19 +#define FRF_AB_GMF_CFGBYTMODE_WIDTH 1 +#define FRF_AB_GMF_HSTDRPLT64_LBN 18 +#define FRF_AB_GMF_HSTDRPLT64_WIDTH 1 +#define FRF_AB_GMF_HSTFLTRFRMDC_LBN 0 +#define FRF_AB_GMF_HSTFLTRFRMDC_WIDTH 18 + +/* TX_SRC_MAC_TBL: Transmit IP source address filter table */ +#define FR_BB_TX_SRC_MAC_TBL 0x00001000 +#define FR_BB_TX_SRC_MAC_TBL_STEP 16 +#define FR_BB_TX_SRC_MAC_TBL_ROWS 16 +#define FRF_BB_TX_SRC_MAC_ADR_1_LBN 64 +#define FRF_BB_TX_SRC_MAC_ADR_1_WIDTH 48 +#define FRF_BB_TX_SRC_MAC_ADR_0_LBN 0 +#define FRF_BB_TX_SRC_MAC_ADR_0_WIDTH 48 + +/* TX_SRC_MAC_CTL_REG: Transmit MAC source address filter control */ +#define FR_BB_TX_SRC_MAC_CTL 0x00001100 +#define FRF_BB_TX_SRC_DROP_CTR_LBN 16 +#define FRF_BB_TX_SRC_DROP_CTR_WIDTH 16 +#define FRF_BB_TX_SRC_FLTR_EN_LBN 15 +#define FRF_BB_TX_SRC_FLTR_EN_WIDTH 1 +#define FRF_BB_TX_DROP_CTR_CLR_LBN 12 +#define FRF_BB_TX_DROP_CTR_CLR_WIDTH 1 +#define FRF_BB_TX_MAC_QID_SEL_LBN 0 +#define FRF_BB_TX_MAC_QID_SEL_WIDTH 3 + +/* XM_ADR_LO_REG: XGMAC address register low */ +#define FR_AB_XM_ADR_LO 0x00001200 +#define FRF_AB_XM_ADR_LO_LBN 0 +#define FRF_AB_XM_ADR_LO_WIDTH 32 + +/* XM_ADR_HI_REG: XGMAC address register high */ +#define FR_AB_XM_ADR_HI 0x00001210 +#define FRF_AB_XM_ADR_HI_LBN 0 +#define FRF_AB_XM_ADR_HI_WIDTH 16 + +/* XM_GLB_CFG_REG: XGMAC global configuration */ +#define FR_AB_XM_GLB_CFG 0x00001220 +#define FRF_AB_XM_RMTFLT_GEN_LBN 17 +#define FRF_AB_XM_RMTFLT_GEN_WIDTH 1 +#define FRF_AB_XM_DEBUG_MODE_LBN 16 +#define FRF_AB_XM_DEBUG_MODE_WIDTH 1 +#define FRF_AB_XM_RX_STAT_EN_LBN 11 +#define FRF_AB_XM_RX_STAT_EN_WIDTH 1 +#define FRF_AB_XM_TX_STAT_EN_LBN 10 +#define FRF_AB_XM_TX_STAT_EN_WIDTH 1 +#define FRF_AB_XM_RX_JUMBO_MODE_LBN 6 +#define FRF_AB_XM_RX_JUMBO_MODE_WIDTH 1 +#define FRF_AB_XM_WAN_MODE_LBN 5 +#define FRF_AB_XM_WAN_MODE_WIDTH 1 +#define FRF_AB_XM_INTCLR_MODE_LBN 3 +#define FRF_AB_XM_INTCLR_MODE_WIDTH 1 +#define FRF_AB_XM_CORE_RST_LBN 0 +#define FRF_AB_XM_CORE_RST_WIDTH 1 + +/* XM_TX_CFG_REG: XGMAC transmit configuration */ +#define FR_AB_XM_TX_CFG 0x00001230 +#define FRF_AB_XM_TX_PROG_LBN 24 +#define FRF_AB_XM_TX_PROG_WIDTH 1 +#define FRF_AB_XM_IPG_LBN 16 +#define FRF_AB_XM_IPG_WIDTH 4 +#define FRF_AB_XM_FCNTL_LBN 10 +#define FRF_AB_XM_FCNTL_WIDTH 1 +#define FRF_AB_XM_TXCRC_LBN 8 +#define FRF_AB_XM_TXCRC_WIDTH 1 +#define FRF_AB_XM_EDRC_LBN 6 +#define FRF_AB_XM_EDRC_WIDTH 1 +#define FRF_AB_XM_AUTO_PAD_LBN 5 +#define FRF_AB_XM_AUTO_PAD_WIDTH 1 +#define FRF_AB_XM_TX_PRMBL_LBN 2 +#define FRF_AB_XM_TX_PRMBL_WIDTH 1 +#define FRF_AB_XM_TXEN_LBN 1 +#define FRF_AB_XM_TXEN_WIDTH 1 +#define FRF_AB_XM_TX_RST_LBN 0 +#define FRF_AB_XM_TX_RST_WIDTH 1 + +/* XM_RX_CFG_REG: XGMAC receive configuration */ +#define FR_AB_XM_RX_CFG 0x00001240 +#define FRF_AB_XM_PASS_LENERR_LBN 26 +#define FRF_AB_XM_PASS_LENERR_WIDTH 1 +#define FRF_AB_XM_PASS_CRC_ERR_LBN 25 +#define FRF_AB_XM_PASS_CRC_ERR_WIDTH 1 +#define FRF_AB_XM_PASS_PRMBLE_ERR_LBN 24 +#define FRF_AB_XM_PASS_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_REJ_BCAST_LBN 20 +#define FRF_AB_XM_REJ_BCAST_WIDTH 1 +#define FRF_AB_XM_ACPT_ALL_MCAST_LBN 11 +#define FRF_AB_XM_ACPT_ALL_MCAST_WIDTH 1 +#define FRF_AB_XM_ACPT_ALL_UCAST_LBN 9 +#define FRF_AB_XM_ACPT_ALL_UCAST_WIDTH 1 +#define FRF_AB_XM_AUTO_DEPAD_LBN 8 +#define FRF_AB_XM_AUTO_DEPAD_WIDTH 1 +#define FRF_AB_XM_RXCRC_LBN 3 +#define FRF_AB_XM_RXCRC_WIDTH 1 +#define FRF_AB_XM_RX_PRMBL_LBN 2 +#define FRF_AB_XM_RX_PRMBL_WIDTH 1 +#define FRF_AB_XM_RXEN_LBN 1 +#define FRF_AB_XM_RXEN_WIDTH 1 +#define FRF_AB_XM_RX_RST_LBN 0 +#define FRF_AB_XM_RX_RST_WIDTH 1 + +/* XM_MGT_INT_MASK: documentation to be written for sum_XM_MGT_INT_MASK */ +#define FR_AB_XM_MGT_INT_MASK 0x00001250 +#define FRF_AB_XM_MSK_STA_INTR_LBN 16 +#define FRF_AB_XM_MSK_STA_INTR_WIDTH 1 +#define FRF_AB_XM_MSK_STAT_CNTR_HF_LBN 9 +#define FRF_AB_XM_MSK_STAT_CNTR_HF_WIDTH 1 +#define FRF_AB_XM_MSK_STAT_CNTR_OF_LBN 8 +#define FRF_AB_XM_MSK_STAT_CNTR_OF_WIDTH 1 +#define FRF_AB_XM_MSK_PRMBLE_ERR_LBN 2 +#define FRF_AB_XM_MSK_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_MSK_RMTFLT_LBN 1 +#define FRF_AB_XM_MSK_RMTFLT_WIDTH 1 +#define FRF_AB_XM_MSK_LCLFLT_LBN 0 +#define FRF_AB_XM_MSK_LCLFLT_WIDTH 1 + +/* XM_FC_REG: XGMAC flow control register */ +#define FR_AB_XM_FC 0x00001270 +#define FRF_AB_XM_PAUSE_TIME_LBN 16 +#define FRF_AB_XM_PAUSE_TIME_WIDTH 16 +#define FRF_AB_XM_RX_MAC_STAT_LBN 11 +#define FRF_AB_XM_RX_MAC_STAT_WIDTH 1 +#define FRF_AB_XM_TX_MAC_STAT_LBN 10 +#define FRF_AB_XM_TX_MAC_STAT_WIDTH 1 +#define FRF_AB_XM_MCNTL_PASS_LBN 8 +#define FRF_AB_XM_MCNTL_PASS_WIDTH 2 +#define FRF_AB_XM_REJ_CNTL_UCAST_LBN 6 +#define FRF_AB_XM_REJ_CNTL_UCAST_WIDTH 1 +#define FRF_AB_XM_REJ_CNTL_MCAST_LBN 5 +#define FRF_AB_XM_REJ_CNTL_MCAST_WIDTH 1 +#define FRF_AB_XM_ZPAUSE_LBN 2 +#define FRF_AB_XM_ZPAUSE_WIDTH 1 +#define FRF_AB_XM_XMIT_PAUSE_LBN 1 +#define FRF_AB_XM_XMIT_PAUSE_WIDTH 1 +#define FRF_AB_XM_DIS_FCNTL_LBN 0 +#define FRF_AB_XM_DIS_FCNTL_WIDTH 1 + +/* XM_PAUSE_TIME_REG: XGMAC pause time register */ +#define FR_AB_XM_PAUSE_TIME 0x00001290 +#define FRF_AB_XM_TX_PAUSE_CNT_LBN 16 +#define FRF_AB_XM_TX_PAUSE_CNT_WIDTH 16 +#define FRF_AB_XM_RX_PAUSE_CNT_LBN 0 +#define FRF_AB_XM_RX_PAUSE_CNT_WIDTH 16 + +/* XM_TX_PARAM_REG: XGMAC transmit parameter register */ +#define FR_AB_XM_TX_PARAM 0x000012d0 +#define FRF_AB_XM_TX_JUMBO_MODE_LBN 31 +#define FRF_AB_XM_TX_JUMBO_MODE_WIDTH 1 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_LBN 19 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH 11 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN 16 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH 3 +#define FRF_AB_XM_PAD_CHAR_LBN 0 +#define FRF_AB_XM_PAD_CHAR_WIDTH 8 + +/* XM_RX_PARAM_REG: XGMAC receive parameter register */ +#define FR_AB_XM_RX_PARAM 0x000012e0 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_LBN 3 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH 11 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN 0 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH 3 + +/* XM_MGT_INT_MSK_REG: XGMAC management interrupt mask register */ +#define FR_AB_XM_MGT_INT_MSK 0x000012f0 +#define FRF_AB_XM_STAT_CNTR_OF_LBN 9 +#define FRF_AB_XM_STAT_CNTR_OF_WIDTH 1 +#define FRF_AB_XM_STAT_CNTR_HF_LBN 8 +#define FRF_AB_XM_STAT_CNTR_HF_WIDTH 1 +#define FRF_AB_XM_PRMBLE_ERR_LBN 2 +#define FRF_AB_XM_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_RMTFLT_LBN 1 +#define FRF_AB_XM_RMTFLT_WIDTH 1 +#define FRF_AB_XM_LCLFLT_LBN 0 +#define FRF_AB_XM_LCLFLT_WIDTH 1 + +/* XX_PWR_RST_REG: XGXS/XAUI powerdown/reset register */ +#define FR_AB_XX_PWR_RST 0x00001300 +#define FRF_AB_XX_PWRDND_SIG_LBN 31 +#define FRF_AB_XX_PWRDND_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNC_SIG_LBN 30 +#define FRF_AB_XX_PWRDNC_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNB_SIG_LBN 29 +#define FRF_AB_XX_PWRDNB_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNA_SIG_LBN 28 +#define FRF_AB_XX_PWRDNA_SIG_WIDTH 1 +#define FRF_AB_XX_SIM_MODE_LBN 27 +#define FRF_AB_XX_SIM_MODE_WIDTH 1 +#define FRF_AB_XX_RSTPLLCD_SIG_LBN 25 +#define FRF_AB_XX_RSTPLLCD_SIG_WIDTH 1 +#define FRF_AB_XX_RSTPLLAB_SIG_LBN 24 +#define FRF_AB_XX_RSTPLLAB_SIG_WIDTH 1 +#define FRF_AB_XX_RESETD_SIG_LBN 23 +#define FRF_AB_XX_RESETD_SIG_WIDTH 1 +#define FRF_AB_XX_RESETC_SIG_LBN 22 +#define FRF_AB_XX_RESETC_SIG_WIDTH 1 +#define FRF_AB_XX_RESETB_SIG_LBN 21 +#define FRF_AB_XX_RESETB_SIG_WIDTH 1 +#define FRF_AB_XX_RESETA_SIG_LBN 20 +#define FRF_AB_XX_RESETA_SIG_WIDTH 1 +#define FRF_AB_XX_RSTXGXSRX_SIG_LBN 18 +#define FRF_AB_XX_RSTXGXSRX_SIG_WIDTH 1 +#define FRF_AB_XX_RSTXGXSTX_SIG_LBN 17 +#define FRF_AB_XX_RSTXGXSTX_SIG_WIDTH 1 +#define FRF_AB_XX_SD_RST_ACT_LBN 16 +#define FRF_AB_XX_SD_RST_ACT_WIDTH 1 +#define FRF_AB_XX_PWRDND_EN_LBN 15 +#define FRF_AB_XX_PWRDND_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNC_EN_LBN 14 +#define FRF_AB_XX_PWRDNC_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNB_EN_LBN 13 +#define FRF_AB_XX_PWRDNB_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNA_EN_LBN 12 +#define FRF_AB_XX_PWRDNA_EN_WIDTH 1 +#define FRF_AB_XX_RSTPLLCD_EN_LBN 9 +#define FRF_AB_XX_RSTPLLCD_EN_WIDTH 1 +#define FRF_AB_XX_RSTPLLAB_EN_LBN 8 +#define FRF_AB_XX_RSTPLLAB_EN_WIDTH 1 +#define FRF_AB_XX_RESETD_EN_LBN 7 +#define FRF_AB_XX_RESETD_EN_WIDTH 1 +#define FRF_AB_XX_RESETC_EN_LBN 6 +#define FRF_AB_XX_RESETC_EN_WIDTH 1 +#define FRF_AB_XX_RESETB_EN_LBN 5 +#define FRF_AB_XX_RESETB_EN_WIDTH 1 +#define FRF_AB_XX_RESETA_EN_LBN 4 +#define FRF_AB_XX_RESETA_EN_WIDTH 1 +#define FRF_AB_XX_RSTXGXSRX_EN_LBN 2 +#define FRF_AB_XX_RSTXGXSRX_EN_WIDTH 1 +#define FRF_AB_XX_RSTXGXSTX_EN_LBN 1 +#define FRF_AB_XX_RSTXGXSTX_EN_WIDTH 1 +#define FRF_AB_XX_RST_XX_EN_LBN 0 +#define FRF_AB_XX_RST_XX_EN_WIDTH 1 + +/* XX_SD_CTL_REG: XGXS/XAUI powerdown/reset control register */ +#define FR_AB_XX_SD_CTL 0x00001310 +#define FRF_AB_XX_TERMADJ1_LBN 17 +#define FRF_AB_XX_TERMADJ1_WIDTH 1 +#define FRF_AB_XX_TERMADJ0_LBN 16 +#define FRF_AB_XX_TERMADJ0_WIDTH 1 +#define FRF_AB_XX_HIDRVD_LBN 15 +#define FRF_AB_XX_HIDRVD_WIDTH 1 +#define FRF_AB_XX_LODRVD_LBN 14 +#define FRF_AB_XX_LODRVD_WIDTH 1 +#define FRF_AB_XX_HIDRVC_LBN 13 +#define FRF_AB_XX_HIDRVC_WIDTH 1 +#define FRF_AB_XX_LODRVC_LBN 12 +#define FRF_AB_XX_LODRVC_WIDTH 1 +#define FRF_AB_XX_HIDRVB_LBN 11 +#define FRF_AB_XX_HIDRVB_WIDTH 1 +#define FRF_AB_XX_LODRVB_LBN 10 +#define FRF_AB_XX_LODRVB_WIDTH 1 +#define FRF_AB_XX_HIDRVA_LBN 9 +#define FRF_AB_XX_HIDRVA_WIDTH 1 +#define FRF_AB_XX_LODRVA_LBN 8 +#define FRF_AB_XX_LODRVA_WIDTH 1 +#define FRF_AB_XX_LPBKD_LBN 3 +#define FRF_AB_XX_LPBKD_WIDTH 1 +#define FRF_AB_XX_LPBKC_LBN 2 +#define FRF_AB_XX_LPBKC_WIDTH 1 +#define FRF_AB_XX_LPBKB_LBN 1 +#define FRF_AB_XX_LPBKB_WIDTH 1 +#define FRF_AB_XX_LPBKA_LBN 0 +#define FRF_AB_XX_LPBKA_WIDTH 1 + +/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */ +#define FR_AB_XX_TXDRV_CTL 0x00001320 +#define FRF_AB_XX_DEQD_LBN 28 +#define FRF_AB_XX_DEQD_WIDTH 4 +#define FRF_AB_XX_DEQC_LBN 24 +#define FRF_AB_XX_DEQC_WIDTH 4 +#define FRF_AB_XX_DEQB_LBN 20 +#define FRF_AB_XX_DEQB_WIDTH 4 +#define FRF_AB_XX_DEQA_LBN 16 +#define FRF_AB_XX_DEQA_WIDTH 4 +#define FRF_AB_XX_DTXD_LBN 12 +#define FRF_AB_XX_DTXD_WIDTH 4 +#define FRF_AB_XX_DTXC_LBN 8 +#define FRF_AB_XX_DTXC_WIDTH 4 +#define FRF_AB_XX_DTXB_LBN 4 +#define FRF_AB_XX_DTXB_WIDTH 4 +#define FRF_AB_XX_DTXA_LBN 0 +#define FRF_AB_XX_DTXA_WIDTH 4 + +/* XX_PRBS_CTL_REG: documentation to be written for sum_XX_PRBS_CTL_REG */ +#define FR_AB_XX_PRBS_CTL 0x00001330 +#define FRF_AB_XX_CH3_RX_PRBS_SEL_LBN 30 +#define FRF_AB_XX_CH3_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH3_RX_PRBS_INV_LBN 29 +#define FRF_AB_XX_CH3_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_LBN 28 +#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH2_RX_PRBS_SEL_LBN 26 +#define FRF_AB_XX_CH2_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH2_RX_PRBS_INV_LBN 25 +#define FRF_AB_XX_CH2_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_LBN 24 +#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH1_RX_PRBS_SEL_LBN 22 +#define FRF_AB_XX_CH1_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH1_RX_PRBS_INV_LBN 21 +#define FRF_AB_XX_CH1_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_LBN 20 +#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH0_RX_PRBS_SEL_LBN 18 +#define FRF_AB_XX_CH0_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH0_RX_PRBS_INV_LBN 17 +#define FRF_AB_XX_CH0_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_LBN 16 +#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH3_TX_PRBS_SEL_LBN 14 +#define FRF_AB_XX_CH3_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH3_TX_PRBS_INV_LBN 13 +#define FRF_AB_XX_CH3_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_LBN 12 +#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH2_TX_PRBS_SEL_LBN 10 +#define FRF_AB_XX_CH2_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH2_TX_PRBS_INV_LBN 9 +#define FRF_AB_XX_CH2_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_LBN 8 +#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH1_TX_PRBS_SEL_LBN 6 +#define FRF_AB_XX_CH1_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH1_TX_PRBS_INV_LBN 5 +#define FRF_AB_XX_CH1_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_LBN 4 +#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH0_TX_PRBS_SEL_LBN 2 +#define FRF_AB_XX_CH0_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH0_TX_PRBS_INV_LBN 1 +#define FRF_AB_XX_CH0_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_LBN 0 +#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_WIDTH 1 + +/* XX_PRBS_CHK_REG: documentation to be written for sum_XX_PRBS_CHK_REG */ +#define FR_AB_XX_PRBS_CHK 0x00001340 +#define FRF_AB_XX_REV_LB_EN_LBN 16 +#define FRF_AB_XX_REV_LB_EN_WIDTH 1 +#define FRF_AB_XX_CH3_DEG_DET_LBN 15 +#define FRF_AB_XX_CH3_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH3_LFSR_LOCK_IND_LBN 14 +#define FRF_AB_XX_CH3_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH3_PRBS_FRUN_LBN 13 +#define FRF_AB_XX_CH3_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH3_ERR_CHK_LBN 12 +#define FRF_AB_XX_CH3_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH2_DEG_DET_LBN 11 +#define FRF_AB_XX_CH2_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH2_LFSR_LOCK_IND_LBN 10 +#define FRF_AB_XX_CH2_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH2_PRBS_FRUN_LBN 9 +#define FRF_AB_XX_CH2_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH2_ERR_CHK_LBN 8 +#define FRF_AB_XX_CH2_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH1_DEG_DET_LBN 7 +#define FRF_AB_XX_CH1_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH1_LFSR_LOCK_IND_LBN 6 +#define FRF_AB_XX_CH1_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH1_PRBS_FRUN_LBN 5 +#define FRF_AB_XX_CH1_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH1_ERR_CHK_LBN 4 +#define FRF_AB_XX_CH1_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH0_DEG_DET_LBN 3 +#define FRF_AB_XX_CH0_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH0_LFSR_LOCK_IND_LBN 2 +#define FRF_AB_XX_CH0_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH0_PRBS_FRUN_LBN 1 +#define FRF_AB_XX_CH0_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH0_ERR_CHK_LBN 0 +#define FRF_AB_XX_CH0_ERR_CHK_WIDTH 1 + +/* XX_PRBS_ERR_REG: documentation to be written for sum_XX_PRBS_ERR_REG */ +#define FR_AB_XX_PRBS_ERR 0x00001350 +#define FRF_AB_XX_CH3_PRBS_ERR_CNT_LBN 24 +#define FRF_AB_XX_CH3_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH2_PRBS_ERR_CNT_LBN 16 +#define FRF_AB_XX_CH2_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH1_PRBS_ERR_CNT_LBN 8 +#define FRF_AB_XX_CH1_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH0_PRBS_ERR_CNT_LBN 0 +#define FRF_AB_XX_CH0_PRBS_ERR_CNT_WIDTH 8 + +/* XX_CORE_STAT_REG: XAUI XGXS core status register */ +#define FR_AB_XX_CORE_STAT 0x00001360 +#define FRF_AB_XX_FORCE_SIG3_LBN 31 +#define FRF_AB_XX_FORCE_SIG3_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG3_VAL_LBN 30 +#define FRF_AB_XX_FORCE_SIG3_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG2_LBN 29 +#define FRF_AB_XX_FORCE_SIG2_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG2_VAL_LBN 28 +#define FRF_AB_XX_FORCE_SIG2_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG1_LBN 27 +#define FRF_AB_XX_FORCE_SIG1_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG1_VAL_LBN 26 +#define FRF_AB_XX_FORCE_SIG1_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG0_LBN 25 +#define FRF_AB_XX_FORCE_SIG0_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG0_VAL_LBN 24 +#define FRF_AB_XX_FORCE_SIG0_VAL_WIDTH 1 +#define FRF_AB_XX_XGXS_LB_EN_LBN 23 +#define FRF_AB_XX_XGXS_LB_EN_WIDTH 1 +#define FRF_AB_XX_XGMII_LB_EN_LBN 22 +#define FRF_AB_XX_XGMII_LB_EN_WIDTH 1 +#define FRF_AB_XX_MATCH_FAULT_LBN 21 +#define FRF_AB_XX_MATCH_FAULT_WIDTH 1 +#define FRF_AB_XX_ALIGN_DONE_LBN 20 +#define FRF_AB_XX_ALIGN_DONE_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT3_LBN 19 +#define FRF_AB_XX_SYNC_STAT3_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT2_LBN 18 +#define FRF_AB_XX_SYNC_STAT2_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT1_LBN 17 +#define FRF_AB_XX_SYNC_STAT1_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT0_LBN 16 +#define FRF_AB_XX_SYNC_STAT0_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH3_LBN 15 +#define FRF_AB_XX_COMMA_DET_CH3_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH2_LBN 14 +#define FRF_AB_XX_COMMA_DET_CH2_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH1_LBN 13 +#define FRF_AB_XX_COMMA_DET_CH1_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH0_LBN 12 +#define FRF_AB_XX_COMMA_DET_CH0_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH3_LBN 11 +#define FRF_AB_XX_CGRP_ALIGN_CH3_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH2_LBN 10 +#define FRF_AB_XX_CGRP_ALIGN_CH2_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH1_LBN 9 +#define FRF_AB_XX_CGRP_ALIGN_CH1_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH0_LBN 8 +#define FRF_AB_XX_CGRP_ALIGN_CH0_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH3_LBN 7 +#define FRF_AB_XX_CHAR_ERR_CH3_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH2_LBN 6 +#define FRF_AB_XX_CHAR_ERR_CH2_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH1_LBN 5 +#define FRF_AB_XX_CHAR_ERR_CH1_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH0_LBN 4 +#define FRF_AB_XX_CHAR_ERR_CH0_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH3_LBN 3 +#define FRF_AB_XX_DISPERR_CH3_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH2_LBN 2 +#define FRF_AB_XX_DISPERR_CH2_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH1_LBN 1 +#define FRF_AB_XX_DISPERR_CH1_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH0_LBN 0 +#define FRF_AB_XX_DISPERR_CH0_WIDTH 1 + +/* RX_DESC_PTR_TBL_KER: Receive descriptor pointer table */ +#define FR_AA_RX_DESC_PTR_TBL_KER 0x00011800 +#define FR_AA_RX_DESC_PTR_TBL_KER_STEP 16 +#define FR_AA_RX_DESC_PTR_TBL_KER_ROWS 4 +/* RX_DESC_PTR_TBL: Receive descriptor pointer table */ +#define FR_BZ_RX_DESC_PTR_TBL 0x00f40000 +#define FR_BZ_RX_DESC_PTR_TBL_STEP 16 +#define FR_BB_RX_DESC_PTR_TBL_ROWS 4096 +#define FR_CZ_RX_DESC_PTR_TBL_ROWS 1024 +#define FRF_CZ_RX_HDR_SPLIT_LBN 90 +#define FRF_CZ_RX_HDR_SPLIT_WIDTH 1 +#define FRF_AA_RX_RESET_LBN 89 +#define FRF_AA_RX_RESET_WIDTH 1 +#define FRF_AZ_RX_ISCSI_DDIG_EN_LBN 88 +#define FRF_AZ_RX_ISCSI_DDIG_EN_WIDTH 1 +#define FRF_AZ_RX_ISCSI_HDIG_EN_LBN 87 +#define FRF_AZ_RX_ISCSI_HDIG_EN_WIDTH 1 +#define FRF_AZ_RX_DESC_PREF_ACT_LBN 86 +#define FRF_AZ_RX_DESC_PREF_ACT_WIDTH 1 +#define FRF_AZ_RX_DC_HW_RPTR_LBN 80 +#define FRF_AZ_RX_DC_HW_RPTR_WIDTH 6 +#define FRF_AZ_RX_DESCQ_HW_RPTR_LBN 68 +#define FRF_AZ_RX_DESCQ_HW_RPTR_WIDTH 12 +#define FRF_AZ_RX_DESCQ_SW_WPTR_LBN 56 +#define FRF_AZ_RX_DESCQ_SW_WPTR_WIDTH 12 +#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_LBN 36 +#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define FRF_AZ_RX_DESCQ_EVQ_ID_LBN 24 +#define FRF_AZ_RX_DESCQ_EVQ_ID_WIDTH 12 +#define FRF_AZ_RX_DESCQ_OWNER_ID_LBN 10 +#define FRF_AZ_RX_DESCQ_OWNER_ID_WIDTH 14 +#define FRF_AZ_RX_DESCQ_LABEL_LBN 5 +#define FRF_AZ_RX_DESCQ_LABEL_WIDTH 5 +#define FRF_AZ_RX_DESCQ_SIZE_LBN 3 +#define FRF_AZ_RX_DESCQ_SIZE_WIDTH 2 +#define FFE_AZ_RX_DESCQ_SIZE_4K 3 +#define FFE_AZ_RX_DESCQ_SIZE_2K 2 +#define FFE_AZ_RX_DESCQ_SIZE_1K 1 +#define FFE_AZ_RX_DESCQ_SIZE_512 0 +#define FRF_AZ_RX_DESCQ_TYPE_LBN 2 +#define FRF_AZ_RX_DESCQ_TYPE_WIDTH 1 +#define FRF_AZ_RX_DESCQ_JUMBO_LBN 1 +#define FRF_AZ_RX_DESCQ_JUMBO_WIDTH 1 +#define FRF_AZ_RX_DESCQ_EN_LBN 0 +#define FRF_AZ_RX_DESCQ_EN_WIDTH 1 + +/* TX_DESC_PTR_TBL_KER: Transmit descriptor pointer */ +#define FR_AA_TX_DESC_PTR_TBL_KER 0x00011900 +#define FR_AA_TX_DESC_PTR_TBL_KER_STEP 16 +#define FR_AA_TX_DESC_PTR_TBL_KER_ROWS 8 +/* TX_DESC_PTR_TBL: Transmit descriptor pointer */ +#define FR_BZ_TX_DESC_PTR_TBL 0x00f50000 +#define FR_BZ_TX_DESC_PTR_TBL_STEP 16 +#define FR_BB_TX_DESC_PTR_TBL_ROWS 4096 +#define FR_CZ_TX_DESC_PTR_TBL_ROWS 1024 +#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_LBN 94 +#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_WIDTH 2 +#define FRF_CZ_TX_DPT_ETH_FILT_EN_LBN 93 +#define FRF_CZ_TX_DPT_ETH_FILT_EN_WIDTH 1 +#define FRF_CZ_TX_DPT_IP_FILT_EN_LBN 92 +#define FRF_CZ_TX_DPT_IP_FILT_EN_WIDTH 1 +#define FRF_BZ_TX_NON_IP_DROP_DIS_LBN 91 +#define FRF_BZ_TX_NON_IP_DROP_DIS_WIDTH 1 +#define FRF_BZ_TX_IP_CHKSM_DIS_LBN 90 +#define FRF_BZ_TX_IP_CHKSM_DIS_WIDTH 1 +#define FRF_BZ_TX_TCP_CHKSM_DIS_LBN 89 +#define FRF_BZ_TX_TCP_CHKSM_DIS_WIDTH 1 +#define FRF_AZ_TX_DESCQ_EN_LBN 88 +#define FRF_AZ_TX_DESCQ_EN_WIDTH 1 +#define FRF_AZ_TX_ISCSI_DDIG_EN_LBN 87 +#define FRF_AZ_TX_ISCSI_DDIG_EN_WIDTH 1 +#define FRF_AZ_TX_ISCSI_HDIG_EN_LBN 86 +#define FRF_AZ_TX_ISCSI_HDIG_EN_WIDTH 1 +#define FRF_AZ_TX_DC_HW_RPTR_LBN 80 +#define FRF_AZ_TX_DC_HW_RPTR_WIDTH 6 +#define FRF_AZ_TX_DESCQ_HW_RPTR_LBN 68 +#define FRF_AZ_TX_DESCQ_HW_RPTR_WIDTH 12 +#define FRF_AZ_TX_DESCQ_SW_WPTR_LBN 56 +#define FRF_AZ_TX_DESCQ_SW_WPTR_WIDTH 12 +#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_LBN 36 +#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define FRF_AZ_TX_DESCQ_EVQ_ID_LBN 24 +#define FRF_AZ_TX_DESCQ_EVQ_ID_WIDTH 12 +#define FRF_AZ_TX_DESCQ_OWNER_ID_LBN 10 +#define FRF_AZ_TX_DESCQ_OWNER_ID_WIDTH 14 +#define FRF_AZ_TX_DESCQ_LABEL_LBN 5 +#define FRF_AZ_TX_DESCQ_LABEL_WIDTH 5 +#define FRF_AZ_TX_DESCQ_SIZE_LBN 3 +#define FRF_AZ_TX_DESCQ_SIZE_WIDTH 2 +#define FFE_AZ_TX_DESCQ_SIZE_4K 3 +#define FFE_AZ_TX_DESCQ_SIZE_2K 2 +#define FFE_AZ_TX_DESCQ_SIZE_1K 1 +#define FFE_AZ_TX_DESCQ_SIZE_512 0 +#define FRF_AZ_TX_DESCQ_TYPE_LBN 1 +#define FRF_AZ_TX_DESCQ_TYPE_WIDTH 2 +#define FRF_AZ_TX_DESCQ_FLUSH_LBN 0 +#define FRF_AZ_TX_DESCQ_FLUSH_WIDTH 1 + +/* EVQ_PTR_TBL_KER: Event queue pointer table */ +#define FR_AA_EVQ_PTR_TBL_KER 0x00011a00 +#define FR_AA_EVQ_PTR_TBL_KER_STEP 16 +#define FR_AA_EVQ_PTR_TBL_KER_ROWS 4 +/* EVQ_PTR_TBL: Event queue pointer table */ +#define FR_BZ_EVQ_PTR_TBL 0x00f60000 +#define FR_BZ_EVQ_PTR_TBL_STEP 16 +#define FR_CZ_EVQ_PTR_TBL_ROWS 1024 +#define FR_BB_EVQ_PTR_TBL_ROWS 4096 +#define FRF_BZ_EVQ_RPTR_IGN_LBN 40 +#define FRF_BZ_EVQ_RPTR_IGN_WIDTH 1 +#define FRF_AB_EVQ_WKUP_OR_INT_EN_LBN 39 +#define FRF_AB_EVQ_WKUP_OR_INT_EN_WIDTH 1 +#define FRF_CZ_EVQ_DOS_PROTECT_EN_LBN 39 +#define FRF_CZ_EVQ_DOS_PROTECT_EN_WIDTH 1 +#define FRF_AZ_EVQ_NXT_WPTR_LBN 24 +#define FRF_AZ_EVQ_NXT_WPTR_WIDTH 15 +#define FRF_AZ_EVQ_EN_LBN 23 +#define FRF_AZ_EVQ_EN_WIDTH 1 +#define FRF_AZ_EVQ_SIZE_LBN 20 +#define FRF_AZ_EVQ_SIZE_WIDTH 3 +#define FFE_AZ_EVQ_SIZE_32K 6 +#define FFE_AZ_EVQ_SIZE_16K 5 +#define FFE_AZ_EVQ_SIZE_8K 4 +#define FFE_AZ_EVQ_SIZE_4K 3 +#define FFE_AZ_EVQ_SIZE_2K 2 +#define FFE_AZ_EVQ_SIZE_1K 1 +#define FFE_AZ_EVQ_SIZE_512 0 +#define FRF_AZ_EVQ_BUF_BASE_ID_LBN 0 +#define FRF_AZ_EVQ_BUF_BASE_ID_WIDTH 20 + +/* BUF_HALF_TBL_KER: Buffer table in half buffer table mode direct access by driver */ +#define FR_AA_BUF_HALF_TBL_KER 0x00018000 +#define FR_AA_BUF_HALF_TBL_KER_STEP 8 +#define FR_AA_BUF_HALF_TBL_KER_ROWS 4096 +/* BUF_HALF_TBL: Buffer table in half buffer table mode direct access by driver */ +#define FR_BZ_BUF_HALF_TBL 0x00800000 +#define FR_BZ_BUF_HALF_TBL_STEP 8 +#define FR_CZ_BUF_HALF_TBL_ROWS 147456 +#define FR_BB_BUF_HALF_TBL_ROWS 524288 +#define FRF_AZ_BUF_ADR_HBUF_ODD_LBN 44 +#define FRF_AZ_BUF_ADR_HBUF_ODD_WIDTH 20 +#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_LBN 32 +#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_WIDTH 12 +#define FRF_AZ_BUF_ADR_HBUF_EVEN_LBN 12 +#define FRF_AZ_BUF_ADR_HBUF_EVEN_WIDTH 20 +#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_LBN 0 +#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_WIDTH 12 + +/* BUF_FULL_TBL_KER: Buffer table in full buffer table mode direct access by driver */ +#define FR_AA_BUF_FULL_TBL_KER 0x00018000 +#define FR_AA_BUF_FULL_TBL_KER_STEP 8 +#define FR_AA_BUF_FULL_TBL_KER_ROWS 4096 +/* BUF_FULL_TBL: Buffer table in full buffer table mode direct access by driver */ +#define FR_BZ_BUF_FULL_TBL 0x00800000 +#define FR_BZ_BUF_FULL_TBL_STEP 8 +#define FR_CZ_BUF_FULL_TBL_ROWS 147456 +#define FR_BB_BUF_FULL_TBL_ROWS 917504 +#define FRF_AZ_BUF_FULL_UNUSED_LBN 51 +#define FRF_AZ_BUF_FULL_UNUSED_WIDTH 13 +#define FRF_AZ_IP_DAT_BUF_SIZE_LBN 50 +#define FRF_AZ_IP_DAT_BUF_SIZE_WIDTH 1 +#define FRF_AZ_BUF_ADR_REGION_LBN 48 +#define FRF_AZ_BUF_ADR_REGION_WIDTH 2 +#define FFE_AZ_BUF_ADR_REGN3 3 +#define FFE_AZ_BUF_ADR_REGN2 2 +#define FFE_AZ_BUF_ADR_REGN1 1 +#define FFE_AZ_BUF_ADR_REGN0 0 +#define FRF_AZ_BUF_ADR_FBUF_LBN 14 +#define FRF_AZ_BUF_ADR_FBUF_WIDTH 34 +#define FRF_AZ_BUF_OWNER_ID_FBUF_LBN 0 +#define FRF_AZ_BUF_OWNER_ID_FBUF_WIDTH 14 + +/* RX_FILTER_TBL0: TCP/IPv4 Receive filter table */ +#define FR_BZ_RX_FILTER_TBL0 0x00f00000 +#define FR_BZ_RX_FILTER_TBL0_STEP 32 +#define FR_BZ_RX_FILTER_TBL0_ROWS 8192 +/* RX_FILTER_TBL1: TCP/IPv4 Receive filter table */ +#define FR_BB_RX_FILTER_TBL1 0x00f00010 +#define FR_BB_RX_FILTER_TBL1_STEP 32 +#define FR_BB_RX_FILTER_TBL1_ROWS 8192 +#define FRF_BZ_RSS_EN_LBN 110 +#define FRF_BZ_RSS_EN_WIDTH 1 +#define FRF_BZ_SCATTER_EN_LBN 109 +#define FRF_BZ_SCATTER_EN_WIDTH 1 +#define FRF_BZ_TCP_UDP_LBN 108 +#define FRF_BZ_TCP_UDP_WIDTH 1 +#define FRF_BZ_RXQ_ID_LBN 96 +#define FRF_BZ_RXQ_ID_WIDTH 12 +#define FRF_BZ_DEST_IP_LBN 64 +#define FRF_BZ_DEST_IP_WIDTH 32 +#define FRF_BZ_DEST_PORT_TCP_LBN 48 +#define FRF_BZ_DEST_PORT_TCP_WIDTH 16 +#define FRF_BZ_SRC_IP_LBN 16 +#define FRF_BZ_SRC_IP_WIDTH 32 +#define FRF_BZ_SRC_TCP_DEST_UDP_LBN 0 +#define FRF_BZ_SRC_TCP_DEST_UDP_WIDTH 16 + +/* RX_MAC_FILTER_TBL0: Receive Ethernet filter table */ +#define FR_CZ_RX_MAC_FILTER_TBL0 0x00f00010 +#define FR_CZ_RX_MAC_FILTER_TBL0_STEP 32 +#define FR_CZ_RX_MAC_FILTER_TBL0_ROWS 512 +#define FRF_CZ_RMFT_RSS_EN_LBN 75 +#define FRF_CZ_RMFT_RSS_EN_WIDTH 1 +#define FRF_CZ_RMFT_SCATTER_EN_LBN 74 +#define FRF_CZ_RMFT_SCATTER_EN_WIDTH 1 +#define FRF_CZ_RMFT_IP_OVERRIDE_LBN 73 +#define FRF_CZ_RMFT_IP_OVERRIDE_WIDTH 1 +#define FRF_CZ_RMFT_RXQ_ID_LBN 61 +#define FRF_CZ_RMFT_RXQ_ID_WIDTH 12 +#define FRF_CZ_RMFT_WILDCARD_MATCH_LBN 60 +#define FRF_CZ_RMFT_WILDCARD_MATCH_WIDTH 1 +#define FRF_CZ_RMFT_DEST_MAC_LBN 16 +#define FRF_CZ_RMFT_DEST_MAC_WIDTH 44 +#define FRF_CZ_RMFT_VLAN_ID_LBN 0 +#define FRF_CZ_RMFT_VLAN_ID_WIDTH 12 + +/* TIMER_TBL: Timer table */ +#define FR_BZ_TIMER_TBL 0x00f70000 +#define FR_BZ_TIMER_TBL_STEP 16 +#define FR_CZ_TIMER_TBL_ROWS 1024 +#define FR_BB_TIMER_TBL_ROWS 4096 +#define FRF_CZ_TIMER_Q_EN_LBN 33 +#define FRF_CZ_TIMER_Q_EN_WIDTH 1 +#define FRF_CZ_INT_ARMD_LBN 32 +#define FRF_CZ_INT_ARMD_WIDTH 1 +#define FRF_CZ_INT_PEND_LBN 31 +#define FRF_CZ_INT_PEND_WIDTH 1 +#define FRF_CZ_HOST_NOTIFY_MODE_LBN 30 +#define FRF_CZ_HOST_NOTIFY_MODE_WIDTH 1 +#define FRF_CZ_RELOAD_TIMER_VAL_LBN 16 +#define FRF_CZ_RELOAD_TIMER_VAL_WIDTH 14 +#define FRF_CZ_TIMER_MODE_LBN 14 +#define FRF_CZ_TIMER_MODE_WIDTH 2 +#define FFE_CZ_TIMER_MODE_INT_HLDOFF 3 +#define FFE_CZ_TIMER_MODE_TRIG_START 2 +#define FFE_CZ_TIMER_MODE_IMMED_START 1 +#define FFE_CZ_TIMER_MODE_DIS 0 +#define FRF_BB_TIMER_MODE_LBN 12 +#define FRF_BB_TIMER_MODE_WIDTH 2 +#define FFE_BB_TIMER_MODE_INT_HLDOFF 2 +#define FFE_BB_TIMER_MODE_TRIG_START 2 +#define FFE_BB_TIMER_MODE_IMMED_START 1 +#define FFE_BB_TIMER_MODE_DIS 0 +#define FRF_CZ_TIMER_VAL_LBN 0 +#define FRF_CZ_TIMER_VAL_WIDTH 14 +#define FRF_BB_TIMER_VAL_LBN 0 +#define FRF_BB_TIMER_VAL_WIDTH 12 + +/* TX_PACE_TBL: Transmit pacing table */ +#define FR_BZ_TX_PACE_TBL 0x00f80000 +#define FR_BZ_TX_PACE_TBL_STEP 16 +#define FR_CZ_TX_PACE_TBL_ROWS 1024 +#define FR_BB_TX_PACE_TBL_ROWS 4096 +#define FRF_BZ_TX_PACE_LBN 0 +#define FRF_BZ_TX_PACE_WIDTH 5 + +/* RX_INDIRECTION_TBL: RX Indirection Table */ +#define FR_BZ_RX_INDIRECTION_TBL 0x00fb0000 +#define FR_BZ_RX_INDIRECTION_TBL_STEP 16 +#define FR_BZ_RX_INDIRECTION_TBL_ROWS 128 +#define FRF_BZ_IT_QUEUE_LBN 0 +#define FRF_BZ_IT_QUEUE_WIDTH 6 + +/* TX_FILTER_TBL0: TCP/IPv4 Transmit filter table */ +#define FR_CZ_TX_FILTER_TBL0 0x00fc0000 +#define FR_CZ_TX_FILTER_TBL0_STEP 16 +#define FR_CZ_TX_FILTER_TBL0_ROWS 8192 +#define FRF_CZ_TIFT_TCP_UDP_LBN 108 +#define FRF_CZ_TIFT_TCP_UDP_WIDTH 1 +#define FRF_CZ_TIFT_TXQ_ID_LBN 96 +#define FRF_CZ_TIFT_TXQ_ID_WIDTH 12 +#define FRF_CZ_TIFT_DEST_IP_LBN 64 +#define FRF_CZ_TIFT_DEST_IP_WIDTH 32 +#define FRF_CZ_TIFT_DEST_PORT_TCP_LBN 48 +#define FRF_CZ_TIFT_DEST_PORT_TCP_WIDTH 16 +#define FRF_CZ_TIFT_SRC_IP_LBN 16 +#define FRF_CZ_TIFT_SRC_IP_WIDTH 32 +#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_LBN 0 +#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_WIDTH 16 + +/* TX_MAC_FILTER_TBL0: Transmit Ethernet filter table */ +#define FR_CZ_TX_MAC_FILTER_TBL0 0x00fe0000 +#define FR_CZ_TX_MAC_FILTER_TBL0_STEP 16 +#define FR_CZ_TX_MAC_FILTER_TBL0_ROWS 512 +#define FRF_CZ_TMFT_TXQ_ID_LBN 61 +#define FRF_CZ_TMFT_TXQ_ID_WIDTH 12 +#define FRF_CZ_TMFT_WILDCARD_MATCH_LBN 60 +#define FRF_CZ_TMFT_WILDCARD_MATCH_WIDTH 1 +#define FRF_CZ_TMFT_SRC_MAC_LBN 16 +#define FRF_CZ_TMFT_SRC_MAC_WIDTH 44 +#define FRF_CZ_TMFT_VLAN_ID_LBN 0 +#define FRF_CZ_TMFT_VLAN_ID_WIDTH 12 + +/* MC_TREG_SMEM: MC Shared Memory */ +#define FR_CZ_MC_TREG_SMEM 0x00ff0000 +#define FR_CZ_MC_TREG_SMEM_STEP 4 +#define FR_CZ_MC_TREG_SMEM_ROWS 512 +#define FRF_CZ_MC_TREG_SMEM_ROW_LBN 0 +#define FRF_CZ_MC_TREG_SMEM_ROW_WIDTH 32 + +/* MSIX_VECTOR_TABLE: MSIX Vector Table */ +#define FR_BB_MSIX_VECTOR_TABLE 0x00ff0000 +#define FR_BZ_MSIX_VECTOR_TABLE_STEP 16 +#define FR_BB_MSIX_VECTOR_TABLE_ROWS 64 +/* MSIX_VECTOR_TABLE: MSIX Vector Table */ +#define FR_CZ_MSIX_VECTOR_TABLE 0x00000000 +/* FR_BZ_MSIX_VECTOR_TABLE_STEP 16 */ +#define FR_CZ_MSIX_VECTOR_TABLE_ROWS 1024 +#define FRF_BZ_MSIX_VECTOR_RESERVED_LBN 97 +#define FRF_BZ_MSIX_VECTOR_RESERVED_WIDTH 31 +#define FRF_BZ_MSIX_VECTOR_MASK_LBN 96 +#define FRF_BZ_MSIX_VECTOR_MASK_WIDTH 1 +#define FRF_BZ_MSIX_MESSAGE_DATA_LBN 64 +#define FRF_BZ_MSIX_MESSAGE_DATA_WIDTH 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_HI_LBN 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_HI_WIDTH 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_LO_LBN 0 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_LO_WIDTH 32 + +/* MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_BB_MSIX_PBA_TABLE 0x00ff2000 +#define FR_BZ_MSIX_PBA_TABLE_STEP 4 +#define FR_BB_MSIX_PBA_TABLE_ROWS 2 +/* MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_CZ_MSIX_PBA_TABLE 0x00008000 +/* FR_BZ_MSIX_PBA_TABLE_STEP 4 */ +#define FR_CZ_MSIX_PBA_TABLE_ROWS 32 +#define FRF_BZ_MSIX_PBA_PEND_DWORD_LBN 0 +#define FRF_BZ_MSIX_PBA_PEND_DWORD_WIDTH 32 + +/* SRM_DBG_REG: SRAM debug access */ +#define FR_BZ_SRM_DBG 0x03000000 +#define FR_BZ_SRM_DBG_STEP 8 +#define FR_CZ_SRM_DBG_ROWS 262144 +#define FR_BB_SRM_DBG_ROWS 2097152 +#define FRF_BZ_SRM_DBG_LBN 0 +#define FRF_BZ_SRM_DBG_WIDTH 64 + +/* TB_MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_CZ_TB_MSIX_PBA_TABLE 0x00008000 +#define FR_CZ_TB_MSIX_PBA_TABLE_STEP 4 +#define FR_CZ_TB_MSIX_PBA_TABLE_ROWS 1024 +#define FRF_CZ_TB_MSIX_PBA_PEND_DWORD_LBN 0 +#define FRF_CZ_TB_MSIX_PBA_PEND_DWORD_WIDTH 32 + +/* DRIVER_EV */ +#define FSF_AZ_DRIVER_EV_SUBCODE_LBN 56 +#define FSF_AZ_DRIVER_EV_SUBCODE_WIDTH 4 +#define FSE_BZ_TX_DSC_ERROR_EV 15 +#define FSE_BZ_RX_DSC_ERROR_EV 14 +#define FSE_AA_RX_RECOVER_EV 11 +#define FSE_AZ_TIMER_EV 10 +#define FSE_AZ_TX_PKT_NON_TCP_UDP 9 +#define FSE_AZ_WAKE_UP_EV 6 +#define FSE_AZ_SRM_UPD_DONE_EV 5 +#define FSE_AB_EVQ_NOT_EN_EV 3 +#define FSE_AZ_EVQ_INIT_DONE_EV 2 +#define FSE_AZ_RX_DESCQ_FLS_DONE_EV 1 +#define FSE_AZ_TX_DESCQ_FLS_DONE_EV 0 +#define FSF_AZ_DRIVER_EV_SUBDATA_LBN 0 +#define FSF_AZ_DRIVER_EV_SUBDATA_WIDTH 14 + +/* EVENT_ENTRY */ +#define FSF_AZ_EV_CODE_LBN 60 +#define FSF_AZ_EV_CODE_WIDTH 4 +#define FSE_CZ_EV_CODE_MCDI_EV 12 +#define FSE_CZ_EV_CODE_USER_EV 8 +#define FSE_AZ_EV_CODE_DRV_GEN_EV 7 +#define FSE_AZ_EV_CODE_GLOBAL_EV 6 +#define FSE_AZ_EV_CODE_DRIVER_EV 5 +#define FSE_AZ_EV_CODE_TX_EV 2 +#define FSE_AZ_EV_CODE_RX_EV 0 +#define FSF_AZ_EV_DATA_LBN 0 +#define FSF_AZ_EV_DATA_WIDTH 60 + +/* GLOBAL_EV */ +#define FSF_BB_GLB_EV_RX_RECOVERY_LBN 12 +#define FSF_BB_GLB_EV_RX_RECOVERY_WIDTH 1 +#define FSF_AA_GLB_EV_RX_RECOVERY_LBN 11 +#define FSF_AA_GLB_EV_RX_RECOVERY_WIDTH 1 +#define FSF_BB_GLB_EV_XG_MGT_INTR_LBN 11 +#define FSF_BB_GLB_EV_XG_MGT_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_XFP_PHY0_INTR_LBN 10 +#define FSF_AB_GLB_EV_XFP_PHY0_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_XG_PHY0_INTR_LBN 9 +#define FSF_AB_GLB_EV_XG_PHY0_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_G_PHY0_INTR_LBN 7 +#define FSF_AB_GLB_EV_G_PHY0_INTR_WIDTH 1 + +/* LEGACY_INT_VEC */ +#define FSF_AZ_NET_IVEC_FATAL_INT_LBN 64 +#define FSF_AZ_NET_IVEC_FATAL_INT_WIDTH 1 +#define FSF_AZ_NET_IVEC_INT_Q_LBN 40 +#define FSF_AZ_NET_IVEC_INT_Q_WIDTH 4 +#define FSF_AZ_NET_IVEC_INT_FLAG_LBN 32 +#define FSF_AZ_NET_IVEC_INT_FLAG_WIDTH 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_HF_LBN 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_HF_WIDTH 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_AF_LBN 0 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_AF_WIDTH 1 + +/* MC_XGMAC_FLTR_RULE_DEF */ +#define FSF_CZ_MC_XFRC_MODE_LBN 416 +#define FSF_CZ_MC_XFRC_MODE_WIDTH 1 +#define FSE_CZ_MC_XFRC_MODE_LAYERED 1 +#define FSE_CZ_MC_XFRC_MODE_SIMPLE 0 +#define FSF_CZ_MC_XFRC_HASH_LBN 384 +#define FSF_CZ_MC_XFRC_HASH_WIDTH 32 +#define FSF_CZ_MC_XFRC_LAYER4_BYTE_MASK_LBN 256 +#define FSF_CZ_MC_XFRC_LAYER4_BYTE_MASK_WIDTH 128 +#define FSF_CZ_MC_XFRC_LAYER3_BYTE_MASK_LBN 128 +#define FSF_CZ_MC_XFRC_LAYER3_BYTE_MASK_WIDTH 128 +#define FSF_CZ_MC_XFRC_LAYER2_OR_SIMPLE_BYTE_MASK_LBN 0 +#define FSF_CZ_MC_XFRC_LAYER2_OR_SIMPLE_BYTE_MASK_WIDTH 128 + +/* RX_EV */ +#define FSF_CZ_RX_EV_PKT_NOT_PARSED_LBN 58 +#define FSF_CZ_RX_EV_PKT_NOT_PARSED_WIDTH 1 +#define FSF_CZ_RX_EV_IPV6_PKT_LBN 57 +#define FSF_CZ_RX_EV_IPV6_PKT_WIDTH 1 +#define FSF_AZ_RX_EV_PKT_OK_LBN 56 +#define FSF_AZ_RX_EV_PKT_OK_WIDTH 1 +#define FSF_AZ_RX_EV_PAUSE_FRM_ERR_LBN 55 +#define FSF_AZ_RX_EV_PAUSE_FRM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_BUF_OWNER_ID_ERR_LBN 54 +#define FSF_AZ_RX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_IP_FRAG_ERR_LBN 53 +#define FSF_AZ_RX_EV_IP_FRAG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR_LBN 52 +#define FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51 +#define FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_ETH_CRC_ERR_LBN 50 +#define FSF_AZ_RX_EV_ETH_CRC_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_FRM_TRUNC_LBN 49 +#define FSF_AZ_RX_EV_FRM_TRUNC_WIDTH 1 +#define FSF_AA_RX_EV_DRIB_NIB_LBN 49 +#define FSF_AA_RX_EV_DRIB_NIB_WIDTH 1 +#define FSF_AZ_RX_EV_TOBE_DISC_LBN 47 +#define FSF_AZ_RX_EV_TOBE_DISC_WIDTH 1 +#define FSF_AZ_RX_EV_PKT_TYPE_LBN 44 +#define FSF_AZ_RX_EV_PKT_TYPE_WIDTH 3 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN_JUMBO 5 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN_LLC 4 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN 3 +#define FSE_AZ_RX_EV_PKT_TYPE_JUMBO 2 +#define FSE_AZ_RX_EV_PKT_TYPE_LLC 1 +#define FSE_AZ_RX_EV_PKT_TYPE_ETH 0 +#define FSF_AZ_RX_EV_HDR_TYPE_LBN 42 +#define FSF_AZ_RX_EV_HDR_TYPE_WIDTH 2 +#define FSE_AZ_RX_EV_HDR_TYPE_OTHER 3 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_OTHER 2 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_OTHER 2 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_UDP 1 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP 1 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_TCP 0 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP 0 +#define FSF_AZ_RX_EV_DESC_Q_EMPTY_LBN 41 +#define FSF_AZ_RX_EV_DESC_Q_EMPTY_WIDTH 1 +#define FSF_AZ_RX_EV_MCAST_HASH_MATCH_LBN 40 +#define FSF_AZ_RX_EV_MCAST_HASH_MATCH_WIDTH 1 +#define FSF_AZ_RX_EV_MCAST_PKT_LBN 39 +#define FSF_AZ_RX_EV_MCAST_PKT_WIDTH 1 +#define FSF_AA_RX_EV_RECOVERY_FLAG_LBN 37 +#define FSF_AA_RX_EV_RECOVERY_FLAG_WIDTH 1 +#define FSF_AZ_RX_EV_Q_LABEL_LBN 32 +#define FSF_AZ_RX_EV_Q_LABEL_WIDTH 5 +#define FSF_AZ_RX_EV_JUMBO_CONT_LBN 31 +#define FSF_AZ_RX_EV_JUMBO_CONT_WIDTH 1 +#define FSF_AZ_RX_EV_PORT_LBN 30 +#define FSF_AZ_RX_EV_PORT_WIDTH 1 +#define FSF_AZ_RX_EV_BYTE_CNT_LBN 16 +#define FSF_AZ_RX_EV_BYTE_CNT_WIDTH 14 +#define FSF_AZ_RX_EV_SOP_LBN 15 +#define FSF_AZ_RX_EV_SOP_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_PKT_OK_LBN 14 +#define FSF_AZ_RX_EV_ISCSI_PKT_OK_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_DDIG_ERR_LBN 13 +#define FSF_AZ_RX_EV_ISCSI_DDIG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_HDIG_ERR_LBN 12 +#define FSF_AZ_RX_EV_ISCSI_HDIG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_DESC_PTR_LBN 0 +#define FSF_AZ_RX_EV_DESC_PTR_WIDTH 12 + +/* RX_KER_DESC */ +#define FSF_AZ_RX_KER_BUF_SIZE_LBN 48 +#define FSF_AZ_RX_KER_BUF_SIZE_WIDTH 14 +#define FSF_AZ_RX_KER_BUF_REGION_LBN 46 +#define FSF_AZ_RX_KER_BUF_REGION_WIDTH 2 +#define FSF_AZ_RX_KER_BUF_ADDR_LBN 0 +#define FSF_AZ_RX_KER_BUF_ADDR_WIDTH 46 + +/* RX_USER_DESC */ +#define FSF_AZ_RX_USER_2BYTE_OFFSET_LBN 20 +#define FSF_AZ_RX_USER_2BYTE_OFFSET_WIDTH 12 +#define FSF_AZ_RX_USER_BUF_ID_LBN 0 +#define FSF_AZ_RX_USER_BUF_ID_WIDTH 20 + +/* TX_EV */ +#define FSF_AZ_TX_EV_PKT_ERR_LBN 38 +#define FSF_AZ_TX_EV_PKT_ERR_WIDTH 1 +#define FSF_AZ_TX_EV_PKT_TOO_BIG_LBN 37 +#define FSF_AZ_TX_EV_PKT_TOO_BIG_WIDTH 1 +#define FSF_AZ_TX_EV_Q_LABEL_LBN 32 +#define FSF_AZ_TX_EV_Q_LABEL_WIDTH 5 +#define FSF_AZ_TX_EV_PORT_LBN 16 +#define FSF_AZ_TX_EV_PORT_WIDTH 1 +#define FSF_AZ_TX_EV_WQ_FF_FULL_LBN 15 +#define FSF_AZ_TX_EV_WQ_FF_FULL_WIDTH 1 +#define FSF_AZ_TX_EV_BUF_OWNER_ID_ERR_LBN 14 +#define FSF_AZ_TX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define FSF_AZ_TX_EV_COMP_LBN 12 +#define FSF_AZ_TX_EV_COMP_WIDTH 1 +#define FSF_AZ_TX_EV_DESC_PTR_LBN 0 +#define FSF_AZ_TX_EV_DESC_PTR_WIDTH 12 + +/* TX_KER_DESC */ +#define FSF_AZ_TX_KER_CONT_LBN 62 +#define FSF_AZ_TX_KER_CONT_WIDTH 1 +#define FSF_AZ_TX_KER_BYTE_COUNT_LBN 48 +#define FSF_AZ_TX_KER_BYTE_COUNT_WIDTH 14 +#define FSF_AZ_TX_KER_BUF_REGION_LBN 46 +#define FSF_AZ_TX_KER_BUF_REGION_WIDTH 2 +#define FSF_AZ_TX_KER_BUF_ADDR_LBN 0 +#define FSF_AZ_TX_KER_BUF_ADDR_WIDTH 46 + +/* TX_USER_DESC */ +#define FSF_AZ_TX_USER_SW_EV_EN_LBN 48 +#define FSF_AZ_TX_USER_SW_EV_EN_WIDTH 1 +#define FSF_AZ_TX_USER_CONT_LBN 46 +#define FSF_AZ_TX_USER_CONT_WIDTH 1 +#define FSF_AZ_TX_USER_BYTE_CNT_LBN 33 +#define FSF_AZ_TX_USER_BYTE_CNT_WIDTH 13 +#define FSF_AZ_TX_USER_BUF_ID_LBN 13 +#define FSF_AZ_TX_USER_BUF_ID_WIDTH 20 +#define FSF_AZ_TX_USER_BYTE_OFS_LBN 0 +#define FSF_AZ_TX_USER_BYTE_OFS_WIDTH 13 + +/* USER_EV */ +#define FSF_CZ_USER_QID_LBN 32 +#define FSF_CZ_USER_QID_WIDTH 10 +#define FSF_CZ_USER_EV_REG_VALUE_LBN 0 +#define FSF_CZ_USER_EV_REG_VALUE_WIDTH 32 + +/************************************************************************** + * + * Falcon B0 PCIe core indirect registers + * + ************************************************************************** + */ + +#define FPCR_BB_PCIE_DEVICE_CTRL_STAT 0x68 + +#define FPCR_BB_PCIE_LINK_CTRL_STAT 0x70 + +#define FPCR_BB_ACK_RPL_TIMER 0x700 +#define FPCRF_BB_ACK_TL_LBN 0 +#define FPCRF_BB_ACK_TL_WIDTH 16 +#define FPCRF_BB_RPL_TL_LBN 16 +#define FPCRF_BB_RPL_TL_WIDTH 16 + +#define FPCR_BB_ACK_FREQ 0x70C +#define FPCRF_BB_ACK_FREQ_LBN 0 +#define FPCRF_BB_ACK_FREQ_WIDTH 7 + +/************************************************************************** + * + * Pseudo-registers and fields + * + ************************************************************************** + */ + +/* Interrupt acknowledge work-around register (A0/A1 only) */ +#define FR_AA_WORK_AROUND_BROKEN_PCI_READS 0x0070 + +/* EE_SPI_HCMD_REG: SPI host command register */ +/* Values for the EE_SPI_HCMD_SF_SEL register field */ +#define FFE_AB_SPI_DEVICE_EEPROM 0 +#define FFE_AB_SPI_DEVICE_FLASH 1 + +/* NIC_STAT_REG: NIC status register */ +#define FRF_AB_STRAP_10G_LBN 2 +#define FRF_AB_STRAP_10G_WIDTH 1 +#define FRF_AA_STRAP_PCIE_LBN 0 +#define FRF_AA_STRAP_PCIE_WIDTH 1 + +/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */ +#define FRF_AZ_FATAL_INTR_LBN 0 +#define FRF_AZ_FATAL_INTR_WIDTH 12 + +/* SRM_CFG_REG: SRAM configuration register */ +/* We treat the number of SRAM banks and bank size as a single field */ +#define FRF_AZ_SRM_NB_SZ_LBN FRF_AZ_SRM_BANK_SIZE_LBN +#define FRF_AZ_SRM_NB_SZ_WIDTH \ + (FRF_AZ_SRM_BANK_SIZE_WIDTH + FRF_AZ_SRM_NUM_BANK_WIDTH) +#define FFE_AB_SRM_NB1_SZ2M 0 +#define FFE_AB_SRM_NB1_SZ4M 1 +#define FFE_AB_SRM_NB1_SZ8M 2 +#define FFE_AB_SRM_NB_SZ_DEF 3 +#define FFE_AB_SRM_NB2_SZ4M 4 +#define FFE_AB_SRM_NB2_SZ8M 5 +#define FFE_AB_SRM_NB2_SZ16M 6 +#define FFE_AB_SRM_NB_SZ_RES 7 + +/* RX_DESC_UPD_REGP0: Receive descriptor update register. */ +/* We write just the last dword of these registers */ +#define FR_AZ_RX_DESC_UPD_DWORD_P0 \ + (BUILD_BUG_ON_ZERO(FR_AA_RX_DESC_UPD_KER != FR_BZ_RX_DESC_UPD_P0) + \ + FR_BZ_RX_DESC_UPD_P0 + 3 * 4) +#define FRF_AZ_RX_DESC_WPTR_DWORD_LBN (FRF_AZ_RX_DESC_WPTR_LBN - 3 * 32) +#define FRF_AZ_RX_DESC_WPTR_DWORD_WIDTH FRF_AZ_RX_DESC_WPTR_WIDTH + +/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */ +#define FR_AZ_TX_DESC_UPD_DWORD_P0 \ + (BUILD_BUG_ON_ZERO(FR_AA_TX_DESC_UPD_KER != FR_BZ_TX_DESC_UPD_P0) + \ + FR_BZ_TX_DESC_UPD_P0 + 3 * 4) +#define FRF_AZ_TX_DESC_WPTR_DWORD_LBN (FRF_AZ_TX_DESC_WPTR_LBN - 3 * 32) +#define FRF_AZ_TX_DESC_WPTR_DWORD_WIDTH FRF_AZ_TX_DESC_WPTR_WIDTH + +/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */ +#define FRF_AB_GMF_HSTFLTRFRM_PAUSE_LBN 12 +#define FRF_AB_GMF_HSTFLTRFRM_PAUSE_WIDTH 1 + +/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */ +#define FRF_AB_GMF_HSTFLTRFRMDC_PAUSE_LBN 12 +#define FRF_AB_GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1 + +/* XM_TX_PARAM_REG: XGMAC transmit parameter register */ +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LBN FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN +#define FRF_AB_XM_MAX_TX_FRM_SIZE_WIDTH (FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH + \ + FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH) + +/* XM_RX_PARAM_REG: XGMAC receive parameter register */ +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LBN FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN +#define FRF_AB_XM_MAX_RX_FRM_SIZE_WIDTH (FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH + \ + FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH) + +/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */ +/* Default values */ +#define FFE_AB_XX_TXDRV_DEQ_DEF 0xe /* deq=.6 */ +#define FFE_AB_XX_TXDRV_DTX_DEF 0x5 /* 1.25 */ +#define FFE_AB_XX_SD_CTL_DRV_DEF 0 /* 20mA */ + +/* XX_CORE_STAT_REG: XAUI XGXS core status register */ +/* XGXS all-lanes status fields */ +#define FRF_AB_XX_SYNC_STAT_LBN FRF_AB_XX_SYNC_STAT0_LBN +#define FRF_AB_XX_SYNC_STAT_WIDTH 4 +#define FRF_AB_XX_COMMA_DET_LBN FRF_AB_XX_COMMA_DET_CH0_LBN +#define FRF_AB_XX_COMMA_DET_WIDTH 4 +#define FRF_AB_XX_CHAR_ERR_LBN FRF_AB_XX_CHAR_ERR_CH0_LBN +#define FRF_AB_XX_CHAR_ERR_WIDTH 4 +#define FRF_AB_XX_DISPERR_LBN FRF_AB_XX_DISPERR_CH0_LBN +#define FRF_AB_XX_DISPERR_WIDTH 4 +#define FFE_AB_XX_STAT_ALL_LANES 0xf +#define FRF_AB_XX_FORCE_SIG_LBN FRF_AB_XX_FORCE_SIG0_VAL_LBN +#define FRF_AB_XX_FORCE_SIG_WIDTH 8 +#define FFE_AB_XX_FORCE_SIG_ALL_LANES 0xff + +/* DRIVER_EV */ +/* Sub-fields of an RX flush completion event */ +#define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_LBN 12 +#define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_WIDTH 1 +#define FSF_AZ_DRIVER_EV_RX_DESCQ_ID_LBN 0 +#define FSF_AZ_DRIVER_EV_RX_DESCQ_ID_WIDTH 12 + +/* EVENT_ENTRY */ +/* Magic number field for event test */ +#define FSF_AZ_DRV_GEN_EV_MAGIC_LBN 0 +#define FSF_AZ_DRV_GEN_EV_MAGIC_WIDTH 32 + +/************************************************************************** + * + * Falcon MAC stats + * + ************************************************************************** + * + */ + +#define GRxGoodOct_offset 0x0 +#define GRxGoodOct_WIDTH 48 +#define GRxBadOct_offset 0x8 +#define GRxBadOct_WIDTH 48 +#define GRxMissPkt_offset 0x10 +#define GRxMissPkt_WIDTH 32 +#define GRxFalseCRS_offset 0x14 +#define GRxFalseCRS_WIDTH 32 +#define GRxPausePkt_offset 0x18 +#define GRxPausePkt_WIDTH 32 +#define GRxBadPkt_offset 0x1C +#define GRxBadPkt_WIDTH 32 +#define GRxUcastPkt_offset 0x20 +#define GRxUcastPkt_WIDTH 32 +#define GRxMcastPkt_offset 0x24 +#define GRxMcastPkt_WIDTH 32 +#define GRxBcastPkt_offset 0x28 +#define GRxBcastPkt_WIDTH 32 +#define GRxGoodLt64Pkt_offset 0x2C +#define GRxGoodLt64Pkt_WIDTH 32 +#define GRxBadLt64Pkt_offset 0x30 +#define GRxBadLt64Pkt_WIDTH 32 +#define GRx64Pkt_offset 0x34 +#define GRx64Pkt_WIDTH 32 +#define GRx65to127Pkt_offset 0x38 +#define GRx65to127Pkt_WIDTH 32 +#define GRx128to255Pkt_offset 0x3C +#define GRx128to255Pkt_WIDTH 32 +#define GRx256to511Pkt_offset 0x40 +#define GRx256to511Pkt_WIDTH 32 +#define GRx512to1023Pkt_offset 0x44 +#define GRx512to1023Pkt_WIDTH 32 +#define GRx1024to15xxPkt_offset 0x48 +#define GRx1024to15xxPkt_WIDTH 32 +#define GRx15xxtoJumboPkt_offset 0x4C +#define GRx15xxtoJumboPkt_WIDTH 32 +#define GRxGtJumboPkt_offset 0x50 +#define GRxGtJumboPkt_WIDTH 32 +#define GRxFcsErr64to15xxPkt_offset 0x54 +#define GRxFcsErr64to15xxPkt_WIDTH 32 +#define GRxFcsErr15xxtoJumboPkt_offset 0x58 +#define GRxFcsErr15xxtoJumboPkt_WIDTH 32 +#define GRxFcsErrGtJumboPkt_offset 0x5C +#define GRxFcsErrGtJumboPkt_WIDTH 32 +#define GTxGoodBadOct_offset 0x80 +#define GTxGoodBadOct_WIDTH 48 +#define GTxGoodOct_offset 0x88 +#define GTxGoodOct_WIDTH 48 +#define GTxSglColPkt_offset 0x90 +#define GTxSglColPkt_WIDTH 32 +#define GTxMultColPkt_offset 0x94 +#define GTxMultColPkt_WIDTH 32 +#define GTxExColPkt_offset 0x98 +#define GTxExColPkt_WIDTH 32 +#define GTxDefPkt_offset 0x9C +#define GTxDefPkt_WIDTH 32 +#define GTxLateCol_offset 0xA0 +#define GTxLateCol_WIDTH 32 +#define GTxExDefPkt_offset 0xA4 +#define GTxExDefPkt_WIDTH 32 +#define GTxPausePkt_offset 0xA8 +#define GTxPausePkt_WIDTH 32 +#define GTxBadPkt_offset 0xAC +#define GTxBadPkt_WIDTH 32 +#define GTxUcastPkt_offset 0xB0 +#define GTxUcastPkt_WIDTH 32 +#define GTxMcastPkt_offset 0xB4 +#define GTxMcastPkt_WIDTH 32 +#define GTxBcastPkt_offset 0xB8 +#define GTxBcastPkt_WIDTH 32 +#define GTxLt64Pkt_offset 0xBC +#define GTxLt64Pkt_WIDTH 32 +#define GTx64Pkt_offset 0xC0 +#define GTx64Pkt_WIDTH 32 +#define GTx65to127Pkt_offset 0xC4 +#define GTx65to127Pkt_WIDTH 32 +#define GTx128to255Pkt_offset 0xC8 +#define GTx128to255Pkt_WIDTH 32 +#define GTx256to511Pkt_offset 0xCC +#define GTx256to511Pkt_WIDTH 32 +#define GTx512to1023Pkt_offset 0xD0 +#define GTx512to1023Pkt_WIDTH 32 +#define GTx1024to15xxPkt_offset 0xD4 +#define GTx1024to15xxPkt_WIDTH 32 +#define GTx15xxtoJumboPkt_offset 0xD8 +#define GTx15xxtoJumboPkt_WIDTH 32 +#define GTxGtJumboPkt_offset 0xDC +#define GTxGtJumboPkt_WIDTH 32 +#define GTxNonTcpUdpPkt_offset 0xE0 +#define GTxNonTcpUdpPkt_WIDTH 16 +#define GTxMacSrcErrPkt_offset 0xE4 +#define GTxMacSrcErrPkt_WIDTH 16 +#define GTxIpSrcErrPkt_offset 0xE8 +#define GTxIpSrcErrPkt_WIDTH 16 +#define GDmaDone_offset 0xEC +#define GDmaDone_WIDTH 32 + +#define XgRxOctets_offset 0x0 +#define XgRxOctets_WIDTH 48 +#define XgRxOctetsOK_offset 0x8 +#define XgRxOctetsOK_WIDTH 48 +#define XgRxPkts_offset 0x10 +#define XgRxPkts_WIDTH 32 +#define XgRxPktsOK_offset 0x14 +#define XgRxPktsOK_WIDTH 32 +#define XgRxBroadcastPkts_offset 0x18 +#define XgRxBroadcastPkts_WIDTH 32 +#define XgRxMulticastPkts_offset 0x1C +#define XgRxMulticastPkts_WIDTH 32 +#define XgRxUnicastPkts_offset 0x20 +#define XgRxUnicastPkts_WIDTH 32 +#define XgRxUndersizePkts_offset 0x24 +#define XgRxUndersizePkts_WIDTH 32 +#define XgRxOversizePkts_offset 0x28 +#define XgRxOversizePkts_WIDTH 32 +#define XgRxJabberPkts_offset 0x2C +#define XgRxJabberPkts_WIDTH 32 +#define XgRxUndersizeFCSerrorPkts_offset 0x30 +#define XgRxUndersizeFCSerrorPkts_WIDTH 32 +#define XgRxDropEvents_offset 0x34 +#define XgRxDropEvents_WIDTH 32 +#define XgRxFCSerrorPkts_offset 0x38 +#define XgRxFCSerrorPkts_WIDTH 32 +#define XgRxAlignError_offset 0x3C +#define XgRxAlignError_WIDTH 32 +#define XgRxSymbolError_offset 0x40 +#define XgRxSymbolError_WIDTH 32 +#define XgRxInternalMACError_offset 0x44 +#define XgRxInternalMACError_WIDTH 32 +#define XgRxControlPkts_offset 0x48 +#define XgRxControlPkts_WIDTH 32 +#define XgRxPausePkts_offset 0x4C +#define XgRxPausePkts_WIDTH 32 +#define XgRxPkts64Octets_offset 0x50 +#define XgRxPkts64Octets_WIDTH 32 +#define XgRxPkts65to127Octets_offset 0x54 +#define XgRxPkts65to127Octets_WIDTH 32 +#define XgRxPkts128to255Octets_offset 0x58 +#define XgRxPkts128to255Octets_WIDTH 32 +#define XgRxPkts256to511Octets_offset 0x5C +#define XgRxPkts256to511Octets_WIDTH 32 +#define XgRxPkts512to1023Octets_offset 0x60 +#define XgRxPkts512to1023Octets_WIDTH 32 +#define XgRxPkts1024to15xxOctets_offset 0x64 +#define XgRxPkts1024to15xxOctets_WIDTH 32 +#define XgRxPkts15xxtoMaxOctets_offset 0x68 +#define XgRxPkts15xxtoMaxOctets_WIDTH 32 +#define XgRxLengthError_offset 0x6C +#define XgRxLengthError_WIDTH 32 +#define XgTxPkts_offset 0x80 +#define XgTxPkts_WIDTH 32 +#define XgTxOctets_offset 0x88 +#define XgTxOctets_WIDTH 48 +#define XgTxMulticastPkts_offset 0x90 +#define XgTxMulticastPkts_WIDTH 32 +#define XgTxBroadcastPkts_offset 0x94 +#define XgTxBroadcastPkts_WIDTH 32 +#define XgTxUnicastPkts_offset 0x98 +#define XgTxUnicastPkts_WIDTH 32 +#define XgTxControlPkts_offset 0x9C +#define XgTxControlPkts_WIDTH 32 +#define XgTxPausePkts_offset 0xA0 +#define XgTxPausePkts_WIDTH 32 +#define XgTxPkts64Octets_offset 0xA4 +#define XgTxPkts64Octets_WIDTH 32 +#define XgTxPkts65to127Octets_offset 0xA8 +#define XgTxPkts65to127Octets_WIDTH 32 +#define XgTxPkts128to255Octets_offset 0xAC +#define XgTxPkts128to255Octets_WIDTH 32 +#define XgTxPkts256to511Octets_offset 0xB0 +#define XgTxPkts256to511Octets_WIDTH 32 +#define XgTxPkts512to1023Octets_offset 0xB4 +#define XgTxPkts512to1023Octets_WIDTH 32 +#define XgTxPkts1024to15xxOctets_offset 0xB8 +#define XgTxPkts1024to15xxOctets_WIDTH 32 +#define XgTxPkts1519toMaxOctets_offset 0xBC +#define XgTxPkts1519toMaxOctets_WIDTH 32 +#define XgTxUndersizePkts_offset 0xC0 +#define XgTxUndersizePkts_WIDTH 32 +#define XgTxOversizePkts_offset 0xC4 +#define XgTxOversizePkts_WIDTH 32 +#define XgTxNonTcpUdpPkt_offset 0xC8 +#define XgTxNonTcpUdpPkt_WIDTH 16 +#define XgTxMacSrcErrPkt_offset 0xCC +#define XgTxMacSrcErrPkt_WIDTH 16 +#define XgTxIpSrcErrPkt_offset 0xD0 +#define XgTxIpSrcErrPkt_WIDTH 16 +#define XgDmaDone_offset 0xD4 +#define XgDmaDone_WIDTH 32 + +#define FALCON_STATS_NOT_DONE 0x00000000 +#define FALCON_STATS_DONE 0xffffffff + +/************************************************************************** + * + * Falcon non-volatile configuration + * + ************************************************************************** + */ + +/* Board configuration v2 (v1 is obsolete; later versions are compatible) */ +struct falcon_nvconfig_board_v2 { + __le16 nports; + u8 port0_phy_addr; + u8 port0_phy_type; + u8 port1_phy_addr; + u8 port1_phy_type; + __le16 asic_sub_revision; + __le16 board_revision; +} __packed; + +/* Board configuration v3 extra information */ +struct falcon_nvconfig_board_v3 { + __le32 spi_device_type[2]; +} __packed; + +/* Bit numbers for spi_device_type */ +#define SPI_DEV_TYPE_SIZE_LBN 0 +#define SPI_DEV_TYPE_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_ADDR_LEN_LBN 6 +#define SPI_DEV_TYPE_ADDR_LEN_WIDTH 2 +#define SPI_DEV_TYPE_ERASE_CMD_LBN 8 +#define SPI_DEV_TYPE_ERASE_CMD_WIDTH 8 +#define SPI_DEV_TYPE_ERASE_SIZE_LBN 16 +#define SPI_DEV_TYPE_ERASE_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_BLOCK_SIZE_LBN 24 +#define SPI_DEV_TYPE_BLOCK_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_FIELD(type, field) \ + (((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(EFX_WIDTH(field))) + +#define FALCON_NVCONFIG_OFFSET 0x300 + +#define FALCON_NVCONFIG_BOARD_MAGIC_NUM 0xFA1C +struct falcon_nvconfig { + efx_oword_t ee_vpd_cfg_reg; /* 0x300 */ + u8 mac_address[2][8]; /* 0x310 */ + efx_oword_t pcie_sd_ctl0123_reg; /* 0x320 */ + efx_oword_t pcie_sd_ctl45_reg; /* 0x330 */ + efx_oword_t pcie_pcs_ctl_stat_reg; /* 0x340 */ + efx_oword_t hw_init_reg; /* 0x350 */ + efx_oword_t nic_stat_reg; /* 0x360 */ + efx_oword_t glb_ctl_reg; /* 0x370 */ + efx_oword_t srm_cfg_reg; /* 0x380 */ + efx_oword_t spare_reg; /* 0x390 */ + __le16 board_magic_num; /* 0x3A0 */ + __le16 board_struct_ver; + __le16 board_checksum; + struct falcon_nvconfig_board_v2 board_v2; + efx_oword_t ee_base_page_reg; /* 0x3B0 */ + struct falcon_nvconfig_board_v3 board_v3; /* 0x3C0 */ +} __packed; + +#endif /* EFX_REGS_H */ diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c index 98bff5ada09a..a97c923b560c 100644 --- a/drivers/net/sfc/rx.c +++ b/drivers/net/sfc/rx.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2005-2008 Solarflare Communications Inc. + * Copyright 2005-2009 Solarflare Communications Inc. * * 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 @@ -16,9 +16,8 @@ #include <net/ip.h> #include <net/checksum.h> #include "net_driver.h" -#include "rx.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "selftest.h" #include "workarounds.h" @@ -61,7 +60,7 @@ * rx_alloc_method = (rx_alloc_level > RX_ALLOC_LEVEL_LRO ? * RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB) */ -static int rx_alloc_method = RX_ALLOC_METHOD_PAGE; +static int rx_alloc_method = RX_ALLOC_METHOD_AUTO; #define RX_ALLOC_LEVEL_LRO 0x2000 #define RX_ALLOC_LEVEL_MAX 0x3000 @@ -293,8 +292,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, * fill anyway. */ fill_level = (rx_queue->added_count - rx_queue->removed_count); - EFX_BUG_ON_PARANOID(fill_level > - rx_queue->efx->type->rxd_ring_mask + 1); + EFX_BUG_ON_PARANOID(fill_level > EFX_RXQ_SIZE); /* Don't fill if we don't need to */ if (fill_level >= rx_queue->fast_fill_trigger) @@ -316,8 +314,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, retry: /* Recalculate current fill level now that we have the lock */ fill_level = (rx_queue->added_count - rx_queue->removed_count); - EFX_BUG_ON_PARANOID(fill_level > - rx_queue->efx->type->rxd_ring_mask + 1); + EFX_BUG_ON_PARANOID(fill_level > EFX_RXQ_SIZE); space = rx_queue->fast_fill_limit - fill_level; if (space < EFX_RX_BATCH) goto out_unlock; @@ -329,8 +326,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, do { for (i = 0; i < EFX_RX_BATCH; ++i) { - index = (rx_queue->added_count & - rx_queue->efx->type->rxd_ring_mask); + index = rx_queue->added_count & EFX_RXQ_MASK; rx_buf = efx_rx_buffer(rx_queue, index); rc = efx_init_rx_buffer(rx_queue, rx_buf); if (unlikely(rc)) @@ -345,7 +341,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, out: /* Send write pointer to card. */ - falcon_notify_rx_desc(rx_queue); + efx_nic_notify_rx_desc(rx_queue); /* If the fast fill is running inside from the refill tasklet, then * for SMP systems it may be running on a different CPU to @@ -448,17 +444,23 @@ static void efx_rx_packet_lro(struct efx_channel *channel, bool checksummed) { struct napi_struct *napi = &channel->napi_str; + gro_result_t gro_result; /* Pass the skb/page into the LRO engine */ if (rx_buf->page) { - struct sk_buff *skb = napi_get_frags(napi); + struct page *page = rx_buf->page; + struct sk_buff *skb; + EFX_BUG_ON_PARANOID(rx_buf->skb); + rx_buf->page = NULL; + + skb = napi_get_frags(napi); if (!skb) { - put_page(rx_buf->page); - goto out; + put_page(page); + return; } - skb_shinfo(skb)->frags[0].page = rx_buf->page; + skb_shinfo(skb)->frags[0].page = page; skb_shinfo(skb)->frags[0].page_offset = efx_rx_buf_offset(rx_buf); skb_shinfo(skb)->frags[0].size = rx_buf->len; @@ -470,17 +472,24 @@ static void efx_rx_packet_lro(struct efx_channel *channel, skb->ip_summed = checksummed ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE; - napi_gro_frags(napi); + skb_record_rx_queue(skb, channel->channel); -out: - EFX_BUG_ON_PARANOID(rx_buf->skb); - rx_buf->page = NULL; + gro_result = napi_gro_frags(napi); } else { - EFX_BUG_ON_PARANOID(!rx_buf->skb); - EFX_BUG_ON_PARANOID(!checksummed); + struct sk_buff *skb = rx_buf->skb; - napi_gro_receive(napi, rx_buf->skb); + EFX_BUG_ON_PARANOID(!skb); + EFX_BUG_ON_PARANOID(!checksummed); rx_buf->skb = NULL; + + gro_result = napi_gro_receive(napi, skb); + } + + if (gro_result == GRO_NORMAL) { + channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; + } else if (gro_result != GRO_DROP) { + channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO; + channel->irq_mod_score += 2; } } @@ -558,7 +567,7 @@ void __efx_rx_packet(struct efx_channel *channel, if (unlikely(efx->loopback_selftest)) { efx_loopback_rx_packet(efx, rx_buf->data, rx_buf->len); efx_free_rx_buffer(efx, rx_buf); - goto done; + return; } if (rx_buf->skb) { @@ -570,34 +579,28 @@ void __efx_rx_packet(struct efx_channel *channel, * at the ethernet header */ rx_buf->skb->protocol = eth_type_trans(rx_buf->skb, efx->net_dev); + + skb_record_rx_queue(rx_buf->skb, channel->channel); } if (likely(checksummed || rx_buf->page)) { efx_rx_packet_lro(channel, rx_buf, checksummed); - goto done; + return; } /* We now own the SKB */ skb = rx_buf->skb; rx_buf->skb = NULL; - - EFX_BUG_ON_PARANOID(rx_buf->page); - EFX_BUG_ON_PARANOID(rx_buf->skb); EFX_BUG_ON_PARANOID(!skb); /* Set the SKB flags */ skb->ip_summed = CHECKSUM_NONE; - skb_record_rx_queue(skb, channel->channel); - /* Pass the packet up */ netif_receive_skb(skb); /* Update allocation strategy method */ channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; - -done: - ; } void efx_rx_strategy(struct efx_channel *channel) @@ -632,12 +635,12 @@ int efx_probe_rx_queue(struct efx_rx_queue *rx_queue) EFX_LOG(efx, "creating RX queue %d\n", rx_queue->queue); /* Allocate RX buffers */ - rxq_size = (efx->type->rxd_ring_mask + 1) * sizeof(*rx_queue->buffer); + rxq_size = EFX_RXQ_SIZE * sizeof(*rx_queue->buffer); rx_queue->buffer = kzalloc(rxq_size, GFP_KERNEL); if (!rx_queue->buffer) return -ENOMEM; - rc = falcon_probe_rx(rx_queue); + rc = efx_nic_probe_rx(rx_queue); if (rc) { kfree(rx_queue->buffer); rx_queue->buffer = NULL; @@ -647,7 +650,6 @@ int efx_probe_rx_queue(struct efx_rx_queue *rx_queue) void efx_init_rx_queue(struct efx_rx_queue *rx_queue) { - struct efx_nic *efx = rx_queue->efx; unsigned int max_fill, trigger, limit; EFX_LOG(rx_queue->efx, "initialising RX queue %d\n", rx_queue->queue); @@ -660,7 +662,7 @@ void efx_init_rx_queue(struct efx_rx_queue *rx_queue) rx_queue->min_overfill = -1U; /* Initialise limit fields */ - max_fill = efx->type->rxd_ring_mask + 1 - EFX_RXD_HEAD_ROOM; + max_fill = EFX_RXQ_SIZE - EFX_RXD_HEAD_ROOM; trigger = max_fill * min(rx_refill_threshold, 100U) / 100U; limit = max_fill * min(rx_refill_limit, 100U) / 100U; @@ -669,7 +671,7 @@ void efx_init_rx_queue(struct efx_rx_queue *rx_queue) rx_queue->fast_fill_limit = limit; /* Set up RX descriptor ring */ - falcon_init_rx(rx_queue); + efx_nic_init_rx(rx_queue); } void efx_fini_rx_queue(struct efx_rx_queue *rx_queue) @@ -679,11 +681,11 @@ void efx_fini_rx_queue(struct efx_rx_queue *rx_queue) EFX_LOG(rx_queue->efx, "shutting down RX queue %d\n", rx_queue->queue); - falcon_fini_rx(rx_queue); + efx_nic_fini_rx(rx_queue); /* Release RX buffers NB start at index 0 not current HW ptr */ if (rx_queue->buffer) { - for (i = 0; i <= rx_queue->efx->type->rxd_ring_mask; i++) { + for (i = 0; i <= EFX_RXQ_MASK; i++) { rx_buf = efx_rx_buffer(rx_queue, i); efx_fini_rx_buffer(rx_queue, rx_buf); } @@ -704,7 +706,7 @@ void efx_remove_rx_queue(struct efx_rx_queue *rx_queue) { EFX_LOG(rx_queue->efx, "destroying RX queue %d\n", rx_queue->queue); - falcon_remove_rx(rx_queue); + efx_nic_remove_rx(rx_queue); kfree(rx_queue->buffer); rx_queue->buffer = NULL; diff --git a/drivers/net/sfc/rx.h b/drivers/net/sfc/rx.h deleted file mode 100644 index 42ee7555a80b..000000000000 --- a/drivers/net/sfc/rx.h +++ /dev/null @@ -1,26 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#ifndef EFX_RX_H -#define EFX_RX_H - -#include "net_driver.h" - -int efx_probe_rx_queue(struct efx_rx_queue *rx_queue); -void efx_remove_rx_queue(struct efx_rx_queue *rx_queue); -void efx_init_rx_queue(struct efx_rx_queue *rx_queue); -void efx_fini_rx_queue(struct efx_rx_queue *rx_queue); - -void efx_rx_strategy(struct efx_channel *channel); -void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue); -void efx_rx_work(struct work_struct *data); -void __efx_rx_packet(struct efx_channel *channel, - struct efx_rx_buffer *rx_buf, bool checksummed); - -#endif /* EFX_RX_H */ diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c index 817c7efc11e0..14949bb303a0 100644 --- a/drivers/net/sfc/selftest.c +++ b/drivers/net/sfc/selftest.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -20,14 +20,12 @@ #include <linux/rtnetlink.h> #include <asm/io.h> #include "net_driver.h" -#include "ethtool.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "selftest.h" -#include "boards.h" #include "workarounds.h" #include "spi.h" -#include "falcon_io.h" +#include "io.h" #include "mdio_10g.h" /* @@ -57,6 +55,7 @@ static const char *payload_msg = * @flush: Drop all packets in efx_loopback_rx_packet * @packet_count: Number of packets being used in this test * @skbs: An array of skbs transmitted + * @offload_csum: Checksums are being offloaded * @rx_good: RX good packet count * @rx_bad: RX bad packet count * @payload: Payload used in tests @@ -65,10 +64,7 @@ struct efx_loopback_state { bool flush; int packet_count; struct sk_buff **skbs; - - /* Checksums are being offloaded */ bool offload_csum; - atomic_t rx_good; atomic_t rx_bad; struct efx_loopback_payload payload; @@ -104,7 +100,7 @@ static int efx_test_mdio(struct efx_nic *efx, struct efx_self_tests *tests) } if (EFX_IS10G(efx)) { - rc = efx_mdio_check_mmds(efx, efx->phy_op->mmds, 0); + rc = efx_mdio_check_mmds(efx, efx->mdio.mmds, 0); if (rc) goto out; } @@ -117,23 +113,26 @@ out: static int efx_test_nvram(struct efx_nic *efx, struct efx_self_tests *tests) { - int rc; + int rc = 0; + + if (efx->type->test_nvram) { + rc = efx->type->test_nvram(efx); + tests->nvram = rc ? -1 : 1; + } - rc = falcon_read_nvram(efx, NULL); - tests->nvram = rc ? -1 : 1; return rc; } static int efx_test_chip(struct efx_nic *efx, struct efx_self_tests *tests) { - int rc; + int rc = 0; - /* Not supported on A-series silicon */ - if (falcon_rev(efx) < FALCON_REV_B0) - return 0; + /* Test register access */ + if (efx->type->test_registers) { + rc = efx->type->test_registers(efx); + tests->registers = rc ? -1 : 1; + } - rc = falcon_test_registers(efx); - tests->registers = rc ? -1 : 1; return rc; } @@ -165,7 +164,7 @@ static int efx_test_interrupts(struct efx_nic *efx, goto success; } - falcon_generate_interrupt(efx); + efx_nic_generate_interrupt(efx); /* Wait for arrival of test interrupt. */ EFX_LOG(efx, "waiting for test interrupt\n"); @@ -177,8 +176,8 @@ static int efx_test_interrupts(struct efx_nic *efx, return -ETIMEDOUT; success: - EFX_LOG(efx, "test interrupt (mode %d) seen on CPU%d\n", - efx->interrupt_mode, efx->last_irq_cpu); + EFX_LOG(efx, "%s test interrupt seen on CPU%d\n", INT_MODE(efx), + efx->last_irq_cpu); tests->interrupt = 1; return 0; } @@ -203,7 +202,7 @@ static int efx_test_eventq_irq(struct efx_channel *channel, channel->eventq_magic = 0; smp_wmb(); - falcon_generate_test_event(channel, magic); + efx_nic_generate_test_event(channel, magic); /* Wait for arrival of interrupt */ count = 0; @@ -254,9 +253,6 @@ static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests, if (!efx->phy_op->run_tests) return 0; - EFX_BUG_ON_PARANOID(efx->phy_op->num_tests == 0 || - efx->phy_op->num_tests > EFX_MAX_PHY_TESTS); - mutex_lock(&efx->mac_lock); rc = efx->phy_op->run_tests(efx, tests->phy, flags); mutex_unlock(&efx->mac_lock); @@ -426,7 +422,7 @@ static int efx_begin_loopback(struct efx_tx_queue *tx_queue) if (efx_dev_registered(efx)) netif_tx_lock_bh(efx->net_dev); - rc = efx_xmit(efx, tx_queue, skb); + rc = efx_enqueue_skb(tx_queue, skb); if (efx_dev_registered(efx)) netif_tx_unlock_bh(efx->net_dev); @@ -439,7 +435,6 @@ static int efx_begin_loopback(struct efx_tx_queue *tx_queue) kfree_skb(skb); return -EPIPE; } - efx->net_dev->trans_start = jiffies; } return 0; @@ -527,7 +522,7 @@ efx_test_loopback(struct efx_tx_queue *tx_queue, for (i = 0; i < 3; i++) { /* Determine how many packets to send */ - state->packet_count = (efx->type->txd_ring_mask + 1) / 3; + state->packet_count = EFX_TXQ_SIZE / 3; state->packet_count = min(1 << (i << 2), state->packet_count); state->skbs = kzalloc(sizeof(state->skbs[0]) * state->packet_count, GFP_KERNEL); @@ -568,14 +563,49 @@ efx_test_loopback(struct efx_tx_queue *tx_queue, return 0; } +/* Wait for link up. On Falcon, we would prefer to rely on efx_monitor, but + * any contention on the mac lock (via e.g. efx_mac_mcast_work) causes it + * to delay and retry. Therefore, it's safer to just poll directly. Wait + * for link up and any faults to dissipate. */ +static int efx_wait_for_link(struct efx_nic *efx) +{ + struct efx_link_state *link_state = &efx->link_state; + int count; + bool link_up; + + for (count = 0; count < 40; count++) { + schedule_timeout_uninterruptible(HZ / 10); + + if (efx->type->monitor != NULL) { + mutex_lock(&efx->mac_lock); + efx->type->monitor(efx); + mutex_unlock(&efx->mac_lock); + } else { + struct efx_channel *channel = &efx->channel[0]; + if (channel->work_pending) + efx_process_channel_now(channel); + } + + mutex_lock(&efx->mac_lock); + link_up = link_state->up; + if (link_up) + link_up = !efx->mac_op->check_fault(efx); + mutex_unlock(&efx->mac_lock); + + if (link_up) + return 0; + } + + return -ETIMEDOUT; +} + static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests, unsigned int loopback_modes) { enum efx_loopback_mode mode; struct efx_loopback_state *state; struct efx_tx_queue *tx_queue; - bool link_up; - int count, rc = 0; + int rc = 0; /* Set the port loopback_selftest member. From this point on * all received packets will be dropped. Mark the state as @@ -594,46 +624,23 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests, /* Move the port into the specified loopback mode. */ state->flush = true; + mutex_lock(&efx->mac_lock); efx->loopback_mode = mode; - efx_reconfigure_port(efx); - - /* Wait for the PHY to signal the link is up. Interrupts - * are enabled for PHY's using LASI, otherwise we poll() - * quickly */ - count = 0; - do { - struct efx_channel *channel = &efx->channel[0]; + rc = __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + if (rc) { + EFX_ERR(efx, "unable to move into %s loopback\n", + LOOPBACK_MODE(efx)); + goto out; + } - efx->phy_op->poll(efx); - schedule_timeout_uninterruptible(HZ / 10); - if (channel->work_pending) - efx_process_channel_now(channel); - /* Wait for PHY events to be processed */ - flush_workqueue(efx->workqueue); - rmb(); - - /* We need both the phy and xaui links to be ok. - * rather than relying on the falcon_xmac irq/poll - * regime, just poll xaui directly */ - link_up = efx->link_up; - if (link_up && EFX_IS10G(efx) && - !falcon_xaui_link_ok(efx)) - link_up = false; - - } while ((++count < 20) && !link_up); - - /* The link should now be up. If it isn't, there is no point - * in attempting a loopback test */ - if (!link_up) { + rc = efx_wait_for_link(efx); + if (rc) { EFX_ERR(efx, "loopback %s never came up\n", LOOPBACK_MODE(efx)); - rc = -EIO; goto out; } - EFX_LOG(efx, "link came up in %s loopback in %d iterations\n", - LOOPBACK_MODE(efx), count); - /* Test every TX queue */ efx_for_each_tx_queue(tx_queue, efx) { state->offload_csum = (tx_queue->queue == @@ -667,7 +674,6 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, enum efx_loopback_mode loopback_mode = efx->loopback_mode; int phy_mode = efx->phy_mode; enum reset_type reset_method = RESET_TYPE_INVISIBLE; - struct ethtool_cmd ecmd; struct efx_channel *channel; int rc_test = 0, rc_reset = 0, rc; @@ -720,21 +726,21 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, mutex_unlock(&efx->mac_lock); /* free up all consumers of SRAM (including all the queues) */ - efx_reset_down(efx, reset_method, &ecmd); + efx_reset_down(efx, reset_method); rc = efx_test_chip(efx, tests); if (rc && !rc_test) rc_test = rc; /* reset the chip to recover from the register test */ - rc_reset = falcon_reset_hw(efx, reset_method); + rc_reset = efx->type->reset(efx, reset_method); /* Ensure that the phy is powered and out of loopback * for the bist and loopback tests */ efx->phy_mode &= ~PHY_MODE_LOW_POWER; efx->loopback_mode = LOOPBACK_NONE; - rc = efx_reset_up(efx, reset_method, &ecmd, rc_reset == 0); + rc = efx_reset_up(efx, reset_method, rc_reset == 0); if (rc && !rc_reset) rc_reset = rc; @@ -753,10 +759,12 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, rc_test = rc; /* restore the PHY to the previous state */ - efx->loopback_mode = loopback_mode; + mutex_lock(&efx->mac_lock); efx->phy_mode = phy_mode; efx->port_inhibited = false; - efx_ethtool_set_settings(efx->net_dev, &ecmd); + efx->loopback_mode = loopback_mode; + __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); return rc_test; } diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c deleted file mode 100644 index 49eb91b5f50c..000000000000 --- a/drivers/net/sfc/sfe4001.c +++ /dev/null @@ -1,435 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -/***************************************************************************** - * Support for the SFE4001 and SFN4111T NICs. - * - * The SFE4001 does not power-up fully at reset due to its high power - * consumption. We control its power via a PCA9539 I/O expander. - * Both boards have a MAX6647 temperature monitor which we expose to - * the lm90 driver. - * - * This also provides minimal support for reflashing the PHY, which is - * initiated by resetting it with the FLASH_CFG_1 pin pulled down. - * On SFE4001 rev A2 and later this is connected to the 3V3X output of - * the IO-expander; on the SFN4111T it is connected to Falcon's GPIO3. - * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually - * exclusive with the network device being open. - */ - -#include <linux/delay.h> -#include <linux/rtnetlink.h> -#include "net_driver.h" -#include "efx.h" -#include "phy.h" -#include "boards.h" -#include "falcon.h" -#include "falcon_hwdefs.h" -#include "falcon_io.h" -#include "mac.h" -#include "workarounds.h" - -/************************************************************************** - * - * I2C IO Expander device - * - **************************************************************************/ -#define PCA9539 0x74 - -#define P0_IN 0x00 -#define P0_OUT 0x02 -#define P0_INVERT 0x04 -#define P0_CONFIG 0x06 - -#define P0_EN_1V0X_LBN 0 -#define P0_EN_1V0X_WIDTH 1 -#define P0_EN_1V2_LBN 1 -#define P0_EN_1V2_WIDTH 1 -#define P0_EN_2V5_LBN 2 -#define P0_EN_2V5_WIDTH 1 -#define P0_EN_3V3X_LBN 3 -#define P0_EN_3V3X_WIDTH 1 -#define P0_EN_5V_LBN 4 -#define P0_EN_5V_WIDTH 1 -#define P0_SHORTEN_JTAG_LBN 5 -#define P0_SHORTEN_JTAG_WIDTH 1 -#define P0_X_TRST_LBN 6 -#define P0_X_TRST_WIDTH 1 -#define P0_DSP_RESET_LBN 7 -#define P0_DSP_RESET_WIDTH 1 - -#define P1_IN 0x01 -#define P1_OUT 0x03 -#define P1_INVERT 0x05 -#define P1_CONFIG 0x07 - -#define P1_AFE_PWD_LBN 0 -#define P1_AFE_PWD_WIDTH 1 -#define P1_DSP_PWD25_LBN 1 -#define P1_DSP_PWD25_WIDTH 1 -#define P1_RESERVED_LBN 2 -#define P1_RESERVED_WIDTH 2 -#define P1_SPARE_LBN 4 -#define P1_SPARE_WIDTH 4 - -/* Temperature Sensor */ -#define MAX664X_REG_RSL 0x02 -#define MAX664X_REG_WLHO 0x0B - -static void sfe4001_poweroff(struct efx_nic *efx) -{ - struct i2c_client *ioexp_client = efx->board_info.ioexp_client; - struct i2c_client *hwmon_client = efx->board_info.hwmon_client; - - /* Turn off all power rails and disable outputs */ - i2c_smbus_write_byte_data(ioexp_client, P0_OUT, 0xff); - i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, 0xff); - i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff); - - /* Clear any over-temperature alert */ - i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); -} - -static int sfe4001_poweron(struct efx_nic *efx) -{ - struct i2c_client *hwmon_client = efx->board_info.hwmon_client; - struct i2c_client *ioexp_client = efx->board_info.ioexp_client; - unsigned int i, j; - int rc; - u8 out; - - /* Clear any previous over-temperature alert */ - rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); - if (rc < 0) - return rc; - - /* Enable port 0 and port 1 outputs on IO expander */ - rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00); - if (rc) - return rc; - rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, - 0xff & ~(1 << P1_SPARE_LBN)); - if (rc) - goto fail_on; - - /* If PHY power is on, turn it all off and wait 1 second to - * ensure a full reset. - */ - rc = i2c_smbus_read_byte_data(ioexp_client, P0_OUT); - if (rc < 0) - goto fail_on; - out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) | - (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) | - (0 << P0_EN_1V0X_LBN)); - if (rc != out) { - EFX_INFO(efx, "power-cycling PHY\n"); - rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); - if (rc) - goto fail_on; - schedule_timeout_uninterruptible(HZ); - } - - for (i = 0; i < 20; ++i) { - /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */ - out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | - (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | - (1 << P0_X_TRST_LBN)); - if (efx->phy_mode & PHY_MODE_SPECIAL) - out |= 1 << P0_EN_3V3X_LBN; - - rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); - if (rc) - goto fail_on; - msleep(10); - - /* Turn on 1V power rail */ - out &= ~(1 << P0_EN_1V0X_LBN); - rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); - if (rc) - goto fail_on; - - EFX_INFO(efx, "waiting for DSP boot (attempt %d)...\n", i); - - /* In flash config mode, DSP does not turn on AFE, so - * just wait 1 second. - */ - if (efx->phy_mode & PHY_MODE_SPECIAL) { - schedule_timeout_uninterruptible(HZ); - return 0; - } - - for (j = 0; j < 10; ++j) { - msleep(100); - - /* Check DSP has asserted AFE power line */ - rc = i2c_smbus_read_byte_data(ioexp_client, P1_IN); - if (rc < 0) - goto fail_on; - if (rc & (1 << P1_AFE_PWD_LBN)) - return 0; - } - } - - EFX_INFO(efx, "timed out waiting for DSP boot\n"); - rc = -ETIMEDOUT; -fail_on: - sfe4001_poweroff(efx); - return rc; -} - -static int sfn4111t_reset(struct efx_nic *efx) -{ - efx_oword_t reg; - - /* GPIO 3 and the GPIO register are shared with I2C, so block that */ - i2c_lock_adapter(&efx->i2c_adap); - - /* Pull RST_N (GPIO 2) low then let it up again, setting the - * FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the - * output enables; the output levels should always be 0 (low) - * and we rely on external pull-ups. */ - falcon_read(efx, ®, GPIO_CTL_REG_KER); - EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, true); - falcon_write(efx, ®, GPIO_CTL_REG_KER); - msleep(1000); - EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, false); - EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, - !!(efx->phy_mode & PHY_MODE_SPECIAL)); - falcon_write(efx, ®, GPIO_CTL_REG_KER); - msleep(1); - - i2c_unlock_adapter(&efx->i2c_adap); - - ssleep(1); - return 0; -} - -static ssize_t show_phy_flash_cfg(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); - return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL)); -} - -static ssize_t set_phy_flash_cfg(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); - enum efx_phy_mode old_mode, new_mode; - int err; - - rtnl_lock(); - old_mode = efx->phy_mode; - if (count == 0 || *buf == '0') - new_mode = old_mode & ~PHY_MODE_SPECIAL; - else - new_mode = PHY_MODE_SPECIAL; - if (old_mode == new_mode) { - err = 0; - } else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) { - err = -EBUSY; - } else { - /* Reset the PHY, reconfigure the MAC and enable/disable - * MAC stats accordingly. */ - efx->phy_mode = new_mode; - if (new_mode & PHY_MODE_SPECIAL) - efx_stats_disable(efx); - if (efx->board_info.type == EFX_BOARD_SFE4001) - err = sfe4001_poweron(efx); - else - err = sfn4111t_reset(efx); - efx_reconfigure_port(efx); - if (!(new_mode & PHY_MODE_SPECIAL)) - efx_stats_enable(efx); - } - rtnl_unlock(); - - return err ? err : count; -} - -static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg); - -static void sfe4001_fini(struct efx_nic *efx) -{ - EFX_INFO(efx, "%s\n", __func__); - - device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); - sfe4001_poweroff(efx); - i2c_unregister_device(efx->board_info.ioexp_client); - i2c_unregister_device(efx->board_info.hwmon_client); -} - -static int sfe4001_check_hw(struct efx_nic *efx) -{ - s32 status; - - /* If XAUI link is up then do not monitor */ - if (EFX_WORKAROUND_7884(efx) && efx->mac_up) - return 0; - - /* Check the powered status of the PHY. Lack of power implies that - * the MAX6647 has shut down power to it, probably due to a temp. - * alarm. Reading the power status rather than the MAX6647 status - * directly because the later is read-to-clear and would thus - * start to power up the PHY again when polled, causing us to blip - * the power undesirably. - * We know we can read from the IO expander because we did - * it during power-on. Assume failure now is bad news. */ - status = i2c_smbus_read_byte_data(efx->board_info.ioexp_client, P1_IN); - if (status >= 0 && - (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0) - return 0; - - /* Use board power control, not PHY power control */ - sfe4001_poweroff(efx); - efx->phy_mode = PHY_MODE_OFF; - - return (status < 0) ? -EIO : -ERANGE; -} - -static struct i2c_board_info sfe4001_hwmon_info = { - I2C_BOARD_INFO("max6647", 0x4e), -}; - -/* This board uses an I2C expander to provider power to the PHY, which needs to - * be turned on before the PHY can be used. - * Context: Process context, rtnl lock held - */ -int sfe4001_init(struct efx_nic *efx) -{ - int rc; - -#if defined(CONFIG_SENSORS_LM90) || defined(CONFIG_SENSORS_LM90_MODULE) - efx->board_info.hwmon_client = - i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info); -#else - efx->board_info.hwmon_client = - i2c_new_dummy(&efx->i2c_adap, sfe4001_hwmon_info.addr); -#endif - if (!efx->board_info.hwmon_client) - return -EIO; - - /* Raise board/PHY high limit from 85 to 90 degrees Celsius */ - rc = i2c_smbus_write_byte_data(efx->board_info.hwmon_client, - MAX664X_REG_WLHO, 90); - if (rc) - goto fail_hwmon; - - efx->board_info.ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539); - if (!efx->board_info.ioexp_client) { - rc = -EIO; - goto fail_hwmon; - } - - /* 10Xpress has fixed-function LED pins, so there is no board-specific - * blink code. */ - efx->board_info.blink = tenxpress_phy_blink; - - efx->board_info.monitor = sfe4001_check_hw; - efx->board_info.fini = sfe4001_fini; - - if (efx->phy_mode & PHY_MODE_SPECIAL) { - /* PHY won't generate a 156.25 MHz clock and MAC stats fetch - * will fail. */ - efx_stats_disable(efx); - } - rc = sfe4001_poweron(efx); - if (rc) - goto fail_ioexp; - - rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); - if (rc) - goto fail_on; - - EFX_INFO(efx, "PHY is powered on\n"); - return 0; - -fail_on: - sfe4001_poweroff(efx); -fail_ioexp: - i2c_unregister_device(efx->board_info.ioexp_client); -fail_hwmon: - i2c_unregister_device(efx->board_info.hwmon_client); - return rc; -} - -static int sfn4111t_check_hw(struct efx_nic *efx) -{ - s32 status; - - /* If XAUI link is up then do not monitor */ - if (EFX_WORKAROUND_7884(efx) && efx->mac_up) - return 0; - - /* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */ - status = i2c_smbus_read_byte_data(efx->board_info.hwmon_client, - MAX664X_REG_RSL); - if (status < 0) - return -EIO; - if (status & 0x57) - return -ERANGE; - return 0; -} - -static void sfn4111t_fini(struct efx_nic *efx) -{ - EFX_INFO(efx, "%s\n", __func__); - - device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); - i2c_unregister_device(efx->board_info.hwmon_client); -} - -static struct i2c_board_info sfn4111t_a0_hwmon_info = { - I2C_BOARD_INFO("max6647", 0x4e), -}; - -static struct i2c_board_info sfn4111t_r5_hwmon_info = { - I2C_BOARD_INFO("max6646", 0x4d), -}; - -int sfn4111t_init(struct efx_nic *efx) -{ - int i = 0; - int rc; - - efx->board_info.hwmon_client = - i2c_new_device(&efx->i2c_adap, - (efx->board_info.minor < 5) ? - &sfn4111t_a0_hwmon_info : - &sfn4111t_r5_hwmon_info); - if (!efx->board_info.hwmon_client) - return -EIO; - - efx->board_info.blink = tenxpress_phy_blink; - efx->board_info.monitor = sfn4111t_check_hw; - efx->board_info.fini = sfn4111t_fini; - - rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); - if (rc) - goto fail_hwmon; - - do { - if (efx->phy_mode & PHY_MODE_SPECIAL) { - /* PHY may not generate a 156.25 MHz clock and MAC - * stats fetch will fail. */ - efx_stats_disable(efx); - sfn4111t_reset(efx); - } - rc = sft9001_wait_boot(efx); - if (rc == 0) - return 0; - efx->phy_mode = PHY_MODE_SPECIAL; - } while (rc == -EINVAL && ++i < 2); - - device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); -fail_hwmon: - i2c_unregister_device(efx->board_info.hwmon_client); - return rc; -} diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c new file mode 100644 index 000000000000..de07a4f031b2 --- /dev/null +++ b/drivers/net/sfc/siena.c @@ -0,0 +1,604 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/module.h> +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "mac.h" +#include "spi.h" +#include "regs.h" +#include "io.h" +#include "phy.h" +#include "workarounds.h" +#include "mcdi.h" +#include "mcdi_pcol.h" + +/* Hardware control for SFC9000 family including SFL9021 (aka Siena). */ + +static void siena_init_wol(struct efx_nic *efx); + + +static void siena_push_irq_moderation(struct efx_channel *channel) +{ + efx_dword_t timer_cmd; + + if (channel->irq_moderation) + EFX_POPULATE_DWORD_2(timer_cmd, + FRF_CZ_TC_TIMER_MODE, + FFE_CZ_TIMER_MODE_INT_HLDOFF, + FRF_CZ_TC_TIMER_VAL, + channel->irq_moderation - 1); + else + EFX_POPULATE_DWORD_2(timer_cmd, + FRF_CZ_TC_TIMER_MODE, + FFE_CZ_TIMER_MODE_DIS, + FRF_CZ_TC_TIMER_VAL, 0); + efx_writed_page_locked(channel->efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, + channel->channel); +} + +static void siena_push_multicast_hash(struct efx_nic *efx) +{ + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + efx_mcdi_rpc(efx, MC_CMD_SET_MCAST_HASH, + efx->multicast_hash.byte, sizeof(efx->multicast_hash), + NULL, 0, NULL); +} + +static int siena_mdio_write(struct net_device *net_dev, + int prtad, int devad, u16 addr, u16 value) +{ + struct efx_nic *efx = netdev_priv(net_dev); + uint32_t status; + int rc; + + rc = efx_mcdi_mdio_write(efx, efx->mdio_bus, prtad, devad, + addr, value, &status); + if (rc) + return rc; + if (status != MC_CMD_MDIO_STATUS_GOOD) + return -EIO; + + return 0; +} + +static int siena_mdio_read(struct net_device *net_dev, + int prtad, int devad, u16 addr) +{ + struct efx_nic *efx = netdev_priv(net_dev); + uint16_t value; + uint32_t status; + int rc; + + rc = efx_mcdi_mdio_read(efx, efx->mdio_bus, prtad, devad, + addr, &value, &status); + if (rc) + return rc; + if (status != MC_CMD_MDIO_STATUS_GOOD) + return -EIO; + + return (int)value; +} + +/* This call is responsible for hooking in the MAC and PHY operations */ +static int siena_probe_port(struct efx_nic *efx) +{ + int rc; + + /* Hook in PHY operations table */ + efx->phy_op = &efx_mcdi_phy_ops; + + /* Set up MDIO structure for PHY */ + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->mdio.mdio_read = siena_mdio_read; + efx->mdio.mdio_write = siena_mdio_write; + + /* Fill out MDIO structure and loopback modes */ + rc = efx->phy_op->probe(efx); + if (rc != 0) + return rc; + + /* Initial assumption */ + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; + + /* Allocate buffer for stats */ + rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer, + MC_CMD_MAC_NSTATS * sizeof(u64)); + if (rc) + return rc; + EFX_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n", + (u64)efx->stats_buffer.dma_addr, + efx->stats_buffer.addr, + (u64)virt_to_phys(efx->stats_buffer.addr)); + + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 1); + + return 0; +} + +void siena_remove_port(struct efx_nic *efx) +{ + efx_nic_free_buffer(efx, &efx->stats_buffer); +} + +static const struct efx_nic_register_test siena_register_tests[] = { + { FR_AZ_ADR_REGION, + EFX_OWORD32(0x0001FFFF, 0x0001FFFF, 0x0001FFFF, 0x0001FFFF) }, + { FR_CZ_USR_EV_CFG, + EFX_OWORD32(0x000103FF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_CFG, + EFX_OWORD32(0xFFFFFFFE, 0xFFFFFFFF, 0x0003FFFF, 0x00000000) }, + { FR_AZ_TX_CFG, + EFX_OWORD32(0x7FFF0037, 0xFFFF8000, 0xFFFFFFFF, 0x03FFFFFF) }, + { FR_AZ_TX_RESERVED, + EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) }, + { FR_AZ_SRM_TX_DC_CFG, + EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_CFG, + EFX_OWORD32(0x00000003, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_PF_WM, + EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_BZ_DP_CTRL, + EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_BZ_RX_RSS_TKEY, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG1, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG2, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG3, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000) }, +}; + +static int siena_test_registers(struct efx_nic *efx) +{ + return efx_nic_test_registers(efx, siena_register_tests, + ARRAY_SIZE(siena_register_tests)); +} + +/************************************************************************** + * + * Device reset + * + ************************************************************************** + */ + +static int siena_reset_hw(struct efx_nic *efx, enum reset_type method) +{ + + if (method == RESET_TYPE_WORLD) + return efx_mcdi_reset_mc(efx); + else + return efx_mcdi_reset_port(efx); +} + +static int siena_probe_nvconfig(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_get_board_cfg(efx, efx->mac_address, NULL); + if (rc) + return rc; + + return 0; +} + +static int siena_probe_nic(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data; + bool already_attached = 0; + int rc; + + /* Allocate storage for hardware specific data */ + nic_data = kzalloc(sizeof(struct siena_nic_data), GFP_KERNEL); + if (!nic_data) + return -ENOMEM; + efx->nic_data = nic_data; + + if (efx_nic_fpga_ver(efx) != 0) { + EFX_ERR(efx, "Siena FPGA not supported\n"); + rc = -ENODEV; + goto fail1; + } + + efx_mcdi_init(efx); + + /* Recover from a failed assertion before probing */ + rc = efx_mcdi_handle_assertion(efx); + if (rc) + goto fail1; + + rc = efx_mcdi_fwver(efx, &nic_data->fw_version, &nic_data->fw_build); + if (rc) { + EFX_ERR(efx, "Failed to read MCPU firmware version - " + "rc %d\n", rc); + goto fail1; /* MCPU absent? */ + } + + /* Let the BMC know that the driver is now in charge of link and + * filter settings. We must do this before we reset the NIC */ + rc = efx_mcdi_drv_attach(efx, true, &already_attached); + if (rc) { + EFX_ERR(efx, "Unable to register driver with MCPU\n"); + goto fail2; + } + if (already_attached) + /* Not a fatal error */ + EFX_ERR(efx, "Host already registered with MCPU\n"); + + /* Now we can reset the NIC */ + rc = siena_reset_hw(efx, RESET_TYPE_ALL); + if (rc) { + EFX_ERR(efx, "failed to reset NIC\n"); + goto fail3; + } + + siena_init_wol(efx); + + /* Allocate memory for INT_KER */ + rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); + if (rc) + goto fail4; + BUG_ON(efx->irq_status.dma_addr & 0x0f); + + EFX_LOG(efx, "INT_KER at %llx (virt %p phys %llx)\n", + (unsigned long long)efx->irq_status.dma_addr, + efx->irq_status.addr, + (unsigned long long)virt_to_phys(efx->irq_status.addr)); + + /* Read in the non-volatile configuration */ + rc = siena_probe_nvconfig(efx); + if (rc == -EINVAL) { + EFX_ERR(efx, "NVRAM is invalid therefore using defaults\n"); + efx->phy_type = PHY_TYPE_NONE; + efx->mdio.prtad = MDIO_PRTAD_NONE; + } else if (rc) { + goto fail5; + } + + return 0; + +fail5: + efx_nic_free_buffer(efx, &efx->irq_status); +fail4: +fail3: + efx_mcdi_drv_attach(efx, false, NULL); +fail2: +fail1: + kfree(efx->nic_data); + return rc; +} + +/* This call performs hardware-specific global initialisation, such as + * defining the descriptor cache sizes and number of RSS channels. + * It does not set up any buffers, descriptor rings or event queues. + */ +static int siena_init_nic(struct efx_nic *efx) +{ + efx_oword_t temp; + int rc; + + /* Recover from a failed assertion post-reset */ + rc = efx_mcdi_handle_assertion(efx); + if (rc) + return rc; + + /* Squash TX of packets of 16 bytes or less */ + efx_reado(efx, &temp, FR_AZ_TX_RESERVED); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); + efx_writeo(efx, &temp, FR_AZ_TX_RESERVED); + + /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 + * descriptors (which is bad). + */ + efx_reado(efx, &temp, FR_AZ_TX_CFG); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_CZ_TX_FILTER_EN_BIT, 1); + efx_writeo(efx, &temp, FR_AZ_TX_CFG); + + efx_reado(efx, &temp, FR_AZ_RX_CFG); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_DESC_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_INGR_EN, 1); + efx_writeo(efx, &temp, FR_AZ_RX_CFG); + + if (efx_nic_rx_xoff_thresh >= 0 || efx_nic_rx_xon_thresh >= 0) + /* No MCDI operation has been defined to set thresholds */ + EFX_ERR(efx, "ignoring RX flow control thresholds\n"); + + /* Enable event logging */ + rc = efx_mcdi_log_ctrl(efx, true, false, 0); + if (rc) + return rc; + + /* Set destination of both TX and RX Flush events */ + EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); + efx_writeo(efx, &temp, FR_BZ_DP_CTRL); + + EFX_POPULATE_OWORD_1(temp, FRF_CZ_USREV_DIS, 1); + efx_writeo(efx, &temp, FR_CZ_USR_EV_CFG); + + efx_nic_init_common(efx); + return 0; +} + +static void siena_remove_nic(struct efx_nic *efx) +{ + efx_nic_free_buffer(efx, &efx->irq_status); + + siena_reset_hw(efx, RESET_TYPE_ALL); + + /* Relinquish the device back to the BMC */ + if (efx_nic_has_mc(efx)) + efx_mcdi_drv_attach(efx, false, NULL); + + /* Tear down the private nic state */ + kfree(efx->nic_data); + efx->nic_data = NULL; +} + +#define STATS_GENERATION_INVALID ((u64)(-1)) + +static int siena_try_update_nic_stats(struct efx_nic *efx) +{ + u64 *dma_stats; + struct efx_mac_stats *mac_stats; + u64 generation_start; + u64 generation_end; + + mac_stats = &efx->mac_stats; + dma_stats = (u64 *)efx->stats_buffer.addr; + + generation_end = dma_stats[MC_CMD_MAC_GENERATION_END]; + if (generation_end == STATS_GENERATION_INVALID) + return 0; + rmb(); + +#define MAC_STAT(M, D) \ + mac_stats->M = dma_stats[MC_CMD_MAC_ ## D] + + MAC_STAT(tx_bytes, TX_BYTES); + MAC_STAT(tx_bad_bytes, TX_BAD_BYTES); + mac_stats->tx_good_bytes = (mac_stats->tx_bytes - + mac_stats->tx_bad_bytes); + MAC_STAT(tx_packets, TX_PKTS); + MAC_STAT(tx_bad, TX_BAD_FCS_PKTS); + MAC_STAT(tx_pause, TX_PAUSE_PKTS); + MAC_STAT(tx_control, TX_CONTROL_PKTS); + MAC_STAT(tx_unicast, TX_UNICAST_PKTS); + MAC_STAT(tx_multicast, TX_MULTICAST_PKTS); + MAC_STAT(tx_broadcast, TX_BROADCAST_PKTS); + MAC_STAT(tx_lt64, TX_LT64_PKTS); + MAC_STAT(tx_64, TX_64_PKTS); + MAC_STAT(tx_65_to_127, TX_65_TO_127_PKTS); + MAC_STAT(tx_128_to_255, TX_128_TO_255_PKTS); + MAC_STAT(tx_256_to_511, TX_256_TO_511_PKTS); + MAC_STAT(tx_512_to_1023, TX_512_TO_1023_PKTS); + MAC_STAT(tx_1024_to_15xx, TX_1024_TO_15XX_PKTS); + MAC_STAT(tx_15xx_to_jumbo, TX_15XX_TO_JUMBO_PKTS); + MAC_STAT(tx_gtjumbo, TX_GTJUMBO_PKTS); + mac_stats->tx_collision = 0; + MAC_STAT(tx_single_collision, TX_SINGLE_COLLISION_PKTS); + MAC_STAT(tx_multiple_collision, TX_MULTIPLE_COLLISION_PKTS); + MAC_STAT(tx_excessive_collision, TX_EXCESSIVE_COLLISION_PKTS); + MAC_STAT(tx_deferred, TX_DEFERRED_PKTS); + MAC_STAT(tx_late_collision, TX_LATE_COLLISION_PKTS); + mac_stats->tx_collision = (mac_stats->tx_single_collision + + mac_stats->tx_multiple_collision + + mac_stats->tx_excessive_collision + + mac_stats->tx_late_collision); + MAC_STAT(tx_excessive_deferred, TX_EXCESSIVE_DEFERRED_PKTS); + MAC_STAT(tx_non_tcpudp, TX_NON_TCPUDP_PKTS); + MAC_STAT(tx_mac_src_error, TX_MAC_SRC_ERR_PKTS); + MAC_STAT(tx_ip_src_error, TX_IP_SRC_ERR_PKTS); + MAC_STAT(rx_bytes, RX_BYTES); + MAC_STAT(rx_bad_bytes, RX_BAD_BYTES); + mac_stats->rx_good_bytes = (mac_stats->rx_bytes - + mac_stats->rx_bad_bytes); + MAC_STAT(rx_packets, RX_PKTS); + MAC_STAT(rx_good, RX_GOOD_PKTS); + mac_stats->rx_bad = mac_stats->rx_packets - mac_stats->rx_good; + MAC_STAT(rx_pause, RX_PAUSE_PKTS); + MAC_STAT(rx_control, RX_CONTROL_PKTS); + MAC_STAT(rx_unicast, RX_UNICAST_PKTS); + MAC_STAT(rx_multicast, RX_MULTICAST_PKTS); + MAC_STAT(rx_broadcast, RX_BROADCAST_PKTS); + MAC_STAT(rx_lt64, RX_UNDERSIZE_PKTS); + MAC_STAT(rx_64, RX_64_PKTS); + MAC_STAT(rx_65_to_127, RX_65_TO_127_PKTS); + MAC_STAT(rx_128_to_255, RX_128_TO_255_PKTS); + MAC_STAT(rx_256_to_511, RX_256_TO_511_PKTS); + MAC_STAT(rx_512_to_1023, RX_512_TO_1023_PKTS); + MAC_STAT(rx_1024_to_15xx, RX_1024_TO_15XX_PKTS); + MAC_STAT(rx_15xx_to_jumbo, RX_15XX_TO_JUMBO_PKTS); + MAC_STAT(rx_gtjumbo, RX_GTJUMBO_PKTS); + mac_stats->rx_bad_lt64 = 0; + mac_stats->rx_bad_64_to_15xx = 0; + mac_stats->rx_bad_15xx_to_jumbo = 0; + MAC_STAT(rx_bad_gtjumbo, RX_JABBER_PKTS); + MAC_STAT(rx_overflow, RX_OVERFLOW_PKTS); + mac_stats->rx_missed = 0; + MAC_STAT(rx_false_carrier, RX_FALSE_CARRIER_PKTS); + MAC_STAT(rx_symbol_error, RX_SYMBOL_ERROR_PKTS); + MAC_STAT(rx_align_error, RX_ALIGN_ERROR_PKTS); + MAC_STAT(rx_length_error, RX_LENGTH_ERROR_PKTS); + MAC_STAT(rx_internal_error, RX_INTERNAL_ERROR_PKTS); + mac_stats->rx_good_lt64 = 0; + + efx->n_rx_nodesc_drop_cnt = dma_stats[MC_CMD_MAC_RX_NODESC_DROPS]; + +#undef MAC_STAT + + rmb(); + generation_start = dma_stats[MC_CMD_MAC_GENERATION_START]; + if (generation_end != generation_start) + return -EAGAIN; + + return 0; +} + +static void siena_update_nic_stats(struct efx_nic *efx) +{ + while (siena_try_update_nic_stats(efx) == -EAGAIN) + cpu_relax(); +} + +static void siena_start_nic_stats(struct efx_nic *efx) +{ + u64 *dma_stats = (u64 *)efx->stats_buffer.addr; + + dma_stats[MC_CMD_MAC_GENERATION_END] = STATS_GENERATION_INVALID; + + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, + MC_CMD_MAC_NSTATS * sizeof(u64), 1, 0); +} + +static void siena_stop_nic_stats(struct efx_nic *efx) +{ + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 0); +} + +void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len) +{ + struct siena_nic_data *nic_data = efx->nic_data; + snprintf(buf, len, "%u.%u.%u.%u", + (unsigned int)(nic_data->fw_version >> 48), + (unsigned int)(nic_data->fw_version >> 32 & 0xffff), + (unsigned int)(nic_data->fw_version >> 16 & 0xffff), + (unsigned int)(nic_data->fw_version & 0xffff)); +} + +/************************************************************************** + * + * Wake on LAN + * + ************************************************************************** + */ + +static void siena_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol) +{ + struct siena_nic_data *nic_data = efx->nic_data; + + wol->supported = WAKE_MAGIC; + if (nic_data->wol_filter_id != -1) + wol->wolopts = WAKE_MAGIC; + else + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + + +static int siena_set_wol(struct efx_nic *efx, u32 type) +{ + struct siena_nic_data *nic_data = efx->nic_data; + int rc; + + if (type & ~WAKE_MAGIC) + return -EINVAL; + + if (type & WAKE_MAGIC) { + if (nic_data->wol_filter_id != -1) + efx_mcdi_wol_filter_remove(efx, + nic_data->wol_filter_id); + rc = efx_mcdi_wol_filter_set_magic(efx, efx->mac_address, + &nic_data->wol_filter_id); + if (rc) + goto fail; + + pci_wake_from_d3(efx->pci_dev, true); + } else { + rc = efx_mcdi_wol_filter_reset(efx); + nic_data->wol_filter_id = -1; + pci_wake_from_d3(efx->pci_dev, false); + if (rc) + goto fail; + } + + return 0; + fail: + EFX_ERR(efx, "%s failed: type=%d rc=%d\n", __func__, type, rc); + return rc; +} + + +static void siena_init_wol(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data = efx->nic_data; + int rc; + + rc = efx_mcdi_wol_filter_get_magic(efx, &nic_data->wol_filter_id); + + if (rc != 0) { + /* If it failed, attempt to get into a synchronised + * state with MC by resetting any set WoL filters */ + efx_mcdi_wol_filter_reset(efx); + nic_data->wol_filter_id = -1; + } else if (nic_data->wol_filter_id != -1) { + pci_wake_from_d3(efx->pci_dev, true); + } +} + + +/************************************************************************** + * + * Revision-dependent attributes used by efx.c and nic.c + * + ************************************************************************** + */ + +struct efx_nic_type siena_a0_nic_type = { + .probe = siena_probe_nic, + .remove = siena_remove_nic, + .init = siena_init_nic, + .fini = efx_port_dummy_op_void, + .monitor = NULL, + .reset = siena_reset_hw, + .probe_port = siena_probe_port, + .remove_port = siena_remove_port, + .prepare_flush = efx_port_dummy_op_void, + .update_stats = siena_update_nic_stats, + .start_stats = siena_start_nic_stats, + .stop_stats = siena_stop_nic_stats, + .set_id_led = efx_mcdi_set_id_led, + .push_irq_moderation = siena_push_irq_moderation, + .push_multicast_hash = siena_push_multicast_hash, + .reconfigure_port = efx_mcdi_phy_reconfigure, + .get_wol = siena_get_wol, + .set_wol = siena_set_wol, + .resume_wol = siena_init_wol, + .test_registers = siena_test_registers, + .default_mac_ops = &efx_mcdi_mac_operations, + + .revision = EFX_REV_SIENA_A0, + .mem_map_size = (FR_CZ_MC_TREG_SMEM + + FR_CZ_MC_TREG_SMEM_STEP * FR_CZ_MC_TREG_SMEM_ROWS), + .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL, + .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL, + .buf_tbl_base = FR_BZ_BUF_FULL_TBL, + .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL, + .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), + .rx_buffer_padding = 0, + .max_interrupt_mode = EFX_INT_MODE_MSIX, + .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy + * interrupt handler only supports 32 + * channels */ + .tx_dc_base = 0x88000, + .rx_dc_base = 0x68000, + .offload_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM, + .reset_world_flags = ETH_RESET_MGMT << ETH_RESET_SHARED_SHIFT, +}; diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h index 1b1ceb411671..8bf4fce0813a 100644 --- a/drivers/net/sfc/spi.h +++ b/drivers/net/sfc/spi.h @@ -36,8 +36,6 @@ /** * struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device - * @efx: The Efx controller that owns this device - * @mtd: MTD state * @device_id: Controller's id for the device * @size: Size (in bytes) * @addr_len: Number of address bytes in read/write commands @@ -54,10 +52,6 @@ * Write commands are limited to blocks with this size and alignment. */ struct efx_spi_device { - struct efx_nic *efx; -#ifdef CONFIG_SFC_MTD - void *mtd; -#endif int device_id; unsigned int size; unsigned int addr_len; @@ -67,12 +61,16 @@ struct efx_spi_device { unsigned int block_size; }; -int falcon_spi_cmd(const struct efx_spi_device *spi, unsigned int command, +int falcon_spi_cmd(struct efx_nic *efx, + const struct efx_spi_device *spi, unsigned int command, int address, const void* in, void *out, size_t len); -int falcon_spi_wait_write(const struct efx_spi_device *spi); -int falcon_spi_read(const struct efx_spi_device *spi, loff_t start, +int falcon_spi_wait_write(struct efx_nic *efx, + const struct efx_spi_device *spi); +int falcon_spi_read(struct efx_nic *efx, + const struct efx_spi_device *spi, loff_t start, size_t len, size_t *retlen, u8 *buffer); -int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, +int falcon_spi_write(struct efx_nic *efx, + const struct efx_spi_device *spi, loff_t start, size_t len, size_t *retlen, const u8 *buffer); /* diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index f4d509015f75..ca11572a49a9 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. + * Copyright 2007-2009 Solarflare Communications Inc. * * 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 @@ -12,10 +12,9 @@ #include <linux/seq_file.h> #include "efx.h" #include "mdio_10g.h" -#include "falcon.h" +#include "nic.h" #include "phy.h" -#include "falcon_hwdefs.h" -#include "boards.h" +#include "regs.h" #include "workarounds.h" #include "selftest.h" @@ -31,13 +30,13 @@ #define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ (1 << LOOPBACK_PCS) | \ (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) + (1 << LOOPBACK_PHYXS_WS)) #define SFT9001_LOOPBACKS ((1 << LOOPBACK_GPHY) | \ (1 << LOOPBACK_PHYXS) | \ (1 << LOOPBACK_PCS) | \ (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) + (1 << LOOPBACK_PHYXS_WS)) /* We complain if we fail to see the link partner as 10G capable this many * times in a row (must be > 1 as sampling the autoneg. registers is racy) @@ -84,9 +83,9 @@ #define PMA_PMD_LED_FLASH (3) #define PMA_PMD_LED_MASK 3 /* All LEDs under hardware control */ -#define PMA_PMD_LED_FULL_AUTO (0) +#define SFT9001_PMA_PMD_LED_DEFAULT 0 /* Green and Amber under hardware control, Red off */ -#define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) +#define SFX7101_PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) #define PMA_PMD_SPEED_ENABLE_REG 49192 #define PMA_PMD_100TX_ADV_LBN 1 @@ -200,15 +199,16 @@ static ssize_t set_phy_short_reach(struct device *dev, const char *buf, size_t count) { struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + int rc; rtnl_lock(); efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR, MDIO_PMA_10GBT_TXPWR_SHORT, count != 0 && *buf != '0'); - efx_reconfigure_port(efx); + rc = efx_reconfigure_port(efx); rtnl_unlock(); - return count; + return rc < 0 ? rc : (ssize_t)count; } static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach, @@ -292,17 +292,36 @@ static int tenxpress_init(struct efx_nic *efx) efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG, 1 << PMA_PMA_LED_ACTIVITY_LBN, true); efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, - PMA_PMD_LED_DEFAULT); + SFX7101_PMA_PMD_LED_DEFAULT); } return 0; } +static int sfx7101_phy_probe(struct efx_nic *efx) +{ + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->loopback_modes = SFX7101_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + return 0; +} + +static int sft9001_phy_probe(struct efx_nic *efx) +{ + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->loopback_modes = (SFT9001_LOOPBACKS | FALCON_XMAC_LOOPBACKS | + FALCON_GMAC_LOOPBACKS); + return 0; +} + static int tenxpress_phy_init(struct efx_nic *efx) { struct tenxpress_phy_data *phy_data; int rc = 0; + falcon_board(efx)->type->init_phy(efx); + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); if (!phy_data) return -ENOMEM; @@ -333,6 +352,15 @@ static int tenxpress_phy_init(struct efx_nic *efx) if (rc < 0) goto fail; + /* Initialise advertising flags */ + efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full); + if (efx->phy_type != PHY_TYPE_SFX7101) + efx->link_advertising |= (ADVERTISED_1000baseT_Full | + ADVERTISED_100baseT_Full); + efx_link_set_wanted_fc(efx, efx->wanted_fc); + efx_mdio_an_reconfigure(efx); + if (efx->phy_type == PHY_TYPE_SFT9001B) { rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_short_reach); @@ -363,7 +391,7 @@ static int tenxpress_special_reset(struct efx_nic *efx) /* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so * a special software reset can glitch the XGMAC sufficiently for stats * requests to fail. */ - efx_stats_disable(efx); + falcon_stop_nic_stats(efx); /* Initiate reset */ reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG); @@ -385,7 +413,7 @@ static int tenxpress_special_reset(struct efx_nic *efx) /* Wait for the XGXS state machine to churn */ mdelay(10); out: - efx_stats_enable(efx); + falcon_start_nic_stats(efx); return rc; } @@ -489,95 +517,76 @@ static void tenxpress_low_power(struct efx_nic *efx) !!(efx->phy_mode & PHY_MODE_LOW_POWER)); } -static void tenxpress_phy_reconfigure(struct efx_nic *efx) +static int tenxpress_phy_reconfigure(struct efx_nic *efx) { struct tenxpress_phy_data *phy_data = efx->phy_data; - struct ethtool_cmd ecmd; bool phy_mode_change, loop_reset; if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) { phy_data->phy_mode = efx->phy_mode; - return; + return 0; } - tenxpress_low_power(efx); - phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL && phy_data->phy_mode != PHY_MODE_NORMAL); - loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, efx->phy_op->loopbacks) || + loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, LOOPBACKS_EXTERNAL(efx)) || LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY)); if (loop_reset || phy_mode_change) { - int rc; - - efx->phy_op->get_settings(efx, &ecmd); - - if (loop_reset || phy_mode_change) { - tenxpress_special_reset(efx); - - /* Reset XAUI if we were in 10G, and are staying - * in 10G. If we're moving into and out of 10G - * then xaui will be reset anyway */ - if (EFX_IS10G(efx)) - falcon_reset_xaui(efx); - } + tenxpress_special_reset(efx); - rc = efx->phy_op->set_settings(efx, &ecmd); - WARN_ON(rc); + /* Reset XAUI if we were in 10G, and are staying + * in 10G. If we're moving into and out of 10G + * then xaui will be reset anyway */ + if (EFX_IS10G(efx)) + falcon_reset_xaui(efx); } + tenxpress_low_power(efx); efx_mdio_transmit_disable(efx); efx_mdio_phy_reconfigure(efx); tenxpress_ext_loopback(efx); + efx_mdio_an_reconfigure(efx); phy_data->loopback_mode = efx->loopback_mode; phy_data->phy_mode = efx->phy_mode; - if (efx->phy_type == PHY_TYPE_SFX7101) { - efx->link_speed = 10000; - efx->link_fd = true; - efx->link_up = sfx7101_link_ok(efx); - } else { - efx->phy_op->get_settings(efx, &ecmd); - efx->link_speed = ecmd.speed; - efx->link_fd = ecmd.duplex == DUPLEX_FULL; - efx->link_up = sft9001_link_ok(efx, &ecmd); - } - efx->link_fc = efx_mdio_get_pause(efx); + return 0; } -/* Poll PHY for interrupt */ -static void tenxpress_phy_poll(struct efx_nic *efx) +static void +tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); + +/* Poll for link state changes */ +static bool tenxpress_phy_poll(struct efx_nic *efx) { - struct tenxpress_phy_data *phy_data = efx->phy_data; - bool change = false; + struct efx_link_state old_state = efx->link_state; if (efx->phy_type == PHY_TYPE_SFX7101) { - bool link_ok = sfx7101_link_ok(efx); - if (link_ok != efx->link_up) { - change = true; - } else { - unsigned int link_fc = efx_mdio_get_pause(efx); - if (link_fc != efx->link_fc) - change = true; - } - sfx7101_check_bad_lp(efx, link_ok); - } else if (efx->loopback_mode) { - bool link_ok = sft9001_link_ok(efx, NULL); - if (link_ok != efx->link_up) - change = true; + efx->link_state.up = sfx7101_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx_mdio_get_pause(efx); + + sfx7101_check_bad_lp(efx, efx->link_state.up); } else { - int status = efx_mdio_read(efx, MDIO_MMD_PMAPMD, - MDIO_PMA_LASI_STAT); - if (status & MDIO_PMA_LASI_LSALARM) - change = true; - } + struct ethtool_cmd ecmd; - if (change) - falcon_sim_phy_event(efx); + /* Check the LASI alarm first */ + if (efx->loopback_mode == LOOPBACK_NONE && + !(efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT) & + MDIO_PMA_LASI_LSALARM)) + return false; - if (phy_data->phy_mode != PHY_MODE_NORMAL) - return; + tenxpress_get_settings(efx, &ecmd); + + efx->link_state.up = sft9001_link_ok(efx, &ecmd); + efx->link_state.speed = ecmd.speed; + efx->link_state.fd = (ecmd.duplex == DUPLEX_FULL); + efx->link_state.fc = efx_mdio_get_pause(efx); + } + + return !efx_link_state_equal(&efx->link_state, &old_state); } static void tenxpress_phy_fini(struct efx_nic *efx) @@ -604,18 +613,29 @@ static void tenxpress_phy_fini(struct efx_nic *efx) } -/* Set the RX and TX LEDs and Link LED flashing. The other LEDs - * (which probably aren't wired anyway) are left in AUTO mode */ -void tenxpress_phy_blink(struct efx_nic *efx, bool blink) +/* Override the RX, TX and link LEDs */ +void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) { int reg; - if (blink) - reg = (PMA_PMD_LED_FLASH << PMA_PMD_LED_TX_LBN) | - (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN) | - (PMA_PMD_LED_FLASH << PMA_PMD_LED_LINK_LBN); - else - reg = PMA_PMD_LED_DEFAULT; + switch (mode) { + case EFX_LED_OFF: + reg = (PMA_PMD_LED_OFF << PMA_PMD_LED_TX_LBN) | + (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) | + (PMA_PMD_LED_OFF << PMA_PMD_LED_LINK_LBN); + break; + case EFX_LED_ON: + reg = (PMA_PMD_LED_ON << PMA_PMD_LED_TX_LBN) | + (PMA_PMD_LED_ON << PMA_PMD_LED_RX_LBN) | + (PMA_PMD_LED_ON << PMA_PMD_LED_LINK_LBN); + break; + default: + if (efx->phy_type == PHY_TYPE_SFX7101) + reg = SFX7101_PMA_PMD_LED_DEFAULT; + else + reg = SFT9001_PMA_PMD_LED_DEFAULT; + break; + } efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, reg); } @@ -624,6 +644,13 @@ static const char *const sfx7101_test_names[] = { "bist" }; +static const char *sfx7101_test_name(struct efx_nic *efx, unsigned int index) +{ + if (index < ARRAY_SIZE(sfx7101_test_names)) + return sfx7101_test_names[index]; + return NULL; +} + static int sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags) { @@ -635,6 +662,9 @@ sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags) /* BIST is automatically run after a special software reset */ rc = tenxpress_special_reset(efx); results[0] = rc ? -1 : 1; + + efx_mdio_an_reconfigure(efx); + return rc; } @@ -650,14 +680,17 @@ static const char *const sft9001_test_names[] = { "cable.pairD.length", }; +static const char *sft9001_test_name(struct efx_nic *efx, unsigned int index) +{ + if (index < ARRAY_SIZE(sft9001_test_names)) + return sft9001_test_names[index]; + return NULL; +} + static int sft9001_run_tests(struct efx_nic *efx, int *results, unsigned flags) { - struct ethtool_cmd ecmd; int rc = 0, rc2, i, ctrl_reg, res_reg; - if (flags & ETH_TEST_FL_OFFLINE) - efx->phy_op->get_settings(efx, &ecmd); - /* Initialise cable diagnostic results to unknown failure */ for (i = 1; i < 9; ++i) results[i] = -1; @@ -709,9 +742,7 @@ out: if (!rc) rc = rc2; - rc2 = efx->phy_op->set_settings(efx, &ecmd); - if (!rc) - rc = rc2; + efx_mdio_an_reconfigure(efx); } return rc; @@ -758,7 +789,7 @@ tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) * but doesn't advertise the correct speed. So override it */ if (efx->loopback_mode == LOOPBACK_GPHY) ecmd->speed = SPEED_1000; - else if (LOOPBACK_MASK(efx) & efx->phy_op->loopbacks) + else if (LOOPBACK_EXTERNAL(efx)) ecmd->speed = SPEED_10000; } @@ -788,35 +819,27 @@ static void sft9001_set_npage_adv(struct efx_nic *efx, u32 advertising) } struct efx_phy_operations falcon_sfx7101_phy_ops = { - .macs = EFX_XMAC, + .probe = sfx7101_phy_probe, .init = tenxpress_phy_init, .reconfigure = tenxpress_phy_reconfigure, .poll = tenxpress_phy_poll, .fini = tenxpress_phy_fini, - .clear_interrupt = efx_port_dummy_op_void, .get_settings = tenxpress_get_settings, .set_settings = tenxpress_set_settings, .set_npage_adv = sfx7101_set_npage_adv, - .num_tests = ARRAY_SIZE(sfx7101_test_names), - .test_names = sfx7101_test_names, + .test_name = sfx7101_test_name, .run_tests = sfx7101_run_tests, - .mmds = TENXPRESS_REQUIRED_DEVS, - .loopbacks = SFX7101_LOOPBACKS, }; struct efx_phy_operations falcon_sft9001_phy_ops = { - .macs = EFX_GMAC | EFX_XMAC, + .probe = sft9001_phy_probe, .init = tenxpress_phy_init, .reconfigure = tenxpress_phy_reconfigure, .poll = tenxpress_phy_poll, .fini = tenxpress_phy_fini, - .clear_interrupt = efx_port_dummy_op_void, .get_settings = tenxpress_get_settings, .set_settings = tenxpress_set_settings, .set_npage_adv = sft9001_set_npage_adv, - .num_tests = ARRAY_SIZE(sft9001_test_names), - .test_names = sft9001_test_names, + .test_name = sft9001_test_name, .run_tests = sft9001_run_tests, - .mmds = TENXPRESS_REQUIRED_DEVS, - .loopbacks = SFT9001_LOOPBACKS, }; diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c index 489c4de31447..e669f94e821b 100644 --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2005-2008 Solarflare Communications Inc. + * Copyright 2005-2009 Solarflare Communications Inc. * * 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 @@ -12,12 +12,13 @@ #include <linux/tcp.h> #include <linux/ip.h> #include <linux/in.h> +#include <linux/ipv6.h> +#include <net/ipv6.h> #include <linux/if_ether.h> #include <linux/highmem.h> #include "net_driver.h" -#include "tx.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "workarounds.h" /* @@ -26,8 +27,7 @@ * The tx_queue descriptor ring fill-level must fall below this value * before we restart the netif queue */ -#define EFX_NETDEV_TX_THRESHOLD(_tx_queue) \ - (_tx_queue->efx->type->txd_ring_mask / 2u) +#define EFX_TXQ_THRESHOLD (EFX_TXQ_MASK / 2u) /* We want to be able to nest calls to netif_stop_queue(), since each * channel can have an individual stop on the queue. @@ -125,6 +125,24 @@ static void efx_tsoh_free(struct efx_tx_queue *tx_queue, } +static inline unsigned +efx_max_tx_len(struct efx_nic *efx, dma_addr_t dma_addr) +{ + /* Depending on the NIC revision, we can use descriptor + * lengths up to 8K or 8K-1. However, since PCI Express + * devices must split read requests at 4K boundaries, there is + * little benefit from using descriptors that cross those + * boundaries and we keep things simple by not doing so. + */ + unsigned len = (~dma_addr & 0xfff) + 1; + + /* Work around hardware bug for unaligned buffers. */ + if (EFX_WORKAROUND_5391(efx) && (dma_addr & 0xf)) + len = min_t(unsigned, len, 512 - (dma_addr & 0xf)); + + return len; +} + /* * Add a socket buffer to a TX queue * @@ -135,11 +153,13 @@ static void efx_tsoh_free(struct efx_tx_queue *tx_queue, * If any DMA mapping fails, any mapped fragments will be unmapped, * the queue's insert pointer will be restored to its original value. * + * This function is split out from efx_hard_start_xmit to allow the + * loopback test to direct packets via specific TX queues. + * * Returns NETDEV_TX_OK or NETDEV_TX_BUSY * You must hold netif_tx_lock() to call this function. */ -static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, - struct sk_buff *skb) +netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) { struct efx_nic *efx = tx_queue->efx; struct pci_dev *pci_dev = efx->pci_dev; @@ -147,7 +167,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, skb_frag_t *fragment; struct page *page; int page_offset; - unsigned int len, unmap_len = 0, fill_level, insert_ptr, misalign; + unsigned int len, unmap_len = 0, fill_level, insert_ptr; dma_addr_t dma_addr, unmap_addr = 0; unsigned int dma_len; bool unmap_single; @@ -156,7 +176,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); - if (skb_shinfo((struct sk_buff *)skb)->gso_size) + if (skb_shinfo(skb)->gso_size) return efx_enqueue_skb_tso(tx_queue, skb); /* Get size of the initial fragment */ @@ -171,7 +191,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, } fill_level = tx_queue->insert_count - tx_queue->old_read_count; - q_space = efx->type->txd_ring_mask - 1 - fill_level; + q_space = EFX_TXQ_MASK - 1 - fill_level; /* Map for DMA. Use pci_map_single rather than pci_map_page * since this is more efficient on machines with sparse @@ -208,16 +228,14 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, &tx_queue->read_count; fill_level = (tx_queue->insert_count - tx_queue->old_read_count); - q_space = (efx->type->txd_ring_mask - 1 - - fill_level); + q_space = EFX_TXQ_MASK - 1 - fill_level; if (unlikely(q_space-- <= 0)) goto stop; smp_mb(); --tx_queue->stopped; } - insert_ptr = (tx_queue->insert_count & - efx->type->txd_ring_mask); + insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK; buffer = &tx_queue->buffer[insert_ptr]; efx_tsoh_free(tx_queue, buffer); EFX_BUG_ON_PARANOID(buffer->tsoh); @@ -226,14 +244,10 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, EFX_BUG_ON_PARANOID(!buffer->continuation); EFX_BUG_ON_PARANOID(buffer->unmap_len); - dma_len = (((~dma_addr) & efx->type->tx_dma_mask) + 1); - if (likely(dma_len > len)) + dma_len = efx_max_tx_len(efx, dma_addr); + if (likely(dma_len >= len)) dma_len = len; - misalign = (unsigned)dma_addr & efx->type->bug5391_mask; - if (misalign && dma_len + misalign > 512) - dma_len = 512 - misalign; - /* Fill out per descriptor fields */ buffer->len = dma_len; buffer->dma_addr = dma_addr; @@ -266,7 +280,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, buffer->continuation = false; /* Pass off to hardware */ - falcon_push_buffers(tx_queue); + efx_nic_push_buffers(tx_queue); return NETDEV_TX_OK; @@ -276,7 +290,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, skb_shinfo(skb)->nr_frags + 1); /* Mark the packet as transmitted, and free the SKB ourselves */ - dev_kfree_skb_any((struct sk_buff *)skb); + dev_kfree_skb_any(skb); goto unwind; stop: @@ -289,7 +303,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, /* Work backwards until we hit the original insert pointer value */ while (tx_queue->insert_count != tx_queue->write_count) { --tx_queue->insert_count; - insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask; + insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK; buffer = &tx_queue->buffer[insert_ptr]; efx_dequeue_buffer(tx_queue, buffer); buffer->len = 0; @@ -318,10 +332,9 @@ static void efx_dequeue_buffers(struct efx_tx_queue *tx_queue, { struct efx_nic *efx = tx_queue->efx; unsigned int stop_index, read_ptr; - unsigned int mask = tx_queue->efx->type->txd_ring_mask; - stop_index = (index + 1) & mask; - read_ptr = tx_queue->read_count & mask; + stop_index = (index + 1) & EFX_TXQ_MASK; + read_ptr = tx_queue->read_count & EFX_TXQ_MASK; while (read_ptr != stop_index) { struct efx_tx_buffer *buffer = &tx_queue->buffer[read_ptr]; @@ -338,28 +351,10 @@ static void efx_dequeue_buffers(struct efx_tx_queue *tx_queue, buffer->len = 0; ++tx_queue->read_count; - read_ptr = tx_queue->read_count & mask; + read_ptr = tx_queue->read_count & EFX_TXQ_MASK; } } -/* Initiate a packet transmission on the specified TX queue. - * Note that returning anything other than NETDEV_TX_OK will cause the - * OS to free the skb. - * - * This function is split out from efx_hard_start_xmit to allow the - * loopback test to direct packets via specific TX queues. It is - * therefore a non-static inline, so as not to penalise performance - * for non-loopback transmissions. - * - * Context: netif_tx_lock held - */ -inline netdev_tx_t efx_xmit(struct efx_nic *efx, - struct efx_tx_queue *tx_queue, struct sk_buff *skb) -{ - /* Map fragments for DMA and add to TX queue */ - return efx_enqueue_skb(tx_queue, skb); -} - /* Initiate a packet transmission. We use one channel per CPU * (sharing when we have more CPUs than channels). On Falcon, the TX * completion events will be directed back to the CPU that transmitted @@ -383,7 +378,7 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, else tx_queue = &efx->tx_queue[EFX_TX_QUEUE_NO_CSUM]; - return efx_xmit(efx, tx_queue, skb); + return efx_enqueue_skb(tx_queue, skb); } void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) @@ -391,7 +386,7 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) unsigned fill_level; struct efx_nic *efx = tx_queue->efx; - EFX_BUG_ON_PARANOID(index > efx->type->txd_ring_mask); + EFX_BUG_ON_PARANOID(index > EFX_TXQ_MASK); efx_dequeue_buffers(tx_queue, index); @@ -401,7 +396,7 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) smp_mb(); if (unlikely(tx_queue->stopped) && likely(efx->port_enabled)) { fill_level = tx_queue->insert_count - tx_queue->read_count; - if (fill_level < EFX_NETDEV_TX_THRESHOLD(tx_queue)) { + if (fill_level < EFX_TXQ_THRESHOLD) { EFX_BUG_ON_PARANOID(!efx_dev_registered(efx)); /* Do this under netif_tx_lock(), to avoid racing @@ -425,15 +420,15 @@ int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) EFX_LOG(efx, "creating TX queue %d\n", tx_queue->queue); /* Allocate software ring */ - txq_size = (efx->type->txd_ring_mask + 1) * sizeof(*tx_queue->buffer); + txq_size = EFX_TXQ_SIZE * sizeof(*tx_queue->buffer); tx_queue->buffer = kzalloc(txq_size, GFP_KERNEL); if (!tx_queue->buffer) return -ENOMEM; - for (i = 0; i <= efx->type->txd_ring_mask; ++i) + for (i = 0; i <= EFX_TXQ_MASK; ++i) tx_queue->buffer[i].continuation = true; /* Allocate hardware ring */ - rc = falcon_probe_tx(tx_queue); + rc = efx_nic_probe_tx(tx_queue); if (rc) goto fail; @@ -456,7 +451,7 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue) BUG_ON(tx_queue->stopped); /* Set up TX descriptor ring */ - falcon_init_tx(tx_queue); + efx_nic_init_tx(tx_queue); } void efx_release_tx_buffers(struct efx_tx_queue *tx_queue) @@ -468,8 +463,7 @@ void efx_release_tx_buffers(struct efx_tx_queue *tx_queue) /* Free any buffers left in the ring */ while (tx_queue->read_count != tx_queue->write_count) { - buffer = &tx_queue->buffer[tx_queue->read_count & - tx_queue->efx->type->txd_ring_mask]; + buffer = &tx_queue->buffer[tx_queue->read_count & EFX_TXQ_MASK]; efx_dequeue_buffer(tx_queue, buffer); buffer->continuation = true; buffer->len = 0; @@ -483,7 +477,7 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue) EFX_LOG(tx_queue->efx, "shutting down TX queue %d\n", tx_queue->queue); /* Flush TX queue, remove descriptor ring */ - falcon_fini_tx(tx_queue); + efx_nic_fini_tx(tx_queue); efx_release_tx_buffers(tx_queue); @@ -500,7 +494,7 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue) void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) { EFX_LOG(tx_queue->efx, "destroying TX queue %d\n", tx_queue->queue); - falcon_remove_tx(tx_queue); + efx_nic_remove_tx(tx_queue); kfree(tx_queue->buffer); tx_queue->buffer = NULL; @@ -539,6 +533,7 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) #define ETH_HDR_LEN(skb) (skb_network_header(skb) - (skb)->data) #define SKB_TCP_OFF(skb) PTR_DIFF(tcp_hdr(skb), (skb)->data) #define SKB_IPV4_OFF(skb) PTR_DIFF(ip_hdr(skb), (skb)->data) +#define SKB_IPV6_OFF(skb) PTR_DIFF(ipv6_hdr(skb), (skb)->data) /** * struct tso_state - TSO state for an SKB @@ -551,6 +546,7 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) * @unmap_len: Length of SKB fragment * @unmap_addr: DMA address of SKB fragment * @unmap_single: DMA single vs page mapping flag + * @protocol: Network protocol (after any VLAN header) * @header_len: Number of bytes of header * @full_packet_size: Number of bytes to put in each outgoing segment * @@ -571,6 +567,7 @@ struct tso_state { dma_addr_t unmap_addr; bool unmap_single; + __be16 protocol; unsigned header_len; int full_packet_size; }; @@ -578,9 +575,9 @@ struct tso_state { /* * Verify that our various assumptions about sk_buffs and the conditions - * under which TSO will be attempted hold true. + * under which TSO will be attempted hold true. Return the protocol number. */ -static void efx_tso_check_safe(struct sk_buff *skb) +static __be16 efx_tso_check_protocol(struct sk_buff *skb) { __be16 protocol = skb->protocol; @@ -595,13 +592,22 @@ static void efx_tso_check_safe(struct sk_buff *skb) if (protocol == htons(ETH_P_IP)) skb_set_transport_header(skb, sizeof(*veh) + 4 * ip_hdr(skb)->ihl); + else if (protocol == htons(ETH_P_IPV6)) + skb_set_transport_header(skb, sizeof(*veh) + + sizeof(struct ipv6hdr)); } - EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IP)); - EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); + if (protocol == htons(ETH_P_IP)) { + EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); + } else { + EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IPV6)); + EFX_BUG_ON_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP); + } EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) + (tcp_hdr(skb)->doff << 2u)) > skb_headlen(skb)); + + return protocol; } @@ -708,14 +714,14 @@ static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue, { struct efx_tx_buffer *buffer; struct efx_nic *efx = tx_queue->efx; - unsigned dma_len, fill_level, insert_ptr, misalign; + unsigned dma_len, fill_level, insert_ptr; int q_space; EFX_BUG_ON_PARANOID(len <= 0); fill_level = tx_queue->insert_count - tx_queue->old_read_count; /* -1 as there is no way to represent all descriptors used */ - q_space = efx->type->txd_ring_mask - 1 - fill_level; + q_space = EFX_TXQ_MASK - 1 - fill_level; while (1) { if (unlikely(q_space-- <= 0)) { @@ -731,7 +737,7 @@ static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue, *(volatile unsigned *)&tx_queue->read_count; fill_level = (tx_queue->insert_count - tx_queue->old_read_count); - q_space = efx->type->txd_ring_mask - 1 - fill_level; + q_space = EFX_TXQ_MASK - 1 - fill_level; if (unlikely(q_space-- <= 0)) { *final_buffer = NULL; return 1; @@ -740,13 +746,13 @@ static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue, --tx_queue->stopped; } - insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask; + insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK; buffer = &tx_queue->buffer[insert_ptr]; ++tx_queue->insert_count; EFX_BUG_ON_PARANOID(tx_queue->insert_count - tx_queue->read_count > - efx->type->txd_ring_mask); + EFX_TXQ_MASK); efx_tsoh_free(tx_queue, buffer); EFX_BUG_ON_PARANOID(buffer->len); @@ -757,12 +763,7 @@ static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue, buffer->dma_addr = dma_addr; - /* Ensure we do not cross a boundary unsupported by H/W */ - dma_len = (~dma_addr & efx->type->tx_dma_mask) + 1; - - misalign = (unsigned)dma_addr & efx->type->bug5391_mask; - if (misalign && dma_len + misalign > 512) - dma_len = 512 - misalign; + dma_len = efx_max_tx_len(efx, dma_addr); /* If there is enough space to send then do so */ if (dma_len >= len) @@ -792,8 +793,7 @@ static void efx_tso_put_header(struct efx_tx_queue *tx_queue, { struct efx_tx_buffer *buffer; - buffer = &tx_queue->buffer[tx_queue->insert_count & - tx_queue->efx->type->txd_ring_mask]; + buffer = &tx_queue->buffer[tx_queue->insert_count & EFX_TXQ_MASK]; efx_tsoh_free(tx_queue, buffer); EFX_BUG_ON_PARANOID(buffer->len); EFX_BUG_ON_PARANOID(buffer->unmap_len); @@ -818,7 +818,7 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue) while (tx_queue->insert_count != tx_queue->write_count) { --tx_queue->insert_count; buffer = &tx_queue->buffer[tx_queue->insert_count & - tx_queue->efx->type->txd_ring_mask]; + EFX_TXQ_MASK]; efx_tsoh_free(tx_queue, buffer); EFX_BUG_ON_PARANOID(buffer->skb); buffer->len = 0; @@ -850,7 +850,10 @@ static void tso_start(struct tso_state *st, const struct sk_buff *skb) + PTR_DIFF(tcp_hdr(skb), skb->data)); st->full_packet_size = st->header_len + skb_shinfo(skb)->gso_size; - st->ipv4_id = ntohs(ip_hdr(skb)->id); + if (st->protocol == htons(ETH_P_IP)) + st->ipv4_id = ntohs(ip_hdr(skb)->id); + else + st->ipv4_id = 0; st->seqnum = ntohl(tcp_hdr(skb)->seq); EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg); @@ -965,7 +968,6 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, struct tso_state *st) { struct efx_tso_header *tsoh; - struct iphdr *tsoh_iph; struct tcphdr *tsoh_th; unsigned ip_length; u8 *header; @@ -989,7 +991,6 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, header = TSOH_BUFFER(tsoh); tsoh_th = (struct tcphdr *)(header + SKB_TCP_OFF(skb)); - tsoh_iph = (struct iphdr *)(header + SKB_IPV4_OFF(skb)); /* Copy and update the headers. */ memcpy(header, skb->data, st->header_len); @@ -1007,11 +1008,22 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, tsoh_th->fin = tcp_hdr(skb)->fin; tsoh_th->psh = tcp_hdr(skb)->psh; } - tsoh_iph->tot_len = htons(ip_length); - /* Linux leaves suitable gaps in the IP ID space for us to fill. */ - tsoh_iph->id = htons(st->ipv4_id); - st->ipv4_id++; + if (st->protocol == htons(ETH_P_IP)) { + struct iphdr *tsoh_iph = + (struct iphdr *)(header + SKB_IPV4_OFF(skb)); + + tsoh_iph->tot_len = htons(ip_length); + + /* Linux leaves suitable gaps in the IP ID space for us to fill. */ + tsoh_iph->id = htons(st->ipv4_id); + st->ipv4_id++; + } else { + struct ipv6hdr *tsoh_iph = + (struct ipv6hdr *)(header + SKB_IPV6_OFF(skb)); + + tsoh_iph->payload_len = htons(ip_length - sizeof(*tsoh_iph)); + } st->packet_space = skb_shinfo(skb)->gso_size; ++tx_queue->tso_packets; @@ -1041,8 +1053,8 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, int frag_i, rc, rc2 = NETDEV_TX_OK; struct tso_state state; - /* Verify TSO is safe - these checks should never fail. */ - efx_tso_check_safe(skb); + /* Find the packet protocol and sanity-check it */ + state.protocol = efx_tso_check_protocol(skb); EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); @@ -1092,14 +1104,14 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, } /* Pass off to hardware */ - falcon_push_buffers(tx_queue); + efx_nic_push_buffers(tx_queue); tx_queue->tso_bursts++; return NETDEV_TX_OK; mem_err: EFX_ERR(efx, "Out of memory for TSO headers, or PCI mapping error\n"); - dev_kfree_skb_any((struct sk_buff *)skb); + dev_kfree_skb_any(skb); goto unwind; stop: @@ -1135,7 +1147,7 @@ static void efx_fini_tso(struct efx_tx_queue *tx_queue) unsigned i; if (tx_queue->buffer) { - for (i = 0; i <= tx_queue->efx->type->txd_ring_mask; ++i) + for (i = 0; i <= EFX_TXQ_MASK; ++i) efx_tsoh_free(tx_queue, &tx_queue->buffer[i]); } diff --git a/drivers/net/sfc/tx.h b/drivers/net/sfc/tx.h deleted file mode 100644 index e3678962a5b4..000000000000 --- a/drivers/net/sfc/tx.h +++ /dev/null @@ -1,25 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. - * - * 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, incorporated herein by reference. - */ - -#ifndef EFX_TX_H -#define EFX_TX_H - -#include "net_driver.h" - -int efx_probe_tx_queue(struct efx_tx_queue *tx_queue); -void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); -void efx_init_tx_queue(struct efx_tx_queue *tx_queue); -void efx_fini_tx_queue(struct efx_tx_queue *tx_queue); - -netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, - struct net_device *net_dev); -void efx_release_tx_buffers(struct efx_tx_queue *tx_queue); - -#endif /* EFX_TX_H */ diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index c821c15445a0..acd9c734e483 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2009 Solarflare Communications Inc. * * 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 @@ -16,7 +16,9 @@ */ #define EFX_WORKAROUND_ALWAYS(efx) 1 -#define EFX_WORKAROUND_FALCON_A(efx) (falcon_rev(efx) <= FALCON_REV_A1) +#define EFX_WORKAROUND_FALCON_A(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) +#define EFX_WORKAROUND_FALCON_AB(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_B0) +#define EFX_WORKAROUND_SIENA(efx) (efx_nic_rev(efx) == EFX_REV_SIENA_A0) #define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx) #define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \ (efx)->phy_type == PHY_TYPE_SFT9001B) @@ -27,20 +29,22 @@ #define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS /* Bit-bashed I2C reads cause performance drop */ #define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G -/* TX pkt parser problem with <= 16 byte TXes */ -#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS /* TX_EV_PKT_ERR can be caused by a dangling TX descriptor * or a PCIe error (bug 11028) */ #define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS /* Transmit flow control may get disabled */ -#define EFX_WORKAROUND_11482 EFX_WORKAROUND_ALWAYS -/* Flush events can take a very long time to appear */ -#define EFX_WORKAROUND_11557 EFX_WORKAROUND_ALWAYS +#define EFX_WORKAROUND_11482 EFX_WORKAROUND_FALCON_AB /* Truncated IPv4 packets can confuse the TX packet parser */ -#define EFX_WORKAROUND_15592 EFX_WORKAROUND_ALWAYS +#define EFX_WORKAROUND_15592 EFX_WORKAROUND_FALCON_AB +/* Legacy ISR read can return zero once */ +#define EFX_WORKAROUND_15783 EFX_WORKAROUND_SIENA +/* Legacy interrupt storm when interrupt fifo fills */ +#define EFX_WORKAROUND_17213 EFX_WORKAROUND_SIENA /* Spurious parity errors in TSORT buffers */ #define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A +/* Unaligned read request >512 bytes after aligning may break TSORT */ +#define EFX_WORKAROUND_5391 EFX_WORKAROUND_FALCON_A /* iSCSI parsing errors */ #define EFX_WORKAROUND_5583 EFX_WORKAROUND_FALCON_A /* RX events go missing */ diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index f4dfd1f679a9..6b364a6c6c60 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -365,11 +365,10 @@ static inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp } skb_reserve(newskb, 2); } else { - skb = netdev_alloc_skb(dev, len + 2); - if (skb) { - skb_reserve(skb, 2); + skb = netdev_alloc_skb_ip_align(dev, len); + if (skb) skb_copy_to_linear_data(skb, rd->skb->data, len); - } + newskb = rd->skb; } memory_squeeze: diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 528b912a4b0d..c88bc1013047 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -30,6 +30,7 @@ #include <linux/phy.h> #include <linux/cache.h> #include <linux/io.h> +#include <linux/pm_runtime.h> #include <asm/cacheflush.h> #include "sh_eth.h" @@ -299,16 +300,20 @@ static void update_mac_address(struct net_device *ndev) * When you want use this device, you must set MAC address in bootloader. * */ -static void read_mac_address(struct net_device *ndev) +static void read_mac_address(struct net_device *ndev, unsigned char *mac) { u32 ioaddr = ndev->base_addr; - ndev->dev_addr[0] = (ctrl_inl(ioaddr + MAHR) >> 24); - ndev->dev_addr[1] = (ctrl_inl(ioaddr + MAHR) >> 16) & 0xFF; - ndev->dev_addr[2] = (ctrl_inl(ioaddr + MAHR) >> 8) & 0xFF; - ndev->dev_addr[3] = (ctrl_inl(ioaddr + MAHR) & 0xFF); - ndev->dev_addr[4] = (ctrl_inl(ioaddr + MALR) >> 8) & 0xFF; - ndev->dev_addr[5] = (ctrl_inl(ioaddr + MALR) & 0xFF); + if (mac[0] || mac[1] || mac[2] || mac[3] || mac[4] || mac[5]) { + memcpy(ndev->dev_addr, mac, 6); + } else { + ndev->dev_addr[0] = (ctrl_inl(ioaddr + MAHR) >> 24); + ndev->dev_addr[1] = (ctrl_inl(ioaddr + MAHR) >> 16) & 0xFF; + ndev->dev_addr[2] = (ctrl_inl(ioaddr + MAHR) >> 8) & 0xFF; + ndev->dev_addr[3] = (ctrl_inl(ioaddr + MAHR) & 0xFF); + ndev->dev_addr[4] = (ctrl_inl(ioaddr + MALR) >> 8) & 0xFF; + ndev->dev_addr[5] = (ctrl_inl(ioaddr + MALR) & 0xFF); + } } struct bb_info { @@ -1009,7 +1014,9 @@ static int sh_eth_open(struct net_device *ndev) int ret = 0; struct sh_eth_private *mdp = netdev_priv(ndev); - ret = request_irq(ndev->irq, &sh_eth_interrupt, + pm_runtime_get_sync(&mdp->pdev->dev); + + ret = request_irq(ndev->irq, sh_eth_interrupt, #if defined(CONFIG_CPU_SUBTYPE_SH7763) || defined(CONFIG_CPU_SUBTYPE_SH7764) IRQF_SHARED, #else @@ -1045,6 +1052,7 @@ static int sh_eth_open(struct net_device *ndev) out_free_irq: free_irq(ndev->irq, ndev); + pm_runtime_put_sync(&mdp->pdev->dev); return ret; } @@ -1176,6 +1184,8 @@ static int sh_eth_close(struct net_device *ndev) ringsize = sizeof(struct sh_eth_txdesc) * TX_RING_SIZE; dma_free_coherent(NULL, ringsize, mdp->tx_ring, mdp->tx_desc_dma); + pm_runtime_put_sync(&mdp->pdev->dev); + return 0; } @@ -1184,6 +1194,8 @@ static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev) struct sh_eth_private *mdp = netdev_priv(ndev); u32 ioaddr = ndev->base_addr; + pm_runtime_get_sync(&mdp->pdev->dev); + mdp->stats.tx_dropped += ctrl_inl(ioaddr + TROCR); ctrl_outl(0, ioaddr + TROCR); /* (write clear) */ mdp->stats.collisions += ctrl_inl(ioaddr + CDCR); @@ -1199,6 +1211,8 @@ static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev) mdp->stats.tx_carrier_errors += ctrl_inl(ioaddr + CNDCR); ctrl_outl(0, ioaddr + CNDCR); /* (write clear) */ #endif + pm_runtime_put_sync(&mdp->pdev->dev); + return &mdp->stats; } @@ -1407,6 +1421,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev) mdp = netdev_priv(ndev); spin_lock_init(&mdp->lock); + mdp->pdev = pdev; + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); pd = (struct sh_eth_plat_data *)(pdev->dev.platform_data); /* get PHY ID */ @@ -1428,7 +1445,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) mdp->post_fw = POST_FW >> (devno << 1); /* read and set MAC address */ - read_mac_address(ndev); + read_mac_address(ndev, pd->mac_addr); /* First device only init */ if (!devno) { @@ -1482,18 +1499,37 @@ static int sh_eth_drv_remove(struct platform_device *pdev) sh_mdio_release(ndev); unregister_netdev(ndev); flush_scheduled_work(); - + pm_runtime_disable(&pdev->dev); free_netdev(ndev); platform_set_drvdata(pdev, NULL); return 0; } +static int sh_eth_runtime_nop(struct device *dev) +{ + /* + * Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static struct dev_pm_ops sh_eth_dev_pm_ops = { + .runtime_suspend = sh_eth_runtime_nop, + .runtime_resume = sh_eth_runtime_nop, +}; + static struct platform_driver sh_eth_driver = { .probe = sh_eth_drv_probe, .remove = sh_eth_drv_remove, .driver = { .name = CARDNAME, + .pm = &sh_eth_dev_pm_ops, }, }; diff --git a/drivers/net/sh_eth.h b/drivers/net/sh_eth.h index ba151f86ae7b..8b47763958f2 100644 --- a/drivers/net/sh_eth.h +++ b/drivers/net/sh_eth.h @@ -703,6 +703,7 @@ struct sh_eth_cpu_data { }; struct sh_eth_private { + struct platform_device *pdev; struct sh_eth_cpu_data *cd; dma_addr_t rx_desc_dma; dma_addr_t tx_desc_dma; diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c index 7cc9898f4e00..31233b4c44a0 100644 --- a/drivers/net/sis190.c +++ b/drivers/net/sis190.c @@ -536,13 +536,12 @@ static bool sis190_try_rx_copy(struct sis190_private *tp, if (pkt_size >= rx_copybreak) goto out; - skb = netdev_alloc_skb(tp->dev, pkt_size + 2); + skb = netdev_alloc_skb_ip_align(tp->dev, pkt_size); if (!skb) goto out; pci_dma_sync_single_for_cpu(tp->pci_dev, addr, tp->rx_buf_sz, PCI_DMA_FROMDEVICE); - skb_reserve(skb, 2); skb_copy_to_linear_data(skb, sk_buff[0]->data, pkt_size); *sk_buff = skb; done = true; diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index c072f7f36acf..7360d4bbf75e 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -1016,7 +1016,7 @@ sis900_open(struct net_device *net_dev) /* Equalizer workaround Rule */ sis630_set_eq(net_dev, sis_priv->chipset_rev); - ret = request_irq(net_dev->irq, &sis900_interrupt, IRQF_SHARED, + ret = request_irq(net_dev->irq, sis900_interrupt, IRQF_SHARED, net_dev->name, net_dev); if (ret) return ret; @@ -1760,7 +1760,7 @@ static int sis900_rx(struct net_device *net_dev) sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - /* refill the Rx buffer, what if there is not enought + /* refill the Rx buffer, what if there is not enough * memory for new socket buffer ?? */ if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { /* @@ -1775,7 +1775,7 @@ static int sis900_rx(struct net_device *net_dev) } /* This situation should never happen, but due to - some unknow bugs, it is possible that + some unknown bugs, it is possible that we are working on NULL sk_buff :-( */ if (sis_priv->rx_skbuff[entry] == NULL) { if (netif_msg_rx_err(sis_priv)) diff --git a/drivers/net/skfp/h/smc.h b/drivers/net/skfp/h/smc.h index 1758d9548361..026a83b9f743 100644 --- a/drivers/net/skfp/h/smc.h +++ b/drivers/net/skfp/h/smc.h @@ -393,10 +393,10 @@ struct smt_config { */ u_long mac_d_max ; /* MAC : D_Max timer value */ - u_long lct_short ; /* LCT : error threshhold */ - u_long lct_medium ; /* LCT : error threshhold */ - u_long lct_long ; /* LCT : error threshhold */ - u_long lct_extended ; /* LCT : error threshhold */ + u_long lct_short ; /* LCT : error threshold */ + u_long lct_medium ; /* LCT : error threshold */ + u_long lct_long ; /* LCT : error threshold */ + u_long lct_extended ; /* LCT : error threshold */ } ; #ifdef DEBUG diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index b27156eaf267..db216a728503 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -1002,7 +1002,7 @@ static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } break; default: - printk("ioctl for %s: unknow cmd: %04x\n", dev->name, ioc.cmd); + printk("ioctl for %s: unknown cmd: %04x\n", dev->name, ioc.cmd); status = -EOPNOTSUPP; } // switch diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 8f5414348e86..379a3dc00163 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -238,8 +238,8 @@ static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct skge_port *skge = netdev_priv(dev); struct skge_hw *hw = skge->hw; - if ((wol->wolopts & ~wol_supported(hw)) - || !device_can_wakeup(&hw->pdev->dev)) + if ((wol->wolopts & ~wol_supported(hw)) || + !device_can_wakeup(&hw->pdev->dev)) return -EOPNOTSUPP; skge->wol = wol->wolopts; @@ -576,9 +576,10 @@ static void skge_get_pauseparam(struct net_device *dev, { struct skge_port *skge = netdev_priv(dev); - ecmd->rx_pause = (skge->flow_control == FLOW_MODE_SYMMETRIC) - || (skge->flow_control == FLOW_MODE_SYM_OR_REM); - ecmd->tx_pause = ecmd->rx_pause || (skge->flow_control == FLOW_MODE_LOC_SEND); + ecmd->rx_pause = ((skge->flow_control == FLOW_MODE_SYMMETRIC) || + (skge->flow_control == FLOW_MODE_SYM_OR_REM)); + ecmd->tx_pause = (ecmd->rx_pause || + (skge->flow_control == FLOW_MODE_LOC_SEND)); ecmd->autoneg = ecmd->rx_pause || ecmd->tx_pause; } @@ -2779,8 +2780,8 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, /* This seems backwards, but it is what the sk98lin * does. Looks like hardware is wrong? */ - if (ipip_hdr(skb)->protocol == IPPROTO_UDP - && hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON) + if (ipip_hdr(skb)->protocol == IPPROTO_UDP && + hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON) control = BMU_TCP_CHECK; else control = BMU_UDP_CHECK; @@ -2948,8 +2949,8 @@ static void genesis_set_multicast(struct net_device *dev) else { memset(filter, 0, sizeof(filter)); - if (skge->flow_status == FLOW_STAT_REM_SEND - || skge->flow_status == FLOW_STAT_SYMMETRIC) + if (skge->flow_status == FLOW_STAT_REM_SEND || + skge->flow_status == FLOW_STAT_SYMMETRIC) genesis_add_filter(filter, pause_mc_addr); for (i = 0; list && i < count; i++, list = list->next) @@ -2972,8 +2973,8 @@ static void yukon_set_multicast(struct net_device *dev) struct skge_hw *hw = skge->hw; int port = skge->port; struct dev_mc_list *list = dev->mc_list; - int rx_pause = (skge->flow_status == FLOW_STAT_REM_SEND - || skge->flow_status == FLOW_STAT_SYMMETRIC); + int rx_pause = (skge->flow_status == FLOW_STAT_REM_SEND || + skge->flow_status == FLOW_STAT_SYMMETRIC); u16 reg; u8 filter[8]; @@ -3071,11 +3072,10 @@ static struct sk_buff *skge_rx_get(struct net_device *dev, goto error; if (len < RX_COPY_THRESHOLD) { - skb = netdev_alloc_skb(dev, len + 2); + skb = netdev_alloc_skb_ip_align(dev, len); if (!skb) goto resubmit; - skb_reserve(skb, 2); pci_dma_sync_single_for_cpu(skge->hw->pdev, pci_unmap_addr(e, mapaddr), len, PCI_DMA_FROMDEVICE); @@ -3086,11 +3086,11 @@ static struct sk_buff *skge_rx_get(struct net_device *dev, skge_rx_reuse(e, skge->rx_buf_size); } else { struct sk_buff *nskb; - nskb = netdev_alloc_skb(dev, skge->rx_buf_size + NET_IP_ALIGN); + + nskb = netdev_alloc_skb_ip_align(dev, skge->rx_buf_size); if (!nskb) goto resubmit; - skb_reserve(nskb, NET_IP_ALIGN); pci_unmap_single(skge->hw->pdev, pci_unmap_addr(e, mapaddr), pci_unmap_len(e, maplen), @@ -3948,7 +3948,7 @@ static int __devinit skge_probe(struct pci_dev *pdev, hw->pdev = pdev; spin_lock_init(&hw->hw_lock); spin_lock_init(&hw->phy_lock); - tasklet_init(&hw->phy_task, &skge_extirq, (unsigned long) hw); + tasklet_init(&hw->phy_task, skge_extirq, (unsigned long) hw); hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); if (!hw->regs) { diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 6a10d7ba5877..044e6817986f 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -50,7 +50,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.25" +#define DRV_VERSION "1.26" #define PFX DRV_NAME " " /* @@ -102,6 +102,7 @@ MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */ + { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E01) }, /* SK-9E21M */ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) }, /* DGE-560T */ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4001) }, /* DGE-550SX */ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4B02) }, /* DGE-560SX */ @@ -139,6 +140,7 @@ static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436D) }, /* 88E8055 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4370) }, /* 88E8075 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4380) }, /* 88E8057 */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4381) }, /* 88E8059 */ { 0 } }; @@ -372,8 +374,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO); /* downshift on PHY 88E1112 and 88E1149 is changed */ - if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) - && (hw->flags & SKY2_HW_NEWER_PHY)) { + if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) && + (hw->flags & SKY2_HW_NEWER_PHY)) { /* set downshift counter to 3x and enable downshift */ ctrl &= ~PHY_M_PC_DSC_MSK; ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA; @@ -602,13 +604,23 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) /* apply workaround for integrated resistors calibration */ gm_phy_write(hw, port, PHY_MARV_PAGE_ADDR, 17); gm_phy_write(hw, port, PHY_MARV_PAGE_DATA, 0x3f60); + } else if (hw->chip_id == CHIP_ID_YUKON_OPT && hw->chip_rev == 0) { + /* apply fixes in PHY AFE */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00ff); + + /* apply RDAC termination workaround */ + gm_phy_write(hw, port, 24, 0x2800); + gm_phy_write(hw, port, 23, 0x2001); + + /* set page register back to 0 */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0); } else if (hw->chip_id != CHIP_ID_YUKON_EX && hw->chip_id < CHIP_ID_YUKON_SUPR) { /* no effect on Yukon-XL */ gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); - if ( !(sky2->flags & SKY2_FLAG_AUTO_SPEED) - || sky2->speed == SPEED_100) { + if (!(sky2->flags & SKY2_FLAG_AUTO_SPEED) || + sky2->speed == SPEED_100) { /* turn on 100 Mbps LED (LED_LINK100) */ ledover |= PHY_M_LED_MO_100(MO_LED_ON); } @@ -786,8 +798,7 @@ static void sky2_set_tx_stfwd(struct sky2_hw *hw, unsigned port) if ( (hw->chip_id == CHIP_ID_YUKON_EX && hw->chip_rev != CHIP_REV_YU_EX_A0) || - hw->chip_id == CHIP_ID_YUKON_FE_P || - hw->chip_id == CHIP_ID_YUKON_SUPR) { + hw->chip_id >= CHIP_ID_YUKON_FE_P) { /* Yukon-Extreme B0 and further Extreme devices */ /* enable Store & Forward mode for TX */ @@ -925,8 +936,14 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) /* On chips without ram buffer, pause is controled by MAC level */ if (!(hw->flags & SKY2_HW_RAM_BUFFER)) { - sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8); - sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8); + /* Pause threshold is scaled by 8 in bytes */ + if (hw->chip_id == CHIP_ID_YUKON_FE_P && + hw->chip_rev == CHIP_REV_YU_FE2_A0) + reg = 1568 / 8; + else + reg = 1024 / 8; + sky2_write16(hw, SK_REG(port, RX_GMF_UP_THR), reg); + sky2_write16(hw, SK_REG(port, RX_GMF_LP_THR), 768 / 8); sky2_set_tx_stfwd(hw, port); } @@ -1336,8 +1353,8 @@ static int sky2_rx_start(struct sky2_port *sky2) /* These chips have no ram buffer? * MAC Rx RAM Read is controlled by hardware */ if (hw->chip_id == CHIP_ID_YUKON_EC_U && - (hw->chip_rev == CHIP_REV_YU_EC_U_A1 - || hw->chip_rev == CHIP_REV_YU_EC_U_B0)) + (hw->chip_rev == CHIP_REV_YU_EC_U_A1 || + hw->chip_rev == CHIP_REV_YU_EC_U_B0)) sky2_write32(hw, Q_ADDR(rxq, Q_TEST), F_M_RX_RAM_DIS); sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1); @@ -1397,6 +1414,31 @@ static int sky2_rx_start(struct sky2_port *sky2) /* Tell chip about available buffers */ sky2_rx_update(sky2, rxq); + + if (hw->chip_id == CHIP_ID_YUKON_EX || + hw->chip_id == CHIP_ID_YUKON_SUPR) { + /* + * Disable flushing of non ASF packets; + * must be done after initializing the BMUs; + * drivers without ASF support should do this too, otherwise + * it may happen that they cannot run on ASF devices; + * remember that the MAC FIFO isn't reset during initialization. + */ + sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_MACSEC_FLUSH_OFF); + } + + if (hw->chip_id >= CHIP_ID_YUKON_SUPR) { + /* Enable RX Home Address & Routing Header checksum fix */ + sky2_write16(hw, SK_REG(sky2->port, RX_GMF_FL_CTRL), + RX_IPV6_SA_MOB_ENA | RX_IPV6_DA_MOB_ENA); + + /* Enable TX Home Address & Routing Header checksum fix */ + sky2_write32(hw, Q_ADDR(txqaddr[sky2->port], Q_TEST), + TBMU_TEST_HOME_ADD_FIX_EN | TBMU_TEST_ROUTING_ADD_FIX_EN); + } + + + return 0; nomem: sky2_rx_clean(sky2); @@ -1518,8 +1560,8 @@ static int sky2_up(struct net_device *dev) sky2_write32(hw, Q_ADDR(txqaddr[port], Q_TEST), F_TX_CHK_AUTO_OFF); /* Set almost empty threshold */ - if (hw->chip_id == CHIP_ID_YUKON_EC_U - && hw->chip_rev == CHIP_REV_YU_EC_U_A0) + if (hw->chip_id == CHIP_ID_YUKON_EC_U && + hw->chip_rev == CHIP_REV_YU_EC_U_A0) sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), ECU_TXFF_LEV); sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, @@ -1865,8 +1907,8 @@ static int sky2_down(struct net_device *dev) sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET); /* Workaround shared GMAC reset */ - if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 - && port == 0 && hw->dev[1] && netif_running(hw->dev[1]))) + if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 && + port == 0 && hw->dev[1] && netif_running(hw->dev[1]))) sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET); sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); @@ -1975,7 +2017,7 @@ static void sky2_link_down(struct sky2_port *sky2) netif_carrier_off(sky2->netdev); - /* Turn on link LED */ + /* Turn off link LED */ sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF); if (netif_msg_link(sky2)) @@ -2043,8 +2085,8 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) sky2->flow_status = FC_TX; } - if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000 - && !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX)) + if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000 && + !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX)) sky2->flow_status = FC_NONE; if (sky2->flow_status & FC_TX) @@ -2096,6 +2138,25 @@ out: spin_unlock(&sky2->phy_lock); } +/* Special quick link interrupt (Yukon-2 Optima only) */ +static void sky2_qlink_intr(struct sky2_hw *hw) +{ + struct sky2_port *sky2 = netdev_priv(hw->dev[0]); + u32 imask; + u16 phy; + + /* disable irq */ + imask = sky2_read32(hw, B0_IMSK); + imask &= ~Y2_IS_PHY_QLNK; + sky2_write32(hw, B0_IMSK, imask); + + /* reset PHY Link Detect */ + phy = sky2_pci_read16(hw, PSM_CONFIG_REG4); + sky2_pci_write16(hw, PSM_CONFIG_REG4, phy | 1); + + sky2_link_up(sky2); +} + /* Transmit timeout is only called if we are running, carrier is up * and tx queue is full (stopped). */ @@ -2191,9 +2252,8 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2, { struct sk_buff *skb; - skb = netdev_alloc_skb(sky2->netdev, length + 2); + skb = netdev_alloc_skb_ip_align(sky2->netdev, length); if (likely(skb)) { - skb_reserve(skb, 2); pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr, length, PCI_DMA_FROMDEVICE); skb_copy_from_linear_data(re->skb, skb->data, length); @@ -2766,6 +2826,9 @@ static int sky2_poll(struct napi_struct *napi, int work_limit) if (status & Y2_IS_IRQ_PHY2) sky2_phy_intr(hw, 1); + if (status & Y2_IS_PHY_QLNK) + sky2_qlink_intr(hw); + while ((idx = sky2_read16(hw, STAT_PUT_IDX)) != hw->st_idx) { work_done += sky2_status_intr(hw, work_limit - work_done, idx); @@ -2815,6 +2878,7 @@ static u32 sky2_mhz(const struct sky2_hw *hw) case CHIP_ID_YUKON_EX: case CHIP_ID_YUKON_SUPR: case CHIP_ID_YUKON_UL_2: + case CHIP_ID_YUKON_OPT: return 125; case CHIP_ID_YUKON_FE: @@ -2904,6 +2968,7 @@ static int __devinit sky2_init(struct sky2_hw *hw) break; case CHIP_ID_YUKON_UL_2: + case CHIP_ID_YUKON_OPT: hw->flags = SKY2_HW_GIGABIT | SKY2_HW_ADV_POWER_CTL; break; @@ -2986,6 +3051,52 @@ static void sky2_reset(struct sky2_hw *hw) sky2_write16(hw, SK_REG(i, GMAC_CTRL), GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON | GMC_BYP_RETR_ON); + + } + + if (hw->chip_id == CHIP_ID_YUKON_SUPR && hw->chip_rev > CHIP_REV_YU_SU_B0) { + /* enable MACSec clock gating */ + sky2_pci_write32(hw, PCI_DEV_REG3, P_CLK_MACSEC_DIS); + } + + if (hw->chip_id == CHIP_ID_YUKON_OPT) { + u16 reg; + u32 msk; + + if (hw->chip_rev == 0) { + /* disable PCI-E PHY power down (set PHY reg 0x80, bit 7 */ + sky2_write32(hw, Y2_PEX_PHY_DATA, (0x80UL << 16) | (1 << 7)); + + /* set PHY Link Detect Timer to 1.1 second (11x 100ms) */ + reg = 10; + } else { + /* set PHY Link Detect Timer to 0.4 second (4x 100ms) */ + reg = 3; + } + + reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE; + + /* reset PHY Link Detect */ + sky2_pci_write16(hw, PSM_CONFIG_REG4, + reg | PSM_CONFIG_REG4_RST_PHY_LINK_DETECT); + sky2_pci_write16(hw, PSM_CONFIG_REG4, reg); + + + /* enable PHY Quick Link */ + msk = sky2_read32(hw, B0_IMSK); + msk |= Y2_IS_PHY_QLNK; + sky2_write32(hw, B0_IMSK, msk); + + /* check if PSMv2 was running before */ + reg = sky2_pci_read16(hw, PSM_CONFIG_REG3); + if (reg & PCI_EXP_LNKCTL_ASPMC) { + int cap = pci_find_capability(pdev, PCI_CAP_ID_EXP); + /* restore the PCIe Link Control register */ + sky2_pci_write16(hw, cap + PCI_EXP_LNKCTL, reg); + } + + /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */ + sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16)); } /* Clear I2C IRQ noise */ @@ -3133,8 +3244,8 @@ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; - if ((wol->wolopts & ~sky2_wol_supported(sky2->hw)) - || !device_can_wakeup(&hw->pdev->dev)) + if ((wol->wolopts & ~sky2_wol_supported(sky2->hw)) || + !device_can_wakeup(&hw->pdev->dev)) return -EOPNOTSUPP; sky2->wol = wol->wolopts; @@ -4406,9 +4517,11 @@ static const char *sky2_name(u8 chipid, char *buf, int sz) "FE+", /* 0xb8 */ "Supreme", /* 0xb9 */ "UL 2", /* 0xba */ + "Unknown", /* 0xbb */ + "Optima", /* 0xbc */ }; - if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_UL_2) + if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_OPT) strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz); else snprintf(buf, sz, "(chip %#x)", chipid); diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index ed54129698b4..365d79c7d834 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -16,6 +16,13 @@ enum { PCI_DEV_REG5 = 0x88, PCI_CFG_REG_0 = 0x90, PCI_CFG_REG_1 = 0x94, + + PSM_CONFIG_REG0 = 0x98, + PSM_CONFIG_REG1 = 0x9C, + PSM_CONFIG_REG2 = 0x160, + PSM_CONFIG_REG3 = 0x164, + PSM_CONFIG_REG4 = 0x168, + }; /* Yukon-2 */ @@ -48,6 +55,37 @@ enum pci_dev_reg_2 { PCI_USEDATA64 = 1<<0, /* Use 64Bit Data bus ext */ }; +/* PCI_OUR_REG_3 32 bit Our Register 3 (Yukon-ECU only) */ +enum pci_dev_reg_3 { + P_CLK_ASF_REGS_DIS = 1<<18,/* Disable Clock ASF (Yukon-Ext.) */ + P_CLK_COR_REGS_D0_DIS = 1<<17,/* Disable Clock Core Regs D0 */ + P_CLK_MACSEC_DIS = 1<<17,/* Disable Clock MACSec (Yukon-Ext.) */ + P_CLK_PCI_REGS_D0_DIS = 1<<16,/* Disable Clock PCI Regs D0 */ + P_CLK_COR_YTB_ARB_DIS = 1<<15,/* Disable Clock YTB Arbiter */ + P_CLK_MAC_LNK1_D3_DIS = 1<<14,/* Disable Clock MAC Link1 D3 */ + P_CLK_COR_LNK1_D0_DIS = 1<<13,/* Disable Clock Core Link1 D0 */ + P_CLK_MAC_LNK1_D0_DIS = 1<<12,/* Disable Clock MAC Link1 D0 */ + P_CLK_COR_LNK1_D3_DIS = 1<<11,/* Disable Clock Core Link1 D3 */ + P_CLK_PCI_MST_ARB_DIS = 1<<10,/* Disable Clock PCI Master Arb. */ + P_CLK_COR_REGS_D3_DIS = 1<<9, /* Disable Clock Core Regs D3 */ + P_CLK_PCI_REGS_D3_DIS = 1<<8, /* Disable Clock PCI Regs D3 */ + P_CLK_REF_LNK1_GM_DIS = 1<<7, /* Disable Clock Ref. Link1 GMAC */ + P_CLK_COR_LNK1_GM_DIS = 1<<6, /* Disable Clock Core Link1 GMAC */ + P_CLK_PCI_COMMON_DIS = 1<<5, /* Disable Clock PCI Common */ + P_CLK_COR_COMMON_DIS = 1<<4, /* Disable Clock Core Common */ + P_CLK_PCI_LNK1_BMU_DIS = 1<<3, /* Disable Clock PCI Link1 BMU */ + P_CLK_COR_LNK1_BMU_DIS = 1<<2, /* Disable Clock Core Link1 BMU */ + P_CLK_PCI_LNK1_BIU_DIS = 1<<1, /* Disable Clock PCI Link1 BIU */ + P_CLK_COR_LNK1_BIU_DIS = 1<<0, /* Disable Clock Core Link1 BIU */ + PCIE_OUR3_WOL_D3_COLD_SET = P_CLK_ASF_REGS_DIS | + P_CLK_COR_REGS_D0_DIS | + P_CLK_COR_LNK1_D0_DIS | + P_CLK_MAC_LNK1_D0_DIS | + P_CLK_PCI_MST_ARB_DIS | + P_CLK_COR_COMMON_DIS | + P_CLK_COR_LNK1_BMU_DIS, +}; + /* PCI_OUR_REG_4 32 bit Our Register 4 (Yukon-ECU only) */ enum pci_dev_reg_4 { /* (Link Training & Status State Machine) */ @@ -114,7 +152,7 @@ enum pci_dev_reg_5 { P_GAT_PCIE_RX_EL_IDLE, }; -#/* PCI_CFG_REG_1 32 bit Config Register 1 (Yukon-Ext only) */ +/* PCI_CFG_REG_1 32 bit Config Register 1 (Yukon-Ext only) */ enum pci_cfg_reg1 { P_CF1_DIS_REL_EVT_RST = 1<<24, /* Dis. Rel. Event during PCIE reset */ /* Bit 23..21: Release Clock on Event */ @@ -145,6 +183,72 @@ enum pci_cfg_reg1 { P_CF1_ENA_TXBMU_WR_IDLE, }; +/* Yukon-Optima */ +enum { + PSM_CONFIG_REG1_AC_PRESENT_STATUS = 1<<31, /* AC Present Status */ + + PSM_CONFIG_REG1_PTP_CLK_SEL = 1<<29, /* PTP Clock Select */ + PSM_CONFIG_REG1_PTP_MODE = 1<<28, /* PTP Mode */ + + PSM_CONFIG_REG1_MUX_PHY_LINK = 1<<27, /* PHY Energy Detect Event */ + + PSM_CONFIG_REG1_EN_PIN63_AC_PRESENT = 1<<26, /* Enable LED_DUPLEX for ac_present */ + PSM_CONFIG_REG1_EN_PCIE_TIMER = 1<<25, /* Enable PCIe Timer */ + PSM_CONFIG_REG1_EN_SPU_TIMER = 1<<24, /* Enable SPU Timer */ + PSM_CONFIG_REG1_POLARITY_AC_PRESENT = 1<<23, /* AC Present Polarity */ + + PSM_CONFIG_REG1_EN_AC_PRESENT = 1<<21, /* Enable AC Present */ + + PSM_CONFIG_REG1_EN_GPHY_INT_PSM = 1<<20, /* Enable GPHY INT for PSM */ + PSM_CONFIG_REG1_DIS_PSM_TIMER = 1<<19, /* Disable PSM Timer */ +}; + +/* Yukon-Supreme */ +enum { + PSM_CONFIG_REG1_GPHY_ENERGY_STS = 1<<31, /* GPHY Energy Detect Status */ + + PSM_CONFIG_REG1_UART_MODE_MSK = 3<<29, /* UART_Mode */ + PSM_CONFIG_REG1_CLK_RUN_ASF = 1<<28, /* Enable Clock Free Running for ASF Subsystem */ + PSM_CONFIG_REG1_UART_CLK_DISABLE= 1<<27, /* Disable UART clock */ + PSM_CONFIG_REG1_VAUX_ONE = 1<<26, /* Tie internal Vaux to 1'b1 */ + PSM_CONFIG_REG1_UART_FC_RI_VAL = 1<<25, /* Default value for UART_RI_n */ + PSM_CONFIG_REG1_UART_FC_DCD_VAL = 1<<24, /* Default value for UART_DCD_n */ + PSM_CONFIG_REG1_UART_FC_DSR_VAL = 1<<23, /* Default value for UART_DSR_n */ + PSM_CONFIG_REG1_UART_FC_CTS_VAL = 1<<22, /* Default value for UART_CTS_n */ + PSM_CONFIG_REG1_LATCH_VAUX = 1<<21, /* Enable Latch current Vaux_avlbl */ + PSM_CONFIG_REG1_FORCE_TESTMODE_INPUT= 1<<20, /* Force Testmode pin as input PAD */ + PSM_CONFIG_REG1_UART_RST = 1<<19, /* UART_RST */ + PSM_CONFIG_REG1_PSM_PCIE_L1_POL = 1<<18, /* PCIE L1 Event Polarity for PSM */ + PSM_CONFIG_REG1_TIMER_STAT = 1<<17, /* PSM Timer Status */ + PSM_CONFIG_REG1_GPHY_INT = 1<<16, /* GPHY INT Status */ + PSM_CONFIG_REG1_FORCE_TESTMODE_ZERO= 1<<15, /* Force internal Testmode as 1'b0 */ + PSM_CONFIG_REG1_EN_INT_ASPM_CLKREQ = 1<<14, /* ENABLE INT for CLKRUN on ASPM and CLKREQ */ + PSM_CONFIG_REG1_EN_SND_TASK_ASPM_CLKREQ = 1<<13, /* ENABLE Snd_task for CLKRUN on ASPM and CLKREQ */ + PSM_CONFIG_REG1_DIS_CLK_GATE_SND_TASK = 1<<12, /* Disable CLK_GATE control snd_task */ + PSM_CONFIG_REG1_DIS_FF_CHIAN_SND_INTA = 1<<11, /* Disable flip-flop chain for sndmsg_inta */ + + PSM_CONFIG_REG1_DIS_LOADER = 1<<9, /* Disable Loader SM after PSM Goes back to IDLE */ + PSM_CONFIG_REG1_DO_PWDN = 1<<8, /* Do Power Down, Start PSM Scheme */ + PSM_CONFIG_REG1_DIS_PIG = 1<<7, /* Disable Plug-in-Go SM after PSM Goes back to IDLE */ + PSM_CONFIG_REG1_DIS_PERST = 1<<6, /* Disable Internal PCIe Reset after PSM Goes back to IDLE */ + PSM_CONFIG_REG1_EN_REG18_PD = 1<<5, /* Enable REG18 Power Down for PSM */ + PSM_CONFIG_REG1_EN_PSM_LOAD = 1<<4, /* Disable EEPROM Loader after PSM Goes back to IDLE */ + PSM_CONFIG_REG1_EN_PSM_HOT_RST = 1<<3, /* Enable PCIe Hot Reset for PSM */ + PSM_CONFIG_REG1_EN_PSM_PERST = 1<<2, /* Enable PCIe Reset Event for PSM */ + PSM_CONFIG_REG1_EN_PSM_PCIE_L1 = 1<<1, /* Enable PCIe L1 Event for PSM */ + PSM_CONFIG_REG1_EN_PSM = 1<<0, /* Enable PSM Scheme */ +}; + +/* PSM_CONFIG_REG4 0x0168 PSM Config Register 4 */ +enum { + /* PHY Link Detect Timer */ + PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_MSK = 0xf<<4, + PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE = 4, + + PSM_CONFIG_REG4_DEBUG_TIMER = 1<<1, /* Debug Timer */ + PSM_CONFIG_REG4_RST_PHY_LINK_DETECT = 1<<0, /* Reset GPHY Link Detect */ +}; + #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ PCI_STATUS_SIG_SYSTEM_ERROR | \ @@ -197,6 +301,9 @@ enum csr_regs { B2_I2C_IRQ = 0x0168, B2_I2C_SW = 0x016c, + Y2_PEX_PHY_DATA = 0x0170, + Y2_PEX_PHY_ADDR = 0x0172, + B3_RAM_ADDR = 0x0180, B3_RAM_DATA_LO = 0x0184, B3_RAM_DATA_HI = 0x0188, @@ -317,6 +424,10 @@ enum { Y2_IS_CHK_TXS2 = 1<<9, /* Descriptor error TXS 2 */ Y2_IS_CHK_TXA2 = 1<<8, /* Descriptor error TXA 2 */ + Y2_IS_PSM_ACK = 1<<7, /* PSM Acknowledge (Yukon-Optima only) */ + Y2_IS_PTP_TIST = 1<<6, /* PTP Time Stamp (Yukon-Optima only) */ + Y2_IS_PHY_QLNK = 1<<5, /* PHY Quick Link (Yukon-Optima only) */ + Y2_IS_IRQ_PHY1 = 1<<4, /* Interrupt from PHY 1 */ Y2_IS_IRQ_MAC1 = 1<<3, /* Interrupt from MAC 1 */ Y2_IS_CHK_RX1 = 1<<2, /* Descriptor error Rx 1 */ @@ -435,6 +546,7 @@ enum { CHIP_ID_YUKON_FE_P = 0xb8, /* YUKON-2 FE+ */ CHIP_ID_YUKON_SUPR = 0xb9, /* YUKON-2 Supreme */ CHIP_ID_YUKON_UL_2 = 0xba, /* YUKON-2 Ultra 2 */ + CHIP_ID_YUKON_OPT = 0xbc, /* YUKON-2 Optima */ }; enum yukon_ec_rev { CHIP_REV_YU_EC_A1 = 0, /* Chip Rev. for Yukon-EC A1/A0 */ @@ -459,6 +571,8 @@ enum yukon_ex_rev { }; enum yukon_supr_rev { CHIP_REV_YU_SU_A0 = 0, + CHIP_REV_YU_SU_B0 = 1, + CHIP_REV_YU_SU_B1 = 3, }; @@ -513,6 +627,12 @@ enum { TIM_T_STEP = 1<<0, /* Test step */ }; +/* Y2_PEX_PHY_ADDR/DATA PEX PHY address and data reg (Yukon-2 only) */ +enum { + PEX_RD_ACCESS = 1<<31, /* Access Mode Read = 1, Write = 0 */ + PEX_DB_ACCESS = 1<<30, /* Access to debug register */ +}; + /* B3_RAM_ADDR 32 bit RAM Address, to read or write */ /* Bit 31..19: reserved */ #define RAM_ADR_RAN 0x0007ffffL /* Bit 18.. 0: RAM Address Range */ @@ -688,10 +808,11 @@ enum { RX_GMF_AF_THR = 0x0c44,/* 32 bit Rx GMAC FIFO Almost Full Thresh. */ RX_GMF_CTRL_T = 0x0c48,/* 32 bit Rx GMAC FIFO Control/Test */ RX_GMF_FL_MSK = 0x0c4c,/* 32 bit Rx GMAC FIFO Flush Mask */ - RX_GMF_FL_THR = 0x0c50,/* 32 bit Rx GMAC FIFO Flush Threshold */ + RX_GMF_FL_THR = 0x0c50,/* 16 bit Rx GMAC FIFO Flush Threshold */ + RX_GMF_FL_CTRL = 0x0c52,/* 16 bit Rx GMAC FIFO Flush Control */ RX_GMF_TR_THR = 0x0c54,/* 32 bit Rx Truncation Threshold (Yukon-2) */ - RX_GMF_UP_THR = 0x0c58,/* 8 bit Rx Upper Pause Thr (Yukon-EC_U) */ - RX_GMF_LP_THR = 0x0c5a,/* 8 bit Rx Lower Pause Thr (Yukon-EC_U) */ + RX_GMF_UP_THR = 0x0c58,/* 16 bit Rx Upper Pause Thr (Yukon-EC_U) */ + RX_GMF_LP_THR = 0x0c5a,/* 16 bit Rx Lower Pause Thr (Yukon-EC_U) */ RX_GMF_VLAN = 0x0c5c,/* 32 bit Rx VLAN Type Register (Yukon-2) */ RX_GMF_WP = 0x0c60,/* 32 bit Rx GMAC FIFO Write Pointer */ @@ -754,6 +875,42 @@ enum { BMU_TX_CLR_IRQ_TCP = 1<<11, /* Clear IRQ on TCP segment length mismatch */ }; +/* TBMU_TEST 0x06B8 Transmit BMU Test Register */ +enum { + TBMU_TEST_BMU_TX_CHK_AUTO_OFF = 1<<31, /* BMU Tx Checksum Auto Calculation Disable */ + TBMU_TEST_BMU_TX_CHK_AUTO_ON = 1<<30, /* BMU Tx Checksum Auto Calculation Enable */ + TBMU_TEST_HOME_ADD_PAD_FIX1_EN = 1<<29, /* Home Address Paddiing FIX1 Enable */ + TBMU_TEST_HOME_ADD_PAD_FIX1_DIS = 1<<28, /* Home Address Paddiing FIX1 Disable */ + TBMU_TEST_ROUTING_ADD_FIX_EN = 1<<27, /* Routing Address Fix Enable */ + TBMU_TEST_ROUTING_ADD_FIX_DIS = 1<<26, /* Routing Address Fix Disable */ + TBMU_TEST_HOME_ADD_FIX_EN = 1<<25, /* Home address checksum fix enable */ + TBMU_TEST_HOME_ADD_FIX_DIS = 1<<24, /* Home address checksum fix disable */ + + TBMU_TEST_TEST_RSPTR_ON = 1<<22, /* Testmode Shadow Read Ptr On */ + TBMU_TEST_TEST_RSPTR_OFF = 1<<21, /* Testmode Shadow Read Ptr Off */ + TBMU_TEST_TESTSTEP_RSPTR = 1<<20, /* Teststep Shadow Read Ptr */ + + TBMU_TEST_TEST_RPTR_ON = 1<<18, /* Testmode Read Ptr On */ + TBMU_TEST_TEST_RPTR_OFF = 1<<17, /* Testmode Read Ptr Off */ + TBMU_TEST_TESTSTEP_RPTR = 1<<16, /* Teststep Read Ptr */ + + TBMU_TEST_TEST_WSPTR_ON = 1<<14, /* Testmode Shadow Write Ptr On */ + TBMU_TEST_TEST_WSPTR_OFF = 1<<13, /* Testmode Shadow Write Ptr Off */ + TBMU_TEST_TESTSTEP_WSPTR = 1<<12, /* Teststep Shadow Write Ptr */ + + TBMU_TEST_TEST_WPTR_ON = 1<<10, /* Testmode Write Ptr On */ + TBMU_TEST_TEST_WPTR_OFF = 1<<9, /* Testmode Write Ptr Off */ + TBMU_TEST_TESTSTEP_WPTR = 1<<8, /* Teststep Write Ptr */ + + TBMU_TEST_TEST_REQ_NB_ON = 1<<6, /* Testmode Req Nbytes/Addr On */ + TBMU_TEST_TEST_REQ_NB_OFF = 1<<5, /* Testmode Req Nbytes/Addr Off */ + TBMU_TEST_TESTSTEP_REQ_NB = 1<<4, /* Teststep Req Nbytes/Addr */ + + TBMU_TEST_TEST_DONE_IDX_ON = 1<<2, /* Testmode Done Index On */ + TBMU_TEST_TEST_DONE_IDX_OFF = 1<<1, /* Testmode Done Index Off */ + TBMU_TEST_TESTSTEP_DONE_IDX = 1<<0, /* Teststep Done Index */ +}; + /* Queue Prefetch Unit Offsets, use Y2_QADDR() to address (Yukon-2 only)*/ /* PREF_UNIT_CTRL 32 bit Prefetch Control register */ enum { @@ -1674,6 +1831,12 @@ enum { /* RX_GMF_CTRL_T 32 bit Rx GMAC FIFO Control/Test */ enum { + RX_GCLKMAC_ENA = 1<<31, /* RX MAC Clock Gating Enable */ + RX_GCLKMAC_OFF = 1<<30, + + RX_STFW_DIS = 1<<29, /* RX Store and Forward Enable */ + RX_STFW_ENA = 1<<28, + RX_TRUNC_ON = 1<<27, /* enable packet truncation */ RX_TRUNC_OFF = 1<<26, /* disable packet truncation */ RX_VLAN_STRIP_ON = 1<<25, /* enable VLAN stripping */ @@ -1711,6 +1874,20 @@ enum { GMF_RX_CTRL_DEF = GMF_OPER_ON | GMF_RX_F_FL_ON, }; +/* RX_GMF_FL_CTRL 16 bit Rx GMAC FIFO Flush Control (Yukon-Supreme) */ +enum { + RX_IPV6_SA_MOB_ENA = 1<<9, /* IPv6 SA Mobility Support Enable */ + RX_IPV6_SA_MOB_DIS = 1<<8, /* IPv6 SA Mobility Support Disable */ + RX_IPV6_DA_MOB_ENA = 1<<7, /* IPv6 DA Mobility Support Enable */ + RX_IPV6_DA_MOB_DIS = 1<<6, /* IPv6 DA Mobility Support Disable */ + RX_PTR_SYNCDLY_ENA = 1<<5, /* Pointers Delay Synch Enable */ + RX_PTR_SYNCDLY_DIS = 1<<4, /* Pointers Delay Synch Disable */ + RX_ASF_NEWFLAG_ENA = 1<<3, /* RX ASF Flag New Logic Enable */ + RX_ASF_NEWFLAG_DIS = 1<<2, /* RX ASF Flag New Logic Disable */ + RX_FLSH_MISSPKT_ENA = 1<<1, /* RX Flush Miss-Packet Enable */ + RX_FLSH_MISSPKT_DIS = 1<<0, /* RX Flush Miss-Packet Disable */ +}; + /* TX_GMF_EA 32 bit Tx GMAC FIFO End Address */ enum { TX_DYN_WM_ENA = 3, /* Yukon-FE+ specific */ diff --git a/drivers/net/slip.c b/drivers/net/slip.c index fe3cebb984de..ba5bbc503446 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -80,6 +80,7 @@ #include <linux/rtnetlink.h> #include <linux/if_arp.h> #include <linux/if_slip.h> +#include <linux/compat.h> #include <linux/delay.h> #include <linux/init.h> #include "slip.h" @@ -955,8 +956,8 @@ static void slip_unesc(struct slip *sl, unsigned char s) clear_bit(SLF_KEEPTEST, &sl->flags); #endif - if (!test_and_clear_bit(SLF_ERROR, &sl->flags) - && (sl->rcount > 2)) + if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && + (sl->rcount > 2)) sl_bump(sl); clear_bit(SLF_ESCAPE, &sl->flags); sl->rcount = 0; @@ -1038,8 +1039,8 @@ static void slip_unesc6(struct slip *sl, unsigned char s) clear_bit(SLF_KEEPTEST, &sl->flags); #endif - if (!test_and_clear_bit(SLF_ERROR, &sl->flags) - && (sl->rcount > 2)) + if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && + (sl->rcount > 2)) sl_bump(sl); sl->rcount = 0; sl->xbits = 0; @@ -1169,6 +1170,27 @@ static int slip_ioctl(struct tty_struct *tty, struct file *file, } } +#ifdef CONFIG_COMPAT +static long slip_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCGIFNAME: + case SIOCGIFENCAP: + case SIOCSIFENCAP: + case SIOCSIFHWADDR: + case SIOCSKEEPALIVE: + case SIOCGKEEPALIVE: + case SIOCSOUTFILL: + case SIOCGOUTFILL: + return slip_ioctl(tty, file, cmd, + (unsigned long)compat_ptr(arg)); + } + + return -ENOIOCTLCMD; +} +#endif + /* VSV changes start here */ #ifdef CONFIG_SLIP_SMART /* function do_ioctl called from net/core/dev.c @@ -1261,6 +1283,9 @@ static struct tty_ldisc_ops sl_ldisc = { .close = slip_close, .hangup = slip_hangup, .ioctl = slip_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = slip_compat_ioctl, +#endif .receive_buf = slip_receive_buf, .write_wakeup = slip_write_wakeup, }; diff --git a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c index c791ef76c1d6..a93f122e9a96 100644 --- a/drivers/net/smc-mca.c +++ b/drivers/net/smc-mca.c @@ -268,9 +268,9 @@ static int __init ultramca_probe(struct device *gen_dev) } } - if(!tirq || !tbase - || (irq && irq != tirq) - || (base_addr && tbase != base_addr)) + if(!tirq || !tbase || + (irq && irq != tirq) || + (base_addr && tbase != base_addr)) /* FIXME: we're trying to force the ordering of the * devices here, there should be a way of getting this * to happen */ diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 2a6b6de95339..44ebbaa7457b 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -1984,7 +1984,7 @@ static int __devinit smc911x_probe(struct net_device *dev) #endif /* Grab the IRQ */ - retval = request_irq(dev->irq, &smc911x_interrupt, + retval = request_irq(dev->irq, smc911x_interrupt, irq_flags, dev->name, dev); if (retval) goto err_out; diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c index 934a12012829..8371b82323ac 100644 --- a/drivers/net/smc9194.c +++ b/drivers/net/smc9194.c @@ -1050,7 +1050,7 @@ static int __init smc_probe(struct net_device *dev, int ioaddr) memset(netdev_priv(dev), 0, sizeof(struct smc_local)); /* Grab the IRQ */ - retval = request_irq(dev->irq, &smc_interrupt, 0, DRV_NAME, dev); + retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev); if (retval) { printk("%s: unable to get IRQ %d (irqval=%d).\n", DRV_NAME, dev->irq, retval); diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index f12206bdbb75..ae4983a5127d 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -2031,7 +2031,7 @@ static int __devinit smc_probe(struct net_device *dev, void __iomem *ioaddr, } /* Grab the IRQ */ - retval = request_irq(dev->irq, &smc_interrupt, irq_flags, dev->name, dev); + retval = request_irq(dev->irq, smc_interrupt, irq_flags, dev->name, dev); if (retval) goto err_out; @@ -2365,9 +2365,10 @@ static int __devexit smc_drv_remove(struct platform_device *pdev) return 0; } -static int smc_drv_suspend(struct platform_device *dev, pm_message_t state) +static int smc_drv_suspend(struct device *dev) { - struct net_device *ndev = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = platform_get_drvdata(pdev); if (ndev) { if (netif_running(ndev)) { @@ -2379,9 +2380,10 @@ static int smc_drv_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int smc_drv_resume(struct platform_device *dev) +static int smc_drv_resume(struct device *dev) { - struct net_device *ndev = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = platform_get_drvdata(pdev); if (ndev) { struct smc_local *lp = netdev_priv(ndev); @@ -2397,14 +2399,18 @@ static int smc_drv_resume(struct platform_device *dev) return 0; } +static struct dev_pm_ops smc_drv_pm_ops = { + .suspend = smc_drv_suspend, + .resume = smc_drv_resume, +}; + static struct platform_driver smc_driver = { .probe = smc_drv_probe, .remove = __devexit_p(smc_drv_remove), - .suspend = smc_drv_suspend, - .resume = smc_drv_resume, .driver = { .name = CARDNAME, .owner = THIS_MODULE, + .pm = &smc_drv_pm_ops, }, }; diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 3911be7c0cba..7815bfc300f5 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -158,8 +158,8 @@ static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l)) #define SMC_IRQ_FLAGS (-1) /* from resource */ -#elif defined(CONFIG_MACH_LOGICPD_PXA270) \ - || defined(CONFIG_MACH_NOMADIK_8815NHK) +#elif defined(CONFIG_MACH_LOGICPD_PXA270) || \ + defined(CONFIG_MACH_NOMADIK_8815NHK) #define SMC_CAN_USE_8BIT 0 #define SMC_CAN_USE_16BIT 1 @@ -258,9 +258,9 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define RPC_LSA_DEFAULT RPC_LED_TX_RX #define RPC_LSB_DEFAULT RPC_LED_100_10 -#elif defined(CONFIG_MACH_LPD79520) \ - || defined(CONFIG_MACH_LPD7A400) \ - || defined(CONFIG_MACH_LPD7A404) +#elif defined(CONFIG_MACH_LPD79520) || \ + defined(CONFIG_MACH_LPD7A400) || \ + defined(CONFIG_MACH_LPD7A404) /* The LPD7X_IOBARRIER is necessary to overcome a mismatch between the * way that the CPU handles chip selects and the way that the SMC chip diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index f9cdcbcb77d4..20d6095cf411 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -748,8 +748,8 @@ static void smsc911x_phy_adjust_link(struct net_device *dev) * usage is 10/100 indicator */ pdata->gpio_setting = smsc911x_reg_read(pdata, GPIO_CFG); - if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) - && (!pdata->using_extphy)) { + if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) && + (!pdata->using_extphy)) { /* Force 10/100 LED off, after saving * orginal GPIO configuration */ pdata->gpio_orig_setting = pdata->gpio_setting; @@ -816,7 +816,7 @@ static int smsc911x_mii_probe(struct net_device *dev) SMSC_TRACE(HW, "Passed Loop Back Test"); #endif /* USE_PHY_WORK_AROUND */ - SMSC_TRACE(HW, "phy initialised succesfully"); + SMSC_TRACE(HW, "phy initialised successfully"); return 0; } @@ -2071,6 +2071,9 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) if (is_valid_ether_addr(dev->dev_addr)) { smsc911x_set_hw_mac_address(pdata, dev->dev_addr); SMSC_TRACE(PROBE, "MAC Address is specified by configuration"); + } else if (is_valid_ether_addr(pdata->config.mac)) { + memcpy(dev->dev_addr, pdata->config.mac, 6); + SMSC_TRACE(PROBE, "MAC Address specified by platform data"); } else { /* Try reading mac address from device. if EEPROM is present * it will already have been set */ diff --git a/drivers/net/smsc911x.h b/drivers/net/smsc911x.h index b5716bd8a597..016360c65ce2 100644 --- a/drivers/net/smsc911x.h +++ b/drivers/net/smsc911x.h @@ -30,7 +30,7 @@ #define SMSC_NAPI_WEIGHT 16 /* implements a PHY loopback test at initialisation time, to ensure a packet - * can be succesfully looped back */ + * can be successfully looped back */ #define USE_PHY_WORK_AROUND #define DPRINTK(nlevel, klevel, fmt, args...) \ diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index 0f7909276237..12f0f5d74e3c 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -1175,7 +1175,7 @@ static int smsc9420_mii_probe(struct net_device *dev) phydev->phy_id); phydev = phy_connect(dev, dev_name(&phydev->dev), - &smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII); + smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { pr_err("%s: Could not attach to PHY\n", dev->name); diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 90e663f4515c..218524857bfc 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -57,6 +57,7 @@ MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com> and Jens Osterkamp " \ MODULE_DESCRIPTION("Spider Southbridge Gigabit Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(VERSION); +MODULE_FIRMWARE(SPIDER_NET_FIRMWARE_NAME); static int rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_DEFAULT; static int tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_DEFAULT; @@ -409,7 +410,7 @@ spider_net_free_rx_chain_contents(struct spider_net_card *card) * @card: card structure * @descr: descriptor to re-init * - * Return 0 on succes, <0 on failure. + * Return 0 on success, <0 on failure. * * Allocates a new rx skb, iommu-maps it and attaches it to the * descriptor. Mark the descriptor as activated, ready-to-use. diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index a36e2b51e88c..95db60adde41 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -928,7 +928,7 @@ static int netdev_open(struct net_device *dev) /* Do we ever need to reset the chip??? */ - retval = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev); + retval = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev); if (retval) return retval; @@ -1482,8 +1482,8 @@ static int __netdev_rx(struct net_device *dev, int *quota) printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d, quota %d.\n", pkt_len, *quota); /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + if (pkt_len < rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev, np->rx_info[entry].mapping, @@ -1793,8 +1793,8 @@ static void set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ rx_mode |= AcceptAll; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ rx_mode |= AcceptBroadcast|AcceptAllMulticast|PerfectFilter; } else if (dev->mc_count <= 14) { diff --git a/drivers/net/stmmac/gmac.c b/drivers/net/stmmac/gmac.c index b624bb5bae0a..52586ee68953 100644 --- a/drivers/net/stmmac/gmac.c +++ b/drivers/net/stmmac/gmac.c @@ -112,7 +112,7 @@ static void gmac_dma_operation_mode(unsigned long ioaddr, int txmode, " (threshold = %d)\n", txmode); csr6 &= ~DMA_CONTROL_TSF; csr6 &= DMA_CONTROL_TC_TX_MASK; - /* Set the transmit threashold */ + /* Set the transmit threshold */ if (txmode <= 32) csr6 |= DMA_CONTROL_TTC_32; else if (txmode <= 64) diff --git a/drivers/net/stmmac/gmac.h b/drivers/net/stmmac/gmac.h index 684a363120a9..2e82d6c9a148 100644 --- a/drivers/net/stmmac/gmac.h +++ b/drivers/net/stmmac/gmac.h @@ -154,14 +154,14 @@ enum rx_tx_priority_ratio { #define DMA_CONTROL_DT 0x04000000 /* Disable Drop TCP/IP csum error */ #define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */ #define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */ -/* Theshold for Activating the FC */ +/* Threshold for Activating the FC */ enum rfa { act_full_minus_1 = 0x00800000, act_full_minus_2 = 0x00800200, act_full_minus_3 = 0x00800400, act_full_minus_4 = 0x00800600, }; -/* Theshold for Deactivating the FC */ +/* Threshold for Deactivating the FC */ enum rfd { deac_full_minus_1 = 0x00400000, deac_full_minus_2 = 0x00400800, diff --git a/drivers/net/stmmac/stmmac_main.c b/drivers/net/stmmac/stmmac_main.c index 9542995ba667..508fba8fa07f 100644 --- a/drivers/net/stmmac/stmmac_main.c +++ b/drivers/net/stmmac/stmmac_main.c @@ -922,8 +922,7 @@ static void stmmac_dma_interrupt(struct net_device *dev) DBG(intr, INFO, "CSR5[15] DMA ABNORMAL IRQ: "); if (unlikely(intr_status & DMA_STATUS_UNF)) { DBG(intr, INFO, "transmit underflow\n"); - if (unlikely(tc != SF_DMA_MODE) - && (tc <= 256)) { + if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) { /* Try to bump up the threshold */ tc += 64; priv->mac_type->ops->dma_mode(ioaddr, tc, @@ -1024,7 +1023,7 @@ static int stmmac_open(struct net_device *dev) } /* Request the IRQ lines */ - ret = request_irq(dev->irq, &stmmac_interrupt, + ret = request_irq(dev->irq, stmmac_interrupt, IRQF_SHARED, dev->name, dev); if (unlikely(ret < 0)) { pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n", diff --git a/drivers/net/sun3_82586.c b/drivers/net/sun3_82586.c index 2f1eaaf7a727..b447a8719427 100644 --- a/drivers/net/sun3_82586.c +++ b/drivers/net/sun3_82586.c @@ -191,7 +191,7 @@ static int sun3_82586_open(struct net_device *dev) startrecv586(dev); sun3_enaint(); - ret = request_irq(dev->irq, &sun3_82586_interrupt,0,dev->name,dev); + ret = request_irq(dev->irq, sun3_82586_interrupt,0,dev->name,dev); if (ret) { sun3_reset586(); diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index 536cf7e06bfd..25e81ebd9cd8 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -919,7 +919,7 @@ static int bigmac_open(struct net_device *dev) struct bigmac *bp = netdev_priv(dev); int ret; - ret = request_irq(dev->irq, &bigmac_interrupt, IRQF_SHARED, dev->name, bp); + ret = request_irq(dev->irq, bigmac_interrupt, IRQF_SHARED, dev->name, bp); if (ret) { printk(KERN_ERR "BIGMAC: Can't order irq %d to go.\n", dev->irq); return ret; diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index e13685a570f4..d58e1891ca60 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -603,8 +603,8 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev, strcmp (media[card_idx], "4") == 0) { np->speed = 100; np->mii_if.full_duplex = 1; - } else if (strcmp (media[card_idx], "100mbps_hd") == 0 - || strcmp (media[card_idx], "3") == 0) { + } else if (strcmp (media[card_idx], "100mbps_hd") == 0 || + strcmp (media[card_idx], "3") == 0) { np->speed = 100; np->mii_if.full_duplex = 0; } else if (strcmp (media[card_idx], "10mbps_fd") == 0 || @@ -819,7 +819,7 @@ static int netdev_open(struct net_device *dev) /* Do we need to reset the chip??? */ - i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev); + i = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev); if (i) return i; @@ -1079,8 +1079,8 @@ start_tx (struct sk_buff *skb, struct net_device *dev) tasklet_schedule(&np->tx_tasklet); /* On some architectures: explicitly flush cache lines here. */ - if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1 - && !netif_queue_stopped(dev)) { + if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1 && + !netif_queue_stopped(dev)) { /* do nothing */ } else { netif_stop_queue (dev); @@ -1336,8 +1336,8 @@ static void rx_poll(unsigned long data) #endif /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + if (pkt_len < rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev, le32_to_cpu(desc->frag[0].addr), @@ -1517,8 +1517,8 @@ static void set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 61640b99b705..b571a1babab9 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -1034,10 +1034,8 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb, (csum_stuff_off << 21)); } - local_irq_save(flags); - if (!spin_trylock(&gp->tx_lock)) { + if (!spin_trylock_irqsave(&gp->tx_lock, flags)) { /* Tell upper layer to requeue */ - local_irq_restore(flags); return NETDEV_TX_LOCKED; } /* We raced with gem_do_stop() */ diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h index f7a02917ce5e..19905460def6 100644 --- a/drivers/net/sungem.h +++ b/drivers/net/sungem.h @@ -1031,8 +1031,8 @@ struct gem { #endif }; -#define found_mii_phy(gp) ((gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) \ - && gp->phy_mii.def && gp->phy_mii.def->ops) +#define found_mii_phy(gp) ((gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) && \ + gp->phy_mii.def && gp->phy_mii.def->ops) #define ALIGNED_RX_SKB_ADDR(addr) \ ((((unsigned long)(addr) + (64UL - 1UL)) & ~(64UL - 1UL)) - (unsigned long)(addr)) diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index 37d721bbdb35..6762f1c6ec8a 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -1226,10 +1226,16 @@ static void happy_meal_clean_rings(struct happy_meal *hp) for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { txd = &hp->happy_block->happy_meal_txd[i]; dma_addr = hme_read_desc32(hp, &txd->tx_addr); - dma_unmap_single(hp->dma_dev, dma_addr, - (hme_read_desc32(hp, &txd->tx_flags) - & TXFLAG_SIZE), - DMA_TO_DEVICE); + if (!frag) + dma_unmap_single(hp->dma_dev, dma_addr, + (hme_read_desc32(hp, &txd->tx_flags) + & TXFLAG_SIZE), + DMA_TO_DEVICE); + else + dma_unmap_page(hp->dma_dev, dma_addr, + (hme_read_desc32(hp, &txd->tx_flags) + & TXFLAG_SIZE), + DMA_TO_DEVICE); if (frag != skb_shinfo(skb)->nr_frags) i++; @@ -1953,7 +1959,10 @@ static void happy_meal_tx(struct happy_meal *hp) dma_len = hme_read_desc32(hp, &this->tx_flags); dma_len &= TXFLAG_SIZE; - dma_unmap_single(hp->dma_dev, dma_addr, dma_len, DMA_TO_DEVICE); + if (!frag) + dma_unmap_single(hp->dma_dev, dma_addr, dma_len, DMA_TO_DEVICE); + else + dma_unmap_page(hp->dma_dev, dma_addr, dma_len, DMA_TO_DEVICE); elem = NEXT_TX(elem); this = &txbase[elem]; @@ -2184,7 +2193,7 @@ static int happy_meal_open(struct net_device *dev) * into a single source which we register handling at probe time. */ if ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO) { - if (request_irq(dev->irq, &happy_meal_interrupt, + if (request_irq(dev->irq, happy_meal_interrupt, IRQF_SHARED, dev->name, (void *)dev)) { HMD(("EAGAIN\n")); printk(KERN_ERR "happy_meal(SBUS): Can't order irq %d to go.\n", @@ -3047,9 +3056,9 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, int len; if (qfe_slot != -1 && - (addr = of_get_property(dp, - "local-mac-address", &len)) != NULL - && len == 6) { + (addr = of_get_property(dp, "local-mac-address", &len)) + != NULL && + len == 6) { memcpy(dev->dev_addr, addr, 6); } else { memcpy(dev->dev_addr, idprom->id_ethaddr, 6); diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index 9d6fd4760eab..64e7d08c878f 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -923,7 +923,7 @@ static int lance_open(struct net_device *dev) STOP_LANCE(lp); - if (request_irq(dev->irq, &lance_interrupt, IRQF_SHARED, + if (request_irq(dev->irq, lance_interrupt, IRQF_SHARED, lancestr, (void *) dev)) { printk(KERN_ERR "Lance: Can't get irq %d\n", dev->irq); return -EAGAIN; diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index dcefb608a9f4..45c383f285ee 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -807,7 +807,7 @@ static struct sunqec * __devinit get_qec(struct of_device *child) qec_init_once(qecp, op); - if (request_irq(op->irqs[0], &qec_interrupt, + if (request_irq(op->irqs[0], qec_interrupt, IRQF_SHARED, "qec", (void *) qecp)) { printk(KERN_ERR "qec: Can't register irq.\n"); goto fail; diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index d1298e5b72c5..75a669d48e5e 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -22,11 +22,7 @@ * All Rights Reserved. */ -#ifdef TC35815_NAPI -#define DRV_VERSION "1.38-NAPI" -#else -#define DRV_VERSION "1.38" -#endif +#define DRV_VERSION "1.39" static const char *version = "tc35815.c:v" DRV_VERSION "\n"; #define MODNAME "tc35815" @@ -54,13 +50,6 @@ static const char *version = "tc35815.c:v" DRV_VERSION "\n"; #include <asm/io.h> #include <asm/byteorder.h> -/* First, a few definitions that the brave might change. */ - -#define GATHER_TXINT /* On-Demand Tx Interrupt */ -#define WORKAROUND_LOSTCAR -#define WORKAROUND_100HALF_PROMISC -/* #define TC35815_USE_PACKEDBUFFER */ - enum tc35815_chiptype { TC35815CF = 0, TC35815_NWU, @@ -330,17 +319,10 @@ struct BDesc { /* Some useful constants. */ -#undef NO_CHECK_CARRIER /* Does not check No-Carrier with TP */ -#ifdef NO_CHECK_CARRIER -#define TX_CTL_CMD (Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \ - Tx_EnExColl | Tx_EnExDefer | Tx_EnUnder | \ - Tx_En) /* maybe 0x7b01 */ -#else -#define TX_CTL_CMD (Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \ +#define TX_CTL_CMD (Tx_EnTxPar | Tx_EnLateColl | \ Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \ Tx_En) /* maybe 0x7b01 */ -#endif /* Do not use Rx_StripCRC -- it causes trouble on BLEx/FDAEx condition */ #define RX_CTL_CMD (Rx_EnGood | Rx_EnRxPar | Rx_EnLongErr | Rx_EnOver \ | Rx_EnCRCErr | Rx_EnAlign | Rx_RxEn) /* maybe 0x6f01 */ @@ -361,13 +343,6 @@ struct BDesc { #define TX_THRESHOLD_KEEP_LIMIT 10 /* 16 + RX_BUF_NUM * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*FD_PAGE_NUM */ -#ifdef TC35815_USE_PACKEDBUFFER -#define FD_PAGE_NUM 2 -#define RX_BUF_NUM 8 /* >= 2 */ -#define RX_FD_NUM 250 /* >= 32 */ -#define TX_FD_NUM 128 -#define RX_BUF_SIZE PAGE_SIZE -#else /* TC35815_USE_PACKEDBUFFER */ #define FD_PAGE_NUM 4 #define RX_BUF_NUM 128 /* < 256 */ #define RX_FD_NUM 256 /* >= 32 */ @@ -381,7 +356,6 @@ struct BDesc { #define RX_BUF_SIZE \ L1_CACHE_ALIGN(ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN + NET_IP_ALIGN) #endif -#endif /* TC35815_USE_PACKEDBUFFER */ #define RX_FD_RESERVE (2 / 2) /* max 2 BD per RxFD */ #define NAPI_WEIGHT 16 @@ -439,11 +413,7 @@ struct tc35815_local { /* * Transmitting: Batch Mode. * 1 BD in 1 TxFD. - * Receiving: Packing Mode. (TC35815_USE_PACKEDBUFFER) - * 1 circular FD for Free Buffer List. - * RX_BUF_NUM BD in Free Buffer FD. - * One Free Buffer BD has PAGE_SIZE data buffer. - * Or Non-Packing Mode. + * Receiving: Non-Packing Mode. * 1 circular FD for Free Buffer List. * RX_BUF_NUM BD in Free Buffer FD. * One Free Buffer BD has ETH_FRAME_LEN data buffer. @@ -457,21 +427,11 @@ struct tc35815_local { struct RxFD *rfd_limit; struct RxFD *rfd_cur; struct FrFD *fbl_ptr; -#ifdef TC35815_USE_PACKEDBUFFER - unsigned char fbl_curid; - void *data_buf[RX_BUF_NUM]; /* packing */ - dma_addr_t data_buf_dma[RX_BUF_NUM]; - struct { - struct sk_buff *skb; - dma_addr_t skb_dma; - } tx_skbs[TX_FD_NUM]; -#else unsigned int fbl_count; struct { struct sk_buff *skb; dma_addr_t skb_dma; } tx_skbs[TX_FD_NUM], rx_skbs[RX_BUF_NUM]; -#endif u32 msg_enable; enum tc35815_chiptype chiptype; }; @@ -486,51 +446,6 @@ static inline void *fd_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus) return (void *)((u8 *)lp->fd_buf + (bus - lp->fd_buf_dma)); } #endif -#ifdef TC35815_USE_PACKEDBUFFER -static inline void *rxbuf_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus) -{ - int i; - for (i = 0; i < RX_BUF_NUM; i++) { - if (bus >= lp->data_buf_dma[i] && - bus < lp->data_buf_dma[i] + PAGE_SIZE) - return (void *)((u8 *)lp->data_buf[i] + - (bus - lp->data_buf_dma[i])); - } - return NULL; -} - -#define TC35815_DMA_SYNC_ONDEMAND -static void *alloc_rxbuf_page(struct pci_dev *hwdev, dma_addr_t *dma_handle) -{ -#ifdef TC35815_DMA_SYNC_ONDEMAND - void *buf; - /* pci_map + pci_dma_sync will be more effective than - * pci_alloc_consistent on some archs. */ - buf = (void *)__get_free_page(GFP_ATOMIC); - if (!buf) - return NULL; - *dma_handle = pci_map_single(hwdev, buf, PAGE_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(hwdev, *dma_handle)) { - free_page((unsigned long)buf); - return NULL; - } - return buf; -#else - return pci_alloc_consistent(hwdev, PAGE_SIZE, dma_handle); -#endif -} - -static void free_rxbuf_page(struct pci_dev *hwdev, void *buf, dma_addr_t dma_handle) -{ -#ifdef TC35815_DMA_SYNC_ONDEMAND - pci_unmap_single(hwdev, dma_handle, PAGE_SIZE, PCI_DMA_FROMDEVICE); - free_page((unsigned long)buf); -#else - pci_free_consistent(hwdev, PAGE_SIZE, buf, dma_handle); -#endif -} -#else /* TC35815_USE_PACKEDBUFFER */ static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev, struct pci_dev *hwdev, dma_addr_t *dma_handle) @@ -555,19 +470,14 @@ static void free_rxbuf_skb(struct pci_dev *hwdev, struct sk_buff *skb, dma_addr_ PCI_DMA_FROMDEVICE); dev_kfree_skb_any(skb); } -#endif /* TC35815_USE_PACKEDBUFFER */ /* Index to functions, as function prototypes. */ static int tc35815_open(struct net_device *dev); static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev); static irqreturn_t tc35815_interrupt(int irq, void *dev_id); -#ifdef TC35815_NAPI static int tc35815_rx(struct net_device *dev, int limit); static int tc35815_poll(struct napi_struct *napi, int budget); -#else -static void tc35815_rx(struct net_device *dev); -#endif static void tc35815_txdone(struct net_device *dev); static int tc35815_close(struct net_device *dev); static struct net_device_stats *tc35815_get_stats(struct net_device *dev); @@ -654,8 +564,6 @@ static void tc_handle_link_change(struct net_device *dev) * TX4939 PCFG.SPEEDn bit will be changed on * NETDEV_CHANGE event. */ - -#if !defined(NO_CHECK_CARRIER) && defined(WORKAROUND_LOSTCAR) /* * WORKAROUND: enable LostCrS only if half duplex * operation. @@ -665,7 +573,6 @@ static void tc_handle_link_change(struct net_device *dev) lp->chiptype != TC35815_TX4939) tc_writel(tc_readl(&tr->Tx_Ctl) | Tx_EnLCarr, &tr->Tx_Ctl); -#endif lp->speed = phydev->speed; lp->duplex = phydev->duplex; @@ -674,11 +581,9 @@ static void tc_handle_link_change(struct net_device *dev) if (phydev->link != lp->link) { if (phydev->link) { -#ifdef WORKAROUND_100HALF_PROMISC /* delayed promiscuous enabling */ if (dev->flags & IFF_PROMISC) tc35815_set_multicast_list(dev); -#endif } else { lp->speed = 0; lp->duplex = -1; @@ -923,9 +828,7 @@ static int __devinit tc35815_init_one(struct pci_dev *pdev, dev->netdev_ops = &tc35815_netdev_ops; dev->ethtool_ops = &tc35815_ethtool_ops; dev->watchdog_timeo = TC35815_TX_TIMEOUT; -#ifdef TC35815_NAPI netif_napi_add(dev, &lp->napi, tc35815_poll, NAPI_WEIGHT); -#endif dev->irq = pdev->irq; dev->base_addr = (unsigned long)ioaddr; @@ -1007,25 +910,6 @@ tc35815_init_queues(struct net_device *dev) if (!lp->fd_buf) return -ENOMEM; for (i = 0; i < RX_BUF_NUM; i++) { -#ifdef TC35815_USE_PACKEDBUFFER - lp->data_buf[i] = - alloc_rxbuf_page(lp->pci_dev, - &lp->data_buf_dma[i]); - if (!lp->data_buf[i]) { - while (--i >= 0) { - free_rxbuf_page(lp->pci_dev, - lp->data_buf[i], - lp->data_buf_dma[i]); - lp->data_buf[i] = NULL; - } - pci_free_consistent(lp->pci_dev, - PAGE_SIZE * FD_PAGE_NUM, - lp->fd_buf, - lp->fd_buf_dma); - lp->fd_buf = NULL; - return -ENOMEM; - } -#else lp->rx_skbs[i].skb = alloc_rxbuf_skb(dev, lp->pci_dev, &lp->rx_skbs[i].skb_dma); @@ -1043,15 +927,9 @@ tc35815_init_queues(struct net_device *dev) lp->fd_buf = NULL; return -ENOMEM; } -#endif } printk(KERN_DEBUG "%s: FD buf %p DataBuf", dev->name, lp->fd_buf); -#ifdef TC35815_USE_PACKEDBUFFER - printk(" DataBuf"); - for (i = 0; i < RX_BUF_NUM; i++) - printk(" %p", lp->data_buf[i]); -#endif printk("\n"); } else { for (i = 0; i < FD_PAGE_NUM; i++) @@ -1084,7 +962,6 @@ tc35815_init_queues(struct net_device *dev) lp->fbl_ptr = (struct FrFD *)fd_addr; lp->fbl_ptr->fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, lp->fbl_ptr)); lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_NUM | FD_CownsFD); -#ifndef TC35815_USE_PACKEDBUFFER /* * move all allocated skbs to head of rx_skbs[] array. * fbl_count mighe not be RX_BUF_NUM if alloc_rxbuf_skb() in @@ -1102,11 +979,7 @@ tc35815_init_queues(struct net_device *dev) lp->fbl_count++; } } -#endif for (i = 0; i < RX_BUF_NUM; i++) { -#ifdef TC35815_USE_PACKEDBUFFER - lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(lp->data_buf_dma[i]); -#else if (i >= lp->fbl_count) { lp->fbl_ptr->bd[i].BuffData = 0; lp->fbl_ptr->bd[i].BDCtl = 0; @@ -1114,15 +987,11 @@ tc35815_init_queues(struct net_device *dev) } lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(lp->rx_skbs[i].skb_dma); -#endif /* BDID is index of FrFD.bd[] */ lp->fbl_ptr->bd[i].BDCtl = cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | RX_BUF_SIZE); } -#ifdef TC35815_USE_PACKEDBUFFER - lp->fbl_curid = 0; -#endif printk(KERN_DEBUG "%s: TxFD %p RxFD %p FrFD %p\n", dev->name, lp->tfd_base, lp->rfd_base, lp->fbl_ptr); @@ -1196,19 +1065,11 @@ tc35815_free_queues(struct net_device *dev) lp->fbl_ptr = NULL; for (i = 0; i < RX_BUF_NUM; i++) { -#ifdef TC35815_USE_PACKEDBUFFER - if (lp->data_buf[i]) { - free_rxbuf_page(lp->pci_dev, - lp->data_buf[i], lp->data_buf_dma[i]); - lp->data_buf[i] = NULL; - } -#else if (lp->rx_skbs[i].skb) { free_rxbuf_skb(lp->pci_dev, lp->rx_skbs[i].skb, lp->rx_skbs[i].skb_dma); lp->rx_skbs[i].skb = NULL; } -#endif } if (lp->fd_buf) { pci_free_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM, @@ -1254,7 +1115,7 @@ dump_rxfd(struct RxFD *fd) return bd_count; } -#if defined(DEBUG) || defined(TC35815_USE_PACKEDBUFFER) +#ifdef DEBUG static void dump_frfd(struct FrFD *fd) { @@ -1271,9 +1132,7 @@ dump_frfd(struct FrFD *fd) le32_to_cpu(fd->bd[i].BDCtl)); printk("\n"); } -#endif -#ifdef DEBUG static void panic_queues(struct net_device *dev) { @@ -1389,7 +1248,7 @@ tc35815_open(struct net_device *dev) * This is used if the interrupt line can turned off (shared). * See 3c503.c for an example of selecting the IRQ at config-time. */ - if (request_irq(dev->irq, &tc35815_interrupt, IRQF_SHARED, + if (request_irq(dev->irq, tc35815_interrupt, IRQF_SHARED, dev->name, dev)) return -EAGAIN; @@ -1400,9 +1259,7 @@ tc35815_open(struct net_device *dev) return -EAGAIN; } -#ifdef TC35815_NAPI napi_enable(&lp->napi); -#endif /* Reset the hardware here. Don't forget to set the station address. */ spin_lock_irq(&lp->lock); @@ -1478,9 +1335,7 @@ static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev) (struct tc35815_regs __iomem *)dev->base_addr; /* Start DMA Transmitter. */ txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL); -#ifdef GATHER_TXINT txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); -#endif if (netif_msg_tx_queued(lp)) { printk("%s: starting TxFD.\n", dev->name); dump_txfd(txfd); @@ -1536,11 +1391,7 @@ static void tc35815_fatal_error_interrupt(struct net_device *dev, u32 status) tc35815_schedule_restart(dev); } -#ifdef TC35815_NAPI static int tc35815_do_interrupt(struct net_device *dev, u32 status, int limit) -#else -static int tc35815_do_interrupt(struct net_device *dev, u32 status) -#endif { struct tc35815_local *lp = netdev_priv(dev); int ret = -1; @@ -1579,12 +1430,7 @@ static int tc35815_do_interrupt(struct net_device *dev, u32 status) /* normal notification */ if (status & Int_IntMacRx) { /* Got a packet(s). */ -#ifdef TC35815_NAPI ret = tc35815_rx(dev, limit); -#else - tc35815_rx(dev); - ret = 0; -#endif lp->lstats.rx_ints++; } if (status & Int_IntMacTx) { @@ -1592,7 +1438,8 @@ static int tc35815_do_interrupt(struct net_device *dev, u32 status) lp->lstats.tx_ints++; tc35815_txdone(dev); netif_wake_queue(dev); - ret = 0; + if (ret < 0) + ret = 0; } return ret; } @@ -1607,7 +1454,6 @@ static irqreturn_t tc35815_interrupt(int irq, void *dev_id) struct tc35815_local *lp = netdev_priv(dev); struct tc35815_regs __iomem *tr = (struct tc35815_regs __iomem *)dev->base_addr; -#ifdef TC35815_NAPI u32 dmactl = tc_readl(&tr->DMA_Ctl); if (!(dmactl & DMA_IntMask)) { @@ -1624,22 +1470,6 @@ static irqreturn_t tc35815_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } return IRQ_NONE; -#else - int handled; - u32 status; - - spin_lock(&lp->lock); - status = tc_readl(&tr->Int_Src); - /* BLEx, FDAEx will be cleared later */ - tc_writel(status & ~(Int_BLEx | Int_FDAEx), - &tr->Int_Src); /* write to clear */ - handled = tc35815_do_interrupt(dev, status); - if (status & (Int_BLEx | Int_FDAEx)) - tc_writel(status & (Int_BLEx | Int_FDAEx), &tr->Int_Src); - (void)tc_readl(&tr->Int_Src); /* flush */ - spin_unlock(&lp->lock); - return IRQ_RETVAL(handled >= 0); -#endif /* TC35815_NAPI */ } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -1652,20 +1482,13 @@ static void tc35815_poll_controller(struct net_device *dev) #endif /* We have a good packet(s), get it/them out of the buffers. */ -#ifdef TC35815_NAPI static int tc35815_rx(struct net_device *dev, int limit) -#else -static void -tc35815_rx(struct net_device *dev) -#endif { struct tc35815_local *lp = netdev_priv(dev); unsigned int fdctl; int i; -#ifdef TC35815_NAPI int received = 0; -#endif while (!((fdctl = le32_to_cpu(lp->rfd_cur->fd.FDCtl)) & FD_CownsFD)) { int status = le32_to_cpu(lp->rfd_cur->fd.FDStat); @@ -1684,52 +1507,9 @@ tc35815_rx(struct net_device *dev) struct sk_buff *skb; unsigned char *data; int cur_bd; -#ifdef TC35815_USE_PACKEDBUFFER - int offset; -#endif -#ifdef TC35815_NAPI if (--limit < 0) break; -#endif -#ifdef TC35815_USE_PACKEDBUFFER - BUG_ON(bd_count > 2); - skb = dev_alloc_skb(pkt_len + NET_IP_ALIGN); - if (skb == NULL) { - printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", - dev->name); - dev->stats.rx_dropped++; - break; - } - skb_reserve(skb, NET_IP_ALIGN); - - data = skb_put(skb, pkt_len); - - /* copy from receive buffer */ - cur_bd = 0; - offset = 0; - while (offset < pkt_len && cur_bd < bd_count) { - int len = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BDCtl) & - BD_BuffLength_MASK; - dma_addr_t dma = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BuffData); - void *rxbuf = rxbuf_bus_to_virt(lp, dma); - if (offset + len > pkt_len) - len = pkt_len - offset; -#ifdef TC35815_DMA_SYNC_ONDEMAND - pci_dma_sync_single_for_cpu(lp->pci_dev, - dma, len, - PCI_DMA_FROMDEVICE); -#endif - memcpy(data + offset, rxbuf, len); -#ifdef TC35815_DMA_SYNC_ONDEMAND - pci_dma_sync_single_for_device(lp->pci_dev, - dma, len, - PCI_DMA_FROMDEVICE); -#endif - offset += len; - cur_bd++; - } -#else /* TC35815_USE_PACKEDBUFFER */ BUG_ON(bd_count > 1); cur_bd = (le32_to_cpu(lp->rfd_cur->bd[0].BDCtl) & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; @@ -1757,16 +1537,11 @@ tc35815_rx(struct net_device *dev) memmove(skb->data, skb->data - NET_IP_ALIGN, pkt_len); data = skb_put(skb, pkt_len); -#endif /* TC35815_USE_PACKEDBUFFER */ if (netif_msg_pktdata(lp)) print_eth(data); skb->protocol = eth_type_trans(skb, dev); -#ifdef TC35815_NAPI netif_receive_skb(skb); received++; -#else - netif_rx(skb); -#endif dev->stats.rx_packets++; dev->stats.rx_bytes += pkt_len; } else { @@ -1803,19 +1578,11 @@ tc35815_rx(struct net_device *dev) BUG_ON(id >= RX_BUF_NUM); #endif /* free old buffers */ -#ifdef TC35815_USE_PACKEDBUFFER - while (lp->fbl_curid != id) -#else lp->fbl_count--; while (lp->fbl_count < RX_BUF_NUM) -#endif { -#ifdef TC35815_USE_PACKEDBUFFER - unsigned char curid = lp->fbl_curid; -#else unsigned char curid = (id + 1 + lp->fbl_count) % RX_BUF_NUM; -#endif struct BDesc *bd = &lp->fbl_ptr->bd[curid]; #ifdef DEBUG bdctl = le32_to_cpu(bd->BDCtl); @@ -1826,7 +1593,6 @@ tc35815_rx(struct net_device *dev) } #endif /* pass BD to controller */ -#ifndef TC35815_USE_PACKEDBUFFER if (!lp->rx_skbs[curid].skb) { lp->rx_skbs[curid].skb = alloc_rxbuf_skb(dev, @@ -1836,21 +1602,11 @@ tc35815_rx(struct net_device *dev) break; /* try on next reception */ bd->BuffData = cpu_to_le32(lp->rx_skbs[curid].skb_dma); } -#endif /* TC35815_USE_PACKEDBUFFER */ /* Note: BDLength was modified by chip. */ bd->BDCtl = cpu_to_le32(BD_CownsBD | (curid << BD_RxBDID_SHIFT) | RX_BUF_SIZE); -#ifdef TC35815_USE_PACKEDBUFFER - lp->fbl_curid = (curid + 1) % RX_BUF_NUM; - if (netif_msg_rx_status(lp)) { - printk("%s: Entering new FBD %d\n", - dev->name, lp->fbl_curid); - dump_frfd(lp->fbl_ptr); - } -#else lp->fbl_count++; -#endif } } @@ -1882,12 +1638,9 @@ tc35815_rx(struct net_device *dev) #endif } -#ifdef TC35815_NAPI return received; -#endif } -#ifdef TC35815_NAPI static int tc35815_poll(struct napi_struct *napi, int budget) { struct tc35815_local *lp = container_of(napi, struct tc35815_local, napi); @@ -1924,13 +1677,8 @@ static int tc35815_poll(struct napi_struct *napi, int budget) } return received; } -#endif -#ifdef NO_CHECK_CARRIER -#define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_LateColl|Tx_TxPar|Tx_SQErr) -#else #define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_NCarr|Tx_LateColl|Tx_TxPar|Tx_SQErr) -#endif static void tc35815_check_tx_stat(struct net_device *dev, int status) @@ -1944,16 +1692,12 @@ tc35815_check_tx_stat(struct net_device *dev, int status) if (status & Tx_TxColl_MASK) dev->stats.collisions += status & Tx_TxColl_MASK; -#ifndef NO_CHECK_CARRIER /* TX4939 does not have NCarr */ if (lp->chiptype == TC35815_TX4939) status &= ~Tx_NCarr; -#ifdef WORKAROUND_LOSTCAR /* WORKAROUND: ignore LostCrS in full duplex operation */ if (!lp->link || lp->duplex == DUPLEX_FULL) status &= ~Tx_NCarr; -#endif -#endif if (!(status & TX_STA_ERR)) { /* no error. */ @@ -1983,12 +1727,10 @@ tc35815_check_tx_stat(struct net_device *dev, int status) dev->stats.tx_fifo_errors++; msg = "Excessive Deferral."; } -#ifndef NO_CHECK_CARRIER if (status & Tx_NCarr) { dev->stats.tx_carrier_errors++; msg = "Lost Carrier Sense."; } -#endif if (status & Tx_LateColl) { dev->stats.tx_aborted_errors++; msg = "Late Collision."; @@ -2044,11 +1786,7 @@ tc35815_txdone(struct net_device *dev) pci_unmap_single(lp->pci_dev, lp->tx_skbs[lp->tfd_end].skb_dma, skb->len, PCI_DMA_TODEVICE); lp->tx_skbs[lp->tfd_end].skb = NULL; lp->tx_skbs[lp->tfd_end].skb_dma = 0; -#ifdef TC35815_NAPI dev_kfree_skb_any(skb); -#else - dev_kfree_skb_irq(skb); -#endif } txfd->fd.FDSystem = cpu_to_le32(0xffffffff); @@ -2083,9 +1821,7 @@ tc35815_txdone(struct net_device *dev) /* start DMA Transmitter again */ txhead->fd.FDNext |= cpu_to_le32(FD_Next_EOL); -#ifdef GATHER_TXINT txhead->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); -#endif if (netif_msg_tx_queued(lp)) { printk("%s: start TxFD on queue.\n", dev->name); @@ -2112,9 +1848,7 @@ tc35815_close(struct net_device *dev) struct tc35815_local *lp = netdev_priv(dev); netif_stop_queue(dev); -#ifdef TC35815_NAPI napi_disable(&lp->napi); -#endif if (lp->phy_dev) phy_stop(lp->phy_dev); cancel_work_sync(&lp->restart_work); @@ -2198,14 +1932,12 @@ tc35815_set_multicast_list(struct net_device *dev) (struct tc35815_regs __iomem *)dev->base_addr; if (dev->flags & IFF_PROMISC) { -#ifdef WORKAROUND_100HALF_PROMISC /* With some (all?) 100MHalf HUB, controller will hang * if we enabled promiscuous mode before linkup... */ struct tc35815_local *lp = netdev_priv(dev); if (!lp->link) return; -#endif /* Enable promiscuous mode */ tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc | CAM_StationAcc, &tr->CAM_Ctl); } else if ((dev->flags & IFF_ALLMULTI) || @@ -2392,9 +2124,6 @@ static void tc35815_chip_init(struct net_device *dev) tc_writel(DMA_BURST_SIZE | DMA_RxAlign_2, &tr->DMA_Ctl); else tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl); -#ifdef TC35815_USE_PACKEDBUFFER - tc_writel(RxFrag_EnPack | ETH_ZLEN, &tr->RxFragSize); /* Packing */ -#endif tc_writel(0, &tr->TxPollCtr); /* Batch mode */ tc_writel(TX_THRESHOLD, &tr->TxThrsh); tc_writel(INT_EN_CMD, &tr->Int_En); @@ -2412,19 +2141,12 @@ static void tc35815_chip_init(struct net_device *dev) tc_writel(RX_CTL_CMD, &tr->Rx_Ctl); /* start MAC receiver */ /* start MAC transmitter */ -#ifndef NO_CHECK_CARRIER /* TX4939 does not have EnLCarr */ if (lp->chiptype == TC35815_TX4939) txctl &= ~Tx_EnLCarr; -#ifdef WORKAROUND_LOSTCAR /* WORKAROUND: ignore LostCrS in full duplex operation */ if (!lp->phy_dev || !lp->link || lp->duplex == DUPLEX_FULL) txctl &= ~Tx_EnLCarr; -#endif -#endif /* !NO_CHECK_CARRIER */ -#ifdef GATHER_TXINT - txctl &= ~Tx_EnComp; /* disable global tx completion int. */ -#endif tc_writel(txctl, &tr->Tx_Ctl); } diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c index ec9dfb251f30..80b404f2b938 100644 --- a/drivers/net/tehuti.c +++ b/drivers/net/tehuti.c @@ -420,7 +420,7 @@ static int bdx_hw_start(struct bdx_priv *priv) GMAC_RX_FILTER_AM | GMAC_RX_FILTER_AB); #define BDX_IRQ_TYPE ((priv->nic->irq_type == IRQ_MSI)?0:IRQF_SHARED) - if ((rc = request_irq(priv->pdev->irq, &bdx_isr_napi, BDX_IRQ_TYPE, + if ((rc = request_irq(priv->pdev->irq, bdx_isr_napi, BDX_IRQ_TYPE, ndev->name, ndev))) goto err_irq; bdx_enable_interrupts(priv); @@ -1784,9 +1784,9 @@ static void bdx_tx_cleanup(struct bdx_priv *priv) } #endif - if (unlikely(netif_queue_stopped(priv->ndev) - && netif_carrier_ok(priv->ndev) - && (priv->tx_level >= BDX_MIN_TX_LEVEL))) { + if (unlikely(netif_queue_stopped(priv->ndev) && + netif_carrier_ok(priv->ndev) && + (priv->tx_level >= BDX_MIN_TX_LEVEL))) { DBG("%s: %s: TX Q WAKE level %d\n", BDX_DRV_NAME, priv->ndev->name, priv->tx_level); netif_wake_queue(priv->ndev); @@ -1878,7 +1878,7 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size) udelay(50); /* give hw a chance to clean fifo */ continue; } - avail = MIN(avail, size); + avail = min(avail, size); DBG("about to push %d bytes starting %p size %d\n", avail, data, size); bdx_tx_push_desc(priv, data, avail); @@ -2105,12 +2105,6 @@ err_pci: } /****************** Ethtool interface *********************/ -/* get strings for tests */ -static const char - bdx_test_names[][ETH_GSTRING_LEN] = { - "No tests defined" -}; - /* get strings for statistics counters */ static const char bdx_stat_names[][ETH_GSTRING_LEN] = { @@ -2279,8 +2273,8 @@ bdx_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal) (((tx_max_coal * BDX_TXF_DESC_SZ) + PCK_TH_MULT - 1) / PCK_TH_MULT); - if ((rx_coal > 0x7FFF) || (tx_coal > 0x7FFF) - || (rx_max_coal > 0xF) || (tx_max_coal > 0xF)) + if ((rx_coal > 0x7FFF) || (tx_coal > 0x7FFF) || + (rx_max_coal > 0xF) || (tx_max_coal > 0xF)) return -EINVAL; rdintcm = INT_REG_VAL(rx_coal, GET_INT_COAL_RC(priv->rdintcm), @@ -2353,8 +2347,8 @@ bdx_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) tx_size = 3; /*Is there anything to do? */ - if ((rx_size == priv->rxf_size) - && (tx_size == priv->txd_size)) + if ((rx_size == priv->rxf_size) && + (tx_size == priv->txd_size)) return 0; priv->rxf_size = rx_size; @@ -2380,9 +2374,6 @@ bdx_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) static void bdx_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { switch (stringset) { - case ETH_SS_TEST: - memcpy(data, *bdx_test_names, sizeof(bdx_test_names)); - break; case ETH_SS_STATS: memcpy(data, *bdx_stat_names, sizeof(bdx_stat_names)); break; @@ -2390,15 +2381,21 @@ static void bdx_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } /* - * bdx_get_stats_count - return number of 64bit statistics counters + * bdx_get_sset_count - return number of statistics or tests * @netdev */ -static int bdx_get_stats_count(struct net_device *netdev) +static int bdx_get_sset_count(struct net_device *netdev, int stringset) { struct bdx_priv *priv = netdev_priv(netdev); - BDX_ASSERT(ARRAY_SIZE(bdx_stat_names) - != sizeof(struct bdx_stats) / sizeof(u64)); - return ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0); + + switch (stringset) { + case ETH_SS_STATS: + BDX_ASSERT(ARRAY_SIZE(bdx_stat_names) + != sizeof(struct bdx_stats) / sizeof(u64)); + return ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0); + default: + return -EINVAL; + } } /* @@ -2441,7 +2438,7 @@ static void bdx_ethtool_ops(struct net_device *netdev) .get_sg = ethtool_op_get_sg, .get_tso = ethtool_op_get_tso, .get_strings = bdx_get_strings, - .get_stats_count = bdx_get_stats_count, + .get_sset_count = bdx_get_sset_count, .get_ethtool_stats = bdx_get_ethtool_stats, }; diff --git a/drivers/net/tehuti.h b/drivers/net/tehuti.h index 4fc875e5dcdd..124141909e42 100644 --- a/drivers/net/tehuti.h +++ b/drivers/net/tehuti.h @@ -76,8 +76,6 @@ #define FIFO_SIZE 4096 #define FIFO_EXTRA_SPACE 1024 -#define MIN(x, y) ((x) < (y) ? (x) : (y)) - #if BITS_PER_LONG == 64 # define H32_64(x) (u32) ((u64)(x) >> 32) # define L32_64(x) (u32) ((u64)(x) & 0xffffffff) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index ba5d3fe753b6..3a74d2168598 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -68,8 +68,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "3.102" -#define DRV_MODULE_RELDATE "September 1, 2009" +#define DRV_MODULE_VERSION "3.105" +#define DRV_MODULE_RELDATE "December 2, 2009" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 @@ -137,6 +137,12 @@ #define TG3_RX_STD_MAP_SZ TG3_RX_DMA_TO_MAP_SZ(TG3_RX_STD_DMA_SZ) #define TG3_RX_JMB_MAP_SZ TG3_RX_DMA_TO_MAP_SZ(TG3_RX_JMB_DMA_SZ) +#define TG3_RX_STD_BUFF_RING_SIZE \ + (sizeof(struct ring_info) * TG3_RX_RING_SIZE) + +#define TG3_RX_JMB_BUFF_RING_SIZE \ + (sizeof(struct ring_info) * TG3_RX_JUMBO_RING_SIZE) + /* minimum number of free TX descriptors required to wake up TX process */ #define TG3_TX_WAKEUP_THRESH(tnapi) ((tnapi)->tx_pending / 4) @@ -235,6 +241,9 @@ static struct pci_device_id tg3_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57760)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57790)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57788)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5717)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5718)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5724)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)}, {PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)}, @@ -396,7 +405,7 @@ static void tg3_write_indirect_mbox(struct tg3 *tp, u32 off, u32 val) TG3_64BIT_REG_LOW, val); return; } - if (off == (MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW)) { + if (off == TG3_RX_STD_PROD_IDX_REG) { pci_write_config_dword(tp->pdev, TG3PCI_STD_RING_PROD_IDX + TG3_64BIT_REG_LOW, val); return; @@ -937,9 +946,10 @@ static void tg3_mdio_config_5785(struct tg3 *tp) u32 val; struct phy_device *phydev; - phydev = tp->mdio_bus->phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) { case TG3_PHY_ID_BCM50610: + case TG3_PHY_ID_BCM50610M: val = MAC_PHYCFG2_50610_LED_MODES; break; case TG3_PHY_ID_BCMAC131: @@ -1031,7 +1041,7 @@ static void tg3_mdio_start(struct tg3 *tp) if (is_serdes) tp->phy_addr += 7; } else - tp->phy_addr = PHY_ADDR; + tp->phy_addr = TG3_PHY_MII_ADDR; if ((tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) && GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) @@ -1062,7 +1072,7 @@ static int tg3_mdio_init(struct tg3 *tp) tp->mdio_bus->read = &tg3_mdio_read; tp->mdio_bus->write = &tg3_mdio_write; tp->mdio_bus->reset = &tg3_mdio_reset; - tp->mdio_bus->phy_mask = ~(1 << PHY_ADDR); + tp->mdio_bus->phy_mask = ~(1 << TG3_PHY_MII_ADDR); tp->mdio_bus->irq = &tp->mdio_irq[0]; for (i = 0; i < PHY_MAX_ADDR; i++) @@ -1084,7 +1094,7 @@ static int tg3_mdio_init(struct tg3 *tp) return i; } - phydev = tp->mdio_bus->phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; if (!phydev || !phydev->drv) { printk(KERN_WARNING "%s: No PHY devices\n", tp->dev->name); @@ -1096,8 +1106,14 @@ static int tg3_mdio_init(struct tg3 *tp) switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) { case TG3_PHY_ID_BCM57780: phydev->interface = PHY_INTERFACE_MODE_GMII; + phydev->dev_flags |= PHY_BRCM_AUTO_PWRDWN_ENABLE; break; case TG3_PHY_ID_BCM50610: + case TG3_PHY_ID_BCM50610M: + phydev->dev_flags |= PHY_BRCM_CLEAR_RGMII_MODE | + PHY_BRCM_RX_REFCLK_UNUSED | + PHY_BRCM_DIS_TXCRXC_NOENRGY | + PHY_BRCM_AUTO_PWRDWN_ENABLE; if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE) phydev->dev_flags |= PHY_BRCM_STD_IBND_DISABLE; if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN) @@ -1111,6 +1127,7 @@ static int tg3_mdio_init(struct tg3 *tp) case TG3_PHY_ID_RTL8201E: case TG3_PHY_ID_BCMAC131: phydev->interface = PHY_INTERFACE_MODE_MII; + phydev->dev_flags |= PHY_BRCM_AUTO_PWRDWN_ENABLE; tp->tg3_flags3 |= TG3_FLG3_PHY_IS_FET; break; } @@ -1311,7 +1328,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv) u32 old_tx_mode = tp->tx_mode; if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) - autoneg = tp->mdio_bus->phy_map[PHY_ADDR]->autoneg; + autoneg = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]->autoneg; else autoneg = tp->link_config.autoneg; @@ -1348,7 +1365,7 @@ static void tg3_adjust_link(struct net_device *dev) u8 oldflowctrl, linkmesg = 0; u32 mac_mode, lcl_adv, rmt_adv; struct tg3 *tp = netdev_priv(dev); - struct phy_device *phydev = tp->mdio_bus->phy_map[PHY_ADDR]; + struct phy_device *phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; spin_lock_bh(&tp->lock); @@ -1363,8 +1380,11 @@ static void tg3_adjust_link(struct net_device *dev) if (phydev->speed == SPEED_100 || phydev->speed == SPEED_10) mac_mode |= MAC_MODE_PORT_MODE_MII; - else + else if (phydev->speed == SPEED_1000 || + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) mac_mode |= MAC_MODE_PORT_MODE_GMII; + else + mac_mode |= MAC_MODE_PORT_MODE_MII; if (phydev->duplex == DUPLEX_HALF) mac_mode |= MAC_MODE_HALF_DUPLEX; @@ -1434,7 +1454,7 @@ static int tg3_phy_init(struct tg3 *tp) /* Bring the PHY back to a known state. */ tg3_bmcr_reset(tp); - phydev = tp->mdio_bus->phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; /* Attach the MAC to the PHY. */ phydev = phy_connect(tp->dev, dev_name(&phydev->dev), tg3_adjust_link, @@ -1461,7 +1481,7 @@ static int tg3_phy_init(struct tg3 *tp) SUPPORTED_Asym_Pause); break; default: - phy_disconnect(tp->mdio_bus->phy_map[PHY_ADDR]); + phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]); return -EINVAL; } @@ -1479,7 +1499,7 @@ static void tg3_phy_start(struct tg3 *tp) if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return; - phydev = tp->mdio_bus->phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; if (tp->link_config.phy_is_low_power) { tp->link_config.phy_is_low_power = 0; @@ -1499,13 +1519,13 @@ static void tg3_phy_stop(struct tg3 *tp) if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return; - phy_stop(tp->mdio_bus->phy_map[PHY_ADDR]); + phy_stop(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]); } static void tg3_phy_fini(struct tg3 *tp) { if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) { - phy_disconnect(tp->mdio_bus->phy_map[PHY_ADDR]); + phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]); tp->tg3_flags3 &= ~TG3_FLG3_PHY_CONNECTED; } } @@ -2149,6 +2169,26 @@ static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power) tw32_f(GRC_MISC_CFG, val | GRC_MISC_CFG_EPHY_IDDQ); udelay(40); return; + } else if (tp->tg3_flags3 & TG3_FLG3_PHY_IS_FET) { + u32 phytest; + if (!tg3_readphy(tp, MII_TG3_FET_TEST, &phytest)) { + u32 phy; + + tg3_writephy(tp, MII_ADVERTISE, 0); + tg3_writephy(tp, MII_BMCR, + BMCR_ANENABLE | BMCR_ANRESTART); + + tg3_writephy(tp, MII_TG3_FET_TEST, + phytest | MII_TG3_FET_SHADOW_EN); + if (!tg3_readphy(tp, MII_TG3_FET_SHDW_AUXMODE4, &phy)) { + phy |= MII_TG3_FET_SHDW_AUXMODE4_SBPD; + tg3_writephy(tp, + MII_TG3_FET_SHDW_AUXMODE4, + phy); + } + tg3_writephy(tp, MII_TG3_FET_TEST, phytest); + } + return; } else if (do_low_power) { tg3_writephy(tp, MII_TG3_EXT_CTRL, MII_TG3_EXT_CTRL_FORCE_LED_OFF); @@ -2218,7 +2258,7 @@ static void tg3_nvram_unlock(struct tg3 *tp) static void tg3_enable_nvram_access(struct tg3 *tp) { if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && - !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) { + !(tp->tg3_flags3 & TG3_FLG3_PROTECTED_NVRAM)) { u32 nvaccess = tr32(NVRAM_ACCESS); tw32(NVRAM_ACCESS, nvaccess | ACCESS_ENABLE); @@ -2229,7 +2269,7 @@ static void tg3_enable_nvram_access(struct tg3 *tp) static void tg3_disable_nvram_access(struct tg3 *tp) { if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && - !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) { + !(tp->tg3_flags3 & TG3_FLG3_PROTECTED_NVRAM)) { u32 nvaccess = tr32(NVRAM_ACCESS); tw32(NVRAM_ACCESS, nvaccess & ~ACCESS_ENABLE); @@ -2474,7 +2514,7 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) struct phy_device *phydev; u32 phyid, advertising; - phydev = tp->mdio_bus->phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; tp->link_config.phy_is_low_power = 1; @@ -3243,15 +3283,6 @@ relink: pci_write_config_word(tp->pdev, tp->pcie_cap + PCI_EXP_LNKCTL, newlnkctl); - } else if (tp->tg3_flags3 & TG3_FLG3_TOGGLE_10_100_L1PLLPD) { - u32 newreg, oldreg = tr32(TG3_PCIE_LNKCTL); - if (tp->link_config.active_speed == SPEED_100 || - tp->link_config.active_speed == SPEED_10) - newreg = oldreg & ~TG3_PCIE_LNKCTL_L1_PLL_PD_EN; - else - newreg = oldreg | TG3_PCIE_LNKCTL_L1_PLL_PD_EN; - if (newreg != oldreg) - tw32(TG3_PCIE_LNKCTL, newreg); } if (current_link_up != netif_carrier_ok(tp->dev)) { @@ -4320,13 +4351,13 @@ static void tg3_tx(struct tg3_napi *tnapi) struct netdev_queue *txq; int index = tnapi - tp->napi; - if (tp->tg3_flags2 & TG3_FLG2_USING_MSIX) + if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS) index--; txq = netdev_get_tx_queue(tp->dev, index); while (sw_idx != hw_idx) { - struct tx_ring_info *ri = &tnapi->tx_buffers[sw_idx]; + struct ring_info *ri = &tnapi->tx_buffers[sw_idx]; struct sk_buff *skb = ri->skb; int i, tx_bug = 0; @@ -4335,7 +4366,10 @@ static void tg3_tx(struct tg3_napi *tnapi) return; } - skb_dma_unmap(&tp->pdev->dev, skb, DMA_TO_DEVICE); + pci_unmap_single(tp->pdev, + pci_unmap_addr(ri, mapping), + skb_headlen(skb), + PCI_DMA_TODEVICE); ri->skb = NULL; @@ -4345,6 +4379,11 @@ static void tg3_tx(struct tg3_napi *tnapi) ri = &tnapi->tx_buffers[sw_idx]; if (unlikely(ri->skb != NULL || sw_idx == hw_idx)) tx_bug = 1; + + pci_unmap_page(tp->pdev, + pci_unmap_addr(ri, mapping), + skb_shinfo(skb)->frags[i].size, + PCI_DMA_TODEVICE); sw_idx = NEXT_TX(sw_idx); } @@ -4375,6 +4414,17 @@ static void tg3_tx(struct tg3_napi *tnapi) } } +static void tg3_rx_skb_free(struct tg3 *tp, struct ring_info *ri, u32 map_sz) +{ + if (!ri->skb) + return; + + pci_unmap_single(tp->pdev, pci_unmap_addr(ri, mapping), + map_sz, PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(ri->skb); + ri->skb = NULL; +} + /* Returns size of skb allocated or < 0 on error. * * We only need to fill in the address because the other members @@ -4386,16 +4436,14 @@ static void tg3_tx(struct tg3_napi *tnapi) * buffers the cpu only reads the last cacheline of the RX descriptor * (to fetch the error flags, vlan tag, checksum, and opaque cookie). */ -static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key, - int src_idx, u32 dest_idx_unmasked) +static int tg3_alloc_rx_skb(struct tg3 *tp, struct tg3_rx_prodring_set *tpr, + u32 opaque_key, u32 dest_idx_unmasked) { - struct tg3 *tp = tnapi->tp; struct tg3_rx_buffer_desc *desc; struct ring_info *map, *src_map; struct sk_buff *skb; dma_addr_t mapping; int skb_size, dest_idx; - struct tg3_rx_prodring_set *tpr = &tp->prodring[0]; src_map = NULL; switch (opaque_key) { @@ -4403,8 +4451,6 @@ static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key, dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; desc = &tpr->rx_std[dest_idx]; map = &tpr->rx_std_buffers[dest_idx]; - if (src_idx >= 0) - src_map = &tpr->rx_std_buffers[src_idx]; skb_size = tp->rx_pkt_map_sz; break; @@ -4412,8 +4458,6 @@ static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key, dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; desc = &tpr->rx_jmb[dest_idx].std; map = &tpr->rx_jmb_buffers[dest_idx]; - if (src_idx >= 0) - src_map = &tpr->rx_jmb_buffers[src_idx]; skb_size = TG3_RX_JMB_MAP_SZ; break; @@ -4435,13 +4479,14 @@ static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key, mapping = pci_map_single(tp->pdev, skb->data, skb_size, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(tp->pdev, mapping)) { + dev_kfree_skb(skb); + return -EIO; + } map->skb = skb; pci_unmap_addr_set(map, mapping, mapping); - if (src_map != NULL) - src_map->skb = NULL; - desc->addr_hi = ((u64)mapping >> 32); desc->addr_lo = ((u64)mapping & 0xffffffff); @@ -4452,30 +4497,32 @@ static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key, * members of the RX descriptor are invariant. See notes above * tg3_alloc_rx_skb for full details. */ -static void tg3_recycle_rx(struct tg3_napi *tnapi, u32 opaque_key, - int src_idx, u32 dest_idx_unmasked) +static void tg3_recycle_rx(struct tg3_napi *tnapi, + struct tg3_rx_prodring_set *dpr, + u32 opaque_key, int src_idx, + u32 dest_idx_unmasked) { struct tg3 *tp = tnapi->tp; struct tg3_rx_buffer_desc *src_desc, *dest_desc; struct ring_info *src_map, *dest_map; int dest_idx; - struct tg3_rx_prodring_set *tpr = &tp->prodring[0]; + struct tg3_rx_prodring_set *spr = &tp->prodring[0]; switch (opaque_key) { case RXD_OPAQUE_RING_STD: dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; - dest_desc = &tpr->rx_std[dest_idx]; - dest_map = &tpr->rx_std_buffers[dest_idx]; - src_desc = &tpr->rx_std[src_idx]; - src_map = &tpr->rx_std_buffers[src_idx]; + dest_desc = &dpr->rx_std[dest_idx]; + dest_map = &dpr->rx_std_buffers[dest_idx]; + src_desc = &spr->rx_std[src_idx]; + src_map = &spr->rx_std_buffers[src_idx]; break; case RXD_OPAQUE_RING_JUMBO: dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; - dest_desc = &tpr->rx_jmb[dest_idx].std; - dest_map = &tpr->rx_jmb_buffers[dest_idx]; - src_desc = &tpr->rx_jmb[src_idx].std; - src_map = &tpr->rx_jmb_buffers[src_idx]; + dest_desc = &dpr->rx_jmb[dest_idx].std; + dest_map = &dpr->rx_jmb_buffers[dest_idx]; + src_desc = &spr->rx_jmb[src_idx].std; + src_map = &spr->rx_jmb_buffers[src_idx]; break; default: @@ -4487,7 +4534,6 @@ static void tg3_recycle_rx(struct tg3_napi *tnapi, u32 opaque_key, pci_unmap_addr(src_map, mapping)); dest_desc->addr_hi = src_desc->addr_hi; dest_desc->addr_lo = src_desc->addr_lo; - src_map->skb = NULL; } @@ -4519,10 +4565,11 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) { struct tg3 *tp = tnapi->tp; u32 work_mask, rx_std_posted = 0; + u32 std_prod_idx, jmb_prod_idx; u32 sw_idx = tnapi->rx_rcb_ptr; u16 hw_idx; int received; - struct tg3_rx_prodring_set *tpr = &tp->prodring[0]; + struct tg3_rx_prodring_set *tpr = tnapi->prodring; hw_idx = *(tnapi->rx_rcb_prod_idx); /* @@ -4532,7 +4579,10 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) rmb(); work_mask = 0; received = 0; + std_prod_idx = tpr->rx_std_prod_idx; + jmb_prod_idx = tpr->rx_jmb_prod_idx; while (sw_idx != hw_idx && budget > 0) { + struct ring_info *ri; struct tg3_rx_buffer_desc *desc = &tnapi->rx_rcb[sw_idx]; unsigned int len; struct sk_buff *skb; @@ -4542,16 +4592,16 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) desc_idx = desc->opaque & RXD_OPAQUE_INDEX_MASK; opaque_key = desc->opaque & RXD_OPAQUE_RING_MASK; if (opaque_key == RXD_OPAQUE_RING_STD) { - struct ring_info *ri = &tpr->rx_std_buffers[desc_idx]; + ri = &tp->prodring[0].rx_std_buffers[desc_idx]; dma_addr = pci_unmap_addr(ri, mapping); skb = ri->skb; - post_ptr = &tpr->rx_std_ptr; + post_ptr = &std_prod_idx; rx_std_posted++; } else if (opaque_key == RXD_OPAQUE_RING_JUMBO) { - struct ring_info *ri = &tpr->rx_jmb_buffers[desc_idx]; + ri = &tp->prodring[0].rx_jmb_buffers[desc_idx]; dma_addr = pci_unmap_addr(ri, mapping); skb = ri->skb; - post_ptr = &tpr->rx_jmb_ptr; + post_ptr = &jmb_prod_idx; } else goto next_pkt_nopost; @@ -4560,7 +4610,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) if ((desc->err_vlan & RXD_ERR_MASK) != 0 && (desc->err_vlan != RXD_ERR_ODD_NIBBLE_RCVD_MII)) { drop_it: - tg3_recycle_rx(tnapi, opaque_key, + tg3_recycle_rx(tnapi, tpr, opaque_key, desc_idx, *post_ptr); drop_it_no_recycle: /* Other statistics kept track of by card. */ @@ -4571,20 +4621,21 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) - ETH_FCS_LEN; - if (len > RX_COPY_THRESHOLD - && tp->rx_offset == NET_IP_ALIGN - /* rx_offset will likely not equal NET_IP_ALIGN - * if this is a 5701 card running in PCI-X mode - * [see tg3_get_invariants()] - */ - ) { + if (len > RX_COPY_THRESHOLD && + tp->rx_offset == NET_IP_ALIGN) { + /* rx_offset will likely not equal NET_IP_ALIGN + * if this is a 5701 card running in PCI-X mode + * [see tg3_get_invariants()] + */ int skb_size; - skb_size = tg3_alloc_rx_skb(tnapi, opaque_key, - desc_idx, *post_ptr); + skb_size = tg3_alloc_rx_skb(tp, tpr, opaque_key, + *post_ptr); if (skb_size < 0) goto drop_it; + ri->skb = NULL; + pci_unmap_single(tp->pdev, dma_addr, skb_size, PCI_DMA_FROMDEVICE); @@ -4592,7 +4643,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) } else { struct sk_buff *copy_skb; - tg3_recycle_rx(tnapi, opaque_key, + tg3_recycle_rx(tnapi, tpr, opaque_key, desc_idx, *post_ptr); copy_skb = netdev_alloc_skb(tp->dev, @@ -4643,9 +4694,7 @@ next_pkt: if (unlikely(rx_std_posted >= tp->rx_std_max_post)) { u32 idx = *post_ptr % TG3_RX_RING_SIZE; - - tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX + - TG3_64BIT_REG_LOW, idx); + tw32_rx_mbox(TG3_RX_STD_PROD_IDX_REG, idx); work_mask &= ~RXD_OPAQUE_RING_STD; rx_std_posted = 0; } @@ -4665,33 +4714,45 @@ next_pkt_nopost: tw32_rx_mbox(tnapi->consmbox, sw_idx); /* Refill RX ring(s). */ - if (work_mask & RXD_OPAQUE_RING_STD) { - sw_idx = tpr->rx_std_ptr % TG3_RX_RING_SIZE; - tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, - sw_idx); - } - if (work_mask & RXD_OPAQUE_RING_JUMBO) { - sw_idx = tpr->rx_jmb_ptr % TG3_RX_JUMBO_RING_SIZE; - tw32_rx_mbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, - sw_idx); + if (!(tp->tg3_flags3 & TG3_FLG3_ENABLE_RSS) || tnapi == &tp->napi[1]) { + if (work_mask & RXD_OPAQUE_RING_STD) { + tpr->rx_std_prod_idx = std_prod_idx % TG3_RX_RING_SIZE; + tw32_rx_mbox(TG3_RX_STD_PROD_IDX_REG, + tpr->rx_std_prod_idx); + } + if (work_mask & RXD_OPAQUE_RING_JUMBO) { + tpr->rx_jmb_prod_idx = jmb_prod_idx % + TG3_RX_JUMBO_RING_SIZE; + tw32_rx_mbox(TG3_RX_JMB_PROD_IDX_REG, + tpr->rx_jmb_prod_idx); + } + mmiowb(); + } else if (work_mask) { + /* rx_std_buffers[] and rx_jmb_buffers[] entries must be + * updated before the producer indices can be updated. + */ + smp_wmb(); + + tpr->rx_std_prod_idx = std_prod_idx % TG3_RX_RING_SIZE; + tpr->rx_jmb_prod_idx = jmb_prod_idx % TG3_RX_JUMBO_RING_SIZE; + + napi_schedule(&tp->napi[1].napi); } - mmiowb(); return received; } -static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget) +static void tg3_poll_link(struct tg3 *tp) { - struct tg3 *tp = tnapi->tp; - struct tg3_hw_status *sblk = tnapi->hw_status; - /* handle link change and other phy events */ if (!(tp->tg3_flags & (TG3_FLAG_USE_LINKCHG_REG | TG3_FLAG_POLL_SERDES))) { + struct tg3_hw_status *sblk = tp->napi[0].hw_status; + if (sblk->status & SD_STATUS_LINK_CHG) { sblk->status = SD_STATUS_UPDATED | - (sblk->status & ~SD_STATUS_LINK_CHG); + (sblk->status & ~SD_STATUS_LINK_CHG); spin_lock(&tp->lock); if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { tw32_f(MAC_STATUS, @@ -4705,6 +4766,98 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget) spin_unlock(&tp->lock); } } +} + +static void tg3_rx_prodring_xfer(struct tg3 *tp, + struct tg3_rx_prodring_set *dpr, + struct tg3_rx_prodring_set *spr) +{ + u32 si, di, cpycnt, src_prod_idx; + int i; + + while (1) { + src_prod_idx = spr->rx_std_prod_idx; + + /* Make sure updates to the rx_std_buffers[] entries and the + * standard producer index are seen in the correct order. + */ + smp_rmb(); + + if (spr->rx_std_cons_idx == src_prod_idx) + break; + + if (spr->rx_std_cons_idx < src_prod_idx) + cpycnt = src_prod_idx - spr->rx_std_cons_idx; + else + cpycnt = TG3_RX_RING_SIZE - spr->rx_std_cons_idx; + + cpycnt = min(cpycnt, TG3_RX_RING_SIZE - dpr->rx_std_prod_idx); + + si = spr->rx_std_cons_idx; + di = dpr->rx_std_prod_idx; + + memcpy(&dpr->rx_std_buffers[di], + &spr->rx_std_buffers[si], + cpycnt * sizeof(struct ring_info)); + + for (i = 0; i < cpycnt; i++, di++, si++) { + struct tg3_rx_buffer_desc *sbd, *dbd; + sbd = &spr->rx_std[si]; + dbd = &dpr->rx_std[di]; + dbd->addr_hi = sbd->addr_hi; + dbd->addr_lo = sbd->addr_lo; + } + + spr->rx_std_cons_idx = (spr->rx_std_cons_idx + cpycnt) % + TG3_RX_RING_SIZE; + dpr->rx_std_prod_idx = (dpr->rx_std_prod_idx + cpycnt) % + TG3_RX_RING_SIZE; + } + + while (1) { + src_prod_idx = spr->rx_jmb_prod_idx; + + /* Make sure updates to the rx_jmb_buffers[] entries and + * the jumbo producer index are seen in the correct order. + */ + smp_rmb(); + + if (spr->rx_jmb_cons_idx == src_prod_idx) + break; + + if (spr->rx_jmb_cons_idx < src_prod_idx) + cpycnt = src_prod_idx - spr->rx_jmb_cons_idx; + else + cpycnt = TG3_RX_JUMBO_RING_SIZE - spr->rx_jmb_cons_idx; + + cpycnt = min(cpycnt, + TG3_RX_JUMBO_RING_SIZE - dpr->rx_jmb_prod_idx); + + si = spr->rx_jmb_cons_idx; + di = dpr->rx_jmb_prod_idx; + + memcpy(&dpr->rx_jmb_buffers[di], + &spr->rx_jmb_buffers[si], + cpycnt * sizeof(struct ring_info)); + + for (i = 0; i < cpycnt; i++, di++, si++) { + struct tg3_rx_buffer_desc *sbd, *dbd; + sbd = &spr->rx_jmb[si].std; + dbd = &dpr->rx_jmb[di].std; + dbd->addr_hi = sbd->addr_hi; + dbd->addr_lo = sbd->addr_lo; + } + + spr->rx_jmb_cons_idx = (spr->rx_jmb_cons_idx + cpycnt) % + TG3_RX_JUMBO_RING_SIZE; + dpr->rx_jmb_prod_idx = (dpr->rx_jmb_prod_idx + cpycnt) % + TG3_RX_JUMBO_RING_SIZE; + } +} + +static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget) +{ + struct tg3 *tp = tnapi->tp; /* run TX completion thread */ if (tnapi->hw_status->idx[0].tx_consumer != tnapi->tx_cons) { @@ -4720,6 +4873,74 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget) if (*(tnapi->rx_rcb_prod_idx) != tnapi->rx_rcb_ptr) work_done += tg3_rx(tnapi, budget - work_done); + if ((tp->tg3_flags3 & TG3_FLG3_ENABLE_RSS) && tnapi == &tp->napi[1]) { + int i; + u32 std_prod_idx = tp->prodring[0].rx_std_prod_idx; + u32 jmb_prod_idx = tp->prodring[0].rx_jmb_prod_idx; + + for (i = 2; i < tp->irq_cnt; i++) + tg3_rx_prodring_xfer(tp, tnapi->prodring, + tp->napi[i].prodring); + + wmb(); + + if (std_prod_idx != tp->prodring[0].rx_std_prod_idx) { + u32 mbox = TG3_RX_STD_PROD_IDX_REG; + tw32_rx_mbox(mbox, tp->prodring[0].rx_std_prod_idx); + } + + if (jmb_prod_idx != tp->prodring[0].rx_jmb_prod_idx) { + u32 mbox = TG3_RX_JMB_PROD_IDX_REG; + tw32_rx_mbox(mbox, tp->prodring[0].rx_jmb_prod_idx); + } + + mmiowb(); + } + + return work_done; +} + +static int tg3_poll_msix(struct napi_struct *napi, int budget) +{ + struct tg3_napi *tnapi = container_of(napi, struct tg3_napi, napi); + struct tg3 *tp = tnapi->tp; + int work_done = 0; + struct tg3_hw_status *sblk = tnapi->hw_status; + + while (1) { + work_done = tg3_poll_work(tnapi, work_done, budget); + + if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) + goto tx_recovery; + + if (unlikely(work_done >= budget)) + break; + + /* tp->last_tag is used in tg3_restart_ints() below + * to tell the hw how much work has been processed, + * so we must read it before checking for more work. + */ + tnapi->last_tag = sblk->status_tag; + tnapi->last_irq_tag = tnapi->last_tag; + rmb(); + + /* check for RX/TX work to do */ + if (sblk->idx[0].tx_consumer == tnapi->tx_cons && + *(tnapi->rx_rcb_prod_idx) == tnapi->rx_rcb_ptr) { + napi_complete(napi); + /* Reenable interrupts. */ + tw32_mailbox(tnapi->int_mbox, tnapi->last_tag << 24); + mmiowb(); + break; + } + } + + return work_done; + +tx_recovery: + /* work_done is guaranteed to be less than budget. */ + napi_complete(napi); + schedule_work(&tp->reset_task); return work_done; } @@ -4731,6 +4952,8 @@ static int tg3_poll(struct napi_struct *napi, int budget) struct tg3_hw_status *sblk = tnapi->hw_status; while (1) { + tg3_poll_link(tp); + work_done = tg3_poll_work(tnapi, work_done, budget); if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) @@ -5093,11 +5316,11 @@ static inline int tg3_40bit_overflow_test(struct tg3 *tp, dma_addr_t mapping, static void tg3_set_txd(struct tg3_napi *, int, dma_addr_t, int, u32, u32); /* Workaround 4GB and 40-bit hardware DMA bugs. */ -static int tigon3_dma_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb, - u32 last_plus_one, u32 *start, - u32 base_flags, u32 mss) +static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi, + struct sk_buff *skb, u32 last_plus_one, + u32 *start, u32 base_flags, u32 mss) { - struct tg3_napi *tnapi = &tp->napi[0]; + struct tg3 *tp = tnapi->tp; struct sk_buff *new_skb; dma_addr_t new_addr = 0; u32 entry = *start; @@ -5118,16 +5341,21 @@ static int tigon3_dma_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb, } else { /* New SKB is guaranteed to be linear. */ entry = *start; - ret = skb_dma_map(&tp->pdev->dev, new_skb, DMA_TO_DEVICE); - new_addr = skb_shinfo(new_skb)->dma_head; + new_addr = pci_map_single(tp->pdev, new_skb->data, new_skb->len, + PCI_DMA_TODEVICE); + /* Make sure the mapping succeeded */ + if (pci_dma_mapping_error(tp->pdev, new_addr)) { + ret = -1; + dev_kfree_skb(new_skb); + new_skb = NULL; /* Make sure new skb does not cross any 4G boundaries. * Drop the packet if it does. */ - if (ret || tg3_4g_overflow_test(new_addr, new_skb->len)) { - if (!ret) - skb_dma_unmap(&tp->pdev->dev, new_skb, - DMA_TO_DEVICE); + } else if ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) && + tg3_4g_overflow_test(new_addr, new_skb->len)) { + pci_unmap_single(tp->pdev, new_addr, new_skb->len, + PCI_DMA_TODEVICE); ret = -1; dev_kfree_skb(new_skb); new_skb = NULL; @@ -5141,15 +5369,28 @@ static int tigon3_dma_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb, /* Now clean up the sw ring entries. */ i = 0; while (entry != last_plus_one) { + int len; + if (i == 0) - tnapi->tx_buffers[entry].skb = new_skb; + len = skb_headlen(skb); else + len = skb_shinfo(skb)->frags[i-1].size; + + pci_unmap_single(tp->pdev, + pci_unmap_addr(&tnapi->tx_buffers[entry], + mapping), + len, PCI_DMA_TODEVICE); + if (i == 0) { + tnapi->tx_buffers[entry].skb = new_skb; + pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping, + new_addr); + } else { tnapi->tx_buffers[entry].skb = NULL; + } entry = NEXT_TX(entry); i++; } - skb_dma_unmap(&tp->pdev->dev, skb, DMA_TO_DEVICE); dev_kfree_skb(skb); return ret; @@ -5179,21 +5420,22 @@ static void tg3_set_txd(struct tg3_napi *tnapi, int entry, } /* hard_start_xmit for devices that don't have any bugs and - * support TG3_FLG2_HW_TSO_2 only. + * support TG3_FLG2_HW_TSO_2 and TG3_FLG2_HW_TSO_3 only. */ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct tg3 *tp = netdev_priv(dev); u32 len, entry, base_flags, mss; - struct skb_shared_info *sp; dma_addr_t mapping; struct tg3_napi *tnapi; struct netdev_queue *txq; + unsigned int i, last; + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); tnapi = &tp->napi[skb_get_queue_mapping(skb)]; - if (tp->tg3_flags2 & TG3_FLG2_USING_MSIX) + if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS) tnapi++; /* We are running in BH disabled context with netif_tx_lock @@ -5238,7 +5480,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, hdrlen = ip_tcp_len + tcp_opt_len; } - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) { + if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) { mss |= (hdrlen & 0xc) << 12; if (hdrlen & 0x10) base_flags |= 0x00000010; @@ -5260,20 +5502,19 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, (vlan_tx_tag_get(skb) << 16)); #endif - if (skb_dma_map(&tp->pdev->dev, skb, DMA_TO_DEVICE)) { + len = skb_headlen(skb); + + /* Queue skb data, a.k.a. the main skb fragment. */ + mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(tp->pdev, mapping)) { dev_kfree_skb(skb); goto out_unlock; } - sp = skb_shinfo(skb); - - mapping = sp->dma_head; - tnapi->tx_buffers[entry].skb = skb; + pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping, mapping); - len = skb_headlen(skb); - - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 && + if ((tp->tg3_flags3 & TG3_FLG3_USE_JUMBO_BDFLAG) && !mss && skb->len > ETH_DATA_LEN) base_flags |= TXD_FLAG_JMB_PKT; @@ -5284,15 +5525,21 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, /* Now loop through additional data fragments, and queue them. */ if (skb_shinfo(skb)->nr_frags > 0) { - unsigned int i, last; - last = skb_shinfo(skb)->nr_frags - 1; for (i = 0; i <= last; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; len = frag->size; - mapping = sp->dma_maps[i]; + mapping = pci_map_page(tp->pdev, + frag->page, + frag->page_offset, + len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(tp->pdev, mapping)) + goto dma_error; + tnapi->tx_buffers[entry].skb = NULL; + pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping, + mapping); tg3_set_txd(tnapi, entry, mapping, len, base_flags, (i == last) | (mss << 1)); @@ -5315,6 +5562,27 @@ out_unlock: mmiowb(); return NETDEV_TX_OK; + +dma_error: + last = i; + entry = tnapi->tx_prod; + tnapi->tx_buffers[entry].skb = NULL; + pci_unmap_single(tp->pdev, + pci_unmap_addr(&tnapi->tx_buffers[entry], mapping), + skb_headlen(skb), + PCI_DMA_TODEVICE); + for (i = 0; i <= last; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + entry = NEXT_TX(entry); + + pci_unmap_page(tp->pdev, + pci_unmap_addr(&tnapi->tx_buffers[entry], + mapping), + frag->size, PCI_DMA_TODEVICE); + } + + dev_kfree_skb(skb); + return NETDEV_TX_OK; } static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *, @@ -5362,12 +5630,17 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb, { struct tg3 *tp = netdev_priv(dev); u32 len, entry, base_flags, mss; - struct skb_shared_info *sp; int would_hit_hwbug; dma_addr_t mapping; - struct tg3_napi *tnapi = &tp->napi[0]; + struct tg3_napi *tnapi; + struct netdev_queue *txq; + unsigned int i, last; - len = skb_headlen(skb); + + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); + tnapi = &tp->napi[skb_get_queue_mapping(skb)]; + if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS) + tnapi++; /* We are running in BH disabled context with netif_tx_lock * and TX reclaim runs via tp->napi.poll inside of a software @@ -5375,8 +5648,8 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb, * no IRQ context deadlocks to worry about either. Rejoice! */ if (unlikely(tg3_tx_avail(tnapi) <= (skb_shinfo(skb)->nr_frags + 1))) { - if (!netif_queue_stopped(dev)) { - netif_stop_queue(dev); + if (!netif_tx_queue_stopped(txq)) { + netif_tx_stop_queue(txq); /* This is a hard error, log it. */ printk(KERN_ERR PFX "%s: BUG! Tx Ring full when " @@ -5389,10 +5662,10 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb, base_flags = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) base_flags |= TXD_FLAG_TCPUDP_CSUM; - mss = 0; + if ((mss = skb_shinfo(skb)->gso_size) != 0) { struct iphdr *iph; - int tcp_opt_len, ip_tcp_len, hdr_len; + u32 tcp_opt_len, ip_tcp_len, hdr_len; if (skb_header_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { @@ -5423,8 +5696,15 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb, IPPROTO_TCP, 0); - if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) || - (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) { + if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) { + mss |= (hdr_len & 0xc) << 12; + if (hdr_len & 0x10) + base_flags |= 0x00000010; + base_flags |= (hdr_len & 0x3e0) << 5; + } else if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) + mss |= hdr_len << 9; + else if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_1) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { if (tcp_opt_len || iph->ihl > 5) { int tsflags; @@ -5446,22 +5726,35 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb, (vlan_tx_tag_get(skb) << 16)); #endif - if (skb_dma_map(&tp->pdev->dev, skb, DMA_TO_DEVICE)) { + if ((tp->tg3_flags3 & TG3_FLG3_USE_JUMBO_BDFLAG) && + !mss && skb->len > ETH_DATA_LEN) + base_flags |= TXD_FLAG_JMB_PKT; + + len = skb_headlen(skb); + + mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(tp->pdev, mapping)) { dev_kfree_skb(skb); goto out_unlock; } - sp = skb_shinfo(skb); - - mapping = sp->dma_head; - tnapi->tx_buffers[entry].skb = skb; + pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping, mapping); would_hit_hwbug = 0; - if (tp->tg3_flags3 & TG3_FLG3_5701_DMA_BUG) + if ((tp->tg3_flags3 & TG3_FLG3_SHORT_DMA_BUG) && len <= 8) would_hit_hwbug = 1; - else if (tg3_4g_overflow_test(mapping, len)) + + if ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) && + tg3_4g_overflow_test(mapping, len)) + would_hit_hwbug = 1; + + if ((tp->tg3_flags3 & TG3_FLG3_40BIT_DMA_LIMIT_BUG) && + tg3_40bit_overflow_test(tp, mapping, len)) + would_hit_hwbug = 1; + + if (tp->tg3_flags3 & TG3_FLG3_5701_DMA_BUG) would_hit_hwbug = 1; tg3_set_txd(tnapi, entry, mapping, len, base_flags, @@ -5471,21 +5764,32 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb, /* Now loop through additional data fragments, and queue them. */ if (skb_shinfo(skb)->nr_frags > 0) { - unsigned int i, last; - last = skb_shinfo(skb)->nr_frags - 1; for (i = 0; i <= last; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; len = frag->size; - mapping = sp->dma_maps[i]; + mapping = pci_map_page(tp->pdev, + frag->page, + frag->page_offset, + len, PCI_DMA_TODEVICE); tnapi->tx_buffers[entry].skb = NULL; + pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping, + mapping); + if (pci_dma_mapping_error(tp->pdev, mapping)) + goto dma_error; - if (tg3_4g_overflow_test(mapping, len)) + if ((tp->tg3_flags3 & TG3_FLG3_SHORT_DMA_BUG) && + len <= 8) would_hit_hwbug = 1; - if (tg3_40bit_overflow_test(tp, mapping, len)) + if ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) && + tg3_4g_overflow_test(mapping, len)) + would_hit_hwbug = 1; + + if ((tp->tg3_flags3 & TG3_FLG3_40BIT_DMA_LIMIT_BUG) && + tg3_40bit_overflow_test(tp, mapping, len)) would_hit_hwbug = 1; if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) @@ -5509,7 +5813,7 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb, /* If the workaround fails due to memory/mapping * failure, silently drop this packet. */ - if (tigon3_dma_hwbug_workaround(tp, skb, last_plus_one, + if (tigon3_dma_hwbug_workaround(tnapi, skb, last_plus_one, &start, base_flags, mss)) goto out_unlock; @@ -5517,19 +5821,40 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb, } /* Packets are ready, update Tx producer idx local and on card. */ - tw32_tx_mbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW, entry); + tw32_tx_mbox(tnapi->prodmbox, entry); tnapi->tx_prod = entry; if (unlikely(tg3_tx_avail(tnapi) <= (MAX_SKB_FRAGS + 1))) { - netif_stop_queue(dev); + netif_tx_stop_queue(txq); if (tg3_tx_avail(tnapi) > TG3_TX_WAKEUP_THRESH(tnapi)) - netif_wake_queue(tp->dev); + netif_tx_wake_queue(txq); } out_unlock: mmiowb(); return NETDEV_TX_OK; + +dma_error: + last = i; + entry = tnapi->tx_prod; + tnapi->tx_buffers[entry].skb = NULL; + pci_unmap_single(tp->pdev, + pci_unmap_addr(&tnapi->tx_buffers[entry], mapping), + skb_headlen(skb), + PCI_DMA_TODEVICE); + for (i = 0; i <= last; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + entry = NEXT_TX(entry); + + pci_unmap_page(tp->pdev, + pci_unmap_addr(&tnapi->tx_buffers[entry], + mapping), + frag->size, PCI_DMA_TODEVICE); + } + + dev_kfree_skb(skb); + return NETDEV_TX_OK; } static inline void tg3_set_mtu(struct net_device *dev, struct tg3 *tp, @@ -5594,36 +5919,33 @@ static void tg3_rx_prodring_free(struct tg3 *tp, struct tg3_rx_prodring_set *tpr) { int i; - struct ring_info *rxp; - - for (i = 0; i < TG3_RX_RING_SIZE; i++) { - rxp = &tpr->rx_std_buffers[i]; - if (rxp->skb == NULL) - continue; + if (tpr != &tp->prodring[0]) { + for (i = tpr->rx_std_cons_idx; i != tpr->rx_std_prod_idx; + i = (i + 1) % TG3_RX_RING_SIZE) + tg3_rx_skb_free(tp, &tpr->rx_std_buffers[i], + tp->rx_pkt_map_sz); + + if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) { + for (i = tpr->rx_jmb_cons_idx; + i != tpr->rx_jmb_prod_idx; + i = (i + 1) % TG3_RX_JUMBO_RING_SIZE) { + tg3_rx_skb_free(tp, &tpr->rx_jmb_buffers[i], + TG3_RX_JMB_MAP_SZ); + } + } - pci_unmap_single(tp->pdev, - pci_unmap_addr(rxp, mapping), - tp->rx_pkt_map_sz, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(rxp->skb); - rxp->skb = NULL; + return; } - if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) { - for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) { - rxp = &tpr->rx_jmb_buffers[i]; - - if (rxp->skb == NULL) - continue; + for (i = 0; i < TG3_RX_RING_SIZE; i++) + tg3_rx_skb_free(tp, &tpr->rx_std_buffers[i], + tp->rx_pkt_map_sz); - pci_unmap_single(tp->pdev, - pci_unmap_addr(rxp, mapping), - TG3_RX_JMB_MAP_SZ, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(rxp->skb); - rxp->skb = NULL; - } + if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) { + for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) + tg3_rx_skb_free(tp, &tpr->rx_jmb_buffers[i], + TG3_RX_JMB_MAP_SZ); } } @@ -5638,7 +5960,19 @@ static int tg3_rx_prodring_alloc(struct tg3 *tp, struct tg3_rx_prodring_set *tpr) { u32 i, rx_pkt_dma_sz; - struct tg3_napi *tnapi = &tp->napi[0]; + + tpr->rx_std_cons_idx = 0; + tpr->rx_std_prod_idx = 0; + tpr->rx_jmb_cons_idx = 0; + tpr->rx_jmb_prod_idx = 0; + + if (tpr != &tp->prodring[0]) { + memset(&tpr->rx_std_buffers[0], 0, TG3_RX_STD_BUFF_RING_SIZE); + if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) + memset(&tpr->rx_jmb_buffers[0], 0, + TG3_RX_JMB_BUFF_RING_SIZE); + goto done; + } /* Zero out all descriptors. */ memset(tpr->rx_std, 0, TG3_RX_RING_BYTES); @@ -5665,7 +5999,7 @@ static int tg3_rx_prodring_alloc(struct tg3 *tp, /* Now allocate fresh SKBs for each rx ring. */ for (i = 0; i < tp->rx_pending; i++) { - if (tg3_alloc_rx_skb(tnapi, RXD_OPAQUE_RING_STD, -1, i) < 0) { + if (tg3_alloc_rx_skb(tp, tpr, RXD_OPAQUE_RING_STD, i) < 0) { printk(KERN_WARNING PFX "%s: Using a smaller RX standard ring, " "only %d out of %d buffers were allocated " @@ -5696,8 +6030,8 @@ static int tg3_rx_prodring_alloc(struct tg3 *tp, } for (i = 0; i < tp->rx_jumbo_pending; i++) { - if (tg3_alloc_rx_skb(tnapi, RXD_OPAQUE_RING_JUMBO, - -1, i) < 0) { + if (tg3_alloc_rx_skb(tp, tpr, RXD_OPAQUE_RING_JUMBO, + i) < 0) { printk(KERN_WARNING PFX "%s: Using a smaller RX jumbo ring, " "only %d out of %d buffers were " @@ -5741,8 +6075,7 @@ static void tg3_rx_prodring_fini(struct tg3 *tp, static int tg3_rx_prodring_init(struct tg3 *tp, struct tg3_rx_prodring_set *tpr) { - tpr->rx_std_buffers = kzalloc(sizeof(struct ring_info) * - TG3_RX_RING_SIZE, GFP_KERNEL); + tpr->rx_std_buffers = kzalloc(TG3_RX_STD_BUFF_RING_SIZE, GFP_KERNEL); if (!tpr->rx_std_buffers) return -ENOMEM; @@ -5752,8 +6085,7 @@ static int tg3_rx_prodring_init(struct tg3 *tp, goto err_out; if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) { - tpr->rx_jmb_buffers = kzalloc(sizeof(struct ring_info) * - TG3_RX_JUMBO_RING_SIZE, + tpr->rx_jmb_buffers = kzalloc(TG3_RX_JMB_BUFF_RING_SIZE, GFP_KERNEL); if (!tpr->rx_jmb_buffers) goto err_out; @@ -5790,8 +6122,9 @@ static void tg3_free_rings(struct tg3 *tp) continue; for (i = 0; i < TG3_TX_RING_SIZE; ) { - struct tx_ring_info *txp; + struct ring_info *txp; struct sk_buff *skb; + unsigned int k; txp = &tnapi->tx_buffers[i]; skb = txp->skb; @@ -5801,17 +6134,29 @@ static void tg3_free_rings(struct tg3 *tp) continue; } - skb_dma_unmap(&tp->pdev->dev, skb, DMA_TO_DEVICE); - + pci_unmap_single(tp->pdev, + pci_unmap_addr(txp, mapping), + skb_headlen(skb), + PCI_DMA_TODEVICE); txp->skb = NULL; - i += skb_shinfo(skb)->nr_frags + 1; + i++; + + for (k = 0; k < skb_shinfo(skb)->nr_frags; k++) { + txp = &tnapi->tx_buffers[i & (TG3_TX_RING_SIZE - 1)]; + pci_unmap_page(tp->pdev, + pci_unmap_addr(txp, mapping), + skb_shinfo(skb)->frags[k].size, + PCI_DMA_TODEVICE); + i++; + } dev_kfree_skb_any(skb); } - } - tg3_rx_prodring_free(tp, &tp->prodring[0]); + if (tp->irq_cnt == 1 || j != tp->irq_cnt - 1) + tg3_rx_prodring_free(tp, &tp->prodring[j]); + } } /* Initialize tx/rx rings for packet processing. @@ -5845,9 +6190,13 @@ static int tg3_init_rings(struct tg3 *tp) tnapi->rx_rcb_ptr = 0; if (tnapi->rx_rcb) memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp)); + + if ((tp->irq_cnt == 1 || i != tp->irq_cnt - 1) && + tg3_rx_prodring_alloc(tp, &tp->prodring[i])) + return -ENOMEM; } - return tg3_rx_prodring_alloc(tp, &tp->prodring[0]); + return 0; } /* @@ -5891,7 +6240,8 @@ static void tg3_free_consistent(struct tg3 *tp) tp->hw_stats = NULL; } - tg3_rx_prodring_fini(tp, &tp->prodring[0]); + for (i = 0; i < (tp->irq_cnt == 1 ? 1 : tp->irq_cnt - 1); i++) + tg3_rx_prodring_fini(tp, &tp->prodring[i]); } /* @@ -5902,8 +6252,10 @@ static int tg3_alloc_consistent(struct tg3 *tp) { int i; - if (tg3_rx_prodring_init(tp, &tp->prodring[0])) - return -ENOMEM; + for (i = 0; i < (tp->irq_cnt == 1 ? 1 : tp->irq_cnt - 1); i++) { + if (tg3_rx_prodring_init(tp, &tp->prodring[i])) + goto err_out; + } tp->hw_stats = pci_alloc_consistent(tp->pdev, sizeof(struct tg3_hw_stats), @@ -5926,6 +6278,24 @@ static int tg3_alloc_consistent(struct tg3 *tp) memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE); sblk = tnapi->hw_status; + /* If multivector TSS is enabled, vector 0 does not handle + * tx interrupts. Don't allocate any resources for it. + */ + if ((!i && !(tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS)) || + (i && (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS))) { + tnapi->tx_buffers = kzalloc(sizeof(struct ring_info) * + TG3_TX_RING_SIZE, + GFP_KERNEL); + if (!tnapi->tx_buffers) + goto err_out; + + tnapi->tx_ring = pci_alloc_consistent(tp->pdev, + TG3_TX_RING_BYTES, + &tnapi->tx_desc_mapping); + if (!tnapi->tx_ring) + goto err_out; + } + /* * When RSS is enabled, the status block format changes * slightly. The "rx_jumbo_consumer", "reserved", @@ -5947,6 +6317,11 @@ static int tg3_alloc_consistent(struct tg3 *tp) break; } + if (tp->irq_cnt == 1) + tnapi->prodring = &tp->prodring[0]; + else if (i) + tnapi->prodring = &tp->prodring[i - 1]; + /* * If multivector RSS is enabled, vector 0 does not handle * rx or tx interrupts. Don't allocate any resources for it. @@ -5961,17 +6336,6 @@ static int tg3_alloc_consistent(struct tg3 *tp) goto err_out; memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp)); - - tnapi->tx_buffers = kzalloc(sizeof(struct tx_ring_info) * - TG3_TX_RING_SIZE, GFP_KERNEL); - if (!tnapi->tx_buffers) - goto err_out; - - tnapi->tx_ring = pci_alloc_consistent(tp->pdev, - TG3_TX_RING_BYTES, - &tnapi->tx_desc_mapping); - if (!tnapi->tx_ring) - goto err_out; } return 0; @@ -6580,10 +6944,35 @@ static int tg3_chip_reset(struct tg3 *tp) tg3_mdio_start(tp); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780) { + u8 phy_addr; + + phy_addr = tp->phy_addr; + tp->phy_addr = TG3_PHY_PCIE_ADDR; + + tg3_writephy(tp, TG3_PCIEPHY_BLOCK_ADDR, + TG3_PCIEPHY_TXB_BLK << TG3_PCIEPHY_BLOCK_SHIFT); + val = TG3_PCIEPHY_TX0CTRL1_TXOCM | TG3_PCIEPHY_TX0CTRL1_RDCTL | + TG3_PCIEPHY_TX0CTRL1_TXCMV | TG3_PCIEPHY_TX0CTRL1_TKSEL | + TG3_PCIEPHY_TX0CTRL1_NB_EN; + tg3_writephy(tp, TG3_PCIEPHY_TX0CTRL1, val); + udelay(10); + + tg3_writephy(tp, TG3_PCIEPHY_BLOCK_ADDR, + TG3_PCIEPHY_XGXS_BLK1 << TG3_PCIEPHY_BLOCK_SHIFT); + val = TG3_PCIEPHY_PWRMGMT4_LOWPWR_EN | + TG3_PCIEPHY_PWRMGMT4_L1PLLPD_EN; + tg3_writephy(tp, TG3_PCIEPHY_PWRMGMT4, val); + udelay(10); + + tp->phy_addr = phy_addr; + } + if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) && tp->pci_chip_rev_id != CHIPREV_ID_5750_A0 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717) { + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765) { val = tr32(0x7c00); tw32(0x7c00, val | (1 << 25)); @@ -6935,19 +7324,21 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) { int i; - if (!(tp->tg3_flags2 & TG3_FLG2_USING_MSIX)) { + if (!(tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS)) { tw32(HOSTCC_TXCOL_TICKS, ec->tx_coalesce_usecs); tw32(HOSTCC_TXMAX_FRAMES, ec->tx_max_coalesced_frames); tw32(HOSTCC_TXCOAL_MAXF_INT, ec->tx_max_coalesced_frames_irq); - - tw32(HOSTCC_RXCOL_TICKS, ec->rx_coalesce_usecs); - tw32(HOSTCC_RXMAX_FRAMES, ec->rx_max_coalesced_frames); - tw32(HOSTCC_RXCOAL_MAXF_INT, ec->rx_max_coalesced_frames_irq); } else { tw32(HOSTCC_TXCOL_TICKS, 0); tw32(HOSTCC_TXMAX_FRAMES, 0); tw32(HOSTCC_TXCOAL_MAXF_INT, 0); + } + if (!(tp->tg3_flags2 & TG3_FLG2_USING_MSIX)) { + tw32(HOSTCC_RXCOL_TICKS, ec->rx_coalesce_usecs); + tw32(HOSTCC_RXMAX_FRAMES, ec->rx_max_coalesced_frames); + tw32(HOSTCC_RXCOAL_MAXF_INT, ec->rx_max_coalesced_frames_irq); + } else { tw32(HOSTCC_RXCOL_TICKS, 0); tw32(HOSTCC_RXMAX_FRAMES, 0); tw32(HOSTCC_RXCOAL_MAXF_INT, 0); @@ -6970,25 +7361,31 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) reg = HOSTCC_RXCOL_TICKS_VEC1 + i * 0x18; tw32(reg, ec->rx_coalesce_usecs); - reg = HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18; - tw32(reg, ec->tx_coalesce_usecs); reg = HOSTCC_RXMAX_FRAMES_VEC1 + i * 0x18; tw32(reg, ec->rx_max_coalesced_frames); - reg = HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18; - tw32(reg, ec->tx_max_coalesced_frames); reg = HOSTCC_RXCOAL_MAXF_INT_VEC1 + i * 0x18; tw32(reg, ec->rx_max_coalesced_frames_irq); - reg = HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18; - tw32(reg, ec->tx_max_coalesced_frames_irq); + + if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS) { + reg = HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18; + tw32(reg, ec->tx_coalesce_usecs); + reg = HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18; + tw32(reg, ec->tx_max_coalesced_frames); + reg = HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18; + tw32(reg, ec->tx_max_coalesced_frames_irq); + } } for (; i < tp->irq_max - 1; i++) { tw32(HOSTCC_RXCOL_TICKS_VEC1 + i * 0x18, 0); - tw32(HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18, 0); tw32(HOSTCC_RXMAX_FRAMES_VEC1 + i * 0x18, 0); - tw32(HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18, 0); tw32(HOSTCC_RXCOAL_MAXF_INT_VEC1 + i * 0x18, 0); - tw32(HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18, 0); + + if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS) { + tw32(HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18, 0); + tw32(HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18, 0); + tw32(HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18, 0); + } } } @@ -7002,6 +7399,8 @@ static void tg3_rings_reset(struct tg3 *tp) /* Disable all transmit rings but the first. */ if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 16; + else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) + limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 2; else limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE; @@ -7016,7 +7415,8 @@ static void tg3_rings_reset(struct tg3 *tp) limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 17; else if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 16; - else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) + else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 4; else limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE; @@ -7089,17 +7489,19 @@ static void tg3_rings_reset(struct tg3 *tp) /* Clear status block in ram. */ memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE); - tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping, - (TG3_TX_RING_SIZE << - BDINFO_FLAGS_MAXLEN_SHIFT), - NIC_SRAM_TX_BUFFER_DESC); + if (tnapi->tx_ring) { + tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping, + (TG3_TX_RING_SIZE << + BDINFO_FLAGS_MAXLEN_SHIFT), + NIC_SRAM_TX_BUFFER_DESC); + txrcb += TG3_BDINFO_SIZE; + } tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping, (TG3_RX_RCB_RING_SIZE(tp) << BDINFO_FLAGS_MAXLEN_SHIFT), 0); stblk += 8; - txrcb += TG3_BDINFO_SIZE; rxrcb += TG3_BDINFO_SIZE; } } @@ -7162,15 +7564,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) tw32(TG3_PCIE_EIDLE_DELAY, val | TG3_PCIE_EIDLE_DELAY_13_CLKS); tw32(TG3_CORR_ERR_STAT, TG3_CORR_ERR_STAT_CLEAR); - } - if (tp->tg3_flags3 & TG3_FLG3_TOGGLE_10_100_L1PLLPD) { - val = tr32(TG3_PCIE_LNKCTL); - if (tp->tg3_flags3 & TG3_FLG3_CLKREQ_BUG) - val |= TG3_PCIE_LNKCTL_L1_PLL_PD_DIS; - else - val &= ~TG3_PCIE_LNKCTL_L1_PLL_PD_DIS; - tw32(TG3_PCIE_LNKCTL, val); + val = tr32(TG3_PCIE_LNKCTL) & ~TG3_PCIE_LNKCTL_L1_PLL_PD_EN; + tw32(TG3_PCIE_LNKCTL, val | TG3_PCIE_LNKCTL_L1_PLL_PD_DIS); } /* This works around an issue with Athlon chipsets on @@ -7217,9 +7613,13 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) if (err) return err; - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784 && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761 && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) { + val = tr32(TG3PCI_DMA_RW_CTRL) & + ~DMA_RWCTRL_DIS_CACHE_ALIGNMENT; + tw32(TG3PCI_DMA_RW_CTRL, val | tp->dma_rwctrl); + } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761) { /* This value is determined during the probe time DMA * engine test, tg3_test_dma. */ @@ -7342,8 +7742,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) ((u64) tpr->rx_std_mapping >> 32)); tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, ((u64) tpr->rx_std_mapping & 0xffffffff)); - tw32(RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR, - NIC_SRAM_RX_BUFFER_DESC); + if (!(tp->tg3_flags3 & TG3_FLG3_5755_PLUS)) + tw32(RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR, + NIC_SRAM_RX_BUFFER_DESC); /* Disable the mini ring */ if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) @@ -7366,14 +7767,16 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, (RX_JUMBO_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT) | BDINFO_FLAGS_USE_EXT_RECV); - tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_NIC_ADDR, - NIC_SRAM_RX_JUMBO_BUFFER_DESC); + if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_NIC_ADDR, + NIC_SRAM_RX_JUMBO_BUFFER_DESC); } else { tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); } - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) val = (RX_STD_MAX_SIZE_5705 << BDINFO_FLAGS_MAXLEN_SHIFT) | (RX_STD_MAX_SIZE << 2); else @@ -7383,16 +7786,15 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, val); - tpr->rx_std_ptr = tp->rx_pending; - tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, - tpr->rx_std_ptr); + tpr->rx_std_prod_idx = tp->rx_pending; + tw32_rx_mbox(TG3_RX_STD_PROD_IDX_REG, tpr->rx_std_prod_idx); - tpr->rx_jmb_ptr = (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) ? + tpr->rx_jmb_prod_idx = (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) ? tp->rx_jumbo_pending : 0; - tw32_rx_mbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, - tpr->rx_jmb_ptr); + tw32_rx_mbox(TG3_RX_JMB_PROD_IDX_REG, tpr->rx_jmb_prod_idx); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) { tw32(STD_REPLENISH_LWM, 32); tw32(JMB_REPLENISH_LWM, 16); } @@ -7453,7 +7855,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) rdmac_mode |= RDMAC_MODE_IPV4_LSO_EN; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || + if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780) rdmac_mode |= RDMAC_MODE_IPV6_LSO_EN; @@ -7602,6 +8005,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) if (tp->tg3_flags3 & TG3_FLG3_5755_PLUS) val |= WDMAC_MODE_STATUS_TAG_FIX; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) + val |= WDMAC_MODE_BURST_ALL_DATA; + tw32_f(WDMAC_MODE, val); udelay(40); @@ -7641,7 +8047,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE | 0x8); val = SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE; - if (tp->tg3_flags2 & TG3_FLG2_USING_MSIX) + if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS) val |= SNDBDI_MODE_MULTI_TXQ_EN; tw32(SNDBDI_MODE, val); tw32(SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE); @@ -8065,7 +8471,8 @@ static int tg3_test_interrupt(struct tg3 *tp) * Turn off MSI one shot mode. Otherwise this test has no * observable way to know whether the interrupt was delivered. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 && + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) && (tp->tg3_flags2 & TG3_FLG2_USING_MSI)) { val = tr32(MSGINT_MODE) | MSGINT_MODE_ONE_SHOT_DISABLE; tw32(MSGINT_MODE, val); @@ -8108,7 +8515,8 @@ static int tg3_test_interrupt(struct tg3 *tp) if (intr_ok) { /* Reenable MSI one shot mode. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 && + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) && (tp->tg3_flags2 & TG3_FLG2_USING_MSI)) { val = tr32(MSGINT_MODE) & ~MSGINT_MODE_ONE_SHOT_DISABLE; tw32(MSGINT_MODE, val); @@ -8249,7 +8657,11 @@ static bool tg3_enable_msix(struct tg3 *tp) for (i = 0; i < tp->irq_max; i++) tp->napi[i].irq_vec = msix_ent[i].vector; - tp->dev->real_num_tx_queues = tp->irq_cnt - 1; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) { + tp->tg3_flags3 |= TG3_FLG3_ENABLE_TSS; + tp->dev->real_num_tx_queues = tp->irq_cnt - 1; + } else + tp->dev->real_num_tx_queues = 1; return true; } @@ -8400,6 +8812,7 @@ static int tg3_open(struct net_device *dev) } if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765 && (tp->tg3_flags2 & TG3_FLG2_USING_MSI) && (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI)) { u32 val = tr32(PCIE_TRANSACTION_CFG); @@ -9240,9 +9653,11 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) struct tg3 *tp = netdev_priv(dev); if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { + struct phy_device *phydev; if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return -EAGAIN; - return phy_ethtool_gset(tp->mdio_bus->phy_map[PHY_ADDR], cmd); + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + return phy_ethtool_gset(phydev, cmd); } cmd->supported = (SUPPORTED_Autoneg); @@ -9281,9 +9696,11 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) struct tg3 *tp = netdev_priv(dev); if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { + struct phy_device *phydev; if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return -EAGAIN; - return phy_ethtool_sset(tp->mdio_bus->phy_map[PHY_ADDR], cmd); + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + return phy_ethtool_sset(phydev, cmd); } if (cmd->autoneg != AUTONEG_ENABLE && @@ -9436,15 +9853,16 @@ static int tg3_set_tso(struct net_device *dev, u32 value) return 0; } if ((dev->features & NETIF_F_IPV6_CSUM) && - (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2)) { + ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) || + (tp->tg3_flags2 & TG3_FLG2_HW_TSO_3))) { if (value) { dev->features |= NETIF_F_TSO6; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 && GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780) dev->features |= NETIF_F_TSO_ECN; } else dev->features &= ~(NETIF_F_TSO6 | NETIF_F_TSO_ECN); @@ -9466,7 +9884,7 @@ static int tg3_nway_reset(struct net_device *dev) if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return -EAGAIN; - r = phy_start_aneg(tp->mdio_bus->phy_map[PHY_ADDR]); + r = phy_start_aneg(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]); } else { u32 bmcr; @@ -9585,7 +10003,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam u32 newadv; struct phy_device *phydev; - phydev = tp->mdio_bus->phy_map[PHY_ADDR]; + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; if (epause->rx_pause) { if (epause->tx_pause) @@ -10339,6 +10757,10 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode) tx_data[i] = (u8) (i & 0xff); map = pci_map_single(tp->pdev, skb->data, tx_len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(tp->pdev, map)) { + dev_kfree_skb(skb); + return -EIO; + } tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE | rnapi->coal_now); @@ -10359,8 +10781,8 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode) udelay(10); - /* 250 usec to allow enough time on some 10/100 Mbps devices. */ - for (i = 0; i < 25; i++) { + /* 350 usec to allow enough time on some 10/100 Mbps devices. */ + for (i = 0; i < 35; i++) { tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE | coal_now); @@ -10565,9 +10987,11 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) int err; if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { + struct phy_device *phydev; if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)) return -EAGAIN; - return phy_mii_ioctl(tp->mdio_bus->phy_map[PHY_ADDR], data, cmd); + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + return phy_mii_ioctl(phydev, data, cmd); } switch(cmd) { @@ -10887,7 +11311,7 @@ static void __devinit tg3_get_5752_nvram_info(struct tg3 *tp) /* NVRAM protection for TPM */ if (nvcfg1 & (1 << 27)) - tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM; + tp->tg3_flags3 |= TG3_FLG3_PROTECTED_NVRAM; switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) { case FLASH_5752VENDOR_ATMEL_EEPROM_64KHZ: @@ -10928,7 +11352,7 @@ static void __devinit tg3_get_5755_nvram_info(struct tg3 *tp) /* NVRAM protection for TPM */ if (nvcfg1 & (1 << 27)) { - tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM; + tp->tg3_flags3 |= TG3_FLG3_PROTECTED_NVRAM; protect = 1; } @@ -11022,7 +11446,7 @@ static void __devinit tg3_get_5761_nvram_info(struct tg3 *tp) /* NVRAM protection for TPM */ if (nvcfg1 & (1 << 27)) { - tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM; + tp->tg3_flags3 |= TG3_FLG3_PROTECTED_NVRAM; protect = 1; } @@ -11283,7 +11707,8 @@ static void __devinit tg3_nvram_init(struct tg3 *tp) tg3_get_5761_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) tg3_get_5906_nvram_info(tp); - else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780) + else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) tg3_get_57780_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) tg3_get_5717_nvram_info(tp); @@ -11524,7 +11949,7 @@ static int tg3_nvram_write_block(struct tg3 *tp, u32 offset, u32 len, u8 *buf) tg3_enable_nvram_access(tp); if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && - !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) + !(tp->tg3_flags3 & TG3_FLG3_PROTECTED_NVRAM)) tw32(NVRAM_WRITE1, 0x406); grc_mode = tr32(GRC_MODE); @@ -12008,7 +12433,7 @@ skip_phy_reset: static void __devinit tg3_read_partno(struct tg3 *tp) { - unsigned char vpd_data[256]; /* in little-endian format */ + unsigned char vpd_data[TG3_NVM_VPD_LEN]; /* in little-endian format */ unsigned int i; u32 magic; @@ -12017,48 +12442,37 @@ static void __devinit tg3_read_partno(struct tg3 *tp) goto out_not_found; if (magic == TG3_EEPROM_MAGIC) { - for (i = 0; i < 256; i += 4) { + for (i = 0; i < TG3_NVM_VPD_LEN; i += 4) { u32 tmp; /* The data is in little-endian format in NVRAM. * Use the big-endian read routines to preserve * the byte order as it exists in NVRAM. */ - if (tg3_nvram_read_be32(tp, 0x100 + i, &tmp)) + if (tg3_nvram_read_be32(tp, TG3_NVM_VPD_OFF + i, &tmp)) goto out_not_found; memcpy(&vpd_data[i], &tmp, sizeof(tmp)); } } else { - int vpd_cap; - - vpd_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_VPD); - for (i = 0; i < 256; i += 4) { - u32 tmp, j = 0; - __le32 v; - u16 tmp16; - - pci_write_config_word(tp->pdev, vpd_cap + PCI_VPD_ADDR, - i); - while (j++ < 100) { - pci_read_config_word(tp->pdev, vpd_cap + - PCI_VPD_ADDR, &tmp16); - if (tmp16 & 0x8000) - break; - msleep(1); - } - if (!(tmp16 & 0x8000)) + ssize_t cnt; + unsigned int pos = 0, i = 0; + + for (; pos < TG3_NVM_VPD_LEN && i < 3; i++, pos += cnt) { + cnt = pci_read_vpd(tp->pdev, pos, + TG3_NVM_VPD_LEN - pos, + &vpd_data[pos]); + if (cnt == -ETIMEDOUT || -EINTR) + cnt = 0; + else if (cnt < 0) goto out_not_found; - - pci_read_config_dword(tp->pdev, vpd_cap + PCI_VPD_DATA, - &tmp); - v = cpu_to_le32(tmp); - memcpy(&vpd_data[i], &v, sizeof(v)); } + if (pos != TG3_NVM_VPD_LEN) + goto out_not_found; } /* Now parse and find the part number. */ - for (i = 0; i < 254; ) { + for (i = 0; i < TG3_NVM_VPD_LEN - 2; ) { unsigned char val = vpd_data[i]; unsigned int block_end; @@ -12077,7 +12491,7 @@ static void __devinit tg3_read_partno(struct tg3 *tp) (vpd_data[i + 2] << 8))); i += 3; - if (block_end > 256) + if (block_end > TG3_NVM_VPD_LEN) goto out_not_found; while (i < (block_end - 2)) { @@ -12086,7 +12500,8 @@ static void __devinit tg3_read_partno(struct tg3 *tp) int partno_len = vpd_data[i + 2]; i += 3; - if (partno_len > 24 || (partno_len + i) > 256) + if (partno_len > TG3_BPN_SIZE || + (partno_len + i) > TG3_NVM_VPD_LEN) goto out_not_found; memcpy(tp->board_part_number, @@ -12117,6 +12532,8 @@ out_not_found: else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 && tp->pdev->device == TG3PCI_DEVICE_TIGON3_57788) strcpy(tp->board_part_number, "BCM57788"); + else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) + strcpy(tp->board_part_number, "BCM57765"); else strcpy(tp->board_part_number, "none"); } @@ -12400,13 +12817,21 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_USE_PROD_ID_REG) { u32 prod_id_asic_rev; - if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717C || - tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717S || - tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718C || - tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718S) + if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_5724) pci_read_config_dword(tp->pdev, TG3PCI_GEN2_PRODID_ASICREV, &prod_id_asic_rev); + else if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_57781 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57785 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57761 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57765 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57791 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57795) + pci_read_config_dword(tp->pdev, + TG3PCI_GEN15_PRODID_ASICREV, + &prod_id_asic_rev); else pci_read_config_dword(tp->pdev, TG3PCI_PRODID_ASICREV, &prod_id_asic_rev); @@ -12560,7 +12985,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) tp->tg3_flags3 |= TG3_FLG3_5755_PLUS; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 || @@ -12586,6 +13012,30 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) tp->dev->features |= NETIF_F_IPV6_CSUM; } + /* Determine TSO capabilities */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) + tp->tg3_flags2 |= TG3_FLG2_HW_TSO_3; + else if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) + tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; + else if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) { + tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 | TG3_FLG2_TSO_BUG; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 && + tp->pci_chip_rev_id >= CHIPREV_ID_5750_C2) + tp->tg3_flags2 &= ~TG3_FLG2_TSO_BUG; + } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701 && + tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) { + tp->tg3_flags2 |= TG3_FLG2_TSO_BUG; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) + tp->fw_needed = FIRMWARE_TG3TSO5; + else + tp->fw_needed = FIRMWARE_TG3TSO; + } + + tp->irq_max = 1; + if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) { tp->tg3_flags |= TG3_FLAG_SUPPORT_MSI; if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_AX || @@ -12597,29 +13047,31 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { - tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI; - } else { - tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 | TG3_FLG2_TSO_BUG; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == - ASIC_REV_5750 && - tp->pci_chip_rev_id >= CHIPREV_ID_5750_C2) - tp->tg3_flags2 &= ~TG3_FLG2_TSO_BUG; } - } - tp->irq_max = 1; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) { + tp->tg3_flags |= TG3_FLAG_SUPPORT_MSIX; + tp->irq_max = TG3_IRQ_MAX_VECS; + } + } -#ifdef TG3_NAPI - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) { - tp->tg3_flags |= TG3_FLAG_SUPPORT_MSIX; - tp->irq_max = TG3_IRQ_MAX_VECS; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) + tp->tg3_flags3 |= TG3_FLG3_SHORT_DMA_BUG; + else if (!(tp->tg3_flags3 & TG3_FLG3_5755_PLUS)) { + tp->tg3_flags3 |= TG3_FLG3_4G_DMA_BNDRY_BUG; + tp->tg3_flags3 |= TG3_FLG3_40BIT_DMA_LIMIT_BUG; } -#endif + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) + tp->tg3_flags3 |= TG3_FLG3_USE_JUMBO_BDFLAG; if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS) || (tp->tg3_flags2 & TG3_FLG2_5780_CLASS) || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) + (tp->tg3_flags3 & TG3_FLG3_USE_JUMBO_BDFLAG)) tp->tg3_flags |= TG3_FLAG_JUMBO_CAPABLE; pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, @@ -12812,7 +13264,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) tp->tg3_flags |= TG3_FLAG_CPMU_PRESENT; /* Set up tp->grc_local_ctrl before calling tg3_set_power_state(). @@ -12891,7 +13344,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) !(tp->tg3_flags3 & TG3_FLG3_PHY_IS_FET) && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57780 && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717) { + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765) { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || @@ -12926,11 +13380,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780) tp->tg3_flags3 |= TG3_FLG3_USE_PHYLIB; - if ((tp->pci_chip_rev_id == CHIPREV_ID_57780_A1 && - tr32(RCVLPC_STATS_ENABLE) & RCVLPC_STATSENAB_ASF_FIX) || - tp->pci_chip_rev_id == CHIPREV_ID_57780_A0) - tp->tg3_flags3 |= TG3_FLG3_TOGGLE_10_100_L1PLLPD; - err = tg3_mdio_init(tp); if (err) return err; @@ -13220,6 +13669,12 @@ static u32 __devinit tg3_calc_dma_bndry(struct tg3 *tp, u32 val) #endif #endif + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) { + val = goal ? 0 : DMA_RWCTRL_DIS_CACHE_ALIGNMENT; + goto out; + } + if (!goal) goto out; @@ -13414,7 +13869,7 @@ static int __devinit tg3_test_dma(struct tg3 *tp) { dma_addr_t buf_dma; u32 *buf, saved_dma_rwctrl; - int ret; + int ret = 0; buf = pci_alloc_consistent(tp->pdev, TEST_BUFFER_SIZE, &buf_dma); if (!buf) { @@ -13427,6 +13882,10 @@ static int __devinit tg3_test_dma(struct tg3 *tp) tp->dma_rwctrl = tg3_calc_dma_bndry(tp, tp->dma_rwctrl); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) + goto out; + if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { /* DMA read watermark not used on PCIE */ tp->dma_rwctrl |= 0x00180000; @@ -13499,7 +13958,6 @@ static int __devinit tg3_test_dma(struct tg3 *tp) tg3_switch_clocks(tp); #endif - ret = 0; if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) goto out; @@ -13618,7 +14076,8 @@ static void __devinit tg3_init_link_config(struct tg3 *tp) static void __devinit tg3_init_bufmgr_config(struct tg3 *tp) { if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717) { + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765) { tp->bufmgr_config.mbuf_read_dma_low_water = DEFAULT_MB_RDMA_LOW_WATER_5705; tp->bufmgr_config.mbuf_mac_rx_low_water = @@ -13678,6 +14137,7 @@ static char * __devinit tg3_phy_string(struct tg3 *tp) case PHY_ID_BCM5756: return "5722/5756"; case PHY_ID_BCM5906: return "5906"; case PHY_ID_BCM5761: return "5761"; + case PHY_ID_BCM5717: return "5717"; case PHY_ID_BCM8002: return "8002/serdes"; case 0: return "serdes"; default: return "unknown"; @@ -13919,51 +14379,6 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, tp->rx_pending = TG3_DEF_RX_RING_PENDING; tp->rx_jumbo_pending = TG3_DEF_RX_JUMBO_RING_PENDING; - intmbx = MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW; - rcvmbx = MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW; - sndmbx = MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW; - for (i = 0; i < TG3_IRQ_MAX_VECS; i++) { - struct tg3_napi *tnapi = &tp->napi[i]; - - tnapi->tp = tp; - tnapi->tx_pending = TG3_DEF_TX_RING_PENDING; - - tnapi->int_mbox = intmbx; - if (i < 4) - intmbx += 0x8; - else - intmbx += 0x4; - - tnapi->consmbox = rcvmbx; - tnapi->prodmbox = sndmbx; - - if (i) - tnapi->coal_now = HOSTCC_MODE_COAL_VEC1_NOW << (i - 1); - else - tnapi->coal_now = HOSTCC_MODE_NOW; - - if (!(tp->tg3_flags & TG3_FLAG_SUPPORT_MSIX)) - break; - - /* - * If we support MSIX, we'll be using RSS. If we're using - * RSS, the first vector only handles link interrupts and the - * remaining vectors handle rx and tx interrupts. Reuse the - * mailbox values for the next iteration. The values we setup - * above are still useful for the single vectored mode. - */ - if (!i) - continue; - - rcvmbx += 0x8; - - if (sndmbx & 0x4) - sndmbx -= 0x4; - else - sndmbx += 0xc; - } - - netif_napi_add(dev, &tp->napi[0].napi, tg3_poll, 64); dev->ethtool_ops = &tg3_ethtool_ops; dev->watchdog_timeo = TG3_TX_TIMEOUT; dev->irq = pdev->irq; @@ -13975,8 +14390,8 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, goto err_out_iounmap; } - if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) + if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) && + tp->pci_chip_rev_id != CHIPREV_ID_5717_A0) dev->netdev_ops = &tg3_netdev_ops; else dev->netdev_ops = &tg3_netdev_ops_dma_bug; @@ -14023,46 +14438,39 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, tg3_init_bufmgr_config(tp); - if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) - tp->fw_needed = FIRMWARE_TG3; - - if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { + /* Selectively allow TSO based on operating conditions */ + if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) || + (tp->fw_needed && !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF))) tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE; + else { + tp->tg3_flags2 &= ~(TG3_FLG2_TSO_CAPABLE | TG3_FLG2_TSO_BUG); + tp->fw_needed = NULL; } - else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 || - tp->pci_chip_rev_id == CHIPREV_ID_5705_A0 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 || - (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0) { - tp->tg3_flags2 &= ~TG3_FLG2_TSO_CAPABLE; - } else { - tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE | TG3_FLG2_TSO_BUG; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) - tp->fw_needed = FIRMWARE_TG3TSO5; - else - tp->fw_needed = FIRMWARE_TG3TSO; - } + + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) + tp->fw_needed = FIRMWARE_TG3; /* TSO is on by default on chips that support hardware TSO. * Firmware TSO on older chips gives lower performance, so it * is off by default, but can be enabled using ethtool. */ - if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { - if (dev->features & NETIF_F_IP_CSUM) - dev->features |= NETIF_F_TSO; - if ((dev->features & NETIF_F_IPV6_CSUM) && - (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2)) + if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) && + (dev->features & NETIF_F_IP_CSUM)) + dev->features |= NETIF_F_TSO; + + if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) || + (tp->tg3_flags2 & TG3_FLG2_HW_TSO_3)) { + if (dev->features & NETIF_F_IPV6_CSUM) dev->features |= NETIF_F_TSO6; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 && GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780) dev->features |= NETIF_F_TSO_ECN; } - if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 && !(tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) && !(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH)) { @@ -14074,7 +14482,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, if (err) { printk(KERN_ERR PFX "Could not obtain valid ethernet address, " "aborting.\n"); - goto err_out_fw; + goto err_out_iounmap; } if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) { @@ -14083,7 +14491,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, printk(KERN_ERR PFX "Cannot map APE registers, " "aborting.\n"); err = -ENOMEM; - goto err_out_fw; + goto err_out_iounmap; } tg3_ape_lock_init(tp); @@ -14113,6 +14521,53 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, tp->tg3_flags |= TG3_FLAG_PAUSE_AUTONEG; tp->link_config.flowctrl = FLOW_CTRL_TX | FLOW_CTRL_RX; + intmbx = MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW; + rcvmbx = MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW; + sndmbx = MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW; + for (i = 0; i < TG3_IRQ_MAX_VECS; i++) { + struct tg3_napi *tnapi = &tp->napi[i]; + + tnapi->tp = tp; + tnapi->tx_pending = TG3_DEF_TX_RING_PENDING; + + tnapi->int_mbox = intmbx; + if (i < 4) + intmbx += 0x8; + else + intmbx += 0x4; + + tnapi->consmbox = rcvmbx; + tnapi->prodmbox = sndmbx; + + if (i) { + tnapi->coal_now = HOSTCC_MODE_COAL_VEC1_NOW << (i - 1); + netif_napi_add(dev, &tnapi->napi, tg3_poll_msix, 64); + } else { + tnapi->coal_now = HOSTCC_MODE_NOW; + netif_napi_add(dev, &tnapi->napi, tg3_poll, 64); + } + + if (!(tp->tg3_flags & TG3_FLAG_SUPPORT_MSIX)) + break; + + /* + * If we support MSIX, we'll be using RSS. If we're using + * RSS, the first vector only handles link interrupts and the + * remaining vectors handle rx and tx interrupts. Reuse the + * mailbox values for the next iteration. The values we setup + * above are still useful for the single vectored mode. + */ + if (!i) + continue; + + rcvmbx += 0x8; + + if (sndmbx & 0x4) + sndmbx -= 0x4; + else + sndmbx += 0xc; + } + tg3_init_coal(tp); pci_set_drvdata(pdev, dev); @@ -14131,13 +14586,14 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, tg3_bus_string(tp, str), dev->dev_addr); - if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) + if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) { + struct phy_device *phydev; + phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; printk(KERN_INFO "%s: attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", - tp->dev->name, - tp->mdio_bus->phy_map[PHY_ADDR]->drv->name, - dev_name(&tp->mdio_bus->phy_map[PHY_ADDR]->dev)); - else + tp->dev->name, phydev->drv->name, + dev_name(&phydev->dev)); + } else printk(KERN_INFO "%s: attached PHY is %s (%s Ethernet) (WireSpeed[%d])\n", tp->dev->name, tg3_phy_string(tp), @@ -14166,10 +14622,6 @@ err_out_apeunmap: tp->aperegs = NULL; } -err_out_fw: - if (tp->fw) - release_firmware(tp->fw); - err_out_iounmap: if (tp->regs) { iounmap(tp->regs); diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index bab7940158e6..cd30889650f8 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -46,10 +46,15 @@ #define TG3PCI_DEVICE_TIGON3_57788 0x1691 #define TG3PCI_DEVICE_TIGON3_5785_G 0x1699 /* GPHY */ #define TG3PCI_DEVICE_TIGON3_5785_F 0x16a0 /* 10/100 only */ -#define TG3PCI_DEVICE_TIGON3_5717C 0x1655 -#define TG3PCI_DEVICE_TIGON3_5717S 0x1656 -#define TG3PCI_DEVICE_TIGON3_5718C 0x1665 -#define TG3PCI_DEVICE_TIGON3_5718S 0x1666 +#define TG3PCI_DEVICE_TIGON3_5717 0x1655 +#define TG3PCI_DEVICE_TIGON3_5718 0x1656 +#define TG3PCI_DEVICE_TIGON3_5724 0x165c +#define TG3PCI_DEVICE_TIGON3_57781 0x16b1 +#define TG3PCI_DEVICE_TIGON3_57785 0x16b5 +#define TG3PCI_DEVICE_TIGON3_57761 0x16b0 +#define TG3PCI_DEVICE_TIGON3_57765 0x16b4 +#define TG3PCI_DEVICE_TIGON3_57791 0x16b2 +#define TG3PCI_DEVICE_TIGON3_57795 0x16b6 /* 0x04 --> 0x64 unused */ #define TG3PCI_MSI_DATA 0x00000064 /* 0x66 --> 0x68 unused */ @@ -103,6 +108,7 @@ #define CHIPREV_ID_5906_A1 0xc001 #define CHIPREV_ID_57780_A0 0x57780000 #define CHIPREV_ID_57780_A1 0x57780001 +#define CHIPREV_ID_5717_A0 0x05717000 #define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12) #define ASIC_REV_5700 0x07 #define ASIC_REV_5701 0x00 @@ -122,6 +128,7 @@ #define ASIC_REV_5785 0x5785 #define ASIC_REV_57780 0x57780 #define ASIC_REV_5717 0x5717 +#define ASIC_REV_57765 0x57785 #define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8) #define CHIPREV_5700_AX 0x70 #define CHIPREV_5700_BX 0x71 @@ -141,8 +148,7 @@ #define METAL_REV_B1 0x01 #define METAL_REV_B2 0x02 #define TG3PCI_DMA_RW_CTRL 0x0000006c -#define DMA_RWCTRL_MIN_DMA 0x000000ff -#define DMA_RWCTRL_MIN_DMA_SHIFT 0 +#define DMA_RWCTRL_DIS_CACHE_ALIGNMENT 0x00000001 #define DMA_RWCTRL_READ_BNDRY_MASK 0x00000700 #define DMA_RWCTRL_READ_BNDRY_DISAB 0x00000000 #define DMA_RWCTRL_READ_BNDRY_16 0x00000100 @@ -221,6 +227,7 @@ /* 0xc0 --> 0xf4 unused */ #define TG3PCI_GEN2_PRODID_ASICREV 0x000000f4 +#define TG3PCI_GEN15_PRODID_ASICREV 0x000000fc /* 0xf8 --> 0x200 unused */ #define TG3_CORR_ERR_STAT 0x00000110 @@ -242,7 +249,11 @@ #define MAILBOX_GENERAL_7 0x00000258 /* 64-bit */ #define MAILBOX_RELOAD_STAT 0x00000260 /* 64-bit */ #define MAILBOX_RCV_STD_PROD_IDX 0x00000268 /* 64-bit */ +#define TG3_RX_STD_PROD_IDX_REG (MAILBOX_RCV_STD_PROD_IDX + \ + TG3_64BIT_REG_LOW) #define MAILBOX_RCV_JUMBO_PROD_IDX 0x00000270 /* 64-bit */ +#define TG3_RX_JMB_PROD_IDX_REG (MAILBOX_RCV_JUMBO_PROD_IDX + \ + TG3_64BIT_REG_LOW) #define MAILBOX_RCV_MINI_PROD_IDX 0x00000278 /* 64-bit */ #define MAILBOX_RCVRET_CON_IDX_0 0x00000280 /* 64-bit */ #define MAILBOX_RCVRET_CON_IDX_1 0x00000288 /* 64-bit */ @@ -1264,8 +1275,9 @@ #define WDMAC_MODE_FIFOURUN_ENAB 0x00000080 #define WDMAC_MODE_FIFOOREAD_ENAB 0x00000100 #define WDMAC_MODE_LNGREAD_ENAB 0x00000200 -#define WDMAC_MODE_RX_ACCEL 0x00000400 +#define WDMAC_MODE_RX_ACCEL 0x00000400 #define WDMAC_MODE_STATUS_TAG_FIX 0x20000000 +#define WDMAC_MODE_BURST_ALL_DATA 0xc0000000 #define WDMAC_STATUS 0x00004c04 #define WDMAC_STATUS_TGTABORT 0x00000004 #define WDMAC_STATUS_MSTABORT 0x00000008 @@ -1809,6 +1821,11 @@ #define TG3_OTP_DEFAULT 0x286c1640 + +/* Hardware Legacy NVRAM layout */ +#define TG3_NVM_VPD_OFF 0x100 +#define TG3_NVM_VPD_LEN 256 + /* Hardware Selfboot NVRAM layout */ #define TG3_NVM_HWSB_CFG1 0x00000004 #define TG3_NVM_HWSB_CFG1_MAJMSK 0xf8000000 @@ -1953,10 +1970,34 @@ #define NIC_SRAM_MBUF_POOL_BASE5705 0x00010000 #define NIC_SRAM_MBUF_POOL_SIZE5705 0x0000e000 + /* Currently this is fixed. */ -#define PHY_ADDR 0x01 +#define TG3_PHY_PCIE_ADDR 0x00 +#define TG3_PHY_MII_ADDR 0x01 + + +/*** Tigon3 specific PHY PCIE registers. ***/ + +#define TG3_PCIEPHY_BLOCK_ADDR 0x1f +#define TG3_PCIEPHY_XGXS_BLK1 0x0801 +#define TG3_PCIEPHY_TXB_BLK 0x0861 +#define TG3_PCIEPHY_BLOCK_SHIFT 4 + +/* TG3_PCIEPHY_TXB_BLK */ +#define TG3_PCIEPHY_TX0CTRL1 0x15 +#define TG3_PCIEPHY_TX0CTRL1_TXOCM 0x0003 +#define TG3_PCIEPHY_TX0CTRL1_RDCTL 0x0008 +#define TG3_PCIEPHY_TX0CTRL1_TXCMV 0x0030 +#define TG3_PCIEPHY_TX0CTRL1_TKSEL 0x0040 +#define TG3_PCIEPHY_TX0CTRL1_NB_EN 0x0400 -/* Tigon3 specific PHY MII registers. */ +/* TG3_PCIEPHY_XGXS_BLK1 */ +#define TG3_PCIEPHY_PWRMGMT4 0x1a +#define TG3_PCIEPHY_PWRMGMT4_L1PLLPD_EN 0x0038 +#define TG3_PCIEPHY_PWRMGMT4_LOWPWR_EN 0x4000 + + +/*** Tigon3 specific PHY MII registers. ***/ #define TG3_BMCR_SPEED1000 0x0040 #define MII_TG3_CTRL 0x09 /* 1000-baseT control register */ @@ -2055,6 +2096,9 @@ #define MII_TG3_FET_SHDW_MISCCTRL 0x10 #define MII_TG3_FET_SHDW_MISCCTRL_MDIX 0x4000 +#define MII_TG3_FET_SHDW_AUXMODE4 0x1a +#define MII_TG3_FET_SHDW_AUXMODE4_SBPD 0x0008 + #define MII_TG3_FET_SHDW_AUXSTAT2 0x1b #define MII_TG3_FET_SHDW_AUXSTAT2_APD 0x0020 @@ -2410,10 +2454,6 @@ struct ring_info { DECLARE_PCI_UNMAP_ADDR(mapping) }; -struct tx_ring_info { - struct sk_buff *skb; -}; - struct tg3_config_info { u32 flags; }; @@ -2542,8 +2582,10 @@ struct tg3_ethtool_stats { }; struct tg3_rx_prodring_set { - u32 rx_std_ptr; - u32 rx_jmb_ptr; + u32 rx_std_prod_idx; + u32 rx_std_cons_idx; + u32 rx_jmb_prod_idx; + u32 rx_jmb_cons_idx; struct tg3_rx_buffer_desc *rx_std; struct tg3_ext_rx_buffer_desc *rx_jmb; struct ring_info *rx_std_buffers; @@ -2571,10 +2613,11 @@ struct tg3_napi { u32 consmbox; u32 rx_rcb_ptr; u16 *rx_rcb_prod_idx; + struct tg3_rx_prodring_set *prodring; struct tg3_rx_buffer_desc *rx_rcb; struct tg3_tx_buffer_desc *tx_ring; - struct tx_ring_info *tx_buffers; + struct ring_info *tx_buffers; dma_addr_t status_mapping; dma_addr_t rx_rcb_mapping; @@ -2654,7 +2697,7 @@ struct tg3 { struct vlan_group *vlgrp; #endif - struct tg3_rx_prodring_set prodring[1]; + struct tg3_rx_prodring_set prodring[TG3_IRQ_MAX_VECS - 1]; /* begin "everything else" cacheline(s) section */ @@ -2725,7 +2768,7 @@ struct tg3 { #define TG3_FLG2_SERDES_PREEMPHASIS 0x00020000 #define TG3_FLG2_5705_PLUS 0x00040000 #define TG3_FLG2_5750_PLUS 0x00080000 -#define TG3_FLG2_PROTECTED_NVRAM 0x00100000 +#define TG3_FLG2_HW_TSO_3 0x00100000 #define TG3_FLG2_USING_MSI 0x00200000 #define TG3_FLG2_USING_MSIX 0x00400000 #define TG3_FLG2_USING_MSI_OR_MSIX (TG3_FLG2_USING_MSI | \ @@ -2737,7 +2780,9 @@ struct tg3 { #define TG3_FLG2_ICH_WORKAROUND 0x02000000 #define TG3_FLG2_5780_CLASS 0x04000000 #define TG3_FLG2_HW_TSO_2 0x08000000 -#define TG3_FLG2_HW_TSO (TG3_FLG2_HW_TSO_1 | TG3_FLG2_HW_TSO_2) +#define TG3_FLG2_HW_TSO (TG3_FLG2_HW_TSO_1 | \ + TG3_FLG2_HW_TSO_2 | \ + TG3_FLG2_HW_TSO_3) #define TG3_FLG2_1SHOT_MSI 0x10000000 #define TG3_FLG2_PHY_JITTER_BUG 0x20000000 #define TG3_FLG2_NO_FWARE_REPORTED 0x40000000 @@ -2745,6 +2790,7 @@ struct tg3 { u32 tg3_flags3; #define TG3_FLG3_NO_NVRAM_ADDR_TRANS 0x00000001 #define TG3_FLG3_ENABLE_APE 0x00000002 +#define TG3_FLG3_PROTECTED_NVRAM 0x00000004 #define TG3_FLG3_5701_DMA_BUG 0x00000008 #define TG3_FLG3_USE_PHYLIB 0x00000010 #define TG3_FLG3_MDIOBUS_INITED 0x00000020 @@ -2756,9 +2802,13 @@ struct tg3 { #define TG3_FLG3_PHY_ENABLE_APD 0x00001000 #define TG3_FLG3_5755_PLUS 0x00002000 #define TG3_FLG3_NO_NVRAM 0x00004000 -#define TG3_FLG3_TOGGLE_10_100_L1PLLPD 0x00008000 #define TG3_FLG3_PHY_IS_FET 0x00010000 #define TG3_FLG3_ENABLE_RSS 0x00020000 +#define TG3_FLG3_ENABLE_TSS 0x00040000 +#define TG3_FLG3_4G_DMA_BNDRY_BUG 0x00080000 +#define TG3_FLG3_40BIT_DMA_LIMIT_BUG 0x00100000 +#define TG3_FLG3_SHORT_DMA_BUG 0x00200000 +#define TG3_FLG3_USE_JUMBO_BDFLAG 0x00400000 struct timer_list timer; u16 timer_counter; @@ -2825,6 +2875,7 @@ struct tg3 { #define PHY_ID_BCM5756 0xbc050ed0 #define PHY_ID_BCM5784 0xbc050fa0 #define PHY_ID_BCM5761 0xbc050fd0 +#define PHY_ID_BCM5717 0x5c0d8a00 #define PHY_ID_BCM5906 0xdc00ac40 #define PHY_ID_BCM8002 0x60010140 #define PHY_ID_INVALID 0xffffffff @@ -2834,6 +2885,7 @@ struct tg3 { #define PHY_REV_BCM5401_C0 0x6 #define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */ #define TG3_PHY_ID_BCM50610 0x143bd60 +#define TG3_PHY_ID_BCM50610M 0x143bd70 #define TG3_PHY_ID_BCMAC131 0x143bc70 #define TG3_PHY_ID_RTL8211C 0x001cc910 #define TG3_PHY_ID_RTL8201E 0x00008200 @@ -2846,8 +2898,9 @@ struct tg3 { u32 led_ctrl; u32 phy_otp; - char board_part_number[24]; -#define TG3_VER_SIZE 32 +#define TG3_BPN_SIZE 24 + char board_part_number[TG3_BPN_SIZE]; +#define TG3_VER_SIZE ETHTOOL_FWVERS_LEN char fw_ver[TG3_VER_SIZE]; u32 nic_sram_data_cfg; u32 pci_clock_ctrl; @@ -2865,7 +2918,7 @@ struct tg3 { (X) == PHY_ID_BCM5780 || (X) == PHY_ID_BCM5787 || \ (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM5756 || \ (X) == PHY_ID_BCM5906 || (X) == PHY_ID_BCM5761 || \ - (X) == PHY_ID_BCM8002) + (X) == PHY_ID_BCM5717 || (X) == PHY_ID_BCM8002) struct tg3_hw_stats *hw_stats; dma_addr_t stats_mapping; diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index 3d31b47332bb..fabaeffb3155 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -1549,7 +1549,8 @@ static u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int ) if (tmpCStat & TLAN_CSTAT_EOC) eoc = 1; - new_skb = netdev_alloc_skb(dev, TLAN_MAX_FRAME_SIZE + 7 ); + new_skb = netdev_alloc_skb_ip_align(dev, + TLAN_MAX_FRAME_SIZE + 5); if ( !new_skb ) goto drop_and_reuse; @@ -1563,7 +1564,6 @@ static u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int ) skb->protocol = eth_type_trans( skb, dev ); netif_rx( skb ); - skb_reserve( new_skb, NET_IP_ALIGN ); head_list->buffer[0].address = pci_map_single(priv->pciDev, new_skb->data, TLAN_MAX_FRAME_SIZE, @@ -1755,8 +1755,8 @@ static u32 TLan_HandleStatusCheck( struct net_device *dev, u16 host_int ) ! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) { tlphy_ctl |= TLAN_TC_SWAPOL; TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl); - } else if ( ( tlphy_sts & TLAN_TS_POLOK ) - && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) { + } else if ( ( tlphy_sts & TLAN_TS_POLOK ) && + ( tlphy_ctl & TLAN_TC_SWAPOL ) ) { tlphy_ctl &= ~TLAN_TC_SWAPOL; TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl); } @@ -1967,13 +1967,12 @@ static void TLan_ResetLists( struct net_device *dev ) list->cStat = TLAN_CSTAT_READY; list->frameSize = TLAN_MAX_FRAME_SIZE; list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; - skb = netdev_alloc_skb(dev, TLAN_MAX_FRAME_SIZE + 7 ); + skb = netdev_alloc_skb_ip_align(dev, TLAN_MAX_FRAME_SIZE + 5); if ( !skb ) { pr_err("TLAN: out of memory for received data.\n" ); break; } - skb_reserve( skb, NET_IP_ALIGN ); list->buffer[0].address = pci_map_single(priv->pciDev, skb->data, TLAN_MAX_FRAME_SIZE, diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c index 724158966ec1..cf552d1d9629 100644 --- a/drivers/net/tokenring/3c359.c +++ b/drivers/net/tokenring/3c359.c @@ -610,9 +610,8 @@ static int xl_open(struct net_device *dev) u16 switchsettings, switchsettings_eeprom ; - if(request_irq(dev->irq, &xl_interrupt, IRQF_SHARED , "3c359", dev)) { + if (request_irq(dev->irq, xl_interrupt, IRQF_SHARED , "3c359", dev)) return -EAGAIN; - } /* * Read the information from the EEPROM that we need. diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 75fa32e34fd0..66272f2a0758 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -96,7 +96,7 @@ * * Change by Mike Sullivan et al.: * + added turbo card support. No need to use lanaid to configure - * the adapter into isa compatiblity mode. + * the adapter into isa compatibility mode. * * Changes by Burt Silverman to allow the computer to behave nicely when * a cable is pulled or not in place, or a PCMCIA card is removed hot. @@ -680,7 +680,7 @@ static int __devinit ibmtr_probe1(struct net_device *dev, int PIOaddr) /* The PCMCIA has already got the interrupt line and the io port, so no chance of anybody else getting it - MLP */ - if (request_irq(dev->irq = irq, &tok_interrupt, 0, "ibmtr", dev) != 0) { + if (request_irq(dev->irq = irq, tok_interrupt, 0, "ibmtr", dev) != 0) { DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n", irq); iounmap(t_mmio); diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c index 26dca2b2bdbd..d6ccd59c7d07 100644 --- a/drivers/net/tokenring/lanstreamer.c +++ b/drivers/net/tokenring/lanstreamer.c @@ -596,7 +596,7 @@ static int streamer_open(struct net_device *dev) rc=streamer_reset(dev); } - if (request_irq(dev->irq, &streamer_interrupt, IRQF_SHARED, "lanstreamer", dev)) { + if (request_irq(dev->irq, streamer_interrupt, IRQF_SHARED, "lanstreamer", dev)) { return -EAGAIN; } #if STREAMER_DEBUG @@ -712,8 +712,8 @@ static int streamer_open(struct net_device *dev) strcat(open_error, " - "); strcat(open_error, open_min_error[(error_code & 0x0f)]); - if (!streamer_priv->streamer_ring_speed - && ((error_code & 0x0f) == 0x0d)) + if (!streamer_priv->streamer_ring_speed && + ((error_code & 0x0f) == 0x0d)) { printk(KERN_WARNING "%s: Tried to autosense ring speed with no monitors present\n", dev->name); printk(KERN_WARNING "%s: Please try again with a specified ring speed \n", dev->name); @@ -1032,8 +1032,8 @@ static irqreturn_t streamer_interrupt(int irq, void *dev_id) sisr = readw(streamer_mmio + SISR); while((sisr & (SISR_MI | SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | - SISR_ARB_CMD | SISR_TRB_REPLY | SISR_PAR_ERR | SISR_SERR_ERR)) - && (max_intr > 0)) { + SISR_ARB_CMD | SISR_TRB_REPLY | SISR_PAR_ERR | SISR_SERR_ERR)) && + (max_intr > 0)) { if(sisr & SISR_PAR_ERR) { writew(~SISR_PAR_ERR, streamer_mmio + SISR_RUM); diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c index d9ec7f0bbd0a..df32025c5132 100644 --- a/drivers/net/tokenring/olympic.c +++ b/drivers/net/tokenring/olympic.c @@ -445,9 +445,9 @@ static int olympic_open(struct net_device *dev) olympic_init(dev); - if(request_irq(dev->irq, &olympic_interrupt, IRQF_SHARED , "olympic", dev)) { + if (request_irq(dev->irq, olympic_interrupt, IRQF_SHARED , "olympic", + dev)) return -EAGAIN; - } #if OLYMPIC_DEBUG printk("BMCTL: %x\n",readl(olympic_mmio+BMCTL_SUM)); diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c index ebda61bc4c2f..5401d86a7be4 100644 --- a/drivers/net/tokenring/smctr.c +++ b/drivers/net/tokenring/smctr.c @@ -426,7 +426,7 @@ static int smctr_alloc_shared_memory(struct net_device *dev) smctr_malloc(dev, 1L); /* Allocate Non-MAC receive data buffers. - * To guarantee a minimum of 256 contigous memory to + * To guarantee a minimum of 256 contiguous memory to * UM_Receive_Packet's lookahead pointer, before a page * change or ring end is encountered, place each rx buffer on * a 256 byte boundary. @@ -2309,9 +2309,9 @@ static irqreturn_t smctr_interrupt(int irq, void *dev_id) else { if((tp->acb_head->cmd - == ACB_CMD_READ_TRC_STATUS) - && (tp->acb_head->subcmd - == RW_TRC_STATUS_BLOCK)) + == ACB_CMD_READ_TRC_STATUS) && + (tp->acb_head->subcmd + == RW_TRC_STATUS_BLOCK)) { if(tp->ptr_bcn_type) { @@ -2331,8 +2331,8 @@ static irqreturn_t smctr_interrupt(int irq, void *dev_id) smctr_disable_16bit(dev); err = smctr_ring_status_chg(dev); smctr_enable_16bit(dev); - if((tp->ring_status & REMOVE_RECEIVED) - && (tp->config_word0 & NO_AUTOREMOVE)) + if((tp->ring_status & REMOVE_RECEIVED) && + (tp->config_word0 & NO_AUTOREMOVE)) { smctr_issue_remove_cmd(dev); } @@ -2511,9 +2511,9 @@ static int smctr_issue_init_timers_cmd(struct net_device *dev) tp->config_word0 = THDREN | DMA_TRIGGER | USETPT | NO_AUTOREMOVE; tp->config_word1 = 0; - if((tp->media_type == MEDIA_STP_16) - || (tp->media_type == MEDIA_UTP_16) - || (tp->media_type == MEDIA_STP_16_UTP_16)) + if((tp->media_type == MEDIA_STP_16) || + (tp->media_type == MEDIA_UTP_16) || + (tp->media_type == MEDIA_STP_16_UTP_16)) { tp->config_word0 |= FREQ_16MB_BIT; } @@ -2556,9 +2556,9 @@ static int smctr_issue_init_timers_cmd(struct net_device *dev) tp->config_word1 &= ~SOURCE_ROUTING_SPANNING_BITS; } - if((tp->media_type == MEDIA_STP_16) - || (tp->media_type == MEDIA_UTP_16) - || (tp->media_type == MEDIA_STP_16_UTP_16)) + if((tp->media_type == MEDIA_STP_16) || + (tp->media_type == MEDIA_UTP_16) || + (tp->media_type == MEDIA_STP_16_UTP_16)) { tp->config_word1 |= INTERFRAME_SPACING_16; } @@ -2568,9 +2568,9 @@ static int smctr_issue_init_timers_cmd(struct net_device *dev) *pTimer_Struc++ = tp->config_word0; *pTimer_Struc++ = tp->config_word1; - if((tp->media_type == MEDIA_STP_4) - || (tp->media_type == MEDIA_UTP_4) - || (tp->media_type == MEDIA_STP_4_UTP_4)) + if((tp->media_type == MEDIA_STP_4) || + (tp->media_type == MEDIA_UTP_4) || + (tp->media_type == MEDIA_STP_4_UTP_4)) { *pTimer_Struc++ = 0x00FA; /* prescale */ *pTimer_Struc++ = 0x2710; /* TPT_limit */ @@ -2990,8 +2990,8 @@ static int smctr_load_firmware(struct net_device *dev) } /* Verify the firmware exists and is there in the right amount. */ - if (!fw->data - || (*(fw->data + UCODE_VERSION_OFFSET) < UCODE_VERSION)) + if (!fw->data || + (*(fw->data + UCODE_VERSION_OFFSET) < UCODE_VERSION)) { err = (UCODE_NOT_PRESENT); goto out; @@ -3010,9 +3010,8 @@ static int smctr_load_firmware(struct net_device *dev) smctr_enable_16bit(dev); smctr_set_page(dev, (__u8 *)tp->ram_access); - if((smctr_checksum_firmware(dev)) - || (*(fw->data + UCODE_VERSION_OFFSET) - > tp->microcode_version)) + if((smctr_checksum_firmware(dev)) || + (*(fw->data + UCODE_VERSION_OFFSET) > tp->microcode_version)) { smctr_enable_adapter_ctrl_store(dev); @@ -3117,9 +3116,9 @@ static int smctr_lobe_media_test(struct net_device *dev) } /* Check if any frames received during test. */ - if((tp->rx_fcb_curr[MAC_QUEUE]->frame_status) - || (tp->rx_fcb_curr[NON_MAC_QUEUE]->frame_status)) - goto err; + if((tp->rx_fcb_curr[MAC_QUEUE]->frame_status) || + (tp->rx_fcb_curr[NON_MAC_QUEUE]->frame_status)) + goto err; /* Set receive mask to "Promisc" mode. */ tp->receive_mask = saved_rcv_mask; @@ -3303,8 +3302,8 @@ static int smctr_make_group_addr(struct net_device *dev, MAC_SUB_VECTOR *tsv) /* Set Group Address Sub-vector to all zeros if only the * Group Address/Functional Address Indicator is set. */ - if(tsv->svv[0] == 0x80 && tsv->svv[1] == 0x00 - && tsv->svv[2] == 0x00 && tsv->svv[3] == 0x00) + if(tsv->svv[0] == 0x80 && tsv->svv[1] == 0x00 && + tsv->svv[2] == 0x00 && tsv->svv[3] == 0x00) tsv->svv[0] = 0x00; return (0); @@ -3876,10 +3875,10 @@ static int smctr_process_rx_packet(MAC_HEADER *rmf, __u16 size, /* NOTE: UNKNOWN MAC frames will NOT be passed up unless * ACCEPT_ATT_MAC_FRAMES is set. */ - if(((tp->receive_mask & ACCEPT_ATT_MAC_FRAMES) - && (xframe == (__u8)0)) - || ((tp->receive_mask & ACCEPT_EXT_MAC_FRAMES) - && (xframe == (__u8)1))) + if(((tp->receive_mask & ACCEPT_ATT_MAC_FRAMES) && + (xframe == (__u8)0)) || + ((tp->receive_mask & ACCEPT_EXT_MAC_FRAMES) && + (xframe == (__u8)1))) { rmf->vl = SWAP_BYTES(rmf->vl); @@ -3934,8 +3933,8 @@ static int smctr_ram_memory_test(struct net_device *dev) word_pattern = start_pattern; - for(j = 1; j < (__u32)(tp->ram_usable * 1024) - 1 - && (~err); j += 2, word_pattern++) + for(j = 1; j < (__u32)(tp->ram_usable * 1024) - 1 && (~err); + j += 2, word_pattern++) { word_read = *(__u16 *)(pword + j); if(word_read != word_pattern) @@ -3959,8 +3958,7 @@ static int smctr_ram_memory_test(struct net_device *dev) for(j = 0; j < (__u32)tp->ram_usable * 1024; j +=2) *(__u16 *)(pword + j) = word_pattern; - for(j =0; j < (__u32)tp->ram_usable * 1024 - && (~err); j += 2) + for(j =0; j < (__u32)tp->ram_usable * 1024 && (~err); j += 2) { word_read = *(__u16 *)(pword + j); if(word_read != word_pattern) @@ -4325,8 +4323,8 @@ static int smctr_restart_tx_chain(struct net_device *dev, short queue) if(smctr_debug > 10) printk(KERN_DEBUG "%s: smctr_restart_tx_chain\n", dev->name); - if(tp->num_tx_fcbs_used[queue] != 0 - && tp->tx_queue_status[queue] == NOT_TRANSMITING) + if(tp->num_tx_fcbs_used[queue] != 0 && + tp->tx_queue_status[queue] == NOT_TRANSMITING) { tp->tx_queue_status[queue] = TRANSMITING; err = smctr_issue_resume_tx_fcb_cmd(dev, queue); @@ -4349,8 +4347,8 @@ static int smctr_ring_status_chg(struct net_device *dev) */ if(tp->ring_status_flags == MONITOR_STATE_CHANGED) { - if((tp->monitor_state == MS_ACTIVE_MONITOR_STATE) - || (tp->monitor_state == MS_STANDBY_MONITOR_STATE)) + if((tp->monitor_state == MS_ACTIVE_MONITOR_STATE) || + (tp->monitor_state == MS_STANDBY_MONITOR_STATE)) { tp->monitor_state_ready = 1; } @@ -4363,8 +4361,8 @@ static int smctr_ring_status_chg(struct net_device *dev) tp->monitor_state_ready = 0; /* Ring speed problem, switching to auto mode. */ - if(tp->monitor_state == MS_MONITOR_FSM_INACTIVE - && !tp->cleanup) + if(tp->monitor_state == MS_MONITOR_FSM_INACTIVE && + !tp->cleanup) { printk(KERN_INFO "%s: Incorrect ring speed switching.\n", dev->name); @@ -4442,8 +4440,8 @@ static int smctr_rx_frame(struct net_device *dev) { err = HARDWARE_FAILED; - if(((status & 0x007f) == 0) - || ((tp->receive_mask & ACCEPT_ERR_PACKETS) != 0)) + if(((status & 0x007f) == 0) || + ((tp->receive_mask & ACCEPT_ERR_PACKETS) != 0)) { /* frame length less the CRC (4 bytes) + FS (1 byte) */ rx_size = tp->rx_fcb_curr[queue]->frame_length - 5; @@ -4538,8 +4536,8 @@ static int smctr_send_dat(struct net_device *dev) } /* Check if GOOD frame Tx'ed. */ - if(!(fcb->frame_status & FCB_COMMAND_DONE) - || fcb->frame_status & (FCB_TX_STATUS_E | FCB_TX_AC_BITS)) + if(!(fcb->frame_status & FCB_COMMAND_DONE) || + fcb->frame_status & (FCB_TX_STATUS_E | FCB_TX_AC_BITS)) { return (INITIALIZE_FAILED); } @@ -4653,8 +4651,8 @@ static int smctr_send_lobe_media_test(struct net_device *dev) } /* Check if GOOD frame Tx'ed */ - if(!(fcb->frame_status & FCB_COMMAND_DONE) - || fcb->frame_status & (FCB_TX_STATUS_E | FCB_TX_AC_BITS)) + if(!(fcb->frame_status & FCB_COMMAND_DONE) || + fcb->frame_status & (FCB_TX_STATUS_E | FCB_TX_AC_BITS)) { return (LOBE_MEDIA_TEST_FAILED); } diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c index a7b6888829b5..e3c42f5ac4a9 100644 --- a/drivers/net/tokenring/tms380tr.c +++ b/drivers/net/tokenring/tms380tr.c @@ -729,8 +729,8 @@ static void tms380tr_timer_chk(unsigned long data) return; tms380tr_chk_outstanding_cmds(dev); - if(time_before(tp->LastSendTime + SEND_TIMEOUT, jiffies) - && (tp->TplFree != tp->TplBusy)) + if(time_before(tp->LastSendTime + SEND_TIMEOUT, jiffies) && + (tp->TplFree != tp->TplBusy)) { /* Anything to send, but stalled too long */ tp->LastSendTime = jiffies; @@ -830,8 +830,8 @@ irqreturn_t tms380tr_interrupt(int irq, void *dev_id) } /* Reset system interrupt if not already done. */ - if(irq_type != STS_IRQ_TRANSMIT_STATUS - && irq_type != STS_IRQ_RECEIVE_STATUS) { + if(irq_type != STS_IRQ_TRANSMIT_STATUS && + irq_type != STS_IRQ_RECEIVE_STATUS) { tms380tr_reset_interrupt(dev); } @@ -895,10 +895,10 @@ static unsigned char tms380tr_chk_ssb(struct net_local *tp, unsigned short IrqTy /* Check if this interrupt does use the SSB. */ - if(IrqType != STS_IRQ_TRANSMIT_STATUS - && IrqType != STS_IRQ_RECEIVE_STATUS - && IrqType != STS_IRQ_COMMAND_STATUS - && IrqType != STS_IRQ_RING_STATUS) + if(IrqType != STS_IRQ_TRANSMIT_STATUS && + IrqType != STS_IRQ_RECEIVE_STATUS && + IrqType != STS_IRQ_COMMAND_STATUS && + IrqType != STS_IRQ_RING_STATUS) { return (1); /* SSB not involved. */ } @@ -1364,6 +1364,8 @@ static int tms380tr_reset_adapter(struct net_device *dev) return (-1); } +MODULE_FIRMWARE("tms380tr.bin"); + /* * Starts bring up diagnostics of token ring adapter and evaluates * diagnostic results. @@ -1483,8 +1485,8 @@ static int tms380tr_init_adapter(struct net_device *dev) /* Mask interesting status bits */ Status = SIFREADW(SIFSTS); Status &= STS_MASK; - } while(((Status &(STS_INITIALIZE | STS_ERROR | STS_TEST)) != 0) - && ((Status & STS_ERROR) == 0) && (loop_cnt != 0)); + } while(((Status &(STS_INITIALIZE | STS_ERROR | STS_TEST)) != 0) && + ((Status & STS_ERROR) == 0) && (loop_cnt != 0)); if((Status & (STS_INITIALIZE | STS_ERROR | STS_TEST)) == 0) { @@ -2181,8 +2183,8 @@ static void tms380tr_rcv_status_irq(struct net_device *dev) } } - if(skb && (rpl->SkbStat == SKB_DATA_COPY - || rpl->SkbStat == SKB_DMA_DIRECT)) + if(skb && (rpl->SkbStat == SKB_DATA_COPY || + rpl->SkbStat == SKB_DMA_DIRECT)) { if(rpl->SkbStat == SKB_DATA_COPY) skb_copy_to_linear_data(skb, ReceiveDataPtr, diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index 7030bd5e9848..a69c4a48bab9 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -802,13 +802,11 @@ static int tsi108_refill_rx(struct net_device *dev, int budget) int rx = data->rxhead; struct sk_buff *skb; - data->rxskbs[rx] = skb = netdev_alloc_skb(dev, - TSI108_RXBUF_SIZE + 2); + skb = netdev_alloc_skb_ip_align(dev, TSI108_RXBUF_SIZE); + data->rxskbs[rx] = skb; if (!skb) break; - skb_reserve(skb, 2); /* Align the data on a 4-byte boundary. */ - data->rxring[rx].buf0 = dma_map_single(NULL, skb->data, TSI108_RX_SKB_SIZE, DMA_FROM_DEVICE); @@ -1356,7 +1354,7 @@ static int tsi108_open(struct net_device *dev) for (i = 0; i < TSI108_RXRING_LEN; i++) { struct sk_buff *skb; - skb = netdev_alloc_skb(dev, TSI108_RXBUF_SIZE + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(dev, TSI108_RXBUF_SIZE); if (!skb) { /* Bah. No memory for now, but maybe we'll get * some more later. @@ -1370,8 +1368,6 @@ static int tsi108_open(struct net_device *dev) } data->rxskbs[i] = skb; - /* Align the payload on a 4-byte boundary */ - skb_reserve(skb, 2); data->rxskbs[i] = skb; data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data); data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT; diff --git a/drivers/net/tulip/21142.c b/drivers/net/tulip/21142.c index db7d5e11855d..9f6742fad6ca 100644 --- a/drivers/net/tulip/21142.c +++ b/drivers/net/tulip/21142.c @@ -209,10 +209,10 @@ void t21142_lnk_change(struct net_device *dev, int csr5) printk(KERN_DEBUG "%s: Setting CSR6 %8.8x/%x CSR12 %8.8x.\n", dev->name, tp->csr6, ioread32(ioaddr + CSR6), ioread32(ioaddr + CSR12)); - } else if ((tp->nwayset && (csr5 & 0x08000000) - && (dev->if_port == 3 || dev->if_port == 5) - && (csr12 & 2) == 2) || - (tp->nway && (csr5 & (TPLnkFail)))) { + } else if ((tp->nwayset && (csr5 & 0x08000000) && + (dev->if_port == 3 || dev->if_port == 5) && + (csr12 & 2) == 2) || + (tp->nway && (csr5 & (TPLnkFail)))) { /* Link blew? Maybe restart NWay. */ del_timer_sync(&tp->timer); t21142_start_nway(dev); diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index 74e5ba42d38d..d4255d44cb75 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -62,9 +62,9 @@ module_param (debug, int, 0); MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number"); /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(CONFIG_SPARC) || defined(__ia64__) \ - || defined(__sh__) || defined(__mips__) +#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \ + defined(CONFIG_SPARC) || defined(__ia64__) || \ + defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else static int rx_copybreak = 100; diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c index a45ded0538b8..ad63621913c3 100644 --- a/drivers/net/tulip/dmfe.c +++ b/drivers/net/tulip/dmfe.c @@ -543,7 +543,7 @@ static int dmfe_open(struct DEVICE *dev) DMFE_DBUG(0, "dmfe_open", 0); - ret = request_irq(dev->irq, &dmfe_interrupt, + ret = request_irq(dev->irq, dmfe_interrupt, IRQF_SHARED, dev->name, dev); if (ret) return ret; diff --git a/drivers/net/tulip/eeprom.c b/drivers/net/tulip/eeprom.c index 391acd32a6a5..889f57aae89b 100644 --- a/drivers/net/tulip/eeprom.c +++ b/drivers/net/tulip/eeprom.c @@ -174,10 +174,10 @@ void __devinit tulip_parse_eeprom(struct net_device *dev) } /* Do a fix-up based on the vendor half of the station address prefix. */ for (i = 0; eeprom_fixups[i].name; i++) { - if (dev->dev_addr[0] == eeprom_fixups[i].addr0 - && dev->dev_addr[1] == eeprom_fixups[i].addr1 - && dev->dev_addr[2] == eeprom_fixups[i].addr2) { - if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) + if (dev->dev_addr[0] == eeprom_fixups[i].addr0 && + dev->dev_addr[1] == eeprom_fixups[i].addr1 && + dev->dev_addr[2] == eeprom_fixups[i].addr2) { + if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) i++; /* An Accton EN1207, not an outlaw Maxtech. */ memcpy(ee_data + 26, eeprom_fixups[i].newtable, sizeof(eeprom_fixups[i].newtable)); diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index c8d220cf2cce..2e8e8ee893c7 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -170,8 +170,8 @@ int tulip_poll(struct napi_struct *napi, int budget) RxDescCollisionSeen | RxDescRunt | RxDescDescErr | - RxWholePkt)) != RxWholePkt - || pkt_len > 1518) { + RxWholePkt)) != RxWholePkt || + pkt_len > 1518) { if ((status & (RxLengthOver2047 | RxWholePkt)) != RxWholePkt) { /* Ingore earlier buffers. */ @@ -201,8 +201,8 @@ int tulip_poll(struct napi_struct *napi, int budget) /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < tulip_rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + if (pkt_len < tulip_rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(tp->pdev, tp->rx_buffers[entry].mapping, @@ -395,8 +395,8 @@ static int tulip_rx(struct net_device *dev) RxDescCollisionSeen | RxDescRunt | RxDescDescErr | - RxWholePkt)) != RxWholePkt - || pkt_len > 1518) { + RxWholePkt)) != RxWholePkt || + pkt_len > 1518) { if ((status & (RxLengthOver2047 | RxWholePkt)) != RxWholePkt) { /* Ingore earlier buffers. */ @@ -425,8 +425,8 @@ static int tulip_rx(struct net_device *dev) /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < tulip_rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + if (pkt_len < tulip_rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(tp->pdev, tp->rx_buffers[entry].mapping, diff --git a/drivers/net/tulip/media.c b/drivers/net/tulip/media.c index daddfa51853e..d8fda83705bf 100644 --- a/drivers/net/tulip/media.c +++ b/drivers/net/tulip/media.c @@ -468,8 +468,8 @@ void __devinit tulip_find_mii (struct net_device *dev, int board_idx) int phy = phyn & 0x1f; int mii_status = tulip_mdio_read (dev, phy, MII_BMSR); if ((mii_status & 0x8301) == 0x8001 || - ((mii_status & BMSR_100BASE4) == 0 - && (mii_status & 0x7800) != 0)) { + ((mii_status & BMSR_100BASE4) == 0 && + (mii_status & 0x7800) != 0)) { /* preserve Becker logic, gain indentation level */ } else { continue; diff --git a/drivers/net/tulip/pnic2.c b/drivers/net/tulip/pnic2.c index f49579128fb5..d8418694bf46 100644 --- a/drivers/net/tulip/pnic2.c +++ b/drivers/net/tulip/pnic2.c @@ -316,9 +316,9 @@ void pnic2_lnk_change(struct net_device *dev, int csr5) } } - if ((tp->nwayset && (csr5 & 0x08000000) - && (dev->if_port == 3 || dev->if_port == 5) - && (csr12 & 2) == 2) || (tp->nway && (csr5 & (TPLnkFail)))) { + if ((tp->nwayset && (csr5 & 0x08000000) && + (dev->if_port == 3 || dev->if_port == 5) && + (csr12 & 2) == 2) || (tp->nway && (csr5 & (TPLnkFail)))) { /* Link blew? Maybe restart NWay. */ diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 6b2330e4206e..0fa3140d65bf 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -64,9 +64,9 @@ const char * const medianame[32] = { }; /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(CONFIG_SPARC) || defined(__ia64__) \ - || defined(__sh__) || defined(__mips__) +#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \ + defined(CONFIG_SPARC) || defined(__ia64__) || \ + defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else static int rx_copybreak = 100; @@ -449,8 +449,8 @@ media_picked: iowrite32(0x0201B078, ioaddr + 0xB8); next_tick = 1*HZ; } - } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) - && ! tp->medialock) { + } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) && + ! tp->medialock) { dev->if_port = 0; tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0); iowrite32(0x0f370000 | ioread16(ioaddr + 0x80), ioaddr + 0x80); @@ -506,7 +506,7 @@ tulip_open(struct net_device *dev) tulip_init_ring (dev); - retval = request_irq(dev->irq, &tulip_interrupt, IRQF_SHARED, dev->name, dev); + retval = request_irq(dev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev); if (retval) goto free_ring; @@ -535,9 +535,9 @@ static void tulip_tx_timeout(struct net_device *dev) if (tulip_debug > 1) printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", dev->name); - } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 - || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881 - || tp->chip_id == DM910X) { + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 || + tp->chip_id == MX98713 || tp->chip_id == COMPEX9881 || + tp->chip_id == DM910X) { printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", dev->name, ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR12), @@ -1538,8 +1538,10 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, } } /* Lite-On boards have the address byte-swapped. */ - if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0 || dev->dev_addr[0] == 0x02) - && dev->dev_addr[1] == 0x00) + if ((dev->dev_addr[0] == 0xA0 || + dev->dev_addr[0] == 0xC0 || + dev->dev_addr[0] == 0x02) && + dev->dev_addr[1] == 0x00) for (i = 0; i < 6; i+=2) { char tmp = dev->dev_addr[i]; dev->dev_addr[i] = dev->dev_addr[i+1]; @@ -1782,7 +1784,7 @@ static int tulip_resume(struct pci_dev *pdev) return retval; } - if ((retval = request_irq(dev->irq, &tulip_interrupt, IRQF_SHARED, dev->name, dev))) { + if ((retval = request_irq(dev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev))) { printk (KERN_ERR "tulip: request_irq failed in resume\n"); return retval; } diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c index c457a0ca55ad..fa019cabc355 100644 --- a/drivers/net/tulip/uli526x.c +++ b/drivers/net/tulip/uli526x.c @@ -461,7 +461,7 @@ static int uli526x_open(struct net_device *dev) /* Initialize ULI526X board */ uli526x_init(dev); - ret = request_irq(dev->irq, &uli526x_interrupt, IRQF_SHARED, dev->name, dev); + ret = request_irq(dev->irq, uli526x_interrupt, IRQF_SHARED, dev->name, dev); if (ret) return ret; diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c index b38d3b7f6e35..869a7a0005f9 100644 --- a/drivers/net/tulip/winbond-840.c +++ b/drivers/net/tulip/winbond-840.c @@ -639,7 +639,7 @@ static int netdev_open(struct net_device *dev) iowrite32(0x00000001, ioaddr + PCIBusCfg); /* Reset */ netif_device_detach(dev); - i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev); + i = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev); if (i) goto out_err; @@ -1230,8 +1230,8 @@ static int netdev_rx(struct net_device *dev) #endif /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + if (pkt_len < rx_copybreak && + (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev,np->rx_addr[entry], np->rx_skbuff[entry]->len, @@ -1357,8 +1357,8 @@ static u32 __set_rx_mode(struct net_device *dev) memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = RxAcceptBroadcast | AcceptMulticast | RxAcceptAllPhys | AcceptMyPhys; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys; diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c index 0f2ca5980c3c..9924c4c7e2d6 100644 --- a/drivers/net/tulip/xircom_cb.c +++ b/drivers/net/tulip/xircom_cb.c @@ -458,7 +458,7 @@ static int xircom_open(struct net_device *dev) int retval; enter("xircom_open"); printk(KERN_INFO "xircom cardbus adaptor found, registering as %s, using irq %i \n",dev->name,dev->irq); - retval = request_irq(dev->irq, &xircom_interrupt, IRQF_SHARED, dev->name, dev); + retval = request_irq(dev->irq, xircom_interrupt, IRQF_SHARED, dev->name, dev); if (retval) { leave("xircom_open - No IRQ"); return retval; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 4fdfa2ae5418..01e99f22210e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -44,7 +44,6 @@ #include <linux/kernel.h> #include <linux/major.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/poll.h> #include <linux/fcntl.h> #include <linux/init.h> @@ -54,6 +53,7 @@ #include <linux/miscdevice.h> #include <linux/ethtool.h> #include <linux/rtnetlink.h> +#include <linux/compat.h> #include <linux/if.h> #include <linux/if_arp.h> #include <linux/if_ether.h> @@ -1110,8 +1110,8 @@ static int set_offload(struct net_device *dev, unsigned long arg) return 0; } -static long tun_chr_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static long __tun_chr_ioctl(struct file *file, unsigned int cmd, + unsigned long arg, int ifreq_len) { struct tun_file *tfile = file->private_data; struct tun_struct *tun; @@ -1121,7 +1121,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd, int ret; if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) - if (copy_from_user(&ifr, argp, sizeof ifr)) + if (copy_from_user(&ifr, argp, ifreq_len)) return -EFAULT; if (cmd == TUNGETFEATURES) { @@ -1144,7 +1144,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd, if (ret) goto unlock; - if (copy_to_user(argp, &ifr, sizeof(ifr))) + if (copy_to_user(argp, &ifr, ifreq_len)) ret = -EFAULT; goto unlock; } @@ -1162,7 +1162,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd, if (ret) break; - if (copy_to_user(argp, &ifr, sizeof(ifr))) + if (copy_to_user(argp, &ifr, ifreq_len)) ret = -EFAULT; break; @@ -1236,7 +1236,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd, /* Get hw addres */ memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN); ifr.ifr_hwaddr.sa_family = tun->dev->type; - if (copy_to_user(argp, &ifr, sizeof ifr)) + if (copy_to_user(argp, &ifr, ifreq_len)) ret = -EFAULT; break; @@ -1275,6 +1275,41 @@ unlock: return ret; } +static long tun_chr_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return __tun_chr_ioctl(file, cmd, arg, sizeof (struct ifreq)); +} + +#ifdef CONFIG_COMPAT +static long tun_chr_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case TUNSETIFF: + case TUNGETIFF: + case TUNSETTXFILTER: + case TUNGETSNDBUF: + case TUNSETSNDBUF: + case SIOCGIFHWADDR: + case SIOCSIFHWADDR: + arg = (unsigned long)compat_ptr(arg); + break; + default: + arg = (compat_ulong_t)arg; + break; + } + + /* + * compat_ifreq is shorter than ifreq, so we must not access beyond + * the end of that structure. All fields that are used in this + * driver are compatible though, we don't need to convert the + * contents. + */ + return __tun_chr_ioctl(file, cmd, arg, sizeof(struct compat_ifreq)); +} +#endif /* CONFIG_COMPAT */ + static int tun_chr_fasync(int fd, struct file *file, int on) { struct tun_struct *tun = tun_get(file); @@ -1285,7 +1320,6 @@ static int tun_chr_fasync(int fd, struct file *file, int on) DBG(KERN_INFO "%s: tun_chr_fasync %d\n", tun->dev->name, on); - lock_kernel(); if ((ret = fasync_helper(fd, file, on, &tun->fasync)) < 0) goto out; @@ -1298,7 +1332,6 @@ static int tun_chr_fasync(int fd, struct file *file, int on) tun->flags &= ~TUN_FASYNC; ret = 0; out: - unlock_kernel(); tun_put(tun); return ret; } @@ -1306,7 +1339,7 @@ out: static int tun_chr_open(struct inode *inode, struct file * file) { struct tun_file *tfile; - cycle_kernel_lock(); + DBG1(KERN_INFO "tunX: tun_chr_open\n"); tfile = kmalloc(sizeof(*tfile), GFP_KERNEL); @@ -1359,7 +1392,10 @@ static const struct file_operations tun_fops = { .write = do_sync_write, .aio_write = tun_chr_aio_write, .poll = tun_chr_poll, - .unlocked_ioctl = tun_chr_ioctl, + .unlocked_ioctl = tun_chr_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tun_chr_compat_ioctl, +#endif .open = tun_chr_open, .release = tun_chr_close, .fasync = tun_chr_fasync diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 5921f5bdd764..39f1fc650be6 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -1769,8 +1769,8 @@ typhoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile __le32 * read csum_bits = rx->rxStatus & (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_UDP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD); if(csum_bits == - (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD) - || csum_bits == + (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD) || + csum_bits == (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_UDP_CHK_GOOD)) { new_skb->ip_summed = CHECKSUM_UNNECESSARY; } else @@ -2151,7 +2151,7 @@ typhoon_open(struct net_device *dev) goto out_sleep; } - err = request_irq(dev->irq, &typhoon_interrupt, IRQF_SHARED, + err = request_irq(dev->irq, typhoon_interrupt, IRQF_SHARED, dev->name, dev); if(err < 0) goto out_sleep; diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 4469f2451a6f..afaf088b72ea 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -1306,8 +1306,8 @@ static int init_max_rx_buff_len(u16 max_rx_buf_len, u16 __iomem *mrblr_register) { /* max_rx_buf_len value must be a multiple of 128 */ - if ((max_rx_buf_len == 0) - || (max_rx_buf_len % UCC_GETH_MRBLR_ALIGNMENT)) + if ((max_rx_buf_len == 0) || + (max_rx_buf_len % UCC_GETH_MRBLR_ALIGNMENT)) return -EINVAL; out_be16(mrblr_register, max_rx_buf_len); @@ -2159,8 +2159,8 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth) } if ((ug_info->numStationAddresses != - UCC_GETH_NUM_OF_STATION_ADDRESSES_1) - && ug_info->rxExtendedFiltering) { + UCC_GETH_NUM_OF_STATION_ADDRESSES_1) && + ug_info->rxExtendedFiltering) { if (netif_msg_probe(ugeth)) ugeth_err("%s: Number of station addresses greater than 1 " "not allowed in extended parsing mode.", @@ -2284,9 +2284,9 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) UCC_GETH_NUM_OF_STATION_ADDRESSES_1); ugeth->rx_extended_features = ugeth->rx_non_dynamic_extended_features || - (ug_info->vlanOperationTagged != UCC_GETH_VLAN_OPERATION_TAGGED_NOP) - || (ug_info->vlanOperationNonTagged != - UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP); + (ug_info->vlanOperationTagged != UCC_GETH_VLAN_OPERATION_TAGGED_NOP) || + (ug_info->vlanOperationNonTagged != + UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP); init_default_reg_vals(&uf_regs->upsmr, &ug_regs->maccfg1, &ug_regs->maccfg2); @@ -2987,11 +2987,11 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->p_init_enet_param_shadow->rgftgfrxglobal |= ugeth->rx_glbl_pram_offset | ug_info->riscRx; if ((ug_info->largestexternallookupkeysize != - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE) - && (ug_info->largestexternallookupkeysize != - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES) - && (ug_info->largestexternallookupkeysize != - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES)) { + QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE) && + (ug_info->largestexternallookupkeysize != + QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES) && + (ug_info->largestexternallookupkeysize != + QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES)) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Invalid largest External Lookup Key Size.", __func__); @@ -3798,7 +3798,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma prop = of_get_property(np, "tx-clock", NULL); if (!prop) { printk(KERN_ERR - "ucc_geth: mising tx-clock-name property\n"); + "ucc_geth: missing tx-clock-name property\n"); return -EINVAL; } if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) { diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h index 03a6ca016d5a..a007e2acf651 100644 --- a/drivers/net/ucc_geth.h +++ b/drivers/net/ucc_geth.h @@ -80,16 +80,16 @@ struct ucc_geth { frames) received that were between 128 (Including FCS length==4) and 255 octets */ u32 txok; /* Total number of octets residing in frames - that where involved in succesfull + that where involved in successfull transmission */ u16 txcf; /* Total number of PAUSE control frames transmitted by this MAC */ u8 res4[0x2]; u32 tmca; /* Total number of frames that were transmitted - succesfully with the group address bit set + successfully with the group address bit set that are not broadcast frames */ u32 tbca; /* Total number of frames transmitted - succesfully that had destination address + successfully that had destination address field equal to the broadcast address */ u32 rxfok; /* Total number of frames received OK */ u32 rxbok; /* Total number of octets received OK */ @@ -98,9 +98,9 @@ struct ucc_geth { HW because it includes octets in frames that never even reach the UCC */ u32 rmca; /* Total number of frames that were received - succesfully with the group address bit set + successfully with the group address bit set that are not broadcast frames */ - u32 rbca; /* Total number of frames received succesfully + u32 rbca; /* Total number of frames received successfully that had destination address equal to the broadcast address */ u32 scar; /* Statistics carry register */ @@ -759,15 +759,15 @@ struct ucc_geth_hardware_statistics { frames) received that were between 128 (Including FCS length==4) and 255 octets */ u32 txok; /* Total number of octets residing in frames - that where involved in succesfull + that where involved in successfull transmission */ u16 txcf; /* Total number of PAUSE control frames transmitted by this MAC */ u32 tmca; /* Total number of frames that were transmitted - succesfully with the group address bit set + successfully with the group address bit set that are not broadcast frames */ u32 tbca; /* Total number of frames transmitted - succesfully that had destination address + successfully that had destination address field equal to the broadcast address */ u32 rxfok; /* Total number of frames received OK */ u32 rxbok; /* Total number of octets received OK */ @@ -776,9 +776,9 @@ struct ucc_geth_hardware_statistics { HW because it includes octets in frames that never even reach the UCC */ u32 rmca; /* Total number of frames that were received - succesfully with the group address bit set + successfully with the group address bit set that are not broadcast frames */ - u32 rbca; /* Total number of frames received succesfully + u32 rbca; /* Total number of frames received successfully that had destination address equal to the broadcast address */ } __attribute__ ((packed)); diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 6ce7f775bb74..a516185cbc9f 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -365,8 +365,8 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, padlen = ((skb->len + 4) % 512) ? 0 : 4; - if ((!skb_cloned(skb)) - && ((headroom + tailroom) >= (4 + padlen))) { + if ((!skb_cloned(skb)) && + ((headroom + tailroom) >= (4 + padlen))) { if ((headroom < 4) || (tailroom < padlen)) { skb->data = memmove(skb->head + 4, skb->data, skb->len); skb_set_tail_pointer(skb, skb->len); @@ -541,8 +541,8 @@ static void asix_set_multicast(struct net_device *net) if (net->flags & IFF_PROMISC) { rx_ctl |= AX_RX_CTL_PRO; - } else if (net->flags & IFF_ALLMULTI - || net->mc_count > AX_MAX_MCAST) { + } else if (net->flags & IFF_ALLMULTI || + net->mc_count > AX_MAX_MCAST) { rx_ctl |= AX_RX_CTL_AMALL; } else if (net->mc_count == 0) { /* just broadcast and directed */ @@ -753,8 +753,8 @@ static void ax88172_set_multicast(struct net_device *net) if (net->flags & IFF_PROMISC) { rx_ctl |= 0x01; - } else if (net->flags & IFF_ALLMULTI - || net->mc_count > AX_MAX_MCAST) { + } else if (net->flags & IFF_ALLMULTI || + net->mc_count > AX_MAX_MCAST) { rx_ctl |= 0x02; } else if (net->mc_count == 0) { /* just broadcast and directed */ @@ -1327,7 +1327,7 @@ static const struct driver_info ax8817x_info = { .status = asix_status, .link_reset = ax88172_link_reset, .reset = ax88172_link_reset, - .flags = FLAG_ETHER, + .flags = FLAG_ETHER | FLAG_LINK_INTR, .data = 0x00130103, }; @@ -1337,7 +1337,7 @@ static const struct driver_info dlink_dub_e100_info = { .status = asix_status, .link_reset = ax88172_link_reset, .reset = ax88172_link_reset, - .flags = FLAG_ETHER, + .flags = FLAG_ETHER | FLAG_LINK_INTR, .data = 0x009f9d9f, }; @@ -1347,7 +1347,7 @@ static const struct driver_info netgear_fa120_info = { .status = asix_status, .link_reset = ax88172_link_reset, .reset = ax88172_link_reset, - .flags = FLAG_ETHER, + .flags = FLAG_ETHER | FLAG_LINK_INTR, .data = 0x00130103, }; @@ -1357,7 +1357,7 @@ static const struct driver_info hawking_uf200_info = { .status = asix_status, .link_reset = ax88172_link_reset, .reset = ax88172_link_reset, - .flags = FLAG_ETHER, + .flags = FLAG_ETHER | FLAG_LINK_INTR, .data = 0x001f1d1f, }; @@ -1367,7 +1367,7 @@ static const struct driver_info ax88772_info = { .status = asix_status, .link_reset = ax88772_link_reset, .reset = ax88772_link_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, .rx_fixup = asix_rx_fixup, .tx_fixup = asix_tx_fixup, }; @@ -1378,7 +1378,7 @@ static const struct driver_info ax88178_info = { .status = asix_status, .link_reset = ax88178_link_reset, .reset = ax88178_link_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, .rx_fixup = asix_rx_fixup, .tx_fixup = asix_tx_fixup, }; diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 2bed6b087d16..22b87e64a810 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -436,8 +436,8 @@ static netdev_tx_t catc_start_xmit(struct sk_buff *skb, clear_bit(TX_RUNNING, &catc->flags); } - if ((catc->is_f5u011 && catc->tx_ptr) - || (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2)))) + if ((catc->is_f5u011 && catc->tx_ptr) || + (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2)))) netif_stop_queue(netdev); spin_unlock_irqrestore(&catc->tx_lock, flags); diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 33d5c579c5ad..6491c9c00c83 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -372,12 +372,12 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) /* Data interface has one inactive and one active setting */ if (data_intf->num_altsetting != 2) return -EINVAL; - if (data_intf->altsetting[0].desc.bNumEndpoints == 0 - && data_intf->altsetting[1].desc.bNumEndpoints == 2) + if (data_intf->altsetting[0].desc.bNumEndpoints == 0 && + data_intf->altsetting[1].desc.bNumEndpoints == 2) data_desc = data_intf->altsetting + 1; else - if (data_intf->altsetting[0].desc.bNumEndpoints == 2 - && data_intf->altsetting[1].desc.bNumEndpoints == 0) + if (data_intf->altsetting[0].desc.bNumEndpoints == 2 && + data_intf->altsetting[1].desc.bNumEndpoints == 0) data_desc = data_intf->altsetting; else return -EINVAL; diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c index 23300656c266..c337ffc3304a 100644 --- a/drivers/net/usb/cdc_eem.c +++ b/drivers/net/usb/cdc_eem.c @@ -121,8 +121,8 @@ static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb, int headroom = skb_headroom(skb); int tailroom = skb_tailroom(skb); - if ((tailroom >= ETH_FCS_LEN + padlen) - && (headroom >= EEM_HEAD)) + if ((tailroom >= ETH_FCS_LEN + padlen) && + (headroom >= EEM_HEAD)) goto done; if ((headroom + tailroom) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 21e1ba160008..21e183a83b99 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -37,23 +37,23 @@ static int is_rndis(struct usb_interface_descriptor *desc) { - return desc->bInterfaceClass == USB_CLASS_COMM - && desc->bInterfaceSubClass == 2 - && desc->bInterfaceProtocol == 0xff; + return (desc->bInterfaceClass == USB_CLASS_COMM && + desc->bInterfaceSubClass == 2 && + desc->bInterfaceProtocol == 0xff); } static int is_activesync(struct usb_interface_descriptor *desc) { - return desc->bInterfaceClass == USB_CLASS_MISC - && desc->bInterfaceSubClass == 1 - && desc->bInterfaceProtocol == 1; + return (desc->bInterfaceClass == USB_CLASS_MISC && + desc->bInterfaceSubClass == 1 && + desc->bInterfaceProtocol == 1); } static int is_wireless_rndis(struct usb_interface_descriptor *desc) { - return desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER - && desc->bInterfaceSubClass == 1 - && desc->bInterfaceProtocol == 3; + return (desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER && + desc->bInterfaceSubClass == 1 && + desc->bInterfaceProtocol == 3); } #else @@ -116,9 +116,9 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) /* this assumes that if there's a non-RNDIS vendor variant * of cdc-acm, it'll fail RNDIS requests cleanly. */ - rndis = is_rndis(&intf->cur_altsetting->desc) - || is_activesync(&intf->cur_altsetting->desc) - || is_wireless_rndis(&intf->cur_altsetting->desc); + rndis = (is_rndis(&intf->cur_altsetting->desc) || + is_activesync(&intf->cur_altsetting->desc) || + is_wireless_rndis(&intf->cur_altsetting->desc)); memset(info, 0, sizeof *info); info->control = intf; @@ -279,10 +279,10 @@ next_desc: dev->status = &info->control->cur_altsetting->endpoint [0]; desc = &dev->status->desc; - if (!usb_endpoint_is_int_in(desc) - || (le16_to_cpu(desc->wMaxPacketSize) - < sizeof(struct usb_cdc_notification)) - || !desc->bInterval) { + if (!usb_endpoint_is_int_in(desc) || + (le16_to_cpu(desc->wMaxPacketSize) + < sizeof(struct usb_cdc_notification)) || + !desc->bInterval) { dev_dbg(&intf->dev, "bad notification endpoint\n"); dev->status = NULL; } @@ -411,13 +411,28 @@ static int cdc_bind(struct usbnet *dev, struct usb_interface *intf) return 0; } +static int cdc_manage_power(struct usbnet *dev, int on) +{ + dev->intf->needs_remote_wakeup = on; + return 0; +} + static const struct driver_info cdc_info = { .description = "CDC Ethernet Device", - .flags = FLAG_ETHER, + .flags = FLAG_ETHER | FLAG_LINK_INTR, // .check_connect = cdc_check_connect, .bind = cdc_bind, .unbind = usbnet_cdc_unbind, .status = cdc_status, + .manage_power = cdc_manage_power, +}; + +static const struct driver_info mbm_info = { + .description = "Mobile Broadband Network Device", + .flags = FLAG_WWAN, + .bind = cdc_bind, + .unbind = usbnet_cdc_unbind, + .status = cdc_status, }; /*-------------------------------------------------------------------------*/ @@ -532,72 +547,72 @@ static const struct usb_device_id products [] = { /* Ericsson F3507g */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1900, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Ericsson F3507g ver. 2 */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1902, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Ericsson F3607gw */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1904, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Ericsson F3607gw ver 2 */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1905, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Ericsson F3607gw ver 3 */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1906, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Ericsson F3307 */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x190a, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Ericsson F3307 ver 2 */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1909, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Ericsson C3607w */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1049, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Toshiba F3507g */ USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130b, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Toshiba F3607gw */ USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130c, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Toshiba F3607gw ver 2 */ USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x1311, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Dell F3507g */ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8147, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Dell F3607gw */ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8183, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { /* Dell F3607gw ver 2 */ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8184, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &cdc_info, + .driver_info = (unsigned long) &mbm_info, }, { }, // END }; @@ -610,6 +625,8 @@ static struct usb_driver cdc_driver = { .disconnect = usbnet_disconnect, .suspend = usbnet_suspend, .resume = usbnet_resume, + .reset_resume = usbnet_resume, + .supports_autosuspend = 1, }; diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index a2b30a10064f..3d406f9b2f29 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -611,7 +611,7 @@ static int dm9601_link_reset(struct usbnet *dev) static const struct driver_info dm9601_info = { .description = "Davicom DM9601 USB Ethernet", - .flags = FLAG_ETHER, + .flags = FLAG_ETHER | FLAG_LINK_INTR, .bind = dm9601_bind, .rx_fixup = dm9601_rx_fixup, .tx_fixup = dm9601_tx_fixup, diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 43bc3fcc0d85..f78f0903b073 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -602,9 +602,9 @@ static struct hso_serial *get_serial_by_shared_int_and_type( port = hso_mux_to_port(mux); for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { - if (serial_table[i] - && (dev2ser(serial_table[i])->shared_int == shared_int) - && ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) { + if (serial_table[i] && + (dev2ser(serial_table[i])->shared_int == shared_int) && + ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) { return dev2ser(serial_table[i]); } } @@ -846,8 +846,8 @@ static void hso_net_tx_timeout(struct net_device *net) dev_warn(&net->dev, "Tx timed out.\n"); /* Tear the waiting frame off the list */ - if (odev->mux_bulk_tx_urb - && (odev->mux_bulk_tx_urb->status == -EINPROGRESS)) + if (odev->mux_bulk_tx_urb && + (odev->mux_bulk_tx_urb->status == -EINPROGRESS)) usb_unlink_urb(odev->mux_bulk_tx_urb); /* Update statistics */ @@ -1020,9 +1020,9 @@ static void read_bulk_callback(struct urb *urb) u32 rest; u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; rest = urb->actual_length % odev->in_endp->wMaxPacketSize; - if (((rest == 5) || (rest == 6)) - && !memcmp(((u8 *) urb->transfer_buffer) + - urb->actual_length - 4, crc_check, 4)) { + if (((rest == 5) || (rest == 6)) && + !memcmp(((u8 *) urb->transfer_buffer) + + urb->actual_length - 4, crc_check, 4)) { urb->actual_length -= 4; } } @@ -1226,9 +1226,9 @@ static void hso_std_serial_read_bulk_callback(struct urb *urb) rest = urb->actual_length % serial->in_endp->wMaxPacketSize; - if (((rest == 5) || (rest == 6)) - && !memcmp(((u8 *) urb->transfer_buffer) + - urb->actual_length - 4, crc_check, 4)) { + if (((rest == 5) || (rest == 6)) && + !memcmp(((u8 *) urb->transfer_buffer) + + urb->actual_length - 4, crc_check, 4)) { urb->actual_length -= 4; } } @@ -2982,8 +2982,8 @@ static int hso_probe(struct usb_interface *interface, case HSO_INTF_BULK: /* It's a regular bulk interface */ - if (((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) - && !disable_net) + if (((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) && + !disable_net) hso_dev = hso_create_net_device(interface, port_spec); else hso_dev = @@ -3146,8 +3146,8 @@ static void hso_free_interface(struct usb_interface *interface) int i; for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { - if (serial_table[i] - && (serial_table[i]->interface == interface)) { + if (serial_table[i] && + (serial_table[i]->interface == interface)) { hso_dev = dev2ser(serial_table[i]); spin_lock_irq(&hso_dev->serial_lock); tty = tty_kref_get(hso_dev->tty); @@ -3163,8 +3163,8 @@ static void hso_free_interface(struct usb_interface *interface) } for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { - if (network_table[i] - && (network_table[i]->interface == interface)) { + if (network_table[i] && + (network_table[i]->interface == interface)) { struct rfkill *rfk = dev2net(network_table[i])->rfkill; /* hso_stop_net_device doesn't stop the net queue since * traffic needs to start it again when suspended */ diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index e391ef969c28..3b80e8d2d621 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -471,16 +471,7 @@ static int kaweth_reset(struct kaweth_device *kaweth) int result; dbg("kaweth_reset(%p)", kaweth); - result = kaweth_control(kaweth, - usb_sndctrlpipe(kaweth->dev, 0), - USB_REQ_SET_CONFIGURATION, - 0, - kaweth->dev->config[0].desc.bConfigurationValue, - 0, - NULL, - 0, - KAWETH_CONTROL_TIMEOUT); - + result = usb_reset_configuration(kaweth->dev); mdelay(10); dbg("kaweth_reset() returns %d.",result); diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 10873d96b2da..87374317f480 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -391,8 +391,8 @@ static void mcs7830_set_multicast(struct net_device *net) if (net->flags & IFF_PROMISC) { data->config |= HIF_REG_CONFIG_PROMISCIOUS; - } else if (net->flags & IFF_ALLMULTI - || net->mc_count > MCS7830_MAX_MCAST) { + } else if (net->flags & IFF_ALLMULTI || + net->mc_count > MCS7830_MAX_MCAST) { data->config |= HIF_REG_CONFIG_ALLMULTICAST; } else if (net->mc_count == 0) { /* just broadcast and directed */ diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index f56dec6119c3..490fa8f55424 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -114,8 +114,8 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) */ /* Issue the request; xid is unique, don't bother byteswapping it */ - if (likely(buf->msg_type != RNDIS_MSG_HALT - && buf->msg_type != RNDIS_MSG_RESET)) { + if (likely(buf->msg_type != RNDIS_MSG_HALT && + buf->msg_type != RNDIS_MSG_RESET)) { xid = dev->xid++; if (!xid) xid = dev->xid++; @@ -493,9 +493,9 @@ int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb) data_len = le32_to_cpu(hdr->data_len); /* don't choke if we see oob, per-packet data, etc */ - if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET - || skb->len < msg_len - || (data_offset + data_len + 8) > msg_len)) { + if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET || + skb->len < msg_len || + (data_offset + data_len + 8) > msg_len)) { dev->net->stats.rx_frame_errors++; devdbg(dev, "bad rndis message %d/%d/%d/%d, len %d", le32_to_cpu(hdr->msg_type), diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index c6c922247d05..0c3c738d7419 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -748,7 +748,7 @@ static int smsc95xx_phy_initialize(struct usbnet *dev) mii_nway_restart(&dev->mii); if (netif_msg_ifup(dev)) - devdbg(dev, "phy initialised succesfully"); + devdbg(dev, "phy initialised successfully"); return 0; } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index ca5ca5ae061d..035fab04c0a0 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -140,8 +140,8 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf) if (!alt || !in || !out) return -EINVAL; - if (alt->desc.bAlternateSetting != 0 - || !(dev->driver_info->flags & FLAG_NO_SETINT)) { + if (alt->desc.bAlternateSetting != 0 || + !(dev->driver_info->flags & FLAG_NO_SETINT)) { tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber, alt->desc.bAlternateSetting); if (tmp < 0) @@ -351,9 +351,10 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) spin_lock_irqsave (&dev->rxq.lock, lockflags); - if (netif_running (dev->net) - && netif_device_present (dev->net) - && !test_bit (EVENT_RX_HALT, &dev->flags)) { + if (netif_running (dev->net) && + netif_device_present (dev->net) && + !test_bit (EVENT_RX_HALT, &dev->flags) && + !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) { switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) { case -EPIPE: usbnet_defer_kevent (dev, EVENT_RX_HALT); @@ -391,8 +392,8 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) static inline void rx_process (struct usbnet *dev, struct sk_buff *skb) { - if (dev->driver_info->rx_fixup - && !dev->driver_info->rx_fixup (dev, skb)) + if (dev->driver_info->rx_fixup && + !dev->driver_info->rx_fixup (dev, skb)) goto error; // else network stack removes extra byte if we forced a short packet @@ -484,8 +485,8 @@ block: defer_bh(dev, skb, &dev->rxq); if (urb) { - if (netif_running (dev->net) - && !test_bit (EVENT_RX_HALT, &dev->flags)) { + if (netif_running (dev->net) && + !test_bit (EVENT_RX_HALT, &dev->flags)) { rx_submit (dev, urb, GFP_ATOMIC); return; } @@ -611,15 +612,39 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs); /*-------------------------------------------------------------------------*/ // precondition: never called in_interrupt +static void usbnet_terminate_urbs(struct usbnet *dev) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup); + DECLARE_WAITQUEUE(wait, current); + int temp; + + /* ensure there are no more active urbs */ + add_wait_queue(&unlink_wakeup, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + dev->wait = &unlink_wakeup; + temp = unlink_urbs(dev, &dev->txq) + + unlink_urbs(dev, &dev->rxq); + + /* maybe wait for deletions to finish. */ + while (!skb_queue_empty(&dev->rxq) + && !skb_queue_empty(&dev->txq) + && !skb_queue_empty(&dev->done)) { + schedule_timeout(UNLINK_TIMEOUT_MS); + set_current_state(TASK_UNINTERRUPTIBLE); + if (netif_msg_ifdown(dev)) + devdbg(dev, "waited for %d urb completions", + temp); + } + set_current_state(TASK_RUNNING); + dev->wait = NULL; + remove_wait_queue(&unlink_wakeup, &wait); +} int usbnet_stop (struct net_device *net) { struct usbnet *dev = netdev_priv(net); struct driver_info *info = dev->driver_info; - int temp; int retval; - DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup); - DECLARE_WAITQUEUE (wait, current); netif_stop_queue (net); @@ -641,25 +666,8 @@ int usbnet_stop (struct net_device *net) info->description); } - if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) { - /* ensure there are no more active urbs */ - add_wait_queue(&unlink_wakeup, &wait); - dev->wait = &unlink_wakeup; - temp = unlink_urbs(dev, &dev->txq) + - unlink_urbs(dev, &dev->rxq); - - /* maybe wait for deletions to finish. */ - while (!skb_queue_empty(&dev->rxq) - && !skb_queue_empty(&dev->txq) - && !skb_queue_empty(&dev->done)) { - msleep(UNLINK_TIMEOUT_MS); - if (netif_msg_ifdown(dev)) - devdbg(dev, "waited for %d urb completions", - temp); - } - dev->wait = NULL; - remove_wait_queue(&unlink_wakeup, &wait); - } + if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) + usbnet_terminate_urbs(dev); usb_kill_urb(dev->interrupt); @@ -672,7 +680,10 @@ int usbnet_stop (struct net_device *net) dev->flags = 0; del_timer_sync (&dev->delay); tasklet_kill (&dev->bh); - usb_autopm_put_interface(dev->intf); + if (info->manage_power) + info->manage_power(dev, 0); + else + usb_autopm_put_interface(dev->intf); return 0; } @@ -753,6 +764,12 @@ int usbnet_open (struct net_device *net) // delay posting reads until we're fully open tasklet_schedule (&dev->bh); + if (info->manage_power) { + retval = info->manage_power(dev, 1); + if (retval < 0) + goto done; + usb_autopm_put_interface(dev->intf); + } return retval; done: usb_autopm_put_interface(dev->intf); @@ -881,11 +898,16 @@ kevent (struct work_struct *work) /* usb_clear_halt() needs a thread context */ if (test_bit (EVENT_TX_HALT, &dev->flags)) { unlink_urbs (dev, &dev->txq); + status = usb_autopm_get_interface(dev->intf); + if (status < 0) + goto fail_pipe; status = usb_clear_halt (dev->udev, dev->out); - if (status < 0 - && status != -EPIPE - && status != -ESHUTDOWN) { + usb_autopm_put_interface(dev->intf); + if (status < 0 && + status != -EPIPE && + status != -ESHUTDOWN) { if (netif_msg_tx_err (dev)) +fail_pipe: deverr (dev, "can't clear tx halt, status %d", status); } else { @@ -896,11 +918,16 @@ kevent (struct work_struct *work) } if (test_bit (EVENT_RX_HALT, &dev->flags)) { unlink_urbs (dev, &dev->rxq); + status = usb_autopm_get_interface(dev->intf); + if (status < 0) + goto fail_halt; status = usb_clear_halt (dev->udev, dev->in); - if (status < 0 - && status != -EPIPE - && status != -ESHUTDOWN) { + usb_autopm_put_interface(dev->intf); + if (status < 0 && + status != -EPIPE && + status != -ESHUTDOWN) { if (netif_msg_rx_err (dev)) +fail_halt: deverr (dev, "can't clear rx halt, status %d", status); } else { @@ -919,7 +946,12 @@ kevent (struct work_struct *work) clear_bit (EVENT_RX_MEMORY, &dev->flags); if (urb != NULL) { clear_bit (EVENT_RX_MEMORY, &dev->flags); + status = usb_autopm_get_interface(dev->intf); + if (status < 0) + goto fail_lowmem; rx_submit (dev, urb, GFP_KERNEL); + usb_autopm_put_interface(dev->intf); +fail_lowmem: tasklet_schedule (&dev->bh); } } @@ -929,11 +961,18 @@ kevent (struct work_struct *work) int retval = 0; clear_bit (EVENT_LINK_RESET, &dev->flags); + status = usb_autopm_get_interface(dev->intf); + if (status < 0) + goto skip_reset; if(info->link_reset && (retval = info->link_reset(dev)) < 0) { + usb_autopm_put_interface(dev->intf); +skip_reset: devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s", retval, dev->udev->bus->bus_name, dev->udev->devpath, info->description); + } else { + usb_autopm_put_interface(dev->intf); } } @@ -971,6 +1010,7 @@ static void tx_complete (struct urb *urb) case -EPROTO: case -ETIME: case -EILSEQ: + usb_mark_last_busy(dev->udev); if (!timer_pending (&dev->delay)) { mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES); @@ -987,6 +1027,7 @@ static void tx_complete (struct urb *urb) } } + usb_autopm_put_interface_async(dev->intf); urb->dev = NULL; entry->state = tx_done; defer_bh(dev, skb, &dev->txq); @@ -1057,14 +1098,34 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, } } - spin_lock_irqsave (&dev->txq.lock, flags); + spin_lock_irqsave(&dev->txq.lock, flags); + retval = usb_autopm_get_interface_async(dev->intf); + if (retval < 0) { + spin_unlock_irqrestore(&dev->txq.lock, flags); + goto drop; + } + +#ifdef CONFIG_PM + /* if this triggers the device is still a sleep */ + if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { + /* transmission will be done in resume */ + usb_anchor_urb(urb, &dev->deferred); + /* no use to process more packets */ + netif_stop_queue(net); + spin_unlock_irqrestore(&dev->txq.lock, flags); + devdbg(dev, "Delaying transmission for resumption"); + goto deferred; + } +#endif switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) { case -EPIPE: netif_stop_queue (net); usbnet_defer_kevent (dev, EVENT_TX_HALT); + usb_autopm_put_interface_async(dev->intf); break; default: + usb_autopm_put_interface_async(dev->intf); if (netif_msg_tx_err (dev)) devdbg (dev, "tx: submit urb err %d", retval); break; @@ -1088,6 +1149,9 @@ drop: devdbg (dev, "> tx, len %d, type 0x%x", length, skb->protocol); } +#ifdef CONFIG_PM +deferred: +#endif return NETDEV_TX_OK; } EXPORT_SYMBOL_GPL(usbnet_start_xmit); @@ -1126,10 +1190,10 @@ static void usbnet_bh (unsigned long param) } // or are we maybe short a few urbs? - } else if (netif_running (dev->net) - && netif_device_present (dev->net) - && !timer_pending (&dev->delay) - && !test_bit (EVENT_RX_HALT, &dev->flags)) { + } else if (netif_running (dev->net) && + netif_device_present (dev->net) && + !timer_pending (&dev->delay) && + !test_bit (EVENT_RX_HALT, &dev->flags)) { int temp = dev->rxq.qlen; int qlen = RX_QLEN (dev); @@ -1210,6 +1274,14 @@ static const struct net_device_ops usbnet_netdev_ops = { // precondition: never called in_interrupt +static struct device_type wlan_type = { + .name = "wlan", +}; + +static struct device_type wwan_type = { + .name = "wwan", +}; + int usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) { @@ -1255,6 +1327,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) dev->bh.func = usbnet_bh; dev->bh.data = (unsigned long) dev; INIT_WORK (&dev->kevent, kevent); + init_usb_anchor(&dev->deferred); dev->delay.function = usbnet_bh; dev->delay.data = (unsigned long) dev; init_timer (&dev->delay); @@ -1289,12 +1362,15 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) // heuristic: "usb%d" for links we know are two-host, // else "eth%d" when there's reasonable doubt. userspace // can rename the link if it knows better. - if ((dev->driver_info->flags & FLAG_ETHER) != 0 - && (net->dev_addr [0] & 0x02) == 0) + if ((dev->driver_info->flags & FLAG_ETHER) != 0 && + (net->dev_addr [0] & 0x02) == 0) strcpy (net->name, "eth%d"); /* WLAN devices should always be named "wlan%d" */ if ((dev->driver_info->flags & FLAG_WLAN) != 0) strcpy(net->name, "wlan%d"); + /* WWAN devices should always be named "wwan%d" */ + if ((dev->driver_info->flags & FLAG_WWAN) != 0) + strcpy(net->name, "wwan%d"); /* maybe the remote can't receive an Ethernet MTU */ if (net->mtu > (dev->hard_mtu - net->hard_header_len)) @@ -1322,6 +1398,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1); SET_NETDEV_DEV(net, &udev->dev); + + if ((dev->driver_info->flags & FLAG_WLAN) != 0) + SET_NETDEV_DEVTYPE(net, &wlan_type); + if ((dev->driver_info->flags & FLAG_WWAN) != 0) + SET_NETDEV_DEVTYPE(net, &wwan_type); + status = register_netdev (net); if (status) goto out3; @@ -1335,9 +1417,11 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) // ok, it's ready to go. usb_set_intfdata (udev, dev); - // start as if the link is up netif_device_attach (net); + if (dev->driver_info->flags & FLAG_LINK_INTR) + netif_carrier_off(net); + return 0; out3: @@ -1363,13 +1447,23 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message) struct usbnet *dev = usb_get_intfdata(intf); if (!dev->suspend_count++) { + spin_lock_irq(&dev->txq.lock); + /* don't autosuspend while transmitting */ + if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) { + spin_unlock_irq(&dev->txq.lock); + return -EBUSY; + } else { + set_bit(EVENT_DEV_ASLEEP, &dev->flags); + spin_unlock_irq(&dev->txq.lock); + } /* * accelerate emptying of the rx and queues, to avoid * having everything error out. */ netif_device_detach (dev->net); - (void) unlink_urbs (dev, &dev->rxq); - (void) unlink_urbs (dev, &dev->txq); + usbnet_terminate_urbs(dev); + usb_kill_urb(dev->interrupt); + /* * reattach so runtime management can use and * wake the device @@ -1383,10 +1477,34 @@ EXPORT_SYMBOL_GPL(usbnet_suspend); int usbnet_resume (struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); + struct sk_buff *skb; + struct urb *res; + int retval; + + if (!--dev->suspend_count) { + spin_lock_irq(&dev->txq.lock); + while ((res = usb_get_from_anchor(&dev->deferred))) { + + printk(KERN_INFO"%s has delayed data\n", __func__); + skb = (struct sk_buff *)res->context; + retval = usb_submit_urb(res, GFP_ATOMIC); + if (retval < 0) { + dev_kfree_skb_any(skb); + usb_free_urb(res); + usb_autopm_put_interface_async(dev->intf); + } else { + dev->net->trans_start = jiffies; + __skb_queue_tail(&dev->txq, skb); + } + } - if (!--dev->suspend_count) + smp_mb(); + clear_bit(EVENT_DEV_ASLEEP, &dev->flags); + spin_unlock_irq(&dev->txq.lock); + if (!(dev->txq.qlen >= TX_QLEN(dev))) + netif_start_queue(dev->net); tasklet_schedule (&dev->bh); - + } return 0; } EXPORT_SYMBOL_GPL(usbnet_resume); diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c index 04882c8f9bf1..3eb0b167b5b4 100644 --- a/drivers/net/usb/zaurus.c +++ b/drivers/net/usb/zaurus.c @@ -174,8 +174,8 @@ static int blan_mdlm_bind(struct usbnet *dev, struct usb_interface *intf) goto bad_desc; } /* expect bcdVersion 1.0, ignore */ - if (memcmp(&desc->bGUID, blan_guid, 16) - && memcmp(&desc->bGUID, safe_guid, 16) ) { + if (memcmp(&desc->bGUID, blan_guid, 16) && + memcmp(&desc->bGUID, safe_guid, 16)) { /* hey, this one might _really_ be MDLM! */ dev_dbg(&intf->dev, "MDLM guid\n"); goto bad_desc; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 52af5017c46b..63099c58a6dd 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -155,8 +155,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) struct veth_net_stats *stats, *rcv_stats; int length, cpu; - skb_orphan(skb); - priv = netdev_priv(dev); rcv = priv->peer; rcv_priv = netdev_priv(rcv); @@ -168,20 +166,12 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) if (!(rcv->flags & IFF_UP)) goto tx_drop; - if (skb->len > (rcv->mtu + MTU_PAD)) - goto rx_drop; - - skb->tstamp.tv64 = 0; - skb->pkt_type = PACKET_HOST; - skb->protocol = eth_type_trans(skb, rcv); if (dev->features & NETIF_F_NO_CSUM) skb->ip_summed = rcv_priv->ip_summed; - skb->mark = 0; - secpath_reset(skb); - nf_reset(skb); - - length = skb->len; + length = skb->len + ETH_HLEN; + if (dev_forward_skb(rcv, skb) != NET_RX_SUCCESS) + goto rx_drop; stats->tx_bytes += length; stats->tx_packets++; @@ -189,7 +179,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) rcv_stats->rx_bytes += length; rcv_stats->rx_packets++; - netif_rx(skb); return NETDEV_TX_OK; tx_drop: @@ -337,7 +326,7 @@ static int veth_validate(struct nlattr *tb[], struct nlattr *data[]) static struct rtnl_link_ops veth_link_ops; -static int veth_newlink(struct net_device *dev, +static int veth_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { int err; @@ -345,6 +334,7 @@ static int veth_newlink(struct net_device *dev, struct veth_priv *priv; char ifname[IFNAMSIZ]; struct nlattr *peer_tb[IFLA_MAX + 1], **tbp; + struct net *net; /* * create and register peer first @@ -377,14 +367,22 @@ static int veth_newlink(struct net_device *dev, else snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d"); - peer = rtnl_create_link(dev_net(dev), ifname, &veth_link_ops, tbp); - if (IS_ERR(peer)) + net = rtnl_link_get_net(src_net, tbp); + if (IS_ERR(net)) + return PTR_ERR(net); + + peer = rtnl_create_link(src_net, net, ifname, &veth_link_ops, tbp); + if (IS_ERR(peer)) { + put_net(net); return PTR_ERR(peer); + } if (tbp[IFLA_ADDRESS] == NULL) random_ether_addr(peer->dev_addr); err = register_netdevice(peer); + put_net(net); + net = NULL; if (err < 0) goto err_register_peer; @@ -439,7 +437,7 @@ err_register_peer: return err; } -static void veth_dellink(struct net_device *dev) +static void veth_dellink(struct net_device *dev, struct list_head *head) { struct veth_priv *priv; struct net_device *peer; @@ -447,8 +445,8 @@ static void veth_dellink(struct net_device *dev) priv = netdev_priv(dev); peer = priv->peer; - unregister_netdevice(dev); - unregister_netdevice(peer); + unregister_netdevice_queue(dev, head); + unregister_netdevice_queue(peer, head); } static const struct nla_policy veth_policy[VETH_INFO_MAX + 1]; diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 1fd70583be44..593e01f64e9b 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -42,9 +42,9 @@ static int max_interrupt_work = 20; /* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1518 effectively disables this feature. */ -#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(CONFIG_SPARC) || defined(__ia64__) \ - || defined(__sh__) || defined(__mips__) +#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \ + defined(CONFIG_SPARC) || defined(__ia64__) || \ + defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else static int rx_copybreak; @@ -1150,7 +1150,7 @@ static int rhine_open(struct net_device *dev) void __iomem *ioaddr = rp->base; int rc; - rc = request_irq(rp->pdev->irq, &rhine_interrupt, IRQF_SHARED, dev->name, + rc = request_irq(rp->pdev->irq, rhine_interrupt, IRQF_SHARED, dev->name, dev); if (rc) return rc; @@ -1484,15 +1484,15 @@ static int rhine_rx(struct net_device *dev, int limit) } } } else { - struct sk_buff *skb; + struct sk_buff *skb = NULL; /* Length should omit the CRC */ int pkt_len = data_size - 4; /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak && - (skb = netdev_alloc_skb(dev, pkt_len + NET_IP_ALIGN)) != NULL) { - skb_reserve(skb, NET_IP_ALIGN); /* 16 byte align the IP header */ + if (pkt_len < rx_copybreak) + skb = netdev_alloc_skb_ip_align(dev, pkt_len); + if (skb) { pci_dma_sync_single_for_cpu(rp->pdev, rp->rx_skbuff_dma[entry], rp->rx_buf_sz, @@ -1683,8 +1683,8 @@ static void rhine_set_rx_mode(struct net_device *dev) rx_mode = 0x1C; iowrite32(0xffffffff, ioaddr + MulticastFilter0); iowrite32(0xffffffff, ioaddr + MulticastFilter1); - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ iowrite32(0xffffffff, ioaddr + MulticastFilter0); iowrite32(0xffffffff, ioaddr + MulticastFilter1); diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index e04e5bee005c..4ceb441f2687 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -9,7 +9,6 @@ * * TODO * rx_copybreak/alignment - * Scatter gather * More testing * * The changes are (c) Copyright 2004, Red Hat Inc. <alan@lxorguk.ukuu.org.uk> @@ -275,7 +274,7 @@ VELOCITY_PARAM(rx_thresh, "Receive fifo threshold"); #define DMA_LENGTH_MIN 0 #define DMA_LENGTH_MAX 7 -#define DMA_LENGTH_DEF 0 +#define DMA_LENGTH_DEF 6 /* DMA_length[] is used for controlling the DMA length 0: 8 DWORDs @@ -298,14 +297,6 @@ VELOCITY_PARAM(DMA_length, "DMA length"); */ VELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned"); -#define TX_CSUM_DEF 1 -/* txcsum_offload[] is used for setting the checksum offload ability of NIC. - (We only support RX checksum offload now) - 0: disable csum_offload[checksum offload - 1: enable checksum offload. (Default) -*/ -VELOCITY_PARAM(txcsum_offload, "Enable transmit packet checksum offload"); - #define FLOW_CNTL_DEF 1 #define FLOW_CNTL_MIN 1 #define FLOW_CNTL_MAX 5 @@ -354,21 +345,10 @@ VELOCITY_PARAM(ValPktLen, "Receiving or Drop invalid 802.3 frame"); */ VELOCITY_PARAM(wol_opts, "Wake On Lan options"); -#define INT_WORKS_DEF 20 -#define INT_WORKS_MIN 10 -#define INT_WORKS_MAX 64 - -VELOCITY_PARAM(int_works, "Number of packets per interrupt services"); - static int rx_copybreak = 200; module_param(rx_copybreak, int, 0644); MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); -#ifdef CONFIG_PM -static DEFINE_SPINLOCK(velocity_dev_list_lock); -static LIST_HEAD(velocity_dev_list); -#endif - /* * Internal board variants. At the moment we have only one */ @@ -417,14 +397,6 @@ static void __devexit velocity_remove1(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct velocity_info *vptr = netdev_priv(dev); -#ifdef CONFIG_PM - unsigned long flags; - - spin_lock_irqsave(&velocity_dev_list_lock, flags); - if (!list_empty(&velocity_dev_list)) - list_del(&vptr->list); - spin_unlock_irqrestore(&velocity_dev_list_lock, flags); -#endif unregister_netdev(dev); iounmap(vptr->mac_regs); pci_release_regions(pdev); @@ -510,13 +482,11 @@ static void __devinit velocity_get_options(struct velocity_opt *opts, int index, velocity_set_int_opt(&opts->numrx, RxDescriptors[index], RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF, "RxDescriptors", devname); velocity_set_int_opt(&opts->numtx, TxDescriptors[index], TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF, "TxDescriptors", devname); - velocity_set_bool_opt(&opts->flags, txcsum_offload[index], TX_CSUM_DEF, VELOCITY_FLAGS_TX_CSUM, "txcsum_offload", devname); velocity_set_int_opt(&opts->flow_cntl, flow_control[index], FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF, "flow_control", devname); velocity_set_bool_opt(&opts->flags, IP_byte_align[index], IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, "IP_byte_align", devname); velocity_set_bool_opt(&opts->flags, ValPktLen[index], VAL_PKT_LEN_DEF, VELOCITY_FLAGS_VAL_PKT_LEN, "ValPktLen", devname); velocity_set_int_opt((int *) &opts->spd_dpx, speed_duplex[index], MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, "Media link mode", devname); velocity_set_int_opt((int *) &opts->wol_opts, wol_opts[index], WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, "Wake On Lan options", devname); - velocity_set_int_opt((int *) &opts->int_works, int_works[index], INT_WORKS_MIN, INT_WORKS_MAX, INT_WORKS_DEF, "Interrupt service works", devname); opts->numrx = (opts->numrx & ~3); } @@ -925,8 +895,8 @@ static int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status) /* Check if new status is consisent with current status - if (((mii_status & curr_status) & VELOCITY_AUTONEG_ENABLE) - || (mii_status==curr_status)) { + if (((mii_status & curr_status) & VELOCITY_AUTONEG_ENABLE) || + (mii_status==curr_status)) { vptr->mii_status=mii_check_media_mode(vptr->mac_regs); vptr->mii_status=check_connection_type(vptr->mac_regs); VELOCITY_PRT(MSG_LEVEL_INFO, "Velocity link no change\n"); @@ -1162,8 +1132,8 @@ static void velocity_set_multi(struct net_device *dev) writel(0xffffffff, ®s->MARCAM[0]); writel(0xffffffff, ®s->MARCAM[4]); rx_mode = (RCR_AM | RCR_AB | RCR_PROM); - } else if ((dev->mc_count > vptr->multicast_limit) - || (dev->flags & IFF_ALLMULTI)) { + } else if ((dev->mc_count > vptr->multicast_limit) || + (dev->flags & IFF_ALLMULTI)) { writel(0xffffffff, ®s->MARCAM[0]); writel(0xffffffff, ®s->MARCAM[4]); rx_mode = (RCR_AM | RCR_AB); @@ -1259,6 +1229,66 @@ static void mii_init(struct velocity_info *vptr, u32 mii_status) } } +/** + * setup_queue_timers - Setup interrupt timers + * + * Setup interrupt frequency during suppression (timeout if the frame + * count isn't filled). + */ +static void setup_queue_timers(struct velocity_info *vptr) +{ + /* Only for newer revisions */ + if (vptr->rev_id >= REV_ID_VT3216_A0) { + u8 txqueue_timer = 0; + u8 rxqueue_timer = 0; + + if (vptr->mii_status & (VELOCITY_SPEED_1000 | + VELOCITY_SPEED_100)) { + txqueue_timer = vptr->options.txqueue_timer; + rxqueue_timer = vptr->options.rxqueue_timer; + } + + writeb(txqueue_timer, &vptr->mac_regs->TQETMR); + writeb(rxqueue_timer, &vptr->mac_regs->RQETMR); + } +} +/** + * setup_adaptive_interrupts - Setup interrupt suppression + * + * @vptr velocity adapter + * + * The velocity is able to suppress interrupt during high interrupt load. + * This function turns on that feature. + */ +static void setup_adaptive_interrupts(struct velocity_info *vptr) +{ + struct mac_regs __iomem *regs = vptr->mac_regs; + u16 tx_intsup = vptr->options.tx_intsup; + u16 rx_intsup = vptr->options.rx_intsup; + + /* Setup default interrupt mask (will be changed below) */ + vptr->int_mask = INT_MASK_DEF; + + /* Set Tx Interrupt Suppression Threshold */ + writeb(CAMCR_PS0, ®s->CAMCR); + if (tx_intsup != 0) { + vptr->int_mask &= ~(ISR_PTXI | ISR_PTX0I | ISR_PTX1I | + ISR_PTX2I | ISR_PTX3I); + writew(tx_intsup, ®s->ISRCTL); + } else + writew(ISRCTL_TSUPDIS, ®s->ISRCTL); + + /* Set Rx Interrupt Suppression Threshold */ + writeb(CAMCR_PS1, ®s->CAMCR); + if (rx_intsup != 0) { + vptr->int_mask &= ~ISR_PRXI; + writew(rx_intsup, ®s->ISRCTL); + } else + writew(ISRCTL_RSUPDIS, ®s->ISRCTL); + + /* Select page to interrupt hold timer */ + writeb(0, ®s->CAMCR); +} /** * velocity_init_registers - initialise MAC registers @@ -1345,7 +1375,7 @@ static void velocity_init_registers(struct velocity_info *vptr, */ enable_mii_autopoll(regs); - vptr->int_mask = INT_MASK_DEF; + setup_adaptive_interrupts(vptr); writel(vptr->rx.pool_dma, ®s->RDBaseLo); writew(vptr->options.numrx - 1, ®s->RDCSize); @@ -1483,7 +1513,8 @@ static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) * Do the gymnastics to get the buffer head for data at * 64byte alignment. */ - skb_reserve(rd_info->skb, (unsigned long) rd_info->skb->data & 63); + skb_reserve(rd_info->skb, + 64 - ((unsigned long) rd_info->skb->data & 63)); rd_info->skb_dma = pci_map_single(vptr->pdev, rd_info->skb->data, vptr->rx.buf_sz, PCI_DMA_FROMDEVICE); @@ -1602,12 +1633,10 @@ out: */ static int velocity_init_td_ring(struct velocity_info *vptr) { - dma_addr_t curr; int j; /* Init the TD ring entries */ for (j = 0; j < vptr->tx.numq; j++) { - curr = vptr->tx.pool_dma[j]; vptr->tx.infos[j] = kcalloc(vptr->options.numtx, sizeof(struct velocity_td_info), @@ -1673,21 +1702,27 @@ err_free_dma_rings_0: * Release an transmit buffer. If the buffer was preallocated then * recycle it, if not then unmap the buffer. */ -static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_info *tdinfo) +static void velocity_free_tx_buf(struct velocity_info *vptr, + struct velocity_td_info *tdinfo, struct tx_desc *td) { struct sk_buff *skb = tdinfo->skb; - int i; - int pktlen; /* * Don't unmap the pre-allocated tx_bufs */ if (tdinfo->skb_dma) { + int i; - pktlen = max_t(unsigned int, skb->len, ETH_ZLEN); for (i = 0; i < tdinfo->nskb_dma; i++) { - pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], pktlen, PCI_DMA_TODEVICE); - tdinfo->skb_dma[i] = 0; + size_t pktlen = max_t(size_t, skb->len, ETH_ZLEN); + + /* For scatter-gather */ + if (skb_shinfo(skb)->nr_frags > 0) + pktlen = max_t(size_t, pktlen, + td->td_buf[i].size & ~TD_QUEUE); + + pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], + le16_to_cpu(pktlen), PCI_DMA_TODEVICE); } } dev_kfree_skb_irq(skb); @@ -1801,6 +1836,8 @@ static void velocity_error(struct velocity_info *vptr, int status) BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); else BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); + + setup_queue_timers(vptr); } /* * Get link status from PHYSR0 @@ -1887,7 +1924,7 @@ static int velocity_tx_srv(struct velocity_info *vptr, u32 status) stats->tx_packets++; stats->tx_bytes += tdinfo->skb->len; } - velocity_free_tx_buf(vptr, tdinfo); + velocity_free_tx_buf(vptr, tdinfo, td); vptr->tx.used[qnum]--; } vptr->tx.tail[qnum] = idx; @@ -1899,8 +1936,8 @@ static int velocity_tx_srv(struct velocity_info *vptr, u32 status) * Look to see if we should kick the transmit network * layer for more work. */ - if (netif_queue_stopped(vptr->dev) && (full == 0) - && (!(vptr->mii_status & VELOCITY_LINK_FAIL))) { + if (netif_queue_stopped(vptr->dev) && (full == 0) && + (!(vptr->mii_status & VELOCITY_LINK_FAIL))) { netif_wake_queue(vptr->dev); } return works; @@ -1949,10 +1986,9 @@ static int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size, if (pkt_size < rx_copybreak) { struct sk_buff *new_skb; - new_skb = netdev_alloc_skb(vptr->dev, pkt_size + 2); + new_skb = netdev_alloc_skb_ip_align(vptr->dev, pkt_size); if (new_skb) { new_skb->ip_summed = rx_skb[0]->ip_summed; - skb_reserve(new_skb, 2); skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size); *rx_skb = new_skb; ret = 0; @@ -2060,13 +2096,14 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) * any received packets from the receive queue. Hand the ring * slots back to the adapter for reuse. */ -static int velocity_rx_srv(struct velocity_info *vptr, int status) +static int velocity_rx_srv(struct velocity_info *vptr, int status, + int budget_left) { struct net_device_stats *stats = &vptr->dev->stats; int rd_curr = vptr->rx.curr; int works = 0; - do { + while (works < budget_left) { struct rx_desc *rd = vptr->rx.ring + rd_curr; if (!vptr->rx.info[rd_curr].skb) @@ -2097,7 +2134,8 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status) rd_curr++; if (rd_curr >= vptr->options.numrx) rd_curr = 0; - } while (++works <= 15); + works++; + } vptr->rx.curr = rd_curr; @@ -2108,6 +2146,40 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status) return works; } +static int velocity_poll(struct napi_struct *napi, int budget) +{ + struct velocity_info *vptr = container_of(napi, + struct velocity_info, napi); + unsigned int rx_done; + u32 isr_status; + + spin_lock(&vptr->lock); + isr_status = mac_read_isr(vptr->mac_regs); + + /* Ack the interrupt */ + mac_write_isr(vptr->mac_regs, isr_status); + if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI))) + velocity_error(vptr, isr_status); + + /* + * Do rx and tx twice for performance (taken from the VIA + * out-of-tree driver). + */ + rx_done = velocity_rx_srv(vptr, isr_status, budget / 2); + velocity_tx_srv(vptr, isr_status); + rx_done += velocity_rx_srv(vptr, isr_status, budget - rx_done); + velocity_tx_srv(vptr, isr_status); + + spin_unlock(&vptr->lock); + + /* If budget not fully consumed, exit the polling mode */ + if (rx_done < budget) { + napi_complete(napi); + mac_enable_int(vptr->mac_regs); + } + + return rx_done; +} /** * velocity_intr - interrupt callback @@ -2124,8 +2196,6 @@ static irqreturn_t velocity_intr(int irq, void *dev_instance) struct net_device *dev = dev_instance; struct velocity_info *vptr = netdev_priv(dev); u32 isr_status; - int max_count = 0; - spin_lock(&vptr->lock); isr_status = mac_read_isr(vptr->mac_regs); @@ -2136,32 +2206,13 @@ static irqreturn_t velocity_intr(int irq, void *dev_instance) return IRQ_NONE; } - mac_disable_int(vptr->mac_regs); - - /* - * Keep processing the ISR until we have completed - * processing and the isr_status becomes zero - */ - - while (isr_status != 0) { - mac_write_isr(vptr->mac_regs, isr_status); - if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI))) - velocity_error(vptr, isr_status); - if (isr_status & (ISR_PRXI | ISR_PPRXI)) - max_count += velocity_rx_srv(vptr, isr_status); - if (isr_status & (ISR_PTXI | ISR_PPTXI)) - max_count += velocity_tx_srv(vptr, isr_status); - isr_status = mac_read_isr(vptr->mac_regs); - if (max_count > vptr->options.int_works) { - printk(KERN_WARNING "%s: excessive work at interrupt.\n", - dev->name); - max_count = 0; - } + if (likely(napi_schedule_prep(&vptr->napi))) { + mac_disable_int(vptr->mac_regs); + __napi_schedule(&vptr->napi); } spin_unlock(&vptr->lock); - mac_enable_int(vptr->mac_regs); - return IRQ_HANDLED; + return IRQ_HANDLED; } /** @@ -2190,7 +2241,7 @@ static int velocity_open(struct net_device *dev) velocity_init_registers(vptr, VELOCITY_INIT_COLD); - ret = request_irq(vptr->pdev->irq, &velocity_intr, IRQF_SHARED, + ret = request_irq(vptr->pdev->irq, velocity_intr, IRQF_SHARED, dev->name, dev); if (ret < 0) { /* Power down the chip */ @@ -2201,6 +2252,7 @@ static int velocity_open(struct net_device *dev) mac_enable_int(vptr->mac_regs); netif_start_queue(dev); + napi_enable(&vptr->napi); vptr->flags |= VELOCITY_FLAGS_OPENED; out: return ret; @@ -2436,6 +2488,7 @@ static int velocity_close(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); + napi_disable(&vptr->napi); netif_stop_queue(dev); velocity_shutdown(vptr); @@ -2470,14 +2523,22 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, struct velocity_td_info *tdinfo; unsigned long flags; int pktlen; - __le16 len; - int index; + int index, prev; + int i = 0; if (skb_padto(skb, ETH_ZLEN)) goto out; - pktlen = max_t(unsigned int, skb->len, ETH_ZLEN); - len = cpu_to_le16(pktlen); + /* The hardware can handle at most 7 memory segments, so merge + * the skb if there are more */ + if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + + pktlen = skb_shinfo(skb)->nr_frags == 0 ? + max_t(unsigned int, skb->len, ETH_ZLEN) : + skb_headlen(skb); spin_lock_irqsave(&vptr->lock, flags); @@ -2494,11 +2555,24 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, */ tdinfo->skb = skb; tdinfo->skb_dma[0] = pci_map_single(vptr->pdev, skb->data, pktlen, PCI_DMA_TODEVICE); - td_ptr->tdesc0.len = len; + td_ptr->tdesc0.len = cpu_to_le16(pktlen); td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); td_ptr->td_buf[0].pa_high = 0; - td_ptr->td_buf[0].size = len; - tdinfo->nskb_dma = 1; + td_ptr->td_buf[0].size = cpu_to_le16(pktlen); + + /* Handle fragments */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + tdinfo->skb_dma[i + 1] = pci_map_page(vptr->pdev, frag->page, + frag->page_offset, frag->size, + PCI_DMA_TODEVICE); + + td_ptr->td_buf[i + 1].pa_low = cpu_to_le32(tdinfo->skb_dma[i + 1]); + td_ptr->td_buf[i + 1].pa_high = 0; + td_ptr->td_buf[i + 1].size = cpu_to_le16(frag->size); + } + tdinfo->nskb_dma = i + 1; td_ptr->tdesc1.cmd = TCPLS_NORMAL + (tdinfo->nskb_dma + 1) * 16; @@ -2510,8 +2584,8 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, /* * Handle hardware checksum */ - if ((vptr->flags & VELOCITY_FLAGS_TX_CSUM) - && (skb->ip_summed == CHECKSUM_PARTIAL)) { + if ((dev->features & NETIF_F_IP_CSUM) && + (skb->ip_summed == CHECKSUM_PARTIAL)) { const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) td_ptr->tdesc1.TCR |= TCR0_TCPCK; @@ -2519,23 +2593,21 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, td_ptr->tdesc1.TCR |= (TCR0_UDPCK); td_ptr->tdesc1.TCR |= TCR0_IPCK; } - { - int prev = index - 1; + prev = index - 1; + if (prev < 0) + prev = vptr->options.numtx - 1; + td_ptr->tdesc0.len |= OWNED_BY_NIC; + vptr->tx.used[qnum]++; + vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx; - if (prev < 0) - prev = vptr->options.numtx - 1; - td_ptr->tdesc0.len |= OWNED_BY_NIC; - vptr->tx.used[qnum]++; - vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx; + if (AVAIL_TD(vptr, qnum) < 1) + netif_stop_queue(dev); - if (AVAIL_TD(vptr, qnum) < 1) - netif_stop_queue(dev); + td_ptr = &(vptr->tx.rings[qnum][prev]); + td_ptr->td_buf[0].size |= TD_QUEUE; + mac_tx_queue_wake(vptr->mac_regs, qnum); - td_ptr = &(vptr->tx.rings[qnum][prev]); - td_ptr->td_buf[0].size |= TD_QUEUE; - mac_tx_queue_wake(vptr->mac_regs, qnum); - } dev->trans_start = jiffies; spin_unlock_irqrestore(&vptr->lock, flags); out: @@ -2578,7 +2650,6 @@ static void __devinit velocity_init_info(struct pci_dev *pdev, vptr->tx.numq = info->txqueue; vptr->multicast_limit = MCAM_SIZE; spin_lock_init(&vptr->lock); - INIT_LIST_HEAD(&vptr->list); } /** @@ -2755,12 +2826,10 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi dev->irq = pdev->irq; dev->netdev_ops = &velocity_netdev_ops; dev->ethtool_ops = &velocity_ethtool_ops; + netif_napi_add(dev, &vptr->napi, velocity_poll, VELOCITY_NAPI_WEIGHT); dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | - NETIF_F_HW_VLAN_RX; - - if (vptr->flags & VELOCITY_FLAGS_TX_CSUM) - dev->features |= NETIF_F_IP_CSUM; + NETIF_F_HW_VLAN_RX | NETIF_F_IP_CSUM; ret = register_netdev(dev); if (ret < 0) @@ -2777,15 +2846,6 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi /* and leave the chip powered down */ pci_set_power_state(pdev, PCI_D3hot); -#ifdef CONFIG_PM - { - unsigned long flags; - - spin_lock_irqsave(&velocity_dev_list_lock, flags); - list_add(&vptr->list, &velocity_dev_list); - spin_unlock_irqrestore(&velocity_dev_list_lock, flags); - } -#endif velocity_nics++; out: return ret; @@ -3222,15 +3282,114 @@ static void velocity_set_msglevel(struct net_device *dev, u32 value) msglevel = value; } +static int get_pending_timer_val(int val) +{ + int mult_bits = val >> 6; + int mult = 1; + + switch (mult_bits) + { + case 1: + mult = 4; break; + case 2: + mult = 16; break; + case 3: + mult = 64; break; + case 0: + default: + break; + } + + return (val & 0x3f) * mult; +} + +static void set_pending_timer_val(int *val, u32 us) +{ + u8 mult = 0; + u8 shift = 0; + + if (us >= 0x3f) { + mult = 1; /* mult with 4 */ + shift = 2; + } + if (us >= 0x3f * 4) { + mult = 2; /* mult with 16 */ + shift = 4; + } + if (us >= 0x3f * 16) { + mult = 3; /* mult with 64 */ + shift = 6; + } + + *val = (mult << 6) | ((us >> shift) & 0x3f); +} + + +static int velocity_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ecmd) +{ + struct velocity_info *vptr = netdev_priv(dev); + + ecmd->tx_max_coalesced_frames = vptr->options.tx_intsup; + ecmd->rx_max_coalesced_frames = vptr->options.rx_intsup; + + ecmd->rx_coalesce_usecs = get_pending_timer_val(vptr->options.rxqueue_timer); + ecmd->tx_coalesce_usecs = get_pending_timer_val(vptr->options.txqueue_timer); + + return 0; +} + +static int velocity_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ecmd) +{ + struct velocity_info *vptr = netdev_priv(dev); + int max_us = 0x3f * 64; + + /* 6 bits of */ + if (ecmd->tx_coalesce_usecs > max_us) + return -EINVAL; + if (ecmd->rx_coalesce_usecs > max_us) + return -EINVAL; + + if (ecmd->tx_max_coalesced_frames > 0xff) + return -EINVAL; + if (ecmd->rx_max_coalesced_frames > 0xff) + return -EINVAL; + + vptr->options.rx_intsup = ecmd->rx_max_coalesced_frames; + vptr->options.tx_intsup = ecmd->tx_max_coalesced_frames; + + set_pending_timer_val(&vptr->options.rxqueue_timer, + ecmd->rx_coalesce_usecs); + set_pending_timer_val(&vptr->options.txqueue_timer, + ecmd->tx_coalesce_usecs); + + /* Setup the interrupt suppression and queue timers */ + mac_disable_int(vptr->mac_regs); + setup_adaptive_interrupts(vptr); + setup_queue_timers(vptr); + + mac_write_int_mask(vptr->int_mask, vptr->mac_regs); + mac_clear_isr(vptr->mac_regs); + mac_enable_int(vptr->mac_regs); + + return 0; +} + static const struct ethtool_ops velocity_ethtool_ops = { .get_settings = velocity_get_settings, .set_settings = velocity_set_settings, .get_drvinfo = velocity_get_drvinfo, + .set_tx_csum = ethtool_op_set_tx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, .get_wol = velocity_ethtool_get_wol, .set_wol = velocity_ethtool_set_wol, .get_msglevel = velocity_get_msglevel, .set_msglevel = velocity_set_msglevel, + .set_sg = ethtool_op_set_sg, .get_link = velocity_get_link, + .get_coalesce = velocity_get_coalesce, + .set_coalesce = velocity_set_coalesce, .begin = velocity_ethtool_up, .complete = velocity_ethtool_down }; @@ -3241,20 +3400,10 @@ static int velocity_netdev_event(struct notifier_block *nb, unsigned long notifi { struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; struct net_device *dev = ifa->ifa_dev->dev; - struct velocity_info *vptr; - unsigned long flags; - if (dev_net(dev) != &init_net) - return NOTIFY_DONE; - - spin_lock_irqsave(&velocity_dev_list_lock, flags); - list_for_each_entry(vptr, &velocity_dev_list, list) { - if (vptr->dev == dev) { - velocity_get_ip(vptr); - break; - } - } - spin_unlock_irqrestore(&velocity_dev_list_lock, flags); + if (dev_net(dev) == &init_net && + dev->netdev_ops == &velocity_netdev_ops) + velocity_get_ip(netdev_priv(dev)); return NOTIFY_DONE; } diff --git a/drivers/net/via-velocity.h b/drivers/net/via-velocity.h index 2f00c13ab502..ef4a0f64ba16 100644 --- a/drivers/net/via-velocity.h +++ b/drivers/net/via-velocity.h @@ -29,9 +29,10 @@ #define VELOCITY_NAME "via-velocity" #define VELOCITY_FULL_DRV_NAM "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver" -#define VELOCITY_VERSION "1.14" +#define VELOCITY_VERSION "1.15" #define VELOCITY_IO_SIZE 256 +#define VELOCITY_NAPI_WEIGHT 64 #define PKT_BUF_SZ 1540 @@ -1005,7 +1006,8 @@ struct mac_regs { volatile __le32 RDBaseLo; /* 0x38 */ volatile __le16 RDIdx; /* 0x3C */ - volatile __le16 reserved_3E; + volatile u8 TQETMR; /* 0x3E, VT3216 and above only */ + volatile u8 RQETMR; /* 0x3F, VT3216 and above only */ volatile __le32 TDBaseLo[4]; /* 0x40 */ @@ -1421,7 +1423,6 @@ enum velocity_msg_level { */ #define VELOCITY_FLAGS_TAGGING 0x00000001UL -#define VELOCITY_FLAGS_TX_CSUM 0x00000002UL #define VELOCITY_FLAGS_RX_CSUM 0x00000004UL #define VELOCITY_FLAGS_IP_ALIGN 0x00000008UL #define VELOCITY_FLAGS_VAL_PKT_LEN 0x00000010UL @@ -1491,6 +1492,10 @@ struct velocity_opt { int rx_bandwidth_hi; int rx_bandwidth_lo; int rx_bandwidth_en; + int rxqueue_timer; + int txqueue_timer; + int tx_intsup; + int rx_intsup; u32 flags; }; @@ -1499,8 +1504,6 @@ struct velocity_opt { #define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx]) struct velocity_info { - struct list_head list; - struct pci_dev *pdev; struct net_device *dev; @@ -1559,6 +1562,8 @@ struct velocity_info { u32 ticks; u8 rev_id; + + struct napi_struct napi; }; /** diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index b9e002fccbca..c708ecc3cb2e 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -282,13 +282,12 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp) do { struct skb_vnet_hdr *hdr; - skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(vi->dev, MAX_PACKET_LEN); if (unlikely(!skb)) { oom = true; break; } - skb_reserve(skb, NET_IP_ALIGN); skb_put(skb, MAX_PACKET_LEN); hdr = skb_vnet_hdr(skb); @@ -343,14 +342,12 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp) do { skb_frag_t *f; - skb = netdev_alloc_skb(vi->dev, GOOD_COPY_LEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(vi->dev, GOOD_COPY_LEN); if (unlikely(!skb)) { oom = true; break; } - skb_reserve(skb, NET_IP_ALIGN); - f = &skb_shinfo(skb)->frags[0]; f->page = get_a_page(vi, gfp); if (!f->page) { @@ -431,8 +428,8 @@ again: /* Out of packets? */ if (received < budget) { napi_complete(napi); - if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq)) - && napi_schedule_prep(napi)) { + if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq)) && + napi_schedule_prep(napi)) { vi->rvq->vq_ops->disable_cb(vi->rvq); __napi_schedule(napi); goto again; @@ -893,9 +890,9 @@ static int virtnet_probe(struct virtio_device *vdev) INIT_DELAYED_WORK(&vi->refill, refill_work); /* If we can receive ANY GSO packets, we must allocate large ones. */ - if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) - || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) - || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN)) + if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) || + virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) || + virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN)) vi->big_packets = true; if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) diff --git a/drivers/net/vmxnet3/vmxnet3_defs.h b/drivers/net/vmxnet3/vmxnet3_defs.h index dc8ee4438a4f..b4889e6c4a57 100644 --- a/drivers/net/vmxnet3/vmxnet3_defs.h +++ b/drivers/net/vmxnet3/vmxnet3_defs.h @@ -90,23 +90,60 @@ enum { VMXNET3_CMD_GET_CONF_INTR }; -struct Vmxnet3_TxDesc { - u64 addr; +/* + * Little Endian layout of bitfields - + * Byte 0 : 7.....len.....0 + * Byte 1 : rsvd gen 13.len.8 + * Byte 2 : 5.msscof.0 ext1 dtype + * Byte 3 : 13...msscof...6 + * + * Big Endian layout of bitfields - + * Byte 0: 13...msscof...6 + * Byte 1 : 5.msscof.0 ext1 dtype + * Byte 2 : rsvd gen 13.len.8 + * Byte 3 : 7.....len.....0 + * + * Thus, le32_to_cpu on the dword will allow the big endian driver to read + * the bit fields correctly. And cpu_to_le32 will convert bitfields + * bit fields written by big endian driver to format required by device. + */ - u32 len:14; - u32 gen:1; /* generation bit */ - u32 rsvd:1; - u32 dtype:1; /* descriptor type */ - u32 ext1:1; - u32 msscof:14; /* MSS, checksum offset, flags */ - - u32 hlen:10; /* header len */ - u32 om:2; /* offload mode */ - u32 eop:1; /* End Of Packet */ - u32 cq:1; /* completion request */ - u32 ext2:1; - u32 ti:1; /* VLAN Tag Insertion */ - u32 tci:16; /* Tag to Insert */ +struct Vmxnet3_TxDesc { + __le64 addr; + +#ifdef __BIG_ENDIAN_BITFIELD + u32 msscof:14; /* MSS, checksum offset, flags */ + u32 ext1:1; + u32 dtype:1; /* descriptor type */ + u32 rsvd:1; + u32 gen:1; /* generation bit */ + u32 len:14; +#else + u32 len:14; + u32 gen:1; /* generation bit */ + u32 rsvd:1; + u32 dtype:1; /* descriptor type */ + u32 ext1:1; + u32 msscof:14; /* MSS, checksum offset, flags */ +#endif /* __BIG_ENDIAN_BITFIELD */ + +#ifdef __BIG_ENDIAN_BITFIELD + u32 tci:16; /* Tag to Insert */ + u32 ti:1; /* VLAN Tag Insertion */ + u32 ext2:1; + u32 cq:1; /* completion request */ + u32 eop:1; /* End Of Packet */ + u32 om:2; /* offload mode */ + u32 hlen:10; /* header len */ +#else + u32 hlen:10; /* header len */ + u32 om:2; /* offload mode */ + u32 eop:1; /* End Of Packet */ + u32 cq:1; /* completion request */ + u32 ext2:1; + u32 ti:1; /* VLAN Tag Insertion */ + u32 tci:16; /* Tag to Insert */ +#endif /* __BIG_ENDIAN_BITFIELD */ }; /* TxDesc.OM values */ @@ -118,6 +155,8 @@ struct Vmxnet3_TxDesc { #define VMXNET3_TXD_EOP_SHIFT 12 #define VMXNET3_TXD_CQ_SHIFT 13 #define VMXNET3_TXD_GEN_SHIFT 14 +#define VMXNET3_TXD_EOP_DWORD_SHIFT 3 +#define VMXNET3_TXD_GEN_DWORD_SHIFT 2 #define VMXNET3_TXD_CQ (1 << VMXNET3_TXD_CQ_SHIFT) #define VMXNET3_TXD_EOP (1 << VMXNET3_TXD_EOP_SHIFT) @@ -130,29 +169,40 @@ struct Vmxnet3_TxDataDesc { u8 data[VMXNET3_HDR_COPY_SIZE]; }; +#define VMXNET3_TCD_GEN_SHIFT 31 +#define VMXNET3_TCD_GEN_SIZE 1 +#define VMXNET3_TCD_TXIDX_SHIFT 0 +#define VMXNET3_TCD_TXIDX_SIZE 12 +#define VMXNET3_TCD_GEN_DWORD_SHIFT 3 struct Vmxnet3_TxCompDesc { u32 txdIdx:12; /* Index of the EOP TxDesc */ u32 ext1:20; - u32 ext2; - u32 ext3; + __le32 ext2; + __le32 ext3; u32 rsvd:24; u32 type:7; /* completion type */ u32 gen:1; /* generation bit */ }; - struct Vmxnet3_RxDesc { - u64 addr; + __le64 addr; +#ifdef __BIG_ENDIAN_BITFIELD + u32 gen:1; /* Generation bit */ + u32 rsvd:15; + u32 dtype:1; /* Descriptor type */ + u32 btype:1; /* Buffer Type */ + u32 len:14; +#else u32 len:14; u32 btype:1; /* Buffer Type */ u32 dtype:1; /* Descriptor type */ u32 rsvd:15; u32 gen:1; /* Generation bit */ - +#endif u32 ext1; }; @@ -164,8 +214,17 @@ struct Vmxnet3_RxDesc { #define VMXNET3_RXD_BTYPE_SHIFT 14 #define VMXNET3_RXD_GEN_SHIFT 31 - struct Vmxnet3_RxCompDesc { +#ifdef __BIG_ENDIAN_BITFIELD + u32 ext2:1; + u32 cnc:1; /* Checksum Not Calculated */ + u32 rssType:4; /* RSS hash type used */ + u32 rqID:10; /* rx queue/ring ID */ + u32 sop:1; /* Start of Packet */ + u32 eop:1; /* End of Packet */ + u32 ext1:2; + u32 rxdIdx:12; /* Index of the RxDesc */ +#else u32 rxdIdx:12; /* Index of the RxDesc */ u32 ext1:2; u32 eop:1; /* End of Packet */ @@ -174,14 +233,36 @@ struct Vmxnet3_RxCompDesc { u32 rssType:4; /* RSS hash type used */ u32 cnc:1; /* Checksum Not Calculated */ u32 ext2:1; +#endif /* __BIG_ENDIAN_BITFIELD */ - u32 rssHash; /* RSS hash value */ + __le32 rssHash; /* RSS hash value */ +#ifdef __BIG_ENDIAN_BITFIELD + u32 tci:16; /* Tag stripped */ + u32 ts:1; /* Tag is stripped */ + u32 err:1; /* Error */ + u32 len:14; /* data length */ +#else u32 len:14; /* data length */ u32 err:1; /* Error */ u32 ts:1; /* Tag is stripped */ u32 tci:16; /* Tag stripped */ +#endif /* __BIG_ENDIAN_BITFIELD */ + +#ifdef __BIG_ENDIAN_BITFIELD + u32 gen:1; /* generation bit */ + u32 type:7; /* completion type */ + u32 fcs:1; /* Frame CRC correct */ + u32 frg:1; /* IP Fragment */ + u32 v4:1; /* IPv4 */ + u32 v6:1; /* IPv6 */ + u32 ipc:1; /* IP Checksum Correct */ + u32 tcp:1; /* TCP packet */ + u32 udp:1; /* UDP packet */ + u32 tuc:1; /* TCP/UDP Checksum Correct */ + u32 csum:16; +#else u32 csum:16; u32 tuc:1; /* TCP/UDP Checksum Correct */ u32 udp:1; /* UDP packet */ @@ -193,6 +274,7 @@ struct Vmxnet3_RxCompDesc { u32 fcs:1; /* Frame CRC correct */ u32 type:7; /* completion type */ u32 gen:1; /* generation bit */ +#endif /* __BIG_ENDIAN_BITFIELD */ }; /* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */ @@ -206,6 +288,8 @@ struct Vmxnet3_RxCompDesc { /* csum OK for TCP/UDP pkts over IP */ #define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | \ 1 << VMXNET3_RCD_IPC_SHIFT) +#define VMXNET3_TXD_GEN_SIZE 1 +#define VMXNET3_TXD_EOP_SIZE 1 /* value of RxCompDesc.rssType */ enum { @@ -219,9 +303,9 @@ enum { /* a union for accessing all cmd/completion descriptors */ union Vmxnet3_GenericDesc { - u64 qword[2]; - u32 dword[4]; - u16 word[8]; + __le64 qword[2]; + __le32 dword[4]; + __le16 word[8]; struct Vmxnet3_TxDesc txd; struct Vmxnet3_RxDesc rxd; struct Vmxnet3_TxCompDesc tcd; @@ -287,18 +371,24 @@ enum { struct Vmxnet3_GOSInfo { - u32 gosBits:2; /* 32-bit or 64-bit? */ - u32 gosType:4; /* which guest */ - u32 gosVer:16; /* gos version */ - u32 gosMisc:10; /* other info about gos */ +#ifdef __BIG_ENDIAN_BITFIELD + u32 gosMisc:10; /* other info about gos */ + u32 gosVer:16; /* gos version */ + u32 gosType:4; /* which guest */ + u32 gosBits:2; /* 32-bit or 64-bit? */ +#else + u32 gosBits:2; /* 32-bit or 64-bit? */ + u32 gosType:4; /* which guest */ + u32 gosVer:16; /* gos version */ + u32 gosMisc:10; /* other info about gos */ +#endif /* __BIG_ENDIAN_BITFIELD */ }; - struct Vmxnet3_DriverInfo { - u32 version; + __le32 version; struct Vmxnet3_GOSInfo gos; - u32 vmxnet3RevSpt; - u32 uptVerSpt; + __le32 vmxnet3RevSpt; + __le32 uptVerSpt; }; @@ -315,42 +405,42 @@ struct Vmxnet3_DriverInfo { struct Vmxnet3_MiscConf { struct Vmxnet3_DriverInfo driverInfo; - u64 uptFeatures; - u64 ddPA; /* driver data PA */ - u64 queueDescPA; /* queue descriptor table PA */ - u32 ddLen; /* driver data len */ - u32 queueDescLen; /* queue desc. table len in bytes */ - u32 mtu; - u16 maxNumRxSG; + __le64 uptFeatures; + __le64 ddPA; /* driver data PA */ + __le64 queueDescPA; /* queue descriptor table PA */ + __le32 ddLen; /* driver data len */ + __le32 queueDescLen; /* queue desc. table len in bytes */ + __le32 mtu; + __le16 maxNumRxSG; u8 numTxQueues; u8 numRxQueues; - u32 reserved[4]; + __le32 reserved[4]; }; struct Vmxnet3_TxQueueConf { - u64 txRingBasePA; - u64 dataRingBasePA; - u64 compRingBasePA; - u64 ddPA; /* driver data */ - u64 reserved; - u32 txRingSize; /* # of tx desc */ - u32 dataRingSize; /* # of data desc */ - u32 compRingSize; /* # of comp desc */ - u32 ddLen; /* size of driver data */ + __le64 txRingBasePA; + __le64 dataRingBasePA; + __le64 compRingBasePA; + __le64 ddPA; /* driver data */ + __le64 reserved; + __le32 txRingSize; /* # of tx desc */ + __le32 dataRingSize; /* # of data desc */ + __le32 compRingSize; /* # of comp desc */ + __le32 ddLen; /* size of driver data */ u8 intrIdx; u8 _pad[7]; }; struct Vmxnet3_RxQueueConf { - u64 rxRingBasePA[2]; - u64 compRingBasePA; - u64 ddPA; /* driver data */ - u64 reserved; - u32 rxRingSize[2]; /* # of rx desc */ - u32 compRingSize; /* # of rx comp desc */ - u32 ddLen; /* size of driver data */ + __le64 rxRingBasePA[2]; + __le64 compRingBasePA; + __le64 ddPA; /* driver data */ + __le64 reserved; + __le32 rxRingSize[2]; /* # of rx desc */ + __le32 compRingSize; /* # of rx comp desc */ + __le32 ddLen; /* size of driver data */ u8 intrIdx; u8 _pad[7]; }; @@ -381,7 +471,7 @@ struct Vmxnet3_IntrConf { u8 eventIntrIdx; u8 modLevels[VMXNET3_MAX_INTRS]; /* moderation level for * each intr */ - u32 reserved[3]; + __le32 reserved[3]; }; /* one bit per VLAN ID, the size is in the units of u32 */ @@ -391,21 +481,21 @@ struct Vmxnet3_IntrConf { struct Vmxnet3_QueueStatus { bool stopped; u8 _pad[3]; - u32 error; + __le32 error; }; struct Vmxnet3_TxQueueCtrl { - u32 txNumDeferred; - u32 txThreshold; - u64 reserved; + __le32 txNumDeferred; + __le32 txThreshold; + __le64 reserved; }; struct Vmxnet3_RxQueueCtrl { bool updateRxProd; u8 _pad[7]; - u64 reserved; + __le64 reserved; }; enum { @@ -417,11 +507,11 @@ enum { }; struct Vmxnet3_RxFilterConf { - u32 rxMode; /* VMXNET3_RXM_xxx */ - u16 mfTableLen; /* size of the multicast filter table */ - u16 _pad1; - u64 mfTablePA; /* PA of the multicast filters table */ - u32 vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */ + __le32 rxMode; /* VMXNET3_RXM_xxx */ + __le16 mfTableLen; /* size of the multicast filter table */ + __le16 _pad1; + __le64 mfTablePA; /* PA of the multicast filters table */ + __le32 vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */ }; @@ -444,7 +534,7 @@ struct Vmxnet3_PM_PktFilter { struct Vmxnet3_PMConf { - u16 wakeUpEvents; /* VMXNET3_PM_WAKEUP_xxx */ + __le16 wakeUpEvents; /* VMXNET3_PM_WAKEUP_xxx */ u8 numFilters; u8 pad[5]; struct Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS]; @@ -452,9 +542,9 @@ struct Vmxnet3_PMConf { struct Vmxnet3_VariableLenConfDesc { - u32 confVer; - u32 confLen; - u64 confPA; + __le32 confVer; + __le32 confLen; + __le64 confPA; }; @@ -491,12 +581,12 @@ struct Vmxnet3_DSDevRead { /* All structures in DriverShared are padded to multiples of 8 bytes */ struct Vmxnet3_DriverShared { - u32 magic; + __le32 magic; /* make devRead start at 64bit boundaries */ - u32 pad; - struct Vmxnet3_DSDevRead devRead; - u32 ecr; - u32 reserved[5]; + __le32 pad; + struct Vmxnet3_DSDevRead devRead; + __le32 ecr; + __le32 reserved[5]; }; diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 004353a46af0..1ceb9d0f8b97 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -24,12 +24,13 @@ * */ +#include <net/ip6_checksum.h> + #include "vmxnet3_int.h" char vmxnet3_driver_name[] = "vmxnet3"; #define VMXNET3_DRIVER_DESC "VMware vmxnet3 virtual NIC driver" - /* * PCI Device ID Table * Last entry must be all 0s @@ -151,11 +152,10 @@ vmxnet3_check_link(struct vmxnet3_adapter *adapter) } } - static void vmxnet3_process_events(struct vmxnet3_adapter *adapter) { - u32 events = adapter->shared->ecr; + u32 events = le32_to_cpu(adapter->shared->ecr); if (!events) return; @@ -173,7 +173,7 @@ vmxnet3_process_events(struct vmxnet3_adapter *adapter) if (adapter->tqd_start->status.stopped) { printk(KERN_ERR "%s: tq error 0x%x\n", adapter->netdev->name, - adapter->tqd_start->status.error); + le32_to_cpu(adapter->tqd_start->status.error)); } if (adapter->rqd_start->status.stopped) { printk(KERN_ERR "%s: rq error 0x%x\n", @@ -185,6 +185,106 @@ vmxnet3_process_events(struct vmxnet3_adapter *adapter) } } +#ifdef __BIG_ENDIAN_BITFIELD +/* + * The device expects the bitfields in shared structures to be written in + * little endian. When CPU is big endian, the following routines are used to + * correctly read and write into ABI. + * The general technique used here is : double word bitfields are defined in + * opposite order for big endian architecture. Then before reading them in + * driver the complete double word is translated using le32_to_cpu. Similarly + * After the driver writes into bitfields, cpu_to_le32 is used to translate the + * double words into required format. + * In order to avoid touching bits in shared structure more than once, temporary + * descriptors are used. These are passed as srcDesc to following functions. + */ +static void vmxnet3_RxDescToCPU(const struct Vmxnet3_RxDesc *srcDesc, + struct Vmxnet3_RxDesc *dstDesc) +{ + u32 *src = (u32 *)srcDesc + 2; + u32 *dst = (u32 *)dstDesc + 2; + dstDesc->addr = le64_to_cpu(srcDesc->addr); + *dst = le32_to_cpu(*src); + dstDesc->ext1 = le32_to_cpu(srcDesc->ext1); +} + +static void vmxnet3_TxDescToLe(const struct Vmxnet3_TxDesc *srcDesc, + struct Vmxnet3_TxDesc *dstDesc) +{ + int i; + u32 *src = (u32 *)(srcDesc + 1); + u32 *dst = (u32 *)(dstDesc + 1); + + /* Working backwards so that the gen bit is set at the end. */ + for (i = 2; i > 0; i--) { + src--; + dst--; + *dst = cpu_to_le32(*src); + } +} + + +static void vmxnet3_RxCompToCPU(const struct Vmxnet3_RxCompDesc *srcDesc, + struct Vmxnet3_RxCompDesc *dstDesc) +{ + int i = 0; + u32 *src = (u32 *)srcDesc; + u32 *dst = (u32 *)dstDesc; + for (i = 0; i < sizeof(struct Vmxnet3_RxCompDesc) / sizeof(u32); i++) { + *dst = le32_to_cpu(*src); + src++; + dst++; + } +} + + +/* Used to read bitfield values from double words. */ +static u32 get_bitfield32(const __le32 *bitfield, u32 pos, u32 size) +{ + u32 temp = le32_to_cpu(*bitfield); + u32 mask = ((1 << size) - 1) << pos; + temp &= mask; + temp >>= pos; + return temp; +} + + + +#endif /* __BIG_ENDIAN_BITFIELD */ + +#ifdef __BIG_ENDIAN_BITFIELD + +# define VMXNET3_TXDESC_GET_GEN(txdesc) get_bitfield32(((const __le32 *) \ + txdesc) + VMXNET3_TXD_GEN_DWORD_SHIFT, \ + VMXNET3_TXD_GEN_SHIFT, VMXNET3_TXD_GEN_SIZE) +# define VMXNET3_TXDESC_GET_EOP(txdesc) get_bitfield32(((const __le32 *) \ + txdesc) + VMXNET3_TXD_EOP_DWORD_SHIFT, \ + VMXNET3_TXD_EOP_SHIFT, VMXNET3_TXD_EOP_SIZE) +# define VMXNET3_TCD_GET_GEN(tcd) get_bitfield32(((const __le32 *)tcd) + \ + VMXNET3_TCD_GEN_DWORD_SHIFT, VMXNET3_TCD_GEN_SHIFT, \ + VMXNET3_TCD_GEN_SIZE) +# define VMXNET3_TCD_GET_TXIDX(tcd) get_bitfield32((const __le32 *)tcd, \ + VMXNET3_TCD_TXIDX_SHIFT, VMXNET3_TCD_TXIDX_SIZE) +# define vmxnet3_getRxComp(dstrcd, rcd, tmp) do { \ + (dstrcd) = (tmp); \ + vmxnet3_RxCompToCPU((rcd), (tmp)); \ + } while (0) +# define vmxnet3_getRxDesc(dstrxd, rxd, tmp) do { \ + (dstrxd) = (tmp); \ + vmxnet3_RxDescToCPU((rxd), (tmp)); \ + } while (0) + +#else + +# define VMXNET3_TXDESC_GET_GEN(txdesc) ((txdesc)->gen) +# define VMXNET3_TXDESC_GET_EOP(txdesc) ((txdesc)->eop) +# define VMXNET3_TCD_GET_GEN(tcd) ((tcd)->gen) +# define VMXNET3_TCD_GET_TXIDX(tcd) ((tcd)->txdIdx) +# define vmxnet3_getRxComp(dstrcd, rcd, tmp) (dstrcd) = (rcd) +# define vmxnet3_getRxDesc(dstrxd, rxd, tmp) (dstrxd) = (rxd) + +#endif /* __BIG_ENDIAN_BITFIELD */ + static void vmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi, @@ -212,7 +312,7 @@ vmxnet3_unmap_pkt(u32 eop_idx, struct vmxnet3_tx_queue *tq, /* no out of order completion */ BUG_ON(tq->buf_info[eop_idx].sop_idx != tq->tx_ring.next2comp); - BUG_ON(tq->tx_ring.base[eop_idx].txd.eop != 1); + BUG_ON(VMXNET3_TXDESC_GET_EOP(&(tq->tx_ring.base[eop_idx].txd)) != 1); skb = tq->buf_info[eop_idx].skb; BUG_ON(skb == NULL); @@ -246,9 +346,10 @@ vmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq, union Vmxnet3_GenericDesc *gdesc; gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; - while (gdesc->tcd.gen == tq->comp_ring.gen) { - completed += vmxnet3_unmap_pkt(gdesc->tcd.txdIdx, tq, - adapter->pdev, adapter); + while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) { + completed += vmxnet3_unmap_pkt(VMXNET3_TCD_GET_TXIDX( + &gdesc->tcd), tq, adapter->pdev, + adapter); vmxnet3_comp_ring_adv_next2proc(&tq->comp_ring); gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; @@ -472,9 +573,9 @@ vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx, } BUG_ON(rbi->dma_addr == 0); - gd->rxd.addr = rbi->dma_addr; - gd->dword[2] = (ring->gen << VMXNET3_RXD_GEN_SHIFT) | val | - rbi->len; + gd->rxd.addr = cpu_to_le64(rbi->dma_addr); + gd->dword[2] = cpu_to_le32((ring->gen << VMXNET3_RXD_GEN_SHIFT) + | val | rbi->len); num_allocated++; vmxnet3_cmd_ring_adv_next2fill(ring); @@ -531,10 +632,10 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, /* no need to map the buffer if headers are copied */ if (ctx->copy_size) { - ctx->sop_txd->txd.addr = tq->data_ring.basePA + + ctx->sop_txd->txd.addr = cpu_to_le64(tq->data_ring.basePA + tq->tx_ring.next2fill * - sizeof(struct Vmxnet3_TxDataDesc); - ctx->sop_txd->dword[2] = dw2 | ctx->copy_size; + sizeof(struct Vmxnet3_TxDataDesc)); + ctx->sop_txd->dword[2] = cpu_to_le32(dw2 | ctx->copy_size); ctx->sop_txd->dword[3] = 0; tbi = tq->buf_info + tq->tx_ring.next2fill; @@ -542,7 +643,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, dev_dbg(&adapter->netdev->dev, "txd[%u]: 0x%Lx 0x%x 0x%x\n", - tq->tx_ring.next2fill, ctx->sop_txd->txd.addr, + tq->tx_ring.next2fill, + le64_to_cpu(ctx->sop_txd->txd.addr), ctx->sop_txd->dword[2], ctx->sop_txd->dword[3]); vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); @@ -570,14 +672,14 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, gdesc = tq->tx_ring.base + tq->tx_ring.next2fill; BUG_ON(gdesc->txd.gen == tq->tx_ring.gen); - gdesc->txd.addr = tbi->dma_addr; - gdesc->dword[2] = dw2 | buf_size; + gdesc->txd.addr = cpu_to_le64(tbi->dma_addr); + gdesc->dword[2] = cpu_to_le32(dw2 | buf_size); gdesc->dword[3] = 0; dev_dbg(&adapter->netdev->dev, "txd[%u]: 0x%Lx 0x%x 0x%x\n", - tq->tx_ring.next2fill, gdesc->txd.addr, - gdesc->dword[2], gdesc->dword[3]); + tq->tx_ring.next2fill, le64_to_cpu(gdesc->txd.addr), + le32_to_cpu(gdesc->dword[2]), gdesc->dword[3]); vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT; @@ -599,14 +701,14 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, gdesc = tq->tx_ring.base + tq->tx_ring.next2fill; BUG_ON(gdesc->txd.gen == tq->tx_ring.gen); - gdesc->txd.addr = tbi->dma_addr; - gdesc->dword[2] = dw2 | frag->size; + gdesc->txd.addr = cpu_to_le64(tbi->dma_addr); + gdesc->dword[2] = cpu_to_le32(dw2 | frag->size); gdesc->dword[3] = 0; dev_dbg(&adapter->netdev->dev, "txd[%u]: 0x%llu %u %u\n", - tq->tx_ring.next2fill, gdesc->txd.addr, - gdesc->dword[2], gdesc->dword[3]); + tq->tx_ring.next2fill, le64_to_cpu(gdesc->txd.addr), + le32_to_cpu(gdesc->dword[2]), gdesc->dword[3]); vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT; } @@ -751,6 +853,10 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, unsigned long flags; struct vmxnet3_tx_ctx ctx; union Vmxnet3_GenericDesc *gdesc; +#ifdef __BIG_ENDIAN_BITFIELD + /* Use temporary descriptor to avoid touching bits multiple times */ + union Vmxnet3_GenericDesc tempTxDesc; +#endif /* conservatively estimate # of descriptors to use */ count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) + @@ -827,16 +933,22 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter); /* setup the EOP desc */ - ctx.eop_txd->dword[3] = VMXNET3_TXD_CQ | VMXNET3_TXD_EOP; + ctx.eop_txd->dword[3] = cpu_to_le32(VMXNET3_TXD_CQ | VMXNET3_TXD_EOP); /* setup the SOP desc */ +#ifdef __BIG_ENDIAN_BITFIELD + gdesc = &tempTxDesc; + gdesc->dword[2] = ctx.sop_txd->dword[2]; + gdesc->dword[3] = ctx.sop_txd->dword[3]; +#else gdesc = ctx.sop_txd; +#endif if (ctx.mss) { gdesc->txd.hlen = ctx.eth_ip_hdr_size + ctx.l4_hdr_size; gdesc->txd.om = VMXNET3_OM_TSO; gdesc->txd.msscof = ctx.mss; - tq->shared->txNumDeferred += (skb->len - gdesc->txd.hlen + - ctx.mss - 1) / ctx.mss; + le32_add_cpu(&tq->shared->txNumDeferred, (skb->len - + gdesc->txd.hlen + ctx.mss - 1) / ctx.mss); } else { if (skb->ip_summed == CHECKSUM_PARTIAL) { gdesc->txd.hlen = ctx.eth_ip_hdr_size; @@ -847,7 +959,7 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, gdesc->txd.om = 0; gdesc->txd.msscof = 0; } - tq->shared->txNumDeferred++; + le32_add_cpu(&tq->shared->txNumDeferred, 1); } if (vlan_tx_tag_present(skb)) { @@ -855,19 +967,27 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, gdesc->txd.tci = vlan_tx_tag_get(skb); } - wmb(); - - /* finally flips the GEN bit of the SOP desc */ - gdesc->dword[2] ^= VMXNET3_TXD_GEN; + /* finally flips the GEN bit of the SOP desc. */ + gdesc->dword[2] = cpu_to_le32(le32_to_cpu(gdesc->dword[2]) ^ + VMXNET3_TXD_GEN); +#ifdef __BIG_ENDIAN_BITFIELD + /* Finished updating in bitfields of Tx Desc, so write them in original + * place. + */ + vmxnet3_TxDescToLe((struct Vmxnet3_TxDesc *)gdesc, + (struct Vmxnet3_TxDesc *)ctx.sop_txd); + gdesc = ctx.sop_txd; +#endif dev_dbg(&adapter->netdev->dev, "txd[%u]: SOP 0x%Lx 0x%x 0x%x\n", (u32)((union Vmxnet3_GenericDesc *)ctx.sop_txd - - tq->tx_ring.base), gdesc->txd.addr, gdesc->dword[2], - gdesc->dword[3]); + tq->tx_ring.base), le64_to_cpu(gdesc->txd.addr), + le32_to_cpu(gdesc->dword[2]), le32_to_cpu(gdesc->dword[3])); spin_unlock_irqrestore(&tq->tx_lock, flags); - if (tq->shared->txNumDeferred >= tq->shared->txThreshold) { + if (le32_to_cpu(tq->shared->txNumDeferred) >= + le32_to_cpu(tq->shared->txThreshold)) { tq->shared->txNumDeferred = 0; VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_TXPROD, tq->tx_ring.next2fill); @@ -889,9 +1009,8 @@ static netdev_tx_t vmxnet3_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct vmxnet3_adapter *adapter = netdev_priv(netdev); - struct vmxnet3_tx_queue *tq = &adapter->tx_queue; - return vmxnet3_tq_xmit(skb, tq, adapter, netdev); + return vmxnet3_tq_xmit(skb, &adapter->tx_queue, adapter, netdev); } @@ -902,7 +1021,7 @@ vmxnet3_rx_csum(struct vmxnet3_adapter *adapter, { if (!gdesc->rcd.cnc && adapter->rxcsum) { /* typical case: TCP/UDP over IP and both csums are correct */ - if ((gdesc->dword[3] & VMXNET3_RCD_CSUM_OK) == + if ((le32_to_cpu(gdesc->dword[3]) & VMXNET3_RCD_CSUM_OK) == VMXNET3_RCD_CSUM_OK) { skb->ip_summed = CHECKSUM_UNNECESSARY; BUG_ON(!(gdesc->rcd.tcp || gdesc->rcd.udp)); @@ -957,8 +1076,12 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, u32 num_rxd = 0; struct Vmxnet3_RxCompDesc *rcd; struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx; - - rcd = &rq->comp_ring.base[rq->comp_ring.next2proc].rcd; +#ifdef __BIG_ENDIAN_BITFIELD + struct Vmxnet3_RxDesc rxCmdDesc; + struct Vmxnet3_RxCompDesc rxComp; +#endif + vmxnet3_getRxComp(rcd, &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, + &rxComp); while (rcd->gen == rq->comp_ring.gen) { struct vmxnet3_rx_buf_info *rbi; struct sk_buff *skb; @@ -976,11 +1099,12 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, idx = rcd->rxdIdx; ring_idx = rcd->rqID == rq->qid ? 0 : 1; - - rxd = &rq->rx_ring[ring_idx].base[idx].rxd; + vmxnet3_getRxDesc(rxd, &rq->rx_ring[ring_idx].base[idx].rxd, + &rxCmdDesc); rbi = rq->buf_info[ring_idx] + idx; - BUG_ON(rxd->addr != rbi->dma_addr || rxd->len != rbi->len); + BUG_ON(rxd->addr != rbi->dma_addr || + rxd->len != rbi->len); if (unlikely(rcd->eop && rcd->err)) { vmxnet3_rx_error(rq, rcd, ctx, adapter); @@ -1078,7 +1202,8 @@ rcd_done: } vmxnet3_comp_ring_adv_next2proc(&rq->comp_ring); - rcd = &rq->comp_ring.base[rq->comp_ring.next2proc].rcd; + vmxnet3_getRxComp(rcd, + &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, &rxComp); } return num_rxd; @@ -1094,7 +1219,11 @@ vmxnet3_rq_cleanup(struct vmxnet3_rx_queue *rq, for (ring_idx = 0; ring_idx < 2; ring_idx++) { for (i = 0; i < rq->rx_ring[ring_idx].size; i++) { - rxd = &rq->rx_ring[ring_idx].base[i].rxd; +#ifdef __BIG_ENDIAN_BITFIELD + struct Vmxnet3_RxDesc rxDesc; +#endif + vmxnet3_getRxDesc(rxd, + &rq->rx_ring[ring_idx].base[i].rxd, &rxDesc); if (rxd->btype == VMXNET3_RXD_BTYPE_HEAD && rq->buf_info[ring_idx][i].skb) { @@ -1346,12 +1475,12 @@ vmxnet3_request_irqs(struct vmxnet3_adapter *adapter) err = request_irq(adapter->intr.msix_entries[0].vector, vmxnet3_intr, 0, adapter->netdev->name, adapter->netdev); - } else -#endif - if (adapter->intr.type == VMXNET3_IT_MSI) { + } else if (adapter->intr.type == VMXNET3_IT_MSI) { err = request_irq(adapter->pdev->irq, vmxnet3_intr, 0, adapter->netdev->name, adapter->netdev); - } else { + } else +#endif + { err = request_irq(adapter->pdev->irq, vmxnet3_intr, IRQF_SHARED, adapter->netdev->name, adapter->netdev); @@ -1412,6 +1541,22 @@ vmxnet3_free_irqs(struct vmxnet3_adapter *adapter) } +inline void set_flag_le16(__le16 *data, u16 flag) +{ + *data = cpu_to_le16(le16_to_cpu(*data) | flag); +} + +inline void set_flag_le64(__le64 *data, u64 flag) +{ + *data = cpu_to_le64(le64_to_cpu(*data) | flag); +} + +inline void reset_flag_le64(__le64 *data, u64 flag) +{ + *data = cpu_to_le64(le64_to_cpu(*data) & ~flag); +} + + static void vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) { @@ -1427,7 +1572,8 @@ vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) adapter->vlan_grp = grp; /* update FEATURES to device */ - devRead->misc.uptFeatures |= UPT1_F_RXVLAN; + set_flag_le64(&devRead->misc.uptFeatures, + UPT1_F_RXVLAN); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_UPDATE_FEATURE); /* @@ -1450,7 +1596,7 @@ vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) struct Vmxnet3_DSDevRead *devRead = &shared->devRead; adapter->vlan_grp = NULL; - if (devRead->misc.uptFeatures & UPT1_F_RXVLAN) { + if (le64_to_cpu(devRead->misc.uptFeatures) & UPT1_F_RXVLAN) { int i; for (i = 0; i < VMXNET3_VFT_SIZE; i++) { @@ -1463,7 +1609,8 @@ vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) VMXNET3_CMD_UPDATE_VLAN_FILTERS); /* update FEATURES to device */ - devRead->misc.uptFeatures &= ~UPT1_F_RXVLAN; + reset_flag_le64(&devRead->misc.uptFeatures, + UPT1_F_RXVLAN); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_UPDATE_FEATURE); } @@ -1565,9 +1712,10 @@ vmxnet3_set_mc(struct net_device *netdev) new_table = vmxnet3_copy_mc(netdev); if (new_table) { new_mode |= VMXNET3_RXM_MCAST; - rxConf->mfTableLen = netdev->mc_count * - ETH_ALEN; - rxConf->mfTablePA = virt_to_phys(new_table); + rxConf->mfTableLen = cpu_to_le16( + netdev->mc_count * ETH_ALEN); + rxConf->mfTablePA = cpu_to_le64(virt_to_phys( + new_table)); } else { printk(KERN_INFO "%s: failed to copy mcast list" ", setting ALL_MULTI\n", netdev->name); @@ -1582,7 +1730,7 @@ vmxnet3_set_mc(struct net_device *netdev) } if (new_mode != rxConf->rxMode) { - rxConf->rxMode = new_mode; + rxConf->rxMode = cpu_to_le32(new_mode); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_UPDATE_RX_MODE); } @@ -1610,63 +1758,69 @@ vmxnet3_setup_driver_shared(struct vmxnet3_adapter *adapter) memset(shared, 0, sizeof(*shared)); /* driver settings */ - shared->magic = VMXNET3_REV1_MAGIC; - devRead->misc.driverInfo.version = VMXNET3_DRIVER_VERSION_NUM; + shared->magic = cpu_to_le32(VMXNET3_REV1_MAGIC); + devRead->misc.driverInfo.version = cpu_to_le32( + VMXNET3_DRIVER_VERSION_NUM); devRead->misc.driverInfo.gos.gosBits = (sizeof(void *) == 4 ? VMXNET3_GOS_BITS_32 : VMXNET3_GOS_BITS_64); devRead->misc.driverInfo.gos.gosType = VMXNET3_GOS_TYPE_LINUX; - devRead->misc.driverInfo.vmxnet3RevSpt = 1; - devRead->misc.driverInfo.uptVerSpt = 1; + *((u32 *)&devRead->misc.driverInfo.gos) = cpu_to_le32( + *((u32 *)&devRead->misc.driverInfo.gos)); + devRead->misc.driverInfo.vmxnet3RevSpt = cpu_to_le32(1); + devRead->misc.driverInfo.uptVerSpt = cpu_to_le32(1); - devRead->misc.ddPA = virt_to_phys(adapter); - devRead->misc.ddLen = sizeof(struct vmxnet3_adapter); + devRead->misc.ddPA = cpu_to_le64(virt_to_phys(adapter)); + devRead->misc.ddLen = cpu_to_le32(sizeof(struct vmxnet3_adapter)); /* set up feature flags */ if (adapter->rxcsum) - devRead->misc.uptFeatures |= UPT1_F_RXCSUM; + set_flag_le64(&devRead->misc.uptFeatures, UPT1_F_RXCSUM); if (adapter->lro) { - devRead->misc.uptFeatures |= UPT1_F_LRO; - devRead->misc.maxNumRxSG = 1 + MAX_SKB_FRAGS; + set_flag_le64(&devRead->misc.uptFeatures, UPT1_F_LRO); + devRead->misc.maxNumRxSG = cpu_to_le16(1 + MAX_SKB_FRAGS); } - if ((adapter->netdev->features & NETIF_F_HW_VLAN_RX) - && adapter->vlan_grp) { - devRead->misc.uptFeatures |= UPT1_F_RXVLAN; + if ((adapter->netdev->features & NETIF_F_HW_VLAN_RX) && + adapter->vlan_grp) { + set_flag_le64(&devRead->misc.uptFeatures, UPT1_F_RXVLAN); } - devRead->misc.mtu = adapter->netdev->mtu; - devRead->misc.queueDescPA = adapter->queue_desc_pa; - devRead->misc.queueDescLen = sizeof(struct Vmxnet3_TxQueueDesc) + - sizeof(struct Vmxnet3_RxQueueDesc); + devRead->misc.mtu = cpu_to_le32(adapter->netdev->mtu); + devRead->misc.queueDescPA = cpu_to_le64(adapter->queue_desc_pa); + devRead->misc.queueDescLen = cpu_to_le32( + sizeof(struct Vmxnet3_TxQueueDesc) + + sizeof(struct Vmxnet3_RxQueueDesc)); /* tx queue settings */ BUG_ON(adapter->tx_queue.tx_ring.base == NULL); devRead->misc.numTxQueues = 1; tqc = &adapter->tqd_start->conf; - tqc->txRingBasePA = adapter->tx_queue.tx_ring.basePA; - tqc->dataRingBasePA = adapter->tx_queue.data_ring.basePA; - tqc->compRingBasePA = adapter->tx_queue.comp_ring.basePA; - tqc->ddPA = virt_to_phys(adapter->tx_queue.buf_info); - tqc->txRingSize = adapter->tx_queue.tx_ring.size; - tqc->dataRingSize = adapter->tx_queue.data_ring.size; - tqc->compRingSize = adapter->tx_queue.comp_ring.size; - tqc->ddLen = sizeof(struct vmxnet3_tx_buf_info) * - tqc->txRingSize; + tqc->txRingBasePA = cpu_to_le64(adapter->tx_queue.tx_ring.basePA); + tqc->dataRingBasePA = cpu_to_le64(adapter->tx_queue.data_ring.basePA); + tqc->compRingBasePA = cpu_to_le64(adapter->tx_queue.comp_ring.basePA); + tqc->ddPA = cpu_to_le64(virt_to_phys( + adapter->tx_queue.buf_info)); + tqc->txRingSize = cpu_to_le32(adapter->tx_queue.tx_ring.size); + tqc->dataRingSize = cpu_to_le32(adapter->tx_queue.data_ring.size); + tqc->compRingSize = cpu_to_le32(adapter->tx_queue.comp_ring.size); + tqc->ddLen = cpu_to_le32(sizeof(struct vmxnet3_tx_buf_info) * + tqc->txRingSize); tqc->intrIdx = adapter->tx_queue.comp_ring.intr_idx; /* rx queue settings */ devRead->misc.numRxQueues = 1; rqc = &adapter->rqd_start->conf; - rqc->rxRingBasePA[0] = adapter->rx_queue.rx_ring[0].basePA; - rqc->rxRingBasePA[1] = adapter->rx_queue.rx_ring[1].basePA; - rqc->compRingBasePA = adapter->rx_queue.comp_ring.basePA; - rqc->ddPA = virt_to_phys(adapter->rx_queue.buf_info); - rqc->rxRingSize[0] = adapter->rx_queue.rx_ring[0].size; - rqc->rxRingSize[1] = adapter->rx_queue.rx_ring[1].size; - rqc->compRingSize = adapter->rx_queue.comp_ring.size; - rqc->ddLen = sizeof(struct vmxnet3_rx_buf_info) * - (rqc->rxRingSize[0] + rqc->rxRingSize[1]); + rqc->rxRingBasePA[0] = cpu_to_le64(adapter->rx_queue.rx_ring[0].basePA); + rqc->rxRingBasePA[1] = cpu_to_le64(adapter->rx_queue.rx_ring[1].basePA); + rqc->compRingBasePA = cpu_to_le64(adapter->rx_queue.comp_ring.basePA); + rqc->ddPA = cpu_to_le64(virt_to_phys( + adapter->rx_queue.buf_info)); + rqc->rxRingSize[0] = cpu_to_le32(adapter->rx_queue.rx_ring[0].size); + rqc->rxRingSize[1] = cpu_to_le32(adapter->rx_queue.rx_ring[1].size); + rqc->compRingSize = cpu_to_le32(adapter->rx_queue.comp_ring.size); + rqc->ddLen = cpu_to_le32(sizeof(struct vmxnet3_rx_buf_info) * + (rqc->rxRingSize[0] + rqc->rxRingSize[1])); rqc->intrIdx = adapter->rx_queue.comp_ring.intr_idx; /* intr settings */ @@ -1715,11 +1869,10 @@ vmxnet3_activate_dev(struct vmxnet3_adapter *adapter) vmxnet3_setup_driver_shared(adapter); - VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL, - VMXNET3_GET_ADDR_LO(adapter->shared_pa)); - VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH, - VMXNET3_GET_ADDR_HI(adapter->shared_pa)); - + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL, VMXNET3_GET_ADDR_LO( + adapter->shared_pa)); + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH, VMXNET3_GET_ADDR_HI( + adapter->shared_pa)); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_ACTIVATE_DEV); ret = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); @@ -2425,7 +2578,7 @@ vmxnet3_suspend(struct device *device) memcpy(pmConf->filters[i].pattern, netdev->dev_addr, ETH_ALEN); pmConf->filters[i].mask[0] = 0x3F; /* LSB ETH_ALEN bits */ - pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER; + set_flag_le16(&pmConf->wakeUpEvents, VMXNET3_PM_WAKEUP_FILTER); i++; } @@ -2467,19 +2620,21 @@ vmxnet3_suspend(struct device *device) pmConf->filters[i].mask[5] = 0x03; /* IPv4 TIP */ in_dev_put(in_dev); - pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER; + set_flag_le16(&pmConf->wakeUpEvents, VMXNET3_PM_WAKEUP_FILTER); i++; } skip_arp: if (adapter->wol & WAKE_MAGIC) - pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_MAGIC; + set_flag_le16(&pmConf->wakeUpEvents, VMXNET3_PM_WAKEUP_MAGIC); pmConf->numFilters = i; - adapter->shared->devRead.pmConfDesc.confVer = 1; - adapter->shared->devRead.pmConfDesc.confLen = sizeof(*pmConf); - adapter->shared->devRead.pmConfDesc.confPA = virt_to_phys(pmConf); + adapter->shared->devRead.pmConfDesc.confVer = cpu_to_le32(1); + adapter->shared->devRead.pmConfDesc.confLen = cpu_to_le32(sizeof( + *pmConf)); + adapter->shared->devRead.pmConfDesc.confPA = cpu_to_le64(virt_to_phys( + pmConf)); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_UPDATE_PMCFG); @@ -2510,9 +2665,11 @@ vmxnet3_resume(struct device *device) pmConf = adapter->pm_conf; memset(pmConf, 0, sizeof(*pmConf)); - adapter->shared->devRead.pmConfDesc.confVer = 1; - adapter->shared->devRead.pmConfDesc.confLen = sizeof(*pmConf); - adapter->shared->devRead.pmConfDesc.confPA = virt_to_phys(pmConf); + adapter->shared->devRead.pmConfDesc.confVer = cpu_to_le32(1); + adapter->shared->devRead.pmConfDesc.confLen = cpu_to_le32(sizeof( + *pmConf)); + adapter->shared->devRead.pmConfDesc.confPA = cpu_to_le32(virt_to_phys( + pmConf)); netif_device_attach(netdev); pci_set_power_state(pdev, PCI_D0); diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index c2c15e4cafc7..3935c4493fb7 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -50,11 +50,13 @@ vmxnet3_set_rx_csum(struct net_device *netdev, u32 val) adapter->rxcsum = val; if (netif_running(netdev)) { if (val) - adapter->shared->devRead.misc.uptFeatures |= - UPT1_F_RXCSUM; + set_flag_le64( + &adapter->shared->devRead.misc.uptFeatures, + UPT1_F_RXCSUM); else - adapter->shared->devRead.misc.uptFeatures &= - ~UPT1_F_RXCSUM; + reset_flag_le64( + &adapter->shared->devRead.misc.uptFeatures, + UPT1_F_RXCSUM); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_UPDATE_FEATURE); diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index 3c0d70d58111..34f392f46fb1 100644 --- a/drivers/net/vmxnet3/vmxnet3_int.h +++ b/drivers/net/vmxnet3/vmxnet3_int.h @@ -27,16 +27,11 @@ #ifndef _VMXNET3_INT_H #define _VMXNET3_INT_H -#include <linux/types.h> #include <linux/ethtool.h> #include <linux/delay.h> -#include <linux/device.h> #include <linux/netdevice.h> #include <linux/pci.h> -#include <linux/ethtool.h> #include <linux/compiler.h> -#include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/ioport.h> @@ -335,14 +330,14 @@ struct vmxnet3_adapter { }; #define VMXNET3_WRITE_BAR0_REG(adapter, reg, val) \ - writel((val), (adapter)->hw_addr0 + (reg)) + writel(cpu_to_le32(val), (adapter)->hw_addr0 + (reg)) #define VMXNET3_READ_BAR0_REG(adapter, reg) \ - readl((adapter)->hw_addr0 + (reg)) + le32_to_cpu(readl((adapter)->hw_addr0 + (reg))) #define VMXNET3_WRITE_BAR1_REG(adapter, reg, val) \ - writel((val), (adapter)->hw_addr1 + (reg)) + writel(cpu_to_le32(val), (adapter)->hw_addr1 + (reg)) #define VMXNET3_READ_BAR1_REG(adapter, reg) \ - readl((adapter)->hw_addr1 + (reg)) + le32_to_cpu(readl((adapter)->hw_addr1 + (reg))) #define VMXNET3_WAKE_QUEUE_THRESHOLD(tq) (5) #define VMXNET3_RX_ALLOC_THRESHOLD(rq, ring_idx, adapter) \ @@ -358,6 +353,10 @@ struct vmxnet3_adapter { #define VMXNET3_MAX_ETH_HDR_SIZE 22 #define VMXNET3_MAX_SKB_BUF_SIZE (3*1024) +void set_flag_le16(__le16 *data, u16 flag); +void set_flag_le64(__le64 *data, u64 flag); +void reset_flag_le64(__le64 *data, u64 flag); + int vmxnet3_quiesce_dev(struct vmxnet3_adapter *adapter); diff --git a/drivers/net/vxge/vxge-config.c b/drivers/net/vxge/vxge-config.c index 9e94c4b0fb18..32a75fa935ed 100644 --- a/drivers/net/vxge/vxge-config.c +++ b/drivers/net/vxge/vxge-config.c @@ -356,10 +356,8 @@ __vxge_hw_device_access_rights_get(u32 host_type, u32 func_id) switch (host_type) { case VXGE_HW_NO_MR_NO_SR_NORMAL_FUNCTION: - if (func_id == 0) { - access_rights |= VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM | - VXGE_HW_DEVICE_ACCESS_RIGHT_SRPCIM; - } + access_rights |= VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM | + VXGE_HW_DEVICE_ACCESS_RIGHT_SRPCIM; break; case VXGE_HW_MR_NO_SR_VH0_BASE_FUNCTION: access_rights |= VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM | @@ -382,6 +380,22 @@ __vxge_hw_device_access_rights_get(u32 host_type, u32 func_id) return access_rights; } /* + * __vxge_hw_device_is_privilaged + * This routine checks if the device function is privilaged or not + */ + +enum vxge_hw_status +__vxge_hw_device_is_privilaged(u32 host_type, u32 func_id) +{ + if (__vxge_hw_device_access_rights_get(host_type, + func_id) & + VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM) + return VXGE_HW_OK; + else + return VXGE_HW_ERR_PRIVILAGED_OPEARATION; +} + +/* * __vxge_hw_device_host_info_get * This routine returns the host type assignments */ @@ -446,220 +460,6 @@ __vxge_hw_verify_pci_e_info(struct __vxge_hw_device *hldev) return VXGE_HW_OK; } -enum vxge_hw_status -__vxge_hw_device_is_privilaged(struct __vxge_hw_device *hldev) -{ - if ((hldev->host_type == VXGE_HW_NO_MR_NO_SR_NORMAL_FUNCTION || - hldev->host_type == VXGE_HW_MR_NO_SR_VH0_BASE_FUNCTION || - hldev->host_type == VXGE_HW_NO_MR_SR_VH0_FUNCTION0) && - (hldev->func_id == 0)) - return VXGE_HW_OK; - else - return VXGE_HW_ERR_PRIVILAGED_OPEARATION; -} - -/* - * vxge_hw_wrr_rebalance - Rebalance the RX_WRR and KDFC_WRR calandars. - * Rebalance the RX_WRR and KDFC_WRR calandars. - */ -static enum -vxge_hw_status vxge_hw_wrr_rebalance(struct __vxge_hw_device *hldev) -{ - u64 val64; - u32 wrr_states[VXGE_HW_WEIGHTED_RR_SERVICE_STATES]; - u32 i, j, how_often = 1; - enum vxge_hw_status status = VXGE_HW_OK; - - status = __vxge_hw_device_is_privilaged(hldev); - if (status != VXGE_HW_OK) - goto exit; - - /* Reset the priorities assigned to the WRR arbitration - phases for the receive traffic */ - for (i = 0; i < VXGE_HW_WRR_RING_COUNT; i++) - writeq(0, ((&hldev->mrpcim_reg->rx_w_round_robin_0) + i)); - - /* Reset the transmit FIFO servicing calendar for FIFOs */ - for (i = 0; i < VXGE_HW_WRR_FIFO_COUNT; i++) { - writeq(0, ((&hldev->mrpcim_reg->kdfc_w_round_robin_0) + i)); - writeq(0, ((&hldev->mrpcim_reg->kdfc_w_round_robin_20) + i)); - } - - /* Assign WRR priority 0 for all FIFOs */ - for (i = 1; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { - writeq(VXGE_HW_KDFC_FIFO_0_CTRL_WRR_NUMBER(0), - ((&hldev->mrpcim_reg->kdfc_fifo_0_ctrl) + i)); - - writeq(VXGE_HW_KDFC_FIFO_17_CTRL_WRR_NUMBER(0), - ((&hldev->mrpcim_reg->kdfc_fifo_17_ctrl) + i)); - } - - /* Reset to service non-offload doorbells */ - writeq(0, &hldev->mrpcim_reg->kdfc_entry_type_sel_0); - writeq(0, &hldev->mrpcim_reg->kdfc_entry_type_sel_1); - - /* Set priority 0 to all receive queues */ - writeq(0, &hldev->mrpcim_reg->rx_queue_priority_0); - writeq(0, &hldev->mrpcim_reg->rx_queue_priority_1); - writeq(0, &hldev->mrpcim_reg->rx_queue_priority_2); - - /* Initialize all the slots as unused */ - for (i = 0; i < VXGE_HW_WEIGHTED_RR_SERVICE_STATES; i++) - wrr_states[i] = -1; - - /* Prepare the Fifo service states */ - for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { - - if (!hldev->config.vp_config[i].min_bandwidth) - continue; - - how_often = VXGE_HW_VPATH_BANDWIDTH_MAX / - hldev->config.vp_config[i].min_bandwidth; - if (how_often) { - - for (j = 0; j < VXGE_HW_WRR_FIFO_SERVICE_STATES;) { - if (wrr_states[j] == -1) { - wrr_states[j] = i; - /* Make sure each fifo is serviced - * atleast once */ - if (i == j) - j += VXGE_HW_MAX_VIRTUAL_PATHS; - else - j += how_often; - } else - j++; - } - } - } - - /* Fill the unused slots with 0 */ - for (j = 0; j < VXGE_HW_WEIGHTED_RR_SERVICE_STATES; j++) { - if (wrr_states[j] == -1) - wrr_states[j] = 0; - } - - /* Assign WRR priority number for FIFOs */ - for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { - writeq(VXGE_HW_KDFC_FIFO_0_CTRL_WRR_NUMBER(i), - ((&hldev->mrpcim_reg->kdfc_fifo_0_ctrl) + i)); - - writeq(VXGE_HW_KDFC_FIFO_17_CTRL_WRR_NUMBER(i), - ((&hldev->mrpcim_reg->kdfc_fifo_17_ctrl) + i)); - } - - /* Modify the servicing algorithm applied to the 3 types of doorbells. - i.e, none-offload, message and offload */ - writeq(VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_0(0) | - VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_1(0) | - VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_2(0) | - VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_3(0) | - VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_4(1) | - VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_5(0) | - VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_6(0) | - VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_7(0), - &hldev->mrpcim_reg->kdfc_entry_type_sel_0); - - writeq(VXGE_HW_KDFC_ENTRY_TYPE_SEL_1_NUMBER_8(1), - &hldev->mrpcim_reg->kdfc_entry_type_sel_1); - - for (i = 0, j = 0; i < VXGE_HW_WRR_FIFO_COUNT; i++) { - - val64 = VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_0(wrr_states[j++]); - val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_1(wrr_states[j++]); - val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_2(wrr_states[j++]); - val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_3(wrr_states[j++]); - val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_4(wrr_states[j++]); - val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_5(wrr_states[j++]); - val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_6(wrr_states[j++]); - val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_7(wrr_states[j++]); - - writeq(val64, (&hldev->mrpcim_reg->kdfc_w_round_robin_0 + i)); - writeq(val64, (&hldev->mrpcim_reg->kdfc_w_round_robin_20 + i)); - } - - /* Set up the priorities assigned to receive queues */ - writeq(VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_0(0) | - VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_1(1) | - VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_2(2) | - VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_3(3) | - VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_4(4) | - VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_5(5) | - VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_6(6) | - VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_7(7), - &hldev->mrpcim_reg->rx_queue_priority_0); - - writeq(VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_8(8) | - VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_9(9) | - VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_10(10) | - VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_11(11) | - VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_12(12) | - VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_13(13) | - VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_14(14) | - VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_15(15), - &hldev->mrpcim_reg->rx_queue_priority_1); - - writeq(VXGE_HW_RX_QUEUE_PRIORITY_2_RX_Q_NUMBER_16(16), - &hldev->mrpcim_reg->rx_queue_priority_2); - - /* Initialize all the slots as unused */ - for (i = 0; i < VXGE_HW_WEIGHTED_RR_SERVICE_STATES; i++) - wrr_states[i] = -1; - - /* Prepare the Ring service states */ - for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { - - if (!hldev->config.vp_config[i].min_bandwidth) - continue; - - how_often = VXGE_HW_VPATH_BANDWIDTH_MAX / - hldev->config.vp_config[i].min_bandwidth; - - if (how_often) { - for (j = 0; j < VXGE_HW_WRR_RING_SERVICE_STATES;) { - if (wrr_states[j] == -1) { - wrr_states[j] = i; - /* Make sure each ring is - * serviced atleast once */ - if (i == j) - j += VXGE_HW_MAX_VIRTUAL_PATHS; - else - j += how_often; - } else - j++; - } - } - } - - /* Fill the unused slots with 0 */ - for (j = 0; j < VXGE_HW_WEIGHTED_RR_SERVICE_STATES; j++) { - if (wrr_states[j] == -1) - wrr_states[j] = 0; - } - - for (i = 0, j = 0; i < VXGE_HW_WRR_RING_COUNT; i++) { - val64 = VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_0( - wrr_states[j++]); - val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_1( - wrr_states[j++]); - val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_2( - wrr_states[j++]); - val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_3( - wrr_states[j++]); - val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_4( - wrr_states[j++]); - val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_5( - wrr_states[j++]); - val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_6( - wrr_states[j++]); - val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_7( - wrr_states[j++]); - - writeq(val64, ((&hldev->mrpcim_reg->rx_w_round_robin_0) + i)); - } -exit: - return status; -} - /* * __vxge_hw_device_initialize * Initialize Titan-V hardware. @@ -668,14 +468,14 @@ enum vxge_hw_status __vxge_hw_device_initialize(struct __vxge_hw_device *hldev) { enum vxge_hw_status status = VXGE_HW_OK; - if (VXGE_HW_OK == __vxge_hw_device_is_privilaged(hldev)) { + if (VXGE_HW_OK == __vxge_hw_device_is_privilaged(hldev->host_type, + hldev->func_id)) { /* Validate the pci-e link width and speed */ status = __vxge_hw_verify_pci_e_info(hldev); if (status != VXGE_HW_OK) goto exit; } - vxge_hw_wrr_rebalance(hldev); exit: return status; } @@ -953,7 +753,8 @@ vxge_hw_mrpcim_stats_access(struct __vxge_hw_device *hldev, u64 val64; enum vxge_hw_status status = VXGE_HW_OK; - status = __vxge_hw_device_is_privilaged(hldev); + status = __vxge_hw_device_is_privilaged(hldev->host_type, + hldev->func_id); if (status != VXGE_HW_OK) goto exit; @@ -990,7 +791,8 @@ vxge_hw_device_xmac_aggr_stats_get(struct __vxge_hw_device *hldev, u32 port, val64 = (u64 *)aggr_stats; - status = __vxge_hw_device_is_privilaged(hldev); + status = __vxge_hw_device_is_privilaged(hldev->host_type, + hldev->func_id); if (status != VXGE_HW_OK) goto exit; @@ -1023,7 +825,8 @@ vxge_hw_device_xmac_port_stats_get(struct __vxge_hw_device *hldev, u32 port, u32 offset = 0x0; val64 = (u64 *) port_stats; - status = __vxge_hw_device_is_privilaged(hldev); + status = __vxge_hw_device_is_privilaged(hldev->host_type, + hldev->func_id); if (status != VXGE_HW_OK) goto exit; @@ -1221,7 +1024,8 @@ enum vxge_hw_status vxge_hw_device_setpause_data(struct __vxge_hw_device *hldev, goto exit; } - status = __vxge_hw_device_is_privilaged(hldev); + status = __vxge_hw_device_is_privilaged(hldev->host_type, + hldev->func_id); if (status != VXGE_HW_OK) goto exit; @@ -2353,6 +2157,28 @@ exit: } /* + * vxge_hw_vpath_strip_fcs_check - Check for FCS strip. + */ +enum vxge_hw_status +vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask) +{ + struct vxge_hw_vpmgmt_reg __iomem *vpmgmt_reg; + enum vxge_hw_status status = VXGE_HW_OK; + int i = 0, j = 0; + + for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { + if (!((vpath_mask) & vxge_mBIT(i))) + continue; + vpmgmt_reg = hldev->vpmgmt_reg[i]; + for (j = 0; j < VXGE_HW_MAC_MAX_MAC_PORT_ID; j++) { + if (readq(&vpmgmt_reg->rxmac_cfg0_port_vpmgmt_clone[j]) + & VXGE_HW_RXMAC_CFG0_PORT_VPMGMT_CLONE_STRIP_FCS) + return VXGE_HW_FAIL; + } + } + return status; +} +/* * vxge_hw_mgmt_reg_Write - Write Titan register. */ enum vxge_hw_status @@ -4056,6 +3882,30 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id) return status; } +void +vxge_hw_vpath_tti_ci_set(struct __vxge_hw_device *hldev, u32 vp_id) +{ + struct __vxge_hw_virtualpath *vpath; + struct vxge_hw_vpath_reg __iomem *vp_reg; + struct vxge_hw_vp_config *config; + u64 val64; + + vpath = &hldev->virtual_paths[vp_id]; + vp_reg = vpath->vp_reg; + config = vpath->vp_config; + + if (config->fifo.enable == VXGE_HW_FIFO_ENABLE) { + val64 = readq(&vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); + + if (config->tti.timer_ci_en != VXGE_HW_TIM_TIMER_CI_ENABLE) { + config->tti.timer_ci_en = VXGE_HW_TIM_TIMER_CI_ENABLE; + val64 |= VXGE_HW_TIM_CFG1_INT_NUM_TIMER_CI; + writeq(val64, + &vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); + } + } + return; +} /* * __vxge_hw_vpath_initialize * This routine is the final phase of init which initializes the @@ -4098,8 +3948,6 @@ __vxge_hw_vpath_initialize(struct __vxge_hw_device *hldev, u32 vp_id) if (status != VXGE_HW_OK) goto exit; - writeq(0, &vp_reg->gendma_int); - val64 = readq(&vp_reg->rtdma_rd_optimization_ctrl); /* Get MRRS value from device control */ diff --git a/drivers/net/vxge/vxge-config.h b/drivers/net/vxge/vxge-config.h index 3e94f0ce0900..e7877df092f3 100644 --- a/drivers/net/vxge/vxge-config.h +++ b/drivers/net/vxge/vxge-config.h @@ -2201,6 +2201,8 @@ __vxge_hw_vpath_func_id_get( enum vxge_hw_status __vxge_hw_vpath_reset_check(struct __vxge_hw_virtualpath *vpath); +enum vxge_hw_status +vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask); /** * vxge_debug * @level: level of debug verbosity. diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/vxge/vxge-main.c index 068d7a9d3e36..f1c4b2a1e867 100644 --- a/drivers/net/vxge/vxge-main.c +++ b/drivers/net/vxge/vxge-main.c @@ -2435,7 +2435,6 @@ static int vxge_add_isr(struct vxgedev *vdev) int ret = 0; #ifdef CONFIG_PCI_MSI int vp_idx = 0, intr_idx = 0, intr_cnt = 0, msix_idx = 0, irq_req = 0; - u64 function_mode = vdev->config.device_hw_info.function_mode; int pci_fun = PCI_FUNC(vdev->pdev->devfn); if (vdev->config.intr_type == MSI_X) @@ -2444,20 +2443,9 @@ static int vxge_add_isr(struct vxgedev *vdev) if (ret) { vxge_debug_init(VXGE_ERR, "%s: Enabling MSI-X Failed", VXGE_DRIVER_NAME); - if ((function_mode == VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) && - test_and_set_bit(__VXGE_STATE_CARD_UP, - &driver_config->inta_dev_open)) - return VXGE_HW_FAIL; - else { - vxge_debug_init(VXGE_ERR, - "%s: Defaulting to INTA", VXGE_DRIVER_NAME); - vdev->config.intr_type = INTA; - vxge_hw_device_set_intr_type(vdev->devh, - VXGE_HW_INTR_MODE_IRQLINE); - vxge_close_vpaths(vdev, 1); - vdev->no_of_vpath = 1; - vdev->stats.vpaths_open = 1; - } + vxge_debug_init(VXGE_ERR, + "%s: Defaulting to INTA", VXGE_DRIVER_NAME); + vdev->config.intr_type = INTA; } if (vdev->config.intr_type == MSI_X) { @@ -2505,24 +2493,11 @@ static int vxge_add_isr(struct vxgedev *vdev) "%s: MSIX - %d Registration failed", vdev->ndev->name, intr_cnt); vxge_rem_msix_isr(vdev); - if ((function_mode == - VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) && - test_and_set_bit(__VXGE_STATE_CARD_UP, - &driver_config->inta_dev_open)) - return VXGE_HW_FAIL; - else { - vxge_hw_device_set_intr_type( - vdev->devh, - VXGE_HW_INTR_MODE_IRQLINE); - vdev->config.intr_type = INTA; - vxge_debug_init(VXGE_ERR, - "%s: Defaulting to INTA" - , vdev->ndev->name); - vxge_close_vpaths(vdev, 1); - vdev->no_of_vpath = 1; - vdev->stats.vpaths_open = 1; + vdev->config.intr_type = INTA; + vxge_debug_init(VXGE_ERR, + "%s: Defaulting to INTA" + , vdev->ndev->name); goto INTA_MODE; - } } if (irq_req) { @@ -2535,9 +2510,9 @@ static int vxge_add_isr(struct vxgedev *vdev) } /* Point to next vpath handler */ - if (((intr_idx + 1) % VXGE_HW_VPATH_MSIX_ACTIVE == 0) - && (vp_idx < (vdev->no_of_vpath - 1))) - vp_idx++; + if (((intr_idx + 1) % VXGE_HW_VPATH_MSIX_ACTIVE == 0) && + (vp_idx < (vdev->no_of_vpath - 1))) + vp_idx++; } intr_cnt = vdev->max_vpath_supported * 2; @@ -2555,23 +2530,11 @@ static int vxge_add_isr(struct vxgedev *vdev) "%s: MSIX - %d Registration failed", vdev->ndev->name, intr_cnt); vxge_rem_msix_isr(vdev); - if ((function_mode == - VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) && - test_and_set_bit(__VXGE_STATE_CARD_UP, - &driver_config->inta_dev_open)) - return VXGE_HW_FAIL; - else { - vxge_hw_device_set_intr_type(vdev->devh, - VXGE_HW_INTR_MODE_IRQLINE); - vdev->config.intr_type = INTA; - vxge_debug_init(VXGE_ERR, - "%s: Defaulting to INTA", - vdev->ndev->name); - vxge_close_vpaths(vdev, 1); - vdev->no_of_vpath = 1; - vdev->stats.vpaths_open = 1; + vdev->config.intr_type = INTA; + vxge_debug_init(VXGE_ERR, + "%s: Defaulting to INTA", + vdev->ndev->name); goto INTA_MODE; - } } vxge_hw_vpath_msix_unmask(vdev->vpaths[vp_idx].handle, @@ -2584,6 +2547,10 @@ INTA_MODE: snprintf(vdev->desc[0], VXGE_INTR_STRLEN, "%s:vxge", vdev->ndev->name); if (vdev->config.intr_type == INTA) { + vxge_hw_device_set_intr_type(vdev->devh, + VXGE_HW_INTR_MODE_IRQLINE); + vxge_hw_vpath_tti_ci_set(vdev->devh, + vdev->vpaths[0].device_id); ret = request_irq((int) vdev->pdev->irq, vxge_isr_napi, IRQF_SHARED, vdev->desc[0], vdev); @@ -2688,13 +2655,6 @@ vxge_open(struct net_device *dev) * initialized */ netif_carrier_off(dev); - /* Check for another device already opn with INTA */ - if ((function_mode == VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) && - test_bit(__VXGE_STATE_CARD_UP, &driver_config->inta_dev_open)) { - ret = -EPERM; - goto out0; - } - /* Open VPATHs */ status = vxge_open_vpaths(vdev); if (status != VXGE_HW_OK) { @@ -2983,7 +2943,6 @@ int do_vxge_close(struct net_device *dev, int do_io) vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d Exiting...", dev->name, __func__, __LINE__); - clear_bit(__VXGE_STATE_CARD_UP, &driver_config->inta_dev_open); clear_bit(__VXGE_STATE_RESET_CARD, &vdev->state); return 0; @@ -3653,11 +3612,12 @@ static int __devinit vxge_config_vpaths( device_config->vp_config[i].fifo.enable = VXGE_HW_FIFO_ENABLE; device_config->vp_config[i].fifo.max_frags = - MAX_SKB_FRAGS; + MAX_SKB_FRAGS + 1; device_config->vp_config[i].fifo.memblock_size = VXGE_HW_MIN_FIFO_MEMBLOCK_SIZE; - txdl_size = MAX_SKB_FRAGS * sizeof(struct vxge_hw_fifo_txd); + txdl_size = device_config->vp_config[i].fifo.max_frags * + sizeof(struct vxge_hw_fifo_txd); txdl_per_memblock = VXGE_HW_MIN_FIFO_MEMBLOCK_SIZE / txdl_size; device_config->vp_config[i].fifo.fifo_blocks = @@ -4088,9 +4048,10 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre) driver_config->config_dev_cnt = 0; driver_config->total_dev_cnt = 0; driver_config->g_no_cpus = 0; - driver_config->vpath_per_dev = max_config_vpath; } + driver_config->vpath_per_dev = max_config_vpath; + driver_config->total_dev_cnt++; if (++driver_config->config_dev_cnt > max_config_dev) { ret = 0; @@ -4243,6 +4204,15 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre) goto _exit3; } + /* if FCS stripping is not disabled in MAC fail driver load */ + if (vxge_hw_vpath_strip_fcs_check(hldev, vpath_mask) != VXGE_HW_OK) { + vxge_debug_init(VXGE_ERR, + "%s: FCS stripping is not disabled in MAC" + " failing driver load", VXGE_DRIVER_NAME); + ret = -EINVAL; + goto _exit4; + } + vxge_hw_device_debug_set(hldev, VXGE_ERR, VXGE_COMPONENT_LL); /* set private device info */ @@ -4387,6 +4357,27 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre) } kfree(device_config); + + /* + * INTA is shared in multi-function mode. This is unlike the INTA + * implementation in MR mode, where each VH has its own INTA message. + * - INTA is masked (disabled) as long as at least one function sets + * its TITAN_MASK_ALL_INT.ALARM bit. + * - INTA is unmasked (enabled) when all enabled functions have cleared + * their own TITAN_MASK_ALL_INT.ALARM bit. + * The TITAN_MASK_ALL_INT ALARM & TRAFFIC bits are cleared on power up. + * Though this driver leaves the top level interrupts unmasked while + * leaving the required module interrupt bits masked on exit, there + * could be a rougue driver around that does not follow this procedure + * resulting in a failure to generate interrupts. The following code is + * present to prevent such a failure. + */ + + if (ll_config.device_hw_info.function_mode == + VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) + if (vdev->config.intr_type == INTA) + vxge_hw_device_unmask_all(hldev); + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d Exiting...", vdev->ndev->name, __func__, __LINE__); diff --git a/drivers/net/vxge/vxge-main.h b/drivers/net/vxge/vxge-main.h index 9c36b3a9a63d..7c83ba4be9d7 100644 --- a/drivers/net/vxge/vxge-main.h +++ b/drivers/net/vxge/vxge-main.h @@ -112,7 +112,6 @@ enum vxge_mac_addr_state { struct vxge_drv_config { int config_dev_cnt; int total_dev_cnt; - unsigned long inta_dev_open; int g_no_cpus; unsigned int vpath_per_dev; }; diff --git a/drivers/net/vxge/vxge-reg.h b/drivers/net/vxge/vxge-reg.h index 9a3b823e08d4..9a0cf8eaa328 100644 --- a/drivers/net/vxge/vxge-reg.h +++ b/drivers/net/vxge/vxge-reg.h @@ -4326,10 +4326,6 @@ struct vxge_hw_vpath_reg { /*0x011e0*/ u64 umq_bwr_init_byte; #define VXGE_HW_UMQ_BWR_INIT_BYTE_COUNT(val) vxge_vBIT(val, 0, 32) /*0x011e8*/ u64 gendma_int; -#define VXGE_HW_GENDMA_INT_IMMED_ENABLE vxge_mBIT(6) -#define VXGE_HW_GENDMA_INT_EVENT_ENABLE vxge_mBIT(7) -#define VXGE_HW_GENDMA_INT_NUMBER(val) vxge_vBIT(val, 9, 7) -#define VXGE_HW_GENDMA_INT_BITMAP(val) vxge_vBIT(val, 16, 16) /*0x011f0*/ u64 umqdmq_ir_init_notify; #define VXGE_HW_UMQDMQ_IR_INIT_NOTIFY_PULSE vxge_mBIT(3) /*0x011f8*/ u64 dmq_init_notify; diff --git a/drivers/net/vxge/vxge-traffic.c b/drivers/net/vxge/vxge-traffic.c index fe3ae518c69c..2c012f4ce465 100644 --- a/drivers/net/vxge/vxge-traffic.c +++ b/drivers/net/vxge/vxge-traffic.c @@ -295,6 +295,8 @@ void vxge_hw_device_intr_enable(struct __vxge_hw_device *hldev) u64 val64; u32 val32; + vxge_hw_device_mask_all(hldev); + for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { if (!(hldev->vpaths_deployed & vxge_mBIT(i))) @@ -1232,7 +1234,7 @@ void vxge_hw_fifo_txdl_post(struct __vxge_hw_fifo *fifo, void *txdlh) vxge_hw_channel_dtr_post(&fifo->channel, txdlh); __vxge_hw_non_offload_db_post(fifo, - (u64)(size_t)txdl_priv->dma_addr, + (u64)txdl_priv->dma_addr, txdl_priv->frags - 1, fifo->no_snoop_bits); @@ -1961,14 +1963,14 @@ enum vxge_hw_status __vxge_hw_vpath_alarm_process( val64 = readq(&vp_reg->asic_ntwk_vp_err_reg); if (((val64 & - VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT) && - (!(val64 & + VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT) && + (!(val64 & VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK))) || ((val64 & - VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT_OCCURR) - && (!(val64 & + VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT_OCCURR) && + (!(val64 & VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK_OCCURR) - ))) { + ))) { sw_stats->error_stats.network_sustained_fault++; writeq( @@ -1981,14 +1983,14 @@ enum vxge_hw_status __vxge_hw_vpath_alarm_process( } if (((val64 & - VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK) && - (!(val64 & + VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK) && + (!(val64 & VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT))) || ((val64 & - VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK_OCCURR) - && (!(val64 & + VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK_OCCURR) && + (!(val64 & VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT_OCCURR) - ))) { + ))) { sw_stats->error_stats.network_sustained_ok++; diff --git a/drivers/net/vxge/vxge-traffic.h b/drivers/net/vxge/vxge-traffic.h index 461742b4442b..861c853e3e84 100644 --- a/drivers/net/vxge/vxge-traffic.h +++ b/drivers/net/vxge/vxge-traffic.h @@ -2389,6 +2389,8 @@ vxge_hw_channel_dtr_free(struct __vxge_hw_channel *channel, void *dtrh); int vxge_hw_channel_dtr_count(struct __vxge_hw_channel *channel); +void +vxge_hw_vpath_tti_ci_set(struct __vxge_hw_device *hldev, u32 vp_id); /* ========================== PRIVATE API ================================= */ diff --git a/drivers/net/vxge/vxge-version.h b/drivers/net/vxge/vxge-version.h index 8fbce7552035..77c2a754b7b8 100644 --- a/drivers/net/vxge/vxge-version.h +++ b/drivers/net/vxge/vxge-version.h @@ -17,7 +17,7 @@ #define VXGE_VERSION_MAJOR "2" #define VXGE_VERSION_MINOR "0" -#define VXGE_VERSION_FIX "5" -#define VXGE_VERSION_BUILD "18053" +#define VXGE_VERSION_FIX "6" +#define VXGE_VERSION_BUILD "18937" #define VXGE_VERSION_FOR "k" #endif diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 8e25ca7080c7..b36bf96eb502 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -297,8 +297,8 @@ static ssize_t cosa_write(struct file *file, static unsigned int cosa_poll(struct file *file, poll_table *poll); static int cosa_open(struct inode *inode, struct file *file); static int cosa_release(struct inode *inode, struct file *file); -static int cosa_chardev_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long cosa_chardev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); #ifdef COSA_FASYNC_WORKING static int cosa_fasync(struct inode *inode, struct file *file, int on); #endif @@ -309,7 +309,7 @@ static const struct file_operations cosa_fops = { .read = cosa_read, .write = cosa_write, .poll = cosa_poll, - .ioctl = cosa_chardev_ioctl, + .unlocked_ioctl = cosa_chardev_ioctl, .open = cosa_open, .release = cosa_release, #ifdef COSA_FASYNC_WORKING @@ -1205,12 +1205,18 @@ static int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return hdlc_ioctl(dev, ifr, cmd); } -static int cosa_chardev_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long cosa_chardev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct channel_data *channel = file->private_data; - struct cosa_data *cosa = channel->cosa; - return cosa_ioctl_common(cosa, channel, cmd, arg); + struct cosa_data *cosa; + long ret; + + lock_kernel(); + cosa = channel->cosa; + ret = cosa_ioctl_common(cosa, channel, cmd, arg); + unlock_kernel(); + return ret; } diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c index 15d353f268b5..421d0715310e 100644 --- a/drivers/net/wan/dlci.c +++ b/drivers/net/wan/dlci.c @@ -77,7 +77,7 @@ static int dlci_header(struct sk_buff *skb, struct net_device *dev, dlp = netdev_priv(dev); hdr.control = FRAD_I_UI; - switch(type) + switch (type) { case ETH_P_IP: hdr.IP_NLPID = FRAD_P_IP; @@ -130,7 +130,7 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev) dev->stats.rx_errors++; } else - switch(hdr->IP_NLPID) + switch (hdr->IP_NLPID) { case FRAD_P_PADDING: if (hdr->NLPID != FRAD_P_SNAP) @@ -208,7 +208,7 @@ static int dlci_config(struct net_device *dev, struct dlci_conf __user *conf, in if (!get) { - if(copy_from_user(&config, conf, sizeof(struct dlci_conf))) + if (copy_from_user(&config, conf, sizeof(struct dlci_conf))) return -EFAULT; if (config.flags & ~DLCI_VALID_FLAGS) return(-EINVAL); @@ -222,7 +222,7 @@ static int dlci_config(struct net_device *dev, struct dlci_conf __user *conf, in if (get) { - if(copy_to_user(conf, &dlp->config, sizeof(struct dlci_conf))) + if (copy_to_user(conf, &dlp->config, sizeof(struct dlci_conf))) return -EFAULT; } @@ -238,7 +238,7 @@ static int dlci_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) dlp = netdev_priv(dev); - switch(cmd) + switch (cmd) { case DLCI_GET_SLAVE: if (!*(short *)(dev->dev_addr)) @@ -417,7 +417,7 @@ static int dlci_ioctl(unsigned int cmd, void __user *arg) if (!capable(CAP_NET_ADMIN)) return(-EPERM); - if(copy_from_user(&add, arg, sizeof(struct dlci_add))) + if (copy_from_user(&add, arg, sizeof(struct dlci_add))) return -EFAULT; switch (cmd) @@ -426,7 +426,7 @@ static int dlci_ioctl(unsigned int cmd, void __user *arg) err = dlci_add(&add); if (!err) - if(copy_to_user(arg, &add, sizeof(struct dlci_add))) + if (copy_to_user(arg, &add, sizeof(struct dlci_add))) return -EFAULT; break; diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 07d00b4cf48a..3f759daf3ca4 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -1128,7 +1128,7 @@ done: init_timer(&dpriv->timer); dpriv->timer.expires = jiffies + 10*HZ; dpriv->timer.data = (unsigned long)dev; - dpriv->timer.function = &dscc4_timer; + dpriv->timer.function = dscc4_timer; add_timer(&dpriv->timer); netif_carrier_on(dev); diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index beda387f2fc7..9bc2e3649157 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -1346,8 +1346,8 @@ do_bottom_half_tx(struct fst_card_info *card) dev = port_to_dev(port); while (!(FST_RDB(card, txDescrRing[pi][port->txpos].bits) & - DMA_OWN) - && !(card->dmatx_in_progress)) { + DMA_OWN) && + !(card->dmatx_in_progress)) { /* * There doesn't seem to be a txdone event per-se * We seem to have to deduce it, by checking the DMA_OWN @@ -1379,8 +1379,8 @@ do_bottom_half_tx(struct fst_card_info *card) */ FST_WRW(card, txDescrRing[pi][port->txpos].bcnt, cnv_bcnt(skb->len)); - if ((skb->len < FST_MIN_DMA_LEN) - || (card->family == FST_FAMILY_TXP)) { + if ((skb->len < FST_MIN_DMA_LEN) || + (card->family == FST_FAMILY_TXP)) { /* Enqueue the packet with normal io */ memcpy_toio(card->mem + BUF_OFFSET(txBuffer[pi] @@ -2030,8 +2030,8 @@ fst_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* Sanity check the parameters. We don't support partial writes * when going over the top */ - if (wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE - || wrthdr.size + wrthdr.offset > FST_MEMSIZE) { + if (wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE || + wrthdr.size + wrthdr.offset > FST_MEMSIZE) { return -ENXIO; } diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index cc07236ea734..9937bbab938d 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -57,7 +57,7 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, { struct hdlc_device *hdlc = dev_to_hdlc(dev); - if (dev_net(dev) != &init_net) { + if (!net_eq(dev_net(dev), &init_net)) { kfree_skb(skb); return 0; } @@ -102,7 +102,7 @@ static int hdlc_device_event(struct notifier_block *this, unsigned long event, unsigned long flags; int on; - if (dev_net(dev) != &init_net) + if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; if (!(dev->priv_flags & IFF_WAN_HDLC)) diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index 840cff72a0f1..0e52993e2079 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -182,7 +182,7 @@ static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) if (pvc->dlci == dlci) return pvc; if (pvc->dlci > dlci) - return NULL; /* the listed is sorted */ + return NULL; /* the list is sorted */ pvc = pvc->next; } @@ -1214,10 +1214,10 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr) return 0; case IF_PROTO_FR: - if(!capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; - if(dev->flags & IFF_UP) + if (dev->flags & IFF_UP) return -EBUSY; if (copy_from_user(&new_settings, fr_s, size)) @@ -1263,7 +1263,7 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr) if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */ return -EINVAL; - if(!capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; if (copy_from_user(&pvc, ifr->ifr_settings.ifs_ifsu.fr_pvc, diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index 15002c3d0d95..74164d29524c 100644 --- a/drivers/net/wan/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -218,7 +218,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq) /* We want a fast IRQ for this device. Actually we'd like an even faster IRQ ;) - This is one driver RtLinux is made for */ - if (request_irq(irq, &z8530_interrupt, IRQF_DISABLED, + if (request_irq(irq, z8530_interrupt, IRQF_DISABLED, "Hostess SV11", sv) < 0) { printk(KERN_WARNING "hostess: IRQ %d already in use.\n", irq); goto err_irq; diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index 7ea71b33d2e9..4b6f27e7c820 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -927,7 +927,7 @@ static int __devinit lmc_init_one(struct pci_dev *pdev, sc->lmc_media = &lmc_t1_media; break; default: - printk(KERN_WARNING "%s: LMC UNKOWN CARD!\n", dev->name); + printk(KERN_WARNING "%s: LMC UNKNOWN CARD!\n", dev->name); break; } @@ -1028,7 +1028,7 @@ static int lmc_open(struct net_device *dev) lmc_softreset (sc); /* Since we have to use PCI bus, this should work on x86,alpha,ppc */ - if (request_irq (dev->irq, &lmc_interrupt, IRQF_SHARED, dev->name, dev)){ + if (request_irq (dev->irq, lmc_interrupt, IRQF_SHARED, dev->name, dev)){ printk(KERN_WARNING "%s: could not get irq: %d\n", dev->name, dev->irq); lmc_trace(dev, "lmc_open irq failed out"); return -EAGAIN; diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c index 58c66819f39b..5394b51bdb2f 100644 --- a/drivers/net/wan/n2.c +++ b/drivers/net/wan/n2.c @@ -376,7 +376,7 @@ static int __init n2_run(unsigned long io, unsigned long irq, } card->io = io; - if (request_irq(irq, &sca_intr, 0, devname, card)) { + if (request_irq(irq, sca_intr, 0, devname, card)) { printk(KERN_ERR "n2: could not allocate IRQ\n"); n2_destroy_card(card); return(-EBUSY); diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c index 79dabc557bd3..aec4d3955420 100644 --- a/drivers/net/wan/pc300_drv.c +++ b/drivers/net/wan/pc300_drv.c @@ -514,8 +514,8 @@ static int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb) RX_BD_ADDR(ch, chan->rx_first_bd)); while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { nchar = cpc_readw(&ptdescr->len); - if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) - || (nchar > BD_DEF_LEN)) { + if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) || + (nchar > BD_DEF_LEN)) { if (nchar > BD_DEF_LEN) status |= DST_RBIT; @@ -1428,8 +1428,7 @@ static void falc_update_stats(pc300_t * card, int ch) if (((conf->media == IF_IFACE_T1) && (cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_LLBAD) && - (!(cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_PDEN))) - || + (!(cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_PDEN))) || ((conf->media == IF_IFACE_E1) && (cpc_readb(falcbase + F_REG(RSP, ch)) & RSP_LLBAD))) { pfalc->prbs = 2; @@ -2285,8 +2284,8 @@ static void falc_e1_intr(pc300_t * card, int ch) if (gis & GIS_ISR1) { isr1 = cpc_readb(falcbase + F_REG(FISR1, ch)); if (isr1 & FISR1_XMB) { - if ((pfalc->xmb_cause & 2) - && pfalc->multiframe_mode) { + if ((pfalc->xmb_cause & 2) && + pfalc->multiframe_mode) { if (cpc_readb (falcbase + F_REG(FRS0, ch)) & (FRS0_LOS | FRS0_AIS | FRS0_LFA)) { cpc_writeb(falcbase + F_REG(XSP, ch), @@ -2639,9 +2638,9 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) !(cpc_readb (scabase + M_REG(CTL, ch)) & CTL_DTR); /* There is no DSR in HD64572 */ } - if (!arg - || copy_to_user(arg, &pc300status, sizeof(pc300status_t))) - return -EINVAL; + if (!arg || + copy_to_user(arg, &pc300status, sizeof(pc300status_t))) + return -EINVAL; return 0; } diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 1cc24a45f003..25477b5cde47 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -195,9 +195,9 @@ static unsigned int netcard_portlist[ ] __initdata = { static inline int __init sbni_isa_probe( struct net_device *dev ) { - if( dev->base_addr > 0x1ff - && request_region( dev->base_addr, SBNI_IO_EXTENT, dev->name ) - && sbni_probe1( dev, dev->base_addr, dev->irq ) ) + if( dev->base_addr > 0x1ff && + request_region( dev->base_addr, SBNI_IO_EXTENT, dev->name ) && + sbni_probe1( dev, dev->base_addr, dev->irq ) ) return 0; else { @@ -286,8 +286,8 @@ static int __init sbni_init(struct net_device *dev) for( i = 0; netcard_portlist[ i ]; ++i ) { int ioaddr = netcard_portlist[ i ]; - if( request_region( ioaddr, SBNI_IO_EXTENT, dev->name ) - && sbni_probe1( dev, ioaddr, 0 )) + if( request_region( ioaddr, SBNI_IO_EXTENT, dev->name ) && + sbni_probe1( dev, ioaddr, 0 )) return 0; } @@ -306,9 +306,9 @@ sbni_pci_probe( struct net_device *dev ) unsigned long pci_ioaddr; u16 subsys; - if( pdev->vendor != SBNI_PCI_VENDOR - && pdev->device != SBNI_PCI_DEVICE ) - continue; + if( pdev->vendor != SBNI_PCI_VENDOR && + pdev->device != SBNI_PCI_DEVICE ) + continue; pci_ioaddr = pci_resource_start( pdev, 0 ); pci_irq_line = pdev->irq; @@ -977,8 +977,8 @@ check_fhdr( u32 ioaddr, u32 *framelen, u32 *frameno, u32 *ack, *ack = *framelen & FRAME_ACK_MASK; *is_first = (*framelen & FRAME_FIRST) != 0; - if( (*framelen &= FRAME_LEN_MASK) < 6 - || *framelen > SBNI_MAX_FRAME - 3 ) + if( (*framelen &= FRAME_LEN_MASK) < 6 || + *framelen > SBNI_MAX_FRAME - 3 ) return 0; value = inb( ioaddr + DAT ); @@ -1173,10 +1173,10 @@ sbni_open( struct net_device *dev ) if( dev->base_addr < 0x400 ) { /* ISA only */ struct net_device **p = sbni_cards; for( ; *p && p < sbni_cards + SBNI_MAX_NUM_CARDS; ++p ) - if( (*p)->irq == dev->irq - && ((*p)->base_addr == dev->base_addr + 4 - || (*p)->base_addr == dev->base_addr - 4) - && (*p)->flags & IFF_UP ) { + if( (*p)->irq == dev->irq && + ((*p)->base_addr == dev->base_addr + 4 || + (*p)->base_addr == dev->base_addr - 4) && + (*p)->flags & IFF_UP ) { ((struct net_local *) (netdev_priv(*p))) ->second = dev; diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c index 2b15a7e40d5b..31c41af2246d 100644 --- a/drivers/net/wan/sdla.c +++ b/drivers/net/wan/sdla.c @@ -1457,7 +1457,7 @@ got_type: } err = -EAGAIN; - if (request_irq(dev->irq, &sdla_isr, 0, dev->name, dev)) + if (request_irq(dev->irq, sdla_isr, 0, dev->name, dev)) goto fail; if (flp->type == SDLA_S507) { diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c index 0c525e24b247..61249f489e37 100644 --- a/drivers/net/wan/sealevel.c +++ b/drivers/net/wan/sealevel.c @@ -84,8 +84,7 @@ static int sealevel_open(struct net_device *d) * Link layer up. */ - switch (unit) - { + switch (unit) { case 0: err = z8530_sync_dma_open(d, slvl->chan); break; @@ -133,8 +132,7 @@ static int sealevel_close(struct net_device *d) hdlc_close(d); netif_stop_queue(d); - switch (unit) - { + switch (unit) { case 0: z8530_sync_dma_close(d, slvl->chan); break; @@ -266,7 +264,7 @@ static __init struct slvl_board *slvl_init(int iobase, int irq, /* We want a fast IRQ for this device. Actually we'd like an even faster IRQ ;) - This is one driver RtLinux is made for */ - if (request_irq(irq, &z8530_interrupt, IRQF_DISABLED, + if (request_irq(irq, z8530_interrupt, IRQF_DISABLED, "SeaLevel", dev) < 0) { printk(KERN_WARNING "sealevel: IRQ %d already in use.\n", irq); goto err_request_irq; @@ -342,8 +340,7 @@ static void __exit slvl_shutdown(struct slvl_board *b) z8530_shutdown(&b->board); - for (u = 0; u < 2; u++) - { + for (u = 0; u < 2; u++) { struct net_device *d = b->dev[u].chan->netdevice; unregister_hdlc_device(d); free_netdev(d); @@ -391,7 +388,7 @@ static int __init slvl_init_module(void) static void __exit slvl_cleanup_module(void) { - if(slvl_unit) + if (slvl_unit) slvl_shutdown(slvl_unit); } diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 27945049c9e1..b9f520b7db6a 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -33,6 +33,7 @@ #include <linux/lapb.h> #include <linux/init.h> #include <linux/rtnetlink.h> +#include <linux/compat.h> #include "x25_asy.h" #include <net/x25device.h> @@ -656,8 +657,8 @@ static void x25_asy_unesc(struct x25_asy *sl, unsigned char s) switch (s) { case X25_END: - if (!test_and_clear_bit(SLF_ERROR, &sl->flags) - && sl->rcount > 2) + if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && + sl->rcount > 2) x25_asy_bump(sl); clear_bit(SLF_ESCAPE, &sl->flags); sl->rcount = 0; @@ -705,6 +706,21 @@ static int x25_asy_ioctl(struct tty_struct *tty, struct file *file, } } +#ifdef CONFIG_COMPAT +static long x25_asy_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCGIFNAME: + case SIOCSIFHWADDR: + return x25_asy_ioctl(tty, file, cmd, + (unsigned long)compat_ptr(arg)); + } + + return -ENOIOCTLCMD; +} +#endif + static int x25_asy_open_dev(struct net_device *dev) { struct x25_asy *sl = netdev_priv(dev); @@ -754,6 +770,9 @@ static struct tty_ldisc_ops x25_ldisc = { .open = x25_asy_open_tty, .close = x25_asy_close_tty, .ioctl = x25_asy_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = x25_asy_compat_ioctl, +#endif .receive_buf = x25_asy_receive_buf, .write_wakeup = x25_asy_write_wakeup, }; diff --git a/drivers/net/wimax/i2400m/Kconfig b/drivers/net/wimax/i2400m/Kconfig index d623b3d99a4b..3f703384295e 100644 --- a/drivers/net/wimax/i2400m/Kconfig +++ b/drivers/net/wimax/i2400m/Kconfig @@ -31,6 +31,14 @@ config WIMAX_I2400M_SDIO If unsure, it is safe to select M (module). +config WIMAX_IWMC3200_SDIO + bool "Intel Wireless Multicom WiMAX Connection 3200 over SDIO" + depends on WIMAX_I2400M_SDIO + select IWMC3200TOP + help + Select if you have a device based on the Intel Multicom WiMAX + Connection 3200 over SDIO. + config WIMAX_I2400M_DEBUG_LEVEL int "WiMAX i2400m debug level" depends on WIMAX_I2400M diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c index 07308686dbcf..944945540391 100644 --- a/drivers/net/wimax/i2400m/control.c +++ b/drivers/net/wimax/i2400m/control.c @@ -54,7 +54,7 @@ * i2400m_set_init_config() * i2400m_cmd_get_state() * i2400m_dev_shutdown() Called by i2400m_dev_stop() - * i2400m->bus_reset() + * i2400m_reset() * * i2400m_{cmd,get,set}_*() * i2400m_msg_to_dev() @@ -82,6 +82,13 @@ #define D_SUBMODULE control #include "debug-levels.h" +int i2400m_passive_mode; /* 0 (passive mode disabled) by default */ +module_param_named(passive_mode, i2400m_passive_mode, int, 0644); +MODULE_PARM_DESC(passive_mode, + "If true, the driver will not do any device setup " + "and leave it up to user space, who must be properly " + "setup."); + /* * Return if a TLV is of a give type and size @@ -263,7 +270,7 @@ int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *l3l4_hdr, if (status == 0) return 0; - if (status > ARRAY_SIZE(ms_to_errno)) { + if (status >= ARRAY_SIZE(ms_to_errno)) { str = "unknown status code"; result = -EBADR; } else { @@ -336,7 +343,7 @@ void i2400m_report_tlv_system_state(struct i2400m *i2400m, /* Huh? just in case, shut it down */ dev_err(dev, "HW BUG? unknown state %u: shutting down\n", i2400m_state); - i2400m->bus_reset(i2400m, I2400M_RT_WARM); + i2400m_reset(i2400m, I2400M_RT_WARM); break; }; d_fnend(3, dev, "(i2400m %p ss %p [%u]) = void\n", @@ -1335,6 +1342,8 @@ int i2400m_dev_initialize(struct i2400m *i2400m) unsigned argc = 0; d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + if (i2400m_passive_mode) + goto out_passive; /* Disable idle mode? (enabled by default) */ if (i2400m_idle_mode_disabled) { if (i2400m_le_v1_3(i2400m)) { @@ -1377,6 +1386,7 @@ int i2400m_dev_initialize(struct i2400m *i2400m) result = i2400m_set_init_config(i2400m, args, argc); if (result < 0) goto error; +out_passive: /* * Update state: Here it just calls a get state; parsing the * result (System State TLV and RF Status TLV [done in the rx diff --git a/drivers/net/wimax/i2400m/debugfs.c b/drivers/net/wimax/i2400m/debugfs.c index 9b81af3f80a9..b1aec3e1892f 100644 --- a/drivers/net/wimax/i2400m/debugfs.c +++ b/drivers/net/wimax/i2400m/debugfs.c @@ -214,7 +214,7 @@ int debugfs_i2400m_reset_set(void *data, u64 val) case I2400M_RT_WARM: case I2400M_RT_COLD: case I2400M_RT_BUS: - result = i2400m->bus_reset(i2400m, rt); + result = i2400m_reset(i2400m, rt); if (result >= 0) result = 0; default: diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 304f0443ca4b..96a615fe09de 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c @@ -41,8 +41,10 @@ * __i2400m_dev_start() * * i2400m_setup() + * i2400m->bus_setup() * i2400m_bootrom_init() * register_netdev() + * wimax_dev_add() * i2400m_dev_start() * __i2400m_dev_start() * i2400m_dev_bootstrap() @@ -50,15 +52,15 @@ * i2400m->bus_dev_start() * i2400m_firmware_check() * i2400m_check_mac_addr() - * wimax_dev_add() * * i2400m_release() - * wimax_dev_rm() * i2400m_dev_stop() * __i2400m_dev_stop() * i2400m_dev_shutdown() * i2400m->bus_dev_stop() * i2400m_tx_release() + * i2400m->bus_release() + * wimax_dev_rm() * unregister_netdev() */ #include "i2400m.h" @@ -66,6 +68,7 @@ #include <linux/wimax/i2400m.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/suspend.h> #define D_SUBMODULE driver #include "debug-levels.h" @@ -90,76 +93,39 @@ MODULE_PARM_DESC(power_save_disabled, "False by default (so the device is told to do power " "saving)."); -/** - * i2400m_queue_work - schedule work on a i2400m's queue - * - * @i2400m: device descriptor - * - * @fn: function to run to execute work. It gets passed a 'struct - * work_struct' that is wrapped in a 'struct i2400m_work'. Once - * done, you have to (1) i2400m_put(i2400m_work->i2400m) and then - * (2) kfree(i2400m_work). - * - * @gfp_flags: GFP flags for memory allocation. - * - * @pl: pointer to a payload buffer that you want to pass to the _work - * function. Use this to pack (for example) a struct with extra - * arguments. - * - * @pl_size: size of the payload buffer. - * - * We do this quite often, so this just saves typing; allocate a - * wrapper for a i2400m, get a ref to it, pack arguments and launch - * the work. - * - * A usual workflow is: - * - * struct my_work_args { - * void *something; - * int whatever; - * }; - * ... - * - * struct my_work_args my_args = { - * .something = FOO, - * .whaetever = BLAH - * }; - * i2400m_queue_work(i2400m, 1, my_work_function, GFP_KERNEL, - * &args, sizeof(args)) - * - * And now the work function can unpack the arguments and call the - * real function (or do the job itself): - * - * static - * void my_work_fn((struct work_struct *ws) - * { - * struct i2400m_work *iw = - * container_of(ws, struct i2400m_work, ws); - * struct my_work_args *my_args = (void *) iw->pl; - * - * my_work(iw->i2400m, my_args->something, my_args->whatevert); - * } - */ -int i2400m_queue_work(struct i2400m *i2400m, - void (*fn)(struct work_struct *), gfp_t gfp_flags, - const void *pl, size_t pl_size) +static char i2400m_debug_params[128]; +module_param_string(debug, i2400m_debug_params, sizeof(i2400m_debug_params), + 0644); +MODULE_PARM_DESC(debug, + "String of space-separated NAME:VALUE pairs, where NAMEs " + "are the different debug submodules and VALUE are the " + "initial debug value to set."); + +static char i2400m_barkers_params[128]; +module_param_string(barkers, i2400m_barkers_params, + sizeof(i2400m_barkers_params), 0644); +MODULE_PARM_DESC(barkers, + "String of comma-separated 32-bit values; each is " + "recognized as the value the device sends as a reboot " + "signal; values are appended to a list--setting one value " + "as zero cleans the existing list and starts a new one."); + +static +struct i2400m_work *__i2400m_work_setup( + struct i2400m *i2400m, void (*fn)(struct work_struct *), + gfp_t gfp_flags, const void *pl, size_t pl_size) { - int result; struct i2400m_work *iw; - BUG_ON(i2400m->work_queue == NULL); - result = -ENOMEM; iw = kzalloc(sizeof(*iw) + pl_size, gfp_flags); if (iw == NULL) - goto error_kzalloc; + return NULL; iw->i2400m = i2400m_get(i2400m); + iw->pl_size = pl_size; memcpy(iw->pl, pl, pl_size); INIT_WORK(&iw->ws, fn); - result = queue_work(i2400m->work_queue, &iw->ws); -error_kzalloc: - return result; + return iw; } -EXPORT_SYMBOL_GPL(i2400m_queue_work); /* @@ -175,21 +141,19 @@ EXPORT_SYMBOL_GPL(i2400m_queue_work); * it should not happen. */ int i2400m_schedule_work(struct i2400m *i2400m, - void (*fn)(struct work_struct *), gfp_t gfp_flags) + void (*fn)(struct work_struct *), gfp_t gfp_flags, + const void *pl, size_t pl_size) { int result; struct i2400m_work *iw; result = -ENOMEM; - iw = kzalloc(sizeof(*iw), gfp_flags); - if (iw == NULL) - goto error_kzalloc; - iw->i2400m = i2400m_get(i2400m); - INIT_WORK(&iw->ws, fn); - result = schedule_work(&iw->ws); - if (result == 0) - result = -ENXIO; -error_kzalloc: + iw = __i2400m_work_setup(i2400m, fn, gfp_flags, pl, pl_size); + if (iw != NULL) { + result = schedule_work(&iw->ws); + if (WARN_ON(result == 0)) + result = -ENXIO; + } return result; } @@ -291,7 +255,7 @@ int i2400m_op_reset(struct wimax_dev *wimax_dev) mutex_lock(&i2400m->init_mutex); i2400m->reset_ctx = &ctx; mutex_unlock(&i2400m->init_mutex); - result = i2400m->bus_reset(i2400m, I2400M_RT_WARM); + result = i2400m_reset(i2400m, I2400M_RT_WARM); if (result < 0) goto out; result = wait_for_completion_timeout(&ctx.completion, 4*HZ); @@ -420,9 +384,15 @@ retry: dev_err(dev, "cannot create workqueue\n"); goto error_create_workqueue; } - result = i2400m->bus_dev_start(i2400m); - if (result < 0) - goto error_bus_dev_start; + if (i2400m->bus_dev_start) { + result = i2400m->bus_dev_start(i2400m); + if (result < 0) + goto error_bus_dev_start; + } + i2400m->ready = 1; + wmb(); /* see i2400m->ready's documentation */ + /* process pending reports from the device */ + queue_work(i2400m->work_queue, &i2400m->rx_report_ws); result = i2400m_firmware_check(i2400m); /* fw versions ok? */ if (result < 0) goto error_fw_check; @@ -430,8 +400,6 @@ retry: result = i2400m_check_mac_addr(i2400m); if (result < 0) goto error_check_mac_addr; - i2400m->ready = 1; - wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); result = i2400m_dev_initialize(i2400m); if (result < 0) goto error_dev_initialize; @@ -443,8 +411,12 @@ retry: error_dev_initialize: error_check_mac_addr: + i2400m->ready = 0; + wmb(); /* see i2400m->ready's documentation */ + flush_workqueue(i2400m->work_queue); error_fw_check: - i2400m->bus_dev_stop(i2400m); + if (i2400m->bus_dev_stop) + i2400m->bus_dev_stop(i2400m); error_bus_dev_start: destroy_workqueue(i2400m->work_queue); error_create_workqueue: @@ -466,11 +438,15 @@ error_bootstrap: static int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) { - int result; + int result = 0; mutex_lock(&i2400m->init_mutex); /* Well, start the device */ - result = __i2400m_dev_start(i2400m, bm_flags); - if (result >= 0) - i2400m->updown = 1; + if (i2400m->updown == 0) { + result = __i2400m_dev_start(i2400m, bm_flags); + if (result >= 0) { + i2400m->updown = 1; + wmb(); /* see i2400m->updown's documentation */ + } + } mutex_unlock(&i2400m->init_mutex); return result; } @@ -495,9 +471,20 @@ void __i2400m_dev_stop(struct i2400m *i2400m) d_fnstart(3, dev, "(i2400m %p)\n", i2400m); wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); + i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST); + complete(&i2400m->msg_completion); + i2400m_net_wake_stop(i2400m); i2400m_dev_shutdown(i2400m); - i2400m->ready = 0; - i2400m->bus_dev_stop(i2400m); + /* + * Make sure no report hooks are running *before* we stop the + * communication infrastructure with the device. + */ + i2400m->ready = 0; /* nobody can queue work anymore */ + wmb(); /* see i2400m->ready's documentation */ + flush_workqueue(i2400m->work_queue); + + if (i2400m->bus_dev_stop) + i2400m->bus_dev_stop(i2400m); destroy_workqueue(i2400m->work_queue); i2400m_rx_release(i2400m); i2400m_tx_release(i2400m); @@ -518,12 +505,139 @@ void i2400m_dev_stop(struct i2400m *i2400m) if (i2400m->updown) { __i2400m_dev_stop(i2400m); i2400m->updown = 0; + wmb(); /* see i2400m->updown's documentation */ } mutex_unlock(&i2400m->init_mutex); } /* + * Listen to PM events to cache the firmware before suspend/hibernation + * + * When the device comes out of suspend, it might go into reset and + * firmware has to be uploaded again. At resume, most of the times, we + * can't load firmware images from disk, so we need to cache it. + * + * i2400m_fw_cache() will allocate a kobject and attach the firmware + * to it; that way we don't have to worry too much about the fw loader + * hitting a race condition. + * + * Note: modus operandi stolen from the Orinoco driver; thx. + */ +static +int i2400m_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, + void *unused) +{ + struct i2400m *i2400m = + container_of(notifier, struct i2400m, pm_notifier); + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p pm_event %lx)\n", i2400m, pm_event); + switch (pm_event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + i2400m_fw_cache(i2400m); + break; + case PM_POST_RESTORE: + /* Restore from hibernation failed. We need to clean + * up in exactly the same way, so fall through. */ + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + i2400m_fw_uncache(i2400m); + break; + + case PM_RESTORE_PREPARE: + default: + break; + } + d_fnend(3, dev, "(i2400m %p pm_event %lx) = void\n", i2400m, pm_event); + return NOTIFY_DONE; +} + + +/* + * pre-reset is called before a device is going on reset + * + * This has to be followed by a call to i2400m_post_reset(), otherwise + * bad things might happen. + */ +int i2400m_pre_reset(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + d_printf(1, dev, "pre-reset shut down\n"); + + result = 0; + mutex_lock(&i2400m->init_mutex); + if (i2400m->updown) { + netif_tx_disable(i2400m->wimax_dev.net_dev); + __i2400m_dev_stop(i2400m); + result = 0; + /* down't set updown to zero -- this way + * post_reset can restore properly */ + } + mutex_unlock(&i2400m->init_mutex); + if (i2400m->bus_release) + i2400m->bus_release(i2400m); + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; +} +EXPORT_SYMBOL_GPL(i2400m_pre_reset); + + +/* + * Restore device state after a reset + * + * Do the work needed after a device reset to bring it up to the same + * state as it was before the reset. + * + * NOTE: this requires i2400m->init_mutex taken + */ +int i2400m_post_reset(struct i2400m *i2400m) +{ + int result = 0; + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + d_printf(1, dev, "post-reset start\n"); + if (i2400m->bus_setup) { + result = i2400m->bus_setup(i2400m); + if (result < 0) { + dev_err(dev, "bus-specific setup failed: %d\n", + result); + goto error_bus_setup; + } + } + mutex_lock(&i2400m->init_mutex); + if (i2400m->updown) { + result = __i2400m_dev_start( + i2400m, I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); + if (result < 0) + goto error_dev_start; + } + mutex_unlock(&i2400m->init_mutex); + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; + +error_dev_start: + if (i2400m->bus_release) + i2400m->bus_release(i2400m); +error_bus_setup: + /* even if the device was up, it could not be recovered, so we + * mark it as down. */ + i2400m->updown = 0; + wmb(); /* see i2400m->updown's documentation */ + mutex_unlock(&i2400m->init_mutex); + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; +} +EXPORT_SYMBOL_GPL(i2400m_post_reset); + + +/* * The device has rebooted; fix up the device and the driver * * Tear down the driver communication with the device, reload the @@ -542,56 +656,69 @@ void i2400m_dev_stop(struct i2400m *i2400m) * _stop()], don't do anything, let it fail and handle it. * * This function is ran always in a thread context + * + * This function gets passed, as payload to i2400m_work() a 'const + * char *' ptr with a "reason" why the reset happened (for messages). */ static void __i2400m_dev_reset_handle(struct work_struct *ws) { int result; struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws); + const char *reason; struct i2400m *i2400m = iw->i2400m; struct device *dev = i2400m_dev(i2400m); - enum wimax_st wimax_state; struct i2400m_reset_ctx *ctx = i2400m->reset_ctx; - d_fnstart(3, dev, "(ws %p i2400m %p)\n", ws, i2400m); + if (WARN_ON(iw->pl_size != sizeof(reason))) + reason = "SW BUG: reason n/a"; + else + memcpy(&reason, iw->pl, sizeof(reason)); + + d_fnstart(3, dev, "(ws %p i2400m %p reason %s)\n", ws, i2400m, reason); + result = 0; if (mutex_trylock(&i2400m->init_mutex) == 0) { /* We are still in i2400m_dev_start() [let it fail] or * i2400m_dev_stop() [we are shutting down anyway, so * ignore it] or we are resetting somewhere else. */ - dev_err(dev, "device rebooted\n"); + dev_err(dev, "device rebooted somewhere else?\n"); i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST); complete(&i2400m->msg_completion); goto out; } - wimax_state = wimax_state_get(&i2400m->wimax_dev); - if (wimax_state < WIMAX_ST_UNINITIALIZED) { - dev_info(dev, "device rebooted: it is down, ignoring\n"); - goto out_unlock; /* ifconfig up/down wasn't called */ + if (i2400m->updown == 0) { + dev_info(dev, "%s: device is down, doing nothing\n", reason); + goto out_unlock; } - dev_err(dev, "device rebooted: reinitializing driver\n"); + dev_err(dev, "%s: reinitializing driver\n", reason); __i2400m_dev_stop(i2400m); - i2400m->updown = 0; result = __i2400m_dev_start(i2400m, I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); if (result < 0) { - dev_err(dev, "device reboot: cannot start the device: %d\n", - result); - result = i2400m->bus_reset(i2400m, I2400M_RT_BUS); - if (result >= 0) - result = -ENODEV; - } else - i2400m->updown = 1; + i2400m->updown = 0; + wmb(); /* see i2400m->updown's documentation */ + dev_err(dev, "%s: cannot start the device: %d\n", + reason, result); + result = -EUCLEAN; + } out_unlock: if (i2400m->reset_ctx) { ctx->result = result; complete(&ctx->completion); } mutex_unlock(&i2400m->init_mutex); + if (result == -EUCLEAN) { + /* ops, need to clean up [w/ init_mutex not held] */ + result = i2400m_reset(i2400m, I2400M_RT_BUS); + if (result >= 0) + result = -ENODEV; + } out: i2400m_put(i2400m); kfree(iw); - d_fnend(3, dev, "(ws %p i2400m %p) = void\n", ws, i2400m); + d_fnend(3, dev, "(ws %p i2400m %p reason %s) = void\n", + ws, i2400m, reason); return; } @@ -608,16 +735,104 @@ out: * reinitializing the driver to handle the reset, calling into the * bus-specific functions ops as needed. */ -int i2400m_dev_reset_handle(struct i2400m *i2400m) +int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason) { i2400m->boot_mode = 1; wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */ return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle, - GFP_ATOMIC); + GFP_ATOMIC, &reason, sizeof(reason)); } EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); +/* + * Alloc the command and ack buffers for boot mode + * + * Get the buffers needed to deal with boot mode messages. These + * buffers need to be allocated before the sdio recieve irq is setup. + */ +static +int i2400m_bm_buf_alloc(struct i2400m *i2400m) +{ + int result; + + result = -ENOMEM; + i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL); + if (i2400m->bm_cmd_buf == NULL) + goto error_bm_cmd_kzalloc; + i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL); + if (i2400m->bm_ack_buf == NULL) + goto error_bm_ack_buf_kzalloc; + return 0; + +error_bm_ack_buf_kzalloc: + kfree(i2400m->bm_cmd_buf); +error_bm_cmd_kzalloc: + return result; +} + + +/* + * Free boot mode command and ack buffers. + */ +static +void i2400m_bm_buf_free(struct i2400m *i2400m) +{ + kfree(i2400m->bm_ack_buf); + kfree(i2400m->bm_cmd_buf); +} + + +/** + * i2400m_init - Initialize a 'struct i2400m' from all zeroes + * + * This is a bus-generic API call. + */ +void i2400m_init(struct i2400m *i2400m) +{ + wimax_dev_init(&i2400m->wimax_dev); + + i2400m->boot_mode = 1; + i2400m->rx_reorder = 1; + init_waitqueue_head(&i2400m->state_wq); + + spin_lock_init(&i2400m->tx_lock); + i2400m->tx_pl_min = UINT_MAX; + i2400m->tx_size_min = UINT_MAX; + + spin_lock_init(&i2400m->rx_lock); + i2400m->rx_pl_min = UINT_MAX; + i2400m->rx_size_min = UINT_MAX; + INIT_LIST_HEAD(&i2400m->rx_reports); + INIT_WORK(&i2400m->rx_report_ws, i2400m_report_hook_work); + + mutex_init(&i2400m->msg_mutex); + init_completion(&i2400m->msg_completion); + + mutex_init(&i2400m->init_mutex); + /* wake_tx_ws is initialized in i2400m_tx_setup() */ +} +EXPORT_SYMBOL_GPL(i2400m_init); + + +int i2400m_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) +{ + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + + /* + * Make sure we stop TXs and down the carrier before + * resetting; this is needed to avoid things like + * i2400m_wake_tx() scheduling stuff in parallel. + */ + if (net_dev->reg_state == NETREG_REGISTERED) { + netif_tx_disable(net_dev); + netif_carrier_off(net_dev); + } + return i2400m->bus_reset(i2400m, rt); +} +EXPORT_SYMBOL_GPL(i2400m_reset); + + /** * i2400m_setup - bus-generic setup function for the i2400m device * @@ -625,13 +840,9 @@ EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); * * Returns: 0 if ok, < 0 errno code on error. * - * Initializes the bus-generic parts of the i2400m driver; the - * bus-specific parts have been initialized, function pointers filled - * out by the bus-specific probe function. - * - * As well, this registers the WiMAX and net device nodes. Once this - * function returns, the device is operative and has to be ready to - * receive and send network traffic and WiMAX control operations. + * Sets up basic device comunication infrastructure, boots the ROM to + * read the MAC address, registers with the WiMAX and network stacks + * and then brings up the device. */ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) { @@ -645,16 +856,21 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) snprintf(wimax_dev->name, sizeof(wimax_dev->name), "i2400m-%s:%s", dev->bus->name, dev_name(dev)); - i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL); - if (i2400m->bm_cmd_buf == NULL) { - dev_err(dev, "cannot allocate USB command buffer\n"); - goto error_bm_cmd_kzalloc; + result = i2400m_bm_buf_alloc(i2400m); + if (result < 0) { + dev_err(dev, "cannot allocate bootmode scratch buffers\n"); + goto error_bm_buf_alloc; } - i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL); - if (i2400m->bm_ack_buf == NULL) { - dev_err(dev, "cannot allocate USB ack buffer\n"); - goto error_bm_ack_buf_kzalloc; + + if (i2400m->bus_setup) { + result = i2400m->bus_setup(i2400m); + if (result < 0) { + dev_err(dev, "bus-specific setup failed: %d\n", + result); + goto error_bus_setup; + } } + result = i2400m_bootrom_init(i2400m, bm_flags); if (result < 0) { dev_err(dev, "read mac addr: bootrom init " @@ -666,6 +882,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) goto error_read_mac_addr; random_ether_addr(i2400m->src_mac_addr); + i2400m->pm_notifier.notifier_call = i2400m_pm_notifier; + register_pm_notifier(&i2400m->pm_notifier); + result = register_netdev(net_dev); /* Okey dokey, bring it up */ if (result < 0) { dev_err(dev, "cannot register i2400m network device: %d\n", @@ -674,18 +893,13 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) } netif_carrier_off(net_dev); - result = i2400m_dev_start(i2400m, bm_flags); - if (result < 0) - goto error_dev_start; - i2400m->wimax_dev.op_msg_from_user = i2400m_op_msg_from_user; i2400m->wimax_dev.op_rfkill_sw_toggle = i2400m_op_rfkill_sw_toggle; i2400m->wimax_dev.op_reset = i2400m_op_reset; + result = wimax_dev_add(&i2400m->wimax_dev, net_dev); if (result < 0) goto error_wimax_dev_add; - /* User space needs to do some init stuff */ - wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); /* Now setup all that requires a registered net and wimax device. */ result = sysfs_create_group(&net_dev->dev.kobj, &i2400m_dev_attr_group); @@ -693,30 +907,37 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) dev_err(dev, "cannot setup i2400m's sysfs: %d\n", result); goto error_sysfs_setup; } + result = i2400m_debugfs_add(i2400m); if (result < 0) { dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result); goto error_debugfs_setup; } + + result = i2400m_dev_start(i2400m, bm_flags); + if (result < 0) + goto error_dev_start; d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); return result; +error_dev_start: + i2400m_debugfs_rm(i2400m); error_debugfs_setup: sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj, &i2400m_dev_attr_group); error_sysfs_setup: wimax_dev_rm(&i2400m->wimax_dev); error_wimax_dev_add: - i2400m_dev_stop(i2400m); -error_dev_start: unregister_netdev(net_dev); error_register_netdev: + unregister_pm_notifier(&i2400m->pm_notifier); error_read_mac_addr: error_bootrom_init: - kfree(i2400m->bm_ack_buf); -error_bm_ack_buf_kzalloc: - kfree(i2400m->bm_cmd_buf); -error_bm_cmd_kzalloc: + if (i2400m->bus_release) + i2400m->bus_release(i2400m); +error_bus_setup: + i2400m_bm_buf_free(i2400m); +error_bm_buf_alloc: d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); return result; } @@ -735,14 +956,17 @@ void i2400m_release(struct i2400m *i2400m) d_fnstart(3, dev, "(i2400m %p)\n", i2400m); netif_stop_queue(i2400m->wimax_dev.net_dev); + i2400m_dev_stop(i2400m); + i2400m_debugfs_rm(i2400m); sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj, &i2400m_dev_attr_group); wimax_dev_rm(&i2400m->wimax_dev); - i2400m_dev_stop(i2400m); unregister_netdev(i2400m->wimax_dev.net_dev); - kfree(i2400m->bm_ack_buf); - kfree(i2400m->bm_cmd_buf); + unregister_pm_notifier(&i2400m->pm_notifier); + if (i2400m->bus_release) + i2400m->bus_release(i2400m); + i2400m_bm_buf_free(i2400m); d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); } EXPORT_SYMBOL_GPL(i2400m_release); @@ -759,6 +983,7 @@ struct d_level D_LEVEL[] = { D_SUBMODULE_DEFINE(netdev), D_SUBMODULE_DEFINE(rfkill), D_SUBMODULE_DEFINE(rx), + D_SUBMODULE_DEFINE(sysfs), D_SUBMODULE_DEFINE(tx), }; size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); @@ -767,7 +992,9 @@ size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); static int __init i2400m_driver_init(void) { - return 0; + d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400m_debug_params, + "i2400m.debug"); + return i2400m_barker_db_init(i2400m_barkers_params); } module_init(i2400m_driver_init); @@ -776,6 +1003,7 @@ void __exit i2400m_driver_exit(void) { /* for scheds i2400m_dev_reset_handle() */ flush_scheduled_work(); + i2400m_barker_db_exit(); return; } module_exit(i2400m_driver_exit); diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c index e81750e54452..64cdfeb299ca 100644 --- a/drivers/net/wimax/i2400m/fw.c +++ b/drivers/net/wimax/i2400m/fw.c @@ -40,11 +40,9 @@ * * THE PROCEDURE * - * (this is decribed for USB, but for SDIO is similar) - * - * The 2400m works in two modes: boot-mode or normal mode. In boot - * mode we can execute only a handful of commands targeted at - * uploading the firmware and launching it. + * The 2400m and derived devices work in two modes: boot-mode or + * normal mode. In boot mode we can execute only a handful of commands + * targeted at uploading the firmware and launching it. * * The 2400m enters boot mode when it is first connected to the * system, when it crashes and when you ask it to reboot. There are @@ -52,18 +50,26 @@ * firmwares signed with a certain private key, non-signed takes any * firmware. Normal hardware takes only signed firmware. * - * Upon entrance to boot mode, the device sends a few zero length - * packets (ZLPs) on the notification endpoint, then a reboot barker - * (4 le32 words with value I2400M_{S,N}BOOT_BARKER). We ack it by - * sending the same barker on the bulk out endpoint. The device acks - * with a reboot ack barker (4 le32 words with value 0xfeedbabe) and - * then the device is fully rebooted. At this point we can upload the - * firmware. + * On boot mode, in USB, we write to the device using the bulk out + * endpoint and read from it in the notification endpoint. In SDIO we + * talk to it via the write address and read from the read address. + * + * Upon entrance to boot mode, the device sends (preceeded with a few + * zero length packets (ZLPs) on the notification endpoint in USB) a + * reboot barker (4 le32 words with the same value). We ack it by + * sending the same barker to the device. The device acks with a + * reboot ack barker (4 le32 words with value I2400M_ACK_BARKER) and + * then is fully booted. At this point we can upload the firmware. + * + * Note that different iterations of the device and EEPROM + * configurations will send different [re]boot barkers; these are + * collected in i2400m_barker_db along with the firmware + * characteristics they require. * * This process is accomplished by the i2400m_bootrom_init() * function. All the device interaction happens through the * i2400m_bm_cmd() [boot mode command]. Special return values will - * indicate if the device resets. + * indicate if the device did reset during the process. * * After this, we read the MAC address and then (if needed) * reinitialize the device. We need to read it ahead of time because @@ -72,11 +78,11 @@ * * We can then upload the firmware file. The file is composed of a BCF * header (basic data, keys and signatures) and a list of write - * commands and payloads. We first upload the header - * [i2400m_dnload_init()] and then pass the commands and payloads - * verbatim to the i2400m_bm_cmd() function - * [i2400m_dnload_bcf()]. Then we tell the device to jump to the new - * firmware [i2400m_dnload_finalize()]. + * commands and payloads. Optionally more BCF headers might follow the + * main payload. We first upload the header [i2400m_dnload_init()] and + * then pass the commands and payloads verbatim to the i2400m_bm_cmd() + * function [i2400m_dnload_bcf()]. Then we tell the device to jump to + * the new firmware [i2400m_dnload_finalize()]. * * Once firmware is uploaded, we are good to go :) * @@ -99,18 +105,32 @@ * read an acknolwedgement from it (or an asynchronous notification) * from it. * + * FIRMWARE LOADING + * + * Note that in some cases, we can't just load a firmware file (for + * example, when resuming). For that, we might cache the firmware + * file. Thus, when doing the bootstrap, if there is a cache firmware + * file, it is used; if not, loading from disk is attempted. + * * ROADMAP * + * i2400m_barker_db_init Called by i2400m_driver_init() + * i2400m_barker_db_add + * + * i2400m_barker_db_exit Called by i2400m_driver_exit() + * * i2400m_dev_bootstrap Called by __i2400m_dev_start() * request_firmware - * i2400m_fw_check - * i2400m_fw_dnload + * i2400m_fw_bootstrap + * i2400m_fw_check + * i2400m_fw_hdr_check + * i2400m_fw_dnload * release_firmware * * i2400m_fw_dnload * i2400m_bootrom_init * i2400m_bm_cmd - * i2400m->bus_reset + * i2400m_reset * i2400m_dnload_init * i2400m_dnload_init_signed * i2400m_dnload_init_nonsigned @@ -125,9 +145,14 @@ * i2400m->bus_bm_cmd_send() * i2400m->bus_bm_wait_for_ack * __i2400m_bm_ack_verify + * i2400m_is_boot_barker * * i2400m_bm_cmd_prepare Used by bus-drivers to prep * commands before sending + * + * i2400m_pm_notifier Called on Power Management events + * i2400m_fw_cache + * i2400m_fw_uncache */ #include <linux/firmware.h> #include <linux/sched.h> @@ -175,6 +200,240 @@ EXPORT_SYMBOL_GPL(i2400m_bm_cmd_prepare); /* + * Database of known barkers. + * + * A barker is what the device sends indicating he is ready to be + * bootloaded. Different versions of the device will send different + * barkers. Depending on the barker, it might mean the device wants + * some kind of firmware or the other. + */ +static struct i2400m_barker_db { + __le32 data[4]; +} *i2400m_barker_db; +static size_t i2400m_barker_db_used, i2400m_barker_db_size; + + +static +int i2400m_zrealloc_2x(void **ptr, size_t *_count, size_t el_size, + gfp_t gfp_flags) +{ + size_t old_count = *_count, + new_count = old_count ? 2 * old_count : 2, + old_size = el_size * old_count, + new_size = el_size * new_count; + void *nptr = krealloc(*ptr, new_size, gfp_flags); + if (nptr) { + /* zero the other half or the whole thing if old_count + * was zero */ + if (old_size == 0) + memset(nptr, 0, new_size); + else + memset(nptr + old_size, 0, old_size); + *_count = new_count; + *ptr = nptr; + return 0; + } else + return -ENOMEM; +} + + +/* + * Add a barker to the database + * + * This cannot used outside of this module and only at at module_init + * time. This is to avoid the need to do locking. + */ +static +int i2400m_barker_db_add(u32 barker_id) +{ + int result; + + struct i2400m_barker_db *barker; + if (i2400m_barker_db_used >= i2400m_barker_db_size) { + result = i2400m_zrealloc_2x( + (void **) &i2400m_barker_db, &i2400m_barker_db_size, + sizeof(i2400m_barker_db[0]), GFP_KERNEL); + if (result < 0) + return result; + } + barker = i2400m_barker_db + i2400m_barker_db_used++; + barker->data[0] = le32_to_cpu(barker_id); + barker->data[1] = le32_to_cpu(barker_id); + barker->data[2] = le32_to_cpu(barker_id); + barker->data[3] = le32_to_cpu(barker_id); + return 0; +} + + +void i2400m_barker_db_exit(void) +{ + kfree(i2400m_barker_db); + i2400m_barker_db = NULL; + i2400m_barker_db_size = 0; + i2400m_barker_db_used = 0; +} + + +/* + * Helper function to add all the known stable barkers to the barker + * database. + */ +static +int i2400m_barker_db_known_barkers(void) +{ + int result; + + result = i2400m_barker_db_add(I2400M_NBOOT_BARKER); + if (result < 0) + goto error_add; + result = i2400m_barker_db_add(I2400M_SBOOT_BARKER); + if (result < 0) + goto error_add; + result = i2400m_barker_db_add(I2400M_SBOOT_BARKER_6050); + if (result < 0) + goto error_add; +error_add: + return result; +} + + +/* + * Initialize the barker database + * + * This can only be used from the module_init function for this + * module; this is to avoid the need to do locking. + * + * @options: command line argument with extra barkers to + * recognize. This is a comma-separated list of 32-bit hex + * numbers. They are appended to the existing list. Setting 0 + * cleans the existing list and starts a new one. + */ +int i2400m_barker_db_init(const char *_options) +{ + int result; + char *options = NULL, *options_orig, *token; + + i2400m_barker_db = NULL; + i2400m_barker_db_size = 0; + i2400m_barker_db_used = 0; + + result = i2400m_barker_db_known_barkers(); + if (result < 0) + goto error_add; + /* parse command line options from i2400m.barkers */ + if (_options != NULL) { + unsigned barker; + + options_orig = kstrdup(_options, GFP_KERNEL); + if (options_orig == NULL) + goto error_parse; + options = options_orig; + + while ((token = strsep(&options, ",")) != NULL) { + if (*token == '\0') /* eat joint commas */ + continue; + if (sscanf(token, "%x", &barker) != 1 + || barker > 0xffffffff) { + printk(KERN_ERR "%s: can't recognize " + "i2400m.barkers value '%s' as " + "a 32-bit number\n", + __func__, token); + result = -EINVAL; + goto error_parse; + } + if (barker == 0) { + /* clean list and start new */ + i2400m_barker_db_exit(); + continue; + } + result = i2400m_barker_db_add(barker); + if (result < 0) + goto error_add; + } + kfree(options_orig); + } + return 0; + +error_parse: +error_add: + kfree(i2400m_barker_db); + return result; +} + + +/* + * Recognize a boot barker + * + * @buf: buffer where the boot barker. + * @buf_size: size of the buffer (has to be 16 bytes). It is passed + * here so the function can check it for the caller. + * + * Note that as a side effect, upon identifying the obtained boot + * barker, this function will set i2400m->barker to point to the right + * barker database entry. Subsequent calls to the function will result + * in verifying that the same type of boot barker is returned when the + * device [re]boots (as long as the same device instance is used). + * + * Return: 0 if @buf matches a known boot barker. -ENOENT if the + * buffer in @buf doesn't match any boot barker in the database or + * -EILSEQ if the buffer doesn't have the right size. + */ +int i2400m_is_boot_barker(struct i2400m *i2400m, + const void *buf, size_t buf_size) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct i2400m_barker_db *barker; + int i; + + result = -ENOENT; + if (buf_size != sizeof(i2400m_barker_db[i].data)) + return result; + + /* Short circuit if we have already discovered the barker + * associated with the device. */ + if (i2400m->barker + && !memcmp(buf, i2400m->barker, sizeof(i2400m->barker->data))) { + unsigned index = (i2400m->barker - i2400m_barker_db) + / sizeof(*i2400m->barker); + d_printf(2, dev, "boot barker cache-confirmed #%u/%08x\n", + index, le32_to_cpu(i2400m->barker->data[0])); + return 0; + } + + for (i = 0; i < i2400m_barker_db_used; i++) { + barker = &i2400m_barker_db[i]; + BUILD_BUG_ON(sizeof(barker->data) != 16); + if (memcmp(buf, barker->data, sizeof(barker->data))) + continue; + + if (i2400m->barker == NULL) { + i2400m->barker = barker; + d_printf(1, dev, "boot barker set to #%u/%08x\n", + i, le32_to_cpu(barker->data[0])); + if (barker->data[0] == le32_to_cpu(I2400M_NBOOT_BARKER)) + i2400m->sboot = 0; + else + i2400m->sboot = 1; + } else if (i2400m->barker != barker) { + dev_err(dev, "HW inconsistency: device " + "reports a different boot barker " + "than set (from %08x to %08x)\n", + le32_to_cpu(i2400m->barker->data[0]), + le32_to_cpu(barker->data[0])); + result = -EIO; + } else + d_printf(2, dev, "boot barker confirmed #%u/%08x\n", + i, le32_to_cpu(barker->data[0])); + result = 0; + break; + } + return result; +} +EXPORT_SYMBOL_GPL(i2400m_is_boot_barker); + + +/* * Verify the ack data received * * Given a reply to a boot mode command, chew it and verify everything @@ -204,20 +463,10 @@ ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode, opcode, ack_size, sizeof(*ack)); goto error_ack_short; } - if (ack_size == sizeof(i2400m_NBOOT_BARKER) - && memcmp(ack, i2400m_NBOOT_BARKER, sizeof(*ack)) == 0) { + result = i2400m_is_boot_barker(i2400m, ack, ack_size); + if (result >= 0) { result = -ERESTARTSYS; - i2400m->sboot = 0; - d_printf(6, dev, "boot-mode cmd %d: " - "HW non-signed boot barker\n", opcode); - goto error_reboot; - } - if (ack_size == sizeof(i2400m_SBOOT_BARKER) - && memcmp(ack, i2400m_SBOOT_BARKER, sizeof(*ack)) == 0) { - result = -ERESTARTSYS; - i2400m->sboot = 1; - d_printf(6, dev, "boot-mode cmd %d: HW signed reboot barker\n", - opcode); + d_printf(6, dev, "boot-mode cmd %d: HW boot barker\n", opcode); goto error_reboot; } if (ack_size == sizeof(i2400m_ACK_BARKER) @@ -343,7 +592,6 @@ ssize_t i2400m_bm_cmd(struct i2400m *i2400m, BUG_ON(i2400m->boot_mode == 0); if (cmd != NULL) { /* send the command */ - memcpy(i2400m->bm_cmd_buf, cmd, cmd_size); result = i2400m->bus_bm_cmd_send(i2400m, cmd, cmd_size, flags); if (result < 0) goto error_cmd_send; @@ -432,8 +680,8 @@ static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk, * Download a BCF file's sections to the device * * @i2400m: device descriptor - * @bcf: pointer to firmware data (followed by the payloads). Assumed - * verified and consistent. + * @bcf: pointer to firmware data (first header followed by the + * payloads). Assumed verified and consistent. * @bcf_len: length (in bytes) of the @bcf buffer. * * Returns: < 0 errno code on error or the offset to the jump instruction. @@ -472,14 +720,17 @@ ssize_t i2400m_dnload_bcf(struct i2400m *i2400m, "downloading section #%zu (@%zu %zu B) to 0x%08x\n", section, offset, sizeof(*bh) + data_size, le32_to_cpu(bh->target_addr)); - if (i2400m_brh_get_opcode(bh) == I2400M_BRH_SIGNED_JUMP) { - /* Secure boot needs to stop here */ - d_printf(5, dev, "signed jump found @%zu\n", offset); + /* + * We look for JUMP cmd from the bootmode header, + * either I2400M_BRH_SIGNED_JUMP for secure boot + * or I2400M_BRH_JUMP for unsecure boot, the last chunk + * should be the bootmode header with JUMP cmd. + */ + if (i2400m_brh_get_opcode(bh) == I2400M_BRH_SIGNED_JUMP || + i2400m_brh_get_opcode(bh) == I2400M_BRH_JUMP) { + d_printf(5, dev, "jump found @%zu\n", offset); break; } - if (offset + section_size == bcf_len) - /* Non-secure boot stops here */ - break; if (offset + section_size > bcf_len) { dev_err(dev, "fw %s: bad section #%zu, " "end (@%zu) beyond EOF (@%zu)\n", @@ -510,13 +761,30 @@ error_send: /* + * Indicate if the device emitted a reboot barker that indicates + * "signed boot" + */ +static +unsigned i2400m_boot_is_signed(struct i2400m *i2400m) +{ + return likely(i2400m->sboot); +} + + +/* * Do the final steps of uploading firmware * + * @bcf_hdr: BCF header we are actually using + * @bcf: pointer to the firmware image (which matches the first header + * that is followed by the actual payloads). + * @offset: [byte] offset into @bcf for the command we need to send. + * * Depending on the boot mode (signed vs non-signed), different * actions need to be taken. */ static int i2400m_dnload_finalize(struct i2400m *i2400m, + const struct i2400m_bcf_hdr *bcf_hdr, const struct i2400m_bcf_hdr *bcf, size_t offset) { int ret = 0; @@ -530,10 +798,14 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, d_fnstart(3, dev, "offset %zu\n", offset); cmd = (void *) bcf + offset; - if (i2400m->sboot == 0) { + if (i2400m_boot_is_signed(i2400m) == 0) { struct i2400m_bootrom_header jump_ack; d_printf(1, dev, "unsecure boot, jumping to 0x%08x\n", le32_to_cpu(cmd->target_addr)); + cmd_buf = i2400m->bm_cmd_buf; + memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); + cmd = &cmd_buf->cmd; + /* now cmd points to the actual bootrom_header in cmd_buf */ i2400m_brh_set_opcode(cmd, I2400M_BRH_JUMP); cmd->data_size = 0; ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), @@ -544,12 +816,13 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, cmd_buf = i2400m->bm_cmd_buf; memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); signature_block_offset = - sizeof(*bcf) - + le32_to_cpu(bcf->key_size) * sizeof(u32) - + le32_to_cpu(bcf->exponent_size) * sizeof(u32); + sizeof(*bcf_hdr) + + le32_to_cpu(bcf_hdr->key_size) * sizeof(u32) + + le32_to_cpu(bcf_hdr->exponent_size) * sizeof(u32); signature_block_size = - le32_to_cpu(bcf->modulus_size) * sizeof(u32); - memcpy(cmd_buf->cmd_pl, (void *) bcf + signature_block_offset, + le32_to_cpu(bcf_hdr->modulus_size) * sizeof(u32); + memcpy(cmd_buf->cmd_pl, + (void *) bcf_hdr + signature_block_offset, signature_block_size); ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, sizeof(cmd_buf->cmd) + signature_block_size, @@ -565,7 +838,7 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, * * @i2400m: device descriptor * @flags: - * I2400M_BRI_SOFT: a reboot notification has been seen + * I2400M_BRI_SOFT: a reboot barker has been seen * already, so don't wait for it. * * I2400M_BRI_NO_REBOOT: Don't send a reboot command, but wait @@ -576,17 +849,15 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, * * < 0 errno code on error, 0 if ok. * - * i2400m->sboot set to 0 for unsecure boot process, 1 for secure - * boot process. - * * Description: * * Tries hard enough to put the device in boot-mode. There are two * main phases to this: * * a. (1) send a reboot command and (2) get a reboot barker - * b. (1) ack the reboot sending a reboot barker and (2) getting an - * ack barker in return + * + * b. (1) echo/ack the reboot sending the reboot barker back and (2) + * getting an ack barker in return * * We want to skip (a) in some cases [soft]. The state machine is * horrible, but it is basically: on each phase, send what has to be @@ -594,6 +865,16 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, * have to backtrack and retry, so we keep a max tries counter for * that. * + * It sucks because we don't know ahead of time which is going to be + * the reboot barker (the device might send different ones depending + * on its EEPROM config) and once the device reboots and waits for the + * echo/ack reboot barker being sent back, it doesn't understand + * anything else. So we can be left at the point where we don't know + * what to send to it -- cold reset and bus reset seem to have little + * effect. So the function iterates (in this case) through all the + * known barkers and tries them all until an ACK is + * received. Otherwise, it gives up. + * * If we get a timeout after sending a warm reset, we do it again. */ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags) @@ -602,10 +883,11 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags) struct device *dev = i2400m_dev(i2400m); struct i2400m_bootrom_header *cmd; struct i2400m_bootrom_header ack; - int count = I2400M_BOOT_RETRIES; + int count = i2400m->bus_bm_retries; int ack_timeout_cnt = 1; + unsigned i; - BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_NBOOT_BARKER)); + BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_barker_db[0].data)); BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER)); d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags); @@ -614,27 +896,59 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags) if (flags & I2400M_BRI_SOFT) goto do_reboot_ack; do_reboot: + ack_timeout_cnt = 1; if (--count < 0) goto error_timeout; d_printf(4, dev, "device reboot: reboot command [%d # left]\n", count); if ((flags & I2400M_BRI_NO_REBOOT) == 0) - i2400m->bus_reset(i2400m, I2400M_RT_WARM); + i2400m_reset(i2400m, I2400M_RT_WARM); result = i2400m_bm_cmd(i2400m, NULL, 0, &ack, sizeof(ack), I2400M_BM_CMD_RAW); flags &= ~I2400M_BRI_NO_REBOOT; switch (result) { case -ERESTARTSYS: + /* + * at this point, i2400m_bm_cmd(), through + * __i2400m_bm_ack_process(), has updated + * i2400m->barker and we are good to go. + */ d_printf(4, dev, "device reboot: got reboot barker\n"); break; case -EISCONN: /* we don't know how it got here...but we follow it */ d_printf(4, dev, "device reboot: got ack barker - whatever\n"); goto do_reboot; - case -ETIMEDOUT: /* device has timed out, we might be in boot - * mode already and expecting an ack, let's try - * that */ - dev_info(dev, "warm reset timed out, trying an ack\n"); - goto do_reboot_ack; + case -ETIMEDOUT: + /* + * Device has timed out, we might be in boot mode + * already and expecting an ack; if we don't know what + * the barker is, we just send them all. Cold reset + * and bus reset don't work. Beats me. + */ + if (i2400m->barker != NULL) { + dev_err(dev, "device boot: reboot barker timed out, " + "trying (set) %08x echo/ack\n", + le32_to_cpu(i2400m->barker->data[0])); + goto do_reboot_ack; + } + for (i = 0; i < i2400m_barker_db_used; i++) { + struct i2400m_barker_db *barker = &i2400m_barker_db[i]; + memcpy(cmd, barker->data, sizeof(barker->data)); + result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), + &ack, sizeof(ack), + I2400M_BM_CMD_RAW); + if (result == -EISCONN) { + dev_warn(dev, "device boot: got ack barker " + "after sending echo/ack barker " + "#%d/%08x; rebooting j.i.c.\n", + i, le32_to_cpu(barker->data[0])); + flags &= ~I2400M_BRI_NO_REBOOT; + goto do_reboot; + } + } + dev_err(dev, "device boot: tried all the echo/acks, could " + "not get device to respond; giving up"); + result = -ESHUTDOWN; case -EPROTO: case -ESHUTDOWN: /* dev is gone */ case -EINTR: /* user cancelled */ @@ -642,6 +956,7 @@ do_reboot: default: dev_err(dev, "device reboot: error %d while waiting " "for reboot barker - rebooting\n", result); + d_dump(1, dev, &ack, result); goto do_reboot; } /* At this point we ack back with 4 REBOOT barkers and expect @@ -650,12 +965,7 @@ do_reboot: * notification and report it as -EISCONN. */ do_reboot_ack: d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count); - if (i2400m->sboot == 0) - memcpy(cmd, i2400m_NBOOT_BARKER, - sizeof(i2400m_NBOOT_BARKER)); - else - memcpy(cmd, i2400m_SBOOT_BARKER, - sizeof(i2400m_SBOOT_BARKER)); + memcpy(cmd, i2400m->barker->data, sizeof(i2400m->barker->data)); result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), &ack, sizeof(ack), I2400M_BM_CMD_RAW); switch (result) { @@ -668,10 +978,8 @@ do_reboot_ack: d_printf(4, dev, "reboot ack: got ack barker - good\n"); break; case -ETIMEDOUT: /* no response, maybe it is the other type? */ - if (ack_timeout_cnt-- >= 0) { - d_printf(4, dev, "reboot ack timedout: " - "trying the other type?\n"); - i2400m->sboot = !i2400m->sboot; + if (ack_timeout_cnt-- < 0) { + d_printf(4, dev, "reboot ack timedout: retrying\n"); goto do_reboot_ack; } else { dev_err(dev, "reboot ack timedout too long: " @@ -839,32 +1147,29 @@ int i2400m_dnload_init_signed(struct i2400m *i2400m, * (signed or non-signed). */ static -int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf) +int i2400m_dnload_init(struct i2400m *i2400m, + const struct i2400m_bcf_hdr *bcf_hdr) { int result; struct device *dev = i2400m_dev(i2400m); - u32 module_id = le32_to_cpu(bcf->module_id); - if (i2400m->sboot == 0 - && (module_id & I2400M_BCF_MOD_ID_POKES) == 0) { - /* non-signed boot process without pokes */ - result = i2400m_dnload_init_nonsigned(i2400m); + if (i2400m_boot_is_signed(i2400m)) { + d_printf(1, dev, "signed boot\n"); + result = i2400m_dnload_init_signed(i2400m, bcf_hdr); if (result == -ERESTARTSYS) return result; if (result < 0) - dev_err(dev, "fw %s: non-signed download " + dev_err(dev, "firmware %s: signed boot download " "initialization failed: %d\n", i2400m->fw_name, result); - } else if (i2400m->sboot == 0 - && (module_id & I2400M_BCF_MOD_ID_POKES)) { - /* non-signed boot process with pokes, nothing to do */ - result = 0; - } else { /* signed boot process */ - result = i2400m_dnload_init_signed(i2400m, bcf); + } else { + /* non-signed boot process without pokes */ + d_printf(1, dev, "non-signed boot\n"); + result = i2400m_dnload_init_nonsigned(i2400m); if (result == -ERESTARTSYS) return result; if (result < 0) - dev_err(dev, "fw %s: signed boot download " + dev_err(dev, "firmware %s: non-signed download " "initialization failed: %d\n", i2400m->fw_name, result); } @@ -873,74 +1178,201 @@ int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf) /* - * Run quick consistency tests on the firmware file + * Run consistency tests on the firmware file and load up headers * * Check for the firmware being made for the i2400m device, * etc...These checks are mostly informative, as the device will make * them too; but the driver's response is more informative on what * went wrong. + * + * This will also look at all the headers present on the firmware + * file, and update i2400m->fw_bcf_hdr to point to them. */ static -int i2400m_fw_check(struct i2400m *i2400m, - const struct i2400m_bcf_hdr *bcf, - size_t bcf_size) +int i2400m_fw_hdr_check(struct i2400m *i2400m, + const struct i2400m_bcf_hdr *bcf_hdr, + size_t index, size_t offset) { - int result; struct device *dev = i2400m_dev(i2400m); + unsigned module_type, header_len, major_version, minor_version, module_id, module_vendor, date, size; - /* Check hard errors */ - result = -EINVAL; - if (bcf_size < sizeof(*bcf)) { /* big enough header? */ - dev_err(dev, "firmware %s too short: " - "%zu B vs %zu (at least) expected\n", - i2400m->fw_name, bcf_size, sizeof(*bcf)); - goto error; - } + module_type = bcf_hdr->module_type; + header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len); + major_version = (le32_to_cpu(bcf_hdr->header_version) & 0xffff0000) + >> 16; + minor_version = le32_to_cpu(bcf_hdr->header_version) & 0x0000ffff; + module_id = le32_to_cpu(bcf_hdr->module_id); + module_vendor = le32_to_cpu(bcf_hdr->module_vendor); + date = le32_to_cpu(bcf_hdr->date); + size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); - module_type = bcf->module_type; - header_len = sizeof(u32) * le32_to_cpu(bcf->header_len); - major_version = le32_to_cpu(bcf->header_version) & 0xffff0000 >> 16; - minor_version = le32_to_cpu(bcf->header_version) & 0x0000ffff; - module_id = le32_to_cpu(bcf->module_id); - module_vendor = le32_to_cpu(bcf->module_vendor); - date = le32_to_cpu(bcf->date); - size = sizeof(u32) * le32_to_cpu(bcf->size); - - if (bcf_size != size) { /* annoyingly paranoid */ - dev_err(dev, "firmware %s: bad size, got " - "%zu B vs %u expected\n", - i2400m->fw_name, bcf_size, size); - goto error; - } + d_printf(1, dev, "firmware %s #%zd@%08zx: BCF header " + "type:vendor:id 0x%x:%x:%x v%u.%u (%u/%u B) built %08x\n", + i2400m->fw_name, index, offset, + module_type, module_vendor, module_id, + major_version, minor_version, header_len, size, date); - d_printf(2, dev, "type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) " - "date %08x (%zu B)\n", - module_type, module_id, module_vendor, - major_version, minor_version, (size_t) header_len, - date, (size_t) size); + /* Hard errors */ + if (major_version != 1) { + dev_err(dev, "firmware %s #%zd@%08zx: major header version " + "v%u.%u not supported\n", + i2400m->fw_name, index, offset, + major_version, minor_version); + return -EBADF; + } if (module_type != 6) { /* built for the right hardware? */ - dev_err(dev, "bad fw %s: unexpected module type 0x%x; " - "aborting\n", i2400m->fw_name, module_type); - goto error; + dev_err(dev, "firmware %s #%zd@%08zx: unexpected module " + "type 0x%x; aborting\n", + i2400m->fw_name, index, offset, + module_type); + return -EBADF; + } + + if (module_vendor != 0x8086) { + dev_err(dev, "firmware %s #%zd@%08zx: unexpected module " + "vendor 0x%x; aborting\n", + i2400m->fw_name, index, offset, module_vendor); + return -EBADF; } - /* Check soft-er errors */ - result = 0; - if (module_vendor != 0x8086) - dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n", - i2400m->fw_name, module_vendor); if (date < 0x20080300) - dev_err(dev, "bad fw %s? build date too old %08x\n", - i2400m->fw_name, date); -error: + dev_warn(dev, "firmware %s #%zd@%08zx: build date %08x " + "too old; unsupported\n", + i2400m->fw_name, index, offset, date); + return 0; +} + + +/* + * Run consistency tests on the firmware file and load up headers + * + * Check for the firmware being made for the i2400m device, + * etc...These checks are mostly informative, as the device will make + * them too; but the driver's response is more informative on what + * went wrong. + * + * This will also look at all the headers present on the firmware + * file, and update i2400m->fw_hdrs to point to them. + */ +static +int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + size_t headers = 0; + const struct i2400m_bcf_hdr *bcf_hdr; + const void *itr, *next, *top; + size_t slots = 0, used_slots = 0; + + for (itr = bcf, top = itr + bcf_size; + itr < top; + headers++, itr = next) { + size_t leftover, offset, header_len, size; + + leftover = top - itr; + offset = itr - (const void *) bcf; + if (leftover <= sizeof(*bcf_hdr)) { + dev_err(dev, "firmware %s: %zu B left at @%zx, " + "not enough for BCF header\n", + i2400m->fw_name, leftover, offset); + break; + } + bcf_hdr = itr; + /* Only the first header is supposed to be followed by + * payload */ + header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len); + size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); + if (headers == 0) + next = itr + size; + else + next = itr + header_len; + + result = i2400m_fw_hdr_check(i2400m, bcf_hdr, headers, offset); + if (result < 0) + continue; + if (used_slots + 1 >= slots) { + /* +1 -> we need to account for the one we'll + * occupy and at least an extra one for + * always being NULL */ + result = i2400m_zrealloc_2x( + (void **) &i2400m->fw_hdrs, &slots, + sizeof(i2400m->fw_hdrs[0]), + GFP_KERNEL); + if (result < 0) + goto error_zrealloc; + } + i2400m->fw_hdrs[used_slots] = bcf_hdr; + used_slots++; + } + if (headers == 0) { + dev_err(dev, "firmware %s: no usable headers found\n", + i2400m->fw_name); + result = -EBADF; + } else + result = 0; +error_zrealloc: return result; } /* + * Match a barker to a BCF header module ID + * + * The device sends a barker which tells the firmware loader which + * header in the BCF file has to be used. This does the matching. + */ +static +unsigned i2400m_bcf_hdr_match(struct i2400m *i2400m, + const struct i2400m_bcf_hdr *bcf_hdr) +{ + u32 barker = le32_to_cpu(i2400m->barker->data[0]) + & 0x7fffffff; + u32 module_id = le32_to_cpu(bcf_hdr->module_id) + & 0x7fffffff; /* high bit used for something else */ + + /* special case for 5x50 */ + if (barker == I2400M_SBOOT_BARKER && module_id == 0) + return 1; + if (module_id == barker) + return 1; + return 0; +} + +static +const struct i2400m_bcf_hdr *i2400m_bcf_hdr_find(struct i2400m *i2400m) +{ + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_bcf_hdr **bcf_itr, *bcf_hdr; + unsigned i = 0; + u32 barker = le32_to_cpu(i2400m->barker->data[0]); + + d_printf(2, dev, "finding BCF header for barker %08x\n", barker); + if (barker == I2400M_NBOOT_BARKER) { + bcf_hdr = i2400m->fw_hdrs[0]; + d_printf(1, dev, "using BCF header #%u/%08x for non-signed " + "barker\n", 0, le32_to_cpu(bcf_hdr->module_id)); + return bcf_hdr; + } + for (bcf_itr = i2400m->fw_hdrs; *bcf_itr != NULL; bcf_itr++, i++) { + bcf_hdr = *bcf_itr; + if (i2400m_bcf_hdr_match(i2400m, bcf_hdr)) { + d_printf(1, dev, "hit on BCF hdr #%u/%08x\n", + i, le32_to_cpu(bcf_hdr->module_id)); + return bcf_hdr; + } else + d_printf(1, dev, "miss on BCF hdr #%u/%08x\n", + i, le32_to_cpu(bcf_hdr->module_id)); + } + dev_err(dev, "cannot find a matching BCF header for barker %08x\n", + barker); + return NULL; +} + + +/* * Download the firmware to the device * * @i2400m: device descriptor @@ -956,14 +1388,16 @@ error: */ static int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf, - size_t bcf_size, enum i2400m_bri flags) + size_t fw_size, enum i2400m_bri flags) { int ret = 0; struct device *dev = i2400m_dev(i2400m); int count = i2400m->bus_bm_retries; + const struct i2400m_bcf_hdr *bcf_hdr; + size_t bcf_size; - d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n", - i2400m, bcf, bcf_size); + d_fnstart(5, dev, "(i2400m %p bcf %p fw size %zu)\n", + i2400m, bcf, fw_size); i2400m->boot_mode = 1; wmb(); /* Make sure other readers see it */ hw_reboot: @@ -985,13 +1419,28 @@ hw_reboot: * Initialize the download, push the bytes to the device and * then jump to the new firmware. Note @ret is passed with the * offset of the jump instruction to _dnload_finalize() + * + * Note we need to use the BCF header in the firmware image + * that matches the barker that the device sent when it + * rebooted, so it has to be passed along. */ - ret = i2400m_dnload_init(i2400m, bcf); /* Init device's dnload */ + ret = -EBADF; + bcf_hdr = i2400m_bcf_hdr_find(i2400m); + if (bcf_hdr == NULL) + goto error_bcf_hdr_find; + + ret = i2400m_dnload_init(i2400m, bcf_hdr); if (ret == -ERESTARTSYS) goto error_dev_rebooted; if (ret < 0) goto error_dnload_init; + /* + * bcf_size refers to one header size plus the fw sections size + * indicated by the header,ie. if there are other extended headers + * at the tail, they are not counted + */ + bcf_size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); ret = i2400m_dnload_bcf(i2400m, bcf, bcf_size); if (ret == -ERESTARTSYS) goto error_dev_rebooted; @@ -1001,7 +1450,7 @@ hw_reboot: goto error_dnload_bcf; } - ret = i2400m_dnload_finalize(i2400m, bcf, ret); + ret = i2400m_dnload_finalize(i2400m, bcf_hdr, bcf, ret); if (ret == -ERESTARTSYS) goto error_dev_rebooted; if (ret < 0) { @@ -1018,10 +1467,11 @@ hw_reboot: error_dnload_finalize: error_dnload_bcf: error_dnload_init: +error_bcf_hdr_find: error_bootrom_init: error_too_many_reboots: d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n", - i2400m, bcf, bcf_size, ret); + i2400m, bcf, fw_size, ret); return ret; error_dev_rebooted: @@ -1031,6 +1481,61 @@ error_dev_rebooted: goto hw_reboot; } +static +int i2400m_fw_bootstrap(struct i2400m *i2400m, const struct firmware *fw, + enum i2400m_bri flags) +{ + int ret; + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_bcf_hdr *bcf; /* Firmware data */ + + d_fnstart(5, dev, "(i2400m %p)\n", i2400m); + bcf = (void *) fw->data; + ret = i2400m_fw_check(i2400m, bcf, fw->size); + if (ret >= 0) + ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); + if (ret < 0) + dev_err(dev, "%s: cannot use: %d, skipping\n", + i2400m->fw_name, ret); + kfree(i2400m->fw_hdrs); + i2400m->fw_hdrs = NULL; + d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); + return ret; +} + + +/* Refcounted container for firmware data */ +struct i2400m_fw { + struct kref kref; + const struct firmware *fw; +}; + + +static +void i2400m_fw_destroy(struct kref *kref) +{ + struct i2400m_fw *i2400m_fw = + container_of(kref, struct i2400m_fw, kref); + release_firmware(i2400m_fw->fw); + kfree(i2400m_fw); +} + + +static +struct i2400m_fw *i2400m_fw_get(struct i2400m_fw *i2400m_fw) +{ + if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) + kref_get(&i2400m_fw->kref); + return i2400m_fw; +} + + +static +void i2400m_fw_put(struct i2400m_fw *i2400m_fw) +{ + kref_put(&i2400m_fw->kref, i2400m_fw_destroy); +} + /** * i2400m_dev_bootstrap - Bring the device to a known state and upload firmware @@ -1049,42 +1554,109 @@ error_dev_rebooted: */ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) { - int ret = 0, itr = 0; + int ret, itr; struct device *dev = i2400m_dev(i2400m); - const struct firmware *fw; + struct i2400m_fw *i2400m_fw; const struct i2400m_bcf_hdr *bcf; /* Firmware data */ + const struct firmware *fw; const char *fw_name; d_fnstart(5, dev, "(i2400m %p)\n", i2400m); + ret = -ENODEV; + spin_lock(&i2400m->rx_lock); + i2400m_fw = i2400m_fw_get(i2400m->fw_cached); + spin_unlock(&i2400m->rx_lock); + if (i2400m_fw == (void *) ~0) { + dev_err(dev, "can't load firmware now!"); + goto out; + } else if (i2400m_fw != NULL) { + dev_info(dev, "firmware %s: loading from cache\n", + i2400m->fw_name); + ret = i2400m_fw_bootstrap(i2400m, i2400m_fw->fw, flags); + i2400m_fw_put(i2400m_fw); + goto out; + } + /* Load firmware files to memory. */ - itr = 0; - while(1) { + for (itr = 0, bcf = NULL, ret = -ENOENT; ; itr++) { fw_name = i2400m->bus_fw_names[itr]; if (fw_name == NULL) { dev_err(dev, "Could not find a usable firmware image\n"); - ret = -ENOENT; - goto error_no_fw; + break; } + d_printf(1, dev, "trying firmware %s (%d)\n", fw_name, itr); ret = request_firmware(&fw, fw_name, dev); - if (ret == 0) - break; /* got it */ - if (ret < 0) + if (ret < 0) { dev_err(dev, "fw %s: cannot load file: %d\n", fw_name, ret); - itr++; + continue; + } + i2400m->fw_name = fw_name; + ret = i2400m_fw_bootstrap(i2400m, fw, flags); + release_firmware(fw); + if (ret >= 0) /* firmware loaded succesfully */ + break; + i2400m->fw_name = NULL; } - - bcf = (void *) fw->data; - i2400m->fw_name = fw_name; - ret = i2400m_fw_check(i2400m, bcf, fw->size); - if (ret < 0) - goto error_fw_bad; - ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); -error_fw_bad: - release_firmware(fw); -error_no_fw: +out: d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); return ret; } EXPORT_SYMBOL_GPL(i2400m_dev_bootstrap); + + +void i2400m_fw_cache(struct i2400m *i2400m) +{ + int result; + struct i2400m_fw *i2400m_fw; + struct device *dev = i2400m_dev(i2400m); + + /* if there is anything there, free it -- now, this'd be weird */ + spin_lock(&i2400m->rx_lock); + i2400m_fw = i2400m->fw_cached; + spin_unlock(&i2400m->rx_lock); + if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) { + i2400m_fw_put(i2400m_fw); + WARN(1, "%s:%u: still cached fw still present?\n", + __func__, __LINE__); + } + + if (i2400m->fw_name == NULL) { + dev_err(dev, "firmware n/a: can't cache\n"); + i2400m_fw = (void *) ~0; + goto out; + } + + i2400m_fw = kzalloc(sizeof(*i2400m_fw), GFP_ATOMIC); + if (i2400m_fw == NULL) + goto out; + kref_init(&i2400m_fw->kref); + result = request_firmware(&i2400m_fw->fw, i2400m->fw_name, dev); + if (result < 0) { + dev_err(dev, "firmware %s: failed to cache: %d\n", + i2400m->fw_name, result); + kfree(i2400m_fw); + i2400m_fw = (void *) ~0; + } else + dev_info(dev, "firmware %s: cached\n", i2400m->fw_name); +out: + spin_lock(&i2400m->rx_lock); + i2400m->fw_cached = i2400m_fw; + spin_unlock(&i2400m->rx_lock); +} + + +void i2400m_fw_uncache(struct i2400m *i2400m) +{ + struct i2400m_fw *i2400m_fw; + + spin_lock(&i2400m->rx_lock); + i2400m_fw = i2400m->fw_cached; + i2400m->fw_cached = NULL; + spin_unlock(&i2400m->rx_lock); + + if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) + i2400m_fw_put(i2400m_fw); +} + diff --git a/drivers/net/wimax/i2400m/i2400m-sdio.h b/drivers/net/wimax/i2400m/i2400m-sdio.h index 9c4e3189f7b5..b9c4bed3b457 100644 --- a/drivers/net/wimax/i2400m/i2400m-sdio.h +++ b/drivers/net/wimax/i2400m/i2400m-sdio.h @@ -67,6 +67,7 @@ /* Host-Device interface for SDIO */ enum { + I2400M_SDIO_BOOT_RETRIES = 3, I2400MS_BLK_SIZE = 256, I2400MS_PL_SIZE_MAX = 0x3E00, @@ -77,9 +78,11 @@ enum { I2400MS_INTR_GET_SIZE_ADDR = 0x2C, /* The number of ticks to wait for the device to signal that * it is ready */ - I2400MS_INIT_SLEEP_INTERVAL = 10, + I2400MS_INIT_SLEEP_INTERVAL = 100, /* How long to wait for the device to settle after reset */ I2400MS_SETTLE_TIME = 40, + /* The number of msec to wait for IOR after sending IOE */ + IWMC3200_IOR_TIMEOUT = 10, }; @@ -97,6 +100,14 @@ enum { * @tx_workqueue: workqeueue used for data TX; we don't use the * system's workqueue as that might cause deadlocks with code in * the bus-generic driver. + * + * @debugfs_dentry: dentry for the SDIO specific debugfs files + * + * Note this value is set to NULL upon destruction; this is + * because some routinges use it to determine if we are inside the + * probe() path or some other path. When debugfs is disabled, + * creation sets the dentry to '(void*) -ENODEV', which is valid + * for the test. */ struct i2400ms { struct i2400m i2400m; /* FIRST! See doc */ @@ -111,6 +122,9 @@ struct i2400ms { wait_queue_head_t bm_wfa_wq; int bm_wait_result; size_t bm_ack_size; + + /* Device is any of the iwmc3200 SKUs */ + unsigned iwmc3200:1; }; diff --git a/drivers/net/wimax/i2400m/i2400m-usb.h b/drivers/net/wimax/i2400m/i2400m-usb.h index 6f76558b170f..5cc0f279417e 100644 --- a/drivers/net/wimax/i2400m/i2400m-usb.h +++ b/drivers/net/wimax/i2400m/i2400m-usb.h @@ -88,6 +88,13 @@ struct edc { u16 errorcount; }; +struct i2400m_endpoint_cfg { + unsigned char bulk_out; + unsigned char notification; + unsigned char reset_cold; + unsigned char bulk_in; +}; + static inline void edc_init(struct edc *edc) { edc->timestart = jiffies; @@ -137,15 +144,13 @@ static inline int edc_inc(struct edc *edc, u16 max_err, u16 timeframe) /* Host-Device interface for USB */ enum { + I2400M_USB_BOOT_RETRIES = 3, I2400MU_MAX_NOTIFICATION_LEN = 256, I2400MU_BLK_SIZE = 16, I2400MU_PL_SIZE_MAX = 0x3EFF, - /* Endpoints */ - I2400MU_EP_BULK_OUT = 0, - I2400MU_EP_NOTIFICATION, - I2400MU_EP_RESET_COLD, - I2400MU_EP_BULK_IN, + /* Device IDs */ + USB_DEVICE_ID_I6050 = 0x0186, }; @@ -215,6 +220,7 @@ struct i2400mu { struct usb_device *usb_dev; struct usb_interface *usb_iface; struct edc urb_edc; /* Error density counter */ + struct i2400m_endpoint_cfg endpoint_cfg; struct urb *notif_urb; struct task_struct *tx_kthread; diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 60330f313f27..04df9bbe340f 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -117,16 +117,30 @@ * well as i2400m->wimax_dev.net_dev and call i2400m_setup(). The * i2400m driver will only register with the WiMAX and network stacks; * the only access done to the device is to read the MAC address so we - * can register a network device. This calls i2400m_dev_start() to - * load firmware, setup communication with the device and configure it - * for operation. + * can register a network device. * - * At this point, control and data communications are possible. + * The high-level call flow is: + * + * bus_probe() + * i2400m_setup() + * i2400m->bus_setup() + * boot rom initialization / read mac addr + * network / WiMAX stacks registration + * i2400m_dev_start() + * i2400m->bus_dev_start() + * i2400m_dev_initialize() * - * On disconnect/driver unload, the bus-specific disconnect function - * calls i2400m_release() to undo i2400m_setup(). i2400m_dev_stop() - * shuts the firmware down and releases resources uses to communicate - * with the device. + * The reverse applies for a disconnect() call: + * + * bus_disconnect() + * i2400m_release() + * i2400m_dev_stop() + * i2400m_dev_shutdown() + * i2400m->bus_dev_stop() + * network / WiMAX stack unregistration + * i2400m->bus_release() + * + * At this point, control and data communications are possible. * * While the device is up, it might reset. The bus-specific driver has * to catch that situation and call i2400m_dev_reset_handle() to deal @@ -148,9 +162,6 @@ /* Misc constants */ enum { - /* Firmware uploading */ - I2400M_BOOT_RETRIES = 3, - I3200_BOOT_RETRIES = 3, /* Size of the Boot Mode Command buffer */ I2400M_BM_CMD_BUF_SIZE = 16 * 1024, I2400M_BM_ACK_BUF_SIZE = 256, @@ -197,6 +208,7 @@ enum i2400m_reset_type { struct i2400m_reset_ctx; struct i2400m_roq; +struct i2400m_barker_db; /** * struct i2400m - descriptor for an Intel 2400m @@ -204,27 +216,50 @@ struct i2400m_roq; * Members marked with [fill] must be filled out/initialized before * calling i2400m_setup(). * + * Note the @bus_setup/@bus_release, @bus_dev_start/@bus_dev_release + * call pairs are very much doing almost the same, and depending on + * the underlying bus, some stuff has to be put in one or the + * other. The idea of setup/release is that they setup the minimal + * amount needed for loading firmware, where us dev_start/stop setup + * the rest needed to do full data/control traffic. + * * @bus_tx_block_size: [fill] SDIO imposes a 256 block size, USB 16, * so we have a tx_blk_size variable that the bus layer sets to * tell the engine how much of that we need. * * @bus_pl_size_max: [fill] Maximum payload size. * - * @bus_dev_start: [fill] Function called by the bus-generic code - * [i2400m_dev_start()] to setup the bus-specific communications - * to the the device. See LIFE CYCLE above. + * @bus_setup: [optional fill] Function called by the bus-generic code + * [i2400m_setup()] to setup the basic bus-specific communications + * to the the device needed to load firmware. See LIFE CYCLE above. * * NOTE: Doesn't need to upload the firmware, as that is taken * care of by the bus-generic code. * - * @bus_dev_stop: [fill] Function called by the bus-generic code - * [i2400m_dev_stop()] to shutdown the bus-specific communications - * to the the device. See LIFE CYCLE above. + * @bus_release: [optional fill] Function called by the bus-generic + * code [i2400m_release()] to shutdown the basic bus-specific + * communications to the the device needed to load firmware. See + * LIFE CYCLE above. * * This function does not need to reset the device, just tear down * all the host resources created to handle communication with * the device. * + * @bus_dev_start: [optional fill] Function called by the bus-generic + * code [i2400m_dev_start()] to do things needed to start the + * device. See LIFE CYCLE above. + * + * NOTE: Doesn't need to upload the firmware, as that is taken + * care of by the bus-generic code. + * + * @bus_dev_stop: [optional fill] Function called by the bus-generic + * code [i2400m_dev_stop()] to do things needed for stopping the + * device. See LIFE CYCLE above. + * + * This function does not need to reset the device, just tear down + * all the host resources created to handle communication with + * the device. + * * @bus_tx_kick: [fill] Function called by the bus-generic code to let * the bus-specific code know that there is data available in the * TX FIFO for transmission to the device. @@ -246,6 +281,9 @@ struct i2400m_roq; * process, so it cannot rely on common infrastructure being laid * out. * + * IMPORTANT: don't call reset on RT_BUS with i2400m->init_mutex + * held, as the .pre/.post reset handlers will deadlock. + * * @bus_bm_retries: [fill] How many times shall a firmware upload / * device initialization be retried? Different models of the same * device might need different values, hence it is set by the @@ -297,6 +335,27 @@ struct i2400m_roq; * force this to be the first field so that we can get from * netdev_priv() the right pointer. * + * @updown: the device is up and ready for transmitting control and + * data packets. This implies @ready (communication infrastructure + * with the device is ready) and the device's firmware has been + * loaded and the device initialized. + * + * Write to it only inside a i2400m->init_mutex protected area + * followed with a wmb(); rmb() before accesing (unless locked + * inside i2400m->init_mutex). Read access can be loose like that + * [just using rmb()] because the paths that use this also do + * other error checks later on. + * + * @ready: Communication infrastructure with the device is ready, data + * frames can start to be passed around (this is lighter than + * using the WiMAX state for certain hot paths). + * + * Write to it only inside a i2400m->init_mutex protected area + * followed with a wmb(); rmb() before accesing (unless locked + * inside i2400m->init_mutex). Read access can be loose like that + * [just using rmb()] because the paths that use this also do + * other error checks later on. + * * @rx_reorder: 1 if RX reordering is enabled; this can only be * set at probe time. * @@ -362,6 +421,13 @@ struct i2400m_roq; * delivered. Then the driver can release them to the host. See * drivers/net/i2400m/rx.c for details. * + * @rx_reports: reports received from the device that couldn't be + * processed because the driver wasn't still ready; when ready, + * they are pulled from here and chewed. + * + * @rx_reports_ws: Work struct used to kick a scan of the RX reports + * list and to process each. + * * @src_mac_addr: MAC address used to make ethernet packets be coming * from. This is generated at i2400m_setup() time and used during * the life cycle of the instance. See i2400m_fake_eth_header(). @@ -422,6 +488,25 @@ struct i2400m_roq; * * @fw_version: version of the firmware interface, Major.minor, * encoded in the high word and low word (major << 16 | minor). + * + * @fw_hdrs: NULL terminated array of pointers to the firmware + * headers. This is only available during firmware load time. + * + * @fw_cached: Used to cache firmware when the system goes to + * suspend/standby/hibernation (as on resume we can't read it). If + * NULL, no firmware was cached, read it. If ~0, you can't read + * any firmware files (the system still didn't come out of suspend + * and failed to cache one), so abort; otherwise, a valid cached + * firmware to be used. Access to this variable is protected by + * the spinlock i2400m->rx_lock. + * + * @barker: barker type that the device uses; this is initialized by + * i2400m_is_boot_barker() the first time it is called. Then it + * won't change during the life cycle of the device and everytime + * a boot barker is received, it is just verified for it being the + * same. + * + * @pm_notifier: used to register for PM events */ struct i2400m { struct wimax_dev wimax_dev; /* FIRST! See doc */ @@ -429,7 +514,7 @@ struct i2400m { unsigned updown:1; /* Network device is up or down */ unsigned boot_mode:1; /* is the device in boot mode? */ unsigned sboot:1; /* signed or unsigned fw boot */ - unsigned ready:1; /* all probing steps done */ + unsigned ready:1; /* Device comm infrastructure ready */ unsigned rx_reorder:1; /* RX reorder is enabled */ u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */ /* typed u8 so /sys/kernel/debug/u8 can tweak */ @@ -440,8 +525,10 @@ struct i2400m { size_t bus_pl_size_max; unsigned bus_bm_retries; + int (*bus_setup)(struct i2400m *); int (*bus_dev_start)(struct i2400m *); void (*bus_dev_stop)(struct i2400m *); + void (*bus_release)(struct i2400m *); void (*bus_tx_kick)(struct i2400m *); int (*bus_reset)(struct i2400m *, enum i2400m_reset_type); ssize_t (*bus_bm_cmd_send)(struct i2400m *, @@ -468,6 +555,8 @@ struct i2400m { rx_num, rx_size_acc, rx_size_min, rx_size_max; struct i2400m_roq *rx_roq; /* not under rx_lock! */ u8 src_mac_addr[ETH_HLEN]; + struct list_head rx_reports; /* under rx_lock! */ + struct work_struct rx_report_ws; struct mutex msg_mutex; /* serialize command execution */ struct completion msg_completion; @@ -487,37 +576,12 @@ struct i2400m { struct dentry *debugfs_dentry; const char *fw_name; /* name of the current firmware image */ unsigned long fw_version; /* version of the firmware interface */ -}; - + const struct i2400m_bcf_hdr **fw_hdrs; + struct i2400m_fw *fw_cached; /* protected by rx_lock */ + struct i2400m_barker_db *barker; -/* - * Initialize a 'struct i2400m' from all zeroes - * - * This is a bus-generic API call. - */ -static inline -void i2400m_init(struct i2400m *i2400m) -{ - wimax_dev_init(&i2400m->wimax_dev); - - i2400m->boot_mode = 1; - i2400m->rx_reorder = 1; - init_waitqueue_head(&i2400m->state_wq); - - spin_lock_init(&i2400m->tx_lock); - i2400m->tx_pl_min = UINT_MAX; - i2400m->tx_size_min = UINT_MAX; - - spin_lock_init(&i2400m->rx_lock); - i2400m->rx_pl_min = UINT_MAX; - i2400m->rx_size_min = UINT_MAX; - - mutex_init(&i2400m->msg_mutex); - init_completion(&i2400m->msg_completion); - - mutex_init(&i2400m->init_mutex); - /* wake_tx_ws is initialized in i2400m_tx_setup() */ -} + struct notifier_block pm_notifier; +}; /* @@ -577,6 +641,14 @@ extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *); extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri); extern int i2400m_read_mac_addr(struct i2400m *); extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri); +extern int i2400m_is_boot_barker(struct i2400m *, const void *, size_t); +static inline +int i2400m_is_d2h_barker(const void *buf) +{ + const __le32 *barker = buf; + return le32_to_cpu(*barker) == I2400M_D2H_MSG_BARKER; +} +extern void i2400m_unknown_barker(struct i2400m *, const void *, size_t); /* Make/grok boot-rom header commands */ @@ -644,6 +716,8 @@ unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr) /* * Driver / device setup and internal functions */ +extern void i2400m_init(struct i2400m *); +extern int i2400m_reset(struct i2400m *, enum i2400m_reset_type); extern void i2400m_netdev_setup(struct net_device *net_dev); extern int i2400m_sysfs_setup(struct device_driver *); extern void i2400m_sysfs_release(struct device_driver *); @@ -654,10 +728,14 @@ extern void i2400m_tx_release(struct i2400m *); extern int i2400m_rx_setup(struct i2400m *); extern void i2400m_rx_release(struct i2400m *); +extern void i2400m_fw_cache(struct i2400m *); +extern void i2400m_fw_uncache(struct i2400m *); + extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, const void *, int); extern void i2400m_net_erx(struct i2400m *, struct sk_buff *, enum i2400m_cs); +extern void i2400m_net_wake_stop(struct i2400m *); enum i2400m_pt; extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt); @@ -672,14 +750,12 @@ static inline int i2400m_debugfs_add(struct i2400m *i2400m) static inline void i2400m_debugfs_rm(struct i2400m *i2400m) {} #endif -/* Called by _dev_start()/_dev_stop() to initialize the device itself */ +/* Initialize/shutdown the device */ extern int i2400m_dev_initialize(struct i2400m *); extern void i2400m_dev_shutdown(struct i2400m *); extern struct attribute_group i2400m_dev_attr_group; -extern int i2400m_schedule_work(struct i2400m *, - void (*)(struct work_struct *), gfp_t); /* HDI message's payload description handling */ @@ -724,7 +800,9 @@ void i2400m_put(struct i2400m *i2400m) dev_put(i2400m->wimax_dev.net_dev); } -extern int i2400m_dev_reset_handle(struct i2400m *); +extern int i2400m_dev_reset_handle(struct i2400m *, const char *); +extern int i2400m_pre_reset(struct i2400m *); +extern int i2400m_post_reset(struct i2400m *); /* * _setup()/_release() are called by the probe/disconnect functions of @@ -737,20 +815,6 @@ extern int i2400m_rx(struct i2400m *, struct sk_buff *); extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *); extern void i2400m_tx_msg_sent(struct i2400m *); -static const __le32 i2400m_NBOOT_BARKER[4] = { - cpu_to_le32(I2400M_NBOOT_BARKER), - cpu_to_le32(I2400M_NBOOT_BARKER), - cpu_to_le32(I2400M_NBOOT_BARKER), - cpu_to_le32(I2400M_NBOOT_BARKER) -}; - -static const __le32 i2400m_SBOOT_BARKER[4] = { - cpu_to_le32(I2400M_SBOOT_BARKER), - cpu_to_le32(I2400M_SBOOT_BARKER), - cpu_to_le32(I2400M_SBOOT_BARKER), - cpu_to_le32(I2400M_SBOOT_BARKER) -}; - extern int i2400m_power_save_disabled; /* @@ -773,10 +837,12 @@ struct device *i2400m_dev(struct i2400m *i2400m) struct i2400m_work { struct work_struct ws; struct i2400m *i2400m; + size_t pl_size; u8 pl[0]; }; -extern int i2400m_queue_work(struct i2400m *, - void (*)(struct work_struct *), gfp_t, + +extern int i2400m_schedule_work(struct i2400m *, + void (*)(struct work_struct *), gfp_t, const void *, size_t); extern int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *, @@ -789,6 +855,7 @@ extern void i2400m_msg_ack_hook(struct i2400m *, const struct i2400m_l3l4_hdr *, size_t); extern void i2400m_report_hook(struct i2400m *, const struct i2400m_l3l4_hdr *, size_t); +extern void i2400m_report_hook_work(struct work_struct *); extern int i2400m_cmd_enter_powersave(struct i2400m *); extern int i2400m_cmd_get_state(struct i2400m *); extern int i2400m_cmd_exit_idle(struct i2400m *); @@ -849,6 +916,12 @@ void __i2400m_msleep(unsigned ms) #endif } + +/* module initialization helpers */ +extern int i2400m_barker_db_init(const char *); +extern void i2400m_barker_db_exit(void); + + /* Module parameters */ extern int i2400m_idle_mode_disabled; diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c index 796396cb4c82..599aa4eb9baa 100644 --- a/drivers/net/wimax/i2400m/netdev.c +++ b/drivers/net/wimax/i2400m/netdev.c @@ -74,6 +74,7 @@ */ #include <linux/if_arp.h> #include <linux/netdevice.h> +#include <linux/ethtool.h> #include "i2400m.h" @@ -88,7 +89,10 @@ enum { * The MTU is 1400 or less */ I2400M_MAX_MTU = 1400, - I2400M_TX_TIMEOUT = HZ, + /* 20 secs? yep, this is the maximum timeout that the device + * might take to get out of IDLE / negotiate it with the base + * station. We add 1sec for good measure. */ + I2400M_TX_TIMEOUT = 21 * HZ, I2400M_TX_QLEN = 5, }; @@ -101,22 +105,19 @@ int i2400m_open(struct net_device *net_dev) struct device *dev = i2400m_dev(i2400m); d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m); - if (i2400m->ready == 0) { - dev_err(dev, "Device is still initializing\n"); - result = -EBUSY; - } else + /* Make sure we wait until init is complete... */ + mutex_lock(&i2400m->init_mutex); + if (i2400m->updown) result = 0; + else + result = -EBUSY; + mutex_unlock(&i2400m->init_mutex); d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", net_dev, i2400m, result); return result; } -/* - * - * On kernel versions where cancel_work_sync() didn't return anything, - * we rely on wake_tx_skb() being non-NULL. - */ static int i2400m_stop(struct net_device *net_dev) { @@ -124,21 +125,7 @@ int i2400m_stop(struct net_device *net_dev) struct device *dev = i2400m_dev(i2400m); d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m); - /* See i2400m_hard_start_xmit(), references are taken there - * and here we release them if the work was still - * pending. Note we can't differentiate work not pending vs - * never scheduled, so the NULL check does that. */ - if (cancel_work_sync(&i2400m->wake_tx_ws) == 0 - && i2400m->wake_tx_skb != NULL) { - unsigned long flags; - struct sk_buff *wake_tx_skb; - spin_lock_irqsave(&i2400m->tx_lock, flags); - wake_tx_skb = i2400m->wake_tx_skb; /* compat help */ - i2400m->wake_tx_skb = NULL; /* compat help */ - spin_unlock_irqrestore(&i2400m->tx_lock, flags); - i2400m_put(i2400m); - kfree_skb(wake_tx_skb); - } + i2400m_net_wake_stop(i2400m); d_fnend(3, dev, "(net_dev %p [i2400m %p]) = 0\n", net_dev, i2400m); return 0; } @@ -167,6 +154,7 @@ void i2400m_wake_tx_work(struct work_struct *ws) { int result; struct i2400m *i2400m = container_of(ws, struct i2400m, wake_tx_ws); + struct net_device *net_dev = i2400m->wimax_dev.net_dev; struct device *dev = i2400m_dev(i2400m); struct sk_buff *skb = i2400m->wake_tx_skb; unsigned long flags; @@ -182,27 +170,36 @@ void i2400m_wake_tx_work(struct work_struct *ws) dev_err(dev, "WAKE&TX: skb dissapeared!\n"); goto out_put; } + /* If we have, somehow, lost the connection after this was + * queued, don't do anything; this might be the device got + * reset or just disconnected. */ + if (unlikely(!netif_carrier_ok(net_dev))) + goto out_kfree; result = i2400m_cmd_exit_idle(i2400m); if (result == -EILSEQ) result = 0; if (result < 0) { dev_err(dev, "WAKE&TX: device didn't get out of idle: " - "%d\n", result); - goto error; + "%d - resetting\n", result); + i2400m_reset(i2400m, I2400M_RT_BUS); + goto error; } result = wait_event_timeout(i2400m->state_wq, - i2400m->state != I2400M_SS_IDLE, 5 * HZ); + i2400m->state != I2400M_SS_IDLE, + net_dev->watchdog_timeo - HZ/2); if (result == 0) result = -ETIMEDOUT; if (result < 0) { dev_err(dev, "WAKE&TX: error waiting for device to exit IDLE: " - "%d\n", result); + "%d - resetting\n", result); + i2400m_reset(i2400m, I2400M_RT_BUS); goto error; } msleep(20); /* device still needs some time or it drops it */ result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA); - netif_wake_queue(i2400m->wimax_dev.net_dev); error: + netif_wake_queue(net_dev); +out_kfree: kfree_skb(skb); /* refcount transferred by _hard_start_xmit() */ out_put: i2400m_put(i2400m); @@ -229,6 +226,38 @@ void i2400m_tx_prep_header(struct sk_buff *skb) } + +/* + * Cleanup resources acquired during i2400m_net_wake_tx() + * + * This is called by __i2400m_dev_stop and means we have to make sure + * the workqueue is flushed from any pending work. + */ +void i2400m_net_wake_stop(struct i2400m *i2400m) +{ + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + /* See i2400m_hard_start_xmit(), references are taken there + * and here we release them if the work was still + * pending. Note we can't differentiate work not pending vs + * never scheduled, so the NULL check does that. */ + if (cancel_work_sync(&i2400m->wake_tx_ws) == 0 + && i2400m->wake_tx_skb != NULL) { + unsigned long flags; + struct sk_buff *wake_tx_skb; + spin_lock_irqsave(&i2400m->tx_lock, flags); + wake_tx_skb = i2400m->wake_tx_skb; /* compat help */ + i2400m->wake_tx_skb = NULL; /* compat help */ + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + i2400m_put(i2400m); + kfree_skb(wake_tx_skb); + } + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); + return; +} + + /* * TX an skb to an idle device * @@ -342,6 +371,20 @@ netdev_tx_t i2400m_hard_start_xmit(struct sk_buff *skb, int result; d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev); + if (skb_header_cloned(skb)) { + /* + * Make tcpdump/wireshark happy -- if they are + * running, the skb is cloned and we will overwrite + * the mac fields in i2400m_tx_prep_header. Expand + * seems to fix this... + */ + result = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (result) { + result = NETDEV_TX_BUSY; + goto error_expand; + } + } + if (i2400m->state == I2400M_SS_IDLE) result = i2400m_net_wake_tx(i2400m, net_dev, skb); else @@ -352,10 +395,11 @@ netdev_tx_t i2400m_hard_start_xmit(struct sk_buff *skb, net_dev->stats.tx_packets++; net_dev->stats.tx_bytes += skb->len; } + result = NETDEV_TX_OK; +error_expand: kfree_skb(skb); - - d_fnend(3, dev, "(skb %p net_dev %p)\n", skb, net_dev); - return NETDEV_TX_OK; + d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result); + return result; } @@ -559,6 +603,22 @@ static const struct net_device_ops i2400m_netdev_ops = { .ndo_change_mtu = i2400m_change_mtu, }; +static void i2400m_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *info) +{ + struct i2400m *i2400m = net_dev_to_i2400m(net_dev); + + strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver) - 1); + strncpy(info->fw_version, i2400m->fw_name, sizeof(info->fw_version) - 1); + if (net_dev->dev.parent) + strncpy(info->bus_info, dev_name(net_dev->dev.parent), + sizeof(info->bus_info) - 1); +} + +static const struct ethtool_ops i2400m_ethtool_ops = { + .get_drvinfo = i2400m_get_drvinfo, + .get_link = ethtool_op_get_link, +}; /** * i2400m_netdev_setup - Setup setup @net_dev's i2400m private data @@ -580,6 +640,7 @@ void i2400m_netdev_setup(struct net_device *net_dev) & ~IFF_MULTICAST); net_dev->watchdog_timeo = I2400M_TX_TIMEOUT; net_dev->netdev_ops = &i2400m_netdev_ops; + net_dev->ethtool_ops = &i2400m_ethtool_ops; d_fnend(3, NULL, "(net_dev %p) = void\n", net_dev); } EXPORT_SYMBOL_GPL(i2400m_netdev_setup); diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c index 07c32e68909f..7ddb173fd4a7 100644 --- a/drivers/net/wimax/i2400m/rx.c +++ b/drivers/net/wimax/i2400m/rx.c @@ -158,30 +158,104 @@ struct i2400m_report_hook_args { struct sk_buff *skb_rx; const struct i2400m_l3l4_hdr *l3l4_hdr; size_t size; + struct list_head list_node; }; /* * Execute i2400m_report_hook in a workqueue * - * Unpacks arguments from the deferred call, executes it and then - * drops the references. + * Goes over the list of queued reports in i2400m->rx_reports and + * processes them. * - * Obvious NOTE: References are needed because we are a separate - * thread; otherwise the buffer changes under us because it is - * released by the original caller. + * NOTE: refcounts on i2400m are not needed because we flush the + * workqueue this runs on (i2400m->work_queue) before destroying + * i2400m. */ -static void i2400m_report_hook_work(struct work_struct *ws) { - struct i2400m_work *iw = - container_of(ws, struct i2400m_work, ws); - struct i2400m_report_hook_args *args = (void *) iw->pl; - if (iw->i2400m->ready) - i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size); - kfree_skb(args->skb_rx); - i2400m_put(iw->i2400m); - kfree(iw); + struct i2400m *i2400m = container_of(ws, struct i2400m, rx_report_ws); + struct device *dev = i2400m_dev(i2400m); + struct i2400m_report_hook_args *args, *args_next; + LIST_HEAD(list); + unsigned long flags; + + while (1) { + spin_lock_irqsave(&i2400m->rx_lock, flags); + list_splice_init(&i2400m->rx_reports, &list); + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + if (list_empty(&list)) + break; + else + d_printf(1, dev, "processing queued reports\n"); + list_for_each_entry_safe(args, args_next, &list, list_node) { + d_printf(2, dev, "processing queued report %p\n", args); + i2400m_report_hook(i2400m, args->l3l4_hdr, args->size); + kfree_skb(args->skb_rx); + list_del(&args->list_node); + kfree(args); + } + } +} + + +/* + * Flush the list of queued reports + */ +static +void i2400m_report_hook_flush(struct i2400m *i2400m) +{ + struct device *dev = i2400m_dev(i2400m); + struct i2400m_report_hook_args *args, *args_next; + LIST_HEAD(list); + unsigned long flags; + + d_printf(1, dev, "flushing queued reports\n"); + spin_lock_irqsave(&i2400m->rx_lock, flags); + list_splice_init(&i2400m->rx_reports, &list); + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + list_for_each_entry_safe(args, args_next, &list, list_node) { + d_printf(2, dev, "flushing queued report %p\n", args); + kfree_skb(args->skb_rx); + list_del(&args->list_node); + kfree(args); + } +} + + +/* + * Queue a report for later processing + * + * @i2400m: device descriptor + * @skb_rx: skb that contains the payload (for reference counting) + * @l3l4_hdr: pointer to the control + * @size: size of the message + */ +static +void i2400m_report_hook_queue(struct i2400m *i2400m, struct sk_buff *skb_rx, + const void *l3l4_hdr, size_t size) +{ + struct device *dev = i2400m_dev(i2400m); + unsigned long flags; + struct i2400m_report_hook_args *args; + + args = kzalloc(sizeof(*args), GFP_NOIO); + if (args) { + args->skb_rx = skb_get(skb_rx); + args->l3l4_hdr = l3l4_hdr; + args->size = size; + spin_lock_irqsave(&i2400m->rx_lock, flags); + list_add_tail(&args->list_node, &i2400m->rx_reports); + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + d_printf(2, dev, "queued report %p\n", args); + rmb(); /* see i2400m->ready's documentation */ + if (likely(i2400m->ready)) /* only send if up */ + queue_work(i2400m->work_queue, &i2400m->rx_report_ws); + } else { + if (printk_ratelimit()) + dev_err(dev, "%s:%u: Can't allocate %zu B\n", + __func__, __LINE__, sizeof(*args)); + } } @@ -295,21 +369,29 @@ void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx, msg_type, size); d_dump(2, dev, l3l4_hdr, size); if (msg_type & I2400M_MT_REPORT_MASK) { - /* These hooks have to be ran serialized; as well, the - * handling might force the execution of commands, and - * that might cause reentrancy issues with - * bus-specific subdrivers and workqueues. So we run - * it in a separate workqueue. */ - struct i2400m_report_hook_args args = { - .skb_rx = skb_rx, - .l3l4_hdr = l3l4_hdr, - .size = size - }; - if (unlikely(i2400m->ready == 0)) /* only send if up */ - return; - skb_get(skb_rx); - i2400m_queue_work(i2400m, i2400m_report_hook_work, - GFP_KERNEL, &args, sizeof(args)); + /* + * Process each report + * + * - has to be ran serialized as well + * + * - the handling might force the execution of + * commands. That might cause reentrancy issues with + * bus-specific subdrivers and workqueues, so the we + * run it in a separate workqueue. + * + * - when the driver is not yet ready to handle them, + * they are queued and at some point the queue is + * restarted [NOTE: we can't queue SKBs directly, as + * this might be a piece of a SKB, not the whole + * thing, and this is cheaper than cloning the + * SKB]. + * + * Note we don't do refcounting for the device + * structure; this is because before destroying + * 'i2400m', we make sure to flush the + * i2400m->work_queue, so there are no issues. + */ + i2400m_report_hook_queue(i2400m, skb_rx, l3l4_hdr, size); if (unlikely(i2400m->trace_msg_from_user)) wimax_msg(&i2400m->wimax_dev, "echo", l3l4_hdr, size, GFP_KERNEL); @@ -363,8 +445,6 @@ void i2400m_rx_trace(struct i2400m *i2400m, msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", msg_type, size); d_dump(2, dev, l3l4_hdr, size); - if (unlikely(i2400m->ready == 0)) /* only send if up */ - return; result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL); if (result < 0) dev_err(dev, "error sending trace to userspace: %d\n", @@ -748,7 +828,7 @@ void i2400m_roq_queue(struct i2400m *i2400m, struct i2400m_roq *roq, dev_err(dev, "SW BUG? queue nsn %d (lbn %u ws %u)\n", nsn, lbn, roq->ws); i2400m_roq_log_dump(i2400m, roq); - i2400m->bus_reset(i2400m, I2400M_RT_WARM); + i2400m_reset(i2400m, I2400M_RT_WARM); } else { __i2400m_roq_queue(i2400m, roq, skb, lbn, nsn); i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET, @@ -814,7 +894,7 @@ void i2400m_roq_queue_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq, dev_err(dev, "SW BUG? queue_update_ws nsn %u (sn %u ws %u)\n", nsn, sn, roq->ws); i2400m_roq_log_dump(i2400m, roq); - i2400m->bus_reset(i2400m, I2400M_RT_WARM); + i2400m_reset(i2400m, I2400M_RT_WARM); } else { /* if the queue is empty, don't bother as we'd queue * it and inmediately unqueue it -- just deliver it */ @@ -1114,7 +1194,7 @@ error: * device. See the file header for the format. Run all checks on the * buffer header, then run over each payload's descriptors, verify * their consistency and act on each payload's contents. If - * everything is succesful, update the device's statistics. + * everything is successful, update the device's statistics. * * Note: You need to set the skb to contain only the length of the * received buffer; for that, use skb_trim(skb, RECEIVED_SIZE). @@ -1194,6 +1274,28 @@ error_msg_hdr_check: EXPORT_SYMBOL_GPL(i2400m_rx); +void i2400m_unknown_barker(struct i2400m *i2400m, + const void *buf, size_t size) +{ + struct device *dev = i2400m_dev(i2400m); + char prefix[64]; + const __le32 *barker = buf; + dev_err(dev, "RX: HW BUG? unknown barker %08x, " + "dropping %zu bytes\n", le32_to_cpu(*barker), size); + snprintf(prefix, sizeof(prefix), "%s %s: ", + dev_driver_string(dev), dev_name(dev)); + if (size > 64) { + print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, + 8, 4, buf, 64, 0); + printk(KERN_ERR "%s... (only first 64 bytes " + "dumped)\n", prefix); + } else + print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, + 8, 4, buf, size, 0); +} +EXPORT_SYMBOL(i2400m_unknown_barker); + + /* * Initialize the RX queue and infrastructure * @@ -1261,4 +1363,6 @@ void i2400m_rx_release(struct i2400m *i2400m) kfree(i2400m->rx_roq[0].log); kfree(i2400m->rx_roq); } + /* at this point, nothing can be received... */ + i2400m_report_hook_flush(i2400m); } diff --git a/drivers/net/wimax/i2400m/sdio-fw.c b/drivers/net/wimax/i2400m/sdio-fw.c index 7d6ec0f475f8..8e025418f5be 100644 --- a/drivers/net/wimax/i2400m/sdio-fw.c +++ b/drivers/net/wimax/i2400m/sdio-fw.c @@ -118,7 +118,8 @@ ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m, if (cmd_size > I2400M_BM_CMD_BUF_SIZE) goto error_too_big; - memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size); /* Prep command */ + if (_cmd != i2400m->bm_cmd_buf) + memmove(i2400m->bm_cmd_buf, _cmd, cmd_size); cmd = i2400m->bm_cmd_buf; if (cmd_size_a > cmd_size) /* Zero pad space */ memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size); @@ -177,10 +178,6 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n", i2400m, ack, ack_size); - spin_lock(&i2400m->rx_lock); - i2400ms->bm_ack_size = -EINPROGRESS; - spin_unlock(&i2400m->rx_lock); - result = wait_event_timeout(i2400ms->bm_wfa_wq, i2400ms->bm_ack_size != -EINPROGRESS, 2 * HZ); @@ -199,6 +196,10 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, size = min(ack_size, i2400ms->bm_ack_size); memcpy(ack, i2400m->bm_ack_buf, size); } + /* + * Remember always to clear the bm_ack_size to -EINPROGRESS + * after the RX data is processed + */ i2400ms->bm_ack_size = -EINPROGRESS; spin_unlock(&i2400m->rx_lock); diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c index 321beadf6e47..8adf6c9b6f8f 100644 --- a/drivers/net/wimax/i2400m/sdio-rx.c +++ b/drivers/net/wimax/i2400m/sdio-rx.c @@ -53,6 +53,7 @@ * i2400ms_irq() * i2400ms_rx() * __i2400ms_rx_get_size() + * i2400m_is_boot_barker() * i2400m_rx() * * i2400ms_rx_setup() @@ -138,6 +139,11 @@ void i2400ms_rx(struct i2400ms *i2400ms) ret = rx_size; goto error_get_size; } + /* + * Hardware quirk: make sure to clear the INTR status register + * AFTER getting the data transfer size. + */ + sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); ret = -ENOMEM; skb = alloc_skb(rx_size, GFP_ATOMIC); @@ -153,25 +159,34 @@ void i2400ms_rx(struct i2400ms *i2400ms) } rmb(); /* make sure we get boot_mode from dev_reset_handle */ - if (i2400m->boot_mode == 1) { + if (unlikely(i2400m->boot_mode == 1)) { spin_lock(&i2400m->rx_lock); i2400ms->bm_ack_size = rx_size; spin_unlock(&i2400m->rx_lock); memcpy(i2400m->bm_ack_buf, skb->data, rx_size); wake_up(&i2400ms->bm_wfa_wq); - dev_err(dev, "RX: SDIO boot mode message\n"); + d_printf(5, dev, "RX: SDIO boot mode message\n"); kfree_skb(skb); - } else if (unlikely(!memcmp(skb->data, i2400m_NBOOT_BARKER, - sizeof(i2400m_NBOOT_BARKER)) - || !memcmp(skb->data, i2400m_SBOOT_BARKER, - sizeof(i2400m_SBOOT_BARKER)))) { - ret = i2400m_dev_reset_handle(i2400m); + goto out; + } + ret = -EIO; + if (unlikely(rx_size < sizeof(__le32))) { + dev_err(dev, "HW BUG? only %zu bytes received\n", rx_size); + goto error_bad_size; + } + if (likely(i2400m_is_d2h_barker(skb->data))) { + skb_put(skb, rx_size); + i2400m_rx(i2400m, skb); + } else if (unlikely(i2400m_is_boot_barker(i2400m, + skb->data, rx_size))) { + ret = i2400m_dev_reset_handle(i2400m, "device rebooted"); dev_err(dev, "RX: SDIO reboot barker\n"); kfree_skb(skb); } else { - skb_put(skb, rx_size); - i2400m_rx(i2400m, skb); + i2400m_unknown_barker(i2400m, skb->data, rx_size); + kfree_skb(skb); } +out: d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); return; @@ -179,6 +194,7 @@ error_memcpy_fromio: kfree_skb(skb); error_alloc_skb: error_get_size: +error_bad_size: d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); return; } @@ -209,7 +225,6 @@ void i2400ms_irq(struct sdio_func *func) dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n"); goto error_no_irq; } - sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); i2400ms_rx(i2400ms); error_no_irq: d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms); @@ -234,6 +249,13 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms) init_waitqueue_head(&i2400ms->bm_wfa_wq); spin_lock(&i2400m->rx_lock); i2400ms->bm_wait_result = -EINPROGRESS; + /* + * Before we are about to enable the RX interrupt, make sure + * bm_ack_size is cleared to -EINPROGRESS which indicates + * no RX interrupt happened yet or the previous interrupt + * has been handled, we are ready to take the new interrupt + */ + i2400ms->bm_ack_size = -EINPROGRESS; spin_unlock(&i2400m->rx_lock); sdio_claim_host(func); diff --git a/drivers/net/wimax/i2400m/sdio-tx.c b/drivers/net/wimax/i2400m/sdio-tx.c index 5105a5ebc44f..de66d068c9cb 100644 --- a/drivers/net/wimax/i2400m/sdio-tx.c +++ b/drivers/net/wimax/i2400m/sdio-tx.c @@ -149,5 +149,8 @@ int i2400ms_tx_setup(struct i2400ms *i2400ms) void i2400ms_tx_release(struct i2400ms *i2400ms) { - destroy_workqueue(i2400ms->tx_workqueue); + if (i2400ms->tx_workqueue) { + destroy_workqueue(i2400ms->tx_workqueue); + i2400ms->tx_workqueue = NULL; + } } diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c index 2981e211e04f..76a50ac02ebb 100644 --- a/drivers/net/wimax/i2400m/sdio.c +++ b/drivers/net/wimax/i2400m/sdio.c @@ -43,18 +43,9 @@ * i2400m_release() * free_netdev(net_dev) * - * i2400ms_bus_reset() Called by i2400m->bus_reset + * i2400ms_bus_reset() Called by i2400m_reset * __i2400ms_reset() * __i2400ms_send_barker() - * - * i2400ms_bus_dev_start() Called by i2400m_dev_start() [who is - * i2400ms_tx_setup() called by i2400m_setup()] - * i2400ms_rx_setup() - * - * i2400ms_bus_dev_stop() Called by i2400m_dev_stop() [who is - * i2400ms_rx_release() is called by i2400m_release()] - * i2400ms_tx_release() - * */ #include <linux/debugfs.h> @@ -71,6 +62,14 @@ static int ioe_timeout = 2; module_param(ioe_timeout, int, 0); +static char i2400ms_debug_params[128]; +module_param_string(debug, i2400ms_debug_params, sizeof(i2400ms_debug_params), + 0644); +MODULE_PARM_DESC(debug, + "String of space-separated NAME:VALUE pairs, where NAMEs " + "are the different debug submodules and VALUE are the " + "initial debug value to set."); + /* Our firmware file name list */ static const char *i2400ms_bus_fw_names[] = { #define I2400MS_FW_FILE_NAME "i2400m-fw-sdio-1.3.sbcf" @@ -95,17 +94,24 @@ static const struct i2400m_poke_table i2400ms_pokes[] = { * when we ask it to explicitly doing). Tries until a timeout is * reached. * + * The @maxtries argument indicates how many times (at most) it should + * be tried to enable the function. 0 means forever. This acts along + * with the timeout (ie: it'll stop trying as soon as the maximum + * number of tries is reached _or_ as soon as the timeout is reached). + * * The reverse of this is...sdio_disable_function() * * Returns: 0 if the SDIO function was enabled, < 0 errno code on * error (-ENODEV when it was unable to enable the function). */ static -int i2400ms_enable_function(struct sdio_func *func) +int i2400ms_enable_function(struct i2400ms *i2400ms, unsigned maxtries) { + struct sdio_func *func = i2400ms->func; u64 timeout; int err; struct device *dev = &func->dev; + unsigned tries = 0; d_fnstart(3, dev, "(func %p)\n", func); /* Setup timeout (FIXME: This needs to read the CIS table to @@ -115,6 +121,14 @@ int i2400ms_enable_function(struct sdio_func *func) err = -ENODEV; while (err != 0 && time_before64(get_jiffies_64(), timeout)) { sdio_claim_host(func); + /* + * There is a sillicon bug on the IWMC3200, where the + * IOE timeout will cause problems on Moorestown + * platforms (system hang). We explicitly overwrite + * func->enable_timeout here to work around the issue. + */ + if (i2400ms->iwmc3200) + func->enable_timeout = IWMC3200_IOR_TIMEOUT; err = sdio_enable_func(func); if (0 == err) { sdio_release_host(func); @@ -122,8 +136,11 @@ int i2400ms_enable_function(struct sdio_func *func) goto function_enabled; } d_printf(2, dev, "SDIO function failed to enable: %d\n", err); - sdio_disable_func(func); sdio_release_host(func); + if (maxtries > 0 && ++tries >= maxtries) { + err = -ETIME; + break; + } msleep(I2400MS_INIT_SLEEP_INTERVAL); } /* If timed out, device is not there yet -- get -ENODEV so @@ -140,46 +157,99 @@ function_enabled: /* - * Setup driver resources needed to communicate with the device + * Setup minimal device communication infrastructure needed to at + * least be able to update the firmware. * - * The fw needs some time to settle, and it was just uploaded, - * so give it a break first. I'd prefer to just wait for the device to - * send something, but seems the poking we do to enable SDIO stuff - * interferes with it, so just give it a break before starting... + * Note the ugly trick: if we are in the probe path + * (i2400ms->debugfs_dentry == NULL), we only retry function + * enablement one, to avoid racing with the iwmc3200 top controller. */ static -int i2400ms_bus_dev_start(struct i2400m *i2400m) +int i2400ms_bus_setup(struct i2400m *i2400m) { int result; - struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); + struct i2400ms *i2400ms = + container_of(i2400m, struct i2400ms, i2400m); + struct device *dev = i2400m_dev(i2400m); struct sdio_func *func = i2400ms->func; - struct device *dev = &func->dev; + int retries; + + sdio_claim_host(func); + result = sdio_set_block_size(func, I2400MS_BLK_SIZE); + sdio_release_host(func); + if (result < 0) { + dev_err(dev, "Failed to set block size: %d\n", result); + goto error_set_blk_size; + } + + if (i2400ms->iwmc3200 && i2400ms->debugfs_dentry == NULL) + retries = 1; + else + retries = 0; + result = i2400ms_enable_function(i2400ms, retries); + if (result < 0) { + dev_err(dev, "Cannot enable SDIO function: %d\n", result); + goto error_func_enable; + } - d_fnstart(3, dev, "(i2400m %p)\n", i2400m); - msleep(200); result = i2400ms_tx_setup(i2400ms); if (result < 0) goto error_tx_setup; - d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); - return result; + result = i2400ms_rx_setup(i2400ms); + if (result < 0) + goto error_rx_setup; + return 0; -error_tx_setup: +error_rx_setup: i2400ms_tx_release(i2400ms); - d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); +error_tx_setup: + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); +error_func_enable: +error_set_blk_size: return result; } +/* + * Tear down minimal device communication infrastructure needed to at + * least be able to update the firmware. + */ +static +void i2400ms_bus_release(struct i2400m *i2400m) +{ + struct i2400ms *i2400ms = + container_of(i2400m, struct i2400ms, i2400m); + struct sdio_func *func = i2400ms->func; + + i2400ms_rx_release(i2400ms); + i2400ms_tx_release(i2400ms); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); +} + + +/* + * Setup driver resources needed to communicate with the device + * + * The fw needs some time to settle, and it was just uploaded, + * so give it a break first. I'd prefer to just wait for the device to + * send something, but seems the poking we do to enable SDIO stuff + * interferes with it, so just give it a break before starting... + */ static -void i2400ms_bus_dev_stop(struct i2400m *i2400m) +int i2400ms_bus_dev_start(struct i2400m *i2400m) { struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); struct sdio_func *func = i2400ms->func; struct device *dev = &func->dev; d_fnstart(3, dev, "(i2400m %p)\n", i2400m); - i2400ms_tx_release(i2400ms); - d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); + msleep(200); + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, 0); + return 0; } @@ -233,18 +303,17 @@ error_kzalloc: * Warm reset: * * The device will be fully reset internally, but won't be - * disconnected from the USB bus (so no reenumeration will + * disconnected from the bus (so no reenumeration will * happen). Firmware upload will be neccessary. * - * The device will send a reboot barker in the notification endpoint - * that will trigger the driver to reinitialize the state - * automatically from notif.c:i2400m_notification_grok() into - * i2400m_dev_bootstrap_delayed(). + * The device will send a reboot barker that will trigger the driver + * to reinitialize the state via __i2400m_dev_reset_handle. * - * Cold and bus (USB) reset: + * + * Cold and bus reset: * * The device will be fully reset internally, disconnected from the - * USB bus an a reenumeration will happen. Firmware upload will be + * bus an a reenumeration will happen. Firmware upload will be * neccessary. Thus, we don't do any locking or struct * reinitialization, as we are going to be fully disconnected and * reenumerated. @@ -283,25 +352,13 @@ int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) sizeof(i2400m_COLD_BOOT_BARKER)); else if (rt == I2400M_RT_BUS) { do_bus_reset: - /* call netif_tx_disable() before sending IOE disable, - * so that all the tx from network layer are stopped - * while IOE is being reset. Make sure it is called - * only after register_netdev() was issued. - */ - if (i2400m->wimax_dev.net_dev->reg_state == NETREG_REGISTERED) - netif_tx_disable(i2400m->wimax_dev.net_dev); - i2400ms_rx_release(i2400ms); - sdio_claim_host(i2400ms->func); - sdio_disable_func(i2400ms->func); - sdio_release_host(i2400ms->func); + i2400ms_bus_release(i2400m); /* Wait for the device to settle */ msleep(40); - result = i2400ms_enable_function(i2400ms->func); - if (result >= 0) - i2400ms_rx_setup(i2400ms); + result = i2400ms_bus_setup(i2400m); } else BUG(); if (result < 0 && rt != I2400M_RT_BUS) { @@ -350,7 +407,7 @@ int i2400ms_debugfs_add(struct i2400ms *i2400ms) int result; struct dentry *dentry = i2400ms->i2400m.wimax_dev.debugfs_dentry; - dentry = debugfs_create_dir("i2400m-usb", dentry); + dentry = debugfs_create_dir("i2400m-sdio", dentry); result = PTR_ERR(dentry); if (IS_ERR(dentry)) { if (result == -ENODEV) @@ -367,6 +424,7 @@ int i2400ms_debugfs_add(struct i2400ms *i2400ms) error: debugfs_remove_recursive(i2400ms->debugfs_dentry); + i2400ms->debugfs_dentry = NULL; return result; } @@ -425,37 +483,30 @@ int i2400ms_probe(struct sdio_func *func, i2400m->bus_tx_block_size = I2400MS_BLK_SIZE; i2400m->bus_pl_size_max = I2400MS_PL_SIZE_MAX; + i2400m->bus_setup = i2400ms_bus_setup; i2400m->bus_dev_start = i2400ms_bus_dev_start; - i2400m->bus_dev_stop = i2400ms_bus_dev_stop; + i2400m->bus_dev_stop = NULL; + i2400m->bus_release = i2400ms_bus_release; i2400m->bus_tx_kick = i2400ms_bus_tx_kick; i2400m->bus_reset = i2400ms_bus_reset; /* The iwmc3200-wimax sometimes requires the driver to try * hard when we paint it into a corner. */ - i2400m->bus_bm_retries = I3200_BOOT_RETRIES; + i2400m->bus_bm_retries = I2400M_SDIO_BOOT_RETRIES; i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send; i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack; i2400m->bus_fw_names = i2400ms_bus_fw_names; i2400m->bus_bm_mac_addr_impaired = 1; i2400m->bus_bm_pokes_table = &i2400ms_pokes[0]; - sdio_claim_host(func); - result = sdio_set_block_size(func, I2400MS_BLK_SIZE); - sdio_release_host(func); - if (result < 0) { - dev_err(dev, "Failed to set block size: %d\n", result); - goto error_set_blk_size; - } - - result = i2400ms_enable_function(i2400ms->func); - if (result < 0) { - dev_err(dev, "Cannot enable SDIO function: %d\n", result); - goto error_func_enable; + switch (func->device) { + case SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX: + case SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5: + i2400ms->iwmc3200 = 1; + break; + default: + i2400ms->iwmc3200 = 0; } - result = i2400ms_rx_setup(i2400ms); - if (result < 0) - goto error_rx_setup; - result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT); if (result < 0) { dev_err(dev, "cannot setup device: %d\n", result); @@ -473,13 +524,6 @@ int i2400ms_probe(struct sdio_func *func, error_debugfs_add: i2400m_release(i2400m); error_setup: - i2400ms_rx_release(i2400ms); -error_rx_setup: - sdio_claim_host(func); - sdio_disable_func(func); - sdio_release_host(func); -error_func_enable: -error_set_blk_size: sdio_set_drvdata(func, NULL); free_netdev(net_dev); error_alloc_netdev: @@ -497,12 +541,9 @@ void i2400ms_remove(struct sdio_func *func) d_fnstart(3, dev, "SDIO func %p\n", func); debugfs_remove_recursive(i2400ms->debugfs_dentry); - i2400ms_rx_release(i2400ms); + i2400ms->debugfs_dentry = NULL; i2400m_release(i2400m); sdio_set_drvdata(func, NULL); - sdio_claim_host(func); - sdio_disable_func(func); - sdio_release_host(func); free_netdev(net_dev); d_fnend(3, dev, "SDIO func %p\n", func); } @@ -512,6 +553,8 @@ const struct sdio_device_id i2400ms_sdio_ids[] = { /* Intel: i2400m WiMAX (iwmc3200) over SDIO */ { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, + SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5) }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, i2400ms_sdio_ids); @@ -529,6 +572,8 @@ struct sdio_driver i2400m_sdio_driver = { static int __init i2400ms_driver_init(void) { + d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400ms_debug_params, + "i2400m_sdio.debug"); return sdio_register_driver(&i2400m_sdio_driver); } module_init(i2400ms_driver_init); diff --git a/drivers/net/wimax/i2400m/tx.c b/drivers/net/wimax/i2400m/tx.c index fa16ccf8e26a..54480e8947f1 100644 --- a/drivers/net/wimax/i2400m/tx.c +++ b/drivers/net/wimax/i2400m/tx.c @@ -310,7 +310,7 @@ size_t __i2400m_tx_tail_room(struct i2400m *i2400m) size_t tail_room; size_t tx_in; - if (unlikely(i2400m->tx_in) == 0) + if (unlikely(i2400m->tx_in == 0)) return I2400M_TX_BUF_SIZE; tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; tail_room = I2400M_TX_BUF_SIZE - tx_in; @@ -642,6 +642,9 @@ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, * current one is out of payload slots or we have a singleton, * close it and start a new one */ spin_lock_irqsave(&i2400m->tx_lock, flags); + result = -ESHUTDOWN; + if (i2400m->tx_buf == NULL) + goto error_tx_new; try_new: if (unlikely(i2400m->tx_msg == NULL)) i2400m_tx_new(i2400m); @@ -697,7 +700,10 @@ try_new: } error_tx_new: spin_unlock_irqrestore(&i2400m->tx_lock, flags); - i2400m->bus_tx_kick(i2400m); /* always kick, might free up space */ + /* kick in most cases, except when the TX subsys is down, as + * it might free space */ + if (likely(result != -ESHUTDOWN)) + i2400m->bus_tx_kick(i2400m); d_fnend(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u) = %d\n", i2400m, buf, buf_len, pl_type, result); return result; @@ -740,6 +746,9 @@ struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *i2400m, d_fnstart(3, dev, "(i2400m %p bus_size %p)\n", i2400m, bus_size); spin_lock_irqsave(&i2400m->tx_lock, flags); + tx_msg_moved = NULL; + if (i2400m->tx_buf == NULL) + goto out_unlock; skip: tx_msg_moved = NULL; if (i2400m->tx_in == i2400m->tx_out) { /* Empty FIFO? */ @@ -829,6 +838,8 @@ void i2400m_tx_msg_sent(struct i2400m *i2400m) d_fnstart(3, dev, "(i2400m %p)\n", i2400m); spin_lock_irqsave(&i2400m->tx_lock, flags); + if (i2400m->tx_buf == NULL) + goto out_unlock; i2400m->tx_out += i2400m->tx_msg_size; d_printf(2, dev, "TX: sent %zu b\n", (size_t) i2400m->tx_msg_size); i2400m->tx_msg_size = 0; @@ -837,6 +848,7 @@ void i2400m_tx_msg_sent(struct i2400m *i2400m) n = i2400m->tx_out / I2400M_TX_BUF_SIZE; i2400m->tx_out %= I2400M_TX_BUF_SIZE; i2400m->tx_in -= n * I2400M_TX_BUF_SIZE; +out_unlock: spin_unlock_irqrestore(&i2400m->tx_lock, flags); d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); } @@ -876,5 +888,9 @@ int i2400m_tx_setup(struct i2400m *i2400m) */ void i2400m_tx_release(struct i2400m *i2400m) { + unsigned long flags; + spin_lock_irqsave(&i2400m->tx_lock, flags); kfree(i2400m->tx_buf); + i2400m->tx_buf = NULL; + spin_unlock_irqrestore(&i2400m->tx_lock, flags); } diff --git a/drivers/net/wimax/i2400m/usb-fw.c b/drivers/net/wimax/i2400m/usb-fw.c index 5ad287c228b8..ce6b9938fde0 100644 --- a/drivers/net/wimax/i2400m/usb-fw.c +++ b/drivers/net/wimax/i2400m/usb-fw.c @@ -99,10 +99,10 @@ ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size) dev_err(dev, "BM-CMD: can't get autopm: %d\n", result); do_autopm = 0; } - epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT); + epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out); pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); retry: - result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, HZ); + result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, 200); switch (result) { case 0: if (len != buf_size) { @@ -113,6 +113,28 @@ retry: } result = len; break; + case -EPIPE: + /* + * Stall -- maybe the device is choking with our + * requests. Clear it and give it some time. If they + * happen to often, it might be another symptom, so we + * reset. + * + * No error handling for usb_clear_halt(0; if it + * works, the retry works; if it fails, this switch + * does the error handling for us. + */ + if (edc_inc(&i2400mu->urb_edc, + 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "BM-CMD: too many stalls in " + "URB; resetting device\n"); + usb_queue_reset_device(i2400mu->usb_iface); + /* fallthrough */ + } else { + usb_clear_halt(i2400mu->usb_dev, pipe); + msleep(10); /* give the device some time */ + goto retry; + } case -EINVAL: /* while removing driver */ case -ENODEV: /* dev disconnect ... */ case -ENOENT: /* just ignore it */ @@ -135,7 +157,6 @@ retry: result); goto retry; } - result = len; if (do_autopm) usb_autopm_put_interface(i2400mu->usb_iface); return result; @@ -172,7 +193,8 @@ ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m, result = -E2BIG; if (cmd_size > I2400M_BM_CMD_BUF_SIZE) goto error_too_big; - memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size); + if (_cmd != i2400m->bm_cmd_buf) + memmove(i2400m->bm_cmd_buf, _cmd, cmd_size); cmd = i2400m->bm_cmd_buf; if (cmd_size_a > cmd_size) /* Zero pad space */ memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size); @@ -226,7 +248,8 @@ int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb, struct usb_endpoint_descriptor *epd; int pipe; - epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION); + epd = usb_get_epd(i2400mu->usb_iface, + i2400mu->endpoint_cfg.notification); pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress); usb_fill_int_urb(urb, i2400mu->usb_dev, pipe, i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE, @@ -328,8 +351,8 @@ error_dev_gone: out: if (do_autopm) usb_autopm_put_interface(i2400mu->usb_iface); - d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %zd\n", - i2400m, ack, ack_size, result); + d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %ld\n", + i2400m, ack, ack_size, (long) result); return result; error_exceeded: diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c index 6add27c3f35c..f88d1c6e35cb 100644 --- a/drivers/net/wimax/i2400m/usb-notif.c +++ b/drivers/net/wimax/i2400m/usb-notif.c @@ -51,6 +51,7 @@ * * i2400mu_usb_notification_cb() Called when a URB is ready * i2400mu_notif_grok() + * i2400m_is_boot_barker() * i2400m_dev_reset_handle() * i2400mu_rx_kick() */ @@ -87,32 +88,21 @@ int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf, d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n", i2400mu, buf, buf_len); ret = -EIO; - if (buf_len < sizeof(i2400m_NBOOT_BARKER)) + if (buf_len < sizeof(i2400m_ZERO_BARKER)) /* Not a bug, just ignore */ goto error_bad_size; - if (!memcmp(i2400m_NBOOT_BARKER, buf, sizeof(i2400m_NBOOT_BARKER)) - || !memcmp(i2400m_SBOOT_BARKER, buf, sizeof(i2400m_SBOOT_BARKER))) - ret = i2400m_dev_reset_handle(i2400m); - else if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) { + ret = 0; + if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) { i2400mu_rx_kick(i2400mu); - ret = 0; - } else { /* Unknown or unexpected data in the notif message */ - char prefix[64]; - ret = -EIO; - dev_err(dev, "HW BUG? Unknown/unexpected data in notification " - "message (%zu bytes)\n", buf_len); - snprintf(prefix, sizeof(prefix), "%s %s: ", - dev_driver_string(dev), dev_name(dev)); - if (buf_len > 64) { - print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, - 8, 4, buf, 64, 0); - printk(KERN_ERR "%s... (only first 64 bytes " - "dumped)\n", prefix); - } else - print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, - 8, 4, buf, buf_len, 0); + goto out; } + ret = i2400m_is_boot_barker(i2400m, buf, buf_len); + if (unlikely(ret >= 0)) + ret = i2400m_dev_reset_handle(i2400m, "device rebooted"); + else /* Unknown or unexpected data in the notif message */ + i2400m_unknown_barker(i2400m, buf, buf_len); error_bad_size: +out: d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n", i2400mu, buf, buf_len, ret); return ret; @@ -220,7 +210,8 @@ int i2400mu_notification_setup(struct i2400mu *i2400mu) dev_err(dev, "notification: cannot allocate URB\n"); goto error_alloc_urb; } - epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION); + epd = usb_get_epd(i2400mu->usb_iface, + i2400mu->endpoint_cfg.notification); usb_pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress); usb_fill_int_urb(i2400mu->notif_urb, i2400mu->usb_dev, usb_pipe, buf, I2400MU_MAX_NOTIFICATION_LEN, diff --git a/drivers/net/wimax/i2400m/usb-rx.c b/drivers/net/wimax/i2400m/usb-rx.c index a314799967cf..ba1b02362dfc 100644 --- a/drivers/net/wimax/i2400m/usb-rx.c +++ b/drivers/net/wimax/i2400m/usb-rx.c @@ -204,7 +204,7 @@ struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb) dev_err(dev, "RX: can't get autopm: %d\n", result); do_autopm = 0; } - epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_IN); + epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_in); usb_pipe = usb_rcvbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); retry: rx_size = skb_end_pointer(rx_skb) - rx_skb->data - rx_skb->len; @@ -214,7 +214,7 @@ retry: } result = usb_bulk_msg( i2400mu->usb_dev, usb_pipe, rx_skb->data + rx_skb->len, - rx_size, &read_size, HZ); + rx_size, &read_size, 200); usb_mark_last_busy(i2400mu->usb_dev); switch (result) { case 0: @@ -222,6 +222,26 @@ retry: goto retry; /* ZLP, just resubmit */ skb_put(rx_skb, read_size); break; + case -EPIPE: + /* + * Stall -- maybe the device is choking with our + * requests. Clear it and give it some time. If they + * happen to often, it might be another symptom, so we + * reset. + * + * No error handling for usb_clear_halt(0; if it + * works, the retry works; if it fails, this switch + * does the error handling for us. + */ + if (edc_inc(&i2400mu->urb_edc, + 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "BM-CMD: too many stalls in " + "URB; resetting device\n"); + goto do_reset; + } + usb_clear_halt(i2400mu->usb_dev, usb_pipe); + msleep(10); /* give the device some time */ + goto retry; case -EINVAL: /* while removing driver */ case -ENODEV: /* dev disconnect ... */ case -ENOENT: /* just ignore it */ @@ -283,6 +303,7 @@ out: error_reset: dev_err(dev, "RX: maximum errors in URB exceeded; " "resetting device\n"); +do_reset: usb_queue_reset_device(i2400mu->usb_iface); rx_skb = ERR_PTR(result); goto out; @@ -316,10 +337,15 @@ int i2400mu_rxd(void *_i2400mu) size_t pending; int rx_size; struct sk_buff *rx_skb; + unsigned long flags; d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); + spin_lock_irqsave(&i2400m->rx_lock, flags); + BUG_ON(i2400mu->rx_kthread != NULL); + i2400mu->rx_kthread = current; + spin_unlock_irqrestore(&i2400m->rx_lock, flags); while (1) { - d_printf(2, dev, "TX: waiting for messages\n"); + d_printf(2, dev, "RX: waiting for messages\n"); pending = 0; wait_event_interruptible( i2400mu->rx_wq, @@ -367,6 +393,9 @@ int i2400mu_rxd(void *_i2400mu) } result = 0; out: + spin_lock_irqsave(&i2400m->rx_lock, flags); + i2400mu->rx_kthread = NULL; + spin_unlock_irqrestore(&i2400m->rx_lock, flags); d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); return result; @@ -403,18 +432,33 @@ int i2400mu_rx_setup(struct i2400mu *i2400mu) struct i2400m *i2400m = &i2400mu->i2400m; struct device *dev = &i2400mu->usb_iface->dev; struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + struct task_struct *kthread; - i2400mu->rx_kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx", - wimax_dev->name); - if (IS_ERR(i2400mu->rx_kthread)) { - result = PTR_ERR(i2400mu->rx_kthread); + kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx", + wimax_dev->name); + /* the kthread function sets i2400mu->rx_thread */ + if (IS_ERR(kthread)) { + result = PTR_ERR(kthread); dev_err(dev, "RX: cannot start thread: %d\n", result); } return result; } + void i2400mu_rx_release(struct i2400mu *i2400mu) { - kthread_stop(i2400mu->rx_kthread); + unsigned long flags; + struct i2400m *i2400m = &i2400mu->i2400m; + struct device *dev = i2400m_dev(i2400m); + struct task_struct *kthread; + + spin_lock_irqsave(&i2400m->rx_lock, flags); + kthread = i2400mu->rx_kthread; + i2400mu->rx_kthread = NULL; + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + if (kthread) + kthread_stop(kthread); + else + d_printf(1, dev, "RX: kthread had already exited\n"); } diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c index dfd893356f49..c65b9979f87e 100644 --- a/drivers/net/wimax/i2400m/usb-tx.c +++ b/drivers/net/wimax/i2400m/usb-tx.c @@ -101,11 +101,11 @@ int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg, dev_err(dev, "TX: can't get autopm: %d\n", result); do_autopm = 0; } - epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT); + epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out); usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); retry: result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe, - tx_msg, tx_msg_size, &sent_size, HZ); + tx_msg, tx_msg_size, &sent_size, 200); usb_mark_last_busy(i2400mu->usb_dev); switch (result) { case 0: @@ -115,6 +115,28 @@ retry: result = -EIO; } break; + case -EPIPE: + /* + * Stall -- maybe the device is choking with our + * requests. Clear it and give it some time. If they + * happen to often, it might be another symptom, so we + * reset. + * + * No error handling for usb_clear_halt(0; if it + * works, the retry works; if it fails, this switch + * does the error handling for us. + */ + if (edc_inc(&i2400mu->urb_edc, + 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "BM-CMD: too many stalls in " + "URB; resetting device\n"); + usb_queue_reset_device(i2400mu->usb_iface); + /* fallthrough */ + } else { + usb_clear_halt(i2400mu->usb_dev, usb_pipe); + msleep(10); /* give the device some time */ + goto retry; + } case -EINVAL: /* while removing driver */ case -ENODEV: /* dev disconnect ... */ case -ENOENT: /* just ignore it */ @@ -161,9 +183,15 @@ int i2400mu_txd(void *_i2400mu) struct device *dev = &i2400mu->usb_iface->dev; struct i2400m_msg_hdr *tx_msg; size_t tx_msg_size; + unsigned long flags; d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); + spin_lock_irqsave(&i2400m->tx_lock, flags); + BUG_ON(i2400mu->tx_kthread != NULL); + i2400mu->tx_kthread = current; + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + while (1) { d_printf(2, dev, "TX: waiting for messages\n"); tx_msg = NULL; @@ -183,6 +211,11 @@ int i2400mu_txd(void *_i2400mu) if (result < 0) break; } + + spin_lock_irqsave(&i2400m->tx_lock, flags); + i2400mu->tx_kthread = NULL; + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); return result; } @@ -213,11 +246,13 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu) struct i2400m *i2400m = &i2400mu->i2400m; struct device *dev = &i2400mu->usb_iface->dev; struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + struct task_struct *kthread; - i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", - wimax_dev->name); - if (IS_ERR(i2400mu->tx_kthread)) { - result = PTR_ERR(i2400mu->tx_kthread); + kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", + wimax_dev->name); + /* the kthread function sets i2400mu->tx_thread */ + if (IS_ERR(kthread)) { + result = PTR_ERR(kthread); dev_err(dev, "TX: cannot start thread: %d\n", result); } return result; @@ -225,5 +260,17 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu) void i2400mu_tx_release(struct i2400mu *i2400mu) { - kthread_stop(i2400mu->tx_kthread); + unsigned long flags; + struct i2400m *i2400m = &i2400mu->i2400m; + struct device *dev = i2400m_dev(i2400m); + struct task_struct *kthread; + + spin_lock_irqsave(&i2400m->tx_lock, flags); + kthread = i2400mu->tx_kthread; + i2400mu->tx_kthread = NULL; + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + if (kthread) + kthread_stop(kthread); + else + d_printf(1, dev, "TX: kthread had already exited\n"); } diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 7eadd11c815b..47e84ef355c5 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c @@ -58,7 +58,7 @@ * i2400mu_rx_release() * i2400mu_tx_release() * - * i2400mu_bus_reset() Called by i2400m->bus_reset + * i2400mu_bus_reset() Called by i2400m_reset * __i2400mu_reset() * __i2400mu_send_barker() * usb_reset_device() @@ -71,13 +71,25 @@ #define D_SUBMODULE usb #include "usb-debug-levels.h" +static char i2400mu_debug_params[128]; +module_param_string(debug, i2400mu_debug_params, sizeof(i2400mu_debug_params), + 0644); +MODULE_PARM_DESC(debug, + "String of space-separated NAME:VALUE pairs, where NAMEs " + "are the different debug submodules and VALUE are the " + "initial debug value to set."); /* Our firmware file name */ -static const char *i2400mu_bus_fw_names[] = { +static const char *i2400mu_bus_fw_names_5x50[] = { #define I2400MU_FW_FILE_NAME_v1_4 "i2400m-fw-usb-1.4.sbcf" I2400MU_FW_FILE_NAME_v1_4, -#define I2400MU_FW_FILE_NAME_v1_3 "i2400m-fw-usb-1.3.sbcf" - I2400MU_FW_FILE_NAME_v1_3, + NULL, +}; + + +static const char *i2400mu_bus_fw_names_6050[] = { +#define I6050U_FW_FILE_NAME_v1_5 "i6050-fw-usb-1.5.sbcf" + I6050U_FW_FILE_NAME_v1_5, NULL, }; @@ -160,14 +172,59 @@ int __i2400mu_send_barker(struct i2400mu *i2400mu, epd = usb_get_epd(i2400mu->usb_iface, endpoint); pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); memcpy(buffer, barker, barker_size); +retry: ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size, - &actual_len, HZ); - if (ret < 0) { - if (ret != -EINVAL) - dev_err(dev, "E: barker error: %d\n", ret); - } else if (actual_len != barker_size) { - dev_err(dev, "E: only %d bytes transmitted\n", actual_len); - ret = -EIO; + &actual_len, 200); + switch (ret) { + case 0: + if (actual_len != barker_size) { /* Too short? drop it */ + dev_err(dev, "E: %s: short write (%d B vs %zu " + "expected)\n", + __func__, actual_len, barker_size); + ret = -EIO; + } + break; + case -EPIPE: + /* + * Stall -- maybe the device is choking with our + * requests. Clear it and give it some time. If they + * happen to often, it might be another symptom, so we + * reset. + * + * No error handling for usb_clear_halt(0; if it + * works, the retry works; if it fails, this switch + * does the error handling for us. + */ + if (edc_inc(&i2400mu->urb_edc, + 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "E: %s: too many stalls in " + "URB; resetting device\n", __func__); + usb_queue_reset_device(i2400mu->usb_iface); + /* fallthrough */ + } else { + usb_clear_halt(i2400mu->usb_dev, pipe); + msleep(10); /* give the device some time */ + goto retry; + } + case -EINVAL: /* while removing driver */ + case -ENODEV: /* dev disconnect ... */ + case -ENOENT: /* just ignore it */ + case -ESHUTDOWN: /* and exit */ + case -ECONNRESET: + ret = -ESHUTDOWN; + break; + default: /* Some error? */ + if (edc_inc(&i2400mu->urb_edc, + EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "E: %s: maximum errors in URB " + "exceeded; resetting device\n", + __func__); + usb_queue_reset_device(i2400mu->usb_iface); + } else { + dev_warn(dev, "W: %s: cannot send URB: %d\n", + __func__, ret); + goto retry; + } } kfree(buffer); error_kzalloc: @@ -232,15 +289,16 @@ int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt); if (rt == I2400M_RT_WARM) - result = __i2400mu_send_barker(i2400mu, i2400m_WARM_BOOT_BARKER, - sizeof(i2400m_WARM_BOOT_BARKER), - I2400MU_EP_BULK_OUT); + result = __i2400mu_send_barker( + i2400mu, i2400m_WARM_BOOT_BARKER, + sizeof(i2400m_WARM_BOOT_BARKER), + i2400mu->endpoint_cfg.bulk_out); else if (rt == I2400M_RT_COLD) - result = __i2400mu_send_barker(i2400mu, i2400m_COLD_BOOT_BARKER, - sizeof(i2400m_COLD_BOOT_BARKER), - I2400MU_EP_RESET_COLD); + result = __i2400mu_send_barker( + i2400mu, i2400m_COLD_BOOT_BARKER, + sizeof(i2400m_COLD_BOOT_BARKER), + i2400mu->endpoint_cfg.reset_cold); else if (rt == I2400M_RT_BUS) { -do_bus_reset: result = usb_reset_device(i2400mu->usb_dev); switch (result) { case 0: @@ -248,7 +306,7 @@ do_bus_reset: case -ENODEV: case -ENOENT: case -ESHUTDOWN: - result = rt == I2400M_RT_WARM ? -ENODEV : 0; + result = 0; break; /* We assume the device is disconnected */ default: dev_err(dev, "USB reset failed (%d), giving up!\n", @@ -261,10 +319,17 @@ do_bus_reset: if (result < 0 && result != -EINVAL /* device is gone */ && rt != I2400M_RT_BUS) { + /* + * Things failed -- resort to lower level reset, that + * we queue in another context; the reason for this is + * that the pre and post reset functionality requires + * the i2400m->init_mutex; RT_WARM and RT_COLD can + * come from areas where i2400m->init_mutex is taken. + */ dev_err(dev, "%s reset failed (%d); trying USB reset\n", rt == I2400M_RT_WARM ? "warm" : "cold", result); - rt = I2400M_RT_BUS; - goto do_bus_reset; + usb_queue_reset_device(i2400mu->usb_iface); + result = -ENODEV; } d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result); return result; @@ -402,20 +467,33 @@ int i2400mu_probe(struct usb_interface *iface, i2400m->bus_tx_block_size = I2400MU_BLK_SIZE; i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX; + i2400m->bus_setup = NULL; i2400m->bus_dev_start = i2400mu_bus_dev_start; i2400m->bus_dev_stop = i2400mu_bus_dev_stop; + i2400m->bus_release = NULL; i2400m->bus_tx_kick = i2400mu_bus_tx_kick; i2400m->bus_reset = i2400mu_bus_reset; - i2400m->bus_bm_retries = I2400M_BOOT_RETRIES; + i2400m->bus_bm_retries = I2400M_USB_BOOT_RETRIES; i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; - i2400m->bus_fw_names = i2400mu_bus_fw_names; i2400m->bus_bm_mac_addr_impaired = 0; + if (id->idProduct == USB_DEVICE_ID_I6050) { + i2400m->bus_fw_names = i2400mu_bus_fw_names_6050; + i2400mu->endpoint_cfg.bulk_out = 0; + i2400mu->endpoint_cfg.notification = 3; + i2400mu->endpoint_cfg.reset_cold = 2; + i2400mu->endpoint_cfg.bulk_in = 1; + } else { + i2400m->bus_fw_names = i2400mu_bus_fw_names_5x50; + i2400mu->endpoint_cfg.bulk_out = 0; + i2400mu->endpoint_cfg.notification = 1; + i2400mu->endpoint_cfg.reset_cold = 2; + i2400mu->endpoint_cfg.bulk_in = 3; + } #ifdef CONFIG_PM iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */ device_init_wakeup(dev, 1); - usb_autopm_enable(i2400mu->usb_iface); usb_dev->autosuspend_delay = 15 * HZ; usb_dev->autosuspend_disabled = 0; #endif @@ -483,7 +561,10 @@ void i2400mu_disconnect(struct usb_interface *iface) * So at the end, the three cases require common handling. * * If at the time of this call the device's firmware is not loaded, - * nothing has to be done. + * nothing has to be done. Note we can be "loose" about not reading + * i2400m->updown under i2400m->init_mutex. If it happens to change + * inmediately, other parts of the call flow will fail and effectively + * catch it. * * If the firmware is loaded, we need to: * @@ -522,6 +603,7 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) #endif d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); + rmb(); /* see i2400m->updown's documentation */ if (i2400m->updown == 0) goto no_firmware; if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { @@ -575,6 +657,7 @@ int i2400mu_resume(struct usb_interface *iface) struct i2400m *i2400m = &i2400mu->i2400m; d_fnstart(3, dev, "(iface %p)\n", iface); + rmb(); /* see i2400m->updown's documentation */ if (i2400m->updown == 0) { d_printf(1, dev, "fw was down, no resume neeed\n"); goto out; @@ -591,7 +674,54 @@ out: static +int i2400mu_reset_resume(struct usb_interface *iface) +{ + int result; + struct device *dev = &iface->dev; + struct i2400mu *i2400mu = usb_get_intfdata(iface); + struct i2400m *i2400m = &i2400mu->i2400m; + + d_fnstart(3, dev, "(iface %p)\n", iface); + result = i2400m_dev_reset_handle(i2400m, "device reset on resume"); + d_fnend(3, dev, "(iface %p) = %d\n", iface, result); + return result < 0 ? result : 0; +} + + +/* + * Another driver or user space is triggering a reset on the device + * which contains the interface passed as an argument. Cease IO and + * save any device state you need to restore. + * + * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if + * you are in atomic context. + */ +static +int i2400mu_pre_reset(struct usb_interface *iface) +{ + struct i2400mu *i2400mu = usb_get_intfdata(iface); + return i2400m_pre_reset(&i2400mu->i2400m); +} + + +/* + * The reset has completed. Restore any saved device state and begin + * using the device again. + * + * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if + * you are in atomic context. + */ +static +int i2400mu_post_reset(struct usb_interface *iface) +{ + struct i2400mu *i2400mu = usb_get_intfdata(iface); + return i2400m_post_reset(&i2400mu->i2400m); +} + + +static struct usb_device_id i2400mu_id_table[] = { + { USB_DEVICE(0x8086, USB_DEVICE_ID_I6050) }, { USB_DEVICE(0x8086, 0x0181) }, { USB_DEVICE(0x8086, 0x1403) }, { USB_DEVICE(0x8086, 0x1405) }, @@ -609,8 +739,11 @@ struct usb_driver i2400mu_driver = { .name = KBUILD_MODNAME, .suspend = i2400mu_suspend, .resume = i2400mu_resume, + .reset_resume = i2400mu_reset_resume, .probe = i2400mu_probe, .disconnect = i2400mu_disconnect, + .pre_reset = i2400mu_pre_reset, + .post_reset = i2400mu_post_reset, .id_table = i2400mu_id_table, .supports_autosuspend = 1, }; @@ -618,6 +751,8 @@ struct usb_driver i2400mu_driver = { static int __init i2400mu_driver_init(void) { + d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400mu_debug_params, + "i2400m_usb.debug"); return usb_register(&i2400mu_driver); } module_init(i2400mu_driver_init); @@ -632,7 +767,7 @@ void __exit i2400mu_driver_exit(void) module_exit(i2400mu_driver_exit); MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); -MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB"); +MODULE_DESCRIPTION("Driver for USB based Intel Wireless WiMAX Connection 2400M " + "(5x50 & 6050)"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_4); -MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_3); diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index d7a764a2fc1a..56dd6650c97a 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -5,6 +5,7 @@ menuconfig WLAN bool "Wireless LAN" depends on !S390 + select WIRELESS default y ---help--- This section contains all the pre 802.11 and 802.11 wireless @@ -15,114 +16,12 @@ menuconfig WLAN if WLAN -menuconfig WLAN_PRE80211 - bool "Wireless LAN (pre-802.11)" - depends on NETDEVICES - ---help--- - Say Y if you have any pre-802.11 wireless LAN hardware. - - This option does not affect the kernel build, it only - lets you choose drivers. - -config STRIP - tristate "STRIP (Metricom starmode radio IP)" - depends on INET && WLAN_PRE80211 - select WIRELESS_EXT - ---help--- - Say Y if you have a Metricom radio and intend to use Starmode Radio - IP. STRIP is a radio protocol developed for the MosquitoNet project - to send Internet traffic using Metricom radios. Metricom radios are - small, battery powered, 100kbit/sec packet radio transceivers, about - the size and weight of a cellular telephone. (You may also have heard - them called "Metricom modems" but we avoid the term "modem" because - it misleads many people into thinking that you can plug a Metricom - modem into a phone line and use it as a modem.) - - You can use STRIP on any Linux machine with a serial port, although - it is obviously most useful for people with laptop computers. If you - think you might get a Metricom radio in the future, there is no harm - in saying Y to STRIP now, except that it makes the kernel a bit - bigger. - - To compile this as a module, choose M here: the module will be - called strip. - -config ARLAN - tristate "Aironet Arlan 655 & IC2200 DS support" - depends on ISA && !64BIT && WLAN_PRE80211 - select WIRELESS_EXT - ---help--- - Aironet makes Arlan, a class of wireless LAN adapters. These use the - www.Telxon.com chip, which is also used on several similar cards. - This driver is tested on the 655 and IC2200 series cards. Look at - <http://www.ylenurme.ee/~elmer/655/> for the latest information. - - The driver is built as two modules, arlan and arlan-proc. The latter - is the /proc interface and is not needed most of time. - - On some computers the card ends up in non-valid state after some - time. Use a ping-reset script to clear it. - -config WAVELAN - tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" - depends on ISA && WLAN_PRE80211 - select WIRELESS_EXT - ---help--- - The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is - a Radio LAN (wireless Ethernet-like Local Area Network) using the - radio frequencies 900 MHz and 2.4 GHz. - - If you want to use an ISA WaveLAN card under Linux, say Y and read - the Ethernet-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. Some more specific - information is contained in - <file:Documentation/networking/wavelan.txt> and in the source code - <file:drivers/net/wireless/wavelan.p.h>. - - You will also need the wireless tools package available from - <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>. - Please read the man pages contained therein. - - To compile this driver as a module, choose M here: the module will be - called wavelan. - -config PCMCIA_WAVELAN - tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" - depends on PCMCIA && WLAN_PRE80211 - select WIRELESS_EXT - help - Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA - (PC-card) wireless Ethernet networking card to your computer. This - driver is for the non-IEEE-802.11 Wavelan cards. - - To compile this driver as a module, choose M here: the module will be - called wavelan_cs. If unsure, say N. - -config PCMCIA_NETWAVE - tristate "Xircom Netwave AirSurfer Pcmcia wireless support" - depends on PCMCIA && WLAN_PRE80211 - select WIRELESS_EXT - help - Say Y here if you intend to attach this type of PCMCIA (PC-card) - wireless Ethernet networking card to your computer. - - To compile this driver as a module, choose M here: the module will be - called netwave_cs. If unsure, say N. - - -menuconfig WLAN_80211 - bool "Wireless LAN (IEEE 802.11)" - depends on NETDEVICES - ---help--- - Say Y if you have any 802.11 wireless LAN hardware. - - This option does not affect the kernel build, it only - lets you choose drivers. - config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4GHz wireless support" - depends on PCMCIA && WLAN_80211 + depends on PCMCIA select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV ---help--- Say Y here if you intend to attach an Aviator/Raytheon PCMCIA (PC-card) wireless Ethernet networking card to your computer. @@ -132,49 +31,9 @@ config PCMCIA_RAYCS To compile this driver as a module, choose M here: the module will be called ray_cs. If unsure, say N. -config LIBERTAS - tristate "Marvell 8xxx Libertas WLAN driver support" - depends on WLAN_80211 - select WIRELESS_EXT - select LIB80211 - select FW_LOADER - ---help--- - A library for Marvell Libertas 8xxx devices. - -config LIBERTAS_USB - tristate "Marvell Libertas 8388 USB 802.11b/g cards" - depends on LIBERTAS && USB - ---help--- - A driver for Marvell Libertas 8388 USB devices. - -config LIBERTAS_CS - tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" - depends on LIBERTAS && PCMCIA - select FW_LOADER - ---help--- - A driver for Marvell Libertas 8385 CompactFlash devices. - -config LIBERTAS_SDIO - tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards" - depends on LIBERTAS && MMC - ---help--- - A driver for Marvell Libertas 8385/8686/8688 SDIO devices. - -config LIBERTAS_SPI - tristate "Marvell Libertas 8686 SPI 802.11b/g cards" - depends on LIBERTAS && SPI - ---help--- - A driver for Marvell Libertas 8686 SPI devices. - -config LIBERTAS_DEBUG - bool "Enable full debugging output in the Libertas module." - depends on LIBERTAS - ---help--- - Debugging support. - config LIBERTAS_THINFIRM tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware" - depends on WLAN_80211 && MAC80211 + depends on MAC80211 select FW_LOADER ---help--- A library for Marvell Libertas 8xxx devices using thinfirm. @@ -187,9 +46,11 @@ config LIBERTAS_THINFIRM_USB config AIRO tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" - depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN) + depends on ISA_DMA_API && (PCI || BROKEN) select WIRELESS_EXT select CRYPTO + select WEXT_SPY + select WEXT_PRIV ---help--- This is the standard Linux driver to support Cisco/Aironet ISA and PCI 802.11 wireless cards. @@ -205,8 +66,9 @@ config AIRO config ATMEL tristate "Atmel at76c50x chipset 802.11b support" - depends on (PCI || PCMCIA) && WLAN_80211 + depends on (PCI || PCMCIA) select WIRELESS_EXT + select WEXT_PRIV select FW_LOADER select CRC32 ---help--- @@ -239,7 +101,7 @@ config PCMCIA_ATMEL config AT76C50X_USB tristate "Atmel at76c503/at76c505/at76c505a USB cards" - depends on MAC80211 && WLAN_80211 && USB + depends on MAC80211 && USB select FW_LOADER ---help--- Enable support for USB Wireless devices using Atmel at76c503, @@ -247,8 +109,9 @@ config AT76C50X_USB config AIRO_CS tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" - depends on PCMCIA && (BROKEN || !M32R) && WLAN_80211 + depends on PCMCIA && (BROKEN || !M32R) select WIRELESS_EXT + select WEXT_SPY select CRYPTO select CRYPTO_AES ---help--- @@ -266,18 +129,21 @@ config AIRO_CS Cisco Linux utilities can be used to configure the card. config PCMCIA_WL3501 - tristate "Planet WL3501 PCMCIA cards" - depends on EXPERIMENTAL && PCMCIA && WLAN_80211 - select WIRELESS_EXT - ---help--- - A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. - It has basic support for Linux wireless extensions and initial - micro support for ethtool. + tristate "Planet WL3501 PCMCIA cards" + depends on EXPERIMENTAL && PCMCIA + select WIRELESS_EXT + select WEXT_SPY + help + A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. + It has basic support for Linux wireless extensions and initial + micro support for ethtool. config PRISM54 tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)' - depends on PCI && EXPERIMENTAL && WLAN_80211 + depends on PCI && EXPERIMENTAL select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV select FW_LOADER ---help--- This enables support for FullMAC PCI/Cardbus prism54 devices. This @@ -298,8 +164,9 @@ config PRISM54 config USB_ZD1201 tristate "USB ZD1201 based Wireless device support" - depends on USB && WLAN_80211 + depends on USB select WIRELESS_EXT + select WEXT_PRIV select FW_LOADER ---help--- Say Y if you want to use wireless LAN adapters based on the ZyDAS @@ -316,7 +183,7 @@ config USB_ZD1201 config USB_NET_RNDIS_WLAN tristate "Wireless RNDIS USB support" - depends on USB && WLAN_80211 && EXPERIMENTAL + depends on USB && EXPERIMENTAL depends on CFG80211 select USB_USBNET select USB_NET_CDCETHER @@ -344,7 +211,7 @@ config USB_NET_RNDIS_WLAN config RTL8180 tristate "Realtek 8180/8185 PCI support" - depends on MAC80211 && PCI && WLAN_80211 && EXPERIMENTAL + depends on MAC80211 && PCI && EXPERIMENTAL select EEPROM_93CX6 ---help--- This is a driver for RTL8180 and RTL8185 based cards. @@ -400,7 +267,7 @@ config RTL8180 config RTL8187 tristate "Realtek 8187 and 8187B USB support" - depends on MAC80211 && USB && WLAN_80211 + depends on MAC80211 && USB select EEPROM_93CX6 ---help--- This is a driver for RTL8187 and RTL8187B based cards. @@ -429,7 +296,7 @@ config RTL8187_LEDS config ADM8211 tristate "ADMtek ADM8211 support" - depends on MAC80211 && PCI && WLAN_80211 && EXPERIMENTAL + depends on MAC80211 && PCI && EXPERIMENTAL select CRC32 select EEPROM_93CX6 ---help--- @@ -456,7 +323,7 @@ config ADM8211 config MAC80211_HWSIM tristate "Simulated radio testing tool for mac80211" - depends on MAC80211 && WLAN_80211 + depends on MAC80211 ---help--- This driver is a developer testing tool that can be used to test IEEE 802.11 networking stack (mac80211) functionality. This is not @@ -469,24 +336,25 @@ config MAC80211_HWSIM config MWL8K tristate "Marvell 88W8xxx PCI/PCIe Wireless support" - depends on MAC80211 && PCI && WLAN_80211 && EXPERIMENTAL + depends on MAC80211 && PCI && EXPERIMENTAL ---help--- This driver supports Marvell TOPDOG 802.11 wireless cards. To compile this driver as a module, choose M here: the module will be called mwl8k. If unsure, say N. -source "drivers/net/wireless/p54/Kconfig" source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/ipw2x00/Kconfig" -source "drivers/net/wireless/iwlwifi/Kconfig" -source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/b43/Kconfig" source "drivers/net/wireless/b43legacy/Kconfig" -source "drivers/net/wireless/zd1211rw/Kconfig" -source "drivers/net/wireless/rt2x00/Kconfig" +source "drivers/net/wireless/hostap/Kconfig" +source "drivers/net/wireless/ipw2x00/Kconfig" +source "drivers/net/wireless/iwlwifi/Kconfig" +source "drivers/net/wireless/iwmc3200wifi/Kconfig" +source "drivers/net/wireless/libertas/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" +source "drivers/net/wireless/p54/Kconfig" +source "drivers/net/wireless/rt2x00/Kconfig" source "drivers/net/wireless/wl12xx/Kconfig" -source "drivers/net/wireless/iwmc3200wifi/Kconfig" +source "drivers/net/wireless/zd1211rw/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 7a4647e78fd3..5d4ce4d2b32b 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -5,16 +5,6 @@ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ -obj-$(CONFIG_STRIP) += strip.o -obj-$(CONFIG_ARLAN) += arlan.o - -arlan-objs := arlan-main.o arlan-proc.o - -# Obsolete cards -obj-$(CONFIG_WAVELAN) += wavelan.o -obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o -obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o - obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_AIRO) += airo.o diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index b80f514877d8..39410016b4ff 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1538,7 +1538,7 @@ static int adm8211_start(struct ieee80211_hw *dev) adm8211_hw_init(dev); adm8211_rf_set_channel(dev, priv->channel); - retval = request_irq(priv->pdev->irq, &adm8211_interrupt, + retval = request_irq(priv->pdev->irq, adm8211_interrupt, IRQF_SHARED, "adm8211", dev); if (retval) { printk(KERN_ERR "%s: failed to register IRQ handler\n", diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index abf896a7390e..4331d675fcc6 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -4806,7 +4806,7 @@ static int airo_config_commit(struct net_device *dev, static inline int sniffing_mode(struct airo_info *ai) { - return le16_to_cpu(ai->config.rmode & RXMODE_MASK) >= + return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >= le16_to_cpu(RXMODE_RFMON); } @@ -5659,7 +5659,8 @@ static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state) pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); pci_save_state(pdev); - return pci_set_power_state(pdev, pci_choose_state(pdev, state)); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; } static int airo_pci_resume(struct pci_dev *pdev) diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 8e1a55dec351..2517364d3ebe 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -121,6 +121,14 @@ static struct fwentry firmwares[] = { [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" }, [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" }, }; +MODULE_FIRMWARE("atmel_at76c503-i3861.bin"); +MODULE_FIRMWARE("atmel_at76c503-i3863.bin"); +MODULE_FIRMWARE("atmel_at76c503-rfmd.bin"); +MODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin"); +MODULE_FIRMWARE("atmel_at76c505-rfmd.bin"); +MODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin"); +MODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin"); +MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin"); #define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) @@ -524,20 +532,6 @@ static char *hex2str(void *buf, int len) return ret; } -#define MAC2STR_BUFFERS 4 - -static inline char *mac2str(u8 *mac) -{ - static atomic_t a = ATOMIC_INIT(0); - static char bufs[MAC2STR_BUFFERS][6 * 3]; - char *str; - - str = bufs[atomic_inc_return(&a) & (MAC2STR_BUFFERS - 1)]; - sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return str; -} - /* LED trigger */ static int tx_activity; static void at76_ledtrig_tx_timerfunc(unsigned long data); @@ -973,13 +967,13 @@ static void at76_dump_mib_mac_addr(struct at76_priv *priv) goto exit; } - at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %s res 0x%x 0x%x", + at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x", wiphy_name(priv->hw->wiphy), - mac2str(m->mac_addr), m->res[0], m->res[1]); + m->mac_addr, m->res[0], m->res[1]); for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) - at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %s, " + at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, " "status %d", wiphy_name(priv->hw->wiphy), i, - mac2str(m->group_addr[i]), m->group_addr_status[i]); + m->group_addr[i], m->group_addr_status[i]); exit: kfree(m); } @@ -1042,7 +1036,7 @@ static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration " "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d " "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " - "current_bssid %s current_essid %s current_bss_type %d " + "current_bssid %pM current_essid %s current_bss_type %d " "pm_mode %d ibss_change %d res %d " "multi_domain_capability_implemented %d " "international_roaming %d country_string %.3s", @@ -1051,7 +1045,7 @@ static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) le16_to_cpu(m->medium_occupancy_limit), le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), m->CFP_mode, m->privacy_option_implemented, m->DTIM_period, - m->CFP_period, mac2str(m->current_bssid), + m->CFP_period, m->current_bssid, hex2str(m->current_essid, IW_ESSID_MAX_SIZE), m->current_bss_type, m->power_mgmt_mode, m->ibss_change, m->res, m->multi_domain_capability_implemented, @@ -1080,7 +1074,7 @@ static void at76_dump_mib_mac(struct at76_priv *priv) "cwmin %d cwmax %d short_retry_time %d long_retry_time %d " "scan_type %d scan_channel %d probe_delay %u " "min_channel_time %d max_channel_time %d listen_int %d " - "desired_ssid %s desired_bssid %s desired_bsstype %d", + "desired_ssid %s desired_bssid %pM desired_bsstype %d", wiphy_name(priv->hw->wiphy), le32_to_cpu(m->max_tx_msdu_lifetime), le32_to_cpu(m->max_rx_lifetime), @@ -1092,7 +1086,7 @@ static void at76_dump_mib_mac(struct at76_priv *priv) le16_to_cpu(m->max_channel_time), le16_to_cpu(m->listen_interval), hex2str(m->desired_ssid, IW_ESSID_MAX_SIZE), - mac2str(m->desired_bssid), m->desired_bsstype); + m->desired_bssid, m->desired_bsstype); exit: kfree(m); } @@ -1194,6 +1188,9 @@ static int at76_start_monitor(struct at76_priv *priv) scan.channel = priv->channel; scan.scan_type = SCAN_TYPE_PASSIVE; scan.international_scan = 0; + scan.min_channel_time = cpu_to_le16(priv->scan_min_time); + scan.max_channel_time = cpu_to_le16(priv->scan_max_time); + scan.probe_delay = cpu_to_le16(0); ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); if (ret >= 0) @@ -2217,6 +2214,8 @@ static struct ieee80211_supported_band at76_supported_band = { static int at76_init_new_device(struct at76_priv *priv, struct usb_interface *interface) { + struct wiphy *wiphy; + size_t len; int ret; /* set up the endpoint information */ @@ -2254,6 +2253,7 @@ static int at76_init_new_device(struct at76_priv *priv, priv->device_unplugged = 0; /* mac80211 initialisation */ + wiphy = priv->hw->wiphy; priv->hw->wiphy->max_scan_ssids = 1; priv->hw->wiphy->max_scan_ie_len = 0; priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); @@ -2265,6 +2265,13 @@ static int at76_init_new_device(struct at76_priv *priv, SET_IEEE80211_DEV(priv->hw, &interface->dev); SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); + len = sizeof(wiphy->fw_version); + snprintf(wiphy->fw_version, len, "%d.%d.%d-%d", + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); + + wiphy->hw_version = priv->board_type; + ret = ieee80211_register_hw(priv->hw); if (ret) { printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", @@ -2274,9 +2281,9 @@ static int at76_init_new_device(struct at76_priv *priv, priv->mac80211_registered = 1; - printk(KERN_INFO "%s: USB %s, MAC %s, firmware %d.%d.%d-%d\n", + printk(KERN_INFO "%s: USB %s, MAC %pM, firmware %d.%d.%d-%d\n", wiphy_name(priv->hw->wiphy), - dev_name(&interface->dev), mac2str(priv->mac_addr), + dev_name(&interface->dev), priv->mac_addr, priv->fw_version.major, priv->fw_version.minor, priv->fw_version.patch, priv->fw_version.build); printk(KERN_INFO "%s: regulatory domain 0x%02x: %s\n", diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 11ded150b932..4e7a7fd695c8 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -1,6 +1,5 @@ menuconfig ATH_COMMON tristate "Atheros Wireless Cards" - depends on WLAN_80211 depends on CFG80211 ---help--- This will enable the support for the Atheros wireless drivers. @@ -16,7 +15,15 @@ menuconfig ATH_COMMON http://wireless.kernel.org/en/users/Drivers/Atheros if ATH_COMMON + +config ATH_DEBUG + bool "Atheros wireless debugging" + ---help--- + Say Y, if you want to debug atheros wireless drivers. + Right now only ath9k makes use of this. + source "drivers/net/wireless/ath/ath5k/Kconfig" source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/ar9170/Kconfig" + endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 4bb0132ada37..8113a5042afa 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -1,6 +1,11 @@ obj-$(CONFIG_ATH5K) += ath5k/ -obj-$(CONFIG_ATH9K) += ath9k/ +obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_AR9170_USB) += ar9170/ obj-$(CONFIG_ATH_COMMON) += ath.o -ath-objs := main.o regd.o + +ath-objs := main.o \ + regd.o \ + hw.o + +ath-$(CONFIG_ATH_DEBUG) += debug.o diff --git a/drivers/net/wireless/ath/ar9170/Kconfig b/drivers/net/wireless/ath/ar9170/Kconfig index 05918f1e685a..d7a4799d20fb 100644 --- a/drivers/net/wireless/ath/ar9170/Kconfig +++ b/drivers/net/wireless/ath/ar9170/Kconfig @@ -1,6 +1,6 @@ config AR9170_USB tristate "Atheros AR9170 802.11n USB support" - depends on USB && MAC80211 && WLAN_80211 + depends on USB && MAC80211 select FW_LOADER help This is a driver for the Atheros "otus" 802.11n USB devices. diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h index 914e4718a9a8..9f9459860d82 100644 --- a/drivers/net/wireless/ath/ar9170/ar9170.h +++ b/drivers/net/wireless/ath/ar9170/ar9170.h @@ -172,8 +172,6 @@ struct ar9170 { /* interface mode settings */ struct ieee80211_vif *vif; - u8 mac_addr[ETH_ALEN]; - u8 bssid[ETH_ALEN]; /* beaconing */ struct sk_buff *beacon; @@ -204,6 +202,8 @@ struct ar9170 { u8 power_2G_ht20[8]; u8 power_2G_ht40[8]; + u8 phy_heavy_clip; + #ifdef CONFIG_AR9170_LEDS struct delayed_work led_work; struct ar9170_led leds[AR9170_NUM_LEDS]; @@ -231,7 +231,7 @@ struct ar9170 { struct sk_buff_head tx_status_ampdu; spinlock_t tx_ampdu_list_lock; struct list_head tx_ampdu_list; - unsigned int tx_ampdu_pending; + atomic_t tx_ampdu_pending; /* rxstream mpdu merge */ struct ar9170_rxstream_mpdu_merge rx_mpdu; diff --git a/drivers/net/wireless/ath/ar9170/cmd.c b/drivers/net/wireless/ath/ar9170/cmd.c index f57a6200167b..cf6f5c4174a6 100644 --- a/drivers/net/wireless/ath/ar9170/cmd.c +++ b/drivers/net/wireless/ath/ar9170/cmd.c @@ -72,8 +72,7 @@ int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val) return err; } -static int ar9170_read_mreg(struct ar9170 *ar, int nregs, - const u32 *regs, u32 *out) +int ar9170_read_mreg(struct ar9170 *ar, int nregs, const u32 *regs, u32 *out) { int i, err; __le32 *offs, *res; diff --git a/drivers/net/wireless/ath/ar9170/cmd.h b/drivers/net/wireless/ath/ar9170/cmd.h index a4f0e50e52b4..826c45e6b274 100644 --- a/drivers/net/wireless/ath/ar9170/cmd.h +++ b/drivers/net/wireless/ath/ar9170/cmd.h @@ -44,6 +44,7 @@ int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len); int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val); int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val); +int ar9170_read_mreg(struct ar9170 *ar, int nregs, const u32 *regs, u32 *out); int ar9170_echo_test(struct ar9170 *ar, u32 v); /* diff --git a/drivers/net/wireless/ath/ar9170/hw.h b/drivers/net/wireless/ath/ar9170/hw.h index 6cbfb2f83391..701ddb7d8400 100644 --- a/drivers/net/wireless/ath/ar9170/hw.h +++ b/drivers/net/wireless/ath/ar9170/hw.h @@ -152,14 +152,14 @@ enum ar9170_cmd { #define AR9170_MAC_REG_FTF_BIT14 BIT(14) #define AR9170_MAC_REG_FTF_BIT15 BIT(15) #define AR9170_MAC_REG_FTF_BAR BIT(24) -#define AR9170_MAC_REG_FTF_BIT25 BIT(25) +#define AR9170_MAC_REG_FTF_BA BIT(25) #define AR9170_MAC_REG_FTF_PSPOLL BIT(26) #define AR9170_MAC_REG_FTF_RTS BIT(27) #define AR9170_MAC_REG_FTF_CTS BIT(28) #define AR9170_MAC_REG_FTF_ACK BIT(29) #define AR9170_MAC_REG_FTF_CFE BIT(30) #define AR9170_MAC_REG_FTF_CFE_ACK BIT(31) -#define AR9170_MAC_REG_FTF_DEFAULTS 0x0500ffff +#define AR9170_MAC_REG_FTF_DEFAULTS 0x0700ffff #define AR9170_MAC_REG_FTF_MONITOR 0xfd00ffff #define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6A0) @@ -311,6 +311,8 @@ struct ar9170_tx_control { #define AR9170_TX_PHY_SHORT_GI 0x80000000 +#define AR5416_MAX_RATE_POWER 63 + struct ar9170_rx_head { u8 plcp[12]; } __packed; diff --git a/drivers/net/wireless/ath/ar9170/mac.c b/drivers/net/wireless/ath/ar9170/mac.c index 614e3218a2bc..ddc8c09dc79e 100644 --- a/drivers/net/wireless/ath/ar9170/mac.c +++ b/drivers/net/wireless/ath/ar9170/mac.c @@ -35,6 +35,9 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <asm/unaligned.h> + #include "ar9170.h" #include "cmd.h" @@ -227,11 +230,8 @@ static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac) ar9170_regwrite_begin(ar); - ar9170_regwrite(reg, - (mac[3] << 24) | (mac[2] << 16) | - (mac[1] << 8) | mac[0]); - - ar9170_regwrite(reg + 4, (mac[5] << 8) | mac[4]); + ar9170_regwrite(reg, get_unaligned_le32(mac)); + ar9170_regwrite(reg + 4, get_unaligned_le16(mac + 4)); ar9170_regwrite_finish(); @@ -311,13 +311,14 @@ static int ar9170_set_promiscouous(struct ar9170 *ar) int ar9170_set_operating_mode(struct ar9170 *ar) { + struct ath_common *common = &ar->common; u32 pm_mode = AR9170_MAC_REG_POWERMGT_DEFAULTS; u8 *mac_addr, *bssid; int err; if (ar->vif) { - mac_addr = ar->mac_addr; - bssid = ar->bssid; + mac_addr = common->macaddr; + bssid = common->curbssid; switch (ar->vif->type) { case NL80211_IFTYPE_MESH_POINT: diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index c1f8c69db165..f9d6db8d013e 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -414,9 +414,9 @@ static void ar9170_tx_ampdu_callback(struct ar9170 *ar, struct sk_buff *skb) skb_queue_tail(&ar->tx_status_ampdu, skb); ar9170_tx_fake_ampdu_status(ar); - ar->tx_ampdu_pending--; - if (!list_empty(&ar->tx_ampdu_list) && !ar->tx_ampdu_pending) + if (atomic_dec_and_test(&ar->tx_ampdu_pending) && + !list_empty(&ar->tx_ampdu_list)) ar9170_tx_ampdu(ar); } @@ -850,6 +850,7 @@ static int ar9170_rx_mac_status(struct ar9170 *ar, } break; + case AR9170_RX_STATUS_MODULATION_DUPOFDM: case AR9170_RX_STATUS_MODULATION_OFDM: switch (head->plcp[0] & 0xf) { case 0xb: @@ -897,8 +898,7 @@ static int ar9170_rx_mac_status(struct ar9170 *ar, status->flag |= RX_FLAG_HT; break; - case AR9170_RX_STATUS_MODULATION_DUPOFDM: - /* XXX */ + default: if (ar9170_nag_limiter(ar)) printk(KERN_ERR "%s: invalid modulation\n", wiphy_name(ar->hw->wiphy)); @@ -1248,6 +1248,7 @@ static int ar9170_op_start(struct ieee80211_hw *hw) ar->global_ampdu_density = 6; ar->global_ampdu_factor = 3; + atomic_set(&ar->tx_ampdu_pending, 0); ar->bad_hw_nagger = jiffies; err = ar->open(ar); @@ -1773,7 +1774,7 @@ static void ar9170_tx(struct ar9170 *ar) msecs_to_jiffies(AR9170_TX_TIMEOUT); if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK) - ar->tx_ampdu_pending++; + atomic_inc(&ar->tx_ampdu_pending); #ifdef AR9170_QUEUE_DEBUG printk(KERN_DEBUG "%s: send frame q:%d =>\n", @@ -1784,7 +1785,7 @@ static void ar9170_tx(struct ar9170 *ar) err = ar->tx(ar, skb); if (unlikely(err)) { if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK) - ar->tx_ampdu_pending--; + atomic_dec(&ar->tx_ampdu_pending); frames_failed++; dev_kfree_skb_any(skb); @@ -1931,7 +1932,7 @@ int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (info->flags & IEEE80211_TX_CTL_AMPDU) { bool run = ar9170_tx_ampdu_queue(ar, skb); - if (run || !ar->tx_ampdu_pending) + if (run || !atomic_read(&ar->tx_ampdu_pending)) ar9170_tx_ampdu(ar); } else { unsigned int queue = skb_get_queue_mapping(skb); @@ -1952,6 +1953,7 @@ static int ar9170_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) { struct ar9170 *ar = hw->priv; + struct ath_common *common = &ar->common; int err = 0; mutex_lock(&ar->mutex); @@ -1962,7 +1964,7 @@ static int ar9170_op_add_interface(struct ieee80211_hw *hw, } ar->vif = conf->vif; - memcpy(ar->mac_addr, conf->mac_addr, ETH_ALEN); + memcpy(common->macaddr, conf->mac_addr, ETH_ALEN); if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) { ar->rx_software_decryption = true; @@ -2131,12 +2133,13 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, u32 changed) { struct ar9170 *ar = hw->priv; + struct ath_common *common = &ar->common; int err = 0; mutex_lock(&ar->mutex); if (changed & BSS_CHANGED_BSSID) { - memcpy(ar->bssid, bss_conf->bssid, ETH_ALEN); + memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); err = ar9170_set_operating_mode(ar); if (err) goto out; @@ -2190,22 +2193,30 @@ static u64 ar9170_op_get_tsf(struct ieee80211_hw *hw) { struct ar9170 *ar = hw->priv; int err; - u32 tsf_low; - u32 tsf_high; u64 tsf; +#define NR 3 + static const u32 addr[NR] = { AR9170_MAC_REG_TSF_H, + AR9170_MAC_REG_TSF_L, + AR9170_MAC_REG_TSF_H }; + u32 val[NR]; + int loops = 0; mutex_lock(&ar->mutex); - err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_L, &tsf_low); - if (!err) - err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_H, &tsf_high); + + while (loops++ < 10) { + err = ar9170_read_mreg(ar, NR, addr, val); + if (err || val[0] == val[2]) + break; + } + mutex_unlock(&ar->mutex); if (WARN_ON(err)) return 0; - - tsf = tsf_high; - tsf = (tsf << 32) | tsf_low; + tsf = val[0]; + tsf = (tsf << 32) | val[1]; return tsf; +#undef NR } static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -2430,6 +2441,7 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue, } static int ar9170_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { @@ -2459,7 +2471,7 @@ static int ar9170_ampdu_action(struct ieee80211_hw *hw, tid_info->state = AR9170_TID_STATE_PROGRESS; tid_info->active = false; spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); - ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: @@ -2469,7 +2481,7 @@ static int ar9170_ampdu_action(struct ieee80211_hw *hw, tid_info->active = false; skb_queue_purge(&tid_info->queue); spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); - ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: diff --git a/drivers/net/wireless/ath/ar9170/phy.c b/drivers/net/wireless/ath/ar9170/phy.c index dbd488da18b1..45a415ea809a 100644 --- a/drivers/net/wireless/ath/ar9170/phy.c +++ b/drivers/net/wireless/ath/ar9170/phy.c @@ -1239,9 +1239,6 @@ static u8 ar9170_get_max_edge_power(struct ar9170 *ar, struct ar9170_calctl_edges edges[], u32 freq) { -/* TODO: move somewhere else */ -#define AR5416_MAX_RATE_POWER 63 - int i; u8 rc = AR5416_MAX_RATE_POWER; u8 f; @@ -1259,10 +1256,11 @@ static u8 ar9170_get_max_edge_power(struct ar9170 *ar, break; } if (i > 0 && f < edges[i].channel) { - if (f > edges[i-1].channel && - edges[i-1].power_flags & AR9170_CALCTL_EDGE_FLAGS) { + if (f > edges[i - 1].channel && + edges[i - 1].power_flags & + AR9170_CALCTL_EDGE_FLAGS) { /* lower channel has the inband flag set */ - rc = edges[i-1].power_flags & + rc = edges[i - 1].power_flags & ~AR9170_CALCTL_EDGE_FLAGS; } break; @@ -1270,18 +1268,48 @@ static u8 ar9170_get_max_edge_power(struct ar9170 *ar, } if (i == AR5416_NUM_BAND_EDGES) { - if (f > edges[i-1].channel && - edges[i-1].power_flags & AR9170_CALCTL_EDGE_FLAGS) { + if (f > edges[i - 1].channel && + edges[i - 1].power_flags & AR9170_CALCTL_EDGE_FLAGS) { /* lower channel has the inband flag set */ - rc = edges[i-1].power_flags & + rc = edges[i - 1].power_flags & ~AR9170_CALCTL_EDGE_FLAGS; } } return rc; } -/* calculate the conformance test limits and apply them to ar->power* - * (derived from otus hal/hpmain.c, line 3706 ff.) +static u8 ar9170_get_heavy_clip(struct ar9170 *ar, + struct ar9170_calctl_edges edges[], + u32 freq, enum ar9170_bw bw) +{ + u8 f; + int i; + u8 rc = 0; + + if (freq < 3000) + f = freq - 2300; + else + f = (freq - 4800) / 5; + + if (bw == AR9170_BW_40_BELOW || bw == AR9170_BW_40_ABOVE) + rc |= 0xf0; + + for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) { + if (edges[i].channel == 0xff) + break; + if (f == edges[i].channel) { + if (!(edges[i].power_flags & AR9170_CALCTL_EDGE_FLAGS)) + rc |= 0x0f; + break; + } + } + + return rc; +} + +/* + * calculate the conformance test limits and the heavy clip parameter + * and apply them to ar->power* (derived from otus hal/hpmain.c, line 3706) */ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) { @@ -1295,7 +1323,8 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) int pwr_cal_len; } *modes; - /* order is relevant in the mode_list_*: we fall back to the + /* + * order is relevant in the mode_list_*: we fall back to the * lower indices if any mode is missed in the EEPROM. */ struct ctl_modes mode_list_2ghz[] = { @@ -1313,7 +1342,10 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) #define EDGES(c, n) (ar->eeprom.ctl_data[c].control_edges[n]) - /* TODO: investigate the differences between OTUS' + ar->phy_heavy_clip = 0; + + /* + * TODO: investigate the differences between OTUS' * hpreg.c::zfHpGetRegulatoryDomain() and * ath/regd.c::ath_regd_get_band_ctl() - * e.g. for FCC3_WORLD the OTUS procedure @@ -1347,6 +1379,15 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) if (ctl_idx < AR5416_NUM_CTLS) { int f_off = 0; + /* determine heav clip parameter from + the 11G edges array */ + if (modes[i].ctl_mode == CTL_11G) { + ar->phy_heavy_clip = + ar9170_get_heavy_clip(ar, + EDGES(ctl_idx, 1), + freq, bw); + } + /* adjust freq for 40MHz */ if (modes[i].ctl_mode == CTL_2GHT40 || modes[i].ctl_mode == CTL_5GHT40) { @@ -1360,13 +1401,15 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) ar9170_get_max_edge_power(ar, EDGES(ctl_idx, 1), freq+f_off); - /* TODO: check if the regulatory max. power is + /* + * TODO: check if the regulatory max. power is * controlled by cfg80211 for DFS * (hpmain applies it to max_power itself for DFS freq) */ } else { - /* Workaround in otus driver, hpmain.c, line 3906: + /* + * Workaround in otus driver, hpmain.c, line 3906: * if no data for 5GHT20 are found, take the * legacy 5G value. * We extend this here to fallback from any other *HT or @@ -1390,6 +1433,19 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) modes[i].max_power); } } + + if (ar->phy_heavy_clip & 0xf0) { + ar->power_2G_ht40[0]--; + ar->power_2G_ht40[1]--; + ar->power_2G_ht40[2]--; + } + if (ar->phy_heavy_clip & 0xf) { + ar->power_2G_ht20[0]++; + ar->power_2G_ht20[1]++; + ar->power_2G_ht20[2]++; + } + + #undef EDGES } @@ -1499,8 +1555,6 @@ static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) /* calc. conformance test limits and apply to ar->power*[] */ ar9170_calc_ctl(ar, freq, bw); - /* TODO: (heavy clip) regulatory domain power level fine-tuning. */ - /* set ACK/CTS TX power */ ar9170_regwrite_begin(ar); @@ -1643,6 +1697,17 @@ int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, if (err) return err; + if (ar->phy_heavy_clip) { + err = ar9170_write_reg(ar, 0x1c59e0, + 0x200 | ar->phy_heavy_clip); + if (err) { + if (ar9170_nag_limiter(ar)) + printk(KERN_ERR "%s: failed to set " + "heavy clip\n", + wiphy_name(ar->hw->wiphy)); + } + } + for (i = 0; i < 2; i++) { ar->noise[i] = ar9170_calc_noise_dbm( (le32_to_cpu(vals[2 + i]) >> 19) & 0x1ff); diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c index e974e5829e1a..e0799d924057 100644 --- a/drivers/net/wireless/ath/ar9170/usb.c +++ b/drivers/net/wireless/ath/ar9170/usb.c @@ -68,8 +68,10 @@ static struct usb_device_id ar9170_usb_ids[] = { { USB_DEVICE(0x0cf3, 0x1002) }, /* Cace Airpcap NX */ { USB_DEVICE(0xcace, 0x0300) }, - /* D-Link DWA 160A */ + /* D-Link DWA 160 A1 */ { USB_DEVICE(0x07d1, 0x3c10) }, + /* D-Link DWA 160 A2 */ + { USB_DEVICE(0x07d1, 0x3a09) }, /* Netgear WNDA3100 */ { USB_DEVICE(0x0846, 0x9010) }, /* Netgear WN111 v2 */ @@ -108,15 +110,15 @@ static void ar9170_usb_submit_urb(struct ar9170_usb *aru) return ; spin_lock_irqsave(&aru->tx_urb_lock, flags); - if (aru->tx_submitted_urbs >= AR9170_NUM_TX_URBS) { + if (atomic_read(&aru->tx_submitted_urbs) >= AR9170_NUM_TX_URBS) { spin_unlock_irqrestore(&aru->tx_urb_lock, flags); return ; } - aru->tx_submitted_urbs++; + atomic_inc(&aru->tx_submitted_urbs); urb = usb_get_from_anchor(&aru->tx_pending); if (!urb) { - aru->tx_submitted_urbs--; + atomic_dec(&aru->tx_submitted_urbs); spin_unlock_irqrestore(&aru->tx_urb_lock, flags); return ; @@ -133,7 +135,7 @@ static void ar9170_usb_submit_urb(struct ar9170_usb *aru) err); usb_unanchor_urb(urb); - aru->tx_submitted_urbs--; + atomic_dec(&aru->tx_submitted_urbs); ar9170_tx_callback(&aru->common, urb->context); } @@ -151,7 +153,7 @@ static void ar9170_usb_tx_urb_complete_frame(struct urb *urb) return ; } - aru->tx_submitted_urbs--; + atomic_dec(&aru->tx_submitted_urbs); ar9170_tx_callback(&aru->common, skb); @@ -794,7 +796,7 @@ static int ar9170_usb_probe(struct usb_interface *intf, spin_lock_init(&aru->tx_urb_lock); aru->tx_pending_urbs = 0; - aru->tx_submitted_urbs = 0; + atomic_set(&aru->tx_submitted_urbs, 0); aru->common.stop = ar9170_usb_stop; aru->common.flush = ar9170_usb_flush; diff --git a/drivers/net/wireless/ath/ar9170/usb.h b/drivers/net/wireless/ath/ar9170/usb.h index d098f4d5d2f2..a2ce3b169ceb 100644 --- a/drivers/net/wireless/ath/ar9170/usb.h +++ b/drivers/net/wireless/ath/ar9170/usb.h @@ -67,7 +67,7 @@ struct ar9170_usb { bool req_one_stage_fw; spinlock_t tx_urb_lock; - unsigned int tx_submitted_urbs; + atomic_t tx_submitted_urbs; unsigned int tx_pending_urbs; struct completion cmd_wait; diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index a63e90cbf9e5..9e05648356fe 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -18,6 +18,35 @@ #define ATH_H #include <linux/skbuff.h> +#include <linux/if_ether.h> +#include <net/mac80211.h> + +/* + * The key cache is used for h/w cipher state and also for + * tracking station state such as the current tx antenna. + * We also setup a mapping table between key cache slot indices + * and station state to short-circuit node lookups on rx. + * Different parts have different size key caches. We handle + * up to ATH_KEYMAX entries (could dynamically allocate state). + */ +#define ATH_KEYMAX 128 /* max key cache size we handle */ + +static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +struct ath_ani { + bool caldone; + int16_t noise_floor; + unsigned int longcal_timer; + unsigned int shortcal_timer; + unsigned int resetcal_timer; + unsigned int checkani_timer; + struct timer_list timer; +}; + +enum ath_device_state { + ATH_HW_UNAVAILABLE, + ATH_HW_INITIALIZED, +}; struct reg_dmn_pair_mapping { u16 regDmnEnum; @@ -36,13 +65,53 @@ struct ath_regulatory { struct reg_dmn_pair_mapping *regpair; }; +struct ath_ops { + unsigned int (*read)(void *, u32 reg_offset); + void (*write)(void *, u32 val, u32 reg_offset); +}; + +struct ath_common; + +struct ath_bus_ops { + void (*read_cachesize)(struct ath_common *common, int *csz); + void (*cleanup)(struct ath_common *common); + bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data); + void (*bt_coex_prep)(struct ath_common *common); +}; + struct ath_common { + void *ah; + void *priv; + struct ieee80211_hw *hw; + int debug_mask; + enum ath_device_state state; + + struct ath_ani ani; + u16 cachelsz; + u16 curaid; + u8 macaddr[ETH_ALEN]; + u8 curbssid[ETH_ALEN]; + u8 bssidmask[ETH_ALEN]; + + u8 tx_chainmask; + u8 rx_chainmask; + + u32 rx_bufsize; + + u32 keymax; + DECLARE_BITMAP(keymap, ATH_KEYMAX); + u8 splitmic; + struct ath_regulatory regulatory; + const struct ath_ops *ops; + const struct ath_bus_ops *bus_ops; }; struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, u32 len, gfp_t gfp_mask); +void ath_hw_setbssidmask(struct ath_common *common); + #endif /* ATH_H */ diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig index 06d006675d7d..eb83b7b4d0e3 100644 --- a/drivers/net/wireless/ath/ath5k/Kconfig +++ b/drivers/net/wireless/ath/ath5k/Kconfig @@ -1,6 +1,6 @@ config ATH5K tristate "Atheros 5xxx wireless cards support" - depends on PCI && MAC80211 && WLAN_80211 + depends on PCI && MAC80211 select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 6cd5efcec417..6a2a96761111 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -35,6 +35,7 @@ * TODO: Make a more generic struct (eg. add more stuff to ath5k_capabilities) * and clean up common bits, then introduce set/get functions in eeprom.c */ #include "eeprom.h" +#include "../ath.h" /* PCI IDs */ #define PCI_DEVICE_ID_ATHEROS_AR5210 0x0007 /* AR5210 */ @@ -165,13 +166,6 @@ #define AR5K_INI_VAL_XR 0 #define AR5K_INI_VAL_MAX 5 -/* Used for BSSID etc manipulation */ -#define AR5K_LOW_ID(_a)( \ -(_a)[0] | (_a)[1] << 8 | (_a)[2] << 16 | (_a)[3] << 24 \ -) - -#define AR5K_HIGH_ID(_a) ((_a)[4] | (_a)[5] << 8) - /* * Some tuneable values (these should be changeable by the user) * TODO: Make use of them and add more options OR use debug/configfs @@ -204,6 +198,7 @@ #define AR5K_TUNE_CWMAX_11B 1023 #define AR5K_TUNE_CWMAX_XR 7 #define AR5K_TUNE_NOISE_FLOOR -72 +#define AR5K_TUNE_CCA_MAX_GOOD_VALUE -95 #define AR5K_TUNE_MAX_TXPOWER 63 #define AR5K_TUNE_DEFAULT_TXPOWER 25 #define AR5K_TUNE_TPC_TXPOWER false @@ -1012,6 +1007,14 @@ struct ath5k_capabilities { } cap_queues; }; +/* size of noise floor history (keep it a power of two) */ +#define ATH5K_NF_CAL_HIST_MAX 8 +struct ath5k_nfcal_hist +{ + s16 index; /* current index into nfval */ + s16 nfval[ATH5K_NF_CAL_HIST_MAX]; /* last few noise floors */ +}; + /***************************************\ HARDWARE ABSTRACTION LAYER STRUCTURE @@ -1027,6 +1030,7 @@ struct ath5k_capabilities { /* TODO: Clean up and merge with ath5k_softc */ struct ath5k_hw { u32 ah_magic; + struct ath_common common; struct ath5k_softc *ah_sc; void __iomem *ah_iobase; @@ -1067,14 +1071,6 @@ struct ath5k_hw { u8 ah_def_ant; bool ah_software_retry; - u8 ah_sta_id[ETH_ALEN]; - - /* Current BSSID we are trying to assoc to / create. - * This is passed by mac80211 on config_interface() and cached here for - * use in resets */ - u8 ah_bssid[ETH_ALEN]; - u8 ah_bssid_mask[ETH_ALEN]; - int ah_gpio_npins; struct ath5k_capabilities ah_capabilities; @@ -1125,6 +1121,8 @@ struct ath5k_hw { struct ieee80211_channel r_last_channel; } ah_radar; + struct ath5k_nfcal_hist ah_nfcal_hist; + /* noise floor from last periodic calibration */ s32 ah_noise_floor; @@ -1160,7 +1158,7 @@ struct ath5k_hw { */ /* Attach/Detach Functions */ -extern struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc); +extern int ath5k_hw_attach(struct ath5k_softc *sc); extern void ath5k_hw_detach(struct ath5k_hw *ah); /* LED functions */ @@ -1203,10 +1201,9 @@ extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah); /* Protocol Control Unit Functions */ extern int ath5k_hw_set_opmode(struct ath5k_hw *ah); /* BSSID Functions */ -extern void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac); extern int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac); -extern void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id); -extern int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask); +extern void ath5k_hw_set_associd(struct ath5k_hw *ah); +extern void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask); /* Receive start/stop functions */ extern void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah); extern void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah); @@ -1288,8 +1285,10 @@ extern int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah); extern bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags); extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel); /* PHY calibration */ +void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah); extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel); extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq); +extern s16 ath5k_hw_get_noise_floor(struct ath5k_hw *ah); extern void ath5k_hw_calibration_poll(struct ath5k_hw *ah); /* Spur mitigation */ bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, @@ -1329,17 +1328,21 @@ static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo) return turbo ? (clock / 80) : (clock / 40); } -/* - * Read from a register - */ +static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah) +{ + return &ah->common; +} + +static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah) +{ + return &(ath5k_hw_common(ah)->regulatory); +} + static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg) { return ioread32(ah->ah_iobase + reg); } -/* - * Write to a register - */ static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg) { iowrite32(val, ah->ah_iobase + reg); diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index 71a1bd254517..42284445b75e 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -101,25 +101,15 @@ static int ath5k_hw_post(struct ath5k_hw *ah) * -ENODEV if the device is not supported or prints an error msg if something * else went wrong. */ -struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc) +int ath5k_hw_attach(struct ath5k_softc *sc) { - struct ath5k_hw *ah; + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); struct pci_dev *pdev = sc->pdev; struct ath5k_eeprom_info *ee; int ret; u32 srev; - /*If we passed the test malloc a ath5k_hw struct*/ - ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL); - if (ah == NULL) { - ret = -ENOMEM; - ATH5K_ERR(sc, "out of memory\n"); - goto err; - } - - ah->ah_sc = sc; - ah->ah_iobase = sc->iobase; - /* * HW information */ @@ -278,12 +268,12 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc) goto err_free; } + ee = &ah->ah_capabilities.cap_eeprom; + /* * Write PCI-E power save settings */ if ((ah->ah_version == AR5K_AR5212) && (pdev->is_pcie)) { - struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; - ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES); ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES); @@ -321,7 +311,6 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc) } /* Crypto settings */ - ee = &ah->ah_capabilities.cap_eeprom; ah->ah_aes_support = srev >= AR5K_SREV_AR5212_V4 && (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 && !AR5K_EEPROM_AES_DIS(ee->ee_misc5)); @@ -336,20 +325,21 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc) ath5k_hw_set_lladdr(ah, (u8[ETH_ALEN]){}); /* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */ - memset(ah->ah_bssid, 0xff, ETH_ALEN); - ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN); + ath5k_hw_set_associd(ah); ath5k_hw_set_opmode(ah); ath5k_hw_rfgain_opt_init(ah); + ath5k_hw_init_nfcal_hist(ah); + /* turn on HW LEDs */ ath5k_hw_set_ledstate(ah, AR5K_LED_INIT); - return ah; + return 0; err_free: kfree(ah); -err: - return ERR_PTR(ret); + return ret; } /** @@ -369,5 +359,4 @@ void ath5k_hw_detach(struct ath5k_hw *ah) ath5k_eeprom_detach(ah); /* assume interrupts are down */ - kfree(ah); } diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 95a8e232b58f..a4c086f069b1 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -195,12 +195,13 @@ static int __devinit ath5k_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id); static void __devexit ath5k_pci_remove(struct pci_dev *pdev); #ifdef CONFIG_PM -static int ath5k_pci_suspend(struct pci_dev *pdev, - pm_message_t state); -static int ath5k_pci_resume(struct pci_dev *pdev); +static int ath5k_pci_suspend(struct device *dev); +static int ath5k_pci_resume(struct device *dev); + +SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume); +#define ATH5K_PM_OPS (&ath5k_pm_ops) #else -#define ath5k_pci_suspend NULL -#define ath5k_pci_resume NULL +#define ATH5K_PM_OPS NULL #endif /* CONFIG_PM */ static struct pci_driver ath5k_pci_driver = { @@ -208,8 +209,7 @@ static struct pci_driver ath5k_pci_driver = { .id_table = ath5k_pci_id_table, .probe = ath5k_pci_probe, .remove = __devexit_p(ath5k_pci_remove), - .suspend = ath5k_pci_suspend, - .resume = ath5k_pci_resume, + .driver.pm = ATH5K_PM_OPS, }; @@ -323,10 +323,13 @@ static inline void ath5k_txbuf_free(struct ath5k_softc *sc, static inline void ath5k_rxbuf_free(struct ath5k_softc *sc, struct ath5k_buf *bf) { + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); + BUG_ON(!bf); if (!bf->skb) return; - pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize, + pci_unmap_single(sc->pdev, bf->skbaddr, common->rx_bufsize, PCI_DMA_FROMDEVICE); dev_kfree_skb_any(bf->skb); bf->skb = NULL; @@ -437,6 +440,22 @@ ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val) return name; } +static unsigned int ath5k_ioread32(void *hw_priv, u32 reg_offset) +{ + struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv; + return ath5k_hw_reg_read(ah, reg_offset); +} + +static void ath5k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) +{ + struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv; + ath5k_hw_reg_write(ah, val, reg_offset); +} + +static const struct ath_ops ath5k_common_ops = { + .read = ath5k_ioread32, + .write = ath5k_iowrite32, +}; static int __devinit ath5k_pci_probe(struct pci_dev *pdev, @@ -444,6 +463,7 @@ ath5k_pci_probe(struct pci_dev *pdev, { void __iomem *mem; struct ath5k_softc *sc; + struct ath_common *common; struct ieee80211_hw *hw; int ret; u8 csz; @@ -547,7 +567,6 @@ ath5k_pci_probe(struct pci_dev *pdev, __set_bit(ATH_STAT_INVALID, sc->status); sc->iobase = mem; /* So we can unmap it on detach */ - sc->common.cachelsz = csz << 2; /* convert to bytes */ sc->opmode = NL80211_IFTYPE_STATION; sc->bintval = 1000; mutex_init(&sc->lock); @@ -565,13 +584,28 @@ ath5k_pci_probe(struct pci_dev *pdev, goto err_free; } - /* Initialize device */ - sc->ah = ath5k_hw_attach(sc); - if (IS_ERR(sc->ah)) { - ret = PTR_ERR(sc->ah); + /*If we passed the test malloc a ath5k_hw struct*/ + sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL); + if (!sc->ah) { + ret = -ENOMEM; + ATH5K_ERR(sc, "out of memory\n"); goto err_irq; } + sc->ah->ah_sc = sc; + sc->ah->ah_iobase = sc->iobase; + common = ath5k_hw_common(sc->ah); + common->ops = &ath5k_common_ops; + common->ah = sc->ah; + common->hw = hw; + common->cachelsz = csz << 2; /* convert to bytes */ + + /* Initialize device */ + ret = ath5k_hw_attach(sc); + if (ret) { + goto err_free_ah; + } + /* set up multi-rate retry capabilities */ if (sc->ah->ah_version == AR5K_AR5212) { hw->max_rates = 4; @@ -640,6 +674,8 @@ err_ah: ath5k_hw_detach(sc->ah); err_irq: free_irq(pdev->irq, sc); +err_free_ah: + kfree(sc->ah); err_free: ieee80211_free_hw(hw); err_map: @@ -661,6 +697,7 @@ ath5k_pci_remove(struct pci_dev *pdev) ath5k_debug_finish_device(sc); ath5k_detach(pdev, hw); ath5k_hw_detach(sc->ah); + kfree(sc->ah); free_irq(pdev->irq, sc); pci_iounmap(pdev, sc->iobase); pci_release_region(pdev, 0); @@ -669,33 +706,20 @@ ath5k_pci_remove(struct pci_dev *pdev) } #ifdef CONFIG_PM -static int -ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int ath5k_pci_suspend(struct device *dev) { - struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(to_pci_dev(dev)); struct ath5k_softc *sc = hw->priv; ath5k_led_off(sc); - - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); - return 0; } -static int -ath5k_pci_resume(struct pci_dev *pdev) +static int ath5k_pci_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath5k_softc *sc = hw->priv; - int err; - - pci_restore_state(pdev); - - err = pci_enable_device(pdev); - if (err) - return err; /* * Suspend/Resume resets the PCI configuration space, so we have to @@ -718,7 +742,7 @@ static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *re { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath5k_softc *sc = hw->priv; - struct ath_regulatory *regulatory = &sc->common.regulatory; + struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah); return ath_reg_notifier_apply(wiphy, request, regulatory); } @@ -728,7 +752,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; - struct ath_regulatory *regulatory = &sc->common.regulatory; + struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); u8 mac[ETH_ALEN] = {}; int ret; @@ -815,7 +839,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) SET_IEEE80211_PERM_ADDR(hw, mac); /* All MAC address bits matter for ACKs */ - memset(sc->bssidmask, 0xff, ETH_ALEN); + memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN); ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain; @@ -1152,24 +1176,26 @@ ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix) static struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr) { + struct ath_common *common = ath5k_hw_common(sc->ah); struct sk_buff *skb; /* * Allocate buffer with headroom_needed space for the * fake physical layer header at the start. */ - skb = ath_rxbuf_alloc(&sc->common, - sc->rxbufsize + sc->common.cachelsz - 1, + skb = ath_rxbuf_alloc(common, + common->rx_bufsize, GFP_ATOMIC); if (!skb) { ATH5K_ERR(sc, "can't alloc skbuff of size %u\n", - sc->rxbufsize + sc->common.cachelsz - 1); + common->rx_bufsize); return NULL; } *skb_addr = pci_map_single(sc->pdev, - skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); + skb->data, common->rx_bufsize, + PCI_DMA_FROMDEVICE); if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) { ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__); dev_kfree_skb(skb); @@ -1605,13 +1631,14 @@ static int ath5k_rx_start(struct ath5k_softc *sc) { struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); struct ath5k_buf *bf; int ret; - sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->common.cachelsz); + common->rx_bufsize = roundup(IEEE80211_MAX_LEN, common->cachelsz); - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rxbufsize %u\n", - sc->common.cachelsz, sc->rxbufsize); + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n", + common->cachelsz, common->rx_bufsize); spin_lock_bh(&sc->rxbuflock); sc->rxlink = NULL; @@ -1656,6 +1683,8 @@ static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds, struct sk_buff *skb, struct ath5k_rx_status *rs) { + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); struct ieee80211_hdr *hdr = (void *)skb->data; unsigned int keyix, hlen; @@ -1672,7 +1701,7 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds, skb->len >= hlen + 4) { keyix = skb->data[hlen + 3] >> 6; - if (test_bit(keyix, sc->keymap)) + if (test_bit(keyix, common->keymap)) return RX_FLAG_DECRYPTED; } @@ -1684,13 +1713,14 @@ static void ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, struct ieee80211_rx_status *rxs) { + struct ath_common *common = ath5k_hw_common(sc->ah); u64 tsf, bc_tstamp; u32 hw_tu; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; if (ieee80211_is_beacon(mgmt->frame_control) && le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && - memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) { + memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) { /* * Received an IBSS beacon with the same BSSID. Hardware *must* * have updated the local TSF. We have to work around various @@ -1745,6 +1775,8 @@ ath5k_tasklet_rx(unsigned long data) struct sk_buff *skb, *next_skb; dma_addr_t next_skb_addr; struct ath5k_softc *sc = (void *)data; + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); struct ath5k_buf *bf; struct ath5k_desc *ds; int ret; @@ -1822,7 +1854,7 @@ accept: if (!next_skb) goto next; - pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize, + pci_unmap_single(sc->pdev, bf->skbaddr, common->rx_bufsize, PCI_DMA_FROMDEVICE); skb_put(skb, rs.rs_datalen); @@ -3008,6 +3040,8 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_key_conf *key) { struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); int ret = 0; if (modparam_nohwcrypt) @@ -3040,14 +3074,14 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ATH5K_ERR(sc, "can't set the key\n"); goto unlock; } - __set_bit(key->keyidx, sc->keymap); + __set_bit(key->keyidx, common->keymap); key->hw_key_idx = key->keyidx; key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV | IEEE80211_KEY_FLAG_GENERATE_MMIC); break; case DISABLE_KEY: ath5k_hw_reset_key(sc->ah, key->keyidx); - __clear_bit(key->keyidx, sc->keymap); + __clear_bit(key->keyidx, common->keymap); break; default: ret = -EINVAL; @@ -3176,6 +3210,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; + struct ath_common *common = ath5k_hw_common(ah); unsigned long flags; mutex_lock(&sc->lock); @@ -3184,10 +3219,9 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, if (changes & BSS_CHANGED_BSSID) { /* Cache for later use during resets */ - memcpy(ah->ah_bssid, bss_conf->bssid, ETH_ALEN); - /* XXX: assoc id is set to 0 for now, mac80211 doesn't have - * a clean way of letting us retrieve this yet. */ - ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); + common->curaid = 0; + ath5k_hw_set_associd(ah); mmiowb(); } @@ -3200,6 +3234,14 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, set_beacon_filter(hw, sc->assoc); ath5k_hw_set_ledstate(sc->ah, sc->assoc ? AR5K_LED_ASSOC : AR5K_LED_INIT); + if (bss_conf->assoc) { + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, + "Bss Info ASSOC %d, bssid: %pM\n", + bss_conf->aid, common->curbssid); + common->curaid = bss_conf->aid; + ath5k_hw_set_associd(ah); + /* Once ANI is available you would start it here */ + } } if (changes & BSS_CHANGED_BEACON) { diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index a28c42f32c9d..952b3a21bbc3 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -36,7 +36,7 @@ */ /* - * Defintions for the Atheros Wireless LAN controller driver. + * Definitions for the Atheros Wireless LAN controller driver. */ #ifndef _DEV_ATH_ATHVAR_H #define _DEV_ATH_ATHVAR_H @@ -115,7 +115,6 @@ struct ath5k_rfkill { * associated with an instance of a device */ struct ath5k_softc { struct pci_dev *pdev; /* for dma mapping */ - struct ath_common common; void __iomem *iobase; /* address of the device */ struct mutex lock; /* dev-level lock */ struct ieee80211_tx_queue_stats tx_stats[AR5K_NUM_TX_QUEUES]; @@ -154,8 +153,6 @@ struct ath5k_softc { enum ath5k_int imask; /* interrupt mask copy */ - DECLARE_BITMAP(keymap, AR5K_KEYCACHE_SIZE); /* key use bit map */ - u8 bssidmask[ETH_ALEN]; unsigned int led_pin, /* GPIO pin for driving LED */ @@ -193,7 +190,7 @@ struct ath5k_softc { struct ath5k_txq *cabq; /* content after beacon */ int power_level; /* Requested tx power in dbm */ - bool assoc; /* assocate state */ + bool assoc; /* associate state */ bool enable_beacon; /* true if beacons are on */ }; @@ -202,15 +199,4 @@ struct ath5k_softc { #define ath5k_hw_hasveol(_ah) \ (ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0) -static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah) -{ - return &ah->ah_sc->common; -} - -static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah) -{ - return &(ath5k_hw_common(ah)->regulatory); - -} - #endif diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index 644962adda97..81ea52c4faff 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -1492,7 +1492,7 @@ ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode) * This info is used to calibrate the baseband power table. Imagine * that for each channel there is a power curve that's hw specific * (depends on amplifier etc) and we try to "correct" this curve using - * offests we pass on to phy chip (baseband -> before amplifier) so that + * offsets we pass on to phy chip (baseband -> before amplifier) so that * it can use accurate power values when setting tx power (takes amplifier's * performance on each channel into account). * diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c index 18eb5190ce4b..8fa439308828 100644 --- a/drivers/net/wireless/ath/ath5k/initvals.c +++ b/drivers/net/wireless/ath/ath5k/initvals.c @@ -560,8 +560,8 @@ static const struct ath5k_ini ar5212_ini_common_start[] = { { AR5K_SLEEP0, 0x0002aaaa }, { AR5K_SLEEP1, 0x02005555 }, { AR5K_SLEEP2, 0x00000000 }, - { AR5K_BSS_IDM0, 0xffffffff }, - { AR5K_BSS_IDM1, 0x0000ffff }, + { AR_BSSMSKL, 0xffffffff }, + { AR_BSSMSKU, 0x0000ffff }, { AR5K_TXPC, 0x00000000 }, { AR5K_PROFCNT_TX, 0x00000000 }, { AR5K_PROFCNT_RX, 0x00000000 }, diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index b548c8eaaae1..d495890355d9 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -59,6 +59,8 @@ static const struct pci_device_id ath5k_led_devices[] = { { ATH_SDEVICE(PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID), ATH_LED(1, 1) }, /* Acer Aspire One A150 (maximlevitsky@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe008), ATH_LED(3, 0) }, + /* Acer Aspire One AO531h AO751h (keng-yu.lin@canonical.com) */ + { ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe00d), ATH_LED(3, 0) }, /* Acer Ferrari 5000 (russ.dill@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) }, /* E-machines E510 (tuliom@gmail.com) */ diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 2942f13c9c4a..64fc1eb9b6d9 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -24,6 +24,8 @@ * Protocol Control Unit Functions * \*********************************/ +#include <asm/unaligned.h> + #include "ath5k.h" #include "reg.h" #include "debug.h" @@ -44,6 +46,7 @@ */ int ath5k_hw_set_opmode(struct ath5k_hw *ah) { + struct ath_common *common = ath5k_hw_common(ah); u32 pcu_reg, beacon_reg, low_id, high_id; @@ -95,8 +98,8 @@ int ath5k_hw_set_opmode(struct ath5k_hw *ah) /* * Set PCU registers */ - low_id = AR5K_LOW_ID(ah->ah_sta_id); - high_id = AR5K_HIGH_ID(ah->ah_sta_id); + low_id = get_unaligned_le32(common->macaddr); + high_id = get_unaligned_le16(common->macaddr + 4); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); @@ -238,28 +241,6 @@ int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) return 0; } - -/****************\ -* BSSID handling * -\****************/ - -/** - * ath5k_hw_get_lladdr - Get station id - * - * @ah: The &struct ath5k_hw - * @mac: The card's mac address - * - * Initialize ah->ah_sta_id using the mac address provided - * (just a memcpy). - * - * TODO: Remove it once we merge ath5k_softc and ath5k_hw - */ -void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) -{ - ATH5K_TRACE(ah->ah_sc); - memcpy(mac, ah->ah_sta_id, ETH_ALEN); -} - /** * ath5k_hw_set_lladdr - Set station id * @@ -270,17 +251,18 @@ void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) */ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) { + struct ath_common *common = ath5k_hw_common(ah); u32 low_id, high_id; u32 pcu_reg; ATH5K_TRACE(ah->ah_sc); /* Set new station ID */ - memcpy(ah->ah_sta_id, mac, ETH_ALEN); + memcpy(common->macaddr, mac, ETH_ALEN); pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; - low_id = AR5K_LOW_ID(mac); - high_id = AR5K_HIGH_ID(mac); + low_id = get_unaligned_le32(mac); + high_id = get_unaligned_le16(mac + 4); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); @@ -297,159 +279,51 @@ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) * * Sets the BSSID which trigers the "SME Join" operation */ -void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) +void ath5k_hw_set_associd(struct ath5k_hw *ah) { - u32 low_id, high_id; + struct ath_common *common = ath5k_hw_common(ah); u16 tim_offset = 0; /* * Set simple BSSID mask on 5212 */ - if (ah->ah_version == AR5K_AR5212) { - ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask), - AR5K_BSS_IDM0); - ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask), - AR5K_BSS_IDM1); - } + if (ah->ah_version == AR5K_AR5212) + ath_hw_setbssidmask(common); /* * Set BSSID which triggers the "SME Join" operation */ - low_id = AR5K_LOW_ID(bssid); - high_id = AR5K_HIGH_ID(bssid); - ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0); - ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) << - AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); - - if (assoc_id == 0) { + ath5k_hw_reg_write(ah, + get_unaligned_le32(common->curbssid), + AR5K_BSS_ID0); + ath5k_hw_reg_write(ah, + get_unaligned_le16(common->curbssid + 4) | + ((common->curaid & 0x3fff) << AR5K_BSS_ID1_AID_S), + AR5K_BSS_ID1); + + if (common->curaid == 0) { ath5k_hw_disable_pspoll(ah); return; } AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM, - tim_offset ? tim_offset + 4 : 0); + tim_offset ? tim_offset + 4 : 0); ath5k_hw_enable_pspoll(ah, NULL, 0); } -/** - * ath5k_hw_set_bssid_mask - filter out bssids we listen - * - * @ah: the &struct ath5k_hw - * @mask: the bssid_mask, a u8 array of size ETH_ALEN - * - * BSSID masking is a method used by AR5212 and newer hardware to inform PCU - * which bits of the interface's MAC address should be looked at when trying - * to decide which packets to ACK. In station mode and AP mode with a single - * BSS every bit matters since we lock to only one BSS. In AP mode with - * multiple BSSes (virtual interfaces) not every bit matters because hw must - * accept frames for all BSSes and so we tweak some bits of our mac address - * in order to have multiple BSSes. - * - * NOTE: This is a simple filter and does *not* filter out all - * relevant frames. Some frames that are not for us might get ACKed from us - * by PCU because they just match the mask. - * - * When handling multiple BSSes you can get the BSSID mask by computing the - * set of ~ ( MAC XOR BSSID ) for all bssids we handle. - * - * When you do this you are essentially computing the common bits of all your - * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with - * the MAC address to obtain the relevant bits and compare the result with - * (frame's BSSID & mask) to see if they match. - */ -/* - * Simple example: on your card you have have two BSSes you have created with - * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. - * There is another BSSID-03 but you are not part of it. For simplicity's sake, - * assuming only 4 bits for a mac address and for BSSIDs you can then have: - * - * \ - * MAC: 0001 | - * BSSID-01: 0100 | --> Belongs to us - * BSSID-02: 1001 | - * / - * ------------------- - * BSSID-03: 0110 | --> External - * ------------------- - * - * Our bssid_mask would then be: - * - * On loop iteration for BSSID-01: - * ~(0001 ^ 0100) -> ~(0101) - * -> 1010 - * bssid_mask = 1010 - * - * On loop iteration for BSSID-02: - * bssid_mask &= ~(0001 ^ 1001) - * bssid_mask = (1010) & ~(0001 ^ 1001) - * bssid_mask = (1010) & ~(1001) - * bssid_mask = (1010) & (0110) - * bssid_mask = 0010 - * - * A bssid_mask of 0010 means "only pay attention to the second least - * significant bit". This is because its the only bit common - * amongst the MAC and all BSSIDs we support. To findout what the real - * common bit is we can simply "&" the bssid_mask now with any BSSID we have - * or our MAC address (we assume the hardware uses the MAC address). - * - * Now, suppose there's an incoming frame for BSSID-03: - * - * IFRAME-01: 0110 - * - * An easy eye-inspeciton of this already should tell you that this frame - * will not pass our check. This is beacuse the bssid_mask tells the - * hardware to only look at the second least significant bit and the - * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB - * as 1, which does not match 0. - * - * So with IFRAME-01 we *assume* the hardware will do: - * - * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; - * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; - * --> allow = (0010) == 0000 ? 1 : 0; - * --> allow = 0 - * - * Lets now test a frame that should work: - * - * IFRAME-02: 0001 (we should allow) - * - * allow = (0001 & 1010) == 1010 - * - * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; - * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; - * --> allow = (0010) == (0010) - * --> allow = 1 - * - * Other examples: - * - * IFRAME-03: 0100 --> allowed - * IFRAME-04: 1001 --> allowed - * IFRAME-05: 1101 --> allowed but its not for us!!! - * - */ -int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) +void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) { - u32 low_id, high_id; + struct ath_common *common = ath5k_hw_common(ah); ATH5K_TRACE(ah->ah_sc); /* Cache bssid mask so that we can restore it * on reset */ - memcpy(ah->ah_bssid_mask, mask, ETH_ALEN); - if (ah->ah_version == AR5K_AR5212) { - low_id = AR5K_LOW_ID(mask); - high_id = AR5K_HIGH_ID(mask); - - ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0); - ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1); - - return 0; - } - - return -EIO; + memcpy(common->bssidmask, mask, ETH_ALEN); + if (ah->ah_version == AR5K_AR5212) + ath_hw_setbssidmask(common); } - /************\ * RX Control * \************/ @@ -1157,14 +1031,17 @@ int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) /* Invalid entry (key table overflow) */ AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); - /* MAC may be NULL if it's a broadcast key. In this case no need to - * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */ + /* + * MAC may be NULL if it's a broadcast key. In this case no need to + * to compute get_unaligned_le32 and get_unaligned_le16 as we + * already know it. + */ if (!mac) { low_id = 0xffffffff; high_id = 0xffff | AR5K_KEYTABLE_VALID; } else { - low_id = AR5K_LOW_ID(mac); - high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID; + low_id = get_unaligned_le32(mac); + high_id = get_unaligned_le16(mac + 4) | AR5K_KEYTABLE_VALID; } ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry)); diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 1a039f2bd732..72474c0ccaff 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -117,7 +117,7 @@ static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah, /* * This code is used to optimize rf gain on different environments - * (temprature mostly) based on feedback from a power detector. + * (temperature mostly) based on feedback from a power detector. * * It's only used on RF5111 and RF5112, later RF chips seem to have * auto adjustment on hw -notice they have a much smaller BANK 7 and @@ -1124,77 +1124,148 @@ ath5k_hw_calibration_poll(struct ath5k_hw *ah) ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION; AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); } +} +static int sign_extend(int val, const int nbits) +{ + int order = BIT(nbits-1); + return (val ^ order) - order; } -/** - * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration - * - * @ah: struct ath5k_hw pointer we are operating on - * @freq: the channel frequency, just used for error logging - * - * This function performs a noise floor calibration of the PHY and waits for - * it to complete. Then the noise floor value is compared to some maximum - * noise floor we consider valid. - * - * Note that this is different from what the madwifi HAL does: it reads the - * noise floor and afterwards initiates the calibration. Since the noise floor - * calibration can take some time to finish, depending on the current channel - * use, that avoids the occasional timeout warnings we are seeing now. - * - * See the following link for an Atheros patent on noise floor calibration: - * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \ - * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7 +static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) +{ + s32 val; + + val = ath5k_hw_reg_read(ah, AR5K_PHY_NF); + return sign_extend(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 9); +} + +void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) +{ + int i; + + ah->ah_nfcal_hist.index = 0; + for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) + ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE; +} + +static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor) +{ + struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist; + hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX-1); + hist->nfval[hist->index] = noise_floor; +} + +static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah) +{ + s16 sort[ATH5K_NF_CAL_HIST_MAX]; + s16 tmp; + int i, j; + + memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort)); + for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) { + for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) { + if (sort[j] > sort[j-1]) { + tmp = sort[j]; + sort[j] = sort[j-1]; + sort[j-1] = tmp; + } + } + } + for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) { + ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, + "cal %d:%d\n", i, sort[i]); + } + return sort[(ATH5K_NF_CAL_HIST_MAX-1) / 2]; +} + +/* + * When we tell the hardware to perform a noise floor calibration + * by setting the AR5K_PHY_AGCCTL_NF bit, it will periodically + * sample-and-hold the minimum noise level seen at the antennas. + * This value is then stored in a ring buffer of recently measured + * noise floor values so we have a moving window of the last few + * samples. * - * XXX: Since during noise floor calibration antennas are detached according to - * the patent, we should stop tx queues here. + * The median of the values in the history is then loaded into the + * hardware for its own use for RSSI and CCA measurements. */ -int -ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) +void ath5k_hw_update_noise_floor(struct ath5k_hw *ah) { - int ret; - unsigned int i; - s32 noise_floor; + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + u32 val; + s16 nf, threshold; + u8 ee_mode; - /* - * Enable noise floor calibration - */ - AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, - AR5K_PHY_AGCCTL_NF); + /* keep last value if calibration hasn't completed */ + if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) { + ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, + "NF did not complete in calibration window\n"); - ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, - AR5K_PHY_AGCCTL_NF, 0, false); - if (ret) { - ATH5K_ERR(ah->ah_sc, - "noise floor calibration timeout (%uMHz)\n", freq); - return -EAGAIN; + return; } - /* Wait until the noise floor is calibrated and read the value */ - for (i = 20; i > 0; i--) { - mdelay(1); - noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF); - noise_floor = AR5K_PHY_NF_RVAL(noise_floor); - if (noise_floor & AR5K_PHY_NF_ACTIVE) { - noise_floor = AR5K_PHY_NF_AVAL(noise_floor); - - if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) - break; - } + switch (ah->ah_current_channel->hw_value & CHANNEL_MODES) { + case CHANNEL_A: + case CHANNEL_T: + case CHANNEL_XR: + ee_mode = AR5K_EEPROM_MODE_11A; + break; + case CHANNEL_G: + case CHANNEL_TG: + ee_mode = AR5K_EEPROM_MODE_11G; + break; + default: + case CHANNEL_B: + ee_mode = AR5K_EEPROM_MODE_11B; + break; } - ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, - "noise floor %d\n", noise_floor); - if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { - ATH5K_ERR(ah->ah_sc, - "noise floor calibration failed (%uMHz)\n", freq); - return -EAGAIN; + /* completed NF calibration, test threshold */ + nf = ath5k_hw_read_measured_noise_floor(ah); + threshold = ee->ee_noise_floor_thr[ee_mode]; + + if (nf > threshold) { + ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, + "noise floor failure detected; " + "read %d, threshold %d\n", + nf, threshold); + + nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE; } - ah->ah_noise_floor = noise_floor; + ath5k_hw_update_nfcal_hist(ah, nf); + nf = ath5k_hw_get_median_noise_floor(ah); - return 0; + /* load noise floor (in .5 dBm) so the hardware will use it */ + val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M; + val |= (nf * 2) & AR5K_PHY_NF_M; + ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); + + AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, + ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE)); + + ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, + 0, false); + + /* + * Load a high max CCA Power value (-50 dBm in .5 dBm units) + * so that we're not capped by the median we just loaded. + * This will be used as the initial value for the next noise + * floor calibration. + */ + val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M); + ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_NF_EN | + AR5K_PHY_AGCCTL_NF_NOUPDATE | + AR5K_PHY_AGCCTL_NF); + + ah->ah_noise_floor = nf; + + ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, + "noise floor calibrated: %d\n", nf); } /* @@ -1287,7 +1358,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, return ret; } - ath5k_hw_noise_floor_calibration(ah, channel->center_freq); + ath5k_hw_update_noise_floor(ah); /* * Re-enable RX/TX and beacons @@ -1328,7 +1399,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, if (i_coffd == 0 || q_coffd == 0) goto done; - i_coff = ((-iq_corr) / i_coffd) & 0x3f; + i_coff = ((-iq_corr) / i_coffd); /* Boundary check */ if (i_coff > 31) @@ -1336,7 +1407,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, if (i_coff < -32) i_coff = -32; - q_coff = (((s32)i_pwr / q_coffd) - 128) & 0x1f; + q_coff = (((s32)i_pwr / q_coffd) - 128); /* Boundary check */ if (q_coff > 15) @@ -1360,7 +1431,7 @@ done: * since noise floor calibration interrupts rx path while I/Q * calibration doesn't. We don't need to run noise floor calibration * as often as I/Q calibration.*/ - ath5k_hw_noise_floor_calibration(ah, channel->center_freq); + ath5k_hw_update_noise_floor(ah); /* Initiate a gain_F calibration */ ath5k_hw_request_rfgain_probe(ah); @@ -2675,7 +2746,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah, /* Fill curves in reverse order * from lower power (max gain) * to higher power. Use curve -> idx - * backmaping we did on eeprom init */ + * backmapping we did on eeprom init */ u8 idx = pdg_curve_to_idx[pdg]; /* Grab the needed curves by index */ @@ -2777,7 +2848,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah, /* Now we have a set of curves for this * channel on tmpL (x range is table_max - table_min * and y values are tmpL[pdg][]) sorted in the same - * order as EEPROM (because we've used the backmaping). + * order as EEPROM (because we've used the backmapping). * So for RF5112 it's from higher power to lower power * and for RF2413 it's from lower power to higher power. * For RF5111 we only have one curve. */ @@ -2954,8 +3025,6 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower); return -EINVAL; } - if (txpower == 0) - txpower = AR5K_TUNE_DEFAULT_TXPOWER; /* Reset TX power values */ memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h index c63ea6afd96f..4cb9c5df9f46 100644 --- a/drivers/net/wireless/ath/ath5k/reg.h +++ b/drivers/net/wireless/ath/ath5k/reg.h @@ -35,7 +35,7 @@ * released by Atheros and on various debug messages found on the net. */ - +#include "../reg.h" /*====MAC DMA REGISTERS====*/ @@ -1650,12 +1650,6 @@ #define AR5K_SLEEP2_DTIM_PER_S 16 /* - * BSSID mask registers - */ -#define AR5K_BSS_IDM0 0x80e0 /* Upper bits */ -#define AR5K_BSS_IDM1 0x80e4 /* Lower bits */ - -/* * TX power control (TPC) register * * XXX: PCDAC steps (0.5dbm) or DBM ? @@ -2039,17 +2033,14 @@ #define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automaticaly */ /* - * PHY noise floor status register + * PHY noise floor status register (CCA = Clear Channel Assessment) */ #define AR5K_PHY_NF 0x9864 /* Register address */ -#define AR5K_PHY_NF_M 0x000001ff /* Noise floor mask */ -#define AR5K_PHY_NF_ACTIVE 0x00000100 /* Noise floor calibration still active */ -#define AR5K_PHY_NF_RVAL(_n) (((_n) >> 19) & AR5K_PHY_NF_M) -#define AR5K_PHY_NF_AVAL(_n) (-((_n) ^ AR5K_PHY_NF_M) + 1) -#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9)) +#define AR5K_PHY_NF_M 0x000001ff /* Noise floor, written to hardware in 1/2 dBm units */ +#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9)) #define AR5K_PHY_NF_THRESH62 0x0007f000 /* Thresh62 -check ANI patent- (field) */ #define AR5K_PHY_NF_THRESH62_S 12 -#define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* ??? */ +#define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* Minimum measured noise level, read from hardware in 1 dBm units */ #define AR5K_PHY_NF_MINCCA_PWR_S 19 /* diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 34e13c700849..62954fc77869 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -25,6 +25,8 @@ Reset functions and helpers \*****************************/ +#include <asm/unaligned.h> + #include <linux/pci.h> /* To determine if a card is pci-e */ #include <linux/log2.h> #include "ath5k.h" @@ -870,6 +872,7 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah, int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool change_channel) { + struct ath_common *common = ath5k_hw_common(ah); u32 s_seq[10], s_ant, s_led[3], staid1_flags, tsf_up, tsf_lo; u32 phy_tst1; u8 mode, freq, ee_mode, ant[2]; @@ -1171,10 +1174,12 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO); /* Restore sta_id flags and preserve our mac address*/ - ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_sta_id), - AR5K_STA_ID0); - ath5k_hw_reg_write(ah, staid1_flags | AR5K_HIGH_ID(ah->ah_sta_id), - AR5K_STA_ID1); + ath5k_hw_reg_write(ah, + get_unaligned_le32(common->macaddr), + AR5K_STA_ID0); + ath5k_hw_reg_write(ah, + staid1_flags | get_unaligned_le16(common->macaddr + 4), + AR5K_STA_ID1); /* @@ -1182,8 +1187,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, */ /* Restore bssid and bssid mask */ - /* XXX: add ah->aid once mac80211 gives this to us */ - ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + ath5k_hw_set_associd(ah); /* Set PCU config */ ath5k_hw_set_opmode(ah); @@ -1289,7 +1293,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, * out and/or noise floor calibration might timeout. */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, - AR5K_PHY_AGCCTL_CAL); + AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF); /* At the same time start I/Q calibration for QAM constellation * -no need for CCK- */ @@ -1310,21 +1314,6 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, channel->center_freq); } - /* - * If we run NF calibration before AGC, it always times out. - * Binary HAL starts NF and AGC calibration at the same time - * and only waits for AGC to finish. Also if AGC or NF cal. - * times out, reset doesn't fail on binary HAL. I believe - * that's wrong because since rx path is routed to a detector, - * if cal. doesn't finish we won't have RX. Sam's HAL for AR5210/5211 - * enables noise floor calibration after offset calibration and if noise - * floor calibration fails, reset fails. I believe that's - * a better approach, we just need to find a polling interval - * that suits best, even if reset continues we need to make - * sure that rx path is ready. - */ - ath5k_hw_noise_floor_calibration(ah, channel->center_freq); - /* Restore antenna mode */ ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index ef5f59c4dd80..03a1106ad725 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -1,9 +1,16 @@ +config ATH9K_HW + tristate +config ATH9K_COMMON + tristate + config ATH9K tristate "Atheros 802.11n wireless cards support" - depends on PCI && MAC80211 && WLAN_80211 + depends on PCI && MAC80211 + select ATH9K_HW select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS + select ATH9K_COMMON ---help--- This module adds support for wireless adapters based on Atheros IEEE 802.11n AR5008, AR9001 and AR9002 family @@ -16,13 +23,12 @@ config ATH9K If you choose to build a module, it'll be called ath9k. -config ATH9K_DEBUG +config ATH9K_DEBUGFS bool "Atheros ath9k debugging" depends on ATH9K ---help--- - Say Y, if you need ath9k to display debug messages. - Pass the debug mask as a module parameter: + Say Y, if you need access to ath9k's statistics for + interrupts, rate control, etc. - modprobe ath9k debug=0x00000200 + Also required for changing debug message flags at run time. - Look in ath9k/debug.h for possible debug masks diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index ff2c9a26c10c..4985b2b1b0a9 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -1,22 +1,28 @@ -ath9k-y += hw.o \ - eeprom.o \ - eeprom_def.o \ - eeprom_4k.o \ - eeprom_9287.o \ - mac.o \ - calib.o \ - ani.o \ - phy.o \ - beacon.o \ +ath9k-y += beacon.o \ main.o \ recv.o \ xmit.o \ virtual.o \ - rc.o \ - btcoex.o + rc.o ath9k-$(CONFIG_PCI) += pci.o ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o -ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o +ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o obj-$(CONFIG_ATH9K) += ath9k.o + +ath9k_hw-y:= hw.o \ + eeprom.o \ + eeprom_def.o \ + eeprom_4k.o \ + eeprom_9287.o \ + calib.o \ + ani.o \ + phy.o \ + btcoex.o \ + mac.o \ + +obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o + +obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o +ath9k_common-y:= common.o diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 2ad7d0280f7a..329e6bc137ab 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -22,27 +22,29 @@ #include "ath9k.h" /* return bus cachesize in 4B word units */ -static void ath_ahb_read_cachesize(struct ath_softc *sc, int *csz) +static void ath_ahb_read_cachesize(struct ath_common *common, int *csz) { *csz = L1_CACHE_BYTES >> 2; } -static void ath_ahb_cleanup(struct ath_softc *sc) +static void ath_ahb_cleanup(struct ath_common *common) { + struct ath_softc *sc = (struct ath_softc *)common->priv; iounmap(sc->mem); } -static bool ath_ahb_eeprom_read(struct ath_hw *ah, u32 off, u16 *data) +static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) { - struct ath_softc *sc = ah->ah_sc; + struct ath_softc *sc = (struct ath_softc *)common->priv; struct platform_device *pdev = to_platform_device(sc->dev); struct ath9k_platform_data *pdata; pdata = (struct ath9k_platform_data *) pdev->dev.platform_data; if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "%s: flash read failed, offset %08x is out of range\n", - __func__, off); + ath_print(common, ATH_DBG_FATAL, + "%s: flash read failed, offset %08x " + "is out of range\n", + __func__, off); return false; } @@ -67,6 +69,7 @@ static int ath_ahb_probe(struct platform_device *pdev) int irq; int ret = 0; struct ath_hw *ah; + char hw_name[64]; if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "no platform data specified\n"); @@ -116,10 +119,9 @@ static int ath_ahb_probe(struct platform_device *pdev) sc->hw = hw; sc->dev = &pdev->dev; sc->mem = mem; - sc->bus_ops = &ath_ahb_bus_ops; sc->irq = irq; - ret = ath_init_device(AR5416_AR9100_DEVID, sc, 0x0); + ret = ath_init_device(AR5416_AR9100_DEVID, sc, 0x0, &ath_ahb_bus_ops); if (ret) { dev_err(&pdev->dev, "failed to initialize device\n"); goto err_free_hw; @@ -132,14 +134,11 @@ static int ath_ahb_probe(struct platform_device *pdev) } ah = sc->sc_ah; + ath9k_hw_name(ah, hw_name, sizeof(hw_name)); printk(KERN_INFO - "%s: Atheros AR%s MAC/BB Rev:%x, " - "AR%s RF Rev:%x, mem=0x%lx, irq=%d\n", + "%s: %s mem=0x%lx, irq=%d\n", wiphy_name(hw->wiphy), - ath_mac_bb_name(ah->hw_version.macVersion), - ah->hw_version.macRev, - ath_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)), - ah->hw_version.phyRev, + hw_name, (unsigned long)mem, irq); return 0; diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 2b493742ef10..2a0cd64c2bfb 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +#include "hw.h" static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, struct ath9k_channel *chan) @@ -31,8 +31,8 @@ static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, } } - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "No more channel states left. Using channel 0\n"); + ath_print(ath9k_hw_common(ah), ATH_DBG_ANI, + "No more channel states left. Using channel 0\n"); return 0; } @@ -41,16 +41,17 @@ static bool ath9k_hw_ani_control(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { struct ar5416AniState *aniState = ah->curani; + struct ath_common *common = ath9k_hw_common(ah); switch (cmd & ah->ani_function) { case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{ u32 level = param; if (level >= ARRAY_SIZE(ah->totalSizeDesired)) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "level out of range (%u > %u)\n", - level, - (unsigned)ARRAY_SIZE(ah->totalSizeDesired)); + ath_print(common, ATH_DBG_ANI, + "level out of range (%u > %u)\n", + level, + (unsigned)ARRAY_SIZE(ah->totalSizeDesired)); return false; } @@ -152,10 +153,10 @@ static bool ath9k_hw_ani_control(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(firstep)) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "level out of range (%u > %u)\n", - level, - (unsigned) ARRAY_SIZE(firstep)); + ath_print(common, ATH_DBG_ANI, + "level out of range (%u > %u)\n", + level, + (unsigned) ARRAY_SIZE(firstep)); return false; } REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, @@ -174,11 +175,10 @@ static bool ath9k_hw_ani_control(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(cycpwrThr1)) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "level out of range (%u > %u)\n", - level, - (unsigned) - ARRAY_SIZE(cycpwrThr1)); + ath_print(common, ATH_DBG_ANI, + "level out of range (%u > %u)\n", + level, + (unsigned) ARRAY_SIZE(cycpwrThr1)); return false; } REG_RMW_FIELD(ah, AR_PHY_TIMING5, @@ -194,25 +194,28 @@ static bool ath9k_hw_ani_control(struct ath_hw *ah, case ATH9K_ANI_PRESENT: break; default: - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "invalid cmd %u\n", cmd); + ath_print(common, ATH_DBG_ANI, + "invalid cmd %u\n", cmd); return false; } - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "ANI parameters:\n"); - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "noiseImmunityLevel=%d, spurImmunityLevel=%d, " - "ofdmWeakSigDetectOff=%d\n", - aniState->noiseImmunityLevel, aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff); - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "cckWeakSigThreshold=%d, " - "firstepLevel=%d, listenTime=%d\n", - aniState->cckWeakSigThreshold, aniState->firstepLevel, - aniState->listenTime); - DPRINTF(ah->ah_sc, ATH_DBG_ANI, + ath_print(common, ATH_DBG_ANI, "ANI parameters:\n"); + ath_print(common, ATH_DBG_ANI, + "noiseImmunityLevel=%d, spurImmunityLevel=%d, " + "ofdmWeakSigDetectOff=%d\n", + aniState->noiseImmunityLevel, + aniState->spurImmunityLevel, + !aniState->ofdmWeakSigDetectOff); + ath_print(common, ATH_DBG_ANI, + "cckWeakSigThreshold=%d, " + "firstepLevel=%d, listenTime=%d\n", + aniState->cckWeakSigThreshold, + aniState->firstepLevel, + aniState->listenTime); + ath_print(common, ATH_DBG_ANI, "cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", - aniState->cycleCount, aniState->ofdmPhyErrCount, + aniState->cycleCount, + aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); return true; @@ -231,6 +234,7 @@ static void ath9k_hw_update_mibstats(struct ath_hw *ah, static void ath9k_ani_restart(struct ath_hw *ah) { struct ar5416AniState *aniState; + struct ath_common *common = ath9k_hw_common(ah); if (!DO_ANI(ah)) return; @@ -240,24 +244,24 @@ static void ath9k_ani_restart(struct ath_hw *ah) if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) { aniState->ofdmPhyErrBase = 0; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "OFDM Trigger is too high for hw counters\n"); + ath_print(common, ATH_DBG_ANI, + "OFDM Trigger is too high for hw counters\n"); } else { aniState->ofdmPhyErrBase = AR_PHY_COUNTMAX - aniState->ofdmTrigHigh; } if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) { aniState->cckPhyErrBase = 0; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "CCK Trigger is too high for hw counters\n"); + ath_print(common, ATH_DBG_ANI, + "CCK Trigger is too high for hw counters\n"); } else { aniState->cckPhyErrBase = AR_PHY_COUNTMAX - aniState->cckTrigHigh; } - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Writing ofdmbase=%u cckbase=%u\n", - aniState->ofdmPhyErrBase, - aniState->cckPhyErrBase); + ath_print(common, ATH_DBG_ANI, + "Writing ofdmbase=%u cckbase=%u\n", + aniState->ofdmPhyErrBase, + aniState->cckPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); @@ -271,7 +275,7 @@ static void ath9k_ani_restart(struct ath_hw *ah) static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) { - struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; struct ar5416AniState *aniState; int32_t rssi; @@ -343,7 +347,7 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) { - struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; struct ar5416AniState *aniState; int32_t rssi; @@ -464,6 +468,7 @@ void ath9k_ani_reset(struct ath_hw *ah) { struct ar5416AniState *aniState; struct ath9k_channel *chan = ah->curchan; + struct ath_common *common = ath9k_hw_common(ah); int index; if (!DO_ANI(ah)) @@ -475,8 +480,8 @@ void ath9k_ani_reset(struct ath_hw *ah) if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION && ah->opmode != NL80211_IFTYPE_ADHOC) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Reset ANI state opmode %u\n", ah->opmode); + ath_print(common, ATH_DBG_ANI, + "Reset ANI state opmode %u\n", ah->opmode); ah->stats.ast_ani_reset++; if (ah->opmode == NL80211_IFTYPE_AP) { @@ -543,6 +548,7 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) { struct ar5416AniState *aniState; + struct ath_common *common = ath9k_hw_common(ah); int32_t listenTime; u32 phyCnt1, phyCnt2; u32 ofdmPhyErrCnt, cckPhyErrCnt; @@ -569,20 +575,22 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, if (phyCnt1 < aniState->ofdmPhyErrBase || phyCnt2 < aniState->cckPhyErrBase) { if (phyCnt1 < aniState->ofdmPhyErrBase) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "phyCnt1 0x%x, resetting " - "counter value to 0x%x\n", - phyCnt1, aniState->ofdmPhyErrBase); + ath_print(common, ATH_DBG_ANI, + "phyCnt1 0x%x, resetting " + "counter value to 0x%x\n", + phyCnt1, + aniState->ofdmPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); } if (phyCnt2 < aniState->cckPhyErrBase) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "phyCnt2 0x%x, resetting " - "counter value to 0x%x\n", - phyCnt2, aniState->cckPhyErrBase); + ath_print(common, ATH_DBG_ANI, + "phyCnt2 0x%x, resetting " + "counter value to 0x%x\n", + phyCnt2, + aniState->cckPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_MASK_2, @@ -621,10 +629,13 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, } } } +EXPORT_SYMBOL(ath9k_hw_ani_monitor); void ath9k_enable_mib_counters(struct ath_hw *ah) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable MIB counters\n"); + struct ath_common *common = ath9k_hw_common(ah); + + ath_print(common, ATH_DBG_ANI, "Enable MIB counters\n"); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); @@ -640,7 +651,10 @@ void ath9k_enable_mib_counters(struct ath_hw *ah) /* Freeze the MIB counters, get the stats and then clear them */ void ath9k_hw_disable_mib_counters(struct ath_hw *ah) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disable MIB counters\n"); + struct ath_common *common = ath9k_hw_common(ah); + + ath_print(common, ATH_DBG_ANI, "Disable MIB counters\n"); + REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC); @@ -653,6 +667,7 @@ u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, u32 *rxf_pcnt, u32 *txf_pcnt) { + struct ath_common *common = ath9k_hw_common(ah); static u32 cycles, rx_clear, rx_frame, tx_frame; u32 good = 1; @@ -662,8 +677,8 @@ u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, u32 cc = REG_READ(ah, AR_CCCNT); if (cycles == 0 || cycles > cc) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "cycle counter wrap. ExtBusy = 0\n"); + ath_print(common, ATH_DBG_ANI, + "cycle counter wrap. ExtBusy = 0\n"); good = 0; } else { u32 cc_d = cc - cycles; @@ -742,6 +757,7 @@ void ath9k_hw_procmibevent(struct ath_hw *ah) ath9k_ani_restart(ah); } } +EXPORT_SYMBOL(ath9k_hw_procmibevent); void ath9k_hw_ani_setup(struct ath_hw *ah) { @@ -762,9 +778,10 @@ void ath9k_hw_ani_setup(struct ath_hw *ah) void ath9k_hw_ani_init(struct ath_hw *ah) { + struct ath_common *common = ath9k_hw_common(ah); int i; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Initialize ANI\n"); + ath_print(common, ATH_DBG_ANI, "Initialize ANI\n"); memset(ah->ani, 0, sizeof(ah->ani)); for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { @@ -786,11 +803,11 @@ void ath9k_hw_ani_init(struct ath_hw *ah) AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH; } - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Setting OfdmErrBase = 0x%08x\n", - ah->ani[0].ofdmPhyErrBase); - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", - ah->ani[0].cckPhyErrBase); + ath_print(common, ATH_DBG_ANI, + "Setting OfdmErrBase = 0x%08x\n", + ah->ani[0].ofdmPhyErrBase); + ath_print(common, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", + ah->ani[0].cckPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase); REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase); @@ -803,7 +820,7 @@ void ath9k_hw_ani_init(struct ath_hw *ah) void ath9k_hw_ani_disable(struct ath_hw *ah) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disabling ANI\n"); + ath_print(ath9k_hw_common(ah), ATH_DBG_ANI, "Disabling ANI\n"); ath9k_hw_disable_mib_counters(ah); REG_WRITE(ah, AR_PHY_ERR_1, 0); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 1d59f10f68da..e2cef2ff5d8f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -19,14 +19,15 @@ #include <linux/etherdevice.h> #include <linux/device.h> -#include <net/mac80211.h> #include <linux/leds.h> -#include "hw.h" -#include "rc.h" #include "debug.h" -#include "../ath.h" -#include "btcoex.h" +#include "common.h" + +/* + * Header for the ath9k.ko driver core *only* -- hw code nor any other driver + * should rely on this file or its contents. + */ struct ath_node; @@ -54,15 +55,11 @@ struct ath_node; #define A_MAX(a, b) ((a) > (b) ? (a) : (b)) -#define ASSERT(exp) BUG_ON(!(exp)) - #define TSF_TO_TU(_h,_l) \ ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) #define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i)) -static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - struct ath_config { u32 ath_aggr_prot; u16 txpowlimit; @@ -103,18 +100,6 @@ enum buffer_type { BUF_XRETRY = BIT(5), }; -struct ath_buf_state { - int bfs_nframes; - u16 bfs_al; - u16 bfs_frmlen; - int bfs_seqno; - int bfs_tidno; - int bfs_retries; - u8 bf_type; - u32 bfs_keyix; - enum ath9k_key_type bfs_keytype; -}; - #define bf_nframes bf_state.bfs_nframes #define bf_al bf_state.bfs_al #define bf_frmlen bf_state.bfs_frmlen @@ -129,21 +114,6 @@ struct ath_buf_state { #define bf_isretried(bf) (bf->bf_state.bf_type & BUF_RETRY) #define bf_isxretried(bf) (bf->bf_state.bf_type & BUF_XRETRY) -struct ath_buf { - struct list_head list; - struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or - an aggregate) */ - struct ath_buf *bf_next; /* next subframe in the aggregate */ - struct sk_buff *bf_mpdu; /* enclosing frame structure */ - struct ath_desc *bf_desc; /* virtual addr of desc */ - dma_addr_t bf_daddr; /* physical addr of desc */ - dma_addr_t bf_buf_addr; /* physical addr of data buffer */ - bool bf_stale; - u16 bf_flags; - struct ath_buf_state bf_state; - dma_addr_t bf_dmacontext; -}; - struct ath_descdma { struct ath_desc *dd_desc; dma_addr_t dd_desc_paddr; @@ -163,13 +133,9 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_MAX_ANTENNA 3 #define ATH_RXBUF 512 -#define WME_NUM_TID 16 #define ATH_TXBUF 512 #define ATH_TXMAXTRY 13 #define ATH_MGT_TXMAXTRY 4 -#define WME_BA_BMP_SIZE 64 -#define WME_MAX_BA WME_BA_BMP_SIZE -#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) #define TID_TO_WME_AC(_tid) \ ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ @@ -177,12 +143,6 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ WME_AC_VO) -#define WME_AC_BE 0 -#define WME_AC_BK 1 -#define WME_AC_VI 2 -#define WME_AC_VO 3 -#define WME_NUM_AC 4 - #define ADDBA_EXCHANGE_ATTEMPTS 10 #define ATH_AGGR_DELIM_SZ 4 #define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */ @@ -191,7 +151,6 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, /* minimum h/w qdepth to be sustained to maximize aggregation */ #define ATH_AGGR_MIN_QDEPTH 2 #define ATH_AMPDU_SUBFRAME_DEFAULT 32 -#define ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1) #define IEEE80211_SEQ_SEQ_SHIFT 4 #define IEEE80211_SEQ_MAX 4096 @@ -238,18 +197,8 @@ struct ath_txq { struct list_head axq_q; spinlock_t axq_lock; u32 axq_depth; - u8 axq_aggr_depth; bool stopped; bool axq_tx_inprogress; - struct ath_buf *axq_linkbuf; - - /* first desc of the last descriptor that contains CTS */ - struct ath_desc *axq_lastdsWithCTS; - - /* final desc of the gating desc that determines whether - lastdsWithCTS has been DMA'ed or not */ - struct ath_desc *axq_gatingds; - struct list_head axq_acq; }; @@ -257,30 +206,6 @@ struct ath_txq { #define AGGR_ADDBA_COMPLETE BIT(2) #define AGGR_ADDBA_PROGRESS BIT(3) -struct ath_atx_tid { - struct list_head list; - struct list_head buf_q; - struct ath_node *an; - struct ath_atx_ac *ac; - struct ath_buf *tx_buf[ATH_TID_MAX_BUFS]; - u16 seq_start; - u16 seq_next; - u16 baw_size; - int tidno; - int baw_head; /* first un-acked tx buffer */ - int baw_tail; /* next unused tx buffer slot */ - int sched; - int paused; - u8 state; -}; - -struct ath_atx_ac { - int sched; - int qnum; - struct list_head list; - struct list_head tid_q; -}; - struct ath_tx_control { struct ath_txq *txq; int if_id; @@ -291,30 +216,6 @@ struct ath_tx_control { #define ATH_TX_XRETRY 0x02 #define ATH_TX_BAR 0x04 -#define ATH_RSSI_LPF_LEN 10 -#define RSSI_LPF_THRESHOLD -20 -#define ATH9K_RSSI_BAD 0x80 -#define ATH_RSSI_EP_MULTIPLIER (1<<7) -#define ATH_EP_MUL(x, mul) ((x) * (mul)) -#define ATH_RSSI_IN(x) (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER)) -#define ATH_LPF_RSSI(x, y, len) \ - ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) -#define ATH_RSSI_LPF(x, y) do { \ - if ((y) >= RSSI_LPF_THRESHOLD) \ - x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ -} while (0) -#define ATH_EP_RND(x, mul) \ - ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) - -struct ath_node { - struct ath_softc *an_sc; - struct ath_atx_tid tid[WME_NUM_TID]; - struct ath_atx_ac ac[WME_NUM_AC]; - u16 maxampdu; - u8 mpdudensity; - int last_rssi; -}; - struct ath_tx { u16 seq_no; u32 txqsetup; @@ -329,7 +230,6 @@ struct ath_rx { u8 defant; u8 rxotherant; u32 *rxlink; - int bufsize; unsigned int rxfilter; spinlock_t rxflushlock; spinlock_t rxbuflock; @@ -427,9 +327,9 @@ struct ath_beacon { void ath_beacon_tasklet(unsigned long data); void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif); -int ath_beaconq_setup(struct ath_hw *ah); int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif); void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp); +int ath_beaconq_config(struct ath_softc *sc); /*******/ /* ANI */ @@ -441,14 +341,24 @@ void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp); #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ -struct ath_ani { - bool caldone; - int16_t noise_floor; - unsigned int longcal_timer; - unsigned int shortcal_timer; - unsigned int resetcal_timer; - unsigned int checkani_timer; - struct timer_list timer; +/* Defines the BT AR_BT_COEX_WGHT used */ +enum ath_stomp_type { + ATH_BTCOEX_NO_STOMP, + ATH_BTCOEX_STOMP_ALL, + ATH_BTCOEX_STOMP_LOW, + ATH_BTCOEX_STOMP_NONE +}; + +struct ath_btcoex { + bool hw_timer_enabled; + spinlock_t btcoex_lock; + struct timer_list period_timer; /* Timer for BT period */ + u32 bt_priority_cnt; + unsigned long bt_priority_time; + int bt_stomp_type; /* Types of BT stomping */ + u32 btcoex_no_stomp; /* in usec */ + u32 btcoex_period; /* in usec */ + struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ }; /********************/ @@ -484,25 +394,13 @@ struct ath_led { * Used when PCI device not fully initialized by bootrom/BIOS */ #define DEFAULT_CACHELINE 32 -#define ATH_DEFAULT_NOISE_FLOOR -95 #define ATH_REGCLASSIDS_MAX 10 #define ATH_CABQ_READY_TIME 80 /* % of beacon interval */ #define ATH_MAX_SW_RETRIES 10 #define ATH_CHAN_MAX 255 #define IEEE80211_WEP_NKID 4 /* number of key ids */ -/* - * The key cache is used for h/w cipher state and also for - * tracking station state such as the current tx antenna. - * We also setup a mapping table between key cache slot indices - * and station state to short-circuit node lookups on rx. - * Different parts have different size key caches. We handle - * up to ATH_KEYMAX entries (could dynamically allocate state). - */ -#define ATH_KEYMAX 128 /* max key cache size we handle */ - #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ -#define ATH_RSSI_DUMMY_MARKER 0x127 #define ATH_RATE_DUMMY_MARKER 0 #define SC_OP_INVALID BIT(0) @@ -522,23 +420,17 @@ struct ath_led { #define SC_OP_WAIT_FOR_PSPOLL_DATA BIT(17) #define SC_OP_WAIT_FOR_TX_ACK BIT(18) #define SC_OP_BEACON_SYNC BIT(19) -#define SC_OP_BTCOEX_ENABLED BIT(20) #define SC_OP_BT_PRIORITY_DETECTED BIT(21) - -struct ath_bus_ops { - void (*read_cachesize)(struct ath_softc *sc, int *csz); - void (*cleanup)(struct ath_softc *sc); - bool (*eeprom_read)(struct ath_hw *ah, u32 off, u16 *data); -}; +#define SC_OP_NULLFUNC_COMPLETED BIT(22) +#define SC_OP_PS_ENABLED BIT(23) struct ath_wiphy; +struct ath_rate_table; struct ath_softc { struct ieee80211_hw *hw; struct device *dev; - struct ath_common common; - spinlock_t wiphy_lock; /* spinlock to protect ath_wiphy data */ struct ath_wiphy *pri_wiphy; struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may @@ -565,32 +457,21 @@ struct ath_softc { spinlock_t sc_pm_lock; struct mutex mutex; - u8 curbssid[ETH_ALEN]; - u8 bssidmask[ETH_ALEN]; u32 intrstatus; u32 sc_flags; /* SC_OP_* */ u16 curtxpow; - u16 curaid; u8 nbcnvifs; u16 nvifs; - u8 tx_chainmask; - u8 rx_chainmask; - u32 keymax; - DECLARE_BITMAP(keymap, ATH_KEYMAX); - u8 splitmic; bool ps_enabled; unsigned long ps_usecount; enum ath9k_int imask; - enum ath9k_ht_extprotspacing ht_extprotspacing; - enum ath9k_ht_macmode tx_chan_width; struct ath_config config; struct ath_rx rx; struct ath_tx tx; struct ath_beacon beacon; - struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; - const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX]; const struct ath_rate_table *cur_rate_table; + enum wireless_mode cur_rate_mode; struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; struct ath_led radio_led; @@ -605,14 +486,12 @@ struct ath_softc { int beacon_interval; - struct ath_ani ani; -#ifdef CONFIG_ATH9K_DEBUG +#ifdef CONFIG_ATH9K_DEBUGFS struct ath9k_debug debug; #endif - struct ath_bus_ops *bus_ops; struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; - struct ath_btcoex_info btcoex_info; + struct ath_btcoex btcoex; }; struct ath_wiphy { @@ -625,6 +504,7 @@ struct ath_wiphy { ATH_WIPHY_PAUSED, ATH_WIPHY_SCAN, } state; + bool idle; int chan_idx; int chan_is_ht; }; @@ -634,31 +514,22 @@ int ath_get_hal_qnum(u16 queue, struct ath_softc *sc); int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc); int ath_cabq_update(struct ath_softc *); -static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah) -{ - return &ah->ah_sc->common; -} - -static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah) +static inline void ath_read_cachesize(struct ath_common *common, int *csz) { - return &(ath9k_hw_common(ah)->regulatory); + common->bus_ops->read_cachesize(common, csz); } -static inline void ath_read_cachesize(struct ath_softc *sc, int *csz) +static inline void ath_bus_cleanup(struct ath_common *common) { - sc->bus_ops->read_cachesize(sc, csz); -} - -static inline void ath_bus_cleanup(struct ath_softc *sc) -{ - sc->bus_ops->cleanup(sc); + common->bus_ops->cleanup(common); } extern struct ieee80211_ops ath9k_ops; irqreturn_t ath_isr(int irq, void *dev); void ath_cleanup(struct ath_softc *sc); -int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid); +int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, + const struct ath_bus_ops *bus_ops); void ath_detach(struct ath_softc *sc); const char *ath_mac_bb_name(u32 mac_bb_version); const char *ath_rf_name(u16 rf_version); @@ -668,8 +539,9 @@ void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, void ath_update_chainmask(struct ath_softc *sc, int is_ht); int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, struct ath9k_channel *hchan); -void ath_radio_enable(struct ath_softc *sc); -void ath_radio_disable(struct ath_softc *sc); + +void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw); +void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw); #ifdef CONFIG_PCI int ath_pci_init(void); @@ -705,9 +577,10 @@ void ath9k_wiphy_pause_all_forced(struct ath_softc *sc, bool ath9k_wiphy_scanning(struct ath_softc *sc); void ath9k_wiphy_work(struct work_struct *work); bool ath9k_all_wiphys_idle(struct ath_softc *sc); +void ath9k_set_wiphy_idle(struct ath_wiphy *aphy, bool idle); -void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val); -unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset); +void ath_mac80211_stop_queue(struct ath_softc *sc, u16 skb_queue); +void ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue); int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype); #endif /* ATH9K_H */ diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 45c4ea57616b..1660ef17aaf5 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -23,10 +23,12 @@ * the operating mode of the station (AP or AdHoc). Parameters are AIFS * settings and channel width min/max */ -static int ath_beaconq_config(struct ath_softc *sc) +int ath_beaconq_config(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; - struct ath9k_tx_queue_info qi; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_tx_queue_info qi, qi_be; + int qnum; ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { @@ -36,14 +38,17 @@ static int ath_beaconq_config(struct ath_softc *sc) qi.tqi_cwmax = 0; } else { /* Adhoc mode; important thing is to use 2x cwmin. */ - qi.tqi_aifs = sc->beacon.beacon_qi.tqi_aifs; - qi.tqi_cwmin = 2*sc->beacon.beacon_qi.tqi_cwmin; - qi.tqi_cwmax = sc->beacon.beacon_qi.tqi_cwmax; + qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, + ATH9K_WME_AC_BE); + ath9k_hw_get_txq_props(ah, qnum, &qi_be); + qi.tqi_aifs = qi_be.tqi_aifs; + qi.tqi_cwmin = 4*qi_be.tqi_cwmin; + qi.tqi_cwmax = qi_be.tqi_cwmax; } if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to update h/w beacon queue parameters\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to update h/w beacon queue parameters\n"); return 0; } else { ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); @@ -61,11 +66,12 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, { struct sk_buff *skb = bf->bf_mpdu; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_desc *ds; struct ath9k_11n_rate_series series[4]; - const struct ath_rate_table *rt; int flags, antenna, ctsrate = 0, ctsduration = 0; - u8 rate; + struct ieee80211_supported_band *sband; + u8 rate = 0; ds = bf->bf_desc; flags = ATH9K_TXDESC_NOACK; @@ -89,10 +95,10 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, ds->ds_data = bf->bf_buf_addr; - rt = sc->cur_rate_table; - rate = rt->info[0].ratecode; + sband = &sc->sbands[common->hw->conf.channel->band]; + rate = sband->bitrates[0].hw_value; if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) - rate |= rt->info[0].short_preamble; + rate |= sband->bitrates[0].hw_value_short; ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN, ATH9K_PKT_TYPE_BEACON, @@ -108,7 +114,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4); series[0].Tries = 1; series[0].Rate = rate; - series[0].ChSel = sc->tx_chainmask; + series[0].ChSel = common->tx_chainmask; series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration, series, 4, 0); @@ -119,6 +125,7 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_buf *bf; struct ath_vif *avp; struct sk_buff *skb; @@ -172,7 +179,8 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; - DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error on beaconing\n"); + ath_print(common, ATH_DBG_FATAL, + "dma_mapping_error on beaconing\n"); return NULL; } @@ -192,8 +200,8 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, if (skb && cabq_depth) { if (sc->nvifs > 1) { - DPRINTF(sc, ATH_DBG_BEACON, - "Flushing previous cabq traffic\n"); + ath_print(common, ATH_DBG_BEACON, + "Flushing previous cabq traffic\n"); ath_draintxq(sc, cabq, false); } } @@ -216,6 +224,7 @@ static void ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf; struct ath_vif *avp; struct sk_buff *skb; @@ -233,25 +242,14 @@ static void ath_beacon_start_adhoc(struct ath_softc *sc, /* NB: caller is known to have already stopped tx dma */ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); ath9k_hw_txstart(ah, sc->beacon.beaconq); - DPRINTF(sc, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n", - sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc); -} - -int ath_beaconq_setup(struct ath_hw *ah) -{ - struct ath9k_tx_queue_info qi; - - memset(&qi, 0, sizeof(qi)); - qi.tqi_aifs = 1; - qi.tqi_cwmin = 0; - qi.tqi_cwmax = 0; - /* NB: don't enable any interrupts */ - return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); + ath_print(common, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n", + sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc); } int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) { struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp; struct ath_buf *bf; struct sk_buff *skb; @@ -309,7 +307,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) /* NB: the beacon data buffer must be 32-bit aligned. */ skb = ieee80211_beacon_get(sc->hw, vif); if (skb == NULL) { - DPRINTF(sc, ATH_DBG_BEACON, "cannot get skb\n"); + ath_print(common, ATH_DBG_BEACON, "cannot get skb\n"); return -ENOMEM; } @@ -333,9 +331,10 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) tsfadjust = intval * avp->av_bslot / ATH_BCBUF; avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); - DPRINTF(sc, ATH_DBG_BEACON, - "stagger beacons, bslot %d intval %u tsfadjust %llu\n", - avp->av_bslot, intval, (unsigned long long)tsfadjust); + ath_print(common, ATH_DBG_BEACON, + "stagger beacons, bslot %d intval " + "%u tsfadjust %llu\n", + avp->av_bslot, intval, (unsigned long long)tsfadjust); ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = avp->tsf_adjust; @@ -349,8 +348,8 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; - DPRINTF(sc, ATH_DBG_FATAL, - "dma_mapping_error on beacon alloc\n"); + ath_print(common, ATH_DBG_FATAL, + "dma_mapping_error on beacon alloc\n"); return -ENOMEM; } @@ -386,6 +385,7 @@ void ath_beacon_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf = NULL; struct ieee80211_vif *vif; struct ath_wiphy *aphy; @@ -405,12 +405,12 @@ void ath_beacon_tasklet(unsigned long data) sc->beacon.bmisscnt++; if (sc->beacon.bmisscnt < BSTUCK_THRESH) { - DPRINTF(sc, ATH_DBG_BEACON, - "missed %u consecutive beacons\n", - sc->beacon.bmisscnt); + ath_print(common, ATH_DBG_BEACON, + "missed %u consecutive beacons\n", + sc->beacon.bmisscnt); } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { - DPRINTF(sc, ATH_DBG_BEACON, - "beacon is officially stuck\n"); + ath_print(common, ATH_DBG_BEACON, + "beacon is officially stuck\n"); sc->sc_flags |= SC_OP_TSF_RESET; ath_reset(sc, false); } @@ -419,9 +419,9 @@ void ath_beacon_tasklet(unsigned long data) } if (sc->beacon.bmisscnt != 0) { - DPRINTF(sc, ATH_DBG_BEACON, - "resume beacon xmit after %u misses\n", - sc->beacon.bmisscnt); + ath_print(common, ATH_DBG_BEACON, + "resume beacon xmit after %u misses\n", + sc->beacon.bmisscnt); sc->beacon.bmisscnt = 0; } @@ -447,9 +447,9 @@ void ath_beacon_tasklet(unsigned long data) vif = sc->beacon.bslot[slot]; aphy = sc->beacon.bslot_aphy[slot]; - DPRINTF(sc, ATH_DBG_BEACON, - "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", - slot, tsf, tsftu, intval, vif); + ath_print(common, ATH_DBG_BEACON, + "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", + slot, tsf, tsftu, intval, vif); bfaddr = 0; if (vif) { @@ -490,7 +490,7 @@ void ath_beacon_tasklet(unsigned long data) * are still pending on the queue. */ if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) { - DPRINTF(sc, ATH_DBG_FATAL, + ath_print(common, ATH_DBG_FATAL, "beacon queue %u did not stop?\n", sc->beacon.beaconq); } @@ -502,6 +502,19 @@ void ath_beacon_tasklet(unsigned long data) } } +static void ath9k_beacon_init(struct ath_softc *sc, + u32 next_beacon, + u32 beacon_period) +{ + if (beacon_period & ATH9K_BEACON_RESET_TSF) + ath9k_ps_wakeup(sc); + + ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period); + + if (beacon_period & ATH9K_BEACON_RESET_TSF) + ath9k_ps_restore(sc); +} + /* * For multi-bss ap support beacons are either staggered evenly over N slots or * burst together. For the former arrange for the SWBA to be delivered for each @@ -534,7 +547,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc, /* Set the computed AP beacon timers */ ath9k_hw_set_interrupts(sc->sc_ah, 0); - ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval); + ath9k_beacon_init(sc, nexttbtt, intval); sc->beacon.bmisscnt = 0; ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); @@ -555,6 +568,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc, static void ath_beacon_config_sta(struct ath_softc *sc, struct ath_beacon_config *conf) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath9k_beacon_state bs; int dtimperiod, dtimcount, sleepduration; int cfpperiod, cfpcount; @@ -651,11 +665,11 @@ static void ath_beacon_config_sta(struct ath_softc *sc, /* TSF out of range threshold fixed at 1 second */ bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; - DPRINTF(sc, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); - DPRINTF(sc, ATH_DBG_BEACON, - "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", - bs.bs_bmissthreshold, bs.bs_sleepduration, - bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); + ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); + ath_print(common, ATH_DBG_BEACON, + "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", + bs.bs_bmissthreshold, bs.bs_sleepduration, + bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); /* Set the computed STA beacon timers */ @@ -669,6 +683,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, struct ath_beacon_config *conf, struct ieee80211_vif *vif) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); u64 tsf; u32 tsftu, intval, nexttbtt; @@ -689,9 +704,9 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, nexttbtt += intval; } while (nexttbtt < tsftu); - DPRINTF(sc, ATH_DBG_BEACON, - "IBSS nexttbtt %u intval %u (%u)\n", - nexttbtt, intval, conf->beacon_interval); + ath_print(common, ATH_DBG_BEACON, + "IBSS nexttbtt %u intval %u (%u)\n", + nexttbtt, intval, conf->beacon_interval); /* * In IBSS mode enable the beacon timers but only enable SWBA interrupts @@ -707,7 +722,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, /* Set the computed ADHOC beacon timers */ ath9k_hw_set_interrupts(sc->sc_ah, 0); - ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval); + ath9k_beacon_init(sc, nexttbtt, intval); sc->beacon.bmisscnt = 0; ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); @@ -719,6 +734,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); enum nl80211_iftype iftype; /* Setup the beacon configuration parameters */ @@ -759,8 +775,8 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) ath_beacon_config_sta(sc, cur_conf); break; default: - DPRINTF(sc, ATH_DBG_CONFIG, - "Unsupported beaconing mode\n"); + ath_print(common, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); return; } diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index 55f607b7699e..fb4ac15f3b93 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -14,10 +14,26 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +#include "hw.h" -static const struct ath_btcoex_config ath_bt_config = { 0, true, true, - ATH_BT_COEX_MODE_SLOTTED, true, true, 2, 5, true }; +enum ath_bt_mode { + ATH_BT_COEX_MODE_LEGACY, /* legacy rx_clear mode */ + ATH_BT_COEX_MODE_UNSLOTTED, /* untimed/unslotted mode */ + ATH_BT_COEX_MODE_SLOTTED, /* slotted mode */ + ATH_BT_COEX_MODE_DISALBED, /* coexistence disabled */ +}; + +struct ath_btcoex_config { + u8 bt_time_extend; + bool bt_txstate_extend; + bool bt_txframe_extend; + enum ath_bt_mode bt_mode; /* coexistence mode */ + bool bt_quiet_collision; + bool bt_rxclear_polarity; /* invert rx_clear as WLAN_ACTIVE*/ + u8 bt_priority_time; + u8 bt_first_slot_time; + bool bt_hold_rx_clear; +}; static const u16 ath_subsysid_tbl[] = { AR9280_COEX2WIRE_SUBSYSID, @@ -29,141 +45,38 @@ static const u16 ath_subsysid_tbl[] = { * Checks the subsystem id of the device to see if it * supports btcoex */ -bool ath_btcoex_supported(u16 subsysid) +bool ath9k_hw_btcoex_supported(struct ath_hw *ah) { int i; - if (!subsysid) + if (!ah->hw_version.subsysid) return false; for (i = 0; i < ARRAY_SIZE(ath_subsysid_tbl); i++) - if (subsysid == ath_subsysid_tbl[i]) + if (ah->hw_version.subsysid == ath_subsysid_tbl[i]) return true; return false; } -/* - * Detects if there is any priority bt traffic - */ -static void ath_detect_bt_priority(struct ath_softc *sc) -{ - struct ath_btcoex_info *btinfo = &sc->btcoex_info; - - if (ath9k_hw_gpio_get(sc->sc_ah, btinfo->btpriority_gpio)) - btinfo->bt_priority_cnt++; - - if (time_after(jiffies, btinfo->bt_priority_time + - msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { - if (btinfo->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { - DPRINTF(sc, ATH_DBG_BTCOEX, - "BT priority traffic detected"); - sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; - } else { - sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; - } - - btinfo->bt_priority_cnt = 0; - btinfo->bt_priority_time = jiffies; - } -} - -/* - * Configures appropriate weight based on stomp type. - */ -static void ath_btcoex_bt_stomp(struct ath_softc *sc, - struct ath_btcoex_info *btinfo, - int stomp_type) -{ - - switch (stomp_type) { - case ATH_BTCOEX_STOMP_ALL: - ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, - AR_STOMP_ALL_WLAN_WGHT); - break; - case ATH_BTCOEX_STOMP_LOW: - ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, - AR_STOMP_LOW_WLAN_WGHT); - break; - case ATH_BTCOEX_STOMP_NONE: - ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, - AR_STOMP_NONE_WLAN_WGHT); - break; - default: - DPRINTF(sc, ATH_DBG_BTCOEX, "Invalid Stomptype\n"); - break; - } - - ath9k_hw_btcoex_enable(sc->sc_ah); -} - -/* - * This is the master bt coex timer which runs for every - * 45ms, bt traffic will be given priority during 55% of this - * period while wlan gets remaining 45% - */ - -static void ath_btcoex_period_timer(unsigned long data) -{ - struct ath_softc *sc = (struct ath_softc *) data; - struct ath_btcoex_info *btinfo = &sc->btcoex_info; - - ath_detect_bt_priority(sc); - - spin_lock_bh(&btinfo->btcoex_lock); - - ath_btcoex_bt_stomp(sc, btinfo, btinfo->bt_stomp_type); - - spin_unlock_bh(&btinfo->btcoex_lock); - - if (btinfo->btcoex_period != btinfo->btcoex_no_stomp) { - if (btinfo->hw_timer_enabled) - ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); - - ath_gen_timer_start(sc->sc_ah, - btinfo->no_stomp_timer, - (ath9k_hw_gettsf32(sc->sc_ah) + - btinfo->btcoex_no_stomp), - btinfo->btcoex_no_stomp * 10); - btinfo->hw_timer_enabled = true; - } - - mod_timer(&btinfo->period_timer, jiffies + - msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); -} - -/* - * Generic tsf based hw timer which configures weight - * registers to time slice between wlan and bt traffic - */ - -static void ath_btcoex_no_stomp_timer(void *arg) -{ - struct ath_softc *sc = (struct ath_softc *)arg; - struct ath_btcoex_info *btinfo = &sc->btcoex_info; - - DPRINTF(sc, ATH_DBG_BTCOEX, "no stomp timer running \n"); - - spin_lock_bh(&btinfo->btcoex_lock); - - if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_LOW) - ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_NONE); - else if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) - ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_LOW); - - spin_unlock_bh(&btinfo->btcoex_lock); -} - -static int ath_init_btcoex_info(struct ath_hw *hw, - struct ath_btcoex_info *btcoex_info) +void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) { + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; + const struct ath_btcoex_config ath_bt_config = { + .bt_time_extend = 0, + .bt_txstate_extend = true, + .bt_txframe_extend = true, + .bt_mode = ATH_BT_COEX_MODE_SLOTTED, + .bt_quiet_collision = true, + .bt_rxclear_polarity = true, + .bt_priority_time = 2, + .bt_first_slot_time = 5, + .bt_hold_rx_clear = true, + }; u32 i; - int qnum; - qnum = ath_tx_get_qnum(hw->ah_sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); - - btcoex_info->bt_coex_mode = - (btcoex_info->bt_coex_mode & AR_BT_QCU_THRESH) | + btcoex_hw->bt_coex_mode = + (btcoex_hw->bt_coex_mode & AR_BT_QCU_THRESH) | SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) | SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) | SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) | @@ -174,167 +87,141 @@ static int ath_init_btcoex_info(struct ath_hw *hw, SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) | SM(qnum, AR_BT_QCU_THRESH); - btcoex_info->bt_coex_mode2 = + btcoex_hw->bt_coex_mode2 = SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) | SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) | AR_BT_DISABLE_BT_ANT; - btcoex_info->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + for (i = 0; i < 32; i++) + ah->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i; +} +EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw); - btcoex_info->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; +void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah) +{ + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - btcoex_info->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * - btcoex_info->btcoex_period / 100; + /* connect bt_active to baseband */ + REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, + (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | + AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); - for (i = 0; i < 32; i++) - hw->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i; + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); - setup_timer(&btcoex_info->period_timer, ath_btcoex_period_timer, - (unsigned long) hw->ah_sc); + /* Set input mux for bt_active to gpio pin */ + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_ACTIVE, + btcoex_hw->btactive_gpio); - btcoex_info->no_stomp_timer = ath_gen_timer_alloc(hw, - ath_btcoex_no_stomp_timer, - ath_btcoex_no_stomp_timer, - (void *)hw->ah_sc, AR_FIRST_NDP_TIMER); + /* Configure the desired gpio port for input */ + ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio); +} +EXPORT_SYMBOL(ath9k_hw_btcoex_init_2wire); + +void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah) +{ + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - if (btcoex_info->no_stomp_timer == NULL) - return -ENOMEM; + /* btcoex 3-wire */ + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | + AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); - spin_lock_init(&btcoex_info->btcoex_lock); + /* Set input mux for bt_prority_async and + * bt_active_async to GPIO pins */ + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_ACTIVE, + btcoex_hw->btactive_gpio); - return 0; + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_PRIORITY, + btcoex_hw->btpriority_gpio); + + /* Configure the desired GPIO ports for input */ + + ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio); + ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btpriority_gpio); } +EXPORT_SYMBOL(ath9k_hw_btcoex_init_3wire); -int ath9k_hw_btcoex_init(struct ath_hw *ah) +static void ath9k_hw_btcoex_enable_2wire(struct ath_hw *ah) { - struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; - int ret = 0; - - if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) { - /* connect bt_active to baseband */ - REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, - (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | - AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); - - REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, - AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); - - /* Set input mux for bt_active to gpio pin */ - REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, - AR_GPIO_INPUT_MUX1_BT_ACTIVE, - btcoex_info->btactive_gpio); - - /* Configure the desired gpio port for input */ - ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio); - } else { - /* btcoex 3-wire */ - REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, - (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | - AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); - - /* Set input mux for bt_prority_async and - * bt_active_async to GPIO pins */ - REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, - AR_GPIO_INPUT_MUX1_BT_ACTIVE, - btcoex_info->btactive_gpio); - - REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, - AR_GPIO_INPUT_MUX1_BT_PRIORITY, - btcoex_info->btpriority_gpio); - - /* Configure the desired GPIO ports for input */ - - ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio); - ath9k_hw_cfg_gpio_input(ah, btcoex_info->btpriority_gpio); - - ret = ath_init_btcoex_info(ah, btcoex_info); - } + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - return ret; + /* Configure the desired GPIO port for TX_FRAME output */ + ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio, + AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); } -void ath9k_hw_btcoex_enable(struct ath_hw *ah) +void ath9k_hw_btcoex_set_weight(struct ath_hw *ah, + u32 bt_weight, + u32 wlan_weight) { - struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; - - if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) { - /* Configure the desired GPIO port for TX_FRAME output */ - ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, - AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); - } else { - /* - * Program coex mode and weight registers to - * enable coex 3-wire - */ - REG_WRITE(ah, AR_BT_COEX_MODE, btcoex_info->bt_coex_mode); - REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_info->bt_coex_weights); - REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_info->bt_coex_mode2); - - REG_RMW_FIELD(ah, AR_QUIET1, - AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); - REG_RMW_FIELD(ah, AR_PCU_MISC, - AR_PCU_BT_ANT_PREVENT_RX, 0); - - ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, - AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL); - } + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - REG_RMW(ah, AR_GPIO_PDPU, - (0x2 << (btcoex_info->btactive_gpio * 2)), - (0x3 << (btcoex_info->btactive_gpio * 2))); - - ah->ah_sc->sc_flags |= SC_OP_BTCOEX_ENABLED; + btcoex_hw->bt_coex_weights = SM(bt_weight, AR_BTCOEX_BT_WGHT) | + SM(wlan_weight, AR_BTCOEX_WL_WGHT); } +EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight); -void ath9k_hw_btcoex_disable(struct ath_hw *ah) +static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah) { - struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - ath9k_hw_set_gpio(ah, btcoex_info->wlanactive_gpio, 0); + /* + * Program coex mode and weight registers to + * enable coex 3-wire + */ + REG_WRITE(ah, AR_BT_COEX_MODE, btcoex_hw->bt_coex_mode); + REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_hw->bt_coex_weights); + REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_hw->bt_coex_mode2); - ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); + REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); - if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) { - REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); - REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0); - REG_WRITE(ah, AR_BT_COEX_MODE2, 0); - } - - ah->ah_sc->sc_flags &= ~SC_OP_BTCOEX_ENABLED; + ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio, + AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL); } -/* - * Pause btcoex timer and bt duty cycle timer - */ -void ath_btcoex_timer_pause(struct ath_softc *sc, - struct ath_btcoex_info *btinfo) +void ath9k_hw_btcoex_enable(struct ath_hw *ah) { + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - del_timer_sync(&btinfo->period_timer); + switch (btcoex_hw->scheme) { + case ATH_BTCOEX_CFG_NONE: + break; + case ATH_BTCOEX_CFG_2WIRE: + ath9k_hw_btcoex_enable_2wire(ah); + break; + case ATH_BTCOEX_CFG_3WIRE: + ath9k_hw_btcoex_enable_3wire(ah); + break; + } - if (btinfo->hw_timer_enabled) - ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); + REG_RMW(ah, AR_GPIO_PDPU, + (0x2 << (btcoex_hw->btactive_gpio * 2)), + (0x3 << (btcoex_hw->btactive_gpio * 2))); - btinfo->hw_timer_enabled = false; + ah->btcoex_hw.enabled = true; } +EXPORT_SYMBOL(ath9k_hw_btcoex_enable); -/* - * (Re)start btcoex timers - */ -void ath_btcoex_timer_resume(struct ath_softc *sc, - struct ath_btcoex_info *btinfo) +void ath9k_hw_btcoex_disable(struct ath_hw *ah) { + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - DPRINTF(sc, ATH_DBG_BTCOEX, "Starting btcoex timers"); + ath9k_hw_set_gpio(ah, btcoex_hw->wlanactive_gpio, 0); - /* make sure duty cycle timer is also stopped when resuming */ - if (btinfo->hw_timer_enabled) - ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); + ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - btinfo->bt_priority_cnt = 0; - btinfo->bt_priority_time = jiffies; - sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; + if (btcoex_hw->scheme == ATH_BTCOEX_CFG_3WIRE) { + REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); + REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0); + REG_WRITE(ah, AR_BT_COEX_MODE2, 0); + } - mod_timer(&btinfo->period_timer, jiffies); + ah->btcoex_hw.enabled = false; } +EXPORT_SYMBOL(ath9k_hw_btcoex_disable); diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 297b027fd3c3..1ba31a73317c 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -17,6 +17,8 @@ #ifndef BTCOEX_H #define BTCOEX_H +#include "hw.h" + #define ATH_WLANACTIVE_GPIO 5 #define ATH_BTACTIVE_GPIO 6 #define ATH_BTPRIORITY_GPIO 7 @@ -34,67 +36,25 @@ enum ath_btcoex_scheme { ATH_BTCOEX_CFG_3WIRE, }; -enum ath_stomp_type { - ATH_BTCOEX_NO_STOMP, - ATH_BTCOEX_STOMP_ALL, - ATH_BTCOEX_STOMP_LOW, - ATH_BTCOEX_STOMP_NONE -}; - -enum ath_bt_mode { - ATH_BT_COEX_MODE_LEGACY, /* legacy rx_clear mode */ - ATH_BT_COEX_MODE_UNSLOTTED, /* untimed/unslotted mode */ - ATH_BT_COEX_MODE_SLOTTED, /* slotted mode */ - ATH_BT_COEX_MODE_DISALBED, /* coexistence disabled */ -}; - -struct ath_btcoex_config { - u8 bt_time_extend; - bool bt_txstate_extend; - bool bt_txframe_extend; - enum ath_bt_mode bt_mode; /* coexistence mode */ - bool bt_quiet_collision; - bool bt_rxclear_polarity; /* invert rx_clear as WLAN_ACTIVE*/ - u8 bt_priority_time; - u8 bt_first_slot_time; - bool bt_hold_rx_clear; -}; - -struct ath_btcoex_info { - enum ath_btcoex_scheme btcoex_scheme; +struct ath_btcoex_hw { + enum ath_btcoex_scheme scheme; + bool enabled; u8 wlanactive_gpio; u8 btactive_gpio; u8 btpriority_gpio; - u8 bt_duty_cycle; /* BT duty cycle in percentage */ - int bt_stomp_type; /* Types of BT stomping */ u32 bt_coex_mode; /* Register setting for AR_BT_COEX_MODE */ u32 bt_coex_weights; /* Register setting for AR_BT_COEX_WEIGHT */ u32 bt_coex_mode2; /* Register setting for AR_BT_COEX_MODE2 */ - u32 btcoex_no_stomp; /* in usec */ - u32 btcoex_period; /* in usec */ - u32 bt_priority_cnt; - unsigned long bt_priority_time; - bool hw_timer_enabled; - spinlock_t btcoex_lock; - struct timer_list period_timer; /* Timer for BT period */ - struct ath_gen_timer *no_stomp_timer; /*Timer for no BT stomping*/ }; -bool ath_btcoex_supported(u16 subsysid); -int ath9k_hw_btcoex_init(struct ath_hw *ah); +bool ath9k_hw_btcoex_supported(struct ath_hw *ah); +void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah); +void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah); +void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum); +void ath9k_hw_btcoex_set_weight(struct ath_hw *ah, + u32 bt_weight, + u32 wlan_weight); void ath9k_hw_btcoex_enable(struct ath_hw *ah); void ath9k_hw_btcoex_disable(struct ath_hw *ah); -void ath_btcoex_timer_resume(struct ath_softc *sc, - struct ath_btcoex_info *btinfo); -void ath_btcoex_timer_pause(struct ath_softc *sc, - struct ath_btcoex_info *btinfo); - -static inline void ath_btcoex_set_weight(struct ath_btcoex_info *btcoex_info, - u32 bt_weight, - u32 wlan_weight) -{ - btcoex_info->bt_coex_weights = SM(bt_weight, AR_BTCOEX_BT_WGHT) | - SM(wlan_weight, AR_BTCOEX_WL_WGHT); -} #endif diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 0ad6d0b76e9e..238a5744d8e9 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +#include "hw.h" /* We can tune this as we go by monitoring really low values */ #define ATH9K_NF_TOO_LOW -60 @@ -26,11 +26,11 @@ static bool ath9k_hw_nf_in_range(struct ath_hw *ah, s16 nf) { if (nf > ATH9K_NF_TOO_LOW) { - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "noise floor value detected (%d) is " - "lower than what we think is a " - "reasonable value (%d)\n", - nf, ATH9K_NF_TOO_LOW); + ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, + "noise floor value detected (%d) is " + "lower than what we think is a " + "reasonable value (%d)\n", + nf, ATH9K_NF_TOO_LOW); return false; } return true; @@ -89,6 +89,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, static void ath9k_hw_do_getnf(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]) { + struct ath_common *common = ath9k_hw_common(ah); int16_t nf; if (AR_SREV_9280_10_OR_LATER(ah)) @@ -98,8 +99,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "NF calibrated [ctl] [chain 0] is %d\n", nf); + ath_print(common, ATH_DBG_CALIBRATE, + "NF calibrated [ctl] [chain 0] is %d\n", nf); nfarray[0] = nf; if (!AR_SREV_9285(ah)) { @@ -112,8 +113,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "NF calibrated [ctl] [chain 1] is %d\n", nf); + ath_print(common, ATH_DBG_CALIBRATE, + "NF calibrated [ctl] [chain 1] is %d\n", nf); nfarray[1] = nf; if (!AR_SREV_9280(ah) && !AR_SREV_9287(ah)) { @@ -121,8 +122,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, AR_PHY_CH2_MINCCA_PWR); if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "NF calibrated [ctl] [chain 2] is %d\n", nf); + ath_print(common, ATH_DBG_CALIBRATE, + "NF calibrated [ctl] [chain 2] is %d\n", nf); nfarray[2] = nf; } } @@ -136,8 +137,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "NF calibrated [ext] [chain 0] is %d\n", nf); + ath_print(common, ATH_DBG_CALIBRATE, + "NF calibrated [ext] [chain 0] is %d\n", nf); nfarray[3] = nf; if (!AR_SREV_9285(ah)) { @@ -150,8 +151,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "NF calibrated [ext] [chain 1] is %d\n", nf); + ath_print(common, ATH_DBG_CALIBRATE, + "NF calibrated [ext] [chain 1] is %d\n", nf); nfarray[4] = nf; if (!AR_SREV_9280(ah) && !AR_SREV_9287(ah)) { @@ -159,8 +160,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, AR_PHY_CH2_EXT_MINCCA_PWR); if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "NF calibrated [ext] [chain 2] is %d\n", nf); + ath_print(common, ATH_DBG_CALIBRATE, + "NF calibrated [ext] [chain 2] is %d\n", nf); nfarray[5] = nf; } } @@ -188,6 +189,8 @@ static bool getNoiseFloorThresh(struct ath_hw *ah, static void ath9k_hw_setup_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal) { + struct ath_common *common = ath9k_hw_common(ah); + REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0), AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, currCal->calData->calCountMax); @@ -195,23 +198,23 @@ static void ath9k_hw_setup_calibration(struct ath_hw *ah, switch (currCal->calData->calType) { case IQ_MISMATCH_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "starting IQ Mismatch Calibration\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "starting IQ Mismatch Calibration\n"); break; case ADC_GAIN_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "starting ADC Gain Calibration\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "starting ADC Gain Calibration\n"); break; case ADC_DC_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "starting ADC DC Calibration\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "starting ADC DC Calibration\n"); break; case ADC_DC_INIT_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "starting Init ADC DC Calibration\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "starting Init ADC DC Calibration\n"); break; } @@ -278,7 +281,7 @@ static bool ath9k_hw_per_calibration(struct ath_hw *ah, static bool ath9k_hw_iscal_supported(struct ath_hw *ah, enum ath9k_cal_types calType) { - struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; switch (calType & ah->supp_cals) { case IQ_MISMATCH_CAL: /* Both 2 GHz and 5 GHz support OFDM */ @@ -304,11 +307,11 @@ static void ath9k_hw_iqcal_collect(struct ath_hw *ah) REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ah->totalIqCorrMeas[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", - ah->cal_samples, i, ah->totalPowerMeasI[i], - ah->totalPowerMeasQ[i], - ah->totalIqCorrMeas[i]); + ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, + "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", + ah->cal_samples, i, ah->totalPowerMeasI[i], + ah->totalPowerMeasQ[i], + ah->totalIqCorrMeas[i]); } } @@ -326,14 +329,14 @@ static void ath9k_hw_adc_gaincal_collect(struct ath_hw *ah) ah->totalAdcQEvenPhase[i] += REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "%d: Chn %d oddi=0x%08x; eveni=0x%08x; " - "oddq=0x%08x; evenq=0x%08x;\n", - ah->cal_samples, i, - ah->totalAdcIOddPhase[i], - ah->totalAdcIEvenPhase[i], - ah->totalAdcQOddPhase[i], - ah->totalAdcQEvenPhase[i]); + ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, + "%d: Chn %d oddi=0x%08x; eveni=0x%08x; " + "oddq=0x%08x; evenq=0x%08x;\n", + ah->cal_samples, i, + ah->totalAdcIOddPhase[i], + ah->totalAdcIEvenPhase[i], + ah->totalAdcQOddPhase[i], + ah->totalAdcQEvenPhase[i]); } } @@ -351,19 +354,20 @@ static void ath9k_hw_adc_dccal_collect(struct ath_hw *ah) ah->totalAdcDcOffsetQEvenPhase[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "%d: Chn %d oddi=0x%08x; eveni=0x%08x; " - "oddq=0x%08x; evenq=0x%08x;\n", - ah->cal_samples, i, - ah->totalAdcDcOffsetIOddPhase[i], - ah->totalAdcDcOffsetIEvenPhase[i], - ah->totalAdcDcOffsetQOddPhase[i], - ah->totalAdcDcOffsetQEvenPhase[i]); + ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, + "%d: Chn %d oddi=0x%08x; eveni=0x%08x; " + "oddq=0x%08x; evenq=0x%08x;\n", + ah->cal_samples, i, + ah->totalAdcDcOffsetIOddPhase[i], + ah->totalAdcDcOffsetIEvenPhase[i], + ah->totalAdcDcOffsetQOddPhase[i], + ah->totalAdcDcOffsetQEvenPhase[i]); } } static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) { + struct ath_common *common = ath9k_hw_common(ah); u32 powerMeasQ, powerMeasI, iqCorrMeas; u32 qCoffDenom, iCoffDenom; int32_t qCoff, iCoff; @@ -374,13 +378,13 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) powerMeasQ = ah->totalPowerMeasQ[i]; iqCorrMeas = ah->totalIqCorrMeas[i]; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Starting IQ Cal and Correction for Chain %d\n", - i); + ath_print(common, ATH_DBG_CALIBRATE, + "Starting IQ Cal and Correction for Chain %d\n", + i); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Orignal: Chn %diq_corr_meas = 0x%08x\n", - i, ah->totalIqCorrMeas[i]); + ath_print(common, ATH_DBG_CALIBRATE, + "Orignal: Chn %diq_corr_meas = 0x%08x\n", + i, ah->totalIqCorrMeas[i]); iqCorrNeg = 0; @@ -389,27 +393,28 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) iqCorrNeg = 1; } - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n", - iqCorrNeg); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); + ath_print(common, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n", + iqCorrNeg); iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128; qCoffDenom = powerMeasQ / 64; - if (powerMeasQ != 0) { + if ((powerMeasQ != 0) && (iCoffDenom != 0) && + (qCoffDenom != 0)) { iCoff = iqCorrMeas / iCoffDenom; qCoff = powerMeasI / qCoffDenom - 64; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d iCoff = 0x%08x\n", i, iCoff); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d qCoff = 0x%08x\n", i, qCoff); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d iCoff = 0x%08x\n", i, iCoff); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d qCoff = 0x%08x\n", i, qCoff); iCoff = iCoff & 0x3f; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "New: Chn %d iCoff = 0x%08x\n", i, iCoff); + ath_print(common, ATH_DBG_CALIBRATE, + "New: Chn %d iCoff = 0x%08x\n", i, iCoff); if (iqCorrNeg == 0x0) iCoff = 0x40 - iCoff; @@ -418,9 +423,9 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) else if (qCoff <= -16) qCoff = 16; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", - i, iCoff, qCoff); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", + i, iCoff, qCoff); REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, @@ -428,9 +433,9 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "IQ Cal and Correction done for Chain %d\n", - i); + ath_print(common, ATH_DBG_CALIBRATE, + "IQ Cal and Correction done for Chain %d\n", + i); } } @@ -440,6 +445,7 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) static void ath9k_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains) { + struct ath_common *common = ath9k_hw_common(ah); u32 iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset, qEvenMeasOffset; u32 qGainMismatch, iGainMismatch, val, i; @@ -449,21 +455,21 @@ static void ath9k_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains) qOddMeasOffset = ah->totalAdcQOddPhase[i]; qEvenMeasOffset = ah->totalAdcQEvenPhase[i]; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Starting ADC Gain Cal for Chain %d\n", i); - - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_odd_i = 0x%08x\n", i, - iOddMeasOffset); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_even_i = 0x%08x\n", i, - iEvenMeasOffset); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_odd_q = 0x%08x\n", i, - qOddMeasOffset); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_even_q = 0x%08x\n", i, - qEvenMeasOffset); + ath_print(common, ATH_DBG_CALIBRATE, + "Starting ADC Gain Cal for Chain %d\n", i); + + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_i = 0x%08x\n", i, + iOddMeasOffset); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_i = 0x%08x\n", i, + iEvenMeasOffset); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_q = 0x%08x\n", i, + qOddMeasOffset); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_q = 0x%08x\n", i, + qEvenMeasOffset); if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) { iGainMismatch = @@ -473,20 +479,20 @@ static void ath9k_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains) ((qOddMeasOffset * 32) / qEvenMeasOffset) & 0x3f; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d gain_mismatch_i = 0x%08x\n", i, - iGainMismatch); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d gain_mismatch_q = 0x%08x\n", i, - qGainMismatch); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d gain_mismatch_i = 0x%08x\n", i, + iGainMismatch); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d gain_mismatch_q = 0x%08x\n", i, + qGainMismatch); val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); val &= 0xfffff000; val |= (qGainMismatch) | (iGainMismatch << 6); REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "ADC Gain Cal done for Chain %d\n", i); + ath_print(common, ATH_DBG_CALIBRATE, + "ADC Gain Cal done for Chain %d\n", i); } } @@ -497,6 +503,7 @@ static void ath9k_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains) static void ath9k_hw_adc_dccal_calibrate(struct ath_hw *ah, u8 numChains) { + struct ath_common *common = ath9k_hw_common(ah); u32 iOddMeasOffset, iEvenMeasOffset, val, i; int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch; const struct ath9k_percal_data *calData = @@ -510,41 +517,41 @@ static void ath9k_hw_adc_dccal_calibrate(struct ath_hw *ah, u8 numChains) qOddMeasOffset = ah->totalAdcDcOffsetQOddPhase[i]; qEvenMeasOffset = ah->totalAdcDcOffsetQEvenPhase[i]; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Starting ADC DC Offset Cal for Chain %d\n", i); - - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_odd_i = %d\n", i, - iOddMeasOffset); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_even_i = %d\n", i, - iEvenMeasOffset); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_odd_q = %d\n", i, - qOddMeasOffset); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d pwr_meas_even_q = %d\n", i, - qEvenMeasOffset); + ath_print(common, ATH_DBG_CALIBRATE, + "Starting ADC DC Offset Cal for Chain %d\n", i); + + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_i = %d\n", i, + iOddMeasOffset); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_i = %d\n", i, + iEvenMeasOffset); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_q = %d\n", i, + qOddMeasOffset); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_q = %d\n", i, + qEvenMeasOffset); iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) / numSamples) & 0x1ff; qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) / numSamples) & 0x1ff; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d dc_offset_mismatch_i = 0x%08x\n", i, - iDcMismatch); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Chn %d dc_offset_mismatch_q = 0x%08x\n", i, - qDcMismatch); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d dc_offset_mismatch_i = 0x%08x\n", i, + iDcMismatch); + ath_print(common, ATH_DBG_CALIBRATE, + "Chn %d dc_offset_mismatch_q = 0x%08x\n", i, + qDcMismatch); val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); val &= 0xc0000fff; val |= (qDcMismatch << 12) | (iDcMismatch << 21); REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "ADC DC Offset Cal done for Chain %d\n", i); + ath_print(common, ATH_DBG_CALIBRATE, + "ADC DC Offset Cal done for Chain %d\n", i); } REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0), @@ -555,7 +562,8 @@ static void ath9k_hw_adc_dccal_calibrate(struct ath_hw *ah, u8 numChains) /* This is done for the currently configured channel */ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) { - struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_conf *conf = &common->hw->conf; struct ath9k_cal_list *currCal = ah->cal_list_curr; if (!ah->curchan) @@ -568,24 +576,25 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) return true; if (currCal->calState != CAL_DONE) { - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Calibration state incorrect, %d\n", - currCal->calState); + ath_print(common, ATH_DBG_CALIBRATE, + "Calibration state incorrect, %d\n", + currCal->calState); return true; } if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType)) return true; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "Resetting Cal %d state for channel %u\n", - currCal->calData->calType, conf->channel->center_freq); + ath_print(common, ATH_DBG_CALIBRATE, + "Resetting Cal %d state for channel %u\n", + currCal->calData->calType, conf->channel->center_freq); ah->curchan->CalValid &= ~currCal->calData->calType; currCal->calState = CAL_WAITING; return false; } +EXPORT_SYMBOL(ath9k_hw_reset_calvalid); void ath9k_hw_start_nfcal(struct ath_hw *ah) { @@ -645,11 +654,11 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) AR_PHY_AGC_CONTROL_NO_UPDATE_NF); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); - for (j = 0; j < 1000; j++) { + for (j = 0; j < 5; j++) { if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0) break; - udelay(10); + udelay(50); } for (i = 0; i < NUM_NF_READINGS; i++) { @@ -665,6 +674,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) int16_t ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) { + struct ath_common *common = ath9k_hw_common(ah); int16_t nf, nfThresh; int16_t nfarray[NUM_NF_READINGS] = { 0 }; struct ath9k_nfcal_hist *h; @@ -672,8 +682,8 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, chan->channelFlags &= (~CHANNEL_CW_INT); if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "NF did not complete in calibration window\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "NF did not complete in calibration window\n"); nf = 0; chan->rawNoiseFloor = nf; return chan->rawNoiseFloor; @@ -682,10 +692,10 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, nf = nfarray[0]; if (getNoiseFloorThresh(ah, c->band, &nfThresh) && nf > nfThresh) { - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "noise floor failed detected; " - "detected %d, threshold %d\n", - nf, nfThresh); + ath_print(common, ATH_DBG_CALIBRATE, + "noise floor failed detected; " + "detected %d, threshold %d\n", + nf, nfThresh); chan->channelFlags |= CHANNEL_CW_INT; } } @@ -737,51 +747,73 @@ s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) return nf; } +EXPORT_SYMBOL(ath9k_hw_getchan_noise); -static void ath9k_olc_temp_compensation(struct ath_hw *ah) +static void ath9k_olc_temp_compensation_9287(struct ath_hw *ah) { - u32 rddata, i; - int delta, currPDADC, regval, slope; + u32 rddata; + int32_t delta, currPDADC, slope; rddata = REG_READ(ah, AR_PHY_TX_PWRCTRL4); currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); + if (ah->initPDADC == 0 || currPDADC == 0) { + /* + * Zero value indicates that no frames have been transmitted yet, + * can't do temperature compensation until frames are transmitted. + */ + return; + } else { + slope = ah->eep_ops->get_eeprom(ah, EEP_TEMPSENSE_SLOPE); + + if (slope == 0) { /* to avoid divide by zero case */ + delta = 0; + } else { + delta = ((currPDADC - ah->initPDADC)*4) / slope; + } + REG_RMW_FIELD(ah, AR_PHY_CH0_TX_PWRCTRL11, + AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta); + REG_RMW_FIELD(ah, AR_PHY_CH1_TX_PWRCTRL11, + AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta); + } +} + +static void ath9k_olc_temp_compensation(struct ath_hw *ah) +{ + u32 rddata, i; + int delta, currPDADC, regval; if (OLC_FOR_AR9287_10_LATER) { + ath9k_olc_temp_compensation_9287(ah); + } else { + rddata = REG_READ(ah, AR_PHY_TX_PWRCTRL4); + currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); + if (ah->initPDADC == 0 || currPDADC == 0) { return; } else { - slope = ah->eep_ops->get_eeprom(ah, EEP_TEMPSENSE_SLOPE); - if (slope == 0) - delta = 0; + if (ah->eep_ops->get_eeprom(ah, EEP_DAC_HPWR_5G)) + delta = (currPDADC - ah->initPDADC + 4) / 8; else - delta = ((currPDADC - ah->initPDADC)*4) / slope; - REG_RMW_FIELD(ah, AR_PHY_CH0_TX_PWRCTRL11, - AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta); - REG_RMW_FIELD(ah, AR_PHY_CH1_TX_PWRCTRL11, - AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta); - } - } else { - if (ah->eep_ops->get_eeprom(ah, EEP_DAC_HPWR_5G)) - delta = (currPDADC - ah->initPDADC + 4) / 8; - else - delta = (currPDADC - ah->initPDADC + 5) / 10; - - if (delta != ah->PDADCdelta) { - ah->PDADCdelta = delta; - for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { - regval = ah->originalGain[i] - delta; - if (regval < 0) - regval = 0; - - REG_RMW_FIELD(ah, AR_PHY_TX_GAIN_TBL1 + i * 4, - AR_PHY_TX_GAIN, regval); + delta = (currPDADC - ah->initPDADC + 5) / 10; + + if (delta != ah->PDADCdelta) { + ah->PDADCdelta = delta; + for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { + regval = ah->originalGain[i] - delta; + if (regval < 0) + regval = 0; + + REG_RMW_FIELD(ah, + AR_PHY_TX_GAIN_TBL1 + i * 4, + AR_PHY_TX_GAIN, regval); + } } } } } -static void ath9k_hw_9271_pa_cal(struct ath_hw *ah) +static void ath9k_hw_9271_pa_cal(struct ath_hw *ah, bool is_reset) { u32 regVal; unsigned int i; @@ -845,7 +877,7 @@ static void ath9k_hw_9271_pa_cal(struct ath_hw *ah) REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0); /* find off_6_1; */ - for (i = 6; i >= 0; i--) { + for (i = 6; i > 0; i--) { regVal = REG_READ(ah, 0x7834); regVal |= (1 << (20 + i)); REG_WRITE(ah, 0x7834, regVal); @@ -857,10 +889,19 @@ static void ath9k_hw_9271_pa_cal(struct ath_hw *ah) REG_WRITE(ah, 0x7834, regVal); } - /* Empirical offset correction */ -#if 0 - REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0x20); -#endif + regVal = (regVal >>20) & 0x7f; + + /* Update PA cal info */ + if ((!is_reset) && (ah->pacal_info.prev_offset == regVal)) { + if (ah->pacal_info.max_skipcount < MAX_PACAL_SKIPCOUNT) + ah->pacal_info.max_skipcount = + 2 * ah->pacal_info.max_skipcount; + ah->pacal_info.skipcount = ah->pacal_info.max_skipcount; + } else { + ah->pacal_info.max_skipcount = 1; + ah->pacal_info.skipcount = 0; + ah->pacal_info.prev_offset = regVal; + } regVal = REG_READ(ah, 0x7834); regVal |= 0x1; @@ -875,7 +916,7 @@ static void ath9k_hw_9271_pa_cal(struct ath_hw *ah) static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah, bool is_reset) { - + struct ath_common *common = ath9k_hw_common(ah); u32 regVal; int i, offset, offs_6_1, offs_0; u32 ccomp_org, reg_field; @@ -889,7 +930,7 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah, bool is_reset) { 0x7838, 0 }, }; - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "Running PA Calibration\n"); + ath_print(common, ATH_DBG_CALIBRATE, "Running PA Calibration\n"); /* PA CAL is not needed for high power solution */ if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == @@ -1011,7 +1052,7 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, if (longcal) { /* Do periodic PAOffset Cal */ if (AR_SREV_9271(ah)) - ath9k_hw_9271_pa_cal(ah); + ath9k_hw_9271_pa_cal(ah, false); else if (AR_SREV_9285_11_OR_LATER(ah)) { if (!ah->pacal_info.skipcount) ath9k_hw_9285_pa_cal(ah, false); @@ -1036,9 +1077,13 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, return iscaldone; } +EXPORT_SYMBOL(ath9k_hw_calibrate); +/* Carrier leakage Calibration fix */ static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan) { + struct ath_common *common = ath9k_hw_common(ah); + REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); if (IS_CHAN_HT20(chan)) { REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); @@ -1049,9 +1094,9 @@ static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan) REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "offset " - "calibration failed to complete in " - "1ms; noisy ??\n"); + ath_print(common, ATH_DBG_CALIBRATE, "offset " + "calibration failed to complete in " + "1ms; noisy ??\n"); return false; } REG_CLR_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); @@ -1064,8 +1109,8 @@ static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan) REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "offset calibration " - "failed to complete in 1ms; noisy ??\n"); + ath_print(common, ATH_DBG_CALIBRATE, "offset calibration " + "failed to complete in 1ms; noisy ??\n"); return false; } @@ -1078,7 +1123,9 @@ static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan) bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { - if (AR_SREV_9285_12_OR_LATER(ah)) { + struct ath_common *common = ath9k_hw_common(ah); + + if (AR_SREV_9271(ah) || AR_SREV_9285_12_OR_LATER(ah)) { if (!ar9285_clc(ah, chan)) return false; } else { @@ -1098,9 +1145,9 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) /* Poll for offset calibration complete */ if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "offset calibration failed to complete in 1ms; " - "noisy environment?\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "offset calibration failed to " + "complete in 1ms; noisy environment?\n"); return false; } @@ -1114,7 +1161,9 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) } /* Do PA Calibration */ - if (AR_SREV_9285_11_OR_LATER(ah)) + if (AR_SREV_9271(ah)) + ath9k_hw_9271_pa_cal(ah, true); + else if (AR_SREV_9285_11_OR_LATER(ah)) ath9k_hw_9285_pa_cal(ah, true); /* Do NF Calibration after DC offset and other calibrations */ @@ -1128,20 +1177,20 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) if (ath9k_hw_iscal_supported(ah, ADC_GAIN_CAL)) { INIT_CAL(&ah->adcgain_caldata); INSERT_CAL(ah, &ah->adcgain_caldata); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "enabling ADC Gain Calibration.\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "enabling ADC Gain Calibration.\n"); } if (ath9k_hw_iscal_supported(ah, ADC_DC_CAL)) { INIT_CAL(&ah->adcdc_caldata); INSERT_CAL(ah, &ah->adcdc_caldata); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "enabling ADC DC Calibration.\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "enabling ADC DC Calibration.\n"); } if (ath9k_hw_iscal_supported(ah, IQ_MISMATCH_CAL)) { INIT_CAL(&ah->iq_caldata); INSERT_CAL(ah, &ah->iq_caldata); - DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, - "enabling IQ Calibration.\n"); + ath_print(common, ATH_DBG_CALIBRATE, + "enabling IQ Calibration.\n"); } ah->cal_list_curr = ah->cal_list; diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 9028ab193e42..b2c873e97485 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -17,6 +17,8 @@ #ifndef CALIB_H #define CALIB_H +#include "hw.h" + extern const struct ath9k_percal_data iq_cal_multi_sample; extern const struct ath9k_percal_data iq_cal_single_sample; extern const struct ath9k_percal_data adc_gain_cal_multi_sample; diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c new file mode 100644 index 000000000000..4d775ae141db --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Module for common driver code between ath9k and ath9k_htc + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "common.h" + +MODULE_AUTHOR("Atheros Communications"); +MODULE_DESCRIPTION("Shared library for Atheros wireless 802.11n LAN cards."); +MODULE_LICENSE("Dual BSD/GPL"); + +/* Common RX processing */ + +/* Assumes you've already done the endian to CPU conversion */ +static bool ath9k_rx_accept(struct ath_common *common, + struct sk_buff *skb, + struct ieee80211_rx_status *rxs, + struct ath_rx_status *rx_stats, + bool *decrypt_error) +{ + struct ath_hw *ah = common->ah; + struct ieee80211_hdr *hdr; + __le16 fc; + + hdr = (struct ieee80211_hdr *) skb->data; + fc = hdr->frame_control; + + if (!rx_stats->rs_datalen) + return false; + /* + * rs_status follows rs_datalen so if rs_datalen is too large + * we can take a hint that hardware corrupted it, so ignore + * those frames. + */ + if (rx_stats->rs_datalen > common->rx_bufsize) + return false; + + /* + * rs_more indicates chained descriptors which can be used + * to link buffers together for a sort of scatter-gather + * operation. + * + * The rx_stats->rs_status will not be set until the end of the + * chained descriptors so it can be ignored if rs_more is set. The + * rs_more will be false at the last element of the chained + * descriptors. + */ + if (!rx_stats->rs_more && rx_stats->rs_status != 0) { + if (rx_stats->rs_status & ATH9K_RXERR_CRC) + rxs->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rx_stats->rs_status & ATH9K_RXERR_PHY) + return false; + + if (rx_stats->rs_status & ATH9K_RXERR_DECRYPT) { + *decrypt_error = true; + } else if (rx_stats->rs_status & ATH9K_RXERR_MIC) { + if (ieee80211_is_ctl(fc)) + /* + * Sometimes, we get invalid + * MIC failures on valid control frames. + * Remove these mic errors. + */ + rx_stats->rs_status &= ~ATH9K_RXERR_MIC; + else + rxs->flag |= RX_FLAG_MMIC_ERROR; + } + /* + * Reject error frames with the exception of + * decryption and MIC failures. For monitor mode, + * we also ignore the CRC error. + */ + if (ah->opmode == NL80211_IFTYPE_MONITOR) { + if (rx_stats->rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | + ATH9K_RXERR_CRC)) + return false; + } else { + if (rx_stats->rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { + return false; + } + } + } + return true; +} + +static u8 ath9k_process_rate(struct ath_common *common, + struct ieee80211_hw *hw, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs, + struct sk_buff *skb) +{ + struct ieee80211_supported_band *sband; + enum ieee80211_band band; + unsigned int i = 0; + + band = hw->conf.channel->band; + sband = hw->wiphy->bands[band]; + + if (rx_stats->rs_rate & 0x80) { + /* HT rate */ + rxs->flag |= RX_FLAG_HT; + if (rx_stats->rs_flags & ATH9K_RX_2040) + rxs->flag |= RX_FLAG_40MHZ; + if (rx_stats->rs_flags & ATH9K_RX_GI) + rxs->flag |= RX_FLAG_SHORT_GI; + return rx_stats->rs_rate & 0x7f; + } + + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].hw_value == rx_stats->rs_rate) + return i; + if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) { + rxs->flag |= RX_FLAG_SHORTPRE; + return i; + } + } + + /* No valid hardware bitrate found -- we should not get here */ + ath_print(common, ATH_DBG_XMIT, "unsupported hw bitrate detected " + "0x%02x using 1 Mbit\n", rx_stats->rs_rate); + if ((common->debug_mask & ATH_DBG_XMIT)) + print_hex_dump_bytes("", DUMP_PREFIX_NONE, skb->data, skb->len); + + return 0; +} + +static void ath9k_process_rssi(struct ath_common *common, + struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ath_rx_status *rx_stats) +{ + struct ath_hw *ah = common->ah; + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + struct ath_node *an; + int last_rssi = ATH_RSSI_DUMMY_MARKER; + __le16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + + rcu_read_lock(); + /* + * XXX: use ieee80211_find_sta! This requires quite a bit of work + * under the current ath9k virtual wiphy implementation as we have + * no way of tying a vif to wiphy. Typically vifs are attached to + * at least one sdata of a wiphy on mac80211 but with ath9k virtual + * wiphy you'd have to iterate over every wiphy and each sdata. + */ + sta = ieee80211_find_sta_by_hw(hw, hdr->addr2); + if (sta) { + an = (struct ath_node *) sta->drv_priv; + if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && + !rx_stats->rs_moreaggr) + ATH_RSSI_LPF(an->last_rssi, rx_stats->rs_rssi); + last_rssi = an->last_rssi; + } + rcu_read_unlock(); + + if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) + rx_stats->rs_rssi = ATH_EP_RND(last_rssi, + ATH_RSSI_EP_MULTIPLIER); + if (rx_stats->rs_rssi < 0) + rx_stats->rs_rssi = 0; + + /* Update Beacon RSSI, this is used by ANI. */ + if (ieee80211_is_beacon(fc)) + ah->stats.avgbrssi = rx_stats->rs_rssi; +} + +/* + * For Decrypt or Demic errors, we only mark packet status here and always push + * up the frame up to let mac80211 handle the actual error case, be it no + * decryption key or real decryption error. This let us keep statistics there. + */ +int ath9k_cmn_rx_skb_preprocess(struct ath_common *common, + struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rx_status, + bool *decrypt_error) +{ + struct ath_hw *ah = common->ah; + + memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); + if (!ath9k_rx_accept(common, skb, rx_status, rx_stats, decrypt_error)) + return -EINVAL; + + ath9k_process_rssi(common, hw, skb, rx_stats); + + rx_status->rate_idx = ath9k_process_rate(common, hw, + rx_stats, rx_status, skb); + rx_status->mactime = ath9k_hw_extend_tsf(ah, rx_stats->rs_tstamp); + rx_status->band = hw->conf.channel->band; + rx_status->freq = hw->conf.channel->center_freq; + rx_status->noise = common->ani.noise_floor; + rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + rx_stats->rs_rssi; + rx_status->antenna = rx_stats->rs_antenna; + rx_status->flag |= RX_FLAG_TSFT; + + return 0; +} +EXPORT_SYMBOL(ath9k_cmn_rx_skb_preprocess); + +void ath9k_cmn_rx_skb_postprocess(struct ath_common *common, + struct sk_buff *skb, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs, + bool decrypt_error) +{ + struct ath_hw *ah = common->ah; + struct ieee80211_hdr *hdr; + int hdrlen, padpos, padsize; + u8 keyix; + __le16 fc; + + /* see if any padding is done by the hw and remove it */ + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + fc = hdr->frame_control; + padpos = ath9k_cmn_padpos(hdr->frame_control); + + /* The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. The general calculation for + * padsize would take into account odd header lengths: + * padsize = (4 - padpos % 4) % 4; However, since only + * even-length headers are used, padding can only be 0 or 2 + * bytes and we can optimize this a bit. In addition, we must + * not try to remove padding from short control frames that do + * not have payload. */ + padsize = padpos & 3; + if (padsize && skb->len>=padpos+padsize+FCS_LEN) { + memmove(skb->data + padsize, skb->data, padpos); + skb_pull(skb, padsize); + } + + keyix = rx_stats->rs_keyix; + + if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) { + rxs->flag |= RX_FLAG_DECRYPTED; + } else if (ieee80211_has_protected(fc) + && !decrypt_error && skb->len >= hdrlen + 4) { + keyix = skb->data[hdrlen + 3] >> 6; + + if (test_bit(keyix, common->keymap)) + rxs->flag |= RX_FLAG_DECRYPTED; + } + if (ah->sw_mgmt_crypto && + (rxs->flag & RX_FLAG_DECRYPTED) && + ieee80211_is_mgmt(fc)) + /* Use software decrypt for management frames. */ + rxs->flag &= ~RX_FLAG_DECRYPTED; +} +EXPORT_SYMBOL(ath9k_cmn_rx_skb_postprocess); + +int ath9k_cmn_padpos(__le16 frame_control) +{ + int padpos = 24; + if (ieee80211_has_a4(frame_control)) { + padpos += ETH_ALEN; + } + if (ieee80211_is_data_qos(frame_control)) { + padpos += IEEE80211_QOS_CTL_LEN; + } + + return padpos; +} +EXPORT_SYMBOL(ath9k_cmn_padpos); + +static int __init ath9k_cmn_init(void) +{ + return 0; +} +module_init(ath9k_cmn_init); + +static void __exit ath9k_cmn_exit(void) +{ + return; +} +module_exit(ath9k_cmn_exit); diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h new file mode 100644 index 000000000000..042999c2fe9c --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <net/mac80211.h> + +#include "../ath.h" +#include "../debug.h" + +#include "hw.h" + +/* Common header for Atheros 802.11n base driver cores */ + +#define WME_NUM_TID 16 +#define WME_BA_BMP_SIZE 64 +#define WME_MAX_BA WME_BA_BMP_SIZE +#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) + +#define WME_AC_BE 0 +#define WME_AC_BK 1 +#define WME_AC_VI 2 +#define WME_AC_VO 3 +#define WME_NUM_AC 4 + +#define ATH_RSSI_DUMMY_MARKER 0x127 +#define ATH_RSSI_LPF_LEN 10 +#define RSSI_LPF_THRESHOLD -20 +#define ATH_RSSI_EP_MULTIPLIER (1<<7) +#define ATH_EP_MUL(x, mul) ((x) * (mul)) +#define ATH_RSSI_IN(x) (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER)) +#define ATH_LPF_RSSI(x, y, len) \ + ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) +#define ATH_RSSI_LPF(x, y) do { \ + if ((y) >= RSSI_LPF_THRESHOLD) \ + x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ +} while (0) +#define ATH_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) + +struct ath_atx_ac { + int sched; + int qnum; + struct list_head list; + struct list_head tid_q; +}; + +struct ath_buf_state { + int bfs_nframes; + u16 bfs_al; + u16 bfs_frmlen; + int bfs_seqno; + int bfs_tidno; + int bfs_retries; + u8 bf_type; + u32 bfs_keyix; + enum ath9k_key_type bfs_keytype; +}; + +struct ath_buf { + struct list_head list; + struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or + an aggregate) */ + struct ath_buf *bf_next; /* next subframe in the aggregate */ + struct sk_buff *bf_mpdu; /* enclosing frame structure */ + struct ath_desc *bf_desc; /* virtual addr of desc */ + dma_addr_t bf_daddr; /* physical addr of desc */ + dma_addr_t bf_buf_addr; /* physical addr of data buffer */ + bool bf_stale; + bool bf_isnullfunc; + u16 bf_flags; + struct ath_buf_state bf_state; + dma_addr_t bf_dmacontext; + struct ath_wiphy *aphy; +}; + +struct ath_atx_tid { + struct list_head list; + struct list_head buf_q; + struct ath_node *an; + struct ath_atx_ac *ac; + struct ath_buf *tx_buf[ATH_TID_MAX_BUFS]; + u16 seq_start; + u16 seq_next; + u16 baw_size; + int tidno; + int baw_head; /* first un-acked tx buffer */ + int baw_tail; /* next unused tx buffer slot */ + int sched; + int paused; + u8 state; +}; + +struct ath_node { + struct ath_common *common; + struct ath_atx_tid tid[WME_NUM_TID]; + struct ath_atx_ac ac[WME_NUM_AC]; + u16 maxampdu; + u8 mpdudensity; + int last_rssi; +}; + +int ath9k_cmn_rx_skb_preprocess(struct ath_common *common, + struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rx_status, + bool *decrypt_error); + +void ath9k_cmn_rx_skb_postprocess(struct ath_common *common, + struct sk_buff *skb, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs, + bool decrypt_error); + +int ath9k_cmn_padpos(__le16 frame_control); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 2be4c2252047..b66f72dbf7b9 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -18,40 +18,30 @@ #include "ath9k.h" -static unsigned int ath9k_debug = DBG_DEFAULT; -module_param_named(debug, ath9k_debug, uint, 0); +#define REG_WRITE_D(_ah, _reg, _val) \ + ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg)) +#define REG_READ_D(_ah, _reg) \ + ath9k_hw_common(_ah)->ops->read((_ah), (_reg)) static struct dentry *ath9k_debugfs_root; -void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...) -{ - if (!sc) - return; - - if (sc->debug.debug_mask & dbg_mask) { - va_list args; - - va_start(args, fmt); - printk(KERN_DEBUG "ath9k: "); - vprintk(fmt, args); - va_end(args); - } -} - static int ath9k_debugfs_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return 0; } +#ifdef CONFIG_ATH_DEBUG + static ssize_t read_file_debug(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); char buf[32]; unsigned int len; - len = snprintf(buf, sizeof(buf), "0x%08x\n", sc->debug.debug_mask); + len = snprintf(buf, sizeof(buf), "0x%08x\n", common->debug_mask); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } @@ -59,6 +49,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long mask; char buf[32]; ssize_t len; @@ -71,7 +62,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf, if (strict_strtoul(buf, 0, &mask)) return -EINVAL; - sc->debug.debug_mask = mask; + common->debug_mask = mask; return count; } @@ -82,6 +73,8 @@ static const struct file_operations fops_debug = { .owner = THIS_MODULE }; +#endif + static ssize_t read_file_dma(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -95,7 +88,7 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, ath9k_ps_wakeup(sc); - REG_WRITE(ah, AR_MACMISC, + REG_WRITE_D(ah, AR_MACMISC, ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); @@ -107,7 +100,7 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, if (i % 4 == 0) len += snprintf(buf + len, sizeof(buf) - len, "\n"); - val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32))); + val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32))); len += snprintf(buf + len, sizeof(buf) - len, "%d: %08x ", i, val[i]); } @@ -157,9 +150,9 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); len += snprintf(buf + len, sizeof(buf) - len, "pcu observe: 0x%x \n", - REG_READ(ah, AR_OBS_BUS_1)); + REG_READ_D(ah, AR_OBS_BUS_1)); len += snprintf(buf + len, sizeof(buf) - len, - "AR_CR: 0x%x \n", REG_READ(ah, AR_CR)); + "AR_CR: 0x%x \n", REG_READ_D(ah, AR_CR)); ath9k_ps_restore(sc); @@ -266,18 +259,11 @@ static const struct file_operations fops_interrupt = { .owner = THIS_MODULE }; -void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb) +void ath_debug_stat_rc(struct ath_softc *sc, int final_rate) { - struct ath_tx_info_priv *tx_info_priv = NULL; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_tx_rate *rates = tx_info->status.rates; - int final_ts_idx, idx; struct ath_rc_stats *stats; - tx_info_priv = ATH_TX_INFO_PRIV(tx_info); - final_ts_idx = tx_info_priv->tx.ts_rateindex; - idx = rates[final_ts_idx].idx; - stats = &sc->debug.stats.rcstats[idx]; + stats = &sc->debug.stats.rcstats[final_rate]; stats->success++; } @@ -376,12 +362,12 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, aphy->chan_idx, aphy->chan_is_ht); } - put_unaligned_le32(REG_READ(sc->sc_ah, AR_STA_ID0), addr); - put_unaligned_le16(REG_READ(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); + put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr); + put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); len += snprintf(buf + len, sizeof(buf) - len, "addr: %pM\n", addr); - put_unaligned_le32(REG_READ(sc->sc_ah, AR_BSSMSKL), addr); - put_unaligned_le16(REG_READ(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4); + put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr); + put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4); len += snprintf(buf + len, sizeof(buf) - len, "addrmask: %pM\n", addr); @@ -568,9 +554,10 @@ static const struct file_operations fops_xmit = { .owner = THIS_MODULE }; -int ath9k_init_debug(struct ath_softc *sc) +int ath9k_init_debug(struct ath_hw *ah) { - sc->debug.debug_mask = ath9k_debug; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; if (!ath9k_debugfs_root) return -ENOENT; @@ -580,10 +567,12 @@ int ath9k_init_debug(struct ath_softc *sc) if (!sc->debug.debugfs_phy) goto err; +#ifdef CONFIG_ATH_DEBUG sc->debug.debugfs_debug = debugfs_create_file("debug", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_debug); if (!sc->debug.debugfs_debug) goto err; +#endif sc->debug.debugfs_dma = debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dma); @@ -619,12 +608,15 @@ int ath9k_init_debug(struct ath_softc *sc) return 0; err: - ath9k_exit_debug(sc); + ath9k_exit_debug(ah); return -ENOMEM; } -void ath9k_exit_debug(struct ath_softc *sc) +void ath9k_exit_debug(struct ath_hw *ah) { + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + debugfs_remove(sc->debug.debugfs_xmit); debugfs_remove(sc->debug.debugfs_wiphy); debugfs_remove(sc->debug.debugfs_rcstat); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 7241f4748338..536663e3ee11 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -17,36 +17,19 @@ #ifndef DEBUG_H #define DEBUG_H -enum ATH_DEBUG { - ATH_DBG_RESET = 0x00000001, - ATH_DBG_QUEUE = 0x00000002, - ATH_DBG_EEPROM = 0x00000004, - ATH_DBG_CALIBRATE = 0x00000008, - ATH_DBG_INTERRUPT = 0x00000010, - ATH_DBG_REGULATORY = 0x00000020, - ATH_DBG_ANI = 0x00000040, - ATH_DBG_XMIT = 0x00000080, - ATH_DBG_BEACON = 0x00000100, - ATH_DBG_CONFIG = 0x00000200, - ATH_DBG_FATAL = 0x00000400, - ATH_DBG_PS = 0x00000800, - ATH_DBG_HWTIMER = 0x00001000, - ATH_DBG_BTCOEX = 0x00002000, - ATH_DBG_ANY = 0xffffffff -}; - -#define DBG_DEFAULT (ATH_DBG_FATAL) +#include "hw.h" +#include "rc.h" struct ath_txq; struct ath_buf; -#ifdef CONFIG_ATH9K_DEBUG +#ifdef CONFIG_ATH9K_DEBUGFS #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++ #else #define TX_STAT_INC(q, c) do { } while (0) #endif -#ifdef CONFIG_ATH9K_DEBUG +#ifdef CONFIG_ATH9K_DEBUGFS /** * struct ath_interrupt_stats - Contains statistics about interrupts @@ -140,7 +123,6 @@ struct ath_stats { }; struct ath9k_debug { - int debug_mask; struct dentry *debugfs_phy; struct dentry *debugfs_debug; struct dentry *debugfs_dma; @@ -151,13 +133,13 @@ struct ath9k_debug { struct ath_stats stats; }; -void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...); -int ath9k_init_debug(struct ath_softc *sc); -void ath9k_exit_debug(struct ath_softc *sc); +int ath9k_init_debug(struct ath_hw *ah); +void ath9k_exit_debug(struct ath_hw *ah); + int ath9k_debug_create_root(void); void ath9k_debug_remove_root(void); void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); -void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb); +void ath_debug_stat_rc(struct ath_softc *sc, int final_rate); void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf); void ath_debug_stat_retries(struct ath_softc *sc, int rix, @@ -165,17 +147,12 @@ void ath_debug_stat_retries(struct ath_softc *sc, int rix, #else -static inline void DPRINTF(struct ath_softc *sc, int dbg_mask, - const char *fmt, ...) -{ -} - -static inline int ath9k_init_debug(struct ath_softc *sc) +static inline int ath9k_init_debug(struct ath_hw *ah) { return 0; } -static inline void ath9k_exit_debug(struct ath_softc *sc) +static inline void ath9k_exit_debug(struct ath_hw *ah) { } @@ -194,7 +171,7 @@ static inline void ath_debug_stat_interrupt(struct ath_softc *sc, } static inline void ath_debug_stat_rc(struct ath_softc *sc, - struct sk_buff *skb) + int final_rate) { } @@ -209,6 +186,6 @@ static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix, { } -#endif /* CONFIG_ATH9K_DEBUG */ +#endif /* CONFIG_ATH9K_DEBUGFS */ #endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index b6e52d0f8c48..dacaae934148 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +#include "hw.h" static inline u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz) { @@ -83,11 +83,9 @@ bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, return false; } -bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) +bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data) { - struct ath_softc *sc = ah->ah_sc; - - return sc->bus_ops->eeprom_read(ah, off, data); + return common->bus_ops->eeprom_read(common, off, data); } void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 4fe33f7eee9d..2f2993b50e2f 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -17,6 +17,7 @@ #ifndef EEPROM_H #define EEPROM_H +#include "../ath.h" #include <net/cfg80211.h> #define AH_USE_EEPROM 0x1 @@ -133,6 +134,7 @@ #define AR5416_EEP_MINOR_VER_17 0x11 #define AR5416_EEP_MINOR_VER_19 0x13 #define AR5416_EEP_MINOR_VER_20 0x14 +#define AR5416_EEP_MINOR_VER_21 0x15 #define AR5416_EEP_MINOR_VER_22 0x16 #define AR5416_NUM_5G_CAL_PIERS 8 @@ -153,7 +155,7 @@ #define AR5416_BCHAN_UNUSED 0xFF #define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64 #define AR5416_MAX_CHAINS 3 -#define AR5416_PWR_TABLE_OFFSET -5 +#define AR5416_PWR_TABLE_OFFSET_DB -5 /* Rx gain type values */ #define AR5416_EEP_RXGAIN_23DB_BACKOFF 0 @@ -301,7 +303,7 @@ struct base_eep_header { u8 txGainType; u8 rcChainMask; u8 desiredScaleCCK; - u8 power_table_offset; + u8 pwr_table_offset; u8 frac_n_5g; u8 futureBase_3[21]; } __packed; @@ -638,6 +640,7 @@ struct ar9287_eeprom { } __packed; enum reg_ext_bitmap { + REG_EXT_FCC_MIDBAND = 0, REG_EXT_JAPAN_MIDBAND = 1, REG_EXT_FCC_DFS_HT40 = 2, REG_EXT_JAPAN_NONDFS_HT40 = 3, @@ -684,7 +687,7 @@ int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, int16_t targetRight); bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, u16 *indexL, u16 *indexR); -bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data); +bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data); void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, u8 *pVpdList, u16 numIntercepts, u8 *pRetVpdList); diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index b8eca7be5f3a..68db16690abf 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +#include "hw.h" static int ath9k_hw_4k_get_eeprom_ver(struct ath_hw *ah) { @@ -29,20 +29,21 @@ static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah) static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) { #define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) + struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data = (u16 *)&ah->eeprom.map4k; int addr, eep_start_loc = 0; eep_start_loc = 64; if (!ath9k_hw_use_flash(ah)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Reading from EEPROM, not flash\n"); + ath_print(common, ATH_DBG_EEPROM, + "Reading from EEPROM, not flash\n"); } for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { - if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Unable to read eeprom region \n"); + if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, eep_data)) { + ath_print(common, ATH_DBG_EEPROM, + "Unable to read eeprom region \n"); return false; } eep_data++; @@ -55,6 +56,7 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) { #define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) + struct ath_common *common = ath9k_hw_common(ah); struct ar5416_eeprom_4k *eep = (struct ar5416_eeprom_4k *) &ah->eeprom.map4k; u16 *eepdata, temp, magic, magic2; @@ -64,15 +66,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, + if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Reading Magic # failed\n"); + ath_print(common, ATH_DBG_FATAL, + "Reading Magic # failed\n"); return false; } - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Read Magic = 0x%04X\n", magic); + ath_print(common, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); @@ -87,16 +89,16 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) eepdata++; } } else { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Invalid EEPROM Magic. " - "endianness mismatch.\n"); + ath_print(common, ATH_DBG_FATAL, + "Invalid EEPROM Magic. " + "endianness mismatch.\n"); return -EINVAL; } } } - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", - need_swap ? "True" : "False"); + ath_print(common, ATH_DBG_EEPROM, "need_swap = %s.\n", + need_swap ? "True" : "False"); if (need_swap) el = swab16(ah->eeprom.map4k.baseEepHeader.length); @@ -117,8 +119,8 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) u32 integer; u16 word; - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "EEPROM Endianness is not native.. Changing\n"); + ath_print(common, ATH_DBG_EEPROM, + "EEPROM Endianness is not native.. Changing\n"); word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; @@ -160,9 +162,9 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); + ath_print(common, ATH_DBG_FATAL, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ah->eep_ops->get_eeprom_ver(ah)); return -EINVAL; } @@ -208,6 +210,8 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, return pBase->rxMask; case EEP_FRAC_N_5G: return 0; + case EEP_PWR_TABLE_OFFSET: + return AR5416_PWR_TABLE_OFFSET_DB; default: return 0; } @@ -385,6 +389,7 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, struct ath9k_channel *chan, int16_t *pTxPowerIndexOffset) { + struct ath_common *common = ath9k_hw_common(ah); struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; struct cal_data_per_freq_4k *pRawDataset; u8 *pCalBChans = NULL; @@ -470,21 +475,21 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, ((pdadcValues[4 * j + 3] & 0xFF) << 24); REG_WRITE(ah, regOffset, reg32); - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "PDADC (%d,%4x): %4.4x %8.8x\n", - i, regChainOffset, regOffset, - reg32); - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "PDADC: Chain %d | " - "PDADC %3d Value %3d | " - "PDADC %3d Value %3d | " - "PDADC %3d Value %3d | " - "PDADC %3d Value %3d |\n", - i, 4 * j, pdadcValues[4 * j], - 4 * j + 1, pdadcValues[4 * j + 1], - 4 * j + 2, pdadcValues[4 * j + 2], - 4 * j + 3, - pdadcValues[4 * j + 3]); + ath_print(common, ATH_DBG_EEPROM, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + ath_print(common, ATH_DBG_EEPROM, + "PDADC: Chain %d | " + "PDADC %3d Value %3d | " + "PDADC %3d Value %3d | " + "PDADC %3d Value %3d | " + "PDADC %3d Value %3d |\n", + i, 4 * j, pdadcValues[4 * j], + 4 * j + 1, pdadcValues[4 * j + 1], + 4 * j + 2, pdadcValues[4 * j + 2], + 4 * j + 3, + pdadcValues[4 * j + 3]); regOffset += 4; } @@ -750,7 +755,7 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, if (AR_SREV_9280_10_OR_LATER(ah)) { for (i = 0; i < Ar5416RateSize; i++) - ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2; + ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; } /* OFDM power per rate */ @@ -1107,6 +1112,10 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); + + if (AR_SREV_9271_10(ah)) + REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, + pModal->txEndToRxOn); REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, @@ -1148,20 +1157,21 @@ static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { #define EEP_MAP4K_SPURCHAN \ (ah->eeprom.map4k.modalHeader.spurChans[i].spurChan) + struct ath_common *common = ath9k_hw_common(ah); u16 spur_val = AR_NO_SPUR; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Getting spur idx %d is2Ghz. %d val %x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); + ath_print(common, ATH_DBG_ANI, + "Getting spur idx %d is2Ghz. %d val %x\n", + i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { case SPUR_DISABLE: break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Getting spur val from new loc. %d\n", spur_val); + ath_print(common, ATH_DBG_ANI, + "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_MAP4K_SPURCHAN; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index c20c21a79b21..839d05a1df29 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +#include "hw.h" static int ath9k_hw_AR9287_get_eeprom_ver(struct ath_hw *ah) { @@ -29,20 +29,22 @@ static int ath9k_hw_AR9287_get_eeprom_rev(struct ath_hw *ah) static bool ath9k_hw_AR9287_fill_eeprom(struct ath_hw *ah) { struct ar9287_eeprom *eep = &ah->eeprom.map9287; + struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data; int addr, eep_start_loc = AR9287_EEP_START_LOC; eep_data = (u16 *)eep; if (!ath9k_hw_use_flash(ah)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Reading from EEPROM, not flash\n"); + ath_print(common, ATH_DBG_EEPROM, + "Reading from EEPROM, not flash\n"); } for (addr = 0; addr < sizeof(struct ar9287_eeprom) / sizeof(u16); addr++) { - if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Unable to read eeprom region \n"); + if (!ath9k_hw_nvram_read(common, + addr + eep_start_loc, eep_data)) { + ath_print(common, ATH_DBG_EEPROM, + "Unable to read eeprom region \n"); return false; } eep_data++; @@ -57,17 +59,18 @@ static int ath9k_hw_AR9287_check_eeprom(struct ath_hw *ah) int i, addr; bool need_swap = false; struct ar9287_eeprom *eep = &ah->eeprom.map9287; + struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read - (ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Reading Magic # failed\n"); + if (!ath9k_hw_nvram_read(common, + AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + ath_print(common, ATH_DBG_FATAL, + "Reading Magic # failed\n"); return false; } - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Read Magic = 0x%04X\n", magic); + ath_print(common, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); @@ -83,15 +86,15 @@ static int ath9k_hw_AR9287_check_eeprom(struct ath_hw *ah) eepdata++; } } else { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Invalid EEPROM Magic. " - "endianness mismatch.\n"); + ath_print(common, ATH_DBG_FATAL, + "Invalid EEPROM Magic. " + "endianness mismatch.\n"); return -EINVAL; } } } - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", need_swap ? - "True" : "False"); + ath_print(common, ATH_DBG_EEPROM, "need_swap = %s.\n", need_swap ? + "True" : "False"); if (need_swap) el = swab16(ah->eeprom.map9287.baseEepHeader.length); @@ -148,9 +151,9 @@ static int ath9k_hw_AR9287_check_eeprom(struct ath_hw *ah) if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR9287_EEP_VER || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); + ath_print(common, ATH_DBG_FATAL, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ah->eep_ops->get_eeprom_ver(ah)); return -EINVAL; } @@ -436,6 +439,7 @@ static void ath9k_hw_set_AR9287_power_cal_table(struct ath_hw *ah, struct ath9k_channel *chan, int16_t *pTxPowerIndexOffset) { + struct ath_common *common = ath9k_hw_common(ah); struct cal_data_per_freq_ar9287 *pRawDataset; struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop; u8 *pCalBChans = NULL; @@ -564,24 +568,25 @@ static void ath9k_hw_set_AR9287_power_cal_table(struct ath_hw *ah, & 0xFF) << 24) ; REG_WRITE(ah, regOffset, reg32); - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "PDADC (%d,%4x): %4.4x %8.8x\n", - i, regChainOffset, regOffset, - reg32); - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "PDADC: Chain %d | " - "PDADC %3d Value %3d | " - "PDADC %3d Value %3d | " - "PDADC %3d Value %3d | " - "PDADC %3d Value %3d |\n", - i, 4 * j, pdadcValues[4 * j], - 4 * j + 1, - pdadcValues[4 * j + 1], - 4 * j + 2, - pdadcValues[4 * j + 2], - 4 * j + 3, - pdadcValues[4 * j + 3]); + ath_print(common, ATH_DBG_EEPROM, + "PDADC (%d,%4x): %4.4x " + "%8.8x\n", + i, regChainOffset, regOffset, + reg32); + + ath_print(common, ATH_DBG_EEPROM, + "PDADC: Chain %d | " + "PDADC %3d Value %3d | " + "PDADC %3d Value %3d | " + "PDADC %3d Value %3d | " + "PDADC %3d Value %3d |\n", + i, 4 * j, pdadcValues[4 * j], + 4 * j + 1, + pdadcValues[4 * j + 1], + 4 * j + 2, + pdadcValues[4 * j + 2], + 4 * j + 3, + pdadcValues[4 * j + 3]); regOffset += 4; } @@ -831,6 +836,7 @@ static void ath9k_hw_AR9287_set_txpower(struct ath_hw *ah, { #define INCREASE_MAXPOW_BY_TWO_CHAIN 6 #define INCREASE_MAXPOW_BY_THREE_CHAIN 10 + struct ath_common *common = ath9k_hw_common(ah); struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ar9287_eeprom *pEepData = &ah->eeprom.map9287; struct modal_eep_ar9287_header *pModal = &pEepData->modalHeader; @@ -966,8 +972,8 @@ static void ath9k_hw_AR9287_set_txpower(struct ath_hw *ah, INCREASE_MAXPOW_BY_THREE_CHAIN; break; default: - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Invalid chainmask configuration\n"); + ath_print(common, ATH_DBG_EEPROM, + "Invalid chainmask configuration\n"); break; } } @@ -1138,19 +1144,20 @@ static u16 ath9k_hw_AR9287_get_spur_channel(struct ath_hw *ah, { #define EEP_MAP9287_SPURCHAN \ (ah->eeprom.map9287.modalHeader.spurChans[i].spurChan) + struct ath_common *common = ath9k_hw_common(ah); u16 spur_val = AR_NO_SPUR; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Getting spur idx %d is2Ghz. %d val %x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); + ath_print(common, ATH_DBG_ANI, + "Getting spur idx %d is2Ghz. %d val %x\n", + i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { case SPUR_DISABLE: break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Getting spur val from new loc. %d\n", spur_val); + ath_print(common, ATH_DBG_ANI, + "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_MAP9287_SPURCHAN; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 4071fc91da0a..404a0341242c 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +#include "hw.h" static void ath9k_get_txgain_index(struct ath_hw *ah, struct ath9k_channel *chan, @@ -89,14 +89,15 @@ static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah) static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) { #define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16)) + struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data = (u16 *)&ah->eeprom.def; int addr, ar5416_eep_start_loc = 0x100; for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) { - if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc, + if (!ath9k_hw_nvram_read(common, addr + ar5416_eep_start_loc, eep_data)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Unable to read eeprom region\n"); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "Unable to read eeprom region\n"); return false; } eep_data++; @@ -109,19 +110,20 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) { struct ar5416_eeprom_def *eep = (struct ar5416_eeprom_def *) &ah->eeprom.def; + struct ath_common *common = ath9k_hw_common(ah); u16 *eepdata, temp, magic, magic2; u32 sum = 0, el; bool need_swap = false; int i, addr, size; - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n"); + if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + ath_print(common, ATH_DBG_FATAL, "Reading Magic # failed\n"); return false; } if (!ath9k_hw_use_flash(ah)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Read Magic = 0x%04X\n", magic); + ath_print(common, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); @@ -137,16 +139,16 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) eepdata++; } } else { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Invalid EEPROM Magic. " - "Endianness mismatch.\n"); + ath_print(common, ATH_DBG_FATAL, + "Invalid EEPROM Magic. " + "Endianness mismatch.\n"); return -EINVAL; } } } - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", - need_swap ? "True" : "False"); + ath_print(common, ATH_DBG_EEPROM, "need_swap = %s.\n", + need_swap ? "True" : "False"); if (need_swap) el = swab16(ah->eeprom.def.baseEepHeader.length); @@ -167,8 +169,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) u32 integer, j; u16 word; - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "EEPROM Endianness is not native.. Changing.\n"); + ath_print(common, ATH_DBG_EEPROM, + "EEPROM Endianness is not native.. Changing.\n"); word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; @@ -214,8 +216,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + ath_print(common, ATH_DBG_FATAL, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", sum, ah->eep_ops->get_eeprom_ver(ah)); return -EINVAL; } @@ -289,6 +291,11 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, return pBase->frac_n_5g; else return 0; + case EEP_PWR_TABLE_OFFSET: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_21) + return pBase->pwr_table_offset; + else + return AR5416_PWR_TABLE_OFFSET_DB; default: return 0; } @@ -739,6 +746,76 @@ static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hw *ah, return; } +static int16_t ath9k_change_gain_boundary_setting(struct ath_hw *ah, + u16 *gb, + u16 numXpdGain, + u16 pdGainOverlap_t2, + int8_t pwr_table_offset, + int16_t *diff) + +{ + u16 k; + + /* Prior to writing the boundaries or the pdadc vs. power table + * into the chip registers the default starting point on the pdadc + * vs. power table needs to be checked and the curve boundaries + * adjusted accordingly + */ + if (AR_SREV_9280_20_OR_LATER(ah)) { + u16 gb_limit; + + if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { + /* get the difference in dB */ + *diff = (u16)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB); + /* get the number of half dB steps */ + *diff *= 2; + /* change the original gain boundary settings + * by the number of half dB steps + */ + for (k = 0; k < numXpdGain; k++) + gb[k] = (u16)(gb[k] - *diff); + } + /* Because of a hardware limitation, ensure the gain boundary + * is not larger than (63 - overlap) + */ + gb_limit = (u16)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2); + + for (k = 0; k < numXpdGain; k++) + gb[k] = (u16)min(gb_limit, gb[k]); + } + + return *diff; +} + +static void ath9k_adjust_pdadc_values(struct ath_hw *ah, + int8_t pwr_table_offset, + int16_t diff, + u8 *pdadcValues) +{ +#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff) + u16 k; + + /* If this is a board that has a pwrTableOffset that differs from + * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the + * pdadc vs pwr table needs to be adjusted prior to writing to the + * chip. + */ + if (AR_SREV_9280_20_OR_LATER(ah)) { + if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { + /* shift the table to start at the new offset */ + for (k = 0; k < (u16)NUM_PDADC(diff); k++ ) { + pdadcValues[k] = pdadcValues[k + diff]; + } + + /* fill the back of the table */ + for (k = (u16)NUM_PDADC(diff); k < NUM_PDADC(0); k++) { + pdadcValues[k] = pdadcValues[NUM_PDADC(diff)]; + } + } + } +#undef NUM_PDADC +} + static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, struct ath9k_channel *chan, int16_t *pTxPowerIndexOffset) @@ -746,7 +823,7 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, #define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x) #define SM_PDGAIN_B(x, y) \ SM((gainBoundaries[x]), AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##y) - + struct ath_common *common = ath9k_hw_common(ah); struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; struct cal_data_per_freq *pRawDataset; u8 *pCalBChans = NULL; @@ -754,15 +831,18 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, static u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK]; u16 numPiers, i, j; - int16_t tMinCalPower; + int16_t tMinCalPower, diff = 0; u16 numXpdGain, xpdMask; u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 }; u32 reg32, regOffset, regChainOffset; int16_t modalIdx; + int8_t pwr_table_offset; modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0; xpdMask = pEepData->modalHeader[modalIdx].xpdGain; + pwr_table_offset = ah->eep_ops->get_eeprom(ah, EEP_PWR_TABLE_OFFSET); + if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_2) { pdGainOverlap_t2 = @@ -842,6 +922,13 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, numXpdGain); } + diff = ath9k_change_gain_boundary_setting(ah, + gainBoundaries, + numXpdGain, + pdGainOverlap_t2, + pwr_table_offset, + &diff); + if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) { if (OLC_FOR_AR9280_20_LATER) { REG_WRITE(ah, @@ -862,6 +949,10 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, } } + + ath9k_adjust_pdadc_values(ah, pwr_table_offset, + diff, pdadcValues); + regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; for (j = 0; j < 32; j++) { reg32 = ((pdadcValues[4 * j + 0] & 0xFF) << 0) | @@ -870,20 +961,20 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, ((pdadcValues[4 * j + 3] & 0xFF) << 24); REG_WRITE(ah, regOffset, reg32); - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "PDADC (%d,%4x): %4.4x %8.8x\n", - i, regChainOffset, regOffset, - reg32); - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "PDADC: Chain %d | PDADC %3d " - "Value %3d | PDADC %3d Value %3d | " - "PDADC %3d Value %3d | PDADC %3d " - "Value %3d |\n", - i, 4 * j, pdadcValues[4 * j], - 4 * j + 1, pdadcValues[4 * j + 1], - 4 * j + 2, pdadcValues[4 * j + 2], - 4 * j + 3, - pdadcValues[4 * j + 3]); + ath_print(common, ATH_DBG_EEPROM, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + ath_print(common, ATH_DBG_EEPROM, + "PDADC: Chain %d | PDADC %3d " + "Value %3d | PDADC %3d Value %3d | " + "PDADC %3d Value %3d | PDADC %3d " + "Value %3d |\n", + i, 4 * j, pdadcValues[4 * j], + 4 * j + 1, pdadcValues[4 * j + 1], + 4 * j + 2, pdadcValues[4 * j + 2], + 4 * j + 3, + pdadcValues[4 * j + 3]); regOffset += 4; } @@ -1197,8 +1288,13 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, } if (AR_SREV_9280_10_OR_LATER(ah)) { - for (i = 0; i < Ar5416RateSize; i++) - ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2; + for (i = 0; i < Ar5416RateSize; i++) { + int8_t pwr_table_offset; + + pwr_table_offset = ah->eep_ops->get_eeprom(ah, + EEP_PWR_TABLE_OFFSET); + ratesArray[i] -= pwr_table_offset * 2; + } } REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, @@ -1297,7 +1393,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, if (AR_SREV_9280_10_OR_LATER(ah)) regulatory->max_power_level = - ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2; + ratesArray[i] + AR5416_PWR_TABLE_OFFSET_DB * 2; else regulatory->max_power_level = ratesArray[i]; @@ -1311,8 +1407,8 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, regulatory->max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; break; default: - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Invalid chainmask configuration\n"); + ath_print(ath9k_hw_common(ah), ATH_DBG_EEPROM, + "Invalid chainmask configuration\n"); break; } } @@ -1349,20 +1445,21 @@ static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { #define EEP_DEF_SPURCHAN \ (ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan) + struct ath_common *common = ath9k_hw_common(ah); u16 spur_val = AR_NO_SPUR; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Getting spur idx %d is2Ghz. %d val %x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); + ath_print(common, ATH_DBG_ANI, + "Getting spur idx %d is2Ghz. %d val %x\n", + i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { case SPUR_DISABLE: break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Getting spur val from new loc. %d\n", spur_val); + ath_print(common, ATH_DBG_ANI, + "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_DEF_SPURCHAN; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index ca7694caf364..2ec61f08cfdb 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -16,9 +16,9 @@ #include <linux/io.h> #include <asm/unaligned.h> -#include <linux/pci.h> -#include "ath9k.h" +#include "hw.h" +#include "rc.h" #include "initvals.h" #define ATH9K_CLOCK_RATE_CCK 22 @@ -26,13 +26,27 @@ #define ATH9K_CLOCK_RATE_2GHZ_OFDM 44 static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); -static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan, - enum ath9k_ht_macmode macmode); +static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan); static u32 ath9k_hw_ini_fixup(struct ath_hw *ah, struct ar5416_eeprom_def *pEepData, u32 reg, u32 value); -static void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan); -static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan); + +MODULE_AUTHOR("Atheros Communications"); +MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); + +static int __init ath9k_init(void) +{ + return 0; +} +module_init(ath9k_init); + +static void __exit ath9k_exit(void) +{ + return; +} +module_exit(ath9k_exit); /********************/ /* Helper Functions */ @@ -40,7 +54,7 @@ static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan static u32 ath9k_hw_mac_usec(struct ath_hw *ah, u32 clks) { - struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; if (!ah->curchan) /* should really check for CCK instead */ return clks / ATH9K_CLOCK_RATE_CCK; @@ -52,7 +66,7 @@ static u32 ath9k_hw_mac_usec(struct ath_hw *ah, u32 clks) static u32 ath9k_hw_mac_to_usec(struct ath_hw *ah, u32 clks) { - struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; if (conf_is_ht40(conf)) return ath9k_hw_mac_usec(ah, clks) / 2; @@ -62,7 +76,7 @@ static u32 ath9k_hw_mac_to_usec(struct ath_hw *ah, u32 clks) static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs) { - struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; if (!ah->curchan) /* should really check for CCK instead */ return usecs *ATH9K_CLOCK_RATE_CCK; @@ -73,7 +87,7 @@ static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs) static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs) { - struct ieee80211_conf *conf = &ah->ah_sc->hw->conf; + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; if (conf_is_ht40(conf)) return ath9k_hw_mac_clks(ah, usecs) * 2; @@ -81,38 +95,6 @@ static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs) return ath9k_hw_mac_clks(ah, usecs); } -/* - * Read and write, they both share the same lock. We do this to serialize - * reads and writes on Atheros 802.11n PCI devices only. This is required - * as the FIFO on these devices can only accept sanely 2 requests. After - * that the device goes bananas. Serializing the reads/writes prevents this - * from happening. - */ - -void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val) -{ - if (ah->config.serialize_regmode == SER_REG_MODE_ON) { - unsigned long flags; - spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); - iowrite32(val, ah->ah_sc->mem + reg_offset); - spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); - } else - iowrite32(val, ah->ah_sc->mem + reg_offset); -} - -unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset) -{ - u32 val; - if (ah->config.serialize_regmode == SER_REG_MODE_ON) { - unsigned long flags; - spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); - val = ioread32(ah->ah_sc->mem + reg_offset); - spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); - } else - val = ioread32(ah->ah_sc->mem + reg_offset); - return val; -} - bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) { int i; @@ -126,12 +108,13 @@ bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) udelay(AH_TIME_QUANTUM); } - DPRINTF(ah->ah_sc, ATH_DBG_ANY, - "timeout (%d us) on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", - timeout, reg, REG_READ(ah, reg), mask, val); + ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, + "timeout (%d us) on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", + timeout, reg, REG_READ(ah, reg), mask, val); return false; } +EXPORT_SYMBOL(ath9k_hw_wait); u32 ath9k_hw_reverse_bits(u32 val, u32 n) { @@ -165,22 +148,19 @@ bool ath9k_get_channel_edges(struct ath_hw *ah, } u16 ath9k_hw_computetxtime(struct ath_hw *ah, - const struct ath_rate_table *rates, + u8 phy, int kbps, u32 frameLen, u16 rateix, bool shortPreamble) { u32 bitsPerSymbol, numBits, numSymbols, phyTime, txTime; - u32 kbps; - - kbps = rates->info[rateix].ratekbps; if (kbps == 0) return 0; - switch (rates->info[rateix].phy) { + switch (phy) { case WLAN_RC_PHY_CCK: phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; - if (shortPreamble && rates->info[rateix].short_preamble) + if (shortPreamble) phyTime >>= 1; numBits = frameLen << 3; txTime = CCK_SIFS_TIME + phyTime + ((numBits * 1000) / kbps); @@ -210,15 +190,15 @@ u16 ath9k_hw_computetxtime(struct ath_hw *ah, } break; default: - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Unknown phy %u (rate ix %u)\n", - rates->info[rateix].phy, rateix); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "Unknown phy %u (rate ix %u)\n", phy, rateix); txTime = 0; break; } return txTime; } +EXPORT_SYMBOL(ath9k_hw_computetxtime); void ath9k_hw_get_channel_centers(struct ath_hw *ah, struct ath9k_channel *chan, @@ -245,10 +225,9 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah, centers->ctl_center = centers->synth_center - (extoff * HT40_CHANNEL_CENTER_SHIFT); + /* 25 MHz spacing is supported by hw but not on upper layers */ centers->ext_center = - centers->synth_center + (extoff * - ((ah->extprotspacing == ATH9K_HT_EXTPROTSPACING_20) ? - HT40_CHANNEL_CENTER_SHIFT : 15)); + centers->synth_center + (extoff * HT40_CHANNEL_CENTER_SHIFT); } /******************/ @@ -317,6 +296,7 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah) static bool ath9k_hw_chip_test(struct ath_hw *ah) { + struct ath_common *common = ath9k_hw_common(ah); u32 regAddr[2] = { AR_STA_ID0, AR_PHY_BASE + (8 << 2) }; u32 regHold[2]; u32 patternData[4] = { 0x55555555, @@ -335,10 +315,11 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah) REG_WRITE(ah, addr, wrData); rdData = REG_READ(ah, addr); if (rdData != wrData) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "address test failed " - "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", - addr, wrData, rdData); + ath_print(common, ATH_DBG_FATAL, + "address test failed " + "addr: 0x%08x - wr:0x%08x != " + "rd:0x%08x\n", + addr, wrData, rdData); return false; } } @@ -347,10 +328,11 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah) REG_WRITE(ah, addr, wrData); rdData = REG_READ(ah, addr); if (wrData != rdData) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "address test failed " - "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", - addr, wrData, rdData); + ath_print(common, ATH_DBG_FATAL, + "address test failed " + "addr: 0x%08x - wr:0x%08x != " + "rd:0x%08x\n", + addr, wrData, rdData); return false; } } @@ -404,8 +386,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.cck_trig_high = 200; ah->config.cck_trig_low = 100; ah->config.enable_ani = 1; - ah->config.diversity_control = ATH9K_ANT_VARIABLE; - ah->config.antenna_switch_swap = 0; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { ah->config.spurchans[i][0] = AR_NO_SPUR; @@ -433,6 +413,7 @@ static void ath9k_hw_init_config(struct ath_hw *ah) if (num_possible_cpus() > 1) ah->config.serialize_regmode = SER_REG_MODE_AUTO; } +EXPORT_SYMBOL(ath9k_hw_init); static void ath9k_hw_init_defaults(struct ath_hw *ah) { @@ -459,27 +440,9 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->acktimeout = (u32) -1; ah->ctstimeout = (u32) -1; ah->globaltxtimeout = (u32) -1; - - ah->gbeacon_rate = 0; - ah->power_mode = ATH9K_PM_UNDEFINED; } -static int ath9k_hw_rfattach(struct ath_hw *ah) -{ - bool rfStatus = false; - int ecode = 0; - - rfStatus = ath9k_hw_init_rf(ah, &ecode); - if (!rfStatus) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "RF setup failed, status: %u\n", ecode); - return ecode; - } - - return 0; -} - static int ath9k_hw_rf_claim(struct ath_hw *ah) { u32 val; @@ -497,9 +460,9 @@ static int ath9k_hw_rf_claim(struct ath_hw *ah) case AR_RAD2122_SREV_MAJOR: break; default: - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Radio Chip Rev 0x%02X not supported\n", - val & AR_RADIO_SREV_MAJOR); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "Radio Chip Rev 0x%02X not supported\n", + val & AR_RADIO_SREV_MAJOR); return -EOPNOTSUPP; } @@ -510,6 +473,7 @@ static int ath9k_hw_rf_claim(struct ath_hw *ah) static int ath9k_hw_init_macaddr(struct ath_hw *ah) { + struct ath_common *common = ath9k_hw_common(ah); u32 sum; int i; u16 eeval; @@ -518,8 +482,8 @@ static int ath9k_hw_init_macaddr(struct ath_hw *ah) for (i = 0; i < 3; i++) { eeval = ah->eep_ops->get_eeprom(ah, AR_EEPROM_MAC(i)); sum += eeval; - ah->macaddr[2 * i] = eeval >> 8; - ah->macaddr[2 * i + 1] = eeval & 0xff; + common->macaddr[2 * i] = eeval >> 8; + common->macaddr[2 * i + 1] = eeval & 0xff; } if (sum == 0 || sum == 0xffff * 3) return -EADDRNOTAVAIL; @@ -590,12 +554,20 @@ static int ath9k_hw_post_init(struct ath_hw *ah) if (ecode != 0) return ecode; - DPRINTF(ah->ah_sc, ATH_DBG_CONFIG, "Eeprom VER: %d, REV: %d\n", - ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); - - ecode = ath9k_hw_rfattach(ah); - if (ecode != 0) - return ecode; + ath_print(ath9k_hw_common(ah), ATH_DBG_CONFIG, + "Eeprom VER: %d, REV: %d\n", + ah->eep_ops->get_eeprom_ver(ah), + ah->eep_ops->get_eeprom_rev(ah)); + + if (!AR_SREV_9280_10_OR_LATER(ah)) { + ecode = ath9k_hw_rf_alloc_ext_banks(ah); + if (ecode) { + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "Failed allocating banks for " + "external radio\n"); + return ecode; + } + } if (!AR_SREV_9100(ah)) { ath9k_hw_ani_setup(ah); @@ -617,6 +589,7 @@ static bool ath9k_hw_devid_supported(u16 devid) case AR9285_DEVID_PCIE: case AR5416_DEVID_AR9287_PCI: case AR5416_DEVID_AR9287_PCIE: + case AR9271_USB: return true; default: break; @@ -634,9 +607,8 @@ static bool ath9k_hw_macversion_supported(u32 macversion) case AR_SREV_VERSION_9280: case AR_SREV_VERSION_9285: case AR_SREV_VERSION_9287: - return true; - /* Not yet */ case AR_SREV_VERSION_9271: + return true; default: break; } @@ -670,10 +642,13 @@ static void ath9k_hw_init_cal_settings(struct ath_hw *ah) static void ath9k_hw_init_mode_regs(struct ath_hw *ah) { if (AR_SREV_9271(ah)) { - INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271_1_0, - ARRAY_SIZE(ar9271Modes_9271_1_0), 6); - INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271_1_0, - ARRAY_SIZE(ar9271Common_9271_1_0), 2); + INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271, + ARRAY_SIZE(ar9271Modes_9271), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271, + ARRAY_SIZE(ar9271Common_9271), 2); + INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only, + ar9271Modes_9271_1_0_only, + ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6); return; } @@ -905,21 +880,27 @@ static void ath9k_hw_init_11a_eeprom_fix(struct ath_hw *ah) int ath9k_hw_init(struct ath_hw *ah) { + struct ath_common *common = ath9k_hw_common(ah); int r = 0; - if (!ath9k_hw_devid_supported(ah->hw_version.devid)) + if (!ath9k_hw_devid_supported(ah->hw_version.devid)) { + ath_print(common, ATH_DBG_FATAL, + "Unsupported device ID: 0x%0x\n", + ah->hw_version.devid); return -EOPNOTSUPP; + } ath9k_hw_init_defaults(ah); ath9k_hw_init_config(ah); if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Couldn't reset chip\n"); + ath_print(common, ATH_DBG_FATAL, + "Couldn't reset chip\n"); return -EIO; } if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Couldn't wakeup chip\n"); + ath_print(common, ATH_DBG_FATAL, "Couldn't wakeup chip\n"); return -EIO; } @@ -934,14 +915,19 @@ int ath9k_hw_init(struct ath_hw *ah) } } - DPRINTF(ah->ah_sc, ATH_DBG_RESET, "serialize_regmode is %d\n", + ath_print(common, ATH_DBG_RESET, "serialize_regmode is %d\n", ah->config.serialize_regmode); + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) + ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1; + else + ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD; + if (!ath9k_hw_macversion_supported(ah->hw_version.macVersion)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Mac Chip Rev 0x%02x.%x is not supported by " - "this driver\n", ah->hw_version.macVersion, - ah->hw_version.macRev); + ath_print(common, ATH_DBG_FATAL, + "Mac Chip Rev 0x%02x.%x is not supported by " + "this driver\n", ah->hw_version.macVersion, + ah->hw_version.macRev); return -EOPNOTSUPP; } @@ -959,8 +945,14 @@ int ath9k_hw_init(struct ath_hw *ah) ath9k_hw_init_cal_settings(ah); ah->ani_function = ATH9K_ANI_ALL; - if (AR_SREV_9280_10_OR_LATER(ah)) + if (AR_SREV_9280_10_OR_LATER(ah)) { ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; + ah->ath9k_hw_rf_set_freq = &ath9k_hw_ar9280_set_channel; + ah->ath9k_hw_spur_mitigate_freq = &ath9k_hw_9280_spur_mitigate; + } else { + ah->ath9k_hw_rf_set_freq = &ath9k_hw_set_channel; + ah->ath9k_hw_spur_mitigate_freq = &ath9k_hw_spur_mitigate; + } ath9k_hw_init_mode_regs(ah); @@ -969,18 +961,31 @@ int ath9k_hw_init(struct ath_hw *ah) else ath9k_hw_disablepcie(ah); + /* Support for Japan ch.14 (2484) spread */ + if (AR_SREV_9287_11_OR_LATER(ah)) { + INIT_INI_ARRAY(&ah->iniCckfirNormal, + ar9287Common_normal_cck_fir_coeff_92871_1, + ARRAY_SIZE(ar9287Common_normal_cck_fir_coeff_92871_1), 2); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9287Common_japan_2484_cck_fir_coeff_92871_1, + ARRAY_SIZE(ar9287Common_japan_2484_cck_fir_coeff_92871_1), 2); + } + r = ath9k_hw_post_init(ah); if (r) return r; ath9k_hw_init_mode_gain_regs(ah); - ath9k_hw_fill_cap_info(ah); + r = ath9k_hw_fill_cap_info(ah); + if (r) + return r; + ath9k_hw_init_11a_eeprom_fix(ah); r = ath9k_hw_init_macaddr(ah); if (r) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Failed to initialize MAC address\n"); + ath_print(common, ATH_DBG_FATAL, + "Failed to initialize MAC address\n"); return r; } @@ -991,6 +996,8 @@ int ath9k_hw_init(struct ath_hw *ah) ath9k_init_nfcal_hist_buffer(ah); + common->state = ATH_HW_INITIALIZED; + return 0; } @@ -1027,6 +1034,22 @@ static void ath9k_hw_init_qos(struct ath_hw *ah) REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); } +static void ath9k_hw_change_target_baud(struct ath_hw *ah, u32 freq, u32 baud) +{ + u32 lcr; + u32 baud_divider = freq * 1000 * 1000 / 16 / baud; + + lcr = REG_READ(ah , 0x5100c); + lcr |= 0x80; + + REG_WRITE(ah, 0x5100c, lcr); + REG_WRITE(ah, 0x51004, (baud_divider >> 8)); + REG_WRITE(ah, 0x51000, (baud_divider & 0xff)); + + lcr &= ~0x80; + REG_WRITE(ah, 0x5100c, lcr); +} + static void ath9k_hw_init_pll(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1090,6 +1113,26 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, } REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); + /* Switch the core clock for ar9271 to 117Mhz */ + if (AR_SREV_9271(ah)) { + if ((pll == 0x142c) || (pll == 0x2850) ) { + udelay(500); + /* set CLKOBS to output AHB clock */ + REG_WRITE(ah, 0x7020, 0xe); + /* + * 0x304: 117Mhz, ahb_ratio: 1x1 + * 0x306: 40Mhz, ahb_ratio: 1x1 + */ + REG_WRITE(ah, 0x50040, 0x304); + /* + * makes adjustments for the baud dividor to keep the + * targetted baud rate based on the used core clock. + */ + ath9k_hw_change_target_baud(ah, AR9271_CORE_CLOCK, + AR9271_TARGET_BAUD_RATE); + } + } + udelay(RTC_PLL_SETTLE_DELAY); REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK); @@ -1107,7 +1150,7 @@ static void ath9k_hw_init_chain_masks(struct ath_hw *ah) REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); case 0x3: - if (((ah)->hw_version.macVersion <= AR_SREV_VERSION_9160)) { + if (ah->hw_version.macVersion == AR_SREV_REVISION_5416_10) { REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); break; @@ -1164,7 +1207,8 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, static bool ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) { if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) { - DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad ack timeout %u\n", us); + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, + "bad ack timeout %u\n", us); ah->acktimeout = (u32) -1; return false; } else { @@ -1178,7 +1222,8 @@ static bool ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) static bool ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) { if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) { - DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad cts timeout %u\n", us); + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, + "bad cts timeout %u\n", us); ah->ctstimeout = (u32) -1; return false; } else { @@ -1192,8 +1237,8 @@ static bool ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) { if (tu > 0xFFFF) { - DPRINTF(ah->ah_sc, ATH_DBG_XMIT, - "bad global tx timeout %u\n", tu); + ath_print(ath9k_hw_common(ah), ATH_DBG_XMIT, + "bad global tx timeout %u\n", tu); ah->globaltxtimeout = (u32) -1; return false; } else { @@ -1205,8 +1250,8 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) static void ath9k_hw_init_user_settings(struct ath_hw *ah) { - DPRINTF(ah->ah_sc, ATH_DBG_RESET, "ah->misc_mode 0x%x\n", - ah->misc_mode); + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, "ah->misc_mode 0x%x\n", + ah->misc_mode); if (ah->misc_mode != 0) REG_WRITE(ah, AR_PCU_MISC, @@ -1229,14 +1274,23 @@ const char *ath9k_hw_probe(u16 vendorid, u16 devid) void ath9k_hw_detach(struct ath_hw *ah) { + struct ath_common *common = ath9k_hw_common(ah); + + if (common->state <= ATH_HW_INITIALIZED) + goto free_hw; + if (!AR_SREV_9100(ah)) ath9k_hw_ani_disable(ah); - ath9k_hw_rf_free(ah); ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); + +free_hw: + if (!AR_SREV_9280_10_OR_LATER(ah)) + ath9k_hw_rf_free_ext_banks(ah); kfree(ah); ah = NULL; } +EXPORT_SYMBOL(ath9k_hw_detach); /*******/ /* INI */ @@ -1254,7 +1308,8 @@ static void ath9k_hw_override_ini(struct ath_hw *ah, * AR9271 1.1 */ if (AR_SREV_9271_10(ah)) { - val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) | AR_PHY_SPECTRAL_SCAN_ENABLE; + val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) | + AR_PHY_SPECTRAL_SCAN_ENABLE; REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val); } else if (AR_SREV_9271_11(ah)) @@ -1298,28 +1353,29 @@ static u32 ath9k_hw_def_ini_fixup(struct ath_hw *ah, u32 reg, u32 value) { struct base_eep_header *pBase = &(pEepData->baseEepHeader); + struct ath_common *common = ath9k_hw_common(ah); switch (ah->hw_version.devid) { case AR9280_DEVID_PCI: if (reg == 0x7894) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + ath_print(common, ATH_DBG_EEPROM, "ini VAL: %x EEPROM: %x\n", value, (pBase->version & 0xff)); if ((pBase->version & 0xff) > 0x0a) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "PWDCLKIND: %d\n", - pBase->pwdclkind); + ath_print(common, ATH_DBG_EEPROM, + "PWDCLKIND: %d\n", + pBase->pwdclkind); value &= ~AR_AN_TOP2_PWDCLKIND; value |= AR_AN_TOP2_PWDCLKIND & (pBase->pwdclkind << AR_AN_TOP2_PWDCLKIND_S); } else { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "PWDCLKIND Earlier Rev\n"); + ath_print(common, ATH_DBG_EEPROM, + "PWDCLKIND Earlier Rev\n"); } - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "final ini VAL: %x\n", value); + ath_print(common, ATH_DBG_EEPROM, + "final ini VAL: %x\n", value); } break; } @@ -1374,8 +1430,7 @@ static u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, } static int ath9k_hw_process_ini(struct ath_hw *ah, - struct ath9k_channel *chan, - enum ath9k_ht_macmode macmode) + struct ath9k_channel *chan) { struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); int i, regWrites = 0; @@ -1469,7 +1524,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, DO_DELAY(regWrites); } - ath9k_hw_write_regs(ah, modesIndex, freqIndex, regWrites); + ath9k_hw_write_regs(ah, freqIndex, regWrites); + + if (AR_SREV_9271_10(ah)) + REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only, + modesIndex, regWrites); if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) { REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, @@ -1477,7 +1536,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, } ath9k_hw_override_ini(ah, chan); - ath9k_hw_set_regs(ah, chan, macmode); + ath9k_hw_set_regs(ah, chan); ath9k_hw_init_chain_masks(ah); if (OLC_FOR_AR9280_20_LATER) @@ -1491,8 +1550,8 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, (u32) regulatory->power_limit)); if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "ar5416SetRfRegs failed\n"); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "ar5416SetRfRegs failed\n"); return -EIO; } @@ -1697,16 +1756,14 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) REG_WRITE(ah, AR_RTC_RC, 0); if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) { - DPRINTF(ah->ah_sc, ATH_DBG_RESET, - "RTC stuck in MAC reset\n"); + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, + "RTC stuck in MAC reset\n"); return false; } if (!AR_SREV_9100(ah)) REG_WRITE(ah, AR_RC, 0); - ath9k_hw_init_pll(ah, NULL); - if (AR_SREV_9100(ah)) udelay(50); @@ -1734,7 +1791,8 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) AR_RTC_STATUS_M, AR_RTC_STATUS_ON, AH_WAIT_TIMEOUT)) { - DPRINTF(ah->ah_sc, ATH_DBG_RESET, "RTC not waking up\n"); + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, + "RTC not waking up\n"); return false; } @@ -1759,8 +1817,7 @@ static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type) } } -static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan, - enum ath9k_ht_macmode macmode) +static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan) { u32 phymode; u32 enableDacFifo = 0; @@ -1779,12 +1836,10 @@ static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan, (chan->chanmode == CHANNEL_G_HT40PLUS)) phymode |= AR_PHY_FC_DYN2040_PRI_CH; - if (ah->extprotspacing == ATH9K_HT_EXTPROTSPACING_25) - phymode |= AR_PHY_FC_DYN2040_EXT_CH; } REG_WRITE(ah, AR_PHY_TURBO, phymode); - ath9k_hw_set11nmac2040(ah, macmode); + ath9k_hw_set11nmac2040(ah); REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); @@ -1810,17 +1865,19 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah, } static bool ath9k_hw_channel_change(struct ath_hw *ah, - struct ath9k_channel *chan, - enum ath9k_ht_macmode macmode) + struct ath9k_channel *chan) { struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *channel = chan->chan; u32 synthDelay, qnum; + int r; for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { if (ath9k_hw_numtxpending(ah, qnum)) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, - "Transmit frames pending on queue %d\n", qnum); + ath_print(common, ATH_DBG_QUEUE, + "Transmit frames pending on " + "queue %d\n", qnum); return false; } } @@ -1828,21 +1885,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN); if (!ath9k_hw_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN, AR_PHY_RFBUS_GRANT_EN, AH_WAIT_TIMEOUT)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Could not kill baseband RX\n"); + ath_print(common, ATH_DBG_FATAL, + "Could not kill baseband RX\n"); return false; } - ath9k_hw_set_regs(ah, chan, macmode); + ath9k_hw_set_regs(ah, chan); - if (AR_SREV_9280_10_OR_LATER(ah)) { - ath9k_hw_ar9280_set_channel(ah, chan); - } else { - if (!(ath9k_hw_set_channel(ah, chan))) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Failed to set channel\n"); - return false; - } + r = ah->ath9k_hw_rf_set_freq(ah, chan); + if (r) { + ath_print(common, ATH_DBG_FATAL, + "Failed to set channel\n"); + return false; } ah->eep_ops->set_txpower(ah, chan, @@ -1865,10 +1919,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) ath9k_hw_set_delta_slope(ah, chan); - if (AR_SREV_9280_10_OR_LATER(ah)) - ath9k_hw_9280_spur_mitigate(ah, chan); - else - ath9k_hw_spur_mitigate(ah, chan); + ah->ath9k_hw_spur_mitigate_freq(ah, chan); if (!chan->oneTimeCalsDone) chan->oneTimeCalsDone = true; @@ -1876,457 +1927,6 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, return true; } -static void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan) -{ - int bb_spur = AR_NO_SPUR; - int freq; - int bin, cur_bin; - int bb_spur_off, spur_subchannel_sd; - int spur_freq_sd; - int spur_delta_phase; - int denominator; - int upper, lower, cur_vit_mask; - int tmp, newVal; - int i; - int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, - AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 - }; - int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, - AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 - }; - int inc[4] = { 0, 100, 0, 0 }; - struct chan_centers centers; - - int8_t mask_m[123]; - int8_t mask_p[123]; - int8_t mask_amt; - int tmp_mask; - int cur_bb_spur; - bool is2GHz = IS_CHAN_2GHZ(chan); - - memset(&mask_m, 0, sizeof(int8_t) * 123); - memset(&mask_p, 0, sizeof(int8_t) * 123); - - ath9k_hw_get_channel_centers(ah, chan, ¢ers); - freq = centers.synth_center; - - ah->config.spurmode = SPUR_ENABLE_EEPROM; - for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { - cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); - - if (is2GHz) - cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_2GHZ; - else - cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_5GHZ; - - if (AR_NO_SPUR == cur_bb_spur) - break; - cur_bb_spur = cur_bb_spur - freq; - - if (IS_CHAN_HT40(chan)) { - if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT40) && - (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT40)) { - bb_spur = cur_bb_spur; - break; - } - } else if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT20) && - (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT20)) { - bb_spur = cur_bb_spur; - break; - } - } - - if (AR_NO_SPUR == bb_spur) { - REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, - AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); - return; - } else { - REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, - AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); - } - - bin = bb_spur * 320; - - tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); - - newVal = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | - AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | - AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | - AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); - REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), newVal); - - newVal = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | - AR_PHY_SPUR_REG_ENABLE_MASK_PPM | - AR_PHY_SPUR_REG_MASK_RATE_SELECT | - AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | - SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); - REG_WRITE(ah, AR_PHY_SPUR_REG, newVal); - - if (IS_CHAN_HT40(chan)) { - if (bb_spur < 0) { - spur_subchannel_sd = 1; - bb_spur_off = bb_spur + 10; - } else { - spur_subchannel_sd = 0; - bb_spur_off = bb_spur - 10; - } - } else { - spur_subchannel_sd = 0; - bb_spur_off = bb_spur; - } - - if (IS_CHAN_HT40(chan)) - spur_delta_phase = - ((bb_spur * 262144) / - 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; - else - spur_delta_phase = - ((bb_spur * 524288) / - 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; - - denominator = IS_CHAN_2GHZ(chan) ? 44 : 40; - spur_freq_sd = ((bb_spur_off * 2048) / denominator) & 0x3ff; - - newVal = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | - SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | - SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); - REG_WRITE(ah, AR_PHY_TIMING11, newVal); - - newVal = spur_subchannel_sd << AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S; - REG_WRITE(ah, AR_PHY_SFCORR_EXT, newVal); - - cur_bin = -6000; - upper = bin + 100; - lower = bin - 100; - - for (i = 0; i < 4; i++) { - int pilot_mask = 0; - int chan_mask = 0; - int bp = 0; - for (bp = 0; bp < 30; bp++) { - if ((cur_bin > lower) && (cur_bin < upper)) { - pilot_mask = pilot_mask | 0x1 << bp; - chan_mask = chan_mask | 0x1 << bp; - } - cur_bin += 100; - } - cur_bin += inc[i]; - REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); - REG_WRITE(ah, chan_mask_reg[i], chan_mask); - } - - cur_vit_mask = 6100; - upper = bin + 120; - lower = bin - 120; - - for (i = 0; i < 123; i++) { - if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { - - /* workaround for gcc bug #37014 */ - volatile int tmp_v = abs(cur_vit_mask - bin); - - if (tmp_v < 75) - mask_amt = 1; - else - mask_amt = 0; - if (cur_vit_mask < 0) - mask_m[abs(cur_vit_mask / 100)] = mask_amt; - else - mask_p[cur_vit_mask / 100] = mask_amt; - } - cur_vit_mask -= 100; - } - - tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) - | (mask_m[48] << 26) | (mask_m[49] << 24) - | (mask_m[50] << 22) | (mask_m[51] << 20) - | (mask_m[52] << 18) | (mask_m[53] << 16) - | (mask_m[54] << 14) | (mask_m[55] << 12) - | (mask_m[56] << 10) | (mask_m[57] << 8) - | (mask_m[58] << 6) | (mask_m[59] << 4) - | (mask_m[60] << 2) | (mask_m[61] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); - REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); - - tmp_mask = (mask_m[31] << 28) - | (mask_m[32] << 26) | (mask_m[33] << 24) - | (mask_m[34] << 22) | (mask_m[35] << 20) - | (mask_m[36] << 18) | (mask_m[37] << 16) - | (mask_m[48] << 14) | (mask_m[39] << 12) - | (mask_m[40] << 10) | (mask_m[41] << 8) - | (mask_m[42] << 6) | (mask_m[43] << 4) - | (mask_m[44] << 2) | (mask_m[45] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); - - tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) - | (mask_m[18] << 26) | (mask_m[18] << 24) - | (mask_m[20] << 22) | (mask_m[20] << 20) - | (mask_m[22] << 18) | (mask_m[22] << 16) - | (mask_m[24] << 14) | (mask_m[24] << 12) - | (mask_m[25] << 10) | (mask_m[26] << 8) - | (mask_m[27] << 6) | (mask_m[28] << 4) - | (mask_m[29] << 2) | (mask_m[30] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); - - tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) - | (mask_m[2] << 26) | (mask_m[3] << 24) - | (mask_m[4] << 22) | (mask_m[5] << 20) - | (mask_m[6] << 18) | (mask_m[7] << 16) - | (mask_m[8] << 14) | (mask_m[9] << 12) - | (mask_m[10] << 10) | (mask_m[11] << 8) - | (mask_m[12] << 6) | (mask_m[13] << 4) - | (mask_m[14] << 2) | (mask_m[15] << 0); - REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); - - tmp_mask = (mask_p[15] << 28) - | (mask_p[14] << 26) | (mask_p[13] << 24) - | (mask_p[12] << 22) | (mask_p[11] << 20) - | (mask_p[10] << 18) | (mask_p[9] << 16) - | (mask_p[8] << 14) | (mask_p[7] << 12) - | (mask_p[6] << 10) | (mask_p[5] << 8) - | (mask_p[4] << 6) | (mask_p[3] << 4) - | (mask_p[2] << 2) | (mask_p[1] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); - - tmp_mask = (mask_p[30] << 28) - | (mask_p[29] << 26) | (mask_p[28] << 24) - | (mask_p[27] << 22) | (mask_p[26] << 20) - | (mask_p[25] << 18) | (mask_p[24] << 16) - | (mask_p[23] << 14) | (mask_p[22] << 12) - | (mask_p[21] << 10) | (mask_p[20] << 8) - | (mask_p[19] << 6) | (mask_p[18] << 4) - | (mask_p[17] << 2) | (mask_p[16] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); - - tmp_mask = (mask_p[45] << 28) - | (mask_p[44] << 26) | (mask_p[43] << 24) - | (mask_p[42] << 22) | (mask_p[41] << 20) - | (mask_p[40] << 18) | (mask_p[39] << 16) - | (mask_p[38] << 14) | (mask_p[37] << 12) - | (mask_p[36] << 10) | (mask_p[35] << 8) - | (mask_p[34] << 6) | (mask_p[33] << 4) - | (mask_p[32] << 2) | (mask_p[31] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); - - tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) - | (mask_p[59] << 26) | (mask_p[58] << 24) - | (mask_p[57] << 22) | (mask_p[56] << 20) - | (mask_p[55] << 18) | (mask_p[54] << 16) - | (mask_p[53] << 14) | (mask_p[52] << 12) - | (mask_p[51] << 10) | (mask_p[50] << 8) - | (mask_p[49] << 6) | (mask_p[48] << 4) - | (mask_p[47] << 2) | (mask_p[46] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); -} - -static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan) -{ - int bb_spur = AR_NO_SPUR; - int bin, cur_bin; - int spur_freq_sd; - int spur_delta_phase; - int denominator; - int upper, lower, cur_vit_mask; - int tmp, new; - int i; - int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, - AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 - }; - int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, - AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 - }; - int inc[4] = { 0, 100, 0, 0 }; - - int8_t mask_m[123]; - int8_t mask_p[123]; - int8_t mask_amt; - int tmp_mask; - int cur_bb_spur; - bool is2GHz = IS_CHAN_2GHZ(chan); - - memset(&mask_m, 0, sizeof(int8_t) * 123); - memset(&mask_p, 0, sizeof(int8_t) * 123); - - for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { - cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); - if (AR_NO_SPUR == cur_bb_spur) - break; - cur_bb_spur = cur_bb_spur - (chan->channel * 10); - if ((cur_bb_spur > -95) && (cur_bb_spur < 95)) { - bb_spur = cur_bb_spur; - break; - } - } - - if (AR_NO_SPUR == bb_spur) - return; - - bin = bb_spur * 32; - - tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); - new = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | - AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | - AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | - AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); - - REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), new); - - new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | - AR_PHY_SPUR_REG_ENABLE_MASK_PPM | - AR_PHY_SPUR_REG_MASK_RATE_SELECT | - AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | - SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); - REG_WRITE(ah, AR_PHY_SPUR_REG, new); - - spur_delta_phase = ((bb_spur * 524288) / 100) & - AR_PHY_TIMING11_SPUR_DELTA_PHASE; - - denominator = IS_CHAN_2GHZ(chan) ? 440 : 400; - spur_freq_sd = ((bb_spur * 2048) / denominator) & 0x3ff; - - new = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | - SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | - SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); - REG_WRITE(ah, AR_PHY_TIMING11, new); - - cur_bin = -6000; - upper = bin + 100; - lower = bin - 100; - - for (i = 0; i < 4; i++) { - int pilot_mask = 0; - int chan_mask = 0; - int bp = 0; - for (bp = 0; bp < 30; bp++) { - if ((cur_bin > lower) && (cur_bin < upper)) { - pilot_mask = pilot_mask | 0x1 << bp; - chan_mask = chan_mask | 0x1 << bp; - } - cur_bin += 100; - } - cur_bin += inc[i]; - REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); - REG_WRITE(ah, chan_mask_reg[i], chan_mask); - } - - cur_vit_mask = 6100; - upper = bin + 120; - lower = bin - 120; - - for (i = 0; i < 123; i++) { - if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { - - /* workaround for gcc bug #37014 */ - volatile int tmp_v = abs(cur_vit_mask - bin); - - if (tmp_v < 75) - mask_amt = 1; - else - mask_amt = 0; - if (cur_vit_mask < 0) - mask_m[abs(cur_vit_mask / 100)] = mask_amt; - else - mask_p[cur_vit_mask / 100] = mask_amt; - } - cur_vit_mask -= 100; - } - - tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) - | (mask_m[48] << 26) | (mask_m[49] << 24) - | (mask_m[50] << 22) | (mask_m[51] << 20) - | (mask_m[52] << 18) | (mask_m[53] << 16) - | (mask_m[54] << 14) | (mask_m[55] << 12) - | (mask_m[56] << 10) | (mask_m[57] << 8) - | (mask_m[58] << 6) | (mask_m[59] << 4) - | (mask_m[60] << 2) | (mask_m[61] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); - REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); - - tmp_mask = (mask_m[31] << 28) - | (mask_m[32] << 26) | (mask_m[33] << 24) - | (mask_m[34] << 22) | (mask_m[35] << 20) - | (mask_m[36] << 18) | (mask_m[37] << 16) - | (mask_m[48] << 14) | (mask_m[39] << 12) - | (mask_m[40] << 10) | (mask_m[41] << 8) - | (mask_m[42] << 6) | (mask_m[43] << 4) - | (mask_m[44] << 2) | (mask_m[45] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); - - tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) - | (mask_m[18] << 26) | (mask_m[18] << 24) - | (mask_m[20] << 22) | (mask_m[20] << 20) - | (mask_m[22] << 18) | (mask_m[22] << 16) - | (mask_m[24] << 14) | (mask_m[24] << 12) - | (mask_m[25] << 10) | (mask_m[26] << 8) - | (mask_m[27] << 6) | (mask_m[28] << 4) - | (mask_m[29] << 2) | (mask_m[30] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); - - tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) - | (mask_m[2] << 26) | (mask_m[3] << 24) - | (mask_m[4] << 22) | (mask_m[5] << 20) - | (mask_m[6] << 18) | (mask_m[7] << 16) - | (mask_m[8] << 14) | (mask_m[9] << 12) - | (mask_m[10] << 10) | (mask_m[11] << 8) - | (mask_m[12] << 6) | (mask_m[13] << 4) - | (mask_m[14] << 2) | (mask_m[15] << 0); - REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); - - tmp_mask = (mask_p[15] << 28) - | (mask_p[14] << 26) | (mask_p[13] << 24) - | (mask_p[12] << 22) | (mask_p[11] << 20) - | (mask_p[10] << 18) | (mask_p[9] << 16) - | (mask_p[8] << 14) | (mask_p[7] << 12) - | (mask_p[6] << 10) | (mask_p[5] << 8) - | (mask_p[4] << 6) | (mask_p[3] << 4) - | (mask_p[2] << 2) | (mask_p[1] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); - - tmp_mask = (mask_p[30] << 28) - | (mask_p[29] << 26) | (mask_p[28] << 24) - | (mask_p[27] << 22) | (mask_p[26] << 20) - | (mask_p[25] << 18) | (mask_p[24] << 16) - | (mask_p[23] << 14) | (mask_p[22] << 12) - | (mask_p[21] << 10) | (mask_p[20] << 8) - | (mask_p[19] << 6) | (mask_p[18] << 4) - | (mask_p[17] << 2) | (mask_p[16] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); - - tmp_mask = (mask_p[45] << 28) - | (mask_p[44] << 26) | (mask_p[43] << 24) - | (mask_p[42] << 22) | (mask_p[41] << 20) - | (mask_p[40] << 18) | (mask_p[39] << 16) - | (mask_p[38] << 14) | (mask_p[37] << 12) - | (mask_p[36] << 10) | (mask_p[35] << 8) - | (mask_p[34] << 6) | (mask_p[33] << 4) - | (mask_p[32] << 2) | (mask_p[31] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); - - tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) - | (mask_p[59] << 26) | (mask_p[58] << 24) - | (mask_p[57] << 22) | (mask_p[56] << 20) - | (mask_p[55] << 18) | (mask_p[54] << 16) - | (mask_p[53] << 14) | (mask_p[52] << 12) - | (mask_p[51] << 10) | (mask_p[50] << 8) - | (mask_p[49] << 6) | (mask_p[48] << 4) - | (mask_p[47] << 2) | (mask_p[46] << 0); - REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); - REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); -} - static void ath9k_enable_rfkill(struct ath_hw *ah) { REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, @@ -2342,17 +1942,16 @@ static void ath9k_enable_rfkill(struct ath_hw *ah) int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, bool bChannelChange) { + struct ath_common *common = ath9k_hw_common(ah); u32 saveLedState; - struct ath_softc *sc = ah->ah_sc; struct ath9k_channel *curchan = ah->curchan; u32 saveDefAntenna; u32 macStaId1; u64 tsf = 0; int i, rx_chainmask, r; - ah->extprotspacing = sc->ht_extprotspacing; - ah->txchainmask = sc->tx_chainmask; - ah->rxchainmask = sc->rx_chainmask; + ah->txchainmask = common->tx_chainmask; + ah->rxchainmask = common->rx_chainmask; if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; @@ -2369,7 +1968,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, !(AR_SREV_9280(ah) || IS_CHAN_A_5MHZ_SPACED(chan) || IS_CHAN_A_5MHZ_SPACED(ah->curchan))) { - if (ath9k_hw_channel_change(ah, chan, sc->tx_chan_width)) { + if (ath9k_hw_channel_change(ah, chan)) { ath9k_hw_loadnf(ah, ah->curchan); ath9k_hw_start_nfcal(ah); return 0; @@ -2400,7 +1999,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } if (!ath9k_hw_chip_reset(ah, chan)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Chip reset failed\n"); + ath_print(common, ATH_DBG_FATAL, "Chip reset failed\n"); return -EINVAL; } @@ -2429,7 +2028,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); } - r = ath9k_hw_process_ini(ah, chan, sc->tx_chan_width); + r = ath9k_hw_process_ini(ah, chan); if (r) return r; @@ -2453,17 +2052,11 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) ath9k_hw_set_delta_slope(ah, chan); - if (AR_SREV_9280_10_OR_LATER(ah)) - ath9k_hw_9280_spur_mitigate(ah, chan); - else - ath9k_hw_spur_mitigate(ah, chan); - + ah->ath9k_hw_spur_mitigate_freq(ah, chan); ah->eep_ops->set_board_values(ah, chan); - ath9k_hw_decrease_chain_power(ah, chan); - - REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(ah->macaddr)); - REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(ah->macaddr + 4) + REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(common->macaddr)); + REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(common->macaddr + 4) | macStaId1 | AR_STA_ID1_RTS_USE_DEF | (ah->config. @@ -2471,24 +2064,19 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, | ah->sta_id1_defaults); ath9k_hw_set_operating_mode(ah, ah->opmode); - REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(sc->bssidmask)); - REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(sc->bssidmask + 4)); + ath_hw_setbssidmask(common); REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); - REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(sc->curbssid)); - REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(sc->curbssid + 4) | - ((sc->curaid & 0x3fff) << AR_BSS_ID1_AID_S)); + ath9k_hw_write_associd(ah); REG_WRITE(ah, AR_ISR, ~0); REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); - if (AR_SREV_9280_10_OR_LATER(ah)) - ath9k_hw_ar9280_set_channel(ah, chan); - else - if (!(ath9k_hw_set_channel(ah, chan))) - return -EIO; + r = ah->ath9k_hw_rf_set_freq(ah, chan); + if (r) + return r; for (i = 0; i < AR_NUM_DCU; i++) REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); @@ -2558,13 +2146,13 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, u32 mask; mask = REG_READ(ah, AR_CFG); if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) { - DPRINTF(ah->ah_sc, ATH_DBG_RESET, + ath_print(common, ATH_DBG_RESET, "CFG Byte Swap Set 0x%x\n", mask); } else { mask = INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB; REG_WRITE(ah, AR_CFG, mask); - DPRINTF(ah->ah_sc, ATH_DBG_RESET, + ath_print(common, ATH_DBG_RESET, "Setting CFG 0x%x\n", REG_READ(ah, AR_CFG)); } } else { @@ -2577,11 +2165,12 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, #endif } - if (ah->ah_sc->sc_flags & SC_OP_BTCOEX_ENABLED) + if (ah->btcoex_hw.enabled) ath9k_hw_btcoex_enable(ah); return 0; } +EXPORT_SYMBOL(ath9k_hw_reset); /************************/ /* Key Cache Management */ @@ -2592,8 +2181,8 @@ bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry) u32 keyType; if (entry >= ah->caps.keycache_size) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "keychache entry %u out of range\n", entry); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "keychache entry %u out of range\n", entry); return false; } @@ -2620,14 +2209,15 @@ bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry) return true; } +EXPORT_SYMBOL(ath9k_hw_keyreset); bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac) { u32 macHi, macLo; if (entry >= ah->caps.keycache_size) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "keychache entry %u out of range\n", entry); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "keychache entry %u out of range\n", entry); return false; } @@ -2648,18 +2238,20 @@ bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac) return true; } +EXPORT_SYMBOL(ath9k_hw_keysetmac); bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, const struct ath9k_keyval *k, const u8 *mac) { const struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath_common *common = ath9k_hw_common(ah); u32 key0, key1, key2, key3, key4; u32 keyType; if (entry >= pCap->keycache_size) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "keycache entry %u out of range\n", entry); + ath_print(common, ATH_DBG_FATAL, + "keycache entry %u out of range\n", entry); return false; } @@ -2669,9 +2261,9 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, break; case ATH9K_CIPHER_AES_CCM: if (!(pCap->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) { - DPRINTF(ah->ah_sc, ATH_DBG_ANY, - "AES-CCM not supported by mac rev 0x%x\n", - ah->hw_version.macRev); + ath_print(common, ATH_DBG_ANY, + "AES-CCM not supported by mac rev 0x%x\n", + ah->hw_version.macRev); return false; } keyType = AR_KEYTABLE_TYPE_CCM; @@ -2680,15 +2272,15 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, keyType = AR_KEYTABLE_TYPE_TKIP; if (ATH9K_IS_MIC_ENABLED(ah) && entry + 64 >= pCap->keycache_size) { - DPRINTF(ah->ah_sc, ATH_DBG_ANY, - "entry %u inappropriate for TKIP\n", entry); + ath_print(common, ATH_DBG_ANY, + "entry %u inappropriate for TKIP\n", entry); return false; } break; case ATH9K_CIPHER_WEP: if (k->kv_len < WLAN_KEY_LEN_WEP40) { - DPRINTF(ah->ah_sc, ATH_DBG_ANY, - "WEP key length %u too small\n", k->kv_len); + ath_print(common, ATH_DBG_ANY, + "WEP key length %u too small\n", k->kv_len); return false; } if (k->kv_len <= WLAN_KEY_LEN_WEP40) @@ -2702,8 +2294,8 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, keyType = AR_KEYTABLE_TYPE_CLR; break; default: - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "cipher %u not supported\n", k->kv_type); + ath_print(common, ATH_DBG_FATAL, + "cipher %u not supported\n", k->kv_type); return false; } @@ -2845,6 +2437,7 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, return true; } +EXPORT_SYMBOL(ath9k_hw_set_keycache_entry); bool ath9k_hw_keyisvalid(struct ath_hw *ah, u16 entry) { @@ -2855,6 +2448,7 @@ bool ath9k_hw_keyisvalid(struct ath_hw *ah, u16 entry) } return false; } +EXPORT_SYMBOL(ath9k_hw_keyisvalid); /******************************/ /* Power Management (Chipset) */ @@ -2869,8 +2463,9 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip) if (!AR_SREV_9100(ah)) REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); - REG_CLR_BIT(ah, (AR_RTC_RESET), - AR_RTC_RESET_EN); + if(!AR_SREV_5416(ah)) + REG_CLR_BIT(ah, (AR_RTC_RESET), + AR_RTC_RESET_EN); } } @@ -2902,6 +2497,7 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip) ATH9K_RESET_POWER_ON) != true) { return false; } + ath9k_hw_init_pll(ah, NULL); } if (AR_SREV_9100(ah)) REG_SET_BIT(ah, AR_RTC_RESET, @@ -2920,8 +2516,9 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip) AR_RTC_FORCE_WAKE_EN); } if (i == 0) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Failed to wakeup in %uus\n", POWER_UP_TIME / 20); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "Failed to wakeup in %uus\n", + POWER_UP_TIME / 20); return false; } } @@ -2931,9 +2528,9 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip) return true; } -static bool ath9k_hw_setpower_nolock(struct ath_hw *ah, - enum ath9k_power_mode mode) +bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) { + struct ath_common *common = ath9k_hw_common(ah); int status = true, setChip = true; static const char *modes[] = { "AWAKE", @@ -2945,8 +2542,8 @@ static bool ath9k_hw_setpower_nolock(struct ath_hw *ah, if (ah->power_mode == mode) return status; - DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s -> %s\n", - modes[ah->power_mode], modes[mode]); + ath_print(common, ATH_DBG_RESET, "%s -> %s\n", + modes[ah->power_mode], modes[mode]); switch (mode) { case ATH9K_PM_AWAKE: @@ -2960,59 +2557,15 @@ static bool ath9k_hw_setpower_nolock(struct ath_hw *ah, ath9k_set_power_network_sleep(ah, setChip); break; default: - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Unknown power mode %u\n", mode); + ath_print(common, ATH_DBG_FATAL, + "Unknown power mode %u\n", mode); return false; } ah->power_mode = mode; return status; } - -bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) -{ - unsigned long flags; - bool ret; - - spin_lock_irqsave(&ah->ah_sc->sc_pm_lock, flags); - ret = ath9k_hw_setpower_nolock(ah, mode); - spin_unlock_irqrestore(&ah->ah_sc->sc_pm_lock, flags); - - return ret; -} - -void ath9k_ps_wakeup(struct ath_softc *sc) -{ - unsigned long flags; - - spin_lock_irqsave(&sc->sc_pm_lock, flags); - if (++sc->ps_usecount != 1) - goto unlock; - - ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_AWAKE); - - unlock: - spin_unlock_irqrestore(&sc->sc_pm_lock, flags); -} - -void ath9k_ps_restore(struct ath_softc *sc) -{ - unsigned long flags; - - spin_lock_irqsave(&sc->sc_pm_lock, flags); - if (--sc->ps_usecount != 0) - goto unlock; - - if (sc->ps_enabled && - !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK))) - ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); - - unlock: - spin_unlock_irqrestore(&sc->sc_pm_lock, flags); -} +EXPORT_SYMBOL(ath9k_hw_setpower); /* * Helper for ASPM support. @@ -3145,6 +2698,7 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off) } } } +EXPORT_SYMBOL(ath9k_hw_configpcipowersave); /**********************/ /* Interrupt Handling */ @@ -3168,6 +2722,7 @@ bool ath9k_hw_intrpend(struct ath_hw *ah) return false; } +EXPORT_SYMBOL(ath9k_hw_intrpend); bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) { @@ -3176,6 +2731,7 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) struct ath9k_hw_capabilities *pCap = &ah->caps; u32 sync_cause = 0; bool fatal_int = false; + struct ath_common *common = ath9k_hw_common(ah); if (!AR_SREV_9100(ah)) { if (REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) { @@ -3249,8 +2805,8 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) } if (isr & AR_ISR_RXORN) { - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, - "receive FIFO overrun interrupt\n"); + ath_print(common, ATH_DBG_INTERRUPT, + "receive FIFO overrun interrupt\n"); } if (!AR_SREV_9100(ah)) { @@ -3292,25 +2848,25 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) if (fatal_int) { if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) { - DPRINTF(ah->ah_sc, ATH_DBG_ANY, - "received PCI FATAL interrupt\n"); + ath_print(common, ATH_DBG_ANY, + "received PCI FATAL interrupt\n"); } if (sync_cause & AR_INTR_SYNC_HOST1_PERR) { - DPRINTF(ah->ah_sc, ATH_DBG_ANY, - "received PCI PERR interrupt\n"); + ath_print(common, ATH_DBG_ANY, + "received PCI PERR interrupt\n"); } *masked |= ATH9K_INT_FATAL; } if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, - "AR_INTR_SYNC_RADM_CPL_TIMEOUT\n"); + ath_print(common, ATH_DBG_INTERRUPT, + "AR_INTR_SYNC_RADM_CPL_TIMEOUT\n"); REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); REG_WRITE(ah, AR_RC, 0); *masked |= ATH9K_INT_FATAL; } if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) { - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, - "AR_INTR_SYNC_LOCAL_TIMEOUT\n"); + ath_print(common, ATH_DBG_INTERRUPT, + "AR_INTR_SYNC_LOCAL_TIMEOUT\n"); } REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); @@ -3319,17 +2875,19 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) return true; } +EXPORT_SYMBOL(ath9k_hw_getisr); enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) { u32 omask = ah->mask_reg; u32 mask, mask2; struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath_common *common = ath9k_hw_common(ah); - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints); + ath_print(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints); if (omask & ATH9K_INT_GLOBAL) { - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "disable IER\n"); + ath_print(common, ATH_DBG_INTERRUPT, "disable IER\n"); REG_WRITE(ah, AR_IER, AR_IER_DISABLE); (void) REG_READ(ah, AR_IER); if (!AR_SREV_9100(ah)) { @@ -3386,7 +2944,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) mask2 |= AR_IMR_S2_CST; } - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "new IMR 0x%x\n", mask); + ath_print(common, ATH_DBG_INTERRUPT, "new IMR 0x%x\n", mask); REG_WRITE(ah, AR_IMR, mask); mask = REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | @@ -3406,7 +2964,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) } if (ints & ATH9K_INT_GLOBAL) { - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "enable IER\n"); + ath_print(common, ATH_DBG_INTERRUPT, "enable IER\n"); REG_WRITE(ah, AR_IER, AR_IER_ENABLE); if (!AR_SREV_9100(ah)) { REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, @@ -3419,12 +2977,13 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) REG_WRITE(ah, AR_INTR_SYNC_MASK, AR_INTR_SYNC_DEFAULT); } - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", - REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER)); + ath_print(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", + REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER)); } return omask; } +EXPORT_SYMBOL(ath9k_hw_set_interrupts); /*******************/ /* Beacon Handling */ @@ -3467,9 +3026,9 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN; break; default: - DPRINTF(ah->ah_sc, ATH_DBG_BEACON, - "%s: unsupported opmode: %d\n", - __func__, ah->opmode); + ath_print(ath9k_hw_common(ah), ATH_DBG_BEACON, + "%s: unsupported opmode: %d\n", + __func__, ah->opmode); return; break; } @@ -3481,18 +3040,19 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) beacon_period &= ~ATH9K_BEACON_ENA; if (beacon_period & ATH9K_BEACON_RESET_TSF) { - beacon_period &= ~ATH9K_BEACON_RESET_TSF; ath9k_hw_reset_tsf(ah); } REG_SET_BIT(ah, AR_TIMER_MODE, flags); } +EXPORT_SYMBOL(ath9k_hw_beaconinit); void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs) { u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath_common *common = ath9k_hw_common(ah); REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt)); @@ -3518,10 +3078,10 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, else nextTbtt = bs->bs_nexttbtt; - DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "next DTIM %d\n", bs->bs_nextdtim); - DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "next beacon %d\n", nextTbtt); - DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "beacon period %d\n", beaconintval); - DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "DTIM period %d\n", dtimperiod); + ath_print(common, ATH_DBG_BEACON, "next DTIM %d\n", bs->bs_nextdtim); + ath_print(common, ATH_DBG_BEACON, "next beacon %d\n", nextTbtt); + ath_print(common, ATH_DBG_BEACON, "beacon period %d\n", beaconintval); + ath_print(common, ATH_DBG_BEACON, "DTIM period %d\n", dtimperiod); REG_WRITE(ah, AR_NEXT_DTIM, TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); @@ -3549,16 +3109,18 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, /* TSF Out of Range Threshold */ REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold); } +EXPORT_SYMBOL(ath9k_hw_set_sta_beacon_timers); /*******************/ /* HW Capabilities */ /*******************/ -void ath9k_hw_fill_cap_info(struct ath_hw *ah) +int ath9k_hw_fill_cap_info(struct ath_hw *ah) { struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); - struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; u16 capField = 0, eeval; @@ -3579,11 +3141,17 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) regulatory->current_rd += 5; else if (regulatory->current_rd == 0x41) regulatory->current_rd = 0x43; - DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, - "regdomain mapped to 0x%x\n", regulatory->current_rd); + ath_print(common, ATH_DBG_REGULATORY, + "regdomain mapped to 0x%x\n", regulatory->current_rd); } eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE); + if ((eeval & (AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A)) == 0) { + ath_print(common, ATH_DBG_FATAL, + "no band has been marked as supported in EEPROM.\n"); + return -EINVAL; + } + bitmap_zero(pCap->wireless_modes, ATH9K_MODE_MAX); if (eeval & AR5416_OPFLAGS_11A) { @@ -3670,7 +3238,11 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->keycache_size = AR_KEYTABLE_SIZE; pCap->hw_caps |= ATH9K_HW_CAP_FASTCC; - pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD; + + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) + pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD >> 1; + else + pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD; if (AR_SREV_9285_10_OR_LATER(ah)) pCap->num_gpio_pins = AR9285_NUM_GPIO; @@ -3719,7 +3291,10 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN; } - pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; + /* Advertise midband for AR5416 with FCC midband set in eeprom */ + if (regulatory->current_rd_ext & (1 << REG_EXT_FCC_MIDBAND) && + AR_SREV_5416(ah)) + pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; pCap->num_antcfg_5ghz = ah->eep_ops->get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_5GHZ); @@ -3727,19 +3302,21 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) ah->eep_ops->get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_2GHZ); if (AR_SREV_9280_10_OR_LATER(ah) && - ath_btcoex_supported(ah->hw_version.subsysid)) { - btcoex_info->btactive_gpio = ATH_BTACTIVE_GPIO; - btcoex_info->wlanactive_gpio = ATH_WLANACTIVE_GPIO; + ath9k_hw_btcoex_supported(ah)) { + btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO; + btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO; if (AR_SREV_9285(ah)) { - btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_3WIRE; - btcoex_info->btpriority_gpio = ATH_BTPRIORITY_GPIO; + btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; + btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO; } else { - btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_2WIRE; + btcoex_hw->scheme = ATH_BTCOEX_CFG_2WIRE; } } else { - btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_NONE; + btcoex_hw->scheme = ATH_BTCOEX_CFG_NONE; } + + return 0; } bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type, @@ -3812,6 +3389,7 @@ bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type, return false; } } +EXPORT_SYMBOL(ath9k_hw_getcapability); bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type, u32 capability, u32 setting, int *status) @@ -3845,6 +3423,7 @@ bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type, return false; } } +EXPORT_SYMBOL(ath9k_hw_setcapability); /****************************/ /* GPIO / RFKILL / Antennae */ @@ -3882,7 +3461,7 @@ void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio) { u32 gpio_shift; - ASSERT(gpio < ah->caps.num_gpio_pins); + BUG_ON(gpio >= ah->caps.num_gpio_pins); gpio_shift = gpio << 1; @@ -3891,6 +3470,7 @@ void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio) (AR_GPIO_OE_OUT_DRV_NO << gpio_shift), (AR_GPIO_OE_OUT_DRV << gpio_shift)); } +EXPORT_SYMBOL(ath9k_hw_cfg_gpio_input); u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) { @@ -3909,6 +3489,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) else return MS_REG_READ(AR, gpio) != 0; } +EXPORT_SYMBOL(ath9k_hw_gpio_get); void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, u32 ah_signal_type) @@ -3924,67 +3505,26 @@ void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, (AR_GPIO_OE_OUT_DRV_ALL << gpio_shift), (AR_GPIO_OE_OUT_DRV << gpio_shift)); } +EXPORT_SYMBOL(ath9k_hw_cfg_output); void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) { REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio), AR_GPIO_BIT(gpio)); } +EXPORT_SYMBOL(ath9k_hw_set_gpio); u32 ath9k_hw_getdefantenna(struct ath_hw *ah) { return REG_READ(ah, AR_DEF_ANTENNA) & 0x7; } +EXPORT_SYMBOL(ath9k_hw_getdefantenna); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna) { REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7)); } - -bool ath9k_hw_setantennaswitch(struct ath_hw *ah, - enum ath9k_ant_setting settings, - struct ath9k_channel *chan, - u8 *tx_chainmask, - u8 *rx_chainmask, - u8 *antenna_cfgd) -{ - static u8 tx_chainmask_cfg, rx_chainmask_cfg; - - if (AR_SREV_9280(ah)) { - if (!tx_chainmask_cfg) { - - tx_chainmask_cfg = *tx_chainmask; - rx_chainmask_cfg = *rx_chainmask; - } - - switch (settings) { - case ATH9K_ANT_FIXED_A: - *tx_chainmask = ATH9K_ANTENNA0_CHAINMASK; - *rx_chainmask = ATH9K_ANTENNA0_CHAINMASK; - *antenna_cfgd = true; - break; - case ATH9K_ANT_FIXED_B: - if (ah->caps.tx_chainmask > - ATH9K_ANTENNA1_CHAINMASK) { - *tx_chainmask = ATH9K_ANTENNA1_CHAINMASK; - } - *rx_chainmask = ATH9K_ANTENNA1_CHAINMASK; - *antenna_cfgd = true; - break; - case ATH9K_ANT_VARIABLE: - *tx_chainmask = tx_chainmask_cfg; - *rx_chainmask = rx_chainmask_cfg; - *antenna_cfgd = true; - break; - default: - break; - } - } else { - ah->config.diversity_control = settings; - } - - return true; -} +EXPORT_SYMBOL(ath9k_hw_setantenna); /*********************/ /* General Operation */ @@ -4002,6 +3542,7 @@ u32 ath9k_hw_getrxfilter(struct ath_hw *ah) return bits; } +EXPORT_SYMBOL(ath9k_hw_getrxfilter); void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits) { @@ -4023,19 +3564,30 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits) REG_WRITE(ah, AR_RXCFG, REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_ZLFDMA); } +EXPORT_SYMBOL(ath9k_hw_setrxfilter); bool ath9k_hw_phy_disable(struct ath_hw *ah) { - return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM); + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) + return false; + + ath9k_hw_init_pll(ah, NULL); + return true; } +EXPORT_SYMBOL(ath9k_hw_phy_disable); bool ath9k_hw_disable(struct ath_hw *ah) { if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return false; - return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD); + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD)) + return false; + + ath9k_hw_init_pll(ah, NULL); + return true; } +EXPORT_SYMBOL(ath9k_hw_disable); void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit) { @@ -4052,35 +3604,36 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit) min((u32) MAX_RATE_POWER, (u32) regulatory->power_limit)); } +EXPORT_SYMBOL(ath9k_hw_set_txpowerlimit); void ath9k_hw_setmac(struct ath_hw *ah, const u8 *mac) { - memcpy(ah->macaddr, mac, ETH_ALEN); + memcpy(ath9k_hw_common(ah)->macaddr, mac, ETH_ALEN); } +EXPORT_SYMBOL(ath9k_hw_setmac); void ath9k_hw_setopmode(struct ath_hw *ah) { ath9k_hw_set_operating_mode(ah, ah->opmode); } +EXPORT_SYMBOL(ath9k_hw_setopmode); void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1) { REG_WRITE(ah, AR_MCAST_FIL0, filter0); REG_WRITE(ah, AR_MCAST_FIL1, filter1); } +EXPORT_SYMBOL(ath9k_hw_setmcastfilter); -void ath9k_hw_setbssidmask(struct ath_softc *sc) +void ath9k_hw_write_associd(struct ath_hw *ah) { - REG_WRITE(sc->sc_ah, AR_BSSMSKL, get_unaligned_le32(sc->bssidmask)); - REG_WRITE(sc->sc_ah, AR_BSSMSKU, get_unaligned_le16(sc->bssidmask + 4)); -} + struct ath_common *common = ath9k_hw_common(ah); -void ath9k_hw_write_associd(struct ath_softc *sc) -{ - REG_WRITE(sc->sc_ah, AR_BSS_ID0, get_unaligned_le32(sc->curbssid)); - REG_WRITE(sc->sc_ah, AR_BSS_ID1, get_unaligned_le16(sc->curbssid + 4) | - ((sc->curaid & 0x3fff) << AR_BSS_ID1_AID_S)); + REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(common->curbssid)); + REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(common->curbssid + 4) | + ((common->curaid & 0x3fff) << AR_BSS_ID1_AID_S)); } +EXPORT_SYMBOL(ath9k_hw_write_associd); u64 ath9k_hw_gettsf64(struct ath_hw *ah) { @@ -4091,24 +3644,25 @@ u64 ath9k_hw_gettsf64(struct ath_hw *ah) return tsf; } +EXPORT_SYMBOL(ath9k_hw_gettsf64); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64) { REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff); REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff); } +EXPORT_SYMBOL(ath9k_hw_settsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah) { - ath9k_ps_wakeup(ah->ah_sc); if (!ath9k_hw_wait(ah, AR_SLP32_MODE, AR_SLP32_TSF_WRITE_STATUS, 0, AH_TSF_WRITE_TIMEOUT)) - DPRINTF(ah->ah_sc, ATH_DBG_RESET, - "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n"); + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, + "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n"); REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); - ath9k_ps_restore(ah->ah_sc); } +EXPORT_SYMBOL(ath9k_hw_reset_tsf); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting) { @@ -4117,11 +3671,28 @@ void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting) else ah->misc_mode &= ~AR_PCU_TX_ADD_TSF; } +EXPORT_SYMBOL(ath9k_hw_set_tsfadjust); + +/* + * Extend 15-bit time stamp from rx descriptor to + * a full 64-bit TSF using the current h/w TSF. +*/ +u64 ath9k_hw_extend_tsf(struct ath_hw *ah, u32 rstamp) +{ + u64 tsf; + + tsf = ath9k_hw_gettsf64(ah); + if ((tsf & 0x7fff) < rstamp) + tsf -= 0x8000; + return (tsf & ~0x7fff) | rstamp; +} +EXPORT_SYMBOL(ath9k_hw_extend_tsf); bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us) { if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) { - DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad slot time %u\n", us); + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, + "bad slot time %u\n", us); ah->slottime = (u32) -1; return false; } else { @@ -4130,13 +3701,14 @@ bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us) return true; } } +EXPORT_SYMBOL(ath9k_hw_setslottime); -void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode) +void ath9k_hw_set11nmac2040(struct ath_hw *ah) { + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; u32 macmode; - if (mode == ATH9K_HT_MACMODE_2040 && - !ah->config.cwm_ignore_extcca) + if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca) macmode = AR_2040_JOINED_RX_CLEAR; else macmode = 0; @@ -4193,6 +3765,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah) { return REG_READ(ah, AR_TSF_L32); } +EXPORT_SYMBOL(ath9k_hw_gettsf32); struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, void (*trigger)(void *), @@ -4206,8 +3779,9 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL); if (timer == NULL) { - printk(KERN_DEBUG "Failed to allocate memory" - "for hw timer[%d]\n", timer_index); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "Failed to allocate memory" + "for hw timer[%d]\n", timer_index); return NULL; } @@ -4220,10 +3794,12 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, return timer; } +EXPORT_SYMBOL(ath_gen_timer_alloc); -void ath_gen_timer_start(struct ath_hw *ah, - struct ath_gen_timer *timer, - u32 timer_next, u32 timer_period) +void ath9k_hw_gen_timer_start(struct ath_hw *ah, + struct ath_gen_timer *timer, + u32 timer_next, + u32 timer_period) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; u32 tsf; @@ -4234,8 +3810,9 @@ void ath_gen_timer_start(struct ath_hw *ah, tsf = ath9k_hw_gettsf32(ah); - DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, "curent tsf %x period %x" - "timer_next %x\n", tsf, timer_period, timer_next); + ath_print(ath9k_hw_common(ah), ATH_DBG_HWTIMER, + "curent tsf %x period %x" + "timer_next %x\n", tsf, timer_period, timer_next); /* * Pull timer_next forward if the current TSF already passed it @@ -4258,15 +3835,10 @@ void ath_gen_timer_start(struct ath_hw *ah, REG_SET_BIT(ah, AR_IMR_S5, (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); - - if ((ah->ah_sc->imask & ATH9K_INT_GENTIMER) == 0) { - ath9k_hw_set_interrupts(ah, 0); - ah->ah_sc->imask |= ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah, ah->ah_sc->imask); - } } +EXPORT_SYMBOL(ath9k_hw_gen_timer_start); -void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) +void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; @@ -4285,14 +3857,8 @@ void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); clear_bit(timer->index, &timer_table->timer_mask.timer_bits); - - /* if no timer is enabled, turn off interrupt mask */ - if (timer_table->timer_mask.val == 0) { - ath9k_hw_set_interrupts(ah, 0); - ah->ah_sc->imask &= ~ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah, ah->ah_sc->imask); - } } +EXPORT_SYMBOL(ath9k_hw_gen_timer_stop); void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer) { @@ -4302,6 +3868,7 @@ void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer) timer_table->timers[timer->index] = NULL; kfree(timer); } +EXPORT_SYMBOL(ath_gen_timer_free); /* * Generic Timer Interrupts handling @@ -4310,6 +3877,7 @@ void ath_gen_timer_isr(struct ath_hw *ah) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer *timer; + struct ath_common *common = ath9k_hw_common(ah); u32 trigger_mask, thresh_mask, index; /* get hardware generic timer interrupt status */ @@ -4324,8 +3892,8 @@ void ath_gen_timer_isr(struct ath_hw *ah) index = rightmost_index(timer_table, &thresh_mask); timer = timer_table->timers[index]; BUG_ON(!timer); - DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, - "TSF overflow for Gen timer %d\n", index); + ath_print(common, ATH_DBG_HWTIMER, + "TSF overflow for Gen timer %d\n", index); timer->overflow(timer->arg); } @@ -4333,21 +3901,95 @@ void ath_gen_timer_isr(struct ath_hw *ah) index = rightmost_index(timer_table, &trigger_mask); timer = timer_table->timers[index]; BUG_ON(!timer); - DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, - "Gen timer[%d] trigger\n", index); + ath_print(common, ATH_DBG_HWTIMER, + "Gen timer[%d] trigger\n", index); timer->trigger(timer->arg); } } +EXPORT_SYMBOL(ath_gen_timer_isr); + +static struct { + u32 version; + const char * name; +} ath_mac_bb_names[] = { + /* Devices with external radios */ + { AR_SREV_VERSION_5416_PCI, "5416" }, + { AR_SREV_VERSION_5416_PCIE, "5418" }, + { AR_SREV_VERSION_9100, "9100" }, + { AR_SREV_VERSION_9160, "9160" }, + /* Single-chip solutions */ + { AR_SREV_VERSION_9280, "9280" }, + { AR_SREV_VERSION_9285, "9285" }, + { AR_SREV_VERSION_9287, "9287" }, + { AR_SREV_VERSION_9271, "9271" }, +}; + +/* For devices with external radios */ +static struct { + u16 version; + const char * name; +} ath_rf_names[] = { + { 0, "5133" }, + { AR_RAD5133_SREV_MAJOR, "5133" }, + { AR_RAD5122_SREV_MAJOR, "5122" }, + { AR_RAD2133_SREV_MAJOR, "2133" }, + { AR_RAD2122_SREV_MAJOR, "2122" } +}; + +/* + * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown. + */ +static const char *ath9k_hw_mac_bb_name(u32 mac_bb_version) +{ + int i; + + for (i=0; i<ARRAY_SIZE(ath_mac_bb_names); i++) { + if (ath_mac_bb_names[i].version == mac_bb_version) { + return ath_mac_bb_names[i].name; + } + } + + return "????"; +} /* - * Primitive to disable ASPM + * Return the RF name. "????" is returned if the RF is unknown. + * Used for devices with external radios. */ -void ath_pcie_aspm_disable(struct ath_softc *sc) +static const char *ath9k_hw_rf_name(u16 rf_version) +{ + int i; + + for (i=0; i<ARRAY_SIZE(ath_rf_names); i++) { + if (ath_rf_names[i].version == rf_version) { + return ath_rf_names[i].name; + } + } + + return "????"; +} + +void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len) { - struct pci_dev *pdev = to_pci_dev(sc->dev); - u8 aspm; + int used; + + /* chipsets >= AR9280 are single-chip */ + if (AR_SREV_9280_10_OR_LATER(ah)) { + used = snprintf(hw_name, len, + "Atheros AR%s Rev:%x", + ath9k_hw_mac_bb_name(ah->hw_version.macVersion), + ah->hw_version.macRev); + } + else { + used = snprintf(hw_name, len, + "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x", + ath9k_hw_mac_bb_name(ah->hw_version.macVersion), + ah->hw_version.macRev, + ath9k_hw_rf_name((ah->hw_version.analog5GhzRev & + AR_RADIO_SREV_MAJOR)), + ah->hw_version.phyRev); + } - pci_read_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, &aspm); - aspm &= ~(ATH_PCIE_CAP_LINK_L0S | ATH_PCIE_CAP_LINK_L1); - pci_write_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, aspm); + hw_name[used] = '\0'; } +EXPORT_SYMBOL(ath9k_hw_name); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index b89234571829..e2b0c73a616f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -27,17 +27,24 @@ #include "calib.h" #include "reg.h" #include "phy.h" +#include "btcoex.h" #include "../regd.h" +#include "../debug.h" #define ATHEROS_VENDOR_ID 0x168c + #define AR5416_DEVID_PCI 0x0023 #define AR5416_DEVID_PCIE 0x0024 #define AR9160_DEVID_PCI 0x0027 #define AR9280_DEVID_PCI 0x0029 #define AR9280_DEVID_PCIE 0x002a #define AR9285_DEVID_PCIE 0x002b + #define AR5416_AR9100_DEVID 0x000b + +#define AR9271_USB 0x9271 + #define AR_SUBVENDOR_ID_NOG 0x0e11 #define AR_SUBVENDOR_ID_NEW_A 0x7065 #define AR5416_MAGIC 0x19641014 @@ -49,9 +56,18 @@ #define AT9285_COEX3WIRE_SA_SUBSYSID 0x30aa #define AT9285_COEX3WIRE_DA_SUBSYSID 0x30ab +#define ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1) + +#define ATH_DEFAULT_NOISE_FLOOR -95 + +#define ATH9K_RSSI_BAD -128 + /* Register read/write primitives */ -#define REG_WRITE(_ah, _reg, _val) ath9k_iowrite32((_ah), (_reg), (_val)) -#define REG_READ(_ah, _reg) ath9k_ioread32((_ah), (_reg)) +#define REG_WRITE(_ah, _reg, _val) \ + ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg)) + +#define REG_READ(_ah, _reg) \ + ath9k_hw_common(_ah)->ops->read((_ah), (_reg)) #define SM(_v, _f) (((_v) << _f##_S) & _f) #define MS(_v, _f) (((_v) & _f) >> _f##_S) @@ -91,7 +107,7 @@ #define AR_GPIO_BIT(_gpio) (1 << (_gpio)) #define BASE_ACTIVATE_DELAY 100 -#define RTC_PLL_SETTLE_DELAY 1000 +#define RTC_PLL_SETTLE_DELAY 100 #define COEF_SCALE_S 24 #define HT40_CHANNEL_CENTER_SHIFT 10 @@ -132,12 +148,6 @@ enum wireless_mode { ATH9K_MODE_MAX, }; -enum ath9k_ant_setting { - ATH9K_ANT_VARIABLE = 0, - ATH9K_ANT_FIXED_A, - ATH9K_ANT_FIXED_B -}; - enum ath9k_hw_caps { ATH9K_HW_CAP_MIC_AESCCM = BIT(0), ATH9K_HW_CAP_MIC_CKIP = BIT(1), @@ -201,8 +211,6 @@ struct ath9k_ops_config { u32 cck_trig_high; u32 cck_trig_low; u32 enable_ani; - enum ath9k_ant_setting diversity_control; - u16 antenna_switch_swap; int serialize_regmode; bool intr_mitigation; #define SPUR_DISABLE 0 @@ -218,6 +226,7 @@ struct ath9k_ops_config { #define AR_SPUR_FEEQ_BOUND_HT20 10 int spurmode; u16 spurchans[AR_EEPROM_MODAL_SPURS][2]; + u8 max_txtrig_level; }; enum ath9k_int { @@ -407,7 +416,7 @@ struct ath9k_hw_version { * Using de Bruijin sequence to to look up 1's index in a 32 bit number * debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001 */ -#define debruijn32 0x077CB531UL +#define debruijn32 0x077CB531U struct ath_gen_timer_configuration { u32 next_addr; @@ -433,7 +442,8 @@ struct ath_gen_timer_table { }; struct ath_hw { - struct ath_softc *ah_sc; + struct ieee80211_hw *hw; + struct ath_common common; struct ath9k_hw_version hw_version; struct ath9k_ops_config config; struct ath9k_hw_capabilities caps; @@ -450,7 +460,6 @@ struct ath_hw { bool sw_mgmt_crypto; bool is_pciexpress; - u8 macaddr[ETH_ALEN]; u16 tx_trig_level; u16 rfsilent; u32 rfkill_gpio; @@ -523,7 +532,14 @@ struct ath_hw { DONT_USE_32KHZ, } enable_32kHz_clock; - /* RF */ + /* Callback for radio frequency change */ + int (*ath9k_hw_rf_set_freq)(struct ath_hw *ah, struct ath9k_channel *chan); + + /* Callback for baseband spur frequency */ + void (*ath9k_hw_spur_mitigate_freq)(struct ath_hw *ah, + struct ath9k_channel *chan); + + /* Used to program the radio on non single-chip devices */ u32 *analogBank0Data; u32 *analogBank1Data; u32 *analogBank2Data; @@ -540,7 +556,6 @@ struct ath_hw { u32 acktimeout; u32 ctstimeout; u32 globaltxtimeout; - u8 gbeacon_rate; /* ANI */ u32 proc_phyerr; @@ -553,8 +568,10 @@ struct ath_hw { int firpwr[5]; enum ath9k_ani_cmd ani_function; + /* Bluetooth coexistance */ + struct ath_btcoex_hw btcoex_hw; + u32 intr_txqs; - enum ath9k_ht_extprotspacing extprotspacing; u8 txchainmask; u8 rxchainmask; @@ -578,20 +595,32 @@ struct ath_hw { struct ar5416IniArray iniModesAdditional; struct ar5416IniArray iniModesRxGain; struct ar5416IniArray iniModesTxGain; + struct ar5416IniArray iniModes_9271_1_0_only; + struct ar5416IniArray iniCckfirNormal; + struct ar5416IniArray iniCckfirJapan2484; u32 intr_gen_timer_trigger; u32 intr_gen_timer_thresh; struct ath_gen_timer_table hw_gen_timers; }; +static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah) +{ + return &ah->common; +} + +static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah) +{ + return &(ath9k_hw_common(ah)->regulatory); +} + /* Initialization, Detach, Reset */ const char *ath9k_hw_probe(u16 vendorid, u16 devid); void ath9k_hw_detach(struct ath_hw *ah); int ath9k_hw_init(struct ath_hw *ah); -void ath9k_hw_rf_free(struct ath_hw *ah); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, bool bChannelChange); -void ath9k_hw_fill_cap_info(struct ath_hw *ah); +int ath9k_hw_fill_cap_info(struct ath_hw *ah); bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type, u32 capability, u32 *result); bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type, @@ -613,18 +642,13 @@ void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); u32 ath9k_hw_getdefantenna(struct ath_hw *ah); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); -bool ath9k_hw_setantennaswitch(struct ath_hw *ah, - enum ath9k_ant_setting settings, - struct ath9k_channel *chan, - u8 *tx_chainmask, u8 *rx_chainmask, - u8 *antenna_cfgd); /* General Operation */ bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); u32 ath9k_hw_reverse_bits(u32 val, u32 n); bool ath9k_get_channel_edges(struct ath_hw *ah, u16 flags, u16 *low, u16 *high); u16 ath9k_hw_computetxtime(struct ath_hw *ah, - const struct ath_rate_table *rates, + u8 phy, int kbps, u32 frameLen, u16 rateix, bool shortPreamble); void ath9k_hw_get_channel_centers(struct ath_hw *ah, struct ath9k_channel *chan, @@ -637,19 +661,21 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit); void ath9k_hw_setmac(struct ath_hw *ah, const u8 *mac); void ath9k_hw_setopmode(struct ath_hw *ah); void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1); -void ath9k_hw_setbssidmask(struct ath_softc *sc); -void ath9k_hw_write_associd(struct ath_softc *sc); +void ath9k_hw_setbssidmask(struct ath_hw *ah); +void ath9k_hw_write_associd(struct ath_hw *ah); u64 ath9k_hw_gettsf64(struct ath_hw *ah); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting); +u64 ath9k_hw_extend_tsf(struct ath_hw *ah, u32 rstamp); bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us); -void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode); +void ath9k_hw_set11nmac2040(struct ath_hw *ah); void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period); void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs); -bool ath9k_hw_setpower(struct ath_hw *ah, - enum ath9k_power_mode mode); + +bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); + void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off); /* Interrupt Handling */ @@ -663,16 +689,20 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, void (*overflow)(void *), void *arg, u8 timer_index); -void ath_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, - u32 timer_next, u32 timer_period); -void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer); +void ath9k_hw_gen_timer_start(struct ath_hw *ah, + struct ath_gen_timer *timer, + u32 timer_next, + u32 timer_period); +void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer); + void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer); void ath_gen_timer_isr(struct ath_hw *hw); u32 ath9k_hw_gettsf32(struct ath_hw *ah); +void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len); + #define ATH_PCIE_CAP_LINK_CTRL 0x70 #define ATH_PCIE_CAP_LINK_L0S 1 #define ATH_PCIE_CAP_LINK_L1 2 -void ath_pcie_aspm_disable(struct ath_softc *sc); #endif diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h index 8622265a030a..8a3bf3ab998d 100644 --- a/drivers/net/wireless/ath/ath9k/initvals.h +++ b/drivers/net/wireless/ath/ath9k/initvals.h @@ -21,6 +21,8 @@ static const u32 ar5416Modes[][6] = { { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, { 0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf }, + { 0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810 }, + { 0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a, 0x0000320a }, { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, @@ -31,11 +33,11 @@ static const u32 ar5416Modes[][6] = { { 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, { 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, { 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, - { 0x00009850, 0x6c48b4e0, 0x6c48b4e0, 0x6c48b0de, 0x6c48b0de, 0x6c48b0de }, + { 0x00009850, 0x6c48b4e0, 0x6d48b4e0, 0x6d48b0de, 0x6c48b0de, 0x6c48b0de }, { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, - { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e }, + { 0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e, 0x31395d5e }, { 0x00009860, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18 }, - { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, { 0x00009868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 }, { 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 }, { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, @@ -46,10 +48,10 @@ static const u32 ar5416Modes[][6] = { { 0x0000a960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 }, { 0x0000b960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 }, { 0x00009964, 0x00000000, 0x00000000, 0x00001120, 0x00001120, 0x00001120 }, - { 0x0000c9bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00 }, + { 0x000099bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00 }, { 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be }, { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, - { 0x000099c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c }, + { 0x000099c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c }, { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, @@ -199,7 +201,6 @@ static const u32 ar5416Common[][2] = { { 0x00008110, 0x00000168 }, { 0x00008118, 0x000100aa }, { 0x0000811c, 0x00003210 }, - { 0x00008120, 0x08f04800 }, { 0x00008124, 0x00000000 }, { 0x00008128, 0x00000000 }, { 0x0000812c, 0x00000000 }, @@ -215,7 +216,6 @@ static const u32 ar5416Common[][2] = { { 0x00008178, 0x00000100 }, { 0x0000817c, 0x00000000 }, { 0x000081c4, 0x00000000 }, - { 0x000081d0, 0x00003210 }, { 0x000081ec, 0x00000000 }, { 0x000081f0, 0x00000000 }, { 0x000081f4, 0x00000000 }, @@ -246,6 +246,7 @@ static const u32 ar5416Common[][2] = { { 0x00008258, 0x00000000 }, { 0x0000825c, 0x400000ff }, { 0x00008260, 0x00080922 }, + { 0x00008264, 0xa8000010 }, { 0x00008270, 0x00000000 }, { 0x00008274, 0x40000000 }, { 0x00008278, 0x003e4180 }, @@ -406,9 +407,9 @@ static const u32 ar5416Common[][2] = { { 0x0000a25c, 0x0f0f0f01 }, { 0x0000a260, 0xdfa91f01 }, { 0x0000a268, 0x00000000 }, - { 0x0000a26c, 0x0ebae9c6 }, - { 0x0000b26c, 0x0ebae9c6 }, - { 0x0000c26c, 0x0ebae9c6 }, + { 0x0000a26c, 0x0e79e5c6 }, + { 0x0000b26c, 0x0e79e5c6 }, + { 0x0000c26c, 0x0e79e5c6 }, { 0x0000d270, 0x00820820 }, { 0x0000a278, 0x1ce739ce }, { 0x0000a27c, 0x051701ce }, @@ -2551,26 +2552,27 @@ static const u32 ar9280Modes_9280_2[][6] = { { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 }, { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, - { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e }, { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, { 0x00009840, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e, 0x206a012e }, { 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 }, - { 0x00009850, 0x6c4000e2, 0x6c4000e2, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2 }, + { 0x00009850, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2 }, { 0x00009858, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, - { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x3139605e, 0x31395d5e, 0x31395d5e }, + { 0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e, 0x31395d5e }, { 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 }, { 0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, - { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000268, 0x0000000b, 0x00000016 }, { 0x00009924, 0xd00a8a0b, 0xd00a8a0b, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, { 0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010 }, { 0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, { 0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, { 0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210, 0x00000210 }, + { 0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce, 0x000003ce }, { 0x000099b8, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c }, { 0x000099bc, 0x00000a00, 0x00000a00, 0x00000c00, 0x00000c00, 0x00000c00 }, { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, @@ -2585,8 +2587,10 @@ static const u32 ar9280Modes_9280_2[][6] = { { 0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 }, { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a23c, 0x13c88000, 0x13c88000, 0x13c88001, 0x13c88000, 0x13c88000 }, { 0x0000a250, 0x001ff000, 0x001ff000, 0x0004a000, 0x0004a000, 0x0004a000 }, { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, + { 0x0000a388, 0x0c000000, 0x0c000000, 0x08000000, 0x0c000000, 0x0c000000 }, { 0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000 }, }; @@ -2813,7 +2817,6 @@ static const u32 ar9280Common_9280_2[][2] = { { 0x00009958, 0x2108ecff }, { 0x00009940, 0x14750604 }, { 0x0000c95c, 0x004b6a8e }, - { 0x0000c968, 0x000003ce }, { 0x00009970, 0x190fb515 }, { 0x00009974, 0x00000000 }, { 0x00009978, 0x00000001 }, @@ -2849,7 +2852,6 @@ static const u32 ar9280Common_9280_2[][2] = { { 0x0000a22c, 0x233f7180 }, { 0x0000a234, 0x20202020 }, { 0x0000a238, 0x20202020 }, - { 0x0000a23c, 0x13c88000 }, { 0x0000a240, 0x38490a20 }, { 0x0000a244, 0x00007bb6 }, { 0x0000a248, 0x0fff3ffc }, @@ -2859,8 +2861,8 @@ static const u32 ar9280Common_9280_2[][2] = { { 0x0000a25c, 0x0f0f0f01 }, { 0x0000a260, 0xdfa91f01 }, { 0x0000a268, 0x00000000 }, - { 0x0000a26c, 0x0ebae9c6 }, - { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000a26c, 0x0e79e5c6 }, + { 0x0000b26c, 0x0e79e5c6 }, { 0x0000d270, 0x00820820 }, { 0x0000a278, 0x1ce739ce }, { 0x0000d35c, 0x07ffffef }, @@ -2874,7 +2876,6 @@ static const u32 ar9280Common_9280_2[][2] = { { 0x0000d37c, 0x7fffffe2 }, { 0x0000d380, 0x7f3c7bba }, { 0x0000d384, 0xf3307ff0 }, - { 0x0000a388, 0x0c000000 }, { 0x0000a38c, 0x20202020 }, { 0x0000a390, 0x20202020 }, { 0x0000a394, 0x1ce739ce }, @@ -2940,7 +2941,7 @@ static const u32 ar9280Modes_fast_clock_9280_2[][3] = { { 0x0000801c, 0x148ec02b, 0x148ec057 }, { 0x00008318, 0x000044c0, 0x00008980 }, { 0x00009820, 0x02020200, 0x02020200 }, - { 0x00009824, 0x00000f0f, 0x00000f0f }, + { 0x00009824, 0x01000f0f, 0x01000f0f }, { 0x00009828, 0x0b020001, 0x0b020001 }, { 0x00009834, 0x00000f0f, 0x00000f0f }, { 0x00009844, 0x03721821, 0x03721821 }, @@ -3348,6 +3349,8 @@ static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = { }; static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { + { 0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, + { 0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce }, { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x0000a304, 0x00003002, 0x00003002, 0x00004002, 0x00004002, 0x00004002 }, { 0x0000a308, 0x00006004, 0x00006004, 0x00007008, 0x00007008, 0x00007008 }, @@ -3376,11 +3379,11 @@ static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = { { 0x00007840, 0x00172000, 0x00172000, 0x00172000, 0x00172000, 0x00172000 }, { 0x00007820, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480 }, { 0x00007844, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480 }, - { 0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, - { 0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce }, }; static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { + { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, + { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce }, { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002, 0x00003002 }, { 0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009, 0x00008009 }, @@ -3409,8 +3412,6 @@ static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = { { 0x00007840, 0x00392000, 0x00392000, 0x00392000, 0x00392000, 0x00392000 }, { 0x00007820, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480 }, { 0x00007844, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480 }, - { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, - { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce }, }; static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { @@ -5918,9 +5919,6 @@ static const u_int32_t ar9287Common_9287_1_1[][2] = { { 0x000099ec, 0x0cc80caa }, { 0x000099f0, 0x00000000 }, { 0x000099fc, 0x00001042 }, - { 0x0000a1f4, 0x00fffeff }, - { 0x0000a1f8, 0x00f5f9ff }, - { 0x0000a1fc, 0xb79f6427 }, { 0x0000a208, 0x803e4788 }, { 0x0000a210, 0x4080a333 }, { 0x0000a214, 0x40206c10 }, @@ -5980,7 +5978,7 @@ static const u_int32_t ar9287Common_9287_1_1[][2] = { { 0x0000b3f4, 0x00000000 }, { 0x0000a7d8, 0x000003f1 }, { 0x00007800, 0x00000800 }, - { 0x00007804, 0x6c35ffc2 }, + { 0x00007804, 0x6c35ffd2 }, { 0x00007808, 0x6db6c000 }, { 0x0000780c, 0x6db6cb30 }, { 0x00007810, 0x6db6cb6c }, @@ -6000,7 +5998,7 @@ static const u_int32_t ar9287Common_9287_1_1[][2] = { { 0x00007848, 0x934934a8 }, { 0x00007850, 0x00000000 }, { 0x00007854, 0x00000800 }, - { 0x00007858, 0x6c35ffc2 }, + { 0x00007858, 0x6c35ffd2 }, { 0x0000785c, 0x6db6c000 }, { 0x00007860, 0x6db6cb30 }, { 0x00007864, 0x6db6cb6c }, @@ -6027,6 +6025,22 @@ static const u_int32_t ar9287Common_9287_1_1[][2] = { { 0x000078b8, 0x2a850160 }, }; +/* + * For Japanese regulatory requirements, 2484 MHz requires the following three + * registers be programmed differently from the channel between 2412 and 2472 MHz. + */ +static const u_int32_t ar9287Common_normal_cck_fir_coeff_92871_1[][2] = { + { 0x0000a1f4, 0x00fffeff }, + { 0x0000a1f8, 0x00f5f9ff }, + { 0x0000a1fc, 0xb79f6427 }, +}; + +static const u_int32_t ar9287Common_japan_2484_cck_fir_coeff_92871_1[][2] = { + { 0x0000a1f4, 0x00000000 }, + { 0x0000a1f8, 0xefff0301 }, + { 0x0000a1fc, 0xca9228ee }, +}; + static const u_int32_t ar9287Modes_tx_gain_9287_1_1[][6] = { /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, @@ -6365,8 +6379,8 @@ static const u_int32_t ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { }; -/* AR9271 initialization values automaticaly created: 03/23/09 */ -static const u_int32_t ar9271Modes_9271_1_0[][6] = { +/* AR9271 initialization values automaticaly created: 06/04/09 */ +static const u_int32_t ar9271Modes_9271[][6] = { { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, @@ -6376,8 +6390,8 @@ static const u_int32_t ar9271Modes_9271_1_0[][6] = { { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 }, { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, - { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, - { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e }, + { 0x00009828, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001 }, { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, { 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e }, @@ -6391,6 +6405,7 @@ static const u_int32_t ar9271Modes_9271_1_0[][6] = { { 0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009910, 0x30002310, 0x30002310, 0x30002310, 0x30002310, 0x30002310 }, { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d }, @@ -6401,7 +6416,7 @@ static const u_int32_t ar9271Modes_9271_1_0[][6] = { { 0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 }, { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, - { 0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329 }, + { 0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f }, { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, @@ -6690,7 +6705,7 @@ static const u_int32_t ar9271Modes_9271_1_0[][6] = { { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, }; -static const u_int32_t ar9271Common_9271_1_0[][2] = { +static const u_int32_t ar9271Common_9271[][2] = { { 0x0000000c, 0x00000000 }, { 0x00000030, 0x00020045 }, { 0x00000034, 0x00000005 }, @@ -6786,7 +6801,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = { { 0x0000803c, 0x00000000 }, { 0x00008048, 0x00000000 }, { 0x00008054, 0x00000000 }, - { 0x00008058, 0x02000000 }, + { 0x00008058, 0x00000000 }, { 0x0000805c, 0x000fc78f }, { 0x00008060, 0x0000000f }, { 0x00008064, 0x00000000 }, @@ -6817,7 +6832,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = { { 0x00008110, 0x00000168 }, { 0x00008118, 0x000100aa }, { 0x0000811c, 0x00003210 }, - { 0x00008120, 0x08f04814 }, + { 0x00008120, 0x08f04810 }, { 0x00008124, 0x00000000 }, { 0x00008128, 0x00000000 }, { 0x0000812c, 0x00000000 }, @@ -6864,7 +6879,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = { { 0x00008258, 0x00000000 }, { 0x0000825c, 0x400000ff }, { 0x00008260, 0x00080922 }, - { 0x00008264, 0xa8a00010 }, + { 0x00008264, 0x88a00010 }, { 0x00008270, 0x00000000 }, { 0x00008274, 0x40000000 }, { 0x00008278, 0x003e4180 }, @@ -6896,7 +6911,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = { { 0x00007814, 0x924934a8 }, { 0x0000781c, 0x00000000 }, { 0x00007820, 0x00000c04 }, - { 0x00007824, 0x00d86bff }, + { 0x00007824, 0x00d8abff }, { 0x00007828, 0x66964300 }, { 0x0000782c, 0x8db6d961 }, { 0x00007830, 0x8db6d96c }, @@ -6930,7 +6945,6 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = { { 0x00009904, 0x00000000 }, { 0x00009908, 0x00000000 }, { 0x0000990c, 0x00000000 }, - { 0x00009910, 0x30002310 }, { 0x0000991c, 0x10000fff }, { 0x00009920, 0x04900000 }, { 0x00009928, 0x00000001 }, @@ -6944,7 +6958,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = { { 0x00009954, 0x5f3ca3de }, { 0x00009958, 0x0108ecff }, { 0x00009968, 0x000003ce }, - { 0x00009970, 0x192bb515 }, + { 0x00009970, 0x192bb514 }, { 0x00009974, 0x00000000 }, { 0x00009978, 0x00000001 }, { 0x0000997c, 0x00000000 }, @@ -7031,3 +7045,8 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = { { 0x0000d380, 0x7f3c7bba }, { 0x0000d384, 0xf3307ff0 }, }; + +static const u_int32_t ar9271Modes_9271_1_0_only[][6] = { + { 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, +}; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 800bfab94635..71b84d91dcff 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -14,16 +14,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +#include "hw.h" static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah, struct ath9k_tx_queue_info *qi) { - DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, - "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", - ah->txok_interrupt_mask, ah->txerr_interrupt_mask, - ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask, - ah->txurn_interrupt_mask); + ath_print(ath9k_hw_common(ah), ATH_DBG_INTERRUPT, + "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", + ah->txok_interrupt_mask, ah->txerr_interrupt_mask, + ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask, + ah->txurn_interrupt_mask); REG_WRITE(ah, AR_IMR_S0, SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK) @@ -39,17 +39,21 @@ u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q) { return REG_READ(ah, AR_QTXDP(q)); } +EXPORT_SYMBOL(ath9k_hw_gettxbuf); void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp) { REG_WRITE(ah, AR_QTXDP(q), txdp); } +EXPORT_SYMBOL(ath9k_hw_puttxbuf); void ath9k_hw_txstart(struct ath_hw *ah, u32 q) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Enable TXE on queue: %u\n", q); + ath_print(ath9k_hw_common(ah), ATH_DBG_QUEUE, + "Enable TXE on queue: %u\n", q); REG_WRITE(ah, AR_Q_TXE, 1 << q); } +EXPORT_SYMBOL(ath9k_hw_txstart); u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q) { @@ -64,13 +68,39 @@ u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q) return npend; } +EXPORT_SYMBOL(ath9k_hw_numtxpending); +/** + * ath9k_hw_updatetxtriglevel - adjusts the frame trigger level + * + * @ah: atheros hardware struct + * @bIncTrigLevel: whether or not the frame trigger level should be updated + * + * The frame trigger level specifies the minimum number of bytes, + * in units of 64 bytes, that must be DMA'ed into the PCU TX FIFO + * before the PCU will initiate sending the frame on the air. This can + * mean we initiate transmit before a full frame is on the PCU TX FIFO. + * Resets to 0x1 (meaning 64 bytes or a full frame, whichever occurs + * first) + * + * Caution must be taken to ensure to set the frame trigger level based + * on the DMA request size. For example if the DMA request size is set to + * 128 bytes the trigger level cannot exceed 6 * 64 = 384. This is because + * there need to be enough space in the tx FIFO for the requested transfer + * size. Hence the tx FIFO will stop with 512 - 128 = 384 bytes. If we set + * the threshold to a value beyond 6, then the transmit will hang. + * + * Current dual stream devices have a PCU TX FIFO size of 8 KB. + * Current single stream devices have a PCU TX FIFO size of 4 KB, however, + * there is a hardware issue which forces us to use 2 KB instead so the + * frame trigger level must not exceed 2 KB for these chipsets. + */ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel) { u32 txcfg, curLevel, newLevel; enum ath9k_int omask; - if (ah->tx_trig_level >= MAX_TX_FIFO_THRESHOLD) + if (ah->tx_trig_level >= ah->config.max_txtrig_level) return false; omask = ath9k_hw_set_interrupts(ah, ah->mask_reg & ~ATH9K_INT_GLOBAL); @@ -79,7 +109,7 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel) curLevel = MS(txcfg, AR_FTRIG); newLevel = curLevel; if (bIncTrigLevel) { - if (curLevel < MAX_TX_FIFO_THRESHOLD) + if (curLevel < ah->config.max_txtrig_level) newLevel++; } else if (curLevel > MIN_TX_FIFO_THRESHOLD) newLevel--; @@ -93,27 +123,28 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel) return newLevel != curLevel; } +EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel); bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) { #define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */ #define ATH9K_TIME_QUANTUM 100 /* usec */ - + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath9k_tx_queue_info *qi; u32 tsfLow, j, wait; u32 wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM; if (q >= pCap->total_queues) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Stopping TX DMA, " - "invalid queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, " + "invalid queue: %u\n", q); return false; } qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Stopping TX DMA, " - "inactive queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, " + "inactive queue: %u\n", q); return false; } @@ -126,9 +157,9 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) } if (ath9k_hw_numtxpending(ah, q)) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, - "%s: Num of pending TX Frames %d on Q %d\n", - __func__, ath9k_hw_numtxpending(ah, q), q); + ath_print(common, ATH_DBG_QUEUE, + "%s: Num of pending TX Frames %d on Q %d\n", + __func__, ath9k_hw_numtxpending(ah, q), q); for (j = 0; j < 2; j++) { tsfLow = REG_READ(ah, AR_TSF_L32); @@ -142,9 +173,9 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) if ((REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10)) break; - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, - "TSF has moved while trying to set " - "quiet time TSF: 0x%08x\n", tsfLow); + ath_print(common, ATH_DBG_QUEUE, + "TSF has moved while trying to set " + "quiet time TSF: 0x%08x\n", tsfLow); } REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); @@ -155,9 +186,9 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) wait = wait_time; while (ath9k_hw_numtxpending(ah, q)) { if ((--wait) == 0) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, - "Failed to stop TX DMA in 100 " - "msec after killing last frame\n"); + ath_print(common, ATH_DBG_QUEUE, + "Failed to stop TX DMA in 100 " + "msec after killing last frame\n"); break; } udelay(ATH9K_TIME_QUANTUM); @@ -172,6 +203,7 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) #undef ATH9K_TX_STOP_DMA_TIMEOUT #undef ATH9K_TIME_QUANTUM } +EXPORT_SYMBOL(ath9k_hw_stoptxdma); void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 segLen, bool firstSeg, @@ -198,6 +230,7 @@ void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, ads->ds_txstatus6 = ads->ds_txstatus7 = 0; ads->ds_txstatus8 = ads->ds_txstatus9 = 0; } +EXPORT_SYMBOL(ath9k_hw_filltxdesc); void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds) { @@ -209,6 +242,7 @@ void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds) ads->ds_txstatus6 = ads->ds_txstatus7 = 0; ads->ds_txstatus8 = ads->ds_txstatus9 = 0; } +EXPORT_SYMBOL(ath9k_hw_cleartxdesc); int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds) { @@ -222,6 +256,8 @@ int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds) ds->ds_txstat.ts_status = 0; ds->ds_txstat.ts_flags = 0; + if (ads->ds_txstatus1 & AR_FrmXmitOK) + ds->ds_txstat.ts_status |= ATH9K_TX_ACKED; if (ads->ds_txstatus1 & AR_ExcessiveRetries) ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY; if (ads->ds_txstatus1 & AR_Filtered) @@ -284,6 +320,7 @@ int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds) return 0; } +EXPORT_SYMBOL(ath9k_hw_txprocdesc); void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds, u32 pktLen, enum ath9k_pkt_type type, u32 txPower, @@ -319,6 +356,7 @@ void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds, ads->ds_ctl11 = 0; } } +EXPORT_SYMBOL(ath9k_hw_set11n_txdesc); void ath9k_hw_set11n_ratescenario(struct ath_hw *ah, struct ath_desc *ds, struct ath_desc *lastds, @@ -374,6 +412,7 @@ void ath9k_hw_set11n_ratescenario(struct ath_hw *ah, struct ath_desc *ds, last_ads->ds_ctl2 = ads->ds_ctl2; last_ads->ds_ctl3 = ads->ds_ctl3; } +EXPORT_SYMBOL(ath9k_hw_set11n_ratescenario); void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, struct ath_desc *ds, u32 aggrLen) @@ -384,6 +423,7 @@ void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, struct ath_desc *ds, ads->ds_ctl6 &= ~AR_AggrLen; ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); } +EXPORT_SYMBOL(ath9k_hw_set11n_aggr_first); void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds, u32 numDelims) @@ -398,6 +438,7 @@ void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds, ctl6 |= SM(numDelims, AR_PadDelim); ads->ds_ctl6 = ctl6; } +EXPORT_SYMBOL(ath9k_hw_set11n_aggr_middle); void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds) { @@ -407,6 +448,7 @@ void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds) ads->ds_ctl1 &= ~AR_MoreAggr; ads->ds_ctl6 &= ~AR_PadDelim; } +EXPORT_SYMBOL(ath9k_hw_set11n_aggr_last); void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds) { @@ -414,6 +456,7 @@ void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds) ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr); } +EXPORT_SYMBOL(ath9k_hw_clr11n_aggr); void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds, u32 burstDuration) @@ -423,6 +466,7 @@ void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds, ads->ds_ctl2 &= ~AR_BurstDur; ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur); } +EXPORT_SYMBOL(ath9k_hw_set11n_burstduration); void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, struct ath_desc *ds, u32 vmf) @@ -440,28 +484,30 @@ void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs) *txqs &= ah->intr_txqs; ah->intr_txqs &= ~(*txqs); } +EXPORT_SYMBOL(ath9k_hw_gettxintrtxqs); bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, const struct ath9k_tx_queue_info *qinfo) { u32 cw; + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath9k_tx_queue_info *qi; if (q >= pCap->total_queues) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set TXQ properties, " - "invalid queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Set TXQ properties, " + "invalid queue: %u\n", q); return false; } qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set TXQ properties, " - "inactive queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Set TXQ properties, " + "inactive queue: %u\n", q); return false; } - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set queue properties for: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Set queue properties for: %u\n", q); qi->tqi_ver = qinfo->tqi_ver; qi->tqi_subtype = qinfo->tqi_subtype; @@ -510,23 +556,25 @@ bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, return true; } +EXPORT_SYMBOL(ath9k_hw_set_txq_props); bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q, struct ath9k_tx_queue_info *qinfo) { + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath9k_tx_queue_info *qi; if (q >= pCap->total_queues) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Get TXQ properties, " - "invalid queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Get TXQ properties, " + "invalid queue: %u\n", q); return false; } qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Get TXQ properties, " - "inactive queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Get TXQ properties, " + "inactive queue: %u\n", q); return false; } @@ -547,10 +595,12 @@ bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q, return true; } +EXPORT_SYMBOL(ath9k_hw_get_txq_props); int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, const struct ath9k_tx_queue_info *qinfo) { + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info *qi; struct ath9k_hw_capabilities *pCap = &ah->caps; int q; @@ -574,23 +624,23 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, ATH9K_TX_QUEUE_INACTIVE) break; if (q == pCap->total_queues) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "No available TX queue\n"); + ath_print(common, ATH_DBG_FATAL, + "No available TX queue\n"); return -1; } break; default: - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Invalid TX queue type: %u\n", - type); + ath_print(common, ATH_DBG_FATAL, + "Invalid TX queue type: %u\n", type); return -1; } - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q); qi = &ah->txq[q]; if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "TX queue: %u already active\n", q); + ath_print(common, ATH_DBG_FATAL, + "TX queue: %u already active\n", q); return -1; } memset(qi, 0, sizeof(struct ath9k_tx_queue_info)); @@ -613,25 +663,27 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, return q; } +EXPORT_SYMBOL(ath9k_hw_setuptxqueue); bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q) { struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info *qi; if (q >= pCap->total_queues) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TXQ, " - "invalid queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Release TXQ, " + "invalid queue: %u\n", q); return false; } qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TXQ, " - "inactive queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Release TXQ, " + "inactive queue: %u\n", q); return false; } - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TX queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Release TX queue: %u\n", q); qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE; ah->txok_interrupt_mask &= ~(1 << q); @@ -643,28 +695,30 @@ bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q) return true; } +EXPORT_SYMBOL(ath9k_hw_releasetxqueue); bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) { struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ath9k_tx_queue_info *qi; u32 cwMin, chanCwMin, value; if (q >= pCap->total_queues) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TXQ, " - "invalid queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Reset TXQ, " + "invalid queue: %u\n", q); return false; } qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TXQ, " - "inactive queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Reset TXQ, " + "inactive queue: %u\n", q); return true; } - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TX queue: %u\n", q); + ath_print(common, ATH_DBG_QUEUE, "Reset TX queue: %u\n", q); if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { if (chan && IS_CHAN_B(chan)) @@ -799,6 +853,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) return true; } +EXPORT_SYMBOL(ath9k_hw_resettxqueue); int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, u32 pa, struct ath_desc *nds, u64 tsf) @@ -880,6 +935,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, return 0; } +EXPORT_SYMBOL(ath9k_hw_rxprocdesc); void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags) @@ -895,7 +951,15 @@ void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) memset(&(ads->u), 0, sizeof(ads->u)); } +EXPORT_SYMBOL(ath9k_hw_setuprxdesc); +/* + * This can stop or re-enables RX. + * + * If bool is set this will kill any frame which is currently being + * transferred between the MAC and baseband and also prevent any new + * frames from getting started. + */ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set) { u32 reg; @@ -911,8 +975,9 @@ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set) AR_DIAG_RX_ABORT)); reg = REG_READ(ah, AR_OBS_BUS_1); - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "RX failed to go idle in 10 ms RXSM=0x%x\n", reg); + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "RX failed to go idle in 10 ms RXSM=0x%x\n", + reg); return false; } @@ -923,16 +988,19 @@ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set) return true; } +EXPORT_SYMBOL(ath9k_hw_setrxabort); void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp) { REG_WRITE(ah, AR_RXDP, rxdp); } +EXPORT_SYMBOL(ath9k_hw_putrxbuf); void ath9k_hw_rxena(struct ath_hw *ah) { REG_WRITE(ah, AR_CR, AR_CR_RXE); } +EXPORT_SYMBOL(ath9k_hw_rxena); void ath9k_hw_startpcureceive(struct ath_hw *ah) { @@ -942,6 +1010,7 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah) REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); } +EXPORT_SYMBOL(ath9k_hw_startpcureceive); void ath9k_hw_stoppcurecv(struct ath_hw *ah) { @@ -949,12 +1018,13 @@ void ath9k_hw_stoppcurecv(struct ath_hw *ah) ath9k_hw_disable_mib_counters(ah); } +EXPORT_SYMBOL(ath9k_hw_stoppcurecv); bool ath9k_hw_stopdmarecv(struct ath_hw *ah) { #define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ #define AH_RX_TIME_QUANTUM 100 /* usec */ - + struct ath_common *common = ath9k_hw_common(ah); int i; REG_WRITE(ah, AR_CR, AR_CR_RXD); @@ -967,12 +1037,12 @@ bool ath9k_hw_stopdmarecv(struct ath_hw *ah) } if (i == 0) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "DMA failed to stop in %d ms " - "AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", - AH_RX_STOP_DMA_TIMEOUT / 1000, - REG_READ(ah, AR_CR), - REG_READ(ah, AR_DIAG_SW)); + ath_print(common, ATH_DBG_FATAL, + "DMA failed to stop in %d ms " + "AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", + AH_RX_STOP_DMA_TIMEOUT / 1000, + REG_READ(ah, AR_CR), + REG_READ(ah, AR_DIAG_SW)); return false; } else { return true; @@ -981,3 +1051,17 @@ bool ath9k_hw_stopdmarecv(struct ath_hw *ah) #undef AH_RX_TIME_QUANTUM #undef AH_RX_STOP_DMA_TIMEOUT } +EXPORT_SYMBOL(ath9k_hw_stopdmarecv); + +int ath9k_hw_beaconq_setup(struct ath_hw *ah) +{ + struct ath9k_tx_queue_info qi; + + memset(&qi, 0, sizeof(qi)); + qi.tqi_aifs = 1; + qi.tqi_cwmin = 0; + qi.tqi_cwmax = 0; + /* NB: don't enable any interrupts */ + return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); +} +EXPORT_SYMBOL(ath9k_hw_beaconq_setup); diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index f56e77da6c3e..0c87771383f0 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -76,6 +76,7 @@ #define ATH9K_TXERR_FIFO 0x04 #define ATH9K_TXERR_XTXOP 0x08 #define ATH9K_TXERR_TIMER_EXPIRED 0x10 +#define ATH9K_TX_ACKED 0x20 #define ATH9K_TX_BA 0x01 #define ATH9K_TX_PWRMGMT 0x02 @@ -85,9 +86,15 @@ #define ATH9K_TX_SW_ABORTED 0x40 #define ATH9K_TX_SW_FILTERED 0x80 +/* 64 bytes */ #define MIN_TX_FIFO_THRESHOLD 0x1 + +/* + * Single stream device AR9285 and AR9271 require 2 KB + * to work around a hardware issue, all other devices + * have can use the max 4 KB limit. + */ #define MAX_TX_FIFO_THRESHOLD ((4096 / 64) - 1) -#define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD struct ath_tx_status { u32 ts_tstamp; @@ -380,6 +387,11 @@ struct ar5416_desc { #define AR_TxBaStatus 0x40000000 #define AR_TxStatusRsvd01 0x80000000 +/* + * AR_FrmXmitOK - Frame transmission success flag. If set, the frame was + * transmitted successfully. If clear, no ACK or BA was received to indicate + * successful transmission when we were expecting an ACK or BA. + */ #define AR_FrmXmitOK 0x00000001 #define AR_ExcessiveRetries 0x00000002 #define AR_FIFOUnderrun 0x00000004 @@ -614,19 +626,8 @@ enum ath9k_cipher { ATH9K_CIPHER_MIC = 127 }; -enum ath9k_ht_macmode { - ATH9K_HT_MACMODE_20 = 0, - ATH9K_HT_MACMODE_2040 = 1, -}; - -enum ath9k_ht_extprotspacing { - ATH9K_HT_EXTPROTSPACING_20 = 0, - ATH9K_HT_EXTPROTSPACING_25 = 1, -}; - struct ath_hw; struct ath9k_channel; -struct ath_rate_table; u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q); void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp); @@ -677,5 +678,6 @@ void ath9k_hw_rxena(struct ath_hw *ah); void ath9k_hw_startpcureceive(struct ath_hw *ah); void ath9k_hw_stoppcurecv(struct ath_hw *ah); bool ath9k_hw_stopdmarecv(struct ath_hw *ah); +int ath9k_hw_beaconq_setup(struct ath_hw *ah); #endif /* MAC_H */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 43d2be9867fc..c48743452515 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -16,6 +16,7 @@ #include <linux/nl80211.h> #include "ath9k.h" +#include "btcoex.h" static char *dev_info = "ath9k"; @@ -28,6 +29,10 @@ static int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); +static unsigned int ath9k_debug = ATH_DBG_DEFAULT; +module_param_named(debug, ath9k_debug, uint, 0); +MODULE_PARM_DESC(debug, "Debugging mask"); + /* We use the hw_value as an index into our private channel structure */ #define CHAN2G(_freq, _idx) { \ @@ -99,37 +104,55 @@ static struct ieee80211_channel ath9k_5ghz_chantable[] = { CHAN5G(5825, 37), /* Channel 165 */ }; +/* Atheros hardware rate code addition for short premble */ +#define SHPCHECK(__hw_rate, __flags) \ + ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) + +#define RATE(_bitrate, _hw_rate, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate), \ + .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ +} + +static struct ieee80211_rate ath9k_legacy_rates[] = { + RATE(10, 0x1b, 0), + RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, 0x0b, 0), + RATE(90, 0x0f, 0), + RATE(120, 0x0a, 0), + RATE(180, 0x0e, 0), + RATE(240, 0x09, 0), + RATE(360, 0x0d, 0), + RATE(480, 0x08, 0), + RATE(540, 0x0c, 0), +}; + static void ath_cache_conf_rate(struct ath_softc *sc, struct ieee80211_conf *conf) { switch (conf->channel->band) { case IEEE80211_BAND_2GHZ: if (conf_is_ht20(conf)) - sc->cur_rate_table = - sc->hw_rate_table[ATH9K_MODE_11NG_HT20]; + sc->cur_rate_mode = ATH9K_MODE_11NG_HT20; else if (conf_is_ht40_minus(conf)) - sc->cur_rate_table = - sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS]; + sc->cur_rate_mode = ATH9K_MODE_11NG_HT40MINUS; else if (conf_is_ht40_plus(conf)) - sc->cur_rate_table = - sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS]; + sc->cur_rate_mode = ATH9K_MODE_11NG_HT40PLUS; else - sc->cur_rate_table = - sc->hw_rate_table[ATH9K_MODE_11G]; + sc->cur_rate_mode = ATH9K_MODE_11G; break; case IEEE80211_BAND_5GHZ: if (conf_is_ht20(conf)) - sc->cur_rate_table = - sc->hw_rate_table[ATH9K_MODE_11NA_HT20]; + sc->cur_rate_mode = ATH9K_MODE_11NA_HT20; else if (conf_is_ht40_minus(conf)) - sc->cur_rate_table = - sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS]; + sc->cur_rate_mode = ATH9K_MODE_11NA_HT40MINUS; else if (conf_is_ht40_plus(conf)) - sc->cur_rate_table = - sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS]; + sc->cur_rate_mode = ATH9K_MODE_11NA_HT40PLUS; else - sc->cur_rate_table = - sc->hw_rate_table[ATH9K_MODE_11A]; + sc->cur_rate_mode = ATH9K_MODE_11A; break; default: BUG_ON(1); @@ -185,50 +208,6 @@ static u8 parse_mpdudensity(u8 mpdudensity) } } -static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band) -{ - const struct ath_rate_table *rate_table = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_rate *rate; - int i, maxrates; - - switch (band) { - case IEEE80211_BAND_2GHZ: - rate_table = sc->hw_rate_table[ATH9K_MODE_11G]; - break; - case IEEE80211_BAND_5GHZ: - rate_table = sc->hw_rate_table[ATH9K_MODE_11A]; - break; - default: - break; - } - - if (rate_table == NULL) - return; - - sband = &sc->sbands[band]; - rate = sc->rates[band]; - - if (rate_table->rate_cnt > ATH_RATE_MAX) - maxrates = ATH_RATE_MAX; - else - maxrates = rate_table->rate_cnt; - - for (i = 0; i < maxrates; i++) { - rate[i].bitrate = rate_table->info[i].ratekbps / 100; - rate[i].hw_value = rate_table->info[i].ratecode; - if (rate_table->info[i].short_preamble) { - rate[i].hw_value_short = rate_table->info[i].ratecode | - rate_table->info[i].short_preamble; - rate[i].flags = IEEE80211_RATE_SHORT_PREAMBLE; - } - sband->n_bitrates++; - - DPRINTF(sc, ATH_DBG_CONFIG, "Rate: %2dMbps, ratecode: %2d\n", - rate[i].bitrate / 10, rate[i].hw_value); - } -} - static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc, struct ieee80211_hw *hw) { @@ -242,6 +221,51 @@ static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc, return channel; } +static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&sc->sc_pm_lock, flags); + ret = ath9k_hw_setpower(sc->sc_ah, mode); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + + return ret; +} + +void ath9k_ps_wakeup(struct ath_softc *sc) +{ + unsigned long flags; + + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (++sc->ps_usecount != 1) + goto unlock; + + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + + unlock: + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); +} + +void ath9k_ps_restore(struct ath_softc *sc) +{ + unsigned long flags; + + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (--sc->ps_usecount != 0) + goto unlock; + + if (sc->ps_enabled && + !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_CAB | + SC_OP_WAIT_FOR_PSPOLL_DATA | + SC_OP_WAIT_FOR_TX_ACK))) + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); + + unlock: + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); +} + /* * Set/change channels. If the channel is really being changed, it's done * by reseting the chip. To accomplish this we must first cleanup any pending @@ -251,6 +275,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_conf *conf = &common->hw->conf; bool fastcc = true, stopped; struct ieee80211_channel *channel = hw->conf.channel; int r; @@ -280,19 +306,19 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET)) fastcc = false; - DPRINTF(sc, ATH_DBG_CONFIG, - "(%u MHz) -> (%u MHz), chanwidth: %d\n", - sc->sc_ah->curchan->channel, - channel->center_freq, sc->tx_chan_width); + ath_print(common, ATH_DBG_CONFIG, + "(%u MHz) -> (%u MHz), conf_is_ht40: %d\n", + sc->sc_ah->curchan->channel, + channel->center_freq, conf_is_ht40(conf)); spin_lock_bh(&sc->sc_resetlock); r = ath9k_hw_reset(ah, hchan, fastcc); if (r) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to reset channel (%u Mhz) " - "reset status %d\n", - channel->center_freq, r); + ath_print(common, ATH_DBG_FATAL, + "Unable to reset channel (%u Mhz) " + "reset status %d\n", + channel->center_freq, r); spin_unlock_bh(&sc->sc_resetlock); goto ps_restore; } @@ -301,8 +327,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, sc->sc_flags &= ~SC_OP_FULL_RESET; if (ath_startrecv(sc) != 0) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to restart recv logic\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to restart recv logic\n"); r = -EIO; goto ps_restore; } @@ -327,6 +353,7 @@ static void ath_ani_calibrate(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); bool longcal = false; bool shortcal = false; bool aniflag = false; @@ -351,33 +378,34 @@ static void ath_ani_calibrate(unsigned long data) ath9k_ps_wakeup(sc); /* Long calibration runs independently of short calibration. */ - if ((timestamp - sc->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { + if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { longcal = true; - DPRINTF(sc, ATH_DBG_ANI, "longcal @%lu\n", jiffies); - sc->ani.longcal_timer = timestamp; + ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); + common->ani.longcal_timer = timestamp; } /* Short calibration applies only while caldone is false */ - if (!sc->ani.caldone) { - if ((timestamp - sc->ani.shortcal_timer) >= short_cal_interval) { + if (!common->ani.caldone) { + if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { shortcal = true; - DPRINTF(sc, ATH_DBG_ANI, "shortcal @%lu\n", jiffies); - sc->ani.shortcal_timer = timestamp; - sc->ani.resetcal_timer = timestamp; + ath_print(common, ATH_DBG_ANI, + "shortcal @%lu\n", jiffies); + common->ani.shortcal_timer = timestamp; + common->ani.resetcal_timer = timestamp; } } else { - if ((timestamp - sc->ani.resetcal_timer) >= + if ((timestamp - common->ani.resetcal_timer) >= ATH_RESTART_CALINTERVAL) { - sc->ani.caldone = ath9k_hw_reset_calvalid(ah); - if (sc->ani.caldone) - sc->ani.resetcal_timer = timestamp; + common->ani.caldone = ath9k_hw_reset_calvalid(ah); + if (common->ani.caldone) + common->ani.resetcal_timer = timestamp; } } /* Verify whether we must check ANI */ - if ((timestamp - sc->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { + if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { aniflag = true; - sc->ani.checkani_timer = timestamp; + common->ani.checkani_timer = timestamp; } /* Skip all processing if there's nothing to do. */ @@ -388,16 +416,21 @@ static void ath_ani_calibrate(unsigned long data) /* Perform calibration if necessary */ if (longcal || shortcal) { - sc->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, - sc->rx_chainmask, longcal); + common->ani.caldone = + ath9k_hw_calibrate(ah, + ah->curchan, + common->rx_chainmask, + longcal); if (longcal) - sc->ani.noise_floor = ath9k_hw_getchan_noise(ah, + common->ani.noise_floor = ath9k_hw_getchan_noise(ah, ah->curchan); - DPRINTF(sc, ATH_DBG_ANI," calibrate chan %u/%x nf: %d\n", - ah->curchan->channel, ah->curchan->channelFlags, - sc->ani.noise_floor); + ath_print(common, ATH_DBG_ANI, + " calibrate chan %u/%x nf: %d\n", + ah->curchan->channel, + ah->curchan->channelFlags, + common->ani.noise_floor); } } @@ -413,21 +446,21 @@ set_timer: cal_interval = ATH_LONG_CALINTERVAL; if (sc->sc_ah->config.enable_ani) cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); - if (!sc->ani.caldone) + if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); - mod_timer(&sc->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); + mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); } -static void ath_start_ani(struct ath_softc *sc) +static void ath_start_ani(struct ath_common *common) { unsigned long timestamp = jiffies_to_msecs(jiffies); - sc->ani.longcal_timer = timestamp; - sc->ani.shortcal_timer = timestamp; - sc->ani.checkani_timer = timestamp; + common->ani.longcal_timer = timestamp; + common->ani.shortcal_timer = timestamp; + common->ani.checkani_timer = timestamp; - mod_timer(&sc->ani.timer, + mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); } @@ -439,17 +472,22 @@ static void ath_start_ani(struct ath_softc *sc) */ void ath_update_chainmask(struct ath_softc *sc, int is_ht) { + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + if ((sc->sc_flags & SC_OP_SCANNING) || is_ht || - (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE)) { - sc->tx_chainmask = sc->sc_ah->caps.tx_chainmask; - sc->rx_chainmask = sc->sc_ah->caps.rx_chainmask; + (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE)) { + common->tx_chainmask = ah->caps.tx_chainmask; + common->rx_chainmask = ah->caps.rx_chainmask; } else { - sc->tx_chainmask = 1; - sc->rx_chainmask = 1; + common->tx_chainmask = 1; + common->rx_chainmask = 1; } - DPRINTF(sc, ATH_DBG_CONFIG, "tx chmask: %d, rx chmask: %d\n", - sc->tx_chainmask, sc->rx_chainmask); + ath_print(common, ATH_DBG_CONFIG, + "tx chmask: %d, rx chmask: %d\n", + common->tx_chainmask, + common->rx_chainmask); } static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta) @@ -478,6 +516,9 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) static void ath9k_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + u32 status = sc->intrstatus; ath9k_ps_wakeup(sc); @@ -502,16 +543,17 @@ static void ath9k_tasklet(unsigned long data) * TSF sync does not look correct; remain awake to sync with * the next Beacon. */ - DPRINTF(sc, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n"); + ath_print(common, ATH_DBG_PS, + "TSFOOR - Sync with next Beacon\n"); sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC; } - if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) if (status & ATH9K_INT_GENTIMER) ath_gen_timer_isr(sc->sc_ah); /* re-enable hardware interrupt */ - ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + ath9k_hw_set_interrupts(ah, sc->imask); ath9k_ps_restore(sc); } @@ -602,7 +644,7 @@ irqreturn_t ath_isr(int irq, void *dev) if (status & ATH9K_INT_TIM_TIMER) { /* Clear RxAbort bit so that we can * receive frames */ - ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); + ath9k_setpower(sc, ATH9K_PM_AWAKE); ath9k_hw_setrxabort(sc->sc_ah, 0); sc->sc_flags |= SC_OP_WAIT_FOR_BEACON; } @@ -664,10 +706,11 @@ static u32 ath_get_extchanmode(struct ath_softc *sc, return chanmode; } -static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key, +static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, struct ath9k_keyval *hk, const u8 *addr, bool authenticator) { + struct ath_hw *ah = common->ah; const u8 *key_rxmic; const u8 *key_txmic; @@ -687,42 +730,42 @@ static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key, memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); } - return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr); + return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); } - if (!sc->splitmic) { + if (!common->splitmic) { /* TX and RX keys share the same key cache entry. */ memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); - return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr); + return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); } /* Separate key cache entries for TX and RX */ /* TX key goes at first index, RX key at +32. */ memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); - if (!ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, NULL)) { + if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) { /* TX MIC entry failed. No need to proceed further */ - DPRINTF(sc, ATH_DBG_FATAL, - "Setting TX MIC Key Failed\n"); + ath_print(common, ATH_DBG_FATAL, + "Setting TX MIC Key Failed\n"); return 0; } memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); /* XXX delete tx key on failure? */ - return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix + 32, hk, addr); + return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr); } -static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc) +static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) { int i; - for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) { - if (test_bit(i, sc->keymap) || - test_bit(i + 64, sc->keymap)) + for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { + if (test_bit(i, common->keymap) || + test_bit(i + 64, common->keymap)) continue; /* At least one part of TKIP key allocated */ - if (sc->splitmic && - (test_bit(i + 32, sc->keymap) || - test_bit(i + 64 + 32, sc->keymap))) + if (common->splitmic && + (test_bit(i + 32, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) continue; /* At least one part of TKIP key allocated */ /* Found a free slot for a TKIP key */ @@ -731,60 +774,60 @@ static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc) return -1; } -static int ath_reserve_key_cache_slot(struct ath_softc *sc) +static int ath_reserve_key_cache_slot(struct ath_common *common) { int i; /* First, try to find slots that would not be available for TKIP. */ - if (sc->splitmic) { - for (i = IEEE80211_WEP_NKID; i < sc->keymax / 4; i++) { - if (!test_bit(i, sc->keymap) && - (test_bit(i + 32, sc->keymap) || - test_bit(i + 64, sc->keymap) || - test_bit(i + 64 + 32, sc->keymap))) + if (common->splitmic) { + for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { + if (!test_bit(i, common->keymap) && + (test_bit(i + 32, common->keymap) || + test_bit(i + 64, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) return i; - if (!test_bit(i + 32, sc->keymap) && - (test_bit(i, sc->keymap) || - test_bit(i + 64, sc->keymap) || - test_bit(i + 64 + 32, sc->keymap))) + if (!test_bit(i + 32, common->keymap) && + (test_bit(i, common->keymap) || + test_bit(i + 64, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) return i + 32; - if (!test_bit(i + 64, sc->keymap) && - (test_bit(i , sc->keymap) || - test_bit(i + 32, sc->keymap) || - test_bit(i + 64 + 32, sc->keymap))) + if (!test_bit(i + 64, common->keymap) && + (test_bit(i , common->keymap) || + test_bit(i + 32, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) return i + 64; - if (!test_bit(i + 64 + 32, sc->keymap) && - (test_bit(i, sc->keymap) || - test_bit(i + 32, sc->keymap) || - test_bit(i + 64, sc->keymap))) + if (!test_bit(i + 64 + 32, common->keymap) && + (test_bit(i, common->keymap) || + test_bit(i + 32, common->keymap) || + test_bit(i + 64, common->keymap))) return i + 64 + 32; } } else { - for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) { - if (!test_bit(i, sc->keymap) && - test_bit(i + 64, sc->keymap)) + for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { + if (!test_bit(i, common->keymap) && + test_bit(i + 64, common->keymap)) return i; - if (test_bit(i, sc->keymap) && - !test_bit(i + 64, sc->keymap)) + if (test_bit(i, common->keymap) && + !test_bit(i + 64, common->keymap)) return i + 64; } } /* No partially used TKIP slots, pick any available slot */ - for (i = IEEE80211_WEP_NKID; i < sc->keymax; i++) { + for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) { /* Do not allow slots that could be needed for TKIP group keys * to be used. This limitation could be removed if we know that * TKIP will not be used. */ if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) continue; - if (sc->splitmic) { + if (common->splitmic) { if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) continue; if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) continue; } - if (!test_bit(i, sc->keymap)) + if (!test_bit(i, common->keymap)) return i; /* Found a free slot for a key */ } @@ -792,11 +835,12 @@ static int ath_reserve_key_cache_slot(struct ath_softc *sc) return -1; } -static int ath_key_config(struct ath_softc *sc, +static int ath_key_config(struct ath_common *common, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { + struct ath_hw *ah = common->ah; struct ath9k_keyval hk; const u8 *mac = NULL; int ret = 0; @@ -842,54 +886,57 @@ static int ath_key_config(struct ath_softc *sc, mac = sta->addr; if (key->alg == ALG_TKIP) - idx = ath_reserve_key_cache_slot_tkip(sc); + idx = ath_reserve_key_cache_slot_tkip(common); else - idx = ath_reserve_key_cache_slot(sc); + idx = ath_reserve_key_cache_slot(common); if (idx < 0) return -ENOSPC; /* no free key cache entries */ } if (key->alg == ALG_TKIP) - ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac, + ret = ath_setkey_tkip(common, idx, key->key, &hk, mac, vif->type == NL80211_IFTYPE_AP); else - ret = ath9k_hw_set_keycache_entry(sc->sc_ah, idx, &hk, mac); + ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac); if (!ret) return -EIO; - set_bit(idx, sc->keymap); + set_bit(idx, common->keymap); if (key->alg == ALG_TKIP) { - set_bit(idx + 64, sc->keymap); - if (sc->splitmic) { - set_bit(idx + 32, sc->keymap); - set_bit(idx + 64 + 32, sc->keymap); + set_bit(idx + 64, common->keymap); + if (common->splitmic) { + set_bit(idx + 32, common->keymap); + set_bit(idx + 64 + 32, common->keymap); } } return idx; } -static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key) +static void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key) { - ath9k_hw_keyreset(sc->sc_ah, key->hw_key_idx); + struct ath_hw *ah = common->ah; + + ath9k_hw_keyreset(ah, key->hw_key_idx); if (key->hw_key_idx < IEEE80211_WEP_NKID) return; - clear_bit(key->hw_key_idx, sc->keymap); + clear_bit(key->hw_key_idx, common->keymap); if (key->alg != ALG_TKIP) return; - clear_bit(key->hw_key_idx + 64, sc->keymap); - if (sc->splitmic) { - clear_bit(key->hw_key_idx + 32, sc->keymap); - clear_bit(key->hw_key_idx + 64 + 32, sc->keymap); + clear_bit(key->hw_key_idx + 64, common->keymap); + if (common->splitmic) { + clear_bit(key->hw_key_idx + 32, common->keymap); + clear_bit(key->hw_key_idx + 64 + 32, common->keymap); } } static void setup_ht_cap(struct ath_softc *sc, struct ieee80211_sta_ht_cap *ht_info) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 tx_streams, rx_streams; ht_info->ht_supported = true; @@ -903,12 +950,15 @@ static void setup_ht_cap(struct ath_softc *sc, /* set up supported mcs set */ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - tx_streams = !(sc->tx_chainmask & (sc->tx_chainmask - 1)) ? 1 : 2; - rx_streams = !(sc->rx_chainmask & (sc->rx_chainmask - 1)) ? 1 : 2; + tx_streams = !(common->tx_chainmask & (common->tx_chainmask - 1)) ? + 1 : 2; + rx_streams = !(common->rx_chainmask & (common->rx_chainmask - 1)) ? + 1 : 2; if (tx_streams != rx_streams) { - DPRINTF(sc, ATH_DBG_CONFIG, "TX streams %d, RX streams: %d\n", - tx_streams, rx_streams); + ath_print(common, ATH_DBG_CONFIG, + "TX streams %d, RX streams: %d\n", + tx_streams, rx_streams); ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; ht_info->mcs.tx_params |= ((tx_streams - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); @@ -925,14 +975,17 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) { + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); if (bss_conf->assoc) { - DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n", - bss_conf->aid, sc->curbssid); + ath_print(common, ATH_DBG_CONFIG, + "Bss Info ASSOC %d, bssid: %pM\n", + bss_conf->aid, common->curbssid); /* New association, store aid */ - sc->curaid = bss_conf->aid; - ath9k_hw_write_associd(sc); + common->curaid = bss_conf->aid; + ath9k_hw_write_associd(ah); /* * Request a re-configuration of Beacon related timers @@ -947,12 +1000,12 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, /* Reset rssi stats */ sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; - ath_start_ani(sc); + ath_start_ani(common); } else { - DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISASSOC\n"); - sc->curaid = 0; + ath_print(common, ATH_DBG_CONFIG, "Bss Info DISASSOC\n"); + common->curaid = 0; /* Stop ANI */ - del_timer_sync(&sc->ani.timer); + del_timer_sync(&common->ani.timer); } } @@ -1042,8 +1095,8 @@ static int ath_register_led(struct ath_softc *sc, struct ath_led *led, ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); if (ret) - DPRINTF(sc, ATH_DBG_FATAL, - "Failed to register led:%s", led->name); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "Failed to register led:%s", led->name); else led->registered = 1; return ret; @@ -1124,10 +1177,11 @@ fail: ath_deinit_leds(sc); } -void ath_radio_enable(struct ath_softc *sc) +void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; - struct ieee80211_channel *channel = sc->hw->conf.channel; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_channel *channel = hw->conf.channel; int r; ath9k_ps_wakeup(sc); @@ -1139,17 +1193,17 @@ void ath_radio_enable(struct ath_softc *sc) spin_lock_bh(&sc->sc_resetlock); r = ath9k_hw_reset(ah, ah->curchan, false); if (r) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to reset channel %u (%uMhz) ", - "reset status %d\n", - channel->center_freq, r); + ath_print(common, ATH_DBG_FATAL, + "Unable to reset channel %u (%uMhz) ", + "reset status %d\n", + channel->center_freq, r); } spin_unlock_bh(&sc->sc_resetlock); ath_update_txpow(sc); if (ath_startrecv(sc) != 0) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to restart recv logic\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to restart recv logic\n"); return; } @@ -1164,18 +1218,18 @@ void ath_radio_enable(struct ath_softc *sc) AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ath9k_hw_set_gpio(ah, ah->led_pin, 0); - ieee80211_wake_queues(sc->hw); + ieee80211_wake_queues(hw); ath9k_ps_restore(sc); } -void ath_radio_disable(struct ath_softc *sc) +void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; - struct ieee80211_channel *channel = sc->hw->conf.channel; + struct ieee80211_channel *channel = hw->conf.channel; int r; ath9k_ps_wakeup(sc); - ieee80211_stop_queues(sc->hw); + ieee80211_stop_queues(hw); /* Disable LED */ ath9k_hw_set_gpio(ah, ah->led_pin, 1); @@ -1189,22 +1243,22 @@ void ath_radio_disable(struct ath_softc *sc) ath_flushrecv(sc); /* flush recv queue */ if (!ah->curchan) - ah->curchan = ath_get_curchannel(sc, sc->hw); + ah->curchan = ath_get_curchannel(sc, hw); spin_lock_bh(&sc->sc_resetlock); r = ath9k_hw_reset(ah, ah->curchan, false); if (r) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to reset channel %u (%uMhz) " - "reset status %d\n", - channel->center_freq, r); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "Unable to reset channel %u (%uMhz) " + "reset status %d\n", + channel->center_freq, r); } spin_unlock_bh(&sc->sc_resetlock); ath9k_hw_phy_disable(ah); ath9k_hw_configpcipowersave(ah, 1, 1); ath9k_ps_restore(sc); - ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); + ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP); } /*******************/ @@ -1236,23 +1290,26 @@ static void ath_start_rfkill_poll(struct ath_softc *sc) wiphy_rfkill_start_polling(sc->hw->wiphy); } -void ath_cleanup(struct ath_softc *sc) +static void ath9k_uninit_hw(struct ath_softc *sc) { - ath_detach(sc); - free_irq(sc->irq, sc); - ath_bus_cleanup(sc); - kfree(sc->sec_wiphy); - ieee80211_free_hw(sc->hw); + struct ath_hw *ah = sc->sc_ah; + + BUG_ON(!ah); + + ath9k_exit_debug(ah); + ath9k_hw_detach(ah); + sc->sc_ah = NULL; } -void ath_detach(struct ath_softc *sc) +static void ath_clean_core(struct ath_softc *sc) { struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; int i = 0; ath9k_ps_wakeup(sc); - DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n"); + dev_dbg(sc->dev, "Detach ATH hw\n"); ath_deinit_leds(sc); wiphy_rfkill_stop_polling(sc->hw->wiphy); @@ -1273,20 +1330,36 @@ void ath_detach(struct ath_softc *sc) tasklet_kill(&sc->bcon_tasklet); if (!(sc->sc_flags & SC_OP_INVALID)) - ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + ath9k_setpower(sc, ATH9K_PM_AWAKE); /* cleanup tx queues */ for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); - if ((sc->btcoex_info.no_stomp_timer) && - sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) - ath_gen_timer_free(sc->sc_ah, sc->btcoex_info.no_stomp_timer); + if ((sc->btcoex.no_stomp_timer) && + ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath_gen_timer_free(ah, sc->btcoex.no_stomp_timer); +} - ath9k_hw_detach(sc->sc_ah); - sc->sc_ah = NULL; - ath9k_exit_debug(sc); +void ath_detach(struct ath_softc *sc) +{ + ath_clean_core(sc); + ath9k_uninit_hw(sc); +} + +void ath_cleanup(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + + ath_clean_core(sc); + free_irq(sc->irq, sc); + ath_bus_cleanup(common); + kfree(sc->sec_wiphy); + ieee80211_free_hw(sc->hw); + + ath9k_uninit_hw(sc); } static int ath9k_reg_notifier(struct wiphy *wiphy, @@ -1295,29 +1368,245 @@ static int ath9k_reg_notifier(struct wiphy *wiphy, struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - struct ath_regulatory *reg = &sc->common.regulatory; + struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah); return ath_reg_notifier_apply(wiphy, request, reg); } /* + * Detects if there is any priority bt traffic + */ +static void ath_detect_bt_priority(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio)) + btcoex->bt_priority_cnt++; + + if (time_after(jiffies, btcoex->bt_priority_time + + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { + if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX, + "BT priority traffic detected"); + sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; + } else { + sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; + } + + btcoex->bt_priority_cnt = 0; + btcoex->bt_priority_time = jiffies; + } +} + +/* + * Configures appropriate weight based on stomp type. + */ +static void ath9k_btcoex_bt_stomp(struct ath_softc *sc, + enum ath_stomp_type stomp_type) +{ + struct ath_hw *ah = sc->sc_ah; + + switch (stomp_type) { + case ATH_BTCOEX_STOMP_ALL: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_ALL_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_LOW: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_NONE: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_NONE_WLAN_WGHT); + break; + default: + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Invalid Stomptype\n"); + break; + } + + ath9k_hw_btcoex_enable(ah); +} + +static void ath9k_gen_timer_start(struct ath_hw *ah, + struct ath_gen_timer *timer, + u32 timer_next, + u32 timer_period) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + + ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period); + + if ((sc->imask & ATH9K_INT_GENTIMER) == 0) { + ath9k_hw_set_interrupts(ah, 0); + sc->imask |= ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah, sc->imask); + } +} + +static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; + + ath9k_hw_gen_timer_stop(ah, timer); + + /* if no timer is enabled, turn off interrupt mask */ + if (timer_table->timer_mask.val == 0) { + ath9k_hw_set_interrupts(ah, 0); + sc->imask &= ~ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah, sc->imask); + } +} + +/* + * This is the master bt coex timer which runs for every + * 45ms, bt traffic will be given priority during 55% of this + * period while wlan gets remaining 45% + */ +static void ath_btcoex_period_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + + ath_detect_bt_priority(sc); + + spin_lock_bh(&btcoex->btcoex_lock); + + ath9k_btcoex_bt_stomp(sc, btcoex->bt_stomp_type); + + spin_unlock_bh(&btcoex->btcoex_lock); + + if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) { + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); + + ath9k_gen_timer_start(ah, + btcoex->no_stomp_timer, + (ath9k_hw_gettsf32(ah) + + btcoex->btcoex_no_stomp), + btcoex->btcoex_no_stomp * 10); + btcoex->hw_timer_enabled = true; + } + + mod_timer(&btcoex->period_timer, jiffies + + msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); +} + +/* + * Generic tsf based hw timer which configures weight + * registers to time slice between wlan and bt traffic + */ +static void ath_btcoex_no_stomp_timer(void *arg) +{ + struct ath_softc *sc = (struct ath_softc *)arg; + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "no stomp timer running \n"); + + spin_lock_bh(&btcoex->btcoex_lock); + + if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW) + ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE); + else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) + ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW); + + spin_unlock_bh(&btcoex->btcoex_lock); +} + +static int ath_init_btcoex_timer(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + + btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; + btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * + btcoex->btcoex_period / 100; + + setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, + (unsigned long) sc); + + spin_lock_init(&btcoex->btcoex_lock); + + btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah, + ath_btcoex_no_stomp_timer, + ath_btcoex_no_stomp_timer, + (void *) sc, AR_FIRST_NDP_TIMER); + + if (!btcoex->no_stomp_timer) + return -ENOMEM; + + return 0; +} + +/* + * Read and write, they both share the same lock. We do this to serialize + * reads and writes on Atheros 802.11n PCI devices only. This is required + * as the FIFO on these devices can only accept sanely 2 requests. After + * that the device goes bananas. Serializing the reads/writes prevents this + * from happening. + */ + +static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&sc->sc_serial_rw, flags); + iowrite32(val, sc->mem + reg_offset); + spin_unlock_irqrestore(&sc->sc_serial_rw, flags); + } else + iowrite32(val, sc->mem + reg_offset); +} + +static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + u32 val; + + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&sc->sc_serial_rw, flags); + val = ioread32(sc->mem + reg_offset); + spin_unlock_irqrestore(&sc->sc_serial_rw, flags); + } else + val = ioread32(sc->mem + reg_offset); + return val; +} + +static const struct ath_ops ath9k_common_ops = { + .read = ath9k_ioread32, + .write = ath9k_iowrite32, +}; + +/* * Initialize and fill ath_softc, ath_sofct is the * "Software Carrier" struct. Historically it has existed * to allow the separation between hardware specific * variables (now in ath_hw) and driver specific variables. */ -static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid) +static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, + const struct ath_bus_ops *bus_ops) { struct ath_hw *ah = NULL; + struct ath_common *common; int r = 0, i; int csz = 0; + int qnum; /* XXX: hardware will not be ready until ath_open() being called */ sc->sc_flags |= SC_OP_INVALID; - if (ath9k_init_debug(sc) < 0) - printk(KERN_ERR "Unable to create debugfs files\n"); - spin_lock_init(&sc->wiphy_lock); spin_lock_init(&sc->sc_resetlock); spin_lock_init(&sc->sc_serial_rw); @@ -1328,75 +1617,80 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid) tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, (unsigned long)sc); - /* - * Cache line size is used to size and align various - * structures used to communicate with the hardware. - */ - ath_read_cachesize(sc, &csz); - /* XXX assert csz is non-zero */ - sc->common.cachelsz = csz << 2; /* convert to bytes */ - ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); - if (!ah) { - r = -ENOMEM; - goto bad_no_ah; - } + if (!ah) + return -ENOMEM; - ah->ah_sc = sc; ah->hw_version.devid = devid; ah->hw_version.subsysid = subsysid; sc->sc_ah = ah; + common = ath9k_hw_common(ah); + common->ops = &ath9k_common_ops; + common->bus_ops = bus_ops; + common->ah = ah; + common->hw = sc->hw; + common->priv = sc; + common->debug_mask = ath9k_debug; + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + ath_read_cachesize(common, &csz); + /* XXX assert csz is non-zero */ + common->cachelsz = csz << 2; /* convert to bytes */ + r = ath9k_hw_init(ah); if (r) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to initialize hardware; " - "initialization status: %d\n", r); - goto bad; + ath_print(common, ATH_DBG_FATAL, + "Unable to initialize hardware; " + "initialization status: %d\n", r); + goto bad_free_hw; + } + + if (ath9k_init_debug(ah) < 0) { + ath_print(common, ATH_DBG_FATAL, + "Unable to create debugfs files\n"); + goto bad_free_hw; } /* Get the hardware key cache size. */ - sc->keymax = ah->caps.keycache_size; - if (sc->keymax > ATH_KEYMAX) { - DPRINTF(sc, ATH_DBG_ANY, - "Warning, using only %u entries in %u key cache\n", - ATH_KEYMAX, sc->keymax); - sc->keymax = ATH_KEYMAX; + common->keymax = ah->caps.keycache_size; + if (common->keymax > ATH_KEYMAX) { + ath_print(common, ATH_DBG_ANY, + "Warning, using only %u entries in %u key cache\n", + ATH_KEYMAX, common->keymax); + common->keymax = ATH_KEYMAX; } /* * Reset the key cache since some parts do not * reset the contents on initial power up. */ - for (i = 0; i < sc->keymax; i++) + for (i = 0; i < common->keymax; i++) ath9k_hw_keyreset(ah, (u16) i); /* default to MONITOR mode */ sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR; - /* Setup rate tables */ - - ath_rate_attach(sc); - ath_setup_rates(sc, IEEE80211_BAND_2GHZ); - ath_setup_rates(sc, IEEE80211_BAND_5GHZ); - /* * Allocate hardware transmit queues: one queue for * beacon frames and one data queue for each QoS * priority. Note that the hal handles reseting * these queues at the needed time. */ - sc->beacon.beaconq = ath_beaconq_setup(ah); + sc->beacon.beaconq = ath9k_hw_beaconq_setup(ah); if (sc->beacon.beaconq == -1) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to setup a beacon xmit queue\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to setup a beacon xmit queue\n"); r = -EIO; goto bad2; } sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); if (sc->beacon.cabq == NULL) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to setup CAB xmit queue\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to setup CAB xmit queue\n"); r = -EIO; goto bad2; } @@ -1410,27 +1704,27 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid) /* Setup data queues */ /* NB: ensure BK queue is the lowest priority h/w queue */ if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to setup xmit queue for BK traffic\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BK traffic\n"); r = -EIO; goto bad2; } if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to setup xmit queue for BE traffic\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BE traffic\n"); r = -EIO; goto bad2; } if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to setup xmit queue for VI traffic\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VI traffic\n"); r = -EIO; goto bad2; } if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to setup xmit queue for VO traffic\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VO traffic\n"); r = -EIO; goto bad2; } @@ -1438,8 +1732,8 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid) /* Initializes the noise floor to a reasonable default value. * Later on this will be updated during ANI processing. */ - sc->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; - setup_timer(&sc->ani.timer, ath_ani_calibrate, (unsigned long)sc); + common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; + setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, ATH9K_CIPHER_TKIP, NULL)) { @@ -1465,7 +1759,7 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid) ATH9K_CIPHER_MIC, NULL) && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT, 0, NULL)) - sc->splitmic = 1; + common->splitmic = 1; /* turn on mcast key search if possible */ if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) @@ -1480,14 +1774,14 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid) sc->sc_flags |= SC_OP_RXAGGR; } - sc->tx_chainmask = ah->caps.tx_chainmask; - sc->rx_chainmask = ah->caps.rx_chainmask; + common->tx_chainmask = ah->caps.tx_chainmask; + common->rx_chainmask = ah->caps.rx_chainmask; ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL); sc->rx.defant = ath9k_hw_getdefantenna(ah); if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN); + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */ @@ -1499,26 +1793,45 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid) /* setup channels and rates */ - sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; - sc->sbands[IEEE80211_BAND_2GHZ].bitrates = - sc->rates[IEEE80211_BAND_2GHZ]; - sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; - sc->sbands[IEEE80211_BAND_2GHZ].n_channels = - ARRAY_SIZE(ath9k_2ghz_chantable); + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { + sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; + sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + sc->sbands[IEEE80211_BAND_2GHZ].n_channels = + ARRAY_SIZE(ath9k_2ghz_chantable); + sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; + sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates); + } if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable; - sc->sbands[IEEE80211_BAND_5GHZ].bitrates = - sc->rates[IEEE80211_BAND_5GHZ]; sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; sc->sbands[IEEE80211_BAND_5GHZ].n_channels = ARRAY_SIZE(ath9k_5ghz_chantable); + sc->sbands[IEEE80211_BAND_5GHZ].bitrates = + ath9k_legacy_rates + 4; + sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates) - 4; } - if (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) { - r = ath9k_hw_btcoex_init(ah); + switch (ah->btcoex_hw.scheme) { + case ATH_BTCOEX_CFG_NONE: + break; + case ATH_BTCOEX_CFG_2WIRE: + ath9k_hw_btcoex_init_2wire(ah); + break; + case ATH_BTCOEX_CFG_3WIRE: + ath9k_hw_btcoex_init_3wire(ah); + r = ath_init_btcoex_timer(sc); if (r) goto bad2; + qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); + ath9k_hw_init_btcoex_hw(ah, qnum); + sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + break; + default: + WARN_ON(1); + break; } return 0; @@ -1527,12 +1840,9 @@ bad2: for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); -bad: - ath9k_hw_detach(ah); - sc->sc_ah = NULL; -bad_no_ah: - ath9k_exit_debug(sc); +bad_free_hw: + ath9k_uninit_hw(sc); return r; } @@ -1555,7 +1865,7 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); - hw->wiphy->ps_default = false; + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->queues = 4; hw->max_rates = 4; @@ -1568,43 +1878,53 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->rate_control_algorithm = "ath9k_rate_control"; - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &sc->sbands[IEEE80211_BAND_2GHZ]; + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &sc->sbands[IEEE80211_BAND_2GHZ]; if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &sc->sbands[IEEE80211_BAND_5GHZ]; } /* Device driver core initialization */ -int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid) +int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, + const struct ath_bus_ops *bus_ops) { struct ieee80211_hw *hw = sc->hw; + struct ath_common *common; + struct ath_hw *ah; int error = 0, i; struct ath_regulatory *reg; - DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n"); + dev_dbg(sc->dev, "Attach ATH hw\n"); - error = ath_init_softc(devid, sc, subsysid); + error = ath_init_softc(devid, sc, subsysid, bus_ops); if (error != 0) return error; + ah = sc->sc_ah; + common = ath9k_hw_common(ah); + /* get mac address from hardware and set in mac80211 */ - SET_IEEE80211_PERM_ADDR(hw, sc->sc_ah->macaddr); + SET_IEEE80211_PERM_ADDR(hw, common->macaddr); ath_set_hw_capab(sc, hw); - error = ath_regd_init(&sc->common.regulatory, sc->hw->wiphy, + error = ath_regd_init(&common->regulatory, sc->hw->wiphy, ath9k_reg_notifier); if (error) return error; - reg = &sc->common.regulatory; + reg = &common->regulatory; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { - setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); - if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) - setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); + if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + if (test_bit(ATH9K_MODE_11G, ah->caps.wireless_modes)) + setup_ht_cap(sc, + &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); + if (test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes)) + setup_ht_cap(sc, + &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); } /* initialize tx/rx engine */ @@ -1641,9 +1961,7 @@ error_attach: if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); - ath9k_hw_detach(sc->sc_ah); - sc->sc_ah = NULL; - ath9k_exit_debug(sc); + ath9k_uninit_hw(sc); return error; } @@ -1651,6 +1969,7 @@ error_attach: int ath_reset(struct ath_softc *sc, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_hw *hw = sc->hw; int r; @@ -1662,12 +1981,13 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) spin_lock_bh(&sc->sc_resetlock); r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false); if (r) - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to reset hardware; reset status %d\n", r); + ath_print(common, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %d\n", r); spin_unlock_bh(&sc->sc_resetlock); if (ath_startrecv(sc) != 0) - DPRINTF(sc, ATH_DBG_FATAL, "Unable to start recv logic\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to start recv logic\n"); /* * We may be doing a reset in response to a request @@ -1710,19 +2030,20 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) #define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) #define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) - + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_desc *ds; struct ath_buf *bf; int i, bsize, error; - DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n", - name, nbuf, ndesc); + ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n", + name, nbuf, ndesc); INIT_LIST_HEAD(head); /* ath_desc must be a multiple of DWORDs */ if ((sizeof(struct ath_desc) % 4) != 0) { - DPRINTF(sc, ATH_DBG_FATAL, "ath_desc not DWORD aligned\n"); - ASSERT((sizeof(struct ath_desc) % 4) == 0); + ath_print(common, ATH_DBG_FATAL, + "ath_desc not DWORD aligned\n"); + BUG_ON((sizeof(struct ath_desc) % 4) != 0); error = -ENOMEM; goto fail; } @@ -1755,9 +2076,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, goto fail; } ds = dd->dd_desc; - DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", - name, ds, (u32) dd->dd_desc_len, - ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); + ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", + name, ds, (u32) dd->dd_desc_len, + ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); /* allocate buffers */ bsize = sizeof(struct ath_buf) * nbuf; @@ -1780,7 +2101,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, * descriptor fetch. */ while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { - ASSERT((caddr_t) bf->bf_desc < + BUG_ON((caddr_t) bf->bf_desc >= ((caddr_t) dd->dd_desc + dd->dd_desc_len)); @@ -1884,31 +2205,50 @@ void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; } - sc->tx_chan_width = ATH9K_HT_MACMODE_20; - - if (conf_is_ht(conf)) { - if (conf_is_ht40(conf)) - sc->tx_chan_width = ATH9K_HT_MACMODE_2040; - + if (conf_is_ht(conf)) ichan->chanmode = ath_get_extchanmode(sc, chan, conf->channel_type); - } } /**********************/ /* mac80211 callbacks */ /**********************/ +/* + * (Re)start btcoex timers + */ +static void ath9k_btcoex_timer_resume(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Starting btcoex timers"); + + /* make sure duty cycle timer is also stopped when resuming */ + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); + + btcoex->bt_priority_cnt = 0; + btcoex->bt_priority_time = jiffies; + sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; + + mod_timer(&btcoex->period_timer, jiffies); +} + static int ath9k_start(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *curchan = hw->conf.channel; struct ath9k_channel *init_channel; int r; - DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with " - "initial channel: %d MHz\n", curchan->center_freq); + ath_print(common, ATH_DBG_CONFIG, + "Starting driver with initial channel: %d MHz\n", + curchan->center_freq); mutex_lock(&sc->mutex); @@ -1940,7 +2280,7 @@ static int ath9k_start(struct ieee80211_hw *hw) init_channel = ath_get_curchannel(sc, hw); /* Reset SERDES registers */ - ath9k_hw_configpcipowersave(sc->sc_ah, 0, 0); + ath9k_hw_configpcipowersave(ah, 0, 0); /* * The basic interface to setting the hardware in a good @@ -1950,12 +2290,12 @@ static int ath9k_start(struct ieee80211_hw *hw) * and then setup of the interrupt mask. */ spin_lock_bh(&sc->sc_resetlock); - r = ath9k_hw_reset(sc->sc_ah, init_channel, false); + r = ath9k_hw_reset(ah, init_channel, false); if (r) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to reset hardware; reset status %d " - "(freq %u MHz)\n", r, - curchan->center_freq); + ath_print(common, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %d " + "(freq %u MHz)\n", r, + curchan->center_freq); spin_unlock_bh(&sc->sc_resetlock); goto mutex_unlock; } @@ -1975,7 +2315,8 @@ static int ath9k_start(struct ieee80211_hw *hw) * here except setup the interrupt mask. */ if (ath_startrecv(sc) != 0) { - DPRINTF(sc, ATH_DBG_FATAL, "Unable to start recv logic\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to start recv logic\n"); r = -EIO; goto mutex_unlock; } @@ -1985,10 +2326,10 @@ static int ath9k_start(struct ieee80211_hw *hw) | ATH9K_INT_RXEOL | ATH9K_INT_RXORN | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_GTT) + if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT) sc->imask |= ATH9K_INT_GTT; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) + if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) sc->imask |= ATH9K_INT_CST; ath_cache_conf_rate(sc, &hw->conf); @@ -1997,21 +2338,22 @@ static int ath9k_start(struct ieee80211_hw *hw) /* Disable BMISS interrupt when we're not associated */ sc->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); - ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + ath9k_hw_set_interrupts(ah, sc->imask); ieee80211_wake_queues(hw); ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); - if ((sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) && - !(sc->sc_flags & SC_OP_BTCOEX_ENABLED)) { - ath_btcoex_set_weight(&sc->btcoex_info, AR_BT_COEX_WGHT, - AR_STOMP_LOW_WLAN_WGHT); - ath9k_hw_btcoex_enable(sc->sc_ah); + if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) && + !ah->btcoex_hw.enabled) { + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + ath9k_hw_btcoex_enable(ah); - ath_pcie_aspm_disable(sc); - if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) - ath_btcoex_timer_resume(sc, &sc->btcoex_info); + if (common->bus_ops->bt_coex_prep) + common->bus_ops->bt_coex_prep(common); + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath9k_btcoex_timer_resume(sc); } mutex_unlock: @@ -2026,17 +2368,19 @@ static int ath9k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_tx_control txctl; - int hdrlen, padsize; + int padpos, padsize; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) { - printk(KERN_DEBUG "ath9k: %s: TX in unexpected wiphy state " - "%d\n", wiphy_name(hw->wiphy), aphy->state); + ath_print(common, ATH_DBG_XMIT, + "ath9k: %s: TX in unexpected wiphy state " + "%d\n", wiphy_name(hw->wiphy), aphy->state); goto exit; } if (sc->ps_enabled) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; /* * mac80211 does not set PM field for normal data frames, so we * need to update that based on the current PS mode. @@ -2044,8 +2388,8 @@ static int ath9k_tx(struct ieee80211_hw *hw, if (ieee80211_is_data(hdr->frame_control) && !ieee80211_is_nullfunc(hdr->frame_control) && !ieee80211_has_pm(hdr->frame_control)) { - DPRINTF(sc, ATH_DBG_PS, "Add PM=1 for a TX frame " - "while in PS mode\n"); + ath_print(common, ATH_DBG_PS, "Add PM=1 for a TX frame " + "while in PS mode\n"); hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); } } @@ -2056,15 +2400,15 @@ static int ath9k_tx(struct ieee80211_hw *hw, * power save mode. Need to wake up hardware for the TX to be * completed and if needed, also for RX of buffered frames. */ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ath9k_ps_wakeup(sc); ath9k_hw_setrxabort(sc->sc_ah, 0); if (ieee80211_is_pspoll(hdr->frame_control)) { - DPRINTF(sc, ATH_DBG_PS, "Sending PS-Poll to pick a " - "buffered frame\n"); + ath_print(common, ATH_DBG_PS, + "Sending PS-Poll to pick a buffered frame\n"); sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA; } else { - DPRINTF(sc, ATH_DBG_PS, "Wake up to complete TX\n"); + ath_print(common, ATH_DBG_PS, + "Wake up to complete TX\n"); sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK; } /* @@ -2083,7 +2427,6 @@ static int ath9k_tx(struct ieee80211_hw *hw, * BSSes. */ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) sc->tx.seq_no += 0x10; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); @@ -2091,13 +2434,13 @@ static int ath9k_tx(struct ieee80211_hw *hw, } /* Add the padding after the header if this is not already done */ - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - if (hdrlen & 3) { - padsize = hdrlen % 4; + padpos = ath9k_cmn_padpos(hdr->frame_control); + padsize = padpos & 3; + if (padsize && skb->len>padpos) { if (skb_headroom(skb) < padsize) return -1; skb_push(skb, padsize); - memmove(skb->data, skb->data + padsize, hdrlen); + memmove(skb->data, skb->data + padsize, padpos); } /* Check if a tx queue is available */ @@ -2106,10 +2449,10 @@ static int ath9k_tx(struct ieee80211_hw *hw, if (!txctl.txq) goto exit; - DPRINTF(sc, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb); + ath_print(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb); if (ath_tx_start(hw, skb, &txctl) != 0) { - DPRINTF(sc, ATH_DBG_XMIT, "TX failed\n"); + ath_print(common, ATH_DBG_XMIT, "TX failed\n"); goto exit; } @@ -2119,10 +2462,28 @@ exit: return 0; } +/* + * Pause btcoex timer and bt duty cycle timer + */ +static void ath9k_btcoex_timer_pause(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + del_timer_sync(&btcoex->period_timer); + + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); + + btcoex->hw_timer_enabled = false; +} + static void ath9k_stop(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); mutex_lock(&sc->mutex); @@ -2137,7 +2498,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) } if (sc->sc_flags & SC_OP_INVALID) { - DPRINTF(sc, ATH_DBG_ANY, "Device not present\n"); + ath_print(common, ATH_DBG_ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; } @@ -2147,33 +2508,33 @@ static void ath9k_stop(struct ieee80211_hw *hw) return; /* another wiphy still in use */ } - if (sc->sc_flags & SC_OP_BTCOEX_ENABLED) { - ath9k_hw_btcoex_disable(sc->sc_ah); - if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) - ath_btcoex_timer_pause(sc, &sc->btcoex_info); + if (ah->btcoex_hw.enabled) { + ath9k_hw_btcoex_disable(ah); + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath9k_btcoex_timer_pause(sc); } /* make sure h/w will not generate any interrupt * before setting the invalid flag. */ - ath9k_hw_set_interrupts(sc->sc_ah, 0); + ath9k_hw_set_interrupts(ah, 0); if (!(sc->sc_flags & SC_OP_INVALID)) { ath_drain_all_txq(sc, false); ath_stoprecv(sc); - ath9k_hw_phy_disable(sc->sc_ah); + ath9k_hw_phy_disable(ah); } else sc->rx.rxlink = NULL; /* disable HAL and put h/w to sleep */ - ath9k_hw_disable(sc->sc_ah); - ath9k_hw_configpcipowersave(sc->sc_ah, 1, 1); - ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); + ath9k_hw_disable(ah); + ath9k_hw_configpcipowersave(ah, 1, 1); + ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP); sc->sc_flags |= SC_OP_INVALID; mutex_unlock(&sc->mutex); - DPRINTF(sc, ATH_DBG_CONFIG, "Driver halt\n"); + ath_print(common, ATH_DBG_CONFIG, "Driver halt\n"); } static int ath9k_add_interface(struct ieee80211_hw *hw, @@ -2181,6 +2542,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)conf->vif->drv_priv; enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED; int ret = 0; @@ -2207,13 +2569,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, ic_opmode = conf->type; break; default: - DPRINTF(sc, ATH_DBG_FATAL, + ath_print(common, ATH_DBG_FATAL, "Interface type %d not yet supported\n", conf->type); ret = -EOPNOTSUPP; goto out; } - DPRINTF(sc, ATH_DBG_CONFIG, "Attach a VIF of type: %d\n", ic_opmode); + ath_print(common, ATH_DBG_CONFIG, + "Attach a VIF of type: %d\n", ic_opmode); /* Set the VIF opmode */ avp->av_opmode = ic_opmode; @@ -2251,7 +2614,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, if (conf->type == NL80211_IFTYPE_AP || conf->type == NL80211_IFTYPE_ADHOC || conf->type == NL80211_IFTYPE_MONITOR) - ath_start_ani(sc); + ath_start_ani(common); out: mutex_unlock(&sc->mutex); @@ -2263,15 +2626,16 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)conf->vif->drv_priv; int i; - DPRINTF(sc, ATH_DBG_CONFIG, "Detach Interface\n"); + ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n"); mutex_lock(&sc->mutex); /* Stop ANI */ - del_timer_sync(&sc->ani.timer); + del_timer_sync(&common->ani.timer); /* Reclaim beacon resources */ if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || @@ -2301,32 +2665,55 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_conf *conf = &hw->conf; struct ath_hw *ah = sc->sc_ah; - bool all_wiphys_idle = false, disable_radio = false; + bool disable_radio; mutex_lock(&sc->mutex); - /* Leave this as the first check */ + /* + * Leave this as the first check because we need to turn on the + * radio if it was disabled before prior to processing the rest + * of the changes. Likewise we must only disable the radio towards + * the end. + */ if (changed & IEEE80211_CONF_CHANGE_IDLE) { + bool enable_radio; + bool all_wiphys_idle; + bool idle = !!(conf->flags & IEEE80211_CONF_IDLE); spin_lock_bh(&sc->wiphy_lock); all_wiphys_idle = ath9k_all_wiphys_idle(sc); + ath9k_set_wiphy_idle(aphy, idle); + + if (!idle && all_wiphys_idle) + enable_radio = true; + + /* + * After we unlock here its possible another wiphy + * can be re-renabled so to account for that we will + * only disable the radio toward the end of this routine + * if by then all wiphys are still idle. + */ spin_unlock_bh(&sc->wiphy_lock); - if (conf->flags & IEEE80211_CONF_IDLE){ - if (all_wiphys_idle) - disable_radio = true; - } - else if (all_wiphys_idle) { - ath_radio_enable(sc); - DPRINTF(sc, ATH_DBG_CONFIG, - "not-idle: enabling radio\n"); + if (enable_radio) { + ath_radio_enable(sc, hw); + ath_print(common, ATH_DBG_CONFIG, + "not-idle: enabling radio\n"); } } + /* + * We just prepare to enable PS. We have to wait until our AP has + * ACK'd our null data frame to disable RX otherwise we'll ignore + * those ACKs and end up retransmitting the same null data frames. + * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode. + */ if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { + sc->sc_flags |= SC_OP_PS_ENABLED; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) { @@ -2334,12 +2721,21 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); } - ath9k_hw_setrxabort(sc->sc_ah, 1); } - sc->ps_enabled = true; + /* + * At this point we know hardware has received an ACK + * of a previously sent null data frame. + */ + if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) { + sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED; + sc->ps_enabled = true; + ath9k_hw_setrxabort(sc->sc_ah, 1); + } } else { sc->ps_enabled = false; - ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + sc->sc_flags &= ~(SC_OP_PS_ENABLED | + SC_OP_NULLFUNC_COMPLETED); + ath9k_setpower(sc, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { ath9k_hw_setrxabort(sc->sc_ah, 0); @@ -2374,8 +2770,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) goto skip_chan_change; } - DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n", - curchan->center_freq); + ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n", + curchan->center_freq); /* XXX: remove me eventualy */ ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]); @@ -2383,7 +2779,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ath_update_chainmask(sc, conf_is_ht(conf)); if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { - DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n"); + ath_print(common, ATH_DBG_FATAL, + "Unable to set channel\n"); mutex_unlock(&sc->mutex); return -EINVAL; } @@ -2393,9 +2790,13 @@ skip_chan_change: if (changed & IEEE80211_CONF_CHANGE_POWER) sc->config.txpowlimit = 2 * conf->power_level; + spin_lock_bh(&sc->wiphy_lock); + disable_radio = ath9k_all_wiphys_idle(sc); + spin_unlock_bh(&sc->wiphy_lock); + if (disable_radio) { - DPRINTF(sc, ATH_DBG_CONFIG, "idle: disabling radio\n"); - ath_radio_disable(sc); + ath_print(common, ATH_DBG_CONFIG, "idle: disabling radio\n"); + ath_radio_disable(sc, hw); } mutex_unlock(&sc->mutex); @@ -2431,7 +2832,8 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw, ath9k_hw_setrxfilter(sc->sc_ah, rfilt); ath9k_ps_restore(sc); - DPRINTF(sc, ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", rfilt); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG, + "Set HW RX filter: 0x%x\n", rfilt); } static void ath9k_sta_notify(struct ieee80211_hw *hw, @@ -2459,6 +2861,7 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue, { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath9k_tx_queue_info qi; int ret = 0, qnum; @@ -2475,15 +2878,19 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue, qi.tqi_burstTime = params->txop; qnum = ath_get_hal_qnum(queue, sc); - DPRINTF(sc, ATH_DBG_CONFIG, - "Configure tx [queue/halq] [%d/%d], " - "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", - queue, qnum, params->aifs, params->cw_min, - params->cw_max, params->txop); + ath_print(common, ATH_DBG_CONFIG, + "Configure tx [queue/halq] [%d/%d], " + "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", + queue, qnum, params->aifs, params->cw_min, + params->cw_max, params->txop); ret = ath_txq_update(sc, qnum, &qi); if (ret) - DPRINTF(sc, ATH_DBG_FATAL, "TXQ Update failed\n"); + ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n"); + + if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) + if ((qnum == sc->tx.hwq_map[ATH9K_WME_AC_BE]) && !ret) + ath_beaconq_config(sc); mutex_unlock(&sc->mutex); @@ -2498,6 +2905,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); int ret = 0; if (modparam_nohwcrypt) @@ -2505,11 +2913,11 @@ static int ath9k_set_key(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); - DPRINTF(sc, ATH_DBG_CONFIG, "Set HW Key\n"); + ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n"); switch (cmd) { case SET_KEY: - ret = ath_key_config(sc, vif, sta, key); + ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ @@ -2522,7 +2930,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, } break; case DISABLE_KEY: - ath_key_delete(sc, key); + ath_key_delete(common, key); break; default: ret = -EINVAL; @@ -2542,94 +2950,67 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; - u32 rfilt = 0; - int error, i; + int error; mutex_lock(&sc->mutex); - /* - * TODO: Need to decide which hw opmode to use for - * multi-interface cases - * XXX: This belongs into add_interface! - */ - if (vif->type == NL80211_IFTYPE_AP && - ah->opmode != NL80211_IFTYPE_AP) { - ah->opmode = NL80211_IFTYPE_STATION; - ath9k_hw_setopmode(ah); - memcpy(sc->curbssid, sc->sc_ah->macaddr, ETH_ALEN); - sc->curaid = 0; - ath9k_hw_write_associd(sc); - /* Request full reset to get hw opmode changed properly */ - sc->sc_flags |= SC_OP_FULL_RESET; - } - - if ((changed & BSS_CHANGED_BSSID) && - !is_zero_ether_addr(bss_conf->bssid)) { - switch (vif->type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - /* Set BSSID */ - memcpy(sc->curbssid, bss_conf->bssid, ETH_ALEN); - memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN); - sc->curaid = 0; - ath9k_hw_write_associd(sc); - - /* Set aggregation protection mode parameters */ - sc->config.ath_aggr_prot = 0; - - DPRINTF(sc, ATH_DBG_CONFIG, - "RX filter 0x%x bssid %pM aid 0x%x\n", - rfilt, sc->curbssid, sc->curaid); - - /* need to reconfigure the beacon */ - sc->sc_flags &= ~SC_OP_BEACONS ; + if (changed & BSS_CHANGED_BSSID) { + /* Set BSSID */ + memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); + memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN); + common->curaid = 0; + ath9k_hw_write_associd(ah); - break; - default: - break; - } + /* Set aggregation protection mode parameters */ + sc->config.ath_aggr_prot = 0; + + /* Only legacy IBSS for now */ + if (vif->type == NL80211_IFTYPE_ADHOC) + ath_update_chainmask(sc, 0); + + ath_print(common, ATH_DBG_CONFIG, + "BSSID: %pM aid: 0x%x\n", + common->curbssid, common->curaid); + + /* need to reconfigure the beacon */ + sc->sc_flags &= ~SC_OP_BEACONS ; } - if ((vif->type == NL80211_IFTYPE_ADHOC) || - (vif->type == NL80211_IFTYPE_AP) || - (vif->type == NL80211_IFTYPE_MESH_POINT)) { - if ((changed & BSS_CHANGED_BEACON) || - (changed & BSS_CHANGED_BEACON_ENABLED && - bss_conf->enable_beacon)) { - /* - * Allocate and setup the beacon frame. - * - * Stop any previous beacon DMA. This may be - * necessary, for example, when an ibss merge - * causes reconfiguration; we may be called - * with beacon transmission active. - */ - ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); + /* Enable transmission of beacons (AP, IBSS, MESH) */ + if ((changed & BSS_CHANGED_BEACON) || + ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) { + ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); + error = ath_beacon_alloc(aphy, vif); + if (!error) + ath_beacon_config(sc, vif); + } + /* Disable transmission of beacons */ + if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) + ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); + + if (changed & BSS_CHANGED_BEACON_INT) { + sc->beacon_interval = bss_conf->beacon_int; + /* + * In case of AP mode, the HW TSF has to be reset + * when the beacon interval changes. + */ + if (vif->type == NL80211_IFTYPE_AP) { + sc->sc_flags |= SC_OP_TSF_RESET; + ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); error = ath_beacon_alloc(aphy, vif); if (!error) ath_beacon_config(sc, vif); + } else { + ath_beacon_config(sc, vif); } } - /* Check for WLAN_CAPABILITY_PRIVACY ? */ - if ((avp->av_opmode != NL80211_IFTYPE_STATION)) { - for (i = 0; i < IEEE80211_WEP_NKID; i++) - if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i)) - ath9k_hw_keysetmac(sc->sc_ah, - (u16)i, - sc->curbssid); - } - - /* Only legacy IBSS for now */ - if (vif->type == NL80211_IFTYPE_ADHOC) - ath_update_chainmask(sc, 0); - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", - bss_conf->use_short_preamble); + ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", + bss_conf->use_short_preamble); if (bss_conf->use_short_preamble) sc->sc_flags |= SC_OP_PREAMBLE_SHORT; else @@ -2637,8 +3018,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_ERP_CTS_PROT) { - DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", - bss_conf->use_cts_prot); + ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", + bss_conf->use_cts_prot); if (bss_conf->use_cts_prot && hw->conf.channel->band != IEEE80211_BAND_5GHZ) sc->sc_flags |= SC_OP_PROTECT_ENABLE; @@ -2647,23 +3028,11 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_ASSOC) { - DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", + ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", bss_conf->assoc); ath9k_bss_assoc_info(sc, vif, bss_conf); } - /* - * The HW TSF has to be reset when the beacon interval changes. - * We set the flag here, and ath_beacon_config_ap() would take this - * into account when it gets called through the subsequent - * config_interface() call - with IFCC_BEACON in the changed field. - */ - - if (changed & BSS_CHANGED_BEACON_INT) { - sc->sc_flags |= SC_OP_TSF_RESET; - sc->beacon_interval = bss_conf->beacon_int; - } - mutex_unlock(&sc->mutex); } @@ -2696,11 +3065,16 @@ static void ath9k_reset_tsf(struct ieee80211_hw *hw) struct ath_softc *sc = aphy->sc; mutex_lock(&sc->mutex); + + ath9k_ps_wakeup(sc); ath9k_hw_reset_tsf(sc->sc_ah); + ath9k_ps_restore(sc); + mutex_unlock(&sc->mutex); } static int ath9k_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn) @@ -2718,17 +3092,18 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, break; case IEEE80211_AMPDU_TX_START: ath_tx_aggr_start(sc, sta, tid, ssn); - ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: ath_tx_aggr_stop(sc, sta, tid); - ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ath_tx_aggr_resume(sc, sta, tid); break; default: - DPRINTF(sc, ATH_DBG_FATAL, "Unknown AMPDU action\n"); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "Unknown AMPDU action\n"); } return ret; @@ -2796,64 +3171,6 @@ struct ieee80211_ops ath9k_ops = { .rfkill_poll = ath9k_rfkill_poll_state, }; -static struct { - u32 version; - const char * name; -} ath_mac_bb_names[] = { - { AR_SREV_VERSION_5416_PCI, "5416" }, - { AR_SREV_VERSION_5416_PCIE, "5418" }, - { AR_SREV_VERSION_9100, "9100" }, - { AR_SREV_VERSION_9160, "9160" }, - { AR_SREV_VERSION_9280, "9280" }, - { AR_SREV_VERSION_9285, "9285" }, - { AR_SREV_VERSION_9287, "9287" } -}; - -static struct { - u16 version; - const char * name; -} ath_rf_names[] = { - { 0, "5133" }, - { AR_RAD5133_SREV_MAJOR, "5133" }, - { AR_RAD5122_SREV_MAJOR, "5122" }, - { AR_RAD2133_SREV_MAJOR, "2133" }, - { AR_RAD2122_SREV_MAJOR, "2122" } -}; - -/* - * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown. - */ -const char * -ath_mac_bb_name(u32 mac_bb_version) -{ - int i; - - for (i=0; i<ARRAY_SIZE(ath_mac_bb_names); i++) { - if (ath_mac_bb_names[i].version == mac_bb_version) { - return ath_mac_bb_names[i].name; - } - } - - return "????"; -} - -/* - * Return the RF name. "????" is returned if the RF is unknown. - */ -const char * -ath_rf_name(u16 rf_version) -{ - int i; - - for (i=0; i<ARRAY_SIZE(ath_rf_names); i++) { - if (ath_rf_names[i].version == rf_version) { - return ath_rf_names[i].name; - } - } - - return "????"; -} - static int __init ath9k_init(void) { int error; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 903dd8ad9d43..5321f735e5a0 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -31,8 +31,9 @@ static struct pci_device_id ath_pci_id_table[] __devinitdata = { }; /* return bus cachesize in 4B word units */ -static void ath_pci_read_cachesize(struct ath_softc *sc, int *csz) +static void ath_pci_read_cachesize(struct ath_common *common, int *csz) { + struct ath_softc *sc = (struct ath_softc *) common->priv; u8 u8tmp; pci_read_config_byte(to_pci_dev(sc->dev), PCI_CACHE_LINE_SIZE, &u8tmp); @@ -48,8 +49,9 @@ static void ath_pci_read_cachesize(struct ath_softc *sc, int *csz) *csz = DEFAULT_CACHELINE >> 2; /* Use the default size */ } -static void ath_pci_cleanup(struct ath_softc *sc) +static void ath_pci_cleanup(struct ath_common *common) { + struct ath_softc *sc = (struct ath_softc *) common->priv; struct pci_dev *pdev = to_pci_dev(sc->dev); pci_iounmap(pdev, sc->mem); @@ -57,9 +59,11 @@ static void ath_pci_cleanup(struct ath_softc *sc) pci_release_region(pdev, 0); } -static bool ath_pci_eeprom_read(struct ath_hw *ah, u32 off, u16 *data) +static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data) { - (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); + struct ath_hw *ah = (struct ath_hw *) common->ah; + + common->ops->read(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); if (!ath9k_hw_wait(ah, AR_EEPROM_STATUS_DATA, @@ -69,16 +73,34 @@ static bool ath_pci_eeprom_read(struct ath_hw *ah, u32 off, u16 *data) return false; } - *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), + *data = MS(common->ops->read(ah, AR_EEPROM_STATUS_DATA), AR_EEPROM_STATUS_DATA_VAL); return true; } -static struct ath_bus_ops ath_pci_bus_ops = { +/* + * Bluetooth coexistance requires disabling ASPM. + */ +static void ath_pci_bt_coex_prep(struct ath_common *common) +{ + struct ath_softc *sc = (struct ath_softc *) common->priv; + struct pci_dev *pdev = to_pci_dev(sc->dev); + u8 aspm; + + if (!pdev->is_pcie) + return; + + pci_read_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, &aspm); + aspm &= ~(ATH_PCIE_CAP_LINK_L0S | ATH_PCIE_CAP_LINK_L1); + pci_write_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, aspm); +} + +const static struct ath_bus_ops ath_pci_bus_ops = { .read_cachesize = ath_pci_read_cachesize, .cleanup = ath_pci_cleanup, .eeprom_read = ath_pci_eeprom_read, + .bt_coex_prep = ath_pci_bt_coex_prep, }; static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -92,6 +114,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) u32 val; int ret = 0; struct ath_hw *ah; + char hw_name[64]; if (pci_enable_device(pdev)) return -EIO; @@ -177,10 +200,9 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc->hw = hw; sc->dev = &pdev->dev; sc->mem = mem; - sc->bus_ops = &ath_pci_bus_ops; pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsysid); - ret = ath_init_device(id->device, sc, subsysid); + ret = ath_init_device(id->device, sc, subsysid, &ath_pci_bus_ops); if (ret) { dev_err(&pdev->dev, "failed to initialize device\n"); goto bad3; @@ -197,14 +219,11 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc->irq = pdev->irq; ah = sc->sc_ah; + ath9k_hw_name(ah, hw_name, sizeof(hw_name)); printk(KERN_INFO - "%s: Atheros AR%s MAC/BB Rev:%x " - "AR%s RF Rev:%x: mem=0x%lx, irq=%d\n", + "%s: %s mem=0x%lx, irq=%d\n", wiphy_name(hw->wiphy), - ath_mac_bb_name(ah->hw_version.macVersion), - ah->hw_version.macRev, - ath_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)), - ah->hw_version.phyRev, + hw_name, (unsigned long)mem, pdev->irq); return 0; diff --git a/drivers/net/wireless/ath/ath9k/phy.c b/drivers/net/wireless/ath/ath9k/phy.c index 63bf9a307c6a..c3b59390fe38 100644 --- a/drivers/net/wireless/ath/ath9k/phy.c +++ b/drivers/net/wireless/ath/ath9k/phy.c @@ -14,90 +14,70 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "ath9k.h" +/** + * DOC: Programming Atheros 802.11n analog front end radios + * + * AR5416 MAC based PCI devices and AR518 MAC based PCI-Express + * devices have either an external AR2133 analog front end radio for single + * band 2.4 GHz communication or an AR5133 analog front end radio for dual + * band 2.4 GHz / 5 GHz communication. + * + * All devices after the AR5416 and AR5418 family starting with the AR9280 + * have their analog front radios, MAC/BB and host PCIe/USB interface embedded + * into a single-chip and require less programming. + * + * The following single-chips exist with a respective embedded radio: + * + * AR9280 - 11n dual-band 2x2 MIMO for PCIe + * AR9281 - 11n single-band 1x2 MIMO for PCIe + * AR9285 - 11n single-band 1x1 for PCIe + * AR9287 - 11n single-band 2x2 MIMO for PCIe + * + * AR9220 - 11n dual-band 2x2 MIMO for PCI + * AR9223 - 11n single-band 2x2 MIMO for PCI + * + * AR9287 - 11n single-band 1x1 MIMO for USB + */ -void -ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex, u32 freqIndex, - int regWrites) -{ - REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites); -} +#include "hw.h" -bool -ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) +/** + * ath9k_hw_write_regs - ?? + * + * @ah: atheros hardware structure + * @freqIndex: + * @regWrites: + * + * Used for both the chipsets with an external AR2133/AR5133 radios and + * single-chip devices. + */ +void ath9k_hw_write_regs(struct ath_hw *ah, u32 freqIndex, int regWrites) { - u32 channelSel = 0; - u32 bModeSynth = 0; - u32 aModeRefSel = 0; - u32 reg32 = 0; - u16 freq; - struct chan_centers centers; - - ath9k_hw_get_channel_centers(ah, chan, ¢ers); - freq = centers.synth_center; - - if (freq < 4800) { - u32 txctl; - - if (((freq - 2192) % 5) == 0) { - channelSel = ((freq - 672) * 2 - 3040) / 10; - bModeSynth = 0; - } else if (((freq - 2224) % 5) == 0) { - channelSel = ((freq - 704) * 2 - 3040) / 10; - bModeSynth = 1; - } else { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Invalid channel %u MHz\n", freq); - return false; - } - - channelSel = (channelSel << 2) & 0xff; - channelSel = ath9k_hw_reverse_bits(channelSel, 8); - - txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); - if (freq == 2484) { - - REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, - txctl | AR_PHY_CCK_TX_CTRL_JAPAN); - } else { - REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, - txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); - } - - } else if ((freq % 20) == 0 && freq >= 5120) { - channelSel = - ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8); - aModeRefSel = ath9k_hw_reverse_bits(1, 2); - } else if ((freq % 10) == 0) { - channelSel = - ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8); - if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) - aModeRefSel = ath9k_hw_reverse_bits(2, 2); - else - aModeRefSel = ath9k_hw_reverse_bits(1, 2); - } else if ((freq % 5) == 0) { - channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8); - aModeRefSel = ath9k_hw_reverse_bits(1, 2); - } else { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Invalid channel %u MHz\n", freq); - return false; - } - - reg32 = - (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) | - (1 << 5) | 0x1; - - REG_WRITE(ah, AR_PHY(0x37), reg32); - - ah->curchan = chan; - ah->curchan_rad_index = -1; - - return true; + REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites); } -void ath9k_hw_ar9280_set_channel(struct ath_hw *ah, - struct ath9k_channel *chan) +/** + * ath9k_hw_ar9280_set_channel - set channel on single-chip device + * @ah: atheros hardware structure + * @chan: + * + * This is the function to change channel on single-chip devices, that is + * all devices after ar9280. + * + * This function takes the channel value in MHz and sets + * hardware channel value. Assumes writes have been enabled to analog bus. + * + * Actual Expression, + * + * For 2GHz channel, + * Channel Frequency = (3/4) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17) + * (freq_ref = 40MHz) + * + * For 5GHz channel, + * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^10) + * (freq_ref = 40MHz/(24>>amodeRefSel)) + */ +int ath9k_hw_ar9280_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) { u16 bMode, fracMode, aModeRefSel = 0; u32 freq, ndiv, channelSel = 0, channelFrac = 0, reg32 = 0; @@ -110,22 +90,34 @@ void ath9k_hw_ar9280_set_channel(struct ath_hw *ah, reg32 = REG_READ(ah, AR_PHY_SYNTH_CONTROL); reg32 &= 0xc0000000; - if (freq < 4800) { + if (freq < 4800) { /* 2 GHz, fractional mode */ u32 txctl; + int regWrites = 0; bMode = 1; fracMode = 1; aModeRefSel = 0; channelSel = (freq * 0x10000) / 15; - txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); - if (freq == 2484) { - - REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, - txctl | AR_PHY_CCK_TX_CTRL_JAPAN); + if (AR_SREV_9287_11_OR_LATER(ah)) { + if (freq == 2484) { + /* Enable channel spreading for channel 14 */ + REG_WRITE_ARRAY(&ah->iniCckfirJapan2484, + 1, regWrites); + } else { + REG_WRITE_ARRAY(&ah->iniCckfirNormal, + 1, regWrites); + } } else { - REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, - txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); + txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); + if (freq == 2484) { + /* Enable channel spreading for channel 14 */ + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl | AR_PHY_CCK_TX_CTRL_JAPAN); + } else { + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); + } } } else { bMode = 0; @@ -143,10 +135,15 @@ void ath9k_hw_ar9280_set_channel(struct ath_hw *ah, case 1: default: aModeRefSel = 0; + /* + * Enable 2G (fractional) mode for channels + * which are 5MHz spaced. + */ fracMode = 1; refDivA = 1; channelSel = (freq * 0x8000) / 15; + /* RefDivA setting */ REG_RMW_FIELD(ah, AR_AN_SYNTH9, AR_AN_SYNTH9_REFDIVA, refDivA); @@ -168,12 +165,284 @@ void ath9k_hw_ar9280_set_channel(struct ath_hw *ah, ah->curchan = chan; ah->curchan_rad_index = -1; + + return 0; +} + +/** + * ath9k_hw_9280_spur_mitigate - convert baseband spur frequency + * @ah: atheros hardware structure + * @chan: + * + * For single-chip solutions. Converts to baseband spur frequency given the + * input channel frequency and compute register settings below. + */ +void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan) +{ + int bb_spur = AR_NO_SPUR; + int freq; + int bin, cur_bin; + int bb_spur_off, spur_subchannel_sd; + int spur_freq_sd; + int spur_delta_phase; + int denominator; + int upper, lower, cur_vit_mask; + int tmp, newVal; + int i; + int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, + AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 + }; + int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, + AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 + }; + int inc[4] = { 0, 100, 0, 0 }; + struct chan_centers centers; + + int8_t mask_m[123]; + int8_t mask_p[123]; + int8_t mask_amt; + int tmp_mask; + int cur_bb_spur; + bool is2GHz = IS_CHAN_2GHZ(chan); + + memset(&mask_m, 0, sizeof(int8_t) * 123); + memset(&mask_p, 0, sizeof(int8_t) * 123); + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = centers.synth_center; + + ah->config.spurmode = SPUR_ENABLE_EEPROM; + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); + + if (is2GHz) + cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_2GHZ; + else + cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_5GHZ; + + if (AR_NO_SPUR == cur_bb_spur) + break; + cur_bb_spur = cur_bb_spur - freq; + + if (IS_CHAN_HT40(chan)) { + if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT40) && + (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT40)) { + bb_spur = cur_bb_spur; + break; + } + } else if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT20) && + (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT20)) { + bb_spur = cur_bb_spur; + break; + } + } + + if (AR_NO_SPUR == bb_spur) { + REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, + AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); + return; + } else { + REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, + AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); + } + + bin = bb_spur * 320; + + tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); + + newVal = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | + AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | + AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | + AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), newVal); + + newVal = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | + AR_PHY_SPUR_REG_ENABLE_MASK_PPM | + AR_PHY_SPUR_REG_MASK_RATE_SELECT | + AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | + SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); + REG_WRITE(ah, AR_PHY_SPUR_REG, newVal); + + if (IS_CHAN_HT40(chan)) { + if (bb_spur < 0) { + spur_subchannel_sd = 1; + bb_spur_off = bb_spur + 10; + } else { + spur_subchannel_sd = 0; + bb_spur_off = bb_spur - 10; + } + } else { + spur_subchannel_sd = 0; + bb_spur_off = bb_spur; + } + + if (IS_CHAN_HT40(chan)) + spur_delta_phase = + ((bb_spur * 262144) / + 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; + else + spur_delta_phase = + ((bb_spur * 524288) / + 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; + + denominator = IS_CHAN_2GHZ(chan) ? 44 : 40; + spur_freq_sd = ((bb_spur_off * 2048) / denominator) & 0x3ff; + + newVal = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | + SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | + SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); + REG_WRITE(ah, AR_PHY_TIMING11, newVal); + + newVal = spur_subchannel_sd << AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S; + REG_WRITE(ah, AR_PHY_SFCORR_EXT, newVal); + + cur_bin = -6000; + upper = bin + 100; + lower = bin - 100; + + for (i = 0; i < 4; i++) { + int pilot_mask = 0; + int chan_mask = 0; + int bp = 0; + for (bp = 0; bp < 30; bp++) { + if ((cur_bin > lower) && (cur_bin < upper)) { + pilot_mask = pilot_mask | 0x1 << bp; + chan_mask = chan_mask | 0x1 << bp; + } + cur_bin += 100; + } + cur_bin += inc[i]; + REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); + REG_WRITE(ah, chan_mask_reg[i], chan_mask); + } + + cur_vit_mask = 6100; + upper = bin + 120; + lower = bin - 120; + + for (i = 0; i < 123; i++) { + if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { + + /* workaround for gcc bug #37014 */ + volatile int tmp_v = abs(cur_vit_mask - bin); + + if (tmp_v < 75) + mask_amt = 1; + else + mask_amt = 0; + if (cur_vit_mask < 0) + mask_m[abs(cur_vit_mask / 100)] = mask_amt; + else + mask_p[cur_vit_mask / 100] = mask_amt; + } + cur_vit_mask -= 100; + } + + tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) + | (mask_m[48] << 26) | (mask_m[49] << 24) + | (mask_m[50] << 22) | (mask_m[51] << 20) + | (mask_m[52] << 18) | (mask_m[53] << 16) + | (mask_m[54] << 14) | (mask_m[55] << 12) + | (mask_m[56] << 10) | (mask_m[57] << 8) + | (mask_m[58] << 6) | (mask_m[59] << 4) + | (mask_m[60] << 2) | (mask_m[61] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); + REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); + + tmp_mask = (mask_m[31] << 28) + | (mask_m[32] << 26) | (mask_m[33] << 24) + | (mask_m[34] << 22) | (mask_m[35] << 20) + | (mask_m[36] << 18) | (mask_m[37] << 16) + | (mask_m[48] << 14) | (mask_m[39] << 12) + | (mask_m[40] << 10) | (mask_m[41] << 8) + | (mask_m[42] << 6) | (mask_m[43] << 4) + | (mask_m[44] << 2) | (mask_m[45] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); + + tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) + | (mask_m[18] << 26) | (mask_m[18] << 24) + | (mask_m[20] << 22) | (mask_m[20] << 20) + | (mask_m[22] << 18) | (mask_m[22] << 16) + | (mask_m[24] << 14) | (mask_m[24] << 12) + | (mask_m[25] << 10) | (mask_m[26] << 8) + | (mask_m[27] << 6) | (mask_m[28] << 4) + | (mask_m[29] << 2) | (mask_m[30] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); + + tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) + | (mask_m[2] << 26) | (mask_m[3] << 24) + | (mask_m[4] << 22) | (mask_m[5] << 20) + | (mask_m[6] << 18) | (mask_m[7] << 16) + | (mask_m[8] << 14) | (mask_m[9] << 12) + | (mask_m[10] << 10) | (mask_m[11] << 8) + | (mask_m[12] << 6) | (mask_m[13] << 4) + | (mask_m[14] << 2) | (mask_m[15] << 0); + REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); + + tmp_mask = (mask_p[15] << 28) + | (mask_p[14] << 26) | (mask_p[13] << 24) + | (mask_p[12] << 22) | (mask_p[11] << 20) + | (mask_p[10] << 18) | (mask_p[9] << 16) + | (mask_p[8] << 14) | (mask_p[7] << 12) + | (mask_p[6] << 10) | (mask_p[5] << 8) + | (mask_p[4] << 6) | (mask_p[3] << 4) + | (mask_p[2] << 2) | (mask_p[1] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); + + tmp_mask = (mask_p[30] << 28) + | (mask_p[29] << 26) | (mask_p[28] << 24) + | (mask_p[27] << 22) | (mask_p[26] << 20) + | (mask_p[25] << 18) | (mask_p[24] << 16) + | (mask_p[23] << 14) | (mask_p[22] << 12) + | (mask_p[21] << 10) | (mask_p[20] << 8) + | (mask_p[19] << 6) | (mask_p[18] << 4) + | (mask_p[17] << 2) | (mask_p[16] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); + + tmp_mask = (mask_p[45] << 28) + | (mask_p[44] << 26) | (mask_p[43] << 24) + | (mask_p[42] << 22) | (mask_p[41] << 20) + | (mask_p[40] << 18) | (mask_p[39] << 16) + | (mask_p[38] << 14) | (mask_p[37] << 12) + | (mask_p[36] << 10) | (mask_p[35] << 8) + | (mask_p[34] << 6) | (mask_p[33] << 4) + | (mask_p[32] << 2) | (mask_p[31] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); + + tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) + | (mask_p[59] << 26) | (mask_p[58] << 24) + | (mask_p[57] << 22) | (mask_p[56] << 20) + | (mask_p[55] << 18) | (mask_p[54] << 16) + | (mask_p[53] << 14) | (mask_p[52] << 12) + | (mask_p[51] << 10) | (mask_p[50] << 8) + | (mask_p[49] << 6) | (mask_p[48] << 4) + | (mask_p[47] << 2) | (mask_p[46] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); } -static void -ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32, - u32 numBits, u32 firstBit, - u32 column) +/* All code below is for non single-chip solutions */ + +/** + * ath9k_phy_modify_rx_buffer() - perform analog swizzling of parameters + * @rfbuf: + * @reg32: + * @numBits: + * @firstBit: + * @column: + * + * Performs analog "swizzling" of parameters into their location. + * Used on external AR2133/AR5133 radios. + */ +static void ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32, + u32 numBits, u32 firstBit, + u32 column) { u32 tmp32, mask, arrayEntry, lastBit; int32_t bitPosition, bitsLeft; @@ -197,26 +466,466 @@ ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32, } } -bool -ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, - u16 modesIndex) +/* + * Fix on 2.4 GHz band for orientation sensitivity issue by increasing + * rf_pwd_icsyndiv. + * + * Theoretical Rules: + * if 2 GHz band + * if forceBiasAuto + * if synth_freq < 2412 + * bias = 0 + * else if 2412 <= synth_freq <= 2422 + * bias = 1 + * else // synth_freq > 2422 + * bias = 2 + * else if forceBias > 0 + * bias = forceBias & 7 + * else + * no change, use value from ini file + * else + * no change, invalid band + * + * 1st Mod: + * 2422 also uses value of 2 + * <approved> + * + * 2nd Mod: + * Less than 2412 uses value of 0, 2412 and above uses value of 2 + */ +static void ath9k_hw_force_bias(struct ath_hw *ah, u16 synth_freq) +{ + struct ath_common *common = ath9k_hw_common(ah); + u32 tmp_reg; + int reg_writes = 0; + u32 new_bias = 0; + + if (!AR_SREV_5416(ah) || synth_freq >= 3000) { + return; + } + + BUG_ON(AR_SREV_9280_10_OR_LATER(ah)); + + if (synth_freq < 2412) + new_bias = 0; + else if (synth_freq < 2422) + new_bias = 1; + else + new_bias = 2; + + /* pre-reverse this field */ + tmp_reg = ath9k_hw_reverse_bits(new_bias, 3); + + ath_print(common, ATH_DBG_CONFIG, + "Force rf_pwd_icsyndiv to %1d on %4d\n", + new_bias, synth_freq); + + /* swizzle rf_pwd_icsyndiv */ + ath9k_phy_modify_rx_buffer(ah->analogBank6Data, tmp_reg, 3, 181, 3); + + /* write Bank 6 with new params */ + REG_WRITE_RF_ARRAY(&ah->iniBank6, ah->analogBank6Data, reg_writes); +} + +/** + * ath9k_hw_set_channel - tune to a channel on the external AR2133/AR5133 radios + * @ah: atheros hardware stucture + * @chan: + * + * For the external AR2133/AR5133 radios, takes the MHz channel value and set + * the channel value. Assumes writes enabled to analog bus and bank6 register + * cache in ah->analogBank6Data. + */ +int ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) +{ + struct ath_common *common = ath9k_hw_common(ah); + u32 channelSel = 0; + u32 bModeSynth = 0; + u32 aModeRefSel = 0; + u32 reg32 = 0; + u16 freq; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = centers.synth_center; + + if (freq < 4800) { + u32 txctl; + + if (((freq - 2192) % 5) == 0) { + channelSel = ((freq - 672) * 2 - 3040) / 10; + bModeSynth = 0; + } else if (((freq - 2224) % 5) == 0) { + channelSel = ((freq - 704) * 2 - 3040) / 10; + bModeSynth = 1; + } else { + ath_print(common, ATH_DBG_FATAL, + "Invalid channel %u MHz\n", freq); + return -EINVAL; + } + + channelSel = (channelSel << 2) & 0xff; + channelSel = ath9k_hw_reverse_bits(channelSel, 8); + + txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); + if (freq == 2484) { + + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl | AR_PHY_CCK_TX_CTRL_JAPAN); + } else { + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); + } + + } else if ((freq % 20) == 0 && freq >= 5120) { + channelSel = + ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8); + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else if ((freq % 10) == 0) { + channelSel = + ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8); + if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) + aModeRefSel = ath9k_hw_reverse_bits(2, 2); + else + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else if ((freq % 5) == 0) { + channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8); + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else { + ath_print(common, ATH_DBG_FATAL, + "Invalid channel %u MHz\n", freq); + return -EINVAL; + } + + ath9k_hw_force_bias(ah, freq); + + reg32 = + (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) | + (1 << 5) | 0x1; + + REG_WRITE(ah, AR_PHY(0x37), reg32); + + ah->curchan = chan; + ah->curchan_rad_index = -1; + + return 0; +} + +/** + * ath9k_hw_spur_mitigate - convert baseband spur frequency for external radios + * @ah: atheros hardware structure + * @chan: + * + * For non single-chip solutions. Converts to baseband spur frequency given the + * input channel frequency and compute register settings below. + */ +void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan) +{ + int bb_spur = AR_NO_SPUR; + int bin, cur_bin; + int spur_freq_sd; + int spur_delta_phase; + int denominator; + int upper, lower, cur_vit_mask; + int tmp, new; + int i; + int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, + AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 + }; + int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, + AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 + }; + int inc[4] = { 0, 100, 0, 0 }; + + int8_t mask_m[123]; + int8_t mask_p[123]; + int8_t mask_amt; + int tmp_mask; + int cur_bb_spur; + bool is2GHz = IS_CHAN_2GHZ(chan); + + memset(&mask_m, 0, sizeof(int8_t) * 123); + memset(&mask_p, 0, sizeof(int8_t) * 123); + + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); + if (AR_NO_SPUR == cur_bb_spur) + break; + cur_bb_spur = cur_bb_spur - (chan->channel * 10); + if ((cur_bb_spur > -95) && (cur_bb_spur < 95)) { + bb_spur = cur_bb_spur; + break; + } + } + + if (AR_NO_SPUR == bb_spur) + return; + + bin = bb_spur * 32; + + tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); + new = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | + AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | + AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | + AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); + + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), new); + + new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | + AR_PHY_SPUR_REG_ENABLE_MASK_PPM | + AR_PHY_SPUR_REG_MASK_RATE_SELECT | + AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | + SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); + REG_WRITE(ah, AR_PHY_SPUR_REG, new); + + spur_delta_phase = ((bb_spur * 524288) / 100) & + AR_PHY_TIMING11_SPUR_DELTA_PHASE; + + denominator = IS_CHAN_2GHZ(chan) ? 440 : 400; + spur_freq_sd = ((bb_spur * 2048) / denominator) & 0x3ff; + + new = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | + SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | + SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); + REG_WRITE(ah, AR_PHY_TIMING11, new); + + cur_bin = -6000; + upper = bin + 100; + lower = bin - 100; + + for (i = 0; i < 4; i++) { + int pilot_mask = 0; + int chan_mask = 0; + int bp = 0; + for (bp = 0; bp < 30; bp++) { + if ((cur_bin > lower) && (cur_bin < upper)) { + pilot_mask = pilot_mask | 0x1 << bp; + chan_mask = chan_mask | 0x1 << bp; + } + cur_bin += 100; + } + cur_bin += inc[i]; + REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); + REG_WRITE(ah, chan_mask_reg[i], chan_mask); + } + + cur_vit_mask = 6100; + upper = bin + 120; + lower = bin - 120; + + for (i = 0; i < 123; i++) { + if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { + + /* workaround for gcc bug #37014 */ + volatile int tmp_v = abs(cur_vit_mask - bin); + + if (tmp_v < 75) + mask_amt = 1; + else + mask_amt = 0; + if (cur_vit_mask < 0) + mask_m[abs(cur_vit_mask / 100)] = mask_amt; + else + mask_p[cur_vit_mask / 100] = mask_amt; + } + cur_vit_mask -= 100; + } + + tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) + | (mask_m[48] << 26) | (mask_m[49] << 24) + | (mask_m[50] << 22) | (mask_m[51] << 20) + | (mask_m[52] << 18) | (mask_m[53] << 16) + | (mask_m[54] << 14) | (mask_m[55] << 12) + | (mask_m[56] << 10) | (mask_m[57] << 8) + | (mask_m[58] << 6) | (mask_m[59] << 4) + | (mask_m[60] << 2) | (mask_m[61] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); + REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); + + tmp_mask = (mask_m[31] << 28) + | (mask_m[32] << 26) | (mask_m[33] << 24) + | (mask_m[34] << 22) | (mask_m[35] << 20) + | (mask_m[36] << 18) | (mask_m[37] << 16) + | (mask_m[48] << 14) | (mask_m[39] << 12) + | (mask_m[40] << 10) | (mask_m[41] << 8) + | (mask_m[42] << 6) | (mask_m[43] << 4) + | (mask_m[44] << 2) | (mask_m[45] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); + + tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) + | (mask_m[18] << 26) | (mask_m[18] << 24) + | (mask_m[20] << 22) | (mask_m[20] << 20) + | (mask_m[22] << 18) | (mask_m[22] << 16) + | (mask_m[24] << 14) | (mask_m[24] << 12) + | (mask_m[25] << 10) | (mask_m[26] << 8) + | (mask_m[27] << 6) | (mask_m[28] << 4) + | (mask_m[29] << 2) | (mask_m[30] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); + + tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) + | (mask_m[2] << 26) | (mask_m[3] << 24) + | (mask_m[4] << 22) | (mask_m[5] << 20) + | (mask_m[6] << 18) | (mask_m[7] << 16) + | (mask_m[8] << 14) | (mask_m[9] << 12) + | (mask_m[10] << 10) | (mask_m[11] << 8) + | (mask_m[12] << 6) | (mask_m[13] << 4) + | (mask_m[14] << 2) | (mask_m[15] << 0); + REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); + + tmp_mask = (mask_p[15] << 28) + | (mask_p[14] << 26) | (mask_p[13] << 24) + | (mask_p[12] << 22) | (mask_p[11] << 20) + | (mask_p[10] << 18) | (mask_p[9] << 16) + | (mask_p[8] << 14) | (mask_p[7] << 12) + | (mask_p[6] << 10) | (mask_p[5] << 8) + | (mask_p[4] << 6) | (mask_p[3] << 4) + | (mask_p[2] << 2) | (mask_p[1] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); + + tmp_mask = (mask_p[30] << 28) + | (mask_p[29] << 26) | (mask_p[28] << 24) + | (mask_p[27] << 22) | (mask_p[26] << 20) + | (mask_p[25] << 18) | (mask_p[24] << 16) + | (mask_p[23] << 14) | (mask_p[22] << 12) + | (mask_p[21] << 10) | (mask_p[20] << 8) + | (mask_p[19] << 6) | (mask_p[18] << 4) + | (mask_p[17] << 2) | (mask_p[16] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); + + tmp_mask = (mask_p[45] << 28) + | (mask_p[44] << 26) | (mask_p[43] << 24) + | (mask_p[42] << 22) | (mask_p[41] << 20) + | (mask_p[40] << 18) | (mask_p[39] << 16) + | (mask_p[38] << 14) | (mask_p[37] << 12) + | (mask_p[36] << 10) | (mask_p[35] << 8) + | (mask_p[34] << 6) | (mask_p[33] << 4) + | (mask_p[32] << 2) | (mask_p[31] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); + + tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) + | (mask_p[59] << 26) | (mask_p[58] << 24) + | (mask_p[57] << 22) | (mask_p[56] << 20) + | (mask_p[55] << 18) | (mask_p[54] << 16) + | (mask_p[53] << 14) | (mask_p[52] << 12) + | (mask_p[51] << 10) | (mask_p[50] << 8) + | (mask_p[49] << 6) | (mask_p[48] << 4) + | (mask_p[47] << 2) | (mask_p[46] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); +} + +/** + * ath9k_hw_rf_alloc_ext_banks - allocates banks for external radio programming + * @ah: atheros hardware structure + * + * Only required for older devices with external AR2133/AR5133 radios. + */ +int ath9k_hw_rf_alloc_ext_banks(struct ath_hw *ah) +{ +#define ATH_ALLOC_BANK(bank, size) do { \ + bank = kzalloc((sizeof(u32) * size), GFP_KERNEL); \ + if (!bank) { \ + ath_print(common, ATH_DBG_FATAL, \ + "Cannot allocate RF banks\n"); \ + return -ENOMEM; \ + } \ + } while (0); + + struct ath_common *common = ath9k_hw_common(ah); + + BUG_ON(AR_SREV_9280_10_OR_LATER(ah)); + + ATH_ALLOC_BANK(ah->analogBank0Data, ah->iniBank0.ia_rows); + ATH_ALLOC_BANK(ah->analogBank1Data, ah->iniBank1.ia_rows); + ATH_ALLOC_BANK(ah->analogBank2Data, ah->iniBank2.ia_rows); + ATH_ALLOC_BANK(ah->analogBank3Data, ah->iniBank3.ia_rows); + ATH_ALLOC_BANK(ah->analogBank6Data, ah->iniBank6.ia_rows); + ATH_ALLOC_BANK(ah->analogBank6TPCData, ah->iniBank6TPC.ia_rows); + ATH_ALLOC_BANK(ah->analogBank7Data, ah->iniBank7.ia_rows); + ATH_ALLOC_BANK(ah->addac5416_21, + ah->iniAddac.ia_rows * ah->iniAddac.ia_columns); + ATH_ALLOC_BANK(ah->bank6Temp, ah->iniBank6.ia_rows); + + return 0; +#undef ATH_ALLOC_BANK +} + + +/** + * ath9k_hw_rf_free_ext_banks - Free memory for analog bank scratch buffers + * @ah: atheros hardware struture + * For the external AR2133/AR5133 radios banks. + */ +void +ath9k_hw_rf_free_ext_banks(struct ath_hw *ah) +{ +#define ATH_FREE_BANK(bank) do { \ + kfree(bank); \ + bank = NULL; \ + } while (0); + + BUG_ON(AR_SREV_9280_10_OR_LATER(ah)); + + ATH_FREE_BANK(ah->analogBank0Data); + ATH_FREE_BANK(ah->analogBank1Data); + ATH_FREE_BANK(ah->analogBank2Data); + ATH_FREE_BANK(ah->analogBank3Data); + ATH_FREE_BANK(ah->analogBank6Data); + ATH_FREE_BANK(ah->analogBank6TPCData); + ATH_FREE_BANK(ah->analogBank7Data); + ATH_FREE_BANK(ah->addac5416_21); + ATH_FREE_BANK(ah->bank6Temp); + +#undef ATH_FREE_BANK +} + +/* * + * ath9k_hw_set_rf_regs - programs rf registers based on EEPROM + * @ah: atheros hardware structure + * @chan: + * @modesIndex: + * + * Used for the external AR2133/AR5133 radios. + * + * Reads the EEPROM header info from the device structure and programs + * all rf registers. This routine requires access to the analog + * rf device. This is not required for single-chip devices. + */ +bool ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, + u16 modesIndex) { u32 eepMinorRev; u32 ob5GHz = 0, db5GHz = 0; u32 ob2GHz = 0, db2GHz = 0; int regWrites = 0; + /* + * Software does not need to program bank data + * for single chip devices, that is AR9280 or anything + * after that. + */ if (AR_SREV_9280_10_OR_LATER(ah)) return true; + /* Setup rf parameters */ eepMinorRev = ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV); + /* Setup Bank 0 Write */ RF_BANK_SETUP(ah->analogBank0Data, &ah->iniBank0, 1); + /* Setup Bank 1 Write */ RF_BANK_SETUP(ah->analogBank1Data, &ah->iniBank1, 1); + /* Setup Bank 2 Write */ RF_BANK_SETUP(ah->analogBank2Data, &ah->iniBank2, 1); + /* Setup Bank 6 Write */ RF_BANK_SETUP(ah->analogBank3Data, &ah->iniBank3, modesIndex); { @@ -227,6 +936,7 @@ ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, } } + /* Only the 5 or 2 GHz OB/DB need to be set for a mode */ if (eepMinorRev >= 2) { if (IS_CHAN_2GHZ(chan)) { ob2GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_2); @@ -245,8 +955,10 @@ ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, } } + /* Setup Bank 7 Setup */ RF_BANK_SETUP(ah->analogBank7Data, &ah->iniBank7, 1); + /* Write Analog registers */ REG_WRITE_RF_ARRAY(&ah->iniBank0, ah->analogBank0Data, regWrites); REG_WRITE_RF_ARRAY(&ah->iniBank1, ah->analogBank1Data, @@ -262,137 +974,3 @@ ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, return true; } - -void -ath9k_hw_rf_free(struct ath_hw *ah) -{ -#define ATH_FREE_BANK(bank) do { \ - kfree(bank); \ - bank = NULL; \ - } while (0); - - ATH_FREE_BANK(ah->analogBank0Data); - ATH_FREE_BANK(ah->analogBank1Data); - ATH_FREE_BANK(ah->analogBank2Data); - ATH_FREE_BANK(ah->analogBank3Data); - ATH_FREE_BANK(ah->analogBank6Data); - ATH_FREE_BANK(ah->analogBank6TPCData); - ATH_FREE_BANK(ah->analogBank7Data); - ATH_FREE_BANK(ah->addac5416_21); - ATH_FREE_BANK(ah->bank6Temp); -#undef ATH_FREE_BANK -} - -bool ath9k_hw_init_rf(struct ath_hw *ah, int *status) -{ - if (!AR_SREV_9280_10_OR_LATER(ah)) { - ah->analogBank0Data = - kzalloc((sizeof(u32) * - ah->iniBank0.ia_rows), GFP_KERNEL); - ah->analogBank1Data = - kzalloc((sizeof(u32) * - ah->iniBank1.ia_rows), GFP_KERNEL); - ah->analogBank2Data = - kzalloc((sizeof(u32) * - ah->iniBank2.ia_rows), GFP_KERNEL); - ah->analogBank3Data = - kzalloc((sizeof(u32) * - ah->iniBank3.ia_rows), GFP_KERNEL); - ah->analogBank6Data = - kzalloc((sizeof(u32) * - ah->iniBank6.ia_rows), GFP_KERNEL); - ah->analogBank6TPCData = - kzalloc((sizeof(u32) * - ah->iniBank6TPC.ia_rows), GFP_KERNEL); - ah->analogBank7Data = - kzalloc((sizeof(u32) * - ah->iniBank7.ia_rows), GFP_KERNEL); - - if (ah->analogBank0Data == NULL - || ah->analogBank1Data == NULL - || ah->analogBank2Data == NULL - || ah->analogBank3Data == NULL - || ah->analogBank6Data == NULL - || ah->analogBank6TPCData == NULL - || ah->analogBank7Data == NULL) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Cannot allocate RF banks\n"); - *status = -ENOMEM; - return false; - } - - ah->addac5416_21 = - kzalloc((sizeof(u32) * - ah->iniAddac.ia_rows * - ah->iniAddac.ia_columns), GFP_KERNEL); - if (ah->addac5416_21 == NULL) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Cannot allocate addac5416_21\n"); - *status = -ENOMEM; - return false; - } - - ah->bank6Temp = - kzalloc((sizeof(u32) * - ah->iniBank6.ia_rows), GFP_KERNEL); - if (ah->bank6Temp == NULL) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Cannot allocate bank6Temp\n"); - *status = -ENOMEM; - return false; - } - } - - return true; -} - -void -ath9k_hw_decrease_chain_power(struct ath_hw *ah, struct ath9k_channel *chan) -{ - int i, regWrites = 0; - u32 bank6SelMask; - u32 *bank6Temp = ah->bank6Temp; - - switch (ah->config.diversity_control) { - case ATH9K_ANT_FIXED_A: - bank6SelMask = - (ah->config.antenna_switch_swap & ANTSWAP_AB) ? - REDUCE_CHAIN_0 : REDUCE_CHAIN_1; - break; - case ATH9K_ANT_FIXED_B: - bank6SelMask = - (ah->config.antenna_switch_swap & ANTSWAP_AB) ? - REDUCE_CHAIN_1 : REDUCE_CHAIN_0; - break; - case ATH9K_ANT_VARIABLE: - return; - break; - default: - return; - break; - } - - for (i = 0; i < ah->iniBank6.ia_rows; i++) - bank6Temp[i] = ah->analogBank6Data[i]; - - REG_WRITE(ah, AR_PHY_BASE + 0xD8, bank6SelMask); - - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 189, 0); - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 190, 0); - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 191, 0); - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 192, 0); - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 193, 0); - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 222, 0); - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 245, 0); - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 246, 0); - ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 247, 0); - - REG_WRITE_RF_ARRAY(&ah->iniBank6, bank6Temp, regWrites); - - REG_WRITE(ah, AR_PHY_BASE + 0xD8, 0x00000053); -#ifdef ALTER_SWITCH - REG_WRITE(ah, PHY_SWITCH_CHAIN_0, - (REG_READ(ah, PHY_SWITCH_CHAIN_0) & ~0x38) - | ((REG_READ(ah, PHY_SWITCH_CHAIN_0) >> 3) & 0x38)); -#endif -} diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h index dfda6f444648..31de27dc0c4a 100644 --- a/drivers/net/wireless/ath/ath9k/phy.h +++ b/drivers/net/wireless/ath/ath9k/phy.h @@ -17,20 +17,23 @@ #ifndef PHY_H #define PHY_H -void ath9k_hw_ar9280_set_channel(struct ath_hw *ah, - struct ath9k_channel - *chan); -bool ath9k_hw_set_channel(struct ath_hw *ah, - struct ath9k_channel *chan); -void ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex, - u32 freqIndex, int regWrites); +/* Common between single chip and non single-chip solutions */ +void ath9k_hw_write_regs(struct ath_hw *ah, u32 freqIndex, int regWrites); + +/* Single chip radio settings */ +int ath9k_hw_ar9280_set_channel(struct ath_hw *ah, struct ath9k_channel *chan); +void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan); + +/* Routines below are for non single-chip solutions */ +int ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan); +void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan); + +int ath9k_hw_rf_alloc_ext_banks(struct ath_hw *ah); +void ath9k_hw_rf_free_ext_banks(struct ath_hw *ah); + bool ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, u16 modesIndex); -void ath9k_hw_decrease_chain_power(struct ath_hw *ah, - struct ath9k_channel *chan); -bool ath9k_hw_init_rf(struct ath_hw *ah, - int *status); #define AR_PHY_BASE 0x9800 #define AR_PHY(_n) (AR_PHY_BASE + ((_n)<<2)) @@ -45,6 +48,7 @@ bool ath9k_hw_init_rf(struct ath_hw *ah, #define AR_PHY_FC_DYN2040_EN 0x00000004 #define AR_PHY_FC_DYN2040_PRI_ONLY 0x00000008 #define AR_PHY_FC_DYN2040_PRI_CH 0x00000010 +/* For 25 MHz channel spacing -- not used but supported by hw */ #define AR_PHY_FC_DYN2040_EXT_CH 0x00000020 #define AR_PHY_FC_HT_EN 0x00000040 #define AR_PHY_FC_SHORT_GI_40 0x00000080 @@ -185,8 +189,20 @@ bool ath9k_hw_init_rf(struct ath_hw *ah, #define AR_PHY_PLL_CTL_44_2133 0xeb #define AR_PHY_PLL_CTL_40_2133 0xea -#define AR_PHY_SPECTRAL_SCAN 0x9912 -#define AR_PHY_SPECTRAL_SCAN_ENABLE 0x1 +#define AR_PHY_SPECTRAL_SCAN 0x9910 /* AR9280 spectral scan configuration register */ +#define AR_PHY_SPECTRAL_SCAN_ENABLE 0x1 +#define AR_PHY_SPECTRAL_SCAN_ENA 0x00000001 /* Enable spectral scan, reg 68, bit 0 */ +#define AR_PHY_SPECTRAL_SCAN_ENA_S 0 /* Enable spectral scan, reg 68, bit 0 */ +#define AR_PHY_SPECTRAL_SCAN_ACTIVE 0x00000002 /* Activate spectral scan reg 68, bit 1*/ +#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S 1 /* Activate spectral scan reg 68, bit 1*/ +#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD 0x000000F0 /* Interval for FFT reports, reg 68, bits 4-7*/ +#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S 4 +#define AR_PHY_SPECTRAL_SCAN_PERIOD 0x0000FF00 /* Interval for FFT reports, reg 68, bits 8-15*/ +#define AR_PHY_SPECTRAL_SCAN_PERIOD_S 8 +#define AR_PHY_SPECTRAL_SCAN_COUNT 0x00FF0000 /* Number of reports, reg 68, bits 16-23*/ +#define AR_PHY_SPECTRAL_SCAN_COUNT_S 16 +#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT 0x01000000 /* Short repeat, reg 68, bit 24*/ +#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24 /* Short repeat, reg 68, bit 24*/ #define AR_PHY_RX_DELAY 0x9914 #define AR_PHY_SEARCH_START_DELAY 0x9918 diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 1895d63aad0a..70fdb9d8db82 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -19,133 +19,92 @@ static const struct ath_rate_table ar5416_11na_ratetable = { 42, + 8, /* MCS start */ { { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ - 5400, 0x0b, 0x00, 12, - 0, 0, 0, 0, 0, 0 }, + 5400, 0, 12, 0, 0, 0, 0, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ - 7800, 0x0f, 0x00, 18, - 0, 1, 1, 1, 1, 0 }, + 7800, 1, 18, 0, 1, 1, 1, 1 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ - 10000, 0x0a, 0x00, 24, - 2, 2, 2, 2, 2, 0 }, + 10000, 2, 24, 2, 2, 2, 2, 2 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ - 13900, 0x0e, 0x00, 36, - 2, 3, 3, 3, 3, 0 }, + 13900, 3, 36, 2, 3, 3, 3, 3 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ - 17300, 0x09, 0x00, 48, - 4, 4, 4, 4, 4, 0 }, + 17300, 4, 48, 4, 4, 4, 4, 4 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ - 23000, 0x0d, 0x00, 72, - 4, 5, 5, 5, 5, 0 }, + 23000, 5, 72, 4, 5, 5, 5, 5 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ - 27400, 0x08, 0x00, 96, - 4, 6, 6, 6, 6, 0 }, + 27400, 6, 96, 4, 6, 6, 6, 6 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ - 29300, 0x0c, 0x00, 108, - 4, 7, 7, 7, 7, 0 }, + 29300, 7, 108, 4, 7, 7, 7, 7 }, { VALID_2040, VALID_2040, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */ - 6400, 0x80, 0x00, 0, - 0, 8, 24, 8, 24, 3216 }, + 6400, 0, 0, 0, 8, 24, 8, 24 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */ - 12700, 0x81, 0x00, 1, - 2, 9, 25, 9, 25, 6434 }, + 12700, 1, 1, 2, 9, 25, 9, 25 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */ - 18800, 0x82, 0x00, 2, - 2, 10, 26, 10, 26, 9650 }, + 18800, 2, 2, 2, 10, 26, 10, 26 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */ - 25000, 0x83, 0x00, 3, - 4, 11, 27, 11, 27, 12868 }, + 25000, 3, 3, 4, 11, 27, 11, 27 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */ - 36700, 0x84, 0x00, 4, - 4, 12, 28, 12, 28, 19304 }, + 36700, 4, 4, 4, 12, 28, 12, 28 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */ - 48100, 0x85, 0x00, 5, - 4, 13, 29, 13, 29, 25740 }, + 48100, 5, 5, 4, 13, 29, 13, 29 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */ - 53500, 0x86, 0x00, 6, - 4, 14, 30, 14, 30, 28956 }, + 53500, 6, 6, 4, 14, 30, 14, 30 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */ - 59000, 0x87, 0x00, 7, - 4, 15, 31, 15, 32, 32180 }, + 59000, 7, 7, 4, 15, 31, 15, 32 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */ - 12700, 0x88, 0x00, - 8, 3, 16, 33, 16, 33, 6430 }, + 12700, 8, 8, 3, 16, 33, 16, 33 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */ - 24800, 0x89, 0x00, 9, - 2, 17, 34, 17, 34, 12860 }, + 24800, 9, 9, 2, 17, 34, 17, 34 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */ - 36600, 0x8a, 0x00, 10, - 2, 18, 35, 18, 35, 19300 }, + 36600, 10, 10, 2, 18, 35, 18, 35 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */ - 48100, 0x8b, 0x00, 11, - 4, 19, 36, 19, 36, 25736 }, + 48100, 11, 11, 4, 19, 36, 19, 36 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */ - 69500, 0x8c, 0x00, 12, - 4, 20, 37, 20, 37, 38600 }, + 69500, 12, 12, 4, 20, 37, 20, 37 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */ - 89500, 0x8d, 0x00, 13, - 4, 21, 38, 21, 38, 51472 }, + 89500, 13, 13, 4, 21, 38, 21, 38 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */ - 98900, 0x8e, 0x00, 14, - 4, 22, 39, 22, 39, 57890 }, + 98900, 14, 14, 4, 22, 39, 22, 39 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */ - 108300, 0x8f, 0x00, 15, - 4, 23, 40, 23, 41, 64320 }, + 108300, 15, 15, 4, 23, 40, 23, 41 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */ - 13200, 0x80, 0x00, 0, - 0, 8, 24, 24, 24, 6684 }, + 13200, 0, 0, 0, 8, 24, 24, 24 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */ - 25900, 0x81, 0x00, 1, - 2, 9, 25, 25, 25, 13368 }, + 25900, 1, 1, 2, 9, 25, 25, 25 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */ - 38600, 0x82, 0x00, 2, - 2, 10, 26, 26, 26, 20052 }, + 38600, 2, 2, 2, 10, 26, 26, 26 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */ - 49800, 0x83, 0x00, 3, - 4, 11, 27, 27, 27, 26738 }, + 49800, 3, 3, 4, 11, 27, 27, 27 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */ - 72200, 0x84, 0x00, 4, - 4, 12, 28, 28, 28, 40104 }, + 72200, 4, 4, 4, 12, 28, 28, 28 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */ - 92900, 0x85, 0x00, 5, - 4, 13, 29, 29, 29, 53476 }, + 92900, 5, 5, 4, 13, 29, 29, 29 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */ - 102700, 0x86, 0x00, 6, - 4, 14, 30, 30, 30, 60156 }, + 102700, 6, 6, 4, 14, 30, 30, 30 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */ - 112000, 0x87, 0x00, 7, - 4, 15, 31, 32, 32, 66840 }, + 112000, 7, 7, 4, 15, 31, 32, 32 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */ - 122000, 0x87, 0x00, 7, - 4, 15, 31, 32, 32, 74200 }, + 122000, 7, 7, 4, 15, 31, 32, 32 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */ - 25800, 0x88, 0x00, 8, - 0, 16, 33, 33, 33, 13360 }, + 25800, 8, 8, 0, 16, 33, 33, 33 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */ - 49800, 0x89, 0x00, 9, - 2, 17, 34, 34, 34, 26720 }, + 49800, 9, 9, 2, 17, 34, 34, 34 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */ - 71900, 0x8a, 0x00, 10, - 2, 18, 35, 35, 35, 40080 }, + 71900, 10, 10, 2, 18, 35, 35, 35 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */ - 92500, 0x8b, 0x00, 11, - 4, 19, 36, 36, 36, 53440 }, + 92500, 11, 11, 4, 19, 36, 36, 36 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */ - 130300, 0x8c, 0x00, 12, - 4, 20, 37, 37, 37, 80160 }, + 130300, 12, 12, 4, 20, 37, 37, 37 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */ - 162800, 0x8d, 0x00, 13, - 4, 21, 38, 38, 38, 106880 }, + 162800, 13, 13, 4, 21, 38, 38, 38 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */ - 178200, 0x8e, 0x00, 14, - 4, 22, 39, 39, 39, 120240 }, + 178200, 14, 14, 4, 22, 39, 39, 39 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */ - 192100, 0x8f, 0x00, 15, - 4, 23, 40, 41, 41, 133600 }, + 192100, 15, 15, 4, 23, 40, 41, 41 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */ - 207000, 0x8f, 0x00, 15, - 4, 23, 40, 41, 41, 148400 }, + 207000, 15, 15, 4, 23, 40, 41, 41 }, }, 50, /* probe interval */ WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ @@ -156,177 +115,125 @@ static const struct ath_rate_table ar5416_11na_ratetable = { static const struct ath_rate_table ar5416_11ng_ratetable = { 46, + 12, /* MCS start */ { { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ - 900, 0x1b, 0x00, 2, - 0, 0, 0, 0, 0, 0 }, + 900, 0, 2, 0, 0, 0, 0, 0 }, { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ - 1900, 0x1a, 0x04, 4, - 1, 1, 1, 1, 1, 0 }, + 1900, 1, 4, 1, 1, 1, 1, 1 }, { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ - 4900, 0x19, 0x04, 11, - 2, 2, 2, 2, 2, 0 }, + 4900, 2, 11, 2, 2, 2, 2, 2 }, { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ - 8100, 0x18, 0x04, 22, - 3, 3, 3, 3, 3, 0 }, + 8100, 3, 22, 3, 3, 3, 3, 3 }, { INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ - 5400, 0x0b, 0x00, 12, - 4, 4, 4, 4, 4, 0 }, + 5400, 4, 12, 4, 4, 4, 4, 4 }, { INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ - 7800, 0x0f, 0x00, 18, - 4, 5, 5, 5, 5, 0 }, + 7800, 5, 18, 4, 5, 5, 5, 5 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ - 10100, 0x0a, 0x00, 24, - 6, 6, 6, 6, 6, 0 }, + 10100, 6, 24, 6, 6, 6, 6, 6 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ - 14100, 0x0e, 0x00, 36, - 6, 7, 7, 7, 7, 0 }, + 14100, 7, 36, 6, 7, 7, 7, 7 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ - 17700, 0x09, 0x00, 48, - 8, 8, 8, 8, 8, 0 }, + 17700, 8, 48, 8, 8, 8, 8, 8 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ - 23700, 0x0d, 0x00, 72, - 8, 9, 9, 9, 9, 0 }, + 23700, 9, 72, 8, 9, 9, 9, 9 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ - 27400, 0x08, 0x00, 96, - 8, 10, 10, 10, 10, 0 }, + 27400, 10, 96, 8, 10, 10, 10, 10 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ - 30900, 0x0c, 0x00, 108, - 8, 11, 11, 11, 11, 0 }, + 30900, 11, 108, 8, 11, 11, 11, 11 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */ - 6400, 0x80, 0x00, 0, - 4, 12, 28, 12, 28, 3216 }, + 6400, 0, 0, 4, 12, 28, 12, 28 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */ - 12700, 0x81, 0x00, 1, - 6, 13, 29, 13, 29, 6434 }, + 12700, 1, 1, 6, 13, 29, 13, 29 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */ - 18800, 0x82, 0x00, 2, - 6, 14, 30, 14, 30, 9650 }, + 18800, 2, 2, 6, 14, 30, 14, 30 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */ - 25000, 0x83, 0x00, 3, - 8, 15, 31, 15, 31, 12868 }, + 25000, 3, 3, 8, 15, 31, 15, 31 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */ - 36700, 0x84, 0x00, 4, - 8, 16, 32, 16, 32, 19304 }, + 36700, 4, 4, 8, 16, 32, 16, 32 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */ - 48100, 0x85, 0x00, 5, - 8, 17, 33, 17, 33, 25740 }, + 48100, 5, 5, 8, 17, 33, 17, 33 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */ - 53500, 0x86, 0x00, 6, - 8, 18, 34, 18, 34, 28956 }, + 53500, 6, 6, 8, 18, 34, 18, 34 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */ - 59000, 0x87, 0x00, 7, - 8, 19, 35, 19, 36, 32180 }, + 59000, 7, 7, 8, 19, 35, 19, 36 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */ - 12700, 0x88, 0x00, 8, - 4, 20, 37, 20, 37, 6430 }, + 12700, 8, 8, 4, 20, 37, 20, 37 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */ - 24800, 0x89, 0x00, 9, - 6, 21, 38, 21, 38, 12860 }, + 24800, 9, 9, 6, 21, 38, 21, 38 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */ - 36600, 0x8a, 0x00, 10, - 6, 22, 39, 22, 39, 19300 }, + 36600, 10, 10, 6, 22, 39, 22, 39 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */ - 48100, 0x8b, 0x00, 11, - 8, 23, 40, 23, 40, 25736 }, + 48100, 11, 11, 8, 23, 40, 23, 40 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */ - 69500, 0x8c, 0x00, 12, - 8, 24, 41, 24, 41, 38600 }, + 69500, 12, 12, 8, 24, 41, 24, 41 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */ - 89500, 0x8d, 0x00, 13, - 8, 25, 42, 25, 42, 51472 }, + 89500, 13, 13, 8, 25, 42, 25, 42 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */ - 98900, 0x8e, 0x00, 14, - 8, 26, 43, 26, 44, 57890 }, + 98900, 14, 14, 8, 26, 43, 26, 44 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */ - 108300, 0x8f, 0x00, 15, - 8, 27, 44, 27, 45, 64320 }, + 108300, 15, 15, 8, 27, 44, 27, 45 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */ - 13200, 0x80, 0x00, 0, - 8, 12, 28, 28, 28, 6684 }, + 13200, 0, 0, 8, 12, 28, 28, 28 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */ - 25900, 0x81, 0x00, 1, - 8, 13, 29, 29, 29, 13368 }, + 25900, 1, 1, 8, 13, 29, 29, 29 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */ - 38600, 0x82, 0x00, 2, - 8, 14, 30, 30, 30, 20052 }, + 38600, 2, 2, 8, 14, 30, 30, 30 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */ - 49800, 0x83, 0x00, 3, - 8, 15, 31, 31, 31, 26738 }, + 49800, 3, 3, 8, 15, 31, 31, 31 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */ - 72200, 0x84, 0x00, 4, - 8, 16, 32, 32, 32, 40104 }, + 72200, 4, 4, 8, 16, 32, 32, 32 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */ - 92900, 0x85, 0x00, 5, - 8, 17, 33, 33, 33, 53476 }, + 92900, 5, 5, 8, 17, 33, 33, 33 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */ - 102700, 0x86, 0x00, 6, - 8, 18, 34, 34, 34, 60156 }, + 102700, 6, 6, 8, 18, 34, 34, 34 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */ - 112000, 0x87, 0x00, 7, - 8, 19, 35, 36, 36, 66840 }, + 112000, 7, 7, 8, 19, 35, 36, 36 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */ - 122000, 0x87, 0x00, 7, - 8, 19, 35, 36, 36, 74200 }, + 122000, 7, 7, 8, 19, 35, 36, 36 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */ - 25800, 0x88, 0x00, 8, - 8, 20, 37, 37, 37, 13360 }, + 25800, 8, 8, 8, 20, 37, 37, 37 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */ - 49800, 0x89, 0x00, 9, - 8, 21, 38, 38, 38, 26720 }, + 49800, 9, 9, 8, 21, 38, 38, 38 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */ - 71900, 0x8a, 0x00, 10, - 8, 22, 39, 39, 39, 40080 }, + 71900, 10, 10, 8, 22, 39, 39, 39 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */ - 92500, 0x8b, 0x00, 11, - 8, 23, 40, 40, 40, 53440 }, + 92500, 11, 11, 8, 23, 40, 40, 40 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */ - 130300, 0x8c, 0x00, 12, - 8, 24, 41, 41, 41, 80160 }, + 130300, 12, 12, 8, 24, 41, 41, 41 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */ - 162800, 0x8d, 0x00, 13, - 8, 25, 42, 42, 42, 106880 }, + 162800, 13, 13, 8, 25, 42, 42, 42 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */ - 178200, 0x8e, 0x00, 14, - 8, 26, 43, 43, 43, 120240 }, + 178200, 14, 14, 8, 26, 43, 43, 43 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */ - 192100, 0x8f, 0x00, 15, - 8, 27, 44, 45, 45, 133600 }, + 192100, 15, 15, 8, 27, 44, 45, 45 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */ - 207000, 0x8f, 0x00, 15, - 8, 27, 44, 45, 45, 148400 }, - }, + 207000, 15, 15, 8, 27, 44, 45, 45 }, + }, 50, /* probe interval */ WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ }; static const struct ath_rate_table ar5416_11a_ratetable = { 8, + 0, { { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ - 5400, 0x0b, 0x00, (0x80|12), - 0, 0, 0 }, + 5400, 0, 12, 0, 0, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ - 7800, 0x0f, 0x00, 18, - 0, 1, 0 }, + 7800, 1, 18, 0, 1, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ - 10000, 0x0a, 0x00, (0x80|24), - 2, 2, 0 }, + 10000, 2, 24, 2, 2, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ - 13900, 0x0e, 0x00, 36, - 2, 3, 0 }, + 13900, 3, 36, 2, 3, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ - 17300, 0x09, 0x00, (0x80|48), - 4, 4, 0 }, + 17300, 4, 48, 4, 4, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ - 23000, 0x0d, 0x00, 72, - 4, 5, 0 }, + 23000, 5, 72, 4, 5, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ - 27400, 0x08, 0x00, 96, - 4, 6, 0 }, + 27400, 6, 96, 4, 6, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ - 29300, 0x0c, 0x00, 108, - 4, 7, 0 }, + 29300, 7, 108, 4, 7, 0 }, }, 50, /* probe interval */ 0, /* Phy rates allowed initially */ @@ -334,48 +241,51 @@ static const struct ath_rate_table ar5416_11a_ratetable = { static const struct ath_rate_table ar5416_11g_ratetable = { 12, + 0, { { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ - 900, 0x1b, 0x00, 2, - 0, 0, 0 }, + 900, 0, 2, 0, 0, 0 }, { VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ - 1900, 0x1a, 0x04, 4, - 1, 1, 0 }, + 1900, 1, 4, 1, 1, 0 }, { VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ - 4900, 0x19, 0x04, 11, - 2, 2, 0 }, + 4900, 2, 11, 2, 2, 0 }, { VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ - 8100, 0x18, 0x04, 22, - 3, 3, 0 }, + 8100, 3, 22, 3, 3, 0 }, { INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ - 5400, 0x0b, 0x00, 12, - 4, 4, 0 }, + 5400, 4, 12, 4, 4, 0 }, { INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ - 7800, 0x0f, 0x00, 18, - 4, 5, 0 }, + 7800, 5, 18, 4, 5, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ - 10000, 0x0a, 0x00, 24, - 6, 6, 0 }, + 10000, 6, 24, 6, 6, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ - 13900, 0x0e, 0x00, 36, - 6, 7, 0 }, + 13900, 7, 36, 6, 7, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ - 17300, 0x09, 0x00, 48, - 8, 8, 0 }, + 17300, 8, 48, 8, 8, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ - 23000, 0x0d, 0x00, 72, - 8, 9, 0 }, + 23000, 9, 72, 8, 9, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ - 27400, 0x08, 0x00, 96, - 8, 10, 0 }, + 27400, 10, 96, 8, 10, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ - 29300, 0x0c, 0x00, 108, - 8, 11, 0 }, + 29300, 11, 108, 8, 11, 0 }, }, 50, /* probe interval */ 0, /* Phy rates allowed initially */ }; +static const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX] = { + [ATH9K_MODE_11A] = &ar5416_11a_ratetable, + [ATH9K_MODE_11G] = &ar5416_11g_ratetable, + [ATH9K_MODE_11NA_HT20] = &ar5416_11na_ratetable, + [ATH9K_MODE_11NG_HT20] = &ar5416_11ng_ratetable, + [ATH9K_MODE_11NA_HT40PLUS] = &ar5416_11na_ratetable, + [ATH9K_MODE_11NA_HT40MINUS] = &ar5416_11na_ratetable, + [ATH9K_MODE_11NG_HT40PLUS] = &ar5416_11ng_ratetable, + [ATH9K_MODE_11NG_HT40MINUS] = &ar5416_11ng_ratetable, +}; + +static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table, + struct ieee80211_tx_rate *rate); + static inline int8_t median(int8_t a, int8_t b, int8_t c) { if (a >= b) { @@ -425,7 +335,7 @@ static void ath_rc_init_valid_txmask(struct ath_rate_priv *ath_rc_priv) static inline void ath_rc_set_valid_txmask(struct ath_rate_priv *ath_rc_priv, u8 index, int valid_tx_rate) { - ASSERT(index <= ath_rc_priv->rate_table_size); + BUG_ON(index > ath_rc_priv->rate_table_size); ath_rc_priv->valid_rate_index[index] = valid_tx_rate ? 1 : 0; } @@ -534,7 +444,7 @@ static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, * capflag matches one of the validity * (VALID/VALID_20/VALID_40) flags */ - if (((rate & 0x7F) == (dot11rate & 0x7F)) && + if ((rate == dot11rate) && ((valid & WLAN_RC_CAP_MODE(capflag)) == WLAN_RC_CAP_MODE(capflag)) && !WLAN_RC_PHY_HT(phy)) { @@ -576,8 +486,7 @@ static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv, u8 rate = rateset->rs_rates[i]; u8 dot11rate = rate_table->info[j].dot11rate; - if (((rate & 0x7F) != (dot11rate & 0x7F)) || - !WLAN_RC_PHY_HT(phy) || + if ((rate != dot11rate) || !WLAN_RC_PHY_HT(phy) || !WLAN_RC_PHY_HT_VALID(valid, capflag)) continue; @@ -696,18 +605,20 @@ static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table, u8 tries, u8 rix, int rtsctsenable) { rate->count = tries; - rate->idx = rix; + rate->idx = rate_table->info[rix].ratecode; if (txrc->short_preamble) rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; if (txrc->rts || rtsctsenable) rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; - if (WLAN_RC_PHY_40(rate_table->info[rix].phy)) - rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy)) - rate->flags |= IEEE80211_TX_RC_SHORT_GI; - if (WLAN_RC_PHY_HT(rate_table->info[rix].phy)) + + if (WLAN_RC_PHY_HT(rate_table->info[rix].phy)) { rate->flags |= IEEE80211_TX_RC_MCS; + if (WLAN_RC_PHY_40(rate_table->info[rix].phy)) + rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy)) + rate->flags |= IEEE80211_TX_RC_SHORT_GI; + } } static void ath_rc_rate_set_rtscts(struct ath_softc *sc, @@ -720,7 +631,7 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc, /* get the cix for the lowest valid rix */ for (i = 3; i >= 0; i--) { if (rates[i].count && (rates[i].idx >= 0)) { - rix = rates[i].idx; + rix = ath_rc_get_rateindex(rate_table, &rates[i]); break; } } @@ -859,12 +770,12 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, static bool ath_rc_update_per(struct ath_softc *sc, const struct ath_rate_table *rate_table, struct ath_rate_priv *ath_rc_priv, - struct ath_tx_info_priv *tx_info_priv, + struct ieee80211_tx_info *tx_info, int tx_rate, int xretries, int retries, u32 now_msec) { bool state_change = false; - int count; + int count, n_bad_frames; u8 last_per; static u32 nretry_to_per_lookup[10] = { 100 * 0 / 1, @@ -880,6 +791,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, }; last_per = ath_rc_priv->per[tx_rate]; + n_bad_frames = tx_info->status.ampdu_len - tx_info->status.ampdu_ack_len; if (xretries) { if (xretries == 1) { @@ -907,7 +819,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, if (retries >= count) retries = count - 1; - if (tx_info_priv->n_bad_frames) { + if (n_bad_frames) { /* new_PER = 7/8*old_PER + 1/8*(currentPER) * Assuming that n_frames is not 0. The current PER * from the retries is 100 * retries / (retries+1), @@ -920,14 +832,14 @@ static bool ath_rc_update_per(struct ath_softc *sc, * the above PER. The expression below is a * simplified version of the sum of these two terms. */ - if (tx_info_priv->n_frames > 0) { - int n_frames, n_bad_frames; + if (tx_info->status.ampdu_len > 0) { + int n_frames, n_bad_tries; u8 cur_per, new_per; - n_bad_frames = retries * tx_info_priv->n_frames + - tx_info_priv->n_bad_frames; - n_frames = tx_info_priv->n_frames * (retries + 1); - cur_per = (100 * n_bad_frames / n_frames) >> 3; + n_bad_tries = retries * tx_info->status.ampdu_len + + n_bad_frames; + n_frames = tx_info->status.ampdu_len * (retries + 1); + cur_per = (100 * n_bad_tries / n_frames) >> 3; new_per = (u8)(last_per - (last_per >> 3) + cur_per); ath_rc_priv->per[tx_rate] = new_per; } @@ -943,8 +855,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, * this was a probe. Otherwise, ignore the probe. */ if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) { - if (retries > 0 || 2 * tx_info_priv->n_bad_frames > - tx_info_priv->n_frames) { + if (retries > 0 || 2 * n_bad_frames > tx_info->status.ampdu_len) { /* * Since we probed with just a single attempt, * any retries means the probe failed. Also, @@ -969,7 +880,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, * Since this probe succeeded, we allow the next * probe twice as soon. This allows the maxRate * to move up faster if the probes are - * succesful. + * successful. */ ath_rc_priv->probe_time = now_msec - rate_table->probe_interval / 2; @@ -1003,7 +914,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, static void ath_rc_update_ht(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, - struct ath_tx_info_priv *tx_info_priv, + struct ieee80211_tx_info *tx_info, int tx_rate, int xretries, int retries) { u32 now_msec = jiffies_to_msecs(jiffies); @@ -1020,7 +931,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, /* Update PER first */ state_change = ath_rc_update_per(sc, rate_table, ath_rc_priv, - tx_info_priv, tx_rate, xretries, + tx_info, tx_rate, xretries, retries, now_msec); /* @@ -1080,15 +991,19 @@ static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table, { int rix; + if (!(rate->flags & IEEE80211_TX_RC_MCS)) + return rate->idx; + + rix = rate->idx + rate_table->mcs_start; if ((rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) && (rate->flags & IEEE80211_TX_RC_SHORT_GI)) - rix = rate_table->info[rate->idx].ht_index; + rix = rate_table->info[rix].ht_index; else if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - rix = rate_table->info[rate->idx].sgi_index; + rix = rate_table->info[rix].sgi_index; else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - rix = rate_table->info[rate->idx].cw40index; + rix = rate_table->info[rix].cw40index; else - rix = rate_table->info[rate->idx].base_index; + rix = rate_table->info[rix].base_index; return rix; } @@ -1098,7 +1013,6 @@ static void ath_rc_tx_status(struct ath_softc *sc, struct ieee80211_tx_info *tx_info, int final_ts_idx, int xretries, int long_retry) { - struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); const struct ath_rate_table *rate_table; struct ieee80211_tx_rate *rates = tx_info->status.rates; u8 flags; @@ -1124,9 +1038,8 @@ static void ath_rc_tx_status(struct ath_softc *sc, return; rix = ath_rc_get_rateindex(rate_table, &rates[i]); - ath_rc_update_ht(sc, ath_rc_priv, - tx_info_priv, rix, - xretries ? 1 : 2, + ath_rc_update_ht(sc, ath_rc_priv, tx_info, + rix, xretries ? 1 : 2, rates[i].count); } } @@ -1149,8 +1062,7 @@ static void ath_rc_tx_status(struct ath_softc *sc, return; rix = ath_rc_get_rateindex(rate_table, &rates[i]); - ath_rc_update_ht(sc, ath_rc_priv, tx_info_priv, rix, - xretries, long_retry); + ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry); } static const @@ -1160,6 +1072,7 @@ struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, bool is_cw_40) { int mode = 0; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); switch(band) { case IEEE80211_BAND_2GHZ: @@ -1177,14 +1090,17 @@ struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, mode = ATH9K_MODE_11NA_HT40PLUS; break; default: - DPRINTF(sc, ATH_DBG_CONFIG, "Invalid band\n"); + ath_print(common, ATH_DBG_CONFIG, "Invalid band\n"); return NULL; } BUG_ON(mode >= ATH9K_MODE_MAX); - DPRINTF(sc, ATH_DBG_CONFIG, "Choosing rate table for mode: %d\n", mode); - return sc->hw_rate_table[mode]; + ath_print(common, ATH_DBG_CONFIG, + "Choosing rate table for mode: %d\n", mode); + + sc->cur_rate_mode = mode; + return hw_rate_table[mode]; } static void ath_rc_init(struct ath_softc *sc, @@ -1194,14 +1110,10 @@ static void ath_rc_init(struct ath_softc *sc, const struct ath_rate_table *rate_table) { struct ath_rateset *rateset = &ath_rc_priv->neg_rates; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 *ht_mcs = (u8 *)&ath_rc_priv->neg_ht_rates; u8 i, j, k, hi = 0, hthi = 0; - if (!rate_table) { - DPRINTF(sc, ATH_DBG_FATAL, "Rate table not initialized\n"); - return; - } - /* Initial rate table size. Will change depending * on the working rate set */ ath_rc_priv->rate_table_size = RATE_TABLE_SIZE; @@ -1239,7 +1151,7 @@ static void ath_rc_init(struct ath_softc *sc, ath_rc_priv->rate_table_size = hi + 1; ath_rc_priv->rate_max_phy = 0; - ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE); + BUG_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE); for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) { for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) { @@ -1253,16 +1165,17 @@ static void ath_rc_init(struct ath_softc *sc, ath_rc_priv->rate_max_phy = ath_rc_priv->valid_phy_rateidx[i][j-1]; } - ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE); - ASSERT(k <= RATE_TABLE_SIZE); + BUG_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE); + BUG_ON(k > RATE_TABLE_SIZE); ath_rc_priv->max_valid_rate = k; ath_rc_sort_validrates(rate_table, ath_rc_priv); ath_rc_priv->rate_max_phy = ath_rc_priv->valid_rate_index[k-4]; sc->cur_rate_table = rate_table; - DPRINTF(sc, ATH_DBG_CONFIG, "RC Initialized with capabilities: 0x%x\n", - ath_rc_priv->ht_cap); + ath_print(common, ATH_DBG_CONFIG, + "RC Initialized with capabilities: 0x%x\n", + ath_rc_priv->ht_cap); } static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta, @@ -1296,44 +1209,52 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, { struct ath_softc *sc = priv; struct ath_rate_priv *ath_rc_priv = priv_sta; - struct ath_tx_info_priv *tx_info_priv = NULL; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr; - int final_ts_idx, tx_status = 0, is_underrun = 0; + int final_ts_idx = 0, tx_status = 0, is_underrun = 0; + int long_retry = 0; __le16 fc; + int i; hdr = (struct ieee80211_hdr *)skb->data; fc = hdr->frame_control; - tx_info_priv = ATH_TX_INFO_PRIV(tx_info); - final_ts_idx = tx_info_priv->tx.ts_rateindex; + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + struct ieee80211_tx_rate *rate = &tx_info->status.rates[i]; + if (!rate->count) + break; + + final_ts_idx = i; + long_retry = rate->count - 1; + } if (!priv_sta || !ieee80211_is_data(fc) || - !tx_info_priv->update_rc) - goto exit; + !(tx_info->pad[0] & ATH_TX_INFO_UPDATE_RC)) + return; - if (tx_info_priv->tx.ts_status & ATH9K_TXERR_FILT) - goto exit; + if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) + return; /* - * If underrun error is seen assume it as an excessive retry only - * if prefetch trigger level have reached the max (0x3f for 5416) - * Adjust the long retry as if the frame was tried hw->max_rate_tries - * times. This affects how ratectrl updates PER for the failed rate. + * If an underrun error is seen assume it as an excessive retry only + * if max frame trigger level has been reached (2 KB for singel stream, + * and 4 KB for dual stream). Adjust the long retry as if the frame was + * tried hw->max_rate_tries times to affect how ratectrl updates PER for + * the failed rate. In case of congestion on the bus penalizing these + * type of underruns should help hardware actually transmit new frames + * successfully by eventually preferring slower rates. This itself + * should also alleviate congestion on the bus. */ - if (tx_info_priv->tx.ts_flags & - (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) && - ((sc->sc_ah->tx_trig_level) >= ath_rc_priv->tx_triglevel_max)) { + if ((tx_info->pad[0] & ATH_TX_INFO_UNDERRUN) && + (sc->sc_ah->tx_trig_level >= ath_rc_priv->tx_triglevel_max)) { tx_status = 1; is_underrun = 1; } - if ((tx_info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) || - (tx_info_priv->tx.ts_status & ATH9K_TXERR_FIFO)) + if (tx_info->pad[0] & ATH_TX_INFO_XRETRY) tx_status = 1; ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status, - (is_underrun) ? sc->hw->max_rate_tries : - tx_info_priv->tx.ts_longretry); + (is_underrun) ? sc->hw->max_rate_tries : long_retry); /* Check if aggregation has to be enabled for this tid */ if (conf_is_ht(&sc->hw->conf) && @@ -1347,13 +1268,12 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, an = (struct ath_node *)sta->drv_priv; if(ath_tx_aggr_check(sc, an, tid)) - ieee80211_start_tx_ba_session(sc->hw, hdr->addr1, tid); + ieee80211_start_tx_ba_session(sta, tid); } } - ath_debug_stat_rc(sc, skb); -exit: - kfree(tx_info_priv); + ath_debug_stat_rc(sc, ath_rc_get_rateindex(sc->cur_rate_table, + &tx_info->status.rates[final_ts_idx])); } static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, @@ -1361,7 +1281,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, { struct ath_softc *sc = priv; struct ath_rate_priv *ath_rc_priv = priv_sta; - const struct ath_rate_table *rate_table = NULL; + const struct ath_rate_table *rate_table; bool is_cw40, is_sgi40; int i, j = 0; @@ -1393,11 +1313,9 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) || (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)) { rate_table = ath_choose_rate_table(sc, sband->band, - sta->ht_cap.ht_supported, - is_cw40); - } else if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { - /* cur_rate_table would be set on init through config() */ - rate_table = sc->cur_rate_table; + sta->ht_cap.ht_supported, is_cw40); + } else { + rate_table = hw_rate_table[sc->cur_rate_mode]; } ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi40); @@ -1438,9 +1356,10 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, oper_cw40, oper_sgi40); ath_rc_init(sc, priv_sta, sband, sta, rate_table); - DPRINTF(sc, ATH_DBG_CONFIG, - "Operating HT Bandwidth changed to: %d\n", - sc->hw->conf.channel_type); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG, + "Operating HT Bandwidth changed to: %d\n", + sc->hw->conf.channel_type); + sc->cur_rate_table = hw_rate_table[sc->cur_rate_mode]; } } } @@ -1463,8 +1382,8 @@ static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp rate_priv = kzalloc(sizeof(struct ath_rate_priv), gfp); if (!rate_priv) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to allocate private rc structure\n"); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "Unable to allocate private rc structure\n"); return NULL; } @@ -1493,26 +1412,6 @@ static struct rate_control_ops ath_rate_ops = { .free_sta = ath_rate_free_sta, }; -void ath_rate_attach(struct ath_softc *sc) -{ - sc->hw_rate_table[ATH9K_MODE_11A] = - &ar5416_11a_ratetable; - sc->hw_rate_table[ATH9K_MODE_11G] = - &ar5416_11g_ratetable; - sc->hw_rate_table[ATH9K_MODE_11NA_HT20] = - &ar5416_11na_ratetable; - sc->hw_rate_table[ATH9K_MODE_11NG_HT20] = - &ar5416_11ng_ratetable; - sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS] = - &ar5416_11na_ratetable; - sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS] = - &ar5416_11na_ratetable; - sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS] = - &ar5416_11ng_ratetable; - sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS] = - &ar5416_11ng_ratetable; -} - int ath_rate_control_register(void) { return ieee80211_rate_control_register(&ath_rate_ops); diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index fa21a628ddd0..9eb96f506998 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -19,6 +19,8 @@ #ifndef RC_H #define RC_H +#include "hw.h" + struct ath_softc; #define ATH_RATE_MAX 30 @@ -102,6 +104,7 @@ enum { */ struct ath_rate_table { int rate_cnt; + int mcs_start; struct { int valid; int valid_single_stream; @@ -109,14 +112,12 @@ struct ath_rate_table { u32 ratekbps; u32 user_ratekbps; u8 ratecode; - u8 short_preamble; u8 dot11rate; u8 ctrl_rate; u8 base_index; u8 cw40index; u8 sgi_index; u8 ht_index; - u32 max_4ms_framelen; } info[RATE_TABLE_SIZE]; u32 probe_interval; u8 initial_ratemax; @@ -165,26 +166,18 @@ struct ath_rate_priv { struct ath_rate_softc *asc; }; +#define ATH_TX_INFO_FRAME_TYPE_INTERNAL (1 << 0) +#define ATH_TX_INFO_FRAME_TYPE_PAUSE (1 << 1) +#define ATH_TX_INFO_UPDATE_RC (1 << 2) +#define ATH_TX_INFO_XRETRY (1 << 3) +#define ATH_TX_INFO_UNDERRUN (1 << 4) + enum ath9k_internal_frame_type { ATH9K_NOT_INTERNAL, ATH9K_INT_PAUSE, ATH9K_INT_UNPAUSE }; -struct ath_tx_info_priv { - struct ath_wiphy *aphy; - struct ath_tx_status tx; - int n_frames; - int n_bad_frames; - bool update_rc; - enum ath9k_internal_frame_type frame_type; -}; - -#define ATH_TX_INFO_PRIV(tx_info) \ - ((struct ath_tx_info_priv *)((tx_info)->rate_driver_data[0])) - -void ath_rate_attach(struct ath_softc *sc); -u8 ath_rate_findrateix(struct ath_softc *sc, u8 dot11_rate); int ath_rate_control_register(void); void ath_rate_control_unregister(void); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index ec0abf823995..477365e5ae69 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -48,6 +48,7 @@ static struct ieee80211_hw * ath_get_virt_hw(struct ath_softc *sc, static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_desc *ds; struct sk_buff *skb; @@ -59,14 +60,16 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) /* virtual addr of the beginning of the buffer. */ skb = bf->bf_mpdu; - ASSERT(skb != NULL); + BUG_ON(skb == NULL); ds->ds_vdata = skb->data; - /* setup rx descriptors. The rx.bufsize here tells the harware + /* + * setup rx descriptors. The rx_bufsize here tells the hardware * how much data it can DMA to us and that we are prepared - * to process */ + * to process + */ ath9k_hw_setuprxdesc(ah, ds, - sc->rx.bufsize, + common->rx_bufsize, 0); if (sc->rx.rxlink == NULL) @@ -86,192 +89,11 @@ static void ath_setdefantenna(struct ath_softc *sc, u32 antenna) sc->rx.rxotherant = 0; } -/* - * Extend 15-bit time stamp from rx descriptor to - * a full 64-bit TSF using the current h/w TSF. -*/ -static u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp) -{ - u64 tsf; - - tsf = ath9k_hw_gettsf64(sc->sc_ah); - if ((tsf & 0x7fff) < rstamp) - tsf -= 0x8000; - return (tsf & ~0x7fff) | rstamp; -} - -/* - * For Decrypt or Demic errors, we only mark packet status here and always push - * up the frame up to let mac80211 handle the actual error case, be it no - * decryption key or real decryption error. This let us keep statistics there. - */ -static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds, - struct ieee80211_rx_status *rx_status, bool *decrypt_error, - struct ath_softc *sc) -{ - struct ieee80211_hdr *hdr; - u8 ratecode; - __le16 fc; - struct ieee80211_hw *hw; - struct ieee80211_sta *sta; - struct ath_node *an; - int last_rssi = ATH_RSSI_DUMMY_MARKER; - - - hdr = (struct ieee80211_hdr *)skb->data; - fc = hdr->frame_control; - memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); - hw = ath_get_virt_hw(sc, hdr); - - if (ds->ds_rxstat.rs_more) { - /* - * Frame spans multiple descriptors; this cannot happen yet - * as we don't support jumbograms. If not in monitor mode, - * discard the frame. Enable this if you want to see - * error frames in Monitor mode. - */ - if (sc->sc_ah->opmode != NL80211_IFTYPE_MONITOR) - goto rx_next; - } else if (ds->ds_rxstat.rs_status != 0) { - if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC) - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) - goto rx_next; - - if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) { - *decrypt_error = true; - } else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) { - if (ieee80211_is_ctl(fc)) - /* - * Sometimes, we get invalid - * MIC failures on valid control frames. - * Remove these mic errors. - */ - ds->ds_rxstat.rs_status &= ~ATH9K_RXERR_MIC; - else - rx_status->flag |= RX_FLAG_MMIC_ERROR; - } - /* - * Reject error frames with the exception of - * decryption and MIC failures. For monitor mode, - * we also ignore the CRC error. - */ - if (sc->sc_ah->opmode == NL80211_IFTYPE_MONITOR) { - if (ds->ds_rxstat.rs_status & - ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | - ATH9K_RXERR_CRC)) - goto rx_next; - } else { - if (ds->ds_rxstat.rs_status & - ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { - goto rx_next; - } - } - } - - ratecode = ds->ds_rxstat.rs_rate; - - if (ratecode & 0x80) { - /* HT rate */ - rx_status->flag |= RX_FLAG_HT; - if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) - rx_status->flag |= RX_FLAG_40MHZ; - if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI) - rx_status->flag |= RX_FLAG_SHORT_GI; - rx_status->rate_idx = ratecode & 0x7f; - } else { - int i = 0, cur_band, n_rates; - - cur_band = hw->conf.channel->band; - n_rates = sc->sbands[cur_band].n_bitrates; - - for (i = 0; i < n_rates; i++) { - if (sc->sbands[cur_band].bitrates[i].hw_value == - ratecode) { - rx_status->rate_idx = i; - break; - } - - if (sc->sbands[cur_band].bitrates[i].hw_value_short == - ratecode) { - rx_status->rate_idx = i; - rx_status->flag |= RX_FLAG_SHORTPRE; - break; - } - } - } - - rcu_read_lock(); - sta = ieee80211_find_sta(sc->hw, hdr->addr2); - if (sta) { - an = (struct ath_node *) sta->drv_priv; - if (ds->ds_rxstat.rs_rssi != ATH9K_RSSI_BAD && - !ds->ds_rxstat.rs_moreaggr) - ATH_RSSI_LPF(an->last_rssi, ds->ds_rxstat.rs_rssi); - last_rssi = an->last_rssi; - } - rcu_read_unlock(); - - if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) - ds->ds_rxstat.rs_rssi = ATH_EP_RND(last_rssi, - ATH_RSSI_EP_MULTIPLIER); - if (ds->ds_rxstat.rs_rssi < 0) - ds->ds_rxstat.rs_rssi = 0; - else if (ds->ds_rxstat.rs_rssi > 127) - ds->ds_rxstat.rs_rssi = 127; - - /* Update Beacon RSSI, this is used by ANI. */ - if (ieee80211_is_beacon(fc)) - sc->sc_ah->stats.avgbrssi = ds->ds_rxstat.rs_rssi; - - rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp); - rx_status->band = hw->conf.channel->band; - rx_status->freq = hw->conf.channel->center_freq; - rx_status->noise = sc->ani.noise_floor; - rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + ds->ds_rxstat.rs_rssi; - rx_status->antenna = ds->ds_rxstat.rs_antenna; - - /* - * Theory for reporting quality: - * - * At a hardware RSSI of 45 you will be able to use MCS 7 reliably. - * At a hardware RSSI of 45 you will be able to use MCS 15 reliably. - * At a hardware RSSI of 35 you should be able use 54 Mbps reliably. - * - * MCS 7 is the highets MCS index usable by a 1-stream device. - * MCS 15 is the highest MCS index usable by a 2-stream device. - * - * All ath9k devices are either 1-stream or 2-stream. - * - * How many bars you see is derived from the qual reporting. - * - * A more elaborate scheme can be used here but it requires tables - * of SNR/throughput for each possible mode used. For the MCS table - * you can refer to the wireless wiki: - * - * http://wireless.kernel.org/en/developers/Documentation/ieee80211/802.11n - * - */ - if (conf_is_ht(&hw->conf)) - rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 45; - else - rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 35; - - /* rssi can be more than 45 though, anything above that - * should be considered at 100% */ - if (rx_status->qual > 100) - rx_status->qual = 100; - - rx_status->flag |= RX_FLAG_TSFT; - - return 1; -rx_next: - return 0; -} - static void ath_opmode_init(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + u32 rfilt, mfilt[2]; /* configure rx filter */ @@ -280,13 +102,13 @@ static void ath_opmode_init(struct ath_softc *sc) /* configure bssid mask */ if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - ath9k_hw_setbssidmask(sc); + ath_hw_setbssidmask(common); /* configure operational mode */ ath9k_hw_setopmode(ah); /* Handle any link-level address change. */ - ath9k_hw_setmac(ah, sc->sc_ah->macaddr); + ath9k_hw_setmac(ah, common->macaddr); /* calculate and install multicast filter */ mfilt[0] = mfilt[1] = ~0; @@ -295,6 +117,7 @@ static void ath_opmode_init(struct ath_softc *sc) int ath_rx_init(struct ath_softc *sc, int nbufs) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct sk_buff *skb; struct ath_buf *bf; int error = 0; @@ -303,24 +126,24 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) sc->sc_flags &= ~SC_OP_RXFLUSH; spin_lock_init(&sc->rx.rxbuflock); - sc->rx.bufsize = roundup(IEEE80211_MAX_MPDU_LEN, - min(sc->common.cachelsz, (u16)64)); + common->rx_bufsize = roundup(IEEE80211_MAX_MPDU_LEN, + min(common->cachelsz, (u16)64)); - DPRINTF(sc, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n", - sc->common.cachelsz, sc->rx.bufsize); + ath_print(common, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n", + common->cachelsz, common->rx_bufsize); /* Initialize rx descriptors */ error = ath_descdma_setup(sc, &sc->rx.rxdma, &sc->rx.rxbuf, "rx", nbufs, 1); if (error != 0) { - DPRINTF(sc, ATH_DBG_FATAL, - "failed to allocate rx descriptors: %d\n", error); + ath_print(common, ATH_DBG_FATAL, + "failed to allocate rx descriptors: %d\n", error); goto err; } list_for_each_entry(bf, &sc->rx.rxbuf, list) { - skb = ath_rxbuf_alloc(&sc->common, sc->rx.bufsize, GFP_KERNEL); + skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_KERNEL); if (skb == NULL) { error = -ENOMEM; goto err; @@ -328,14 +151,14 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) bf->bf_mpdu = skb; bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, - sc->rx.bufsize, + common->rx_bufsize, DMA_FROM_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; - DPRINTF(sc, ATH_DBG_FATAL, - "dma_mapping_error() on RX init\n"); + ath_print(common, ATH_DBG_FATAL, + "dma_mapping_error() on RX init\n"); error = -ENOMEM; goto err; } @@ -352,6 +175,8 @@ err: void ath_rx_cleanup(struct ath_softc *sc) { + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct sk_buff *skb; struct ath_buf *bf; @@ -359,7 +184,7 @@ void ath_rx_cleanup(struct ath_softc *sc) skb = bf->bf_mpdu; if (skb) { dma_unmap_single(sc->dev, bf->bf_buf_addr, - sc->rx.bufsize, DMA_FROM_DEVICE); + common->rx_bufsize, DMA_FROM_DEVICE); dev_kfree_skb(skb); } } @@ -420,7 +245,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc) else rfilt |= ATH9K_RX_FILTER_BEACON; - if (sc->rx.rxfilter & FIF_PSPOLL) + if ((AR_SREV_9280_10_OR_LATER(sc->sc_ah) || + AR_SREV_9285_10_OR_LATER(sc->sc_ah)) && + (sc->sc_ah->opmode == NL80211_IFTYPE_AP) && + (sc->rx.rxfilter & FIF_PSPOLL)) rfilt |= ATH9K_RX_FILTER_PSPOLL; if (conf_is_ht(&sc->hw->conf)) @@ -527,20 +355,22 @@ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) { struct ieee80211_mgmt *mgmt; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); if (skb->len < 24 + 8 + 2 + 2) return; mgmt = (struct ieee80211_mgmt *)skb->data; - if (memcmp(sc->curbssid, mgmt->bssid, ETH_ALEN) != 0) + if (memcmp(common->curbssid, mgmt->bssid, ETH_ALEN) != 0) return; /* not from our current AP */ sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON; if (sc->sc_flags & SC_OP_BEACON_SYNC) { sc->sc_flags &= ~SC_OP_BEACON_SYNC; - DPRINTF(sc, ATH_DBG_PS, "Reconfigure Beacon timers based on " - "timestamp from the AP\n"); + ath_print(common, ATH_DBG_PS, + "Reconfigure Beacon timers based on " + "timestamp from the AP\n"); ath_beacon_config(sc, NULL); } @@ -552,8 +382,8 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) * a backup trigger for returning into NETWORK SLEEP state, * so we are waiting for it as well. */ - DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating " - "buffered broadcast/multicast frame(s)\n"); + ath_print(common, ATH_DBG_PS, "Received DTIM beacon indicating " + "buffered broadcast/multicast frame(s)\n"); sc->sc_flags |= SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_BEACON; return; } @@ -565,13 +395,15 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) * been delivered. */ sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB; - DPRINTF(sc, ATH_DBG_PS, "PS wait for CAB frames timed out\n"); + ath_print(common, ATH_DBG_PS, + "PS wait for CAB frames timed out\n"); } } static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) { struct ieee80211_hdr *hdr; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); hdr = (struct ieee80211_hdr *)skb->data; @@ -589,14 +421,15 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) * point. */ sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB; - DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to " - "sleep\n"); + ath_print(common, ATH_DBG_PS, + "All PS CAB frames received, back to sleep\n"); } else if ((sc->sc_flags & SC_OP_WAIT_FOR_PSPOLL_DATA) && !is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_morefrags(hdr->frame_control)) { sc->sc_flags &= ~SC_OP_WAIT_FOR_PSPOLL_DATA; - DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having " - "received PS-Poll data (0x%x)\n", + ath_print(common, ATH_DBG_PS, + "Going back to sleep after having received " + "PS-Poll data (0x%x)\n", sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_PSPOLL_DATA | @@ -604,8 +437,9 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) } } -static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) +static void ath_rx_send_to_mac80211(struct ieee80211_hw *hw, + struct ath_softc *sc, struct sk_buff *skb, + struct ieee80211_rx_status *rxs) { struct ieee80211_hdr *hdr; @@ -625,19 +459,14 @@ static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb, if (aphy == NULL) continue; nskb = skb_copy(skb, GFP_ATOMIC); - if (nskb) { - memcpy(IEEE80211_SKB_RXCB(nskb), rx_status, - sizeof(*rx_status)); - ieee80211_rx(aphy->hw, nskb); - } + if (!nskb) + continue; + ieee80211_rx(aphy->hw, nskb); } - memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status)); ieee80211_rx(sc->hw, skb); - } else { + } else /* Deliver unicast frames based on receiver address */ - memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status)); - ieee80211_rx(ath_get_virt_hw(sc, hdr), skb); - } + ieee80211_rx(hw, skb); } int ath_rx_tasklet(struct ath_softc *sc, int flush) @@ -648,14 +477,20 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) struct ath_buf *bf; struct ath_desc *ds; + struct ath_rx_status *rx_stats; struct sk_buff *skb = NULL, *requeue_skb; - struct ieee80211_rx_status rx_status; + struct ieee80211_rx_status *rxs; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + /* + * The hw can techncically differ from common->hw when using ath9k + * virtual wiphy so to account for that we iterate over the active + * wiphys and find the appropriate wiphy and therefore hw. + */ + struct ieee80211_hw *hw = NULL; struct ieee80211_hdr *hdr; - int hdrlen, padsize, retval; + int retval; bool decrypt_error = false; - u8 keyix; - __le16 fc; spin_lock_bh(&sc->rx.rxbuflock); @@ -727,9 +562,15 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) * 2. requeueing the same buffer to h/w */ dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr, - sc->rx.bufsize, + common->rx_bufsize, DMA_FROM_DEVICE); + hdr = (struct ieee80211_hdr *) skb->data; + rxs = IEEE80211_SKB_RXCB(skb); + + hw = ath_get_virt_hw(sc, hdr); + rx_stats = &ds->ds_rxstat; + /* * If we're asked to flush receive queue, directly * chain it back at the queue without processing it. @@ -737,19 +578,14 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) if (flush) goto requeue; - if (!ds->ds_rxstat.rs_datalen) - goto requeue; - - /* The status portion of the descriptor could get corrupted. */ - if (sc->rx.bufsize < ds->ds_rxstat.rs_datalen) - goto requeue; - - if (!ath_rx_prepare(skb, ds, &rx_status, &decrypt_error, sc)) + retval = ath9k_cmn_rx_skb_preprocess(common, hw, skb, rx_stats, + rxs, &decrypt_error); + if (retval) goto requeue; /* Ensure we always have an skb to requeue once we are done * processing the current buffer's skb */ - requeue_skb = ath_rxbuf_alloc(&sc->common, sc->rx.bufsize, GFP_ATOMIC); + requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC); /* If there is no memory we ignore the current RX'd frame, * tell hardware it can give us a new frame using the old @@ -760,60 +596,26 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) /* Unmap the frame */ dma_unmap_single(sc->dev, bf->bf_buf_addr, - sc->rx.bufsize, + common->rx_bufsize, DMA_FROM_DEVICE); - skb_put(skb, ds->ds_rxstat.rs_datalen); - - /* see if any padding is done by the hw and remove it */ - hdr = (struct ieee80211_hdr *)skb->data; - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - fc = hdr->frame_control; - - /* The MAC header is padded to have 32-bit boundary if the - * packet payload is non-zero. The general calculation for - * padsize would take into account odd header lengths: - * padsize = (4 - hdrlen % 4) % 4; However, since only - * even-length headers are used, padding can only be 0 or 2 - * bytes and we can optimize this a bit. In addition, we must - * not try to remove padding from short control frames that do - * not have payload. */ - padsize = hdrlen & 3; - if (padsize && hdrlen >= 24) { - memmove(skb->data + padsize, skb->data, hdrlen); - skb_pull(skb, padsize); - } - - keyix = ds->ds_rxstat.rs_keyix; + skb_put(skb, rx_stats->rs_datalen); - if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) { - rx_status.flag |= RX_FLAG_DECRYPTED; - } else if (ieee80211_has_protected(fc) - && !decrypt_error && skb->len >= hdrlen + 4) { - keyix = skb->data[hdrlen + 3] >> 6; - - if (test_bit(keyix, sc->keymap)) - rx_status.flag |= RX_FLAG_DECRYPTED; - } - if (ah->sw_mgmt_crypto && - (rx_status.flag & RX_FLAG_DECRYPTED) && - ieee80211_is_mgmt(fc)) { - /* Use software decrypt for management frames. */ - rx_status.flag &= ~RX_FLAG_DECRYPTED; - } + ath9k_cmn_rx_skb_postprocess(common, skb, rx_stats, + rxs, decrypt_error); /* We will now give hardware our shiny new allocated skb */ bf->bf_mpdu = requeue_skb; bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data, - sc->rx.bufsize, - DMA_FROM_DEVICE); + common->rx_bufsize, + DMA_FROM_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(requeue_skb); bf->bf_mpdu = NULL; - DPRINTF(sc, ATH_DBG_FATAL, - "dma_mapping_error() on RX\n"); - ath_rx_send_to_mac80211(sc, skb, &rx_status); + ath_print(common, ATH_DBG_FATAL, + "dma_mapping_error() on RX\n"); + ath_rx_send_to_mac80211(hw, sc, skb, rxs); break; } bf->bf_dmacontext = bf->bf_buf_addr; @@ -824,7 +626,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) */ if (sc->rx.defant != ds->ds_rxstat.rs_antenna) { if (++sc->rx.rxotherant >= 3) - ath_setdefantenna(sc, ds->ds_rxstat.rs_antenna); + ath_setdefantenna(sc, rx_stats->rs_antenna); } else { sc->rx.rxotherant = 0; } @@ -834,7 +636,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) SC_OP_WAIT_FOR_PSPOLL_DATA))) ath_rx_ps(sc, skb); - ath_rx_send_to_mac80211(sc, skb, &rx_status); + ath_rx_send_to_mac80211(hw, sc, skb, rxs); requeue: list_move_tail(&bf->list, &sc->rx.rxbuf); diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index d83b77f821e9..8e653fb937a1 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -17,6 +17,8 @@ #ifndef REG_H #define REG_H +#include "../reg.h" + #define AR_CR 0x0008 #define AR_CR_RXE 0x00000004 #define AR_CR_RXD 0x00000020 @@ -969,10 +971,10 @@ enum { #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_S 4 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF 0x00000080 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB 0x00000400 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S 10 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB 0x00001000 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S 12 -#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB 0x00001000 -#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S 1 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB 0x00008000 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S 15 #define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE 0x00010000 @@ -1330,13 +1332,22 @@ enum { #define AR_MCAST_FIL0 0x8040 #define AR_MCAST_FIL1 0x8044 +/* + * AR_DIAG_SW - Register which can be used for diagnostics and testing purposes. + * + * The force RX abort (AR_DIAG_RX_ABORT, bit 25) can be used in conjunction with + * RX block (AR_DIAG_RX_DIS, bit 5) to help fast channel change to shut down + * receive. The force RX abort bit will kill any frame which is currently being + * transferred between the MAC and baseband. The RX block bit (AR_DIAG_RX_DIS) + * will prevent any new frames from getting started. + */ #define AR_DIAG_SW 0x8048 #define AR_DIAG_CACHE_ACK 0x00000001 #define AR_DIAG_ACK_DIS 0x00000002 #define AR_DIAG_CTS_DIS 0x00000004 #define AR_DIAG_ENCRYPT_DIS 0x00000008 #define AR_DIAG_DECRYPT_DIS 0x00000010 -#define AR_DIAG_RX_DIS 0x00000020 +#define AR_DIAG_RX_DIS 0x00000020 /* RX block */ #define AR_DIAG_LOOP_BACK 0x00000040 #define AR_DIAG_CORR_FCS 0x00000080 #define AR_DIAG_CHAN_INFO 0x00000100 @@ -1345,12 +1356,12 @@ enum { #define AR_DIAG_FRAME_NV0 0x00020000 #define AR_DIAG_OBS_PT_SEL1 0x000C0000 #define AR_DIAG_OBS_PT_SEL1_S 18 -#define AR_DIAG_FORCE_RX_CLEAR 0x00100000 +#define AR_DIAG_FORCE_RX_CLEAR 0x00100000 /* force rx_clear high */ #define AR_DIAG_IGNORE_VIRT_CS 0x00200000 #define AR_DIAG_FORCE_CH_IDLE_HIGH 0x00400000 #define AR_DIAG_EIFS_CTRL_ENA 0x00800000 #define AR_DIAG_DUAL_CHAIN_INFO 0x01000000 -#define AR_DIAG_RX_ABORT 0x02000000 +#define AR_DIAG_RX_ABORT 0x02000000 /* Force RX abort */ #define AR_DIAG_SATURATE_CYCLE_CNT 0x04000000 #define AR_DIAG_OBS_PT_SEL2 0x08000000 #define AR_DIAG_RX_CLEAR_CTL_LOW 0x10000000 @@ -1421,9 +1432,6 @@ enum { #define AR_SLEEP2_BEACON_TIMEOUT 0xFFE00000 #define AR_SLEEP2_BEACON_TIMEOUT_S 21 -#define AR_BSSMSKL 0x80e0 -#define AR_BSSMSKU 0x80e4 - #define AR_TPC 0x80e8 #define AR_TPC_ACK 0x0000003f #define AR_TPC_ACK_S 0x00 @@ -1705,4 +1713,7 @@ enum { #define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24) #define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28) +#define AR9271_CORE_CLOCK 117 /* clock to 117Mhz */ +#define AR9271_TARGET_BAUD_RATE 19200 /* 115200 */ + #endif diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c index 19b88f8177fd..cd26caaf44e7 100644 --- a/drivers/net/wireless/ath/ath9k/virtual.c +++ b/drivers/net/wireless/ath/ath9k/virtual.c @@ -40,6 +40,7 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath9k_vif_iter_data iter_data; int i, j; u8 mask[ETH_ALEN]; @@ -51,7 +52,7 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw) */ iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC); if (iter_data.addr) { - memcpy(iter_data.addr, sc->sc_ah->macaddr, ETH_ALEN); + memcpy(iter_data.addr, common->macaddr, ETH_ALEN); iter_data.count = 1; } else iter_data.count = 0; @@ -86,20 +87,21 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw) kfree(iter_data.addr); /* Invert the mask and configure hardware */ - sc->bssidmask[0] = ~mask[0]; - sc->bssidmask[1] = ~mask[1]; - sc->bssidmask[2] = ~mask[2]; - sc->bssidmask[3] = ~mask[3]; - sc->bssidmask[4] = ~mask[4]; - sc->bssidmask[5] = ~mask[5]; - - ath9k_hw_setbssidmask(sc); + common->bssidmask[0] = ~mask[0]; + common->bssidmask[1] = ~mask[1]; + common->bssidmask[2] = ~mask[2]; + common->bssidmask[3] = ~mask[3]; + common->bssidmask[4] = ~mask[4]; + common->bssidmask[5] = ~mask[5]; + + ath_hw_setbssidmask(common); } int ath9k_wiphy_add(struct ath_softc *sc) { int i, error; struct ath_wiphy *aphy; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_hw *hw; u8 addr[ETH_ALEN]; @@ -138,7 +140,7 @@ int ath9k_wiphy_add(struct ath_softc *sc) sc->sec_wiphy[i] = aphy; spin_unlock_bh(&sc->wiphy_lock); - memcpy(addr, sc->sc_ah->macaddr, ETH_ALEN); + memcpy(addr, common->macaddr, ETH_ALEN); addr[0] |= 0x02; /* Locally managed address */ /* * XOR virtual wiphy index into the least significant bits to generate @@ -296,6 +298,7 @@ static void ath9k_wiphy_unpause_channel(struct ath_softc *sc) void ath9k_wiphy_chan_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, chan_work); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_wiphy *aphy = sc->next_wiphy; if (aphy == NULL) @@ -311,6 +314,10 @@ void ath9k_wiphy_chan_work(struct work_struct *work) /* XXX: remove me eventually */ ath9k_update_ichannel(sc, aphy->hw, &sc->sc_ah->channels[sc->chan_idx]); + + /* sync hw configuration for hw code */ + common->hw = aphy->hw; + ath_update_chainmask(sc, sc->chan_is_ht); if (ath_set_channel(sc, aphy->hw, &sc->sc_ah->channels[sc->chan_idx]) < 0) { @@ -331,13 +338,11 @@ void ath9k_wiphy_chan_work(struct work_struct *work) void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath_wiphy *aphy = hw->priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); - if (tx_info_priv && tx_info_priv->frame_type == ATH9K_INT_PAUSE && + if ((tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_PAUSE) && aphy->state == ATH_WIPHY_PAUSING) { - if (!(info->flags & IEEE80211_TX_STAT_ACK)) { + if (!(tx_info->flags & IEEE80211_TX_STAT_ACK)) { printk(KERN_DEBUG "ath9k: %s: no ACK for pause " "frame\n", wiphy_name(hw->wiphy)); /* @@ -356,9 +361,6 @@ void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } } - kfree(tx_info_priv); - tx_info->rate_driver_data[0] = NULL; - dev_kfree_skb(skb); } @@ -519,8 +521,9 @@ int ath9k_wiphy_select(struct ath_wiphy *aphy) * frame being completed) */ spin_unlock_bh(&sc->wiphy_lock); - ath_radio_disable(sc); - ath_radio_enable(sc); + ath_radio_disable(sc, aphy->hw); + ath_radio_enable(sc, aphy->hw); + /* Only the primary wiphy hw is used for queuing work */ ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work); return -EBUSY; /* previous select still in progress */ @@ -666,15 +669,78 @@ void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int) bool ath9k_all_wiphys_idle(struct ath_softc *sc) { unsigned int i; - if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) { + if (!sc->pri_wiphy->idle) return false; - } for (i = 0; i < sc->num_sec_wiphy; i++) { struct ath_wiphy *aphy = sc->sec_wiphy[i]; if (!aphy) continue; - if (aphy->state != ATH_WIPHY_INACTIVE) + if (!aphy->idle) return false; } return true; } + +/* caller must hold wiphy_lock */ +void ath9k_set_wiphy_idle(struct ath_wiphy *aphy, bool idle) +{ + struct ath_softc *sc = aphy->sc; + + aphy->idle = idle; + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG, + "Marking %s as %s\n", + wiphy_name(aphy->hw->wiphy), + idle ? "idle" : "not-idle"); +} +/* Only bother starting a queue on an active virtual wiphy */ +void ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue) +{ + struct ieee80211_hw *hw = sc->pri_wiphy->hw; + unsigned int i; + + spin_lock_bh(&sc->wiphy_lock); + + /* Start the primary wiphy */ + if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) { + ieee80211_wake_queue(hw, skb_queue); + goto unlock; + } + + /* Now start the secondary wiphy queues */ + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (!aphy) + continue; + if (aphy->state != ATH_WIPHY_ACTIVE) + continue; + + hw = aphy->hw; + ieee80211_wake_queue(hw, skb_queue); + break; + } + +unlock: + spin_unlock_bh(&sc->wiphy_lock); +} + +/* Go ahead and propagate information to all virtual wiphys, it won't hurt */ +void ath_mac80211_stop_queue(struct ath_softc *sc, u16 skb_queue) +{ + struct ieee80211_hw *hw = sc->pri_wiphy->hw; + unsigned int i; + + spin_lock_bh(&sc->wiphy_lock); + + /* Stop the primary wiphy */ + ieee80211_stop_queue(hw, skb_queue); + + /* Now stop the secondary wiphy queues */ + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (!aphy) + continue; + hw = aphy->hw; + ieee80211_stop_queue(hw, skb_queue); + } + spin_unlock_bh(&sc->wiphy_lock); +} diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 42551a48c8ac..564c6cb1c2b4 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -70,6 +70,29 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, int nbad, int txok, bool update_rc); +enum { + MCS_DEFAULT, + MCS_HT40, + MCS_HT40_SGI, +}; + +static int ath_max_4ms_framelen[3][16] = { + [MCS_DEFAULT] = { + 3216, 6434, 9650, 12868, 19304, 25740, 28956, 32180, + 6430, 12860, 19300, 25736, 38600, 51472, 57890, 64320, + }, + [MCS_HT40] = { + 6684, 13368, 20052, 26738, 40104, 53476, 60156, 66840, + 13360, 26720, 40080, 53440, 80160, 106880, 120240, 133600, + }, + [MCS_HT40_SGI] = { + /* TODO: Only MCS 7 and 15 updated, recalculate the rest */ + 6684, 13368, 20052, 26738, 40104, 53476, 60156, 74200, + 13360, 26720, 40080, 53440, 80160, 106880, 120240, 148400, + } +}; + + /*********************/ /* Aggregation logic */ /*********************/ @@ -107,7 +130,7 @@ static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid) { struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; - ASSERT(tid->paused > 0); + BUG_ON(tid->paused <= 0); spin_lock_bh(&txq->axq_lock); tid->paused--; @@ -131,7 +154,7 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) struct list_head bf_head; INIT_LIST_HEAD(&bf_head); - ASSERT(tid->paused > 0); + BUG_ON(tid->paused <= 0); spin_lock_bh(&txq->axq_lock); tid->paused--; @@ -143,7 +166,7 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) while (!list_empty(&tid->buf_q)) { bf = list_first_entry(&tid->buf_q, struct ath_buf, list); - ASSERT(!bf_isretried(bf)); + BUG_ON(bf_isretried(bf)); list_move_tail(&bf->list, &bf_head); ath_tx_send_ht_normal(sc, txq, tid, &bf_head); } @@ -178,7 +201,7 @@ static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid, index = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); - ASSERT(tid->tx_buf[cindex] == NULL); + BUG_ON(tid->tx_buf[cindex] != NULL); tid->tx_buf[cindex] = bf; if (index >= ((tid->baw_tail - tid->baw_head) & @@ -251,6 +274,7 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf) ATH_TXBUF_RESET(tbf); + tbf->aphy = bf->aphy; tbf->bf_mpdu = bf->bf_mpdu; tbf->bf_buf_addr = bf->bf_buf_addr; *(tbf->bf_desc) = *(bf->bf_desc); @@ -267,7 +291,9 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ath_node *an = NULL; struct sk_buff *skb; struct ieee80211_sta *sta; + struct ieee80211_hw *hw; struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info; struct ath_atx_tid *tid = NULL; struct ath_buf *bf_next, *bf_last = bf->bf_lastbf; struct ath_desc *ds = bf_last->bf_desc; @@ -280,9 +306,13 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, skb = bf->bf_mpdu; hdr = (struct ieee80211_hdr *)skb->data; + tx_info = IEEE80211_SKB_CB(skb); + hw = bf->aphy->hw; + rcu_read_lock(); - sta = ieee80211_find_sta(sc->hw, hdr->addr1); + /* XXX: use ieee80211_find_sta! */ + sta = ieee80211_find_sta_by_hw(hw, hdr->addr1); if (!sta) { rcu_read_unlock(); return; @@ -358,7 +388,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, else INIT_LIST_HEAD(&bf_head); } else { - ASSERT(!list_empty(bf_q)); + BUG_ON(list_empty(bf_q)); list_move_tail(&bf->list, &bf_head); } @@ -452,11 +482,9 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, struct ath_atx_tid *tid) { - const struct ath_rate_table *rate_table = sc->cur_rate_table; struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; - struct ath_tx_info_priv *tx_info_priv; u32 max_4ms_framelen, frmlen; u16 aggr_limit, legacy = 0; int i; @@ -464,7 +492,6 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, skb = bf->bf_mpdu; tx_info = IEEE80211_SKB_CB(skb); rates = tx_info->control.rates; - tx_info_priv = (struct ath_tx_info_priv *)tx_info->rate_driver_data[0]; /* * Find the lowest frame length among the rate series that will have a @@ -475,12 +502,20 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, for (i = 0; i < 4; i++) { if (rates[i].count) { - if (!WLAN_RC_PHY_HT(rate_table->info[rates[i].idx].phy)) { + int modeidx; + if (!(rates[i].flags & IEEE80211_TX_RC_MCS)) { legacy = 1; break; } - frmlen = rate_table->info[rates[i].idx].max_4ms_framelen; + if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI) + modeidx = MCS_HT40_SGI; + else if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + modeidx = MCS_HT40; + else + modeidx = MCS_DEFAULT; + + frmlen = ath_max_4ms_framelen[modeidx][rates[i].idx]; max_4ms_framelen = min(max_4ms_framelen, frmlen); } } @@ -518,12 +553,11 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, struct ath_buf *bf, u16 frmlen) { - const struct ath_rate_table *rt = sc->cur_rate_table; struct sk_buff *skb = bf->bf_mpdu; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); u32 nsymbits, nsymbols; u16 minlen; - u8 rc, flags, rix; + u8 flags, rix; int width, half_gi, ndelim, mindelim; /* Select standard number of delimiters based on frame length alone */ @@ -553,7 +587,6 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, rix = tx_info->control.rates[0].idx; flags = tx_info->control.rates[0].flags; - rc = rt->info[rix].ratecode; width = (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ? 1 : 0; half_gi = (flags & IEEE80211_TX_RC_SHORT_GI) ? 1 : 0; @@ -565,7 +598,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, if (nsymbols == 0) nsymbols = 1; - nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width]; + nsymbits = bits_per_symbol[rix][width]; minlen = (nsymbols * nsymbits) / BITS_PER_BYTE; if (frmlen < minlen) { @@ -694,7 +727,6 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, /* anchor last desc of aggregate */ ath9k_hw_set11n_aggr_last(sc->sc_ah, bf->bf_lastbf->bf_desc); - txq->axq_aggr_depth++; ath_tx_txqaddbuf(sc, txq, &bf_q); TX_STAT_INC(txq->axq_qnum, a_aggr); @@ -815,6 +847,7 @@ static void ath_txq_drain_pending_buffers(struct ath_softc *sc, struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info qi; int qnum; @@ -854,9 +887,9 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) return NULL; } if (qnum >= ARRAY_SIZE(sc->tx.txq)) { - DPRINTF(sc, ATH_DBG_FATAL, - "qnum %u out of range, max %u!\n", - qnum, (unsigned int)ARRAY_SIZE(sc->tx.txq)); + ath_print(common, ATH_DBG_FATAL, + "qnum %u out of range, max %u!\n", + qnum, (unsigned int)ARRAY_SIZE(sc->tx.txq)); ath9k_hw_releasetxqueue(ah, qnum); return NULL; } @@ -869,8 +902,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) INIT_LIST_HEAD(&txq->axq_acq); spin_lock_init(&txq->axq_lock); txq->axq_depth = 0; - txq->axq_aggr_depth = 0; - txq->axq_linkbuf = NULL; txq->axq_tx_inprogress = false; sc->tx.txqsetup |= 1<<qnum; } @@ -884,9 +915,9 @@ int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype) switch (qtype) { case ATH9K_TX_QUEUE_DATA: if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) { - DPRINTF(sc, ATH_DBG_FATAL, - "HAL AC %u out of range, max %zu!\n", - haltype, ARRAY_SIZE(sc->tx.hwq_map)); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "HAL AC %u out of range, max %zu!\n", + haltype, ARRAY_SIZE(sc->tx.hwq_map)); return -1; } qnum = sc->tx.hwq_map[haltype]; @@ -906,18 +937,19 @@ int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype) struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb) { struct ath_txq *txq = NULL; + u16 skb_queue = skb_get_queue_mapping(skb); int qnum; - qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc); + qnum = ath_get_hal_qnum(skb_queue, sc); txq = &sc->tx.txq[qnum]; spin_lock_bh(&txq->axq_lock); if (txq->axq_depth >= (ATH_TXBUF - 20)) { - DPRINTF(sc, ATH_DBG_XMIT, - "TX queue: %d is full, depth: %d\n", - qnum, txq->axq_depth); - ieee80211_stop_queue(sc->hw, skb_get_queue_mapping(skb)); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_XMIT, + "TX queue: %d is full, depth: %d\n", + qnum, txq->axq_depth); + ath_mac80211_stop_queue(sc, skb_queue); txq->stopped = 1; spin_unlock_bh(&txq->axq_lock); return NULL; @@ -945,7 +977,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum, return 0; } - ASSERT(sc->tx.txq[qnum].axq_qnum == qnum); + BUG_ON(sc->tx.txq[qnum].axq_qnum != qnum); ath9k_hw_get_txq_props(ah, qnum, &qi); qi.tqi_aifs = qinfo->tqi_aifs; @@ -955,8 +987,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum, qi.tqi_readyTime = qinfo->tqi_readyTime; if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to update hardware queue %u!\n", qnum); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "Unable to update hardware queue %u!\n", qnum); error = -EIO; } else { ath9k_hw_resettxqueue(ah, qnum); @@ -1004,7 +1036,6 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) if (list_empty(&txq->axq_q)) { txq->axq_link = NULL; - txq->axq_linkbuf = NULL; spin_unlock_bh(&txq->axq_lock); break; } @@ -1055,6 +1086,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_txq *txq; int i, npend = 0; @@ -1076,14 +1108,15 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) if (npend) { int r; - DPRINTF(sc, ATH_DBG_XMIT, "Unable to stop TxDMA. Reset HAL!\n"); + ath_print(common, ATH_DBG_XMIT, + "Unable to stop TxDMA. Reset HAL!\n"); spin_lock_bh(&sc->sc_resetlock); r = ath9k_hw_reset(ah, sc->sc_ah->curchan, true); if (r) - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to reset hardware; reset status %d\n", - r); + ath_print(common, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %d\n", + r); spin_unlock_bh(&sc->sc_resetlock); } @@ -1147,8 +1180,8 @@ int ath_tx_setup(struct ath_softc *sc, int haltype) struct ath_txq *txq; if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) { - DPRINTF(sc, ATH_DBG_FATAL, - "HAL AC %u out of range, max %zu!\n", + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "HAL AC %u out of range, max %zu!\n", haltype, ARRAY_SIZE(sc->tx.hwq_map)); return 0; } @@ -1172,6 +1205,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, struct list_head *head) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf; /* @@ -1186,21 +1220,20 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, list_splice_tail_init(head, &txq->axq_q); txq->axq_depth++; - txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list); - DPRINTF(sc, ATH_DBG_QUEUE, - "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth); + ath_print(common, ATH_DBG_QUEUE, + "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth); if (txq->axq_link == NULL) { ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); - DPRINTF(sc, ATH_DBG_XMIT, - "TXDP[%u] = %llx (%p)\n", - txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); + ath_print(common, ATH_DBG_XMIT, + "TXDP[%u] = %llx (%p)\n", + txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); } else { *txq->axq_link = bf->bf_daddr; - DPRINTF(sc, ATH_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n", - txq->axq_qnum, txq->axq_link, - ito64(bf->bf_daddr), bf->bf_desc); + ath_print(common, ATH_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n", + txq->axq_qnum, txq->axq_link, + ito64(bf->bf_daddr), bf->bf_desc); } txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link); ath9k_hw_txstart(ah, txq->axq_qnum); @@ -1420,22 +1453,14 @@ static int setup_tx_flags(struct ath_softc *sc, struct sk_buff *skb, static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf, int width, int half_gi, bool shortPreamble) { - const struct ath_rate_table *rate_table = sc->cur_rate_table; u32 nbits, nsymbits, duration, nsymbols; - u8 rc; int streams, pktlen; pktlen = bf_isaggr(bf) ? bf->bf_al : bf->bf_frmlen; - rc = rate_table->info[rix].ratecode; - - /* for legacy rates, use old function to compute packet duration */ - if (!IS_HT_RATE(rc)) - return ath9k_hw_computetxtime(sc->sc_ah, rate_table, pktlen, - rix, shortPreamble); /* find number of symbols: PLCP + data */ nbits = (pktlen << 3) + OFDM_PLCP_BITS; - nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width]; + nsymbits = bits_per_symbol[rix][width]; nsymbols = (nbits + nsymbits - 1) / nsymbits; if (!half_gi) @@ -1444,7 +1469,7 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf, duration = SYMBOL_TIME_HALFGI(nsymbols); /* addup duration for legacy/ht training and signal fields */ - streams = HT_RC_2_STREAMS(rc); + streams = HT_RC_2_STREAMS(rix); duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams); return duration; @@ -1452,11 +1477,12 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf, static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) { - const struct ath_rate_table *rt = sc->cur_rate_table; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath9k_11n_rate_series series[4]; struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; + const struct ieee80211_rate *rate; struct ieee80211_hdr *hdr; int i, flags = 0; u8 rix = 0, ctsrate = 0; @@ -1475,11 +1501,10 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) * checking the BSS's global flag. * But for the rate series, IEEE80211_TX_RC_USE_SHORT_PREAMBLE is used. */ + rate = ieee80211_get_rts_cts_rate(sc->hw, tx_info); + ctsrate = rate->hw_value; if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) - ctsrate = rt->info[tx_info->control.rts_cts_rate_idx].ratecode | - rt->info[tx_info->control.rts_cts_rate_idx].short_preamble; - else - ctsrate = rt->info[tx_info->control.rts_cts_rate_idx].ratecode; + ctsrate |= rate->hw_value_short; /* * ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive. @@ -1502,18 +1527,15 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) flags &= ~(ATH9K_TXDESC_RTSENA); for (i = 0; i < 4; i++) { + bool is_40, is_sgi, is_sp; + int phy; + if (!rates[i].count || (rates[i].idx < 0)) continue; rix = rates[i].idx; series[i].Tries = rates[i].count; - series[i].ChSel = sc->tx_chainmask; - - if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - series[i].Rate = rt->info[rix].ratecode | - rt->info[rix].short_preamble; - else - series[i].Rate = rt->info[rix].ratecode; + series[i].ChSel = common->tx_chainmask; if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; @@ -1522,10 +1544,36 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI) series[i].RateFlags |= ATH9K_RATESERIES_HALFGI; - series[i].PktDuration = ath_pkt_duration(sc, rix, bf, - (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) != 0, - (rates[i].flags & IEEE80211_TX_RC_SHORT_GI), - (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)); + is_sgi = !!(rates[i].flags & IEEE80211_TX_RC_SHORT_GI); + is_40 = !!(rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH); + is_sp = !!(rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE); + + if (rates[i].flags & IEEE80211_TX_RC_MCS) { + /* MCS rates */ + series[i].Rate = rix | 0x80; + series[i].PktDuration = ath_pkt_duration(sc, rix, bf, + is_40, is_sgi, is_sp); + continue; + } + + /* legcay rates */ + if ((tx_info->band == IEEE80211_BAND_2GHZ) && + !(rate->flags & IEEE80211_RATE_ERP_G)) + phy = WLAN_RC_PHY_CCK; + else + phy = WLAN_RC_PHY_OFDM; + + rate = &sc->sbands[tx_info->band].bitrates[rates[i].idx]; + series[i].Rate = rate->hw_value; + if (rate->hw_value_short) { + if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + series[i].Rate |= rate->hw_value_short; + } else { + is_sp = false; + } + + series[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah, + phy, rate->bitrate * 100, bf->bf_frmlen, rix, is_sp); } /* set dur_update_en for l-sig computation except for PS-Poll frames */ @@ -1546,24 +1594,36 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, struct ath_softc *sc = aphy->sc; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ath_tx_info_priv *tx_info_priv; int hdrlen; __le16 fc; + int padpos, padsize; - tx_info_priv = kzalloc(sizeof(*tx_info_priv), GFP_ATOMIC); - if (unlikely(!tx_info_priv)) - return -ENOMEM; - tx_info->rate_driver_data[0] = tx_info_priv; - tx_info_priv->aphy = aphy; - tx_info_priv->frame_type = txctl->frame_type; + tx_info->pad[0] = 0; + switch (txctl->frame_type) { + case ATH9K_NOT_INTERNAL: + break; + case ATH9K_INT_PAUSE: + tx_info->pad[0] |= ATH_TX_INFO_FRAME_TYPE_PAUSE; + /* fall through */ + case ATH9K_INT_UNPAUSE: + tx_info->pad[0] |= ATH_TX_INFO_FRAME_TYPE_INTERNAL; + break; + } hdrlen = ieee80211_get_hdrlen_from_skb(skb); fc = hdr->frame_control; ATH_TXBUF_RESET(bf); - bf->bf_frmlen = skb->len + FCS_LEN - (hdrlen & 3); + bf->aphy = aphy; + bf->bf_frmlen = skb->len + FCS_LEN; + /* Remove the padding size from bf_frmlen, if any */ + padpos = ath9k_cmn_padpos(hdr->frame_control); + padsize = padpos & 3; + if (padsize && skb->len>padpos+padsize) { + bf->bf_frmlen -= padsize; + } - if (conf_is_ht(&sc->hw->conf) && !is_pae(skb)) + if (conf_is_ht(&hw->conf) && !is_pae(skb)) bf->bf_state.bf_type |= BUF_HT; bf->bf_flags = setup_tx_flags(sc, skb, txctl->txq); @@ -1585,13 +1645,20 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_dmacontext))) { bf->bf_mpdu = NULL; - kfree(tx_info_priv); - tx_info->rate_driver_data[0] = NULL; - DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error() on TX\n"); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "dma_mapping_error() on TX\n"); return -ENOMEM; } bf->bf_buf_addr = bf->bf_dmacontext; + + /* tag if this is a nullfunc frame to enable PS when AP acks it */ + if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc)) { + bf->bf_isnullfunc = true; + sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED; + } else + bf->bf_isnullfunc = false; + return 0; } @@ -1669,12 +1736,13 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_buf *bf; int r; bf = ath_tx_get_buffer(sc); if (!bf) { - DPRINTF(sc, ATH_DBG_XMIT, "TX buffers are full\n"); + ath_print(common, ATH_DBG_XMIT, "TX buffers are full\n"); return -1; } @@ -1682,7 +1750,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, if (unlikely(r)) { struct ath_txq *txq = txctl->txq; - DPRINTF(sc, ATH_DBG_FATAL, "TX mem alloc failure\n"); + ath_print(common, ATH_DBG_FATAL, "TX mem alloc failure\n"); /* upon ath_tx_processq() this TX queue will be resumed, we * guarantee this will happen by knowing beforehand that @@ -1690,8 +1758,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, * on the queue */ spin_lock_bh(&txq->axq_lock); if (sc->tx.txq[txq->axq_qnum].axq_depth > 1) { - ieee80211_stop_queue(sc->hw, - skb_get_queue_mapping(skb)); + ath_mac80211_stop_queue(sc, skb_get_queue_mapping(skb)); txq->stopped = 1; } spin_unlock_bh(&txq->axq_lock); @@ -1712,6 +1779,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); int hdrlen, padsize; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ath_tx_control txctl; @@ -1736,7 +1804,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) if (hdrlen & 3) { padsize = hdrlen % 4; if (skb_headroom(skb) < padsize) { - DPRINTF(sc, ATH_DBG_XMIT, "TX CABQ padding failed\n"); + ath_print(common, ATH_DBG_XMIT, + "TX CABQ padding failed\n"); dev_kfree_skb_any(skb); return; } @@ -1746,10 +1815,11 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) txctl.txq = sc->beacon.cabq; - DPRINTF(sc, ATH_DBG_XMIT, "transmitting CABQ packet, skb: %p\n", skb); + ath_print(common, ATH_DBG_XMIT, + "transmitting CABQ packet, skb: %p\n", skb); if (ath_tx_start(hw, skb, &txctl) != 0) { - DPRINTF(sc, ATH_DBG_XMIT, "CABQ TX failed\n"); + ath_print(common, ATH_DBG_XMIT, "CABQ TX failed\n"); goto exit; } @@ -1763,26 +1833,17 @@ exit: /*****************/ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, - int tx_flags) + struct ath_wiphy *aphy, int tx_flags) { struct ieee80211_hw *hw = sc->hw; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); int hdrlen, padsize; - int frame_type = ATH9K_NOT_INTERNAL; - DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb); + ath_print(common, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb); - if (tx_info_priv) { - hw = tx_info_priv->aphy->hw; - frame_type = tx_info_priv->frame_type; - } - - if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK || - tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) { - kfree(tx_info_priv); - tx_info->rate_driver_data[0] = NULL; - } + if (aphy) + hw = aphy->hw; if (tx_flags & ATH_TX_BAR) tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; @@ -1805,18 +1866,19 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, if (sc->sc_flags & SC_OP_WAIT_FOR_TX_ACK) { sc->sc_flags &= ~SC_OP_WAIT_FOR_TX_ACK; - DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having " - "received TX status (0x%x)\n", + ath_print(common, ATH_DBG_PS, + "Going back to sleep after having " + "received TX status (0x%x)\n", sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_PSPOLL_DATA | SC_OP_WAIT_FOR_TX_ACK)); } - if (frame_type == ATH9K_NOT_INTERNAL) - ieee80211_tx_status(hw, skb); - else + if (unlikely(tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_INTERNAL)) ath9k_tx_status(hw, skb); + else + ieee80211_tx_status(hw, skb); } static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, @@ -1839,7 +1901,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, } dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE); - ath_tx_complete(sc, skb, tx_flags); + ath_tx_complete(sc, skb, bf->aphy, tx_flags); ath_debug_stat_tx(sc, txq, bf); /* @@ -1887,8 +1949,7 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, struct sk_buff *skb = bf->bf_mpdu; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); - struct ieee80211_hw *hw = tx_info_priv->aphy->hw; + struct ieee80211_hw *hw = bf->aphy->hw; u8 i, tx_rateindex; if (txok) @@ -1897,22 +1958,29 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, tx_rateindex = ds->ds_txstat.ts_rateindex; WARN_ON(tx_rateindex >= hw->max_rates); - tx_info_priv->update_rc = update_rc; + if (update_rc) + tx_info->pad[0] |= ATH_TX_INFO_UPDATE_RC; if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 && (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) { if (ieee80211_is_data(hdr->frame_control)) { - memcpy(&tx_info_priv->tx, &ds->ds_txstat, - sizeof(tx_info_priv->tx)); - tx_info_priv->n_frames = bf->bf_nframes; - tx_info_priv->n_bad_frames = nbad; + if (ds->ds_txstat.ts_flags & + (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN)) + tx_info->pad[0] |= ATH_TX_INFO_UNDERRUN; + if ((ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY) || + (ds->ds_txstat.ts_status & ATH9K_TXERR_FIFO)) + tx_info->pad[0] |= ATH_TX_INFO_XRETRY; + tx_info->status.ampdu_len = bf->bf_nframes; + tx_info->status.ampdu_ack_len = bf->bf_nframes - nbad; } } - for (i = tx_rateindex + 1; i < hw->max_rates; i++) + for (i = tx_rateindex + 1; i < hw->max_rates; i++) { tx_info->status.rates[i].count = 0; + tx_info->status.rates[i].idx = -1; + } tx_info->status.rates[tx_rateindex].count = bf->bf_retries + 1; } @@ -1926,7 +1994,7 @@ static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) sc->tx.txq[txq->axq_qnum].axq_depth <= (ATH_TXBUF - 20)) { qnum = ath_get_mac80211_qnum(txq->axq_qnum, sc); if (qnum != -1) { - ieee80211_wake_queue(sc->hw, qnum); + ath_mac80211_start_queue(sc, qnum); txq->stopped = 0; } } @@ -1936,21 +2004,21 @@ static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf, *lastbf, *bf_held = NULL; struct list_head bf_head; struct ath_desc *ds; int txok; int status; - DPRINTF(sc, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n", - txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum), - txq->axq_link); + ath_print(common, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n", + txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum), + txq->axq_link); for (;;) { spin_lock_bh(&txq->axq_lock); if (list_empty(&txq->axq_q)) { txq->axq_link = NULL; - txq->axq_linkbuf = NULL; spin_unlock_bh(&txq->axq_lock); break; } @@ -1984,10 +2052,19 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) spin_unlock_bh(&txq->axq_lock); break; } - if (bf->bf_desc == txq->axq_lastdsWithCTS) - txq->axq_lastdsWithCTS = NULL; - if (ds == txq->axq_gatingds) - txq->axq_gatingds = NULL; + + /* + * We now know the nullfunc frame has been ACKed so we + * can disable RX. + */ + if (bf->bf_isnullfunc && + (ds->ds_txstat.ts_status & ATH9K_TX_ACKED)) { + if ((sc->sc_flags & SC_OP_PS_ENABLED)) { + sc->ps_enabled = true; + ath9k_hw_setrxabort(sc->sc_ah, 1); + } else + sc->sc_flags |= SC_OP_NULLFUNC_COMPLETED; + } /* * Remove ath_buf's of the same transmit unit from txq, @@ -2001,9 +2078,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) &txq->axq_q, lastbf->list.prev); txq->axq_depth--; - if (bf_isaggr(bf)) - txq->axq_aggr_depth--; - txok = (ds->ds_txstat.ts_status == 0); txq->axq_tx_inprogress = false; spin_unlock_bh(&txq->axq_lock); @@ -2064,8 +2138,11 @@ static void ath_tx_complete_poll_work(struct work_struct *work) } if (needreset) { - DPRINTF(sc, ATH_DBG_RESET, "tx hung, resetting the chip\n"); + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET, + "tx hung, resetting the chip\n"); + ath9k_ps_wakeup(sc); ath_reset(sc, false); + ath9k_ps_restore(sc); } ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, @@ -2093,6 +2170,7 @@ void ath_tx_tasklet(struct ath_softc *sc) int ath_tx_init(struct ath_softc *sc, int nbufs) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); int error = 0; spin_lock_init(&sc->tx.txbuflock); @@ -2100,16 +2178,16 @@ int ath_tx_init(struct ath_softc *sc, int nbufs) error = ath_descdma_setup(sc, &sc->tx.txdma, &sc->tx.txbuf, "tx", nbufs, 1); if (error != 0) { - DPRINTF(sc, ATH_DBG_FATAL, - "Failed to allocate tx descriptors: %d\n", error); + ath_print(common, ATH_DBG_FATAL, + "Failed to allocate tx descriptors: %d\n", error); goto err; } error = ath_descdma_setup(sc, &sc->beacon.bdma, &sc->beacon.bbuf, "beacon", ATH_BCBUF, 1); if (error != 0) { - DPRINTF(sc, ATH_DBG_FATAL, - "Failed to allocate beacon descriptors: %d\n", error); + ath_print(common, ATH_DBG_FATAL, + "Failed to allocate beacon descriptors: %d\n", error); goto err; } diff --git a/drivers/net/wireless/ath/debug.c b/drivers/net/wireless/ath/debug.c new file mode 100644 index 000000000000..53e77bd131b9 --- /dev/null +++ b/drivers/net/wireless/ath/debug.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath.h" +#include "debug.h" + +void ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...) +{ + va_list args; + + if (likely(!(common->debug_mask & dbg_mask))) + return; + + va_start(args, fmt); + printk(KERN_DEBUG "ath: "); + vprintk(fmt, args); + va_end(args); +} +EXPORT_SYMBOL(ath_print); diff --git a/drivers/net/wireless/ath/debug.h b/drivers/net/wireless/ath/debug.h new file mode 100644 index 000000000000..d6b685a06c5e --- /dev/null +++ b/drivers/net/wireless/ath/debug.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ATH_DEBUG_H +#define ATH_DEBUG_H + +#include "ath.h" + +/** + * enum ath_debug_level - atheros wireless debug level + * + * @ATH_DBG_RESET: reset processing + * @ATH_DBG_QUEUE: hardware queue management + * @ATH_DBG_EEPROM: eeprom processing + * @ATH_DBG_CALIBRATE: periodic calibration + * @ATH_DBG_INTERRUPT: interrupt processing + * @ATH_DBG_REGULATORY: regulatory processing + * @ATH_DBG_ANI: adaptive noise immunitive processing + * @ATH_DBG_XMIT: basic xmit operation + * @ATH_DBG_BEACON: beacon handling + * @ATH_DBG_CONFIG: configuration of the hardware + * @ATH_DBG_FATAL: fatal errors, this is the default, DBG_DEFAULT + * @ATH_DBG_PS: power save processing + * @ATH_DBG_HWTIMER: hardware timer handling + * @ATH_DBG_BTCOEX: bluetooth coexistance + * @ATH_DBG_ANY: enable all debugging + * + * The debug level is used to control the amount and type of debugging output + * we want to see. Each driver has its own method for enabling debugging and + * modifying debug level states -- but this is typically done through a + * module parameter 'debug' along with a respective 'debug' debugfs file + * entry. + */ +enum ATH_DEBUG { + ATH_DBG_RESET = 0x00000001, + ATH_DBG_QUEUE = 0x00000002, + ATH_DBG_EEPROM = 0x00000004, + ATH_DBG_CALIBRATE = 0x00000008, + ATH_DBG_INTERRUPT = 0x00000010, + ATH_DBG_REGULATORY = 0x00000020, + ATH_DBG_ANI = 0x00000040, + ATH_DBG_XMIT = 0x00000080, + ATH_DBG_BEACON = 0x00000100, + ATH_DBG_CONFIG = 0x00000200, + ATH_DBG_FATAL = 0x00000400, + ATH_DBG_PS = 0x00000800, + ATH_DBG_HWTIMER = 0x00001000, + ATH_DBG_BTCOEX = 0x00002000, + ATH_DBG_ANY = 0xffffffff +}; + +#define ATH_DBG_DEFAULT (ATH_DBG_FATAL) + +#ifdef CONFIG_ATH_DEBUG +void ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...); +#else +static inline void ath_print(struct ath_common *common, + int dbg_mask, + const char *fmt, ...) +{ +} +#endif /* CONFIG_ATH_DEBUG */ + +#endif /* ATH_DEBUG_H */ diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c new file mode 100644 index 000000000000..ecc9eb01f4fa --- /dev/null +++ b/drivers/net/wireless/ath/hw.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <asm/unaligned.h> + +#include "ath.h" +#include "reg.h" + +#define REG_READ common->ops->read +#define REG_WRITE common->ops->write + +/** + * ath_hw_set_bssid_mask - filter out bssids we listen + * + * @common: the ath_common struct for the device. + * + * BSSID masking is a method used by AR5212 and newer hardware to inform PCU + * which bits of the interface's MAC address should be looked at when trying + * to decide which packets to ACK. In station mode and AP mode with a single + * BSS every bit matters since we lock to only one BSS. In AP mode with + * multiple BSSes (virtual interfaces) not every bit matters because hw must + * accept frames for all BSSes and so we tweak some bits of our mac address + * in order to have multiple BSSes. + * + * NOTE: This is a simple filter and does *not* filter out all + * relevant frames. Some frames that are not for us might get ACKed from us + * by PCU because they just match the mask. + * + * When handling multiple BSSes you can get the BSSID mask by computing the + * set of ~ ( MAC XOR BSSID ) for all bssids we handle. + * + * When you do this you are essentially computing the common bits of all your + * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with + * the MAC address to obtain the relevant bits and compare the result with + * (frame's BSSID & mask) to see if they match. + * + * Simple example: on your card you have have two BSSes you have created with + * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. + * There is another BSSID-03 but you are not part of it. For simplicity's sake, + * assuming only 4 bits for a mac address and for BSSIDs you can then have: + * + * \ + * MAC: 0001 | + * BSSID-01: 0100 | --> Belongs to us + * BSSID-02: 1001 | + * / + * ------------------- + * BSSID-03: 0110 | --> External + * ------------------- + * + * Our bssid_mask would then be: + * + * On loop iteration for BSSID-01: + * ~(0001 ^ 0100) -> ~(0101) + * -> 1010 + * bssid_mask = 1010 + * + * On loop iteration for BSSID-02: + * bssid_mask &= ~(0001 ^ 1001) + * bssid_mask = (1010) & ~(0001 ^ 1001) + * bssid_mask = (1010) & ~(1001) + * bssid_mask = (1010) & (0110) + * bssid_mask = 0010 + * + * A bssid_mask of 0010 means "only pay attention to the second least + * significant bit". This is because its the only bit common + * amongst the MAC and all BSSIDs we support. To findout what the real + * common bit is we can simply "&" the bssid_mask now with any BSSID we have + * or our MAC address (we assume the hardware uses the MAC address). + * + * Now, suppose there's an incoming frame for BSSID-03: + * + * IFRAME-01: 0110 + * + * An easy eye-inspeciton of this already should tell you that this frame + * will not pass our check. This is beacuse the bssid_mask tells the + * hardware to only look at the second least significant bit and the + * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB + * as 1, which does not match 0. + * + * So with IFRAME-01 we *assume* the hardware will do: + * + * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; + * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; + * --> allow = (0010) == 0000 ? 1 : 0; + * --> allow = 0 + * + * Lets now test a frame that should work: + * + * IFRAME-02: 0001 (we should allow) + * + * allow = (0001 & 1010) == 1010 + * + * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; + * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; + * --> allow = (0010) == (0010) + * --> allow = 1 + * + * Other examples: + * + * IFRAME-03: 0100 --> allowed + * IFRAME-04: 1001 --> allowed + * IFRAME-05: 1101 --> allowed but its not for us!!! + * + */ +void ath_hw_setbssidmask(struct ath_common *common) +{ + void *ah = common->ah; + + REG_WRITE(ah, get_unaligned_le32(common->bssidmask), AR_BSSMSKL); + REG_WRITE(ah, get_unaligned_le16(common->bssidmask + 4), AR_BSSMSKU); +} +EXPORT_SYMBOL(ath_hw_setbssidmask); diff --git a/drivers/net/wireless/ath/reg.h b/drivers/net/wireless/ath/reg.h new file mode 100644 index 000000000000..dfe1fbec24f5 --- /dev/null +++ b/drivers/net/wireless/ath/reg.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ATH_REGISTERS_H +#define ATH_REGISTERS_H + +/* + * BSSID mask registers. See ath_hw_set_bssid_mask() + * for detailed documentation about these registers. + */ +#define AR_BSSMSKL 0x80e0 +#define AR_BSSMSKU 0x80e4 + +#endif /* ATH_REGISTERS_H */ diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 077bcc142cde..039ac490465c 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -450,7 +450,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, const struct ieee80211_regdomain *regd; wiphy->reg_notifier = reg_notifier; - wiphy->strict_regulatory = true; + wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; if (ath_is_world_regd(reg)) { /* @@ -458,8 +458,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, * saved on the wiphy orig_* parameters */ regd = ath_world_regdomain(reg); - wiphy->custom_regulatory = true; - wiphy->strict_regulatory = false; + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; } else { /* * This gets applied in the case of the absense of CRDA, diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h index c1dd857697a7..a1c39526161a 100644 --- a/drivers/net/wireless/ath/regd.h +++ b/drivers/net/wireless/ath/regd.h @@ -65,10 +65,13 @@ enum CountryCode { CTRY_ALGERIA = 12, CTRY_ARGENTINA = 32, CTRY_ARMENIA = 51, + CTRY_ARUBA = 533, CTRY_AUSTRALIA = 36, CTRY_AUSTRIA = 40, CTRY_AZERBAIJAN = 31, CTRY_BAHRAIN = 48, + CTRY_BANGLADESH = 50, + CTRY_BARBADOS = 52, CTRY_BELARUS = 112, CTRY_BELGIUM = 56, CTRY_BELIZE = 84, @@ -77,6 +80,7 @@ enum CountryCode { CTRY_BRAZIL = 76, CTRY_BRUNEI_DARUSSALAM = 96, CTRY_BULGARIA = 100, + CTRY_CAMBODIA = 116, CTRY_CANADA = 124, CTRY_CHILE = 152, CTRY_CHINA = 156, @@ -97,7 +101,11 @@ enum CountryCode { CTRY_GEORGIA = 268, CTRY_GERMANY = 276, CTRY_GREECE = 300, + CTRY_GREENLAND = 304, + CTRY_GRENEDA = 308, + CTRY_GUAM = 316, CTRY_GUATEMALA = 320, + CTRY_HAITI = 332, CTRY_HONDURAS = 340, CTRY_HONG_KONG = 344, CTRY_HUNGARY = 348, diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h index 9847af72208c..248c670fdfbe 100644 --- a/drivers/net/wireless/ath/regd_common.h +++ b/drivers/net/wireless/ath/regd_common.h @@ -288,13 +288,16 @@ static struct country_code_to_enum_rd allCountries[] = { {CTRY_DEFAULT, FCC1_FCCA, "CO"}, {CTRY_ALBANIA, NULL1_WORLD, "AL"}, {CTRY_ALGERIA, NULL1_WORLD, "DZ"}, - {CTRY_ARGENTINA, APL3_WORLD, "AR"}, + {CTRY_ARGENTINA, FCC3_WORLD, "AR"}, {CTRY_ARMENIA, ETSI4_WORLD, "AM"}, + {CTRY_ARUBA, ETSI1_WORLD, "AW"}, {CTRY_AUSTRALIA, FCC2_WORLD, "AU"}, {CTRY_AUSTRALIA2, FCC6_WORLD, "AU"}, {CTRY_AUSTRIA, ETSI1_WORLD, "AT"}, {CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ"}, {CTRY_BAHRAIN, APL6_WORLD, "BH"}, + {CTRY_BANGLADESH, NULL1_WORLD, "BD"}, + {CTRY_BARBADOS, FCC2_WORLD, "BB"}, {CTRY_BELARUS, ETSI1_WORLD, "BY"}, {CTRY_BELGIUM, ETSI1_WORLD, "BE"}, {CTRY_BELGIUM2, ETSI4_WORLD, "BL"}, @@ -304,13 +307,14 @@ static struct country_code_to_enum_rd allCountries[] = { {CTRY_BRAZIL, FCC3_WORLD, "BR"}, {CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN"}, {CTRY_BULGARIA, ETSI6_WORLD, "BG"}, - {CTRY_CANADA, FCC2_FCCA, "CA"}, + {CTRY_CAMBODIA, ETSI1_WORLD, "KH"}, + {CTRY_CANADA, FCC3_FCCA, "CA"}, {CTRY_CANADA2, FCC6_FCCA, "CA"}, {CTRY_CHILE, APL6_WORLD, "CL"}, {CTRY_CHINA, APL1_WORLD, "CN"}, {CTRY_COLOMBIA, FCC1_FCCA, "CO"}, {CTRY_COSTA_RICA, FCC1_WORLD, "CR"}, - {CTRY_CROATIA, ETSI3_WORLD, "HR"}, + {CTRY_CROATIA, ETSI1_WORLD, "HR"}, {CTRY_CYPRUS, ETSI1_WORLD, "CY"}, {CTRY_CZECH, ETSI3_WORLD, "CZ"}, {CTRY_DENMARK, ETSI1_WORLD, "DK"}, @@ -324,18 +328,22 @@ static struct country_code_to_enum_rd allCountries[] = { {CTRY_GEORGIA, ETSI4_WORLD, "GE"}, {CTRY_GERMANY, ETSI1_WORLD, "DE"}, {CTRY_GREECE, ETSI1_WORLD, "GR"}, + {CTRY_GREENLAND, ETSI1_WORLD, "GL"}, + {CTRY_GRENEDA, FCC3_FCCA, "GD"}, + {CTRY_GUAM, FCC1_FCCA, "GU"}, {CTRY_GUATEMALA, FCC1_FCCA, "GT"}, + {CTRY_HAITI, ETSI1_WORLD, "HT"}, {CTRY_HONDURAS, NULL1_WORLD, "HN"}, - {CTRY_HONG_KONG, FCC2_WORLD, "HK"}, + {CTRY_HONG_KONG, FCC3_WORLD, "HK"}, {CTRY_HUNGARY, ETSI1_WORLD, "HU"}, {CTRY_ICELAND, ETSI1_WORLD, "IS"}, {CTRY_INDIA, APL6_WORLD, "IN"}, - {CTRY_INDONESIA, APL1_WORLD, "ID"}, + {CTRY_INDONESIA, NULL1_WORLD, "ID"}, {CTRY_IRAN, APL1_WORLD, "IR"}, {CTRY_IRELAND, ETSI1_WORLD, "IE"}, {CTRY_ISRAEL, NULL1_WORLD, "IL"}, {CTRY_ITALY, ETSI1_WORLD, "IT"}, - {CTRY_JAMAICA, ETSI1_WORLD, "JM"}, + {CTRY_JAMAICA, FCC3_WORLD, "JM"}, {CTRY_JAPAN, MKK1_MKKA, "JP"}, {CTRY_JAPAN1, MKK1_MKKB, "JP"}, @@ -402,7 +410,7 @@ static struct country_code_to_enum_rd allCountries[] = { {CTRY_KOREA_ROC, APL9_WORLD, "KR"}, {CTRY_KOREA_ROC2, APL2_WORLD, "K2"}, {CTRY_KOREA_ROC3, APL9_WORLD, "K3"}, - {CTRY_KUWAIT, NULL1_WORLD, "KW"}, + {CTRY_KUWAIT, ETSI3_WORLD, "KW"}, {CTRY_LATVIA, ETSI1_WORLD, "LV"}, {CTRY_LEBANON, NULL1_WORLD, "LB"}, {CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI"}, @@ -414,13 +422,13 @@ static struct country_code_to_enum_rd allCountries[] = { {CTRY_MALTA, ETSI1_WORLD, "MT"}, {CTRY_MEXICO, FCC1_FCCA, "MX"}, {CTRY_MONACO, ETSI4_WORLD, "MC"}, - {CTRY_MOROCCO, NULL1_WORLD, "MA"}, + {CTRY_MOROCCO, APL4_WORLD, "MA"}, {CTRY_NEPAL, APL1_WORLD, "NP"}, {CTRY_NETHERLANDS, ETSI1_WORLD, "NL"}, {CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN"}, {CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ"}, {CTRY_NORWAY, ETSI1_WORLD, "NO"}, - {CTRY_OMAN, APL6_WORLD, "OM"}, + {CTRY_OMAN, FCC3_WORLD, "OM"}, {CTRY_PAKISTAN, NULL1_WORLD, "PK"}, {CTRY_PANAMA, FCC1_FCCA, "PA"}, {CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG"}, @@ -429,7 +437,7 @@ static struct country_code_to_enum_rd allCountries[] = { {CTRY_POLAND, ETSI1_WORLD, "PL"}, {CTRY_PORTUGAL, ETSI1_WORLD, "PT"}, {CTRY_PUERTO_RICO, FCC1_FCCA, "PR"}, - {CTRY_QATAR, NULL1_WORLD, "QA"}, + {CTRY_QATAR, APL1_WORLD, "QA"}, {CTRY_ROMANIA, NULL1_WORLD, "RO"}, {CTRY_RUSSIA, NULL1_WORLD, "RU"}, {CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA"}, @@ -445,7 +453,7 @@ static struct country_code_to_enum_rd allCountries[] = { {CTRY_SYRIA, NULL1_WORLD, "SY"}, {CTRY_TAIWAN, APL3_FCCA, "TW"}, {CTRY_THAILAND, FCC3_WORLD, "TH"}, - {CTRY_TRINIDAD_Y_TOBAGO, ETSI4_WORLD, "TT"}, + {CTRY_TRINIDAD_Y_TOBAGO, FCC3_WORLD, "TT"}, {CTRY_TUNISIA, ETSI3_WORLD, "TN"}, {CTRY_TURKEY, ETSI3_WORLD, "TR"}, {CTRY_UKRAINE, NULL1_WORLD, "UA"}, @@ -456,7 +464,7 @@ static struct country_code_to_enum_rd allCountries[] = { * would need to assign new special alpha2 to CRDA db as with the world * regdomain and use another alpha2 */ {CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS"}, - {CTRY_URUGUAY, APL2_WORLD, "UY"}, + {CTRY_URUGUAY, FCC3_WORLD, "UY"}, {CTRY_UZBEKISTAN, FCC3_FCCA, "UZ"}, {CTRY_VENEZUELA, APL2_ETSIC, "VE"}, {CTRY_VIET_NAM, NULL1_WORLD, "VN"}, diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index cce188837d10..3edbbcf0f548 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -99,6 +99,22 @@ static struct { { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" }, { ATMEL_FW_TYPE_NONE, NULL, NULL } }; +MODULE_FIRMWARE("atmel_at76c502-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502.bin"); +MODULE_FIRMWARE("atmel_at76c502d-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502d.bin"); +MODULE_FIRMWARE("atmel_at76c502e-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502e.bin"); +MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502_3com.bin"); +MODULE_FIRMWARE("atmel_at76c504-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504.bin"); +MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504_2958.bin"); +MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504a_2958.bin"); +MODULE_FIRMWARE("atmel_at76c506-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c506.bin"); #define MAX_SSID_LENGTH 32 #define MGMT_JIFFIES (256 * HZ / 100) diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 54ea61c15d8b..64c12e1bced3 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -1,6 +1,6 @@ config B43 tristate "Broadcom 43xx wireless support (mac80211 stack)" - depends on SSB_POSSIBLE && MAC80211 && WLAN_80211 && HAS_DMA + depends on SSB_POSSIBLE && MAC80211 && HAS_DMA select SSB select FW_LOADER ---help--- diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 660716214d49..fe3bf9491997 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -26,8 +26,6 @@ # define B43_DEBUG 0 #endif -#define B43_RX_MAX_SSI 60 - /* MMIO offsets */ #define B43_MMIO_DMA0_REASON 0x20 #define B43_MMIO_DMA0_IRQ_MASK 0x24 @@ -749,12 +747,6 @@ struct b43_wldev { #endif }; -/* - * Include goes here to avoid a dependency problem. - * A better fix would be to integrate xmit.h into b43.h. - */ -#include "xmit.h" - /* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ struct b43_wl { /* Pointer to the active wireless device on this chip */ @@ -830,13 +822,9 @@ struct b43_wl { struct b43_leds leds; #ifdef CONFIG_B43_PIO - /* - * RX/TX header/tail buffers used by the frame transmit functions. - */ - struct b43_rxhdr_fw4 rxhdr; - struct b43_txhdr txhdr; - u8 rx_tail[4]; - u8 tx_tail[4]; + /* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */ + u8 pio_scratchspace[110] __attribute__((__aligned__(8))); + u8 pio_tailspace[4] __attribute__((__aligned__(8))); #endif /* CONFIG_B43_PIO */ }; diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index de4e804bedf0..027be275e035 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -383,44 +383,160 @@ static inline } } +/* Check if a DMA region fits the device constraints. + * Returns true, if the region is OK for usage with this device. */ +static inline bool b43_dma_address_ok(struct b43_dmaring *ring, + dma_addr_t addr, size_t size) +{ + switch (ring->type) { + case B43_DMA_30BIT: + if ((u64)addr + size > (1ULL << 30)) + return 0; + break; + case B43_DMA_32BIT: + if ((u64)addr + size > (1ULL << 32)) + return 0; + break; + case B43_DMA_64BIT: + /* Currently we can't have addresses beyond + * 64bit in the kernel. */ + break; + } + return 1; +} + +#define is_4k_aligned(addr) (((u64)(addr) & 0x0FFFull) == 0) +#define is_8k_aligned(addr) (((u64)(addr) & 0x1FFFull) == 0) + +static void b43_unmap_and_free_ringmem(struct b43_dmaring *ring, void *base, + dma_addr_t dmaaddr, size_t size) +{ + ssb_dma_unmap_single(ring->dev->dev, dmaaddr, size, DMA_TO_DEVICE); + free_pages((unsigned long)base, get_order(size)); +} + +static void * __b43_get_and_map_ringmem(struct b43_dmaring *ring, + dma_addr_t *dmaaddr, size_t size, + gfp_t gfp_flags) +{ + void *base; + + base = (void *)__get_free_pages(gfp_flags, get_order(size)); + if (!base) + return NULL; + memset(base, 0, size); + *dmaaddr = ssb_dma_map_single(ring->dev->dev, base, size, + DMA_TO_DEVICE); + if (ssb_dma_mapping_error(ring->dev->dev, *dmaaddr)) { + free_pages((unsigned long)base, get_order(size)); + return NULL; + } + + return base; +} + +static void * b43_get_and_map_ringmem(struct b43_dmaring *ring, + dma_addr_t *dmaaddr, size_t size) +{ + void *base; + + base = __b43_get_and_map_ringmem(ring, dmaaddr, size, + GFP_KERNEL); + if (!base) { + b43err(ring->dev->wl, "Failed to allocate or map pages " + "for DMA ringmemory\n"); + return NULL; + } + if (!b43_dma_address_ok(ring, *dmaaddr, size)) { + /* The memory does not fit our device constraints. + * Retry with GFP_DMA set to get lower memory. */ + b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size); + base = __b43_get_and_map_ringmem(ring, dmaaddr, size, + GFP_KERNEL | GFP_DMA); + if (!base) { + b43err(ring->dev->wl, "Failed to allocate or map pages " + "in the GFP_DMA region for DMA ringmemory\n"); + return NULL; + } + if (!b43_dma_address_ok(ring, *dmaaddr, size)) { + b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size); + b43err(ring->dev->wl, "Failed to allocate DMA " + "ringmemory that fits device constraints\n"); + return NULL; + } + } + /* We expect the memory to be 4k aligned, at least. */ + if (B43_WARN_ON(!is_4k_aligned(*dmaaddr))) { + b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size); + return NULL; + } + + return base; +} + static int alloc_ringmemory(struct b43_dmaring *ring) { - gfp_t flags = GFP_KERNEL; - - /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K - * alignment and 8K buffers for 64-bit DMA with 8K alignment. Testing - * has shown that 4K is sufficient for the latter as long as the buffer - * does not cross an 8K boundary. - * - * For unknown reasons - possibly a hardware error - the BCM4311 rev - * 02, which uses 64-bit DMA, needs the ring buffer in very low memory, - * which accounts for the GFP_DMA flag below. - * - * The flags here must match the flags in free_ringmemory below! + unsigned int required; + void *base; + dma_addr_t dmaaddr; + + /* There are several requirements to the descriptor ring memory: + * - The memory region needs to fit the address constraints for the + * device (same as for frame buffers). + * - For 30/32bit DMA devices, the descriptor ring must be 4k aligned. + * - For 64bit DMA devices, the descriptor ring must be 8k aligned. */ + if (ring->type == B43_DMA_64BIT) - flags |= GFP_DMA; - ring->descbase = ssb_dma_alloc_consistent(ring->dev->dev, - B43_DMA_RINGMEMSIZE, - &(ring->dmabase), flags); - if (!ring->descbase) { - b43err(ring->dev->wl, "DMA ringmemory allocation failed\n"); + required = ring->nr_slots * sizeof(struct b43_dmadesc64); + else + required = ring->nr_slots * sizeof(struct b43_dmadesc32); + if (B43_WARN_ON(required > 0x1000)) + return -ENOMEM; + + ring->alloc_descsize = 0x1000; + base = b43_get_and_map_ringmem(ring, &dmaaddr, ring->alloc_descsize); + if (!base) + return -ENOMEM; + ring->alloc_descbase = base; + ring->alloc_dmabase = dmaaddr; + + if ((ring->type != B43_DMA_64BIT) || is_8k_aligned(dmaaddr)) { + /* We're on <=32bit DMA, or we already got 8k aligned memory. + * That's all we need, so we're fine. */ + ring->descbase = base; + ring->dmabase = dmaaddr; + return 0; + } + b43_unmap_and_free_ringmem(ring, base, dmaaddr, ring->alloc_descsize); + + /* Ok, we failed at the 8k alignment requirement. + * Try to force-align the memory region now. */ + ring->alloc_descsize = 0x2000; + base = b43_get_and_map_ringmem(ring, &dmaaddr, ring->alloc_descsize); + if (!base) return -ENOMEM; + ring->alloc_descbase = base; + ring->alloc_dmabase = dmaaddr; + + if (is_8k_aligned(dmaaddr)) { + /* We're already 8k aligned. That Ok, too. */ + ring->descbase = base; + ring->dmabase = dmaaddr; + return 0; } - memset(ring->descbase, 0, B43_DMA_RINGMEMSIZE); + /* Force-align it to 8k */ + ring->descbase = (void *)((u8 *)base + 0x1000); + ring->dmabase = dmaaddr + 0x1000; + B43_WARN_ON(!is_8k_aligned(ring->dmabase)); return 0; } static void free_ringmemory(struct b43_dmaring *ring) { - gfp_t flags = GFP_KERNEL; - - if (ring->type == B43_DMA_64BIT) - flags |= GFP_DMA; - - ssb_dma_free_consistent(ring->dev->dev, B43_DMA_RINGMEMSIZE, - ring->descbase, ring->dmabase, flags); + b43_unmap_and_free_ringmem(ring, ring->alloc_descbase, + ring->alloc_dmabase, ring->alloc_descsize); } /* Reset the RX DMA channel */ @@ -530,29 +646,14 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring, if (unlikely(ssb_dma_mapping_error(ring->dev->dev, addr))) return 1; - switch (ring->type) { - case B43_DMA_30BIT: - if ((u64)addr + buffersize > (1ULL << 30)) - goto address_error; - break; - case B43_DMA_32BIT: - if ((u64)addr + buffersize > (1ULL << 32)) - goto address_error; - break; - case B43_DMA_64BIT: - /* Currently we can't have addresses beyond - * 64bit in the kernel. */ - break; + if (!b43_dma_address_ok(ring, addr, buffersize)) { + /* We can't support this address. Unmap it again. */ + unmap_descbuffer(ring, addr, buffersize, dma_to_device); + return 1; } /* The address is OK. */ return 0; - -address_error: - /* We can't support this address. Unmap it again. */ - unmap_descbuffer(ring, addr, buffersize, dma_to_device); - - return 1; } static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb) @@ -614,6 +715,9 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring, meta->dmaaddr = dmaaddr; ring->ops->fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0); + ssb_dma_sync_single_for_device(ring->dev->dev, + ring->alloc_dmabase, + ring->alloc_descsize, DMA_TO_DEVICE); return 0; } @@ -770,7 +874,7 @@ static void free_all_descbuffers(struct b43_dmaring *ring) for (i = 0; i < ring->nr_slots; i++) { desc = ring->ops->idx2desc(ring, i, &meta); - if (!meta->skb) { + if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) { B43_WARN_ON(!ring->tx); continue; } @@ -822,7 +926,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, enum b43_dmatype type) { struct b43_dmaring *ring; - int err; + int i, err; dma_addr_t dma_test; ring = kzalloc(sizeof(*ring), GFP_KERNEL); @@ -837,6 +941,8 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, GFP_KERNEL); if (!ring->meta) goto err_kfree_ring; + for (i = 0; i < ring->nr_slots; i++) + ring->meta->skb = B43_DMA_PTR_POISON; ring->type = type; ring->dev = dev; @@ -1147,28 +1253,29 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) case 0x5000: ring = dma->tx_ring_mcast; break; - default: - B43_WARN_ON(1); } *slot = (cookie & 0x0FFF); - B43_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots)); + if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) { + b43dbg(dev->wl, "TX-status contains " + "invalid cookie: 0x%04X\n", cookie); + return NULL; + } return ring; } static int dma_tx_fragment(struct b43_dmaring *ring, - struct sk_buff **in_skb) + struct sk_buff *skb) { - struct sk_buff *skb = *in_skb; const struct b43_dma_ops *ops = ring->ops; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct b43_private_tx_info *priv_info = b43_get_priv_tx_info(info); u8 *header; int slot, old_top_slot, old_used_slots; int err; struct b43_dmadesc_generic *desc; struct b43_dmadesc_meta *meta; struct b43_dmadesc_meta *meta_hdr; - struct sk_buff *bounce_skb; u16 cookie; size_t hdrsize = b43_txhdr_size(ring->dev); @@ -1212,34 +1319,28 @@ static int dma_tx_fragment(struct b43_dmaring *ring, meta->skb = skb; meta->is_last_fragment = 1; + priv_info->bouncebuffer = NULL; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); /* create a bounce buffer in zone_dma on mapping failure. */ if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { - bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); - if (!bounce_skb) { + priv_info->bouncebuffer = kmalloc(skb->len, GFP_ATOMIC | GFP_DMA); + if (!priv_info->bouncebuffer) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; err = -ENOMEM; goto out_unmap_hdr; } + memcpy(priv_info->bouncebuffer, skb->data, skb->len); - memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); - memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); - bounce_skb->dev = skb->dev; - skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); - info = IEEE80211_SKB_CB(bounce_skb); - - dev_kfree_skb_any(skb); - skb = bounce_skb; - *in_skb = bounce_skb; - meta->skb = skb; - meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + meta->dmaaddr = map_descbuffer(ring, priv_info->bouncebuffer, skb->len, 1); if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { + kfree(priv_info->bouncebuffer); + priv_info->bouncebuffer = NULL; ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; err = -EIO; - goto out_free_bounce; + goto out_unmap_hdr; } } @@ -1253,11 +1354,12 @@ static int dma_tx_fragment(struct b43_dmaring *ring, } /* Now transfer the whole frame. */ wmb(); + ssb_dma_sync_single_for_device(ring->dev->dev, + ring->alloc_dmabase, + ring->alloc_descsize, DMA_TO_DEVICE); ops->poke_tx(ring, next_slot(ring, slot)); return 0; -out_free_bounce: - dev_kfree_skb_any(skb); out_unmap_hdr: unmap_descbuffer(ring, meta_hdr->dmaaddr, hdrsize, 1); @@ -1362,11 +1464,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) * static, so we don't need to store it per frame. */ ring->queue_prio = skb_get_queue_mapping(skb); - /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing - * into the skb data or cb now. */ - hdr = NULL; - info = NULL; - err = dma_tx_fragment(ring, &skb); + err = dma_tx_fragment(ring, skb); if (unlikely(err == -ENOKEY)) { /* Drop this packet, as we don't have the encryption key * anymore and must not transmit it unencrypted. */ @@ -1400,30 +1498,63 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, struct b43_dmaring *ring; struct b43_dmadesc_generic *desc; struct b43_dmadesc_meta *meta; - int slot; + int slot, firstused; bool frame_succeed; ring = parse_cookie(dev, status->cookie, &slot); if (unlikely(!ring)) return; - B43_WARN_ON(!ring->tx); + + /* Sanity check: TX packets are processed in-order on one ring. + * Check if the slot deduced from the cookie really is the first + * used slot. */ + firstused = ring->current_slot - ring->used_slots + 1; + if (firstused < 0) + firstused = ring->nr_slots + firstused; + if (unlikely(slot != firstused)) { + /* This possibly is a firmware bug and will result in + * malfunction, memory leaks and/or stall of DMA functionality. */ + b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. " + "Expected %d, but got %d\n", + ring->index, firstused, slot); + return; + } + ops = ring->ops; while (1) { - B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); + B43_WARN_ON(slot < 0 || slot >= ring->nr_slots); desc = ops->idx2desc(ring, slot, &meta); - if (meta->skb) - unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, - 1); - else + if (b43_dma_ptr_is_poisoned(meta->skb)) { + b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) " + "on ring %d\n", + slot, firstused, ring->index); + break; + } + if (meta->skb) { + struct b43_private_tx_info *priv_info = + b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); + + unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); + kfree(priv_info->bouncebuffer); + priv_info->bouncebuffer = NULL; + } else { unmap_descbuffer(ring, meta->dmaaddr, b43_txhdr_size(dev), 1); + } if (meta->is_last_fragment) { struct ieee80211_tx_info *info; - BUG_ON(!meta->skb); + if (unlikely(!meta->skb)) { + /* This is a scatter-gather fragment of a frame, so + * the skb pointer must not be NULL. */ + b43dbg(dev->wl, "TX status unexpected NULL skb " + "at slot %d (first=%d) on ring %d\n", + slot, firstused, ring->index); + break; + } info = IEEE80211_SKB_CB(meta->skb); @@ -1441,20 +1572,29 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, #endif /* DEBUG */ ieee80211_tx_status(dev->wl->hw, meta->skb); - /* skb is freed by ieee80211_tx_status() */ - meta->skb = NULL; + /* skb will be freed by ieee80211_tx_status(). + * Poison our pointer. */ + meta->skb = B43_DMA_PTR_POISON; } else { /* No need to call free_descriptor_buffer here, as * this is only the txhdr, which is not allocated. */ - B43_WARN_ON(meta->skb); + if (unlikely(meta->skb)) { + b43dbg(dev->wl, "TX status unexpected non-NULL skb " + "at slot %d (first=%d) on ring %d\n", + slot, firstused, ring->index); + break; + } } /* Everything unmapped and free'd. So it's not used anymore. */ ring->used_slots--; - if (meta->is_last_fragment) + if (meta->is_last_fragment) { + /* This is the last scatter-gather + * fragment of the frame. We are done. */ break; + } slot = next_slot(ring, slot); } if (ring->stopped) { diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h index f0b0838fb5ba..e607b392314c 100644 --- a/drivers/net/wireless/b43/dma.h +++ b/drivers/net/wireless/b43/dma.h @@ -1,7 +1,7 @@ #ifndef B43_DMA_H_ #define B43_DMA_H_ -#include <linux/ieee80211.h> +#include <linux/err.h> #include "b43.h" @@ -157,7 +157,6 @@ struct b43_dmadesc_generic { } __attribute__ ((__packed__)); /* Misc DMA constants */ -#define B43_DMA_RINGMEMSIZE PAGE_SIZE #define B43_DMA0_RX_FRAMEOFFSET 30 /* DMA engine tuning knobs */ @@ -165,6 +164,10 @@ struct b43_dmadesc_generic { #define B43_RXRING_SLOTS 64 #define B43_DMA0_RX_BUFFERSIZE IEEE80211_MAX_FRAME_LEN +/* Pointer poison */ +#define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM)) +#define b43_dma_ptr_is_poisoned(ptr) (unlikely((ptr) == B43_DMA_PTR_POISON)) + struct sk_buff; struct b43_private; @@ -243,6 +246,12 @@ struct b43_dmaring { /* The QOS priority assigned to this ring. Only used for TX rings. * This is the mac80211 "queue" value. */ u8 queue_prio; + /* Pointers and size of the originally allocated and mapped memory + * region for the descriptor ring. */ + void *alloc_descbase; + dma_addr_t alloc_dmabase; + unsigned int alloc_descsize; + /* Pointer to our wireless device. */ struct b43_wldev *dev; #ifdef CONFIG_B43_DEBUG /* Maximum number of used slots. */ diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index 1e8dba488004..c587115dd2b9 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -246,6 +246,7 @@ static void b43_led_get_sprominfo(struct b43_wldev *dev, *behaviour = B43_LED_OFF; break; default: + *behaviour = B43_LED_OFF; B43_WARN_ON(1); return; } diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 098dda1a67c1..71e5c996bd09 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2955,7 +2955,7 @@ static void do_periodic_work(struct b43_wldev *dev) /* Periodic work locking policy: * The whole periodic work handler is protected by * wl->mutex. If another lock is needed somewhere in the - * pwork callchain, it's aquired in-place, where it's needed. + * pwork callchain, it's acquired in-place, where it's needed. */ static void b43_periodic_work_handler(struct work_struct *work) { @@ -3573,7 +3573,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) if (conf->channel->hw_value != phy->channel) b43_switch_channel(dev, conf->channel->hw_value); - dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_RADIOTAP); + dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); /* Adjust the desired TX power level. */ if (conf->power_level != 0) { @@ -4669,7 +4669,7 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; struct ssb_bus *bus = dev->dev->bus; - struct pci_dev *pdev = bus->host_pci; + struct pci_dev *pdev = (bus->bustype == SSB_BUSTYPE_PCI) ? bus->host_pci : NULL; int err; bool have_2ghz_phy = 0, have_5ghz_phy = 0; u32 tmp; @@ -4802,7 +4802,7 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl) if (!list_empty(&wl->devlist)) { /* We are not the first core on this chip. */ - pdev = dev->bus->host_pci; + pdev = (dev->bus->bustype == SSB_BUSTYPE_PCI) ? dev->bus->host_pci : NULL; /* Only special chips support more than one wireless * core, although some of the other chips have more than * one wireless core as well. Check for this and diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 1e318d815a5b..3e046ec1ff86 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -67,6 +67,7 @@ static void b43_lpphy_op_prepare_structs(struct b43_wldev *dev) struct b43_phy_lp *lpphy = phy->lp; memset(lpphy, 0, sizeof(*lpphy)); + lpphy->antenna = B43_ANTENNA_DEFAULT; //TODO } @@ -751,11 +752,17 @@ static void lpphy_clear_deaf(struct b43_wldev *dev, bool user) } } +static void lpphy_set_trsw_over(struct b43_wldev *dev, bool tx, bool rx) +{ + u16 trsw = (tx << 1) | rx; + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, trsw); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3); +} + static void lpphy_disable_crs(struct b43_wldev *dev, bool user) { lpphy_set_deaf(dev, user); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, 0x1); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3); + lpphy_set_trsw_over(dev, false, true); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7); @@ -790,6 +797,60 @@ static void lpphy_restore_crs(struct b43_wldev *dev, bool user) struct lpphy_tx_gains { u16 gm, pga, pad, dac; }; +static void lpphy_disable_rx_gain_override(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF); + if (dev->phy.rev >= 2) { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF); + b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7); + } + } else { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF); + } +} + +static void lpphy_enable_rx_gain_override(struct b43_wldev *dev) +{ + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); + if (dev->phy.rev >= 2) { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400); + b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8); + } + } else { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200); + } +} + +static void lpphy_disable_tx_gain_override(struct b43_wldev *dev) +{ + if (dev->phy.rev < 2) + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); + else { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF); + } + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF); +} + +static void lpphy_enable_tx_gain_override(struct b43_wldev *dev) +{ + if (dev->phy.rev < 2) + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); + else { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x80); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x4000); + } + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x40); +} + static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev) { struct lpphy_tx_gains gains; @@ -819,6 +880,17 @@ static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac) b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl); } +static u16 lpphy_get_pa_gain(struct b43_wldev *dev) +{ + return b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x7F; +} + +static void lpphy_set_pa_gain(struct b43_wldev *dev, u16 gain) +{ + b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0xE03F, gain << 6); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x80FF, gain << 8); +} + static void lpphy_set_tx_gains(struct b43_wldev *dev, struct lpphy_tx_gains gains) { @@ -829,25 +901,22 @@ static void lpphy_set_tx_gains(struct b43_wldev *dev, b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0xF800, rf_gain); } else { - pa_gain = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x1FC0; - pa_gain <<= 2; + pa_gain = lpphy_get_pa_gain(dev); b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, (gains.pga << 8) | gains.gm); + /* + * SPEC FIXME The spec calls for (pa_gain << 8) here, but that + * conflicts with the spec for set_pa_gain! Vendor driver bug? + */ b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), - 0x8000, gains.pad | pa_gain); + 0x8000, gains.pad | (pa_gain << 6)); b43_phy_write(dev, B43_PHY_OFDM(0xFC), (gains.pga << 8) | gains.gm); b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), - 0x8000, gains.pad | pa_gain); + 0x8000, gains.pad | (pa_gain << 8)); } lpphy_set_dac_gain(dev, gains.dac); - if (dev->phy.rev < 2) { - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF, 1 << 8); - } else { - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F, 1 << 7); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF, 1 << 14); - } - b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF, 1 << 6); + lpphy_enable_tx_gain_override(dev); } static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain) @@ -887,38 +956,6 @@ static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain) } } -static void lpphy_disable_rx_gain_override(struct b43_wldev *dev) -{ - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF); - if (dev->phy.rev >= 2) { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF); - b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7); - } - } else { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF); - } -} - -static void lpphy_enable_rx_gain_override(struct b43_wldev *dev) -{ - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); - if (dev->phy.rev >= 2) { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400); - b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8); - } - } else { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200); - } -} - static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain) { if (dev->phy.rev < 2) @@ -1003,8 +1040,7 @@ static int lpphy_loopback(struct b43_wldev *dev) memset(&iq_est, 0, sizeof(iq_est)); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, 0x3); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3); + lpphy_set_trsw_over(dev, true, true); b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1); b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); @@ -1126,7 +1162,7 @@ static void lpphy_set_tx_power_control(struct b43_wldev *dev, b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0x8FFF, ((u16)lpphy->tssi_npt << 16)); //TODO Set "TSSI Transmit Count" variable to total transmitted frame count - //TODO Disable TX gain override + lpphy_disable_tx_gain_override(dev); lpphy->tx_pwr_idx_over = -1; } } @@ -1312,15 +1348,73 @@ static void lpphy_calibrate_rc(struct b43_wldev *dev) } } +static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) +{ + if (dev->phy.rev >= 2) + return; // rev2+ doesn't support antenna diversity + + if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1)) + return; + + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); + + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2); + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1); + + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); + + dev->phy.lp->antenna = antenna; +} + +static void lpphy_set_tx_iqcc(struct b43_wldev *dev, u16 a, u16 b) +{ + u16 tmp[2]; + + tmp[0] = a; + tmp[1] = b; + b43_lptab_write_bulk(dev, B43_LPTAB16(0, 80), 2, tmp); +} + static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index) { struct b43_phy_lp *lpphy = dev->phy.lp; + struct lpphy_tx_gains gains; + u32 iq_comp, tx_gain, coeff, rf_power; lpphy->tx_pwr_idx_over = index; + lpphy_read_tx_pctl_mode_from_hardware(dev); if (lpphy->txpctl_mode != B43_LPPHY_TXPCTL_OFF) lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_SW); - - //TODO + if (dev->phy.rev >= 2) { + iq_comp = b43_lptab_read(dev, B43_LPTAB32(7, index + 320)); + tx_gain = b43_lptab_read(dev, B43_LPTAB32(7, index + 192)); + gains.pad = (tx_gain >> 16) & 0xFF; + gains.gm = tx_gain & 0xFF; + gains.pga = (tx_gain >> 8) & 0xFF; + gains.dac = (iq_comp >> 28) & 0xFF; + lpphy_set_tx_gains(dev, gains); + } else { + iq_comp = b43_lptab_read(dev, B43_LPTAB32(10, index + 320)); + tx_gain = b43_lptab_read(dev, B43_LPTAB32(10, index + 192)); + b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, + 0xF800, (tx_gain >> 4) & 0x7FFF); + lpphy_set_dac_gain(dev, tx_gain & 0x7); + lpphy_set_pa_gain(dev, (tx_gain >> 24) & 0x7F); + } + lpphy_set_bb_mult(dev, (iq_comp >> 20) & 0xFF); + lpphy_set_tx_iqcc(dev, (iq_comp >> 10) & 0x3FF, iq_comp & 0x3FF); + if (dev->phy.rev >= 2) { + coeff = b43_lptab_read(dev, B43_LPTAB32(7, index + 448)); + } else { + coeff = b43_lptab_read(dev, B43_LPTAB32(10, index + 448)); + } + b43_lptab_write(dev, B43_LPTAB16(0, 85), coeff & 0xFFFF); + if (dev->phy.rev >= 2) { + rf_power = b43_lptab_read(dev, B43_LPTAB32(7, index + 576)); + b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, + rf_power & 0xFFFF);//SPEC FIXME mask & set != 0 + } + lpphy_enable_tx_gain_override(dev); } static void lpphy_btcoex_override(struct b43_wldev *dev) @@ -1329,58 +1423,45 @@ static void lpphy_btcoex_override(struct b43_wldev *dev) b43_write16(dev, B43_MMIO_BTCOEX_TXCTL, 0xFF); } -static void lpphy_pr41573_workaround(struct b43_wldev *dev) +static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev, + bool blocked) { - struct b43_phy_lp *lpphy = dev->phy.lp; - u32 *saved_tab; - const unsigned int saved_tab_size = 256; - enum b43_lpphy_txpctl_mode txpctl_mode; - s8 tx_pwr_idx_over; - u16 tssi_npt, tssi_idx; - - saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL); - if (!saved_tab) { - b43err(dev->wl, "PR41573 failed. Out of memory!\n"); - return; - } - - lpphy_read_tx_pctl_mode_from_hardware(dev); - txpctl_mode = lpphy->txpctl_mode; - tx_pwr_idx_over = lpphy->tx_pwr_idx_over; - tssi_npt = lpphy->tssi_npt; - tssi_idx = lpphy->tssi_idx; - - if (dev->phy.rev < 2) { - b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140), - saved_tab_size, saved_tab); + //TODO check MAC control register + if (blocked) { + if (dev->phy.rev >= 2) { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x83FF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00); + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0x80FF); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xDFFF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0808); + } else { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xE0FF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFCFF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0018); + } } else { - b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140), - saved_tab_size, saved_tab); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xE0FF); + if (dev->phy.rev >= 2) + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xF7F7); + else + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFFE7); } - //TODO - - kfree(saved_tab); } -static void lpphy_calibration(struct b43_wldev *dev) +/* This was previously called lpphy_japan_filter */ +static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel) { struct b43_phy_lp *lpphy = dev->phy.lp; - enum b43_lpphy_txpctl_mode saved_pctl_mode; - - b43_mac_suspend(dev); - - lpphy_btcoex_override(dev); - lpphy_read_tx_pctl_mode_from_hardware(dev); - saved_pctl_mode = lpphy->txpctl_mode; - lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); - //TODO Perform transmit power table I/Q LO calibration - if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF)) - lpphy_pr41573_workaround(dev); - //TODO If a full calibration has not been performed on this channel yet, perform PAPD TX-power calibration - lpphy_set_tx_power_control(dev, saved_pctl_mode); - //TODO Perform I/Q calibration with a single control value set + u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter! - b43_mac_enable(dev); + if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific? + b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9); + if ((dev->phy.rev == 1) && (lpphy->rc_cap)) + lpphy_set_rc_cap(dev); + } else { + b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F); + } } static void lpphy_set_tssi_mux(struct b43_wldev *dev, enum tssi_mux_mode mode) @@ -1489,6 +1570,473 @@ static void lpphy_tx_pctl_init(struct b43_wldev *dev) } } +static void lpphy_pr41573_workaround(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u32 *saved_tab; + const unsigned int saved_tab_size = 256; + enum b43_lpphy_txpctl_mode txpctl_mode; + s8 tx_pwr_idx_over; + u16 tssi_npt, tssi_idx; + + saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL); + if (!saved_tab) { + b43err(dev->wl, "PR41573 failed. Out of memory!\n"); + return; + } + + lpphy_read_tx_pctl_mode_from_hardware(dev); + txpctl_mode = lpphy->txpctl_mode; + tx_pwr_idx_over = lpphy->tx_pwr_idx_over; + tssi_npt = lpphy->tssi_npt; + tssi_idx = lpphy->tssi_idx; + + if (dev->phy.rev < 2) { + b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140), + saved_tab_size, saved_tab); + } else { + b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140), + saved_tab_size, saved_tab); + } + //FIXME PHY reset + lpphy_table_init(dev); //FIXME is table init needed? + lpphy_baseband_init(dev); + lpphy_tx_pctl_init(dev); + b43_lpphy_op_software_rfkill(dev, false); + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); + if (dev->phy.rev < 2) { + b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140), + saved_tab_size, saved_tab); + } else { + b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140), + saved_tab_size, saved_tab); + } + b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel); + lpphy->tssi_npt = tssi_npt; + lpphy->tssi_idx = tssi_idx; + lpphy_set_analog_filter(dev, lpphy->channel); + if (tx_pwr_idx_over != -1) + lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over); + if (lpphy->rc_cap) + lpphy_set_rc_cap(dev); + b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna); + lpphy_set_tx_power_control(dev, txpctl_mode); + kfree(saved_tab); +} + +struct lpphy_rx_iq_comp { u8 chan; s8 c1, c0; }; + +static const struct lpphy_rx_iq_comp lpphy_5354_iq_table[] = { + { .chan = 1, .c1 = -66, .c0 = 15, }, + { .chan = 2, .c1 = -66, .c0 = 15, }, + { .chan = 3, .c1 = -66, .c0 = 15, }, + { .chan = 4, .c1 = -66, .c0 = 15, }, + { .chan = 5, .c1 = -66, .c0 = 15, }, + { .chan = 6, .c1 = -66, .c0 = 15, }, + { .chan = 7, .c1 = -66, .c0 = 14, }, + { .chan = 8, .c1 = -66, .c0 = 14, }, + { .chan = 9, .c1 = -66, .c0 = 14, }, + { .chan = 10, .c1 = -66, .c0 = 14, }, + { .chan = 11, .c1 = -66, .c0 = 14, }, + { .chan = 12, .c1 = -66, .c0 = 13, }, + { .chan = 13, .c1 = -66, .c0 = 13, }, + { .chan = 14, .c1 = -66, .c0 = 13, }, +}; + +static const struct lpphy_rx_iq_comp lpphy_rev0_1_iq_table[] = { + { .chan = 1, .c1 = -64, .c0 = 13, }, + { .chan = 2, .c1 = -64, .c0 = 13, }, + { .chan = 3, .c1 = -64, .c0 = 13, }, + { .chan = 4, .c1 = -64, .c0 = 13, }, + { .chan = 5, .c1 = -64, .c0 = 12, }, + { .chan = 6, .c1 = -64, .c0 = 12, }, + { .chan = 7, .c1 = -64, .c0 = 12, }, + { .chan = 8, .c1 = -64, .c0 = 12, }, + { .chan = 9, .c1 = -64, .c0 = 12, }, + { .chan = 10, .c1 = -64, .c0 = 11, }, + { .chan = 11, .c1 = -64, .c0 = 11, }, + { .chan = 12, .c1 = -64, .c0 = 11, }, + { .chan = 13, .c1 = -64, .c0 = 11, }, + { .chan = 14, .c1 = -64, .c0 = 10, }, + { .chan = 34, .c1 = -62, .c0 = 24, }, + { .chan = 38, .c1 = -62, .c0 = 24, }, + { .chan = 42, .c1 = -62, .c0 = 24, }, + { .chan = 46, .c1 = -62, .c0 = 23, }, + { .chan = 36, .c1 = -62, .c0 = 24, }, + { .chan = 40, .c1 = -62, .c0 = 24, }, + { .chan = 44, .c1 = -62, .c0 = 23, }, + { .chan = 48, .c1 = -62, .c0 = 23, }, + { .chan = 52, .c1 = -62, .c0 = 23, }, + { .chan = 56, .c1 = -62, .c0 = 22, }, + { .chan = 60, .c1 = -62, .c0 = 22, }, + { .chan = 64, .c1 = -62, .c0 = 22, }, + { .chan = 100, .c1 = -62, .c0 = 16, }, + { .chan = 104, .c1 = -62, .c0 = 16, }, + { .chan = 108, .c1 = -62, .c0 = 15, }, + { .chan = 112, .c1 = -62, .c0 = 14, }, + { .chan = 116, .c1 = -62, .c0 = 14, }, + { .chan = 120, .c1 = -62, .c0 = 13, }, + { .chan = 124, .c1 = -62, .c0 = 12, }, + { .chan = 128, .c1 = -62, .c0 = 12, }, + { .chan = 132, .c1 = -62, .c0 = 12, }, + { .chan = 136, .c1 = -62, .c0 = 11, }, + { .chan = 140, .c1 = -62, .c0 = 10, }, + { .chan = 149, .c1 = -61, .c0 = 9, }, + { .chan = 153, .c1 = -61, .c0 = 9, }, + { .chan = 157, .c1 = -61, .c0 = 9, }, + { .chan = 161, .c1 = -61, .c0 = 8, }, + { .chan = 165, .c1 = -61, .c0 = 8, }, + { .chan = 184, .c1 = -62, .c0 = 25, }, + { .chan = 188, .c1 = -62, .c0 = 25, }, + { .chan = 192, .c1 = -62, .c0 = 25, }, + { .chan = 196, .c1 = -62, .c0 = 25, }, + { .chan = 200, .c1 = -62, .c0 = 25, }, + { .chan = 204, .c1 = -62, .c0 = 25, }, + { .chan = 208, .c1 = -62, .c0 = 25, }, + { .chan = 212, .c1 = -62, .c0 = 25, }, + { .chan = 216, .c1 = -62, .c0 = 26, }, +}; + +static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = { + .chan = 0, + .c1 = -64, + .c0 = 0, +}; + +static u8 lpphy_nbits(s32 val) +{ + u32 tmp = abs(val); + u8 nbits = 0; + + while (tmp != 0) { + nbits++; + tmp >>= 1; + } + + return nbits; +} + +static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples) +{ + struct lpphy_iq_est iq_est; + u16 c0, c1; + int prod, ipwr, qpwr, prod_msb, q_msb, tmp1, tmp2, tmp3, tmp4, ret; + + c1 = b43_phy_read(dev, B43_LPPHY_RX_COMP_COEFF_S); + c0 = c1 >> 8; + c1 |= 0xFF; + + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, 0x00C0); + b43_phy_mask(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF); + + ret = lpphy_rx_iq_est(dev, samples, 32, &iq_est); + if (!ret) + goto out; + + prod = iq_est.iq_prod; + ipwr = iq_est.i_pwr; + qpwr = iq_est.q_pwr; + + if (ipwr + qpwr < 2) { + ret = 0; + goto out; + } + + prod_msb = lpphy_nbits(prod); + q_msb = lpphy_nbits(qpwr); + tmp1 = prod_msb - 20; + + if (tmp1 >= 0) { + tmp3 = ((prod << (30 - prod_msb)) + (ipwr >> (1 + tmp1))) / + (ipwr >> tmp1); + } else { + tmp3 = ((prod << (30 - prod_msb)) + (ipwr << (-1 - tmp1))) / + (ipwr << -tmp1); + } + + tmp2 = q_msb - 11; + + if (tmp2 >= 0) + tmp4 = (qpwr << (31 - q_msb)) / (ipwr >> tmp2); + else + tmp4 = (qpwr << (31 - q_msb)) / (ipwr << -tmp2); + + tmp4 -= tmp3 * tmp3; + tmp4 = -int_sqrt(tmp4); + + c0 = tmp3 >> 3; + c1 = tmp4 >> 4; + +out: + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, c1); + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, c0 << 8); + return ret; +} + +/* Complex number using 2 32-bit signed integers */ +typedef struct {s32 i, q;} lpphy_c32; + +static lpphy_c32 lpphy_cordic(int theta) +{ + u32 arctg[] = { 2949120, 1740967, 919879, 466945, 234379, 117304, + 58666, 29335, 14668, 7334, 3667, 1833, 917, 458, + 229, 115, 57, 29, }; + int i, tmp, signx = 1, angle = 0; + lpphy_c32 ret = { .i = 39797, .q = 0, }; + + theta = clamp_t(int, theta, -180, 180); + + if (theta > 90) { + theta -= 180; + signx = -1; + } else if (theta < -90) { + theta += 180; + signx = -1; + } + + for (i = 0; i <= 17; i++) { + if (theta > angle) { + tmp = ret.i - (ret.q >> i); + ret.q += ret.i >> i; + ret.i = tmp; + angle += arctg[i]; + } else { + tmp = ret.i + (ret.q >> i); + ret.q -= ret.i >> i; + ret.i = tmp; + angle -= arctg[i]; + } + } + + ret.i *= signx; + ret.q *= signx; + + return ret; +} + +static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops, + u16 wait) +{ + b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, + 0xFFC0, samples - 1); + if (loops != 0xFFFF) + loops--; + b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000, loops); + b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0x3F, wait << 6); + b43_phy_set(dev, B43_LPPHY_A_PHY_CTL_ADDR, 0x1); +} + +//SPEC FIXME what does a negative freq mean? +static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 buf[64]; + int i, samples = 0, angle = 0, rotation = (9 * freq) / 500; + lpphy_c32 sample; + + lpphy->tx_tone_freq = freq; + + if (freq) { + /* Find i for which abs(freq) integrally divides 20000 * i */ + for (i = 1; samples * abs(freq) != 20000 * i; i++) { + samples = (20000 * i) / abs(freq); + if(B43_WARN_ON(samples > 63)) + return; + } + } else { + samples = 2; + } + + for (i = 0; i < samples; i++) { + sample = lpphy_cordic(angle); + angle += rotation; + buf[i] = ((sample.i * max) & 0xFF) << 8; + buf[i] |= (sample.q * max) & 0xFF; + } + + b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf); + + lpphy_run_samples(dev, samples, 0xFFFF, 0); +} + +static void lpphy_stop_tx_tone(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + int i; + + lpphy->tx_tone_freq = 0; + + b43_phy_mask(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000); + for (i = 0; i < 31; i++) { + if (!(b43_phy_read(dev, B43_LPPHY_A_PHY_CTL_ADDR) & 0x1)) + break; + udelay(100); + } +} + + +static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains, + int mode, bool useindex, u8 index) +{ + //TODO +} + +static void lpphy_papd_cal_txpwr(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct ssb_bus *bus = dev->dev->bus; + struct lpphy_tx_gains gains, oldgains; + int old_txpctl, old_afe_ovr, old_rf, old_bbmult; + + lpphy_read_tx_pctl_mode_from_hardware(dev); + old_txpctl = lpphy->txpctl_mode; + old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40; + if (old_afe_ovr) + oldgains = lpphy_get_tx_gains(dev); + old_rf = b43_phy_read(dev, B43_LPPHY_RF_PWR_OVERRIDE) & 0xFF; + old_bbmult = lpphy_get_bb_mult(dev); + + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); + + if (bus->chip_id == 0x4325 && bus->chip_rev == 0) + lpphy_papd_cal(dev, gains, 0, 1, 30); + else + lpphy_papd_cal(dev, gains, 0, 1, 65); + + if (old_afe_ovr) + lpphy_set_tx_gains(dev, oldgains); + lpphy_set_bb_mult(dev, old_bbmult); + lpphy_set_tx_power_control(dev, old_txpctl); + b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, old_rf); +} + +static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx, + bool rx, bool pa, struct lpphy_tx_gains *gains) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct ssb_bus *bus = dev->dev->bus; + const struct lpphy_rx_iq_comp *iqcomp = NULL; + struct lpphy_tx_gains nogains, oldgains; + u16 tmp; + int i, ret; + + memset(&nogains, 0, sizeof(nogains)); + memset(&oldgains, 0, sizeof(oldgains)); + + if (bus->chip_id == 0x5354) { + for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) { + if (lpphy_5354_iq_table[i].chan == lpphy->channel) { + iqcomp = &lpphy_5354_iq_table[i]; + } + } + } else if (dev->phy.rev >= 2) { + iqcomp = &lpphy_rev2plus_iq_comp; + } else { + for (i = 0; i < ARRAY_SIZE(lpphy_rev0_1_iq_table); i++) { + if (lpphy_rev0_1_iq_table[i].chan == lpphy->channel) { + iqcomp = &lpphy_rev0_1_iq_table[i]; + } + } + } + + if (B43_WARN_ON(!iqcomp)) + return 0; + + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, iqcomp->c1); + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, + 0x00FF, iqcomp->c0 << 8); + + if (noise) { + tx = true; + rx = false; + pa = false; + } + + lpphy_set_trsw_over(dev, tx, rx); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, + 0xFFF7, pa << 3); + } else { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, + 0xFFDF, pa << 5); + } + + tmp = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40; + + if (noise) + lpphy_set_rx_gain(dev, 0x2D5D); + else { + if (tmp) + oldgains = lpphy_get_tx_gains(dev); + if (!gains) + gains = &nogains; + lpphy_set_tx_gains(dev, *gains); + } + + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800); + lpphy_set_deaf(dev, false); + if (noise) + ret = lpphy_calc_rx_iq_comp(dev, 0xFFF0); + else { + lpphy_start_tx_tone(dev, 4000, 100); + ret = lpphy_calc_rx_iq_comp(dev, 0x4000); + lpphy_stop_tx_tone(dev); + } + lpphy_clear_deaf(dev, false); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFC); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFF7); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFDF); + if (!noise) { + if (tmp) + lpphy_set_tx_gains(dev, oldgains); + else + lpphy_disable_tx_gain_override(dev); + } + lpphy_disable_rx_gain_override(dev); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xF7FF); + return ret; +} + +static void lpphy_calibration(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + enum b43_lpphy_txpctl_mode saved_pctl_mode; + bool full_cal = false; + + if (lpphy->full_calib_chan != lpphy->channel) { + full_cal = true; + lpphy->full_calib_chan = lpphy->channel; + } + + b43_mac_suspend(dev); + + lpphy_btcoex_override(dev); + if (dev->phy.rev >= 2) + lpphy_save_dig_flt_state(dev); + lpphy_read_tx_pctl_mode_from_hardware(dev); + saved_pctl_mode = lpphy->txpctl_mode; + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); + //TODO Perform transmit power table I/Q LO calibration + if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF)) + lpphy_pr41573_workaround(dev); + if ((dev->phy.rev >= 2) && full_cal) { + lpphy_papd_cal_txpwr(dev); + } + lpphy_set_tx_power_control(dev, saved_pctl_mode); + if (dev->phy.rev >= 2) + lpphy_restore_dig_flt_state(dev); + lpphy_rx_iq_cal(dev, true, true, false, false, NULL); + + b43_mac_enable(dev); +} + static u16 b43_lpphy_op_read(struct b43_wldev *dev, u16 reg) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); @@ -1533,12 +2081,6 @@ static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); } -static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev, - bool blocked) -{ - //TODO -} - struct b206x_channel { u8 channel; u16 freq; @@ -2004,22 +2546,6 @@ static int lpphy_b2062_tune(struct b43_wldev *dev, return err; } - -/* This was previously called lpphy_japan_filter */ -static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter! - - if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific? - b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9); - if ((dev->phy.rev == 1) && (lpphy->rc_cap)) - lpphy_set_rc_cap(dev); - } else { - b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F); - } -} - static void lpphy_b2063_vco_calib(struct b43_wldev *dev) { u16 tmp; @@ -2204,18 +2730,6 @@ static int b43_lpphy_op_init(struct b43_wldev *dev) return 0; } -static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) -{ - if (dev->phy.rev >= 2) - return; // rev2+ doesn't support antenna diversity - - if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1)) - return; - - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2); - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1); -} - static void b43_lpphy_op_adjust_txpower(struct b43_wldev *dev) { //TODO @@ -2238,6 +2752,11 @@ void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on) } } +static void b43_lpphy_op_pwork_15sec(struct b43_wldev *dev) +{ + //TODO +} + const struct b43_phy_operations b43_phyops_lp = { .allocate = b43_lpphy_op_allocate, .free = b43_lpphy_op_free, @@ -2255,4 +2774,6 @@ const struct b43_phy_operations b43_phyops_lp = { .set_rx_antenna = b43_lpphy_op_set_rx_antenna, .recalc_txpower = b43_lpphy_op_recalc_txpower, .adjust_txpower = b43_lpphy_op_adjust_txpower, + .pwork_15sec = b43_lpphy_op_pwork_15sec, + .pwork_60sec = lpphy_calibration, }; diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/b43/phy_lp.h index c3232c17b60a..62737f700cbc 100644 --- a/drivers/net/wireless/b43/phy_lp.h +++ b/drivers/net/wireless/b43/phy_lp.h @@ -286,6 +286,7 @@ #define B43_LPPHY_TR_LOOKUP_6 B43_PHY_OFDM(0xC8) /* TR Lookup 6 */ #define B43_LPPHY_TR_LOOKUP_7 B43_PHY_OFDM(0xC9) /* TR Lookup 7 */ #define B43_LPPHY_TR_LOOKUP_8 B43_PHY_OFDM(0xCA) /* TR Lookup 8 */ +#define B43_LPPHY_RF_PWR_OVERRIDE B43_PHY_OFDM(0xD3) /* RF power override */ @@ -871,12 +872,12 @@ struct b43_phy_lp { u8 rssi_gs; /* RC cap */ - u8 rc_cap; /* FIXME initial value? */ + u8 rc_cap; /* BX arch */ u8 bx_arch; /* Full calibration channel */ - u8 full_calib_chan; /* FIXME initial value? */ + u8 full_calib_chan; /* Transmit iqlocal best coeffs */ bool tx_iqloc_best_coeffs_valid; @@ -891,6 +892,12 @@ struct b43_phy_lp { /* The channel we are tuned to */ u8 channel; + + /* The active antenna diversity mode */ + int antenna; + + /* Frequency of the active TX tone */ + int tx_tone_freq; }; enum tssi_mux_mode { diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index 9b9044400218..c01b8e02412f 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -342,12 +342,15 @@ static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q, q->mmio_base + B43_PIO_TXDATA, sizeof(u16)); if (data_len & 1) { + u8 *tail = wl->pio_tailspace; + BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2); + /* Write the last byte. */ ctl &= ~B43_PIO_TXCTL_WRITEHI; b43_piotx_write16(q, B43_PIO_TXCTL, ctl); - wl->tx_tail[0] = data[data_len - 1]; - wl->tx_tail[1] = 0; - ssb_block_write(dev->dev, wl->tx_tail, 2, + tail[0] = data[data_len - 1]; + tail[1] = 0; + ssb_block_write(dev->dev, tail, 2, q->mmio_base + B43_PIO_TXDATA, sizeof(u16)); } @@ -393,31 +396,31 @@ static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q, q->mmio_base + B43_PIO8_TXDATA, sizeof(u32)); if (data_len & 3) { - wl->tx_tail[3] = 0; + u8 *tail = wl->pio_tailspace; + BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4); + + memset(tail, 0, 4); /* Write the last few bytes. */ ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31); switch (data_len & 3) { case 3: ctl |= B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_8_15; - wl->tx_tail[0] = data[data_len - 3]; - wl->tx_tail[1] = data[data_len - 2]; - wl->tx_tail[2] = data[data_len - 1]; + tail[0] = data[data_len - 3]; + tail[1] = data[data_len - 2]; + tail[2] = data[data_len - 1]; break; case 2: ctl |= B43_PIO8_TXCTL_8_15; - wl->tx_tail[0] = data[data_len - 2]; - wl->tx_tail[1] = data[data_len - 1]; - wl->tx_tail[2] = 0; + tail[0] = data[data_len - 2]; + tail[1] = data[data_len - 1]; break; case 1: - wl->tx_tail[0] = data[data_len - 1]; - wl->tx_tail[1] = 0; - wl->tx_tail[2] = 0; + tail[0] = data[data_len - 1]; break; } b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); - ssb_block_write(dev->dev, wl->tx_tail, 4, + ssb_block_write(dev->dev, tail, 4, q->mmio_base + B43_PIO8_TXDATA, sizeof(u32)); } @@ -456,6 +459,7 @@ static int pio_tx_frame(struct b43_pio_txqueue *q, int err; unsigned int hdrlen; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct b43_txhdr *txhdr = (struct b43_txhdr *)wl->pio_scratchspace; B43_WARN_ON(list_empty(&q->packets_list)); pack = list_entry(q->packets_list.next, @@ -463,7 +467,9 @@ static int pio_tx_frame(struct b43_pio_txqueue *q, cookie = generate_cookie(q, pack); hdrlen = b43_txhdr_size(dev); - err = b43_generate_txhdr(dev, (u8 *)&wl->txhdr, skb, + BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(struct b43_txhdr)); + B43_WARN_ON(sizeof(wl->pio_scratchspace) < hdrlen); + err = b43_generate_txhdr(dev, (u8 *)txhdr, skb, info, cookie); if (err) return err; @@ -477,9 +483,9 @@ static int pio_tx_frame(struct b43_pio_txqueue *q, pack->skb = skb; if (q->rev >= 8) - pio_tx_frame_4byte_queue(pack, (const u8 *)&wl->txhdr, hdrlen); + pio_tx_frame_4byte_queue(pack, (const u8 *)txhdr, hdrlen); else - pio_tx_frame_2byte_queue(pack, (const u8 *)&wl->txhdr, hdrlen); + pio_tx_frame_2byte_queue(pack, (const u8 *)txhdr, hdrlen); /* Remove it from the list of available packet slots. * It will be put back when we receive the status report. */ @@ -625,8 +631,11 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q) unsigned int i, padding; struct sk_buff *skb; const char *err_msg = NULL; + struct b43_rxhdr_fw4 *rxhdr = + (struct b43_rxhdr_fw4 *)wl->pio_scratchspace; - memset(&wl->rxhdr, 0, sizeof(wl->rxhdr)); + BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(*rxhdr)); + memset(rxhdr, 0, sizeof(*rxhdr)); /* Check if we have data and wait for it to get ready. */ if (q->rev >= 8) { @@ -664,16 +673,16 @@ data_ready: /* Get the preamble (RX header) */ if (q->rev >= 8) { - ssb_block_read(dev->dev, &wl->rxhdr, sizeof(wl->rxhdr), + ssb_block_read(dev->dev, rxhdr, sizeof(*rxhdr), q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); } else { - ssb_block_read(dev->dev, &wl->rxhdr, sizeof(wl->rxhdr), + ssb_block_read(dev->dev, rxhdr, sizeof(*rxhdr), q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); } /* Sanity checks. */ - len = le16_to_cpu(wl->rxhdr.frame_len); + len = le16_to_cpu(rxhdr->frame_len); if (unlikely(len > 0x700)) { err_msg = "len > 0x700"; goto rx_error; @@ -683,7 +692,7 @@ data_ready: goto rx_error; } - macstat = le32_to_cpu(wl->rxhdr.mac_status); + macstat = le32_to_cpu(rxhdr->mac_status); if (macstat & B43_RX_MAC_FCSERR) { if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) { /* Drop frames with failed FCS. */ @@ -708,22 +717,25 @@ data_ready: q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); if (len & 3) { + u8 *tail = wl->pio_tailspace; + BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4); + /* Read the last few bytes. */ - ssb_block_read(dev->dev, wl->rx_tail, 4, + ssb_block_read(dev->dev, tail, 4, q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); switch (len & 3) { case 3: - skb->data[len + padding - 3] = wl->rx_tail[0]; - skb->data[len + padding - 2] = wl->rx_tail[1]; - skb->data[len + padding - 1] = wl->rx_tail[2]; + skb->data[len + padding - 3] = tail[0]; + skb->data[len + padding - 2] = tail[1]; + skb->data[len + padding - 1] = tail[2]; break; case 2: - skb->data[len + padding - 2] = wl->rx_tail[0]; - skb->data[len + padding - 1] = wl->rx_tail[1]; + skb->data[len + padding - 2] = tail[0]; + skb->data[len + padding - 1] = tail[1]; break; case 1: - skb->data[len + padding - 1] = wl->rx_tail[0]; + skb->data[len + padding - 1] = tail[0]; break; } } @@ -732,22 +744,29 @@ data_ready: q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); if (len & 1) { + u8 *tail = wl->pio_tailspace; + BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2); + /* Read the last byte. */ - ssb_block_read(dev->dev, wl->rx_tail, 2, + ssb_block_read(dev->dev, tail, 2, q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); - skb->data[len + padding - 1] = wl->rx_tail[0]; + skb->data[len + padding - 1] = tail[0]; } } - b43_rx(q->dev, skb, &wl->rxhdr); + b43_rx(q->dev, skb, rxhdr); return 1; rx_error: if (err_msg) b43dbg(q->dev->wl, "PIO RX error: %s\n", err_msg); - b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY); + if (q->rev >= 8) + b43_piorx_write32(q, B43_PIO8_RXCTL, B43_PIO8_RXCTL_DATARDY); + else + b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY); + return 1; } diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c index ffdce6f3c909..78016ae21c50 100644 --- a/drivers/net/wireless/b43/rfkill.c +++ b/drivers/net/wireless/b43/rfkill.c @@ -33,8 +33,14 @@ bool b43_is_hw_radio_enabled(struct b43_wldev *dev) & B43_MMIO_RADIO_HWENABLED_HI_MASK)) return 1; } else { - if (b43_status(dev) >= B43_STAT_STARTED && - b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO) + /* To prevent CPU fault on PPC, do not read a register + * unless the interface is started; however, on resume + * for hibernation, this routine is entered early. When + * that happens, unconditionally return TRUE. + */ + if (b43_status(dev) < B43_STAT_STARTED) + return 1; + if (b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO) & B43_MMIO_RADIO_HWENABLED_LO_MASK) return 1; } diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index f4e9695ec186..eda06529ef5f 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -27,7 +27,7 @@ */ -#include "b43.h" +#include "xmit.h" #include "phy_common.h" #include "dma.h" #include "pio.h" @@ -621,7 +621,6 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) (phystat0 & B43_RX_PHYST0_OFDM), (phystat0 & B43_RX_PHYST0_GAINCTL), (phystat3 & B43_RX_PHYST3_TRSTATE)); - status.qual = (rxhdr->jssi * 100) / B43_RX_MAX_SSI; } if (phystat0 & B43_RX_PHYST0_OFDM) @@ -690,10 +689,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) } memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - - local_bh_disable(); - ieee80211_rx(dev->wl->hw, skb); - local_bh_enable(); + ieee80211_rx_ni(dev->wl->hw, skb); #if B43_DEBUG dev->rx_count++; diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h index 3530de871873..d23ff9fe0c9e 100644 --- a/drivers/net/wireless/b43/xmit.h +++ b/drivers/net/wireless/b43/xmit.h @@ -2,6 +2,8 @@ #define B43_XMIT_H_ #include "main.h" +#include <net/mac80211.h> + #define _b43_declare_plcp_hdr(size) \ struct b43_plcp_hdr##size { \ @@ -332,4 +334,21 @@ static inline u8 b43_kidx_to_raw(struct b43_wldev *dev, u8 firmware_kidx) return raw_kidx; } +/* struct b43_private_tx_info - TX info private to b43. + * The structure is placed in (struct ieee80211_tx_info *)->rate_driver_data + * + * @bouncebuffer: DMA Bouncebuffer (if used) + */ +struct b43_private_tx_info { + void *bouncebuffer; +}; + +static inline struct b43_private_tx_info * +b43_get_priv_tx_info(struct ieee80211_tx_info *info) +{ + BUILD_BUG_ON(sizeof(struct b43_private_tx_info) > + sizeof(info->rate_driver_data)); + return (struct b43_private_tx_info *)info->rate_driver_data; +} + #endif /* B43_XMIT_H_ */ diff --git a/drivers/net/wireless/b43legacy/Kconfig b/drivers/net/wireless/b43legacy/Kconfig index 94a463478053..1ffa28835c58 100644 --- a/drivers/net/wireless/b43legacy/Kconfig +++ b/drivers/net/wireless/b43legacy/Kconfig @@ -1,6 +1,6 @@ config B43LEGACY tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)" - depends on SSB_POSSIBLE && MAC80211 && WLAN_80211 && HAS_DMA + depends on SSB_POSSIBLE && MAC80211 && HAS_DMA select SSB select FW_LOADER ---help--- diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h index 038baa8869e2..89fe2f972c72 100644 --- a/drivers/net/wireless/b43legacy/b43legacy.h +++ b/drivers/net/wireless/b43legacy/b43legacy.h @@ -29,8 +29,6 @@ #define B43legacy_IRQWAIT_MAX_RETRIES 20 -#define B43legacy_RX_MAX_SSI 60 /* best guess at max ssi */ - /* MMIO offsets */ #define B43legacy_MMIO_DMA0_REASON 0x20 #define B43legacy_MMIO_DMA0_IRQ_MASK 0x24 diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index 866403415811..0a86bdf53154 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -1240,8 +1240,9 @@ struct b43legacy_dmaring *parse_cookie(struct b43legacy_wldev *dev, } static int dma_tx_fragment(struct b43legacy_dmaring *ring, - struct sk_buff *skb) + struct sk_buff **in_skb) { + struct sk_buff *skb = *in_skb; const struct b43legacy_dma_ops *ops = ring->ops; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u8 *header; @@ -1305,8 +1306,14 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring, } memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); + memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); + bounce_skb->dev = skb->dev; + skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); + info = IEEE80211_SKB_CB(bounce_skb); + dev_kfree_skb_any(skb); skb = bounce_skb; + *in_skb = bounce_skb; meta->skb = skb; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { @@ -1360,8 +1367,10 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, struct sk_buff *skb) { struct b43legacy_dmaring *ring; + struct ieee80211_hdr *hdr; int err = 0; unsigned long flags; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); spin_lock_irqsave(&ring->lock, flags); @@ -1386,7 +1395,11 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, goto out_unlock; } - err = dma_tx_fragment(ring, skb); + /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing + * into the skb data or cb now. */ + hdr = NULL; + info = NULL; + err = dma_tx_fragment(ring, &skb); if (unlikely(err == -ENOKEY)) { /* Drop this packet, as we don't have the encryption key * anymore and must not transmit it unencrypted. */ diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 4b60148a5e61..4a905b6a886b 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2277,7 +2277,7 @@ static void do_periodic_work(struct b43legacy_wldev *dev) /* Periodic work locking policy: * The whole periodic work handler is protected by * wl->mutex. If another lock is needed somewhere in the - * pwork callchain, it's aquired in-place, where it's needed. + * pwork callchain, it's acquired in-place, where it's needed. */ static void b43legacy_periodic_work_handler(struct work_struct *work) { @@ -2677,7 +2677,7 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw, if (conf->channel->hw_value != phy->channel) b43legacy_radio_selectchannel(dev, conf->channel->hw_value, 0); - dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_RADIOTAP); + dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); /* Adjust the desired TX power level. */ if (conf->power_level != 0) { @@ -3593,7 +3593,7 @@ static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; struct ssb_bus *bus = dev->dev->bus; - struct pci_dev *pdev = bus->host_pci; + struct pci_dev *pdev = (bus->bustype == SSB_BUSTYPE_PCI) ? bus->host_pci : NULL; int err; int have_bphy = 0; int have_gphy = 0; @@ -3707,7 +3707,7 @@ static int b43legacy_one_core_attach(struct ssb_device *dev, if (!list_empty(&wl->devlist)) { /* We are not the first core on this chip. */ - pdev = dev->bus->host_pci; + pdev = (dev->bus->bustype == SSB_BUSTYPE_PCI) ? dev->bus->host_pci : NULL; /* Only special chips support more than one wireless * core, although some of the other chips have more than * one wireless core as well. Check for this and diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/b43legacy/rfkill.c index 8783022db11e..d579df72b783 100644 --- a/drivers/net/wireless/b43legacy/rfkill.c +++ b/drivers/net/wireless/b43legacy/rfkill.c @@ -34,6 +34,13 @@ bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev) & B43legacy_MMIO_RADIO_HWENABLED_HI_MASK)) return 1; } else { + /* To prevent CPU fault on PPC, do not read a register + * unless the interface is started; however, on resume + * for hibernation, this routine is entered early. When + * that happens, unconditionally return TRUE. + */ + if (b43legacy_status(dev) < B43legacy_STAT_STARTED) + return 1; if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO) & B43legacy_MMIO_RADIO_HWENABLED_LO_MASK) return 1; diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index 103f3c9e7f58..9c8882d9275e 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -549,7 +549,6 @@ void b43legacy_rx(struct b43legacy_wldev *dev, (phystat0 & B43legacy_RX_PHYST0_GAINCTL), (phystat3 & B43legacy_RX_PHYST3_TRSTATE)); status.noise = dev->stats.link_noise; - status.qual = (jssi * 100) / B43legacy_RX_MAX_SSI; /* change to support A PHY */ if (phystat0 & B43legacy_RX_PHYST0_OFDM) status.rate_idx = b43legacy_plcp_get_bitrate_idx_ofdm(plcp, false); diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig index c15db2293515..287d82728bc3 100644 --- a/drivers/net/wireless/hostap/Kconfig +++ b/drivers/net/wireless/hostap/Kconfig @@ -1,7 +1,8 @@ config HOSTAP tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)" - depends on WLAN_80211 select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV select CRYPTO select CRYPTO_ARC4 select CRYPTO_ECB diff --git a/drivers/net/wireless/i82593.h b/drivers/net/wireless/i82593.h deleted file mode 100644 index afac5c7a323d..000000000000 --- a/drivers/net/wireless/i82593.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Definitions for Intel 82593 CSMA/CD Core LAN Controller - * The definitions are taken from the 1992 users manual with Intel - * order number 297125-001. - * - * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp - * - * Copyright 1994, Anders Klemets <klemets@it.kth.se> - * - * HISTORY - * i82593.h,v - * Revision 1.4 2005/11/4 09:15:00 baroniunas - * Modified copyright with permission of author as follows: - * - * "If I82539.H is the only file with my copyright statement - * that is included in the Source Forge project, then you have - * my approval to change the copyright statement to be a GPL - * license, in the way you proposed on October 10." - * - * Revision 1.1 1996/07/17 15:23:12 root - * Initial revision - * - * Revision 1.3 1995/04/05 15:13:58 adj - * Initial alpha release - * - * Revision 1.2 1994/06/16 23:57:31 klemets - * Mirrored all the fields in the configuration block. - * - * Revision 1.1 1994/06/02 20:25:34 klemets - * Initial revision - * - * - */ -#ifndef _I82593_H -#define _I82593_H - -/* Intel 82593 CSMA/CD Core LAN Controller */ - -/* Port 0 Command Register definitions */ - -/* Execution operations */ -#define OP0_NOP 0 /* CHNL = 0 */ -#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */ -#define OP0_IA_SETUP 1 -#define OP0_CONFIGURE 2 -#define OP0_MC_SETUP 3 -#define OP0_TRANSMIT 4 -#define OP0_TDR 5 -#define OP0_DUMP 6 -#define OP0_DIAGNOSE 7 -#define OP0_TRANSMIT_NO_CRC 9 -#define OP0_RETRANSMIT 12 -#define OP0_ABORT 13 -/* Reception operations */ -#define OP0_RCV_ENABLE 8 -#define OP0_RCV_DISABLE 10 -#define OP0_STOP_RCV 11 -/* Status pointer control operations */ -#define OP0_FIX_PTR 15 /* CHNL = 1 */ -#define OP0_RLS_PTR 15 /* CHNL = 0 */ -#define OP0_RESET 14 - -#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */ -#define CR0_STATUS_0 0x00 -#define CR0_STATUS_1 0x20 -#define CR0_STATUS_2 0x40 -#define CR0_STATUS_3 0x60 -#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */ - -/* Port 0 Status Register definitions */ - -#define SR0_NO_RESULT 0 /* dummy */ -#define SR0_EVENT_MASK 0x0f -#define SR0_IA_SETUP_DONE 1 -#define SR0_CONFIGURE_DONE 2 -#define SR0_MC_SETUP_DONE 3 -#define SR0_TRANSMIT_DONE 4 -#define SR0_TDR_DONE 5 -#define SR0_DUMP_DONE 6 -#define SR0_DIAGNOSE_PASSED 7 -#define SR0_TRANSMIT_NO_CRC_DONE 9 -#define SR0_RETRANSMIT_DONE 12 -#define SR0_EXECUTION_ABORTED 13 -#define SR0_END_OF_FRAME 8 -#define SR0_RECEPTION_ABORTED 10 -#define SR0_DIAGNOSE_FAILED 15 -#define SR0_STOP_REG_HIT 11 - -#define SR0_CHNL (1 << 4) -#define SR0_EXECUTION (1 << 5) -#define SR0_RECEPTION (1 << 6) -#define SR0_INTERRUPT (1 << 7) -#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION) - -#define SR3_EXEC_STATE_MASK 0x03 -#define SR3_EXEC_IDLE 0 -#define SR3_TX_ABORT_IN_PROGRESS 1 -#define SR3_EXEC_ACTIVE 2 -#define SR3_ABORT_IN_PROGRESS 3 -#define SR3_EXEC_CHNL (1 << 2) -#define SR3_STP_ON_NO_RSRC (1 << 3) -#define SR3_RCVING_NO_RSRC (1 << 4) -#define SR3_RCV_STATE_MASK 0x60 -#define SR3_RCV_IDLE 0x00 -#define SR3_RCV_READY 0x20 -#define SR3_RCV_ACTIVE 0x40 -#define SR3_RCV_STOP_IN_PROG 0x60 -#define SR3_RCV_CHNL (1 << 7) - -/* Port 1 Command Register definitions */ - -#define OP1_NOP 0 -#define OP1_SWIT_TO_PORT_0 1 -#define OP1_INT_DISABLE 2 -#define OP1_INT_ENABLE 3 -#define OP1_SET_TS 5 -#define OP1_RST_TS 7 -#define OP1_POWER_DOWN 8 -#define OP1_RESET_RING_MNGMT 11 -#define OP1_RESET 14 -#define OP1_SEL_RST 15 - -#define CR1_STATUS_4 0x00 -#define CR1_STATUS_5 0x20 -#define CR1_STATUS_6 0x40 -#define CR1_STOP_REG_UPDATE (1 << 7) - -/* Receive frame status bits */ - -#define RX_RCLD (1 << 0) -#define RX_IA_MATCH (1 << 1) -#define RX_NO_AD_MATCH (1 << 2) -#define RX_NO_SFD (1 << 3) -#define RX_SRT_FRM (1 << 7) -#define RX_OVRRUN (1 << 8) -#define RX_ALG_ERR (1 << 10) -#define RX_CRC_ERR (1 << 11) -#define RX_LEN_ERR (1 << 12) -#define RX_RCV_OK (1 << 13) -#define RX_TYP_LEN (1 << 15) - -/* Transmit status bits */ - -#define TX_NCOL_MASK 0x0f -#define TX_FRTL (1 << 4) -#define TX_MAX_COL (1 << 5) -#define TX_HRT_BEAT (1 << 6) -#define TX_DEFER (1 << 7) -#define TX_UND_RUN (1 << 8) -#define TX_LOST_CTS (1 << 9) -#define TX_LOST_CRS (1 << 10) -#define TX_LTCOL (1 << 11) -#define TX_OK (1 << 13) -#define TX_COLL (1 << 15) - -struct i82593_conf_block { - u_char fifo_limit : 4, - forgnesi : 1, - fifo_32 : 1, - d6mod : 1, - throttle_enb : 1; - u_char throttle : 6, - cntrxint : 1, - contin : 1; - u_char addr_len : 3, - acloc : 1, - preamb_len : 2, - loopback : 2; - u_char lin_prio : 3, - tbofstop : 1, - exp_prio : 3, - bof_met : 1; - u_char : 4, - ifrm_spc : 4; - u_char : 5, - slottim_low : 3; - u_char slottim_hi : 3, - : 1, - max_retr : 4; - u_char prmisc : 1, - bc_dis : 1, - : 1, - crs_1 : 1, - nocrc_ins : 1, - crc_1632 : 1, - : 1, - crs_cdt : 1; - u_char cs_filter : 3, - crs_src : 1, - cd_filter : 3, - : 1; - u_char : 2, - min_fr_len : 6; - u_char lng_typ : 1, - lng_fld : 1, - rxcrc_xf : 1, - artx : 1, - sarec : 1, - tx_jabber : 1, /* why is this called max_len in the manual? */ - hash_1 : 1, - lbpkpol : 1; - u_char : 6, - fdx : 1, - : 1; - u_char dummy_6 : 6, /* supposed to be ones */ - mult_ia : 1, - dis_bof : 1; - u_char dummy_1 : 1, /* supposed to be one */ - tx_ifs_retrig : 2, - mc_all : 1, - rcv_mon : 2, - frag_acpt : 1, - tstrttrs : 1; - u_char fretx : 1, - runt_eop : 1, - hw_sw_pin : 1, - big_endn : 1, - syncrqs : 1, - sttlen : 1, - tx_eop : 1, - rx_eop : 1; - u_char rbuf_size : 5, - rcvstop : 1, - : 2; -}; - -#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ - -#endif /* _I82593_H */ diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/ipw2x00/Kconfig index a8131384c6b9..2715b101aded 100644 --- a/drivers/net/wireless/ipw2x00/Kconfig +++ b/drivers/net/wireless/ipw2x00/Kconfig @@ -4,8 +4,10 @@ config IPW2100 tristate "Intel PRO/Wireless 2100 Network Connection" - depends on PCI && WLAN_80211 && CFG80211 + depends on PCI && CFG80211 select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV select FW_LOADER select LIB80211 select LIBIPW @@ -63,8 +65,10 @@ config IPW2100_DEBUG config IPW2200 tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" - depends on PCI && WLAN_80211 && CFG80211 + depends on PCI && CFG80211 && CFG80211_WEXT select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV select FW_LOADER select LIB80211 select LIBIPW @@ -150,8 +154,9 @@ config IPW2200_DEBUG config LIBIPW tristate - depends on PCI && WLAN_80211 && CFG80211 + depends on PCI && CFG80211 select WIRELESS_EXT + select WEXT_SPY select CRYPTO select CRYPTO_ARC4 select CRYPTO_ECB diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 6e2fc0cb6f8a..56afcf041f81 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -296,6 +296,33 @@ static const char *command_types[] = { }; #endif +#define WEXT_USECHANNELS 1 + +static const long ipw2100_frequencies[] = { + 2412, 2417, 2422, 2427, + 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, + 2472, 2484 +}; + +#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies) + +static const long ipw2100_rates_11b[] = { + 1000000, + 2000000, + 5500000, + 11000000 +}; + +static struct ieee80211_rate ipw2100_bg_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, +}; + +#define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b) + /* Pre-decl until we get the code solid and then we can clean it up */ static void ipw2100_tx_send_commands(struct ipw2100_priv *priv); static void ipw2100_tx_send_data(struct ipw2100_priv *priv); @@ -551,7 +578,7 @@ static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord, /* get number of entries */ field_count = *(((u16 *) & field_info) + 1); - /* abort if no enought memory */ + /* abort if no enough memory */ total_length = field_len * field_count; if (total_length > *len) { *len = total_length; @@ -1141,6 +1168,7 @@ static int rf_kill_active(struct ipw2100_priv *priv) int i; if (!(priv->hw_features & HW_FEATURE_RFKILL)) { + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); priv->status &= ~STATUS_RF_KILL_HW; return 0; } @@ -1151,10 +1179,13 @@ static int rf_kill_active(struct ipw2100_priv *priv) value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1); } - if (value == 0) + if (value == 0) { + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); priv->status |= STATUS_RF_KILL_HW; - else + } else { + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); priv->status &= ~STATUS_RF_KILL_HW; + } return (value == 0); } @@ -1814,13 +1845,6 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) return rc; } -/* Called by register_netdev() */ -static int ipw2100_net_init(struct net_device *dev) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - return ipw2100_up(priv, 1); -} - static void ipw2100_down(struct ipw2100_priv *priv) { unsigned long flags; @@ -1875,6 +1899,64 @@ static void ipw2100_down(struct ipw2100_priv *priv) netif_stop_queue(priv->net_dev); } +/* Called by register_netdev() */ +static int ipw2100_net_init(struct net_device *dev) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + struct wireless_dev *wdev = &priv->ieee->wdev; + int ret; + int i; + + ret = ipw2100_up(priv, 1); + if (ret) + return ret; + + memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); + + /* fill-out priv->ieee->bg_band */ + if (geo->bg_channels) { + struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; + + bg_band->band = IEEE80211_BAND_2GHZ; + bg_band->n_channels = geo->bg_channels; + bg_band->channels = + kzalloc(geo->bg_channels * + sizeof(struct ieee80211_channel), GFP_KERNEL); + /* translate geo->bg to bg_band.channels */ + for (i = 0; i < geo->bg_channels; i++) { + bg_band->channels[i].band = IEEE80211_BAND_2GHZ; + bg_band->channels[i].center_freq = geo->bg[i].freq; + bg_band->channels[i].hw_value = geo->bg[i].channel; + bg_band->channels[i].max_power = geo->bg[i].max_power; + if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) + bg_band->channels[i].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) + bg_band->channels[i].flags |= + IEEE80211_CHAN_NO_IBSS; + if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) + bg_band->channels[i].flags |= + IEEE80211_CHAN_RADAR; + /* No equivalent for LIBIPW_CH_80211H_RULES, + LIBIPW_CH_UNIFORM_SPREADING, or + LIBIPW_CH_B_ONLY... */ + } + /* point at bitrate info */ + bg_band->bitrates = ipw2100_bg_rates; + bg_band->n_bitrates = RATE_COUNT; + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; + } + + set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); + if (wiphy_register(wdev->wiphy)) { + ipw2100_down(priv); + return -EIO; + } + return 0; +} + static void ipw2100_reset_adapter(struct work_struct *work) { struct ipw2100_priv *priv = @@ -2090,6 +2172,7 @@ static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status) priv->net_dev->name); /* RF_KILL is now enabled (else we wouldn't be here) */ + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); priv->status |= STATUS_RF_KILL_HW; /* Make sure the RF Kill check timer is running */ @@ -3044,7 +3127,7 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) IPW_MAX_BDS)) { /* TODO: Support merging buffers if more than * IPW_MAX_BDS are used */ - IPW_DEBUG_INFO("%s: Maximum BD theshold exceeded. " + IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded. " "Increase fragmentation level.\n", priv->net_dev->name); } @@ -6029,7 +6112,7 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, struct ipw2100_priv *priv; struct net_device *dev; - dev = alloc_ieee80211(sizeof(struct ipw2100_priv)); + dev = alloc_ieee80211(sizeof(struct ipw2100_priv), 0); if (!dev) return NULL; priv = libipw_priv(dev); @@ -6342,7 +6425,7 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); - free_ieee80211(dev); + free_ieee80211(dev, 0); pci_set_drvdata(pci_dev, NULL); } @@ -6400,7 +6483,10 @@ static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev) if (dev->base_addr) iounmap((void __iomem *)dev->base_addr); - free_ieee80211(dev); + /* wiphy_unregister needs to be here, before free_ieee80211 */ + wiphy_unregister(priv->ieee->wdev.wiphy); + kfree(priv->ieee->bg_band.channels); + free_ieee80211(dev, 0); } pci_release_regions(pci_dev); @@ -6487,6 +6573,16 @@ static int ipw2100_resume(struct pci_dev *pci_dev) } #endif +static void ipw2100_shutdown(struct pci_dev *pci_dev) +{ + struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); + + /* Take down the device; powers it off, etc. */ + ipw2100_down(priv); + + pci_disable_device(pci_dev); +} + #define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x } static struct pci_device_id ipw2100_pci_id_table[] __devinitdata = { @@ -6550,6 +6646,7 @@ static struct pci_driver ipw2100_pci_driver = { .suspend = ipw2100_suspend, .resume = ipw2100_resume, #endif + .shutdown = ipw2100_shutdown, }; /** @@ -6601,26 +6698,6 @@ static void __exit ipw2100_exit(void) module_init(ipw2100_init); module_exit(ipw2100_exit); -#define WEXT_USECHANNELS 1 - -static const long ipw2100_frequencies[] = { - 2412, 2417, 2422, 2427, - 2432, 2437, 2442, 2447, - 2452, 2457, 2462, 2467, - 2472, 2484 -}; - -#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies) - -static const long ipw2100_rates_11b[] = { - 1000000, - 2000000, - 5500000, - 11000000 -}; - -#define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b) - static int ipw2100_wx_get_name(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) @@ -6820,7 +6897,7 @@ static int ipw2100_wx_get_range(struct net_device *dev, range->max_qual.updated = 7; /* Updated all three */ range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */ - /* TODO: Find real 'good' to 'bad' threshol value for RSSI */ + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM; range->avg_qual.noise = 0; range->avg_qual.updated = 7; /* Updated all three */ @@ -8462,6 +8539,12 @@ static int ipw2100_get_firmware(struct ipw2100_priv *priv, return 0; } +MODULE_FIRMWARE(IPW2100_FW_NAME("-i")); +#ifdef CONFIG_IPW2100_MONITOR +MODULE_FIRMWARE(IPW2100_FW_NAME("-p")); +#endif +MODULE_FIRMWARE(IPW2100_FW_NAME("")); + static void ipw2100_release_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw) { diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index a6ca536e44f8..09ddd3e6bedc 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -81,6 +81,11 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("ipw2200-ibss.fw"); +#ifdef CONFIG_IPW2200_MONITOR +MODULE_FIRMWARE("ipw2200-sniffer.fw"); +#endif +MODULE_FIRMWARE("ipw2200-bss.fw"); static int cmdlog = 0; static int debug = 0; @@ -104,6 +109,25 @@ static int antenna = CFG_SYS_ANTENNA_BOTH; static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */ #endif +static struct ieee80211_rate ipw2200_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60 }, + { .bitrate = 90 }, + { .bitrate = 120 }, + { .bitrate = 180 }, + { .bitrate = 240 }, + { .bitrate = 360 }, + { .bitrate = 480 }, + { .bitrate = 540 } +}; + +#define ipw2200_a_rates (ipw2200_rates + 4) +#define ipw2200_num_a_rates 8 +#define ipw2200_bg_rates (ipw2200_rates + 0) +#define ipw2200_num_bg_rates 12 #ifdef CONFIG_IPW2200_QOS static int qos_enable = 0; @@ -768,7 +792,7 @@ static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len) /* get number of entries */ field_count = *(((u16 *) & field_info) + 1); - /* abort if not enought memory */ + /* abort if not enough memory */ total_len = field_len * field_count; if (total_len > *len) { *len = total_len; @@ -1734,10 +1758,13 @@ static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO, static int rf_kill_active(struct ipw_priv *priv) { - if (0 == (ipw_read32(priv, 0x30) & 0x10000)) + if (0 == (ipw_read32(priv, 0x30) & 0x10000)) { priv->status |= STATUS_RF_KILL_HW; - else + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); + } else { priv->status &= ~STATUS_RF_KILL_HW; + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); + } return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0; } @@ -2020,6 +2047,7 @@ static void ipw_irq_tasklet(struct ipw_priv *priv) if (inta & IPW_INTA_BIT_RF_KILL_DONE) { IPW_DEBUG_RF_KILL("RF_KILL_DONE\n"); priv->status |= STATUS_RF_KILL_HW; + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); wake_up_interruptible(&priv->wait_command_queue); priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); cancel_delayed_work(&priv->request_scan); @@ -7732,7 +7760,7 @@ static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv, case SEC_LEVEL_0: break; default: - printk(KERN_ERR "Unknow security level %d\n", + printk(KERN_ERR "Unknown security level %d\n", priv->ieee->sec.level); break; } @@ -8655,24 +8683,6 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) * */ -static int ipw_wx_get_name(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - if (priv->status & STATUS_RF_KILL_MASK) - strcpy(wrqu->name, "radio off"); - else if (!(priv->status & STATUS_ASSOCIATED)) - strcpy(wrqu->name, "unassociated"); - else - snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c", - ipw_modes[priv->assoc_request.ieee_mode]); - IPW_DEBUG_WX("Name: %s\n", wrqu->name); - mutex_unlock(&priv->mutex); - return 0; -} - static int ipw_set_channel(struct ipw_priv *priv, u8 channel) { if (channel == 0) { @@ -8916,7 +8926,7 @@ static int ipw_wx_get_range(struct net_device *dev, range->max_qual.updated = 7; /* Updated all three */ range->avg_qual.qual = 70; - /* TODO: Find real 'good' to 'bad' threshol value for RSSI */ + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ range->avg_qual.level = 0; /* FIXME to real average level */ range->avg_qual.noise = 0; range->avg_qual.updated = 7; /* Updated all three */ @@ -9972,7 +9982,7 @@ static int ipw_wx_sw_reset(struct net_device *dev, /* Rebase the WE IOCTLs to zero for the handler array */ #define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT] static iw_handler ipw_wx_handlers[] = { - IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name, + IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq, IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq, IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode, @@ -10289,7 +10299,7 @@ static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb, case SEC_LEVEL_0: break; default: - printk(KERN_ERR "Unknow security level %d\n", + printk(KERN_ERR "Unknown security level %d\n", priv->ieee->sec.level); break; } @@ -11275,6 +11285,7 @@ static int ipw_up(struct ipw_priv *priv) if (!(priv->config & CFG_CUSTOM_MAC)) eeprom_parse_mac(priv, priv->mac_addr); memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN); + memcpy(priv->net_dev->perm_addr, priv->mac_addr, ETH_ALEN); for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) { if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE], @@ -11416,16 +11427,100 @@ static void ipw_bg_down(struct work_struct *work) /* Called by register_netdev() */ static int ipw_net_init(struct net_device *dev) { + int i, rc = 0; struct ipw_priv *priv = libipw_priv(dev); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + struct wireless_dev *wdev = &priv->ieee->wdev; mutex_lock(&priv->mutex); if (ipw_up(priv)) { - mutex_unlock(&priv->mutex); - return -EIO; + rc = -EIO; + goto out; + } + + memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); + + /* fill-out priv->ieee->bg_band */ + if (geo->bg_channels) { + struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; + + bg_band->band = IEEE80211_BAND_2GHZ; + bg_band->n_channels = geo->bg_channels; + bg_band->channels = + kzalloc(geo->bg_channels * + sizeof(struct ieee80211_channel), GFP_KERNEL); + /* translate geo->bg to bg_band.channels */ + for (i = 0; i < geo->bg_channels; i++) { + bg_band->channels[i].band = IEEE80211_BAND_2GHZ; + bg_band->channels[i].center_freq = geo->bg[i].freq; + bg_band->channels[i].hw_value = geo->bg[i].channel; + bg_band->channels[i].max_power = geo->bg[i].max_power; + if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) + bg_band->channels[i].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) + bg_band->channels[i].flags |= + IEEE80211_CHAN_NO_IBSS; + if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) + bg_band->channels[i].flags |= + IEEE80211_CHAN_RADAR; + /* No equivalent for LIBIPW_CH_80211H_RULES, + LIBIPW_CH_UNIFORM_SPREADING, or + LIBIPW_CH_B_ONLY... */ + } + /* point at bitrate info */ + bg_band->bitrates = ipw2200_bg_rates; + bg_band->n_bitrates = ipw2200_num_bg_rates; + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; + } + + /* fill-out priv->ieee->a_band */ + if (geo->a_channels) { + struct ieee80211_supported_band *a_band = &priv->ieee->a_band; + + a_band->band = IEEE80211_BAND_5GHZ; + a_band->n_channels = geo->a_channels; + a_band->channels = + kzalloc(geo->a_channels * + sizeof(struct ieee80211_channel), GFP_KERNEL); + /* translate geo->bg to a_band.channels */ + for (i = 0; i < geo->a_channels; i++) { + a_band->channels[i].band = IEEE80211_BAND_2GHZ; + a_band->channels[i].center_freq = geo->a[i].freq; + a_band->channels[i].hw_value = geo->a[i].channel; + a_band->channels[i].max_power = geo->a[i].max_power; + if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) + a_band->channels[i].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + if (geo->a[i].flags & LIBIPW_CH_NO_IBSS) + a_band->channels[i].flags |= + IEEE80211_CHAN_NO_IBSS; + if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT) + a_band->channels[i].flags |= + IEEE80211_CHAN_RADAR; + /* No equivalent for LIBIPW_CH_80211H_RULES, + LIBIPW_CH_UNIFORM_SPREADING, or + LIBIPW_CH_B_ONLY... */ + } + /* point at bitrate info */ + a_band->bitrates = ipw2200_a_rates; + a_band->n_bitrates = ipw2200_num_a_rates; + + wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = a_band; + } + + set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); + + /* With that information in place, we can now register the wiphy... */ + if (wiphy_register(wdev->wiphy)) { + rc = -EIO; + goto out; } +out: mutex_unlock(&priv->mutex); - return 0; + return rc; } /* PCI driver stuff */ @@ -11556,7 +11651,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv) if (priv->prom_net_dev) return -EPERM; - priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv)); + priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv), 1); if (priv->prom_net_dev == NULL) return -ENOMEM; @@ -11575,7 +11670,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv) rc = register_netdev(priv->prom_net_dev); if (rc) { - free_ieee80211(priv->prom_net_dev); + free_ieee80211(priv->prom_net_dev, 1); priv->prom_net_dev = NULL; return rc; } @@ -11589,7 +11684,7 @@ static void ipw_prom_free(struct ipw_priv *priv) return; unregister_netdev(priv->prom_net_dev); - free_ieee80211(priv->prom_net_dev); + free_ieee80211(priv->prom_net_dev, 1); priv->prom_net_dev = NULL; } @@ -11617,7 +11712,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, struct ipw_priv *priv; int i; - net_dev = alloc_ieee80211(sizeof(struct ipw_priv)); + net_dev = alloc_ieee80211(sizeof(struct ipw_priv), 0); if (net_dev == NULL) { err = -ENOMEM; goto out; @@ -11765,7 +11860,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); out_free_ieee80211: - free_ieee80211(priv->net_dev); + free_ieee80211(priv->net_dev, 0); out: return err; } @@ -11832,7 +11927,11 @@ static void __devexit ipw_pci_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); - free_ieee80211(priv->net_dev); + /* wiphy_unregister needs to be here, before free_ieee80211 */ + wiphy_unregister(priv->ieee->wdev.wiphy); + kfree(priv->ieee->a_band.channels); + kfree(priv->ieee->bg_band.channels); + free_ieee80211(priv->net_dev, 0); free_firmware(); } diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h index 1e334ff6bd52..bf45391172f3 100644 --- a/drivers/net/wireless/ipw2x00/libipw.h +++ b/drivers/net/wireless/ipw2x00/libipw.h @@ -31,6 +31,7 @@ #include <linux/ieee80211.h> #include <net/lib80211.h> +#include <net/cfg80211.h> #define LIBIPW_VERSION "git-1.1.13" @@ -783,12 +784,15 @@ struct libipw_geo { struct libipw_device { struct net_device *dev; + struct wireless_dev wdev; struct libipw_security sec; /* Bookkeeping structures */ struct libipw_stats ieee_stats; struct libipw_geo geo; + struct ieee80211_supported_band bg_band; + struct ieee80211_supported_band a_band; /* Probe / Beacon management */ struct list_head network_free_list; @@ -1014,8 +1018,8 @@ static inline int libipw_is_cck_rate(u8 rate) } /* ieee80211.c */ -extern void free_ieee80211(struct net_device *dev); -extern struct net_device *alloc_ieee80211(int sizeof_priv); +extern void free_ieee80211(struct net_device *dev, int monitor); +extern struct net_device *alloc_ieee80211(int sizeof_priv, int monitor); extern int libipw_change_mtu(struct net_device *dev, int new_mtu); extern void libipw_networks_age(struct libipw_device *ieee, diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c index eb2b60834c17..1ae0b2b02c38 100644 --- a/drivers/net/wireless/ipw2x00/libipw_module.c +++ b/drivers/net/wireless/ipw2x00/libipw_module.c @@ -62,6 +62,9 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); +struct cfg80211_ops libipw_config_ops = { }; +void *libipw_wiphy_privid = &libipw_wiphy_privid; + static int libipw_networks_allocate(struct libipw_device *ieee) { if (ieee->networks) @@ -140,7 +143,7 @@ int libipw_change_mtu(struct net_device *dev, int new_mtu) } EXPORT_SYMBOL(libipw_change_mtu); -struct net_device *alloc_ieee80211(int sizeof_priv) +struct net_device *alloc_ieee80211(int sizeof_priv, int monitor) { struct libipw_device *ieee; struct net_device *dev; @@ -157,10 +160,31 @@ struct net_device *alloc_ieee80211(int sizeof_priv) ieee->dev = dev; + if (!monitor) { + ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0); + if (!ieee->wdev.wiphy) { + LIBIPW_ERROR("Unable to allocate wiphy.\n"); + goto failed_free_netdev; + } + + ieee->dev->ieee80211_ptr = &ieee->wdev; + ieee->wdev.iftype = NL80211_IFTYPE_STATION; + + /* Fill-out wiphy structure bits we know... Not enough info + here to call set_wiphy_dev or set MAC address or channel info + -- have to do that in ->ndo_init... */ + ieee->wdev.wiphy->privid = libipw_wiphy_privid; + + ieee->wdev.wiphy->max_scan_ssids = 1; + ieee->wdev.wiphy->max_scan_ie_len = 0; + ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_ADHOC); + } + err = libipw_networks_allocate(ieee); if (err) { LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err); - goto failed_free_netdev; + goto failed_free_wiphy; } libipw_networks_initialize(ieee); @@ -175,7 +199,7 @@ struct net_device *alloc_ieee80211(int sizeof_priv) ieee->host_decrypt = 1; ieee->host_mc_decrypt = 1; - /* Host fragementation in Open mode. Default is enabled. + /* Host fragmentation in Open mode. Default is enabled. * Note: host fragmentation is always enabled if host encryption * is enabled. For cards can do hardware encryption, they must do * hardware fragmentation as well. So we don't need a variable @@ -193,19 +217,27 @@ struct net_device *alloc_ieee80211(int sizeof_priv) return dev; +failed_free_wiphy: + if (!monitor) + wiphy_free(ieee->wdev.wiphy); failed_free_netdev: free_netdev(dev); failed: return NULL; } -void free_ieee80211(struct net_device *dev) +void free_ieee80211(struct net_device *dev, int monitor) { struct libipw_device *ieee = netdev_priv(dev); lib80211_crypt_info_free(&ieee->crypt_info); libipw_networks_free(ieee); + + /* free cfg80211 resources */ + if (!monitor) + wiphy_free(ieee->wdev.wiphy); + free_netdev(dev); } @@ -216,17 +248,22 @@ u32 libipw_debug_level = 0; EXPORT_SYMBOL_GPL(libipw_debug_level); static struct proc_dir_entry *libipw_proc = NULL; -static int show_debug_level(char *page, char **start, off_t offset, - int count, int *eof, void *data) +static int debug_level_proc_show(struct seq_file *m, void *v) { - return snprintf(page, count, "0x%08X\n", libipw_debug_level); + seq_printf(m, "0x%08X\n", libipw_debug_level); + return 0; } -static int store_debug_level(struct file *file, const char __user * buffer, - unsigned long count, void *data) +static int debug_level_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_level_proc_show, NULL); +} + +static ssize_t debug_level_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) { char buf[] = "0x00000000\n"; - unsigned long len = min((unsigned long)sizeof(buf) - 1, count); + size_t len = min(sizeof(buf) - 1, count); unsigned long val; if (copy_from_user(buf, buffer, len)) @@ -240,6 +277,15 @@ static int store_debug_level(struct file *file, const char __user * buffer, return strnlen(buf, len); } + +static const struct file_operations debug_level_proc_fops = { + .owner = THIS_MODULE, + .open = debug_level_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = debug_level_proc_write, +}; #endif /* CONFIG_LIBIPW_DEBUG */ static int __init libipw_init(void) @@ -254,16 +300,13 @@ static int __init libipw_init(void) " proc directory\n"); return -EIO; } - e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR, - libipw_proc); + e = proc_create("debug_level", S_IRUGO | S_IWUSR, libipw_proc, + &debug_level_proc_fops); if (!e) { remove_proc_entry(DRV_NAME, init_net.proc_net); libipw_proc = NULL; return -EIO; } - e->read_proc = show_debug_level; - e->write_proc = store_debug_level; - e->data = NULL; #endif /* CONFIG_LIBIPW_DEBUG */ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 99310c033253..b16b06c2031f 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -1,17 +1,7 @@ config IWLWIFI tristate "Intel Wireless Wifi" - depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL - select LIB80211 + depends on PCI && MAC80211 && EXPERIMENTAL select FW_LOADER - select MAC80211_LEDS if IWLWIFI_LEDS - select LEDS_CLASS if IWLWIFI_LEDS - -config IWLWIFI_LEDS - bool "Enable LED support in iwlagn and iwl3945 drivers" - depends on IWLWIFI - default y - ---help--- - Select this if you want LED support. config IWLWIFI_SPECTRUM_MEASUREMENT bool "Enable Spectrum Measurement in iwlagn driver" @@ -50,6 +40,24 @@ config IWLWIFI_DEBUGFS ---help--- Enable creation of debugfs files for the iwlwifi drivers. +config IWLWIFI_DEVICE_TRACING + bool "iwlwifi device access tracing" + depends on IWLWIFI + depends on EVENT_TRACING + help + Say Y here to trace all commands, including TX frames and IO + accesses, sent to the device. If you say yes, iwlwifi will + register with the ftrace framework for event tracing and dump + all this information to the ringbuffer, you may need to + increase the ringbuffer size. See the ftrace documentation + for more information. + + When tracing is not enabled, this option still has some + (though rather small) overhead. + + If unsure, say Y so we can help you better when problems + occur. + config IWLAGN tristate "Intel Wireless WiFi Next Gen AGN (iwlagn)" depends on IWLWIFI diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 1d4e0a226fd4..7f82044af242 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -1,20 +1,22 @@ obj-$(CONFIG_IWLWIFI) += iwlcore.o iwlcore-objs := iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o iwlcore-objs += iwl-rx.o iwl-tx.o iwl-sta.o iwl-calib.o -iwlcore-objs += iwl-scan.o +iwlcore-objs += iwl-scan.o iwl-led.o iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o -iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o iwlcore-$(CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT) += iwl-spectrum.o +iwlcore-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o +CFLAGS_iwl-devtrace.o := -I$(src) + +# AGN obj-$(CONFIG_IWLAGN) += iwlagn.o -iwlagn-objs := iwl-agn.o iwl-agn-rs.o +iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o iwlagn-$(CONFIG_IWL4965) += iwl-4965.o iwlagn-$(CONFIG_IWL5000) += iwl-5000.o iwlagn-$(CONFIG_IWL5000) += iwl-6000.o iwlagn-$(CONFIG_IWL5000) += iwl-1000.o +# 3945 obj-$(CONFIG_IWL3945) += iwl3945.o iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl-3945-led.o - - diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 950267ab556a..8414178bcff4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -44,6 +44,7 @@ #include "iwl-sta.h" #include "iwl-helpers.h" #include "iwl-5000-hw.h" +#include "iwl-agn-led.h" /* Highest firmware API version supported */ #define IWL1000_UCODE_API_MAX 3 @@ -76,7 +77,10 @@ static void iwl1000_set_ct_threshold(struct iwl_priv *priv) /* NIC configuration for 1000 series */ static void iwl1000_nic_config(struct iwl_priv *priv) { - iwl5000_nic_config(priv); + /* set CSR_HW_CONFIG_REG for uCode use */ + iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); /* Setting digital SVR for 1000 card to 1.32V */ /* locking is acquired in iwl_set_bits_mask_prph() function */ @@ -106,9 +110,8 @@ static struct iwl_lib_ops iwl1000_lib = { .send_tx_power = iwl5000_send_tx_power, .update_chain_flags = iwl_update_chain_flags, .apm_ops = { - .init = iwl5000_apm_init, - .reset = iwl5000_apm_reset, - .stop = iwl5000_apm_stop, + .init = iwl_apm_init, + .stop = iwl_apm_stop, .config = iwl1000_nic_config, .set_pwr_src = iwl_set_pwr_src, }, @@ -142,6 +145,7 @@ static struct iwl_ops iwl1000_ops = { .lib = &iwl1000_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl5000_hcmd_utils, + .led = &iwlagn_led_ops, }; struct iwl_cfg iwl1000_bgn_cfg = { @@ -152,15 +156,50 @@ struct iwl_cfg iwl1000_bgn_cfg = { .sku = IWL_SKU_G|IWL_SKU_N, .ops = &iwl1000_ops, .eeprom_size = OTP_LOW_IMAGE_SIZE, - .eeprom_ver = EEPROM_5000_EEPROM_VERSION, + .eeprom_ver = EEPROM_1000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_A, .valid_rx_ant = ANT_AB, - .need_pll_cfg = true, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, .max_ll_items = OTP_MAX_LL_ITEMS_1000, .shadow_ram_support = false, .ht_greenfield_support = true, + .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .support_ct_kill_exit = true, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, }; +struct iwl_cfg iwl1000_bg_cfg = { + .name = "1000 Series BG", + .fw_name_pre = IWL1000_FW_PRE, + .ucode_api_max = IWL1000_UCODE_API_MAX, + .ucode_api_min = IWL1000_UCODE_API_MIN, + .sku = IWL_SKU_G, + .ops = &iwl1000_ops, + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .eeprom_ver = EEPROM_1000_EEPROM_VERSION, + .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, + .mod_params = &iwl50_mod_params, + .valid_tx_ant = ANT_A, + .valid_rx_ant = ANT_AB, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, + .max_ll_items = OTP_MAX_LL_ITEMS_1000, + .shadow_ram_support = false, + .ht_greenfield_support = true, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .support_ct_kill_exit = true, +}; + +MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h index 16772780c5b0..6fd10d443ba3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -71,12 +71,6 @@ #include "iwl-eeprom.h" -/* - * uCode queue management definitions ... - * Queue #4 is the command queue for 3945 and 4965. - */ -#define IWL_CMD_QUEUE_NUM 4 - /* Time constants */ #define SHORT_SLOT_TIME 9 #define LONG_SLOT_TIME 20 @@ -254,12 +248,6 @@ struct iwl3945_eeprom { #define TFD_CTL_PAD_SET(n) (n << 28) #define TFD_CTL_PAD_GET(ctl) (ctl >> 28) -/* - * RX related structures and functions - */ -#define RX_FREE_BUFFERS 64 -#define RX_LOW_WATERMARK 8 - /* Sizes and addresses for instruction and data memory (SRAM) in * 3945's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ #define IWL39_RTC_INST_LOWER_BOUND (0x000000) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c index 8c29ded7d02c..a871d09d598f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c @@ -24,8 +24,6 @@ * *****************************************************************************/ -#ifdef CONFIG_IWLWIFI_LEDS - #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -43,388 +41,51 @@ #include "iwl-3945.h" #include "iwl-core.h" #include "iwl-dev.h" +#include "iwl-3945-led.h" -#ifdef CONFIG_IWLWIFI_DEBUG -static const char *led_type_str[] = { - __stringify(IWL_LED_TRG_TX), - __stringify(IWL_LED_TRG_RX), - __stringify(IWL_LED_TRG_ASSOC), - __stringify(IWL_LED_TRG_RADIO), - NULL -}; -#endif /* CONFIG_IWLWIFI_DEBUG */ - -static const struct { - u16 brightness; - u8 on_time; - u8 off_time; -} blink_tbl[] = -{ - {300, 25, 25}, - {200, 40, 40}, - {100, 55, 55}, - {70, 65, 65}, - {50, 75, 75}, - {20, 85, 85}, - {15, 95, 95 }, - {10, 110, 110}, - {5, 130, 130}, - {0, 167, 167}, - /* SOLID_ON */ - {-1, IWL_LED_SOLID, 0} -}; - -#define IWL_1MB_RATE (128 * 1024) -#define IWL_LED_THRESHOLD (16) -#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /*Exclude Solid on*/ -#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1) - -static void iwl3945_led_cmd_callback(struct iwl_priv *priv, - struct iwl_device_cmd *cmd, - struct sk_buff *skb) -{ -} - -static inline int iwl3945_brightness_to_idx(enum led_brightness brightness) -{ - return fls(0x000000FF & (u32)brightness); -} /* Send led command */ -static int iwl_send_led_cmd(struct iwl_priv *priv, - struct iwl_led_cmd *led_cmd) +static int iwl3945_send_led_cmd(struct iwl_priv *priv, + struct iwl_led_cmd *led_cmd) { struct iwl_host_cmd cmd = { .id = REPLY_LEDS_CMD, .len = sizeof(struct iwl_led_cmd), .data = led_cmd, .flags = CMD_ASYNC, - .callback = iwl3945_led_cmd_callback, + .callback = NULL, }; return iwl_send_cmd(priv, &cmd); } - - -/* Set led on command */ -static int iwl3945_led_pattern(struct iwl_priv *priv, int led_id, - unsigned int idx) -{ - struct iwl_led_cmd led_cmd = { - .id = led_id, - .interval = IWL_DEF_LED_INTRVL - }; - - BUG_ON(idx > IWL_MAX_BLINK_TBL); - - led_cmd.on = blink_tbl[idx].on_time; - led_cmd.off = blink_tbl[idx].off_time; - - return iwl_send_led_cmd(priv, &led_cmd); -} - - /* Set led on command */ -static int iwl3945_led_on(struct iwl_priv *priv, int led_id) +static int iwl3945_led_on(struct iwl_priv *priv) { struct iwl_led_cmd led_cmd = { - .id = led_id, + .id = IWL_LED_LINK, .on = IWL_LED_SOLID, .off = 0, .interval = IWL_DEF_LED_INTRVL }; - return iwl_send_led_cmd(priv, &led_cmd); + return iwl3945_send_led_cmd(priv, &led_cmd); } /* Set led off command */ -static int iwl3945_led_off(struct iwl_priv *priv, int led_id) +static int iwl3945_led_off(struct iwl_priv *priv) { struct iwl_led_cmd led_cmd = { - .id = led_id, + .id = IWL_LED_LINK, .on = 0, .off = 0, .interval = IWL_DEF_LED_INTRVL }; - IWL_DEBUG_LED(priv, "led off %d\n", led_id); - return iwl_send_led_cmd(priv, &led_cmd); + IWL_DEBUG_LED(priv, "led off\n"); + return iwl3945_send_led_cmd(priv, &led_cmd); } -/* - * Set led on in case of association - * */ -static int iwl3945_led_associate(struct iwl_priv *priv, int led_id) -{ - IWL_DEBUG_LED(priv, "Associated\n"); - - priv->allow_blinking = 1; - return iwl3945_led_on(priv, led_id); -} -/* Set Led off in case of disassociation */ -static int iwl3945_led_disassociate(struct iwl_priv *priv, int led_id) -{ - IWL_DEBUG_LED(priv, "Disassociated\n"); - - priv->allow_blinking = 0; - - return 0; -} - -/* - * brightness call back function for Tx/Rx LED - */ -static int iwl3945_led_associated(struct iwl_priv *priv, int led_id) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status) || - !test_bit(STATUS_READY, &priv->status)) - return 0; - - - /* start counting Tx/Rx bytes */ - if (!priv->last_blink_time && priv->allow_blinking) - priv->last_blink_time = jiffies; - return 0; -} - -/* - * brightness call back for association and radio - */ -static void iwl3945_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct iwl_led *led = container_of(led_cdev, - struct iwl_led, led_dev); - struct iwl_priv *priv = led->priv; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n", - led_type_str[led->type], brightness); - - switch (brightness) { - case LED_FULL: - if (led->led_on) - led->led_on(priv, IWL_LED_LINK); - break; - case LED_OFF: - if (led->led_off) - led->led_off(priv, IWL_LED_LINK); - break; - default: - if (led->led_pattern) { - int idx = iwl3945_brightness_to_idx(brightness); - led->led_pattern(priv, IWL_LED_LINK, idx); - } - break; - } -} - -/* - * Register led class with the system - */ -static int iwl3945_led_register_led(struct iwl_priv *priv, - struct iwl_led *led, - enum led_type type, u8 set_led, - char *trigger) -{ - struct device *device = wiphy_dev(priv->hw->wiphy); - int ret; - - led->led_dev.name = led->name; - led->led_dev.brightness_set = iwl3945_led_brightness_set; - led->led_dev.default_trigger = trigger; - - led->priv = priv; - led->type = type; - - ret = led_classdev_register(device, &led->led_dev); - if (ret) { - IWL_ERR(priv, "Error: failed to register led handler.\n"); - return ret; - } - - led->registered = 1; - - if (set_led && led->led_on) - led->led_on(priv, IWL_LED_LINK); - return 0; -} - - -/* - * calculate blink rate according to last 2 sec Tx/Rx activities - */ -static inline u8 get_blink_rate(struct iwl_priv *priv) -{ - int index; - s64 tpt = priv->rxtxpackets; - - if (tpt < 0) - tpt = -tpt; - - IWL_DEBUG_LED(priv, "tpt %lld \n", (long long)tpt); - - if (!priv->allow_blinking) - index = IWL_MAX_BLINK_TBL; - else - for (index = 0; index < IWL_MAX_BLINK_TBL; index++) - if (tpt > (blink_tbl[index].brightness * IWL_1MB_RATE)) - break; - - IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", index); - return index; -} - -/* - * this function called from handler. Since setting Led command can - * happen very frequent we postpone led command to be called from - * REPLY handler so we know ucode is up - */ -void iwl3945_led_background(struct iwl_priv *priv) -{ - u8 blink_idx; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { - priv->last_blink_time = 0; - return; - } - if (iwl_is_rfkill(priv)) { - priv->last_blink_time = 0; - return; - } - - if (!priv->allow_blinking) { - priv->last_blink_time = 0; - if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) { - priv->last_blink_rate = IWL_SOLID_BLINK_IDX; - iwl3945_led_pattern(priv, IWL_LED_LINK, - IWL_SOLID_BLINK_IDX); - } - return; - } - if (!priv->last_blink_time || - !time_after(jiffies, priv->last_blink_time + - msecs_to_jiffies(1000))) - return; - - blink_idx = get_blink_rate(priv); - - /* call only if blink rate change */ - if (blink_idx != priv->last_blink_rate) - iwl3945_led_pattern(priv, IWL_LED_LINK, blink_idx); - - priv->last_blink_time = jiffies; - priv->last_blink_rate = blink_idx; - priv->rxtxpackets = 0; -} - - -/* Register all led handler */ -int iwl3945_led_register(struct iwl_priv *priv) -{ - char *trigger; - int ret; - - priv->last_blink_rate = 0; - priv->rxtxpackets = 0; - priv->led_tpt = 0; - priv->last_blink_time = 0; - priv->allow_blinking = 0; - - trigger = ieee80211_get_radio_led_name(priv->hw); - snprintf(priv->led[IWL_LED_TRG_RADIO].name, - sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio", - wiphy_name(priv->hw->wiphy)); - - priv->led[IWL_LED_TRG_RADIO].led_on = iwl3945_led_on; - priv->led[IWL_LED_TRG_RADIO].led_off = iwl3945_led_off; - priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL; - - ret = iwl3945_led_register_led(priv, - &priv->led[IWL_LED_TRG_RADIO], - IWL_LED_TRG_RADIO, 1, trigger); - - if (ret) - goto exit_fail; - - trigger = ieee80211_get_assoc_led_name(priv->hw); - snprintf(priv->led[IWL_LED_TRG_ASSOC].name, - sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc", - wiphy_name(priv->hw->wiphy)); - - ret = iwl3945_led_register_led(priv, - &priv->led[IWL_LED_TRG_ASSOC], - IWL_LED_TRG_ASSOC, 0, trigger); - - /* for assoc always turn led on */ - priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_associate; - priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_disassociate; - priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL; - - if (ret) - goto exit_fail; - - trigger = ieee80211_get_rx_led_name(priv->hw); - snprintf(priv->led[IWL_LED_TRG_RX].name, - sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX", - wiphy_name(priv->hw->wiphy)); - - ret = iwl3945_led_register_led(priv, - &priv->led[IWL_LED_TRG_RX], - IWL_LED_TRG_RX, 0, trigger); - - priv->led[IWL_LED_TRG_RX].led_on = iwl3945_led_associated; - priv->led[IWL_LED_TRG_RX].led_off = iwl3945_led_associated; - priv->led[IWL_LED_TRG_RX].led_pattern = iwl3945_led_pattern; - - if (ret) - goto exit_fail; - - trigger = ieee80211_get_tx_led_name(priv->hw); - snprintf(priv->led[IWL_LED_TRG_TX].name, - sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX", - wiphy_name(priv->hw->wiphy)); - - ret = iwl3945_led_register_led(priv, - &priv->led[IWL_LED_TRG_TX], - IWL_LED_TRG_TX, 0, trigger); - - priv->led[IWL_LED_TRG_TX].led_on = iwl3945_led_associated; - priv->led[IWL_LED_TRG_TX].led_off = iwl3945_led_associated; - priv->led[IWL_LED_TRG_TX].led_pattern = iwl3945_led_pattern; - - if (ret) - goto exit_fail; - - return 0; - -exit_fail: - iwl3945_led_unregister(priv); - return ret; -} - - -/* unregister led class */ -static void iwl3945_led_unregister_led(struct iwl_led *led, u8 set_led) -{ - if (!led->registered) - return; - - led_classdev_unregister(&led->led_dev); - - if (set_led) - led->led_dev.brightness_set(&led->led_dev, LED_OFF); - led->registered = 0; -} - -/* Unregister all led handlers */ -void iwl3945_led_unregister(struct iwl_priv *priv) -{ - iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0); - iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RX], 0); - iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_TX], 0); - iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1); -} - -#endif +const struct iwl_led_ops iwl3945_led_ops = { + .cmd = iwl3945_send_led_cmd, + .on = iwl3945_led_on, + .off = iwl3945_led_off, +}; diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.h b/drivers/net/wireless/iwlwifi/iwl-3945-led.h index 3b65642258ca..5a1033ca7aaa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.h @@ -24,23 +24,9 @@ * *****************************************************************************/ -#ifndef IWL3945_LEDS_H -#define IWL3945_LEDS_H +#ifndef __iwl_3945_led_h__ +#define __iwl_3945_led_h__ -struct iwl_priv; +extern const struct iwl_led_ops iwl3945_led_ops; -#ifdef CONFIG_IWLWIFI_LEDS - -#include "iwl-led.h" - -extern int iwl3945_led_register(struct iwl_priv *priv); -extern void iwl3945_led_unregister(struct iwl_priv *priv); -extern void iwl3945_led_background(struct iwl_priv *priv); - -#else -static inline int iwl3945_led_register(struct iwl_priv *priv) { return 0; } -static inline void iwl3945_led_unregister(struct iwl_priv *priv) {} -static inline void iwl3945_led_background(struct iwl_priv *priv) {} - -#endif /* IWLWIFI_LEDS*/ -#endif /* IWL3945_LEDS_H */ +#endif /* __iwl_3945_led_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index cbb0585083a9..d4b49883b30e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -42,38 +42,6 @@ #define RS_NAME "iwl-3945-rs" -struct iwl3945_rate_scale_data { - u64 data; - s32 success_counter; - s32 success_ratio; - s32 counter; - s32 average_tpt; - unsigned long stamp; -}; - -struct iwl3945_rs_sta { - spinlock_t lock; - struct iwl_priv *priv; - s32 *expected_tpt; - unsigned long last_partial_flush; - unsigned long last_flush; - u32 flush_time; - u32 last_tx_packets; - u32 tx_packets; - u8 tgg; - u8 flush_pending; - u8 start_rate; - u8 ibss_sta_added; - struct timer_list rate_scale_flush; - struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945]; -#ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_stats_table_file; -#endif - - /* used to be in sta_info */ - int last_txrate_idx; -}; - static s32 iwl3945_expected_tpt_g[IWL_RATE_COUNT_3945] = { 7, 13, 35, 58, 0, 0, 76, 104, 130, 168, 191, 202 }; @@ -370,6 +338,28 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, IWL_DEBUG_RATE(priv, "enter\n"); + spin_lock_init(&rs_sta->lock); + + rs_sta->priv = priv; + + rs_sta->start_rate = IWL_RATE_INVALID; + + /* default to just 802.11b */ + rs_sta->expected_tpt = iwl3945_expected_tpt_b; + + rs_sta->last_partial_flush = jiffies; + rs_sta->last_flush = jiffies; + rs_sta->flush_time = IWL_RATE_FLUSH; + rs_sta->last_tx_packets = 0; + rs_sta->ibss_sta_added = 0; + + init_timer(&rs_sta->rate_scale_flush); + rs_sta->rate_scale_flush.data = (unsigned long)rs_sta; + rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush; + + for (i = 0; i < IWL_RATE_COUNT_3945; i++) + iwl3945_clear_window(&rs_sta->win[i]); + /* TODO: what is a good starting rate for STA? About middle? Maybe not * the lowest or the highest rate.. Could consider using RSSI from * previous packets? Need to have IEEE 802.1X auth succeed immediately @@ -409,45 +399,11 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp) { struct iwl3945_rs_sta *rs_sta; struct iwl3945_sta_priv *psta = (void *) sta->drv_priv; - struct iwl_priv *priv = iwl_priv; - int i; - - /* - * XXX: If it's using sta->drv_priv anyway, it might - * as well just put all the information there. - */ + struct iwl_priv *priv __maybe_unused = iwl_priv; IWL_DEBUG_RATE(priv, "enter\n"); - rs_sta = kzalloc(sizeof(struct iwl3945_rs_sta), gfp); - if (!rs_sta) { - IWL_DEBUG_RATE(priv, "leave: ENOMEM\n"); - return NULL; - } - - psta->rs_sta = rs_sta; - - spin_lock_init(&rs_sta->lock); - - rs_sta->priv = priv; - - rs_sta->start_rate = IWL_RATE_INVALID; - - /* default to just 802.11b */ - rs_sta->expected_tpt = iwl3945_expected_tpt_b; - - rs_sta->last_partial_flush = jiffies; - rs_sta->last_flush = jiffies; - rs_sta->flush_time = IWL_RATE_FLUSH; - rs_sta->last_tx_packets = 0; - rs_sta->ibss_sta_added = 0; - - init_timer(&rs_sta->rate_scale_flush); - rs_sta->rate_scale_flush.data = (unsigned long)rs_sta; - rs_sta->rate_scale_flush.function = &iwl3945_bg_rate_scale_flush; - - for (i = 0; i < IWL_RATE_COUNT_3945; i++) - iwl3945_clear_window(&rs_sta->win[i]); + rs_sta = &psta->rs_sta; IWL_DEBUG_RATE(priv, "leave\n"); @@ -458,14 +414,11 @@ static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta, void *priv_sta) { struct iwl3945_sta_priv *psta = (void *) sta->drv_priv; - struct iwl3945_rs_sta *rs_sta = priv_sta; + struct iwl3945_rs_sta *rs_sta = &psta->rs_sta; struct iwl_priv *priv __maybe_unused = rs_sta->priv; - psta->rs_sta = NULL; - IWL_DEBUG_RATE(priv, "enter\n"); del_timer_sync(&rs_sta->rate_scale_flush); - kfree(rs_sta); IWL_DEBUG_RATE(priv, "leave\n"); } @@ -960,14 +913,15 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) rcu_read_lock(); - sta = ieee80211_find_sta(hw, priv->stations[sta_id].sta.sta.addr); + sta = ieee80211_find_sta(priv->vif, + priv->stations[sta_id].sta.sta.addr); if (!sta) { rcu_read_unlock(); return; } psta = (void *) sta->drv_priv; - rs_sta = psta->rs_sta; + rs_sta = &psta->rs_sta; spin_lock_irqsave(&rs_sta->lock, flags); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index f059b49dc691..7da1dab933d9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -47,7 +47,8 @@ #include "iwl-eeprom.h" #include "iwl-helpers.h" #include "iwl-core.h" -#include "iwl-agn-rs.h" +#include "iwl-led.h" +#include "iwl-3945-led.h" #define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ @@ -293,7 +294,7 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv, static void iwl3945_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); @@ -353,16 +354,12 @@ static void iwl3945_rx_reply_tx(struct iwl_priv *priv, void iwl3945_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", (int)sizeof(struct iwl3945_notif_statistics), le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); memcpy(&priv->statistics_39, pkt->u.raw, sizeof(priv->statistics_39)); - - iwl3945_led_background(priv); - - priv->last_statistics_time = jiffies; } /****************************************************************************** @@ -545,14 +542,18 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, struct ieee80211_rx_status *stats) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IWL_RX_DATA(pkt); struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt); - short len = le16_to_cpu(rx_hdr->len); + u16 len = le16_to_cpu(rx_hdr->len); + struct sk_buff *skb; + int ret; + __le16 fc = hdr->frame_control; /* We received data from the HW, so stop the watchdog */ - if (unlikely((len + IWL39_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) { + if (unlikely(len + IWL39_RX_FRAME_SIZE > + PAGE_SIZE << priv->hw_params.rx_page_order)) { IWL_DEBUG_DROP(priv, "Corruption detected!\n"); return; } @@ -564,24 +565,50 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv, return; } - skb_reserve(rxb->skb, (void *)rx_hdr->payload - (void *)pkt); - /* Set the size of the skb to the size of the frame */ - skb_put(rxb->skb, le16_to_cpu(rx_hdr->len)); + skb = alloc_skb(IWL_LINK_HDR_MAX * 2, GFP_ATOMIC); + if (!skb) { + IWL_ERR(priv, "alloc_skb failed\n"); + return; + } if (!iwl3945_mod_params.sw_crypto) iwl_set_decrypted_flag(priv, - (struct ieee80211_hdr *)rxb->skb->data, + (struct ieee80211_hdr *)rxb_addr(rxb), le32_to_cpu(rx_end->status), stats); -#ifdef CONFIG_IWLWIFI_LEDS - if (ieee80211_is_data(hdr->frame_control)) - priv->rxtxpackets += len; -#endif - iwl_update_stats(priv, false, hdr->frame_control, len); + skb_reserve(skb, IWL_LINK_HDR_MAX); + skb_add_rx_frag(skb, 0, rxb->page, + (void *)rx_hdr->payload - (void *)pkt, len); + + /* mac80211 currently doesn't support paged SKB. Convert it to + * linear SKB for management frame and data frame requires + * software decryption or software defragementation. */ + if (ieee80211_is_mgmt(fc) || + ieee80211_has_protected(fc) || + ieee80211_has_morefrags(fc) || + le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) + ret = skb_linearize(skb); + else + ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ? + 0 : -ENOMEM; + + if (ret) { + kfree_skb(skb); + goto out; + } - memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats)); - ieee80211_rx_irqsafe(priv->hw, rxb->skb); - rxb->skb = NULL; + /* + * XXX: We cannot touch the page and its virtual memory (pkt) after + * here. It might have already been freed by the above skb change. + */ + + iwl_update_stats(priv, false, fc, len); + memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); + + ieee80211_rx(priv->hw, skb); + out: + priv->alloc_rxb_page--; + rxb->page = NULL; } #define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) @@ -591,7 +618,7 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, { struct ieee80211_hdr *header; struct ieee80211_rx_status rx_status; - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt); struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt); @@ -791,29 +818,31 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, u8 data_retry_limit; __le32 tx_flags; __le16 fc = hdr->frame_control; - struct iwl3945_tx_cmd *tx = (struct iwl3945_tx_cmd *)cmd->cmd.payload; + struct iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload; rate = iwl3945_rates[rate_index].plcp; - tx_flags = tx->tx_flags; + tx_flags = tx_cmd->tx_flags; /* We need to figure out how to get the sta->supp_rates while * in this running context */ rate_mask = IWL_RATES_MASK; + + /* Set retry limit on DATA packets and Probe Responses*/ + if (ieee80211_is_probe_resp(fc)) + data_retry_limit = 3; + else + data_retry_limit = IWL_DEFAULT_TX_RETRY; + tx_cmd->data_retry_limit = data_retry_limit; + if (tx_id >= IWL_CMD_QUEUE_NUM) rts_retry_limit = 3; else rts_retry_limit = 7; - if (ieee80211_is_probe_resp(fc)) { - data_retry_limit = 3; - if (data_retry_limit < rts_retry_limit) - rts_retry_limit = data_retry_limit; - } else - data_retry_limit = IWL_DEFAULT_TX_RETRY; - - if (priv->data_retry_limit != -1) - data_retry_limit = priv->data_retry_limit; + if (data_retry_limit < rts_retry_limit) + rts_retry_limit = data_retry_limit; + tx_cmd->rts_retry_limit = rts_retry_limit; if (ieee80211_is_mgmt(fc)) { switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { @@ -831,22 +860,20 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, } } - tx->rts_retry_limit = rts_retry_limit; - tx->data_retry_limit = data_retry_limit; - tx->rate = rate; - tx->tx_flags = tx_flags; + tx_cmd->rate = rate; + tx_cmd->tx_flags = tx_flags; /* OFDM */ - tx->supp_rates[0] = + tx_cmd->supp_rates[0] = ((rate_mask & IWL_OFDM_RATES_MASK) >> IWL_FIRST_OFDM_RATE) & 0xFF; /* CCK */ - tx->supp_rates[1] = (rate_mask & 0xF); + tx_cmd->supp_rates[1] = (rate_mask & 0xF); IWL_DEBUG_RATE(priv, "Tx sta id: %d, rate: %d (plcp), flags: 0x%4X " "cck/ofdm mask: 0x%x/0x%x\n", sta_id, - tx->rate, le32_to_cpu(tx->tx_flags), - tx->supp_rates[1], tx->supp_rates[0]); + tx_cmd->rate, le32_to_cpu(tx_cmd->tx_flags), + tx_cmd->supp_rates[1], tx_cmd->supp_rates[0]); } u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id, u16 tx_rate, u8 flags) @@ -962,6 +989,11 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) iwl3945_hw_txq_ctx_free(priv); + /* allocate tx queue structure */ + rc = iwl_alloc_txq_mem(priv); + if (rc) + return rc; + /* Tx CMD queue */ rc = iwl3945_tx_reset(priv); if (rc) @@ -986,41 +1018,25 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) return rc; } + +/* + * Start up 3945's basic functionality after it has been reset + * (e.g. after platform boot, or shutdown via iwl_apm_stop()) + * NOTE: This does not load uCode nor start the embedded processor + */ static int iwl3945_apm_init(struct iwl_priv *priv) { - int ret; - - iwl_power_initialize(priv); - - iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + int ret = iwl_apm_init(priv); - /* disable L0s without affecting L1 :don't wait for ICH L0s bug W/A) */ - iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); - - /* set "initialization complete" bit to move adapter - * D0U* --> D0A* state */ - iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (ret < 0) { - IWL_DEBUG_INFO(priv, "Failed to init the card\n"); - goto out; - } - - /* enable DMA */ - iwl_write_prph(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT | - APMG_CLK_VAL_BSM_CLK_RQT); - - udelay(20); + /* Clear APMG (NIC's internal power management) interrupts */ + iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0); + iwl_write_prph(priv, APMG_RTC_INT_STT_REG, 0xFFFFFFFF); - /* disable L1-Active */ - iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + /* Reset radio chip */ + iwl_set_bits_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); + udelay(5); + iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); -out: return ret; } @@ -1145,12 +1161,16 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv) int txq_id; /* Tx queues */ - for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) - if (txq_id == IWL_CMD_QUEUE_NUM) - iwl_cmd_queue_free(priv); - else - iwl_tx_queue_free(priv, txq_id); + if (priv->txq) + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; + txq_id++) + if (txq_id == IWL_CMD_QUEUE_NUM) + iwl_cmd_queue_free(priv); + else + iwl_tx_queue_free(priv, txq_id); + /* free tx queue structure */ + iwl_free_txq_mem(priv); } void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) @@ -1159,6 +1179,7 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) /* stop SCD */ iwl_write_prph(priv, ALM_SCD_MODE_REG, 0); + iwl_write_prph(priv, ALM_SCD_TXFACT_REG, 0); /* reset TFD queues */ for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { @@ -1171,85 +1192,6 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) iwl3945_hw_txq_ctx_free(priv); } -static int iwl3945_apm_stop_master(struct iwl_priv *priv) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - /* set stop master bit */ - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); - - iwl_poll_direct_bit(priv, CSR_RESET, - CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); - - if (ret < 0) - goto out; - -out: - spin_unlock_irqrestore(&priv->lock, flags); - IWL_DEBUG_INFO(priv, "stop master\n"); - - return ret; -} - -static void iwl3945_apm_stop(struct iwl_priv *priv) -{ - unsigned long flags; - - iwl3945_apm_stop_master(priv); - - spin_lock_irqsave(&priv->lock, flags); - - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - /* clear "init complete" move adapter D0A* --> D0U state */ - iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int iwl3945_apm_reset(struct iwl_priv *priv) -{ - iwl3945_apm_stop_master(priv); - - - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - udelay(10); - - iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - iwl_poll_direct_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - - iwl_write_prph(priv, APMG_CLK_CTRL_REG, - APMG_CLK_VAL_BSM_CLK_RQT); - - iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0); - iwl_write_prph(priv, APMG_RTC_INT_STT_REG, - 0xFFFFFFFF); - - /* enable DMA */ - iwl_write_prph(priv, APMG_CLK_EN_REG, - APMG_CLK_VAL_DMA_CLK_RQT | - APMG_CLK_VAL_BSM_CLK_RQT); - udelay(10); - - iwl_set_bits_prph(priv, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - udelay(5); - iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - - /* Clear the 'host command active' bit... */ - clear_bit(STATUS_HCMD_ACTIVE, &priv->status); - - wake_up_interruptible(&priv->wait_command_queue); - - return 0; -} - /** * iwl3945_hw_reg_adjust_power_by_temp * return index delta into power gain settings table @@ -1858,7 +1800,7 @@ int iwl3945_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) static int iwl3945_send_rxon_assoc(struct iwl_priv *priv) { int rc = 0; - struct iwl_rx_packet *res = NULL; + struct iwl_rx_packet *pkt; struct iwl3945_rxon_assoc_cmd rxon_assoc; struct iwl_host_cmd cmd = { .id = REPLY_RXON_ASSOC, @@ -1887,14 +1829,14 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv) if (rc) return rc; - res = (struct iwl_rx_packet *)cmd.reply_skb->data; - if (res->hdr.flags & IWL_CMD_FAILED_MSK) { + pkt = (struct iwl_rx_packet *)cmd.reply_page; + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_RXON_ASSOC command\n"); rc = -EIO; } - priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.reply_skb); + priv->alloc_rxb_page--; + free_pages(cmd.reply_page, priv->hw_params.rx_page_order); return rc; } @@ -2042,12 +1984,6 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) return 0; } -/* will add 3945 channel switch cmd handling later */ -int iwl3945_hw_channel_switch(struct iwl_priv *priv, u16 channel) -{ - return 0; -} - /** * iwl3945_reg_txpower_periodic - called when time to check our temperature. * @@ -2557,11 +2493,10 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv) } /* Assign number of Usable TX queues */ - priv->hw_params.max_txq_num = IWL39_NUM_QUEUES; + priv->hw_params.max_txq_num = priv->cfg->num_of_queues; priv->hw_params.tfd_size = sizeof(struct iwl3945_tfd); - priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_3K; - priv->hw_params.max_pkt_size = 2342; + priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_3K); priv->hw_params.max_rxq_size = RX_QUEUE_SIZE; priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; priv->hw_params.max_stations = IWL3945_STATION_COUNT; @@ -2844,8 +2779,7 @@ static struct iwl_lib_ops iwl3945_lib = { .dump_nic_error_log = iwl3945_dump_nic_error_log, .apm_ops = { .init = iwl3945_apm_init, - .reset = iwl3945_apm_reset, - .stop = iwl3945_apm_stop, + .stop = iwl_apm_stop, .config = iwl3945_nic_config, .set_pwr_src = iwl3945_set_pwr_src, }, @@ -2874,6 +2808,7 @@ static struct iwl_lib_ops iwl3945_lib = { static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = { .get_hcmd_size = iwl3945_get_hcmd_size, .build_addsta_hcmd = iwl3945_build_addsta_hcmd, + .rts_tx_cmd_flag = iwlcore_rts_tx_cmd_flag, }; static struct iwl_ops iwl3945_ops = { @@ -2881,6 +2816,7 @@ static struct iwl_ops iwl3945_ops = { .lib = &iwl3945_lib, .hcmd = &iwl3945_hcmd, .utils = &iwl3945_hcmd_utils, + .led = &iwl3945_led_ops, }; static struct iwl_cfg iwl3945_bg_cfg = { @@ -2892,9 +2828,14 @@ static struct iwl_cfg iwl3945_bg_cfg = { .eeprom_size = IWL3945_EEPROM_IMG_SIZE, .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .ops = &iwl3945_ops, + .num_of_queues = IWL39_NUM_QUEUES, .mod_params = &iwl3945_mod_params, + .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, + .set_l0s = false, + .use_bsm = true, .use_isr_legacy = true, .ht_greenfield_support = false, + .led_compensation = 64, }; static struct iwl_cfg iwl3945_abg_cfg = { @@ -2906,9 +2847,11 @@ static struct iwl_cfg iwl3945_abg_cfg = { .eeprom_size = IWL3945_EEPROM_IMG_SIZE, .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .ops = &iwl3945_ops, + .num_of_queues = IWL39_NUM_QUEUES, .mod_params = &iwl3945_mod_params, .use_isr_legacy = true, .ht_greenfield_support = false, + .led_compensation = 64, }; struct pci_device_id iwl3945_hw_card_ids[] = { diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index 21679bf3a1aa..ecc23ec1f6a4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -46,7 +46,7 @@ extern struct pci_device_id iwl3945_hw_card_ids[]; #include "iwl-debug.h" #include "iwl-power.h" #include "iwl-dev.h" -#include "iwl-3945-led.h" +#include "iwl-led.h" /* Highest firmware API version supported */ #define IWL3945_UCODE_API_MAX 2 @@ -74,8 +74,41 @@ extern struct pci_device_id iwl3945_hw_card_ids[]; /* Module parameters accessible from iwl-*.c */ extern struct iwl_mod_params iwl3945_mod_params; +struct iwl3945_rate_scale_data { + u64 data; + s32 success_counter; + s32 success_ratio; + s32 counter; + s32 average_tpt; + unsigned long stamp; +}; + +struct iwl3945_rs_sta { + spinlock_t lock; + struct iwl_priv *priv; + s32 *expected_tpt; + unsigned long last_partial_flush; + unsigned long last_flush; + u32 flush_time; + u32 last_tx_packets; + u32 tx_packets; + u8 tgg; + u8 flush_pending; + u8 start_rate; + u8 ibss_sta_added; + struct timer_list rate_scale_flush; + struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945]; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_stats_table_file; +#endif + + /* used to be in sta_info */ + int last_txrate_idx; +}; + + struct iwl3945_sta_priv { - struct iwl3945_rs_sta *rs_sta; + struct iwl3945_rs_sta rs_sta; }; enum iwl3945_antenna { @@ -130,12 +163,6 @@ struct iwl3945_frame { #define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) #define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) -/* - * RX related structures and functions - */ -#define RX_FREE_BUFFERS 64 -#define RX_LOW_WATERMARK 8 - #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 @@ -194,22 +221,13 @@ struct iwl3945_ibss_seq { * for use by iwl-*.c * *****************************************************************************/ -extern int iwl3945_power_init_handle(struct iwl_priv *priv); -extern int iwl3945_eeprom_init(struct iwl_priv *priv); extern int iwl3945_calc_db_from_ratio(int sig_ratio); extern int iwl3945_calc_sig_qual(int rssi_dbm, int noise_dbm); -extern int iwl3945_tx_queue_init(struct iwl_priv *priv, - struct iwl_tx_queue *txq, int count, u32 id); extern void iwl3945_rx_replenish(void *data); extern void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); -extern void iwl3945_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq); -extern int iwl3945_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, - const void *data); -extern int __must_check iwl3945_send_cmd(struct iwl_priv *priv, - struct iwl_host_cmd *cmd); extern unsigned int iwl3945_fill_beacon_frame(struct iwl_priv *priv, struct ieee80211_hdr *hdr,int left); -extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv); +extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log); extern void iwl3945_dump_nic_error_log(struct iwl_priv *priv); /* @@ -280,8 +298,6 @@ extern void iwl3945_config_ap(struct iwl_priv *priv); */ extern u8 iwl3945_hw_find_station(struct iwl_priv *priv, const u8 *bssid); -extern int iwl3945_hw_channel_switch(struct iwl_priv *priv, u16 channel); - /* * Forward declare iwl-3945.c functions for iwl-base.c */ diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h index b34322a32458..c606366b582c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h @@ -76,12 +76,9 @@ /* * uCode queue management definitions ... - * Queue #4 is the command queue for 3945 and 4965; map it to Tx FIFO chnl 4. * The first queue used for block-ack aggregation is #7 (4965 only). * All block-ack aggregation queues should map to Tx DMA/FIFO channel 7. */ -#define IWL_CMD_QUEUE_NUM 4 -#define IWL_CMD_FIFO_NUM 4 #define IWL49_FIRST_AMPDU_QUEUE 7 /* Time constants */ diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 6f703a041847..386513b601f5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -45,6 +45,7 @@ #include "iwl-helpers.h" #include "iwl-calib.h" #include "iwl-sta.h" +#include "iwl-agn-led.h" static int iwl4965_send_tx_power(struct iwl_priv *priv); static int iwl4965_hw_get_temperature(struct iwl_priv *priv); @@ -62,8 +63,6 @@ static int iwl4965_hw_get_temperature(struct iwl_priv *priv); /* module parameters */ static struct iwl_mod_params iwl4965_mod_params = { - .num_of_queues = IWL49_NUM_QUEUES, - .num_of_ampdu_queues = IWL49_NUM_AMPDU_QUEUES, .amsdu_size_8K = 1, .restart_fw = 1, /* the rest are 0 by default */ @@ -319,63 +318,13 @@ static void iwl4965_txq_set_sched(struct iwl_priv *priv, u32 mask) iwl_write_prph(priv, IWL49_SCD_TXFACT, mask); } -static int iwl4965_apm_init(struct iwl_priv *priv) -{ - int ret = 0; - - iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); - - /* disable L0s without affecting L1 :don't wait for ICH L0s bug W/A) */ - iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); - - /* set "initialization complete" bit to move adapter - * D0U* --> D0A* state */ - iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* wait for clock stabilization */ - ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (ret < 0) { - IWL_DEBUG_INFO(priv, "Failed to init the card\n"); - goto out; - } - - /* enable DMA */ - iwl_write_prph(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT | - APMG_CLK_VAL_BSM_CLK_RQT); - - udelay(20); - - /* disable L1-Active */ - iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - -out: - return ret; -} - - static void iwl4965_nic_config(struct iwl_priv *priv) { unsigned long flags; u16 radio_cfg; - u16 lctl; spin_lock_irqsave(&priv->lock, flags); - lctl = iwl_pcie_link_ctl(priv); - - /* HW bug W/A - negligible power consumption */ - /* L1-ASPM is enabled by BIOS */ - if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN) - /* L1-ASPM enabled: disable L0S */ - iwl_set_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - else - /* L1-ASPM disabled: enable L0S */ - iwl_clear_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); /* write radio config values to register */ @@ -396,79 +345,6 @@ static void iwl4965_nic_config(struct iwl_priv *priv) spin_unlock_irqrestore(&priv->lock, flags); } -static int iwl4965_apm_stop_master(struct iwl_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - /* set stop master bit */ - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); - - iwl_poll_direct_bit(priv, CSR_RESET, - CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); - - spin_unlock_irqrestore(&priv->lock, flags); - IWL_DEBUG_INFO(priv, "stop master\n"); - - return 0; -} - -static void iwl4965_apm_stop(struct iwl_priv *priv) -{ - unsigned long flags; - - iwl4965_apm_stop_master(priv); - - spin_lock_irqsave(&priv->lock, flags); - - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - /* clear "init complete" move adapter D0A* --> D0U state */ - iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int iwl4965_apm_reset(struct iwl_priv *priv) -{ - int ret = 0; - - iwl4965_apm_stop_master(priv); - - - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - - /* FIXME: put here L1A -L0S w/a */ - - iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (ret < 0) - goto out; - - udelay(10); - - /* Enable DMA and BSM Clock */ - iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT | - APMG_CLK_VAL_BSM_CLK_RQT); - - udelay(10); - - /* disable L1A */ - iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - - clear_bit(STATUS_HCMD_ACTIVE, &priv->status); - wake_up_interruptible(&priv->wait_command_queue); - -out: - return ret; -} - /* Reset differential Rx gains in NIC to prepare for chain noise calibration. * Called after every association, but this runs only once! * ... once chain noise is calibrated the first time, it's good forever. */ @@ -496,14 +372,15 @@ static void iwl4965_chain_noise_reset(struct iwl_priv *priv) static void iwl4965_gain_computation(struct iwl_priv *priv, u32 *average_noise, u16 min_average_noise_antenna_i, - u32 min_average_noise) + u32 min_average_noise, + u8 default_chain) { int i, ret; struct iwl_chain_noise_data *data = &priv->chain_noise_data; data->delta_gain_code[min_average_noise_antenna_i] = 0; - for (i = 0; i < NUM_RX_CHAINS; i++) { + for (i = default_chain; i < NUM_RX_CHAINS; i++) { s32 delta_g = 0; if (!(data->disconn_array[i]) && @@ -557,18 +434,6 @@ static void iwl4965_gain_computation(struct iwl_priv *priv, data->beacon_count = 0; } -static void iwl4965_rts_tx_cmd_flag(struct ieee80211_tx_info *info, - __le32 *tx_flags) -{ - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { - *tx_flags |= TX_CMD_FLG_RTS_MSK; - *tx_flags &= ~TX_CMD_FLG_CTS_MSK; - } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - *tx_flags &= ~TX_CMD_FLG_RTS_MSK; - *tx_flags |= TX_CMD_FLG_CTS_MSK; - } -} - static void iwl4965_bg_txpower_work(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, @@ -663,7 +528,8 @@ static int iwl4965_alive_notify(struct iwl_priv *priv) iwl_write_targ_mem(priv, a, 0); for (; a < priv->scd_base_addr + IWL49_SCD_TRANSLATE_TBL_OFFSET; a += 4) iwl_write_targ_mem(priv, a, 0); - for (; a < sizeof(u16) * priv->hw_params.max_txq_num; a += 4) + for (; a < priv->scd_base_addr + + IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(priv->hw_params.max_txq_num); a += 4) iwl_write_targ_mem(priv, a, 0); /* Tel 4965 where to find Tx byte count tables */ @@ -748,6 +614,10 @@ static struct iwl_sensitivity_ranges iwl4965_sensitivity = { .nrg_th_cck = 100, .nrg_th_ofdm = 100, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, }; static void iwl4965_set_ct_threshold(struct iwl_priv *priv) @@ -764,19 +634,16 @@ static void iwl4965_set_ct_threshold(struct iwl_priv *priv) */ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv) { + if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES && + priv->cfg->mod_params->num_of_queues <= IWL49_NUM_QUEUES) + priv->cfg->num_of_queues = + priv->cfg->mod_params->num_of_queues; - if ((priv->cfg->mod_params->num_of_queues > IWL49_NUM_QUEUES) || - (priv->cfg->mod_params->num_of_queues < IWL_MIN_NUM_QUEUES)) { - IWL_ERR(priv, - "invalid queues_num, should be between %d and %d\n", - IWL_MIN_NUM_QUEUES, IWL49_NUM_QUEUES); - return -EINVAL; - } - - priv->hw_params.max_txq_num = priv->cfg->mod_params->num_of_queues; + priv->hw_params.max_txq_num = priv->cfg->num_of_queues; priv->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM; priv->hw_params.scd_bc_tbls_size = - IWL49_NUM_QUEUES * sizeof(struct iwl4965_scd_bc_tbl); + priv->cfg->num_of_queues * + sizeof(struct iwl4965_scd_bc_tbl); priv->hw_params.tfd_size = sizeof(struct iwl_tfd); priv->hw_params.max_stations = IWL4965_STATION_COUNT; priv->hw_params.bcast_sta_id = IWL4965_BROADCAST_ID; @@ -787,10 +654,10 @@ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR; - priv->hw_params.tx_chains_num = 2; - priv->hw_params.rx_chains_num = 2; - priv->hw_params.valid_tx_ant = ANT_A | ANT_B; - priv->hw_params.valid_rx_ant = ANT_A | ANT_B; + priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); + priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant); + priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; + priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant; if (priv->cfg->ops->lib->temp_ops.set_ct_kill) priv->cfg->ops->lib->temp_ops.set_ct_kill(priv); @@ -1567,14 +1434,13 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv) return ret; } -#ifdef IEEE80211_CONF_CHANNEL_SWITCH static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) { int rc; u8 band = 0; bool is_ht40 = false; u8 ctrl_chan_high = 0; - struct iwl4965_channel_switch_cmd cmd = { 0 }; + struct iwl4965_channel_switch_cmd cmd; const struct iwl_channel_info *ch_info; band = priv->band == IEEE80211_BAND_2GHZ; @@ -1584,19 +1450,22 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) is_ht40 = is_ht40_channel(priv->staging_rxon.flags); if (is_ht40 && - (priv->active_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) + (priv->staging_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) ctrl_chan_high = 1; cmd.band = band; cmd.expect_beacon = 0; cmd.channel = cpu_to_le16(channel); - cmd.rxon_flags = priv->active_rxon.flags; - cmd.rxon_filter_flags = priv->active_rxon.filter_flags; + cmd.rxon_flags = priv->staging_rxon.flags; + cmd.rxon_filter_flags = priv->staging_rxon.filter_flags; cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); if (ch_info) cmd.expect_beacon = is_channel_radar(ch_info); - else - cmd.expect_beacon = 1; + else { + IWL_ERR(priv, "invalid channel switch from %u to %u\n", + priv->active_rxon.channel, channel); + return -EFAULT; + } rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_ht40, ctrl_chan_high, &cmd.tx_power); @@ -1605,10 +1474,11 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) return rc; } - rc = iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd); - return rc; + priv->switch_rxon.channel = cpu_to_le16(channel); + priv->switch_rxon.switch_in_progress = true; + + return iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd); } -#endif /** * iwl4965_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array @@ -1805,11 +1675,13 @@ static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, u16 ssn_idx, u8 tx_fifo) { if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) || - (IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES <= txq_id)) { + (IWL49_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues + <= txq_id)) { IWL_WARN(priv, "queue number out of range: %d, must be %d to %d\n", txq_id, IWL49_FIRST_AMPDU_QUEUE, - IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES - 1); + IWL49_FIRST_AMPDU_QUEUE + + priv->cfg->num_of_ampdu_queues - 1); return -EINVAL; } @@ -1870,11 +1742,13 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id, u16 ra_tid; if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) || - (IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES <= txq_id)) { + (IWL49_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues + <= txq_id)) { IWL_WARN(priv, "queue number out of range: %d, must be %d to %d\n", txq_id, IWL49_FIRST_AMPDU_QUEUE, - IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES - 1); + IWL49_FIRST_AMPDU_QUEUE + + priv->cfg->num_of_ampdu_queues - 1); return -EINVAL; } @@ -1944,8 +1818,9 @@ static u16 iwl4965_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; + addsta->sleep_tx_count = cmd->sleep_tx_count; addsta->reserved1 = cpu_to_le16(0); - addsta->reserved2 = cpu_to_le32(0); + addsta->reserved2 = cpu_to_le16(0); return (u16)sizeof(struct iwl4965_addsta_cmd); } @@ -1991,8 +1866,7 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]); info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags &= ~IEEE80211_TX_CTL_AMPDU; - info->flags |= iwl_is_tx_success(status) ? - IEEE80211_TX_STAT_ACK : 0; + info->flags |= iwl_tx_status_to_mac80211(status); iwl_hwrate_to_tx_control(priv, rate_n_flags, info); /* FIXME: code repetition end */ @@ -2078,7 +1952,7 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, static void iwl4965_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); @@ -2147,8 +2021,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, } } else { info->status.rates[0].count = tx_resp->failure_frame + 1; - info->flags |= iwl_is_tx_success(status) ? - IEEE80211_TX_STAT_ACK : 0; + info->flags |= iwl_tx_status_to_mac80211(status); iwl_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), info); @@ -2279,7 +2152,7 @@ static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = { .build_addsta_hcmd = iwl4965_build_addsta_hcmd, .chain_noise_reset = iwl4965_chain_noise_reset, .gain_computation = iwl4965_gain_computation, - .rts_tx_cmd_flag = iwl4965_rts_tx_cmd_flag, + .rts_tx_cmd_flag = iwlcore_rts_tx_cmd_flag, .calc_rssi = iwl4965_calc_rssi, }; @@ -2301,10 +2174,10 @@ static struct iwl_lib_ops iwl4965_lib = { .load_ucode = iwl4965_load_bsm, .dump_nic_event_log = iwl_dump_nic_event_log, .dump_nic_error_log = iwl_dump_nic_error_log, + .set_channel_switch = iwl4965_hw_channel_switch, .apm_ops = { - .init = iwl4965_apm_init, - .reset = iwl4965_apm_reset, - .stop = iwl4965_apm_stop, + .init = iwl_apm_init, + .stop = iwl_apm_stop, .config = iwl4965_nic_config, .set_pwr_src = iwl_set_pwr_src, }, @@ -2340,6 +2213,7 @@ static struct iwl_ops iwl4965_ops = { .lib = &iwl4965_lib, .hcmd = &iwl4965_hcmd, .utils = &iwl4965_hcmd_utils, + .led = &iwlagn_led_ops, }; struct iwl_cfg iwl4965_agn_cfg = { @@ -2352,30 +2226,41 @@ struct iwl_cfg iwl4965_agn_cfg = { .eeprom_ver = EEPROM_4965_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, .ops = &iwl4965_ops, + .num_of_queues = IWL49_NUM_QUEUES, + .num_of_ampdu_queues = IWL49_NUM_AMPDU_QUEUES, .mod_params = &iwl4965_mod_params, + .valid_tx_ant = ANT_AB, + .valid_rx_ant = ANT_ABC, + .pll_cfg_val = 0, + .set_l0s = true, + .use_bsm = true, .use_isr_legacy = true, .ht_greenfield_support = false, .broken_powersave = true, + .led_compensation = 61, + .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, }; /* Module firmware */ MODULE_FIRMWARE(IWL4965_MODULE_FIRMWARE(IWL4965_UCODE_API_MAX)); -module_param_named(antenna, iwl4965_mod_params.antenna, int, 0444); +module_param_named(antenna, iwl4965_mod_params.antenna, int, S_IRUGO); MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); -module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, 0444); +module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, S_IRUGO); MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); module_param_named( - disable_hw_scan, iwl4965_mod_params.disable_hw_scan, int, 0444); + disable_hw_scan, iwl4965_mod_params.disable_hw_scan, int, S_IRUGO); MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); -module_param_named(queues_num, iwl4965_mod_params.num_of_queues, int, 0444); +module_param_named(queues_num, iwl4965_mod_params.num_of_queues, int, S_IRUGO); MODULE_PARM_DESC(queues_num, "number of hw queues."); /* 11n */ -module_param_named(11n_disable, iwl4965_mod_params.disable_11n, int, 0444); +module_param_named(11n_disable, iwl4965_mod_params.disable_11n, int, S_IRUGO); MODULE_PARM_DESC(11n_disable, "disable 11n functionality"); -module_param_named(amsdu_size_8K, iwl4965_mod_params.amsdu_size_8K, int, 0444); +module_param_named(amsdu_size_8K, iwl4965_mod_params.amsdu_size_8K, + int, S_IRUGO); MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); -module_param_named(fw_restart4965, iwl4965_mod_params.restart_fw, int, 0444); +module_param_named(fw_restart4965, iwl4965_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart4965, "restart firmware in case of error"); diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 6e6f516ba404..e2f8615c8c9b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -43,6 +43,7 @@ #include "iwl-io.h" #include "iwl-sta.h" #include "iwl-helpers.h" +#include "iwl-agn-led.h" #include "iwl-5000-hw.h" #include "iwl-6000-hw.h" @@ -72,157 +73,18 @@ static const u16 iwl5000_default_queue_to_tx_fifo[] = { IWL_TX_FIFO_HCCA_2 }; -/* FIXME: same implementation as 4965 */ -static int iwl5000_apm_stop_master(struct iwl_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - /* set stop master bit */ - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); - - iwl_poll_direct_bit(priv, CSR_RESET, - CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); - - spin_unlock_irqrestore(&priv->lock, flags); - IWL_DEBUG_INFO(priv, "stop master\n"); - - return 0; -} - - -int iwl5000_apm_init(struct iwl_priv *priv) -{ - int ret = 0; - - iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); - - /* disable L0s without affecting L1 :don't wait for ICH L0s bug W/A) */ - iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); - - /* Set FH wait threshold to maximum (HW error during stress W/A) */ - iwl_set_bit(priv, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); - - /* enable HAP INTA to move device L1a -> L0s */ - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); - - if (priv->cfg->need_pll_cfg) - iwl_set_bit(priv, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL); - - /* set "initialization complete" bit to move adapter - * D0U* --> D0A* state */ - iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* wait for clock stabilization */ - ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (ret < 0) { - IWL_DEBUG_INFO(priv, "Failed to init the card\n"); - return ret; - } - - /* enable DMA */ - iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); - - udelay(20); - - /* disable L1-Active */ - iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - - return ret; -} - -/* FIXME: this is identical to 4965 */ -void iwl5000_apm_stop(struct iwl_priv *priv) -{ - unsigned long flags; - - iwl5000_apm_stop_master(priv); - - spin_lock_irqsave(&priv->lock, flags); - - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - - /* clear "init complete" move adapter D0A* --> D0U state */ - iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - spin_unlock_irqrestore(&priv->lock, flags); -} - - -int iwl5000_apm_reset(struct iwl_priv *priv) -{ - int ret = 0; - - iwl5000_apm_stop_master(priv); - - iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - - - /* FIXME: put here L1A -L0S w/a */ - - if (priv->cfg->need_pll_cfg) - iwl_set_bit(priv, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL); - - /* set "initialization complete" bit to move adapter - * D0U* --> D0A* state */ - iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* wait for clock stabilization */ - ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (ret < 0) { - IWL_DEBUG_INFO(priv, "Failed to init the card\n"); - goto out; - } - - /* enable DMA */ - iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); - - udelay(20); - - /* disable L1-Active */ - iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); -out: - - return ret; -} - - -/* NIC configuration for 5000 series and up */ +/* NIC configuration for 5000 series */ void iwl5000_nic_config(struct iwl_priv *priv) { unsigned long flags; u16 radio_cfg; - u16 lctl; spin_lock_irqsave(&priv->lock, flags); - lctl = iwl_pcie_link_ctl(priv); - - /* HW bug W/A */ - /* L1-ASPM is enabled by BIOS */ - if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN) - /* L1-APSM enabled: disable L0S */ - iwl_set_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - else - /* L1-ASPM disabled: enable L0S */ - iwl_clear_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); /* write radio config values to register */ - if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) < EEPROM_5000_RF_CFG_TYPE_MAX) + if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) < EEPROM_RF_CONFIG_TYPE_MAX) iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | EEPROM_RF_CFG_STEP_MSK(radio_cfg) | @@ -302,19 +164,22 @@ u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv) static void iwl5000_gain_computation(struct iwl_priv *priv, u32 average_noise[NUM_RX_CHAINS], u16 min_average_noise_antenna_i, - u32 min_average_noise) + u32 min_average_noise, + u8 default_chain) { int i; s32 delta_g; struct iwl_chain_noise_data *data = &priv->chain_noise_data; - /* Find Gain Code for the antennas B and C */ - for (i = 1; i < NUM_RX_CHAINS; i++) { + /* + * Find Gain Code for the chains based on "default chain" + */ + for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) { if ((data->disconn_array[i])) { data->delta_gain_code[i] = 0; continue; } - delta_g = (1000 * ((s32)average_noise[0] - + delta_g = (1000 * ((s32)average_noise[default_chain] - (s32)average_noise[i])) / 1500; /* bound gain by 2 bits value max, 3rd bit is sign */ data->delta_gain_code[i] = @@ -407,6 +272,10 @@ static struct iwl_sensitivity_ranges iwl5000_sensitivity = { .auto_corr_max_cck_mrc = 400, .nrg_th_cck = 95, .nrg_th_ofdm = 95, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, }; static struct iwl_sensitivity_ranges iwl5150_sensitivity = { @@ -429,6 +298,10 @@ static struct iwl_sensitivity_ranges iwl5150_sensitivity = { .auto_corr_max_cck_mrc = 400, .nrg_th_cck = 95, .nrg_th_ofdm = 95, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, }; const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, @@ -493,7 +366,7 @@ static int iwl5000_send_calib_cfg(struct iwl_priv *priv) static void iwl5000_rx_calib_result(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw; int len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; int index; @@ -719,16 +592,6 @@ static void iwl5000_tx_queue_set_status(struct iwl_priv *priv, scd_retry ? "BA" : "AC", txq_id, tx_fifo_id); } -static int iwl5000_send_wimax_coex(struct iwl_priv *priv) -{ - struct iwl_wimax_coex_cmd coex_cmd; - - memset(&coex_cmd, 0, sizeof(coex_cmd)); - - return iwl_send_cmd_pdu(priv, COEX_PRIORITY_TABLE_CMD, - sizeof(coex_cmd), &coex_cmd); -} - int iwl5000_alive_notify(struct iwl_priv *priv) { u32 a; @@ -746,7 +609,8 @@ int iwl5000_alive_notify(struct iwl_priv *priv) for (; a < priv->scd_base_addr + IWL50_SCD_TRANSLATE_TBL_OFFSET; a += 4) iwl_write_targ_mem(priv, a, 0); - for (; a < sizeof(u16) * priv->hw_params.max_txq_num; a += 4) + for (; a < priv->scd_base_addr + + IWL50_SCD_TRANSLATE_TBL_OFFSET_QUEUE(priv->hw_params.max_txq_num); a += 4) iwl_write_targ_mem(priv, a, 0); iwl_write_prph(priv, IWL50_SCD_DRAM_BASE_ADDR, @@ -798,9 +662,13 @@ int iwl5000_alive_notify(struct iwl_priv *priv) iwl_txq_ctx_activate(priv, i); iwl5000_tx_queue_set_status(priv, &priv->txq[i], ac, 0); } - /* TODO - need to initialize those FIFOs inside the loop above, - * not only mark them as active */ - iwl_txq_ctx_activate(priv, 4); + + /* + * TODO - need to initialize these queues and map them to FIFOs + * in the loop above, not only mark them as active. We do this + * because we want the first aggregation queue to be queue #10, + * but do not use 8 or 9 otherwise yet. + */ iwl_txq_ctx_activate(priv, 7); iwl_txq_ctx_activate(priv, 8); iwl_txq_ctx_activate(priv, 9); @@ -808,7 +676,7 @@ int iwl5000_alive_notify(struct iwl_priv *priv) spin_unlock_irqrestore(&priv->lock, flags); - iwl5000_send_wimax_coex(priv); + iwl_send_wimax_coex(priv); iwl5000_set_Xtal_calib(priv); iwl_send_calib_results(priv); @@ -818,32 +686,22 @@ int iwl5000_alive_notify(struct iwl_priv *priv) int iwl5000_hw_set_hw_params(struct iwl_priv *priv) { - if ((priv->cfg->mod_params->num_of_queues > IWL50_NUM_QUEUES) || - (priv->cfg->mod_params->num_of_queues < IWL_MIN_NUM_QUEUES)) { - IWL_ERR(priv, - "invalid queues_num, should be between %d and %d\n", - IWL_MIN_NUM_QUEUES, IWL50_NUM_QUEUES); - return -EINVAL; - } + if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES && + priv->cfg->mod_params->num_of_queues <= IWL50_NUM_QUEUES) + priv->cfg->num_of_queues = + priv->cfg->mod_params->num_of_queues; - priv->hw_params.max_txq_num = priv->cfg->mod_params->num_of_queues; + priv->hw_params.max_txq_num = priv->cfg->num_of_queues; priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM; priv->hw_params.scd_bc_tbls_size = - IWL50_NUM_QUEUES * sizeof(struct iwl5000_scd_bc_tbl); + priv->cfg->num_of_queues * + sizeof(struct iwl5000_scd_bc_tbl); priv->hw_params.tfd_size = sizeof(struct iwl_tfd); priv->hw_params.max_stations = IWL5000_STATION_COUNT; priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID; - switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { - case CSR_HW_REV_TYPE_6x00: - case CSR_HW_REV_TYPE_6x50: - priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE; - priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE; - break; - default: - priv->hw_params.max_data_size = IWL50_RTC_DATA_SIZE; - priv->hw_params.max_inst_size = IWL50_RTC_INST_SIZE; - } + priv->hw_params.max_data_size = IWL50_RTC_DATA_SIZE; + priv->hw_params.max_inst_size = IWL50_RTC_INST_SIZE; priv->hw_params.max_bsm_size = 0; priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | @@ -989,11 +847,13 @@ int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, u16 ra_tid; if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) || - (IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) { + (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues + <= txq_id)) { IWL_WARN(priv, "queue number out of range: %d, must be %d to %d\n", txq_id, IWL50_FIRST_AMPDU_QUEUE, - IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES - 1); + IWL50_FIRST_AMPDU_QUEUE + + priv->cfg->num_of_ampdu_queues - 1); return -EINVAL; } @@ -1047,11 +907,13 @@ int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, u16 ssn_idx, u8 tx_fifo) { if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) || - (IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) { + (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues + <= txq_id)) { IWL_ERR(priv, "queue number out of range: %d, must be %d to %d\n", txq_id, IWL50_FIRST_AMPDU_QUEUE, - IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES - 1); + IWL50_FIRST_AMPDU_QUEUE + + priv->cfg->num_of_ampdu_queues - 1); return -EINVAL; } @@ -1132,8 +994,7 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv, info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]); info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags &= ~IEEE80211_TX_CTL_AMPDU; - info->flags |= iwl_is_tx_success(status) ? - IEEE80211_TX_STAT_ACK : 0; + info->flags |= iwl_tx_status_to_mac80211(status); iwl_hwrate_to_tx_control(priv, rate_n_flags, info); /* FIXME: code repetition end */ @@ -1218,7 +1079,7 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv, static void iwl5000_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); @@ -1278,8 +1139,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, BUG_ON(txq_id != txq->swq_id); info->status.rates[0].count = tx_resp->failure_frame + 1; - info->flags |= iwl_is_tx_success(status) ? - IEEE80211_TX_STAT_ACK : 0; + info->flags |= iwl_tx_status_to_mac80211(status); iwl_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), info); @@ -1389,6 +1249,22 @@ int iwl5000_send_tx_power(struct iwl_priv *priv) /* half dBm need to multiply */ tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); + + if (priv->tx_power_lmt_in_half_dbm && + priv->tx_power_lmt_in_half_dbm < tx_power_cmd.global_lmt) { + /* + * For the newer devices which using enhanced/extend tx power + * table in EEPROM, the format is in half dBm. driver need to + * convert to dBm format before report to mac80211. + * By doing so, there is a possibility of 1/2 dBm resolution + * lost. driver will perform "round-up" operation before + * reporting, but it will cause 1/2 dBm tx power over the + * regulatory limit. Perform the checking here, if the + * "tx_power_user_lmt" is higher than EEPROM value (in + * half-dBm format), lower the tx power based on EEPROM + */ + tx_power_cmd.global_lmt = priv->tx_power_lmt_in_half_dbm; + } tx_power_cmd.flags = IWL50_TX_POWER_NO_CLOSED; tx_power_cmd.srv_chan_lmt = IWL50_TX_POWER_AUTO; @@ -1459,6 +1335,24 @@ int iwl5000_calc_rssi(struct iwl_priv *priv, return max_rssi - agc - IWL49_RSSI_OFFSET; } +static int iwl5000_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) +{ + struct iwl_tx_ant_config_cmd tx_ant_cmd = { + .valid = cpu_to_le32(valid_tx_ant), + }; + + if (IWL_UCODE_API(priv->ucode_ver) > 1) { + IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); + return iwl_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, + sizeof(struct iwl_tx_ant_config_cmd), + &tx_ant_cmd); + } else { + IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n"); + return -EOPNOTSUPP; + } +} + + #define IWL5000_UCODE_GET(item) \ static u32 iwl5000_ucode_get_##item(const struct iwl_ucode_header *ucode,\ u32 api_ver) \ @@ -1497,10 +1391,43 @@ IWL5000_UCODE_GET(init_size); IWL5000_UCODE_GET(init_data_size); IWL5000_UCODE_GET(boot_size); +static int iwl5000_hw_channel_switch(struct iwl_priv *priv, u16 channel) +{ + struct iwl5000_channel_switch_cmd cmd; + const struct iwl_channel_info *ch_info; + struct iwl_host_cmd hcmd = { + .id = REPLY_CHANNEL_SWITCH, + .len = sizeof(cmd), + .flags = CMD_SIZE_HUGE, + .data = &cmd, + }; + + IWL_DEBUG_11H(priv, "channel switch from %d to %d\n", + priv->active_rxon.channel, channel); + cmd.band = priv->band == IEEE80211_BAND_2GHZ; + cmd.channel = cpu_to_le16(channel); + cmd.rxon_flags = priv->staging_rxon.flags; + cmd.rxon_filter_flags = priv->staging_rxon.filter_flags; + cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); + ch_info = iwl_get_channel_info(priv, priv->band, channel); + if (ch_info) + cmd.expect_beacon = is_channel_radar(ch_info); + else { + IWL_ERR(priv, "invalid channel switch from %u to %u\n", + priv->active_rxon.channel, channel); + return -EFAULT; + } + priv->switch_rxon.channel = cpu_to_le16(channel); + priv->switch_rxon.switch_in_progress = true; + + return iwl_send_cmd_sync(priv, &hcmd); +} + struct iwl_hcmd_ops iwl5000_hcmd = { .rxon_assoc = iwl5000_send_rxon_assoc, .commit_rxon = iwl_commit_rxon, .set_rxon_chain = iwl_set_rxon_chain, + .set_tx_ant = iwl5000_send_tx_ant_config, }; struct iwl_hcmd_utils_ops iwl5000_hcmd_utils = { @@ -1543,10 +1470,10 @@ struct iwl_lib_ops iwl5000_lib = { .alive_notify = iwl5000_alive_notify, .send_tx_power = iwl5000_send_tx_power, .update_chain_flags = iwl_update_chain_flags, + .set_channel_switch = iwl5000_hw_channel_switch, .apm_ops = { - .init = iwl5000_apm_init, - .reset = iwl5000_apm_reset, - .stop = iwl5000_apm_stop, + .init = iwl_apm_init, + .stop = iwl_apm_stop, .config = iwl5000_nic_config, .set_pwr_src = iwl_set_pwr_src, }, @@ -1595,10 +1522,10 @@ static struct iwl_lib_ops iwl5150_lib = { .alive_notify = iwl5000_alive_notify, .send_tx_power = iwl5000_send_tx_power, .update_chain_flags = iwl_update_chain_flags, + .set_channel_switch = iwl5000_hw_channel_switch, .apm_ops = { - .init = iwl5000_apm_init, - .reset = iwl5000_apm_reset, - .stop = iwl5000_apm_stop, + .init = iwl_apm_init, + .stop = iwl_apm_stop, .config = iwl5000_nic_config, .set_pwr_src = iwl_set_pwr_src, }, @@ -1627,11 +1554,12 @@ static struct iwl_lib_ops iwl5150_lib = { }, }; -struct iwl_ops iwl5000_ops = { +static struct iwl_ops iwl5000_ops = { .ucode = &iwl5000_ucode, .lib = &iwl5000_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl5000_hcmd_utils, + .led = &iwlagn_led_ops, }; static struct iwl_ops iwl5150_ops = { @@ -1639,11 +1567,10 @@ static struct iwl_ops iwl5150_ops = { .lib = &iwl5150_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl5000_hcmd_utils, + .led = &iwlagn_led_ops, }; struct iwl_mod_params iwl50_mod_params = { - .num_of_queues = IWL50_NUM_QUEUES, - .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .amsdu_size_8K = 1, .restart_fw = 1, /* the rest are 0 by default */ @@ -1660,28 +1587,41 @@ struct iwl_cfg iwl5300_agn_cfg = { .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, - .need_pll_cfg = true, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, .ht_greenfield_support = true, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, }; -struct iwl_cfg iwl5100_bg_cfg = { - .name = "5100BG", +struct iwl_cfg iwl5100_bgn_cfg = { + .name = "5100BGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, - .sku = IWL_SKU_G, + .sku = IWL_SKU_G|IWL_SKU_N, .ops = &iwl5000_ops, .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, - .need_pll_cfg = true, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, .ht_greenfield_support = true, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, }; struct iwl_cfg iwl5100_abg_cfg = { @@ -1694,11 +1634,16 @@ struct iwl_cfg iwl5100_abg_cfg = { .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, - .need_pll_cfg = true, - .ht_greenfield_support = true, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, }; struct iwl_cfg iwl5100_agn_cfg = { @@ -1711,11 +1656,18 @@ struct iwl_cfg iwl5100_agn_cfg = { .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, - .need_pll_cfg = true, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, .ht_greenfield_support = true, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, }; struct iwl_cfg iwl5350_agn_cfg = { @@ -1728,11 +1680,18 @@ struct iwl_cfg iwl5350_agn_cfg = { .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, .eeprom_ver = EEPROM_5050_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, - .need_pll_cfg = true, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, .ht_greenfield_support = true, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, }; struct iwl_cfg iwl5150_agn_cfg = { @@ -1745,24 +1704,54 @@ struct iwl_cfg iwl5150_agn_cfg = { .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, .eeprom_ver = EEPROM_5050_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_A, .valid_rx_ant = ANT_AB, - .need_pll_cfg = true, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, .ht_greenfield_support = true, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, +}; + +struct iwl_cfg iwl5150_abg_cfg = { + .name = "5150ABG", + .fw_name_pre = IWL5150_FW_PRE, + .ucode_api_max = IWL5150_UCODE_API_MAX, + .ucode_api_min = IWL5150_UCODE_API_MIN, + .sku = IWL_SKU_A|IWL_SKU_G, + .ops = &iwl5150_ops, + .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_ver = EEPROM_5050_EEPROM_VERSION, + .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, + .mod_params = &iwl50_mod_params, + .valid_tx_ant = ANT_A, + .valid_rx_ant = ANT_AB, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .set_l0s = true, + .use_bsm = false, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, }; MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX)); -module_param_named(swcrypto50, iwl50_mod_params.sw_crypto, bool, 0444); +module_param_named(swcrypto50, iwl50_mod_params.sw_crypto, bool, S_IRUGO); MODULE_PARM_DESC(swcrypto50, "using software crypto engine (default 0 [hardware])\n"); -module_param_named(queues_num50, iwl50_mod_params.num_of_queues, int, 0444); +module_param_named(queues_num50, iwl50_mod_params.num_of_queues, int, S_IRUGO); MODULE_PARM_DESC(queues_num50, "number of hw queues in 50xx series"); -module_param_named(11n_disable50, iwl50_mod_params.disable_11n, int, 0444); +module_param_named(11n_disable50, iwl50_mod_params.disable_11n, int, S_IRUGO); MODULE_PARM_DESC(11n_disable50, "disable 50XX 11n functionality"); -module_param_named(amsdu_size_8K50, iwl50_mod_params.amsdu_size_8K, int, 0444); +module_param_named(amsdu_size_8K50, iwl50_mod_params.amsdu_size_8K, + int, S_IRUGO); MODULE_PARM_DESC(amsdu_size_8K50, "enable 8K amsdu size in 50XX series"); -module_param_named(fw_restart50, iwl50_mod_params.restart_fw, int, 0444); +module_param_named(fw_restart50, iwl50_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart50, "restart firmware in case of error"); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 1473452ba22f..74e571049273 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -44,14 +44,16 @@ #include "iwl-sta.h" #include "iwl-helpers.h" #include "iwl-5000-hw.h" +#include "iwl-6000-hw.h" +#include "iwl-agn-led.h" /* Highest firmware API version supported */ #define IWL6000_UCODE_API_MAX 4 #define IWL6050_UCODE_API_MAX 4 /* Lowest firmware API version supported */ -#define IWL6000_UCODE_API_MIN 1 -#define IWL6050_UCODE_API_MIN 1 +#define IWL6000_UCODE_API_MIN 4 +#define IWL6050_UCODE_API_MIN 4 #define IWL6000_FW_PRE "iwlwifi-6000-" #define _IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE #api ".ucode" @@ -71,14 +73,24 @@ static void iwl6000_set_ct_threshold(struct iwl_priv *priv) /* NIC configuration for 6000 series */ static void iwl6000_nic_config(struct iwl_priv *priv) { - iwl5000_nic_config(priv); + u16 radio_cfg; + + radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); + + /* write radio config values to register */ + if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) + iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | + EEPROM_RF_CFG_STEP_MSK(radio_cfg) | + EEPROM_RF_CFG_DASH_MSK(radio_cfg)); + + /* set CSR_HW_CONFIG_REG for uCode use */ + iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); /* no locking required for register write */ - if (priv->cfg->pa_type == IWL_PA_HYBRID) { - /* 2x2 hybrid phy type */ - iwl_write32(priv, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB); - } else if (priv->cfg->pa_type == IWL_PA_INTERNAL) { + if (priv->cfg->pa_type == IWL_PA_INTERNAL) { /* 2x2 IPA phy type */ iwl_write32(priv, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); @@ -86,8 +98,109 @@ static void iwl6000_nic_config(struct iwl_priv *priv) /* else do nothing, uCode configured */ } +static struct iwl_sensitivity_ranges iwl6000_sensitivity = { + .min_nrg_cck = 97, + .max_nrg_cck = 0, /* not used, set to 0 */ + .auto_corr_min_ofdm = 80, + .auto_corr_min_ofdm_mrc = 128, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 192, + + .auto_corr_max_ofdm = 145, + .auto_corr_max_ofdm_mrc = 232, + .auto_corr_max_ofdm_x1 = 145, + .auto_corr_max_ofdm_mrc_x1 = 232, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 175, + .auto_corr_min_cck_mrc = 160, + .auto_corr_max_cck_mrc = 310, + .nrg_th_cck = 97, + .nrg_th_ofdm = 100, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static int iwl6000_hw_set_hw_params(struct iwl_priv *priv) +{ + if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES && + priv->cfg->mod_params->num_of_queues <= IWL50_NUM_QUEUES) + priv->cfg->num_of_queues = + priv->cfg->mod_params->num_of_queues; + + priv->hw_params.max_txq_num = priv->cfg->num_of_queues; + priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM; + priv->hw_params.scd_bc_tbls_size = + priv->cfg->num_of_queues * + sizeof(struct iwl5000_scd_bc_tbl); + priv->hw_params.tfd_size = sizeof(struct iwl_tfd); + priv->hw_params.max_stations = IWL5000_STATION_COUNT; + priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID; + + priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE; + priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE; + + priv->hw_params.max_bsm_size = 0; + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | + BIT(IEEE80211_BAND_5GHZ); + priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR; + + priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); + priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant); + priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; + priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant; + + if (priv->cfg->ops->lib->temp_ops.set_ct_kill) + priv->cfg->ops->lib->temp_ops.set_ct_kill(priv); + + /* Set initial sensitivity parameters */ + /* Set initial calibration set */ + priv->hw_params.sens = &iwl6000_sensitivity; + priv->hw_params.calib_init_cfg = + BIT(IWL_CALIB_XTAL) | + BIT(IWL_CALIB_LO) | + BIT(IWL_CALIB_TX_IQ) | + BIT(IWL_CALIB_BASE_BAND); + return 0; +} + +static int iwl6000_hw_channel_switch(struct iwl_priv *priv, u16 channel) +{ + struct iwl6000_channel_switch_cmd cmd; + const struct iwl_channel_info *ch_info; + struct iwl_host_cmd hcmd = { + .id = REPLY_CHANNEL_SWITCH, + .len = sizeof(cmd), + .flags = CMD_SIZE_HUGE, + .data = &cmd, + }; + + IWL_DEBUG_11H(priv, "channel switch from %d to %d\n", + priv->active_rxon.channel, channel); + + cmd.band = priv->band == IEEE80211_BAND_2GHZ; + cmd.channel = cpu_to_le16(channel); + cmd.rxon_flags = priv->staging_rxon.flags; + cmd.rxon_filter_flags = priv->staging_rxon.filter_flags; + cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); + ch_info = iwl_get_channel_info(priv, priv->band, channel); + if (ch_info) + cmd.expect_beacon = is_channel_radar(ch_info); + else { + IWL_ERR(priv, "invalid channel switch from %u to %u\n", + priv->active_rxon.channel, channel); + return -EFAULT; + } + priv->switch_rxon.channel = cpu_to_le16(channel); + priv->switch_rxon.switch_in_progress = true; + + return iwl_send_cmd_sync(priv, &hcmd); +} + static struct iwl_lib_ops iwl6000_lib = { - .set_hw_params = iwl5000_hw_set_hw_params, + .set_hw_params = iwl6000_hw_set_hw_params, .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, .txq_set_sched = iwl5000_txq_set_sched, @@ -106,10 +219,10 @@ static struct iwl_lib_ops iwl6000_lib = { .alive_notify = iwl5000_alive_notify, .send_tx_power = iwl5000_send_tx_power, .update_chain_flags = iwl_update_chain_flags, + .set_channel_switch = iwl6000_hw_channel_switch, .apm_ops = { - .init = iwl5000_apm_init, - .reset = iwl5000_apm_reset, - .stop = iwl5000_apm_stop, + .init = iwl_apm_init, + .stop = iwl_apm_stop, .config = iwl6000_nic_config, .set_pwr_src = iwl_set_pwr_src, }, @@ -139,25 +252,33 @@ static struct iwl_lib_ops iwl6000_lib = { }, }; -static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = { +static struct iwl_ops iwl6000_ops = { + .ucode = &iwl5000_ucode, + .lib = &iwl6000_lib, + .hcmd = &iwl5000_hcmd, + .utils = &iwl5000_hcmd_utils, + .led = &iwlagn_led_ops, +}; + +static struct iwl_hcmd_utils_ops iwl6050_hcmd_utils = { .get_hcmd_size = iwl5000_get_hcmd_size, .build_addsta_hcmd = iwl5000_build_addsta_hcmd, .rts_tx_cmd_flag = iwl5000_rts_tx_cmd_flag, .calc_rssi = iwl5000_calc_rssi, }; -static struct iwl_ops iwl6000_ops = { +static struct iwl_ops iwl6050_ops = { .ucode = &iwl5000_ucode, .lib = &iwl6000_lib, .hcmd = &iwl5000_hcmd, - .utils = &iwl6000_hcmd_utils, + .utils = &iwl6050_hcmd_utils, + .led = &iwlagn_led_ops, }; - /* - * "h": Hybrid configuration, use both internal and external Power Amplifier + * "i": Internal configuration, use internal Power Amplifier */ -struct iwl_cfg iwl6000h_2agn_cfg = { +struct iwl_cfg iwl6000i_2agn_cfg = { .name = "6000 Series 2x2 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, @@ -165,41 +286,85 @@ struct iwl_cfg iwl6000h_2agn_cfg = { .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, .eeprom_size = OTP_LOW_IMAGE_SIZE, - .eeprom_ver = EEPROM_5000_EEPROM_VERSION, + .eeprom_ver = EEPROM_6000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, - .valid_tx_ant = ANT_AB, - .valid_rx_ant = ANT_AB, - .need_pll_cfg = false, - .pa_type = IWL_PA_HYBRID, + .valid_tx_ant = ANT_BC, + .valid_rx_ant = ANT_BC, + .pll_cfg_val = 0, + .set_l0s = true, + .use_bsm = false, + .pa_type = IWL_PA_INTERNAL, .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .ht_greenfield_support = true, + .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .supports_idle = true, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, }; -/* - * "i": Internal configuration, use internal Power Amplifier - */ -struct iwl_cfg iwl6000i_2agn_cfg = { - .name = "6000 Series 2x2 AGN", +struct iwl_cfg iwl6000i_2abg_cfg = { + .name = "6000 Series 2x2 ABG", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, - .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, + .sku = IWL_SKU_A|IWL_SKU_G, .ops = &iwl6000_ops, .eeprom_size = OTP_LOW_IMAGE_SIZE, - .eeprom_ver = EEPROM_5000_EEPROM_VERSION, + .eeprom_ver = EEPROM_6000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_BC, .valid_rx_ant = ANT_BC, - .need_pll_cfg = false, + .pll_cfg_val = 0, + .set_l0s = true, + .use_bsm = false, .pa_type = IWL_PA_INTERNAL, .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .ht_greenfield_support = true, - .use_rts_for_ht = true, /* use rts/cts protection */ + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .supports_idle = true, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, +}; + +struct iwl_cfg iwl6000i_2bg_cfg = { + .name = "6000 Series 2x2 BG", + .fw_name_pre = IWL6000_FW_PRE, + .ucode_api_max = IWL6000_UCODE_API_MAX, + .ucode_api_min = IWL6000_UCODE_API_MIN, + .sku = IWL_SKU_G, + .ops = &iwl6000_ops, + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .eeprom_ver = EEPROM_6000_EEPROM_VERSION, + .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, + .mod_params = &iwl50_mod_params, + .valid_tx_ant = ANT_BC, + .valid_rx_ant = ANT_BC, + .pll_cfg_val = 0, + .set_l0s = true, + .use_bsm = false, + .pa_type = IWL_PA_INTERNAL, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .supports_idle = true, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, }; struct iwl_cfg iwl6050_2agn_cfg = { @@ -208,61 +373,89 @@ struct iwl_cfg iwl6050_2agn_cfg = { .ucode_api_max = IWL6050_UCODE_API_MAX, .ucode_api_min = IWL6050_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, - .ops = &iwl6000_ops, + .ops = &iwl6050_ops, .eeprom_size = OTP_LOW_IMAGE_SIZE, - .eeprom_ver = EEPROM_5000_EEPROM_VERSION, + .eeprom_ver = EEPROM_6050_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_AB, .valid_rx_ant = ANT_AB, - .need_pll_cfg = false, + .pll_cfg_val = 0, + .set_l0s = true, + .use_bsm = false, .pa_type = IWL_PA_SYSTEM, - .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .max_ll_items = OTP_MAX_LL_ITEMS_6x50, .shadow_ram_support = true, .ht_greenfield_support = true, + .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .supports_idle = true, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DYNAMIC, }; -struct iwl_cfg iwl6000_3agn_cfg = { - .name = "6000 Series 3x3 AGN", - .fw_name_pre = IWL6000_FW_PRE, - .ucode_api_max = IWL6000_UCODE_API_MAX, - .ucode_api_min = IWL6000_UCODE_API_MIN, - .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, - .ops = &iwl6000_ops, +struct iwl_cfg iwl6050_2abg_cfg = { + .name = "6050 Series 2x2 ABG", + .fw_name_pre = IWL6050_FW_PRE, + .ucode_api_max = IWL6050_UCODE_API_MAX, + .ucode_api_min = IWL6050_UCODE_API_MIN, + .sku = IWL_SKU_A|IWL_SKU_G, + .ops = &iwl6050_ops, .eeprom_size = OTP_LOW_IMAGE_SIZE, - .eeprom_ver = EEPROM_5000_EEPROM_VERSION, + .eeprom_ver = EEPROM_6050_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, - .valid_tx_ant = ANT_ABC, - .valid_rx_ant = ANT_ABC, - .need_pll_cfg = false, + .valid_tx_ant = ANT_AB, + .valid_rx_ant = ANT_AB, + .pll_cfg_val = 0, + .set_l0s = true, + .use_bsm = false, .pa_type = IWL_PA_SYSTEM, - .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .max_ll_items = OTP_MAX_LL_ITEMS_6x50, .shadow_ram_support = true, .ht_greenfield_support = true, - .use_rts_for_ht = true, /* use rts/cts protection */ + .led_compensation = 51, + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .supports_idle = true, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, }; -struct iwl_cfg iwl6050_3agn_cfg = { - .name = "6050 Series 3x3 AGN", - .fw_name_pre = IWL6050_FW_PRE, - .ucode_api_max = IWL6050_UCODE_API_MAX, - .ucode_api_min = IWL6050_UCODE_API_MIN, +struct iwl_cfg iwl6000_3agn_cfg = { + .name = "6000 Series 3x3 AGN", + .fw_name_pre = IWL6000_FW_PRE, + .ucode_api_max = IWL6000_UCODE_API_MAX, + .ucode_api_min = IWL6000_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, .eeprom_size = OTP_LOW_IMAGE_SIZE, - .eeprom_ver = EEPROM_5000_EEPROM_VERSION, + .eeprom_ver = EEPROM_6000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, + .num_of_queues = IWL50_NUM_QUEUES, + .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, - .need_pll_cfg = false, + .pll_cfg_val = 0, + .set_l0s = true, + .use_bsm = false, .pa_type = IWL_PA_SYSTEM, .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .ht_greenfield_support = true, + .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ + .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .supports_idle = true, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-led.c b/drivers/net/wireless/iwlwifi/iwl-agn-led.c new file mode 100644 index 000000000000..3bccba20f6da --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-agn-led.c @@ -0,0 +1,85 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <net/mac80211.h> +#include <linux/etherdevice.h> +#include <asm/unaligned.h> + +#include "iwl-commands.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-agn-led.h" + +/* Send led command */ +static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_LEDS_CMD, + .len = sizeof(struct iwl_led_cmd), + .data = led_cmd, + .flags = CMD_ASYNC, + .callback = NULL, + }; + u32 reg; + + reg = iwl_read32(priv, CSR_LED_REG); + if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) + iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); + + return iwl_send_cmd(priv, &cmd); +} + +/* Set led register off */ +static int iwl_led_on_reg(struct iwl_priv *priv) +{ + IWL_DEBUG_LED(priv, "led on\n"); + iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON); + return 0; +} + +/* Set led register off */ +static int iwl_led_off_reg(struct iwl_priv *priv) +{ + IWL_DEBUG_LED(priv, "LED Reg off\n"); + iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF); + return 0; +} + +const struct iwl_led_ops iwlagn_led_ops = { + .cmd = iwl_send_led_cmd, + .on = iwl_led_on_reg, + .off = iwl_led_off_reg, +}; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-led.h b/drivers/net/wireless/iwlwifi/iwl-agn-led.h new file mode 100644 index 000000000000..ab55f92a161d --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-agn-led.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_agn_led_h__ +#define __iwl_agn_led_h__ + +extern const struct iwl_led_ops iwlagn_led_ops; + +#endif /* __iwl_agn_led_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 81726ee32858..fe511cbf012e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -75,104 +75,6 @@ static const u8 ant_toggle_lookup[] = { /*ANT_ABC -> */ ANT_ABC, }; -/** - * struct iwl_rate_scale_data -- tx success history for one rate - */ -struct iwl_rate_scale_data { - u64 data; /* bitmap of successful frames */ - s32 success_counter; /* number of frames successful */ - s32 success_ratio; /* per-cent * 128 */ - s32 counter; /* number of frames attempted */ - s32 average_tpt; /* success ratio * expected throughput */ - unsigned long stamp; -}; - -/** - * struct iwl_scale_tbl_info -- tx params and success history for all rates - * - * There are two of these in struct iwl_lq_sta, - * one for "active", and one for "search". - */ -struct iwl_scale_tbl_info { - enum iwl_table_type lq_type; - u8 ant_type; - u8 is_SGI; /* 1 = short guard interval */ - u8 is_ht40; /* 1 = 40 MHz channel width */ - u8 is_dup; /* 1 = duplicated data streams */ - u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ - u8 max_search; /* maximun number of tables we can search */ - s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - u32 current_rate; /* rate_n_flags, uCode API format */ - struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ -}; - -struct iwl_traffic_load { - unsigned long time_stamp; /* age of the oldest statistics */ - u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time - * slice */ - u32 total; /* total num of packets during the - * last TID_MAX_TIME_DIFF */ - u8 queue_count; /* number of queues that has - * been used since the last cleanup */ - u8 head; /* start of the circular buffer */ -}; - -/** - * struct iwl_lq_sta -- driver's rate scaling private structure - * - * Pointer to this gets passed back and forth between driver and mac80211. - */ -struct iwl_lq_sta { - u8 active_tbl; /* index of active table, range 0-1 */ - u8 enable_counter; /* indicates HT mode */ - u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ - u8 search_better_tbl; /* 1: currently trying alternate mode */ - s32 last_tpt; - - /* The following determine when to search for a new mode */ - u32 table_count_limit; - u32 max_failure_limit; /* # failed frames before new search */ - u32 max_success_limit; /* # successful frames before new search */ - u32 table_count; - u32 total_failed; /* total failed frames, any/all rates */ - u32 total_success; /* total successful frames, any/all rates */ - u64 flush_timer; /* time staying in mode before new search */ - - u8 action_counter; /* # mode-switch actions tried */ - u8 is_green; - u8 is_dup; - enum ieee80211_band band; - u8 ibss_sta_added; - - /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ - u32 supp_rates; - u16 active_legacy_rate; - u16 active_siso_rate; - u16 active_mimo2_rate; - u16 active_mimo3_rate; - u16 active_rate_basic; - s8 max_rate_idx; /* Max rate set by user */ - u8 missed_rate_counter; - - struct iwl_link_quality_cmd lq; - struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - struct iwl_traffic_load load[TID_MAX_LOAD_COUNT]; - u8 tx_agg_tid_en; -#ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_scale_table_file; - struct dentry *rs_sta_dbgfs_stats_table_file; - struct dentry *rs_sta_dbgfs_rate_scale_data_file; - struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; - u32 dbg_fixed_rate; -#endif - struct iwl_priv *drv; - - /* used to be in sta_info */ - int last_txrate_idx; - /* last tx rate_n_flags */ - u32 last_rate_n_flags; -}; - static void rs_rate_scale_perform(struct iwl_priv *priv, struct sk_buff *skb, struct ieee80211_sta *sta, @@ -190,84 +92,78 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, {} #endif -/* - * Expected throughput metrics for following rates: - * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits - * "G" is the only table that supports CCK (the first 4 rates). +/** + * The following tables contain the expected throughput metrics for all rates + * + * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits + * + * where invalid entries are zeros. + * + * CCK rates are only valid in legacy table and will only be used in G + * (2.4 GHz) band. */ -static s32 expected_tpt_A[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186 -}; - -static s32 expected_tpt_G[IWL_RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 186 -}; - -static s32 expected_tpt_siso20MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 42, 42, 76, 102, 124, 159, 183, 193, 202 -}; - -static s32 expected_tpt_siso20MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 46, 46, 82, 110, 132, 168, 192, 202, 211 -}; - -static s32 expected_tpt_mimo2_20MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 74, 74, 123, 155, 179, 214, 236, 244, 251 -}; - -static s32 expected_tpt_mimo2_20MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 81, 81, 131, 164, 188, 222, 243, 251, 257 +static s32 expected_tpt_legacy[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 }; -static s32 expected_tpt_siso40MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 77, 77, 127, 160, 184, 220, 242, 250, 257 +static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 42, 0, 76, 102, 124, 158, 183, 193, 202}, /* Norm */ + {0, 0, 0, 0, 46, 0, 82, 110, 132, 167, 192, 202, 210}, /* SGI */ + {0, 0, 0, 0, 48, 0, 93, 135, 176, 251, 319, 351, 381}, /* AGG */ + {0, 0, 0, 0, 53, 0, 102, 149, 193, 275, 348, 381, 413}, /* AGG+SGI */ }; -static s32 expected_tpt_siso40MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 83, 83, 135, 169, 193, 229, 250, 257, 264 +static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ + {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ + {0, 0, 0, 0, 96, 0, 182, 259, 328, 451, 553, 598, 640}, /* AGG */ + {0, 0, 0, 0, 106, 0, 199, 282, 357, 487, 593, 640, 683}, /* AGG+SGI */ }; -static s32 expected_tpt_mimo2_40MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 123, 123, 182, 214, 235, 264, 279, 285, 289 +static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250}, /* Norm */ + {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256}, /* SGI */ + {0, 0, 0, 0, 92, 0, 175, 250, 317, 436, 534, 578, 619}, /* AGG */ + {0, 0, 0, 0, 102, 0, 192, 273, 344, 470, 573, 619, 660}, /* AGG+SGI*/ }; -static s32 expected_tpt_mimo2_40MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 131, 131, 191, 222, 242, 270, 284, 289, 293 +static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ + {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ + {0, 0, 0, 0, 180, 0, 327, 446, 545, 708, 828, 878, 922}, /* AGG */ + {0, 0, 0, 0, 197, 0, 355, 481, 584, 752, 872, 922, 966}, /* AGG+SGI */ }; -/* Expected throughput metric MIMO3 */ -static s32 expected_tpt_mimo3_20MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 99, 99, 153, 186, 208, 239, 256, 263, 268 +static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ + {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ + {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ + {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ }; -static s32 expected_tpt_mimo3_20MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 106, 106, 162, 194, 215, 246, 262, 268, 273 -}; - -static s32 expected_tpt_mimo3_40MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 152, 152, 211, 239, 255, 279, 290, 294, 297 -}; - -static s32 expected_tpt_mimo3_40MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 160, 160, 219, 245, 261, 284, 294, 297, 300 +static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ + {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ + {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ + {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */ }; /* mbps, mcs */ const static struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { - {"1", ""}, - {"2", ""}, - {"5.5", ""}, - {"11", ""}, - {"6", "BPSK 1/2"}, - {"9", "BPSK 1/2"}, - {"12", "QPSK 1/2"}, - {"18", "QPSK 3/4"}, - {"24", "16QAM 1/2"}, - {"36", "16QAM 3/4"}, - {"48", "64QAM 2/3"}, - {"54", "64QAM 3/4"}, - {"60", "64QAM 5/6"} + { "1", "BPSK DSSS"}, + { "2", "QPSK DSSS"}, + {"5.5", "BPSK CCK"}, + { "11", "QPSK CCK"}, + { "6", "BPSK 1/2"}, + { "9", "BPSK 1/2"}, + { "12", "QPSK 1/2"}, + { "18", "QPSK 3/4"}, + { "24", "16QAM 1/2"}, + { "36", "16QAM 3/4"}, + { "48", "64QAM 2/3"}, + { "54", "64QAM 3/4"}, + { "60", "64QAM 5/6"}, }; #define MCS_INDEX_PER_STREAM (8) @@ -405,7 +301,7 @@ static void rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, if (rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) { IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); - ieee80211_start_tx_ba_session(priv->hw, sta->addr, tid); + ieee80211_start_tx_ba_session(sta, tid); } } @@ -444,7 +340,7 @@ static inline int get_num_of_ant_from_rate(u32 rate_n_flags) * packets. */ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows, - int scale_index, s32 tpt, int retries, + int scale_index, s32 tpt, int attempts, int successes) { struct iwl_rate_scale_data *window = NULL; @@ -454,7 +350,7 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows, if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) return -EINVAL; - /* Select data for current tx bit rate */ + /* Select window for current tx bit rate */ window = &(windows[scale_index]); /* @@ -465,7 +361,7 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows, * subtract "1" from the success counter (this is the main reason * we keep these bitmaps!). */ - while (retries > 0) { + while (attempts > 0) { if (window->counter >= IWL_RATE_MAX_WINDOW) { /* remove earliest */ @@ -480,17 +376,17 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows, /* Increment frames-attempted counter */ window->counter++; - /* Shift bitmap by one frame (throw away oldest history), - * OR in "1", and increment "success" if this - * frame was successful. */ + /* Shift bitmap by one frame to throw away oldest history */ window->data <<= 1; + + /* Mark the most recent #successes attempts as successful */ if (successes > 0) { window->success_counter++; window->data |= 0x1; successes--; } - retries--; + attempts--; } /* Calculate current success ratio, avoid divide-by-0! */ @@ -671,7 +567,7 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, * there are no non-GF stations present in the BSS. */ static inline u8 rs_use_green(struct ieee80211_sta *sta, - struct iwl_ht_info *ht_conf) + struct iwl_ht_config *ht_conf) { return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && !(ht_conf->non_GF_STA_present); @@ -821,27 +717,45 @@ out: } /* + * Simple function to compare two rate scale table types + */ +static bool table_type_matches(struct iwl_scale_tbl_info *a, + struct iwl_scale_tbl_info *b) +{ + return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && + (a->is_SGI == b->is_SGI); +} +/* + * Static function to get the expected throughput from an iwl_scale_tbl_info + * that wraps a NULL pointer check + */ +static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) +{ + if (tbl->expected_tpt) + return tbl->expected_tpt[rs_index]; + return 0; +} + +/* * mac80211 sends us Tx status */ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) { - int status; - u8 retries; - int rs_index, mac_index, index = 0; + int legacy_success; + int retries; + int rs_index, mac_index, i; struct iwl_lq_sta *lq_sta = priv_sta; struct iwl_link_quality_cmd *table; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct iwl_priv *priv = (struct iwl_priv *)priv_r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_rate_scale_data *window = NULL; - struct iwl_rate_scale_data *search_win = NULL; enum mac80211_rate_control_flags mac_flags; u32 tx_rate; struct iwl_scale_tbl_info tbl_type; - struct iwl_scale_tbl_info *curr_tbl, *search_tbl; - u8 active_index = 0; + struct iwl_scale_tbl_info *curr_tbl, *other_tbl; s32 tpt = 0; IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); @@ -850,30 +764,14 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, info->flags & IEEE80211_TX_CTL_NO_ACK) return; - /* This packet was aggregated but doesn't carry rate scale info */ + /* This packet was aggregated but doesn't carry status info */ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && !(info->flags & IEEE80211_TX_STAT_AMPDU)) return; - if (info->flags & IEEE80211_TX_STAT_AMPDU) - retries = 0; - else - retries = info->status.rates[0].count - 1; - - if (retries > 15) - retries = 15; - if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) && !lq_sta->ibss_sta_added) - goto out; - - table = &lq_sta->lq; - active_index = lq_sta->active_tbl; - - curr_tbl = &(lq_sta->lq_info[active_index]); - search_tbl = &(lq_sta->lq_info[(1 - active_index)]); - window = (struct iwl_rate_scale_data *)&(curr_tbl->win[0]); - search_win = (struct iwl_rate_scale_data *)&(search_tbl->win[0]); + return; /* * Ignore this Tx frame response if its initial rate doesn't match @@ -883,6 +781,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, * to check "search" mode, or a prior "search" mode after we've moved * to a new "search" mode (which might become the new "active" mode). */ + table = &lq_sta->lq; tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); if (priv->band == IEEE80211_BAND_5GHZ) @@ -901,7 +800,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, if (priv->band == IEEE80211_BAND_2GHZ) mac_index += IWL_FIRST_OFDM_RATE; } - + /* Here we actually compare this rate to the latest LQ command */ if ((mac_index < 0) || (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || @@ -911,124 +810,106 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || (rs_index != mac_index)) { IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate); - /* the last LQ command could failed so the LQ in ucode not - * the same in driver sync up + /* + * Since rates mis-match, the last LQ command may have failed. + * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with + * ... driver. */ lq_sta->missed_rate_counter++; if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); } - goto out; + /* Regardless, ignore this status info for outdated rate */ + return; + } else + /* Rate did match, so reset the missed_rate_counter */ + lq_sta->missed_rate_counter = 0; + + /* Figure out if rate scale algorithm is in active or search table */ + if (table_type_matches(&tbl_type, + &(lq_sta->lq_info[lq_sta->active_tbl]))) { + curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + } else if (table_type_matches(&tbl_type, + &lq_sta->lq_info[1 - lq_sta->active_tbl])) { + curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + } else { + IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n"); + return; } + window = (struct iwl_rate_scale_data *)&(curr_tbl->win[0]); - lq_sta->missed_rate_counter = 0; - /* Update frame history window with "failure" for each Tx retry. */ - while (retries) { - /* Look up the rate and other info used for each tx attempt. - * Each tx attempt steps one entry deeper in the rate table. */ - tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, - &tbl_type, &rs_index); - - /* If type matches "search" table, - * add failure to "search" history */ - if ((tbl_type.lq_type == search_tbl->lq_type) && - (tbl_type.ant_type == search_tbl->ant_type) && - (tbl_type.is_SGI == search_tbl->is_SGI)) { - if (search_tbl->expected_tpt) - tpt = search_tbl->expected_tpt[rs_index]; - else - tpt = 0; - rs_collect_tx_data(search_win, rs_index, tpt, 1, 0); - - /* Else if type matches "current/active" table, - * add failure to "current/active" history */ - } else if ((tbl_type.lq_type == curr_tbl->lq_type) && - (tbl_type.ant_type == curr_tbl->ant_type) && - (tbl_type.is_SGI == curr_tbl->is_SGI)) { - if (curr_tbl->expected_tpt) - tpt = curr_tbl->expected_tpt[rs_index]; - else - tpt = 0; - rs_collect_tx_data(window, rs_index, tpt, 1, 0); + /* + * Updating the frame history depends on whether packets were + * aggregated. + * + * For aggregation, all packets were transmitted at the same rate, the + * first index into rate scale table. + */ + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, + &rs_index); + tpt = get_expected_tpt(curr_tbl, rs_index); + rs_collect_tx_data(window, rs_index, tpt, + info->status.ampdu_ack_len, + info->status.ampdu_ack_map); + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += info->status.ampdu_ack_map; + lq_sta->total_failed += (info->status.ampdu_ack_len - + info->status.ampdu_ack_map); } - - /* If not searching for a new mode, increment failed counter - * ... this helps determine when to start searching again */ - if (lq_sta->stay_in_tbl) - lq_sta->total_failed++; - --retries; - index++; - - } - + } else { /* - * Find (by rate) the history window to update with final Tx attempt; - * if Tx was successful first try, use original rate, - * else look up the rate that was, finally, successful. + * For legacy, update frame history with for each Tx retry. */ - tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags); - lq_sta->last_rate_n_flags = tx_rate; - rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); - - /* Update frame history window with "success" if Tx got ACKed ... */ - status = !!(info->flags & IEEE80211_TX_STAT_ACK); - - /* If type matches "search" table, - * add final tx status to "search" history */ - if ((tbl_type.lq_type == search_tbl->lq_type) && - (tbl_type.ant_type == search_tbl->ant_type) && - (tbl_type.is_SGI == search_tbl->is_SGI)) { - if (search_tbl->expected_tpt) - tpt = search_tbl->expected_tpt[rs_index]; - else - tpt = 0; - if (info->flags & IEEE80211_TX_STAT_AMPDU) - rs_collect_tx_data(search_win, rs_index, tpt, - info->status.ampdu_ack_len, - info->status.ampdu_ack_map); - else - rs_collect_tx_data(search_win, rs_index, tpt, - 1, status); - /* Else if type matches "current/active" table, - * add final tx status to "current/active" history */ - } else if ((tbl_type.lq_type == curr_tbl->lq_type) && - (tbl_type.ant_type == curr_tbl->ant_type) && - (tbl_type.is_SGI == curr_tbl->is_SGI)) { - if (curr_tbl->expected_tpt) - tpt = curr_tbl->expected_tpt[rs_index]; - else - tpt = 0; - if (info->flags & IEEE80211_TX_STAT_AMPDU) - rs_collect_tx_data(window, rs_index, tpt, - info->status.ampdu_ack_len, - info->status.ampdu_ack_map); - else - rs_collect_tx_data(window, rs_index, tpt, - 1, status); - } + retries = info->status.rates[0].count - 1; + /* HW doesn't send more than 15 retries */ + retries = min(retries, 15); + + /* The last transmission may have been successful */ + legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); + /* Collect data for each rate used during failed TX attempts */ + for (i = 0; i <= retries; ++i) { + tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, + &tbl_type, &rs_index); + /* + * Only collect stats if retried rate is in the same RS + * table as active/search. + */ + if (table_type_matches(&tbl_type, curr_tbl)) + tpt = get_expected_tpt(curr_tbl, rs_index); + else if (table_type_matches(&tbl_type, other_tbl)) + tpt = get_expected_tpt(other_tbl, rs_index); + else + continue; - /* If not searching for new mode, increment success/failed counter - * ... these help determine when to start searching again */ - if (lq_sta->stay_in_tbl) { - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - lq_sta->total_success += info->status.ampdu_ack_map; - lq_sta->total_failed += - (info->status.ampdu_ack_len - info->status.ampdu_ack_map); - } else { - if (status) - lq_sta->total_success++; + /* Constants mean 1 transmission, 0 successes */ + if (i < retries) + rs_collect_tx_data(window, rs_index, tpt, 1, + 0); else - lq_sta->total_failed++; + rs_collect_tx_data(window, rs_index, tpt, 1, + legacy_success); + } + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += legacy_success; + lq_sta->total_failed += retries + (1 - legacy_success); } } + /* The last TX rate is cached in lq_sta; it's set in if/else above */ + lq_sta->last_rate_n_flags = tx_rate; /* See if there's a better rate or modulation mode to try. */ if (sta && sta->supp_rates[sband->band]) rs_rate_scale_perform(priv, skb, sta, lq_sta); -out: - return; } /* @@ -1066,43 +947,45 @@ static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl) { + /* Used to choose among HT tables */ + s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; + + /* Check for invalid LQ type */ + if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Legacy rates have only one table */ if (is_legacy(tbl->lq_type)) { - if (!is_a_band(tbl->lq_type)) - tbl->expected_tpt = expected_tpt_G; - else - tbl->expected_tpt = expected_tpt_A; - } else if (is_siso(tbl->lq_type)) { - if (tbl->is_ht40 && !lq_sta->is_dup) - if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_siso40MHzSGI; - else - tbl->expected_tpt = expected_tpt_siso40MHz; - else if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_siso20MHzSGI; - else - tbl->expected_tpt = expected_tpt_siso20MHz; - } else if (is_mimo2(tbl->lq_type)) { - if (tbl->is_ht40 && !lq_sta->is_dup) - if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_mimo2_40MHzSGI; - else - tbl->expected_tpt = expected_tpt_mimo2_40MHz; - else if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_mimo2_20MHzSGI; - else - tbl->expected_tpt = expected_tpt_mimo2_20MHz; - } else if (is_mimo3(tbl->lq_type)) { - if (tbl->is_ht40 && !lq_sta->is_dup) - if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_mimo3_40MHzSGI; - else - tbl->expected_tpt = expected_tpt_mimo3_40MHz; - else if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_mimo3_20MHzSGI; - else - tbl->expected_tpt = expected_tpt_mimo3_20MHz; - } else - tbl->expected_tpt = expected_tpt_G; + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Choose among many HT tables depending on number of streams + * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation + * status */ + if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_siso20MHz; + else if (is_siso(tbl->lq_type)) + ht_tbl_pointer = expected_tpt_siso40MHz; + else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + else if (is_mimo2(tbl->lq_type)) + ht_tbl_pointer = expected_tpt_mimo2_40MHz; + else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_mimo3_20MHz; + else /* if (is_mimo3(tbl->lq_type)) <-- must be true */ + ht_tbl_pointer = expected_tpt_mimo3_40MHz; + + if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ + tbl->expected_tpt = ht_tbl_pointer[0]; + else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ + tbl->expected_tpt = ht_tbl_pointer[1]; + else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ + tbl->expected_tpt = ht_tbl_pointer[2]; + else /* AGG+SGI */ + tbl->expected_tpt = ht_tbl_pointer[3]; } /* @@ -2077,6 +1960,14 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; tid = rs_tl_add_packet(lq_sta, hdr); + if ((tid != MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) { + tid_data = &priv->stations[lq_sta->lq.sta_id].tid[tid]; + if (tid_data->agg.state == IWL_AGG_OFF) + lq_sta->is_agg = 0; + else + lq_sta->is_agg = 1; + } else + lq_sta->is_agg = 0; /* * Select rate-scale / modulation-mode table to work with in @@ -2177,10 +2068,10 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, goto out; } - /* Else we have enough samples; calculate estimate of * actual average throughput */ + /* Sanity-check TPT calculations */ BUG_ON(window->average_tpt != ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128)); @@ -2584,22 +2475,13 @@ static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, gfp_t gfp) { struct iwl_lq_sta *lq_sta; + struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv; struct iwl_priv *priv; - int i, j; priv = (struct iwl_priv *)priv_rate; IWL_DEBUG_RATE(priv, "create station rate scale window\n"); - lq_sta = kzalloc(sizeof(struct iwl_lq_sta), gfp); - - if (lq_sta == NULL) - return NULL; - lq_sta->lq.sta_id = 0xff; - - - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); + lq_sta = &sta_priv->lq_sta; return lq_sta; } @@ -2613,6 +2495,12 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct iwl_lq_sta *lq_sta = priv_sta; + lq_sta->lq.sta_id = 0xff; + + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); + lq_sta->flush_timer = 0; lq_sta->supp_rates = sta->supp_rates[sband->band]; for (j = 0; j < LQ_SIZE; j++) @@ -2690,6 +2578,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); if (sband->band == IEEE80211_BAND_5GHZ) lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; + lq_sta->is_agg = 0; rs_initialize_lq(priv, conf, sta, lq_sta); } @@ -2808,7 +2697,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv, repeat_rate--; } - lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_MAX; + lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; lq_cmd->agg_params.agg_time_limit = cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); @@ -2827,11 +2716,9 @@ static void rs_free(void *priv_rate) static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta, void *priv_sta) { - struct iwl_lq_sta *lq_sta = priv_sta; struct iwl_priv *priv __maybe_unused = priv_r; IWL_DEBUG_RATE(priv, "enter\n"); - kfree(lq_sta); IWL_DEBUG_RATE(priv, "leave\n"); } @@ -2942,8 +2829,9 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); desc += sprintf(buff+desc, " %s", (tbl->is_ht40) ? "40MHz" : "20MHz"); - desc += sprintf(buff+desc, " %s %s\n", (tbl->is_SGI) ? "SGI" : "", - (lq_sta->is_green) ? "GF enabled" : ""); + desc += sprintf(buff+desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "", + (lq_sta->is_green) ? "GF enabled" : "", + (lq_sta->is_agg) ? "AGG on" : ""); } desc += sprintf(buff+desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); @@ -3076,16 +2964,16 @@ static void rs_add_debugfs(void *priv, void *priv_sta, { struct iwl_lq_sta *lq_sta = priv_sta; lq_sta->rs_sta_dbgfs_scale_table_file = - debugfs_create_file("rate_scale_table", 0600, dir, + debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, lq_sta, &rs_sta_dbgfs_scale_table_ops); lq_sta->rs_sta_dbgfs_stats_table_file = - debugfs_create_file("rate_stats_table", 0600, dir, + debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_stats_table_ops); lq_sta->rs_sta_dbgfs_rate_scale_data_file = - debugfs_create_file("rate_scale_data", 0600, dir, + debugfs_create_file("rate_scale_data", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = - debugfs_create_u8("tx_agg_tid_enable", 0600, dir, + debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, &lq_sta->tx_agg_tid_en); } diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h index 9fac530cfb7e..affc0c5a2f2c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h @@ -54,6 +54,7 @@ struct iwl3945_rate_info { u8 prev_table_rs; /* prev in rate table cmd */ }; + /* * These serve as indexes into * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; @@ -335,6 +336,106 @@ struct iwl_rate_mcs_info { char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; }; +/** + * struct iwl_rate_scale_data -- tx success history for one rate + */ +struct iwl_rate_scale_data { + u64 data; /* bitmap of successful frames */ + s32 success_counter; /* number of frames successful */ + s32 success_ratio; /* per-cent * 128 */ + s32 counter; /* number of frames attempted */ + s32 average_tpt; /* success ratio * expected throughput */ + unsigned long stamp; +}; + +/** + * struct iwl_scale_tbl_info -- tx params and success history for all rates + * + * There are two of these in struct iwl_lq_sta, + * one for "active", and one for "search". + */ +struct iwl_scale_tbl_info { + enum iwl_table_type lq_type; + u8 ant_type; + u8 is_SGI; /* 1 = short guard interval */ + u8 is_ht40; /* 1 = 40 MHz channel width */ + u8 is_dup; /* 1 = duplicated data streams */ + u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ + u8 max_search; /* maximun number of tables we can search */ + s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + u32 current_rate; /* rate_n_flags, uCode API format */ + struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ +}; + +struct iwl_traffic_load { + unsigned long time_stamp; /* age of the oldest statistics */ + u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time + * slice */ + u32 total; /* total num of packets during the + * last TID_MAX_TIME_DIFF */ + u8 queue_count; /* number of queues that has + * been used since the last cleanup */ + u8 head; /* start of the circular buffer */ +}; + +/** + * struct iwl_lq_sta -- driver's rate scaling private structure + * + * Pointer to this gets passed back and forth between driver and mac80211. + */ +struct iwl_lq_sta { + u8 active_tbl; /* index of active table, range 0-1 */ + u8 enable_counter; /* indicates HT mode */ + u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 search_better_tbl; /* 1: currently trying alternate mode */ + s32 last_tpt; + + /* The following determine when to search for a new mode */ + u32 table_count_limit; + u32 max_failure_limit; /* # failed frames before new search */ + u32 max_success_limit; /* # successful frames before new search */ + u32 table_count; + u32 total_failed; /* total failed frames, any/all rates */ + u32 total_success; /* total successful frames, any/all rates */ + u64 flush_timer; /* time staying in mode before new search */ + + u8 action_counter; /* # mode-switch actions tried */ + u8 is_green; + u8 is_dup; + enum ieee80211_band band; + u8 ibss_sta_added; + + /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ + u32 supp_rates; + u16 active_legacy_rate; + u16 active_siso_rate; + u16 active_mimo2_rate; + u16 active_mimo3_rate; + u16 active_rate_basic; + s8 max_rate_idx; /* Max rate set by user */ + u8 missed_rate_counter; + + struct iwl_link_quality_cmd lq; + struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ + struct iwl_traffic_load load[TID_MAX_LOAD_COUNT]; + u8 tx_agg_tid_en; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_scale_table_file; + struct dentry *rs_sta_dbgfs_stats_table_file; + struct dentry *rs_sta_dbgfs_rate_scale_data_file; + struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; + u32 dbg_fixed_rate; +#endif + struct iwl_priv *drv; + + /* used to be in sta_info */ + int last_txrate_idx; + /* last tx rate_n_flags */ + u32 last_rate_n_flags; + /* packets destined for this STA are aggregated */ + u8 is_agg; +}; + static inline u8 num_of_ant(u8 mask) { return !!((mask) & ANT_A) + diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 921dc4a26fe2..b8377efb3ba7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -123,6 +123,17 @@ int iwl_commit_rxon(struct iwl_priv *priv) return -EINVAL; } + /* + * receive commit_rxon request + * abort any previous channel switch if still in process + */ + if (priv->switch_rxon.switch_in_progress && + (priv->switch_rxon.channel != priv->staging_rxon.channel)) { + IWL_DEBUG_11H(priv, "abort channel switch on %d\n", + le16_to_cpu(priv->switch_rxon.channel)); + priv->switch_rxon.switch_in_progress = false; + } + /* If we don't need to send a full RXON, we can use * iwl_rxon_assoc_cmd which is used to reconfigure filter * and other flags for the current radio configuration. */ @@ -134,6 +145,7 @@ int iwl_commit_rxon(struct iwl_priv *priv) } memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); + iwl_print_rx_config_cmd(priv); return 0; } @@ -191,11 +203,7 @@ int iwl_commit_rxon(struct iwl_priv *priv) priv->start_calib = 0; /* Add the broadcast address so we can send broadcast frames */ - if (iwl_rxon_add_station(priv, iwl_bcast_addr, 0) == - IWL_INVALID_STATION) { - IWL_ERR(priv, "Error adding BROADCAST address for transmit.\n"); - return -EIO; - } + iwl_add_bcast_station(priv); /* If we have set the ASSOC_MSK and we are in BSS mode then * add the IWL_AP_ID to the station rate table */ @@ -233,6 +241,7 @@ int iwl_commit_rxon(struct iwl_priv *priv) } memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); } + iwl_print_rx_config_cmd(priv); iwl_init_sensitivity(priv); @@ -302,7 +311,7 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame) list_add(&frame->list, &priv->free_frames); } -static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, +static u32 iwl_fill_beacon_frame(struct iwl_priv *priv, struct ieee80211_hdr *hdr, int left) { @@ -319,34 +328,74 @@ static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, return priv->ibss_beacon->len; } +/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ +static void iwl_set_beacon_tim(struct iwl_priv *priv, + struct iwl_tx_beacon_cmd *tx_beacon_cmd, + u8 *beacon, u32 frame_size) +{ + u16 tim_idx; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; + + /* + * The index is relative to frame start but we start looking at the + * variable-length part of the beacon. + */ + tim_idx = mgmt->u.beacon.variable - beacon; + + /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ + while ((tim_idx < (frame_size - 2)) && + (beacon[tim_idx] != WLAN_EID_TIM)) + tim_idx += beacon[tim_idx+1] + 2; + + /* If TIM field was found, set variables */ + if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { + tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); + tx_beacon_cmd->tim_size = beacon[tim_idx+1]; + } else + IWL_WARN(priv, "Unable to find TIM Element in beacon\n"); +} + static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv, - struct iwl_frame *frame, u8 rate) + struct iwl_frame *frame) { struct iwl_tx_beacon_cmd *tx_beacon_cmd; - unsigned int frame_size; + u32 frame_size; + u32 rate_flags; + u32 rate; + /* + * We have to set up the TX command, the TX Beacon command, and the + * beacon contents. + */ + /* Initialize memory */ tx_beacon_cmd = &frame->u.beacon; memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); - tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; - tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - + /* Set up TX beacon contents */ frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame, sizeof(frame->u) - sizeof(*tx_beacon_cmd)); + if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE)) + return 0; - BUG_ON(frame_size > MAX_MPDU_SIZE); + /* Set up TX command fields */ tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); + tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | + TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; - if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP)) - tx_beacon_cmd->tx.rate_n_flags = - iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK); - else - tx_beacon_cmd->tx.rate_n_flags = - iwl_hw_set_rate_n_flags(rate, 0); + /* Set up TX beacon command fields */ + iwl_set_beacon_tim(priv, tx_beacon_cmd, (u8 *)tx_beacon_cmd->frame, + frame_size); - tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | - TX_CMD_FLG_TSF_MSK | - TX_CMD_FLG_STA_RATE_MSK; + /* Set up packet rate and flags */ + rate = iwl_rate_get_lowest_plcp(priv); + priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant); + rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant); + if ((rate >= IWL_FIRST_CCK_RATE) && (rate <= IWL_LAST_CCK_RATE)) + rate_flags |= RATE_MCS_CCK_MSK; + tx_beacon_cmd->tx.rate_n_flags = iwl_hw_set_rate_n_flags(rate, + rate_flags); return sizeof(*tx_beacon_cmd) + frame_size; } @@ -355,19 +404,20 @@ static int iwl_send_beacon_cmd(struct iwl_priv *priv) struct iwl_frame *frame; unsigned int frame_size; int rc; - u8 rate; frame = iwl_get_free_frame(priv); - if (!frame) { IWL_ERR(priv, "Could not obtain free frame buffer for beacon " "command.\n"); return -ENOMEM; } - rate = iwl_rate_get_lowest_plcp(priv); - - frame_size = iwl_hw_get_beacon_cmd(priv, frame, rate); + frame_size = iwl_hw_get_beacon_cmd(priv, frame); + if (!frame_size) { + IWL_ERR(priv, "Error configuring the beacon command\n"); + iwl_free_frame(priv, frame); + return -EINVAL; + } rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, &frame->u.cmd[0]); @@ -525,7 +575,7 @@ int iwl_hw_tx_queue_init(struct iwl_priv *priv, static void iwl_rx_reply_alive(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_alive_resp *palive; struct delayed_work *pwork; @@ -604,14 +654,14 @@ static void iwl_bg_statistics_periodic(unsigned long data) if (!iwl_is_ready_rf(priv)) return; - iwl_send_statistics_request(priv, CMD_ASYNC); + iwl_send_statistics_request(priv, CMD_ASYNC, false); } static void iwl_rx_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { #ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl4965_beacon_notif *beacon = (struct iwl4965_beacon_notif *)pkt->u.raw; u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); @@ -635,7 +685,7 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv, static void iwl_rx_card_state_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); unsigned long status = priv->status; @@ -721,7 +771,7 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv) * statistics request from the host as well as for the periodic * statistics notifications (after received beacons) from the uCode. */ - priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_rx_statistics; + priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_reply_statistics; priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics; iwl_setup_spectrum_handlers(priv); @@ -770,7 +820,7 @@ void iwl_rx_handle(struct iwl_priv *priv) IWL_DEBUG_RX(priv, "r = %d, i = %d\n", r, i); /* calculate total frames need to be restock after handling RX */ - total_empty = r - priv->rxq.write_actual; + total_empty = r - rxq->write_actual; if (total_empty < 0) total_empty += RX_QUEUE_SIZE; @@ -787,10 +837,13 @@ void iwl_rx_handle(struct iwl_priv *priv) rxq->queue[i] = NULL; - pci_unmap_single(priv->pci_dev, rxb->real_dma_addr, - priv->hw_params.rx_buf_size + 256, - PCI_DMA_FROMDEVICE); - pkt = (struct iwl_rx_packet *)rxb->skb->data; + pci_unmap_page(priv->pci_dev, rxb->page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + pkt = rxb_addr(rxb); + + trace_iwlwifi_dev_rx(priv, pkt, + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); /* Reclaim a command buffer only if this packet is a response * to a (driver-originated) command. @@ -812,8 +865,8 @@ void iwl_rx_handle(struct iwl_priv *priv) if (priv->rx_handlers[pkt->hdr.cmd]) { IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); - priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); priv->isr_stats.rx_handlers[pkt->hdr.cmd]++; + priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); } else { /* No handling needed */ IWL_DEBUG_RX(priv, @@ -822,35 +875,45 @@ void iwl_rx_handle(struct iwl_priv *priv) pkt->hdr.cmd); } + /* + * XXX: After here, we should always check rxb->page + * against NULL before touching it or its virtual + * memory (pkt). Because some rx_handler might have + * already taken or freed the pages. + */ + if (reclaim) { - /* Invoke any callbacks, transfer the skb to caller, and - * fire off the (possibly) blocking iwl_send_cmd() + /* Invoke any callbacks, transfer the buffer to caller, + * and fire off the (possibly) blocking iwl_send_cmd() * as we reclaim the driver command queue */ - if (rxb && rxb->skb) + if (rxb->page) iwl_tx_cmd_complete(priv, rxb); else IWL_WARN(priv, "Claim null rxb?\n"); } - /* For now we just don't re-use anything. We can tweak this - * later to try and re-use notification packets and SKBs that - * fail to Rx correctly */ - if (rxb->skb != NULL) { - priv->alloc_rxb_skb--; - dev_kfree_skb_any(rxb->skb); - rxb->skb = NULL; - } - + /* Reuse the page if possible. For notification packets and + * SKBs that fail to Rx correctly, add them back into the + * rx_free list for reuse later. */ spin_lock_irqsave(&rxq->lock, flags); - list_add_tail(&rxb->list, &priv->rxq.rx_used); + if (rxb->page != NULL) { + rxb->page_dma = pci_map_page(priv->pci_dev, rxb->page, + 0, PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } else + list_add_tail(&rxb->list, &rxq->rx_used); + spin_unlock_irqrestore(&rxq->lock, flags); + i = (i + 1) & RX_QUEUE_MASK; /* If there are a lot of unused frames, * restock the Rx queue so ucode wont assert. */ if (fill_rx) { count++; if (count >= 8) { - priv->rxq.read = i; + rxq->read = i; iwl_rx_replenish_now(priv); count = 0; } @@ -858,7 +921,7 @@ void iwl_rx_handle(struct iwl_priv *priv) } /* Backtrack one entry */ - priv->rxq.read = i; + rxq->read = i; if (fill_rx) iwl_rx_replenish_now(priv); else @@ -878,6 +941,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) u32 inta, handled = 0; u32 inta_fh; unsigned long flags; + u32 i; #ifdef CONFIG_IWLWIFI_DEBUG u32 inta_mask; #endif @@ -905,6 +969,8 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) } #endif + spin_unlock_irqrestore(&priv->lock, flags); + /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not * atomic, make sure that inta covers all the interrupts that * we've discovered, even if FH interrupt came in just after @@ -926,8 +992,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) handled |= CSR_INT_BIT_HW_ERR; - spin_unlock_irqrestore(&priv->lock, flags); - return; } @@ -995,19 +1059,17 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) handled |= CSR_INT_BIT_SW_ERR; } - /* uCode wakes up after power-down sleep */ + /* + * uCode wakes up after power-down sleep. + * Tell device about any new tx or host commands enqueued, + * and about any Rx buffers made available while asleep. + */ if (inta & CSR_INT_BIT_WAKEUP) { IWL_DEBUG_ISR(priv, "Wakeup interrupt\n"); iwl_rx_queue_update_write_ptr(priv, &priv->rxq); - iwl_txq_update_write_ptr(priv, &priv->txq[0]); - iwl_txq_update_write_ptr(priv, &priv->txq[1]); - iwl_txq_update_write_ptr(priv, &priv->txq[2]); - iwl_txq_update_write_ptr(priv, &priv->txq[3]); - iwl_txq_update_write_ptr(priv, &priv->txq[4]); - iwl_txq_update_write_ptr(priv, &priv->txq[5]); - + for (i = 0; i < priv->hw_params.max_txq_num; i++) + iwl_txq_update_write_ptr(priv, &priv->txq[i]); priv->isr_stats.wakeup++; - handled |= CSR_INT_BIT_WAKEUP; } @@ -1020,11 +1082,12 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); } + /* This "Tx" DMA channel is used only for loading uCode */ if (inta & CSR_INT_BIT_FH_TX) { - IWL_DEBUG_ISR(priv, "Tx interrupt\n"); + IWL_DEBUG_ISR(priv, "uCode load interrupt\n"); priv->isr_stats.tx++; handled |= CSR_INT_BIT_FH_TX; - /* FH finished to write, send event */ + /* Wake up uCode load routine, now that load is complete */ priv->ucode_write_complete = 1; wake_up_interruptible(&priv->wait_command_queue); } @@ -1054,7 +1117,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); } #endif - spin_unlock_irqrestore(&priv->lock, flags); } /* tasklet for iwlagn interrupt */ @@ -1063,6 +1125,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) u32 inta = 0; u32 handled = 0; unsigned long flags; + u32 i; #ifdef CONFIG_IWLWIFI_DEBUG u32 inta_mask; #endif @@ -1084,6 +1147,9 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) inta, inta_mask); } #endif + + spin_unlock_irqrestore(&priv->lock, flags); + /* saved interrupt in inta variable now we can reset priv->inta */ priv->inta = 0; @@ -1099,8 +1165,6 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) handled |= CSR_INT_BIT_HW_ERR; - spin_unlock_irqrestore(&priv->lock, flags); - return; } @@ -1172,12 +1236,8 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) if (inta & CSR_INT_BIT_WAKEUP) { IWL_DEBUG_ISR(priv, "Wakeup interrupt\n"); iwl_rx_queue_update_write_ptr(priv, &priv->rxq); - iwl_txq_update_write_ptr(priv, &priv->txq[0]); - iwl_txq_update_write_ptr(priv, &priv->txq[1]); - iwl_txq_update_write_ptr(priv, &priv->txq[2]); - iwl_txq_update_write_ptr(priv, &priv->txq[3]); - iwl_txq_update_write_ptr(priv, &priv->txq[4]); - iwl_txq_update_write_ptr(priv, &priv->txq[5]); + for (i = 0; i < priv->hw_params.max_txq_num; i++) + iwl_txq_update_write_ptr(priv, &priv->txq[i]); priv->isr_stats.wakeup++; @@ -1206,26 +1266,36 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) * 3- update RX shared data to indicate last write index. * 4- send interrupt. * This could lead to RX race, driver could receive RX interrupt - * but the shared data changes does not reflect this. - * this could lead to RX race, RX periodic will solve this race + * but the shared data changes does not reflect this; + * periodic interrupt will detect any dangling Rx activity. */ - iwl_write32(priv, CSR_INT_PERIODIC_REG, + + /* Disable periodic interrupt; we use it as just a one-shot. */ + iwl_write8(priv, CSR_INT_PERIODIC_REG, CSR_INT_PERIODIC_DIS); iwl_rx_handle(priv); - /* Only set RX periodic if real RX is received. */ + + /* + * Enable periodic interrupt in 8 msec only if we received + * real RX interrupt (instead of just periodic int), to catch + * any dangling Rx interrupt. If it was just the periodic + * interrupt, there was no dangling Rx activity, and no need + * to extend the periodic interrupt; one-shot is enough. + */ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) - iwl_write32(priv, CSR_INT_PERIODIC_REG, + iwl_write8(priv, CSR_INT_PERIODIC_REG, CSR_INT_PERIODIC_ENA); priv->isr_stats.rx++; } + /* This "Tx" DMA channel is used only for loading uCode */ if (inta & CSR_INT_BIT_FH_TX) { iwl_write32(priv, CSR_FH_INT_STATUS, CSR49_FH_INT_TX_MASK); - IWL_DEBUG_ISR(priv, "Tx interrupt\n"); + IWL_DEBUG_ISR(priv, "uCode load interrupt\n"); priv->isr_stats.tx++; handled |= CSR_INT_BIT_FH_TX; - /* FH finished to write, send event */ + /* Wake up uCode load routine, now that load is complete */ priv->ucode_write_complete = 1; wake_up_interruptible(&priv->wait_command_queue); } @@ -1240,14 +1310,10 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) inta & ~priv->inta_mask); } - /* Re-enable all interrupts */ /* only Re-enable if diabled by irq */ if (test_bit(STATUS_INT_ENABLED, &priv->status)) iwl_enable_interrupts(priv); - - spin_unlock_irqrestore(&priv->lock, flags); - } @@ -1367,6 +1433,14 @@ static int iwl_read_ucode(struct iwl_priv *priv) IWL_UCODE_API(priv->ucode_ver), IWL_UCODE_SERIAL(priv->ucode_ver)); + snprintf(priv->hw->wiphy->fw_version, + sizeof(priv->hw->wiphy->fw_version), + "%u.%u.%u.%u", + IWL_UCODE_MAJOR(priv->ucode_ver), + IWL_UCODE_MINOR(priv->ucode_ver), + IWL_UCODE_API(priv->ucode_ver), + IWL_UCODE_SERIAL(priv->ucode_ver)); + if (build) IWL_DEBUG_INFO(priv, "Build %u\n", build); @@ -1531,7 +1605,6 @@ static int iwl_read_ucode(struct iwl_priv *priv) return ret; } -#ifdef CONFIG_IWLWIFI_DEBUG static const char *desc_lookup_text[] = { "OK", "FAIL", @@ -1589,7 +1662,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv) base = le32_to_cpu(priv->card_alive.error_event_table_ptr); if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { - IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base); + IWL_ERR(priv, + "Not valid error log pointer 0x%08X for %s uCode\n", + base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT"); return; } @@ -1611,6 +1686,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv) line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32)); time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32)); + trace_iwlwifi_dev_ucode_error(priv, desc, time, data1, data2, line, + blink1, blink2, ilink1, ilink2); + IWL_ERR(priv, "Desc Time " "data1 data2 line\n"); IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n", @@ -1635,6 +1713,7 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ u32 ptr; /* SRAM byte address of log data */ u32 ev, time, data; /* event log data */ + unsigned long reg_flags; if (num_events == 0) return; @@ -1650,26 +1729,72 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, ptr = base + EVENT_START_OFFSET + (start_idx * event_size); + /* Make sure device is powered up for SRAM reads */ + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + + /* Set starting address; reads will auto-increment */ + _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr); + rmb(); + /* "time" is actually "data" for mode 0 (no timestamp). * place event id # at far right for easier visual parsing. */ for (i = 0; i < num_events; i++) { - ev = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - time = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); + ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); if (mode == 0) { /* data, ev */ + trace_iwlwifi_dev_ucode_event(priv, 0, time, ev); IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); } else { - data = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); + data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", time, data, ev); + trace_iwlwifi_dev_ucode_event(priv, time, data, ev); } } + + /* Allow device to power down */ + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +/** + * iwl_print_last_event_logs - Dump the newest # of event log to syslog + */ +static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, + u32 num_wraps, u32 next_entry, + u32 size, u32 mode) +{ + /* + * display the newest DEFAULT_LOG_ENTRIES entries + * i.e the entries just before the next ont that uCode would fill. + */ + if (num_wraps) { + if (next_entry < size) { + iwl_print_event_log(priv, + capacity - (size - next_entry), + size - next_entry, mode); + iwl_print_event_log(priv, 0, + next_entry, mode); + } else + iwl_print_event_log(priv, next_entry - size, + size, mode); + } else { + if (next_entry < size) + iwl_print_event_log(priv, 0, next_entry, mode); + else + iwl_print_event_log(priv, next_entry - size, + size, mode); + } } -void iwl_dump_nic_event_log(struct iwl_priv *priv) +/* For sanity check only. Actual size is determined by uCode, typ. 512 */ +#define MAX_EVENT_LOG_SIZE (512) + +#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) + +void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log) { u32 base; /* SRAM byte address of event log header */ u32 capacity; /* event log capacity in # entries */ @@ -1684,7 +1809,9 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv) base = le32_to_cpu(priv->card_alive.log_event_table_ptr); if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { - IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base); + IWL_ERR(priv, + "Invalid event log pointer 0x%08X for %s uCode\n", + base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT"); return; } @@ -1694,6 +1821,18 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv) num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); + if (capacity > MAX_EVENT_LOG_SIZE) { + IWL_ERR(priv, "Log capacity %d is bogus, limit to %d entries\n", + capacity, MAX_EVENT_LOG_SIZE); + capacity = MAX_EVENT_LOG_SIZE; + } + + if (next_entry > MAX_EVENT_LOG_SIZE) { + IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n", + next_entry, MAX_EVENT_LOG_SIZE); + next_entry = MAX_EVENT_LOG_SIZE; + } + size = num_wraps ? capacity : next_entry; /* bail out if nothing in log */ @@ -1702,19 +1841,37 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv) return; } - IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", - size, num_wraps); - - /* if uCode has wrapped back to top of log, start at the oldest entry, - * i.e the next one that uCode would fill. */ - if (num_wraps) - iwl_print_event_log(priv, next_entry, - capacity - next_entry, mode); - /* (then/else) start at top of log */ - iwl_print_event_log(priv, 0, next_entry, mode); +#ifdef CONFIG_IWLWIFI_DEBUG + if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS)) + size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; +#else + size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; +#endif + IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", + size); -} +#ifdef CONFIG_IWLWIFI_DEBUG + if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { + /* + * if uCode has wrapped back to top of log, + * start at the oldest entry, + * i.e the next one that uCode would fill. + */ + if (num_wraps) + iwl_print_event_log(priv, next_entry, + capacity - next_entry, mode); + /* (then/else) start at top of log */ + iwl_print_event_log(priv, 0, next_entry, mode); + } else + iwl_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode); +#else + iwl_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode); #endif +} /** * iwl_alive_start - called after REPLY_ALIVE notification received @@ -1763,6 +1920,10 @@ static void iwl_alive_start(struct iwl_priv *priv) priv->active_rate = priv->rates_mask; priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; + /* Configure Tx antenna selection based on H/W config */ + if (priv->cfg->ops->hcmd->set_tx_ant) + priv->cfg->ops->hcmd->set_tx_ant(priv, priv->cfg->valid_tx_ant); + if (iwl_is_associated(priv)) { struct iwl_rxon_cmd *active_rxon = (struct iwl_rxon_cmd *)&priv->active_rxon; @@ -1790,7 +1951,7 @@ static void iwl_alive_start(struct iwl_priv *priv) /* At this point, the NIC is initialized and operational */ iwl_rf_kill_ct_config(priv); - iwl_leds_register(priv); + iwl_leds_init(priv); IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); set_bit(STATUS_READY, &priv->status); @@ -1828,8 +1989,6 @@ static void __iwl_down(struct iwl_priv *priv) if (!exit_pending) set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl_leds_unregister(priv); - iwl_clear_stations_table(priv); /* Unblock any waiting calls */ @@ -1877,24 +2036,20 @@ static void __iwl_down(struct iwl_priv *priv) /* device going down, Stop using ICT table */ iwl_disable_ict(priv); - spin_lock_irqsave(&priv->lock, flags); - iwl_clear_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - spin_unlock_irqrestore(&priv->lock, flags); iwl_txq_ctx_stop(priv); iwl_rxq_stop(priv); - iwl_write_prph(priv, APMG_CLK_DIS_REG, - APMG_CLK_VAL_DMA_CLK_RQT); - + /* Power-down device's busmaster DMA clocks */ + iwl_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(5); - /* FIXME: apm_ops.suspend(priv) */ - if (exit_pending) - priv->cfg->ops->lib->apm_ops.stop(priv); - else - priv->cfg->ops->lib->apm_ops.reset(priv); + /* Make sure (redundant) we've released our request to stay awake */ + iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* Stop the device, and put it in low power state */ + priv->cfg->ops->lib->apm_ops.stop(priv); + exit: memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp)); @@ -2281,6 +2436,67 @@ void iwl_post_associate(struct iwl_priv *priv) #define UCODE_READY_TIMEOUT (4 * HZ) +/* + * Not a mac80211 entry point function, but it fits in with all the + * other mac80211 functions grouped here. + */ +static int iwl_setup_mac(struct iwl_priv *priv) +{ + int ret; + struct ieee80211_hw *hw = priv->hw; + hw->rate_control_algorithm = "iwl-agn-rs"; + + /* Tell mac80211 our characteristics */ + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_NOISE_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SPECTRUM_MGMT; + + if (!priv->cfg->broken_powersave) + hw->flags |= IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + + hw->sta_data_size = sizeof(struct iwl_station_priv); + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + + hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY | + WIPHY_FLAG_DISABLE_BEACON_HINTS; + + /* + * For now, disable PS by default because it affects + * RX performance significantly. + */ + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; + /* we create the 802.11 header and a zero-length SSID element */ + hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2; + + /* Default value; 4 EDCA QOS priorities */ + hw->queues = 4; + + hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + + if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &priv->bands[IEEE80211_BAND_2GHZ]; + if (priv->bands[IEEE80211_BAND_5GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &priv->bands[IEEE80211_BAND_5GHZ]; + + ret = ieee80211_register_hw(priv->hw); + if (ret) { + IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); + return ret; + } + priv->mac80211_registered = 1; + + return 0; +} + + static int iwl_mac_start(struct ieee80211_hw *hw) { struct iwl_priv *priv = hw->priv; @@ -2328,6 +2544,8 @@ static int iwl_mac_start(struct ieee80211_hw *hw) } } + iwl_led_start(priv); + out: priv->is_open = 1; IWL_DEBUG_MAC80211(priv, "leave\n"); @@ -2404,6 +2622,10 @@ void iwl_config_ap(struct iwl_priv *priv) IWL_WARN(priv, "REPLY_RXON_TIMING failed - " "Attempting to continue.\n"); + /* AP has all antennas */ + priv->chain_noise_data.active_chains = + priv->hw_params.valid_rx_ant; + iwl_set_rxon_ht(priv, &priv->current_ht_config); if (priv->cfg->ops->hcmd->set_rxon_chain) priv->cfg->ops->hcmd->set_rxon_chain(priv); @@ -2432,10 +2654,11 @@ void iwl_config_ap(struct iwl_priv *priv) /* restore RXON assoc */ priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; iwlcore_commit_rxon(priv); + iwl_reset_qos(priv); spin_lock_irqsave(&priv->lock, flags); iwl_activate_qos(priv, 1); spin_unlock_irqrestore(&priv->lock, flags); - iwl_rxon_add_station(priv, iwl_bcast_addr, 0); + iwl_add_bcast_station(priv); } iwl_send_beacon_cmd(priv); @@ -2527,6 +2750,7 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { @@ -2580,6 +2804,45 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw, return 0; } +static void iwl_mac_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + int sta_id; + + /* + * TODO: We really should use this callback to + * actually maintain the station table in + * the device. + */ + + switch (cmd) { + case STA_NOTIFY_ADD: + atomic_set(&sta_priv->pending_frames, 0); + if (vif->type == NL80211_IFTYPE_AP) + sta_priv->client = true; + break; + case STA_NOTIFY_SLEEP: + WARN_ON(!sta_priv->client); + sta_priv->asleep = true; + if (atomic_read(&sta_priv->pending_frames) > 0) + ieee80211_sta_block_awake(hw, sta, true); + break; + case STA_NOTIFY_AWAKE: + WARN_ON(!sta_priv->client); + sta_priv->asleep = false; + sta_id = iwl_find_station(priv, sta->addr); + if (sta_id != IWL_INVALID_STATION) + iwl_sta_modify_ps_wake(priv, sta_id); + break; + default: + break; + } +} + /***************************************************************************** * * sysfs attributes @@ -2774,7 +3037,7 @@ static ssize_t show_statistics(struct device *d, return -EAGAIN; mutex_lock(&priv->mutex); - rc = iwl_send_statistics_request(priv, 0); + rc = iwl_send_statistics_request(priv, CMD_SYNC, false); mutex_unlock(&priv->mutex); if (rc) { @@ -2799,6 +3062,40 @@ static ssize_t show_statistics(struct device *d, static DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL); +static ssize_t show_rts_ht_protection(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + + return sprintf(buf, "%s\n", + priv->cfg->use_rts_for_ht ? "RTS/CTS" : "CTS-to-self"); +} + +static ssize_t store_rts_ht_protection(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + IWL_INFO(priv, "Input is not in decimal form.\n"); + else { + if (!iwl_is_associated(priv)) + priv->cfg->use_rts_for_ht = val ? true : false; + else + IWL_ERR(priv, "Sta associated with AP - " + "Change protection mechanism is not allowed\n"); + ret = count; + } + return ret; +} + +static DEVICE_ATTR(rts_ht_protection, S_IWUSR | S_IRUGO, + show_rts_ht_protection, store_rts_ht_protection); + /***************************************************************************** * @@ -2849,12 +3146,103 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv) del_timer_sync(&priv->statistics_periodic); } +static void iwl_init_hw_rates(struct iwl_priv *priv, + struct ieee80211_rate *rates) +{ + int i; + + for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) { + rates[i].bitrate = iwl_rates[i].ieee * 5; + rates[i].hw_value = i; /* Rate scaling will work on indexes */ + rates[i].hw_value_short = i; + rates[i].flags = 0; + if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) { + /* + * If CCK != 1M then set short preamble rate flag. + */ + rates[i].flags |= + (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ? + 0 : IEEE80211_RATE_SHORT_PREAMBLE; + } + } +} + +static int iwl_init_drv(struct iwl_priv *priv) +{ + int ret; + + priv->ibss_beacon = NULL; + + spin_lock_init(&priv->lock); + spin_lock_init(&priv->sta_lock); + spin_lock_init(&priv->hcmd_lock); + + INIT_LIST_HEAD(&priv->free_frames); + + mutex_init(&priv->mutex); + + /* Clear the driver's (not device's) station table */ + iwl_clear_stations_table(priv); + + priv->ieee_channels = NULL; + priv->ieee_rates = NULL; + priv->band = IEEE80211_BAND_2GHZ; + + priv->iw_mode = NL80211_IFTYPE_STATION; + + /* Choose which receivers/antennas to use */ + if (priv->cfg->ops->hcmd->set_rxon_chain) + priv->cfg->ops->hcmd->set_rxon_chain(priv); + + iwl_init_scan_params(priv); + + iwl_reset_qos(priv); + + priv->qos_data.qos_active = 0; + priv->qos_data.qos_cap.val = 0; + + priv->rates_mask = IWL_RATES_MASK; + /* Set the tx_power_user_lmt to the lowest power level + * this value will get overwritten by channel max power avg + * from eeprom */ + priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MIN; + + ret = iwl_init_channel_map(priv); + if (ret) { + IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); + goto err; + } + + ret = iwlcore_init_geos(priv); + if (ret) { + IWL_ERR(priv, "initializing geos failed: %d\n", ret); + goto err_free_channel_map; + } + iwl_init_hw_rates(priv, priv->ieee_rates); + + return 0; + +err_free_channel_map: + iwl_free_channel_map(priv); +err: + return ret; +} + +static void iwl_uninit_drv(struct iwl_priv *priv) +{ + iwl_calib_free_results(priv); + iwlcore_free_geos(priv); + iwl_free_channel_map(priv); + kfree(priv->scan); +} + static struct attribute *iwl_sysfs_entries[] = { &dev_attr_flags.attr, &dev_attr_filter_flags.attr, &dev_attr_statistics.attr, &dev_attr_temperature.attr, &dev_attr_tx_power.attr, + &dev_attr_rts_ht_protection.attr, #ifdef CONFIG_IWLWIFI_DEBUG &dev_attr_debug_level.attr, #endif @@ -2882,7 +3270,8 @@ static struct ieee80211_ops iwl_hw_ops = { .reset_tsf = iwl_mac_reset_tsf, .bss_info_changed = iwl_bss_info_changed, .ampdu_action = iwl_mac_ampdu_action, - .hw_scan = iwl_mac_hw_scan + .hw_scan = iwl_mac_hw_scan, + .sta_notify = iwl_mac_sta_notify, }; static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -2990,12 +3379,6 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_iounmap; } - /* amp init */ - err = priv->cfg->ops->lib->apm_ops.init(priv); - if (err < 0) { - IWL_ERR(priv, "Failed to init APMG\n"); - goto out_iounmap; - } /***************** * 4. Read EEPROM *****************/ @@ -3141,6 +3524,15 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) iwl_down(priv); } + /* + * Make sure device is reset to low power before unloading driver. + * This may be redundant with iwl_down(), but there are paths to + * run iwl_down() without calling apm_ops.stop(), and there are + * paths to avoid running iwl_down() at all before leaving driver. + * This (inexpensive) call *makes sure* device is reset. + */ + priv->cfg->ops->lib->apm_ops.stop(priv); + iwl_tt_exit(priv); /* make sure we flush any pending irq or @@ -3203,37 +3595,97 @@ static struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)}, #endif /* CONFIG_IWL4965 */ #ifdef CONFIG_IWL5000 - {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bg_cfg)}, - {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bg_cfg)}, - {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, - {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, - {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, - {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, - {IWL_PCI_DEVICE(0x4232, PCI_ANY_ID, iwl5100_agn_cfg)}, - {IWL_PCI_DEVICE(0x4235, PCI_ANY_ID, iwl5300_agn_cfg)}, - {IWL_PCI_DEVICE(0x4236, PCI_ANY_ID, iwl5300_agn_cfg)}, - {IWL_PCI_DEVICE(0x4237, PCI_ANY_ID, iwl5100_agn_cfg)}, -/* 5350 WiFi/WiMax */ - {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, - {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, - {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, -/* 5150 Wifi/WiMax */ - {IWL_PCI_DEVICE(0x423C, PCI_ANY_ID, iwl5150_agn_cfg)}, - {IWL_PCI_DEVICE(0x423D, PCI_ANY_ID, iwl5150_agn_cfg)}, -/* 6000/6050 Series */ - {IWL_PCI_DEVICE(0x008D, PCI_ANY_ID, iwl6000h_2agn_cfg)}, - {IWL_PCI_DEVICE(0x008E, PCI_ANY_ID, iwl6000h_2agn_cfg)}, - {IWL_PCI_DEVICE(0x422B, PCI_ANY_ID, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x4238, PCI_ANY_ID, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0086, PCI_ANY_ID, iwl6050_3agn_cfg)}, - {IWL_PCI_DEVICE(0x0087, PCI_ANY_ID, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0088, PCI_ANY_ID, iwl6050_3agn_cfg)}, - {IWL_PCI_DEVICE(0x0089, PCI_ANY_ID, iwl6050_2agn_cfg)}, +/* 5100 Series WiFi */ + {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */ + +/* 5300 Series WiFi */ + {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */ + +/* 5350 Series WiFi/WiMax */ + {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */ + +/* 5150 Series Wifi/WiMax */ + {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */ + + {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */ + +/* 6x00 Series */ + {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)}, + {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)}, + +/* 6x50 WiFi/WiMax Series */ + {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, + /* 1000 Series WiFi */ - {IWL_PCI_DEVICE(0x0083, PCI_ANY_ID, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0084, PCI_ANY_ID, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)}, #endif /* CONFIG_IWL5000 */ {0} @@ -3288,9 +3740,9 @@ module_exit(iwl_exit); module_init(iwl_init); #ifdef CONFIG_IWLWIFI_DEBUG -module_param_named(debug50, iwl_debug_level, uint, 0444); +module_param_named(debug50, iwl_debug_level, uint, S_IRUGO); MODULE_PARM_DESC(debug50, "50XX debug output mask (deprecated)"); -module_param_named(debug, iwl_debug_level, uint, 0644); +module_param_named(debug, iwl_debug_level, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "debug output mask"); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.c b/drivers/net/wireless/iwlwifi/iwl-calib.c index c4b565a2de94..95a57b36a7ea 100644 --- a/drivers/net/wireless/iwlwifi/iwl-calib.c +++ b/drivers/net/wireless/iwlwifi/iwl-calib.c @@ -132,6 +132,7 @@ void iwl_calib_free_results(struct iwl_priv *priv) priv->calib_results[i].buf_len = 0; } } +EXPORT_SYMBOL(iwl_calib_free_results); /***************************************************************************** * RUNTIME calibrations framework @@ -447,11 +448,11 @@ static int iwl_sensitivity_write(struct iwl_priv *priv) cpu_to_le16((u16)data->nrg_th_ofdm); cmd.table[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = - cpu_to_le16(190); + cpu_to_le16(data->barker_corr_th_min); cmd.table[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16(390); + cpu_to_le16(data->barker_corr_th_min_mrc); cmd.table[HD_OFDM_ENERGY_TH_IN_INDEX] = - cpu_to_le16(62); + cpu_to_le16(data->nrg_th_cca); IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, @@ -516,7 +517,7 @@ void iwl_init_sensitivity(struct iwl_priv *priv) for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) data->nrg_silence_rssi[i] = 0; - data->auto_corr_ofdm = 90; + data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; @@ -524,6 +525,9 @@ void iwl_init_sensitivity(struct iwl_priv *priv) data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; data->nrg_th_cck = ranges->nrg_th_cck; data->nrg_th_ofdm = ranges->nrg_th_ofdm; + data->barker_corr_th_min = ranges->barker_corr_th_min; + data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; + data->nrg_th_cca = ranges->nrg_th_cca; data->last_bad_plcp_cnt_ofdm = 0; data->last_fa_cnt_ofdm = 0; @@ -643,6 +647,15 @@ void iwl_sensitivity_calibration(struct iwl_priv *priv, } EXPORT_SYMBOL(iwl_sensitivity_calibration); +static inline u8 find_first_chain(u8 mask) +{ + if (mask & ANT_A) + return CHAIN_A; + if (mask & ANT_B) + return CHAIN_B; + return CHAIN_C; +} + /* * Accumulate 20 beacons of signal and noise statistics for each of * 3 receivers/antennas/rx-chains, then figure out: @@ -675,14 +688,17 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, u8 num_tx_chains; unsigned long flags; struct statistics_rx_non_phy *rx_info = &(stat_resp->rx.general); + u8 first_chain; if (priv->disable_chain_noise_cal) return; data = &(priv->chain_noise_data); - /* Accumulate just the first 20 beacons after the first association, - * then we're done forever. */ + /* + * Accumulate just the first "chain_noise_num_beacons" after + * the first association, then we're done forever. + */ if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { if (data->state == IWL_CHAIN_NOISE_ALIVE) IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); @@ -710,7 +726,10 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, return; } - /* Accumulate beacon statistics values across 20 beacons */ + /* + * Accumulate beacon statistics values across + * "chain_noise_num_beacons" + */ chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & @@ -741,16 +760,19 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b, chain_noise_c); - /* If this is the 20th beacon, determine: + /* If this is the "chain_noise_num_beacons", determine: * 1) Disconnected antennas (using signal strengths) * 2) Differential gain (using silence noise) to balance receivers */ - if (data->beacon_count != CAL_NUM_OF_BEACONS) + if (data->beacon_count != priv->cfg->chain_noise_num_beacons) return; /* Analyze signal for disconnected antenna */ - average_sig[0] = (data->chain_signal_a) / CAL_NUM_OF_BEACONS; - average_sig[1] = (data->chain_signal_b) / CAL_NUM_OF_BEACONS; - average_sig[2] = (data->chain_signal_c) / CAL_NUM_OF_BEACONS; + average_sig[0] = + (data->chain_signal_a) / priv->cfg->chain_noise_num_beacons; + average_sig[1] = + (data->chain_signal_b) / priv->cfg->chain_noise_num_beacons; + average_sig[2] = + (data->chain_signal_c) / priv->cfg->chain_noise_num_beacons; if (average_sig[0] >= average_sig[1]) { max_average_sig = average_sig[0]; @@ -803,13 +825,17 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, /* there is a Tx antenna connected */ break; if (num_tx_chains == priv->hw_params.tx_chains_num && - data->disconn_array[i]) { - /* This is the last TX antenna and is also - * disconnected connect it anyway */ - data->disconn_array[i] = 0; - active_chains |= ant_msk; - IWL_DEBUG_CALIB(priv, "All Tx chains are disconnected W/A - " - "declare %d as connected\n", i); + data->disconn_array[i]) { + /* + * If all chains are disconnected + * connect the first valid tx chain + */ + first_chain = + find_first_chain(priv->cfg->valid_tx_ant); + data->disconn_array[first_chain] = 0; + active_chains |= BIT(first_chain); + IWL_DEBUG_CALIB(priv, "All Tx chains are disconnected W/A - declare %d as connected\n", + first_chain); break; } } @@ -820,9 +846,12 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, active_chains); /* Analyze noise for rx balance */ - average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS); - average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS); - average_noise[2] = ((data->chain_noise_c)/CAL_NUM_OF_BEACONS); + average_noise[0] = + ((data->chain_noise_a) / priv->cfg->chain_noise_num_beacons); + average_noise[1] = + ((data->chain_noise_b) / priv->cfg->chain_noise_num_beacons); + average_noise[2] = + ((data->chain_noise_c) / priv->cfg->chain_noise_num_beacons); for (i = 0; i < NUM_RX_CHAINS; i++) { if (!(data->disconn_array[i]) && @@ -843,7 +872,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, if (priv->cfg->ops->utils->gain_computation) priv->cfg->ops->utils->gain_computation(priv, average_noise, - min_average_noise_antenna_i, min_average_noise); + min_average_noise_antenna_i, min_average_noise, + find_first_chain(priv->cfg->valid_rx_ant)); /* Some power changes may have been made during the calibration. * Update and commit the RXON @@ -870,7 +900,7 @@ void iwl_reset_run_time_calib(struct iwl_priv *priv) /* Ask for statistics now, the uCode will send notification * periodically after association */ - iwl_send_statistics_request(priv, CMD_ASYNC); + iwl_send_statistics_request(priv, CMD_ASYNC, true); } EXPORT_SYMBOL(iwl_reset_run_time_calib); diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 4afaf773aeac..e91507531923 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -109,11 +109,12 @@ enum { REPLY_TX_LINK_QUALITY_CMD = 0x4e, /* 4965 only */ /* WiMAX coexistence */ - COEX_PRIORITY_TABLE_CMD = 0x5a, /*5000 only */ + COEX_PRIORITY_TABLE_CMD = 0x5a, /* for 5000 series and up */ COEX_MEDIUM_NOTIFICATION = 0x5b, COEX_EVENT_CMD = 0x5c, /* Calibration */ + TEMPERATURE_NOTIFICATION = 0x62, CALIBRATION_CFG_CMD = 0x65, CALIBRATION_RES_NOTIFICATION = 0x66, CALIBRATION_COMPLETE_NOTIFICATION = 0x67, @@ -148,7 +149,7 @@ enum { QUIET_NOTIFICATION = 0x96, /* not used */ REPLY_TX_PWR_TABLE_CMD = 0x97, REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */ - TX_ANT_CONFIGURATION_CMD = 0x98, /* not used */ + TX_ANT_CONFIGURATION_CMD = 0x98, MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */ /* Bluetooth device coexistence config command */ @@ -353,6 +354,9 @@ struct iwl3945_power_per_rate { #define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32 #define POWER_TABLE_CCK_ENTRY 32 +#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24 +#define IWL_PWR_CCK_ENTRIES 2 + /** * union iwl4965_tx_power_dual_stream * @@ -411,6 +415,16 @@ struct iwl5000_tx_power_dbm_cmd { u8 reserved; } __attribute__ ((packed)); +/** + * Command TX_ANT_CONFIGURATION_CMD = 0x98 + * This command is used to configure valid Tx antenna. + * By default uCode concludes the valid antenna according to the radio flavor. + * This command enables the driver to override/modify this conclusion. + */ +struct iwl_tx_ant_config_cmd { + __le32 valid; +} __attribute__ ((packed)); + /****************************************************************************** * (0a) * Alive and Error Commands & Responses: @@ -793,7 +807,7 @@ struct iwl3945_channel_switch_cmd { struct iwl3945_power_per_rate power[IWL_MAX_RATES]; } __attribute__ ((packed)); -struct iwl_channel_switch_cmd { +struct iwl4965_channel_switch_cmd { u8 band; u8 expect_beacon; __le16 channel; @@ -803,6 +817,48 @@ struct iwl_channel_switch_cmd { struct iwl4965_tx_power_db tx_power; } __attribute__ ((packed)); +/** + * struct iwl5000_channel_switch_cmd + * @band: 0- 5.2GHz, 1- 2.4GHz + * @expect_beacon: 0- resume transmits after channel switch + * 1- wait for beacon to resume transmits + * @channel: new channel number + * @rxon_flags: Rx on flags + * @rxon_filter_flags: filtering parameters + * @switch_time: switch time in extended beacon format + * @reserved: reserved bytes + */ +struct iwl5000_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; +} __attribute__ ((packed)); + +/** + * struct iwl6000_channel_switch_cmd + * @band: 0- 5.2GHz, 1- 2.4GHz + * @expect_beacon: 0- resume transmits after channel switch + * 1- wait for beacon to resume transmits + * @channel: new channel number + * @rxon_flags: Rx on flags + * @rxon_filter_flags: filtering parameters + * @switch_time: switch time in extended beacon format + * @reserved: reserved bytes + */ +struct iwl6000_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; +} __attribute__ ((packed)); + /* * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) */ @@ -921,6 +977,7 @@ struct iwl_qosparam_cmd { #define STA_MODIFY_TX_RATE_MSK 0x04 #define STA_MODIFY_ADDBA_TID_MSK 0x08 #define STA_MODIFY_DELBA_TID_MSK 0x10 +#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 /* Receiver address (actually, Rx station's index into station table), * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ @@ -1051,7 +1108,14 @@ struct iwl4965_addsta_cmd { * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ __le16 add_immediate_ba_ssn; - __le32 reserved2; + /* + * Number of packets OK to transmit to station even though + * it is asleep -- used to synchronise PS-poll and u-APSD + * responses while ucode keeps track of STA sleep state. + */ + __le16 sleep_tx_count; + + __le16 reserved2; } __attribute__ ((packed)); /* 5000 */ @@ -1082,7 +1146,14 @@ struct iwl_addsta_cmd { * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ __le16 add_immediate_ba_ssn; - __le32 reserved2; + /* + * Number of packets OK to transmit to station even though + * it is asleep -- used to synchronise PS-poll and u-APSD + * responses while ucode keeps track of STA sleep state. + */ + __le16 sleep_tx_count; + + __le16 reserved2; } __attribute__ ((packed)); @@ -1634,6 +1705,21 @@ enum { TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ }; +static inline u32 iwl_tx_status_to_mac80211(u32 status) +{ + status &= TX_STATUS_MSK; + + switch (status) { + case TX_STATUS_SUCCESS: + case TX_STATUS_DIRECT_DONE: + return IEEE80211_TX_STAT_ACK; + case TX_STATUS_FAIL_DEST_PS: + return IEEE80211_TX_STAT_TX_FILTERED; + default: + return 0; + } +} + static inline bool iwl_is_tx_success(u32 status) { status &= TX_STATUS_MSK; @@ -2162,6 +2248,19 @@ struct iwl_link_quality_cmd { __le32 reserved2; } __attribute__ ((packed)); +#define BT_COEX_DISABLE (0x0) +#define BT_COEX_MODE_2W (0x1) +#define BT_COEX_MODE_3W (0x2) +#define BT_COEX_MODE_4W (0x3) + +#define BT_LEAD_TIME_MIN (0x0) +#define BT_LEAD_TIME_DEF (0x1E) +#define BT_LEAD_TIME_MAX (0xFF) + +#define BT_MAX_KILL_MIN (0x1) +#define BT_MAX_KILL_DEF (0x5) +#define BT_MAX_KILL_MAX (0xFF) + /* * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) * @@ -2497,9 +2596,10 @@ struct iwl_scan_channel { /** * struct iwl_ssid_ie - directed scan network information element * - * Up to 4 of these may appear in REPLY_SCAN_CMD, selected by "type" field - * in struct iwl_scan_channel; each channel may select different ssids from - * among the 4 entries. SSID IEs get transmitted in reverse order of entry. + * Up to 20 of these may appear in REPLY_SCAN_CMD (Note: Only 4 are in + * 3945 SCAN api), selected by "type" bit field in struct iwl_scan_channel; + * each channel may select different ssids from among the 20 (4) entries. + * SSID IEs get transmitted in reverse order of entry. */ struct iwl_ssid_ie { u8 id; @@ -3001,6 +3101,10 @@ struct statistics_general { __le32 reserved3; } __attribute__ ((packed)); +#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) +#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) +#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) + /* * REPLY_STATISTICS_CMD = 0x9c, * 3945 and 4965 identical. @@ -3237,12 +3341,6 @@ struct iwl_missed_beacon_notif { * Lower values mean higher energy; this means making sure that the value * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". * - * Driver should set the following entries to fixed values: - * - * HD_MIN_ENERGY_OFDM_DET_INDEX 100 - * HD_BARKER_CORR_TH_ADD_MIN_INDEX 190 - * HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX 390 - * HD_OFDM_ENERGY_TH_IN_INDEX 62 */ /* @@ -3440,30 +3538,134 @@ struct iwl_led_cmd { } __attribute__ ((packed)); /* - * Coexistence WIFI/WIMAX Command - * COEX_PRIORITY_TABLE_CMD = 0x5a - * + * station priority table entries + * also used as potential "events" value for both + * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD + */ + +/* + * COEX events entry flag masks + * RP - Requested Priority + * WP - Win Medium Priority: priority assigned when the contention has been won + */ +#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1) +#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2) +#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4) + +#define COEX_CU_UNASSOC_IDLE_RP 4 +#define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4 +#define COEX_CU_UNASSOC_AUTO_SCAN_RP 4 +#define COEX_CU_CALIBRATION_RP 4 +#define COEX_CU_PERIODIC_CALIBRATION_RP 4 +#define COEX_CU_CONNECTION_ESTAB_RP 4 +#define COEX_CU_ASSOCIATED_IDLE_RP 4 +#define COEX_CU_ASSOC_MANUAL_SCAN_RP 4 +#define COEX_CU_ASSOC_AUTO_SCAN_RP 4 +#define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4 +#define COEX_CU_RF_ON_RP 6 +#define COEX_CU_RF_OFF_RP 4 +#define COEX_CU_STAND_ALONE_DEBUG_RP 6 +#define COEX_CU_IPAN_ASSOC_LEVEL_RP 4 +#define COEX_CU_RSRVD1_RP 4 +#define COEX_CU_RSRVD2_RP 4 + +#define COEX_CU_UNASSOC_IDLE_WP 3 +#define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3 +#define COEX_CU_UNASSOC_AUTO_SCAN_WP 3 +#define COEX_CU_CALIBRATION_WP 3 +#define COEX_CU_PERIODIC_CALIBRATION_WP 3 +#define COEX_CU_CONNECTION_ESTAB_WP 3 +#define COEX_CU_ASSOCIATED_IDLE_WP 3 +#define COEX_CU_ASSOC_MANUAL_SCAN_WP 3 +#define COEX_CU_ASSOC_AUTO_SCAN_WP 3 +#define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3 +#define COEX_CU_RF_ON_WP 3 +#define COEX_CU_RF_OFF_WP 3 +#define COEX_CU_STAND_ALONE_DEBUG_WP 6 +#define COEX_CU_IPAN_ASSOC_LEVEL_WP 3 +#define COEX_CU_RSRVD1_WP 3 +#define COEX_CU_RSRVD2_WP 3 + +#define COEX_UNASSOC_IDLE_FLAGS 0 +#define COEX_UNASSOC_MANUAL_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_UNASSOC_AUTO_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_CALIBRATION_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_PERIODIC_CALIBRATION_FLAGS 0 +/* + * COEX_CONNECTION_ESTAB: + * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. + */ +#define COEX_CONNECTION_ESTAB_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) +#define COEX_ASSOCIATED_IDLE_FLAGS 0 +#define COEX_ASSOC_MANUAL_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_ASSOC_AUTO_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0 +#define COEX_RF_ON_FLAGS 0 +#define COEX_RF_OFF_FLAGS 0 +#define COEX_STAND_ALONE_DEBUG_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_IPAN_ASSOC_LEVEL_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) +#define COEX_RSRVD1_FLAGS 0 +#define COEX_RSRVD2_FLAGS 0 +/* + * COEX_CU_RF_ON is the event wrapping all radio ownership. + * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. */ +#define COEX_CU_RF_ON_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) + + enum { + /* un-association part */ COEX_UNASSOC_IDLE = 0, COEX_UNASSOC_MANUAL_SCAN = 1, COEX_UNASSOC_AUTO_SCAN = 2, + /* calibration */ COEX_CALIBRATION = 3, COEX_PERIODIC_CALIBRATION = 4, + /* connection */ COEX_CONNECTION_ESTAB = 5, + /* association part */ COEX_ASSOCIATED_IDLE = 6, COEX_ASSOC_MANUAL_SCAN = 7, COEX_ASSOC_AUTO_SCAN = 8, COEX_ASSOC_ACTIVE_LEVEL = 9, + /* RF ON/OFF */ COEX_RF_ON = 10, COEX_RF_OFF = 11, COEX_STAND_ALONE_DEBUG = 12, + /* IPAN */ COEX_IPAN_ASSOC_LEVEL = 13, + /* reserved */ COEX_RSRVD1 = 14, COEX_RSRVD2 = 15, COEX_NUM_OF_EVENTS = 16 }; +/* + * Coexistence WIFI/WIMAX Command + * COEX_PRIORITY_TABLE_CMD = 0x5a + * + */ struct iwl_wimax_coex_event_entry { u8 request_prio; u8 win_medium_prio; @@ -3488,6 +3690,55 @@ struct iwl_wimax_coex_cmd { struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS]; } __attribute__ ((packed)); +/* + * Coexistence MEDIUM NOTIFICATION + * COEX_MEDIUM_NOTIFICATION = 0x5b + * + * notification from uCode to host to indicate medium changes + * + */ +/* + * status field + * bit 0 - 2: medium status + * bit 3: medium change indication + * bit 4 - 31: reserved + */ +/* status option values, (0 - 2 bits) */ +#define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */ +#define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */ +#define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */ +#define COEX_MEDIUM_MSK (0x7) + +/* send notification status (1 bit) */ +#define COEX_MEDIUM_CHANGED (0x8) +#define COEX_MEDIUM_CHANGED_MSK (0x8) +#define COEX_MEDIUM_SHIFT (3) + +struct iwl_coex_medium_notification { + __le32 status; + __le32 events; +} __attribute__ ((packed)); + +/* + * Coexistence EVENT Command + * COEX_EVENT_CMD = 0x5c + * + * send from host to uCode for coex event request. + */ +/* flags options */ +#define COEX_EVENT_REQUEST_MSK (0x1) + +struct iwl_coex_event_cmd { + u8 flags; + u8 event; + __le16 reserved; +} __attribute__ ((packed)); + +struct iwl_coex_event_resp { + __le32 status; +} __attribute__ ((packed)); + + /****************************************************************************** * (13) * Union of all expected notifications/responses: @@ -3495,6 +3746,16 @@ struct iwl_wimax_coex_cmd { *****************************************************************************/ struct iwl_rx_packet { + /* + * The first 4 bytes of the RX frame header contain both the RX frame + * size and some flags. + * Bit fields: + * 31: flag flush RB request + * 30: flag ignore TC (terminal counter) request + * 29: flag fast IRQ request + * 28-14: Reserved + * 13-00: RX frame size + */ __le32 len_n_flags; struct iwl_cmd_header hdr; union { @@ -3514,6 +3775,8 @@ struct iwl_rx_packet { struct iwl_notif_statistics stats; struct iwl_compressed_ba_resp compressed_ba; struct iwl_missed_beacon_notif missed_beacon; + struct iwl_coex_medium_notification coex_medium_notif; + struct iwl_coex_event_resp coex_event; __le32 status; u8 raw[0]; } u; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 2dc928755454..574d36658702 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -47,6 +47,37 @@ MODULE_VERSION(IWLWIFI_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); +static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = { + {COEX_CU_UNASSOC_IDLE_RP, COEX_CU_UNASSOC_IDLE_WP, + 0, COEX_UNASSOC_IDLE_FLAGS}, + {COEX_CU_UNASSOC_MANUAL_SCAN_RP, COEX_CU_UNASSOC_MANUAL_SCAN_WP, + 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, + {COEX_CU_UNASSOC_AUTO_SCAN_RP, COEX_CU_UNASSOC_AUTO_SCAN_WP, + 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, + {COEX_CU_CALIBRATION_RP, COEX_CU_CALIBRATION_WP, + 0, COEX_CALIBRATION_FLAGS}, + {COEX_CU_PERIODIC_CALIBRATION_RP, COEX_CU_PERIODIC_CALIBRATION_WP, + 0, COEX_PERIODIC_CALIBRATION_FLAGS}, + {COEX_CU_CONNECTION_ESTAB_RP, COEX_CU_CONNECTION_ESTAB_WP, + 0, COEX_CONNECTION_ESTAB_FLAGS}, + {COEX_CU_ASSOCIATED_IDLE_RP, COEX_CU_ASSOCIATED_IDLE_WP, + 0, COEX_ASSOCIATED_IDLE_FLAGS}, + {COEX_CU_ASSOC_MANUAL_SCAN_RP, COEX_CU_ASSOC_MANUAL_SCAN_WP, + 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, + {COEX_CU_ASSOC_AUTO_SCAN_RP, COEX_CU_ASSOC_AUTO_SCAN_WP, + 0, COEX_ASSOC_AUTO_SCAN_FLAGS}, + {COEX_CU_ASSOC_ACTIVE_LEVEL_RP, COEX_CU_ASSOC_ACTIVE_LEVEL_WP, + 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS}, + {COEX_CU_RF_ON_RP, COEX_CU_RF_ON_WP, 0, COEX_CU_RF_ON_FLAGS}, + {COEX_CU_RF_OFF_RP, COEX_CU_RF_OFF_WP, 0, COEX_RF_OFF_FLAGS}, + {COEX_CU_STAND_ALONE_DEBUG_RP, COEX_CU_STAND_ALONE_DEBUG_WP, + 0, COEX_STAND_ALONE_DEBUG_FLAGS}, + {COEX_CU_IPAN_ASSOC_LEVEL_RP, COEX_CU_IPAN_ASSOC_LEVEL_WP, + 0, COEX_IPAN_ASSOC_LEVEL_FLAGS}, + {COEX_CU_RSRVD1_RP, COEX_CU_RSRVD1_WP, 0, COEX_RSRVD1_FLAGS}, + {COEX_CU_RSRVD2_RP, COEX_CU_RSRVD2_WP, 0, COEX_RSRVD2_FLAGS} +}; + #define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ IWL_RATE_SISO_##s##M_PLCP, \ @@ -178,6 +209,7 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant) } return ant; } +EXPORT_SYMBOL(iwl_toggle_tx_ant); const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; EXPORT_SYMBOL(iwl_bcast_addr); @@ -224,7 +256,10 @@ int iwl_hw_nic_init(struct iwl_priv *priv) /* nic_init */ spin_lock_irqsave(&priv->lock, flags); priv->cfg->ops->lib->apm_ops.init(priv); - iwl_write32(priv, CSR_INT_COALESCING, 512 / 32); + + /* Set interrupt coalescing timer to 512 usecs */ + iwl_write8(priv, CSR_INT_COALESCING, 512 / 32); + spin_unlock_irqrestore(&priv->lock, flags); ret = priv->cfg->ops->lib->apm_ops.set_pwr_src(priv, IWL_PWR_SRC_VMAIN); @@ -416,8 +451,7 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv, ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; ht_info->cap |= (IEEE80211_HT_CAP_SM_PS & - (WLAN_HT_CAP_SM_PS_DISABLED << 2)); - + (priv->cfg->sm_ps_mode << 2)); max_bit_rate = MAX_BIT_RATE_20_MHZ; if (priv->hw_params.ht40_channel & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; @@ -452,28 +486,6 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv, } } -static void iwlcore_init_hw_rates(struct iwl_priv *priv, - struct ieee80211_rate *rates) -{ - int i; - - for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) { - rates[i].bitrate = iwl_rates[i].ieee * 5; - rates[i].hw_value = i; /* Rate scaling will work on indexes */ - rates[i].hw_value_short = i; - rates[i].flags = 0; - if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) { - /* - * If CCK != 1M then set short preamble rate flag. - */ - rates[i].flags |= - (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ? - 0 : IEEE80211_RATE_SHORT_PREAMBLE; - } - } -} - - /** * iwlcore_init_geos - Initialize mac80211's geo/channel info based from eeprom */ @@ -605,11 +617,27 @@ void iwlcore_free_geos(struct iwl_priv *priv) } EXPORT_SYMBOL(iwlcore_free_geos); +/* + * iwlcore_rts_tx_cmd_flag: Set rts/cts. 3945 and 4965 only share this + * function. + */ +void iwlcore_rts_tx_cmd_flag(struct ieee80211_tx_info *info, + __le32 *tx_flags) +{ + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { + *tx_flags |= TX_CMD_FLG_RTS_MSK; + *tx_flags &= ~TX_CMD_FLG_CTS_MSK; + } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + *tx_flags &= ~TX_CMD_FLG_RTS_MSK; + *tx_flags |= TX_CMD_FLG_CTS_MSK; + } +} +EXPORT_SYMBOL(iwlcore_rts_tx_cmd_flag); + static bool is_single_rx_stream(struct iwl_priv *priv) { return !priv->current_ht_config.is_ht || - ((priv->current_ht_config.mcs.rx_mask[1] == 0) && - (priv->current_ht_config.mcs.rx_mask[2] == 0)); + priv->current_ht_config.single_chain_sufficient; } static u8 iwl_is_channel_extension(struct iwl_priv *priv, @@ -635,10 +663,9 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv, u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct ieee80211_sta_ht_cap *sta_ht_inf) { - struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; - if ((!iwl_ht_conf->is_ht) || - (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ)) + if (!ht_conf->is_ht || !ht_conf->is_40mhz) return 0; /* We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 @@ -654,7 +681,7 @@ u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv, #endif return iwl_is_channel_extension(priv, priv->band, le16_to_cpu(priv->staging_rxon.channel), - iwl_ht_conf->extension_chan_offset); + ht_conf->extension_chan_offset); } EXPORT_SYMBOL(iwl_is_ht40_tx_allowed); @@ -878,11 +905,11 @@ u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_rate_get_lowest_plcp); -void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) +void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) { struct iwl_rxon_cmd *rxon = &priv->staging_rxon; - if (!ht_info->is_ht) { + if (!ht_conf->is_ht) { rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK | @@ -893,7 +920,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) /* FIXME: if the definition of ht_protection changed, the "translation" * will be needed for rxon->flags */ - rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS); + rxon->flags |= cpu_to_le32(ht_conf->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS); /* Set up channel bandwidth: * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ @@ -902,10 +929,10 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); if (iwl_is_ht40_tx_allowed(priv, NULL)) { /* pure ht40 */ - if (ht_info->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { + if (ht_conf->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; /* Note: control channel is opposite of extension channel */ - switch (ht_info->extension_chan_offset) { + switch (ht_conf->extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rxon->flags &= ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; break; @@ -915,7 +942,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) } } else { /* Note: control channel is opposite of extension channel */ - switch (ht_info->extension_chan_offset) { + switch (ht_conf->extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; @@ -938,14 +965,10 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) if (priv->cfg->ops->hcmd->set_rxon_chain) priv->cfg->ops->hcmd->set_rxon_chain(priv); - IWL_DEBUG_ASSOC(priv, "supported HT rate 0x%X 0x%X 0x%X " - "rxon flags 0x%X operation mode :0x%X " + IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X " "extension channel offset 0x%x\n", - ht_info->mcs.rx_mask[0], - ht_info->mcs.rx_mask[1], - ht_info->mcs.rx_mask[2], - le32_to_cpu(rxon->flags), ht_info->ht_protection, - ht_info->extension_chan_offset); + le32_to_cpu(rxon->flags), ht_conf->ht_protection, + ht_conf->extension_chan_offset); return; } EXPORT_SYMBOL(iwl_set_rxon_ht); @@ -955,44 +978,50 @@ EXPORT_SYMBOL(iwl_set_rxon_ht); #define IWL_NUM_IDLE_CHAINS_DUAL 2 #define IWL_NUM_IDLE_CHAINS_SINGLE 1 -/* Determine how many receiver/antenna chains to use. - * More provides better reception via diversity. Fewer saves power. +/* + * Determine how many receiver/antenna chains to use. + * + * More provides better reception via diversity. Fewer saves power + * at the expense of throughput, but only when not in powersave to + * start with. + * * MIMO (dual stream) requires at least 2, but works better with 3. * This does not determine *which* chains to use, just how many. */ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) { - bool is_single = is_single_rx_stream(priv); - bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); - /* # of Rx chains to use when expecting MIMO. */ - if (is_single || (!is_cam && (priv->current_ht_config.sm_ps == - WLAN_HT_CAP_SM_PS_STATIC))) + if (is_single_rx_stream(priv)) return IWL_NUM_RX_CHAINS_SINGLE; else return IWL_NUM_RX_CHAINS_MULTIPLE; } +/* + * When we are in power saving mode, unless device support spatial + * multiplexing power save, use the active count for rx chain count. + */ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) { - int idle_cnt; + int idle_cnt = active_cnt; bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); + /* # Rx chains when idling and maybe trying to save power */ - switch (priv->current_ht_config.sm_ps) { + switch (priv->cfg->sm_ps_mode) { case WLAN_HT_CAP_SM_PS_STATIC: + idle_cnt = (is_cam) ? active_cnt : IWL_NUM_IDLE_CHAINS_SINGLE; + break; case WLAN_HT_CAP_SM_PS_DYNAMIC: idle_cnt = (is_cam) ? IWL_NUM_IDLE_CHAINS_DUAL : - IWL_NUM_IDLE_CHAINS_SINGLE; + IWL_NUM_IDLE_CHAINS_SINGLE; break; case WLAN_HT_CAP_SM_PS_DISABLED: - idle_cnt = (is_cam) ? active_cnt : IWL_NUM_IDLE_CHAINS_SINGLE; break; case WLAN_HT_CAP_SM_PS_INVALID: default: - IWL_ERR(priv, "invalid mimo ps mode %d\n", - priv->current_ht_config.sm_ps); + IWL_ERR(priv, "invalid sm_ps mode %u\n", + priv->cfg->sm_ps_mode); WARN_ON(1); - idle_cnt = -1; break; } return idle_cnt; @@ -1005,7 +1034,7 @@ static u8 iwl_count_chain_bitmap(u32 chain_bitmap) res = (chain_bitmap & BIT(0)) >> 0; res += (chain_bitmap & BIT(1)) >> 1; res += (chain_bitmap & BIT(2)) >> 2; - res += (chain_bitmap & BIT(4)) >> 4; + res += (chain_bitmap & BIT(3)) >> 3; return res; } @@ -1281,18 +1310,28 @@ static void iwl_set_rate(struct iwl_priv *priv) void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon; struct iwl_csa_notification *csa = &(pkt->u.csa_notif); - IWL_DEBUG_11H(priv, "CSA notif: channel %d, status %d\n", - le16_to_cpu(csa->channel), le32_to_cpu(csa->status)); - rxon->channel = csa->channel; - priv->staging_rxon.channel = csa->channel; + + if (priv->switch_rxon.switch_in_progress) { + if (!le32_to_cpu(csa->status) && + (csa->channel == priv->switch_rxon.channel)) { + rxon->channel = csa->channel; + priv->staging_rxon.channel = csa->channel; + IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", + le16_to_cpu(csa->channel)); + } else + IWL_ERR(priv, "CSA notif (fail) : channel %d\n", + le16_to_cpu(csa->channel)); + + priv->switch_rxon.switch_in_progress = false; + } } EXPORT_SYMBOL(iwl_rx_csa); #ifdef CONFIG_IWLWIFI_DEBUG -static void iwl_print_rx_config_cmd(struct iwl_priv *priv) +void iwl_print_rx_config_cmd(struct iwl_priv *priv) { struct iwl_rxon_cmd *rxon = &priv->staging_rxon; @@ -1310,6 +1349,7 @@ static void iwl_print_rx_config_cmd(struct iwl_priv *priv) IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); } +EXPORT_SYMBOL(iwl_print_rx_config_cmd); #endif /** * iwl_irq_handle_error - called for HW or SW error interrupt from card @@ -1322,12 +1362,11 @@ void iwl_irq_handle_error(struct iwl_priv *priv) /* Cancel currently queued command. */ clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + priv->cfg->ops->lib->dump_nic_error_log(priv); + priv->cfg->ops->lib->dump_nic_event_log(priv, false); #ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) { - priv->cfg->ops->lib->dump_nic_error_log(priv); - priv->cfg->ops->lib->dump_nic_event_log(priv); + if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) iwl_print_rx_config_cmd(priv); - } #endif wake_up_interruptible(&priv->wait_command_queue); @@ -1346,6 +1385,160 @@ void iwl_irq_handle_error(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_irq_handle_error); +int iwl_apm_stop_master(struct iwl_priv *priv) +{ + int ret = 0; + + /* stop device's busmaster DMA activity */ + iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); + + ret = iwl_poll_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, + CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); + if (ret) + IWL_WARN(priv, "Master Disable Timed Out, 100 usec\n"); + + IWL_DEBUG_INFO(priv, "stop master\n"); + + return ret; +} +EXPORT_SYMBOL(iwl_apm_stop_master); + +void iwl_apm_stop(struct iwl_priv *priv) +{ + IWL_DEBUG_INFO(priv, "Stop card, put in low power state\n"); + + /* Stop device's DMA activity */ + iwl_apm_stop_master(priv); + + /* Reset the entire device */ + iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); +} +EXPORT_SYMBOL(iwl_apm_stop); + + +/* + * Start up NIC's basic functionality after it has been reset + * (e.g. after platform boot, or shutdown via iwl_apm_stop()) + * NOTE: This does not load uCode nor start the embedded processor + */ +int iwl_apm_init(struct iwl_priv *priv) +{ + int ret = 0; + u16 lctl; + + IWL_DEBUG_INFO(priv, "Init card's basic functions\n"); + + /* + * Use "set_bit" below rather than "write", to preserve any hardware + * bits already set by default after reset. + */ + + /* Disable L0S exit timer (platform NMI Work/Around) */ + iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + + /* + * Disable L0s without affecting L1; + * don't wait for ICH L0s (ICH bug W/A) + */ + iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); + + /* Set FH wait threshold to maximum (HW error during stress W/A) */ + iwl_set_bit(priv, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); + + /* + * Enable HAP INTA (interrupt from management bus) to + * wake device's PCI Express link L1a -> L0s + * NOTE: This is no-op for 3945 (non-existant bit) + */ + iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + + /* + * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition. + * Check if BIOS (or OS) enabled L1-ASPM on this device. + * If so (likely), disable L0S, so device moves directly L0->L1; + * costs negligible amount of power savings. + * If not (unlikely), enable L0S, so there is at least some + * power savings, even without L1. + */ + if (priv->cfg->set_l0s) { + lctl = iwl_pcie_link_ctl(priv); + if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == + PCI_CFG_LINK_CTRL_VAL_L1_EN) { + /* L1-ASPM enabled; disable(!) L0S */ + iwl_set_bit(priv, CSR_GIO_REG, + CSR_GIO_REG_VAL_L0S_ENABLED); + IWL_DEBUG_POWER(priv, "L1 Enabled; Disabling L0S\n"); + } else { + /* L1-ASPM disabled; enable(!) L0S */ + iwl_clear_bit(priv, CSR_GIO_REG, + CSR_GIO_REG_VAL_L0S_ENABLED); + IWL_DEBUG_POWER(priv, "L1 Disabled; Enabling L0S\n"); + } + } + + /* Configure analog phase-lock-loop before activating to D0A */ + if (priv->cfg->pll_cfg_val) + iwl_set_bit(priv, CSR_ANA_PLL_CFG, priv->cfg->pll_cfg_val); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is supported, e.g. iwl_write_prph() + * and accesses to uCode SRAM. + */ + ret = iwl_poll_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + if (ret < 0) { + IWL_DEBUG_INFO(priv, "Failed to init the card\n"); + goto out; + } + + /* + * Enable DMA and BSM (if used) clocks, wait for them to stabilize. + * BSM (Boostrap State Machine) is only in 3945 and 4965; + * later devices (i.e. 5000 and later) have non-volatile SRAM, + * and don't need BSM to restore data after power-saving sleep. + * + * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits + * do not disable clocks. This preserves any hardware bits already + * set by default in "CLK_CTRL_REG" after reset. + */ + if (priv->cfg->use_bsm) + iwl_write_prph(priv, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); + else + iwl_write_prph(priv, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + udelay(20); + + /* Disable L1-Active */ + iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + +out: + return ret; +} +EXPORT_SYMBOL(iwl_apm_init); + + + void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, @@ -1393,73 +1586,14 @@ void iwl_configure_filter(struct ieee80211_hw *hw, } EXPORT_SYMBOL(iwl_configure_filter); -int iwl_setup_mac(struct iwl_priv *priv) -{ - int ret; - struct ieee80211_hw *hw = priv->hw; - hw->rate_control_algorithm = "iwl-agn-rs"; - - /* Tell mac80211 our characteristics */ - hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_NOISE_DBM | - IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_SPECTRUM_MGMT; - - if (!priv->cfg->broken_powersave) - hw->flags |= IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_DYNAMIC_PS; - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); - - hw->wiphy->custom_regulatory = true; - - /* Firmware does not support this */ - hw->wiphy->disable_beacon_hints = true; - - /* - * For now, disable PS by default because it affects - * RX performance significantly. - */ - hw->wiphy->ps_default = false; - - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; - /* we create the 802.11 header and a zero-length SSID element */ - hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2; - - /* Default value; 4 EDCA QOS priorities */ - hw->queues = 4; - - hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - - if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) - priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &priv->bands[IEEE80211_BAND_2GHZ]; - if (priv->bands[IEEE80211_BAND_5GHZ].n_channels) - priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &priv->bands[IEEE80211_BAND_5GHZ]; - - ret = ieee80211_register_hw(priv->hw); - if (ret) { - IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); - return ret; - } - priv->mac80211_registered = 1; - - return 0; -} -EXPORT_SYMBOL(iwl_setup_mac); - int iwl_set_hw_params(struct iwl_priv *priv) { priv->hw_params.max_rxq_size = RX_QUEUE_SIZE; priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; if (priv->cfg->mod_params->amsdu_size_8K) - priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_8K; + priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_8K); else - priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_4K; - priv->hw_params.max_pkt_size = priv->hw_params.rx_buf_size - 256; + priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_4K); priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL; @@ -1471,71 +1605,6 @@ int iwl_set_hw_params(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_set_hw_params); -int iwl_init_drv(struct iwl_priv *priv) -{ - int ret; - - priv->ibss_beacon = NULL; - - spin_lock_init(&priv->lock); - spin_lock_init(&priv->sta_lock); - spin_lock_init(&priv->hcmd_lock); - - INIT_LIST_HEAD(&priv->free_frames); - - mutex_init(&priv->mutex); - - /* Clear the driver's (not device's) station table */ - iwl_clear_stations_table(priv); - - priv->data_retry_limit = -1; - priv->ieee_channels = NULL; - priv->ieee_rates = NULL; - priv->band = IEEE80211_BAND_2GHZ; - - priv->iw_mode = NL80211_IFTYPE_STATION; - - priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED; - - /* Choose which receivers/antennas to use */ - if (priv->cfg->ops->hcmd->set_rxon_chain) - priv->cfg->ops->hcmd->set_rxon_chain(priv); - - iwl_init_scan_params(priv); - - iwl_reset_qos(priv); - - priv->qos_data.qos_active = 0; - priv->qos_data.qos_cap.val = 0; - - priv->rates_mask = IWL_RATES_MASK; - /* Set the tx_power_user_lmt to the lowest power level - * this value will get overwritten by channel max power avg - * from eeprom */ - priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MIN; - - ret = iwl_init_channel_map(priv); - if (ret) { - IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); - goto err; - } - - ret = iwlcore_init_geos(priv); - if (ret) { - IWL_ERR(priv, "initializing geos failed: %d\n", ret); - goto err_free_channel_map; - } - iwlcore_init_hw_rates(priv, priv->ieee_rates); - - return 0; - -err_free_channel_map: - iwl_free_channel_map(priv); -err: - return ret; -} -EXPORT_SYMBOL(iwl_init_drv); - int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) { int ret = 0; @@ -1583,15 +1652,6 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) } EXPORT_SYMBOL(iwl_set_tx_power); -void iwl_uninit_drv(struct iwl_priv *priv) -{ - iwl_calib_free_results(priv); - iwlcore_free_geos(priv); - iwl_free_channel_map(priv); - kfree(priv->scan); -} -EXPORT_SYMBOL(iwl_uninit_drv); - #define ICT_COUNT (PAGE_SIZE/sizeof(u32)) /* Free dram table */ @@ -1915,9 +1975,9 @@ EXPORT_SYMBOL(iwl_isr_legacy); int iwl_send_bt_config(struct iwl_priv *priv) { struct iwl_bt_cmd bt_cmd = { - .flags = 3, - .lead_time = 0xAA, - .max_kill = 1, + .flags = BT_COEX_MODE_4W, + .lead_time = BT_LEAD_TIME_DEF, + .max_kill = BT_MAX_KILL_DEF, .kill_ack_mask = 0, .kill_cts_mask = 0, }; @@ -1927,16 +1987,21 @@ int iwl_send_bt_config(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_send_bt_config); -int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags) +int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) { - u32 stat_flags = 0; - struct iwl_host_cmd cmd = { - .id = REPLY_STATISTICS_CMD, - .flags = flags, - .len = sizeof(stat_flags), - .data = (u8 *) &stat_flags, + struct iwl_statistics_cmd statistics_cmd = { + .configuration_flags = + clear ? IWL_STATS_CONF_CLEAR_STATS : 0, }; - return iwl_send_cmd(priv, &cmd); + + if (flags & CMD_ASYNC) + return iwl_send_cmd_pdu_async(priv, REPLY_STATISTICS_CMD, + sizeof(struct iwl_statistics_cmd), + &statistics_cmd, NULL); + else + return iwl_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, + sizeof(struct iwl_statistics_cmd), + &statistics_cmd); } EXPORT_SYMBOL(iwl_send_statistics_request); @@ -2077,10 +2142,7 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv) spin_unlock_irqrestore(&priv->lock, flags); priv->thermal_throttle.ct_kill_toggle = false; - switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { - case CSR_HW_REV_TYPE_1000: - case CSR_HW_REV_TYPE_6x00: - case CSR_HW_REV_TYPE_6x50: + if (priv->cfg->support_ct_kill_exit) { adv_cmd.critical_temperature_enter = cpu_to_le32(priv->hw_params.ct_kill_threshold); adv_cmd.critical_temperature_exit = @@ -2097,8 +2159,7 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv) "exit is %d\n", priv->hw_params.ct_kill_threshold, priv->hw_params.ct_kill_exit_threshold); - break; - default: + } else { cmd.critical_temperature_R = cpu_to_le32(priv->hw_params.ct_kill_threshold); @@ -2111,7 +2172,6 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv) "succeeded, " "critical temperature is %d\n", priv->hw_params.ct_kill_threshold); - break; } } EXPORT_SYMBOL(iwl_rf_kill_ct_config); @@ -2143,7 +2203,7 @@ void iwl_rx_pm_sleep_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { #ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_sleep_notification *sleep = &(pkt->u.sleep_notif); IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n", sleep->pm_sleep_mode, sleep->pm_wakeup_src); @@ -2154,7 +2214,7 @@ EXPORT_SYMBOL(iwl_rx_pm_sleep_notif); void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " "notification for %s:\n", len, @@ -2166,7 +2226,7 @@ EXPORT_SYMBOL(iwl_rx_pm_debug_statistics_notif); void iwl_rx_reply_error(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); IWL_ERR(priv, "Error Reply type 0x%08X cmd %s (0x%02X) " "seq 0x%04X ser 0x%08X\n", @@ -2228,42 +2288,58 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, EXPORT_SYMBOL(iwl_mac_conf_tx); static void iwl_ht_conf(struct iwl_priv *priv, - struct ieee80211_bss_conf *bss_conf) + struct ieee80211_bss_conf *bss_conf) { - struct ieee80211_sta_ht_cap *ht_conf; - struct iwl_ht_info *iwl_conf = &priv->current_ht_config; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; struct ieee80211_sta *sta; IWL_DEBUG_MAC80211(priv, "enter: \n"); - if (!iwl_conf->is_ht) + if (!ht_conf->is_ht) return; + ht_conf->ht_protection = + bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; + ht_conf->non_GF_STA_present = + !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - /* - * It is totally wrong to base global information on something - * that is valid only when associated, alas, this driver works - * that way and I don't know how to fix it. - */ + ht_conf->single_chain_sufficient = false; - rcu_read_lock(); - sta = ieee80211_find_sta(priv->hw, priv->bssid); - if (!sta) { + switch (priv->iw_mode) { + case NL80211_IFTYPE_STATION: + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, priv->bssid); + if (sta) { + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + int maxstreams; + + maxstreams = (ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) + >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + maxstreams += 1; + + if ((ht_cap->mcs.rx_mask[1] == 0) && + (ht_cap->mcs.rx_mask[2] == 0)) + ht_conf->single_chain_sufficient = true; + if (maxstreams <= 1) + ht_conf->single_chain_sufficient = true; + } else { + /* + * If at all, this can only happen through a race + * when the AP disconnects us while we're still + * setting up the connection, in that case mac80211 + * will soon tell us about that. + */ + ht_conf->single_chain_sufficient = true; + } rcu_read_unlock(); - return; + break; + case NL80211_IFTYPE_ADHOC: + ht_conf->single_chain_sufficient = true; + break; + default: + break; } - ht_conf = &sta->ht_cap; - - iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2); - - memcpy(&iwl_conf->mcs, &ht_conf->mcs, 16); - - iwl_conf->ht_protection = - bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; - iwl_conf->non_GF_STA_present = - !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - - rcu_read_unlock(); IWL_DEBUG_MAC80211(priv, "leave\n"); } @@ -2387,6 +2463,8 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, priv->timestamp = bss_conf->timestamp; priv->assoc_capability = bss_conf->assoc_capability; + iwl_led_associate(priv); + /* * We have just associated, don't start scan too early * leave time for EAPOL exchange to complete. @@ -2397,9 +2475,20 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, IWL_DELAY_NEXT_SCAN_AFTER_ASSOC; if (!iwl_is_rfkill(priv)) priv->cfg->ops->lib->post_associate(priv); - } else + } else { priv->assoc_id = 0; + iwl_led_disassociate(priv); + /* + * inform the ucode that there is no longer an + * association and that no more packets should be + * send + */ + priv->staging_rxon.filter_flags &= + ~RXON_FILTER_ASSOC_MSK; + priv->staging_rxon.assoc_id = 0; + iwlcore_commit_rxon(priv); + } } if (changes && iwl_is_associated(priv) && priv->assoc_id) { @@ -2414,6 +2503,14 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, } } + if ((changes & BSS_CHANGED_BEACON_ENABLED) && + vif->bss_conf.enable_beacon) { + memcpy(priv->staging_rxon.bssid_addr, + bss_conf->bssid, ETH_ALEN); + memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); + iwlcore_config_ap(priv); + } + mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); @@ -2570,7 +2667,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) struct iwl_priv *priv = hw->priv; const struct iwl_channel_info *ch_info; struct ieee80211_conf *conf = &hw->conf; - struct iwl_ht_info *ht_conf = &priv->current_ht_config; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; unsigned long flags = 0; int ret = 0; u16 ch; @@ -2620,21 +2717,18 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) if (conf_is_ht40_minus(conf)) { ht_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - ht_conf->supported_chan_width = - IWL_CHANNEL_WIDTH_40MHZ; + ht_conf->is_40mhz = true; } else if (conf_is_ht40_plus(conf)) { ht_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - ht_conf->supported_chan_width = - IWL_CHANNEL_WIDTH_40MHZ; + ht_conf->is_40mhz = true; } else { ht_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - ht_conf->supported_chan_width = - IWL_CHANNEL_WIDTH_20MHZ; + ht_conf->is_40mhz = false; } } else - ht_conf->supported_chan_width = IWL_CHANNEL_WIDTH_20MHZ; + ht_conf->is_40mhz = false; /* Default to no protection. Protection mode will later be set * from BSS config in iwl_ht_conf */ ht_conf->ht_protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; @@ -2649,6 +2743,22 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) iwl_set_flags_for_band(priv, conf->channel->band); spin_unlock_irqrestore(&priv->lock, flags); + if (iwl_is_associated(priv) && + (le16_to_cpu(priv->active_rxon.channel) != ch) && + priv->cfg->ops->lib->set_channel_switch) { + iwl_set_rate(priv); + /* + * at this point, staging_rxon has the + * configuration for channel switch + */ + ret = priv->cfg->ops->lib->set_channel_switch(priv, + ch); + if (!ret) { + iwl_print_rx_config_cmd(priv); + goto out; + } + priv->switch_rxon.switch_in_progress = false; + } set_ch_out: /* The list of supported rates and rate mask can be different * for each band; since the band may have changed, reset @@ -2656,7 +2766,8 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) iwl_set_rate(priv); } - if (changed & IEEE80211_CONF_CHANGE_PS) { + if (changed & (IEEE80211_CONF_CHANGE_PS | + IEEE80211_CONF_CHANGE_IDLE)) { ret = iwl_power_update_mode(priv, false); if (ret) IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); @@ -2740,7 +2851,7 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) IWL_DEBUG_MAC80211(priv, "enter\n"); spin_lock_irqsave(&priv->lock, flags); - memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_info)); + memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_config)); spin_unlock_irqrestore(&priv->lock, flags); iwl_reset_qos(priv); @@ -2792,6 +2903,55 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) } EXPORT_SYMBOL(iwl_mac_reset_tsf); +int iwl_alloc_txq_mem(struct iwl_priv *priv) +{ + if (!priv->txq) + priv->txq = kzalloc( + sizeof(struct iwl_tx_queue) * priv->cfg->num_of_queues, + GFP_KERNEL); + if (!priv->txq) { + IWL_ERR(priv, "Not enough memory for txq \n"); + return -ENOMEM; + } + return 0; +} +EXPORT_SYMBOL(iwl_alloc_txq_mem); + +void iwl_free_txq_mem(struct iwl_priv *priv) +{ + kfree(priv->txq); + priv->txq = NULL; +} +EXPORT_SYMBOL(iwl_free_txq_mem); + +int iwl_send_wimax_coex(struct iwl_priv *priv) +{ + struct iwl_wimax_coex_cmd uninitialized_var(coex_cmd); + + if (priv->cfg->support_wimax_coexist) { + /* UnMask wake up src at associated sleep */ + coex_cmd.flags |= COEX_FLAGS_ASSOC_WA_UNMASK_MSK; + + /* UnMask wake up src at unassociated sleep */ + coex_cmd.flags |= COEX_FLAGS_UNASSOC_WA_UNMASK_MSK; + memcpy(coex_cmd.sta_prio, cu_priorities, + sizeof(struct iwl_wimax_coex_event_entry) * + COEX_NUM_OF_EVENTS); + + /* enabling the coexistence feature */ + coex_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK; + + /* enabling the priorities tables */ + coex_cmd.flags |= COEX_FLAGS_STA_TABLE_VALID_MSK; + } else { + /* coexistence is disabled */ + memset(&coex_cmd, 0, sizeof(coex_cmd)); + } + return iwl_send_cmd_pdu(priv, COEX_PRIORITY_TABLE_CMD, + sizeof(coex_cmd), &coex_cmd); +} +EXPORT_SYMBOL(iwl_send_wimax_coex); + #ifdef CONFIG_IWLWIFI_DEBUGFS #define IWL_TRAFFIC_DUMP_SIZE (IWL_TRAFFIC_ENTRY_SIZE * IWL_TRAFFIC_ENTRIES) @@ -2929,15 +3089,11 @@ const char *get_ctrl_string(int cmd) } } -void iwl_clear_tx_stats(struct iwl_priv *priv) +void iwl_clear_traffic_stats(struct iwl_priv *priv) { memset(&priv->tx_stats, 0, sizeof(struct traffic_stats)); - -} - -void iwl_clear_rx_stats(struct iwl_priv *priv) -{ memset(&priv->rx_stats, 0, sizeof(struct traffic_stats)); + priv->led_tpt = 0; } /* @@ -3030,6 +3186,7 @@ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len) stats->data_cnt++; stats->data_bytes += len; } + iwl_leds_background(priv); } EXPORT_SYMBOL(iwl_update_stats); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 7754538c2194..675b7df632fc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -63,6 +63,8 @@ #ifndef __iwl_core_h__ #define __iwl_core_h__ +#include <linux/utsrelease.h> + /************************ * forward declarations * ************************/ @@ -70,7 +72,7 @@ struct iwl_host_cmd; struct iwl_cmd; -#define IWLWIFI_VERSION "1.3.27k" +#define IWLWIFI_VERSION UTS_RELEASE "-k" #define DRV_COPYRIGHT "Copyright(c) 2003-2009 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" @@ -89,6 +91,7 @@ struct iwl_hcmd_ops { int (*rxon_assoc)(struct iwl_priv *priv); int (*commit_rxon)(struct iwl_priv *priv); void (*set_rxon_chain)(struct iwl_priv *priv); + int (*set_tx_ant)(struct iwl_priv *priv, u8 valid_tx_ant); }; struct iwl_hcmd_utils_ops { @@ -97,7 +100,8 @@ struct iwl_hcmd_utils_ops { void (*gain_computation)(struct iwl_priv *priv, u32 *average_noise, u16 min_average_noise_antennat_i, - u32 min_average_noise); + u32 min_average_noise, + u8 default_chain); void (*chain_noise_reset)(struct iwl_priv *priv); void (*rts_tx_cmd_flag)(struct ieee80211_tx_info *info, __le32 *tx_flags); @@ -107,7 +111,6 @@ struct iwl_hcmd_utils_ops { struct iwl_apm_ops { int (*init)(struct iwl_priv *priv); - int (*reset)(struct iwl_priv *priv); void (*stop)(struct iwl_priv *priv); void (*config)(struct iwl_priv *priv); int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src); @@ -166,8 +169,9 @@ struct iwl_lib_ops { int (*is_valid_rtc_data_addr)(u32 addr); /* 1st ucode load */ int (*load_ucode)(struct iwl_priv *priv); - void (*dump_nic_event_log)(struct iwl_priv *priv); + void (*dump_nic_event_log)(struct iwl_priv *priv, bool full_log); void (*dump_nic_error_log)(struct iwl_priv *priv); + int (*set_channel_switch)(struct iwl_priv *priv, u16 channel); /* power management */ struct iwl_apm_ops apm_ops; @@ -185,18 +189,24 @@ struct iwl_lib_ops { struct iwl_temp_ops temp_ops; }; +struct iwl_led_ops { + int (*cmd)(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd); + int (*on)(struct iwl_priv *priv); + int (*off)(struct iwl_priv *priv); +}; + struct iwl_ops { const struct iwl_ucode_ops *ucode; const struct iwl_lib_ops *lib; const struct iwl_hcmd_ops *hcmd; const struct iwl_hcmd_utils_ops *utils; + const struct iwl_led_ops *led; }; struct iwl_mod_params { int sw_crypto; /* def: 0 = using hardware encryption */ int disable_hw_scan; /* def: 0 = use h/w scan */ int num_of_queues; /* def: HW dependent */ - int num_of_ampdu_queues;/* def: HW dependent */ int disable_11n; /* def: 0 = 11n capabilities enabled */ int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */ int antenna; /* def: 0 = both antennas (use diversity) */ @@ -213,7 +223,15 @@ struct iwl_mod_params { * @pa_type: used by 6000 series only to identify the type of Power Amplifier * @max_ll_items: max number of OTP blocks * @shadow_ram_support: shadow support for OTP memory + * @led_compensation: compensate on the led on/off time per HW according + * to the deviation to achieve the desired led frequency. + * The detail algorithm is described in iwl-led.c * @use_rts_for_ht: use rts/cts protection for HT traffic + * @chain_noise_num_beacons: number of beacons used to compute chain noise + * @adv_thermal_throttle: support advance thermal throttle + * @support_ct_kill_exit: support ct kill exit condition + * @sm_ps_mode: spatial multiplexing power save mode + * @support_wimax_coexist: support wimax/wifi co-exist * * We enable the driver to be backward compatible wrt API version. The * driver specifies which APIs it supports (with @ucode_api_max being the @@ -245,18 +263,32 @@ struct iwl_cfg { int eeprom_size; u16 eeprom_ver; u16 eeprom_calib_ver; + int num_of_queues; /* def: HW dependent */ + int num_of_ampdu_queues;/* def: HW dependent */ const struct iwl_ops *ops; const struct iwl_mod_params *mod_params; u8 valid_tx_ant; u8 valid_rx_ant; - bool need_pll_cfg; + + /* for iwl_apm_init() */ + u32 pll_cfg_val; + bool set_l0s; + bool use_bsm; + bool use_isr_legacy; enum iwl_pa_type pa_type; const u16 max_ll_items; const bool shadow_ram_support; const bool ht_greenfield_support; + u16 led_compensation; const bool broken_powersave; bool use_rts_for_ht; + int chain_noise_num_beacons; + const bool supports_idle; + bool adv_thermal_throttle; + bool support_ct_kill_exit; + u8 sm_ps_mode; + const bool support_wimax_coexist; }; /*************************** @@ -275,7 +307,7 @@ int iwl_check_rxon_cmd(struct iwl_priv *priv); int iwl_full_rxon_required(struct iwl_priv *priv); void iwl_set_rxon_chain(struct iwl_priv *priv); int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch); -void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info); +void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf); u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct ieee80211_sta_ht_cap *sta_ht_inf); void iwl_set_flags_for_band(struct iwl_priv *priv, enum ieee80211_band band); @@ -289,10 +321,7 @@ void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast); int iwl_hw_nic_init(struct iwl_priv *priv); -int iwl_setup_mac(struct iwl_priv *priv); int iwl_set_hw_params(struct iwl_priv *priv); -int iwl_init_drv(struct iwl_priv *priv); -void iwl_uninit_drv(struct iwl_priv *priv); bool iwl_is_monitor_mode(struct iwl_priv *priv); void iwl_post_associate(struct iwl_priv *priv); void iwl_bss_info_changed(struct ieee80211_hw *hw, @@ -311,6 +340,11 @@ void iwl_config_ap(struct iwl_priv *priv); int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats); void iwl_mac_reset_tsf(struct ieee80211_hw *hw); +int iwl_alloc_txq_mem(struct iwl_priv *priv); +void iwl_free_txq_mem(struct iwl_priv *priv); +void iwlcore_rts_tx_cmd_flag(struct ieee80211_tx_info *info, + __le32 *tx_flags); +int iwl_send_wimax_coex(struct iwl_priv *priv); #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_alloc_traffic_mem(struct iwl_priv *priv); void iwl_free_traffic_mem(struct iwl_priv *priv); @@ -321,8 +355,7 @@ void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, u16 length, struct ieee80211_hdr *header); const char *get_mgmt_string(int cmd); const char *get_ctrl_string(int cmd); -void iwl_clear_tx_stats(struct iwl_priv *priv); -void iwl_clear_rx_stats(struct iwl_priv *priv); +void iwl_clear_traffic_stats(struct iwl_priv *priv); void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len); #else @@ -358,6 +391,7 @@ static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx, /* data */ stats->data_bytes += len; } + iwl_leds_background(priv); } #endif /***************************************************** @@ -393,6 +427,8 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); +void iwl_reply_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); /* TX helpers */ @@ -511,7 +547,7 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len, const void *data, void (*callback)(struct iwl_priv *priv, struct iwl_device_cmd *cmd, - struct sk_buff *skb)); + struct iwl_rx_packet *pkt)); int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); @@ -544,15 +580,12 @@ int iwl_pci_resume(struct pci_dev *pdev); /***************************************************** * Error Handling Debugging ******************************************************/ -#ifdef CONFIG_IWLWIFI_DEBUG -void iwl_dump_nic_event_log(struct iwl_priv *priv); void iwl_dump_nic_error_log(struct iwl_priv *priv); +void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log); +#ifdef CONFIG_IWLWIFI_DEBUG +void iwl_print_rx_config_cmd(struct iwl_priv *priv); #else -static inline void iwl_dump_nic_event_log(struct iwl_priv *priv) -{ -} - -static inline void iwl_dump_nic_error_log(struct iwl_priv *priv) +static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv) { } #endif @@ -571,6 +604,7 @@ void iwlcore_free_geos(struct iwl_priv *priv); #define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */ #define STATUS_INT_ENABLED 2 #define STATUS_RF_KILL_HW 3 +#define STATUS_CT_KILL 4 #define STATUS_INIT 5 #define STATUS_ALIVE 6 #define STATUS_READY 7 @@ -615,6 +649,11 @@ static inline int iwl_is_rfkill(struct iwl_priv *priv) return iwl_is_rfkill_hw(priv); } +static inline int iwl_is_ctkill(struct iwl_priv *priv) +{ + return test_bit(STATUS_CT_KILL, &priv->status); +} + static inline int iwl_is_ready_rf(struct iwl_priv *priv) { @@ -626,7 +665,8 @@ static inline int iwl_is_ready_rf(struct iwl_priv *priv) extern void iwl_rf_kill_ct_config(struct iwl_priv *priv); extern int iwl_send_bt_config(struct iwl_priv *priv); -extern int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags); +extern int iwl_send_statistics_request(struct iwl_priv *priv, + u8 flags, bool clear); extern int iwl_verify_ucode(struct iwl_priv *priv); extern int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_link_quality_cmd *lq, u8 flags); @@ -636,6 +676,9 @@ extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); +void iwl_apm_stop(struct iwl_priv *priv); +int iwl_apm_stop_master(struct iwl_priv *priv); +int iwl_apm_init(struct iwl_priv *priv); void iwl_setup_rxon_timing(struct iwl_priv *priv); static inline int iwl_send_rxon_assoc(struct iwl_priv *priv) @@ -655,5 +698,4 @@ static inline const struct ieee80211_supported_band *iwl_get_hw_mode( { return priv->hw->wiphy->bands[band]; } - #endif /* __iwl_core_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 06437d13e73e..a7bfae01f19b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -62,11 +62,29 @@ *****************************************************************************/ #ifndef __iwl_csr_h__ #define __iwl_csr_h__ -/*=== CSR (control and status registers) ===*/ +/* + * CSR (control and status registers) + * + * CSR registers are mapped directly into PCI bus space, and are accessible + * whenever platform supplies power to device, even when device is in + * low power states due to driver-invoked device resets + * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. + * + * Use iwl_write32() and iwl_read32() family to access these registers; + * these provide simple PCI bus access, without waking up the MAC. + * Do not use iwl_write_direct32() family for these registers; + * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. + * The MAC (uCode processor, etc.) does not need to be powered up for accessing + * the CSR registers. + * + * NOTE: Newer devices using one-time-programmable (OTP) memory + * require device to be awake in order to read this memory + * via CSR_EEPROM and CSR_OTP registers + */ #define CSR_BASE (0x000) #define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ -#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ +#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ #define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */ #define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */ #define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/ @@ -74,43 +92,67 @@ #define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/ #define CSR_GP_CNTRL (CSR_BASE+0x024) +/* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */ +#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) + /* * Hardware revision info * Bit fields: * 31-8: Reserved - * 7-4: Type of device: 0x0 = 4965, 0xd = 3945 + * 7-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D - * 1-0: "Dash" value, as in A-1, etc. + * 1-0: "Dash" (-) value, as in A-1, etc. * * NOTE: Revision step affects calculation of CCK txpower for 4965. + * NOTE: See also CSR_HW_REV_WA_REG (work-around for bug in 4965). */ #define CSR_HW_REV (CSR_BASE+0x028) -/* EEPROM reads */ +/* + * EEPROM and OTP (one-time-programmable) memory reads + * + * NOTE: For (newer) devices using OTP, device must be awake, initialized via + * apm_ops.init() in order to read. Older devices (3945/4965/5000) + * use EEPROM and do not require this. + */ #define CSR_EEPROM_REG (CSR_BASE+0x02c) #define CSR_EEPROM_GP (CSR_BASE+0x030) #define CSR_OTP_GP_REG (CSR_BASE+0x034) + #define CSR_GIO_REG (CSR_BASE+0x03C) #define CSR_GP_UCODE_REG (CSR_BASE+0x048) #define CSR_GP_DRIVER_REG (CSR_BASE+0x050) + +/* + * UCODE-DRIVER GP (general purpose) mailbox registers. + * SET/CLR registers set/clear bit(s) if "1" is written. + */ #define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) #define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) #define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) #define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060) + #define CSR_LED_REG (CSR_BASE+0x094) #define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) + +/* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) -#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) /* Analog phase-lock-loop configuration */ #define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) + /* - * Indicates hardware rev, to determine CCK backoff for txpower calculation. + * CSR Hardware Revision Workaround Register. Indicates hardware rev; + * "step" determines CCK backoff for txpower calculation. Used for 4965 only. + * See also CSR_HW_REV register. * Bit fields: * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step + * 1-0: "Dash" (-) value, as in C-1, etc. */ -#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) -#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) +#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) + +#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) +#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) /* Bits for CSR_HW_IF_CONFIG_REG */ #define CSR49_HW_IF_CONFIG_REG_BIT_4965_R (0x00000010) @@ -125,14 +167,14 @@ #define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000) #define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000) -#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) -#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) -#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) -#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) -#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) +#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) +#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) +#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ +#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ +#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ -#define CSR_INT_PERIODIC_DIS (0x00) -#define CSR_INT_PERIODIC_ENA (0xFF) +#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/ +#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/ /* interrupt flags in INTA, set by uCode or hardware (e.g. dma), * acknowledged (reset) by host writing "1" to flagged bits. */ @@ -195,8 +237,46 @@ #define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) #define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) #define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) +#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) -/* GP (general purpose) CONTROL */ +/* + * GP (general purpose) CONTROL REGISTER + * Bit fields: + * 27: HW_RF_KILL_SW + * Indicates state of (platform's) hardware RF-Kill switch + * 26-24: POWER_SAVE_TYPE + * Indicates current power-saving mode: + * 000 -- No power saving + * 001 -- MAC power-down + * 010 -- PHY (radio) power-down + * 011 -- Error + * 9-6: SYS_CONFIG + * Indicates current system configuration, reflecting pins on chip + * as forced high/low by device circuit board. + * 4: GOING_TO_SLEEP + * Indicates MAC is entering a power-saving sleep power-down. + * Not a good time to access device-internal resources. + * 3: MAC_ACCESS_REQ + * Host sets this to request and maintain MAC wakeup, to allow host + * access to device-internal resources. Host must wait for + * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR + * device registers. + * 2: INIT_DONE + * Host sets this to put device into fully operational D0 power mode. + * Host resets this after SW_RESET to put device into low power mode. + * 0: MAC_CLOCK_READY + * Indicates MAC (ucode processor, etc.) is powered up and can run. + * Internal resources are accessible. + * NOTE: This does not indicate that the processor is actually running. + * NOTE: This does not indicate that 4965 or 3945 has completed + * init or post-power-down restore of internal SRAM memory. + * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that + * SRAM is restored and uCode is in normal operation mode. + * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and + * do not need to save/restore it. + * NOTE: After device reset, this bit remains "0" until host sets + * INIT_DONE + */ #define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) #define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) #define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) @@ -229,18 +309,58 @@ #define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) /* EEPROM GP */ -#define CSR_EEPROM_GP_VALID_MSK (0x00000007) -#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000) +#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ #define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) +#define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP (0x00000000) +#define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP (0x00000001) +#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) +#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) + +/* One-time-programmable memory general purpose reg */ #define CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */ #define CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */ #define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */ #define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */ +/* GP REG */ +#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ +#define CSR_GP_REG_NO_POWER_SAVE (0x00000000) +#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000) +#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000) +#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) + + /* CSR GIO */ #define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) -/* UCODE DRV GP */ +/* + * UCODE-DRIVER GP (general purpose) mailbox register 1 + * Host driver and uCode write and/or read this register to communicate with + * each other. + * Bit fields: + * 4: UCODE_DISABLE + * Host sets this to request permanent halt of uCode, same as + * sending CARD_STATE command with "halt" bit set. + * 3: CT_KILL_EXIT + * Host sets this to request exit from CT_KILL state, i.e. host thinks + * device temperature is low enough to continue normal operation. + * 2: CMD_BLOCKED + * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) + * to release uCode to clear all Tx and command queues, enter + * unassociated mode, and power down. + * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. + * 1: SW_BIT_RFKILL + * Host sets this when issuing CARD_STATE command to request + * device sleep. + * 0: MAC_SLEEP + * uCode sets this when preparing a power-saving power-down. + * uCode resets this when power-up is complete and SRAM is sane. + * NOTE: 3945/4965 saves internal SRAM data to host when powering down, + * and must restore this data after powering back up. + * MAC_SLEEP is the best indication that restore is complete. + * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and + * do not need to save/restore it. + */ #define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) #define CSR_UCODE_SW_BIT_RFKILL (0x00000002) #define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) @@ -253,7 +373,7 @@ #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002) -/* GI Chicken Bits */ +/* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) #define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) @@ -273,8 +393,23 @@ #define CSR_DRAM_INT_TBL_ENABLE (1 << 31) #define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) -/*=== HBUS (Host-side Bus) ===*/ +/* + * HBUS (Host-side Bus) + * + * HBUS registers are mapped directly into PCI bus space, but are used + * to indirectly access device's internal memory or registers that + * may be powered-down. + * + * Use iwl_write_direct32()/iwl_read_direct32() family for these registers; + * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ + * to make sure the MAC (uCode processor, etc.) is powered up for accessing + * internal resources. + * + * Do not use iwl_write32()/iwl_read32() family to access these registers; + * these provide only simple PCI bus access, without waking up the MAC. + */ #define HBUS_BASE (0x400) + /* * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM * structures, error log, event log, verifying uCode load). @@ -289,6 +424,10 @@ #define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018) #define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c) +/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ +#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) +#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) + /* * Registers for accessing device's internal peripheral registers * (e.g. SCD, BSM, etc.). First write to address register, @@ -303,16 +442,12 @@ #define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050) /* - * Per-Tx-queue write pointer (index, really!) (3945 and 4965). + * Per-Tx-queue write pointer (index, really!) * Indicates index to next TFD that driver will fill (1 past latest filled). * Bit usage: * 0-7: queue write index * 11-8: queue selector */ #define HBUS_TARG_WRPTR (HBUS_BASE+0x060) -#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) - -#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) - #endif /* !__iwl_csr_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index cbc62904655d..d61293ab67c9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -84,9 +84,7 @@ struct iwl_debugfs { struct dentry *file_interrupt; struct dentry *file_qos; struct dentry *file_thermal_throttling; -#ifdef CONFIG_IWLWIFI_LEDS struct dentry *file_led; -#endif struct dentry *file_disable_ht40; struct dentry *file_sleep_level_override; struct dentry *file_current_sleep_command; @@ -108,6 +106,9 @@ struct iwl_debugfs { struct dentry *file_sensitivity; struct dentry *file_chain_noise; struct dentry *file_tx_power; + struct dentry *file_power_save_status; + struct dentry *file_clear_ucode_statistics; + struct dentry *file_clear_traffic_statistics; } dbgfs_debug_files; u32 sram_offset; u32 sram_len; diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index a198bcf61022..21e0f6699daf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -47,9 +47,9 @@ goto err; \ } while (0) -#define DEBUGFS_ADD_FILE(name, parent) do { \ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_file(#name, S_IWUSR | S_IRUSR, \ + debugfs_create_file(#name, mode, \ dbgfs->dir_##parent, priv, \ &iwl_dbgfs_##name##_ops); \ if (!(dbgfs->dbgfs_##parent##_files.file_##name)) \ @@ -131,21 +131,22 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file, int cnt; ssize_t ret; - const size_t bufsz = 100 + sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX); + const size_t bufsz = 100 + + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, - "\t%s\t\t: %u\n", + "\t%25s\t\t: %u\n", get_mgmt_string(cnt), priv->tx_stats.mgmt[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "Control\n"); for (cnt = 0; cnt < CONTROL_MAX; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, - "\t%s\t\t: %u\n", + "\t%25s\t\t: %u\n", get_ctrl_string(cnt), priv->tx_stats.ctrl[cnt]); } @@ -159,7 +160,7 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file, return ret; } -static ssize_t iwl_dbgfs_tx_statistics_write(struct file *file, +static ssize_t iwl_dbgfs_clear_traffic_statistics_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { @@ -174,8 +175,7 @@ static ssize_t iwl_dbgfs_tx_statistics_write(struct file *file, return -EFAULT; if (sscanf(buf, "%x", &clear_flag) != 1) return -EFAULT; - if (clear_flag == 1) - iwl_clear_tx_stats(priv); + iwl_clear_traffic_stats(priv); return count; } @@ -190,7 +190,7 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, int cnt; ssize_t ret; const size_t bufsz = 100 + - sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX); + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -198,14 +198,14 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, - "\t%s\t\t: %u\n", + "\t%25s\t\t: %u\n", get_mgmt_string(cnt), priv->rx_stats.mgmt[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "Control:\n"); for (cnt = 0; cnt < CONTROL_MAX; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, - "\t%s\t\t: %u\n", + "\t%25s\t\t: %u\n", get_ctrl_string(cnt), priv->rx_stats.ctrl[cnt]); } @@ -220,26 +220,6 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, return ret; } -static ssize_t iwl_dbgfs_rx_statistics_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - u32 clear_flag; - char buf[8]; - int buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &clear_flag) != 1) - return -EFAULT; - if (clear_flag == 1) - iwl_clear_rx_stats(priv); - return count; -} - #define BYTE1_MASK 0x000000ff; #define BYTE2_MASK 0x0000ffff; #define BYTE3_MASK 0x00ffffff; @@ -248,13 +228,29 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, size_t count, loff_t *ppos) { u32 val; - char buf[1024]; + char *buf; ssize_t ret; int i; int pos = 0; struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - const size_t bufsz = sizeof(buf); - + size_t bufsz; + + /* default is to dump the entire data segment */ + if (!priv->dbgfs->sram_offset && !priv->dbgfs->sram_len) { + priv->dbgfs->sram_offset = 0x800000; + if (priv->ucode_type == UCODE_INIT) + priv->dbgfs->sram_len = priv->ucode_init_data.len; + else + priv->dbgfs->sram_len = priv->ucode_data.len; + } + bufsz = 30 + priv->dbgfs->sram_len * sizeof(char) * 10; + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", + priv->dbgfs->sram_len); + pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", + priv->dbgfs->sram_offset); for (i = priv->dbgfs->sram_len; i > 0; i -= 4) { val = iwl_read_targ_mem(priv, priv->dbgfs->sram_offset + \ priv->dbgfs->sram_len - i); @@ -271,11 +267,14 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, break; } } + if (!(i % 16)) + pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); return ret; } @@ -335,8 +334,6 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, pos += scnprintf(buf + pos, bufsz - pos, "flags: 0x%x\n", station->sta.station_flags_msk); - pos += scnprintf(buf + pos, bufsz - pos, - "ps_status: %u\n", station->ps_status); pos += scnprintf(buf + pos, bufsz - pos, "tid data:\n"); pos += scnprintf(buf + pos, bufsz - pos, "seq_num\t\ttxq_id"); @@ -383,6 +380,7 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, int pos = 0, ofs = 0, buf_size = 0; const u8 *ptr; char *buf; + u16 eeprom_ver; size_t eeprom_len = priv->cfg->eeprom_size; buf_size = 4 * eeprom_len + 256; @@ -403,9 +401,11 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, IWL_ERR(priv, "Can not allocate Buffer\n"); return -ENOMEM; } - pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s\n", + eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); + pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s, " + "version: 0x%x\n", (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM"); + ? "OTP" : "EEPROM", eeprom_ver); for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos, @@ -436,7 +436,7 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file, if (sscanf(buf, "%d", &event_log_flag) != 1) return -EFAULT; if (event_log_flag == 1) - priv->cfg->ops->lib->dump_nic_event_log(priv); + priv->cfg->ops->lib->dump_nic_event_log(priv, true); return count; } @@ -532,6 +532,8 @@ static ssize_t iwl_dbgfs_status_read(struct file *file, test_bit(STATUS_INT_ENABLED, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", test_bit(STATUS_RF_KILL_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n", + test_bit(STATUS_CT_KILL, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n", test_bit(STATUS_INIT, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", @@ -672,7 +674,6 @@ static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, return ret; } -#ifdef CONFIG_IWLWIFI_LEDS static ssize_t iwl_dbgfs_led_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -697,7 +698,6 @@ static ssize_t iwl_dbgfs_led_read(struct file *file, char __user *user_buf, ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); return ret; } -#endif static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, char __user *user_buf, @@ -798,15 +798,20 @@ static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, * valid here. However, let's not confuse them and present * IWL_POWER_INDEX_1 as "1", not "0". */ - if (value > 0) + if (value == 0) + return -EINVAL; + else if (value > 0) value -= 1; if (value != -1 && (value < 0 || value >= IWL_POWER_NUM)) return -EINVAL; + if (!iwl_is_ready_rf(priv)) + return -EAGAIN; + priv->power_data.debug_sleep_level_override = value; - iwl_power_update_mode(priv, false); + iwl_power_update_mode(priv, true); return count; } @@ -861,9 +866,7 @@ DEBUGFS_READ_FILE_OPS(channels); DEBUGFS_READ_FILE_OPS(status); DEBUGFS_READ_WRITE_FILE_OPS(interrupt); DEBUGFS_READ_FILE_OPS(qos); -#ifdef CONFIG_IWLWIFI_LEDS DEBUGFS_READ_FILE_OPS(led); -#endif DEBUGFS_READ_FILE_OPS(thermal_throttling); DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); @@ -881,10 +884,14 @@ static ssize_t iwl_dbgfs_traffic_log_read(struct file *file, struct iwl_rx_queue *rxq = &priv->rxq; char *buf; int bufsz = ((IWL_TRAFFIC_ENTRIES * IWL_TRAFFIC_ENTRY_SIZE * 64) * 2) + - (IWL_MAX_NUM_QUEUES * 32 * 8) + 400; + (priv->cfg->num_of_queues * 32 * 8) + 400; const u8 *ptr; ssize_t ret; + if (!priv->txq) { + IWL_ERR(priv, "txq not ready\n"); + return -EAGAIN; + } buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IWL_ERR(priv, "Can not allocate buffer\n"); @@ -976,8 +983,12 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, int pos = 0; int cnt; int ret; - const size_t bufsz = sizeof(char) * 60 * IWL_MAX_NUM_QUEUES; + const size_t bufsz = sizeof(char) * 64 * priv->cfg->num_of_queues; + if (!priv->txq) { + IWL_ERR(priv, "txq not ready\n"); + return -EAGAIN; + } buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -1028,10 +1039,6 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) -#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) -#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) - static int iwl_dbgfs_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz) { @@ -1068,17 +1075,17 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, sizeof(struct statistics_rx_non_phy) * 20 + sizeof(struct statistics_rx_ht_phy) * 20 + 400; ssize_t ret; - struct statistics_rx_phy *ofdm; - struct statistics_rx_phy *cck; - struct statistics_rx_non_phy *general; - struct statistics_rx_ht_phy *ht; + struct statistics_rx_phy *ofdm, *accum_ofdm; + struct statistics_rx_phy *cck, *accum_cck; + struct statistics_rx_non_phy *general, *accum_general; + struct statistics_rx_ht_phy *ht, *accum_ht; if (!iwl_is_alive(priv)) return -EAGAIN; /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, 0); + ret = iwl_send_statistics_request(priv, CMD_SYNC, false); mutex_unlock(&priv->mutex); if (ret) { @@ -1100,155 +1107,268 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, cck = &priv->statistics.rx.cck; general = &priv->statistics.rx.general; ht = &priv->statistics.rx.ofdm_ht; + accum_ofdm = &priv->accum_statistics.rx.ofdm; + accum_cck = &priv->accum_statistics.rx.cck; + accum_general = &priv->accum_statistics.rx.general; + accum_ht = &priv->accum_statistics.rx.ofdm_ht; pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt: %u\n", - le32_to_cpu(ofdm->ina_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt: %u\n", - le32_to_cpu(ofdm->fina_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", - le32_to_cpu(ofdm->plcp_err)); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", - le32_to_cpu(ofdm->crc32_err)); - pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", - le32_to_cpu(ofdm->overrun_err)); - pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", - le32_to_cpu(ofdm->early_overrun_err)); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", - le32_to_cpu(ofdm->crc32_good)); - pos += scnprintf(buf + pos, bufsz - pos, "false_alarm_cnt: %u\n", - le32_to_cpu(ofdm->false_alarm_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "fina_sync_err_cnt: %u\n", - le32_to_cpu(ofdm->fina_sync_err_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "sfd_timeout: %u\n", - le32_to_cpu(ofdm->sfd_timeout)); - pos += scnprintf(buf + pos, bufsz - pos, "fina_timeout: %u\n", - le32_to_cpu(ofdm->fina_timeout)); - pos += scnprintf(buf + pos, bufsz - pos, "unresponded_rts: %u\n", - le32_to_cpu(ofdm->unresponded_rts)); - pos += scnprintf(buf + pos, bufsz - pos, - "rxe_frame_limit_overrun: %u\n", - le32_to_cpu(ofdm->rxe_frame_limit_overrun)); - pos += scnprintf(buf + pos, bufsz - pos, "sent_ack_cnt: %u\n", - le32_to_cpu(ofdm->sent_ack_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "sent_cts_cnt: %u\n", - le32_to_cpu(ofdm->sent_cts_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "sent_ba_rsp_cnt: %u\n", - le32_to_cpu(ofdm->sent_ba_rsp_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "dsp_self_kill: %u\n", - le32_to_cpu(ofdm->dsp_self_kill)); - pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", - le32_to_cpu(ofdm->mh_format_err)); - pos += scnprintf(buf + pos, bufsz - pos, "re_acq_main_rssi_sum: %u\n", - le32_to_cpu(ofdm->re_acq_main_rssi_sum)); + pos += scnprintf(buf + pos, bufsz - pos, + "\t\t\tcurrent\t\t\taccumulative\n"); + pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + "overrun_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->overrun_err), + accum_ofdm->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + "early_overrun_err:\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->early_overrun_err), + accum_ofdm->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->crc32_good), + accum_ofdm->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + "false_alarm_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->false_alarm_cnt), + accum_ofdm->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "fina_sync_err_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->fina_sync_err_cnt), + accum_ofdm->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "sfd_timeout:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->sfd_timeout), + accum_ofdm->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + "fina_timeout:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->fina_timeout), + accum_ofdm->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + "unresponded_rts:\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->unresponded_rts), + accum_ofdm->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, + "rxe_frame_lmt_ovrun:\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->rxe_frame_limit_overrun), + accum_ofdm->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, + "sent_ack_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->sent_ack_cnt), + accum_ofdm->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "sent_cts_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->sent_cts_cnt), + accum_ofdm->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "sent_ba_rsp_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->sent_ba_rsp_cnt), + accum_ofdm->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "dsp_self_kill:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->dsp_self_kill), + accum_ofdm->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, + "mh_format_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->mh_format_err), + accum_ofdm->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + "re_acq_main_rssi_sum:\t%u\t\t\t%u\n", + le32_to_cpu(ofdm->re_acq_main_rssi_sum), + accum_ofdm->re_acq_main_rssi_sum); pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - CCK:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt: %u\n", - le32_to_cpu(cck->ina_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt: %u\n", - le32_to_cpu(cck->fina_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", - le32_to_cpu(cck->plcp_err)); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", - le32_to_cpu(cck->crc32_err)); - pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", - le32_to_cpu(cck->overrun_err)); - pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", - le32_to_cpu(cck->early_overrun_err)); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", - le32_to_cpu(cck->crc32_good)); - pos += scnprintf(buf + pos, bufsz - pos, "false_alarm_cnt: %u\n", - le32_to_cpu(cck->false_alarm_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "fina_sync_err_cnt: %u\n", - le32_to_cpu(cck->fina_sync_err_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "sfd_timeout: %u\n", - le32_to_cpu(cck->sfd_timeout)); - pos += scnprintf(buf + pos, bufsz - pos, "fina_timeout: %u\n", - le32_to_cpu(cck->fina_timeout)); - pos += scnprintf(buf + pos, bufsz - pos, "unresponded_rts: %u\n", - le32_to_cpu(cck->unresponded_rts)); - pos += scnprintf(buf + pos, bufsz - pos, - "rxe_frame_limit_overrun: %u\n", - le32_to_cpu(cck->rxe_frame_limit_overrun)); - pos += scnprintf(buf + pos, bufsz - pos, "sent_ack_cnt: %u\n", - le32_to_cpu(cck->sent_ack_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "sent_cts_cnt: %u\n", - le32_to_cpu(cck->sent_cts_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "sent_ba_rsp_cnt: %u\n", - le32_to_cpu(cck->sent_ba_rsp_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "dsp_self_kill: %u\n", - le32_to_cpu(cck->dsp_self_kill)); - pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", - le32_to_cpu(cck->mh_format_err)); - pos += scnprintf(buf + pos, bufsz - pos, "re_acq_main_rssi_sum: %u\n", - le32_to_cpu(cck->re_acq_main_rssi_sum)); + pos += scnprintf(buf + pos, bufsz - pos, + "\t\t\tcurrent\t\t\taccumulative\n"); + pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->plcp_err), accum_cck->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->crc32_err), accum_cck->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + "overrun_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->overrun_err), + accum_cck->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + "early_overrun_err:\t%u\t\t\t%u\n", + le32_to_cpu(cck->early_overrun_err), + accum_cck->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->crc32_good), accum_cck->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + "false_alarm_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(cck->false_alarm_cnt), + accum_cck->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "fina_sync_err_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(cck->fina_sync_err_cnt), + accum_cck->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "sfd_timeout:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->sfd_timeout), + accum_cck->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + "fina_timeout:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->fina_timeout), + accum_cck->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + "unresponded_rts:\t%u\t\t\t%u\n", + le32_to_cpu(cck->unresponded_rts), + accum_cck->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, + "rxe_frame_lmt_ovrun:\t%u\t\t\t%u\n", + le32_to_cpu(cck->rxe_frame_limit_overrun), + accum_cck->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, + "sent_ack_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->sent_ack_cnt), + accum_cck->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "sent_cts_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->sent_cts_cnt), + accum_cck->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "sent_ba_rsp_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(cck->sent_ba_rsp_cnt), + accum_cck->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "dsp_self_kill:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->dsp_self_kill), + accum_cck->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, + "mh_format_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(cck->mh_format_err), + accum_cck->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + "re_acq_main_rssi_sum:\t%u\t\t\t%u\n", + le32_to_cpu(cck->re_acq_main_rssi_sum), + accum_cck->re_acq_main_rssi_sum); pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - GENERAL:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "bogus_cts: %u\n", - le32_to_cpu(general->bogus_cts)); - pos += scnprintf(buf + pos, bufsz - pos, "bogus_ack: %u\n", - le32_to_cpu(general->bogus_ack)); - pos += scnprintf(buf + pos, bufsz - pos, "non_bssid_frames: %u\n", - le32_to_cpu(general->non_bssid_frames)); - pos += scnprintf(buf + pos, bufsz - pos, "filtered_frames: %u\n", - le32_to_cpu(general->filtered_frames)); - pos += scnprintf(buf + pos, bufsz - pos, "non_channel_beacons: %u\n", - le32_to_cpu(general->non_channel_beacons)); - pos += scnprintf(buf + pos, bufsz - pos, "channel_beacons: %u\n", - le32_to_cpu(general->channel_beacons)); - pos += scnprintf(buf + pos, bufsz - pos, "num_missed_bcon: %u\n", - le32_to_cpu(general->num_missed_bcon)); - pos += scnprintf(buf + pos, bufsz - pos, - "adc_rx_saturation_time: %u\n", - le32_to_cpu(general->adc_rx_saturation_time)); - pos += scnprintf(buf + pos, bufsz - pos, - "ina_detection_search_time: %u\n", - le32_to_cpu(general->ina_detection_search_time)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_a: %u\n", - le32_to_cpu(general->beacon_silence_rssi_a)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_b: %u\n", - le32_to_cpu(general->beacon_silence_rssi_b)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_c: %u\n", - le32_to_cpu(general->beacon_silence_rssi_c)); - pos += scnprintf(buf + pos, bufsz - pos, - "interference_data_flag: %u\n", - le32_to_cpu(general->interference_data_flag)); - pos += scnprintf(buf + pos, bufsz - pos, "channel_load: %u\n", - le32_to_cpu(general->channel_load)); - pos += scnprintf(buf + pos, bufsz - pos, "dsp_false_alarms: %u\n", - le32_to_cpu(general->dsp_false_alarms)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_a: %u\n", - le32_to_cpu(general->beacon_rssi_a)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_b: %u\n", - le32_to_cpu(general->beacon_rssi_b)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_c: %u\n", - le32_to_cpu(general->beacon_rssi_c)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_a: %u\n", - le32_to_cpu(general->beacon_energy_a)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_b: %u\n", - le32_to_cpu(general->beacon_energy_b)); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_c: %u\n", - le32_to_cpu(general->beacon_energy_c)); + pos += scnprintf(buf + pos, bufsz - pos, + "\t\t\tcurrent\t\t\taccumulative\n"); + pos += scnprintf(buf + pos, bufsz - pos, "bogus_cts:\t\t%u\t\t\t%u\n", + le32_to_cpu(general->bogus_cts), + accum_general->bogus_cts); + pos += scnprintf(buf + pos, bufsz - pos, "bogus_ack:\t\t%u\t\t\t%u\n", + le32_to_cpu(general->bogus_ack), + accum_general->bogus_ack); + pos += scnprintf(buf + pos, bufsz - pos, + "non_bssid_frames:\t%u\t\t\t%u\n", + le32_to_cpu(general->non_bssid_frames), + accum_general->non_bssid_frames); + pos += scnprintf(buf + pos, bufsz - pos, + "filtered_frames:\t%u\t\t\t%u\n", + le32_to_cpu(general->filtered_frames), + accum_general->filtered_frames); + pos += scnprintf(buf + pos, bufsz - pos, + "non_channel_beacons:\t%u\t\t\t%u\n", + le32_to_cpu(general->non_channel_beacons), + accum_general->non_channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, + "channel_beacons:\t%u\t\t\t%u\n", + le32_to_cpu(general->channel_beacons), + accum_general->channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, + "num_missed_bcon:\t%u\t\t\t%u\n", + le32_to_cpu(general->num_missed_bcon), + accum_general->num_missed_bcon); + pos += scnprintf(buf + pos, bufsz - pos, + "adc_rx_saturation_time:\t%u\t\t\t%u\n", + le32_to_cpu(general->adc_rx_saturation_time), + accum_general->adc_rx_saturation_time); + pos += scnprintf(buf + pos, bufsz - pos, + "ina_detect_search_tm:\t%u\t\t\t%u\n", + le32_to_cpu(general->ina_detection_search_time), + accum_general->ina_detection_search_time); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_silence_rssi_a:\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_silence_rssi_a), + accum_general->beacon_silence_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_silence_rssi_b:\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_silence_rssi_b), + accum_general->beacon_silence_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_silence_rssi_c:\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_silence_rssi_c), + accum_general->beacon_silence_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, + "interference_data_flag:\t%u\t\t\t%u\n", + le32_to_cpu(general->interference_data_flag), + accum_general->interference_data_flag); + pos += scnprintf(buf + pos, bufsz - pos, + "channel_load:\t\t%u\t\t\t%u\n", + le32_to_cpu(general->channel_load), + accum_general->channel_load); + pos += scnprintf(buf + pos, bufsz - pos, + "dsp_false_alarms:\t%u\t\t\t%u\n", + le32_to_cpu(general->dsp_false_alarms), + accum_general->dsp_false_alarms); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_rssi_a:\t\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_rssi_a), + accum_general->beacon_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_rssi_b:\t\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_rssi_b), + accum_general->beacon_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_rssi_c:\t\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_rssi_c), + accum_general->beacon_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_energy_a:\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_energy_a), + accum_general->beacon_energy_a); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_energy_b:\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_energy_b), + accum_general->beacon_energy_b); + pos += scnprintf(buf + pos, bufsz - pos, + "beacon_energy_c:\t%u\t\t\t%u\n", + le32_to_cpu(general->beacon_energy_c), + accum_general->beacon_energy_c); pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM_HT:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", - le32_to_cpu(ht->plcp_err)); - pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", - le32_to_cpu(ht->overrun_err)); - pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", - le32_to_cpu(ht->early_overrun_err)); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", - le32_to_cpu(ht->crc32_good)); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", - le32_to_cpu(ht->crc32_err)); - pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", - le32_to_cpu(ht->mh_format_err)); - pos += scnprintf(buf + pos, bufsz - pos, "agg_crc32_good: %u\n", - le32_to_cpu(ht->agg_crc32_good)); - pos += scnprintf(buf + pos, bufsz - pos, "agg_mpdu_cnt: %u\n", - le32_to_cpu(ht->agg_mpdu_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "agg_cnt: %u\n", - le32_to_cpu(ht->agg_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, + "\t\t\tcurrent\t\t\taccumulative\n"); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->plcp_err), accum_ht->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + "overrun_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->overrun_err), accum_ht->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + "early_overrun_err:\t%u\t\t\t%u\n", + le32_to_cpu(ht->early_overrun_err), + accum_ht->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->crc32_good), accum_ht->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->crc32_err), accum_ht->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + "mh_format_err:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->mh_format_err), + accum_ht->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + "agg_crc32_good:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->agg_crc32_good), + accum_ht->agg_crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + "agg_mpdu_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->agg_mpdu_cnt), + accum_ht->agg_mpdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "agg_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); @@ -1264,14 +1384,14 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, char *buf; int bufsz = (sizeof(struct statistics_tx) * 24) + 250; ssize_t ret; - struct statistics_tx *tx; + struct statistics_tx *tx, *accum_tx; if (!iwl_is_alive(priv)) return -EAGAIN; /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, 0); + ret = iwl_send_statistics_request(priv, CMD_SYNC, false); mutex_unlock(&priv->mutex); if (ret) { @@ -1290,62 +1410,107 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, * might not reflect the current uCode activity */ tx = &priv->statistics.tx; + accum_tx = &priv->accum_statistics.tx; pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Tx:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "preamble: %u\n", - le32_to_cpu(tx->preamble_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "rx_detected_cnt: %u\n", - le32_to_cpu(tx->rx_detected_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "bt_prio_defer_cnt: %u\n", - le32_to_cpu(tx->bt_prio_defer_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "bt_prio_kill_cnt: %u\n", - le32_to_cpu(tx->bt_prio_kill_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "few_bytes_cnt: %u\n", - le32_to_cpu(tx->few_bytes_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "cts_timeout: %u\n", - le32_to_cpu(tx->cts_timeout)); - pos += scnprintf(buf + pos, bufsz - pos, "ack_timeout: %u\n", - le32_to_cpu(tx->ack_timeout)); - pos += scnprintf(buf + pos, bufsz - pos, "expected_ack_cnt: %u\n", - le32_to_cpu(tx->expected_ack_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "actual_ack_cnt: %u\n", - le32_to_cpu(tx->actual_ack_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "dump_msdu_cnt: %u\n", - le32_to_cpu(tx->dump_msdu_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, - "burst_abort_next_frame_mismatch_cnt: %u\n", - le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, - "burst_abort_missing_next_frame_cnt: %u\n", - le32_to_cpu(tx->burst_abort_missing_next_frame_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "cts_timeout_collision: %u\n", - le32_to_cpu(tx->cts_timeout_collision)); - pos += scnprintf(buf + pos, bufsz - pos, - "ack_or_ba_timeout_collision: %u\n", - le32_to_cpu(tx->ack_or_ba_timeout_collision)); - pos += scnprintf(buf + pos, bufsz - pos, "agg ba_timeout: %u\n", - le32_to_cpu(tx->agg.ba_timeout)); - pos += scnprintf(buf + pos, bufsz - pos, - "agg ba_reschedule_frames: %u\n", - le32_to_cpu(tx->agg.ba_reschedule_frames)); - pos += scnprintf(buf + pos, bufsz - pos, - "agg scd_query_agg_frame_cnt: %u\n", - le32_to_cpu(tx->agg.scd_query_agg_frame_cnt)); - pos += scnprintf(buf + pos, bufsz - pos, "agg scd_query_no_agg: %u\n", - le32_to_cpu(tx->agg.scd_query_no_agg)); - pos += scnprintf(buf + pos, bufsz - pos, "agg scd_query_agg: %u\n", - le32_to_cpu(tx->agg.scd_query_agg)); - pos += scnprintf(buf + pos, bufsz - pos, - "agg scd_query_mismatch: %u\n", - le32_to_cpu(tx->agg.scd_query_mismatch)); - pos += scnprintf(buf + pos, bufsz - pos, "agg frame_not_ready: %u\n", - le32_to_cpu(tx->agg.frame_not_ready)); - pos += scnprintf(buf + pos, bufsz - pos, "agg underrun: %u\n", - le32_to_cpu(tx->agg.underrun)); - pos += scnprintf(buf + pos, bufsz - pos, "agg bt_prio_kill: %u\n", - le32_to_cpu(tx->agg.bt_prio_kill)); - pos += scnprintf(buf + pos, bufsz - pos, "agg rx_ba_rsp_cnt: %u\n", - le32_to_cpu(tx->agg.rx_ba_rsp_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, + "\t\t\tcurrent\t\t\taccumulative\n"); + pos += scnprintf(buf + pos, bufsz - pos, "preamble:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->preamble_cnt), + accum_tx->preamble_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "rx_detected_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->rx_detected_cnt), + accum_tx->rx_detected_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "bt_prio_defer_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->bt_prio_defer_cnt), + accum_tx->bt_prio_defer_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "bt_prio_kill_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->bt_prio_kill_cnt), + accum_tx->bt_prio_kill_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "few_bytes_cnt:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->few_bytes_cnt), + accum_tx->few_bytes_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "cts_timeout:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + "ack_timeout:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->ack_timeout), + accum_tx->ack_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + "expected_ack_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->expected_ack_cnt), + accum_tx->expected_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "actual_ack_cnt:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->actual_ack_cnt), + accum_tx->actual_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "dump_msdu_cnt:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->dump_msdu_cnt), + accum_tx->dump_msdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "abort_nxt_frame_mismatch:" + "\t%u\t\t\t%u\n", + le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), + accum_tx->burst_abort_next_frame_mismatch_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "abort_missing_nxt_frame:" + "\t%u\t\t\t%u\n", + le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), + accum_tx->burst_abort_missing_next_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "cts_timeout_collision:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->cts_timeout_collision), + accum_tx->cts_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, + "ack_ba_timeout_collision:\t%u\t\t\t%u\n", + le32_to_cpu(tx->ack_or_ba_timeout_collision), + accum_tx->ack_or_ba_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, + "agg ba_timeout:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.ba_timeout), + accum_tx->agg.ba_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + "agg ba_resched_frames:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.ba_reschedule_frames), + accum_tx->agg.ba_reschedule_frames); + pos += scnprintf(buf + pos, bufsz - pos, + "agg scd_query_agg_frame:\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), + accum_tx->agg.scd_query_agg_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "agg scd_query_no_agg:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.scd_query_no_agg), + accum_tx->agg.scd_query_no_agg); + pos += scnprintf(buf + pos, bufsz - pos, + "agg scd_query_agg:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.scd_query_agg), + accum_tx->agg.scd_query_agg); + pos += scnprintf(buf + pos, bufsz - pos, + "agg scd_query_mismatch:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.scd_query_mismatch), + accum_tx->agg.scd_query_mismatch); + pos += scnprintf(buf + pos, bufsz - pos, + "agg frame_not_ready:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.frame_not_ready), + accum_tx->agg.frame_not_ready); + pos += scnprintf(buf + pos, bufsz - pos, + "agg underrun:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.underrun), + accum_tx->agg.underrun); + pos += scnprintf(buf + pos, bufsz - pos, + "agg bt_prio_kill:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.bt_prio_kill), + accum_tx->agg.bt_prio_kill); + pos += scnprintf(buf + pos, bufsz - pos, + "agg rx_ba_rsp_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(tx->agg.rx_ba_rsp_cnt), + accum_tx->agg.rx_ba_rsp_cnt); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); @@ -1361,16 +1526,16 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, char *buf; int bufsz = sizeof(struct statistics_general) * 4 + 250; ssize_t ret; - struct statistics_general *general; - struct statistics_dbg *dbg; - struct statistics_div *div; + struct statistics_general *general, *accum_general; + struct statistics_dbg *dbg, *accum_dbg; + struct statistics_div *div, *accum_div; if (!iwl_is_alive(priv)) return -EAGAIN; /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, 0); + ret = iwl_send_statistics_request(priv, CMD_SYNC, false); mutex_unlock(&priv->mutex); if (ret) { @@ -1391,34 +1556,53 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, general = &priv->statistics.general; dbg = &priv->statistics.general.dbg; div = &priv->statistics.general.div; + accum_general = &priv->accum_statistics.general; + accum_dbg = &priv->accum_statistics.general.dbg; + accum_div = &priv->accum_statistics.general.div; pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, "Statistics_General:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "temperature: %u\n", + pos += scnprintf(buf + pos, bufsz - pos, + "\t\t\tcurrent\t\t\taccumulative\n"); + pos += scnprintf(buf + pos, bufsz - pos, "temperature:\t\t\t%u\n", le32_to_cpu(general->temperature)); - pos += scnprintf(buf + pos, bufsz - pos, "temperature_m: %u\n", + pos += scnprintf(buf + pos, bufsz - pos, "temperature_m:\t\t\t%u\n", le32_to_cpu(general->temperature_m)); - pos += scnprintf(buf + pos, bufsz - pos, "burst_check: %u\n", - le32_to_cpu(dbg->burst_check)); - pos += scnprintf(buf + pos, bufsz - pos, "burst_count: %u\n", - le32_to_cpu(dbg->burst_count)); - pos += scnprintf(buf + pos, bufsz - pos, "sleep_time: %u\n", - le32_to_cpu(general->sleep_time)); - pos += scnprintf(buf + pos, bufsz - pos, "slots_out: %u\n", - le32_to_cpu(general->slots_out)); - pos += scnprintf(buf + pos, bufsz - pos, "slots_idle: %u\n", - le32_to_cpu(general->slots_idle)); - pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp: %u\n", + pos += scnprintf(buf + pos, bufsz - pos, + "burst_check:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(dbg->burst_check), + accum_dbg->burst_check); + pos += scnprintf(buf + pos, bufsz - pos, + "burst_count:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(dbg->burst_count), + accum_dbg->burst_count); + pos += scnprintf(buf + pos, bufsz - pos, + "sleep_time:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(general->sleep_time), + accum_general->sleep_time); + pos += scnprintf(buf + pos, bufsz - pos, + "slots_out:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(general->slots_out), + accum_general->slots_out); + pos += scnprintf(buf + pos, bufsz - pos, + "slots_idle:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(general->slots_idle), + accum_general->slots_idle); + pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n", le32_to_cpu(general->ttl_timestamp)); - pos += scnprintf(buf + pos, bufsz - pos, "tx_on_a: %u\n", - le32_to_cpu(div->tx_on_a)); - pos += scnprintf(buf + pos, bufsz - pos, "tx_on_b: %u\n", - le32_to_cpu(div->tx_on_b)); - pos += scnprintf(buf + pos, bufsz - pos, "exec_time: %u\n", - le32_to_cpu(div->exec_time)); - pos += scnprintf(buf + pos, bufsz - pos, "probe_time: %u\n", - le32_to_cpu(div->probe_time)); - pos += scnprintf(buf + pos, bufsz - pos, "rx_enable_counter: %u\n", - le32_to_cpu(general->rx_enable_counter)); + pos += scnprintf(buf + pos, bufsz - pos, "tx_on_a:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(div->tx_on_a), accum_div->tx_on_a); + pos += scnprintf(buf + pos, bufsz - pos, "tx_on_b:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(div->tx_on_b), accum_div->tx_on_b); + pos += scnprintf(buf + pos, bufsz - pos, + "exec_time:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(div->exec_time), accum_div->exec_time); + pos += scnprintf(buf + pos, bufsz - pos, + "probe_time:\t\t\t%u\t\t\t%u\n", + le32_to_cpu(div->probe_time), accum_div->probe_time); + pos += scnprintf(buf + pos, bufsz - pos, + "rx_enable_counter:\t\t%u\t\t\t%u\n", + le32_to_cpu(general->rx_enable_counter), + accum_general->rx_enable_counter); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; @@ -1579,7 +1763,7 @@ static ssize_t iwl_dbgfs_tx_power_read(struct file *file, else { /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, 0); + ret = iwl_send_statistics_request(priv, CMD_SYNC, false); mutex_unlock(&priv->mutex); if (ret) { @@ -1614,8 +1798,55 @@ static ssize_t iwl_dbgfs_tx_power_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -DEBUGFS_READ_WRITE_FILE_OPS(rx_statistics); -DEBUGFS_READ_WRITE_FILE_OPS(tx_statistics); +static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[60]; + int pos = 0; + const size_t bufsz = sizeof(buf); + u32 pwrsave_status; + + pwrsave_status = iwl_read32(priv, CSR_GP_CNTRL) & + CSR_GP_REG_POWER_SAVE_STATUS_MSK; + + pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : + (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : + (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : + "error"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int clear; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &clear) != 1) + return -EFAULT; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + iwl_send_statistics_request(priv, CMD_SYNC, true); + mutex_unlock(&priv->mutex); + + return count; +} + +DEBUGFS_READ_FILE_OPS(rx_statistics); +DEBUGFS_READ_FILE_OPS(tx_statistics); DEBUGFS_READ_WRITE_FILE_OPS(traffic_log); DEBUGFS_READ_FILE_OPS(rx_queue); DEBUGFS_READ_FILE_OPS(tx_queue); @@ -1625,6 +1856,9 @@ DEBUGFS_READ_FILE_OPS(ucode_general_stats); DEBUGFS_READ_FILE_OPS(sensitivity); DEBUGFS_READ_FILE_OPS(chain_noise); DEBUGFS_READ_FILE_OPS(tx_power); +DEBUGFS_READ_FILE_OPS(power_save_status); +DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); +DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics); /* * Create the debugfs files and directories @@ -1653,33 +1887,34 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) DEBUGFS_ADD_DIR(data, dbgfs->dir_drv); DEBUGFS_ADD_DIR(rf, dbgfs->dir_drv); DEBUGFS_ADD_DIR(debug, dbgfs->dir_drv); - DEBUGFS_ADD_FILE(nvm, data); - DEBUGFS_ADD_FILE(sram, data); - DEBUGFS_ADD_FILE(log_event, data); - DEBUGFS_ADD_FILE(stations, data); - DEBUGFS_ADD_FILE(channels, data); - DEBUGFS_ADD_FILE(status, data); - DEBUGFS_ADD_FILE(interrupt, data); - DEBUGFS_ADD_FILE(qos, data); -#ifdef CONFIG_IWLWIFI_LEDS - DEBUGFS_ADD_FILE(led, data); -#endif - DEBUGFS_ADD_FILE(sleep_level_override, data); - DEBUGFS_ADD_FILE(current_sleep_command, data); - DEBUGFS_ADD_FILE(thermal_throttling, data); - DEBUGFS_ADD_FILE(disable_ht40, data); - DEBUGFS_ADD_FILE(rx_statistics, debug); - DEBUGFS_ADD_FILE(tx_statistics, debug); - DEBUGFS_ADD_FILE(traffic_log, debug); - DEBUGFS_ADD_FILE(rx_queue, debug); - DEBUGFS_ADD_FILE(tx_queue, debug); - DEBUGFS_ADD_FILE(tx_power, debug); + DEBUGFS_ADD_FILE(nvm, data, S_IRUSR); + DEBUGFS_ADD_FILE(sram, data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(log_event, data, S_IWUSR); + DEBUGFS_ADD_FILE(stations, data, S_IRUSR); + DEBUGFS_ADD_FILE(channels, data, S_IRUSR); + DEBUGFS_ADD_FILE(status, data, S_IRUSR); + DEBUGFS_ADD_FILE(interrupt, data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(qos, data, S_IRUSR); + DEBUGFS_ADD_FILE(led, data, S_IRUSR); + DEBUGFS_ADD_FILE(sleep_level_override, data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(current_sleep_command, data, S_IRUSR); + DEBUGFS_ADD_FILE(thermal_throttling, data, S_IRUSR); + DEBUGFS_ADD_FILE(disable_ht40, data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rx_statistics, debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_statistics, debug, S_IRUSR); + DEBUGFS_ADD_FILE(traffic_log, debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rx_queue, debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_queue, debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_power, debug, S_IRUSR); + DEBUGFS_ADD_FILE(power_save_status, debug, S_IRUSR); + DEBUGFS_ADD_FILE(clear_ucode_statistics, debug, S_IWUSR); + DEBUGFS_ADD_FILE(clear_traffic_statistics, debug, S_IWUSR); if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { - DEBUGFS_ADD_FILE(ucode_rx_stats, debug); - DEBUGFS_ADD_FILE(ucode_tx_stats, debug); - DEBUGFS_ADD_FILE(ucode_general_stats, debug); - DEBUGFS_ADD_FILE(sensitivity, debug); - DEBUGFS_ADD_FILE(chain_noise, debug); + DEBUGFS_ADD_FILE(ucode_rx_stats, debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tx_stats, debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_general_stats, debug, S_IRUSR); + DEBUGFS_ADD_FILE(sensitivity, debug, S_IRUSR); + DEBUGFS_ADD_FILE(chain_noise, debug, S_IRUSR); } DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal); DEBUGFS_ADD_BOOL(disable_chain_noise, rf, @@ -1716,9 +1951,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv) DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_status); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_interrupt); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_qos); -#ifdef CONFIG_IWLWIFI_LEDS DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_led); -#endif DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_thermal_throttling); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_disable_ht40); DEBUGFS_REMOVE(priv->dbgfs->dir_data); @@ -1728,6 +1961,11 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv) DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_queue); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_power_save_status); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_clear_ucode_statistics); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_clear_traffic_statistics); if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. file_ucode_rx_stats); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 028d50599550..2673e9a4db92 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -43,7 +43,6 @@ #include "iwl-debug.h" #include "iwl-4965-hw.h" #include "iwl-3945-hw.h" -#include "iwl-3945-led.h" #include "iwl-led.h" #include "iwl-power.h" #include "iwl-agn-rs.h" @@ -53,21 +52,23 @@ extern struct iwl_cfg iwl4965_agn_cfg; extern struct iwl_cfg iwl5300_agn_cfg; extern struct iwl_cfg iwl5100_agn_cfg; extern struct iwl_cfg iwl5350_agn_cfg; -extern struct iwl_cfg iwl5100_bg_cfg; +extern struct iwl_cfg iwl5100_bgn_cfg; extern struct iwl_cfg iwl5100_abg_cfg; extern struct iwl_cfg iwl5150_agn_cfg; -extern struct iwl_cfg iwl6000h_2agn_cfg; +extern struct iwl_cfg iwl5150_abg_cfg; extern struct iwl_cfg iwl6000i_2agn_cfg; +extern struct iwl_cfg iwl6000i_2abg_cfg; +extern struct iwl_cfg iwl6000i_2bg_cfg; extern struct iwl_cfg iwl6000_3agn_cfg; extern struct iwl_cfg iwl6050_2agn_cfg; -extern struct iwl_cfg iwl6050_3agn_cfg; +extern struct iwl_cfg iwl6050_2abg_cfg; extern struct iwl_cfg iwl1000_bgn_cfg; +extern struct iwl_cfg iwl1000_bg_cfg; struct iwl_tx_queue; /* shared structures from iwl-5000.c */ extern struct iwl_mod_params iwl50_mod_params; -extern struct iwl_ops iwl5000_ops; extern struct iwl_ucode_ops iwl5000_ucode; extern struct iwl_lib_ops iwl5000_lib; extern struct iwl_hcmd_ops iwl5000_hcmd; @@ -81,9 +82,6 @@ extern void iwl5000_rts_tx_cmd_flag(struct ieee80211_tx_info *info, __le32 *tx_flags); extern int iwl5000_calc_rssi(struct iwl_priv *priv, struct iwl_rx_phy_res *rx_resp); -extern int iwl5000_apm_init(struct iwl_priv *priv); -extern void iwl5000_apm_stop(struct iwl_priv *priv); -extern int iwl5000_apm_reset(struct iwl_priv *priv); extern void iwl5000_nic_config(struct iwl_priv *priv); extern u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv); extern const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, @@ -144,12 +142,13 @@ extern void iwl5000_temperature(struct iwl_priv *priv); #define DEFAULT_LONG_RETRY_LIMIT 4U struct iwl_rx_mem_buffer { - dma_addr_t real_dma_addr; - dma_addr_t aligned_dma_addr; - struct sk_buff *skb; + dma_addr_t page_dma; + struct page *page; struct list_head list; }; +#define rxb_addr(r) page_address(r->page) + /* defined below */ struct iwl_device_cmd; @@ -165,7 +164,7 @@ struct iwl_cmd_meta { */ void (*callback)(struct iwl_priv *priv, struct iwl_device_cmd *cmd, - struct sk_buff *skb); + struct iwl_rx_packet *pkt); /* The CMD_SIZE_HUGE flag bit indicates that the command * structure is stored at the end of the shared queue memory. */ @@ -293,9 +292,6 @@ struct iwl_channel_info { /* HT40 channel info */ s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - s8 ht40_curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) */ - s8 ht40_min_power; /* always 0 */ - s8 ht40_scan_power; /* (dBm) eeprom, direct scans, any rate */ u8 ht40_flags; /* flags copied from EEPROM */ u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ @@ -321,6 +317,13 @@ struct iwl_channel_info { * queue, 2 (unused) HCCA queues, and 4 HT queues (one for each AC) */ #define IWL_MIN_NUM_QUEUES 10 +/* + * Queue #4 is the command queue for 3945/4965/5x00/1000/6x00, + * the driver maps it into the appropriate device FIFO for the + * uCode. + */ +#define IWL_CMD_QUEUE_NUM 4 + /* Power management (not Tx power) structures */ enum iwl_pwr_src { @@ -356,7 +359,14 @@ enum { CMD_WANT_SKB = (1 << 2), }; -#define IWL_CMD_MAX_PAYLOAD 320 +#define DEF_CMD_PAYLOAD_SIZE 320 + +/* + * IWL_LINK_HDR_MAX should include ieee80211_hdr, radiotap header, + * SNAP header and alignment. It should also be big enough for 802.11 + * control frames. + */ +#define IWL_LINK_HDR_MAX 64 /** * struct iwl_device_cmd @@ -373,7 +383,8 @@ struct iwl_device_cmd { u16 val16; u32 val32; struct iwl_tx_cmd tx; - u8 payload[IWL_CMD_MAX_PAYLOAD]; + struct iwl6000_channel_switch_cmd chswitch; + u8 payload[DEF_CMD_PAYLOAD_SIZE]; } __attribute__ ((packed)) cmd; } __attribute__ ((packed)); @@ -382,21 +393,15 @@ struct iwl_device_cmd { struct iwl_host_cmd { const void *data; - struct sk_buff *reply_skb; + unsigned long reply_page; void (*callback)(struct iwl_priv *priv, struct iwl_device_cmd *cmd, - struct sk_buff *skb); + struct iwl_rx_packet *pkt); u32 flags; u16 len; u8 id; }; -/* - * RX related structures and functions - */ -#define RX_FREE_BUFFERS 64 -#define RX_LOW_WATERMARK 8 - #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 @@ -502,12 +507,11 @@ union iwl_ht_rate_supp { #define CFG_HT_MPDU_DENSITY_4USEC (0x5) #define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC -struct iwl_ht_info { +struct iwl_ht_config { /* self configuration data */ - u8 is_ht; - u8 supported_chan_width; - u8 sm_ps; - struct ieee80211_mcs_info mcs; + bool is_ht; + bool is_40mhz; + bool single_chain_sufficient; /* BSS related data */ u8 extension_chan_offset; u8 ht_protection; @@ -541,26 +545,27 @@ struct iwl_qos_info { struct iwl_qosparam_cmd def_qos_parm; }; -#define STA_PS_STATUS_WAKE 0 -#define STA_PS_STATUS_SLEEP 1 - - -struct iwl3945_station_entry { - struct iwl3945_addsta_cmd sta; - struct iwl_tid_data tid[MAX_TID_COUNT]; - u8 used; - u8 ps_status; - struct iwl_hw_key keyinfo; -}; - struct iwl_station_entry { struct iwl_addsta_cmd sta; struct iwl_tid_data tid[MAX_TID_COUNT]; u8 used; - u8 ps_status; struct iwl_hw_key keyinfo; }; +/* + * iwl_station_priv: Driver's private station information + * + * When mac80211 creates a station it reserves some space (hw->sta_data_size) + * in the structure for use by driver. This structure is places in that + * space. + */ +struct iwl_station_priv { + struct iwl_lq_sta lq_sta; + atomic_t pending_frames; + bool client; + bool asleep; +}; + /* one for each uCode image (inst/data, boot/init/runtime) */ struct fw_desc { void *v_addr; /* access by driver */ @@ -622,6 +627,10 @@ struct iwl_sensitivity_ranges { u16 auto_corr_max_cck_mrc; u16 auto_corr_min_cck; u16 auto_corr_min_cck_mrc; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; }; @@ -639,7 +648,7 @@ struct iwl_sensitivity_ranges { * @valid_tx/rx_ant: usable antennas * @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2) * @max_rxq_log: Log-base-2 of max_rxq_size - * @rx_buf_size: Rx buffer size + * @rx_page_order: Rx buffer page order * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR * @max_stations: * @bcast_sta_id: @@ -662,9 +671,8 @@ struct iwl_hw_params { u8 valid_rx_ant; u16 max_rxq_size; u16 max_rxq_log; - u32 rx_buf_size; + u32 rx_page_order; u32 rx_wrt_ptr_reg; - u32 max_pkt_size; u8 max_stations; u8 bcast_sta_id; u8 ht40_channel; @@ -711,7 +719,11 @@ static inline int iwl_queue_used(const struct iwl_queue *q, int i) static inline u8 get_cmd_index(struct iwl_queue *q, u32 index, int is_huge) { - /* This is for scan command, the big buffer at end of command array */ + /* + * This is for init calibration result and scan command which + * required buffer > TFD_MAX_PAYLOAD_SIZE, + * the big buffer at end of command array + */ if (is_huge) return q->n_window; /* must be power of 2 */ @@ -726,9 +738,6 @@ struct iwl_dma_ptr { size_t size; }; -#define IWL_CHANNEL_WIDTH_20MHZ 0 -#define IWL_CHANNEL_WIDTH_40MHZ 1 - #define IWL_OPERATION_MODE_AUTO 0 #define IWL_OPERATION_MODE_HT_ONLY 1 #define IWL_OPERATION_MODE_MIXED 2 @@ -741,7 +750,8 @@ struct iwl_dma_ptr { /* Sensitivity and chain noise calibration */ #define INITIALIZATION_VALUE 0xFFFF -#define CAL_NUM_OF_BEACONS 20 +#define IWL4965_CAL_NUM_BEACONS 20 +#define IWL_CAL_NUM_BEACONS 16 #define MAXIMUM_ALLOWED_PATHLOSS 15 #define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 @@ -845,6 +855,10 @@ struct iwl_sensitivity_data { s32 nrg_auto_corr_silence_diff; u32 num_in_cck_no_fa; u32 nrg_th_ofdm; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; }; /* Chain noise (differential Rx gain) calib data */ @@ -894,13 +908,11 @@ enum iwl_access_mode { /** * enum iwl_pa_type - Power Amplifier type * @IWL_PA_SYSTEM: based on uCode configuration - * @IWL_PA_HYBRID: use both Internal and external PA * @IWL_PA_INTERNAL: use Internal only */ enum iwl_pa_type { IWL_PA_SYSTEM = 0, - IWL_PA_HYBRID = 1, - IWL_PA_INTERNAL = 2, + IWL_PA_INTERNAL = 1, }; /* interrupt statistics */ @@ -961,7 +973,16 @@ struct traffic_stats { }; #endif -#define IWL_MAX_NUM_QUEUES 20 /* FIXME: do dynamic allocation */ +/* + * iwl_switch_rxon: "channel switch" structure + * + * @ switch_in_progress: channel switch in progress + * @ channel: new channel + */ +struct iwl_switch_rxon { + bool switch_in_progress; + __le16 channel; +}; struct iwl_priv { @@ -976,7 +997,7 @@ struct iwl_priv { int frames_count; enum ieee80211_band band; - int alloc_rxb_skb; + int alloc_rxb_page; void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); @@ -1056,21 +1077,18 @@ struct iwl_priv { const struct iwl_rxon_cmd active_rxon; struct iwl_rxon_cmd staging_rxon; - struct iwl_rxon_cmd recovery_rxon; + struct iwl_switch_rxon switch_rxon; /* 1st responses from initialize and runtime uCode images. * 4965's initialize alive response contains some calibration data. */ struct iwl_init_alive_resp card_alive_init; struct iwl_alive_resp card_alive; -#ifdef CONFIG_IWLWIFI_LEDS unsigned long last_blink_time; u8 last_blink_rate; u8 allow_blinking; u64 led_tpt; - struct iwl_led led[IWL_LED_TRG_MAX]; - unsigned int rxtxpackets; -#endif + u16 active_rate; u16 active_rate_basic; @@ -1080,11 +1098,10 @@ struct iwl_priv { struct iwl_chain_noise_data chain_noise_data; __le16 sensitivity_tbl[HD_TABLE_SIZE]; - struct iwl_ht_info current_ht_config; + struct iwl_ht_config current_ht_config; u8 last_phy_res[100]; /* Rate scaling data */ - s8 data_retry_limit; u8 retry_rate; wait_queue_head_t wait_command_queue; @@ -1093,7 +1110,7 @@ struct iwl_priv { /* Rx and Tx DMA processing queues */ struct iwl_rx_queue rxq; - struct iwl_tx_queue txq[IWL_MAX_NUM_QUEUES]; + struct iwl_tx_queue *txq; unsigned long txq_ctx_active_msk; struct iwl_dma_ptr kw; /* keep warm address */ struct iwl_dma_ptr scd_bc_tbls; @@ -1116,7 +1133,9 @@ struct iwl_priv { struct iwl_tt_mgmt thermal_throttle; struct iwl_notif_statistics statistics; - unsigned long last_statistics_time; +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_notif_statistics accum_statistics; +#endif /* context information */ u16 rates_mask; @@ -1216,6 +1235,7 @@ struct iwl_priv { /* TX Power */ s8 tx_power_user_lmt; s8 tx_power_device_lmt; + s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */ #ifdef CONFIG_IWLWIFI_DEBUG diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c new file mode 100644 index 000000000000..e7d88d1da15d --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c @@ -0,0 +1,14 @@ +#include <linux/module.h> + +/* sparse doesn't like tracepoint macros */ +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "iwl-devtrace.h" + +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_rx); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error); +#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h new file mode 100644 index 000000000000..21361968ab7e --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -0,0 +1,197 @@ +#if !defined(__IWLWIFI_DEVICE_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE + +#include <linux/tracepoint.h> +#include "iwl-dev.h" + +#if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__) +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif + +#define PRIV_ENTRY __field(struct iwl_priv *, priv) +#define PRIV_ASSIGN __entry->priv = priv + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_io + +TRACE_EVENT(iwlwifi_dev_ioread32, + TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), + TP_ARGS(priv, offs, val), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%p] read io[%#x] = %#x", __entry->priv, __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_iowrite8, + TP_PROTO(struct iwl_priv *priv, u32 offs, u8 val), + TP_ARGS(priv, offs, val), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, offs) + __field(u8, val) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_iowrite32, + TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), + TP_ARGS(priv, offs, val), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val) +); + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi + +TRACE_EVENT(iwlwifi_dev_hcmd, + TP_PROTO(struct iwl_priv *priv, void *hcmd, size_t len, u32 flags), + TP_ARGS(priv, hcmd, len, flags), + TP_STRUCT__entry( + PRIV_ENTRY + __dynamic_array(u8, hcmd, len) + __field(u32, flags) + ), + TP_fast_assign( + PRIV_ASSIGN; + memcpy(__get_dynamic_array(hcmd), hcmd, len); + __entry->flags = flags; + ), + TP_printk("[%p] hcmd %#.2x (%ssync)", + __entry->priv, ((u8 *)__get_dynamic_array(hcmd))[0], + __entry->flags & CMD_ASYNC ? "a" : "") +); + +TRACE_EVENT(iwlwifi_dev_rx, + TP_PROTO(struct iwl_priv *priv, void *rxbuf, size_t len), + TP_ARGS(priv, rxbuf, len), + TP_STRUCT__entry( + PRIV_ENTRY + __dynamic_array(u8, rxbuf, len) + ), + TP_fast_assign( + PRIV_ASSIGN; + memcpy(__get_dynamic_array(rxbuf), rxbuf, len); + ), + TP_printk("[%p] RX cmd %#.2x", + __entry->priv, ((u8 *)__get_dynamic_array(rxbuf))[4]) +); + +TRACE_EVENT(iwlwifi_dev_tx, + TP_PROTO(struct iwl_priv *priv, void *tfd, size_t tfdlen, + void *buf0, size_t buf0_len, + void *buf1, size_t buf1_len), + TP_ARGS(priv, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), + TP_STRUCT__entry( + PRIV_ENTRY + + __field(size_t, framelen) + __dynamic_array(u8, tfd, tfdlen) + + /* + * Do not insert between or below these items, + * we want to keep the frame together (except + * for the possible padding). + */ + __dynamic_array(u8, buf0, buf0_len) + __dynamic_array(u8, buf1, buf1_len) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->framelen = buf0_len + buf1_len; + memcpy(__get_dynamic_array(tfd), tfd, tfdlen); + memcpy(__get_dynamic_array(buf0), buf0, buf0_len); + memcpy(__get_dynamic_array(buf1), buf1, buf0_len); + ), + TP_printk("[%p] TX %.2x (%zu bytes)", + __entry->priv, + ((u8 *)__get_dynamic_array(buf0))[0], + __entry->framelen) +); + +TRACE_EVENT(iwlwifi_dev_ucode_error, + TP_PROTO(struct iwl_priv *priv, u32 desc, u32 time, + u32 data1, u32 data2, u32 line, u32 blink1, + u32 blink2, u32 ilink1, u32 ilink2), + TP_ARGS(priv, desc, time, data1, data2, line, + blink1, blink2, ilink1, ilink2), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, desc) + __field(u32, time) + __field(u32, data1) + __field(u32, data2) + __field(u32, line) + __field(u32, blink1) + __field(u32, blink2) + __field(u32, ilink1) + __field(u32, ilink2) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->desc = desc; + __entry->time = time; + __entry->data1 = data1; + __entry->data2 = data2; + __entry->line = line; + __entry->blink1 = blink1; + __entry->blink2 = blink2; + __entry->ilink1 = ilink1; + __entry->ilink2 = ilink2; + ), + TP_printk("[%p] #%02d %010u data 0x%08X 0x%08X line %u, " + "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X", + __entry->priv, __entry->desc, __entry->time, __entry->data1, + __entry->data2, __entry->line, __entry->blink1, + __entry->blink2, __entry->ilink1, __entry->ilink2) +); + +TRACE_EVENT(iwlwifi_dev_ucode_event, + TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev), + TP_ARGS(priv, time, data, ev), + TP_STRUCT__entry( + PRIV_ENTRY + + __field(u32, time) + __field(u32, data) + __field(u32, ev) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->time = time; + __entry->data = data; + __entry->ev = ev; + ), + TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u", + __entry->priv, __entry->time, __entry->data, __entry->ev) +); +#endif /* __IWLWIFI_DEVICE_TRACE */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace +#include <trace/define_trace.h> diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index e14c9952a935..3946e5c03f81 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -215,12 +215,35 @@ static const struct iwl_txpwr_section enhinfo[] = { int iwlcore_eeprom_verify_signature(struct iwl_priv *priv) { - u32 gp = iwl_read32(priv, CSR_EEPROM_GP); - if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) { - IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); - return -ENOENT; + u32 gp = iwl_read32(priv, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; + int ret = 0; + + IWL_DEBUG_INFO(priv, "EEPROM signature=0x%08x\n", gp); + switch (gp) { + case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: + if (priv->nvm_device_type != NVM_DEVICE_TYPE_OTP) { + IWL_ERR(priv, "EEPROM with bad signature: 0x%08x\n", + gp); + ret = -ENOENT; + } + break; + case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: + case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: + if (priv->nvm_device_type != NVM_DEVICE_TYPE_EEPROM) { + IWL_ERR(priv, "OTP with bad signature: 0x%08x\n", gp); + ret = -ENOENT; + } + break; + case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: + default: + IWL_ERR(priv, "bad EEPROM/OTP signature, type=%s, " + "EEPROM_GP=0x%08x\n", + (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + ? "OTP" : "EEPROM", gp); + ret = -ENOENT; + break; } - return 0; + return ret; } EXPORT_SYMBOL(iwlcore_eeprom_verify_signature); @@ -283,7 +306,8 @@ int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv) CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); /* See if we got it */ - ret = iwl_poll_direct_bit(priv, CSR_HW_IF_CONFIG_REG, + ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, EEPROM_SEM_TIMEOUT); if (ret >= 0) { @@ -322,7 +346,8 @@ static int iwl_init_otp_access(struct iwl_priv *priv) CSR_GP_CNTRL_REG_FLAG_INIT_DONE); /* wait for clock to be ready */ - ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL, + ret = iwl_poll_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); if (ret < 0) @@ -333,6 +358,14 @@ static int iwl_init_otp_access(struct iwl_priv *priv) udelay(5); iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); + + /* + * CSR auto clock gate disable bit - + * this is only applicable for HW with OTP shadow RAM + */ + if (priv->cfg->shadow_ram_support) + iwl_set_bit(priv, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); } return ret; } @@ -345,7 +378,8 @@ static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, u16 *eeprom_data) _iwl_write32(priv, CSR_EEPROM_REG, CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, + ret = iwl_poll_bit(priv, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, CSR_EEPROM_REG_READ_VALID_MSK, IWL_EEPROM_ACCESS_TIMEOUT); if (ret < 0) { @@ -484,6 +518,11 @@ int iwl_eeprom_init(struct iwl_priv *priv) } e = (u16 *)priv->eeprom; + if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { + /* OTP reads require powered-up chip */ + priv->cfg->ops->lib->apm_ops.init(priv); + } + ret = priv->cfg->ops->lib->eeprom_ops.verify_signature(priv); if (ret < 0) { IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); @@ -498,7 +537,9 @@ int iwl_eeprom_init(struct iwl_priv *priv) ret = -ENOENT; goto err; } + if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { + ret = iwl_init_otp_access(priv); if (ret) { IWL_ERR(priv, "Failed to initialize OTP access.\n"); @@ -529,6 +570,13 @@ int iwl_eeprom_init(struct iwl_priv *priv) e[cache_addr / 2] = eeprom_data; cache_addr += sizeof(u16); } + + /* + * Now that OTP reads are complete, reset chip to save + * power until we load uCode during "up". + */ + priv->cfg->ops->lib->apm_ops.stop(priv); + } else { /* eeprom is an array of 16bit values */ for (addr = 0; addr < sz; addr += sizeof(u16)) { @@ -537,7 +585,8 @@ int iwl_eeprom_init(struct iwl_priv *priv) _iwl_write32(priv, CSR_EEPROM_REG, CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, + ret = iwl_poll_bit(priv, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, CSR_EEPROM_REG_READ_VALID_MSK, IWL_EEPROM_ACCESS_TIMEOUT); if (ret < 0) { @@ -705,9 +754,6 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, ch_info->ht40_eeprom = *eeprom_ch; ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; - ch_info->ht40_curr_txpow = eeprom_ch->max_power_avg; - ch_info->ht40_min_power = 0; - ch_info->ht40_scan_power = eeprom_ch->max_power_avg; ch_info->ht40_flags = eeprom_ch->flags; ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel; @@ -719,7 +765,8 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, * find the highest tx power from all chains for the channel */ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, - struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, int element) + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, + int element, s8 *max_txpower_in_half_dbm) { s8 max_txpower_avg = 0; /* (dBm) */ @@ -751,10 +798,14 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, (enhanced_txpower[element].mimo3_max > max_txpower_avg)) max_txpower_avg = enhanced_txpower[element].mimo3_max; - /* max. tx power in EEPROM is in 1/2 dBm format - * convert from 1/2 dBm to dBm + /* + * max. tx power in EEPROM is in 1/2 dBm format + * convert from 1/2 dBm to dBm (round-up convert) + * but we also do not want to loss 1/2 dBm resolution which + * will impact performance */ - return max_txpower_avg >> 1; + *max_txpower_in_half_dbm = max_txpower_avg; + return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1); } /** @@ -763,7 +814,7 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, */ static s8 iwl_update_common_txpower(struct iwl_priv *priv, struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, - int section, int element) + int section, int element, s8 *max_txpower_in_half_dbm) { struct iwl_channel_info *ch_info; int ch; @@ -777,25 +828,25 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv, if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX) is_ht40 = true; max_txpower_avg = - iwl_get_max_txpower_avg(priv, enhanced_txpower, element); + iwl_get_max_txpower_avg(priv, enhanced_txpower, + element, max_txpower_in_half_dbm); + ch_info = priv->channel_info; for (ch = 0; ch < priv->channel_count; ch++) { /* find matching band and update tx power if needed */ if ((ch_info->band == enhinfo[section].band) && - (ch_info->max_power_avg < max_txpower_avg) && (!is_ht40)) { + (ch_info->max_power_avg < max_txpower_avg) && + (!is_ht40)) { /* Update regulatory-based run-time data */ ch_info->max_power_avg = ch_info->curr_txpow = - max_txpower_avg; + max_txpower_avg; ch_info->scan_power = max_txpower_avg; } if ((ch_info->band == enhinfo[section].band) && is_ht40 && - ch_info->ht40_max_power_avg && (ch_info->ht40_max_power_avg < max_txpower_avg)) { /* Update regulatory-based run-time data */ ch_info->ht40_max_power_avg = max_txpower_avg; - ch_info->ht40_curr_txpow = max_txpower_avg; - ch_info->ht40_scan_power = max_txpower_avg; } ch_info++; } @@ -808,7 +859,7 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv, */ static s8 iwl_update_channel_txpower(struct iwl_priv *priv, struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, - int section, int element) + int section, int element, s8 *max_txpower_in_half_dbm) { struct iwl_channel_info *ch_info; int ch; @@ -817,7 +868,8 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv, channel = enhinfo[section].iwl_eeprom_section_channel[element]; max_txpower_avg = - iwl_get_max_txpower_avg(priv, enhanced_txpower, element); + iwl_get_max_txpower_avg(priv, enhanced_txpower, + element, max_txpower_in_half_dbm); ch_info = priv->channel_info; for (ch = 0; ch < priv->channel_count; ch++) { @@ -831,12 +883,9 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv, ch_info->scan_power = max_txpower_avg; } if ((enhinfo[section].is_ht40) && - (ch_info->ht40_max_power_avg) && (ch_info->ht40_max_power_avg < max_txpower_avg)) { /* Update regulatory-based run-time data */ ch_info->ht40_max_power_avg = max_txpower_avg; - ch_info->ht40_curr_txpow = max_txpower_avg; - ch_info->ht40_scan_power = max_txpower_avg; } break; } @@ -855,6 +904,7 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv) struct iwl_eeprom_enhanced_txpwr *enhanced_txpower; u32 offset; s8 max_txpower_avg; /* (dBm) */ + s8 max_txpower_in_half_dbm; /* (half-dBm) */ /* Loop through all the sections * adjust bands and channel's max tx power @@ -867,20 +917,43 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv) enhanced_txpower = (struct iwl_eeprom_enhanced_txpwr *) iwl_eeprom_query_addr(priv, offset); + /* + * check for valid entry - + * different version of EEPROM might contain different set + * of enhanced tx power table + * always check for valid entry before process + * the information + */ + if (!enhanced_txpower->common || enhanced_txpower->reserved) + continue; + for (element = 0; element < eeprom_section_count; element++) { if (enhinfo[section].is_common) max_txpower_avg = iwl_update_common_txpower(priv, - enhanced_txpower, section, element); + enhanced_txpower, section, + element, + &max_txpower_in_half_dbm); else max_txpower_avg = iwl_update_channel_txpower(priv, - enhanced_txpower, section, element); + enhanced_txpower, section, + element, + &max_txpower_in_half_dbm); /* Update the tx_power_user_lmt to the highest power * supported by any channel */ if (max_txpower_avg > priv->tx_power_user_lmt) priv->tx_power_user_lmt = max_txpower_avg; + + /* + * Update the tx_power_lmt_in_half_dbm to + * the highest power supported by any channel + */ + if (max_txpower_in_half_dbm > + priv->tx_power_lmt_in_half_dbm) + priv->tx_power_lmt_in_half_dbm = + max_txpower_in_half_dbm; } } } diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 80b9e45d9b9c..5cd2b66bbe45 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -63,6 +63,8 @@ #ifndef __iwl_eeprom_h__ #define __iwl_eeprom_h__ +#include <net/mac80211.h> + struct iwl_priv; /* @@ -125,19 +127,21 @@ struct iwl_eeprom_channel { * Enhanced regulatory tx power portion of eeprom image can be broken down * into individual structures; each one is 8 bytes in size and contain the * following information + * @common: (desc + channel) not used by driver, should _NOT_ be "zero" * @chain_a_max_pwr: chain a max power in 1/2 dBm * @chain_b_max_pwr: chain b max power in 1/2 dBm * @chain_c_max_pwr: chain c max power in 1/2 dBm + * @reserved: not used, should be "zero" * @mimo2_max_pwr: mimo2 max power in 1/2 dBm * @mimo3_max_pwr: mimo3 max power in 1/2 dBm * */ struct iwl_eeprom_enhanced_txpwr { - u16 reserved; + u16 common; s8 chain_a_max; s8 chain_b_max; s8 chain_c_max; - s8 reserved1; + s8 reserved; s8 mimo2_max; s8 mimo3_max; } __attribute__ ((packed)); @@ -256,6 +260,15 @@ struct iwl_eeprom_enhanced_txpwr { #define EEPROM_5050_TX_POWER_VERSION (4) #define EEPROM_5050_EEPROM_VERSION (0x21E) +/* 1000 Specific */ +#define EEPROM_1000_EEPROM_VERSION (0x15C) + +/* 6x00 Specific */ +#define EEPROM_6000_EEPROM_VERSION (0x434) + +/* 6x50 Specific */ +#define EEPROM_6050_EEPROM_VERSION (0x532) + /* OTP */ /* lower blocks contain EEPROM image and calibration data */ #define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ @@ -370,12 +383,10 @@ struct iwl_eeprom_calib_info { #define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ #define EEPROM_VERSION (2*0x44) /* 2 bytes */ #define EEPROM_SKU_CAP (2*0x45) /* 1 bytes */ -#define EEPROM_LEDS_MODE (2*0x45+1) /* 1 bytes */ #define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ #define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */ #define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ #define EEPROM_3945_M_VERSION (2*0x4A) /* 1 bytes */ -#define EEPROM_ANTENNA_SWITCH_TYPE (2*0x4A+1) /* 1 bytes */ /* The following masks are to be applied on EEPROM_RADIO_CONFIG */ #define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ @@ -387,7 +398,12 @@ struct iwl_eeprom_calib_info { #define EEPROM_3945_RF_CFG_TYPE_MAX 0x0 #define EEPROM_4965_RF_CFG_TYPE_MAX 0x1 -#define EEPROM_5000_RF_CFG_TYPE_MAX 0x3 + +/* Radio Config for 5000 and up */ +#define EEPROM_RF_CONFIG_TYPE_R3x3 0x0 +#define EEPROM_RF_CONFIG_TYPE_R2x2 0x1 +#define EEPROM_RF_CONFIG_TYPE_R1x2 0x2 +#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 /* * Per-channel regulatory data. diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index a6856daf14cb..a23165948202 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -56,6 +56,8 @@ const char *get_cmd_string(u8 cmd) IWL_CMD(REPLY_LEDS_CMD); IWL_CMD(REPLY_TX_LINK_QUALITY_CMD); IWL_CMD(COEX_PRIORITY_TABLE_CMD); + IWL_CMD(COEX_MEDIUM_NOTIFICATION); + IWL_CMD(COEX_EVENT_CMD); IWL_CMD(RADAR_NOTIFICATION); IWL_CMD(REPLY_QUIET_CMD); IWL_CMD(REPLY_CHANNEL_SWITCH); @@ -93,6 +95,8 @@ const char *get_cmd_string(u8 cmd) IWL_CMD(CALIBRATION_RES_NOTIFICATION); IWL_CMD(CALIBRATION_COMPLETE_NOTIFICATION); IWL_CMD(REPLY_TX_POWER_DBM_CMD); + IWL_CMD(TEMPERATURE_NOTIFICATION); + IWL_CMD(TX_ANT_CONFIGURATION_CMD); default: return "UNKNOWN"; @@ -104,17 +108,8 @@ EXPORT_SYMBOL(get_cmd_string); static void iwl_generic_cmd_callback(struct iwl_priv *priv, struct iwl_device_cmd *cmd, - struct sk_buff *skb) + struct iwl_rx_packet *pkt) { - struct iwl_rx_packet *pkt = NULL; - - if (!skb) { - IWL_ERR(priv, "Error: Response NULL in %s.\n", - get_cmd_string(cmd->hdr.cmd)); - return; - } - - pkt = (struct iwl_rx_packet *)skb->data; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); @@ -205,18 +200,18 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) } if (test_bit(STATUS_RF_KILL_HW, &priv->status)) { - IWL_DEBUG_INFO(priv, "Command %s aborted: RF KILL Switch\n", + IWL_ERR(priv, "Command %s aborted: RF KILL Switch\n", get_cmd_string(cmd->id)); ret = -ECANCELED; goto fail; } if (test_bit(STATUS_FW_ERROR, &priv->status)) { - IWL_DEBUG_INFO(priv, "Command %s failed: FW Error\n", + IWL_ERR(priv, "Command %s failed: FW Error\n", get_cmd_string(cmd->id)); ret = -EIO; goto fail; } - if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_skb) { + if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { IWL_ERR(priv, "Error: Response NULL in '%s'\n", get_cmd_string(cmd->id)); ret = -EIO; @@ -238,9 +233,9 @@ cancel: ~CMD_WANT_SKB; } fail: - if (cmd->reply_skb) { - dev_kfree_skb_any(cmd->reply_skb); - cmd->reply_skb = NULL; + if (cmd->reply_page) { + free_pages(cmd->reply_page, priv->hw_params.rx_page_order); + cmd->reply_page = 0; } out: clear_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status); @@ -273,7 +268,7 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len, const void *data, void (*callback)(struct iwl_priv *priv, struct iwl_device_cmd *cmd, - struct sk_buff *skb)) + struct iwl_rx_packet *pkt)) { struct iwl_host_cmd cmd = { .id = id, diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index d30cb0275d19..e552d4c4bdbe 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -32,6 +32,7 @@ #include <linux/io.h> #include "iwl-debug.h" +#include "iwl-devtrace.h" /* * IO, register, and NIC memory access functions @@ -61,7 +62,32 @@ * */ -#define _iwl_write32(priv, ofs, val) iowrite32((val), (priv)->hw_base + (ofs)) +static inline void _iwl_write8(struct iwl_priv *priv, u32 ofs, u8 val) +{ + trace_iwlwifi_dev_iowrite8(priv, ofs, val); + iowrite8(val, priv->hw_base + ofs); +} + +#ifdef CONFIG_IWLWIFI_DEBUG +static inline void __iwl_write8(const char *f, u32 l, struct iwl_priv *priv, + u32 ofs, u8 val) +{ + IWL_DEBUG_IO(priv, "write8(0x%08X, 0x%02X) - %s %d\n", ofs, val, f, l); + _iwl_write8(priv, ofs, val); +} +#define iwl_write8(priv, ofs, val) \ + __iwl_write8(__FILE__, __LINE__, priv, ofs, val) +#else +#define iwl_write8(priv, ofs, val) _iwl_write8(priv, ofs, val) +#endif + + +static inline void _iwl_write32(struct iwl_priv *priv, u32 ofs, u32 val) +{ + trace_iwlwifi_dev_iowrite32(priv, ofs, val); + iowrite32(val, priv->hw_base + ofs); +} + #ifdef CONFIG_IWLWIFI_DEBUG static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *priv, u32 ofs, u32 val) @@ -75,7 +101,13 @@ static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *priv, #define iwl_write32(priv, ofs, val) _iwl_write32(priv, ofs, val) #endif -#define _iwl_read32(priv, ofs) ioread32((priv)->hw_base + (ofs)) +static inline u32 _iwl_read32(struct iwl_priv *priv, u32 ofs) +{ + u32 val = ioread32(priv->hw_base + ofs); + trace_iwlwifi_dev_ioread32(priv, ofs, val); + return val; +} + #ifdef CONFIG_IWLWIFI_DEBUG static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *priv, u32 ofs) { @@ -188,6 +220,26 @@ static inline int _iwl_grab_nic_access(struct iwl_priv *priv) /* this bit wakes up the NIC */ _iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* + * These bits say the device is running, and should keep running for + * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), + * but they do not indicate that embedded SRAM is restored yet; + * 3945 and 4965 have volatile SRAM, and must save/restore contents + * to/from host DRAM when sleeping/waking for power-saving. + * Each direction takes approximately 1/4 millisecond; with this + * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a + * series of register accesses are expected (e.g. reading Event Log), + * to keep device from sleeping. + * + * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that + * SRAM is okay/restored. We don't check that here because this call + * is just for hardware register access; but GP1 MAC_SLEEP check is a + * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). + * + * 5000 series and later (including 1000 series) have non-volatile SRAM, + * and do not save/restore SRAM when power cycling. + */ ret = _iwl_poll_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index f420c99e7240..46c7a95b88f0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -42,15 +42,11 @@ #include "iwl-core.h" #include "iwl-io.h" -#ifdef CONFIG_IWLWIFI_DEBUG -static const char *led_type_str[] = { - __stringify(IWL_LED_TRG_TX), - __stringify(IWL_LED_TRG_RX), - __stringify(IWL_LED_TRG_ASSOC), - __stringify(IWL_LED_TRG_RADIO), - NULL -}; -#endif /* CONFIG_IWLWIFI_DEBUG */ +/* default: IWL_LED_BLINK(0) using blinking index table */ +static int led_mode; +module_param(led_mode, int, S_IRUGO); +MODULE_PARM_DESC(led_mode, "led mode: 0=blinking, 1=On(RF On)/Off(RF Off), " + "(default 0)\n"); static const struct { @@ -65,11 +61,11 @@ static const struct { {70, 65, 65}, {50, 75, 75}, {20, 85, 85}, - {15, 95, 95 }, - {10, 110, 110}, - {5, 130, 130}, + {10, 95, 95}, + {5, 110, 110}, + {1, 130, 130}, {0, 167, 167}, -/* SOLID_ON */ + /* SOLID_ON */ {-1, IWL_LED_SOLID, 0} }; @@ -78,191 +74,74 @@ static const struct { #define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */ #define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1) -/* [0-256] -> [0..8] FIXME: we need [0..10] */ -static inline int iwl_brightness_to_idx(enum led_brightness brightness) -{ - return fls(0x000000FF & (u32)brightness); -} - -/* Send led command */ -static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) +/* + * Adjust led blink rate to compensate on a MAC Clock difference on every HW + * Led blink rate analysis showed an average deviation of 0% on 3945, + * 5% on 4965 HW and 20% on 5000 series and up. + * Need to compensate on the led on/off time per HW according to the deviation + * to achieve the desired led frequency + * The calculation is: (100-averageDeviation)/100 * blinkTime + * For code efficiency the calculation will be: + * compensation = (100 - averageDeviation) * 64 / 100 + * NewBlinkTime = (compensation * BlinkTime) / 64 + */ +static inline u8 iwl_blink_compensation(struct iwl_priv *priv, + u8 time, u16 compensation) { - struct iwl_host_cmd cmd = { - .id = REPLY_LEDS_CMD, - .len = sizeof(struct iwl_led_cmd), - .data = led_cmd, - .flags = CMD_ASYNC, - .callback = NULL, - }; - u32 reg; - - reg = iwl_read32(priv, CSR_LED_REG); - if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) - iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); + if (!compensation) { + IWL_ERR(priv, "undefined blink compensation: " + "use pre-defined blinking time\n"); + return time; + } - return iwl_send_cmd(priv, &cmd); + return (u8)((time * compensation) >> 6); } /* Set led pattern command */ -static int iwl_led_pattern(struct iwl_priv *priv, int led_id, - unsigned int idx) +static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx) { struct iwl_led_cmd led_cmd = { - .id = led_id, + .id = IWL_LED_LINK, .interval = IWL_DEF_LED_INTRVL }; BUG_ON(idx > IWL_MAX_BLINK_TBL); - led_cmd.on = blink_tbl[idx].on_time; - led_cmd.off = blink_tbl[idx].off_time; - - return iwl_send_led_cmd(priv, &led_cmd); -} - -/* Set led register off */ -static int iwl_led_on_reg(struct iwl_priv *priv, int led_id) -{ - IWL_DEBUG_LED(priv, "led on %d\n", led_id); - iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON); - return 0; -} + IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n", + priv->cfg->led_compensation); + led_cmd.on = + iwl_blink_compensation(priv, blink_tbl[idx].on_time, + priv->cfg->led_compensation); + led_cmd.off = + iwl_blink_compensation(priv, blink_tbl[idx].off_time, + priv->cfg->led_compensation); -#if 0 -/* Set led on command */ -static int iwl_led_on(struct iwl_priv *priv, int led_id) -{ - struct iwl_led_cmd led_cmd = { - .id = led_id, - .on = IWL_LED_SOLID, - .off = 0, - .interval = IWL_DEF_LED_INTRVL - }; - return iwl_send_led_cmd(priv, &led_cmd); + return priv->cfg->ops->led->cmd(priv, &led_cmd); } -/* Set led off command */ -int iwl_led_off(struct iwl_priv *priv, int led_id) +int iwl_led_start(struct iwl_priv *priv) { - struct iwl_led_cmd led_cmd = { - .id = led_id, - .on = 0, - .off = 0, - .interval = IWL_DEF_LED_INTRVL - }; - IWL_DEBUG_LED(priv, "led off %d\n", led_id); - return iwl_send_led_cmd(priv, &led_cmd); + return priv->cfg->ops->led->on(priv); } -#endif - +EXPORT_SYMBOL(iwl_led_start); -/* Set led register off */ -static int iwl_led_off_reg(struct iwl_priv *priv, int led_id) -{ - IWL_DEBUG_LED(priv, "LED Reg off\n"); - iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF); - return 0; -} - -/* - * Set led register in case of disassociation according to rfkill state - */ -static int iwl_led_associate(struct iwl_priv *priv, int led_id) +int iwl_led_associate(struct iwl_priv *priv) { IWL_DEBUG_LED(priv, "Associated\n"); - priv->allow_blinking = 1; - return iwl_led_on_reg(priv, led_id); -} -static int iwl_led_disassociate(struct iwl_priv *priv, int led_id) -{ - priv->allow_blinking = 0; - - return 0; -} - -/* - * brightness call back function for Tx/Rx LED - */ -static int iwl_led_associated(struct iwl_priv *priv, int led_id) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status) || - !test_bit(STATUS_READY, &priv->status)) - return 0; - + if (led_mode == IWL_LED_BLINK) + priv->allow_blinking = 1; + priv->last_blink_time = jiffies; - /* start counting Tx/Rx bytes */ - if (!priv->last_blink_time && priv->allow_blinking) - priv->last_blink_time = jiffies; return 0; } -/* - * brightness call back for association and radio - */ -static void iwl_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +int iwl_led_disassociate(struct iwl_priv *priv) { - struct iwl_led *led = container_of(led_cdev, struct iwl_led, led_dev); - struct iwl_priv *priv = led->priv; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - - IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n", - led_type_str[led->type], brightness); - switch (brightness) { - case LED_FULL: - if (led->led_on) - led->led_on(priv, IWL_LED_LINK); - break; - case LED_OFF: - if (led->led_off) - led->led_off(priv, IWL_LED_LINK); - break; - default: - if (led->led_pattern) { - int idx = iwl_brightness_to_idx(brightness); - led->led_pattern(priv, IWL_LED_LINK, idx); - } - break; - } -} - - - -/* - * Register led class with the system - */ -static int iwl_leds_register_led(struct iwl_priv *priv, struct iwl_led *led, - enum led_type type, u8 set_led, - char *trigger) -{ - struct device *device = wiphy_dev(priv->hw->wiphy); - int ret; - - led->led_dev.name = led->name; - led->led_dev.brightness_set = iwl_led_brightness_set; - led->led_dev.default_trigger = trigger; - - led->priv = priv; - led->type = type; - - ret = led_classdev_register(device, &led->led_dev); - if (ret) { - IWL_ERR(priv, "Error: failed to register led handler.\n"); - return ret; - } - - led->registered = 1; - - if (set_led && led->led_on) - led->led_on(priv, IWL_LED_LINK); + priv->allow_blinking = 0; return 0; } - /* * calculate blink rate according to last second Tx/Rx activities */ @@ -288,7 +167,7 @@ static int iwl_get_blink_rate(struct iwl_priv *priv) i = IWL_MAX_BLINK_TBL; else for (i = 0; i < IWL_MAX_BLINK_TBL; i++) - if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE)) + if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE)) break; IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i); @@ -317,8 +196,7 @@ void iwl_leds_background(struct iwl_priv *priv) priv->last_blink_time = 0; if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) { priv->last_blink_rate = IWL_SOLID_BLINK_IDX; - iwl_led_pattern(priv, IWL_LED_LINK, - IWL_SOLID_BLINK_IDX); + iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX); } return; } @@ -331,111 +209,17 @@ void iwl_leds_background(struct iwl_priv *priv) /* call only if blink rate change */ if (blink_idx != priv->last_blink_rate) - iwl_led_pattern(priv, IWL_LED_LINK, blink_idx); + iwl_led_pattern(priv, blink_idx); priv->last_blink_time = jiffies; priv->last_blink_rate = blink_idx; } +EXPORT_SYMBOL(iwl_leds_background); -/* Register all led handler */ -int iwl_leds_register(struct iwl_priv *priv) +void iwl_leds_init(struct iwl_priv *priv) { - char *trigger; - int ret; - priv->last_blink_rate = 0; - priv->led_tpt = 0; priv->last_blink_time = 0; priv->allow_blinking = 0; - - trigger = ieee80211_get_radio_led_name(priv->hw); - snprintf(priv->led[IWL_LED_TRG_RADIO].name, - sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio", - wiphy_name(priv->hw->wiphy)); - - priv->led[IWL_LED_TRG_RADIO].led_on = iwl_led_on_reg; - priv->led[IWL_LED_TRG_RADIO].led_off = iwl_led_off_reg; - priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL; - - ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RADIO], - IWL_LED_TRG_RADIO, 1, trigger); - if (ret) - goto exit_fail; - - trigger = ieee80211_get_assoc_led_name(priv->hw); - snprintf(priv->led[IWL_LED_TRG_ASSOC].name, - sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc", - wiphy_name(priv->hw->wiphy)); - - ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_ASSOC], - IWL_LED_TRG_ASSOC, 0, trigger); - - /* for assoc always turn led on */ - priv->led[IWL_LED_TRG_ASSOC].led_on = iwl_led_associate; - priv->led[IWL_LED_TRG_ASSOC].led_off = iwl_led_disassociate; - priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL; - - if (ret) - goto exit_fail; - - trigger = ieee80211_get_rx_led_name(priv->hw); - snprintf(priv->led[IWL_LED_TRG_RX].name, - sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX", - wiphy_name(priv->hw->wiphy)); - - ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RX], - IWL_LED_TRG_RX, 0, trigger); - - priv->led[IWL_LED_TRG_RX].led_on = iwl_led_associated; - priv->led[IWL_LED_TRG_RX].led_off = iwl_led_associated; - priv->led[IWL_LED_TRG_RX].led_pattern = iwl_led_pattern; - - if (ret) - goto exit_fail; - - trigger = ieee80211_get_tx_led_name(priv->hw); - snprintf(priv->led[IWL_LED_TRG_TX].name, - sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX", - wiphy_name(priv->hw->wiphy)); - - ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_TX], - IWL_LED_TRG_TX, 0, trigger); - - priv->led[IWL_LED_TRG_TX].led_on = iwl_led_associated; - priv->led[IWL_LED_TRG_TX].led_off = iwl_led_associated; - priv->led[IWL_LED_TRG_TX].led_pattern = iwl_led_pattern; - - if (ret) - goto exit_fail; - - return 0; - -exit_fail: - iwl_leds_unregister(priv); - return ret; } -EXPORT_SYMBOL(iwl_leds_register); - -/* unregister led class */ -static void iwl_leds_unregister_led(struct iwl_led *led, u8 set_led) -{ - if (!led->registered) - return; - - led_classdev_unregister(&led->led_dev); - - if (set_led) - led->led_dev.brightness_set(&led->led_dev, LED_OFF); - led->registered = 0; -} - -/* Unregister all led handlers */ -void iwl_leds_unregister(struct iwl_priv *priv) -{ - iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0); - iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_RX], 0); - iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_TX], 0); - iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1); -} -EXPORT_SYMBOL(iwl_leds_unregister); - +EXPORT_SYMBOL(iwl_leds_init); diff --git a/drivers/net/wireless/iwlwifi/iwl-led.h b/drivers/net/wireless/iwlwifi/iwl-led.h index ef9b174c37ff..f47f053f02ea 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.h +++ b/drivers/net/wireless/iwlwifi/iwl-led.h @@ -30,9 +30,6 @@ struct iwl_priv; -#ifdef CONFIG_IWLWIFI_LEDS -#include <linux/leds.h> - #define IWL_LED_SOLID 11 #define IWL_LED_NAME_LEN 31 #define IWL_DEF_LED_INTRVL cpu_to_le32(1000) @@ -47,38 +44,23 @@ enum led_type { IWL_LED_TRG_RADIO, IWL_LED_TRG_MAX, }; -#endif - -#ifdef CONFIG_IWLWIFI_LEDS - -struct iwl_led { - struct iwl_priv *priv; - struct led_classdev led_dev; - char name[32]; - int (*led_on) (struct iwl_priv *priv, int led_id); - int (*led_off) (struct iwl_priv *priv, int led_id); - int (*led_pattern) (struct iwl_priv *priv, int led_id, unsigned int idx); - - enum led_type type; - unsigned int registered; +/* + * LED mode + * IWL_LED_BLINK: adjust led blink rate based on blink table + * IWL_LED_RF_STATE: turn LED on/off based on RF state + * LED ON = RF ON + * LED OFF = RF OFF + */ +enum iwl_led_mode { + IWL_LED_BLINK, + IWL_LED_RF_STATE, }; -int iwl_leds_register(struct iwl_priv *priv); -void iwl_leds_unregister(struct iwl_priv *priv); +void iwl_leds_init(struct iwl_priv *priv); void iwl_leds_background(struct iwl_priv *priv); +int iwl_led_start(struct iwl_priv *priv); +int iwl_led_associate(struct iwl_priv *priv); +int iwl_led_disassociate(struct iwl_priv *priv); -#else -static inline int iwl_leds_register(struct iwl_priv *priv) -{ - return 0; -} -static inline void iwl_leds_unregister(struct iwl_priv *priv) -{ -} -static inline void iwl_leds_background(struct iwl_priv *priv) -{ -} - -#endif /* CONFIG_IWLWIFI_LEDS */ #endif /* __iwl_leds_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index 60be976afff8..8ccc0bb1d9ed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -66,7 +66,7 @@ MODULE_PARM_DESC(no_sleep_autoadjust, struct iwl_power_vec_entry { struct iwl_powertable_cmd cmd; - u8 no_dtim; + u8 no_dtim; /* number of skip dtim */ }; #define IWL_DTIM_RANGE_0_MAX 2 @@ -83,8 +83,9 @@ struct iwl_power_vec_entry { cpu_to_le32(X4)} /* default power management (not Tx power) table values */ /* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ +/* DTIM 0 - 2 */ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, @@ -93,15 +94,17 @@ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { /* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ +/* DTIM 3 - 10 */ static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 7, 10, 10)}, 2} + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2} }; /* for DTIM period > IWL_DTIM_RANGE_1_MAX */ +/* DTIM 11 - */ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, @@ -115,13 +118,15 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, enum iwl_power_level lvl, int period) { const struct iwl_power_vec_entry *table; - int max_sleep, i; - bool skip; + int max_sleep[IWL_POWER_VEC_SIZE] = { 0 }; + int i; + u8 skip; + u32 slp_itrvl; table = range_2; - if (period < IWL_DTIM_RANGE_1_MAX) + if (period <= IWL_DTIM_RANGE_1_MAX) table = range_1; - if (period < IWL_DTIM_RANGE_0_MAX) + if (period <= IWL_DTIM_RANGE_0_MAX) table = range_0; BUG_ON(lvl < 0 || lvl >= IWL_POWER_NUM); @@ -129,34 +134,60 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, *cmd = table[lvl].cmd; if (period == 0) { - skip = false; + skip = 0; period = 1; + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + max_sleep[i] = 1; + } else { - skip = !!table[lvl].no_dtim; + skip = table[lvl].no_dtim; + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]); + max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1; } - if (skip) { - __le32 slp_itrvl = cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]; - max_sleep = le32_to_cpu(slp_itrvl); - if (max_sleep == 0xFF) - max_sleep = period * (skip + 1); - else if (max_sleep > period) - max_sleep = (le32_to_cpu(slp_itrvl) / period) * period; + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + /* figure out the listen interval based on dtim period and skip */ + if (slp_itrvl == 0xFF) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32(period * (skip + 1)); + + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + if (slp_itrvl > period) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32((slp_itrvl / period) * period); + + if (skip) cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; - } else { - max_sleep = period; + else cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; - } - for (i = 0; i < IWL_POWER_VEC_SIZE; i++) - if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) - cmd->sleep_interval[i] = cpu_to_le32(max_sleep); + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL); + + /* enforce max sleep interval */ + for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { + if (le32_to_cpu(cmd->sleep_interval[i]) > + (max_sleep[i] * period)) + cmd->sleep_interval[i] = + cpu_to_le32(max_sleep[i] * period); + if (i != (IWL_POWER_VEC_SIZE - 1)) { + if (le32_to_cpu(cmd->sleep_interval[i]) > + le32_to_cpu(cmd->sleep_interval[i+1])) + cmd->sleep_interval[i] = + cmd->sleep_interval[i+1]; + } + } if (priv->power_data.pci_pm) cmd->flags |= IWL_POWER_PCI_PM_MSK; else cmd->flags &= ~IWL_POWER_PCI_PM_MSK; + IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n", + skip, period); IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); } @@ -165,26 +196,26 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, *============================================================================= * Condition Nxt State Condition Nxt State Condition Nxt State *----------------------------------------------------------------------------- - * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A - * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 - * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 + * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 *============================================================================= */ static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, - {IWL_TI_1, 105, CT_KILL_THRESHOLD}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} + {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} }; static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, - {IWL_TI_2, 110, CT_KILL_THRESHOLD}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} + {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} }; static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} }; static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, @@ -294,6 +325,9 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force) if (priv->cfg->broken_powersave) iwl_power_sleep_cam_cmd(priv, &cmd); + else if (priv->cfg->supports_idle && + priv->hw->conf.flags & IEEE80211_CONF_IDLE) + iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_5, 20); else if (tt->state >= IWL_TI_1) iwl_static_sleep_cmd(priv, &cmd, tt->tt_power_mode, dtimper); else if (!enabled) @@ -348,6 +382,23 @@ bool iwl_ht_enabled(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_ht_enabled); +bool iwl_within_ct_kill_margin(struct iwl_priv *priv) +{ + s32 temp = priv->temperature; /* degrees CELSIUS except 4965 */ + bool within_margin = false; + + if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) + temp = KELVIN_TO_CELSIUS(priv->temperature); + + if (!priv->thermal_throttle.advanced_tt) + within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= + CT_KILL_THRESHOLD_LEGACY) ? true : false; + else + within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= + CT_KILL_THRESHOLD) ? true : false; + return within_margin; +} + enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; @@ -372,6 +423,7 @@ enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) } #define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ +#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ /* * toggle the bit to wake up uCode and check the temperature @@ -409,6 +461,7 @@ static void iwl_tt_check_exit_ct_kill(unsigned long data) /* Reschedule the ct_kill timer to occur in * CT_KILL_EXIT_DURATION seconds to ensure we get a * thermal update */ + IWL_DEBUG_POWER(priv, "schedule ct_kill exit timer\n"); mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + CT_KILL_EXIT_DURATION * HZ); } @@ -432,6 +485,33 @@ static void iwl_perform_ct_kill_task(struct iwl_priv *priv, } } +static void iwl_tt_ready_for_ct_kill(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* temperature timer expired, ready to go into CT_KILL state */ + if (tt->state != IWL_TI_CT_KILL) { + IWL_DEBUG_POWER(priv, "entering CT_KILL state when temperature timer expired\n"); + tt->state = IWL_TI_CT_KILL; + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } +} + +static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) +{ + IWL_DEBUG_POWER(priv, "Prepare to enter IWL_TI_CT_KILL\n"); + /* make request to retrieve statistics information */ + iwl_send_statistics_request(priv, CMD_SYNC, false); + /* Reschedule the ct_kill wait timer */ + mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, + jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); +} + #define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) #define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) #define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) @@ -445,7 +525,7 @@ static void iwl_perform_ct_kill_task(struct iwl_priv *priv, * Throttle early enough to lower the power consumption before * drastic steps are needed */ -static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp) +static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; enum iwl_tt_state old_state; @@ -474,6 +554,8 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp) #ifdef CONFIG_IWLWIFI_DEBUG tt->tt_previous_temp = temp; #endif + /* stop ct_kill_waiting_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); if (tt->state != old_state) { switch (tt->state) { case IWL_TI_0: @@ -494,17 +576,28 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp) break; } mutex_lock(&priv->mutex); - if (iwl_power_update_mode(priv, true)) { + if (old_state == IWL_TI_CT_KILL) + clear_bit(STATUS_CT_KILL, &priv->status); + if (tt->state != IWL_TI_CT_KILL && + iwl_power_update_mode(priv, true)) { /* TT state not updated * try again during next temperature read */ + if (old_state == IWL_TI_CT_KILL) + set_bit(STATUS_CT_KILL, &priv->status); tt->state = old_state; IWL_ERR(priv, "Cannot update power mode, " "TT state not updated\n"); } else { - if (tt->state == IWL_TI_CT_KILL) - iwl_perform_ct_kill_task(priv, true); - else if (old_state == IWL_TI_CT_KILL && + if (tt->state == IWL_TI_CT_KILL) { + if (force) { + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } else { + iwl_prepare_ct_kill_task(priv); + tt->state = old_state; + } + } else if (old_state == IWL_TI_CT_KILL && tt->state != IWL_TI_CT_KILL) iwl_perform_ct_kill_task(priv, false); IWL_DEBUG_POWER(priv, "Temperature state changed %u\n", @@ -531,13 +624,13 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp) *============================================================================= * Condition Nxt State Condition Nxt State Condition Nxt State *----------------------------------------------------------------------------- - * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A - * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 - * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 + * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 *============================================================================= */ -static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp) +static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; int i; @@ -582,6 +675,8 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp) break; } } + /* stop ct_kill_waiting_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); if (changed) { struct iwl_rxon_cmd *rxon = &priv->staging_rxon; @@ -613,12 +708,17 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp) iwl_set_rxon_ht(priv, &priv->current_ht_config); } mutex_lock(&priv->mutex); - if (iwl_power_update_mode(priv, true)) { + if (old_state == IWL_TI_CT_KILL) + clear_bit(STATUS_CT_KILL, &priv->status); + if (tt->state != IWL_TI_CT_KILL && + iwl_power_update_mode(priv, true)) { /* TT state not updated * try again during next temperature read */ IWL_ERR(priv, "Cannot update power mode, " "TT state not updated\n"); + if (old_state == IWL_TI_CT_KILL) + set_bit(STATUS_CT_KILL, &priv->status); tt->state = old_state; } else { IWL_DEBUG_POWER(priv, @@ -626,9 +726,15 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp) tt->state); if (old_state != IWL_TI_CT_KILL && tt->state == IWL_TI_CT_KILL) { - IWL_DEBUG_POWER(priv, "Enter IWL_TI_CT_KILL\n"); - iwl_perform_ct_kill_task(priv, true); - + if (force) { + IWL_DEBUG_POWER(priv, + "Enter IWL_TI_CT_KILL\n"); + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } else { + iwl_prepare_ct_kill_task(priv); + tt->state = old_state; + } } else if (old_state == IWL_TI_CT_KILL && tt->state != IWL_TI_CT_KILL) { IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n"); @@ -665,10 +771,11 @@ static void iwl_bg_ct_enter(struct work_struct *work) "- ucode going to sleep!\n"); if (!priv->thermal_throttle.advanced_tt) iwl_legacy_tt_handler(priv, - IWL_MINIMAL_POWER_THRESHOLD); + IWL_MINIMAL_POWER_THRESHOLD, + true); else iwl_advance_tt_handler(priv, - CT_KILL_THRESHOLD + 1); + CT_KILL_THRESHOLD + 1, true); } } @@ -695,11 +802,18 @@ static void iwl_bg_ct_exit(struct work_struct *work) IWL_ERR(priv, "Device temperature below critical" "- ucode awake!\n"); + /* + * exit from CT_KILL state + * reset the current temperature reading + */ + priv->temperature = 0; if (!priv->thermal_throttle.advanced_tt) iwl_legacy_tt_handler(priv, - IWL_REDUCED_PERFORMANCE_THRESHOLD_2); + IWL_REDUCED_PERFORMANCE_THRESHOLD_2, + true); else - iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD); + iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD, + true); } } @@ -735,9 +849,9 @@ static void iwl_bg_tt_work(struct work_struct *work) temp = KELVIN_TO_CELSIUS(priv->temperature); if (!priv->thermal_throttle.advanced_tt) - iwl_legacy_tt_handler(priv, temp); + iwl_legacy_tt_handler(priv, temp, false); else - iwl_advance_tt_handler(priv, temp); + iwl_advance_tt_handler(priv, temp, false); } void iwl_tt_handler(struct iwl_priv *priv) @@ -768,16 +882,18 @@ void iwl_tt_initialize(struct iwl_priv *priv) tt->state = IWL_TI_0; init_timer(&priv->thermal_throttle.ct_kill_exit_tm); priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; - priv->thermal_throttle.ct_kill_exit_tm.function = iwl_tt_check_exit_ct_kill; - + priv->thermal_throttle.ct_kill_exit_tm.function = + iwl_tt_check_exit_ct_kill; + init_timer(&priv->thermal_throttle.ct_kill_waiting_tm); + priv->thermal_throttle.ct_kill_waiting_tm.data = (unsigned long)priv; + priv->thermal_throttle.ct_kill_waiting_tm.function = + iwl_tt_ready_for_ct_kill; /* setup deferred ct kill work */ INIT_WORK(&priv->tt_work, iwl_bg_tt_work); INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); - switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { - case CSR_HW_REV_TYPE_6x00: - case CSR_HW_REV_TYPE_6x50: + if (priv->cfg->adv_thermal_throttle) { IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n"); tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) * IWL_TI_STATE_MAX, GFP_KERNEL); @@ -810,11 +926,9 @@ void iwl_tt_initialize(struct iwl_priv *priv) &restriction_range[0], size); priv->thermal_throttle.advanced_tt = true; } - break; - default: + } else { IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n"); priv->thermal_throttle.advanced_tt = false; - break; } } EXPORT_SYMBOL(iwl_tt_initialize); @@ -826,6 +940,8 @@ void iwl_tt_exit(struct iwl_priv *priv) /* stop ct_kill_exit_tm timer if activated */ del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + /* stop ct_kill_waiting_tm timer if activated */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); cancel_work_sync(&priv->tt_work); cancel_work_sync(&priv->ct_enter); cancel_work_sync(&priv->ct_exit); diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h index df6f6a49712b..310c32e8f698 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.h +++ b/drivers/net/wireless/iwlwifi/iwl-power.h @@ -33,6 +33,7 @@ #define IWL_ABSOLUTE_ZERO 0 #define IWL_ABSOLUTE_MAX 0xFFFFFFFF #define IWL_TT_INCREASE_MARGIN 5 +#define IWL_TT_CT_KILL_MARGIN 3 enum iwl_antenna_ok { IWL_ANT_OK_NONE, @@ -110,6 +111,7 @@ struct iwl_tt_mgmt { struct iwl_tt_restriction *restriction; struct iwl_tt_trans *transaction; struct timer_list ct_kill_exit_tm; + struct timer_list ct_kill_waiting_tm; }; enum iwl_power_level { @@ -129,6 +131,7 @@ struct iwl_power_mgr { int iwl_power_update_mode(struct iwl_priv *priv, bool force); bool iwl_ht_enabled(struct iwl_priv *priv); +bool iwl_within_ct_kill_margin(struct iwl_priv *priv); enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); void iwl_tt_enter_ct_kill(struct iwl_priv *priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index d393e8f02102..6d95832db06d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -254,7 +254,8 @@ * device. A queue maps to only one (selectable by driver) Tx DMA channel, * but one DMA channel may take input from several queues. * - * Tx DMA channels have dedicated purposes. For 4965, they are used as follows: + * Tx DMA channels have dedicated purposes. For 4965, they are used as follows + * (cf. default_queue_to_tx_fifo in iwl-4965.c): * * 0 -- EDCA BK (background) frames, lowest priority * 1 -- EDCA BE (best effort) frames, normal priority @@ -265,9 +266,21 @@ * 6 -- HCCA long frames * 7 -- not used by driver (device-internal only) * + * For 5000 series and up, they are used slightly differently + * (cf. iwl5000_default_queue_to_tx_fifo in iwl-5000.c): + * + * 0 -- EDCA BK (background) frames, lowest priority + * 1 -- EDCA BE (best effort) frames, normal priority + * 2 -- EDCA VI (video) frames, higher priority + * 3 -- EDCA VO (voice) and management frames, highest priority + * 4 -- (TBD) + * 5 -- HCCA short frames + * 6 -- HCCA long frames + * 7 -- Commands + * * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. - * In addition, driver can map queues 7-15 to Tx DMA/FIFO channels 0-3 to - * support 11n aggregation via EDCA DMA channels. + * In addition, driver can map the remaining queues to Tx DMA/FIFO + * channels 0-3 to support 11n aggregation via EDCA DMA channels. * * The driver sets up each queue to work in one of two modes: * diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 493626bcd3ec..6090bc15a6d5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -140,6 +140,8 @@ int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q) reg = iwl_read32(priv, CSR_UCODE_DRV_GP1); if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(priv, "Rx queue requesting wakeup, GP1 = 0x%x\n", + reg); iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); goto exit_unlock; @@ -200,7 +202,7 @@ int iwl_rx_queue_restock(struct iwl_priv *priv) list_del(element); /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(priv, rxb->aligned_dma_addr); + rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(priv, rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; @@ -239,8 +241,9 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority) struct iwl_rx_queue *rxq = &priv->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; - struct sk_buff *skb; + struct page *page; unsigned long flags; + gfp_t gfp_mask = priority; while (1) { spin_lock_irqsave(&rxq->lock, flags); @@ -251,30 +254,35 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority) spin_unlock_irqrestore(&rxq->lock, flags); if (rxq->free_count > RX_LOW_WATERMARK) - priority |= __GFP_NOWARN; - /* Alloc a new receive buffer */ - skb = alloc_skb(priv->hw_params.rx_buf_size + 256, - priority); + gfp_mask |= __GFP_NOWARN; + + if (priv->hw_params.rx_page_order > 0) + gfp_mask |= __GFP_COMP; - if (!skb) { + /* Alloc a new receive buffer */ + page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order); + if (!page) { if (net_ratelimit()) - IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n"); + IWL_DEBUG_INFO(priv, "alloc_pages failed, " + "order: %d\n", + priv->hw_params.rx_page_order); + if ((rxq->free_count <= RX_LOW_WATERMARK) && net_ratelimit()) - IWL_CRIT(priv, "Failed to allocate SKB buffer with %s. Only %u free buffers remaining.\n", + IWL_CRIT(priv, "Failed to alloc_pages with %s. Only %u free buffers remaining.\n", priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ - break; + return; } spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); - dev_kfree_skb_any(skb); + __free_pages(page, priv->hw_params.rx_page_order); return; } element = rxq->rx_used.next; @@ -283,24 +291,21 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority) spin_unlock_irqrestore(&rxq->lock, flags); - rxb->skb = skb; - /* Get physical address of RB/SKB */ - rxb->real_dma_addr = pci_map_single( - priv->pci_dev, - rxb->skb->data, - priv->hw_params.rx_buf_size + 256, - PCI_DMA_FROMDEVICE); + rxb->page = page; + /* Get physical address of the RB */ + rxb->page_dma = pci_map_page(priv->pci_dev, page, 0, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); /* dma address must be no more than 36 bits */ - BUG_ON(rxb->real_dma_addr & ~DMA_BIT_MASK(36)); + BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); /* and also 256 byte aligned! */ - rxb->aligned_dma_addr = ALIGN(rxb->real_dma_addr, 256); - skb_reserve(rxb->skb, rxb->aligned_dma_addr - rxb->real_dma_addr); + BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); spin_lock_irqsave(&rxq->lock, flags); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; - priv->alloc_rxb_skb++; + priv->alloc_rxb_page++; spin_unlock_irqrestore(&rxq->lock, flags); } @@ -336,12 +341,14 @@ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq) { int i; for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { - if (rxq->pool[i].skb != NULL) { - pci_unmap_single(priv->pci_dev, - rxq->pool[i].real_dma_addr, - priv->hw_params.rx_buf_size + 256, - PCI_DMA_FROMDEVICE); - dev_kfree_skb(rxq->pool[i].skb); + if (rxq->pool[i].page != NULL) { + pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __free_pages(rxq->pool[i].page, + priv->hw_params.rx_page_order); + rxq->pool[i].page = NULL; + priv->alloc_rxb_page--; } } @@ -405,14 +412,14 @@ void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { /* In the reset function, these buffers may have been allocated * to an SKB, so we need to unmap and free potential storage */ - if (rxq->pool[i].skb != NULL) { - pci_unmap_single(priv->pci_dev, - rxq->pool[i].real_dma_addr, - priv->hw_params.rx_buf_size + 256, - PCI_DMA_FROMDEVICE); - priv->alloc_rxb_skb--; - dev_kfree_skb(rxq->pool[i].skb); - rxq->pool[i].skb = NULL; + if (rxq->pool[i].page != NULL) { + pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + priv->alloc_rxb_page--; + __free_pages(rxq->pool[i].page, + priv->hw_params.rx_page_order); + rxq->pool[i].page = NULL; } list_add_tail(&rxq->pool[i].list, &rxq->rx_used); } @@ -470,7 +477,8 @@ int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); - iwl_write32(priv, CSR_INT_COALESCING, 0x40); + /* Set interrupt coalescing timer to 64 x 32 = 2048 usecs */ + iwl_write8(priv, CSR_INT_COALESCING, 0x40); return 0; } @@ -491,7 +499,7 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_missed_beacon_notif *missed_beacon; missed_beacon = &pkt->u.missed_beacon; @@ -548,13 +556,51 @@ static void iwl_rx_calc_noise(struct iwl_priv *priv) priv->last_rx_noise); } +#ifdef CONFIG_IWLWIFI_DEBUG +/* + * based on the assumption of all statistics counter are in DWORD + * FIXME: This function is for debugging, do not deal with + * the case of counters roll-over. + */ +static void iwl_accumulative_statistics(struct iwl_priv *priv, + __le32 *stats) +{ + int i; + __le32 *prev_stats; + u32 *accum_stats; + + prev_stats = (__le32 *)&priv->statistics; + accum_stats = (u32 *)&priv->accum_statistics; + + for (i = sizeof(__le32); i < sizeof(struct iwl_notif_statistics); + i += sizeof(__le32), stats++, prev_stats++, accum_stats++) + if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) + *accum_stats += (le32_to_cpu(*stats) - + le32_to_cpu(*prev_stats)); + + /* reset accumulative statistics for "no-counter" type statistics */ + priv->accum_statistics.general.temperature = + priv->statistics.general.temperature; + priv->accum_statistics.general.temperature_m = + priv->statistics.general.temperature_m; + priv->accum_statistics.general.ttl_timestamp = + priv->statistics.general.ttl_timestamp; + priv->accum_statistics.tx.tx_power.ant_a = + priv->statistics.tx.tx_power.ant_a; + priv->accum_statistics.tx.tx_power.ant_b = + priv->statistics.tx.tx_power.ant_b; + priv->accum_statistics.tx.tx_power.ant_c = + priv->statistics.tx.tx_power.ant_c; +} +#endif + #define REG_RECALIB_PERIOD (60) void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { int change; - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", (int)sizeof(priv->statistics), @@ -566,6 +612,9 @@ void iwl_rx_statistics(struct iwl_priv *priv, STATISTICS_REPLY_FLG_HT40_MODE_MSK) != (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); +#ifdef CONFIG_IWLWIFI_DEBUG + iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); +#endif memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); set_bit(STATUS_STATISTICS, &priv->status); @@ -582,14 +631,29 @@ void iwl_rx_statistics(struct iwl_priv *priv, iwl_rx_calc_noise(priv); queue_work(priv->workqueue, &priv->run_time_calib_work); } - - iwl_leds_background(priv); - if (priv->cfg->ops->lib->temp_ops.temperature && change) priv->cfg->ops->lib->temp_ops.temperature(priv); } EXPORT_SYMBOL(iwl_rx_statistics); +void iwl_reply_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) { + memset(&priv->statistics, 0, + sizeof(struct iwl_notif_statistics)); +#ifdef CONFIG_IWLWIFI_DEBUG + memset(&priv->accum_statistics, 0, + sizeof(struct iwl_notif_statistics)); +#endif + IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); + } + iwl_rx_statistics(priv, rxb); +} +EXPORT_SYMBOL(iwl_reply_statistics); + #define PERFECT_RSSI (-20) /* dBm */ #define WORST_RSSI (-95) /* dBm */ #define RSSI_RANGE (PERFECT_RSSI - WORST_RSSI) @@ -878,6 +942,10 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, struct ieee80211_rx_status *stats) { + struct sk_buff *skb; + int ret = 0; + __le16 fc = hdr->frame_control; + /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { IWL_DEBUG_DROP_LIMIT(priv, @@ -890,15 +958,44 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, iwl_set_decrypted_flag(priv, hdr, ampdu_status, stats)) return; - /* Resize SKB from mac header to end of packet */ - skb_reserve(rxb->skb, (void *)hdr - (void *)rxb->skb->data); - skb_put(rxb->skb, len); + skb = alloc_skb(IWL_LINK_HDR_MAX * 2, GFP_ATOMIC); + if (!skb) { + IWL_ERR(priv, "alloc_skb failed\n"); + return; + } + + skb_reserve(skb, IWL_LINK_HDR_MAX); + skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len); + + /* mac80211 currently doesn't support paged SKB. Convert it to + * linear SKB for management frame and data frame requires + * software decryption or software defragementation. */ + if (ieee80211_is_mgmt(fc) || + ieee80211_has_protected(fc) || + ieee80211_has_morefrags(fc) || + le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) + ret = skb_linearize(skb); + else + ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ? + 0 : -ENOMEM; + + if (ret) { + kfree_skb(skb); + goto out; + } + + /* + * XXX: We cannot touch the page and its virtual memory (hdr) after + * here. It might have already been freed by the above skb change. + */ - iwl_update_stats(priv, false, hdr->frame_control, len); - memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats)); - ieee80211_rx_irqsafe(priv->hw, rxb->skb); - priv->alloc_rxb_skb--; - rxb->skb = NULL; + iwl_update_stats(priv, false, fc, len); + memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); + + ieee80211_rx(priv->hw, skb); + out: + priv->alloc_rxb_page--; + rxb->page = NULL; } /* This is necessary only for a number of statistics, see the caller. */ @@ -926,13 +1023,12 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, { struct ieee80211_hdr *header; struct ieee80211_rx_status rx_status; - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_phy_res *phy_res; __le32 rx_pkt_status; struct iwl4965_rx_mpdu_res_start *amsdu; u32 len; u32 ampdu_status; - u16 fc; u32 rate_n_flags; /** @@ -1065,20 +1161,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, priv->last_tsf = le64_to_cpu(phy_res->timestamp); } - fc = le16_to_cpu(header->frame_control); - switch (fc & IEEE80211_FCTL_FTYPE) { - case IEEE80211_FTYPE_MGMT: - case IEEE80211_FTYPE_DATA: - if (priv->iw_mode == NL80211_IFTYPE_AP) - iwl_update_ps_mode(priv, fc & IEEE80211_FCTL_PM, - header->addr2); - /* fall through */ - default: - iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status, - rxb, &rx_status); - break; - - } + iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status, + rxb, &rx_status); } EXPORT_SYMBOL(iwl_rx_reply_rx); @@ -1087,7 +1171,7 @@ EXPORT_SYMBOL(iwl_rx_reply_rx); void iwl_rx_reply_rx_phy(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); priv->last_phy_res[0] = 1; memcpy(&priv->last_phy_res[1], &(pkt->u.raw[0]), sizeof(struct iwl_rx_phy_res)); diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 4f3a108fa990..a2b2b8315ff9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -27,7 +27,6 @@ *****************************************************************************/ #include <linux/types.h> #include <linux/etherdevice.h> -#include <net/lib80211.h> #include <net/mac80211.h> #include "iwl-eeprom.h" @@ -112,7 +111,7 @@ EXPORT_SYMBOL(iwl_scan_cancel_timeout); static int iwl_send_scan_abort(struct iwl_priv *priv) { int ret = 0; - struct iwl_rx_packet *res; + struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = REPLY_SCAN_ABORT_CMD, .flags = CMD_WANT_SKB, @@ -132,21 +131,21 @@ static int iwl_send_scan_abort(struct iwl_priv *priv) return ret; } - res = (struct iwl_rx_packet *)cmd.reply_skb->data; - if (res->u.status != CAN_ABORT_STATUS) { + pkt = (struct iwl_rx_packet *)cmd.reply_page; + if (pkt->u.status != CAN_ABORT_STATUS) { /* The scan abort will return 1 for success or * 2 for "failure". A failure condition can be * due to simply not being in an active scan which * can occur if we send the scan abort before we * the microcode has notified us that a scan is * completed. */ - IWL_DEBUG_INFO(priv, "SCAN_ABORT returned %d.\n", res->u.status); + IWL_DEBUG_INFO(priv, "SCAN_ABORT returned %d.\n", pkt->u.status); clear_bit(STATUS_SCAN_ABORTING, &priv->status); clear_bit(STATUS_SCAN_HW, &priv->status); } - priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.reply_skb); + priv->alloc_rxb_page--; + free_pages(cmd.reply_page, priv->hw_params.rx_page_order); return ret; } @@ -156,7 +155,7 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { #ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scanreq_notification *notif = (struct iwl_scanreq_notification *)pkt->u.raw; @@ -168,7 +167,7 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv, static void iwl_rx_scan_start_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scanstart_notification *notif = (struct iwl_scanstart_notification *)pkt->u.raw; priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); @@ -187,7 +186,7 @@ static void iwl_rx_scan_results_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { #ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scanresults_notification *notif = (struct iwl_scanresults_notification *)pkt->u.raw; @@ -214,7 +213,7 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { #ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw; IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", @@ -402,6 +401,7 @@ void iwl_init_scan_params(struct iwl_priv *priv) if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; } +EXPORT_SYMBOL(iwl_init_scan_params); static int iwl_scan_initiate(struct iwl_priv *priv) { @@ -581,6 +581,7 @@ static void iwl_bg_request_scan(struct work_struct *data) u8 rate; bool is_active = false; int chan_mod; + u8 active_chains; conf = ieee80211_get_hw_conf(priv->hw); @@ -734,9 +735,22 @@ static void iwl_bg_request_scan(struct work_struct *data) rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); + /* In power save mode use one chain, otherwise use all chains */ + if (test_bit(STATUS_POWER_PMI, &priv->status)) { + /* rx_ant has been set to all valid chains previously */ + active_chains = rx_ant & + ((u8)(priv->chain_noise_data.active_chains)); + if (!active_chains) + active_chains = rx_ant; + + IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", + priv->chain_noise_data.active_chains); + + rx_ant = first_antenna(active_chains); + } /* MIMO is not used here, but value is required */ - rx_chain |= ANT_ABC << RXON_RX_CHAIN_VALID_POS; - rx_chain |= ANT_ABC << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; + rx_chain |= priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; scan->rx_chain = cpu_to_le16(rx_chain); diff --git a/drivers/net/wireless/iwlwifi/iwl-spectrum.c b/drivers/net/wireless/iwlwifi/iwl-spectrum.c index 022bcf115731..1ea5cd345fe8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-spectrum.c +++ b/drivers/net/wireless/iwlwifi/iwl-spectrum.c @@ -177,7 +177,7 @@ static int iwl_get_measurement(struct iwl_priv *priv, static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif); if (!report->state) { diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index c6633fec8216..cd6a6901216e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -99,32 +99,25 @@ static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) static void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_device_cmd *cmd, - struct sk_buff *skb) + struct iwl_rx_packet *pkt) { - struct iwl_rx_packet *res = NULL; struct iwl_addsta_cmd *addsta = (struct iwl_addsta_cmd *)cmd->cmd.payload; u8 sta_id = addsta->sta.sta_id; - if (!skb) { - IWL_ERR(priv, "Error: Response NULL in REPLY_ADD_STA.\n"); - return; - } - - res = (struct iwl_rx_packet *)skb->data; - if (res->hdr.flags & IWL_CMD_FAILED_MSK) { + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", - res->hdr.flags); + pkt->hdr.flags); return; } - switch (res->u.add_sta.status) { + switch (pkt->u.add_sta.status) { case ADD_STA_SUCCESS_MSK: iwl_sta_ucode_activate(priv, sta_id); /* fall through */ default: IWL_DEBUG_HC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", - res->u.add_sta.status); + pkt->u.add_sta.status); break; } } @@ -132,7 +125,7 @@ static void iwl_add_sta_callback(struct iwl_priv *priv, int iwl_send_add_sta(struct iwl_priv *priv, struct iwl_addsta_cmd *sta, u8 flags) { - struct iwl_rx_packet *res = NULL; + struct iwl_rx_packet *pkt = NULL; int ret = 0; u8 data[sizeof(*sta)]; struct iwl_host_cmd cmd = { @@ -152,15 +145,15 @@ int iwl_send_add_sta(struct iwl_priv *priv, if (ret || (flags & CMD_ASYNC)) return ret; - res = (struct iwl_rx_packet *)cmd.reply_skb->data; - if (res->hdr.flags & IWL_CMD_FAILED_MSK) { + pkt = (struct iwl_rx_packet *)cmd.reply_page; + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", - res->hdr.flags); + pkt->hdr.flags); ret = -EIO; } if (ret == 0) { - switch (res->u.add_sta.status) { + switch (pkt->u.add_sta.status) { case ADD_STA_SUCCESS_MSK: iwl_sta_ucode_activate(priv, sta->sta.sta_id); IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); @@ -172,8 +165,8 @@ int iwl_send_add_sta(struct iwl_priv *priv, } } - priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.reply_skb); + priv->alloc_rxb_page--; + free_pages(cmd.reply_page, priv->hw_params.rx_page_order); return ret; } @@ -189,6 +182,11 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, goto done; mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; + IWL_DEBUG_ASSOC(priv, "spatial multiplexing power save mode: %s\n", + (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? + "static" : + (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? + "dynamic" : "disabled"); sta_flags = priv->stations[index].sta.station_flags; @@ -324,26 +322,19 @@ static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const char *addr) static void iwl_remove_sta_callback(struct iwl_priv *priv, struct iwl_device_cmd *cmd, - struct sk_buff *skb) + struct iwl_rx_packet *pkt) { - struct iwl_rx_packet *res = NULL; struct iwl_rem_sta_cmd *rm_sta = - (struct iwl_rem_sta_cmd *)cmd->cmd.payload; + (struct iwl_rem_sta_cmd *)cmd->cmd.payload; const char *addr = rm_sta->addr; - if (!skb) { - IWL_ERR(priv, "Error: Response NULL in REPLY_REMOVE_STA.\n"); - return; - } - - res = (struct iwl_rx_packet *)skb->data; - if (res->hdr.flags & IWL_CMD_FAILED_MSK) { + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", - res->hdr.flags); + pkt->hdr.flags); return; } - switch (res->u.rem_sta.status) { + switch (pkt->u.rem_sta.status) { case REM_STA_SUCCESS_MSK: iwl_sta_ucode_deactivate(priv, addr); break; @@ -356,7 +347,7 @@ static void iwl_remove_sta_callback(struct iwl_priv *priv, static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, u8 flags) { - struct iwl_rx_packet *res = NULL; + struct iwl_rx_packet *pkt; int ret; struct iwl_rem_sta_cmd rm_sta_cmd; @@ -381,15 +372,15 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, if (ret || (flags & CMD_ASYNC)) return ret; - res = (struct iwl_rx_packet *)cmd.reply_skb->data; - if (res->hdr.flags & IWL_CMD_FAILED_MSK) { + pkt = (struct iwl_rx_packet *)cmd.reply_page; + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", - res->hdr.flags); + pkt->hdr.flags); ret = -EIO; } if (!ret) { - switch (res->u.rem_sta.status) { + switch (pkt->u.rem_sta.status) { case REM_STA_SUCCESS_MSK: iwl_sta_ucode_deactivate(priv, addr); IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); @@ -401,8 +392,8 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, } } - priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.reply_skb); + priv->alloc_rxb_page--; + free_pages(cmd.reply_page, priv->hw_params.rx_page_order); return ret; } @@ -1026,7 +1017,7 @@ int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) */ if (priv->current_ht_config.is_ht) { rcu_read_lock(); - sta = ieee80211_find_sta(priv->hw, addr); + sta = ieee80211_find_sta(priv->vif, addr); if (sta) { memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config)); cur_ht_config = &ht_config; @@ -1044,6 +1035,68 @@ int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) EXPORT_SYMBOL(iwl_rxon_add_station); /** + * iwl_sta_init_bcast_lq - Initialize a bcast station's hardware rate table + * + * NOTE: Run REPLY_ADD_STA command to set up station table entry, before + * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). + */ +static void iwl_sta_init_bcast_lq(struct iwl_priv *priv) +{ + int i, r; + struct iwl_link_quality_cmd link_cmd = { + .reserved1 = 0, + }; + u32 rate_flags; + + /* Set up the rate scaling to start at selected rate, fall back + * all the way down to 1M in IEEE order, and then spin on 1M */ + if (priv->band == IEEE80211_BAND_5GHZ) + r = IWL_RATE_6M_INDEX; + else + r = IWL_RATE_1M_INDEX; + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + rate_flags = 0; + if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << + RATE_MCS_ANT_POS; + + link_cmd.rs_table[i].rate_n_flags = + iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); + r = iwl_get_prev_ieee_rate(r); + } + + link_cmd.general_params.single_stream_ant_msk = + first_antenna(priv->hw_params.valid_tx_ant); + link_cmd.general_params.dual_stream_ant_msk = 3; + link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + link_cmd.agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + + /* Update the rate scaling for control frame Tx to AP */ + link_cmd.sta_id = priv->hw_params.bcast_sta_id; + + iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD, + sizeof(link_cmd), &link_cmd, NULL); +} + + +/** + * iwl_add_bcast_station - add broadcast station into station table. + */ +void iwl_add_bcast_station(struct iwl_priv *priv) +{ + iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL); + + /* Set up default rate scaling table in device's station table */ + iwl_sta_init_bcast_lq(priv); +} +EXPORT_SYMBOL(iwl_add_bcast_station); + +/** * iwl_get_sta_id - Find station's index within station table * * If new IBSS station, create new entry in station table @@ -1163,7 +1216,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid) } EXPORT_SYMBOL(iwl_sta_rx_agg_stop); -static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) +void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) { unsigned long flags; @@ -1171,27 +1224,26 @@ static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK; priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; priv->stations[sta_id].sta.sta.modify_mask = 0; + priv->stations[sta_id].sta.sleep_tx_count = 0; priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; spin_unlock_irqrestore(&priv->sta_lock, flags); iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); } +EXPORT_SYMBOL(iwl_sta_modify_ps_wake); -void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr) +void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) { - /* FIXME: need locking over ps_status ??? */ - u8 sta_id = iwl_find_station(priv, addr); + unsigned long flags; - if (sta_id != IWL_INVALID_STATION) { - u8 sta_awake = priv->stations[sta_id]. - ps_status == STA_PS_STATUS_WAKE; + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK; + priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; + priv->stations[sta_id].sta.sta.modify_mask = + STA_MODIFY_SLEEP_TX_COUNT_MSK; + priv->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + spin_unlock_irqrestore(&priv->sta_lock, flags); - if (sta_awake && ps_bit) - priv->stations[sta_id].ps_status = STA_PS_STATUS_SLEEP; - else if (!sta_awake && !ps_bit) { - iwl_sta_modify_ps_wake(priv, sta_id); - priv->stations[sta_id].ps_status = STA_PS_STATUS_WAKE; - } - } + iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); } - diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h index 6deebade6361..8d052de2d405 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.h +++ b/drivers/net/wireless/iwlwifi/iwl-sta.h @@ -52,6 +52,7 @@ void iwl_update_tkip_key(struct iwl_priv *priv, const u8 *addr, u32 iv32, u16 *phase1key); int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); +void iwl_add_bcast_station(struct iwl_priv *priv); int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); void iwl_clear_stations_table(struct iwl_priv *priv); int iwl_get_free_ucode_key_index(struct iwl_priv *priv); @@ -65,5 +66,6 @@ void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); int iwl_sta_rx_agg_start(struct iwl_priv *priv, const u8 *addr, int tid, u16 ssn); int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid); -void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr); +void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id); +void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt); #endif /* __iwl_sta_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index b7e196e3c8d3..58b132f9cf28 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -97,7 +97,8 @@ int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq) reg = iwl_read32(priv, CSR_UCODE_DRV_GP1); if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - IWL_DEBUG_INFO(priv, "Requesting wakeup, GP1 = 0x%x\n", reg); + IWL_DEBUG_INFO(priv, "Tx queue %d requesting wakeup, GP1 = 0x%x\n", + txq_id, reg); iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); return ret; @@ -132,7 +133,7 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) struct iwl_tx_queue *txq = &priv->txq[txq_id]; struct iwl_queue *q = &txq->q; struct pci_dev *dev = priv->pci_dev; - int i, len; + int i; if (q->n_bd == 0) return; @@ -142,8 +143,6 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) priv->cfg->ops->lib->txq_free_tfd(priv, txq); - len = sizeof(struct iwl_device_cmd) * q->n_window; - /* De-alloc array of command/tx buffers */ for (i = 0; i < TFD_TX_CMD_SLOTS; i++) kfree(txq->cmd[i]); @@ -181,14 +180,11 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; struct iwl_queue *q = &txq->q; struct pci_dev *dev = priv->pci_dev; - int i, len; + int i; if (q->n_bd == 0) return; - len = sizeof(struct iwl_device_cmd) * q->n_window; - len += IWL_MAX_SCAN_SIZE; - /* De-alloc array of command/tx buffers */ for (i = 0; i <= TFD_CMD_SLOTS; i++) kfree(txq->cmd[i]); @@ -370,8 +366,13 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, txq->need_update = 0; - /* aggregation TX queues will get their ID when aggregation begins */ - if (txq_id <= IWL_TX_FIFO_AC3) + /* + * Aggregation TX queues will get their ID when aggregation begins; + * they overwrite the setting done here. The command FIFO doesn't + * need an swq_id so don't set one to catch errors, all others can + * be set up to the identity mapping. + */ + if (txq_id != IWL_CMD_QUEUE_NUM) txq->swq_id = txq_id; /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise @@ -406,15 +407,19 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv) int txq_id; /* Tx queues */ - for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) - if (txq_id == IWL_CMD_QUEUE_NUM) - iwl_cmd_queue_free(priv); - else - iwl_tx_queue_free(priv, txq_id); - + if (priv->txq) + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; + txq_id++) + if (txq_id == IWL_CMD_QUEUE_NUM) + iwl_cmd_queue_free(priv); + else + iwl_tx_queue_free(priv, txq_id); iwl_free_dma_ptr(priv, &priv->kw); iwl_free_dma_ptr(priv, &priv->scd_bc_tbls); + + /* free tx queue structure */ + iwl_free_txq_mem(priv); } EXPORT_SYMBOL(iwl_hw_txq_ctx_free); @@ -446,6 +451,12 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv) IWL_ERR(priv, "Keep Warm allocation failed\n"); goto error_kw; } + + /* allocate tx queue structure */ + ret = iwl_alloc_txq_mem(priv); + if (ret) + goto error; + spin_lock_irqsave(&priv->lock, flags); /* Turn off all Tx DMA fifos */ @@ -582,9 +593,7 @@ static void iwl_tx_cmd_build_rate(struct iwl_priv *priv, u8 rate_plcp; /* Set retry limit on DATA packets and Probe Responses*/ - if (priv->data_retry_limit != -1) - data_retry_limit = priv->data_retry_limit; - else if (ieee80211_is_probe_resp(fc)) + if (ieee80211_is_probe_resp(fc)) data_retry_limit = 3; else data_retry_limit = IWL_DEFAULT_TX_RETRY; @@ -701,6 +710,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = info->control.sta; + struct iwl_station_priv *sta_priv = NULL; struct iwl_tx_queue *txq; struct iwl_queue *q; struct iwl_device_cmd *out_cmd; @@ -710,7 +721,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) dma_addr_t phys_addr; dma_addr_t txcmd_phys; dma_addr_t scratch_phys; - u16 len, len_org; + u16 len, len_org, firstlen, secondlen; u16 seq_number = 0; __le16 fc; u8 hdr_len; @@ -763,6 +774,24 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); + if (sta) + sta_priv = (void *)sta->drv_priv; + + if (sta_priv && sta_id != priv->hw_params.bcast_sta_id && + sta_priv->asleep) { + WARN_ON(!(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)); + /* + * This sends an asynchronous command to the device, + * but we can rely on it being processed before the + * next frame is processed -- and the next frame to + * this station is the one that will consume this + * counter. + * For now set the counter to just 1 since we do not + * support uAPSD yet. + */ + iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); + } + txq_id = skb_get_queue_mapping(skb); if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); @@ -843,7 +872,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sizeof(struct iwl_cmd_header) + hdr_len; len_org = len; - len = (len + 3) & ~3; + firstlen = len = (len + 3) & ~3; if (len_org != len) len_org = 1; @@ -877,7 +906,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Set up TFD's 2nd entry to point directly to remainder of skb, * if any (802.11 null frames have no payload). */ - len = skb->len - hdr_len; + secondlen = len = skb->len - hdr_len; if (len) { phys_addr = pci_map_single(priv->pci_dev, skb->data + hdr_len, len, PCI_DMA_TODEVICE); @@ -911,11 +940,28 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys, len, PCI_DMA_BIDIRECTIONAL); + trace_iwlwifi_dev_tx(priv, + &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], + sizeof(struct iwl_tfd), + &out_cmd->hdr, firstlen, + skb->data + hdr_len, secondlen); + /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); ret = iwl_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); + /* + * At this point the frame is "transmitted" successfully + * and we will get a TX status notification eventually, + * regardless of the value of ret. "ret" only indicates + * whether or not we should update the write pointer. + */ + + /* avoid atomic ops if it isn't an associated client */ + if (sta_priv && sta_priv->client) + atomic_inc(&sta_priv->pending_frames); + if (ret) return ret; @@ -970,13 +1016,20 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && !(cmd->flags & CMD_SIZE_HUGE)); - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_INFO(priv, "Not sending command - RF KILL\n"); + if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { + IWL_WARN(priv, "Not sending command - %s KILL\n", + iwl_is_rfkill(priv) ? "RF" : "CT"); return -EIO; } if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { - IWL_ERR(priv, "No space for Tx\n"); + IWL_ERR(priv, "No space in command queue\n"); + if (iwl_within_ct_kill_margin(priv)) + iwl_tt_enter_ct_kill(priv); + else { + IWL_ERR(priv, "Restarting adapter due to queue full\n"); + queue_work(priv->workqueue, &priv->restart); + } return -ENOSPC; } @@ -1039,6 +1092,8 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) pci_unmap_addr_set(out_meta, mapping, phys_addr); pci_unmap_len_set(out_meta, len, fix_size); + trace_iwlwifi_dev_hcmd(priv, &out_cmd->hdr, fix_size, cmd->flags); + priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, phys_addr, fix_size, 1, U32_PAD(cmd->len)); @@ -1051,6 +1106,24 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) return ret ? ret : idx; } +static void iwl_tx_status(struct iwl_priv *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_sta *sta; + struct iwl_station_priv *sta_priv; + + sta = ieee80211_find_sta(priv->vif, hdr->addr1); + if (sta) { + sta_priv = (void *)sta->drv_priv; + /* avoid atomic ops if this isn't a client */ + if (sta_priv->client && + atomic_dec_return(&sta_priv->pending_frames) == 0) + ieee80211_sta_block_awake(priv->hw, sta, false); + } + + ieee80211_tx_status_irqsafe(priv->hw, skb); +} + int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) { struct iwl_tx_queue *txq = &priv->txq[txq_id]; @@ -1070,7 +1143,7 @@ int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { tx_info = &txq->txb[txq->q.read_ptr]; - ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb[0]); + iwl_tx_status(priv, tx_info->skb[0]); tx_info->skb[0] = NULL; if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl) @@ -1105,11 +1178,6 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, return; } - pci_unmap_single(priv->pci_dev, - pci_unmap_addr(&txq->meta[cmd_idx], mapping), - pci_unmap_len(&txq->meta[cmd_idx], len), - PCI_DMA_BIDIRECTIONAL); - for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { @@ -1132,7 +1200,7 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, */ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); @@ -1157,12 +1225,17 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) cmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index]; meta = &priv->txq[IWL_CMD_QUEUE_NUM].meta[cmd_index]; + pci_unmap_single(priv->pci_dev, + pci_unmap_addr(meta, mapping), + pci_unmap_len(meta, len), + PCI_DMA_BIDIRECTIONAL); + /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { - meta->source->reply_skb = rxb->skb; - rxb->skb = NULL; + meta->source->reply_page = (unsigned long)rxb_addr(rxb); + rxb->page = NULL; } else if (meta->callback) - meta->callback(priv, cmd, rxb->skb); + meta->callback(priv, cmd, pkt); iwl_hcmd_queue_reclaim(priv, txq_id, index, cmd_index); @@ -1240,7 +1313,7 @@ int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn) if (tid_data->tfds_in_queue == 0) { IWL_DEBUG_HT(priv, "HW queue is empty\n"); tid_data->agg.state = IWL_AGG_ON; - ieee80211_start_tx_ba_cb_irqsafe(priv->hw, ra, tid); + ieee80211_start_tx_ba_cb_irqsafe(priv->vif, ra, tid); } else { IWL_DEBUG_HT(priv, "HW queue is NOT empty: %d packets in HW queue\n", tid_data->tfds_in_queue); @@ -1313,7 +1386,7 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) if (ret) return ret; - ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, ra, tid); + ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, ra, tid); return 0; } @@ -1337,7 +1410,7 @@ int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id) priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, ssn, tx_fifo); tid_data->agg.state = IWL_AGG_OFF; - ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, addr, tid); + ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, addr, tid); } break; case IWL_EMPTYING_HW_QUEUE_ADDBA: @@ -1345,7 +1418,7 @@ int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id) if (tid_data->tfds_in_queue == 0) { IWL_DEBUG_HT(priv, "HW queue empty: continue ADDBA flow\n"); tid_data->agg.state = IWL_AGG_ON; - ieee80211_start_tx_ba_cb_irqsafe(priv->hw, addr, tid); + ieee80211_start_tx_ba_cb_irqsafe(priv->vif, addr, tid); } break; } @@ -1409,7 +1482,7 @@ static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv, info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb[0]); memset(&info->status, 0, sizeof(info->status)); - info->flags = IEEE80211_TX_STAT_ACK; + info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_map = successes; info->status.ampdu_ack_len = agg->frame_count; @@ -1429,7 +1502,7 @@ static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv, void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; struct iwl_tx_queue *txq = NULL; struct iwl_ht_agg *agg; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index d00a80334095..2a28a1f8b1fe 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -42,7 +42,6 @@ #include <linux/if_arp.h> #include <net/ieee80211_radiotap.h> -#include <net/lib80211.h> #include <net/mac80211.h> #include <asm/div64.h> @@ -77,11 +76,9 @@ #define VS #endif -#define IWL39_VERSION "1.2.26k" VD VS +#define DRV_VERSION IWLWIFI_VERSION VD VS #define DRV_COPYRIGHT "Copyright(c) 2003-2009 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" -#define DRV_VERSION IWL39_VERSION - MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); @@ -90,7 +87,6 @@ MODULE_LICENSE("GPL"); /* module parameters */ struct iwl_mod_params iwl3945_mod_params = { - .num_of_queues = IWL39_NUM_QUEUES, /* Not used */ .sw_crypto = 1, .restart_fw = 1, /* the rest are 0 by default */ @@ -368,13 +364,13 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, struct sk_buff *skb_frag, int sta_id) { - struct iwl3945_tx_cmd *tx = (struct iwl3945_tx_cmd *)cmd->cmd.payload; + struct iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload; struct iwl_hw_key *keyinfo = &priv->stations[sta_id].keyinfo; switch (keyinfo->alg) { case ALG_CCMP: - tx->sec_ctl = TX_CMD_SEC_CCM; - memcpy(tx->key, keyinfo->key, keyinfo->keylen); + tx_cmd->sec_ctl = TX_CMD_SEC_CCM; + memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen); IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); break; @@ -382,13 +378,13 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, break; case ALG_WEP: - tx->sec_ctl = TX_CMD_SEC_WEP | + tx_cmd->sec_ctl = TX_CMD_SEC_WEP | (info->control.hw_key->hw_key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT; if (keyinfo->keylen == 13) - tx->sec_ctl |= TX_CMD_SEC_KEY128; + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - memcpy(&tx->key[3], keyinfo->key, keyinfo->keylen); + memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen); IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " "with key %d\n", info->control.hw_key->hw_key_idx); @@ -408,12 +404,11 @@ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, u8 std_id) { - struct iwl3945_tx_cmd *tx = (struct iwl3945_tx_cmd *)cmd->cmd.payload; - __le32 tx_flags = tx->tx_flags; + struct iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload; + __le32 tx_flags = tx_cmd->tx_flags; __le16 fc = hdr->frame_control; - u8 rc_flags = info->control.rates[0].flags; - tx->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { tx_flags |= TX_CMD_FLG_ACK_MSK; if (ieee80211_is_mgmt(fc)) @@ -426,25 +421,19 @@ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv, tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } - tx->sta_id = std_id; + tx_cmd->sta_id = std_id; if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; if (ieee80211_is_data_qos(fc)) { u8 *qc = ieee80211_get_qos_ctl(hdr); - tx->tid_tspec = qc[0] & 0xf; + tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { - tx_flags |= TX_CMD_FLG_RTS_MSK; - tx_flags &= ~TX_CMD_FLG_CTS_MSK; - } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - tx_flags &= ~TX_CMD_FLG_RTS_MSK; - tx_flags |= TX_CMD_FLG_CTS_MSK; - } + priv->cfg->ops->utils->rts_tx_cmd_flag(info, &tx_flags); if ((tx_flags & TX_CMD_FLG_RTS_MSK) || (tx_flags & TX_CMD_FLG_CTS_MSK)) tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; @@ -452,19 +441,16 @@ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv, tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); if (ieee80211_is_mgmt(fc)) { if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) - tx->timeout.pm_frame_timeout = cpu_to_le16(3); + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); else - tx->timeout.pm_frame_timeout = cpu_to_le16(2); + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); } else { - tx->timeout.pm_frame_timeout = 0; -#ifdef CONFIG_IWLWIFI_LEDS - priv->rxtxpackets += le16_to_cpu(cmd->cmd.tx.len); -#endif + tx_cmd->timeout.pm_frame_timeout = 0; } - tx->driver_txop = 0; - tx->tx_flags = tx_flags; - tx->next_frame_len = 0; + tx_cmd->driver_txop = 0; + tx_cmd->tx_flags = tx_flags; + tx_cmd->next_frame_len = 0; } /* @@ -474,7 +460,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl3945_tx_cmd *tx; + struct iwl3945_tx_cmd *tx_cmd; struct iwl_tx_queue *txq = NULL; struct iwl_queue *q = NULL; struct iwl_device_cmd *out_cmd; @@ -573,9 +559,9 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Init first empty entry in queue's array of Tx/cmd buffers */ out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; - tx = (struct iwl3945_tx_cmd *)out_cmd->cmd.payload; + tx_cmd = (struct iwl3945_tx_cmd *)out_cmd->cmd.payload; memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); - memset(tx, 0, sizeof(*tx)); + memset(tx_cmd, 0, sizeof(*tx_cmd)); /* * Set up the Tx-command (not MAC!) header. @@ -588,7 +574,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) INDEX_TO_SEQ(q->write_ptr))); /* Copy MAC header from skb into command buffer */ - memcpy(tx->hdr, hdr, hdr_len); + memcpy(tx_cmd->hdr, hdr, hdr_len); if (info->control.hw_key) @@ -602,12 +588,12 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Total # bytes to be transmitted */ len = (u16)skb->len; - tx->len = cpu_to_le16(len); + tx_cmd->len = cpu_to_le16(len); iwl_dbg_log_tx_data_frame(priv, len, hdr); iwl_update_stats(priv, true, fc, len); - tx->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; - tx->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; + tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; + tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; if (!ieee80211_has_morefrags(hdr->frame_control)) { txq->need_update = 1; @@ -620,9 +606,9 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "sequence nr = 0X%x \n", le16_to_cpu(out_cmd->hdr.sequence)); - IWL_DEBUG_TX(priv, "tx_flags = 0X%x \n", le32_to_cpu(tx->tx_flags)); - iwl_print_hex_dump(priv, IWL_DL_TX, tx, sizeof(*tx)); - iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx->hdr, + IWL_DEBUG_TX(priv, "tx_flags = 0X%x \n", le32_to_cpu(tx_cmd->tx_flags)); + iwl_print_hex_dump(priv, IWL_DL_TX, tx_cmd, sizeof(*tx_cmd)); + iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, ieee80211_hdrlen(fc)); /* @@ -758,7 +744,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, u8 type) { struct iwl_spectrum_cmd spectrum; - struct iwl_rx_packet *res; + struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = REPLY_SPECTRUM_MEASUREMENT_CMD, .data = (void *)&spectrum, @@ -803,18 +789,18 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, if (rc) return rc; - res = (struct iwl_rx_packet *)cmd.reply_skb->data; - if (res->hdr.flags & IWL_CMD_FAILED_MSK) { + pkt = (struct iwl_rx_packet *)cmd.reply_page; + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_RX_ON_ASSOC command\n"); rc = -EIO; } - spectrum_resp_status = le16_to_cpu(res->u.spectrum.status); + spectrum_resp_status = le16_to_cpu(pkt->u.spectrum.status); switch (spectrum_resp_status) { case 0: /* Command will be handled */ - if (res->u.spectrum.id != 0xff) { + if (pkt->u.spectrum.id != 0xff) { IWL_DEBUG_INFO(priv, "Replaced existing measurement: %d\n", - res->u.spectrum.id); + pkt->u.spectrum.id); priv->measurement_status &= ~MEASUREMENT_READY; } priv->measurement_status |= MEASUREMENT_ACTIVE; @@ -826,7 +812,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, break; } - dev_kfree_skb_any(cmd.reply_skb); + free_pages(cmd.reply_page, priv->hw_params.rx_page_order); return rc; } @@ -835,7 +821,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, static void iwl3945_rx_reply_alive(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_alive_resp *palive; struct delayed_work *pwork; @@ -872,7 +858,7 @@ static void iwl3945_rx_reply_add_sta(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { #ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); #endif IWL_DEBUG_RX(priv, "Received REPLY_ADD_STA: 0x%02X\n", pkt->u.status); @@ -908,7 +894,7 @@ static void iwl3945_rx_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { #ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl3945_beacon_notif *beacon = &(pkt->u.beacon_status); u8 rate = beacon->beacon_notify_hdr.rate; @@ -931,7 +917,7 @@ static void iwl3945_rx_beacon_notif(struct iwl_priv *priv, static void iwl3945_rx_card_state_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { - struct iwl_rx_packet *pkt = (void *)rxb->skb->data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); unsigned long status = priv->status; @@ -1095,7 +1081,7 @@ static int iwl3945_rx_queue_restock(struct iwl_priv *priv) list_del(element); /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwl3945_dma_addr2rbd_ptr(priv, rxb->real_dma_addr); + rxq->bd[rxq->write] = iwl3945_dma_addr2rbd_ptr(priv, rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; @@ -1135,8 +1121,9 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) struct iwl_rx_queue *rxq = &priv->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; - struct sk_buff *skb; + struct page *page; unsigned long flags; + gfp_t gfp_mask = priority; while (1) { spin_lock_irqsave(&rxq->lock, flags); @@ -1148,10 +1135,14 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) spin_unlock_irqrestore(&rxq->lock, flags); if (rxq->free_count > RX_LOW_WATERMARK) - priority |= __GFP_NOWARN; + gfp_mask |= __GFP_NOWARN; + + if (priv->hw_params.rx_page_order > 0) + gfp_mask |= __GFP_COMP; + /* Alloc a new receive buffer */ - skb = alloc_skb(priv->hw_params.rx_buf_size, priority); - if (!skb) { + page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order); + if (!page) { if (net_ratelimit()) IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n"); if ((rxq->free_count <= RX_LOW_WATERMARK) && @@ -1168,7 +1159,7 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); - dev_kfree_skb_any(skb); + __free_pages(page, priv->hw_params.rx_page_order); return; } element = rxq->rx_used.next; @@ -1176,26 +1167,18 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) list_del(element); spin_unlock_irqrestore(&rxq->lock, flags); - rxb->skb = skb; - - /* If radiotap head is required, reserve some headroom here. - * The physical head count is a variable rx_stats->phy_count. - * We reserve 4 bytes here. Plus these extra bytes, the - * headroom of the physical head should be enough for the - * radiotap head that iwl3945 supported. See iwl3945_rt. - */ - skb_reserve(rxb->skb, 4); - + rxb->page = page; /* Get physical address of RB/SKB */ - rxb->real_dma_addr = pci_map_single(priv->pci_dev, - rxb->skb->data, - priv->hw_params.rx_buf_size, - PCI_DMA_FROMDEVICE); + rxb->page_dma = pci_map_page(priv->pci_dev, page, 0, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); spin_lock_irqsave(&rxq->lock, flags); + list_add_tail(&rxb->list, &rxq->rx_free); - priv->alloc_rxb_skb++; rxq->free_count++; + priv->alloc_rxb_page++; + spin_unlock_irqrestore(&rxq->lock, flags); } } @@ -1211,14 +1194,14 @@ void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { /* In the reset function, these buffers may have been allocated * to an SKB, so we need to unmap and free potential storage */ - if (rxq->pool[i].skb != NULL) { - pci_unmap_single(priv->pci_dev, - rxq->pool[i].real_dma_addr, - priv->hw_params.rx_buf_size, - PCI_DMA_FROMDEVICE); - priv->alloc_rxb_skb--; - dev_kfree_skb(rxq->pool[i].skb); - rxq->pool[i].skb = NULL; + if (rxq->pool[i].page != NULL) { + pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + priv->alloc_rxb_page--; + __free_pages(rxq->pool[i].page, + priv->hw_params.rx_page_order); + rxq->pool[i].page = NULL; } list_add_tail(&rxq->pool[i].list, &rxq->rx_used); } @@ -1226,8 +1209,8 @@ void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) /* Set us so that we have processed and used all buffers, but have * not restocked the Rx queue with fresh buffers */ rxq->read = rxq->write = 0; - rxq->free_count = 0; rxq->write_actual = 0; + rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); } @@ -1260,12 +1243,14 @@ static void iwl3945_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rx { int i; for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { - if (rxq->pool[i].skb != NULL) { - pci_unmap_single(priv->pci_dev, - rxq->pool[i].real_dma_addr, - priv->hw_params.rx_buf_size, - PCI_DMA_FROMDEVICE); - dev_kfree_skb(rxq->pool[i].skb); + if (rxq->pool[i].page != NULL) { + pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __free_pages(rxq->pool[i].page, + priv->hw_params.rx_page_order); + rxq->pool[i].page = NULL; + priv->alloc_rxb_page--; } } @@ -1381,7 +1366,7 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) i = rxq->read; /* calculate total frames need to be restock after handling RX */ - total_empty = r - priv->rxq.write_actual; + total_empty = r - rxq->write_actual; if (total_empty < 0) total_empty += RX_QUEUE_SIZE; @@ -1401,10 +1386,13 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) rxq->queue[i] = NULL; - pci_unmap_single(priv->pci_dev, rxb->real_dma_addr, - priv->hw_params.rx_buf_size, - PCI_DMA_FROMDEVICE); - pkt = (struct iwl_rx_packet *)rxb->skb->data; + pci_unmap_page(priv->pci_dev, rxb->page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + pkt = rxb_addr(rxb); + + trace_iwlwifi_dev_rx(priv, pkt, + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); /* Reclaim a command buffer only if this packet is a response * to a (driver-originated) command. @@ -1422,44 +1410,55 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) if (priv->rx_handlers[pkt->hdr.cmd]) { IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); - priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); priv->isr_stats.rx_handlers[pkt->hdr.cmd]++; + priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); } else { /* No handling needed */ - IWL_DEBUG_RX(priv, "r %d i %d No handler needed for %s, 0x%02x\n", + IWL_DEBUG_RX(priv, + "r %d i %d No handler needed for %s, 0x%02x\n", r, i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); } + /* + * XXX: After here, we should always check rxb->page + * against NULL before touching it or its virtual + * memory (pkt). Because some rx_handler might have + * already taken or freed the pages. + */ + if (reclaim) { - /* Invoke any callbacks, transfer the skb to caller, and - * fire off the (possibly) blocking iwl_send_cmd() + /* Invoke any callbacks, transfer the buffer to caller, + * and fire off the (possibly) blocking iwl_send_cmd() * as we reclaim the driver command queue */ - if (rxb && rxb->skb) + if (rxb->page) iwl_tx_cmd_complete(priv, rxb); else IWL_WARN(priv, "Claim null rxb?\n"); } - /* For now we just don't re-use anything. We can tweak this - * later to try and re-use notification packets and SKBs that - * fail to Rx correctly */ - if (rxb->skb != NULL) { - priv->alloc_rxb_skb--; - dev_kfree_skb_any(rxb->skb); - rxb->skb = NULL; - } - + /* Reuse the page if possible. For notification packets and + * SKBs that fail to Rx correctly, add them back into the + * rx_free list for reuse later. */ spin_lock_irqsave(&rxq->lock, flags); - list_add_tail(&rxb->list, &priv->rxq.rx_used); + if (rxb->page != NULL) { + rxb->page_dma = pci_map_page(priv->pci_dev, rxb->page, + 0, PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } else + list_add_tail(&rxb->list, &rxq->rx_used); + spin_unlock_irqrestore(&rxq->lock, flags); + i = (i + 1) & RX_QUEUE_MASK; /* If there are a lot of unused frames, * restock the Rx queue so ucode won't assert. */ if (fill_rx) { count++; if (count >= 8) { - priv->rxq.read = i; + rxq->read = i; iwl3945_rx_replenish_now(priv); count = 0; } @@ -1467,7 +1466,7 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) } /* Backtrack one entry */ - priv->rxq.read = i; + rxq->read = i; if (fill_rx) iwl3945_rx_replenish_now(priv); else @@ -1482,7 +1481,6 @@ static inline void iwl_synchronize_irq(struct iwl_priv *priv) tasklet_kill(&priv->irq_tasklet); } -#ifdef CONFIG_IWLWIFI_DEBUG static const char *desc_lookup(int i) { switch (i) { @@ -1551,8 +1549,9 @@ void iwl3945_dump_nic_error_log(struct iwl_priv *priv) "%-13s (#%d) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n", desc_lookup(desc), desc, time, blink1, blink2, ilink1, ilink2, data1); + trace_iwlwifi_dev_ucode_error(priv, desc, time, data1, 0, + 0, blink1, blink2, ilink1, ilink2); } - } #define EVENT_START_OFFSET (6 * sizeof(u32)) @@ -1569,6 +1568,7 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ u32 ptr; /* SRAM byte address of log data */ u32 ev, time, data; /* event log data */ + unsigned long reg_flags; if (num_events == 0) return; @@ -1582,25 +1582,71 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, ptr = base + EVENT_START_OFFSET + (start_idx * event_size); + /* Make sure device is powered up for SRAM reads */ + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + + /* Set starting address; reads will auto-increment */ + _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr); + rmb(); + /* "time" is actually "data" for mode 0 (no timestamp). * place event id # at far right for easier visual parsing. */ for (i = 0; i < num_events; i++) { - ev = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - time = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); + ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); if (mode == 0) { /* data, ev */ IWL_ERR(priv, "0x%08x\t%04u\n", time, ev); + trace_iwlwifi_dev_ucode_event(priv, 0, time, ev); } else { - data = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); + data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); IWL_ERR(priv, "%010u\t0x%08x\t%04u\n", time, data, ev); + trace_iwlwifi_dev_ucode_event(priv, time, data, ev); } } + + /* Allow device to power down */ + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); } -void iwl3945_dump_nic_event_log(struct iwl_priv *priv) +/** + * iwl3945_print_last_event_logs - Dump the newest # of event log to syslog + */ +static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity, + u32 num_wraps, u32 next_entry, + u32 size, u32 mode) +{ + /* + * display the newest DEFAULT_LOG_ENTRIES entries + * i.e the entries just before the next ont that uCode would fill. + */ + if (num_wraps) { + if (next_entry < size) { + iwl3945_print_event_log(priv, + capacity - (size - next_entry), + size - next_entry, mode); + iwl3945_print_event_log(priv, 0, + next_entry, mode); + } else + iwl3945_print_event_log(priv, next_entry - size, + size, mode); + } else { + if (next_entry < size) + iwl3945_print_event_log(priv, 0, next_entry, mode); + else + iwl3945_print_event_log(priv, next_entry - size, + size, mode); + } +} + +/* For sanity check only. Actual size is determined by uCode, typ. 512 */ +#define IWL3945_MAX_EVENT_LOG_SIZE (512) + +#define DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES (20) + +void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log) { u32 base; /* SRAM byte address of event log header */ u32 capacity; /* event log capacity in # entries */ @@ -1621,6 +1667,18 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv) num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); + if (capacity > IWL3945_MAX_EVENT_LOG_SIZE) { + IWL_ERR(priv, "Log capacity %d is bogus, limit to %d entries\n", + capacity, IWL3945_MAX_EVENT_LOG_SIZE); + capacity = IWL3945_MAX_EVENT_LOG_SIZE; + } + + if (next_entry > IWL3945_MAX_EVENT_LOG_SIZE) { + IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n", + next_entry, IWL3945_MAX_EVENT_LOG_SIZE); + next_entry = IWL3945_MAX_EVENT_LOG_SIZE; + } + size = num_wraps ? capacity : next_entry; /* bail out if nothing in log */ @@ -1629,30 +1687,40 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv) return; } - IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", - size, num_wraps); +#ifdef CONFIG_IWLWIFI_DEBUG + if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS)) + size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size; +#else + size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size; +#endif + + IWL_ERR(priv, "Start IWL Event Log Dump: display last %d count\n", + size); - /* if uCode has wrapped back to top of log, start at the oldest entry, - * i.e the next one that uCode would fill. */ - if (num_wraps) - iwl3945_print_event_log(priv, next_entry, +#ifdef CONFIG_IWLWIFI_DEBUG + if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { + /* if uCode has wrapped back to top of log, + * start at the oldest entry, + * i.e the next one that uCode would fill. + */ + if (num_wraps) + iwl3945_print_event_log(priv, next_entry, capacity - next_entry, mode); - /* (then/else) start at top of log */ - iwl3945_print_event_log(priv, 0, next_entry, mode); - -} + /* (then/else) start at top of log */ + iwl3945_print_event_log(priv, 0, next_entry, mode); + } else + iwl3945_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode); #else -void iwl3945_dump_nic_event_log(struct iwl_priv *priv) -{ -} + iwl3945_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode); +#endif -void iwl3945_dump_nic_error_log(struct iwl_priv *priv) -{ } -#endif - static void iwl3945_irq_tasklet(struct iwl_priv *priv) { u32 inta, handled = 0; @@ -1685,6 +1753,8 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) } #endif + spin_unlock_irqrestore(&priv->lock, flags); + /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not * atomic, make sure that inta covers all the interrupts that * we've discovered, even if FH interrupt came in just after @@ -1706,8 +1776,6 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) handled |= CSR_INT_BIT_HW_ERR; - spin_unlock_irqrestore(&priv->lock, flags); - return; } @@ -1799,7 +1867,6 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); } #endif - spin_unlock_irqrestore(&priv->lock, flags); } static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, @@ -2158,6 +2225,14 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) IWL_UCODE_API(priv->ucode_ver), IWL_UCODE_SERIAL(priv->ucode_ver)); + snprintf(priv->hw->wiphy->fw_version, + sizeof(priv->hw->wiphy->fw_version), + "%u.%u.%u.%u", + IWL_UCODE_MAJOR(priv->ucode_ver), + IWL_UCODE_MINOR(priv->ucode_ver), + IWL_UCODE_API(priv->ucode_ver), + IWL_UCODE_SERIAL(priv->ucode_ver)); + IWL_DEBUG_INFO(priv, "f/w package hdr ucode version raw = 0x%x\n", priv->ucode_ver); IWL_DEBUG_INFO(priv, "f/w package hdr runtime inst size = %u\n", @@ -2458,7 +2533,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv) priv->active_rate = priv->rates_mask; priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; - iwl_power_update_mode(priv, false); + iwl_power_update_mode(priv, true); if (iwl_is_associated(priv)) { struct iwl3945_rxon_cmd *active_rxon = @@ -2479,7 +2554,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv) iwl3945_reg_txpower_periodic(priv); - iwl3945_led_register(priv); + iwl_leds_init(priv); IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); set_bit(STATUS_READY, &priv->status); @@ -2517,7 +2592,6 @@ static void __iwl3945_down(struct iwl_priv *priv) if (!exit_pending) set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl3945_led_unregister(priv); iwl_clear_stations_table(priv); /* Unblock any waiting calls */ @@ -2563,23 +2637,15 @@ static void __iwl3945_down(struct iwl_priv *priv) test_bit(STATUS_EXIT_PENDING, &priv->status) << STATUS_EXIT_PENDING; - priv->cfg->ops->lib->apm_ops.reset(priv); - spin_lock_irqsave(&priv->lock, flags); - iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - spin_unlock_irqrestore(&priv->lock, flags); - iwl3945_hw_txq_ctx_stop(priv); iwl3945_hw_rxq_stop(priv); - iwl_write_prph(priv, APMG_CLK_DIS_REG, - APMG_CLK_VAL_DMA_CLK_RQT); - + /* Power-down device's busmaster DMA clocks */ + iwl_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(5); - if (exit_pending) - priv->cfg->ops->lib->apm_ops.stop(priv); - else - priv->cfg->ops->lib->apm_ops.reset(priv); + /* Stop the device, and put it in low power state */ + priv->cfg->ops->lib->apm_ops.stop(priv); exit: memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp)); @@ -2724,19 +2790,34 @@ static void iwl3945_bg_alive_start(struct work_struct *data) mutex_unlock(&priv->mutex); } +/* + * 3945 cannot interrupt driver when hardware rf kill switch toggles; + * driver must poll CSR_GP_CNTRL_REG register for change. This register + * *is* readable even when device has been SW_RESET into low power mode + * (e.g. during RF KILL). + */ static void iwl3945_rfkill_poll(struct work_struct *data) { struct iwl_priv *priv = container_of(data, struct iwl_priv, rfkill_poll.work); + bool old_rfkill = test_bit(STATUS_RF_KILL_HW, &priv->status); + bool new_rfkill = !(iwl_read32(priv, CSR_GP_CNTRL) + & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); - if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) - clear_bit(STATUS_RF_KILL_HW, &priv->status); - else - set_bit(STATUS_RF_KILL_HW, &priv->status); + if (new_rfkill != old_rfkill) { + if (new_rfkill) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + wiphy_rfkill_set_hw_state(priv->hw->wiphy, new_rfkill); - wiphy_rfkill_set_hw_state(priv->hw->wiphy, - test_bit(STATUS_RF_KILL_HW, &priv->status)); + IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n", + new_rfkill ? "disable radio" : "enable radio"); + } + /* Keep this running, even if radio now enabled. This will be + * cancelled in mac_start() if system decides to start again */ queue_delayed_work(priv->workqueue, &priv->rfkill_poll, round_jiffies_relative(2 * HZ)); @@ -3152,6 +3233,8 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw) * no need to poll the killswitch state anymore */ cancel_delayed_work(&priv->rfkill_poll); + iwl_led_start(priv); + priv->is_open = 1; IWL_DEBUG_MAC80211(priv, "leave\n"); return 0; @@ -3606,7 +3689,7 @@ static ssize_t show_statistics(struct device *d, return -EAGAIN; mutex_lock(&priv->mutex); - rc = iwl_send_statistics_request(priv, 0); + rc = iwl_send_statistics_request(priv, CMD_SYNC, false); mutex_unlock(&priv->mutex); if (rc) { @@ -3795,7 +3878,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv) /* Clear the driver's (not device's) station table */ iwl_clear_stations_table(priv); - priv->data_retry_limit = -1; priv->ieee_channels = NULL; priv->ieee_rates = NULL; priv->band = IEEE80211_BAND_2GHZ; @@ -3862,10 +3944,8 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->custom_regulatory = true; - - /* Firmware does not support this */ - hw->wiphy->disable_beacon_hints = true; + hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY | + WIPHY_FLAG_DISABLE_BEACON_HINTS; hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945; /* we create the 802.11 header and a zero-length SSID element */ @@ -3982,13 +4062,6 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e */ spin_lock_init(&priv->reg_lock); - /* amp init */ - err = priv->cfg->ops->lib->apm_ops.init(priv); - if (err < 0) { - IWL_DEBUG_INFO(priv, "Failed to init the card\n"); - goto out_iounmap; - } - /*********************** * 4. Read EEPROM * ********************/ @@ -4054,6 +4127,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e &priv->bands[IEEE80211_BAND_2GHZ].channels[5]); iwl3945_setup_deferred_work(priv); iwl3945_setup_rx_handlers(priv); + iwl_power_initialize(priv); /********************************* * 8. Setup and Register mac80211 @@ -4124,6 +4198,15 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) iwl3945_down(priv); } + /* + * Make sure device is reset to low power before unloading driver. + * This may be redundant with iwl_down(), but there are paths to + * run iwl_down() without calling apm_ops.stop(), and there are + * paths to avoid running iwl_down() at all before leaving driver. + * This (inexpensive) call *makes sure* device is reset. + */ + priv->cfg->ops->lib->apm_ops.stop(priv); + /* make sure we flush any pending irq or * tasklet for the driver */ @@ -4226,18 +4309,19 @@ static void __exit iwl3945_exit(void) MODULE_FIRMWARE(IWL3945_MODULE_FIRMWARE(IWL3945_UCODE_API_MAX)); -module_param_named(antenna, iwl3945_mod_params.antenna, int, 0444); +module_param_named(antenna, iwl3945_mod_params.antenna, int, S_IRUGO); MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); -module_param_named(swcrypto, iwl3945_mod_params.sw_crypto, int, 0444); +module_param_named(swcrypto, iwl3945_mod_params.sw_crypto, int, S_IRUGO); MODULE_PARM_DESC(swcrypto, "using software crypto (default 1 [software])\n"); #ifdef CONFIG_IWLWIFI_DEBUG -module_param_named(debug, iwl_debug_level, uint, 0644); +module_param_named(debug, iwl_debug_level, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "debug output mask"); #endif -module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, 0444); +module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, + int, S_IRUGO); MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); -module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, 0444); +module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); module_exit(iwl3945_exit); diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig index c25a04371ca8..b9d34a766964 100644 --- a/drivers/net/wireless/iwmc3200wifi/Kconfig +++ b/drivers/net/wireless/iwmc3200wifi/Kconfig @@ -1,8 +1,9 @@ config IWM tristate "Intel Wireless Multicomm 3200 WiFi driver" - depends on MMC && WLAN_80211 && EXPERIMENTAL + depends on MMC && EXPERIMENTAL depends on CFG80211 select FW_LOADER + select IWMC3200TOP help The Intel Wireless Multicomm 3200 hardware is a combo card with GPS, Bluetooth, WiMax and 802.11 radios. It diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c index f3c55658225b..7c4f44a9c3e6 100644 --- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c @@ -405,39 +405,21 @@ static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, { struct iwm_priv *iwm = wiphy_to_iwm(wiphy); struct ieee80211_channel *chan = params->channel; - struct cfg80211_bss *bss; if (!test_bit(IWM_STATUS_READY, &iwm->status)) return -EIO; - /* UMAC doesn't support creating IBSS network with specified bssid. - * This should be removed after we have join only mode supported. */ + /* UMAC doesn't support creating or joining an IBSS network + * with specified bssid. */ if (params->bssid) return -EOPNOTSUPP; - bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL, - params->ssid, params->ssid_len); - if (!bss) { - iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len); - schedule_timeout_interruptible(2 * HZ); - bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL, - params->ssid, params->ssid_len); - } - /* IBSS join only mode is not supported by UMAC ATM */ - if (bss) { - cfg80211_put_bss(bss); - return -EOPNOTSUPP; - } - iwm->channel = ieee80211_frequency_to_channel(chan->center_freq); iwm->umac_profile->ibss.band = chan->band; iwm->umac_profile->ibss.channel = iwm->channel; iwm->umac_profile->ssid.ssid_len = params->ssid_len; memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len); - if (params->bssid) - memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN); - return iwm_send_mlme_profile(iwm); } @@ -490,12 +472,12 @@ static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version) return 0; } + if (wpa_version & NL80211_WPA_VERSION_1) + iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK; + if (wpa_version & NL80211_WPA_VERSION_2) iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK; - if (wpa_version & NL80211_WPA_VERSION_1) - iwm->umac_profile->sec.flags |= UMAC_SEC_FLG_WPA_ON_MSK; - return 0; } @@ -646,6 +628,13 @@ static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, iwm->default_key = sme->key_idx; } + /* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */ + if ((iwm->umac_profile->sec.flags & + (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) && + iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) { + iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK; + } + ret = iwm_send_mlme_profile(iwm); if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK || @@ -682,10 +671,24 @@ static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, static int iwm_cfg80211_set_txpower(struct wiphy *wiphy, enum tx_power_setting type, int dbm) { + struct iwm_priv *iwm = wiphy_to_iwm(wiphy); + int ret; + switch (type) { case TX_POWER_AUTOMATIC: return 0; + case TX_POWER_FIXED: + if (!test_bit(IWM_STATUS_READY, &iwm->status)) + return 0; + + ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, + CFG_TX_PWR_LIMIT_USR, dbm * 2); + if (ret < 0) + return ret; + + return iwm_tx_power_trigger(iwm); default: + IWM_ERR(iwm, "Unsupported power type: %d\n", type); return -EOPNOTSUPP; } @@ -696,7 +699,7 @@ static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) { struct iwm_priv *iwm = wiphy_to_iwm(wiphy); - *dbm = iwm->txpower; + *dbm = iwm->txpower >> 1; return 0; } @@ -722,6 +725,33 @@ static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy, CFG_POWER_INDEX, iwm->conf.power_index); } +int iwm_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa) +{ + struct iwm_priv *iwm = wiphy_to_iwm(wiphy); + + return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD); +} + +int iwm_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa) +{ + struct iwm_priv *iwm = wiphy_to_iwm(wiphy); + + return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL); +} + +int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) +{ + struct iwm_priv *iwm = wiphy_to_iwm(wiphy); + struct cfg80211_pmksa pmksa; + + memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); + + return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH); +} + + static struct cfg80211_ops iwm_cfg80211_ops = { .change_virtual_intf = iwm_cfg80211_change_iface, .add_key = iwm_cfg80211_add_key, @@ -738,6 +768,9 @@ static struct cfg80211_ops iwm_cfg80211_ops = { .set_tx_power = iwm_cfg80211_set_txpower, .get_tx_power = iwm_cfg80211_get_txpower, .set_power_mgmt = iwm_cfg80211_set_power_mgmt, + .set_pmksa = iwm_cfg80211_set_pmksa, + .del_pmksa = iwm_cfg80211_del_pmksa, + .flush_pmksa = iwm_cfg80211_flush_pmksa, }; static const u32 cipher_suites[] = { @@ -783,6 +816,7 @@ struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev) set_wiphy_dev(wdev->wiphy, dev); wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX; + wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS; wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz; diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c index 84158b6d35d8..777584d76a88 100644 --- a/drivers/net/wireless/iwmc3200wifi/commands.c +++ b/drivers/net/wireless/iwmc3200wifi/commands.c @@ -77,6 +77,11 @@ int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, int ret; u8 oid = hdr->oid; + if (!test_bit(IWM_STATUS_READY, &iwm->status)) { + IWM_ERR(iwm, "Interface is not ready yet"); + return -EAGAIN; + } + umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER; umac_cmd.resp = resp; @@ -94,6 +99,10 @@ int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, return ret; } +static int modparam_wiwi = COEX_MODE_CM; +module_param_named(wiwi, modparam_wiwi, int, 0644); +MODULE_PARM_DESC(wiwi, "Wifi-WiMAX coexistence: 1=SA, 2=XOR, 3=CM (default)"); + static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] = { {4, 3, 0, COEX_UNASSOC_IDLE_FLAGS}, @@ -117,18 +126,18 @@ static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] = static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] = { {1, 1, 0, COEX_UNASSOC_IDLE_FLAGS}, - {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, + {4, 4, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, - {5, 5, 0, COEX_CALIBRATION_FLAGS}, + {6, 6, 0, COEX_CALIBRATION_FLAGS}, {3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, - {5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS}, + {6, 5, 0, COEX_CONNECTION_ESTAB_FLAGS}, {4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS}, {4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, {4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS}, {4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS}, {1, 1, 0, COEX_RF_ON_FLAGS}, {1, 1, 0, COEX_RF_OFF_FLAGS}, - {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, + {7, 7, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, {5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS}, {1, 1, 0, COEX_RSRVD1_FLAGS}, {1, 1, 0, COEX_RSRVD2_FLAGS} @@ -143,7 +152,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm) coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK; - switch (iwm->conf.coexist_mode) { + switch (modparam_wiwi) { case COEX_MODE_XOR: case COEX_MODE_CM: coex_enabled = 1; @@ -168,7 +177,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm) COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK | COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK; - switch (iwm->conf.coexist_mode) { + switch (modparam_wiwi) { case COEX_MODE_XOR: memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl, sizeof(iwm_sta_xor_prio_tbl)); @@ -179,7 +188,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm) break; default: IWM_ERR(iwm, "Invalid coex_mode 0x%x\n", - iwm->conf.coexist_mode); + modparam_wiwi); break; } } else @@ -187,7 +196,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm) return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD, &coex_table_cmd, - sizeof(struct iwm_coex_prio_table_cmd), 1); + sizeof(struct iwm_coex_prio_table_cmd), 0); } int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested) @@ -275,6 +284,17 @@ int iwm_send_calib_results(struct iwm_priv *iwm) return ret; } +int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit) +{ + struct iwm_ct_kill_cfg_cmd cmd; + + cmd.entry_threshold = entry; + cmd.exit_threshold = exit; + + return iwm_send_lmac_ptrough_cmd(iwm, REPLY_CT_KILL_CONFIG_CMD, &cmd, + sizeof(struct iwm_ct_kill_cfg_cmd), 0); +} + int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp) { struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; @@ -380,7 +400,7 @@ int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags) return ret; ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, - CFG_COEX_MODE, iwm->conf.coexist_mode); + CFG_COEX_MODE, modparam_wiwi); if (ret < 0) return ret; @@ -778,11 +798,24 @@ int iwm_invalidate_mlme_profile(struct iwm_priv *iwm) return ret; ret = wait_event_interruptible_timeout(iwm->mlme_queue, - (iwm->umac_profile_active == 0), 2 * HZ); + (iwm->umac_profile_active == 0), 5 * HZ); return ret ? 0 : -EBUSY; } +int iwm_tx_power_trigger(struct iwm_priv *iwm) +{ + struct iwm_umac_pwr_trigger pwr_trigger; + + pwr_trigger.hdr.oid = UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER; + pwr_trigger.hdr.buf_size = + cpu_to_le16(sizeof(struct iwm_umac_pwr_trigger) - + sizeof(struct iwm_umac_wifi_if)); + + + return iwm_send_wifi_if_cmd(iwm, &pwr_trigger, sizeof(pwr_trigger), 1); +} + int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags) { struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; @@ -900,3 +933,58 @@ int iwm_target_reset(struct iwm_priv *iwm) return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); } + +int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, + struct iwm_umac_notif_stop_resume_tx *ntf) +{ + struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; + struct iwm_umac_cmd umac_cmd; + struct iwm_umac_cmd_stop_resume_tx stp_res_cmd; + struct iwm_sta_info *sta_info; + u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id); + int i; + + sta_info = &iwm->sta_table[sta_id]; + if (!sta_info->valid) { + IWM_ERR(iwm, "Invalid STA: %d\n", sta_id); + return -EINVAL; + } + + umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX; + umac_cmd.resp = 0; + + stp_res_cmd.flags = ntf->flags; + stp_res_cmd.sta_id = ntf->sta_id; + stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk; + for (i = 0; i < IWM_UMAC_TID_NR; i++) + stp_res_cmd.last_seq_num[i] = + sta_info->tid_info[i].last_seq_num; + + return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd, + sizeof(struct iwm_umac_cmd_stop_resume_tx)); + +} + +int iwm_send_pmkid_update(struct iwm_priv *iwm, + struct cfg80211_pmksa *pmksa, u32 command) +{ + struct iwm_umac_pmkid_update update; + int ret; + + memset(&update, 0, sizeof(struct iwm_umac_pmkid_update)); + + update.command = cpu_to_le32(command); + if (pmksa->bssid) + memcpy(&update.bssid, pmksa->bssid, ETH_ALEN); + if (pmksa->pmkid) + memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN); + + ret = iwm_send_wifi_if_cmd(iwm, &update, + sizeof(struct iwm_umac_pmkid_update), 0); + if (ret) { + IWM_ERR(iwm, "PMKID update command failed\n"); + return ret; + } + + return 0; +} diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h index e24d5b633997..06af0552cd75 100644 --- a/drivers/net/wireless/iwmc3200wifi/commands.h +++ b/drivers/net/wireless/iwmc3200wifi/commands.h @@ -102,7 +102,6 @@ enum { CFG_SCAN_NUM_PASSIVE_CHAN_PER_PARTIAL_SCAN, CFG_TLC_SUPPORTED_TX_HT_RATES, CFG_TLC_SUPPORTED_TX_RATES, - CFG_TLC_VALID_ANTENNA, CFG_TLC_SPATIAL_STREAM_SUPPORTED, CFG_TLC_RETRY_PER_RATE, CFG_TLC_RETRY_PER_HT_RATE, @@ -136,6 +135,10 @@ enum { CFG_TLC_RENEW_ADDBA_DELAY, CFG_TLC_NUM_OF_MULTISEC_TO_COUN_LOAD, CFG_TLC_IS_STABLE_IN_HT, + CFG_TLC_SR_SIC_1ST_FAIL, + CFG_TLC_SR_SIC_1ST_PASS, + CFG_TLC_SR_SIC_TOTAL_FAIL, + CFG_TLC_SR_SIC_TOTAL_PASS, CFG_RLC_CHAIN_CTRL, CFG_TRK_TABLE_OP_MODE, CFG_TRK_TABLE_RSSI_THRESHOLD, @@ -147,6 +150,58 @@ enum { CFG_MLME_DBG_NOTIF_BLOCK, CFG_BT_OFF_BECONS_INTERVALS, CFG_BT_FRAG_DURATION, + CFG_ACTIVE_CHAINS, + CFG_CALIB_CTRL, + CFG_CAPABILITY_SUPPORTED_HT_RATES, + CFG_HT_MAC_PARAM_INFO, + CFG_MIMO_PS_MODE, + CFG_HT_DEFAULT_CAPABILIES_INFO, + CFG_LED_SC_RESOLUTION_FACTOR, + CFG_PTAM_ENERGY_CCK_DET_DEFAULT, + CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_DEFAULT, + CFG_PTAM_CORR40_4_TH_ADD_MIN_DEFAULT, + CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_DEFAULT, + CFG_PTAM_CORR32_4_TH_ADD_MIN_DEFAULT, + CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_DEFAULT, + CFG_PTAM_CORR32_1_TH_ADD_MIN_DEFAULT, + CFG_PTAM_ENERGY_CCK_DET_MIN_VAL, + CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_MIN_VAL, + CFG_PTAM_CORR40_4_TH_ADD_MIN_MIN_VAL, + CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_MIN_VAL, + CFG_PTAM_CORR32_4_TH_ADD_MIN_MIN_VAL, + CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_MIN_VAL, + CFG_PTAM_CORR32_1_TH_ADD_MIN_MIN_VAL, + CFG_PTAM_ENERGY_CCK_DET_MAX_VAL, + CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_MAX_VAL, + CFG_PTAM_CORR40_4_TH_ADD_MIN_MAX_VAL, + CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_MAX_VAL, + CFG_PTAM_CORR32_4_TH_ADD_MIN_MAX_VAL, + CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_MAX_VAL, + CFG_PTAM_CORR32_1_TH_ADD_MIN_MAX_VAL, + CFG_PTAM_ENERGY_CCK_DET_STEP_VAL, + CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_STEP_VAL, + CFG_PTAM_CORR40_4_TH_ADD_MIN_STEP_VAL, + CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_STEP_VAL, + CFG_PTAM_CORR32_4_TH_ADD_MIN_STEP_VAL, + CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_STEP_VAL, + CFG_PTAM_CORR32_1_TH_ADD_MIN_STEP_VAL, + CFG_PTAM_LINK_SENS_FA_OFDM_MAX, + CFG_PTAM_LINK_SENS_FA_OFDM_MIN, + CFG_PTAM_LINK_SENS_FA_CCK_MAX, + CFG_PTAM_LINK_SENS_FA_CCK_MIN, + CFG_PTAM_LINK_SENS_NRG_DIFF, + CFG_PTAM_LINK_SENS_NRG_MARGIN, + CFG_PTAM_LINK_SENS_MAX_NUMBER_OF_TIMES_IN_CCK_NO_FA, + CFG_PTAM_LINK_SENS_AUTO_CORR_MAX_TH_CCK, + CFG_AGG_MGG_TID_LOAD_ADDBA_THRESHOLD, + CFG_AGG_MGG_TID_LOAD_DELBA_THRESHOLD, + CFG_AGG_MGG_ADDBA_BUF_SIZE, + CFG_AGG_MGG_ADDBA_INACTIVE_TIMEOUT, + CFG_AGG_MGG_ADDBA_DEBUG_FLAGS, + CFG_SCAN_PERIODIC_RSSI_HIGH_THRESHOLD, + CFG_SCAN_PERIODIC_COEF_RSSI_HIGH, + CFG_11D_ENABLED, + CFG_11H_FEATURE_FLAGS, /* <-- LAST --> */ CFG_TBL_FIX_LAST @@ -155,7 +210,8 @@ enum { /* variable size table */ enum { CFG_NET_ADDR = 0, - CFG_PROFILE, + CFG_LED_PATTERN_TABLE, + /* <-- LAST --> */ CFG_TBL_VAR_LAST }; @@ -288,6 +344,9 @@ struct iwm_umac_cmd_scan_request { /* iwm_umac_security.flag is WSC mode on -- bits [2:2] */ #define UMAC_SEC_FLG_WSC_ON_POS 2 #define UMAC_SEC_FLG_WSC_ON_SEED 1 +#define UMAC_SEC_FLG_WSC_ON_MSK (UMAC_SEC_FLG_WSC_ON_SEED << \ + UMAC_SEC_FLG_WSC_ON_POS) + /* Legacy profile can use only WEP40 and WEP104 for encryption and * OPEN or PSK for authentication */ @@ -382,10 +441,34 @@ struct iwm_umac_tx_key_id { u8 reserved[3]; } __attribute__ ((packed)); +struct iwm_umac_pwr_trigger { + struct iwm_umac_wifi_if hdr; + __le32 reseved; +} __attribute__ ((packed)); + struct iwm_umac_cmd_stats_req { __le32 flags; } __attribute__ ((packed)); +struct iwm_umac_cmd_stop_resume_tx { + u8 flags; + u8 sta_id; + __le16 stop_resume_tid_msk; + __le16 last_seq_num[IWM_UMAC_TID_NR]; + u16 reserved; +} __attribute__ ((packed)); + +#define IWM_CMD_PMKID_ADD 1 +#define IWM_CMD_PMKID_DEL 2 +#define IWM_CMD_PMKID_FLUSH 3 + +struct iwm_umac_pmkid_update { + __le32 command; + u8 bssid[ETH_ALEN]; + __le16 reserved; + u8 pmkid[WLAN_PMKID_LEN]; +} __attribute__ ((packed)); + /* LMAC commands */ int iwm_read_mac(struct iwm_priv *iwm, u8 *mac); int iwm_send_prio_table(struct iwm_priv *iwm); @@ -393,6 +476,7 @@ int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested); int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested); int iwm_send_calib_results(struct iwm_priv *iwm); int iwm_store_rxiq_calib_result(struct iwm_priv *iwm); +int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit); /* UMAC commands */ int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, @@ -407,11 +491,16 @@ int iwm_invalidate_mlme_profile(struct iwm_priv *iwm); int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id); int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx); int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key); +int iwm_tx_power_trigger(struct iwm_priv *iwm); int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags); int iwm_send_umac_channel_list(struct iwm_priv *iwm); int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, int ssid_num); int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len); +int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, + struct iwm_umac_notif_stop_resume_tx *ntf); +int iwm_send_pmkid_update(struct iwm_priv *iwm, + struct cfg80211_pmksa *pmksa, u32 command); /* UDMA commands */ int iwm_target_reset(struct iwm_priv *iwm); diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c index 1465379f900a..be992ca41cf1 100644 --- a/drivers/net/wireless/iwmc3200wifi/debugfs.c +++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c @@ -158,6 +158,29 @@ static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer, } spin_unlock_irqrestore(&txq->queue.lock, flags); + + spin_lock_irqsave(&txq->stopped_queue.lock, flags); + + len += snprintf(buf + len, buf_len - len, + "\tStopped Queue len: %d\n", + skb_queue_len(&txq->stopped_queue)); + for (j = 0; j < skb_queue_len(&txq->stopped_queue); j++) { + struct iwm_tx_info *tx_info; + + skb = skb->next; + tx_info = skb_to_tx_info(skb); + + len += snprintf(buf + len, buf_len - len, + "\tSKB #%d\n", j); + len += snprintf(buf + len, buf_len - len, + "\t\tsta: %d\n", tx_info->sta); + len += snprintf(buf + len, buf_len - len, + "\t\tcolor: %d\n", tx_info->color); + len += snprintf(buf + len, buf_len - len, + "\t\ttid: %d\n", tx_info->tid); + } + + spin_unlock_irqrestore(&txq->stopped_queue.lock, flags); } ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.c b/drivers/net/wireless/iwmc3200wifi/eeprom.c index 365910fbe01e..8091421ee5e5 100644 --- a/drivers/net/wireless/iwmc3200wifi/eeprom.c +++ b/drivers/net/wireless/iwmc3200wifi/eeprom.c @@ -66,6 +66,10 @@ static struct iwm_eeprom_entry eeprom_map[] = { [IWM_EEPROM_SKU_CAP] = {"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN}, + [IWM_EEPROM_FAT_CHANNELS_CAP] = + {"HT channels capabilities", IWM_EEPROM_FAT_CHANNELS_CAP_OFF, + IWM_EEPROM_FAT_CHANNELS_CAP_LEN}, + [IWM_EEPROM_CALIB_RXIQ_OFFSET] = {"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN}, @@ -146,6 +150,52 @@ u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id) return iwm->eeprom + eeprom_map[eeprom_id].offset; } +int iwm_eeprom_fat_channels(struct iwm_priv *iwm) +{ + struct wiphy *wiphy = iwm_to_wiphy(iwm); + struct ieee80211_supported_band *band; + u16 *channels, i; + + channels = (u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_FAT_CHANNELS_CAP); + if (IS_ERR(channels)) + return PTR_ERR(channels); + + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + band->ht_cap.ht_supported = true; + + for (i = 0; i < IWM_EEPROM_FAT_CHANNELS_24; i++) + if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED)) + band->ht_cap.ht_supported = false; + + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + band->ht_cap.ht_supported = true; + for (i = IWM_EEPROM_FAT_CHANNELS_24; i < IWM_EEPROM_FAT_CHANNELS; i++) + if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED)) + band->ht_cap.ht_supported = false; + + return 0; +} + +u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm) +{ + u16 sku_cap; + u32 wireless_mode = 0; + + sku_cap = *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP)); + + if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_24GHZ) + wireless_mode |= WIRELESS_MODE_11G; + + if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_52GHZ) + wireless_mode |= WIRELESS_MODE_11A; + + if (sku_cap & IWM_EEPROM_SKU_CAP_11N_ENABLE) + wireless_mode |= WIRELESS_MODE_11N; + + return wireless_mode; +} + + int iwm_eeprom_init(struct iwm_priv *iwm) { int i, ret = 0; diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.h b/drivers/net/wireless/iwmc3200wifi/eeprom.h index cdb31a6a1f5f..4e3a3fdab0d3 100644 --- a/drivers/net/wireless/iwmc3200wifi/eeprom.h +++ b/drivers/net/wireless/iwmc3200wifi/eeprom.h @@ -48,6 +48,7 @@ enum { IWM_EEPROM_CARD_ID, IWM_EEPROM_RADIO_CONF, IWM_EEPROM_SKU_CAP, + IWM_EEPROM_FAT_CHANNELS_CAP, IWM_EEPROM_INDIRECT_OFFSET, IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET, @@ -58,14 +59,15 @@ enum { IWM_EEPROM_LAST, }; -#define IWM_EEPROM_SIG_OFF 0x00 -#define IWM_EEPROM_VERSION_OFF (0x54 << 1) -#define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1) -#define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1) -#define IWM_EEPROM_CARD_ID_OFF (0x5d << 1) -#define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1) -#define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1) -#define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1) +#define IWM_EEPROM_SIG_OFF 0x00 +#define IWM_EEPROM_VERSION_OFF (0x54 << 1) +#define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1) +#define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1) +#define IWM_EEPROM_CARD_ID_OFF (0x5d << 1) +#define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1) +#define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1) +#define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1) +#define IWM_EEPROM_FAT_CHANNELS_CAP_OFF (0xde << 1) #define IWM_EEPROM_SIG_LEN 4 #define IWM_EEPROM_VERSION_LEN 2 @@ -74,6 +76,7 @@ enum { #define IWM_EEPROM_CARD_ID_LEN 2 #define IWM_EEPROM_RADIO_CONF_LEN 2 #define IWM_EEPROM_SKU_CAP_LEN 2 +#define IWM_EEPROM_FAT_CHANNELS_CAP_LEN 40 #define IWM_EEPROM_INDIRECT_LEN 2 #define IWM_MAX_EEPROM_DATA_LEN 240 @@ -87,6 +90,14 @@ enum { #define IWM_EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) #define IWM_EEPROM_SKU_CAP_11N_ENABLE (1 << 6) +#define IWM_EEPROM_FAT_CHANNELS 20 +/* 2.4 gHz FAT primary channels: 1, 2, 3, 4, 5, 6, 7, 8, 9 */ +#define IWM_EEPROM_FAT_CHANNELS_24 9 +/* 5.2 gHz FAT primary channels: 36,44,52,60,100,108,116,124,132,149,157 */ +#define IWM_EEPROM_FAT_CHANNELS_52 11 + +#define IWM_EEPROM_FAT_CHANNEL_ENABLED (1 << 0) + enum { IWM_EEPROM_CALIB_CAL_HDR, IWM_EEPROM_CALIB_TX_POWER, @@ -110,5 +121,7 @@ struct iwm_eeprom_entry { int iwm_eeprom_init(struct iwm_priv *iwm); void iwm_eeprom_exit(struct iwm_priv *iwm); u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id); +int iwm_eeprom_fat_channels(struct iwm_priv *iwm); +u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm); #endif diff --git a/drivers/net/wireless/iwmc3200wifi/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c index 6b0bcad758ca..49067092d336 100644 --- a/drivers/net/wireless/iwmc3200wifi/fw.c +++ b/drivers/net/wireless/iwmc3200wifi/fw.c @@ -217,6 +217,13 @@ static int iwm_load_img(struct iwm_priv *iwm, const char *img_name) IWM_BUILD_YEAR(build_date), IWM_BUILD_MONTH(build_date), IWM_BUILD_DAY(build_date)); + if (!strcmp(img_name, iwm->bus_ops->umac_name)) + sprintf(iwm->umac_version, "%02X.%02X", + ver->major, ver->minor); + + if (!strcmp(img_name, iwm->bus_ops->lmac_name)) + sprintf(iwm->lmac_version, "%02X.%02X", + ver->major, ver->minor); err_release_fw: release_firmware(fw); @@ -398,6 +405,8 @@ int iwm_load_fw(struct iwm_priv *iwm) iwm_send_prio_table(iwm); iwm_send_calib_results(iwm); iwm_send_periodic_calib_cfg(iwm, periodic_calib_map); + iwm_send_ct_kill_cfg(iwm, iwm->conf.ct_kill_entry, + iwm->conf.ct_kill_exit); return 0; diff --git a/drivers/net/wireless/iwmc3200wifi/hal.c b/drivers/net/wireless/iwmc3200wifi/hal.c index c430418248b4..d13c8853ee82 100644 --- a/drivers/net/wireless/iwmc3200wifi/hal.c +++ b/drivers/net/wireless/iwmc3200wifi/hal.c @@ -411,7 +411,7 @@ static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr, /* * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC. * Sending command to the LMAC is equivalent to sending a - * regular UMAC command with the LMAC passtrough or the LMAC + * regular UMAC command with the LMAC passthrough or the LMAC * wrapper UMAC command IDs. */ int iwm_hal_send_host_cmd(struct iwm_priv *iwm, diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 1b02a4e2a1ac..5a26bb05a33a 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h @@ -65,6 +65,8 @@ struct iwm_conf { u32 sdio_ior_timeout; unsigned long calib_map; unsigned long expected_calib_map; + u8 ct_kill_entry; + u8 ct_kill_exit; bool reset_on_fatal_err; bool auto_connect; bool wimax_not_present; @@ -79,7 +81,6 @@ struct iwm_conf { u32 assoc_timeout; u32 roam_timeout; u32 wireless_mode; - u32 coexist_mode; u8 ibss_band; u8 ibss_channel; @@ -129,11 +130,18 @@ struct iwm_notif { unsigned long buf_size; }; +struct iwm_tid_info { + __le16 last_seq_num; + bool stopped; + struct mutex mutex; +}; + struct iwm_sta_info { u8 addr[ETH_ALEN]; bool valid; bool qos; u8 color; + struct iwm_tid_info tid_info[IWM_UMAC_TID_NR]; }; struct iwm_tx_info { @@ -183,6 +191,8 @@ struct iwm_key { struct iwm_tx_queue { int id; struct sk_buff_head queue; + struct sk_buff_head stopped_queue; + spinlock_t lock; struct workqueue_struct *wq; struct work_struct worker; u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE]; @@ -276,12 +286,14 @@ struct iwm_priv { struct iw_statistics wstats; struct delayed_work stats_request; struct delayed_work disconnect; + struct delayed_work ct_kill_delay; struct iwm_debugfs dbg; u8 *eeprom; struct timer_list watchdog; struct work_struct reset_worker; + struct work_struct auth_retry_worker; struct mutex mutex; u8 *req_ie; @@ -290,6 +302,8 @@ struct iwm_priv { int resp_ie_len; struct iwm_fw_error_hdr *last_fw_err; + char umac_version[8]; + char lmac_version[8]; char private[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; @@ -335,6 +349,7 @@ int iwm_up(struct iwm_priv *iwm); int iwm_down(struct iwm_priv *iwm); /* TX API */ +u16 iwm_tid_to_queue(u16 tid); void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages); void iwm_tx_worker(struct work_struct *work); int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev); diff --git a/drivers/net/wireless/iwmc3200wifi/lmac.h b/drivers/net/wireless/iwmc3200wifi/lmac.h index 6c1a14c4480f..a3a79b5e2898 100644 --- a/drivers/net/wireless/iwmc3200wifi/lmac.h +++ b/drivers/net/wireless/iwmc3200wifi/lmac.h @@ -187,6 +187,14 @@ struct iwm_coex_prio_table_cmd { COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK) +/* CT kill config command */ +struct iwm_ct_kill_cfg_cmd { + u32 exit_threshold; + u32 reserved; + u32 entry_threshold; +} __attribute__ ((packed)); + + /* LMAC OP CODES */ #define REPLY_PAD 0x0 #define REPLY_ALIVE 0x1 diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index 222eb2cf1b30..7f34d6dd3c41 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c @@ -64,9 +64,10 @@ static struct iwm_conf def_iwm_conf = { BIT(PHY_CALIBRATE_TX_IQ_CMD) | BIT(PHY_CALIBRATE_RX_IQ_CMD) | BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD), + .ct_kill_entry = 110, + .ct_kill_exit = 110, .reset_on_fatal_err = 1, .auto_connect = 1, - .wimax_not_present = 0, .enable_qos = 1, .mode = UMAC_MODE_BSS, @@ -78,8 +79,8 @@ static struct iwm_conf def_iwm_conf = { .assoc_timeout = 2, .roam_timeout = 10, - .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G, - .coexist_mode = COEX_MODE_CM, + .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G | + WIRELESS_MODE_11N, /* IBSS */ .ibss_band = UMAC_BAND_2GHZ, @@ -92,6 +93,10 @@ static int modparam_reset; module_param_named(reset, modparam_reset, bool, 0644); MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])"); +static int modparam_wimax_enable = 1; +module_param_named(wimax_enable, modparam_wimax_enable, bool, 0644); +MODULE_PARM_DESC(wimax_enable, "Enable wimax core (default 1 [wimax enabled])"); + int iwm_mode_to_nl80211_iftype(int mode) { switch (mode) { @@ -134,6 +139,17 @@ static void iwm_disconnect_work(struct work_struct *work) cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, GFP_KERNEL); } +static void iwm_ct_kill_work(struct work_struct *work) +{ + struct iwm_priv *iwm = + container_of(work, struct iwm_priv, ct_kill_delay.work); + struct wiphy *wiphy = iwm_to_wiphy(iwm); + + IWM_INFO(iwm, "CT kill delay timeout\n"); + + wiphy_rfkill_set_hw_state(wiphy, false); +} + static int __iwm_up(struct iwm_priv *iwm); static int __iwm_down(struct iwm_priv *iwm); @@ -195,6 +211,33 @@ static void iwm_reset_worker(struct work_struct *work) mutex_unlock(&iwm->mutex); } +static void iwm_auth_retry_worker(struct work_struct *work) +{ + struct iwm_priv *iwm; + int i, ret; + + iwm = container_of(work, struct iwm_priv, auth_retry_worker); + if (iwm->umac_profile_active) { + ret = iwm_invalidate_mlme_profile(iwm); + if (ret < 0) + return; + } + + iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; + + ret = iwm_send_mlme_profile(iwm); + if (ret < 0) + return; + + for (i = 0; i < IWM_NUM_KEYS; i++) + if (iwm->keys[i].key_len) + iwm_set_key(iwm, 0, &iwm->keys[i]); + + iwm_set_tx_key(iwm, iwm->default_key); +} + + + static void iwm_watchdog(unsigned long data) { struct iwm_priv *iwm = (struct iwm_priv *)data; @@ -207,7 +250,7 @@ static void iwm_watchdog(unsigned long data) int iwm_priv_init(struct iwm_priv *iwm) { - int i; + int i, j; char name[32]; iwm->status = 0; @@ -226,7 +269,9 @@ int iwm_priv_init(struct iwm_priv *iwm) iwm->scan_id = 1; INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request); INIT_DELAYED_WORK(&iwm->disconnect, iwm_disconnect_work); + INIT_DELAYED_WORK(&iwm->ct_kill_delay, iwm_ct_kill_work); INIT_WORK(&iwm->reset_worker, iwm_reset_worker); + INIT_WORK(&iwm->auth_retry_worker, iwm_auth_retry_worker); INIT_LIST_HEAD(&iwm->bss_list); skb_queue_head_init(&iwm->rx_list); @@ -249,6 +294,8 @@ int iwm_priv_init(struct iwm_priv *iwm) return -EAGAIN; skb_queue_head_init(&iwm->txq[i].queue); + skb_queue_head_init(&iwm->txq[i].stopped_queue); + spin_lock_init(&iwm->txq[i].lock); } for (i = 0; i < IWM_NUM_KEYS; i++) @@ -256,6 +303,12 @@ int iwm_priv_init(struct iwm_priv *iwm) iwm->default_key = -1; + for (i = 0; i < IWM_STA_TABLE_NUM; i++) + for (j = 0; j < IWM_UMAC_TID_NR; j++) { + mutex_init(&iwm->sta_table[i].tid_info[j].mutex); + iwm->sta_table[i].tid_info[j].stopped = false; + } + init_timer(&iwm->watchdog); iwm->watchdog.function = iwm_watchdog; iwm->watchdog.data = (unsigned long)iwm; @@ -436,7 +489,7 @@ static int iwm_config_boot_params(struct iwm_priv *iwm) int ret; /* check Wimax is off and config debug monitor */ - if (iwm->conf.wimax_not_present) { + if (!modparam_wimax_enable) { u32 data1 = 0x1f; u32 addr1 = 0x606BE258; @@ -529,6 +582,7 @@ void iwm_link_off(struct iwm_priv *iwm) for (i = 0; i < IWM_TX_QUEUES; i++) { skb_queue_purge(&iwm->txq[i].queue); + skb_queue_purge(&iwm->txq[i].stopped_queue); iwm->txq[i].concat_count = 0; iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf; @@ -587,6 +641,8 @@ static int __iwm_up(struct iwm_priv *iwm) { int ret; struct iwm_notif *notif_reboot, *notif_ack = NULL; + struct wiphy *wiphy = iwm_to_wiphy(iwm); + u32 wireless_mode; ret = iwm_bus_enable(iwm); if (ret) { @@ -638,6 +694,8 @@ static int __iwm_up(struct iwm_priv *iwm) IWM_ERR(iwm, "MAC reading failed\n"); goto err_disable; } + memcpy(iwm_to_ndev(iwm)->perm_addr, iwm_to_ndev(iwm)->dev_addr, + ETH_ALEN); /* We can load the FWs */ ret = iwm_load_fw(iwm); @@ -646,6 +704,30 @@ static int __iwm_up(struct iwm_priv *iwm) goto err_disable; } + ret = iwm_eeprom_fat_channels(iwm); + if (ret) { + IWM_ERR(iwm, "Couldnt read HT channels EEPROM entries\n"); + goto err_fw; + } + + /* + * Read our SKU capabilities. + * If it's valid, we AND the configured wireless mode with the + * device EEPROM value as the current profile wireless mode. + */ + wireless_mode = iwm_eeprom_wireless_mode(iwm); + if (wireless_mode) { + iwm->conf.wireless_mode &= wireless_mode; + if (iwm->umac_profile) + iwm->umac_profile->wireless_mode = + iwm->conf.wireless_mode; + } else + IWM_ERR(iwm, "Wrong SKU capabilities: 0x%x\n", + *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP))); + + snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "L%s_U%s", + iwm->lmac_version, iwm->umac_version); + /* We configure the UMAC and enable the wifi module */ ret = iwm_send_umac_config(iwm, cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) | diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c index 35ec006c2d2c..e4f0f8705f65 100644 --- a/drivers/net/wireless/iwmc3200wifi/netdev.c +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c @@ -76,6 +76,14 @@ static int iwm_stop(struct net_device *ndev) */ static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; +u16 iwm_tid_to_queue(u16 tid) +{ + if (tid > IWM_UMAC_TID_NR - 2) + return -EINVAL; + + return iwm_1d_to_queue[tid]; +} + static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb) { skb->priority = cfg80211_classify8021d(skb); @@ -152,6 +160,7 @@ void iwm_if_free(struct iwm_priv *iwm) if (!iwm_to_ndev(iwm)) return; + cancel_delayed_work_sync(&iwm->ct_kill_delay); free_netdev(iwm_to_ndev(iwm)); iwm_priv_deinit(iwm); kfree(iwm->umac_profile); diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index 771a301003c9..1c57c1f72cba 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -423,7 +423,9 @@ static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf, if (IS_ERR(ticket_node)) return PTR_ERR(ticket_node); - IWM_DBG_RX(iwm, DBG, "TICKET RELEASE(%d)\n", + IWM_DBG_RX(iwm, DBG, "TICKET %s(%d)\n", + ticket->action == IWM_RX_TICKET_RELEASE ? + "RELEASE" : "DROP", ticket->id); list_add_tail(&ticket_node->node, &iwm->rx_tickets); @@ -500,6 +502,18 @@ static int iwm_mlme_assoc_start(struct iwm_priv *iwm, u8 *buf, return 0; } +static u8 iwm_is_open_wep_profile(struct iwm_priv *iwm) +{ + if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 || + iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) && + (iwm->umac_profile->sec.ucast_cipher == + iwm->umac_profile->sec.mcast_cipher) && + (iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN)) + return 1; + + return 0; +} + static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, struct iwm_wifi_cmd *cmd) @@ -565,11 +579,17 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, goto ibss; if (!test_bit(IWM_STATUS_RESETTING, &iwm->status)) - cfg80211_connect_result(iwm_to_ndev(iwm), - complete->bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + if (!iwm_is_open_wep_profile(iwm)) { + cfg80211_connect_result(iwm_to_ndev(iwm), + complete->bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } else { + /* Let's try shared WEP auth */ + IWM_ERR(iwm, "Trying WEP shared auth\n"); + schedule_work(&iwm->auth_retry_worker); + } else cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, GFP_KERNEL); @@ -713,6 +733,19 @@ static int iwm_mlme_update_sta_table(struct iwm_priv *iwm, u8 *buf, return 0; } +static int iwm_mlme_medium_lost(struct iwm_priv *iwm, u8 *buf, + unsigned long buf_size, + struct iwm_wifi_cmd *cmd) +{ + struct wiphy *wiphy = iwm_to_wiphy(iwm); + + IWM_DBG_NTF(iwm, DBG, "WiFi/WiMax coexistence radio is OFF\n"); + + wiphy_rfkill_set_hw_state(wiphy, true); + + return 0; +} + static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, struct iwm_wifi_cmd *cmd) @@ -899,6 +932,8 @@ static int iwm_ntf_mlme(struct iwm_priv *iwm, u8 *buf, case WIFI_IF_NTFY_EXTENDED_IE_REQUIRED: IWM_DBG_MLME(iwm, DBG, "Extended IE required\n"); break; + case WIFI_IF_NTFY_RADIO_PREEMPTION: + return iwm_mlme_medium_lost(iwm, buf, buf_size, cmd); case WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED: return iwm_mlme_update_bss_table(iwm, buf, buf_size, cmd); case WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED: @@ -1052,12 +1087,83 @@ static int iwm_ntf_channel_info_list(struct iwm_priv *iwm, u8 *buf, return 0; } +static int iwm_ntf_stop_resume_tx(struct iwm_priv *iwm, u8 *buf, + unsigned long buf_size, + struct iwm_wifi_cmd *cmd) +{ + struct iwm_umac_notif_stop_resume_tx *stp_res_tx = + (struct iwm_umac_notif_stop_resume_tx *)buf; + struct iwm_sta_info *sta_info; + struct iwm_tid_info *tid_info; + u8 sta_id = STA_ID_N_COLOR_ID(stp_res_tx->sta_id); + u16 tid_msk = le16_to_cpu(stp_res_tx->stop_resume_tid_msk); + int bit, ret = 0; + bool stop = false; + + IWM_DBG_NTF(iwm, DBG, "stop/resume notification:\n" + "\tflags: 0x%x\n" + "\tSTA id: %d\n" + "\tTID bitmask: 0x%x\n", + stp_res_tx->flags, stp_res_tx->sta_id, + stp_res_tx->stop_resume_tid_msk); + + if (stp_res_tx->flags & UMAC_STOP_TX_FLAG) + stop = true; + + sta_info = &iwm->sta_table[sta_id]; + if (!sta_info->valid) { + IWM_ERR(iwm, "Stoping an invalid STA: %d %d\n", + sta_id, stp_res_tx->sta_id); + return -EINVAL; + } + + for_each_bit(bit, (unsigned long *)&tid_msk, IWM_UMAC_TID_NR) { + tid_info = &sta_info->tid_info[bit]; + + mutex_lock(&tid_info->mutex); + tid_info->stopped = stop; + mutex_unlock(&tid_info->mutex); + + if (!stop) { + struct iwm_tx_queue *txq; + u16 queue = iwm_tid_to_queue(bit); + + if (queue < 0) + continue; + + txq = &iwm->txq[queue]; + /* + * If we resume, we have to move our SKBs + * back to the tx queue and queue some work. + */ + spin_lock_bh(&txq->lock); + skb_queue_splice_init(&txq->queue, &txq->stopped_queue); + spin_unlock_bh(&txq->lock); + + queue_work(txq->wq, &txq->worker); + } + + } + + /* We send an ACK only for the stop case */ + if (stop) + ret = iwm_send_umac_stop_resume_tx(iwm, stp_res_tx); + + return ret; +} + static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, struct iwm_wifi_cmd *cmd) { - struct iwm_umac_wifi_if *hdr = - (struct iwm_umac_wifi_if *)cmd->buf.payload; + struct iwm_umac_wifi_if *hdr; + + if (cmd == NULL) { + IWM_ERR(iwm, "Couldn't find expected wifi command\n"); + return -EINVAL; + } + + hdr = (struct iwm_umac_wifi_if *)cmd->buf.payload; IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: " "oid is 0x%x\n", hdr->oid); @@ -1079,6 +1185,7 @@ static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, return 0; } +#define CT_KILL_DELAY (30 * HZ) static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, struct iwm_wifi_cmd *cmd) { @@ -1091,7 +1198,20 @@ static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf, flags & IWM_CARD_STATE_HW_DISABLED ? "ON" : "OFF", flags & IWM_CARD_STATE_CTKILL_DISABLED ? "ON" : "OFF"); - wiphy_rfkill_set_hw_state(wiphy, flags & IWM_CARD_STATE_HW_DISABLED); + if (flags & IWM_CARD_STATE_CTKILL_DISABLED) { + /* + * We got a CTKILL event: We bring the interface down in + * oder to cool the device down, and try to bring it up + * 30 seconds later. If it's still too hot, we'll go through + * this code path again. + */ + cancel_delayed_work_sync(&iwm->ct_kill_delay); + schedule_delayed_work(&iwm->ct_kill_delay, CT_KILL_DELAY); + } + + wiphy_rfkill_set_hw_state(wiphy, flags & + (IWM_CARD_STATE_HW_DISABLED | + IWM_CARD_STATE_CTKILL_DISABLED)); return 0; } @@ -1282,6 +1402,14 @@ int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size) switch (le32_to_cpu(hdr->cmd)) { case UMAC_REBOOT_BARKER: + if (test_bit(IWM_STATUS_READY, &iwm->status)) { + IWM_ERR(iwm, "Unexpected BARKER\n"); + + schedule_work(&iwm->reset_worker); + + return 0; + } + return iwm_notif_send(iwm, NULL, IWM_BARKER_REBOOT_NOTIFICATION, IWM_SRC_UDMA, buf, buf_size); case UMAC_ACK_BARKER: @@ -1308,6 +1436,7 @@ static const iwm_handler iwm_umac_handlers[] = [UMAC_NOTIFY_OPCODE_STATS] = iwm_ntf_statistics, [UMAC_CMD_OPCODE_EEPROM_PROXY] = iwm_ntf_eeprom_proxy, [UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST] = iwm_ntf_channel_info_list, + [UMAC_CMD_OPCODE_STOP_RESUME_STA_TX] = iwm_ntf_stop_resume_tx, [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet, [UMAC_CMD_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_wifi_if_wrapper, }; @@ -1444,11 +1573,12 @@ static void iwm_rx_process_packet(struct iwm_priv *iwm, } break; case IWM_RX_TICKET_DROP: - IWM_DBG_RX(iwm, DBG, "DROP packet\n"); + IWM_DBG_RX(iwm, DBG, "DROP packet: 0x%x\n", + le16_to_cpu(ticket_node->ticket->flags)); kfree_skb(packet->skb); break; default: - IWM_ERR(iwm, "Unknow ticket action: %d\n", + IWM_ERR(iwm, "Unknown ticket action: %d\n", le16_to_cpu(ticket_node->ticket->action)); kfree_skb(packet->skb); } diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c index 8b1de84003ca..a7ec7eac9137 100644 --- a/drivers/net/wireless/iwmc3200wifi/sdio.c +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c @@ -224,8 +224,6 @@ static int if_sdio_disable(struct iwm_priv *iwm) struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); int ret; - iwm_reset(iwm); - sdio_claim_host(hw->func); sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret); if (ret < 0) @@ -237,6 +235,8 @@ static int if_sdio_disable(struct iwm_priv *iwm) iwm_sdio_rx_free(hw); + iwm_reset(iwm); + IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n"); return 0; @@ -399,6 +399,9 @@ static struct iwm_if_ops if_sdio_ops = { .calib_lmac_name = "iwmc3200wifi-calib-sdio.bin", .lmac_name = "iwmc3200wifi-lmac-sdio.bin", }; +MODULE_FIRMWARE("iwmc3200wifi-umac-sdio.bin"); +MODULE_FIRMWARE("iwmc3200wifi-calib-sdio.bin"); +MODULE_FIRMWARE("iwmc3200wifi-lmac-sdio.bin"); static int iwm_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) @@ -493,8 +496,10 @@ static void iwm_sdio_remove(struct sdio_func *func) } static const struct sdio_device_id iwm_sdio_ids[] = { - { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, - SDIO_DEVICE_ID_INTEL_IWMC3200WIFI) }, + /* Global/AGN SKU */ + { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1403) }, + /* BGN SKU */ + { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1408) }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids); diff --git a/drivers/net/wireless/iwmc3200wifi/tx.c b/drivers/net/wireless/iwmc3200wifi/tx.c index e3b4f7902daf..55905f02309c 100644 --- a/drivers/net/wireless/iwmc3200wifi/tx.c +++ b/drivers/net/wireless/iwmc3200wifi/tx.c @@ -329,7 +329,7 @@ static int iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb, memcpy(buf + sizeof(*hdr), skb->data, skb->len); - return 0; + return umac_cmd.seq_num; } static int iwm_tx_send_concat_packets(struct iwm_priv *iwm, @@ -354,16 +354,15 @@ static int iwm_tx_send_concat_packets(struct iwm_priv *iwm, return ret; } -#define CONFIG_IWM_TX_CONCATENATED 1 - void iwm_tx_worker(struct work_struct *work) { struct iwm_priv *iwm; struct iwm_tx_info *tx_info = NULL; struct sk_buff *skb; - int cmdlen, ret; struct iwm_tx_queue *txq; - int pool_id; + struct iwm_sta_info *sta_info; + struct iwm_tid_info *tid_info; + int cmdlen, ret, pool_id; txq = container_of(work, struct iwm_tx_queue, worker); iwm = container_of(txq, struct iwm_priv, txq[txq->id]); @@ -373,19 +372,46 @@ void iwm_tx_worker(struct work_struct *work) while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) && !skb_queue_empty(&txq->queue)) { + spin_lock_bh(&txq->lock); skb = skb_dequeue(&txq->queue); + spin_unlock_bh(&txq->lock); + tx_info = skb_to_tx_info(skb); + sta_info = &iwm->sta_table[tx_info->sta]; + if (!sta_info->valid) { + IWM_ERR(iwm, "Trying to send a frame to unknown STA\n"); + kfree_skb(skb); + continue; + } + + tid_info = &sta_info->tid_info[tx_info->tid]; + + mutex_lock(&tid_info->mutex); + + /* + * If the RAxTID is stopped, we queue the skb to the stopped + * queue. + * Whenever we'll get a UMAC notification to resume the tx flow + * for this RAxTID, we'll merge back the stopped queue into the + * regular queue. See iwm_ntf_stop_resume_tx() from rx.c. + */ + if (tid_info->stopped) { + IWM_DBG_TX(iwm, DBG, "%dx%d stopped\n", + tx_info->sta, tx_info->tid); + spin_lock_bh(&txq->lock); + skb_queue_tail(&txq->stopped_queue, skb); + spin_unlock_bh(&txq->lock); + + mutex_unlock(&tid_info->mutex); + continue; + } + cmdlen = IWM_UDMA_HDR_LEN + skb->len; IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: " "%d, color: %d\n", txq->id, skb, tx_info->sta, tx_info->color); -#if !CONFIG_IWM_TX_CONCATENATED - /* temporarily keep this to comparing the performance */ - ret = iwm_send_packet(iwm, skb, pool_id); -#else - if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE) iwm_tx_send_concat_packets(iwm, txq); @@ -393,14 +419,21 @@ void iwm_tx_worker(struct work_struct *work) if (ret) { IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue " "%d, Tx worker stopped\n", txq->id); + spin_lock_bh(&txq->lock); skb_queue_head(&txq->queue, skb); + spin_unlock_bh(&txq->lock); + + mutex_unlock(&tid_info->mutex); break; } txq->concat_ptr = txq->concat_buf + txq->concat_count; - iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr); + tid_info->last_seq_num = + iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr); txq->concat_count += ALIGN(cmdlen, 16); -#endif + + mutex_unlock(&tid_info->mutex); + kfree_skb(skb); } @@ -419,14 +452,14 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) struct iwm_priv *iwm = ndev_to_iwm(netdev); struct net_device *ndev = iwm_to_ndev(iwm); struct wireless_dev *wdev = iwm_to_wdev(iwm); - u8 *dst_addr; struct iwm_tx_info *tx_info; struct iwm_tx_queue *txq; struct iwm_sta_info *sta_info; - u8 sta_id; + u8 *dst_addr, sta_id; u16 queue; int ret; + if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: " "not associated\n"); @@ -440,7 +473,8 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) txq = &iwm->txq[queue]; /* No free space for Tx, tx_worker is too slow */ - if (skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) { + if ((skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) || + (skb_queue_len(&txq->stopped_queue) > IWM_TX_LIST_SIZE)) { IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue); netif_stop_subqueue(netdev, queue); return NETDEV_TX_BUSY; @@ -477,7 +511,9 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) else tx_info->tid = IWM_UMAC_MGMT_TID; + spin_lock_bh(&iwm->txq[queue].lock); skb_queue_tail(&iwm->txq[queue].queue, skb); + spin_unlock_bh(&iwm->txq[queue].lock); queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker); diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h index c5a14ae3160a..7f54a145ca65 100644 --- a/drivers/net/wireless/iwmc3200wifi/umac.h +++ b/drivers/net/wireless/iwmc3200wifi/umac.h @@ -83,6 +83,20 @@ struct iwm_udma_out_wifi_hdr { ((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\ (UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS)) +/* STA ID and color */ +#define STA_ID_SEED (0x0f) +#define STA_ID_POS (0) +#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS) + +#define STA_COLOR_SEED (0x7) +#define STA_COLOR_POS (4) +#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS) + +#define STA_ID_N_COLOR_COLOR(id_n_color) \ + (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS) +#define STA_ID_N_COLOR_ID(id_n_color) \ + (((id_n_color) & STA_ID_MSK) >> STA_ID_POS) + /* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */ #define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0 #define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF @@ -260,6 +274,9 @@ struct iwm_udma_out_wifi_hdr { #define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16 #define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17 #define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18 +#define UMAC_CMD_OPCODE_STOP_RESUME_STA_TX 0x19 +#define UMAC_CMD_OPCODE_TEST_BLOCK_ACK 0x1A + #define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA #define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB #define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC @@ -281,6 +298,7 @@ struct iwm_udma_out_wifi_hdr { #define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B #define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C #define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E +#define UMAC_WIFI_IF_CMD_PMKID_UPDATE 0x1F #define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20 /* UMAC WiFi interface ports */ @@ -687,19 +705,24 @@ struct iwm_umac_notif_rx_ticket { /* Tx/Rx rates window (number of max of last update window per second) */ #define UMAC_NTF_RATE_SAMPLE_NR 4 +/* Max numbers of bits required to go through all antennae in bitmasks */ +#define UMAC_PHY_NUM_CHAINS 3 + #define IWM_UMAC_MGMT_TID 8 -#define IWM_UMAC_TID_NR 8 +#define IWM_UMAC_TID_NR 9 /* 8 TIDs + MGMT */ struct iwm_umac_notif_stats { struct iwm_umac_wifi_in_hdr hdr; __le32 flags; __le32 timestamp; - __le16 tid_load[IWM_UMAC_TID_NR + 2]; /* 1 non-QoS + 1 dword align */ + __le16 tid_load[IWM_UMAC_TID_NR + 1]; /* 1 non-QoS + 1 dword align */ __le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR]; __le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR]; + __le32 chain_energy[UMAC_PHY_NUM_CHAINS]; s32 rssi_dbm; s32 noise_dbm; __le32 supp_rates; + __le32 supp_ht_rates; __le32 missed_beacons; __le32 rx_beacons; __le32 rx_dir_pkts; @@ -737,6 +760,20 @@ struct iwm_umac_notif_stats { __le32 roam_ap_loadblance; } __attribute__ ((packed)); +#define UMAC_STOP_TX_FLAG 0x1 +#define UMAC_RESUME_TX_FLAG 0x2 + +#define LAST_SEQ_NUM_INVALID 0xFFFF + +struct iwm_umac_notif_stop_resume_tx { + struct iwm_umac_wifi_in_hdr hdr; + u8 flags; /* UMAC_*_TX_FLAG_* */ + u8 sta_id; + __le16 stop_resume_tid_msk; /* tid bitmask */ +} __attribute__ ((packed)); + +#define UMAC_MAX_NUM_PMKIDS 4 + /* WiFi interface wrapper header */ struct iwm_umac_wifi_if { u8 oid; diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c deleted file mode 100644 index 5c6968101f0d..000000000000 --- a/drivers/net/wireless/libertas/11d.c +++ /dev/null @@ -1,696 +0,0 @@ -/** - * This file contains functions for 802.11D. - */ -#include <linux/ctype.h> -#include <linux/kernel.h> -#include <linux/wireless.h> - -#include "host.h" -#include "decl.h" -#include "11d.h" -#include "dev.h" -#include "wext.h" - -#define TX_PWR_DEFAULT 10 - -static struct region_code_mapping region_code_mapping[] = { - {"US ", 0x10}, /* US FCC */ - {"CA ", 0x10}, /* IC Canada */ - {"SG ", 0x10}, /* Singapore */ - {"EU ", 0x30}, /* ETSI */ - {"AU ", 0x30}, /* Australia */ - {"KR ", 0x30}, /* Republic Of Korea */ - {"ES ", 0x31}, /* Spain */ - {"FR ", 0x32}, /* France */ - {"JP ", 0x40}, /* Japan */ -}; - -/* Following 2 structure defines the supported channels */ -static struct chan_freq_power channel_freq_power_UN_BG[] = { - {1, 2412, TX_PWR_DEFAULT}, - {2, 2417, TX_PWR_DEFAULT}, - {3, 2422, TX_PWR_DEFAULT}, - {4, 2427, TX_PWR_DEFAULT}, - {5, 2432, TX_PWR_DEFAULT}, - {6, 2437, TX_PWR_DEFAULT}, - {7, 2442, TX_PWR_DEFAULT}, - {8, 2447, TX_PWR_DEFAULT}, - {9, 2452, TX_PWR_DEFAULT}, - {10, 2457, TX_PWR_DEFAULT}, - {11, 2462, TX_PWR_DEFAULT}, - {12, 2467, TX_PWR_DEFAULT}, - {13, 2472, TX_PWR_DEFAULT}, - {14, 2484, TX_PWR_DEFAULT} -}; - -static u8 lbs_region_2_code(u8 *region) -{ - u8 i; - - for (i = 0; i < COUNTRY_CODE_LEN && region[i]; i++) - region[i] = toupper(region[i]); - - for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { - if (!memcmp(region, region_code_mapping[i].region, - COUNTRY_CODE_LEN)) - return (region_code_mapping[i].code); - } - - /* default is US */ - return (region_code_mapping[0].code); -} - -static u8 *lbs_code_2_region(u8 code) -{ - u8 i; - - for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { - if (region_code_mapping[i].code == code) - return (region_code_mapping[i].region); - } - /* default is US */ - return (region_code_mapping[0].region); -} - -/** - * @brief This function finds the nrchan-th chan after the firstchan - * @param band band - * @param firstchan first channel number - * @param nrchan number of channels - * @return the nrchan-th chan number -*/ -static u8 lbs_get_chan_11d(u8 firstchan, u8 nrchan, u8 *chan) -/*find the nrchan-th chan after the firstchan*/ -{ - u8 i; - struct chan_freq_power *cfp; - u8 cfp_no; - - cfp = channel_freq_power_UN_BG; - cfp_no = ARRAY_SIZE(channel_freq_power_UN_BG); - - for (i = 0; i < cfp_no; i++) { - if ((cfp + i)->channel == firstchan) { - lbs_deb_11d("firstchan found\n"); - break; - } - } - - if (i < cfp_no) { - /*if beyond the boundary */ - if (i + nrchan < cfp_no) { - *chan = (cfp + i + nrchan)->channel; - return 1; - } - } - - return 0; -} - -/** - * @brief This function Checks if chan txpwr is learned from AP/IBSS - * @param chan chan number - * @param parsed_region_chan pointer to parsed_region_chan_11d - * @return TRUE; FALSE -*/ -static u8 lbs_channel_known_11d(u8 chan, - struct parsed_region_chan_11d * parsed_region_chan) -{ - struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr; - u8 nr_chan = parsed_region_chan->nr_chan; - u8 i = 0; - - lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)chanpwr, - sizeof(struct chan_power_11d) * nr_chan); - - for (i = 0; i < nr_chan; i++) { - if (chan == chanpwr[i].chan) { - lbs_deb_11d("found chan %d\n", chan); - return 1; - } - } - - lbs_deb_11d("chan %d not found\n", chan); - return 0; -} - -u32 lbs_chan_2_freq(u8 chan) -{ - struct chan_freq_power *cf; - u16 i; - u32 freq = 0; - - cf = channel_freq_power_UN_BG; - - for (i = 0; i < ARRAY_SIZE(channel_freq_power_UN_BG); i++) { - if (chan == cf[i].channel) - freq = cf[i].freq; - } - - return freq; -} - -static int generate_domain_info_11d(struct parsed_region_chan_11d - *parsed_region_chan, - struct lbs_802_11d_domain_reg *domaininfo) -{ - u8 nr_subband = 0; - - u8 nr_chan = parsed_region_chan->nr_chan; - u8 nr_parsedchan = 0; - - u8 firstchan = 0, nextchan = 0, maxpwr = 0; - - u8 i, flag = 0; - - memcpy(domaininfo->countrycode, parsed_region_chan->countrycode, - COUNTRY_CODE_LEN); - - lbs_deb_11d("nrchan %d\n", nr_chan); - lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)parsed_region_chan, - sizeof(struct parsed_region_chan_11d)); - - for (i = 0; i < nr_chan; i++) { - if (!flag) { - flag = 1; - nextchan = firstchan = - parsed_region_chan->chanpwr[i].chan; - maxpwr = parsed_region_chan->chanpwr[i].pwr; - nr_parsedchan = 1; - continue; - } - - if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 && - parsed_region_chan->chanpwr[i].pwr == maxpwr) { - nextchan++; - nr_parsedchan++; - } else { - domaininfo->subband[nr_subband].firstchan = firstchan; - domaininfo->subband[nr_subband].nrchan = - nr_parsedchan; - domaininfo->subband[nr_subband].maxtxpwr = maxpwr; - nr_subband++; - nextchan = firstchan = - parsed_region_chan->chanpwr[i].chan; - maxpwr = parsed_region_chan->chanpwr[i].pwr; - } - } - - if (flag) { - domaininfo->subband[nr_subband].firstchan = firstchan; - domaininfo->subband[nr_subband].nrchan = nr_parsedchan; - domaininfo->subband[nr_subband].maxtxpwr = maxpwr; - nr_subband++; - } - domaininfo->nr_subband = nr_subband; - - lbs_deb_11d("nr_subband=%x\n", domaininfo->nr_subband); - lbs_deb_hex(LBS_DEB_11D, "domaininfo", (char *)domaininfo, - COUNTRY_CODE_LEN + 1 + - sizeof(struct ieee_subbandset) * nr_subband); - return 0; -} - -/** - * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS - * @param region_chan pointer to struct region_channel - * @param *parsed_region_chan pointer to parsed_region_chan_11d - * @return N/A -*/ -static void lbs_generate_parsed_region_chan_11d(struct region_channel *region_chan, - struct parsed_region_chan_11d * - parsed_region_chan) -{ - u8 i; - struct chan_freq_power *cfp; - - if (region_chan == NULL) { - lbs_deb_11d("region_chan is NULL\n"); - return; - } - - cfp = region_chan->CFP; - if (cfp == NULL) { - lbs_deb_11d("cfp is NULL \n"); - return; - } - - parsed_region_chan->band = region_chan->band; - parsed_region_chan->region = region_chan->region; - memcpy(parsed_region_chan->countrycode, - lbs_code_2_region(region_chan->region), COUNTRY_CODE_LEN); - - lbs_deb_11d("region 0x%x, band %d\n", parsed_region_chan->region, - parsed_region_chan->band); - - for (i = 0; i < region_chan->nrcfp; i++, cfp++) { - parsed_region_chan->chanpwr[i].chan = cfp->channel; - parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower; - lbs_deb_11d("chan %d, pwr %d\n", - parsed_region_chan->chanpwr[i].chan, - parsed_region_chan->chanpwr[i].pwr); - } - parsed_region_chan->nr_chan = region_chan->nrcfp; - - lbs_deb_11d("nrchan %d\n", parsed_region_chan->nr_chan); - - return; -} - -/** - * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS - * @param region region ID - * @param band band - * @param chan chan - * @return TRUE;FALSE -*/ -static u8 lbs_region_chan_supported_11d(u8 region, u8 chan) -{ - struct chan_freq_power *cfp; - int cfp_no; - u8 idx; - int ret = 0; - - lbs_deb_enter(LBS_DEB_11D); - - cfp = lbs_get_region_cfp_table(region, &cfp_no); - if (cfp == NULL) - return 0; - - for (idx = 0; idx < cfp_no; idx++) { - if (chan == (cfp + idx)->channel) { - /* If Mrvl Chip Supported? */ - if ((cfp + idx)->unsupported) { - ret = 0; - } else { - ret = 1; - } - goto done; - } - } - - /*chan is not in the region table */ - -done: - lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); - return ret; -} - -/** - * @brief This function checks if chan txpwr is learned from AP/IBSS - * @param chan chan number - * @param parsed_region_chan pointer to parsed_region_chan_11d - * @return 0 -*/ -static int parse_domain_info_11d(struct ieee_ie_country_info_full_set *countryinfo, - u8 band, - struct parsed_region_chan_11d *parsed_region_chan) -{ - u8 nr_subband, nrchan; - u8 lastchan, firstchan; - u8 region; - u8 curchan = 0; - - u8 idx = 0; /*chan index in parsed_region_chan */ - - u8 j, i; - - lbs_deb_enter(LBS_DEB_11D); - - /*validation Rules: - 1. valid region Code - 2. First Chan increment - 3. channel range no overlap - 4. channel is valid? - 5. channel is supported by region? - 6. Others - */ - - lbs_deb_hex(LBS_DEB_11D, "countryinfo", (u8 *) countryinfo, 30); - - if ((*(countryinfo->countrycode)) == 0 - || (countryinfo->header.len <= COUNTRY_CODE_LEN)) { - /* No region Info or Wrong region info: treat as No 11D info */ - goto done; - } - - /*Step1: check region_code */ - parsed_region_chan->region = region = - lbs_region_2_code(countryinfo->countrycode); - - lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region); - lbs_deb_hex(LBS_DEB_11D, "countrycode", (char *)countryinfo->countrycode, - COUNTRY_CODE_LEN); - - parsed_region_chan->band = band; - - memcpy(parsed_region_chan->countrycode, countryinfo->countrycode, - COUNTRY_CODE_LEN); - - nr_subband = (countryinfo->header.len - COUNTRY_CODE_LEN) / - sizeof(struct ieee_subbandset); - - for (j = 0, lastchan = 0; j < nr_subband; j++) { - - if (countryinfo->subband[j].firstchan <= lastchan) { - /*Step2&3. Check First Chan Num increment and no overlap */ - lbs_deb_11d("chan %d>%d, overlap\n", - countryinfo->subband[j].firstchan, lastchan); - continue; - } - - firstchan = countryinfo->subband[j].firstchan; - nrchan = countryinfo->subband[j].nrchan; - - for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) { - /*step4: channel is supported? */ - - if (!lbs_get_chan_11d(firstchan, i, &curchan)) { - /* Chan is not found in UN table */ - lbs_deb_11d("chan is not supported: %d \n", i); - break; - } - - lastchan = curchan; - - if (lbs_region_chan_supported_11d(region, curchan)) { - /*step5: Check if curchan is supported by mrvl in region */ - parsed_region_chan->chanpwr[idx].chan = curchan; - parsed_region_chan->chanpwr[idx].pwr = - countryinfo->subband[j].maxtxpwr; - idx++; - } else { - /*not supported and ignore the chan */ - lbs_deb_11d( - "i %d, chan %d unsupported in region %x, band %d\n", - i, curchan, region, band); - } - } - - /*Step6: Add other checking if any */ - - } - - parsed_region_chan->nr_chan = idx; - - lbs_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan); - lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (u8 *) parsed_region_chan, - 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); - -done: - lbs_deb_enter(LBS_DEB_11D); - return 0; -} - -/** - * @brief This function calculates the scan type for channels - * @param chan chan number - * @param parsed_region_chan pointer to parsed_region_chan_11d - * @return PASSIVE if chan is unknown; ACTIVE if chan is known -*/ -u8 lbs_get_scan_type_11d(u8 chan, - struct parsed_region_chan_11d * parsed_region_chan) -{ - u8 scan_type = CMD_SCAN_TYPE_PASSIVE; - - lbs_deb_enter(LBS_DEB_11D); - - if (lbs_channel_known_11d(chan, parsed_region_chan)) { - lbs_deb_11d("found, do active scan\n"); - scan_type = CMD_SCAN_TYPE_ACTIVE; - } else { - lbs_deb_11d("not found, do passive scan\n"); - } - - lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type); - return scan_type; - -} - -void lbs_init_11d(struct lbs_private *priv) -{ - priv->enable11d = 0; - memset(&(priv->parsed_region_chan), 0, - sizeof(struct parsed_region_chan_11d)); - return; -} - -/** - * @brief This function sets DOMAIN INFO to FW - * @param priv pointer to struct lbs_private - * @return 0; -1 -*/ -static int set_domain_info_11d(struct lbs_private *priv) -{ - int ret; - - if (!priv->enable11d) { - lbs_deb_11d("dnld domain Info with 11d disabled\n"); - return 0; - } - - ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO, - CMD_ACT_SET, - CMD_OPTION_WAITFORRSP, 0, NULL); - if (ret) - lbs_deb_11d("fail to dnld domain info\n"); - - return ret; -} - -/** - * @brief This function setups scan channels - * @param priv pointer to struct lbs_private - * @param band band - * @return 0 -*/ -int lbs_set_universaltable(struct lbs_private *priv, u8 band) -{ - u16 size = sizeof(struct chan_freq_power); - u16 i = 0; - - memset(priv->universal_channel, 0, - sizeof(priv->universal_channel)); - - priv->universal_channel[i].nrcfp = - sizeof(channel_freq_power_UN_BG) / size; - lbs_deb_11d("BG-band nrcfp %d\n", - priv->universal_channel[i].nrcfp); - - priv->universal_channel[i].CFP = channel_freq_power_UN_BG; - priv->universal_channel[i].valid = 1; - priv->universal_channel[i].region = UNIVERSAL_REGION_CODE; - priv->universal_channel[i].band = band; - i++; - - return 0; -} - -/** - * @brief This function implements command CMD_802_11D_DOMAIN_INFO - * @param priv pointer to struct lbs_private - * @param cmd pointer to cmd buffer - * @param cmdno cmd ID - * @param cmdOption cmd action - * @return 0 -*/ -int lbs_cmd_802_11d_domain_info(struct lbs_private *priv, - struct cmd_ds_command *cmd, u16 cmdno, - u16 cmdoption) -{ - struct cmd_ds_802_11d_domain_info *pdomaininfo = - &cmd->params.domaininfo; - struct mrvl_ie_domain_param_set *domain = &pdomaininfo->domain; - u8 nr_subband = priv->domainreg.nr_subband; - - lbs_deb_enter(LBS_DEB_11D); - - lbs_deb_11d("nr_subband=%x\n", nr_subband); - - cmd->command = cpu_to_le16(cmdno); - pdomaininfo->action = cpu_to_le16(cmdoption); - if (cmdoption == CMD_ACT_GET) { - cmd->size = - cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); - lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, - le16_to_cpu(cmd->size)); - goto done; - } - - domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); - memcpy(domain->countrycode, priv->domainreg.countrycode, - sizeof(domain->countrycode)); - - domain->header.len = - cpu_to_le16(nr_subband * sizeof(struct ieee_subbandset) + - sizeof(domain->countrycode)); - - if (nr_subband) { - memcpy(domain->subband, priv->domainreg.subband, - nr_subband * sizeof(struct ieee_subbandset)); - - cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + - le16_to_cpu(domain->header.len) + - sizeof(struct mrvl_ie_header) + - S_DS_GEN); - } else { - cmd->size = - cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); - } - - lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, le16_to_cpu(cmd->size)); - -done: - lbs_deb_enter(LBS_DEB_11D); - return 0; -} - -/** - * @brief This function parses countryinfo from AP and download country info to FW - * @param priv pointer to struct lbs_private - * @param resp pointer to command response buffer - * @return 0; -1 - */ -int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp) -{ - struct cmd_ds_802_11d_domain_info *domaininfo = &resp->params.domaininforesp; - struct mrvl_ie_domain_param_set *domain = &domaininfo->domain; - u16 action = le16_to_cpu(domaininfo->action); - s16 ret = 0; - u8 nr_subband = 0; - - lbs_deb_enter(LBS_DEB_11D); - - lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp, - (int)le16_to_cpu(resp->size)); - - nr_subband = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) / - sizeof(struct ieee_subbandset); - - lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband); - - if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { - lbs_deb_11d("Invalid Numrer of Subband returned!!\n"); - return -1; - } - - switch (action) { - case CMD_ACT_SET: /*Proc Set action */ - break; - - case CMD_ACT_GET: - break; - default: - lbs_deb_11d("Invalid action:%d\n", domaininfo->action); - ret = -1; - break; - } - - lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); - return ret; -} - -/** - * @brief This function parses countryinfo from AP and download country info to FW - * @param priv pointer to struct lbs_private - * @return 0; -1 - */ -int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv, - struct bss_descriptor * bss) -{ - int ret; - - lbs_deb_enter(LBS_DEB_11D); - if (priv->enable11d) { - memset(&priv->parsed_region_chan, 0, - sizeof(struct parsed_region_chan_11d)); - ret = parse_domain_info_11d(&bss->countryinfo, 0, - &priv->parsed_region_chan); - - if (ret == -1) { - lbs_deb_11d("error parsing domain_info from AP\n"); - goto done; - } - - memset(&priv->domainreg, 0, - sizeof(struct lbs_802_11d_domain_reg)); - generate_domain_info_11d(&priv->parsed_region_chan, - &priv->domainreg); - - ret = set_domain_info_11d(priv); - - if (ret) { - lbs_deb_11d("error setting domain info\n"); - goto done; - } - } - ret = 0; - -done: - lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); - return ret; -} - -/** - * @brief This function generates 11D info from user specified regioncode and download to FW - * @param priv pointer to struct lbs_private - * @return 0; -1 - */ -int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv) -{ - int ret; - struct region_channel *region_chan; - u8 j; - - lbs_deb_enter(LBS_DEB_11D); - lbs_deb_11d("curbssparams.band %d\n", priv->curbssparams.band); - - if (priv->enable11d) { - /* update parsed_region_chan_11; dnld domaininf to FW */ - - for (j = 0; j < ARRAY_SIZE(priv->region_channel); j++) { - region_chan = &priv->region_channel[j]; - - lbs_deb_11d("%d region_chan->band %d\n", j, - region_chan->band); - - if (!region_chan || !region_chan->valid - || !region_chan->CFP) - continue; - if (region_chan->band != priv->curbssparams.band) - continue; - break; - } - - if (j >= ARRAY_SIZE(priv->region_channel)) { - lbs_deb_11d("region_chan not found, band %d\n", - priv->curbssparams.band); - ret = -1; - goto done; - } - - memset(&priv->parsed_region_chan, 0, - sizeof(struct parsed_region_chan_11d)); - lbs_generate_parsed_region_chan_11d(region_chan, - &priv-> - parsed_region_chan); - - memset(&priv->domainreg, 0, - sizeof(struct lbs_802_11d_domain_reg)); - generate_domain_info_11d(&priv->parsed_region_chan, - &priv->domainreg); - - ret = set_domain_info_11d(priv); - - if (ret) { - lbs_deb_11d("error setting domain info\n"); - goto done; - } - - } - ret = 0; - -done: - lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); - return ret; -} diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h deleted file mode 100644 index fb75d3e321a0..000000000000 --- a/drivers/net/wireless/libertas/11d.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * This header file contains data structures and - * function declarations of 802.11d - */ -#ifndef _LBS_11D_ -#define _LBS_11D_ - -#include "types.h" -#include "defs.h" - -#define UNIVERSAL_REGION_CODE 0xff - -/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr) - */ -#define MRVDRV_MAX_SUBBAND_802_11D 83 - -#define COUNTRY_CODE_LEN 3 -#define MAX_NO_OF_CHAN 40 - -struct cmd_ds_command; - -/** Data structure for Country IE*/ -struct ieee_subbandset { - u8 firstchan; - u8 nrchan; - u8 maxtxpwr; -} __attribute__ ((packed)); - -struct ieee_ie_country_info_set { - struct ieee_ie_header header; - - u8 countrycode[COUNTRY_CODE_LEN]; - struct ieee_subbandset subband[1]; -}; - -struct ieee_ie_country_info_full_set { - struct ieee_ie_header header; - - u8 countrycode[COUNTRY_CODE_LEN]; - struct ieee_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; -} __attribute__ ((packed)); - -struct mrvl_ie_domain_param_set { - struct mrvl_ie_header header; - - u8 countrycode[COUNTRY_CODE_LEN]; - struct ieee_subbandset subband[1]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11d_domain_info { - __le16 action; - struct mrvl_ie_domain_param_set domain; -} __attribute__ ((packed)); - -/** domain regulatory information */ -struct lbs_802_11d_domain_reg { - /** country Code*/ - u8 countrycode[COUNTRY_CODE_LEN]; - /** No. of subband*/ - u8 nr_subband; - struct ieee_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; -}; - -struct chan_power_11d { - u8 chan; - u8 pwr; -} __attribute__ ((packed)); - -struct parsed_region_chan_11d { - u8 band; - u8 region; - s8 countrycode[COUNTRY_CODE_LEN]; - struct chan_power_11d chanpwr[MAX_NO_OF_CHAN]; - u8 nr_chan; -} __attribute__ ((packed)); - -struct region_code_mapping { - u8 region[COUNTRY_CODE_LEN]; - u8 code; -}; - -struct lbs_private; - -u8 lbs_get_scan_type_11d(u8 chan, - struct parsed_region_chan_11d *parsed_region_chan); - -u32 lbs_chan_2_freq(u8 chan); - -void lbs_init_11d(struct lbs_private *priv); - -int lbs_set_universaltable(struct lbs_private *priv, u8 band); - -int lbs_cmd_802_11d_domain_info(struct lbs_private *priv, - struct cmd_ds_command *cmd, u16 cmdno, - u16 cmdOption); - -int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp); - -struct bss_descriptor; -int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv, - struct bss_descriptor * bss); - -int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv); - -#endif diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig new file mode 100644 index 000000000000..30aa9d48d67e --- /dev/null +++ b/drivers/net/wireless/libertas/Kconfig @@ -0,0 +1,39 @@ +config LIBERTAS + tristate "Marvell 8xxx Libertas WLAN driver support" + depends on CFG80211 + select WIRELESS_EXT + select WEXT_SPY + select LIB80211 + select FW_LOADER + ---help--- + A library for Marvell Libertas 8xxx devices. + +config LIBERTAS_USB + tristate "Marvell Libertas 8388 USB 802.11b/g cards" + depends on LIBERTAS && USB + ---help--- + A driver for Marvell Libertas 8388 USB devices. + +config LIBERTAS_CS + tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" + depends on LIBERTAS && PCMCIA + ---help--- + A driver for Marvell Libertas 8385 CompactFlash devices. + +config LIBERTAS_SDIO + tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards" + depends on LIBERTAS && MMC + ---help--- + A driver for Marvell Libertas 8385/8686/8688 SDIO devices. + +config LIBERTAS_SPI + tristate "Marvell Libertas 8686 SPI 802.11b/g cards" + depends on LIBERTAS && SPI + ---help--- + A driver for Marvell Libertas 8686 SPI devices. + +config LIBERTAS_DEBUG + bool "Enable full debugging output in the Libertas module." + depends on LIBERTAS + ---help--- + Debugging support. diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile index 0b6918584503..b188cd97a053 100644 --- a/drivers/net/wireless/libertas/Makefile +++ b/drivers/net/wireless/libertas/Makefile @@ -1,5 +1,15 @@ -libertas-objs := main.o wext.o rx.o tx.o cmd.o cmdresp.o scan.o 11d.o \ - debugfs.o persistcfg.o ethtool.o assoc.o +libertas-y += assoc.o +libertas-y += cfg.o +libertas-y += cmd.o +libertas-y += cmdresp.o +libertas-y += debugfs.o +libertas-y += ethtool.o +libertas-y += main.o +libertas-y += mesh.o +libertas-y += rx.o +libertas-y += scan.o +libertas-y += tx.o +libertas-y += wext.o usb8xxx-objs += if_usb.o libertas_cs-objs += if_cs.o diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README index ab6a2d518af0..2726c044430f 100644 --- a/drivers/net/wireless/libertas/README +++ b/drivers/net/wireless/libertas/README @@ -1,5 +1,5 @@ ================================================================================ - README for USB8388 + README for Libertas (c) Copyright © 2003-2006, Marvell International Ltd. All Rights Reserved @@ -226,4 +226,28 @@ setuserscan All entries in the scan table (not just the new scan data when keep=1) will be displayed upon completion by use of the getscantable ioctl. +======================== +IWCONFIG COMMANDS +======================== +power period + + This command is used to configure the station in deep sleep mode / + auto deep sleep mode. + + The timer is implemented to monitor the activities (command, event, + etc.). When an activity is detected station will exit from deep + sleep mode automatically and restart the timer. At timer expiry + (no activity for defined time period) the deep sleep mode is entered + automatically. + + Note: this command is for SDIO interface only. + + Usage: + To enable deep sleep mode do: + iwconfig wlan0 power period 0 + To enable auto deep sleep mode with idle time period 5 seconds do: + iwconfig wlan0 power period 5 + To disable deep sleep/auto deep sleep mode do: + iwconfig wlan0 power period -1 + ============================================================================== diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c index dd8732611ba9..751067369ba8 100644 --- a/drivers/net/wireless/libertas/assoc.c +++ b/drivers/net/wireless/libertas/assoc.c @@ -23,6 +23,13 @@ static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) = */ #define CAPINFO_MASK (~(0xda00)) +/** + * 802.11b/g supported bitrates (in 500Kb/s units) + */ +u8 lbs_bg_rates[MAX_RATES] = + { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0x00, 0x00 }; + /** * @brief This function finds common rates between rates and card rates. @@ -147,6 +154,397 @@ static int lbs_set_authentication(struct lbs_private *priv, u8 bssid[6], u8 auth } +int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action, + struct assoc_request *assoc) +{ + struct cmd_ds_802_11_set_wep cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.command = cpu_to_le16(CMD_802_11_SET_WEP); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + + cmd.action = cpu_to_le16(cmd_action); + + if (cmd_action == CMD_ACT_ADD) { + int i; + + /* default tx key index */ + cmd.keyindex = cpu_to_le16(assoc->wep_tx_keyidx & + CMD_WEP_KEY_INDEX_MASK); + + /* Copy key types and material to host command structure */ + for (i = 0; i < 4; i++) { + struct enc_key *pkey = &assoc->wep_keys[i]; + + switch (pkey->len) { + case KEY_LEN_WEP_40: + cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; + memmove(cmd.keymaterial[i], pkey->key, pkey->len); + lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i); + break; + case KEY_LEN_WEP_104: + cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; + memmove(cmd.keymaterial[i], pkey->key, pkey->len); + lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i); + break; + case 0: + break; + default: + lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n", + i, pkey->len); + ret = -1; + goto done; + break; + } + } + } else if (cmd_action == CMD_ACT_REMOVE) { + /* ACT_REMOVE clears _all_ WEP keys */ + + /* default tx key index */ + cmd.keyindex = cpu_to_le16(priv->wep_tx_keyidx & + CMD_WEP_KEY_INDEX_MASK); + lbs_deb_cmd("SET_WEP: remove key %d\n", priv->wep_tx_keyidx); + } + + ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); +done: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action, + uint16_t *enable) +{ + struct cmd_ds_802_11_enable_rsn cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(cmd_action); + + if (cmd_action == CMD_ACT_GET) + cmd.enable = 0; + else { + if (*enable) + cmd.enable = cpu_to_le16(CMD_ENABLE_RSN); + else + cmd.enable = cpu_to_le16(CMD_DISABLE_RSN); + lbs_deb_cmd("ENABLE_RSN: %d\n", *enable); + } + + ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); + if (!ret && cmd_action == CMD_ACT_GET) + *enable = le16_to_cpu(cmd.enable); + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam, + struct enc_key *key) +{ + lbs_deb_enter(LBS_DEB_CMD); + + if (key->flags & KEY_INFO_WPA_ENABLED) + keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED); + if (key->flags & KEY_INFO_WPA_UNICAST) + keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); + if (key->flags & KEY_INFO_WPA_MCAST) + keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); + + keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + keyparam->keytypeid = cpu_to_le16(key->type); + keyparam->keylen = cpu_to_le16(key->len); + memcpy(keyparam->key, key->key, key->len); + + /* Length field doesn't include the {type,length} header */ + keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4); + lbs_deb_leave(LBS_DEB_CMD); +} + +int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action, + struct assoc_request *assoc) +{ + struct cmd_ds_802_11_key_material cmd; + int ret = 0; + int index = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.action = cpu_to_le16(cmd_action); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + + if (cmd_action == CMD_ACT_GET) { + cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_header) + 2); + } else { + memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet)); + + if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) { + set_one_wpa_key(&cmd.keyParamSet[index], + &assoc->wpa_unicast_key); + index++; + } + + if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) { + set_one_wpa_key(&cmd.keyParamSet[index], + &assoc->wpa_mcast_key); + index++; + } + + /* The common header and as many keys as we included */ + cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd), + keyParamSet[index])); + } + ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); + /* Copy the returned key to driver private data */ + if (!ret && cmd_action == CMD_ACT_GET) { + void *buf_ptr = cmd.keyParamSet; + void *resp_end = &(&cmd)[1]; + + while (buf_ptr < resp_end) { + struct MrvlIEtype_keyParamSet *keyparam = buf_ptr; + struct enc_key *key; + uint16_t param_set_len = le16_to_cpu(keyparam->length); + uint16_t key_len = le16_to_cpu(keyparam->keylen); + uint16_t key_flags = le16_to_cpu(keyparam->keyinfo); + uint16_t key_type = le16_to_cpu(keyparam->keytypeid); + void *end; + + end = (void *)keyparam + sizeof(keyparam->type) + + sizeof(keyparam->length) + param_set_len; + + /* Make sure we don't access past the end of the IEs */ + if (end > resp_end) + break; + + if (key_flags & KEY_INFO_WPA_UNICAST) + key = &priv->wpa_unicast_key; + else if (key_flags & KEY_INFO_WPA_MCAST) + key = &priv->wpa_mcast_key; + else + break; + + /* Copy returned key into driver */ + memset(key, 0, sizeof(struct enc_key)); + if (key_len > sizeof(key->key)) + break; + key->type = key_type; + key->flags = key_flags; + key->len = key_len; + memcpy(key->key, keyparam->key, key->len); + + buf_ptr = end + 1; + } + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +static __le16 lbs_rate_to_fw_bitmap(int rate, int lower_rates_ok) +{ +/* Bit Rate +* 15:13 Reserved +* 12 54 Mbps +* 11 48 Mbps +* 10 36 Mbps +* 9 24 Mbps +* 8 18 Mbps +* 7 12 Mbps +* 6 9 Mbps +* 5 6 Mbps +* 4 Reserved +* 3 11 Mbps +* 2 5.5 Mbps +* 1 2 Mbps +* 0 1 Mbps +**/ + + uint16_t ratemask; + int i = lbs_data_rate_to_fw_index(rate); + if (lower_rates_ok) + ratemask = (0x1fef >> (12 - i)); + else + ratemask = (1 << i); + return cpu_to_le16(ratemask); +} + +int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, + uint16_t cmd_action) +{ + struct cmd_ds_802_11_rate_adapt_rateset cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + if (!priv->cur_rate && !priv->enablehwauto) + return -EINVAL; + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + + cmd.action = cpu_to_le16(cmd_action); + cmd.enablehwauto = cpu_to_le16(priv->enablehwauto); + cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto); + ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd); + if (!ret && cmd_action == CMD_ACT_GET) { + priv->ratebitmap = le16_to_cpu(cmd.bitmap); + priv->enablehwauto = le16_to_cpu(cmd.enablehwauto); + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * @brief Set the data rate + * + * @param priv A pointer to struct lbs_private structure + * @param rate The desired data rate, or 0 to clear a locked rate + * + * @return 0 on success, error on failure + */ +int lbs_set_data_rate(struct lbs_private *priv, u8 rate) +{ + struct cmd_ds_802_11_data_rate cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + + if (rate > 0) { + cmd.action = cpu_to_le16(CMD_ACT_SET_TX_FIX_RATE); + cmd.rates[0] = lbs_data_rate_to_fw_index(rate); + if (cmd.rates[0] == 0) { + lbs_deb_cmd("DATA_RATE: invalid requested rate of" + " 0x%02X\n", rate); + ret = 0; + goto out; + } + lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n", cmd.rates[0]); + } else { + cmd.action = cpu_to_le16(CMD_ACT_SET_TX_AUTO); + lbs_deb_cmd("DATA_RATE: setting auto\n"); + } + + ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, &cmd); + if (ret) + goto out; + + lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof(cmd)); + + /* FIXME: get actual rates FW can do if this command actually returns + * all data rates supported. + */ + priv->cur_rate = lbs_fw_index_to_data_rate(cmd.rates[0]); + lbs_deb_cmd("DATA_RATE: current rate is 0x%02x\n", priv->cur_rate); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + + +int lbs_cmd_802_11_rssi(struct lbs_private *priv, + struct cmd_ds_command *cmd) +{ + + lbs_deb_enter(LBS_DEB_CMD); + cmd->command = cpu_to_le16(CMD_802_11_RSSI); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + + sizeof(struct cmd_header)); + cmd->params.rssi.N = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR); + + /* reset Beacon SNR/NF/RSSI values */ + priv->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; + priv->SNR[TYPE_BEACON][TYPE_AVG] = 0; + priv->NF[TYPE_BEACON][TYPE_NOAVG] = 0; + priv->NF[TYPE_BEACON][TYPE_AVG] = 0; + priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; + priv->RSSI[TYPE_BEACON][TYPE_AVG] = 0; + + lbs_deb_leave(LBS_DEB_CMD); + return 0; +} + +int lbs_ret_802_11_rssi(struct lbs_private *priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; + + lbs_deb_enter(LBS_DEB_CMD); + + /* store the non average value */ + priv->SNR[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->SNR); + priv->NF[TYPE_BEACON][TYPE_NOAVG] = + get_unaligned_le16(&rssirsp->noisefloor); + + priv->SNR[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgSNR); + priv->NF[TYPE_BEACON][TYPE_AVG] = + get_unaligned_le16(&rssirsp->avgnoisefloor); + + priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = + CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG], + priv->NF[TYPE_BEACON][TYPE_NOAVG]); + + priv->RSSI[TYPE_BEACON][TYPE_AVG] = + CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, + priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); + + lbs_deb_cmd("RSSI: beacon %d, avg %d\n", + priv->RSSI[TYPE_BEACON][TYPE_NOAVG], + priv->RSSI[TYPE_BEACON][TYPE_AVG]); + + lbs_deb_leave(LBS_DEB_CMD); + return 0; +} + + +int lbs_cmd_bcn_ctrl(struct lbs_private *priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_beacon_control + *bcn_ctrl = &cmd->params.bcn_ctrl; + + lbs_deb_enter(LBS_DEB_CMD); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control) + + sizeof(struct cmd_header)); + cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL); + + bcn_ctrl->action = cpu_to_le16(cmd_action); + bcn_ctrl->beacon_enable = cpu_to_le16(priv->beacon_enable); + bcn_ctrl->beacon_period = cpu_to_le16(priv->beacon_period); + + lbs_deb_leave(LBS_DEB_CMD); + return 0; +} + +int lbs_ret_802_11_bcn_ctrl(struct lbs_private *priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_beacon_control *bcn_ctrl = + &resp->params.bcn_ctrl; + + lbs_deb_enter(LBS_DEB_CMD); + + if (bcn_ctrl->action == CMD_ACT_GET) { + priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable); + priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period); + } + + lbs_deb_enter(LBS_DEB_CMD); + return 0; +} + + + static int lbs_assoc_post(struct lbs_private *priv, struct cmd_ds_802_11_associate_response *resp) { @@ -226,7 +624,7 @@ static int lbs_assoc_post(struct lbs_private *priv, priv->connect_status = LBS_CONNECTED; /* Update current SSID and BSSID */ - memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE); + memcpy(&priv->curbssparams.ssid, &bss->ssid, IEEE80211_MAX_SSID_LEN); priv->curbssparams.ssid_len = bss->ssid_len; memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN); @@ -369,12 +767,7 @@ static int lbs_associate(struct lbs_private *priv, (u16)(pos - (u8 *) &cmd.iebuf)); /* update curbssparams */ - priv->curbssparams.channel = bss->phy.ds.channel; - - if (lbs_parse_dnld_countryinfo_11d(priv, bss)) { - ret = -1; - goto done; - } + priv->channel = bss->phy.ds.channel; ret = lbs_cmd_with_response(priv, command, &cmd); if (ret == 0) { @@ -472,7 +865,7 @@ static int lbs_adhoc_post(struct lbs_private *priv, memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN); /* Set the new SSID to current SSID */ - memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE); + memcpy(&priv->curbssparams.ssid, &bss->ssid, IEEE80211_MAX_SSID_LEN); priv->curbssparams.ssid_len = bss->ssid_len; netif_carrier_on(priv->dev); @@ -487,7 +880,7 @@ static int lbs_adhoc_post(struct lbs_private *priv, lbs_deb_join("ADHOC_RESP: Joined/started '%s', BSSID %pM, channel %d\n", print_ssid(ssid, bss->ssid, bss->ssid_len), priv->curbssparams.bssid, - priv->curbssparams.channel); + priv->channel); done: lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); @@ -560,7 +953,7 @@ static int lbs_adhoc_join(struct lbs_private *priv, lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band); priv->adhoccreate = 0; - priv->curbssparams.channel = bss->channel; + priv->channel = bss->channel; /* Build the join command */ memset(&cmd, 0, sizeof(cmd)); @@ -633,11 +1026,6 @@ static int lbs_adhoc_join(struct lbs_private *priv, } } - if (lbs_parse_dnld_countryinfo_11d(priv, bss)) { - ret = -1; - goto out; - } - ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd); if (ret == 0) { ret = lbs_adhoc_post(priv, @@ -737,12 +1125,6 @@ static int lbs_adhoc_start(struct lbs_private *priv, lbs_deb_join("ADHOC_START: rates=%02x %02x %02x %02x\n", cmd.rates[0], cmd.rates[1], cmd.rates[2], cmd.rates[3]); - if (lbs_create_dnld_countryinfo_11d(priv)) { - lbs_deb_join("ADHOC_START: dnld_countryinfo_11d failed\n"); - ret = -1; - goto out; - } - lbs_deb_join("ADHOC_START: Starting Ad-Hoc BSS on channel %d, band %d\n", assoc_req->channel, assoc_req->band); @@ -1099,7 +1481,7 @@ static int assoc_helper_essid(struct lbs_private *priv, /* else send START command */ lbs_deb_assoc("SSID not found, creating adhoc network\n"); memcpy(&assoc_req->bss.ssid, &assoc_req->ssid, - IW_ESSID_MAX_SIZE); + IEEE80211_MAX_SSID_LEN); assoc_req->bss.ssid_len = assoc_req->ssid_len; lbs_adhoc_start(priv, assoc_req); } @@ -1185,7 +1567,8 @@ static int assoc_helper_mode(struct lbs_private *priv, } priv->mode = assoc_req->mode; - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, assoc_req->mode); + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, + assoc_req->mode == IW_MODE_ADHOC ? 2 : 1); done: lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); @@ -1205,7 +1588,7 @@ static int assoc_helper_channel(struct lbs_private *priv, goto done; } - if (assoc_req->channel == priv->curbssparams.channel) + if (assoc_req->channel == priv->channel) goto done; if (priv->mesh_dev) { @@ -1217,7 +1600,7 @@ static int assoc_helper_channel(struct lbs_private *priv, } lbs_deb_assoc("ASSOC: channel: %d -> %d\n", - priv->curbssparams.channel, assoc_req->channel); + priv->channel, assoc_req->channel); ret = lbs_set_channel(priv, assoc_req->channel); if (ret < 0) @@ -1232,7 +1615,7 @@ static int assoc_helper_channel(struct lbs_private *priv, goto done; } - if (assoc_req->channel != priv->curbssparams.channel) { + if (assoc_req->channel != priv->channel) { lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n", assoc_req->channel); goto restore_mesh; @@ -1253,7 +1636,7 @@ static int assoc_helper_channel(struct lbs_private *priv, restore_mesh: if (priv->mesh_dev) lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - priv->curbssparams.channel); + priv->channel); done: lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); @@ -1475,7 +1858,7 @@ static int should_stop_adhoc(struct lbs_private *priv, } if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { - if (assoc_req->channel != priv->curbssparams.channel) + if (assoc_req->channel != priv->channel) return 1; } @@ -1557,7 +1940,7 @@ static int lbs_find_best_network_ssid(struct lbs_private *priv, found = lbs_find_best_ssid_in_list(priv, preferred_mode); if (found && (found->ssid_len > 0)) { - memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE); + memcpy(out_ssid, &found->ssid, IEEE80211_MAX_SSID_LEN); *out_ssid_len = found->ssid_len; *out_mode = found->mode; ret = 0; @@ -1775,12 +2158,12 @@ struct assoc_request *lbs_get_association_request(struct lbs_private *priv) assoc_req = priv->pending_assoc_req; if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { memcpy(&assoc_req->ssid, &priv->curbssparams.ssid, - IW_ESSID_MAX_SIZE); + IEEE80211_MAX_SSID_LEN); assoc_req->ssid_len = priv->curbssparams.ssid_len; } if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) - assoc_req->channel = priv->curbssparams.channel; + assoc_req->channel = priv->channel; if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags)) assoc_req->band = priv->curbssparams.band; diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h index 6e765e9f91a3..40621b789fc5 100644 --- a/drivers/net/wireless/libertas/assoc.h +++ b/drivers/net/wireless/libertas/assoc.h @@ -3,7 +3,126 @@ #ifndef _LBS_ASSOC_H_ #define _LBS_ASSOC_H_ -#include "dev.h" + +#include "defs.h" +#include "host.h" + + +struct lbs_private; + +/* + * In theory, the IE is limited to the IE length, 255, + * but in practice 64 bytes are enough. + */ +#define MAX_WPA_IE_LEN 64 + + + +struct lbs_802_11_security { + u8 WPAenabled; + u8 WPA2enabled; + u8 wep_enabled; + u8 auth_mode; + u32 key_mgmt; +}; + +/** Current Basic Service Set State Structure */ +struct current_bss_params { + /** bssid */ + u8 bssid[ETH_ALEN]; + /** ssid */ + u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 ssid_len; + + /** band */ + u8 band; + /** channel is directly in priv->channel */ + /** zero-terminated array of supported data rates */ + u8 rates[MAX_RATES + 1]; +}; + +/** + * @brief Structure used to store information for each beacon/probe response + */ +struct bss_descriptor { + u8 bssid[ETH_ALEN]; + + u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 ssid_len; + + u16 capability; + u32 rssi; + u32 channel; + u16 beaconperiod; + __le16 atimwindow; + + /* IW_MODE_AUTO, IW_MODE_ADHOC, IW_MODE_INFRA */ + u8 mode; + + /* zero-terminated array of supported data rates */ + u8 rates[MAX_RATES + 1]; + + unsigned long last_scanned; + + union ieee_phy_param_set phy; + union ieee_ss_param_set ss; + + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + + u8 mesh; + + struct list_head list; +}; + +/** Association request + * + * Encapsulates all the options that describe a specific assocation request + * or configuration of the wireless card's radio, mode, and security settings. + */ +struct assoc_request { +#define ASSOC_FLAG_SSID 1 +#define ASSOC_FLAG_CHANNEL 2 +#define ASSOC_FLAG_BAND 3 +#define ASSOC_FLAG_MODE 4 +#define ASSOC_FLAG_BSSID 5 +#define ASSOC_FLAG_WEP_KEYS 6 +#define ASSOC_FLAG_WEP_TX_KEYIDX 7 +#define ASSOC_FLAG_WPA_MCAST_KEY 8 +#define ASSOC_FLAG_WPA_UCAST_KEY 9 +#define ASSOC_FLAG_SECINFO 10 +#define ASSOC_FLAG_WPA_IE 11 + unsigned long flags; + + u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 ssid_len; + u8 channel; + u8 band; + u8 mode; + u8 bssid[ETH_ALEN] __attribute__ ((aligned (2))); + + /** WEP keys */ + struct enc_key wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct enc_key wpa_mcast_key; + struct enc_key wpa_unicast_key; + + struct lbs_802_11_security secinfo; + + /** WPA Information Elements*/ + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; + + /* BSS to associate with for infrastructure of Ad-Hoc join */ + struct bss_descriptor bss; +}; + + +extern u8 lbs_bg_rates[MAX_RATES]; void lbs_association_worker(struct work_struct *work); struct assoc_request *lbs_get_association_request(struct lbs_private *priv); @@ -13,4 +132,24 @@ int lbs_adhoc_stop(struct lbs_private *priv); int lbs_cmd_80211_deauthenticate(struct lbs_private *priv, u8 bssid[ETH_ALEN], u16 reason); +int lbs_cmd_802_11_rssi(struct lbs_private *priv, + struct cmd_ds_command *cmd); +int lbs_ret_802_11_rssi(struct lbs_private *priv, + struct cmd_ds_command *resp); + +int lbs_cmd_bcn_ctrl(struct lbs_private *priv, + struct cmd_ds_command *cmd, + u16 cmd_action); +int lbs_ret_802_11_bcn_ctrl(struct lbs_private *priv, + struct cmd_ds_command *resp); + +int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action, + struct assoc_request *assoc); + +int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action, + uint16_t *enable); + +int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action, + struct assoc_request *assoc); + #endif /* _LBS_ASSOC_H */ diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c new file mode 100644 index 000000000000..4396dccd12ac --- /dev/null +++ b/drivers/net/wireless/libertas/cfg.c @@ -0,0 +1,198 @@ +/* + * Implement cfg80211 ("iw") support. + * + * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany + * Holger Schurig <hs4233@mail.mn-solutions.de> + * + */ + +#include <net/cfg80211.h> + +#include "cfg.h" +#include "cmd.h" + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel lbs_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +#define RATETAB_ENT(_rate, _rateid, _flags) { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ +} + + +static struct ieee80211_rate lbs_rates[] = { + RATETAB_ENT(10, 0x1, 0), + RATETAB_ENT(20, 0x2, 0), + RATETAB_ENT(55, 0x4, 0), + RATETAB_ENT(110, 0x8, 0), + RATETAB_ENT(60, 0x10, 0), + RATETAB_ENT(90, 0x20, 0), + RATETAB_ENT(120, 0x40, 0), + RATETAB_ENT(180, 0x80, 0), + RATETAB_ENT(240, 0x100, 0), + RATETAB_ENT(360, 0x200, 0), + RATETAB_ENT(480, 0x400, 0), + RATETAB_ENT(540, 0x800, 0), +}; + +static struct ieee80211_supported_band lbs_band_2ghz = { + .channels = lbs_2ghz_channels, + .n_channels = ARRAY_SIZE(lbs_2ghz_channels), + .bitrates = lbs_rates, + .n_bitrates = ARRAY_SIZE(lbs_rates), +}; + + +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, +}; + + + +static int lbs_cfg_set_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = -ENOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", chan->center_freq, channel_type); + + if (channel_type != NL80211_CHAN_NO_HT) + goto out; + + ret = lbs_set_channel(priv, chan->hw_value); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + + +static struct cfg80211_ops lbs_cfg80211_ops = { + .set_channel = lbs_cfg_set_channel, +}; + + +/* + * At this time lbs_private *priv doesn't even exist, so we just allocate + * memory and don't initialize the wiphy further. This is postponed until we + * can talk to the firmware and happens at registration time in + * lbs_cfg_wiphy_register(). + */ +struct wireless_dev *lbs_cfg_alloc(struct device *dev) +{ + int ret = 0; + struct wireless_dev *wdev; + + lbs_deb_enter(LBS_DEB_CFG80211); + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) { + dev_err(dev, "cannot allocate wireless device\n"); + return ERR_PTR(-ENOMEM); + } + + wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private)); + if (!wdev->wiphy) { + dev_err(dev, "cannot allocate wiphy\n"); + ret = -ENOMEM; + goto err_wiphy_new; + } + + lbs_deb_leave(LBS_DEB_CFG80211); + return wdev; + + err_wiphy_new: + kfree(wdev); + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ERR_PTR(ret); +} + + +/* + * This function get's called after lbs_setup_firmware() determined the + * firmware capabities. So we can setup the wiphy according to our + * hardware/firmware. + */ +int lbs_cfg_register(struct lbs_private *priv) +{ + struct wireless_dev *wdev = priv->wdev; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + wdev->wiphy->max_scan_ssids = 1; + wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + /* TODO: BIT(NL80211_IFTYPE_ADHOC); */ + wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + /* TODO: honor priv->regioncode */ + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz; + + /* + * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have + * never seen a firmware without WPA + */ + wdev->wiphy->cipher_suites = cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + ret = wiphy_register(wdev->wiphy); + if (ret < 0) + lbs_pr_err("cannot register wiphy device\n"); + + ret = register_netdev(priv->dev); + if (ret) + lbs_pr_err("cannot register network device\n"); + + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + +void lbs_cfg_free(struct lbs_private *priv) +{ + struct wireless_dev *wdev = priv->wdev; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!wdev) + return; + + if (wdev->wiphy) { + wiphy_unregister(wdev->wiphy); + wiphy_free(wdev->wiphy); + } + kfree(wdev); +} diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h new file mode 100644 index 000000000000..e09a193a34d6 --- /dev/null +++ b/drivers/net/wireless/libertas/cfg.h @@ -0,0 +1,16 @@ +#ifndef __LBS_CFG80211_H__ +#define __LBS_CFG80211_H__ + +#include "dev.h" + +struct wireless_dev *lbs_cfg_alloc(struct device *dev); +int lbs_cfg_register(struct lbs_private *priv); +void lbs_cfg_free(struct lbs_private *priv); + +int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid, + u8 ssid_len); +int lbs_scan_networks(struct lbs_private *priv, int full_scan); +void lbs_cfg_scan_worker(struct work_struct *work); + + +#endif diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 0a324dcd264c..b9b371bfa30f 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -3,21 +3,20 @@ * It prepares command and sends it to firmware when it is ready. */ -#include <net/iw_handler.h> -#include <net/lib80211.h> #include <linux/kfifo.h> #include <linux/sched.h> + #include "host.h" -#include "hostcmd.h" #include "decl.h" #include "defs.h" #include "dev.h" #include "assoc.h" #include "wext.h" +#include "scan.h" #include "cmd.h" -static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv); +static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv); /** * @brief Simple callback that copies response back into command @@ -77,6 +76,30 @@ static u8 is_command_allowed_in_ps(u16 cmd) } /** + * @brief This function checks if the command is allowed. + * + * @param priv A pointer to lbs_private structure + * @return allowed or not allowed. + */ + +static int lbs_is_cmd_allowed(struct lbs_private *priv) +{ + int ret = 1; + + lbs_deb_enter(LBS_DEB_CMD); + + if (!priv->is_auto_deep_sleep_enabled) { + if (priv->is_deep_sleep) { + lbs_deb_cmd("command not allowed in deep sleep\n"); + ret = 0; + } + } + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** * @brief Updates the hardware details like MAC address and regulatory region * * @param priv A pointer to struct lbs_private structure @@ -169,11 +192,6 @@ int lbs_update_hw_spec(struct lbs_private *priv) goto out; } - if (lbs_set_universaltable(priv, 0)) { - ret = -1; - goto out; - } - out: lbs_deb_leave(LBS_DEB_CMD); return ret; @@ -222,7 +240,7 @@ static int lbs_cmd_802_11_ps_mode(struct cmd_ds_command *cmd, cmd->command = cpu_to_le16(CMD_802_11_PS_MODE); cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) + - S_DS_GEN); + sizeof(struct cmd_header)); psm->action = cpu_to_le16(cmd_action); psm->multipledtim = 0; switch (cmd_action) { @@ -251,33 +269,6 @@ static int lbs_cmd_802_11_ps_mode(struct cmd_ds_command *cmd, return 0; } -int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv, - uint16_t cmd_action, uint16_t *timeout) -{ - struct cmd_ds_802_11_inactivity_timeout cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - cmd.hdr.command = cpu_to_le16(CMD_802_11_INACTIVITY_TIMEOUT); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - - cmd.action = cpu_to_le16(cmd_action); - - if (cmd_action == CMD_ACT_SET) - cmd.timeout = cpu_to_le16(*timeout); - else - cmd.timeout = 0; - - ret = lbs_cmd_with_response(priv, CMD_802_11_INACTIVITY_TIMEOUT, &cmd); - - if (!ret) - *timeout = le16_to_cpu(cmd.timeout); - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return 0; -} - int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, struct sleep_params *sp) { @@ -320,190 +311,53 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, return 0; } -int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action, - struct assoc_request *assoc) +static int lbs_wait_for_ds_awake(struct lbs_private *priv) { - struct cmd_ds_802_11_set_wep cmd; int ret = 0; lbs_deb_enter(LBS_DEB_CMD); - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.command = cpu_to_le16(CMD_802_11_SET_WEP); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - - cmd.action = cpu_to_le16(cmd_action); - - if (cmd_action == CMD_ACT_ADD) { - int i; - - /* default tx key index */ - cmd.keyindex = cpu_to_le16(assoc->wep_tx_keyidx & - CMD_WEP_KEY_INDEX_MASK); - - /* Copy key types and material to host command structure */ - for (i = 0; i < 4; i++) { - struct enc_key *pkey = &assoc->wep_keys[i]; - - switch (pkey->len) { - case KEY_LEN_WEP_40: - cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; - memmove(cmd.keymaterial[i], pkey->key, pkey->len); - lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i); - break; - case KEY_LEN_WEP_104: - cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; - memmove(cmd.keymaterial[i], pkey->key, pkey->len); - lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i); - break; - case 0: - break; - default: - lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n", - i, pkey->len); - ret = -1; - goto done; - break; - } + if (priv->is_deep_sleep) { + if (!wait_event_interruptible_timeout(priv->ds_awake_q, + !priv->is_deep_sleep, (10 * HZ))) { + lbs_pr_err("ds_awake_q: timer expired\n"); + ret = -1; } - } else if (cmd_action == CMD_ACT_REMOVE) { - /* ACT_REMOVE clears _all_ WEP keys */ - - /* default tx key index */ - cmd.keyindex = cpu_to_le16(priv->wep_tx_keyidx & - CMD_WEP_KEY_INDEX_MASK); - lbs_deb_cmd("SET_WEP: remove key %d\n", priv->wep_tx_keyidx); - } - - ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); -done: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action, - uint16_t *enable) -{ - struct cmd_ds_802_11_enable_rsn cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(cmd_action); - - if (cmd_action == CMD_ACT_GET) - cmd.enable = 0; - else { - if (*enable) - cmd.enable = cpu_to_le16(CMD_ENABLE_RSN); - else - cmd.enable = cpu_to_le16(CMD_DISABLE_RSN); - lbs_deb_cmd("ENABLE_RSN: %d\n", *enable); } - ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); - if (!ret && cmd_action == CMD_ACT_GET) - *enable = le16_to_cpu(cmd.enable); - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } -static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam, - struct enc_key *key) +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) { - lbs_deb_enter(LBS_DEB_CMD); - - if (key->flags & KEY_INFO_WPA_ENABLED) - keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED); - if (key->flags & KEY_INFO_WPA_UNICAST) - keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); - if (key->flags & KEY_INFO_WPA_MCAST) - keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); - - keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); - keyparam->keytypeid = cpu_to_le16(key->type); - keyparam->keylen = cpu_to_le16(key->len); - memcpy(keyparam->key, key->key, key->len); - - /* Length field doesn't include the {type,length} header */ - keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4); - lbs_deb_leave(LBS_DEB_CMD); -} - -int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action, - struct assoc_request *assoc) -{ - struct cmd_ds_802_11_key_material cmd; - int ret = 0; - int index = 0; + int ret = 0; lbs_deb_enter(LBS_DEB_CMD); - cmd.action = cpu_to_le16(cmd_action); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - - if (cmd_action == CMD_ACT_GET) { - cmd.hdr.size = cpu_to_le16(S_DS_GEN + 2); - } else { - memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet)); - - if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) { - set_one_wpa_key(&cmd.keyParamSet[index], - &assoc->wpa_unicast_key); - index++; - } - - if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) { - set_one_wpa_key(&cmd.keyParamSet[index], - &assoc->wpa_mcast_key); - index++; + if (deep_sleep) { + if (priv->is_deep_sleep != 1) { + lbs_deb_cmd("deep sleep: sleep\n"); + BUG_ON(!priv->enter_deep_sleep); + ret = priv->enter_deep_sleep(priv); + if (!ret) { + netif_stop_queue(priv->dev); + netif_carrier_off(priv->dev); + } + } else { + lbs_pr_err("deep sleep: already enabled\n"); } - - /* The common header and as many keys as we included */ - cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd), - keyParamSet[index])); - } - ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); - /* Copy the returned key to driver private data */ - if (!ret && cmd_action == CMD_ACT_GET) { - void *buf_ptr = cmd.keyParamSet; - void *resp_end = &(&cmd)[1]; - - while (buf_ptr < resp_end) { - struct MrvlIEtype_keyParamSet *keyparam = buf_ptr; - struct enc_key *key; - uint16_t param_set_len = le16_to_cpu(keyparam->length); - uint16_t key_len = le16_to_cpu(keyparam->keylen); - uint16_t key_flags = le16_to_cpu(keyparam->keyinfo); - uint16_t key_type = le16_to_cpu(keyparam->keytypeid); - void *end; - - end = (void *)keyparam + sizeof(keyparam->type) - + sizeof(keyparam->length) + param_set_len; - - /* Make sure we don't access past the end of the IEs */ - if (end > resp_end) - break; - - if (key_flags & KEY_INFO_WPA_UNICAST) - key = &priv->wpa_unicast_key; - else if (key_flags & KEY_INFO_WPA_MCAST) - key = &priv->wpa_mcast_key; - else - break; - - /* Copy returned key into driver */ - memset(key, 0, sizeof(struct enc_key)); - if (key_len > sizeof(key->key)) - break; - key->type = key_type; - key->flags = key_flags; - key->len = key_len; - memcpy(key->key, keyparam->key, key->len); - - buf_ptr = end + 1; + } else { + if (priv->is_deep_sleep) { + lbs_deb_cmd("deep sleep: wakeup\n"); + BUG_ON(!priv->exit_deep_sleep); + ret = priv->exit_deep_sleep(priv); + if (!ret) { + ret = lbs_wait_for_ds_awake(priv); + if (ret) + lbs_pr_err("deep sleep: wakeup" + "failed\n"); + } } } @@ -535,7 +389,7 @@ int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val) switch (oid) { case SNMP_MIB_OID_BSS_TYPE: cmd.bufsize = cpu_to_le16(sizeof(u8)); - cmd.value[0] = (val == IW_MODE_ADHOC) ? 2 : 1; + cmd.value[0] = val; break; case SNMP_MIB_OID_11D_ENABLE: case SNMP_MIB_OID_FRAG_THRESHOLD: @@ -588,13 +442,7 @@ int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) switch (le16_to_cpu(cmd.bufsize)) { case sizeof(u8): - if (oid == SNMP_MIB_OID_BSS_TYPE) { - if (cmd.value[0] == 2) - *out_val = IW_MODE_ADHOC; - else - *out_val = IW_MODE_INFRA; - } else - *out_val = cmd.value[0]; + *out_val = cmd.value[0]; break; case sizeof(u16): *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); @@ -681,7 +529,7 @@ static int lbs_cmd_802_11_monitor_mode(struct cmd_ds_command *cmd, cmd->command = cpu_to_le16(CMD_802_11_MONITOR_MODE); cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_monitor_mode) + - S_DS_GEN); + sizeof(struct cmd_header)); monitor->action = cpu_to_le16(cmd_action); if (cmd_action == CMD_ACT_SET) { @@ -692,111 +540,6 @@ static int lbs_cmd_802_11_monitor_mode(struct cmd_ds_command *cmd, return 0; } -static __le16 lbs_rate_to_fw_bitmap(int rate, int lower_rates_ok) -{ -/* Bit Rate -* 15:13 Reserved -* 12 54 Mbps -* 11 48 Mbps -* 10 36 Mbps -* 9 24 Mbps -* 8 18 Mbps -* 7 12 Mbps -* 6 9 Mbps -* 5 6 Mbps -* 4 Reserved -* 3 11 Mbps -* 2 5.5 Mbps -* 1 2 Mbps -* 0 1 Mbps -**/ - - uint16_t ratemask; - int i = lbs_data_rate_to_fw_index(rate); - if (lower_rates_ok) - ratemask = (0x1fef >> (12 - i)); - else - ratemask = (1 << i); - return cpu_to_le16(ratemask); -} - -int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, - uint16_t cmd_action) -{ - struct cmd_ds_802_11_rate_adapt_rateset cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - if (!priv->cur_rate && !priv->enablehwauto) - return -EINVAL; - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - - cmd.action = cpu_to_le16(cmd_action); - cmd.enablehwauto = cpu_to_le16(priv->enablehwauto); - cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto); - ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd); - if (!ret && cmd_action == CMD_ACT_GET) { - priv->ratebitmap = le16_to_cpu(cmd.bitmap); - priv->enablehwauto = le16_to_cpu(cmd.enablehwauto); - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_cmd_802_11_rate_adapt_rateset); - -/** - * @brief Set the data rate - * - * @param priv A pointer to struct lbs_private structure - * @param rate The desired data rate, or 0 to clear a locked rate - * - * @return 0 on success, error on failure - */ -int lbs_set_data_rate(struct lbs_private *priv, u8 rate) -{ - struct cmd_ds_802_11_data_rate cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - - if (rate > 0) { - cmd.action = cpu_to_le16(CMD_ACT_SET_TX_FIX_RATE); - cmd.rates[0] = lbs_data_rate_to_fw_index(rate); - if (cmd.rates[0] == 0) { - lbs_deb_cmd("DATA_RATE: invalid requested rate of" - " 0x%02X\n", rate); - ret = 0; - goto out; - } - lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n", cmd.rates[0]); - } else { - cmd.action = cpu_to_le16(CMD_ACT_SET_TX_AUTO); - lbs_deb_cmd("DATA_RATE: setting auto\n"); - } - - ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, &cmd); - if (ret) - goto out; - - lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof (cmd)); - - /* FIXME: get actual rates FW can do if this command actually returns - * all data rates supported. - */ - priv->cur_rate = lbs_fw_index_to_data_rate(cmd.rates[0]); - lbs_deb_cmd("DATA_RATE: current rate is 0x%02x\n", priv->cur_rate); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - /** * @brief Get the radio channel * @@ -804,7 +547,7 @@ out: * * @return The channel on success, error on failure */ -int lbs_get_channel(struct lbs_private *priv) +static int lbs_get_channel(struct lbs_private *priv) { struct cmd_ds_802_11_rf_channel cmd; int ret = 0; @@ -836,7 +579,7 @@ int lbs_update_channel(struct lbs_private *priv) ret = lbs_get_channel(priv); if (ret > 0) { - priv->curbssparams.channel = ret; + priv->channel = ret; ret = 0; } lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); @@ -855,7 +598,7 @@ int lbs_set_channel(struct lbs_private *priv, u8 channel) { struct cmd_ds_802_11_rf_channel cmd; #ifdef DEBUG - u8 old_channel = priv->curbssparams.channel; + u8 old_channel = priv->channel; #endif int ret = 0; @@ -870,36 +613,15 @@ int lbs_set_channel(struct lbs_private *priv, u8 channel) if (ret) goto out; - priv->curbssparams.channel = (uint8_t) le16_to_cpu(cmd.channel); + priv->channel = (uint8_t) le16_to_cpu(cmd.channel); lbs_deb_cmd("channel switch from %d to %d\n", old_channel, - priv->curbssparams.channel); + priv->channel); out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } -static int lbs_cmd_802_11_rssi(struct lbs_private *priv, - struct cmd_ds_command *cmd) -{ - - lbs_deb_enter(LBS_DEB_CMD); - cmd->command = cpu_to_le16(CMD_802_11_RSSI); - cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN); - cmd->params.rssi.N = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR); - - /* reset Beacon SNR/NF/RSSI values */ - priv->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; - priv->SNR[TYPE_BEACON][TYPE_AVG] = 0; - priv->NF[TYPE_BEACON][TYPE_NOAVG] = 0; - priv->NF[TYPE_BEACON][TYPE_AVG] = 0; - priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; - priv->RSSI[TYPE_BEACON][TYPE_AVG] = 0; - - lbs_deb_leave(LBS_DEB_CMD); - return 0; -} - static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr, u8 cmd_action, void *pdata_buf) { @@ -916,7 +638,7 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr, cmdptr->size = cpu_to_le16(sizeof (struct cmd_ds_mac_reg_access) - + S_DS_GEN); + + sizeof(struct cmd_header)); macreg = (struct cmd_ds_mac_reg_access *)&cmdptr->params. macreg; @@ -935,7 +657,7 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr, cmdptr->size = cpu_to_le16(sizeof (struct cmd_ds_bbp_reg_access) - + S_DS_GEN); + + sizeof(struct cmd_header)); bbpreg = (struct cmd_ds_bbp_reg_access *)&cmdptr->params. bbpreg; @@ -954,7 +676,7 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr, cmdptr->size = cpu_to_le16(sizeof (struct cmd_ds_rf_reg_access) + - S_DS_GEN); + sizeof(struct cmd_header)); rfreg = (struct cmd_ds_rf_reg_access *)&cmdptr->params. rfreg; @@ -974,192 +696,6 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr, return 0; } -static int lbs_cmd_bt_access(struct cmd_ds_command *cmd, - u16 cmd_action, void *pdata_buf) -{ - struct cmd_ds_bt_access *bt_access = &cmd->params.bt; - lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); - - cmd->command = cpu_to_le16(CMD_BT_ACCESS); - cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + S_DS_GEN); - cmd->result = 0; - bt_access->action = cpu_to_le16(cmd_action); - - switch (cmd_action) { - case CMD_ACT_BT_ACCESS_ADD: - memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN); - lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr", bt_access->addr1, 6); - break; - case CMD_ACT_BT_ACCESS_DEL: - memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN); - lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr", bt_access->addr1, 6); - break; - case CMD_ACT_BT_ACCESS_LIST: - bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); - break; - case CMD_ACT_BT_ACCESS_RESET: - break; - case CMD_ACT_BT_ACCESS_SET_INVERT: - bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); - break; - case CMD_ACT_BT_ACCESS_GET_INVERT: - break; - default: - break; - } - lbs_deb_leave(LBS_DEB_CMD); - return 0; -} - -static int lbs_cmd_fwt_access(struct cmd_ds_command *cmd, - u16 cmd_action, void *pdata_buf) -{ - struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt; - lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); - - cmd->command = cpu_to_le16(CMD_FWT_ACCESS); - cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + S_DS_GEN); - cmd->result = 0; - - if (pdata_buf) - memcpy(fwt_access, pdata_buf, sizeof(*fwt_access)); - else - memset(fwt_access, 0, sizeof(*fwt_access)); - - fwt_access->action = cpu_to_le16(cmd_action); - - lbs_deb_leave(LBS_DEB_CMD); - return 0; -} - -int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, - struct cmd_ds_mesh_access *cmd) -{ - int ret; - - lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); - - cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); - cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); - cmd->hdr.result = 0; - - cmd->action = cpu_to_le16(cmd_action); - - ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -static int __lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - u16 command = CMD_MESH_CONFIG_OLD; - - lbs_deb_enter(LBS_DEB_CMD); - - /* - * Command id is 0xac for v10 FW along with mesh interface - * id in bits 14-13-12. - */ - if (priv->mesh_fw_ver == MESH_FW_NEW) - command = CMD_MESH_CONFIG | - (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); - - cmd->hdr.command = cpu_to_le16(command); - cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); - cmd->hdr.result = 0; - - cmd->type = cpu_to_le16(type); - cmd->action = cpu_to_le16(action); - - ret = lbs_cmd_with_response(priv, command, cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - - if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) - return -EOPNOTSUPP; - - ret = __lbs_mesh_config_send(priv, cmd, action, type); - return ret; -} - -/* This function is the CMD_MESH_CONFIG legacy function. It only handles the - * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG - * are all handled by preparing a struct cmd_ds_mesh_config and passing it to - * lbs_mesh_config_send. - */ -int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_meshie *ie; - DECLARE_SSID_BUF(ssid); - - memset(&cmd, 0, sizeof(cmd)); - cmd.channel = cpu_to_le16(chan); - ie = (struct mrvl_meshie *)cmd.data; - - switch (action) { - case CMD_ACT_MESH_CONFIG_START: - ie->id = WLAN_EID_GENERIC; - ie->val.oui[0] = 0x00; - ie->val.oui[1] = 0x50; - ie->val.oui[2] = 0x43; - ie->val.type = MARVELL_MESH_IE_TYPE; - ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; - ie->val.version = MARVELL_MESH_IE_VERSION; - ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; - ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; - ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; - ie->val.mesh_id_len = priv->mesh_ssid_len; - memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); - ie->len = sizeof(struct mrvl_meshie_val) - - IW_ESSID_MAX_SIZE + priv->mesh_ssid_len; - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); - break; - case CMD_ACT_MESH_CONFIG_STOP: - break; - default: - return -1; - } - lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n", - action, priv->mesh_tlv, chan, - print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len)); - - return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); -} - -static int lbs_cmd_bcn_ctrl(struct lbs_private * priv, - struct cmd_ds_command *cmd, - u16 cmd_action) -{ - struct cmd_ds_802_11_beacon_control - *bcn_ctrl = &cmd->params.bcn_ctrl; - - lbs_deb_enter(LBS_DEB_CMD); - cmd->size = - cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control) - + S_DS_GEN); - cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL); - - bcn_ctrl->action = cpu_to_le16(cmd_action); - bcn_ctrl->beacon_enable = cpu_to_le16(priv->beacon_enable); - bcn_ctrl->beacon_period = cpu_to_le16(priv->beacon_period); - - lbs_deb_leave(LBS_DEB_CMD); - return 0; -} - static void lbs_queue_cmd(struct lbs_private *priv, struct cmd_ctrl_node *cmdnode) { @@ -1243,8 +779,17 @@ static void lbs_submit_command(struct lbs_private *priv, timeo = HZ/4; } - /* Setup the timer after transmit command */ - mod_timer(&priv->command_timer, jiffies + timeo); + if (command == CMD_802_11_DEEP_SLEEP) { + if (priv->is_auto_deep_sleep_enabled) { + priv->wakeup_dev_required = 1; + priv->dnld_sent = 0; + } + priv->is_deep_sleep = 1; + lbs_complete_command(priv, cmdnode, 0); + } else { + /* Setup the timer after transmit command */ + mod_timer(&priv->command_timer, jiffies + timeo); + } lbs_deb_leave(LBS_DEB_HOST); } @@ -1391,6 +936,11 @@ int lbs_prepare_and_send_command(struct lbs_private *priv, goto done; } + if (!lbs_is_cmd_allowed(priv)) { + ret = -EBUSY; + goto done; + } + cmdnode = lbs_get_cmd_ctrl_node(priv); if (cmdnode == NULL) { @@ -1441,7 +991,7 @@ int lbs_prepare_and_send_command(struct lbs_private *priv, cmdptr->command = cpu_to_le16(cmd_no); cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) + - S_DS_GEN); + sizeof(struct cmd_header)); memmove(&cmdptr->params.afc, pdata_buf, sizeof(struct cmd_ds_802_11_afc)); @@ -1449,45 +999,17 @@ int lbs_prepare_and_send_command(struct lbs_private *priv, ret = 0; goto done; - case CMD_802_11D_DOMAIN_INFO: - ret = lbs_cmd_802_11d_domain_info(priv, cmdptr, - cmd_no, cmd_action); - break; - case CMD_802_11_TPC_CFG: cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG); cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) + - S_DS_GEN); + sizeof(struct cmd_header)); memmove(&cmdptr->params.tpccfg, pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg)); ret = 0; break; - case CMD_802_11_LED_GPIO_CTRL: - { - struct mrvl_ie_ledgpio *gpio = - (struct mrvl_ie_ledgpio*) - cmdptr->params.ledgpio.data; - - memmove(&cmdptr->params.ledgpio, - pdata_buf, - sizeof(struct cmd_ds_802_11_led_ctrl)); - - cmdptr->command = - cpu_to_le16(CMD_802_11_LED_GPIO_CTRL); - -#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8 - cmdptr->size = - cpu_to_le16(le16_to_cpu(gpio->header.len) - + S_DS_GEN - + ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN); - gpio->header.len = gpio->header.len; - - ret = 0; - break; - } case CMD_BT_ACCESS: ret = lbs_cmd_bt_access(cmdptr, cmd_action, pdata_buf); @@ -1497,15 +1019,13 @@ int lbs_prepare_and_send_command(struct lbs_private *priv, ret = lbs_cmd_fwt_access(cmdptr, cmd_action, pdata_buf); break; - case CMD_GET_TSF: - cmdptr->command = cpu_to_le16(CMD_GET_TSF); - cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_get_tsf) + - S_DS_GEN); - ret = 0; - break; case CMD_802_11_BEACON_CTRL: ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action); break; + case CMD_802_11_DEEP_SLEEP: + cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP); + cmdptr->size = cpu_to_le16(sizeof(struct cmd_header)); + break; default: lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no); ret = -1; @@ -1823,30 +1343,6 @@ done: return ret; } -void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str) -{ - union iwreq_data iwrq; - u8 buf[50]; - - lbs_deb_enter(LBS_DEB_WEXT); - - memset(&iwrq, 0, sizeof(union iwreq_data)); - memset(buf, 0, sizeof(buf)); - - snprintf(buf, sizeof(buf) - 1, "%s", str); - - iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; - - /* Send Event to upper layer */ - lbs_deb_wext("event indication string %s\n", (char *)buf); - lbs_deb_wext("event indication length %d\n", iwrq.data.length); - lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str); - - wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf); - - lbs_deb_leave(LBS_DEB_WEXT); -} - static void lbs_send_confirmsleep(struct lbs_private *priv) { unsigned long flags; @@ -2024,7 +1520,7 @@ int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, } -static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, +struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), unsigned long callback_arg) @@ -2039,6 +1535,11 @@ static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, goto done; } + if (!lbs_is_cmd_allowed(priv)) { + cmdnode = ERR_PTR(-EBUSY); + goto done; + } + cmdnode = lbs_get_cmd_ctrl_node(priv); if (cmdnode == NULL) { lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); @@ -2117,5 +1618,3 @@ done: return ret; } EXPORT_SYMBOL_GPL(__lbs_cmd); - - diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h index 392e578ca095..2862748aef70 100644 --- a/drivers/net/wireless/libertas/cmd.h +++ b/drivers/net/wireless/libertas/cmd.h @@ -3,11 +3,30 @@ #ifndef _LBS_CMD_H_ #define _LBS_CMD_H_ -#include "hostcmd.h" +#include "host.h" #include "dev.h" + +/* Command & response transfer between host and card */ + +struct cmd_ctrl_node { + struct list_head list; + int result; + /* command response */ + int (*callback)(struct lbs_private *, + unsigned long, + struct cmd_header *); + unsigned long callback_arg; + /* command data */ + struct cmd_header *cmdbuf; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + + /* lbs_cmd() infers the size of the buffer to copy data back into, from - the size of the target of the pointer. Since the command to be sent + the size of the target of the pointer. Since the command to be sent may often be smaller, that size is set in cmd->size by the caller.*/ #define lbs_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ @@ -18,6 +37,11 @@ #define lbs_cmd_with_response(priv, cmdnr, cmd) \ lbs_cmd(priv, cmdnr, cmd, lbs_cmd_copyback, (unsigned long) (cmd)) +int lbs_prepare_and_send_command(struct lbs_private *priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf); + void lbs_cmd_async(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size); @@ -26,62 +50,93 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command, int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), unsigned long callback_arg); -int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, - int8_t p1, int8_t p2); +struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, + uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg); -int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, - int8_t p2, int usesnr); +int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, + struct cmd_header *resp); -int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, - int8_t p1, int8_t p2); +int lbs_allocate_cmd_buffer(struct lbs_private *priv); +int lbs_free_cmd_buffer(struct lbs_private *priv); -int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, - int8_t p2, int usesnr); +int lbs_execute_next_command(struct lbs_private *priv); +void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result); +int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); -int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, - struct cmd_header *resp); -int lbs_update_hw_spec(struct lbs_private *priv); +/* From cmdresp.c */ -int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, - struct cmd_ds_mesh_access *cmd); +void lbs_mac_event_disconnected(struct lbs_private *priv); -int lbs_set_data_rate(struct lbs_private *priv, u8 rate); -int lbs_get_channel(struct lbs_private *priv); + +/* Events */ + +int lbs_process_event(struct lbs_private *priv, u32 event); + + +/* Actual commands */ + +int lbs_update_hw_spec(struct lbs_private *priv); + int lbs_set_channel(struct lbs_private *priv, u8 channel); -int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type); -int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan); +int lbs_update_channel(struct lbs_private *priv); int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, struct wol_config *p_wol_config); -int lbs_suspend(struct lbs_private *priv); -void lbs_resume(struct lbs_private *priv); -int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, - uint16_t cmd_action); -int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv, - uint16_t cmd_action, uint16_t *timeout); int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, struct sleep_params *sp); -int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action, - struct assoc_request *assoc); -int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action, - uint16_t *enable); -int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action, - struct assoc_request *assoc); -int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, - s16 *maxlevel); -int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); +void lbs_ps_sleep(struct lbs_private *priv, int wait_option); + +void lbs_ps_wakeup(struct lbs_private *priv, int wait_option); + +void lbs_ps_confirm_sleep(struct lbs_private *priv); int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on); +void lbs_set_mac_control(struct lbs_private *priv); + +int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, + s16 *maxlevel); + int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val); int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val); + +/* Mesh related */ + +int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, + struct cmd_ds_mesh_access *cmd); + +int lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type); + +int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan); + + +/* Commands only used in wext.c, assoc. and scan.c */ + +int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, + int8_t p1, int8_t p2); + +int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, + int8_t p2, int usesnr); + +int lbs_set_data_rate(struct lbs_private *priv, u8 rate); + +int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, + uint16_t cmd_action); + +int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); + +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); + #endif /* _LBS_CMD_H */ diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index 23f684337fdd..21d57690c20a 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -11,6 +11,7 @@ #include "host.h" #include "decl.h" +#include "cmd.h" #include "defs.h" #include "dev.h" #include "assoc.h" @@ -26,23 +27,17 @@ */ void lbs_mac_event_disconnected(struct lbs_private *priv) { - union iwreq_data wrqu; - if (priv->connect_status != LBS_CONNECTED) return; lbs_deb_enter(LBS_DEB_ASSOC); - memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - /* * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. * It causes problem in the Supplicant */ - msleep_interruptible(1000); - wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); + lbs_send_disconnect_notification(priv); /* report disconnect to upper layer */ netif_stop_queue(priv->dev); @@ -67,7 +62,7 @@ void lbs_mac_event_disconnected(struct lbs_private *priv) * no longer valid. */ memset(&priv->curbssparams.bssid, 0, ETH_ALEN); - memset(&priv->curbssparams.ssid, 0, IW_ESSID_MAX_SIZE); + memset(&priv->curbssparams.ssid, 0, IEEE80211_MAX_SSID_LEN); priv->curbssparams.ssid_len = 0; if (priv->psstate != PS_STATE_FULL_POWER) { @@ -78,32 +73,6 @@ void lbs_mac_event_disconnected(struct lbs_private *priv) lbs_deb_leave(LBS_DEB_ASSOC); } -/** - * @brief This function handles MIC failure event. - * - * @param priv A pointer to struct lbs_private structure - * @para event the event id - * @return n/a - */ -static void handle_mic_failureevent(struct lbs_private *priv, u32 event) -{ - char buf[50]; - - lbs_deb_enter(LBS_DEB_CMD); - memset(buf, 0, sizeof(buf)); - - sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); - - if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) { - strcat(buf, "unicast "); - } else { - strcat(buf, "multicast "); - } - - lbs_send_iwevcustom_event(priv, buf); - lbs_deb_leave(LBS_DEB_CMD); -} - static int lbs_ret_reg_access(struct lbs_private *priv, u16 type, struct cmd_ds_command *resp) { @@ -147,53 +116,6 @@ static int lbs_ret_reg_access(struct lbs_private *priv, return ret; } -static int lbs_ret_802_11_rssi(struct lbs_private *priv, - struct cmd_ds_command *resp) -{ - struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; - - lbs_deb_enter(LBS_DEB_CMD); - - /* store the non average value */ - priv->SNR[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->SNR); - priv->NF[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->noisefloor); - - priv->SNR[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgSNR); - priv->NF[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgnoisefloor); - - priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = - CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG], - priv->NF[TYPE_BEACON][TYPE_NOAVG]); - - priv->RSSI[TYPE_BEACON][TYPE_AVG] = - CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, - priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); - - lbs_deb_cmd("RSSI: beacon %d, avg %d\n", - priv->RSSI[TYPE_BEACON][TYPE_NOAVG], - priv->RSSI[TYPE_BEACON][TYPE_AVG]); - - lbs_deb_leave(LBS_DEB_CMD); - return 0; -} - -static int lbs_ret_802_11_bcn_ctrl(struct lbs_private * priv, - struct cmd_ds_command *resp) -{ - struct cmd_ds_802_11_beacon_control *bcn_ctrl = - &resp->params.bcn_ctrl; - - lbs_deb_enter(LBS_DEB_CMD); - - if (bcn_ctrl->action == CMD_ACT_GET) { - priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable); - priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period); - } - - lbs_deb_enter(LBS_DEB_CMD); - return 0; -} - static inline int handle_cmd_response(struct lbs_private *priv, struct cmd_header *cmd_response) { @@ -227,29 +149,13 @@ static inline int handle_cmd_response(struct lbs_private *priv, ret = lbs_ret_802_11_rssi(priv, resp); break; - case CMD_RET(CMD_802_11D_DOMAIN_INFO): - ret = lbs_ret_802_11d_domain_info(resp); - break; - case CMD_RET(CMD_802_11_TPC_CFG): spin_lock_irqsave(&priv->driver_lock, flags); memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg, sizeof(struct cmd_ds_802_11_tpc_cfg)); spin_unlock_irqrestore(&priv->driver_lock, flags); break; - case CMD_RET(CMD_802_11_LED_GPIO_CTRL): - spin_lock_irqsave(&priv->driver_lock, flags); - memmove((void *)priv->cur_cmd->callback_arg, &resp->params.ledgpio, - sizeof(struct cmd_ds_802_11_led_ctrl)); - spin_unlock_irqrestore(&priv->driver_lock, flags); - break; - case CMD_RET(CMD_GET_TSF): - spin_lock_irqsave(&priv->driver_lock, flags); - memcpy((void *)priv->cur_cmd->callback_arg, - &resp->params.gettsf.tsfvalue, sizeof(u64)); - spin_unlock_irqrestore(&priv->driver_lock, flags); - break; case CMD_RET(CMD_BT_ACCESS): spin_lock_irqsave(&priv->driver_lock, flags); if (priv->cur_cmd->callback_arg) @@ -505,9 +411,21 @@ int lbs_process_event(struct lbs_private *priv, u32 event) case MACREG_INT_CODE_HOST_AWAKE: lbs_deb_cmd("EVENT: host awake\n"); + if (priv->reset_deep_sleep_wakeup) + priv->reset_deep_sleep_wakeup(priv); + priv->is_deep_sleep = 0; lbs_send_confirmwake(priv); break; + case MACREG_INT_CODE_DEEP_SLEEP_AWAKE: + if (priv->reset_deep_sleep_wakeup) + priv->reset_deep_sleep_wakeup(priv); + lbs_deb_cmd("EVENT: ds awake\n"); + priv->is_deep_sleep = 0; + priv->wakeup_dev_required = 0; + wake_up_interruptible(&priv->ds_awake_q); + break; + case MACREG_INT_CODE_PS_AWAKE: lbs_deb_cmd("EVENT: ps awake\n"); /* handle unexpected PS AWAKE event */ @@ -533,12 +451,12 @@ int lbs_process_event(struct lbs_private *priv, u32 event) case MACREG_INT_CODE_MIC_ERR_UNICAST: lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); - handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST); + lbs_send_mic_failureevent(priv, event); break; case MACREG_INT_CODE_MIC_ERR_MULTICAST: lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); - handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST); + lbs_send_mic_failureevent(priv, event); break; case MACREG_INT_CODE_MIB_CHANGED: diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index 893a55ca344a..587b0cb0088d 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c @@ -451,10 +451,12 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf, CMD_MAC_REG_ACCESS, 0, CMD_OPTION_WAITFORRSP, 0, &offval); mdelay(10); - pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n", + if (!ret) { + pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n", priv->mac_offset, priv->offsetvalue.value); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } free_page(addr); return ret; } @@ -514,7 +516,8 @@ static ssize_t lbs_wrmac_write(struct file *file, CMD_OPTION_WAITFORRSP, 0, &offval); mdelay(10); - res = count; + if (!res) + res = count; out_unlock: free_page(addr); return res; @@ -539,10 +542,12 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf, CMD_BBP_REG_ACCESS, 0, CMD_OPTION_WAITFORRSP, 0, &offval); mdelay(10); - pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n", + if (!ret) { + pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n", priv->bbp_offset, priv->offsetvalue.value); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } free_page(addr); return ret; @@ -603,7 +608,8 @@ static ssize_t lbs_wrbbp_write(struct file *file, CMD_OPTION_WAITFORRSP, 0, &offval); mdelay(10); - res = count; + if (!res) + res = count; out_unlock: free_page(addr); return res; @@ -628,10 +634,12 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf, CMD_RF_REG_ACCESS, 0, CMD_OPTION_WAITFORRSP, 0, &offval); mdelay(10); - pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n", + if (!ret) { + pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n", priv->rf_offset, priv->offsetvalue.value); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } free_page(addr); return ret; @@ -692,7 +700,8 @@ static ssize_t lbs_wrrf_write(struct file *file, CMD_OPTION_WAITFORRSP, 0, &offval); mdelay(10); - res = count; + if (!res) + res = count; out_unlock: free_page(addr); return res; diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h index 8b15380ae6e1..709ffcad22ad 100644 --- a/drivers/net/wireless/libertas/decl.h +++ b/drivers/net/wireless/libertas/decl.h @@ -8,71 +8,46 @@ #include <linux/netdevice.h> -#include "defs.h" -/** Function Prototype Declaration */ struct lbs_private; struct sk_buff; struct net_device; -struct cmd_ctrl_node; -struct cmd_ds_command; -void lbs_set_mac_control(struct lbs_private *priv); -void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count); - -int lbs_free_cmd_buffer(struct lbs_private *priv); - -int lbs_prepare_and_send_command(struct lbs_private *priv, - u16 cmd_no, - u16 cmd_action, - u16 wait_option, u32 cmd_oid, void *pdata_buf); +/* ethtool.c */ +extern const struct ethtool_ops lbs_ethtool_ops; -int lbs_allocate_cmd_buffer(struct lbs_private *priv); -int lbs_execute_next_command(struct lbs_private *priv); -int lbs_process_event(struct lbs_private *priv, u32 event); -void lbs_queue_event(struct lbs_private *priv, u32 event); -void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx); -u32 lbs_fw_index_to_data_rate(u8 index); -u8 lbs_data_rate_to_fw_index(u32 rate); - -/** The proc fs interface */ -int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); -void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result); +/* tx.c */ +void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count); netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); -int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band); +/* rx.c */ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *); -void lbs_ps_sleep(struct lbs_private *priv, int wait_option); -void lbs_ps_confirm_sleep(struct lbs_private *priv); -void lbs_ps_wakeup(struct lbs_private *priv, int wait_option); - -struct chan_freq_power *lbs_find_cfp_by_band_and_channel( - struct lbs_private *priv, - u8 band, - u16 channel); - -void lbs_mac_event_disconnected(struct lbs_private *priv); - -void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str); - -/* persistcfg.c */ -void lbs_persist_config_init(struct net_device *net); -void lbs_persist_config_remove(struct net_device *net); /* main.c */ -struct chan_freq_power *lbs_get_region_cfp_table(u8 region, - int *cfp_no); struct lbs_private *lbs_add_card(void *card, struct device *dmdev); void lbs_remove_card(struct lbs_private *priv); int lbs_start_card(struct lbs_private *priv); void lbs_stop_card(struct lbs_private *priv); void lbs_host_to_card_done(struct lbs_private *priv); -int lbs_update_channel(struct lbs_private *priv); +int lbs_set_mac_address(struct net_device *dev, void *addr); +void lbs_set_multicast_list(struct net_device *dev); + +int lbs_suspend(struct lbs_private *priv); +void lbs_resume(struct lbs_private *priv); + +void lbs_queue_event(struct lbs_private *priv, u32 event); +void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx); + +int lbs_enter_auto_deep_sleep(struct lbs_private *priv); +int lbs_exit_auto_deep_sleep(struct lbs_private *priv); + +u32 lbs_fw_index_to_data_rate(u8 index); +u8 lbs_data_rate_to_fw_index(u32 rate); + #endif diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index 72f3479a4d70..6b6ea9f7bf5b 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -42,6 +42,7 @@ #define LBS_DEB_SDIO 0x00400000 #define LBS_DEB_SYSFS 0x00800000 #define LBS_DEB_SPI 0x01000000 +#define LBS_DEB_CFG80211 0x02000000 extern unsigned int lbs_debug; @@ -86,6 +87,7 @@ do { if ((lbs_debug & (grp)) == (grp)) \ #define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args) #define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args) #define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args) +#define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) #define lbs_pr_info(format, args...) \ printk(KERN_INFO DRV_NAME": " format, ## args) @@ -320,7 +322,6 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in extern const char lbs_driver_version[]; extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE]; -extern u8 lbs_bg_rates[MAX_RATES]; /** ENUM definition*/ /** SNRNF_TYPE */ diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index d3b69a4b4b5e..6a8d2b291d8c 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -6,75 +6,11 @@ #ifndef _LBS_DEV_H_ #define _LBS_DEV_H_ -#include <linux/netdevice.h> -#include <linux/wireless.h> -#include <linux/ethtool.h> -#include <linux/debugfs.h> +#include "mesh.h" +#include "scan.h" +#include "assoc.h" -#include "defs.h" -#include "hostcmd.h" -extern const struct ethtool_ops lbs_ethtool_ops; - -#define MAX_BSSID_PER_CHANNEL 16 - -#define NR_TX_QUEUE 3 - -/* For the extended Scan */ -#define MAX_EXTENDED_SCAN_BSSID_LIST MAX_BSSID_PER_CHANNEL * \ - MRVDRV_MAX_CHANNEL_SIZE + 1 - -#define MAX_REGION_CHANNEL_NUM 2 - -/** Chan-freq-TxPower mapping table*/ -struct chan_freq_power { - /** channel Number */ - u16 channel; - /** frequency of this channel */ - u32 freq; - /** Max allowed Tx power level */ - u16 maxtxpower; - /** TRUE:channel unsupported; FLASE:supported*/ - u8 unsupported; -}; - -/** region-band mapping table*/ -struct region_channel { - /** TRUE if this entry is valid */ - u8 valid; - /** region code for US, Japan ... */ - u8 region; - /** band B/G/A, used for BAND_CONFIG cmd */ - u8 band; - /** Actual No. of elements in the array below */ - u8 nrcfp; - /** chan-freq-txpower mapping table*/ - struct chan_freq_power *CFP; -}; - -struct lbs_802_11_security { - u8 WPAenabled; - u8 WPA2enabled; - u8 wep_enabled; - u8 auth_mode; - u32 key_mgmt; -}; - -/** Current Basic Service Set State Structure */ -struct current_bss_params { - /** bssid */ - u8 bssid[ETH_ALEN]; - /** ssid */ - u8 ssid[IW_ESSID_MAX_SIZE + 1]; - u8 ssid_len; - - /** band */ - u8 band; - /** channel */ - u8 channel; - /** zero-terminated array of supported data rates */ - u8 rates[MAX_RATES + 1]; -}; /** sleep_params */ struct sleep_params { @@ -86,109 +22,99 @@ struct sleep_params { uint16_t sp_reserved; }; -/* Mesh statistics */ -struct lbs_mesh_stats { - u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ - u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ - u32 fwd_drop_ttl; /* Fwd: TTL zero */ - u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ - u32 fwd_drop_noroute; /* Fwd: No route to Destination */ - u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ - u32 drop_blind; /* Rx: Dropped by blinding table */ - u32 tx_failed_cnt; /* Tx: Failed transmissions */ -}; /** Private structure for the MV device */ struct lbs_private { - int mesh_open; - int mesh_fw_ver; - int infra_open; - int mesh_autostart_enabled; - char name[DEV_NAME_LEN]; - - void *card; + /* Basic networking */ struct net_device *dev; + u32 connect_status; + int infra_open; + struct work_struct mcast_work; + u32 nr_of_multicastmacaddr; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + + /* CFG80211 */ + struct wireless_dev *wdev; + /* Mesh */ struct net_device *mesh_dev; /* Virtual device */ + u32 mesh_connect_status; + struct lbs_mesh_stats mstats; + int mesh_open; + int mesh_fw_ver; + int mesh_autostart_enabled; + uint16_t mesh_tlv; + u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 mesh_ssid_len; + struct work_struct sync_channel; + + /* Monitor mode */ struct net_device *rtap_net_dev; + u32 monitormode; - struct iw_statistics wstats; - struct lbs_mesh_stats mstats; + /* Debugfs */ struct dentry *debugfs_dir; struct dentry *debugfs_debug; struct dentry *debugfs_files[6]; - struct dentry *events_dir; struct dentry *debugfs_events_files[6]; - struct dentry *regs_dir; struct dentry *debugfs_regs_files[6]; + /* Hardware debugging */ u32 mac_offset; u32 bbp_offset; u32 rf_offset; + struct lbs_offset_value offsetvalue; - /* Download sent: - bit0 1/0=data_sent/data_tx_done, - bit1 1/0=cmd_sent/cmd_tx_done, - all other bits reserved 0 */ - u8 dnld_sent; - - /** thread to service interrupts */ - struct task_struct *main_thread; - wait_queue_head_t waitq; - struct workqueue_struct *work_thread; - - struct work_struct mcast_work; + /* Power management */ + u16 psmode; + u32 psstate; + u8 needtowakeup; - /** Scanning */ - struct delayed_work scan_work; - struct delayed_work assoc_work; - struct work_struct sync_channel; - /* remember which channel was scanned last, != 0 if currently scanning */ - int scan_channel; - u8 scan_ssid[IW_ESSID_MAX_SIZE + 1]; - u8 scan_ssid_len; + /* Deep sleep */ + int is_deep_sleep; + int is_auto_deep_sleep_enabled; + int wakeup_dev_required; + int is_activity_detected; + int auto_deep_sleep_timeout; /* in ms */ + wait_queue_head_t ds_awake_q; + struct timer_list auto_deepsleep_timer; - /** Hardware access */ + /* Hardware access */ + void *card; + u8 fw_ready; + u8 surpriseremoved; int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); void (*reset_card) (struct lbs_private *priv); + int (*enter_deep_sleep) (struct lbs_private *priv); + int (*exit_deep_sleep) (struct lbs_private *priv); + int (*reset_deep_sleep_wakeup) (struct lbs_private *priv); - /* Wake On LAN */ - uint32_t wol_criteria; - uint8_t wol_gpio; - uint8_t wol_gap; - - /** Wlan adapter data structure*/ - /** STATUS variables */ + /* Adapter info (from EEPROM) */ u32 fwrelease; u32 fwcapinfo; + u16 regioncode; + u8 current_addr[ETH_ALEN]; - struct mutex lock; - - /* TX packet ready to be sent... */ - int tx_pending_len; /* -1 while building packet */ - - u8 tx_pending_buf[LBS_UPLD_SIZE]; - /* protected by hard_start_xmit serialization */ - - /** command-related variables */ + /* Command download */ + u8 dnld_sent; + /* bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ u16 seqnum; - struct cmd_ctrl_node *cmd_array; - /** Current command */ struct cmd_ctrl_node *cur_cmd; - int cur_cmd_retcode; - /** command Queues */ - /** Free command buffers */ - struct list_head cmdfreeq; - /** Pending command buffers */ - struct list_head cmdpendingq; - + struct list_head cmdfreeq; /* free command buffers */ + struct list_head cmdpendingq; /* pending command buffers */ wait_queue_head_t cmd_pending; + struct timer_list command_timer; + int nr_retries; + int cmd_timed_out; /* Command responses sent from the hardware to the driver */ + int cur_cmd_retcode; u8 resp_idx; u8 resp_buf[2][LBS_UPLD_SIZE]; u32 resp_len[2]; @@ -196,95 +122,76 @@ struct lbs_private { /* Events sent from hardware to driver */ struct kfifo *event_fifo; - /* nickname */ - u8 nodename[16]; - - /** spin locks */ - spinlock_t driver_lock; - - /** Timers */ - struct timer_list command_timer; - int nr_retries; - int cmd_timed_out; - - /** current ssid/bssid related parameters*/ - struct current_bss_params curbssparams; - - uint16_t mesh_tlv; - u8 mesh_ssid[IW_ESSID_MAX_SIZE + 1]; - u8 mesh_ssid_len; - - /* IW_MODE_* */ - u8 mode; - - /* Scan results list */ - struct list_head network_list; - struct list_head network_free_list; - struct bss_descriptor *networks; - - u16 beacon_period; - u8 beacon_enable; - u8 adhoccreate; - - /** capability Info used in Association, start, join */ - u16 capability; - - /** MAC address information */ - u8 current_addr[ETH_ALEN]; - u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; - u32 nr_of_multicastmacaddr; + /** thread to service interrupts */ + struct task_struct *main_thread; + wait_queue_head_t waitq; + struct workqueue_struct *work_thread; - /** 802.11 statistics */ -// struct cmd_DS_802_11_GET_STAT wlan802_11Stat; + /** Encryption stuff */ + struct lbs_802_11_security secinfo; + struct enc_key wpa_mcast_key; + struct enc_key wpa_unicast_key; + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; + u16 wep_tx_keyidx; + struct enc_key wep_keys[4]; - uint16_t enablehwauto; - uint16_t ratebitmap; + /* Wake On LAN */ + uint32_t wol_criteria; + uint8_t wol_gpio; + uint8_t wol_gap; + /* Transmitting */ + int tx_pending_len; /* -1 while building packet */ + u8 tx_pending_buf[LBS_UPLD_SIZE]; + /* protected by hard_start_xmit serialization */ u8 txretrycount; - - /** Tx-related variables (for single packet tx) */ struct sk_buff *currenttxskb; - /** NIC Operation characteristics */ + /* Locks */ + struct mutex lock; + spinlock_t driver_lock; + + /* NIC/link operation characteristics */ u16 mac_control; - u32 connect_status; - u32 mesh_connect_status; - u16 regioncode; + u8 radio_on; + u8 channel; s16 txpower_cur; s16 txpower_min; s16 txpower_max; - /** POWER MANAGEMENT AND PnP SUPPORT */ - u8 surpriseremoved; - - u16 psmode; /* Wlan802_11PowermodeCAM=disable - Wlan802_11PowermodeMAX_PSP=enable */ - u32 psstate; - u8 needtowakeup; + /** Scanning */ + struct delayed_work scan_work; + int scan_channel; + /* remember which channel was scanned last, != 0 if currently scanning */ + u8 scan_ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 scan_ssid_len; + /* Associating */ + struct delayed_work assoc_work; + struct current_bss_params curbssparams; + u8 mode; + struct list_head network_list; + struct list_head network_free_list; + struct bss_descriptor *networks; struct assoc_request * pending_assoc_req; struct assoc_request * in_progress_assoc_req; + u16 capability; + uint16_t enablehwauto; + uint16_t ratebitmap; - /** Encryption parameter */ - struct lbs_802_11_security secinfo; - - /** WEP keys */ - struct enc_key wep_keys[4]; - u16 wep_tx_keyidx; - - /** WPA keys */ - struct enc_key wpa_mcast_key; - struct enc_key wpa_unicast_key; - -/* - * In theory, the IE is limited to the IE length, 255, - * but in practice 64 bytes are enough. - */ -#define MAX_WPA_IE_LEN 64 + /* ADHOC */ + u16 beacon_period; + u8 beacon_enable; + u8 adhoccreate; - /** WPA Information Elements*/ - u8 wpa_ie[MAX_WPA_IE_LEN]; - u8 wpa_ie_len; + /* WEXT */ + char name[DEV_NAME_LEN]; + u8 nodename[16]; + struct iw_statistics wstats; + u8 cur_rate; +#define MAX_REGION_CHANNEL_NUM 2 + struct region_channel region_channel[MAX_REGION_CHANNEL_NUM]; /** Requested Signal Strength*/ u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG]; @@ -294,116 +201,8 @@ struct lbs_private { u8 rawNF[DEFAULT_DATA_AVG_FACTOR]; u16 nextSNRNF; u16 numSNRNF; - - u8 radio_on; - - /** data rate stuff */ - u8 cur_rate; - - /** RF calibration data */ - -#define MAX_REGION_CHANNEL_NUM 2 - /** region channel data */ - struct region_channel region_channel[MAX_REGION_CHANNEL_NUM]; - - struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM]; - - /** 11D and Domain Regulatory Data */ - struct lbs_802_11d_domain_reg domainreg; - struct parsed_region_chan_11d parsed_region_chan; - - /** FSM variable for 11d support */ - u32 enable11d; - - /** MISCELLANEOUS */ - struct lbs_offset_value offsetvalue; - - u32 monitormode; - u8 fw_ready; }; extern struct cmd_confirm_sleep confirm_sleep; -/** - * @brief Structure used to store information for each beacon/probe response - */ -struct bss_descriptor { - u8 bssid[ETH_ALEN]; - - u8 ssid[IW_ESSID_MAX_SIZE + 1]; - u8 ssid_len; - - u16 capability; - u32 rssi; - u32 channel; - u16 beaconperiod; - __le16 atimwindow; - - /* IW_MODE_AUTO, IW_MODE_ADHOC, IW_MODE_INFRA */ - u8 mode; - - /* zero-terminated array of supported data rates */ - u8 rates[MAX_RATES + 1]; - - unsigned long last_scanned; - - union ieee_phy_param_set phy; - union ieee_ss_param_set ss; - - struct ieee_ie_country_info_full_set countryinfo; - - u8 wpa_ie[MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - u8 rsn_ie[MAX_WPA_IE_LEN]; - size_t rsn_ie_len; - - u8 mesh; - - struct list_head list; -}; - -/** Association request - * - * Encapsulates all the options that describe a specific assocation request - * or configuration of the wireless card's radio, mode, and security settings. - */ -struct assoc_request { -#define ASSOC_FLAG_SSID 1 -#define ASSOC_FLAG_CHANNEL 2 -#define ASSOC_FLAG_BAND 3 -#define ASSOC_FLAG_MODE 4 -#define ASSOC_FLAG_BSSID 5 -#define ASSOC_FLAG_WEP_KEYS 6 -#define ASSOC_FLAG_WEP_TX_KEYIDX 7 -#define ASSOC_FLAG_WPA_MCAST_KEY 8 -#define ASSOC_FLAG_WPA_UCAST_KEY 9 -#define ASSOC_FLAG_SECINFO 10 -#define ASSOC_FLAG_WPA_IE 11 - unsigned long flags; - - u8 ssid[IW_ESSID_MAX_SIZE + 1]; - u8 ssid_len; - u8 channel; - u8 band; - u8 mode; - u8 bssid[ETH_ALEN] __attribute__ ((aligned (2))); - - /** WEP keys */ - struct enc_key wep_keys[4]; - u16 wep_tx_keyidx; - - /** WPA keys */ - struct enc_key wpa_mcast_key; - struct enc_key wpa_unicast_key; - - struct lbs_802_11_security secinfo; - - /** WPA Information Elements*/ - u8 wpa_ie[MAX_WPA_IE_LEN]; - u8 wpa_ie_len; - - /* BSS to associate with for infrastructure of Ad-Hoc join */ - struct bss_descriptor bss; -}; - #endif diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c index 53d56ab83c03..63d020374c2b 100644 --- a/drivers/net/wireless/libertas/ethtool.c +++ b/drivers/net/wireless/libertas/ethtool.c @@ -8,17 +8,8 @@ #include "dev.h" #include "wext.h" #include "cmd.h" +#include "mesh.h" -static const char * mesh_stat_strings[]= { - "drop_duplicate_bcast", - "drop_ttl_zero", - "drop_no_fwd_route", - "drop_no_buffers", - "fwded_unicast_cnt", - "fwded_bcast_cnt", - "drop_blind_table", - "tx_failed_cnt" -}; static void lbs_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) @@ -73,73 +64,6 @@ out: return ret; } -static void lbs_ethtool_get_stats(struct net_device *dev, - struct ethtool_stats *stats, uint64_t *data) -{ - struct lbs_private *priv = dev->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - - lbs_deb_enter(LBS_DEB_ETHTOOL); - - /* Get Mesh Statistics */ - ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access); - - if (ret) { - memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t))); - return; - } - - priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]); - priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]); - priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]); - priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]); - priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]); - priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]); - priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]); - priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]); - - data[0] = priv->mstats.fwd_drop_rbt; - data[1] = priv->mstats.fwd_drop_ttl; - data[2] = priv->mstats.fwd_drop_noroute; - data[3] = priv->mstats.fwd_drop_nobuf; - data[4] = priv->mstats.fwd_unicast_cnt; - data[5] = priv->mstats.fwd_bcast_cnt; - data[6] = priv->mstats.drop_blind; - data[7] = priv->mstats.tx_failed_cnt; - - lbs_deb_enter(LBS_DEB_ETHTOOL); -} - -static int lbs_ethtool_get_sset_count(struct net_device *dev, int sset) -{ - struct lbs_private *priv = dev->ml_priv; - - if (sset == ETH_SS_STATS && dev == priv->mesh_dev) - return MESH_STATS_NUM; - - return -EOPNOTSUPP; -} - -static void lbs_ethtool_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *s) -{ - int i; - - lbs_deb_enter(LBS_DEB_ETHTOOL); - - switch (stringset) { - case ETH_SS_STATS: - for (i=0; i < MESH_STATS_NUM; i++) { - memcpy(s + i * ETH_GSTRING_LEN, - mesh_stat_strings[i], - ETH_GSTRING_LEN); - } - break; - } - lbs_deb_enter(LBS_DEB_ETHTOOL); -} - static void lbs_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { @@ -190,9 +114,9 @@ const struct ethtool_ops lbs_ethtool_ops = { .get_drvinfo = lbs_ethtool_get_drvinfo, .get_eeprom = lbs_ethtool_get_eeprom, .get_eeprom_len = lbs_ethtool_get_eeprom_len, - .get_sset_count = lbs_ethtool_get_sset_count, - .get_ethtool_stats = lbs_ethtool_get_stats, - .get_strings = lbs_ethtool_get_strings, + .get_sset_count = lbs_mesh_ethtool_get_sset_count, + .get_ethtool_stats = lbs_mesh_ethtool_get_stats, + .get_strings = lbs_mesh_ethtool_get_strings, .get_wol = lbs_ethtool_get_wol, .set_wol = lbs_ethtool_set_wol, }; diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h index fe8f0cb737bc..3809c0b49464 100644 --- a/drivers/net/wireless/libertas/host.h +++ b/drivers/net/wireless/libertas/host.h @@ -1,201 +1,190 @@ /** - * This file contains definitions of WLAN commands. + * This file function prototypes, data structure + * and definitions for all the host/station commands */ #ifndef _LBS_HOST_H_ #define _LBS_HOST_H_ -/** PUBLIC DEFINITIONS */ -#define DEFAULT_AD_HOC_CHANNEL 6 -#define DEFAULT_AD_HOC_CHANNEL_A 36 +#include "types.h" +#include "defs.h" -#define CMD_OPTION_WAITFORRSP 0x0002 +#define DEFAULT_AD_HOC_CHANNEL 6 + +#define CMD_OPTION_WAITFORRSP 0x0002 /** Host command IDs */ /* Return command are almost always the same as the host command, but with * bit 15 set high. There are a few exceptions, though... */ -#define CMD_RET(cmd) (0x8000 | cmd) +#define CMD_RET(cmd) (0x8000 | cmd) /* Return command convention exceptions: */ -#define CMD_RET_802_11_ASSOCIATE 0x8012 +#define CMD_RET_802_11_ASSOCIATE 0x8012 /* Command codes */ -#define CMD_GET_HW_SPEC 0x0003 -#define CMD_EEPROM_UPDATE 0x0004 -#define CMD_802_11_RESET 0x0005 -#define CMD_802_11_SCAN 0x0006 -#define CMD_802_11_GET_LOG 0x000b -#define CMD_MAC_MULTICAST_ADR 0x0010 -#define CMD_802_11_AUTHENTICATE 0x0011 -#define CMD_802_11_EEPROM_ACCESS 0x0059 -#define CMD_802_11_ASSOCIATE 0x0050 -#define CMD_802_11_SET_WEP 0x0013 -#define CMD_802_11_GET_STAT 0x0014 -#define CMD_802_3_GET_STAT 0x0015 -#define CMD_802_11_SNMP_MIB 0x0016 -#define CMD_MAC_REG_MAP 0x0017 -#define CMD_BBP_REG_MAP 0x0018 -#define CMD_MAC_REG_ACCESS 0x0019 -#define CMD_BBP_REG_ACCESS 0x001a -#define CMD_RF_REG_ACCESS 0x001b -#define CMD_802_11_RADIO_CONTROL 0x001c -#define CMD_802_11_RF_CHANNEL 0x001d -#define CMD_802_11_RF_TX_POWER 0x001e -#define CMD_802_11_RSSI 0x001f -#define CMD_802_11_RF_ANTENNA 0x0020 -#define CMD_802_11_PS_MODE 0x0021 -#define CMD_802_11_DATA_RATE 0x0022 -#define CMD_RF_REG_MAP 0x0023 -#define CMD_802_11_DEAUTHENTICATE 0x0024 -#define CMD_802_11_REASSOCIATE 0x0025 -#define CMD_MAC_CONTROL 0x0028 -#define CMD_802_11_AD_HOC_START 0x002b -#define CMD_802_11_AD_HOC_JOIN 0x002c -#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e -#define CMD_802_11_ENABLE_RSN 0x002f -#define CMD_802_11_SET_AFC 0x003c -#define CMD_802_11_GET_AFC 0x003d -#define CMD_802_11_AD_HOC_STOP 0x0040 -#define CMD_802_11_HOST_SLEEP_CFG 0x0043 -#define CMD_802_11_WAKEUP_CONFIRM 0x0044 -#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045 -#define CMD_802_11_BEACON_STOP 0x0049 -#define CMD_802_11_MAC_ADDRESS 0x004d -#define CMD_802_11_LED_GPIO_CTRL 0x004e -#define CMD_802_11_EEPROM_ACCESS 0x0059 -#define CMD_802_11_BAND_CONFIG 0x0058 -#define CMD_GSPI_BUS_CONFIG 0x005a -#define CMD_802_11D_DOMAIN_INFO 0x005b -#define CMD_802_11_KEY_MATERIAL 0x005e -#define CMD_802_11_SLEEP_PARAMS 0x0066 -#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067 -#define CMD_802_11_SLEEP_PERIOD 0x0068 -#define CMD_802_11_TPC_CFG 0x0072 -#define CMD_802_11_PA_CFG 0x0073 -#define CMD_802_11_FW_WAKE_METHOD 0x0074 -#define CMD_802_11_SUBSCRIBE_EVENT 0x0075 -#define CMD_802_11_RATE_ADAPT_RATESET 0x0076 -#define CMD_802_11_TX_RATE_QUERY 0x007f -#define CMD_GET_TSF 0x0080 -#define CMD_BT_ACCESS 0x0087 -#define CMD_FWT_ACCESS 0x0095 -#define CMD_802_11_MONITOR_MODE 0x0098 -#define CMD_MESH_ACCESS 0x009b -#define CMD_MESH_CONFIG_OLD 0x00a3 -#define CMD_MESH_CONFIG 0x00ac -#define CMD_SET_BOOT2_VER 0x00a5 -#define CMD_FUNC_INIT 0x00a9 -#define CMD_FUNC_SHUTDOWN 0x00aa -#define CMD_802_11_BEACON_CTRL 0x00b0 +#define CMD_GET_HW_SPEC 0x0003 +#define CMD_EEPROM_UPDATE 0x0004 +#define CMD_802_11_RESET 0x0005 +#define CMD_802_11_SCAN 0x0006 +#define CMD_802_11_GET_LOG 0x000b +#define CMD_MAC_MULTICAST_ADR 0x0010 +#define CMD_802_11_AUTHENTICATE 0x0011 +#define CMD_802_11_EEPROM_ACCESS 0x0059 +#define CMD_802_11_ASSOCIATE 0x0050 +#define CMD_802_11_SET_WEP 0x0013 +#define CMD_802_11_GET_STAT 0x0014 +#define CMD_802_3_GET_STAT 0x0015 +#define CMD_802_11_SNMP_MIB 0x0016 +#define CMD_MAC_REG_MAP 0x0017 +#define CMD_BBP_REG_MAP 0x0018 +#define CMD_MAC_REG_ACCESS 0x0019 +#define CMD_BBP_REG_ACCESS 0x001a +#define CMD_RF_REG_ACCESS 0x001b +#define CMD_802_11_RADIO_CONTROL 0x001c +#define CMD_802_11_RF_CHANNEL 0x001d +#define CMD_802_11_RF_TX_POWER 0x001e +#define CMD_802_11_RSSI 0x001f +#define CMD_802_11_RF_ANTENNA 0x0020 +#define CMD_802_11_PS_MODE 0x0021 +#define CMD_802_11_DATA_RATE 0x0022 +#define CMD_RF_REG_MAP 0x0023 +#define CMD_802_11_DEAUTHENTICATE 0x0024 +#define CMD_802_11_REASSOCIATE 0x0025 +#define CMD_MAC_CONTROL 0x0028 +#define CMD_802_11_AD_HOC_START 0x002b +#define CMD_802_11_AD_HOC_JOIN 0x002c +#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e +#define CMD_802_11_ENABLE_RSN 0x002f +#define CMD_802_11_SET_AFC 0x003c +#define CMD_802_11_GET_AFC 0x003d +#define CMD_802_11_DEEP_SLEEP 0x003e +#define CMD_802_11_AD_HOC_STOP 0x0040 +#define CMD_802_11_HOST_SLEEP_CFG 0x0043 +#define CMD_802_11_WAKEUP_CONFIRM 0x0044 +#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045 +#define CMD_802_11_BEACON_STOP 0x0049 +#define CMD_802_11_MAC_ADDRESS 0x004d +#define CMD_802_11_LED_GPIO_CTRL 0x004e +#define CMD_802_11_EEPROM_ACCESS 0x0059 +#define CMD_802_11_BAND_CONFIG 0x0058 +#define CMD_GSPI_BUS_CONFIG 0x005a +#define CMD_802_11D_DOMAIN_INFO 0x005b +#define CMD_802_11_KEY_MATERIAL 0x005e +#define CMD_802_11_SLEEP_PARAMS 0x0066 +#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067 +#define CMD_802_11_SLEEP_PERIOD 0x0068 +#define CMD_802_11_TPC_CFG 0x0072 +#define CMD_802_11_PA_CFG 0x0073 +#define CMD_802_11_FW_WAKE_METHOD 0x0074 +#define CMD_802_11_SUBSCRIBE_EVENT 0x0075 +#define CMD_802_11_RATE_ADAPT_RATESET 0x0076 +#define CMD_802_11_TX_RATE_QUERY 0x007f +#define CMD_GET_TSF 0x0080 +#define CMD_BT_ACCESS 0x0087 +#define CMD_FWT_ACCESS 0x0095 +#define CMD_802_11_MONITOR_MODE 0x0098 +#define CMD_MESH_ACCESS 0x009b +#define CMD_MESH_CONFIG_OLD 0x00a3 +#define CMD_MESH_CONFIG 0x00ac +#define CMD_SET_BOOT2_VER 0x00a5 +#define CMD_FUNC_INIT 0x00a9 +#define CMD_FUNC_SHUTDOWN 0x00aa +#define CMD_802_11_BEACON_CTRL 0x00b0 /* For the IEEE Power Save */ -#define CMD_SUBCMD_ENTER_PS 0x0030 -#define CMD_SUBCMD_EXIT_PS 0x0031 -#define CMD_SUBCMD_SLEEP_CONFIRMED 0x0034 -#define CMD_SUBCMD_FULL_POWERDOWN 0x0035 -#define CMD_SUBCMD_FULL_POWERUP 0x0036 +#define CMD_SUBCMD_ENTER_PS 0x0030 +#define CMD_SUBCMD_EXIT_PS 0x0031 +#define CMD_SUBCMD_SLEEP_CONFIRMED 0x0034 +#define CMD_SUBCMD_FULL_POWERDOWN 0x0035 +#define CMD_SUBCMD_FULL_POWERUP 0x0036 -#define CMD_ENABLE_RSN 0x0001 -#define CMD_DISABLE_RSN 0x0000 +#define CMD_ENABLE_RSN 0x0001 +#define CMD_DISABLE_RSN 0x0000 -#define CMD_ACT_GET 0x0000 -#define CMD_ACT_SET 0x0001 -#define CMD_ACT_GET_AES 0x0002 -#define CMD_ACT_SET_AES 0x0003 -#define CMD_ACT_REMOVE_AES 0x0004 +#define CMD_ACT_GET 0x0000 +#define CMD_ACT_SET 0x0001 /* Define action or option for CMD_802_11_SET_WEP */ -#define CMD_ACT_ADD 0x0002 -#define CMD_ACT_REMOVE 0x0004 -#define CMD_ACT_USE_DEFAULT 0x0008 - -#define CMD_TYPE_WEP_40_BIT 0x01 -#define CMD_TYPE_WEP_104_BIT 0x02 +#define CMD_ACT_ADD 0x0002 +#define CMD_ACT_REMOVE 0x0004 -#define CMD_NUM_OF_WEP_KEYS 4 +#define CMD_TYPE_WEP_40_BIT 0x01 +#define CMD_TYPE_WEP_104_BIT 0x02 -#define CMD_WEP_KEY_INDEX_MASK 0x3fff +#define CMD_NUM_OF_WEP_KEYS 4 -/* Define action or option for CMD_802_11_RESET */ -#define CMD_ACT_HALT 0x0003 +#define CMD_WEP_KEY_INDEX_MASK 0x3fff /* Define action or option for CMD_802_11_SCAN */ -#define CMD_BSS_TYPE_BSS 0x0001 -#define CMD_BSS_TYPE_IBSS 0x0002 -#define CMD_BSS_TYPE_ANY 0x0003 +#define CMD_BSS_TYPE_BSS 0x0001 +#define CMD_BSS_TYPE_IBSS 0x0002 +#define CMD_BSS_TYPE_ANY 0x0003 /* Define action or option for CMD_802_11_SCAN */ -#define CMD_SCAN_TYPE_ACTIVE 0x0000 -#define CMD_SCAN_TYPE_PASSIVE 0x0001 +#define CMD_SCAN_TYPE_ACTIVE 0x0000 +#define CMD_SCAN_TYPE_PASSIVE 0x0001 -#define CMD_SCAN_RADIO_TYPE_BG 0 +#define CMD_SCAN_RADIO_TYPE_BG 0 -#define CMD_SCAN_PROBE_DELAY_TIME 0 +#define CMD_SCAN_PROBE_DELAY_TIME 0 /* Define action or option for CMD_MAC_CONTROL */ -#define CMD_ACT_MAC_RX_ON 0x0001 -#define CMD_ACT_MAC_TX_ON 0x0002 -#define CMD_ACT_MAC_LOOPBACK_ON 0x0004 -#define CMD_ACT_MAC_WEP_ENABLE 0x0008 -#define CMD_ACT_MAC_INT_ENABLE 0x0010 -#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 -#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 -#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 -#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 -#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 +#define CMD_ACT_MAC_RX_ON 0x0001 +#define CMD_ACT_MAC_TX_ON 0x0002 +#define CMD_ACT_MAC_LOOPBACK_ON 0x0004 +#define CMD_ACT_MAC_WEP_ENABLE 0x0008 +#define CMD_ACT_MAC_INT_ENABLE 0x0010 +#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 +#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 +#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 +#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 /* Event flags for CMD_802_11_SUBSCRIBE_EVENT */ -#define CMD_SUBSCRIBE_RSSI_LOW 0x0001 -#define CMD_SUBSCRIBE_SNR_LOW 0x0002 -#define CMD_SUBSCRIBE_FAILCOUNT 0x0004 -#define CMD_SUBSCRIBE_BCNMISS 0x0008 -#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010 -#define CMD_SUBSCRIBE_SNR_HIGH 0x0020 +#define CMD_SUBSCRIBE_RSSI_LOW 0x0001 +#define CMD_SUBSCRIBE_SNR_LOW 0x0002 +#define CMD_SUBSCRIBE_FAILCOUNT 0x0004 +#define CMD_SUBSCRIBE_BCNMISS 0x0008 +#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010 +#define CMD_SUBSCRIBE_SNR_HIGH 0x0020 -#define RADIO_PREAMBLE_LONG 0x00 -#define RADIO_PREAMBLE_SHORT 0x02 -#define RADIO_PREAMBLE_AUTO 0x04 +#define RADIO_PREAMBLE_LONG 0x00 +#define RADIO_PREAMBLE_SHORT 0x02 +#define RADIO_PREAMBLE_AUTO 0x04 /* Define action or option for CMD_802_11_RF_CHANNEL */ -#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 -#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 +#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 +#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 /* Define action or option for CMD_802_11_DATA_RATE */ -#define CMD_ACT_SET_TX_AUTO 0x0000 -#define CMD_ACT_SET_TX_FIX_RATE 0x0001 -#define CMD_ACT_GET_TX_RATE 0x0002 - -#define CMD_ACT_SET_RX 0x0001 -#define CMD_ACT_SET_TX 0x0002 -#define CMD_ACT_SET_BOTH 0x0003 -#define CMD_ACT_GET_RX 0x0004 -#define CMD_ACT_GET_TX 0x0008 -#define CMD_ACT_GET_BOTH 0x000c +#define CMD_ACT_SET_TX_AUTO 0x0000 +#define CMD_ACT_SET_TX_FIX_RATE 0x0001 +#define CMD_ACT_GET_TX_RATE 0x0002 /* Define action or option for CMD_802_11_PS_MODE */ -#define CMD_TYPE_CAM 0x0000 -#define CMD_TYPE_MAX_PSP 0x0001 -#define CMD_TYPE_FAST_PSP 0x0002 +#define CMD_TYPE_CAM 0x0000 +#define CMD_TYPE_MAX_PSP 0x0001 +#define CMD_TYPE_FAST_PSP 0x0002 /* Options for CMD_802_11_FW_WAKE_METHOD */ -#define CMD_WAKE_METHOD_UNCHANGED 0x0000 -#define CMD_WAKE_METHOD_COMMAND_INT 0x0001 -#define CMD_WAKE_METHOD_GPIO 0x0002 +#define CMD_WAKE_METHOD_UNCHANGED 0x0000 +#define CMD_WAKE_METHOD_COMMAND_INT 0x0001 +#define CMD_WAKE_METHOD_GPIO 0x0002 /* Object IDs for CMD_802_11_SNMP_MIB */ -#define SNMP_MIB_OID_BSS_TYPE 0x0000 -#define SNMP_MIB_OID_OP_RATE_SET 0x0001 -#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */ -#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */ -#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */ -#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005 -#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006 -#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007 -#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008 -#define SNMP_MIB_OID_11D_ENABLE 0x0009 -#define SNMP_MIB_OID_11H_ENABLE 0x000A +#define SNMP_MIB_OID_BSS_TYPE 0x0000 +#define SNMP_MIB_OID_OP_RATE_SET 0x0001 +#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */ +#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */ +#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */ +#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005 +#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006 +#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007 +#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008 +#define SNMP_MIB_OID_11D_ENABLE 0x0009 +#define SNMP_MIB_OID_11H_ENABLE 0x000A /* Define action or option for CMD_BT_ACCESS */ enum cmd_bt_access_opts { @@ -302,4 +291,672 @@ enum cmd_mesh_config_types { #define MACREG_INT_CODE_MESH_AUTO_STARTED 35 #define MACREG_INT_CODE_FIRMWARE_READY 48 + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* union to cope up with later FW revisions */ + union { + /* Current Tx packet status */ + __le32 tx_status; + struct { + /* BSS type: client, AP, etc. */ + u8 bss_type; + /* BSS number */ + u8 bss_num; + /* Reserved */ + __le16 reserved; + } bss; + } u; + /* Tx control */ + __le32 tx_control; + __le32 tx_packet_location; + /* Tx packet length */ + __le16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Amount of time the packet has been queued (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +} __attribute__ ((packed)); + +/* RxPD Descriptor */ +struct rxpd { + /* union to cope up with later FW revisions */ + union { + /* Current Rx packet status */ + __le16 status; + struct { + /* BSS type: client, AP, etc. */ + u8 bss_type; + /* BSS number */ + u8 bss_num; + } __attribute__ ((packed)) bss; + } __attribute__ ((packed)) u; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + __le16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + __le32 pkt_ptr; + + /* Next Rx RxPD addr */ + __le32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +} __attribute__ ((packed)); + +struct cmd_header { + __le16 command; + __le16 size; + __le16 seqnum; + __le16 result; +} __attribute__ ((packed)); + +/* Generic structure to hold all key types. */ +struct enc_key { + u16 len; + u16 flags; /* KEY_INFO_* from defs.h */ + u16 type; /* KEY_TYPE_* from defs.h */ + u8 key[32]; +}; + +/* lbs_offset_value */ +struct lbs_offset_value { + u32 offset; + u32 value; +} __attribute__ ((packed)); + +/* + * Define data structure for CMD_GET_HW_SPEC + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + struct cmd_header hdr; + + /* HW Interface version number */ + __le16 hwifversion; + /* HW version number */ + __le16 version; + /* Max number of TxPD FW can handle */ + __le16 nr_txpd; + /* Max no of Multicast address */ + __le16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + __le16 regioncode; + + /* Number of antenna used */ + __le16 nr_antenna; + + /* FW release number, example 0x01030304 = 2.3.4p1 */ + __le32 fwrelease; + + /* Base Address of TxPD queue */ + __le32 wcb_base; + /* Read Pointer of RxPd queue */ + __le32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + __le32 rxpd_wrptr; + + /*FW/HW capability */ + __le32 fwcapinfo; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_subscribe_event { + struct cmd_header hdr; + + __le16 action; + __le16 events; + + /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a + * number of TLVs. From the v5.1 manual, those TLVs would add up to + * 40 bytes. However, future firmware might add additional TLVs, so I + * bump this up a bit. + */ + uint8_t tlv[128]; +} __attribute__ ((packed)); + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for CMD_802_11_SCAN + */ +struct cmd_ds_802_11_scan { + struct cmd_header hdr; + + uint8_t bsstype; + uint8_t bssid[ETH_ALEN]; + uint8_t tlvbuffer[0]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_scan_rsp { + struct cmd_header hdr; + + __le16 bssdescriptsize; + uint8_t nr_sets; + uint8_t bssdesc_and_tlvbuffer[0]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_get_log { + struct cmd_header hdr; + + __le32 mcasttxframe; + __le32 failed; + __le32 retry; + __le32 multiretry; + __le32 framedup; + __le32 rtssuccess; + __le32 rtsfailure; + __le32 ackfailure; + __le32 rxfrag; + __le32 mcastrxframe; + __le32 fcserror; + __le32 txframe; + __le32 wepundecryptable; +} __attribute__ ((packed)); + +struct cmd_ds_mac_control { + struct cmd_header hdr; + __le16 action; + u16 reserved; +} __attribute__ ((packed)); + +struct cmd_ds_mac_multicast_adr { + struct cmd_header hdr; + __le16 action; + __le16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_authenticate { + struct cmd_header hdr; + + u8 bssid[ETH_ALEN]; + u8 authtype; + u8 reserved[10]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_deauthenticate { + struct cmd_header hdr; + + u8 macaddr[ETH_ALEN]; + __le16 reasoncode; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_associate { + struct cmd_header hdr; + + u8 bssid[6]; + __le16 capability; + __le16 listeninterval; + __le16 bcnperiod; + u8 dtimperiod; + u8 iebuf[512]; /* Enough for required and most optional IEs */ +} __attribute__ ((packed)); + +struct cmd_ds_802_11_associate_response { + struct cmd_header hdr; + + __le16 capability; + __le16 statuscode; + __le16 aid; + u8 iebuf[512]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_set_wep { + struct cmd_header hdr; + + /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ + __le16 action; + + /* key Index selected for Tx */ + __le16 keyindex; + + /* 40, 128bit or TXWEP */ + uint8_t keytype[4]; + uint8_t keymaterial[4][16]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_snmp_mib { + struct cmd_header hdr; + + __le16 action; + __le16 oid; + __le16 bufsize; + u8 value[128]; +} __attribute__ ((packed)); + +struct cmd_ds_mac_reg_access { + __le16 action; + __le16 offset; + __le32 value; +} __attribute__ ((packed)); + +struct cmd_ds_bbp_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __attribute__ ((packed)); + +struct cmd_ds_rf_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_radio_control { + struct cmd_header hdr; + + __le16 action; + __le16 control; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_beacon_control { + __le16 action; + __le16 beacon_enable; + __le16 beacon_period; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_sleep_params { + struct cmd_header hdr; + + /* ACT_GET/ACT_SET */ + __le16 action; + + /* Sleep clock error in ppm */ + __le16 error; + + /* Wakeup offset in usec */ + __le16 offset; + + /* Clock stabilization time in usec */ + __le16 stabletime; + + /* control periodic calibration */ + uint8_t calcontrol; + + /* control the use of external sleep clock */ + uint8_t externalsleepclk; + + /* reserved field, should be set to zero */ + __le16 reserved; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_rf_channel { + struct cmd_header hdr; + + __le16 action; + __le16 channel; + __le16 rftype; /* unused */ + __le16 reserved; /* unused */ + u8 channellist[32]; /* unused */ +} __attribute__ ((packed)); + +struct cmd_ds_802_11_rssi { + /* weighting factor */ + __le16 N; + + __le16 reserved_0; + __le16 reserved_1; + __le16 reserved_2; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_rssi_rsp { + __le16 SNR; + __le16 noisefloor; + __le16 avgSNR; + __le16 avgnoisefloor; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_mac_address { + struct cmd_header hdr; + + __le16 action; + u8 macadd[ETH_ALEN]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_rf_tx_power { + struct cmd_header hdr; + + __le16 action; + __le16 curlevel; + s8 maxlevel; + s8 minlevel; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_monitor_mode { + __le16 action; + __le16 mode; +} __attribute__ ((packed)); + +struct cmd_ds_set_boot2_ver { + struct cmd_header hdr; + + __le16 action; + __le16 version; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_fw_wake_method { + struct cmd_header hdr; + + __le16 action; + __le16 method; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ps_mode { + __le16 action; + __le16 nullpktinterval; + __le16 multipledtim; + __le16 reserved; + __le16 locallisteninterval; +} __attribute__ ((packed)); + +struct cmd_confirm_sleep { + struct cmd_header hdr; + + __le16 action; + __le16 nullpktinterval; + __le16 multipledtim; + __le16 reserved; + __le16 locallisteninterval; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_data_rate { + struct cmd_header hdr; + + __le16 action; + __le16 reserved; + u8 rates[MAX_RATES]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_rate_adapt_rateset { + struct cmd_header hdr; + __le16 action; + __le16 enablehwauto; + __le16 bitmap; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ad_hoc_start { + struct cmd_header hdr; + + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bsstype; + __le16 beaconperiod; + u8 dtimperiod; /* Reserved on v9 and later */ + struct ieee_ie_ibss_param_set ibss; + u8 reserved1[4]; + struct ieee_ie_ds_param_set ds; + u8 reserved2[4]; + __le16 probedelay; /* Reserved on v9 and later */ + __le16 capability; + u8 rates[MAX_RATES]; + u8 tlv_memory_size_pad[100]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ad_hoc_result { + struct cmd_header hdr; + + u8 pad[3]; + u8 bssid[ETH_ALEN]; +} __attribute__ ((packed)); + +struct adhoc_bssdesc { + u8 bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 type; + __le16 beaconperiod; + u8 dtimperiod; + __le64 timestamp; + __le64 localtime; + struct ieee_ie_ds_param_set ds; + u8 reserved1[4]; + struct ieee_ie_ibss_param_set ibss; + u8 reserved2[4]; + __le16 capability; + u8 rates[MAX_RATES]; + + /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the + * Adhoc join command and will cause a binary layout mismatch with + * the firmware + */ +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ad_hoc_join { + struct cmd_header hdr; + + struct adhoc_bssdesc bss; + __le16 failtimeout; /* Reserved on v9 and later */ + __le16 probedelay; /* Reserved on v9 and later */ +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ad_hoc_stop { + struct cmd_header hdr; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_enable_rsn { + struct cmd_header hdr; + + __le16 action; + __le16 enable; +} __attribute__ ((packed)); + +struct MrvlIEtype_keyParamSet { + /* type ID */ + __le16 type; + + /* length of Payload */ + __le16 length; + + /* type of key: WEP=0, TKIP=1, AES=2 */ + __le16 keytypeid; + + /* key control Info specific to a keytypeid */ + __le16 keyinfo; + + /* length of key */ + __le16 keylen; + + /* key material of size keylen */ + u8 key[32]; +} __attribute__ ((packed)); + +#define MAX_WOL_RULES 16 + +struct host_wol_rule { + uint8_t rule_no; + uint8_t rule_ops; + __le16 sig_offset; + __le16 sig_length; + __le16 reserve; + __be32 sig_mask; + __be32 signature; +} __attribute__ ((packed)); + +struct wol_config { + uint8_t action; + uint8_t pattern; + uint8_t no_rules_in_cmd; + uint8_t result; + struct host_wol_rule rule[MAX_WOL_RULES]; +} __attribute__ ((packed)); + +struct cmd_ds_host_sleep { + struct cmd_header hdr; + __le32 criteria; + uint8_t gpio; + uint16_t gap; + struct wol_config wol_conf; +} __attribute__ ((packed)); + + + +struct cmd_ds_802_11_key_material { + struct cmd_header hdr; + + __le16 action; + struct MrvlIEtype_keyParamSet keyParamSet[2]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_eeprom_access { + struct cmd_header hdr; + __le16 action; + __le16 offset; + __le16 len; + /* firmware says it returns a maximum of 20 bytes */ +#define LBS_EEPROM_READ_LEN 20 + u8 value[LBS_EEPROM_READ_LEN]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_tpc_cfg { + struct cmd_header hdr; + + __le16 action; + uint8_t enable; + int8_t P0; + int8_t P1; + int8_t P2; + uint8_t usesnr; +} __attribute__ ((packed)); + + +struct cmd_ds_802_11_pa_cfg { + struct cmd_header hdr; + + __le16 action; + uint8_t enable; + int8_t P0; + int8_t P1; + int8_t P2; +} __attribute__ ((packed)); + + +struct cmd_ds_802_11_led_ctrl { + __le16 action; + __le16 numled; + u8 data[256]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_afc { + __le16 afc_auto; + union { + struct { + __le16 threshold; + __le16 period; + }; + struct { + __le16 timing_offset; /* signed */ + __le16 carrier_offset; /* signed */ + }; + }; +} __attribute__ ((packed)); + +struct cmd_tx_rate_query { + __le16 txrate; +} __attribute__ ((packed)); + +struct cmd_ds_get_tsf { + __le64 tsfvalue; +} __attribute__ ((packed)); + +struct cmd_ds_bt_access { + __le16 action; + __le32 id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; +} __attribute__ ((packed)); + +struct cmd_ds_fwt_access { + __le16 action; + __le32 id; + u8 valid; + u8 da[ETH_ALEN]; + u8 dir; + u8 ra[ETH_ALEN]; + __le32 ssn; + __le32 dsn; + __le32 metric; + u8 rate; + u8 hopcount; + u8 ttl; + __le32 expiration; + u8 sleepmode; + __le32 snr; + __le32 references; + u8 prec[ETH_ALEN]; +} __attribute__ ((packed)); + +struct cmd_ds_mesh_config { + struct cmd_header hdr; + + __le16 action; + __le16 channel; + __le16 type; + __le16 length; + u8 data[128]; /* last position reserved */ +} __attribute__ ((packed)); + +struct cmd_ds_mesh_access { + struct cmd_header hdr; + + __le16 action; + __le32 data[32]; /* last position reserved */ +} __attribute__ ((packed)); + +/* Number of stats counters returned by the firmware */ +#define MESH_STATS_NUM 8 + +struct cmd_ds_command { + /* command header */ + __le16 command; + __le16 size; + __le16 seqnum; + __le16 result; + + /* command Body */ + union { + struct cmd_ds_802_11_ps_mode psmode; + struct cmd_ds_802_11_monitor_mode monitor; + struct cmd_ds_802_11_rssi rssi; + struct cmd_ds_802_11_rssi_rsp rssirsp; + struct cmd_ds_mac_reg_access macreg; + struct cmd_ds_bbp_reg_access bbpreg; + struct cmd_ds_rf_reg_access rfreg; + + struct cmd_ds_802_11_tpc_cfg tpccfg; + struct cmd_ds_802_11_afc afc; + struct cmd_ds_802_11_led_ctrl ledgpio; + + struct cmd_ds_bt_access bt; + struct cmd_ds_fwt_access fwt; + struct cmd_ds_802_11_beacon_control bcn_ctrl; + } params; +} __attribute__ ((packed)); + #endif diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h deleted file mode 100644 index c8a1998d4744..000000000000 --- a/drivers/net/wireless/libertas/hostcmd.h +++ /dev/null @@ -1,800 +0,0 @@ -/* - * This file contains the function prototypes, data structure - * and defines for all the host/station commands - */ -#ifndef _LBS_HOSTCMD_H -#define _LBS_HOSTCMD_H - -#include <linux/wireless.h> -#include "11d.h" -#include "types.h" - -/* 802.11-related definitions */ - -/* TxPD descriptor */ -struct txpd { - /* union to cope up with later FW revisions */ - union { - /* Current Tx packet status */ - __le32 tx_status; - struct { - /* BSS type: client, AP, etc. */ - u8 bss_type; - /* BSS number */ - u8 bss_num; - /* Reserved */ - __le16 reserved; - } bss; - } u; - /* Tx control */ - __le32 tx_control; - __le32 tx_packet_location; - /* Tx packet length */ - __le16 tx_packet_length; - /* First 2 byte of destination MAC address */ - u8 tx_dest_addr_high[2]; - /* Last 4 byte of destination MAC address */ - u8 tx_dest_addr_low[4]; - /* Pkt Priority */ - u8 priority; - /* Pkt Trasnit Power control */ - u8 powermgmt; - /* Amount of time the packet has been queued in the driver (units = 2ms) */ - u8 pktdelay_2ms; - /* reserved */ - u8 reserved1; -} __attribute__ ((packed)); - -/* RxPD Descriptor */ -struct rxpd { - /* union to cope up with later FW revisions */ - union { - /* Current Rx packet status */ - __le16 status; - struct { - /* BSS type: client, AP, etc. */ - u8 bss_type; - /* BSS number */ - u8 bss_num; - } __attribute__ ((packed)) bss; - } __attribute__ ((packed)) u; - - /* SNR */ - u8 snr; - - /* Tx control */ - u8 rx_control; - - /* Pkt length */ - __le16 pkt_len; - - /* Noise Floor */ - u8 nf; - - /* Rx Packet Rate */ - u8 rx_rate; - - /* Pkt addr */ - __le32 pkt_ptr; - - /* Next Rx RxPD addr */ - __le32 next_rxpd_ptr; - - /* Pkt Priority */ - u8 priority; - u8 reserved[3]; -} __attribute__ ((packed)); - -struct cmd_header { - __le16 command; - __le16 size; - __le16 seqnum; - __le16 result; -} __attribute__ ((packed)); - -struct cmd_ctrl_node { - struct list_head list; - int result; - /* command response */ - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *); - unsigned long callback_arg; - /* command data */ - struct cmd_header *cmdbuf; - /* wait queue */ - u16 cmdwaitqwoken; - wait_queue_head_t cmdwait_q; -}; - -/* Generic structure to hold all key types. */ -struct enc_key { - u16 len; - u16 flags; /* KEY_INFO_* from defs.h */ - u16 type; /* KEY_TYPE_* from defs.h */ - u8 key[32]; -}; - -/* lbs_offset_value */ -struct lbs_offset_value { - u32 offset; - u32 value; -} __attribute__ ((packed)); - -/* Define general data structure */ -/* cmd_DS_GEN */ -struct cmd_ds_gen { - __le16 command; - __le16 size; - __le16 seqnum; - __le16 result; - void *cmdresp[0]; -} __attribute__ ((packed)); - -#define S_DS_GEN sizeof(struct cmd_ds_gen) - - -/* - * Define data structure for CMD_GET_HW_SPEC - * This structure defines the response for the GET_HW_SPEC command - */ -struct cmd_ds_get_hw_spec { - struct cmd_header hdr; - - /* HW Interface version number */ - __le16 hwifversion; - /* HW version number */ - __le16 version; - /* Max number of TxPD FW can handle */ - __le16 nr_txpd; - /* Max no of Multicast address */ - __le16 nr_mcast_adr; - /* MAC address */ - u8 permanentaddr[6]; - - /* region Code */ - __le16 regioncode; - - /* Number of antenna used */ - __le16 nr_antenna; - - /* FW release number, example 0x01030304 = 2.3.4p1 */ - __le32 fwrelease; - - /* Base Address of TxPD queue */ - __le32 wcb_base; - /* Read Pointer of RxPd queue */ - __le32 rxpd_rdptr; - - /* Write Pointer of RxPd queue */ - __le32 rxpd_wrptr; - - /*FW/HW capability */ - __le32 fwcapinfo; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_subscribe_event { - struct cmd_header hdr; - - __le16 action; - __le16 events; - - /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a - * number of TLVs. From the v5.1 manual, those TLVs would add up to - * 40 bytes. However, future firmware might add additional TLVs, so I - * bump this up a bit. - */ - uint8_t tlv[128]; -} __attribute__ ((packed)); - -/* - * This scan handle Country Information IE(802.11d compliant) - * Define data structure for CMD_802_11_SCAN - */ -struct cmd_ds_802_11_scan { - struct cmd_header hdr; - - uint8_t bsstype; - uint8_t bssid[ETH_ALEN]; - uint8_t tlvbuffer[0]; -#if 0 - mrvlietypes_ssidparamset_t ssidParamSet; - mrvlietypes_chanlistparamset_t ChanListParamSet; - mrvlietypes_ratesparamset_t OpRateSet; -#endif -} __attribute__ ((packed)); - -struct cmd_ds_802_11_scan_rsp { - struct cmd_header hdr; - - __le16 bssdescriptsize; - uint8_t nr_sets; - uint8_t bssdesc_and_tlvbuffer[0]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_get_log { - struct cmd_header hdr; - - __le32 mcasttxframe; - __le32 failed; - __le32 retry; - __le32 multiretry; - __le32 framedup; - __le32 rtssuccess; - __le32 rtsfailure; - __le32 ackfailure; - __le32 rxfrag; - __le32 mcastrxframe; - __le32 fcserror; - __le32 txframe; - __le32 wepundecryptable; -} __attribute__ ((packed)); - -struct cmd_ds_mac_control { - struct cmd_header hdr; - __le16 action; - u16 reserved; -} __attribute__ ((packed)); - -struct cmd_ds_mac_multicast_adr { - struct cmd_header hdr; - __le16 action; - __le16 nr_of_adrs; - u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; -} __attribute__ ((packed)); - -struct cmd_ds_gspi_bus_config { - struct cmd_header hdr; - __le16 action; - __le16 bus_delay_mode; - __le16 host_time_delay_to_read_port; - __le16 host_time_delay_to_read_register; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_authenticate { - struct cmd_header hdr; - - u8 bssid[ETH_ALEN]; - u8 authtype; - u8 reserved[10]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_deauthenticate { - struct cmd_header hdr; - - u8 macaddr[ETH_ALEN]; - __le16 reasoncode; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_associate { - struct cmd_header hdr; - - u8 bssid[6]; - __le16 capability; - __le16 listeninterval; - __le16 bcnperiod; - u8 dtimperiod; - u8 iebuf[512]; /* Enough for required and most optional IEs */ -} __attribute__ ((packed)); - -struct cmd_ds_802_11_associate_response { - struct cmd_header hdr; - - __le16 capability; - __le16 statuscode; - __le16 aid; - u8 iebuf[512]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_set_wep { - struct cmd_header hdr; - - /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ - __le16 action; - - /* key Index selected for Tx */ - __le16 keyindex; - - /* 40, 128bit or TXWEP */ - uint8_t keytype[4]; - uint8_t keymaterial[4][16]; -} __attribute__ ((packed)); - -struct cmd_ds_802_3_get_stat { - __le32 xmitok; - __le32 rcvok; - __le32 xmiterror; - __le32 rcverror; - __le32 rcvnobuffer; - __le32 rcvcrcerror; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_get_stat { - __le32 txfragmentcnt; - __le32 mcasttxframecnt; - __le32 failedcnt; - __le32 retrycnt; - __le32 Multipleretrycnt; - __le32 rtssuccesscnt; - __le32 rtsfailurecnt; - __le32 ackfailurecnt; - __le32 frameduplicatecnt; - __le32 rxfragmentcnt; - __le32 mcastrxframecnt; - __le32 fcserrorcnt; - __le32 bcasttxframecnt; - __le32 bcastrxframecnt; - __le32 txbeacon; - __le32 rxbeacon; - __le32 wepundecryptable; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_snmp_mib { - struct cmd_header hdr; - - __le16 action; - __le16 oid; - __le16 bufsize; - u8 value[128]; -} __attribute__ ((packed)); - -struct cmd_ds_mac_reg_map { - __le16 buffersize; - u8 regmap[128]; - __le16 reserved; -} __attribute__ ((packed)); - -struct cmd_ds_bbp_reg_map { - __le16 buffersize; - u8 regmap[128]; - __le16 reserved; -} __attribute__ ((packed)); - -struct cmd_ds_rf_reg_map { - __le16 buffersize; - u8 regmap[64]; - __le16 reserved; -} __attribute__ ((packed)); - -struct cmd_ds_mac_reg_access { - __le16 action; - __le16 offset; - __le32 value; -} __attribute__ ((packed)); - -struct cmd_ds_bbp_reg_access { - __le16 action; - __le16 offset; - u8 value; - u8 reserved[3]; -} __attribute__ ((packed)); - -struct cmd_ds_rf_reg_access { - __le16 action; - __le16 offset; - u8 value; - u8 reserved[3]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_radio_control { - struct cmd_header hdr; - - __le16 action; - __le16 control; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_beacon_control { - __le16 action; - __le16 beacon_enable; - __le16 beacon_period; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_sleep_params { - struct cmd_header hdr; - - /* ACT_GET/ACT_SET */ - __le16 action; - - /* Sleep clock error in ppm */ - __le16 error; - - /* Wakeup offset in usec */ - __le16 offset; - - /* Clock stabilization time in usec */ - __le16 stabletime; - - /* control periodic calibration */ - uint8_t calcontrol; - - /* control the use of external sleep clock */ - uint8_t externalsleepclk; - - /* reserved field, should be set to zero */ - __le16 reserved; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_inactivity_timeout { - struct cmd_header hdr; - - /* ACT_GET/ACT_SET */ - __le16 action; - - /* Inactivity timeout in msec */ - __le16 timeout; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_rf_channel { - struct cmd_header hdr; - - __le16 action; - __le16 channel; - __le16 rftype; /* unused */ - __le16 reserved; /* unused */ - u8 channellist[32]; /* unused */ -} __attribute__ ((packed)); - -struct cmd_ds_802_11_rssi { - /* weighting factor */ - __le16 N; - - __le16 reserved_0; - __le16 reserved_1; - __le16 reserved_2; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_rssi_rsp { - __le16 SNR; - __le16 noisefloor; - __le16 avgSNR; - __le16 avgnoisefloor; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_mac_address { - struct cmd_header hdr; - - __le16 action; - u8 macadd[ETH_ALEN]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_rf_tx_power { - struct cmd_header hdr; - - __le16 action; - __le16 curlevel; - s8 maxlevel; - s8 minlevel; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_rf_antenna { - __le16 action; - - /* Number of antennas or 0xffff(diversity) */ - __le16 antennamode; - -} __attribute__ ((packed)); - -struct cmd_ds_802_11_monitor_mode { - __le16 action; - __le16 mode; -} __attribute__ ((packed)); - -struct cmd_ds_set_boot2_ver { - struct cmd_header hdr; - - __le16 action; - __le16 version; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_fw_wake_method { - struct cmd_header hdr; - - __le16 action; - __le16 method; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_sleep_period { - struct cmd_header hdr; - - __le16 action; - __le16 period; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_ps_mode { - __le16 action; - __le16 nullpktinterval; - __le16 multipledtim; - __le16 reserved; - __le16 locallisteninterval; -} __attribute__ ((packed)); - -struct cmd_confirm_sleep { - struct cmd_header hdr; - - __le16 action; - __le16 nullpktinterval; - __le16 multipledtim; - __le16 reserved; - __le16 locallisteninterval; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_data_rate { - struct cmd_header hdr; - - __le16 action; - __le16 reserved; - u8 rates[MAX_RATES]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_rate_adapt_rateset { - struct cmd_header hdr; - __le16 action; - __le16 enablehwauto; - __le16 bitmap; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_ad_hoc_start { - struct cmd_header hdr; - - u8 ssid[IW_ESSID_MAX_SIZE]; - u8 bsstype; - __le16 beaconperiod; - u8 dtimperiod; /* Reserved on v9 and later */ - struct ieee_ie_ibss_param_set ibss; - u8 reserved1[4]; - struct ieee_ie_ds_param_set ds; - u8 reserved2[4]; - __le16 probedelay; /* Reserved on v9 and later */ - __le16 capability; - u8 rates[MAX_RATES]; - u8 tlv_memory_size_pad[100]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_ad_hoc_result { - struct cmd_header hdr; - - u8 pad[3]; - u8 bssid[ETH_ALEN]; -} __attribute__ ((packed)); - -struct adhoc_bssdesc { - u8 bssid[ETH_ALEN]; - u8 ssid[IW_ESSID_MAX_SIZE]; - u8 type; - __le16 beaconperiod; - u8 dtimperiod; - __le64 timestamp; - __le64 localtime; - struct ieee_ie_ds_param_set ds; - u8 reserved1[4]; - struct ieee_ie_ibss_param_set ibss; - u8 reserved2[4]; - __le16 capability; - u8 rates[MAX_RATES]; - - /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the - * Adhoc join command and will cause a binary layout mismatch with - * the firmware - */ -} __attribute__ ((packed)); - -struct cmd_ds_802_11_ad_hoc_join { - struct cmd_header hdr; - - struct adhoc_bssdesc bss; - __le16 failtimeout; /* Reserved on v9 and later */ - __le16 probedelay; /* Reserved on v9 and later */ -} __attribute__ ((packed)); - -struct cmd_ds_802_11_ad_hoc_stop { - struct cmd_header hdr; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_enable_rsn { - struct cmd_header hdr; - - __le16 action; - __le16 enable; -} __attribute__ ((packed)); - -struct MrvlIEtype_keyParamSet { - /* type ID */ - __le16 type; - - /* length of Payload */ - __le16 length; - - /* type of key: WEP=0, TKIP=1, AES=2 */ - __le16 keytypeid; - - /* key control Info specific to a keytypeid */ - __le16 keyinfo; - - /* length of key */ - __le16 keylen; - - /* key material of size keylen */ - u8 key[32]; -} __attribute__ ((packed)); - -#define MAX_WOL_RULES 16 - -struct host_wol_rule { - uint8_t rule_no; - uint8_t rule_ops; - __le16 sig_offset; - __le16 sig_length; - __le16 reserve; - __be32 sig_mask; - __be32 signature; -} __attribute__ ((packed)); - -struct wol_config { - uint8_t action; - uint8_t pattern; - uint8_t no_rules_in_cmd; - uint8_t result; - struct host_wol_rule rule[MAX_WOL_RULES]; -} __attribute__ ((packed)); - -struct cmd_ds_host_sleep { - struct cmd_header hdr; - __le32 criteria; - uint8_t gpio; - uint16_t gap; - struct wol_config wol_conf; -} __attribute__ ((packed)); - - - -struct cmd_ds_802_11_key_material { - struct cmd_header hdr; - - __le16 action; - struct MrvlIEtype_keyParamSet keyParamSet[2]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_eeprom_access { - struct cmd_header hdr; - __le16 action; - __le16 offset; - __le16 len; - /* firmware says it returns a maximum of 20 bytes */ -#define LBS_EEPROM_READ_LEN 20 - u8 value[LBS_EEPROM_READ_LEN]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_tpc_cfg { - struct cmd_header hdr; - - __le16 action; - uint8_t enable; - int8_t P0; - int8_t P1; - int8_t P2; - uint8_t usesnr; -} __attribute__ ((packed)); - - -struct cmd_ds_802_11_pa_cfg { - struct cmd_header hdr; - - __le16 action; - uint8_t enable; - int8_t P0; - int8_t P1; - int8_t P2; -} __attribute__ ((packed)); - - -struct cmd_ds_802_11_led_ctrl { - __le16 action; - __le16 numled; - u8 data[256]; -} __attribute__ ((packed)); - -struct cmd_ds_802_11_afc { - __le16 afc_auto; - union { - struct { - __le16 threshold; - __le16 period; - }; - struct { - __le16 timing_offset; /* signed */ - __le16 carrier_offset; /* signed */ - }; - }; -} __attribute__ ((packed)); - -struct cmd_tx_rate_query { - __le16 txrate; -} __attribute__ ((packed)); - -struct cmd_ds_get_tsf { - __le64 tsfvalue; -} __attribute__ ((packed)); - -struct cmd_ds_bt_access { - __le16 action; - __le32 id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; -} __attribute__ ((packed)); - -struct cmd_ds_fwt_access { - __le16 action; - __le32 id; - u8 valid; - u8 da[ETH_ALEN]; - u8 dir; - u8 ra[ETH_ALEN]; - __le32 ssn; - __le32 dsn; - __le32 metric; - u8 rate; - u8 hopcount; - u8 ttl; - __le32 expiration; - u8 sleepmode; - __le32 snr; - __le32 references; - u8 prec[ETH_ALEN]; -} __attribute__ ((packed)); - - -struct cmd_ds_mesh_config { - struct cmd_header hdr; - - __le16 action; - __le16 channel; - __le16 type; - __le16 length; - u8 data[128]; /* last position reserved */ -} __attribute__ ((packed)); - - -struct cmd_ds_mesh_access { - struct cmd_header hdr; - - __le16 action; - __le32 data[32]; /* last position reserved */ -} __attribute__ ((packed)); - -/* Number of stats counters returned by the firmware */ -#define MESH_STATS_NUM 8 - -struct cmd_ds_command { - /* command header */ - __le16 command; - __le16 size; - __le16 seqnum; - __le16 result; - - /* command Body */ - union { - struct cmd_ds_802_11_ps_mode psmode; - struct cmd_ds_802_11_get_stat gstat; - struct cmd_ds_802_3_get_stat gstat_8023; - struct cmd_ds_802_11_rf_antenna rant; - struct cmd_ds_802_11_monitor_mode monitor; - struct cmd_ds_802_11_rssi rssi; - struct cmd_ds_802_11_rssi_rsp rssirsp; - struct cmd_ds_mac_reg_access macreg; - struct cmd_ds_bbp_reg_access bbpreg; - struct cmd_ds_rf_reg_access rfreg; - - struct cmd_ds_802_11d_domain_info domaininfo; - struct cmd_ds_802_11d_domain_info domaininforesp; - - struct cmd_ds_802_11_tpc_cfg tpccfg; - struct cmd_ds_802_11_afc afc; - struct cmd_ds_802_11_led_ctrl ledgpio; - - struct cmd_tx_rate_query txrate; - struct cmd_ds_bt_access bt; - struct cmd_ds_fwt_access fwt; - struct cmd_ds_get_tsf gettsf; - struct cmd_ds_802_11_beacon_control bcn_ctrl; - } params; -} __attribute__ ((packed)); - -#endif diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index b1d84592b959..1f6cb58dd66c 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -48,6 +48,7 @@ MODULE_AUTHOR("Holger Schurig <hs4233@mail.mn-solutions.de>"); MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("libertas_cs_helper.fw"); @@ -932,6 +933,9 @@ static int if_cs_probe(struct pcmcia_device *p_dev) card->priv = priv; priv->card = card; priv->hw_host_to_card = if_cs_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; priv->fw_ready = 1; /* Now actually get the IRQ */ diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 485a8d406525..7a73f625273b 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -99,6 +99,12 @@ static struct if_sdio_model if_sdio_models[] = { .firmware = "sd8688.bin", }, }; +MODULE_FIRMWARE("sd8385_helper.bin"); +MODULE_FIRMWARE("sd8385.bin"); +MODULE_FIRMWARE("sd8686_helper.bin"); +MODULE_FIRMWARE("sd8686.bin"); +MODULE_FIRMWARE("sd8688_helper.bin"); +MODULE_FIRMWARE("sd8688.bin"); struct if_sdio_packet { struct if_sdio_packet *next; @@ -831,6 +837,58 @@ out: return ret; } +static int if_sdio_enter_deep_sleep(struct lbs_private *priv) +{ + int ret = -1; + struct cmd_header cmd; + + memset(&cmd, 0, sizeof(cmd)); + + lbs_deb_sdio("send DEEP_SLEEP command\n"); + ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd), + lbs_cmd_copyback, (unsigned long) &cmd); + if (ret) + lbs_pr_err("DEEP_SLEEP cmd failed\n"); + + mdelay(200); + return ret; +} + +static int if_sdio_exit_deep_sleep(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret = -1; + + lbs_deb_enter(LBS_DEB_SDIO); + sdio_claim_host(card->func); + + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + if (ret) + lbs_pr_err("sdio_writeb failed!\n"); + + sdio_release_host(card->func); + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret = -1; + + lbs_deb_enter(LBS_DEB_SDIO); + sdio_claim_host(card->func); + + sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret); + if (ret) + lbs_pr_err("sdio_writeb failed!\n"); + + sdio_release_host(card->func); + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; + +} + /*******************************************************************/ /* SDIO callbacks */ /*******************************************************************/ @@ -859,6 +917,7 @@ static void if_sdio_interrupt(struct sdio_func *func) * Ignore the define name, this really means the card has * successfully received the command. */ + card->priv->is_activity_detected = 1; if (cause & IF_SDIO_H_INT_DNLD) lbs_host_to_card_done(card->priv); @@ -934,7 +993,7 @@ static int if_sdio_probe(struct sdio_func *func, } if (i == ARRAY_SIZE(if_sdio_models)) { - lbs_pr_err("unkown card model 0x%x\n", card->model); + lbs_pr_err("unknown card model 0x%x\n", card->model); ret = -ENODEV; goto free; } @@ -998,6 +1057,9 @@ static int if_sdio_probe(struct sdio_func *func, priv->card = card; priv->hw_host_to_card = if_sdio_host_to_card; + priv->enter_deep_sleep = if_sdio_enter_deep_sleep; + priv->exit_deep_sleep = if_sdio_exit_deep_sleep; + priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; priv->fw_ready = 1; diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h index 60c9b2fcef03..12179c1dc9c9 100644 --- a/drivers/net/wireless/libertas/if_sdio.h +++ b/drivers/net/wireless/libertas/if_sdio.h @@ -51,5 +51,6 @@ #define IF_SDIO_EVENT 0x80fc #define IF_SDIO_BLOCK_SIZE 256 - +#define CONFIGURATION_REG 0x03 +#define HOST_POWER_UP (0x1U << 1) #endif diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 5b3672c4d0cc..bf4bfbae6227 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -32,12 +32,6 @@ #include "dev.h" #include "if_spi.h" -struct if_spi_packet { - struct list_head list; - u16 blen; - u8 buffer[0] __attribute__((aligned(4))); -}; - struct if_spi_card { struct spi_device *spi; struct lbs_private *priv; @@ -66,33 +60,10 @@ struct if_spi_card { struct semaphore spi_thread_terminated; u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE]; - - /* A buffer of incoming packets from libertas core. - * Since we can't sleep in hw_host_to_card, we have to buffer - * them. */ - struct list_head cmd_packet_list; - struct list_head data_packet_list; - - /* Protects cmd_packet_list and data_packet_list */ - spinlock_t buffer_lock; }; static void free_if_spi_card(struct if_spi_card *card) { - struct list_head *cursor, *next; - struct if_spi_packet *packet; - - BUG_ON(card->run_thread); - list_for_each_safe(cursor, next, &card->cmd_packet_list) { - packet = container_of(cursor, struct if_spi_packet, list); - list_del(&packet->list); - kfree(packet); - } - list_for_each_safe(cursor, next, &card->data_packet_list) { - packet = container_of(cursor, struct if_spi_packet, list); - list_del(&packet->list); - kfree(packet); - } spi_set_drvdata(card->spi, NULL); kfree(card); } @@ -774,40 +745,6 @@ out: return err; } -/* Move data or a command from the host to the card. */ -static void if_spi_h2c(struct if_spi_card *card, - struct if_spi_packet *packet, int type) -{ - int err = 0; - u16 int_type, port_reg; - - switch (type) { - case MVMS_DAT: - int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER; - port_reg = IF_SPI_DATA_RDWRPORT_REG; - break; - case MVMS_CMD: - int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER; - port_reg = IF_SPI_CMD_RDWRPORT_REG; - break; - default: - lbs_pr_err("can't transfer buffer of type %d\n", type); - err = -EINVAL; - goto out; - } - - /* Write the data to the card */ - err = spu_write(card, port_reg, packet->buffer, packet->blen); - if (err) - goto out; - -out: - kfree(packet); - - if (err) - lbs_pr_err("%s: error %d\n", __func__, err); -} - /* Inform the host about a card event */ static void if_spi_e2h(struct if_spi_card *card) { @@ -837,8 +774,6 @@ static int lbs_spi_thread(void *data) int err; struct if_spi_card *card = data; u16 hiStatus; - unsigned long flags; - struct if_spi_packet *packet; while (1) { /* Wait to be woken up by one of two things. First, our ISR @@ -877,43 +812,9 @@ static int lbs_spi_thread(void *data) if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || (card->priv->psstate != PS_STATE_FULL_POWER && (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { - /* This means two things. First of all, - * if there was a previous command sent, the card has - * successfully received it. - * Secondly, it is now ready to download another - * command. - */ lbs_host_to_card_done(card->priv); - - /* Do we have any command packets from the host to - * send? */ - packet = NULL; - spin_lock_irqsave(&card->buffer_lock, flags); - if (!list_empty(&card->cmd_packet_list)) { - packet = (struct if_spi_packet *)(card-> - cmd_packet_list.next); - list_del(&packet->list); - } - spin_unlock_irqrestore(&card->buffer_lock, flags); - - if (packet) - if_spi_h2c(card, packet, MVMS_CMD); } - if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { - /* Do we have any data packets from the host to - * send? */ - packet = NULL; - spin_lock_irqsave(&card->buffer_lock, flags); - if (!list_empty(&card->data_packet_list)) { - packet = (struct if_spi_packet *)(card-> - data_packet_list.next); - list_del(&packet->list); - } - spin_unlock_irqrestore(&card->buffer_lock, flags); - if (packet) - if_spi_h2c(card, packet, MVMS_DAT); - } if (hiStatus & IF_SPI_HIST_CARD_EVENT) if_spi_e2h(card); @@ -942,40 +843,18 @@ static int if_spi_host_to_card(struct lbs_private *priv, u8 type, u8 *buf, u16 nb) { int err = 0; - unsigned long flags; struct if_spi_card *card = priv->card; - struct if_spi_packet *packet; - u16 blen; lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb); - if (nb == 0) { - lbs_pr_err("%s: invalid size requested: %d\n", __func__, nb); - err = -EINVAL; - goto out; - } - blen = ALIGN(nb, 4); - packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); - if (!packet) { - err = -ENOMEM; - goto out; - } - packet->blen = blen; - memcpy(packet->buffer, buf, nb); - memset(packet->buffer + nb, 0, blen - nb); + nb = ALIGN(nb, 4); switch (type) { case MVMS_CMD: - priv->dnld_sent = DNLD_CMD_SENT; - spin_lock_irqsave(&card->buffer_lock, flags); - list_add_tail(&packet->list, &card->cmd_packet_list); - spin_unlock_irqrestore(&card->buffer_lock, flags); + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, buf, nb); break; case MVMS_DAT: - priv->dnld_sent = DNLD_DATA_SENT; - spin_lock_irqsave(&card->buffer_lock, flags); - list_add_tail(&packet->list, &card->data_packet_list); - spin_unlock_irqrestore(&card->buffer_lock, flags); + err = spu_write(card, IF_SPI_DATA_RDWRPORT_REG, buf, nb); break; default: lbs_pr_err("can't transfer buffer of type %d", type); @@ -983,9 +862,6 @@ static int if_spi_host_to_card(struct lbs_private *priv, break; } - /* Wake up the spi thread */ - up(&card->spi_ready); -out: lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err); return err; } @@ -1026,6 +902,10 @@ static int if_spi_calculate_fw_names(u16 card_id, chip_id_to_device_name[i].name); return 0; } +MODULE_FIRMWARE("libertas/gspi8385_hlp.bin"); +MODULE_FIRMWARE("libertas/gspi8385.bin"); +MODULE_FIRMWARE("libertas/gspi8686_hlp.bin"); +MODULE_FIRMWARE("libertas/gspi8686.bin"); static int __devinit if_spi_probe(struct spi_device *spi) { @@ -1062,9 +942,6 @@ static int __devinit if_spi_probe(struct spi_device *spi) sema_init(&card->spi_ready, 0); sema_init(&card->spi_thread_terminated, 0); - INIT_LIST_HEAD(&card->cmd_packet_list); - INIT_LIST_HEAD(&card->data_packet_list); - spin_lock_init(&card->buffer_lock); /* Initialize the SPI Interface Unit */ err = spu_init(card, pdata->use_dummy_writes); @@ -1117,6 +994,9 @@ static int __devinit if_spi_probe(struct spi_device *spi) card->priv = priv; priv->card = card; priv->hw_host_to_card = if_spi_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; priv->fw_ready = 1; /* Initialize interrupt handling stuff. */ @@ -1138,6 +1018,9 @@ static int __devinit if_spi_probe(struct spi_device *spi) goto terminate_thread; } + /* poke the IRQ handler so that we don't miss the first interrupt */ + up(&card->spi_ready); + /* Start the card. * This will call register_netdev, and we'll start * getting interrupts... */ diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index 3fac4efa5ac8..65e174595d12 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -28,6 +28,8 @@ static char *lbs_fw_name = "usb8388.bin"; module_param_named(fw_name, lbs_fw_name, charp, 0644); +MODULE_FIRMWARE("usb8388.bin"); + static struct usb_device_id if_usb_table[] = { /* Enter the device signature inside */ { USB_DEVICE(0x1286, 0x2001) }, @@ -300,6 +302,9 @@ static int if_usb_probe(struct usb_interface *intf, cardp->priv->fw_ready = 1; priv->hw_host_to_card = if_usb_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; #ifdef CONFIG_OLPC if (machine_is_olpc()) priv->reset_card = if_usb_reset_olpc_card; diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 87b4e497faa2..db38a5a719fa 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -14,11 +14,13 @@ #include <linux/stddef.h> #include <linux/ieee80211.h> #include <net/iw_handler.h> +#include <net/cfg80211.h> #include "host.h" #include "decl.h" #include "dev.h" #include "wext.h" +#include "cfg.h" #include "debugfs.h" #include "scan.h" #include "assoc.h" @@ -43,119 +45,6 @@ module_param_named(libertas_debug, lbs_debug, int, 0644); struct cmd_confirm_sleep confirm_sleep; -#define LBS_TX_PWR_DEFAULT 20 /*100mW */ -#define LBS_TX_PWR_US_DEFAULT 20 /*100mW */ -#define LBS_TX_PWR_JP_DEFAULT 16 /*50mW */ -#define LBS_TX_PWR_FR_DEFAULT 20 /*100mW */ -#define LBS_TX_PWR_EMEA_DEFAULT 20 /*100mW */ - -/* Format { channel, frequency (MHz), maxtxpower } */ -/* band: 'B/G', region: USA FCC/Canada IC */ -static struct chan_freq_power channel_freq_power_US_BG[] = { - {1, 2412, LBS_TX_PWR_US_DEFAULT}, - {2, 2417, LBS_TX_PWR_US_DEFAULT}, - {3, 2422, LBS_TX_PWR_US_DEFAULT}, - {4, 2427, LBS_TX_PWR_US_DEFAULT}, - {5, 2432, LBS_TX_PWR_US_DEFAULT}, - {6, 2437, LBS_TX_PWR_US_DEFAULT}, - {7, 2442, LBS_TX_PWR_US_DEFAULT}, - {8, 2447, LBS_TX_PWR_US_DEFAULT}, - {9, 2452, LBS_TX_PWR_US_DEFAULT}, - {10, 2457, LBS_TX_PWR_US_DEFAULT}, - {11, 2462, LBS_TX_PWR_US_DEFAULT} -}; - -/* band: 'B/G', region: Europe ETSI */ -static struct chan_freq_power channel_freq_power_EU_BG[] = { - {1, 2412, LBS_TX_PWR_EMEA_DEFAULT}, - {2, 2417, LBS_TX_PWR_EMEA_DEFAULT}, - {3, 2422, LBS_TX_PWR_EMEA_DEFAULT}, - {4, 2427, LBS_TX_PWR_EMEA_DEFAULT}, - {5, 2432, LBS_TX_PWR_EMEA_DEFAULT}, - {6, 2437, LBS_TX_PWR_EMEA_DEFAULT}, - {7, 2442, LBS_TX_PWR_EMEA_DEFAULT}, - {8, 2447, LBS_TX_PWR_EMEA_DEFAULT}, - {9, 2452, LBS_TX_PWR_EMEA_DEFAULT}, - {10, 2457, LBS_TX_PWR_EMEA_DEFAULT}, - {11, 2462, LBS_TX_PWR_EMEA_DEFAULT}, - {12, 2467, LBS_TX_PWR_EMEA_DEFAULT}, - {13, 2472, LBS_TX_PWR_EMEA_DEFAULT} -}; - -/* band: 'B/G', region: Spain */ -static struct chan_freq_power channel_freq_power_SPN_BG[] = { - {10, 2457, LBS_TX_PWR_DEFAULT}, - {11, 2462, LBS_TX_PWR_DEFAULT} -}; - -/* band: 'B/G', region: France */ -static struct chan_freq_power channel_freq_power_FR_BG[] = { - {10, 2457, LBS_TX_PWR_FR_DEFAULT}, - {11, 2462, LBS_TX_PWR_FR_DEFAULT}, - {12, 2467, LBS_TX_PWR_FR_DEFAULT}, - {13, 2472, LBS_TX_PWR_FR_DEFAULT} -}; - -/* band: 'B/G', region: Japan */ -static struct chan_freq_power channel_freq_power_JPN_BG[] = { - {1, 2412, LBS_TX_PWR_JP_DEFAULT}, - {2, 2417, LBS_TX_PWR_JP_DEFAULT}, - {3, 2422, LBS_TX_PWR_JP_DEFAULT}, - {4, 2427, LBS_TX_PWR_JP_DEFAULT}, - {5, 2432, LBS_TX_PWR_JP_DEFAULT}, - {6, 2437, LBS_TX_PWR_JP_DEFAULT}, - {7, 2442, LBS_TX_PWR_JP_DEFAULT}, - {8, 2447, LBS_TX_PWR_JP_DEFAULT}, - {9, 2452, LBS_TX_PWR_JP_DEFAULT}, - {10, 2457, LBS_TX_PWR_JP_DEFAULT}, - {11, 2462, LBS_TX_PWR_JP_DEFAULT}, - {12, 2467, LBS_TX_PWR_JP_DEFAULT}, - {13, 2472, LBS_TX_PWR_JP_DEFAULT}, - {14, 2484, LBS_TX_PWR_JP_DEFAULT} -}; - -/** - * the structure for channel, frequency and power - */ -struct region_cfp_table { - u8 region; - struct chan_freq_power *cfp_BG; - int cfp_no_BG; -}; - -/** - * the structure for the mapping between region and CFP - */ -static struct region_cfp_table region_cfp_table[] = { - {0x10, /*US FCC */ - channel_freq_power_US_BG, - ARRAY_SIZE(channel_freq_power_US_BG), - } - , - {0x20, /*CANADA IC */ - channel_freq_power_US_BG, - ARRAY_SIZE(channel_freq_power_US_BG), - } - , - {0x30, /*EU*/ channel_freq_power_EU_BG, - ARRAY_SIZE(channel_freq_power_EU_BG), - } - , - {0x31, /*SPAIN*/ channel_freq_power_SPN_BG, - ARRAY_SIZE(channel_freq_power_SPN_BG), - } - , - {0x32, /*FRANCE*/ channel_freq_power_FR_BG, - ARRAY_SIZE(channel_freq_power_FR_BG), - } - , - {0x40, /*JAPAN*/ channel_freq_power_JPN_BG, - ARRAY_SIZE(channel_freq_power_JPN_BG), - } - , -/*Add new region here */ -}; - /** * the table to keep region code */ @@ -163,13 +52,6 @@ u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; /** - * 802.11b/g supported bitrates (in 500Kb/s units) - */ -u8 lbs_bg_rates[MAX_RATES] = - { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, -0x00, 0x00 }; - -/** * FW rate table. FW refers to rates by their index in this table, not by the * rate value itself. Values of 0x00 are * reserved positions. @@ -212,107 +94,9 @@ u8 lbs_data_rate_to_fw_index(u32 rate) return 0; } -/** - * Attributes exported through sysfs - */ - -/** - * @brief Get function for sysfs attribute anycast_mask - */ -static ssize_t lbs_anycast_get(struct device *dev, - struct device_attribute *attr, char * buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - - memset(&mesh_access, 0, sizeof(mesh_access)); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access); - if (ret) - return ret; - - return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0])); -} - -/** - * @brief Set function for sysfs attribute anycast_mask - */ -static ssize_t lbs_anycast_set(struct device *dev, - struct device_attribute *attr, const char * buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - uint32_t datum; - int ret; - - memset(&mesh_access, 0, sizeof(mesh_access)); - sscanf(buf, "%x", &datum); - mesh_access.data[0] = cpu_to_le32(datum); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * @brief Get function for sysfs attribute prb_rsp_limit - */ -static ssize_t lbs_prb_rsp_limit_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - u32 retry_limit; - - memset(&mesh_access, 0, sizeof(mesh_access)); - mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, - &mesh_access); - if (ret) - return ret; - - retry_limit = le32_to_cpu(mesh_access.data[1]); - return snprintf(buf, 10, "%d\n", retry_limit); -} - -/** - * @brief Set function for sysfs attribute prb_rsp_limit - */ -static ssize_t lbs_prb_rsp_limit_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - unsigned long retry_limit; - - memset(&mesh_access, 0, sizeof(mesh_access)); - mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); - - if (!strict_strtoul(buf, 10, &retry_limit)) - return -ENOTSUPP; - if (retry_limit > 15) - return -ENOTSUPP; - - mesh_access.data[1] = cpu_to_le32(retry_limit); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, - &mesh_access); - if (ret) - return ret; - - return strlen(buf); -} static int lbs_add_rtap(struct lbs_private *priv); static void lbs_remove_rtap(struct lbs_private *priv); -static int lbs_add_mesh(struct lbs_private *priv); -static void lbs_remove_mesh(struct lbs_private *priv); /** @@ -378,74 +162,7 @@ static ssize_t lbs_rtap_set(struct device *dev, static DEVICE_ATTR(lbs_rtap, 0644, lbs_rtap_get, lbs_rtap_set ); /** - * Get function for sysfs attribute mesh - */ -static ssize_t lbs_mesh_get(struct device *dev, - struct device_attribute *attr, char * buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev); -} - -/** - * Set function for sysfs attribute mesh - */ -static ssize_t lbs_mesh_set(struct device *dev, - struct device_attribute *attr, const char * buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - int enable; - int ret, action = CMD_ACT_MESH_CONFIG_STOP; - - sscanf(buf, "%x", &enable); - enable = !!enable; - if (enable == !!priv->mesh_dev) - return count; - if (enable) - action = CMD_ACT_MESH_CONFIG_START; - ret = lbs_mesh_config(priv, action, priv->curbssparams.channel); - if (ret) - return ret; - - if (enable) - lbs_add_mesh(priv); - else - lbs_remove_mesh(priv); - - return count; -} - -/** - * lbs_mesh attribute to be exported per ethX interface - * through sysfs (/sys/class/net/ethX/lbs_mesh) - */ -static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set); - -/** - * anycast_mask attribute to be exported per mshX interface - * through sysfs (/sys/class/net/mshX/anycast_mask) - */ -static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); - -/** - * prb_rsp_limit attribute to be exported per mshX interface - * through sysfs (/sys/class/net/mshX/prb_rsp_limit) - */ -static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get, - lbs_prb_rsp_limit_set); - -static struct attribute *lbs_mesh_sysfs_entries[] = { - &dev_attr_anycast_mask.attr, - &dev_attr_prb_rsp_limit.attr, - NULL, -}; - -static struct attribute_group lbs_mesh_attr_group = { - .attrs = lbs_mesh_sysfs_entries, -}; - -/** - * @brief This function opens the ethX or mshX interface + * @brief This function opens the ethX interface * * @param dev A pointer to net_device structure * @return 0 or -EBUSY if monitor mode active @@ -464,18 +181,12 @@ static int lbs_dev_open(struct net_device *dev) goto out; } - if (dev == priv->mesh_dev) { - priv->mesh_open = 1; - priv->mesh_connect_status = LBS_CONNECTED; - netif_carrier_on(dev); - } else { - priv->infra_open = 1; + priv->infra_open = 1; - if (priv->connect_status == LBS_CONNECTED) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - } + if (priv->connect_status == LBS_CONNECTED) + netif_carrier_on(dev); + else + netif_carrier_off(dev); if (!priv->tx_pending_len) netif_wake_queue(dev); @@ -487,33 +198,6 @@ static int lbs_dev_open(struct net_device *dev) } /** - * @brief This function closes the mshX interface - * - * @param dev A pointer to net_device structure - * @return 0 - */ -static int lbs_mesh_stop(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - - lbs_deb_enter(LBS_DEB_MESH); - spin_lock_irq(&priv->driver_lock); - - priv->mesh_open = 0; - priv->mesh_connect_status = LBS_DISCONNECTED; - - netif_stop_queue(dev); - netif_carrier_off(dev); - - spin_unlock_irq(&priv->driver_lock); - - schedule_work(&priv->mcast_work); - - lbs_deb_leave(LBS_DEB_MESH); - return 0; -} - -/** * @brief This function closes the ethX interface * * @param dev A pointer to net_device structure @@ -574,15 +258,17 @@ void lbs_host_to_card_done(struct lbs_private *priv) priv->dnld_sent = DNLD_RES_RECEIVED; /* Wake main thread if commands are pending */ - if (!priv->cur_cmd || priv->tx_pending_len > 0) - wake_up_interruptible(&priv->waitq); + if (!priv->cur_cmd || priv->tx_pending_len > 0) { + if (!priv->wakeup_dev_required) + wake_up_interruptible(&priv->waitq); + } spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_THREAD); } EXPORT_SYMBOL_GPL(lbs_host_to_card_done); -static int lbs_set_mac_address(struct net_device *dev, void *addr) +int lbs_set_mac_address(struct net_device *dev, void *addr) { int ret = 0; struct lbs_private *priv = dev->ml_priv; @@ -716,7 +402,7 @@ static void lbs_set_mcast_worker(struct work_struct *work) lbs_deb_leave(LBS_DEB_NET); } -static void lbs_set_multicast_list(struct net_device *dev) +void lbs_set_multicast_list(struct net_device *dev) { struct lbs_private *priv = dev->ml_priv; @@ -770,7 +456,8 @@ static int lbs_thread(void *data) shouldsleep = 0; /* We have a command response */ else if (priv->cur_cmd) shouldsleep = 1; /* Can't send a command; one already running */ - else if (!list_empty(&priv->cmdpendingq)) + else if (!list_empty(&priv->cmdpendingq) && + !(priv->wakeup_dev_required)) shouldsleep = 0; /* We have a command to send */ else if (__kfifo_len(priv->event_fifo)) shouldsleep = 0; /* We have an event to process */ @@ -822,6 +509,26 @@ static int lbs_thread(void *data) } spin_unlock_irq(&priv->driver_lock); + /* Process hardware events, e.g. card removed, link lost */ + spin_lock_irq(&priv->driver_lock); + while (__kfifo_len(priv->event_fifo)) { + u32 event; + __kfifo_get(priv->event_fifo, (unsigned char *) &event, + sizeof(event)); + spin_unlock_irq(&priv->driver_lock); + lbs_process_event(priv, event); + spin_lock_irq(&priv->driver_lock); + } + spin_unlock_irq(&priv->driver_lock); + + if (priv->wakeup_dev_required) { + lbs_deb_thread("Waking up device...\n"); + /* Wake up device */ + if (priv->exit_deep_sleep(priv)) + lbs_deb_thread("Wakeup device failed\n"); + continue; + } + /* command timeout stuff */ if (priv->cmd_timed_out && priv->cur_cmd) { struct cmd_ctrl_node *cmdnode = priv->cur_cmd; @@ -849,18 +556,7 @@ static int lbs_thread(void *data) } priv->cmd_timed_out = 0; - /* Process hardware events, e.g. card removed, link lost */ - spin_lock_irq(&priv->driver_lock); - while (__kfifo_len(priv->event_fifo)) { - u32 event; - __kfifo_get(priv->event_fifo, (unsigned char *) &event, - sizeof(event)); - spin_unlock_irq(&priv->driver_lock); - lbs_process_event(priv, event); - spin_lock_irq(&priv->driver_lock); - } - spin_unlock_irq(&priv->driver_lock); if (!priv->fw_ready) continue; @@ -894,6 +590,9 @@ static int lbs_thread(void *data) (priv->psstate == PS_STATE_PRE_SLEEP)) continue; + if (priv->is_deep_sleep) + continue; + /* Execute the next command */ if (!priv->dnld_sent && !priv->cur_cmd) lbs_execute_next_command(priv); @@ -928,6 +627,7 @@ static int lbs_thread(void *data) } del_timer(&priv->command_timer); + del_timer(&priv->auto_deepsleep_timer); wake_up_all(&priv->cmd_pending); lbs_deb_leave(LBS_DEB_THREAD); @@ -1050,6 +750,62 @@ out: lbs_deb_leave(LBS_DEB_CMD); } +/** + * This function put the device back to deep sleep mode when timer expires + * and no activity (command, event, data etc.) is detected. + */ +static void auto_deepsleep_timer_fn(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + if (priv->is_activity_detected) { + priv->is_activity_detected = 0; + } else { + if (priv->is_auto_deep_sleep_enabled && + (!priv->wakeup_dev_required) && + (priv->connect_status != LBS_CONNECTED)) { + lbs_deb_main("Entering auto deep sleep mode...\n"); + ret = lbs_prepare_and_send_command(priv, + CMD_802_11_DEEP_SLEEP, 0, + 0, 0, NULL); + if (ret) + lbs_pr_err("Enter Deep Sleep command failed\n"); + } + } + mod_timer(&priv->auto_deepsleep_timer , jiffies + + (priv->auto_deep_sleep_timeout * HZ)/1000); + lbs_deb_leave(LBS_DEB_CMD); +} + +int lbs_enter_auto_deep_sleep(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_SDIO); + + priv->is_auto_deep_sleep_enabled = 1; + if (priv->is_deep_sleep) + priv->wakeup_dev_required = 1; + mod_timer(&priv->auto_deepsleep_timer , + jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); + + lbs_deb_leave(LBS_DEB_SDIO); + return 0; +} + +int lbs_exit_auto_deep_sleep(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_SDIO); + + priv->is_auto_deep_sleep_enabled = 0; + priv->auto_deep_sleep_timeout = 0; + del_timer(&priv->auto_deepsleep_timer); + + lbs_deb_leave(LBS_DEB_SDIO); + return 0; +} + static void lbs_sync_channel_worker(struct work_struct *work) { struct lbs_private *priv = container_of(work, struct lbs_private, @@ -1092,18 +848,24 @@ static int lbs_init_adapter(struct lbs_private *priv) priv->mesh_connect_status = LBS_DISCONNECTED; priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; priv->mode = IW_MODE_INFRA; - priv->curbssparams.channel = DEFAULT_AD_HOC_CHANNEL; + priv->channel = DEFAULT_AD_HOC_CHANNEL; priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; priv->radio_on = 1; priv->enablehwauto = 1; priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; priv->psmode = LBS802_11POWERMODECAM; priv->psstate = PS_STATE_FULL_POWER; + priv->is_deep_sleep = 0; + priv->is_auto_deep_sleep_enabled = 0; + priv->wakeup_dev_required = 0; + init_waitqueue_head(&priv->ds_awake_q); mutex_init(&priv->lock); setup_timer(&priv->command_timer, command_timer_fn, (unsigned long)priv); + setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, + (unsigned long)priv); INIT_LIST_HEAD(&priv->cmdfreeq); INIT_LIST_HEAD(&priv->cmdpendingq); @@ -1142,6 +904,7 @@ static void lbs_free_adapter(struct lbs_private *priv) if (priv->event_fifo) kfifo_free(priv->event_fifo); del_timer(&priv->command_timer); + del_timer(&priv->auto_deepsleep_timer); kfree(priv->networks); priv->networks = NULL; @@ -1168,31 +931,41 @@ static const struct net_device_ops lbs_netdev_ops = { */ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) { - struct net_device *dev = NULL; + struct net_device *dev; + struct wireless_dev *wdev; struct lbs_private *priv = NULL; lbs_deb_enter(LBS_DEB_MAIN); /* Allocate an Ethernet device and register it */ - dev = alloc_etherdev(sizeof(struct lbs_private)); - if (!dev) { - lbs_pr_err("init wlanX device failed\n"); + wdev = lbs_cfg_alloc(dmdev); + if (IS_ERR(wdev)) { + lbs_pr_err("cfg80211 init failed\n"); goto done; } - priv = netdev_priv(dev); - dev->ml_priv = priv; + /* TODO? */ + wdev->iftype = NL80211_IFTYPE_STATION; + priv = wdev_priv(wdev); + priv->wdev = wdev; if (lbs_init_adapter(priv)) { lbs_pr_err("failed to initialize adapter structure.\n"); - goto err_init_adapter; + goto err_wdev; } + //TODO? dev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES); + dev = alloc_netdev(0, "wlan%d", ether_setup); + if (!dev) { + dev_err(dmdev, "no memory for network device instance\n"); + goto err_adapter; + } + + dev->ieee80211_ptr = wdev; + dev->ml_priv = priv; + SET_NETDEV_DEV(dev, dmdev); + wdev->netdev = dev; priv->dev = dev; - priv->card = card; - priv->mesh_open = 0; - priv->infra_open = 0; - /* Setup the OS Interface to our functions */ dev->netdev_ops = &lbs_netdev_ops; dev->watchdog_timeo = 5 * HZ; dev->ethtool_ops = &lbs_ethtool_ops; @@ -1201,7 +974,13 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) #endif dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - SET_NETDEV_DEV(dev, dmdev); + + // TODO: kzalloc + iwm_init_default_profile(iwm, iwm->umac_profile); ?? + + + priv->card = card; + priv->infra_open = 0; + priv->rtap_net_dev = NULL; strcpy(dev->name, "wlan%d"); @@ -1211,7 +990,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); if (IS_ERR(priv->main_thread)) { lbs_deb_thread("Error creating main thread.\n"); - goto err_init_adapter; + goto err_ndev; } priv->work_thread = create_singlethread_workqueue("lbs_worker"); @@ -1220,6 +999,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker); + priv->mesh_open = 0; sprintf(priv->mesh_ssid, "mesh"); priv->mesh_ssid_len = 4; @@ -1228,9 +1008,15 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) goto done; -err_init_adapter: - lbs_free_adapter(priv); + err_ndev: free_netdev(dev); + + err_adapter: + lbs_free_adapter(priv); + + err_wdev: + lbs_cfg_free(priv); + priv = NULL; done: @@ -1243,7 +1029,6 @@ EXPORT_SYMBOL_GPL(lbs_add_card); void lbs_remove_card(struct lbs_private *priv) { struct net_device *dev = priv->dev; - union iwreq_data wrqu; lbs_deb_enter(LBS_DEB_MAIN); @@ -1268,15 +1053,19 @@ void lbs_remove_card(struct lbs_private *priv) lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP); } - memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); + lbs_send_disconnect_notification(priv); + + if (priv->is_deep_sleep) { + priv->is_deep_sleep = 0; + wake_up_interruptible(&priv->ds_awake_q); + } /* Stop the thread servicing the interrupts */ priv->surpriseremoved = 1; kthread_stop(priv->main_thread); lbs_free_adapter(priv); + lbs_cfg_free(priv); priv->dev = NULL; free_netdev(dev); @@ -1298,60 +1087,19 @@ int lbs_start_card(struct lbs_private *priv) if (ret) goto done; - /* init 802.11d */ - lbs_init_11d(priv); - - if (register_netdev(dev)) { - lbs_pr_err("cannot register ethX device\n"); + if (lbs_cfg_register(priv)) { + lbs_pr_err("cannot register device\n"); goto done; } lbs_update_channel(priv); - /* Check mesh FW version and appropriately send the mesh start - * command + /* + * While rtap isn't related to mesh, only mesh-enabled + * firmware implements the rtap functionality via + * CMD_802_11_MONITOR_MODE. */ - if (priv->mesh_fw_ver == MESH_FW_OLD) { - /* Enable mesh, if supported, and work out which TLV it uses. - 0x100 + 291 is an unofficial value used in 5.110.20.pXX - 0x100 + 37 is the official value used in 5.110.21.pXX - but we check them in that order because 20.pXX doesn't - give an error -- it just silently fails. */ - - /* 5.110.20.pXX firmware will fail the command if the channel - doesn't match the existing channel. But only if the TLV - is correct. If the channel is wrong, _BOTH_ versions will - give an error to 0x100+291, and allow 0x100+37 to succeed. - It's just that 5.110.20.pXX will not have done anything - useful */ - - priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - priv->curbssparams.channel)) { - priv->mesh_tlv = TLV_TYPE_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - priv->curbssparams.channel)) - priv->mesh_tlv = 0; - } - } else if (priv->mesh_fw_ver == MESH_FW_NEW) { - /* 10.0.0.pXX new firmwares should succeed with TLV - * 0x100+37; Do not invoke command with old TLV. - */ - priv->mesh_tlv = TLV_TYPE_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - priv->curbssparams.channel)) - priv->mesh_tlv = 0; - } - if (priv->mesh_tlv) { - lbs_add_mesh(priv); - - if (device_create_file(&dev->dev, &dev_attr_lbs_mesh)) - lbs_pr_err("cannot register lbs_mesh attribute\n"); - - /* While rtap isn't related to mesh, only mesh-enabled - * firmware implements the rtap functionality via - * CMD_802_11_MONITOR_MODE. - */ + if (lbs_init_mesh(priv)) { if (device_create_file(&dev->dev, &dev_attr_lbs_rtap)) lbs_pr_err("cannot register lbs_rtap attribute\n"); } @@ -1385,13 +1133,12 @@ void lbs_stop_card(struct lbs_private *priv) netif_carrier_off(dev); lbs_debugfs_remove_one(priv); - if (priv->mesh_tlv) { - device_remove_file(&dev->dev, &dev_attr_lbs_mesh); + if (lbs_deinit_mesh(priv)) device_remove_file(&dev->dev, &dev_attr_lbs_rtap); - } /* Delete the timeout of the currently processing command */ del_timer_sync(&priv->command_timer); + del_timer_sync(&priv->auto_deepsleep_timer); /* Flush pending command nodes */ spin_lock_irqsave(&priv->driver_lock, flags); @@ -1420,157 +1167,6 @@ out: EXPORT_SYMBOL_GPL(lbs_stop_card); -static const struct net_device_ops mesh_netdev_ops = { - .ndo_open = lbs_dev_open, - .ndo_stop = lbs_mesh_stop, - .ndo_start_xmit = lbs_hard_start_xmit, - .ndo_set_mac_address = lbs_set_mac_address, - .ndo_set_multicast_list = lbs_set_multicast_list, -}; - -/** - * @brief This function adds mshX interface - * - * @param priv A pointer to the struct lbs_private structure - * @return 0 if successful, -X otherwise - */ -static int lbs_add_mesh(struct lbs_private *priv) -{ - struct net_device *mesh_dev = NULL; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - /* Allocate a virtual mesh device */ - if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) { - lbs_deb_mesh("init mshX device failed\n"); - ret = -ENOMEM; - goto done; - } - mesh_dev->ml_priv = priv; - priv->mesh_dev = mesh_dev; - - mesh_dev->netdev_ops = &mesh_netdev_ops; - mesh_dev->ethtool_ops = &lbs_ethtool_ops; - memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, - sizeof(priv->dev->dev_addr)); - - SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); - -#ifdef WIRELESS_EXT - mesh_dev->wireless_handlers = (struct iw_handler_def *)&mesh_handler_def; -#endif - mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - /* Register virtual mesh interface */ - ret = register_netdev(mesh_dev); - if (ret) { - lbs_pr_err("cannot register mshX virtual interface\n"); - goto err_free; - } - - ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); - if (ret) - goto err_unregister; - - lbs_persist_config_init(mesh_dev); - - /* Everything successful */ - ret = 0; - goto done; - -err_unregister: - unregister_netdev(mesh_dev); - -err_free: - free_netdev(mesh_dev); - -done: - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - -static void lbs_remove_mesh(struct lbs_private *priv) -{ - struct net_device *mesh_dev; - - - mesh_dev = priv->mesh_dev; - if (!mesh_dev) - return; - - lbs_deb_enter(LBS_DEB_MESH); - netif_stop_queue(mesh_dev); - netif_carrier_off(mesh_dev); - sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); - lbs_persist_config_remove(mesh_dev); - unregister_netdev(mesh_dev); - priv->mesh_dev = NULL; - free_netdev(mesh_dev); - lbs_deb_leave(LBS_DEB_MESH); -} - -/** - * @brief This function finds the CFP in - * region_cfp_table based on region and band parameter. - * - * @param region The region code - * @param band The band - * @param cfp_no A pointer to CFP number - * @return A pointer to CFP - */ -struct chan_freq_power *lbs_get_region_cfp_table(u8 region, int *cfp_no) -{ - int i, end; - - lbs_deb_enter(LBS_DEB_MAIN); - - end = ARRAY_SIZE(region_cfp_table); - - for (i = 0; i < end ; i++) { - lbs_deb_main("region_cfp_table[i].region=%d\n", - region_cfp_table[i].region); - if (region_cfp_table[i].region == region) { - *cfp_no = region_cfp_table[i].cfp_no_BG; - lbs_deb_leave(LBS_DEB_MAIN); - return region_cfp_table[i].cfp_BG; - } - } - - lbs_deb_leave_args(LBS_DEB_MAIN, "ret NULL"); - return NULL; -} - -int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band) -{ - int ret = 0; - int i = 0; - - struct chan_freq_power *cfp; - int cfp_no; - - lbs_deb_enter(LBS_DEB_MAIN); - - memset(priv->region_channel, 0, sizeof(priv->region_channel)); - - cfp = lbs_get_region_cfp_table(region, &cfp_no); - if (cfp != NULL) { - priv->region_channel[i].nrcfp = cfp_no; - priv->region_channel[i].CFP = cfp; - } else { - lbs_deb_main("wrong region code %#x in band B/G\n", - region); - ret = -1; - goto out; - } - priv->region_channel[i].valid = 1; - priv->region_channel[i].region = region; - priv->region_channel[i].band = band; - i++; -out: - lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); - return ret; -} - void lbs_queue_event(struct lbs_private *priv, u32 event) { unsigned long flags; diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c new file mode 100644 index 000000000000..2f91c9b808af --- /dev/null +++ b/drivers/net/wireless/libertas/mesh.c @@ -0,0 +1,1141 @@ +#include <linux/moduleparam.h> +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/kthread.h> +#include <linux/kfifo.h> + +#include "mesh.h" +#include "decl.h" +#include "cmd.h" + + +/*************************************************************************** + * Mesh sysfs support + */ + +/** + * Attributes exported through sysfs + */ + +/** + * @brief Get function for sysfs attribute anycast_mask + */ +static ssize_t lbs_anycast_get(struct device *dev, + struct device_attribute *attr, char * buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + + memset(&mesh_access, 0, sizeof(mesh_access)); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access); + if (ret) + return ret; + + return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0])); +} + +/** + * @brief Set function for sysfs attribute anycast_mask + */ +static ssize_t lbs_anycast_set(struct device *dev, + struct device_attribute *attr, const char * buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + uint32_t datum; + int ret; + + memset(&mesh_access, 0, sizeof(mesh_access)); + sscanf(buf, "%x", &datum); + mesh_access.data[0] = cpu_to_le32(datum); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * @brief Get function for sysfs attribute prb_rsp_limit + */ +static ssize_t lbs_prb_rsp_limit_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + u32 retry_limit; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, + &mesh_access); + if (ret) + return ret; + + retry_limit = le32_to_cpu(mesh_access.data[1]); + return snprintf(buf, 10, "%d\n", retry_limit); +} + +/** + * @brief Set function for sysfs attribute prb_rsp_limit + */ +static ssize_t lbs_prb_rsp_limit_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + unsigned long retry_limit; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); + + if (!strict_strtoul(buf, 10, &retry_limit)) + return -ENOTSUPP; + if (retry_limit > 15) + return -ENOTSUPP; + + mesh_access.data[1] = cpu_to_le32(retry_limit); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, + &mesh_access); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * Get function for sysfs attribute mesh + */ +static ssize_t lbs_mesh_get(struct device *dev, + struct device_attribute *attr, char * buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev); +} + +/** + * Set function for sysfs attribute mesh + */ +static ssize_t lbs_mesh_set(struct device *dev, + struct device_attribute *attr, const char * buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + int enable; + int ret, action = CMD_ACT_MESH_CONFIG_STOP; + + sscanf(buf, "%x", &enable); + enable = !!enable; + if (enable == !!priv->mesh_dev) + return count; + if (enable) + action = CMD_ACT_MESH_CONFIG_START; + ret = lbs_mesh_config(priv, action, priv->channel); + if (ret) + return ret; + + if (enable) + lbs_add_mesh(priv); + else + lbs_remove_mesh(priv); + + return count; +} + +/** + * lbs_mesh attribute to be exported per ethX interface + * through sysfs (/sys/class/net/ethX/lbs_mesh) + */ +static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set); + +/** + * anycast_mask attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/anycast_mask) + */ +static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); + +/** + * prb_rsp_limit attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/prb_rsp_limit) + */ +static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get, + lbs_prb_rsp_limit_set); + +static struct attribute *lbs_mesh_sysfs_entries[] = { + &dev_attr_anycast_mask.attr, + &dev_attr_prb_rsp_limit.attr, + NULL, +}; + +static struct attribute_group lbs_mesh_attr_group = { + .attrs = lbs_mesh_sysfs_entries, +}; + + + +/*************************************************************************** + * Initializing and starting, stopping mesh + */ + +/* + * Check mesh FW version and appropriately send the mesh start + * command + */ +int lbs_init_mesh(struct lbs_private *priv) +{ + struct net_device *dev = priv->dev; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + if (priv->mesh_fw_ver == MESH_FW_OLD) { + /* Enable mesh, if supported, and work out which TLV it uses. + 0x100 + 291 is an unofficial value used in 5.110.20.pXX + 0x100 + 37 is the official value used in 5.110.21.pXX + but we check them in that order because 20.pXX doesn't + give an error -- it just silently fails. */ + + /* 5.110.20.pXX firmware will fail the command if the channel + doesn't match the existing channel. But only if the TLV + is correct. If the channel is wrong, _BOTH_ versions will + give an error to 0x100+291, and allow 0x100+37 to succeed. + It's just that 5.110.20.pXX will not have done anything + useful */ + + priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, + priv->channel)) { + priv->mesh_tlv = TLV_TYPE_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, + priv->channel)) + priv->mesh_tlv = 0; + } + } else if (priv->mesh_fw_ver == MESH_FW_NEW) { + /* 10.0.0.pXX new firmwares should succeed with TLV + * 0x100+37; Do not invoke command with old TLV. + */ + priv->mesh_tlv = TLV_TYPE_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, + priv->channel)) + priv->mesh_tlv = 0; + } + if (priv->mesh_tlv) { + lbs_add_mesh(priv); + + if (device_create_file(&dev->dev, &dev_attr_lbs_mesh)) + lbs_pr_err("cannot register lbs_mesh attribute\n"); + + ret = 1; + } + + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + + +int lbs_deinit_mesh(struct lbs_private *priv) +{ + struct net_device *dev = priv->dev; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + if (priv->mesh_tlv) { + device_remove_file(&dev->dev, &dev_attr_lbs_mesh); + ret = 1; + } + + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + + +/** + * @brief This function closes the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int lbs_mesh_stop(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + + lbs_deb_enter(LBS_DEB_MESH); + spin_lock_irq(&priv->driver_lock); + + priv->mesh_open = 0; + priv->mesh_connect_status = LBS_DISCONNECTED; + + netif_stop_queue(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&priv->driver_lock); + + schedule_work(&priv->mcast_work); + + lbs_deb_leave(LBS_DEB_MESH); + return 0; +} + +/** + * @brief This function opens the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 or -EBUSY if monitor mode active + */ +static int lbs_mesh_dev_open(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + int ret = 0; + + lbs_deb_enter(LBS_DEB_NET); + + spin_lock_irq(&priv->driver_lock); + + if (priv->monitormode) { + ret = -EBUSY; + goto out; + } + + priv->mesh_open = 1; + priv->mesh_connect_status = LBS_CONNECTED; + netif_carrier_on(dev); + + if (!priv->tx_pending_len) + netif_wake_queue(dev); + out: + + spin_unlock_irq(&priv->driver_lock); + lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); + return ret; +} + +static const struct net_device_ops mesh_netdev_ops = { + .ndo_open = lbs_mesh_dev_open, + .ndo_stop = lbs_mesh_stop, + .ndo_start_xmit = lbs_hard_start_xmit, + .ndo_set_mac_address = lbs_set_mac_address, + .ndo_set_multicast_list = lbs_set_multicast_list, +}; + +/** + * @brief This function adds mshX interface + * + * @param priv A pointer to the struct lbs_private structure + * @return 0 if successful, -X otherwise + */ +int lbs_add_mesh(struct lbs_private *priv) +{ + struct net_device *mesh_dev = NULL; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + /* Allocate a virtual mesh device */ + mesh_dev = alloc_netdev(0, "msh%d", ether_setup); + if (!mesh_dev) { + lbs_deb_mesh("init mshX device failed\n"); + ret = -ENOMEM; + goto done; + } + mesh_dev->ml_priv = priv; + priv->mesh_dev = mesh_dev; + + mesh_dev->netdev_ops = &mesh_netdev_ops; + mesh_dev->ethtool_ops = &lbs_ethtool_ops; + memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, + sizeof(priv->dev->dev_addr)); + + SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); + +#ifdef WIRELESS_EXT + mesh_dev->wireless_handlers = &mesh_handler_def; +#endif + mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + /* Register virtual mesh interface */ + ret = register_netdev(mesh_dev); + if (ret) { + lbs_pr_err("cannot register mshX virtual interface\n"); + goto err_free; + } + + ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); + if (ret) + goto err_unregister; + + lbs_persist_config_init(mesh_dev); + + /* Everything successful */ + ret = 0; + goto done; + +err_unregister: + unregister_netdev(mesh_dev); + +err_free: + free_netdev(mesh_dev); + +done: + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + +void lbs_remove_mesh(struct lbs_private *priv) +{ + struct net_device *mesh_dev; + + mesh_dev = priv->mesh_dev; + if (!mesh_dev) + return; + + lbs_deb_enter(LBS_DEB_MESH); + netif_stop_queue(mesh_dev); + netif_carrier_off(mesh_dev); + sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); + lbs_persist_config_remove(mesh_dev); + unregister_netdev(mesh_dev); + priv->mesh_dev = NULL; + free_netdev(mesh_dev); + lbs_deb_leave(LBS_DEB_MESH); +} + + + +/*************************************************************************** + * Sending and receiving + */ +struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, + struct net_device *dev, struct rxpd *rxpd) +{ + if (priv->mesh_dev) { + if (priv->mesh_fw_ver == MESH_FW_OLD) { + if (rxpd->rx_control & RxPD_MESH_FRAME) + dev = priv->mesh_dev; + } else if (priv->mesh_fw_ver == MESH_FW_NEW) { + if (rxpd->u.bss.bss_num == MESH_IFACE_ID) + dev = priv->mesh_dev; + } + } + return dev; +} + + +void lbs_mesh_set_txpd(struct lbs_private *priv, + struct net_device *dev, struct txpd *txpd) +{ + if (dev == priv->mesh_dev) { + if (priv->mesh_fw_ver == MESH_FW_OLD) + txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); + else if (priv->mesh_fw_ver == MESH_FW_NEW) + txpd->u.bss.bss_num = MESH_IFACE_ID; + } +} + + +/*************************************************************************** + * Mesh command handling + */ + +int lbs_cmd_bt_access(struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_bt_access *bt_access = &cmd->params.bt; + lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); + + cmd->command = cpu_to_le16(CMD_BT_ACCESS); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + + sizeof(struct cmd_header)); + cmd->result = 0; + bt_access->action = cpu_to_le16(cmd_action); + + switch (cmd_action) { + case CMD_ACT_BT_ACCESS_ADD: + memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN); + lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr", + bt_access->addr1, 6); + break; + case CMD_ACT_BT_ACCESS_DEL: + memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN); + lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr", + bt_access->addr1, 6); + break; + case CMD_ACT_BT_ACCESS_LIST: + bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); + break; + case CMD_ACT_BT_ACCESS_RESET: + break; + case CMD_ACT_BT_ACCESS_SET_INVERT: + bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); + break; + case CMD_ACT_BT_ACCESS_GET_INVERT: + break; + default: + break; + } + lbs_deb_leave(LBS_DEB_CMD); + return 0; +} + +int lbs_cmd_fwt_access(struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt; + lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); + + cmd->command = cpu_to_le16(CMD_FWT_ACCESS); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + + sizeof(struct cmd_header)); + cmd->result = 0; + + if (pdata_buf) + memcpy(fwt_access, pdata_buf, sizeof(*fwt_access)); + else + memset(fwt_access, 0, sizeof(*fwt_access)); + + fwt_access->action = cpu_to_le16(cmd_action); + + lbs_deb_leave(LBS_DEB_CMD); + return 0; +} + +int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, + struct cmd_ds_mesh_access *cmd) +{ + int ret; + + lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); + + cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); + cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); + cmd->hdr.result = 0; + + cmd->action = cpu_to_le16(cmd_action); + + ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int __lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + u16 command = CMD_MESH_CONFIG_OLD; + + lbs_deb_enter(LBS_DEB_CMD); + + /* + * Command id is 0xac for v10 FW along with mesh interface + * id in bits 14-13-12. + */ + if (priv->mesh_fw_ver == MESH_FW_NEW) + command = CMD_MESH_CONFIG | + (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); + + cmd->hdr.command = cpu_to_le16(command); + cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); + cmd->hdr.result = 0; + + cmd->type = cpu_to_le16(type); + cmd->action = cpu_to_le16(action); + + ret = lbs_cmd_with_response(priv, command, cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +int lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + + if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) + return -EOPNOTSUPP; + + ret = __lbs_mesh_config_send(priv, cmd, action, type); + return ret; +} + +/* This function is the CMD_MESH_CONFIG legacy function. It only handles the + * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG + * are all handled by preparing a struct cmd_ds_mesh_config and passing it to + * lbs_mesh_config_send. + */ +int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_meshie *ie; + DECLARE_SSID_BUF(ssid); + + memset(&cmd, 0, sizeof(cmd)); + cmd.channel = cpu_to_le16(chan); + ie = (struct mrvl_meshie *)cmd.data; + + switch (action) { + case CMD_ACT_MESH_CONFIG_START: + ie->id = WLAN_EID_GENERIC; + ie->val.oui[0] = 0x00; + ie->val.oui[1] = 0x50; + ie->val.oui[2] = 0x43; + ie->val.type = MARVELL_MESH_IE_TYPE; + ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; + ie->val.version = MARVELL_MESH_IE_VERSION; + ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; + ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; + ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; + ie->val.mesh_id_len = priv->mesh_ssid_len; + memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); + ie->len = sizeof(struct mrvl_meshie_val) - + IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len; + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); + break; + case CMD_ACT_MESH_CONFIG_STOP: + break; + default: + return -1; + } + lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n", + action, priv->mesh_tlv, chan, + print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len)); + + return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); +} + + + +/*************************************************************************** + * Persistent configuration support + */ + +static int mesh_get_default_parameters(struct device *dev, + struct mrvl_mesh_defaults *defs) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + int ret; + + memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, + CMD_TYPE_MESH_GET_DEFAULTS); + + if (ret) + return -EOPNOTSUPP; + + memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); + + return 0; +} + +/** + * @brief Get function for sysfs attribute bootflag + */ +static ssize_t bootflag_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); +} + +/** + * @brief Set function for sysfs attribute bootflag + */ +static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 1)) + return -EINVAL; + + *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); + cmd.length = cpu_to_le16(sizeof(uint32_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_BOOTFLAG); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * @brief Get function for sysfs attribute boottime + */ +static ssize_t boottime_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", defs.boottime); +} + +/** + * @brief Set function for sysfs attribute boottime + */ +static ssize_t boottime_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* A too small boot time will result in the device booting into + * standalone (no-host) mode before the host can take control of it, + * so the change will be hard to revert. This may be a desired + * feature (e.g to configure a very fast boot time for devices that + * will not be attached to a host), but dangerous. So I'm enforcing a + * lower limit of 20 seconds: remove and recompile the driver if this + * does not work for you. + */ + datum = (datum < 20) ? 20 : datum; + cmd.data[0] = datum; + cmd.length = cpu_to_le16(sizeof(uint8_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_BOOTTIME); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * @brief Get function for sysfs attribute channel + */ +static ssize_t channel_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); +} + +/** + * @brief Set function for sysfs attribute channel + */ +static ssize_t channel_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if (ret != 1 || datum < 1 || datum > 11) + return -EINVAL; + + *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); + cmd.length = cpu_to_le16(sizeof(uint16_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_DEF_CHANNEL); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * @brief Get function for sysfs attribute mesh_id + */ +static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mrvl_mesh_defaults defs; + int maxlen; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) { + lbs_pr_err("inconsistent mesh ID length"); + defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN; + } + + /* SSID not null terminated: reserve room for \0 + \n */ + maxlen = defs.meshie.val.mesh_id_len + 2; + maxlen = (PAGE_SIZE > maxlen) ? maxlen : PAGE_SIZE; + + defs.meshie.val.mesh_id[defs.meshie.val.mesh_id_len] = '\0'; + + return snprintf(buf, maxlen, "%s\n", defs.meshie.val.mesh_id); +} + +/** + * @brief Set function for sysfs attribute mesh_id + */ +static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + int len; + int ret; + + if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1) + return -EINVAL; + + memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); + ie = (struct mrvl_meshie *) &cmd.data[0]; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + + len = count - 1; + memcpy(ie->val.mesh_id, buf, len); + /* SSID len */ + ie->val.mesh_id_len = len; + /* IE len */ + ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * @brief Get function for sysfs attribute protocol_id + */ +static ssize_t protocol_id_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id); +} + +/** + * @brief Set function for sysfs attribute protocol_id + */ +static ssize_t protocol_id_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update protocol id */ + ie->val.active_protocol_id = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * @brief Get function for sysfs attribute metric_id + */ +static ssize_t metric_id_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id); +} + +/** + * @brief Set function for sysfs attribute metric_id + */ +static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update metric id */ + ie->val.active_metric_id = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * @brief Get function for sysfs attribute capability + */ +static ssize_t capability_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability); +} + +/** + * @brief Set function for sysfs attribute capability + */ +static ssize_t capability_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update value */ + ie->val.mesh_capability = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + + +static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set); +static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set); +static DEVICE_ATTR(channel, 0644, channel_get, channel_set); +static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set); +static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set); +static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set); +static DEVICE_ATTR(capability, 0644, capability_get, capability_set); + +static struct attribute *boot_opts_attrs[] = { + &dev_attr_bootflag.attr, + &dev_attr_boottime.attr, + &dev_attr_channel.attr, + NULL +}; + +static struct attribute_group boot_opts_group = { + .name = "boot_options", + .attrs = boot_opts_attrs, +}; + +static struct attribute *mesh_ie_attrs[] = { + &dev_attr_mesh_id.attr, + &dev_attr_protocol_id.attr, + &dev_attr_metric_id.attr, + &dev_attr_capability.attr, + NULL +}; + +static struct attribute_group mesh_ie_group = { + .name = "mesh_ie", + .attrs = mesh_ie_attrs, +}; + +void lbs_persist_config_init(struct net_device *dev) +{ + int ret; + ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); + ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); +} + +void lbs_persist_config_remove(struct net_device *dev) +{ + sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); + sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); +} + + + +/*************************************************************************** + * Ethtool related + */ + +static const char *mesh_stat_strings[] = { + "drop_duplicate_bcast", + "drop_ttl_zero", + "drop_no_fwd_route", + "drop_no_buffers", + "fwded_unicast_cnt", + "fwded_bcast_cnt", + "drop_blind_table", + "tx_failed_cnt" +}; + +void lbs_mesh_ethtool_get_stats(struct net_device *dev, + struct ethtool_stats *stats, uint64_t *data) +{ + struct lbs_private *priv = dev->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + + lbs_deb_enter(LBS_DEB_ETHTOOL); + + /* Get Mesh Statistics */ + ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access); + + if (ret) { + memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t))); + return; + } + + priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]); + priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]); + priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]); + priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]); + priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]); + priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]); + priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]); + priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]); + + data[0] = priv->mstats.fwd_drop_rbt; + data[1] = priv->mstats.fwd_drop_ttl; + data[2] = priv->mstats.fwd_drop_noroute; + data[3] = priv->mstats.fwd_drop_nobuf; + data[4] = priv->mstats.fwd_unicast_cnt; + data[5] = priv->mstats.fwd_bcast_cnt; + data[6] = priv->mstats.drop_blind; + data[7] = priv->mstats.tx_failed_cnt; + + lbs_deb_enter(LBS_DEB_ETHTOOL); +} + +int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset) +{ + struct lbs_private *priv = dev->ml_priv; + + if (sset == ETH_SS_STATS && dev == priv->mesh_dev) + return MESH_STATS_NUM; + + return -EOPNOTSUPP; +} + +void lbs_mesh_ethtool_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *s) +{ + int i; + + lbs_deb_enter(LBS_DEB_ETHTOOL); + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < MESH_STATS_NUM; i++) { + memcpy(s + i * ETH_GSTRING_LEN, + mesh_stat_strings[i], + ETH_GSTRING_LEN); + } + break; + } + lbs_deb_enter(LBS_DEB_ETHTOOL); +} diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h new file mode 100644 index 000000000000..fea9b5d005fc --- /dev/null +++ b/drivers/net/wireless/libertas/mesh.h @@ -0,0 +1,78 @@ +/** + * Contains all definitions needed for the Libertas' MESH implementation. + */ +#ifndef _LBS_MESH_H_ +#define _LBS_MESH_H_ + + +#include <net/iw_handler.h> +#include <net/lib80211.h> + + +/* Mesh statistics */ +struct lbs_mesh_stats { + u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ + u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ + u32 fwd_drop_ttl; /* Fwd: TTL zero */ + u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ + u32 fwd_drop_noroute; /* Fwd: No route to Destination */ + u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ + u32 drop_blind; /* Rx: Dropped by blinding table */ + u32 tx_failed_cnt; /* Tx: Failed transmissions */ +}; + + +struct net_device; +struct lbs_private; + +int lbs_init_mesh(struct lbs_private *priv); +int lbs_deinit_mesh(struct lbs_private *priv); + +int lbs_add_mesh(struct lbs_private *priv); +void lbs_remove_mesh(struct lbs_private *priv); + + +/* Sending / Receiving */ + +struct rxpd; +struct txpd; + +struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, + struct net_device *dev, struct rxpd *rxpd); +void lbs_mesh_set_txpd(struct lbs_private *priv, + struct net_device *dev, struct txpd *txpd); + + +/* Command handling */ + +struct cmd_ds_command; + +int lbs_cmd_bt_access(struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf); +int lbs_cmd_fwt_access(struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf); + + +/* Persistent configuration */ + +void lbs_persist_config_init(struct net_device *net); +void lbs_persist_config_remove(struct net_device *net); + + +/* WEXT handler */ + +extern struct iw_handler_def mesh_handler_def; + + +/* Ethtool statistics */ + +struct ethtool_stats; + +void lbs_mesh_ethtool_get_stats(struct net_device *dev, + struct ethtool_stats *stats, uint64_t *data); +int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset); +void lbs_mesh_ethtool_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *s); + + +#endif diff --git a/drivers/net/wireless/libertas/persistcfg.c b/drivers/net/wireless/libertas/persistcfg.c deleted file mode 100644 index 18fe29faf99b..000000000000 --- a/drivers/net/wireless/libertas/persistcfg.c +++ /dev/null @@ -1,453 +0,0 @@ -#include <linux/moduleparam.h> -#include <linux/delay.h> -#include <linux/etherdevice.h> -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/kthread.h> -#include <linux/kfifo.h> - -#include "host.h" -#include "decl.h" -#include "dev.h" -#include "wext.h" -#include "debugfs.h" -#include "scan.h" -#include "assoc.h" -#include "cmd.h" - -static int mesh_get_default_parameters(struct device *dev, - struct mrvl_mesh_defaults *defs) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - int ret; - - memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, - CMD_TYPE_MESH_GET_DEFAULTS); - - if (ret) - return -EOPNOTSUPP; - - memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); - - return 0; -} - -/** - * @brief Get function for sysfs attribute bootflag - */ -static ssize_t bootflag_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); -} - -/** - * @brief Set function for sysfs attribute bootflag - */ -static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 1)) - return -EINVAL; - - *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); - cmd.length = cpu_to_le16(sizeof(uint32_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_BOOTFLAG); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * @brief Get function for sysfs attribute boottime - */ -static ssize_t boottime_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", defs.boottime); -} - -/** - * @brief Set function for sysfs attribute boottime - */ -static ssize_t boottime_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* A too small boot time will result in the device booting into - * standalone (no-host) mode before the host can take control of it, - * so the change will be hard to revert. This may be a desired - * feature (e.g to configure a very fast boot time for devices that - * will not be attached to a host), but dangerous. So I'm enforcing a - * lower limit of 20 seconds: remove and recompile the driver if this - * does not work for you. - */ - datum = (datum < 20) ? 20 : datum; - cmd.data[0] = datum; - cmd.length = cpu_to_le16(sizeof(uint8_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_BOOTTIME); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * @brief Get function for sysfs attribute channel - */ -static ssize_t channel_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); -} - -/** - * @brief Set function for sysfs attribute channel - */ -static ssize_t channel_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if (ret != 1 || datum < 1 || datum > 11) - return -EINVAL; - - *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); - cmd.length = cpu_to_le16(sizeof(uint16_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_DEF_CHANNEL); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * @brief Get function for sysfs attribute mesh_id - */ -static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct mrvl_mesh_defaults defs; - int maxlen; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - if (defs.meshie.val.mesh_id_len > IW_ESSID_MAX_SIZE) { - lbs_pr_err("inconsistent mesh ID length"); - defs.meshie.val.mesh_id_len = IW_ESSID_MAX_SIZE; - } - - /* SSID not null terminated: reserve room for \0 + \n */ - maxlen = defs.meshie.val.mesh_id_len + 2; - maxlen = (PAGE_SIZE > maxlen) ? maxlen : PAGE_SIZE; - - defs.meshie.val.mesh_id[defs.meshie.val.mesh_id_len] = '\0'; - - return snprintf(buf, maxlen, "%s\n", defs.meshie.val.mesh_id); -} - -/** - * @brief Set function for sysfs attribute mesh_id - */ -static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - int len; - int ret; - - if (count < 2 || count > IW_ESSID_MAX_SIZE + 1) - return -EINVAL; - - memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); - ie = (struct mrvl_meshie *) &cmd.data[0]; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - - len = count - 1; - memcpy(ie->val.mesh_id, buf, len); - /* SSID len */ - ie->val.mesh_id_len = len; - /* IE len */ - ie->len = sizeof(struct mrvl_meshie_val) - IW_ESSID_MAX_SIZE + len; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * @brief Get function for sysfs attribute protocol_id - */ -static ssize_t protocol_id_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id); -} - -/** - * @brief Set function for sysfs attribute protocol_id - */ -static ssize_t protocol_id_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update protocol id */ - ie->val.active_protocol_id = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * @brief Get function for sysfs attribute metric_id - */ -static ssize_t metric_id_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id); -} - -/** - * @brief Set function for sysfs attribute metric_id - */ -static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update metric id */ - ie->val.active_metric_id = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * @brief Get function for sysfs attribute capability - */ -static ssize_t capability_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability); -} - -/** - * @brief Set function for sysfs attribute capability - */ -static ssize_t capability_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update value */ - ie->val.mesh_capability = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - - -static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set); -static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set); -static DEVICE_ATTR(channel, 0644, channel_get, channel_set); -static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set); -static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set); -static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set); -static DEVICE_ATTR(capability, 0644, capability_get, capability_set); - -static struct attribute *boot_opts_attrs[] = { - &dev_attr_bootflag.attr, - &dev_attr_boottime.attr, - &dev_attr_channel.attr, - NULL -}; - -static struct attribute_group boot_opts_group = { - .name = "boot_options", - .attrs = boot_opts_attrs, -}; - -static struct attribute *mesh_ie_attrs[] = { - &dev_attr_mesh_id.attr, - &dev_attr_protocol_id.attr, - &dev_attr_metric_id.attr, - &dev_attr_capability.attr, - NULL -}; - -static struct attribute_group mesh_ie_group = { - .name = "mesh_ie", - .attrs = mesh_ie_attrs, -}; - -void lbs_persist_config_init(struct net_device *dev) -{ - int ret; - ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); - ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); -} - -void lbs_persist_config_remove(struct net_device *dev) -{ - sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); - sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); -} diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c index 65f02cc6752f..2daf8ffdb7e1 100644 --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -4,7 +4,7 @@ #include <linux/etherdevice.h> #include <linux/types.h> -#include "hostcmd.h" +#include "host.h" #include "radiotap.h" #include "decl.h" #include "dev.h" @@ -160,15 +160,8 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) p_rx_pd = (struct rxpd *) skb->data; p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + le32_to_cpu(p_rx_pd->pkt_ptr)); - if (priv->mesh_dev) { - if (priv->mesh_fw_ver == MESH_FW_OLD) { - if (p_rx_pd->rx_control & RxPD_MESH_FRAME) - dev = priv->mesh_dev; - } else if (priv->mesh_fw_ver == MESH_FW_NEW) { - if (p_rx_pd->u.bss.bss_num == MESH_IFACE_ID) - dev = priv->mesh_dev; - } - } + + dev = lbs_mesh_set_dev(priv, dev, p_rx_pd); lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min_t(unsigned int, skb->len, 100)); diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c index 6c95af3023cc..c6a6c042b82f 100644 --- a/drivers/net/wireless/libertas/scan.c +++ b/drivers/net/wireless/libertas/scan.c @@ -12,18 +12,19 @@ #include <net/lib80211.h> #include "host.h" -#include "decl.h" #include "dev.h" #include "scan.h" +#include "assoc.h" +#include "wext.h" #include "cmd.h" //! Approximate amount of data needed to pass a scan result back to iwlist #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ - + IW_ESSID_MAX_SIZE \ + + IEEE80211_MAX_SSID_LEN \ + IW_EV_UINT_LEN \ + IW_EV_FREQ_LEN \ + IW_EV_QUAL_LEN \ - + IW_ESSID_MAX_SIZE \ + + IEEE80211_MAX_SSID_LEN \ + IW_EV_PARAM_LEN \ + 40) /* 40 for WPAIE */ @@ -121,6 +122,189 @@ static inline int is_same_network(struct bss_descriptor *src, +/*********************************************************************/ +/* */ +/* Region channel support */ +/* */ +/*********************************************************************/ + +#define LBS_TX_PWR_DEFAULT 20 /*100mW */ +#define LBS_TX_PWR_US_DEFAULT 20 /*100mW */ +#define LBS_TX_PWR_JP_DEFAULT 16 /*50mW */ +#define LBS_TX_PWR_FR_DEFAULT 20 /*100mW */ +#define LBS_TX_PWR_EMEA_DEFAULT 20 /*100mW */ + +/* Format { channel, frequency (MHz), maxtxpower } */ +/* band: 'B/G', region: USA FCC/Canada IC */ +static struct chan_freq_power channel_freq_power_US_BG[] = { + {1, 2412, LBS_TX_PWR_US_DEFAULT}, + {2, 2417, LBS_TX_PWR_US_DEFAULT}, + {3, 2422, LBS_TX_PWR_US_DEFAULT}, + {4, 2427, LBS_TX_PWR_US_DEFAULT}, + {5, 2432, LBS_TX_PWR_US_DEFAULT}, + {6, 2437, LBS_TX_PWR_US_DEFAULT}, + {7, 2442, LBS_TX_PWR_US_DEFAULT}, + {8, 2447, LBS_TX_PWR_US_DEFAULT}, + {9, 2452, LBS_TX_PWR_US_DEFAULT}, + {10, 2457, LBS_TX_PWR_US_DEFAULT}, + {11, 2462, LBS_TX_PWR_US_DEFAULT} +}; + +/* band: 'B/G', region: Europe ETSI */ +static struct chan_freq_power channel_freq_power_EU_BG[] = { + {1, 2412, LBS_TX_PWR_EMEA_DEFAULT}, + {2, 2417, LBS_TX_PWR_EMEA_DEFAULT}, + {3, 2422, LBS_TX_PWR_EMEA_DEFAULT}, + {4, 2427, LBS_TX_PWR_EMEA_DEFAULT}, + {5, 2432, LBS_TX_PWR_EMEA_DEFAULT}, + {6, 2437, LBS_TX_PWR_EMEA_DEFAULT}, + {7, 2442, LBS_TX_PWR_EMEA_DEFAULT}, + {8, 2447, LBS_TX_PWR_EMEA_DEFAULT}, + {9, 2452, LBS_TX_PWR_EMEA_DEFAULT}, + {10, 2457, LBS_TX_PWR_EMEA_DEFAULT}, + {11, 2462, LBS_TX_PWR_EMEA_DEFAULT}, + {12, 2467, LBS_TX_PWR_EMEA_DEFAULT}, + {13, 2472, LBS_TX_PWR_EMEA_DEFAULT} +}; + +/* band: 'B/G', region: Spain */ +static struct chan_freq_power channel_freq_power_SPN_BG[] = { + {10, 2457, LBS_TX_PWR_DEFAULT}, + {11, 2462, LBS_TX_PWR_DEFAULT} +}; + +/* band: 'B/G', region: France */ +static struct chan_freq_power channel_freq_power_FR_BG[] = { + {10, 2457, LBS_TX_PWR_FR_DEFAULT}, + {11, 2462, LBS_TX_PWR_FR_DEFAULT}, + {12, 2467, LBS_TX_PWR_FR_DEFAULT}, + {13, 2472, LBS_TX_PWR_FR_DEFAULT} +}; + +/* band: 'B/G', region: Japan */ +static struct chan_freq_power channel_freq_power_JPN_BG[] = { + {1, 2412, LBS_TX_PWR_JP_DEFAULT}, + {2, 2417, LBS_TX_PWR_JP_DEFAULT}, + {3, 2422, LBS_TX_PWR_JP_DEFAULT}, + {4, 2427, LBS_TX_PWR_JP_DEFAULT}, + {5, 2432, LBS_TX_PWR_JP_DEFAULT}, + {6, 2437, LBS_TX_PWR_JP_DEFAULT}, + {7, 2442, LBS_TX_PWR_JP_DEFAULT}, + {8, 2447, LBS_TX_PWR_JP_DEFAULT}, + {9, 2452, LBS_TX_PWR_JP_DEFAULT}, + {10, 2457, LBS_TX_PWR_JP_DEFAULT}, + {11, 2462, LBS_TX_PWR_JP_DEFAULT}, + {12, 2467, LBS_TX_PWR_JP_DEFAULT}, + {13, 2472, LBS_TX_PWR_JP_DEFAULT}, + {14, 2484, LBS_TX_PWR_JP_DEFAULT} +}; + +/** + * the structure for channel, frequency and power + */ +struct region_cfp_table { + u8 region; + struct chan_freq_power *cfp_BG; + int cfp_no_BG; +}; + +/** + * the structure for the mapping between region and CFP + */ +static struct region_cfp_table region_cfp_table[] = { + {0x10, /*US FCC */ + channel_freq_power_US_BG, + ARRAY_SIZE(channel_freq_power_US_BG), + } + , + {0x20, /*CANADA IC */ + channel_freq_power_US_BG, + ARRAY_SIZE(channel_freq_power_US_BG), + } + , + {0x30, /*EU*/ channel_freq_power_EU_BG, + ARRAY_SIZE(channel_freq_power_EU_BG), + } + , + {0x31, /*SPAIN*/ channel_freq_power_SPN_BG, + ARRAY_SIZE(channel_freq_power_SPN_BG), + } + , + {0x32, /*FRANCE*/ channel_freq_power_FR_BG, + ARRAY_SIZE(channel_freq_power_FR_BG), + } + , + {0x40, /*JAPAN*/ channel_freq_power_JPN_BG, + ARRAY_SIZE(channel_freq_power_JPN_BG), + } + , +/*Add new region here */ +}; + +/** + * @brief This function finds the CFP in + * region_cfp_table based on region and band parameter. + * + * @param region The region code + * @param band The band + * @param cfp_no A pointer to CFP number + * @return A pointer to CFP + */ +static struct chan_freq_power *lbs_get_region_cfp_table(u8 region, int *cfp_no) +{ + int i, end; + + lbs_deb_enter(LBS_DEB_MAIN); + + end = ARRAY_SIZE(region_cfp_table); + + for (i = 0; i < end ; i++) { + lbs_deb_main("region_cfp_table[i].region=%d\n", + region_cfp_table[i].region); + if (region_cfp_table[i].region == region) { + *cfp_no = region_cfp_table[i].cfp_no_BG; + lbs_deb_leave(LBS_DEB_MAIN); + return region_cfp_table[i].cfp_BG; + } + } + + lbs_deb_leave_args(LBS_DEB_MAIN, "ret NULL"); + return NULL; +} + +int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band) +{ + int ret = 0; + int i = 0; + + struct chan_freq_power *cfp; + int cfp_no; + + lbs_deb_enter(LBS_DEB_MAIN); + + memset(priv->region_channel, 0, sizeof(priv->region_channel)); + + cfp = lbs_get_region_cfp_table(region, &cfp_no); + if (cfp != NULL) { + priv->region_channel[i].nrcfp = cfp_no; + priv->region_channel[i].CFP = cfp; + } else { + lbs_deb_main("wrong region code %#x in band B/G\n", + region); + ret = -1; + goto out; + } + priv->region_channel[i].valid = 1; + priv->region_channel[i].region = region; + priv->region_channel[i].band = band; + i++; +out: + lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); + return ret; +} + + + /*********************************************************************/ /* */ @@ -161,31 +345,15 @@ static int lbs_scan_create_channel_list(struct lbs_private *priv, scantype = CMD_SCAN_TYPE_ACTIVE; for (rgnidx = 0; rgnidx < ARRAY_SIZE(priv->region_channel); rgnidx++) { - if (priv->enable11d && (priv->connect_status != LBS_CONNECTED) - && (priv->mesh_connect_status != LBS_CONNECTED)) { - /* Scan all the supported chan for the first scan */ - if (!priv->universal_channel[rgnidx].valid) - continue; - scanregion = &priv->universal_channel[rgnidx]; - - /* clear the parsed_region_chan for the first scan */ - memset(&priv->parsed_region_chan, 0x00, - sizeof(priv->parsed_region_chan)); - } else { - if (!priv->region_channel[rgnidx].valid) - continue; - scanregion = &priv->region_channel[rgnidx]; - } + if (!priv->region_channel[rgnidx].valid) + continue; + scanregion = &priv->region_channel[rgnidx]; for (nextchan = 0; nextchan < scanregion->nrcfp; nextchan++, chanidx++) { struct chanscanparamset *chan = &scanchanlist[chanidx]; cfp = scanregion->CFP + nextchan; - if (priv->enable11d) - scantype = lbs_get_scan_type_11d(cfp->channel, - &priv->parsed_region_chan); - if (scanregion->band == BAND_B || scanregion->band == BAND_G) chan->radiotype = CMD_SCAN_RADIO_TYPE_BG; @@ -519,7 +687,6 @@ static int lbs_process_bss(struct bss_descriptor *bss, struct ieee_ie_cf_param_set *cf; struct ieee_ie_ibss_param_set *ibss; DECLARE_SSID_BUF(ssid); - struct ieee_ie_country_info_set *pcountryinfo; uint8_t *pos, *end, *p; uint8_t n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0; uint16_t beaconsize = 0; @@ -642,26 +809,6 @@ static int lbs_process_bss(struct bss_descriptor *bss, lbs_deb_scan("got IBSS IE\n"); break; - case WLAN_EID_COUNTRY: - pcountryinfo = (struct ieee_ie_country_info_set *) pos; - lbs_deb_scan("got COUNTRY IE\n"); - if (pcountryinfo->header.len < sizeof(pcountryinfo->countrycode) - || pcountryinfo->header.len > 254) { - lbs_deb_scan("%s: 11D- Err CountryInfo len %d, min %zd, max 254\n", - __func__, - pcountryinfo->header.len, - sizeof(pcountryinfo->countrycode)); - ret = -1; - goto done; - } - - memcpy(&bss->countryinfo, pcountryinfo, - pcountryinfo->header.len + 2); - lbs_deb_hex(LBS_DEB_SCAN, "process_bss: 11d countryinfo", - (uint8_t *) pcountryinfo, - (int) (pcountryinfo->header.len + 2)); - break; - case WLAN_EID_EXT_SUPP_RATES: /* only process extended supported rate if data rate is * already found. Data rate IE should come before @@ -812,7 +959,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, /* SSID */ iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; - iwe.u.data.length = min((uint32_t) bss->ssid_len, (uint32_t) IW_ESSID_MAX_SIZE); + iwe.u.data.length = min((uint32_t) bss->ssid_len, (uint32_t) IEEE80211_MAX_SSID_LEN); start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid); /* Mode */ @@ -1022,9 +1169,12 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info, return -EAGAIN; /* Update RSSI if current BSS is a locally created ad-hoc BSS */ - if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate) - lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, - CMD_OPTION_WAITFORRSP, 0, NULL); + if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate) { + err = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, + CMD_OPTION_WAITFORRSP, 0, NULL); + if (err) + goto out; + } mutex_lock(&priv->lock); list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) { @@ -1058,7 +1208,7 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info, dwrq->length = (ev - extra); dwrq->flags = 0; - +out: lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", err); return err; } @@ -1141,11 +1291,11 @@ static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy, /* The size of the TLV buffer is equal to the entire command response * size (scanrespsize) minus the fixed fields (sizeof()'s), the * BSS Descriptions (bssdescriptsize as bytesLef) and the command - * response header (S_DS_GEN) + * response header (sizeof(struct cmd_header)) */ tlvbufsize = scanrespsize - (bytesleft + sizeof(scanresp->bssdescriptsize) + sizeof(scanresp->nr_sets) - + S_DS_GEN); + + sizeof(struct cmd_header)); /* * Process each scan response returned (scanresp->nr_sets). Save diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h index fab7d5d097fc..8fb1706d7526 100644 --- a/drivers/net/wireless/libertas/scan.h +++ b/drivers/net/wireless/libertas/scan.h @@ -9,8 +9,36 @@ #include <net/iw_handler.h> +struct lbs_private; + #define MAX_NETWORK_COUNT 128 +/** Chan-freq-TxPower mapping table*/ +struct chan_freq_power { + /** channel Number */ + u16 channel; + /** frequency of this channel */ + u32 freq; + /** Max allowed Tx power level */ + u16 maxtxpower; + /** TRUE:channel unsupported; FLASE:supported*/ + u8 unsupported; +}; + +/** region-band mapping table*/ +struct region_channel { + /** TRUE if this entry is valid */ + u8 valid; + /** region code for US, Japan ... */ + u8 region; + /** band B/G/A, used for BAND_CONFIG cmd */ + u8 band; + /** Actual No. of elements in the array below */ + u8 nrcfp; + /** chan-freq-txpower mapping table*/ + struct chan_freq_power *CFP; +}; + /** * @brief Maximum number of channels that can be sent in a setuserscan ioctl */ @@ -18,6 +46,8 @@ int lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len); +int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band); + int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid, u8 ssid_len); diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index 8c3766a6e8e7..315d1ce286ca 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c @@ -5,7 +5,7 @@ #include <linux/etherdevice.h> #include <linux/sched.h> -#include "hostcmd.h" +#include "host.h" #include "radiotap.h" #include "decl.h" #include "defs.h" @@ -131,12 +131,7 @@ netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) txpd->tx_packet_length = cpu_to_le16(pkt_len); txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); - if (dev == priv->mesh_dev) { - if (priv->mesh_fw_ver == MESH_FW_OLD) - txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); - else if (priv->mesh_fw_ver == MESH_FW_NEW) - txpd->u.bss.bss_num = MESH_IFACE_ID; - } + lbs_mesh_set_txpd(priv, dev, txpd); lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd)); diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h index 99905df65b25..3e72c86ceca8 100644 --- a/drivers/net/wireless/libertas/types.h +++ b/drivers/net/wireless/libertas/types.h @@ -5,8 +5,8 @@ #define _LBS_TYPES_H_ #include <linux/if_ether.h> +#include <linux/ieee80211.h> #include <asm/byteorder.h> -#include <linux/wireless.h> struct ieee_ie_header { u8 id; @@ -247,7 +247,7 @@ struct mrvl_meshie_val { uint8_t active_metric_id; uint8_t mesh_capability; uint8_t mesh_id_len; - uint8_t mesh_id[IW_ESSID_MAX_SIZE]; + uint8_t mesh_id[IEEE80211_MAX_SSID_LEN]; } __attribute__ ((packed)); struct mrvl_meshie { diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c index be837a0d2517..a8eb9e1fcf36 100644 --- a/drivers/net/wireless/libertas/wext.c +++ b/drivers/net/wireless/libertas/wext.c @@ -45,6 +45,63 @@ static inline void lbs_cancel_association_work(struct lbs_private *priv) priv->pending_assoc_req = NULL; } +void lbs_send_disconnect_notification(struct lbs_private *priv) +{ + union iwreq_data wrqu; + + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); +} + +static void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str) +{ + union iwreq_data iwrq; + u8 buf[50]; + + lbs_deb_enter(LBS_DEB_WEXT); + + memset(&iwrq, 0, sizeof(union iwreq_data)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, sizeof(buf) - 1, "%s", str); + + iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; + + /* Send Event to upper layer */ + lbs_deb_wext("event indication string %s\n", (char *)buf); + lbs_deb_wext("event indication length %d\n", iwrq.data.length); + lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str); + + wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf); + + lbs_deb_leave(LBS_DEB_WEXT); +} + +/** + * @brief This function handles MIC failure event. + * + * @param priv A pointer to struct lbs_private structure + * @para event the event id + * @return n/a + */ +void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event) +{ + char buf[50]; + + lbs_deb_enter(LBS_DEB_CMD); + memset(buf, 0, sizeof(buf)); + + sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); + + if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) + strcat(buf, "unicast "); + else + strcat(buf, "multicast "); + + lbs_send_iwevcustom_event(priv, buf); + lbs_deb_leave(LBS_DEB_CMD); +} /** * @brief Find the channel frequency power info with specific channel @@ -66,8 +123,6 @@ struct chan_freq_power *lbs_find_cfp_by_band_and_channel( for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) { rc = &priv->region_channel[j]; - if (priv->enable11d) - rc = &priv->universal_channel[j]; if (!rc->valid || !rc->CFP) continue; if (rc->band != band) @@ -107,8 +162,6 @@ static struct chan_freq_power *find_cfp_by_band_and_freq( for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) { rc = &priv->region_channel[j]; - if (priv->enable11d) - rc = &priv->universal_channel[j]; if (!rc->valid || !rc->CFP) continue; if (rc->band != band) @@ -169,12 +222,12 @@ static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info, lbs_deb_enter(LBS_DEB_WEXT); cfp = lbs_find_cfp_by_band_and_channel(priv, 0, - priv->curbssparams.channel); + priv->channel); if (!cfp) { - if (priv->curbssparams.channel) + if (priv->channel) lbs_deb_wext("invalid channel %d\n", - priv->curbssparams.channel); + priv->channel); return -EINVAL; } @@ -547,8 +600,6 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info, struct chan_freq_power *cfp; u8 rates[MAX_RATES + 1]; - u8 flag = 0; - lbs_deb_enter(LBS_DEB_WEXT); dwrq->length = sizeof(struct iw_range); @@ -570,52 +621,21 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info, range->scan_capa = IW_SCAN_CAPA_ESSID; - if (priv->enable11d && - (priv->connect_status == LBS_CONNECTED || - priv->mesh_connect_status == LBS_CONNECTED)) { - u8 chan_no; - u8 band; - - struct parsed_region_chan_11d *parsed_region_chan = - &priv->parsed_region_chan; - - if (parsed_region_chan == NULL) { - lbs_deb_wext("11d: parsed_region_chan is NULL\n"); - goto out; - } - band = parsed_region_chan->band; - lbs_deb_wext("band %d, nr_char %d\n", band, - parsed_region_chan->nr_chan); - + for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (j < ARRAY_SIZE(priv->region_channel)); j++) { + cfp = priv->region_channel[j].CFP; for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) - && (i < parsed_region_chan->nr_chan); i++) { - chan_no = parsed_region_chan->chanpwr[i].chan; - lbs_deb_wext("chan_no %d\n", chan_no); - range->freq[range->num_frequency].i = (long)chan_no; + && priv->region_channel[j].valid + && cfp + && (i < priv->region_channel[j].nrcfp); i++) { + range->freq[range->num_frequency].i = + (long)cfp->channel; range->freq[range->num_frequency].m = - (long)lbs_chan_2_freq(chan_no) * 100000; + (long)cfp->freq * 100000; range->freq[range->num_frequency].e = 1; + cfp++; range->num_frequency++; } - flag = 1; - } - if (!flag) { - for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES) - && (j < ARRAY_SIZE(priv->region_channel)); j++) { - cfp = priv->region_channel[j].CFP; - for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) - && priv->region_channel[j].valid - && cfp - && (i < priv->region_channel[j].nrcfp); i++) { - range->freq[range->num_frequency].i = - (long)cfp->channel; - range->freq[range->num_frequency].m = - (long)cfp->freq * 100000; - range->freq[range->num_frequency].e = 1; - cfp++; - range->num_frequency++; - } - } } lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n", @@ -700,7 +720,6 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info, | IW_ENC_CAPA_CIPHER_CCMP; } -out: lbs_deb_leave(LBS_DEB_WEXT); return 0; } @@ -709,6 +728,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct lbs_private *priv = dev->ml_priv; + int ret = 0; lbs_deb_enter(LBS_DEB_WEXT); @@ -737,8 +757,54 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info, "setting power timeout is not supported\n"); return -EINVAL; } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { - lbs_deb_wext("setting power period not supported\n"); - return -EINVAL; + vwrq->value = vwrq->value / 1000; + if (!priv->enter_deep_sleep) { + lbs_pr_err("deep sleep feature is not implemented " + "for this interface driver\n"); + return -EINVAL; + } + + if (priv->connect_status == LBS_CONNECTED) { + if ((priv->is_auto_deep_sleep_enabled) && + (vwrq->value == -1000)) { + lbs_exit_auto_deep_sleep(priv); + return 0; + } else { + lbs_pr_err("can't use deep sleep cmd in " + "connected state\n"); + return -EINVAL; + } + } + + if ((vwrq->value < 0) && (vwrq->value != -1000)) { + lbs_pr_err("unknown option\n"); + return -EINVAL; + } + + if (vwrq->value > 0) { + if (!priv->is_auto_deep_sleep_enabled) { + priv->is_activity_detected = 0; + priv->auto_deep_sleep_timeout = vwrq->value; + lbs_enter_auto_deep_sleep(priv); + } else { + priv->auto_deep_sleep_timeout = vwrq->value; + lbs_deb_debugfs("auto deep sleep: " + "already enabled\n"); + } + return 0; + } else { + if (priv->is_auto_deep_sleep_enabled) { + lbs_exit_auto_deep_sleep(priv); + /* Try to exit deep sleep if auto */ + /*deep sleep disabled */ + ret = lbs_set_deep_sleep(priv, 0); + } + if (vwrq->value == 0) + ret = lbs_set_deep_sleep(priv, 1); + else if (vwrq->value == -1000) + ret = lbs_set_deep_sleep(priv, 0); + return ret; + } } if (priv->psmode != LBS802_11POWERMODECAM) { @@ -752,6 +818,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info, } lbs_deb_leave(LBS_DEB_WEXT); + return 0; } @@ -785,7 +852,7 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev) u32 rssi_qual; u32 tx_qual; u32 quality = 0; - int stats_valid = 0; + int ret, stats_valid = 0; u8 rssi; u32 tx_retries; struct cmd_ds_802_11_get_log log; @@ -834,7 +901,9 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev) memset(&log, 0, sizeof(log)); log.hdr.size = cpu_to_le16(sizeof(log)); - lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log); + ret = lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log); + if (ret) + goto out; tx_retries = le32_to_cpu(log.retry); @@ -862,8 +931,10 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev) stats_valid = 1; /* update stats asynchronously for future calls */ - lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, + ret = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, 0, 0, NULL); + if (ret) + lbs_pr_err("RSSI command failed\n"); out: if (!stats_valid) { priv->wstats.miss.beacon = 0; @@ -973,7 +1044,7 @@ static int lbs_mesh_set_freq(struct net_device *dev, goto out; } - if (fwrq->m != priv->curbssparams.channel) { + if (fwrq->m != priv->channel) { lbs_deb_wext("mesh channel change forces eth disconnect\n"); if (priv->mode == IW_MODE_INFRA) lbs_cmd_80211_deauthenticate(priv, @@ -1000,6 +1071,7 @@ static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info, u8 rates[MAX_RATES + 1]; lbs_deb_enter(LBS_DEB_WEXT); + lbs_deb_wext("vwrq->value %d\n", vwrq->value); lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed); @@ -1975,7 +2047,7 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info, { struct lbs_private *priv = dev->ml_priv; int ret = 0; - u8 ssid[IW_ESSID_MAX_SIZE]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len = 0; struct assoc_request * assoc_req; int in_ssid_len = dwrq->length; @@ -1989,7 +2061,7 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info, } /* Check the size of the string */ - if (in_ssid_len > IW_ESSID_MAX_SIZE) { + if (in_ssid_len > IEEE80211_MAX_SSID_LEN) { ret = -E2BIG; goto out; } @@ -2020,7 +2092,7 @@ out: ret = -ENOMEM; } else { /* Copy the SSID to the association request */ - memcpy(&assoc_req->ssid, &ssid, IW_ESSID_MAX_SIZE); + memcpy(&assoc_req->ssid, &ssid, IEEE80211_MAX_SSID_LEN); assoc_req->ssid_len = ssid_len; set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); lbs_postpone_association_work(priv); @@ -2071,7 +2143,7 @@ static int lbs_mesh_set_essid(struct net_device *dev, } /* Check the size of the string */ - if (dwrq->length > IW_ESSID_MAX_SIZE) { + if (dwrq->length > IEEE80211_MAX_SSID_LEN) { ret = -E2BIG; goto out; } @@ -2086,7 +2158,7 @@ static int lbs_mesh_set_essid(struct net_device *dev, } lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - priv->curbssparams.channel); + priv->channel); out: lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); return ret; diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h index 4c08db497606..f3f19fe8c6c6 100644 --- a/drivers/net/wireless/libertas/wext.h +++ b/drivers/net/wireless/libertas/wext.h @@ -4,7 +4,14 @@ #ifndef _LBS_WEXT_H_ #define _LBS_WEXT_H_ +void lbs_send_disconnect_notification(struct lbs_private *priv); +void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); + +struct chan_freq_power *lbs_find_cfp_by_band_and_channel( + struct lbs_private *priv, + u8 band, + u16 channel); + extern struct iw_handler_def lbs_handler_def; -extern struct iw_handler_def mesh_handler_def; #endif diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c index 392337b37b1d..3691c307e674 100644 --- a/drivers/net/wireless/libertas_tf/if_usb.c +++ b/drivers/net/wireless/libertas_tf/if_usb.c @@ -23,6 +23,8 @@ static char *lbtf_fw_name = "lbtf_usb.bin"; module_param_named(fw_name, lbtf_fw_name, charp, 0644); +MODULE_FIRMWARE("lbtf_usb.bin"); + static struct usb_device_id if_usb_table[] = { /* Enter the device signature inside */ { USB_DEVICE(0x1286, 0x2001) }, diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 38cfd79e0590..88e41176e7fd 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -284,7 +284,7 @@ struct mac80211_hwsim_data { struct ieee80211_channel *channel; unsigned long beacon_int; /* in jiffies unit */ unsigned int rx_filter; - int started; + bool started, idle; struct timer_list beacon_timer; enum ps_mode { PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL @@ -365,6 +365,49 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, } +static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr) +{ + struct mac80211_hwsim_data *data = hw->priv; + struct sk_buff *skb; + struct hwsim_radiotap_hdr *hdr; + u16 flags; + struct ieee80211_hdr *hdr11; + + if (!netif_running(hwsim_mon)) + return; + + skb = dev_alloc_skb(100); + if (skb == NULL) + return; + + hdr = (struct hwsim_radiotap_hdr *) skb_put(skb, sizeof(*hdr)); + hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION; + hdr->hdr.it_pad = 0; + hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); + hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL)); + hdr->rt_flags = 0; + hdr->rt_rate = 0; + hdr->rt_channel = cpu_to_le16(data->channel->center_freq); + flags = IEEE80211_CHAN_2GHZ; + hdr->rt_chbitmask = cpu_to_le16(flags); + + hdr11 = (struct ieee80211_hdr *) skb_put(skb, 10); + hdr11->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_ACK); + hdr11->duration_id = cpu_to_le16(0); + memcpy(hdr11->addr1, addr, ETH_ALEN); + + skb->dev = hwsim_mon; + skb_set_mac_header(skb, 0); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + + static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, struct sk_buff *skb) { @@ -402,6 +445,12 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_rx_status rx_status; + if (data->idle) { + printk(KERN_DEBUG "%s: Trying to TX when idle - reject\n", + wiphy_name(hw->wiphy)); + return false; + } + memset(&rx_status, 0, sizeof(rx_status)); /* TODO: set mactime */ rx_status.freq = data->channel->center_freq; @@ -428,7 +477,8 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, if (data == data2) continue; - if (!data2->started || !hwsim_ps_rx_ok(data2, skb) || + if (data2->idle || !data2->started || + !hwsim_ps_rx_ok(data2, skb) || !data->channel || !data2->channel || data->channel->center_freq != data2->channel->center_freq || !(data->group & data2->group)) @@ -464,6 +514,10 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) } ack = mac80211_hwsim_tx_frame(hw, skb); + if (ack && skb->len >= 16) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + mac80211_hwsim_monitor_ack(hw, hdr->addr2); + } txi = IEEE80211_SKB_CB(skb); @@ -571,6 +625,8 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) !!(conf->flags & IEEE80211_CONF_IDLE), !!(conf->flags & IEEE80211_CONF_PS)); + data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); + data->channel = conf->channel; if (!data->started || !data->beacon_int) del_timer(&data->beacon_timer); @@ -1045,19 +1101,20 @@ static int __init init_mac80211_hwsim(void) sband->channels = data->channels_2ghz; sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); + sband->bitrates = data->rates; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates); break; case IEEE80211_BAND_5GHZ: sband->channels = data->channels_5ghz; sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); + sband->bitrates = data->rates + 4; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; break; default: break; } - sband->bitrates = data->rates; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates); - sband->ht_cap.ht_supported = true; sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_GRN_FLD | @@ -1089,46 +1146,46 @@ static int __init init_mac80211_hwsim(void) break; case HWSIM_REGTEST_WORLD_ROAM: if (i == 0) { - hw->wiphy->custom_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } break; case HWSIM_REGTEST_CUSTOM_WORLD: - hw->wiphy->custom_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); break; case HWSIM_REGTEST_CUSTOM_WORLD_2: if (i == 0) { - hw->wiphy->custom_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } else if (i == 1) { - hw->wiphy->custom_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_02); } break; case HWSIM_REGTEST_STRICT_ALL: - hw->wiphy->strict_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; break; case HWSIM_REGTEST_STRICT_FOLLOW: case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: if (i == 0) - hw->wiphy->strict_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; break; case HWSIM_REGTEST_ALL: if (i == 0) { - hw->wiphy->custom_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } else if (i == 1) { - hw->wiphy->custom_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_02); } else if (i == 4) - hw->wiphy->strict_regulatory = true; + hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; break; default: break; diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 746532ebe5a8..0cb5ecc822a8 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/list.h> #include <linux/pci.h> @@ -27,18 +28,6 @@ #define MWL8K_NAME KBUILD_MODNAME #define MWL8K_VERSION "0.10" -MODULE_DESCRIPTION(MWL8K_DESC); -MODULE_VERSION(MWL8K_VERSION); -MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>"); -MODULE_LICENSE("GPL"); - -static DEFINE_PCI_DEVICE_TABLE(mwl8k_table) = { - { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = 8687, }, - { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = 8687, }, - { } -}; -MODULE_DEVICE_TABLE(pci, mwl8k_table); - /* Register definitions */ #define MWL8K_HIU_GEN_PTR 0x00000c10 #define MWL8K_MODE_STA 0x0000005a @@ -88,72 +77,89 @@ MODULE_DEVICE_TABLE(pci, mwl8k_table); MWL8K_A2H_INT_RX_READY | \ MWL8K_A2H_INT_TX_DONE) -/* WME stream classes */ -#define WME_AC_BE 0 /* best effort */ -#define WME_AC_BK 1 /* background */ -#define WME_AC_VI 2 /* video */ -#define WME_AC_VO 3 /* voice */ - #define MWL8K_RX_QUEUES 1 #define MWL8K_TX_QUEUES 4 +struct rxd_ops { + int rxd_size; + void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); + void (*rxd_refill)(void *rxd, dma_addr_t addr, int len); + int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status); +}; + +struct mwl8k_device_info { + char *part_name; + char *helper_image; + char *fw_image; + struct rxd_ops *rxd_ops; + u16 modes; +}; + struct mwl8k_rx_queue { - int rx_desc_count; + int rxd_count; /* hw receives here */ - int rx_head; + int head; /* refill descs here */ - int rx_tail; + int tail; - struct mwl8k_rx_desc *rx_desc_area; - dma_addr_t rx_desc_dma; - struct sk_buff **rx_skb; + void *rxd; + dma_addr_t rxd_dma; + struct { + struct sk_buff *skb; + DECLARE_PCI_UNMAP_ADDR(dma) + } *buf; }; struct mwl8k_tx_queue { /* hw transmits here */ - int tx_head; + int head; /* sw appends here */ - int tx_tail; + int tail; - struct ieee80211_tx_queue_stats tx_stats; - struct mwl8k_tx_desc *tx_desc_area; - dma_addr_t tx_desc_dma; - struct sk_buff **tx_skb; + struct ieee80211_tx_queue_stats stats; + struct mwl8k_tx_desc *txd; + dma_addr_t txd_dma; + struct sk_buff **skb; }; /* Pointers to the firmware data and meta information about it. */ struct mwl8k_firmware { - /* Microcode */ - struct firmware *ucode; - /* Boot helper code */ struct firmware *helper; + + /* Microcode */ + struct firmware *ucode; }; struct mwl8k_priv { + void __iomem *sram; void __iomem *regs; struct ieee80211_hw *hw; struct pci_dev *pdev; - u8 name[16]; + + struct mwl8k_device_info *device_info; + bool ap_fw; + struct rxd_ops *rxd_ops; /* firmware files and meta data */ struct mwl8k_firmware fw; - u32 part_num; /* firmware access */ struct mutex fw_mutex; struct task_struct *fw_mutex_owner; int fw_mutex_depth; - struct completion *tx_wait; struct completion *hostcmd_wait; /* lock held over TX and TX reap */ spinlock_t tx_lock; + /* TX quiesce completion, protected by fw_mutex and tx_lock */ + struct completion *tx_wait; + struct ieee80211_vif *vif; struct ieee80211_channel *current_channel; @@ -178,10 +184,11 @@ struct mwl8k_priv { /* PHY parameters */ struct ieee80211_supported_band band; struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[12]; + struct ieee80211_rate rates[13]; bool radio_on; bool radio_short_preamble; + bool sniffer_enabled; bool wmm_enabled; /* XXX need to convert this to handle multiple interfaces */ @@ -199,9 +206,6 @@ struct mwl8k_priv { /* Tasklet to reclaim TX descriptors and buffers after tx */ struct tasklet_struct tx_reclaim_task; - - /* Work thread to serialize configuration requests */ - struct workqueue_struct *config_wq; }; /* Per interface specific private data */ @@ -220,7 +224,7 @@ struct mwl8k_vif { * Subset of supported legacy rates. * Intersection of AP and STA supported rates. */ - struct ieee80211_rate legacy_rates[12]; + struct ieee80211_rate legacy_rates[13]; /* number of supported legacy rates */ u8 legacy_nrates; @@ -252,9 +256,10 @@ static const struct ieee80211_rate mwl8k_rates[] = { { .bitrate = 10, .hw_value = 2, }, { .bitrate = 20, .hw_value = 4, }, { .bitrate = 55, .hw_value = 11, }, + { .bitrate = 110, .hw_value = 22, }, + { .bitrate = 220, .hw_value = 44, }, { .bitrate = 60, .hw_value = 12, }, { .bitrate = 90, .hw_value = 18, }, - { .bitrate = 110, .hw_value = 22, }, { .bitrate = 120, .hw_value = 24, }, { .bitrate = 180, .hw_value = 36, }, { .bitrate = 240, .hw_value = 48, }, @@ -270,10 +275,12 @@ static const struct ieee80211_rate mwl8k_rates[] = { /* Firmware command codes */ #define MWL8K_CMD_CODE_DNLD 0x0001 #define MWL8K_CMD_GET_HW_SPEC 0x0003 +#define MWL8K_CMD_SET_HW_SPEC 0x0004 #define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 #define MWL8K_CMD_GET_STAT 0x0014 #define MWL8K_CMD_RADIO_CONTROL 0x001c #define MWL8K_CMD_RF_TX_POWER 0x001e +#define MWL8K_CMD_RF_ANTENNA 0x0020 #define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_POST_SCAN 0x0108 #define MWL8K_CMD_SET_RF_CHANNEL 0x010a @@ -287,6 +294,7 @@ static const struct ieee80211_rate mwl8k_rates[] = { #define MWL8K_CMD_MIMO_CONFIG 0x0125 #define MWL8K_CMD_USE_FIXED_RATE 0x0126 #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 +#define MWL8K_CMD_SET_MAC_ADDR 0x0202 #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 #define MWL8K_CMD_UPDATE_STADB 0x1123 @@ -299,10 +307,12 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) switch (cmd & ~0x8000) { MWL8K_CMDNAME(CODE_DNLD); MWL8K_CMDNAME(GET_HW_SPEC); + MWL8K_CMDNAME(SET_HW_SPEC); MWL8K_CMDNAME(MAC_MULTICAST_ADR); MWL8K_CMDNAME(GET_STAT); MWL8K_CMDNAME(RADIO_CONTROL); MWL8K_CMDNAME(RF_TX_POWER); + MWL8K_CMDNAME(RF_ANTENNA); MWL8K_CMDNAME(SET_PRE_SCAN); MWL8K_CMDNAME(SET_POST_SCAN); MWL8K_CMDNAME(SET_RF_CHANNEL); @@ -316,6 +326,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(MIMO_CONFIG); MWL8K_CMDNAME(USE_FIXED_RATE); MWL8K_CMDNAME(ENABLE_SNIFFER); + MWL8K_CMDNAME(SET_MAC_ADDR); MWL8K_CMDNAME(SET_RATEADAPT_MODE); MWL8K_CMDNAME(UPDATE_STADB); default: @@ -353,41 +364,35 @@ static void mwl8k_release_firmware(struct mwl8k_priv *priv) /* Request fw image */ static int mwl8k_request_fw(struct mwl8k_priv *priv, - const char *fname, struct firmware **fw) + const char *fname, struct firmware **fw) { /* release current image */ if (*fw != NULL) mwl8k_release_fw(fw); return request_firmware((const struct firmware **)fw, - fname, &priv->pdev->dev); + fname, &priv->pdev->dev); } -static int mwl8k_request_firmware(struct mwl8k_priv *priv, u32 part_num) +static int mwl8k_request_firmware(struct mwl8k_priv *priv) { - u8 filename[64]; + struct mwl8k_device_info *di = priv->device_info; int rc; - priv->part_num = part_num; - - snprintf(filename, sizeof(filename), - "mwl8k/helper_%u.fw", priv->part_num); - - rc = mwl8k_request_fw(priv, filename, &priv->fw.helper); - if (rc) { - printk(KERN_ERR - "%s Error requesting helper firmware file %s\n", - pci_name(priv->pdev), filename); - return rc; + if (di->helper_image != NULL) { + rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw.helper); + if (rc) { + printk(KERN_ERR "%s: Error requesting helper " + "firmware file %s\n", pci_name(priv->pdev), + di->helper_image); + return rc; + } } - snprintf(filename, sizeof(filename), - "mwl8k/fmimage_%u.fw", priv->part_num); - - rc = mwl8k_request_fw(priv, filename, &priv->fw.ucode); + rc = mwl8k_request_fw(priv, di->fw_image, &priv->fw.ucode); if (rc) { - printk(KERN_ERR "%s Error requesting firmware file %s\n", - pci_name(priv->pdev), filename); + printk(KERN_ERR "%s: Error requesting firmware file %s\n", + pci_name(priv->pdev), di->fw_image); mwl8k_release_fw(&priv->fw.helper); return rc; } @@ -395,6 +400,9 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv, u32 part_num) return 0; } +MODULE_FIRMWARE("mwl8k/helper_8687.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); + struct mwl8k_cmd_pkt { __le16 code; __le16 length; @@ -434,6 +442,7 @@ mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) break; } + cond_resched(); udelay(1); } while (--loops); @@ -542,43 +551,62 @@ static int mwl8k_feed_fw_image(struct mwl8k_priv *priv, return rc; } -static int mwl8k_load_firmware(struct mwl8k_priv *priv) +static int mwl8k_load_firmware(struct ieee80211_hw *hw) { - int loops, rc; + struct mwl8k_priv *priv = hw->priv; + struct firmware *fw = priv->fw.ucode; + struct mwl8k_device_info *di = priv->device_info; + int rc; + int loops; - const u8 *ucode = priv->fw.ucode->data; - size_t ucode_len = priv->fw.ucode->size; - const u8 *helper = priv->fw.helper->data; - size_t helper_len = priv->fw.helper->size; + if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) { + struct firmware *helper = priv->fw.helper; - if (!memcmp(ucode, "\x01\x00\x00\x00", 4)) { - rc = mwl8k_load_fw_image(priv, helper, helper_len); + if (helper == NULL) { + printk(KERN_ERR "%s: helper image needed but none " + "given\n", pci_name(priv->pdev)); + return -EINVAL; + } + + rc = mwl8k_load_fw_image(priv, helper->data, helper->size); if (rc) { printk(KERN_ERR "%s: unable to load firmware " - "helper image\n", pci_name(priv->pdev)); + "helper image\n", pci_name(priv->pdev)); return rc; } msleep(1); - rc = mwl8k_feed_fw_image(priv, ucode, ucode_len); + rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); } else { - rc = mwl8k_load_fw_image(priv, ucode, ucode_len); + rc = mwl8k_load_fw_image(priv, fw->data, fw->size); } if (rc) { - printk(KERN_ERR "%s: unable to load firmware data\n", - pci_name(priv->pdev)); + printk(KERN_ERR "%s: unable to load firmware image\n", + pci_name(priv->pdev)); return rc; } - iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); + if (di->modes & BIT(NL80211_IFTYPE_AP)) + iowrite32(MWL8K_MODE_AP, priv->regs + MWL8K_HIU_GEN_PTR); + else + iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); msleep(1); loops = 200000; do { - if (ioread32(priv->regs + MWL8K_HIU_INT_CODE) - == MWL8K_FWSTA_READY) + u32 ready_code; + + ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE); + if (ready_code == MWL8K_FWAP_READY) { + priv->ap_fw = 1; + break; + } else if (ready_code == MWL8K_FWSTA_READY) { + priv->ap_fw = 0; break; + } + + cond_resched(); udelay(1); } while (--loops); @@ -605,7 +633,7 @@ struct ewc_ht_info { /* Peer Entry flags - used to define the type of the peer node */ #define MWL8K_PEER_TYPE_ACCESSPOINT 2 -#define MWL8K_IEEE_LEGACY_DATA_RATES 12 +#define MWL8K_IEEE_LEGACY_DATA_RATES 13 #define MWL8K_MCS_BITMAP_SIZE 16 struct peer_capability_info { @@ -731,16 +759,96 @@ static inline void mwl8k_add_dma_header(struct sk_buff *skb) /* - * Packet reception. + * Packet reception for 88w8366. */ -#define MWL8K_RX_CTRL_OWNED_BY_HOST 0x02 +struct mwl8k_rxd_8366 { + __le16 pkt_len; + __u8 sq2; + __u8 rate; + __le32 pkt_phys_addr; + __le32 next_rxd_phys_addr; + __le16 qos_control; + __le16 htsig2; + __le32 hw_rssi_info; + __le32 hw_noise_floor_info; + __u8 noise_floor; + __u8 pad0[3]; + __u8 rssi; + __u8 rx_status; + __u8 channel; + __u8 rx_ctrl; +} __attribute__((packed)); + +#define MWL8K_8366_RX_CTRL_OWNED_BY_HOST 0x80 + +static void mwl8k_rxd_8366_init(void *_rxd, dma_addr_t next_dma_addr) +{ + struct mwl8k_rxd_8366 *rxd = _rxd; + + rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); + rxd->rx_ctrl = MWL8K_8366_RX_CTRL_OWNED_BY_HOST; +} + +static void mwl8k_rxd_8366_refill(void *_rxd, dma_addr_t addr, int len) +{ + struct mwl8k_rxd_8366 *rxd = _rxd; + + rxd->pkt_len = cpu_to_le16(len); + rxd->pkt_phys_addr = cpu_to_le32(addr); + wmb(); + rxd->rx_ctrl = 0; +} + +static int +mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status) +{ + struct mwl8k_rxd_8366 *rxd = _rxd; + + if (!(rxd->rx_ctrl & MWL8K_8366_RX_CTRL_OWNED_BY_HOST)) + return -1; + rmb(); + + memset(status, 0, sizeof(*status)); + + status->signal = -rxd->rssi; + status->noise = -rxd->noise_floor; -struct mwl8k_rx_desc { + if (rxd->rate & 0x80) { + status->flag |= RX_FLAG_HT; + status->rate_idx = rxd->rate & 0x7f; + } else { + int i; + + for (i = 0; i < ARRAY_SIZE(mwl8k_rates); i++) { + if (mwl8k_rates[i].hw_value == rxd->rate) { + status->rate_idx = i; + break; + } + } + } + + status->band = IEEE80211_BAND_2GHZ; + status->freq = ieee80211_channel_to_frequency(rxd->channel); + + return le16_to_cpu(rxd->pkt_len); +} + +static struct rxd_ops rxd_8366_ops = { + .rxd_size = sizeof(struct mwl8k_rxd_8366), + .rxd_init = mwl8k_rxd_8366_init, + .rxd_refill = mwl8k_rxd_8366_refill, + .rxd_process = mwl8k_rxd_8366_process, +}; + +/* + * Packet reception for 88w8687. + */ +struct mwl8k_rxd_8687 { __le16 pkt_len; __u8 link_quality; __u8 noise_level; __le32 pkt_phys_addr; - __le32 next_rx_desc_phys_addr; + __le32 next_rxd_phys_addr; __le16 qos_control; __le16 rate_info; __le32 pad0[4]; @@ -752,6 +860,76 @@ struct mwl8k_rx_desc { __u8 pad2[2]; } __attribute__((packed)); +#define MWL8K_8687_RATE_INFO_SHORTPRE 0x8000 +#define MWL8K_8687_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) +#define MWL8K_8687_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) +#define MWL8K_8687_RATE_INFO_40MHZ 0x0004 +#define MWL8K_8687_RATE_INFO_SHORTGI 0x0002 +#define MWL8K_8687_RATE_INFO_MCS_FORMAT 0x0001 + +#define MWL8K_8687_RX_CTRL_OWNED_BY_HOST 0x02 + +static void mwl8k_rxd_8687_init(void *_rxd, dma_addr_t next_dma_addr) +{ + struct mwl8k_rxd_8687 *rxd = _rxd; + + rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); + rxd->rx_ctrl = MWL8K_8687_RX_CTRL_OWNED_BY_HOST; +} + +static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len) +{ + struct mwl8k_rxd_8687 *rxd = _rxd; + + rxd->pkt_len = cpu_to_le16(len); + rxd->pkt_phys_addr = cpu_to_le32(addr); + wmb(); + rxd->rx_ctrl = 0; +} + +static int +mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status) +{ + struct mwl8k_rxd_8687 *rxd = _rxd; + u16 rate_info; + + if (!(rxd->rx_ctrl & MWL8K_8687_RX_CTRL_OWNED_BY_HOST)) + return -1; + rmb(); + + rate_info = le16_to_cpu(rxd->rate_info); + + memset(status, 0, sizeof(*status)); + + status->signal = -rxd->rssi; + status->noise = -rxd->noise_level; + status->qual = rxd->link_quality; + status->antenna = MWL8K_8687_RATE_INFO_ANTSELECT(rate_info); + status->rate_idx = MWL8K_8687_RATE_INFO_RATEID(rate_info); + + if (rate_info & MWL8K_8687_RATE_INFO_SHORTPRE) + status->flag |= RX_FLAG_SHORTPRE; + if (rate_info & MWL8K_8687_RATE_INFO_40MHZ) + status->flag |= RX_FLAG_40MHZ; + if (rate_info & MWL8K_8687_RATE_INFO_SHORTGI) + status->flag |= RX_FLAG_SHORT_GI; + if (rate_info & MWL8K_8687_RATE_INFO_MCS_FORMAT) + status->flag |= RX_FLAG_HT; + + status->band = IEEE80211_BAND_2GHZ; + status->freq = ieee80211_channel_to_frequency(rxd->channel); + + return le16_to_cpu(rxd->pkt_len); +} + +static struct rxd_ops rxd_8687_ops = { + .rxd_size = sizeof(struct mwl8k_rxd_8687), + .rxd_init = mwl8k_rxd_8687_init, + .rxd_refill = mwl8k_rxd_8687_refill, + .rxd_process = mwl8k_rxd_8687_process, +}; + + #define MWL8K_RX_DESCS 256 #define MWL8K_RX_MAXSZ 3800 @@ -762,43 +940,44 @@ static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index) int size; int i; - rxq->rx_desc_count = 0; - rxq->rx_head = 0; - rxq->rx_tail = 0; + rxq->rxd_count = 0; + rxq->head = 0; + rxq->tail = 0; - size = MWL8K_RX_DESCS * sizeof(struct mwl8k_rx_desc); + size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size; - rxq->rx_desc_area = - pci_alloc_consistent(priv->pdev, size, &rxq->rx_desc_dma); - if (rxq->rx_desc_area == NULL) { + rxq->rxd = pci_alloc_consistent(priv->pdev, size, &rxq->rxd_dma); + if (rxq->rxd == NULL) { printk(KERN_ERR "%s: failed to alloc RX descriptors\n", - priv->name); + wiphy_name(hw->wiphy)); return -ENOMEM; } - memset(rxq->rx_desc_area, 0, size); + memset(rxq->rxd, 0, size); - rxq->rx_skb = kmalloc(MWL8K_RX_DESCS * - sizeof(*rxq->rx_skb), GFP_KERNEL); - if (rxq->rx_skb == NULL) { + rxq->buf = kmalloc(MWL8K_RX_DESCS * sizeof(*rxq->buf), GFP_KERNEL); + if (rxq->buf == NULL) { printk(KERN_ERR "%s: failed to alloc RX skbuff list\n", - priv->name); - pci_free_consistent(priv->pdev, size, - rxq->rx_desc_area, rxq->rx_desc_dma); + wiphy_name(hw->wiphy)); + pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma); return -ENOMEM; } - memset(rxq->rx_skb, 0, MWL8K_RX_DESCS * sizeof(*rxq->rx_skb)); + memset(rxq->buf, 0, MWL8K_RX_DESCS * sizeof(*rxq->buf)); for (i = 0; i < MWL8K_RX_DESCS; i++) { - struct mwl8k_rx_desc *rx_desc; + int desc_size; + void *rxd; int nexti; + dma_addr_t next_dma_addr; - rx_desc = rxq->rx_desc_area + i; - nexti = (i + 1) % MWL8K_RX_DESCS; + desc_size = priv->rxd_ops->rxd_size; + rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size); - rx_desc->next_rx_desc_phys_addr = - cpu_to_le32(rxq->rx_desc_dma - + nexti * sizeof(*rx_desc)); - rx_desc->rx_ctrl = MWL8K_RX_CTRL_OWNED_BY_HOST; + nexti = i + 1; + if (nexti == MWL8K_RX_DESCS) + nexti = 0; + next_dma_addr = rxq->rxd_dma + (nexti * desc_size); + + priv->rxd_ops->rxd_init(rxd, next_dma_addr); } return 0; @@ -811,27 +990,28 @@ static int rxq_refill(struct ieee80211_hw *hw, int index, int limit) int refilled; refilled = 0; - while (rxq->rx_desc_count < MWL8K_RX_DESCS && limit--) { + while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) { struct sk_buff *skb; + dma_addr_t addr; int rx; + void *rxd; skb = dev_alloc_skb(MWL8K_RX_MAXSZ); if (skb == NULL) break; - rxq->rx_desc_count++; - - rx = rxq->rx_tail; - rxq->rx_tail = (rx + 1) % MWL8K_RX_DESCS; + addr = pci_map_single(priv->pdev, skb->data, + MWL8K_RX_MAXSZ, DMA_FROM_DEVICE); - rxq->rx_desc_area[rx].pkt_phys_addr = - cpu_to_le32(pci_map_single(priv->pdev, skb->data, - MWL8K_RX_MAXSZ, DMA_FROM_DEVICE)); + rxq->rxd_count++; + rx = rxq->tail++; + if (rxq->tail == MWL8K_RX_DESCS) + rxq->tail = 0; + rxq->buf[rx].skb = skb; + pci_unmap_addr_set(&rxq->buf[rx], dma, addr); - rxq->rx_desc_area[rx].pkt_len = cpu_to_le16(MWL8K_RX_MAXSZ); - rxq->rx_skb[rx] = skb; - wmb(); - rxq->rx_desc_area[rx].rx_ctrl = 0; + rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size); + priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ); refilled++; } @@ -847,24 +1027,24 @@ static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index) int i; for (i = 0; i < MWL8K_RX_DESCS; i++) { - if (rxq->rx_skb[i] != NULL) { - unsigned long addr; - - addr = le32_to_cpu(rxq->rx_desc_area[i].pkt_phys_addr); - pci_unmap_single(priv->pdev, addr, MWL8K_RX_MAXSZ, - PCI_DMA_FROMDEVICE); - kfree_skb(rxq->rx_skb[i]); - rxq->rx_skb[i] = NULL; + if (rxq->buf[i].skb != NULL) { + pci_unmap_single(priv->pdev, + pci_unmap_addr(&rxq->buf[i], dma), + MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); + pci_unmap_addr_set(&rxq->buf[i], dma, 0); + + kfree_skb(rxq->buf[i].skb); + rxq->buf[i].skb = NULL; } } - kfree(rxq->rx_skb); - rxq->rx_skb = NULL; + kfree(rxq->buf); + rxq->buf = NULL; pci_free_consistent(priv->pdev, - MWL8K_RX_DESCS * sizeof(struct mwl8k_rx_desc), - rxq->rx_desc_area, rxq->rx_desc_dma); - rxq->rx_desc_area = NULL; + MWL8K_RX_DESCS * priv->rxd_ops->rxd_size, + rxq->rxd, rxq->rxd_dma); + rxq->rxd = NULL; } @@ -880,9 +1060,11 @@ mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh) !compare_ether_addr(wh->addr3, priv->capture_bssid); } -static inline void mwl8k_save_beacon(struct mwl8k_priv *priv, - struct sk_buff *skb) +static inline void mwl8k_save_beacon(struct ieee80211_hw *hw, + struct sk_buff *skb) { + struct mwl8k_priv *priv = hw->priv; + priv->capture_beacon = false; memset(priv->capture_bssid, 0, ETH_ALEN); @@ -893,8 +1075,7 @@ static inline void mwl8k_save_beacon(struct mwl8k_priv *priv, */ priv->beacon_skb = skb_copy(skb, GFP_ATOMIC); if (priv->beacon_skb != NULL) - queue_work(priv->config_wq, - &priv->finalize_join_worker); + ieee80211_queue_work(hw, &priv->finalize_join_worker); } static int rxq_process(struct ieee80211_hw *hw, int index, int limit) @@ -904,53 +1085,46 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) int processed; processed = 0; - while (rxq->rx_desc_count && limit--) { - struct mwl8k_rx_desc *rx_desc; + while (rxq->rxd_count && limit--) { struct sk_buff *skb; + void *rxd; + int pkt_len; struct ieee80211_rx_status status; - unsigned long addr; - struct ieee80211_hdr *wh; - rx_desc = rxq->rx_desc_area + rxq->rx_head; - if (!(rx_desc->rx_ctrl & MWL8K_RX_CTRL_OWNED_BY_HOST)) + skb = rxq->buf[rxq->head].skb; + if (skb == NULL) break; - rmb(); - skb = rxq->rx_skb[rxq->rx_head]; - if (skb == NULL) + rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size); + + pkt_len = priv->rxd_ops->rxd_process(rxd, &status); + if (pkt_len < 0) break; - rxq->rx_skb[rxq->rx_head] = NULL; - rxq->rx_head = (rxq->rx_head + 1) % MWL8K_RX_DESCS; - rxq->rx_desc_count--; + rxq->buf[rxq->head].skb = NULL; - addr = le32_to_cpu(rx_desc->pkt_phys_addr); - pci_unmap_single(priv->pdev, addr, - MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); + pci_unmap_single(priv->pdev, + pci_unmap_addr(&rxq->buf[rxq->head], dma), + MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); + pci_unmap_addr_set(&rxq->buf[rxq->head], dma, 0); - skb_put(skb, le16_to_cpu(rx_desc->pkt_len)); - mwl8k_remove_dma_header(skb); + rxq->head++; + if (rxq->head == MWL8K_RX_DESCS) + rxq->head = 0; + + rxq->rxd_count--; - wh = (struct ieee80211_hdr *)skb->data; + skb_put(skb, pkt_len); + mwl8k_remove_dma_header(skb); /* - * Check for pending join operation. save a copy of - * the beacon and schedule a tasklet to send finalize - * join command to the firmware. + * Check for a pending join operation. Save a + * copy of the beacon and schedule a tasklet to + * send a FINALIZE_JOIN command to the firmware. */ - if (mwl8k_capture_bssid(priv, wh)) - mwl8k_save_beacon(priv, skb); - - memset(&status, 0, sizeof(status)); - status.mactime = 0; - status.signal = -rx_desc->rssi; - status.noise = -rx_desc->noise_level; - status.qual = rx_desc->link_quality; - status.antenna = 1; - status.rate_idx = 1; - status.flag = 0; - status.band = IEEE80211_BAND_2GHZ; - status.freq = ieee80211_channel_to_frequency(rx_desc->channel); + if (mwl8k_capture_bssid(priv, (void *)skb->data)) + mwl8k_save_beacon(hw, skb); + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); ieee80211_rx_irqsafe(hw, skb); @@ -965,24 +1139,10 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) * Packet transmission. */ -/* Transmit queue assignment. */ -enum { - MWL8K_WME_AC_BK = 0, /* background access */ - MWL8K_WME_AC_BE = 1, /* best effort access */ - MWL8K_WME_AC_VI = 2, /* video access */ - MWL8K_WME_AC_VO = 3, /* voice access */ -}; - /* Transmit packet ACK policy */ #define MWL8K_TXD_ACK_POLICY_NORMAL 0 #define MWL8K_TXD_ACK_POLICY_BLOCKACK 3 -#define GET_TXQ(_ac) (\ - ((_ac) == WME_AC_VO) ? MWL8K_WME_AC_VO : \ - ((_ac) == WME_AC_VI) ? MWL8K_WME_AC_VI : \ - ((_ac) == WME_AC_BK) ? MWL8K_WME_AC_BK : \ - MWL8K_WME_AC_BE) - #define MWL8K_TXD_STATUS_OK 0x00000001 #define MWL8K_TXD_STATUS_OK_RETRY 0x00000002 #define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004 @@ -997,7 +1157,7 @@ struct mwl8k_tx_desc { __le32 pkt_phys_addr; __le16 pkt_len; __u8 dest_MAC_addr[ETH_ALEN]; - __le32 next_tx_desc_phys_addr; + __le32 next_txd_phys_addr; __le32 reserved; __le16 rate_info; __u8 peer_id; @@ -1013,44 +1173,40 @@ static int mwl8k_txq_init(struct ieee80211_hw *hw, int index) int size; int i; - memset(&txq->tx_stats, 0, sizeof(struct ieee80211_tx_queue_stats)); - txq->tx_stats.limit = MWL8K_TX_DESCS; - txq->tx_head = 0; - txq->tx_tail = 0; + memset(&txq->stats, 0, sizeof(struct ieee80211_tx_queue_stats)); + txq->stats.limit = MWL8K_TX_DESCS; + txq->head = 0; + txq->tail = 0; size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc); - txq->tx_desc_area = - pci_alloc_consistent(priv->pdev, size, &txq->tx_desc_dma); - if (txq->tx_desc_area == NULL) { + txq->txd = pci_alloc_consistent(priv->pdev, size, &txq->txd_dma); + if (txq->txd == NULL) { printk(KERN_ERR "%s: failed to alloc TX descriptors\n", - priv->name); + wiphy_name(hw->wiphy)); return -ENOMEM; } - memset(txq->tx_desc_area, 0, size); + memset(txq->txd, 0, size); - txq->tx_skb = kmalloc(MWL8K_TX_DESCS * sizeof(*txq->tx_skb), - GFP_KERNEL); - if (txq->tx_skb == NULL) { + txq->skb = kmalloc(MWL8K_TX_DESCS * sizeof(*txq->skb), GFP_KERNEL); + if (txq->skb == NULL) { printk(KERN_ERR "%s: failed to alloc TX skbuff list\n", - priv->name); - pci_free_consistent(priv->pdev, size, - txq->tx_desc_area, txq->tx_desc_dma); + wiphy_name(hw->wiphy)); + pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma); return -ENOMEM; } - memset(txq->tx_skb, 0, MWL8K_TX_DESCS * sizeof(*txq->tx_skb)); + memset(txq->skb, 0, MWL8K_TX_DESCS * sizeof(*txq->skb)); for (i = 0; i < MWL8K_TX_DESCS; i++) { struct mwl8k_tx_desc *tx_desc; int nexti; - tx_desc = txq->tx_desc_area + i; + tx_desc = txq->txd + i; nexti = (i + 1) % MWL8K_TX_DESCS; tx_desc->status = 0; - tx_desc->next_tx_desc_phys_addr = - cpu_to_le32(txq->tx_desc_dma + - nexti * sizeof(*tx_desc)); + tx_desc->next_txd_phys_addr = + cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc)); } return 0; @@ -1065,11 +1221,6 @@ static inline void mwl8k_tx_start(struct mwl8k_priv *priv) ioread32(priv->regs + MWL8K_HIU_INT_CODE); } -static inline int mwl8k_txq_busy(struct mwl8k_priv *priv) -{ - return priv->pending_tx_pkts; -} - struct mwl8k_txq_info { u32 fw_owned; u32 drv_owned; @@ -1089,14 +1240,13 @@ static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv, memset(txinfo, 0, MWL8K_TX_QUEUES * sizeof(struct mwl8k_txq_info)); - spin_lock_bh(&priv->tx_lock); for (count = 0; count < MWL8K_TX_QUEUES; count++) { txq = priv->txq + count; - txinfo[count].len = txq->tx_stats.len; - txinfo[count].head = txq->tx_head; - txinfo[count].tail = txq->tx_tail; + txinfo[count].len = txq->stats.len; + txinfo[count].head = txq->head; + txinfo[count].tail = txq->tail; for (desc = 0; desc < MWL8K_TX_DESCS; desc++) { - tx_desc = txq->tx_desc_area + desc; + tx_desc = txq->txd + desc; status = le32_to_cpu(tx_desc->status); if (status & MWL8K_TXD_STATUS_FW_OWNED) @@ -1108,30 +1258,26 @@ static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv, txinfo[count].unused++; } } - spin_unlock_bh(&priv->tx_lock); return ndescs; } /* - * Must be called with hw->fw_mutex held and tx queues stopped. + * Must be called with priv->fw_mutex held and tx queues stopped. */ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; - DECLARE_COMPLETION_ONSTACK(cmd_wait); + DECLARE_COMPLETION_ONSTACK(tx_wait); u32 count; unsigned long timeout; might_sleep(); spin_lock_bh(&priv->tx_lock); - count = mwl8k_txq_busy(priv); - if (count) { - priv->tx_wait = &cmd_wait; - if (priv->radio_on) - mwl8k_tx_start(priv); - } + count = priv->pending_tx_pkts; + if (count) + priv->tx_wait = &tx_wait; spin_unlock_bh(&priv->tx_lock); if (count) { @@ -1139,23 +1285,23 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) int index; int newcount; - timeout = wait_for_completion_timeout(&cmd_wait, + timeout = wait_for_completion_timeout(&tx_wait, msecs_to_jiffies(5000)); if (timeout) return 0; spin_lock_bh(&priv->tx_lock); priv->tx_wait = NULL; - newcount = mwl8k_txq_busy(priv); + newcount = priv->pending_tx_pkts; + mwl8k_scan_tx_ring(priv, txinfo); spin_unlock_bh(&priv->tx_lock); printk(KERN_ERR "%s(%u) TIMEDOUT:5000ms Pend:%u-->%u\n", __func__, __LINE__, count, newcount); - mwl8k_scan_tx_ring(priv, txinfo); for (index = 0; index < MWL8K_TX_QUEUES; index++) - printk(KERN_ERR - "TXQ:%u L:%u H:%u T:%u FW:%u DRV:%u U:%u\n", + printk(KERN_ERR "TXQ:%u L:%u H:%u T:%u FW:%u " + "DRV:%u U:%u\n", index, txinfo[index].len, txinfo[index].head, @@ -1181,7 +1327,7 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) struct mwl8k_tx_queue *txq = priv->txq + index; int wake = 0; - while (txq->tx_stats.len > 0) { + while (txq->stats.len > 0) { int tx; struct mwl8k_tx_desc *tx_desc; unsigned long addr; @@ -1190,8 +1336,8 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) struct ieee80211_tx_info *info; u32 status; - tx = txq->tx_head; - tx_desc = txq->tx_desc_area + tx; + tx = txq->head; + tx_desc = txq->txd + tx; status = le32_to_cpu(tx_desc->status); @@ -1202,15 +1348,15 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) ~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED); } - txq->tx_head = (tx + 1) % MWL8K_TX_DESCS; - BUG_ON(txq->tx_stats.len == 0); - txq->tx_stats.len--; + txq->head = (tx + 1) % MWL8K_TX_DESCS; + BUG_ON(txq->stats.len == 0); + txq->stats.len--; priv->pending_tx_pkts--; addr = le32_to_cpu(tx_desc->pkt_phys_addr); size = le16_to_cpu(tx_desc->pkt_len); - skb = txq->tx_skb[tx]; - txq->tx_skb[tx] = NULL; + skb = txq->skb[tx]; + txq->skb[tx] = NULL; BUG_ON(skb == NULL); pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE); @@ -1243,13 +1389,13 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) mwl8k_txq_reclaim(hw, index, 1); - kfree(txq->tx_skb); - txq->tx_skb = NULL; + kfree(txq->skb); + txq->skb = NULL; pci_free_consistent(priv->pdev, MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc), - txq->tx_desc_area, txq->tx_desc_dma); - txq->tx_desc_area = NULL; + txq->txd, txq->txd_dma); + txq->txd = NULL; } static int @@ -1317,7 +1463,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) if (pci_dma_mapping_error(priv->pdev, dma)) { printk(KERN_DEBUG "%s: failed to dma map skb, " - "dropping TX frame.\n", priv->name); + "dropping TX frame.\n", wiphy_name(hw->wiphy)); dev_kfree_skb(skb); return NETDEV_TX_OK; } @@ -1326,10 +1472,10 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) txq = priv->txq + index; - BUG_ON(txq->tx_skb[txq->tx_tail] != NULL); - txq->tx_skb[txq->tx_tail] = skb; + BUG_ON(txq->skb[txq->tail] != NULL); + txq->skb[txq->tail] = skb; - tx = txq->tx_desc_area + txq->tx_tail; + tx = txq->txd + txq->tail; tx->data_rate = txdatarate; tx->tx_priority = index; tx->qos_control = cpu_to_le16(qos); @@ -1340,15 +1486,15 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) wmb(); tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); - txq->tx_stats.count++; - txq->tx_stats.len++; + txq->stats.count++; + txq->stats.len++; priv->pending_tx_pkts++; - txq->tx_tail++; - if (txq->tx_tail == MWL8K_TX_DESCS) - txq->tx_tail = 0; + txq->tail++; + if (txq->tail == MWL8K_TX_DESCS) + txq->tail = 0; - if (txq->tx_head == txq->tx_tail) + if (txq->head == txq->tail) ieee80211_stop_queue(hw, index); mwl8k_tx_start(priv); @@ -1431,7 +1577,7 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) unsigned long timeout = 0; u8 buf[32]; - cmd->result = 0xFFFF; + cmd->result = 0xffff; dma_size = le16_to_cpu(cmd->length); dma_addr = pci_map_single(priv->pdev, cmd, dma_size, PCI_DMA_BIDIRECTIONAL); @@ -1464,7 +1610,7 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) if (!timeout) { printk(KERN_ERR "%s: Command %s timeout after %u ms\n", - priv->name, + wiphy_name(hw->wiphy), mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), MWL8K_CMD_TIMEOUT_MS); rc = -ETIMEDOUT; @@ -1472,7 +1618,7 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) rc = cmd->result ? -EINVAL : 0; if (rc) printk(KERN_ERR "%s: Command %s error 0x%x\n", - priv->name, + wiphy_name(hw->wiphy), mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), le16_to_cpu(cmd->result)); } @@ -1481,9 +1627,9 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) } /* - * GET_HW_SPEC. + * CMD_GET_HW_SPEC (STA version). */ -struct mwl8k_cmd_get_hw_spec { +struct mwl8k_cmd_get_hw_spec_sta { struct mwl8k_cmd_pkt header; __u8 hw_rev; __u8 host_interface; @@ -1499,13 +1645,13 @@ struct mwl8k_cmd_get_hw_spec { __le32 tx_queue_ptrs[MWL8K_TX_QUEUES]; __le32 caps2; __le32 num_tx_desc_per_queue; - __le32 total_rx_desc; + __le32 total_rxd; } __attribute__((packed)); -static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw) +static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_get_hw_spec *cmd; + struct mwl8k_cmd_get_hw_spec_sta *cmd; int rc; int i; @@ -1518,12 +1664,12 @@ static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw) memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); - cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rx_desc_dma); + cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); for (i = 0; i < MWL8K_TX_QUEUES; i++) - cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].tx_desc_dma); + cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); - cmd->total_rx_desc = cpu_to_le32(MWL8K_RX_DESCS); + cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); rc = mwl8k_post_cmd(hw, &cmd->header); @@ -1539,6 +1685,129 @@ static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw) } /* + * CMD_GET_HW_SPEC (AP version). + */ +struct mwl8k_cmd_get_hw_spec_ap { + struct mwl8k_cmd_pkt header; + __u8 hw_rev; + __u8 host_interface; + __le16 num_wcb; + __le16 num_mcaddrs; + __u8 perm_addr[ETH_ALEN]; + __le16 region_code; + __le16 num_antenna; + __le32 fw_rev; + __le32 wcbbase0; + __le32 rxwrptr; + __le32 rxrdptr; + __le32 ps_cookie; + __le32 wcbbase1; + __le32 wcbbase2; + __le32 wcbbase3; +} __attribute__((packed)); + +static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_get_hw_spec_ap *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); + cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); + + rc = mwl8k_post_cmd(hw, &cmd->header); + + if (!rc) { + int off; + + SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr); + priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); + priv->fw_rev = le32_to_cpu(cmd->fw_rev); + priv->hw_rev = cmd->hw_rev; + + off = le32_to_cpu(cmd->wcbbase0) & 0xffff; + iowrite32(cpu_to_le32(priv->txq[0].txd_dma), priv->sram + off); + + off = le32_to_cpu(cmd->rxwrptr) & 0xffff; + iowrite32(cpu_to_le32(priv->rxq[0].rxd_dma), priv->sram + off); + + off = le32_to_cpu(cmd->rxrdptr) & 0xffff; + iowrite32(cpu_to_le32(priv->rxq[0].rxd_dma), priv->sram + off); + + off = le32_to_cpu(cmd->wcbbase1) & 0xffff; + iowrite32(cpu_to_le32(priv->txq[1].txd_dma), priv->sram + off); + + off = le32_to_cpu(cmd->wcbbase2) & 0xffff; + iowrite32(cpu_to_le32(priv->txq[2].txd_dma), priv->sram + off); + + off = le32_to_cpu(cmd->wcbbase3) & 0xffff; + iowrite32(cpu_to_le32(priv->txq[3].txd_dma), priv->sram + off); + } + + kfree(cmd); + return rc; +} + +/* + * CMD_SET_HW_SPEC. + */ +struct mwl8k_cmd_set_hw_spec { + struct mwl8k_cmd_pkt header; + __u8 hw_rev; + __u8 host_interface; + __le16 num_mcaddrs; + __u8 perm_addr[ETH_ALEN]; + __le16 region_code; + __le32 fw_rev; + __le32 ps_cookie; + __le32 caps; + __le32 rx_queue_ptr; + __le32 num_tx_queues; + __le32 tx_queue_ptrs[MWL8K_TX_QUEUES]; + __le32 flags; + __le32 num_tx_desc_per_queue; + __le32 total_rxd; +} __attribute__((packed)); + +#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 + +static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_set_hw_spec *cmd; + int rc; + int i; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); + cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); + cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); + for (i = 0; i < MWL8K_TX_QUEUES; i++) + cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); + cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT); + cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); + cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* * CMD_MAC_MULTICAST_ADR. */ struct mwl8k_cmd_mac_multicast_adr { @@ -1548,19 +1817,23 @@ struct mwl8k_cmd_mac_multicast_adr { __u8 addr[0][ETH_ALEN]; }; -#define MWL8K_ENABLE_RX_MULTICAST 0x000F +#define MWL8K_ENABLE_RX_DIRECTED 0x0001 +#define MWL8K_ENABLE_RX_MULTICAST 0x0002 +#define MWL8K_ENABLE_RX_ALL_MULTICAST 0x0004 +#define MWL8K_ENABLE_RX_BROADCAST 0x0008 static struct mwl8k_cmd_pkt * -__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, +__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, int mc_count, struct dev_addr_list *mclist) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_mac_multicast_adr *cmd; int size; - int i; - if (mc_count > priv->num_mcaddrs) - mc_count = priv->num_mcaddrs; + if (allmulti || mc_count > priv->num_mcaddrs) { + allmulti = 1; + mc_count = 0; + } size = sizeof(*cmd) + mc_count * ETH_ALEN; @@ -1570,16 +1843,24 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR); cmd->header.length = cpu_to_le16(size); - cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); - cmd->numaddr = cpu_to_le16(mc_count); - - for (i = 0; i < mc_count && mclist; i++) { - if (mclist->da_addrlen != ETH_ALEN) { - kfree(cmd); - return NULL; + cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED | + MWL8K_ENABLE_RX_BROADCAST); + + if (allmulti) { + cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST); + } else if (mc_count) { + int i; + + cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); + cmd->numaddr = cpu_to_le16(mc_count); + for (i = 0; i < mc_count && mclist; i++) { + if (mclist->da_addrlen != ETH_ALEN) { + kfree(cmd); + return NULL; + } + memcpy(cmd->addr[i], mclist->da_addr, ETH_ALEN); + mclist = mclist->next; } - memcpy(cmd->addr[i], mclist->da_addr, ETH_ALEN); - mclist = mclist->next; } return &cmd->header; @@ -1590,7 +1871,6 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, */ struct mwl8k_cmd_802_11_get_stat { struct mwl8k_cmd_pkt header; - __le16 action; __le32 stats[64]; } __attribute__((packed)); @@ -1611,7 +1891,6 @@ static int mwl8k_cmd_802_11_get_stat(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_GET); rc = mwl8k_post_cmd(hw, &cmd->header); if (!rc) { @@ -1727,6 +2006,39 @@ static int mwl8k_cmd_802_11_rf_tx_power(struct ieee80211_hw *hw, int dBm) } /* + * CMD_RF_ANTENNA. + */ +struct mwl8k_cmd_rf_antenna { + struct mwl8k_cmd_pkt header; + __le16 antenna; + __le16 mode; +} __attribute__((packed)); + +#define MWL8K_RF_ANTENNA_RX 1 +#define MWL8K_RF_ANTENNA_TX 2 + +static int +mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) +{ + struct mwl8k_cmd_rf_antenna *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->antenna = cpu_to_le16(antenna); + cmd->mode = cpu_to_le16(mask); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* * CMD_SET_PRE_SCAN. */ struct mwl8k_cmd_set_pre_scan { @@ -1904,6 +2216,46 @@ static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable) } /* + * CMD_SET_MAC_ADDR. + */ +struct mwl8k_cmd_set_mac_addr { + struct mwl8k_cmd_pkt header; + union { + struct { + __le16 mac_type; + __u8 mac_addr[ETH_ALEN]; + } mbss; + __u8 mac_addr[ETH_ALEN]; + }; +} __attribute__((packed)); + +static int mwl8k_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_set_mac_addr *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + if (priv->ap_fw) { + cmd->mbss.mac_type = 0; + memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); + } else { + memcpy(cmd->mac_addr, mac, ETH_ALEN); + } + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + + +/* * CMD_SET_RATEADAPT_MODE. */ struct mwl8k_cmd_set_rate_adapt_mode { @@ -2005,17 +2357,34 @@ struct mwl8k_cmd_set_edca_params { /* TX opportunity in units of 32 us */ __le16 txop; - /* Log exponent of max contention period: 0...15*/ - __u8 log_cw_max; + union { + struct { + /* Log exponent of max contention period: 0...15 */ + __le32 log_cw_max; + + /* Log exponent of min contention period: 0...15 */ + __le32 log_cw_min; + + /* Adaptive interframe spacing in units of 32us */ + __u8 aifs; + + /* TX queue to configure */ + __u8 txq; + } ap; + struct { + /* Log exponent of max contention period: 0...15 */ + __u8 log_cw_max; - /* Log exponent of min contention period: 0...15 */ - __u8 log_cw_min; + /* Log exponent of min contention period: 0...15 */ + __u8 log_cw_min; - /* Adaptive interframe spacing in units of 32us */ - __u8 aifs; + /* Adaptive interframe spacing in units of 32us */ + __u8 aifs; - /* TX queue to configure */ - __u8 txq; + /* TX queue to configure */ + __u8 txq; + } sta; + }; } __attribute__((packed)); #define MWL8K_SET_EDCA_CW 0x01 @@ -2031,6 +2400,7 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, __u16 cw_min, __u16 cw_max, __u8 aifs, __u16 txop) { + struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_set_edca_params *cmd; int rc; @@ -2038,14 +2408,27 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, if (cmd == NULL) return -ENOMEM; + /* + * Queues 0 (BE) and 1 (BK) are swapped in hardware for + * this call. + */ + qnum ^= !(qnum >> 1); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); cmd->txop = cpu_to_le16(txop); - cmd->log_cw_max = (u8)ilog2(cw_max + 1); - cmd->log_cw_min = (u8)ilog2(cw_min + 1); - cmd->aifs = aifs; - cmd->txq = qnum; + if (priv->ap_fw) { + cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1)); + cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1)); + cmd->ap.aifs = aifs; + cmd->ap.txq = qnum; + } else { + cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1); + cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1); + cmd->sta.aifs = aifs; + cmd->sta.txq = qnum; + } rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2093,8 +2476,8 @@ static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame, /* XXX TBD Might just have to abort and return an error */ if (payload_len > MWL8K_FJ_BEACON_MAXLEN) printk(KERN_ERR "%s(): WARNING: Incomplete beacon " - "sent to firmware. Sz=%u MAX=%u\n", __func__, - payload_len, MWL8K_FJ_BEACON_MAXLEN); + "sent to firmware. Sz=%u MAX=%u\n", __func__, + payload_len, MWL8K_FJ_BEACON_MAXLEN); if (payload_len > MWL8K_FJ_BEACON_MAXLEN) payload_len = MWL8K_FJ_BEACON_MAXLEN; @@ -2341,9 +2724,10 @@ static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw, cmd->rate_type = cpu_to_le32(rate_type); if (rate_table != NULL) { - /* Copy over each field manually so - * that bitflipping can be done - */ + /* + * Copy over each field manually so that endian + * conversion can be done. + */ cmd->rate_table.allow_rate_drop = cpu_to_le32(rate_table->allow_rate_drop); cmd->rate_table.num_rates = @@ -2399,7 +2783,7 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { if (!mutex_is_locked(&priv->fw_mutex) && - priv->radio_on && mwl8k_txq_busy(priv)) + priv->radio_on && priv->pending_tx_pkts) mwl8k_tx_start(priv); } @@ -2418,7 +2802,7 @@ static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (priv->current_channel == NULL) { printk(KERN_DEBUG "%s: dropped TX frame since radio " - "disabled\n", priv->name); + "disabled\n", wiphy_name(hw->wiphy)); dev_kfree_skb(skb); return NETDEV_TX_OK; } @@ -2433,11 +2817,11 @@ static int mwl8k_start(struct ieee80211_hw *hw) struct mwl8k_priv *priv = hw->priv; int rc; - rc = request_irq(priv->pdev->irq, &mwl8k_interrupt, + rc = request_irq(priv->pdev->irq, mwl8k_interrupt, IRQF_SHARED, MWL8K_NAME, hw); if (rc) { printk(KERN_ERR "%s: failed to register IRQ handler\n", - priv->name); + wiphy_name(hw->wiphy)); return -EIO; } @@ -2451,12 +2835,17 @@ static int mwl8k_start(struct ieee80211_hw *hw) if (!rc) { rc = mwl8k_cmd_802_11_radio_enable(hw); - if (!rc) - rc = mwl8k_cmd_set_pre_scan(hw); + if (!priv->ap_fw) { + if (!rc) + rc = mwl8k_enable_sniffer(hw, 0); - if (!rc) - rc = mwl8k_cmd_set_post_scan(hw, - "\x00\x00\x00\x00\x00\x00"); + if (!rc) + rc = mwl8k_cmd_set_pre_scan(hw); + + if (!rc) + rc = mwl8k_cmd_set_post_scan(hw, + "\x00\x00\x00\x00\x00\x00"); + } if (!rc) rc = mwl8k_cmd_setrateadaptmode(hw, 0); @@ -2464,9 +2853,6 @@ static int mwl8k_start(struct ieee80211_hw *hw) if (!rc) rc = mwl8k_set_wmm(hw, 0); - if (!rc) - rc = mwl8k_enable_sniffer(hw, 0); - mwl8k_fw_unlock(hw); } @@ -2500,9 +2886,6 @@ static void mwl8k_stop(struct ieee80211_hw *hw) /* Stop tx reclaim tasklet */ tasklet_disable(&priv->tx_reclaim_task); - /* Stop config thread */ - flush_workqueue(priv->config_wq); - /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_reclaim(hw, i, 1); @@ -2526,11 +2909,24 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, if (conf->type != NL80211_IFTYPE_STATION) return -EINVAL; + /* + * Reject interface creation if sniffer mode is active, as + * STA operation is mutually exclusive with hardware sniffer + * mode. + */ + if (priv->sniffer_enabled) { + printk(KERN_INFO "%s: unable to create STA " + "interface due to sniffer mode being enabled\n", + wiphy_name(hw->wiphy)); + return -EINVAL; + } + /* Clean out driver private area */ mwl8k_vif = MWL8K_VIF(conf->vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); - /* Save the mac address */ + /* Set and save the mac address */ + mwl8k_set_mac_addr(hw, conf->mac_addr); memcpy(mwl8k_vif->mac_addr, conf->mac_addr, ETH_ALEN); /* Back pointer to parent config block */ @@ -2558,6 +2954,8 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, if (priv->vif == NULL) return; + mwl8k_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); + priv->vif = NULL; } @@ -2593,8 +2991,13 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (rc) goto out; - if (mwl8k_cmd_mimo_config(hw, 0x7, 0x7)) - rc = -EINVAL; + if (priv->ap_fw) { + rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x7); + if (!rc) + rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); + } else { + rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7); + } out: mwl8k_fw_unlock(hw); @@ -2681,32 +3084,108 @@ static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, { struct mwl8k_cmd_pkt *cmd; - cmd = __mwl8k_cmd_mac_multicast_adr(hw, mc_count, mclist); + /* + * Synthesize and return a command packet that programs the + * hardware multicast address filter. At this point we don't + * know whether FIF_ALLMULTI is being requested, but if it is, + * we'll end up throwing this packet away and creating a new + * one in mwl8k_configure_filter(). + */ + cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_count, mclist); return (unsigned long)cmd; } +static int +mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags) +{ + struct mwl8k_priv *priv = hw->priv; + + /* + * Hardware sniffer mode is mutually exclusive with STA + * operation, so refuse to enable sniffer mode if a STA + * interface is active. + */ + if (priv->vif != NULL) { + if (net_ratelimit()) + printk(KERN_INFO "%s: not enabling sniffer " + "mode because STA interface is active\n", + wiphy_name(hw->wiphy)); + return 0; + } + + if (!priv->sniffer_enabled) { + if (mwl8k_enable_sniffer(hw, 1)) + return 0; + priv->sniffer_enabled = true; + } + + *total_flags &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL | + FIF_OTHER_BSS; + + return 1; +} + static void mwl8k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_pkt *multicast_adr_cmd; + struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast; + + /* + * AP firmware doesn't allow fine-grained control over + * the receive filter. + */ + if (priv->ap_fw) { + *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; + kfree(cmd); + return; + } + + /* + * Enable hardware sniffer mode if FIF_CONTROL or + * FIF_OTHER_BSS is requested. + */ + if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) && + mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) { + kfree(cmd); + return; + } /* Clear unsupported feature flags */ - *total_flags &= FIF_BCN_PRBRESP_PROMISC; + *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; if (mwl8k_fw_lock(hw)) return; + if (priv->sniffer_enabled) { + mwl8k_enable_sniffer(hw, 0); + priv->sniffer_enabled = false; + } + if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { - if (*total_flags & FIF_BCN_PRBRESP_PROMISC) + if (*total_flags & FIF_BCN_PRBRESP_PROMISC) { + /* + * Disable the BSS filter. + */ mwl8k_cmd_set_pre_scan(hw); - else { + } else { u8 *bssid; - bssid = "\x00\x00\x00\x00\x00\x00"; + /* + * Enable the BSS filter. + * + * If there is an active STA interface, use that + * interface's BSSID, otherwise use a dummy one + * (where the OUI part needs to be nonzero for + * the BSSID to be accepted by POST_SCAN). + */ + bssid = "\x01\x00\x00\x00\x00\x00"; if (priv->vif != NULL) bssid = MWL8K_VIF(priv->vif)->bssid; @@ -2714,10 +3193,20 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, } } - multicast_adr_cmd = (void *)(unsigned long)multicast; - if (multicast_adr_cmd != NULL) { - mwl8k_post_cmd(hw, multicast_adr_cmd); - kfree(multicast_adr_cmd); + /* + * If FIF_ALLMULTI is being requested, throw away the command + * packet that ->prepare_multicast() built and replace it with + * a command packet that enables reception of all multicast + * packets. + */ + if (*total_flags & FIF_ALLMULTI) { + kfree(cmd); + cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, 0, NULL); + } + + if (cmd != NULL) { + mwl8k_post_cmd(hw, cmd); + kfree(cmd); } mwl8k_fw_unlock(hw); @@ -2762,7 +3251,7 @@ static int mwl8k_get_tx_stats(struct ieee80211_hw *hw, spin_lock_bh(&priv->tx_lock); for (index = 0; index < MWL8K_TX_QUEUES; index++) { txq = priv->txq + index; - memcpy(&stats[index], &txq->tx_stats, + memcpy(&stats[index], &txq->stats, sizeof(struct ieee80211_tx_queue_stats)); } spin_unlock_bh(&priv->tx_lock); @@ -2802,7 +3291,7 @@ static void mwl8k_tx_reclaim_handler(unsigned long data) for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_reclaim(hw, i, 0); - if (priv->tx_wait != NULL && mwl8k_txq_busy(priv) == 0) { + if (priv->tx_wait != NULL && !priv->pending_tx_pkts) { complete(priv->tx_wait); priv->tx_wait = NULL; } @@ -2822,6 +3311,36 @@ static void mwl8k_finalize_join_worker(struct work_struct *work) priv->beacon_skb = NULL; } +enum { + MWL8687 = 0, + MWL8366, +}; + +static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { + { + .part_name = "88w8687", + .helper_image = "mwl8k/helper_8687.fw", + .fw_image = "mwl8k/fmimage_8687.fw", + .rxd_ops = &rxd_8687_ops, + .modes = BIT(NL80211_IFTYPE_STATION), + }, + { + .part_name = "88w8366", + .helper_image = "mwl8k/helper_8366.fw", + .fw_image = "mwl8k/fmimage_8366.fw", + .rxd_ops = &rxd_8366_ops, + .modes = 0, + }, +}; + +static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = { + { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, + { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, + { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, + { }, +}; +MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); + static int __devinit mwl8k_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -2862,17 +3381,34 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, priv = hw->priv; priv->hw = hw; priv->pdev = pdev; + priv->device_info = &mwl8k_info_tbl[id->driver_data]; + priv->rxd_ops = priv->device_info->rxd_ops; + priv->sniffer_enabled = false; priv->wmm_enabled = false; priv->pending_tx_pkts = 0; - strncpy(priv->name, MWL8K_NAME, sizeof(priv->name)); SET_IEEE80211_DEV(hw, &pdev->dev); pci_set_drvdata(pdev, hw); + priv->sram = pci_iomap(pdev, 0, 0x10000); + if (priv->sram == NULL) { + printk(KERN_ERR "%s: Cannot map device SRAM\n", + wiphy_name(hw->wiphy)); + goto err_iounmap; + } + + /* + * If BAR0 is a 32 bit BAR, the register BAR will be BAR1. + * If BAR0 is a 64 bit BAR, the register BAR will be BAR2. + */ priv->regs = pci_iomap(pdev, 1, 0x10000); if (priv->regs == NULL) { - printk(KERN_ERR "%s: Cannot map device memory\n", priv->name); - goto err_iounmap; + priv->regs = pci_iomap(pdev, 2, 0x10000); + if (priv->regs == NULL) { + printk(KERN_ERR "%s: Cannot map device registers\n", + wiphy_name(hw->wiphy)); + goto err_iounmap; + } } memcpy(priv->channels, mwl8k_channels, sizeof(mwl8k_channels)); @@ -2897,7 +3433,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, hw->queues = MWL8K_TX_QUEUES; - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + hw->wiphy->interface_modes = priv->device_info->modes; /* Set rssi and noise values to dBm */ hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; @@ -2916,11 +3452,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, mwl8k_tx_reclaim_handler, (unsigned long)hw); tasklet_disable(&priv->tx_reclaim_task); - /* Config workthread */ - priv->config_wq = create_singlethread_workqueue("mwl8k_config"); - if (priv->config_wq == NULL) - goto err_iounmap; - /* Power management cookie */ priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); if (priv->cookie == NULL) @@ -2934,11 +3465,12 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, mutex_init(&priv->fw_mutex); priv->fw_mutex_owner = NULL; priv->fw_mutex_depth = 0; - priv->tx_wait = NULL; priv->hostcmd_wait = NULL; spin_lock_init(&priv->tx_lock); + priv->tx_wait = NULL; + for (i = 0; i < MWL8K_TX_QUEUES; i++) { rc = mwl8k_txq_init(hw, i); if (rc) @@ -2950,11 +3482,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); - rc = request_irq(priv->pdev->irq, &mwl8k_interrupt, + rc = request_irq(priv->pdev->irq, mwl8k_interrupt, IRQF_SHARED, MWL8K_NAME, hw); if (rc) { printk(KERN_ERR "%s: failed to register IRQ handler\n", - priv->name); + wiphy_name(hw->wiphy)); goto err_free_queues; } @@ -2962,16 +3494,18 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, mwl8k_hw_reset(priv); /* Ask userland hotplug daemon for the device firmware */ - rc = mwl8k_request_firmware(priv, (u32)id->driver_data); + rc = mwl8k_request_firmware(priv); if (rc) { - printk(KERN_ERR "%s: Firmware files not found\n", priv->name); + printk(KERN_ERR "%s: Firmware files not found\n", + wiphy_name(hw->wiphy)); goto err_free_irq; } /* Load firmware into hardware */ - rc = mwl8k_load_firmware(priv); + rc = mwl8k_load_firmware(hw); if (rc) { - printk(KERN_ERR "%s: Cannot start firmware\n", priv->name); + printk(KERN_ERR "%s: Cannot start firmware\n", + wiphy_name(hw->wiphy)); goto err_stop_firmware; } @@ -2986,16 +3520,31 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); /* Get config data, mac addrs etc */ - rc = mwl8k_cmd_get_hw_spec(hw); + if (priv->ap_fw) { + rc = mwl8k_cmd_get_hw_spec_ap(hw); + if (!rc) + rc = mwl8k_cmd_set_hw_spec(hw); + } else { + rc = mwl8k_cmd_get_hw_spec_sta(hw); + } if (rc) { - printk(KERN_ERR "%s: Cannot initialise firmware\n", priv->name); + printk(KERN_ERR "%s: Cannot initialise firmware\n", + wiphy_name(hw->wiphy)); goto err_stop_firmware; } /* Turn radio off */ rc = mwl8k_cmd_802_11_radio_disable(hw); if (rc) { - printk(KERN_ERR "%s: Cannot disable\n", priv->name); + printk(KERN_ERR "%s: Cannot disable\n", wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + + /* Clear MAC address */ + rc = mwl8k_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); + if (rc) { + printk(KERN_ERR "%s: Cannot clear MAC address\n", + wiphy_name(hw->wiphy)); goto err_stop_firmware; } @@ -3005,13 +3554,15 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, rc = ieee80211_register_hw(hw); if (rc) { - printk(KERN_ERR "%s: Cannot register device\n", priv->name); + printk(KERN_ERR "%s: Cannot register device\n", + wiphy_name(hw->wiphy)); goto err_stop_firmware; } - printk(KERN_INFO "%s: 88w%u v%d, %pM, firmware version %u.%u.%u.%u\n", - wiphy_name(hw->wiphy), priv->part_num, priv->hw_rev, - hw->wiphy->perm_addr, + printk(KERN_INFO "%s: %s v%d, %pM, %s firmware %u.%u.%u.%u\n", + wiphy_name(hw->wiphy), priv->device_info->part_name, + priv->hw_rev, hw->wiphy->perm_addr, + priv->ap_fw ? "AP" : "STA", (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff, (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff); @@ -3038,8 +3589,8 @@ err_iounmap: if (priv->regs != NULL) pci_iounmap(pdev, priv->regs); - if (priv->config_wq != NULL) - destroy_workqueue(priv->config_wq); + if (priv->sram != NULL) + pci_iounmap(pdev, priv->sram); pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); @@ -3073,9 +3624,6 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) /* Remove tx reclaim tasklet */ tasklet_kill(&priv->tx_reclaim_task); - /* Stop config thread */ - destroy_workqueue(priv->config_wq); - /* Stop hardware */ mwl8k_hw_reset(priv); @@ -3088,10 +3636,10 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) mwl8k_rxq_deinit(hw, 0); - pci_free_consistent(priv->pdev, 4, - priv->cookie, priv->cookie_dma); + pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma); pci_iounmap(pdev, priv->regs); + pci_iounmap(pdev, priv->sram); pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); pci_release_regions(pdev); @@ -3100,7 +3648,7 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) static struct pci_driver mwl8k_driver = { .name = MWL8K_NAME, - .id_table = mwl8k_table, + .id_table = mwl8k_pci_id_table, .probe = mwl8k_probe, .remove = __devexit_p(mwl8k_remove), .shutdown = __devexit_p(mwl8k_shutdown), @@ -3118,3 +3666,8 @@ static void __exit mwl8k_exit(void) module_init(mwl8k_init); module_exit(mwl8k_exit); + +MODULE_DESCRIPTION(MWL8K_DESC); +MODULE_VERSION(MWL8K_VERSION); +MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index 83b635fd7784..e2a2c18920aa 100644 --- a/drivers/net/wireless/orinoco/Kconfig +++ b/drivers/net/wireless/orinoco/Kconfig @@ -1,8 +1,10 @@ config HERMES tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" - depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211 - depends on CFG80211 + depends on (PPC_PMAC || PCI || PCMCIA) + depends on CFG80211 && CFG80211_WEXT select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV select FW_LOADER select CRYPTO select CRYPTO_MICHAEL_MIC diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/orinoco/fw.c index 1257250a1e22..cfa72962052b 100644 --- a/drivers/net/wireless/orinoco/fw.c +++ b/drivers/net/wireless/orinoco/fw.c @@ -28,6 +28,12 @@ static const struct fw_info orinoco_fw[] = { { NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 }, { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 } }; +MODULE_FIRMWARE("agere_sta_fw.bin"); +MODULE_FIRMWARE("agere_ap_fw.bin"); +MODULE_FIRMWARE("prism_sta_fw.bin"); +MODULE_FIRMWARE("prism_ap_fw.bin"); +MODULE_FIRMWARE("symbol_sp24t_prim_fw"); +MODULE_FIRMWARE("symbol_sp24t_sec_fw"); /* Structure used to access fields in FW * Make sure LE decoding macros are used diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c index a3eefe109df4..84200da900b6 100644 --- a/drivers/net/wireless/orinoco/hermes_dld.c +++ b/drivers/net/wireless/orinoco/hermes_dld.c @@ -550,7 +550,7 @@ static const struct { \ #define DEFAULT_PDR(pid) default_pdr_data_##pid -/* HWIF Compatiblity */ +/* HWIF Compatibility */ DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); /* PPPPSign */ @@ -656,7 +656,7 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, record_id + 1, pdi); } break; - case 0x5: /* HWIF Compatiblity */ + case 0x5: /* HWIF Compatibility */ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005); break; case 0x108: /* PPPPSign */ diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c index 359652d35e63..404830f47ab2 100644 --- a/drivers/net/wireless/orinoco/hw.c +++ b/drivers/net/wireless/orinoco/hw.c @@ -60,8 +60,15 @@ static inline fwtype_t determine_firmware_type(struct comp_id *nic_id) /* Set priv->firmware type, determine firmware properties * This function can be called before we have registerred with netdev, * so all errors go out with dev_* rather than printk + * + * If non-NULL stores a firmware description in fw_name. + * If non-NULL stores a HW version in hw_ver + * + * These are output via generic cfg80211 ethtool support. */ -int determine_fw_capabilities(struct orinoco_private *priv) +int determine_fw_capabilities(struct orinoco_private *priv, + char *fw_name, size_t fw_name_len, + u32 *hw_ver) { struct device *dev = priv->dev; hermes_t *hw = &priv->hw; @@ -85,6 +92,12 @@ int determine_fw_capabilities(struct orinoco_private *priv) dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n", nic_id.id, nic_id.variant, nic_id.major, nic_id.minor); + if (hw_ver) + *hw_ver = (((nic_id.id & 0xff) << 24) | + ((nic_id.variant & 0xff) << 16) | + ((nic_id.major & 0xff) << 8) | + (nic_id.minor & 0xff)); + priv->firmware_type = determine_firmware_type(&nic_id); /* Get the firmware version */ @@ -135,8 +148,9 @@ int determine_fw_capabilities(struct orinoco_private *priv) case FIRMWARE_TYPE_AGERE: /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ - snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, - "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor); + if (fw_name) + snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d", + sta_id.major, sta_id.minor); firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; @@ -185,8 +199,8 @@ int determine_fw_capabilities(struct orinoco_private *priv) tmp[SYMBOL_MAX_VER_LEN] = '\0'; } - snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, - "Symbol %s", tmp); + if (fw_name) + snprintf(fw_name, fw_name_len, "Symbol %s", tmp); priv->has_ibss = (firmver >= 0x20000); priv->has_wep = (firmver >= 0x15012); @@ -224,9 +238,9 @@ int determine_fw_capabilities(struct orinoco_private *priv) * different and less well tested */ /* D-Link MAC : 00:40:05:* */ /* Addtron MAC : 00:90:D1:* */ - snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, - "Intersil %d.%d.%d", sta_id.major, sta_id.minor, - sta_id.variant); + if (fw_name) + snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d", + sta_id.major, sta_id.minor, sta_id.variant); firmver = ((unsigned long)sta_id.major << 16) | ((unsigned long)sta_id.minor << 8) | sta_id.variant; @@ -245,7 +259,8 @@ int determine_fw_capabilities(struct orinoco_private *priv) } break; } - dev_info(dev, "Firmware determined as %s\n", priv->fw_name); + if (fw_name) + dev_info(dev, "Firmware determined as %s\n", fw_name); return 0; } diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h index 8df6e8752be6..e2f7fdc4d45a 100644 --- a/drivers/net/wireless/orinoco/hw.h +++ b/drivers/net/wireless/orinoco/hw.h @@ -24,7 +24,8 @@ struct orinoco_private; struct dev_addr_list; -int determine_fw_capabilities(struct orinoco_private *priv); +int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name, + size_t fw_name_len, u32 *hw_ver); int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr); int orinoco_hw_allocate_fid(struct orinoco_private *priv); int orinoco_get_bitratemode(int bitrate, int automatic); diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index 7a32bcb0c037..753a1804eee7 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -83,7 +83,6 @@ #include <linux/device.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> -#include <linux/ethtool.h> #include <linux/suspend.h> #include <linux/if_arp.h> #include <linux/wireless.h> @@ -162,8 +161,6 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; | HERMES_EV_WTERR | HERMES_EV_INFO \ | HERMES_EV_INFDROP) -static const struct ethtool_ops orinoco_ethtool_ops; - /********************************************************************/ /* Data types */ /********************************************************************/ @@ -1994,7 +1991,9 @@ int orinoco_init(struct orinoco_private *priv) goto out; } - err = determine_fw_capabilities(priv); + err = determine_fw_capabilities(priv, wiphy->fw_version, + sizeof(wiphy->fw_version), + &wiphy->hw_version); if (err != 0) { dev_err(dev, "Incompatible firmware, aborting\n"); goto out; @@ -2010,7 +2009,9 @@ int orinoco_init(struct orinoco_private *priv) priv->do_fw_download = 0; /* Check firmware version again */ - err = determine_fw_capabilities(priv); + err = determine_fw_capabilities(priv, wiphy->fw_version, + sizeof(wiphy->fw_version), + &wiphy->hw_version); if (err != 0) { dev_err(dev, "Incompatible firmware, aborting\n"); goto out; @@ -2212,7 +2213,6 @@ int orinoco_if_add(struct orinoco_private *priv, dev->ieee80211_ptr = wdev; dev->netdev_ops = &orinoco_netdev_ops; dev->watchdog_timeo = HZ; /* 1 second timeout */ - dev->ethtool_ops = &orinoco_ethtool_ops; dev->wireless_handlers = &orinoco_handler_def; #ifdef WIRELESS_SPY dev->wireless_data = &priv->wireless_data; @@ -2225,6 +2225,7 @@ int orinoco_if_add(struct orinoco_private *priv, netif_carrier_off(dev); memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); + memcpy(dev->perm_addr, wiphy->perm_addr, ETH_ALEN); dev->base_addr = base_addr; dev->irq = irq; @@ -2348,27 +2349,6 @@ void orinoco_down(struct orinoco_private *priv) } EXPORT_SYMBOL(orinoco_down); -static void orinoco_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - struct orinoco_private *priv = ndev_priv(dev); - - strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1); - strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1); - strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1); - if (dev->dev.parent) - strncpy(info->bus_info, dev_name(dev->dev.parent), - sizeof(info->bus_info) - 1); - else - snprintf(info->bus_info, sizeof(info->bus_info) - 1, - "PCMCIA %p", priv->hw.iobase); -} - -static const struct ethtool_ops orinoco_ethtool_ops = { - .get_drvinfo = orinoco_get_drvinfo, - .get_link = ethtool_op_get_link, -}; - /********************************************************************/ /* Module initialization */ /********************************************************************/ diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h index 9ac6f1dda4b0..665ef56f8382 100644 --- a/drivers/net/wireless/orinoco/orinoco.h +++ b/drivers/net/wireless/orinoco/orinoco.h @@ -93,7 +93,6 @@ struct orinoco_private { /* Capabilities of the hardware/firmware */ fwtype_t firmware_type; - char fw_name[32]; int ibss_port; int nicbuf_size; u16 channel_mask; diff --git a/drivers/net/wireless/p54/Kconfig b/drivers/net/wireless/p54/Kconfig index b45d6a4ed1e8..b0342a520bf1 100644 --- a/drivers/net/wireless/p54/Kconfig +++ b/drivers/net/wireless/p54/Kconfig @@ -1,6 +1,6 @@ config P54_COMMON tristate "Softmac Prism54 support" - depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + depends on MAC80211 && EXPERIMENTAL select FW_LOADER ---help--- This is common code for isl38xx/stlc45xx based modules. diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index 0efe67deedee..8e3818f6832e 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c @@ -126,7 +126,7 @@ static int p54_generate_band(struct ieee80211_hw *dev, int ret = -ENOMEM; if ((!list->entries) || (!list->band_channel_num[band])) - return 0; + return -EINVAL; tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) @@ -158,6 +158,7 @@ static int p54_generate_band(struct ieee80211_hw *dev, (list->channels[i].data & CHAN_HAS_CURVE ? "" : " [curve data]"), list->channels[i].index, list->channels[i].freq); + continue; } tmp->channels[j].band = list->channels[i].band; @@ -165,7 +166,16 @@ static int p54_generate_band(struct ieee80211_hw *dev, j++; } - tmp->n_channels = list->band_channel_num[band]; + if (j == 0) { + printk(KERN_ERR "%s: Disabling totally damaged %s band.\n", + wiphy_name(dev->wiphy), (band == IEEE80211_BAND_2GHZ) ? + "2 GHz" : "5 GHz"); + + ret = -ENODATA; + goto err_out; + } + + tmp->n_channels = j; old = priv->band_table[band]; priv->band_table[band] = tmp; if (old) { @@ -228,13 +238,13 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) struct p54_common *priv = dev->priv; struct p54_channel_list *list; unsigned int i, j, max_channel_num; - int ret = -ENOMEM; + int ret = 0; u16 freq; if ((priv->iq_autocal_len != priv->curve_data->entries) || (priv->iq_autocal_len != priv->output_limit->entries)) - printk(KERN_ERR "%s: EEPROM is damaged... you may not be able" - "to use all channels with this device.\n", + printk(KERN_ERR "%s: Unsupported or damaged EEPROM detected. " + "You may not be able to use all channels.\n", wiphy_name(dev->wiphy)); max_channel_num = max_t(unsigned int, priv->output_limit->entries, @@ -243,8 +253,10 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) priv->curve_data->entries); list = kzalloc(sizeof(*list), GFP_KERNEL); - if (!list) + if (!list) { + ret = -ENOMEM; goto free; + } list->max_entries = max_channel_num; list->channels = kzalloc(sizeof(struct p54_channel_entry) * @@ -282,13 +294,8 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) p54_compare_channels, NULL); for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) { - if (list->band_channel_num[i]) { - ret = p54_generate_band(dev, list, i); - if (ret) - goto free; - + if (p54_generate_band(dev, list, i) == 0) j++; - } } if (j == 0) { /* no useable band available. */ diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 4d486bf9f725..18012dbfb45d 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -579,7 +579,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) * For now, disable PS by default because it affects * link stability significantly. */ - dev->wiphy->ps_default = false; + dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; mutex_init(&priv->conf_mutex); mutex_init(&priv->eeprom_mutex); diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index d348c265e867..a15962a19b2a 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -411,7 +411,7 @@ static int p54p_open(struct ieee80211_hw *dev) int err; init_completion(&priv->boot_comp); - err = request_irq(priv->pdev->irq, &p54p_interrupt, + err = request_irq(priv->pdev->irq, p54p_interrupt, IRQF_SHARED, "p54pci", dev); if (err) { dev_err(&priv->pdev->dev, "failed to register IRQ handler\n"); diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index bc08464d8323..f7f5c793514b 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -1897,7 +1897,7 @@ prism54_get_mac(struct net_device *ndev, struct iw_request_info *info, return 0; } -/* Setting policy also clears the MAC acl, even if we don't change the defaut +/* Setting policy also clears the MAC acl, even if we don't change the default * policy */ @@ -2323,7 +2323,7 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid, case DOT11_OID_BEACON: send_formatted_event(priv, - "Received a beacon from an unkown AP", + "Received a beacon from an unknown AP", mlme, 0); break; diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index 2505be56ae39..a3ba3539db02 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -41,6 +41,9 @@ #define ISL3877_IMAGE_FILE "isl3877" #define ISL3886_IMAGE_FILE "isl3886" #define ISL3890_IMAGE_FILE "isl3890" +MODULE_FIRMWARE(ISL3877_IMAGE_FILE); +MODULE_FIRMWARE(ISL3886_IMAGE_FILE); +MODULE_FIRMWARE(ISL3890_IMAGE_FILE); static int prism54_bring_down(islpci_private *); static int islpci_alloc_memory(islpci_private *); diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/prism54/islpci_hotplug.c index 83d366258c81..e4f2bb7368f2 100644 --- a/drivers/net/wireless/prism54/islpci_hotplug.c +++ b/drivers/net/wireless/prism54/islpci_hotplug.c @@ -181,7 +181,7 @@ prism54_probe(struct pci_dev *pdev, const struct pci_device_id *id) isl38xx_disable_interrupts(priv->device_base); /* request for the interrupt before uploading the firmware */ - rvalue = request_irq(pdev->irq, &islpci_interrupt, + rvalue = request_irq(pdev->irq, islpci_interrupt, IRQF_SHARED, ndev->name, priv); if (rvalue) { diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 5b8e3e4cdd9f..88e1e4e32b22 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -2854,18 +2854,8 @@ static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type) /*===========================================================================*/ #ifdef CONFIG_PROC_FS -static void raycs_write(const char *name, write_proc_t *w, void *data) -{ - struct proc_dir_entry *entry = - create_proc_entry(name, S_IFREG | S_IWUSR, NULL); - if (entry) { - entry->write_proc = w; - entry->data = data; - } -} - -static int write_essid(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t ray_cs_essid_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) { static char proc_essid[33]; unsigned int len = count; @@ -2879,8 +2869,13 @@ static int write_essid(struct file *file, const char __user *buffer, return count; } -static int write_int(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static const struct file_operations ray_cs_essid_proc_fops = { + .owner = THIS_MODULE, + .write = ray_cs_essid_proc_write, +}; + +static ssize_t int_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) { static char proc_number[10]; char *p; @@ -2903,9 +2898,14 @@ static int write_int(struct file *file, const char __user *buffer, nr = nr * 10 + c; p++; } while (--len); - *(int *)data = nr; + *(int *)PDE(file->f_path.dentry->d_inode)->data = nr; return count; } + +static const struct file_operations int_proc_fops = { + .owner = THIS_MODULE, + .write = int_proc_write, +}; #endif static struct pcmcia_device_id ray_ids[] = { @@ -2940,9 +2940,9 @@ static int __init init_ray_cs(void) proc_mkdir("driver/ray_cs", NULL); proc_create("driver/ray_cs/ray_cs", 0, NULL, &ray_cs_proc_fops); - raycs_write("driver/ray_cs/essid", write_essid, NULL); - raycs_write("driver/ray_cs/net_type", write_int, &net_type); - raycs_write("driver/ray_cs/translate", write_int, &translate); + proc_create("driver/ray_cs/essid", S_IWUSR, NULL, &ray_cs_essid_proc_fops); + proc_create_data("driver/ray_cs/net_type", S_IWUSR, NULL, &int_proc_fops, &net_type); + proc_create_data("driver/ray_cs/translate", S_IWUSR, NULL, &int_proc_fops, &translate); #endif if (translate != 0) translate = 1; diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 54175b6fa86c..2ecbedb26e15 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -83,11 +83,11 @@ MODULE_PARM_DESC(roamdelta, "set roaming tendency: 0=aggressive, 1=moderate, " "2=conservative (default: moderate)"); -static int modparam_workaround_interval = 500; +static int modparam_workaround_interval; module_param_named(workaround_interval, modparam_workaround_interval, int, 0444); MODULE_PARM_DESC(workaround_interval, - "set stall workaround interval in msecs (default: 500)"); + "set stall workaround interval in msecs (0=disabled) (default: 0)"); /* various RNDIS OID defs */ @@ -733,12 +733,13 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) le32_to_cpu(u.get_c->status)); if (ret == 0) { + memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len); + ret = le32_to_cpu(u.get_c->len); if (ret > *len) *len = ret; - memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len); - ret = rndis_error_status(u.get_c->status); + ret = rndis_error_status(u.get_c->status); if (ret < 0) devdbg(dev, "rndis_query_oid(%s): device returned " "error, 0x%08x (%d)", oid_to_string(oid), @@ -1072,6 +1073,8 @@ static int set_auth_mode(struct usbnet *usbdev, u32 wpa_version, auth_mode = NDIS_80211_AUTH_SHARED; else if (auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) auth_mode = NDIS_80211_AUTH_OPEN; + else if (auth_type == NL80211_AUTHTYPE_AUTOMATIC) + auth_mode = NDIS_80211_AUTH_AUTO_SWITCH; else return -ENOTSUPP; @@ -2547,7 +2550,7 @@ static void rndis_device_poller(struct work_struct *work) /* Workaround transfer stalls on poor quality links. * TODO: find right way to fix these stalls (as stalls do not happen * with ndiswrapper/windows driver). */ - if (priv->last_qual <= 25) { + if (priv->param_workaround_interval > 0 && priv->last_qual <= 25) { /* Decrease stats worker interval to catch stalls. * faster. Faster than 400-500ms causes packet loss, * Slower doesn't catch stalls fast enough. diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index ed1f997e3521..bf60689aaabb 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -1,6 +1,6 @@ menuconfig RT2X00 tristate "Ralink driver support" - depends on MAC80211 && WLAN_80211 + depends on MAC80211 ---help--- This will enable the support for the Ralink drivers, developed in the rt2x00 project <http://rt2x00.serialmonkey.com>. @@ -53,6 +53,36 @@ config RT61PCI When compiled as a module, this driver will be called rt61pci. +config RT2800PCI_PCI + tristate + depends on PCI + default y + +config RT2800PCI_SOC + tristate + depends on RALINK_RT288X || RALINK_RT305X + default y + +config RT2800PCI + tristate "Ralink rt2800 (PCI/PCMCIA) support (VERY EXPERIMENTAL)" + depends on (RT2800PCI_PCI || RT2800PCI_SOC) && EXPERIMENTAL + select RT2800_LIB + select RT2X00_LIB_PCI if RT2800PCI_PCI + select RT2X00_LIB_SOC if RT2800PCI_SOC + select RT2X00_LIB_HT + select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_CCITT + select EEPROM_93CX6 + ---help--- + This adds support for rt2800 wireless chipset family. + Supported chips: RT2760, RT2790, RT2860, RT2880, RT2890 & RT3052 + + This driver is non-functional at the moment and is intended for + developers. + + When compiled as a module, this driver will be called "rt2800pci.ko". + config RT2500USB tristate "Ralink rt2500 (USB) support" depends on USB @@ -78,8 +108,9 @@ config RT73USB When compiled as a module, this driver will be called rt73usb. config RT2800USB - tristate "Ralink rt2800 (USB) support" + tristate "Ralink rt2800 (USB) support (EXPERIMENTAL)" depends on USB && EXPERIMENTAL + select RT2800_LIB select RT2X00_LIB_USB select RT2X00_LIB_HT select RT2X00_LIB_FIRMWARE @@ -89,12 +120,23 @@ config RT2800USB This adds experimental support for rt2800 wireless chipset family. Supported chips: RT2770, RT2870 & RT3070. + Known issues: + - support for RT2870 chips doesn't work with 802.11n APs yet + - support for RT3070 chips is non-functional at the moment + When compiled as a module, this driver will be called "rt2800usb.ko". +config RT2800_LIB + tristate + config RT2X00_LIB_PCI tristate select RT2X00_LIB +config RT2X00_LIB_SOC + tristate + select RT2X00_LIB + config RT2X00_LIB_USB tristate select RT2X00_LIB diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/rt2x00/Makefile index 13043ea97667..971339858297 100644 --- a/drivers/net/wireless/rt2x00/Makefile +++ b/drivers/net/wireless/rt2x00/Makefile @@ -11,10 +11,13 @@ rt2x00lib-$(CONFIG_RT2X00_LIB_HT) += rt2x00ht.o obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o +obj-$(CONFIG_RT2X00_LIB_SOC) += rt2x00soc.o obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o +obj-$(CONFIG_RT2800_LIB) += rt2800lib.o obj-$(CONFIG_RT2400PCI) += rt2400pci.o obj-$(CONFIG_RT2500PCI) += rt2500pci.o obj-$(CONFIG_RT61PCI) += rt61pci.o +obj-$(CONFIG_RT2800PCI) += rt2800pci.o obj-$(CONFIG_RT2500USB) += rt2500usb.o obj-$(CONFIG_RT73USB) += rt73usb.o obj-$(CONFIG_RT2800USB) += rt2800usb.o diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 798f625e38f7..e7f46405a418 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -1341,6 +1341,7 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, CSR0, ®); rt2x00_set_chip_rf(rt2x00dev, value, reg); + rt2x00_print_chip(rt2x00dev); if (!rt2x00_rf(&rt2x00dev->chip, RF2420) && !rt2x00_rf(&rt2x00dev->chip, RF2421)) { @@ -1431,7 +1432,6 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; - rt2x00dev->hw->extra_tx_headroom = 0; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, @@ -1622,20 +1622,21 @@ static const struct data_queue_desc rt2400pci_queue_atim = { }; static const struct rt2x00_ops rt2400pci_ops = { - .name = KBUILD_MODNAME, - .max_sta_intf = 1, - .max_ap_intf = 1, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .rx = &rt2400pci_queue_rx, - .tx = &rt2400pci_queue_tx, - .bcn = &rt2400pci_queue_bcn, - .atim = &rt2400pci_queue_atim, - .lib = &rt2400pci_rt2x00_ops, - .hw = &rt2400pci_mac80211_ops, + .name = KBUILD_MODNAME, + .max_sta_intf = 1, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .extra_tx_headroom = 0, + .rx = &rt2400pci_queue_rx, + .tx = &rt2400pci_queue_tx, + .bcn = &rt2400pci_queue_bcn, + .atim = &rt2400pci_queue_atim, + .lib = &rt2400pci_rt2x00_ops, + .hw = &rt2400pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2400pci_rt2x00debug, + .debugfs = &rt2400pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h index ccd644104ad1..c3dea697b907 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.h +++ b/drivers/net/wireless/rt2x00/rt2400pci.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -35,7 +35,7 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 100 diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 2e872ac69826..408fcfc120f5 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -1505,6 +1505,7 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, CSR0, ®); rt2x00_set_chip_rf(rt2x00dev, value, reg); + rt2x00_print_chip(rt2x00dev); if (!rt2x00_rf(&rt2x00dev->chip, RF2522) && !rt2x00_rf(&rt2x00dev->chip, RF2523) && @@ -1732,8 +1733,6 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; - rt2x00dev->hw->extra_tx_headroom = 0; - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, @@ -1921,20 +1920,21 @@ static const struct data_queue_desc rt2500pci_queue_atim = { }; static const struct rt2x00_ops rt2500pci_ops = { - .name = KBUILD_MODNAME, - .max_sta_intf = 1, - .max_ap_intf = 1, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .rx = &rt2500pci_queue_rx, - .tx = &rt2500pci_queue_tx, - .bcn = &rt2500pci_queue_bcn, - .atim = &rt2500pci_queue_atim, - .lib = &rt2500pci_rt2x00_ops, - .hw = &rt2500pci_mac80211_ops, + .name = KBUILD_MODNAME, + .max_sta_intf = 1, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .extra_tx_headroom = 0, + .rx = &rt2500pci_queue_rx, + .tx = &rt2500pci_queue_tx, + .bcn = &rt2500pci_queue_bcn, + .atim = &rt2500pci_queue_atim, + .lib = &rt2500pci_rt2x00_ops, + .hw = &rt2500pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2500pci_rt2x00debug, + .debugfs = &rt2500pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h index 54d37957883c..c6bd1fcae7eb 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.h +++ b/drivers/net/wireless/rt2x00/rt2500pci.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -46,7 +46,7 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 121 diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 22dd6d9e2981..83f2592c59de 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -716,139 +716,6 @@ static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev, } /* - * NOTE: This function is directly ported from legacy driver, but - * despite it being declared it was never called. Although link tuning - * sounds like a good idea, and usually works well for the other drivers, - * it does _not_ work with rt2500usb. Enabling this function will result - * in TX capabilities only until association kicks in. Immediately - * after the successful association all TX frames will be kept in the - * hardware queue and never transmitted. - */ -#if 0 -static void rt2500usb_link_tuner(struct rt2x00_dev *rt2x00dev) -{ - int rssi = rt2x00_get_link_rssi(&rt2x00dev->link); - u16 bbp_thresh; - u16 vgc_bound; - u16 sens; - u16 r24; - u16 r25; - u16 r61; - u16 r17_sens; - u8 r17; - u8 up_bound; - u8 low_bound; - - /* - * Read current r17 value, as well as the sensitivity values - * for the r17 register. - */ - rt2500usb_bbp_read(rt2x00dev, 17, &r17); - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &r17_sens); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &vgc_bound); - up_bound = rt2x00_get_field16(vgc_bound, EEPROM_BBPTUNE_VGCUPPER); - low_bound = rt2x00_get_field16(vgc_bound, EEPROM_BBPTUNE_VGCLOWER); - - /* - * If we are not associated, we should go straight to the - * dynamic CCA tuning. - */ - if (!rt2x00dev->intf_associated) - goto dynamic_cca_tune; - - /* - * Determine the BBP tuning threshold and correctly - * set BBP 24, 25 and 61. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &bbp_thresh); - bbp_thresh = rt2x00_get_field16(bbp_thresh, EEPROM_BBPTUNE_THRESHOLD); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &r24); - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &r25); - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &r61); - - if ((rssi + bbp_thresh) > 0) { - r24 = rt2x00_get_field16(r24, EEPROM_BBPTUNE_R24_HIGH); - r25 = rt2x00_get_field16(r25, EEPROM_BBPTUNE_R25_HIGH); - r61 = rt2x00_get_field16(r61, EEPROM_BBPTUNE_R61_HIGH); - } else { - r24 = rt2x00_get_field16(r24, EEPROM_BBPTUNE_R24_LOW); - r25 = rt2x00_get_field16(r25, EEPROM_BBPTUNE_R25_LOW); - r61 = rt2x00_get_field16(r61, EEPROM_BBPTUNE_R61_LOW); - } - - rt2500usb_bbp_write(rt2x00dev, 24, r24); - rt2500usb_bbp_write(rt2x00dev, 25, r25); - rt2500usb_bbp_write(rt2x00dev, 61, r61); - - /* - * A too low RSSI will cause too much false CCA which will - * then corrupt the R17 tuning. To remidy this the tuning should - * be stopped (While making sure the R17 value will not exceed limits) - */ - if (rssi >= -40) { - if (r17 != 0x60) - rt2500usb_bbp_write(rt2x00dev, 17, 0x60); - return; - } - - /* - * Special big-R17 for short distance - */ - if (rssi >= -58) { - sens = rt2x00_get_field16(r17_sens, EEPROM_BBPTUNE_R17_LOW); - if (r17 != sens) - rt2500usb_bbp_write(rt2x00dev, 17, sens); - return; - } - - /* - * Special mid-R17 for middle distance - */ - if (rssi >= -74) { - sens = rt2x00_get_field16(r17_sens, EEPROM_BBPTUNE_R17_HIGH); - if (r17 != sens) - rt2500usb_bbp_write(rt2x00dev, 17, sens); - return; - } - - /* - * Leave short or middle distance condition, restore r17 - * to the dynamic tuning range. - */ - low_bound = 0x32; - if (rssi < -77) - up_bound -= (-77 - rssi); - - if (up_bound < low_bound) - up_bound = low_bound; - - if (r17 > up_bound) { - rt2500usb_bbp_write(rt2x00dev, 17, up_bound); - rt2x00dev->link.vgc_level = up_bound; - return; - } - -dynamic_cca_tune: - - /* - * R17 is inside the dynamic tuning range, - * start tuning the link based on the false cca counter. - */ - if (rt2x00dev->link.qual.false_cca > 512 && r17 < up_bound) { - rt2500usb_bbp_write(rt2x00dev, 17, ++r17); - rt2x00dev->link.vgc_level = r17; - } else if (rt2x00dev->link.qual.false_cca < 100 && r17 > low_bound) { - rt2500usb_bbp_write(rt2x00dev, 17, --r17); - rt2x00dev->link.vgc_level = r17; - } -} -#else -#define rt2500usb_link_tuner NULL -#endif - -/* * Initialization functions. */ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) @@ -1542,6 +1409,7 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2570, value, reg); + rt2x00_print_chip(rt2x00dev); if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0) || rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) { @@ -1788,8 +1656,6 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; - rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, @@ -1910,7 +1776,6 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { .rfkill_poll = rt2500usb_rfkill_poll, .link_stats = rt2500usb_link_stats, .reset_tuner = rt2500usb_reset_tuner, - .link_tuner = rt2500usb_link_tuner, .write_tx_desc = rt2500usb_write_tx_desc, .write_tx_data = rt2x00usb_write_tx_data, .write_beacon = rt2500usb_write_beacon, @@ -1956,20 +1821,21 @@ static const struct data_queue_desc rt2500usb_queue_atim = { }; static const struct rt2x00_ops rt2500usb_ops = { - .name = KBUILD_MODNAME, - .max_sta_intf = 1, - .max_ap_intf = 1, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .rx = &rt2500usb_queue_rx, - .tx = &rt2500usb_queue_tx, - .bcn = &rt2500usb_queue_bcn, - .atim = &rt2500usb_queue_atim, - .lib = &rt2500usb_rt2x00_ops, - .hw = &rt2500usb_mac80211_ops, + .name = KBUILD_MODNAME, + .max_sta_intf = 1, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .extra_tx_headroom = TXD_DESC_SIZE, + .rx = &rt2500usb_queue_rx, + .tx = &rt2500usb_queue_tx, + .bcn = &rt2500usb_queue_bcn, + .atim = &rt2500usb_queue_atim, + .lib = &rt2500usb_rt2x00_ops, + .hw = &rt2500usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2500usb_rt2x00debug, + .debugfs = &rt2500usb_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h index b01edca42583..b493306a7eed 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.h +++ b/drivers/net/wireless/rt2x00/rt2500usb.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -46,7 +46,7 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 120 diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h new file mode 100644 index 000000000000..c5fe867665e6 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -0,0 +1,1852 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + <http://rt2x00.serialmonkey.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Module: rt2800 + Abstract: Data structures and registers for the rt2800 modules. + Supported chipsets: RT2800E, RT2800ED & RT2800U. + */ + +#ifndef RT2800_H +#define RT2800_H + +/* + * RF chip defines. + * + * RF2820 2.4G 2T3R + * RF2850 2.4G/5G 2T3R + * RF2720 2.4G 1T2R + * RF2750 2.4G/5G 1T2R + * RF3020 2.4G 1T1R + * RF2020 2.4G B/G + * RF3021 2.4G 1T2R + * RF3022 2.4G 2T2R + * RF3052 2.4G 2T2R + */ +#define RF2820 0x0001 +#define RF2850 0x0002 +#define RF2720 0x0003 +#define RF2750 0x0004 +#define RF3020 0x0005 +#define RF2020 0x0006 +#define RF3021 0x0007 +#define RF3022 0x0008 +#define RF3052 0x0009 + +/* + * Chipset version. + */ +#define RT2860C_VERSION 0x28600100 +#define RT2860D_VERSION 0x28600101 +#define RT2880E_VERSION 0x28720200 +#define RT2883_VERSION 0x28830300 +#define RT3070_VERSION 0x30700200 + +/* + * Signal information. + * Default offset is required for RSSI <-> dBm conversion. + */ +#define DEFAULT_RSSI_OFFSET 120 /* FIXME */ + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x1000 +#define CSR_REG_SIZE 0x0800 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0110 +#define BBP_BASE 0x0000 +#define BBP_SIZE 0x0080 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 4 + +/* + * USB registers. + */ + +/* + * INT_SOURCE_CSR: Interrupt source register. + * Write one to clear corresponding bit. + * TX_FIFO_STATUS: FIFO Statistics is full, sw should read 0x171c + */ +#define INT_SOURCE_CSR 0x0200 +#define INT_SOURCE_CSR_RXDELAYINT FIELD32(0x00000001) +#define INT_SOURCE_CSR_TXDELAYINT FIELD32(0x00000002) +#define INT_SOURCE_CSR_RX_DONE FIELD32(0x00000004) +#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00000008) +#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00000010) +#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00000020) +#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00000040) +#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00000080) +#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00000100) +#define INT_SOURCE_CSR_MCU_COMMAND FIELD32(0x00000200) +#define INT_SOURCE_CSR_RXTX_COHERENT FIELD32(0x00000400) +#define INT_SOURCE_CSR_TBTT FIELD32(0x00000800) +#define INT_SOURCE_CSR_PRE_TBTT FIELD32(0x00001000) +#define INT_SOURCE_CSR_TX_FIFO_STATUS FIELD32(0x00002000) +#define INT_SOURCE_CSR_AUTO_WAKEUP FIELD32(0x00004000) +#define INT_SOURCE_CSR_GPTIMER FIELD32(0x00008000) +#define INT_SOURCE_CSR_RX_COHERENT FIELD32(0x00010000) +#define INT_SOURCE_CSR_TX_COHERENT FIELD32(0x00020000) + +/* + * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. + */ +#define INT_MASK_CSR 0x0204 +#define INT_MASK_CSR_RXDELAYINT FIELD32(0x00000001) +#define INT_MASK_CSR_TXDELAYINT FIELD32(0x00000002) +#define INT_MASK_CSR_RX_DONE FIELD32(0x00000004) +#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00000008) +#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00000010) +#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00000020) +#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00000040) +#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00000080) +#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00000100) +#define INT_MASK_CSR_MCU_COMMAND FIELD32(0x00000200) +#define INT_MASK_CSR_RXTX_COHERENT FIELD32(0x00000400) +#define INT_MASK_CSR_TBTT FIELD32(0x00000800) +#define INT_MASK_CSR_PRE_TBTT FIELD32(0x00001000) +#define INT_MASK_CSR_TX_FIFO_STATUS FIELD32(0x00002000) +#define INT_MASK_CSR_AUTO_WAKEUP FIELD32(0x00004000) +#define INT_MASK_CSR_GPTIMER FIELD32(0x00008000) +#define INT_MASK_CSR_RX_COHERENT FIELD32(0x00010000) +#define INT_MASK_CSR_TX_COHERENT FIELD32(0x00020000) + +/* + * WPDMA_GLO_CFG + */ +#define WPDMA_GLO_CFG 0x0208 +#define WPDMA_GLO_CFG_ENABLE_TX_DMA FIELD32(0x00000001) +#define WPDMA_GLO_CFG_TX_DMA_BUSY FIELD32(0x00000002) +#define WPDMA_GLO_CFG_ENABLE_RX_DMA FIELD32(0x00000004) +#define WPDMA_GLO_CFG_RX_DMA_BUSY FIELD32(0x00000008) +#define WPDMA_GLO_CFG_WP_DMA_BURST_SIZE FIELD32(0x00000030) +#define WPDMA_GLO_CFG_TX_WRITEBACK_DONE FIELD32(0x00000040) +#define WPDMA_GLO_CFG_BIG_ENDIAN FIELD32(0x00000080) +#define WPDMA_GLO_CFG_RX_HDR_SCATTER FIELD32(0x0000ff00) +#define WPDMA_GLO_CFG_HDR_SEG_LEN FIELD32(0xffff0000) + +/* + * WPDMA_RST_IDX + */ +#define WPDMA_RST_IDX 0x020c +#define WPDMA_RST_IDX_DTX_IDX0 FIELD32(0x00000001) +#define WPDMA_RST_IDX_DTX_IDX1 FIELD32(0x00000002) +#define WPDMA_RST_IDX_DTX_IDX2 FIELD32(0x00000004) +#define WPDMA_RST_IDX_DTX_IDX3 FIELD32(0x00000008) +#define WPDMA_RST_IDX_DTX_IDX4 FIELD32(0x00000010) +#define WPDMA_RST_IDX_DTX_IDX5 FIELD32(0x00000020) +#define WPDMA_RST_IDX_DRX_IDX0 FIELD32(0x00010000) + +/* + * DELAY_INT_CFG + */ +#define DELAY_INT_CFG 0x0210 +#define DELAY_INT_CFG_RXMAX_PTIME FIELD32(0x000000ff) +#define DELAY_INT_CFG_RXMAX_PINT FIELD32(0x00007f00) +#define DELAY_INT_CFG_RXDLY_INT_EN FIELD32(0x00008000) +#define DELAY_INT_CFG_TXMAX_PTIME FIELD32(0x00ff0000) +#define DELAY_INT_CFG_TXMAX_PINT FIELD32(0x7f000000) +#define DELAY_INT_CFG_TXDLY_INT_EN FIELD32(0x80000000) + +/* + * WMM_AIFSN_CFG: Aifsn for each EDCA AC + * AIFSN0: AC_BE + * AIFSN1: AC_BK + * AIFSN2: AC_VI + * AIFSN3: AC_VO + */ +#define WMM_AIFSN_CFG 0x0214 +#define WMM_AIFSN_CFG_AIFSN0 FIELD32(0x0000000f) +#define WMM_AIFSN_CFG_AIFSN1 FIELD32(0x000000f0) +#define WMM_AIFSN_CFG_AIFSN2 FIELD32(0x00000f00) +#define WMM_AIFSN_CFG_AIFSN3 FIELD32(0x0000f000) + +/* + * WMM_CWMIN_CSR: CWmin for each EDCA AC + * CWMIN0: AC_BE + * CWMIN1: AC_BK + * CWMIN2: AC_VI + * CWMIN3: AC_VO + */ +#define WMM_CWMIN_CFG 0x0218 +#define WMM_CWMIN_CFG_CWMIN0 FIELD32(0x0000000f) +#define WMM_CWMIN_CFG_CWMIN1 FIELD32(0x000000f0) +#define WMM_CWMIN_CFG_CWMIN2 FIELD32(0x00000f00) +#define WMM_CWMIN_CFG_CWMIN3 FIELD32(0x0000f000) + +/* + * WMM_CWMAX_CSR: CWmax for each EDCA AC + * CWMAX0: AC_BE + * CWMAX1: AC_BK + * CWMAX2: AC_VI + * CWMAX3: AC_VO + */ +#define WMM_CWMAX_CFG 0x021c +#define WMM_CWMAX_CFG_CWMAX0 FIELD32(0x0000000f) +#define WMM_CWMAX_CFG_CWMAX1 FIELD32(0x000000f0) +#define WMM_CWMAX_CFG_CWMAX2 FIELD32(0x00000f00) +#define WMM_CWMAX_CFG_CWMAX3 FIELD32(0x0000f000) + +/* + * AC_TXOP0: AC_BK/AC_BE TXOP register + * AC0TXOP: AC_BK in unit of 32us + * AC1TXOP: AC_BE in unit of 32us + */ +#define WMM_TXOP0_CFG 0x0220 +#define WMM_TXOP0_CFG_AC0TXOP FIELD32(0x0000ffff) +#define WMM_TXOP0_CFG_AC1TXOP FIELD32(0xffff0000) + +/* + * AC_TXOP1: AC_VO/AC_VI TXOP register + * AC2TXOP: AC_VI in unit of 32us + * AC3TXOP: AC_VO in unit of 32us + */ +#define WMM_TXOP1_CFG 0x0224 +#define WMM_TXOP1_CFG_AC2TXOP FIELD32(0x0000ffff) +#define WMM_TXOP1_CFG_AC3TXOP FIELD32(0xffff0000) + +/* + * GPIO_CTRL_CFG: + */ +#define GPIO_CTRL_CFG 0x0228 +#define GPIO_CTRL_CFG_BIT0 FIELD32(0x00000001) +#define GPIO_CTRL_CFG_BIT1 FIELD32(0x00000002) +#define GPIO_CTRL_CFG_BIT2 FIELD32(0x00000004) +#define GPIO_CTRL_CFG_BIT3 FIELD32(0x00000008) +#define GPIO_CTRL_CFG_BIT4 FIELD32(0x00000010) +#define GPIO_CTRL_CFG_BIT5 FIELD32(0x00000020) +#define GPIO_CTRL_CFG_BIT6 FIELD32(0x00000040) +#define GPIO_CTRL_CFG_BIT7 FIELD32(0x00000080) +#define GPIO_CTRL_CFG_BIT8 FIELD32(0x00000100) + +/* + * MCU_CMD_CFG + */ +#define MCU_CMD_CFG 0x022c + +/* + * AC_BK register offsets + */ +#define TX_BASE_PTR0 0x0230 +#define TX_MAX_CNT0 0x0234 +#define TX_CTX_IDX0 0x0238 +#define TX_DTX_IDX0 0x023c + +/* + * AC_BE register offsets + */ +#define TX_BASE_PTR1 0x0240 +#define TX_MAX_CNT1 0x0244 +#define TX_CTX_IDX1 0x0248 +#define TX_DTX_IDX1 0x024c + +/* + * AC_VI register offsets + */ +#define TX_BASE_PTR2 0x0250 +#define TX_MAX_CNT2 0x0254 +#define TX_CTX_IDX2 0x0258 +#define TX_DTX_IDX2 0x025c + +/* + * AC_VO register offsets + */ +#define TX_BASE_PTR3 0x0260 +#define TX_MAX_CNT3 0x0264 +#define TX_CTX_IDX3 0x0268 +#define TX_DTX_IDX3 0x026c + +/* + * HCCA register offsets + */ +#define TX_BASE_PTR4 0x0270 +#define TX_MAX_CNT4 0x0274 +#define TX_CTX_IDX4 0x0278 +#define TX_DTX_IDX4 0x027c + +/* + * MGMT register offsets + */ +#define TX_BASE_PTR5 0x0280 +#define TX_MAX_CNT5 0x0284 +#define TX_CTX_IDX5 0x0288 +#define TX_DTX_IDX5 0x028c + +/* + * RX register offsets + */ +#define RX_BASE_PTR 0x0290 +#define RX_MAX_CNT 0x0294 +#define RX_CRX_IDX 0x0298 +#define RX_DRX_IDX 0x029c + +/* + * PBF_SYS_CTRL + * HOST_RAM_WRITE: enable Host program ram write selection + */ +#define PBF_SYS_CTRL 0x0400 +#define PBF_SYS_CTRL_READY FIELD32(0x00000080) +#define PBF_SYS_CTRL_HOST_RAM_WRITE FIELD32(0x00010000) + +/* + * HOST-MCU shared memory + */ +#define HOST_CMD_CSR 0x0404 +#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x000000ff) + +/* + * PBF registers + * Most are for debug. Driver doesn't touch PBF register. + */ +#define PBF_CFG 0x0408 +#define PBF_MAX_PCNT 0x040c +#define PBF_CTRL 0x0410 +#define PBF_INT_STA 0x0414 +#define PBF_INT_ENA 0x0418 + +/* + * BCN_OFFSET0: + */ +#define BCN_OFFSET0 0x042c +#define BCN_OFFSET0_BCN0 FIELD32(0x000000ff) +#define BCN_OFFSET0_BCN1 FIELD32(0x0000ff00) +#define BCN_OFFSET0_BCN2 FIELD32(0x00ff0000) +#define BCN_OFFSET0_BCN3 FIELD32(0xff000000) + +/* + * BCN_OFFSET1: + */ +#define BCN_OFFSET1 0x0430 +#define BCN_OFFSET1_BCN4 FIELD32(0x000000ff) +#define BCN_OFFSET1_BCN5 FIELD32(0x0000ff00) +#define BCN_OFFSET1_BCN6 FIELD32(0x00ff0000) +#define BCN_OFFSET1_BCN7 FIELD32(0xff000000) + +/* + * PBF registers + * Most are for debug. Driver doesn't touch PBF register. + */ +#define TXRXQ_PCNT 0x0438 +#define PBF_DBG 0x043c + +/* + * RF registers + */ +#define RF_CSR_CFG 0x0500 +#define RF_CSR_CFG_DATA FIELD32(0x000000ff) +#define RF_CSR_CFG_REGNUM FIELD32(0x00001f00) +#define RF_CSR_CFG_WRITE FIELD32(0x00010000) +#define RF_CSR_CFG_BUSY FIELD32(0x00020000) + +/* + * EFUSE_CSR: RT30x0 EEPROM + */ +#define EFUSE_CTRL 0x0580 +#define EFUSE_CTRL_ADDRESS_IN FIELD32(0x03fe0000) +#define EFUSE_CTRL_MODE FIELD32(0x000000c0) +#define EFUSE_CTRL_KICK FIELD32(0x40000000) +#define EFUSE_CTRL_PRESENT FIELD32(0x80000000) + +/* + * EFUSE_DATA0 + */ +#define EFUSE_DATA0 0x0590 + +/* + * EFUSE_DATA1 + */ +#define EFUSE_DATA1 0x0594 + +/* + * EFUSE_DATA2 + */ +#define EFUSE_DATA2 0x0598 + +/* + * EFUSE_DATA3 + */ +#define EFUSE_DATA3 0x059c + +/* + * MAC Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + * ASIC_REV: 0 + * ASIC_VER: 2860 or 2870 + */ +#define MAC_CSR0 0x1000 +#define MAC_CSR0_ASIC_REV FIELD32(0x0000ffff) +#define MAC_CSR0_ASIC_VER FIELD32(0xffff0000) + +/* + * MAC_SYS_CTRL: + */ +#define MAC_SYS_CTRL 0x1004 +#define MAC_SYS_CTRL_RESET_CSR FIELD32(0x00000001) +#define MAC_SYS_CTRL_RESET_BBP FIELD32(0x00000002) +#define MAC_SYS_CTRL_ENABLE_TX FIELD32(0x00000004) +#define MAC_SYS_CTRL_ENABLE_RX FIELD32(0x00000008) +#define MAC_SYS_CTRL_CONTINUOUS_TX FIELD32(0x00000010) +#define MAC_SYS_CTRL_LOOPBACK FIELD32(0x00000020) +#define MAC_SYS_CTRL_WLAN_HALT FIELD32(0x00000040) +#define MAC_SYS_CTRL_RX_TIMESTAMP FIELD32(0x00000080) + +/* + * MAC_ADDR_DW0: STA MAC register 0 + */ +#define MAC_ADDR_DW0 0x1008 +#define MAC_ADDR_DW0_BYTE0 FIELD32(0x000000ff) +#define MAC_ADDR_DW0_BYTE1 FIELD32(0x0000ff00) +#define MAC_ADDR_DW0_BYTE2 FIELD32(0x00ff0000) +#define MAC_ADDR_DW0_BYTE3 FIELD32(0xff000000) + +/* + * MAC_ADDR_DW1: STA MAC register 1 + * UNICAST_TO_ME_MASK: + * Used to mask off bits from byte 5 of the MAC address + * to determine the UNICAST_TO_ME bit for RX frames. + * The full mask is complemented by BSS_ID_MASK: + * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK + */ +#define MAC_ADDR_DW1 0x100c +#define MAC_ADDR_DW1_BYTE4 FIELD32(0x000000ff) +#define MAC_ADDR_DW1_BYTE5 FIELD32(0x0000ff00) +#define MAC_ADDR_DW1_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) + +/* + * MAC_BSSID_DW0: BSSID register 0 + */ +#define MAC_BSSID_DW0 0x1010 +#define MAC_BSSID_DW0_BYTE0 FIELD32(0x000000ff) +#define MAC_BSSID_DW0_BYTE1 FIELD32(0x0000ff00) +#define MAC_BSSID_DW0_BYTE2 FIELD32(0x00ff0000) +#define MAC_BSSID_DW0_BYTE3 FIELD32(0xff000000) + +/* + * MAC_BSSID_DW1: BSSID register 1 + * BSS_ID_MASK: + * 0: 1-BSSID mode (BSS index = 0) + * 1: 2-BSSID mode (BSS index: Byte5, bit 0) + * 2: 4-BSSID mode (BSS index: byte5, bit 0 - 1) + * 3: 8-BSSID mode (BSS index: byte5, bit 0 - 2) + * This mask is used to mask off bits 0, 1 and 2 of byte 5 of the + * BSSID. This will make sure that those bits will be ignored + * when determining the MY_BSS of RX frames. + */ +#define MAC_BSSID_DW1 0x1014 +#define MAC_BSSID_DW1_BYTE4 FIELD32(0x000000ff) +#define MAC_BSSID_DW1_BYTE5 FIELD32(0x0000ff00) +#define MAC_BSSID_DW1_BSS_ID_MASK FIELD32(0x00030000) +#define MAC_BSSID_DW1_BSS_BCN_NUM FIELD32(0x001c0000) + +/* + * MAX_LEN_CFG: Maximum frame length register. + * MAX_MPDU: rt2860b max 16k bytes + * MAX_PSDU: Maximum PSDU length + * (power factor) 0:2^13, 1:2^14, 2:2^15, 3:2^16 + */ +#define MAX_LEN_CFG 0x1018 +#define MAX_LEN_CFG_MAX_MPDU FIELD32(0x00000fff) +#define MAX_LEN_CFG_MAX_PSDU FIELD32(0x00003000) +#define MAX_LEN_CFG_MIN_PSDU FIELD32(0x0000c000) +#define MAX_LEN_CFG_MIN_MPDU FIELD32(0x000f0000) + +/* + * BBP_CSR_CFG: BBP serial control register + * VALUE: Register value to program into BBP + * REG_NUM: Selected BBP register + * READ_CONTROL: 0 write BBP, 1 read BBP + * BUSY: ASIC is busy executing BBP commands + * BBP_PAR_DUR: 0 4 MAC clocks, 1 8 MAC clocks + * BBP_RW_MODE: 0 serial, 1 paralell + */ +#define BBP_CSR_CFG 0x101c +#define BBP_CSR_CFG_VALUE FIELD32(0x000000ff) +#define BBP_CSR_CFG_REGNUM FIELD32(0x0000ff00) +#define BBP_CSR_CFG_READ_CONTROL FIELD32(0x00010000) +#define BBP_CSR_CFG_BUSY FIELD32(0x00020000) +#define BBP_CSR_CFG_BBP_PAR_DUR FIELD32(0x00040000) +#define BBP_CSR_CFG_BBP_RW_MODE FIELD32(0x00080000) + +/* + * RF_CSR_CFG0: RF control register + * REGID_AND_VALUE: Register value to program into RF + * BITWIDTH: Selected RF register + * STANDBYMODE: 0 high when standby, 1 low when standby + * SEL: 0 RF_LE0 activate, 1 RF_LE1 activate + * BUSY: ASIC is busy executing RF commands + */ +#define RF_CSR_CFG0 0x1020 +#define RF_CSR_CFG0_REGID_AND_VALUE FIELD32(0x00ffffff) +#define RF_CSR_CFG0_BITWIDTH FIELD32(0x1f000000) +#define RF_CSR_CFG0_REG_VALUE_BW FIELD32(0x1fffffff) +#define RF_CSR_CFG0_STANDBYMODE FIELD32(0x20000000) +#define RF_CSR_CFG0_SEL FIELD32(0x40000000) +#define RF_CSR_CFG0_BUSY FIELD32(0x80000000) + +/* + * RF_CSR_CFG1: RF control register + * REGID_AND_VALUE: Register value to program into RF + * RFGAP: Gap between BB_CONTROL_RF and RF_LE + * 0: 3 system clock cycle (37.5usec) + * 1: 5 system clock cycle (62.5usec) + */ +#define RF_CSR_CFG1 0x1024 +#define RF_CSR_CFG1_REGID_AND_VALUE FIELD32(0x00ffffff) +#define RF_CSR_CFG1_RFGAP FIELD32(0x1f000000) + +/* + * RF_CSR_CFG2: RF control register + * VALUE: Register value to program into RF + */ +#define RF_CSR_CFG2 0x1028 +#define RF_CSR_CFG2_VALUE FIELD32(0x00ffffff) + +/* + * LED_CFG: LED control + * color LED's: + * 0: off + * 1: blinking upon TX2 + * 2: periodic slow blinking + * 3: always on + * LED polarity: + * 0: active low + * 1: active high + */ +#define LED_CFG 0x102c +#define LED_CFG_ON_PERIOD FIELD32(0x000000ff) +#define LED_CFG_OFF_PERIOD FIELD32(0x0000ff00) +#define LED_CFG_SLOW_BLINK_PERIOD FIELD32(0x003f0000) +#define LED_CFG_R_LED_MODE FIELD32(0x03000000) +#define LED_CFG_G_LED_MODE FIELD32(0x0c000000) +#define LED_CFG_Y_LED_MODE FIELD32(0x30000000) +#define LED_CFG_LED_POLAR FIELD32(0x40000000) + +/* + * XIFS_TIME_CFG: MAC timing + * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX + * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX + * OFDM_XIFS_TIME: unit 1us. Applied after OFDM RX + * when MAC doesn't reference BBP signal BBRXEND + * EIFS: unit 1us + * BB_RXEND_ENABLE: reference RXEND signal to begin XIFS defer + * + */ +#define XIFS_TIME_CFG 0x1100 +#define XIFS_TIME_CFG_CCKM_SIFS_TIME FIELD32(0x000000ff) +#define XIFS_TIME_CFG_OFDM_SIFS_TIME FIELD32(0x0000ff00) +#define XIFS_TIME_CFG_OFDM_XIFS_TIME FIELD32(0x000f0000) +#define XIFS_TIME_CFG_EIFS FIELD32(0x1ff00000) +#define XIFS_TIME_CFG_BB_RXEND_ENABLE FIELD32(0x20000000) + +/* + * BKOFF_SLOT_CFG: + */ +#define BKOFF_SLOT_CFG 0x1104 +#define BKOFF_SLOT_CFG_SLOT_TIME FIELD32(0x000000ff) +#define BKOFF_SLOT_CFG_CC_DELAY_TIME FIELD32(0x0000ff00) + +/* + * NAV_TIME_CFG: + */ +#define NAV_TIME_CFG 0x1108 +#define NAV_TIME_CFG_SIFS FIELD32(0x000000ff) +#define NAV_TIME_CFG_SLOT_TIME FIELD32(0x0000ff00) +#define NAV_TIME_CFG_EIFS FIELD32(0x01ff0000) +#define NAV_TIME_ZERO_SIFS FIELD32(0x02000000) + +/* + * CH_TIME_CFG: count as channel busy + */ +#define CH_TIME_CFG 0x110c + +/* + * PBF_LIFE_TIMER: TX/RX MPDU timestamp timer (free run) Unit: 1us + */ +#define PBF_LIFE_TIMER 0x1110 + +/* + * BCN_TIME_CFG: + * BEACON_INTERVAL: in unit of 1/16 TU + * TSF_TICKING: Enable TSF auto counting + * TSF_SYNC: Enable TSF sync, 00: disable, 01: infra mode, 10: ad-hoc mode + * BEACON_GEN: Enable beacon generator + */ +#define BCN_TIME_CFG 0x1114 +#define BCN_TIME_CFG_BEACON_INTERVAL FIELD32(0x0000ffff) +#define BCN_TIME_CFG_TSF_TICKING FIELD32(0x00010000) +#define BCN_TIME_CFG_TSF_SYNC FIELD32(0x00060000) +#define BCN_TIME_CFG_TBTT_ENABLE FIELD32(0x00080000) +#define BCN_TIME_CFG_BEACON_GEN FIELD32(0x00100000) +#define BCN_TIME_CFG_TX_TIME_COMPENSATE FIELD32(0xf0000000) + +/* + * TBTT_SYNC_CFG: + */ +#define TBTT_SYNC_CFG 0x1118 + +/* + * TSF_TIMER_DW0: Local lsb TSF timer, read-only + */ +#define TSF_TIMER_DW0 0x111c +#define TSF_TIMER_DW0_LOW_WORD FIELD32(0xffffffff) + +/* + * TSF_TIMER_DW1: Local msb TSF timer, read-only + */ +#define TSF_TIMER_DW1 0x1120 +#define TSF_TIMER_DW1_HIGH_WORD FIELD32(0xffffffff) + +/* + * TBTT_TIMER: TImer remains till next TBTT, read-only + */ +#define TBTT_TIMER 0x1124 + +/* + * INT_TIMER_CFG: + */ +#define INT_TIMER_CFG 0x1128 + +/* + * INT_TIMER_EN: GP-timer and pre-tbtt Int enable + */ +#define INT_TIMER_EN 0x112c + +/* + * CH_IDLE_STA: channel idle time + */ +#define CH_IDLE_STA 0x1130 + +/* + * CH_BUSY_STA: channel busy time + */ +#define CH_BUSY_STA 0x1134 + +/* + * MAC_STATUS_CFG: + * BBP_RF_BUSY: When set to 0, BBP and RF are stable. + * if 1 or higher one of the 2 registers is busy. + */ +#define MAC_STATUS_CFG 0x1200 +#define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003) + +/* + * PWR_PIN_CFG: + */ +#define PWR_PIN_CFG 0x1204 + +/* + * AUTOWAKEUP_CFG: Manual power control / status register + * TBCN_BEFORE_WAKE: ForceWake has high privilege than PutToSleep when both set + * AUTOWAKE: 0:sleep, 1:awake + */ +#define AUTOWAKEUP_CFG 0x1208 +#define AUTOWAKEUP_CFG_AUTO_LEAD_TIME FIELD32(0x000000ff) +#define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00) +#define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000) + +/* + * EDCA_AC0_CFG: + */ +#define EDCA_AC0_CFG 0x1300 +#define EDCA_AC0_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC0_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC0_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC0_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC1_CFG: + */ +#define EDCA_AC1_CFG 0x1304 +#define EDCA_AC1_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC1_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC1_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC1_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC2_CFG: + */ +#define EDCA_AC2_CFG 0x1308 +#define EDCA_AC2_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC2_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC2_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC2_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC3_CFG: + */ +#define EDCA_AC3_CFG 0x130c +#define EDCA_AC3_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC3_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC3_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC3_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_TID_AC_MAP: + */ +#define EDCA_TID_AC_MAP 0x1310 + +/* + * TX_PWR_CFG_0: + */ +#define TX_PWR_CFG_0 0x1314 +#define TX_PWR_CFG_0_1MBS FIELD32(0x0000000f) +#define TX_PWR_CFG_0_2MBS FIELD32(0x000000f0) +#define TX_PWR_CFG_0_55MBS FIELD32(0x00000f00) +#define TX_PWR_CFG_0_11MBS FIELD32(0x0000f000) +#define TX_PWR_CFG_0_6MBS FIELD32(0x000f0000) +#define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000) +#define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000) +#define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_1: + */ +#define TX_PWR_CFG_1 0x1318 +#define TX_PWR_CFG_1_24MBS FIELD32(0x0000000f) +#define TX_PWR_CFG_1_36MBS FIELD32(0x000000f0) +#define TX_PWR_CFG_1_48MBS FIELD32(0x00000f00) +#define TX_PWR_CFG_1_54MBS FIELD32(0x0000f000) +#define TX_PWR_CFG_1_MCS0 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000) +#define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000) +#define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_2: + */ +#define TX_PWR_CFG_2 0x131c +#define TX_PWR_CFG_2_MCS4 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_MCS5 FIELD32(0x000000f0) +#define TX_PWR_CFG_2_MCS6 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_MCS7 FIELD32(0x0000f000) +#define TX_PWR_CFG_2_MCS8 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000) +#define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000) +#define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_3: + */ +#define TX_PWR_CFG_3 0x1320 +#define TX_PWR_CFG_3_MCS12 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_MCS13 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_MCS14 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_MCS15 FIELD32(0x0000f000) +#define TX_PWR_CFG_3_UKNOWN1 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000) +#define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000) +#define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_4: + */ +#define TX_PWR_CFG_4 0x1324 +#define TX_PWR_CFG_4_UKNOWN5 FIELD32(0x0000000f) +#define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0) +#define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00) +#define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000) + +/* + * TX_PIN_CFG: + */ +#define TX_PIN_CFG 0x1328 +#define TX_PIN_CFG_PA_PE_A0_EN FIELD32(0x00000001) +#define TX_PIN_CFG_PA_PE_G0_EN FIELD32(0x00000002) +#define TX_PIN_CFG_PA_PE_A1_EN FIELD32(0x00000004) +#define TX_PIN_CFG_PA_PE_G1_EN FIELD32(0x00000008) +#define TX_PIN_CFG_PA_PE_A0_POL FIELD32(0x00000010) +#define TX_PIN_CFG_PA_PE_G0_POL FIELD32(0x00000020) +#define TX_PIN_CFG_PA_PE_A1_POL FIELD32(0x00000040) +#define TX_PIN_CFG_PA_PE_G1_POL FIELD32(0x00000080) +#define TX_PIN_CFG_LNA_PE_A0_EN FIELD32(0x00000100) +#define TX_PIN_CFG_LNA_PE_G0_EN FIELD32(0x00000200) +#define TX_PIN_CFG_LNA_PE_A1_EN FIELD32(0x00000400) +#define TX_PIN_CFG_LNA_PE_G1_EN FIELD32(0x00000800) +#define TX_PIN_CFG_LNA_PE_A0_POL FIELD32(0x00001000) +#define TX_PIN_CFG_LNA_PE_G0_POL FIELD32(0x00002000) +#define TX_PIN_CFG_LNA_PE_A1_POL FIELD32(0x00004000) +#define TX_PIN_CFG_LNA_PE_G1_POL FIELD32(0x00008000) +#define TX_PIN_CFG_RFTR_EN FIELD32(0x00010000) +#define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000) +#define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000) +#define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000) + +/* + * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz + */ +#define TX_BAND_CFG 0x132c +#define TX_BAND_CFG_HT40_PLUS FIELD32(0x00000001) +#define TX_BAND_CFG_A FIELD32(0x00000002) +#define TX_BAND_CFG_BG FIELD32(0x00000004) + +/* + * TX_SW_CFG0: + */ +#define TX_SW_CFG0 0x1330 + +/* + * TX_SW_CFG1: + */ +#define TX_SW_CFG1 0x1334 + +/* + * TX_SW_CFG2: + */ +#define TX_SW_CFG2 0x1338 + +/* + * TXOP_THRES_CFG: + */ +#define TXOP_THRES_CFG 0x133c + +/* + * TXOP_CTRL_CFG: + */ +#define TXOP_CTRL_CFG 0x1340 + +/* + * TX_RTS_CFG: + * RTS_THRES: unit:byte + * RTS_FBK_EN: enable rts rate fallback + */ +#define TX_RTS_CFG 0x1344 +#define TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT FIELD32(0x000000ff) +#define TX_RTS_CFG_RTS_THRES FIELD32(0x00ffff00) +#define TX_RTS_CFG_RTS_FBK_EN FIELD32(0x01000000) + +/* + * TX_TIMEOUT_CFG: + * MPDU_LIFETIME: expiration time = 2^(9+MPDU LIFE TIME) us + * RX_ACK_TIMEOUT: unit:slot. Used for TX procedure + * TX_OP_TIMEOUT: TXOP timeout value for TXOP truncation. + * it is recommended that: + * (SLOT_TIME) > (TX_OP_TIMEOUT) > (RX_ACK_TIMEOUT) + */ +#define TX_TIMEOUT_CFG 0x1348 +#define TX_TIMEOUT_CFG_MPDU_LIFETIME FIELD32(0x000000f0) +#define TX_TIMEOUT_CFG_RX_ACK_TIMEOUT FIELD32(0x0000ff00) +#define TX_TIMEOUT_CFG_TX_OP_TIMEOUT FIELD32(0x00ff0000) + +/* + * TX_RTY_CFG: + * SHORT_RTY_LIMIT: short retry limit + * LONG_RTY_LIMIT: long retry limit + * LONG_RTY_THRE: Long retry threshoold + * NON_AGG_RTY_MODE: Non-Aggregate MPDU retry mode + * 0:expired by retry limit, 1: expired by mpdu life timer + * AGG_RTY_MODE: Aggregate MPDU retry mode + * 0:expired by retry limit, 1: expired by mpdu life timer + * TX_AUTO_FB_ENABLE: Tx retry PHY rate auto fallback enable + */ +#define TX_RTY_CFG 0x134c +#define TX_RTY_CFG_SHORT_RTY_LIMIT FIELD32(0x000000ff) +#define TX_RTY_CFG_LONG_RTY_LIMIT FIELD32(0x0000ff00) +#define TX_RTY_CFG_LONG_RTY_THRE FIELD32(0x0fff0000) +#define TX_RTY_CFG_NON_AGG_RTY_MODE FIELD32(0x10000000) +#define TX_RTY_CFG_AGG_RTY_MODE FIELD32(0x20000000) +#define TX_RTY_CFG_TX_AUTO_FB_ENABLE FIELD32(0x40000000) + +/* + * TX_LINK_CFG: + * REMOTE_MFB_LIFETIME: remote MFB life time. unit: 32us + * MFB_ENABLE: TX apply remote MFB 1:enable + * REMOTE_UMFS_ENABLE: remote unsolicit MFB enable + * 0: not apply remote remote unsolicit (MFS=7) + * TX_MRQ_EN: MCS request TX enable + * TX_RDG_EN: RDG TX enable + * TX_CF_ACK_EN: Piggyback CF-ACK enable + * REMOTE_MFB: remote MCS feedback + * REMOTE_MFS: remote MCS feedback sequence number + */ +#define TX_LINK_CFG 0x1350 +#define TX_LINK_CFG_REMOTE_MFB_LIFETIME FIELD32(0x000000ff) +#define TX_LINK_CFG_MFB_ENABLE FIELD32(0x00000100) +#define TX_LINK_CFG_REMOTE_UMFS_ENABLE FIELD32(0x00000200) +#define TX_LINK_CFG_TX_MRQ_EN FIELD32(0x00000400) +#define TX_LINK_CFG_TX_RDG_EN FIELD32(0x00000800) +#define TX_LINK_CFG_TX_CF_ACK_EN FIELD32(0x00001000) +#define TX_LINK_CFG_REMOTE_MFB FIELD32(0x00ff0000) +#define TX_LINK_CFG_REMOTE_MFS FIELD32(0xff000000) + +/* + * HT_FBK_CFG0: + */ +#define HT_FBK_CFG0 0x1354 +#define HT_FBK_CFG0_HTMCS0FBK FIELD32(0x0000000f) +#define HT_FBK_CFG0_HTMCS1FBK FIELD32(0x000000f0) +#define HT_FBK_CFG0_HTMCS2FBK FIELD32(0x00000f00) +#define HT_FBK_CFG0_HTMCS3FBK FIELD32(0x0000f000) +#define HT_FBK_CFG0_HTMCS4FBK FIELD32(0x000f0000) +#define HT_FBK_CFG0_HTMCS5FBK FIELD32(0x00f00000) +#define HT_FBK_CFG0_HTMCS6FBK FIELD32(0x0f000000) +#define HT_FBK_CFG0_HTMCS7FBK FIELD32(0xf0000000) + +/* + * HT_FBK_CFG1: + */ +#define HT_FBK_CFG1 0x1358 +#define HT_FBK_CFG1_HTMCS8FBK FIELD32(0x0000000f) +#define HT_FBK_CFG1_HTMCS9FBK FIELD32(0x000000f0) +#define HT_FBK_CFG1_HTMCS10FBK FIELD32(0x00000f00) +#define HT_FBK_CFG1_HTMCS11FBK FIELD32(0x0000f000) +#define HT_FBK_CFG1_HTMCS12FBK FIELD32(0x000f0000) +#define HT_FBK_CFG1_HTMCS13FBK FIELD32(0x00f00000) +#define HT_FBK_CFG1_HTMCS14FBK FIELD32(0x0f000000) +#define HT_FBK_CFG1_HTMCS15FBK FIELD32(0xf0000000) + +/* + * LG_FBK_CFG0: + */ +#define LG_FBK_CFG0 0x135c +#define LG_FBK_CFG0_OFDMMCS0FBK FIELD32(0x0000000f) +#define LG_FBK_CFG0_OFDMMCS1FBK FIELD32(0x000000f0) +#define LG_FBK_CFG0_OFDMMCS2FBK FIELD32(0x00000f00) +#define LG_FBK_CFG0_OFDMMCS3FBK FIELD32(0x0000f000) +#define LG_FBK_CFG0_OFDMMCS4FBK FIELD32(0x000f0000) +#define LG_FBK_CFG0_OFDMMCS5FBK FIELD32(0x00f00000) +#define LG_FBK_CFG0_OFDMMCS6FBK FIELD32(0x0f000000) +#define LG_FBK_CFG0_OFDMMCS7FBK FIELD32(0xf0000000) + +/* + * LG_FBK_CFG1: + */ +#define LG_FBK_CFG1 0x1360 +#define LG_FBK_CFG0_CCKMCS0FBK FIELD32(0x0000000f) +#define LG_FBK_CFG0_CCKMCS1FBK FIELD32(0x000000f0) +#define LG_FBK_CFG0_CCKMCS2FBK FIELD32(0x00000f00) +#define LG_FBK_CFG0_CCKMCS3FBK FIELD32(0x0000f000) + +/* + * CCK_PROT_CFG: CCK Protection + * PROTECT_RATE: Protection control frame rate for CCK TX(RTS/CTS/CFEnd) + * PROTECT_CTRL: Protection control frame type for CCK TX + * 0:none, 1:RTS/CTS, 2:CTS-to-self + * PROTECT_NAV: TXOP protection type for CCK TX + * 0:none, 1:ShortNAVprotect, 2:LongNAVProtect + * TX_OP_ALLOW_CCK: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_OFDM: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_MM20: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_MM40: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_GF20: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_GF40: CCK TXOP allowance, 0:disallow + * RTS_TH_EN: RTS threshold enable on CCK TX + */ +#define CCK_PROT_CFG 0x1364 +#define CCK_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define CCK_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define CCK_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) +#define CCK_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define CCK_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define CCK_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define CCK_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define CCK_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define CCK_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define CCK_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * OFDM_PROT_CFG: OFDM Protection + */ +#define OFDM_PROT_CFG 0x1368 +#define OFDM_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define OFDM_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define OFDM_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define OFDM_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * MM20_PROT_CFG: MM20 Protection + */ +#define MM20_PROT_CFG 0x136c +#define MM20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define MM20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define MM20_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) +#define MM20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define MM20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define MM20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define MM20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define MM20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define MM20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define MM20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * MM40_PROT_CFG: MM40 Protection + */ +#define MM40_PROT_CFG 0x1370 +#define MM40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define MM40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define MM40_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) +#define MM40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define MM40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define MM40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define MM40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define MM40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define MM40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define MM40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * GF20_PROT_CFG: GF20 Protection + */ +#define GF20_PROT_CFG 0x1374 +#define GF20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define GF20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define GF20_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) +#define GF20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define GF20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define GF20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define GF20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define GF20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define GF20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define GF20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * GF40_PROT_CFG: GF40 Protection + */ +#define GF40_PROT_CFG 0x1378 +#define GF40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define GF40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define GF40_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) +#define GF40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define GF40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define GF40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define GF40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define GF40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define GF40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define GF40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * EXP_CTS_TIME: + */ +#define EXP_CTS_TIME 0x137c + +/* + * EXP_ACK_TIME: + */ +#define EXP_ACK_TIME 0x1380 + +/* + * RX_FILTER_CFG: RX configuration register. + */ +#define RX_FILTER_CFG 0x1400 +#define RX_FILTER_CFG_DROP_CRC_ERROR FIELD32(0x00000001) +#define RX_FILTER_CFG_DROP_PHY_ERROR FIELD32(0x00000002) +#define RX_FILTER_CFG_DROP_NOT_TO_ME FIELD32(0x00000004) +#define RX_FILTER_CFG_DROP_NOT_MY_BSSD FIELD32(0x00000008) +#define RX_FILTER_CFG_DROP_VER_ERROR FIELD32(0x00000010) +#define RX_FILTER_CFG_DROP_MULTICAST FIELD32(0x00000020) +#define RX_FILTER_CFG_DROP_BROADCAST FIELD32(0x00000040) +#define RX_FILTER_CFG_DROP_DUPLICATE FIELD32(0x00000080) +#define RX_FILTER_CFG_DROP_CF_END_ACK FIELD32(0x00000100) +#define RX_FILTER_CFG_DROP_CF_END FIELD32(0x00000200) +#define RX_FILTER_CFG_DROP_ACK FIELD32(0x00000400) +#define RX_FILTER_CFG_DROP_CTS FIELD32(0x00000800) +#define RX_FILTER_CFG_DROP_RTS FIELD32(0x00001000) +#define RX_FILTER_CFG_DROP_PSPOLL FIELD32(0x00002000) +#define RX_FILTER_CFG_DROP_BA FIELD32(0x00004000) +#define RX_FILTER_CFG_DROP_BAR FIELD32(0x00008000) +#define RX_FILTER_CFG_DROP_CNTL FIELD32(0x00010000) + +/* + * AUTO_RSP_CFG: + * AUTORESPONDER: 0: disable, 1: enable + * BAC_ACK_POLICY: 0:long, 1:short preamble + * CTS_40_MMODE: Response CTS 40MHz duplicate mode + * CTS_40_MREF: Response CTS 40MHz duplicate mode + * AR_PREAMBLE: Auto responder preamble 0:long, 1:short preamble + * DUAL_CTS_EN: Power bit value in control frame + * ACK_CTS_PSM_BIT:Power bit value in control frame + */ +#define AUTO_RSP_CFG 0x1404 +#define AUTO_RSP_CFG_AUTORESPONDER FIELD32(0x00000001) +#define AUTO_RSP_CFG_BAC_ACK_POLICY FIELD32(0x00000002) +#define AUTO_RSP_CFG_CTS_40_MMODE FIELD32(0x00000004) +#define AUTO_RSP_CFG_CTS_40_MREF FIELD32(0x00000008) +#define AUTO_RSP_CFG_AR_PREAMBLE FIELD32(0x00000010) +#define AUTO_RSP_CFG_DUAL_CTS_EN FIELD32(0x00000040) +#define AUTO_RSP_CFG_ACK_CTS_PSM_BIT FIELD32(0x00000080) + +/* + * LEGACY_BASIC_RATE: + */ +#define LEGACY_BASIC_RATE 0x1408 + +/* + * HT_BASIC_RATE: + */ +#define HT_BASIC_RATE 0x140c + +/* + * HT_CTRL_CFG: + */ +#define HT_CTRL_CFG 0x1410 + +/* + * SIFS_COST_CFG: + */ +#define SIFS_COST_CFG 0x1414 + +/* + * RX_PARSER_CFG: + * Set NAV for all received frames + */ +#define RX_PARSER_CFG 0x1418 + +/* + * TX_SEC_CNT0: + */ +#define TX_SEC_CNT0 0x1500 + +/* + * RX_SEC_CNT0: + */ +#define RX_SEC_CNT0 0x1504 + +/* + * CCMP_FC_MUTE: + */ +#define CCMP_FC_MUTE 0x1508 + +/* + * TXOP_HLDR_ADDR0: + */ +#define TXOP_HLDR_ADDR0 0x1600 + +/* + * TXOP_HLDR_ADDR1: + */ +#define TXOP_HLDR_ADDR1 0x1604 + +/* + * TXOP_HLDR_ET: + */ +#define TXOP_HLDR_ET 0x1608 + +/* + * QOS_CFPOLL_RA_DW0: + */ +#define QOS_CFPOLL_RA_DW0 0x160c + +/* + * QOS_CFPOLL_RA_DW1: + */ +#define QOS_CFPOLL_RA_DW1 0x1610 + +/* + * QOS_CFPOLL_QC: + */ +#define QOS_CFPOLL_QC 0x1614 + +/* + * RX_STA_CNT0: RX PLCP error count & RX CRC error count + */ +#define RX_STA_CNT0 0x1700 +#define RX_STA_CNT0_CRC_ERR FIELD32(0x0000ffff) +#define RX_STA_CNT0_PHY_ERR FIELD32(0xffff0000) + +/* + * RX_STA_CNT1: RX False CCA count & RX LONG frame count + */ +#define RX_STA_CNT1 0x1704 +#define RX_STA_CNT1_FALSE_CCA FIELD32(0x0000ffff) +#define RX_STA_CNT1_PLCP_ERR FIELD32(0xffff0000) + +/* + * RX_STA_CNT2: + */ +#define RX_STA_CNT2 0x1708 +#define RX_STA_CNT2_RX_DUPLI_COUNT FIELD32(0x0000ffff) +#define RX_STA_CNT2_RX_FIFO_OVERFLOW FIELD32(0xffff0000) + +/* + * TX_STA_CNT0: TX Beacon count + */ +#define TX_STA_CNT0 0x170c +#define TX_STA_CNT0_TX_FAIL_COUNT FIELD32(0x0000ffff) +#define TX_STA_CNT0_TX_BEACON_COUNT FIELD32(0xffff0000) + +/* + * TX_STA_CNT1: TX tx count + */ +#define TX_STA_CNT1 0x1710 +#define TX_STA_CNT1_TX_SUCCESS FIELD32(0x0000ffff) +#define TX_STA_CNT1_TX_RETRANSMIT FIELD32(0xffff0000) + +/* + * TX_STA_CNT2: TX tx count + */ +#define TX_STA_CNT2 0x1714 +#define TX_STA_CNT2_TX_ZERO_LEN_COUNT FIELD32(0x0000ffff) +#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000) + +/* + * TX_STA_FIFO: TX Result for specific PID status fifo register + */ +#define TX_STA_FIFO 0x1718 +#define TX_STA_FIFO_VALID FIELD32(0x00000001) +#define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e) +#define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020) +#define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040) +#define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080) +#define TX_STA_FIFO_WCID FIELD32(0x0000ff00) +#define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000) +#define TX_STA_FIFO_MCS FIELD32(0x007f0000) +#define TX_STA_FIFO_PHYMODE FIELD32(0xc0000000) + +/* + * TX_AGG_CNT: Debug counter + */ +#define TX_AGG_CNT 0x171c +#define TX_AGG_CNT_NON_AGG_TX_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT_AGG_TX_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT0: + */ +#define TX_AGG_CNT0 0x1720 +#define TX_AGG_CNT0_AGG_SIZE_1_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT0_AGG_SIZE_2_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT1: + */ +#define TX_AGG_CNT1 0x1724 +#define TX_AGG_CNT1_AGG_SIZE_3_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT1_AGG_SIZE_4_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT2: + */ +#define TX_AGG_CNT2 0x1728 +#define TX_AGG_CNT2_AGG_SIZE_5_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT2_AGG_SIZE_6_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT3: + */ +#define TX_AGG_CNT3 0x172c +#define TX_AGG_CNT3_AGG_SIZE_7_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT3_AGG_SIZE_8_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT4: + */ +#define TX_AGG_CNT4 0x1730 +#define TX_AGG_CNT4_AGG_SIZE_9_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT4_AGG_SIZE_10_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT5: + */ +#define TX_AGG_CNT5 0x1734 +#define TX_AGG_CNT5_AGG_SIZE_11_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT5_AGG_SIZE_12_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT6: + */ +#define TX_AGG_CNT6 0x1738 +#define TX_AGG_CNT6_AGG_SIZE_13_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT6_AGG_SIZE_14_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT7: + */ +#define TX_AGG_CNT7 0x173c +#define TX_AGG_CNT7_AGG_SIZE_15_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT7_AGG_SIZE_16_COUNT FIELD32(0xffff0000) + +/* + * MPDU_DENSITY_CNT: + * TX_ZERO_DEL: TX zero length delimiter count + * RX_ZERO_DEL: RX zero length delimiter count + */ +#define MPDU_DENSITY_CNT 0x1740 +#define MPDU_DENSITY_CNT_TX_ZERO_DEL FIELD32(0x0000ffff) +#define MPDU_DENSITY_CNT_RX_ZERO_DEL FIELD32(0xffff0000) + +/* + * Security key table memory. + * MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry + * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry + * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry + * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry + * SHARED_KEY_TABLE_BASE: 32-byte * 16-entry + * SHARED_KEY_MODE_BASE: 4-byte * 16-entry + */ +#define MAC_WCID_BASE 0x1800 +#define PAIRWISE_KEY_TABLE_BASE 0x4000 +#define MAC_IVEIV_TABLE_BASE 0x6000 +#define MAC_WCID_ATTRIBUTE_BASE 0x6800 +#define SHARED_KEY_TABLE_BASE 0x6c00 +#define SHARED_KEY_MODE_BASE 0x7000 + +#define MAC_WCID_ENTRY(__idx) \ + ( MAC_WCID_BASE + ((__idx) * sizeof(struct mac_wcid_entry)) ) +#define PAIRWISE_KEY_ENTRY(__idx) \ + ( PAIRWISE_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry)) ) +#define MAC_IVEIV_ENTRY(__idx) \ + ( MAC_IVEIV_TABLE_BASE + ((__idx) & sizeof(struct mac_iveiv_entry)) ) +#define MAC_WCID_ATTR_ENTRY(__idx) \ + ( MAC_WCID_ATTRIBUTE_BASE + ((__idx) * sizeof(u32)) ) +#define SHARED_KEY_ENTRY(__idx) \ + ( SHARED_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry)) ) +#define SHARED_KEY_MODE_ENTRY(__idx) \ + ( SHARED_KEY_MODE_BASE + ((__idx) * sizeof(u32)) ) + +struct mac_wcid_entry { + u8 mac[6]; + u8 reserved[2]; +} __attribute__ ((packed)); + +struct hw_key_entry { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __attribute__ ((packed)); + +struct mac_iveiv_entry { + u8 iv[8]; +} __attribute__ ((packed)); + +/* + * MAC_WCID_ATTRIBUTE: + */ +#define MAC_WCID_ATTRIBUTE_KEYTAB FIELD32(0x00000001) +#define MAC_WCID_ATTRIBUTE_CIPHER FIELD32(0x0000000e) +#define MAC_WCID_ATTRIBUTE_BSS_IDX FIELD32(0x00000070) +#define MAC_WCID_ATTRIBUTE_RX_WIUDF FIELD32(0x00000380) + +/* + * SHARED_KEY_MODE: + */ +#define SHARED_KEY_MODE_BSS0_KEY0 FIELD32(0x00000007) +#define SHARED_KEY_MODE_BSS0_KEY1 FIELD32(0x00000070) +#define SHARED_KEY_MODE_BSS0_KEY2 FIELD32(0x00000700) +#define SHARED_KEY_MODE_BSS0_KEY3 FIELD32(0x00007000) +#define SHARED_KEY_MODE_BSS1_KEY0 FIELD32(0x00070000) +#define SHARED_KEY_MODE_BSS1_KEY1 FIELD32(0x00700000) +#define SHARED_KEY_MODE_BSS1_KEY2 FIELD32(0x07000000) +#define SHARED_KEY_MODE_BSS1_KEY3 FIELD32(0x70000000) + +/* + * HOST-MCU communication + */ + +/* + * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. + */ +#define H2M_MAILBOX_CSR 0x7010 +#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) +#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) +#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) +#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) + +/* + * H2M_MAILBOX_CID: + */ +#define H2M_MAILBOX_CID 0x7014 +#define H2M_MAILBOX_CID_CMD0 FIELD32(0x000000ff) +#define H2M_MAILBOX_CID_CMD1 FIELD32(0x0000ff00) +#define H2M_MAILBOX_CID_CMD2 FIELD32(0x00ff0000) +#define H2M_MAILBOX_CID_CMD3 FIELD32(0xff000000) + +/* + * H2M_MAILBOX_STATUS: + */ +#define H2M_MAILBOX_STATUS 0x701c + +/* + * H2M_INT_SRC: + */ +#define H2M_INT_SRC 0x7024 + +/* + * H2M_BBP_AGENT: + */ +#define H2M_BBP_AGENT 0x7028 + +/* + * MCU_LEDCS: LED control for MCU Mailbox. + */ +#define MCU_LEDCS_LED_MODE FIELD8(0x1f) +#define MCU_LEDCS_POLARITY FIELD8(0x01) + +/* + * HW_CS_CTS_BASE: + * Carrier-sense CTS frame base address. + * It's where mac stores carrier-sense frame for carrier-sense function. + */ +#define HW_CS_CTS_BASE 0x7700 + +/* + * HW_DFS_CTS_BASE: + * DFS CTS frame base address. It's where mac stores CTS frame for DFS. + */ +#define HW_DFS_CTS_BASE 0x7780 + +/* + * TXRX control registers - base address 0x3000 + */ + +/* + * TXRX_CSR1: + * rt2860b UNKNOWN reg use R/O Reg Addr 0x77d0 first.. + */ +#define TXRX_CSR1 0x77d0 + +/* + * HW_DEBUG_SETTING_BASE: + * since NULL frame won't be that long (256 byte) + * We steal 16 tail bytes to save debugging settings + */ +#define HW_DEBUG_SETTING_BASE 0x77f0 +#define HW_DEBUG_SETTING_BASE2 0x7770 + +/* + * HW_BEACON_BASE + * In order to support maximum 8 MBSS and its maximum length + * is 512 bytes for each beacon + * Three section discontinue memory segments will be used. + * 1. The original region for BCN 0~3 + * 2. Extract memory from FCE table for BCN 4~5 + * 3. Extract memory from Pair-wise key table for BCN 6~7 + * It occupied those memory of wcid 238~253 for BCN 6 + * and wcid 222~237 for BCN 7 + * + * IMPORTANT NOTE: Not sure why legacy driver does this, + * but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6. + */ +#define HW_BEACON_BASE0 0x7800 +#define HW_BEACON_BASE1 0x7a00 +#define HW_BEACON_BASE2 0x7c00 +#define HW_BEACON_BASE3 0x7e00 +#define HW_BEACON_BASE4 0x7200 +#define HW_BEACON_BASE5 0x7400 +#define HW_BEACON_BASE6 0x5dc0 +#define HW_BEACON_BASE7 0x5bc0 + +#define HW_BEACON_OFFSET(__index) \ + ( ((__index) < 4) ? ( HW_BEACON_BASE0 + (__index * 0x0200) ) : \ + (((__index) < 6) ? ( HW_BEACON_BASE4 + ((__index - 4) * 0x0200) ) : \ + (HW_BEACON_BASE6 - ((__index - 6) * 0x0200))) ) + +/* + * BBP registers. + * The wordsize of the BBP is 8 bits. + */ + +/* + * BBP 1: TX Antenna + */ +#define BBP1_TX_POWER FIELD8(0x07) +#define BBP1_TX_ANTENNA FIELD8(0x18) + +/* + * BBP 3: RX Antenna + */ +#define BBP3_RX_ANTENNA FIELD8(0x18) +#define BBP3_HT40_PLUS FIELD8(0x20) + +/* + * BBP 4: Bandwidth + */ +#define BBP4_TX_BF FIELD8(0x01) +#define BBP4_BANDWIDTH FIELD8(0x18) + +/* + * RFCSR registers + * The wordsize of the RFCSR is 8 bits. + */ + +/* + * RFCSR 6: + */ +#define RFCSR6_R FIELD8(0x03) + +/* + * RFCSR 7: + */ +#define RFCSR7_RF_TUNING FIELD8(0x01) + +/* + * RFCSR 12: + */ +#define RFCSR12_TX_POWER FIELD8(0x1f) + +/* + * RFCSR 22: + */ +#define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01) + +/* + * RFCSR 23: + */ +#define RFCSR23_FREQ_OFFSET FIELD8(0x7f) + +/* + * RFCSR 30: + */ +#define RFCSR30_RF_CALIBRATION FIELD8(0x80) + +/* + * RF registers + */ + +/* + * RF 2 + */ +#define RF2_ANTENNA_RX2 FIELD32(0x00000040) +#define RF2_ANTENNA_TX1 FIELD32(0x00004000) +#define RF2_ANTENNA_RX1 FIELD32(0x00020000) + +/* + * RF 3 + */ +#define RF3_TXPOWER_G FIELD32(0x00003e00) +#define RF3_TXPOWER_A_7DBM_BOOST FIELD32(0x00000200) +#define RF3_TXPOWER_A FIELD32(0x00003c00) + +/* + * RF 4 + */ +#define RF4_TXPOWER_G FIELD32(0x000007c0) +#define RF4_TXPOWER_A_7DBM_BOOST FIELD32(0x00000040) +#define RF4_TXPOWER_A FIELD32(0x00000780) +#define RF4_FREQ_OFFSET FIELD32(0x001f8000) +#define RF4_HT40 FIELD32(0x00200000) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * EEPROM Version + */ +#define EEPROM_VERSION 0x0001 +#define EEPROM_VERSION_FAE FIELD16(0x00ff) +#define EEPROM_VERSION_VERSION FIELD16(0xff00) + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM ANTENNA config + * RXPATH: 1: 1R, 2: 2R, 3: 3R + * TXPATH: 1: 1T, 2: 2T + */ +#define EEPROM_ANTENNA 0x001a +#define EEPROM_ANTENNA_RXPATH FIELD16(0x000f) +#define EEPROM_ANTENNA_TXPATH FIELD16(0x00f0) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0x0f00) + +/* + * EEPROM NIC config + * CARDBUS_ACCEL: 0 - enable, 1 - disable + */ +#define EEPROM_NIC 0x001b +#define EEPROM_NIC_HW_RADIO FIELD16(0x0001) +#define EEPROM_NIC_DYNAMIC_TX_AGC FIELD16(0x0002) +#define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0004) +#define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0008) +#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0010) +#define EEPROM_NIC_BW40M_SB_BG FIELD16(0x0020) +#define EEPROM_NIC_BW40M_SB_A FIELD16(0x0040) +#define EEPROM_NIC_WPS_PBC FIELD16(0x0080) +#define EEPROM_NIC_BW40M_BG FIELD16(0x0100) +#define EEPROM_NIC_BW40M_A FIELD16(0x0200) + +/* + * EEPROM frequency + */ +#define EEPROM_FREQ 0x001d +#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) +#define EEPROM_FREQ_LED_MODE FIELD16(0x7f00) +#define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000) + +/* + * EEPROM LED + * POLARITY_RDY_G: Polarity RDY_G setting. + * POLARITY_RDY_A: Polarity RDY_A setting. + * POLARITY_ACT: Polarity ACT setting. + * POLARITY_GPIO_0: Polarity GPIO0 setting. + * POLARITY_GPIO_1: Polarity GPIO1 setting. + * POLARITY_GPIO_2: Polarity GPIO2 setting. + * POLARITY_GPIO_3: Polarity GPIO3 setting. + * POLARITY_GPIO_4: Polarity GPIO4 setting. + * LED_MODE: Led mode. + */ +#define EEPROM_LED1 0x001e +#define EEPROM_LED2 0x001f +#define EEPROM_LED3 0x0020 +#define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001) +#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) +#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) +#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) +#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) +#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) +#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) +#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) +#define EEPROM_LED_LED_MODE FIELD16(0x1f00) + +/* + * EEPROM LNA + */ +#define EEPROM_LNA 0x0022 +#define EEPROM_LNA_BG FIELD16(0x00ff) +#define EEPROM_LNA_A0 FIELD16(0xff00) + +/* + * EEPROM RSSI BG offset + */ +#define EEPROM_RSSI_BG 0x0023 +#define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff) +#define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00) + +/* + * EEPROM RSSI BG2 offset + */ +#define EEPROM_RSSI_BG2 0x0024 +#define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff) +#define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00) + +/* + * EEPROM RSSI A offset + */ +#define EEPROM_RSSI_A 0x0025 +#define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff) +#define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00) + +/* + * EEPROM RSSI A2 offset + */ +#define EEPROM_RSSI_A2 0x0026 +#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff) +#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00) + +/* + * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power. + * This is delta in 40MHZ. + * VALUE: Tx Power dalta value (MAX=4) + * TYPE: 1: Plus the delta value, 0: minus the delta value + * TXPOWER: Enable: + */ +#define EEPROM_TXPOWER_DELTA 0x0028 +#define EEPROM_TXPOWER_DELTA_VALUE FIELD16(0x003f) +#define EEPROM_TXPOWER_DELTA_TYPE FIELD16(0x0040) +#define EEPROM_TXPOWER_DELTA_TXPOWER FIELD16(0x0080) + +/* + * EEPROM TXPOWER 802.11BG + */ +#define EEPROM_TXPOWER_BG1 0x0029 +#define EEPROM_TXPOWER_BG2 0x0030 +#define EEPROM_TXPOWER_BG_SIZE 7 +#define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00) + +/* + * EEPROM TXPOWER 802.11A + */ +#define EEPROM_TXPOWER_A1 0x003c +#define EEPROM_TXPOWER_A2 0x0053 +#define EEPROM_TXPOWER_A_SIZE 6 +#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) + +/* + * EEPROM TXpower byrate: 20MHZ power + */ +#define EEPROM_TXPOWER_BYRATE 0x006f + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x0078 +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * MCU mailbox commands. + */ +#define MCU_SLEEP 0x30 +#define MCU_WAKEUP 0x31 +#define MCU_RADIO_OFF 0x35 +#define MCU_CURRENT 0x36 +#define MCU_LED 0x50 +#define MCU_LED_STRENGTH 0x51 +#define MCU_LED_1 0x52 +#define MCU_LED_2 0x53 +#define MCU_LED_3 0x54 +#define MCU_RADAR 0x60 +#define MCU_BOOT_SIGNAL 0x72 +#define MCU_BBP_SIGNAL 0x80 +#define MCU_POWER_SAVE 0x83 + +/* + * MCU mailbox tokens + */ +#define TOKEN_WAKUP 3 + +/* + * DMA descriptor defines. + */ +#define TXWI_DESC_SIZE ( 4 * sizeof(__le32) ) +#define RXWI_DESC_SIZE ( 4 * sizeof(__le32) ) + +/* + * TX WI structure + */ + +/* + * Word0 + * FRAG: 1 To inform TKIP engine this is a fragment. + * MIMO_PS: The remote peer is in dynamic MIMO-PS mode + * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs + * BW: Channel bandwidth 20MHz or 40 MHz + * STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED + */ +#define TXWI_W0_FRAG FIELD32(0x00000001) +#define TXWI_W0_MIMO_PS FIELD32(0x00000002) +#define TXWI_W0_CF_ACK FIELD32(0x00000004) +#define TXWI_W0_TS FIELD32(0x00000008) +#define TXWI_W0_AMPDU FIELD32(0x00000010) +#define TXWI_W0_MPDU_DENSITY FIELD32(0x000000e0) +#define TXWI_W0_TX_OP FIELD32(0x00000300) +#define TXWI_W0_MCS FIELD32(0x007f0000) +#define TXWI_W0_BW FIELD32(0x00800000) +#define TXWI_W0_SHORT_GI FIELD32(0x01000000) +#define TXWI_W0_STBC FIELD32(0x06000000) +#define TXWI_W0_IFS FIELD32(0x08000000) +#define TXWI_W0_PHYMODE FIELD32(0xc0000000) + +/* + * Word1 + */ +#define TXWI_W1_ACK FIELD32(0x00000001) +#define TXWI_W1_NSEQ FIELD32(0x00000002) +#define TXWI_W1_BW_WIN_SIZE FIELD32(0x000000fc) +#define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00) +#define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) +#define TXWI_W1_PACKETID FIELD32(0xf0000000) + +/* + * Word2 + */ +#define TXWI_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + */ +#define TXWI_W3_EIV FIELD32(0xffffffff) + +/* + * RX WI structure + */ + +/* + * Word0 + */ +#define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff) +#define RXWI_W0_KEY_INDEX FIELD32(0x00000300) +#define RXWI_W0_BSSID FIELD32(0x00001c00) +#define RXWI_W0_UDF FIELD32(0x0000e000) +#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) +#define RXWI_W0_TID FIELD32(0xf0000000) + +/* + * Word1 + */ +#define RXWI_W1_FRAG FIELD32(0x0000000f) +#define RXWI_W1_SEQUENCE FIELD32(0x0000fff0) +#define RXWI_W1_MCS FIELD32(0x007f0000) +#define RXWI_W1_BW FIELD32(0x00800000) +#define RXWI_W1_SHORT_GI FIELD32(0x01000000) +#define RXWI_W1_STBC FIELD32(0x06000000) +#define RXWI_W1_PHYMODE FIELD32(0xc0000000) + +/* + * Word2 + */ +#define RXWI_W2_RSSI0 FIELD32(0x000000ff) +#define RXWI_W2_RSSI1 FIELD32(0x0000ff00) +#define RXWI_W2_RSSI2 FIELD32(0x00ff0000) + +/* + * Word3 + */ +#define RXWI_W3_SNR0 FIELD32(0x000000ff) +#define RXWI_W3_SNR1 FIELD32(0x0000ff00) + +/* + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. + */ +#define MIN_G_TXPOWER 0 +#define MIN_A_TXPOWER -7 +#define MAX_G_TXPOWER 31 +#define MAX_A_TXPOWER 15 +#define DEFAULT_TXPOWER 5 + +#define TXPOWER_G_FROM_DEV(__txpower) \ + ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_G_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER) + +#define TXPOWER_A_FROM_DEV(__txpower) \ + ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_A_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER) + +#endif /* RT2800_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c new file mode 100644 index 000000000000..eb1e1d00bec3 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -0,0 +1,2284 @@ +/* + Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> + Copyright (C) 2009 Gertjan van Wingerde <gwingerde@gmail.com> + + Based on the original rt2800pci.c and rt2800usb.c. + Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + <http://rt2x00.serialmonkey.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Module: rt2800lib + Abstract: rt2800 generic device routines. + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "rt2x00.h" +#ifdef CONFIG_RT2800USB +#include "rt2x00usb.h" +#endif +#include "rt2800lib.h" +#include "rt2800.h" +#include "rt2800usb.h" + +MODULE_AUTHOR("Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("rt2800 library"); +MODULE_LICENSE("GPL"); + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2800_register_read and rt2800_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + * The _lock versions must be used if you already hold the csr_mutex + */ +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2800_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg)) +#define WAIT_FOR_RFCSR(__dev, __reg) \ + rt2800_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2800_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg)) +#define WAIT_FOR_MCU(__dev, __reg) \ + rt2800_regbusy_read((__dev), H2M_MAILBOX_CSR, \ + H2M_MAILBOX_CSR_OWNER, (__reg)) + +static void rt2800_bbp_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBP_CSR_CFG_VALUE, value); + rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); + rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 0); + if (rt2x00_intf_is_pci(rt2x00dev)) + rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); + + rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_bbp_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); + rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 1); + if (rt2x00_intf_is_pci(rt2x00dev)) + rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); + + rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); + + WAIT_FOR_BBP(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, BBP_CSR_CFG_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RFCSR becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_DATA, value); + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RFCSR becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 0); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + + WAIT_FOR_RFCSR(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u32 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG0_REG_VALUE_BW, value); + rt2x00_set_field32(®, RF_CSR_CFG0_STANDBYMODE, 0); + rt2x00_set_field32(®, RF_CSR_CFG0_SEL, 0); + rt2x00_set_field32(®, RF_CSR_CFG0_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG0, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, + const u8 command, const u8 token, + const u8 arg0, const u8 arg1) +{ + u32 reg; + + /* + * RT2880 and RT3052 don't support MCU requests. + */ + if (rt2x00_rt(&rt2x00dev->chip, RT2880) || + rt2x00_rt(&rt2x00dev->chip, RT3052)) + return; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the MCU becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_MCU(rt2x00dev, ®)) { + rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); + rt2800_register_write_lock(rt2x00dev, H2M_MAILBOX_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); + rt2800_register_write_lock(rt2x00dev, HOST_CMD_CSR, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} +EXPORT_SYMBOL_GPL(rt2800_mcu_request); + +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +const struct rt2x00debug rt2800_rt2x00debug = { + .owner = THIS_MODULE, + .csr = { + .read = rt2800_register_read, + .write = rt2800_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .eeprom = { + .read = rt2x00_eeprom_read, + .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .bbp = { + .read = rt2800_bbp_read, + .write = rt2800_bbp_write, + .word_base = BBP_BASE, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, + .rf = { + .read = rt2x00_rf_read, + .write = rt2800_rf_write, + .word_base = RF_BASE, + .word_size = sizeof(u32), + .word_count = RF_SIZE / sizeof(u32), + }, +}; +EXPORT_SYMBOL_GPL(rt2800_rt2x00debug); +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, GPIO_CTRL_CFG, ®); + return rt2x00_get_field32(reg, GPIO_CTRL_CFG_BIT2); +} +EXPORT_SYMBOL_GPL(rt2800_rfkill_poll); + +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2800_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + unsigned int bg_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); + unsigned int polarity = + rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, + EEPROM_FREQ_LED_POLARITY); + unsigned int ledmode = + rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, + EEPROM_FREQ_LED_MODE); + + if (led->type == LED_TYPE_RADIO) { + rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, + enabled ? 0x20 : 0); + } else if (led->type == LED_TYPE_ASSOC) { + rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, + enabled ? (bg_mode ? 0x60 : 0xa0) : 0x20); + } else if (led->type == LED_TYPE_QUALITY) { + /* + * The brightness is divided into 6 levels (0 - 5), + * The specs tell us the following levels: + * 0, 1 ,3, 7, 15, 31 + * to determine the level in a simple way we can simply + * work with bitshifting: + * (1 << level) - 1 + */ + rt2800_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, + (1 << brightness / (LED_FULL / 6)) - 1, + polarity); + } +} + +static int rt2800_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; + + rt2800_register_read(led->rt2x00dev, LED_CFG, ®); + rt2x00_set_field32(®, LED_CFG_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, LED_CFG_OFF_PERIOD, *delay_off); + rt2x00_set_field32(®, LED_CFG_SLOW_BLINK_PERIOD, 3); + rt2x00_set_field32(®, LED_CFG_R_LED_MODE, 3); + rt2x00_set_field32(®, LED_CFG_G_LED_MODE, 12); + rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, 3); + rt2x00_set_field32(®, LED_CFG_LED_POLAR, 1); + rt2800_register_write(led->rt2x00dev, LED_CFG, reg); + + return 0; +} + +void rt2800_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2800_brightness_set; + led->led_dev.blink_set = rt2800_blink_set; + led->flags = LED_INITIALIZED; +} +EXPORT_SYMBOL_GPL(rt2800_init_led); +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ +static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct mac_wcid_entry wcid_entry; + struct mac_iveiv_entry iveiv_entry; + u32 offset; + u32 reg; + + offset = MAC_WCID_ATTR_ENTRY(key->hw_key_idx); + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, + !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, + (crypto->cmd == SET_KEY) * crypto->cipher); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX, + (crypto->cmd == SET_KEY) * crypto->bssidx); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher); + rt2800_register_write(rt2x00dev, offset, reg); + + offset = MAC_IVEIV_ENTRY(key->hw_key_idx); + + memset(&iveiv_entry, 0, sizeof(iveiv_entry)); + if ((crypto->cipher == CIPHER_TKIP) || + (crypto->cipher == CIPHER_TKIP_NO_MIC) || + (crypto->cipher == CIPHER_AES)) + iveiv_entry.iv[3] |= 0x20; + iveiv_entry.iv[3] |= key->keyidx << 6; + rt2800_register_multiwrite(rt2x00dev, offset, + &iveiv_entry, sizeof(iveiv_entry)); + + offset = MAC_WCID_ENTRY(key->hw_key_idx); + + memset(&wcid_entry, 0, sizeof(wcid_entry)); + if (crypto->cmd == SET_KEY) + memcpy(&wcid_entry, crypto->address, ETH_ALEN); + rt2800_register_multiwrite(rt2x00dev, offset, + &wcid_entry, sizeof(wcid_entry)); +} + +int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + u32 offset; + u32 reg; + + if (crypto->cmd == SET_KEY) { + key->hw_key_idx = (4 * crypto->bssidx) + key->keyidx; + + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + offset = SHARED_KEY_ENTRY(key->hw_key_idx); + rt2800_register_multiwrite(rt2x00dev, offset, + &key_entry, sizeof(key_entry)); + } + + /* + * The cipher types are stored over multiple registers + * starting with SHARED_KEY_MODE_BASE each word will have + * 32 bits and contains the cipher types for 2 bssidx each. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + field.bit_offset = 4 * (key->hw_key_idx % 8); + field.bit_mask = 0x7 << field.bit_offset; + + offset = SHARED_KEY_MODE_ENTRY(key->hw_key_idx / 8); + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, + (crypto->cmd == SET_KEY) * crypto->cipher); + rt2800_register_write(rt2x00dev, offset, reg); + + /* + * Update WCID information + */ + rt2800_config_wcid_attr(rt2x00dev, crypto, key); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_config_shared_key); + +int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + u32 offset; + + if (crypto->cmd == SET_KEY) { + /* + * 1 pairwise key is possible per AID, this means that the AID + * equals our hw_key_idx. Make sure the WCID starts _after_ the + * last possible shared key entry. + */ + if (crypto->aid > (256 - 32)) + return -ENOSPC; + + key->hw_key_idx = 32 + crypto->aid; + + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + offset = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + rt2800_register_multiwrite(rt2x00dev, offset, + &key_entry, sizeof(key_entry)); + } + + /* + * Update WCID information + */ + rt2800_config_wcid_attr(rt2x00dev, crypto, key); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_config_pairwise_key); + +void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2800_register_read(rt2x00dev, RX_FILTER_CFG, ®); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CRC_ERROR, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PHY_ERROR, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_TO_ME, + !(filter_flags & FIF_PROMISC_IN_BSS)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_VER_ERROR, 1); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BROADCAST, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_DUPLICATE, 1); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END_ACK, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_ACK, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CTS, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_RTS, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, + !(filter_flags & FIF_PSPOLL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, 1); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, + !(filter_flags & FIF_CONTROL)); + rt2800_register_write(rt2x00dev, RX_FILTER_CFG, reg); +} +EXPORT_SYMBOL_GPL(rt2800_config_filter); + +void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, const unsigned int flags) +{ + unsigned int beacon_base; + u32 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Clear current synchronisation setup. + * For the Beacon base registers we only need to clear + * the first byte since that byte contains the VALID and OWNER + * bits which (when set to 0) will invalidate the entire beacon. + */ + beacon_base = HW_BEACON_OFFSET(intf->beacon->entry_idx); + rt2800_register_write(rt2x00dev, beacon_base, 0); + + /* + * Enable synchronisation. + */ + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, conf->sync); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, + (conf->sync == TSF_SYNC_BEACON)); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } + + if (flags & CONFIG_UPDATE_MAC) { + reg = le32_to_cpu(conf->mac[1]); + rt2x00_set_field32(®, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff); + conf->mac[1] = cpu_to_le32(reg); + + rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0, + conf->mac, sizeof(conf->mac)); + } + + if (flags & CONFIG_UPDATE_BSSID) { + reg = le32_to_cpu(conf->bssid[1]); + rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_ID_MASK, 0); + rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 0); + conf->bssid[1] = cpu_to_le32(reg); + + rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0, + conf->bssid, sizeof(conf->bssid)); + } +} +EXPORT_SYMBOL_GPL(rt2800_config_intf); + +void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, TX_TIMEOUT_CFG, ®); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_RX_ACK_TIMEOUT, 0x20); + rt2800_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg); + + rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); + rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, + !!erp->short_preamble); + rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, + !!erp->short_preamble); + rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); + + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, + erp->cts_protection ? 2 : 0); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + + rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, + erp->basic_rates); + rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); + + rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); + rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, erp->slot_time); + rt2x00_set_field32(®, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2); + rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); + + rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); + rt2x00_set_field32(®, XIFS_TIME_CFG_CCKM_SIFS_TIME, erp->sifs); + rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_SIFS_TIME, erp->sifs); + rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4); + rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, erp->eifs); + rt2x00_set_field32(®, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1); + rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); + + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); +} +EXPORT_SYMBOL_GPL(rt2800_config_erp); + +void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) +{ + u8 r1; + u8 r3; + + rt2800_bbp_read(rt2x00dev, 1, &r1); + rt2800_bbp_read(rt2x00dev, 3, &r3); + + /* + * Configure the TX antenna. + */ + switch ((int)ant->tx) { + case 1: + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); + if (rt2x00_intf_is_pci(rt2x00dev)) + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0); + break; + case 2: + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); + break; + case 3: + /* Do nothing */ + break; + } + + /* + * Configure the RX antenna. + */ + switch ((int)ant->rx) { + case 1: + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0); + break; + case 2: + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1); + break; + case 3: + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2); + break; + } + + rt2800_bbp_write(rt2x00dev, 3, r3); + rt2800_bbp_write(rt2x00dev, 1, r1); +} +EXPORT_SYMBOL_GPL(rt2800_config_ant); + +static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u16 eeprom; + short lna_gain; + + if (libconf->rf.channel <= 14) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG); + } else if (libconf->rf.channel <= 64) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0); + } else if (libconf->rf.channel <= 128) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1); + } else { + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2); + } + + rt2x00dev->lna_gain = lna_gain; +} + +static void rt2800_config_channel_rt2x(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + if (rt2x00dev->default_ant.tx == 1) + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_TX1, 1); + + if (rt2x00dev->default_ant.rx == 1) { + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX1, 1); + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); + } else if (rt2x00dev->default_ant.rx == 2) + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); + + if (rf->channel > 14) { + /* + * When TX power is below 0, we should increase it by 7 to + * make it a positive value (Minumum value is -7). + * However this means that values between 0 and 7 have + * double meaning, and we should set a 7DBm boost flag. + */ + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST, + (info->tx_power1 >= 0)); + + if (info->tx_power1 < 0) + info->tx_power1 += 7; + + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, + TXPOWER_A_TO_DEV(info->tx_power1)); + + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST, + (info->tx_power2 >= 0)); + + if (info->tx_power2 < 0) + info->tx_power2 += 7; + + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, + TXPOWER_A_TO_DEV(info->tx_power2)); + } else { + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, + TXPOWER_G_TO_DEV(info->tx_power1)); + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, + TXPOWER_G_TO_DEV(info->tx_power2)); + } + + rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf)); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); +} + +static void rt2800_config_channel_rt3x(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr; + + rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 3, rf->rf3); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R, rf->rf2); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, + TXPOWER_G_TO_DEV(info->tx_power1)); + rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); + rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 24, + rt2x00dev->calibration[conf_is_ht40(conf)]); + + rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); + rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); +} + +static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u32 reg; + unsigned int tx_pin; + u8 bbp; + + if ((rt2x00_rt(&rt2x00dev->chip, RT3070) || + rt2x00_rt(&rt2x00dev->chip, RT3090)) && + (rt2x00_rf(&rt2x00dev->chip, RF2020) || + rt2x00_rf(&rt2x00dev->chip, RF3020) || + rt2x00_rf(&rt2x00dev->chip, RF3021) || + rt2x00_rf(&rt2x00dev->chip, RF3022))) + rt2800_config_channel_rt3x(rt2x00dev, conf, rf, info); + else + rt2800_config_channel_rt2x(rt2x00dev, conf, rf, info); + + /* + * Change BBP settings + */ + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 86, 0); + + if (rf->channel <= 14) { + if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) { + rt2800_bbp_write(rt2x00dev, 82, 0x62); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + } else { + rt2800_bbp_write(rt2x00dev, 82, 0x84); + rt2800_bbp_write(rt2x00dev, 75, 0x50); + } + } else { + rt2800_bbp_write(rt2x00dev, 82, 0xf2); + + if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) + rt2800_bbp_write(rt2x00dev, 75, 0x46); + else + rt2800_bbp_write(rt2x00dev, 75, 0x50); + } + + rt2800_register_read(rt2x00dev, TX_BAND_CFG, ®); + rt2x00_set_field32(®, TX_BAND_CFG_HT40_PLUS, conf_is_ht40_plus(conf)); + rt2x00_set_field32(®, TX_BAND_CFG_A, rf->channel > 14); + rt2x00_set_field32(®, TX_BAND_CFG_BG, rf->channel <= 14); + rt2800_register_write(rt2x00dev, TX_BAND_CFG, reg); + + tx_pin = 0; + + /* Turn on unused PA or LNA when not using 1T or 1R */ + if (rt2x00dev->default_ant.tx != 1) { + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1); + } + + /* Turn on unused PA or LNA when not using 1T or 1R */ + if (rt2x00dev->default_ant.rx != 1) { + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1); + } + + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, rf->channel <= 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14); + + rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf)); + rt2800_bbp_write(rt2x00dev, 4, bbp); + + rt2800_bbp_read(rt2x00dev, 3, &bbp); + rt2x00_set_field8(&bbp, BBP3_HT40_PLUS, conf_is_ht40_plus(conf)); + rt2800_bbp_write(rt2x00dev, 3, bbp); + + if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) { + if (conf_is_ht40(conf)) { + rt2800_bbp_write(rt2x00dev, 69, 0x1a); + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + rt2800_bbp_write(rt2x00dev, 73, 0x16); + } else { + rt2800_bbp_write(rt2x00dev, 69, 0x16); + rt2800_bbp_write(rt2x00dev, 70, 0x08); + rt2800_bbp_write(rt2x00dev, 73, 0x11); + } + } + + msleep(1); +} + +static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, + const int txpower) +{ + u32 reg; + u32 value = TXPOWER_G_TO_DEV(txpower); + u8 r1; + + rt2800_bbp_read(rt2x00dev, 1, &r1); + rt2x00_set_field8(®, BBP1_TX_POWER, 0); + rt2800_bbp_write(rt2x00dev, 1, r1); + + rt2800_register_read(rt2x00dev, TX_PWR_CFG_0, ®); + rt2x00_set_field32(®, TX_PWR_CFG_0_1MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_0_2MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_0_55MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_0_11MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_0_6MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_0_9MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_0_12MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_0_18MBS, value); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, reg); + + rt2800_register_read(rt2x00dev, TX_PWR_CFG_1, ®); + rt2x00_set_field32(®, TX_PWR_CFG_1_24MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_1_36MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_1_48MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_1_54MBS, value); + rt2x00_set_field32(®, TX_PWR_CFG_1_MCS0, value); + rt2x00_set_field32(®, TX_PWR_CFG_1_MCS1, value); + rt2x00_set_field32(®, TX_PWR_CFG_1_MCS2, value); + rt2x00_set_field32(®, TX_PWR_CFG_1_MCS3, value); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, reg); + + rt2800_register_read(rt2x00dev, TX_PWR_CFG_2, ®); + rt2x00_set_field32(®, TX_PWR_CFG_2_MCS4, value); + rt2x00_set_field32(®, TX_PWR_CFG_2_MCS5, value); + rt2x00_set_field32(®, TX_PWR_CFG_2_MCS6, value); + rt2x00_set_field32(®, TX_PWR_CFG_2_MCS7, value); + rt2x00_set_field32(®, TX_PWR_CFG_2_MCS8, value); + rt2x00_set_field32(®, TX_PWR_CFG_2_MCS9, value); + rt2x00_set_field32(®, TX_PWR_CFG_2_MCS10, value); + rt2x00_set_field32(®, TX_PWR_CFG_2_MCS11, value); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, reg); + + rt2800_register_read(rt2x00dev, TX_PWR_CFG_3, ®); + rt2x00_set_field32(®, TX_PWR_CFG_3_MCS12, value); + rt2x00_set_field32(®, TX_PWR_CFG_3_MCS13, value); + rt2x00_set_field32(®, TX_PWR_CFG_3_MCS14, value); + rt2x00_set_field32(®, TX_PWR_CFG_3_MCS15, value); + rt2x00_set_field32(®, TX_PWR_CFG_3_UKNOWN1, value); + rt2x00_set_field32(®, TX_PWR_CFG_3_UKNOWN2, value); + rt2x00_set_field32(®, TX_PWR_CFG_3_UKNOWN3, value); + rt2x00_set_field32(®, TX_PWR_CFG_3_UKNOWN4, value); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, reg); + + rt2800_register_read(rt2x00dev, TX_PWR_CFG_4, ®); + rt2x00_set_field32(®, TX_PWR_CFG_4_UKNOWN5, value); + rt2x00_set_field32(®, TX_PWR_CFG_4_UKNOWN6, value); + rt2x00_set_field32(®, TX_PWR_CFG_4_UKNOWN7, value); + rt2x00_set_field32(®, TX_PWR_CFG_4_UKNOWN8, value); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, reg); +} + +static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); + rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, + libconf->conf->short_frame_max_tx_count); + rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_THRE, 2000); + rt2x00_set_field32(®, TX_RTY_CFG_NON_AGG_RTY_MODE, 0); + rt2x00_set_field32(®, TX_RTY_CFG_AGG_RTY_MODE, 0); + rt2x00_set_field32(®, TX_RTY_CFG_TX_AUTO_FB_ENABLE, 1); + rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); +} + +static void rt2800_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; + + if (state == STATE_SLEEP) { + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0); + + rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 5); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, + libconf->conf->listen_interval - 1); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 1); + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); + } else { + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); + + rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 0); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, 0); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 0); + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); + } +} + +void rt2800_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + /* Always recalculate LNA gain before changing configuration */ + rt2800_config_lna_gain(rt2x00dev, libconf); + + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt2800_config_channel(rt2x00dev, libconf->conf, + &libconf->rf, &libconf->channel); + if (flags & IEEE80211_CONF_CHANGE_POWER) + rt2800_config_txpower(rt2x00dev, libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt2800_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2800_config_ps(rt2x00dev, libconf); +} +EXPORT_SYMBOL_GPL(rt2800_config); + +/* + * Link tuning + */ +void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) +{ + u32 reg; + + /* + * Update FCS error count from register. + */ + rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); + qual->rx_failed = rt2x00_get_field32(reg, RX_STA_CNT0_CRC_ERR); +} +EXPORT_SYMBOL_GPL(rt2800_link_stats); + +static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) +{ + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { + if (rt2x00_intf_is_usb(rt2x00dev) && + rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) + return 0x1c + (2 * rt2x00dev->lna_gain); + else + return 0x2e + rt2x00dev->lna_gain; + } + + if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + return 0x32 + (rt2x00dev->lna_gain * 5) / 3; + else + return 0x3a + (rt2x00dev->lna_gain * 5) / 3; +} + +static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) +{ + if (qual->vgc_level != vgc_level) { + rt2800_bbp_write(rt2x00dev, 66, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } +} + +void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) +{ + rt2800_set_vgc(rt2x00dev, qual, rt2800_get_default_vgc(rt2x00dev)); +} +EXPORT_SYMBOL_GPL(rt2800_reset_tuner); + +void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, + const u32 count) +{ + if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) + return; + + /* + * When RSSI is better then -80 increase VGC level with 0x10 + */ + rt2800_set_vgc(rt2x00dev, qual, + rt2800_get_default_vgc(rt2x00dev) + + ((qual->rssi > -80) * 0x10)); +} +EXPORT_SYMBOL_GPL(rt2800_link_tuner); + +/* + * Initialization functions. + */ +int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + unsigned int i; + + if (rt2x00_intf_is_usb(rt2x00dev)) { + /* + * Wait until BBP and RF are ready. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg && reg != ~0) + break; + msleep(1); + } + + if (i == REGISTER_BUSY_COUNT) { + ERROR(rt2x00dev, "Unstable hardware.\n"); + return -EBUSY; + } + + rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, ®); + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, + reg & ~0x00002000); + } else if (rt2x00_intf_is_pci(rt2x00dev)) + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); + + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + if (rt2x00_intf_is_usb(rt2x00dev)) { + rt2800_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000); +#ifdef CONFIG_RT2800USB + rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, + USB_MODE_RESET, REGISTER_TIMEOUT); +#endif + } + + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + rt2800_register_read(rt2x00dev, BCN_OFFSET0, ®); + rt2x00_set_field32(®, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */ + rt2x00_set_field32(®, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */ + rt2x00_set_field32(®, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */ + rt2x00_set_field32(®, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */ + rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg); + + rt2800_register_read(rt2x00dev, BCN_OFFSET1, ®); + rt2x00_set_field32(®, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */ + rt2x00_set_field32(®, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */ + rt2x00_set_field32(®, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */ + rt2x00_set_field32(®, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */ + rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg); + + rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f); + rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); + + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + if (rt2x00_intf_is_usb(rt2x00dev) && + rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + } + + rt2800_register_read(rt2x00dev, TX_LINK_CFG, ®); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB_LIFETIME, 32); + rt2x00_set_field32(®, TX_LINK_CFG_MFB_ENABLE, 0); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_UMFS_ENABLE, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_MRQ_EN, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_RDG_EN, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_CF_ACK_EN, 1); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB, 0); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFS, 0); + rt2800_register_write(rt2x00dev, TX_LINK_CFG, reg); + + rt2800_register_read(rt2x00dev, TX_TIMEOUT_CFG, ®); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_MPDU_LIFETIME, 9); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_TX_OP_TIMEOUT, 10); + rt2800_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg); + + rt2800_register_read(rt2x00dev, MAX_LEN_CFG, ®); + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE); + if (rt2x00_rev(&rt2x00dev->chip) >= RT2880E_VERSION && + rt2x00_rev(&rt2x00dev->chip) < RT3070_VERSION) + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 2); + else + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 1); + rt2x00_set_field32(®, MAX_LEN_CFG_MIN_PSDU, 0); + rt2x00_set_field32(®, MAX_LEN_CFG_MIN_MPDU, 0); + rt2800_register_write(rt2x00dev, MAX_LEN_CFG, reg); + + rt2800_register_write(rt2x00dev, PBF_MAX_PCNT, 0x1f3fbf9f); + + rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); + rt2x00_set_field32(®, AUTO_RSP_CFG_AUTORESPONDER, 1); + rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MMODE, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MREF, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_DUAL_CTS_EN, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0); + rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); + + rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_RATE, 8); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_NAV, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM40, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF40, 1); + rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_RATE, 8); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_NAV, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM40, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF40, 1); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, 0x4004); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_NAV, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, 0x4084); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_NAV, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF40, 1); + rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, 0x4004); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_NAV, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, 0x4084); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_NAV, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1); + rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); + + if (rt2x00_intf_is_usb(rt2x00dev)) { + rt2800_register_write(rt2x00dev, PBF_CFG, 0xf40006); + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 3); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_BIG_ENDIAN, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_HDR_SCATTER, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_HDR_SEG_LEN, 0); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + } + + rt2800_register_write(rt2x00dev, TXOP_CTRL_CFG, 0x0000583f); + rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, 0x00000002); + + rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); + rt2x00_set_field32(®, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, + IEEE80211_MAX_RTS_THRESHOLD); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_FBK_EN, 0); + rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); + + rt2800_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca); + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); + + /* + * ASIC will keep garbage value after boot, clear encryption keys. + */ + for (i = 0; i < 4; i++) + rt2800_register_write(rt2x00dev, + SHARED_KEY_MODE_ENTRY(i), 0); + + for (i = 0; i < 256; i++) { + u32 wcid[2] = { 0xffffffff, 0x00ffffff }; + rt2800_register_multiwrite(rt2x00dev, MAC_WCID_ENTRY(i), + wcid, sizeof(wcid)); + + rt2800_register_write(rt2x00dev, MAC_WCID_ATTR_ENTRY(i), 1); + rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0); + } + + /* + * Clear all beacons + * For the Beacon base registers we only need to clear + * the first byte since that byte contains the VALID and OWNER + * bits which (when set to 0) will invalidate the entire beacon. + */ + rt2800_register_write(rt2x00dev, HW_BEACON_BASE0, 0); + rt2800_register_write(rt2x00dev, HW_BEACON_BASE1, 0); + rt2800_register_write(rt2x00dev, HW_BEACON_BASE2, 0); + rt2800_register_write(rt2x00dev, HW_BEACON_BASE3, 0); + rt2800_register_write(rt2x00dev, HW_BEACON_BASE4, 0); + rt2800_register_write(rt2x00dev, HW_BEACON_BASE5, 0); + rt2800_register_write(rt2x00dev, HW_BEACON_BASE6, 0); + rt2800_register_write(rt2x00dev, HW_BEACON_BASE7, 0); + + if (rt2x00_intf_is_usb(rt2x00dev)) { + rt2800_register_read(rt2x00dev, USB_CYC_CFG, ®); + rt2x00_set_field32(®, USB_CYC_CFG_CLOCK_CYCLE, 30); + rt2800_register_write(rt2x00dev, USB_CYC_CFG, reg); + } + + rt2800_register_read(rt2x00dev, HT_FBK_CFG0, ®); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS0FBK, 0); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS1FBK, 0); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS2FBK, 1); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS3FBK, 2); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS4FBK, 3); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS5FBK, 4); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS6FBK, 5); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS7FBK, 6); + rt2800_register_write(rt2x00dev, HT_FBK_CFG0, reg); + + rt2800_register_read(rt2x00dev, HT_FBK_CFG1, ®); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS8FBK, 8); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS9FBK, 8); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS10FBK, 9); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS11FBK, 10); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS12FBK, 11); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS13FBK, 12); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS14FBK, 13); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS15FBK, 14); + rt2800_register_write(rt2x00dev, HT_FBK_CFG1, reg); + + rt2800_register_read(rt2x00dev, LG_FBK_CFG0, ®); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS0FBK, 8); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS1FBK, 8); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS2FBK, 9); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS3FBK, 10); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS4FBK, 11); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS5FBK, 12); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS6FBK, 13); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS7FBK, 14); + rt2800_register_write(rt2x00dev, LG_FBK_CFG0, reg); + + rt2800_register_read(rt2x00dev, LG_FBK_CFG1, ®); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS0FBK, 0); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS1FBK, 0); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS2FBK, 1); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS3FBK, 2); + rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg); + + /* + * We must clear the error counters. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); + rt2800_register_read(rt2x00dev, RX_STA_CNT1, ®); + rt2800_register_read(rt2x00dev, RX_STA_CNT2, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT0, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT1, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT2, ®); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_init_registers); + +static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, MAC_STATUS_CFG, ®); + if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY)) + return 0; + + udelay(REGISTER_BUSY_DELAY); + } + + ERROR(rt2x00dev, "BBP/RF register access failed, aborting.\n"); + return -EACCES; +} + +static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u8 value; + + /* + * BBP was enabled after firmware was loaded, + * but we need to reactivate it now. + */ + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + msleep(1); + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + return 0; + udelay(REGISTER_BUSY_DELAY); + } + + ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); + return -EACCES; +} + +int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) || + rt2800_wait_bbp_ready(rt2x00dev))) + return -EACCES; + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + rt2800_bbp_write(rt2x00dev, 81, 0x37); + rt2800_bbp_write(rt2x00dev, 82, 0x62); + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + rt2800_bbp_write(rt2x00dev, 84, 0x99); + rt2800_bbp_write(rt2x00dev, 86, 0x00); + rt2800_bbp_write(rt2x00dev, 91, 0x04); + rt2800_bbp_write(rt2x00dev, 92, 0x00); + rt2800_bbp_write(rt2x00dev, 103, 0x00); + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) { + rt2800_bbp_write(rt2x00dev, 69, 0x16); + rt2800_bbp_write(rt2x00dev, 73, 0x12); + } + + if (rt2x00_rev(&rt2x00dev->chip) > RT2860D_VERSION) + rt2800_bbp_write(rt2x00dev, 84, 0x19); + + if (rt2x00_intf_is_usb(rt2x00dev) && + rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) { + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + rt2800_bbp_write(rt2x00dev, 84, 0x99); + rt2800_bbp_write(rt2x00dev, 105, 0x05); + } + + if (rt2x00_rt(&rt2x00dev->chip, RT3052)) { + rt2800_bbp_write(rt2x00dev, 31, 0x08); + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); + } + + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + rt2800_bbp_write(rt2x00dev, reg_id, value); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_init_bbp); + +static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev, + bool bw40, u8 rfcsr24, u8 filter_target) +{ + unsigned int i; + u8 bbp; + u8 rfcsr; + u8 passband; + u8 stopband; + u8 overtuned = 0; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * bw40); + rt2800_bbp_write(rt2x00dev, 4, bbp); + + rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 1); + rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); + + /* + * Set power & frequency of passband test tone + */ + rt2800_bbp_write(rt2x00dev, 24, 0); + + for (i = 0; i < 100; i++) { + rt2800_bbp_write(rt2x00dev, 25, 0x90); + msleep(1); + + rt2800_bbp_read(rt2x00dev, 55, &passband); + if (passband) + break; + } + + /* + * Set power & frequency of stopband test tone + */ + rt2800_bbp_write(rt2x00dev, 24, 0x06); + + for (i = 0; i < 100; i++) { + rt2800_bbp_write(rt2x00dev, 25, 0x90); + msleep(1); + + rt2800_bbp_read(rt2x00dev, 55, &stopband); + + if ((passband - stopband) <= filter_target) { + rfcsr24++; + overtuned += ((passband - stopband) == filter_target); + } else + break; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + } + + rfcsr24 -= !!overtuned; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + return rfcsr24; +} + +int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) +{ + u8 rfcsr; + u8 bbp; + + if (rt2x00_intf_is_usb(rt2x00dev) && + rt2x00_rev(&rt2x00dev->chip) != RT3070_VERSION) + return 0; + + if (rt2x00_intf_is_pci(rt2x00dev)) { + if (!rt2x00_rf(&rt2x00dev->chip, RF3020) && + !rt2x00_rf(&rt2x00dev->chip, RF3021) && + !rt2x00_rf(&rt2x00dev->chip, RF3022)) + return 0; + } + + /* + * Init RF calibration. + */ + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + msleep(1); + rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + if (rt2x00_intf_is_usb(rt2x00dev)) { + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x03); + rt2800_rfcsr_write(rt2x00dev, 6, 0x02); + rt2800_rfcsr_write(rt2x00dev, 7, 0x70); + rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 10, 0x71); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x58); + rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 17, 0x92); + rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0xba); + rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 24, 0x16); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); + } else if (rt2x00_intf_is_pci(rt2x00dev)) { + rt2800_rfcsr_write(rt2x00dev, 0, 0x50); + rt2800_rfcsr_write(rt2x00dev, 1, 0x01); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf7); + rt2800_rfcsr_write(rt2x00dev, 3, 0x75); + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x03); + rt2800_rfcsr_write(rt2x00dev, 6, 0x02); + rt2800_rfcsr_write(rt2x00dev, 7, 0x50); + rt2800_rfcsr_write(rt2x00dev, 8, 0x39); + rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 10, 0x60); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x75); + rt2800_rfcsr_write(rt2x00dev, 13, 0x75); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x58); + rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 17, 0x92); + rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0xba); + rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x31); + rt2800_rfcsr_write(rt2x00dev, 24, 0x08); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + rt2800_rfcsr_write(rt2x00dev, 26, 0x25); + rt2800_rfcsr_write(rt2x00dev, 27, 0x23); + rt2800_rfcsr_write(rt2x00dev, 28, 0x13); + rt2800_rfcsr_write(rt2x00dev, 29, 0x83); + } + + /* + * Set RX Filter calibration for 20MHz and 40MHz + */ + rt2x00dev->calibration[0] = + rt2800_init_rx_filter(rt2x00dev, false, 0x07, 0x16); + rt2x00dev->calibration[1] = + rt2800_init_rx_filter(rt2x00dev, true, 0x27, 0x19); + + /* + * Set back to initial state + */ + rt2800_bbp_write(rt2x00dev, 24, 0); + + rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 0); + rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); + + /* + * set BBP back to BW20 + */ + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 0); + rt2800_bbp_write(rt2x00dev, 4, bbp); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_init_rfcsr); + +int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, EFUSE_CTRL, ®); + + return rt2x00_get_field32(reg, EFUSE_CTRL_PRESENT); +} +EXPORT_SYMBOL_GPL(rt2800_efuse_detect); + +static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + rt2800_register_read_lock(rt2x00dev, EFUSE_CTRL, ®); + rt2x00_set_field32(®, EFUSE_CTRL_ADDRESS_IN, i); + rt2x00_set_field32(®, EFUSE_CTRL_MODE, 0); + rt2x00_set_field32(®, EFUSE_CTRL_KICK, 1); + rt2800_register_write_lock(rt2x00dev, EFUSE_CTRL, reg); + + /* Wait until the EEPROM has been loaded */ + rt2800_regbusy_read(rt2x00dev, EFUSE_CTRL, EFUSE_CTRL_KICK, ®); + + /* Apparently the data is read from end to start */ + rt2800_register_read_lock(rt2x00dev, EFUSE_DATA3, + (u32 *)&rt2x00dev->eeprom[i]); + rt2800_register_read_lock(rt2x00dev, EFUSE_DATA2, + (u32 *)&rt2x00dev->eeprom[i + 2]); + rt2800_register_read_lock(rt2x00dev, EFUSE_DATA1, + (u32 *)&rt2x00dev->eeprom[i + 4]); + rt2800_register_read_lock(rt2x00dev, EFUSE_DATA0, + (u32 *)&rt2x00dev->eeprom[i + 6]); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + for (i = 0; i < EEPROM_SIZE / sizeof(u16); i += 8) + rt2800_efuse_read(rt2x00dev, i); +} +EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse); + +int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + u8 *mac; + u8 default_lna_gain; + + /* + * Start validation of the data that has been read. + */ + mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + if (!is_valid_ether_addr(mac)) { + random_ether_addr(mac); + EEPROM(rt2x00dev, "MAC: %pM\n", mac); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_ANTENNA_RXPATH, 2); + rt2x00_set_field16(&word, EEPROM_ANTENNA_TXPATH, 1); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2820); + rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); + EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); + } else if (rt2x00_rev(&rt2x00dev->chip) < RT2883_VERSION) { + /* + * There is a max of 2 RX streams for RT28x0 series + */ + if (rt2x00_get_field16(word, EEPROM_ANTENNA_RXPATH) > 2) + rt2x00_set_field16(&word, EEPROM_ANTENNA_RXPATH, 2); + rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_HW_RADIO, 0); + rt2x00_set_field16(&word, EEPROM_NIC_DYNAMIC_TX_AGC, 0); + rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_BG, 0); + rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_A, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); + rt2x00_set_field16(&word, EEPROM_NIC_BW40M_SB_BG, 0); + rt2x00_set_field16(&word, EEPROM_NIC_BW40M_SB_A, 0); + rt2x00_set_field16(&word, EEPROM_NIC_WPS_PBC, 0); + rt2x00_set_field16(&word, EEPROM_NIC_BW40M_BG, 0); + rt2x00_set_field16(&word, EEPROM_NIC_BW40M_A, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); + EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); + if ((word & 0x00ff) == 0x00ff) { + rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); + rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE, + LED_MODE_TXRX_ACTIVITY); + rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2x00_eeprom_write(rt2x00dev, EEPROM_LED1, 0x5555); + rt2x00_eeprom_write(rt2x00dev, EEPROM_LED2, 0x2221); + rt2x00_eeprom_write(rt2x00dev, EEPROM_LED3, 0xa9f8); + EEPROM(rt2x00dev, "Freq: 0x%04x\n", word); + } + + /* + * During the LNA validation we are going to use + * lna0 as correct value. Note that EEPROM_LNA + * is never validated. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word); + default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0); + if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 || + rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff) + rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1, + default_lna_gain); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0); + if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 || + rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff) + rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2, + default_lna_gain); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_validate_eeprom); + +int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); + + rt2x00_set_chip_rf(rt2x00dev, value, reg); + + if (rt2x00_intf_is_usb(rt2x00dev)) { + struct rt2x00_chip *chip = &rt2x00dev->chip; + + /* + * The check for rt2860 is not a typo, some rt2870 hardware + * identifies itself as rt2860 in the CSR register. + */ + if (rt2x00_check_rev(chip, 0xfff00000, 0x28600000) || + rt2x00_check_rev(chip, 0xfff00000, 0x28700000) || + rt2x00_check_rev(chip, 0xfff00000, 0x28800000)) { + rt2x00_set_chip_rt(rt2x00dev, RT2870); + } else if (rt2x00_check_rev(chip, 0xffff0000, 0x30700000)) { + rt2x00_set_chip_rt(rt2x00dev, RT3070); + } else { + ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); + return -ENODEV; + } + } + rt2x00_print_chip(rt2x00dev); + + if (!rt2x00_rf(&rt2x00dev->chip, RF2820) && + !rt2x00_rf(&rt2x00dev->chip, RF2850) && + !rt2x00_rf(&rt2x00dev->chip, RF2720) && + !rt2x00_rf(&rt2x00dev->chip, RF2750) && + !rt2x00_rf(&rt2x00dev->chip, RF3020) && + !rt2x00_rf(&rt2x00dev->chip, RF2020) && + !rt2x00_rf(&rt2x00dev->chip, RF3021) && + !rt2x00_rf(&rt2x00dev->chip, RF3022)) { + ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->default_ant.tx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TXPATH); + rt2x00dev->default_ant.rx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RXPATH); + + /* + * Read frequency offset and RF programming sequence. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); + + /* + * Read external LNA informations. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); + + if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A)) + __set_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG)) + __set_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags); + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_NIC_HW_RADIO)) + __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); + + /* + * Store led settings, for correct led behaviour. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + rt2800_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); + rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &rt2x00dev->led_mcu_reg); +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_init_eeprom); + +/* + * RF value list for rt28x0 + * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750) + */ +static const struct rf_channel rf_vals[] = { + { 1, 0x18402ecc, 0x184c0786, 0x1816b455, 0x1800510b }, + { 2, 0x18402ecc, 0x184c0786, 0x18168a55, 0x1800519f }, + { 3, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800518b }, + { 4, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800519f }, + { 5, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800518b }, + { 6, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800519f }, + { 7, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800518b }, + { 8, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800519f }, + { 9, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800518b }, + { 10, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800519f }, + { 11, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800518b }, + { 12, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800519f }, + { 13, 0x18402ecc, 0x184c079e, 0x18168a55, 0x1800518b }, + { 14, 0x18402ecc, 0x184c07a2, 0x18168a55, 0x18005193 }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x18402ecc, 0x184c099a, 0x18158a55, 0x180ed1a3 }, + { 38, 0x18402ecc, 0x184c099e, 0x18158a55, 0x180ed193 }, + { 40, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed183 }, + { 44, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed1a3 }, + { 46, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed18b }, + { 48, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed19b }, + { 52, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed193 }, + { 54, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed1a3 }, + { 56, 0x18402ec8, 0x184c068e, 0x18158a55, 0x180ed18b }, + { 60, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed183 }, + { 62, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed193 }, + { 64, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed1a3 }, + + /* 802.11 HyperLan 2 */ + { 100, 0x18402ec8, 0x184c06b2, 0x18178a55, 0x180ed783 }, + { 102, 0x18402ec8, 0x184c06b2, 0x18578a55, 0x180ed793 }, + { 104, 0x18402ec8, 0x185c06b2, 0x18578a55, 0x180ed1a3 }, + { 108, 0x18402ecc, 0x185c0a32, 0x18578a55, 0x180ed193 }, + { 110, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed183 }, + { 112, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed19b }, + { 116, 0x18402ecc, 0x184c0a3a, 0x18178a55, 0x180ed1a3 }, + { 118, 0x18402ecc, 0x184c0a3e, 0x18178a55, 0x180ed193 }, + { 120, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed183 }, + { 124, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed193 }, + { 126, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed15b }, + { 128, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed1a3 }, + { 132, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed18b }, + { 134, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed193 }, + { 136, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed19b }, + { 140, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed183 }, + + /* 802.11 UNII */ + { 149, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed1a7 }, + { 151, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed187 }, + { 153, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed18f }, + { 157, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed19f }, + { 159, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed1a7 }, + { 161, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed187 }, + { 165, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed197 }, + { 167, 0x18402ec4, 0x184c03d2, 0x18179855, 0x1815531f }, + { 169, 0x18402ec4, 0x184c03d2, 0x18179855, 0x18155327 }, + { 171, 0x18402ec4, 0x184c03d6, 0x18179855, 0x18155307 }, + { 173, 0x18402ec4, 0x184c03d6, 0x18179855, 0x1815530f }, + + /* 802.11 Japan */ + { 184, 0x15002ccc, 0x1500491e, 0x1509be55, 0x150c0a0b }, + { 188, 0x15002ccc, 0x15004922, 0x1509be55, 0x150c0a13 }, + { 192, 0x15002ccc, 0x15004926, 0x1509be55, 0x150c0a1b }, + { 196, 0x15002ccc, 0x1500492a, 0x1509be55, 0x150c0a23 }, + { 208, 0x15002ccc, 0x1500493a, 0x1509be55, 0x150c0a13 }, + { 212, 0x15002ccc, 0x1500493e, 0x1509be55, 0x150c0a1b }, + { 216, 0x15002ccc, 0x15004982, 0x1509be55, 0x150c0a23 }, +}; + +/* + * RF value list for rt3070 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_302x[] = { + {1, 241, 2, 2 }, + {2, 241, 2, 7 }, + {3, 242, 2, 2 }, + {4, 242, 2, 7 }, + {5, 243, 2, 2 }, + {6, 243, 2, 7 }, + {7, 244, 2, 2 }, + {8, 244, 2, 7 }, + {9, 245, 2, 2 }, + {10, 245, 2, 7 }, + {11, 246, 2, 2 }, + {12, 246, 2, 7 }, + {13, 247, 2, 2 }, + {14, 248, 2, 4 }, +}; + +int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct rt2x00_chip *chip = &rt2x00dev->chip; + struct hw_mode_spec *spec = &rt2x00dev->spec; + struct channel_info *info; + char *tx_power1; + char *tx_power2; + unsigned int i; + u16 eeprom; + + /* + * Initialize all hw fields. + */ + rt2x00dev->hw->flags = + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, + rt2x00_eeprom_addr(rt2x00dev, + EEPROM_MAC_ADDR_0)); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Initialize hw_mode information. + */ + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; + + if (rt2x00_rf(chip, RF2820) || + rt2x00_rf(chip, RF2720) || + (rt2x00_intf_is_pci(rt2x00dev) && rt2x00_rf(chip, RF3052))) { + spec->num_channels = 14; + spec->channels = rf_vals; + } else if (rt2x00_rf(chip, RF2850) || rt2x00_rf(chip, RF2750)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; + spec->num_channels = ARRAY_SIZE(rf_vals); + spec->channels = rf_vals; + } else if (rt2x00_rf(chip, RF3020) || + rt2x00_rf(chip, RF2020) || + rt2x00_rf(chip, RF3021) || + rt2x00_rf(chip, RF3022)) { + spec->num_channels = ARRAY_SIZE(rf_vals_302x); + spec->channels = rf_vals_302x; + } + + /* + * Initialize HT information. + */ + if (!rt2x00_rf(chip, RF2020)) + spec->ht.ht_supported = true; + else + spec->ht.ht_supported = false; + + spec->ht.cap = + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_TX_STBC | + IEEE80211_HT_CAP_RX_STBC | + IEEE80211_HT_CAP_PSMP_SUPPORT; + spec->ht.ampdu_factor = 3; + spec->ht.ampdu_density = 4; + spec->ht.mcs.tx_params = + IEEE80211_HT_MCS_TX_DEFINED | + IEEE80211_HT_MCS_TX_RX_DIFF | + ((rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TXPATH) - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + + switch (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RXPATH)) { + case 3: + spec->ht.mcs.rx_mask[2] = 0xff; + case 2: + spec->ht.mcs.rx_mask[1] = 0xff; + case 1: + spec->ht.mcs.rx_mask[0] = 0xff; + spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */ + break; + } + + /* + * Create channel information array + */ + info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); + tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); + + for (i = 0; i < 14; i++) { + info[i].tx_power1 = TXPOWER_G_FROM_DEV(tx_power1[i]); + info[i].tx_power2 = TXPOWER_G_FROM_DEV(tx_power2[i]); + } + + if (spec->num_channels > 14) { + tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1); + tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2); + + for (i = 14; i < spec->num_channels; i++) { + info[i].tx_power1 = TXPOWER_A_FROM_DEV(tx_power1[i]); + info[i].tx_power2 = TXPOWER_A_FROM_DEV(tx_power2[i]); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_probe_hw_mode); + +/* + * IEEE80211 stack callback functions. + */ +static void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, + u32 *iv32, u16 *iv16) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct mac_iveiv_entry iveiv_entry; + u32 offset; + + offset = MAC_IVEIV_ENTRY(hw_key_idx); + rt2800_register_multiread(rt2x00dev, offset, + &iveiv_entry, sizeof(iveiv_entry)); + + memcpy(&iveiv_entry.iv[0], iv16, sizeof(iv16)); + memcpy(&iveiv_entry.iv[4], iv32, sizeof(iv32)); +} + +static int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + bool enabled = (value < IEEE80211_MAX_RTS_THRESHOLD); + + rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, value); + rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); + + rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); + rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); + rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); + rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); + rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); + rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); + + return 0; +} + +static int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, + const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + struct rt2x00_field32 field; + int retval; + u32 reg; + u32 offset; + + /* + * First pass the configuration through rt2x00lib, that will + * update the queue settings and validate the input. After that + * we are free to update the registers based on the value + * in the queue parameter. + */ + retval = rt2x00mac_conf_tx(hw, queue_idx, params); + if (retval) + return retval; + + /* + * We only need to perform additional register initialization + * for WMM queues/ + */ + if (queue_idx >= 4) + return 0; + + queue = rt2x00queue_get_queue(rt2x00dev, queue_idx); + + /* Update WMM TXOP register */ + offset = WMM_TXOP0_CFG + (sizeof(u32) * (!!(queue_idx & 2))); + field.bit_offset = (queue_idx & 1) * 16; + field.bit_mask = 0xffff << field.bit_offset; + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, queue->txop); + rt2800_register_write(rt2x00dev, offset, reg); + + /* Update WMM registers */ + field.bit_offset = queue_idx * 4; + field.bit_mask = 0xf << field.bit_offset; + + rt2800_register_read(rt2x00dev, WMM_AIFSN_CFG, ®); + rt2x00_set_field32(®, field, queue->aifs); + rt2800_register_write(rt2x00dev, WMM_AIFSN_CFG, reg); + + rt2800_register_read(rt2x00dev, WMM_CWMIN_CFG, ®); + rt2x00_set_field32(®, field, queue->cw_min); + rt2800_register_write(rt2x00dev, WMM_CWMIN_CFG, reg); + + rt2800_register_read(rt2x00dev, WMM_CWMAX_CFG, ®); + rt2x00_set_field32(®, field, queue->cw_max); + rt2800_register_write(rt2x00dev, WMM_CWMAX_CFG, reg); + + /* Update EDCA registers */ + offset = EDCA_AC0_CFG + (sizeof(u32) * queue_idx); + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, EDCA_AC0_CFG_TX_OP, queue->txop); + rt2x00_set_field32(®, EDCA_AC0_CFG_AIFSN, queue->aifs); + rt2x00_set_field32(®, EDCA_AC0_CFG_CWMIN, queue->cw_min); + rt2x00_set_field32(®, EDCA_AC0_CFG_CWMAX, queue->cw_max); + rt2800_register_write(rt2x00dev, offset, reg); + + return 0; +} + +static u64 rt2800_get_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2800_register_read(rt2x00dev, TSF_TIMER_DW1, ®); + tsf = (u64) rt2x00_get_field32(reg, TSF_TIMER_DW1_HIGH_WORD) << 32; + rt2800_register_read(rt2x00dev, TSF_TIMER_DW0, ®); + tsf |= rt2x00_get_field32(reg, TSF_TIMER_DW0_LOW_WORD); + + return tsf; +} + +const struct ieee80211_ops rt2800_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, + .set_key = rt2x00mac_set_key, + .get_stats = rt2x00mac_get_stats, + .get_tkip_seq = rt2800_get_tkip_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tx_stats = rt2x00mac_get_tx_stats, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, +}; +EXPORT_SYMBOL_GPL(rt2800_mac80211_ops); diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h new file mode 100644 index 000000000000..535ce22f2ac8 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -0,0 +1,151 @@ +/* + Copyright (C) 2009 Bartlomiej Zolnierkiewicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef RT2800LIB_H +#define RT2800LIB_H + +struct rt2800_ops { + void (*register_read)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 *value); + void (*register_read_lock)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 *value); + void (*register_write)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 value); + void (*register_write_lock)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 value); + + void (*register_multiread)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length); + void (*register_multiwrite)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, const u32 length); + + int (*regbusy_read)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, u32 *reg); +}; + +static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->priv; + + rt2800ops->register_read(rt2x00dev, offset, value); +} + +static inline void rt2800_register_read_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->priv; + + rt2800ops->register_read_lock(rt2x00dev, offset, value); +} + +static inline void rt2800_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->priv; + + rt2800ops->register_write(rt2x00dev, offset, value); +} + +static inline void rt2800_register_write_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->priv; + + rt2800ops->register_write_lock(rt2x00dev, offset, value); +} + +static inline void rt2800_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->priv; + + rt2800ops->register_multiread(rt2x00dev, offset, value, length); +} + +static inline void rt2800_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, + const u32 length) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->priv; + + rt2800ops->register_multiwrite(rt2x00dev, offset, value, length); +} + +static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->priv; + + return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg); +} + +void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, + const u8 command, const u8 token, + const u8 arg0, const u8 arg1); + +extern const struct rt2x00debug rt2800_rt2x00debug; + +int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev); +void rt2800_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, enum led_type type); +int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); +int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); +void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags); +void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, const unsigned int flags); +void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp); +void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant); +void rt2800_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags); +void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); +void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); +void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, + const u32 count); + +int rt2800_init_registers(struct rt2x00_dev *rt2x00dev); +int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev); +int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev); + +int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev); +void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); +int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev); +int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev); +int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev); + +extern const struct ieee80211_ops rt2800_mac80211_ops; + +#endif /* RT2800LIB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c new file mode 100644 index 000000000000..dfc886fcb44d --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -0,0 +1,1322 @@ +/* + Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + <http://rt2x00.serialmonkey.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Module: rt2800pci + Abstract: rt2800pci device specific routines. + Supported chipsets: RT2800E & RT2800ED. + */ + +#include <linux/crc-ccitt.h> +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/eeprom_93cx6.h> + +#include "rt2x00.h" +#include "rt2x00pci.h" +#include "rt2x00soc.h" +#include "rt2800lib.h" +#include "rt2800.h" +#include "rt2800pci.h" + +#ifdef CONFIG_RT2800PCI_PCI_MODULE +#define CONFIG_RT2800PCI_PCI +#endif + +#ifdef CONFIG_RT2800PCI_WISOC_MODULE +#define CONFIG_RT2800PCI_WISOC +#endif + +/* + * Allow hardware encryption to be disabled. + */ +static int modparam_nohwcrypt = 1; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) +{ + unsigned int i; + u32 reg; + + for (i = 0; i < 200; i++) { + rt2800_register_read(rt2x00dev, H2M_MAILBOX_CID, ®); + + if ((rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD0) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD1) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD2) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD3) == token)) + break; + + udelay(REGISTER_BUSY_DELAY); + } + + if (i == 200) + ERROR(rt2x00dev, "MCU request failed, no response from hardware\n"); + + rt2800_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); +} + +#ifdef CONFIG_RT2800PCI_WISOC +static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) +{ + u32 *base_addr = (u32 *) KSEG1ADDR(0x1F040000); /* XXX for RT3052 */ + + memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); +} +#else +static inline void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) +{ +} +#endif /* CONFIG_RT2800PCI_WISOC */ + +#ifdef CONFIG_RT2800PCI_PCI +static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2800_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); + eeprom->reg_data_clock = + !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK); + eeprom->reg_chip_select = + !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT); +} + +static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in); + rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out); + rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2800_register_write(rt2x00dev, E2PROM_CSR, reg); +} + +static void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + + rt2800_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt2800pci_eepromregister_read; + eeprom.register_write = rt2800pci_eepromregister_write; + eeprom.width = !rt2x00_get_field32(reg, E2PROM_CSR_TYPE) ? + PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); +} + +static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + return rt2800_efuse_detect(rt2x00dev); +} + +static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +{ + rt2800_read_eeprom_efuse(rt2x00dev); +} +#else +static inline void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) +{ +} + +static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + return 0; +} + +static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +{ +} +#endif /* CONFIG_RT2800PCI_PCI */ + +/* + * Firmware functions + */ +static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + return FIRMWARE_RT2860; +} + +static int rt2800pci_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + u16 fw_crc; + u16 crc; + + /* + * Only support 8kb firmware files. + */ + if (len != 8192) + return FW_BAD_LENGTH; + + /* + * The last 2 bytes in the firmware array are the crc checksum itself, + * this means that we should never pass those 2 bytes to the crc + * algorithm. + */ + fw_crc = (data[len - 2] << 8 | data[len - 1]); + + /* + * Use the crc ccitt algorithm. + * This will return the same value as the legacy driver which + * used bit ordering reversion on the both the firmware bytes + * before input input as well as on the final output. + * Obviously using crc ccitt directly is much more efficient. + */ + crc = crc_ccitt(~0, data, len - 2); + + /* + * There is a small difference between the crc-itu-t + bitrev and + * the crc-ccitt crc calculation. In the latter method the 2 bytes + * will be swapped, use swab16 to convert the crc to the correct + * value. + */ + crc = swab16(crc); + + return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; +} + +static int rt2800pci_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + unsigned int i; + u32 reg; + + /* + * Wait for stable hardware. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg && reg != ~0) + break; + msleep(1); + } + + if (i == REGISTER_BUSY_COUNT) { + ERROR(rt2x00dev, "Unstable hardware.\n"); + return -EBUSY; + } + + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002); + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000); + + /* + * Disable DMA, will be reenabled later when enabling + * the radio. + */ + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + + /* + * enable Host program ram write selection + */ + reg = 0; + rt2x00_set_field32(®, PBF_SYS_CTRL_HOST_RAM_WRITE, 1); + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, reg); + + /* + * Write firmware to device. + */ + rt2800_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data, len); + + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000); + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001); + + /* + * Wait for device to stabilize. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, ®); + if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY)) + break; + msleep(1); + } + + if (i == REGISTER_BUSY_COUNT) { + ERROR(rt2x00dev, "PBF system register not ready.\n"); + return -EBUSY; + } + + /* + * Disable interrupts + */ + rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF); + + /* + * Initialize BBP R/W access agent + */ + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + + return 0; +} + +/* + * Initialization functions. + */ +static bool rt2800pci_get_entry_state(struct queue_entry *entry) +{ + struct queue_entry_priv_pci *entry_priv = entry->priv_data; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); + } +} + +static void rt2800pci_clear_entry(struct queue_entry *entry) +{ + struct queue_entry_priv_pci *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 0, word); + + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); + rt2x00_desc_write(entry_priv->desc, 1, word); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); + rt2x00_desc_write(entry_priv->desc, 1, word); + } +} + +static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct queue_entry_priv_pci *entry_priv; + u32 reg; + + rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, ®); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); + rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg); + + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + + /* + * Initialize registers. + */ + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2800_register_write(rt2x00dev, TX_BASE_PTR0, entry_priv->desc_dma); + rt2800_register_write(rt2x00dev, TX_MAX_CNT0, rt2x00dev->tx[0].limit); + rt2800_register_write(rt2x00dev, TX_CTX_IDX0, 0); + rt2800_register_write(rt2x00dev, TX_DTX_IDX0, 0); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2800_register_write(rt2x00dev, TX_BASE_PTR1, entry_priv->desc_dma); + rt2800_register_write(rt2x00dev, TX_MAX_CNT1, rt2x00dev->tx[1].limit); + rt2800_register_write(rt2x00dev, TX_CTX_IDX1, 0); + rt2800_register_write(rt2x00dev, TX_DTX_IDX1, 0); + + entry_priv = rt2x00dev->tx[2].entries[0].priv_data; + rt2800_register_write(rt2x00dev, TX_BASE_PTR2, entry_priv->desc_dma); + rt2800_register_write(rt2x00dev, TX_MAX_CNT2, rt2x00dev->tx[2].limit); + rt2800_register_write(rt2x00dev, TX_CTX_IDX2, 0); + rt2800_register_write(rt2x00dev, TX_DTX_IDX2, 0); + + entry_priv = rt2x00dev->tx[3].entries[0].priv_data; + rt2800_register_write(rt2x00dev, TX_BASE_PTR3, entry_priv->desc_dma); + rt2800_register_write(rt2x00dev, TX_MAX_CNT3, rt2x00dev->tx[3].limit); + rt2800_register_write(rt2x00dev, TX_CTX_IDX3, 0); + rt2800_register_write(rt2x00dev, TX_DTX_IDX3, 0); + + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2800_register_write(rt2x00dev, RX_BASE_PTR, entry_priv->desc_dma); + rt2800_register_write(rt2x00dev, RX_MAX_CNT, rt2x00dev->rx[0].limit); + rt2800_register_write(rt2x00dev, RX_CRX_IDX, rt2x00dev->rx[0].limit - 1); + rt2800_register_write(rt2x00dev, RX_DRX_IDX, 0); + + /* + * Enable global DMA configuration + */ + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + + rt2800_register_write(rt2x00dev, DELAY_INT_CFG, 0); + + return 0; +} + +/* + * Device state switch handlers. + */ +static void rt2800pci_toggle_rx(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, + (state == STATE_RADIO_RX_ON) || + (state == STATE_RADIO_RX_ON_LINK)); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); +} + +static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int mask = (state == STATE_RADIO_IRQ_ON); + u32 reg; + + /* + * When interrupts are being enabled, the interrupt registers + * should clear the register to assure a clean state. + */ + if (state == STATE_RADIO_IRQ_ON) { + rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + } + + rt2800_register_read(rt2x00dev, INT_MASK_CSR, ®); + rt2x00_set_field32(®, INT_MASK_CSR_RXDELAYINT, mask); + rt2x00_set_field32(®, INT_MASK_CSR_TXDELAYINT, mask); + rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_AC0_DMA_DONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_AC1_DMA_DONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_AC2_DMA_DONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_AC3_DMA_DONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_HCCA_DMA_DONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_MGMT_DMA_DONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_MCU_COMMAND, mask); + rt2x00_set_field32(®, INT_MASK_CSR_RXTX_COHERENT, mask); + rt2x00_set_field32(®, INT_MASK_CSR_TBTT, mask); + rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, mask); + rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, mask); + rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, mask); + rt2x00_set_field32(®, INT_MASK_CSR_GPTIMER, mask); + rt2x00_set_field32(®, INT_MASK_CSR_RX_COHERENT, mask); + rt2x00_set_field32(®, INT_MASK_CSR_TX_COHERENT, mask); + rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); +} + +static int rt2800pci_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && + !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) + return 0; + + msleep(1); + } + + ERROR(rt2x00dev, "WPDMA TX/RX busy, aborting.\n"); + return -EACCES; +} + +static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 word; + + /* + * Initialize all registers. + */ + if (unlikely(rt2800pci_wait_wpdma_ready(rt2x00dev) || + rt2800pci_init_queues(rt2x00dev) || + rt2800_init_registers(rt2x00dev) || + rt2800pci_wait_wpdma_ready(rt2x00dev) || + rt2800_init_bbp(rt2x00dev) || + rt2800_init_rfcsr(rt2x00dev))) + return -EIO; + + /* + * Send signal to firmware during boot time. + */ + rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0xff, 0, 0); + + /* + * Enable RX. + */ + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1); + rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + /* + * Initialize LED control + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED1, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_1, 0xff, + word & 0xff, (word >> 8) & 0xff); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED2, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_2, 0xff, + word & 0xff, (word >> 8) & 0xff); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED3, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_3, 0xff, + word & 0xff, (word >> 8) & 0xff); + + return 0; +} + +static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0); + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); + + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001280); + + rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, ®); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); + rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg); + + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); + rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + + /* Wait for DMA, ignore error */ + rt2800pci_wait_wpdma_ready(rt2x00dev); +} + +static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + /* + * Always put the device to sleep (even when we intend to wakeup!) + * if the device is booting and wasn't asleep it will return + * failure when attempting to wakeup. + */ + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 2); + + if (state == STATE_AWAKE) { + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0); + rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKUP); + } + + return 0; +} + +static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + /* + * Before the radio can be enabled, the device first has + * to be woken up. After that it needs a bit of time + * to be fully awake and then the radio can be enabled. + */ + rt2800pci_set_state(rt2x00dev, STATE_AWAKE); + msleep(1); + retval = rt2800pci_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + /* + * After the radio has been disabled, the device should + * be put to sleep for powersaving. + */ + rt2800pci_disable_radio(rt2x00dev); + rt2800pci_set_state(rt2x00dev, STATE_SLEEP); + break; + case STATE_RADIO_RX_ON: + case STATE_RADIO_RX_ON_LINK: + case STATE_RADIO_RX_OFF: + case STATE_RADIO_RX_OFF_LINK: + rt2800pci_toggle_rx(rt2x00dev, state); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2800pci_toggle_irq(rt2x00dev, state); + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt2800pci_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n", + state, retval); + + return retval; +} + +/* + * TX descriptor initialization + */ +static void rt2800pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + __le32 *txd = skbdesc->desc; + __le32 *txwi = (__le32 *)(skb->data - rt2x00dev->ops->extra_tx_headroom); + u32 word; + + /* + * Initialize TX Info descriptor + */ + rt2x00_desc_read(txwi, 0, &word); + rt2x00_set_field32(&word, TXWI_W0_FRAG, + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, 0); + rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0); + rt2x00_set_field32(&word, TXWI_W0_TS, + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_AMPDU, + test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, txdesc->mpdu_density); + rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->ifs); + rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->mcs); + rt2x00_set_field32(&word, TXWI_W0_BW, + test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_SHORT_GI, + test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->stbc); + rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode); + rt2x00_desc_write(txwi, 0, word); + + rt2x00_desc_read(txwi, 1, &word); + rt2x00_set_field32(&word, TXWI_W1_ACK, + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W1_NSEQ, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->ba_size); + rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID, + test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ? + txdesc->key_idx : 0xff); + rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT, + skb->len - txdesc->l2pad); + rt2x00_set_field32(&word, TXWI_W1_PACKETID, + skbdesc->entry->queue->qid + 1); + rt2x00_desc_write(txwi, 1, word); + + /* + * Always write 0 to IV/EIV fields, hardware will insert the IV + * from the IVEIV register when TXD_W3_WIV is set to 0. + * When TXD_W3_WIV is set to 1 it will use the IV data + * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which + * crypto entry in the registers should be used to encrypt the frame. + */ + _rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */); + _rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */); + + /* + * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 + * must contains a TXWI structure + 802.11 header + padding + 802.11 + * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and + * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 + * data. It means that LAST_SEC0 is always 0. + */ + + /* + * Initialize TX descriptor + */ + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); + rt2x00_desc_write(txd, 0, word); + + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_SD_LEN1, skb->len); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, + !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_SD_LEN0, + rt2x00dev->ops->extra_tx_headroom); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_SD_PTR1, + skbdesc->skb_dma + rt2x00dev->ops->extra_tx_headroom); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 3, &word); + rt2x00_set_field32(&word, TXD_W3_WIV, + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W3_QSEL, 2); + rt2x00_desc_write(txd, 3, word); +} + +/* + * TX data initialization + */ +static void rt2800pci_write_beacon(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + unsigned int beacon_base; + u32 reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + /* + * Write entire beacon with descriptor to register. + */ + beacon_base = HW_BEACON_OFFSET(entry->entry_idx); + rt2800_register_multiwrite(rt2x00dev, + beacon_base, + skbdesc->desc, skbdesc->desc_len); + rt2800_register_multiwrite(rt2x00dev, + beacon_base + skbdesc->desc_len, + entry->skb->data, entry->skb->len); + + /* + * Clean up beacon skb. + */ + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; +} + +static void rt2800pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, + const enum data_queue_qid queue_idx) +{ + struct data_queue *queue; + unsigned int idx, qidx = 0; + u32 reg; + + if (queue_idx == QID_BEACON) { + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + if (!rt2x00_get_field32(reg, BCN_TIME_CFG_BEACON_GEN)) { + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } + return; + } + + if (queue_idx > QID_HCCA && queue_idx != QID_MGMT) + return; + + queue = rt2x00queue_get_queue(rt2x00dev, queue_idx); + idx = queue->index[Q_INDEX]; + + if (queue_idx == QID_MGMT) + qidx = 5; + else + qidx = queue_idx; + + rt2800_register_write(rt2x00dev, TX_CTX_IDX(qidx), idx); +} + +static void rt2800pci_kill_tx_queue(struct rt2x00_dev *rt2x00dev, + const enum data_queue_qid qid) +{ + u32 reg; + + if (qid == QID_BEACON) { + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, 0); + return; + } + + rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, ®); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, (qid == QID_AC_BE)); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, (qid == QID_AC_BK)); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, (qid == QID_AC_VI)); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, (qid == QID_AC_VO)); + rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg); +} + +/* + * RX control handlers + */ +static void rt2800pci_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_pci *entry_priv = entry->priv_data; + __le32 *rxd = entry_priv->desc; + __le32 *rxwi = (__le32 *)entry->skb->data; + u32 rxd3; + u32 rxwi0; + u32 rxwi1; + u32 rxwi2; + u32 rxwi3; + + rt2x00_desc_read(rxd, 3, &rxd3); + rt2x00_desc_read(rxwi, 0, &rxwi0); + rt2x00_desc_read(rxwi, 1, &rxwi1); + rt2x00_desc_read(rxwi, 2, &rxwi2); + rt2x00_desc_read(rxwi, 3, &rxwi3); + + if (rt2x00_get_field32(rxd3, RXD_W3_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) { + /* + * Unfortunately we don't know the cipher type used during + * decryption. This prevents us from correct providing + * correct statistics through debugfs. + */ + rxdesc->cipher = rt2x00_get_field32(rxwi0, RXWI_W0_UDF); + rxdesc->cipher_status = + rt2x00_get_field32(rxd3, RXD_W3_CIPHER_ERROR); + } + + if (rt2x00_get_field32(rxd3, RXD_W3_DECRYPTED)) { + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. Unfortunately the descriptor doesn't contain + * any fields with the EIV/IV data either, so they can't + * be restored by rt2x00lib. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + if (rt2x00_get_field32(rxd3, RXD_W3_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + if (rt2x00_get_field32(rxd3, RXD_W3_L2PAD)) { + rxdesc->dev_flags |= RXDONE_L2PAD; + skbdesc->flags |= SKBDESC_L2_PADDED; + } + + if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI)) + rxdesc->flags |= RX_FLAG_SHORT_GI; + + if (rt2x00_get_field32(rxwi1, RXWI_W1_BW)) + rxdesc->flags |= RX_FLAG_40MHZ; + + /* + * Detect RX rate, always use MCS as signal type. + */ + rxdesc->dev_flags |= RXDONE_SIGNAL_MCS; + rxdesc->rate_mode = rt2x00_get_field32(rxwi1, RXWI_W1_PHYMODE); + rxdesc->signal = rt2x00_get_field32(rxwi1, RXWI_W1_MCS); + + /* + * Mask of 0x8 bit to remove the short preamble flag. + */ + if (rxdesc->rate_mode == RATE_MODE_CCK) + rxdesc->signal &= ~0x8; + + rxdesc->rssi = + (rt2x00_get_field32(rxwi2, RXWI_W2_RSSI0) + + rt2x00_get_field32(rxwi2, RXWI_W2_RSSI1)) / 2; + + rxdesc->noise = + (rt2x00_get_field32(rxwi3, RXWI_W3_SNR0) + + rt2x00_get_field32(rxwi3, RXWI_W3_SNR1)) / 2; + + rxdesc->size = rt2x00_get_field32(rxwi0, RXWI_W0_MPDU_TOTAL_BYTE_COUNT); + + /* + * Set RX IDX in register to inform hardware that we have handled + * this entry and it is available for reuse again. + */ + rt2800_register_write(rt2x00dev, RX_CRX_IDX, entry->entry_idx); + + /* + * Remove TXWI descriptor from start of buffer. + */ + skb_pull(entry->skb, RXWI_DESC_SIZE); + skb_trim(entry->skb, rxdesc->size); +} + +/* + * Interrupt functions. + */ +static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + struct queue_entry *entry_done; + struct queue_entry_priv_pci *entry_priv; + struct txdone_entry_desc txdesc; + u32 word; + u32 reg; + u32 old_reg; + unsigned int type; + unsigned int index; + u16 mcs, real_mcs; + + /* + * During each loop we will compare the freshly read + * TX_STA_FIFO register value with the value read from + * the previous loop. If the 2 values are equal then + * we should stop processing because the chance it + * quite big that the device has been unplugged and + * we risk going into an endless loop. + */ + old_reg = 0; + + while (1) { + rt2800_register_read(rt2x00dev, TX_STA_FIFO, ®); + if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID)) + break; + + if (old_reg == reg) + break; + old_reg = reg; + + /* + * Skip this entry when it contains an invalid + * queue identication number. + */ + type = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE) - 1; + if (type >= QID_RX) + continue; + + queue = rt2x00queue_get_queue(rt2x00dev, type); + if (unlikely(!queue)) + continue; + + /* + * Skip this entry when it contains an invalid + * index number. + */ + index = rt2x00_get_field32(reg, TX_STA_FIFO_WCID) - 1; + if (unlikely(index >= queue->limit)) + continue; + + entry = &queue->entries[index]; + entry_priv = entry->priv_data; + rt2x00_desc_read((__le32 *)entry->skb->data, 0, &word); + + entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + while (entry != entry_done) { + /* + * Catch up. + * Just report any entries we missed as failed. + */ + WARNING(rt2x00dev, + "TX status report missed for entry %d\n", + entry_done->entry_idx); + + txdesc.flags = 0; + __set_bit(TXDONE_UNKNOWN, &txdesc.flags); + txdesc.retry = 0; + + rt2x00lib_txdone(entry_done, &txdesc); + entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + } + + /* + * Obtain the status about this packet. + */ + txdesc.flags = 0; + if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + else + __set_bit(TXDONE_FAILURE, &txdesc.flags); + + /* + * Ralink has a retry mechanism using a global fallback + * table. We setup this fallback table to try immediate + * lower rate for all rates. In the TX_STA_FIFO, + * the MCS field contains the MCS used for the successfull + * transmission. If the first transmission succeed, + * we have mcs == tx_mcs. On the second transmission, + * we have mcs = tx_mcs - 1. So the number of + * retry is (tx_mcs - mcs). + */ + mcs = rt2x00_get_field32(word, TXWI_W0_MCS); + real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS); + __set_bit(TXDONE_FALLBACK, &txdesc.flags); + txdesc.retry = mcs - min(mcs, real_mcs); + + rt2x00lib_txdone(entry, &txdesc); + } +} + +static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg; + + /* Read status and ACK all interrupts */ + rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + if (!reg) + return IRQ_NONE; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return IRQ_HANDLED; + + /* + * 1 - Rx ring done interrupt. + */ + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) + rt2x00pci_rxdone(rt2x00dev); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) + rt2800pci_txdone(rt2x00dev); + + return IRQ_HANDLED; +} + +/* + * Device probe functions. + */ +static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + /* + * Read EEPROM into buffer + */ + switch (rt2x00dev->chip.rt) { + case RT2880: + case RT3052: + rt2800pci_read_eeprom_soc(rt2x00dev); + break; + default: + if (rt2800pci_efuse_detect(rt2x00dev)) + rt2800pci_read_eeprom_efuse(rt2x00dev); + else + rt2800pci_read_eeprom_pci(rt2x00dev); + break; + } + + return rt2800_validate_eeprom(rt2x00dev); +} + +static const struct rt2800_ops rt2800pci_rt2800_ops = { + .register_read = rt2x00pci_register_read, + .register_read_lock = rt2x00pci_register_read, /* same for PCI */ + .register_write = rt2x00pci_register_write, + .register_write_lock = rt2x00pci_register_write, /* same for PCI */ + + .register_multiread = rt2x00pci_register_multiread, + .register_multiwrite = rt2x00pci_register_multiwrite, + + .regbusy_read = rt2x00pci_regbusy_read, +}; + +static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + rt2x00dev->priv = (void *)&rt2800pci_rt2800_ops; + + /* + * Allocate eeprom data. + */ + retval = rt2800pci_validate_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2800_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Initialize hw specifications. + */ + retval = rt2800_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * This device has multiple filters for control frames + * and has a separate filter for PS Poll frames. + */ + __set_bit(DRIVER_SUPPORT_CONTROL_FILTERS, &rt2x00dev->flags); + __set_bit(DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, &rt2x00dev->flags); + + /* + * This device requires firmware. + */ + if (!rt2x00_rt(&rt2x00dev->chip, RT2880) && + !rt2x00_rt(&rt2x00dev->chip, RT3052)) + __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); + __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags); + __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags); + if (!modparam_nohwcrypt) + __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); + + /* + * Set the rssi offset. + */ + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; + + return 0; +} + +static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { + .irq_handler = rt2800pci_interrupt, + .probe_hw = rt2800pci_probe_hw, + .get_firmware_name = rt2800pci_get_firmware_name, + .check_firmware = rt2800pci_check_firmware, + .load_firmware = rt2800pci_load_firmware, + .initialize = rt2x00pci_initialize, + .uninitialize = rt2x00pci_uninitialize, + .get_entry_state = rt2800pci_get_entry_state, + .clear_entry = rt2800pci_clear_entry, + .set_device_state = rt2800pci_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .write_tx_desc = rt2800pci_write_tx_desc, + .write_tx_data = rt2x00pci_write_tx_data, + .write_beacon = rt2800pci_write_beacon, + .kick_tx_queue = rt2800pci_kick_tx_queue, + .kill_tx_queue = rt2800pci_kill_tx_queue, + .fill_rxdone = rt2800pci_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, +}; + +static const struct data_queue_desc rt2800pci_queue_rx = { + .entry_num = RX_ENTRIES, + .data_size = AGGREGATION_SIZE, + .desc_size = RXD_DESC_SIZE, + .priv_size = sizeof(struct queue_entry_priv_pci), +}; + +static const struct data_queue_desc rt2800pci_queue_tx = { + .entry_num = TX_ENTRIES, + .data_size = AGGREGATION_SIZE, + .desc_size = TXD_DESC_SIZE, + .priv_size = sizeof(struct queue_entry_priv_pci), +}; + +static const struct data_queue_desc rt2800pci_queue_bcn = { + .entry_num = 8 * BEACON_ENTRIES, + .data_size = 0, /* No DMA required for beacons */ + .desc_size = TXWI_DESC_SIZE, + .priv_size = sizeof(struct queue_entry_priv_pci), +}; + +static const struct rt2x00_ops rt2800pci_ops = { + .name = KBUILD_MODNAME, + .max_sta_intf = 1, + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .extra_tx_headroom = TXWI_DESC_SIZE, + .rx = &rt2800pci_queue_rx, + .tx = &rt2800pci_queue_tx, + .bcn = &rt2800pci_queue_bcn, + .lib = &rt2800pci_rt2x00_ops, + .hw = &rt2800_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * RT2800pci module information. + */ +static struct pci_device_id rt2800pci_device_table[] = { + { PCI_DEVICE(0x1462, 0x891a), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1432, 0x7708), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1432, 0x7727), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1432, 0x7728), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1432, 0x7738), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1432, 0x7748), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1432, 0x7758), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1432, 0x7768), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x0601), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x0681), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x0701), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x0781), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x3060), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x3062), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x3090), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x3091), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x3092), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x3562), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x3592), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1a3b, 0x1059), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards"); +#ifdef CONFIG_RT2800PCI_PCI +MODULE_FIRMWARE(FIRMWARE_RT2860); +MODULE_DEVICE_TABLE(pci, rt2800pci_device_table); +#endif /* CONFIG_RT2800PCI_PCI */ +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_RT2800PCI_WISOC +#if defined(CONFIG_RALINK_RT288X) +__rt2x00soc_probe(RT2880, &rt2800pci_ops); +#elif defined(CONFIG_RALINK_RT305X) +__rt2x00soc_probe(RT3052, &rt2800pci_ops); +#endif + +static struct platform_driver rt2800soc_driver = { + .driver = { + .name = "rt2800_wmac", + .owner = THIS_MODULE, + .mod_name = KBUILD_MODNAME, + }, + .probe = __rt2x00soc_probe, + .remove = __devexit_p(rt2x00soc_remove), + .suspend = rt2x00soc_suspend, + .resume = rt2x00soc_resume, +}; +#endif /* CONFIG_RT2800PCI_WISOC */ + +#ifdef CONFIG_RT2800PCI_PCI +static struct pci_driver rt2800pci_driver = { + .name = KBUILD_MODNAME, + .id_table = rt2800pci_device_table, + .probe = rt2x00pci_probe, + .remove = __devexit_p(rt2x00pci_remove), + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +}; +#endif /* CONFIG_RT2800PCI_PCI */ + +static int __init rt2800pci_init(void) +{ + int ret = 0; + +#ifdef CONFIG_RT2800PCI_WISOC + ret = platform_driver_register(&rt2800soc_driver); + if (ret) + return ret; +#endif +#ifdef CONFIG_RT2800PCI_PCI + ret = pci_register_driver(&rt2800pci_driver); + if (ret) { +#ifdef CONFIG_RT2800PCI_WISOC + platform_driver_unregister(&rt2800soc_driver); +#endif + return ret; + } +#endif + + return ret; +} + +static void __exit rt2800pci_exit(void) +{ +#ifdef CONFIG_RT2800PCI_PCI + pci_unregister_driver(&rt2800pci_driver); +#endif +#ifdef CONFIG_RT2800PCI_WISOC + platform_driver_unregister(&rt2800soc_driver); +#endif +} + +module_init(rt2800pci_init); +module_exit(rt2800pci_exit); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/rt2x00/rt2800pci.h new file mode 100644 index 000000000000..afc8e7da27cb --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800pci.h @@ -0,0 +1,159 @@ +/* + Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + <http://rt2x00.serialmonkey.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Module: rt2800pci + Abstract: Data structures and registers for the rt2800pci module. + Supported chipsets: RT2800E & RT2800ED. + */ + +#ifndef RT2800PCI_H +#define RT2800PCI_H + +/* + * PCI registers. + */ + +/* + * E2PROM_CSR: EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE: 0: 93c46, 1:93c66. + * LOAD_STATUS: 1:loading, 0:done. + */ +#define E2PROM_CSR 0x0004 +#define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000001) +#define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000002) +#define E2PROM_CSR_DATA_IN FIELD32(0x00000004) +#define E2PROM_CSR_DATA_OUT FIELD32(0x00000008) +#define E2PROM_CSR_TYPE FIELD32(0x00000030) +#define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) +#define E2PROM_CSR_RELOAD FIELD32(0x00000080) + +/* + * Queue register offset macros + */ +#define TX_QUEUE_REG_OFFSET 0x10 +#define TX_BASE_PTR(__x) TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET) +#define TX_MAX_CNT(__x) TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET) +#define TX_CTX_IDX(__x) TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET) +#define TX_DTX_IDX(__x) TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET) + +/* + * 8051 firmware image. + */ +#define FIRMWARE_RT2860 "rt2860.bin" +#define FIRMWARE_IMAGE_BASE 0x2000 + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 4 * sizeof(__le32) ) +#define RXD_DESC_SIZE ( 4 * sizeof(__le32) ) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) +#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) +#define TXD_W1_BURST FIELD32(0x00008000) +#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) +#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) +#define TXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) + +/* + * Word3 + * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI + * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. + * 0:MGMT, 1:HCCA 2:EDCA + */ +#define TXD_W3_WIV FIELD32(0x01000000) +#define TXD_W3_QSEL FIELD32(0x06000000) +#define TXD_W3_TCO FIELD32(0x20000000) +#define TXD_W3_UCO FIELD32(0x40000000) +#define TXD_W3_ICO FIELD32(0x80000000) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_SDP0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define RXD_W1_SDL1 FIELD32(0x00003fff) +#define RXD_W1_SDL0 FIELD32(0x3fff0000) +#define RXD_W1_LS0 FIELD32(0x40000000) +#define RXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define RXD_W2_SDP1 FIELD32(0xffffffff) + +/* + * Word3 + * AMSDU: RX with 802.3 header, not 802.11 header. + * DECRYPTED: This frame is being decrypted. + */ +#define RXD_W3_BA FIELD32(0x00000001) +#define RXD_W3_DATA FIELD32(0x00000002) +#define RXD_W3_NULLDATA FIELD32(0x00000004) +#define RXD_W3_FRAG FIELD32(0x00000008) +#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) +#define RXD_W3_MULTICAST FIELD32(0x00000020) +#define RXD_W3_BROADCAST FIELD32(0x00000040) +#define RXD_W3_MY_BSS FIELD32(0x00000080) +#define RXD_W3_CRC_ERROR FIELD32(0x00000100) +#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) +#define RXD_W3_AMSDU FIELD32(0x00000800) +#define RXD_W3_HTC FIELD32(0x00001000) +#define RXD_W3_RSSI FIELD32(0x00002000) +#define RXD_W3_L2PAD FIELD32(0x00004000) +#define RXD_W3_AMPDU FIELD32(0x00008000) +#define RXD_W3_DECRYPTED FIELD32(0x00010000) +#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) +#define RXD_W3_PLCP_RSSI FIELD32(0x00040000) + +#endif /* RT2800PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 9fe770f7d7bb..af85d18cdbe7 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -1,5 +1,9 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Axel Kollhofer <rain_maker@root-forum.org> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -34,6 +38,8 @@ #include "rt2x00.h" #include "rt2x00usb.h" +#include "rt2800lib.h" +#include "rt2800.h" #include "rt2800usb.h" /* @@ -44,1027 +50,6 @@ module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); /* - * Register access. - * All access to the CSR registers will go through the methods - * rt2x00usb_register_read and rt2x00usb_register_write. - * BBP and RF register require indirect register access, - * and use the CSR registers BBPCSR and RFCSR to achieve this. - * These indirect registers work with busy bits, - * and we will try maximal REGISTER_BUSY_COUNT times to access - * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attampt. When the busy bit is still set at that time, - * the access attempt is considered to have failed, - * and we will print an error. - * The _lock versions must be used if you already hold the csr_mutex - */ -#define WAIT_FOR_BBP(__dev, __reg) \ - rt2x00usb_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg)) -#define WAIT_FOR_RFCSR(__dev, __reg) \ - rt2x00usb_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg)) -#define WAIT_FOR_RF(__dev, __reg) \ - rt2x00usb_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg)) -#define WAIT_FOR_MCU(__dev, __reg) \ - rt2x00usb_regbusy_read((__dev), H2M_MAILBOX_CSR, \ - H2M_MAILBOX_CSR_OWNER, (__reg)) - -static void rt2800usb_bbp_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, BBP_CSR_CFG_VALUE, value); - rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); - rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 0); - - rt2x00usb_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800usb_bbp_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); - rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 1); - - rt2x00usb_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); - - WAIT_FOR_BBP(rt2x00dev, ®); - } - - *value = rt2x00_get_field32(reg, BBP_CSR_CFG_VALUE); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800usb_rfcsr_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RFCSR becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RF_CSR_CFG_DATA, value); - rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); - rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); - - rt2x00usb_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800usb_rfcsr_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RFCSR becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 0); - rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); - - rt2x00usb_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); - - WAIT_FOR_RFCSR(rt2x00dev, ®); - } - - *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800usb_rf_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u32 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RF becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RF(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RF_CSR_CFG0_REG_VALUE_BW, value); - rt2x00_set_field32(®, RF_CSR_CFG0_STANDBYMODE, 0); - rt2x00_set_field32(®, RF_CSR_CFG0_SEL, 0); - rt2x00_set_field32(®, RF_CSR_CFG0_BUSY, 1); - - rt2x00usb_register_write_lock(rt2x00dev, RF_CSR_CFG0, reg); - rt2x00_rf_write(rt2x00dev, word, value); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800usb_mcu_request(struct rt2x00_dev *rt2x00dev, - const u8 command, const u8 token, - const u8 arg0, const u8 arg1) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the MCU becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_MCU(rt2x00dev, ®)) { - rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); - rt2x00usb_register_write_lock(rt2x00dev, H2M_MAILBOX_CSR, reg); - - reg = 0; - rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); - rt2x00usb_register_write_lock(rt2x00dev, HOST_CMD_CSR, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -static const struct rt2x00debug rt2800usb_rt2x00debug = { - .owner = THIS_MODULE, - .csr = { - .read = rt2x00usb_register_read, - .write = rt2x00usb_register_write, - .flags = RT2X00DEBUGFS_OFFSET, - .word_base = CSR_REG_BASE, - .word_size = sizeof(u32), - .word_count = CSR_REG_SIZE / sizeof(u32), - }, - .eeprom = { - .read = rt2x00_eeprom_read, - .write = rt2x00_eeprom_write, - .word_base = EEPROM_BASE, - .word_size = sizeof(u16), - .word_count = EEPROM_SIZE / sizeof(u16), - }, - .bbp = { - .read = rt2800usb_bbp_read, - .write = rt2800usb_bbp_write, - .word_base = BBP_BASE, - .word_size = sizeof(u8), - .word_count = BBP_SIZE / sizeof(u8), - }, - .rf = { - .read = rt2x00_rf_read, - .write = rt2800usb_rf_write, - .word_base = RF_BASE, - .word_size = sizeof(u32), - .word_count = RF_SIZE / sizeof(u32), - }, -}; -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -static int rt2800usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00usb_register_read(rt2x00dev, GPIO_CTRL_CFG, ®); - return rt2x00_get_field32(reg, GPIO_CTRL_CFG_BIT2); -} - -#ifdef CONFIG_RT2X00_LIB_LEDS -static void rt2800usb_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - unsigned int enabled = brightness != LED_OFF; - unsigned int bg_mode = - (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); - unsigned int polarity = - rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, - EEPROM_FREQ_LED_POLARITY); - unsigned int ledmode = - rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, - EEPROM_FREQ_LED_MODE); - - if (led->type == LED_TYPE_RADIO) { - rt2800usb_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, - enabled ? 0x20 : 0); - } else if (led->type == LED_TYPE_ASSOC) { - rt2800usb_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, - enabled ? (bg_mode ? 0x60 : 0xa0) : 0x20); - } else if (led->type == LED_TYPE_QUALITY) { - /* - * The brightness is divided into 6 levels (0 - 5), - * The specs tell us the following levels: - * 0, 1 ,3, 7, 15, 31 - * to determine the level in a simple way we can simply - * work with bitshifting: - * (1 << level) - 1 - */ - rt2800usb_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, - (1 << brightness / (LED_FULL / 6)) - 1, - polarity); - } -} - -static int rt2800usb_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - u32 reg; - - rt2x00usb_register_read(led->rt2x00dev, LED_CFG, ®); - rt2x00_set_field32(®, LED_CFG_ON_PERIOD, *delay_on); - rt2x00_set_field32(®, LED_CFG_OFF_PERIOD, *delay_off); - rt2x00_set_field32(®, LED_CFG_SLOW_BLINK_PERIOD, 3); - rt2x00_set_field32(®, LED_CFG_R_LED_MODE, 3); - rt2x00_set_field32(®, LED_CFG_G_LED_MODE, 12); - rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, 3); - rt2x00_set_field32(®, LED_CFG_LED_POLAR, 1); - rt2x00usb_register_write(led->rt2x00dev, LED_CFG, reg); - - return 0; -} - -static void rt2800usb_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, - enum led_type type) -{ - led->rt2x00dev = rt2x00dev; - led->type = type; - led->led_dev.brightness_set = rt2800usb_brightness_set; - led->led_dev.blink_set = rt2800usb_blink_set; - led->flags = LED_INITIALIZED; -} -#endif /* CONFIG_RT2X00_LIB_LEDS */ - -/* - * Configuration handlers. - */ -static void rt2800usb_config_wcid_attr(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct mac_wcid_entry wcid_entry; - struct mac_iveiv_entry iveiv_entry; - u32 offset; - u32 reg; - - offset = MAC_WCID_ATTR_ENTRY(key->hw_key_idx); - - rt2x00usb_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, - !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, - (crypto->cmd == SET_KEY) * crypto->cipher); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX, - (crypto->cmd == SET_KEY) * crypto->bssidx); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher); - rt2x00usb_register_write(rt2x00dev, offset, reg); - - offset = MAC_IVEIV_ENTRY(key->hw_key_idx); - - memset(&iveiv_entry, 0, sizeof(iveiv_entry)); - if ((crypto->cipher == CIPHER_TKIP) || - (crypto->cipher == CIPHER_TKIP_NO_MIC) || - (crypto->cipher == CIPHER_AES)) - iveiv_entry.iv[3] |= 0x20; - iveiv_entry.iv[3] |= key->keyidx << 6; - rt2x00usb_register_multiwrite(rt2x00dev, offset, - &iveiv_entry, sizeof(iveiv_entry)); - - offset = MAC_WCID_ENTRY(key->hw_key_idx); - - memset(&wcid_entry, 0, sizeof(wcid_entry)); - if (crypto->cmd == SET_KEY) - memcpy(&wcid_entry, crypto->address, ETH_ALEN); - rt2x00usb_register_multiwrite(rt2x00dev, offset, - &wcid_entry, sizeof(wcid_entry)); -} - -static int rt2800usb_config_shared_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct hw_key_entry key_entry; - struct rt2x00_field32 field; - int timeout; - u32 offset; - u32 reg; - - if (crypto->cmd == SET_KEY) { - key->hw_key_idx = (4 * crypto->bssidx) + key->keyidx; - - memcpy(key_entry.key, crypto->key, - sizeof(key_entry.key)); - memcpy(key_entry.tx_mic, crypto->tx_mic, - sizeof(key_entry.tx_mic)); - memcpy(key_entry.rx_mic, crypto->rx_mic, - sizeof(key_entry.rx_mic)); - - offset = SHARED_KEY_ENTRY(key->hw_key_idx); - timeout = REGISTER_TIMEOUT32(sizeof(key_entry)); - rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, - offset, &key_entry, - sizeof(key_entry), - timeout); - } - - /* - * The cipher types are stored over multiple registers - * starting with SHARED_KEY_MODE_BASE each word will have - * 32 bits and contains the cipher types for 2 bssidx each. - * Using the correct defines correctly will cause overhead, - * so just calculate the correct offset. - */ - field.bit_offset = 4 * (key->hw_key_idx % 8); - field.bit_mask = 0x7 << field.bit_offset; - - offset = SHARED_KEY_MODE_ENTRY(key->hw_key_idx / 8); - - rt2x00usb_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, field, - (crypto->cmd == SET_KEY) * crypto->cipher); - rt2x00usb_register_write(rt2x00dev, offset, reg); - - /* - * Update WCID information - */ - rt2800usb_config_wcid_attr(rt2x00dev, crypto, key); - - return 0; -} - -static int rt2800usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct hw_key_entry key_entry; - int timeout; - u32 offset; - - if (crypto->cmd == SET_KEY) { - /* - * 1 pairwise key is possible per AID, this means that the AID - * equals our hw_key_idx. Make sure the WCID starts _after_ the - * last possible shared key entry. - */ - if (crypto->aid > (256 - 32)) - return -ENOSPC; - - key->hw_key_idx = 32 + crypto->aid; - - memcpy(key_entry.key, crypto->key, - sizeof(key_entry.key)); - memcpy(key_entry.tx_mic, crypto->tx_mic, - sizeof(key_entry.tx_mic)); - memcpy(key_entry.rx_mic, crypto->rx_mic, - sizeof(key_entry.rx_mic)); - - offset = PAIRWISE_KEY_ENTRY(key->hw_key_idx); - timeout = REGISTER_TIMEOUT32(sizeof(key_entry)); - rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, - offset, &key_entry, - sizeof(key_entry), - timeout); - } - - /* - * Update WCID information - */ - rt2800usb_config_wcid_attr(rt2x00dev, crypto, key); - - return 0; -} - -static void rt2800usb_config_filter(struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags) -{ - u32 reg; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2x00usb_register_read(rt2x00dev, RX_FILTER_CFG, ®); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CRC_ERROR, - !(filter_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PHY_ERROR, - !(filter_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_TO_ME, - !(filter_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_VER_ERROR, 1); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_MULTICAST, - !(filter_flags & FIF_ALLMULTI)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BROADCAST, 0); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_DUPLICATE, 1); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END_ACK, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_ACK, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CTS, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_RTS, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, - !(filter_flags & FIF_PSPOLL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, 1); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, 0); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, - !(filter_flags & FIF_CONTROL)); - rt2x00usb_register_write(rt2x00dev, RX_FILTER_CFG, reg); -} - -static void rt2800usb_config_intf(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, - const unsigned int flags) -{ - unsigned int beacon_base; - u32 reg; - - if (flags & CONFIG_UPDATE_TYPE) { - /* - * Clear current synchronisation setup. - * For the Beacon base registers we only need to clear - * the first byte since that byte contains the VALID and OWNER - * bits which (when set to 0) will invalidate the entire beacon. - */ - beacon_base = HW_BEACON_OFFSET(intf->beacon->entry_idx); - rt2x00usb_register_write(rt2x00dev, beacon_base, 0); - - /* - * Enable synchronisation. - */ - rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, conf->sync); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); - rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); - } - - if (flags & CONFIG_UPDATE_MAC) { - reg = le32_to_cpu(conf->mac[1]); - rt2x00_set_field32(®, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff); - conf->mac[1] = cpu_to_le32(reg); - - rt2x00usb_register_multiwrite(rt2x00dev, MAC_ADDR_DW0, - conf->mac, sizeof(conf->mac)); - } - - if (flags & CONFIG_UPDATE_BSSID) { - reg = le32_to_cpu(conf->bssid[1]); - rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_ID_MASK, 0); - rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 0); - conf->bssid[1] = cpu_to_le32(reg); - - rt2x00usb_register_multiwrite(rt2x00dev, MAC_BSSID_DW0, - conf->bssid, sizeof(conf->bssid)); - } -} - -static void rt2800usb_config_erp(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_erp *erp) -{ - u32 reg; - - rt2x00usb_register_read(rt2x00dev, TX_TIMEOUT_CFG, ®); - rt2x00_set_field32(®, TX_TIMEOUT_CFG_RX_ACK_TIMEOUT, 0x20); - rt2x00usb_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, AUTO_RSP_CFG, ®); - rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, - !!erp->short_preamble); - rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, - !!erp->short_preamble); - rt2x00usb_register_write(rt2x00dev, AUTO_RSP_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, OFDM_PROT_CFG, ®); - rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, - erp->cts_protection ? 2 : 0); - rt2x00usb_register_write(rt2x00dev, OFDM_PROT_CFG, reg); - - rt2x00usb_register_write(rt2x00dev, LEGACY_BASIC_RATE, - erp->basic_rates); - rt2x00usb_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); - - rt2x00usb_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); - rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, erp->slot_time); - rt2x00_set_field32(®, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2); - rt2x00usb_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, XIFS_TIME_CFG, ®); - rt2x00_set_field32(®, XIFS_TIME_CFG_CCKM_SIFS_TIME, erp->sifs); - rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_SIFS_TIME, erp->sifs); - rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4); - rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, erp->eifs); - rt2x00_set_field32(®, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1); - rt2x00usb_register_write(rt2x00dev, XIFS_TIME_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, - erp->beacon_int * 16); - rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); -} - -static void rt2800usb_config_ant(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u8 r1; - u8 r3; - - rt2800usb_bbp_read(rt2x00dev, 1, &r1); - rt2800usb_bbp_read(rt2x00dev, 3, &r3); - - /* - * Configure the TX antenna. - */ - switch ((int)ant->tx) { - case 1: - rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); - break; - case 2: - rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); - break; - case 3: - /* Do nothing */ - break; - } - - /* - * Configure the RX antenna. - */ - switch ((int)ant->rx) { - case 1: - rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0); - break; - case 2: - rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1); - break; - case 3: - rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2); - break; - } - - rt2800usb_bbp_write(rt2x00dev, 3, r3); - rt2800usb_bbp_write(rt2x00dev, 1, r1); -} - -static void rt2800usb_config_lna_gain(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u16 eeprom; - short lna_gain; - - if (libconf->rf.channel <= 14) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG); - } else if (libconf->rf.channel <= 64) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0); - } else if (libconf->rf.channel <= 128) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1); - } else { - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2); - } - - rt2x00dev->lna_gain = lna_gain; -} - -static void rt2800usb_config_channel_rt2x(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); - - if (rt2x00dev->default_ant.tx == 1) - rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_TX1, 1); - - if (rt2x00dev->default_ant.rx == 1) { - rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX1, 1); - rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); - } else if (rt2x00dev->default_ant.rx == 2) - rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); - - if (rf->channel > 14) { - /* - * When TX power is below 0, we should increase it by 7 to - * make it a positive value (Minumum value is -7). - * However this means that values between 0 and 7 have - * double meaning, and we should set a 7DBm boost flag. - */ - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST, - (info->tx_power1 >= 0)); - - if (info->tx_power1 < 0) - info->tx_power1 += 7; - - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, - TXPOWER_A_TO_DEV(info->tx_power1)); - - rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST, - (info->tx_power2 >= 0)); - - if (info->tx_power2 < 0) - info->tx_power2 += 7; - - rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, - TXPOWER_A_TO_DEV(info->tx_power2)); - } else { - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, - TXPOWER_G_TO_DEV(info->tx_power1)); - rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, - TXPOWER_G_TO_DEV(info->tx_power2)); - } - - rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf)); - - rt2800usb_rf_write(rt2x00dev, 1, rf->rf1); - rt2800usb_rf_write(rt2x00dev, 2, rf->rf2); - rt2800usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt2800usb_rf_write(rt2x00dev, 4, rf->rf4); - - udelay(200); - - rt2800usb_rf_write(rt2x00dev, 1, rf->rf1); - rt2800usb_rf_write(rt2x00dev, 2, rf->rf2); - rt2800usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); - rt2800usb_rf_write(rt2x00dev, 4, rf->rf4); - - udelay(200); - - rt2800usb_rf_write(rt2x00dev, 1, rf->rf1); - rt2800usb_rf_write(rt2x00dev, 2, rf->rf2); - rt2800usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt2800usb_rf_write(rt2x00dev, 4, rf->rf4); -} - -static void rt2800usb_config_channel_rt3x(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - u8 rfcsr; - - rt2800usb_rfcsr_write(rt2x00dev, 2, rf->rf1); - rt2800usb_rfcsr_write(rt2x00dev, 2, rf->rf3); - - rt2800usb_rfcsr_read(rt2x00dev, 6, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR6_R, rf->rf2); - rt2800usb_rfcsr_write(rt2x00dev, 6, rfcsr); - - rt2800usb_rfcsr_read(rt2x00dev, 12, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, - TXPOWER_G_TO_DEV(info->tx_power1)); - rt2800usb_rfcsr_write(rt2x00dev, 12, rfcsr); - - rt2800usb_rfcsr_read(rt2x00dev, 23, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); - rt2800usb_rfcsr_write(rt2x00dev, 23, rfcsr); - - rt2800usb_rfcsr_write(rt2x00dev, 24, - rt2x00dev->calibration[conf_is_ht40(conf)]); - - rt2800usb_rfcsr_read(rt2x00dev, 23, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); - rt2800usb_rfcsr_write(rt2x00dev, 23, rfcsr); -} - -static void rt2800usb_config_channel(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - u32 reg; - unsigned int tx_pin; - u8 bbp; - - if (rt2x00_rev(&rt2x00dev->chip) != RT3070_VERSION) - rt2800usb_config_channel_rt2x(rt2x00dev, conf, rf, info); - else - rt2800usb_config_channel_rt3x(rt2x00dev, conf, rf, info); - - /* - * Change BBP settings - */ - rt2800usb_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); - rt2800usb_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); - rt2800usb_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); - rt2800usb_bbp_write(rt2x00dev, 86, 0); - - if (rf->channel <= 14) { - if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) { - rt2800usb_bbp_write(rt2x00dev, 82, 0x62); - rt2800usb_bbp_write(rt2x00dev, 75, 0x46); - } else { - rt2800usb_bbp_write(rt2x00dev, 82, 0x84); - rt2800usb_bbp_write(rt2x00dev, 75, 0x50); - } - } else { - rt2800usb_bbp_write(rt2x00dev, 82, 0xf2); - - if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) - rt2800usb_bbp_write(rt2x00dev, 75, 0x46); - else - rt2800usb_bbp_write(rt2x00dev, 75, 0x50); - } - - rt2x00usb_register_read(rt2x00dev, TX_BAND_CFG, ®); - rt2x00_set_field32(®, TX_BAND_CFG_HT40_PLUS, conf_is_ht40_plus(conf)); - rt2x00_set_field32(®, TX_BAND_CFG_A, rf->channel > 14); - rt2x00_set_field32(®, TX_BAND_CFG_BG, rf->channel <= 14); - rt2x00usb_register_write(rt2x00dev, TX_BAND_CFG, reg); - - tx_pin = 0; - - /* Turn on unused PA or LNA when not using 1T or 1R */ - if (rt2x00dev->default_ant.tx != 1) { - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1); - } - - /* Turn on unused PA or LNA when not using 1T or 1R */ - if (rt2x00dev->default_ant.rx != 1) { - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1); - } - - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, rf->channel <= 14); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14); - - rt2x00usb_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); - - rt2800usb_bbp_read(rt2x00dev, 4, &bbp); - rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf)); - rt2800usb_bbp_write(rt2x00dev, 4, bbp); - - rt2800usb_bbp_read(rt2x00dev, 3, &bbp); - rt2x00_set_field8(&bbp, BBP3_HT40_PLUS, conf_is_ht40_plus(conf)); - rt2800usb_bbp_write(rt2x00dev, 3, bbp); - - if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) { - if (conf_is_ht40(conf)) { - rt2800usb_bbp_write(rt2x00dev, 69, 0x1a); - rt2800usb_bbp_write(rt2x00dev, 70, 0x0a); - rt2800usb_bbp_write(rt2x00dev, 73, 0x16); - } else { - rt2800usb_bbp_write(rt2x00dev, 69, 0x16); - rt2800usb_bbp_write(rt2x00dev, 70, 0x08); - rt2800usb_bbp_write(rt2x00dev, 73, 0x11); - } - } - - msleep(1); -} - -static void rt2800usb_config_txpower(struct rt2x00_dev *rt2x00dev, - const int txpower) -{ - u32 reg; - u32 value = TXPOWER_G_TO_DEV(txpower); - u8 r1; - - rt2800usb_bbp_read(rt2x00dev, 1, &r1); - rt2x00_set_field8(®, BBP1_TX_POWER, 0); - rt2800usb_bbp_write(rt2x00dev, 1, r1); - - rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_0, ®); - rt2x00_set_field32(®, TX_PWR_CFG_0_1MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_0_2MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_0_55MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_0_11MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_0_6MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_0_9MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_0_12MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_0_18MBS, value); - rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_0, reg); - - rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_1, ®); - rt2x00_set_field32(®, TX_PWR_CFG_1_24MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_1_36MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_1_48MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_1_54MBS, value); - rt2x00_set_field32(®, TX_PWR_CFG_1_MCS0, value); - rt2x00_set_field32(®, TX_PWR_CFG_1_MCS1, value); - rt2x00_set_field32(®, TX_PWR_CFG_1_MCS2, value); - rt2x00_set_field32(®, TX_PWR_CFG_1_MCS3, value); - rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_1, reg); - - rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_2, ®); - rt2x00_set_field32(®, TX_PWR_CFG_2_MCS4, value); - rt2x00_set_field32(®, TX_PWR_CFG_2_MCS5, value); - rt2x00_set_field32(®, TX_PWR_CFG_2_MCS6, value); - rt2x00_set_field32(®, TX_PWR_CFG_2_MCS7, value); - rt2x00_set_field32(®, TX_PWR_CFG_2_MCS8, value); - rt2x00_set_field32(®, TX_PWR_CFG_2_MCS9, value); - rt2x00_set_field32(®, TX_PWR_CFG_2_MCS10, value); - rt2x00_set_field32(®, TX_PWR_CFG_2_MCS11, value); - rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_2, reg); - - rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_3, ®); - rt2x00_set_field32(®, TX_PWR_CFG_3_MCS12, value); - rt2x00_set_field32(®, TX_PWR_CFG_3_MCS13, value); - rt2x00_set_field32(®, TX_PWR_CFG_3_MCS14, value); - rt2x00_set_field32(®, TX_PWR_CFG_3_MCS15, value); - rt2x00_set_field32(®, TX_PWR_CFG_3_UKNOWN1, value); - rt2x00_set_field32(®, TX_PWR_CFG_3_UKNOWN2, value); - rt2x00_set_field32(®, TX_PWR_CFG_3_UKNOWN3, value); - rt2x00_set_field32(®, TX_PWR_CFG_3_UKNOWN4, value); - rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_3, reg); - - rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_4, ®); - rt2x00_set_field32(®, TX_PWR_CFG_4_UKNOWN5, value); - rt2x00_set_field32(®, TX_PWR_CFG_4_UKNOWN6, value); - rt2x00_set_field32(®, TX_PWR_CFG_4_UKNOWN7, value); - rt2x00_set_field32(®, TX_PWR_CFG_4_UKNOWN8, value); - rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_4, reg); -} - -static void rt2800usb_config_retry_limit(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u32 reg; - - rt2x00usb_register_read(rt2x00dev, TX_RTY_CFG, ®); - rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, - libconf->conf->short_frame_max_tx_count); - rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, - libconf->conf->long_frame_max_tx_count); - rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_THRE, 2000); - rt2x00_set_field32(®, TX_RTY_CFG_NON_AGG_RTY_MODE, 0); - rt2x00_set_field32(®, TX_RTY_CFG_AGG_RTY_MODE, 0); - rt2x00_set_field32(®, TX_RTY_CFG_TX_AUTO_FB_ENABLE, 1); - rt2x00usb_register_write(rt2x00dev, TX_RTY_CFG, reg); -} - -static void rt2800usb_config_ps(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - enum dev_state state = - (libconf->conf->flags & IEEE80211_CONF_PS) ? - STATE_SLEEP : STATE_AWAKE; - u32 reg; - - if (state == STATE_SLEEP) { - rt2x00usb_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0); - - rt2x00usb_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 5); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, - libconf->conf->listen_interval - 1); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 1); - rt2x00usb_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); - - rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); - } else { - rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); - - rt2x00usb_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 0); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, 0); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 0); - rt2x00usb_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); - } -} - -static void rt2800usb_config(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int flags) -{ - /* Always recalculate LNA gain before changing configuration */ - rt2800usb_config_lna_gain(rt2x00dev, libconf); - - if (flags & IEEE80211_CONF_CHANGE_CHANNEL) - rt2800usb_config_channel(rt2x00dev, libconf->conf, - &libconf->rf, &libconf->channel); - if (flags & IEEE80211_CONF_CHANGE_POWER) - rt2800usb_config_txpower(rt2x00dev, libconf->conf->power_level); - if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - rt2800usb_config_retry_limit(rt2x00dev, libconf); - if (flags & IEEE80211_CONF_CHANGE_PS) - rt2800usb_config_ps(rt2x00dev, libconf); -} - -/* - * Link tuning - */ -static void rt2800usb_link_stats(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - u32 reg; - - /* - * Update FCS error count from register. - */ - rt2x00usb_register_read(rt2x00dev, RX_STA_CNT0, ®); - qual->rx_failed = rt2x00_get_field32(reg, RX_STA_CNT0_CRC_ERR); -} - -static u8 rt2800usb_get_default_vgc(struct rt2x00_dev *rt2x00dev) -{ - if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { - if (rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) - return 0x1c + (2 * rt2x00dev->lna_gain); - else - return 0x2e + rt2x00dev->lna_gain; - } - - if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) - return 0x32 + (rt2x00dev->lna_gain * 5) / 3; - else - return 0x3a + (rt2x00dev->lna_gain * 5) / 3; -} - -static inline void rt2800usb_set_vgc(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, u8 vgc_level) -{ - if (qual->vgc_level != vgc_level) { - rt2800usb_bbp_write(rt2x00dev, 66, vgc_level); - qual->vgc_level = vgc_level; - qual->vgc_level_reg = vgc_level; - } -} - -static void rt2800usb_reset_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - rt2800usb_set_vgc(rt2x00dev, qual, - rt2800usb_get_default_vgc(rt2x00dev)); -} - -static void rt2800usb_link_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, const u32 count) -{ - if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) - return; - - /* - * When RSSI is better then -80 increase VGC level with 0x10 - */ - rt2800usb_set_vgc(rt2x00dev, qual, - rt2800usb_get_default_vgc(rt2x00dev) + - ((qual->rssi > -80) * 0x10)); -} - -/* * Firmware functions */ static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) @@ -1172,7 +157,7 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev, * Wait for stable hardware. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); if (reg && reg != ~0) break; msleep(1); @@ -1192,8 +177,8 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev, data + offset, length, REGISTER_TIMEOUT32(length)); - rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); - rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); /* * Send firmware request to device to load firmware, @@ -1208,18 +193,18 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev, } msleep(10); - rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); /* * Send signal to firmware during boot time. */ - rt2800usb_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0xff, 0, 0); + rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0xff, 0, 0); if ((chipset == 0x3070) || (chipset == 0x3071) || (chipset == 0x3572)) { udelay(200); - rt2800usb_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0); + rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0); udelay(10); } @@ -1227,7 +212,7 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev, * Wait for device to stabilize. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00usb_register_read(rt2x00dev, PBF_SYS_CTRL, ®); + rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, ®); if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY)) break; msleep(1); @@ -1241,536 +226,14 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev, /* * Initialize firmware. */ - rt2x00usb_register_write(rt2x00dev, H2M_BBP_AGENT, 0); - rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); msleep(1); return 0; } /* - * Initialization functions. - */ -static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - unsigned int i; - - /* - * Wait untill BBP and RF are ready. - */ - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); - if (reg && reg != ~0) - break; - msleep(1); - } - - if (i == REGISTER_BUSY_COUNT) { - ERROR(rt2x00dev, "Unstable hardware.\n"); - return -EBUSY; - } - - rt2x00usb_register_read(rt2x00dev, PBF_SYS_CTRL, ®); - rt2x00usb_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000); - - rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000); - - rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, - USB_MODE_RESET, REGISTER_TIMEOUT); - - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); - - rt2x00usb_register_read(rt2x00dev, BCN_OFFSET0, ®); - rt2x00_set_field32(®, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */ - rt2x00_set_field32(®, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */ - rt2x00_set_field32(®, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */ - rt2x00_set_field32(®, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */ - rt2x00usb_register_write(rt2x00dev, BCN_OFFSET0, reg); - - rt2x00usb_register_read(rt2x00dev, BCN_OFFSET1, ®); - rt2x00_set_field32(®, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */ - rt2x00_set_field32(®, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */ - rt2x00_set_field32(®, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */ - rt2x00_set_field32(®, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */ - rt2x00usb_register_write(rt2x00dev, BCN_OFFSET1, reg); - - rt2x00usb_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f); - rt2x00usb_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); - - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); - - rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0); - rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - if (rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) { - rt2x00usb_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); - rt2x00usb_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); - rt2x00usb_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); - } else { - rt2x00usb_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000); - rt2x00usb_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); - } - - rt2x00usb_register_read(rt2x00dev, TX_LINK_CFG, ®); - rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB_LIFETIME, 32); - rt2x00_set_field32(®, TX_LINK_CFG_MFB_ENABLE, 0); - rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_UMFS_ENABLE, 0); - rt2x00_set_field32(®, TX_LINK_CFG_TX_MRQ_EN, 0); - rt2x00_set_field32(®, TX_LINK_CFG_TX_RDG_EN, 0); - rt2x00_set_field32(®, TX_LINK_CFG_TX_CF_ACK_EN, 1); - rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB, 0); - rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFS, 0); - rt2x00usb_register_write(rt2x00dev, TX_LINK_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, TX_TIMEOUT_CFG, ®); - rt2x00_set_field32(®, TX_TIMEOUT_CFG_MPDU_LIFETIME, 9); - rt2x00_set_field32(®, TX_TIMEOUT_CFG_TX_OP_TIMEOUT, 10); - rt2x00usb_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, MAX_LEN_CFG, ®); - rt2x00_set_field32(®, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE); - if (rt2x00_rev(&rt2x00dev->chip) >= RT2880E_VERSION && - rt2x00_rev(&rt2x00dev->chip) < RT3070_VERSION) - rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 2); - else - rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 1); - rt2x00_set_field32(®, MAX_LEN_CFG_MIN_PSDU, 0); - rt2x00_set_field32(®, MAX_LEN_CFG_MIN_MPDU, 0); - rt2x00usb_register_write(rt2x00dev, MAX_LEN_CFG, reg); - - rt2x00usb_register_write(rt2x00dev, PBF_MAX_PCNT, 0x1f3fbf9f); - - rt2x00usb_register_read(rt2x00dev, AUTO_RSP_CFG, ®); - rt2x00_set_field32(®, AUTO_RSP_CFG_AUTORESPONDER, 1); - rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MMODE, 0); - rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MREF, 0); - rt2x00_set_field32(®, AUTO_RSP_CFG_DUAL_CTS_EN, 0); - rt2x00_set_field32(®, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0); - rt2x00usb_register_write(rt2x00dev, AUTO_RSP_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, CCK_PROT_CFG, ®); - rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_RATE, 8); - rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_NAV, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM40, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF40, 1); - rt2x00usb_register_write(rt2x00dev, CCK_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, OFDM_PROT_CFG, ®); - rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_RATE, 8); - rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_NAV, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM40, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF40, 1); - rt2x00usb_register_write(rt2x00dev, OFDM_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, MM20_PROT_CFG, ®); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, 0x4004); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_NAV, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF40, 0); - rt2x00usb_register_write(rt2x00dev, MM20_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, MM40_PROT_CFG, ®); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, 0x4084); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_NAV, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF40, 1); - rt2x00usb_register_write(rt2x00dev, MM40_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, GF20_PROT_CFG, ®); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, 0x4004); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_NAV, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF40, 0); - rt2x00usb_register_write(rt2x00dev, GF20_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, GF40_PROT_CFG, ®); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, 0x4084); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_NAV, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1); - rt2x00usb_register_write(rt2x00dev, GF40_PROT_CFG, reg); - - rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); - - rt2x00usb_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 3); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_BIG_ENDIAN, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_HDR_SCATTER, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_HDR_SEG_LEN, 0); - rt2x00usb_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); - - rt2x00usb_register_write(rt2x00dev, TXOP_CTRL_CFG, 0x0000583f); - rt2x00usb_register_write(rt2x00dev, TXOP_HLDR_ET, 0x00000002); - - rt2x00usb_register_read(rt2x00dev, TX_RTS_CFG, ®); - rt2x00_set_field32(®, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32); - rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, - IEEE80211_MAX_RTS_THRESHOLD); - rt2x00_set_field32(®, TX_RTS_CFG_RTS_FBK_EN, 0); - rt2x00usb_register_write(rt2x00dev, TX_RTS_CFG, reg); - - rt2x00usb_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca); - rt2x00usb_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); - - /* - * ASIC will keep garbage value after boot, clear encryption keys. - */ - for (i = 0; i < 4; i++) - rt2x00usb_register_write(rt2x00dev, - SHARED_KEY_MODE_ENTRY(i), 0); - - for (i = 0; i < 256; i++) { - u32 wcid[2] = { 0xffffffff, 0x00ffffff }; - rt2x00usb_register_multiwrite(rt2x00dev, MAC_WCID_ENTRY(i), - wcid, sizeof(wcid)); - - rt2x00usb_register_write(rt2x00dev, MAC_WCID_ATTR_ENTRY(i), 1); - rt2x00usb_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0); - } - - /* - * Clear all beacons - * For the Beacon base registers we only need to clear - * the first byte since that byte contains the VALID and OWNER - * bits which (when set to 0) will invalidate the entire beacon. - */ - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE4, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE5, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE6, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE7, 0); - - rt2x00usb_register_read(rt2x00dev, USB_CYC_CFG, ®); - rt2x00_set_field32(®, USB_CYC_CFG_CLOCK_CYCLE, 30); - rt2x00usb_register_write(rt2x00dev, USB_CYC_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, HT_FBK_CFG0, ®); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS0FBK, 0); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS1FBK, 0); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS2FBK, 1); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS3FBK, 2); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS4FBK, 3); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS5FBK, 4); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS6FBK, 5); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS7FBK, 6); - rt2x00usb_register_write(rt2x00dev, HT_FBK_CFG0, reg); - - rt2x00usb_register_read(rt2x00dev, HT_FBK_CFG1, ®); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS8FBK, 8); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS9FBK, 8); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS10FBK, 9); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS11FBK, 10); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS12FBK, 11); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS13FBK, 12); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS14FBK, 13); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS15FBK, 14); - rt2x00usb_register_write(rt2x00dev, HT_FBK_CFG1, reg); - - rt2x00usb_register_read(rt2x00dev, LG_FBK_CFG0, ®); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS0FBK, 8); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS1FBK, 8); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS2FBK, 9); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS3FBK, 10); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS4FBK, 11); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS5FBK, 12); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS6FBK, 13); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS7FBK, 14); - rt2x00usb_register_write(rt2x00dev, LG_FBK_CFG0, reg); - - rt2x00usb_register_read(rt2x00dev, LG_FBK_CFG1, ®); - rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS0FBK, 0); - rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS1FBK, 0); - rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS2FBK, 1); - rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS3FBK, 2); - rt2x00usb_register_write(rt2x00dev, LG_FBK_CFG1, reg); - - /* - * We must clear the error counters. - * These registers are cleared on read, - * so we may pass a useless variable to store the value. - */ - rt2x00usb_register_read(rt2x00dev, RX_STA_CNT0, ®); - rt2x00usb_register_read(rt2x00dev, RX_STA_CNT1, ®); - rt2x00usb_register_read(rt2x00dev, RX_STA_CNT2, ®); - rt2x00usb_register_read(rt2x00dev, TX_STA_CNT0, ®); - rt2x00usb_register_read(rt2x00dev, TX_STA_CNT1, ®); - rt2x00usb_register_read(rt2x00dev, TX_STA_CNT2, ®); - - return 0; -} - -static int rt2800usb_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u32 reg; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00usb_register_read(rt2x00dev, MAC_STATUS_CFG, ®); - if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY)) - return 0; - - udelay(REGISTER_BUSY_DELAY); - } - - ERROR(rt2x00dev, "BBP/RF register access failed, aborting.\n"); - return -EACCES; -} - -static int rt2800usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u8 value; - - /* - * BBP was enabled after firmware was loaded, - * but we need to reactivate it now. - */ - rt2x00usb_register_write(rt2x00dev, H2M_BBP_AGENT, 0); - rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - msleep(1); - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800usb_bbp_read(rt2x00dev, 0, &value); - if ((value != 0xff) && (value != 0x00)) - return 0; - udelay(REGISTER_BUSY_DELAY); - } - - ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); - return -EACCES; -} - -static int rt2800usb_init_bbp(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u16 eeprom; - u8 reg_id; - u8 value; - - if (unlikely(rt2800usb_wait_bbp_rf_ready(rt2x00dev) || - rt2800usb_wait_bbp_ready(rt2x00dev))) - return -EACCES; - - rt2800usb_bbp_write(rt2x00dev, 65, 0x2c); - rt2800usb_bbp_write(rt2x00dev, 66, 0x38); - rt2800usb_bbp_write(rt2x00dev, 69, 0x12); - rt2800usb_bbp_write(rt2x00dev, 70, 0x0a); - rt2800usb_bbp_write(rt2x00dev, 73, 0x10); - rt2800usb_bbp_write(rt2x00dev, 81, 0x37); - rt2800usb_bbp_write(rt2x00dev, 82, 0x62); - rt2800usb_bbp_write(rt2x00dev, 83, 0x6a); - rt2800usb_bbp_write(rt2x00dev, 84, 0x99); - rt2800usb_bbp_write(rt2x00dev, 86, 0x00); - rt2800usb_bbp_write(rt2x00dev, 91, 0x04); - rt2800usb_bbp_write(rt2x00dev, 92, 0x00); - rt2800usb_bbp_write(rt2x00dev, 103, 0x00); - rt2800usb_bbp_write(rt2x00dev, 105, 0x05); - - if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) { - rt2800usb_bbp_write(rt2x00dev, 69, 0x16); - rt2800usb_bbp_write(rt2x00dev, 73, 0x12); - } - - if (rt2x00_rev(&rt2x00dev->chip) > RT2860D_VERSION) { - rt2800usb_bbp_write(rt2x00dev, 84, 0x19); - } - - if (rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) { - rt2800usb_bbp_write(rt2x00dev, 70, 0x0a); - rt2800usb_bbp_write(rt2x00dev, 84, 0x99); - rt2800usb_bbp_write(rt2x00dev, 105, 0x05); - } - - for (i = 0; i < EEPROM_BBP_SIZE; i++) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); - - if (eeprom != 0xffff && eeprom != 0x0000) { - reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); - value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - rt2800usb_bbp_write(rt2x00dev, reg_id, value); - } - } - - return 0; -} - -static u8 rt2800usb_init_rx_filter(struct rt2x00_dev *rt2x00dev, - bool bw40, u8 rfcsr24, u8 filter_target) -{ - unsigned int i; - u8 bbp; - u8 rfcsr; - u8 passband; - u8 stopband; - u8 overtuned = 0; - - rt2800usb_rfcsr_write(rt2x00dev, 24, rfcsr24); - - rt2800usb_bbp_read(rt2x00dev, 4, &bbp); - rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * bw40); - rt2800usb_bbp_write(rt2x00dev, 4, bbp); - - rt2800usb_rfcsr_read(rt2x00dev, 22, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 1); - rt2800usb_rfcsr_write(rt2x00dev, 22, rfcsr); - - /* - * Set power & frequency of passband test tone - */ - rt2800usb_bbp_write(rt2x00dev, 24, 0); - - for (i = 0; i < 100; i++) { - rt2800usb_bbp_write(rt2x00dev, 25, 0x90); - msleep(1); - - rt2800usb_bbp_read(rt2x00dev, 55, &passband); - if (passband) - break; - } - - /* - * Set power & frequency of stopband test tone - */ - rt2800usb_bbp_write(rt2x00dev, 24, 0x06); - - for (i = 0; i < 100; i++) { - rt2800usb_bbp_write(rt2x00dev, 25, 0x90); - msleep(1); - - rt2800usb_bbp_read(rt2x00dev, 55, &stopband); - - if ((passband - stopband) <= filter_target) { - rfcsr24++; - overtuned += ((passband - stopband) == filter_target); - } else - break; - - rt2800usb_rfcsr_write(rt2x00dev, 24, rfcsr24); - } - - rfcsr24 -= !!overtuned; - - rt2800usb_rfcsr_write(rt2x00dev, 24, rfcsr24); - return rfcsr24; -} - -static int rt2800usb_init_rfcsr(struct rt2x00_dev *rt2x00dev) -{ - u8 rfcsr; - u8 bbp; - - if (rt2x00_rev(&rt2x00dev->chip) != RT3070_VERSION) - return 0; - - /* - * Init RF calibration. - */ - rt2800usb_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); - rt2800usb_rfcsr_write(rt2x00dev, 30, rfcsr); - msleep(1); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); - rt2800usb_rfcsr_write(rt2x00dev, 30, rfcsr); - - rt2800usb_rfcsr_write(rt2x00dev, 4, 0x40); - rt2800usb_rfcsr_write(rt2x00dev, 5, 0x03); - rt2800usb_rfcsr_write(rt2x00dev, 6, 0x02); - rt2800usb_rfcsr_write(rt2x00dev, 7, 0x70); - rt2800usb_rfcsr_write(rt2x00dev, 9, 0x0f); - rt2800usb_rfcsr_write(rt2x00dev, 10, 0x71); - rt2800usb_rfcsr_write(rt2x00dev, 11, 0x21); - rt2800usb_rfcsr_write(rt2x00dev, 12, 0x7b); - rt2800usb_rfcsr_write(rt2x00dev, 14, 0x90); - rt2800usb_rfcsr_write(rt2x00dev, 15, 0x58); - rt2800usb_rfcsr_write(rt2x00dev, 16, 0xb3); - rt2800usb_rfcsr_write(rt2x00dev, 17, 0x92); - rt2800usb_rfcsr_write(rt2x00dev, 18, 0x2c); - rt2800usb_rfcsr_write(rt2x00dev, 19, 0x02); - rt2800usb_rfcsr_write(rt2x00dev, 20, 0xba); - rt2800usb_rfcsr_write(rt2x00dev, 21, 0xdb); - rt2800usb_rfcsr_write(rt2x00dev, 24, 0x16); - rt2800usb_rfcsr_write(rt2x00dev, 25, 0x01); - rt2800usb_rfcsr_write(rt2x00dev, 27, 0x03); - rt2800usb_rfcsr_write(rt2x00dev, 29, 0x1f); - - /* - * Set RX Filter calibration for 20MHz and 40MHz - */ - rt2x00dev->calibration[0] = - rt2800usb_init_rx_filter(rt2x00dev, false, 0x07, 0x16); - rt2x00dev->calibration[1] = - rt2800usb_init_rx_filter(rt2x00dev, true, 0x27, 0x19); - - /* - * Set back to initial state - */ - rt2800usb_bbp_write(rt2x00dev, 24, 0); - - rt2800usb_rfcsr_read(rt2x00dev, 22, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 0); - rt2800usb_rfcsr_write(rt2x00dev, 22, rfcsr); - - /* - * set BBP back to BW20 - */ - rt2800usb_bbp_read(rt2x00dev, 4, &bbp); - rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 0); - rt2800usb_bbp_write(rt2x00dev, 4, bbp); - - return 0; -} - -/* * Device state switch handlers. */ static void rt2800usb_toggle_rx(struct rt2x00_dev *rt2x00dev, @@ -1778,11 +241,11 @@ static void rt2800usb_toggle_rx(struct rt2x00_dev *rt2x00dev, { u32 reg; - rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, (state == STATE_RADIO_RX_ON) || (state == STATE_RADIO_RX_ON_LINK)); - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); } static int rt2800usb_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) @@ -1791,7 +254,7 @@ static int rt2800usb_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) u32 reg; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00usb_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) return 0; @@ -1812,25 +275,25 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) * Initialize all registers. */ if (unlikely(rt2800usb_wait_wpdma_ready(rt2x00dev) || - rt2800usb_init_registers(rt2x00dev) || - rt2800usb_init_bbp(rt2x00dev) || - rt2800usb_init_rfcsr(rt2x00dev))) + rt2800_init_registers(rt2x00dev) || + rt2800_init_bbp(rt2x00dev) || + rt2800_init_rfcsr(rt2x00dev))) return -EIO; - rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); udelay(50); - rt2x00usb_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1); - rt2x00usb_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); - rt2x00usb_register_read(rt2x00dev, USB_DMA_CFG, ®); + rt2800_register_read(rt2x00dev, USB_DMA_CFG, ®); rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0); /* Don't use bulk in aggregation when working with USB 1.1 */ rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, @@ -1844,26 +307,26 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) ((RX_ENTRIES * DATA_FRAME_SIZE) / 1024) - 3); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_EN, 1); rt2x00_set_field32(®, USB_DMA_CFG_TX_BULK_EN, 1); - rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, reg); + rt2800_register_write(rt2x00dev, USB_DMA_CFG, reg); - rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); /* * Initialize LED control */ rt2x00_eeprom_read(rt2x00dev, EEPROM_LED1, &word); - rt2800usb_mcu_request(rt2x00dev, MCU_LED_1, 0xff, + rt2800_mcu_request(rt2x00dev, MCU_LED_1, 0xff, word & 0xff, (word >> 8) & 0xff); rt2x00_eeprom_read(rt2x00dev, EEPROM_LED2, &word); - rt2800usb_mcu_request(rt2x00dev, MCU_LED_2, 0xff, + rt2800_mcu_request(rt2x00dev, MCU_LED_2, 0xff, word & 0xff, (word >> 8) & 0xff); rt2x00_eeprom_read(rt2x00dev, EEPROM_LED3, &word); - rt2800usb_mcu_request(rt2x00dev, MCU_LED_3, 0xff, + rt2800_mcu_request(rt2x00dev, MCU_LED_3, 0xff, word & 0xff, (word >> 8) & 0xff); return 0; @@ -1873,14 +336,14 @@ static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt2x00usb_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); - rt2x00usb_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0); - rt2x00usb_register_write(rt2x00dev, PWR_PIN_CFG, 0); - rt2x00usb_register_write(rt2x00dev, TX_PIN_CFG, 0); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0); + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); /* Wait for DMA, ignore error */ rt2800usb_wait_wpdma_ready(rt2x00dev); @@ -1892,9 +355,9 @@ static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { if (state == STATE_AWAKE) - rt2800usb_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 0); + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 0); else - rt2800usb_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 2); + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 2); return 0; } @@ -2048,9 +511,9 @@ static void rt2800usb_write_beacon(struct queue_entry *entry) * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ - rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); /* * Write entire beacon with descriptor to register. @@ -2093,12 +556,12 @@ static void rt2800usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, return; } - rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); if (!rt2x00_get_field32(reg, BCN_TIME_CFG_BEACON_GEN)) { rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); - rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); } } @@ -2124,7 +587,7 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, */ memcpy(skbdesc->desc, rxd, skbdesc->desc_len); rxd = (__le32 *)skbdesc->desc; - rxwi = &rxd[RXD_DESC_SIZE / sizeof(__le32)]; + rxwi = &rxd[RXINFO_DESC_SIZE / sizeof(__le32)]; /* * It is now safe to read the descriptor on all architectures. @@ -2135,16 +598,16 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, rt2x00_desc_read(rxwi, 2, &rxwi2); rt2x00_desc_read(rxwi, 3, &rxwi3); - if (rt2x00_get_field32(rxd0, RXD_W0_CRC_ERROR)) + if (rt2x00_get_field32(rxd0, RXINFO_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) { rxdesc->cipher = rt2x00_get_field32(rxwi0, RXWI_W0_UDF); rxdesc->cipher_status = - rt2x00_get_field32(rxd0, RXD_W0_CIPHER_ERROR); + rt2x00_get_field32(rxd0, RXINFO_W0_CIPHER_ERROR); } - if (rt2x00_get_field32(rxd0, RXD_W0_DECRYPTED)) { + if (rt2x00_get_field32(rxd0, RXINFO_W0_DECRYPTED)) { /* * Hardware has stripped IV/EIV data from 802.11 frame during * decryption. Unfortunately the descriptor doesn't contain @@ -2159,10 +622,10 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, rxdesc->flags |= RX_FLAG_MMIC_ERROR; } - if (rt2x00_get_field32(rxd0, RXD_W0_MY_BSS)) + if (rt2x00_get_field32(rxd0, RXINFO_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; - if (rt2x00_get_field32(rxd0, RXD_W0_L2PAD)) { + if (rt2x00_get_field32(rxd0, RXINFO_W0_L2PAD)) { rxdesc->dev_flags |= RXDONE_L2PAD; skbdesc->flags |= SKBDESC_L2_PADDED; } @@ -2208,402 +671,33 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, */ static int rt2800usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) { - u16 word; - u8 *mac; - u8 default_lna_gain; - - rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); - - /* - * Start validation of the data that has been read. - */ - mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - random_ether_addr(mac); - EEPROM(rt2x00dev, "MAC: %pM\n", mac); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_ANTENNA_RXPATH, 2); - rt2x00_set_field16(&word, EEPROM_ANTENNA_TXPATH, 1); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2820); - rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); - } else if (rt2x00_rev(&rt2x00dev->chip) < RT2883_VERSION) { - /* - * There is a max of 2 RX streams for RT2870 series - */ - if (rt2x00_get_field16(word, EEPROM_ANTENNA_RXPATH) > 2) - rt2x00_set_field16(&word, EEPROM_ANTENNA_RXPATH, 2); - rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_NIC_HW_RADIO, 0); - rt2x00_set_field16(&word, EEPROM_NIC_DYNAMIC_TX_AGC, 0); - rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_BG, 0); - rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_A, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); - rt2x00_set_field16(&word, EEPROM_NIC_BW40M_SB_BG, 0); - rt2x00_set_field16(&word, EEPROM_NIC_BW40M_SB_A, 0); - rt2x00_set_field16(&word, EEPROM_NIC_WPS_PBC, 0); - rt2x00_set_field16(&word, EEPROM_NIC_BW40M_BG, 0); - rt2x00_set_field16(&word, EEPROM_NIC_BW40M_A, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); - if ((word & 0x00ff) == 0x00ff) { - rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); - rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE, - LED_MODE_TXRX_ACTIVITY); - rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); - rt2x00_eeprom_write(rt2x00dev, EEPROM_LED1, 0x5555); - rt2x00_eeprom_write(rt2x00dev, EEPROM_LED2, 0x2221); - rt2x00_eeprom_write(rt2x00dev, EEPROM_LED3, 0xa9f8); - EEPROM(rt2x00dev, "Freq: 0x%04x\n", word); - } - - /* - * During the LNA validation we are going to use - * lna0 as correct value. Note that EEPROM_LNA - * is never validated. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word); - default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0); - if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 || - rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff) - rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1, - default_lna_gain); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0); - if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 || - rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff) - rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2, - default_lna_gain); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); + if (rt2800_efuse_detect(rt2x00dev)) + rt2800_read_eeprom_efuse(rt2x00dev); + else + rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, + EEPROM_SIZE); - return 0; + return rt2800_validate_eeprom(rt2x00dev); } -static int rt2800usb_init_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 value; - u16 eeprom; - - /* - * Read EEPROM word for configuration. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); - - /* - * Identify RF chipset. - */ - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip(rt2x00dev, RT2870, value, reg); - - /* - * The check for rt2860 is not a typo, some rt2870 hardware - * identifies itself as rt2860 in the CSR register. - */ - if (!rt2x00_check_rev(&rt2x00dev->chip, 0xfff00000, 0x28600000) && - !rt2x00_check_rev(&rt2x00dev->chip, 0xfff00000, 0x28700000) && - !rt2x00_check_rev(&rt2x00dev->chip, 0xfff00000, 0x28800000) && - !rt2x00_check_rev(&rt2x00dev->chip, 0xffff0000, 0x30700000)) { - ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); - return -ENODEV; - } - - if (!rt2x00_rf(&rt2x00dev->chip, RF2820) && - !rt2x00_rf(&rt2x00dev->chip, RF2850) && - !rt2x00_rf(&rt2x00dev->chip, RF2720) && - !rt2x00_rf(&rt2x00dev->chip, RF2750) && - !rt2x00_rf(&rt2x00dev->chip, RF3020) && - !rt2x00_rf(&rt2x00dev->chip, RF2020)) { - ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); - return -ENODEV; - } - - /* - * Identify default antenna configuration. - */ - rt2x00dev->default_ant.tx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TXPATH); - rt2x00dev->default_ant.rx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RXPATH); - - /* - * Read frequency offset and RF programming sequence. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); - rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); - - /* - * Read external LNA informations. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); - - if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A)) - __set_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG)) - __set_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags); - - /* - * Detect if this device has an hardware controlled radio. - */ - if (rt2x00_get_field16(eeprom, EEPROM_NIC_HW_RADIO)) - __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); - - /* - * Store led settings, for correct led behaviour. - */ -#ifdef CONFIG_RT2X00_LIB_LEDS - rt2800usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); - rt2800usb_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); - rt2800usb_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); +static const struct rt2800_ops rt2800usb_rt2800_ops = { + .register_read = rt2x00usb_register_read, + .register_read_lock = rt2x00usb_register_read_lock, + .register_write = rt2x00usb_register_write, + .register_write_lock = rt2x00usb_register_write_lock, - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, - &rt2x00dev->led_mcu_reg); -#endif /* CONFIG_RT2X00_LIB_LEDS */ + .register_multiread = rt2x00usb_register_multiread, + .register_multiwrite = rt2x00usb_register_multiwrite, - return 0; -} - -/* - * RF value list for rt2870 - * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750) - */ -static const struct rf_channel rf_vals[] = { - { 1, 0x18402ecc, 0x184c0786, 0x1816b455, 0x1800510b }, - { 2, 0x18402ecc, 0x184c0786, 0x18168a55, 0x1800519f }, - { 3, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800518b }, - { 4, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800519f }, - { 5, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800518b }, - { 6, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800519f }, - { 7, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800518b }, - { 8, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800519f }, - { 9, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800518b }, - { 10, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800519f }, - { 11, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800518b }, - { 12, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800519f }, - { 13, 0x18402ecc, 0x184c079e, 0x18168a55, 0x1800518b }, - { 14, 0x18402ecc, 0x184c07a2, 0x18168a55, 0x18005193 }, - - /* 802.11 UNI / HyperLan 2 */ - { 36, 0x18402ecc, 0x184c099a, 0x18158a55, 0x180ed1a3 }, - { 38, 0x18402ecc, 0x184c099e, 0x18158a55, 0x180ed193 }, - { 40, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed183 }, - { 44, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed1a3 }, - { 46, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed18b }, - { 48, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed19b }, - { 52, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed193 }, - { 54, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed1a3 }, - { 56, 0x18402ec8, 0x184c068e, 0x18158a55, 0x180ed18b }, - { 60, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed183 }, - { 62, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed193 }, - { 64, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed1a3 }, - - /* 802.11 HyperLan 2 */ - { 100, 0x18402ec8, 0x184c06b2, 0x18178a55, 0x180ed783 }, - { 102, 0x18402ec8, 0x184c06b2, 0x18578a55, 0x180ed793 }, - { 104, 0x18402ec8, 0x185c06b2, 0x18578a55, 0x180ed1a3 }, - { 108, 0x18402ecc, 0x185c0a32, 0x18578a55, 0x180ed193 }, - { 110, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed183 }, - { 112, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed19b }, - { 116, 0x18402ecc, 0x184c0a3a, 0x18178a55, 0x180ed1a3 }, - { 118, 0x18402ecc, 0x184c0a3e, 0x18178a55, 0x180ed193 }, - { 120, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed183 }, - { 124, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed193 }, - { 126, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed15b }, - { 128, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed1a3 }, - { 132, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed18b }, - { 134, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed193 }, - { 136, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed19b }, - { 140, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed183 }, - - /* 802.11 UNII */ - { 149, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed1a7 }, - { 151, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed187 }, - { 153, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed18f }, - { 157, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed19f }, - { 159, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed1a7 }, - { 161, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed187 }, - { 165, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed197 }, - { 167, 0x18402ec4, 0x184c03d2, 0x18179855, 0x1815531f }, - { 169, 0x18402ec4, 0x184c03d2, 0x18179855, 0x18155327 }, - { 171, 0x18402ec4, 0x184c03d6, 0x18179855, 0x18155307 }, - { 173, 0x18402ec4, 0x184c03d6, 0x18179855, 0x1815530f }, - - /* 802.11 Japan */ - { 184, 0x15002ccc, 0x1500491e, 0x1509be55, 0x150c0a0b }, - { 188, 0x15002ccc, 0x15004922, 0x1509be55, 0x150c0a13 }, - { 192, 0x15002ccc, 0x15004926, 0x1509be55, 0x150c0a1b }, - { 196, 0x15002ccc, 0x1500492a, 0x1509be55, 0x150c0a23 }, - { 208, 0x15002ccc, 0x1500493a, 0x1509be55, 0x150c0a13 }, - { 212, 0x15002ccc, 0x1500493e, 0x1509be55, 0x150c0a1b }, - { 216, 0x15002ccc, 0x15004982, 0x1509be55, 0x150c0a23 }, -}; - -/* - * RF value list for rt3070 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_3070[] = { - {1, 241, 2, 2 }, - {2, 241, 2, 7 }, - {3, 242, 2, 2 }, - {4, 242, 2, 7 }, - {5, 243, 2, 2 }, - {6, 243, 2, 7 }, - {7, 244, 2, 2 }, - {8, 244, 2, 7 }, - {9, 245, 2, 2 }, - {10, 245, 2, 7 }, - {11, 246, 2, 2 }, - {12, 246, 2, 7 }, - {13, 247, 2, 2 }, - {14, 248, 2, 4 }, + .regbusy_read = rt2x00usb_regbusy_read, }; -static int rt2800usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - struct channel_info *info; - char *tx_power1; - char *tx_power2; - unsigned int i; - u16 eeprom; - - /* - * Initialize all hw fields. - */ - rt2x00dev->hw->flags = - IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_PS_NULLFUNC_STACK; - rt2x00dev->hw->extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE; - - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); - SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, - rt2x00_eeprom_addr(rt2x00dev, - EEPROM_MAC_ADDR_0)); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); - - /* - * Initialize HT information. - */ - spec->ht.ht_supported = true; - spec->ht.cap = - IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_TX_STBC | - IEEE80211_HT_CAP_RX_STBC | - IEEE80211_HT_CAP_PSMP_SUPPORT; - spec->ht.ampdu_factor = 3; - spec->ht.ampdu_density = 4; - spec->ht.mcs.tx_params = - IEEE80211_HT_MCS_TX_DEFINED | - IEEE80211_HT_MCS_TX_RX_DIFF | - ((rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TXPATH) - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - - switch (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RXPATH)) { - case 3: - spec->ht.mcs.rx_mask[2] = 0xff; - case 2: - spec->ht.mcs.rx_mask[1] = 0xff; - case 1: - spec->ht.mcs.rx_mask[0] = 0xff; - spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */ - break; - } - - /* - * Initialize hw_mode information. - */ - spec->supported_bands = SUPPORT_BAND_2GHZ; - spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - - if (rt2x00_rf(&rt2x00dev->chip, RF2820) || - rt2x00_rf(&rt2x00dev->chip, RF2720)) { - spec->num_channels = 14; - spec->channels = rf_vals; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2850) || - rt2x00_rf(&rt2x00dev->chip, RF2750)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; - spec->num_channels = ARRAY_SIZE(rf_vals); - spec->channels = rf_vals; - } else if (rt2x00_rf(&rt2x00dev->chip, RF3020) || - rt2x00_rf(&rt2x00dev->chip, RF2020)) { - spec->num_channels = ARRAY_SIZE(rf_vals_3070); - spec->channels = rf_vals_3070; - } - - /* - * Create channel information array - */ - info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - spec->channels_info = info; - - tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); - tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); - - for (i = 0; i < 14; i++) { - info[i].tx_power1 = TXPOWER_G_FROM_DEV(tx_power1[i]); - info[i].tx_power2 = TXPOWER_G_FROM_DEV(tx_power2[i]); - } - - if (spec->num_channels > 14) { - tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1); - tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2); - - for (i = 14; i < spec->num_channels; i++) { - info[i].tx_power1 = TXPOWER_A_FROM_DEV(tx_power1[i]); - info[i].tx_power2 = TXPOWER_A_FROM_DEV(tx_power2[i]); - } - } - - return 0; -} - static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; + rt2x00dev->priv = (void *)&rt2800usb_rt2800_ops; + /* * Allocate eeprom data. */ @@ -2611,14 +705,14 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) if (retval) return retval; - retval = rt2800usb_init_eeprom(rt2x00dev); + retval = rt2800_init_eeprom(rt2x00dev); if (retval) return retval; /* * Initialize hw specifications. */ - retval = rt2800usb_probe_hw_mode(rt2x00dev); + retval = rt2800_probe_hw_mode(rt2x00dev); if (retval) return retval; @@ -2645,162 +739,6 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) return 0; } -/* - * IEEE80211 stack callback functions. - */ -static void rt2800usb_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, - u32 *iv32, u16 *iv16) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct mac_iveiv_entry iveiv_entry; - u32 offset; - - offset = MAC_IVEIV_ENTRY(hw_key_idx); - rt2x00usb_register_multiread(rt2x00dev, offset, - &iveiv_entry, sizeof(iveiv_entry)); - - memcpy(&iveiv_entry.iv[0], iv16, sizeof(iv16)); - memcpy(&iveiv_entry.iv[4], iv32, sizeof(iv32)); -} - -static int rt2800usb_set_rts_threshold(struct ieee80211_hw *hw, u32 value) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; - bool enabled = (value < IEEE80211_MAX_RTS_THRESHOLD); - - rt2x00usb_register_read(rt2x00dev, TX_RTS_CFG, ®); - rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, value); - rt2x00usb_register_write(rt2x00dev, TX_RTS_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, CCK_PROT_CFG, ®); - rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, enabled); - rt2x00usb_register_write(rt2x00dev, CCK_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, OFDM_PROT_CFG, ®); - rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, enabled); - rt2x00usb_register_write(rt2x00dev, OFDM_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, MM20_PROT_CFG, ®); - rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, enabled); - rt2x00usb_register_write(rt2x00dev, MM20_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, MM40_PROT_CFG, ®); - rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, enabled); - rt2x00usb_register_write(rt2x00dev, MM40_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, GF20_PROT_CFG, ®); - rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, enabled); - rt2x00usb_register_write(rt2x00dev, GF20_PROT_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, GF40_PROT_CFG, ®); - rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, enabled); - rt2x00usb_register_write(rt2x00dev, GF40_PROT_CFG, reg); - - return 0; -} - -static int rt2800usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, - const struct ieee80211_tx_queue_params *params) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_queue *queue; - struct rt2x00_field32 field; - int retval; - u32 reg; - u32 offset; - - /* - * First pass the configuration through rt2x00lib, that will - * update the queue settings and validate the input. After that - * we are free to update the registers based on the value - * in the queue parameter. - */ - retval = rt2x00mac_conf_tx(hw, queue_idx, params); - if (retval) - return retval; - - /* - * We only need to perform additional register initialization - * for WMM queues/ - */ - if (queue_idx >= 4) - return 0; - - queue = rt2x00queue_get_queue(rt2x00dev, queue_idx); - - /* Update WMM TXOP register */ - offset = WMM_TXOP0_CFG + (sizeof(u32) * (!!(queue_idx & 2))); - field.bit_offset = (queue_idx & 1) * 16; - field.bit_mask = 0xffff << field.bit_offset; - - rt2x00usb_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, field, queue->txop); - rt2x00usb_register_write(rt2x00dev, offset, reg); - - /* Update WMM registers */ - field.bit_offset = queue_idx * 4; - field.bit_mask = 0xf << field.bit_offset; - - rt2x00usb_register_read(rt2x00dev, WMM_AIFSN_CFG, ®); - rt2x00_set_field32(®, field, queue->aifs); - rt2x00usb_register_write(rt2x00dev, WMM_AIFSN_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, WMM_CWMIN_CFG, ®); - rt2x00_set_field32(®, field, queue->cw_min); - rt2x00usb_register_write(rt2x00dev, WMM_CWMIN_CFG, reg); - - rt2x00usb_register_read(rt2x00dev, WMM_CWMAX_CFG, ®); - rt2x00_set_field32(®, field, queue->cw_max); - rt2x00usb_register_write(rt2x00dev, WMM_CWMAX_CFG, reg); - - /* Update EDCA registers */ - offset = EDCA_AC0_CFG + (sizeof(u32) * queue_idx); - - rt2x00usb_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, EDCA_AC0_CFG_TX_OP, queue->txop); - rt2x00_set_field32(®, EDCA_AC0_CFG_AIFSN, queue->aifs); - rt2x00_set_field32(®, EDCA_AC0_CFG_CWMIN, queue->cw_min); - rt2x00_set_field32(®, EDCA_AC0_CFG_CWMAX, queue->cw_max); - rt2x00usb_register_write(rt2x00dev, offset, reg); - - return 0; -} - -static u64 rt2800usb_get_tsf(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u64 tsf; - u32 reg; - - rt2x00usb_register_read(rt2x00dev, TSF_TIMER_DW1, ®); - tsf = (u64) rt2x00_get_field32(reg, TSF_TIMER_DW1_HIGH_WORD) << 32; - rt2x00usb_register_read(rt2x00dev, TSF_TIMER_DW0, ®); - tsf |= rt2x00_get_field32(reg, TSF_TIMER_DW0_LOW_WORD); - - return tsf; -} - -static const struct ieee80211_ops rt2800usb_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .set_tim = rt2x00mac_set_tim, - .set_key = rt2x00mac_set_key, - .get_stats = rt2x00mac_get_stats, - .get_tkip_seq = rt2800usb_get_tkip_seq, - .set_rts_threshold = rt2800usb_set_rts_threshold, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2800usb_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, - .get_tsf = rt2800usb_get_tsf, - .rfkill_poll = rt2x00mac_rfkill_poll, -}; - static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { .probe_hw = rt2800usb_probe_hw, .get_firmware_name = rt2800usb_get_firmware_name, @@ -2810,10 +748,10 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { .uninitialize = rt2x00usb_uninitialize, .clear_entry = rt2x00usb_clear_entry, .set_device_state = rt2800usb_set_device_state, - .rfkill_poll = rt2800usb_rfkill_poll, - .link_stats = rt2800usb_link_stats, - .reset_tuner = rt2800usb_reset_tuner, - .link_tuner = rt2800usb_link_tuner, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, .write_tx_desc = rt2800usb_write_tx_desc, .write_tx_data = rt2x00usb_write_tx_data, .write_beacon = rt2800usb_write_beacon, @@ -2821,19 +759,19 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { .kick_tx_queue = rt2800usb_kick_tx_queue, .kill_tx_queue = rt2x00usb_kill_tx_queue, .fill_rxdone = rt2800usb_fill_rxdone, - .config_shared_key = rt2800usb_config_shared_key, - .config_pairwise_key = rt2800usb_config_pairwise_key, - .config_filter = rt2800usb_config_filter, - .config_intf = rt2800usb_config_intf, - .config_erp = rt2800usb_config_erp, - .config_ant = rt2800usb_config_ant, - .config = rt2800usb_config, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, }; static const struct data_queue_desc rt2800usb_queue_rx = { .entry_num = RX_ENTRIES, .data_size = AGGREGATION_SIZE, - .desc_size = RXD_DESC_SIZE + RXWI_DESC_SIZE, + .desc_size = RXINFO_DESC_SIZE + RXWI_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; @@ -2852,19 +790,20 @@ static const struct data_queue_desc rt2800usb_queue_bcn = { }; static const struct rt2x00_ops rt2800usb_ops = { - .name = KBUILD_MODNAME, - .max_sta_intf = 1, - .max_ap_intf = 8, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .rx = &rt2800usb_queue_rx, - .tx = &rt2800usb_queue_tx, - .bcn = &rt2800usb_queue_bcn, - .lib = &rt2800usb_rt2x00_ops, - .hw = &rt2800usb_mac80211_ops, + .name = KBUILD_MODNAME, + .max_sta_intf = 1, + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE, + .rx = &rt2800usb_queue_rx, + .tx = &rt2800usb_queue_tx, + .bcn = &rt2800usb_queue_bcn, + .lib = &rt2800usb_rt2x00_ops, + .hw = &rt2800_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2800usb_rt2x00debug, + .debugfs = &rt2800_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; @@ -2886,17 +825,23 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0e0b, 0x9041), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Amit */ { USB_DEVICE(0x15c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Askey */ + { USB_DEVICE(0x1690, 0x0740), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1690, 0x0744), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0930, 0x0a07), USB_DEVICE_DATA(&rt2800usb_ops) }, /* ASUS */ { USB_DEVICE(0x0b05, 0x1731), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1732), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1742), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1760), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1761), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0b05, 0x1784), USB_DEVICE_DATA(&rt2800usb_ops) }, /* AzureWave */ { USB_DEVICE(0x13d3, 0x3247), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x13d3, 0x3262), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x13d3, 0x3273), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x13d3, 0x3284), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x13d3, 0x3305), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Belkin */ { USB_DEVICE(0x050d, 0x8053), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x050d, 0x805c), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -2905,6 +850,8 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Buffalo */ { USB_DEVICE(0x0411, 0x00e8), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0411, 0x012e), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Cisco */ + { USB_DEVICE(0x167b, 0x4001), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Conceptronic */ { USB_DEVICE(0x14b2, 0x3c06), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x14b2, 0x3c07), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -2920,6 +867,8 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x07aa, 0x002f), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07aa, 0x003c), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07aa, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07aa, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07aa, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x18c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x18c5, 0x0012), USB_DEVICE_DATA(&rt2800usb_ops) }, /* D-Link */ @@ -2931,18 +880,24 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x07d1, 0x3c0f), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07d1, 0x3c11), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07d1, 0x3c13), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07d1, 0x3c15), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Edimax */ { USB_DEVICE(0x7392, 0x7711), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x7392, 0x7717), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x7392, 0x7718), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Encore */ { USB_DEVICE(0x203d, 0x1480), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x203d, 0x14a1), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x203d, 0x14a9), USB_DEVICE_DATA(&rt2800usb_ops) }, /* EnGenius */ { USB_DEVICE(0X1740, 0x9701), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9702), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9703), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9705), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9706), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1740, 0x9707), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1740, 0x9708), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1740, 0x9709), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9801), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Gemtek */ { USB_DEVICE(0x15a9, 0x0010), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -2956,7 +911,10 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0e66, 0x0009), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0e66, 0x000b), USB_DEVICE_DATA(&rt2800usb_ops) }, /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x0944), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x04bb, 0x0945), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x04bb, 0x0947), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x04bb, 0x0948), USB_DEVICE_DATA(&rt2800usb_ops) }, /* LevelOne */ { USB_DEVICE(0x1740, 0x0605), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x0615), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -2971,8 +929,18 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Motorola */ { USB_DEVICE(0x100d, 0x9031), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x100d, 0x9032), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x3820), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x3821), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x3870), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x6899), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x821a), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x870a), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x899a), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Ovislink */ { USB_DEVICE(0x1b75, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Para */ + { USB_DEVICE(0x20b8, 0x8888), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Pegatron */ { USB_DEVICE(0x1d4d, 0x0002), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1d4d, 0x000c), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -2988,8 +956,6 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Quanta */ { USB_DEVICE(0x1a32, 0x0304), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Ralink */ - { USB_DEVICE(0x0db0, 0x3820), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0db0, 0x6899), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x148f, 0x2070), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x148f, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x148f, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -3013,7 +979,12 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0df6, 0x003e), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x0040), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x0047), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x0048), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x004a), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x004d), USB_DEVICE_DATA(&rt2800usb_ops) }, /* SMC */ { USB_DEVICE(0x083a, 0x6618), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0x7511), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -3022,6 +993,8 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x083a, 0x8522), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0xa512), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0xa618), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x083a, 0xa701), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x083a, 0xa702), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0xb522), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0xc522), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Sparklan */ @@ -3039,6 +1012,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x5a57, 0x0280), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x5a57, 0x0282), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x5a57, 0x0283), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x5a57, 0x0284), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x5a57, 0x5257), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Zyxel */ { USB_DEVICE(0x0586, 0x3416), USB_DEVICE_DATA(&rt2800usb_ops) }, diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/rt2x00/rt2800usb.h index 4d9991c9a51c..1e4340a182ef 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.h +++ b/drivers/net/wireless/rt2x00/rt2800usb.h @@ -1,5 +1,9 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Axel Kollhofer <rain_maker@root-forum.org> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -28,288 +32,10 @@ #define RT2800USB_H /* - * RF chip defines. - * - * RF2820 2.4G 2T3R - * RF2850 2.4G/5G 2T3R - * RF2720 2.4G 1T2R - * RF2750 2.4G/5G 1T2R - * RF3020 2.4G 1T1R - * RF2020 2.4G B/G - * RF3021 2.4G 1T2R - * RF3022 2.4G 2T2R - * RF3052 2.4G 2T2R - */ -#define RF2820 0x0001 -#define RF2850 0x0002 -#define RF2720 0x0003 -#define RF2750 0x0004 -#define RF3020 0x0005 -#define RF2020 0x0006 -#define RF3021 0x0007 -#define RF3022 0x0008 -#define RF3052 0x0009 - -/* - * RT2870 version - */ -#define RT2860C_VERSION 0x28600100 -#define RT2860D_VERSION 0x28600101 -#define RT2880E_VERSION 0x28720200 -#define RT2883_VERSION 0x28830300 -#define RT3070_VERSION 0x30700200 - -/* - * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. - */ -#define DEFAULT_RSSI_OFFSET 120 /* FIXME */ - -/* - * Register layout information. - */ -#define CSR_REG_BASE 0x1000 -#define CSR_REG_SIZE 0x0800 -#define EEPROM_BASE 0x0000 -#define EEPROM_SIZE 0x0110 -#define BBP_BASE 0x0000 -#define BBP_SIZE 0x0080 -#define RF_BASE 0x0004 -#define RF_SIZE 0x0010 - -/* - * Number of TX queues. - */ -#define NUM_TX_QUEUES 4 - -/* * USB registers. */ /* - * HOST-MCU shared memory - */ -#define HOST_CMD_CSR 0x0404 -#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x000000ff) - -/* - * INT_SOURCE_CSR: Interrupt source register. - * Write one to clear corresponding bit. - * TX_FIFO_STATUS: FIFO Statistics is full, sw should read 0x171c - */ -#define INT_SOURCE_CSR 0x0200 -#define INT_SOURCE_CSR_RXDELAYINT FIELD32(0x00000001) -#define INT_SOURCE_CSR_TXDELAYINT FIELD32(0x00000002) -#define INT_SOURCE_CSR_RX_DONE FIELD32(0x00000004) -#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00000008) -#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00000010) -#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00000020) -#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00000040) -#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00000080) -#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00000100) -#define INT_SOURCE_CSR_MCU_COMMAND FIELD32(0x00000200) -#define INT_SOURCE_CSR_RXTX_COHERENT FIELD32(0x00000400) -#define INT_SOURCE_CSR_TBTT FIELD32(0x00000800) -#define INT_SOURCE_CSR_PRE_TBTT FIELD32(0x00001000) -#define INT_SOURCE_CSR_TX_FIFO_STATUS FIELD32(0x00002000) -#define INT_SOURCE_CSR_AUTO_WAKEUP FIELD32(0x00004000) -#define INT_SOURCE_CSR_GPTIMER FIELD32(0x00008000) -#define INT_SOURCE_CSR_RX_COHERENT FIELD32(0x00010000) -#define INT_SOURCE_CSR_TX_COHERENT FIELD32(0x00020000) - -/* - * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. - */ -#define INT_MASK_CSR 0x0204 -#define INT_MASK_CSR_RXDELAYINT FIELD32(0x00000001) -#define INT_MASK_CSR_TXDELAYINT FIELD32(0x00000002) -#define INT_MASK_CSR_RX_DONE FIELD32(0x00000004) -#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00000008) -#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00000010) -#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00000020) -#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00000040) -#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00000080) -#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00000100) -#define INT_MASK_CSR_MCU_COMMAND FIELD32(0x00000200) -#define INT_MASK_CSR_RXTX_COHERENT FIELD32(0x00000400) -#define INT_MASK_CSR_TBTT FIELD32(0x00000800) -#define INT_MASK_CSR_PRE_TBTT FIELD32(0x00001000) -#define INT_MASK_CSR_TX_FIFO_STATUS FIELD32(0x00002000) -#define INT_MASK_CSR_AUTO_WAKEUP FIELD32(0x00004000) -#define INT_MASK_CSR_GPTIMER FIELD32(0x00008000) -#define INT_MASK_CSR_RX_COHERENT FIELD32(0x00010000) -#define INT_MASK_CSR_TX_COHERENT FIELD32(0x00020000) - -/* - * WPDMA_GLO_CFG - */ -#define WPDMA_GLO_CFG 0x0208 -#define WPDMA_GLO_CFG_ENABLE_TX_DMA FIELD32(0x00000001) -#define WPDMA_GLO_CFG_TX_DMA_BUSY FIELD32(0x00000002) -#define WPDMA_GLO_CFG_ENABLE_RX_DMA FIELD32(0x00000004) -#define WPDMA_GLO_CFG_RX_DMA_BUSY FIELD32(0x00000008) -#define WPDMA_GLO_CFG_WP_DMA_BURST_SIZE FIELD32(0x00000030) -#define WPDMA_GLO_CFG_TX_WRITEBACK_DONE FIELD32(0x00000040) -#define WPDMA_GLO_CFG_BIG_ENDIAN FIELD32(0x00000080) -#define WPDMA_GLO_CFG_RX_HDR_SCATTER FIELD32(0x0000ff00) -#define WPDMA_GLO_CFG_HDR_SEG_LEN FIELD32(0xffff0000) - -/* - * WPDMA_RST_IDX - */ -#define WPDMA_RST_IDX 0x020c -#define WPDMA_RST_IDX_DTX_IDX0 FIELD32(0x00000001) -#define WPDMA_RST_IDX_DTX_IDX1 FIELD32(0x00000002) -#define WPDMA_RST_IDX_DTX_IDX2 FIELD32(0x00000004) -#define WPDMA_RST_IDX_DTX_IDX3 FIELD32(0x00000008) -#define WPDMA_RST_IDX_DTX_IDX4 FIELD32(0x00000010) -#define WPDMA_RST_IDX_DTX_IDX5 FIELD32(0x00000020) -#define WPDMA_RST_IDX_DRX_IDX0 FIELD32(0x00010000) - -/* - * DELAY_INT_CFG - */ -#define DELAY_INT_CFG 0x0210 -#define DELAY_INT_CFG_RXMAX_PTIME FIELD32(0x000000ff) -#define DELAY_INT_CFG_RXMAX_PINT FIELD32(0x00007f00) -#define DELAY_INT_CFG_RXDLY_INT_EN FIELD32(0x00008000) -#define DELAY_INT_CFG_TXMAX_PTIME FIELD32(0x00ff0000) -#define DELAY_INT_CFG_TXMAX_PINT FIELD32(0x7f000000) -#define DELAY_INT_CFG_TXDLY_INT_EN FIELD32(0x80000000) - -/* - * WMM_AIFSN_CFG: Aifsn for each EDCA AC - * AIFSN0: AC_BE - * AIFSN1: AC_BK - * AIFSN1: AC_VI - * AIFSN1: AC_VO - */ -#define WMM_AIFSN_CFG 0x0214 -#define WMM_AIFSN_CFG_AIFSN0 FIELD32(0x0000000f) -#define WMM_AIFSN_CFG_AIFSN1 FIELD32(0x000000f0) -#define WMM_AIFSN_CFG_AIFSN2 FIELD32(0x00000f00) -#define WMM_AIFSN_CFG_AIFSN3 FIELD32(0x0000f000) - -/* - * WMM_CWMIN_CSR: CWmin for each EDCA AC - * CWMIN0: AC_BE - * CWMIN1: AC_BK - * CWMIN1: AC_VI - * CWMIN1: AC_VO - */ -#define WMM_CWMIN_CFG 0x0218 -#define WMM_CWMIN_CFG_CWMIN0 FIELD32(0x0000000f) -#define WMM_CWMIN_CFG_CWMIN1 FIELD32(0x000000f0) -#define WMM_CWMIN_CFG_CWMIN2 FIELD32(0x00000f00) -#define WMM_CWMIN_CFG_CWMIN3 FIELD32(0x0000f000) - -/* - * WMM_CWMAX_CSR: CWmax for each EDCA AC - * CWMAX0: AC_BE - * CWMAX1: AC_BK - * CWMAX1: AC_VI - * CWMAX1: AC_VO - */ -#define WMM_CWMAX_CFG 0x021c -#define WMM_CWMAX_CFG_CWMAX0 FIELD32(0x0000000f) -#define WMM_CWMAX_CFG_CWMAX1 FIELD32(0x000000f0) -#define WMM_CWMAX_CFG_CWMAX2 FIELD32(0x00000f00) -#define WMM_CWMAX_CFG_CWMAX3 FIELD32(0x0000f000) - -/* - * AC_TXOP0: AC_BK/AC_BE TXOP register - * AC0TXOP: AC_BK in unit of 32us - * AC1TXOP: AC_BE in unit of 32us - */ -#define WMM_TXOP0_CFG 0x0220 -#define WMM_TXOP0_CFG_AC0TXOP FIELD32(0x0000ffff) -#define WMM_TXOP0_CFG_AC1TXOP FIELD32(0xffff0000) - -/* - * AC_TXOP1: AC_VO/AC_VI TXOP register - * AC2TXOP: AC_VI in unit of 32us - * AC3TXOP: AC_VO in unit of 32us - */ -#define WMM_TXOP1_CFG 0x0224 -#define WMM_TXOP1_CFG_AC2TXOP FIELD32(0x0000ffff) -#define WMM_TXOP1_CFG_AC3TXOP FIELD32(0xffff0000) - -/* - * GPIO_CTRL_CFG: - */ -#define GPIO_CTRL_CFG 0x0228 -#define GPIO_CTRL_CFG_BIT0 FIELD32(0x00000001) -#define GPIO_CTRL_CFG_BIT1 FIELD32(0x00000002) -#define GPIO_CTRL_CFG_BIT2 FIELD32(0x00000004) -#define GPIO_CTRL_CFG_BIT3 FIELD32(0x00000008) -#define GPIO_CTRL_CFG_BIT4 FIELD32(0x00000010) -#define GPIO_CTRL_CFG_BIT5 FIELD32(0x00000020) -#define GPIO_CTRL_CFG_BIT6 FIELD32(0x00000040) -#define GPIO_CTRL_CFG_BIT7 FIELD32(0x00000080) -#define GPIO_CTRL_CFG_BIT8 FIELD32(0x00000100) - -/* - * MCU_CMD_CFG - */ -#define MCU_CMD_CFG 0x022c - -/* - * AC_BK register offsets - */ -#define TX_BASE_PTR0 0x0230 -#define TX_MAX_CNT0 0x0234 -#define TX_CTX_IDX0 0x0238 -#define TX_DTX_IDX0 0x023c - -/* - * AC_BE register offsets - */ -#define TX_BASE_PTR1 0x0240 -#define TX_MAX_CNT1 0x0244 -#define TX_CTX_IDX1 0x0248 -#define TX_DTX_IDX1 0x024c - -/* - * AC_VI register offsets - */ -#define TX_BASE_PTR2 0x0250 -#define TX_MAX_CNT2 0x0254 -#define TX_CTX_IDX2 0x0258 -#define TX_DTX_IDX2 0x025c - -/* - * AC_VO register offsets - */ -#define TX_BASE_PTR3 0x0260 -#define TX_MAX_CNT3 0x0264 -#define TX_CTX_IDX3 0x0268 -#define TX_DTX_IDX3 0x026c - -/* - * HCCA register offsets - */ -#define TX_BASE_PTR4 0x0270 -#define TX_MAX_CNT4 0x0274 -#define TX_CTX_IDX4 0x0278 -#define TX_DTX_IDX4 0x027c - -/* - * MGMT register offsets - */ -#define TX_BASE_PTR5 0x0280 -#define TX_MAX_CNT5 0x0284 -#define TX_CTX_IDX5 0x0288 -#define TX_DTX_IDX5 0x028c - -/* - * RX register offsets - */ -#define RX_BASE_PTR 0x0290 -#define RX_MAX_CNT 0x0294 -#define RX_CRX_IDX 0x0298 -#define RX_DRX_IDX 0x029c - -/* * USB_DMA_CFG * RX_BULK_AGG_TIMEOUT: Rx Bulk Aggregation TimeOut in unit of 33ns. * RX_BULK_AGG_LIMIT: Rx Bulk Aggregation Limit in unit of 256 bytes. @@ -343,1448 +69,16 @@ #define USB_CYC_CFG_CLOCK_CYCLE FIELD32(0x000000ff) /* - * PBF_SYS_CTRL - * HOST_RAM_WRITE: enable Host program ram write selection - */ -#define PBF_SYS_CTRL 0x0400 -#define PBF_SYS_CTRL_READY FIELD32(0x00000080) -#define PBF_SYS_CTRL_HOST_RAM_WRITE FIELD32(0x00010000) - -/* - * PBF registers - * Most are for debug. Driver doesn't touch PBF register. - */ -#define PBF_CFG 0x0408 -#define PBF_MAX_PCNT 0x040c -#define PBF_CTRL 0x0410 -#define PBF_INT_STA 0x0414 -#define PBF_INT_ENA 0x0418 - -/* - * BCN_OFFSET0: - */ -#define BCN_OFFSET0 0x042c -#define BCN_OFFSET0_BCN0 FIELD32(0x000000ff) -#define BCN_OFFSET0_BCN1 FIELD32(0x0000ff00) -#define BCN_OFFSET0_BCN2 FIELD32(0x00ff0000) -#define BCN_OFFSET0_BCN3 FIELD32(0xff000000) - -/* - * BCN_OFFSET1: - */ -#define BCN_OFFSET1 0x0430 -#define BCN_OFFSET1_BCN4 FIELD32(0x000000ff) -#define BCN_OFFSET1_BCN5 FIELD32(0x0000ff00) -#define BCN_OFFSET1_BCN6 FIELD32(0x00ff0000) -#define BCN_OFFSET1_BCN7 FIELD32(0xff000000) - -/* - * PBF registers - * Most are for debug. Driver doesn't touch PBF register. - */ -#define TXRXQ_PCNT 0x0438 -#define PBF_DBG 0x043c - -/* - * RF registers - */ -#define RF_CSR_CFG 0x0500 -#define RF_CSR_CFG_DATA FIELD32(0x000000ff) -#define RF_CSR_CFG_REGNUM FIELD32(0x00001f00) -#define RF_CSR_CFG_WRITE FIELD32(0x00010000) -#define RF_CSR_CFG_BUSY FIELD32(0x00020000) - -/* - * MAC Control/Status Registers(CSR). - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * MAC_CSR0: ASIC revision number. - * ASIC_REV: 0 - * ASIC_VER: 2870 - */ -#define MAC_CSR0 0x1000 -#define MAC_CSR0_ASIC_REV FIELD32(0x0000ffff) -#define MAC_CSR0_ASIC_VER FIELD32(0xffff0000) - -/* - * MAC_SYS_CTRL: - */ -#define MAC_SYS_CTRL 0x1004 -#define MAC_SYS_CTRL_RESET_CSR FIELD32(0x00000001) -#define MAC_SYS_CTRL_RESET_BBP FIELD32(0x00000002) -#define MAC_SYS_CTRL_ENABLE_TX FIELD32(0x00000004) -#define MAC_SYS_CTRL_ENABLE_RX FIELD32(0x00000008) -#define MAC_SYS_CTRL_CONTINUOUS_TX FIELD32(0x00000010) -#define MAC_SYS_CTRL_LOOPBACK FIELD32(0x00000020) -#define MAC_SYS_CTRL_WLAN_HALT FIELD32(0x00000040) -#define MAC_SYS_CTRL_RX_TIMESTAMP FIELD32(0x00000080) - -/* - * MAC_ADDR_DW0: STA MAC register 0 - */ -#define MAC_ADDR_DW0 0x1008 -#define MAC_ADDR_DW0_BYTE0 FIELD32(0x000000ff) -#define MAC_ADDR_DW0_BYTE1 FIELD32(0x0000ff00) -#define MAC_ADDR_DW0_BYTE2 FIELD32(0x00ff0000) -#define MAC_ADDR_DW0_BYTE3 FIELD32(0xff000000) - -/* - * MAC_ADDR_DW1: STA MAC register 1 - * UNICAST_TO_ME_MASK: - * Used to mask off bits from byte 5 of the MAC address - * to determine the UNICAST_TO_ME bit for RX frames. - * The full mask is complemented by BSS_ID_MASK: - * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK - */ -#define MAC_ADDR_DW1 0x100c -#define MAC_ADDR_DW1_BYTE4 FIELD32(0x000000ff) -#define MAC_ADDR_DW1_BYTE5 FIELD32(0x0000ff00) -#define MAC_ADDR_DW1_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) - -/* - * MAC_BSSID_DW0: BSSID register 0 - */ -#define MAC_BSSID_DW0 0x1010 -#define MAC_BSSID_DW0_BYTE0 FIELD32(0x000000ff) -#define MAC_BSSID_DW0_BYTE1 FIELD32(0x0000ff00) -#define MAC_BSSID_DW0_BYTE2 FIELD32(0x00ff0000) -#define MAC_BSSID_DW0_BYTE3 FIELD32(0xff000000) - -/* - * MAC_BSSID_DW1: BSSID register 1 - * BSS_ID_MASK: - * 0: 1-BSSID mode (BSS index = 0) - * 1: 2-BSSID mode (BSS index: Byte5, bit 0) - * 2: 4-BSSID mode (BSS index: byte5, bit 0 - 1) - * 3: 8-BSSID mode (BSS index: byte5, bit 0 - 2) - * This mask is used to mask off bits 0, 1 and 2 of byte 5 of the - * BSSID. This will make sure that those bits will be ignored - * when determining the MY_BSS of RX frames. - */ -#define MAC_BSSID_DW1 0x1014 -#define MAC_BSSID_DW1_BYTE4 FIELD32(0x000000ff) -#define MAC_BSSID_DW1_BYTE5 FIELD32(0x0000ff00) -#define MAC_BSSID_DW1_BSS_ID_MASK FIELD32(0x00030000) -#define MAC_BSSID_DW1_BSS_BCN_NUM FIELD32(0x001c0000) - -/* - * MAX_LEN_CFG: Maximum frame length register. - * MAX_MPDU: rt2860b max 16k bytes - * MAX_PSDU: Maximum PSDU length - * (power factor) 0:2^13, 1:2^14, 2:2^15, 3:2^16 - */ -#define MAX_LEN_CFG 0x1018 -#define MAX_LEN_CFG_MAX_MPDU FIELD32(0x00000fff) -#define MAX_LEN_CFG_MAX_PSDU FIELD32(0x00003000) -#define MAX_LEN_CFG_MIN_PSDU FIELD32(0x0000c000) -#define MAX_LEN_CFG_MIN_MPDU FIELD32(0x000f0000) - -/* - * BBP_CSR_CFG: BBP serial control register - * VALUE: Register value to program into BBP - * REG_NUM: Selected BBP register - * READ_CONTROL: 0 write BBP, 1 read BBP - * BUSY: ASIC is busy executing BBP commands - * BBP_PAR_DUR: 0 4 MAC clocks, 1 8 MAC clocks - * BBP_RW_MODE: 0 serial, 1 paralell - */ -#define BBP_CSR_CFG 0x101c -#define BBP_CSR_CFG_VALUE FIELD32(0x000000ff) -#define BBP_CSR_CFG_REGNUM FIELD32(0x0000ff00) -#define BBP_CSR_CFG_READ_CONTROL FIELD32(0x00010000) -#define BBP_CSR_CFG_BUSY FIELD32(0x00020000) -#define BBP_CSR_CFG_BBP_PAR_DUR FIELD32(0x00040000) -#define BBP_CSR_CFG_BBP_RW_MODE FIELD32(0x00080000) - -/* - * RF_CSR_CFG0: RF control register - * REGID_AND_VALUE: Register value to program into RF - * BITWIDTH: Selected RF register - * STANDBYMODE: 0 high when standby, 1 low when standby - * SEL: 0 RF_LE0 activate, 1 RF_LE1 activate - * BUSY: ASIC is busy executing RF commands - */ -#define RF_CSR_CFG0 0x1020 -#define RF_CSR_CFG0_REGID_AND_VALUE FIELD32(0x00ffffff) -#define RF_CSR_CFG0_BITWIDTH FIELD32(0x1f000000) -#define RF_CSR_CFG0_REG_VALUE_BW FIELD32(0x1fffffff) -#define RF_CSR_CFG0_STANDBYMODE FIELD32(0x20000000) -#define RF_CSR_CFG0_SEL FIELD32(0x40000000) -#define RF_CSR_CFG0_BUSY FIELD32(0x80000000) - -/* - * RF_CSR_CFG1: RF control register - * REGID_AND_VALUE: Register value to program into RF - * RFGAP: Gap between BB_CONTROL_RF and RF_LE - * 0: 3 system clock cycle (37.5usec) - * 1: 5 system clock cycle (62.5usec) - */ -#define RF_CSR_CFG1 0x1024 -#define RF_CSR_CFG1_REGID_AND_VALUE FIELD32(0x00ffffff) -#define RF_CSR_CFG1_RFGAP FIELD32(0x1f000000) - -/* - * RF_CSR_CFG2: RF control register - * VALUE: Register value to program into RF - * RFGAP: Gap between BB_CONTROL_RF and RF_LE - * 0: 3 system clock cycle (37.5usec) - * 1: 5 system clock cycle (62.5usec) - */ -#define RF_CSR_CFG2 0x1028 -#define RF_CSR_CFG2_VALUE FIELD32(0x00ffffff) - -/* - * LED_CFG: LED control - * color LED's: - * 0: off - * 1: blinking upon TX2 - * 2: periodic slow blinking - * 3: always on - * LED polarity: - * 0: active low - * 1: active high - */ -#define LED_CFG 0x102c -#define LED_CFG_ON_PERIOD FIELD32(0x000000ff) -#define LED_CFG_OFF_PERIOD FIELD32(0x0000ff00) -#define LED_CFG_SLOW_BLINK_PERIOD FIELD32(0x003f0000) -#define LED_CFG_R_LED_MODE FIELD32(0x03000000) -#define LED_CFG_G_LED_MODE FIELD32(0x0c000000) -#define LED_CFG_Y_LED_MODE FIELD32(0x30000000) -#define LED_CFG_LED_POLAR FIELD32(0x40000000) - -/* - * XIFS_TIME_CFG: MAC timing - * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX - * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX - * OFDM_XIFS_TIME: unit 1us. Applied after OFDM RX - * when MAC doesn't reference BBP signal BBRXEND - * EIFS: unit 1us - * BB_RXEND_ENABLE: reference RXEND signal to begin XIFS defer - * - */ -#define XIFS_TIME_CFG 0x1100 -#define XIFS_TIME_CFG_CCKM_SIFS_TIME FIELD32(0x000000ff) -#define XIFS_TIME_CFG_OFDM_SIFS_TIME FIELD32(0x0000ff00) -#define XIFS_TIME_CFG_OFDM_XIFS_TIME FIELD32(0x000f0000) -#define XIFS_TIME_CFG_EIFS FIELD32(0x1ff00000) -#define XIFS_TIME_CFG_BB_RXEND_ENABLE FIELD32(0x20000000) - -/* - * BKOFF_SLOT_CFG: - */ -#define BKOFF_SLOT_CFG 0x1104 -#define BKOFF_SLOT_CFG_SLOT_TIME FIELD32(0x000000ff) -#define BKOFF_SLOT_CFG_CC_DELAY_TIME FIELD32(0x0000ff00) - -/* - * NAV_TIME_CFG: - */ -#define NAV_TIME_CFG 0x1108 -#define NAV_TIME_CFG_SIFS FIELD32(0x000000ff) -#define NAV_TIME_CFG_SLOT_TIME FIELD32(0x0000ff00) -#define NAV_TIME_CFG_EIFS FIELD32(0x01ff0000) -#define NAV_TIME_ZERO_SIFS FIELD32(0x02000000) - -/* - * CH_TIME_CFG: count as channel busy - */ -#define CH_TIME_CFG 0x110c - -/* - * PBF_LIFE_TIMER: TX/RX MPDU timestamp timer (free run) Unit: 1us - */ -#define PBF_LIFE_TIMER 0x1110 - -/* - * BCN_TIME_CFG: - * BEACON_INTERVAL: in unit of 1/16 TU - * TSF_TICKING: Enable TSF auto counting - * TSF_SYNC: Enable TSF sync, 00: disable, 01: infra mode, 10: ad-hoc mode - * BEACON_GEN: Enable beacon generator - */ -#define BCN_TIME_CFG 0x1114 -#define BCN_TIME_CFG_BEACON_INTERVAL FIELD32(0x0000ffff) -#define BCN_TIME_CFG_TSF_TICKING FIELD32(0x00010000) -#define BCN_TIME_CFG_TSF_SYNC FIELD32(0x00060000) -#define BCN_TIME_CFG_TBTT_ENABLE FIELD32(0x00080000) -#define BCN_TIME_CFG_BEACON_GEN FIELD32(0x00100000) -#define BCN_TIME_CFG_TX_TIME_COMPENSATE FIELD32(0xf0000000) - -/* - * TBTT_SYNC_CFG: - */ -#define TBTT_SYNC_CFG 0x1118 - -/* - * TSF_TIMER_DW0: Local lsb TSF timer, read-only - */ -#define TSF_TIMER_DW0 0x111c -#define TSF_TIMER_DW0_LOW_WORD FIELD32(0xffffffff) - -/* - * TSF_TIMER_DW1: Local msb TSF timer, read-only - */ -#define TSF_TIMER_DW1 0x1120 -#define TSF_TIMER_DW1_HIGH_WORD FIELD32(0xffffffff) - -/* - * TBTT_TIMER: TImer remains till next TBTT, read-only - */ -#define TBTT_TIMER 0x1124 - -/* - * INT_TIMER_CFG: - */ -#define INT_TIMER_CFG 0x1128 - -/* - * INT_TIMER_EN: GP-timer and pre-tbtt Int enable - */ -#define INT_TIMER_EN 0x112c - -/* - * CH_IDLE_STA: channel idle time - */ -#define CH_IDLE_STA 0x1130 - -/* - * CH_BUSY_STA: channel busy time - */ -#define CH_BUSY_STA 0x1134 - -/* - * MAC_STATUS_CFG: - * BBP_RF_BUSY: When set to 0, BBP and RF are stable. - * if 1 or higher one of the 2 registers is busy. - */ -#define MAC_STATUS_CFG 0x1200 -#define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003) - -/* - * PWR_PIN_CFG: - */ -#define PWR_PIN_CFG 0x1204 - -/* - * AUTOWAKEUP_CFG: Manual power control / status register - * TBCN_BEFORE_WAKE: ForceWake has high privilege than PutToSleep when both set - * AUTOWAKE: 0:sleep, 1:awake - */ -#define AUTOWAKEUP_CFG 0x1208 -#define AUTOWAKEUP_CFG_AUTO_LEAD_TIME FIELD32(0x000000ff) -#define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00) -#define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000) - -/* - * EDCA_AC0_CFG: - */ -#define EDCA_AC0_CFG 0x1300 -#define EDCA_AC0_CFG_TX_OP FIELD32(0x000000ff) -#define EDCA_AC0_CFG_AIFSN FIELD32(0x00000f00) -#define EDCA_AC0_CFG_CWMIN FIELD32(0x0000f000) -#define EDCA_AC0_CFG_CWMAX FIELD32(0x000f0000) - -/* - * EDCA_AC1_CFG: - */ -#define EDCA_AC1_CFG 0x1304 -#define EDCA_AC1_CFG_TX_OP FIELD32(0x000000ff) -#define EDCA_AC1_CFG_AIFSN FIELD32(0x00000f00) -#define EDCA_AC1_CFG_CWMIN FIELD32(0x0000f000) -#define EDCA_AC1_CFG_CWMAX FIELD32(0x000f0000) - -/* - * EDCA_AC2_CFG: - */ -#define EDCA_AC2_CFG 0x1308 -#define EDCA_AC2_CFG_TX_OP FIELD32(0x000000ff) -#define EDCA_AC2_CFG_AIFSN FIELD32(0x00000f00) -#define EDCA_AC2_CFG_CWMIN FIELD32(0x0000f000) -#define EDCA_AC2_CFG_CWMAX FIELD32(0x000f0000) - -/* - * EDCA_AC3_CFG: - */ -#define EDCA_AC3_CFG 0x130c -#define EDCA_AC3_CFG_TX_OP FIELD32(0x000000ff) -#define EDCA_AC3_CFG_AIFSN FIELD32(0x00000f00) -#define EDCA_AC3_CFG_CWMIN FIELD32(0x0000f000) -#define EDCA_AC3_CFG_CWMAX FIELD32(0x000f0000) - -/* - * EDCA_TID_AC_MAP: - */ -#define EDCA_TID_AC_MAP 0x1310 - -/* - * TX_PWR_CFG_0: - */ -#define TX_PWR_CFG_0 0x1314 -#define TX_PWR_CFG_0_1MBS FIELD32(0x0000000f) -#define TX_PWR_CFG_0_2MBS FIELD32(0x000000f0) -#define TX_PWR_CFG_0_55MBS FIELD32(0x00000f00) -#define TX_PWR_CFG_0_11MBS FIELD32(0x0000f000) -#define TX_PWR_CFG_0_6MBS FIELD32(0x000f0000) -#define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000) -#define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000) -#define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_1: - */ -#define TX_PWR_CFG_1 0x1318 -#define TX_PWR_CFG_1_24MBS FIELD32(0x0000000f) -#define TX_PWR_CFG_1_36MBS FIELD32(0x000000f0) -#define TX_PWR_CFG_1_48MBS FIELD32(0x00000f00) -#define TX_PWR_CFG_1_54MBS FIELD32(0x0000f000) -#define TX_PWR_CFG_1_MCS0 FIELD32(0x000f0000) -#define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000) -#define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000) -#define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_2: - */ -#define TX_PWR_CFG_2 0x131c -#define TX_PWR_CFG_2_MCS4 FIELD32(0x0000000f) -#define TX_PWR_CFG_2_MCS5 FIELD32(0x000000f0) -#define TX_PWR_CFG_2_MCS6 FIELD32(0x00000f00) -#define TX_PWR_CFG_2_MCS7 FIELD32(0x0000f000) -#define TX_PWR_CFG_2_MCS8 FIELD32(0x000f0000) -#define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000) -#define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000) -#define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_3: - */ -#define TX_PWR_CFG_3 0x1320 -#define TX_PWR_CFG_3_MCS12 FIELD32(0x0000000f) -#define TX_PWR_CFG_3_MCS13 FIELD32(0x000000f0) -#define TX_PWR_CFG_3_MCS14 FIELD32(0x00000f00) -#define TX_PWR_CFG_3_MCS15 FIELD32(0x0000f000) -#define TX_PWR_CFG_3_UKNOWN1 FIELD32(0x000f0000) -#define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000) -#define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000) -#define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_4: - */ -#define TX_PWR_CFG_4 0x1324 -#define TX_PWR_CFG_4_UKNOWN5 FIELD32(0x0000000f) -#define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0) -#define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00) -#define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000) - -/* - * TX_PIN_CFG: - */ -#define TX_PIN_CFG 0x1328 -#define TX_PIN_CFG_PA_PE_A0_EN FIELD32(0x00000001) -#define TX_PIN_CFG_PA_PE_G0_EN FIELD32(0x00000002) -#define TX_PIN_CFG_PA_PE_A1_EN FIELD32(0x00000004) -#define TX_PIN_CFG_PA_PE_G1_EN FIELD32(0x00000008) -#define TX_PIN_CFG_PA_PE_A0_POL FIELD32(0x00000010) -#define TX_PIN_CFG_PA_PE_G0_POL FIELD32(0x00000020) -#define TX_PIN_CFG_PA_PE_A1_POL FIELD32(0x00000040) -#define TX_PIN_CFG_PA_PE_G1_POL FIELD32(0x00000080) -#define TX_PIN_CFG_LNA_PE_A0_EN FIELD32(0x00000100) -#define TX_PIN_CFG_LNA_PE_G0_EN FIELD32(0x00000200) -#define TX_PIN_CFG_LNA_PE_A1_EN FIELD32(0x00000400) -#define TX_PIN_CFG_LNA_PE_G1_EN FIELD32(0x00000800) -#define TX_PIN_CFG_LNA_PE_A0_POL FIELD32(0x00001000) -#define TX_PIN_CFG_LNA_PE_G0_POL FIELD32(0x00002000) -#define TX_PIN_CFG_LNA_PE_A1_POL FIELD32(0x00004000) -#define TX_PIN_CFG_LNA_PE_G1_POL FIELD32(0x00008000) -#define TX_PIN_CFG_RFTR_EN FIELD32(0x00010000) -#define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000) -#define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000) -#define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000) - -/* - * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz - */ -#define TX_BAND_CFG 0x132c -#define TX_BAND_CFG_HT40_PLUS FIELD32(0x00000001) -#define TX_BAND_CFG_A FIELD32(0x00000002) -#define TX_BAND_CFG_BG FIELD32(0x00000004) - -/* - * TX_SW_CFG0: - */ -#define TX_SW_CFG0 0x1330 - -/* - * TX_SW_CFG1: - */ -#define TX_SW_CFG1 0x1334 - -/* - * TX_SW_CFG2: - */ -#define TX_SW_CFG2 0x1338 - -/* - * TXOP_THRES_CFG: - */ -#define TXOP_THRES_CFG 0x133c - -/* - * TXOP_CTRL_CFG: - */ -#define TXOP_CTRL_CFG 0x1340 - -/* - * TX_RTS_CFG: - * RTS_THRES: unit:byte - * RTS_FBK_EN: enable rts rate fallback - */ -#define TX_RTS_CFG 0x1344 -#define TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT FIELD32(0x000000ff) -#define TX_RTS_CFG_RTS_THRES FIELD32(0x00ffff00) -#define TX_RTS_CFG_RTS_FBK_EN FIELD32(0x01000000) - -/* - * TX_TIMEOUT_CFG: - * MPDU_LIFETIME: expiration time = 2^(9+MPDU LIFE TIME) us - * RX_ACK_TIMEOUT: unit:slot. Used for TX procedure - * TX_OP_TIMEOUT: TXOP timeout value for TXOP truncation. - * it is recommended that: - * (SLOT_TIME) > (TX_OP_TIMEOUT) > (RX_ACK_TIMEOUT) - */ -#define TX_TIMEOUT_CFG 0x1348 -#define TX_TIMEOUT_CFG_MPDU_LIFETIME FIELD32(0x000000f0) -#define TX_TIMEOUT_CFG_RX_ACK_TIMEOUT FIELD32(0x0000ff00) -#define TX_TIMEOUT_CFG_TX_OP_TIMEOUT FIELD32(0x00ff0000) - -/* - * TX_RTY_CFG: - * SHORT_RTY_LIMIT: short retry limit - * LONG_RTY_LIMIT: long retry limit - * LONG_RTY_THRE: Long retry threshoold - * NON_AGG_RTY_MODE: Non-Aggregate MPDU retry mode - * 0:expired by retry limit, 1: expired by mpdu life timer - * AGG_RTY_MODE: Aggregate MPDU retry mode - * 0:expired by retry limit, 1: expired by mpdu life timer - * TX_AUTO_FB_ENABLE: Tx retry PHY rate auto fallback enable - */ -#define TX_RTY_CFG 0x134c -#define TX_RTY_CFG_SHORT_RTY_LIMIT FIELD32(0x000000ff) -#define TX_RTY_CFG_LONG_RTY_LIMIT FIELD32(0x0000ff00) -#define TX_RTY_CFG_LONG_RTY_THRE FIELD32(0x0fff0000) -#define TX_RTY_CFG_NON_AGG_RTY_MODE FIELD32(0x10000000) -#define TX_RTY_CFG_AGG_RTY_MODE FIELD32(0x20000000) -#define TX_RTY_CFG_TX_AUTO_FB_ENABLE FIELD32(0x40000000) - -/* - * TX_LINK_CFG: - * REMOTE_MFB_LIFETIME: remote MFB life time. unit: 32us - * MFB_ENABLE: TX apply remote MFB 1:enable - * REMOTE_UMFS_ENABLE: remote unsolicit MFB enable - * 0: not apply remote remote unsolicit (MFS=7) - * TX_MRQ_EN: MCS request TX enable - * TX_RDG_EN: RDG TX enable - * TX_CF_ACK_EN: Piggyback CF-ACK enable - * REMOTE_MFB: remote MCS feedback - * REMOTE_MFS: remote MCS feedback sequence number - */ -#define TX_LINK_CFG 0x1350 -#define TX_LINK_CFG_REMOTE_MFB_LIFETIME FIELD32(0x000000ff) -#define TX_LINK_CFG_MFB_ENABLE FIELD32(0x00000100) -#define TX_LINK_CFG_REMOTE_UMFS_ENABLE FIELD32(0x00000200) -#define TX_LINK_CFG_TX_MRQ_EN FIELD32(0x00000400) -#define TX_LINK_CFG_TX_RDG_EN FIELD32(0x00000800) -#define TX_LINK_CFG_TX_CF_ACK_EN FIELD32(0x00001000) -#define TX_LINK_CFG_REMOTE_MFB FIELD32(0x00ff0000) -#define TX_LINK_CFG_REMOTE_MFS FIELD32(0xff000000) - -/* - * HT_FBK_CFG0: - */ -#define HT_FBK_CFG0 0x1354 -#define HT_FBK_CFG0_HTMCS0FBK FIELD32(0x0000000f) -#define HT_FBK_CFG0_HTMCS1FBK FIELD32(0x000000f0) -#define HT_FBK_CFG0_HTMCS2FBK FIELD32(0x00000f00) -#define HT_FBK_CFG0_HTMCS3FBK FIELD32(0x0000f000) -#define HT_FBK_CFG0_HTMCS4FBK FIELD32(0x000f0000) -#define HT_FBK_CFG0_HTMCS5FBK FIELD32(0x00f00000) -#define HT_FBK_CFG0_HTMCS6FBK FIELD32(0x0f000000) -#define HT_FBK_CFG0_HTMCS7FBK FIELD32(0xf0000000) - -/* - * HT_FBK_CFG1: - */ -#define HT_FBK_CFG1 0x1358 -#define HT_FBK_CFG1_HTMCS8FBK FIELD32(0x0000000f) -#define HT_FBK_CFG1_HTMCS9FBK FIELD32(0x000000f0) -#define HT_FBK_CFG1_HTMCS10FBK FIELD32(0x00000f00) -#define HT_FBK_CFG1_HTMCS11FBK FIELD32(0x0000f000) -#define HT_FBK_CFG1_HTMCS12FBK FIELD32(0x000f0000) -#define HT_FBK_CFG1_HTMCS13FBK FIELD32(0x00f00000) -#define HT_FBK_CFG1_HTMCS14FBK FIELD32(0x0f000000) -#define HT_FBK_CFG1_HTMCS15FBK FIELD32(0xf0000000) - -/* - * LG_FBK_CFG0: - */ -#define LG_FBK_CFG0 0x135c -#define LG_FBK_CFG0_OFDMMCS0FBK FIELD32(0x0000000f) -#define LG_FBK_CFG0_OFDMMCS1FBK FIELD32(0x000000f0) -#define LG_FBK_CFG0_OFDMMCS2FBK FIELD32(0x00000f00) -#define LG_FBK_CFG0_OFDMMCS3FBK FIELD32(0x0000f000) -#define LG_FBK_CFG0_OFDMMCS4FBK FIELD32(0x000f0000) -#define LG_FBK_CFG0_OFDMMCS5FBK FIELD32(0x00f00000) -#define LG_FBK_CFG0_OFDMMCS6FBK FIELD32(0x0f000000) -#define LG_FBK_CFG0_OFDMMCS7FBK FIELD32(0xf0000000) - -/* - * LG_FBK_CFG1: - */ -#define LG_FBK_CFG1 0x1360 -#define LG_FBK_CFG0_CCKMCS0FBK FIELD32(0x0000000f) -#define LG_FBK_CFG0_CCKMCS1FBK FIELD32(0x000000f0) -#define LG_FBK_CFG0_CCKMCS2FBK FIELD32(0x00000f00) -#define LG_FBK_CFG0_CCKMCS3FBK FIELD32(0x0000f000) - -/* - * CCK_PROT_CFG: CCK Protection - * PROTECT_RATE: Protection control frame rate for CCK TX(RTS/CTS/CFEnd) - * PROTECT_CTRL: Protection control frame type for CCK TX - * 0:none, 1:RTS/CTS, 2:CTS-to-self - * PROTECT_NAV: TXOP protection type for CCK TX - * 0:none, 1:ShortNAVprotect, 2:LongNAVProtect - * TX_OP_ALLOW_CCK: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_OFDM: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_MM20: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_MM40: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_GF20: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_GF40: CCK TXOP allowance, 0:disallow - * RTS_TH_EN: RTS threshold enable on CCK TX - */ -#define CCK_PROT_CFG 0x1364 -#define CCK_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define CCK_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define CCK_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) -#define CCK_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define CCK_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define CCK_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define CCK_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define CCK_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define CCK_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define CCK_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * OFDM_PROT_CFG: OFDM Protection - */ -#define OFDM_PROT_CFG 0x1368 -#define OFDM_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define OFDM_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define OFDM_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define OFDM_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * MM20_PROT_CFG: MM20 Protection - */ -#define MM20_PROT_CFG 0x136c -#define MM20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define MM20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define MM20_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) -#define MM20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define MM20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define MM20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define MM20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define MM20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define MM20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define MM20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * MM40_PROT_CFG: MM40 Protection - */ -#define MM40_PROT_CFG 0x1370 -#define MM40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define MM40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define MM40_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) -#define MM40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define MM40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define MM40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define MM40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define MM40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define MM40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define MM40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * GF20_PROT_CFG: GF20 Protection - */ -#define GF20_PROT_CFG 0x1374 -#define GF20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define GF20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define GF20_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) -#define GF20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define GF20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define GF20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define GF20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define GF20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define GF20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define GF20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * GF40_PROT_CFG: GF40 Protection - */ -#define GF40_PROT_CFG 0x1378 -#define GF40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define GF40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define GF40_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000) -#define GF40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define GF40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define GF40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define GF40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define GF40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define GF40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define GF40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * EXP_CTS_TIME: - */ -#define EXP_CTS_TIME 0x137c - -/* - * EXP_ACK_TIME: - */ -#define EXP_ACK_TIME 0x1380 - -/* - * RX_FILTER_CFG: RX configuration register. - */ -#define RX_FILTER_CFG 0x1400 -#define RX_FILTER_CFG_DROP_CRC_ERROR FIELD32(0x00000001) -#define RX_FILTER_CFG_DROP_PHY_ERROR FIELD32(0x00000002) -#define RX_FILTER_CFG_DROP_NOT_TO_ME FIELD32(0x00000004) -#define RX_FILTER_CFG_DROP_NOT_MY_BSSD FIELD32(0x00000008) -#define RX_FILTER_CFG_DROP_VER_ERROR FIELD32(0x00000010) -#define RX_FILTER_CFG_DROP_MULTICAST FIELD32(0x00000020) -#define RX_FILTER_CFG_DROP_BROADCAST FIELD32(0x00000040) -#define RX_FILTER_CFG_DROP_DUPLICATE FIELD32(0x00000080) -#define RX_FILTER_CFG_DROP_CF_END_ACK FIELD32(0x00000100) -#define RX_FILTER_CFG_DROP_CF_END FIELD32(0x00000200) -#define RX_FILTER_CFG_DROP_ACK FIELD32(0x00000400) -#define RX_FILTER_CFG_DROP_CTS FIELD32(0x00000800) -#define RX_FILTER_CFG_DROP_RTS FIELD32(0x00001000) -#define RX_FILTER_CFG_DROP_PSPOLL FIELD32(0x00002000) -#define RX_FILTER_CFG_DROP_BA FIELD32(0x00004000) -#define RX_FILTER_CFG_DROP_BAR FIELD32(0x00008000) -#define RX_FILTER_CFG_DROP_CNTL FIELD32(0x00010000) - -/* - * AUTO_RSP_CFG: - * AUTORESPONDER: 0: disable, 1: enable - * BAC_ACK_POLICY: 0:long, 1:short preamble - * CTS_40_MMODE: Response CTS 40MHz duplicate mode - * CTS_40_MREF: Response CTS 40MHz duplicate mode - * AR_PREAMBLE: Auto responder preamble 0:long, 1:short preamble - * DUAL_CTS_EN: Power bit value in control frame - * ACK_CTS_PSM_BIT:Power bit value in control frame - */ -#define AUTO_RSP_CFG 0x1404 -#define AUTO_RSP_CFG_AUTORESPONDER FIELD32(0x00000001) -#define AUTO_RSP_CFG_BAC_ACK_POLICY FIELD32(0x00000002) -#define AUTO_RSP_CFG_CTS_40_MMODE FIELD32(0x00000004) -#define AUTO_RSP_CFG_CTS_40_MREF FIELD32(0x00000008) -#define AUTO_RSP_CFG_AR_PREAMBLE FIELD32(0x00000010) -#define AUTO_RSP_CFG_DUAL_CTS_EN FIELD32(0x00000040) -#define AUTO_RSP_CFG_ACK_CTS_PSM_BIT FIELD32(0x00000080) - -/* - * LEGACY_BASIC_RATE: - */ -#define LEGACY_BASIC_RATE 0x1408 - -/* - * HT_BASIC_RATE: - */ -#define HT_BASIC_RATE 0x140c - -/* - * HT_CTRL_CFG: - */ -#define HT_CTRL_CFG 0x1410 - -/* - * SIFS_COST_CFG: - */ -#define SIFS_COST_CFG 0x1414 - -/* - * RX_PARSER_CFG: - * Set NAV for all received frames - */ -#define RX_PARSER_CFG 0x1418 - -/* - * TX_SEC_CNT0: - */ -#define TX_SEC_CNT0 0x1500 - -/* - * RX_SEC_CNT0: - */ -#define RX_SEC_CNT0 0x1504 - -/* - * CCMP_FC_MUTE: - */ -#define CCMP_FC_MUTE 0x1508 - -/* - * TXOP_HLDR_ADDR0: - */ -#define TXOP_HLDR_ADDR0 0x1600 - -/* - * TXOP_HLDR_ADDR1: - */ -#define TXOP_HLDR_ADDR1 0x1604 - -/* - * TXOP_HLDR_ET: - */ -#define TXOP_HLDR_ET 0x1608 - -/* - * QOS_CFPOLL_RA_DW0: - */ -#define QOS_CFPOLL_RA_DW0 0x160c - -/* - * QOS_CFPOLL_RA_DW1: - */ -#define QOS_CFPOLL_RA_DW1 0x1610 - -/* - * QOS_CFPOLL_QC: - */ -#define QOS_CFPOLL_QC 0x1614 - -/* - * RX_STA_CNT0: RX PLCP error count & RX CRC error count - */ -#define RX_STA_CNT0 0x1700 -#define RX_STA_CNT0_CRC_ERR FIELD32(0x0000ffff) -#define RX_STA_CNT0_PHY_ERR FIELD32(0xffff0000) - -/* - * RX_STA_CNT1: RX False CCA count & RX LONG frame count - */ -#define RX_STA_CNT1 0x1704 -#define RX_STA_CNT1_FALSE_CCA FIELD32(0x0000ffff) -#define RX_STA_CNT1_PLCP_ERR FIELD32(0xffff0000) - -/* - * RX_STA_CNT2: - */ -#define RX_STA_CNT2 0x1708 -#define RX_STA_CNT2_RX_DUPLI_COUNT FIELD32(0x0000ffff) -#define RX_STA_CNT2_RX_FIFO_OVERFLOW FIELD32(0xffff0000) - -/* - * TX_STA_CNT0: TX Beacon count - */ -#define TX_STA_CNT0 0x170c -#define TX_STA_CNT0_TX_FAIL_COUNT FIELD32(0x0000ffff) -#define TX_STA_CNT0_TX_BEACON_COUNT FIELD32(0xffff0000) - -/* - * TX_STA_CNT1: TX tx count - */ -#define TX_STA_CNT1 0x1710 -#define TX_STA_CNT1_TX_SUCCESS FIELD32(0x0000ffff) -#define TX_STA_CNT1_TX_RETRANSMIT FIELD32(0xffff0000) - -/* - * TX_STA_CNT2: TX tx count - */ -#define TX_STA_CNT2 0x1714 -#define TX_STA_CNT2_TX_ZERO_LEN_COUNT FIELD32(0x0000ffff) -#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000) - -/* - * TX_STA_FIFO: TX Result for specific PID status fifo register - */ -#define TX_STA_FIFO 0x1718 -#define TX_STA_FIFO_VALID FIELD32(0x00000001) -#define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e) -#define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020) -#define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040) -#define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080) -#define TX_STA_FIFO_WCID FIELD32(0x0000ff00) -#define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000) - -/* - * TX_AGG_CNT: Debug counter - */ -#define TX_AGG_CNT 0x171c -#define TX_AGG_CNT_NON_AGG_TX_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT_AGG_TX_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT0: - */ -#define TX_AGG_CNT0 0x1720 -#define TX_AGG_CNT0_AGG_SIZE_1_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT0_AGG_SIZE_2_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT1: - */ -#define TX_AGG_CNT1 0x1724 -#define TX_AGG_CNT1_AGG_SIZE_3_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT1_AGG_SIZE_4_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT2: - */ -#define TX_AGG_CNT2 0x1728 -#define TX_AGG_CNT2_AGG_SIZE_5_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT2_AGG_SIZE_6_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT3: - */ -#define TX_AGG_CNT3 0x172c -#define TX_AGG_CNT3_AGG_SIZE_7_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT3_AGG_SIZE_8_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT4: - */ -#define TX_AGG_CNT4 0x1730 -#define TX_AGG_CNT4_AGG_SIZE_9_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT4_AGG_SIZE_10_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT5: - */ -#define TX_AGG_CNT5 0x1734 -#define TX_AGG_CNT5_AGG_SIZE_11_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT5_AGG_SIZE_12_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT6: - */ -#define TX_AGG_CNT6 0x1738 -#define TX_AGG_CNT6_AGG_SIZE_13_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT6_AGG_SIZE_14_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT7: - */ -#define TX_AGG_CNT7 0x173c -#define TX_AGG_CNT7_AGG_SIZE_15_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT7_AGG_SIZE_16_COUNT FIELD32(0xffff0000) - -/* - * MPDU_DENSITY_CNT: - * TX_ZERO_DEL: TX zero length delimiter count - * RX_ZERO_DEL: RX zero length delimiter count - */ -#define MPDU_DENSITY_CNT 0x1740 -#define MPDU_DENSITY_CNT_TX_ZERO_DEL FIELD32(0x0000ffff) -#define MPDU_DENSITY_CNT_RX_ZERO_DEL FIELD32(0xffff0000) - -/* - * Security key table memory. - * MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry - * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry - * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry - * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry - * SHARED_KEY_TABLE_BASE: 32 bytes * 32-entry - * SHARED_KEY_MODE_BASE: 4 bits * 32-entry - */ -#define MAC_WCID_BASE 0x1800 -#define PAIRWISE_KEY_TABLE_BASE 0x4000 -#define MAC_IVEIV_TABLE_BASE 0x6000 -#define MAC_WCID_ATTRIBUTE_BASE 0x6800 -#define SHARED_KEY_TABLE_BASE 0x6c00 -#define SHARED_KEY_MODE_BASE 0x7000 - -#define MAC_WCID_ENTRY(__idx) \ - ( MAC_WCID_BASE + ((__idx) * sizeof(struct mac_wcid_entry)) ) -#define PAIRWISE_KEY_ENTRY(__idx) \ - ( PAIRWISE_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry)) ) -#define MAC_IVEIV_ENTRY(__idx) \ - ( MAC_IVEIV_TABLE_BASE + ((__idx) & sizeof(struct mac_iveiv_entry)) ) -#define MAC_WCID_ATTR_ENTRY(__idx) \ - ( MAC_WCID_ATTRIBUTE_BASE + ((__idx) * sizeof(u32)) ) -#define SHARED_KEY_ENTRY(__idx) \ - ( SHARED_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry)) ) -#define SHARED_KEY_MODE_ENTRY(__idx) \ - ( SHARED_KEY_MODE_BASE + ((__idx) * sizeof(u32)) ) - -struct mac_wcid_entry { - u8 mac[6]; - u8 reserved[2]; -} __attribute__ ((packed)); - -struct hw_key_entry { - u8 key[16]; - u8 tx_mic[8]; - u8 rx_mic[8]; -} __attribute__ ((packed)); - -struct mac_iveiv_entry { - u8 iv[8]; -} __attribute__ ((packed)); - -/* - * MAC_WCID_ATTRIBUTE: - */ -#define MAC_WCID_ATTRIBUTE_KEYTAB FIELD32(0x00000001) -#define MAC_WCID_ATTRIBUTE_CIPHER FIELD32(0x0000000e) -#define MAC_WCID_ATTRIBUTE_BSS_IDX FIELD32(0x00000070) -#define MAC_WCID_ATTRIBUTE_RX_WIUDF FIELD32(0x00000380) - -/* - * SHARED_KEY_MODE: - */ -#define SHARED_KEY_MODE_BSS0_KEY0 FIELD32(0x00000007) -#define SHARED_KEY_MODE_BSS0_KEY1 FIELD32(0x00000070) -#define SHARED_KEY_MODE_BSS0_KEY2 FIELD32(0x00000700) -#define SHARED_KEY_MODE_BSS0_KEY3 FIELD32(0x00007000) -#define SHARED_KEY_MODE_BSS1_KEY0 FIELD32(0x00070000) -#define SHARED_KEY_MODE_BSS1_KEY1 FIELD32(0x00700000) -#define SHARED_KEY_MODE_BSS1_KEY2 FIELD32(0x07000000) -#define SHARED_KEY_MODE_BSS1_KEY3 FIELD32(0x70000000) - -/* - * HOST-MCU communication - */ - -/* - * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. - */ -#define H2M_MAILBOX_CSR 0x7010 -#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) -#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) -#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) -#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) - -/* - * H2M_MAILBOX_CID: - */ -#define H2M_MAILBOX_CID 0x7014 -#define H2M_MAILBOX_CID_CMD0 FIELD32(0x000000ff) -#define H2M_MAILBOX_CID_CMD1 FIELD32(0x0000ff00) -#define H2M_MAILBOX_CID_CMD2 FIELD32(0x00ff0000) -#define H2M_MAILBOX_CID_CMD3 FIELD32(0xff000000) - -/* - * H2M_MAILBOX_STATUS: - */ -#define H2M_MAILBOX_STATUS 0x701c - -/* - * H2M_INT_SRC: - */ -#define H2M_INT_SRC 0x7024 - -/* - * H2M_BBP_AGENT: - */ -#define H2M_BBP_AGENT 0x7028 - -/* - * MCU_LEDCS: LED control for MCU Mailbox. - */ -#define MCU_LEDCS_LED_MODE FIELD8(0x1f) -#define MCU_LEDCS_POLARITY FIELD8(0x01) - -/* - * HW_CS_CTS_BASE: - * Carrier-sense CTS frame base address. - * It's where mac stores carrier-sense frame for carrier-sense function. - */ -#define HW_CS_CTS_BASE 0x7700 - -/* - * HW_DFS_CTS_BASE: - * FS CTS frame base address. It's where mac stores CTS frame for DFS. - */ -#define HW_DFS_CTS_BASE 0x7780 - -/* - * TXRX control registers - base address 0x3000 - */ - -/* - * TXRX_CSR1: - * rt2860b UNKNOWN reg use R/O Reg Addr 0x77d0 first.. - */ -#define TXRX_CSR1 0x77d0 - -/* - * HW_DEBUG_SETTING_BASE: - * since NULL frame won't be that long (256 byte) - * We steal 16 tail bytes to save debugging settings - */ -#define HW_DEBUG_SETTING_BASE 0x77f0 -#define HW_DEBUG_SETTING_BASE2 0x7770 - -/* - * HW_BEACON_BASE - * In order to support maximum 8 MBSS and its maximum length - * is 512 bytes for each beacon - * Three section discontinue memory segments will be used. - * 1. The original region for BCN 0~3 - * 2. Extract memory from FCE table for BCN 4~5 - * 3. Extract memory from Pair-wise key table for BCN 6~7 - * It occupied those memory of wcid 238~253 for BCN 6 - * and wcid 222~237 for BCN 7 - * - * IMPORTANT NOTE: Not sure why legacy driver does this, - * but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6. - */ -#define HW_BEACON_BASE0 0x7800 -#define HW_BEACON_BASE1 0x7a00 -#define HW_BEACON_BASE2 0x7c00 -#define HW_BEACON_BASE3 0x7e00 -#define HW_BEACON_BASE4 0x7200 -#define HW_BEACON_BASE5 0x7400 -#define HW_BEACON_BASE6 0x5dc0 -#define HW_BEACON_BASE7 0x5bc0 - -#define HW_BEACON_OFFSET(__index) \ - ( ((__index) < 4) ? ( HW_BEACON_BASE0 + (__index * 0x0200) ) : \ - (((__index) < 6) ? ( HW_BEACON_BASE4 + ((__index - 4) * 0x0200) ) : \ - (HW_BEACON_BASE6 - ((__index - 6) * 0x0200))) ) - -/* * 8051 firmware image. */ #define FIRMWARE_RT2870 "rt2870.bin" #define FIRMWARE_IMAGE_BASE 0x3000 /* - * BBP registers. - * The wordsize of the BBP is 8 bits. - */ - -/* - * BBP 1: TX Antenna - */ -#define BBP1_TX_POWER FIELD8(0x07) -#define BBP1_TX_ANTENNA FIELD8(0x18) - -/* - * BBP 3: RX Antenna - */ -#define BBP3_RX_ANTENNA FIELD8(0x18) -#define BBP3_HT40_PLUS FIELD8(0x20) - -/* - * BBP 4: Bandwidth - */ -#define BBP4_TX_BF FIELD8(0x01) -#define BBP4_BANDWIDTH FIELD8(0x18) - -/* - * RFCSR registers - * The wordsize of the RFCSR is 8 bits. - */ - -/* - * RFCSR 6: - */ -#define RFCSR6_R FIELD8(0x03) - -/* - * RFCSR 7: - */ -#define RFCSR7_RF_TUNING FIELD8(0x01) - -/* - * RFCSR 12: - */ -#define RFCSR12_TX_POWER FIELD8(0x1f) - -/* - * RFCSR 22: - */ -#define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01) - -/* - * RFCSR 23: - */ -#define RFCSR23_FREQ_OFFSET FIELD8(0x7f) - -/* - * RFCSR 30: - */ -#define RFCSR30_RF_CALIBRATION FIELD8(0x80) - -/* - * RF registers - */ - -/* - * RF 2 - */ -#define RF2_ANTENNA_RX2 FIELD32(0x00000040) -#define RF2_ANTENNA_TX1 FIELD32(0x00004000) -#define RF2_ANTENNA_RX1 FIELD32(0x00020000) - -/* - * RF 3 - */ -#define RF3_TXPOWER_G FIELD32(0x00003e00) -#define RF3_TXPOWER_A_7DBM_BOOST FIELD32(0x00000200) -#define RF3_TXPOWER_A FIELD32(0x00003c00) - -/* - * RF 4 - */ -#define RF4_TXPOWER_G FIELD32(0x000007c0) -#define RF4_TXPOWER_A_7DBM_BOOST FIELD32(0x00000040) -#define RF4_TXPOWER_A FIELD32(0x00000780) -#define RF4_FREQ_OFFSET FIELD32(0x001f8000) -#define RF4_HT40 FIELD32(0x00200000) - -/* - * EEPROM content. - * The wordsize of the EEPROM is 16 bits. - */ - -/* - * EEPROM Version - */ -#define EEPROM_VERSION 0x0001 -#define EEPROM_VERSION_FAE FIELD16(0x00ff) -#define EEPROM_VERSION_VERSION FIELD16(0xff00) - -/* - * HW MAC address. - */ -#define EEPROM_MAC_ADDR_0 0x0002 -#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_1 0x0003 -#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_2 0x0004 -#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) - -/* - * EEPROM ANTENNA config - * RXPATH: 1: 1R, 2: 2R, 3: 3R - * TXPATH: 1: 1T, 2: 2T - */ -#define EEPROM_ANTENNA 0x001a -#define EEPROM_ANTENNA_RXPATH FIELD16(0x000f) -#define EEPROM_ANTENNA_TXPATH FIELD16(0x00f0) -#define EEPROM_ANTENNA_RF_TYPE FIELD16(0x0f00) - -/* - * EEPROM NIC config - * CARDBUS_ACCEL: 0 - enable, 1 - disable - */ -#define EEPROM_NIC 0x001b -#define EEPROM_NIC_HW_RADIO FIELD16(0x0001) -#define EEPROM_NIC_DYNAMIC_TX_AGC FIELD16(0x0002) -#define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0004) -#define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0008) -#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0010) -#define EEPROM_NIC_BW40M_SB_BG FIELD16(0x0020) -#define EEPROM_NIC_BW40M_SB_A FIELD16(0x0040) -#define EEPROM_NIC_WPS_PBC FIELD16(0x0080) -#define EEPROM_NIC_BW40M_BG FIELD16(0x0100) -#define EEPROM_NIC_BW40M_A FIELD16(0x0200) - -/* - * EEPROM frequency - */ -#define EEPROM_FREQ 0x001d -#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) -#define EEPROM_FREQ_LED_MODE FIELD16(0x7f00) -#define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000) - -/* - * EEPROM LED - * POLARITY_RDY_G: Polarity RDY_G setting. - * POLARITY_RDY_A: Polarity RDY_A setting. - * POLARITY_ACT: Polarity ACT setting. - * POLARITY_GPIO_0: Polarity GPIO0 setting. - * POLARITY_GPIO_1: Polarity GPIO1 setting. - * POLARITY_GPIO_2: Polarity GPIO2 setting. - * POLARITY_GPIO_3: Polarity GPIO3 setting. - * POLARITY_GPIO_4: Polarity GPIO4 setting. - * LED_MODE: Led mode. - */ -#define EEPROM_LED1 0x001e -#define EEPROM_LED2 0x001f -#define EEPROM_LED3 0x0020 -#define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001) -#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) -#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) -#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) -#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) -#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) -#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) -#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) -#define EEPROM_LED_LED_MODE FIELD16(0x1f00) - -/* - * EEPROM LNA - */ -#define EEPROM_LNA 0x0022 -#define EEPROM_LNA_BG FIELD16(0x00ff) -#define EEPROM_LNA_A0 FIELD16(0xff00) - -/* - * EEPROM RSSI BG offset - */ -#define EEPROM_RSSI_BG 0x0023 -#define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff) -#define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00) - -/* - * EEPROM RSSI BG2 offset - */ -#define EEPROM_RSSI_BG2 0x0024 -#define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff) -#define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00) - -/* - * EEPROM RSSI A offset - */ -#define EEPROM_RSSI_A 0x0025 -#define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff) -#define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00) - -/* - * EEPROM RSSI A2 offset - */ -#define EEPROM_RSSI_A2 0x0026 -#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff) -#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00) - -/* - * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power. - * This is delta in 40MHZ. - * VALUE: Tx Power dalta value (MAX=4) - * TYPE: 1: Plus the delta value, 0: minus the delta value - * TXPOWER: Enable: - */ -#define EEPROM_TXPOWER_DELTA 0x0028 -#define EEPROM_TXPOWER_DELTA_VALUE FIELD16(0x003f) -#define EEPROM_TXPOWER_DELTA_TYPE FIELD16(0x0040) -#define EEPROM_TXPOWER_DELTA_TXPOWER FIELD16(0x0080) - -/* - * EEPROM TXPOWER 802.11BG - */ -#define EEPROM_TXPOWER_BG1 0x0029 -#define EEPROM_TXPOWER_BG2 0x0030 -#define EEPROM_TXPOWER_BG_SIZE 7 -#define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00) - -/* - * EEPROM TXPOWER 802.11A - */ -#define EEPROM_TXPOWER_A1 0x003c -#define EEPROM_TXPOWER_A2 0x0053 -#define EEPROM_TXPOWER_A_SIZE 6 -#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) - -/* - * EEPROM TXpower byrate: 20MHZ power - */ -#define EEPROM_TXPOWER_BYRATE 0x006f - -/* - * EEPROM BBP. - */ -#define EEPROM_BBP_START 0x0078 -#define EEPROM_BBP_SIZE 16 -#define EEPROM_BBP_VALUE FIELD16(0x00ff) -#define EEPROM_BBP_REG_ID FIELD16(0xff00) - -/* - * MCU mailbox commands. - */ -#define MCU_SLEEP 0x30 -#define MCU_WAKEUP 0x31 -#define MCU_RADIO_OFF 0x35 -#define MCU_CURRENT 0x36 -#define MCU_LED 0x50 -#define MCU_LED_STRENGTH 0x51 -#define MCU_LED_1 0x52 -#define MCU_LED_2 0x53 -#define MCU_LED_3 0x54 -#define MCU_RADAR 0x60 -#define MCU_BOOT_SIGNAL 0x72 -#define MCU_BBP_SIGNAL 0x80 -#define MCU_POWER_SAVE 0x83 - -/* - * MCU mailbox tokens - */ -#define TOKEN_WAKUP 3 - -/* * DMA descriptor defines. */ -#define TXD_DESC_SIZE ( 4 * sizeof(__le32) ) #define TXINFO_DESC_SIZE ( 1 * sizeof(__le32) ) -#define TXWI_DESC_SIZE ( 4 * sizeof(__le32) ) -#define RXD_DESC_SIZE ( 1 * sizeof(__le32) ) -#define RXWI_DESC_SIZE ( 4 * sizeof(__le32) ) - -/* - * TX descriptor format for TX, PRIO and Beacon Ring. - */ - -/* - * Word0 - */ -#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) - -/* - * Word1 - */ -#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) -#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) -#define TXD_W1_BURST FIELD32(0x00008000) -#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) -#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) -#define TXD_W1_DMA_DONE FIELD32(0x80000000) - -/* - * Word2 - */ -#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) - -/* - * Word3 - * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI - * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. - * 0:MGMT, 1:HCCA 2:EDCA - */ -#define TXD_W3_WIV FIELD32(0x01000000) -#define TXD_W3_QSEL FIELD32(0x06000000) -#define TXD_W3_TCO FIELD32(0x20000000) -#define TXD_W3_UCO FIELD32(0x40000000) -#define TXD_W3_ICO FIELD32(0x80000000) +#define RXINFO_DESC_SIZE ( 1 * sizeof(__le32) ) /* * TX Info structure @@ -1807,52 +101,6 @@ struct mac_iveiv_entry { #define TXINFO_W0_USB_DMA_TX_BURST FIELD32(0x80000000) /* - * TX WI structure - */ - -/* - * Word0 - * FRAG: 1 To inform TKIP engine this is a fragment. - * MIMO_PS: The remote peer is in dynamic MIMO-PS mode - * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs - * BW: Channel bandwidth 20MHz or 40 MHz - * STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED - */ -#define TXWI_W0_FRAG FIELD32(0x00000001) -#define TXWI_W0_MIMO_PS FIELD32(0x00000002) -#define TXWI_W0_CF_ACK FIELD32(0x00000004) -#define TXWI_W0_TS FIELD32(0x00000008) -#define TXWI_W0_AMPDU FIELD32(0x00000010) -#define TXWI_W0_MPDU_DENSITY FIELD32(0x000000e0) -#define TXWI_W0_TX_OP FIELD32(0x00000300) -#define TXWI_W0_MCS FIELD32(0x007f0000) -#define TXWI_W0_BW FIELD32(0x00800000) -#define TXWI_W0_SHORT_GI FIELD32(0x01000000) -#define TXWI_W0_STBC FIELD32(0x06000000) -#define TXWI_W0_IFS FIELD32(0x08000000) -#define TXWI_W0_PHYMODE FIELD32(0xc0000000) - -/* - * Word1 - */ -#define TXWI_W1_ACK FIELD32(0x00000001) -#define TXWI_W1_NSEQ FIELD32(0x00000002) -#define TXWI_W1_BW_WIN_SIZE FIELD32(0x000000fc) -#define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00) -#define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) -#define TXWI_W1_PACKETID FIELD32(0xf0000000) - -/* - * Word2 - */ -#define TXWI_W2_IV FIELD32(0xffffffff) - -/* - * Word3 - */ -#define TXWI_W3_EIV FIELD32(0xffffffff) - -/* * RX descriptor format for RX Ring. */ @@ -1867,85 +115,25 @@ struct mac_iveiv_entry { * AMSDU: rx with 802.3 header, not 802.11 header. */ -#define RXD_W0_BA FIELD32(0x00000001) -#define RXD_W0_DATA FIELD32(0x00000002) -#define RXD_W0_NULLDATA FIELD32(0x00000004) -#define RXD_W0_FRAG FIELD32(0x00000008) -#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000010) -#define RXD_W0_MULTICAST FIELD32(0x00000020) -#define RXD_W0_BROADCAST FIELD32(0x00000040) -#define RXD_W0_MY_BSS FIELD32(0x00000080) -#define RXD_W0_CRC_ERROR FIELD32(0x00000100) -#define RXD_W0_CIPHER_ERROR FIELD32(0x00000600) -#define RXD_W0_AMSDU FIELD32(0x00000800) -#define RXD_W0_HTC FIELD32(0x00001000) -#define RXD_W0_RSSI FIELD32(0x00002000) -#define RXD_W0_L2PAD FIELD32(0x00004000) -#define RXD_W0_AMPDU FIELD32(0x00008000) -#define RXD_W0_DECRYPTED FIELD32(0x00010000) -#define RXD_W0_PLCP_RSSI FIELD32(0x00020000) -#define RXD_W0_CIPHER_ALG FIELD32(0x00040000) -#define RXD_W0_LAST_AMSDU FIELD32(0x00080000) -#define RXD_W0_PLCP_SIGNAL FIELD32(0xfff00000) - -/* - * RX WI structure - */ - -/* - * Word0 - */ -#define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff) -#define RXWI_W0_KEY_INDEX FIELD32(0x00000300) -#define RXWI_W0_BSSID FIELD32(0x00001c00) -#define RXWI_W0_UDF FIELD32(0x0000e000) -#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) -#define RXWI_W0_TID FIELD32(0xf0000000) - -/* - * Word1 - */ -#define RXWI_W1_FRAG FIELD32(0x0000000f) -#define RXWI_W1_SEQUENCE FIELD32(0x0000fff0) -#define RXWI_W1_MCS FIELD32(0x007f0000) -#define RXWI_W1_BW FIELD32(0x00800000) -#define RXWI_W1_SHORT_GI FIELD32(0x01000000) -#define RXWI_W1_STBC FIELD32(0x06000000) -#define RXWI_W1_PHYMODE FIELD32(0xc0000000) - -/* - * Word2 - */ -#define RXWI_W2_RSSI0 FIELD32(0x000000ff) -#define RXWI_W2_RSSI1 FIELD32(0x0000ff00) -#define RXWI_W2_RSSI2 FIELD32(0x00ff0000) - -/* - * Word3 - */ -#define RXWI_W3_SNR0 FIELD32(0x000000ff) -#define RXWI_W3_SNR1 FIELD32(0x0000ff00) - -/* - * Macros for converting txpower from EEPROM to mac80211 value - * and from mac80211 value to register value. - */ -#define MIN_G_TXPOWER 0 -#define MIN_A_TXPOWER -7 -#define MAX_G_TXPOWER 31 -#define MAX_A_TXPOWER 15 -#define DEFAULT_TXPOWER 5 - -#define TXPOWER_G_FROM_DEV(__txpower) \ - ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) - -#define TXPOWER_G_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER) - -#define TXPOWER_A_FROM_DEV(__txpower) \ - ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) - -#define TXPOWER_A_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER) +#define RXINFO_W0_BA FIELD32(0x00000001) +#define RXINFO_W0_DATA FIELD32(0x00000002) +#define RXINFO_W0_NULLDATA FIELD32(0x00000004) +#define RXINFO_W0_FRAG FIELD32(0x00000008) +#define RXINFO_W0_UNICAST_TO_ME FIELD32(0x00000010) +#define RXINFO_W0_MULTICAST FIELD32(0x00000020) +#define RXINFO_W0_BROADCAST FIELD32(0x00000040) +#define RXINFO_W0_MY_BSS FIELD32(0x00000080) +#define RXINFO_W0_CRC_ERROR FIELD32(0x00000100) +#define RXINFO_W0_CIPHER_ERROR FIELD32(0x00000600) +#define RXINFO_W0_AMSDU FIELD32(0x00000800) +#define RXINFO_W0_HTC FIELD32(0x00001000) +#define RXINFO_W0_RSSI FIELD32(0x00002000) +#define RXINFO_W0_L2PAD FIELD32(0x00004000) +#define RXINFO_W0_AMPDU FIELD32(0x00008000) +#define RXINFO_W0_DECRYPTED FIELD32(0x00010000) +#define RXINFO_W0_PLCP_RSSI FIELD32(0x00020000) +#define RXINFO_W0_CIPHER_ALG FIELD32(0x00040000) +#define RXINFO_W0_LAST_AMSDU FIELD32(0x00080000) +#define RXINFO_W0_PLCP_SIGNAL FIELD32(0xfff00000) #endif /* RT2800USB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 27bc6b7fbfde..4d841c07c970 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1,5 +1,6 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -144,6 +145,11 @@ struct avg_val { int avg_weight; }; +enum rt2x00_chip_intf { + RT2X00_CHIP_INTF_PCI, + RT2X00_CHIP_INTF_USB, +}; + /* * Chipset identification * The chipset on the device is composed of a RT and RF chip. @@ -158,10 +164,20 @@ struct rt2x00_chip { #define RT2561 0x0302 #define RT2661 0x0401 #define RT2571 0x1300 +#define RT2860 0x0601 /* 2.4GHz PCI/CB */ +#define RT2860D 0x0681 /* 2.4GHz, 5GHz PCI/CB */ +#define RT2890 0x0701 /* 2.4GHz PCIe */ +#define RT2890D 0x0781 /* 2.4GHz, 5GHz PCIe */ +#define RT2880 0x2880 /* WSOC */ +#define RT3052 0x3052 /* WSOC */ +#define RT3090 0x3090 /* 2.4GHz PCIe */ #define RT2870 0x1600 +#define RT3070 0x1800 u16 rf; u32 rev; + + enum rt2x00_chip_intf intf; }; /* @@ -299,13 +315,6 @@ struct link { struct avg_val avg_rssi; /* - * Currently precalculated percentages of successful - * TX and RX frames. - */ - int rx_percentage; - int tx_percentage; - - /* * Work structure for scheduling periodic link tuning. */ struct delayed_work work; @@ -579,6 +588,7 @@ struct rt2x00_ops { const unsigned int eeprom_size; const unsigned int rf_size; const unsigned int tx_queues; + const unsigned int extra_tx_headroom; const struct data_queue_desc *rx; const struct data_queue_desc *tx; const struct data_queue_desc *bcn; @@ -835,9 +845,23 @@ struct rt2x00_dev { * Firmware image. */ const struct firmware *fw; + + /* + * Driver specific data. + */ + void *priv; }; /* + * Register defines. + * Some registers require multiple attempts before success, + * in those cases REGISTER_BUSY_COUNT attempts should be + * taken with a REGISTER_BUSY_DELAY interval. + */ +#define REGISTER_BUSY_COUNT 5 +#define REGISTER_BUSY_DELAY 100 + +/* * Generic RF access. * The RF is being accessed by word index. */ @@ -883,10 +907,6 @@ static inline void rt2x00_eeprom_write(struct rt2x00_dev *rt2x00dev, static inline void rt2x00_set_chip(struct rt2x00_dev *rt2x00dev, const u16 rt, const u16 rf, const u32 rev) { - INFO(rt2x00dev, - "Chipset detected - rt: %04x, rf: %04x, rev: %08x.\n", - rt, rf, rev); - rt2x00dev->chip.rt = rt; rt2x00dev->chip.rf = rf; rt2x00dev->chip.rev = rev; @@ -904,6 +924,13 @@ static inline void rt2x00_set_chip_rf(struct rt2x00_dev *rt2x00dev, rt2x00_set_chip(rt2x00dev, rt2x00dev->chip.rt, rf, rev); } +static inline void rt2x00_print_chip(struct rt2x00_dev *rt2x00dev) +{ + INFO(rt2x00dev, + "Chipset detected - rt: %04x, rf: %04x, rev: %08x.\n", + rt2x00dev->chip.rt, rt2x00dev->chip.rf, rt2x00dev->chip.rev); +} + static inline char rt2x00_rt(const struct rt2x00_chip *chipset, const u16 chip) { return (chipset->rt == chip); @@ -925,6 +952,28 @@ static inline bool rt2x00_check_rev(const struct rt2x00_chip *chipset, return ((chipset->rev & mask) == rev); } +static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev, + enum rt2x00_chip_intf intf) +{ + rt2x00dev->chip.intf = intf; +} + +static inline bool rt2x00_intf(const struct rt2x00_chip *chipset, + enum rt2x00_chip_intf intf) +{ + return (chipset->intf == intf); +} + +static inline bool rt2x00_intf_is_pci(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(&rt2x00dev->chip, RT2X00_CHIP_INTF_PCI); +} + +static inline bool rt2x00_intf_is_usb(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(&rt2x00dev->chip, RT2X00_CHIP_INTF_USB); +} + /** * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes. * @rt2x00dev: Pointer to &struct rt2x00_dev. diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 40a201e2e151..098315a271ca 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c index de36837dcf86..d291c7862e10 100644 --- a/drivers/net/wireless/rt2x00/rt2x00crypto.c +++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 68bc9bb1dbf9..7d323a763b54 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/rt2x00/rt2x00debug.h index 035cbc98c593..fa11409cb5c6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.h +++ b/drivers/net/wireless/rt2x00/rt2x00debug.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 73bbec58341e..06c43ca39bf8 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -205,6 +205,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, enum data_queue_qid qid = skb_get_queue_mapping(entry->skb); unsigned int header_length = ieee80211_get_hdrlen_from_skb(entry->skb); u8 rate_idx, rate_flags, retry_rates; + u8 skbdesc_flags = skbdesc->flags; unsigned int i; bool success; @@ -287,12 +288,12 @@ void rt2x00lib_txdone(struct queue_entry *entry, } /* - * Only send the status report to mac80211 when TX status was - * requested by it. If this was a extra frame coming through - * a mac80211 library call (RTS/CTS) then we should not send the - * status report back. + * Only send the status report to mac80211 when it's a frame + * that originated in mac80211. If this was a extra frame coming + * through a mac80211 library call (RTS/CTS) then we should not + * send the status report back. */ - if (tx_info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) + if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb); else dev_kfree_skb_irq(entry->skb); @@ -430,7 +431,6 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, rx_status->mactime = rxdesc.timestamp; rx_status->rate_idx = rate_idx; - rx_status->qual = rt2x00link_calculate_signal(rt2x00dev, rxdesc.rssi); rx_status->signal = rxdesc.rssi; rx_status->noise = rxdesc.noise; rx_status->flag = rxdesc.flags; @@ -684,6 +684,11 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->queues = rt2x00dev->ops->tx_queues; /* + * Initialize extra TX headroom required. + */ + rt2x00dev->hw->extra_tx_headroom = rt2x00dev->ops->extra_tx_headroom; + + /* * Register HW. */ status = ieee80211_register_hw(rt2x00dev->hw); diff --git a/drivers/net/wireless/rt2x00/rt2x00dump.h b/drivers/net/wireless/rt2x00/rt2x00dump.h index fdedb5122928..727019a748e7 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dump.h +++ b/drivers/net/wireless/rt2x00/rt2x00dump.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c index d2deea2f2679..34beb00c4347 100644 --- a/drivers/net/wireless/rt2x00/rt2x00firmware.c +++ b/drivers/net/wireless/rt2x00/rt2x00firmware.c @@ -1,5 +1,6 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00ht.c b/drivers/net/wireless/rt2x00/rt2x00ht.c index e3cec839e540..1056c92143a8 100644 --- a/drivers/net/wireless/rt2x00/rt2x00ht.c +++ b/drivers/net/wireless/rt2x00/rt2x00ht.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/rt2x00/rt2x00leds.c index 49671fed91d7..ca585e34d00e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00leds.c +++ b/drivers/net/wireless/rt2x00/rt2x00leds.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.h b/drivers/net/wireless/rt2x00/rt2x00leds.h index 1046977e6a12..3b46f0c3332a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00leds.h +++ b/drivers/net/wireless/rt2x00/rt2x00leds.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -33,8 +33,6 @@ enum led_type { LED_TYPE_QUALITY, }; -#ifdef CONFIG_RT2X00_LIB_LEDS - struct rt2x00_led { struct rt2x00_dev *rt2x00dev; struct led_classdev led_dev; @@ -45,6 +43,4 @@ struct rt2x00_led { #define LED_REGISTERED ( 1 << 1 ) }; -#endif /* CONFIG_RT2X00_LIB_LEDS */ - #endif /* RT2X00LEDS_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index 567f029a8cda..be2e37fb4071 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -1,5 +1,6 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -161,8 +162,10 @@ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length); * rt2x00queue_write_tx_frame - Write TX frame to hardware * @queue: Queue over which the frame should be send * @skb: The skb to send + * @local: frame is not from mac80211 */ -int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb); +int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, + bool local); /** * rt2x00queue_update_beacon - Send new beacon from mac80211 to hardware @@ -223,19 +226,6 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, struct rxdone_entry_desc *rxdesc); /** - * rt2x00link_calculate_signal - Calculate signal quality - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @rssi: RX Frame RSSI - * - * Calculate the signal quality of a frame based on the rssi - * measured during the receiving of the frame and the global - * link quality statistics measured since the start of the - * link tuning. The result is a value between 0 and 100 which - * is an indication of the signal quality. - */ -int rt2x00link_calculate_signal(struct rt2x00_dev *rt2x00dev, int rssi); - -/** * rt2x00link_start_tuner - Start periodic link tuner work * @rt2x00dev: Pointer to &struct rt2x00_dev. * diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c index c708d0be9155..0efbf5a6c254 100644 --- a/drivers/net/wireless/rt2x00/rt2x00link.c +++ b/drivers/net/wireless/rt2x00/rt2x00link.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -36,24 +36,6 @@ #define DEFAULT_RSSI -128 /* - * When no TX/RX percentage could be calculated due to lack of - * frames on the air, we fallback to a percentage of 50%. - * This will assure we will get at least get some decent value - * when the link tuner starts. - * The value will be dropped and overwritten with the correct (measured) - * value anyway during the first run of the link tuner. - */ -#define DEFAULT_PERCENTAGE 50 - -/* - * Small helper macro for percentage calculation - * This is a very simple macro with the only catch that it will - * produce a default value in case no total value was provided. - */ -#define PERCENTAGE(__value, __total) \ - ( (__total) ? (((__value) * 100) / (__total)) : (DEFAULT_PERCENTAGE) ) - -/* * Helper struct and macro to work with moving/walking averages. * When adding a value to the average value the following calculation * is needed: @@ -91,27 +73,6 @@ __new; \ }) -/* - * For calculating the Signal quality we have determined - * the total number of success and failed RX and TX frames. - * With the addition of the average RSSI value we can determine - * the link quality using the following algorithm: - * - * rssi_percentage = (avg_rssi * 100) / rssi_offset - * rx_percentage = (rx_success * 100) / rx_total - * tx_percentage = (tx_success * 100) / tx_total - * avg_signal = ((WEIGHT_RSSI * avg_rssi) + - * (WEIGHT_TX * tx_percentage) + - * (WEIGHT_RX * rx_percentage)) / 100 - * - * This value should then be checked to not be greater then 100. - * This means the values of WEIGHT_RSSI, WEIGHT_RX, WEIGHT_TX must - * sum up to 100 as well. - */ -#define WEIGHT_RSSI 20 -#define WEIGHT_RX 40 -#define WEIGHT_TX 40 - static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; @@ -304,46 +265,6 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi); } -static void rt2x00link_precalculate_signal(struct rt2x00_dev *rt2x00dev) -{ - struct link *link = &rt2x00dev->link; - struct link_qual *qual = &rt2x00dev->link.qual; - - link->rx_percentage = - PERCENTAGE(qual->rx_success, qual->rx_failed + qual->rx_success); - link->tx_percentage = - PERCENTAGE(qual->tx_success, qual->tx_failed + qual->tx_success); -} - -int rt2x00link_calculate_signal(struct rt2x00_dev *rt2x00dev, int rssi) -{ - struct link *link = &rt2x00dev->link; - int rssi_percentage = 0; - int signal; - - /* - * We need a positive value for the RSSI. - */ - if (rssi < 0) - rssi += rt2x00dev->rssi_offset; - - /* - * Calculate the different percentages, - * which will be used for the signal. - */ - rssi_percentage = PERCENTAGE(rssi, rt2x00dev->rssi_offset); - - /* - * Add the individual percentages and use the WEIGHT - * defines to calculate the current link signal. - */ - signal = ((WEIGHT_RSSI * rssi_percentage) + - (WEIGHT_TX * link->tx_percentage) + - (WEIGHT_RX * link->rx_percentage)) / 100; - - return max_t(int, signal, 100); -} - void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) { struct link *link = &rt2x00dev->link; @@ -357,9 +278,6 @@ void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) if (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count) return; - link->rx_percentage = DEFAULT_PERCENTAGE; - link->tx_percentage = DEFAULT_PERCENTAGE; - rt2x00link_reset_tuner(rt2x00dev, false); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) @@ -448,12 +366,6 @@ static void rt2x00link_tuner(struct work_struct *work) rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); /* - * Precalculate a portion of the link signal which is - * in based on the tx/rx success/failure counters. - */ - rt2x00link_precalculate_signal(rt2x00dev); - - /* * Send a signal to the led to update the led signal strength. */ rt2x00leds_led_quality(rt2x00dev, qual->rssi); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 929b85f34f38..de549c244ed8 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -66,7 +66,6 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, rts_info = IEEE80211_SKB_CB(skb); rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS; rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_CTS_PROTECT; - rts_info->flags &= ~IEEE80211_TX_CTL_REQ_TX_STATUS; if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) rts_info->flags |= IEEE80211_TX_CTL_NO_ACK; @@ -91,7 +90,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, frag_skb->data, data_length, tx_info, (struct ieee80211_rts *)(skb->data)); - retval = rt2x00queue_write_tx_frame(queue, skb); + retval = rt2x00queue_write_tx_frame(queue, skb, true); if (retval) { dev_kfree_skb_any(skb); WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); @@ -104,10 +103,8 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; enum data_queue_qid qid = skb_get_queue_mapping(skb); struct data_queue *queue; - u16 frame_control; /* * Mac80211 might be calling this function while we are trying @@ -142,7 +139,6 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * either RTS or CTS-to-self frame and handles everything * inside the hardware. */ - frame_control = le16_to_cpu(ieee80211hdr->frame_control); if ((tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS | IEEE80211_TX_RC_USE_CTS_PROTECT)) && !rt2x00dev->ops->hw->set_rts_threshold) { @@ -153,7 +149,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) goto exit_fail; } - if (rt2x00queue_write_tx_frame(queue, skb)) + if (rt2x00queue_write_tx_frame(queue, skb, false)) goto exit_fail; if (rt2x00queue_threshold(queue)) diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index cdd5154bd4c0..0feb4d0e4668 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -310,6 +310,8 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) rt2x00dev->irq = pci_dev->irq; rt2x00dev->name = pci_name(pci_dev); + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); + /* * Determine RT chipset by reading PCI header. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/rt2x00/rt2x00pci.h index 15a12487e04b..d4f9449ab0a4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.h +++ b/drivers/net/wireless/rt2x00/rt2x00pci.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -35,15 +35,6 @@ #define PCI_DEVICE_DATA(__ops) .driver_data = (kernel_ulong_t)(__ops) /* - * Register defines. - * Some registers require multiple attempts before success, - * in those cases REGISTER_BUSY_COUNT attempts should be - * taken with a REGISTER_BUSY_DELAY interval. - */ -#define REGISTER_BUSY_COUNT 5 -#define REGISTER_BUSY_DELAY 100 - -/* * Register access. */ static inline void rt2x00pci_register_read(struct rt2x00_dev *rt2x00dev, @@ -53,10 +44,9 @@ static inline void rt2x00pci_register_read(struct rt2x00_dev *rt2x00dev, *value = readl(rt2x00dev->csr.base + offset); } -static inline void -rt2x00pci_register_multiread(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u16 length) +static inline void rt2x00pci_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length) { memcpy_fromio(value, rt2x00dev->csr.base + offset, length); } @@ -68,10 +58,10 @@ static inline void rt2x00pci_register_write(struct rt2x00_dev *rt2x00dev, writel(value, rt2x00dev->csr.base + offset); } -static inline void -rt2x00pci_register_multiwrite(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const void *value, const u16 length) +static inline void rt2x00pci_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, + const u32 length) { memcpy_toio(rt2x00dev->csr.base + offset, value, length); } diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 577029efe320..239afc7a9c0b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -1,5 +1,6 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -161,10 +162,10 @@ void rt2x00queue_align_frame(struct sk_buff *skb) skb_trim(skb, frame_length); } -void rt2x00queue_align_payload(struct sk_buff *skb, unsigned int header_lengt) +void rt2x00queue_align_payload(struct sk_buff *skb, unsigned int header_length) { unsigned int frame_length = skb->len; - unsigned int align = ALIGN_SIZE(skb, header_lengt); + unsigned int align = ALIGN_SIZE(skb, header_length); if (!align) return; @@ -213,7 +214,7 @@ void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length) skb_push(skb, header_align); memmove(skb->data, skb->data + header_align, header_length); memmove(skb->data + header_length + l2pad, - skb->data + header_length + l2pad + header_align, + skb->data + header_length + l2pad + payload_align, frame_length - header_length); skbdesc->flags |= SKBDESC_L2_PADDED; } @@ -453,7 +454,8 @@ static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry, rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, queue->qid); } -int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) +int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, + bool local) { struct ieee80211_tx_info *tx_info; struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX); @@ -494,6 +496,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) skbdesc->tx_rate_idx = rate_idx; skbdesc->tx_rate_flags = rate_flags; + if (local) + skbdesc->flags |= SKBDESC_NOT_MAC80211; + /* * When hardware encryption is supported, and this frame * is to be encrypted, we should strip the IV/EIV data from diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index a5591fb2b191..70775e5ba1ac 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -94,12 +94,15 @@ enum data_queue_qid { * mac80211 but was stripped for processing by the driver. * @SKBDESC_L2_PADDED: Payload has been padded for 4-byte alignment, * the padded bytes are located between header and payload. + * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211, + * don't try to pass it back. */ enum skb_frame_desc_flags { SKBDESC_DMA_MAPPED_RX = 1 << 0, SKBDESC_DMA_MAPPED_TX = 1 << 1, SKBDESC_IV_STRIPPED = 1 << 2, - SKBDESC_L2_PADDED = 1 << 3 + SKBDESC_L2_PADDED = 1 << 3, + SKBDESC_NOT_MAC80211 = 1 << 4, }; /** diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h index 983e52e127a7..603bfc0adaa3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00reg.h +++ b/drivers/net/wireless/rt2x00/rt2x00reg.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.c b/drivers/net/wireless/rt2x00/rt2x00soc.c new file mode 100644 index 000000000000..19e684f8ffa1 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00soc.c @@ -0,0 +1,165 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Felix Fietkau <nbd@openwrt.org> + <http://rt2x00.serialmonkey.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Module: rt2x00soc + Abstract: rt2x00 generic soc device routines. + */ + +#include <linux/bug.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "rt2x00.h" +#include "rt2x00soc.h" + +static void rt2x00soc_free_reg(struct rt2x00_dev *rt2x00dev) +{ + kfree(rt2x00dev->rf); + rt2x00dev->rf = NULL; + + kfree(rt2x00dev->eeprom); + rt2x00dev->eeprom = NULL; +} + +static int rt2x00soc_alloc_reg(struct rt2x00_dev *rt2x00dev) +{ + struct platform_device *pdev = to_platform_device(rt2x00dev->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + rt2x00dev->csr.base = (void __iomem *)KSEG1ADDR(res->start); + if (!rt2x00dev->csr.base) + goto exit; + + rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); + if (!rt2x00dev->eeprom) + goto exit; + + rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); + if (!rt2x00dev->rf) + goto exit; + + return 0; + +exit: + ERROR_PROBE("Failed to allocate registers.\n"); + rt2x00soc_free_reg(rt2x00dev); + + return -ENOMEM; +} + +int rt2x00soc_probe(struct platform_device *pdev, + const unsigned short chipset, + const struct rt2x00_ops *ops) +{ + struct ieee80211_hw *hw; + struct rt2x00_dev *rt2x00dev; + int retval; + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); + if (!hw) { + ERROR_PROBE("Failed to allocate hardware.\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, hw); + + rt2x00dev = hw->priv; + rt2x00dev->dev = &pdev->dev; + rt2x00dev->ops = ops; + rt2x00dev->hw = hw; + rt2x00dev->irq = platform_get_irq(pdev, 0); + rt2x00dev->name = pdev->dev.driver->name; + + /* + * SoC devices mimic PCI behavior. + */ + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); + + rt2x00_set_chip_rt(rt2x00dev, chipset); + + retval = rt2x00soc_alloc_reg(rt2x00dev); + if (retval) + goto exit_free_device; + + retval = rt2x00lib_probe_dev(rt2x00dev); + if (retval) + goto exit_free_reg; + + return 0; + +exit_free_reg: + rt2x00soc_free_reg(rt2x00dev); + +exit_free_device: + ieee80211_free_hw(hw); + + return retval; +} + +int rt2x00soc_remove(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Free all allocated data. + */ + rt2x00lib_remove_dev(rt2x00dev); + rt2x00soc_free_reg(rt2x00dev); + ieee80211_free_hw(hw); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00soc_remove); + +#ifdef CONFIG_PM +int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00lib_suspend(rt2x00dev, state); +} +EXPORT_SYMBOL_GPL(rt2x00soc_suspend); + +int rt2x00soc_resume(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00lib_resume(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00soc_resume); +#endif /* CONFIG_PM */ + +/* + * rt2x00soc module information. + */ +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 soc library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.h b/drivers/net/wireless/rt2x00/rt2x00soc.h new file mode 100644 index 000000000000..8a3416624af5 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00soc.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Module: rt2x00soc + Abstract: Data structures for the rt2x00soc module. + */ + +#ifndef RT2X00SOC_H +#define RT2X00SOC_H + +#define KSEG1ADDR(__ptr) __ptr + +#define __rt2x00soc_probe(__chipset, __ops) \ +static int __rt2x00soc_probe(struct platform_device *pdev) \ +{ \ + return rt2x00soc_probe(pdev, (__chipset), (__ops)); \ +} + +/* + * SoC driver handlers. + */ +int rt2x00soc_probe(struct platform_device *pdev, + const unsigned short chipset, + const struct rt2x00_ops *ops); +int rt2x00soc_remove(struct platform_device *pdev); +#ifdef CONFIG_PM +int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state); +int rt2x00soc_resume(struct platform_device *pdev); +#else +#define rt2x00soc_suspend NULL +#define rt2x00soc_resume NULL +#endif /* CONFIG_PM */ + +#endif /* RT2X00SOC_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index f02b48a90593..0a751e73aa0f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -160,7 +160,7 @@ EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_large_buff); int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, - struct rt2x00_field32 field, + const struct rt2x00_field32 field, u32 *reg) { unsigned int i; @@ -653,6 +653,8 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, rt2x00dev->ops = ops; rt2x00dev->hw = hw; + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); + retval = rt2x00usb_alloc_reg(rt2x00dev); if (retval) goto exit_free_device; diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index bd2d59c85f1b..3da6841b5d42 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -26,6 +26,8 @@ #ifndef RT2X00USB_H #define RT2X00USB_H +#include <linux/usb.h> + #define to_usb_device_intf(d) \ ({ \ struct usb_interface *intf = to_usb_interface(d); \ @@ -39,17 +41,11 @@ #define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) /* - * Register defines. - * Some registers require multiple attempts before success, - * in those cases REGISTER_BUSY_COUNT attempts should be - * taken with a REGISTER_BUSY_DELAY interval. * For USB vendor requests we need to pass a timeout * time in ms, for this we use the REGISTER_TIMEOUT, * however when loading firmware a higher value is * required. In that case we use the REGISTER_TIMEOUT_FIRMWARE. */ -#define REGISTER_BUSY_COUNT 5 -#define REGISTER_BUSY_DELAY 100 #define REGISTER_TIMEOUT 500 #define REGISTER_TIMEOUT_FIRMWARE 1000 @@ -232,7 +228,7 @@ static inline int rt2x00usb_eeprom_read(struct rt2x00_dev *rt2x00dev, } /** - * rt2x00usb_regbusy_read - Read 32bit register word + * rt2x00usb_register_read - Read 32bit register word * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @value: Pointer to where register contents should be stored @@ -340,12 +336,13 @@ static inline void rt2x00usb_register_write_lock(struct rt2x00_dev *rt2x00dev, * through rt2x00usb_vendor_request_buff(). */ static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u32 length) + const unsigned int offset, + const void *value, + const u32 length) { rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, - value, length, + (void *)value, length, REGISTER_TIMEOUT32(length)); } @@ -364,7 +361,7 @@ static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, */ int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, - struct rt2x00_field32 field, + const struct rt2x00_field32 field, u32 *reg); /* diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index b20e3eac9d67..687e17dc2e9f 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -51,7 +51,7 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attampt. When the busy bit is still set at that time, + * between each attempt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. */ @@ -386,7 +386,7 @@ static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev, * The driver does not support the IV/EIV generation * in hardware. However it doesn't support the IV/EIV * inside the ieee80211 frame either, but requires it - * to be provided seperately for the descriptor. + * to be provided separately for the descriptor. * rt2x00lib will cut the IV/EIV data out of all frames * given to us by mac80211, but we must tell mac80211 * to generate the IV/EIV data. @@ -397,7 +397,7 @@ static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev, /* * SEC_CSR0 contains only single-bit fields to indicate * a particular key is valid. Because using the FIELD32() - * defines directly will cause a lot of overhead we use + * defines directly will cause a lot of overhead, we use * a calculation to determine the correct bit directly. */ mask = 1 << key->hw_key_idx; @@ -425,11 +425,11 @@ static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev, /* * rt2x00lib can't determine the correct free * key_idx for pairwise keys. We have 2 registers - * with key valid bits. The goal is simple, read - * the first register, if that is full move to + * with key valid bits. The goal is simple: read + * the first register. If that is full, move to * the next register. - * When both registers are full, we drop the key, - * otherwise we use the first invalid entry. + * When both registers are full, we drop the key. + * Otherwise, we use the first invalid entry. */ rt2x00pci_register_read(rt2x00dev, SEC_CSR2, ®); if (reg && reg == ~0) { @@ -464,8 +464,8 @@ static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev, &addr_entry, sizeof(addr_entry)); /* - * Enable pairwise lookup table for given BSS idx, - * without this received frames will not be decrypted + * Enable pairwise lookup table for given BSS idx. + * Without this, received frames will not be decrypted * by the hardware. */ rt2x00pci_register_read(rt2x00dev, SEC_CSR4, ®); @@ -487,7 +487,7 @@ static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev, /* * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate * a particular key is valid. Because using the FIELD32() - * defines directly will cause a lot of overhead we use + * defines directly will cause a lot of overhead, we use * a calculation to determine the correct bit directly. */ if (key->hw_key_idx < 32) { @@ -556,7 +556,7 @@ static void rt61pci_config_intf(struct rt2x00_dev *rt2x00dev, if (flags & CONFIG_UPDATE_TYPE) { /* * Clear current synchronisation setup. - * For the Beacon base registers we only need to clear + * For the Beacon base registers, we only need to clear * the first byte since that byte contains the VALID and OWNER * bits which (when set to 0) will invalidate the entire beacon. */ @@ -1168,8 +1168,8 @@ static int rt61pci_check_firmware(struct rt2x00_dev *rt2x00dev, return FW_BAD_LENGTH; /* - * The last 2 bytes in the firmware array are the crc checksum itself, - * this means that we should never pass those 2 bytes to the crc + * The last 2 bytes in the firmware array are the crc checksum itself. + * This means that we should never pass those 2 bytes to the crc * algorithm. */ fw_crc = (data[len - 2] << 8 | data[len - 1]); @@ -1986,7 +1986,7 @@ static void rt61pci_fill_rxdone(struct queue_entry *entry, /* * Hardware has stripped IV/EIV data from 802.11 frame during - * decryption. It has provided the data seperately but rt2x00lib + * decryption. It has provided the data separately but rt2x00lib * should decide if it should be reinserted. */ rxdesc->flags |= RX_FLAG_IV_STRIPPED; @@ -2042,7 +2042,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) * During each loop we will compare the freshly read * STA_CSR4 register value with the value read from * the previous loop. If the 2 values are equal then - * we should stop processing because the chance it + * we should stop processing because the chance is * quite big that the device has been unplugged and * we risk going into an endless loop. */ @@ -2300,6 +2300,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip_rf(rt2x00dev, value, reg); + rt2x00_print_chip(rt2x00dev); if (!rt2x00_rf(&rt2x00dev->chip, RF5225) && !rt2x00_rf(&rt2x00dev->chip, RF5325) && @@ -2330,7 +2331,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) __set_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags); /* - * Detect if this device has an hardware controlled radio. + * Detect if this device has a hardware controlled radio. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); @@ -2355,7 +2356,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) __set_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags); /* - * When working with a RF2529 chip without double antenna + * When working with a RF2529 chip without double antenna, * the antenna settings should be gathered from the NIC * eeprom word. */ @@ -2545,7 +2546,6 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; - rt2x00dev->hw->extra_tx_headroom = 0; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, @@ -2668,7 +2668,7 @@ static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, /* * We only need to perform additional register initialization - * for WMM queues/ + * for WMM queues. */ if (queue_idx >= 4) return 0; @@ -2787,19 +2787,20 @@ static const struct data_queue_desc rt61pci_queue_bcn = { }; static const struct rt2x00_ops rt61pci_ops = { - .name = KBUILD_MODNAME, - .max_sta_intf = 1, - .max_ap_intf = 4, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .rx = &rt61pci_queue_rx, - .tx = &rt61pci_queue_tx, - .bcn = &rt61pci_queue_bcn, - .lib = &rt61pci_rt2x00_ops, - .hw = &rt61pci_mac80211_ops, + .name = KBUILD_MODNAME, + .max_sta_intf = 1, + .max_ap_intf = 4, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .extra_tx_headroom = 0, + .rx = &rt61pci_queue_rx, + .tx = &rt61pci_queue_tx, + .bcn = &rt61pci_queue_bcn, + .lib = &rt61pci_rt2x00_ops, + .hw = &rt61pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt61pci_rt2x00debug, + .debugfs = &rt61pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h index 93eb699165cc..8f13810622bd 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.h +++ b/drivers/net/wireless/rt2x00/rt61pci.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -37,7 +37,7 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 120 diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 14e7bb210075..ced3b6ab5e16 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -1825,6 +1825,7 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2571, value, reg); + rt2x00_print_chip(rt2x00dev); if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0x25730) || rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) { @@ -2068,7 +2069,6 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; - rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, @@ -2305,19 +2305,20 @@ static const struct data_queue_desc rt73usb_queue_bcn = { }; static const struct rt2x00_ops rt73usb_ops = { - .name = KBUILD_MODNAME, - .max_sta_intf = 1, - .max_ap_intf = 4, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .rx = &rt73usb_queue_rx, - .tx = &rt73usb_queue_tx, - .bcn = &rt73usb_queue_bcn, - .lib = &rt73usb_rt2x00_ops, - .hw = &rt73usb_mac80211_ops, + .name = KBUILD_MODNAME, + .max_sta_intf = 1, + .max_ap_intf = 4, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .extra_tx_headroom = TXD_DESC_SIZE, + .rx = &rt73usb_queue_rx, + .tx = &rt73usb_queue_tx, + .bcn = &rt73usb_queue_bcn, + .lib = &rt73usb_rt2x00_ops, + .hw = &rt73usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt73usb_rt2x00debug, + .debugfs = &rt73usb_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h index 81fe0be51c42..7942f810e928 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.h +++ b/drivers/net/wireless/rt2x00/rt73usb.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -37,7 +37,7 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 120 diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 16429c49139c..a1a3dd15c664 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -548,7 +548,7 @@ static int rtl8180_start(struct ieee80211_hw *dev) rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); - ret = request_irq(priv->pdev->irq, &rtl8180_interrupt, + ret = request_irq(priv->pdev->irq, rtl8180_interrupt, IRQF_SHARED, KBUILD_MODNAME, dev); if (ret) { printk(KERN_ERR "%s: failed to register IRQ handler\n", diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index bf9175a8c1f4..abb4907cf296 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -119,7 +119,6 @@ struct rtl8187_priv { } hw_rev; struct sk_buff_head rx_queue; u8 signal; - u8 quality; u8 noise; u8 slot_time; u8 aifsn[4]; diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 2017ccc00145..76973b8c7099 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -320,7 +320,6 @@ static void rtl8187_rx_cb(struct urb *urb) struct ieee80211_rx_status rx_status = { 0 }; int rate, signal; u32 flags; - u32 quality; unsigned long f; spin_lock_irqsave(&priv->rx_queue.lock, f); @@ -338,10 +337,9 @@ static void rtl8187_rx_cb(struct urb *urb) (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); flags = le32_to_cpu(hdr->flags); /* As with the RTL8187B below, the AGC is used to calculate - * signal strength and quality. In this case, the scaling + * signal strength. In this case, the scaling * constants are derived from the output of p54usb. */ - quality = 130 - ((41 * hdr->agc) >> 6); signal = -4 - ((27 * hdr->agc) >> 6); rx_status.antenna = (hdr->signal >> 7) & 1; rx_status.mactime = le64_to_cpu(hdr->mac_time); @@ -354,23 +352,18 @@ static void rtl8187_rx_cb(struct urb *urb) * In testing, none of these quantities show qualitative * agreement with AP signal strength, except for the AGC, * which is inversely proportional to the strength of the - * signal. In the following, the quality and signal strength - * are derived from the AGC. The arbitrary scaling constants + * signal. In the following, the signal strength + * is derived from the AGC. The arbitrary scaling constants * are chosen to make the results close to the values obtained * for a BCM4312 using b43 as the driver. The noise is ignored * for now. */ flags = le32_to_cpu(hdr->flags); - quality = 170 - hdr->agc; signal = 14 - hdr->agc / 2; rx_status.antenna = (hdr->rssi >> 7) & 1; rx_status.mactime = le64_to_cpu(hdr->mac_time); } - if (quality > 100) - quality = 100; - rx_status.qual = quality; - priv->quality = quality; rx_status.signal = signal; priv->signal = signal; rate = (flags >> 20) & 0xF; diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 88060e117541..785e0244e305 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -1,6 +1,6 @@ menuconfig WL12XX tristate "TI wl12xx driver support" - depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + depends on MAC80211 && EXPERIMENTAL ---help--- This will enable TI wl12xx driver support. The drivers make use of the mac80211 stack. @@ -42,6 +42,7 @@ config WL1251_SDIO config WL1271 tristate "TI wl1271 support" depends on WL12XX && SPI_MASTER && GENERIC_HARDIRQS + depends on INET select FW_LOADER select CRC7 ---help--- diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index 998e4b6252bd..054533f7a124 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h @@ -269,6 +269,7 @@ struct wl1251 { void (*set_power)(bool enable); int irq; + bool use_eeprom; enum wl1251_state state; struct mutex mutex; @@ -354,6 +355,8 @@ struct wl1251 { /* is firmware in elp mode */ bool elp; + struct delayed_work elp_work; + /* we can be in psm, but not in elp, we have to differentiate */ bool psm; @@ -374,6 +377,8 @@ struct wl1251 { u8 buffer_busyword[WL1251_BUSY_WORD_LEN]; struct wl1251_rx_descriptor *rx_descriptor; + struct ieee80211_vif *vif; + u32 chip_id; char fw_ver[21]; }; diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.c b/drivers/net/wireless/wl12xx/wl1251_acx.c index 10b26c4532c9..acfa086dbfc5 100644 --- a/drivers/net/wireless/wl12xx/wl1251_acx.c +++ b/drivers/net/wireless/wl12xx/wl1251_acx.c @@ -494,7 +494,7 @@ out: return ret; } -int wl1251_acx_beacon_filter_opt(struct wl1251 *wl) +int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter) { struct acx_beacon_filter_option *beacon_filter; int ret; @@ -507,7 +507,7 @@ int wl1251_acx_beacon_filter_opt(struct wl1251 *wl) goto out; } - beacon_filter->enable = 0; + beacon_filter->enable = enable_filter; beacon_filter->max_num_beacons = 0; ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_OPT, @@ -525,6 +525,7 @@ out: int wl1251_acx_beacon_filter_table(struct wl1251 *wl) { struct acx_beacon_filter_ie_table *ie_table; + int idx = 0; int ret; wl1251_debug(DEBUG_ACX, "acx beacon filter table"); @@ -535,8 +536,10 @@ int wl1251_acx_beacon_filter_table(struct wl1251 *wl) goto out; } - ie_table->num_ie = 0; - memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE); + /* configure default beacon pass-through rules */ + ie_table->num_ie = 1; + ie_table->table[idx++] = BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN; + ie_table->table[idx++] = BEACON_RULE_PASS_ON_APPEARANCE; ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, ie_table, sizeof(*ie_table)); @@ -550,6 +553,35 @@ out: return ret; } +int wl1251_acx_conn_monit_params(struct wl1251 *wl) +{ + struct acx_conn_monit_params *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx connection monitor parameters"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->synch_fail_thold = SYNCH_FAIL_DEFAULT_THRESHOLD; + acx->bss_lose_timeout = NO_BEACON_DEFAULT_TIMEOUT; + + ret = wl1251_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, + acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("failed to set connection monitor " + "parameters: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + int wl1251_acx_sg_enable(struct wl1251 *wl) { struct acx_bt_wlan_coex *pta; @@ -916,3 +948,31 @@ out: kfree(mem_conf); return ret; } + +int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim) +{ + struct wl1251_acx_wr_tbtt_and_dtim *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx tbtt and dtim"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->tbtt = tbtt; + acx->dtim = dtim; + + ret = wl1251_cmd_configure(wl, ACX_WR_TBTT_AND_DTIM, + acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("failed to set tbtt and dtim: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.h b/drivers/net/wireless/wl12xx/wl1251_acx.h index cafb91459504..652371432cd8 100644 --- a/drivers/net/wireless/wl12xx/wl1251_acx.h +++ b/drivers/net/wireless/wl12xx/wl1251_acx.h @@ -450,6 +450,11 @@ struct acx_beacon_filter_option { (BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \ BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE)) +#define BEACON_RULE_PASS_ON_CHANGE BIT(0) +#define BEACON_RULE_PASS_ON_APPEARANCE BIT(1) + +#define BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN (37) + struct acx_beacon_filter_ie_table { struct acx_header header; @@ -458,6 +463,16 @@ struct acx_beacon_filter_ie_table { u8 pad[3]; } __attribute__ ((packed)); +#define SYNCH_FAIL_DEFAULT_THRESHOLD 10 /* number of beacons */ +#define NO_BEACON_DEFAULT_TIMEOUT (500) /* in microseconds */ + +struct acx_conn_monit_params { + struct acx_header header; + + u32 synch_fail_thold; /* number of beacons missed */ + u32 bss_lose_timeout; /* number of TU's from synch fail */ +}; + enum { SG_ENABLE = 0, SG_DISABLE, @@ -1134,6 +1149,23 @@ struct wl1251_acx_mem_map { u32 num_rx_mem_blocks; } __attribute__ ((packed)); + +struct wl1251_acx_wr_tbtt_and_dtim { + + struct acx_header header; + + /* Time in TUs between two consecutive beacons */ + u16 tbtt; + + /* + * DTIM period + * For BSS: Number of TBTTs in a DTIM period (range: 1-10) + * For IBSS: value shall be set to 1 + */ + u8 dtim; + u8 padding; +} __attribute__ ((packed)); + /************************************************************************* Host Interrupt Register (WiLink -> Host) @@ -1273,8 +1305,9 @@ int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time); int wl1251_acx_group_address_tbl(struct wl1251 *wl); int wl1251_acx_service_period_timeout(struct wl1251 *wl); int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold); -int wl1251_acx_beacon_filter_opt(struct wl1251 *wl); +int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter); int wl1251_acx_beacon_filter_table(struct wl1251 *wl); +int wl1251_acx_conn_monit_params(struct wl1251 *wl); int wl1251_acx_sg_enable(struct wl1251 *wl); int wl1251_acx_sg_cfg(struct wl1251 *wl); int wl1251_acx_cca_threshold(struct wl1251 *wl); @@ -1288,5 +1321,6 @@ int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats); int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime); int wl1251_acx_rate_policies(struct wl1251 *wl); int wl1251_acx_mem_cfg(struct wl1251 *wl); +int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim); #endif /* __WL1251_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_boot.c b/drivers/net/wireless/wl12xx/wl1251_boot.c index 452d748e42c6..2e733e7bdfd4 100644 --- a/drivers/net/wireless/wl12xx/wl1251_boot.c +++ b/drivers/net/wireless/wl12xx/wl1251_boot.c @@ -296,8 +296,12 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) WL1251_ACX_INTR_INIT_COMPLETE; wl1251_boot_target_enable_interrupts(wl); - /* unmask all mbox events */ - wl->event_mask = 0xffffffff; + wl->event_mask = SCAN_COMPLETE_EVENT_ID | BSS_LOSE_EVENT_ID | + SYNCHRONIZATION_TIMEOUT_EVENT_ID | + ROAMING_TRIGGER_LOW_RSSI_EVENT_ID | + ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID | + REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID | + BT_PTA_PREDICTION_EVENT_ID; ret = wl1251_event_unmask(wl); if (ret < 0) { @@ -314,8 +318,8 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) static int wl1251_boot_upload_firmware(struct wl1251 *wl) { int addr, chunk_num, partition_limit; - size_t fw_data_len; - u8 *p; + size_t fw_data_len, len; + u8 *p, *buf; /* whal_FwCtrl_LoadFwImageSm() */ @@ -334,6 +338,12 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) return -EIO; } + buf = kmalloc(CHUNK_SIZE, GFP_KERNEL); + if (!buf) { + wl1251_error("allocation for firmware upload chunk failed"); + return -ENOMEM; + } + wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START, WL1251_PART_DOWN_MEM_SIZE, WL1251_PART_DOWN_REG_START, @@ -364,7 +374,11 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); - wl1251_mem_write(wl, addr, p, CHUNK_SIZE); + + /* need to copy the chunk for dma */ + len = CHUNK_SIZE; + memcpy(buf, p, len); + wl1251_mem_write(wl, addr, buf, len); chunk_num++; } @@ -372,9 +386,16 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) /* 10.4 upload the last chunk */ addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; + + /* need to copy the chunk for dma */ + len = fw_data_len % CHUNK_SIZE; + memcpy(buf, p, len); + wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", - fw_data_len % CHUNK_SIZE, p, addr); - wl1251_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); + len, p, addr); + wl1251_mem_write(wl, addr, buf, len); + + kfree(buf); return 0; } @@ -473,13 +494,19 @@ int wl1251_boot(struct wl1251 *wl) goto out; /* 2. start processing NVS file */ - ret = wl1251_boot_upload_nvs(wl); - if (ret < 0) - goto out; - - /* write firmware's last address (ie. it's length) to - * ACX_EEPROMLESS_IND_REG */ - wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); + if (wl->use_eeprom) { + wl1251_reg_write32(wl, ACX_REG_EE_START, START_EEPROM_MGR); + msleep(4000); + wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, USE_EEPROM); + } else { + ret = wl1251_boot_upload_nvs(wl); + if (ret < 0) + goto out; + + /* write firmware's last address (ie. it's length) to + * ACX_EEPROMLESS_IND_REG */ + wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); + } /* 6. read the EEPROM parameters */ tmp = wl1251_reg_read32(wl, SCR_PAD2); diff --git a/drivers/net/wireless/wl12xx/wl1251_event.c b/drivers/net/wireless/wl12xx/wl1251_event.c index 00076c4a8a21..020d764f9c13 100644 --- a/drivers/net/wireless/wl12xx/wl1251_event.c +++ b/drivers/net/wireless/wl12xx/wl1251_event.c @@ -79,6 +79,21 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) } } + if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID && wl->psm) { + wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT"); + + /* indicate to the stack, that beacons have been lost */ + ieee80211_beacon_loss(wl->vif); + } + + if (vector & REGAINED_BSS_EVENT_ID) { + if (wl->psm_requested) { + ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + if (ret < 0) + return ret; + } + } + return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1251_init.c b/drivers/net/wireless/wl12xx/wl1251_init.c index b2ee4f468fc4..5cb573383eeb 100644 --- a/drivers/net/wireless/wl12xx/wl1251_init.c +++ b/drivers/net/wireless/wl12xx/wl1251_init.c @@ -147,7 +147,8 @@ int wl1251_hw_init_beacon_filter(struct wl1251 *wl) { int ret; - ret = wl1251_acx_beacon_filter_opt(wl); + /* disable beacon filtering at this stage */ + ret = wl1251_acx_beacon_filter_opt(wl, false); if (ret < 0) return ret; @@ -364,6 +365,11 @@ int wl1251_hw_init(struct wl1251 *wl) if (ret < 0) goto out_free_data_path; + /* Initialize connection monitoring thresholds */ + ret = wl1251_acx_conn_monit_params(wl); + if (ret < 0) + goto out_free_data_path; + /* Beacon filtering */ ret = wl1251_hw_init_beacon_filter(wl); if (ret < 0) diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 1103256ad989..ff4be7bf5d36 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -28,6 +28,7 @@ #include <linux/irq.h> #include <linux/crc32.h> #include <linux/etherdevice.h> +#include <linux/vmalloc.h> #include "wl1251.h" #include "wl12xx_80211.h" @@ -83,7 +84,7 @@ static int wl1251_fetch_firmware(struct wl1251 *wl) } wl->fw_len = fw->size; - wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); + wl->fw = vmalloc(wl->fw_len); if (!wl->fw) { wl1251_error("could not allocate memory for the firmware"); @@ -183,8 +184,11 @@ static int wl1251_chip_wakeup(struct wl1251 *wl) wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)", wl->chip_id); break; - case CHIP_ID_1251_PG10: case CHIP_ID_1251_PG11: + wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG11)", + wl->chip_id); + break; + case CHIP_ID_1251_PG10: default: wl1251_error("unsupported chip id: 0x%x", wl->chip_id); ret = -ENODEV; @@ -208,9 +212,10 @@ out: return ret; } +#define WL1251_IRQ_LOOP_COUNT 10 static void wl1251_irq_work(struct work_struct *work) { - u32 intr; + u32 intr, ctr = WL1251_IRQ_LOOP_COUNT; struct wl1251 *wl = container_of(work, struct wl1251, irq_work); int ret; @@ -231,78 +236,86 @@ static void wl1251_irq_work(struct work_struct *work) intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr); - if (wl->data_path) { - wl->rx_counter = - wl1251_mem_read32(wl, wl->data_path->rx_control_addr); - - /* We handle a frmware bug here */ - switch ((wl->rx_counter - wl->rx_handled) & 0xf) { - case 0: - wl1251_debug(DEBUG_IRQ, "RX: FW and host in sync"); - intr &= ~WL1251_ACX_INTR_RX0_DATA; - intr &= ~WL1251_ACX_INTR_RX1_DATA; - break; - case 1: - wl1251_debug(DEBUG_IRQ, "RX: FW +1"); - intr |= WL1251_ACX_INTR_RX0_DATA; - intr &= ~WL1251_ACX_INTR_RX1_DATA; - break; - case 2: - wl1251_debug(DEBUG_IRQ, "RX: FW +2"); - intr |= WL1251_ACX_INTR_RX0_DATA; - intr |= WL1251_ACX_INTR_RX1_DATA; - break; - default: - wl1251_warning("RX: FW and host out of sync: %d", - wl->rx_counter - wl->rx_handled); - break; - } - - wl->rx_handled = wl->rx_counter; + do { + if (wl->data_path) { + wl->rx_counter = wl1251_mem_read32( + wl, wl->data_path->rx_control_addr); + + /* We handle a frmware bug here */ + switch ((wl->rx_counter - wl->rx_handled) & 0xf) { + case 0: + wl1251_debug(DEBUG_IRQ, + "RX: FW and host in sync"); + intr &= ~WL1251_ACX_INTR_RX0_DATA; + intr &= ~WL1251_ACX_INTR_RX1_DATA; + break; + case 1: + wl1251_debug(DEBUG_IRQ, "RX: FW +1"); + intr |= WL1251_ACX_INTR_RX0_DATA; + intr &= ~WL1251_ACX_INTR_RX1_DATA; + break; + case 2: + wl1251_debug(DEBUG_IRQ, "RX: FW +2"); + intr |= WL1251_ACX_INTR_RX0_DATA; + intr |= WL1251_ACX_INTR_RX1_DATA; + break; + default: + wl1251_warning( + "RX: FW and host out of sync: %d", + wl->rx_counter - wl->rx_handled); + break; + } + wl->rx_handled = wl->rx_counter; - wl1251_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter); - } + wl1251_debug(DEBUG_IRQ, "RX counter: %d", + wl->rx_counter); + } - intr &= wl->intr_mask; + intr &= wl->intr_mask; - if (intr == 0) { - wl1251_debug(DEBUG_IRQ, "INTR is 0"); - wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, - ~(wl->intr_mask)); + if (intr == 0) { + wl1251_debug(DEBUG_IRQ, "INTR is 0"); + goto out_sleep; + } - goto out_sleep; - } + if (intr & WL1251_ACX_INTR_RX0_DATA) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); + wl1251_rx(wl); + } - if (intr & WL1251_ACX_INTR_RX0_DATA) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); - wl1251_rx(wl); - } + if (intr & WL1251_ACX_INTR_RX1_DATA) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); + wl1251_rx(wl); + } - if (intr & WL1251_ACX_INTR_RX1_DATA) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); - wl1251_rx(wl); - } + if (intr & WL1251_ACX_INTR_TX_RESULT) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); + wl1251_tx_complete(wl); + } - if (intr & WL1251_ACX_INTR_TX_RESULT) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); - wl1251_tx_complete(wl); - } + if (intr & (WL1251_ACX_INTR_EVENT_A | + WL1251_ACX_INTR_EVENT_B)) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", + intr); + if (intr & WL1251_ACX_INTR_EVENT_A) + wl1251_event_handle(wl, 0); + else + wl1251_event_handle(wl, 1); + } - if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr); - if (intr & WL1251_ACX_INTR_EVENT_A) - wl1251_event_handle(wl, 0); - else - wl1251_event_handle(wl, 1); - } + if (intr & WL1251_ACX_INTR_INIT_COMPLETE) + wl1251_debug(DEBUG_IRQ, + "WL1251_ACX_INTR_INIT_COMPLETE"); - if (intr & WL1251_ACX_INTR_INIT_COMPLETE) - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); + if (--ctr == 0) + break; - wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); + intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); + } while (intr); out_sleep: + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); wl1251_ps_elp_sleep(wl); out: @@ -506,6 +519,12 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, conf->type, conf->mac_addr); mutex_lock(&wl->mutex); + if (wl->vif) { + ret = -EBUSY; + goto out; + } + + wl->vif = conf->vif; switch (conf->type) { case NL80211_IFTYPE_STATION: @@ -535,7 +554,12 @@ out: static void wl1251_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) { + struct wl1251 *wl = hw->priv; + + mutex_lock(&wl->mutex); wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); + wl->vif = NULL; + mutex_unlock(&wl->mutex); } static int wl1251_build_null_data(struct wl1251 *wl) @@ -552,7 +576,8 @@ static int wl1251_build_null_data(struct wl1251 *wl) memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC); + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template, sizeof(template)); @@ -565,7 +590,10 @@ static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid) memcpy(template.bssid, wl->bssid, ETH_ALEN); memcpy(template.ta, wl->mac_addr, ETH_ALEN); - template.aid = aid; + + /* aid in PS-Poll has its two MSBs each set to 1 */ + template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); + template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template, @@ -1087,8 +1115,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, wl->beacon_int = bss_conf->beacon_int; wl->dtim_period = bss_conf->dtim_period; - /* FIXME: call join */ - + ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, + wl->dtim_period); wl->aid = bss_conf->aid; ret = wl1251_build_ps_poll(wl, wl->aid); @@ -1308,7 +1336,9 @@ int wl1251_init_ieee80211(struct wl1251 *wl) wl->hw->channel_change_time = 10000; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_NOISE_DBM; + IEEE80211_HW_NOISE_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_BEACON_FILTER; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->max_scan_ssids = 1; @@ -1351,6 +1381,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) skb_queue_head_init(&wl->tx_queue); INIT_WORK(&wl->filter_work, wl1251_filter_work); + INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); wl->channel = WL1251_DEFAULT_CHANNEL; wl->scanning = false; wl->default_key = 0; @@ -1368,6 +1399,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->beacon_int = WL1251_DEFAULT_BEACON_INT; wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; + wl->vif = NULL; for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) wl->tx_frames[i] = NULL; @@ -1409,7 +1441,7 @@ int wl1251_free_hw(struct wl1251 *wl) kfree(wl->target_mem_map); kfree(wl->data_path); - kfree(wl->fw); + vfree(wl->fw); wl->fw = NULL; kfree(wl->nvs); wl->nvs = NULL; @@ -1426,4 +1458,5 @@ EXPORT_SYMBOL_GPL(wl1251_free_hw); MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); -MODULE_ALIAS("spi:wl12xx"); +MODULE_ALIAS("spi:wl1251"); +MODULE_FIRMWARE(WL1251_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/wl1251_netlink.h b/drivers/net/wireless/wl12xx/wl1251_netlink.h deleted file mode 100644 index ee36695e134e..000000000000 --- a/drivers/net/wireless/wl12xx/wl1251_netlink.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of wl1251 - * - * Copyright (C) 2009 Nokia Corporation - * - * Contact: Kalle Valo <kalle.valo@nokia.com> - * - * 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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __WL1251_NETLINK_H__ -#define __WL1251_NETLINK_H__ - -int wl1251_nl_register(void); -void wl1251_nl_unregister(void); - -#endif /* __WL1251_NETLINK_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c index c53e28727ed4..9931b197ff77 100644 --- a/drivers/net/wireless/wl12xx/wl1251_ps.c +++ b/drivers/net/wireless/wl12xx/wl1251_ps.c @@ -28,17 +28,41 @@ #define WL1251_WAKEUP_TIMEOUT 2000 -/* Routines to toggle sleep mode while in ELP */ -void wl1251_ps_elp_sleep(struct wl1251 *wl) +void wl1251_elp_work(struct work_struct *work) { + struct delayed_work *dwork; + struct wl1251 *wl; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1251, elp_work); + + wl1251_debug(DEBUG_PSM, "elp work"); + + mutex_lock(&wl->mutex); + if (wl->elp || !wl->psm) - return; + goto out; wl1251_debug(DEBUG_PSM, "chip to elp"); - wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); - wl->elp = true; + +out: + mutex_unlock(&wl->mutex); +} + +#define ELP_ENTRY_DELAY 5 + +/* Routines to toggle sleep mode while in ELP */ +void wl1251_ps_elp_sleep(struct wl1251 *wl) +{ + unsigned long delay; + + if (wl->psm) { + cancel_delayed_work(&wl->elp_work); + delay = msecs_to_jiffies(ELP_ENTRY_DELAY); + ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay); + } } int wl1251_ps_elp_wakeup(struct wl1251 *wl) @@ -119,6 +143,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) case STATION_POWER_SAVE_MODE: wl1251_debug(DEBUG_PSM, "entering psm"); + /* enable beacon filtering */ + ret = wl1251_acx_beacon_filter_opt(wl, true); + if (ret < 0) + return ret; + ret = wl1251_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, wl->listen_int); @@ -142,6 +171,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) if (ret < 0) return ret; + /* disable beacon filtering */ + ret = wl1251_acx_beacon_filter_opt(wl, false); + if (ret < 0) + return ret; + ret = wl1251_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, wl->listen_int); diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.h b/drivers/net/wireless/wl12xx/wl1251_ps.h index db036fe12f25..c688ac57aee4 100644 --- a/drivers/net/wireless/wl12xx/wl1251_ps.h +++ b/drivers/net/wireless/wl12xx/wl1251_ps.h @@ -31,6 +31,7 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode); void wl1251_ps_elp_sleep(struct wl1251 *wl); int wl1251_ps_elp_wakeup(struct wl1251 *wl); +void wl1251_elp_work(struct work_struct *work); #endif /* __WL1251_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_reg.h b/drivers/net/wireless/wl12xx/wl1251_reg.h index 06e1bd94a739..0ca3b4326056 100644 --- a/drivers/net/wireless/wl12xx/wl1251_reg.h +++ b/drivers/net/wireless/wl12xx/wl1251_reg.h @@ -370,6 +370,7 @@ enum wl12xx_acx_int_reg { EEPROM location specified in the EE_ADDR register. The Wlan hardware hardware clears this bit automatically. *===============================================*/ +#define EE_CTL (REGISTERS_BASE + 0x2000) #define ACX_EE_CTL_REG EE_CTL #define EE_WRITE 0x00000001ul #define EE_READ 0x00000002ul @@ -380,6 +381,7 @@ enum wl12xx_acx_int_reg { This register specifies the address within the EEPROM from/to which to read/write data. ===============================================*/ +#define EE_ADDR (REGISTERS_BASE + 0x2008) #define ACX_EE_ADDR_REG EE_ADDR /*=============================================== @@ -389,8 +391,12 @@ enum wl12xx_acx_int_reg { data from the EEPROM or the write data to be written to the EEPROM. ===============================================*/ +#define EE_DATA (REGISTERS_BASE + 0x2004) #define ACX_EE_DATA_REG EE_DATA +#define EEPROM_ACCESS_TO 10000 /* timeout counter */ +#define START_EEPROM_MGR 0x00000001 + /*=============================================== EEPROM Base Address - 32bit RW ------------------------------------------ diff --git a/drivers/net/wireless/wl12xx/wl1251_rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c index 17c54b59ef86..f84cc89cbffc 100644 --- a/drivers/net/wireless/wl12xx/wl1251_rx.c +++ b/drivers/net/wireless/wl12xx/wl1251_rx.c @@ -72,10 +72,6 @@ static void wl1251_rx_status(struct wl1251 *wl, } status->signal = desc->rssi; - status->qual = (desc->rssi - WL1251_RX_MIN_RSSI) * 100 / - (WL1251_RX_MAX_RSSI - WL1251_RX_MIN_RSSI); - status->qual = min(status->qual, 100); - status->qual = max(status->qual, 0); /* * FIXME: guessing that snr needs to be divided by two, otherwise @@ -153,7 +149,7 @@ static void wl1251_rx_body(struct wl1251 *wl, beacon ? "beacon" : ""); memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx(wl->hw, skb); + ieee80211_rx_ni(wl->hw, skb); } static void wl1251_rx_ack(struct wl1251 *wl) diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c index 14eff2b3d4c6..9cc8c323830f 100644 --- a/drivers/net/wireless/wl12xx/wl1251_spi.c +++ b/drivers/net/wireless/wl12xx/wl1251_spi.c @@ -270,6 +270,8 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi) return -ENODEV; } + wl->use_eeprom = pdata->use_eeprom; + ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl); if (ret < 0) { wl1251_error("request_irq() failed: %d", ret); @@ -307,7 +309,7 @@ static int __devexit wl1251_spi_remove(struct spi_device *spi) static struct spi_driver wl1251_spi_driver = { .driver = { - .name = "wl12xx", + .name = "wl1251", .bus = &spi_bus_type, .owner = THIS_MODULE, }, diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 55818f94017b..94359b1a861f 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -32,6 +32,8 @@ #include <linux/bitops.h> #include <net/mac80211.h> +#include "wl1271_conf.h" + #define DRIVER_NAME "wl1271" #define DRIVER_PREFIX DRIVER_NAME ": " @@ -97,21 +99,42 @@ enum { } while (0) #define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ - CFG_BSSID_FILTER_EN) + CFG_BSSID_FILTER_EN | \ + CFG_MC_FILTER_EN) #define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \ CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \ CFG_RX_CTL_EN | CFG_RX_BCN_EN | \ CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) +#define WL1271_DEFAULT_BASIC_RATE_SET (CONF_TX_RATE_MASK_ALL) + #define WL1271_FW_NAME "wl1271-fw.bin" #define WL1271_NVS_NAME "wl1271-nvs.bin" -#define WL1271_BUSY_WORD_LEN 8 +/* + * Enable/disable 802.11a support for WL1273 + */ +#undef WL1271_80211A_ENABLED + +/* + * FIXME: for the wl1271, a busy word count of 1 here will result in a more + * optimal SPI interface. There is some SPI bug however, causing RXS time outs + * with this mode occasionally on boot, so lets have three for now. A value of + * three should make sure, that the chipset will always be ready, though this + * will impact throughput and latencies slightly. + */ +#define WL1271_BUSY_WORD_CNT 3 +#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32)) #define WL1271_ELP_HW_STATE_ASLEEP 0 #define WL1271_ELP_HW_STATE_IRQ 1 +#define WL1271_DEFAULT_BEACON_INT 100 +#define WL1271_DEFAULT_DTIM_PERIOD 1 + +#define ACX_TX_DESCRIPTORS 32 + enum wl1271_state { WL1271_STATE_OFF, WL1271_STATE_ON, @@ -134,6 +157,8 @@ struct wl1271_partition { struct wl1271_partition_set { struct wl1271_partition mem; struct wl1271_partition reg; + struct wl1271_partition mem2; + struct wl1271_partition mem3; }; struct wl1271; @@ -258,15 +283,15 @@ struct wl1271_debugfs { /* FW status registers */ struct wl1271_fw_status { - u32 intr; + __le32 intr; u8 fw_rx_counter; u8 drv_rx_counter; u8 reserved; u8 tx_results_counter; - u32 rx_pkt_descs[NUM_RX_PKT_DESC]; - u32 tx_released_blks[NUM_TX_QUEUES]; - u32 fw_localtime; - u32 padding[2]; + __le32 rx_pkt_descs[NUM_RX_PKT_DESC]; + __le32 tx_released_blks[NUM_TX_QUEUES]; + __le32 fw_localtime; + __le32 padding[2]; } __attribute__ ((packed)); struct wl1271_rx_mem_pool_addr { @@ -274,6 +299,15 @@ struct wl1271_rx_mem_pool_addr { u32 addr_extra; }; +struct wl1271_scan { + u8 state; + u8 ssid[IW_ESSID_MAX_SIZE+1]; + size_t ssid_len; + u8 active; + u8 high_prio; + u8 probe_requests; +}; + struct wl1271 { struct ieee80211_hw *hw; bool mac80211_registered; @@ -288,10 +322,7 @@ struct wl1271 { enum wl1271_state state; struct mutex mutex; - int physical_mem_addr; - int physical_reg_addr; - int virtual_mem_addr; - int virtual_reg_addr; + struct wl1271_partition_set part; struct wl1271_chip chip; @@ -308,7 +339,6 @@ struct wl1271 { u8 bss_type; u8 ssid[IW_ESSID_MAX_SIZE + 1]; u8 ssid_len; - u8 listen_int; int channel; struct wl1271_acx_mem_map *target_mem_map; @@ -332,10 +362,14 @@ struct wl1271 { bool tx_queue_stopped; struct work_struct tx_work; - struct work_struct filter_work; /* Pending TX frames */ - struct sk_buff *tx_frames[16]; + struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS]; + + /* Security sequence number counters */ + u8 tx_security_last_seq; + u16 tx_security_seq_16; + u32 tx_security_seq_32; /* FW Rx counter */ u32 rx_counter; @@ -354,10 +388,17 @@ struct wl1271 { /* Are we currently scanning */ bool scanning; + struct wl1271_scan scan; /* Our association ID */ u16 aid; + /* currently configured rate set */ + u32 basic_rate_set; + + /* The current band */ + enum ieee80211_band band; + /* Default key (for WEP) */ u32 default_key; @@ -368,6 +409,7 @@ struct wl1271 { bool elp; struct completion *elp_compl; + struct delayed_work elp_work; /* we can be in psm, but not in elp, we have to differentiate */ bool psm; @@ -375,6 +417,9 @@ struct wl1271 { /* PSM mode requested */ bool psm_requested; + /* retry counter for PSM entries */ + u8 psm_entry_retry; + /* in dBm */ int power_level; @@ -383,11 +428,20 @@ struct wl1271 { u32 buffer_32; u32 buffer_cmd; - u8 buffer_busyword[WL1271_BUSY_WORD_LEN]; - struct wl1271_rx_descriptor *rx_descriptor; + u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; struct wl1271_fw_status *fw_status; struct wl1271_tx_hw_res_if *tx_res_if; + + struct ieee80211_vif *vif; + + /* Used for a workaround to send disconnect before rejoining */ + bool joined; + + /* Current chipset configuration */ + struct conf_drv_settings conf; + + struct list_head list; }; int wl1271_plt_start(struct wl1271 *wl); @@ -404,4 +458,13 @@ int wl1271_plt_stop(struct wl1271 *wl); /* WL1271 needs a 200ms sleep after power on */ #define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */ +static inline bool wl1271_11a_enabled(void) +{ +#ifdef WL1271_80211A_ENABLED + return true; +#else + return false; +#endif +} + #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index f622a4092615..5cc89bbdac7a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -34,8 +34,7 @@ #include "wl1271_spi.h" #include "wl1271_ps.h" -int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event, - u8 listen_interval) +int wl1271_acx_wake_up_conditions(struct wl1271 *wl) { struct acx_wake_up_condition *wake_up; int ret; @@ -48,8 +47,8 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event, goto out; } - wake_up->wake_up_event = wake_up_event; - wake_up->listen_interval = listen_interval; + wake_up->wake_up_event = wl->conf.conn.wake_up_event; + wake_up->listen_interval = wl->conf.conn.listen_interval; ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, wake_up, sizeof(*wake_up)); @@ -137,7 +136,12 @@ int wl1271_acx_tx_power(struct wl1271 *wl, int power) goto out; } - acx->current_tx_power = power * 10; + /* + * FIXME: This is a workaround needed while we don't the correct + * calibration, to avoid distortions + */ + /* acx->current_tx_power = power * 10; */ + acx->current_tx_power = 120; ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx)); if (ret < 0) { @@ -193,7 +197,7 @@ int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, return 0; } -int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time) +int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl) { struct acx_rx_msdu_lifetime *acx; int ret; @@ -206,7 +210,7 @@ int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time) goto out; } - acx->lifetime = life_time; + acx->lifetime = cpu_to_le32(wl->conf.rx.rx_msdu_life_time); ret = wl1271_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME, acx, sizeof(*acx)); if (ret < 0) { @@ -232,8 +236,8 @@ int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter) goto out; } - rx_config->config_options = config; - rx_config->filter_options = filter; + rx_config->config_options = cpu_to_le32(config); + rx_config->filter_options = cpu_to_le32(filter); ret = wl1271_cmd_configure(wl, ACX_RX_CFG, rx_config, sizeof(*rx_config)); @@ -260,7 +264,7 @@ int wl1271_acx_pd_threshold(struct wl1271 *wl) goto out; } - /* FIXME: threshold value not set */ + pd->threshold = cpu_to_le32(wl->conf.rx.packet_detection_threshold); ret = wl1271_cmd_configure(wl, ACX_PD_THRESHOLD, pd, sizeof(*pd)); if (ret < 0) { @@ -300,7 +304,8 @@ out: return ret; } -int wl1271_acx_group_address_tbl(struct wl1271 *wl) +int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, + void *mc_list, u32 mc_list_len) { struct acx_dot11_grp_addr_tbl *acx; int ret; @@ -314,9 +319,9 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl) } /* MAC filtering */ - acx->enabled = 0; - acx->num_groups = 0; - memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN); + acx->enabled = enable; + acx->num_groups = mc_list_len; + memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN); ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, acx, sizeof(*acx)); @@ -343,8 +348,8 @@ int wl1271_acx_service_period_timeout(struct wl1271 *wl) wl1271_debug(DEBUG_ACX, "acx service period timeout"); - rx_timeout->ps_poll_timeout = RX_TIMEOUT_PS_POLL_DEF; - rx_timeout->upsd_timeout = RX_TIMEOUT_UPSD_DEF; + rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.rx.ps_poll_timeout); + rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.rx.upsd_timeout); ret = wl1271_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT, rx_timeout, sizeof(*rx_timeout)); @@ -372,7 +377,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold) goto out; } - rts->threshold = rts_threshold; + rts->threshold = cpu_to_le16(rts_threshold); ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); if (ret < 0) { @@ -385,20 +390,29 @@ out: return ret; } -int wl1271_acx_beacon_filter_opt(struct wl1271 *wl) +int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter) { - struct acx_beacon_filter_option *beacon_filter; - int ret; + struct acx_beacon_filter_option *beacon_filter = NULL; + int ret = 0; wl1271_debug(DEBUG_ACX, "acx beacon filter opt"); + if (enable_filter && + wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED) + goto out; + beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL); if (!beacon_filter) { ret = -ENOMEM; goto out; } - beacon_filter->enable = 0; + beacon_filter->enable = enable_filter; + + /* + * When set to zero, and the filter is enabled, beacons + * without the unicast TIM bit set are dropped. + */ beacon_filter->max_num_beacons = 0; ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_OPT, @@ -416,7 +430,9 @@ out: int wl1271_acx_beacon_filter_table(struct wl1271 *wl) { struct acx_beacon_filter_ie_table *ie_table; + int i, idx = 0; int ret; + bool vendor_spec = false; wl1271_debug(DEBUG_ACX, "acx beacon filter table"); @@ -426,8 +442,32 @@ int wl1271_acx_beacon_filter_table(struct wl1271 *wl) goto out; } + /* configure default beacon pass-through rules */ ie_table->num_ie = 0; - memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE); + for (i = 0; i < wl->conf.conn.bcn_filt_ie_count; i++) { + struct conf_bcn_filt_rule *r = &(wl->conf.conn.bcn_filt_ie[i]); + ie_table->table[idx++] = r->ie; + ie_table->table[idx++] = r->rule; + + if (r->ie == WLAN_EID_VENDOR_SPECIFIC) { + /* only one vendor specific ie allowed */ + if (vendor_spec) + continue; + + /* for vendor specific rules configure the + additional fields */ + memcpy(&(ie_table->table[idx]), r->oui, + CONF_BCN_IE_OUI_LEN); + idx += CONF_BCN_IE_OUI_LEN; + ie_table->table[idx++] = r->type; + memcpy(&(ie_table->table[idx]), r->version, + CONF_BCN_IE_VER_LEN); + idx += CONF_BCN_IE_VER_LEN; + vendor_spec = true; + } + + ie_table->num_ie++; + } ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, ie_table, sizeof(*ie_table)); @@ -441,6 +481,36 @@ out: return ret; } +int wl1271_acx_conn_monit_params(struct wl1271 *wl) +{ + struct acx_conn_monit_params *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx connection monitor parameters"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->synch_fail_thold = cpu_to_le32(wl->conf.conn.synch_fail_thold); + acx->bss_lose_timeout = cpu_to_le32(wl->conf.conn.bss_lose_timeout); + + ret = wl1271_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("failed to set connection monitor " + "parameters: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + + int wl1271_acx_sg_enable(struct wl1271 *wl) { struct acx_bt_wlan_coex *pta; @@ -470,6 +540,7 @@ out: int wl1271_acx_sg_cfg(struct wl1271 *wl) { struct acx_bt_wlan_coex_param *param; + struct conf_sg_settings *c = &wl->conf.sg; int ret; wl1271_debug(DEBUG_ACX, "acx sg cfg"); @@ -481,34 +552,19 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) } /* BT-WLAN coext parameters */ - param->min_rate = RATE_INDEX_24MBPS; - param->bt_hp_max_time = PTA_BT_HP_MAXTIME_DEF; - param->wlan_hp_max_time = PTA_WLAN_HP_MAX_TIME_DEF; - param->sense_disable_timer = PTA_SENSE_DISABLE_TIMER_DEF; - param->rx_time_bt_hp = PTA_PROTECTIVE_RX_TIME_DEF; - param->tx_time_bt_hp = PTA_PROTECTIVE_TX_TIME_DEF; - param->rx_time_bt_hp_fast = PTA_PROTECTIVE_RX_TIME_FAST_DEF; - param->tx_time_bt_hp_fast = PTA_PROTECTIVE_TX_TIME_FAST_DEF; - param->wlan_cycle_fast = PTA_CYCLE_TIME_FAST_DEF; - param->bt_anti_starvation_period = PTA_ANTI_STARVE_PERIOD_DEF; - param->next_bt_lp_packet = PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF; - param->wake_up_beacon = PTA_TIME_BEFORE_BEACON_DEF; - param->hp_dm_max_guard_time = PTA_HPDM_MAX_TIME_DEF; - param->next_wlan_packet = PTA_TIME_OUT_NEXT_WLAN_DEF; - param->antenna_type = PTA_ANTENNA_TYPE_DEF; - param->signal_type = PTA_SIGNALING_TYPE_DEF; - param->afh_leverage_on = PTA_AFH_LEVERAGE_ON_DEF; - param->quiet_cycle_num = PTA_NUMBER_QUIET_CYCLE_DEF; - param->max_cts = PTA_MAX_NUM_CTS_DEF; - param->wlan_packets_num = PTA_NUMBER_OF_WLAN_PACKETS_DEF; - param->bt_packets_num = PTA_NUMBER_OF_BT_PACKETS_DEF; - param->missed_rx_avalanche = PTA_RX_FOR_AVALANCHE_DEF; - param->wlan_elp_hp = PTA_ELP_HP_DEF; - param->bt_anti_starvation_cycles = PTA_ANTI_STARVE_NUM_CYCLE_DEF; - param->ack_mode_dual_ant = PTA_ACK_MODE_DEF; - param->pa_sd_enable = PTA_ALLOW_PA_SD_DEF; - param->pta_auto_mode_enable = PTA_AUTO_MODE_NO_CTS_DEF; - param->bt_hp_respected_num = PTA_BT_HP_RESPECTED_DEF; + param->per_threshold = cpu_to_le32(c->per_threshold); + param->max_scan_compensation_time = + cpu_to_le32(c->max_scan_compensation_time); + param->nfs_sample_interval = cpu_to_le16(c->nfs_sample_interval); + param->load_ratio = c->load_ratio; + param->auto_ps_mode = c->auto_ps_mode; + param->probe_req_compensation = c->probe_req_compensation; + param->scan_window_compensation = c->scan_window_compensation; + param->antenna_config = c->antenna_config; + param->beacon_miss_threshold = c->beacon_miss_threshold; + param->rate_adaptation_threshold = + cpu_to_le32(c->rate_adaptation_threshold); + param->rate_adaptation_snr = c->rate_adaptation_snr; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); if (ret < 0) { @@ -534,8 +590,8 @@ int wl1271_acx_cca_threshold(struct wl1271 *wl) goto out; } - detection->rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D; - detection->tx_energy_detection = 0; + detection->rx_cca_threshold = cpu_to_le16(wl->conf.rx.rx_cca_threshold); + detection->tx_energy_detection = wl->conf.tx.tx_energy_detection; ret = wl1271_cmd_configure(wl, ACX_CCA_THRESHOLD, detection, sizeof(*detection)); @@ -562,10 +618,10 @@ int wl1271_acx_bcn_dtim_options(struct wl1271 *wl) goto out; } - bb->beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE; - bb->broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE; - bb->rx_broadcast_in_ps = RX_BROADCAST_IN_PS_DEF_VALUE; - bb->ps_poll_threshold = CONSECUTIVE_PS_POLL_FAILURE_DEF; + bb->beacon_rx_timeout = cpu_to_le16(wl->conf.conn.beacon_rx_timeout); + bb->broadcast_timeout = cpu_to_le16(wl->conf.conn.broadcast_timeout); + bb->rx_broadcast_in_ps = wl->conf.conn.rx_broadcast_in_ps; + bb->ps_poll_threshold = wl->conf.conn.ps_poll_threshold; ret = wl1271_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb)); if (ret < 0) { @@ -591,7 +647,7 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid) goto out; } - acx_aid->aid = aid; + acx_aid->aid = cpu_to_le16(aid); ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid)); if (ret < 0) { @@ -618,9 +674,8 @@ int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask) } /* high event mask is unused */ - mask->high_event_mask = 0xffffffff; - - mask->event_mask = event_mask; + mask->high_event_mask = cpu_to_le32(0xffffffff); + mask->event_mask = cpu_to_le32(event_mask); ret = wl1271_cmd_configure(wl, ACX_EVENT_MBOX_MASK, mask, sizeof(*mask)); @@ -703,9 +758,10 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) return 0; } -int wl1271_acx_rate_policies(struct wl1271 *wl) +int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates) { struct acx_rate_policy *acx; + struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf; int ret = 0; wl1271_debug(DEBUG_ACX, "acx rate policies"); @@ -718,11 +774,11 @@ int wl1271_acx_rate_policies(struct wl1271 *wl) } /* configure one default (one-size-fits-all) rate class */ - acx->rate_class_cnt = 1; - acx->rate_class[0].enabled_rates = ACX_RATE_MASK_ALL; - acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT; - acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT; - acx->rate_class[0].aflags = 0; + acx->rate_class_cnt = cpu_to_le32(1); + acx->rate_class[0].enabled_rates = cpu_to_le32(enabled_rates); + acx->rate_class[0].short_retry_limit = c->short_retry_limit; + acx->rate_class[0].long_retry_limit = c->long_retry_limit; + acx->rate_class[0].aflags = c->aflags; ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { @@ -749,22 +805,14 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl) goto out; } - /* - * FIXME: Configure each AC with appropriate values (most suitable - * values will probably be different for each AC. - */ - for (i = 0; i < WL1271_ACX_AC_COUNT; i++) { - acx->ac = i; - - /* - * FIXME: The following default values originate from - * the TI reference driver. What do they mean? - */ - acx->cw_min = 15; - acx->cw_max = 63; - acx->aifsn = 3; + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + struct conf_tx_ac_category *c = &(wl->conf.tx.ac_conf[i]); + acx->ac = c->ac; + acx->cw_min = c->cw_min; + acx->cw_max = cpu_to_le16(c->cw_max); + acx->aifsn = c->aifsn; acx->reserved = 0; - acx->tx_op_limit = 0; + acx->tx_op_limit = cpu_to_le16(c->tx_op_limit); ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); if (ret < 0) { @@ -793,12 +841,15 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl) goto out; } - /* FIXME: configure each TID with a different AC reference */ - for (i = 0; i < WL1271_ACX_TID_COUNT; i++) { - acx->queue_id = i; - acx->tsid = WL1271_ACX_AC_BE; - acx->ps_scheme = WL1271_ACX_PS_SCHEME_LEGACY; - acx->ack_policy = WL1271_ACX_ACK_POLICY_LEGACY; + for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { + struct conf_tx_tid *c = &(wl->conf.tx.tid_conf[i]); + acx->queue_id = c->queue_id; + acx->channel_type = c->channel_type; + acx->tsid = c->tsid; + acx->ps_scheme = c->ps_scheme; + acx->ack_policy = c->ack_policy; + acx->apsd_conf[0] = cpu_to_le32(c->apsd_conf[0]); + acx->apsd_conf[1] = cpu_to_le32(c->apsd_conf[1]); ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); if (ret < 0) { @@ -826,7 +877,7 @@ int wl1271_acx_frag_threshold(struct wl1271 *wl) goto out; } - acx->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; + acx->frag_threshold = cpu_to_le16(wl->conf.tx.frag_threshold); ret = wl1271_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of frag threshold failed: %d", ret); @@ -852,8 +903,8 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl) goto out; } - acx->tx_compl_timeout = WL1271_ACX_TX_COMPL_TIMEOUT; - acx->tx_compl_threshold = WL1271_ACX_TX_COMPL_THRESHOLD; + acx->tx_compl_timeout = cpu_to_le16(wl->conf.tx.tx_compl_timeout); + acx->tx_compl_threshold = cpu_to_le16(wl->conf.tx.tx_compl_threshold); ret = wl1271_cmd_configure(wl, ACX_TX_CONFIG_OPT, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of tx options failed: %d", ret); @@ -879,11 +930,11 @@ int wl1271_acx_mem_cfg(struct wl1271 *wl) } /* memory config */ - mem_conf->num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS); + mem_conf->num_stations = DEFAULT_NUM_STATIONS; mem_conf->rx_mem_block_num = ACX_RX_MEM_BLOCKS; mem_conf->tx_min_mem_block_num = ACX_TX_MIN_MEM_BLOCKS; mem_conf->num_ssid_profiles = ACX_NUM_SSID_PROFILES; - mem_conf->total_tx_descriptors = ACX_TX_DESCRIPTORS; + mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, sizeof(*mem_conf)); @@ -906,7 +957,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) return ret; wl->target_mem_map = kzalloc(sizeof(struct wl1271_acx_mem_map), - GFP_KERNEL); + GFP_KERNEL); if (!wl->target_mem_map) { wl1271_error("couldn't allocate target memory map"); return -ENOMEM; @@ -923,7 +974,8 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) } /* initialize TX block book keeping */ - wl->tx_blocks_available = wl->target_mem_map->num_tx_mem_blocks; + wl->tx_blocks_available = + le32_to_cpu(wl->target_mem_map->num_tx_mem_blocks); wl1271_debug(DEBUG_TX, "available tx blocks: %d", wl->tx_blocks_available); @@ -943,10 +995,10 @@ int wl1271_acx_init_rx_interrupt(struct wl1271 *wl) goto out; } - rx_conf->threshold = WL1271_RX_INTR_THRESHOLD_DEF; - rx_conf->timeout = WL1271_RX_INTR_TIMEOUT_DEF; - rx_conf->mblk_threshold = USHORT_MAX; /* Disabled */ - rx_conf->queue_type = RX_QUEUE_TYPE_RX_LOW_PRIORITY; + rx_conf->threshold = cpu_to_le16(wl->conf.rx.irq_pkt_threshold); + rx_conf->timeout = cpu_to_le16(wl->conf.rx.irq_timeout); + rx_conf->mblk_threshold = cpu_to_le16(wl->conf.rx.irq_blk_threshold); + rx_conf->queue_type = wl->conf.rx.queue_type; ret = wl1271_cmd_configure(wl, ACX_RX_CONFIG_OPT, rx_conf, sizeof(*rx_conf)); @@ -959,3 +1011,124 @@ out: kfree(rx_conf); return ret; } + +int wl1271_acx_smart_reflex(struct wl1271 *wl) +{ + struct acx_smart_reflex_state *sr_state = NULL; + struct acx_smart_reflex_config_params *sr_param = NULL; + int i, ret; + + wl1271_debug(DEBUG_ACX, "acx smart reflex"); + + sr_param = kzalloc(sizeof(*sr_param), GFP_KERNEL); + if (!sr_param) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < CONF_SR_ERR_TBL_COUNT; i++) { + struct conf_mart_reflex_err_table *e = + &(wl->conf.init.sr_err_tbl[i]); + + sr_param->error_table[i].len = e->len; + sr_param->error_table[i].upper_limit = e->upper_limit; + memcpy(sr_param->error_table[i].values, e->values, e->len); + } + + ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_PARAMS, + sr_param, sizeof(*sr_param)); + if (ret < 0) { + wl1271_warning("failed to set smart reflex params: %d", ret); + goto out; + } + + sr_state = kzalloc(sizeof(*sr_state), GFP_KERNEL); + if (!sr_state) { + ret = -ENOMEM; + goto out; + } + + /* enable smart reflex */ + sr_state->enable = wl->conf.init.sr_enable; + + ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_STATE, + sr_state, sizeof(*sr_state)); + if (ret < 0) { + wl1271_warning("failed to set smart reflex params: %d", ret); + goto out; + } + +out: + kfree(sr_state); + kfree(sr_param); + return ret; + +} + +int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable) +{ + struct wl1271_acx_bet_enable *acx = NULL; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx bet enable"); + + if (enable && wl->conf.conn.bet_enable == CONF_BET_MODE_DISABLE) + goto out; + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE; + acx->max_consecutive = wl->conf.conn.bet_max_consecutive; + + ret = wl1271_cmd_configure(wl, ACX_BET_ENABLE, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx bet enable failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, + u8 version) +{ + struct wl1271_acx_arp_filter *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->version = version; + acx->enable = enable; + + if (enable == true) { + if (version == ACX_IPV4_VERSION) + memcpy(acx->address, address, ACX_IPV4_ADDR_SIZE); + else if (version == ACX_IPV6_VERSION) + memcpy(acx->address, address, sizeof(acx->address)); + else + wl1271_error("Invalid IP version"); + } + + ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("failed to set arp ip filter: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index 9068daaf0ddf..2ce0a8128542 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -61,8 +61,9 @@ WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA) -#define WL1271_INTR_MASK (WL1271_ACX_INTR_EVENT_A | \ - WL1271_ACX_INTR_EVENT_B | \ +#define WL1271_INTR_MASK (WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA) /* Target's information element */ @@ -70,11 +71,11 @@ struct acx_header { struct wl1271_cmd_header cmd; /* acx (or information element) header */ - u16 id; + __le16 id; /* payload length (not including headers */ - u16 len; -}; + __le16 len; +} __attribute__ ((packed)); struct acx_error_counter { struct acx_header header; @@ -82,21 +83,21 @@ struct acx_error_counter { /* The number of PLCP errors since the last time this */ /* information element was interrogated. This field is */ /* automatically cleared when it is interrogated.*/ - u32 PLCP_error; + __le32 PLCP_error; /* The number of FCS errors since the last time this */ /* information element was interrogated. This field is */ /* automatically cleared when it is interrogated.*/ - u32 FCS_error; + __le32 FCS_error; /* The number of MPDUs without PLCP header errors received*/ /* since the last time this information element was interrogated. */ /* This field is automatically cleared when it is interrogated.*/ - u32 valid_frame; + __le32 valid_frame; /* the number of missed sequence numbers in the squentially */ /* values of frames seq numbers */ - u32 seq_num_miss; + __le32 seq_num_miss; } __attribute__ ((packed)); struct acx_revision { @@ -125,7 +126,7 @@ struct acx_revision { * (1 = first spin, 2 = second spin, and so on). * bits 24 - 31: Chip ID - The WiLink chip ID. */ - u32 hw_version; + __le32 hw_version; } __attribute__ ((packed)); enum wl1271_psm_mode { @@ -170,7 +171,6 @@ enum { #define DP_RX_PACKET_RING_CHUNK_NUM 2 #define DP_TX_PACKET_RING_CHUNK_NUM 2 #define DP_TX_COMPLETE_TIME_OUT 20 -#define FW_TX_CMPLT_BLOCK_SIZE 16 #define TX_MSDU_LIFETIME_MIN 0 #define TX_MSDU_LIFETIME_MAX 3000 @@ -186,7 +186,7 @@ struct acx_rx_msdu_lifetime { * The maximum amount of time, in TU, before the * firmware discards the MSDU. */ - u32 lifetime; + __le32 lifetime; } __attribute__ ((packed)); /* @@ -273,14 +273,14 @@ struct acx_rx_msdu_lifetime { struct acx_rx_config { struct acx_header header; - u32 config_options; - u32 filter_options; + __le32 config_options; + __le32 filter_options; } __attribute__ ((packed)); struct acx_packet_detection { struct acx_header header; - u32 threshold; + __le32 threshold; } __attribute__ ((packed)); @@ -302,8 +302,8 @@ struct acx_slot { } __attribute__ ((packed)); -#define ADDRESS_GROUP_MAX (8) -#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX) +#define ACX_MC_ADDRESS_GROUP_MAX (8) +#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX) struct acx_dot11_grp_addr_tbl { struct acx_header header; @@ -314,40 +314,17 @@ struct acx_dot11_grp_addr_tbl { u8 mac_table[ADDRESS_GROUP_MAX_LEN]; } __attribute__ ((packed)); - -#define RX_TIMEOUT_PS_POLL_MIN 0 -#define RX_TIMEOUT_PS_POLL_MAX (200000) -#define RX_TIMEOUT_PS_POLL_DEF (15) -#define RX_TIMEOUT_UPSD_MIN 0 -#define RX_TIMEOUT_UPSD_MAX (200000) -#define RX_TIMEOUT_UPSD_DEF (15) - struct acx_rx_timeout { struct acx_header header; - /* - * The longest time the STA will wait to receive - * traffic from the AP after a PS-poll has been - * transmitted. - */ - u16 ps_poll_timeout; - - /* - * The longest time the STA will wait to receive - * traffic from the AP after a frame has been sent - * from an UPSD enabled queue. - */ - u16 upsd_timeout; + __le16 ps_poll_timeout; + __le16 upsd_timeout; } __attribute__ ((packed)); -#define RTS_THRESHOLD_MIN 0 -#define RTS_THRESHOLD_MAX 4096 -#define RTS_THRESHOLD_DEF 2347 - struct acx_rts_threshold { struct acx_header header; - u16 threshold; + __le16 threshold; u8 pad[2]; } __attribute__ ((packed)); @@ -408,6 +385,13 @@ struct acx_beacon_filter_ie_table { u8 pad[3]; } __attribute__ ((packed)); +struct acx_conn_monit_params { + struct acx_header header; + + __le32 synch_fail_thold; /* number of beacons missed */ + __le32 bss_lose_timeout; /* number of TU's from synch fail */ +} __attribute__ ((packed)); + enum { SG_ENABLE = 0, SG_DISABLE, @@ -431,6 +415,25 @@ struct acx_bt_wlan_coex { u8 pad[3]; } __attribute__ ((packed)); +struct acx_smart_reflex_state { + struct acx_header header; + + u8 enable; + u8 padding[3]; +} __attribute__ ((packed)); + +struct smart_reflex_err_table { + u8 len; + s8 upper_limit; + s8 values[14]; +} __attribute__ ((packed)); + +struct acx_smart_reflex_config_params { + struct acx_header header; + + struct smart_reflex_err_table error_table[3]; +} __attribute__ ((packed)); + #define PTA_ANTENNA_TYPE_DEF (0) #define PTA_BT_HP_MAXTIME_DEF (2000) #define PTA_WLAN_HP_MAX_TIME_DEF (5000) @@ -463,150 +466,34 @@ struct acx_bt_wlan_coex { struct acx_bt_wlan_coex_param { struct acx_header header; - /* - * The minimum rate of a received WLAN packet in the STA, - * during protective mode, of which a new BT-HP request - * during this Rx will always be respected and gain the antenna. - */ - u32 min_rate; - - /* Max time the BT HP will be respected. */ - u16 bt_hp_max_time; - - /* Max time the WLAN HP will be respected. */ - u16 wlan_hp_max_time; - - /* - * The time between the last BT activity - * and the moment when the sense mode returns - * to SENSE_INACTIVE. - */ - u16 sense_disable_timer; - - /* Time before the next BT HP instance */ - u16 rx_time_bt_hp; - u16 tx_time_bt_hp; - - /* range: 10-20000 default: 1500 */ - u16 rx_time_bt_hp_fast; - u16 tx_time_bt_hp_fast; - - /* range: 2000-65535 default: 8700 */ - u16 wlan_cycle_fast; - - /* range: 0 - 15000 (Msec) default: 1000 */ - u16 bt_anti_starvation_period; - - /* range 400-10000(Usec) default: 3000 */ - u16 next_bt_lp_packet; - - /* Deafult: worst case for BT DH5 traffic */ - u16 wake_up_beacon; - - /* range: 0-50000(Usec) default: 1050 */ - u16 hp_dm_max_guard_time; - - /* - * This is to prevent both BT & WLAN antenna - * starvation. - * Range: 100-50000(Usec) default:2550 - */ - u16 next_wlan_packet; - - /* 0 -> shared antenna */ - u8 antenna_type; - - /* - * 0 -> TI legacy - * 1 -> Palau - */ - u8 signal_type; - - /* - * BT AFH status - * 0 -> no AFH - * 1 -> from dedicated GPIO - * 2 -> AFH on (from host) - */ - u8 afh_leverage_on; - - /* - * The number of cycles during which no - * TX will be sent after 1 cycle of RX - * transaction in protective mode - */ - u8 quiet_cycle_num; - - /* - * The maximum number of CTSs that will - * be sent for receiving RX packet in - * protective mode - */ - u8 max_cts; - - /* - * The number of WLAN packets - * transferred in common mode before - * switching to BT. - */ - u8 wlan_packets_num; - - /* - * The number of BT packets - * transferred in common mode before - * switching to WLAN. - */ - u8 bt_packets_num; - - /* range: 1-255 default: 5 */ - u8 missed_rx_avalanche; - - /* range: 0-1 default: 1 */ - u8 wlan_elp_hp; - - /* range: 0 - 15 default: 4 */ - u8 bt_anti_starvation_cycles; - - u8 ack_mode_dual_ant; - - /* - * Allow PA_SD assertion/de-assertion - * during enabled BT activity. - */ - u8 pa_sd_enable; - - /* - * Enable/Disable PTA in auto mode: - * Support Both Active & P.S modes - */ - u8 pta_auto_mode_enable; - - /* range: 0 - 20 default: 1 */ - u8 bt_hp_respected_num; + __le32 per_threshold; + __le32 max_scan_compensation_time; + __le16 nfs_sample_interval; + u8 load_ratio; + u8 auto_ps_mode; + u8 probe_req_compensation; + u8 scan_window_compensation; + u8 antenna_config; + u8 beacon_miss_threshold; + __le32 rate_adaptation_threshold; + s8 rate_adaptation_snr; + u8 padding[3]; } __attribute__ ((packed)); -#define CCA_THRSH_ENABLE_ENERGY_D 0x140A -#define CCA_THRSH_DISABLE_ENERGY_D 0xFFEF - struct acx_energy_detection { struct acx_header header; /* The RX Clear Channel Assessment threshold in the PHY */ - u16 rx_cca_threshold; + __le16 rx_cca_threshold; u8 tx_energy_detection; u8 pad; } __attribute__ ((packed)); -#define BCN_RX_TIMEOUT_DEF_VALUE 10000 -#define BROADCAST_RX_TIMEOUT_DEF_VALUE 20000 -#define RX_BROADCAST_IN_PS_DEF_VALUE 1 -#define CONSECUTIVE_PS_POLL_FAILURE_DEF 4 - struct acx_beacon_broadcast { struct acx_header header; - u16 beacon_rx_timeout; - u16 broadcast_timeout; + __le16 beacon_rx_timeout; + __le16 broadcast_timeout; /* Enables receiving of broadcast packets in PS mode */ u8 rx_broadcast_in_ps; @@ -619,8 +506,8 @@ struct acx_beacon_broadcast { struct acx_event_mask { struct acx_header header; - u32 event_mask; - u32 high_event_mask; /* Unused */ + __le32 event_mask; + __le32 high_event_mask; /* Unused */ } __attribute__ ((packed)); #define CFG_RX_FCS BIT(2) @@ -657,11 +544,15 @@ struct acx_event_mask { #define SCAN_TRIGGERED BIT(2) #define SCAN_PRIORITY_HIGH BIT(3) +/* When set, disable HW encryption */ +#define DF_ENCRYPTION_DISABLE 0x01 +#define DF_SNIFF_MODE_ENABLE 0x80 + struct acx_feature_config { struct acx_header header; - u32 options; - u32 data_flow_options; + __le32 options; + __le32 data_flow_options; } __attribute__ ((packed)); struct acx_current_tx_power { @@ -671,14 +562,6 @@ struct acx_current_tx_power { u8 padding[3]; } __attribute__ ((packed)); -enum acx_wake_up_event { - WAKE_UP_EVENT_BEACON_BITMAP = 0x01, /* Wake on every Beacon*/ - WAKE_UP_EVENT_DTIM_BITMAP = 0x02, /* Wake on every DTIM*/ - WAKE_UP_EVENT_N_DTIM_BITMAP = 0x04, /* Wake on every Nth DTIM */ - WAKE_UP_EVENT_N_BEACONS_BITMAP = 0x08, /* Wake on every Nth Beacon */ - WAKE_UP_EVENT_BITS_MASK = 0x0F -}; - struct acx_wake_up_condition { struct acx_header header; @@ -693,7 +576,7 @@ struct acx_aid { /* * To be set when associated with an AP. */ - u16 aid; + __le16 aid; u8 pad[2]; } __attribute__ ((packed)); @@ -725,152 +608,152 @@ struct acx_ctsprotect { } __attribute__ ((packed)); struct acx_tx_statistics { - u32 internal_desc_overflow; + __le32 internal_desc_overflow; } __attribute__ ((packed)); struct acx_rx_statistics { - u32 out_of_mem; - u32 hdr_overflow; - u32 hw_stuck; - u32 dropped; - u32 fcs_err; - u32 xfr_hint_trig; - u32 path_reset; - u32 reset_counter; + __le32 out_of_mem; + __le32 hdr_overflow; + __le32 hw_stuck; + __le32 dropped; + __le32 fcs_err; + __le32 xfr_hint_trig; + __le32 path_reset; + __le32 reset_counter; } __attribute__ ((packed)); struct acx_dma_statistics { - u32 rx_requested; - u32 rx_errors; - u32 tx_requested; - u32 tx_errors; + __le32 rx_requested; + __le32 rx_errors; + __le32 tx_requested; + __le32 tx_errors; } __attribute__ ((packed)); struct acx_isr_statistics { /* host command complete */ - u32 cmd_cmplt; + __le32 cmd_cmplt; /* fiqisr() */ - u32 fiqs; + __le32 fiqs; /* (INT_STS_ND & INT_TRIG_RX_HEADER) */ - u32 rx_headers; + __le32 rx_headers; /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */ - u32 rx_completes; + __le32 rx_completes; /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */ - u32 rx_mem_overflow; + __le32 rx_mem_overflow; /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */ - u32 rx_rdys; + __le32 rx_rdys; /* irqisr() */ - u32 irqs; + __le32 irqs; /* (INT_STS_ND & INT_TRIG_TX_PROC) */ - u32 tx_procs; + __le32 tx_procs; /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */ - u32 decrypt_done; + __le32 decrypt_done; /* (INT_STS_ND & INT_TRIG_DMA0) */ - u32 dma0_done; + __le32 dma0_done; /* (INT_STS_ND & INT_TRIG_DMA1) */ - u32 dma1_done; + __le32 dma1_done; /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */ - u32 tx_exch_complete; + __le32 tx_exch_complete; /* (INT_STS_ND & INT_TRIG_COMMAND) */ - u32 commands; + __le32 commands; /* (INT_STS_ND & INT_TRIG_RX_PROC) */ - u32 rx_procs; + __le32 rx_procs; /* (INT_STS_ND & INT_TRIG_PM_802) */ - u32 hw_pm_mode_changes; + __le32 hw_pm_mode_changes; /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */ - u32 host_acknowledges; + __le32 host_acknowledges; /* (INT_STS_ND & INT_TRIG_PM_PCI) */ - u32 pci_pm; + __le32 pci_pm; /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */ - u32 wakeups; + __le32 wakeups; /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */ - u32 low_rssi; + __le32 low_rssi; } __attribute__ ((packed)); struct acx_wep_statistics { /* WEP address keys configured */ - u32 addr_key_count; + __le32 addr_key_count; /* default keys configured */ - u32 default_key_count; + __le32 default_key_count; - u32 reserved; + __le32 reserved; /* number of times that WEP key not found on lookup */ - u32 key_not_found; + __le32 key_not_found; /* number of times that WEP key decryption failed */ - u32 decrypt_fail; + __le32 decrypt_fail; /* WEP packets decrypted */ - u32 packets; + __le32 packets; /* WEP decrypt interrupts */ - u32 interrupt; + __le32 interrupt; } __attribute__ ((packed)); #define ACX_MISSED_BEACONS_SPREAD 10 struct acx_pwr_statistics { /* the amount of enters into power save mode (both PD & ELP) */ - u32 ps_enter; + __le32 ps_enter; /* the amount of enters into ELP mode */ - u32 elp_enter; + __le32 elp_enter; /* the amount of missing beacon interrupts to the host */ - u32 missing_bcns; + __le32 missing_bcns; /* the amount of wake on host-access times */ - u32 wake_on_host; + __le32 wake_on_host; /* the amount of wake on timer-expire */ - u32 wake_on_timer_exp; + __le32 wake_on_timer_exp; /* the number of packets that were transmitted with PS bit set */ - u32 tx_with_ps; + __le32 tx_with_ps; /* the number of packets that were transmitted with PS bit clear */ - u32 tx_without_ps; + __le32 tx_without_ps; /* the number of received beacons */ - u32 rcvd_beacons; + __le32 rcvd_beacons; /* the number of entering into PowerOn (power save off) */ - u32 power_save_off; + __le32 power_save_off; /* the number of entries into power save mode */ - u16 enable_ps; + __le16 enable_ps; /* * the number of exits from power save, not including failed PS * transitions */ - u16 disable_ps; + __le16 disable_ps; /* * the number of times the TSF counter was adjusted because * of drift */ - u32 fix_tsf_ps; + __le32 fix_tsf_ps; /* Gives statistics about the spread continuous missed beacons. * The 16 LSB are dedicated for the PS mode. @@ -881,53 +764,53 @@ struct acx_pwr_statistics { * ... * cont_miss_bcns_spread[9] - ten and more continuous missed beacons. */ - u32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; + __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; /* the number of beacons in awake mode */ - u32 rcvd_awake_beacons; + __le32 rcvd_awake_beacons; } __attribute__ ((packed)); struct acx_mic_statistics { - u32 rx_pkts; - u32 calc_failure; + __le32 rx_pkts; + __le32 calc_failure; } __attribute__ ((packed)); struct acx_aes_statistics { - u32 encrypt_fail; - u32 decrypt_fail; - u32 encrypt_packets; - u32 decrypt_packets; - u32 encrypt_interrupt; - u32 decrypt_interrupt; + __le32 encrypt_fail; + __le32 decrypt_fail; + __le32 encrypt_packets; + __le32 decrypt_packets; + __le32 encrypt_interrupt; + __le32 decrypt_interrupt; } __attribute__ ((packed)); struct acx_event_statistics { - u32 heart_beat; - u32 calibration; - u32 rx_mismatch; - u32 rx_mem_empty; - u32 rx_pool; - u32 oom_late; - u32 phy_transmit_error; - u32 tx_stuck; + __le32 heart_beat; + __le32 calibration; + __le32 rx_mismatch; + __le32 rx_mem_empty; + __le32 rx_pool; + __le32 oom_late; + __le32 phy_transmit_error; + __le32 tx_stuck; } __attribute__ ((packed)); struct acx_ps_statistics { - u32 pspoll_timeouts; - u32 upsd_timeouts; - u32 upsd_max_sptime; - u32 upsd_max_apturn; - u32 pspoll_max_apturn; - u32 pspoll_utilization; - u32 upsd_utilization; + __le32 pspoll_timeouts; + __le32 upsd_timeouts; + __le32 upsd_max_sptime; + __le32 upsd_max_apturn; + __le32 pspoll_max_apturn; + __le32 pspoll_utilization; + __le32 upsd_utilization; } __attribute__ ((packed)); struct acx_rxpipe_statistics { - u32 rx_prep_beacon_drop; - u32 descr_host_int_trig_rx_data; - u32 beacon_buffer_thres_host_int_trig_rx_data; - u32 missed_beacon_host_int_trig_rx_data; - u32 tx_xfr_host_int_trig_rx_data; + __le32 rx_prep_beacon_drop; + __le32 descr_host_int_trig_rx_data; + __le32 beacon_buffer_thres_host_int_trig_rx_data; + __le32 missed_beacon_host_int_trig_rx_data; + __le32 tx_xfr_host_int_trig_rx_data; } __attribute__ ((packed)); struct acx_statistics { @@ -946,13 +829,8 @@ struct acx_statistics { struct acx_rxpipe_statistics rxpipe; } __attribute__ ((packed)); -#define ACX_MAX_RATE_CLASSES 8 -#define ACX_RATE_MASK_UNSPECIFIED 0 -#define ACX_RATE_MASK_ALL 0x1eff -#define ACX_RATE_RETRY_LIMIT 10 - struct acx_rate_class { - u32 enabled_rates; + __le32 enabled_rates; u8 short_retry_limit; u8 long_retry_limit; u8 aflags; @@ -962,47 +840,20 @@ struct acx_rate_class { struct acx_rate_policy { struct acx_header header; - u32 rate_class_cnt; - struct acx_rate_class rate_class[ACX_MAX_RATE_CLASSES]; + __le32 rate_class_cnt; + struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES]; } __attribute__ ((packed)); -#define WL1271_ACX_AC_COUNT 4 - struct acx_ac_cfg { struct acx_header header; u8 ac; u8 cw_min; - u16 cw_max; + __le16 cw_max; u8 aifsn; u8 reserved; - u16 tx_op_limit; + __le16 tx_op_limit; } __attribute__ ((packed)); -enum wl1271_acx_ac { - WL1271_ACX_AC_BE = 0, - WL1271_ACX_AC_BK = 1, - WL1271_ACX_AC_VI = 2, - WL1271_ACX_AC_VO = 3, - WL1271_ACX_AC_CTS2SELF = 4, - WL1271_ACX_AC_ANY_TID = 0x1F, - WL1271_ACX_AC_INVALID = 0xFF, -}; - -enum wl1271_acx_ps_scheme { - WL1271_ACX_PS_SCHEME_LEGACY = 0, - WL1271_ACX_PS_SCHEME_UPSD_TRIGGER = 1, - WL1271_ACX_PS_SCHEME_LEGACY_PSPOLL = 2, - WL1271_ACX_PS_SCHEME_SAPSD = 3, -}; - -enum wl1271_acx_ack_policy { - WL1271_ACX_ACK_POLICY_LEGACY = 0, - WL1271_ACX_ACK_POLICY_NO_ACK = 1, - WL1271_ACX_ACK_POLICY_BLOCK = 2, -}; - -#define WL1271_ACX_TID_COUNT 7 - struct acx_tid_config { struct acx_header header; u8 queue_id; @@ -1011,22 +862,19 @@ struct acx_tid_config { u8 ps_scheme; u8 ack_policy; u8 padding[3]; - u32 apsd_conf[2]; + __le32 apsd_conf[2]; } __attribute__ ((packed)); struct acx_frag_threshold { struct acx_header header; - u16 frag_threshold; + __le16 frag_threshold; u8 padding[2]; } __attribute__ ((packed)); -#define WL1271_ACX_TX_COMPL_TIMEOUT 5 -#define WL1271_ACX_TX_COMPL_THRESHOLD 5 - struct acx_tx_config_options { struct acx_header header; - u16 tx_compl_timeout; /* msec */ - u16 tx_compl_threshold; /* number of packets */ + __le16 tx_compl_timeout; /* msec */ + __le16 tx_compl_threshold; /* number of packets */ } __attribute__ ((packed)); #define ACX_RX_MEM_BLOCKS 64 @@ -1041,79 +889,87 @@ struct wl1271_acx_config_memory { u8 tx_min_mem_block_num; u8 num_stations; u8 num_ssid_profiles; - u32 total_tx_descriptors; + __le32 total_tx_descriptors; } __attribute__ ((packed)); struct wl1271_acx_mem_map { struct acx_header header; - void *code_start; - void *code_end; + __le32 code_start; + __le32 code_end; - void *wep_defkey_start; - void *wep_defkey_end; + __le32 wep_defkey_start; + __le32 wep_defkey_end; - void *sta_table_start; - void *sta_table_end; + __le32 sta_table_start; + __le32 sta_table_end; - void *packet_template_start; - void *packet_template_end; + __le32 packet_template_start; + __le32 packet_template_end; /* Address of the TX result interface (control block) */ - u32 tx_result; - u32 tx_result_queue_start; + __le32 tx_result; + __le32 tx_result_queue_start; - void *queue_memory_start; - void *queue_memory_end; + __le32 queue_memory_start; + __le32 queue_memory_end; - u32 packet_memory_pool_start; - u32 packet_memory_pool_end; + __le32 packet_memory_pool_start; + __le32 packet_memory_pool_end; - void *debug_buffer1_start; - void *debug_buffer1_end; + __le32 debug_buffer1_start; + __le32 debug_buffer1_end; - void *debug_buffer2_start; - void *debug_buffer2_end; + __le32 debug_buffer2_start; + __le32 debug_buffer2_end; /* Number of blocks FW allocated for TX packets */ - u32 num_tx_mem_blocks; + __le32 num_tx_mem_blocks; /* Number of blocks FW allocated for RX packets */ - u32 num_rx_mem_blocks; + __le32 num_rx_mem_blocks; /* the following 4 fields are valid in SLAVE mode only */ u8 *tx_cbuf; u8 *rx_cbuf; - void *rx_ctrl; - void *tx_ctrl; + __le32 rx_ctrl; + __le32 tx_ctrl; } __attribute__ ((packed)); -enum wl1271_acx_rx_queue_type { - RX_QUEUE_TYPE_RX_LOW_PRIORITY, /* All except the high priority */ - RX_QUEUE_TYPE_RX_HIGH_PRIORITY, /* Management and voice packets */ - RX_QUEUE_TYPE_NUM, - RX_QUEUE_TYPE_MAX = USHORT_MAX -}; - -#define WL1271_RX_INTR_THRESHOLD_DEF 0 /* no pacing, send interrupt on - * every event */ -#define WL1271_RX_INTR_THRESHOLD_MIN 0 -#define WL1271_RX_INTR_THRESHOLD_MAX 15 - -#define WL1271_RX_INTR_TIMEOUT_DEF 5 -#define WL1271_RX_INTR_TIMEOUT_MIN 1 -#define WL1271_RX_INTR_TIMEOUT_MAX 100 - struct wl1271_acx_rx_config_opt { struct acx_header header; - u16 mblk_threshold; - u16 threshold; - u16 timeout; + __le16 mblk_threshold; + __le16 threshold; + __le16 timeout; u8 queue_type; u8 reserved; } __attribute__ ((packed)); + +struct wl1271_acx_bet_enable { + struct acx_header header; + + u8 enable; + u8 max_consecutive; + u8 padding[2]; +} __attribute__ ((packed)); + +#define ACX_IPV4_VERSION 4 +#define ACX_IPV6_VERSION 6 +#define ACX_IPV4_ADDR_SIZE 4 +struct wl1271_acx_arp_filter { + struct acx_header header; + u8 version; /* ACX_IPV4_VERSION, ACX_IPV6_VERSION */ + u8 enable; /* 1 to enable ARP filtering, 0 to disable */ + u8 padding[2]; + u8 address[16]; /* The configured device IP address - all ARP + requests directed to this IP address will pass + through. For IPv4, the first four bytes are + used. */ +} __attribute__((packed)); + + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1170,6 +1026,9 @@ enum { ACX_PEER_HT_CAP = 0x0057, ACX_HT_BSS_OPERATION = 0x0058, ACX_COEX_ACTIVITY = 0x0059, + ACX_SET_SMART_REFLEX_DEBUG = 0x005A, + ACX_SET_SMART_REFLEX_STATE = 0x005B, + ACX_SET_SMART_REFLEX_PARAMS = 0x005F, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, DOT11_RX_DOT11_MODE = 0x1012, @@ -1182,23 +1041,24 @@ enum { }; -int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event, - u8 listen_interval); +int wl1271_acx_wake_up_conditions(struct wl1271 *wl); int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth); int wl1271_acx_fw_version(struct wl1271 *wl, char *buf, size_t len); int wl1271_acx_tx_power(struct wl1271 *wl, int power); int wl1271_acx_feature_cfg(struct wl1271 *wl); int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, size_t len); -int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time); +int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl); int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter); int wl1271_acx_pd_threshold(struct wl1271 *wl); int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time); -int wl1271_acx_group_address_tbl(struct wl1271 *wl); +int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, + void *mc_list, u32 mc_list_len); int wl1271_acx_service_period_timeout(struct wl1271 *wl); int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); -int wl1271_acx_beacon_filter_opt(struct wl1271 *wl); +int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); +int wl1271_acx_conn_monit_params(struct wl1271 *wl); int wl1271_acx_sg_enable(struct wl1271 *wl); int wl1271_acx_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); @@ -1207,9 +1067,9 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid); int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask); int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble); int wl1271_acx_cts_protect(struct wl1271 *wl, - enum acx_ctsprotect_type ctsprotect); + enum acx_ctsprotect_type ctsprotect); int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats); -int wl1271_acx_rate_policies(struct wl1271 *wl); +int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates); int wl1271_acx_ac_cfg(struct wl1271 *wl); int wl1271_acx_tid_cfg(struct wl1271 *wl); int wl1271_acx_frag_threshold(struct wl1271 *wl); @@ -1217,5 +1077,9 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl1271_acx_mem_cfg(struct wl1271 *wl); int wl1271_acx_init_mem_config(struct wl1271 *wl); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); +int wl1271_acx_smart_reflex(struct wl1271 *wl); +int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); +int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, + u8 version); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index 8228ef474a7e..b7c96454cca3 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -39,6 +39,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { .start = REGISTERS_BASE, .size = 0x00008800 }, + .mem2 = { + .start = 0x00000000, + .size = 0x00000000 + }, + .mem3 = { + .start = 0x00000000, + .size = 0x00000000 + }, }, [PART_WORK] = { @@ -48,7 +56,15 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { }, .reg = { .start = REGISTERS_BASE, - .size = 0x0000b000 + .size = 0x0000a000 + }, + .mem2 = { + .start = 0x003004f8, + .size = 0x00000004 + }, + .mem3 = { + .start = 0x00040404, + .size = 0x00000000 }, }, @@ -60,6 +76,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { .reg = { .start = DRPW_BASE, .size = 0x00006000 + }, + .mem2 = { + .start = 0x00000000, + .size = 0x00000000 + }, + .mem3 = { + .start = 0x00000000, + .size = 0x00000000 } } }; @@ -69,19 +93,19 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) u32 cpu_ctrl; /* 10.5.0 run the firmware (I) */ - cpu_ctrl = wl1271_reg_read32(wl, ACX_REG_ECPU_CONTROL); + cpu_ctrl = wl1271_spi_read32(wl, ACX_REG_ECPU_CONTROL); /* 10.5.1 run the firmware (II) */ cpu_ctrl |= flag; - wl1271_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); + wl1271_spi_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); } static void wl1271_boot_fw_version(struct wl1271 *wl) { struct wl1271_static_data static_data; - wl1271_spi_mem_read(wl, wl->cmd_box_addr, - &static_data, sizeof(static_data)); + wl1271_spi_read(wl, wl->cmd_box_addr, + &static_data, sizeof(static_data), false); strncpy(wl->chip.fw_ver, static_data.fw_version, sizeof(wl->chip.fw_ver)); @@ -93,8 +117,9 @@ static void wl1271_boot_fw_version(struct wl1271 *wl) static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, size_t fw_data_len, u32 dest) { + struct wl1271_partition_set partition; int addr, chunk_num, partition_limit; - u8 *p; + u8 *p, *chunk; /* whal_FwCtrl_LoadFwImageSm() */ @@ -103,16 +128,20 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d", fw_data_len, CHUNK_SIZE); - if ((fw_data_len % 4) != 0) { wl1271_error("firmware length not multiple of four"); return -EIO; } - wl1271_set_partition(wl, dest, - part_table[PART_DOWN].mem.size, - part_table[PART_DOWN].reg.start, - part_table[PART_DOWN].reg.size); + chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL); + if (!chunk) { + wl1271_error("allocation for firmware upload chunk failed"); + return -ENOMEM; + } + + memcpy(&partition, &part_table[PART_DOWN], sizeof(partition)); + partition.mem.start = dest; + wl1271_set_partition(wl, &partition); /* 10.1 set partition limit and chunk num */ chunk_num = 0; @@ -125,21 +154,17 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, addr = dest + chunk_num * CHUNK_SIZE; partition_limit = chunk_num * CHUNK_SIZE + part_table[PART_DOWN].mem.size; - - /* FIXME: Over 80 chars! */ - wl1271_set_partition(wl, - addr, - part_table[PART_DOWN].mem.size, - part_table[PART_DOWN].reg.start, - part_table[PART_DOWN].reg.size); + partition.mem.start = addr; + wl1271_set_partition(wl, &partition); } /* 10.3 upload the chunk */ addr = dest + chunk_num * CHUNK_SIZE; p = buf + chunk_num * CHUNK_SIZE; + memcpy(chunk, p, CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); - wl1271_spi_mem_write(wl, addr, p, CHUNK_SIZE); + wl1271_spi_write(wl, addr, chunk, CHUNK_SIZE, false); chunk_num++; } @@ -147,28 +172,31 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, /* 10.4 upload the last chunk */ addr = dest + chunk_num * CHUNK_SIZE; p = buf + chunk_num * CHUNK_SIZE; + memcpy(chunk, p, fw_data_len % CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x", fw_data_len % CHUNK_SIZE, p, addr); - wl1271_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); + wl1271_spi_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); + kfree(chunk); return 0; } static int wl1271_boot_upload_firmware(struct wl1271 *wl) { u32 chunks, addr, len; + int ret = 0; u8 *fw; fw = wl->fw; - chunks = be32_to_cpup((u32 *) fw); + chunks = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); wl1271_debug(DEBUG_BOOT, "firmware chunks to be uploaded: %u", chunks); while (chunks--) { - addr = be32_to_cpup((u32 *) fw); + addr = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); - len = be32_to_cpup((u32 *) fw); + len = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); if (len > 300000) { @@ -177,11 +205,13 @@ static int wl1271_boot_upload_firmware(struct wl1271 *wl) } wl1271_debug(DEBUG_BOOT, "chunk %d addr 0x%x len %u", chunks, addr, len); - wl1271_boot_upload_firmware_chunk(wl, fw, len, addr); + ret = wl1271_boot_upload_firmware_chunk(wl, fw, len, addr); + if (ret != 0) + break; fw += len; } - return 0; + return ret; } static int wl1271_boot_upload_nvs(struct wl1271 *wl) @@ -235,7 +265,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "nvs burst write 0x%x: 0x%x", dest_addr, val); - wl1271_reg_write32(wl, dest_addr, val); + wl1271_spi_write32(wl, dest_addr, val); nvs_ptr += 4; dest_addr += 4; @@ -253,20 +283,18 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) /* FIXME: The driver sets the partition here, but this is not needed, since it sets to the same one as currently in use */ /* Now we must set the partition correctly */ - wl1271_set_partition(wl, - part_table[PART_WORK].mem.start, - part_table[PART_WORK].mem.size, - part_table[PART_WORK].reg.start, - part_table[PART_WORK].reg.size); + wl1271_set_partition(wl, &part_table[PART_WORK]); /* Copy the NVS tables to a new block to ensure alignment */ nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); + if (!nvs_aligned) + return -ENOMEM; /* And finally we upload the NVS tables */ /* FIXME: In wl1271, we upload everything at once. No endianness handling needed here?! The ref driver doesn't do anything about it at this point */ - wl1271_spi_mem_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len); + wl1271_spi_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false); kfree(nvs_aligned); return 0; @@ -275,9 +303,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) static void wl1271_boot_enable_interrupts(struct wl1271 *wl) { enable_irq(wl->irq); - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); - wl1271_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL); + wl1271_spi_write32(wl, HI_CFG, HI_CFG_DEF_VAL); } static int wl1271_boot_soft_reset(struct wl1271 *wl) @@ -286,12 +314,13 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) u32 boot_data; /* perform soft reset */ - wl1271_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); + wl1271_spi_write32(wl, ACX_REG_SLV_SOFT_RESET, + ACX_SLV_SOFT_RESET_BIT); /* SOFT_RESET is self clearing */ timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); while (1) { - boot_data = wl1271_reg_read32(wl, ACX_REG_SLV_SOFT_RESET); + boot_data = wl1271_spi_read32(wl, ACX_REG_SLV_SOFT_RESET); wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) break; @@ -307,10 +336,10 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) } /* disable Rx/Tx */ - wl1271_reg_write32(wl, ENABLE, 0x0); + wl1271_spi_write32(wl, ENABLE, 0x0); /* disable auto calibration on start*/ - wl1271_reg_write32(wl, SPARE_A2, 0xffff); + wl1271_spi_write32(wl, SPARE_A2, 0xffff); return 0; } @@ -322,7 +351,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); - chip_id = wl1271_reg_read32(wl, CHIP_ID_B); + chip_id = wl1271_spi_read32(wl, CHIP_ID_B); wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); @@ -335,7 +364,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) loop = 0; while (loop++ < INIT_LOOP) { udelay(INIT_LOOP_DELAY); - interrupt = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + interrupt = wl1271_spi_read32(wl, + ACX_REG_INTERRUPT_NO_CLEAR); if (interrupt == 0xffffffff) { wl1271_error("error reading hardware complete " @@ -344,30 +374,26 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) } /* check that ACX_INTR_INIT_COMPLETE is enabled */ else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) { - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK, + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK, WL1271_ACX_INTR_INIT_COMPLETE); break; } } - if (loop >= INIT_LOOP) { + if (loop > INIT_LOOP) { wl1271_error("timeout waiting for the hardware to " "complete initialization"); return -EIO; } /* get hardware config command mail box */ - wl->cmd_box_addr = wl1271_reg_read32(wl, REG_COMMAND_MAILBOX_PTR); + wl->cmd_box_addr = wl1271_spi_read32(wl, REG_COMMAND_MAILBOX_PTR); /* get hardware config event mail box */ - wl->event_box_addr = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->event_box_addr = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR); /* set the working partition to its "running" mode offset */ - wl1271_set_partition(wl, - part_table[PART_WORK].mem.start, - part_table[PART_WORK].mem.size, - part_table[PART_WORK].reg.start, - part_table[PART_WORK].reg.size); + wl1271_set_partition(wl, &part_table[PART_WORK]); wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", wl->cmd_box_addr, wl->event_box_addr); @@ -379,11 +405,10 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) * ready to receive event from the command mailbox */ - /* enable gpio interrupts */ - wl1271_boot_enable_interrupts(wl); - - /* unmask all mbox events */ - wl->event_mask = 0xffffffff; + /* unmask required mbox events */ + wl->event_mask = BSS_LOSE_EVENT_ID | + SCAN_COMPLETE_EVENT_ID | + PS_REPORT_EVENT_ID; ret = wl1271_event_unmask(wl); if (ret < 0) { @@ -399,34 +424,13 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) static int wl1271_boot_write_irq_polarity(struct wl1271 *wl) { - u32 polarity, status, i; - - wl1271_reg_write32(wl, OCP_POR_CTR, OCP_REG_POLARITY); - wl1271_reg_write32(wl, OCP_CMD, OCP_CMD_READ); - - /* Wait until the command is complete (ie. bit 18 is set) */ - for (i = 0; i < OCP_CMD_LOOP; i++) { - polarity = wl1271_reg_read32(wl, OCP_DATA_READ); - if (polarity & OCP_READY_MASK) - break; - } - if (i == OCP_CMD_LOOP) { - wl1271_error("OCP command timeout!"); - return -EIO; - } + u32 polarity; - status = polarity & OCP_STATUS_MASK; - if (status != OCP_STATUS_OK) { - wl1271_error("OCP command failed (%d)", status); - return -EIO; - } + polarity = wl1271_top_reg_read(wl, OCP_REG_POLARITY); /* We use HIGH polarity, so unset the LOW bit */ polarity &= ~POLARITY_LOW; - - wl1271_reg_write32(wl, OCP_POR_CTR, OCP_REG_POLARITY); - wl1271_reg_write32(wl, OCP_DATA_WRITE, polarity); - wl1271_reg_write32(wl, OCP_CMD, OCP_CMD_WRITE); + wl1271_top_reg_write(wl, OCP_REG_POLARITY, polarity); return 0; } @@ -436,16 +440,32 @@ int wl1271_boot(struct wl1271 *wl) int ret = 0; u32 tmp, clk, pause; - if (REF_CLOCK == 0 || REF_CLOCK == 2) - /* ref clk: 19.2/38.4 */ + if (REF_CLOCK == 0 || REF_CLOCK == 2 || REF_CLOCK == 4) + /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; else if (REF_CLOCK == 1 || REF_CLOCK == 3) /* ref clk: 26/52 */ clk = 0x5; - wl1271_reg_write32(wl, PLL_PARAMETERS, clk); + if (REF_CLOCK != 0) { + u16 val; + /* Set clock type */ + val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); + val &= FREF_CLK_TYPE_BITS; + val |= CLK_REQ_PRCM; + wl1271_top_reg_write(wl, OCP_REG_CLK_TYPE, val); + } else { + u16 val; + /* Set clock polarity */ + val = wl1271_top_reg_read(wl, OCP_REG_CLK_POLARITY); + val &= FREF_CLK_POLARITY_BITS; + val |= CLK_REQ_OUTN_SEL; + wl1271_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); + } + + wl1271_spi_write32(wl, PLL_PARAMETERS, clk); - pause = wl1271_reg_read32(wl, PLL_PARAMETERS); + pause = wl1271_spi_read32(wl, PLL_PARAMETERS); wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); @@ -454,39 +474,31 @@ int wl1271_boot(struct wl1271 *wl) * 0x3ff (magic number ). How does * this work?! */ pause |= WU_COUNTER_PAUSE_VAL; - wl1271_reg_write32(wl, WU_COUNTER_PAUSE, pause); + wl1271_spi_write32(wl, WU_COUNTER_PAUSE, pause); /* Continue the ELP wake up sequence */ - wl1271_reg_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + wl1271_spi_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); - wl1271_set_partition(wl, - part_table[PART_DRPW].mem.start, - part_table[PART_DRPW].mem.size, - part_table[PART_DRPW].reg.start, - part_table[PART_DRPW].reg.size); + wl1271_set_partition(wl, &part_table[PART_DRPW]); /* Read-modify-write DRPW_SCRATCH_START register (see next state) to be used by DRPw FW. The RTRIM value will be added by the FW before taking DRPw out of reset */ wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START); - clk = wl1271_reg_read32(wl, DRPW_SCRATCH_START); + clk = wl1271_spi_read32(wl, DRPW_SCRATCH_START); wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); /* 2 */ clk |= (REF_CLOCK << 1) << 4; - wl1271_reg_write32(wl, DRPW_SCRATCH_START, clk); + wl1271_spi_write32(wl, DRPW_SCRATCH_START, clk); - wl1271_set_partition(wl, - part_table[PART_WORK].mem.start, - part_table[PART_WORK].mem.size, - part_table[PART_WORK].reg.start, - part_table[PART_WORK].reg.size); + wl1271_set_partition(wl, &part_table[PART_WORK]); /* Disable interrupts */ - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); ret = wl1271_boot_soft_reset(wl); if (ret < 0) @@ -501,21 +513,22 @@ int wl1271_boot(struct wl1271 *wl) * ACX_EEPROMLESS_IND_REG */ wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); - wl1271_reg_write32(wl, ACX_EEPROMLESS_IND_REG, ACX_EEPROMLESS_IND_REG); + wl1271_spi_write32(wl, ACX_EEPROMLESS_IND_REG, + ACX_EEPROMLESS_IND_REG); - tmp = wl1271_reg_read32(wl, CHIP_ID_B); + tmp = wl1271_spi_read32(wl, CHIP_ID_B); wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); /* 6. read the EEPROM parameters */ - tmp = wl1271_reg_read32(wl, SCR_PAD2); + tmp = wl1271_spi_read32(wl, SCR_PAD2); ret = wl1271_boot_write_irq_polarity(wl); if (ret < 0) goto out; /* FIXME: Need to check whether this is really what we want */ - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); /* WL1271: The reference driver skips steps 7 to 10 (jumps directly @@ -530,6 +543,9 @@ int wl1271_boot(struct wl1271 *wl) if (ret < 0) goto out; + /* Enable firmware interrupts now */ + wl1271_boot_enable_interrupts(wl); + /* set the wl1271 default filters */ wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl->rx_filter = WL1271_DEFAULT_RX_FILTER; diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.h b/drivers/net/wireless/wl12xx/wl1271_boot.h index b0d8fb46a439..412443ee655a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.h +++ b/drivers/net/wireless/wl12xx/wl1271_boot.h @@ -50,23 +50,17 @@ struct wl1271_static_data { #define WU_COUNTER_PAUSE_VAL 0x3FF #define WELP_ARM_COMMAND_VAL 0x4 -#define OCP_CMD_LOOP 32 - -#define OCP_CMD_WRITE 0x1 -#define OCP_CMD_READ 0x2 - -#define OCP_READY_MASK BIT(18) -#define OCP_STATUS_MASK (BIT(16) | BIT(17)) - -#define OCP_STATUS_NO_RESP 0x00000 -#define OCP_STATUS_OK 0x10000 -#define OCP_STATUS_REQ_FAILED 0x20000 -#define OCP_STATUS_RESP_ERROR 0x30000 - -#define OCP_REG_POLARITY 0x30032 +#define OCP_REG_POLARITY 0x0064 +#define OCP_REG_CLK_TYPE 0x0448 +#define OCP_REG_CLK_POLARITY 0x0cb2 #define CMD_MBOX_ADDRESS 0x407B4 #define POLARITY_LOW BIT(1) +#define FREF_CLK_TYPE_BITS 0xfffffe7f +#define CLK_REQ_PRCM 0x100 +#define FREF_CLK_POLARITY_BITS 0xfffff8ff +#define CLK_REQ_OUTN_SEL 0x700 + #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 2a4351ff54dc..886a9bc39cc1 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -42,26 +42,28 @@ * @buf: buffer containing the command, must work with dma * @len: length of the buffer */ -int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len) +int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len) { struct wl1271_cmd_header *cmd; unsigned long timeout; u32 intr; int ret = 0; + u16 status; cmd = buf; - cmd->id = id; + cmd->id = cpu_to_le16(id); cmd->status = 0; WARN_ON(len % 4 != 0); - wl1271_spi_mem_write(wl, wl->cmd_box_addr, buf, len); + wl1271_spi_write(wl, wl->cmd_box_addr, buf, len, false); - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); - intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); @@ -71,17 +73,28 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len) msleep(1); - intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); } - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK, + /* read back the status code of the command */ + if (res_len == 0) + res_len = sizeof(struct wl1271_cmd_header); + wl1271_spi_read(wl, wl->cmd_box_addr, cmd, res_len, false); + + status = le16_to_cpu(cmd->status); + if (status != CMD_STATUS_SUCCESS) { + wl1271_error("command execute failure %d", status); + ret = -EIO; + } + + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); out: return ret; } -int wl1271_cmd_cal_channel_tune(struct wl1271 *wl) +static int wl1271_cmd_cal_channel_tune(struct wl1271 *wl) { struct wl1271_cmd_cal_channel_tune *cmd; int ret = 0; @@ -104,7 +117,7 @@ int wl1271_cmd_cal_channel_tune(struct wl1271 *wl) return ret; } -int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl) +static int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl) { struct wl1271_cmd_cal_update_ref_point *cmd; int ret = 0; @@ -129,7 +142,7 @@ int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl) return ret; } -int wl1271_cmd_cal_p2g(struct wl1271 *wl) +static int wl1271_cmd_cal_p2g(struct wl1271 *wl) { struct wl1271_cmd_cal_p2g *cmd; int ret = 0; @@ -150,7 +163,7 @@ int wl1271_cmd_cal_p2g(struct wl1271 *wl) return ret; } -int wl1271_cmd_cal(struct wl1271 *wl) +static int wl1271_cmd_cal(struct wl1271 *wl) { /* * FIXME: we must make sure that we're not sleeping when calibration @@ -175,11 +188,116 @@ int wl1271_cmd_cal(struct wl1271 *wl) return ret; } -int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, - u16 beacon_interval, u8 wait) +int wl1271_cmd_general_parms(struct wl1271 *wl) +{ + struct wl1271_general_parms_cmd *gen_parms; + struct conf_general_parms *g = &wl->conf.init.genparam; + int ret; + + gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); + if (!gen_parms) + return -ENOMEM; + + gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM; + + gen_parms->ref_clk = g->ref_clk; + gen_parms->settling_time = g->settling_time; + gen_parms->clk_valid_on_wakeup = g->clk_valid_on_wakeup; + gen_parms->dc2dcmode = g->dc2dcmode; + gen_parms->single_dual_band = g->single_dual_band; + gen_parms->tx_bip_fem_autodetect = g->tx_bip_fem_autodetect; + gen_parms->tx_bip_fem_manufacturer = g->tx_bip_fem_manufacturer; + gen_parms->settings = g->settings; + + ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0); + if (ret < 0) + wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); + + kfree(gen_parms); + return ret; +} + +int wl1271_cmd_radio_parms(struct wl1271 *wl) +{ + struct wl1271_radio_parms_cmd *radio_parms; + struct conf_radio_parms *r = &wl->conf.init.radioparam; + int i, ret; + + radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); + if (!radio_parms) + return -ENOMEM; + + radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; + + /* Static radio parameters */ + radio_parms->rx_trace_loss = r->rx_trace_loss; + radio_parms->tx_trace_loss = r->tx_trace_loss; + memcpy(radio_parms->rx_rssi_and_proc_compens, + r->rx_rssi_and_proc_compens, + CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE); + + memcpy(radio_parms->rx_trace_loss_5, r->rx_trace_loss_5, + CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->tx_trace_loss_5, r->tx_trace_loss_5, + CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->rx_rssi_and_proc_compens_5, + r->rx_rssi_and_proc_compens_5, + CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE); + + /* Dynamic radio parameters */ + radio_parms->tx_ref_pd_voltage = cpu_to_le16(r->tx_ref_pd_voltage); + radio_parms->tx_ref_power = r->tx_ref_power; + radio_parms->tx_offset_db = r->tx_offset_db; + + memcpy(radio_parms->tx_rate_limits_normal, r->tx_rate_limits_normal, + CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_rate_limits_degraded, r->tx_rate_limits_degraded, + CONF_NUMBER_OF_RATE_GROUPS); + + memcpy(radio_parms->tx_channel_limits_11b, r->tx_channel_limits_11b, + CONF_NUMBER_OF_CHANNELS_2_4); + memcpy(radio_parms->tx_channel_limits_ofdm, r->tx_channel_limits_ofdm, + CONF_NUMBER_OF_CHANNELS_2_4); + memcpy(radio_parms->tx_pdv_rate_offsets, r->tx_pdv_rate_offsets, + CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_ibias, r->tx_ibias, CONF_NUMBER_OF_RATE_GROUPS); + + radio_parms->rx_fem_insertion_loss = r->rx_fem_insertion_loss; + + for (i = 0; i < CONF_NUMBER_OF_SUB_BANDS_5; i++) + radio_parms->tx_ref_pd_voltage_5[i] = + cpu_to_le16(r->tx_ref_pd_voltage_5[i]); + memcpy(radio_parms->tx_ref_power_5, r->tx_ref_power_5, + CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->tx_offset_db_5, r->tx_offset_db_5, + CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->tx_rate_limits_normal_5, + r->tx_rate_limits_normal_5, CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_rate_limits_degraded_5, + r->tx_rate_limits_degraded_5, CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_channel_limits_ofdm_5, + r->tx_channel_limits_ofdm_5, CONF_NUMBER_OF_CHANNELS_5); + memcpy(radio_parms->tx_pdv_rate_offsets_5, r->tx_pdv_rate_offsets_5, + CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->tx_ibias_5, r->tx_ibias_5, + CONF_NUMBER_OF_RATE_GROUPS); + memcpy(radio_parms->rx_fem_insertion_loss_5, + r->rx_fem_insertion_loss_5, CONF_NUMBER_OF_SUB_BANDS_5); + + wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", + radio_parms, sizeof(*radio_parms)); + + ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0); + if (ret < 0) + wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed"); + + kfree(radio_parms); + return ret; +} + +int wl1271_cmd_join(struct wl1271 *wl) { static bool do_cal = true; - unsigned long timeout; struct wl1271_cmd_join *join; int ret, i; u8 *bssid; @@ -193,6 +311,18 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, do_cal = false; } + /* FIXME: This is a workaround, because with the current stack, we + * cannot know when we have disassociated. So, if we have already + * joined, we disconnect before joining again. */ + if (wl->joined) { + ret = wl1271_cmd_disconnect(wl); + if (ret < 0) { + wl1271_error("failed to disconnect before rejoining"); + goto out; + } + + wl->joined = false; + } join = kzalloc(sizeof(*join), GFP_KERNEL); if (!join) { @@ -207,15 +337,34 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, for (i = 0; i < ETH_ALEN; i++) bssid[i] = wl->bssid[ETH_ALEN - i - 1]; - join->rx_config_options = wl->rx_config; - join->rx_filter_options = wl->rx_filter; + join->rx_config_options = cpu_to_le32(wl->rx_config); + join->rx_filter_options = cpu_to_le32(wl->rx_filter); + join->bss_type = wl->bss_type; - join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | - RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; + /* + * FIXME: disable temporarily all filters because after commit + * 9cef8737 "mac80211: fix managed mode BSSID handling" broke + * association. The filter logic needs to be implemented properly + * and once that is done, this hack can be removed. + */ + join->rx_config_options = cpu_to_le32(0); + join->rx_filter_options = cpu_to_le32(WL1271_DEFAULT_RX_FILTER); + + if (wl->band == IEEE80211_BAND_2GHZ) + join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS | + CONF_HW_BIT_RATE_2MBPS | + CONF_HW_BIT_RATE_5_5MBPS | + CONF_HW_BIT_RATE_11MBPS); + else { + join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ; + join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_6MBPS | + CONF_HW_BIT_RATE_12MBPS | + CONF_HW_BIT_RATE_24MBPS); + } + + join->beacon_interval = cpu_to_le16(WL1271_DEFAULT_BEACON_INT); + join->dtim_interval = WL1271_DEFAULT_DTIM_PERIOD; - join->beacon_interval = beacon_interval; - join->dtim_interval = dtim_interval; - join->bss_type = bss_type; join->channel = wl->channel; join->ssid_len = wl->ssid_len; memcpy(join->ssid, wl->ssid, wl->ssid_len); @@ -228,21 +377,24 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, join->ctrl |= wl->session_counter << WL1271_JOIN_CMD_TX_SESSION_OFFSET; + /* reset TX security counters */ + wl->tx_security_last_seq = 0; + wl->tx_security_seq_16 = 0; + wl->tx_security_seq_32 = 0; - ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join)); + ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join), 0); if (ret < 0) { wl1271_error("failed to initiate cmd join"); goto out_free; } - timeout = msecs_to_jiffies(JOIN_TIMEOUT); + wl->joined = true; /* * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to * simplify locking we just sleep instead, for now */ - if (wait) - msleep(10); + msleep(10); out_free: kfree(join); @@ -262,34 +414,21 @@ out: int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer) { int ret; + size_t res_len = 0; wl1271_debug(DEBUG_CMD, "cmd test"); - ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len); + if (answer) + res_len = buf_len; + + ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len, res_len); if (ret < 0) { wl1271_warning("TEST command failed"); return ret; } - if (answer) { - struct wl1271_command *cmd_answer; - - /* - * The test command got in, we can read the answer. - * The answer would be a wl1271_command, where the - * parameter array contains the actual answer. - */ - wl1271_spi_mem_read(wl, wl->cmd_box_addr, buf, buf_len); - - cmd_answer = buf; - - if (cmd_answer->header.status != CMD_STATUS_SUCCESS) - wl1271_error("TEST command answer error: %d", - cmd_answer->header.status); - } - - return 0; + return ret; } /** @@ -307,26 +446,15 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) wl1271_debug(DEBUG_CMD, "cmd interrogate"); - acx->id = id; + acx->id = cpu_to_le16(id); /* payload length, does not include any headers */ - acx->len = len - sizeof(*acx); + acx->len = cpu_to_le16(len - sizeof(*acx)); - ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx)); - if (ret < 0) { + ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len); + if (ret < 0) wl1271_error("INTERROGATE command failed"); - goto out; - } - /* the interrogate command got in, we can read the answer */ - wl1271_spi_mem_read(wl, wl->cmd_box_addr, buf, len); - - acx = buf; - if (acx->cmd.status != CMD_STATUS_SUCCESS) - wl1271_error("INTERROGATE command error: %d", - acx->cmd.status); - -out: return ret; } @@ -345,12 +473,12 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) wl1271_debug(DEBUG_CMD, "cmd configure"); - acx->id = id; + acx->id = cpu_to_le16(id); /* payload length, does not include any headers */ - acx->len = len - sizeof(*acx); + acx->len = cpu_to_le16(len - sizeof(*acx)); - ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len); + ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0); if (ret < 0) { wl1271_warning("CONFIGURE command NOK"); return ret; @@ -383,7 +511,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) cmd_tx = CMD_DISABLE_TX; } - ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("rx %s cmd for channel %d failed", enable ? "start" : "stop", channel); @@ -393,7 +521,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d", enable ? "start" : "stop", channel); - ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("tx %s cmd for channel %d failed", enable ? "start" : "stop", channel); @@ -414,8 +542,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) int ret = 0; /* FIXME: this should be in ps.c */ - ret = wl1271_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, - wl->listen_int); + ret = wl1271_acx_wake_up_conditions(wl); if (ret < 0) { wl1271_error("couldn't set wake up conditions"); goto out; @@ -433,10 +560,10 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) ps_params->send_null_data = 1; ps_params->retries = 5; ps_params->hang_over_period = 128; - ps_params->null_data_rate = 1; /* 1 Mbps */ + ps_params->null_data_rate = cpu_to_le32(1); /* 1 Mbps */ ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params, - sizeof(*ps_params)); + sizeof(*ps_params), 0); if (ret < 0) { wl1271_error("cmd set_ps_mode failed"); goto out; @@ -464,22 +591,17 @@ int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, WARN_ON(len > MAX_READ_SIZE); len = min_t(size_t, len, MAX_READ_SIZE); - cmd->addr = addr; - cmd->size = len; + cmd->addr = cpu_to_le32(addr); + cmd->size = cpu_to_le32(len); - ret = wl1271_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd), + sizeof(*cmd)); if (ret < 0) { wl1271_error("read memory command failed: %d", ret); goto out; } - /* the read command got in, we can now read the answer */ - wl1271_spi_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); - - if (cmd->header.status != CMD_STATUS_SUCCESS) - wl1271_error("error in read command result: %d", - cmd->header.status); - + /* the read command got in */ memcpy(answer, cmd->value, len); out: @@ -488,14 +610,31 @@ out: } int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 num_channels, + u8 active_scan, u8 high_prio, u8 band, u8 probe_requests) { struct wl1271_cmd_trigger_scan_to *trigger = NULL; struct wl1271_cmd_scan *params = NULL; - int i, ret; + struct ieee80211_channel *channels; + int i, j, n_ch, ret; u16 scan_options = 0; + u8 ieee_band; + + if (band == WL1271_SCAN_BAND_2_4_GHZ) + ieee_band = IEEE80211_BAND_2GHZ; + else if (band == WL1271_SCAN_BAND_DUAL && wl1271_11a_enabled()) + ieee_band = IEEE80211_BAND_2GHZ; + else if (band == WL1271_SCAN_BAND_5_GHZ && wl1271_11a_enabled()) + ieee_band = IEEE80211_BAND_5GHZ; + else + return -EINVAL; + + if (wl->hw->wiphy->bands[ieee_band]->channels == NULL) + return -EINVAL; + + channels = wl->hw->wiphy->bands[ieee_band]->channels; + n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels; if (wl->scanning) return -EINVAL; @@ -512,32 +651,43 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, scan_options |= WL1271_SCAN_OPT_PASSIVE; if (high_prio) scan_options |= WL1271_SCAN_OPT_PRIORITY_HIGH; - params->params.scan_options = scan_options; + params->params.scan_options = cpu_to_le16(scan_options); - params->params.num_channels = num_channels; params->params.num_probe_requests = probe_requests; - params->params.tx_rate = cpu_to_le32(RATE_MASK_2MBPS); + /* Let the fw autodetect suitable tx_rate for probes */ + params->params.tx_rate = 0; params->params.tid_trigger = 0; params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; - for (i = 0; i < num_channels; i++) { - params->channels[i].min_duration = - cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION); - params->channels[i].max_duration = - cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION); - memset(¶ms->channels[i].bssid_lsb, 0xff, 4); - memset(¶ms->channels[i].bssid_msb, 0xff, 2); - params->channels[i].early_termination = 0; - params->channels[i].tx_power_att = WL1271_SCAN_CURRENT_TX_PWR; - params->channels[i].channel = i + 1; + if (band == WL1271_SCAN_BAND_DUAL) + params->params.band = WL1271_SCAN_BAND_2_4_GHZ; + else + params->params.band = band; + + for (i = 0, j = 0; i < n_ch && i < WL1271_SCAN_MAX_CHANNELS; i++) { + if (!(channels[i].flags & IEEE80211_CHAN_DISABLED)) { + params->channels[j].min_duration = + cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION); + params->channels[j].max_duration = + cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION); + memset(¶ms->channels[j].bssid_lsb, 0xff, 4); + memset(¶ms->channels[j].bssid_msb, 0xff, 2); + params->channels[j].early_termination = 0; + params->channels[j].tx_power_att = + WL1271_SCAN_CURRENT_TX_PWR; + params->channels[j].channel = channels[i].hw_value; + j++; + } } + params->params.num_channels = j; + if (len && ssid) { params->params.ssid_len = len; memcpy(params->params.ssid, ssid, len); } - ret = wl1271_cmd_build_probe_req(wl, ssid, len); + ret = wl1271_cmd_build_probe_req(wl, ssid, len, ieee_band); if (ret < 0) { wl1271_error("PROBE request template failed"); goto out; @@ -553,7 +703,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, trigger->timeout = 0; ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, - sizeof(*trigger)); + sizeof(*trigger), 0); if (ret < 0) { wl1271_error("trigger scan to failed for hw scan"); goto out; @@ -562,20 +712,24 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); wl->scanning = true; + if (wl1271_11a_enabled()) { + wl->scan.state = band; + if (band == WL1271_SCAN_BAND_DUAL) { + wl->scan.active = active_scan; + wl->scan.high_prio = high_prio; + wl->scan.probe_requests = probe_requests; + if (len && ssid) { + wl->scan.ssid_len = len; + memcpy(wl->scan.ssid, ssid, len); + } else + wl->scan.ssid_len = 0; + } + } - ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); + ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params), 0); if (ret < 0) { wl1271_error("SCAN failed"); - goto out; - } - - wl1271_spi_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); - - if (params->header.status != CMD_STATUS_SUCCESS) { - wl1271_error("Scan command error: %d", - params->header.status); wl->scanning = false; - ret = -EIO; goto out; } @@ -603,14 +757,14 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, cmd->len = cpu_to_le16(buf_len); cmd->template_type = template_id; - cmd->enabled_rates = ACX_RATE_MASK_UNSPECIFIED; - cmd->short_retry_limit = ACX_RATE_RETRY_LIMIT; - cmd->long_retry_limit = ACX_RATE_RETRY_LIMIT; + cmd->enabled_rates = cpu_to_le32(wl->conf.tx.rc_conf.enabled_rates); + cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit; + cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit; if (buf) memcpy(cmd->template_data, buf, buf_len); - ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("cmd set_template failed: %d", ret); goto out_free; @@ -623,30 +777,62 @@ out: return ret; } -static int wl1271_build_basic_rates(char *rates) +static int wl1271_build_basic_rates(char *rates, u8 band) { u8 index = 0; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; + if (band == IEEE80211_BAND_2GHZ) { + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; + } else if (band == IEEE80211_BAND_5GHZ) { + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; + } else { + wl1271_error("build_basic_rates invalid band: %d", band); + } return index; } -static int wl1271_build_extended_rates(char *rates) +static int wl1271_build_extended_rates(char *rates, u8 band) { u8 index = 0; - rates[index++] = IEEE80211_OFDM_RATE_6MB; - rates[index++] = IEEE80211_OFDM_RATE_9MB; - rates[index++] = IEEE80211_OFDM_RATE_12MB; - rates[index++] = IEEE80211_OFDM_RATE_18MB; - rates[index++] = IEEE80211_OFDM_RATE_24MB; - rates[index++] = IEEE80211_OFDM_RATE_36MB; - rates[index++] = IEEE80211_OFDM_RATE_48MB; - rates[index++] = IEEE80211_OFDM_RATE_54MB; + if (band == IEEE80211_BAND_2GHZ) { + rates[index++] = IEEE80211_OFDM_RATE_6MB; + rates[index++] = IEEE80211_OFDM_RATE_9MB; + rates[index++] = IEEE80211_OFDM_RATE_12MB; + rates[index++] = IEEE80211_OFDM_RATE_18MB; + rates[index++] = IEEE80211_OFDM_RATE_24MB; + rates[index++] = IEEE80211_OFDM_RATE_36MB; + rates[index++] = IEEE80211_OFDM_RATE_48MB; + rates[index++] = IEEE80211_OFDM_RATE_54MB; + } else if (band == IEEE80211_BAND_5GHZ) { + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB; + rates[index++] = + IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB; + } else { + wl1271_error("build_basic_rates invalid band: %d", band); + } return index; } @@ -665,7 +851,8 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl) memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC); + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template, sizeof(template)); @@ -678,7 +865,10 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) memcpy(template.bssid, wl->bssid, ETH_ALEN); memcpy(template.ta, wl->mac_addr, ETH_ALEN); - template.aid = aid; + + /* aid in PS-Poll has its two MSBs each set to 1 */ + template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); + template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template, @@ -686,12 +876,14 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) } -int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len) +int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, + u8 band) { struct wl12xx_probe_req_template template; struct wl12xx_ie_rates *rates; char *ptr; u16 size; + int ret; ptr = (char *)&template; size = sizeof(struct ieee80211_header); @@ -713,20 +905,25 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len) /* Basic Rates */ rates = (struct wl12xx_ie_rates *)ptr; rates->header.id = WLAN_EID_SUPP_RATES; - rates->header.len = wl1271_build_basic_rates(rates->rates); + rates->header.len = wl1271_build_basic_rates(rates->rates, band); size += sizeof(struct wl12xx_ie_header) + rates->header.len; ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; /* Extended rates */ rates = (struct wl12xx_ie_rates *)ptr; rates->header.id = WLAN_EID_EXT_SUPP_RATES; - rates->header.len = wl1271_build_extended_rates(rates->rates); + rates->header.len = wl1271_build_extended_rates(rates->rates, band); size += sizeof(struct wl12xx_ie_header) + rates->header.len; wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); - return wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, - &template, size); + if (band == IEEE80211_BAND_2GHZ) + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + &template, size); + else + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + &template, size); + return ret; } int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) @@ -743,10 +940,10 @@ int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) } cmd->id = id; - cmd->key_action = KEY_SET_ID; + cmd->key_action = cpu_to_le16(KEY_SET_ID); cmd->key_type = KEY_WEP; - ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("cmd set_default_wep_key failed: %d", ret); goto out; @@ -759,7 +956,8 @@ out: } int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, - u8 key_size, const u8 *key, const u8 *addr) + u8 key_size, const u8 *key, const u8 *addr, + u32 tx_seq_32, u16 tx_seq_16) { struct wl1271_cmd_set_keys *cmd; int ret = 0; @@ -773,16 +971,18 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, if (key_type != KEY_WEP) memcpy(cmd->addr, addr, ETH_ALEN); - cmd->key_action = action; + cmd->key_action = cpu_to_le16(action); cmd->key_size = key_size; cmd->key_type = key_type; + cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16); + cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32); + /* we have only one SSID profile */ cmd->ssid_profile = 0; cmd->id = id; - /* FIXME: this is from wl1251, needs to be checked */ if (key_type == KEY_TKIP) { /* * We get the key in the following form: @@ -800,7 +1000,7 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, wl1271_dump(DEBUG_CRYPT, "TARGET KEY: ", cmd, sizeof(*cmd)); - ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd)); + ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("could not set keys"); goto out; @@ -811,3 +1011,34 @@ out: return ret; } + +int wl1271_cmd_disconnect(struct wl1271 *wl) +{ + struct wl1271_cmd_disconnect *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd disconnect"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->rx_config_options = cpu_to_le32(wl->rx_config); + cmd->rx_filter_options = cpu_to_le32(wl->rx_filter); + /* disconnect reason is not used in immediate disconnections */ + cmd->type = DISCONNECT_IMMEDIATE; + + ret = wl1271_cmd_send(wl, CMD_DISCONNECT, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send disconnect command"); + goto out_free; + } + +out_free: + kfree(cmd); + +out: + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index 951a8447a516..b4fa4acb9229 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -29,9 +29,11 @@ struct acx_header; -int wl1271_cmd_send(struct wl1271 *wl, u16 type, void *buf, size_t buf_len); -int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, - u16 beacon_interval, u8 wait); +int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len); +int wl1271_cmd_general_parms(struct wl1271 *wl); +int wl1271_cmd_radio_parms(struct wl1271 *wl); +int wl1271_cmd_join(struct wl1271 *wl); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); @@ -40,16 +42,19 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 num_channels, + u8 active_scan, u8 high_prio, u8 band, u8 probe_requests); int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, void *buf, size_t buf_len); int wl1271_cmd_build_null_data(struct wl1271 *wl); int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); -int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len); +int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, + u8 band); int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, - u8 key_size, const u8 *key, const u8 *addr); + u8 key_size, const u8 *key, const u8 *addr, + u32 tx_seq_32, u16 tx_seq_16); +int wl1271_cmd_disconnect(struct wl1271 *wl); enum wl1271_commands { CMD_INTERROGATE = 1, /*use this to read information elements*/ @@ -118,8 +123,8 @@ enum cmd_templ { #define WL1271_CMD_TEMPL_MAX_SIZE 252 struct wl1271_cmd_header { - u16 id; - u16 status; + __le16 id; + __le16 status; /* payload */ u8 data[0]; } __attribute__ ((packed)); @@ -172,17 +177,17 @@ struct cmd_read_write_memory { struct wl1271_cmd_header header; /* The address of the memory to read from or write to.*/ - u32 addr; + __le32 addr; /* The amount of data in bytes to read from or write to the WiLink * device.*/ - u32 size; + __le32 size; /* The actual value read from or written to the Wilink. The source of this field is the Host in WRITE command or the Wilink in READ command. */ u8 value[MAX_READ_SIZE]; -}; +} __attribute__ ((packed)); #define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 @@ -196,22 +201,23 @@ enum { #define WL1271_JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */ #define WL1271_JOIN_CMD_TX_SESSION_OFFSET 1 +#define WL1271_JOIN_CMD_BSS_TYPE_5GHZ 0x10 struct wl1271_cmd_join { struct wl1271_cmd_header header; - u32 bssid_lsb; - u16 bssid_msb; - u16 beacon_interval; /* in TBTTs */ - u32 rx_config_options; - u32 rx_filter_options; + __le32 bssid_lsb; + __le16 bssid_msb; + __le16 beacon_interval; /* in TBTTs */ + __le32 rx_config_options; + __le32 rx_filter_options; /* * The target uses this field to determine the rate at * which to transmit control frame responses (such as * ACK or CTS frames). */ - u32 basic_rate_set; + __le32 basic_rate_set; u8 dtim_interval; /* * bits 0-2: This bitwise field specifies the type @@ -240,10 +246,10 @@ struct cmd_enabledisable_path { struct wl1271_cmd_template_set { struct wl1271_cmd_header header; - u16 len; + __le16 len; u8 template_type; u8 index; /* relevant only for KLV_TEMPLATE type */ - u32 enabled_rates; + __le32 enabled_rates; u8 short_retry_limit; u8 long_retry_limit; u8 aflags; @@ -280,18 +286,13 @@ struct wl1271_cmd_ps_params { * to power save mode. */ u8 hang_over_period; - u32 null_data_rate; + __le32 null_data_rate; } __attribute__ ((packed)); /* HW encryption keys */ #define NUM_ACCESS_CATEGORIES_COPY 4 #define MAX_KEY_SIZE 32 -/* When set, disable HW encryption */ -#define DF_ENCRYPTION_DISABLE 0x01 -/* When set, disable HW decryption */ -#define DF_SNIFF_MODE_ENABLE 0x80 - enum wl1271_cmd_key_action { KEY_ADD_OR_REPLACE = 1, KEY_REMOVE = 2, @@ -316,9 +317,9 @@ struct wl1271_cmd_set_keys { u8 addr[ETH_ALEN]; /* key_action_e */ - u16 key_action; + __le16 key_action; - u16 reserved_1; + __le16 reserved_1; /* key size in bytes */ u8 key_size; @@ -334,8 +335,8 @@ struct wl1271_cmd_set_keys { u8 id; u8 reserved_2[6]; u8 key[MAX_KEY_SIZE]; - u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; - u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; + __le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; + __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; } __attribute__ ((packed)); @@ -347,19 +348,22 @@ struct wl1271_cmd_set_keys { #define WL1271_SCAN_OPT_PRIORITY_HIGH 4 #define WL1271_SCAN_CHAN_MIN_DURATION 30000 /* TU */ #define WL1271_SCAN_CHAN_MAX_DURATION 60000 /* TU */ +#define WL1271_SCAN_BAND_2_4_GHZ 0 +#define WL1271_SCAN_BAND_5_GHZ 1 +#define WL1271_SCAN_BAND_DUAL 2 struct basic_scan_params { - u32 rx_config_options; - u32 rx_filter_options; + __le32 rx_config_options; + __le32 rx_filter_options; /* Scan option flags (WL1271_SCAN_OPT_*) */ - u16 scan_options; + __le16 scan_options; /* Number of scan channels in the list (maximum 30) */ u8 num_channels; /* This field indicates the number of probe requests to send per channel for an active scan */ u8 num_probe_requests; /* Rate bit field for sending the probes */ - u32 tx_rate; + __le32 tx_rate; u8 tid_trigger; u8 ssid_len; /* in order to align */ @@ -374,10 +378,10 @@ struct basic_scan_params { struct basic_scan_channel_params { /* Duration in TU to wait for frames on a channel for active scan */ - u32 min_duration; - u32 max_duration; - u32 bssid_lsb; - u16 bssid_msb; + __le32 min_duration; + __le32 max_duration; + __le32 bssid_lsb; + __le16 bssid_msb; u8 early_termination; u8 tx_power_att; u8 channel; @@ -397,13 +401,13 @@ struct wl1271_cmd_scan { struct wl1271_cmd_trigger_scan_to { struct wl1271_cmd_header header; - u32 timeout; -}; + __le32 timeout; +} __attribute__ ((packed)); struct wl1271_cmd_test_header { u8 id; u8 padding[3]; -}; +} __attribute__ ((packed)); enum wl1271_channel_tune_bands { WL1271_CHANNEL_TUNE_BAND_2_4, @@ -416,6 +420,76 @@ enum wl1271_channel_tune_bands { #define TEST_CMD_P2G_CAL 0x02 #define TEST_CMD_CHANNEL_TUNE 0x0d #define TEST_CMD_UPDATE_PD_REFERENCE_POINT 0x1d +#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19 +#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E + +struct wl1271_general_parms_cmd { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + u8 ref_clk; + u8 settling_time; + u8 clk_valid_on_wakeup; + u8 dc2dcmode; + u8 single_dual_band; + + u8 tx_bip_fem_autodetect; + u8 tx_bip_fem_manufacturer; + u8 settings; +} __attribute__ ((packed)); + +struct wl1271_radio_parms_cmd { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + /* Static radio parameters */ + /* 2.4GHz */ + u8 rx_trace_loss; + u8 tx_trace_loss; + s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* 5GHz */ + u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* Dynamic radio parameters */ + /* 2.4GHz */ + __le16 tx_ref_pd_voltage; + s8 tx_ref_power; + s8 tx_offset_db; + + s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4]; + s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4]; + s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS]; + + u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS]; + u8 rx_fem_insertion_loss; + + u8 padding2; + + /* 5GHz */ + __le16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5]; + + s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5]; + s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS]; + + /* FIXME: this is inconsistent with the types for 2.4GHz */ + s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS]; + s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + + u8 padding3[2]; +} __attribute__ ((packed)); struct wl1271_cmd_cal_channel_tune { struct wl1271_cmd_header header; @@ -425,7 +499,7 @@ struct wl1271_cmd_cal_channel_tune { u8 band; u8 channel; - u16 radio_status; + __le16 radio_status; } __attribute__ ((packed)); struct wl1271_cmd_cal_update_ref_point { @@ -433,8 +507,8 @@ struct wl1271_cmd_cal_update_ref_point { struct wl1271_cmd_test_header test; - s32 ref_power; - s32 ref_detector; + __le32 ref_power; + __le32 ref_detector; u8 sub_band; u8 padding[3]; } __attribute__ ((packed)); @@ -449,16 +523,42 @@ struct wl1271_cmd_cal_p2g { struct wl1271_cmd_test_header test; - u16 len; + __le16 len; u8 buf[MAX_TLV_LENGTH]; u8 type; u8 padding; - s16 radio_status; + __le16 radio_status; u8 nvs_version[MAX_NVS_VERSION_LENGTH]; u8 sub_band_mask; u8 padding2; } __attribute__ ((packed)); + +/* + * There are three types of disconnections: + * + * DISCONNECT_IMMEDIATE: the fw doesn't send any frames + * DISCONNECT_DEAUTH: the fw generates a DEAUTH request with the reason + * we have passed + * DISCONNECT_DISASSOC: the fw generates a DESASSOC request with the reason + * we have passed + */ +enum wl1271_disconnect_type { + DISCONNECT_IMMEDIATE, + DISCONNECT_DEAUTH, + DISCONNECT_DISASSOC +}; + +struct wl1271_cmd_disconnect { + __le32 rx_config_options; + __le32 rx_filter_options; + + __le16 reason; + u8 type; + + u8 padding; +} __attribute__ ((packed)); + #endif /* __WL1271_CMD_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h new file mode 100644 index 000000000000..565373ede265 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -0,0 +1,919 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_CONF_H__ +#define __WL1271_CONF_H__ + +enum { + CONF_HW_BIT_RATE_1MBPS = BIT(0), + CONF_HW_BIT_RATE_2MBPS = BIT(1), + CONF_HW_BIT_RATE_5_5MBPS = BIT(2), + CONF_HW_BIT_RATE_6MBPS = BIT(3), + CONF_HW_BIT_RATE_9MBPS = BIT(4), + CONF_HW_BIT_RATE_11MBPS = BIT(5), + CONF_HW_BIT_RATE_12MBPS = BIT(6), + CONF_HW_BIT_RATE_18MBPS = BIT(7), + CONF_HW_BIT_RATE_22MBPS = BIT(8), + CONF_HW_BIT_RATE_24MBPS = BIT(9), + CONF_HW_BIT_RATE_36MBPS = BIT(10), + CONF_HW_BIT_RATE_48MBPS = BIT(11), + CONF_HW_BIT_RATE_54MBPS = BIT(12), + CONF_HW_BIT_RATE_MCS_0 = BIT(13), + CONF_HW_BIT_RATE_MCS_1 = BIT(14), + CONF_HW_BIT_RATE_MCS_2 = BIT(15), + CONF_HW_BIT_RATE_MCS_3 = BIT(16), + CONF_HW_BIT_RATE_MCS_4 = BIT(17), + CONF_HW_BIT_RATE_MCS_5 = BIT(18), + CONF_HW_BIT_RATE_MCS_6 = BIT(19), + CONF_HW_BIT_RATE_MCS_7 = BIT(20) +}; + +enum { + CONF_HW_RATE_INDEX_1MBPS = 0, + CONF_HW_RATE_INDEX_2MBPS = 1, + CONF_HW_RATE_INDEX_5_5MBPS = 2, + CONF_HW_RATE_INDEX_6MBPS = 3, + CONF_HW_RATE_INDEX_9MBPS = 4, + CONF_HW_RATE_INDEX_11MBPS = 5, + CONF_HW_RATE_INDEX_12MBPS = 6, + CONF_HW_RATE_INDEX_18MBPS = 7, + CONF_HW_RATE_INDEX_22MBPS = 8, + CONF_HW_RATE_INDEX_24MBPS = 9, + CONF_HW_RATE_INDEX_36MBPS = 10, + CONF_HW_RATE_INDEX_48MBPS = 11, + CONF_HW_RATE_INDEX_54MBPS = 12, + CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS, +}; + +struct conf_sg_settings { + /* + * Defines the PER threshold in PPM of the BT voice of which reaching + * this value will trigger raising the priority of the BT voice by + * the BT IP until next NFS sample interval time as defined in + * nfs_sample_interval. + * + * Unit: PER value in PPM (parts per million) + * #Error_packets / #Total_packets + + * Range: u32 + */ + u32 per_threshold; + + /* + * This value is an absolute time in micro-seconds to limit the + * maximum scan duration compensation while in SG + */ + u32 max_scan_compensation_time; + + /* Defines the PER threshold of the BT voice of which reaching this + * value will trigger raising the priority of the BT voice until next + * NFS sample interval time as defined in sample_interval. + * + * Unit: msec + * Range: 1-65000 + */ + u16 nfs_sample_interval; + + /* + * Defines the load ratio for the BT. + * The WLAN ratio is: 100 - load_ratio + * + * Unit: Percent + * Range: 0-100 + */ + u8 load_ratio; + + /* + * true - Co-ex is allowed to enter/exit P.S automatically and + * transparently to the host + * + * false - Co-ex is disallowed to enter/exit P.S and will trigger an + * event to the host to notify for the need to enter/exit P.S + * due to BT change state + * + */ + u8 auto_ps_mode; + + /* + * This parameter defines the compensation percentage of num of probe + * requests in case scan is initiated during BT voice/BT ACL + * guaranteed link. + * + * Unit: Percent + * Range: 0-255 (0 - No compensation) + */ + u8 probe_req_compensation; + + /* + * This parameter defines the compensation percentage of scan window + * size in case scan is initiated during BT voice/BT ACL Guaranteed + * link. + * + * Unit: Percent + * Range: 0-255 (0 - No compensation) + */ + u8 scan_window_compensation; + + /* + * Defines the antenna configuration. + * + * Range: 0 - Single Antenna; 1 - Dual Antenna + */ + u8 antenna_config; + + /* + * The percent out of the Max consecutive beacon miss roaming trigger + * which is the threshold for raising the priority of beacon + * reception. + * + * Range: 1-100 + * N = MaxConsecutiveBeaconMiss + * P = coexMaxConsecutiveBeaconMissPrecent + * Threshold = MIN( N-1, round(N * P / 100)) + */ + u8 beacon_miss_threshold; + + /* + * The RX rate threshold below which rate adaptation is assumed to be + * occurring at the AP which will raise priority for ACTIVE_RX and RX + * SP. + * + * Range: HW_BIT_RATE_* + */ + u32 rate_adaptation_threshold; + + /* + * The SNR above which the RX rate threshold indicating AP rate + * adaptation is valid + * + * Range: -128 - 127 + */ + s8 rate_adaptation_snr; +}; + +enum conf_rx_queue_type { + CONF_RX_QUEUE_TYPE_LOW_PRIORITY, /* All except the high priority */ + CONF_RX_QUEUE_TYPE_HIGH_PRIORITY, /* Management and voice packets */ +}; + +struct conf_rx_settings { + /* + * The maximum amount of time, in TU, before the + * firmware discards the MSDU. + * + * Range: 0 - 0xFFFFFFFF + */ + u32 rx_msdu_life_time; + + /* + * Packet detection threshold in the PHY. + * + * FIXME: details unknown. + */ + u32 packet_detection_threshold; + + /* + * The longest time the STA will wait to receive traffic from the AP + * after a PS-poll has been transmitted. + * + * Range: 0 - 200000 + */ + u16 ps_poll_timeout; + /* + * The longest time the STA will wait to receive traffic from the AP + * after a frame has been sent from an UPSD enabled queue. + * + * Range: 0 - 200000 + */ + u16 upsd_timeout; + + /* + * The number of octets in an MPDU, below which an RTS/CTS + * handshake is not performed. + * + * Range: 0 - 4096 + */ + u16 rts_threshold; + + /* + * The RX Clear Channel Assessment threshold in the PHY + * (the energy threshold). + * + * Range: ENABLE_ENERGY_D == 0x140A + * DISABLE_ENERGY_D == 0xFFEF + */ + u16 rx_cca_threshold; + + /* + * Occupied Rx mem-blocks number which requires interrupting the host + * (0 = no buffering, 0xffff = disabled). + * + * Range: u16 + */ + u16 irq_blk_threshold; + + /* + * Rx packets number which requires interrupting the host + * (0 = no buffering). + * + * Range: u16 + */ + u16 irq_pkt_threshold; + + /* + * Max time in msec the FW may delay RX-Complete interrupt. + * + * Range: 1 - 100 + */ + u16 irq_timeout; + + /* + * The RX queue type. + * + * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY, + */ + u8 queue_type; +}; + +#define CONF_TX_MAX_RATE_CLASSES 8 + +#define CONF_TX_RATE_MASK_UNSPECIFIED 0 +#define CONF_TX_RATE_MASK_ALL 0x1eff +#define CONF_TX_RATE_RETRY_LIMIT 10 + +struct conf_tx_rate_class { + + /* + * The rates enabled for this rate class. + * + * Range: CONF_HW_BIT_RATE_* bit mask + */ + u32 enabled_rates; + + /* + * The dot11 short retry limit used for TX retries. + * + * Range: u8 + */ + u8 short_retry_limit; + + /* + * The dot11 long retry limit used for TX retries. + * + * Range: u8 + */ + u8 long_retry_limit; + + /* + * Flags controlling the attributes of TX transmission. + * + * Range: bit 0: Truncate - when set, FW attempts to send a frame stop + * when the total valid per-rate attempts have + * been exhausted; otherwise transmissions + * will continue at the lowest available rate + * until the appropriate one of the + * short_retry_limit, long_retry_limit, + * dot11_max_transmit_msdu_life_time, or + * max_tx_life_time, is exhausted. + * 1: Preamble Override - indicates if the preamble type + * should be used in TX. + * 2: Preamble Type - the type of the preamble to be used by + * the policy (0 - long preamble, 1 - short preamble. + */ + u8 aflags; +}; + +#define CONF_TX_MAX_AC_COUNT 4 + +/* Slot number setting to start transmission at PIFS interval */ +#define CONF_TX_AIFS_PIFS 1 +/* Slot number setting to start transmission at DIFS interval normal + * DCF access */ +#define CONF_TX_AIFS_DIFS 2 + + +enum conf_tx_ac { + CONF_TX_AC_BE = 0, /* best effort / legacy */ + CONF_TX_AC_BK = 1, /* background */ + CONF_TX_AC_VI = 2, /* video */ + CONF_TX_AC_VO = 3, /* voice */ + CONF_TX_AC_CTS2SELF = 4, /* fictious AC, follows AC_VO */ + CONF_TX_AC_ANY_TID = 0x1f +}; + +struct conf_tx_ac_category { + /* + * The AC class identifier. + * + * Range: enum conf_tx_ac + */ + u8 ac; + + /* + * The contention window minimum size (in slots) for the access + * class. + * + * Range: u8 + */ + u8 cw_min; + + /* + * The contention window maximum size (in slots) for the access + * class. + * + * Range: u8 + */ + u16 cw_max; + + /* + * The AIF value (in slots) for the access class. + * + * Range: u8 + */ + u8 aifsn; + + /* + * The TX Op Limit (in microseconds) for the access class. + * + * Range: u16 + */ + u16 tx_op_limit; +}; + +#define CONF_TX_MAX_TID_COUNT 7 + +enum { + CONF_CHANNEL_TYPE_DCF = 0, /* DC/LEGACY*/ + CONF_CHANNEL_TYPE_EDCF = 1, /* EDCA*/ + CONF_CHANNEL_TYPE_HCCA = 2, /* HCCA*/ +}; + +enum { + CONF_PS_SCHEME_LEGACY = 0, + CONF_PS_SCHEME_UPSD_TRIGGER = 1, + CONF_PS_SCHEME_LEGACY_PSPOLL = 2, + CONF_PS_SCHEME_SAPSD = 3, +}; + +enum { + CONF_ACK_POLICY_LEGACY = 0, + CONF_ACK_POLICY_NO_ACK = 1, + CONF_ACK_POLICY_BLOCK = 2, +}; + + +struct conf_tx_tid { + u8 queue_id; + u8 channel_type; + u8 tsid; + u8 ps_scheme; + u8 ack_policy; + u32 apsd_conf[2]; +}; + +struct conf_tx_settings { + /* + * The TX ED value for TELEC Enable/Disable. + * + * Range: 0, 1 + */ + u8 tx_energy_detection; + + /* + * Configuration for rate classes for TX (currently only one + * rate class supported.) + */ + struct conf_tx_rate_class rc_conf; + + /* + * Configuration for access categories for TX rate control. + */ + u8 ac_conf_count; + struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT]; + + /* + * Configuration for TID parameters. + */ + u8 tid_conf_count; + struct conf_tx_tid tid_conf[CONF_TX_MAX_TID_COUNT]; + + /* + * The TX fragmentation threshold. + * + * Range: u16 + */ + u16 frag_threshold; + + /* + * Max time in msec the FW may delay frame TX-Complete interrupt. + * + * Range: u16 + */ + u16 tx_compl_timeout; + + /* + * Completed TX packet count which requires to issue the TX-Complete + * interrupt. + * + * Range: u16 + */ + u16 tx_compl_threshold; + +}; + +enum { + CONF_WAKE_UP_EVENT_BEACON = 0x01, /* Wake on every Beacon*/ + CONF_WAKE_UP_EVENT_DTIM = 0x02, /* Wake on every DTIM*/ + CONF_WAKE_UP_EVENT_N_DTIM = 0x04, /* Wake every Nth DTIM */ + CONF_WAKE_UP_EVENT_N_BEACONS = 0x08, /* Wake every Nth beacon */ + CONF_WAKE_UP_EVENT_BITS_MASK = 0x0F +}; + +#define CONF_MAX_BCN_FILT_IE_COUNT 32 + +#define CONF_BCN_RULE_PASS_ON_CHANGE BIT(0) +#define CONF_BCN_RULE_PASS_ON_APPEARANCE BIT(1) + +#define CONF_BCN_IE_OUI_LEN 3 +#define CONF_BCN_IE_VER_LEN 2 + +struct conf_bcn_filt_rule { + /* + * IE number to which to associate a rule. + * + * Range: u8 + */ + u8 ie; + + /* + * Rule to associate with the specific ie. + * + * Range: CONF_BCN_RULE_PASS_ON_* + */ + u8 rule; + + /* + * OUI for the vendor specifie IE (221) + */ + u8 oui[CONF_BCN_IE_OUI_LEN]; + + /* + * Type for the vendor specifie IE (221) + */ + u8 type; + + /* + * Version for the vendor specifie IE (221) + */ + u8 version[CONF_BCN_IE_VER_LEN]; +}; + +#define CONF_MAX_RSSI_SNR_TRIGGERS 8 + +enum { + CONF_TRIG_METRIC_RSSI_BEACON = 0, + CONF_TRIG_METRIC_RSSI_DATA, + CONF_TRIG_METRIC_SNR_BEACON, + CONF_TRIG_METRIC_SNR_DATA +}; + +enum { + CONF_TRIG_EVENT_TYPE_LEVEL = 0, + CONF_TRIG_EVENT_TYPE_EDGE +}; + +enum { + CONF_TRIG_EVENT_DIR_LOW = 0, + CONF_TRIG_EVENT_DIR_HIGH, + CONF_TRIG_EVENT_DIR_BIDIR +}; + + +struct conf_sig_trigger { + /* + * The RSSI / SNR threshold value. + * + * FIXME: what is the range? + */ + s16 threshold; + + /* + * Minimum delay between two trigger events for this trigger in ms. + * + * Range: 0 - 60000 + */ + u16 pacing; + + /* + * The measurement data source for this trigger. + * + * Range: CONF_TRIG_METRIC_* + */ + u8 metric; + + /* + * The trigger type of this trigger. + * + * Range: CONF_TRIG_EVENT_TYPE_* + */ + u8 type; + + /* + * The direction of the trigger. + * + * Range: CONF_TRIG_EVENT_DIR_* + */ + u8 direction; + + /* + * Hysteresis range of the trigger around the threshold (in dB) + * + * Range: u8 + */ + u8 hysteresis; + + /* + * Index of the trigger rule. + * + * Range: 0 - CONF_MAX_RSSI_SNR_TRIGGERS-1 + */ + u8 index; + + /* + * Enable / disable this rule (to use for clearing rules.) + * + * Range: 1 - Enabled, 2 - Not enabled + */ + u8 enable; +}; + +struct conf_sig_weights { + + /* + * RSSI from beacons average weight. + * + * Range: u8 + */ + u8 rssi_bcn_avg_weight; + + /* + * RSSI from data average weight. + * + * Range: u8 + */ + u8 rssi_pkt_avg_weight; + + /* + * SNR from beacons average weight. + * + * Range: u8 + */ + u8 snr_bcn_avg_weight; + + /* + * SNR from data average weight. + * + * Range: u8 + */ + u8 snr_pkt_avg_weight; +}; + +enum conf_bcn_filt_mode { + CONF_BCN_FILT_MODE_DISABLED = 0, + CONF_BCN_FILT_MODE_ENABLED = 1 +}; + +enum conf_bet_mode { + CONF_BET_MODE_DISABLE = 0, + CONF_BET_MODE_ENABLE = 1, +}; + +struct conf_conn_settings { + /* + * Firmware wakeup conditions configuration. The host may set only + * one bit. + * + * Range: CONF_WAKE_UP_EVENT_* + */ + u8 wake_up_event; + + /* + * Listen interval for beacons or Dtims. + * + * Range: 0 for beacon and Dtim wakeup + * 1-10 for x Dtims + * 1-255 for x beacons + */ + u8 listen_interval; + + /* + * Enable or disable the beacon filtering. + * + * Range: CONF_BCN_FILT_MODE_* + */ + enum conf_bcn_filt_mode bcn_filt_mode; + + /* + * Configure Beacon filter pass-thru rules. + */ + u8 bcn_filt_ie_count; + struct conf_bcn_filt_rule bcn_filt_ie[CONF_MAX_BCN_FILT_IE_COUNT]; + + /* + * The number of consequtive beacons to lose, before the firmware + * becomes out of synch. + * + * Range: u32 + */ + u32 synch_fail_thold; + + /* + * After out-of-synch, the number of TU's to wait without a further + * received beacon (or probe response) before issuing the BSS_EVENT_LOSE + * event. + * + * Range: u32 + */ + u32 bss_lose_timeout; + + /* + * Beacon receive timeout. + * + * Range: u32 + */ + u32 beacon_rx_timeout; + + /* + * Broadcast receive timeout. + * + * Range: u32 + */ + u32 broadcast_timeout; + + /* + * Enable/disable reception of broadcast packets in power save mode + * + * Range: 1 - enable, 0 - disable + */ + u8 rx_broadcast_in_ps; + + /* + * Consequtive PS Poll failures before sending event to driver + * + * Range: u8 + */ + u8 ps_poll_threshold; + + /* + * Configuration of signal (rssi/snr) triggers. + */ + u8 sig_trigger_count; + struct conf_sig_trigger sig_trigger[CONF_MAX_RSSI_SNR_TRIGGERS]; + + /* + * Configuration of signal average weights. + */ + struct conf_sig_weights sig_weights; + + /* + * Specifies if beacon early termination procedure is enabled or + * disabled. + * + * Range: CONF_BET_MODE_* + */ + u8 bet_enable; + + /* + * Specifies the maximum number of consecutive beacons that may be + * early terminated. After this number is reached at least one full + * beacon must be correctly received in FW before beacon ET + * resumes. + * + * Range 0 - 255 + */ + u8 bet_max_consecutive; + + /* + * Specifies the maximum number of times to try PSM entry if it fails + * (if sending the appropriate null-func message fails.) + * + * Range 0 - 255 + */ + u8 psm_entry_retries; +}; + +#define CONF_SR_ERR_TBL_MAX_VALUES 14 + +struct conf_mart_reflex_err_table { + /* + * Length of the error table values table. + * + * Range: 0 - CONF_SR_ERR_TBL_MAX_VALUES + */ + u8 len; + + /* + * Smart Reflex error table upper limit. + * + * Range: s8 + */ + s8 upper_limit; + + /* + * Smart Reflex error table values. + * + * Range: s8 + */ + s8 values[CONF_SR_ERR_TBL_MAX_VALUES]; +}; + +enum { + CONF_REF_CLK_19_2_E, + CONF_REF_CLK_26_E, + CONF_REF_CLK_38_4_E, + CONF_REF_CLK_52_E +}; + +enum single_dual_band_enum { + CONF_SINGLE_BAND, + CONF_DUAL_BAND +}; + +struct conf_general_parms { + /* + * RF Reference Clock type / speed + * + * Range: CONF_REF_CLK_* + */ + u8 ref_clk; + + /* + * Settling time of the reference clock after boot. + * + * Range: u8 + */ + u8 settling_time; + + /* + * Flag defining whether clock is valid on wakeup. + * + * Range: 0 - not valid on wakeup, 1 - valid on wakeup + */ + u8 clk_valid_on_wakeup; + + /* + * DC-to-DC mode. + * + * Range: Unknown + */ + u8 dc2dcmode; + + /* + * Flag defining whether used as single or dual-band. + * + * Range: CONF_SINGLE_BAND, CONF_DUAL_BAND + */ + u8 single_dual_band; + + /* + * TX bip fem autodetect flag. + * + * Range: Unknown + */ + u8 tx_bip_fem_autodetect; + + /* + * TX bip gem manufacturer. + * + * Range: Unknown + */ + u8 tx_bip_fem_manufacturer; + + /* + * Settings flags. + * + * Range: Unknown + */ + u8 settings; +}; + +#define CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE 15 +#define CONF_NUMBER_OF_SUB_BANDS_5 7 +#define CONF_NUMBER_OF_RATE_GROUPS 6 +#define CONF_NUMBER_OF_CHANNELS_2_4 14 +#define CONF_NUMBER_OF_CHANNELS_5 35 + +struct conf_radio_parms { + /* + * Static radio parameters for 2.4GHz + * + * Range: unknown + */ + u8 rx_trace_loss; + u8 tx_trace_loss; + s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* + * Static radio parameters for 5GHz + * + * Range: unknown + */ + u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* + * Dynamic radio parameters for 2.4GHz + * + * Range: unknown + */ + s16 tx_ref_pd_voltage; + s8 tx_ref_power; + s8 tx_offset_db; + + s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4]; + s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4]; + s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS]; + + u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS]; + u8 rx_fem_insertion_loss; + + /* + * Dynamic radio parameters for 5GHz + * + * Range: unknown + */ + s16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5]; + s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5]; + + s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5]; + s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS]; + + /* FIXME: this is inconsistent with the types for 2.4GHz */ + s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS]; + s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; +}; + +#define CONF_SR_ERR_TBL_COUNT 3 + +struct conf_init_settings { + /* + * Configure Smart Reflex error table values. + */ + struct conf_mart_reflex_err_table sr_err_tbl[CONF_SR_ERR_TBL_COUNT]; + + /* + * Smart Reflex enable flag. + * + * Range: 1 - Smart Reflex enabled, 0 - Smart Reflex disabled + */ + u8 sr_enable; + + /* + * Configure general parameters. + */ + struct conf_general_parms genparam; + + /* + * Configure radio parameters. + */ + struct conf_radio_parms radioparam; + +}; + +struct conf_drv_settings { + struct conf_sg_settings sg; + struct conf_rx_settings rx; + struct conf_tx_settings tx; + struct conf_conn_settings conn; + struct conf_init_settings init; +}; + +#endif diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c index f3afd4a6ff33..d13fdd99c85c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.c +++ b/drivers/net/wireless/wl12xx/wl1271_event.c @@ -26,23 +26,86 @@ #include "wl1271_spi.h" #include "wl1271_event.h" #include "wl1271_ps.h" +#include "wl12xx_80211.h" static int wl1271_event_scan_complete(struct wl1271 *wl, struct event_mailbox *mbox) { + int size = sizeof(struct wl12xx_probe_req_template); wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); if (wl->scanning) { - mutex_unlock(&wl->mutex); - ieee80211_scan_completed(wl->hw, false); - mutex_lock(&wl->mutex); - wl->scanning = false; + if (wl->scan.state == WL1271_SCAN_BAND_DUAL) { + wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + NULL, size); + /* 2.4 GHz band scanned, scan 5 GHz band, pretend + * to the wl1271_cmd_scan function that we are not + * scanning as it checks that. + */ + wl->scanning = false; + wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len, + wl->scan.active, + wl->scan.high_prio, + WL1271_SCAN_BAND_5_GHZ, + wl->scan.probe_requests); + } else { + if (wl->scan.state == WL1271_SCAN_BAND_2_4_GHZ) + wl1271_cmd_template_set(wl, + CMD_TEMPL_CFG_PROBE_REQ_2_4, + NULL, size); + else + wl1271_cmd_template_set(wl, + CMD_TEMPL_CFG_PROBE_REQ_5, + NULL, size); + + mutex_unlock(&wl->mutex); + ieee80211_scan_completed(wl->hw, false); + mutex_lock(&wl->mutex); + wl->scanning = false; + } } - return 0; } +static int wl1271_event_ps_report(struct wl1271 *wl, + struct event_mailbox *mbox, + bool *beacon_loss) +{ + int ret = 0; + + wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); + + switch (mbox->ps_status) { + case EVENT_ENTER_POWER_SAVE_FAIL: + if (!wl->psm) { + wl->psm_entry_retry = 0; + break; + } + + if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) { + wl->psm_entry_retry++; + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + } else { + wl1271_error("PSM entry failed, giving up.\n"); + wl->psm_entry_retry = 0; + *beacon_loss = true; + } + break; + case EVENT_ENTER_POWER_SAVE_SUCCESS: + wl->psm_entry_retry = 0; + break; + case EVENT_EXIT_POWER_SAVE_FAIL: + wl1271_info("PSM exit failed"); + break; + case EVENT_EXIT_POWER_SAVE_SUCCESS: + default: + break; + } + + return ret; +} + static void wl1271_event_mbox_dump(struct event_mailbox *mbox) { wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); @@ -54,10 +117,12 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) { int ret; u32 vector; + bool beacon_loss = false; wl1271_event_mbox_dump(mbox); - vector = mbox->events_vector & ~(mbox->events_mask); + vector = le32_to_cpu(mbox->events_vector); + vector &= ~(le32_to_cpu(mbox->events_mask)); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { @@ -66,14 +131,34 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) return ret; } - if (vector & BSS_LOSE_EVENT_ID) { + /* + * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon + * filtering) is enabled. Without PSM, the stack will receive all + * beacons and can detect beacon loss by itself. + */ + if (vector & BSS_LOSE_EVENT_ID && wl->psm) { wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); - if (wl->psm_requested && wl->psm) { - ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); - if (ret < 0) - return ret; - } + /* indicate to the stack, that beacons have been lost */ + beacon_loss = true; + } + + if (vector & PS_REPORT_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); + ret = wl1271_event_ps_report(wl, mbox, &beacon_loss); + if (ret < 0) + return ret; + } + + if (beacon_loss) { + /* Obviously, it's dangerous to release the mutex while + we are holding many of the variables in the wl struct. + That's why it's done last in the function, and care must + be taken that nothing more is done after this function + returns. */ + mutex_unlock(&wl->mutex); + ieee80211_beacon_loss(wl->vif); + mutex_lock(&wl->mutex); } return 0; @@ -92,14 +177,14 @@ int wl1271_event_unmask(struct wl1271 *wl) void wl1271_event_mbox_config(struct wl1271 *wl) { - wl->mbox_ptr[0] = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->mbox_ptr[0] = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR); wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); } -int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) +int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack) { struct event_mailbox mbox; int ret; @@ -110,8 +195,8 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) return -EINVAL; /* first we read the mbox descriptor */ - wl1271_spi_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox, - sizeof(struct event_mailbox)); + wl1271_spi_read(wl, wl->mbox_ptr[mbox_num], &mbox, + sizeof(struct event_mailbox), false); /* process the descriptor */ ret = wl1271_event_process(wl, &mbox); @@ -119,7 +204,9 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) return ret; /* then we let the firmware know it can go on...*/ - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); + if (do_ack) + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, + INTR_TRIG_EVENT_ACK); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h index 2cdce7c34bf0..4e3f55ebb1a8 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.h +++ b/drivers/net/wireless/wl12xx/wl1271_event.h @@ -63,36 +63,43 @@ enum { EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, }; +enum { + EVENT_ENTER_POWER_SAVE_FAIL = 0, + EVENT_ENTER_POWER_SAVE_SUCCESS, + EVENT_EXIT_POWER_SAVE_FAIL, + EVENT_EXIT_POWER_SAVE_SUCCESS, +}; + struct event_debug_report { u8 debug_event_id; u8 num_params; - u16 pad; - u32 report_1; - u32 report_2; - u32 report_3; + __le16 pad; + __le32 report_1; + __le32 report_2; + __le32 report_3; } __attribute__ ((packed)); #define NUM_OF_RSSI_SNR_TRIGGERS 8 struct event_mailbox { - u32 events_vector; - u32 events_mask; - u32 reserved_1; - u32 reserved_2; + __le32 events_vector; + __le32 events_mask; + __le32 reserved_1; + __le32 reserved_2; u8 dbg_event_id; u8 num_relevant_params; - u16 reserved_3; - u32 event_report_p1; - u32 event_report_p2; - u32 event_report_p3; + __le16 reserved_3; + __le32 event_report_p1; + __le32 event_report_p2; + __le32 event_report_p3; u8 number_of_scan_results; u8 scan_tag; u8 reserved_4[2]; - u32 compl_scheduled_scan_status; + __le32 compl_scheduled_scan_status; - u16 scheduled_scan_attended_channels; + __le16 scheduled_scan_attended_channels; u8 soft_gemini_sense_info; u8 soft_gemini_protective_info; s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; @@ -105,6 +112,6 @@ struct event_mailbox { int wl1271_event_unmask(struct wl1271 *wl); void wl1271_event_mbox_config(struct wl1271 *wl); -int wl1271_event_handle(struct wl1271 *wl, u8 mbox); +int wl1271_event_handle(struct wl1271 *wl, u8 mbox, bool do_ack); #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 490df217605a..11249b436cf1 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -59,6 +59,14 @@ static int wl1271_init_templates_config(struct wl1271 *wl) if (ret < 0) return ret; + if (wl1271_11a_enabled()) { + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + NULL, + sizeof(struct wl12xx_probe_req_template)); + if (ret < 0) + return ret; + } + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL, sizeof(struct wl12xx_null_data_template)); if (ret < 0) @@ -94,7 +102,7 @@ static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter) { int ret; - ret = wl1271_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); + ret = wl1271_acx_rx_msdu_life_time(wl); if (ret < 0) return ret; @@ -117,7 +125,7 @@ static int wl1271_init_phy_config(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_group_address_tbl(wl); + ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0); if (ret < 0) return ret; @@ -125,7 +133,7 @@ static int wl1271_init_phy_config(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); + ret = wl1271_acx_rts_threshold(wl, wl->conf.rx.rts_threshold); if (ret < 0) return ret; @@ -136,7 +144,8 @@ static int wl1271_init_beacon_filter(struct wl1271 *wl) { int ret; - ret = wl1271_acx_beacon_filter_opt(wl); + /* disable beacon filtering at this stage */ + ret = wl1271_acx_beacon_filter_opt(wl, false); if (ret < 0) return ret; @@ -184,118 +193,15 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl) return 0; } -static int wl1271_init_general_parms(struct wl1271 *wl) -{ - struct wl1271_general_parms *gen_parms; - int ret; - - gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); - if (!gen_parms) - return -ENOMEM; - - gen_parms->id = TEST_CMD_INI_FILE_GENERAL_PARAM; - - gen_parms->ref_clk = REF_CLK_38_4_E; - /* FIXME: magic numbers */ - gen_parms->settling_time = 5; - gen_parms->clk_valid_on_wakeup = 0; - gen_parms->dc2dcmode = 0; - gen_parms->single_dual_band = 0; - gen_parms->tx_bip_fem_autodetect = 1; - gen_parms->tx_bip_fem_manufacturer = 1; - gen_parms->settings = 1; - - ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0); - if (ret < 0) { - wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); - return ret; - } - - kfree(gen_parms); - return 0; -} - -static int wl1271_init_radio_parms(struct wl1271 *wl) -{ - /* - * FIXME: All these magic numbers should be moved to some place where - * they can be configured (separate file?) - */ - - struct wl1271_radio_parms *radio_parms; - int ret; - u8 compensation[] = { 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, 0xfc, 0x00, - 0x08, 0x10, 0xf0, 0xf8, 0x00, 0x0a, 0x14 }; - - u8 tx_rate_limits_normal[] = { 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 }; - u8 tx_rate_limits_degraded[] = { 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 }; - - u8 tx_channel_limits_11b[] = { 0x22, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x22, 0x50, - 0x22, 0x50 }; - - u8 tx_channel_limits_ofdm[] = { 0x20, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x20, 0x50, - 0x20, 0x50 }; - - u8 tx_pdv_rate_offsets[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - u8 tx_ibias[] = { 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 }; - - radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); - if (!radio_parms) - return -ENOMEM; - - radio_parms->id = TEST_CMD_INI_FILE_RADIO_PARAM; - - /* Static radio parameters */ - radio_parms->rx_trace_loss = 10; - radio_parms->tx_trace_loss = 10; - memcpy(radio_parms->rx_rssi_and_proc_compens, compensation, - sizeof(compensation)); - - /* We don't set the 5GHz -- N/A */ - - /* Dynamic radio parameters */ - radio_parms->tx_ref_pd_voltage = cpu_to_le16(0x24e); - radio_parms->tx_ref_power = 0x78; - radio_parms->tx_offset_db = 0x0; - - memcpy(radio_parms->tx_rate_limits_normal, tx_rate_limits_normal, - sizeof(tx_rate_limits_normal)); - memcpy(radio_parms->tx_rate_limits_degraded, tx_rate_limits_degraded, - sizeof(tx_rate_limits_degraded)); - - memcpy(radio_parms->tx_channel_limits_11b, tx_channel_limits_11b, - sizeof(tx_channel_limits_11b)); - memcpy(radio_parms->tx_channel_limits_ofdm, tx_channel_limits_ofdm, - sizeof(tx_channel_limits_ofdm)); - memcpy(radio_parms->tx_pdv_rate_offsets, tx_pdv_rate_offsets, - sizeof(tx_pdv_rate_offsets)); - memcpy(radio_parms->tx_ibias, tx_ibias, - sizeof(tx_ibias)); - - radio_parms->rx_fem_insertion_loss = 0x14; - - ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0); - if (ret < 0) - wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed"); - - kfree(radio_parms); - return ret; -} - int wl1271_hw_init(struct wl1271 *wl) { int ret; - ret = wl1271_init_general_parms(wl); + ret = wl1271_cmd_general_parms(wl); if (ret < 0) return ret; - ret = wl1271_init_radio_parms(wl); + ret = wl1271_cmd_radio_parms(wl); if (ret < 0) return ret; @@ -311,8 +217,8 @@ int wl1271_hw_init(struct wl1271 *wl) /* RX config */ ret = wl1271_init_rx_config(wl, - RX_CFG_PROMISCUOUS | RX_CFG_TSF, - RX_FILTER_OPTION_DEF); + RX_CFG_PROMISCUOUS | RX_CFG_TSF, + RX_FILTER_OPTION_DEF); /* RX_CONFIG_OPTION_ANY_DST_ANY_BSS, RX_FILTER_OPTION_FILTER_ALL); */ if (ret < 0) @@ -323,6 +229,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* Initialize connection monitoring thresholds */ + ret = wl1271_acx_conn_monit_params(wl); + if (ret < 0) + goto out_free_memmap; + /* Beacon filtering */ ret = wl1271_init_beacon_filter(wl); if (ret < 0) @@ -369,7 +280,7 @@ int wl1271_hw_init(struct wl1271 *wl) goto out_free_memmap; /* Configure TX rate classes */ - ret = wl1271_acx_rate_policies(wl); + ret = wl1271_acx_rate_policies(wl, CONF_TX_RATE_MASK_ALL); if (ret < 0) goto out_free_memmap; @@ -388,10 +299,16 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* Configure smart reflex */ + ret = wl1271_acx_smart_reflex(wl); + if (ret < 0) + goto out_free_memmap; + return 0; out_free_memmap: kfree(wl->target_mem_map); + wl->target_mem_map = NULL; return ret; } diff --git a/drivers/net/wireless/wl12xx/wl1271_init.h b/drivers/net/wireless/wl12xx/wl1271_init.h index bd8ff0fa2272..930677fbe852 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.h +++ b/drivers/net/wireless/wl12xx/wl1271_init.h @@ -29,87 +29,4 @@ int wl1271_hw_init_power_auth(struct wl1271 *wl); int wl1271_hw_init(struct wl1271 *wl); -/* These are not really a TEST_CMD, but the ref driver uses them as such */ -#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19 -#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E - -struct wl1271_general_parms { - u8 id; - u8 padding[3]; - - u8 ref_clk; - u8 settling_time; - u8 clk_valid_on_wakeup; - u8 dc2dcmode; - u8 single_dual_band; - - u8 tx_bip_fem_autodetect; - u8 tx_bip_fem_manufacturer; - u8 settings; -} __attribute__ ((packed)); - -enum ref_clk_enum { - REF_CLK_19_2_E, - REF_CLK_26_E, - REF_CLK_38_4_E, - REF_CLK_52_E -}; - -#define RSSI_AND_PROCESS_COMPENSATION_SIZE 15 -#define NUMBER_OF_SUB_BANDS_5 7 -#define NUMBER_OF_RATE_GROUPS 6 -#define NUMBER_OF_CHANNELS_2_4 14 -#define NUMBER_OF_CHANNELS_5 35 - -struct wl1271_radio_parms { - u8 id; - u8 padding[3]; - - /* Static radio parameters */ - /* 2.4GHz */ - u8 rx_trace_loss; - u8 tx_trace_loss; - s8 rx_rssi_and_proc_compens[RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* 5GHz */ - u8 rx_trace_loss_5[NUMBER_OF_SUB_BANDS_5]; - u8 tx_trace_loss_5[NUMBER_OF_SUB_BANDS_5]; - s8 rx_rssi_and_proc_compens_5[RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* Dynamic radio parameters */ - /* 2.4GHz */ - s16 tx_ref_pd_voltage; - s8 tx_ref_power; - s8 tx_offset_db; - - s8 tx_rate_limits_normal[NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded[NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_11b[NUMBER_OF_CHANNELS_2_4]; - s8 tx_channel_limits_ofdm[NUMBER_OF_CHANNELS_2_4]; - s8 tx_pdv_rate_offsets[NUMBER_OF_RATE_GROUPS]; - - u8 tx_ibias[NUMBER_OF_RATE_GROUPS]; - u8 rx_fem_insertion_loss; - - u8 padding2; - - /* 5GHz */ - s16 tx_ref_pd_voltage_5[NUMBER_OF_SUB_BANDS_5]; - s8 tx_ref_power_5[NUMBER_OF_SUB_BANDS_5]; - s8 tx_offset_db_5[NUMBER_OF_SUB_BANDS_5]; - - s8 tx_rate_limits_normal_5[NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded_5[NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_ofdm_5[NUMBER_OF_CHANNELS_5]; - s8 tx_pdv_rate_offsets_5[NUMBER_OF_RATE_GROUPS]; - - /* FIXME: this is inconsistent with the types for 2.4GHz */ - s8 tx_ibias_5[NUMBER_OF_RATE_GROUPS]; - s8 rx_fem_insertion_loss_5[NUMBER_OF_SUB_BANDS_5]; - - u8 padding3[2]; -} __attribute__ ((packed)); - #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 27298b19d5bd..b62c00ff42fe 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -30,7 +30,9 @@ #include <linux/spi/spi.h> #include <linux/crc32.h> #include <linux/etherdevice.h> +#include <linux/vmalloc.h> #include <linux/spi/wl12xx.h> +#include <linux/inetdevice.h> #include "wl1271.h" #include "wl12xx_80211.h" @@ -45,10 +47,314 @@ #include "wl1271_cmd.h" #include "wl1271_boot.h" +static struct conf_drv_settings default_conf = { + .sg = { + .per_threshold = 7500, + .max_scan_compensation_time = 120000, + .nfs_sample_interval = 400, + .load_ratio = 50, + .auto_ps_mode = 0, + .probe_req_compensation = 170, + .scan_window_compensation = 50, + .antenna_config = 0, + .beacon_miss_threshold = 60, + .rate_adaptation_threshold = CONF_HW_BIT_RATE_12MBPS, + .rate_adaptation_snr = 0 + }, + .rx = { + .rx_msdu_life_time = 512000, + .packet_detection_threshold = 0, + .ps_poll_timeout = 15, + .upsd_timeout = 15, + .rts_threshold = 2347, + .rx_cca_threshold = 0xFFEF, + .irq_blk_threshold = 0, + .irq_pkt_threshold = USHORT_MAX, + .irq_timeout = 5, + .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY, + }, + .tx = { + .tx_energy_detection = 0, + .rc_conf = { + .enabled_rates = CONF_TX_RATE_MASK_UNSPECIFIED, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0 + }, + .ac_conf_count = 4, + .ac_conf = { + [0] = { + .ac = CONF_TX_AC_BE, + .cw_min = 15, + .cw_max = 63, + .aifsn = 3, + .tx_op_limit = 0, + }, + [1] = { + .ac = CONF_TX_AC_BK, + .cw_min = 15, + .cw_max = 63, + .aifsn = 7, + .tx_op_limit = 0, + }, + [2] = { + .ac = CONF_TX_AC_VI, + .cw_min = 15, + .cw_max = 63, + .aifsn = CONF_TX_AIFS_PIFS, + .tx_op_limit = 3008, + }, + [3] = { + .ac = CONF_TX_AC_VO, + .cw_min = 15, + .cw_max = 63, + .aifsn = CONF_TX_AIFS_PIFS, + .tx_op_limit = 1504, + }, + }, + .tid_conf_count = 7, + .tid_conf = { + [0] = { + .queue_id = 0, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [1] = { + .queue_id = 1, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [2] = { + .queue_id = 2, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [3] = { + .queue_id = 3, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [4] = { + .queue_id = 4, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [5] = { + .queue_id = 5, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [6] = { + .queue_id = 6, + .channel_type = CONF_CHANNEL_TYPE_DCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + } + }, + .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, + .tx_compl_timeout = 5, + .tx_compl_threshold = 5 + }, + .conn = { + .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, + .listen_interval = 0, + .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, + .bcn_filt_ie_count = 1, + .bcn_filt_ie = { + [0] = { + .ie = WLAN_EID_CHANNEL_SWITCH, + .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, + } + }, + .synch_fail_thold = 5, + .bss_lose_timeout = 100, + .beacon_rx_timeout = 10000, + .broadcast_timeout = 20000, + .rx_broadcast_in_ps = 1, + .ps_poll_threshold = 4, + .sig_trigger_count = 2, + .sig_trigger = { + [0] = { + .threshold = -75, + .pacing = 500, + .metric = CONF_TRIG_METRIC_RSSI_BEACON, + .type = CONF_TRIG_EVENT_TYPE_EDGE, + .direction = CONF_TRIG_EVENT_DIR_LOW, + .hysteresis = 2, + .index = 0, + .enable = 1 + }, + [1] = { + .threshold = -75, + .pacing = 500, + .metric = CONF_TRIG_METRIC_RSSI_BEACON, + .type = CONF_TRIG_EVENT_TYPE_EDGE, + .direction = CONF_TRIG_EVENT_DIR_HIGH, + .hysteresis = 2, + .index = 1, + .enable = 1 + } + }, + .sig_weights = { + .rssi_bcn_avg_weight = 10, + .rssi_pkt_avg_weight = 10, + .snr_bcn_avg_weight = 10, + .snr_pkt_avg_weight = 10 + }, + .bet_enable = CONF_BET_MODE_ENABLE, + .bet_max_consecutive = 10, + .psm_entry_retries = 3 + }, + .init = { + .sr_err_tbl = { + [0] = { + .len = 7, + .upper_limit = 0x03, + .values = { + 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8, + 0x00 } + }, + [1] = { + .len = 7, + .upper_limit = 0x03, + .values = { + 0x18, 0x10, 0x05, 0xf6, 0xf0, 0xe8, + 0x00 } + }, + [2] = { + .len = 7, + .upper_limit = 0x03, + .values = { + 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8, + 0x00 } + } + }, + .sr_enable = 1, + .genparam = { + .ref_clk = CONF_REF_CLK_38_4_E, + .settling_time = 5, + .clk_valid_on_wakeup = 0, + .dc2dcmode = 0, + .single_dual_band = CONF_SINGLE_BAND, + .tx_bip_fem_autodetect = 0, + .tx_bip_fem_manufacturer = 1, + .settings = 1, + }, + .radioparam = { + .rx_trace_loss = 10, + .tx_trace_loss = 10, + .rx_rssi_and_proc_compens = { + 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, + 0xfc, 0x00, 0x08, 0x10, 0xf0, 0xf8, + 0x00, 0x0a, 0x14 }, + .rx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 }, + .tx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 }, + .rx_rssi_and_proc_compens_5 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }, + .tx_ref_pd_voltage = 0x24e, + .tx_ref_power = 0x78, + .tx_offset_db = 0x0, + .tx_rate_limits_normal = { + 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 }, + .tx_rate_limits_degraded = { + 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 }, + .tx_channel_limits_11b = { + 0x22, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x22, 0x50, + 0x22, 0x50 }, + .tx_channel_limits_ofdm = { + 0x20, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x20, 0x50, + 0x20, 0x50 }, + .tx_pdv_rate_offsets = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .tx_ibias = { + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 }, + .rx_fem_insertion_loss = 0x14, + .tx_ref_pd_voltage_5 = { + 0x0190, 0x01a4, 0x01c3, 0x01d8, + 0x020a, 0x021c }, + .tx_ref_power_5 = { + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + .tx_offset_db_5 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .tx_rate_limits_normal_5 = { + 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 }, + .tx_rate_limits_degraded_5 = { + 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 }, + .tx_channel_limits_ofdm_5 = { + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50 }, + .tx_pdv_rate_offsets_5 = { + 0x01, 0x02, 0x02, 0x02, 0x02, 0x00 }, + .tx_ibias_5 = { + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, + .rx_fem_insertion_loss_5 = { + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 } + } + } +}; + +static LIST_HEAD(wl_list); + +static void wl1271_conf_init(struct wl1271 *wl) +{ + + /* + * This function applies the default configuration to the driver. This + * function is invoked upon driver load (spi probe.) + * + * The configuration is stored in a run-time structure in order to + * facilitate for run-time adjustment of any of the parameters. Making + * changes to the configuration structure will apply the new values on + * the next interface up (wl1271_op_start.) + */ + + /* apply driver default configuration */ + memcpy(&wl->conf, &default_conf, sizeof(default_conf)); + + if (wl1271_11a_enabled()) + wl->conf.init.genparam.single_dual_band = CONF_DUAL_BAND; +} + + static int wl1271_plt_init(struct wl1271 *wl) { int ret; + ret = wl1271_cmd_general_parms(wl); + if (ret < 0) + return ret; + + ret = wl1271_cmd_radio_parms(wl); + if (ret < 0) + return ret; + ret = wl1271_acx_init_mem_config(wl); if (ret < 0) return ret; @@ -75,20 +381,14 @@ static void wl1271_power_on(struct wl1271 *wl) wl->set_power(true); } -static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status) +static void wl1271_fw_status(struct wl1271 *wl, + struct wl1271_fw_status *status) { u32 total = 0; int i; - /* - * FIXME: Reading the FW status directly from the registers seems to - * be the right thing to do, but it doesn't work. And in the - * reference driver, there is a workaround called - * USE_SDIO_24M_WORKAROUND, which reads the status from memory - * instead, so we do the same here. - */ - - wl1271_spi_mem_read(wl, STATUS_MEM_ADDRESS, status, sizeof(*status)); + wl1271_spi_read(wl, FW_STATUS_ADDR, status, + sizeof(*status), false); wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", @@ -99,25 +399,28 @@ static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status) /* update number of available TX blocks */ for (i = 0; i < NUM_TX_QUEUES; i++) { - u32 cnt = status->tx_released_blks[i] - wl->tx_blocks_freed[i]; - wl->tx_blocks_freed[i] = status->tx_released_blks[i]; + u32 cnt = le32_to_cpu(status->tx_released_blks[i]) - + wl->tx_blocks_freed[i]; + + wl->tx_blocks_freed[i] = + le32_to_cpu(status->tx_released_blks[i]); wl->tx_blocks_available += cnt; total += cnt; } /* if more blocks are available now, schedule some tx work */ if (total && !skb_queue_empty(&wl->tx_queue)) - schedule_work(&wl->tx_work); + ieee80211_queue_work(wl->hw, &wl->tx_work); /* update the host-chipset time offset */ - wl->time_offset = jiffies_to_usecs(jiffies) - status->fw_localtime; + wl->time_offset = jiffies_to_usecs(jiffies) - + le32_to_cpu(status->fw_localtime); } -#define WL1271_IRQ_MAX_LOOPS 10 static void wl1271_irq_work(struct work_struct *work) { - u32 intr, ctr = WL1271_IRQ_MAX_LOOPS; int ret; + u32 intr; struct wl1271 *wl = container_of(work, struct wl1271, irq_work); @@ -132,9 +435,10 @@ static void wl1271_irq_work(struct work_struct *work) if (ret < 0) goto out; - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); - intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); + wl1271_fw_status(wl, wl->fw_status); + intr = le32_to_cpu(wl->fw_status->intr); if (!intr) { wl1271_debug(DEBUG_IRQ, "Zero interrupt received."); goto out_sleep; @@ -142,46 +446,39 @@ static void wl1271_irq_work(struct work_struct *work) intr &= WL1271_INTR_MASK; - do { - wl1271_fw_status(wl, wl->fw_status); - - - if (intr & (WL1271_ACX_INTR_EVENT_A | - WL1271_ACX_INTR_EVENT_B)) { - wl1271_debug(DEBUG_IRQ, - "WL1271_ACX_INTR_EVENT (0x%x)", intr); - if (intr & WL1271_ACX_INTR_EVENT_A) - wl1271_event_handle(wl, 0); - else - wl1271_event_handle(wl, 1); - } + if (intr & WL1271_ACX_INTR_EVENT_A) { + bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true; + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); + wl1271_event_handle(wl, 0, do_ack); + } - if (intr & WL1271_ACX_INTR_INIT_COMPLETE) - wl1271_debug(DEBUG_IRQ, - "WL1271_ACX_INTR_INIT_COMPLETE"); + if (intr & WL1271_ACX_INTR_EVENT_B) { + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); + wl1271_event_handle(wl, 1, true); + } - if (intr & WL1271_ACX_INTR_HW_AVAILABLE) - wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); + if (intr & WL1271_ACX_INTR_INIT_COMPLETE) + wl1271_debug(DEBUG_IRQ, + "WL1271_ACX_INTR_INIT_COMPLETE"); - if (intr & WL1271_ACX_INTR_DATA) { - u8 tx_res_cnt = wl->fw_status->tx_results_counter - - wl->tx_results_count; + if (intr & WL1271_ACX_INTR_HW_AVAILABLE) + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); - wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); + if (intr & WL1271_ACX_INTR_DATA) { + u8 tx_res_cnt = wl->fw_status->tx_results_counter - + wl->tx_results_count; - /* check for tx results */ - if (tx_res_cnt) - wl1271_tx_complete(wl, tx_res_cnt); + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); - wl1271_rx(wl, wl->fw_status); - } + /* check for tx results */ + if (tx_res_cnt) + wl1271_tx_complete(wl, tx_res_cnt); - intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); - intr &= WL1271_INTR_MASK; - } while (intr && --ctr); + wl1271_rx(wl, wl->fw_status); + } out_sleep: - wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); wl1271_ps_elp_sleep(wl); @@ -205,7 +502,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) wl->elp_compl = NULL; } - schedule_work(&wl->irq_work); + ieee80211_queue_work(wl->hw, &wl->irq_work); spin_unlock_irqrestore(&wl->wl_lock, flags); return IRQ_HANDLED; @@ -231,7 +528,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) } wl->fw_len = fw->size; - wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); + wl->fw = vmalloc(wl->fw_len); if (!wl->fw) { wl1271_error("could not allocate memory for the firmware"); @@ -292,7 +589,7 @@ static void wl1271_fw_wakeup(struct wl1271 *wl) u32 elp_reg; elp_reg = ELPCTRL_WAKE_UP; - wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); + wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); } static int wl1271_setup(struct wl1271 *wl) @@ -314,6 +611,7 @@ static int wl1271_setup(struct wl1271 *wl) static int wl1271_chip_wakeup(struct wl1271 *wl) { + struct wl1271_partition_set partition; int ret = 0; wl1271_power_on(wl); @@ -323,11 +621,10 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) /* We don't need a real memory partition here, because we only want * to use the registers at this point. */ - wl1271_set_partition(wl, - 0x00000000, - 0x00000000, - REGISTERS_BASE, - REGISTERS_DOWN_SIZE); + memset(&partition, 0, sizeof(partition)); + partition.reg.start = REGISTERS_BASE; + partition.reg.size = REGISTERS_DOWN_SIZE; + wl1271_set_partition(wl, &partition); /* ELP module wake up */ wl1271_fw_wakeup(wl); @@ -335,7 +632,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) /* whal_FwCtrl_BootSm() */ /* 0. read chip id from CHIP_ID */ - wl->chip.id = wl1271_reg_read32(wl, CHIP_ID_B); + wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B); /* 1. check if chip id is valid */ @@ -346,7 +643,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) - goto out; + goto out_power_off; break; case CHIP_ID_1271_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", @@ -354,56 +651,34 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) - goto out; + goto out_power_off; break; default: wl1271_error("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; - goto out; + goto out_power_off; } if (wl->fw == NULL) { ret = wl1271_fetch_firmware(wl); if (ret < 0) - goto out; + goto out_power_off; } /* No NVS from netlink, try to get it from the filesystem */ if (wl->nvs == NULL) { ret = wl1271_fetch_nvs(wl); if (ret < 0) - goto out; + goto out_power_off; } -out: - return ret; -} - -static void wl1271_filter_work(struct work_struct *work) -{ - struct wl1271 *wl = - container_of(work, struct wl1271, filter_work); - int ret; - - mutex_lock(&wl->mutex); - - if (wl->state == WL1271_STATE_OFF) - goto out; - - ret = wl1271_ps_elp_wakeup(wl, false); - if (ret < 0) - goto out; - - /* FIXME: replace the magic numbers with proper definitions */ - ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); - if (ret < 0) - goto out_sleep; + goto out; -out_sleep: - wl1271_ps_elp_sleep(wl); +out_power_off: + wl1271_power_off(wl); out: - mutex_unlock(&wl->mutex); + return ret; } int wl1271_plt_start(struct wl1271 *wl) @@ -429,13 +704,26 @@ int wl1271_plt_start(struct wl1271 *wl) ret = wl1271_boot(wl); if (ret < 0) - goto out; + goto out_power_off; wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver); ret = wl1271_plt_init(wl); if (ret < 0) - goto out; + goto out_irq_disable; + + /* Make sure power saving is disabled */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + if (ret < 0) + goto out_irq_disable; + + goto out; + +out_irq_disable: + wl1271_disable_interrupts(wl); + +out_power_off: + wl1271_power_off(wl); out: mutex_unlock(&wl->mutex); @@ -462,6 +750,7 @@ int wl1271_plt_stop(struct wl1271 *wl) wl1271_power_off(wl); wl->state = WL1271_STATE_OFF; + wl->rx_counter = 0; out: mutex_unlock(&wl->mutex); @@ -481,7 +770,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * before that, the tx_work will not be initialized! */ - schedule_work(&wl->tx_work); + ieee80211_queue_work(wl->hw, &wl->tx_work); /* * The workqueue is slow to process the tx_queue and we need stop @@ -501,6 +790,93 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) return NETDEV_TX_OK; } +static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, + void *arg) +{ + struct net_device *dev; + struct wireless_dev *wdev; + struct wiphy *wiphy; + struct ieee80211_hw *hw; + struct wl1271 *wl; + struct wl1271 *wl_temp; + struct in_device *idev; + struct in_ifaddr *ifa = arg; + int ret = 0; + + /* FIXME: this ugly function should probably be implemented in the + * mac80211, and here should only be a simple callback handling actual + * setting of the filters. Now we need to dig up references to + * various structures to gain access to what we need. + * Also, because of this, there is no "initial" setting of the filter + * in "op_start", because we don't want to dig up struct net_device + * there - the filter will be set upon first change of the interface + * IP address. */ + + dev = ifa->ifa_dev->dev; + + wdev = dev->ieee80211_ptr; + if (wdev == NULL) + return NOTIFY_DONE; + + wiphy = wdev->wiphy; + if (wiphy == NULL) + return NOTIFY_DONE; + + hw = wiphy_priv(wiphy); + if (hw == NULL) + return NOTIFY_DONE; + + /* Check that the interface is one supported by this driver. */ + wl_temp = hw->priv; + list_for_each_entry(wl, &wl_list, list) { + if (wl == wl_temp) + break; + } + if (wl == NULL) + return NOTIFY_DONE; + + /* Get the interface IP address for the device. "ifa" will become + NULL if: + - there is no IPV4 protocol address configured + - there are multiple (virtual) IPV4 addresses configured + When "ifa" is NULL, filtering will be disabled. + */ + ifa = NULL; + idev = dev->ip_ptr; + if (idev) + ifa = idev->ifa_list; + + if (ifa && ifa->ifa_next) + ifa = NULL; + + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + if (ifa) + ret = wl1271_acx_arp_ip_filter(wl, true, + (u8 *)&ifa->ifa_address, + ACX_IPV4_VERSION); + else + ret = wl1271_acx_arp_ip_filter(wl, false, NULL, + ACX_IPV4_VERSION); + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return NOTIFY_OK; +} + +static struct notifier_block wl1271_dev_notifier = { + .notifier_call = wl1271_dev_notify, +}; + + static int wl1271_op_start(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; @@ -523,22 +899,32 @@ static int wl1271_op_start(struct ieee80211_hw *hw) ret = wl1271_boot(wl); if (ret < 0) - goto out; + goto out_power_off; ret = wl1271_hw_init(wl); if (ret < 0) - goto out; + goto out_irq_disable; wl->state = WL1271_STATE_ON; wl1271_info("firmware booted (%s)", wl->chip.fw_ver); -out: - if (ret < 0) - wl1271_power_off(wl); + goto out; + +out_irq_disable: + wl1271_disable_interrupts(wl); + +out_power_off: + wl1271_power_off(wl); +out: mutex_unlock(&wl->mutex); + if (!ret) { + list_add(&wl->list, &wl_list); + register_inetaddr_notifier(&wl1271_dev_notifier); + } + return ret; } @@ -551,6 +937,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); + unregister_inetaddr_notifier(&wl1271_dev_notifier); + list_del(&wl->list); + mutex_lock(&wl->mutex); WARN_ON(wl->state != WL1271_STATE_ON); @@ -570,7 +959,6 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->tx_work); - cancel_work_sync(&wl->filter_work); mutex_lock(&wl->mutex); @@ -581,19 +969,25 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) memset(wl->bssid, 0, ETH_ALEN); memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1); wl->ssid_len = 0; - wl->listen_int = 1; wl->bss_type = MAX_BSS_TYPE; + wl->band = IEEE80211_BAND_2GHZ; wl->rx_counter = 0; wl->elp = false; wl->psm = 0; + wl->psm_entry_retry = 0; wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->tx_blocks_available = 0; wl->tx_results_count = 0; wl->tx_packets_count = 0; + wl->tx_security_last_seq = 0; + wl->tx_security_seq_16 = 0; + wl->tx_security_seq_32 = 0; wl->time_offset = 0; wl->session_counter = 0; + wl->joined = false; + for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -611,6 +1005,12 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, conf->type, conf->mac_addr); mutex_lock(&wl->mutex); + if (wl->vif) { + ret = -EBUSY; + goto out; + } + + wl->vif = conf->vif; switch (conf->type) { case NL80211_IFTYPE_STATION: @@ -634,7 +1034,12 @@ out: static void wl1271_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) { + struct wl1271 *wl = hw->priv; + + mutex_lock(&wl->mutex); wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); + wl->vif = NULL; + mutex_unlock(&wl->mutex); } #if 0 @@ -657,23 +1062,24 @@ static int wl1271_op_config_interface(struct ieee80211_hw *hw, if (ret < 0) goto out; - memcpy(wl->bssid, conf->bssid, ETH_ALEN); + if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) { + wl1271_debug(DEBUG_MAC80211, "bssid changed"); - ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) - goto out_sleep; + memcpy(wl->bssid, conf->bssid, ETH_ALEN); - wl->ssid_len = conf->ssid_len; - if (wl->ssid_len) - memcpy(wl->ssid, conf->ssid, wl->ssid_len); + ret = wl1271_cmd_join(wl); + if (ret < 0) + goto out_sleep; - if (wl->bss_type != BSS_TYPE_IBSS) { - /* FIXME: replace the magic numbers with proper definitions */ - ret = wl1271_cmd_join(wl, wl->bss_type, 5, 100, 1); + ret = wl1271_cmd_build_null_data(wl); if (ret < 0) goto out_sleep; } + wl->ssid_len = conf->ssid_len; + if (wl->ssid_len) + memcpy(wl->ssid, conf->ssid, wl->ssid_len); + if (conf->changed & IEEE80211_IFCC_BEACON) { beacon = ieee80211_beacon_get(hw, vif); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, @@ -691,12 +1097,6 @@ static int wl1271_op_config_interface(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - - /* FIXME: replace the magic numbers with proper definitions */ - ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); - - if (ret < 0) - goto out_sleep; } out_sleep: @@ -724,26 +1124,22 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&wl->mutex); + wl->band = conf->channel->band; + ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; if (channel != wl->channel) { - u8 old_channel = wl->channel; + /* + * We assume that the stack will configure the right channel + * before associating, so we don't need to send a join + * command here. We will join the right channel when the + * BSSID changes + */ wl->channel = channel; - - /* FIXME: use beacon interval provided by mac80211 */ - ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); - if (ret < 0) { - wl->channel = old_channel; - goto out_sleep; - } } - ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) - goto out_sleep; - if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { wl1271_info("psm enabled"); @@ -768,7 +1164,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) if (conf->power_level != wl->power_level) { ret = wl1271_acx_tx_power(wl, conf->power_level); if (ret < 0) - goto out; + goto out_sleep; wl->power_level = conf->power_level; } @@ -782,6 +1178,45 @@ out: return ret; } +struct wl1271_filter_params { + bool enabled; + int mc_list_length; + u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN]; +}; + +static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mc_list) +{ + struct wl1271_filter_params *fp; + int i; + + fp = kzalloc(sizeof(*fp), GFP_ATOMIC); + if (!fp) { + wl1271_error("Out of memory setting filters."); + return 0; + } + + /* update multicast filtering parameters */ + fp->enabled = true; + if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) { + mc_count = 0; + fp->enabled = false; + } + + fp->mc_list_length = 0; + for (i = 0; i < mc_count; i++) { + if (mc_list->da_addrlen == ETH_ALEN) { + memcpy(fp->mc_list[fp->mc_list_length], + mc_list->da_addr, ETH_ALEN); + fp->mc_list_length++; + } else + wl1271_warning("Unknown mc address length."); + mc_list = mc_list->next; + } + + return (u64)(unsigned long)fp; +} + #define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_FCSFAIL | \ @@ -791,28 +1226,53 @@ out: static void wl1271_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total,u64 multicast) + unsigned int *total, u64 multicast) { + struct wl1271_filter_params *fp = (void *)(unsigned long)multicast; struct wl1271 *wl = hw->priv; + int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter"); + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + *total &= WL1271_SUPPORTED_FILTERS; changed &= WL1271_SUPPORTED_FILTERS; + if (*total & FIF_ALLMULTI) + ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0); + else if (fp) + ret = wl1271_acx_group_address_tbl(wl, fp->enabled, + fp->mc_list, + fp->mc_list_length); + if (ret < 0) + goto out_sleep; + + kfree(fp); + + /* FIXME: We still need to set our filters properly */ + + /* determine, whether supported filter values have changed */ if (changed == 0) - return; + goto out_sleep; - /* FIXME: wl->rx_config and wl->rx_filter are not protected */ - wl->rx_config = WL1271_DEFAULT_RX_CONFIG; - wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + /* apply configured filters */ + ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter); + if (ret < 0) + goto out_sleep; - /* - * FIXME: workqueues need to be properly cancelled on stop(), for - * now let's just disable changing the filter settings. They will - * be updated any on config(). - */ - /* schedule_work(&wl->filter_work); */ +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); } static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -823,6 +1283,8 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct wl1271 *wl = hw->priv; const u8 *addr; int ret; + u32 tx_seq_32 = 0; + u16 tx_seq_16 = 0; u8 key_type; static const u8 bcast_addr[ETH_ALEN] = @@ -861,11 +1323,15 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, key_type = KEY_TKIP; key_conf->hw_key_idx = key_conf->keyidx; + tx_seq_32 = wl->tx_security_seq_32; + tx_seq_16 = wl->tx_security_seq_16; break; case ALG_CCMP: key_type = KEY_AES; key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + tx_seq_32 = wl->tx_security_seq_32; + tx_seq_16 = wl->tx_security_seq_16; break; default: wl1271_error("Unknown key algo 0x%x", key_conf->alg); @@ -879,7 +1345,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, - addr); + addr, tx_seq_32, tx_seq_16); if (ret < 0) { wl1271_error("Could not add or replace key"); goto out_sleep; @@ -890,7 +1356,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = wl1271_cmd_set_key(wl, KEY_REMOVE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, - addr); + addr, 0, 0); if (ret < 0) { wl1271_error("Could not remove key"); goto out_sleep; @@ -921,13 +1387,13 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, struct wl1271 *wl = hw->priv; int ret; u8 *ssid = NULL; - size_t ssid_len = 0; + size_t len = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan"); if (req->n_ssids) { ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; + len = req->ssids[0].ssid_len; } mutex_lock(&wl->mutex); @@ -936,7 +1402,12 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, if (ret < 0) goto out; - ret = wl1271_cmd_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); + if (wl1271_11a_enabled()) + ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0, + WL1271_SCAN_BAND_DUAL, 3); + else + ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0, + WL1271_SCAN_BAND_2_4_GHZ, 3); wl1271_ps_elp_sleep(wl); @@ -969,6 +1440,22 @@ out: return ret; } +static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set) +{ + struct ieee80211_supported_band *band; + u32 enabled_rates = 0; + int bit; + + band = wl->hw->wiphy->bands[wl->band]; + for (bit = 0; bit < band->n_bitrates; bit++) { + if (basic_rate_set & 0x1) + enabled_rates |= band->bitrates[bit].hw_value; + basic_rate_set >>= 1; + } + + return enabled_rates; +} + static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -990,6 +1477,12 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (bss_conf->assoc) { wl->aid = bss_conf->aid; + /* + * with wl1271, we don't need to update the + * beacon_int and dtim_period, because the firmware + * updates it by itself when the first beacon is + * received after a join. + */ ret = wl1271_cmd_build_ps_poll(wl, wl->aid); if (ret < 0) goto out_sleep; @@ -1005,8 +1498,14 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; } + } else { + /* use defaults when not associated */ + wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET; + wl->aid = 0; } + } + if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT); @@ -1036,6 +1535,17 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, } } + if (changed & BSS_CHANGED_BASIC_RATES) { + wl->basic_rate_set = wl1271_enabled_rates_get( + wl, bss_conf->basic_rates); + + ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set); + if (ret < 0) { + wl1271_warning("Set rate policies failed %d", ret); + goto out_sleep; + } + } + out_sleep: wl1271_ps_elp_sleep(wl); @@ -1047,44 +1557,44 @@ out: /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1271_rates[] = { { .bitrate = 10, - .hw_value = 0x1, - .hw_value_short = 0x1, }, + .hw_value = CONF_HW_BIT_RATE_1MBPS, + .hw_value_short = CONF_HW_BIT_RATE_1MBPS, }, { .bitrate = 20, - .hw_value = 0x2, - .hw_value_short = 0x2, + .hw_value = CONF_HW_BIT_RATE_2MBPS, + .hw_value_short = CONF_HW_BIT_RATE_2MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, - .hw_value = 0x4, - .hw_value_short = 0x4, + .hw_value = CONF_HW_BIT_RATE_5_5MBPS, + .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, - .hw_value = 0x20, - .hw_value_short = 0x20, + .hw_value = CONF_HW_BIT_RATE_11MBPS, + .hw_value_short = CONF_HW_BIT_RATE_11MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, - .hw_value = 0x8, - .hw_value_short = 0x8, }, + .hw_value = CONF_HW_BIT_RATE_6MBPS, + .hw_value_short = CONF_HW_BIT_RATE_6MBPS, }, { .bitrate = 90, - .hw_value = 0x10, - .hw_value_short = 0x10, }, + .hw_value = CONF_HW_BIT_RATE_9MBPS, + .hw_value_short = CONF_HW_BIT_RATE_9MBPS, }, { .bitrate = 120, - .hw_value = 0x40, - .hw_value_short = 0x40, }, + .hw_value = CONF_HW_BIT_RATE_12MBPS, + .hw_value_short = CONF_HW_BIT_RATE_12MBPS, }, { .bitrate = 180, - .hw_value = 0x80, - .hw_value_short = 0x80, }, + .hw_value = CONF_HW_BIT_RATE_18MBPS, + .hw_value_short = CONF_HW_BIT_RATE_18MBPS, }, { .bitrate = 240, - .hw_value = 0x200, - .hw_value_short = 0x200, }, + .hw_value = CONF_HW_BIT_RATE_24MBPS, + .hw_value_short = CONF_HW_BIT_RATE_24MBPS, }, { .bitrate = 360, - .hw_value = 0x400, - .hw_value_short = 0x400, }, + .hw_value = CONF_HW_BIT_RATE_36MBPS, + .hw_value_short = CONF_HW_BIT_RATE_36MBPS, }, { .bitrate = 480, - .hw_value = 0x800, - .hw_value_short = 0x800, }, + .hw_value = CONF_HW_BIT_RATE_48MBPS, + .hw_value_short = CONF_HW_BIT_RATE_48MBPS, }, { .bitrate = 540, - .hw_value = 0x1000, - .hw_value_short = 0x1000, }, + .hw_value = CONF_HW_BIT_RATE_54MBPS, + .hw_value_short = CONF_HW_BIT_RATE_54MBPS, }, }; /* can't be const, mac80211 writes to this */ @@ -1112,6 +1622,88 @@ static struct ieee80211_supported_band wl1271_band_2ghz = { .n_bitrates = ARRAY_SIZE(wl1271_rates), }; +/* 5 GHz data rates for WL1273 */ +static struct ieee80211_rate wl1271_rates_5ghz[] = { + { .bitrate = 60, + .hw_value = CONF_HW_BIT_RATE_6MBPS, + .hw_value_short = CONF_HW_BIT_RATE_6MBPS, }, + { .bitrate = 90, + .hw_value = CONF_HW_BIT_RATE_9MBPS, + .hw_value_short = CONF_HW_BIT_RATE_9MBPS, }, + { .bitrate = 120, + .hw_value = CONF_HW_BIT_RATE_12MBPS, + .hw_value_short = CONF_HW_BIT_RATE_12MBPS, }, + { .bitrate = 180, + .hw_value = CONF_HW_BIT_RATE_18MBPS, + .hw_value_short = CONF_HW_BIT_RATE_18MBPS, }, + { .bitrate = 240, + .hw_value = CONF_HW_BIT_RATE_24MBPS, + .hw_value_short = CONF_HW_BIT_RATE_24MBPS, }, + { .bitrate = 360, + .hw_value = CONF_HW_BIT_RATE_36MBPS, + .hw_value_short = CONF_HW_BIT_RATE_36MBPS, }, + { .bitrate = 480, + .hw_value = CONF_HW_BIT_RATE_48MBPS, + .hw_value_short = CONF_HW_BIT_RATE_48MBPS, }, + { .bitrate = 540, + .hw_value = CONF_HW_BIT_RATE_54MBPS, + .hw_value_short = CONF_HW_BIT_RATE_54MBPS, }, +}; + +/* 5 GHz band channels for WL1273 */ +static struct ieee80211_channel wl1271_channels_5ghz[] = { + { .hw_value = 183, .center_freq = 4915}, + { .hw_value = 184, .center_freq = 4920}, + { .hw_value = 185, .center_freq = 4925}, + { .hw_value = 187, .center_freq = 4935}, + { .hw_value = 188, .center_freq = 4940}, + { .hw_value = 189, .center_freq = 4945}, + { .hw_value = 192, .center_freq = 4960}, + { .hw_value = 196, .center_freq = 4980}, + { .hw_value = 7, .center_freq = 5035}, + { .hw_value = 8, .center_freq = 5040}, + { .hw_value = 9, .center_freq = 5045}, + { .hw_value = 11, .center_freq = 5055}, + { .hw_value = 12, .center_freq = 5060}, + { .hw_value = 16, .center_freq = 5080}, + { .hw_value = 34, .center_freq = 5170}, + { .hw_value = 36, .center_freq = 5180}, + { .hw_value = 38, .center_freq = 5190}, + { .hw_value = 40, .center_freq = 5200}, + { .hw_value = 42, .center_freq = 5210}, + { .hw_value = 44, .center_freq = 5220}, + { .hw_value = 46, .center_freq = 5230}, + { .hw_value = 48, .center_freq = 5240}, + { .hw_value = 52, .center_freq = 5260}, + { .hw_value = 56, .center_freq = 5280}, + { .hw_value = 60, .center_freq = 5300}, + { .hw_value = 64, .center_freq = 5320}, + { .hw_value = 100, .center_freq = 5500}, + { .hw_value = 104, .center_freq = 5520}, + { .hw_value = 108, .center_freq = 5540}, + { .hw_value = 112, .center_freq = 5560}, + { .hw_value = 116, .center_freq = 5580}, + { .hw_value = 120, .center_freq = 5600}, + { .hw_value = 124, .center_freq = 5620}, + { .hw_value = 128, .center_freq = 5640}, + { .hw_value = 132, .center_freq = 5660}, + { .hw_value = 136, .center_freq = 5680}, + { .hw_value = 140, .center_freq = 5700}, + { .hw_value = 149, .center_freq = 5745}, + { .hw_value = 153, .center_freq = 5765}, + { .hw_value = 157, .center_freq = 5785}, + { .hw_value = 161, .center_freq = 5805}, + { .hw_value = 165, .center_freq = 5825}, +}; + + +static struct ieee80211_supported_band wl1271_band_5ghz = { + .channels = wl1271_channels_5ghz, + .n_channels = ARRAY_SIZE(wl1271_channels_5ghz), + .bitrates = wl1271_rates_5ghz, + .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz), +}; + static const struct ieee80211_ops wl1271_ops = { .start = wl1271_op_start, .stop = wl1271_op_stop, @@ -1119,6 +1711,7 @@ static const struct ieee80211_ops wl1271_ops = { .remove_interface = wl1271_op_remove_interface, .config = wl1271_op_config, /* .config_interface = wl1271_op_config_interface, */ + .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, .set_key = wl1271_op_set_key, @@ -1151,24 +1744,26 @@ static int wl1271_register_hw(struct wl1271 *wl) static int wl1271_init_ieee80211(struct wl1271 *wl) { - /* - * The tx descriptor buffer and the TKIP space. - * - * FIXME: add correct 1271 descriptor size - */ - wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE; + /* The tx descriptor buffer and the TKIP space. */ + wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE + + sizeof(struct wl1271_tx_hw_descr); /* unit us */ /* FIXME: find a proper value */ wl->hw->channel_change_time = 10000; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_NOISE_DBM; + IEEE80211_HW_NOISE_DBM | + IEEE80211_HW_BEACON_FILTER | + IEEE80211_HW_SUPPORTS_PS; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz; + if (wl1271_11a_enabled()) + wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; + SET_IEEE80211_DEV(wl->hw, &wl->spi->dev); return 0; @@ -1213,29 +1808,33 @@ static int __devinit wl1271_probe(struct spi_device *spi) wl = hw->priv; memset(wl, 0, sizeof(*wl)); + INIT_LIST_HEAD(&wl->list); + wl->hw = hw; dev_set_drvdata(&spi->dev, wl); wl->spi = spi; skb_queue_head_init(&wl->tx_queue); - INIT_WORK(&wl->filter_work, wl1271_filter_work); + INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); wl->channel = WL1271_DEFAULT_CHANNEL; wl->scanning = false; wl->default_key = 0; - wl->listen_int = 1; wl->rx_counter = 0; wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl->rx_filter = WL1271_DEFAULT_RX_FILTER; wl->elp = false; wl->psm = 0; wl->psm_requested = false; + wl->psm_entry_retry = 0; wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; + wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET; + wl->band = IEEE80211_BAND_2GHZ; + wl->vif = NULL; + wl->joined = false; - /* We use the default power on sleep time until we know which chip - * we're using */ - for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + for (i = 0; i < ACX_TX_DESCRIPTORS; i++) wl->tx_frames[i] = NULL; spin_lock_init(&wl->wl_lock); @@ -1250,13 +1849,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) wl->state = WL1271_STATE_OFF; mutex_init(&wl->mutex); - wl->rx_descriptor = kmalloc(sizeof(*wl->rx_descriptor), GFP_KERNEL); - if (!wl->rx_descriptor) { - wl1271_error("could not allocate memory for rx descriptor"); - ret = -ENOMEM; - goto out_free; - } - /* This is the only SPI value that we need to set here, the rest * comes from the board-peripherals file */ spi->bits_per_word = 32; @@ -1298,6 +1890,9 @@ static int __devinit wl1271_probe(struct spi_device *spi) } dev_set_drvdata(&wl1271_device.dev, wl); + /* Apply default driver configuration. */ + wl1271_conf_init(wl); + ret = wl1271_init_ieee80211(wl); if (ret) goto out_platform; @@ -1319,9 +1914,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) free_irq(wl->irq, wl); out_free: - kfree(wl->rx_descriptor); - wl->rx_descriptor = NULL; - ieee80211_free_hw(hw); return ret; @@ -1337,14 +1929,11 @@ static int __devexit wl1271_remove(struct spi_device *spi) platform_device_unregister(&wl1271_device); free_irq(wl->irq, wl); kfree(wl->target_mem_map); - kfree(wl->fw); + vfree(wl->fw); wl->fw = NULL; kfree(wl->nvs); wl->nvs = NULL; - kfree(wl->rx_descriptor); - wl->rx_descriptor = NULL; - kfree(wl->fw_status); kfree(wl->tx_res_if); @@ -1391,3 +1980,5 @@ module_exit(wl1271_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); +MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); +MODULE_FIRMWARE(WL1271_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c index 1dc74b0c7736..507cd91d7eed 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.c +++ b/drivers/net/wireless/wl12xx/wl1271_ps.c @@ -27,25 +27,38 @@ #define WL1271_WAKEUP_TIMEOUT 500 +void wl1271_elp_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1271, elp_work); + + wl1271_debug(DEBUG_PSM, "elp work"); + + mutex_lock(&wl->mutex); + + if (wl->elp || !wl->psm) + goto out; + + wl1271_debug(DEBUG_PSM, "chip to elp"); + wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); + wl->elp = true; + +out: + mutex_unlock(&wl->mutex); +} + +#define ELP_ENTRY_DELAY 5 + /* Routines to toggle sleep mode while in ELP */ void wl1271_ps_elp_sleep(struct wl1271 *wl) { - /* - * FIXME: due to a problem in the firmware (causing a firmware - * crash), ELP entry is prevented below. Remove the "true" to - * re-enable ELP entry. - */ - if (true || wl->elp || !wl->psm) - return; - - /* - * Go to ELP unless there is work already pending - pending work - * will immediately wakeup the chipset anyway. - */ - if (!work_pending(&wl->irq_work) && !work_pending(&wl->tx_work)) { - wl1271_debug(DEBUG_PSM, "chip to elp"); - wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); - wl->elp = true; + if (wl->psm) { + cancel_delayed_work(&wl->elp_work); + ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, + msecs_to_jiffies(ELP_ENTRY_DELAY)); } } @@ -73,7 +86,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) wl->elp_compl = &compl; spin_unlock_irqrestore(&wl->wl_lock, flags); - wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); + wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); if (!pending) { ret = wait_for_completion_timeout( @@ -111,6 +124,17 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) switch (mode) { case STATION_POWER_SAVE_MODE: wl1271_debug(DEBUG_PSM, "entering psm"); + + /* enable beacon filtering */ + ret = wl1271_acx_beacon_filter_opt(wl, true); + if (ret < 0) + return ret; + + /* enable beacon early termination */ + ret = wl1271_acx_bet_enable(wl, true); + if (ret < 0) + return ret; + ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); if (ret < 0) return ret; @@ -128,6 +152,16 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) if (ret < 0) return ret; + /* disable beacon early termination */ + ret = wl1271_acx_bet_enable(wl, false); + if (ret < 0) + return ret; + + /* disable beacon filtering */ + ret = wl1271_acx_beacon_filter_opt(wl, false); + if (ret < 0) + return ret; + ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.h b/drivers/net/wireless/wl12xx/wl1271_ps.h index de2bd3c7dc9c..779653d0ae85 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.h +++ b/drivers/net/wireless/wl12xx/wl1271_ps.h @@ -30,6 +30,6 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode); void wl1271_ps_elp_sleep(struct wl1271 *wl); int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); - +void wl1271_elp_work(struct work_struct *work); #endif /* __WL1271_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_reg.h b/drivers/net/wireless/wl12xx/wl1271_reg.h index f8ed4a4fc691..1f237389d1c7 100644 --- a/drivers/net/wireless/wl12xx/wl1271_reg.h +++ b/drivers/net/wireless/wl12xx/wl1271_reg.h @@ -34,7 +34,7 @@ #define REGISTERS_WORK_SIZE 0x0000b000 #define HW_ACCESS_ELP_CTRL_REG_ADDR 0x1FFFC -#define STATUS_MEM_ADDRESS 0x40400 +#define FW_STATUS_ADDR (0x14FC0 + 0xA000) /* ELP register commands */ #define ELPCTRL_WAKE_UP 0x1 @@ -213,7 +213,6 @@ ==============================================*/ #define ACX_REG_INTERRUPT_ACK (REGISTERS_BASE + 0x04F0) -#define RX_DRIVER_DUMMY_WRITE_ADDRESS (REGISTERS_BASE + 0x0534) #define RX_DRIVER_COUNTER_ADDRESS (REGISTERS_BASE + 0x0538) /* Device Configuration registers*/ @@ -614,50 +613,6 @@ enum { MAX_RADIO_BANDS = 0xFF }; -enum { - NO_RATE = 0, - RATE_1MBPS = 0x0A, - RATE_2MBPS = 0x14, - RATE_5_5MBPS = 0x37, - RATE_6MBPS = 0x0B, - RATE_9MBPS = 0x0F, - RATE_11MBPS = 0x6E, - RATE_12MBPS = 0x0A, - RATE_18MBPS = 0x0E, - RATE_22MBPS = 0xDC, - RATE_24MBPS = 0x09, - RATE_36MBPS = 0x0D, - RATE_48MBPS = 0x08, - RATE_54MBPS = 0x0C -}; - -enum { - RATE_INDEX_1MBPS = 0, - RATE_INDEX_2MBPS = 1, - RATE_INDEX_5_5MBPS = 2, - RATE_INDEX_6MBPS = 3, - RATE_INDEX_9MBPS = 4, - RATE_INDEX_11MBPS = 5, - RATE_INDEX_12MBPS = 6, - RATE_INDEX_18MBPS = 7, - RATE_INDEX_22MBPS = 8, - RATE_INDEX_24MBPS = 9, - RATE_INDEX_36MBPS = 10, - RATE_INDEX_48MBPS = 11, - RATE_INDEX_54MBPS = 12, - RATE_INDEX_MAX = RATE_INDEX_54MBPS, - MAX_RATE_INDEX, - INVALID_RATE_INDEX = MAX_RATE_INDEX, - RATE_INDEX_ENUM_MAX_SIZE = 0x7FFFFFFF -}; - -enum { - RATE_MASK_1MBPS = 0x1, - RATE_MASK_2MBPS = 0x2, - RATE_MASK_5_5MBPS = 0x4, - RATE_MASK_11MBPS = 0x20, -}; - #define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */ #define OFDM_RATE_BIT BIT(6) #define PBCC_RATE_BIT BIT(7) diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c index ad8b6904c5eb..ca645f38109b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_rx.c +++ b/drivers/net/wireless/wl12xx/wl1271_rx.c @@ -30,14 +30,15 @@ static u8 wl1271_rx_get_mem_block(struct wl1271_fw_status *status, u32 drv_rx_counter) { - return status->rx_pkt_descs[drv_rx_counter] & RX_MEM_BLOCK_MASK; + return le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) & + RX_MEM_BLOCK_MASK; } static u32 wl1271_rx_get_buf_size(struct wl1271_fw_status *status, u32 drv_rx_counter) { - return (status->rx_pkt_descs[drv_rx_counter] & RX_BUF_SIZE_MASK) >> - RX_BUF_SIZE_SHIFT_DIV; + return (le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) & + RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV; } /* The values of this table must match the wl1271_rates[] array */ @@ -70,6 +71,36 @@ static u8 wl1271_rx_rate_to_idx[] = { 0 /* WL1271_RATE_1 */ }; +/* The values of this table must match the wl1271_rates[] array */ +static u8 wl1271_5_ghz_rx_rate_to_idx[] = { + /* MCS rates are used only with 11n */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS7 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS6 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS5 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS4 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS3 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS2 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS1 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS0 */ + + 7, /* WL1271_RATE_54 */ + 6, /* WL1271_RATE_48 */ + 5, /* WL1271_RATE_36 */ + 4, /* WL1271_RATE_24 */ + + /* TI-specific rate */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_22 */ + + 3, /* WL1271_RATE_18 */ + 2, /* WL1271_RATE_12 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_11 */ + 1, /* WL1271_RATE_9 */ + 0, /* WL1271_RATE_6 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_5_5 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_2 */ + WL1271_RX_RATE_UNSUPPORTED /* WL1271_RATE_1 */ +}; + static void wl1271_rx_status(struct wl1271 *wl, struct wl1271_rx_descriptor *desc, struct ieee80211_rx_status *status, @@ -77,12 +108,21 @@ static void wl1271_rx_status(struct wl1271 *wl, { memset(status, 0, sizeof(struct ieee80211_rx_status)); - if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG) + if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == + WL1271_RX_DESC_BAND_BG) { status->band = IEEE80211_BAND_2GHZ; - else + status->rate_idx = wl1271_rx_rate_to_idx[desc->rate]; + } else if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == + WL1271_RX_DESC_BAND_A) { + status->band = IEEE80211_BAND_5GHZ; + status->rate_idx = wl1271_5_ghz_rx_rate_to_idx[desc->rate]; + } else wl1271_warning("unsupported band 0x%x", desc->flags & WL1271_RX_DESC_BAND_MASK); + if (unlikely(status->rate_idx == WL1271_RX_RATE_UNSUPPORTED)) + wl1271_warning("unsupported rate"); + /* * FIXME: Add mactime handling. For IBSS (ad-hoc) we need to get the * timestamp from the beacon (acx_tsf_info). In BSS mode (infra) we @@ -91,12 +131,6 @@ static void wl1271_rx_status(struct wl1271 *wl, */ status->signal = desc->rssi; - /* FIXME: Should this be optimized? */ - status->qual = (desc->rssi - WL1271_RX_MIN_RSSI) * 100 / - (WL1271_RX_MAX_RSSI - WL1271_RX_MIN_RSSI); - status->qual = min(status->qual, 100); - status->qual = max(status->qual, 0); - /* * FIXME: In wl1251, the SNR should be divided by two. In wl1271 we * need to divide by two for now, but TI has been discussing about @@ -109,17 +143,11 @@ static void wl1271_rx_status(struct wl1271 *wl, if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; - if (likely(!(desc->flags & WL1271_RX_DESC_DECRYPT_FAIL))) + if (likely(!(desc->status & WL1271_RX_DESC_DECRYPT_FAIL))) status->flag |= RX_FLAG_DECRYPTED; - - if (unlikely(desc->flags & WL1271_RX_DESC_MIC_FAIL)) + if (unlikely(desc->status & WL1271_RX_DESC_MIC_FAIL)) status->flag |= RX_FLAG_MMIC_ERROR; } - - status->rate_idx = wl1271_rx_rate_to_idx[desc->rate]; - - if (status->rate_idx == WL1271_RX_RATE_UNSUPPORTED) - wl1271_warning("unsupported rate"); } static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) @@ -131,14 +159,14 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) u8 *buf; u8 beacon = 0; - skb = dev_alloc_skb(length); + skb = __dev_alloc_skb(length, GFP_KERNEL); if (!skb) { wl1271_error("Couldn't allocate RX frame"); return; } buf = skb_put(skb, length); - wl1271_spi_reg_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); + wl1271_spi_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) buf; @@ -156,7 +184,7 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) beacon ? "beacon" : ""); memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - ieee80211_rx(wl->hw, skb); + ieee80211_rx_ni(wl->hw, skb); } void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) @@ -176,15 +204,15 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) break; } - wl->rx_mem_pool_addr.addr = - (mem_block << 8) + wl_mem_map->packet_memory_pool_start; + wl->rx_mem_pool_addr.addr = (mem_block << 8) + + le32_to_cpu(wl_mem_map->packet_memory_pool_start); wl->rx_mem_pool_addr.addr_extra = wl->rx_mem_pool_addr.addr + 4; /* Choose the block we want to read */ - wl1271_spi_reg_write(wl, WL1271_SLV_REG_DATA, - &wl->rx_mem_pool_addr, - sizeof(wl->rx_mem_pool_addr), false); + wl1271_spi_write(wl, WL1271_SLV_REG_DATA, + &wl->rx_mem_pool_addr, + sizeof(wl->rx_mem_pool_addr), false); wl1271_rx_handle_data(wl, buf_size); @@ -192,9 +220,5 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; } - wl1271_reg_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); - - /* This is a workaround for some problems in the chip */ - wl1271_reg_write32(wl, RX_DRIVER_DUMMY_WRITE_ADDRESS, 0x1); - + wl1271_spi_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.h b/drivers/net/wireless/wl12xx/wl1271_rx.h index d1ca60e43a25..1ae6d1783ed4 100644 --- a/drivers/net/wireless/wl12xx/wl1271_rx.h +++ b/drivers/net/wireless/wl12xx/wl1271_rx.h @@ -102,14 +102,14 @@ #define RX_BUF_SIZE_SHIFT_DIV 6 struct wl1271_rx_descriptor { - u16 length; + __le16 length; u8 status; u8 flags; u8 rate; u8 channel; s8 rssi; u8 snr; - u32 timestamp; + __le32 timestamp; u8 packet_class; u8 process_id; u8 pad_len; diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 4a12880c16a8..02978a16e732 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -30,17 +30,29 @@ #include "wl12xx_80211.h" #include "wl1271_spi.h" -static int wl1271_translate_reg_addr(struct wl1271 *wl, int addr) +static int wl1271_translate_addr(struct wl1271 *wl, int addr) { - return addr - wl->physical_reg_addr + wl->virtual_reg_addr; -} - -static int wl1271_translate_mem_addr(struct wl1271 *wl, int addr) -{ - return addr - wl->physical_mem_addr + wl->virtual_mem_addr; + /* + * To translate, first check to which window of addresses the + * particular address belongs. Then subtract the starting address + * of that window from the address. Then, add offset of the + * translated region. + * + * The translated regions occur next to each other in physical device + * memory, so just add the sizes of the preceeding address regions to + * get the offset to the new region. + * + * Currently, only the two first regions are addressed, and the + * assumption is that all addresses will fall into either of those + * two. + */ + if ((addr >= wl->part.reg.start) && + (addr < wl->part.reg.start + wl->part.reg.size)) + return addr - wl->part.reg.start + wl->part.mem.size; + else + return addr - wl->part.mem.start; } - void wl1271_spi_reset(struct wl1271 *wl) { u8 *cmd; @@ -123,133 +135,137 @@ void wl1271_spi_init(struct wl1271 *wl) /* Set the SPI partitions to access the chip addresses * - * There are two VIRTUAL (SPI) partitions (the memory partition and the - * registers partition), which are mapped to two different areas of the - * PHYSICAL (hardware) memory. This function also makes other checks to - * ensure that the partitions are not overlapping. In the diagram below, the - * memory partition comes before the register partition, but the opposite is - * also supported. + * To simplify driver code, a fixed (virtual) memory map is defined for + * register and memory addresses. Because in the chipset, in different stages + * of operation, those addresses will move around, an address translation + * mechanism is required. * - * PHYSICAL address + * There are four partitions (three memory and one register partition), + * which are mapped to two different areas of the hardware memory. + * + * Virtual address * space * * | | - * ...+----+--> mem_start - * VIRTUAL address ... | | + * ...+----+--> mem.start + * Physical address ... | | * space ... | | [PART_0] * ... | | - * 0x00000000 <--+----+... ...+----+--> mem_start + mem_size + * 00000000 <--+----+... ...+----+--> mem.start + mem.size * | | ... | | * |MEM | ... | | * | | ... | | - * part_size <--+----+... | | {unused area) + * mem.size <--+----+... | | {unused area) * | | ... | | * |REG | ... | | - * part_size | | ... | | - * + <--+----+... ...+----+--> reg_start - * reg_size ... | | - * ... | | [PART_1] - * ... | | - * ...+----+--> reg_start + reg_size + * mem.size | | ... | | + * + <--+----+... ...+----+--> reg.start + * reg.size | | ... | | + * |MEM2| ... | | [PART_1] + * | | ... | | + * ...+----+--> reg.start + reg.size * | | * */ int wl1271_set_partition(struct wl1271 *wl, - u32 mem_start, u32 mem_size, - u32 reg_start, u32 reg_size) + struct wl1271_partition_set *p) { - struct wl1271_partition *partition; - struct spi_transfer t; - struct spi_message m; - size_t len, cmd_len; - u32 *cmd; - int addr; - - cmd_len = sizeof(u32) + 2 * sizeof(struct wl1271_partition); - cmd = kzalloc(cmd_len, GFP_KERNEL); - if (!cmd) - return -ENOMEM; - - spi_message_init(&m); - memset(&t, 0, sizeof(t)); - - partition = (struct wl1271_partition *) (cmd + 1); - addr = HW_ACCESS_PART0_SIZE_ADDR; - len = 2 * sizeof(struct wl1271_partition); - - *cmd |= WSPI_CMD_WRITE; - *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; - *cmd |= addr & WSPI_CMD_BYTE_ADDR; + /* copy partition info */ + memcpy(&wl->part, p, sizeof(*p)); wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - mem_start, mem_size); + p->mem.start, p->mem.size); wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - reg_start, reg_size); - - /* Make sure that the two partitions together don't exceed the - * address range */ - if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) { - wl1271_debug(DEBUG_SPI, "Total size exceeds maximum virtual" - " address range. Truncating partition[0]."); - mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size; - wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - mem_start, mem_size); - wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - reg_start, reg_size); - } + p->reg.start, p->reg.size); + wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X", + p->mem2.start, p->mem2.size); + wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X", + p->mem3.start, p->mem3.size); + + /* write partition info to the chipset */ + wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); + wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); + wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); + wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); + wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); + wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); + wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); - if ((mem_start < reg_start) && - ((mem_start + mem_size) > reg_start)) { - /* Guarantee that the memory partition doesn't overlap the - * registers partition */ - wl1271_debug(DEBUG_SPI, "End of partition[0] is " - "overlapping partition[1]. Adjusted."); - mem_size = reg_start - mem_start; - wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - mem_start, mem_size); - wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - reg_start, reg_size); - } else if ((reg_start < mem_start) && - ((reg_start + reg_size) > mem_start)) { - /* Guarantee that the register partition doesn't overlap the - * memory partition */ - wl1271_debug(DEBUG_SPI, "End of partition[1] is" - " overlapping partition[0]. Adjusted."); - reg_size = mem_start - reg_start; - wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - mem_start, mem_size); - wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - reg_start, reg_size); - } + return 0; +} - partition[0].start = mem_start; - partition[0].size = mem_size; - partition[1].start = reg_start; - partition[1].size = reg_size; +#define WL1271_BUSY_WORD_TIMEOUT 1000 - wl->physical_mem_addr = mem_start; - wl->physical_reg_addr = reg_start; +/* FIXME: Check busy words, removed due to SPI bug */ +#if 0 +static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len) +{ + struct spi_transfer t[1]; + struct spi_message m; + u32 *busy_buf; + int num_busy_bytes = 0; - wl->virtual_mem_addr = 0; - wl->virtual_reg_addr = mem_size; + wl1271_info("spi read BUSY!"); - t.tx_buf = cmd; - t.len = cmd_len; - spi_message_add_tail(&t, &m); + /* + * Look for the non-busy word in the read buffer, and if found, + * read in the remaining data into the buffer. + */ + busy_buf = (u32 *)buf; + for (; (u32)busy_buf < (u32)buf + len; busy_buf++) { + num_busy_bytes += sizeof(u32); + if (*busy_buf & 0x1) { + spi_message_init(&m); + memset(t, 0, sizeof(t)); + memmove(buf, busy_buf, len - num_busy_bytes); + t[0].rx_buf = buf + (len - num_busy_bytes); + t[0].len = num_busy_bytes; + spi_message_add_tail(&t[0], &m); + spi_sync(wl->spi, &m); + return; + } + } - spi_sync(wl->spi, &m); + /* + * Read further busy words from SPI until a non-busy word is + * encountered, then read the data itself into the buffer. + */ + wl1271_info("spi read BUSY-polling needed!"); - kfree(cmd); + num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT; + busy_buf = wl->buffer_busyword; + while (num_busy_bytes) { + num_busy_bytes--; + spi_message_init(&m); + memset(t, 0, sizeof(t)); + t[0].rx_buf = busy_buf; + t[0].len = sizeof(u32); + spi_message_add_tail(&t[0], &m); + spi_sync(wl->spi, &m); + + if (*busy_buf & 0x1) { + spi_message_init(&m); + memset(t, 0, sizeof(t)); + t[0].rx_buf = buf; + t[0].len = len; + spi_message_add_tail(&t[0], &m); + spi_sync(wl->spi, &m); + return; + } + } - return 0; + /* The SPI bus is unresponsive, the read failed. */ + memset(buf, 0, len); + wl1271_error("SPI read busy-word timeout!\n"); } +#endif -void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { struct spi_transfer t[3]; struct spi_message m; - u8 *busy_buf; + u32 *busy_buf; u32 *cmd; cmd = &wl->buffer_cmd; @@ -281,14 +297,16 @@ void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, spi_sync(wl->spi, &m); - /* FIXME: check busy words */ + /* FIXME: Check busy words, removed due to SPI bug */ + /* if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1)) + wl1271_spi_read_busy(wl, buf, len); */ wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); } -void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { struct spi_transfer t[2]; struct spi_message m; @@ -321,62 +339,77 @@ void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); } -void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf, - size_t len) +void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) { int physical; - physical = wl1271_translate_mem_addr(wl, addr); + physical = wl1271_translate_addr(wl, addr); - wl1271_spi_read(wl, physical, buf, len, false); + wl1271_spi_raw_read(wl, physical, buf, len, fixed); } -void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf, - size_t len) +void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) { int physical; - physical = wl1271_translate_mem_addr(wl, addr); + physical = wl1271_translate_addr(wl, addr); - wl1271_spi_write(wl, physical, buf, len, false); + wl1271_spi_raw_write(wl, physical, buf, len, fixed); } -void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed) +u32 wl1271_spi_read32(struct wl1271 *wl, int addr) { - int physical; - - physical = wl1271_translate_reg_addr(wl, addr); + return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); +} - wl1271_spi_read(wl, physical, buf, len, fixed); +void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val) +{ + wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val); } -void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed) +void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) { - int physical; + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ + addr = (addr >> 1) + 0x30000; + wl1271_spi_write32(wl, OCP_POR_CTR, addr); - physical = wl1271_translate_reg_addr(wl, addr); + /* write value to OCP_POR_WDATA */ + wl1271_spi_write32(wl, OCP_DATA_WRITE, val); - wl1271_spi_write(wl, physical, buf, len, fixed); + /* write 1 to OCP_CMD */ + wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_WRITE); } -u32 wl1271_mem_read32(struct wl1271 *wl, int addr) +u16 wl1271_top_reg_read(struct wl1271 *wl, int addr) { - return wl1271_read32(wl, wl1271_translate_mem_addr(wl, addr)); -} + u32 val; + int timeout = OCP_CMD_LOOP; -void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val) -{ - wl1271_write32(wl, wl1271_translate_mem_addr(wl, addr), val); -} + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ + addr = (addr >> 1) + 0x30000; + wl1271_spi_write32(wl, OCP_POR_CTR, addr); -u32 wl1271_reg_read32(struct wl1271 *wl, int addr) -{ - return wl1271_read32(wl, wl1271_translate_reg_addr(wl, addr)); -} + /* write 2 to OCP_CMD */ + wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_READ); -void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val) -{ - wl1271_write32(wl, wl1271_translate_reg_addr(wl, addr), val); + /* poll for data ready */ + do { + val = wl1271_spi_read32(wl, OCP_DATA_READ); + timeout--; + } while (!(val & OCP_READY_MASK) && timeout); + + if (!timeout) { + wl1271_warning("Top register access timed out."); + return 0xffff; + } + + /* check data status and return if OK */ + if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) + return val & 0xffff; + else { + wl1271_warning("Top register access returned error."); + return 0xffff; + } } diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.h b/drivers/net/wireless/wl12xx/wl1271_spi.h index 2c9968458646..cb7df1c56314 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.h +++ b/drivers/net/wireless/wl12xx/wl1271_spi.h @@ -29,10 +29,14 @@ #define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0 -#define HW_ACCESS_PART0_SIZE_ADDR 0x1FFC0 -#define HW_ACCESS_PART0_START_ADDR 0x1FFC4 -#define HW_ACCESS_PART1_SIZE_ADDR 0x1FFC8 -#define HW_ACCESS_PART1_START_ADDR 0x1FFCC +#define HW_PARTITION_REGISTERS_ADDR 0x1ffc0 +#define HW_PART0_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR) +#define HW_PART0_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 4) +#define HW_PART1_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 8) +#define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12) +#define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16) +#define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20) +#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24) #define HW_ACCESS_REGISTER_SIZE 4 @@ -67,47 +71,56 @@ ((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32)) #define HW_ACCESS_WSPI_INIT_CMD_MASK 0 +#define OCP_CMD_LOOP 32 + +#define OCP_CMD_WRITE 0x1 +#define OCP_CMD_READ 0x2 + +#define OCP_READY_MASK BIT(18) +#define OCP_STATUS_MASK (BIT(16) | BIT(17)) + +#define OCP_STATUS_NO_RESP 0x00000 +#define OCP_STATUS_OK 0x10000 +#define OCP_STATUS_REQ_FAILED 0x20000 +#define OCP_STATUS_RESP_ERROR 0x30000 /* Raw target IO, address is not translated */ -void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, +void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed); -void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, +void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed); -/* Memory target IO, address is tranlated to partition 0 */ -void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf, size_t len); -void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf, size_t len); -u32 wl1271_mem_read32(struct wl1271 *wl, int addr); -void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val); +/* Translated target IO */ +void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +u32 wl1271_spi_read32(struct wl1271 *wl, int addr); +void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val); -/* Registers IO */ -void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed); -void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed); -u32 wl1271_reg_read32(struct wl1271 *wl, int addr); -void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val); +/* Top Register IO */ +void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); +u16 wl1271_top_reg_read(struct wl1271 *wl, int addr); /* INIT and RESET words */ void wl1271_spi_reset(struct wl1271 *wl); void wl1271_spi_init(struct wl1271 *wl); int wl1271_set_partition(struct wl1271 *wl, - u32 part_start, u32 part_size, - u32 reg_start, u32 reg_size); + struct wl1271_partition_set *p); -static inline u32 wl1271_read32(struct wl1271 *wl, int addr) +static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) { - wl1271_spi_read(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + wl1271_spi_raw_read(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); return wl->buffer_32; } -static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val) +static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) { wl->buffer_32 = val; - wl1271_spi_write(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + wl1271_spi_raw_write(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); } #endif /* __WL1271_SPI_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c index ff221258b941..00af065c77c2 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.c +++ b/drivers/net/wireless/wl12xx/wl1271_tx.c @@ -33,8 +33,7 @@ static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb) { int i; - - for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + for (i = 0; i < ACX_TX_DESCRIPTORS; i++) if (wl->tx_frames[i] == NULL) { wl->tx_frames[i] = skb; return i; @@ -58,8 +57,8 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra) /* approximate the number of blocks required for this packet in the firmware */ /* FIXME: try to figure out what is done here and make it cleaner */ - total_blocks = (skb->len) >> TX_HW_BLOCK_SHIFT_DIV; - excluded = (total_blocks << 2) + (skb->len & 0xff) + 34; + total_blocks = (total_len + 20) >> TX_HW_BLOCK_SHIFT_DIV; + excluded = (total_blocks << 2) + ((total_len + 20) & 0xff) + 34; total_blocks += (excluded > 252) ? 2 : 1; total_blocks += TX_HW_BLOCK_SPARE; @@ -89,15 +88,25 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, { struct wl1271_tx_hw_descr *desc; int pad; + u16 tx_attr; desc = (struct wl1271_tx_hw_descr *) skb->data; + /* relocate space for security header */ + if (extra) { + void *framestart = skb->data + sizeof(*desc); + u16 fc = *(u16 *)(framestart + extra); + int hdrlen = ieee80211_hdrlen(cpu_to_le16(fc)); + memmove(framestart, framestart + extra, hdrlen); + } + /* configure packet life time */ - desc->start_time = jiffies_to_usecs(jiffies) - wl->time_offset; - desc->life_time = TX_HW_MGMT_PKT_LIFETIME_TU; + desc->start_time = cpu_to_le32(jiffies_to_usecs(jiffies) - + wl->time_offset); + desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU); /* configure the tx attributes */ - desc->tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; + tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; /* FIXME: do we know the packet priority? can we identify mgmt packets, and use max prio for them at least? */ desc->tid = 0; @@ -106,11 +115,13 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, /* align the length (and store in terms of words) */ pad = WL1271_TX_ALIGN(skb->len); - desc->length = pad >> 2; + desc->length = cpu_to_le16(pad >> 2); /* calculate number of padding bytes */ pad = pad - skb->len; - desc->tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; + tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; + + desc->tx_attr = cpu_to_le16(tx_attr); wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad); return 0; @@ -147,11 +158,11 @@ static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb, len = WL1271_TX_ALIGN(skb->len); /* perform a fixed address block write with the packet */ - wl1271_spi_reg_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); + wl1271_spi_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); /* write packet new counter into the write access register */ wl->tx_packets_count++; - wl1271_reg_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); + wl1271_spi_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); desc = (struct wl1271_tx_hw_descr *) skb->data; wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", @@ -254,14 +265,13 @@ out: static void wl1271_tx_complete_packet(struct wl1271 *wl, struct wl1271_tx_hw_res_descr *result) { - struct ieee80211_tx_info *info; struct sk_buff *skb; - u32 header_len; + u16 seq; int id = result->id; /* check for id legality */ - if (id >= TX_HW_RESULT_QUEUE_LEN || wl->tx_frames[id] == NULL) { + if (id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL) { wl1271_warning("TX result illegal id: %d", id); return; } @@ -284,22 +294,32 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, /* info->status.retry_count = result->ack_failures; */ wl->stats.retry_count += result->ack_failures; - /* get header len */ + /* update security sequence number */ + seq = wl->tx_security_seq_16 + + (result->lsb_security_sequence_number - + wl->tx_security_last_seq); + wl->tx_security_last_seq = result->lsb_security_sequence_number; + + if (seq < wl->tx_security_seq_16) + wl->tx_security_seq_32++; + wl->tx_security_seq_16 = seq; + + /* remove private header from packet */ + skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); + + /* remove TKIP header space if present */ if (info->control.hw_key && - info->control.hw_key->alg == ALG_TKIP) - header_len = WL1271_TKIP_IV_SPACE + - sizeof(struct wl1271_tx_hw_descr); - else - header_len = sizeof(struct wl1271_tx_hw_descr); + info->control.hw_key->alg == ALG_TKIP) { + int hdrlen = ieee80211_get_hdrlen_from_skb(skb); + memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen); + skb_pull(skb, WL1271_TKIP_IV_SPACE); + } wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" " status 0x%x", result->id, skb, result->ack_failures, result->rate_class_index, result->status); - /* remove private header from packet */ - skb_pull(skb, header_len); - /* return the packet to the stack */ ieee80211_tx_status(wl->hw, skb); wl->tx_frames[result->id] = NULL; @@ -315,8 +335,8 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); /* read the tx results from the chipset */ - wl1271_spi_mem_read(wl, memmap->tx_result, - wl->tx_res_if, sizeof(*wl->tx_res_if)); + wl1271_spi_read(wl, le32_to_cpu(memmap->tx_result), + wl->tx_res_if, sizeof(*wl->tx_res_if), false); /* verify that the result buffer is not getting overrun */ if (count > TX_HW_RESULT_QUEUE_LEN) { @@ -337,10 +357,10 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) } /* write host counter to chipset (to ack) */ - wl1271_mem_write32(wl, memmap->tx_result + + wl1271_spi_write32(wl, le32_to_cpu(memmap->tx_result) + offsetof(struct wl1271_tx_hw_res_if, tx_result_host_counter), - wl->tx_res_if->tx_result_fw_counter); + le32_to_cpu(wl->tx_res_if->tx_result_fw_counter)); } /* caller must hold wl->mutex */ @@ -364,7 +384,7 @@ void wl1271_tx_flush(struct wl1271 *wl) ieee80211_tx_status(wl->hw, skb); } - for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + for (i = 0; i < ACX_TX_DESCRIPTORS; i++) if (wl->tx_frames[i] != NULL) { skb = wl->tx_frames[i]; info = IEEE80211_SKB_CB(skb); diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h index 4a614067ddba..416396caf0a0 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.h +++ b/drivers/net/wireless/wl12xx/wl1271_tx.h @@ -58,7 +58,7 @@ struct wl1271_tx_hw_descr { /* Length of packet in words, including descriptor+header+data */ - u16 length; + __le16 length; /* Number of extra memory blocks to allocate for this packet in addition to the number of blocks derived from the packet length */ u8 extra_mem_blocks; @@ -67,12 +67,12 @@ struct wl1271_tx_hw_descr { HW!! */ u8 total_mem_blocks; /* Device time (in us) when the packet arrived to the driver */ - u32 start_time; + __le32 start_time; /* Max delay in TUs until transmission. The last device time the packet can be transmitted is: startTime+(1024*LifeTime) */ - u16 life_time; + __le16 life_time; /* Bitwise fields - see TX_ATTR... definitions above. */ - u16 tx_attr; + __le16 tx_attr; /* Packet identifier used also in the Tx-Result. */ u8 id; /* The packet TID value (as User-Priority) */ @@ -100,12 +100,12 @@ struct wl1271_tx_hw_res_descr { several possible reasons for failure. */ u8 status; /* Total air access duration including all retrys and overheads.*/ - u16 medium_usage; + __le16 medium_usage; /* The time passed from host xfer to Tx-complete.*/ - u32 fw_handling_time; + __le32 fw_handling_time; /* Total media delay (from 1st EDCA AIFS counter until TX Complete). */ - u32 medium_delay; + __le32 medium_delay; /* LS-byte of last TKIP seq-num (saved per AC for recovery). */ u8 lsb_security_sequence_number; /* Retry count - number of transmissions without successful ACK.*/ @@ -118,8 +118,8 @@ struct wl1271_tx_hw_res_descr { } __attribute__ ((packed)); struct wl1271_tx_hw_res_if { - u32 tx_result_fw_counter; - u32 tx_result_host_counter; + __le32 tx_result_fw_counter; + __le32 tx_result_host_counter; struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; } __attribute__ ((packed)); diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 657c2dbcb7d3..055d7bc6f592 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -122,8 +122,8 @@ struct wl12xx_null_data_template { } __attribute__ ((packed)); struct wl12xx_ps_poll_template { - u16 fc; - u16 aid; + __le16 fc; + __le16 aid; u8 bssid[ETH_ALEN]; u8 ta[ETH_ALEN]; } __attribute__ ((packed)); diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 5f0401a52cff..7b9621de239f 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -365,7 +365,7 @@ static void wl3501_free_tx_buffer(struct wl3501_card *this, u16 ptr) static int wl3501_esbq_req_test(struct wl3501_card *this) { - u8 tmp; + u8 tmp = 0; wl3501_get_from_wla(this, this->esbq_req_head + 3, &tmp, sizeof(tmp)); return tmp & 0x80; diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index bc81974a2bc7..33c8be7ec8e6 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -112,6 +112,9 @@ exit: return err; } +MODULE_FIRMWARE("zd1201-ap.fw"); +MODULE_FIRMWARE("zd1201.fw"); + static void zd1201_usbfree(struct urb *urb) { struct zd1201 *zd = urb->context; diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig index 74b31eafe72d..5f809695f71a 100644 --- a/drivers/net/wireless/zd1211rw/Kconfig +++ b/drivers/net/wireless/zd1211rw/Kconfig @@ -1,6 +1,6 @@ config ZD1211RW tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" - depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL + depends on USB && MAC80211 && EXPERIMENTAL select FW_LOADER ---help--- This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c index 4e79a9800134..dfa1b9bc22c8 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -755,7 +755,7 @@ static int hw_reset_phy(struct zd_chip *chip) static int zd1211_hw_init_hmac(struct zd_chip *chip) { static const struct zd_ioreq32 ioreqs[] = { - { CR_ZD1211_RETRY_MAX, 0x2 }, + { CR_ZD1211_RETRY_MAX, ZD1211_RETRY_COUNT }, { CR_RX_THRESHOLD, 0x000c0640 }, }; @@ -767,7 +767,7 @@ static int zd1211_hw_init_hmac(struct zd_chip *chip) static int zd1211b_hw_init_hmac(struct zd_chip *chip) { static const struct zd_ioreq32 ioreqs[] = { - { CR_ZD1211B_RETRY_MAX, 0x02020202 }, + { CR_ZD1211B_RETRY_MAX, ZD1211B_RETRY_COUNT }, { CR_ZD1211B_CWIN_MAX_MIN_AC0, 0x007f003f }, { CR_ZD1211B_CWIN_MAX_MIN_AC1, 0x007f003f }, { CR_ZD1211B_CWIN_MAX_MIN_AC2, 0x003f001f }, diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index 678c139a840c..9fd8f3508d66 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -642,13 +642,29 @@ enum { #define CR_ZD1211B_TXOP CTL_REG(0x0b20) #define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28) +/* Value for CR_ZD1211_RETRY_MAX & CR_ZD1211B_RETRY_MAX. Vendor driver uses 2, + * we use 0. The first rate is tried (count+2), then all next rates are tried + * twice, until 1 Mbits is tried. */ +#define ZD1211_RETRY_COUNT 0 +#define ZD1211B_RETRY_COUNT \ + (ZD1211_RETRY_COUNT << 0)| \ + (ZD1211_RETRY_COUNT << 8)| \ + (ZD1211_RETRY_COUNT << 16)| \ + (ZD1211_RETRY_COUNT << 24) + /* Used to detect PLL lock */ #define UW2453_INTR_REG ((zd_addr_t)0x85c1) #define CWIN_SIZE 0x007f043f -#define HWINT_ENABLED 0x004f0000 +#define HWINT_ENABLED \ + (INT_TX_COMPLETE_EN| \ + INT_RX_COMPLETE_EN| \ + INT_RETRY_FAIL_EN| \ + INT_WAKEUP_EN| \ + INT_CFG_NEXT_BCN_EN) + #define HWINT_DISABLED 0 #define E2P_PWR_INT_GUARD 8 diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 6d666359a42f..cf51e8f8174b 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -88,6 +88,34 @@ static const struct ieee80211_rate zd_rates[] = { .flags = 0 }, }; +/* + * Zydas retry rates table. Each line is listed in the same order as + * in zd_rates[] and contains all the rate used when a packet is sent + * starting with a given rates. Let's consider an example : + * + * "11 Mbits : 4, 3, 2, 1, 0" means : + * - packet is sent using 4 different rates + * - 1st rate is index 3 (ie 11 Mbits) + * - 2nd rate is index 2 (ie 5.5 Mbits) + * - 3rd rate is index 1 (ie 2 Mbits) + * - 4th rate is index 0 (ie 1 Mbits) + */ + +static const struct tx_retry_rate zd_retry_rates[] = { + { /* 1 Mbits */ 1, { 0 }}, + { /* 2 Mbits */ 2, { 1, 0 }}, + { /* 5.5 Mbits */ 3, { 2, 1, 0 }}, + { /* 11 Mbits */ 4, { 3, 2, 1, 0 }}, + { /* 6 Mbits */ 5, { 4, 3, 2, 1, 0 }}, + { /* 9 Mbits */ 6, { 5, 4, 3, 2, 1, 0}}, + { /* 12 Mbits */ 5, { 6, 3, 2, 1, 0 }}, + { /* 18 Mbits */ 6, { 7, 6, 3, 2, 1, 0 }}, + { /* 24 Mbits */ 6, { 8, 6, 3, 2, 1, 0 }}, + { /* 36 Mbits */ 7, { 9, 8, 6, 3, 2, 1, 0 }}, + { /* 48 Mbits */ 8, {10, 9, 8, 6, 3, 2, 1, 0 }}, + { /* 54 Mbits */ 9, {11, 10, 9, 8, 6, 3, 2, 1, 0 }} +}; + static const struct ieee80211_channel zd_channels[] = { { .center_freq = 2412, .hw_value = 1 }, { .center_freq = 2417, .hw_value = 2 }, @@ -282,7 +310,7 @@ static void zd_op_stop(struct ieee80211_hw *hw) } /** - * tx_status - reports tx status of a packet if required + * zd_mac_tx_status - reports tx status of a packet if required * @hw - a &struct ieee80211_hw pointer * @skb - a sk-buffer * @flags: extra flags to set in the TX status info @@ -295,15 +323,49 @@ static void zd_op_stop(struct ieee80211_hw *hw) * * If no status information has been requested, the skb is freed. */ -static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, - int ackssi, bool success) +static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, + int ackssi, struct tx_status *tx_status) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int i; + int success = 1, retry = 1; + int first_idx; + const struct tx_retry_rate *retries; ieee80211_tx_info_clear_status(info); - if (success) + if (tx_status) { + success = !tx_status->failure; + retry = tx_status->retry + success; + } + + if (success) { + /* success */ info->flags |= IEEE80211_TX_STAT_ACK; + } else { + /* failure */ + info->flags &= ~IEEE80211_TX_STAT_ACK; + } + + first_idx = info->status.rates[0].idx; + ZD_ASSERT(0<=first_idx && first_idx<ARRAY_SIZE(zd_retry_rates)); + retries = &zd_retry_rates[first_idx]; + ZD_ASSERT(0<=retry && retry<=retries->count); + + info->status.rates[0].idx = retries->rate[0]; + info->status.rates[0].count = 1; // (retry > 1 ? 2 : 1); + + for (i=1; i<IEEE80211_TX_MAX_RATES-1 && i<retry; i++) { + info->status.rates[i].idx = retries->rate[i]; + info->status.rates[i].count = 1; // ((i==retry-1) && success ? 1:2); + } + for (; i<IEEE80211_TX_MAX_RATES && i<retry; i++) { + info->status.rates[i].idx = retries->rate[retry-1]; + info->status.rates[i].count = 1; // (success ? 1:2); + } + if (i<IEEE80211_TX_MAX_RATES) + info->status.rates[i].idx = -1; /* terminate */ + info->status.ack_signal = ackssi; ieee80211_tx_status_irqsafe(hw, skb); } @@ -312,20 +374,83 @@ static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, * zd_mac_tx_failed - callback for failed frames * @dev: the mac80211 wireless device * - * This function is called if a frame couldn't be succesfully be + * This function is called if a frame couldn't be successfully be * transferred. The first frame from the tx queue, will be selected and * reported as error to the upper layers. */ -void zd_mac_tx_failed(struct ieee80211_hw *hw) +void zd_mac_tx_failed(struct urb *urb) { - struct sk_buff_head *q = &zd_hw_mac(hw)->ack_wait_queue; + struct ieee80211_hw * hw = zd_usb_to_hw(urb->context); + struct zd_mac *mac = zd_hw_mac(hw); + struct sk_buff_head *q = &mac->ack_wait_queue; struct sk_buff *skb; + struct tx_status *tx_status = (struct tx_status *)urb->transfer_buffer; + unsigned long flags; + int success = !tx_status->failure; + int retry = tx_status->retry + success; + int found = 0; + int i, position = 0; - skb = skb_dequeue(q); - if (skb == NULL) - return; + q = &mac->ack_wait_queue; + spin_lock_irqsave(&q->lock, flags); + + skb_queue_walk(q, skb) { + struct ieee80211_hdr *tx_hdr; + struct ieee80211_tx_info *info; + int first_idx, final_idx; + const struct tx_retry_rate *retries; + u8 final_rate; + + position ++; + + /* if the hardware reports a failure and we had a 802.11 ACK + * pending, then we skip the first skb when searching for a + * matching frame */ + if (tx_status->failure && mac->ack_pending && + skb_queue_is_first(q, skb)) { + continue; + } + + tx_hdr = (struct ieee80211_hdr *)skb->data; + + /* we skip all frames not matching the reported destination */ + if (unlikely(memcmp(tx_hdr->addr1, tx_status->mac, ETH_ALEN))) { + continue; + } + + /* we skip all frames not matching the reported final rate */ - tx_status(hw, skb, 0, 0); + info = IEEE80211_SKB_CB(skb); + first_idx = info->status.rates[0].idx; + ZD_ASSERT(0<=first_idx && first_idx<ARRAY_SIZE(zd_retry_rates)); + retries = &zd_retry_rates[first_idx]; + if (retry < 0 || retry > retries->count) { + continue; + } + + ZD_ASSERT(0<=retry && retry<=retries->count); + final_idx = retries->rate[retry-1]; + final_rate = zd_rates[final_idx].hw_value; + + if (final_rate != tx_status->rate) { + continue; + } + + found = 1; + break; + } + + if (found) { + for (i=1; i<=position; i++) { + skb = __skb_dequeue(q); + zd_mac_tx_status(hw, skb, + mac->ack_pending ? mac->ack_signal : 0, + i == position ? tx_status : NULL); + mac->ack_pending = 0; + } + } + + spin_unlock_irqrestore(&q->lock, flags); } /** @@ -342,18 +467,27 @@ void zd_mac_tx_to_dev(struct sk_buff *skb, int error) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hw *hw = info->rate_driver_data[0]; + struct zd_mac *mac = zd_hw_mac(hw); + + ieee80211_tx_info_clear_status(info); skb_pull(skb, sizeof(struct zd_ctrlset)); if (unlikely(error || (info->flags & IEEE80211_TX_CTL_NO_ACK))) { - tx_status(hw, skb, 0, !error); + /* + * FIXME : do we need to fill in anything ? + */ + ieee80211_tx_status_irqsafe(hw, skb); } else { - struct sk_buff_head *q = - &zd_hw_mac(hw)->ack_wait_queue; + struct sk_buff_head *q = &mac->ack_wait_queue; skb_queue_tail(q, skb); - while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) - zd_mac_tx_failed(hw); + while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) { + zd_mac_tx_status(hw, skb_dequeue(q), + mac->ack_pending ? mac->ack_signal : 0, + NULL); + mac->ack_pending = 0; + } } } @@ -606,27 +740,47 @@ fail: static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr, struct ieee80211_rx_status *stats) { + struct zd_mac *mac = zd_hw_mac(hw); struct sk_buff *skb; struct sk_buff_head *q; unsigned long flags; + int found = 0; + int i, position = 0; if (!ieee80211_is_ack(rx_hdr->frame_control)) return 0; - q = &zd_hw_mac(hw)->ack_wait_queue; + q = &mac->ack_wait_queue; spin_lock_irqsave(&q->lock, flags); skb_queue_walk(q, skb) { struct ieee80211_hdr *tx_hdr; + position ++; + + if (mac->ack_pending && skb_queue_is_first(q, skb)) + continue; + tx_hdr = (struct ieee80211_hdr *)skb->data; if (likely(!memcmp(tx_hdr->addr2, rx_hdr->addr1, ETH_ALEN))) { - __skb_unlink(skb, q); - tx_status(hw, skb, stats->signal, 1); - goto out; + found = 1; + break; } } -out: + + if (found) { + for (i=1; i<position; i++) { + skb = __skb_dequeue(q); + zd_mac_tx_status(hw, skb, + mac->ack_pending ? mac->ack_signal : 0, + NULL); + mac->ack_pending = 0; + } + + mac->ack_pending = 1; + mac->ack_signal = stats->signal; + } + spin_unlock_irqrestore(&q->lock, flags); return 1; } @@ -709,6 +863,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length) skb_reserve(skb, 2); } + /* FIXME : could we avoid this big memcpy ? */ memcpy(skb_put(skb, length), buffer, length); memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); @@ -999,7 +1154,14 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) hw->queues = 1; hw->extra_tx_headroom = sizeof(struct zd_ctrlset); + /* + * Tell mac80211 that we support multi rate retries + */ + hw->max_rates = IEEE80211_TX_MAX_RATES; + hw->max_rate_tries = 18; /* 9 rates * 2 retries/rate */ + skb_queue_head_init(&mac->ack_wait_queue); + mac->ack_pending = 0; zd_chip_init(&mac->chip, hw, intf); housekeeping_init(mac); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h index 7c2759118d13..630c298a730e 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.h +++ b/drivers/net/wireless/zd1211rw/zd_mac.h @@ -140,6 +140,21 @@ struct rx_status { #define ZD_RX_CRC16_ERROR 0x40 #define ZD_RX_ERROR 0x80 +struct tx_retry_rate { + int count; /* number of valid element in rate[] array */ + int rate[10]; /* retry rates, described by an index in zd_rates[] */ +}; + +struct tx_status { + u8 type; /* must always be 0x01 : USB_INT_TYPE */ + u8 id; /* must always be 0xa0 : USB_INT_ID_RETRY_FAILED */ + u8 rate; + u8 pad; + u8 mac[ETH_ALEN]; + u8 retry; + u8 failure; +} __attribute__((packed)); + enum mac_flags { MAC_FIXED_CHANNEL = 0x01, }; @@ -150,7 +165,7 @@ struct housekeeping { #define ZD_MAC_STATS_BUFFER_SIZE 16 -#define ZD_MAC_MAX_ACK_WAITERS 10 +#define ZD_MAC_MAX_ACK_WAITERS 50 struct zd_mac { struct zd_chip chip; @@ -184,6 +199,12 @@ struct zd_mac { /* whether to pass control frames to stack */ unsigned int pass_ctrl:1; + + /* whether we have received a 802.11 ACK that is pending */ + unsigned int ack_pending:1; + + /* signal strength of the last 802.11 ACK received */ + int ack_signal; }; #define ZD_REGDOMAIN_FCC 0x10 @@ -279,7 +300,7 @@ int zd_mac_preinit_hw(struct ieee80211_hw *hw); int zd_mac_init_hw(struct ieee80211_hw *hw); int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length); -void zd_mac_tx_failed(struct ieee80211_hw *hw); +void zd_mac_tx_failed(struct urb *urb); void zd_mac_tx_to_dev(struct sk_buff *skb, int error); #ifdef DEBUG diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 23a6a6d4863b..ac19ecd19cfe 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -318,6 +318,13 @@ error: return r; } +MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ur"); +MODULE_FIRMWARE(FW_ZD1211_PREFIX "ur"); +MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ub"); +MODULE_FIRMWARE(FW_ZD1211_PREFIX "ub"); +MODULE_FIRMWARE(FW_ZD1211B_PREFIX "uphr"); +MODULE_FIRMWARE(FW_ZD1211_PREFIX "uphr"); + /* Read data from device address space using "firmware interface" which does * not require firmware to be loaded. */ int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len) @@ -419,7 +426,7 @@ static void int_urb_complete(struct urb *urb) handle_regs_int(urb); break; case USB_INT_ID_RETRY_FAILED: - zd_mac_tx_failed(zd_usb_to_hw(urb->context)); + zd_mac_tx_failed(urb); break; default: dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb, @@ -553,6 +560,8 @@ static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, if (length < sizeof(struct rx_length_info)) { /* It's not a complete packet anyhow. */ + printk("%s: invalid, small RX packet : %d\n", + __func__, length); return; } length_info = (struct rx_length_info *) diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index 83a044dbd1d7..8c777ba4e2b3 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -660,7 +660,7 @@ static int xemaclite_open(struct net_device *dev) xemaclite_set_mac_address(lp, dev->dev_addr); /* Grab the IRQ */ - retval = request_irq(dev->irq, &xemaclite_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, xemaclite_interrupt, 0, dev->name, dev); if (retval) { dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n", dev->irq); diff --git a/drivers/net/xtsonic.c b/drivers/net/xtsonic.c index 0c44135c0b1f..389ba9df7120 100644 --- a/drivers/net/xtsonic.c +++ b/drivers/net/xtsonic.c @@ -92,7 +92,7 @@ static unsigned short known_revisions[] = static int xtsonic_open(struct net_device *dev) { - if (request_irq(dev->irq,&sonic_interrupt,IRQF_DISABLED,"sonic",dev)) { + if (request_irq(dev->irq,sonic_interrupt,IRQF_DISABLED,"sonic",dev)) { printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq); return -EAGAIN; diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 40ad0dee0406..0f773a9a3ff2 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -579,7 +579,7 @@ static int yellowfin_open(struct net_device *dev) /* Reset the chip. */ iowrite32(0x80000000, ioaddr + DMACtrl); - ret = request_irq(dev->irq, &yellowfin_interrupt, IRQF_SHARED, dev->name, dev); + ret = request_irq(dev->irq, yellowfin_interrupt, IRQF_SHARED, dev->name, dev); if (ret) return ret; @@ -944,8 +944,8 @@ static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance) dev_kfree_skb_irq(skb); yp->tx_skbuff[entry] = NULL; } - if (yp->tx_full - && yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE - 4) { + if (yp->tx_full && + yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE - 4) { /* The ring is no longer full, clear tbusy. */ yp->tx_full = 0; netif_wake_queue(dev); @@ -1014,8 +1014,8 @@ static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance) } #endif - if (yp->tx_full - && yp->cur_tx - dirty_tx < TX_QUEUE_SIZE - 2) { + if (yp->tx_full && + yp->cur_tx - dirty_tx < TX_QUEUE_SIZE - 2) { /* The ring is no longer full, clear tbusy. */ yp->tx_full = 0; netif_wake_queue(dev); diff --git a/drivers/net/znet.c b/drivers/net/znet.c index b42347333750..bc5ae0f6e934 100644 --- a/drivers/net/znet.c +++ b/drivers/net/znet.c @@ -103,8 +103,7 @@ #include <asm/io.h> #include <asm/dma.h> -/* This include could be elsewhere, since it is not wireless specific */ -#include "wireless/i82593.h" +#include <linux/i82593.h> static char version[] __initdata = "znet.c:v1.02 9/23/94 becker@scyld.com\n"; @@ -170,7 +169,7 @@ static int znet_request_resources (struct net_device *dev) { struct znet_private *znet = netdev_priv(dev); - if (request_irq (dev->irq, &znet_interrupt, 0, "ZNet", dev)) + if (request_irq (dev->irq, znet_interrupt, 0, "ZNet", dev)) goto failed; if (request_dma (znet->rx_dma, "ZNet rx")) goto free_irq; @@ -698,8 +697,8 @@ static void znet_rx(struct net_device *dev) the same area of the backwards links we now have. This allows us to pass packets to the upper layers in the order they were received -- important for fast-path sequential operations. */ - while (znet->rx_start + cur_frame_end_offset != znet->rx_cur - && ++boguscount < 5) { + while (znet->rx_start + cur_frame_end_offset != znet->rx_cur && + ++boguscount < 5) { unsigned short hi_cnt, lo_cnt, hi_status, lo_status; int count, status; diff --git a/drivers/of/base.c b/drivers/of/base.c index ddf224d456b2..e6627b2320f1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -9,7 +9,8 @@ * * Adapted for sparc and sparc64 by David S. Miller davem@davemloft.net * - * Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell. + * Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell and + * Grant Likely. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -82,6 +83,29 @@ struct property *of_find_property(const struct device_node *np, } EXPORT_SYMBOL(of_find_property); +/** + * of_find_all_nodes - Get next node in global list + * @prev: Previous node or NULL to start iteration + * of_node_put() will be called on it + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_all_nodes(struct device_node *prev) +{ + struct device_node *np; + + read_lock(&devtree_lock); + np = prev ? prev->allnext : allnodes; + for (; np != NULL; np = np->allnext) + if (of_node_get(np)) + break; + of_node_put(prev); + read_unlock(&devtree_lock); + return np; +} +EXPORT_SYMBOL(of_find_all_nodes); + /* * Find a property with a given name for a given node * and return the value. diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index a6b4a5a53d40..f511e70d454c 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -650,7 +650,7 @@ ccio_clear_io_tlb(struct ioc *ioc, dma_addr_t iovp, size_t byte_cnt) * Mark the I/O Pdir entries invalid and blow away the corresponding I/O * TLB entries. * - * FIXME: at some threshhold it might be "cheaper" to just blow + * FIXME: at some threshold it might be "cheaper" to just blow * away the entire I/O TLB instead of individual entries. * * FIXME: Uturn has 256 TLB entries. We don't need to purge every diff --git a/drivers/parisc/eisa_eeprom.c b/drivers/parisc/eisa_eeprom.c index 8c0b26e9b98a..cce00ed81f37 100644 --- a/drivers/parisc/eisa_eeprom.c +++ b/drivers/parisc/eisa_eeprom.c @@ -75,17 +75,8 @@ static ssize_t eisa_eeprom_read(struct file * file, return ret; } -static int eisa_eeprom_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, - unsigned long arg) -{ - return -ENOTTY; -} - static int eisa_eeprom_open(struct inode *inode, struct file *file) { - cycle_kernel_lock(); - if (file->f_mode & FMODE_WRITE) return -EINVAL; @@ -104,7 +95,6 @@ static const struct file_operations eisa_eeprom_fops = { .owner = THIS_MODULE, .llseek = eisa_eeprom_llseek, .read = eisa_eeprom_read, - .ioctl = eisa_eeprom_ioctl, .open = eisa_eeprom_open, .release = eisa_eeprom_release, }; diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c index 9581d3619450..79caf1ca4a29 100644 --- a/drivers/parisc/led.c +++ b/drivers/parisc/led.c @@ -352,11 +352,9 @@ static __inline__ int led_get_net_activity(void) rx_total = tx_total = 0; - /* we are running as a workqueue task, so locking dev_base - * for reading should be OK */ - read_lock(&dev_base_lock); + /* we are running as a workqueue task, so we can use an RCU lookup */ rcu_read_lock(); - for_each_netdev(&init_net, dev) { + for_each_netdev_rcu(&init_net, dev) { const struct net_device_stats *stats; struct in_device *in_dev = __in_dev_get_rcu(dev); if (!in_dev || !in_dev->ifa_list) @@ -368,7 +366,6 @@ static __inline__ int led_get_net_activity(void) tx_total += stats->tx_packets; } rcu_read_unlock(); - read_unlock(&dev_base_lock); retval = 0; diff --git a/drivers/parport/parport_mfc3.c b/drivers/parport/parport_mfc3.c index 6dec9ba5ed28..362db31d8ca6 100644 --- a/drivers/parport/parport_mfc3.c +++ b/drivers/parport/parport_mfc3.c @@ -386,7 +386,7 @@ static void __exit parport_mfc3_exit(void) if (!this_port[i]) continue; parport_remove_port(this_port[i]); - if (!this_port[i]->irq != PARPORT_IRQ_NONE) { + if (this_port[i]->irq != PARPORT_IRQ_NONE) { if (--use_cnt == 0) free_irq(IRQ_AMIGA_PORTS, &pp_mfc3_ops); } diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 8eefe56f1cbe..3f56bc086cb5 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -233,10 +233,10 @@ static int do_hardware_modes (ctl_table *table, int write, return copy_to_user(result, buffer, len) ? -EFAULT : 0; } -#define PARPORT_PORT_DIR(CHILD) { .ctl_name = 0, .procname = NULL, .mode = 0555, .child = CHILD } -#define PARPORT_PARPORT_DIR(CHILD) { .ctl_name = DEV_PARPORT, .procname = "parport", \ +#define PARPORT_PORT_DIR(CHILD) { .procname = NULL, .mode = 0555, .child = CHILD } +#define PARPORT_PARPORT_DIR(CHILD) { .procname = "parport", \ .mode = 0555, .child = CHILD } -#define PARPORT_DEV_DIR(CHILD) { .ctl_name = CTL_DEV, .procname = "dev", .mode = 0555, .child = CHILD } +#define PARPORT_DEV_DIR(CHILD) { .procname = "dev", .mode = 0555, .child = CHILD } #define PARPORT_DEVICES_ROOT_DIR { .procname = "devices", \ .mode = 0555, .child = NULL } @@ -270,7 +270,7 @@ static const struct parport_sysctl_table parport_sysctl_template = { .data = NULL, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, + .proc_handler = proc_dointvec_minmax, .extra1 = (void*) &parport_min_spintime_value, .extra2 = (void*) &parport_max_spintime_value }, @@ -279,28 +279,28 @@ static const struct parport_sysctl_table parport_sysctl_template = { .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_hardware_base_addr + .proc_handler = do_hardware_base_addr }, { .procname = "irq", .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_hardware_irq + .proc_handler = do_hardware_irq }, { .procname = "dma", .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_hardware_dma + .proc_handler = do_hardware_dma }, { .procname = "modes", .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_hardware_modes + .proc_handler = do_hardware_modes }, PARPORT_DEVICES_ROOT_DIR, #ifdef CONFIG_PARPORT_1284 @@ -309,35 +309,35 @@ static const struct parport_sysctl_table parport_sysctl_template = { .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_autoprobe + .proc_handler = do_autoprobe }, { .procname = "autoprobe0", .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_autoprobe + .proc_handler = do_autoprobe }, { .procname = "autoprobe1", .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_autoprobe + .proc_handler = do_autoprobe }, { .procname = "autoprobe2", .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_autoprobe + .proc_handler = do_autoprobe }, { .procname = "autoprobe3", .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_autoprobe + .proc_handler = do_autoprobe }, #endif /* IEEE 1284 support */ {} @@ -348,7 +348,7 @@ static const struct parport_sysctl_table parport_sysctl_template = { .data = NULL, .maxlen = 0, .mode = 0444, - .proc_handler = &do_active_device + .proc_handler = do_active_device }, {} }, @@ -386,14 +386,13 @@ parport_device_sysctl_template = { .data = NULL, .maxlen = sizeof(unsigned long), .mode = 0644, - .proc_handler = &proc_doulongvec_ms_jiffies_minmax, + .proc_handler = proc_doulongvec_ms_jiffies_minmax, .extra1 = (void*) &parport_min_timeslice_value, .extra2 = (void*) &parport_max_timeslice_value }, }, { { - .ctl_name = 0, .procname = NULL, .data = NULL, .maxlen = 0, @@ -438,7 +437,7 @@ parport_default_sysctl_table = { .data = &parport_default_timeslice, .maxlen = sizeof(parport_default_timeslice), .mode = 0644, - .proc_handler = &proc_doulongvec_ms_jiffies_minmax, + .proc_handler = proc_doulongvec_ms_jiffies_minmax, .extra1 = (void*) &parport_min_timeslice_value, .extra2 = (void*) &parport_max_timeslice_value }, @@ -447,7 +446,7 @@ parport_default_sysctl_table = { .data = &parport_default_spintime, .maxlen = sizeof(parport_default_spintime), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, + .proc_handler = proc_dointvec_minmax, .extra1 = (void*) &parport_min_spintime_value, .extra2 = (void*) &parport_max_spintime_value }, @@ -455,7 +454,6 @@ parport_default_sysctl_table = { }, { { - .ctl_name = DEV_PARPORT_DEFAULT, .procname = "default", .mode = 0555, .child = parport_default_sysctl_table.vars @@ -495,7 +493,6 @@ int parport_proc_register(struct parport *port) t->vars[6 + i].extra2 = &port->probe_info[i]; t->port_dir[0].procname = port->name; - t->port_dir[0].ctl_name = 0; t->port_dir[0].child = t->vars; t->parport_dir[0].child = t->port_dir; @@ -534,11 +531,9 @@ int parport_device_proc_register(struct pardevice *device) t->dev_dir[0].child = t->parport_dir; t->parport_dir[0].child = t->port_dir; t->port_dir[0].procname = port->name; - t->port_dir[0].ctl_name = 0; t->port_dir[0].child = t->devices_root_dir; t->devices_root_dir[0].child = t->device_dir; - t->device_dir[0].ctl_name = 0; t->device_dir[0].procname = device->name; t->device_dir[0].child = t->vars; t->vars[0].data = &device->timeslice; diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index a73028ec52e5..0f32571b94df 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -471,7 +471,7 @@ int acpi_pci_detect_ejectable(acpi_handle handle) return found; acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, - check_hotplug, (void *)&found, NULL); + check_hotplug, NULL, (void *)&found, NULL); return found; } EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 58d25a163a8b..df1b0ea089d1 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -266,7 +266,7 @@ static int detect_ejectable_slots(acpi_handle handle) int found = acpi_pci_detect_ejectable(handle); if (!found) { acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - is_pci_dock_device, (void *)&found, NULL); + is_pci_dock_device, NULL, (void *)&found, NULL); } return found; } @@ -281,7 +281,7 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge) /* register all slot objects under this bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1, - register_slot, bridge, NULL); + register_slot, NULL, bridge, NULL); if (ACPI_FAILURE(status)) { list_del(&bridge->list); return; @@ -447,7 +447,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) /* search P2P bridges under this p2p bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, NULL, NULL); + find_p2p_bridge, NULL, NULL, NULL); if (ACPI_FAILURE(status)) warn("find_p2p_bridge failed (error code = 0x%x)\n", status); @@ -485,7 +485,7 @@ static int add_bridge(acpi_handle handle) /* search P2P bridges under this host bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, NULL, NULL); + find_p2p_bridge, NULL, NULL, NULL); if (ACPI_FAILURE(status)) warn("find_p2p_bridge failed (error code = 0x%x)\n", status); @@ -573,7 +573,7 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) /* cleanup p2p bridges under this P2P bridge in a depth-first manner */ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - cleanup_p2p_bridge, NULL, NULL); + cleanup_p2p_bridge, NULL, NULL, NULL); bridge = acpiphp_handle_to_bridge(handle); if (bridge) @@ -589,7 +589,7 @@ static void remove_bridge(acpi_handle handle) /* cleanup p2p bridges under this host bridge in a depth-first manner */ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, - (u32)1, cleanup_p2p_bridge, NULL, NULL); + (u32)1, cleanup_p2p_bridge, NULL, NULL, NULL); /* * On root bridges with hotplug slots directly underneath (ie, @@ -778,7 +778,7 @@ static int acpiphp_configure_ioapics(acpi_handle handle) { ioapic_add(handle, 0, NULL, NULL); acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, - ACPI_UINT32_MAX, ioapic_add, NULL, NULL); + ACPI_UINT32_MAX, ioapic_add, NULL, NULL, NULL); return 0; } @@ -786,7 +786,7 @@ static int acpiphp_unconfigure_ioapics(acpi_handle handle) { ioapic_remove(handle, 0, NULL, NULL); acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, - ACPI_UINT32_MAX, ioapic_remove, NULL, NULL); + ACPI_UINT32_MAX, ioapic_remove, NULL, NULL, NULL); return 0; } @@ -1367,7 +1367,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont bridge = acpiphp_handle_to_bridge(handle); if (type == ACPI_NOTIFY_BUS_CHECK) { acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX, - count_sub_bridges, &num_sub_bridges, NULL); + count_sub_bridges, NULL, &num_sub_bridges, NULL); } if (!bridge && !num_sub_bridges) { @@ -1388,7 +1388,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont } if (num_sub_bridges) acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, - ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL); + ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL); break; case ACPI_NOTIFY_DEVICE_CHECK: @@ -1512,7 +1512,7 @@ int __init acpiphp_glue_init(void) int num = 0; acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_root_bridges, &num, NULL); + ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); if (num <= 0) return -1; diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index e7be66dbac21..aa5df485f8cf 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -434,7 +434,7 @@ static int __init ibm_acpiphp_init(void) dbg("%s\n", __func__); if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, ibm_find_acpi_device, + ACPI_UINT32_MAX, ibm_find_acpi_device, NULL, &ibm_acpi_handle, NULL) != FOUND_APCI) { err("%s: acpi_walk_namespace failed\n", __func__); retval = -ENODEV; diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 0ed78a764ded..3b3658669bee 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -2,6 +2,7 @@ #include <linux/dmar.h> #include <linux/spinlock.h> #include <linux/jiffies.h> +#include <linux/hpet.h> #include <linux/pci.h> #include <linux/irq.h> #include <asm/io_apic.h> @@ -14,7 +15,8 @@ #include "pci.h" static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; -static int ir_ioapic_num; +static struct hpet_scope ir_hpet[MAX_HPET_TBS]; +static int ir_ioapic_num, ir_hpet_num; int intr_remapping_enabled; static int disable_intremap; @@ -343,6 +345,16 @@ int flush_irte(int irq) return rc; } +struct intel_iommu *map_hpet_to_ir(u8 hpet_id) +{ + int i; + + for (i = 0; i < MAX_HPET_TBS; i++) + if (ir_hpet[i].id == hpet_id) + return ir_hpet[i].iommu; + return NULL; +} + struct intel_iommu *map_ioapic_to_ir(int apic) { int i; @@ -470,6 +482,36 @@ int set_ioapic_sid(struct irte *irte, int apic) return 0; } +int set_hpet_sid(struct irte *irte, u8 id) +{ + int i; + u16 sid = 0; + + if (!irte) + return -1; + + for (i = 0; i < MAX_HPET_TBS; i++) { + if (ir_hpet[i].id == id) { + sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn; + break; + } + } + + if (sid == 0) { + pr_warning("Failed to set source-id of HPET block (%d)\n", id); + return -1; + } + + /* + * Should really use SQ_ALL_16. Some platforms are broken. + * While we figure out the right quirks for these broken platforms, use + * SQ_13_IGNORE_3 for now. + */ + set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, sid); + + return 0; +} + int set_msi_sid(struct irte *irte, struct pci_dev *dev) { struct pci_dev *bridge; @@ -711,6 +753,34 @@ error: return -1; } +static void ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope, + struct intel_iommu *iommu) +{ + struct acpi_dmar_pci_path *path; + u8 bus; + int count; + + bus = scope->bus; + path = (struct acpi_dmar_pci_path *)(scope + 1); + count = (scope->length - sizeof(struct acpi_dmar_device_scope)) + / sizeof(struct acpi_dmar_pci_path); + + while (--count > 0) { + /* + * Access PCI directly due to the PCI + * subsystem isn't initialized yet. + */ + bus = read_pci_config_byte(bus, path->dev, path->fn, + PCI_SECONDARY_BUS); + path++; + } + ir_hpet[ir_hpet_num].bus = bus; + ir_hpet[ir_hpet_num].devfn = PCI_DEVFN(path->dev, path->fn); + ir_hpet[ir_hpet_num].iommu = iommu; + ir_hpet[ir_hpet_num].id = scope->enumeration_id; + ir_hpet_num++; +} + static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope, struct intel_iommu *iommu) { @@ -740,8 +810,8 @@ static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope, ir_ioapic_num++; } -static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, - struct intel_iommu *iommu) +static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header, + struct intel_iommu *iommu) { struct acpi_dmar_hardware_unit *drhd; struct acpi_dmar_device_scope *scope; @@ -765,6 +835,17 @@ static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, drhd->address); ir_parse_one_ioapic_scope(scope, iommu); + } else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET) { + if (ir_hpet_num == MAX_HPET_TBS) { + printk(KERN_WARNING "Exceeded Max HPET blocks\n"); + return -1; + } + + printk(KERN_INFO "HPET id %d under DRHD base" + " 0x%Lx\n", scope->enumeration_id, + drhd->address); + + ir_parse_one_hpet_scope(scope, iommu); } start += scope->length; } @@ -785,7 +866,7 @@ int __init parse_ioapics_under_ir(void) struct intel_iommu *iommu = drhd->iommu; if (ecap_ir_support(iommu->ecap)) { - if (ir_parse_ioapic_scope(drhd->hdr, iommu)) + if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu)) return -1; ir_supported = 1; diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h index 63a263c18415..5662fecfee60 100644 --- a/drivers/pci/intr_remapping.h +++ b/drivers/pci/intr_remapping.h @@ -7,4 +7,11 @@ struct ioapic_scope { unsigned int devfn; /* PCI devfn number */ }; +struct hpet_scope { + struct intel_iommu *iommu; + u8 id; + unsigned int bus; + unsigned int devfn; +}; + #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index f3ccbccf5f21..cd5082d3ca19 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -179,7 +179,7 @@ config PCMCIA_BCM63XX depends on BCM63XX && PCMCIA config PCMCIA_SOC_COMMON - bool + tristate config PCMCIA_SA1100 tristate "SA1100 support" diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index 68570bc3ac86..663781d20129 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -23,8 +23,8 @@ #include <asm/io.h> #include <asm/sizes.h> -#include <mach/mux.h> -#include <mach/tc.h> +#include <plat/mux.h> +#include <plat/tc.h> /* NOTE: don't expect this to support many I/O cards. The 16xx chips have diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c index 11cc3ba1260a..8db86b90c200 100644 --- a/drivers/pcmcia/sa1100_generic.c +++ b/drivers/pcmcia/sa1100_generic.c @@ -51,7 +51,7 @@ static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = { #ifdef CONFIG_SA1100_CERF pcmcia_cerf_init, #endif -#ifdef CONFIG_SA1100_H3600 +#if defined(CONFIG_SA1100_H3100) || defined(CONFIG_SA1100_H3600) pcmcia_h3600_init, #endif #ifdef CONFIG_SA1100_SHANNON diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c index 3a121ac697d6..56329ad575a9 100644 --- a/drivers/pcmcia/sa1100_h3600.c +++ b/drivers/pcmcia/sa1100_h3600.c @@ -10,47 +10,139 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/gpio.h> #include <mach/hardware.h> #include <asm/irq.h> #include <asm/mach-types.h> -#include <mach/h3600.h> +#include <mach/h3xxx.h> #include "sa1100_generic.h" static struct pcmcia_irqs irqs[] = { - { 0, IRQ_GPIO_H3600_PCMCIA_CD0, "PCMCIA CD0" }, - { 1, IRQ_GPIO_H3600_PCMCIA_CD1, "PCMCIA CD1" } + { .sock = 0, .str = "PCMCIA CD0" }, /* .irq will be filled later */ + { .sock = 1, .str = "PCMCIA CD1" } }; static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - skt->socket.pci_irq = skt->nr ? IRQ_GPIO_H3600_PCMCIA_IRQ1 - : IRQ_GPIO_H3600_PCMCIA_IRQ0; + int err; + switch (skt->nr) { + case 0: + err = gpio_request(H3XXX_GPIO_PCMCIA_IRQ0, "PCMCIA IRQ0"); + if (err) + goto err00; + err = gpio_direction_input(H3XXX_GPIO_PCMCIA_IRQ0); + if (err) + goto err01; + skt->socket.pci_irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_IRQ0); + + err = gpio_request(H3XXX_GPIO_PCMCIA_CD0, "PCMCIA CD0"); + if (err) + goto err01; + err = gpio_direction_input(H3XXX_GPIO_PCMCIA_CD0); + if (err) + goto err02; + irqs[0].irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_CD0); + + err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON"); + if (err) + goto err02; + err = gpio_direction_output(H3XXX_EGPIO_OPT_NVRAM_ON, 0); + if (err) + goto err03; + err = gpio_request(H3XXX_EGPIO_OPT_ON, "OPT ON"); + if (err) + goto err03; + err = gpio_direction_output(H3XXX_EGPIO_OPT_ON, 0); + if (err) + goto err04; + err = gpio_request(H3XXX_EGPIO_OPT_RESET, "OPT RESET"); + if (err) + goto err04; + err = gpio_direction_output(H3XXX_EGPIO_OPT_RESET, 0); + if (err) + goto err05; + err = gpio_request(H3XXX_EGPIO_CARD_RESET, "PCMCIA CARD RESET"); + if (err) + goto err05; + err = gpio_direction_output(H3XXX_EGPIO_CARD_RESET, 0); + if (err) + goto err06; + err = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + if (err) + goto err06; + break; + case 1: + err = gpio_request(H3XXX_GPIO_PCMCIA_IRQ1, "PCMCIA IRQ1"); + if (err) + goto err10; + err = gpio_direction_input(H3XXX_GPIO_PCMCIA_IRQ1); + if (err) + goto err11; + skt->socket.pci_irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_IRQ1); + + err = gpio_request(H3XXX_GPIO_PCMCIA_CD1, "PCMCIA CD1"); + if (err) + goto err11; + err = gpio_direction_input(H3XXX_GPIO_PCMCIA_CD1); + if (err) + goto err12; + irqs[1].irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_CD1); + + err = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + if (err) + goto err12; + break; + } + return 0; - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); +err06: gpio_free(H3XXX_EGPIO_CARD_RESET); +err05: gpio_free(H3XXX_EGPIO_OPT_RESET); +err04: gpio_free(H3XXX_EGPIO_OPT_ON); +err03: gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); +err02: gpio_free(H3XXX_GPIO_PCMCIA_CD0); +err01: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); +err00: return err; + +err12: gpio_free(H3XXX_GPIO_PCMCIA_CD0); +err11: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); +err10: return err; } static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) { soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); - /* Disable CF bus: */ - assign_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON, 0); - assign_h3600_egpio(IPAQ_EGPIO_OPT_ON, 0); - assign_h3600_egpio(IPAQ_EGPIO_OPT_RESET, 1); + switch (skt->nr) { + case 0: + /* Disable CF bus: */ + gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0); + gpio_set_value(H3XXX_EGPIO_OPT_ON, 0); + gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1); + + gpio_free(H3XXX_EGPIO_CARD_RESET); + gpio_free(H3XXX_EGPIO_OPT_RESET); + gpio_free(H3XXX_EGPIO_OPT_ON); + gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); + gpio_free(H3XXX_GPIO_PCMCIA_CD0); + gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); + break; + case 1: + gpio_free(H3XXX_GPIO_PCMCIA_CD1); + gpio_free(H3XXX_GPIO_PCMCIA_IRQ1); + break; + } } static void h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - unsigned long levels = GPLR; - switch (skt->nr) { case 0: - state->detect = levels & GPIO_H3600_PCMCIA_CD0 ? 0 : 1; - state->ready = levels & GPIO_H3600_PCMCIA_IRQ0 ? 1 : 0; + state->detect = !gpio_get_value(H3XXX_GPIO_PCMCIA_CD0); + state->ready = !!gpio_get_value(H3XXX_GPIO_PCMCIA_IRQ0); state->bvd1 = 0; state->bvd2 = 0; state->wrprot = 0; /* Not available on H3600. */ @@ -59,8 +151,8 @@ h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *st break; case 1: - state->detect = levels & GPIO_H3600_PCMCIA_CD1 ? 0 : 1; - state->ready = levels & GPIO_H3600_PCMCIA_IRQ1 ? 1 : 0; + state->detect = !gpio_get_value(H3XXX_GPIO_PCMCIA_CD1); + state->ready = !!gpio_get_value(H3XXX_GPIO_PCMCIA_IRQ1); state->bvd1 = 0; state->bvd2 = 0; state->wrprot = 0; /* Not available on H3600. */ @@ -79,7 +171,7 @@ h3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_ return -1; } - assign_h3600_egpio(IPAQ_EGPIO_CARD_RESET, !!(state->flags & SS_RESET)); + gpio_set_value(H3XXX_EGPIO_CARD_RESET, !!(state->flags & SS_RESET)); /* Silently ignore Vpp, output enable, speaker enable. */ @@ -89,9 +181,9 @@ h3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_ static void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt) { /* Enable CF bus: */ - assign_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON, 1); - assign_h3600_egpio(IPAQ_EGPIO_OPT_ON, 1); - assign_h3600_egpio(IPAQ_EGPIO_OPT_RESET, 0); + gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 1); + gpio_set_value(H3XXX_EGPIO_OPT_ON, 1); + gpio_set_value(H3XXX_EGPIO_OPT_RESET, 0); msleep(10); @@ -109,10 +201,10 @@ static void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) * socket 0 then socket 1. */ if (skt->nr == 1) { - assign_h3600_egpio(IPAQ_EGPIO_OPT_ON, 0); - assign_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON, 0); + gpio_set_value(H3XXX_EGPIO_OPT_ON, 0); + gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0); /* hmm, does this suck power? */ - assign_h3600_egpio(IPAQ_EGPIO_OPT_RESET, 1); + gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1); } } @@ -131,7 +223,7 @@ int __init pcmcia_h3600_init(struct device *dev) { int ret = -ENODEV; - if (machine_is_h3600()) + if (machine_is_h3600() || machine_is_h3100()) ret = sa11xx_drv_pcmcia_probe(dev, &h3600_pcmcia_ops, 0, 2); return ret; diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 9652c3fe7f5e..8390dca2b4e1 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -1,5 +1,3 @@ -# drivers/platform/Kconfig - if X86 source "drivers/platform/x86/Kconfig" endif diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 29432a50be45..f0a90a6bf396 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -510,7 +510,7 @@ static int __init intel_menlow_module_init(void) /* Looking for sensors in each ACPI thermal zone */ status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - intel_menlow_register_sensor, NULL, NULL); + intel_menlow_register_sensor, NULL, NULL, NULL); if (ACPI_FAILURE(status)) return -ENODEV; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index a2a742c8ff7e..7a2cc8a5c975 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1203,7 +1203,7 @@ static int sony_nc_add(struct acpi_device *device) if (debug) { status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, - 1, sony_walk_callback, NULL, NULL); + 1, sony_walk_callback, NULL, NULL, NULL); if (ACPI_FAILURE(status)) { printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n"); result = -ENODEV; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a848c7e20aeb..0ed84806f8ae 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -1087,7 +1087,7 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) */ status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, - tpacpi_acpi_walk_find_bcl, NULL, + tpacpi_acpi_walk_find_bcl, NULL, NULL, &bcl_ptr); if (ACPI_SUCCESS(status) && bcl_levels > 2) { @@ -6545,7 +6545,7 @@ static struct ibm_struct volume_driver_data = { * The speeds are stored on handles * (FANA:FAN9), (FANC:FANB), (FANE:FAND). * - * There are three default speed sets, acessible as handles: + * There are three default speed sets, accessible as handles: * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H * * ACPI DSDT switches which set is in use depending on various diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index 87b4f49a5251..a5135ebe5f07 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -191,7 +191,7 @@ static unsigned char *pnpbios_parse_allocated_resource_data(struct pnp_dev *dev, return (unsigned char *)p; break; - default: /* an unkown tag */ + default: /* an unknown tag */ len_err: dev_err(&dev->dev, "unknown tag %#x length %d\n", tag, len); @@ -405,7 +405,7 @@ pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, case SMALL_TAG_END: return p + 2; - default: /* an unkown tag */ + default: /* an unknown tag */ len_err: dev_err(&dev->dev, "unknown tag %#x length %d\n", tag, len); @@ -475,7 +475,7 @@ static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p, return (unsigned char *)p; break; - default: /* an unkown tag */ + default: /* an unknown tag */ len_err: dev_err(&dev->dev, "unknown tag %#x length %d\n", tag, len); @@ -744,7 +744,7 @@ static unsigned char *pnpbios_encode_allocated_resource_data(struct pnp_dev return (unsigned char *)p; break; - default: /* an unkown tag */ + default: /* an unknown tag */ len_err: dev_err(&dev->dev, "unknown tag %#x length %d\n", tag, len); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index cea6cef27e89..118674925516 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -77,6 +77,13 @@ config BATTERY_TOSA Say Y to enable support for the battery on the Sharp Zaurus SL-6000 (tosa) models. +config BATTERY_COLLIE + tristate "Sharp SL-5500 (collie) battery" + depends on SA1100_COLLIE && MCP_UCB1200 + help + Say Y to enable support for the battery on the Sharp Zaurus + SL-5500 (collie) models. + config BATTERY_WM97XX bool "WM97xx generic battery driver" depends on TOUCHSCREEN_WM97XX=y diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b96f29d91c28..356cdfd3c8b2 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o +obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c new file mode 100644 index 000000000000..039f41ae217d --- /dev/null +++ b/drivers/power/collie_battery.c @@ -0,0 +1,418 @@ +/* + * Battery and Power Management code for the Sharp SL-5x00 + * + * Copyright (C) 2009 Thomas Kunze + * + * based on tosa_battery.c + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/mfd/ucb1x00.h> + +#include <asm/mach/sharpsl_param.h> +#include <asm/mach-types.h> +#include <mach/collie.h> + +static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ +static struct work_struct bat_work; +static struct ucb1x00 *ucb; + +struct collie_bat { + int status; + struct power_supply psy; + int full_chrg; + + struct mutex work_lock; /* protects data */ + + bool (*is_present)(struct collie_bat *bat); + int gpio_full; + int gpio_charge_on; + + int technology; + + int gpio_bat; + int adc_bat; + int adc_bat_divider; + int bat_max; + int bat_min; + + int gpio_temp; + int adc_temp; + int adc_temp_divider; +}; + +static struct collie_bat collie_bat_main; + +static unsigned long collie_read_bat(struct collie_bat *bat) +{ + unsigned long value = 0; + + if (bat->gpio_bat < 0 || bat->adc_bat < 0) + return 0; + mutex_lock(&bat_lock); + gpio_set_value(bat->gpio_bat, 1); + msleep(5); + ucb1x00_adc_enable(ucb); + value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC); + ucb1x00_adc_disable(ucb); + gpio_set_value(bat->gpio_bat, 0); + mutex_unlock(&bat_lock); + value = value * 1000000 / bat->adc_bat_divider; + + return value; +} + +static unsigned long collie_read_temp(struct collie_bat *bat) +{ + unsigned long value = 0; + if (bat->gpio_temp < 0 || bat->adc_temp < 0) + return 0; + + mutex_lock(&bat_lock); + gpio_set_value(bat->gpio_temp, 1); + msleep(5); + ucb1x00_adc_enable(ucb); + value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC); + ucb1x00_adc_disable(ucb); + gpio_set_value(bat->gpio_temp, 0); + mutex_unlock(&bat_lock); + + value = value * 10000 / bat->adc_temp_divider; + + return value; +} + +static int collie_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct collie_bat *bat = container_of(psy, struct collie_bat, psy); + + if (bat->is_present && !bat->is_present(bat) + && psp != POWER_SUPPLY_PROP_PRESENT) { + return -ENODEV; + } + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = bat->status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = bat->technology; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = collie_read_bat(bat); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + if (bat->full_chrg == -1) + val->intval = bat->bat_max; + else + val->intval = bat->full_chrg; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = bat->bat_max; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = bat->bat_min; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = collie_read_temp(bat); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bat->is_present ? bat->is_present(bat) : 1; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static void collie_bat_external_power_changed(struct power_supply *psy) +{ + schedule_work(&bat_work); +} + +static irqreturn_t collie_bat_gpio_isr(int irq, void *data) +{ + pr_info("collie_bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq))); + schedule_work(&bat_work); + return IRQ_HANDLED; +} + +static void collie_bat_update(struct collie_bat *bat) +{ + int old; + struct power_supply *psy = &bat->psy; + + mutex_lock(&bat->work_lock); + + old = bat->status; + + if (bat->is_present && !bat->is_present(bat)) { + printk(KERN_NOTICE "%s not present\n", psy->name); + bat->status = POWER_SUPPLY_STATUS_UNKNOWN; + bat->full_chrg = -1; + } else if (power_supply_am_i_supplied(psy)) { + if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) { + gpio_set_value(bat->gpio_charge_on, 1); + mdelay(15); + } + + if (gpio_get_value(bat->gpio_full)) { + if (old == POWER_SUPPLY_STATUS_CHARGING || + bat->full_chrg == -1) + bat->full_chrg = collie_read_bat(bat); + + gpio_set_value(bat->gpio_charge_on, 0); + bat->status = POWER_SUPPLY_STATUS_FULL; + } else { + gpio_set_value(bat->gpio_charge_on, 1); + bat->status = POWER_SUPPLY_STATUS_CHARGING; + } + } else { + gpio_set_value(bat->gpio_charge_on, 0); + bat->status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + if (old != bat->status) + power_supply_changed(psy); + + mutex_unlock(&bat->work_lock); +} + +static void collie_bat_work(struct work_struct *work) +{ + collie_bat_update(&collie_bat_main); +} + + +static enum power_supply_property collie_bat_main_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TEMP, +}; + +static enum power_supply_property collie_bat_bu_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_PRESENT, +}; + +static struct collie_bat collie_bat_main = { + .status = POWER_SUPPLY_STATUS_DISCHARGING, + .full_chrg = -1, + .psy = { + .name = "main-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = collie_bat_main_props, + .num_properties = ARRAY_SIZE(collie_bat_main_props), + .get_property = collie_bat_get_property, + .external_power_changed = collie_bat_external_power_changed, + .use_for_apm = 1, + }, + + .gpio_full = COLLIE_GPIO_CO, + .gpio_charge_on = COLLIE_GPIO_CHARGE_ON, + + .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, + + .gpio_bat = COLLIE_GPIO_MBAT_ON, + .adc_bat = UCB_ADC_INP_AD1, + .adc_bat_divider = 155, + .bat_max = 4310000, + .bat_min = 1551 * 1000000 / 414, + + .gpio_temp = COLLIE_GPIO_TMP_ON, + .adc_temp = UCB_ADC_INP_AD0, + .adc_temp_divider = 10000, +}; + +static struct collie_bat collie_bat_bu = { + .status = POWER_SUPPLY_STATUS_UNKNOWN, + .full_chrg = -1, + + .psy = { + .name = "backup-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = collie_bat_bu_props, + .num_properties = ARRAY_SIZE(collie_bat_bu_props), + .get_property = collie_bat_get_property, + .external_power_changed = collie_bat_external_power_changed, + }, + + .gpio_full = -1, + .gpio_charge_on = -1, + + .technology = POWER_SUPPLY_TECHNOLOGY_LiMn, + + .gpio_bat = COLLIE_GPIO_BBAT_ON, + .adc_bat = UCB_ADC_INP_AD1, + .adc_bat_divider = 155, + .bat_max = 3000000, + .bat_min = 1900000, + + .gpio_temp = -1, + .adc_temp = -1, + .adc_temp_divider = -1, +}; + +static struct { + int gpio; + char *name; + bool output; + int value; +} gpios[] = { + { COLLIE_GPIO_CO, "main battery full", 0, 0 }, + { COLLIE_GPIO_MAIN_BAT_LOW, "main battery low", 0, 0 }, + { COLLIE_GPIO_CHARGE_ON, "main charge on", 1, 0 }, + { COLLIE_GPIO_MBAT_ON, "main battery", 1, 0 }, + { COLLIE_GPIO_TMP_ON, "main battery temp", 1, 0 }, + { COLLIE_GPIO_BBAT_ON, "backup battery", 1, 0 }, +}; + +#ifdef CONFIG_PM +static int collie_bat_suspend(struct ucb1x00_dev *dev, pm_message_t state) +{ + /* flush all pending status updates */ + flush_scheduled_work(); + return 0; +} + +static int collie_bat_resume(struct ucb1x00_dev *dev) +{ + /* things may have changed while we were away */ + schedule_work(&bat_work); + return 0; +} +#else +#define collie_bat_suspend NULL +#define collie_bat_resume NULL +#endif + +static int __devinit collie_bat_probe(struct ucb1x00_dev *dev) +{ + int ret; + int i; + + if (!machine_is_collie()) + return -ENODEV; + + ucb = dev->ucb; + + for (i = 0; i < ARRAY_SIZE(gpios); i++) { + ret = gpio_request(gpios[i].gpio, gpios[i].name); + if (ret) { + i--; + goto err_gpio; + } + + if (gpios[i].output) + ret = gpio_direction_output(gpios[i].gpio, + gpios[i].value); + else + ret = gpio_direction_input(gpios[i].gpio); + + if (ret) + goto err_gpio; + } + + mutex_init(&collie_bat_main.work_lock); + + INIT_WORK(&bat_work, collie_bat_work); + + ret = power_supply_register(&dev->ucb->dev, &collie_bat_main.psy); + if (ret) + goto err_psy_reg_main; + ret = power_supply_register(&dev->ucb->dev, &collie_bat_bu.psy); + if (ret) + goto err_psy_reg_bu; + + ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO), + collie_bat_gpio_isr, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "main full", &collie_bat_main); + if (!ret) { + schedule_work(&bat_work); + return 0; + } + power_supply_unregister(&collie_bat_bu.psy); +err_psy_reg_bu: + power_supply_unregister(&collie_bat_main.psy); +err_psy_reg_main: + + /* see comment in collie_bat_remove */ + flush_scheduled_work(); + + i--; +err_gpio: + for (; i >= 0; i--) + gpio_free(gpios[i].gpio); + + return ret; +} + +static void __devexit collie_bat_remove(struct ucb1x00_dev *dev) +{ + int i; + + free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main); + + power_supply_unregister(&collie_bat_bu.psy); + power_supply_unregister(&collie_bat_main.psy); + + /* + * now flush all pending work. + * we won't get any more schedules, since all + * sources (isr and external_power_changed) + * are unregistered now. + */ + flush_scheduled_work(); + + for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--) + gpio_free(gpios[i].gpio); +} + +static struct ucb1x00_driver collie_bat_driver = { + .add = collie_bat_probe, + .remove = __devexit_p(collie_bat_remove), + .suspend = collie_bat_suspend, + .resume = collie_bat_resume, +}; + +static int __init collie_bat_init(void) +{ + return ucb1x00_register_driver(&collie_bat_driver); +} + +static void __exit collie_bat_exit(void) +{ + ucb1x00_unregister_driver(&collie_bat_driver); +} + +module_init(collie_bat_init); +module_exit(collie_bat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Kunze"); +MODULE_DESCRIPTION("Collie battery driver"); diff --git a/drivers/ps3/ps3-sys-manager.c b/drivers/ps3/ps3-sys-manager.c index 88cb74088611..3cbaf1811bd0 100644 --- a/drivers/ps3/ps3-sys-manager.c +++ b/drivers/ps3/ps3-sys-manager.c @@ -46,7 +46,7 @@ /** * struct ps3_sys_manager_header - System manager message header. * @version: Header version, currently 1. - * @size: Header size in bytes, curently 16. + * @size: Header size in bytes, currently 16. * @payload_size: Message payload size in bytes. * @service_id: Message type, one of enum ps3_sys_manager_service_id. * @request_tag: Unique number to identify reply. diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 3c20dae43ce2..f2e1004d12c7 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -509,6 +509,15 @@ config RTC_DRV_M48T59 This driver can also be built as a module, if so, the module will be called "rtc-m48t59". +config RTC_DRV_MSM6242 + tristate "Oki MSM6242" + help + If you say yes here you get support for the Oki MSM6242 + timekeeping chip. It is used in some Amiga models (e.g. A2000). + + This driver can also be built as a module. If so, the module + will be called rtc-msm6242. + config RTC_MXC tristate "Freescale MXC Real Time Clock" depends on ARCH_MXC @@ -529,6 +538,16 @@ config RTC_DRV_BQ4802 This driver can also be built as a module. If so, the module will be called rtc-bq4802. +config RTC_DRV_RP5C01 + tristate "Ricoh RP5C01" + help + If you say yes here you get support for the Ricoh RP5C01 + timekeeping chip. It is used in some Amiga models (e.g. A3000 + and A4000). + + This driver can also be built as a module. If so, the module + will be called rtc-rp5c01. + config RTC_DRV_V3020 tristate "EM Microelectronic V3020" help @@ -780,7 +799,7 @@ config RTC_DRV_TX4939 config RTC_DRV_MV tristate "Marvell SoC RTC" - depends on ARCH_KIRKWOOD + depends on ARCH_KIRKWOOD || ARCH_DOVE help If you say yes here you will get support for the in-chip RTC that can be found in some of Marvell's SoC devices, such as diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index aa3fbd5517a1..af1ba7ae2857 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o obj-$(CONFIG_RTC_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o +obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o @@ -64,6 +65,7 @@ obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o +obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index d490628b64da..1e73c8f42e38 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -201,7 +201,7 @@ static struct platform_driver ds1302_platform_driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, - .remove = __exit_p(ds1302_rtc_remove), + .remove = __devexit_p(ds1302_rtc_remove), }; static int __init ds1302_rtc_init(void) diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 0b6b7730c716..539676e25fd8 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -2,7 +2,7 @@ * An rtc driver for the Dallas DS1511 * * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> - * Copyright (C) 2007 Andrew Sharp <andy.sharp@onstor.com> + * Copyright (C) 2007 Andrew Sharp <andy.sharp@lsi.com> * * 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 @@ -636,7 +636,7 @@ ds1511_rtc_exit(void) module_init(ds1511_rtc_init); module_exit(ds1511_rtc_exit); -MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>"); +MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>"); MODULE_DESCRIPTION("Dallas DS1511 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c new file mode 100644 index 000000000000..5f5968a48925 --- /dev/null +++ b/drivers/rtc/rtc-msm6242.c @@ -0,0 +1,269 @@ +/* + * Oki MSM6242 RTC Driver + * + * Copyright 2009 Geert Uytterhoeven + * + * Based on the A2000 TOD code in arch/m68k/amiga/config.c + * Copyright (C) 1993 Hamish Macdonald + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + + +enum { + MSM6242_SECOND1 = 0x0, /* 1-second digit register */ + MSM6242_SECOND10 = 0x1, /* 10-second digit register */ + MSM6242_MINUTE1 = 0x2, /* 1-minute digit register */ + MSM6242_MINUTE10 = 0x3, /* 10-minute digit register */ + MSM6242_HOUR1 = 0x4, /* 1-hour digit register */ + MSM6242_HOUR10 = 0x5, /* PM/AM, 10-hour digit register */ + MSM6242_DAY1 = 0x6, /* 1-day digit register */ + MSM6242_DAY10 = 0x7, /* 10-day digit register */ + MSM6242_MONTH1 = 0x8, /* 1-month digit register */ + MSM6242_MONTH10 = 0x9, /* 10-month digit register */ + MSM6242_YEAR1 = 0xa, /* 1-year digit register */ + MSM6242_YEAR10 = 0xb, /* 10-year digit register */ + MSM6242_WEEK = 0xc, /* Week register */ + MSM6242_CD = 0xd, /* Control Register D */ + MSM6242_CE = 0xe, /* Control Register E */ + MSM6242_CF = 0xf, /* Control Register F */ +}; + +#define MSM6242_HOUR10_AM (0 << 2) +#define MSM6242_HOUR10_PM (1 << 2) +#define MSM6242_HOUR10_HR_MASK (3 << 0) + +#define MSM6242_WEEK_SUNDAY 0 +#define MSM6242_WEEK_MONDAY 1 +#define MSM6242_WEEK_TUESDAY 2 +#define MSM6242_WEEK_WEDNESDAY 3 +#define MSM6242_WEEK_THURSDAY 4 +#define MSM6242_WEEK_FRIDAY 5 +#define MSM6242_WEEK_SATURDAY 6 + +#define MSM6242_CD_30_S_ADJ (1 << 3) /* 30-second adjustment */ +#define MSM6242_CD_IRQ_FLAG (1 << 2) +#define MSM6242_CD_BUSY (1 << 1) +#define MSM6242_CD_HOLD (1 << 0) + +#define MSM6242_CE_T_MASK (3 << 2) +#define MSM6242_CE_T_64HZ (0 << 2) /* period 1/64 second */ +#define MSM6242_CE_T_1HZ (1 << 2) /* period 1 second */ +#define MSM6242_CE_T_1MINUTE (2 << 2) /* period 1 minute */ +#define MSM6242_CE_T_1HOUR (3 << 2) /* period 1 hour */ + +#define MSM6242_CE_ITRPT_STND (1 << 1) +#define MSM6242_CE_MASK (1 << 0) /* STD.P output control */ + +#define MSM6242_CF_TEST (1 << 3) +#define MSM6242_CF_12H (0 << 2) +#define MSM6242_CF_24H (1 << 2) +#define MSM6242_CF_STOP (1 << 1) +#define MSM6242_CF_REST (1 << 0) /* reset */ + + +struct msm6242_priv { + u32 __iomem *regs; + struct rtc_device *rtc; +}; + +static inline unsigned int msm6242_read(struct msm6242_priv *priv, + unsigned int reg) +{ + return __raw_readl(&priv->regs[reg]) & 0xf; +} + +static inline void msm6242_write(struct msm6242_priv *priv, unsigned int val, + unsigned int reg) +{ + return __raw_writel(val, &priv->regs[reg]); +} + +static inline void msm6242_set(struct msm6242_priv *priv, unsigned int val, + unsigned int reg) +{ + msm6242_write(priv, msm6242_read(priv, reg) | val, reg); +} + +static inline void msm6242_clear(struct msm6242_priv *priv, unsigned int val, + unsigned int reg) +{ + msm6242_write(priv, msm6242_read(priv, reg) & ~val, reg); +} + +static void msm6242_lock(struct msm6242_priv *priv) +{ + int cnt = 5; + + msm6242_set(priv, MSM6242_CD_HOLD, MSM6242_CD); + + while ((msm6242_read(priv, MSM6242_CD) & MSM6242_CD_BUSY) && cnt) { + msm6242_clear(priv, MSM6242_CD_HOLD, MSM6242_CD); + udelay(70); + msm6242_set(priv, MSM6242_CD_HOLD, MSM6242_CD); + cnt--; + } + + if (!cnt) + pr_warning("msm6242: timed out waiting for RTC (0x%x)\n", + msm6242_read(priv, MSM6242_CD)); +} + +static void msm6242_unlock(struct msm6242_priv *priv) +{ + msm6242_clear(priv, MSM6242_CD_HOLD, MSM6242_CD); +} + +static int msm6242_read_time(struct device *dev, struct rtc_time *tm) +{ + struct msm6242_priv *priv = dev_get_drvdata(dev); + + msm6242_lock(priv); + + tm->tm_sec = msm6242_read(priv, MSM6242_SECOND10) * 10 + + msm6242_read(priv, MSM6242_SECOND1); + tm->tm_min = msm6242_read(priv, MSM6242_MINUTE10) * 10 + + msm6242_read(priv, MSM6242_MINUTE1); + tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10 & 3)) * 10 + + msm6242_read(priv, MSM6242_HOUR1); + tm->tm_mday = msm6242_read(priv, MSM6242_DAY10) * 10 + + msm6242_read(priv, MSM6242_DAY1); + tm->tm_wday = msm6242_read(priv, MSM6242_WEEK); + tm->tm_mon = msm6242_read(priv, MSM6242_MONTH10) * 10 + + msm6242_read(priv, MSM6242_MONTH1) - 1; + tm->tm_year = msm6242_read(priv, MSM6242_YEAR10) * 10 + + msm6242_read(priv, MSM6242_YEAR1); + if (tm->tm_year <= 69) + tm->tm_year += 100; + + if (!(msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)) { + unsigned int pm = msm6242_read(priv, MSM6242_HOUR10) & + MSM6242_HOUR10_PM; + if (!pm && tm->tm_hour == 12) + tm->tm_hour = 0; + else if (pm && tm->tm_hour != 12) + tm->tm_hour += 12; + } + + msm6242_unlock(priv); + + return rtc_valid_tm(tm); +} + +static int msm6242_set_time(struct device *dev, struct rtc_time *tm) +{ + struct msm6242_priv *priv = dev_get_drvdata(dev); + + msm6242_lock(priv); + + msm6242_write(priv, tm->tm_sec / 10, MSM6242_SECOND10); + msm6242_write(priv, tm->tm_sec % 10, MSM6242_SECOND1); + msm6242_write(priv, tm->tm_min / 10, MSM6242_MINUTE10); + msm6242_write(priv, tm->tm_min % 10, MSM6242_MINUTE1); + if (msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H) + msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10); + else if (tm->tm_hour >= 12) + msm6242_write(priv, MSM6242_HOUR10_PM + (tm->tm_hour - 12) / 10, + MSM6242_HOUR10); + else + msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10); + msm6242_write(priv, tm->tm_hour % 10, MSM6242_HOUR1); + msm6242_write(priv, tm->tm_mday / 10, MSM6242_DAY10); + msm6242_write(priv, tm->tm_mday % 10, MSM6242_DAY1); + if (tm->tm_wday != -1) + msm6242_write(priv, tm->tm_wday, MSM6242_WEEK); + msm6242_write(priv, (tm->tm_mon + 1) / 10, MSM6242_MONTH10); + msm6242_write(priv, (tm->tm_mon + 1) % 10, MSM6242_MONTH1); + if (tm->tm_year >= 100) + tm->tm_year -= 100; + msm6242_write(priv, tm->tm_year / 10, MSM6242_YEAR10); + msm6242_write(priv, tm->tm_year % 10, MSM6242_YEAR1); + + msm6242_unlock(priv); + return 0; +} + +static const struct rtc_class_ops msm6242_rtc_ops = { + .read_time = msm6242_read_time, + .set_time = msm6242_set_time, +}; + +static int __init msm6242_rtc_probe(struct platform_device *dev) +{ + struct resource *res; + struct msm6242_priv *priv; + struct rtc_device *rtc; + int error; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regs = ioremap(res->start, resource_size(res)); + if (!priv->regs) { + error = -ENOMEM; + goto out_free_priv; + } + + rtc = rtc_device_register("rtc-msm6242", &dev->dev, &msm6242_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) { + error = PTR_ERR(rtc); + goto out_unmap; + } + + priv->rtc = rtc; + platform_set_drvdata(dev, priv); + return 0; + +out_unmap: + iounmap(priv->regs); +out_free_priv: + kfree(priv); + return error; +} + +static int __exit msm6242_rtc_remove(struct platform_device *dev) +{ + struct msm6242_priv *priv = platform_get_drvdata(dev); + + rtc_device_unregister(priv->rtc); + iounmap(priv->regs); + kfree(priv); + return 0; +} + +static struct platform_driver msm6242_rtc_driver = { + .driver = { + .name = "rtc-msm6242", + .owner = THIS_MODULE, + }, + .remove = __exit_p(msm6242_rtc_remove), +}; + +static int __init msm6242_rtc_init(void) +{ + return platform_driver_probe(&msm6242_rtc_driver, msm6242_rtc_probe); +} + +static void __exit msm6242_rtc_fini(void) +{ + platform_driver_unregister(&msm6242_rtc_driver); +} + +module_init(msm6242_rtc_init); +module_exit(msm6242_rtc_fini); + +MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Oki MSM6242 RTC driver"); +MODULE_ALIAS("platform:rtc-msm6242"); diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c new file mode 100644 index 000000000000..e1313feb060f --- /dev/null +++ b/drivers/rtc/rtc-rp5c01.c @@ -0,0 +1,222 @@ +/* + * Ricoh RP5C01 RTC Driver + * + * Copyright 2009 Geert Uytterhoeven + * + * Based on the A3000 TOD code in arch/m68k/amiga/config.c + * Copyright (C) 1993 Hamish Macdonald + */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + + +enum { + RP5C01_1_SECOND = 0x0, /* MODE 00 */ + RP5C01_10_SECOND = 0x1, /* MODE 00 */ + RP5C01_1_MINUTE = 0x2, /* MODE 00 and MODE 01 */ + RP5C01_10_MINUTE = 0x3, /* MODE 00 and MODE 01 */ + RP5C01_1_HOUR = 0x4, /* MODE 00 and MODE 01 */ + RP5C01_10_HOUR = 0x5, /* MODE 00 and MODE 01 */ + RP5C01_DAY_OF_WEEK = 0x6, /* MODE 00 and MODE 01 */ + RP5C01_1_DAY = 0x7, /* MODE 00 and MODE 01 */ + RP5C01_10_DAY = 0x8, /* MODE 00 and MODE 01 */ + RP5C01_1_MONTH = 0x9, /* MODE 00 */ + RP5C01_10_MONTH = 0xa, /* MODE 00 */ + RP5C01_1_YEAR = 0xb, /* MODE 00 */ + RP5C01_10_YEAR = 0xc, /* MODE 00 */ + + RP5C01_12_24_SELECT = 0xa, /* MODE 01 */ + RP5C01_LEAP_YEAR = 0xb, /* MODE 01 */ + + RP5C01_MODE = 0xd, /* all modes */ + RP5C01_TEST = 0xe, /* all modes */ + RP5C01_RESET = 0xf, /* all modes */ +}; + +#define RP5C01_12_24_SELECT_12 (0 << 0) +#define RP5C01_12_24_SELECT_24 (1 << 0) + +#define RP5C01_10_HOUR_AM (0 << 1) +#define RP5C01_10_HOUR_PM (1 << 1) + +#define RP5C01_MODE_TIMER_EN (1 << 3) /* timer enable */ +#define RP5C01_MODE_ALARM_EN (1 << 2) /* alarm enable */ + +#define RP5C01_MODE_MODE_MASK (3 << 0) +#define RP5C01_MODE_MODE00 (0 << 0) /* time */ +#define RP5C01_MODE_MODE01 (1 << 0) /* alarm, 12h/24h, leap year */ +#define RP5C01_MODE_RAM_BLOCK10 (2 << 0) /* RAM 4 bits x 13 */ +#define RP5C01_MODE_RAM_BLOCK11 (3 << 0) /* RAM 4 bits x 13 */ + +#define RP5C01_RESET_1HZ_PULSE (1 << 3) +#define RP5C01_RESET_16HZ_PULSE (1 << 2) +#define RP5C01_RESET_SECOND (1 << 1) /* reset divider stages for */ + /* seconds or smaller units */ +#define RP5C01_RESET_ALARM (1 << 0) /* reset all alarm registers */ + + +struct rp5c01_priv { + u32 __iomem *regs; + struct rtc_device *rtc; +}; + +static inline unsigned int rp5c01_read(struct rp5c01_priv *priv, + unsigned int reg) +{ + return __raw_readl(&priv->regs[reg]) & 0xf; +} + +static inline void rp5c01_write(struct rp5c01_priv *priv, unsigned int val, + unsigned int reg) +{ + return __raw_writel(val, &priv->regs[reg]); +} + +static void rp5c01_lock(struct rp5c01_priv *priv) +{ + rp5c01_write(priv, RP5C01_MODE_MODE00, RP5C01_MODE); +} + +static void rp5c01_unlock(struct rp5c01_priv *priv) +{ + rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01, + RP5C01_MODE); +} + +static int rp5c01_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rp5c01_priv *priv = dev_get_drvdata(dev); + + rp5c01_lock(priv); + + tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 + + rp5c01_read(priv, RP5C01_1_SECOND); + tm->tm_min = rp5c01_read(priv, RP5C01_10_MINUTE) * 10 + + rp5c01_read(priv, RP5C01_1_MINUTE); + tm->tm_hour = rp5c01_read(priv, RP5C01_10_HOUR) * 10 + + rp5c01_read(priv, RP5C01_1_HOUR); + tm->tm_mday = rp5c01_read(priv, RP5C01_10_DAY) * 10 + + rp5c01_read(priv, RP5C01_1_DAY); + tm->tm_wday = rp5c01_read(priv, RP5C01_DAY_OF_WEEK); + tm->tm_mon = rp5c01_read(priv, RP5C01_10_MONTH) * 10 + + rp5c01_read(priv, RP5C01_1_MONTH) - 1; + tm->tm_year = rp5c01_read(priv, RP5C01_10_YEAR) * 10 + + rp5c01_read(priv, RP5C01_1_YEAR); + if (tm->tm_year <= 69) + tm->tm_year += 100; + + rp5c01_unlock(priv); + + return rtc_valid_tm(tm); +} + +static int rp5c01_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rp5c01_priv *priv = dev_get_drvdata(dev); + + rp5c01_lock(priv); + + rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND); + rp5c01_write(priv, tm->tm_sec % 10, RP5C01_1_SECOND); + rp5c01_write(priv, tm->tm_min / 10, RP5C01_10_MINUTE); + rp5c01_write(priv, tm->tm_min % 10, RP5C01_1_MINUTE); + rp5c01_write(priv, tm->tm_hour / 10, RP5C01_10_HOUR); + rp5c01_write(priv, tm->tm_hour % 10, RP5C01_1_HOUR); + rp5c01_write(priv, tm->tm_mday / 10, RP5C01_10_DAY); + rp5c01_write(priv, tm->tm_mday % 10, RP5C01_1_DAY); + if (tm->tm_wday != -1) + rp5c01_write(priv, tm->tm_wday, RP5C01_DAY_OF_WEEK); + rp5c01_write(priv, (tm->tm_mon + 1) / 10, RP5C01_10_MONTH); + rp5c01_write(priv, (tm->tm_mon + 1) % 10, RP5C01_1_MONTH); + if (tm->tm_year >= 100) + tm->tm_year -= 100; + rp5c01_write(priv, tm->tm_year / 10, RP5C01_10_YEAR); + rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR); + + rp5c01_unlock(priv); + return 0; +} + +static const struct rtc_class_ops rp5c01_rtc_ops = { + .read_time = rp5c01_read_time, + .set_time = rp5c01_set_time, +}; + +static int __init rp5c01_rtc_probe(struct platform_device *dev) +{ + struct resource *res; + struct rp5c01_priv *priv; + struct rtc_device *rtc; + int error; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regs = ioremap(res->start, resource_size(res)); + if (!priv->regs) { + error = -ENOMEM; + goto out_free_priv; + } + + rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) { + error = PTR_ERR(rtc); + goto out_unmap; + } + + priv->rtc = rtc; + platform_set_drvdata(dev, priv); + return 0; + +out_unmap: + iounmap(priv->regs); +out_free_priv: + kfree(priv); + return error; +} + +static int __exit rp5c01_rtc_remove(struct platform_device *dev) +{ + struct rp5c01_priv *priv = platform_get_drvdata(dev); + + rtc_device_unregister(priv->rtc); + iounmap(priv->regs); + kfree(priv); + return 0; +} + +static struct platform_driver rp5c01_rtc_driver = { + .driver = { + .name = "rtc-rp5c01", + .owner = THIS_MODULE, + }, + .remove = __exit_p(rp5c01_rtc_remove), +}; + +static int __init rp5c01_rtc_init(void) +{ + return platform_driver_probe(&rp5c01_rtc_driver, rp5c01_rtc_probe); +} + +static void __exit rp5c01_rtc_fini(void) +{ + platform_driver_unregister(&rp5c01_rtc_driver); +} + +module_init(rp5c01_rtc_init); +module_exit(rp5c01_rtc_fini); + +MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Ricoh RP5C01 RTC driver"); +MODULE_ALIAS("platform:rtc-rp5c01"); diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c index 7d1547b0070e..d491eb265c38 100644 --- a/drivers/rtc/rtc-stk17ta8.c +++ b/drivers/rtc/rtc-stk17ta8.c @@ -286,7 +286,7 @@ static struct bin_attribute stk17ta8_nvram_attr = { .write = stk17ta8_nvram_write, }; -static int __init stk17ta8_rtc_probe(struct platform_device *pdev) +static int __devinit stk17ta8_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; struct resource *res; diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c index 423cd5a30b10..ad741afd47d8 100644 --- a/drivers/rtc/rtc-v3020.c +++ b/drivers/rtc/rtc-v3020.c @@ -335,7 +335,7 @@ static int rtc_probe(struct platform_device *pdev) goto err_io; } - /* Make sure frequency measurment mode, test modes, and lock + /* Make sure frequency measurement mode, test modes, and lock * are all disabled */ v3020_set_reg(chip, V3020_STATUS_0, 0x0); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index aaccc8ecfa8f..fdb2e7c14506 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -24,7 +24,6 @@ #include <asm/ccwdev.h> #include <asm/ebcdic.h> #include <asm/idals.h> -#include <asm/todclk.h> #include <asm/itcw.h> /* This is ugly... */ @@ -64,6 +63,7 @@ static void do_restore_device(struct work_struct *); static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); static void dasd_device_timeout(unsigned long); static void dasd_block_timeout(unsigned long); +static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *); /* * SECTION: Operations on the device structure. @@ -960,7 +960,7 @@ static void dasd_device_timeout(unsigned long ptr) device = (struct dasd_device *) ptr; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); /* re-activate request queue */ - device->stopped &= ~DASD_STOPPED_PENDING; + dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); dasd_schedule_device_bh(device); } @@ -994,10 +994,9 @@ static void dasd_handle_killed_request(struct ccw_device *cdev, return; cqr = (struct dasd_ccw_req *) intparm; if (cqr->status != DASD_CQR_IN_IO) { - DBF_EVENT(DBF_DEBUG, - "invalid status in handle_killed_request: " - "bus_id %s, status %02x", - dev_name(&cdev->dev), cqr->status); + DBF_EVENT_DEVID(DBF_DEBUG, cdev, + "invalid status in handle_killed_request: " + "%02x", cqr->status); return; } @@ -1023,7 +1022,7 @@ void dasd_generic_handle_state_change(struct dasd_device *device) /* First of all start sense subsystem status request. */ dasd_eer_snss(device); - device->stopped &= ~DASD_STOPPED_PENDING; + dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING); dasd_schedule_device_bh(device); if (device->block) dasd_schedule_block_bh(device->block); @@ -1045,12 +1044,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, case -EIO: break; case -ETIMEDOUT: - DBF_EVENT(DBF_WARNING, "%s(%s): request timed out\n", - __func__, dev_name(&cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: " + "request timed out\n", __func__); break; default: - DBF_EVENT(DBF_WARNING, "%s(%s): unknown error %ld\n", - __func__, dev_name(&cdev->dev), PTR_ERR(irb)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: " + "unknown error %ld\n", __func__, + PTR_ERR(irb)); } dasd_handle_killed_request(cdev, intparm); return; @@ -1405,6 +1405,20 @@ void dasd_schedule_device_bh(struct dasd_device *device) tasklet_hi_schedule(&device->tasklet); } +void dasd_device_set_stop_bits(struct dasd_device *device, int bits) +{ + device->stopped |= bits; +} +EXPORT_SYMBOL_GPL(dasd_device_set_stop_bits); + +void dasd_device_remove_stop_bits(struct dasd_device *device, int bits) +{ + device->stopped &= ~bits; + if (!device->stopped) + wake_up(&generic_waitq); +} +EXPORT_SYMBOL_GPL(dasd_device_remove_stop_bits); + /* * Queue a request to the head of the device ccw_queue. * Start the I/O if possible. @@ -1465,58 +1479,135 @@ static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr) } /* - * Queue a request to the tail of the device ccw_queue and wait for - * it's completion. + * checks if error recovery is necessary, returns 1 if yes, 0 otherwise. */ -int dasd_sleep_on(struct dasd_ccw_req *cqr) +static int __dasd_sleep_on_erp(struct dasd_ccw_req *cqr) { struct dasd_device *device; - int rc; + dasd_erp_fn_t erp_fn; + if (cqr->status == DASD_CQR_FILLED) + return 0; device = cqr->startdev; + if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) { + if (cqr->status == DASD_CQR_TERMINATED) { + device->discipline->handle_terminated_request(cqr); + return 1; + } + if (cqr->status == DASD_CQR_NEED_ERP) { + erp_fn = device->discipline->erp_action(cqr); + erp_fn(cqr); + return 1; + } + if (cqr->status == DASD_CQR_FAILED) + dasd_log_sense(cqr, &cqr->irb); + if (cqr->refers) { + __dasd_process_erp(device, cqr); + return 1; + } + } + return 0; +} - cqr->callback = dasd_wakeup_cb; - cqr->callback_data = (void *) &generic_waitq; - dasd_add_request_tail(cqr); - wait_event(generic_waitq, _wait_for_wakeup(cqr)); +static int __dasd_sleep_on_loop_condition(struct dasd_ccw_req *cqr) +{ + if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) { + if (cqr->refers) /* erp is not done yet */ + return 1; + return ((cqr->status != DASD_CQR_DONE) && + (cqr->status != DASD_CQR_FAILED)); + } else + return (cqr->status == DASD_CQR_FILLED); +} - if (cqr->status == DASD_CQR_DONE) +static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) +{ + struct dasd_device *device; + int rc; + struct list_head ccw_queue; + struct dasd_ccw_req *cqr; + + INIT_LIST_HEAD(&ccw_queue); + maincqr->status = DASD_CQR_FILLED; + device = maincqr->startdev; + list_add(&maincqr->blocklist, &ccw_queue); + for (cqr = maincqr; __dasd_sleep_on_loop_condition(cqr); + cqr = list_first_entry(&ccw_queue, + struct dasd_ccw_req, blocklist)) { + + if (__dasd_sleep_on_erp(cqr)) + continue; + if (cqr->status != DASD_CQR_FILLED) /* could be failed */ + continue; + + /* Non-temporary stop condition will trigger fail fast */ + if (device->stopped & ~DASD_STOPPED_PENDING && + test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + (!dasd_eer_enabled(device))) { + cqr->status = DASD_CQR_FAILED; + continue; + } + + /* Don't try to start requests if device is stopped */ + if (interruptible) { + rc = wait_event_interruptible( + generic_waitq, !(device->stopped)); + if (rc == -ERESTARTSYS) { + cqr->status = DASD_CQR_FAILED; + maincqr->intrc = rc; + continue; + } + } else + wait_event(generic_waitq, !(device->stopped)); + + cqr->callback = dasd_wakeup_cb; + cqr->callback_data = (void *) &generic_waitq; + dasd_add_request_tail(cqr); + if (interruptible) { + rc = wait_event_interruptible( + generic_waitq, _wait_for_wakeup(cqr)); + if (rc == -ERESTARTSYS) { + dasd_cancel_req(cqr); + /* wait (non-interruptible) for final status */ + wait_event(generic_waitq, + _wait_for_wakeup(cqr)); + cqr->status = DASD_CQR_FAILED; + maincqr->intrc = rc; + continue; + } + } else + wait_event(generic_waitq, _wait_for_wakeup(cqr)); + } + + maincqr->endclk = get_clock(); + if ((maincqr->status != DASD_CQR_DONE) && + (maincqr->intrc != -ERESTARTSYS)) + dasd_log_sense(maincqr, &maincqr->irb); + if (maincqr->status == DASD_CQR_DONE) rc = 0; - else if (cqr->intrc) - rc = cqr->intrc; + else if (maincqr->intrc) + rc = maincqr->intrc; else rc = -EIO; return rc; } /* + * Queue a request to the tail of the device ccw_queue and wait for + * it's completion. + */ +int dasd_sleep_on(struct dasd_ccw_req *cqr) +{ + return _dasd_sleep_on(cqr, 0); +} + +/* * Queue a request to the tail of the device ccw_queue and wait * interruptible for it's completion. */ int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr) { - struct dasd_device *device; - int rc; - - device = cqr->startdev; - cqr->callback = dasd_wakeup_cb; - cqr->callback_data = (void *) &generic_waitq; - dasd_add_request_tail(cqr); - rc = wait_event_interruptible(generic_waitq, _wait_for_wakeup(cqr)); - if (rc == -ERESTARTSYS) { - dasd_cancel_req(cqr); - /* wait (non-interruptible) for final status */ - wait_event(generic_waitq, _wait_for_wakeup(cqr)); - cqr->intrc = rc; - } - - if (cqr->status == DASD_CQR_DONE) - rc = 0; - else if (cqr->intrc) - rc = cqr->intrc; - else - rc = -EIO; - return rc; + return _dasd_sleep_on(cqr, 1); } /* @@ -1630,7 +1721,7 @@ static void dasd_block_timeout(unsigned long ptr) block = (struct dasd_block *) ptr; spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags); /* re-activate request queue */ - block->base->stopped &= ~DASD_STOPPED_PENDING; + dasd_device_remove_stop_bits(block->base, DASD_STOPPED_PENDING); spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags); dasd_schedule_block_bh(block); } @@ -1657,11 +1748,10 @@ void dasd_block_clear_timer(struct dasd_block *block) /* * Process finished error recovery ccw. */ -static inline void __dasd_block_process_erp(struct dasd_block *block, - struct dasd_ccw_req *cqr) +static void __dasd_process_erp(struct dasd_device *device, + struct dasd_ccw_req *cqr) { dasd_erp_fn_t erp_fn; - struct dasd_device *device = block->base; if (cqr->status == DASD_CQR_DONE) DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful"); @@ -1725,9 +1815,12 @@ static void __dasd_process_request_queue(struct dasd_block *block) */ if (!list_empty(&block->ccw_queue)) break; - spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags); - basedev->stopped |= DASD_STOPPED_PENDING; - spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags); + spin_lock_irqsave( + get_ccwdev_lock(basedev->cdev), flags); + dasd_device_set_stop_bits(basedev, + DASD_STOPPED_PENDING); + spin_unlock_irqrestore( + get_ccwdev_lock(basedev->cdev), flags); dasd_block_set_timer(block, HZ/2); break; } @@ -1813,7 +1906,7 @@ restart: cqr->status = DASD_CQR_FILLED; cqr->retries = 255; spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); - base->stopped |= DASD_STOPPED_QUIESCE; + dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); goto restart; @@ -1821,7 +1914,7 @@ restart: /* Process finished ERP request. */ if (cqr->refers) { - __dasd_block_process_erp(block, cqr); + __dasd_process_erp(base, cqr); goto restart; } @@ -1952,7 +2045,7 @@ restart_cb: /* Process finished ERP request. */ if (cqr->refers) { spin_lock_bh(&block->queue_lock); - __dasd_block_process_erp(block, cqr); + __dasd_process_erp(block->base, cqr); spin_unlock_bh(&block->queue_lock); /* restart list_for_xx loop since dasd_process_erp * might remove multiple elements */ @@ -2208,18 +2301,11 @@ int dasd_generic_probe(struct ccw_device *cdev, { int ret; - ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); - if (ret) { - DBF_EVENT(DBF_WARNING, - "dasd_generic_probe: could not set ccw-device options " - "for %s\n", dev_name(&cdev->dev)); - return ret; - } ret = dasd_add_sysfs_files(cdev); if (ret) { - DBF_EVENT(DBF_WARNING, - "dasd_generic_probe: could not add sysfs entries " - "for %s\n", dev_name(&cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", + "dasd_generic_probe: could not add " + "sysfs entries"); return ret; } cdev->handler = &dasd_int_handler; @@ -2418,16 +2504,16 @@ int dasd_generic_notify(struct ccw_device *cdev, int event) cqr->status = DASD_CQR_QUEUED; cqr->retries++; } - device->stopped |= DASD_STOPPED_DC_WAIT; + dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT); dasd_device_clear_timer(device); dasd_schedule_device_bh(device); ret = 1; break; case CIO_OPER: /* FIXME: add a sanity check. */ - device->stopped &= ~DASD_STOPPED_DC_WAIT; + dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT); if (device->stopped & DASD_UNRESUMED_PM) { - device->stopped &= ~DASD_UNRESUMED_PM; + dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM); dasd_restore_device(device); ret = 1; break; @@ -2452,7 +2538,7 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) if (IS_ERR(device)) return PTR_ERR(device); /* disallow new I/O */ - device->stopped |= DASD_STOPPED_PM; + dasd_device_set_stop_bits(device, DASD_STOPPED_PM); /* clear active requests */ INIT_LIST_HEAD(&freeze_queue); spin_lock_irq(get_ccwdev_lock(cdev)); @@ -2504,14 +2590,18 @@ int dasd_generic_restore_device(struct ccw_device *cdev) return PTR_ERR(device); /* allow new IO again */ - device->stopped &= ~DASD_STOPPED_PM; - device->stopped &= ~DASD_UNRESUMED_PM; + dasd_device_remove_stop_bits(device, + (DASD_STOPPED_PM | DASD_UNRESUMED_PM)); dasd_schedule_device_bh(device); - if (device->discipline->restore) + /* + * call discipline restore function + * if device is stopped do nothing e.g. for disconnected devices + */ + if (device->discipline->restore && !(device->stopped)) rc = device->discipline->restore(device); - if (rc) + if (rc || device->stopped) /* * if the resume failed for the DASD we put it in * an UNRESUMED stop state @@ -2561,8 +2651,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, cqr->startdev = device; cqr->memdev = device; cqr->expires = 10*HZ; - clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); - cqr->retries = 2; + cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; return cqr; diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index e8ff7b0c961d..44796ba4eb9b 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -12,7 +12,6 @@ #include <linux/timer.h> #include <linux/slab.h> #include <asm/idals.h> -#include <asm/todclk.h> #define PRINTK_HEADER "dasd_erp(3990): " @@ -70,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) * processing until the started timer has expired or an related * interrupt was received. */ -static void -dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) +static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires) { struct dasd_device *device = erp->startdev; @@ -81,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) "blocking request queue for %is", expires/HZ); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped |= DASD_STOPPED_PENDING; + dasd_device_set_stop_bits(device, DASD_STOPPED_PENDING); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); erp->status = DASD_CQR_FILLED; - dasd_block_set_timer(device->block, expires); + if (erp->block) + dasd_block_set_timer(erp->block, expires); + else + dasd_device_set_timer(device, expires); } /* @@ -243,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) * DESCRIPTION * Setup ERP to do the ERP action 1 (see Reference manual). * Repeat the operation on a different channel path. - * If all alternate paths have been tried, the request is posted with a - * permanent error. - * Note: duplex handling is not implemented (yet). + * As deviation from the recommended recovery action, we reset the path mask + * after we have tried each path and go through all paths a second time. + * This will cover situations where only one path at a time is actually down, + * but all paths fail and recover just with the same sequence and timing as + * we try to use them (flapping links). + * If all alternate paths have been tried twice, the request is posted with + * a permanent error. * * PARAMETER * erp pointer to the current ERP @@ -254,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) * erp pointer to the ERP * */ -static struct dasd_ccw_req * -dasd_3990_erp_action_1(struct dasd_ccw_req * erp) +static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp) { + erp->function = dasd_3990_erp_action_1_sec; + dasd_3990_erp_alternate_path(erp); + return erp; +} +static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp) +{ erp->function = dasd_3990_erp_action_1; - dasd_3990_erp_alternate_path(erp); - + if (erp->status == DASD_CQR_FAILED) { + erp->status = DASD_CQR_FILLED; + erp->retries = 10; + erp->lpm = LPM_ANYPATH; + erp->function = dasd_3990_erp_action_1_sec; + } return erp; - -} /* end dasd_3990_erp_action_1 */ +} /* end dasd_3990_erp_action_1(b) */ /* * DASD_3990_ERP_ACTION_4 @@ -2295,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) return cqr; } + ccw = cqr->cpaddr; if (cqr->cpmode == 1) { /* make a shallow copy of the original tcw but set new tsb */ erp->cpmode = 1; @@ -2303,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) tsb = (struct tsb *) &tcw[1]; *tcw = *((struct tcw *)cqr->cpaddr); tcw->tsb = (long)tsb; + } else if (ccw->cmd_code == DASD_ECKD_CCW_PSF) { + /* PSF cannot be chained from NOOP/TIC */ + erp->cpaddr = cqr->cpaddr; } else { /* initialize request with default TIC to current ERP/CQR */ ccw = erp->cpaddr; @@ -2487,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) erp = dasd_3990_erp_action_1(erp); + } else if (erp->function == dasd_3990_erp_action_1_sec) { + erp = dasd_3990_erp_action_1_sec(erp); } else if (erp->function == dasd_3990_erp_action_5) { /* retries have not been successful */ diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 70a008c00522..fd1231738ef4 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -152,6 +152,7 @@ static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid) INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work); INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work); spin_lock_init(&lcu->lock); + init_completion(&lcu->lcu_setup); return lcu; out_err4: @@ -240,6 +241,67 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) } /* + * The first device to be registered on an LCU will have to do + * some additional setup steps to configure that LCU on the + * storage server. All further devices should wait with their + * initialization until the first device is done. + * To synchronize this work, the first device will call + * dasd_alias_lcu_setup_complete when it is done, and all + * other devices will wait for it with dasd_alias_wait_for_lcu_setup. + */ +void dasd_alias_lcu_setup_complete(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + unsigned long flags; + struct alias_server *server; + struct alias_lcu *lcu; + struct dasd_uid *uid; + + private = (struct dasd_eckd_private *) device->private; + uid = &private->uid; + lcu = NULL; + spin_lock_irqsave(&aliastree.lock, flags); + server = _find_server(uid); + if (server) + lcu = _find_lcu(server, uid); + spin_unlock_irqrestore(&aliastree.lock, flags); + if (!lcu) { + DBF_EVENT_DEVID(DBF_ERR, device->cdev, + "could not find lcu for %04x %02x", + uid->ssid, uid->real_unit_addr); + WARN_ON(1); + return; + } + complete_all(&lcu->lcu_setup); +} + +void dasd_alias_wait_for_lcu_setup(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + unsigned long flags; + struct alias_server *server; + struct alias_lcu *lcu; + struct dasd_uid *uid; + + private = (struct dasd_eckd_private *) device->private; + uid = &private->uid; + lcu = NULL; + spin_lock_irqsave(&aliastree.lock, flags); + server = _find_server(uid); + if (server) + lcu = _find_lcu(server, uid); + spin_unlock_irqrestore(&aliastree.lock, flags); + if (!lcu) { + DBF_EVENT_DEVID(DBF_ERR, device->cdev, + "could not find lcu for %04x %02x", + uid->ssid, uid->real_unit_addr); + WARN_ON(1); + return; + } + wait_for_completion(&lcu->lcu_setup); +} + +/* * This function removes a device from the scope of alias management. * The complicated part is to make sure that it is not in use by * any of the workers. If necessary cancel the work. @@ -755,11 +817,11 @@ static void __stop_device_on_lcu(struct dasd_device *device, { /* If pos == device then device is already locked! */ if (pos == device) { - pos->stopped |= DASD_STOPPED_SU; + dasd_device_set_stop_bits(pos, DASD_STOPPED_SU); return; } spin_lock(get_ccwdev_lock(pos->cdev)); - pos->stopped |= DASD_STOPPED_SU; + dasd_device_set_stop_bits(pos, DASD_STOPPED_SU); spin_unlock(get_ccwdev_lock(pos->cdev)); } @@ -793,26 +855,26 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu) list_for_each_entry(device, &lcu->active_devices, alias_list) { spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_SU; + dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } list_for_each_entry(device, &lcu->inactive_devices, alias_list) { spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_SU; + dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } list_for_each_entry(pavgroup, &lcu->grouplist, group) { list_for_each_entry(device, &pavgroup->baselist, alias_list) { spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_SU; + dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_SU; + dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } @@ -836,7 +898,8 @@ static void summary_unit_check_handling_work(struct work_struct *work) /* 2. reset summary unit check */ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING); + dasd_device_remove_stop_bits(device, + (DASD_STOPPED_SU | DASD_STOPPED_PENDING)); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); reset_summary_unit_check(lcu, device, suc_data->reason); diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 4e49b4a6c880..f64d0db881b4 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -24,7 +24,6 @@ #include <asm/ebcdic.h> #include <asm/io.h> #include <asm/s390_ext.h> -#include <asm/todclk.h> #include <asm/vtoc.h> #include <asm/diag.h> @@ -145,6 +144,15 @@ dasd_diag_erp(struct dasd_device *device) mdsk_term_io(device); rc = mdsk_init_io(device, device->block->bp_block, 0, NULL); + if (rc == 4) { + if (!(device->features & DASD_FEATURE_READONLY)) { + dev_warn(&device->cdev->dev, + "The access mode of a DIAG device changed" + " to read-only"); + device->features |= DASD_FEATURE_READONLY; + } + rc = 0; + } if (rc) dev_warn(&device->cdev->dev, "DIAG ERP failed with " "rc=%d\n", rc); @@ -433,16 +441,20 @@ dasd_diag_check_device(struct dasd_device *device) for (sb = 512; sb < bsize; sb = sb << 1) block->s2b_shift++; rc = mdsk_init_io(device, block->bp_block, 0, NULL); - if (rc) { + if (rc && (rc != 4)) { dev_warn(&device->cdev->dev, "DIAG initialization " "failed with rc=%d\n", rc); rc = -EIO; } else { + if (rc == 4) + device->features |= DASD_FEATURE_READONLY; dev_info(&device->cdev->dev, - "New DASD with %ld byte/block, total size %ld KB\n", + "New DASD with %ld byte/block, total size %ld KB%s\n", (unsigned long) block->bp_block, (unsigned long) (block->blocks << - block->s2b_shift) >> 1); + block->s2b_shift) >> 1, + (rc == 4) ? ", read-only device" : ""); + rc = 0; } out_label: free_page((long) label); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 417b97cd3f94..5819dc02a143 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -24,7 +24,6 @@ #include <asm/idals.h> #include <asm/ebcdic.h> #include <asm/io.h> -#include <asm/todclk.h> #include <asm/uaccess.h> #include <asm/cio.h> #include <asm/ccwdev.h> @@ -78,6 +77,11 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids); static struct ccw_driver dasd_eckd_driver; /* see below */ +#define INIT_CQR_OK 0 +#define INIT_CQR_UNFORMATTED 1 +#define INIT_CQR_ERROR 2 + + /* initial attempt at a probe function. this can be simplified once * the other detection code is gone */ static int @@ -86,11 +90,12 @@ dasd_eckd_probe (struct ccw_device *cdev) int ret; /* set ECKD specific ccw-device options */ - ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE); + ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE | + CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH); if (ret) { - DBF_EVENT(DBF_WARNING, - "dasd_eckd_probe: could not set ccw-device options " - "for %s\n", dev_name(&cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", + "dasd_eckd_probe: could not set " + "ccw-device options"); return ret; } ret = dasd_generic_probe(cdev, &dasd_eckd_discipline); @@ -749,8 +754,7 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, cqr->block = NULL; cqr->expires = 10*HZ; cqr->lpm = lpm; - clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); - cqr->retries = 2; + cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -885,16 +889,15 @@ static int dasd_eckd_read_conf(struct dasd_device *device) rc = dasd_eckd_read_conf_lpm(device, &conf_data, &conf_len, lpm); if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ - DBF_EVENT(DBF_WARNING, + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "Read configuration data returned " - "error %d for device: %s", rc, - dev_name(&device->cdev->dev)); + "error %d", rc); return rc; } if (conf_data == NULL) { - DBF_EVENT(DBF_WARNING, "No configuration " - "data retrieved for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "No configuration data " + "retrieved"); continue; /* no error */ } /* save first valid configuration data */ @@ -941,16 +944,14 @@ static int dasd_eckd_read_features(struct dasd_device *device) sizeof(struct dasd_rssd_features)), device); if (IS_ERR(cqr)) { - DBF_EVENT(DBF_WARNING, "Could not allocate initialization " - "request for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not " + "allocate initialization request"); return PTR_ERR(cqr); } cqr->startdev = device; cqr->memdev = device; cqr->block = NULL; - clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); - cqr->retries = 5; + cqr->retries = 256; cqr->expires = 10 * HZ; /* Prepare for Read Subsystem Data */ @@ -1012,9 +1013,9 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, } psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; psf_ssc_data->order = PSF_ORDER_SSC; - psf_ssc_data->suborder = 0x40; + psf_ssc_data->suborder = 0xc0; if (enable_pav) { - psf_ssc_data->suborder |= 0x88; + psf_ssc_data->suborder |= 0x08; psf_ssc_data->reserved[0] = 0x88; } ccw = cqr->cpaddr; @@ -1025,6 +1026,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, cqr->startdev = device; cqr->memdev = device; cqr->block = NULL; + cqr->retries = 256; cqr->expires = 10*HZ; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -1057,7 +1059,7 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav) /* * Valide storage server of current device. */ -static int dasd_eckd_validate_server(struct dasd_device *device) +static void dasd_eckd_validate_server(struct dasd_device *device) { int rc; struct dasd_eckd_private *private; @@ -1068,15 +1070,12 @@ static int dasd_eckd_validate_server(struct dasd_device *device) else enable_pav = 1; rc = dasd_eckd_psf_ssc(device, enable_pav); + /* may be requested feature is not available on server, * therefore just report error and go ahead */ private = (struct dasd_eckd_private *) device->private; - DBF_EVENT(DBF_WARNING, "PSF-SSC on storage subsystem %s.%s.%04x " - "returned rc=%d for device: %s", - private->uid.vendor, private->uid.serial, - private->uid.ssid, rc, dev_name(&device->cdev->dev)); - /* RE-Read Configuration Data */ - return dasd_eckd_read_conf(device); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x " + "returned rc=%d", private->uid.ssid, rc); } /* @@ -1090,6 +1089,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device) struct dasd_block *block; int is_known, rc; + if (!ccw_device_is_pathgroup(device->cdev)) { + dev_warn(&device->cdev->dev, + "A channel path group could not be established\n"); + return -EIO; + } + if (!ccw_device_is_multipath(device->cdev)) { + dev_info(&device->cdev->dev, + "The DASD is not operating in multipath mode\n"); + } private = (struct dasd_eckd_private *) device->private; if (!private) { private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); @@ -1123,9 +1131,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) if (private->uid.type == UA_BASE_DEVICE) { block = dasd_alloc_block(); if (IS_ERR(block)) { - DBF_EVENT(DBF_WARNING, "could not allocate dasd " - "block structure for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "could not allocate dasd " + "block structure"); rc = PTR_ERR(block); goto out_err1; } @@ -1139,12 +1147,21 @@ dasd_eckd_check_characteristics(struct dasd_device *device) rc = is_known; goto out_err2; } + /* + * dasd_eckd_vaildate_server is done on the first device that + * is found for an LCU. All later other devices have to wait + * for it, so they will read the correct feature codes. + */ if (!is_known) { - /* new lcu found */ - rc = dasd_eckd_validate_server(device); /* will switch pav on */ - if (rc) - goto out_err3; - } + dasd_eckd_validate_server(device); + dasd_alias_lcu_setup_complete(device); + } else + dasd_alias_wait_for_lcu_setup(device); + + /* device may report different configuration data after LCU setup */ + rc = dasd_eckd_read_conf(device); + if (rc) + goto out_err3; /* Read Feature Codes */ dasd_eckd_read_features(device); @@ -1153,9 +1170,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device) rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, &private->rdc_data, 64); if (rc) { - DBF_EVENT(DBF_WARNING, - "Read device characteristics failed, rc=%d for " - "device: %s", rc, dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Read device characteristic failed, rc=%d", rc); goto out_err3; } /* find the vaild cylinder size */ @@ -1256,12 +1272,29 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) cqr->block = NULL; cqr->startdev = device; cqr->memdev = device; - cqr->retries = 0; + cqr->retries = 255; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } +/* differentiate between 'no record found' and any other error */ +static int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr) +{ + char *sense; + if (init_cqr->status == DASD_CQR_DONE) + return INIT_CQR_OK; + else if (init_cqr->status == DASD_CQR_NEED_ERP || + init_cqr->status == DASD_CQR_FAILED) { + sense = dasd_get_sense(&init_cqr->irb); + if (sense && (sense[1] & SNS1_NO_REC_FOUND)) + return INIT_CQR_UNFORMATTED; + else + return INIT_CQR_ERROR; + } else + return INIT_CQR_ERROR; +} + /* * This is the callback function for the init_analysis cqr. It saves * the status of the initial analysis ccw before it frees it and kicks @@ -1269,21 +1302,20 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) * dasd_eckd_do_analysis again (if the devices has not been marked * for deletion in the meantime). */ -static void -dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) +static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, + void *data) { struct dasd_eckd_private *private; struct dasd_device *device; device = init_cqr->startdev; private = (struct dasd_eckd_private *) device->private; - private->init_cqr_status = init_cqr->status; + private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr); dasd_sfree_request(init_cqr, device); dasd_kick_device(device); } -static int -dasd_eckd_start_analysis(struct dasd_block *block) +static int dasd_eckd_start_analysis(struct dasd_block *block) { struct dasd_eckd_private *private; struct dasd_ccw_req *init_cqr; @@ -1295,27 +1327,44 @@ dasd_eckd_start_analysis(struct dasd_block *block) init_cqr->callback = dasd_eckd_analysis_callback; init_cqr->callback_data = NULL; init_cqr->expires = 5*HZ; + /* first try without ERP, so we can later handle unformatted + * devices as special case + */ + clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags); + init_cqr->retries = 0; dasd_add_request_head(init_cqr); return -EAGAIN; } -static int -dasd_eckd_end_analysis(struct dasd_block *block) +static int dasd_eckd_end_analysis(struct dasd_block *block) { struct dasd_device *device; struct dasd_eckd_private *private; struct eckd_count *count_area; unsigned int sb, blk_per_trk; int status, i; + struct dasd_ccw_req *init_cqr; device = block->base; private = (struct dasd_eckd_private *) device->private; status = private->init_cqr_status; private->init_cqr_status = -1; - if (status != DASD_CQR_DONE) { - dev_warn(&device->cdev->dev, - "The DASD is not formatted\n"); + if (status == INIT_CQR_ERROR) { + /* try again, this time with full ERP */ + init_cqr = dasd_eckd_analysis_ccw(device); + dasd_sleep_on(init_cqr); + status = dasd_eckd_analysis_evaluation(init_cqr); + dasd_sfree_request(init_cqr, device); + } + + if (status == INIT_CQR_UNFORMATTED) { + dev_warn(&device->cdev->dev, "The DASD is not formatted\n"); return -EMEDIUMTYPE; + } else if (status == INIT_CQR_ERROR) { + dev_err(&device->cdev->dev, + "Detecting the DASD disk layout failed because " + "of an I/O error\n"); + return -EIO; } private->uses_cdl = 1; @@ -1607,8 +1656,7 @@ dasd_eckd_format_device(struct dasd_device * device, } fcp->startdev = device; fcp->memdev = device; - clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags); - fcp->retries = 5; /* set retry counter to enable default ERP */ + fcp->retries = 256; fcp->buildclk = get_clock(); fcp->status = DASD_CQR_FILLED; return fcp; @@ -2690,6 +2738,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) cqr->startdev = device; cqr->memdev = device; cqr->retries = 0; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); cqr->expires = 10 * HZ; /* Prepare for Read Subsystem Data */ @@ -3240,11 +3289,15 @@ int dasd_eckd_restore_device(struct dasd_device *device) if (is_known < 0) return is_known; if (!is_known) { - /* new lcu found */ - rc = dasd_eckd_validate_server(device); /* will switch pav on */ - if (rc) - goto out_err; - } + dasd_eckd_validate_server(device); + dasd_alias_lcu_setup_complete(device); + } else + dasd_alias_wait_for_lcu_setup(device); + + /* RE-Read Configuration Data */ + rc = dasd_eckd_read_conf(device); + if (rc) + goto out_err; /* Read Feature Codes */ dasd_eckd_read_features(device); @@ -3253,9 +3306,8 @@ int dasd_eckd_restore_device(struct dasd_device *device) rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, &temp_rdc_data, 64); if (rc) { - DBF_EVENT(DBF_WARNING, - "Read device characteristics failed, rc=%d for " - "device: %s", rc, dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Read device characteristic failed, rc=%d", rc); goto out_err; } spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index ad45bcac3ce4..864d53c04201 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -414,6 +414,7 @@ struct alias_lcu { struct summary_unit_check_work_data suc_data; struct read_uac_work_data ruac_data; struct dasd_ccw_req *rsu_cqr; + struct completion lcu_setup; }; struct alias_pav_group { @@ -460,5 +461,6 @@ int dasd_alias_remove_device(struct dasd_device *); struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *); void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *); void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); - +void dasd_alias_lcu_setup_complete(struct dasd_device *); +void dasd_alias_wait_for_lcu_setup(struct dasd_device *); #endif /* DASD_ECKD_H */ diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index d96039eae59b..1f3e967aaba8 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -536,7 +536,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL); if (!eerb) return -ENOMEM; - lock_kernel(); eerb->buffer_page_count = eer_pages; if (eerb->buffer_page_count < 1 || eerb->buffer_page_count > INT_MAX / PAGE_SIZE) { @@ -544,7 +543,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) DBF_EVENT(DBF_WARNING, "can't open device since module " "parameter eer_pages is smaller than 1 or" " bigger than %d", (int)(INT_MAX / PAGE_SIZE)); - unlock_kernel(); return -EINVAL; } eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE; @@ -552,14 +550,12 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) GFP_KERNEL); if (!eerb->buffer) { kfree(eerb); - unlock_kernel(); return -ENOMEM; } if (dasd_eer_allocate_buffer_pages(eerb->buffer, eerb->buffer_page_count)) { kfree(eerb->buffer); kfree(eerb); - unlock_kernel(); return -ENOMEM; } filp->private_data = eerb; @@ -567,7 +563,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) list_add(&eerb->list, &bufferlist); spin_unlock_irqrestore(&bufferlock, flags); - unlock_kernel(); return nonseekable_open(inp,filp); } diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index f245377e8e27..0f152444ac77 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -20,7 +20,6 @@ #include <asm/idals.h> #include <asm/ebcdic.h> #include <asm/io.h> -#include <asm/todclk.h> #include <asm/ccwdev.h> #include "dasd_int.h" @@ -141,9 +140,8 @@ dasd_fba_check_characteristics(struct dasd_device *device) } block = dasd_alloc_block(); if (IS_ERR(block)) { - DBF_EVENT(DBF_WARNING, "could not allocate dasd block " - "structure for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate " + "dasd block structure"); device->private = NULL; kfree(private); return PTR_ERR(block); @@ -155,9 +153,8 @@ dasd_fba_check_characteristics(struct dasd_device *device) rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, &private->rdc_data, 32); if (rc) { - DBF_EVENT(DBF_WARNING, "Read device characteristics returned " - "error %d for device: %s", - rc, dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device " + "characteristics returned error %d", rc); device->block = NULL; dasd_free_block(block); device->private = NULL; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 8afd9fa00875..e4c2143dabf6 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -108,6 +108,16 @@ do { \ d_data); \ } while(0) +#define DBF_EVENT_DEVID(d_level, d_cdev, d_str, d_data...) \ +do { \ + struct ccw_dev_id __dev_id; \ + ccw_device_get_id(d_cdev, &__dev_id); \ + debug_sprintf_event(dasd_debug_area, \ + d_level, \ + "0.%x.%04x " d_str "\n", \ + __dev_id.ssid, __dev_id.devno, d_data); \ +} while (0) + #define DBF_EXC(d_level, d_str, d_data...)\ do { \ debug_sprintf_exception(dasd_debug_area, \ @@ -595,6 +605,9 @@ int dasd_generic_restore_device(struct ccw_device *); int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int); char *dasd_get_sense(struct irb *); +void dasd_device_set_stop_bits(struct dasd_device *, int); +void dasd_device_remove_stop_bits(struct dasd_device *, int); + /* externals in dasd_devmap.c */ extern int dasd_max_devindex; extern int dasd_probeonly; diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index f756a1b0c57a..478bcdb90b6f 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block) pr_info("%s: The DASD has been put in the quiesce " "state\n", dev_name(&base->cdev->dev)); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); - base->stopped |= DASD_STOPPED_QUIESCE; + dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); return 0; } @@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block) pr_info("%s: I/O operations have been resumed " "on the DASD\n", dev_name(&base->cdev->dev)); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); - base->stopped &= ~DASD_STOPPED_QUIESCE; + dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); dasd_schedule_block_bh(block); diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 654daa3cdfda..5f23eca82804 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -215,7 +215,7 @@ dasd_statistics_read(char *page, char **start, off_t off, } prof = &dasd_global_profile; - /* prevent couter 'overflow' on output */ + /* prevent counter 'overflow' on output */ for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999; factor *= 10); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 21639d6c996f..9d61683b5633 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -857,7 +857,6 @@ static struct console con3215 = { /* * 3215 console initialization code called from console_init(). - * NOTE: This is called before kmalloc is available. */ static int __init con3215_init(void) { diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index bb838bdf829d..6bca81aea396 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -572,7 +572,6 @@ static struct console con3270 = { /* * 3270 console initialization code called from console_init(). - * NOTE: This is called before kmalloc is available. */ static int __init con3270_init(void) diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 097d3846a828..28e4649fa9e4 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -38,6 +38,8 @@ struct fs3270 { size_t rdbuf_size; /* size of data returned by RDBUF */ }; +static DEFINE_MUTEX(fs3270_mutex); + static void fs3270_wake_up(struct raw3270_request *rq, void *data) { @@ -74,7 +76,7 @@ fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) } rc = raw3270_start(view, rq); if (rc == 0) { - /* Started sucessfully. Now wait for completion. */ + /* Started successfully. Now wait for completion. */ wait_event(fp->wait, raw3270_request_final(rq)); } } while (rc == -EACCES); @@ -328,7 +330,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!fp) return -ENODEV; rc = 0; - lock_kernel(); + mutex_lock(&fs3270_mutex); switch (cmd) { case TUBICMD: fp->read_command = arg; @@ -354,7 +356,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) rc = -EFAULT; break; } - unlock_kernel(); + mutex_unlock(&fs3270_mutex); return rc; } @@ -437,7 +439,7 @@ fs3270_open(struct inode *inode, struct file *filp) minor = tty->index + RAW3270_FIRSTMINOR; tty_kref_put(tty); } - lock_kernel(); + mutex_lock(&fs3270_mutex); /* Check if some other program is already using fullscreen mode. */ fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); if (!IS_ERR(fp)) { @@ -478,7 +480,7 @@ fs3270_open(struct inode *inode, struct file *filp) } filp->private_data = fp; out: - unlock_kernel(); + mutex_unlock(&fs3270_mutex); return rc; } diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 66e21dd23154..60473f86e1f9 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -12,7 +12,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/kernel.h> @@ -283,7 +282,6 @@ static int mon_open(struct inode *inode, struct file *filp) /* * only one user allowed */ - lock_kernel(); rc = -EBUSY; if (test_and_set_bit(MON_IN_USE, &mon_in_use)) goto out; @@ -321,7 +319,6 @@ static int mon_open(struct inode *inode, struct file *filp) } filp->private_data = monpriv; dev_set_drvdata(monreader_device, monpriv); - unlock_kernel(); return nonseekable_open(inode, filp); out_path: @@ -331,7 +328,6 @@ out_priv: out_use: clear_bit(MON_IN_USE, &mon_in_use); out: - unlock_kernel(); return rc; } @@ -607,6 +603,10 @@ static int __init mon_init(void) } dcss_mkname(mon_dcss_name, &user_data_connect[8]); + /* + * misc_register() has to be the last action in module_init(), because + * file operations will be available right after this. + */ rc = misc_register(&mon_dev); if (rc < 0 ) goto out; diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 66fb8eba93f4..6532ed8b4afa 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -13,7 +13,6 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/errno.h> -#include <linux/smp_lock.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/miscdevice.h> @@ -185,13 +184,11 @@ static int monwrite_open(struct inode *inode, struct file *filp) monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL); if (!monpriv) return -ENOMEM; - lock_kernel(); INIT_LIST_HEAD(&monpriv->list); monpriv->hdr_to_read = sizeof(monpriv->hdr); mutex_init(&monpriv->thread_mutex); filp->private_data = monpriv; list_add_tail(&monpriv->priv_list, &mon_priv_list); - unlock_kernel(); return nonseekable_open(inode, filp); } @@ -364,6 +361,10 @@ static int __init mon_init(void) goto out_driver; } + /* + * misc_register() has to be the last action in module_init(), because + * file operations will be available right after this. + */ rc = misc_register(&mon_dev); if (rc) goto out_device; diff --git a/drivers/s390/char/sclp_async.c b/drivers/s390/char/sclp_async.c index b44462a6c6d3..740fe405c395 100644 --- a/drivers/s390/char/sclp_async.c +++ b/drivers/s390/char/sclp_async.c @@ -101,18 +101,17 @@ static struct ctl_table callhome_table[] = { .mode = 0644, .proc_handler = proc_handler_callhome, }, - { .ctl_name = 0 } + {} }; static struct ctl_table kern_dir_table[] = { { - .ctl_name = CTL_KERN, .procname = "kernel", .maxlen = 0, .mode = 0555, .child = callhome_table, }, - { .ctl_name = 0 } + {} }; /* diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 5cc11c636d38..28b5afc129c3 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -84,6 +84,7 @@ static void __init sclp_read_info_early(void) do { memset(sccb, 0, sizeof(*sccb)); sccb->header.length = sizeof(*sccb); + sccb->header.function_code = 0x80; sccb->header.control_mask[2] = 0x80; rc = sclp_cmd_sync_early(commands[i], sccb); } while (rc == -EBUSY); diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index a26333774701..7a242f073632 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -212,6 +212,9 @@ struct tape_device { struct tape_class_device * nt; struct tape_class_device * rt; + /* Device mutex to serialize tape commands. */ + struct mutex mutex; + /* Device discipline information. */ struct tape_discipline * discipline; void * discdata; @@ -292,9 +295,9 @@ extern int tape_generic_pm_suspend(struct ccw_device *); extern int tape_generic_probe(struct ccw_device *); extern void tape_generic_remove(struct ccw_device *); -extern struct tape_device *tape_get_device(int devindex); -extern struct tape_device *tape_get_device_reference(struct tape_device *); -extern struct tape_device *tape_put_device(struct tape_device *); +extern struct tape_device *tape_find_device(int devindex); +extern struct tape_device *tape_get_device(struct tape_device *); +extern void tape_put_device(struct tape_device *); /* Externals from tape_char.c */ extern int tapechar_init(void); diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 2fe45ff77b75..3657fe103c27 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -113,16 +113,16 @@ tape_34xx_work_handler(struct work_struct *work) { struct tape_34xx_work *p = container_of(work, struct tape_34xx_work, work); + struct tape_device *device = p->device; switch(p->op) { case TO_MSEN: - tape_34xx_medium_sense(p->device); + tape_34xx_medium_sense(device); break; default: DBF_EVENT(3, "T34XX: internal error: unknown work\n"); } - - p->device = tape_put_device(p->device); + tape_put_device(device); kfree(p); } @@ -136,7 +136,7 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op) INIT_WORK(&p->work, tape_34xx_work_handler); - p->device = tape_get_device_reference(device); + p->device = tape_get_device(device); p->op = op; schedule_work(&p->work); diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index e4cc3aae9162..0c72aadb8391 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -608,7 +608,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op) INIT_WORK(&p->work, tape_3590_work_handler); - p->device = tape_get_device_reference(device); + p->device = tape_get_device(device); p->op = op; schedule_work(&p->work); diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index 0c0705b91c28..4799cc2f73c3 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -54,7 +54,7 @@ static const struct block_device_operations tapeblock_fops = { .owner = THIS_MODULE, .open = tapeblock_open, .release = tapeblock_release, - .locked_ioctl = tapeblock_ioctl, + .ioctl = tapeblock_ioctl, .media_changed = tapeblock_medium_changed, .revalidate_disk = tapeblock_revalidate_disk, }; @@ -239,7 +239,7 @@ tapeblock_setup_device(struct tape_device * device) disk->major = tapeblock_major; disk->first_minor = device->first_minor; disk->fops = &tapeblock_fops; - disk->private_data = tape_get_device_reference(device); + disk->private_data = tape_get_device(device); disk->queue = blkdat->request_queue; set_capacity(disk, 0); sprintf(disk->disk_name, "btibm%d", @@ -247,11 +247,11 @@ tapeblock_setup_device(struct tape_device * device) blkdat->disk = disk; blkdat->medium_changed = 1; - blkdat->request_queue->queuedata = tape_get_device_reference(device); + blkdat->request_queue->queuedata = tape_get_device(device); add_disk(disk); - tape_get_device_reference(device); + tape_get_device(device); INIT_WORK(&blkdat->requeue_task, tapeblock_requeue); return 0; @@ -274,13 +274,14 @@ tapeblock_cleanup_device(struct tape_device *device) } del_gendisk(device->blk_data.disk); - device->blk_data.disk->private_data = - tape_put_device(device->blk_data.disk->private_data); + device->blk_data.disk->private_data = NULL; + tape_put_device(device); put_disk(device->blk_data.disk); device->blk_data.disk = NULL; cleanup_queue: - device->blk_data.request_queue->queuedata = tape_put_device(device); + device->blk_data.request_queue->queuedata = NULL; + tape_put_device(device); blk_cleanup_queue(device->blk_data.request_queue); device->blk_data.request_queue = NULL; @@ -363,7 +364,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode) struct tape_device * device; int rc; - device = tape_get_device_reference(disk->private_data); + device = tape_get_device(disk->private_data); if (device->required_tapemarks) { DBF_EVENT(2, "TBLOCK: missing tapemarks\n"); diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index 31566c55adfe..23d773a0d113 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -33,8 +33,7 @@ static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *); static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *); static int tapechar_open(struct inode *,struct file *); static int tapechar_release(struct inode *,struct file *); -static int tapechar_ioctl(struct inode *, struct file *, unsigned int, - unsigned long); +static long tapechar_ioctl(struct file *, unsigned int, unsigned long); static long tapechar_compat_ioctl(struct file *, unsigned int, unsigned long); @@ -43,7 +42,7 @@ static const struct file_operations tape_fops = .owner = THIS_MODULE, .read = tapechar_read, .write = tapechar_write, - .ioctl = tapechar_ioctl, + .unlocked_ioctl = tapechar_ioctl, .compat_ioctl = tapechar_compat_ioctl, .open = tapechar_open, .release = tapechar_release, @@ -170,7 +169,6 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos) if (rc == 0) { rc = block_size - request->rescnt; DBF_EVENT(6, "TCHAR:rbytes: %x\n", rc); - filp->f_pos += rc; /* Copy data from idal buffer to user space. */ if (idal_buffer_to_user(device->char_data.idal_buf, data, rc) != 0) @@ -238,7 +236,6 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t break; DBF_EVENT(6, "TCHAR:wbytes: %lx\n", block_size - request->rescnt); - filp->f_pos += block_size - request->rescnt; written += block_size - request->rescnt; if (request->rescnt != 0) break; @@ -286,26 +283,20 @@ tapechar_open (struct inode *inode, struct file *filp) if (imajor(filp->f_path.dentry->d_inode) != tapechar_major) return -ENODEV; - lock_kernel(); minor = iminor(filp->f_path.dentry->d_inode); - device = tape_get_device(minor / TAPE_MINORS_PER_DEV); + device = tape_find_device(minor / TAPE_MINORS_PER_DEV); if (IS_ERR(device)) { - DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n"); - rc = PTR_ERR(device); - goto out; + DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n"); + return PTR_ERR(device); } - rc = tape_open(device); if (rc == 0) { filp->private_data = device; - rc = nonseekable_open(inode, filp); - } - else + nonseekable_open(inode, filp); + } else tape_put_device(device); -out: - unlock_kernel(); return rc; } @@ -342,7 +333,8 @@ tapechar_release(struct inode *inode, struct file *filp) device->char_data.idal_buf = NULL; } tape_release(device); - filp->private_data = tape_put_device(device); + filp->private_data = NULL; + tape_put_device(device); return 0; } @@ -351,16 +343,11 @@ tapechar_release(struct inode *inode, struct file *filp) * Tape device io controls. */ static int -tapechar_ioctl(struct inode *inp, struct file *filp, - unsigned int no, unsigned long data) +__tapechar_ioctl(struct tape_device *device, + unsigned int no, unsigned long data) { - struct tape_device *device; int rc; - DBF_EVENT(6, "TCHAR:ioct\n"); - - device = (struct tape_device *) filp->private_data; - if (no == MTIOCTOP) { struct mtop op; @@ -453,15 +440,30 @@ tapechar_ioctl(struct inode *inp, struct file *filp, } static long +tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data) +{ + struct tape_device *device; + long rc; + + DBF_EVENT(6, "TCHAR:ioct\n"); + + device = (struct tape_device *) filp->private_data; + mutex_lock(&device->mutex); + rc = __tapechar_ioctl(device, no, data); + mutex_unlock(&device->mutex); + return rc; +} + +static long tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data) { struct tape_device *device = filp->private_data; int rval = -ENOIOCTLCMD; if (device->discipline->ioctl_fn) { - lock_kernel(); + mutex_lock(&device->mutex); rval = device->discipline->ioctl_fn(device, no, data); - unlock_kernel(); + mutex_unlock(&device->mutex); if (rval == -EINVAL) rval = -ENOIOCTLCMD; } diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 5cd31e071647..f5d6802dc5da 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -492,6 +492,7 @@ tape_alloc_device(void) kfree(device); return ERR_PTR(-ENOMEM); } + mutex_init(&device->mutex); INIT_LIST_HEAD(&device->req_queue); INIT_LIST_HEAD(&device->node); init_waitqueue_head(&device->state_change_wq); @@ -511,11 +512,12 @@ tape_alloc_device(void) * increment the reference count. */ struct tape_device * -tape_get_device_reference(struct tape_device *device) +tape_get_device(struct tape_device *device) { - DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device, - atomic_inc_return(&device->ref_count)); + int count; + count = atomic_inc_return(&device->ref_count); + DBF_EVENT(4, "tape_get_device(%p) = %i\n", device, count); return device; } @@ -525,32 +527,25 @@ tape_get_device_reference(struct tape_device *device) * The function returns a NULL pointer to be used by the caller * for clearing reference pointers. */ -struct tape_device * +void tape_put_device(struct tape_device *device) { - int remain; + int count; - remain = atomic_dec_return(&device->ref_count); - if (remain > 0) { - DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain); - } else { - if (remain < 0) { - DBF_EVENT(4, "put device without reference\n"); - } else { - DBF_EVENT(4, "tape_free_device(%p)\n", device); - kfree(device->modeset_byte); - kfree(device); - } + count = atomic_dec_return(&device->ref_count); + DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, count); + BUG_ON(count < 0); + if (count == 0) { + kfree(device->modeset_byte); + kfree(device); } - - return NULL; } /* * Find tape device by a device index. */ struct tape_device * -tape_get_device(int devindex) +tape_find_device(int devindex) { struct tape_device *device, *tmp; @@ -558,7 +553,7 @@ tape_get_device(int devindex) read_lock(&tape_device_lock); list_for_each_entry(tmp, &tape_device_list, node) { if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) { - device = tape_get_device_reference(tmp); + device = tape_get_device(tmp); break; } } @@ -579,7 +574,8 @@ tape_generic_probe(struct ccw_device *cdev) device = tape_alloc_device(); if (IS_ERR(device)) return -ENODEV; - ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); + ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | + CCWDEV_DO_MULTIPATH); ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group); if (ret) { tape_put_device(device); @@ -606,7 +602,8 @@ __tape_discard_requests(struct tape_device *device) list_del(&request->list); /* Decrease ref_count for removed request. */ - request->device = tape_put_device(device); + request->device = NULL; + tape_put_device(device); request->rc = -EIO; if (request->callback != NULL) request->callback(request, request->callback_data); @@ -664,9 +661,11 @@ tape_generic_remove(struct ccw_device *cdev) tape_cleanup_device(device); } - if (!dev_get_drvdata(&cdev->dev)) { + device = dev_get_drvdata(&cdev->dev); + if (device) { sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group); - dev_set_drvdata(&cdev->dev, tape_put_device(dev_get_drvdata(&cdev->dev))); + dev_set_drvdata(&cdev->dev, NULL); + tape_put_device(device); } } @@ -721,9 +720,8 @@ tape_free_request (struct tape_request * request) { DBF_LH(6, "Free request %p\n", request); - if (request->device != NULL) { - request->device = tape_put_device(request->device); - } + if (request->device) + tape_put_device(request->device); kfree(request->cpdata); kfree(request->cpaddr); kfree(request); @@ -838,7 +836,8 @@ static void tape_long_busy_timeout(unsigned long data) BUG_ON(request->status != TAPE_REQUEST_LONG_BUSY); DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id); __tape_start_next_request(device); - device->lb_timeout.data = (unsigned long) tape_put_device(device); + device->lb_timeout.data = 0UL; + tape_put_device(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); } @@ -918,7 +917,7 @@ __tape_start_request(struct tape_device *device, struct tape_request *request) } /* Increase use count of device for the added request. */ - request->device = tape_get_device_reference(device); + request->device = tape_get_device(device); if (list_empty(&device->req_queue)) { /* No other requests are on the queue. Start this one. */ @@ -1117,8 +1116,8 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) if (req->status == TAPE_REQUEST_LONG_BUSY) { DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id); if (del_timer(&device->lb_timeout)) { - device->lb_timeout.data = (unsigned long) - tape_put_device(device); + device->lb_timeout.data = 0UL; + tape_put_device(device); __tape_start_next_request(device); } return; @@ -1173,7 +1172,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) break; case TAPE_IO_LONG_BUSY: device->lb_timeout.data = - (unsigned long)tape_get_device_reference(device); + (unsigned long) tape_get_device(device); device->lb_timeout.expires = jiffies + LONG_BUSY_TIMEOUT * HZ; DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id); @@ -1326,7 +1325,7 @@ EXPORT_SYMBOL(tape_generic_online); EXPORT_SYMBOL(tape_generic_offline); EXPORT_SYMBOL(tape_generic_pm_suspend); EXPORT_SYMBOL(tape_put_device); -EXPORT_SYMBOL(tape_get_device_reference); +EXPORT_SYMBOL(tape_get_device); EXPORT_SYMBOL(tape_state_verbose); EXPORT_SYMBOL(tape_op_verbose); EXPORT_SYMBOL(tape_state_set); diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c index 202f42132939..ebd820ccfb24 100644 --- a/drivers/s390/char/tape_proc.c +++ b/drivers/s390/char/tape_proc.c @@ -45,7 +45,7 @@ static int tape_proc_show(struct seq_file *m, void *v) seq_printf(m, "TapeNo\tBusID CuType/Model\t" "DevType/Model\tBlkSize\tState\tOp\tMedState\n"); } - device = tape_get_device(n); + device = tape_find_device(n); if (IS_ERR(device)) return 0; spin_lock_irq(get_ccwdev_lock(device->cdev)); diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 38385677c653..911822db614d 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/bootmem.h> +#include <linux/compat.h> #include <asm/ccwdev.h> #include <asm/cio.h> @@ -1731,6 +1732,22 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file, return kbd_ioctl(tp->kbd, file, cmd, arg); } +#ifdef CONFIG_COMPAT +static long +tty3270_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return -ENODEV; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + return kbd_ioctl(tp->kbd, file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + static const struct tty_operations tty3270_ops = { .open = tty3270_open, .close = tty3270_close, @@ -1745,6 +1762,9 @@ static const struct tty_operations tty3270_ops = { .hangup = tty3270_hangup, .wait_until_sent = tty3270_wait_until_sent, .ioctl = tty3270_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tty3270_compat_ioctl, +#endif .set_termios = tty3270_set_termios }; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index d1a142fa3eb4..899aa795bf38 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -312,11 +312,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) return -ENOSYS; /* Besure this device hasn't already been opened */ - lock_kernel(); spin_lock_bh(&logptr->priv_lock); if (logptr->dev_in_use) { spin_unlock_bh(&logptr->priv_lock); - unlock_kernel(); return -EBUSY; } logptr->dev_in_use = 1; @@ -360,9 +358,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) || (logptr->iucv_path_severed)); if (logptr->iucv_path_severed) goto out_record; - ret = nonseekable_open(inode, filp); - unlock_kernel(); - return ret; + nonseekable_open(inode, filp); + return 0; out_record: if (logptr->autorecording) @@ -372,7 +369,6 @@ out_path: logptr->path = NULL; out_dev: logptr->dev_in_use = 0; - unlock_kernel(); return -EIO; } diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 77571b68539a..cc56fc708bae 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -695,7 +695,6 @@ static int ur_open(struct inode *inode, struct file *file) if (accmode == O_RDWR) return -EACCES; - lock_kernel(); /* * We treat the minor number as the devno of the ur device * to find in the driver tree. @@ -749,7 +748,6 @@ static int ur_open(struct inode *inode, struct file *file) goto fail_urfile_free; urf->file_reclen = rc; file->private_data = urf; - unlock_kernel(); return 0; fail_urfile_free: @@ -761,7 +759,6 @@ fail_unlock: fail_put: urdev_put(urd); out: - unlock_kernel(); return rc; } diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index f2bc287b69e4..c974058e48d2 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -19,7 +19,6 @@ #include <linux/moduleparam.h> #include <linux/suspend.h> #include <linux/watchdog.h> -#include <linux/smp_lock.h> #include <asm/ebcdic.h> #include <asm/io.h> @@ -49,6 +48,8 @@ static unsigned int vmwdt_interval = 60; static unsigned long vmwdt_is_open; static int vmwdt_expect_close; +static DEFINE_MUTEX(vmwdt_mutex); + #define VMWDT_OPEN 0 /* devnode is open or suspend in progress */ #define VMWDT_RUNNING 1 /* The watchdog is armed */ @@ -133,15 +134,11 @@ static int __init vmwdt_probe(void) static int vmwdt_open(struct inode *i, struct file *f) { int ret; - lock_kernel(); - if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { - unlock_kernel(); + if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) return -EBUSY; - } ret = vmwdt_keepalive(); if (ret) clear_bit(VMWDT_OPEN, &vmwdt_is_open); - unlock_kernel(); return ret ? ret : nonseekable_open(i, f); } @@ -160,8 +157,7 @@ static struct watchdog_info vmwdt_info = { .identity = "z/VM Watchdog Timer", }; -static int vmwdt_ioctl(struct inode *i, struct file *f, - unsigned int cmd, unsigned long arg) +static int __vmwdt_ioctl(unsigned int cmd, unsigned long arg) { switch (cmd) { case WDIOC_GETSUPPORT: @@ -205,10 +201,19 @@ static int vmwdt_ioctl(struct inode *i, struct file *f, case WDIOC_KEEPALIVE: return vmwdt_keepalive(); } - return -EINVAL; } +static long vmwdt_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int rc; + + mutex_lock(&vmwdt_mutex); + rc = __vmwdt_ioctl(cmd, arg); + mutex_unlock(&vmwdt_mutex); + return (long) rc; +} + static ssize_t vmwdt_write(struct file *f, const char __user *buf, size_t count, loff_t *ppos) { @@ -288,7 +293,7 @@ static struct notifier_block vmwdt_power_notifier = { static const struct file_operations vmwdt_fops = { .open = &vmwdt_open, .release = &vmwdt_close, - .ioctl = &vmwdt_ioctl, + .unlocked_ioctl = &vmwdt_ioctl, .write = &vmwdt_write, .owner = THIS_MODULE, }; @@ -309,6 +314,10 @@ static int __init vmwdt_init(void) ret = register_pm_notifier(&vmwdt_power_notifier); if (ret) return ret; + /* + * misc_register() has to be the last action in module_init(), because + * file operations will be available right after this. + */ ret = misc_register(&vmwdt_dev); if (ret) { unregister_pm_notifier(&vmwdt_power_notifier); diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index fa4c9662f65e..d033414f7599 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -3,7 +3,7 @@ # obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \ - fcx.o itcw.o crw.o + fcx.o itcw.o crw.o ccwreq.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c new file mode 100644 index 000000000000..9509e3860934 --- /dev/null +++ b/drivers/s390/cio/ccwreq.c @@ -0,0 +1,328 @@ +/* + * Handling of internal CCW device requests. + * + * Copyright IBM Corp. 2009 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#include <linux/types.h> +#include <linux/err.h> +#include <asm/ccwdev.h> +#include <asm/cio.h> + +#include "io_sch.h" +#include "cio.h" +#include "device.h" +#include "cio_debug.h" + +/** + * lpm_adjust - adjust path mask + * @lpm: path mask to adjust + * @mask: mask of available paths + * + * Shift @lpm right until @lpm and @mask have at least one bit in common or + * until @lpm is zero. Return the resulting lpm. + */ +int lpm_adjust(int lpm, int mask) +{ + while (lpm && ((lpm & mask) == 0)) + lpm >>= 1; + return lpm; +} + +/* + * Adjust path mask to use next path and reset retry count. Return resulting + * path mask. + */ +static u16 ccwreq_next_path(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + + req->retries = req->maxretries; + req->mask = lpm_adjust(req->mask >>= 1, req->lpm); + + return req->mask; +} + +/* + * Clean up device state and report to callback. + */ +static void ccwreq_stop(struct ccw_device *cdev, int rc) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + if (req->done) + return; + req->done = 1; + ccw_device_set_timeout(cdev, 0); + memset(&cdev->private->irb, 0, sizeof(struct irb)); + sch->lpm = sch->schib.pmcw.pam; + if (rc && rc != -ENODEV && req->drc) + rc = req->drc; + req->callback(cdev, req->data, rc); +} + +/* + * (Re-)Start the operation until retries and paths are exhausted. + */ +static void ccwreq_do(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw1 *cp = req->cp; + int rc = -EACCES; + + while (req->mask) { + if (req->retries-- == 0) { + /* Retries exhausted, try next path. */ + ccwreq_next_path(cdev); + continue; + } + /* Perform start function. */ + sch->lpm = 0xff; + memset(&cdev->private->irb, 0, sizeof(struct irb)); + rc = cio_start(sch, cp, (u8) req->mask); + if (rc == 0) { + /* I/O started successfully. */ + ccw_device_set_timeout(cdev, req->timeout); + return; + } + if (rc == -ENODEV) { + /* Permanent device error. */ + break; + } + if (rc == -EACCES) { + /* Permant path error. */ + ccwreq_next_path(cdev); + continue; + } + /* Temporary improper status. */ + rc = cio_clear(sch); + if (rc) + break; + return; + } + ccwreq_stop(cdev, rc); +} + +/** + * ccw_request_start - perform I/O request + * @cdev: ccw device + * + * Perform the I/O request specified by cdev->req. + */ +void ccw_request_start(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + + /* Try all paths twice to counter link flapping. */ + req->mask = 0x8080; + req->retries = req->maxretries; + req->mask = lpm_adjust(req->mask, req->lpm); + req->drc = 0; + req->done = 0; + req->cancel = 0; + if (!req->mask) + goto out_nopath; + ccwreq_do(cdev); + return; + +out_nopath: + ccwreq_stop(cdev, -EACCES); +} + +/** + * ccw_request_cancel - cancel running I/O request + * @cdev: ccw device + * + * Cancel the I/O request specified by cdev->req. Return non-zero if request + * has already finished, zero otherwise. + */ +int ccw_request_cancel(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + int rc; + + if (req->done) + return 1; + req->cancel = 1; + rc = cio_clear(sch); + if (rc) + ccwreq_stop(cdev, rc); + return 0; +} + +/* + * Return the status of the internal I/O started on the specified ccw device. + * Perform BASIC SENSE if required. + */ +static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb) +{ + struct irb *irb = &cdev->private->irb; + struct cmd_scsw *scsw = &irb->scsw.cmd; + + /* Perform BASIC SENSE if needed. */ + if (ccw_device_accumulate_and_sense(cdev, lcirb)) + return IO_RUNNING; + /* Check for halt/clear interrupt. */ + if (scsw->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) + return IO_KILLED; + /* Check for path error. */ + if (scsw->cc == 3 || scsw->pno) + return IO_PATH_ERROR; + /* Handle BASIC SENSE data. */ + if (irb->esw.esw0.erw.cons) { + CIO_TRACE_EVENT(2, "sensedata"); + CIO_HEX_EVENT(2, &cdev->private->dev_id, + sizeof(struct ccw_dev_id)); + CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT); + /* Check for command reject. */ + if (irb->ecw[0] & SNS0_CMD_REJECT) + return IO_REJECTED; + /* Assume that unexpected SENSE data implies an error. */ + return IO_STATUS_ERROR; + } + /* Check for channel errors. */ + if (scsw->cstat != 0) + return IO_STATUS_ERROR; + /* Check for device errors. */ + if (scsw->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) + return IO_STATUS_ERROR; + /* Check for final state. */ + if (!(scsw->dstat & DEV_STAT_DEV_END)) + return IO_RUNNING; + /* Check for other improper status. */ + if (scsw->cc == 1 && (scsw->stctl & SCSW_STCTL_ALERT_STATUS)) + return IO_STATUS_ERROR; + return IO_DONE; +} + +/* + * Log ccw request status. + */ +static void ccwreq_log_status(struct ccw_device *cdev, enum io_status status) +{ + struct ccw_request *req = &cdev->private->req; + struct { + struct ccw_dev_id dev_id; + u16 retries; + u8 lpm; + u8 status; + } __attribute__ ((packed)) data; + data.dev_id = cdev->private->dev_id; + data.retries = req->retries; + data.lpm = (u8) req->mask; + data.status = (u8) status; + CIO_TRACE_EVENT(2, "reqstat"); + CIO_HEX_EVENT(2, &data, sizeof(data)); +} + +/** + * ccw_request_handler - interrupt handler for I/O request procedure. + * @cdev: ccw device + * + * Handle interrupt during I/O request procedure. + */ +void ccw_request_handler(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + struct irb *irb = (struct irb *) __LC_IRB; + enum io_status status; + int rc = -EOPNOTSUPP; + + /* Check status of I/O request. */ + status = ccwreq_status(cdev, irb); + if (req->filter) + status = req->filter(cdev, req->data, irb, status); + if (status != IO_RUNNING) + ccw_device_set_timeout(cdev, 0); + if (status != IO_DONE && status != IO_RUNNING) + ccwreq_log_status(cdev, status); + switch (status) { + case IO_DONE: + break; + case IO_RUNNING: + return; + case IO_REJECTED: + goto err; + case IO_PATH_ERROR: + goto out_next_path; + case IO_STATUS_ERROR: + goto out_restart; + case IO_KILLED: + /* Check if request was cancelled on purpose. */ + if (req->cancel) { + rc = -EIO; + goto err; + } + goto out_restart; + } + /* Check back with request initiator. */ + if (!req->check) + goto out; + switch (req->check(cdev, req->data)) { + case 0: + break; + case -EAGAIN: + goto out_restart; + case -EACCES: + goto out_next_path; + default: + goto err; + } +out: + ccwreq_stop(cdev, 0); + return; + +out_next_path: + /* Try next path and restart I/O. */ + if (!ccwreq_next_path(cdev)) { + rc = -EACCES; + goto err; + } +out_restart: + /* Restart. */ + ccwreq_do(cdev); + return; +err: + ccwreq_stop(cdev, rc); +} + + +/** + * ccw_request_timeout - timeout handler for I/O request procedure + * @cdev: ccw device + * + * Handle timeout during I/O request procedure. + */ +void ccw_request_timeout(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + int rc; + + if (!ccwreq_next_path(cdev)) { + /* set the final return code for this request */ + req->drc = -ETIME; + } + rc = cio_clear(sch); + if (rc) + goto err; + return; + +err: + ccwreq_stop(cdev, rc); +} + +/** + * ccw_request_notoper - notoper handler for I/O request procedure + * @cdev: ccw device + * + * Handle timeout during I/O request procedure. + */ +void ccw_request_notoper(struct ccw_device *cdev) +{ + ccwreq_stop(cdev, -ENODEV); +} diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 8ab51608da55..c268a2e5b7c3 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -65,7 +65,7 @@ static void set_chp_logically_online(struct chp_id chpid, int onoff) chpid_to_chp(chpid)->state = onoff; } -/* On succes return 0 if channel-path is varied offline, 1 if it is varied +/* On success return 0 if channel-path is varied offline, 1 if it is varied * online. Return -ENODEV if channel-path is not registered. */ int chp_get_status(struct chp_id chpid) { diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 138124fcfcad..126f240715a4 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -618,6 +618,7 @@ void __irq_entry do_IRQ(struct pt_regs *regs) old_regs = set_irq_regs(regs); s390_idle_check(); irq_enter(); + __get_cpu_var(s390_idle).nohz_delay = 1; if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) /* Serve timer interrupts first. */ clock_comparator_work(); diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 2e43558c704b..bf7f80f5a330 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -68,6 +68,11 @@ struct schib { __u8 mda[4]; /* model dependent area */ } __attribute__ ((packed,aligned(4))); +enum sch_todo { + SCH_TODO_NOTHING, + SCH_TODO_UNREG, +}; + /* subchannel data structure used by I/O subroutines */ struct subchannel { struct subchannel_id schid; @@ -95,7 +100,8 @@ struct subchannel { struct device dev; /* entry in device tree */ struct css_driver *driver; void *private; /* private per subchannel type data */ - struct work_struct work; + enum sch_todo todo; + struct work_struct todo_work; struct schib_config config; } __attribute__ ((aligned(8))); diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 30f516111307..2985eb439485 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -462,7 +462,7 @@ static struct cmb_area cmb_area = { * block of memory, which can not be moved as long as any channel * is active. Therefore, a maximum number of subchannels needs to * be defined somewhere. This is a module parameter, defaulting to - * a resonable value of 1024, or 32 kb of memory. + * a reasonable value of 1024, or 32 kb of memory. * Current kernels don't allow kmalloc with more than 128kb, so the * maximum is 4096. */ diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 91c25706fa83..92ff88ac1107 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -133,6 +133,8 @@ out: return rc; } +static void css_sch_todo(struct work_struct *work); + static struct subchannel * css_alloc_subchannel(struct subchannel_id schid) { @@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid) kfree(sch); return ERR_PTR(ret); } + INIT_WORK(&sch->todo_work, css_sch_todo); return sch; } @@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch) } EXPORT_SYMBOL_GPL(css_sch_device_unregister); +static void css_sch_todo(struct work_struct *work) +{ + struct subchannel *sch; + enum sch_todo todo; + + sch = container_of(work, struct subchannel, todo_work); + /* Find out todo. */ + spin_lock_irq(sch->lock); + todo = sch->todo; + CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid, + sch->schid.sch_no, todo); + sch->todo = SCH_TODO_NOTHING; + spin_unlock_irq(sch->lock); + /* Perform todo. */ + if (todo == SCH_TODO_UNREG) + css_sch_device_unregister(sch); + /* Release workqueue ref. */ + put_device(&sch->dev); +} + +/** + * css_sched_sch_todo - schedule a subchannel operation + * @sch: subchannel + * @todo: todo + * + * Schedule the operation identified by @todo to be performed on the slow path + * workqueue. Do nothing if another operation with higher priority is already + * scheduled. Needs to be called with subchannel lock held. + */ +void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo) +{ + CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n", + sch->schid.ssid, sch->schid.sch_no, todo); + if (sch->todo >= todo) + return; + /* Get workqueue ref. */ + if (!get_device(&sch->dev)) + return; + sch->todo = todo; + if (!queue_work(slow_path_wq, &sch->todo_work)) { + /* Already queued, release workqueue ref. */ + put_device(&sch->dev); + } +} + static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) { int i; @@ -376,8 +424,8 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) /* Unusable - ignore. */ return 0; } - CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, " - "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER); + CIO_MSG_EVENT(4, "event: sch 0.%x.%04x, new\n", schid.ssid, + schid.sch_no); return css_probe_device(schid); } @@ -394,6 +442,10 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) "Got subchannel machine check but " "no sch_event handler provided.\n"); } + if (ret != 0 && ret != -EAGAIN) { + CIO_MSG_EVENT(2, "eval: sch 0.%x.%04x, rc=%d\n", + sch->schid.ssid, sch->schid.sch_no, ret); + } return ret; } @@ -684,6 +736,7 @@ static int __init setup_css(int nr) css->pseudo_subchannel->dev.parent = &css->device; css->pseudo_subchannel->dev.release = css_subchannel_release; dev_set_name(&css->pseudo_subchannel->dev, "defunct"); + mutex_init(&css->pseudo_subchannel->reg_mutex); ret = cio_create_sch_lock(css->pseudo_subchannel); if (ret) { kfree(css->pseudo_subchannel); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 68d6b0bf151c..fe84b92cde60 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -11,6 +11,8 @@ #include <asm/chpid.h> #include <asm/schid.h> +#include "cio.h" + /* * path grouping stuff */ @@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *); extern struct workqueue_struct *slow_path_wq; void css_wait_for_slow_path(void); +void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo); #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 2490b741e16a..9fecfb4223a8 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -7,6 +7,10 @@ * Cornelia Huck (cornelia.huck@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com) */ + +#define KMSG_COMPONENT "cio" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/spinlock.h> @@ -299,53 +303,18 @@ int ccw_device_is_orphan(struct ccw_device *cdev) static void ccw_device_unregister(struct ccw_device *cdev) { - if (test_and_clear_bit(1, &cdev->private->registered)) { + if (device_is_registered(&cdev->dev)) { + /* Undo device_add(). */ device_del(&cdev->dev); + } + if (cdev->private->flags.initialized) { + cdev->private->flags.initialized = 0; /* Release reference from device_initialize(). */ put_device(&cdev->dev); } } -static void ccw_device_remove_orphan_cb(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - ccw_device_unregister(cdev); - /* Release cdev reference for workqueue processing. */ - put_device(&cdev->dev); -} - -static void -ccw_device_remove_disconnected(struct ccw_device *cdev) -{ - unsigned long flags; - - /* - * Forced offline in disconnected state means - * 'throw away device'. - */ - if (ccw_device_is_orphan(cdev)) { - /* - * Deregister ccw device. - * Unfortunately, we cannot do this directly from the - * attribute method. - */ - /* Get cdev reference for workqueue processing. */ - if (!get_device(&cdev->dev)) - return; - spin_lock_irqsave(cdev->ccwlock, flags); - cdev->private->state = DEV_STATE_NOT_OPER; - spin_unlock_irqrestore(cdev->ccwlock, flags); - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_remove_orphan_cb); - queue_work(slow_path_wq, &cdev->private->kick_work); - } else - /* Deregister subchannel, which will kill the ccw device. */ - ccw_device_schedule_sch_unregister(cdev); -} +static void io_subchannel_quiesce(struct subchannel *); /** * ccw_device_set_offline() - disable a ccw device for I/O @@ -360,7 +329,8 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) */ int ccw_device_set_offline(struct ccw_device *cdev) { - int ret; + struct subchannel *sch; + int ret, state; if (!cdev) return -ENODEV; @@ -374,6 +344,7 @@ int ccw_device_set_offline(struct ccw_device *cdev) } cdev->online = 0; spin_lock_irq(cdev->ccwlock); + sch = to_subchannel(cdev->dev.parent); /* Wait until a final state or DISCONNECTED is reached */ while (!dev_fsm_final_state(cdev) && cdev->private->state != DEV_STATE_DISCONNECTED) { @@ -382,20 +353,37 @@ int ccw_device_set_offline(struct ccw_device *cdev) cdev->private->state == DEV_STATE_DISCONNECTED)); spin_lock_irq(cdev->ccwlock); } - ret = ccw_device_offline(cdev); - if (ret) - goto error; + do { + ret = ccw_device_offline(cdev); + if (!ret) + break; + CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device " + "0.%x.%04x\n", ret, cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + if (ret != -EBUSY) + goto error; + state = cdev->private->state; + spin_unlock_irq(cdev->ccwlock); + io_subchannel_quiesce(sch); + spin_lock_irq(cdev->ccwlock); + cdev->private->state = state; + } while (ret == -EBUSY); spin_unlock_irq(cdev->ccwlock); wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || cdev->private->state == DEV_STATE_DISCONNECTED)); + /* Inform the user if set offline failed. */ + if (cdev->private->state == DEV_STATE_BOXED) { + pr_warning("%s: The device entered boxed state while " + "being set offline\n", dev_name(&cdev->dev)); + } else if (cdev->private->state == DEV_STATE_NOT_OPER) { + pr_warning("%s: The device stopped operating while " + "being set offline\n", dev_name(&cdev->dev)); + } /* Give up reference from ccw_device_set_online(). */ put_device(&cdev->dev); return 0; error: - CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device 0.%x.%04x\n", - ret, cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); cdev->private->state = DEV_STATE_OFFLINE; dev_fsm_event(cdev, DEV_EVENT_NOTOPER); spin_unlock_irq(cdev->ccwlock); @@ -448,6 +436,16 @@ int ccw_device_set_online(struct ccw_device *cdev) if ((cdev->private->state != DEV_STATE_ONLINE) && (cdev->private->state != DEV_STATE_W4SENSE)) { spin_unlock_irq(cdev->ccwlock); + /* Inform the user that set online failed. */ + if (cdev->private->state == DEV_STATE_BOXED) { + pr_warning("%s: Setting the device online failed " + "because it is boxed\n", + dev_name(&cdev->dev)); + } else if (cdev->private->state == DEV_STATE_NOT_OPER) { + pr_warning("%s: Setting the device online failed " + "because it is not operational\n", + dev_name(&cdev->dev)); + } /* Give up online reference since onlining failed. */ put_device(&cdev->dev); return -ENODEV; @@ -494,27 +492,22 @@ error: static int online_store_handle_offline(struct ccw_device *cdev) { - if (cdev->private->state == DEV_STATE_DISCONNECTED) - ccw_device_remove_disconnected(cdev); - else if (cdev->online && cdev->drv && cdev->drv->set_offline) + if (cdev->private->state == DEV_STATE_DISCONNECTED) { + spin_lock_irq(cdev->ccwlock); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL); + spin_unlock_irq(cdev->ccwlock); + } else if (cdev->online && cdev->drv && cdev->drv->set_offline) return ccw_device_set_offline(cdev); return 0; } static int online_store_recog_and_online(struct ccw_device *cdev) { - int ret; - /* Do device recognition, if needed. */ if (cdev->private->state == DEV_STATE_BOXED) { - ret = ccw_device_recognition(cdev); - if (ret) { - CIO_MSG_EVENT(0, "Couldn't start recognition " - "for device 0.%x.%04x (ret=%d)\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - return ret; - } + spin_lock_irq(cdev->ccwlock); + ccw_device_recognition(cdev); + spin_unlock_irq(cdev->ccwlock); wait_event(cdev->private->wait_q, cdev->private->flags.recog_done); if (cdev->private->state != DEV_STATE_OFFLINE) @@ -553,11 +546,10 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, int force, ret; unsigned long i; - if ((cdev->private->state != DEV_STATE_OFFLINE && - cdev->private->state != DEV_STATE_ONLINE && - cdev->private->state != DEV_STATE_BOXED && - cdev->private->state != DEV_STATE_DISCONNECTED) || - atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) + if (!dev_fsm_final_state(cdev) && + cdev->private->state != DEV_STATE_DISCONNECTED) + return -EAGAIN; + if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) return -EAGAIN; if (cdev->drv && !try_module_get(cdev->drv->owner)) { @@ -665,81 +657,31 @@ static int ccw_device_register(struct ccw_device *cdev) cdev->private->dev_id.devno); if (ret) return ret; - ret = device_add(dev); - if (ret) - return ret; - - set_bit(1, &cdev->private->registered); - return ret; + return device_add(dev); } -struct match_data { - struct ccw_dev_id dev_id; - struct ccw_device * sibling; -}; - -static int -match_devno(struct device * dev, void * data) -{ - struct match_data * d = data; - struct ccw_device * cdev; - - cdev = to_ccwdev(dev); - if ((cdev->private->state == DEV_STATE_DISCONNECTED) && - !ccw_device_is_orphan(cdev) && - ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) && - (cdev != d->sibling)) - return 1; - return 0; -} - -static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id, - struct ccw_device *sibling) -{ - struct device *dev; - struct match_data data; - - data.dev_id = *dev_id; - data.sibling = sibling; - dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno); - - return dev ? to_ccwdev(dev) : NULL; -} - -static int match_orphan(struct device *dev, void *data) +static int match_dev_id(struct device *dev, void *data) { - struct ccw_dev_id *dev_id; - struct ccw_device *cdev; + struct ccw_device *cdev = to_ccwdev(dev); + struct ccw_dev_id *dev_id = data; - dev_id = data; - cdev = to_ccwdev(dev); return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id); } -static struct ccw_device * -get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css, - struct ccw_dev_id *dev_id) +static struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id) { struct device *dev; - dev = device_find_child(&css->pseudo_subchannel->dev, dev_id, - match_orphan); + dev = bus_find_device(&ccw_bus_type, NULL, dev_id, match_dev_id); return dev ? to_ccwdev(dev) : NULL; } -void ccw_device_do_unbind_bind(struct work_struct *work) +static void ccw_device_do_unbind_bind(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; - struct subchannel *sch; int ret; - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - sch = to_subchannel(cdev->dev.parent); - - if (test_bit(1, &cdev->private->registered)) { + if (device_is_registered(&cdev->dev)) { device_release_driver(&cdev->dev); ret = device_attach(&cdev->dev); WARN_ON(ret == -ENODEV); @@ -773,6 +715,8 @@ static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch) return ERR_PTR(-ENOMEM); } +static void ccw_device_todo(struct work_struct *work); + static int io_subchannel_initialize_dev(struct subchannel *sch, struct ccw_device *cdev) { @@ -780,7 +724,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, atomic_set(&cdev->private->onoff, 0); cdev->dev.parent = &sch->dev; cdev->dev.release = ccw_device_release; - INIT_WORK(&cdev->private->kick_work, NULL); + INIT_WORK(&cdev->private->todo_work, ccw_device_todo); cdev->dev.groups = ccwdev_attr_groups; /* Do first half of device_register. */ device_initialize(&cdev->dev); @@ -789,6 +733,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, put_device(&cdev->dev); return -ENODEV; } + cdev->private->flags.initialized = 1; return 0; } @@ -806,76 +751,7 @@ static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch) return cdev; } -static int io_subchannel_recog(struct ccw_device *, struct subchannel *); - -static void sch_attach_device(struct subchannel *sch, - struct ccw_device *cdev) -{ - css_update_ssd_info(sch); - spin_lock_irq(sch->lock); - sch_set_cdev(sch, cdev); - cdev->private->schid = sch->schid; - cdev->ccwlock = sch->lock; - ccw_device_trigger_reprobe(cdev); - spin_unlock_irq(sch->lock); -} - -static void sch_attach_disconnected_device(struct subchannel *sch, - struct ccw_device *cdev) -{ - struct subchannel *other_sch; - int ret; - - /* Get reference for new parent. */ - if (!get_device(&sch->dev)) - return; - other_sch = to_subchannel(cdev->dev.parent); - /* Note: device_move() changes cdev->dev.parent */ - ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); - if (ret) { - CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed " - "(ret=%d)!\n", cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - /* Put reference for new parent. */ - put_device(&sch->dev); - return; - } - sch_set_cdev(other_sch, NULL); - /* No need to keep a subchannel without ccw device around. */ - css_sch_device_unregister(other_sch); - sch_attach_device(sch, cdev); - /* Put reference for old parent. */ - put_device(&other_sch->dev); -} - -static void sch_attach_orphaned_device(struct subchannel *sch, - struct ccw_device *cdev) -{ - int ret; - struct subchannel *pseudo_sch; - - /* Get reference for new parent. */ - if (!get_device(&sch->dev)) - return; - pseudo_sch = to_subchannel(cdev->dev.parent); - /* - * Try to move the ccw device to its new subchannel. - * Note: device_move() changes cdev->dev.parent - */ - ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); - if (ret) { - CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage " - "failed (ret=%d)!\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - /* Put reference for new parent. */ - put_device(&sch->dev); - return; - } - sch_attach_device(sch, cdev); - /* Put reference on pseudo subchannel. */ - put_device(&pseudo_sch->dev); -} +static void io_subchannel_recog(struct ccw_device *, struct subchannel *); static void sch_create_and_recog_new_device(struct subchannel *sch) { @@ -888,100 +764,19 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) css_sch_device_unregister(sch); return; } - spin_lock_irq(sch->lock); - sch_set_cdev(sch, cdev); - spin_unlock_irq(sch->lock); /* Start recognition for the new ccw device. */ - if (io_subchannel_recog(cdev, sch)) { - spin_lock_irq(sch->lock); - sch_set_cdev(sch, NULL); - spin_unlock_irq(sch->lock); - css_sch_device_unregister(sch); - /* Put reference from io_subchannel_create_ccwdev(). */ - put_device(&sch->dev); - /* Give up initial reference. */ - put_device(&cdev->dev); - } -} - - -void ccw_device_move_to_orphanage(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - struct ccw_device *replacing_cdev; - struct subchannel *sch; - int ret; - struct channel_subsystem *css; - struct ccw_dev_id dev_id; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - sch = to_subchannel(cdev->dev.parent); - css = to_css(sch->dev.parent); - dev_id.devno = sch->schib.pmcw.dev; - dev_id.ssid = sch->schid.ssid; - - /* Increase refcount for pseudo subchannel. */ - get_device(&css->pseudo_subchannel->dev); - /* - * Move the orphaned ccw device to the orphanage so the replacing - * ccw device can take its place on the subchannel. - * Note: device_move() changes cdev->dev.parent - */ - ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev, - DPM_ORDER_NONE); - if (ret) { - CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed " - "(ret=%d)!\n", cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - /* Decrease refcount for pseudo subchannel again. */ - put_device(&css->pseudo_subchannel->dev); - return; - } - cdev->ccwlock = css->pseudo_subchannel->lock; - /* - * Search for the replacing ccw device - * - among the disconnected devices - * - in the orphanage - */ - replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev); - if (replacing_cdev) { - sch_attach_disconnected_device(sch, replacing_cdev); - /* Release reference from get_disc_ccwdev_by_dev_id() */ - put_device(&replacing_cdev->dev); - /* Release reference of subchannel from old cdev. */ - put_device(&sch->dev); - return; - } - replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id); - if (replacing_cdev) { - sch_attach_orphaned_device(sch, replacing_cdev); - /* Release reference from get_orphaned_ccwdev_by_dev_id() */ - put_device(&replacing_cdev->dev); - /* Release reference of subchannel from old cdev. */ - put_device(&sch->dev); - return; - } - sch_create_and_recog_new_device(sch); - /* Release reference of subchannel from old cdev. */ - put_device(&sch->dev); + io_subchannel_recog(cdev, sch); } /* * Register recognized device. */ -static void -io_subchannel_register(struct work_struct *work) +static void io_subchannel_register(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; struct subchannel *sch; int ret; unsigned long flags; - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); /* * Check if subchannel is still registered. It may have become @@ -1033,41 +828,23 @@ out: cdev->private->flags.recog_done = 1; wake_up(&cdev->private->wait_q); out_err: - /* Release reference for workqueue processing. */ - put_device(&cdev->dev); if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); } -static void ccw_device_call_sch_unregister(struct work_struct *work) +static void ccw_device_call_sch_unregister(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; struct subchannel *sch; - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; /* Get subchannel reference for local processing. */ if (!get_device(cdev->dev.parent)) return; sch = to_subchannel(cdev->dev.parent); css_sch_device_unregister(sch); - /* Release cdev reference for workqueue processing.*/ - put_device(&cdev->dev); /* Release subchannel reference for local processing. */ put_device(&sch->dev); } -void ccw_device_schedule_sch_unregister(struct ccw_device *cdev) -{ - /* Get cdev reference for workqueue processing. */ - if (!get_device(&cdev->dev)) - return; - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister); - queue_work(slow_path_wq, &cdev->private->kick_work); -} - /* * subchannel recognition done. Called from the state machine. */ @@ -1083,7 +860,8 @@ io_subchannel_recog_done(struct ccw_device *cdev) /* Device did not respond in time. */ case DEV_STATE_NOT_OPER: cdev->private->flags.recog_done = 1; - ccw_device_schedule_sch_unregister(cdev); + /* Remove device found not operational. */ + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); break; @@ -1092,22 +870,15 @@ io_subchannel_recog_done(struct ccw_device *cdev) * We can't register the device in interrupt context so * we schedule a work item. */ - if (!get_device(&cdev->dev)) - break; - PREPARE_WORK(&cdev->private->kick_work, - io_subchannel_register); - queue_work(slow_path_wq, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_REGISTER); break; } } -static int -io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) +static void io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) { - int rc; struct ccw_device_private *priv; - sch_set_cdev(sch, cdev); cdev->ccwlock = sch->lock; /* Init private data. */ @@ -1125,62 +896,81 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) /* Start async. device sensing. */ spin_lock_irq(sch->lock); - rc = ccw_device_recognition(cdev); + sch_set_cdev(sch, cdev); + ccw_device_recognition(cdev); spin_unlock_irq(sch->lock); - if (rc) { - if (atomic_dec_and_test(&ccw_device_init_count)) - wake_up(&ccw_device_init_wq); - } - return rc; } -static void ccw_device_move_to_sch(struct work_struct *work) +static int ccw_device_move_to_sch(struct ccw_device *cdev, + struct subchannel *sch) { - struct ccw_device_private *priv; - int rc; - struct subchannel *sch; - struct ccw_device *cdev; - struct subchannel *former_parent; + struct subchannel *old_sch; + int rc, old_enabled = 0; - priv = container_of(work, struct ccw_device_private, kick_work); - sch = priv->sch; - cdev = priv->cdev; - former_parent = to_subchannel(cdev->dev.parent); - /* Get reference for new parent. */ + old_sch = to_subchannel(cdev->dev.parent); + /* Obtain child reference for new parent. */ if (!get_device(&sch->dev)) - return; + return -ENODEV; + + if (!sch_is_pseudo_sch(old_sch)) { + spin_lock_irq(old_sch->lock); + old_enabled = old_sch->schib.pmcw.ena; + rc = 0; + if (old_enabled) + rc = cio_disable_subchannel(old_sch); + spin_unlock_irq(old_sch->lock); + if (rc == -EBUSY) { + /* Release child reference for new parent. */ + put_device(&sch->dev); + return rc; + } + } + mutex_lock(&sch->reg_mutex); - /* - * Try to move the ccw device to its new subchannel. - * Note: device_move() changes cdev->dev.parent - */ rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); mutex_unlock(&sch->reg_mutex); if (rc) { - CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to subchannel " - "0.%x.%04x failed (ret=%d)!\n", + CIO_MSG_EVENT(0, "device_move(0.%x.%04x,0.%x.%04x)=%d\n", cdev->private->dev_id.ssid, cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, rc); - css_sch_device_unregister(sch); - /* Put reference for new parent again. */ + sch->schib.pmcw.dev, rc); + if (old_enabled) { + /* Try to reenable the old subchannel. */ + spin_lock_irq(old_sch->lock); + cio_enable_subchannel(old_sch, (u32)(addr_t)old_sch); + spin_unlock_irq(old_sch->lock); + } + /* Release child reference for new parent. */ put_device(&sch->dev); - goto out; + return rc; } - if (!sch_is_pseudo_sch(former_parent)) { - spin_lock_irq(former_parent->lock); - sch_set_cdev(former_parent, NULL); - spin_unlock_irq(former_parent->lock); - css_sch_device_unregister(former_parent); - /* Reset intparm to zeroes. */ - former_parent->config.intparm = 0; - cio_commit_config(former_parent); + /* Clean up old subchannel. */ + if (!sch_is_pseudo_sch(old_sch)) { + spin_lock_irq(old_sch->lock); + sch_set_cdev(old_sch, NULL); + spin_unlock_irq(old_sch->lock); + css_schedule_eval(old_sch->schid); } - sch_attach_device(sch, cdev); -out: - /* Put reference for old parent. */ - put_device(&former_parent->dev); - put_device(&cdev->dev); + /* Release child reference for old parent. */ + put_device(&old_sch->dev); + /* Initialize new subchannel. */ + spin_lock_irq(sch->lock); + cdev->private->schid = sch->schid; + cdev->ccwlock = sch->lock; + if (!sch_is_pseudo_sch(sch)) + sch_set_cdev(sch, cdev); + spin_unlock_irq(sch->lock); + if (!sch_is_pseudo_sch(sch)) + css_update_ssd_info(sch); + return 0; +} + +static int ccw_device_move_to_orph(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct channel_subsystem *css = to_css(sch->dev.parent); + + return ccw_device_move_to_sch(cdev, css->pseudo_subchannel); } static void io_subchannel_irq(struct subchannel *sch) @@ -1199,9 +989,6 @@ void io_subchannel_init_config(struct subchannel *sch) { memset(&sch->config, 0, sizeof(sch->config)); sch->config.csense = 1; - /* Use subchannel mp mode when there is more than 1 installed CHPID. */ - if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0) - sch->config.mp = 1; } static void io_subchannel_init_fields(struct subchannel *sch) @@ -1222,23 +1009,6 @@ static void io_subchannel_init_fields(struct subchannel *sch) io_subchannel_init_config(sch); } -static void io_subchannel_do_unreg(struct work_struct *work) -{ - struct subchannel *sch; - - sch = container_of(work, struct subchannel, work); - css_sch_device_unregister(sch); - put_device(&sch->dev); -} - -/* Schedule unregister if we have no cdev. */ -static void io_subchannel_schedule_removal(struct subchannel *sch) -{ - get_device(&sch->dev); - INIT_WORK(&sch->work, io_subchannel_do_unreg); - queue_work(slow_path_wq, &sch->work); -} - /* * Note: We always return 0 so that we bind to the device even on error. * This is needed so that our remove function is called on unregister. @@ -1247,8 +1017,6 @@ static int io_subchannel_probe(struct subchannel *sch) { struct ccw_device *cdev; int rc; - unsigned long flags; - struct ccw_dev_id dev_id; if (cio_is_console(sch->schid)) { rc = sysfs_create_group(&sch->dev.kobj, @@ -1268,6 +1036,7 @@ static int io_subchannel_probe(struct subchannel *sch) cdev = sch_get_cdev(sch); cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); + cdev->private->flags.initialized = 1; ccw_device_register(cdev); /* * Check if the device is already online. If it is @@ -1292,44 +1061,14 @@ static int io_subchannel_probe(struct subchannel *sch) sch->private = kzalloc(sizeof(struct io_subchannel_private), GFP_KERNEL | GFP_DMA); if (!sch->private) - goto out_err; - /* - * First check if a fitting device may be found amongst the - * disconnected devices or in the orphanage. - */ - dev_id.devno = sch->schib.pmcw.dev; - dev_id.ssid = sch->schid.ssid; - cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); - if (!cdev) - cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), - &dev_id); - if (cdev) { - /* - * Schedule moving the device until when we have a registered - * subchannel to move to and succeed the probe. We can - * unregister later again, when the probe is through. - */ - cdev->private->sch = sch; - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_move_to_sch); - queue_work(slow_path_wq, &cdev->private->kick_work); - return 0; - } - cdev = io_subchannel_create_ccwdev(sch); - if (IS_ERR(cdev)) - goto out_err; - rc = io_subchannel_recog(cdev, sch); - if (rc) { - spin_lock_irqsave(sch->lock, flags); - io_subchannel_recog_done(cdev); - spin_unlock_irqrestore(sch->lock, flags); - } + goto out_schedule; + css_schedule_eval(sch->schid); return 0; -out_err: - kfree(sch->private); - sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); + out_schedule: - io_subchannel_schedule_removal(sch); + spin_lock_irq(sch->lock); + css_sched_sch_todo(sch, SCH_TODO_UNREG); + spin_unlock_irq(sch->lock); return 0; } @@ -1337,32 +1076,23 @@ static int io_subchannel_remove (struct subchannel *sch) { struct ccw_device *cdev; - unsigned long flags; cdev = sch_get_cdev(sch); if (!cdev) - return 0; + goto out_free; + io_subchannel_quiesce(sch); /* Set ccw device to not operational and drop reference. */ - spin_lock_irqsave(cdev->ccwlock, flags); + spin_lock_irq(cdev->ccwlock); sch_set_cdev(sch, NULL); cdev->private->state = DEV_STATE_NOT_OPER; - spin_unlock_irqrestore(cdev->ccwlock, flags); + spin_unlock_irq(cdev->ccwlock); ccw_device_unregister(cdev); +out_free: kfree(sch->private); sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); return 0; } -static int io_subchannel_notify(struct subchannel *sch, int event) -{ - struct ccw_device *cdev; - - cdev = sch_get_cdev(sch); - if (!cdev) - return 0; - return ccw_device_notify(cdev, event); -} - static void io_subchannel_verify(struct subchannel *sch) { struct ccw_device *cdev; @@ -1372,36 +1102,6 @@ static void io_subchannel_verify(struct subchannel *sch) dev_fsm_event(cdev, DEV_EVENT_VERIFY); } -static int check_for_io_on_path(struct subchannel *sch, int mask) -{ - if (cio_update_schib(sch)) - return 0; - if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask) - return 1; - return 0; -} - -static void terminate_internal_io(struct subchannel *sch, - struct ccw_device *cdev) -{ - if (cio_clear(sch)) { - /* Recheck device in case clear failed. */ - sch->lpm = 0; - if (cdev->online) - dev_fsm_event(cdev, DEV_EVENT_VERIFY); - else - css_schedule_eval(sch->schid); - return; - } - cdev->private->state = DEV_STATE_CLEAR_VERIFY; - /* Request retry of internal operation. */ - cdev->private->flags.intretry = 1; - /* Call handler. */ - if (cdev->handler) - cdev->handler(cdev, cdev->private->intparm, - ERR_PTR(-EIO)); -} - static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask) { struct ccw_device *cdev; @@ -1409,18 +1109,24 @@ static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask) cdev = sch_get_cdev(sch); if (!cdev) return; - if (check_for_io_on_path(sch, mask)) { - if (cdev->private->state == DEV_STATE_ONLINE) - ccw_device_kill_io(cdev); - else { - terminate_internal_io(sch, cdev); - /* Re-start path verification. */ - dev_fsm_event(cdev, DEV_EVENT_VERIFY); - } - } else - /* trigger path verification. */ - dev_fsm_event(cdev, DEV_EVENT_VERIFY); + if (cio_update_schib(sch)) + goto err; + /* Check for I/O on path. */ + if (scsw_actl(&sch->schib.scsw) == 0 || sch->schib.pmcw.lpum != mask) + goto out; + if (cdev->private->state == DEV_STATE_ONLINE) { + ccw_device_kill_io(cdev); + goto out; + } + if (cio_clear(sch)) + goto err; +out: + /* Trigger path verification. */ + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + return; +err: + dev_fsm_event(cdev, DEV_EVENT_NOTOPER); } static int io_subchannel_chp_event(struct subchannel *sch, @@ -1457,46 +1163,41 @@ static int io_subchannel_chp_event(struct subchannel *sch, return 0; } -static void -io_subchannel_shutdown(struct subchannel *sch) +static void io_subchannel_quiesce(struct subchannel *sch) { struct ccw_device *cdev; int ret; + spin_lock_irq(sch->lock); cdev = sch_get_cdev(sch); - if (cio_is_console(sch->schid)) - return; + goto out_unlock; if (!sch->schib.pmcw.ena) - /* Nothing to do. */ - return; + goto out_unlock; ret = cio_disable_subchannel(sch); if (ret != -EBUSY) - /* Subchannel is disabled, we're done. */ - return; - cdev->private->state = DEV_STATE_QUIESCE; + goto out_unlock; if (cdev->handler) - cdev->handler(cdev, cdev->private->intparm, - ERR_PTR(-EIO)); - ret = ccw_device_cancel_halt_clear(cdev); - if (ret == -EBUSY) { - ccw_device_set_timeout(cdev, HZ/10); - wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); + cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); + while (ret == -EBUSY) { + cdev->private->state = DEV_STATE_QUIESCE; + ret = ccw_device_cancel_halt_clear(cdev); + if (ret == -EBUSY) { + ccw_device_set_timeout(cdev, HZ/10); + spin_unlock_irq(sch->lock); + wait_event(cdev->private->wait_q, + cdev->private->state != DEV_STATE_QUIESCE); + spin_lock_irq(sch->lock); + } + ret = cio_disable_subchannel(sch); } - cio_disable_subchannel(sch); +out_unlock: + spin_unlock_irq(sch->lock); } -static int io_subchannel_get_status(struct subchannel *sch) +static void io_subchannel_shutdown(struct subchannel *sch) { - struct schib schib; - - if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) - return CIO_GONE; - if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) - return CIO_REVALIDATE; - if (!sch->lpm) - return CIO_NO_PATH; - return CIO_OPER; + io_subchannel_quiesce(sch); } static int device_is_disconnected(struct ccw_device *cdev) @@ -1575,20 +1276,16 @@ static void ccw_device_schedule_recovery(void) static int purge_fn(struct device *dev, void *data) { struct ccw_device *cdev = to_ccwdev(dev); - struct ccw_device_private *priv = cdev->private; - int unreg; + struct ccw_dev_id *id = &cdev->private->dev_id; spin_lock_irq(cdev->ccwlock); - unreg = is_blacklisted(priv->dev_id.ssid, priv->dev_id.devno) && - (priv->state == DEV_STATE_OFFLINE); + if (is_blacklisted(id->ssid, id->devno) && + (cdev->private->state == DEV_STATE_OFFLINE)) { + CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid, + id->devno); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); + } spin_unlock_irq(cdev->ccwlock); - if (!unreg) - goto out; - CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, - priv->dev_id.devno); - ccw_device_schedule_sch_unregister(cdev); - -out: /* Abort loop in case of pending signal. */ if (signal_pending(current)) return -EINTR; @@ -1630,91 +1327,169 @@ void ccw_device_set_notoper(struct ccw_device *cdev) cdev->private->state = DEV_STATE_NOT_OPER; } -static int io_subchannel_sch_event(struct subchannel *sch, int slow) +enum io_sch_action { + IO_SCH_UNREG, + IO_SCH_ORPH_UNREG, + IO_SCH_ATTACH, + IO_SCH_UNREG_ATTACH, + IO_SCH_ORPH_ATTACH, + IO_SCH_REPROBE, + IO_SCH_VERIFY, + IO_SCH_DISC, + IO_SCH_NOP, +}; + +static enum io_sch_action sch_get_action(struct subchannel *sch) +{ + struct ccw_device *cdev; + + cdev = sch_get_cdev(sch); + if (cio_update_schib(sch)) { + /* Not operational. */ + if (!cdev) + return IO_SCH_UNREG; + if (!ccw_device_notify(cdev, CIO_GONE)) + return IO_SCH_UNREG; + return IO_SCH_ORPH_UNREG; + } + /* Operational. */ + if (!cdev) + return IO_SCH_ATTACH; + if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { + if (!ccw_device_notify(cdev, CIO_GONE)) + return IO_SCH_UNREG_ATTACH; + return IO_SCH_ORPH_ATTACH; + } + if ((sch->schib.pmcw.pam & sch->opm) == 0) { + if (!ccw_device_notify(cdev, CIO_NO_PATH)) + return IO_SCH_UNREG; + return IO_SCH_DISC; + } + if (device_is_disconnected(cdev)) + return IO_SCH_REPROBE; + if (cdev->online) + return IO_SCH_VERIFY; + return IO_SCH_NOP; +} + +/** + * io_subchannel_sch_event - process subchannel event + * @sch: subchannel + * @process: non-zero if function is called in process context + * + * An unspecified event occurred for this subchannel. Adjust data according + * to the current operational state of the subchannel and device. Return + * zero when the event has been handled sufficiently or -EAGAIN when this + * function should be called again in process context. + */ +static int io_subchannel_sch_event(struct subchannel *sch, int process) { - int event, ret, disc; unsigned long flags; - enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE, DISC } action; struct ccw_device *cdev; + struct ccw_dev_id dev_id; + enum io_sch_action action; + int rc = -EAGAIN; spin_lock_irqsave(sch->lock, flags); + if (!device_is_registered(&sch->dev)) + goto out_unlock; + if (work_pending(&sch->todo_work)) + goto out_unlock; cdev = sch_get_cdev(sch); - disc = device_is_disconnected(cdev); - if (disc && slow) { - /* Disconnected devices are evaluated directly only.*/ - spin_unlock_irqrestore(sch->lock, flags); - return 0; - } - /* No interrupt after machine check - kill pending timers. */ - if (cdev) - ccw_device_set_timeout(cdev, 0); - if (!disc && !slow) { - /* Non-disconnected devices are evaluated on the slow path. */ - spin_unlock_irqrestore(sch->lock, flags); - return -EAGAIN; + if (cdev && work_pending(&cdev->private->todo_work)) + goto out_unlock; + action = sch_get_action(sch); + CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n", + sch->schid.ssid, sch->schid.sch_no, process, + action); + /* Perform immediate actions while holding the lock. */ + switch (action) { + case IO_SCH_REPROBE: + /* Trigger device recognition. */ + ccw_device_trigger_reprobe(cdev); + rc = 0; + goto out_unlock; + case IO_SCH_VERIFY: + /* Trigger path verification. */ + io_subchannel_verify(sch); + rc = 0; + goto out_unlock; + case IO_SCH_DISC: + ccw_device_set_disconnected(cdev); + rc = 0; + goto out_unlock; + case IO_SCH_ORPH_UNREG: + case IO_SCH_ORPH_ATTACH: + ccw_device_set_disconnected(cdev); + break; + case IO_SCH_UNREG_ATTACH: + case IO_SCH_UNREG: + if (cdev) + ccw_device_set_notoper(cdev); + break; + case IO_SCH_NOP: + rc = 0; + goto out_unlock; + default: + break; } - event = io_subchannel_get_status(sch); - CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", - sch->schid.ssid, sch->schid.sch_no, event, - disc ? "disconnected" : "normal", - slow ? "slow" : "fast"); - /* Analyze subchannel status. */ - action = NONE; - switch (event) { - case CIO_NO_PATH: - if (disc) { - /* Check if paths have become available. */ - action = REPROBE; - break; - } - /* fall through */ - case CIO_GONE: - /* Ask driver what to do with device. */ - if (io_subchannel_notify(sch, event)) - action = DISC; - else - action = UNREGISTER; + spin_unlock_irqrestore(sch->lock, flags); + /* All other actions require process context. */ + if (!process) + goto out; + /* Handle attached ccw device. */ + switch (action) { + case IO_SCH_ORPH_UNREG: + case IO_SCH_ORPH_ATTACH: + /* Move ccw device to orphanage. */ + rc = ccw_device_move_to_orph(cdev); + if (rc) + goto out; break; - case CIO_REVALIDATE: - /* Device will be removed, so no notify necessary. */ - if (disc) - /* Reprobe because immediate unregister might block. */ - action = REPROBE; - else - action = UNREGISTER_PROBE; + case IO_SCH_UNREG_ATTACH: + /* Unregister ccw device. */ + ccw_device_unregister(cdev); break; - case CIO_OPER: - if (disc) - /* Get device operational again. */ - action = REPROBE; + default: break; } - /* Perform action. */ - ret = 0; + /* Handle subchannel. */ switch (action) { - case UNREGISTER: - case UNREGISTER_PROBE: - ccw_device_set_notoper(cdev); - /* Unregister device (will use subchannel lock). */ - spin_unlock_irqrestore(sch->lock, flags); + case IO_SCH_ORPH_UNREG: + case IO_SCH_UNREG: css_sch_device_unregister(sch); - spin_lock_irqsave(sch->lock, flags); break; - case REPROBE: + case IO_SCH_ORPH_ATTACH: + case IO_SCH_UNREG_ATTACH: + case IO_SCH_ATTACH: + dev_id.ssid = sch->schid.ssid; + dev_id.devno = sch->schib.pmcw.dev; + cdev = get_ccwdev_by_dev_id(&dev_id); + if (!cdev) { + sch_create_and_recog_new_device(sch); + break; + } + rc = ccw_device_move_to_sch(cdev, sch); + if (rc) { + /* Release reference from get_ccwdev_by_dev_id() */ + put_device(&cdev->dev); + goto out; + } + spin_lock_irqsave(sch->lock, flags); ccw_device_trigger_reprobe(cdev); - break; - case DISC: - ccw_device_set_disconnected(cdev); + spin_unlock_irqrestore(sch->lock, flags); + /* Release reference from get_ccwdev_by_dev_id() */ + put_device(&cdev->dev); break; default: break; } - spin_unlock_irqrestore(sch->lock, flags); - /* Probe if necessary. */ - if (action == UNREGISTER_PROBE) - ret = css_probe_device(sch->schid); + return 0; - return ret; +out_unlock: + spin_unlock_irqrestore(sch->lock, flags); +out: + return rc; } #ifdef CONFIG_CCW_CONSOLE @@ -1744,10 +1519,7 @@ static int ccw_device_console_enable(struct ccw_device *cdev, sch->driver = &io_subchannel_driver; /* Initialize the ccw_device structure. */ cdev->dev.parent= &sch->dev; - rc = io_subchannel_recog(cdev, sch); - if (rc) - return rc; - + io_subchannel_recog(cdev, sch); /* Now wait for the async. recognition to come to an end. */ spin_lock_irq(cdev->ccwlock); while (!dev_fsm_final_state(cdev)) @@ -1763,7 +1535,7 @@ static int ccw_device_console_enable(struct ccw_device *cdev, rc = 0; out_unlock: spin_unlock_irq(cdev->ccwlock); - return 0; + return rc; } struct ccw_device * @@ -1919,7 +1691,7 @@ static int ccw_device_pm_prepare(struct device *dev) { struct ccw_device *cdev = to_ccwdev(dev); - if (work_pending(&cdev->private->kick_work)) + if (work_pending(&cdev->private->todo_work)) return -EAGAIN; /* Fail while device is being set online/offline. */ if (atomic_read(&cdev->private->onoff)) @@ -2005,7 +1777,6 @@ static int ccw_device_pm_thaw(struct device *dev) static void __ccw_device_pm_restore(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); - int ret; if (cio_is_console(sch->schid)) goto out; @@ -2015,22 +1786,10 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev) */ spin_lock_irq(sch->lock); cdev->private->flags.resuming = 1; - ret = ccw_device_recognition(cdev); + ccw_device_recognition(cdev); spin_unlock_irq(sch->lock); - if (ret) { - CIO_MSG_EVENT(0, "Couldn't start recognition for device " - "0.%x.%04x (ret=%d)\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - spin_lock_irq(sch->lock); - cdev->private->state = DEV_STATE_DISCONNECTED; - spin_unlock_irq(sch->lock); - /* notify driver after the resume cb */ - goto out; - } wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) || cdev->private->state == DEV_STATE_DISCONNECTED); - out: cdev->private->flags.resuming = 0; } @@ -2040,7 +1799,7 @@ static int resume_handle_boxed(struct ccw_device *cdev) cdev->private->state = DEV_STATE_BOXED; if (ccw_device_notify(cdev, CIO_BOXED)) return 0; - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); return -ENODEV; } @@ -2049,7 +1808,7 @@ static int resume_handle_disc(struct ccw_device *cdev) cdev->private->state = DEV_STATE_DISCONNECTED; if (ccw_device_notify(cdev, CIO_GONE)) return 0; - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); return -ENODEV; } @@ -2094,9 +1853,7 @@ static int ccw_device_pm_restore(struct device *dev) /* check if the device type has changed */ if (!ccw_device_test_sense_data(cdev)) { ccw_device_update_sense_data(cdev); - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_do_unbind_bind); - queue_work(ccw_device_work, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_REBIND); ret = -ENODEV; goto out_unlock; } @@ -2140,7 +1897,7 @@ out_disc_unlock: goto out_restore; out_unreg_unlock: - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL); ret = -ENODEV; out_unlock: spin_unlock_irq(sch->lock); @@ -2205,6 +1962,77 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev) return sch->schid; } +static void ccw_device_todo(struct work_struct *work) +{ + struct ccw_device_private *priv; + struct ccw_device *cdev; + struct subchannel *sch; + enum cdev_todo todo; + + priv = container_of(work, struct ccw_device_private, todo_work); + cdev = priv->cdev; + sch = to_subchannel(cdev->dev.parent); + /* Find out todo. */ + spin_lock_irq(cdev->ccwlock); + todo = priv->todo; + priv->todo = CDEV_TODO_NOTHING; + CIO_MSG_EVENT(4, "cdev_todo: cdev=0.%x.%04x todo=%d\n", + priv->dev_id.ssid, priv->dev_id.devno, todo); + spin_unlock_irq(cdev->ccwlock); + /* Perform todo. */ + switch (todo) { + case CDEV_TODO_ENABLE_CMF: + cmf_reenable(cdev); + break; + case CDEV_TODO_REBIND: + ccw_device_do_unbind_bind(cdev); + break; + case CDEV_TODO_REGISTER: + io_subchannel_register(cdev); + break; + case CDEV_TODO_UNREG_EVAL: + if (!sch_is_pseudo_sch(sch)) + css_schedule_eval(sch->schid); + /* fall-through */ + case CDEV_TODO_UNREG: + if (sch_is_pseudo_sch(sch)) + ccw_device_unregister(cdev); + else + ccw_device_call_sch_unregister(cdev); + break; + default: + break; + } + /* Release workqueue ref. */ + put_device(&cdev->dev); +} + +/** + * ccw_device_sched_todo - schedule ccw device operation + * @cdev: ccw device + * @todo: todo + * + * Schedule the operation identified by @todo to be performed on the slow path + * workqueue. Do nothing if another operation with higher priority is already + * scheduled. Needs to be called with ccwdev lock held. + */ +void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo) +{ + CIO_MSG_EVENT(4, "cdev_todo: sched cdev=0.%x.%04x todo=%d\n", + cdev->private->dev_id.ssid, cdev->private->dev_id.devno, + todo); + if (cdev->private->todo >= todo) + return; + cdev->private->todo = todo; + /* Get workqueue ref. */ + if (!get_device(&cdev->dev)) + return; + if (!queue_work(slow_path_wq, &cdev->private->todo_work)) { + /* Already queued, release workqueue ref. */ + put_device(&cdev->dev); + } +} + MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_online); EXPORT_SYMBOL(ccw_device_set_offline); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 246c6482842c..bcfe13e42638 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -21,7 +21,6 @@ enum dev_state { DEV_STATE_DISBAND_PGID, DEV_STATE_BOXED, /* states to wait for i/o completion before doing something */ - DEV_STATE_CLEAR_VERIFY, DEV_STATE_TIMEOUT_KILL, DEV_STATE_QUIESCE, /* special states for devices gone not operational */ @@ -29,6 +28,7 @@ enum dev_state { DEV_STATE_DISCONNECTED_SENSE_ID, DEV_STATE_CMFCHANGE, DEV_STATE_CMFUPDATE, + DEV_STATE_STEAL_LOCK, /* last element! */ NR_DEV_STATES }; @@ -81,17 +81,16 @@ void io_subchannel_init_config(struct subchannel *sch); int ccw_device_cancel_halt_clear(struct ccw_device *); -void ccw_device_do_unbind_bind(struct work_struct *); -void ccw_device_move_to_orphanage(struct work_struct *); int ccw_device_is_orphan(struct ccw_device *); -int ccw_device_recognition(struct ccw_device *); +void ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); int ccw_device_offline(struct ccw_device *); void ccw_device_update_sense_data(struct ccw_device *); int ccw_device_test_sense_data(struct ccw_device *); void ccw_device_schedule_sch_unregister(struct ccw_device *); int ccw_purge_blacklisted(void); +void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo); /* Function prototypes for device status and basic sense stuff. */ void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); @@ -99,24 +98,28 @@ void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); int ccw_device_accumulate_and_sense(struct ccw_device *, struct irb *); int ccw_device_do_sense(struct ccw_device *, struct irb *); +/* Function prototype for internal request handling. */ +int lpm_adjust(int lpm, int mask); +void ccw_request_start(struct ccw_device *); +int ccw_request_cancel(struct ccw_device *cdev); +void ccw_request_handler(struct ccw_device *cdev); +void ccw_request_timeout(struct ccw_device *cdev); +void ccw_request_notoper(struct ccw_device *cdev); + /* Function prototypes for sense id stuff. */ void ccw_device_sense_id_start(struct ccw_device *); -void ccw_device_sense_id_irq(struct ccw_device *, enum dev_event); void ccw_device_sense_id_done(struct ccw_device *, int); /* Function prototypes for path grouping stuff. */ -void ccw_device_sense_pgid_start(struct ccw_device *); -void ccw_device_sense_pgid_irq(struct ccw_device *, enum dev_event); -void ccw_device_sense_pgid_done(struct ccw_device *, int); - void ccw_device_verify_start(struct ccw_device *); -void ccw_device_verify_irq(struct ccw_device *, enum dev_event); void ccw_device_verify_done(struct ccw_device *, int); void ccw_device_disband_start(struct ccw_device *); -void ccw_device_disband_irq(struct ccw_device *, enum dev_event); void ccw_device_disband_done(struct ccw_device *, int); +void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *); +void ccw_device_stlck_done(struct ccw_device *, void *, int); + int ccw_device_call_handler(struct ccw_device *); int ccw_device_stlck(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index b9613d7df9ef..ae760658a131 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -229,8 +229,8 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) sch = to_subchannel(cdev->dev.parent); - ccw_device_set_timeout(cdev, 0); - cio_disable_subchannel(sch); + if (cio_disable_subchannel(sch)) + state = DEV_STATE_NOT_OPER; /* * Now that we tried recognition, we have performed device selection * through ssch() and the path information is up to date. @@ -263,22 +263,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) } switch (state) { case DEV_STATE_NOT_OPER: - CIO_MSG_EVENT(2, "SenseID : unknown device %04x on " - "subchannel 0.%x.%04x\n", - cdev->private->dev_id.devno, - sch->schid.ssid, sch->schid.sch_no); break; case DEV_STATE_OFFLINE: if (!cdev->online) { ccw_device_update_sense_data(cdev); - /* Issue device info message. */ - CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: " - "CU Type/Mod = %04X/%02X, Dev Type/Mod " - "= %04X/%02X\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - cdev->id.cu_type, cdev->id.cu_model, - cdev->id.dev_type, cdev->id.dev_model); break; } cdev->private->state = DEV_STATE_OFFLINE; @@ -289,16 +277,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) wake_up(&cdev->private->wait_q); } else { ccw_device_update_sense_data(cdev); - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_do_unbind_bind); - queue_work(ccw_device_work, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_REBIND); } return; case DEV_STATE_BOXED: - CIO_MSG_EVENT(0, "SenseID : boxed device %04x on " - " subchannel 0.%x.%04x\n", - cdev->private->dev_id.devno, - sch->schid.ssid, sch->schid.sch_no); if (cdev->id.cu_type != 0) { /* device was recognized before */ cdev->private->flags.recog_done = 1; cdev->private->state = DEV_STATE_BOXED; @@ -343,28 +325,16 @@ int ccw_device_notify(struct ccw_device *cdev, int event) return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; } -static void cmf_reenable_delayed(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - cmf_reenable(cdev); -} - static void ccw_device_oper_notify(struct ccw_device *cdev) { if (ccw_device_notify(cdev, CIO_OPER)) { /* Reenable channel measurements, if needed. */ - PREPARE_WORK(&cdev->private->kick_work, cmf_reenable_delayed); - queue_work(ccw_device_work, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF); return; } /* Driver doesn't want device back. */ ccw_device_set_notoper(cdev); - PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unbind_bind); - queue_work(ccw_device_work, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_REBIND); } /* @@ -392,14 +362,14 @@ ccw_device_done(struct ccw_device *cdev, int state) CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED)) - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); cdev->private->flags.donotify = 0; break; case DEV_STATE_NOT_OPER: CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (!ccw_device_notify(cdev, CIO_GONE)) - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); else ccw_device_set_disconnected(cdev); cdev->private->flags.donotify = 0; @@ -409,7 +379,7 @@ ccw_device_done(struct ccw_device *cdev, int state) "%04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (!ccw_device_notify(cdev, CIO_NO_PATH)) - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); else ccw_device_set_disconnected(cdev); cdev->private->flags.donotify = 0; @@ -425,107 +395,12 @@ ccw_device_done(struct ccw_device *cdev, int state) wake_up(&cdev->private->wait_q); } -static int cmp_pgid(struct pgid *p1, struct pgid *p2) -{ - char *c1; - char *c2; - - c1 = (char *)p1; - c2 = (char *)p2; - - return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1); -} - -static void __ccw_device_get_common_pgid(struct ccw_device *cdev) -{ - int i; - int last; - - last = 0; - for (i = 0; i < 8; i++) { - if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET) - /* No PGID yet */ - continue; - if (cdev->private->pgid[last].inf.ps.state1 == - SNID_STATE1_RESET) { - /* First non-zero PGID */ - last = i; - continue; - } - if (cmp_pgid(&cdev->private->pgid[i], - &cdev->private->pgid[last]) == 0) - /* Non-conflicting PGIDs */ - continue; - - /* PGID mismatch, can't pathgroup. */ - CIO_MSG_EVENT(0, "SNID - pgid mismatch for device " - "0.%x.%04x, can't pathgroup\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); - cdev->private->options.pgroup = 0; - return; - } - if (cdev->private->pgid[last].inf.ps.state1 == - SNID_STATE1_RESET) - /* No previous pgid found */ - memcpy(&cdev->private->pgid[0], - &channel_subsystems[0]->global_pgid, - sizeof(struct pgid)); - else - /* Use existing pgid */ - memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last], - sizeof(struct pgid)); -} - -/* - * Function called from device_pgid.c after sense path ground has completed. - */ -void -ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) -{ - struct subchannel *sch; - - sch = to_subchannel(cdev->dev.parent); - switch (err) { - case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */ - cdev->private->options.pgroup = 0; - break; - case 0: /* success */ - case -EACCES: /* partial success, some paths not operational */ - /* Check if all pgids are equal or 0. */ - __ccw_device_get_common_pgid(cdev); - break; - case -ETIME: /* Sense path group id stopped by timeout. */ - case -EUSERS: /* device is reserved for someone else. */ - ccw_device_done(cdev, DEV_STATE_BOXED); - return; - default: - ccw_device_done(cdev, DEV_STATE_NOT_OPER); - return; - } - /* Start Path Group verification. */ - cdev->private->state = DEV_STATE_VERIFY; - cdev->private->flags.doverify = 0; - ccw_device_verify_start(cdev); -} - /* * Start device recognition. */ -int -ccw_device_recognition(struct ccw_device *cdev) +void ccw_device_recognition(struct ccw_device *cdev) { - struct subchannel *sch; - int ret; - - sch = to_subchannel(cdev->dev.parent); - ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); - if (ret != 0) - /* Couldn't enable the subchannel for i/o. Sick device. */ - return ret; - - /* After 60s the device recognition is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); + struct subchannel *sch = to_subchannel(cdev->dev.parent); /* * We used to start here with a sense pgid to find out whether a device @@ -537,32 +412,33 @@ ccw_device_recognition(struct ccw_device *cdev) */ cdev->private->flags.recog_done = 0; cdev->private->state = DEV_STATE_SENSE_ID; + if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) { + ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); + return; + } ccw_device_sense_id_start(cdev); - return 0; } /* - * Handle timeout in device recognition. + * Handle events for states that use the ccw request infrastructure. */ -static void -ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) +static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e) { - int ret; - - ret = ccw_device_cancel_halt_clear(cdev); - switch (ret) { - case 0: - ccw_device_recog_done(cdev, DEV_STATE_BOXED); + switch (e) { + case DEV_EVENT_NOTOPER: + ccw_request_notoper(cdev); break; - case -ENODEV: - ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); + case DEV_EVENT_INTERRUPT: + ccw_request_handler(cdev); + break; + case DEV_EVENT_TIMEOUT: + ccw_request_timeout(cdev); break; default: - ccw_device_set_timeout(cdev, 3*HZ); + break; } } - void ccw_device_verify_done(struct ccw_device *cdev, int err) { @@ -571,21 +447,18 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) sch = to_subchannel(cdev->dev.parent); /* Update schib - pom may have changed. */ if (cio_update_schib(sch)) { - cdev->private->flags.donotify = 0; - ccw_device_done(cdev, DEV_STATE_NOT_OPER); - return; + err = -ENODEV; + goto callback; } /* Update lpm with verified path mask. */ sch->lpm = sch->vpm; /* Repeat path verification? */ if (cdev->private->flags.doverify) { - cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); return; } +callback: switch (err) { - case -EOPNOTSUPP: /* path grouping not supported, just set online. */ - cdev->private->options.pgroup = 0; case 0: ccw_device_done(cdev, DEV_STATE_ONLINE); /* Deliver fake irb to device driver, if needed. */ @@ -604,18 +477,20 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) } break; case -ETIME: + case -EUSERS: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; ccw_device_done(cdev, DEV_STATE_BOXED); break; + case -EACCES: + /* Reset oper notify indication after verify error. */ + cdev->private->flags.donotify = 0; + ccw_device_done(cdev, DEV_STATE_DISCONNECTED); + break; default: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; - if (cdev->online) { - ccw_device_set_timeout(cdev, 0); - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - } else - ccw_device_done(cdev, DEV_STATE_NOT_OPER); + ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } } @@ -640,17 +515,9 @@ ccw_device_online(struct ccw_device *cdev) dev_fsm_event(cdev, DEV_EVENT_NOTOPER); return ret; } - /* Do we want to do path grouping? */ - if (!cdev->private->options.pgroup) { - /* Start initial path verification. */ - cdev->private->state = DEV_STATE_VERIFY; - cdev->private->flags.doverify = 0; - ccw_device_verify_start(cdev); - return 0; - } - /* Do a SensePGID first. */ - cdev->private->state = DEV_STATE_SENSE_PGID; - ccw_device_sense_pgid_start(cdev); + /* Start initial path verification. */ + cdev->private->state = DEV_STATE_VERIFY; + ccw_device_verify_start(cdev); return 0; } @@ -666,7 +533,6 @@ ccw_device_disband_done(struct ccw_device *cdev, int err) break; default: cdev->private->flags.donotify = 0; - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } @@ -703,7 +569,7 @@ ccw_device_offline(struct ccw_device *cdev) if (cdev->private->state != DEV_STATE_ONLINE) return -EINVAL; /* Are we doing path grouping? */ - if (!cdev->private->options.pgroup) { + if (!cdev->private->flags.pgroup) { /* No, set state offline immediately. */ ccw_device_done(cdev, DEV_STATE_OFFLINE); return 0; @@ -715,43 +581,13 @@ ccw_device_offline(struct ccw_device *cdev) } /* - * Handle timeout in device online/offline process. - */ -static void -ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event) -{ - int ret; - - ret = ccw_device_cancel_halt_clear(cdev); - switch (ret) { - case 0: - ccw_device_done(cdev, DEV_STATE_BOXED); - break; - case -ENODEV: - ccw_device_done(cdev, DEV_STATE_NOT_OPER); - break; - default: - ccw_device_set_timeout(cdev, 3*HZ); - } -} - -/* - * Handle not oper event in device recognition. - */ -static void -ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) -{ - ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); -} - -/* * Handle not operational event in non-special state. */ static void ccw_device_generic_notoper(struct ccw_device *cdev, enum dev_event dev_event) { if (!ccw_device_notify(cdev, CIO_GONE)) - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); else ccw_device_set_disconnected(cdev); } @@ -802,11 +638,27 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) } /* Device is idle, we can do the path verification. */ cdev->private->state = DEV_STATE_VERIFY; - cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); } /* + * Handle path verification event in boxed state. + */ +static void ccw_device_boxed_verify(struct ccw_device *cdev, + enum dev_event dev_event) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + + if (cdev->online) { + if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) + ccw_device_done(cdev, DEV_STATE_NOT_OPER); + else + ccw_device_online_verify(cdev, dev_event); + } else + css_schedule_eval(sch->schid); +} + +/* * Got an interrupt for a normal io (state online). */ static void @@ -904,12 +756,6 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) */ if (scsw_fctl(&irb->scsw) & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) { - /* Retry Basic Sense if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - ccw_device_do_sense(cdev, irb); - return; - } cdev->private->flags.dosense = 0; memset(&cdev->private->irb, 0, sizeof(struct irb)); ccw_device_accumulate_irb(cdev, irb); @@ -933,21 +779,6 @@ call_handler: } static void -ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event) -{ - struct irb *irb; - - irb = (struct irb *) __LC_IRB; - /* Accumulate status. We don't do basic sense. */ - ccw_device_accumulate_irb(cdev, irb); - /* Remember to clear irb to avoid residuals. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - /* Try to start delayed device verification. */ - ccw_device_online_verify(cdev, 0); - /* Note: Don't call handler for cio initiated clear! */ -} - -static void ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) { struct subchannel *sch; @@ -1004,32 +835,6 @@ ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event) } static void -ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event) -{ - struct irb *irb; - - switch (dev_event) { - case DEV_EVENT_INTERRUPT: - irb = (struct irb *) __LC_IRB; - /* Check for unsolicited interrupt. */ - if ((scsw_stctl(&irb->scsw) == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) && - (!scsw_cc(&irb->scsw))) - /* FIXME: we should restart stlck here, but this - * is extremely unlikely ... */ - goto out_wakeup; - - ccw_device_accumulate_irb(cdev, irb); - /* We don't care about basic sense etc. */ - break; - default: /* timeout */ - break; - } -out_wakeup: - wake_up(&cdev->private->wait_q); -} - -static void ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) { struct subchannel *sch; @@ -1038,10 +843,6 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return; - - /* After 60s the device recognition is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); - cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID; ccw_device_sense_id_start(cdev); } @@ -1072,22 +873,20 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev) /* We should also udate ssd info, but this has to wait. */ /* Check if this is another device which appeared on the same sch. */ - if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_move_to_orphanage); - queue_work(slow_path_wq, &cdev->private->kick_work); - } else + if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) + css_schedule_eval(sch->schid); + else ccw_device_start_id(cdev, 0); } -static void -ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void ccw_device_disabled_irq(struct ccw_device *cdev, + enum dev_event dev_event) { struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); /* - * An interrupt in state offline means a previous disable was not + * An interrupt in a disabled state means a previous disable was not * successful - should not happen, but we try to disable again. */ cio_disable_subchannel(sch); @@ -1113,10 +912,7 @@ static void ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) { ccw_device_set_timeout(cdev, 0); - if (dev_event == DEV_EVENT_NOTOPER) - cdev->private->state = DEV_STATE_NOT_OPER; - else - cdev->private->state = DEV_STATE_OFFLINE; + cdev->private->state = DEV_STATE_NOT_OPER; wake_up(&cdev->private->wait_q); } @@ -1126,17 +922,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event) int ret; ret = ccw_device_cancel_halt_clear(cdev); - switch (ret) { - case 0: - cdev->private->state = DEV_STATE_OFFLINE; - wake_up(&cdev->private->wait_q); - break; - case -ENODEV: + if (ret == -EBUSY) { + ccw_device_set_timeout(cdev, HZ/10); + } else { cdev->private->state = DEV_STATE_NOT_OPER; wake_up(&cdev->private->wait_q); - break; - default: - ccw_device_set_timeout(cdev, HZ/10); } } @@ -1150,50 +940,37 @@ ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event) } /* - * Bug operation action. - */ -static void -ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event) -{ - CIO_MSG_EVENT(0, "Internal state [%i][%i] not handled for device " - "0.%x.%04x\n", cdev->private->state, dev_event, - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); - BUG(); -} - -/* * device statemachine */ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_STATE_NOT_OPER] = { [DEV_EVENT_NOTOPER] = ccw_device_nop, - [DEV_EVENT_INTERRUPT] = ccw_device_bug, + [DEV_EVENT_INTERRUPT] = ccw_device_disabled_irq, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_SENSE_PGID] = { - [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_SENSE_ID] = { - [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_OFFLINE] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_offline_irq, + [DEV_EVENT_INTERRUPT] = ccw_device_disabled_irq, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_offline_verify, }, [DEV_STATE_VERIFY] = { - [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_delay_verify, }, [DEV_STATE_ONLINE] = { @@ -1209,24 +986,18 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_online_verify, }, [DEV_STATE_DISBAND_PGID] = { - [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_disband_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_BOXED] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_stlck_done, - [DEV_EVENT_TIMEOUT] = ccw_device_stlck_done, - [DEV_EVENT_VERIFY] = ccw_device_nop, - }, - /* states to wait for i/o completion before doing something */ - [DEV_STATE_CLEAR_VERIFY] = { - [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_clear_verify, + [DEV_EVENT_INTERRUPT] = ccw_device_nop, [DEV_EVENT_TIMEOUT] = ccw_device_nop, - [DEV_EVENT_VERIFY] = ccw_device_nop, + [DEV_EVENT_VERIFY] = ccw_device_boxed_verify, }, + /* states to wait for i/o completion before doing something */ [DEV_STATE_TIMEOUT_KILL] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_killing_irq, @@ -1243,13 +1014,13 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_STATE_DISCONNECTED] = { [DEV_EVENT_NOTOPER] = ccw_device_nop, [DEV_EVENT_INTERRUPT] = ccw_device_start_id, - [DEV_EVENT_TIMEOUT] = ccw_device_bug, + [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_start_id, }, [DEV_STATE_DISCONNECTED_SENSE_ID] = { - [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_CMFCHANGE] = { @@ -1264,6 +1035,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock, [DEV_EVENT_VERIFY] = ccw_device_update_cmfblock, }, + [DEV_STATE_STEAL_LOCK] = { + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, + [DEV_EVENT_VERIFY] = ccw_device_nop, + }, }; EXPORT_SYMBOL_GPL(ccw_device_set_timeout); diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 1bdaa614e34f..78a0b43862c5 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -1,40 +1,39 @@ /* - * drivers/s390/cio/device_id.c + * CCW device SENSE ID I/O handling. * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com) - * - * Sense ID functions. + * Copyright IBM Corp. 2002,2009 + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> */ -#include <linux/module.h> -#include <linux/init.h> #include <linux/kernel.h> - +#include <linux/string.h> +#include <linux/types.h> +#include <linux/errno.h> #include <asm/ccwdev.h> -#include <asm/delay.h> +#include <asm/setup.h> #include <asm/cio.h> -#include <asm/lowcore.h> #include <asm/diag.h> #include "cio.h" #include "cio_debug.h" -#include "css.h" #include "device.h" -#include "ioasm.h" #include "io_sch.h" +#define SENSE_ID_RETRIES 256 +#define SENSE_ID_TIMEOUT (10 * HZ) +#define SENSE_ID_MIN_LEN 4 +#define SENSE_ID_BASIC_LEN 7 + /** - * vm_vdev_to_cu_type - Convert vm virtual device into control unit type - * for certain devices. - * @class: virtual device class - * @type: virtual device type + * diag210_to_senseid - convert diag 0x210 data to sense id information + * @senseid: sense id + * @diag: diag 0x210 data * - * Returns control unit type if a match was made or %0xffff otherwise. + * Return 0 on success, non-zero otherwise. */ -static int vm_vdev_to_cu_type(int class, int type) +static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag) { static struct { int class, type, cu_type; @@ -71,253 +70,153 @@ static int vm_vdev_to_cu_type(int class, int type) }; int i; - for (i = 0; i < ARRAY_SIZE(vm_devices); i++) - if (class == vm_devices[i].class && type == vm_devices[i].type) - return vm_devices[i].cu_type; + /* Special case for osa devices. */ + if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) { + senseid->cu_type = 0x3088; + senseid->cu_model = 0x60; + senseid->reserved = 0xff; + return 0; + } + for (i = 0; i < ARRAY_SIZE(vm_devices); i++) { + if (diag->vrdcvcla == vm_devices[i].class && + diag->vrdcvtyp == vm_devices[i].type) { + senseid->cu_type = vm_devices[i].cu_type; + senseid->reserved = 0xff; + return 0; + } + } - return 0xffff; + return -ENODEV; } /** - * diag_get_dev_info - retrieve device information via DIAG X'210' - * @devno: device number - * @ps: pointer to sense ID data area + * diag_get_dev_info - retrieve device information via diag 0x210 + * @cdev: ccw device * * Returns zero on success, non-zero otherwise. */ -static int diag_get_dev_info(u16 devno, struct senseid *ps) +static int diag210_get_dev_info(struct ccw_device *cdev) { + struct ccw_dev_id *dev_id = &cdev->private->dev_id; + struct senseid *senseid = &cdev->private->senseid; struct diag210 diag_data; - int ccode; - - CIO_TRACE_EVENT (4, "VMvdinf"); - - diag_data = (struct diag210) { - .vrdcdvno = devno, - .vrdclen = sizeof (diag_data), - }; - - ccode = diag210 (&diag_data); - if ((ccode == 0) || (ccode == 2)) { - ps->reserved = 0xff; - - /* Special case for osa devices. */ - if (diag_data.vrdcvcla == 0x02 && diag_data.vrdcvtyp == 0x20) { - ps->cu_type = 0x3088; - ps->cu_model = 0x60; - return 0; - } - ps->cu_type = vm_vdev_to_cu_type(diag_data.vrdcvcla, - diag_data.vrdcvtyp); - if (ps->cu_type != 0xffff) - return 0; - } - - CIO_MSG_EVENT(0, "DIAG X'210' for device %04X returned (cc = %d):" - "vdev class : %02X, vdev type : %04X \n ... " - "rdev class : %02X, rdev type : %04X, " - "rdev model: %02X\n", - devno, ccode, - diag_data.vrdcvcla, diag_data.vrdcvtyp, - diag_data.vrdcrccl, diag_data.vrdccrty, - diag_data.vrdccrmd); - + int rc; + + if (dev_id->ssid != 0) + return -ENODEV; + memset(&diag_data, 0, sizeof(diag_data)); + diag_data.vrdcdvno = dev_id->devno; + diag_data.vrdclen = sizeof(diag_data); + rc = diag210(&diag_data); + CIO_TRACE_EVENT(4, "diag210"); + CIO_HEX_EVENT(4, &rc, sizeof(rc)); + CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data)); + if (rc != 0 && rc != 2) + goto err_failed; + if (diag210_to_senseid(senseid, &diag_data)) + goto err_unknown; + return 0; + +err_unknown: + CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n", + dev_id->ssid, dev_id->devno); + return -ENODEV; +err_failed: + CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n", + dev_id->ssid, dev_id->devno, rc); return -ENODEV; } /* - * Start Sense ID helper function. - * Try to obtain the 'control unit'/'device type' information - * associated with the subchannel. + * Initialize SENSE ID data. */ -static int -__ccw_device_sense_id_start(struct ccw_device *cdev) -{ - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - - sch = to_subchannel(cdev->dev.parent); - /* Setup sense channel program. */ - ccw = cdev->private->iccws; - ccw->cmd_code = CCW_CMD_SENSE_ID; - ccw->cda = (__u32) __pa (&cdev->private->senseid); - ccw->count = sizeof (struct senseid); - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - /* Try on every path. */ - ret = -ENODEV; - while (cdev->private->imask != 0) { - cdev->private->senseid.cu_type = 0xFFFF; - if ((sch->opm & cdev->private->imask) != 0 && - cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* ret is 0, -EBUSY, -EACCES or -ENODEV */ - if (ret != -EACCES) - return ret; - } - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - } - return ret; -} - -void -ccw_device_sense_id_start(struct ccw_device *cdev) +static void snsid_init(struct ccw_device *cdev) { - int ret; - - memset (&cdev->private->senseid, 0, sizeof (struct senseid)); - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - ret = __ccw_device_sense_id_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_id_done(cdev, ret); + cdev->private->flags.esid = 0; + memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid)); + cdev->private->senseid.cu_type = 0xffff; } /* - * Called from interrupt context to check if a valid answer - * to Sense ID was received. + * Check for complete SENSE ID data. */ -static int -ccw_device_check_sense_id(struct ccw_device *cdev) +static int snsid_check(struct ccw_device *cdev, void *data) { - struct subchannel *sch; - struct irb *irb; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - - /* Check the error cases. */ - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry Sense ID if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; - } - return -ETIME; - } - if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) { - /* - * if the device doesn't support the SenseID - * command further retries wouldn't help ... - * NB: We don't check here for intervention required like we - * did before, because tape devices with no tape inserted - * may present this status *in conjunction with* the - * sense id information. So, for intervention required, - * we use the "whack it until it talks" strategy... - */ - CIO_MSG_EVENT(0, "SenseID : device %04x on Subchannel " - "0.%x.%04x reports cmd reject\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no); + struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd; + int len = sizeof(struct senseid) - scsw->count; + + /* Check for incomplete SENSE ID data. */ + if (len < SENSE_ID_MIN_LEN) + goto out_restart; + if (cdev->private->senseid.cu_type == 0xffff) + goto out_restart; + /* Check for incompatible SENSE ID data. */ + if (cdev->private->senseid.reserved != 0xff) return -EOPNOTSUPP; - } - if (irb->esw.esw0.erw.cons) { - CIO_MSG_EVENT(2, "SenseID : UC on dev 0.%x.%04x, " - "lpum %02X, cnt %02d, sns :" - " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - irb->esw.esw0.sublog.lpum, - irb->esw.esw0.erw.scnt, - irb->ecw[0], irb->ecw[1], - irb->ecw[2], irb->ecw[3], - irb->ecw[4], irb->ecw[5], - irb->ecw[6], irb->ecw[7]); - return -EAGAIN; - } - if (irb->scsw.cmd.cc == 3) { - u8 lpm; + /* Check for extended-identification information. */ + if (len > SENSE_ID_BASIC_LEN) + cdev->private->flags.esid = 1; + return 0; - lpm = to_io_private(sch)->orb.cmd.lpm; - if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) - CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x " - "on subchannel 0.%x.%04x is " - "'not operational'\n", lpm, - cdev->private->dev_id.devno, - sch->schid.ssid, sch->schid.sch_no); - return -EACCES; - } - - /* Did we get a proper answer ? */ - if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF && - cdev->private->senseid.reserved == 0xFF) { - if (irb->scsw.cmd.count < sizeof(struct senseid) - 8) - cdev->private->flags.esid = 1; - return 0; /* Success */ - } - - /* Hmm, whatever happened, try again. */ - CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on " - "subchannel 0.%x.%04x returns status %02X%02X\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, - irb->scsw.cmd.dstat, irb->scsw.cmd.cstat); +out_restart: + snsid_init(cdev); return -EAGAIN; } /* - * Got interrupt for Sense ID. + * Process SENSE ID request result. */ -void -ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void snsid_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - int ret; - - sch = to_subchannel(cdev->dev.parent); - irb = (struct irb *) __LC_IRB; - /* Retry sense id, if needed. */ - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) { - ret = __ccw_device_sense_id_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_id_done(cdev, ret); + struct ccw_dev_id *id = &cdev->private->dev_id; + struct senseid *senseid = &cdev->private->senseid; + int vm = 0; + + if (rc && MACHINE_IS_VM) { + /* Try diag 0x210 fallback on z/VM. */ + snsid_init(cdev); + if (diag210_get_dev_info(cdev) == 0) { + rc = 0; + vm = 1; } - return; } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - ret = ccw_device_check_sense_id(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - switch (ret) { - /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN or -EACCES */ - case 0: /* Sense id succeeded. */ - case -ETIME: /* Sense id stopped by timeout. */ - ccw_device_sense_id_done(cdev, ret); - break; - case -EACCES: /* channel is not operational. */ - sch->lpm &= ~cdev->private->imask; - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - /* fall through. */ - case -EAGAIN: /* try again. */ - ret = __ccw_device_sense_id_start(cdev); - if (ret == 0 || ret == -EBUSY) - break; - /* fall through. */ - default: /* Sense ID failed. Try asking VM. */ - if (MACHINE_IS_VM) - ret = diag_get_dev_info(cdev->private->dev_id.devno, - &cdev->private->senseid); - else - /* - * If we can't couldn't identify the device type we - * consider the device "not operational". - */ - ret = -ENODEV; + CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x " + "%04x/%02x%s\n", id->ssid, id->devno, rc, + senseid->cu_type, senseid->cu_model, senseid->dev_type, + senseid->dev_model, vm ? " (diag210)" : ""); + ccw_device_sense_id_done(cdev, rc); +} - ccw_device_sense_id_done(cdev, ret); - break; - } +/** + * ccw_device_sense_id_start - perform SENSE ID + * @cdev: ccw device + * + * Execute a SENSE ID channel program on @cdev to update its sense id + * information. When finished, call ccw_device_sense_id_done with a + * return code specifying the result. + */ +void ccw_device_sense_id_start(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + + CIO_TRACE_EVENT(4, "snsid"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Data setup. */ + snsid_init(cdev); + /* Channel program setup. */ + cp->cmd_code = CCW_CMD_SENSE_ID; + cp->cda = (u32) (addr_t) &cdev->private->senseid; + cp->count = sizeof(struct senseid); + cp->flags = CCW_FLAG_SLI; + /* Request setup. */ + memset(req, 0, sizeof(*req)); + req->cp = cp; + req->timeout = SENSE_ID_TIMEOUT; + req->maxretries = SENSE_ID_RETRIES; + req->lpm = sch->schib.pmcw.pam & sch->opm; + req->check = snsid_check; + req->callback = snsid_callback; + ccw_request_start(cdev); } diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 2d0efee8a290..6da84543dfe9 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -11,6 +11,7 @@ #include <linux/list.h> #include <linux/device.h> #include <linux/delay.h> +#include <linux/completion.h> #include <asm/ccwdev.h> #include <asm/idals.h> @@ -46,6 +47,7 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0; cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0; cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0; + cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0; return 0; } @@ -74,6 +76,7 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags) cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0; cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0; cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0; + cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0; return 0; } @@ -90,9 +93,34 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags) cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0; cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0; cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; + cdev->private->options.mpath &= (flags & CCWDEV_DO_MULTIPATH) == 0; } /** + * ccw_device_is_pathgroup - determine if paths to this device are grouped + * @cdev: ccw device + * + * Return non-zero if there is a path group, zero otherwise. + */ +int ccw_device_is_pathgroup(struct ccw_device *cdev) +{ + return cdev->private->flags.pgroup; +} +EXPORT_SYMBOL(ccw_device_is_pathgroup); + +/** + * ccw_device_is_multipath - determine if device is operating in multipath mode + * @cdev: ccw device + * + * Return non-zero if device is operating in multipath mode, zero otherwise. + */ +int ccw_device_is_multipath(struct ccw_device *cdev) +{ + return cdev->private->flags.mpath; +} +EXPORT_SYMBOL(ccw_device_is_multipath); + +/** * ccw_device_clear() - terminate I/O request processing * @cdev: target ccw device * @intparm: interruption parameter; value is only used if no I/O is @@ -167,8 +195,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, return -EINVAL; if (cdev->private->state == DEV_STATE_NOT_OPER) return -ENODEV; - if (cdev->private->state == DEV_STATE_VERIFY || - cdev->private->state == DEV_STATE_CLEAR_VERIFY) { + if (cdev->private->state == DEV_STATE_VERIFY) { /* Remember to fake irb when finished. */ if (!cdev->private->flags.fake_irb) { cdev->private->flags.fake_irb = 1; @@ -478,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev) return sch->lpm; } -/* - * Try to break the lock on a boxed device. - */ -int -ccw_device_stlck(struct ccw_device *cdev) -{ - void *buf, *buf2; - unsigned long flags; - struct subchannel *sch; - int ret; +struct stlck_data { + struct completion done; + int rc; +}; - if (!cdev) - return -ENODEV; +void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc) +{ + struct stlck_data *sdata = data; - if (cdev->drv && !cdev->private->options.force) - return -EINVAL; + sdata->rc = rc; + complete(&sdata->done); +} - sch = to_subchannel(cdev->dev.parent); - - CIO_TRACE_EVENT(2, "stl lock"); - CIO_TRACE_EVENT(2, dev_name(&cdev->dev)); +/* + * Perform unconditional reserve + release. + */ +int ccw_device_stlck(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct stlck_data data; + u8 *buffer; + int rc; - buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL); - if (!buf) - return -ENOMEM; - buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL); - if (!buf2) { - kfree(buf); - return -ENOMEM; + /* Check if steal lock operation is valid for this device. */ + if (cdev->drv) { + if (!cdev->private->options.force) + return -EINVAL; } - spin_lock_irqsave(sch->lock, flags); - ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); - if (ret) - goto out_unlock; - /* - * Setup ccw. We chain an unconditional reserve and a release so we - * only break the lock. - */ - cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK; - cdev->private->iccws[0].cda = (__u32) __pa(buf); - cdev->private->iccws[0].count = 32; - cdev->private->iccws[0].flags = CCW_FLAG_CC; - cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE; - cdev->private->iccws[1].cda = (__u32) __pa(buf2); - cdev->private->iccws[1].count = 32; - cdev->private->iccws[1].flags = 0; - ret = cio_start(sch, cdev->private->iccws, 0); - if (ret) { - cio_disable_subchannel(sch); //FIXME: return code? + buffer = kzalloc(64, GFP_DMA | GFP_KERNEL); + if (!buffer) + return -ENOMEM; + init_completion(&data.done); + data.rc = -EIO; + spin_lock_irq(sch->lock); + rc = cio_enable_subchannel(sch, (u32) (addr_t) sch); + if (rc) goto out_unlock; + /* Perform operation. */ + cdev->private->state = DEV_STATE_STEAL_LOCK, + ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]); + spin_unlock_irq(sch->lock); + /* Wait for operation to finish. */ + if (wait_for_completion_interruptible(&data.done)) { + /* Got a signal. */ + spin_lock_irq(sch->lock); + ccw_request_cancel(cdev); + spin_unlock_irq(sch->lock); + wait_for_completion(&data.done); } - cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND; - spin_unlock_irqrestore(sch->lock, flags); - wait_event(cdev->private->wait_q, - cdev->private->irb.scsw.cmd.actl == 0); - spin_lock_irqsave(sch->lock, flags); - cio_disable_subchannel(sch); //FIXME: return code? - if ((cdev->private->irb.scsw.cmd.dstat != - (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || - (cdev->private->irb.scsw.cmd.cstat != 0)) - ret = -EIO; - /* Clear irb. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); + rc = data.rc; + /* Check results. */ + spin_lock_irq(sch->lock); + cio_disable_subchannel(sch); + cdev->private->state = DEV_STATE_BOXED; out_unlock: - kfree(buf); - kfree(buf2); - spin_unlock_irqrestore(sch->lock, flags); - return ret; + spin_unlock_irq(sch->lock); + kfree(buffer); + + return rc; } void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index fc5ca1dd52b3..aad188e43b4f 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -1,594 +1,561 @@ /* - * drivers/s390/cio/device_pgid.c + * CCW device PGID and path verification I/O handling. * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com) - * - * Path Group ID functions. + * Copyright IBM Corp. 2002,2009 + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> */ -#include <linux/module.h> -#include <linux/init.h> - +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/bitops.h> #include <asm/ccwdev.h> #include <asm/cio.h> -#include <asm/delay.h> -#include <asm/lowcore.h> #include "cio.h" #include "cio_debug.h" -#include "css.h" #include "device.h" -#include "ioasm.h" #include "io_sch.h" +#define PGID_RETRIES 256 +#define PGID_TIMEOUT (10 * HZ) + /* - * Helper function called from interrupt context to decide whether an - * operation should be tried again. + * Process path verification data and report result. */ -static int __ccw_device_should_retry(union scsw *scsw) +static void verify_done(struct ccw_device *cdev, int rc) { - /* CC is only valid if start function bit is set. */ - if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1) - return 1; - /* No more activity. For sense and set PGID we stubbornly try again. */ - if (!scsw->cmd.actl) - return 1; - return 0; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_dev_id *id = &cdev->private->dev_id; + int mpath = cdev->private->flags.mpath; + int pgroup = cdev->private->flags.pgroup; + + if (rc) + goto out; + /* Ensure consistent multipathing state at device and channel. */ + if (sch->config.mp != mpath) { + sch->config.mp = mpath; + rc = cio_commit_config(sch); + } +out: + CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d " + "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath, + sch->vpm); + ccw_device_verify_done(cdev, rc); } /* - * Start Sense Path Group ID helper function. Used in ccw_device_recog - * and ccw_device_sense_pgid. + * Create channel program to perform a NOOP. */ -static int -__ccw_device_sense_pgid_start(struct ccw_device *cdev) +static void nop_build_cp(struct ccw_device *cdev) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - int i; - - sch = to_subchannel(cdev->dev.parent); - /* Return if we already checked on all paths. */ - if (cdev->private->imask == 0) - return (sch->lpm == 0) ? -ENODEV : -EACCES; - i = 8 - ffs(cdev->private->imask); - - /* Setup sense path group id channel program. */ - ccw = cdev->private->iccws; - ccw->cmd_code = CCW_CMD_SENSE_PGID; - ccw->count = sizeof (struct pgid); - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - /* Try on every path. */ - ret = -ENODEV; - while (cdev->private->imask != 0) { - /* Try every path multiple times. */ - ccw->cda = (__u32) __pa (&cdev->private->pgid[i]); - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* ret is 0, -EBUSY, -EACCES or -ENODEV */ - if (ret != -EACCES) - return ret; - CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not " - "operational'\n", - cdev->private->dev_id.devno, - sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - - } - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - i++; - } - - return ret; + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + + cp->cmd_code = CCW_CMD_NOOP; + cp->cda = 0; + cp->count = 0; + cp->flags = CCW_FLAG_SLI; + req->cp = cp; } -void -ccw_device_sense_pgid_start(struct ccw_device *cdev) +/* + * Perform NOOP on a single path. + */ +static void nop_do(struct ccw_device *cdev) { - int ret; - - /* Set a timeout of 60s */ - ccw_device_set_timeout(cdev, 60*HZ); - - cdev->private->state = DEV_STATE_SENSE_PGID; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid)); - ret = __ccw_device_sense_pgid_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + /* Adjust lpm. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm); + if (!req->lpm) + goto out_nopath; + nop_build_cp(cdev); + ccw_request_start(cdev); + return; + +out_nopath: + verify_done(cdev, sch->vpm ? 0 : -EACCES); } /* - * Called from interrupt context to check if a valid answer - * to Sense Path Group ID was received. + * Adjust NOOP I/O status. */ -static int -__ccw_device_check_sense_pgid(struct ccw_device *cdev) +static enum io_status nop_filter(struct ccw_device *cdev, void *data, + struct irb *irb, enum io_status status) { - struct subchannel *sch; - struct irb *irb; - int i; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry Sense PGID if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; - } - return -ETIME; - } - if (irb->esw.esw0.erw.cons && - (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) { - /* - * If the device doesn't support the Sense Path Group ID - * command further retries wouldn't help ... - */ - return -EOPNOTSUPP; - } - if (irb->esw.esw0.erw.cons) { - CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, " - "lpum %02X, cnt %02d, sns : " - "%02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - irb->esw.esw0.sublog.lpum, - irb->esw.esw0.erw.scnt, - irb->ecw[0], irb->ecw[1], - irb->ecw[2], irb->ecw[3], - irb->ecw[4], irb->ecw[5], - irb->ecw[6], irb->ecw[7]); - return -EAGAIN; - } - if (irb->scsw.cmd.cc == 3) { - u8 lpm; - - lpm = to_io_private(sch)->orb.cmd.lpm; - CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, lpm); - return -EACCES; - } - i = 8 - ffs(cdev->private->imask); - if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { - CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " - "is reserved by someone else\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no); - return -EUSERS; - } - return 0; + /* Only subchannel status might indicate a path error. */ + if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0) + return IO_DONE; + return status; } /* - * Got interrupt for Sense Path Group ID. + * Process NOOP request result for a single path. */ -void -ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void nop_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - int ret; - - irb = (struct irb *) __LC_IRB; - - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) { - ret = __ccw_device_sense_pgid_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); - } - return; - } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - ret = __ccw_device_check_sense_pgid(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - switch (ret) { - /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ - case -EOPNOTSUPP: /* Sense Path Group ID not supported */ - ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); - break; - case -ETIME: /* Sense path group id stopped by timeout. */ - ccw_device_sense_pgid_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - sch->lpm &= ~cdev->private->imask; - /* Fall through. */ - case 0: /* Sense Path Group ID successful. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - /* Fall through. */ - case -EAGAIN: /* Try again. */ - ret = __ccw_device_sense_pgid_start(cdev); - if (ret != 0 && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); - break; - case -EUSERS: /* device is reserved for someone else. */ - ccw_device_sense_pgid_done(cdev, -EUSERS); - break; - } + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + if (rc == 0) + sch->vpm |= req->lpm; + else if (rc != -EACCES) + goto err; + req->lpm >>= 1; + nop_do(cdev); + return; + +err: + verify_done(cdev, rc); } /* - * Path Group ID helper function. + * Create channel program to perform SET PGID on a single path. */ -static int -__ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) +static void spid_build_cp(struct ccw_device *cdev, u8 fn) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - - sch = to_subchannel(cdev->dev.parent); - - /* Setup sense path group id channel program. */ - cdev->private->pgid[0].inf.fc = func; - ccw = cdev->private->iccws; - if (cdev->private->flags.pgid_single) - cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH; - else - cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH; - ccw->cmd_code = CCW_CMD_SET_PGID; - ccw->cda = (__u32) __pa (&cdev->private->pgid[0]); - ccw->count = sizeof (struct pgid); - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - /* Try multiple times. */ - ret = -EACCES; - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* We expect an interrupt in case of success or busy - * indication. */ - if ((ret == 0) || (ret == -EBUSY)) - return ret; - } - /* PGID command failed on this path. */ - CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return ret; + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + int i = 8 - ffs(req->lpm); + struct pgid *pgid = &cdev->private->pgid[i]; + + pgid->inf.fc = fn; + cp->cmd_code = CCW_CMD_SET_PGID; + cp->cda = (u32) (addr_t) pgid; + cp->count = sizeof(*pgid); + cp->flags = CCW_FLAG_SLI; + req->cp = cp; } /* - * Helper function to send a nop ccw down a path. + * Perform establish/resign SET PGID on a single path. */ -static int __ccw_device_do_nop(struct ccw_device *cdev) +static void spid_do(struct ccw_device *cdev) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - - sch = to_subchannel(cdev->dev.parent); - - /* Setup nop channel program. */ - ccw = cdev->private->iccws; - ccw->cmd_code = CCW_CMD_NOOP; - ccw->cda = 0; - ccw->count = 0; - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - /* Try multiple times. */ - ret = -EACCES; - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* We expect an interrupt in case of success or busy - * indication. */ - if ((ret == 0) || (ret == -EBUSY)) - return ret; - } - /* nop command failed on this path. */ - CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return ret; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + u8 fn; + + /* Use next available path that is not already in correct state. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm); + if (!req->lpm) + goto out_nopath; + /* Channel program setup. */ + if (req->lpm & sch->opm) + fn = SPID_FUNC_ESTABLISH; + else + fn = SPID_FUNC_RESIGN; + if (cdev->private->flags.mpath) + fn |= SPID_FUNC_MULTI_PATH; + spid_build_cp(cdev, fn); + ccw_request_start(cdev); + return; + +out_nopath: + verify_done(cdev, sch->vpm ? 0 : -EACCES); } +static void verify_start(struct ccw_device *cdev); /* - * Called from interrupt context to check if a valid answer - * to Set Path Group ID was received. + * Process SET PGID request result for a single path. */ -static int -__ccw_device_check_pgid(struct ccw_device *cdev) +static void spid_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry Set PGID if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + switch (rc) { + case 0: + sch->vpm |= req->lpm & sch->opm; + break; + case -EACCES: + break; + case -EOPNOTSUPP: + if (cdev->private->flags.mpath) { + /* Try without multipathing. */ + cdev->private->flags.mpath = 0; + goto out_restart; } - return -ETIME; + /* Try without pathgrouping. */ + cdev->private->flags.pgroup = 0; + goto out_restart; + default: + goto err; } - if (irb->esw.esw0.erw.cons) { - if (irb->ecw[0] & SNS0_CMD_REJECT) - return -EOPNOTSUPP; - /* Hmm, whatever happened, try again. */ - CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, " - "cnt %02d, " - "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - irb->esw.esw0.erw.scnt, - irb->ecw[0], irb->ecw[1], - irb->ecw[2], irb->ecw[3], - irb->ecw[4], irb->ecw[5], - irb->ecw[6], irb->ecw[7]); - return -EAGAIN; - } - if (irb->scsw.cmd.cc == 3) { - CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return -EACCES; - } - return 0; + req->lpm >>= 1; + spid_do(cdev); + return; + +out_restart: + verify_start(cdev); + return; +err: + verify_done(cdev, rc); +} + +static void spid_start(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + + /* Initialize request data. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = 0x80; + req->callback = spid_callback; + spid_do(cdev); +} + +static int pgid_cmp(struct pgid *p1, struct pgid *p2) +{ + return memcmp((char *) p1 + 1, (char *) p2 + 1, + sizeof(struct pgid) - 1); } /* - * Called from interrupt context to check the path status after a nop has - * been send. + * Determine pathgroup state from PGID data. */ -static int __ccw_device_check_nop(struct ccw_device *cdev) +static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, + int *mismatch, int *reserved, int *reset) { - struct subchannel *sch; - struct irb *irb; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry NOP if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; + struct pgid *pgid = &cdev->private->pgid[0]; + struct pgid *first = NULL; + int lpm; + int i; + + *mismatch = 0; + *reserved = 0; + *reset = 0; + for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) { + if ((cdev->private->pgid_valid_mask & lpm) == 0) + continue; + if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) + *reserved = 1; + if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { + /* A PGID was reset. */ + *reset = 1; + continue; } - return -ETIME; - } - if (irb->scsw.cmd.cc == 3) { - CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return -EACCES; + if (!first) { + first = pgid; + continue; + } + if (pgid_cmp(pgid, first) != 0) + *mismatch = 1; } - return 0; + if (!first) + first = &channel_subsystems[0]->global_pgid; + *p = first; } -static void -__ccw_device_verify_start(struct ccw_device *cdev) +static u8 pgid_to_vpm(struct ccw_device *cdev) { - struct subchannel *sch; - __u8 func; - int ret; - - sch = to_subchannel(cdev->dev.parent); - /* Repeat for all paths. */ - for (; cdev->private->imask; cdev->private->imask >>= 1, - cdev->private->iretry = 5) { - if ((cdev->private->imask & sch->schib.pmcw.pam) == 0) - /* Path not available, try next. */ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct pgid *pgid; + int i; + int lpm; + u8 vpm = 0; + + /* Set VPM bits for paths which are already in the target state. */ + for (i = 0; i < 8; i++) { + lpm = 0x80 >> i; + if ((cdev->private->pgid_valid_mask & lpm) == 0) continue; - if (cdev->private->options.pgroup) { - if (sch->opm & cdev->private->imask) - func = SPID_FUNC_ESTABLISH; - else - func = SPID_FUNC_RESIGN; - ret = __ccw_device_do_pgid(cdev, func); - } else - ret = __ccw_device_do_nop(cdev); - /* We expect an interrupt in case of success or busy - * indication. */ - if (ret == 0 || ret == -EBUSY) - return; - /* Permanent path failure, try next. */ + pgid = &cdev->private->pgid[i]; + if (sch->opm & lpm) { + if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED) + continue; + } else { + if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED) + continue; + } + if (cdev->private->flags.mpath) { + if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH) + continue; + } else { + if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH) + continue; + } + vpm |= lpm; } - /* Done with all paths. */ - ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV); + + return vpm; } - -/* - * Got interrupt for Set Path Group ID. - */ -void -ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) + +static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) { - struct subchannel *sch; - struct irb *irb; - int ret; + int i; - irb = (struct irb *) __LC_IRB; + for (i = 0; i < 8; i++) + memcpy(&cdev->private->pgid[i], pgid, sizeof(struct pgid)); +} - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) - __ccw_device_verify_start(cdev); - return; +/* + * Process SENSE PGID data and report result. + */ +static void snid_done(struct ccw_device *cdev, int rc) +{ + struct ccw_dev_id *id = &cdev->private->dev_id; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct pgid *pgid; + int mismatch = 0; + int reserved = 0; + int reset = 0; + + if (rc) + goto out; + pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); + if (reserved) + rc = -EUSERS; + else if (mismatch) + rc = -EOPNOTSUPP; + else { + sch->vpm = pgid_to_vpm(cdev); + pgid_fill(cdev, pgid); } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - if (cdev->private->options.pgroup) - ret = __ccw_device_check_pgid(cdev); - else - ret = __ccw_device_check_nop(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - switch (ret) { - /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ +out: + CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " + "mism=%d rsvd=%d reset=%d\n", id->ssid, id->devno, rc, + cdev->private->pgid_valid_mask, sch->vpm, mismatch, + reserved, reset); + switch (rc) { case 0: - /* Path verification ccw finished successfully, update lpm. */ - sch->vpm |= sch->opm & cdev->private->imask; - /* Go on with next path. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_verify_start(cdev); + /* Anything left to do? */ + if (sch->vpm == sch->schib.pmcw.pam) { + verify_done(cdev, sch->vpm == 0 ? -EACCES : 0); + return; + } + /* Perform path-grouping. */ + spid_start(cdev); break; case -EOPNOTSUPP: - /* - * One of those strange devices which claim to be able - * to do multipathing but not for Set Path Group ID. - */ - if (cdev->private->flags.pgid_single) - cdev->private->options.pgroup = 0; - else - cdev->private->flags.pgid_single = 1; - /* Retry */ - sch->vpm = 0; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - /* fall through. */ - case -EAGAIN: /* Try again. */ - __ccw_device_verify_start(cdev); - break; - case -ETIME: /* Set path group id stopped by timeout. */ - ccw_device_verify_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_verify_start(cdev); + /* Path-grouping not supported. */ + cdev->private->flags.pgroup = 0; + cdev->private->flags.mpath = 0; + verify_start(cdev); break; + default: + verify_done(cdev, rc); } } -void -ccw_device_verify_start(struct ccw_device *cdev) +/* + * Create channel program to perform a SENSE PGID on a single path. + */ +static void snid_build_cp(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + int i = 8 - ffs(req->lpm); + + /* Channel program setup. */ + cp->cmd_code = CCW_CMD_SENSE_PGID; + cp->cda = (u32) (addr_t) &cdev->private->pgid[i]; + cp->count = sizeof(struct pgid); + cp->flags = CCW_FLAG_SLI; + req->cp = cp; +} + +/* + * Perform SENSE PGID on a single path. + */ +static void snid_do(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + /* Adjust lpm if paths are not set in pam. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); + if (!req->lpm) + goto out_nopath; + snid_build_cp(cdev); + ccw_request_start(cdev); + return; + +out_nopath: + snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES); +} - cdev->private->flags.pgid_single = 0; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; +/* + * Process SENSE PGID request result for single path. + */ +static void snid_callback(struct ccw_device *cdev, void *data, int rc) +{ + struct ccw_request *req = &cdev->private->req; + + if (rc == 0) + cdev->private->pgid_valid_mask |= req->lpm; + else if (rc != -EACCES) + goto err; + req->lpm >>= 1; + snid_do(cdev); + return; + +err: + snid_done(cdev, rc); +} - /* Start with empty vpm. */ - sch->vpm = 0; +/* + * Perform path verification. + */ +static void verify_start(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + struct ccw_dev_id *devid = &cdev->private->dev_id; - /* Get current pam. */ - if (cio_update_schib(sch)) { - ccw_device_verify_done(cdev, -ENODEV); - return; + sch->vpm = 0; + /* Initialize request data. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = 0x80; + if (cdev->private->flags.pgroup) { + CIO_TRACE_EVENT(4, "snid"); + CIO_HEX_EVENT(4, devid, sizeof(*devid)); + req->callback = snid_callback; + snid_do(cdev); + } else { + CIO_TRACE_EVENT(4, "nop"); + CIO_HEX_EVENT(4, devid, sizeof(*devid)); + req->filter = nop_filter; + req->callback = nop_callback; + nop_do(cdev); } - /* After 60s path verification is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); - __ccw_device_verify_start(cdev); } -static void -__ccw_device_disband_start(struct ccw_device *cdev) +/** + * ccw_device_verify_start - perform path verification + * @cdev: ccw device + * + * Perform an I/O on each available channel path to @cdev to determine which + * paths are operational. The resulting path mask is stored in sch->vpm. + * If device options specify pathgrouping, establish a pathgroup for the + * operational paths. When finished, call ccw_device_verify_done with a + * return code specifying the result. + */ +void ccw_device_verify_start(struct ccw_device *cdev) { - struct subchannel *sch; - int ret; - - sch = to_subchannel(cdev->dev.parent); - while (cdev->private->imask != 0) { - if (sch->lpm & cdev->private->imask) { - ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND); - if (ret == 0) - return; - } - cdev->private->iretry = 5; - cdev->private->imask >>= 1; - } - ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); + CIO_TRACE_EVENT(4, "vrfy"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Initialize PGID data. */ + memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); + cdev->private->pgid_valid_mask = 0; + /* + * Initialize pathgroup and multipath state with target values. + * They may change in the course of path verification. + */ + cdev->private->flags.pgroup = cdev->private->options.pgroup; + cdev->private->flags.mpath = cdev->private->options.mpath; + cdev->private->flags.doverify = 0; + verify_start(cdev); } /* - * Got interrupt for Unset Path Group ID. + * Process disband SET PGID request result. */ -void -ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void disband_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - int ret; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_dev_id *id = &cdev->private->dev_id; + + if (rc) + goto out; + /* Ensure consistent multipathing state at device and channel. */ + cdev->private->flags.mpath = 0; + if (sch->config.mp) { + sch->config.mp = 0; + rc = cio_commit_config(sch); + } +out: + CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno, + rc); + ccw_device_disband_done(cdev, rc); +} - irb = (struct irb *) __LC_IRB; +/** + * ccw_device_disband_start - disband pathgroup + * @cdev: ccw device + * + * Execute a SET PGID channel program on @cdev to disband a previously + * established pathgroup. When finished, call ccw_device_disband_done with + * a return code specifying the result. + */ +void ccw_device_disband_start(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + u8 fn; + + CIO_TRACE_EVENT(4, "disb"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Request setup. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = sch->schib.pmcw.pam & sch->opm; + req->callback = disband_callback; + fn = SPID_FUNC_DISBAND; + if (cdev->private->flags.mpath) + fn |= SPID_FUNC_MULTI_PATH; + spid_build_cp(cdev, fn); + ccw_request_start(cdev); +} - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) - __ccw_device_disband_start(cdev); - return; - } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - ret = __ccw_device_check_pgid(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - switch (ret) { - /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ - case 0: /* disband successful. */ - ccw_device_disband_done(cdev, ret); - break; - case -EOPNOTSUPP: - /* - * One of those strange devices which claim to be able - * to do multipathing but not for Unset Path Group ID. - */ - cdev->private->flags.pgid_single = 1; - /* fall through. */ - case -EAGAIN: /* Try again. */ - __ccw_device_disband_start(cdev); - break; - case -ETIME: /* Set path group id stopped by timeout. */ - ccw_device_disband_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_disband_start(cdev); - break; - } +static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2) +{ + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + + cp[0].cmd_code = CCW_CMD_STLCK; + cp[0].cda = (u32) (addr_t) buf1; + cp[0].count = 32; + cp[0].flags = CCW_FLAG_CC; + cp[1].cmd_code = CCW_CMD_RELEASE; + cp[1].cda = (u32) (addr_t) buf2; + cp[1].count = 32; + cp[1].flags = 0; + req->cp = cp; } -void -ccw_device_disband_start(struct ccw_device *cdev) +static void stlck_callback(struct ccw_device *cdev, void *data, int rc) { - /* After 60s disbanding is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); + ccw_device_stlck_done(cdev, data, rc); +} - cdev->private->flags.pgid_single = 0; - cdev->private->iretry = 5; - cdev->private->imask = 0x80; - __ccw_device_disband_start(cdev); +/** + * ccw_device_stlck_start - perform unconditional release + * @cdev: ccw device + * @data: data pointer to be passed to ccw_device_stlck_done + * @buf1: data pointer used in channel program + * @buf2: data pointer used in channel program + * + * Execute a channel program on @cdev to release an existing PGID reservation. + * When finished, call ccw_device_stlck_done with a return code specifying the + * result. + */ +void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1, + void *buf2) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + CIO_TRACE_EVENT(4, "stlck"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Request setup. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = sch->schib.pmcw.pam & sch->opm; + req->data = data; + req->callback = stlck_callback; + stlck_build_cp(cdev, buf1, buf2); + ccw_request_start(cdev); } + diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index 5814dbee2410..66d8066ef22a 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -336,9 +336,6 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) sense_ccw->count = SENSE_MAX_COUNT; sense_ccw->flags = CCW_FLAG_SLI; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - rc = cio_start(sch, sense_ccw, 0xff); if (rc == -ENODEV || rc == -EACCES) dev_fsm_event(cdev, DEV_EVENT_VERIFY); diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 0b8f381bd20e..d72ae4c93af9 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -1,7 +1,10 @@ #ifndef S390_IO_SCH_H #define S390_IO_SCH_H +#include <linux/types.h> #include <asm/schid.h> +#include <asm/ccwdev.h> +#include "css.h" /* * command-mode operation request block @@ -68,6 +71,52 @@ struct io_subchannel_private { #define MAX_CIWS 8 /* + * Possible status values for a CCW request's I/O. + */ +enum io_status { + IO_DONE, + IO_RUNNING, + IO_STATUS_ERROR, + IO_PATH_ERROR, + IO_REJECTED, + IO_KILLED +}; + +/** + * ccw_request - Internal CCW request. + * @cp: channel program to start + * @timeout: maximum allowable time in jiffies between start I/O and interrupt + * @maxretries: number of retries per I/O operation and path + * @lpm: mask of paths to use + * @check: optional callback that determines if results are final + * @filter: optional callback to adjust request status based on IRB data + * @callback: final callback + * @data: user-defined pointer passed to all callbacks + * @mask: current path mask + * @retries: current number of retries + * @drc: delayed return code + * @cancel: non-zero if request was cancelled + * @done: non-zero if request was finished + */ +struct ccw_request { + struct ccw1 *cp; + unsigned long timeout; + u16 maxretries; + u8 lpm; + int (*check)(struct ccw_device *, void *); + enum io_status (*filter)(struct ccw_device *, void *, struct irb *, + enum io_status); + void (*callback)(struct ccw_device *, void *, int); + void *data; + /* These fields are used internally. */ + u16 mask; + u16 retries; + int drc; + int cancel:1; + int done:1; +} __attribute__((packed)); + +/* * sense-id response buffer layout */ struct senseid { @@ -82,32 +131,43 @@ struct senseid { struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */ } __attribute__ ((packed, aligned(4))); +enum cdev_todo { + CDEV_TODO_NOTHING, + CDEV_TODO_ENABLE_CMF, + CDEV_TODO_REBIND, + CDEV_TODO_REGISTER, + CDEV_TODO_UNREG, + CDEV_TODO_UNREG_EVAL, +}; + struct ccw_device_private { struct ccw_device *cdev; struct subchannel *sch; int state; /* device state */ atomic_t onoff; - unsigned long registered; struct ccw_dev_id dev_id; /* device id */ struct subchannel_id schid; /* subchannel number */ - u8 imask; /* lpm mask for SNID/SID/SPGID */ - int iretry; /* retry counter SNID/SID/SPGID */ + struct ccw_request req; /* internal I/O request */ + int iretry; + u8 pgid_valid_mask; /* mask of valid PGIDs */ struct { unsigned int fast:1; /* post with "channel end" */ unsigned int repall:1; /* report every interrupt status */ unsigned int pgroup:1; /* do path grouping */ unsigned int force:1; /* allow forced online */ + unsigned int mpath:1; /* do multipathing */ } __attribute__ ((packed)) options; struct { - unsigned int pgid_single:1; /* use single path for Set PGID */ unsigned int esid:1; /* Ext. SenseID supported by HW */ unsigned int dosense:1; /* delayed SENSE required */ unsigned int doverify:1; /* delayed path verification */ unsigned int donotify:1; /* call notify function */ unsigned int recog_done:1; /* dev. recog. complete */ unsigned int fake_irb:1; /* deliver faked irb */ - unsigned int intretry:1; /* retry internal operation */ unsigned int resuming:1; /* recognition while resume */ + unsigned int pgroup:1; /* pathgroup is set up */ + unsigned int mpath:1; /* multipathing is set up */ + unsigned int initialized:1; /* set if initial reference held */ } __attribute__((packed)) flags; unsigned long intparm; /* user interruption parameter */ struct qdio_irq *qdio_data; @@ -115,7 +175,8 @@ struct ccw_device_private { struct senseid senseid; /* SenseID info */ struct pgid pgid[8]; /* path group IDs per chpid*/ struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */ - struct work_struct kick_work; + struct work_struct todo_work; + enum cdev_todo todo; wait_queue_head_t wait_q; struct timer_list timer; void *cmb; /* measurement information */ diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 1294876bf7b4..20836eff88c5 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -102,6 +102,7 @@ static atomic_t ap_poll_requests = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); static struct task_struct *ap_poll_kthread = NULL; static DEFINE_MUTEX(ap_poll_thread_mutex); +static DEFINE_SPINLOCK(ap_poll_timer_lock); static void *ap_interrupt_indicator; static struct hrtimer ap_poll_timer; /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. @@ -282,6 +283,7 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind) * @psmid: The program supplied message identifier * @msg: The message text * @length: The message length + * @special: Special Bit * * Returns AP queue status structure. * Condition code 1 on NQAP can't happen because the L bit is 1. @@ -289,7 +291,8 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind) * because a segment boundary was reached. The NQAP is repeated. */ static inline struct ap_queue_status -__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) +__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length, + unsigned int special) { typedef struct { char _[length]; } msgblock; register unsigned long reg0 asm ("0") = qid | 0x40000000UL; @@ -299,6 +302,9 @@ __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32); register unsigned long reg5 asm ("5") = (unsigned int) psmid; + if (special == 1) + reg0 |= 0x400000UL; + asm volatile ( "0: .long 0xb2ad0042\n" /* DQAP */ " brc 2,0b" @@ -312,13 +318,15 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) { struct ap_queue_status status; - status = __ap_send(qid, psmid, msg, length); + status = __ap_send(qid, psmid, msg, length, 0); switch (status.response_code) { case AP_RESPONSE_NORMAL: return 0; case AP_RESPONSE_Q_FULL: case AP_RESPONSE_RESET_IN_PROGRESS: return -EBUSY; + case AP_RESPONSE_REQ_FAC_NOT_INST: + return -EINVAL; default: /* Device is gone. */ return -ENODEV; } @@ -1008,7 +1016,7 @@ static int ap_probe_device_type(struct ap_device *ap_dev) } status = __ap_send(ap_dev->qid, 0x0102030405060708ULL, - msg, sizeof(msg)); + msg, sizeof(msg), 0); if (status.response_code != AP_RESPONSE_NORMAL) { rc = -ENODEV; goto out_free; @@ -1163,16 +1171,19 @@ ap_config_timeout(unsigned long ptr) static inline void ap_schedule_poll_timer(void) { ktime_t hr_time; + + spin_lock_bh(&ap_poll_timer_lock); if (ap_using_interrupts() || ap_suspend_flag) - return; + goto out; if (hrtimer_is_queued(&ap_poll_timer)) - return; + goto out; if (ktime_to_ns(hrtimer_expires_remaining(&ap_poll_timer)) <= 0) { hr_time = ktime_set(0, poll_timeout); hrtimer_forward_now(&ap_poll_timer, hr_time); hrtimer_restart(&ap_poll_timer); } - return; +out: + spin_unlock_bh(&ap_poll_timer_lock); } /** @@ -1243,7 +1254,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) /* Start the next request on the queue. */ ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list); status = __ap_send(ap_dev->qid, ap_msg->psmid, - ap_msg->message, ap_msg->length); + ap_msg->message, ap_msg->length, ap_msg->special); switch (status.response_code) { case AP_RESPONSE_NORMAL: atomic_inc(&ap_poll_requests); @@ -1261,6 +1272,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) *flags |= 2; break; case AP_RESPONSE_MESSAGE_TOO_BIG: + case AP_RESPONSE_REQ_FAC_NOT_INST: return -EINVAL; default: return -ENODEV; @@ -1302,7 +1314,8 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms if (list_empty(&ap_dev->requestq) && ap_dev->queue_count < ap_dev->queue_depth) { status = __ap_send(ap_dev->qid, ap_msg->psmid, - ap_msg->message, ap_msg->length); + ap_msg->message, ap_msg->length, + ap_msg->special); switch (status.response_code) { case AP_RESPONSE_NORMAL: list_add_tail(&ap_msg->list, &ap_dev->pendingq); @@ -1317,6 +1330,7 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms ap_dev->requestq_count++; ap_dev->total_request_count++; return -EBUSY; + case AP_RESPONSE_REQ_FAC_NOT_INST: case AP_RESPONSE_MESSAGE_TOO_BIG: ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL)); return -EINVAL; @@ -1658,6 +1672,7 @@ int __init ap_module_init(void) */ if (MACHINE_IS_VM) poll_timeout = 1500000; + spin_lock_init(&ap_poll_timer_lock); hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ap_poll_timer.function = ap_poll_timeout; diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index a35362241805..4785d07cd447 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -87,6 +87,7 @@ struct ap_queue_status { #define AP_RESPONSE_INDEX_TOO_BIG 0x11 #define AP_RESPONSE_NO_FIRST_PART 0x13 #define AP_RESPONSE_MESSAGE_TOO_BIG 0x15 +#define AP_RESPONSE_REQ_FAC_NOT_INST 0x16 /* * Known device types @@ -96,8 +97,8 @@ struct ap_queue_status { #define AP_DEVICE_TYPE_PCIXCC 5 #define AP_DEVICE_TYPE_CEX2A 6 #define AP_DEVICE_TYPE_CEX2C 7 -#define AP_DEVICE_TYPE_CEX2A2 8 -#define AP_DEVICE_TYPE_CEX2C2 9 +#define AP_DEVICE_TYPE_CEX3A 8 +#define AP_DEVICE_TYPE_CEX3C 9 /* * AP reset flag states @@ -161,12 +162,25 @@ struct ap_message { size_t length; /* Message length. */ void *private; /* ap driver private pointer. */ + unsigned int special:1; /* Used for special commands. */ }; #define AP_DEVICE(dt) \ .dev_type=(dt), \ .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE, +/** + * ap_init_message() - Initialize ap_message. + * Initialize a message before using. Otherwise this might result in + * unexpected behaviour. + */ +static inline void ap_init_message(struct ap_message *ap_msg) +{ + ap_msg->psmid = 0; + ap_msg->length = 0; + ap_msg->special = 0; +} + /* * Note: don't use ap_send/ap_recv after using ap_queue_message * for the first time. Otherwise the ap message queue will get diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 65b6a96afe6b..0d4d18bdd45c 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -299,9 +299,7 @@ static ssize_t zcrypt_write(struct file *filp, const char __user *buf, */ static int zcrypt_open(struct inode *inode, struct file *filp) { - lock_kernel(); atomic_inc(&zcrypt_open_count); - unlock_kernel(); return 0; } @@ -1009,6 +1007,10 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset, zcrypt_count_type(ZCRYPT_CEX2C)); len += sprintf(resp_buff + len, "CEX2A count: %d\n", zcrypt_count_type(ZCRYPT_CEX2A)); + len += sprintf(resp_buff + len, "CEX3C count: %d\n", + zcrypt_count_type(ZCRYPT_CEX3C)); + len += sprintf(resp_buff + len, "CEX3A count: %d\n", + zcrypt_count_type(ZCRYPT_CEX3A)); len += sprintf(resp_buff + len, "requestq count: %d\n", zcrypt_requestq_count()); len += sprintf(resp_buff + len, "pendingq count: %d\n", @@ -1017,7 +1019,7 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset, atomic_read(&zcrypt_open_count)); zcrypt_status_mask(workarea); len += sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) " - "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A", + "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A 7=CEX3C 8=CEX3A", resp_buff+len, workarea, AP_DEVICES); zcrypt_qdepth_mask(workarea); len += sprinthx("Waiting work element counts", @@ -1095,8 +1097,9 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer, * '0' for no device, '1' for PCICA, '2' for PCICC, * '3' for PCIXCC_MCL2, '4' for PCIXCC_MCL3, * '5' for CEX2C and '6' for CEX2A' + * '7' for CEX3C and '8' for CEX3A */ - if (*ptr >= '0' && *ptr <= '6') + if (*ptr >= '0' && *ptr <= '8') j++; else if (*ptr == 'd' || *ptr == 'D') zcrypt_disable_card(j++); diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index 1d1ec74dadb2..8e7ffbf2466c 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -71,6 +71,8 @@ struct ica_z90_status { #define ZCRYPT_PCIXCC_MCL3 4 #define ZCRYPT_CEX2C 5 #define ZCRYPT_CEX2A 6 +#define ZCRYPT_CEX3C 7 +#define ZCRYPT_CEX3A 8 /** * Large random numbers are pulled in 4096 byte chunks from the crypto cards diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 326ea08f67c9..c6fb0aa89507 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -39,17 +39,24 @@ #define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */ #define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */ +#define CEX3A_MIN_MOD_SIZE CEX2A_MIN_MOD_SIZE +#define CEX3A_MAX_MOD_SIZE CEX2A_MAX_MOD_SIZE #define CEX2A_SPEED_RATING 970 +#define CEX3A_SPEED_RATING 900 /* Fixme: Needs finetuning */ #define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */ #define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */ +#define CEX3A_MAX_MESSAGE_SIZE CEX2A_MAX_MESSAGE_SIZE +#define CEX3A_MAX_RESPONSE_SIZE CEX2A_MAX_RESPONSE_SIZE + #define CEX2A_CLEANUP_TIME (15*HZ) +#define CEX3A_CLEANUP_TIME CEX2A_CLEANUP_TIME static struct ap_device_id zcrypt_cex2a_ids[] = { { AP_DEVICE(AP_DEVICE_TYPE_CEX2A) }, - { AP_DEVICE(AP_DEVICE_TYPE_CEX2A2) }, + { AP_DEVICE(AP_DEVICE_TYPE_CEX3A) }, { /* end of list */ }, }; @@ -298,6 +305,7 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -335,6 +343,7 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -373,31 +382,45 @@ static struct zcrypt_ops zcrypt_cex2a_ops = { */ static int zcrypt_cex2a_probe(struct ap_device *ap_dev) { - struct zcrypt_device *zdev; - int rc; - - zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE); - if (!zdev) - return -ENOMEM; - zdev->ap_dev = ap_dev; - zdev->ops = &zcrypt_cex2a_ops; - zdev->online = 1; - zdev->user_space_type = ZCRYPT_CEX2A; - zdev->type_string = "CEX2A"; - zdev->min_mod_size = CEX2A_MIN_MOD_SIZE; - zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; - zdev->short_crt = 1; - zdev->speed_rating = CEX2A_SPEED_RATING; - ap_dev->reply = &zdev->reply; - ap_dev->private = zdev; - rc = zcrypt_device_register(zdev); - if (rc) - goto out_free; - return 0; - -out_free: - ap_dev->private = NULL; - zcrypt_device_free(zdev); + struct zcrypt_device *zdev = NULL; + int rc = 0; + + switch (ap_dev->device_type) { + case AP_DEVICE_TYPE_CEX2A: + zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE); + if (!zdev) + return -ENOMEM; + zdev->user_space_type = ZCRYPT_CEX2A; + zdev->type_string = "CEX2A"; + zdev->min_mod_size = CEX2A_MIN_MOD_SIZE; + zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; + zdev->short_crt = 1; + zdev->speed_rating = CEX2A_SPEED_RATING; + break; + case AP_DEVICE_TYPE_CEX3A: + zdev = zcrypt_device_alloc(CEX3A_MAX_RESPONSE_SIZE); + if (!zdev) + return -ENOMEM; + zdev->user_space_type = ZCRYPT_CEX3A; + zdev->type_string = "CEX3A"; + zdev->min_mod_size = CEX3A_MIN_MOD_SIZE; + zdev->max_mod_size = CEX3A_MAX_MOD_SIZE; + zdev->short_crt = 1; + zdev->speed_rating = CEX3A_SPEED_RATING; + break; + } + if (zdev != NULL) { + zdev->ap_dev = ap_dev; + zdev->ops = &zcrypt_cex2a_ops; + zdev->online = 1; + ap_dev->reply = &zdev->reply; + ap_dev->private = zdev; + rc = zcrypt_device_register(zdev); + } + if (rc) { + ap_dev->private = NULL; + zcrypt_device_free(zdev); + } return rc; } diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 17ba81b58c78..e78df3671caf 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -281,6 +281,7 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -318,6 +319,7 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index f4b0c4795434..a23726a0735c 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -483,6 +483,7 @@ static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -521,6 +522,7 @@ static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 5677b40e4ac0..79c120578e61 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -43,10 +43,13 @@ #define PCIXCC_MIN_MOD_SIZE 16 /* 128 bits */ #define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */ #define PCIXCC_MAX_MOD_SIZE 256 /* 2048 bits */ +#define CEX3C_MIN_MOD_SIZE PCIXCC_MIN_MOD_SIZE +#define CEX3C_MAX_MOD_SIZE PCIXCC_MAX_MOD_SIZE -#define PCIXCC_MCL2_SPEED_RATING 7870 /* FIXME: needs finetuning */ +#define PCIXCC_MCL2_SPEED_RATING 7870 #define PCIXCC_MCL3_SPEED_RATING 7870 -#define CEX2C_SPEED_RATING 8540 +#define CEX2C_SPEED_RATING 7000 +#define CEX3C_SPEED_RATING 6500 /* FIXME: needs finetuning */ #define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */ #define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */ @@ -72,7 +75,7 @@ struct response_type { static struct ap_device_id zcrypt_pcixcc_ids[] = { { AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) }, { AP_DEVICE(AP_DEVICE_TYPE_CEX2C) }, - { AP_DEVICE(AP_DEVICE_TYPE_CEX2C2) }, + { AP_DEVICE(AP_DEVICE_TYPE_CEX3C) }, { /* end of list */ }, }; @@ -326,6 +329,11 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len; memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code)); + if (memcmp(function_code, "US", 2) == 0) + ap_msg->special = 1; + else + ap_msg->special = 0; + /* copy data block */ if (xcRB->request_data_length && copy_from_user(req_data, xcRB->request_data_address, @@ -688,6 +696,7 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev, }; int rc; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -727,6 +736,7 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev, }; int rc; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -766,6 +776,7 @@ static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev, }; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -805,6 +816,7 @@ static long zcrypt_pcixcc_rng(struct zcrypt_device *zdev, }; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -972,6 +984,7 @@ static int zcrypt_pcixcc_rng_supported(struct ap_device *ap_dev) } __attribute__((packed)) *reply; int rc, i; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -1016,14 +1029,15 @@ out_free: static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) { struct zcrypt_device *zdev; - int rc; + int rc = 0; zdev = zcrypt_device_alloc(PCIXCC_MAX_RESPONSE_SIZE); if (!zdev) return -ENOMEM; zdev->ap_dev = ap_dev; zdev->online = 1; - if (ap_dev->device_type == AP_DEVICE_TYPE_PCIXCC) { + switch (ap_dev->device_type) { + case AP_DEVICE_TYPE_PCIXCC: rc = zcrypt_pcixcc_mcl(ap_dev); if (rc < 0) { zcrypt_device_free(zdev); @@ -1041,13 +1055,25 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; } - } else { + break; + case AP_DEVICE_TYPE_CEX2C: zdev->user_space_type = ZCRYPT_CEX2C; zdev->type_string = "CEX2C"; zdev->speed_rating = CEX2C_SPEED_RATING; zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + break; + case AP_DEVICE_TYPE_CEX3C: + zdev->user_space_type = ZCRYPT_CEX3C; + zdev->type_string = "CEX3C"; + zdev->speed_rating = CEX3C_SPEED_RATING; + zdev->min_mod_size = CEX3C_MIN_MOD_SIZE; + zdev->max_mod_size = CEX3C_MAX_MOD_SIZE; + break; + default: + goto out_free; } + rc = zcrypt_pcixcc_rng_supported(ap_dev); if (rc < 0) { zcrypt_device_free(zdev); diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 96eddb3b1d08..6cab5a62f99e 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile @@ -3,11 +3,11 @@ # ctcm-y += ctcm_main.o ctcm_fsms.o ctcm_mpc.o ctcm_sysfs.o ctcm_dbug.o -obj-$(CONFIG_CTCM) += ctcm.o fsm.o cu3088.o +obj-$(CONFIG_CTCM) += ctcm.o fsm.o obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o obj-$(CONFIG_SMSGIUCV) += smsgiucv.o -obj-$(CONFIG_LCS) += lcs.o cu3088.o -obj-$(CONFIG_CLAW) += claw.o cu3088.o +obj-$(CONFIG_LCS) += lcs.o +obj-$(CONFIG_CLAW) += claw.o qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o obj-$(CONFIG_QETH) += qeth.o qeth_l2-y += qeth_l2_main.o diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index c63babefb698..3c77bfe0764c 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -90,7 +90,6 @@ #include <linux/timer.h> #include <linux/types.h> -#include "cu3088.h" #include "claw.h" /* @@ -258,6 +257,9 @@ static int claw_pm_prepare(struct ccwgroup_device *gdev) return -EPERM; } +/* the root device for claw group devices */ +static struct device *claw_root_dev; + /* ccwgroup table */ static struct ccwgroup_driver claw_group_driver = { @@ -272,6 +274,47 @@ static struct ccwgroup_driver claw_group_driver = { .prepare = claw_pm_prepare, }; +static struct ccw_device_id claw_ids[] = { + {CCW_DEVICE(0x3088, 0x61), .driver_info = claw_channel_type_claw}, + {}, +}; +MODULE_DEVICE_TABLE(ccw, claw_ids); + +static struct ccw_driver claw_ccw_driver = { + .owner = THIS_MODULE, + .name = "claw", + .ids = claw_ids, + .probe = ccwgroup_probe_ccwdev, + .remove = ccwgroup_remove_ccwdev, +}; + +static ssize_t +claw_driver_group_store(struct device_driver *ddrv, const char *buf, + size_t count) +{ + int err; + err = ccwgroup_create_from_string(claw_root_dev, + claw_group_driver.driver_id, + &claw_ccw_driver, 3, buf); + return err ? err : count; +} + +static DRIVER_ATTR(group, 0200, NULL, claw_driver_group_store); + +static struct attribute *claw_group_attrs[] = { + &driver_attr_group.attr, + NULL, +}; + +static struct attribute_group claw_group_attr_group = { + .attrs = claw_group_attrs, +}; + +static const struct attribute_group *claw_group_attr_groups[] = { + &claw_group_attr_group, + NULL, +}; + /* * Key functions */ @@ -3326,7 +3369,11 @@ claw_remove_files(struct device *dev) static void __exit claw_cleanup(void) { - unregister_cu3088_discipline(&claw_group_driver); + driver_remove_file(&claw_group_driver.driver, + &driver_attr_group); + ccwgroup_driver_unregister(&claw_group_driver); + ccw_driver_unregister(&claw_ccw_driver); + root_device_unregister(claw_root_dev); claw_unregister_debug_facility(); pr_info("Driver unloaded\n"); @@ -3348,16 +3395,31 @@ claw_init(void) if (ret) { pr_err("Registering with the S/390 debug feature" " failed with error code %d\n", ret); - return ret; + goto out_err; } CLAW_DBF_TEXT(2, setup, "init_mod"); - ret = register_cu3088_discipline(&claw_group_driver); - if (ret) { - CLAW_DBF_TEXT(2, setup, "init_bad"); - claw_unregister_debug_facility(); - pr_err("Registering with the cu3088 device driver failed " - "with error code %d\n", ret); - } + claw_root_dev = root_device_register("qeth"); + ret = IS_ERR(claw_root_dev) ? PTR_ERR(claw_root_dev) : 0; + if (ret) + goto register_err; + ret = ccw_driver_register(&claw_ccw_driver); + if (ret) + goto ccw_err; + claw_group_driver.driver.groups = claw_group_attr_groups; + ret = ccwgroup_driver_register(&claw_group_driver); + if (ret) + goto ccwgroup_err; + return 0; + +ccwgroup_err: + ccw_driver_unregister(&claw_ccw_driver); +ccw_err: + root_device_unregister(claw_root_dev); +register_err: + CLAW_DBF_TEXT(2, setup, "init_bad"); + claw_unregister_debug_facility(); +out_err: + pr_err("Initializing the claw device driver failed\n"); return ret; } diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h index 005072c420d3..46d59a13db12 100644 --- a/drivers/s390/net/claw.h +++ b/drivers/s390/net/claw.h @@ -129,6 +129,18 @@ static inline int claw_dbf_passes(debug_info_t *dbf_grp, int level) } \ } while (0) +/** + * Enum for classifying detected devices. + */ +enum claw_channel_types { + /* Device is not a channel */ + claw_channel_type_none, + + /* Device is a CLAW channel device */ + claw_channel_type_claw +}; + + /******************************************************* * Define Control Blocks * * * diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 4ded9ac2c5ef..70eb7f138414 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -44,7 +44,6 @@ #include <asm/idals.h> #include "fsm.h" -#include "cu3088.h" #include "ctcm_dbug.h" #include "ctcm_main.h" diff --git a/drivers/s390/net/ctcm_fsms.h b/drivers/s390/net/ctcm_fsms.h index 2326aba9807a..046d077fabbb 100644 --- a/drivers/s390/net/ctcm_fsms.h +++ b/drivers/s390/net/ctcm_fsms.h @@ -39,7 +39,6 @@ #include <asm/idals.h> #include "fsm.h" -#include "cu3088.h" #include "ctcm_main.h" /* diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index c5b83874500c..e35713dd0504 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -51,12 +51,16 @@ #include <asm/idals.h> -#include "cu3088.h" #include "ctcm_fsms.h" #include "ctcm_main.h" /* Some common global variables */ +/** + * The root device for ctcm group devices + */ +static struct device *ctcm_root_dev; + /* * Linked list of all detected channels. */ @@ -246,7 +250,7 @@ static void channel_remove(struct channel *ch) * * returns Pointer to a channel or NULL if no matching channel available. */ -static struct channel *channel_get(enum channel_types type, +static struct channel *channel_get(enum ctcm_channel_types type, char *id, int direction) { struct channel *ch = channels; @@ -1342,7 +1346,7 @@ static int ctcm_probe_device(struct ccwgroup_device *cgdev) * * returns 0 on success, !0 on error. */ -static int add_channel(struct ccw_device *cdev, enum channel_types type, +static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type, struct ctcm_priv *priv) { struct channel **c = &channels; @@ -1501,13 +1505,13 @@ free_return: /* note that all channel pointers are 0 or valid */ /* * Return type of a detected device. */ -static enum channel_types get_channel_type(struct ccw_device_id *id) +static enum ctcm_channel_types get_channel_type(struct ccw_device_id *id) { - enum channel_types type; - type = (enum channel_types)id->driver_info; + enum ctcm_channel_types type; + type = (enum ctcm_channel_types)id->driver_info; - if (type == channel_type_ficon) - type = channel_type_escon; + if (type == ctcm_channel_type_ficon) + type = ctcm_channel_type_escon; return type; } @@ -1525,16 +1529,21 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) char read_id[CTCM_ID_SIZE]; char write_id[CTCM_ID_SIZE]; int direction; - enum channel_types type; + enum ctcm_channel_types type; struct ctcm_priv *priv; struct net_device *dev; struct ccw_device *cdev0; struct ccw_device *cdev1; + struct channel *readc; + struct channel *writec; int ret; + int result; priv = dev_get_drvdata(&cgdev->dev); - if (!priv) - return -ENODEV; + if (!priv) { + result = -ENODEV; + goto out_err_result; + } cdev0 = cgdev->cdev[0]; cdev1 = cgdev->cdev[1]; @@ -1545,31 +1554,40 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) snprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev)); ret = add_channel(cdev0, type, priv); - if (ret) - return ret; + if (ret) { + result = ret; + goto out_err_result; + } ret = add_channel(cdev1, type, priv); - if (ret) - return ret; + if (ret) { + result = ret; + goto out_remove_channel1; + } ret = ccw_device_set_online(cdev0); if (ret != 0) { - /* may be ok to fail now - can be done later */ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, "%s(%s) set_online rc=%d", CTCM_FUNTAIL, read_id, ret); + result = -EIO; + goto out_remove_channel2; } ret = ccw_device_set_online(cdev1); if (ret != 0) { - /* may be ok to fail now - can be done later */ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, "%s(%s) set_online rc=%d", CTCM_FUNTAIL, write_id, ret); + + result = -EIO; + goto out_ccw1; } dev = ctcm_init_netdevice(priv); - if (dev == NULL) - goto out; + if (dev == NULL) { + result = -ENODEV; + goto out_ccw2; + } for (direction = READ; direction <= WRITE; direction++) { priv->channel[direction] = @@ -1587,12 +1605,14 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) /* sysfs magic */ SET_NETDEV_DEV(dev, &cgdev->dev); - if (register_netdev(dev)) - goto out_dev; + if (register_netdev(dev)) { + result = -ENODEV; + goto out_dev; + } if (ctcm_add_attributes(&cgdev->dev)) { - unregister_netdev(dev); - goto out_dev; + result = -ENODEV; + goto out_unregister; } strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name)); @@ -1608,13 +1628,22 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) priv->channel[WRITE]->id, priv->protocol); return 0; +out_unregister: + unregister_netdev(dev); out_dev: ctcm_free_netdevice(dev); -out: +out_ccw2: ccw_device_set_offline(cgdev->cdev[1]); +out_ccw1: ccw_device_set_offline(cgdev->cdev[0]); - - return -ENODEV; +out_remove_channel2: + readc = channel_get(type, read_id, READ); + channel_remove(readc); +out_remove_channel1: + writec = channel_get(type, write_id, WRITE); + channel_remove(writec); +out_err_result: + return result; } /** @@ -1695,6 +1724,11 @@ static int ctcm_pm_suspend(struct ccwgroup_device *gdev) return 0; netif_device_detach(priv->channel[READ]->netdev); ctcm_close(priv->channel[READ]->netdev); + if (!wait_event_timeout(priv->fsm->wait_q, + fsm_getstate(priv->fsm) == DEV_STATE_STOPPED, CTCM_TIME_5_SEC)) { + netif_device_attach(priv->channel[READ]->netdev); + return -EBUSY; + } ccw_device_set_offline(gdev->cdev[1]); ccw_device_set_offline(gdev->cdev[0]); return 0; @@ -1719,6 +1753,22 @@ err_out: return rc; } +static struct ccw_device_id ctcm_ids[] = { + {CCW_DEVICE(0x3088, 0x08), .driver_info = ctcm_channel_type_parallel}, + {CCW_DEVICE(0x3088, 0x1e), .driver_info = ctcm_channel_type_ficon}, + {CCW_DEVICE(0x3088, 0x1f), .driver_info = ctcm_channel_type_escon}, + {}, +}; +MODULE_DEVICE_TABLE(ccw, ctcm_ids); + +static struct ccw_driver ctcm_ccw_driver = { + .owner = THIS_MODULE, + .name = "ctcm", + .ids = ctcm_ids, + .probe = ccwgroup_probe_ccwdev, + .remove = ccwgroup_remove_ccwdev, +}; + static struct ccwgroup_driver ctcm_group_driver = { .owner = THIS_MODULE, .name = CTC_DRIVER_NAME, @@ -1733,6 +1783,33 @@ static struct ccwgroup_driver ctcm_group_driver = { .restore = ctcm_pm_resume, }; +static ssize_t +ctcm_driver_group_store(struct device_driver *ddrv, const char *buf, + size_t count) +{ + int err; + + err = ccwgroup_create_from_string(ctcm_root_dev, + ctcm_group_driver.driver_id, + &ctcm_ccw_driver, 2, buf); + return err ? err : count; +} + +static DRIVER_ATTR(group, 0200, NULL, ctcm_driver_group_store); + +static struct attribute *ctcm_group_attrs[] = { + &driver_attr_group.attr, + NULL, +}; + +static struct attribute_group ctcm_group_attr_group = { + .attrs = ctcm_group_attrs, +}; + +static const struct attribute_group *ctcm_group_attr_groups[] = { + &ctcm_group_attr_group, + NULL, +}; /* * Module related routines @@ -1746,7 +1823,10 @@ static struct ccwgroup_driver ctcm_group_driver = { */ static void __exit ctcm_exit(void) { - unregister_cu3088_discipline(&ctcm_group_driver); + driver_remove_file(&ctcm_group_driver.driver, &driver_attr_group); + ccwgroup_driver_unregister(&ctcm_group_driver); + ccw_driver_unregister(&ctcm_ccw_driver); + root_device_unregister(ctcm_root_dev); ctcm_unregister_dbf_views(); pr_info("CTCM driver unloaded\n"); } @@ -1772,17 +1852,31 @@ static int __init ctcm_init(void) channels = NULL; ret = ctcm_register_dbf_views(); - if (ret) { - return ret; - } - ret = register_cu3088_discipline(&ctcm_group_driver); - if (ret) { - ctcm_unregister_dbf_views(); - pr_err("%s / register_cu3088_discipline failed, ret = %d\n", - __func__, ret); - return ret; - } + if (ret) + goto out_err; + ctcm_root_dev = root_device_register("ctcm"); + ret = IS_ERR(ctcm_root_dev) ? PTR_ERR(ctcm_root_dev) : 0; + if (ret) + goto register_err; + ret = ccw_driver_register(&ctcm_ccw_driver); + if (ret) + goto ccw_err; + ctcm_group_driver.driver.groups = ctcm_group_attr_groups; + ret = ccwgroup_driver_register(&ctcm_group_driver); + if (ret) + goto ccwgroup_err; print_banner(); + return 0; + +ccwgroup_err: + ccw_driver_unregister(&ctcm_ccw_driver); +ccw_err: + root_device_unregister(ctcm_root_dev); +register_err: + ctcm_unregister_dbf_views(); +out_err: + pr_err("%s / Initializing the ctcm device driver failed, ret = %d\n", + __func__, ret); return ret; } diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h index d925e732b7d8..d34fa14f44e7 100644 --- a/drivers/s390/net/ctcm_main.h +++ b/drivers/s390/net/ctcm_main.h @@ -16,7 +16,6 @@ #include <linux/netdevice.h> #include "fsm.h" -#include "cu3088.h" #include "ctcm_dbug.h" #include "ctcm_mpc.h" @@ -66,6 +65,23 @@ ctcmpc_dumpit(buf, len); \ } while (0) +/** + * Enum for classifying detected devices + */ +enum ctcm_channel_types { + /* Device is not a channel */ + ctcm_channel_type_none, + + /* Device is a CTC/A */ + ctcm_channel_type_parallel, + + /* Device is a FICON channel */ + ctcm_channel_type_ficon, + + /* Device is a ESCON channel */ + ctcm_channel_type_escon +}; + /* * CCW commands, used in this driver. */ @@ -121,7 +137,7 @@ struct channel { * Type of this channel. * CTC/A or Escon for valid channels. */ - enum channel_types type; + enum ctcm_channel_types type; /* * Misc. flags. See CHANNEL_FLAGS_... below */ diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index 781e18be7e8f..5978b390153f 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -53,7 +53,6 @@ #include <linux/moduleparam.h> #include <asm/idals.h> -#include "cu3088.h" #include "ctcm_mpc.h" #include "ctcm_main.h" #include "ctcm_fsms.h" diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c index 8452bb052d68..738ad26c74a7 100644 --- a/drivers/s390/net/ctcm_sysfs.c +++ b/drivers/s390/net/ctcm_sysfs.c @@ -158,6 +158,15 @@ static ssize_t ctcm_proto_store(struct device *dev, return count; } +const char *ctcm_type[] = { + "not a channel", + "CTC/A", + "FICON channel", + "ESCON channel", + "unknown channel type", + "unsupported channel type", +}; + static ssize_t ctcm_type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -168,7 +177,7 @@ static ssize_t ctcm_type_show(struct device *dev, return -ENODEV; return sprintf(buf, "%s\n", - cu3088_type[cgdev->cdev[0]->id.driver_info]); + ctcm_type[cgdev->cdev[0]->id.driver_info]); } static DEVICE_ATTR(buffer, 0644, ctcm_buffer_show, ctcm_buffer_write); diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c deleted file mode 100644 index 48383459e99b..000000000000 --- a/drivers/s390/net/cu3088.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * CTC / LCS ccw_device driver - * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Arnd Bergmann <arndb@de.ibm.com> - * Cornelia Huck <cornelia.huck@de.ibm.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/err.h> - -#include <asm/ccwdev.h> -#include <asm/ccwgroup.h> - -#include "cu3088.h" - -const char *cu3088_type[] = { - "not a channel", - "CTC/A", - "ESCON channel", - "FICON channel", - "OSA LCS card", - "CLAW channel device", - "unknown channel type", - "unsupported channel type", -}; - -/* static definitions */ - -static struct ccw_device_id cu3088_ids[] = { - { CCW_DEVICE(0x3088, 0x08), .driver_info = channel_type_parallel }, - { CCW_DEVICE(0x3088, 0x1f), .driver_info = channel_type_escon }, - { CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon }, - { CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 }, - { CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw }, - { /* end of list */ } -}; - -static struct ccw_driver cu3088_driver; - -static struct device *cu3088_root_dev; - -static ssize_t -group_write(struct device_driver *drv, const char *buf, size_t count) -{ - int ret; - struct ccwgroup_driver *cdrv; - - cdrv = to_ccwgroupdrv(drv); - if (!cdrv) - return -EINVAL; - ret = ccwgroup_create_from_string(cu3088_root_dev, cdrv->driver_id, - &cu3088_driver, 2, buf); - - return (ret == 0) ? count : ret; -} - -static DRIVER_ATTR(group, 0200, NULL, group_write); - -/* Register-unregister for ctc&lcs */ -int -register_cu3088_discipline(struct ccwgroup_driver *dcp) -{ - int rc; - - if (!dcp) - return -EINVAL; - - /* Register discipline.*/ - rc = ccwgroup_driver_register(dcp); - if (rc) - return rc; - - rc = driver_create_file(&dcp->driver, &driver_attr_group); - if (rc) - ccwgroup_driver_unregister(dcp); - - return rc; - -} - -void -unregister_cu3088_discipline(struct ccwgroup_driver *dcp) -{ - if (!dcp) - return; - - driver_remove_file(&dcp->driver, &driver_attr_group); - ccwgroup_driver_unregister(dcp); -} - -static struct ccw_driver cu3088_driver = { - .owner = THIS_MODULE, - .ids = cu3088_ids, - .name = "cu3088", - .probe = ccwgroup_probe_ccwdev, - .remove = ccwgroup_remove_ccwdev, -}; - -/* module setup */ -static int __init -cu3088_init (void) -{ - int rc; - - cu3088_root_dev = root_device_register("cu3088"); - if (IS_ERR(cu3088_root_dev)) - return PTR_ERR(cu3088_root_dev); - rc = ccw_driver_register(&cu3088_driver); - if (rc) - root_device_unregister(cu3088_root_dev); - - return rc; -} - -static void __exit -cu3088_exit (void) -{ - ccw_driver_unregister(&cu3088_driver); - root_device_unregister(cu3088_root_dev); -} - -MODULE_DEVICE_TABLE(ccw,cu3088_ids); -MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); -MODULE_LICENSE("GPL"); - -module_init(cu3088_init); -module_exit(cu3088_exit); - -EXPORT_SYMBOL_GPL(cu3088_type); -EXPORT_SYMBOL_GPL(register_cu3088_discipline); -EXPORT_SYMBOL_GPL(unregister_cu3088_discipline); diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h deleted file mode 100644 index d8558a7105a5..000000000000 --- a/drivers/s390/net/cu3088.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _CU3088_H -#define _CU3088_H - -/** - * Enum for classifying detected devices. - */ -enum channel_types { - /* Device is not a channel */ - channel_type_none, - - /* Device is a CTC/A */ - channel_type_parallel, - - /* Device is a ESCON channel */ - channel_type_escon, - - /* Device is a FICON channel */ - channel_type_ficon, - - /* Device is a OSA2 card */ - channel_type_osa2, - - /* Device is a CLAW channel device */ - channel_type_claw, - - /* Device is a channel, but we don't know - * anything about it */ - channel_type_unknown, - - /* Device is an unsupported model */ - channel_type_unsupported, - - /* number of type entries */ - num_channel_types -}; - -extern const char *cu3088_type[num_channel_types]; -extern int register_cu3088_discipline(struct ccwgroup_driver *); -extern void unregister_cu3088_discipline(struct ccwgroup_driver *); - -#endif diff --git a/drivers/s390/net/fsm.c b/drivers/s390/net/fsm.c index 2c1db8036b7c..cae48cbc5e96 100644 --- a/drivers/s390/net/fsm.c +++ b/drivers/s390/net/fsm.c @@ -27,6 +27,7 @@ init_fsm(char *name, const char **state_names, const char **event_names, int nr_ return NULL; } strlcpy(this->name, name, sizeof(this->name)); + init_waitqueue_head(&this->wait_q); f = kzalloc(sizeof(fsm), order); if (f == NULL) { diff --git a/drivers/s390/net/fsm.h b/drivers/s390/net/fsm.h index af679c10f1bd..1e8b235d95b5 100644 --- a/drivers/s390/net/fsm.h +++ b/drivers/s390/net/fsm.h @@ -66,6 +66,7 @@ typedef struct fsm_instance_t { char name[16]; void *userdata; int userint; + wait_queue_head_t wait_q; #if FSM_DEBUG_HISTORY int history_index; int history_size; @@ -197,6 +198,7 @@ fsm_newstate(fsm_instance *fi, int newstate) printk(KERN_DEBUG "fsm(%s): New state %s\n", fi->name, fi->f->state_names[newstate]); #endif + wake_up(&fi->wait_q); } /** diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index a70de9b4bf29..f6cc46dc0501 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -47,7 +47,6 @@ #include <asm/ccwgroup.h> #include "lcs.h" -#include "cu3088.h" #if !defined(CONFIG_NET_ETHERNET) && \ @@ -60,7 +59,11 @@ */ static char version[] __initdata = "LCS driver"; -static char debug_buffer[255]; + +/** + * the root device for lcs group devices + */ +static struct device *lcs_root_dev; /** * Some prototypes. @@ -76,6 +79,7 @@ static int lcs_recovery(void *ptr); /** * Debug Facility Stuff */ +static char debug_buffer[255]; static debug_info_t *lcs_dbf_setup; static debug_info_t *lcs_dbf_trace; @@ -889,7 +893,7 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, rc = lcs_ready_buffer(&card->write, buffer); if (rc) return rc; - init_timer(&timer); + init_timer_on_stack(&timer); timer.function = lcs_lancmd_timeout; timer.data = (unsigned long) reply; timer.expires = jiffies + HZ*card->lancmd_timeout; @@ -1968,6 +1972,15 @@ lcs_portno_store (struct device *dev, struct device_attribute *attr, const char static DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store); +const char *lcs_type[] = { + "not a channel", + "2216 parallel", + "2216 channel", + "OSA LCS card", + "unknown channel type", + "unsupported channel type", +}; + static ssize_t lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1977,7 +1990,7 @@ lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf) if (!cgdev) return -ENODEV; - return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]); + return sprintf(buf, "%s\n", lcs_type[cgdev->cdev[0]->id.driver_info]); } static DEVICE_ATTR(type, 0444, lcs_type_show, NULL); @@ -2130,8 +2143,12 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) card->write.ccwdev = ccwgdev->cdev[1]; recover_state = card->state; - ccw_device_set_online(card->read.ccwdev); - ccw_device_set_online(card->write.ccwdev); + rc = ccw_device_set_online(card->read.ccwdev); + if (rc) + goto out_err; + rc = ccw_device_set_online(card->write.ccwdev); + if (rc) + goto out_werr; LCS_DBF_TEXT(3, setup, "lcsnewdv"); @@ -2210,8 +2227,10 @@ netdev_out: return 0; out: - ccw_device_set_offline(card->read.ccwdev); ccw_device_set_offline(card->write.ccwdev); +out_werr: + ccw_device_set_offline(card->read.ccwdev); +out_err: return -ENODEV; } @@ -2364,6 +2383,22 @@ static int lcs_restore(struct ccwgroup_device *gdev) return lcs_pm_resume(card); } +static struct ccw_device_id lcs_ids[] = { + {CCW_DEVICE(0x3088, 0x08), .driver_info = lcs_channel_type_parallel}, + {CCW_DEVICE(0x3088, 0x1f), .driver_info = lcs_channel_type_2216}, + {CCW_DEVICE(0x3088, 0x60), .driver_info = lcs_channel_type_osa2}, + {}, +}; +MODULE_DEVICE_TABLE(ccw, lcs_ids); + +static struct ccw_driver lcs_ccw_driver = { + .owner = THIS_MODULE, + .name = "lcs", + .ids = lcs_ids, + .probe = ccwgroup_probe_ccwdev, + .remove = ccwgroup_remove_ccwdev, +}; + /** * LCS ccwgroup driver registration */ @@ -2383,6 +2418,33 @@ static struct ccwgroup_driver lcs_group_driver = { .restore = lcs_restore, }; +static ssize_t +lcs_driver_group_store(struct device_driver *ddrv, const char *buf, + size_t count) +{ + int err; + err = ccwgroup_create_from_string(lcs_root_dev, + lcs_group_driver.driver_id, + &lcs_ccw_driver, 2, buf); + return err ? err : count; +} + +static DRIVER_ATTR(group, 0200, NULL, lcs_driver_group_store); + +static struct attribute *lcs_group_attrs[] = { + &driver_attr_group.attr, + NULL, +}; + +static struct attribute_group lcs_group_attr_group = { + .attrs = lcs_group_attrs, +}; + +static const struct attribute_group *lcs_group_attr_groups[] = { + &lcs_group_attr_group, + NULL, +}; + /** * LCS Module/Kernel initialization function */ @@ -2394,17 +2456,30 @@ __init lcs_init_module(void) pr_info("Loading %s\n", version); rc = lcs_register_debug_facility(); LCS_DBF_TEXT(0, setup, "lcsinit"); - if (rc) { - pr_err("Initialization failed\n"); - return rc; - } - - rc = register_cu3088_discipline(&lcs_group_driver); - if (rc) { - pr_err("Initialization failed\n"); - return rc; - } + if (rc) + goto out_err; + lcs_root_dev = root_device_register("lcs"); + rc = IS_ERR(lcs_root_dev) ? PTR_ERR(lcs_root_dev) : 0; + if (rc) + goto register_err; + rc = ccw_driver_register(&lcs_ccw_driver); + if (rc) + goto ccw_err; + lcs_group_driver.driver.groups = lcs_group_attr_groups; + rc = ccwgroup_driver_register(&lcs_group_driver); + if (rc) + goto ccwgroup_err; return 0; + +ccwgroup_err: + ccw_driver_unregister(&lcs_ccw_driver); +ccw_err: + root_device_unregister(lcs_root_dev); +register_err: + lcs_unregister_debug_facility(); +out_err: + pr_err("Initializing the lcs device driver failed\n"); + return rc; } @@ -2416,7 +2491,11 @@ __exit lcs_cleanup_module(void) { pr_info("Terminating lcs module.\n"); LCS_DBF_TEXT(0, trace, "cleanup"); - unregister_cu3088_discipline(&lcs_group_driver); + driver_remove_file(&lcs_group_driver.driver, + &driver_attr_group); + ccwgroup_driver_unregister(&lcs_group_driver); + ccw_driver_unregister(&lcs_ccw_driver); + root_device_unregister(lcs_root_dev); lcs_unregister_debug_facility(); } diff --git a/drivers/s390/net/lcs.h b/drivers/s390/net/lcs.h index 6d668642af27..8c03392ac833 100644 --- a/drivers/s390/net/lcs.h +++ b/drivers/s390/net/lcs.h @@ -36,6 +36,24 @@ static inline int lcs_dbf_passes(debug_info_t *dbf_grp, int level) #define CARD_FROM_DEV(cdev) \ (struct lcs_card *) dev_get_drvdata( \ &((struct ccwgroup_device *)dev_get_drvdata(&cdev->dev))->dev); + +/** + * Enum for classifying detected devices. + */ +enum lcs_channel_types { + /* Device is not a channel */ + lcs_channel_type_none, + + /* Device is a 2216 channel */ + lcs_channel_type_parallel, + + /* Device is a 2216 channel */ + lcs_channel_type_2216, + + /* Device is a OSA2 card */ + lcs_channel_type_osa2 +}; + /** * CCW commands used in this driver */ diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index c84eadd3602a..395c04c2b00f 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -741,13 +741,13 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg) if (single_flag) { if ((skb = skb_dequeue(&conn->commit_queue))) { atomic_dec(&skb->users); - dev_kfree_skb_any(skb); if (privptr) { privptr->stats.tx_packets++; privptr->stats.tx_bytes += (skb->len - NETIUCV_HDRLEN - - NETIUCV_HDRLEN); + - NETIUCV_HDRLEN); } + dev_kfree_skb_any(skb); } } conn->tx_buff->data = conn->tx_buff->head; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 31a2b4e502ce..b232693378cd 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -122,7 +122,6 @@ struct qeth_perf_stats { __u64 outbound_do_qdio_start_time; unsigned int outbound_do_qdio_cnt; unsigned int outbound_do_qdio_time; - /* eddp data */ unsigned int large_send_bytes; unsigned int large_send_cnt; unsigned int sg_skbs_sent; @@ -135,6 +134,7 @@ struct qeth_perf_stats { unsigned int sg_frags_rx; unsigned int sg_alloc_page_rx; unsigned int tx_csum; + unsigned int tx_lin; }; /* Routing stuff */ @@ -648,6 +648,7 @@ struct qeth_card_options { enum qeth_large_send_types large_send; int performance_stats; int rx_sg_cb; + enum qeth_ipa_isolation_modes isolation; }; /* @@ -776,7 +777,6 @@ static inline void qeth_put_buffer_pool_entry(struct qeth_card *card, list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list); } -struct qeth_eddp_context; extern struct ccwgroup_driver qeth_l2_ccwgroup_driver; extern struct ccwgroup_driver qeth_l3_ccwgroup_driver; const char *qeth_get_cardname_short(struct qeth_card *); @@ -836,7 +836,6 @@ void qeth_prepare_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, char); struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *); int qeth_mdio_read(struct net_device *, int, int); int qeth_snmp_command(struct qeth_card *, char __user *); -int qeth_set_large_send(struct qeth_card *, enum qeth_large_send_types); struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *, __u32, __u32); int qeth_default_setadapterparms_cb(struct qeth_card *, struct qeth_reply *, unsigned long); @@ -849,13 +848,14 @@ int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int, int, int); int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int); -int qeth_core_get_stats_count(struct net_device *); +int qeth_core_get_sset_count(struct net_device *, int); void qeth_core_get_ethtool_stats(struct net_device *, struct ethtool_stats *, u64 *); void qeth_core_get_strings(struct net_device *, u32, u8 *); void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *); void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...); int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *); +int qeth_set_access_ctrl_online(struct qeth_card *card); /* exports for OSN */ int qeth_osn_assist(struct net_device *, void *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index c4a42d970158..d34804d5ece1 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -270,41 +270,6 @@ int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt) return qeth_alloc_buffer_pool(card); } -int qeth_set_large_send(struct qeth_card *card, - enum qeth_large_send_types type) -{ - int rc = 0; - - if (card->dev == NULL) { - card->options.large_send = type; - return 0; - } - if (card->state == CARD_STATE_UP) - netif_tx_disable(card->dev); - card->options.large_send = type; - switch (card->options.large_send) { - case QETH_LARGE_SEND_TSO: - if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { - card->dev->features |= NETIF_F_TSO | NETIF_F_SG | - NETIF_F_HW_CSUM; - } else { - card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | - NETIF_F_HW_CSUM); - card->options.large_send = QETH_LARGE_SEND_NO; - rc = -EOPNOTSUPP; - } - break; - default: /* includes QETH_LARGE_SEND_NO */ - card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | - NETIF_F_HW_CSUM); - break; - } - if (card->state == CARD_STATE_UP) - netif_wake_queue(card->dev); - return rc; -} -EXPORT_SYMBOL_GPL(qeth_set_large_send); - static int qeth_issue_next_read(struct qeth_card *card) { int rc; @@ -1079,6 +1044,7 @@ static void qeth_set_intial_options(struct qeth_card *card) card->options.add_hhlen = DEFAULT_ADD_HHLEN; card->options.performance_stats = 0; card->options.rx_sg_cb = QETH_RX_SG_CB; + card->options.isolation = ISOLATION_MODE_NONE; } static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread) @@ -3389,6 +3355,156 @@ int qeth_setadpparms_change_macaddr(struct qeth_card *card) } EXPORT_SYMBOL_GPL(qeth_setadpparms_change_macaddr); +static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card, + struct qeth_reply *reply, unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + struct qeth_set_access_ctrl *access_ctrl_req; + int rc; + + QETH_DBF_TEXT(TRACE, 4, "setaccb"); + + cmd = (struct qeth_ipa_cmd *) data; + access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; + QETH_DBF_TEXT_(SETUP, 2, "setaccb"); + QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); + QETH_DBF_TEXT_(SETUP, 2, "rc=%d", + cmd->data.setadapterparms.hdr.return_code); + switch (cmd->data.setadapterparms.hdr.return_code) { + case SET_ACCESS_CTRL_RC_SUCCESS: + case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED: + case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED: + { + card->options.isolation = access_ctrl_req->subcmd_code; + if (card->options.isolation == ISOLATION_MODE_NONE) { + dev_info(&card->gdev->dev, + "QDIO data connection isolation is deactivated\n"); + } else { + dev_info(&card->gdev->dev, + "QDIO data connection isolation is activated\n"); + } + QETH_DBF_MESSAGE(3, "OK:SET_ACCESS_CTRL(%s, %d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + rc = 0; + break; + } + case SET_ACCESS_CTRL_RC_NOT_SUPPORTED: + { + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + dev_err(&card->gdev->dev, "Adapter does not " + "support QDIO data connection isolation\n"); + + /* ensure isolation mode is "none" */ + card->options.isolation = ISOLATION_MODE_NONE; + rc = -EOPNOTSUPP; + break; + } + case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER: + { + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + dev_err(&card->gdev->dev, + "Adapter is dedicated. " + "QDIO data connection isolation not supported\n"); + + /* ensure isolation mode is "none" */ + card->options.isolation = ISOLATION_MODE_NONE; + rc = -EOPNOTSUPP; + break; + } + case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF: + { + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + dev_err(&card->gdev->dev, + "TSO does not permit QDIO data connection isolation\n"); + + /* ensure isolation mode is "none" */ + card->options.isolation = ISOLATION_MODE_NONE; + rc = -EPERM; + break; + } + default: + { + /* this should never happen */ + QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d" + "==UNKNOWN\n", + card->gdev->dev.kobj.name, + access_ctrl_req->subcmd_code, + cmd->data.setadapterparms.hdr.return_code); + + /* ensure isolation mode is "none" */ + card->options.isolation = ISOLATION_MODE_NONE; + rc = 0; + break; + } + } + qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); + return rc; +} + +static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, + enum qeth_ipa_isolation_modes isolation) +{ + int rc; + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + struct qeth_set_access_ctrl *access_ctrl_req; + + QETH_DBF_TEXT(TRACE, 4, "setacctl"); + + QETH_DBF_TEXT_(SETUP, 2, "setacctl"); + QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); + + iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_ACCESS_CONTROL, + sizeof(struct qeth_ipacmd_setadpparms_hdr) + + sizeof(struct qeth_set_access_ctrl)); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; + access_ctrl_req->subcmd_code = isolation; + + rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb, + NULL); + QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc); + return rc; +} + +int qeth_set_access_ctrl_online(struct qeth_card *card) +{ + int rc = 0; + + QETH_DBF_TEXT(TRACE, 4, "setactlo"); + + if (card->info.type == QETH_CARD_TYPE_OSAE && + qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) { + rc = qeth_setadpparms_set_access_ctrl(card, + card->options.isolation); + if (rc) { + QETH_DBF_MESSAGE(3, + "IPA(SET_ACCESS_CTRL,%s,%d) sent failed", + card->gdev->dev.kobj.name, + rc); + } + } else if (card->options.isolation != ISOLATION_MODE_NONE) { + card->options.isolation = ISOLATION_MODE_NONE; + + dev_err(&card->gdev->dev, "Adapter does not " + "support QDIO data connection isolation\n"); + rc = -EOPNOTSUPP; + } + return rc; +} +EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online); + void qeth_tx_timeout(struct net_device *dev) { struct qeth_card *card; @@ -3732,30 +3848,36 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev, int qeth_core_hardsetup_card(struct qeth_card *card) { struct qdio_ssqd_desc *ssqd; - int retries = 3; + int retries = 0; int mpno = 0; int rc; QETH_DBF_TEXT(SETUP, 2, "hrdsetup"); atomic_set(&card->force_alloc_skb, 0); retry: - if (retries < 3) { + if (retries) QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", dev_name(&card->gdev->dev)); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); - ccw_device_set_online(CARD_RDEV(card)); - ccw_device_set_online(CARD_WDEV(card)); - ccw_device_set_online(CARD_DDEV(card)); - } + ccw_device_set_offline(CARD_DDEV(card)); + ccw_device_set_offline(CARD_WDEV(card)); + ccw_device_set_offline(CARD_RDEV(card)); + rc = ccw_device_set_online(CARD_RDEV(card)); + if (rc) + goto retriable; + rc = ccw_device_set_online(CARD_WDEV(card)); + if (rc) + goto retriable; + rc = ccw_device_set_online(CARD_DDEV(card)); + if (rc) + goto retriable; rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); +retriable: if (rc == -ERESTARTSYS) { QETH_DBF_TEXT(SETUP, 2, "break1"); return rc; } else if (rc) { QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - if (--retries < 0) + if (++retries > 3) goto out; else goto retry; @@ -4303,13 +4425,19 @@ static struct { {"tx do_QDIO time"}, {"tx do_QDIO count"}, {"tx csum"}, + {"tx lin"}, }; -int qeth_core_get_stats_count(struct net_device *dev) +int qeth_core_get_sset_count(struct net_device *dev, int stringset) { - return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN); + switch (stringset) { + case ETH_SS_STATS: + return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN); + default: + return -EINVAL; + } } -EXPORT_SYMBOL_GPL(qeth_core_get_stats_count); +EXPORT_SYMBOL_GPL(qeth_core_get_sset_count); void qeth_core_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) @@ -4355,6 +4483,7 @@ void qeth_core_get_ethtool_stats(struct net_device *dev, data[31] = card->perf_stats.outbound_do_qdio_time; data[32] = card->perf_stats.outbound_do_qdio_cnt; data[33] = card->perf_stats.tx_csum; + data[34] = card->perf_stats.tx_lin; } EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats); diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index eecb2ee62e85..1ba51152f667 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -234,18 +234,19 @@ enum qeth_ipa_setdelip_flags { /* SETADAPTER IPA Command: ****************************************************/ enum qeth_ipa_setadp_cmd { - IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x0001, - IPA_SETADP_ALTER_MAC_ADDRESS = 0x0002, - IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x0004, - IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x0008, - IPA_SETADP_SET_ADDRESSING_MODE = 0x0010, - IPA_SETADP_SET_CONFIG_PARMS = 0x0020, - IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x0040, - IPA_SETADP_SET_BROADCAST_MODE = 0x0080, - IPA_SETADP_SEND_OSA_MESSAGE = 0x0100, - IPA_SETADP_SET_SNMP_CONTROL = 0x0200, - IPA_SETADP_QUERY_CARD_INFO = 0x0400, - IPA_SETADP_SET_PROMISC_MODE = 0x0800, + IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x00000001L, + IPA_SETADP_ALTER_MAC_ADDRESS = 0x00000002L, + IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x00000004L, + IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x00000008L, + IPA_SETADP_SET_ADDRESSING_MODE = 0x00000010L, + IPA_SETADP_SET_CONFIG_PARMS = 0x00000020L, + IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x00000040L, + IPA_SETADP_SET_BROADCAST_MODE = 0x00000080L, + IPA_SETADP_SEND_OSA_MESSAGE = 0x00000100L, + IPA_SETADP_SET_SNMP_CONTROL = 0x00000200L, + IPA_SETADP_QUERY_CARD_INFO = 0x00000400L, + IPA_SETADP_SET_PROMISC_MODE = 0x00000800L, + IPA_SETADP_SET_ACCESS_CONTROL = 0x00010000L, }; enum qeth_ipa_mac_ops { CHANGE_ADDR_READ_MAC = 0, @@ -264,6 +265,20 @@ enum qeth_ipa_promisc_modes { SET_PROMISC_MODE_OFF = 0, SET_PROMISC_MODE_ON = 1, }; +enum qeth_ipa_isolation_modes { + ISOLATION_MODE_NONE = 0x00000000L, + ISOLATION_MODE_FWD = 0x00000001L, + ISOLATION_MODE_DROP = 0x00000002L, +}; +enum qeth_ipa_set_access_mode_rc { + SET_ACCESS_CTRL_RC_SUCCESS = 0x0000, + SET_ACCESS_CTRL_RC_NOT_SUPPORTED = 0x0004, + SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED = 0x0008, + SET_ACCESS_CTRL_RC_ALREADY_ISOLATED = 0x0010, + SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER = 0x0014, + SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF = 0x0018, +}; + /* (SET)DELIP(M) IPA stuff ***************************************************/ struct qeth_ipacmd_setdelip4 { @@ -376,6 +391,11 @@ struct qeth_snmp_ureq { struct qeth_snmp_cmd cmd; } __attribute__((packed)); +/* SET_ACCESS_CONTROL: same format for request and reply */ +struct qeth_set_access_ctrl { + __u32 subcmd_code; +} __attribute__((packed)); + struct qeth_ipacmd_setadpparms_hdr { __u32 supp_hw_cmds; __u32 reserved1; @@ -394,6 +414,7 @@ struct qeth_ipacmd_setadpparms { struct qeth_query_cmds_supp query_cmds_supp; struct qeth_change_addr change_addr; struct qeth_snmp_cmd snmp; + struct qeth_set_access_ctrl set_access_ctrl; __u32 mode; } data; } __attribute__ ((packed)); @@ -507,7 +528,7 @@ extern unsigned char ULP_ENABLE[]; (PDU_ENCAPSULATION(buffer) + 0x17) #define QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer) \ (PDU_ENCAPSULATION(buffer) + 0x2b) -/* Layer 2 defintions */ +/* Layer 2 definitions */ #define QETH_PROT_LAYER2 0x08 #define QETH_PROT_TCPIP 0x03 #define QETH_PROT_OSN2 0x0a diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 33505c2a0e3a..9ff2b36fdc43 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -416,7 +416,11 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, static DEVICE_ATTR(layer2, 0644, qeth_dev_layer2_show, qeth_dev_layer2_store); -static ssize_t qeth_dev_large_send_show(struct device *dev, +#define ATTR_QETH_ISOLATION_NONE ("none") +#define ATTR_QETH_ISOLATION_FWD ("forward") +#define ATTR_QETH_ISOLATION_DROP ("drop") + +static ssize_t qeth_dev_isolation_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qeth_card *card = dev_get_drvdata(dev); @@ -424,44 +428,69 @@ static ssize_t qeth_dev_large_send_show(struct device *dev, if (!card) return -EINVAL; - switch (card->options.large_send) { - case QETH_LARGE_SEND_NO: - return sprintf(buf, "%s\n", "no"); - case QETH_LARGE_SEND_TSO: - return sprintf(buf, "%s\n", "TSO"); + switch (card->options.isolation) { + case ISOLATION_MODE_NONE: + return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_NONE); + case ISOLATION_MODE_FWD: + return snprintf(buf, 9, "%s\n", ATTR_QETH_ISOLATION_FWD); + case ISOLATION_MODE_DROP: + return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_DROP); default: - return sprintf(buf, "%s\n", "N/A"); + return snprintf(buf, 5, "%s\n", "N/A"); } } -static ssize_t qeth_dev_large_send_store(struct device *dev, +static ssize_t qeth_dev_isolation_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); - enum qeth_large_send_types type; + enum qeth_ipa_isolation_modes isolation; int rc = 0; - char *tmp; + char *tmp, *curtoken; + curtoken = (char *) buf; - if (!card) - return -EINVAL; - tmp = strsep((char **) &buf, "\n"); - if (!strcmp(tmp, "no")) { - type = QETH_LARGE_SEND_NO; - } else if (!strcmp(tmp, "TSO")) { - type = QETH_LARGE_SEND_TSO; + if (!card) { + rc = -EINVAL; + goto out; + } + + /* check for unknown, too, in case we do not yet know who we are */ + if (card->info.type != QETH_CARD_TYPE_OSAE && + card->info.type != QETH_CARD_TYPE_UNKNOWN) { + rc = -EOPNOTSUPP; + dev_err(&card->gdev->dev, "Adapter does not " + "support QDIO data connection isolation\n"); + goto out; + } + + /* parse input into isolation mode */ + tmp = strsep(&curtoken, "\n"); + if (!strcmp(tmp, ATTR_QETH_ISOLATION_NONE)) { + isolation = ISOLATION_MODE_NONE; + } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_FWD)) { + isolation = ISOLATION_MODE_FWD; + } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_DROP)) { + isolation = ISOLATION_MODE_DROP; } else { - return -EINVAL; + rc = -EINVAL; + goto out; } - if (card->options.large_send == type) - return count; - rc = qeth_set_large_send(card, type); - if (rc) - return rc; - return count; + rc = count; + + /* defer IP assist if device is offline (until discipline->set_online)*/ + card->options.isolation = isolation; + if (card->state == CARD_STATE_SOFTSETUP || + card->state == CARD_STATE_UP) { + int ipa_rc = qeth_set_access_ctrl_online(card); + if (ipa_rc != 0) + rc = ipa_rc; + } +out: + return rc; } -static DEVICE_ATTR(large_send, 0644, qeth_dev_large_send_show, - qeth_dev_large_send_store); +static DEVICE_ATTR(isolation, 0644, qeth_dev_isolation_show, + qeth_dev_isolation_store); static ssize_t qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value) { @@ -582,7 +611,7 @@ static struct attribute *qeth_device_attrs[] = { &dev_attr_recover.attr, &dev_attr_performance_stats.attr, &dev_attr_layer2.attr, - &dev_attr_large_send.attr, + &dev_attr_isolation.attr, NULL, }; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index f4f3ca1393b2..0b763396d5d1 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -866,7 +866,7 @@ static const struct ethtool_ops qeth_l2_ethtool_ops = { .get_link = ethtool_op_get_link, .get_strings = qeth_core_get_strings, .get_ethtool_stats = qeth_core_get_ethtool_stats, - .get_stats_count = qeth_core_get_stats_count, + .get_sset_count = qeth_core_get_sset_count, .get_drvinfo = qeth_core_get_drvinfo, .get_settings = qeth_core_ethtool_get_settings, }; @@ -874,7 +874,7 @@ static const struct ethtool_ops qeth_l2_ethtool_ops = { static const struct ethtool_ops qeth_l2_osn_ops = { .get_strings = qeth_core_get_strings, .get_ethtool_stats = qeth_core_get_ethtool_stats, - .get_stats_count = qeth_core_get_stats_count, + .get_sset_count = qeth_core_get_sset_count, .get_drvinfo = qeth_core_get_drvinfo, }; @@ -940,30 +940,17 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1); recover_flag = card->state; - rc = ccw_device_set_online(CARD_RDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = ccw_device_set_online(CARD_WDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = ccw_device_set_online(CARD_DDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = qeth_core_hardsetup_card(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); + rc = -ENODEV; goto out_remove; } - if (!card->dev && qeth_l2_setup_netdev(card)) + if (!card->dev && qeth_l2_setup_netdev(card)) { + rc = -ENODEV; goto out_remove; + } if (card->info.type != QETH_CARD_TYPE_OSN) qeth_l2_send_setmac(card, &card->dev->dev_addr[0]); @@ -983,12 +970,14 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) card->lan_online = 0; return 0; } + rc = -ENODEV; goto out_remove; } else card->lan_online = 1; if (card->info.type != QETH_CARD_TYPE_OSN) { - qeth_set_large_send(card, card->options.large_send); + /* configure isolation level */ + qeth_set_access_ctrl_online(card); qeth_l2_process_vlans(card, 0); } @@ -997,6 +986,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) rc = qeth_init_qdio_queues(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + rc = -ENODEV; goto out_remove; } card->state = CARD_STATE_SOFTSETUP; @@ -1018,6 +1008,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); return 0; + out_remove: card->use_hard_stop = 1; qeth_l2_stop_card(card, 0); @@ -1028,7 +1019,7 @@ out_remove: card->state = CARD_STATE_RECOVER; else card->state = CARD_STATE_DOWN; - return -ENODEV; + return rc; } static int qeth_l2_set_online(struct ccwgroup_device *gdev) diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 9f143c83bba3..321988fa9f7d 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -60,5 +60,7 @@ void qeth_l3_del_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *); int qeth_l3_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *); void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions, const u8 *); +int qeth_l3_set_large_send(struct qeth_card *, enum qeth_large_send_types); +int qeth_l3_set_rx_csum(struct qeth_card *, enum qeth_checksum_types); #endif /* __QETH_L3_H__ */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 073b6d354915..fd1b6ed3721f 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -41,6 +41,32 @@ static int qeth_l3_deregister_addr_entry(struct qeth_card *, static int __qeth_l3_set_online(struct ccwgroup_device *, int); static int __qeth_l3_set_offline(struct ccwgroup_device *, int); +int qeth_l3_set_large_send(struct qeth_card *card, + enum qeth_large_send_types type) +{ + int rc = 0; + + card->options.large_send = type; + if (card->dev == NULL) + return 0; + + if (card->options.large_send == QETH_LARGE_SEND_TSO) { + if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { + card->dev->features |= NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM; + } else { + card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM); + card->options.large_send = QETH_LARGE_SEND_NO; + rc = -EOPNOTSUPP; + } + } else { + card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM); + card->options.large_send = QETH_LARGE_SEND_NO; + } + return rc; +} static int qeth_l3_isxdigit(char *buf) { @@ -1439,6 +1465,35 @@ static int qeth_l3_send_checksum_command(struct qeth_card *card) return 0; } +int qeth_l3_set_rx_csum(struct qeth_card *card, + enum qeth_checksum_types csum_type) +{ + int rc = 0; + + if (card->options.checksum_type == HW_CHECKSUMMING) { + if ((csum_type != HW_CHECKSUMMING) && + (card->state != CARD_STATE_DOWN)) { + rc = qeth_l3_send_simple_setassparms(card, + IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0); + if (rc) + return -EIO; + } + } else { + if (csum_type == HW_CHECKSUMMING) { + if (card->state != CARD_STATE_DOWN) { + if (!qeth_is_supported(card, + IPA_INBOUND_CHECKSUM)) + return -EPERM; + rc = qeth_l3_send_checksum_command(card); + if (rc) + return -EIO; + } + } + } + card->options.checksum_type = csum_type; + return rc; +} + static int qeth_l3_start_ipa_checksum(struct qeth_card *card) { int rc = 0; @@ -1506,6 +1561,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card) static int qeth_l3_start_ipassists(struct qeth_card *card) { QETH_DBF_TEXT(TRACE, 3, "strtipas"); + + qeth_set_access_ctrl_online(card); /* go on*/ qeth_l3_start_ipa_arp_processing(card); /* go on*/ qeth_l3_start_ipa_ip_fragmentation(card); /* go on*/ qeth_l3_start_ipa_source_mac(card); /* go on*/ @@ -2684,6 +2741,24 @@ static void qeth_tx_csum(struct sk_buff *skb) *(__sum16 *)(skb->data + offset) = csum_fold(csum); } +static inline int qeth_l3_tso_elements(struct sk_buff *skb) +{ + unsigned long tcpd = (unsigned long)tcp_hdr(skb) + + tcp_hdr(skb)->doff * 4; + int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data); + int elements = PFN_UP(tcpd + tcpd_len) - PFN_DOWN(tcpd); + elements += skb_shinfo(skb)->nr_frags; + return elements; +} + +static inline int qeth_l3_tso_check(struct sk_buff *skb) +{ + int len = ((unsigned long)tcp_hdr(skb) + tcp_hdr(skb)->doff * 4) - + (unsigned long)skb->data; + return (((unsigned long)skb->data & PAGE_MASK) != + (((unsigned long)skb->data + len) & PAGE_MASK)); +} + static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { int rc; @@ -2777,16 +2852,21 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) /* fix hardware limitation: as long as we do not have sbal * chaining we can not send long frag lists */ - if ((large_send == QETH_LARGE_SEND_TSO) && - ((skb_shinfo(new_skb)->nr_frags + 2) > 16)) { - if (skb_linearize(new_skb)) - goto tx_drop; + if (large_send == QETH_LARGE_SEND_TSO) { + if (qeth_l3_tso_elements(new_skb) + 1 > 16) { + if (skb_linearize(new_skb)) + goto tx_drop; + if (card->options.performance_stats) + card->perf_stats.tx_lin++; + } } if ((large_send == QETH_LARGE_SEND_TSO) && (cast_type == RTN_UNSPEC)) { hdr = (struct qeth_hdr *)skb_push(new_skb, sizeof(struct qeth_hdr_tso)); + if (qeth_l3_tso_check(new_skb)) + QETH_DBF_MESSAGE(2, "tso skb misaligned\n"); memset(hdr, 0, sizeof(struct qeth_hdr_tso)); qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type); qeth_tso_fill_header(card, hdr, new_skb); @@ -2903,46 +2983,28 @@ static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev) static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data) { struct qeth_card *card = dev->ml_priv; - enum qeth_card_states old_state; enum qeth_checksum_types csum_type; - if ((card->state != CARD_STATE_UP) && - (card->state != CARD_STATE_DOWN)) - return -EPERM; - if (data) csum_type = HW_CHECKSUMMING; else csum_type = SW_CHECKSUMMING; - if (card->options.checksum_type != csum_type) { - old_state = card->state; - if (card->state == CARD_STATE_UP) - __qeth_l3_set_offline(card->gdev, 1); - card->options.checksum_type = csum_type; - if (old_state == CARD_STATE_UP) - __qeth_l3_set_online(card->gdev, 1); - } - return 0; + return qeth_l3_set_rx_csum(card, csum_type); } static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data) { struct qeth_card *card = dev->ml_priv; + int rc = 0; if (data) { - if (card->options.large_send == QETH_LARGE_SEND_NO) { - if (card->info.type == QETH_CARD_TYPE_IQD) - return -EPERM; - else - card->options.large_send = QETH_LARGE_SEND_TSO; - dev->features |= NETIF_F_TSO; - } + rc = qeth_l3_set_large_send(card, QETH_LARGE_SEND_TSO); } else { dev->features &= ~NETIF_F_TSO; card->options.large_send = QETH_LARGE_SEND_NO; } - return 0; + return rc; } static const struct ethtool_ops qeth_l3_ethtool_ops = { @@ -2957,7 +3019,7 @@ static const struct ethtool_ops qeth_l3_ethtool_ops = { .set_tso = qeth_l3_ethtool_set_tso, .get_strings = qeth_core_get_strings, .get_ethtool_stats = qeth_core_get_ethtool_stats, - .get_stats_count = qeth_core_get_stats_count, + .get_sset_count = qeth_core_get_sset_count, .get_drvinfo = qeth_core_get_drvinfo, .get_settings = qeth_core_ethtool_get_settings, }; @@ -3058,6 +3120,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; card->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; + card->dev->gso_max_size = 15 * PAGE_SIZE; SET_NETDEV_DEV(card->dev, &card->gdev->dev); return register_netdev(card->dev); @@ -3154,32 +3217,19 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1); recover_flag = card->state; - rc = ccw_device_set_online(CARD_RDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = ccw_device_set_online(CARD_WDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = ccw_device_set_online(CARD_DDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - return -EIO; - } - rc = qeth_core_hardsetup_card(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); + rc = -ENODEV; goto out_remove; } qeth_l3_query_ipassists(card, QETH_PROT_IPV4); - if (!card->dev && qeth_l3_setup_netdev(card)) + if (!card->dev && qeth_l3_setup_netdev(card)) { + rc = -ENODEV; goto out_remove; + } card->state = CARD_STATE_HARDSETUP; qeth_print_status_message(card); @@ -3196,10 +3246,11 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) card->lan_online = 0; return 0; } + rc = -ENODEV; goto out_remove; } else card->lan_online = 1; - qeth_set_large_send(card, card->options.large_send); + qeth_l3_set_large_send(card, card->options.large_send); rc = qeth_l3_setadapter_parms(card); if (rc) @@ -3218,6 +3269,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) rc = qeth_init_qdio_queues(card); if (rc) { QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + rc = -ENODEV; goto out_remove; } card->state = CARD_STATE_SOFTSETUP; @@ -3248,7 +3300,7 @@ out_remove: card->state = CARD_STATE_RECOVER; else card->state = CARD_STATE_DOWN; - return -ENODEV; + return rc; } static int qeth_l3_set_online(struct ccwgroup_device *gdev) diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index c144b9924d52..3360b0941aa1 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -293,31 +293,79 @@ static ssize_t qeth_l3_dev_checksum_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); + enum qeth_checksum_types csum_type; char *tmp; + int rc; if (!card) return -EINVAL; - if ((card->state != CARD_STATE_DOWN) && - (card->state != CARD_STATE_RECOVER)) - return -EPERM; - tmp = strsep((char **) &buf, "\n"); if (!strcmp(tmp, "sw_checksumming")) - card->options.checksum_type = SW_CHECKSUMMING; + csum_type = SW_CHECKSUMMING; else if (!strcmp(tmp, "hw_checksumming")) - card->options.checksum_type = HW_CHECKSUMMING; + csum_type = HW_CHECKSUMMING; else if (!strcmp(tmp, "no_checksumming")) - card->options.checksum_type = NO_CHECKSUMMING; - else { + csum_type = NO_CHECKSUMMING; + else return -EINVAL; - } + + rc = qeth_l3_set_rx_csum(card, csum_type); + if (rc) + return rc; return count; } static DEVICE_ATTR(checksumming, 0644, qeth_l3_dev_checksum_show, qeth_l3_dev_checksum_store); +static ssize_t qeth_l3_dev_large_send_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qeth_card *card = dev_get_drvdata(dev); + + if (!card) + return -EINVAL; + + switch (card->options.large_send) { + case QETH_LARGE_SEND_NO: + return sprintf(buf, "%s\n", "no"); + case QETH_LARGE_SEND_TSO: + return sprintf(buf, "%s\n", "TSO"); + default: + return sprintf(buf, "%s\n", "N/A"); + } +} + +static ssize_t qeth_l3_dev_large_send_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct qeth_card *card = dev_get_drvdata(dev); + enum qeth_large_send_types type; + int rc = 0; + char *tmp; + + if (!card) + return -EINVAL; + tmp = strsep((char **) &buf, "\n"); + if (!strcmp(tmp, "no")) + type = QETH_LARGE_SEND_NO; + else if (!strcmp(tmp, "TSO")) + type = QETH_LARGE_SEND_TSO; + else + return -EINVAL; + + if (card->options.large_send == type) + return count; + rc = qeth_l3_set_large_send(card, type); + if (rc) + return rc; + return count; +} + +static DEVICE_ATTR(large_send, 0644, qeth_l3_dev_large_send_show, + qeth_l3_dev_large_send_store); + static struct attribute *qeth_l3_device_attrs[] = { &dev_attr_route4.attr, &dev_attr_route6.attr, @@ -325,6 +373,7 @@ static struct attribute *qeth_l3_device_attrs[] = { &dev_attr_broadcast_mode.attr, &dev_attr_canonical_macaddr.attr, &dev_attr_checksumming.attr, + &dev_attr_large_send.attr, NULL, }; diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 2889e5f2dfd3..9d0c941b7d33 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -31,6 +31,7 @@ #include <linux/miscdevice.h> #include <linux/seq_file.h> #include "zfcp_ext.h" +#include "zfcp_fc.h" #define ZFCP_BUS_ID_SIZE 20 @@ -80,48 +81,40 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) { - struct ccw_device *ccwdev; + struct ccw_device *cdev; struct zfcp_adapter *adapter; struct zfcp_port *port; struct zfcp_unit *unit; - ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); - if (!ccwdev) + cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); + if (!cdev) return; - if (ccw_device_set_online(ccwdev)) - goto out_ccwdev; + if (ccw_device_set_online(cdev)) + goto out_ccw_device; - mutex_lock(&zfcp_data.config_mutex); - adapter = dev_get_drvdata(&ccwdev->dev); + adapter = zfcp_ccw_adapter_by_cdev(cdev); if (!adapter) - goto out_unlock; - zfcp_adapter_get(adapter); + goto out_ccw_device; port = zfcp_get_port_by_wwpn(adapter, wwpn); if (!port) goto out_port; - zfcp_port_get(port); unit = zfcp_unit_enqueue(port, lun); if (IS_ERR(unit)) goto out_unit; - mutex_unlock(&zfcp_data.config_mutex); zfcp_erp_unit_reopen(unit, 0, "auidc_1", NULL); zfcp_erp_wait(adapter); flush_work(&unit->scsi_work); - mutex_lock(&zfcp_data.config_mutex); - zfcp_unit_put(unit); out_unit: - zfcp_port_put(port); + put_device(&port->sysfs_device); out_port: - zfcp_adapter_put(adapter); -out_unlock: - mutex_unlock(&zfcp_data.config_mutex); -out_ccwdev: - put_device(&ccwdev->dev); + zfcp_ccw_adapter_put(adapter); +out_ccw_device: + put_device(&cdev->dev); return; } @@ -167,7 +160,7 @@ static int __init zfcp_module_init(void) int retval = -ENOMEM; zfcp_data.gpn_ft_cache = zfcp_cache_hw_align("zfcp_gpn", - sizeof(struct ct_iu_gpn_ft_req)); + sizeof(struct zfcp_fc_gpn_ft_req)); if (!zfcp_data.gpn_ft_cache) goto out; @@ -182,12 +175,14 @@ static int __init zfcp_module_init(void) goto out_sr_cache; zfcp_data.gid_pn_cache = zfcp_cache_hw_align("zfcp_gid", - sizeof(struct zfcp_gid_pn_data)); + sizeof(struct zfcp_fc_gid_pn)); if (!zfcp_data.gid_pn_cache) goto out_gid_cache; - mutex_init(&zfcp_data.config_mutex); - rwlock_init(&zfcp_data.config_lock); + zfcp_data.adisc_cache = zfcp_cache_hw_align("zfcp_adisc", + sizeof(struct zfcp_fc_els_adisc)); + if (!zfcp_data.adisc_cache) + goto out_adisc_cache; zfcp_data.scsi_transport_template = fc_attach_transport(&zfcp_transport_functions); @@ -200,7 +195,7 @@ static int __init zfcp_module_init(void) goto out_misc; } - retval = zfcp_ccw_register(); + retval = ccw_driver_register(&zfcp_ccw_driver); if (retval) { pr_err("The zfcp device driver could not register with " "the common I/O layer\n"); @@ -216,6 +211,8 @@ out_ccw_register: out_misc: fc_release_transport(zfcp_data.scsi_transport_template); out_transport: + kmem_cache_destroy(zfcp_data.adisc_cache); +out_adisc_cache: kmem_cache_destroy(zfcp_data.gid_pn_cache); out_gid_cache: kmem_cache_destroy(zfcp_data.sr_buffer_cache); @@ -229,6 +226,20 @@ out: module_init(zfcp_module_init); +static void __exit zfcp_module_exit(void) +{ + ccw_driver_unregister(&zfcp_ccw_driver); + misc_deregister(&zfcp_cfdc_misc); + fc_release_transport(zfcp_data.scsi_transport_template); + kmem_cache_destroy(zfcp_data.adisc_cache); + kmem_cache_destroy(zfcp_data.gid_pn_cache); + kmem_cache_destroy(zfcp_data.sr_buffer_cache); + kmem_cache_destroy(zfcp_data.qtcb_cache); + kmem_cache_destroy(zfcp_data.gpn_ft_cache); +} + +module_exit(zfcp_module_exit); + /** * zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN * @port: pointer to port to search for unit @@ -238,12 +249,18 @@ module_init(zfcp_module_init); */ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun) { + unsigned long flags; struct zfcp_unit *unit; - list_for_each_entry(unit, &port->unit_list_head, list) - if ((unit->fcp_lun == fcp_lun) && - !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE)) - return unit; + read_lock_irqsave(&port->unit_list_lock, flags); + list_for_each_entry(unit, &port->unit_list, list) + if (unit->fcp_lun == fcp_lun) { + if (!get_device(&unit->sysfs_device)) + unit = NULL; + read_unlock_irqrestore(&port->unit_list_lock, flags); + return unit; + } + read_unlock_irqrestore(&port->unit_list_lock, flags); return NULL; } @@ -257,18 +274,35 @@ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun) struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, u64 wwpn) { + unsigned long flags; struct zfcp_port *port; - list_for_each_entry(port, &adapter->port_list_head, list) - if ((port->wwpn == wwpn) && - !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE)) + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) + if (port->wwpn == wwpn) { + if (!get_device(&port->sysfs_device)) + port = NULL; + read_unlock_irqrestore(&adapter->port_list_lock, flags); return port; + } + read_unlock_irqrestore(&adapter->port_list_lock, flags); return NULL; } -static void zfcp_sysfs_unit_release(struct device *dev) +/** + * zfcp_unit_release - dequeue unit + * @dev: pointer to device + * + * waits until all work is done on unit and removes it then from the unit->list + * of the associated port. + */ +static void zfcp_unit_release(struct device *dev) { - kfree(container_of(dev, struct zfcp_unit, sysfs_device)); + struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, + sysfs_device); + + put_device(&unit->port->sysfs_device); + kfree(unit); } /** @@ -276,43 +310,40 @@ static void zfcp_sysfs_unit_release(struct device *dev) * @port: pointer to port where unit is added * @fcp_lun: FCP LUN of unit to be enqueued * Returns: pointer to enqueued unit on success, ERR_PTR on error - * Locks: config_mutex must be held to serialize changes to the unit list * * Sets up some unit internal structures and creates sysfs entry. */ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) { struct zfcp_unit *unit; + int retval = -ENOMEM; - read_lock_irq(&zfcp_data.config_lock); - if (zfcp_get_unit_by_lun(port, fcp_lun)) { - read_unlock_irq(&zfcp_data.config_lock); - return ERR_PTR(-EINVAL); + get_device(&port->sysfs_device); + + unit = zfcp_get_unit_by_lun(port, fcp_lun); + if (unit) { + put_device(&unit->sysfs_device); + retval = -EEXIST; + goto err_out; } - read_unlock_irq(&zfcp_data.config_lock); unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); if (!unit) - return ERR_PTR(-ENOMEM); - - atomic_set(&unit->refcount, 0); - init_waitqueue_head(&unit->remove_wq); - INIT_WORK(&unit->scsi_work, zfcp_scsi_scan); + goto err_out; unit->port = port; unit->fcp_lun = fcp_lun; + unit->sysfs_device.parent = &port->sysfs_device; + unit->sysfs_device.release = zfcp_unit_release; if (dev_set_name(&unit->sysfs_device, "0x%016llx", (unsigned long long) fcp_lun)) { kfree(unit); - return ERR_PTR(-ENOMEM); + goto err_out; } - unit->sysfs_device.parent = &port->sysfs_device; - unit->sysfs_device.release = zfcp_sysfs_unit_release; - dev_set_drvdata(&unit->sysfs_device, unit); + retval = -EINVAL; - /* mark unit unusable as long as sysfs registration is not complete */ - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); + INIT_WORK(&unit->scsi_work, zfcp_scsi_scan); spin_lock_init(&unit->latencies.lock); unit->latencies.write.channel.min = 0xFFFFFFFF; @@ -324,50 +355,30 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) if (device_register(&unit->sysfs_device)) { put_device(&unit->sysfs_device); - return ERR_PTR(-EINVAL); + goto err_out; } if (sysfs_create_group(&unit->sysfs_device.kobj, - &zfcp_sysfs_unit_attrs)) { - device_unregister(&unit->sysfs_device); - return ERR_PTR(-EINVAL); - } + &zfcp_sysfs_unit_attrs)) + goto err_out_put; - zfcp_unit_get(unit); + write_lock_irq(&port->unit_list_lock); + list_add_tail(&unit->list, &port->unit_list); + write_unlock_irq(&port->unit_list_lock); - write_lock_irq(&zfcp_data.config_lock); - list_add_tail(&unit->list, &port->unit_list_head); - atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status); - write_unlock_irq(&zfcp_data.config_lock); - - zfcp_port_get(port); - return unit; -} -/** - * zfcp_unit_dequeue - dequeue unit - * @unit: pointer to zfcp_unit - * - * waits until all work is done on unit and removes it then from the unit->list - * of the associated port. - */ -void zfcp_unit_dequeue(struct zfcp_unit *unit) -{ - wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0); - write_lock_irq(&zfcp_data.config_lock); - list_del(&unit->list); - write_unlock_irq(&zfcp_data.config_lock); - zfcp_port_put(unit->port); - sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs); +err_out_put: device_unregister(&unit->sysfs_device); +err_out: + put_device(&port->sysfs_device); + return ERR_PTR(retval); } static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) { - /* must only be called with zfcp_data.config_mutex taken */ adapter->pool.erp_req = mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); if (!adapter->pool.erp_req) @@ -405,9 +416,9 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) if (!adapter->pool.status_read_data) return -ENOMEM; - adapter->pool.gid_pn_data = + adapter->pool.gid_pn = mempool_create_slab_pool(1, zfcp_data.gid_pn_cache); - if (!adapter->pool.gid_pn_data) + if (!adapter->pool.gid_pn) return -ENOMEM; return 0; @@ -415,7 +426,6 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { - /* zfcp_data.config_mutex must be held */ if (adapter->pool.erp_req) mempool_destroy(adapter->pool.erp_req); if (adapter->pool.scsi_req) @@ -428,8 +438,8 @@ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) mempool_destroy(adapter->pool.status_read_req); if (adapter->pool.status_read_data) mempool_destroy(adapter->pool.status_read_data); - if (adapter->pool.gid_pn_data) - mempool_destroy(adapter->pool.gid_pn_data); + if (adapter->pool.gid_pn) + mempool_destroy(adapter->pool.gid_pn); } /** @@ -497,53 +507,56 @@ static void zfcp_destroy_adapter_work_queue(struct zfcp_adapter *adapter) * zfcp_adapter_enqueue - enqueue a new adapter to the list * @ccw_device: pointer to the struct cc_device * - * Returns: 0 if a new adapter was successfully enqueued - * -ENOMEM if alloc failed + * Returns: struct zfcp_adapter* * Enqueues an adapter at the end of the adapter list in the driver data. * All adapter internal structures are set up. * Proc-fs entries are also created. - * locks: config_mutex must be held to serialize changes to the adapter list */ -int zfcp_adapter_enqueue(struct ccw_device *ccw_device) +struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) { struct zfcp_adapter *adapter; - /* - * Note: It is safe to release the list_lock, as any list changes - * are protected by the config_mutex, which must be held to get here - */ + if (!get_device(&ccw_device->dev)) + return ERR_PTR(-ENODEV); adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL); - if (!adapter) - return -ENOMEM; + if (!adapter) { + put_device(&ccw_device->dev); + return ERR_PTR(-ENOMEM); + } + + kref_init(&adapter->ref); ccw_device->handler = NULL; adapter->ccw_device = ccw_device; - atomic_set(&adapter->refcount, 0); + + INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); + INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports); if (zfcp_qdio_setup(adapter)) - goto qdio_failed; + goto failed; if (zfcp_allocate_low_mem_buffers(adapter)) - goto low_mem_buffers_failed; + goto failed; if (zfcp_reqlist_alloc(adapter)) - goto low_mem_buffers_failed; + goto failed; if (zfcp_dbf_adapter_register(adapter)) - goto debug_register_failed; + goto failed; if (zfcp_setup_adapter_work_queue(adapter)) - goto work_queue_failed; + goto failed; if (zfcp_fc_gs_setup(adapter)) - goto generic_services_failed; + goto failed; + + rwlock_init(&adapter->port_list_lock); + INIT_LIST_HEAD(&adapter->port_list); - init_waitqueue_head(&adapter->remove_wq); init_waitqueue_head(&adapter->erp_ready_wq); init_waitqueue_head(&adapter->erp_done_wqh); - INIT_LIST_HEAD(&adapter->port_list_head); INIT_LIST_HEAD(&adapter->erp_ready_head); INIT_LIST_HEAD(&adapter->erp_running_head); @@ -553,83 +566,85 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) rwlock_init(&adapter->abort_lock); if (zfcp_erp_thread_setup(adapter)) - goto erp_thread_failed; - - INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); - INIT_WORK(&adapter->scan_work, _zfcp_fc_scan_ports_later); + goto failed; adapter->service_level.seq_print = zfcp_print_sl; - /* mark adapter unusable as long as sysfs registration is not complete */ - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); - dev_set_drvdata(&ccw_device->dev, adapter); if (sysfs_create_group(&ccw_device->dev.kobj, &zfcp_sysfs_adapter_attrs)) - goto sysfs_failed; - - atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); + goto failed; if (!zfcp_adapter_scsi_register(adapter)) - return 0; + return adapter; -sysfs_failed: - zfcp_erp_thread_kill(adapter); -erp_thread_failed: - zfcp_fc_gs_destroy(adapter); -generic_services_failed: +failed: + zfcp_adapter_unregister(adapter); + return ERR_PTR(-ENOMEM); +} + +void zfcp_adapter_unregister(struct zfcp_adapter *adapter) +{ + struct ccw_device *cdev = adapter->ccw_device; + + cancel_work_sync(&adapter->scan_work); + cancel_work_sync(&adapter->stat_work); zfcp_destroy_adapter_work_queue(adapter); -work_queue_failed: + + zfcp_fc_wka_ports_force_offline(adapter->gs); + zfcp_adapter_scsi_unregister(adapter); + sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs); + + zfcp_erp_thread_kill(adapter); zfcp_dbf_adapter_unregister(adapter->dbf); -debug_register_failed: - dev_set_drvdata(&ccw_device->dev, NULL); - kfree(adapter->req_list); -low_mem_buffers_failed: - zfcp_free_low_mem_buffers(adapter); -qdio_failed: zfcp_qdio_destroy(adapter->qdio); - kfree(adapter); - return -ENOMEM; + + zfcp_ccw_adapter_put(adapter); /* final put to release */ } /** - * zfcp_adapter_dequeue - remove the adapter from the resource list - * @adapter: pointer to struct zfcp_adapter which should be removed + * zfcp_adapter_release - remove the adapter from the resource list + * @ref: pointer to struct kref * locks: adapter list write lock is assumed to be held by caller */ -void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) +void zfcp_adapter_release(struct kref *ref) { - int retval = 0; - unsigned long flags; + struct zfcp_adapter *adapter = container_of(ref, struct zfcp_adapter, + ref); + struct ccw_device *cdev = adapter->ccw_device; - cancel_work_sync(&adapter->stat_work); - zfcp_fc_wka_ports_force_offline(adapter->gs); - sysfs_remove_group(&adapter->ccw_device->dev.kobj, - &zfcp_sysfs_adapter_attrs); dev_set_drvdata(&adapter->ccw_device->dev, NULL); - /* sanity check: no pending FSF requests */ - spin_lock_irqsave(&adapter->req_list_lock, flags); - retval = zfcp_reqlist_isempty(adapter); - spin_unlock_irqrestore(&adapter->req_list_lock, flags); - if (!retval) - return; - zfcp_fc_gs_destroy(adapter); - zfcp_erp_thread_kill(adapter); - zfcp_destroy_adapter_work_queue(adapter); - zfcp_dbf_adapter_unregister(adapter->dbf); zfcp_free_low_mem_buffers(adapter); - zfcp_qdio_destroy(adapter->qdio); kfree(adapter->req_list); kfree(adapter->fc_stats); kfree(adapter->stats_reset_data); kfree(adapter); + put_device(&cdev->dev); } -static void zfcp_sysfs_port_release(struct device *dev) +/** + * zfcp_device_unregister - remove port, unit from system + * @dev: reference to device which is to be removed + * @grp: related reference to attribute group + * + * Helper function to unregister port, unit from system + */ +void zfcp_device_unregister(struct device *dev, + const struct attribute_group *grp) { - kfree(container_of(dev, struct zfcp_port, sysfs_device)); + sysfs_remove_group(&dev->kobj, grp); + device_unregister(dev); +} + +static void zfcp_port_release(struct device *dev) +{ + struct zfcp_port *port = container_of(dev, struct zfcp_port, + sysfs_device); + + zfcp_ccw_adapter_put(port->adapter); + kfree(port); } /** @@ -639,7 +654,6 @@ static void zfcp_sysfs_port_release(struct device *dev) * @status: initial status for the port * @d_id: destination id of the remote port to be enqueued * Returns: pointer to enqueued port on success, ERR_PTR on error - * Locks: config_mutex must be held to serialize changes to the port list * * All port internal structures are set up and the sysfs entry is generated. * d_id is used to enqueue ports with a well known address like the Directory @@ -649,20 +663,24 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, u32 status, u32 d_id) { struct zfcp_port *port; + int retval = -ENOMEM; - read_lock_irq(&zfcp_data.config_lock); - if (zfcp_get_port_by_wwpn(adapter, wwpn)) { - read_unlock_irq(&zfcp_data.config_lock); - return ERR_PTR(-EINVAL); + kref_get(&adapter->ref); + + port = zfcp_get_port_by_wwpn(adapter, wwpn); + if (port) { + put_device(&port->sysfs_device); + retval = -EEXIST; + goto err_out; } - read_unlock_irq(&zfcp_data.config_lock); port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL); if (!port) - return ERR_PTR(-ENOMEM); + goto err_out; + + rwlock_init(&port->unit_list_lock); + INIT_LIST_HEAD(&port->unit_list); - init_waitqueue_head(&port->remove_wq); - INIT_LIST_HEAD(&port->unit_list_head); INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); INIT_WORK(&port->rport_work, zfcp_scsi_rport_work); @@ -671,58 +689,38 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, port->d_id = d_id; port->wwpn = wwpn; port->rport_task = RPORT_NONE; - - /* mark port unusable as long as sysfs registration is not complete */ - atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status); - atomic_set(&port->refcount, 0); + port->sysfs_device.parent = &adapter->ccw_device->dev; + port->sysfs_device.release = zfcp_port_release; if (dev_set_name(&port->sysfs_device, "0x%016llx", (unsigned long long)wwpn)) { kfree(port); - return ERR_PTR(-ENOMEM); + goto err_out; } - port->sysfs_device.parent = &adapter->ccw_device->dev; - port->sysfs_device.release = zfcp_sysfs_port_release; - dev_set_drvdata(&port->sysfs_device, port); + retval = -EINVAL; if (device_register(&port->sysfs_device)) { put_device(&port->sysfs_device); - return ERR_PTR(-EINVAL); + goto err_out; } if (sysfs_create_group(&port->sysfs_device.kobj, - &zfcp_sysfs_port_attrs)) { - device_unregister(&port->sysfs_device); - return ERR_PTR(-EINVAL); - } + &zfcp_sysfs_port_attrs)) + goto err_out_put; - zfcp_port_get(port); + write_lock_irq(&adapter->port_list_lock); + list_add_tail(&port->list, &adapter->port_list); + write_unlock_irq(&adapter->port_list_lock); - write_lock_irq(&zfcp_data.config_lock); - list_add_tail(&port->list, &adapter->port_list_head); - atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); - atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status); + atomic_set_mask(status | ZFCP_STATUS_COMMON_RUNNING, &port->status); - write_unlock_irq(&zfcp_data.config_lock); - - zfcp_adapter_get(adapter); return port; -} -/** - * zfcp_port_dequeue - dequeues a port from the port list of the adapter - * @port: pointer to struct zfcp_port which should be removed - */ -void zfcp_port_dequeue(struct zfcp_port *port) -{ - write_lock_irq(&zfcp_data.config_lock); - list_del(&port->list); - write_unlock_irq(&zfcp_data.config_lock); - wait_event(port->remove_wq, atomic_read(&port->refcount) == 0); - cancel_work_sync(&port->rport_work); /* usually not necessary */ - zfcp_adapter_put(port->adapter); - sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs); +err_out_put: device_unregister(&port->sysfs_device); +err_out: + zfcp_ccw_adapter_put(adapter); + return ERR_PTR(retval); } /** diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index e08339428ecf..c22cb72a5ae8 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -13,28 +13,34 @@ #define ZFCP_MODEL_PRIV 0x4 -static int zfcp_ccw_suspend(struct ccw_device *cdev) +static DEFINE_SPINLOCK(zfcp_ccw_adapter_ref_lock); +struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *cdev) { - struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev); - - if (!adapter) - return 0; - - mutex_lock(&zfcp_data.config_mutex); + struct zfcp_adapter *adapter; + unsigned long flags; - zfcp_erp_adapter_shutdown(adapter, 0, "ccsusp1", NULL); - zfcp_erp_wait(adapter); + spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags); + adapter = dev_get_drvdata(&cdev->dev); + if (adapter) + kref_get(&adapter->ref); + spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags); + return adapter; +} - mutex_unlock(&zfcp_data.config_mutex); +void zfcp_ccw_adapter_put(struct zfcp_adapter *adapter) +{ + unsigned long flags; - return 0; + spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags); + kref_put(&adapter->ref, zfcp_adapter_release); + spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags); } static int zfcp_ccw_activate(struct ccw_device *cdev) { - struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); if (!adapter) return 0; @@ -46,6 +52,8 @@ static int zfcp_ccw_activate(struct ccw_device *cdev) zfcp_erp_wait(adapter); flush_work(&adapter->scan_work); + zfcp_ccw_adapter_put(adapter); + return 0; } @@ -67,28 +75,28 @@ int zfcp_ccw_priv_sch(struct zfcp_adapter *adapter) /** * zfcp_ccw_probe - probe function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: pointer to belonging ccw device * * This function gets called by the common i/o layer for each FCP * device found on the current system. This is only a stub to make cio * work: To only allocate adapter resources for devices actually used, * the allocation is deferred to the first call to ccw_set_online. */ -static int zfcp_ccw_probe(struct ccw_device *ccw_device) +static int zfcp_ccw_probe(struct ccw_device *cdev) { return 0; } /** * zfcp_ccw_remove - remove function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: pointer to belonging ccw device * * This function gets called by the common i/o layer and removes an adapter * from the system. Task of this function is to get rid of all units and * ports that belong to this adapter. And in addition all resources of this * adapter will be freed too. */ -static void zfcp_ccw_remove(struct ccw_device *ccw_device) +static void zfcp_ccw_remove(struct ccw_device *cdev) { struct zfcp_adapter *adapter; struct zfcp_port *port, *p; @@ -96,49 +104,37 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device) LIST_HEAD(unit_remove_lh); LIST_HEAD(port_remove_lh); - ccw_device_set_offline(ccw_device); + ccw_device_set_offline(cdev); - mutex_lock(&zfcp_data.config_mutex); - adapter = dev_get_drvdata(&ccw_device->dev); + adapter = zfcp_ccw_adapter_by_cdev(cdev); if (!adapter) - goto out; - mutex_unlock(&zfcp_data.config_mutex); + return; - cancel_work_sync(&adapter->scan_work); - - mutex_lock(&zfcp_data.config_mutex); - - /* this also removes the scsi devices, so call it first */ - zfcp_adapter_scsi_unregister(adapter); - - write_lock_irq(&zfcp_data.config_lock); - list_for_each_entry_safe(port, p, &adapter->port_list_head, list) { - list_for_each_entry_safe(unit, u, &port->unit_list_head, list) { + write_lock_irq(&adapter->port_list_lock); + list_for_each_entry_safe(port, p, &adapter->port_list, list) { + write_lock(&port->unit_list_lock); + list_for_each_entry_safe(unit, u, &port->unit_list, list) list_move(&unit->list, &unit_remove_lh); - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, - &unit->status); - } + write_unlock(&port->unit_list_lock); list_move(&port->list, &port_remove_lh); - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); } - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); - write_unlock_irq(&zfcp_data.config_lock); + write_unlock_irq(&adapter->port_list_lock); + zfcp_ccw_adapter_put(adapter); /* put from zfcp_ccw_adapter_by_cdev */ - list_for_each_entry_safe(port, p, &port_remove_lh, list) { - list_for_each_entry_safe(unit, u, &unit_remove_lh, list) - zfcp_unit_dequeue(unit); - zfcp_port_dequeue(port); - } - wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0); - zfcp_adapter_dequeue(adapter); + list_for_each_entry_safe(unit, u, &unit_remove_lh, list) + zfcp_device_unregister(&unit->sysfs_device, + &zfcp_sysfs_unit_attrs); -out: - mutex_unlock(&zfcp_data.config_mutex); + list_for_each_entry_safe(port, p, &port_remove_lh, list) + zfcp_device_unregister(&port->sysfs_device, + &zfcp_sysfs_port_attrs); + + zfcp_adapter_unregister(adapter); } /** * zfcp_ccw_set_online - set_online function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: pointer to belonging ccw device * * This function gets called by the common i/o layer and sets an * adapter into state online. The first call will allocate all @@ -149,23 +145,20 @@ out: * the SCSI stack, that the QDIO queues will be set up and that the * adapter will be opened. */ -static int zfcp_ccw_set_online(struct ccw_device *ccw_device) +static int zfcp_ccw_set_online(struct ccw_device *cdev) { - struct zfcp_adapter *adapter; - int ret = 0; - - mutex_lock(&zfcp_data.config_mutex); - adapter = dev_get_drvdata(&ccw_device->dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); if (!adapter) { - ret = zfcp_adapter_enqueue(ccw_device); - if (ret) { - dev_err(&ccw_device->dev, + adapter = zfcp_adapter_enqueue(cdev); + + if (IS_ERR(adapter)) { + dev_err(&cdev->dev, "Setting up data structures for the " "FCP adapter failed\n"); - goto out; + return PTR_ERR(adapter); } - adapter = dev_get_drvdata(&ccw_device->dev); + kref_get(&adapter->ref); } /* initialize request counter */ @@ -177,58 +170,61 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device) zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, "ccsonl2", NULL); zfcp_erp_wait(adapter); -out: - mutex_unlock(&zfcp_data.config_mutex); - if (!ret) - flush_work(&adapter->scan_work); - return ret; + + flush_work(&adapter->scan_work); + + zfcp_ccw_adapter_put(adapter); + return 0; } /** * zfcp_ccw_set_offline - set_offline function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: pointer to belonging ccw device * * This function gets called by the common i/o layer and sets an adapter * into state offline. */ -static int zfcp_ccw_set_offline(struct ccw_device *ccw_device) +static int zfcp_ccw_set_offline(struct ccw_device *cdev) { - struct zfcp_adapter *adapter; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return 0; - mutex_lock(&zfcp_data.config_mutex); - adapter = dev_get_drvdata(&ccw_device->dev); zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL); zfcp_erp_wait(adapter); - mutex_unlock(&zfcp_data.config_mutex); + + zfcp_ccw_adapter_put(adapter); return 0; } /** * zfcp_ccw_notify - ccw notify function - * @ccw_device: pointer to belonging ccw device + * @cdev: pointer to belonging ccw device * @event: indicates if adapter was detached or attached * * This function gets called by the common i/o layer if an adapter has gone * or reappeared. */ -static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event) +static int zfcp_ccw_notify(struct ccw_device *cdev, int event) { - struct zfcp_adapter *adapter = dev_get_drvdata(&ccw_device->dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return 1; switch (event) { case CIO_GONE: - dev_warn(&adapter->ccw_device->dev, - "The FCP device has been detached\n"); + dev_warn(&cdev->dev, "The FCP device has been detached\n"); zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1", NULL); break; case CIO_NO_PATH: - dev_warn(&adapter->ccw_device->dev, + dev_warn(&cdev->dev, "The CHPID for the FCP device is offline\n"); zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2", NULL); break; case CIO_OPER: - dev_info(&adapter->ccw_device->dev, - "The FCP device is operational again\n"); + dev_info(&cdev->dev, "The FCP device is operational again\n"); zfcp_erp_modify_adapter_status(adapter, "ccnoti3", NULL, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); @@ -236,11 +232,13 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event) "ccnoti4", NULL); break; case CIO_BOXED: - dev_warn(&adapter->ccw_device->dev, "The FCP device " - "did not respond within the specified time\n"); + dev_warn(&cdev->dev, "The FCP device did not respond within " + "the specified time\n"); zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5", NULL); break; } + + zfcp_ccw_adapter_put(adapter); return 1; } @@ -250,18 +248,16 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event) */ static void zfcp_ccw_shutdown(struct ccw_device *cdev) { - struct zfcp_adapter *adapter; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); - mutex_lock(&zfcp_data.config_mutex); - adapter = dev_get_drvdata(&cdev->dev); if (!adapter) - goto out; + return; zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL); zfcp_erp_wait(adapter); zfcp_erp_thread_kill(adapter); -out: - mutex_unlock(&zfcp_data.config_mutex); + + zfcp_ccw_adapter_put(adapter); } struct ccw_driver zfcp_ccw_driver = { @@ -274,18 +270,7 @@ struct ccw_driver zfcp_ccw_driver = { .set_offline = zfcp_ccw_set_offline, .notify = zfcp_ccw_notify, .shutdown = zfcp_ccw_shutdown, - .freeze = zfcp_ccw_suspend, + .freeze = zfcp_ccw_set_offline, .thaw = zfcp_ccw_activate, .restore = zfcp_ccw_activate, }; - -/** - * zfcp_ccw_register - ccw register function - * - * Registers the driver at the common i/o layer. This function will be called - * at module load time/system start. - */ -int __init zfcp_ccw_register(void) -{ - return ccw_driver_register(&zfcp_ccw_driver); -} diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c index ef681dfed0cc..f932400e980a 100644 --- a/drivers/s390/scsi/zfcp_cfdc.c +++ b/drivers/s390/scsi/zfcp_cfdc.c @@ -86,22 +86,17 @@ static int zfcp_cfdc_copy_to_user(void __user *user_buffer, static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno) { char busid[9]; - struct ccw_device *ccwdev; - struct zfcp_adapter *adapter = NULL; + struct ccw_device *cdev; + struct zfcp_adapter *adapter; snprintf(busid, sizeof(busid), "0.0.%04x", devno); - ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); - if (!ccwdev) - goto out; - - adapter = dev_get_drvdata(&ccwdev->dev); - if (!adapter) - goto out_put; - - zfcp_adapter_get(adapter); -out_put: - put_device(&ccwdev->dev); -out: + cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); + if (!cdev) + return NULL; + + adapter = zfcp_ccw_adapter_by_cdev(cdev); + + put_device(&cdev->dev); return adapter; } @@ -212,7 +207,6 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command, retval = -ENXIO; goto free_buffer; } - zfcp_adapter_get(adapter); retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg, data_user->control_file); @@ -245,7 +239,7 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command, free_sg: zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES); adapter_put: - zfcp_adapter_put(adapter); + zfcp_ccw_adapter_put(adapter); free_buffer: kfree(data); no_mem_sense: diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 215b70749e95..84450955ae11 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -13,6 +13,7 @@ #include <asm/debug.h> #include "zfcp_dbf.h" #include "zfcp_ext.h" +#include "zfcp_fc.h" static u32 dbfsize = 4; @@ -177,8 +178,7 @@ void _zfcp_dbf_hba_fsf_response(const char *tag2, int level, case FSF_QTCB_SEND_ELS: send_els = (struct zfcp_send_els *)fsf_req->data; - response->u.els.d_id = qtcb->bottom.support.d_id; - response->u.els.ls_code = send_els->ls_code >> 24; + response->u.els.d_id = ntoh24(qtcb->bottom.support.d_id); break; case FSF_QTCB_ABORT_FCP_CMND: @@ -348,7 +348,6 @@ static void zfcp_dbf_hba_view_response(char **p, case FSF_QTCB_SEND_ELS: zfcp_dbf_out(p, "d_id", "0x%06x", r->u.els.d_id); - zfcp_dbf_out(p, "ls_code", "0x%02x", r->u.els.ls_code); break; case FSF_QTCB_ABORT_FCP_CMND: @@ -677,14 +676,14 @@ void zfcp_dbf_rec_action(char *id2, struct zfcp_erp_action *erp_action) /** * zfcp_dbf_san_ct_request - trace event for issued CT request * @fsf_req: request containing issued CT data + * @d_id: destination id where ct request is sent to */ -void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req) +void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req, u32 d_id) { - struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data; - struct zfcp_wka_port *wka_port = ct->wka_port; - struct zfcp_adapter *adapter = wka_port->adapter; + struct zfcp_fsf_ct_els *ct = (struct zfcp_fsf_ct_els *)fsf_req->data; + struct zfcp_adapter *adapter = fsf_req->adapter; struct zfcp_dbf *dbf = adapter->dbf; - struct ct_hdr *hdr = sg_virt(ct->req); + struct fc_ct_hdr *hdr = sg_virt(ct->req); struct zfcp_dbf_san_record *r = &dbf->san_buf; struct zfcp_dbf_san_record_ct_request *oct = &r->u.ct_req; int level = 3; @@ -695,19 +694,18 @@ void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req) strncpy(r->tag, "octc", ZFCP_DBF_TAG_SIZE); r->fsf_reqid = fsf_req->req_id; r->fsf_seqno = fsf_req->seq_no; - r->s_id = fc_host_port_id(adapter->scsi_host); - r->d_id = wka_port->d_id; - oct->cmd_req_code = hdr->cmd_rsp_code; - oct->revision = hdr->revision; - oct->gs_type = hdr->gs_type; - oct->gs_subtype = hdr->gs_subtype; - oct->options = hdr->options; - oct->max_res_size = hdr->max_res_size; - oct->len = min((int)ct->req->length - (int)sizeof(struct ct_hdr), + oct->d_id = d_id; + oct->cmd_req_code = hdr->ct_cmd; + oct->revision = hdr->ct_rev; + oct->gs_type = hdr->ct_fs_type; + oct->gs_subtype = hdr->ct_fs_subtype; + oct->options = hdr->ct_options; + oct->max_res_size = hdr->ct_mr_size; + oct->len = min((int)ct->req->length - (int)sizeof(struct fc_ct_hdr), ZFCP_DBF_SAN_MAX_PAYLOAD); debug_event(dbf->san, level, r, sizeof(*r)); zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level, - (void *)hdr + sizeof(struct ct_hdr), oct->len); + (void *)hdr + sizeof(struct fc_ct_hdr), oct->len); spin_unlock_irqrestore(&dbf->san_lock, flags); } @@ -717,10 +715,9 @@ void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req) */ void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *fsf_req) { - struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data; - struct zfcp_wka_port *wka_port = ct->wka_port; - struct zfcp_adapter *adapter = wka_port->adapter; - struct ct_hdr *hdr = sg_virt(ct->resp); + struct zfcp_fsf_ct_els *ct = (struct zfcp_fsf_ct_els *)fsf_req->data; + struct zfcp_adapter *adapter = fsf_req->adapter; + struct fc_ct_hdr *hdr = sg_virt(ct->resp); struct zfcp_dbf *dbf = adapter->dbf; struct zfcp_dbf_san_record *r = &dbf->san_buf; struct zfcp_dbf_san_record_ct_response *rct = &r->u.ct_resp; @@ -732,25 +729,23 @@ void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *fsf_req) strncpy(r->tag, "rctc", ZFCP_DBF_TAG_SIZE); r->fsf_reqid = fsf_req->req_id; r->fsf_seqno = fsf_req->seq_no; - r->s_id = wka_port->d_id; - r->d_id = fc_host_port_id(adapter->scsi_host); - rct->cmd_rsp_code = hdr->cmd_rsp_code; - rct->revision = hdr->revision; - rct->reason_code = hdr->reason_code; - rct->expl = hdr->reason_code_expl; - rct->vendor_unique = hdr->vendor_unique; - rct->max_res_size = hdr->max_res_size; - rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr), + rct->cmd_rsp_code = hdr->ct_cmd; + rct->revision = hdr->ct_rev; + rct->reason_code = hdr->ct_reason; + rct->expl = hdr->ct_explan; + rct->vendor_unique = hdr->ct_vendor; + rct->max_res_size = hdr->ct_mr_size; + rct->len = min((int)ct->resp->length - (int)sizeof(struct fc_ct_hdr), ZFCP_DBF_SAN_MAX_PAYLOAD); debug_event(dbf->san, level, r, sizeof(*r)); zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level, - (void *)hdr + sizeof(struct ct_hdr), rct->len); + (void *)hdr + sizeof(struct fc_ct_hdr), rct->len); spin_unlock_irqrestore(&dbf->san_lock, flags); } static void zfcp_dbf_san_els(const char *tag, int level, - struct zfcp_fsf_req *fsf_req, u32 s_id, u32 d_id, - u8 ls_code, void *buffer, int buflen) + struct zfcp_fsf_req *fsf_req, u32 d_id, + void *buffer, int buflen) { struct zfcp_adapter *adapter = fsf_req->adapter; struct zfcp_dbf *dbf = adapter->dbf; @@ -762,9 +757,7 @@ static void zfcp_dbf_san_els(const char *tag, int level, strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); rec->fsf_reqid = fsf_req->req_id; rec->fsf_seqno = fsf_req->seq_no; - rec->s_id = s_id; - rec->d_id = d_id; - rec->u.els.ls_code = ls_code; + rec->u.els.d_id = d_id; debug_event(dbf->san, level, rec, sizeof(*rec)); zfcp_dbf_hexdump(dbf->san, rec, sizeof(*rec), level, buffer, min(buflen, ZFCP_DBF_SAN_MAX_PAYLOAD)); @@ -777,12 +770,11 @@ static void zfcp_dbf_san_els(const char *tag, int level, */ void zfcp_dbf_san_els_request(struct zfcp_fsf_req *fsf_req) { - struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data; + struct zfcp_fsf_ct_els *els = (struct zfcp_fsf_ct_els *)fsf_req->data; + u32 d_id = ntoh24(fsf_req->qtcb->bottom.support.d_id); - zfcp_dbf_san_els("oels", 2, fsf_req, - fc_host_port_id(els->adapter->scsi_host), - els->d_id, *(u8 *) sg_virt(els->req), - sg_virt(els->req), els->req->length); + zfcp_dbf_san_els("oels", 2, fsf_req, d_id, + sg_virt(els->req), els->req->length); } /** @@ -791,12 +783,11 @@ void zfcp_dbf_san_els_request(struct zfcp_fsf_req *fsf_req) */ void zfcp_dbf_san_els_response(struct zfcp_fsf_req *fsf_req) { - struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data; + struct zfcp_fsf_ct_els *els = (struct zfcp_fsf_ct_els *)fsf_req->data; + u32 d_id = ntoh24(fsf_req->qtcb->bottom.support.d_id); - zfcp_dbf_san_els("rels", 2, fsf_req, els->d_id, - fc_host_port_id(els->adapter->scsi_host), - *(u8 *)sg_virt(els->req), sg_virt(els->resp), - els->resp->length); + zfcp_dbf_san_els("rels", 2, fsf_req, d_id, + sg_virt(els->resp), els->resp->length); } /** @@ -805,16 +796,13 @@ void zfcp_dbf_san_els_response(struct zfcp_fsf_req *fsf_req) */ void zfcp_dbf_san_incoming_els(struct zfcp_fsf_req *fsf_req) { - struct zfcp_adapter *adapter = fsf_req->adapter; struct fsf_status_read_buffer *buf = (struct fsf_status_read_buffer *)fsf_req->data; int length = (int)buf->length - (int)((void *)&buf->payload - (void *)buf); - zfcp_dbf_san_els("iels", 1, fsf_req, buf->d_id, - fc_host_port_id(adapter->scsi_host), - buf->payload.data[0], (void *)buf->payload.data, - length); + zfcp_dbf_san_els("iels", 1, fsf_req, ntoh24(buf->d_id), + (void *)buf->payload.data, length); } static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view, @@ -829,11 +817,10 @@ static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view, zfcp_dbf_tag(&p, "tag", r->tag); zfcp_dbf_out(&p, "fsf_reqid", "0x%0Lx", r->fsf_reqid); zfcp_dbf_out(&p, "fsf_seqno", "0x%08x", r->fsf_seqno); - zfcp_dbf_out(&p, "s_id", "0x%06x", r->s_id); - zfcp_dbf_out(&p, "d_id", "0x%06x", r->d_id); if (strncmp(r->tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) { struct zfcp_dbf_san_record_ct_request *ct = &r->u.ct_req; + zfcp_dbf_out(&p, "d_id", "0x%06x", ct->d_id); zfcp_dbf_out(&p, "cmd_req_code", "0x%04x", ct->cmd_req_code); zfcp_dbf_out(&p, "revision", "0x%02x", ct->revision); zfcp_dbf_out(&p, "gs_type", "0x%02x", ct->gs_type); @@ -852,7 +839,7 @@ static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view, strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 || strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) { struct zfcp_dbf_san_record_els *els = &r->u.els; - zfcp_dbf_out(&p, "ls_code", "0x%02x", els->ls_code); + zfcp_dbf_out(&p, "d_id", "0x%06x", els->d_id); } return p - out_buf; } @@ -870,8 +857,9 @@ void _zfcp_dbf_scsi(const char *tag, const char *tag2, int level, struct zfcp_dbf_scsi_record *rec = &dbf->scsi_buf; struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec; unsigned long flags; - struct fcp_rsp_iu *fcp_rsp; - char *fcp_rsp_info = NULL, *fcp_sns_info = NULL; + struct fcp_resp_with_ext *fcp_rsp; + struct fcp_resp_rsp_info *fcp_rsp_info = NULL; + char *fcp_sns_info = NULL; int offset = 0, buflen = 0; spin_lock_irqsave(&dbf->scsi_lock, flags); @@ -895,20 +883,22 @@ void _zfcp_dbf_scsi(const char *tag, const char *tag2, int level, rec->scsi_allowed = scsi_cmnd->allowed; } if (fsf_req != NULL) { - fcp_rsp = (struct fcp_rsp_iu *) - &(fsf_req->qtcb->bottom.io.fcp_rsp); - fcp_rsp_info = (unsigned char *) &fcp_rsp[1]; - fcp_sns_info = - zfcp_get_fcp_sns_info_ptr(fcp_rsp); - - rec->rsp_validity = fcp_rsp->validity.value; - rec->rsp_scsi_status = fcp_rsp->scsi_status; - rec->rsp_resid = fcp_rsp->fcp_resid; - if (fcp_rsp->validity.bits.fcp_rsp_len_valid) - rec->rsp_code = *(fcp_rsp_info + 3); - if (fcp_rsp->validity.bits.fcp_sns_len_valid) { - buflen = min((int)fcp_rsp->fcp_sns_len, - ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO); + fcp_rsp = (struct fcp_resp_with_ext *) + &(fsf_req->qtcb->bottom.io.fcp_rsp); + fcp_rsp_info = (struct fcp_resp_rsp_info *) + &fcp_rsp[1]; + fcp_sns_info = (char *) &fcp_rsp[1]; + if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) + fcp_sns_info += fcp_rsp->ext.fr_sns_len; + + rec->rsp_validity = fcp_rsp->resp.fr_flags; + rec->rsp_scsi_status = fcp_rsp->resp.fr_status; + rec->rsp_resid = fcp_rsp->ext.fr_resid; + if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) + rec->rsp_code = fcp_rsp_info->rsp_code; + if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) { + buflen = min(fcp_rsp->ext.fr_sns_len, + (u32)ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO); rec->sns_info_len = buflen; memcpy(rec->sns_info, fcp_sns_info, min(buflen, @@ -1067,6 +1057,8 @@ err_out: */ void zfcp_dbf_adapter_unregister(struct zfcp_dbf *dbf) { + if (!dbf) + return; debug_unregister(dbf->scsi); debug_unregister(dbf->san); debug_unregister(dbf->hba); diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 6b1461e8f847..8b7fd9a1033e 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -22,6 +22,7 @@ #ifndef ZFCP_DBF_H #define ZFCP_DBF_H +#include <scsi/fc/fc_fcp.h> #include "zfcp_ext.h" #include "zfcp_fsf.h" #include "zfcp_def.h" @@ -122,7 +123,6 @@ struct zfcp_dbf_hba_record_response { } unit; struct { u32 d_id; - u8 ls_code; } els; } u; } __attribute__ ((packed)); @@ -166,6 +166,7 @@ struct zfcp_dbf_san_record_ct_request { u8 options; u16 max_res_size; u32 len; + u32 d_id; } __attribute__ ((packed)); struct zfcp_dbf_san_record_ct_response { @@ -179,16 +180,13 @@ struct zfcp_dbf_san_record_ct_response { } __attribute__ ((packed)); struct zfcp_dbf_san_record_els { - u8 ls_code; - u32 len; + u32 d_id; } __attribute__ ((packed)); struct zfcp_dbf_san_record { u8 tag[ZFCP_DBF_TAG_SIZE]; u64 fsf_reqid; u32 fsf_seqno; - u32 s_id; - u32 d_id; union { struct zfcp_dbf_san_record_ct_request ct_req; struct zfcp_dbf_san_record_ct_response ct_resp; @@ -343,7 +341,7 @@ static inline void zfcp_dbf_scsi_devreset(const char *tag, u8 flag, struct zfcp_unit *unit, struct scsi_cmnd *scsi_cmnd) { - zfcp_dbf_scsi(flag == FCP_TARGET_RESET ? "trst" : "lrst", tag, 1, + zfcp_dbf_scsi(flag == FCP_TMF_TGT_RESET ? "trst" : "lrst", tag, 1, unit->port->adapter->dbf, scsi_cmnd, NULL, 0); } diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 7da2fad8f515..e1b5b88e2ddb 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -71,131 +71,6 @@ /* timeout value for "default timer" for fsf requests */ #define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ) -/*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/ - -/* task attribute values in FCP-2 FCP_CMND IU */ -#define SIMPLE_Q 0 -#define HEAD_OF_Q 1 -#define ORDERED_Q 2 -#define ACA_Q 4 -#define UNTAGGED 5 - -/* task management flags in FCP-2 FCP_CMND IU */ -#define FCP_CLEAR_ACA 0x40 -#define FCP_TARGET_RESET 0x20 -#define FCP_LOGICAL_UNIT_RESET 0x10 -#define FCP_CLEAR_TASK_SET 0x04 -#define FCP_ABORT_TASK_SET 0x02 - -#define FCP_CDB_LENGTH 16 - -#define ZFCP_DID_MASK 0x00FFFFFF - -/* FCP(-2) FCP_CMND IU */ -struct fcp_cmnd_iu { - u64 fcp_lun; /* FCP logical unit number */ - u8 crn; /* command reference number */ - u8 reserved0:5; /* reserved */ - u8 task_attribute:3; /* task attribute */ - u8 task_management_flags; /* task management flags */ - u8 add_fcp_cdb_length:6; /* additional FCP_CDB length */ - u8 rddata:1; /* read data */ - u8 wddata:1; /* write data */ - u8 fcp_cdb[FCP_CDB_LENGTH]; -} __attribute__((packed)); - -/* FCP(-2) FCP_RSP IU */ -struct fcp_rsp_iu { - u8 reserved0[10]; - union { - struct { - u8 reserved1:3; - u8 fcp_conf_req:1; - u8 fcp_resid_under:1; - u8 fcp_resid_over:1; - u8 fcp_sns_len_valid:1; - u8 fcp_rsp_len_valid:1; - } bits; - u8 value; - } validity; - u8 scsi_status; - u32 fcp_resid; - u32 fcp_sns_len; - u32 fcp_rsp_len; -} __attribute__((packed)); - - -#define RSP_CODE_GOOD 0 -#define RSP_CODE_LENGTH_MISMATCH 1 -#define RSP_CODE_FIELD_INVALID 2 -#define RSP_CODE_RO_MISMATCH 3 -#define RSP_CODE_TASKMAN_UNSUPP 4 -#define RSP_CODE_TASKMAN_FAILED 5 - -/* see fc-fs */ -#define LS_RSCN 0x61 -#define LS_LOGO 0x05 -#define LS_PLOGI 0x03 - -struct fcp_rscn_head { - u8 command; - u8 page_length; /* always 0x04 */ - u16 payload_len; -} __attribute__((packed)); - -struct fcp_rscn_element { - u8 reserved:2; - u8 event_qual:4; - u8 addr_format:2; - u32 nport_did:24; -} __attribute__((packed)); - -/* see fc-ph */ -struct fcp_logo { - u32 command; - u32 nport_did; - u64 nport_wwpn; -} __attribute__((packed)); - -/* - * FC-FS stuff - */ -#define R_A_TOV 10 /* seconds */ - -#define ZFCP_LS_RLS 0x0f -#define ZFCP_LS_ADISC 0x52 -#define ZFCP_LS_RPS 0x56 -#define ZFCP_LS_RSCN 0x61 -#define ZFCP_LS_RNID 0x78 - -struct zfcp_ls_adisc { - u8 code; - u8 field[3]; - u32 hard_nport_id; - u64 wwpn; - u64 wwnn; - u32 nport_id; -} __attribute__ ((packed)); - -/* - * FC-GS-2 stuff - */ -#define ZFCP_CT_REVISION 0x01 -#define ZFCP_CT_DIRECTORY_SERVICE 0xFC -#define ZFCP_CT_NAME_SERVER 0x02 -#define ZFCP_CT_SYNCHRONOUS 0x00 -#define ZFCP_CT_SCSI_FCP 0x08 -#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09 -#define ZFCP_CT_GID_PN 0x0121 -#define ZFCP_CT_GPN_FT 0x0172 -#define ZFCP_CT_ACCEPT 0x8002 -#define ZFCP_CT_REJECT 0x8001 - -/* - * FC-GS-4 stuff - */ -#define ZFCP_CT_TIMEOUT (3 * R_A_TOV) - /*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/ /* @@ -205,7 +80,6 @@ struct zfcp_ls_adisc { #define ZFCP_COMMON_FLAGS 0xfff00000 /* common status bits */ -#define ZFCP_STATUS_COMMON_REMOVE 0x80000000 #define ZFCP_STATUS_COMMON_RUNNING 0x40000000 #define ZFCP_STATUS_COMMON_ERP_FAILED 0x20000000 #define ZFCP_STATUS_COMMON_UNBLOCKED 0x10000000 @@ -222,21 +96,10 @@ struct zfcp_ls_adisc { #define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100 #define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200 -/* FC-PH/FC-GS well-known address identifiers for generic services */ -#define ZFCP_DID_WKA 0xFFFFF0 - /* remote port status */ #define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001 #define ZFCP_STATUS_PORT_LINK_TEST 0x00000002 -/* well known address (WKA) port status*/ -enum zfcp_wka_status { - ZFCP_WKA_PORT_OFFLINE, - ZFCP_WKA_PORT_CLOSING, - ZFCP_WKA_PORT_OPENING, - ZFCP_WKA_PORT_ONLINE, -}; - /* logical unit status */ #define ZFCP_STATUS_UNIT_SHARED 0x00000004 #define ZFCP_STATUS_UNIT_READONLY 0x00000008 @@ -247,10 +110,7 @@ enum zfcp_wka_status { #define ZFCP_STATUS_FSFREQ_CLEANUP 0x00000010 #define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED 0x00000040 #define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED 0x00000080 -#define ZFCP_STATUS_FSFREQ_ABORTED 0x00000100 #define ZFCP_STATUS_FSFREQ_TMFUNCFAILED 0x00000200 -#define ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP 0x00000400 -#define ZFCP_STATUS_FSFREQ_RETRY 0x00000800 #define ZFCP_STATUS_FSFREQ_DISMISSED 0x00001000 /************************* STRUCTURE DEFINITIONS *****************************/ @@ -265,125 +125,10 @@ struct zfcp_adapter_mempool { mempool_t *scsi_abort; mempool_t *status_read_req; mempool_t *status_read_data; - mempool_t *gid_pn_data; + mempool_t *gid_pn; mempool_t *qtcb_pool; }; -/* - * header for CT_IU - */ -struct ct_hdr { - u8 revision; // 0x01 - u8 in_id[3]; // 0x00 - u8 gs_type; // 0xFC Directory Service - u8 gs_subtype; // 0x02 Name Server - u8 options; // 0x00 single bidirectional exchange - u8 reserved0; - u16 cmd_rsp_code; // 0x0121 GID_PN, or 0x0100 GA_NXT - u16 max_res_size; // <= (4096 - 16) / 4 - u8 reserved1; - u8 reason_code; - u8 reason_code_expl; - u8 vendor_unique; -} __attribute__ ((packed)); - -/* nameserver request CT_IU -- for requests where - * a port name is required */ -struct ct_iu_gid_pn_req { - struct ct_hdr header; - u64 wwpn; -} __attribute__ ((packed)); - -/* FS_ACC IU and data unit for GID_PN nameserver request */ -struct ct_iu_gid_pn_resp { - struct ct_hdr header; - u32 d_id; -} __attribute__ ((packed)); - -struct ct_iu_gpn_ft_req { - struct ct_hdr header; - u8 flags; - u8 domain_id_scope; - u8 area_id_scope; - u8 fc4_type; -} __attribute__ ((packed)); - - -/** - * struct zfcp_send_ct - used to pass parameters to function zfcp_fsf_send_ct - * @wka_port: port where the request is sent to - * @req: scatter-gather list for request - * @resp: scatter-gather list for response - * @handler: handler function (called for response to the request) - * @handler_data: data passed to handler function - * @completion: completion for synchronization purposes - * @status: used to pass error status to calling function - */ -struct zfcp_send_ct { - struct zfcp_wka_port *wka_port; - struct scatterlist *req; - struct scatterlist *resp; - void (*handler)(unsigned long); - unsigned long handler_data; - struct completion *completion; - int status; -}; - -/* used for name server requests in error recovery */ -struct zfcp_gid_pn_data { - struct zfcp_send_ct ct; - struct scatterlist req; - struct scatterlist resp; - struct ct_iu_gid_pn_req ct_iu_req; - struct ct_iu_gid_pn_resp ct_iu_resp; - struct zfcp_port *port; -}; - -/** - * struct zfcp_send_els - used to pass parameters to function zfcp_fsf_send_els - * @adapter: adapter where request is sent from - * @port: port where ELS is destinated (port reference count has to be increased) - * @d_id: destiniation id of port where request is sent to - * @req: scatter-gather list for request - * @resp: scatter-gather list for response - * @handler: handler function (called for response to the request) - * @handler_data: data passed to handler function - * @completion: completion for synchronization purposes - * @ls_code: hex code of ELS command - * @status: used to pass error status to calling function - */ -struct zfcp_send_els { - struct zfcp_adapter *adapter; - struct zfcp_port *port; - u32 d_id; - struct scatterlist *req; - struct scatterlist *resp; - void (*handler)(unsigned long); - unsigned long handler_data; - struct completion *completion; - int ls_code; - int status; -}; - -struct zfcp_wka_port { - struct zfcp_adapter *adapter; - wait_queue_head_t completion_wq; - enum zfcp_wka_status status; - atomic_t refcount; - u32 d_id; - u32 handle; - struct mutex mutex; - struct delayed_work work; -}; - -struct zfcp_wka_ports { - struct zfcp_wka_port ms; /* management service */ - struct zfcp_wka_port ts; /* time service */ - struct zfcp_wka_port ds; /* directory service */ - struct zfcp_wka_port as; /* alias service */ - struct zfcp_wka_port ks; /* key distribution service */ -}; - struct zfcp_qdio_queue { struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q]; u8 first; /* index of next free bfr in queue */ @@ -446,9 +191,7 @@ struct zfcp_qdio { }; struct zfcp_adapter { - atomic_t refcount; /* reference count */ - wait_queue_head_t remove_wq; /* can be used to wait for - refcount drop to zero */ + struct kref ref; u64 peer_wwnn; /* P2P peer WWNN */ u64 peer_wwpn; /* P2P peer WWPN */ u32 peer_d_id; /* P2P peer D_ID */ @@ -461,7 +204,8 @@ struct zfcp_adapter { u32 hardware_version; /* of FCP channel */ u16 timer_ticks; /* time int for a tick */ struct Scsi_Host *scsi_host; /* Pointer to mid-layer */ - struct list_head port_list_head; /* remote port list */ + struct list_head port_list; /* remote port list */ + rwlock_t port_list_lock; /* port list lock */ unsigned long req_no; /* unique FSF req number */ struct list_head *req_list; /* list of pending reqs */ spinlock_t req_list_lock; /* request list lock */ @@ -485,7 +229,7 @@ struct zfcp_adapter { u32 erp_low_mem_count; /* nr of erp actions waiting for memory */ struct task_struct *erp_thread; - struct zfcp_wka_ports *gs; /* generic services */ + struct zfcp_fc_wka_ports *gs; /* generic services */ struct zfcp_dbf *dbf; /* debug traces */ struct zfcp_adapter_mempool pool; /* Adapter memory pools */ struct fc_host_statistics *fc_stats; @@ -500,11 +244,9 @@ struct zfcp_port { struct device sysfs_device; /* sysfs device */ struct fc_rport *rport; /* rport of fc transport class */ struct list_head list; /* list of remote ports */ - atomic_t refcount; /* reference count */ - wait_queue_head_t remove_wq; /* can be used to wait for - refcount drop to zero */ struct zfcp_adapter *adapter; /* adapter used to access port */ - struct list_head unit_list_head; /* head of logical unit list */ + struct list_head unit_list; /* head of logical unit list */ + rwlock_t unit_list_lock; /* unit list lock */ atomic_t status; /* status of this remote port */ u64 wwnn; /* WWNN if known */ u64 wwpn; /* WWPN */ @@ -523,9 +265,6 @@ struct zfcp_port { struct zfcp_unit { struct device sysfs_device; /* sysfs device */ struct list_head list; /* list of logical units */ - atomic_t refcount; /* reference count */ - wait_queue_head_t remove_wq; /* can be used to wait for - refcount drop to zero */ struct zfcp_port *port; /* remote port of unit */ atomic_t status; /* status of this logical unit */ u64 fcp_lun; /* own FCP_LUN */ @@ -601,14 +340,11 @@ struct zfcp_fsf_req { struct zfcp_data { struct scsi_host_template scsi_host_template; struct scsi_transport_template *scsi_transport_template; - rwlock_t config_lock; /* serialises changes - to adapter/port/unit - lists */ - struct mutex config_mutex; struct kmem_cache *gpn_ft_cache; struct kmem_cache *qtcb_cache; struct kmem_cache *sr_buffer_cache; struct kmem_cache *gid_pn_cache; + struct kmem_cache *adisc_cache; }; /********************** ZFCP SPECIFIC DEFINES ********************************/ @@ -657,47 +393,4 @@ zfcp_reqlist_find_safe(struct zfcp_adapter *adapter, struct zfcp_fsf_req *req) return NULL; } -/* - * functions needed for reference/usage counting - */ - -static inline void -zfcp_unit_get(struct zfcp_unit *unit) -{ - atomic_inc(&unit->refcount); -} - -static inline void -zfcp_unit_put(struct zfcp_unit *unit) -{ - if (atomic_dec_return(&unit->refcount) == 0) - wake_up(&unit->remove_wq); -} - -static inline void -zfcp_port_get(struct zfcp_port *port) -{ - atomic_inc(&port->refcount); -} - -static inline void -zfcp_port_put(struct zfcp_port *port) -{ - if (atomic_dec_return(&port->refcount) == 0) - wake_up(&port->remove_wq); -} - -static inline void -zfcp_adapter_get(struct zfcp_adapter *adapter) -{ - atomic_inc(&adapter->refcount); -} - -static inline void -zfcp_adapter_put(struct zfcp_adapter *adapter) -{ - if (atomic_dec_return(&adapter->refcount) == 0) - wake_up(&adapter->remove_wq); -} - #endif /* ZFCP_DEF_H */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index f73e2180f333..b51a11a82e63 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -99,9 +99,12 @@ static void zfcp_erp_action_dismiss_port(struct zfcp_port *port) if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE) zfcp_erp_action_dismiss(&port->erp_action); - else - list_for_each_entry(unit, &port->unit_list_head, list) - zfcp_erp_action_dismiss_unit(unit); + else { + read_lock(&port->unit_list_lock); + list_for_each_entry(unit, &port->unit_list, list) + zfcp_erp_action_dismiss_unit(unit); + read_unlock(&port->unit_list_lock); + } } static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) @@ -110,9 +113,12 @@ static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE) zfcp_erp_action_dismiss(&adapter->erp_action); - else - list_for_each_entry(port, &adapter->port_list_head, list) + else { + read_lock(&adapter->port_list_lock); + list_for_each_entry(port, &adapter->port_list, list) zfcp_erp_action_dismiss_port(port); + read_unlock(&adapter->port_list_lock); + } } static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter, @@ -168,7 +174,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, switch (need) { case ZFCP_ERP_ACTION_REOPEN_UNIT: - zfcp_unit_get(unit); + if (!get_device(&unit->sysfs_device)) + return NULL; atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status); erp_action = &unit->erp_action; if (!(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_RUNNING)) @@ -177,7 +184,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, case ZFCP_ERP_ACTION_REOPEN_PORT: case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: - zfcp_port_get(port); + if (!get_device(&port->sysfs_device)) + return NULL; zfcp_erp_action_dismiss_port(port); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status); erp_action = &port->erp_action; @@ -186,7 +194,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, break; case ZFCP_ERP_ACTION_REOPEN_ADAPTER: - zfcp_adapter_get(adapter); + kref_get(&adapter->ref); zfcp_erp_action_dismiss_adapter(adapter); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status); erp_action = &adapter->erp_action; @@ -264,11 +272,16 @@ void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, { unsigned long flags; - read_lock_irqsave(&zfcp_data.config_lock, flags); - write_lock(&adapter->erp_lock); - _zfcp_erp_adapter_reopen(adapter, clear, id, ref); - write_unlock(&adapter->erp_lock); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + zfcp_erp_adapter_block(adapter, clear); + zfcp_scsi_schedule_rports_block(adapter); + + write_lock_irqsave(&adapter->erp_lock, flags); + if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + zfcp_erp_adapter_failed(adapter, "erareo1", NULL); + else + zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter, + NULL, NULL, id, ref); + write_unlock_irqrestore(&adapter->erp_lock, flags); } /** @@ -345,11 +358,9 @@ void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id, unsigned long flags; struct zfcp_adapter *adapter = port->adapter; - read_lock_irqsave(&zfcp_data.config_lock, flags); - write_lock(&adapter->erp_lock); + write_lock_irqsave(&adapter->erp_lock, flags); _zfcp_erp_port_forced_reopen(port, clear, id, ref); - write_unlock(&adapter->erp_lock); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + write_unlock_irqrestore(&adapter->erp_lock, flags); } static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, @@ -377,15 +388,13 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, */ int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, void *ref) { - unsigned long flags; int retval; + unsigned long flags; struct zfcp_adapter *adapter = port->adapter; - read_lock_irqsave(&zfcp_data.config_lock, flags); - write_lock(&adapter->erp_lock); + write_lock_irqsave(&adapter->erp_lock, flags); retval = _zfcp_erp_port_reopen(port, clear, id, ref); - write_unlock(&adapter->erp_lock); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + write_unlock_irqrestore(&adapter->erp_lock, flags); return retval; } @@ -424,11 +433,9 @@ void zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, char *id, struct zfcp_port *port = unit->port; struct zfcp_adapter *adapter = port->adapter; - read_lock_irqsave(&zfcp_data.config_lock, flags); - write_lock(&adapter->erp_lock); + write_lock_irqsave(&adapter->erp_lock, flags); _zfcp_erp_unit_reopen(unit, clear, id, ref); - write_unlock(&adapter->erp_lock); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + write_unlock_irqrestore(&adapter->erp_lock, flags); } static int status_change_set(unsigned long mask, atomic_t *status) @@ -540,8 +547,10 @@ static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter, { struct zfcp_port *port; - list_for_each_entry(port, &adapter->port_list_head, list) + read_lock(&adapter->port_list_lock); + list_for_each_entry(port, &adapter->port_list, list) _zfcp_erp_port_reopen(port, clear, id, ref); + read_unlock(&adapter->port_list_lock); } static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear, @@ -549,8 +558,10 @@ static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear, { struct zfcp_unit *unit; - list_for_each_entry(unit, &port->unit_list_head, list) + read_lock(&port->unit_list_lock); + list_for_each_entry(unit, &port->unit_list, list) _zfcp_erp_unit_reopen(unit, clear, id, ref); + read_unlock(&port->unit_list_lock); } static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act) @@ -590,16 +601,14 @@ static void zfcp_erp_wakeup(struct zfcp_adapter *adapter) { unsigned long flags; - read_lock_irqsave(&zfcp_data.config_lock, flags); - read_lock(&adapter->erp_lock); + read_lock_irqsave(&adapter->erp_lock, flags); if (list_empty(&adapter->erp_ready_head) && list_empty(&adapter->erp_running_head)) { atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status); wake_up(&adapter->erp_done_wqh); } - read_unlock(&adapter->erp_lock); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + read_unlock_irqrestore(&adapter->erp_lock, flags); } static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *act) @@ -1170,28 +1179,28 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) switch (act->action) { case ZFCP_ERP_ACTION_REOPEN_UNIT: if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) { - zfcp_unit_get(unit); + get_device(&unit->sysfs_device); if (scsi_queue_work(unit->port->adapter->scsi_host, &unit->scsi_work) <= 0) - zfcp_unit_put(unit); + put_device(&unit->sysfs_device); } - zfcp_unit_put(unit); + put_device(&unit->sysfs_device); break; case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: case ZFCP_ERP_ACTION_REOPEN_PORT: if (result == ZFCP_ERP_SUCCEEDED) zfcp_scsi_schedule_rport_register(port); - zfcp_port_put(port); + put_device(&port->sysfs_device); break; case ZFCP_ERP_ACTION_REOPEN_ADAPTER: if (result == ZFCP_ERP_SUCCEEDED) { register_service_level(&adapter->service_level); - schedule_work(&adapter->scan_work); + queue_work(adapter->work_queue, &adapter->scan_work); } else unregister_service_level(&adapter->service_level); - zfcp_adapter_put(adapter); + kref_put(&adapter->ref, zfcp_adapter_release); break; } } @@ -1214,12 +1223,12 @@ static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action) static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action) { int retval; - struct zfcp_adapter *adapter = erp_action->adapter; unsigned long flags; + struct zfcp_adapter *adapter = erp_action->adapter; - read_lock_irqsave(&zfcp_data.config_lock, flags); - write_lock(&adapter->erp_lock); + kref_get(&adapter->ref); + write_lock_irqsave(&adapter->erp_lock, flags); zfcp_erp_strategy_check_fsfreq(erp_action); if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) { @@ -1231,11 +1240,9 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action) zfcp_erp_action_to_running(erp_action); /* no lock to allow for blocking operations */ - write_unlock(&adapter->erp_lock); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + write_unlock_irqrestore(&adapter->erp_lock, flags); retval = zfcp_erp_strategy_do_action(erp_action); - read_lock_irqsave(&zfcp_data.config_lock, flags); - write_lock(&adapter->erp_lock); + write_lock_irqsave(&adapter->erp_lock, flags); if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) retval = ZFCP_ERP_CONTINUES; @@ -1273,12 +1280,12 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action) zfcp_erp_strategy_followup_failed(erp_action); unlock: - write_unlock(&adapter->erp_lock); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + write_unlock_irqrestore(&adapter->erp_lock, flags); if (retval != ZFCP_ERP_CONTINUES) zfcp_erp_action_cleanup(erp_action, retval); + kref_put(&adapter->ref, zfcp_adapter_release); return retval; } @@ -1415,6 +1422,7 @@ void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, char *id, void *ref, u32 mask, int set_or_clear) { struct zfcp_port *port; + unsigned long flags; u32 common_mask = mask & ZFCP_COMMON_FLAGS; if (set_or_clear == ZFCP_SET) { @@ -1429,10 +1437,13 @@ void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, char *id, atomic_set(&adapter->erp_counter, 0); } - if (common_mask) - list_for_each_entry(port, &adapter->port_list_head, list) + if (common_mask) { + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) zfcp_erp_modify_port_status(port, id, ref, common_mask, set_or_clear); + read_unlock_irqrestore(&adapter->port_list_lock, flags); + } } /** @@ -1449,6 +1460,7 @@ void zfcp_erp_modify_port_status(struct zfcp_port *port, char *id, void *ref, u32 mask, int set_or_clear) { struct zfcp_unit *unit; + unsigned long flags; u32 common_mask = mask & ZFCP_COMMON_FLAGS; if (set_or_clear == ZFCP_SET) { @@ -1463,10 +1475,13 @@ void zfcp_erp_modify_port_status(struct zfcp_port *port, char *id, void *ref, atomic_set(&port->erp_counter, 0); } - if (common_mask) - list_for_each_entry(unit, &port->unit_list_head, list) + if (common_mask) { + read_lock_irqsave(&port->unit_list_lock, flags); + list_for_each_entry(unit, &port->unit_list, list) zfcp_erp_modify_unit_status(unit, id, ref, common_mask, set_or_clear); + read_unlock_irqrestore(&port->unit_list_lock, flags); + } } /** @@ -1502,12 +1517,8 @@ void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, char *id, void *ref, */ void zfcp_erp_port_boxed(struct zfcp_port *port, char *id, void *ref) { - unsigned long flags; - - read_lock_irqsave(&zfcp_data.config_lock, flags); zfcp_erp_modify_port_status(port, id, ref, ZFCP_STATUS_COMMON_ACCESS_BOXED, ZFCP_SET); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref); } @@ -1535,13 +1546,9 @@ void zfcp_erp_unit_boxed(struct zfcp_unit *unit, char *id, void *ref) */ void zfcp_erp_port_access_denied(struct zfcp_port *port, char *id, void *ref) { - unsigned long flags; - - read_lock_irqsave(&zfcp_data.config_lock, flags); zfcp_erp_modify_port_status(port, id, ref, ZFCP_STATUS_COMMON_ERP_FAILED | ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); } /** @@ -1574,12 +1581,15 @@ static void zfcp_erp_port_access_changed(struct zfcp_port *port, char *id, void *ref) { struct zfcp_unit *unit; + unsigned long flags; int status = atomic_read(&port->status); if (!(status & (ZFCP_STATUS_COMMON_ACCESS_DENIED | ZFCP_STATUS_COMMON_ACCESS_BOXED))) { - list_for_each_entry(unit, &port->unit_list_head, list) + read_lock_irqsave(&port->unit_list_lock, flags); + list_for_each_entry(unit, &port->unit_list, list) zfcp_erp_unit_access_changed(unit, id, ref); + read_unlock_irqrestore(&port->unit_list_lock, flags); return; } @@ -1595,14 +1605,14 @@ static void zfcp_erp_port_access_changed(struct zfcp_port *port, char *id, void zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter, char *id, void *ref) { - struct zfcp_port *port; unsigned long flags; + struct zfcp_port *port; if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) return; - read_lock_irqsave(&zfcp_data.config_lock, flags); - list_for_each_entry(port, &adapter->port_list_head, list) + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) zfcp_erp_port_access_changed(port, id, ref); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + read_unlock_irqrestore(&adapter->port_list_lock, flags); } diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index b3f28deb4505..03dec832b465 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -9,26 +9,31 @@ #ifndef ZFCP_EXT_H #define ZFCP_EXT_H +#include <linux/types.h> +#include <scsi/fc/fc_els.h> #include "zfcp_def.h" +#include "zfcp_fc.h" /* zfcp_aux.c */ extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, u64); extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, u64); -extern int zfcp_adapter_enqueue(struct ccw_device *); -extern void zfcp_adapter_dequeue(struct zfcp_adapter *); +extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *); extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32, u32); -extern void zfcp_port_dequeue(struct zfcp_port *); extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, u64); -extern void zfcp_unit_dequeue(struct zfcp_unit *); extern int zfcp_reqlist_isempty(struct zfcp_adapter *); extern void zfcp_sg_free_table(struct scatterlist *, int); extern int zfcp_sg_setup_table(struct scatterlist *, int); +extern void zfcp_device_unregister(struct device *, + const struct attribute_group *); +extern void zfcp_adapter_release(struct kref *); +extern void zfcp_adapter_unregister(struct zfcp_adapter *); /* zfcp_ccw.c */ -extern int zfcp_ccw_register(void); extern int zfcp_ccw_priv_sch(struct zfcp_adapter *); extern struct ccw_driver zfcp_ccw_driver; +extern struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *); +extern void zfcp_ccw_adapter_put(struct zfcp_adapter *); /* zfcp_cfdc.c */ extern struct miscdevice zfcp_cfdc_misc; @@ -51,7 +56,7 @@ extern void _zfcp_dbf_hba_fsf_unsol(const char *, int level, struct zfcp_dbf *, struct fsf_status_read_buffer *); extern void zfcp_dbf_hba_qdio(struct zfcp_dbf *, unsigned int, int, int); extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *); -extern void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *); +extern void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *, u32); extern void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *); extern void zfcp_dbf_san_els_request(struct zfcp_fsf_req *); extern void zfcp_dbf_san_els_response(struct zfcp_fsf_req *); @@ -92,24 +97,22 @@ extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, char *, extern void zfcp_erp_timeout_handler(unsigned long); /* zfcp_fc.c */ -extern int zfcp_fc_scan_ports(struct zfcp_adapter *); -extern void _zfcp_fc_scan_ports_later(struct work_struct *); +extern void zfcp_fc_scan_ports(struct work_struct *); extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *); extern void zfcp_fc_port_did_lookup(struct work_struct *); extern void zfcp_fc_trigger_did_lookup(struct zfcp_port *); -extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *); +extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fc_els_flogi *); extern void zfcp_fc_test_link(struct zfcp_port *); extern void zfcp_fc_link_test_work(struct work_struct *); -extern void zfcp_fc_wka_ports_force_offline(struct zfcp_wka_ports *); +extern void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *); extern int zfcp_fc_gs_setup(struct zfcp_adapter *); extern void zfcp_fc_gs_destroy(struct zfcp_adapter *); -extern int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *); -extern int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *); +extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *); /* zfcp_fsf.c */ extern int zfcp_fsf_open_port(struct zfcp_erp_action *); -extern int zfcp_fsf_open_wka_port(struct zfcp_wka_port *); -extern int zfcp_fsf_close_wka_port(struct zfcp_wka_port *); +extern int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *); +extern int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *); extern int zfcp_fsf_close_port(struct zfcp_erp_action *); extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *); extern int zfcp_fsf_open_unit(struct zfcp_erp_action *); @@ -125,8 +128,10 @@ extern struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *, extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *); extern int zfcp_fsf_status_read(struct zfcp_qdio *); extern int zfcp_status_read_refill(struct zfcp_adapter *adapter); -extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *); -extern int zfcp_fsf_send_els(struct zfcp_send_els *); +extern int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *, struct zfcp_fsf_ct_els *, + mempool_t *); +extern int zfcp_fsf_send_els(struct zfcp_adapter *, u32, + struct zfcp_fsf_ct_els *); extern int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *, struct scsi_cmnd *); extern void zfcp_fsf_req_free(struct zfcp_fsf_req *); @@ -153,7 +158,6 @@ extern void zfcp_qdio_close(struct zfcp_qdio *); extern struct zfcp_data zfcp_data; extern int zfcp_adapter_scsi_register(struct zfcp_adapter *); extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *); -extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *); extern struct fc_function_template zfcp_transport_functions; extern void zfcp_scsi_rport_work(struct work_struct *); extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *); diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index df23bcead23d..ac5e3b7a3576 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -9,73 +9,38 @@ #define KMSG_COMPONENT "zfcp" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/types.h> +#include <scsi/fc/fc_els.h> +#include <scsi/libfc.h> #include "zfcp_ext.h" +#include "zfcp_fc.h" -enum rscn_address_format { - RSCN_PORT_ADDRESS = 0x0, - RSCN_AREA_ADDRESS = 0x1, - RSCN_DOMAIN_ADDRESS = 0x2, - RSCN_FABRIC_ADDRESS = 0x3, +static u32 zfcp_fc_rscn_range_mask[] = { + [ELS_ADDR_FMT_PORT] = 0xFFFFFF, + [ELS_ADDR_FMT_AREA] = 0xFFFF00, + [ELS_ADDR_FMT_DOM] = 0xFF0000, + [ELS_ADDR_FMT_FAB] = 0x000000, }; -static u32 rscn_range_mask[] = { - [RSCN_PORT_ADDRESS] = 0xFFFFFF, - [RSCN_AREA_ADDRESS] = 0xFFFF00, - [RSCN_DOMAIN_ADDRESS] = 0xFF0000, - [RSCN_FABRIC_ADDRESS] = 0x000000, -}; - -struct gpn_ft_resp_acc { - u8 control; - u8 port_id[3]; - u8 reserved[4]; - u64 wwpn; -} __attribute__ ((packed)); - -#define ZFCP_CT_SIZE_ONE_PAGE (PAGE_SIZE - sizeof(struct ct_hdr)) -#define ZFCP_GPN_FT_ENTRIES (ZFCP_CT_SIZE_ONE_PAGE \ - / sizeof(struct gpn_ft_resp_acc)) -#define ZFCP_GPN_FT_BUFFERS 4 -#define ZFCP_GPN_FT_MAX_SIZE (ZFCP_GPN_FT_BUFFERS * PAGE_SIZE \ - - sizeof(struct ct_hdr)) -#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1) - -struct ct_iu_gpn_ft_resp { - struct ct_hdr header; - struct gpn_ft_resp_acc accept[ZFCP_GPN_FT_ENTRIES]; -} __attribute__ ((packed)); - -struct zfcp_gpn_ft { - struct zfcp_send_ct ct; - struct scatterlist sg_req; - struct scatterlist sg_resp[ZFCP_GPN_FT_BUFFERS]; -}; - -struct zfcp_fc_ns_handler_data { - struct completion done; - void (*handler)(unsigned long); - unsigned long handler_data; -}; - -static int zfcp_fc_wka_port_get(struct zfcp_wka_port *wka_port) +static int zfcp_fc_wka_port_get(struct zfcp_fc_wka_port *wka_port) { if (mutex_lock_interruptible(&wka_port->mutex)) return -ERESTARTSYS; - if (wka_port->status == ZFCP_WKA_PORT_OFFLINE || - wka_port->status == ZFCP_WKA_PORT_CLOSING) { - wka_port->status = ZFCP_WKA_PORT_OPENING; + if (wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE || + wka_port->status == ZFCP_FC_WKA_PORT_CLOSING) { + wka_port->status = ZFCP_FC_WKA_PORT_OPENING; if (zfcp_fsf_open_wka_port(wka_port)) - wka_port->status = ZFCP_WKA_PORT_OFFLINE; + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; } mutex_unlock(&wka_port->mutex); wait_event(wka_port->completion_wq, - wka_port->status == ZFCP_WKA_PORT_ONLINE || - wka_port->status == ZFCP_WKA_PORT_OFFLINE); + wka_port->status == ZFCP_FC_WKA_PORT_ONLINE || + wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE); - if (wka_port->status == ZFCP_WKA_PORT_ONLINE) { + if (wka_port->status == ZFCP_FC_WKA_PORT_ONLINE) { atomic_inc(&wka_port->refcount); return 0; } @@ -85,24 +50,24 @@ static int zfcp_fc_wka_port_get(struct zfcp_wka_port *wka_port) static void zfcp_fc_wka_port_offline(struct work_struct *work) { struct delayed_work *dw = to_delayed_work(work); - struct zfcp_wka_port *wka_port = - container_of(dw, struct zfcp_wka_port, work); + struct zfcp_fc_wka_port *wka_port = + container_of(dw, struct zfcp_fc_wka_port, work); mutex_lock(&wka_port->mutex); if ((atomic_read(&wka_port->refcount) != 0) || - (wka_port->status != ZFCP_WKA_PORT_ONLINE)) + (wka_port->status != ZFCP_FC_WKA_PORT_ONLINE)) goto out; - wka_port->status = ZFCP_WKA_PORT_CLOSING; + wka_port->status = ZFCP_FC_WKA_PORT_CLOSING; if (zfcp_fsf_close_wka_port(wka_port)) { - wka_port->status = ZFCP_WKA_PORT_OFFLINE; + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; wake_up(&wka_port->completion_wq); } out: mutex_unlock(&wka_port->mutex); } -static void zfcp_fc_wka_port_put(struct zfcp_wka_port *wka_port) +static void zfcp_fc_wka_port_put(struct zfcp_fc_wka_port *wka_port) { if (atomic_dec_return(&wka_port->refcount) != 0) return; @@ -110,7 +75,7 @@ static void zfcp_fc_wka_port_put(struct zfcp_wka_port *wka_port) schedule_delayed_work(&wka_port->work, HZ / 100); } -static void zfcp_fc_wka_port_init(struct zfcp_wka_port *wka_port, u32 d_id, +static void zfcp_fc_wka_port_init(struct zfcp_fc_wka_port *wka_port, u32 d_id, struct zfcp_adapter *adapter) { init_waitqueue_head(&wka_port->completion_wq); @@ -118,107 +83,107 @@ static void zfcp_fc_wka_port_init(struct zfcp_wka_port *wka_port, u32 d_id, wka_port->adapter = adapter; wka_port->d_id = d_id; - wka_port->status = ZFCP_WKA_PORT_OFFLINE; + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; atomic_set(&wka_port->refcount, 0); mutex_init(&wka_port->mutex); INIT_DELAYED_WORK(&wka_port->work, zfcp_fc_wka_port_offline); } -static void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka) +static void zfcp_fc_wka_port_force_offline(struct zfcp_fc_wka_port *wka) { cancel_delayed_work_sync(&wka->work); mutex_lock(&wka->mutex); - wka->status = ZFCP_WKA_PORT_OFFLINE; + wka->status = ZFCP_FC_WKA_PORT_OFFLINE; mutex_unlock(&wka->mutex); } -void zfcp_fc_wka_ports_force_offline(struct zfcp_wka_ports *gs) +void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *gs) { + if (!gs) + return; zfcp_fc_wka_port_force_offline(&gs->ms); zfcp_fc_wka_port_force_offline(&gs->ts); zfcp_fc_wka_port_force_offline(&gs->ds); zfcp_fc_wka_port_force_offline(&gs->as); - zfcp_fc_wka_port_force_offline(&gs->ks); } static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range, - struct fcp_rscn_element *elem) + struct fc_els_rscn_page *page) { unsigned long flags; + struct zfcp_adapter *adapter = fsf_req->adapter; struct zfcp_port *port; - read_lock_irqsave(&zfcp_data.config_lock, flags); - list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) { - if ((port->d_id & range) == (elem->nport_did & range)) + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) { + if ((port->d_id & range) == (ntoh24(page->rscn_fid) & range)) zfcp_fc_test_link(port); if (!port->d_id) zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, "fcrscn1", NULL); } - - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + read_unlock_irqrestore(&adapter->port_list_lock, flags); } static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req) { struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data; - struct fcp_rscn_head *fcp_rscn_head; - struct fcp_rscn_element *fcp_rscn_element; + struct fc_els_rscn *head; + struct fc_els_rscn_page *page; u16 i; u16 no_entries; - u32 range_mask; + unsigned int afmt; - fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload.data; - fcp_rscn_element = (struct fcp_rscn_element *) fcp_rscn_head; + head = (struct fc_els_rscn *) status_buffer->payload.data; + page = (struct fc_els_rscn_page *) head; /* see FC-FS */ - no_entries = fcp_rscn_head->payload_len / - sizeof(struct fcp_rscn_element); + no_entries = head->rscn_plen / sizeof(struct fc_els_rscn_page); for (i = 1; i < no_entries; i++) { /* skip head and start with 1st element */ - fcp_rscn_element++; - range_mask = rscn_range_mask[fcp_rscn_element->addr_format]; - _zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element); + page++; + afmt = page->rscn_page_flags & ELS_RSCN_ADDR_FMT_MASK; + _zfcp_fc_incoming_rscn(fsf_req, zfcp_fc_rscn_range_mask[afmt], + page); } - schedule_work(&fsf_req->adapter->scan_work); + queue_work(fsf_req->adapter->work_queue, &fsf_req->adapter->scan_work); } static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, u64 wwpn) { + unsigned long flags; struct zfcp_adapter *adapter = req->adapter; struct zfcp_port *port; - unsigned long flags; - read_lock_irqsave(&zfcp_data.config_lock, flags); - list_for_each_entry(port, &adapter->port_list_head, list) - if (port->wwpn == wwpn) + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) + if (port->wwpn == wwpn) { + zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req); break; - read_unlock_irqrestore(&zfcp_data.config_lock, flags); - - if (port && (port->wwpn == wwpn)) - zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req); + } + read_unlock_irqrestore(&adapter->port_list_lock, flags); } static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req) { - struct fsf_status_read_buffer *status_buffer = - (struct fsf_status_read_buffer *)req->data; - struct fsf_plogi *els_plogi = - (struct fsf_plogi *) status_buffer->payload.data; + struct fsf_status_read_buffer *status_buffer; + struct fc_els_flogi *plogi; - zfcp_fc_incoming_wwpn(req, els_plogi->serv_param.wwpn); + status_buffer = (struct fsf_status_read_buffer *) req->data; + plogi = (struct fc_els_flogi *) status_buffer->payload.data; + zfcp_fc_incoming_wwpn(req, plogi->fl_wwpn); } static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req) { struct fsf_status_read_buffer *status_buffer = (struct fsf_status_read_buffer *)req->data; - struct fcp_logo *els_logo = - (struct fcp_logo *) status_buffer->payload.data; + struct fc_els_logo *logo = + (struct fc_els_logo *) status_buffer->payload.data; - zfcp_fc_incoming_wwpn(req, els_logo->nport_wwpn); + zfcp_fc_incoming_wwpn(req, logo->fl_n_port_wwn); } /** @@ -232,79 +197,72 @@ void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req) unsigned int els_type = status_buffer->payload.data[0]; zfcp_dbf_san_incoming_els(fsf_req); - if (els_type == LS_PLOGI) + if (els_type == ELS_PLOGI) zfcp_fc_incoming_plogi(fsf_req); - else if (els_type == LS_LOGO) + else if (els_type == ELS_LOGO) zfcp_fc_incoming_logo(fsf_req); - else if (els_type == LS_RSCN) + else if (els_type == ELS_RSCN) zfcp_fc_incoming_rscn(fsf_req); } -static void zfcp_fc_ns_handler(unsigned long data) +static void zfcp_fc_ns_gid_pn_eval(void *data) { - struct zfcp_fc_ns_handler_data *compl_rec = - (struct zfcp_fc_ns_handler_data *) data; - - if (compl_rec->handler) - compl_rec->handler(compl_rec->handler_data); - - complete(&compl_rec->done); -} - -static void zfcp_fc_ns_gid_pn_eval(unsigned long data) -{ - struct zfcp_gid_pn_data *gid_pn = (struct zfcp_gid_pn_data *) data; - struct zfcp_send_ct *ct = &gid_pn->ct; - struct ct_iu_gid_pn_req *ct_iu_req = sg_virt(ct->req); - struct ct_iu_gid_pn_resp *ct_iu_resp = sg_virt(ct->resp); + struct zfcp_fc_gid_pn *gid_pn = data; + struct zfcp_fsf_ct_els *ct = &gid_pn->ct; + struct zfcp_fc_gid_pn_req *gid_pn_req = sg_virt(ct->req); + struct zfcp_fc_gid_pn_resp *gid_pn_resp = sg_virt(ct->resp); struct zfcp_port *port = gid_pn->port; if (ct->status) return; - if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) + if (gid_pn_resp->ct_hdr.ct_cmd != FC_FS_ACC) return; /* paranoia */ - if (ct_iu_req->wwpn != port->wwpn) + if (gid_pn_req->gid_pn.fn_wwpn != port->wwpn) return; /* looks like a valid d_id */ - port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK; + port->d_id = ntoh24(gid_pn_resp->gid_pn.fp_fid); +} + +static void zfcp_fc_complete(void *data) +{ + complete(data); } static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port, - struct zfcp_gid_pn_data *gid_pn) + struct zfcp_fc_gid_pn *gid_pn) { struct zfcp_adapter *adapter = port->adapter; - struct zfcp_fc_ns_handler_data compl_rec; + DECLARE_COMPLETION_ONSTACK(completion); int ret; /* setup parameters for send generic command */ gid_pn->port = port; - gid_pn->ct.wka_port = &adapter->gs->ds; - gid_pn->ct.handler = zfcp_fc_ns_handler; - gid_pn->ct.handler_data = (unsigned long) &compl_rec; - gid_pn->ct.req = &gid_pn->req; - gid_pn->ct.resp = &gid_pn->resp; - sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req, - sizeof(struct ct_iu_gid_pn_req)); - sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp, - sizeof(struct ct_iu_gid_pn_resp)); + gid_pn->ct.handler = zfcp_fc_complete; + gid_pn->ct.handler_data = &completion; + gid_pn->ct.req = &gid_pn->sg_req; + gid_pn->ct.resp = &gid_pn->sg_resp; + sg_init_one(&gid_pn->sg_req, &gid_pn->gid_pn_req, + sizeof(struct zfcp_fc_gid_pn_req)); + sg_init_one(&gid_pn->sg_resp, &gid_pn->gid_pn_resp, + sizeof(struct zfcp_fc_gid_pn_resp)); /* setup nameserver request */ - gid_pn->ct_iu_req.header.revision = ZFCP_CT_REVISION; - gid_pn->ct_iu_req.header.gs_type = ZFCP_CT_DIRECTORY_SERVICE; - gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER; - gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS; - gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN; - gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_SIZE_ONE_PAGE / 4; - gid_pn->ct_iu_req.wwpn = port->wwpn; - - init_completion(&compl_rec.done); - compl_rec.handler = zfcp_fc_ns_gid_pn_eval; - compl_rec.handler_data = (unsigned long) gid_pn; - ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.gid_pn_req); - if (!ret) - wait_for_completion(&compl_rec.done); + gid_pn->gid_pn_req.ct_hdr.ct_rev = FC_CT_REV; + gid_pn->gid_pn_req.ct_hdr.ct_fs_type = FC_FST_DIR; + gid_pn->gid_pn_req.ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE; + gid_pn->gid_pn_req.ct_hdr.ct_options = 0; + gid_pn->gid_pn_req.ct_hdr.ct_cmd = FC_NS_GID_PN; + gid_pn->gid_pn_req.ct_hdr.ct_mr_size = ZFCP_FC_CT_SIZE_PAGE / 4; + gid_pn->gid_pn_req.gid_pn.fn_wwpn = port->wwpn; + + ret = zfcp_fsf_send_ct(&adapter->gs->ds, &gid_pn->ct, + adapter->pool.gid_pn_req); + if (!ret) { + wait_for_completion(&completion); + zfcp_fc_ns_gid_pn_eval(gid_pn); + } return ret; } @@ -316,10 +274,10 @@ static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port, static int zfcp_fc_ns_gid_pn(struct zfcp_port *port) { int ret; - struct zfcp_gid_pn_data *gid_pn; + struct zfcp_fc_gid_pn *gid_pn; struct zfcp_adapter *adapter = port->adapter; - gid_pn = mempool_alloc(adapter->pool.gid_pn_data, GFP_ATOMIC); + gid_pn = mempool_alloc(adapter->pool.gid_pn, GFP_ATOMIC); if (!gid_pn) return -ENOMEM; @@ -333,7 +291,7 @@ static int zfcp_fc_ns_gid_pn(struct zfcp_port *port) zfcp_fc_wka_port_put(&adapter->gs->ds); out: - mempool_free(gid_pn, adapter->pool.gid_pn_data); + mempool_free(gid_pn, adapter->pool.gid_pn); return ret; } @@ -357,7 +315,7 @@ void zfcp_fc_port_did_lookup(struct work_struct *work) zfcp_erp_port_reopen(port, 0, "fcgpn_3", NULL); out: - zfcp_port_put(port); + put_device(&port->sysfs_device); } /** @@ -366,9 +324,9 @@ out: */ void zfcp_fc_trigger_did_lookup(struct zfcp_port *port) { - zfcp_port_get(port); + get_device(&port->sysfs_device); if (!queue_work(port->adapter->work_queue, &port->gid_pn_work)) - zfcp_port_put(port); + put_device(&port->sysfs_device); } /** @@ -378,33 +336,36 @@ void zfcp_fc_trigger_did_lookup(struct zfcp_port *port) * * Evaluate PLOGI playload and copy important fields into zfcp_port structure */ -void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi) -{ - port->maxframe_size = plogi->serv_param.common_serv_param[7] | - ((plogi->serv_param.common_serv_param[6] & 0x0F) << 8); - if (plogi->serv_param.class1_serv_param[0] & 0x80) +void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fc_els_flogi *plogi) +{ + if (plogi->fl_wwpn != port->wwpn) { + port->d_id = 0; + dev_warn(&port->adapter->ccw_device->dev, + "A port opened with WWPN 0x%016Lx returned data that " + "identifies it as WWPN 0x%016Lx\n", + (unsigned long long) port->wwpn, + (unsigned long long) plogi->fl_wwpn); + return; + } + + port->wwnn = plogi->fl_wwnn; + port->maxframe_size = plogi->fl_csp.sp_bb_data; + + if (plogi->fl_cssp[0].cp_class & FC_CPC_VALID) port->supported_classes |= FC_COS_CLASS1; - if (plogi->serv_param.class2_serv_param[0] & 0x80) + if (plogi->fl_cssp[1].cp_class & FC_CPC_VALID) port->supported_classes |= FC_COS_CLASS2; - if (plogi->serv_param.class3_serv_param[0] & 0x80) + if (plogi->fl_cssp[2].cp_class & FC_CPC_VALID) port->supported_classes |= FC_COS_CLASS3; - if (plogi->serv_param.class4_serv_param[0] & 0x80) + if (plogi->fl_cssp[3].cp_class & FC_CPC_VALID) port->supported_classes |= FC_COS_CLASS4; } -struct zfcp_els_adisc { - struct zfcp_send_els els; - struct scatterlist req; - struct scatterlist resp; - struct zfcp_ls_adisc ls_adisc; - struct zfcp_ls_adisc ls_adisc_acc; -}; - -static void zfcp_fc_adisc_handler(unsigned long data) +static void zfcp_fc_adisc_handler(void *data) { - struct zfcp_els_adisc *adisc = (struct zfcp_els_adisc *) data; + struct zfcp_fc_els_adisc *adisc = data; struct zfcp_port *port = adisc->els.port; - struct zfcp_ls_adisc *ls_adisc = &adisc->ls_adisc_acc; + struct fc_els_adisc *adisc_resp = &adisc->adisc_resp; if (adisc->els.status) { /* request rejected or timed out */ @@ -414,9 +375,9 @@ static void zfcp_fc_adisc_handler(unsigned long data) } if (!port->wwnn) - port->wwnn = ls_adisc->wwnn; + port->wwnn = adisc_resp->adisc_wwnn; - if ((port->wwpn != ls_adisc->wwpn) || + if ((port->wwpn != adisc_resp->adisc_wwpn) || !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) { zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, "fcadh_2", NULL); @@ -427,40 +388,44 @@ static void zfcp_fc_adisc_handler(unsigned long data) zfcp_scsi_schedule_rport_register(port); out: atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status); - zfcp_port_put(port); - kfree(adisc); + put_device(&port->sysfs_device); + kmem_cache_free(zfcp_data.adisc_cache, adisc); } static int zfcp_fc_adisc(struct zfcp_port *port) { - struct zfcp_els_adisc *adisc; + struct zfcp_fc_els_adisc *adisc; struct zfcp_adapter *adapter = port->adapter; + int ret; - adisc = kzalloc(sizeof(struct zfcp_els_adisc), GFP_ATOMIC); + adisc = kmem_cache_alloc(zfcp_data.adisc_cache, GFP_ATOMIC); if (!adisc) return -ENOMEM; + adisc->els.port = port; adisc->els.req = &adisc->req; adisc->els.resp = &adisc->resp; - sg_init_one(adisc->els.req, &adisc->ls_adisc, - sizeof(struct zfcp_ls_adisc)); - sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc, - sizeof(struct zfcp_ls_adisc)); + sg_init_one(adisc->els.req, &adisc->adisc_req, + sizeof(struct fc_els_adisc)); + sg_init_one(adisc->els.resp, &adisc->adisc_resp, + sizeof(struct fc_els_adisc)); - adisc->els.adapter = adapter; - adisc->els.port = port; - adisc->els.d_id = port->d_id; adisc->els.handler = zfcp_fc_adisc_handler; - adisc->els.handler_data = (unsigned long) adisc; - adisc->els.ls_code = adisc->ls_adisc.code = ZFCP_LS_ADISC; + adisc->els.handler_data = adisc; /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports without FC-AL-2 capability, so we don't set it */ - adisc->ls_adisc.wwpn = fc_host_port_name(adapter->scsi_host); - adisc->ls_adisc.wwnn = fc_host_node_name(adapter->scsi_host); - adisc->ls_adisc.nport_id = fc_host_port_id(adapter->scsi_host); + adisc->adisc_req.adisc_wwpn = fc_host_port_name(adapter->scsi_host); + adisc->adisc_req.adisc_wwnn = fc_host_node_name(adapter->scsi_host); + adisc->adisc_req.adisc_cmd = ELS_ADISC; + hton24(adisc->adisc_req.adisc_port_id, + fc_host_port_id(adapter->scsi_host)); + + ret = zfcp_fsf_send_els(adapter, port->d_id, &adisc->els); + if (ret) + kmem_cache_free(zfcp_data.adisc_cache, adisc); - return zfcp_fsf_send_els(&adisc->els); + return ret; } void zfcp_fc_link_test_work(struct work_struct *work) @@ -469,7 +434,7 @@ void zfcp_fc_link_test_work(struct work_struct *work) container_of(work, struct zfcp_port, test_link_work); int retval; - zfcp_port_get(port); + get_device(&port->sysfs_device); port->rport_task = RPORT_DEL; zfcp_scsi_rport_work(&port->rport_work); @@ -488,7 +453,7 @@ void zfcp_fc_link_test_work(struct work_struct *work) zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL); out: - zfcp_port_put(port); + put_device(&port->sysfs_device); } /** @@ -501,12 +466,12 @@ out: */ void zfcp_fc_test_link(struct zfcp_port *port) { - zfcp_port_get(port); + get_device(&port->sysfs_device); if (!queue_work(port->adapter->work_queue, &port->test_link_work)) - zfcp_port_put(port); + put_device(&port->sysfs_device); } -static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num) +static void zfcp_free_sg_env(struct zfcp_fc_gpn_ft *gpn_ft, int buf_num) { struct scatterlist *sg = &gpn_ft->sg_req; @@ -516,10 +481,10 @@ static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num) kfree(gpn_ft); } -static struct zfcp_gpn_ft *zfcp_alloc_sg_env(int buf_num) +static struct zfcp_fc_gpn_ft *zfcp_alloc_sg_env(int buf_num) { - struct zfcp_gpn_ft *gpn_ft; - struct ct_iu_gpn_ft_req *req; + struct zfcp_fc_gpn_ft *gpn_ft; + struct zfcp_fc_gpn_ft_req *req; gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL); if (!gpn_ft) @@ -542,159 +507,152 @@ out: } -static int zfcp_fc_send_gpn_ft(struct zfcp_gpn_ft *gpn_ft, +static int zfcp_fc_send_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft, struct zfcp_adapter *adapter, int max_bytes) { - struct zfcp_send_ct *ct = &gpn_ft->ct; - struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req); - struct zfcp_fc_ns_handler_data compl_rec; + struct zfcp_fsf_ct_els *ct = &gpn_ft->ct; + struct zfcp_fc_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req); + DECLARE_COMPLETION_ONSTACK(completion); int ret; /* prepare CT IU for GPN_FT */ - req->header.revision = ZFCP_CT_REVISION; - req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE; - req->header.gs_subtype = ZFCP_CT_NAME_SERVER; - req->header.options = ZFCP_CT_SYNCHRONOUS; - req->header.cmd_rsp_code = ZFCP_CT_GPN_FT; - req->header.max_res_size = max_bytes / 4; - req->flags = 0; - req->domain_id_scope = 0; - req->area_id_scope = 0; - req->fc4_type = ZFCP_CT_SCSI_FCP; + req->ct_hdr.ct_rev = FC_CT_REV; + req->ct_hdr.ct_fs_type = FC_FST_DIR; + req->ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE; + req->ct_hdr.ct_options = 0; + req->ct_hdr.ct_cmd = FC_NS_GPN_FT; + req->ct_hdr.ct_mr_size = max_bytes / 4; + req->gpn_ft.fn_domain_id_scope = 0; + req->gpn_ft.fn_area_id_scope = 0; + req->gpn_ft.fn_fc4_type = FC_TYPE_FCP; /* prepare zfcp_send_ct */ - ct->wka_port = &adapter->gs->ds; - ct->handler = zfcp_fc_ns_handler; - ct->handler_data = (unsigned long)&compl_rec; + ct->handler = zfcp_fc_complete; + ct->handler_data = &completion; ct->req = &gpn_ft->sg_req; ct->resp = gpn_ft->sg_resp; - init_completion(&compl_rec.done); - compl_rec.handler = NULL; - ret = zfcp_fsf_send_ct(ct, NULL); + ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct, NULL); if (!ret) - wait_for_completion(&compl_rec.done); + wait_for_completion(&completion); return ret; } -static void zfcp_fc_validate_port(struct zfcp_port *port) +static void zfcp_fc_validate_port(struct zfcp_port *port, struct list_head *lh) { - struct zfcp_adapter *adapter = port->adapter; - if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC)) return; atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status); if ((port->supported_classes != 0) || - !list_empty(&port->unit_list_head)) { - zfcp_port_put(port); + !list_empty(&port->unit_list)) return; - } - zfcp_erp_port_shutdown(port, 0, "fcpval1", NULL); - zfcp_erp_wait(adapter); - zfcp_port_put(port); - zfcp_port_dequeue(port); + + list_move_tail(&port->list, lh); } -static int zfcp_fc_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries) +static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft, + struct zfcp_adapter *adapter, int max_entries) { - struct zfcp_send_ct *ct = &gpn_ft->ct; + struct zfcp_fsf_ct_els *ct = &gpn_ft->ct; struct scatterlist *sg = gpn_ft->sg_resp; - struct ct_hdr *hdr = sg_virt(sg); - struct gpn_ft_resp_acc *acc = sg_virt(sg); - struct zfcp_adapter *adapter = ct->wka_port->adapter; + struct fc_ct_hdr *hdr = sg_virt(sg); + struct fc_gpn_ft_resp *acc = sg_virt(sg); struct zfcp_port *port, *tmp; + unsigned long flags; + LIST_HEAD(remove_lh); u32 d_id; int ret = 0, x, last = 0; if (ct->status) return -EIO; - if (hdr->cmd_rsp_code != ZFCP_CT_ACCEPT) { - if (hdr->reason_code == ZFCP_CT_UNABLE_TO_PERFORM_CMD) + if (hdr->ct_cmd != FC_FS_ACC) { + if (hdr->ct_reason == FC_BA_RJT_UNABLE) return -EAGAIN; /* might be a temporary condition */ return -EIO; } - if (hdr->max_res_size) { + if (hdr->ct_mr_size) { dev_warn(&adapter->ccw_device->dev, "The name server reported %d words residual data\n", - hdr->max_res_size); + hdr->ct_mr_size); return -E2BIG; } - mutex_lock(&zfcp_data.config_mutex); - /* first entry is the header */ for (x = 1; x < max_entries && !last; x++) { - if (x % (ZFCP_GPN_FT_ENTRIES + 1)) + if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1)) acc++; else acc = sg_virt(++sg); - last = acc->control & 0x80; - d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 | - acc->port_id[2]; + last = acc->fp_flags & FC_NS_FID_LAST; + d_id = ntoh24(acc->fp_fid); /* don't attach ports with a well known address */ - if ((d_id & ZFCP_DID_WKA) == ZFCP_DID_WKA) + if (d_id >= FC_FID_WELL_KNOWN_BASE) continue; /* skip the adapter's port and known remote ports */ - if (acc->wwpn == fc_host_port_name(adapter->scsi_host)) - continue; - port = zfcp_get_port_by_wwpn(adapter, acc->wwpn); - if (port) + if (acc->fp_wwpn == fc_host_port_name(adapter->scsi_host)) continue; - port = zfcp_port_enqueue(adapter, acc->wwpn, + port = zfcp_port_enqueue(adapter, acc->fp_wwpn, ZFCP_STATUS_COMMON_NOESC, d_id); - if (IS_ERR(port)) - ret = PTR_ERR(port); - else + if (!IS_ERR(port)) zfcp_erp_port_reopen(port, 0, "fcegpf1", NULL); + else if (PTR_ERR(port) != -EEXIST) + ret = PTR_ERR(port); } zfcp_erp_wait(adapter); - list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list) - zfcp_fc_validate_port(port); - mutex_unlock(&zfcp_data.config_mutex); + write_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry_safe(port, tmp, &adapter->port_list, list) + zfcp_fc_validate_port(port, &remove_lh); + write_unlock_irqrestore(&adapter->port_list_lock, flags); + + list_for_each_entry_safe(port, tmp, &remove_lh, list) { + zfcp_erp_port_shutdown(port, 0, "fcegpf2", NULL); + zfcp_device_unregister(&port->sysfs_device, + &zfcp_sysfs_port_attrs); + } + return ret; } /** * zfcp_fc_scan_ports - scan remote ports and attach new ports - * @adapter: pointer to struct zfcp_adapter + * @work: reference to scheduled work */ -int zfcp_fc_scan_ports(struct zfcp_adapter *adapter) +void zfcp_fc_scan_ports(struct work_struct *work) { + struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter, + scan_work); int ret, i; - struct zfcp_gpn_ft *gpn_ft; + struct zfcp_fc_gpn_ft *gpn_ft; int chain, max_entries, buf_num, max_bytes; chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS; - buf_num = chain ? ZFCP_GPN_FT_BUFFERS : 1; - max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES; - max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE; + buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1; + max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE; + max_bytes = chain ? ZFCP_FC_GPN_FT_MAX_SIZE : ZFCP_FC_CT_SIZE_PAGE; if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT && fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV) - return 0; + return; - ret = zfcp_fc_wka_port_get(&adapter->gs->ds); - if (ret) - return ret; + if (zfcp_fc_wka_port_get(&adapter->gs->ds)) + return; gpn_ft = zfcp_alloc_sg_env(buf_num); - if (!gpn_ft) { - ret = -ENOMEM; + if (!gpn_ft) goto out; - } for (i = 0; i < 3; i++) { ret = zfcp_fc_send_gpn_ft(gpn_ft, adapter, max_bytes); if (!ret) { - ret = zfcp_fc_eval_gpn_ft(gpn_ft, max_entries); + ret = zfcp_fc_eval_gpn_ft(gpn_ft, adapter, max_entries); if (ret == -EAGAIN) ssleep(1); else @@ -704,174 +662,116 @@ int zfcp_fc_scan_ports(struct zfcp_adapter *adapter) zfcp_free_sg_env(gpn_ft, buf_num); out: zfcp_fc_wka_port_put(&adapter->gs->ds); - return ret; -} - - -void _zfcp_fc_scan_ports_later(struct work_struct *work) -{ - zfcp_fc_scan_ports(container_of(work, struct zfcp_adapter, scan_work)); } -struct zfcp_els_fc_job { - struct zfcp_send_els els; - struct fc_bsg_job *job; -}; - -static void zfcp_fc_generic_els_handler(unsigned long data) +static void zfcp_fc_ct_els_job_handler(void *data) { - struct zfcp_els_fc_job *els_fc_job = (struct zfcp_els_fc_job *) data; - struct fc_bsg_job *job = els_fc_job->job; - struct fc_bsg_reply *reply = job->reply; - - if (els_fc_job->els.status) { - /* request rejected or timed out */ - reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_REJECT; - goto out; - } - - reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; - reply->reply_payload_rcv_len = job->reply_payload.payload_len; + struct fc_bsg_job *job = data; + struct zfcp_fsf_ct_els *zfcp_ct_els = job->dd_data; + int status = zfcp_ct_els->status; + int reply_status; -out: - job->state_flags = FC_RQST_STATE_DONE; + reply_status = status ? FC_CTELS_STATUS_REJECT : FC_CTELS_STATUS_OK; + job->reply->reply_data.ctels_reply.status = reply_status; + job->reply->reply_payload_rcv_len = job->reply_payload.payload_len; job->job_done(job); - kfree(els_fc_job); } -int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *job) +static int zfcp_fc_exec_els_job(struct fc_bsg_job *job, + struct zfcp_adapter *adapter) { - struct zfcp_els_fc_job *els_fc_job; + struct zfcp_fsf_ct_els *els = job->dd_data; struct fc_rport *rport = job->rport; - struct Scsi_Host *shost; - struct zfcp_adapter *adapter; struct zfcp_port *port; - u8 *port_did; - - shost = rport ? rport_to_shost(rport) : job->shost; - adapter = (struct zfcp_adapter *)shost->hostdata[0]; - - if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN)) - return -EINVAL; - - els_fc_job = kzalloc(sizeof(struct zfcp_els_fc_job), GFP_KERNEL); - if (!els_fc_job) - return -ENOMEM; + u32 d_id; - els_fc_job->els.adapter = adapter; if (rport) { - read_lock_irq(&zfcp_data.config_lock); port = zfcp_get_port_by_wwpn(adapter, rport->port_name); - if (port) - els_fc_job->els.d_id = port->d_id; - read_unlock_irq(&zfcp_data.config_lock); - if (!port) { - kfree(els_fc_job); + if (!port) return -EINVAL; - } - } else { - port_did = job->request->rqst_data.h_els.port_id; - els_fc_job->els.d_id = (port_did[0] << 16) + - (port_did[1] << 8) + port_did[2]; - } - - els_fc_job->els.req = job->request_payload.sg_list; - els_fc_job->els.resp = job->reply_payload.sg_list; - els_fc_job->els.handler = zfcp_fc_generic_els_handler; - els_fc_job->els.handler_data = (unsigned long) els_fc_job; - els_fc_job->job = job; - - return zfcp_fsf_send_els(&els_fc_job->els); -} - -struct zfcp_ct_fc_job { - struct zfcp_send_ct ct; - struct fc_bsg_job *job; -}; - -static void zfcp_fc_generic_ct_handler(unsigned long data) -{ - struct zfcp_ct_fc_job *ct_fc_job = (struct zfcp_ct_fc_job *) data; - struct fc_bsg_job *job = ct_fc_job->job; - - job->reply->reply_data.ctels_reply.status = ct_fc_job->ct.status ? - FC_CTELS_STATUS_REJECT : FC_CTELS_STATUS_OK; - job->reply->reply_payload_rcv_len = job->reply_payload.payload_len; - job->state_flags = FC_RQST_STATE_DONE; - job->job_done(job); - zfcp_fc_wka_port_put(ct_fc_job->ct.wka_port); + d_id = port->d_id; + put_device(&port->sysfs_device); + } else + d_id = ntoh24(job->request->rqst_data.h_els.port_id); - kfree(ct_fc_job); + return zfcp_fsf_send_els(adapter, d_id, els); } -int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *job) +static int zfcp_fc_exec_ct_job(struct fc_bsg_job *job, + struct zfcp_adapter *adapter) { int ret; u8 gs_type; - struct fc_rport *rport = job->rport; - struct Scsi_Host *shost; - struct zfcp_adapter *adapter; - struct zfcp_ct_fc_job *ct_fc_job; + struct zfcp_fsf_ct_els *ct = job->dd_data; + struct zfcp_fc_wka_port *wka_port; u32 preamble_word1; - shost = rport ? rport_to_shost(rport) : job->shost; - - adapter = (struct zfcp_adapter *)shost->hostdata[0]; - if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN)) - return -EINVAL; - - ct_fc_job = kzalloc(sizeof(struct zfcp_ct_fc_job), GFP_KERNEL); - if (!ct_fc_job) - return -ENOMEM; - preamble_word1 = job->request->rqst_data.r_ct.preamble_word1; gs_type = (preamble_word1 & 0xff000000) >> 24; switch (gs_type) { case FC_FST_ALIAS: - ct_fc_job->ct.wka_port = &adapter->gs->as; + wka_port = &adapter->gs->as; break; case FC_FST_MGMT: - ct_fc_job->ct.wka_port = &adapter->gs->ms; + wka_port = &adapter->gs->ms; break; case FC_FST_TIME: - ct_fc_job->ct.wka_port = &adapter->gs->ts; + wka_port = &adapter->gs->ts; break; case FC_FST_DIR: - ct_fc_job->ct.wka_port = &adapter->gs->ds; + wka_port = &adapter->gs->ds; break; default: - kfree(ct_fc_job); return -EINVAL; /* no such service */ } - ret = zfcp_fc_wka_port_get(ct_fc_job->ct.wka_port); - if (ret) { - kfree(ct_fc_job); + ret = zfcp_fc_wka_port_get(wka_port); + if (ret) return ret; - } - ct_fc_job->ct.req = job->request_payload.sg_list; - ct_fc_job->ct.resp = job->reply_payload.sg_list; - ct_fc_job->ct.handler = zfcp_fc_generic_ct_handler; - ct_fc_job->ct.handler_data = (unsigned long) ct_fc_job; - ct_fc_job->ct.completion = NULL; - ct_fc_job->job = job; + ret = zfcp_fsf_send_ct(wka_port, ct, NULL); + if (ret) + zfcp_fc_wka_port_put(wka_port); - ret = zfcp_fsf_send_ct(&ct_fc_job->ct, NULL); - if (ret) { - kfree(ct_fc_job); - zfcp_fc_wka_port_put(ct_fc_job->ct.wka_port); - } return ret; } +int zfcp_fc_exec_bsg_job(struct fc_bsg_job *job) +{ + struct Scsi_Host *shost; + struct zfcp_adapter *adapter; + struct zfcp_fsf_ct_els *ct_els = job->dd_data; + + shost = job->rport ? rport_to_shost(job->rport) : job->shost; + adapter = (struct zfcp_adapter *)shost->hostdata[0]; + + if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN)) + return -EINVAL; + + ct_els->req = job->request_payload.sg_list; + ct_els->resp = job->reply_payload.sg_list; + ct_els->handler = zfcp_fc_ct_els_job_handler; + ct_els->handler_data = job; + + switch (job->request->msgcode) { + case FC_BSG_RPT_ELS: + case FC_BSG_HST_ELS_NOLOGIN: + return zfcp_fc_exec_els_job(job, adapter); + case FC_BSG_RPT_CT: + case FC_BSG_HST_CT: + return zfcp_fc_exec_ct_job(job, adapter); + default: + return -EINVAL; + } +} + int zfcp_fc_gs_setup(struct zfcp_adapter *adapter) { - struct zfcp_wka_ports *wka_ports; + struct zfcp_fc_wka_ports *wka_ports; - wka_ports = kzalloc(sizeof(struct zfcp_wka_ports), GFP_KERNEL); + wka_ports = kzalloc(sizeof(struct zfcp_fc_wka_ports), GFP_KERNEL); if (!wka_ports) return -ENOMEM; @@ -880,7 +780,6 @@ int zfcp_fc_gs_setup(struct zfcp_adapter *adapter) zfcp_fc_wka_port_init(&wka_ports->ts, FC_FID_TIME_SERV, adapter); zfcp_fc_wka_port_init(&wka_ports->ds, FC_FID_DIR_SERV, adapter); zfcp_fc_wka_port_init(&wka_ports->as, FC_FID_ALIASES, adapter); - zfcp_fc_wka_port_init(&wka_ports->ks, FC_FID_SEC_KEY, adapter); return 0; } diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h new file mode 100644 index 000000000000..cb2a3669a384 --- /dev/null +++ b/drivers/s390/scsi/zfcp_fc.h @@ -0,0 +1,260 @@ +/* + * zfcp device driver + * + * Fibre Channel related definitions and inline functions for the zfcp + * device driver + * + * Copyright IBM Corporation 2009 + */ + +#ifndef ZFCP_FC_H +#define ZFCP_FC_H + +#include <scsi/fc/fc_els.h> +#include <scsi/fc/fc_fcp.h> +#include <scsi/fc/fc_ns.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_tcq.h> +#include "zfcp_fsf.h" + +#define ZFCP_FC_CT_SIZE_PAGE (PAGE_SIZE - sizeof(struct fc_ct_hdr)) +#define ZFCP_FC_GPN_FT_ENT_PAGE (ZFCP_FC_CT_SIZE_PAGE \ + / sizeof(struct fc_gpn_ft_resp)) +#define ZFCP_FC_GPN_FT_NUM_BUFS 4 /* memory pages */ + +#define ZFCP_FC_GPN_FT_MAX_SIZE (ZFCP_FC_GPN_FT_NUM_BUFS * PAGE_SIZE \ + - sizeof(struct fc_ct_hdr)) +#define ZFCP_FC_GPN_FT_MAX_ENT (ZFCP_FC_GPN_FT_NUM_BUFS * \ + (ZFCP_FC_GPN_FT_ENT_PAGE + 1)) + +/** + * struct zfcp_fc_gid_pn_req - container for ct header plus gid_pn request + * @ct_hdr: FC GS common transport header + * @gid_pn: GID_PN request + */ +struct zfcp_fc_gid_pn_req { + struct fc_ct_hdr ct_hdr; + struct fc_ns_gid_pn gid_pn; +} __packed; + +/** + * struct zfcp_fc_gid_pn_resp - container for ct header plus gid_pn response + * @ct_hdr: FC GS common transport header + * @gid_pn: GID_PN response + */ +struct zfcp_fc_gid_pn_resp { + struct fc_ct_hdr ct_hdr; + struct fc_gid_pn_resp gid_pn; +} __packed; + +/** + * struct zfcp_fc_gid_pn - everything required in zfcp for gid_pn request + * @ct: data passed to zfcp_fsf for issuing fsf request + * @sg_req: scatterlist entry for request data + * @sg_resp: scatterlist entry for response data + * @gid_pn_req: GID_PN request data + * @gid_pn_resp: GID_PN response data + */ +struct zfcp_fc_gid_pn { + struct zfcp_fsf_ct_els ct; + struct scatterlist sg_req; + struct scatterlist sg_resp; + struct zfcp_fc_gid_pn_req gid_pn_req; + struct zfcp_fc_gid_pn_resp gid_pn_resp; + struct zfcp_port *port; +}; + +/** + * struct zfcp_fc_gpn_ft - container for ct header plus gpn_ft request + * @ct_hdr: FC GS common transport header + * @gpn_ft: GPN_FT request + */ +struct zfcp_fc_gpn_ft_req { + struct fc_ct_hdr ct_hdr; + struct fc_ns_gid_ft gpn_ft; +} __packed; + +/** + * struct zfcp_fc_gpn_ft_resp - container for ct header plus gpn_ft response + * @ct_hdr: FC GS common transport header + * @gpn_ft: Array of gpn_ft response data to fill one memory page + */ +struct zfcp_fc_gpn_ft_resp { + struct fc_ct_hdr ct_hdr; + struct fc_gpn_ft_resp gpn_ft[ZFCP_FC_GPN_FT_ENT_PAGE]; +} __packed; + +/** + * struct zfcp_fc_gpn_ft - zfcp data for gpn_ft request + * @ct: data passed to zfcp_fsf for issuing fsf request + * @sg_req: scatter list entry for gpn_ft request + * @sg_resp: scatter list entries for gpn_ft responses (per memory page) + */ +struct zfcp_fc_gpn_ft { + struct zfcp_fsf_ct_els ct; + struct scatterlist sg_req; + struct scatterlist sg_resp[ZFCP_FC_GPN_FT_NUM_BUFS]; +}; + +/** + * struct zfcp_fc_els_adisc - everything required in zfcp for issuing ELS ADISC + * @els: data required for issuing els fsf command + * @req: scatterlist entry for ELS ADISC request + * @resp: scatterlist entry for ELS ADISC response + * @adisc_req: ELS ADISC request data + * @adisc_resp: ELS ADISC response data + */ +struct zfcp_fc_els_adisc { + struct zfcp_fsf_ct_els els; + struct scatterlist req; + struct scatterlist resp; + struct fc_els_adisc adisc_req; + struct fc_els_adisc adisc_resp; +}; + +/** + * enum zfcp_fc_wka_status - FC WKA port status in zfcp + * @ZFCP_FC_WKA_PORT_OFFLINE: Port is closed and not in use + * @ZFCP_FC_WKA_PORT_CLOSING: The FSF "close port" request is pending + * @ZFCP_FC_WKA_PORT_OPENING: The FSF "open port" request is pending + * @ZFCP_FC_WKA_PORT_ONLINE: The port is open and the port handle is valid + */ +enum zfcp_fc_wka_status { + ZFCP_FC_WKA_PORT_OFFLINE, + ZFCP_FC_WKA_PORT_CLOSING, + ZFCP_FC_WKA_PORT_OPENING, + ZFCP_FC_WKA_PORT_ONLINE, +}; + +/** + * struct zfcp_fc_wka_port - representation of well-known-address (WKA) FC port + * @adapter: Pointer to adapter structure this WKA port belongs to + * @completion_wq: Wait for completion of open/close command + * @status: Current status of WKA port + * @refcount: Reference count to keep port open as long as it is in use + * @d_id: FC destination id or well-known-address + * @handle: FSF handle for the open WKA port + * @mutex: Mutex used during opening/closing state changes + * @work: For delaying the closing of the WKA port + */ +struct zfcp_fc_wka_port { + struct zfcp_adapter *adapter; + wait_queue_head_t completion_wq; + enum zfcp_fc_wka_status status; + atomic_t refcount; + u32 d_id; + u32 handle; + struct mutex mutex; + struct delayed_work work; +}; + +/** + * struct zfcp_fc_wka_ports - Data structures for FC generic services + * @ms: FC Management service + * @ts: FC time service + * @ds: FC directory service + * @as: FC alias service + */ +struct zfcp_fc_wka_ports { + struct zfcp_fc_wka_port ms; + struct zfcp_fc_wka_port ts; + struct zfcp_fc_wka_port ds; + struct zfcp_fc_wka_port as; +}; + +/** + * zfcp_fc_scsi_to_fcp - setup FCP command with data from scsi_cmnd + * @fcp: fcp_cmnd to setup + * @scsi: scsi_cmnd where to get LUN, task attributes/flags and CDB + */ +static inline +void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi) +{ + char tag[2]; + + int_to_scsilun(scsi->device->lun, (struct scsi_lun *) &fcp->fc_lun); + + if (scsi_populate_tag_msg(scsi, tag)) { + switch (tag[0]) { + case MSG_ORDERED_TAG: + fcp->fc_pri_ta |= FCP_PTA_ORDERED; + break; + case MSG_SIMPLE_TAG: + fcp->fc_pri_ta |= FCP_PTA_SIMPLE; + break; + }; + } else + fcp->fc_pri_ta = FCP_PTA_SIMPLE; + + if (scsi->sc_data_direction == DMA_FROM_DEVICE) + fcp->fc_flags |= FCP_CFL_RDDATA; + if (scsi->sc_data_direction == DMA_TO_DEVICE) + fcp->fc_flags |= FCP_CFL_WRDATA; + + memcpy(fcp->fc_cdb, scsi->cmnd, scsi->cmd_len); + + fcp->fc_dl = scsi_bufflen(scsi); +} + +/** + * zfcp_fc_fcp_tm - setup FCP command as task management command + * @fcp: fcp_cmnd to setup + * @dev: scsi_device where to send the task management command + * @tm: task management flags to setup tm command + */ +static inline +void zfcp_fc_fcp_tm(struct fcp_cmnd *fcp, struct scsi_device *dev, u8 tm_flags) +{ + int_to_scsilun(dev->lun, (struct scsi_lun *) &fcp->fc_lun); + fcp->fc_tm_flags |= tm_flags; +} + +/** + * zfcp_fc_evap_fcp_rsp - evaluate FCP RSP IU and update scsi_cmnd accordingly + * @fcp_rsp: FCP RSP IU to evaluate + * @scsi: SCSI command where to update status and sense buffer + */ +static inline +void zfcp_fc_eval_fcp_rsp(struct fcp_resp_with_ext *fcp_rsp, + struct scsi_cmnd *scsi) +{ + struct fcp_resp_rsp_info *rsp_info; + char *sense; + u32 sense_len, resid; + u8 rsp_flags; + + set_msg_byte(scsi, COMMAND_COMPLETE); + scsi->result |= fcp_rsp->resp.fr_status; + + rsp_flags = fcp_rsp->resp.fr_flags; + + if (unlikely(rsp_flags & FCP_RSP_LEN_VAL)) { + rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1]; + if (rsp_info->rsp_code == FCP_TMF_CMPL) + set_host_byte(scsi, DID_OK); + else { + set_host_byte(scsi, DID_ERROR); + return; + } + } + + if (unlikely(rsp_flags & FCP_SNS_LEN_VAL)) { + sense = (char *) &fcp_rsp[1]; + if (rsp_flags & FCP_RSP_LEN_VAL) + sense += fcp_rsp->ext.fr_sns_len; + sense_len = min(fcp_rsp->ext.fr_sns_len, + (u32) SCSI_SENSE_BUFFERSIZE); + memcpy(scsi->sense_buffer, sense, sense_len); + } + + if (unlikely(rsp_flags & FCP_RESID_UNDER)) { + resid = fcp_rsp->ext.fr_resid; + scsi_set_resid(scsi, resid); + if (scsi_bufflen(scsi) - resid < scsi->underflow && + !(rsp_flags & FCP_SNS_LEN_VAL) && + fcp_rsp->resp.fr_status == SAM_STAT_GOOD) + set_host_byte(scsi, DID_ERROR); + } +} + +#endif diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 4e41baa0c141..482dcd97aa5d 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -10,7 +10,9 @@ #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include <linux/blktrace_api.h> +#include <scsi/fc/fc_els.h> #include "zfcp_ext.h" +#include "zfcp_fc.h" #include "zfcp_dbf.h" static void zfcp_fsf_request_timeout_handler(unsigned long data) @@ -122,36 +124,32 @@ void zfcp_fsf_req_free(struct zfcp_fsf_req *req) static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req) { + unsigned long flags; struct fsf_status_read_buffer *sr_buf = req->data; struct zfcp_adapter *adapter = req->adapter; struct zfcp_port *port; - int d_id = sr_buf->d_id & ZFCP_DID_MASK; - unsigned long flags; + int d_id = ntoh24(sr_buf->d_id); - read_lock_irqsave(&zfcp_data.config_lock, flags); - list_for_each_entry(port, &adapter->port_list_head, list) + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) if (port->d_id == d_id) { - read_unlock_irqrestore(&zfcp_data.config_lock, flags); zfcp_erp_port_reopen(port, 0, "fssrpc1", req); - return; + break; } - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + read_unlock_irqrestore(&adapter->port_list_lock, flags); } static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, char *id, struct fsf_link_down_info *link_down) { struct zfcp_adapter *adapter = req->adapter; - unsigned long flags; if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED) return; atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); - read_lock_irqsave(&zfcp_data.config_lock, flags); zfcp_scsi_schedule_rports_block(adapter); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); if (!link_down) goto out; @@ -291,7 +289,7 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req) zfcp_erp_adapter_access_changed(adapter, "fssrh_3", req); if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_INCOMING_ELS) - schedule_work(&adapter->scan_work); + queue_work(adapter->work_queue, &adapter->scan_work); break; case FSF_STATUS_READ_CFDC_UPDATED: zfcp_erp_adapter_access_changed(adapter, "fssrh_4", req); @@ -317,7 +315,6 @@ static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req) case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: return; case FSF_SQ_COMMAND_ABORTED: - req->status |= ZFCP_STATUS_FSFREQ_ABORTED; break; case FSF_SQ_NO_RECOM: dev_err(&req->adapter->ccw_device->dev, @@ -358,8 +355,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req) zfcp_dbf_hba_fsf_response(req); if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */ + req->status |= ZFCP_STATUS_FSFREQ_ERROR; return; } @@ -377,7 +373,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req) case FSF_PROT_ERROR_STATE: case FSF_PROT_SEQ_NUMB_ERROR: zfcp_erp_adapter_reopen(adapter, 0, "fspse_2", req); - req->status |= ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PROT_UNSUPP_QTCB_TYPE: dev_err(&adapter->ccw_device->dev, @@ -480,20 +476,27 @@ void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter) static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) { - struct fsf_qtcb_bottom_config *bottom; + struct fsf_qtcb_bottom_config *bottom = &req->qtcb->bottom.config; struct zfcp_adapter *adapter = req->adapter; struct Scsi_Host *shost = adapter->scsi_host; + struct fc_els_flogi *nsp, *plogi; - bottom = &req->qtcb->bottom.config; + /* adjust pointers for missing command code */ + nsp = (struct fc_els_flogi *) ((u8 *)&bottom->nport_serv_param + - sizeof(u32)); + plogi = (struct fc_els_flogi *) ((u8 *)&bottom->plogi_payload + - sizeof(u32)); if (req->data) memcpy(req->data, bottom, sizeof(*bottom)); - fc_host_node_name(shost) = bottom->nport_serv_param.wwnn; - fc_host_port_name(shost) = bottom->nport_serv_param.wwpn; - fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK; + fc_host_port_name(shost) = nsp->fl_wwpn; + fc_host_node_name(shost) = nsp->fl_wwnn; + fc_host_port_id(shost) = ntoh24(bottom->s_id); fc_host_speed(shost) = bottom->fc_link_speed; fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3; + fc_host_supported_fc4s(shost)[2] = 1; /* FCP */ + fc_host_active_fc4s(shost)[2] = 1; /* FCP */ adapter->hydra_version = bottom->adapter_type; adapter->timer_ticks = bottom->timer_interval; @@ -503,9 +506,9 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) switch (bottom->fc_topology) { case FSF_TOPO_P2P: - adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK; - adapter->peer_wwpn = bottom->plogi_payload.wwpn; - adapter->peer_wwnn = bottom->plogi_payload.wwnn; + adapter->peer_d_id = ntoh24(bottom->peer_d_id); + adapter->peer_wwpn = plogi->fl_wwpn; + adapter->peer_wwnn = plogi->fl_wwnn; fc_host_port_type(shost) = FC_PORTTYPE_PTP; break; case FSF_TOPO_FABRIC: @@ -881,13 +884,11 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req) break; case FSF_PORT_BOXED: zfcp_erp_port_boxed(unit->port, "fsafch3", req); - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_LUN_BOXED: zfcp_erp_unit_boxed(unit, "fsafch4", req); - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: switch (fsq->word[0]) { @@ -958,10 +959,10 @@ out: static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req) { struct zfcp_adapter *adapter = req->adapter; - struct zfcp_send_ct *send_ct = req->data; + struct zfcp_fsf_ct_els *ct = req->data; struct fsf_qtcb_header *header = &req->qtcb->header; - send_ct->status = -EINVAL; + ct->status = -EINVAL; if (req->status & ZFCP_STATUS_FSFREQ_ERROR) goto skip_fsfstatus; @@ -969,7 +970,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req) switch (header->fsf_status) { case FSF_GOOD: zfcp_dbf_san_ct_response(req); - send_ct->status = 0; + ct->status = 0; break; case FSF_SERVICE_CLASS_NOT_SUPPORTED: zfcp_fsf_class_not_supp(req); @@ -985,8 +986,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req) case FSF_ACCESS_DENIED: break; case FSF_PORT_BOXED: - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PORT_HANDLE_NOT_VALID: zfcp_erp_adapter_reopen(adapter, 0, "fsscth1", req); @@ -1001,8 +1001,8 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req) } skip_fsfstatus: - if (send_ct->handler) - send_ct->handler(send_ct->handler_data); + if (ct->handler) + ct->handler(ct->handler_data); } static void zfcp_fsf_setup_ct_els_unchained(struct qdio_buffer_element *sbale, @@ -1071,15 +1071,17 @@ static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req, int max_sbals) { int ret; + unsigned int fcp_chan_timeout; ret = zfcp_fsf_setup_ct_els_sbals(req, sg_req, sg_resp, max_sbals); if (ret) return ret; /* common settings for ct/gs and els requests */ + fcp_chan_timeout = 2 * FC_DEF_R_A_TOV / 1000; req->qtcb->bottom.support.service_class = FSF_CLASS_3; - req->qtcb->bottom.support.timeout = 2 * R_A_TOV; - zfcp_fsf_start_timer(req, (2 * R_A_TOV + 10) * HZ); + req->qtcb->bottom.support.timeout = fcp_chan_timeout; + zfcp_fsf_start_timer(req, (fcp_chan_timeout + 10) * HZ); return 0; } @@ -1089,9 +1091,9 @@ static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req, * @ct: pointer to struct zfcp_send_ct with data for request * @pool: if non-null this mempool is used to allocate struct zfcp_fsf_req */ -int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool) +int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port, + struct zfcp_fsf_ct_els *ct, mempool_t *pool) { - struct zfcp_wka_port *wka_port = ct->wka_port; struct zfcp_qdio *qdio = wka_port->adapter->qdio; struct zfcp_fsf_req *req; int ret = -EIO; @@ -1117,7 +1119,7 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool) req->qtcb->header.port_handle = wka_port->handle; req->data = ct; - zfcp_dbf_san_ct_request(req); + zfcp_dbf_san_ct_request(req, wka_port->d_id); ret = zfcp_fsf_req_send(req); if (ret) @@ -1134,7 +1136,7 @@ out: static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req) { - struct zfcp_send_els *send_els = req->data; + struct zfcp_fsf_ct_els *send_els = req->data; struct zfcp_port *port = send_els->port; struct fsf_qtcb_header *header = &req->qtcb->header; @@ -1154,9 +1156,6 @@ static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req) case FSF_ADAPTER_STATUS_AVAILABLE: switch (header->fsf_status_qual.word[0]){ case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - if (port && (send_els->ls_code != ZFCP_LS_ADISC)) - zfcp_fc_test_link(port); - /*fall through */ case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: case FSF_SQ_RETRY_IF_POSSIBLE: req->status |= ZFCP_STATUS_FSFREQ_ERROR; @@ -1188,10 +1187,11 @@ skip_fsfstatus: * zfcp_fsf_send_els - initiate an ELS command (FC-FS) * @els: pointer to struct zfcp_send_els with data for the command */ -int zfcp_fsf_send_els(struct zfcp_send_els *els) +int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id, + struct zfcp_fsf_ct_els *els) { struct zfcp_fsf_req *req; - struct zfcp_qdio *qdio = els->adapter->qdio; + struct zfcp_qdio *qdio = adapter->qdio; int ret = -EIO; spin_lock_bh(&qdio->req_q_lock); @@ -1211,7 +1211,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els) if (ret) goto failed_send; - req->qtcb->bottom.support.d_id = els->d_id; + hton24(req->qtcb->bottom.support.d_id, d_id); req->handler = zfcp_fsf_send_els_handler; req->data = els; @@ -1422,7 +1422,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req) { struct zfcp_port *port = req->data; struct fsf_qtcb_header *header = &req->qtcb->header; - struct fsf_plogi *plogi; + struct fc_els_flogi *plogi; if (req->status & ZFCP_STATUS_FSFREQ_ERROR) goto out; @@ -1472,23 +1472,10 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req) * another GID_PN straight after a port has been opened. * Alternately, an ADISC/PDISC ELS should suffice, as well. */ - plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els; + plogi = (struct fc_els_flogi *) req->qtcb->bottom.support.els; if (req->qtcb->bottom.support.els1_length >= - FSF_PLOGI_MIN_LEN) { - if (plogi->serv_param.wwpn != port->wwpn) { - port->d_id = 0; - dev_warn(&port->adapter->ccw_device->dev, - "A port opened with WWPN 0x%016Lx " - "returned data that identifies it as " - "WWPN 0x%016Lx\n", - (unsigned long long) port->wwpn, - (unsigned long long) - plogi->serv_param.wwpn); - } else { - port->wwnn = plogi->serv_param.wwnn; + FSF_PLOGI_MIN_LEN) zfcp_fc_plogi_evaluate(port, plogi); - } - } break; case FSF_UNKNOWN_OP_SUBTYPE: req->status |= ZFCP_STATUS_FSFREQ_ERROR; @@ -1496,7 +1483,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req) } out: - zfcp_port_put(port); + put_device(&port->sysfs_device); } /** @@ -1530,18 +1517,18 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action) sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; req->handler = zfcp_fsf_open_port_handler; - req->qtcb->bottom.support.d_id = port->d_id; + hton24(req->qtcb->bottom.support.d_id, port->d_id); req->data = port; req->erp_action = erp_action; erp_action->fsf_req = req; - zfcp_port_get(port); + get_device(&port->sysfs_device); zfcp_fsf_start_erp_timer(req); retval = zfcp_fsf_req_send(req); if (retval) { zfcp_fsf_req_free(req); erp_action->fsf_req = NULL; - zfcp_port_put(port); + put_device(&port->sysfs_device); } out: spin_unlock_bh(&qdio->req_q_lock); @@ -1618,11 +1605,11 @@ out: static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req) { - struct zfcp_wka_port *wka_port = req->data; + struct zfcp_fc_wka_port *wka_port = req->data; struct fsf_qtcb_header *header = &req->qtcb->header; if (req->status & ZFCP_STATUS_FSFREQ_ERROR) { - wka_port->status = ZFCP_WKA_PORT_OFFLINE; + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; goto out; } @@ -1635,13 +1622,13 @@ static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req) req->status |= ZFCP_STATUS_FSFREQ_ERROR; /* fall through */ case FSF_ACCESS_DENIED: - wka_port->status = ZFCP_WKA_PORT_OFFLINE; + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; break; case FSF_GOOD: wka_port->handle = header->port_handle; /* fall through */ case FSF_PORT_ALREADY_OPEN: - wka_port->status = ZFCP_WKA_PORT_ONLINE; + wka_port->status = ZFCP_FC_WKA_PORT_ONLINE; } out: wake_up(&wka_port->completion_wq); @@ -1649,10 +1636,10 @@ out: /** * zfcp_fsf_open_wka_port - create and send open wka-port request - * @wka_port: pointer to struct zfcp_wka_port + * @wka_port: pointer to struct zfcp_fc_wka_port * Returns: 0 on success, error otherwise */ -int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port) +int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) { struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = wka_port->adapter->qdio; @@ -1677,7 +1664,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port) sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; req->handler = zfcp_fsf_open_wka_port_handler; - req->qtcb->bottom.support.d_id = wka_port->d_id; + hton24(req->qtcb->bottom.support.d_id, wka_port->d_id); req->data = wka_port; zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); @@ -1691,23 +1678,23 @@ out: static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) { - struct zfcp_wka_port *wka_port = req->data; + struct zfcp_fc_wka_port *wka_port = req->data; if (req->qtcb->header.fsf_status == FSF_PORT_HANDLE_NOT_VALID) { req->status |= ZFCP_STATUS_FSFREQ_ERROR; zfcp_erp_adapter_reopen(wka_port->adapter, 0, "fscwph1", req); } - wka_port->status = ZFCP_WKA_PORT_OFFLINE; + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; wake_up(&wka_port->completion_wq); } /** * zfcp_fsf_close_wka_port - create and send close wka port request - * @erp_action: pointer to struct zfcp_erp_action + * @wka_port: WKA port to open * Returns: 0 on success, error otherwise */ -int zfcp_fsf_close_wka_port(struct zfcp_wka_port *wka_port) +int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) { struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = wka_port->adapter->qdio; @@ -1765,13 +1752,13 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req) /* can't use generic zfcp_erp_modify_port_status because * ZFCP_STATUS_COMMON_OPEN must not be reset for the port */ atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status); - list_for_each_entry(unit, &port->unit_list_head, list) + read_lock(&port->unit_list_lock); + list_for_each_entry(unit, &port->unit_list, list) atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status); + read_unlock(&port->unit_list_lock); zfcp_erp_port_boxed(port, "fscpph2", req); - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; - + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: switch (header->fsf_status_qual.word[0]) { @@ -1787,9 +1774,11 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req) * ZFCP_STATUS_COMMON_OPEN must not be reset for the port */ atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status); - list_for_each_entry(unit, &port->unit_list_head, list) + read_lock(&port->unit_list_lock); + list_for_each_entry(unit, &port->unit_list, list) atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status); + read_unlock(&port->unit_list_lock); break; } } @@ -1873,8 +1862,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req) break; case FSF_PORT_BOXED: zfcp_erp_port_boxed(unit->port, "fsouh_2", req); - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_LUN_SHARING_VIOLATION: if (header->fsf_status_qual.word[0]) @@ -2036,8 +2024,7 @@ static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req) break; case FSF_PORT_BOXED: zfcp_erp_port_boxed(unit->port, "fscuh_3", req); - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: switch (req->qtcb->header.fsf_status_qual.word[0]) { @@ -2109,72 +2096,57 @@ static void zfcp_fsf_update_lat(struct fsf_latency_record *lat_rec, u32 lat) lat_rec->max = max(lat_rec->max, lat); } -static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req) +static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi) { - struct fsf_qual_latency_info *lat_inf; - struct latency_cont *lat; + struct fsf_qual_latency_info *lat_in; + struct latency_cont *lat = NULL; struct zfcp_unit *unit = req->unit; + struct zfcp_blk_drv_data blktrc; + int ticks = req->adapter->timer_ticks; - lat_inf = &req->qtcb->prefix.prot_status_qual.latency_info; - - switch (req->qtcb->bottom.io.data_direction) { - case FSF_DATADIR_READ: - lat = &unit->latencies.read; - break; - case FSF_DATADIR_WRITE: - lat = &unit->latencies.write; - break; - case FSF_DATADIR_CMND: - lat = &unit->latencies.cmd; - break; - default: - return; - } + lat_in = &req->qtcb->prefix.prot_status_qual.latency_info; - spin_lock(&unit->latencies.lock); - zfcp_fsf_update_lat(&lat->channel, lat_inf->channel_lat); - zfcp_fsf_update_lat(&lat->fabric, lat_inf->fabric_lat); - lat->counter++; - spin_unlock(&unit->latencies.lock); -} - -#ifdef CONFIG_BLK_DEV_IO_TRACE -static void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req) -{ - struct fsf_qual_latency_info *lat_inf; - struct scsi_cmnd *scsi_cmnd = (struct scsi_cmnd *)fsf_req->data; - struct request *req = scsi_cmnd->request; - struct zfcp_blk_drv_data trace; - int ticks = fsf_req->adapter->timer_ticks; + blktrc.flags = 0; + blktrc.magic = ZFCP_BLK_DRV_DATA_MAGIC; + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + blktrc.flags |= ZFCP_BLK_REQ_ERROR; + blktrc.inb_usage = req->queue_req.qdio_inb_usage; + blktrc.outb_usage = req->queue_req.qdio_outb_usage; + + if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) { + blktrc.flags |= ZFCP_BLK_LAT_VALID; + blktrc.channel_lat = lat_in->channel_lat * ticks; + blktrc.fabric_lat = lat_in->fabric_lat * ticks; + + switch (req->qtcb->bottom.io.data_direction) { + case FSF_DATADIR_READ: + lat = &unit->latencies.read; + break; + case FSF_DATADIR_WRITE: + lat = &unit->latencies.write; + break; + case FSF_DATADIR_CMND: + lat = &unit->latencies.cmd; + break; + } - trace.flags = 0; - trace.magic = ZFCP_BLK_DRV_DATA_MAGIC; - if (fsf_req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) { - trace.flags |= ZFCP_BLK_LAT_VALID; - lat_inf = &fsf_req->qtcb->prefix.prot_status_qual.latency_info; - trace.channel_lat = lat_inf->channel_lat * ticks; - trace.fabric_lat = lat_inf->fabric_lat * ticks; + if (lat) { + spin_lock(&unit->latencies.lock); + zfcp_fsf_update_lat(&lat->channel, lat_in->channel_lat); + zfcp_fsf_update_lat(&lat->fabric, lat_in->fabric_lat); + lat->counter++; + spin_unlock(&unit->latencies.lock); + } } - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) - trace.flags |= ZFCP_BLK_REQ_ERROR; - trace.inb_usage = fsf_req->queue_req.qdio_inb_usage; - trace.outb_usage = fsf_req->queue_req.qdio_outb_usage; - blk_add_driver_data(req->q, req, &trace, sizeof(trace)); -} -#else -static inline void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req) -{ + blk_add_driver_data(scsi->request->q, scsi->request, &blktrc, + sizeof(blktrc)); } -#endif static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req) { struct scsi_cmnd *scpnt; - struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *) - &(req->qtcb->bottom.io.fcp_rsp); - u32 sns_len; - char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1]; + struct fcp_resp_with_ext *fcp_rsp; unsigned long flags; read_lock_irqsave(&req->adapter->abort_lock, flags); @@ -2185,50 +2157,16 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req) return; } - if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ABORTED)) { - set_host_byte(scpnt, DID_SOFT_ERROR); - goto skip_fsfstatus; - } - if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) { - set_host_byte(scpnt, DID_ERROR); + set_host_byte(scpnt, DID_TRANSPORT_DISRUPTED); goto skip_fsfstatus; } - set_msg_byte(scpnt, COMMAND_COMPLETE); - - scpnt->result |= fcp_rsp_iu->scsi_status; + fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp; + zfcp_fc_eval_fcp_rsp(fcp_rsp, scpnt); - if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) - zfcp_fsf_req_latency(req); - - zfcp_fsf_trace_latency(req); - - if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) { - if (fcp_rsp_info[3] == RSP_CODE_GOOD) - set_host_byte(scpnt, DID_OK); - else { - set_host_byte(scpnt, DID_ERROR); - goto skip_fsfstatus; - } - } - - if (unlikely(fcp_rsp_iu->validity.bits.fcp_sns_len_valid)) { - sns_len = FSF_FCP_RSP_SIZE - sizeof(struct fcp_rsp_iu) + - fcp_rsp_iu->fcp_rsp_len; - sns_len = min(sns_len, (u32) SCSI_SENSE_BUFFERSIZE); - sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len); - - memcpy(scpnt->sense_buffer, - zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), sns_len); - } + zfcp_fsf_req_trace(req, scpnt); - if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_under)) { - scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid); - if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) < - scpnt->underflow) - set_host_byte(scpnt, DID_ERROR); - } skip_fsfstatus: if (scpnt->result != 0) zfcp_dbf_scsi_result("erro", 3, req->adapter->dbf, scpnt, req); @@ -2250,11 +2188,13 @@ skip_fsfstatus: static void zfcp_fsf_send_fcp_ctm_handler(struct zfcp_fsf_req *req) { - struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *) - &(req->qtcb->bottom.io.fcp_rsp); - char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1]; + struct fcp_resp_with_ext *fcp_rsp; + struct fcp_resp_rsp_info *rsp_info; + + fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp; + rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1]; - if ((fcp_rsp_info[3] != RSP_CODE_GOOD) || + if ((rsp_info->rsp_code != FCP_TMF_CMPL) || (req->status & ZFCP_STATUS_FSFREQ_ERROR)) req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED; } @@ -2314,13 +2254,11 @@ static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req) break; case FSF_PORT_BOXED: zfcp_erp_port_boxed(unit->port, "fssfch5", req); - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_LUN_BOXED: zfcp_erp_unit_boxed(unit, "fssfch6", req); - req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: if (header->fsf_status_qual.word[0] == @@ -2335,24 +2273,10 @@ skip_fsfstatus: else { zfcp_fsf_send_fcp_command_task_handler(req); req->unit = NULL; - zfcp_unit_put(unit); + put_device(&unit->sysfs_device); } } -static void zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, u32 fcp_dl) -{ - u32 *fcp_dl_ptr; - - /* - * fcp_dl_addr = start address of fcp_cmnd structure + - * size of fixed part + size of dynamically sized add_dcp_cdb field - * SEE FCP-2 documentation - */ - fcp_dl_ptr = (u32 *) ((unsigned char *) &fcp_cmd[1] + - (fcp_cmd->add_fcp_cdb_length << 2)); - *fcp_dl_ptr = fcp_dl; -} - /** * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command) * @unit: unit where command is sent to @@ -2362,7 +2286,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, struct scsi_cmnd *scsi_cmnd) { struct zfcp_fsf_req *req; - struct fcp_cmnd_iu *fcp_cmnd_iu; + struct fcp_cmnd *fcp_cmnd; unsigned int sbtype = SBAL_FLAGS0_TYPE_READ; int real_bytes, retval = -EIO; struct zfcp_adapter *adapter = unit->port->adapter; @@ -2387,23 +2311,21 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - zfcp_unit_get(unit); + get_device(&unit->sysfs_device); req->unit = unit; req->data = scsi_cmnd; req->handler = zfcp_fsf_send_fcp_command_handler; req->qtcb->header.lun_handle = unit->handle; req->qtcb->header.port_handle = unit->port->handle; req->qtcb->bottom.io.service_class = FSF_CLASS_3; + req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN; scsi_cmnd->host_scribble = (unsigned char *) req->req_id; - fcp_cmnd_iu = (struct fcp_cmnd_iu *) &(req->qtcb->bottom.io.fcp_cmnd); - fcp_cmnd_iu->fcp_lun = unit->fcp_lun; /* * set depending on data direction: * data direction bits in SBALE (SB Type) * data direction bits in QTCB - * data direction bits in FCP_CMND IU */ switch (scsi_cmnd->sc_data_direction) { case DMA_NONE: @@ -2411,32 +2333,17 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, break; case DMA_FROM_DEVICE: req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ; - fcp_cmnd_iu->rddata = 1; break; case DMA_TO_DEVICE: req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE; sbtype = SBAL_FLAGS0_TYPE_WRITE; - fcp_cmnd_iu->wddata = 1; break; case DMA_BIDIRECTIONAL: goto failed_scsi_cmnd; } - if (likely((scsi_cmnd->device->simple_tags) || - ((atomic_read(&unit->status) & ZFCP_STATUS_UNIT_READONLY) && - (atomic_read(&unit->status) & ZFCP_STATUS_UNIT_SHARED)))) - fcp_cmnd_iu->task_attribute = SIMPLE_Q; - else - fcp_cmnd_iu->task_attribute = UNTAGGED; - - if (unlikely(scsi_cmnd->cmd_len > FCP_CDB_LENGTH)) - fcp_cmnd_iu->add_fcp_cdb_length = - (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2; - - memcpy(fcp_cmnd_iu->fcp_cdb, scsi_cmnd->cmnd, scsi_cmnd->cmd_len); - - req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) + - fcp_cmnd_iu->add_fcp_cdb_length + sizeof(u32); + fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd; + zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd); real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->queue_req, sbtype, scsi_sglist(scsi_cmnd), @@ -2454,8 +2361,6 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, goto failed_scsi_cmnd; } - zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes); - retval = zfcp_fsf_req_send(req); if (unlikely(retval)) goto failed_scsi_cmnd; @@ -2463,7 +2368,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, goto out; failed_scsi_cmnd: - zfcp_unit_put(unit); + put_device(&unit->sysfs_device); zfcp_fsf_req_free(req); scsi_cmnd->host_scribble = NULL; out: @@ -2481,7 +2386,7 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags) { struct qdio_buffer_element *sbale; struct zfcp_fsf_req *req = NULL; - struct fcp_cmnd_iu *fcp_cmnd_iu; + struct fcp_cmnd *fcp_cmnd; struct zfcp_qdio *qdio = unit->port->adapter->qdio; if (unlikely(!(atomic_read(&unit->status) & @@ -2507,16 +2412,14 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags) req->qtcb->header.port_handle = unit->port->handle; req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND; req->qtcb->bottom.io.service_class = FSF_CLASS_3; - req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) + - sizeof(u32); + req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN; sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req); sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE; sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; - fcp_cmnd_iu = (struct fcp_cmnd_iu *) &req->qtcb->bottom.io.fcp_cmnd; - fcp_cmnd_iu->fcp_lun = unit->fcp_lun; - fcp_cmnd_iu->task_management_flags = tm_flags; + fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd; + zfcp_fc_fcp_tm(fcp_cmnd, unit->device, tm_flags); zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT); if (!zfcp_fsf_req_send(req)) diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index dcc7c1dbcf58..b3de682b64cf 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -11,6 +11,7 @@ #include <linux/pfn.h> #include <linux/scatterlist.h> +#include <scsi/libfc.h> #define FSF_QTCB_CURRENT_VERSION 0x00000001 @@ -228,7 +229,8 @@ struct fsf_status_read_buffer { u32 length; u32 res1; struct fsf_queue_designator queue_designator; - u32 d_id; + u8 res2; + u8 d_id[3]; u32 class; u64 fcp_lun; u8 res3[24]; @@ -309,22 +311,7 @@ struct fsf_qtcb_header { u8 res4[16]; } __attribute__ ((packed)); -struct fsf_nport_serv_param { - u8 common_serv_param[16]; - u64 wwpn; - u64 wwnn; - u8 class1_serv_param[16]; - u8 class2_serv_param[16]; - u8 class3_serv_param[16]; - u8 class4_serv_param[16]; - u8 vendor_version_level[16]; -} __attribute__ ((packed)); - #define FSF_PLOGI_MIN_LEN 112 -struct fsf_plogi { - u32 code; - struct fsf_nport_serv_param serv_param; -} __attribute__ ((packed)); #define FSF_FCP_CMND_SIZE 288 #define FSF_FCP_RSP_SIZE 128 @@ -342,8 +329,8 @@ struct fsf_qtcb_bottom_io { struct fsf_qtcb_bottom_support { u32 operation_subtype; - u8 res1[12]; - u32 d_id; + u8 res1[13]; + u8 d_id[3]; u32 option; u64 fcp_lun; u64 res2; @@ -372,18 +359,18 @@ struct fsf_qtcb_bottom_config { u32 fc_topology; u32 fc_link_speed; u32 adapter_type; - u32 peer_d_id; + u8 res0; + u8 peer_d_id[3]; u8 res1[2]; u16 timer_interval; - u8 res2[8]; - u32 s_id; - struct fsf_nport_serv_param nport_serv_param; - u8 reserved_nport_serv_param[16]; + u8 res2[9]; + u8 s_id[3]; + u8 nport_serv_param[128]; u8 res3[8]; u32 adapter_ports; u32 hardware_version; u8 serial_number[32]; - struct fsf_nport_serv_param plogi_payload; + u8 plogi_payload[112]; struct fsf_statistics_info stat_info; u8 res4[112]; } __attribute__ ((packed)); @@ -450,4 +437,22 @@ struct zfcp_blk_drv_data { u64 fabric_lat; } __attribute__ ((packed)); +/** + * struct zfcp_fsf_ct_els - zfcp data for ct or els request + * @req: scatter-gather list for request + * @resp: scatter-gather list for response + * @handler: handler function (called for response to the request) + * @handler_data: data passed to handler function + * @port: Optional pointer to port for zfcp internal ELS (only test link ADISC) + * @status: used to pass error status to calling function + */ +struct zfcp_fsf_ct_els { + struct scatterlist *req; + struct scatterlist *resp; + void (*handler)(void *); + void *handler_data; + struct zfcp_port *port; + int status; +}; + #endif /* FSF_H */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 0e1a34627a2e..771cc536a989 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -9,29 +9,33 @@ #define KMSG_COMPONENT "zfcp" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/types.h> +#include <scsi/fc/fc_fcp.h> #include <asm/atomic.h> #include "zfcp_ext.h" #include "zfcp_dbf.h" +#include "zfcp_fc.h" static unsigned int default_depth = 32; module_param_named(queue_depth, default_depth, uint, 0600); MODULE_PARM_DESC(queue_depth, "Default queue depth for new SCSI devices"); -/* Find start of Sense Information in FCP response unit*/ -char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu) +static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth, + int reason) { - char *fcp_sns_info_ptr; - - fcp_sns_info_ptr = (unsigned char *) &fcp_rsp_iu[1]; - if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid) - fcp_sns_info_ptr += fcp_rsp_iu->fcp_rsp_len; - - return fcp_sns_info_ptr; -} - -static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth) -{ - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + switch (reason) { + case SCSI_QDEPTH_DEFAULT: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + case SCSI_QDEPTH_QFULL: + scsi_track_queue_full(sdev, depth); + break; + case SCSI_QDEPTH_RAMP_UP: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + default: + return -EOPNOTSUPP; + } return sdev->queue_depth; } @@ -39,7 +43,7 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt) { struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata; unit->device = NULL; - zfcp_unit_put(unit); + put_device(&unit->sysfs_device); } static int zfcp_scsi_slave_configure(struct scsi_device *sdp) @@ -99,12 +103,26 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt, } status = atomic_read(&unit->status); - if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) || - !(status & ZFCP_STATUS_COMMON_RUNNING))) { + if (unlikely(status & ZFCP_STATUS_COMMON_ERP_FAILED) && + !(atomic_read(&unit->port->status) & + ZFCP_STATUS_COMMON_ERP_FAILED)) { + /* only unit access denied, but port is good + * not covered by FC transport, have to fail here */ zfcp_scsi_command_fail(scpnt, DID_ERROR); return 0; } + if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) { + /* This could be either + * open unit pending: this is temporary, will result in + * open unit or ERP_FAILED, so retry command + * call to rport_delete pending: mimic retry from + * fc_remote_port_chkready until rport is BLOCKED + */ + zfcp_scsi_command_fail(scpnt, DID_IMM_RETRY); + return 0; + } + ret = zfcp_fsf_send_fcp_command_task(unit, scpnt); if (unlikely(ret == -EBUSY)) return SCSI_MLQUEUE_DEVICE_BUSY; @@ -115,49 +133,44 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt, } static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *adapter, - int channel, unsigned int id, - unsigned int lun) + unsigned int id, u64 lun) { + unsigned long flags; struct zfcp_port *port; - struct zfcp_unit *unit; - int scsi_lun; + struct zfcp_unit *unit = NULL; - list_for_each_entry(port, &adapter->port_list_head, list) { + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) { if (!port->rport || (id != port->rport->scsi_target_id)) continue; - list_for_each_entry(unit, &port->unit_list_head, list) { - scsi_lun = scsilun_to_int( - (struct scsi_lun *)&unit->fcp_lun); - if (lun == scsi_lun) - return unit; - } + unit = zfcp_get_unit_by_lun(port, lun); + if (unit) + break; } + read_unlock_irqrestore(&adapter->port_list_lock, flags); - return NULL; + return unit; } static int zfcp_scsi_slave_alloc(struct scsi_device *sdp) { struct zfcp_adapter *adapter; struct zfcp_unit *unit; - unsigned long flags; - int retval = -ENXIO; + u64 lun; adapter = (struct zfcp_adapter *) sdp->host->hostdata[0]; if (!adapter) goto out; - read_lock_irqsave(&zfcp_data.config_lock, flags); - unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun); + int_to_scsilun(sdp->lun, (struct scsi_lun *)&lun); + unit = zfcp_unit_lookup(adapter, sdp->id, lun); if (unit) { sdp->hostdata = unit; unit->device = sdp; - zfcp_unit_get(unit); - retval = 0; + return 0; } - read_unlock_irqrestore(&zfcp_data.config_lock, flags); out: - return retval; + return -ENXIO; } static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) @@ -196,6 +209,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) break; zfcp_erp_wait(adapter); + fc_block_scsi_eh(scpnt); if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { zfcp_dbf_scsi_abort("nres", adapter->dbf, scpnt, NULL, @@ -235,6 +249,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) break; zfcp_erp_wait(adapter); + fc_block_scsi_eh(scpnt); if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { zfcp_dbf_scsi_devreset("nres", tm_flags, unit, scpnt); @@ -249,9 +264,6 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) { zfcp_dbf_scsi_devreset("fail", tm_flags, unit, scpnt); retval = FAILED; - } else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) { - zfcp_dbf_scsi_devreset("nsup", tm_flags, unit, scpnt); - retval = FAILED; } else zfcp_dbf_scsi_devreset("okay", tm_flags, unit, scpnt); @@ -261,12 +273,12 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) { - return zfcp_task_mgmt_function(scpnt, FCP_LOGICAL_UNIT_RESET); + return zfcp_task_mgmt_function(scpnt, FCP_TMF_LUN_RESET); } static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt) { - return zfcp_task_mgmt_function(scpnt, FCP_TARGET_RESET); + return zfcp_task_mgmt_function(scpnt, FCP_TMF_TGT_RESET); } static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) @@ -276,6 +288,7 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) zfcp_erp_adapter_reopen(adapter, 0, "schrh_1", scpnt); zfcp_erp_wait(adapter); + fc_block_scsi_eh(scpnt); return SUCCESS; } @@ -303,7 +316,7 @@ int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter) adapter->scsi_host->max_lun = 1; adapter->scsi_host->max_channel = 0; adapter->scsi_host->unique_id = dev_id.devno; - adapter->scsi_host->max_cmd_len = 255; + adapter->scsi_host->max_cmd_len = 16; /* in struct fcp_cmnd */ adapter->scsi_host->transportt = zfcp_data.scsi_transport_template; adapter->scsi_host->hostdata[0] = (unsigned long) adapter; @@ -325,12 +338,11 @@ void zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter) if (!shost) return; - read_lock_irq(&zfcp_data.config_lock); - list_for_each_entry(port, &adapter->port_list_head, list) - if (port->rport) - port->rport = NULL; + read_lock_irq(&adapter->port_list_lock); + list_for_each_entry(port, &adapter->port_list, list) + port->rport = NULL; + read_unlock_irq(&adapter->port_list_lock); - read_unlock_irq(&zfcp_data.config_lock); fc_remove_host(shost); scsi_remove_host(shost); scsi_host_put(shost); @@ -348,7 +360,7 @@ zfcp_init_fc_host_stats(struct zfcp_adapter *adapter) fc_stats = kmalloc(sizeof(*fc_stats), GFP_KERNEL); if (!fc_stats) return NULL; - adapter->fc_stats = fc_stats; /* freed in adater_dequeue */ + adapter->fc_stats = fc_stats; /* freed in adapter_release */ } memset(adapter->fc_stats, 0, sizeof(*adapter->fc_stats)); return adapter->fc_stats; @@ -464,7 +476,7 @@ static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost) adapter->stats_reset = jiffies/HZ; kfree(adapter->stats_reset_data); adapter->stats_reset_data = data; /* finally freed in - adapter_dequeue */ + adapter_release */ } } @@ -495,7 +507,7 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) * @rport: The FC rport where to teminate I/O * * Abort all pending SCSI commands for a port by closing the - * port. Using a reopen for avoids a conflict with a shutdown + * port. Using a reopen avoiding a conflict with a shutdown * overwriting a reopen. */ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) @@ -505,15 +517,11 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0]; - write_lock_irq(&zfcp_data.config_lock); port = zfcp_get_port_by_wwpn(adapter, rport->port_name); - if (port) - zfcp_port_get(port); - write_unlock_irq(&zfcp_data.config_lock); if (port) { zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL); - zfcp_port_put(port); + put_device(&port->sysfs_device); } } @@ -555,31 +563,34 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port) void zfcp_scsi_schedule_rport_register(struct zfcp_port *port) { - zfcp_port_get(port); + get_device(&port->sysfs_device); port->rport_task = RPORT_ADD; if (!queue_work(port->adapter->work_queue, &port->rport_work)) - zfcp_port_put(port); + put_device(&port->sysfs_device); } void zfcp_scsi_schedule_rport_block(struct zfcp_port *port) { - zfcp_port_get(port); + get_device(&port->sysfs_device); port->rport_task = RPORT_DEL; if (port->rport && queue_work(port->adapter->work_queue, &port->rport_work)) return; - zfcp_port_put(port); + put_device(&port->sysfs_device); } void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter) { + unsigned long flags; struct zfcp_port *port; - list_for_each_entry(port, &adapter->port_list_head, list) + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) zfcp_scsi_schedule_rport_block(port); + read_unlock_irqrestore(&adapter->port_list_lock, flags); } void zfcp_scsi_rport_work(struct work_struct *work) @@ -597,7 +608,7 @@ void zfcp_scsi_rport_work(struct work_struct *work) } } - zfcp_port_put(port); + put_device(&port->sysfs_device); } @@ -615,21 +626,7 @@ void zfcp_scsi_scan(struct work_struct *work) scsilun_to_int((struct scsi_lun *) &unit->fcp_lun), 0); - zfcp_unit_put(unit); -} - -static int zfcp_execute_fc_job(struct fc_bsg_job *job) -{ - switch (job->request->msgcode) { - case FC_BSG_RPT_ELS: - case FC_BSG_HST_ELS_NOLOGIN: - return zfcp_fc_execute_els_fc_job(job); - case FC_BSG_RPT_CT: - case FC_BSG_HST_CT: - return zfcp_fc_execute_ct_fc_job(job); - default: - return -EINVAL; - } + put_device(&unit->sysfs_device); } struct fc_function_template zfcp_transport_functions = { @@ -643,6 +640,7 @@ struct fc_function_template zfcp_transport_functions = { .show_host_port_name = 1, .show_host_permanent_port_name = 1, .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, .show_host_supported_speeds = 1, .show_host_maxframe_size = 1, .show_host_serial_number = 1, @@ -652,13 +650,15 @@ struct fc_function_template zfcp_transport_functions = { .get_host_port_state = zfcp_get_host_port_state, .terminate_rport_io = zfcp_scsi_terminate_rport_io, .show_host_port_state = 1, - .bsg_request = zfcp_execute_fc_job, + .show_host_active_fc4s = 1, + .bsg_request = zfcp_fc_exec_bsg_job, /* no functions registered for following dynamic attributes but directly set by LLDD */ .show_host_port_type = 1, .show_host_speed = 1, .show_host_port_id = 1, .disable_target_scan = 1, + .dd_bsg_size = sizeof(struct zfcp_fsf_ct_els), }; struct zfcp_data zfcp_data = { diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index d31000886ca8..f539e006683c 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -3,7 +3,7 @@ * * sysfs attributes. * - * Copyright IBM Corporation 2008 + * Copyright IBM Corporation 2008, 2009 */ #define KMSG_COMPONENT "zfcp" @@ -19,30 +19,44 @@ static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \ struct device_attribute *at,\ char *buf) \ { \ - struct _feat_def *_feat = dev_get_drvdata(dev); \ + struct _feat_def *_feat = container_of(dev, struct _feat_def, \ + sysfs_device); \ \ return sprintf(buf, _format, _value); \ } \ static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \ zfcp_sysfs_##_feat##_##_name##_show, NULL); -ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, status, "0x%08x\n", - atomic_read(&adapter->status)); -ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwnn, "0x%016llx\n", - (unsigned long long) adapter->peer_wwnn); -ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwpn, "0x%016llx\n", - (unsigned long long) adapter->peer_wwpn); -ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_d_id, "0x%06x\n", - adapter->peer_d_id); -ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, card_version, "0x%04x\n", - adapter->hydra_version); -ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, lic_version, "0x%08x\n", - adapter->fsf_lic_version); -ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, hardware_version, "0x%08x\n", - adapter->hardware_version); -ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, in_recovery, "%d\n", - (atomic_read(&adapter->status) & - ZFCP_STATUS_COMMON_ERP_INUSE) != 0); +#define ZFCP_DEFINE_A_ATTR(_name, _format, _value) \ +static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \ + struct device_attribute *at,\ + char *buf) \ +{ \ + struct ccw_device *cdev = to_ccwdev(dev); \ + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); \ + int i; \ + \ + if (!adapter) \ + return -ENODEV; \ + \ + i = sprintf(buf, _format, _value); \ + zfcp_ccw_adapter_put(adapter); \ + return i; \ +} \ +static ZFCP_DEV_ATTR(adapter, _name, S_IRUGO, \ + zfcp_sysfs_adapter_##_name##_show, NULL); + +ZFCP_DEFINE_A_ATTR(status, "0x%08x\n", atomic_read(&adapter->status)); +ZFCP_DEFINE_A_ATTR(peer_wwnn, "0x%016llx\n", + (unsigned long long) adapter->peer_wwnn); +ZFCP_DEFINE_A_ATTR(peer_wwpn, "0x%016llx\n", + (unsigned long long) adapter->peer_wwpn); +ZFCP_DEFINE_A_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id); +ZFCP_DEFINE_A_ATTR(card_version, "0x%04x\n", adapter->hydra_version); +ZFCP_DEFINE_A_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version); +ZFCP_DEFINE_A_ATTR(hardware_version, "0x%08x\n", adapter->hardware_version); +ZFCP_DEFINE_A_ATTR(in_recovery, "%d\n", (atomic_read(&adapter->status) & + ZFCP_STATUS_COMMON_ERP_INUSE) != 0); ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n", atomic_read(&port->status)); @@ -73,7 +87,8 @@ static ssize_t zfcp_sysfs_##_feat##_failed_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct _feat_def *_feat = dev_get_drvdata(dev); \ + struct _feat_def *_feat = container_of(dev, struct _feat_def, \ + sysfs_device); \ \ if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_ERP_FAILED) \ return sprintf(buf, "1\n"); \ @@ -84,15 +99,13 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \ struct device_attribute *attr,\ const char *buf, size_t count)\ { \ - struct _feat_def *_feat = dev_get_drvdata(dev); \ + struct _feat_def *_feat = container_of(dev, struct _feat_def, \ + sysfs_device); \ unsigned long val; \ int retval = 0; \ \ - mutex_lock(&zfcp_data.config_mutex); \ - if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_REMOVE) { \ - retval = -EBUSY; \ - goto out; \ - } \ + if (!(_feat && get_device(&_feat->sysfs_device))) \ + return -EBUSY; \ \ if (strict_strtoul(buf, 0, &val) || val != 0) { \ retval = -EINVAL; \ @@ -105,29 +118,82 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \ _reopen_id, NULL); \ zfcp_erp_wait(_adapter); \ out: \ - mutex_unlock(&zfcp_data.config_mutex); \ + put_device(&_feat->sysfs_device); \ return retval ? retval : (ssize_t) count; \ } \ static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO, \ zfcp_sysfs_##_feat##_failed_show, \ zfcp_sysfs_##_feat##_failed_store); -ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, "syafai1", "syafai2"); ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, "sypfai1", "sypfai2"); ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, "syufai1", "syufai2"); +static ssize_t zfcp_sysfs_adapter_failed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + int i; + + if (!adapter) + return -ENODEV; + + if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + i = sprintf(buf, "1\n"); + else + i = sprintf(buf, "0\n"); + + zfcp_ccw_adapter_put(adapter); + return i; +} + +static ssize_t zfcp_sysfs_adapter_failed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + unsigned long val; + int retval = 0; + + if (!adapter) + return -ENODEV; + + if (strict_strtoul(buf, 0, &val) || val != 0) { + retval = -EINVAL; + goto out; + } + + zfcp_erp_modify_adapter_status(adapter, "syafai1", NULL, + ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, + "syafai2", NULL); + zfcp_erp_wait(adapter); +out: + zfcp_ccw_adapter_put(adapter); + return retval ? retval : (ssize_t) count; +} +static ZFCP_DEV_ATTR(adapter, failed, S_IWUSR | S_IRUGO, + zfcp_sysfs_adapter_failed_show, + zfcp_sysfs_adapter_failed_store); + static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct zfcp_adapter *adapter = dev_get_drvdata(dev); - int ret; + struct ccw_device *cdev = to_ccwdev(dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); - if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) - return -EBUSY; + if (!adapter) + return -ENODEV; - ret = zfcp_fc_scan_ports(adapter); - return ret ? ret : (ssize_t) count; + /* sync the user-space- with the kernel-invocation of scan_work */ + queue_work(adapter->work_queue, &adapter->scan_work); + flush_work(&adapter->scan_work); + zfcp_ccw_adapter_put(adapter); + + return (ssize_t) count; } static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, zfcp_sysfs_port_rescan_store); @@ -136,44 +202,34 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct zfcp_adapter *adapter = dev_get_drvdata(dev); + struct ccw_device *cdev = to_ccwdev(dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); struct zfcp_port *port; u64 wwpn; - int retval = 0; - LIST_HEAD(port_remove_lh); + int retval = -EINVAL; - mutex_lock(&zfcp_data.config_mutex); - if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) { - retval = -EBUSY; - goto out; - } + if (!adapter) + return -ENODEV; - if (strict_strtoull(buf, 0, (unsigned long long *) &wwpn)) { - retval = -EINVAL; + if (strict_strtoull(buf, 0, (unsigned long long *) &wwpn)) goto out; - } - write_lock_irq(&zfcp_data.config_lock); port = zfcp_get_port_by_wwpn(adapter, wwpn); - if (port && (atomic_read(&port->refcount) == 0)) { - zfcp_port_get(port); - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); - list_move(&port->list, &port_remove_lh); - } else - port = NULL; - write_unlock_irq(&zfcp_data.config_lock); - - if (!port) { - retval = -ENXIO; + if (!port) goto out; - } + else + retval = 0; + + write_lock_irq(&adapter->port_list_lock); + list_del(&port->list); + write_unlock_irq(&adapter->port_list_lock); + + put_device(&port->sysfs_device); zfcp_erp_port_shutdown(port, 0, "syprs_1", NULL); - zfcp_erp_wait(adapter); - zfcp_port_put(port); - zfcp_port_dequeue(port); + zfcp_device_unregister(&port->sysfs_device, &zfcp_sysfs_port_attrs); out: - mutex_unlock(&zfcp_data.config_mutex); + zfcp_ccw_adapter_put(adapter); return retval ? retval : (ssize_t) count; } static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL, @@ -202,16 +258,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct zfcp_port *port = dev_get_drvdata(dev); + struct zfcp_port *port = container_of(dev, struct zfcp_port, + sysfs_device); struct zfcp_unit *unit; u64 fcp_lun; int retval = -EINVAL; - mutex_lock(&zfcp_data.config_mutex); - if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) { - retval = -EBUSY; - goto out; - } + if (!(port && get_device(&port->sysfs_device))) + return -EBUSY; if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) goto out; @@ -219,15 +273,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev, unit = zfcp_unit_enqueue(port, fcp_lun); if (IS_ERR(unit)) goto out; - - retval = 0; + else + retval = 0; zfcp_erp_unit_reopen(unit, 0, "syuas_1", NULL); zfcp_erp_wait(unit->port->adapter); flush_work(&unit->scsi_work); - zfcp_unit_put(unit); out: - mutex_unlock(&zfcp_data.config_mutex); + put_device(&port->sysfs_device); return retval ? retval : (ssize_t) count; } static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store); @@ -236,54 +289,37 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct zfcp_port *port = dev_get_drvdata(dev); + struct zfcp_port *port = container_of(dev, struct zfcp_port, + sysfs_device); struct zfcp_unit *unit; u64 fcp_lun; - int retval = 0; - LIST_HEAD(unit_remove_lh); + int retval = -EINVAL; - mutex_lock(&zfcp_data.config_mutex); - if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) { - retval = -EBUSY; - goto out; - } + if (!(port && get_device(&port->sysfs_device))) + return -EBUSY; - if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) { - retval = -EINVAL; + if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) goto out; - } - write_lock_irq(&zfcp_data.config_lock); unit = zfcp_get_unit_by_lun(port, fcp_lun); - if (unit) { - write_unlock_irq(&zfcp_data.config_lock); - /* wait for possible timeout during SCSI probe */ - flush_work(&unit->scsi_work); - write_lock_irq(&zfcp_data.config_lock); - - if (atomic_read(&unit->refcount) == 0) { - zfcp_unit_get(unit); - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, - &unit->status); - list_move(&unit->list, &unit_remove_lh); - } else { - unit = NULL; - } - } + if (!unit) + goto out; + else + retval = 0; - write_unlock_irq(&zfcp_data.config_lock); + /* wait for possible timeout during SCSI probe */ + flush_work(&unit->scsi_work); - if (!unit) { - retval = -ENXIO; - goto out; - } + write_lock_irq(&port->unit_list_lock); + list_del(&unit->list); + write_unlock_irq(&port->unit_list_lock); + + put_device(&unit->sysfs_device); zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL); - zfcp_erp_wait(unit->port->adapter); - zfcp_unit_put(unit); - zfcp_unit_dequeue(unit); + zfcp_device_unregister(&unit->sysfs_device, &zfcp_sysfs_unit_attrs); out: - mutex_unlock(&zfcp_data.config_mutex); + put_device(&port->sysfs_device); return retval ? retval : (ssize_t) count; } static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store); diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index 58e583b61e60..aa2b60a868ba 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -92,11 +92,11 @@ #define ENVCTRL_CPUTEMP_MON 1 /* cpu temperature monitor */ #define ENVCTRL_CPUVOLTAGE_MON 2 /* voltage monitor */ #define ENVCTRL_FANSTAT_MON 3 /* fan status monitor */ -#define ENVCTRL_ETHERTEMP_MON 4 /* ethernet temperarture */ +#define ENVCTRL_ETHERTEMP_MON 4 /* ethernet temperature */ /* monitor */ #define ENVCTRL_VOLTAGESTAT_MON 5 /* voltage status monitor */ #define ENVCTRL_MTHRBDTEMP_MON 6 /* motherboard temperature */ -#define ENVCTRL_SCSITEMP_MON 7 /* scsi temperarture */ +#define ENVCTRL_SCSITEMP_MON 7 /* scsi temperature */ #define ENVCTRL_GLOBALADDR_MON 8 /* global address */ /* Child device type. diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index 36c21b19e5d7..3bf75924741f 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -186,8 +186,12 @@ static ssize_t twa_show_stats(struct device *dev, } /* End twa_show_stats() */ /* This function will set a devices queue depth */ -static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth) +static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (queue_depth > TW_Q_LENGTH-2) queue_depth = TW_Q_LENGTH-2; scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); @@ -732,7 +736,7 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int break; case TW_IOCTL_GET_COMPATIBILITY_INFO: tw_ioctl->driver_command.status = 0; - /* Copy compatiblity struct into ioctl data buffer */ + /* Copy compatibility struct into ioctl data buffer */ tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer; memcpy(tw_compat_info, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info)); break; diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c new file mode 100644 index 000000000000..4d314d740de4 --- /dev/null +++ b/drivers/scsi/3w-sas.c @@ -0,0 +1,1924 @@ +/* + 3w-sas.c -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux. + + Written By: Adam Radford <linuxraid@lsi.com> + + Copyright (C) 2009 LSI Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Controllers supported by this driver: + + LSI 3ware 9750 6Gb/s SAS/SATA-RAID + + Bugs/Comments/Suggestions should be mailed to: + linuxraid@lsi.com + + For more information, goto: + http://www.lsi.com + + History + ------- + 3.26.02.000 - Initial driver release. +*/ + +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/time.h> +#include <linux/mutex.h> +#include <linux/smp_lock.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_cmnd.h> +#include "3w-sas.h" + +/* Globals */ +#define TW_DRIVER_VERSION "3.26.02.000" +static TW_Device_Extension *twl_device_extension_list[TW_MAX_SLOT]; +static unsigned int twl_device_extension_count; +static int twl_major = -1; +extern struct timezone sys_tz; + +/* Module parameters */ +MODULE_AUTHOR ("LSI"); +MODULE_DESCRIPTION ("LSI 3ware SAS/SATA-RAID Linux Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(TW_DRIVER_VERSION); + +static int use_msi; +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0"); + +/* Function prototypes */ +static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset); + +/* Functions */ + +/* This function returns AENs through sysfs */ +static ssize_t twl_sysfs_aen_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *outbuf, loff_t offset, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct Scsi_Host *shost = class_to_shost(dev); + TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata; + unsigned long flags = 0; + ssize_t ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + spin_lock_irqsave(tw_dev->host->host_lock, flags); + ret = memory_read_from_buffer(outbuf, count, &offset, tw_dev->event_queue[0], sizeof(TW_Event) * TW_Q_LENGTH); + spin_unlock_irqrestore(tw_dev->host->host_lock, flags); + + return ret; +} /* End twl_sysfs_aen_read() */ + +/* aen_read sysfs attribute initializer */ +static struct bin_attribute twl_sysfs_aen_read_attr = { + .attr = { + .name = "3ware_aen_read", + .mode = S_IRUSR, + }, + .size = 0, + .read = twl_sysfs_aen_read +}; + +/* This function returns driver compatibility info through sysfs */ +static ssize_t twl_sysfs_compat_info(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *outbuf, loff_t offset, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct Scsi_Host *shost = class_to_shost(dev); + TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata; + unsigned long flags = 0; + ssize_t ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + spin_lock_irqsave(tw_dev->host->host_lock, flags); + ret = memory_read_from_buffer(outbuf, count, &offset, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info)); + spin_unlock_irqrestore(tw_dev->host->host_lock, flags); + + return ret; +} /* End twl_sysfs_compat_info() */ + +/* compat_info sysfs attribute initializer */ +static struct bin_attribute twl_sysfs_compat_info_attr = { + .attr = { + .name = "3ware_compat_info", + .mode = S_IRUSR, + }, + .size = 0, + .read = twl_sysfs_compat_info +}; + +/* Show some statistics about the card */ +static ssize_t twl_show_stats(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *host = class_to_shost(dev); + TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + unsigned long flags = 0; + ssize_t len; + + spin_lock_irqsave(tw_dev->host->host_lock, flags); + len = snprintf(buf, PAGE_SIZE, "3w-sas Driver version: %s\n" + "Current commands posted: %4d\n" + "Max commands posted: %4d\n" + "Last sgl length: %4d\n" + "Max sgl length: %4d\n" + "Last sector count: %4d\n" + "Max sector count: %4d\n" + "SCSI Host Resets: %4d\n" + "AEN's: %4d\n", + TW_DRIVER_VERSION, + tw_dev->posted_request_count, + tw_dev->max_posted_request_count, + tw_dev->sgl_entries, + tw_dev->max_sgl_entries, + tw_dev->sector_count, + tw_dev->max_sector_count, + tw_dev->num_resets, + tw_dev->aen_count); + spin_unlock_irqrestore(tw_dev->host->host_lock, flags); + return len; +} /* End twl_show_stats() */ + +/* This function will set a devices queue depth */ +static int twl_change_queue_depth(struct scsi_device *sdev, int queue_depth, + int reason) +{ + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + + if (queue_depth > TW_Q_LENGTH-2) + queue_depth = TW_Q_LENGTH-2; + scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); + return queue_depth; +} /* End twl_change_queue_depth() */ + +/* stats sysfs attribute initializer */ +static struct device_attribute twl_host_stats_attr = { + .attr = { + .name = "3ware_stats", + .mode = S_IRUGO, + }, + .show = twl_show_stats +}; + +/* Host attributes initializer */ +static struct device_attribute *twl_host_attrs[] = { + &twl_host_stats_attr, + NULL, +}; + +/* This function will look up an AEN severity string */ +static char *twl_aen_severity_lookup(unsigned char severity_code) +{ + char *retval = NULL; + + if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) || + (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG)) + goto out; + + retval = twl_aen_severity_table[severity_code]; +out: + return retval; +} /* End twl_aen_severity_lookup() */ + +/* This function will queue an event */ +static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header) +{ + u32 local_time; + struct timeval time; + TW_Event *event; + unsigned short aen; + char host[16]; + char *error_str; + + tw_dev->aen_count++; + + /* Fill out event info */ + event = tw_dev->event_queue[tw_dev->error_index]; + + host[0] = '\0'; + if (tw_dev->host) + sprintf(host, " scsi%d:", tw_dev->host->host_no); + + aen = le16_to_cpu(header->status_block.error); + memset(event, 0, sizeof(TW_Event)); + + event->severity = TW_SEV_OUT(header->status_block.severity__reserved); + do_gettimeofday(&time); + local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60)); + event->time_stamp_sec = local_time; + event->aen_code = aen; + event->retrieved = TW_AEN_NOT_RETRIEVED; + event->sequence_id = tw_dev->error_sequence_id; + tw_dev->error_sequence_id++; + + /* Check for embedded error string */ + error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]); + + header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0'; + event->parameter_len = strlen(header->err_specific_desc); + memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + 1 + strlen(error_str)); + if (event->severity != TW_AEN_SEVERITY_DEBUG) + printk(KERN_WARNING "3w-sas:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n", + host, + twl_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)), + TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, error_str, + header->err_specific_desc); + else + tw_dev->aen_count--; + + tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH; +} /* End twl_aen_queue_event() */ + +/* This function will attempt to post a command packet to the board */ +static int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id) +{ + dma_addr_t command_que_value; + + command_que_value = tw_dev->command_packet_phys[request_id]; + command_que_value += TW_COMMAND_OFFSET; + + /* First write upper 4 bytes */ + writel((u32)((u64)command_que_value >> 32), TWL_HIBQPH_REG_ADDR(tw_dev)); + /* Then the lower 4 bytes */ + writel((u32)(command_que_value | TWL_PULL_MODE), TWL_HIBQPL_REG_ADDR(tw_dev)); + + tw_dev->state[request_id] = TW_S_POSTED; + tw_dev->posted_request_count++; + if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) + tw_dev->max_posted_request_count = tw_dev->posted_request_count; + + return 0; +} /* End twl_post_command_packet() */ + +/* This function will perform a pci-dma mapping for a scatter gather list */ +static int twl_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id) +{ + int use_sg; + struct scsi_cmnd *cmd = tw_dev->srb[request_id]; + + use_sg = scsi_dma_map(cmd); + if (!use_sg) + return 0; + else if (use_sg < 0) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Failed to map scatter gather list"); + return 0; + } + + cmd->SCp.phase = TW_PHASE_SGLIST; + cmd->SCp.have_data_in = use_sg; + + return use_sg; +} /* End twl_map_scsi_sg_data() */ + +/* This function hands scsi cdb's to the firmware */ +static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry_ISO *sglistarg) +{ + TW_Command_Full *full_command_packet; + TW_Command_Apache *command_packet; + int i, sg_count; + struct scsi_cmnd *srb = NULL; + struct scatterlist *sglist = NULL, *sg; + int retval = 1; + + if (tw_dev->srb[request_id]) { + srb = tw_dev->srb[request_id]; + if (scsi_sglist(srb)) + sglist = scsi_sglist(srb); + } + + /* Initialize command packet */ + full_command_packet = tw_dev->command_packet_virt[request_id]; + full_command_packet->header.header_desc.size_header = 128; + full_command_packet->header.status_block.error = 0; + full_command_packet->header.status_block.severity__reserved = 0; + + command_packet = &full_command_packet->command.newcommand; + command_packet->status = 0; + command_packet->opcode__reserved = TW_OPRES_IN(0, TW_OP_EXECUTE_SCSI); + + /* We forced 16 byte cdb use earlier */ + if (!cdb) + memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN); + else + memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN); + + if (srb) { + command_packet->unit = srb->device->id; + command_packet->request_id__lunl = + cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id)); + } else { + command_packet->request_id__lunl = + cpu_to_le16(TW_REQ_LUN_IN(0, request_id)); + command_packet->unit = 0; + } + + command_packet->sgl_offset = 16; + + if (!sglistarg) { + /* Map sglist from scsi layer to cmd packet */ + if (scsi_sg_count(srb)) { + sg_count = twl_map_scsi_sg_data(tw_dev, request_id); + if (sg_count == 0) + goto out; + + scsi_for_each_sg(srb, sg, sg_count, i) { + command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(sg)); + command_packet->sg_list[i].length = TW_CPU_TO_SGL(sg_dma_len(sg)); + } + command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id]))); + } + } else { + /* Internal cdb post */ + for (i = 0; i < use_sg; i++) { + command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address); + command_packet->sg_list[i].length = TW_CPU_TO_SGL(sglistarg[i].length); + } + command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg)); + } + + /* Update some stats */ + if (srb) { + tw_dev->sector_count = scsi_bufflen(srb) / 512; + if (tw_dev->sector_count > tw_dev->max_sector_count) + tw_dev->max_sector_count = tw_dev->sector_count; + tw_dev->sgl_entries = scsi_sg_count(srb); + if (tw_dev->sgl_entries > tw_dev->max_sgl_entries) + tw_dev->max_sgl_entries = tw_dev->sgl_entries; + } + + /* Now post the command to the board */ + retval = twl_post_command_packet(tw_dev, request_id); + +out: + return retval; +} /* End twl_scsiop_execute_scsi() */ + +/* This function will read the aen queue from the isr */ +static int twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) +{ + char cdb[TW_MAX_CDB_LEN]; + TW_SG_Entry_ISO sglist[1]; + TW_Command_Full *full_command_packet; + int retval = 1; + + full_command_packet = tw_dev->command_packet_virt[request_id]; + memset(full_command_packet, 0, sizeof(TW_Command_Full)); + + /* Initialize cdb */ + memset(&cdb, 0, TW_MAX_CDB_LEN); + cdb[0] = REQUEST_SENSE; /* opcode */ + cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */ + + /* Initialize sglist */ + memset(&sglist, 0, sizeof(TW_SG_Entry_ISO)); + sglist[0].length = TW_SECTOR_SIZE; + sglist[0].address = tw_dev->generic_buffer_phys[request_id]; + + /* Mark internal command */ + tw_dev->srb[request_id] = NULL; + + /* Now post the command packet */ + if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Post failed while reading AEN queue"); + goto out; + } + retval = 0; +out: + return retval; +} /* End twl_aen_read_queue() */ + +/* This function will sync firmware time with the host time */ +static void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id) +{ + u32 schedulertime; + struct timeval utc; + TW_Command_Full *full_command_packet; + TW_Command *command_packet; + TW_Param_Apache *param; + u32 local_time; + + /* Fill out the command packet */ + full_command_packet = tw_dev->command_packet_virt[request_id]; + memset(full_command_packet, 0, sizeof(TW_Command_Full)); + command_packet = &full_command_packet->command.oldcommand; + command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM); + command_packet->request_id = request_id; + command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); + command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE); + command_packet->size = TW_COMMAND_SIZE; + command_packet->byte6_offset.parameter_count = cpu_to_le16(1); + + /* Setup the param */ + param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; + memset(param, 0, TW_SECTOR_SIZE); + param->table_id = cpu_to_le16(TW_TIMEKEEP_TABLE | 0x8000); /* Controller time keep table */ + param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */ + param->parameter_size_bytes = cpu_to_le16(4); + + /* Convert system time in UTC to local time seconds since last + Sunday 12:00AM */ + do_gettimeofday(&utc); + local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60)); + schedulertime = local_time - (3 * 86400); + schedulertime = cpu_to_le32(schedulertime % 604800); + + memcpy(param->data, &schedulertime, sizeof(u32)); + + /* Mark internal command */ + tw_dev->srb[request_id] = NULL; + + /* Now post the command */ + twl_post_command_packet(tw_dev, request_id); +} /* End twl_aen_sync_time() */ + +/* This function will assign an available request id */ +static void twl_get_request_id(TW_Device_Extension *tw_dev, int *request_id) +{ + *request_id = tw_dev->free_queue[tw_dev->free_head]; + tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH; + tw_dev->state[*request_id] = TW_S_STARTED; +} /* End twl_get_request_id() */ + +/* This function will free a request id */ +static void twl_free_request_id(TW_Device_Extension *tw_dev, int request_id) +{ + tw_dev->free_queue[tw_dev->free_tail] = request_id; + tw_dev->state[request_id] = TW_S_FINISHED; + tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH; +} /* End twl_free_request_id() */ + +/* This function will complete an aen request from the isr */ +static int twl_aen_complete(TW_Device_Extension *tw_dev, int request_id) +{ + TW_Command_Full *full_command_packet; + TW_Command *command_packet; + TW_Command_Apache_Header *header; + unsigned short aen; + int retval = 1; + + header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; + tw_dev->posted_request_count--; + aen = le16_to_cpu(header->status_block.error); + full_command_packet = tw_dev->command_packet_virt[request_id]; + command_packet = &full_command_packet->command.oldcommand; + + /* First check for internal completion of set param for time sync */ + if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) { + /* Keep reading the queue in case there are more aen's */ + if (twl_aen_read_queue(tw_dev, request_id)) + goto out2; + else { + retval = 0; + goto out; + } + } + + switch (aen) { + case TW_AEN_QUEUE_EMPTY: + /* Quit reading the queue if this is the last one */ + break; + case TW_AEN_SYNC_TIME_WITH_HOST: + twl_aen_sync_time(tw_dev, request_id); + retval = 0; + goto out; + default: + twl_aen_queue_event(tw_dev, header); + + /* If there are more aen's, keep reading the queue */ + if (twl_aen_read_queue(tw_dev, request_id)) + goto out2; + else { + retval = 0; + goto out; + } + } + retval = 0; +out2: + tw_dev->state[request_id] = TW_S_COMPLETED; + twl_free_request_id(tw_dev, request_id); + clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags); +out: + return retval; +} /* End twl_aen_complete() */ + +/* This function will poll for a response */ +static int twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds) +{ + unsigned long before; + dma_addr_t mfa; + u32 regh, regl; + u32 response; + int retval = 1; + int found = 0; + + before = jiffies; + + while (!found) { + if (sizeof(dma_addr_t) > 4) { + regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev)); + regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev)); + mfa = ((u64)regh << 32) | regl; + } else + mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev)); + + response = (u32)mfa; + + if (TW_RESID_OUT(response) == request_id) + found = 1; + + if (time_after(jiffies, before + HZ * seconds)) + goto out; + + msleep(50); + } + retval = 0; +out: + return retval; +} /* End twl_poll_response() */ + +/* This function will drain the aen queue */ +static int twl_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset) +{ + int request_id = 0; + char cdb[TW_MAX_CDB_LEN]; + TW_SG_Entry_ISO sglist[1]; + int finished = 0, count = 0; + TW_Command_Full *full_command_packet; + TW_Command_Apache_Header *header; + unsigned short aen; + int first_reset = 0, queue = 0, retval = 1; + + if (no_check_reset) + first_reset = 0; + else + first_reset = 1; + + full_command_packet = tw_dev->command_packet_virt[request_id]; + memset(full_command_packet, 0, sizeof(TW_Command_Full)); + + /* Initialize cdb */ + memset(&cdb, 0, TW_MAX_CDB_LEN); + cdb[0] = REQUEST_SENSE; /* opcode */ + cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */ + + /* Initialize sglist */ + memset(&sglist, 0, sizeof(TW_SG_Entry_ISO)); + sglist[0].length = TW_SECTOR_SIZE; + sglist[0].address = tw_dev->generic_buffer_phys[request_id]; + + /* Mark internal command */ + tw_dev->srb[request_id] = NULL; + + do { + /* Send command to the board */ + if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "Error posting request sense"); + goto out; + } + + /* Now poll for completion */ + if (twl_poll_response(tw_dev, request_id, 30)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "No valid response while draining AEN queue"); + tw_dev->posted_request_count--; + goto out; + } + + tw_dev->posted_request_count--; + header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; + aen = le16_to_cpu(header->status_block.error); + queue = 0; + count++; + + switch (aen) { + case TW_AEN_QUEUE_EMPTY: + if (first_reset != 1) + goto out; + else + finished = 1; + break; + case TW_AEN_SOFT_RESET: + if (first_reset == 0) + first_reset = 1; + else + queue = 1; + break; + case TW_AEN_SYNC_TIME_WITH_HOST: + break; + default: + queue = 1; + } + + /* Now queue an event info */ + if (queue) + twl_aen_queue_event(tw_dev, header); + } while ((finished == 0) && (count < TW_MAX_AEN_DRAIN)); + + if (count == TW_MAX_AEN_DRAIN) + goto out; + + retval = 0; +out: + tw_dev->state[request_id] = TW_S_INITIAL; + return retval; +} /* End twl_aen_drain_queue() */ + +/* This function will allocate memory and check if it is correctly aligned */ +static int twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which) +{ + int i; + dma_addr_t dma_handle; + unsigned long *cpu_addr; + int retval = 1; + + cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle); + if (!cpu_addr) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed"); + goto out; + } + + memset(cpu_addr, 0, size*TW_Q_LENGTH); + + for (i = 0; i < TW_Q_LENGTH; i++) { + switch(which) { + case 0: + tw_dev->command_packet_phys[i] = dma_handle+(i*size); + tw_dev->command_packet_virt[i] = (TW_Command_Full *)((unsigned char *)cpu_addr + (i*size)); + break; + case 1: + tw_dev->generic_buffer_phys[i] = dma_handle+(i*size); + tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); + break; + case 2: + tw_dev->sense_buffer_phys[i] = dma_handle+(i*size); + tw_dev->sense_buffer_virt[i] = (TW_Command_Apache_Header *)((unsigned char *)cpu_addr + (i*size)); + break; + } + } + retval = 0; +out: + return retval; +} /* End twl_allocate_memory() */ + +/* This function will load the request id and various sgls for ioctls */ +static void twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length) +{ + TW_Command *oldcommand; + TW_Command_Apache *newcommand; + TW_SG_Entry_ISO *sgl; + unsigned int pae = 0; + + if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4)) + pae = 1; + + if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { + newcommand = &full_command_packet->command.newcommand; + newcommand->request_id__lunl = + cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id)); + if (length) { + newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1); + newcommand->sg_list[0].length = TW_CPU_TO_SGL(length); + } + newcommand->sgl_entries__lunh = + cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), length ? 1 : 0)); + } else { + oldcommand = &full_command_packet->command.oldcommand; + oldcommand->request_id = request_id; + + if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) { + /* Load the sg list */ + sgl = (TW_SG_Entry_ISO *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry_ISO)/4) + pae + (sizeof(dma_addr_t) > 4 ? 1 : 0)); + sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1); + sgl->length = TW_CPU_TO_SGL(length); + oldcommand->size += pae; + oldcommand->size += sizeof(dma_addr_t) > 4 ? 1 : 0; + } + } +} /* End twl_load_sgl() */ + +/* This function handles ioctl for the character device + This interface is used by smartmontools open source software */ +static int twl_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + long timeout; + unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0; + dma_addr_t dma_handle; + int request_id = 0; + TW_Ioctl_Driver_Command driver_command; + TW_Ioctl_Buf_Apache *tw_ioctl; + TW_Command_Full *full_command_packet; + TW_Device_Extension *tw_dev = twl_device_extension_list[iminor(inode)]; + int retval = -EFAULT; + void __user *argp = (void __user *)arg; + + /* Only let one of these through at a time */ + if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) { + retval = -EINTR; + goto out; + } + + /* First copy down the driver command */ + if (copy_from_user(&driver_command, argp, sizeof(TW_Ioctl_Driver_Command))) + goto out2; + + /* Check data buffer size */ + if (driver_command.buffer_length > TW_MAX_SECTORS * 2048) { + retval = -EINVAL; + goto out2; + } + + /* Hardware can only do multiple of 512 byte transfers */ + data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511; + + /* Now allocate ioctl buf memory */ + cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle, GFP_KERNEL); + if (!cpu_addr) { + retval = -ENOMEM; + goto out2; + } + + tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr; + + /* Now copy down the entire ioctl */ + if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1)) + goto out3; + + /* See which ioctl we are doing */ + switch (cmd) { + case TW_IOCTL_FIRMWARE_PASS_THROUGH: + spin_lock_irqsave(tw_dev->host->host_lock, flags); + twl_get_request_id(tw_dev, &request_id); + + /* Flag internal command */ + tw_dev->srb[request_id] = NULL; + + /* Flag chrdev ioctl */ + tw_dev->chrdev_request_id = request_id; + + full_command_packet = (TW_Command_Full *)&tw_ioctl->firmware_command; + + /* Load request id and sglist for both command types */ + twl_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); + + memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full)); + + /* Now post the command packet to the controller */ + twl_post_command_packet(tw_dev, request_id); + spin_unlock_irqrestore(tw_dev->host->host_lock, flags); + + timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ; + + /* Now wait for command to complete */ + timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); + + /* We timed out, and didn't get an interrupt */ + if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { + /* Now we need to reset the board */ + printk(KERN_WARNING "3w-sas: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n", + tw_dev->host->host_no, TW_DRIVER, 0x6, + cmd); + retval = -EIO; + twl_reset_device_extension(tw_dev, 1); + goto out3; + } + + /* Now copy in the command packet response */ + memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full)); + + /* Now complete the io */ + spin_lock_irqsave(tw_dev->host->host_lock, flags); + tw_dev->posted_request_count--; + tw_dev->state[request_id] = TW_S_COMPLETED; + twl_free_request_id(tw_dev, request_id); + spin_unlock_irqrestore(tw_dev->host->host_lock, flags); + break; + default: + retval = -ENOTTY; + goto out3; + } + + /* Now copy the entire response to userspace */ + if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0) + retval = 0; +out3: + /* Now free ioctl buf memory */ + dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle); +out2: + mutex_unlock(&tw_dev->ioctl_lock); +out: + return retval; +} /* End twl_chrdev_ioctl() */ + +/* This function handles open for the character device */ +static int twl_chrdev_open(struct inode *inode, struct file *file) +{ + unsigned int minor_number; + int retval = -ENODEV; + + if (!capable(CAP_SYS_ADMIN)) { + retval = -EACCES; + goto out; + } + + cycle_kernel_lock(); + minor_number = iminor(inode); + if (minor_number >= twl_device_extension_count) + goto out; + retval = 0; +out: + return retval; +} /* End twl_chrdev_open() */ + +/* File operations struct for character device */ +static const struct file_operations twl_fops = { + .owner = THIS_MODULE, + .ioctl = twl_chrdev_ioctl, + .open = twl_chrdev_open, + .release = NULL +}; + +/* This function passes sense data from firmware to scsi layer */ +static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, int copy_sense, int print_host) +{ + TW_Command_Apache_Header *header; + TW_Command_Full *full_command_packet; + unsigned short error; + char *error_str; + int retval = 1; + + header = tw_dev->sense_buffer_virt[i]; + full_command_packet = tw_dev->command_packet_virt[request_id]; + + /* Get embedded firmware error string */ + error_str = &(header->err_specific_desc[strlen(header->err_specific_desc) + 1]); + + /* Don't print error for Logical unit not supported during rollcall */ + error = le16_to_cpu(header->status_block.error); + if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE) && (error != TW_ERROR_INVALID_FIELD_IN_CDB)) { + if (print_host) + printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n", + tw_dev->host->host_no, + TW_MESSAGE_SOURCE_CONTROLLER_ERROR, + header->status_block.error, + error_str, + header->err_specific_desc); + else + printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s:%s.\n", + TW_MESSAGE_SOURCE_CONTROLLER_ERROR, + header->status_block.error, + error_str, + header->err_specific_desc); + } + + if (copy_sense) { + memcpy(tw_dev->srb[request_id]->sense_buffer, header->sense_data, TW_SENSE_DATA_LENGTH); + tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1); + goto out; + } +out: + return retval; +} /* End twl_fill_sense() */ + +/* This function will free up device extension resources */ +static void twl_free_device_extension(TW_Device_Extension *tw_dev) +{ + if (tw_dev->command_packet_virt[0]) + pci_free_consistent(tw_dev->tw_pci_dev, + sizeof(TW_Command_Full)*TW_Q_LENGTH, + tw_dev->command_packet_virt[0], + tw_dev->command_packet_phys[0]); + + if (tw_dev->generic_buffer_virt[0]) + pci_free_consistent(tw_dev->tw_pci_dev, + TW_SECTOR_SIZE*TW_Q_LENGTH, + tw_dev->generic_buffer_virt[0], + tw_dev->generic_buffer_phys[0]); + + if (tw_dev->sense_buffer_virt[0]) + pci_free_consistent(tw_dev->tw_pci_dev, + sizeof(TW_Command_Apache_Header)* + TW_Q_LENGTH, + tw_dev->sense_buffer_virt[0], + tw_dev->sense_buffer_phys[0]); + + kfree(tw_dev->event_queue[0]); +} /* End twl_free_device_extension() */ + +/* This function will get parameter table entries from the firmware */ +static void *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes) +{ + TW_Command_Full *full_command_packet; + TW_Command *command_packet; + TW_Param_Apache *param; + void *retval = NULL; + + /* Setup the command packet */ + full_command_packet = tw_dev->command_packet_virt[request_id]; + memset(full_command_packet, 0, sizeof(TW_Command_Full)); + command_packet = &full_command_packet->command.oldcommand; + + command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM); + command_packet->size = TW_COMMAND_SIZE; + command_packet->request_id = request_id; + command_packet->byte6_offset.block_count = cpu_to_le16(1); + + /* Now setup the param */ + param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; + memset(param, 0, TW_SECTOR_SIZE); + param->table_id = cpu_to_le16(table_id | 0x8000); + param->parameter_id = cpu_to_le16(parameter_id); + param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes); + + command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); + command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE); + + /* Post the command packet to the board */ + twl_post_command_packet(tw_dev, request_id); + + /* Poll for completion */ + if (twl_poll_response(tw_dev, request_id, 30)) + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "No valid response during get param") + else + retval = (void *)&(param->data[0]); + + tw_dev->posted_request_count--; + tw_dev->state[request_id] = TW_S_INITIAL; + + return retval; +} /* End twl_get_param() */ + +/* This function will send an initconnection command to controller */ +static int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits, + u32 set_features, unsigned short current_fw_srl, + unsigned short current_fw_arch_id, + unsigned short current_fw_branch, + unsigned short current_fw_build, + unsigned short *fw_on_ctlr_srl, + unsigned short *fw_on_ctlr_arch_id, + unsigned short *fw_on_ctlr_branch, + unsigned short *fw_on_ctlr_build, + u32 *init_connect_result) +{ + TW_Command_Full *full_command_packet; + TW_Initconnect *tw_initconnect; + int request_id = 0, retval = 1; + + /* Initialize InitConnection command packet */ + full_command_packet = tw_dev->command_packet_virt[request_id]; + memset(full_command_packet, 0, sizeof(TW_Command_Full)); + full_command_packet->header.header_desc.size_header = 128; + + tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand; + tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION); + tw_initconnect->request_id = request_id; + tw_initconnect->message_credits = cpu_to_le16(message_credits); + tw_initconnect->features = set_features; + + /* Turn on 64-bit sgl support if we need to */ + tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0; + + tw_initconnect->features = cpu_to_le32(tw_initconnect->features); + + if (set_features & TW_EXTENDED_INIT_CONNECT) { + tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED; + tw_initconnect->fw_srl = cpu_to_le16(current_fw_srl); + tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id); + tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch); + tw_initconnect->fw_build = cpu_to_le16(current_fw_build); + } else + tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE; + + /* Send command packet to the board */ + twl_post_command_packet(tw_dev, request_id); + + /* Poll for completion */ + if (twl_poll_response(tw_dev, request_id, 30)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x8, "No valid response during init connection"); + } else { + if (set_features & TW_EXTENDED_INIT_CONNECT) { + *fw_on_ctlr_srl = le16_to_cpu(tw_initconnect->fw_srl); + *fw_on_ctlr_arch_id = le16_to_cpu(tw_initconnect->fw_arch_id); + *fw_on_ctlr_branch = le16_to_cpu(tw_initconnect->fw_branch); + *fw_on_ctlr_build = le16_to_cpu(tw_initconnect->fw_build); + *init_connect_result = le32_to_cpu(tw_initconnect->result); + } + retval = 0; + } + + tw_dev->posted_request_count--; + tw_dev->state[request_id] = TW_S_INITIAL; + + return retval; +} /* End twl_initconnection() */ + +/* This function will initialize the fields of a device extension */ +static int twl_initialize_device_extension(TW_Device_Extension *tw_dev) +{ + int i, retval = 1; + + /* Initialize command packet buffers */ + if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x9, "Command packet memory allocation failed"); + goto out; + } + + /* Initialize generic buffer */ + if (twl_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Generic memory allocation failed"); + goto out; + } + + /* Allocate sense buffers */ + if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Apache_Header), 2)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0xb, "Sense buffer allocation failed"); + goto out; + } + + /* Allocate event info space */ + tw_dev->event_queue[0] = kcalloc(TW_Q_LENGTH, sizeof(TW_Event), GFP_KERNEL); + if (!tw_dev->event_queue[0]) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "Event info memory allocation failed"); + goto out; + } + + for (i = 0; i < TW_Q_LENGTH; i++) { + tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event))); + tw_dev->free_queue[i] = i; + tw_dev->state[i] = TW_S_INITIAL; + } + + tw_dev->free_head = TW_Q_START; + tw_dev->free_tail = TW_Q_START; + tw_dev->error_sequence_id = 1; + tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; + + mutex_init(&tw_dev->ioctl_lock); + init_waitqueue_head(&tw_dev->ioctl_wqueue); + + retval = 0; +out: + return retval; +} /* End twl_initialize_device_extension() */ + +/* This function will perform a pci-dma unmap */ +static void twl_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id) +{ + struct scsi_cmnd *cmd = tw_dev->srb[request_id]; + + if (cmd->SCp.phase == TW_PHASE_SGLIST) + scsi_dma_unmap(cmd); +} /* End twl_unmap_scsi_data() */ + +/* This function will handle attention interrupts */ +static int twl_handle_attention_interrupt(TW_Device_Extension *tw_dev) +{ + int retval = 1; + u32 request_id, doorbell; + + /* Read doorbell status */ + doorbell = readl(TWL_HOBDB_REG_ADDR(tw_dev)); + + /* Check for controller errors */ + if (doorbell & TWL_DOORBELL_CONTROLLER_ERROR) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "Microcontroller Error: clearing"); + goto out; + } + + /* Check if we need to perform an AEN drain */ + if (doorbell & TWL_DOORBELL_ATTENTION_INTERRUPT) { + if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) { + twl_get_request_id(tw_dev, &request_id); + if (twl_aen_read_queue(tw_dev, request_id)) { + tw_dev->state[request_id] = TW_S_COMPLETED; + twl_free_request_id(tw_dev, request_id); + clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags); + } + } + } + + retval = 0; +out: + /* Clear doorbell interrupt */ + TWL_CLEAR_DB_INTERRUPT(tw_dev); + + /* Make sure the clear was flushed by reading it back */ + readl(TWL_HOBDBC_REG_ADDR(tw_dev)); + + return retval; +} /* End twl_handle_attention_interrupt() */ + +/* Interrupt service routine */ +static irqreturn_t twl_interrupt(int irq, void *dev_instance) +{ + TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; + int i, handled = 0, error = 0; + dma_addr_t mfa = 0; + u32 reg, regl, regh, response, request_id = 0; + struct scsi_cmnd *cmd; + TW_Command_Full *full_command_packet; + + spin_lock(tw_dev->host->host_lock); + + /* Read host interrupt status */ + reg = readl(TWL_HISTAT_REG_ADDR(tw_dev)); + + /* Check if this is our interrupt, otherwise bail */ + if (!(reg & TWL_HISTATUS_VALID_INTERRUPT)) + goto twl_interrupt_bail; + + handled = 1; + + /* If we are resetting, bail */ + if (test_bit(TW_IN_RESET, &tw_dev->flags)) + goto twl_interrupt_bail; + + /* Attention interrupt */ + if (reg & TWL_HISTATUS_ATTENTION_INTERRUPT) { + if (twl_handle_attention_interrupt(tw_dev)) { + TWL_MASK_INTERRUPTS(tw_dev); + goto twl_interrupt_bail; + } + } + + /* Response interrupt */ + while (reg & TWL_HISTATUS_RESPONSE_INTERRUPT) { + if (sizeof(dma_addr_t) > 4) { + regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev)); + regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev)); + mfa = ((u64)regh << 32) | regl; + } else + mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev)); + + error = 0; + response = (u32)mfa; + + /* Check for command packet error */ + if (!TW_NOTMFA_OUT(response)) { + for (i=0;i<TW_Q_LENGTH;i++) { + if (tw_dev->sense_buffer_phys[i] == mfa) { + request_id = le16_to_cpu(tw_dev->sense_buffer_virt[i]->header_desc.request_id); + if (tw_dev->srb[request_id] != NULL) + error = twl_fill_sense(tw_dev, i, request_id, 1, 1); + else { + /* Skip ioctl error prints */ + if (request_id != tw_dev->chrdev_request_id) + error = twl_fill_sense(tw_dev, i, request_id, 0, 1); + else + memcpy(tw_dev->command_packet_virt[request_id], tw_dev->sense_buffer_virt[i], sizeof(TW_Command_Apache_Header)); + } + + /* Now re-post the sense buffer */ + writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev)); + writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev)); + break; + } + } + } else + request_id = TW_RESID_OUT(response); + + full_command_packet = tw_dev->command_packet_virt[request_id]; + + /* Check for correct state */ + if (tw_dev->state[request_id] != TW_S_POSTED) { + if (tw_dev->srb[request_id] != NULL) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Received a request id that wasn't posted"); + TWL_MASK_INTERRUPTS(tw_dev); + goto twl_interrupt_bail; + } + } + + /* Check for internal command completion */ + if (tw_dev->srb[request_id] == NULL) { + if (request_id != tw_dev->chrdev_request_id) { + if (twl_aen_complete(tw_dev, request_id)) + TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "Error completing AEN during attention interrupt"); + } else { + tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; + wake_up(&tw_dev->ioctl_wqueue); + } + } else { + cmd = tw_dev->srb[request_id]; + + if (!error) + cmd->result = (DID_OK << 16); + + /* Report residual bytes for single sgl */ + if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) { + if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id])) + scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length); + } + + /* Now complete the io */ + tw_dev->state[request_id] = TW_S_COMPLETED; + twl_free_request_id(tw_dev, request_id); + tw_dev->posted_request_count--; + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + twl_unmap_scsi_data(tw_dev, request_id); + } + + /* Check for another response interrupt */ + reg = readl(TWL_HISTAT_REG_ADDR(tw_dev)); + } + +twl_interrupt_bail: + spin_unlock(tw_dev->host->host_lock); + return IRQ_RETVAL(handled); +} /* End twl_interrupt() */ + +/* This function will poll for a register change */ +static int twl_poll_register(TW_Device_Extension *tw_dev, void *reg, u32 value, u32 result, int seconds) +{ + unsigned long before; + int retval = 1; + u32 reg_value; + + reg_value = readl(reg); + before = jiffies; + + while ((reg_value & value) != result) { + reg_value = readl(reg); + if (time_after(jiffies, before + HZ * seconds)) + goto out; + msleep(50); + } + retval = 0; +out: + return retval; +} /* End twl_poll_register() */ + +/* This function will reset a controller */ +static int twl_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset) +{ + int retval = 1; + int i = 0; + u32 status = 0; + unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0; + unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0; + u32 init_connect_result = 0; + int tries = 0; + int do_soft_reset = soft_reset; + + while (tries < TW_MAX_RESET_TRIES) { + /* Do a soft reset if one is needed */ + if (do_soft_reset) { + TWL_SOFT_RESET(tw_dev); + + /* Make sure controller is in a good state */ + if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, 0x0, 30)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Controller never went non-ready during reset sequence"); + tries++; + continue; + } + if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, TWL_CONTROLLER_READY, 60)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x11, "Controller not ready during reset sequence"); + tries++; + continue; + } + } + + /* Initconnect */ + if (twl_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS, + TW_EXTENDED_INIT_CONNECT, TW_CURRENT_DRIVER_SRL, + TW_9750_ARCH_ID, TW_CURRENT_DRIVER_BRANCH, + TW_CURRENT_DRIVER_BUILD, &fw_on_ctlr_srl, + &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, + &fw_on_ctlr_build, &init_connect_result)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x12, "Initconnection failed while checking SRL"); + do_soft_reset = 1; + tries++; + continue; + } + + /* Load sense buffers */ + while (i < TW_Q_LENGTH) { + writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev)); + writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev)); + + /* Check status for over-run after each write */ + status = readl(TWL_STATUS_REG_ADDR(tw_dev)); + if (!(status & TWL_STATUS_OVERRUN_SUBMIT)) + i++; + } + + /* Now check status */ + status = readl(TWL_STATUS_REG_ADDR(tw_dev)); + if (status) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "Bad controller status after loading sense buffers"); + do_soft_reset = 1; + tries++; + continue; + } + + /* Drain the AEN queue */ + if (twl_aen_drain_queue(tw_dev, soft_reset)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x14, "AEN drain failed during reset sequence"); + do_soft_reset = 1; + tries++; + continue; + } + + /* Load rest of compatibility struct */ + strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION)); + tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL; + tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH; + tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD; + tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL; + tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH; + tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD; + tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl; + tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch; + tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build; + + /* If we got here, controller is in a good state */ + retval = 0; + goto out; + } +out: + return retval; +} /* End twl_reset_sequence() */ + +/* This function will reset a device extension */ +static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset) +{ + int i = 0, retval = 1; + unsigned long flags = 0; + + /* Block SCSI requests while we are resetting */ + if (ioctl_reset) + scsi_block_requests(tw_dev->host); + + set_bit(TW_IN_RESET, &tw_dev->flags); + TWL_MASK_INTERRUPTS(tw_dev); + TWL_CLEAR_DB_INTERRUPT(tw_dev); + + spin_lock_irqsave(tw_dev->host->host_lock, flags); + + /* Abort all requests that are in progress */ + for (i = 0; i < TW_Q_LENGTH; i++) { + if ((tw_dev->state[i] != TW_S_FINISHED) && + (tw_dev->state[i] != TW_S_INITIAL) && + (tw_dev->state[i] != TW_S_COMPLETED)) { + if (tw_dev->srb[i]) { + tw_dev->srb[i]->result = (DID_RESET << 16); + tw_dev->srb[i]->scsi_done(tw_dev->srb[i]); + twl_unmap_scsi_data(tw_dev, i); + } + } + } + + /* Reset queues and counts */ + for (i = 0; i < TW_Q_LENGTH; i++) { + tw_dev->free_queue[i] = i; + tw_dev->state[i] = TW_S_INITIAL; + } + tw_dev->free_head = TW_Q_START; + tw_dev->free_tail = TW_Q_START; + tw_dev->posted_request_count = 0; + + spin_unlock_irqrestore(tw_dev->host->host_lock, flags); + + if (twl_reset_sequence(tw_dev, 1)) + goto out; + + TWL_UNMASK_INTERRUPTS(tw_dev); + + clear_bit(TW_IN_RESET, &tw_dev->flags); + tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; + + retval = 0; +out: + if (ioctl_reset) + scsi_unblock_requests(tw_dev->host); + return retval; +} /* End twl_reset_device_extension() */ + +/* This funciton returns unit geometry in cylinders/heads/sectors */ +static int twl_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]) +{ + int heads, sectors; + TW_Device_Extension *tw_dev; + + tw_dev = (TW_Device_Extension *)sdev->host->hostdata; + + if (capacity >= 0x200000) { + heads = 255; + sectors = 63; + } else { + heads = 64; + sectors = 32; + } + + geom[0] = heads; + geom[1] = sectors; + geom[2] = sector_div(capacity, heads * sectors); /* cylinders */ + + return 0; +} /* End twl_scsi_biosparam() */ + +/* This is the new scsi eh reset function */ +static int twl_scsi_eh_reset(struct scsi_cmnd *SCpnt) +{ + TW_Device_Extension *tw_dev = NULL; + int retval = FAILED; + + tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + + tw_dev->num_resets++; + + sdev_printk(KERN_WARNING, SCpnt->device, + "WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n", + TW_DRIVER, 0x2c, SCpnt->cmnd[0]); + + /* Make sure we are not issuing an ioctl or resetting from ioctl */ + mutex_lock(&tw_dev->ioctl_lock); + + /* Now reset the card and some of the device extension data */ + if (twl_reset_device_extension(tw_dev, 0)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "Controller reset failed during scsi host reset"); + goto out; + } + + retval = SUCCESS; +out: + mutex_unlock(&tw_dev->ioctl_lock); + return retval; +} /* End twl_scsi_eh_reset() */ + +/* This is the main scsi queue function to handle scsi opcodes */ +static int twl_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) +{ + int request_id, retval; + TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + + /* If we are resetting due to timed out ioctl, report as busy */ + if (test_bit(TW_IN_RESET, &tw_dev->flags)) { + retval = SCSI_MLQUEUE_HOST_BUSY; + goto out; + } + + /* Save done function into scsi_cmnd struct */ + SCpnt->scsi_done = done; + + /* Get a free request id */ + twl_get_request_id(tw_dev, &request_id); + + /* Save the scsi command for use by the ISR */ + tw_dev->srb[request_id] = SCpnt; + + /* Initialize phase to zero */ + SCpnt->SCp.phase = TW_PHASE_INITIAL; + + retval = twl_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL); + if (retval) { + tw_dev->state[request_id] = TW_S_COMPLETED; + twl_free_request_id(tw_dev, request_id); + SCpnt->result = (DID_ERROR << 16); + done(SCpnt); + retval = 0; + } +out: + return retval; +} /* End twl_scsi_queue() */ + +/* This function tells the controller to shut down */ +static void __twl_shutdown(TW_Device_Extension *tw_dev) +{ + /* Disable interrupts */ + TWL_MASK_INTERRUPTS(tw_dev); + + /* Free up the IRQ */ + free_irq(tw_dev->tw_pci_dev->irq, tw_dev); + + printk(KERN_WARNING "3w-sas: Shutting down host %d.\n", tw_dev->host->host_no); + + /* Tell the card we are shutting down */ + if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Connection shutdown failed"); + } else { + printk(KERN_WARNING "3w-sas: Shutdown complete.\n"); + } + + /* Clear doorbell interrupt just before exit */ + TWL_CLEAR_DB_INTERRUPT(tw_dev); +} /* End __twl_shutdown() */ + +/* Wrapper for __twl_shutdown */ +static void twl_shutdown(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + TW_Device_Extension *tw_dev; + + if (!host) + return; + + tw_dev = (TW_Device_Extension *)host->hostdata; + + if (tw_dev->online) + __twl_shutdown(tw_dev); +} /* End twl_shutdown() */ + +/* This function configures unit settings when a unit is coming on-line */ +static int twl_slave_configure(struct scsi_device *sdev) +{ + /* Force 60 second timeout */ + blk_queue_rq_timeout(sdev->request_queue, 60 * HZ); + + return 0; +} /* End twl_slave_configure() */ + +/* scsi_host_template initializer */ +static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = "3w-sas", + .queuecommand = twl_scsi_queue, + .eh_host_reset_handler = twl_scsi_eh_reset, + .bios_param = twl_scsi_biosparam, + .change_queue_depth = twl_change_queue_depth, + .can_queue = TW_Q_LENGTH-2, + .slave_configure = twl_slave_configure, + .this_id = -1, + .sg_tablesize = TW_LIBERATOR_MAX_SGL_LENGTH, + .max_sectors = TW_MAX_SECTORS, + .cmd_per_lun = TW_MAX_CMDS_PER_LUN, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = twl_host_attrs, + .emulated = 1 +}; + +/* This function will probe and initialize a card */ +static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) +{ + struct Scsi_Host *host = NULL; + TW_Device_Extension *tw_dev; + int retval = -ENODEV; + int *ptr_phycount, phycount=0; + + retval = pci_enable_device(pdev); + if (retval) { + TW_PRINTK(host, TW_DRIVER, 0x17, "Failed to enable pci device"); + goto out_disable_device; + } + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) + || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) + || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + TW_PRINTK(host, TW_DRIVER, 0x18, "Failed to set dma mask"); + retval = -ENODEV; + goto out_disable_device; + } + + host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension)); + if (!host) { + TW_PRINTK(host, TW_DRIVER, 0x19, "Failed to allocate memory for device extension"); + retval = -ENOMEM; + goto out_disable_device; + } + tw_dev = shost_priv(host); + + /* Save values to device extension */ + tw_dev->host = host; + tw_dev->tw_pci_dev = pdev; + + if (twl_initialize_device_extension(tw_dev)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Failed to initialize device extension"); + goto out_free_device_extension; + } + + /* Request IO regions */ + retval = pci_request_regions(pdev, "3w-sas"); + if (retval) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Failed to get mem region"); + goto out_free_device_extension; + } + + /* Save base address, use region 1 */ + tw_dev->base_addr = pci_iomap(pdev, 1, 0); + if (!tw_dev->base_addr) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to ioremap"); + goto out_release_mem_region; + } + + /* Disable interrupts on the card */ + TWL_MASK_INTERRUPTS(tw_dev); + + /* Initialize the card */ + if (twl_reset_sequence(tw_dev, 0)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Controller reset failed during probe"); + goto out_iounmap; + } + + /* Set host specific parameters */ + host->max_id = TW_MAX_UNITS; + host->max_cmd_len = TW_MAX_CDB_LEN; + host->max_lun = TW_MAX_LUNS; + host->max_channel = 0; + + /* Register the card with the kernel SCSI layer */ + retval = scsi_add_host(host, &pdev->dev); + if (retval) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "scsi add host failed"); + goto out_iounmap; + } + + pci_set_drvdata(pdev, host); + + printk(KERN_WARNING "3w-sas: scsi%d: Found an LSI 3ware %s Controller at 0x%llx, IRQ: %d.\n", + host->host_no, + (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE, + TW_PARAM_MODEL, TW_PARAM_MODEL_LENGTH), + (u64)pci_resource_start(pdev, 1), pdev->irq); + + ptr_phycount = twl_get_param(tw_dev, 2, TW_PARAM_PHY_SUMMARY_TABLE, + TW_PARAM_PHYCOUNT, TW_PARAM_PHYCOUNT_LENGTH); + if (ptr_phycount) + phycount = le32_to_cpu(*(int *)ptr_phycount); + + printk(KERN_WARNING "3w-sas: scsi%d: Firmware %s, BIOS %s, Phys: %d.\n", + host->host_no, + (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE, + TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH), + (char *)twl_get_param(tw_dev, 2, TW_VERSION_TABLE, + TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH), + phycount); + + /* Try to enable MSI */ + if (use_msi && !pci_enable_msi(pdev)) + set_bit(TW_USING_MSI, &tw_dev->flags); + + /* Now setup the interrupt handler */ + retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev); + if (retval) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Error requesting IRQ"); + goto out_remove_host; + } + + twl_device_extension_list[twl_device_extension_count] = tw_dev; + twl_device_extension_count++; + + /* Re-enable interrupts on the card */ + TWL_UNMASK_INTERRUPTS(tw_dev); + + /* Finally, scan the host */ + scsi_scan_host(host); + + /* Add sysfs binary files */ + if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr)) + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Failed to create sysfs binary file: 3ware_aen_read"); + if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr)) + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Failed to create sysfs binary file: 3ware_compat_info"); + + if (twl_major == -1) { + if ((twl_major = register_chrdev (0, "twl", &twl_fops)) < 0) + TW_PRINTK(host, TW_DRIVER, 0x22, "Failed to register character device"); + } + tw_dev->online = 1; + return 0; + +out_remove_host: + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_disable_msi(pdev); + scsi_remove_host(host); +out_iounmap: + iounmap(tw_dev->base_addr); +out_release_mem_region: + pci_release_regions(pdev); +out_free_device_extension: + twl_free_device_extension(tw_dev); + scsi_host_put(host); +out_disable_device: + pci_disable_device(pdev); + + return retval; +} /* End twl_probe() */ + +/* This function is called to remove a device */ +static void twl_remove(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + TW_Device_Extension *tw_dev; + + if (!host) + return; + + tw_dev = (TW_Device_Extension *)host->hostdata; + + if (!tw_dev->online) + return; + + /* Remove sysfs binary files */ + sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr); + sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr); + + scsi_remove_host(tw_dev->host); + + /* Unregister character device */ + if (twl_major >= 0) { + unregister_chrdev(twl_major, "twl"); + twl_major = -1; + } + + /* Shutdown the card */ + __twl_shutdown(tw_dev); + + /* Disable MSI if enabled */ + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_disable_msi(pdev); + + /* Free IO remapping */ + iounmap(tw_dev->base_addr); + + /* Free up the mem region */ + pci_release_regions(pdev); + + /* Free up device extension resources */ + twl_free_device_extension(tw_dev); + + scsi_host_put(tw_dev->host); + pci_disable_device(pdev); + twl_device_extension_count--; +} /* End twl_remove() */ + +#ifdef CONFIG_PM +/* This function is called on PCI suspend */ +static int twl_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + + printk(KERN_WARNING "3w-sas: Suspending host %d.\n", tw_dev->host->host_no); + /* Disable interrupts */ + TWL_MASK_INTERRUPTS(tw_dev); + + free_irq(tw_dev->tw_pci_dev->irq, tw_dev); + + /* Tell the card we are shutting down */ + if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x23, "Connection shutdown failed during suspend"); + } else { + printk(KERN_WARNING "3w-sas: Suspend complete.\n"); + } + + /* Clear doorbell interrupt */ + TWL_CLEAR_DB_INTERRUPT(tw_dev); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} /* End twl_suspend() */ + +/* This function is called on PCI resume */ +static int twl_resume(struct pci_dev *pdev) +{ + int retval = 0; + struct Scsi_Host *host = pci_get_drvdata(pdev); + TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + + printk(KERN_WARNING "3w-sas: Resuming host %d.\n", tw_dev->host->host_no); + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + + retval = pci_enable_device(pdev); + if (retval) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x24, "Enable device failed during resume"); + return retval; + } + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) + || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) + || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + TW_PRINTK(host, TW_DRIVER, 0x25, "Failed to set dma mask during resume"); + retval = -ENODEV; + goto out_disable_device; + } + + /* Initialize the card */ + if (twl_reset_sequence(tw_dev, 0)) { + retval = -ENODEV; + goto out_disable_device; + } + + /* Now setup the interrupt handler */ + retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev); + if (retval) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Error requesting IRQ during resume"); + retval = -ENODEV; + goto out_disable_device; + } + + /* Now enable MSI if enabled */ + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_enable_msi(pdev); + + /* Re-enable interrupts on the card */ + TWL_UNMASK_INTERRUPTS(tw_dev); + + printk(KERN_WARNING "3w-sas: Resume complete.\n"); + return 0; + +out_disable_device: + scsi_remove_host(host); + pci_disable_device(pdev); + + return retval; +} /* End twl_resume() */ +#endif + +/* PCI Devices supported by this driver */ +static struct pci_device_id twl_pci_tbl[] __devinitdata = { + { PCI_VDEVICE(3WARE, PCI_DEVICE_ID_3WARE_9750) }, + { } +}; +MODULE_DEVICE_TABLE(pci, twl_pci_tbl); + +/* pci_driver initializer */ +static struct pci_driver twl_driver = { + .name = "3w-sas", + .id_table = twl_pci_tbl, + .probe = twl_probe, + .remove = twl_remove, +#ifdef CONFIG_PM + .suspend = twl_suspend, + .resume = twl_resume, +#endif + .shutdown = twl_shutdown +}; + +/* This function is called on driver initialization */ +static int __init twl_init(void) +{ + printk(KERN_INFO "LSI 3ware SAS/SATA-RAID Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION); + + return pci_register_driver(&twl_driver); +} /* End twl_init() */ + +/* This function is called on driver exit */ +static void __exit twl_exit(void) +{ + pci_unregister_driver(&twl_driver); +} /* End twl_exit() */ + +module_init(twl_init); +module_exit(twl_exit); + diff --git a/drivers/scsi/3w-sas.h b/drivers/scsi/3w-sas.h new file mode 100644 index 000000000000..d474892701d4 --- /dev/null +++ b/drivers/scsi/3w-sas.h @@ -0,0 +1,396 @@ +/* + 3w-sas.h -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux. + + Written By: Adam Radford <linuxraid@lsi.com> + + Copyright (C) 2009 LSI Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Bugs/Comments/Suggestions should be mailed to: + linuxraid@lsi.com + + For more information, goto: + http://www.lsi.com +*/ + +#ifndef _3W_SAS_H +#define _3W_SAS_H + +/* AEN severity table */ +static char *twl_aen_severity_table[] = +{ + "None", "ERROR", "WARNING", "INFO", "DEBUG", NULL +}; + +/* Liberator register offsets */ +#define TWL_STATUS 0x0 /* Status */ +#define TWL_HIBDB 0x20 /* Inbound doorbell */ +#define TWL_HISTAT 0x30 /* Host interrupt status */ +#define TWL_HIMASK 0x34 /* Host interrupt mask */ +#define TWL_HOBDB 0x9C /* Outbound doorbell */ +#define TWL_HOBDBC 0xA0 /* Outbound doorbell clear */ +#define TWL_SCRPD3 0xBC /* Scratchpad */ +#define TWL_HIBQPL 0xC0 /* Host inbound Q low */ +#define TWL_HIBQPH 0xC4 /* Host inbound Q high */ +#define TWL_HOBQPL 0xC8 /* Host outbound Q low */ +#define TWL_HOBQPH 0xCC /* Host outbound Q high */ +#define TWL_HISTATUS_VALID_INTERRUPT 0xC +#define TWL_HISTATUS_ATTENTION_INTERRUPT 0x4 +#define TWL_HISTATUS_RESPONSE_INTERRUPT 0x8 +#define TWL_STATUS_OVERRUN_SUBMIT 0x2000 +#define TWL_ISSUE_SOFT_RESET 0x100 +#define TWL_CONTROLLER_READY 0x2000 +#define TWL_DOORBELL_CONTROLLER_ERROR 0x200000 +#define TWL_DOORBELL_ATTENTION_INTERRUPT 0x40000 +#define TWL_PULL_MODE 0x1 + +/* Command packet opcodes used by the driver */ +#define TW_OP_INIT_CONNECTION 0x1 +#define TW_OP_GET_PARAM 0x12 +#define TW_OP_SET_PARAM 0x13 +#define TW_OP_EXECUTE_SCSI 0x10 + +/* Asynchronous Event Notification (AEN) codes used by the driver */ +#define TW_AEN_QUEUE_EMPTY 0x0000 +#define TW_AEN_SOFT_RESET 0x0001 +#define TW_AEN_SYNC_TIME_WITH_HOST 0x031 +#define TW_AEN_SEVERITY_ERROR 0x1 +#define TW_AEN_SEVERITY_DEBUG 0x4 +#define TW_AEN_NOT_RETRIEVED 0x1 + +/* Command state defines */ +#define TW_S_INITIAL 0x1 /* Initial state */ +#define TW_S_STARTED 0x2 /* Id in use */ +#define TW_S_POSTED 0x4 /* Posted to the controller */ +#define TW_S_COMPLETED 0x8 /* Completed by isr */ +#define TW_S_FINISHED 0x10 /* I/O completely done */ + +/* Compatibility defines */ +#define TW_9750_ARCH_ID 10 +#define TW_CURRENT_DRIVER_SRL 40 +#define TW_CURRENT_DRIVER_BUILD 0 +#define TW_CURRENT_DRIVER_BRANCH 0 + +/* Phase defines */ +#define TW_PHASE_INITIAL 0 +#define TW_PHASE_SGLIST 2 + +/* Misc defines */ +#define TW_SECTOR_SIZE 512 +#define TW_MAX_UNITS 32 +#define TW_INIT_MESSAGE_CREDITS 0x100 +#define TW_INIT_COMMAND_PACKET_SIZE 0x3 +#define TW_INIT_COMMAND_PACKET_SIZE_EXTENDED 0x6 +#define TW_EXTENDED_INIT_CONNECT 0x2 +#define TW_BASE_FW_SRL 24 +#define TW_BASE_FW_BRANCH 0 +#define TW_BASE_FW_BUILD 1 +#define TW_Q_LENGTH 256 +#define TW_Q_START 0 +#define TW_MAX_SLOT 32 +#define TW_MAX_RESET_TRIES 2 +#define TW_MAX_CMDS_PER_LUN 254 +#define TW_MAX_AEN_DRAIN 255 +#define TW_IN_RESET 2 +#define TW_USING_MSI 3 +#define TW_IN_ATTENTION_LOOP 4 +#define TW_MAX_SECTORS 256 +#define TW_MAX_CDB_LEN 16 +#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */ +#define TW_IOCTL_CHRDEV_FREE -1 +#define TW_COMMAND_OFFSET 128 /* 128 bytes */ +#define TW_VERSION_TABLE 0x0402 +#define TW_TIMEKEEP_TABLE 0x040A +#define TW_INFORMATION_TABLE 0x0403 +#define TW_PARAM_FWVER 3 +#define TW_PARAM_FWVER_LENGTH 16 +#define TW_PARAM_BIOSVER 4 +#define TW_PARAM_BIOSVER_LENGTH 16 +#define TW_PARAM_MODEL 8 +#define TW_PARAM_MODEL_LENGTH 16 +#define TW_PARAM_PHY_SUMMARY_TABLE 1 +#define TW_PARAM_PHYCOUNT 2 +#define TW_PARAM_PHYCOUNT_LENGTH 1 +#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108 // Used by smartmontools +#define TW_ALLOCATION_LENGTH 128 +#define TW_SENSE_DATA_LENGTH 18 +#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x10a +#define TW_ERROR_INVALID_FIELD_IN_CDB 0x10d +#define TW_ERROR_UNIT_OFFLINE 0x128 +#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR 3 +#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT 4 +#define TW_DRIVER 6 +#ifndef PCI_DEVICE_ID_3WARE_9750 +#define PCI_DEVICE_ID_3WARE_9750 0x1010 +#endif + +/* Bitmask macros to eliminate bitfields */ + +/* opcode: 5, reserved: 3 */ +#define TW_OPRES_IN(x,y) ((x << 5) | (y & 0x1f)) +#define TW_OP_OUT(x) (x & 0x1f) + +/* opcode: 5, sgloffset: 3 */ +#define TW_OPSGL_IN(x,y) ((x << 5) | (y & 0x1f)) +#define TW_SGL_OUT(x) ((x >> 5) & 0x7) + +/* severity: 3, reserved: 5 */ +#define TW_SEV_OUT(x) (x & 0x7) + +/* not_mfa: 1, reserved: 7, status: 8, request_id: 16 */ +#define TW_RESID_OUT(x) ((x >> 16) & 0xffff) +#define TW_NOTMFA_OUT(x) (x & 0x1) + +/* request_id: 12, lun: 4 */ +#define TW_REQ_LUN_IN(lun, request_id) (((lun << 12) & 0xf000) | (request_id & 0xfff)) +#define TW_LUN_OUT(lun) ((lun >> 12) & 0xf) + +/* Register access macros */ +#define TWL_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_STATUS) +#define TWL_HOBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPL) +#define TWL_HOBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPH) +#define TWL_HOBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDB) +#define TWL_HOBDBC_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDBC) +#define TWL_HIMASK_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIMASK) +#define TWL_HISTAT_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HISTAT) +#define TWL_HIBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPH) +#define TWL_HIBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPL) +#define TWL_HIBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBDB) +#define TWL_SCRPD3_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_SCRPD3) +#define TWL_MASK_INTERRUPTS(x) (writel(~0, TWL_HIMASK_REG_ADDR(tw_dev))) +#define TWL_UNMASK_INTERRUPTS(x) (writel(~TWL_HISTATUS_VALID_INTERRUPT, TWL_HIMASK_REG_ADDR(tw_dev))) +#define TWL_CLEAR_DB_INTERRUPT(x) (writel(~0, TWL_HOBDBC_REG_ADDR(tw_dev))) +#define TWL_SOFT_RESET(x) (writel(TWL_ISSUE_SOFT_RESET, TWL_HIBDB_REG_ADDR(tw_dev))) + +/* Macros */ +#define TW_PRINTK(h,a,b,c) { \ +if (h) \ +printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s.\n",h->host_no,a,b,c); \ +else \ +printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s.\n",a,b,c); \ +} +#define TW_MAX_LUNS 16 +#define TW_COMMAND_SIZE (sizeof(dma_addr_t) > 4 ? 6 : 4) +#define TW_LIBERATOR_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 46 : 92) +#define TW_LIBERATOR_MAX_SGL_LENGTH_OLD (sizeof(dma_addr_t) > 4 ? 47 : 94) +#define TW_PADDING_LENGTH_LIBERATOR 136 +#define TW_PADDING_LENGTH_LIBERATOR_OLD 132 +#define TW_CPU_TO_SGL(x) (sizeof(dma_addr_t) > 4 ? cpu_to_le64(x) : cpu_to_le32(x)) + +#pragma pack(1) + +/* SGL entry */ +typedef struct TAG_TW_SG_Entry_ISO { + dma_addr_t address; + dma_addr_t length; +} TW_SG_Entry_ISO; + +/* Old Command Packet with ISO SGL */ +typedef struct TW_Command { + unsigned char opcode__sgloffset; + unsigned char size; + unsigned char request_id; + unsigned char unit__hostid; + /* Second DWORD */ + unsigned char status; + unsigned char flags; + union { + unsigned short block_count; + unsigned short parameter_count; + } byte6_offset; + union { + struct { + u32 lba; + TW_SG_Entry_ISO sgl[TW_LIBERATOR_MAX_SGL_LENGTH_OLD]; + unsigned char padding[TW_PADDING_LENGTH_LIBERATOR_OLD]; + } io; + struct { + TW_SG_Entry_ISO sgl[TW_LIBERATOR_MAX_SGL_LENGTH_OLD]; + u32 padding; + unsigned char padding2[TW_PADDING_LENGTH_LIBERATOR_OLD]; + } param; + } byte8_offset; +} TW_Command; + +/* New Command Packet with ISO SGL */ +typedef struct TAG_TW_Command_Apache { + unsigned char opcode__reserved; + unsigned char unit; + unsigned short request_id__lunl; + unsigned char status; + unsigned char sgl_offset; + unsigned short sgl_entries__lunh; + unsigned char cdb[16]; + TW_SG_Entry_ISO sg_list[TW_LIBERATOR_MAX_SGL_LENGTH]; + unsigned char padding[TW_PADDING_LENGTH_LIBERATOR]; +} TW_Command_Apache; + +/* New command packet header */ +typedef struct TAG_TW_Command_Apache_Header { + unsigned char sense_data[TW_SENSE_DATA_LENGTH]; + struct { + char reserved[4]; + unsigned short error; + unsigned char padding; + unsigned char severity__reserved; + } status_block; + unsigned char err_specific_desc[98]; + struct { + unsigned char size_header; + unsigned short request_id; + unsigned char size_sense; + } header_desc; +} TW_Command_Apache_Header; + +/* This struct is a union of the 2 command packets */ +typedef struct TAG_TW_Command_Full { + TW_Command_Apache_Header header; + union { + TW_Command oldcommand; + TW_Command_Apache newcommand; + } command; +} TW_Command_Full; + +/* Initconnection structure */ +typedef struct TAG_TW_Initconnect { + unsigned char opcode__reserved; + unsigned char size; + unsigned char request_id; + unsigned char res2; + unsigned char status; + unsigned char flags; + unsigned short message_credits; + u32 features; + unsigned short fw_srl; + unsigned short fw_arch_id; + unsigned short fw_branch; + unsigned short fw_build; + u32 result; +} TW_Initconnect; + +/* Event info structure */ +typedef struct TAG_TW_Event +{ + unsigned int sequence_id; + unsigned int time_stamp_sec; + unsigned short aen_code; + unsigned char severity; + unsigned char retrieved; + unsigned char repeat_count; + unsigned char parameter_len; + unsigned char parameter_data[98]; +} TW_Event; + +typedef struct TAG_TW_Ioctl_Driver_Command { + unsigned int control_code; + unsigned int status; + unsigned int unique_id; + unsigned int sequence_id; + unsigned int os_specific; + unsigned int buffer_length; +} TW_Ioctl_Driver_Command; + +typedef struct TAG_TW_Ioctl_Apache { + TW_Ioctl_Driver_Command driver_command; + char padding[488]; + TW_Command_Full firmware_command; + char data_buffer[1]; +} TW_Ioctl_Buf_Apache; + +/* GetParam descriptor */ +typedef struct { + unsigned short table_id; + unsigned short parameter_id; + unsigned short parameter_size_bytes; + unsigned short actual_parameter_size_bytes; + unsigned char data[1]; +} TW_Param_Apache; + +/* Compatibility information structure */ +typedef struct TAG_TW_Compatibility_Info +{ + char driver_version[32]; + unsigned short working_srl; + unsigned short working_branch; + unsigned short working_build; + unsigned short driver_srl_high; + unsigned short driver_branch_high; + unsigned short driver_build_high; + unsigned short driver_srl_low; + unsigned short driver_branch_low; + unsigned short driver_build_low; + unsigned short fw_on_ctlr_srl; + unsigned short fw_on_ctlr_branch; + unsigned short fw_on_ctlr_build; +} TW_Compatibility_Info; + +#pragma pack() + +typedef struct TAG_TW_Device_Extension { + void __iomem *base_addr; + unsigned long *generic_buffer_virt[TW_Q_LENGTH]; + dma_addr_t generic_buffer_phys[TW_Q_LENGTH]; + TW_Command_Full *command_packet_virt[TW_Q_LENGTH]; + dma_addr_t command_packet_phys[TW_Q_LENGTH]; + TW_Command_Apache_Header *sense_buffer_virt[TW_Q_LENGTH]; + dma_addr_t sense_buffer_phys[TW_Q_LENGTH]; + struct pci_dev *tw_pci_dev; + struct scsi_cmnd *srb[TW_Q_LENGTH]; + unsigned char free_queue[TW_Q_LENGTH]; + unsigned char free_head; + unsigned char free_tail; + int state[TW_Q_LENGTH]; + unsigned int posted_request_count; + unsigned int max_posted_request_count; + unsigned int max_sgl_entries; + unsigned int sgl_entries; + unsigned int num_resets; + unsigned int sector_count; + unsigned int max_sector_count; + unsigned int aen_count; + struct Scsi_Host *host; + long flags; + TW_Event *event_queue[TW_Q_LENGTH]; + unsigned char error_index; + unsigned int error_sequence_id; + int chrdev_request_id; + wait_queue_head_t ioctl_wqueue; + struct mutex ioctl_lock; + TW_Compatibility_Info tw_compat_info; + char online; +} TW_Device_Extension; + +#endif /* _3W_SAS_H */ + diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index faa0fcfed71e..f65a1e92340c 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -8,7 +8,7 @@ Copyright (C) 1999-2009 3ware Inc. - Kernel compatiblity By: Andre Hedrick <andre@suse.com> + Kernel compatibility By: Andre Hedrick <andre@suse.com> Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com> Further tiny build fixes and trivial hoovering Alan Cox @@ -521,8 +521,12 @@ static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr, } /* End tw_show_stats() */ /* This function will set a devices queue depth */ -static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth) +static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (queue_depth > TW_Q_LENGTH-2) queue_depth = TW_Q_LENGTH-2; scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c index f5a9addb7050..9f4a911a6d8c 100644 --- a/drivers/scsi/53c700.c +++ b/drivers/scsi/53c700.c @@ -175,7 +175,7 @@ STATIC void NCR_700_chip_reset(struct Scsi_Host *host); STATIC int NCR_700_slave_alloc(struct scsi_device *SDpnt); STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt); STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt); -static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth); +static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth, int reason); static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth); STATIC struct device_attribute *NCR_700_dev_attrs[]; @@ -1491,7 +1491,7 @@ NCR_700_intr(int irq, void *dev_id) unsigned long flags; int handled = 0; - /* Use the host lock to serialise acess to the 53c700 + /* Use the host lock to serialise access to the 53c700 * hardware. Note: In future, we may need to take the queue * lock to enter the done routines. When that happens, we * need to ensure that for this driver, the host lock and the @@ -2082,8 +2082,11 @@ NCR_700_slave_destroy(struct scsi_device *SDp) } static int -NCR_700_change_queue_depth(struct scsi_device *SDp, int depth) +NCR_700_change_queue_depth(struct scsi_device *SDp, int depth, int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (depth > NCR_700_MAX_TAGS) depth = NCR_700_MAX_TAGS; diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index e11cca4c784c..36900c71a592 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -399,6 +399,17 @@ config SCSI_3W_9XXX Please read the comments at the top of <file:drivers/scsi/3w-9xxx.c>. +config SCSI_3W_SAS + tristate "3ware 97xx SAS/SATA-RAID support" + depends on PCI && SCSI + help + This driver supports the LSI 3ware 9750 6Gb/s SAS/SATA-RAID cards. + + <http://www.lsi.com> + + Please read the comments at the top of + <file:drivers/scsi/3w-sas.c>. + config SCSI_7000FASST tristate "7000FASST SCSI support" depends on ISA && SCSI && ISA_DMA_API @@ -621,6 +632,14 @@ config SCSI_FLASHPOINT substantial, so users of MultiMaster Host Adapters may not wish to include it. +config VMWARE_PVSCSI + tristate "VMware PVSCSI driver support" + depends on PCI && SCSI && X86 + help + This driver supports VMware's para virtualized SCSI HBA. + To compile this driver as a module, choose M here: the + module will be called vmw_pvscsi. + config LIBFC tristate "LibFC module" select SCSI_FC_ATTRS @@ -644,7 +663,7 @@ config FCOE config FCOE_FNIC tristate "Cisco FNIC Driver" depends on PCI && X86 - select LIBFC + select LIBFCOE help This is support for the Cisco PCI-Express FCoE HBA. @@ -1818,6 +1837,14 @@ config SCSI_PMCRAID ---help--- This driver supports the PMC SIERRA MaxRAID adapters. +config SCSI_PM8001 + tristate "PMC-Sierra SPC 8001 SAS/SATA Based Host Adapter driver" + depends on PCI && SCSI + select SCSI_SAS_LIBSAS + help + This driver supports PMC-Sierra PCIE SAS/SATA 8x6G SPC 8001 chip + based host adapters. + config SCSI_SRP tristate "SCSI RDMA Protocol helper library" depends on SCSI && PCI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 3ad61db5e3fa..280d3c657d60 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_SCSI_AIC79XX) += aic7xxx/ obj-$(CONFIG_SCSI_AACRAID) += aacraid/ obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/ +obj-$(CONFIG_SCSI_PM8001) += pm8001/ obj-$(CONFIG_SCSI_IPS) += ips.o obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o @@ -113,6 +114,7 @@ obj-$(CONFIG_SCSI_MESH) += mesh.o obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o obj-$(CONFIG_SCSI_3W_9XXX) += 3w-9xxx.o +obj-$(CONFIG_SCSI_3W_SAS) += 3w-sas.o obj-$(CONFIG_SCSI_PPA) += ppa.o obj-$(CONFIG_SCSI_IMM) += imm.o obj-$(CONFIG_JAZZ_ESP) += esp_scsi.o jazz_esp.o @@ -133,6 +135,7 @@ obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/ obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/ obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/ obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o +obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o obj-$(CONFIG_ARM) += arm/ diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index cdbdec9f4fb2..83986ed86556 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -526,10 +526,10 @@ struct aac_driver_ident /* * The adapter interface specs all queues to be located in the same - * physically contigous block. The host structure that defines the + * physically contiguous block. The host structure that defines the * commuication queues will assume they are each a separate physically - * contigous memory region that will support them all being one big - * contigous block. + * contiguous memory region that will support them all being one big + * contiguous block. * There is a command and response queue for each level and direction of * commuication. These regions are accessed by both the host and adapter. */ diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index d598eba630d0..666d5151d628 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -226,7 +226,7 @@ static int aac_comm_init(struct aac_dev * dev) spin_lock_init(&dev->fib_lock); /* - * Allocate the physically contigous space for the commuication + * Allocate the physically contiguous space for the commuication * queue headers. */ diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 9b97c3e016fe..e9373a2d14fa 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -472,8 +472,12 @@ static int aac_slave_configure(struct scsi_device *sdev) * total capacity and the queue depth supported by the target device. */ -static int aac_change_queue_depth(struct scsi_device *sdev, int depth) +static int aac_change_queue_depth(struct scsi_device *sdev, int depth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (sdev->tagged_supported && (sdev->type == TYPE_DISK) && (sdev_channel(sdev) == CONTAINER_CHANNEL)) { struct scsi_device * dev; diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index b756041f0b26..22626abdb630 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -7969,7 +7969,7 @@ static int advansys_reset(struct scsi_cmnd *scp) ASC_DBG(1, "before AscInitAsc1000Driver()\n"); status = AscInitAsc1000Driver(asc_dvc); - /* Refer to ASC_IERR_* defintions for meaning of 'err_code'. */ + /* Refer to ASC_IERR_* definitions for meaning of 'err_code'. */ if (asc_dvc->err_code) { scmd_printk(KERN_INFO, scp, "SCSI bus reset error: " "0x%x\n", asc_dvc->err_code); diff --git a/drivers/scsi/aic7xxx/aic79xx.seq b/drivers/scsi/aic7xxx/aic79xx.seq index 58bc17591b54..2fb78e35a9e5 100644 --- a/drivers/scsi/aic7xxx/aic79xx.seq +++ b/drivers/scsi/aic7xxx/aic79xx.seq @@ -217,7 +217,7 @@ BEGIN_CRITICAL; scbdma_tohost_done: test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone; /* - * An SCB has been succesfully uploaded to the host. + * An SCB has been successfully uploaded to the host. * If the SCB was uploaded for some reason other than * bad SCSI status (currently only for underruns), we * queue the SCB for normal completion. Otherwise, we @@ -1281,7 +1281,7 @@ END_CRITICAL; * Is it a disconnect message? Set a flag in the SCB to remind us * and await the bus going free. If this is an untagged transaction * store the SCB id for it in our untagged target table for lookup on - * a reselction. + * a reselection. */ mesgin_disconnect: /* diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index 63b521d615f2..4d419c155ce9 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c @@ -2487,7 +2487,7 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat) /* * Although the driver does not care about the * 'Selection in Progress' status bit, the busy - * LED does. SELINGO is only cleared by a sucessfull + * LED does. SELINGO is only cleared by a successfull * selection, so we must manually clear it to insure * the LED turns off just incase no future successful * selections occur (e.g. no devices on the bus). diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index 75b23317bd26..1222a7ac698a 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -2335,7 +2335,7 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd) /* * The sequencer will never re-reference the * in-core SCB. To make sure we are notified - * during reslection, set the MK_MESSAGE flag in + * during reselection, set the MK_MESSAGE flag in * the card's copy of the SCB. */ ahd_outb(ahd, SCB_CONTROL, diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq index 15196390e28d..5a4cfc954a9f 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.seq +++ b/drivers/scsi/aic7xxx/aic7xxx.seq @@ -1693,7 +1693,7 @@ if ((ahc->flags & AHC_INITIATORROLE) != 0) { * Is it a disconnect message? Set a flag in the SCB to remind us * and await the bus going free. If this is an untagged transaction * store the SCB id for it in our untagged target table for lookup on - * a reselction. + * a reselection. */ mesgin_disconnect: /* diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c index 8dfb59d58992..45aa728a76b2 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_core.c +++ b/drivers/scsi/aic7xxx/aic7xxx_core.c @@ -1733,7 +1733,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) /* * Although the driver does not care about the * 'Selection in Progress' status bit, the busy - * LED does. SELINGO is only cleared by a sucessfull + * LED does. SELINGO is only cleared by a successfull * selection, so we must manually clear it to insure * the LED turns off just incase no future successful * selections occur (e.g. no devices on the bus). diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index fd2b9785ff4f..8cb05dc8e6a1 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -2290,7 +2290,7 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) * In the non-paging case, the sequencer will * never re-reference the in-core SCB. * To make sure we are notified during - * reslection, set the MK_MESSAGE flag in + * reselection, set the MK_MESSAGE flag in * the card's copy of the SCB. */ if ((ahc->flags & AHC_PAGESCBS) == 0) { diff --git a/drivers/scsi/aic94xx/aic94xx_reg_def.h b/drivers/scsi/aic94xx/aic94xx_reg_def.h index a43e8cdf4ee4..28aaf349c111 100644 --- a/drivers/scsi/aic94xx/aic94xx_reg_def.h +++ b/drivers/scsi/aic94xx/aic94xx_reg_def.h @@ -1,5 +1,5 @@ /* - * Aic94xx SAS/SATA driver hardware registers defintions. + * Aic94xx SAS/SATA driver hardware registers definitions. * * Copyright (C) 2004 Adaptec, Inc. All rights reserved. * Copyright (C) 2004 David Chaw <david_chaw@adaptec.com> diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 80aac01b5a6f..47d5d19f8c92 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -98,8 +98,11 @@ static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb); static const char *arcmsr_info(struct Scsi_Host *); static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb); static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, - int queue_depth) + int queue_depth, int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (queue_depth > ARCMSR_MAX_CMD_PERLUN) queue_depth = ARCMSR_MAX_CMD_PERLUN; scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h index b36020dcf012..a93a5040f087 100644 --- a/drivers/scsi/be2iscsi/be.h +++ b/drivers/scsi/be2iscsi/be.h @@ -20,8 +20,10 @@ #include <linux/pci.h> #include <linux/if_vlan.h> - -#define FW_VER_LEN 32 +#include <linux/blk-iopoll.h> +#define FW_VER_LEN 32 +#define MCC_Q_LEN 128 +#define MCC_CQ_LEN 256 struct be_dma_mem { void *va; @@ -74,18 +76,14 @@ static inline void queue_tail_inc(struct be_queue_info *q) struct be_eq_obj { struct be_queue_info q; - char desc[32]; - - /* Adaptive interrupt coalescing (AIC) info */ - bool enable_aic; - u16 min_eqd; /* in usecs */ - u16 max_eqd; /* in usecs */ - u16 cur_eqd; /* in usecs */ + struct beiscsi_hba *phba; + struct be_queue_info *cq; + struct blk_iopoll iopoll; }; struct be_mcc_obj { - struct be_queue_info *q; - struct be_queue_info *cq; + struct be_queue_info q; + struct be_queue_info cq; }; struct be_ctrl_info { @@ -176,8 +174,4 @@ static inline void swap_dws(void *wrb, int len) } while (len); #endif /* __BIG_ENDIAN */ } - -extern void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, - u16 num_popped); - #endif /* BEISCSI_H */ diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 08007b6e42df..698a527d6cca 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -19,6 +19,16 @@ #include "be_mgmt.h" #include "be_main.h" +static void be_mcc_notify(struct beiscsi_hba *phba) +{ + struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; + u32 val = 0; + + val |= mccq->id & DB_MCCQ_RING_ID_MASK; + val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT; + iowrite32(val, phba->db_va + DB_MCCQ_OFFSET); +} + static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl) { if (compl->flags != 0) { @@ -54,13 +64,56 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl, return 0; } + static inline bool is_link_state_evt(u32 trailer) { return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) & - ASYNC_TRAILER_EVENT_CODE_MASK) == ASYNC_EVENT_CODE_LINK_STATE); + ASYNC_TRAILER_EVENT_CODE_MASK) == + ASYNC_EVENT_CODE_LINK_STATE); +} + +static struct be_mcc_compl *be_mcc_compl_get(struct beiscsi_hba *phba) +{ + struct be_queue_info *mcc_cq = &phba->ctrl.mcc_obj.cq; + struct be_mcc_compl *compl = queue_tail_node(mcc_cq); + + if (be_mcc_compl_is_new(compl)) { + queue_tail_inc(mcc_cq); + return compl; + } + return NULL; +} + +static void be2iscsi_fail_session(struct iscsi_cls_session *cls_session) +{ + iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); +} + +static void beiscsi_async_link_state_process(struct beiscsi_hba *phba, + struct be_async_event_link_state *evt) +{ + switch (evt->port_link_status) { + case ASYNC_EVENT_LINK_DOWN: + SE_DEBUG(DBG_LVL_1, "Link Down on Physical Port %d \n", + evt->physical_port); + phba->state |= BE_ADAPTER_LINK_DOWN; + break; + case ASYNC_EVENT_LINK_UP: + phba->state = BE_ADAPTER_UP; + SE_DEBUG(DBG_LVL_1, "Link UP on Physical Port %d \n", + evt->physical_port); + iscsi_host_for_each_session(phba->shost, + be2iscsi_fail_session); + break; + default: + SE_DEBUG(DBG_LVL_1, "Unexpected Async Notification %d on" + "Physical Port %d \n", + evt->port_link_status, + evt->physical_port); + } } -void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, +static void beiscsi_cq_notify(struct beiscsi_hba *phba, u16 qid, bool arm, u16 num_popped) { u32 val = 0; @@ -68,7 +121,66 @@ void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, if (arm) val |= 1 << DB_CQ_REARM_SHIFT; val |= num_popped << DB_CQ_NUM_POPPED_SHIFT; - iowrite32(val, ctrl->db + DB_CQ_OFFSET); + iowrite32(val, phba->db_va + DB_CQ_OFFSET); +} + + +int beiscsi_process_mcc(struct beiscsi_hba *phba) +{ + struct be_mcc_compl *compl; + int num = 0, status = 0; + struct be_ctrl_info *ctrl = &phba->ctrl; + + spin_lock_bh(&phba->ctrl.mcc_cq_lock); + while ((compl = be_mcc_compl_get(phba))) { + if (compl->flags & CQE_FLAGS_ASYNC_MASK) { + /* Interpret flags as an async trailer */ + BUG_ON(!is_link_state_evt(compl->flags)); + + /* Interpret compl as a async link evt */ + beiscsi_async_link_state_process(phba, + (struct be_async_event_link_state *) compl); + } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) { + status = be_mcc_compl_process(ctrl, compl); + atomic_dec(&phba->ctrl.mcc_obj.q.used); + } + be_mcc_compl_use(compl); + num++; + } + + if (num) + beiscsi_cq_notify(phba, phba->ctrl.mcc_obj.cq.id, true, num); + + spin_unlock_bh(&phba->ctrl.mcc_cq_lock); + return status; +} + +/* Wait till no more pending mcc requests are present */ +static int be_mcc_wait_compl(struct beiscsi_hba *phba) +{ +#define mcc_timeout 120000 /* 5s timeout */ + int i, status; + for (i = 0; i < mcc_timeout; i++) { + status = beiscsi_process_mcc(phba); + if (status) + return status; + + if (atomic_read(&phba->ctrl.mcc_obj.q.used) == 0) + break; + udelay(100); + } + if (i == mcc_timeout) { + dev_err(&phba->pcidev->dev, "mccq poll timed out\n"); + return -1; + } + return 0; +} + +/* Notify MCC requests and wait for completion */ +int be_mcc_notify_wait(struct beiscsi_hba *phba) +{ + be_mcc_notify(phba); + return be_mcc_wait_compl(phba); } static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl) @@ -142,6 +254,52 @@ int be_mbox_notify(struct be_ctrl_info *ctrl) return 0; } +/* + * Insert the mailbox address into the doorbell in two steps + * Polls on the mbox doorbell till a command completion (or a timeout) occurs + */ +static int be_mbox_notify_wait(struct beiscsi_hba *phba) +{ + int status; + u32 val = 0; + void __iomem *db = phba->ctrl.db + MPU_MAILBOX_DB_OFFSET; + struct be_dma_mem *mbox_mem = &phba->ctrl.mbox_mem; + struct be_mcc_mailbox *mbox = mbox_mem->va; + struct be_mcc_compl *compl = &mbox->compl; + struct be_ctrl_info *ctrl = &phba->ctrl; + + val |= MPU_MAILBOX_DB_HI_MASK; + /* at bits 2 - 31 place mbox dma addr msb bits 34 - 63 */ + val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2; + iowrite32(val, db); + + /* wait for ready to be set */ + status = be_mbox_db_ready_wait(ctrl); + if (status != 0) + return status; + + val = 0; + /* at bits 2 - 31 place mbox dma addr lsb bits 4 - 33 */ + val |= (u32)(mbox_mem->dma >> 4) << 2; + iowrite32(val, db); + + status = be_mbox_db_ready_wait(ctrl); + if (status != 0) + return status; + + /* A cq entry has been made now */ + if (be_mcc_compl_is_new(compl)) { + status = be_mcc_compl_process(ctrl, &mbox->compl); + be_mcc_compl_use(compl); + if (status) + return status; + } else { + dev_err(&phba->pcidev->dev, "invalid mailbox completion\n"); + return -1; + } + return 0; +} + void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, bool embedded, u8 sge_cnt) { @@ -203,6 +361,20 @@ struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem) return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb; } +struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba) +{ + struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; + struct be_mcc_wrb *wrb; + + BUG_ON(atomic_read(&mccq->used) >= mccq->len); + wrb = queue_head_node(mccq); + queue_head_inc(mccq); + atomic_inc(&mccq->used); + memset(wrb, 0, sizeof(*wrb)); + return wrb; +} + + int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_queue_info *eq, int eq_delay) { @@ -212,6 +384,7 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem = &eq->dma_mem; int status; + SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_eq_create\n"); spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -249,6 +422,7 @@ int be_cmd_fw_initialize(struct be_ctrl_info *ctrl) int status; u8 *endian_check; + SE_DEBUG(DBG_LVL_8, "In be_cmd_fw_initialize\n"); spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -282,6 +456,7 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, void *ctxt = &req->context; int status; + SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_cq_create \n"); spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -289,7 +464,6 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_CQ_CREATE, sizeof(*req)); - if (!q_mem->va) SE_DEBUG(DBG_LVL_1, "uninitialized q_mem->va\n"); @@ -329,6 +503,53 @@ static u32 be_encoded_q_len(int q_len) len_encoded = 0; return len_encoded; } + +int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba, + struct be_queue_info *mccq, + struct be_queue_info *cq) +{ + struct be_mcc_wrb *wrb; + struct be_cmd_req_mcc_create *req; + struct be_dma_mem *q_mem = &mccq->dma_mem; + struct be_ctrl_info *ctrl; + void *ctxt; + int status; + + spin_lock(&phba->ctrl.mbox_lock); + ctrl = &phba->ctrl; + wrb = wrb_from_mbox(&ctrl->mbox_mem); + req = embedded_payload(wrb); + ctxt = &req->context; + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_MCC_CREATE, sizeof(*req)); + + req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); + + AMAP_SET_BITS(struct amap_mcc_context, fid, ctxt, + PCI_FUNC(phba->pcidev->devfn)); + AMAP_SET_BITS(struct amap_mcc_context, valid, ctxt, 1); + AMAP_SET_BITS(struct amap_mcc_context, ring_size, ctxt, + be_encoded_q_len(mccq->len)); + AMAP_SET_BITS(struct amap_mcc_context, cq_id, ctxt, cq->id); + + be_dws_cpu_to_le(ctxt, sizeof(req->context)); + + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify_wait(phba); + if (!status) { + struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb); + mccq->id = le16_to_cpu(resp->id); + mccq->created = true; + } + spin_unlock(&phba->ctrl.mbox_lock); + + return status; +} + int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, int queue_type) { @@ -337,6 +558,7 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, u8 subsys = 0, opcode = 0; int status; + SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_q_destroy \n"); spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -350,6 +572,10 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, subsys = CMD_SUBSYSTEM_COMMON; opcode = OPCODE_COMMON_CQ_DESTROY; break; + case QTYPE_MCCQ: + subsys = CMD_SUBSYSTEM_COMMON; + opcode = OPCODE_COMMON_MCC_DESTROY; + break; case QTYPE_WRBQ: subsys = CMD_SUBSYSTEM_ISCSI; opcode = OPCODE_COMMON_ISCSI_WRBQ_DESTROY; @@ -377,30 +603,6 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, return status; } -int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr) -{ - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb); - int status; - - spin_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, - OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG, - sizeof(*req)); - - status = be_mbox_notify(ctrl); - if (!status) { - struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb); - - memcpy(mac_addr, resp->mac_address, ETH_ALEN); - } - - spin_unlock(&ctrl->mbox_lock); - return status; -} - int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl, struct be_queue_info *cq, struct be_queue_info *dq, int length, @@ -412,6 +614,7 @@ int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl, void *ctxt = &req->context; int status; + SE_DEBUG(DBG_LVL_8, "In be_cmd_create_default_pdu_queue\n"); spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -468,8 +671,10 @@ int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); status = be_mbox_notify(ctrl); - if (!status) + if (!status) { wrbq->id = le16_to_cpu(resp->cid); + wrbq->created = true; + } spin_unlock(&ctrl->mbox_lock); return status; } diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index c20d686cbb43..5de8acb924cb 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -47,6 +47,8 @@ struct be_mcc_wrb { #define CQE_FLAGS_VALID_MASK (1 << 31) #define CQE_FLAGS_ASYNC_MASK (1 << 30) +#define CQE_FLAGS_COMPLETED_MASK (1 << 28) +#define CQE_FLAGS_CONSUMED_MASK (1 << 27) /* Completion Status */ #define MCC_STATUS_SUCCESS 0x0 @@ -173,7 +175,7 @@ struct be_cmd_req_hdr { u8 domain; /* dword 0 */ u32 timeout; /* dword 1 */ u32 request_length; /* dword 2 */ - u32 rsvd; /* dword 3 */ + u32 rsvd0; /* dword 3 */ }; struct be_cmd_resp_hdr { @@ -382,7 +384,6 @@ struct be_cmd_req_modify_eq_delay { #define ETH_ALEN 6 - struct be_cmd_req_get_mac_addr { struct be_cmd_req_hdr hdr; u32 nic_port_count; @@ -417,14 +418,21 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, int type); +int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba, + struct be_queue_info *mccq, + struct be_queue_info *cq); + int be_poll_mcc(struct be_ctrl_info *ctrl); -unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl); -int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr); +unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba); +int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr); /*ISCSI Functuions */ int be_cmd_fw_initialize(struct be_ctrl_info *ctrl); struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem); +struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba); +int be_mcc_notify_wait(struct beiscsi_hba *phba); int be_mbox_notify(struct be_ctrl_info *ctrl); @@ -531,6 +539,23 @@ struct amap_sol_cqe { u8 valid; /* dword 3 */ } __packed; +#define SOL_ICD_INDEX_MASK 0x0003FFC0 +struct amap_sol_cqe_ring { + u8 hw_sts[8]; /* dword 0 */ + u8 i_sts[8]; /* dword 0 */ + u8 i_resp[8]; /* dword 0 */ + u8 i_flags[7]; /* dword 0 */ + u8 s; /* dword 0 */ + u8 i_exp_cmd_sn[32]; /* dword 1 */ + u8 code[6]; /* dword 2 */ + u8 icd_index[12]; /* dword 2 */ + u8 rsvd[6]; /* dword 2 */ + u8 i_cmd_wnd[8]; /* dword 2 */ + u8 i_res_cnt[31]; /* dword 3 */ + u8 valid; /* dword 3 */ +} __packed; + + /** * Post WRB Queue Doorbell Register used by the host Storage @@ -664,8 +689,8 @@ struct be_fw_cfg { #define OPCODE_COMMON_TCP_UPLOAD 56 #define OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS 1 /* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */ -#define CMD_ISCSI_CONNECTION_INVALIDATE 1 -#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 2 +#define CMD_ISCSI_CONNECTION_INVALIDATE 0x8001 +#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 0x8002 #define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42 #define INI_WR_CMD 1 /* Initiator write command */ diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 2fd25442cfaf..d587b0362f18 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -67,11 +67,11 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn; } - cls_session = iscsi_session_setup(&beiscsi_iscsi_transport, - shost, cmds_max, - sizeof(*beiscsi_sess), - sizeof(*io_task), - initial_cmdsn, ISCSI_MAX_TARGET); + cls_session = iscsi_session_setup(&beiscsi_iscsi_transport, + shost, cmds_max, + sizeof(*beiscsi_sess), + sizeof(*io_task), + initial_cmdsn, ISCSI_MAX_TARGET); if (!cls_session) return NULL; sess = cls_session->dd_data; @@ -297,7 +297,7 @@ int beiscsi_get_host_param(struct Scsi_Host *shost, switch (param) { case ISCSI_HOST_PARAM_HWADDRESS: - be_cmd_get_mac_addr(&phba->ctrl, phba->mac_address); + be_cmd_get_mac_addr(phba, phba->mac_address); len = sysfs_format_mac(buf, phba->mac_address, ETH_ALEN); break; default: @@ -377,16 +377,12 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn) struct beiscsi_conn *beiscsi_conn = conn->dd_data; struct beiscsi_endpoint *beiscsi_ep; struct beiscsi_offload_params params; - struct iscsi_session *session = conn->session; - struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session); - struct beiscsi_hba *phba = iscsi_host_priv(shost); memset(¶ms, 0, sizeof(struct beiscsi_offload_params)); beiscsi_ep = beiscsi_conn->ep; if (!beiscsi_ep) SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n"); - free_mgmt_sgl_handle(phba, beiscsi_conn->plogin_sgl_handle); beiscsi_conn->login_in_progress = 0; beiscsi_set_params_for_offld(beiscsi_conn, ¶ms); beiscsi_offload_connection(beiscsi_conn, ¶ms); @@ -498,6 +494,13 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, SE_DEBUG(DBG_LVL_1, "shost is NULL \n"); return ERR_PTR(ret); } + + if (phba->state) { + ret = -EBUSY; + SE_DEBUG(DBG_LVL_1, "The Adapet state is Not UP \n"); + return ERR_PTR(ret); + } + ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint)); if (!ep) { ret = -ENOMEM; diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 4f1aca346e38..1a557fa77888 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -39,7 +39,8 @@ static unsigned int be_iopoll_budget = 10; static unsigned int be_max_phys_size = 64; -static unsigned int enable_msix; +static unsigned int enable_msix = 1; +static unsigned int ring_mode; MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table); MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR); @@ -58,6 +59,17 @@ static int beiscsi_slave_configure(struct scsi_device *sdev) return 0; } +/*------------------- PCI Driver operations and data ----------------- */ +static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = { + { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID3) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID4) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table); + static struct scsi_host_template beiscsi_sht = { .module = THIS_MODULE, .name = "ServerEngines 10Gbe open-iscsi Initiator Driver", @@ -76,16 +88,8 @@ static struct scsi_host_template beiscsi_sht = { .cmd_per_lun = BEISCSI_CMD_PER_LUN, .use_clustering = ENABLE_CLUSTERING, }; -static struct scsi_transport_template *beiscsi_scsi_transport; -/*------------------- PCI Driver operations and data ----------------- */ -static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = { - { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, - { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) }, - { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table); +static struct scsi_transport_template *beiscsi_scsi_transport; static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev) { @@ -104,7 +108,6 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev) shost->max_cmd_len = BEISCSI_MAX_CMD_LEN; shost->max_lun = BEISCSI_NUM_MAX_LUN; shost->transportt = beiscsi_scsi_transport; - phba = iscsi_host_priv(shost); memset(phba, 0, sizeof(*phba)); phba->shost = shost; @@ -181,6 +184,7 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev) return ret; } + pci_set_master(pcidev); if (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) { ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32)); if (ret) { @@ -203,7 +207,6 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev) status = beiscsi_map_pci_bars(phba, pdev); if (status) return status; - mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16; mbox_mem_alloc->va = pci_alloc_consistent(pdev, mbox_mem_alloc->size, @@ -219,6 +222,9 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev) mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16); memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox)); spin_lock_init(&ctrl->mbox_lock); + spin_lock_init(&phba->ctrl.mcc_lock); + spin_lock_init(&phba->ctrl.mcc_cq_lock); + return status; } @@ -268,6 +274,113 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba, } /** + * be_isr_mcc - The isr routine of the driver. + * @irq: Not used + * @dev_id: Pointer to host adapter structure + */ +static irqreturn_t be_isr_mcc(int irq, void *dev_id) +{ + struct beiscsi_hba *phba; + struct be_eq_entry *eqe = NULL; + struct be_queue_info *eq; + struct be_queue_info *mcc; + unsigned int num_eq_processed; + struct be_eq_obj *pbe_eq; + unsigned long flags; + + pbe_eq = dev_id; + eq = &pbe_eq->q; + phba = pbe_eq->phba; + mcc = &phba->ctrl.mcc_obj.cq; + eqe = queue_tail_node(eq); + if (!eqe) + SE_DEBUG(DBG_LVL_1, "eqe is NULL\n"); + + num_eq_processed = 0; + + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + if (((eqe->dw[offsetof(struct amap_eq_entry, + resource_id) / 32] & + EQE_RESID_MASK) >> 16) == mcc->id) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_mcc_cq = 1; + spin_unlock_irqrestore(&phba->isr_lock, flags); + } + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_eq_processed++; + } + if (phba->todo_mcc_cq) + queue_work(phba->wq, &phba->work_cqs); + if (num_eq_processed) + hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1); + + return IRQ_HANDLED; +} + +/** + * be_isr_msix - The isr routine of the driver. + * @irq: Not used + * @dev_id: Pointer to host adapter structure + */ +static irqreturn_t be_isr_msix(int irq, void *dev_id) +{ + struct beiscsi_hba *phba; + struct be_eq_entry *eqe = NULL; + struct be_queue_info *eq; + struct be_queue_info *cq; + unsigned int num_eq_processed; + struct be_eq_obj *pbe_eq; + unsigned long flags; + + pbe_eq = dev_id; + eq = &pbe_eq->q; + cq = pbe_eq->cq; + eqe = queue_tail_node(eq); + if (!eqe) + SE_DEBUG(DBG_LVL_1, "eqe is NULL\n"); + + phba = pbe_eq->phba; + num_eq_processed = 0; + if (blk_iopoll_enabled) { + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + if (!blk_iopoll_sched_prep(&pbe_eq->iopoll)) + blk_iopoll_sched(&pbe_eq->iopoll); + + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_eq_processed++; + } + if (num_eq_processed) + hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1); + + return IRQ_HANDLED; + } else { + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_cq = 1; + spin_unlock_irqrestore(&phba->isr_lock, flags); + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_eq_processed++; + } + if (phba->todo_cq) + queue_work(phba->wq, &phba->work_cqs); + + if (num_eq_processed) + hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1); + + return IRQ_HANDLED; + } +} + +/** * be_isr - The isr routine of the driver. * @irq: Not used * @dev_id: Pointer to host adapter structure @@ -280,48 +393,70 @@ static irqreturn_t be_isr(int irq, void *dev_id) struct be_eq_entry *eqe = NULL; struct be_queue_info *eq; struct be_queue_info *cq; + struct be_queue_info *mcc; unsigned long flags, index; - unsigned int num_eq_processed; + unsigned int num_mcceq_processed, num_ioeq_processed; struct be_ctrl_info *ctrl; + struct be_eq_obj *pbe_eq; int isr; phba = dev_id; - if (!enable_msix) { - ctrl = &phba->ctrl;; - isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET + - (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE)); - if (!isr) - return IRQ_NONE; - } + ctrl = &phba->ctrl;; + isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET + + (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE)); + if (!isr) + return IRQ_NONE; phwi_ctrlr = phba->phwi_ctrlr; phwi_context = phwi_ctrlr->phwi_ctxt; - eq = &phwi_context->be_eq.q; - cq = &phwi_context->be_cq; + pbe_eq = &phwi_context->be_eq[0]; + + eq = &phwi_context->be_eq[0].q; + mcc = &phba->ctrl.mcc_obj.cq; index = 0; eqe = queue_tail_node(eq); if (!eqe) SE_DEBUG(DBG_LVL_1, "eqe is NULL\n"); - num_eq_processed = 0; + num_ioeq_processed = 0; + num_mcceq_processed = 0; if (blk_iopoll_enabled) { while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { - if (!blk_iopoll_sched_prep(&phba->iopoll)) - blk_iopoll_sched(&phba->iopoll); - + if (((eqe->dw[offsetof(struct amap_eq_entry, + resource_id) / 32] & + EQE_RESID_MASK) >> 16) == mcc->id) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_mcc_cq = 1; + spin_unlock_irqrestore(&phba->isr_lock, flags); + num_mcceq_processed++; + } else { + if (!blk_iopoll_sched_prep(&pbe_eq->iopoll)) + blk_iopoll_sched(&pbe_eq->iopoll); + num_ioeq_processed++; + } AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); eqe = queue_tail_node(eq); - num_eq_processed++; - SE_DEBUG(DBG_LVL_8, "Valid EQE\n"); } - if (num_eq_processed) { - hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 0, 1); + if (num_ioeq_processed || num_mcceq_processed) { + if (phba->todo_mcc_cq) + queue_work(phba->wq, &phba->work_cqs); + + if ((num_mcceq_processed) && (!num_ioeq_processed)) + hwi_ring_eq_db(phba, eq->id, 0, + (num_ioeq_processed + + num_mcceq_processed) , 1, 1); + else + hwi_ring_eq_db(phba, eq->id, 0, + (num_ioeq_processed + + num_mcceq_processed), 0, 1); + return IRQ_HANDLED; } else return IRQ_NONE; } else { + cq = &phwi_context->be_cq[0]; while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { @@ -339,13 +474,14 @@ static irqreturn_t be_isr(int irq, void *dev_id) AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); eqe = queue_tail_node(eq); - num_eq_processed++; + num_ioeq_processed++; } if (phba->todo_cq || phba->todo_mcc_cq) queue_work(phba->wq, &phba->work_cqs); - if (num_eq_processed) { - hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 1, 1); + if (num_ioeq_processed) { + hwi_ring_eq_db(phba, eq->id, 0, + num_ioeq_processed, 1, 1); return IRQ_HANDLED; } else return IRQ_NONE; @@ -355,13 +491,32 @@ static irqreturn_t be_isr(int irq, void *dev_id) static int beiscsi_init_irqs(struct beiscsi_hba *phba) { struct pci_dev *pcidev = phba->pcidev; - int ret; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + int ret, msix_vec, i = 0; + char desc[32]; - ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED, "beiscsi", phba); - if (ret) { - shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-" - "Failed to register irq\\n"); - return ret; + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + + if (phba->msix_enabled) { + for (i = 0; i < phba->num_cpus; i++) { + sprintf(desc, "beiscsi_msix_%04x", i); + msix_vec = phba->msix_entries[i].vector; + ret = request_irq(msix_vec, be_isr_msix, 0, desc, + &phwi_context->be_eq[i]); + } + msix_vec = phba->msix_entries[i].vector; + ret = request_irq(msix_vec, be_isr_mcc, 0, "beiscsi_msix_mcc", + &phwi_context->be_eq[i]); + } else { + ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED, + "beiscsi", phba); + if (ret) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-" + "Failed to register irq\\n"); + return ret; + } } return 0; } @@ -378,15 +533,6 @@ static void hwi_ring_cq_db(struct beiscsi_hba *phba, iowrite32(val, phba->db_va + DB_CQ_OFFSET); } -/* - * async pdus include - * a. unsolicited NOP-In (target initiated NOP-In) - * b. Async Messages - * c. Reject PDU - * d. Login response - * These headers arrive unprocessed by the EP firmware and iSCSI layer - * process them - */ static unsigned int beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn, struct beiscsi_hba *phba, @@ -397,6 +543,9 @@ beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn, { struct iscsi_conn *conn = beiscsi_conn->conn; struct iscsi_session *session = conn->session; + struct iscsi_task *task; + struct beiscsi_io_task *io_task; + struct iscsi_hdr *login_hdr; switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] & PDUBASE_OPCODE_MASK) { @@ -412,6 +561,10 @@ beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn, SE_DEBUG(DBG_LVL_1, "In ISCSI_OP_REJECT\n"); break; case ISCSI_OP_LOGIN_RSP: + task = conn->login_task; + io_task = task->dd_data; + login_hdr = (struct iscsi_hdr *)ppdu; + login_hdr->itt = io_task->libiscsi_itt; break; default: shost_printk(KERN_WARNING, phba->shost, @@ -440,7 +593,8 @@ static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba) io_sgl_alloc_index]; phba->io_sgl_hndl_base[phba->io_sgl_alloc_index] = NULL; phba->io_sgl_hndl_avbl--; - if (phba->io_sgl_alloc_index == (phba->params.ios_per_ctrl - 1)) + if (phba->io_sgl_alloc_index == (phba->params. + ios_per_ctrl - 1)) phba->io_sgl_alloc_index = 0; else phba->io_sgl_alloc_index++; @@ -490,9 +644,18 @@ struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, phwi_ctrlr = phba->phwi_ctrlr; pwrb_context = &phwi_ctrlr->wrb_context[cid]; - pwrb_handle = pwrb_context->pwrb_handle_base[index]; - pwrb_handle->wrb_index = index; - pwrb_handle->nxt_wrb_index = index; + if (pwrb_context->wrb_handles_available) { + pwrb_handle = pwrb_context->pwrb_handle_base[ + pwrb_context->alloc_index]; + pwrb_context->wrb_handles_available--; + pwrb_handle->nxt_wrb_index = pwrb_handle->wrb_index; + if (pwrb_context->alloc_index == + (phba->params.wrbs_per_cxn - 1)) + pwrb_context->alloc_index = 0; + else + pwrb_context->alloc_index++; + } else + pwrb_handle = NULL; return pwrb_handle; } @@ -508,11 +671,20 @@ static void free_wrb_handle(struct beiscsi_hba *phba, struct hwi_wrb_context *pwrb_context, struct wrb_handle *pwrb_handle) { + if (!ring_mode) + pwrb_context->pwrb_handle_base[pwrb_context->free_index] = + pwrb_handle; + pwrb_context->wrb_handles_available++; + if (pwrb_context->free_index == (phba->params.wrbs_per_cxn - 1)) + pwrb_context->free_index = 0; + else + pwrb_context->free_index++; + SE_DEBUG(DBG_LVL_8, - "FREE WRB: pwrb_handle=%p free_index=%d=0x%x" + "FREE WRB: pwrb_handle=%p free_index=0x%x" "wrb_handles_available=%d \n", pwrb_handle, pwrb_context->free_index, - pwrb_context->free_index, pwrb_context->wrb_handles_available); + pwrb_context->wrb_handles_available); } static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba) @@ -540,6 +712,8 @@ void free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle) { + SE_DEBUG(DBG_LVL_8, "In free_mgmt_sgl_handle,eh_sgl_free_index=%d \n", + phba->eh_sgl_free_index); if (phba->eh_sgl_hndl_base[phba->eh_sgl_free_index]) { /* * this can happen if clean_task is called on a task that @@ -572,10 +746,10 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn, u32 resid = 0, exp_cmdsn, max_cmdsn; u8 rsp, status, flags; - exp_cmdsn = be32_to_cpu(psol-> + exp_cmdsn = (psol-> dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK); - max_cmdsn = be32_to_cpu((psol-> + max_cmdsn = ((psol-> dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) @@ -610,9 +784,9 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn, } if (status == SAM_STAT_CHECK_CONDITION) { + unsigned short *slen = (unsigned short *)sts_bhs->sense_info; sense = sts_bhs->sense_info + sizeof(unsigned short); - sense_len = - cpu_to_be16((unsigned short)(sts_bhs->sense_info[0])); + sense_len = cpu_to_be16(*slen); memcpy(task->sc->sense_buffer, sense, min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE)); } @@ -620,8 +794,8 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn, if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32] & SOL_RES_CNT_MASK) conn->rxdata_octets += (psol-> - dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32] - & SOL_RES_CNT_MASK); + dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32] + & SOL_RES_CNT_MASK); } unmap: scsi_dma_unmap(io_task->scsi_cmnd); @@ -633,6 +807,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn, struct iscsi_task *task, struct sol_cqe *psol) { struct iscsi_logout_rsp *hdr; + struct beiscsi_io_task *io_task = task->dd_data; struct iscsi_conn *conn = beiscsi_conn->conn; hdr = (struct iscsi_logout_rsp *)task->hdr; @@ -651,7 +826,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn, ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) / 32] & SOL_CMD_WND_MASK) >> 24) - 1); hdr->hlength = 0; - + hdr->itt = io_task->libiscsi_itt; __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); } @@ -661,6 +836,7 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn, { struct iscsi_tm_rsp *hdr; struct iscsi_conn *conn = beiscsi_conn->conn; + struct beiscsi_io_task *io_task = task->dd_data; hdr = (struct iscsi_tm_rsp *)task->hdr; hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] @@ -668,11 +844,12 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn, hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) / 32] & SOL_RESP_MASK); hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe, - i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK); + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK); hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + hdr->itt = io_task->libiscsi_itt; __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); } @@ -681,18 +858,36 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, struct beiscsi_hba *phba, struct sol_cqe *psol) { struct hwi_wrb_context *pwrb_context; - struct wrb_handle *pwrb_handle; + struct wrb_handle *pwrb_handle = NULL; + struct sgl_handle *psgl_handle = NULL; struct hwi_controller *phwi_ctrlr; + struct iscsi_task *task; + struct beiscsi_io_task *io_task; struct iscsi_conn *conn = beiscsi_conn->conn; struct iscsi_session *session = conn->session; phwi_ctrlr = phba->phwi_ctrlr; - pwrb_context = &phwi_ctrlr->wrb_context[((psol-> + if (ring_mode) { + psgl_handle = phba->sgl_hndl_array[((psol-> + dw[offsetof(struct amap_sol_cqe_ring, icd_index) / + 32] & SOL_ICD_INDEX_MASK) >> 6)]; + pwrb_context = &phwi_ctrlr->wrb_context[psgl_handle->cid]; + task = psgl_handle->task; + pwrb_handle = NULL; + } else { + pwrb_context = &phwi_ctrlr->wrb_context[((psol-> dw[offsetof(struct amap_sol_cqe, cid) / 32] & SOL_CID_MASK) >> 6)]; - pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol-> + pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol-> dw[offsetof(struct amap_sol_cqe, wrb_index) / 32] & SOL_WRB_INDEX_MASK) >> 16)]; + task = pwrb_handle->pio_handle; + } + + io_task = task->dd_data; + spin_lock(&phba->mgmt_sgl_lock); + free_mgmt_sgl_handle(phba, io_task->psgl_handle); + spin_unlock(&phba->mgmt_sgl_lock); spin_lock_bh(&session->lock); free_wrb_handle(phba, pwrb_context, pwrb_handle); spin_unlock_bh(&session->lock); @@ -704,6 +899,7 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn, { struct iscsi_nopin *hdr; struct iscsi_conn *conn = beiscsi_conn->conn; + struct beiscsi_io_task *io_task = task->dd_data; hdr = (struct iscsi_nopin *)task->hdr; hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] @@ -715,6 +911,7 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn, ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) / 32] & SOL_CMD_WND_MASK) >> 24) - 1); hdr->opcode = ISCSI_OP_NOOP_IN; + hdr->itt = io_task->libiscsi_itt; __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); } @@ -726,25 +923,33 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn, struct iscsi_wrb *pwrb = NULL; struct hwi_controller *phwi_ctrlr; struct iscsi_task *task; - struct beiscsi_io_task *io_task; + struct sgl_handle *psgl_handle = NULL; + unsigned int type; struct iscsi_conn *conn = beiscsi_conn->conn; struct iscsi_session *session = conn->session; phwi_ctrlr = phba->phwi_ctrlr; - - pwrb_context = &phwi_ctrlr-> - wrb_context[((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32] - & SOL_CID_MASK) >> 6)]; - pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol-> + if (ring_mode) { + psgl_handle = phba->sgl_hndl_array[((psol-> + dw[offsetof(struct amap_sol_cqe_ring, icd_index) / + 32] & SOL_ICD_INDEX_MASK) >> 6)]; + task = psgl_handle->task; + type = psgl_handle->type; + } else { + pwrb_context = &phwi_ctrlr-> + wrb_context[((psol->dw[offsetof + (struct amap_sol_cqe, cid) / 32] + & SOL_CID_MASK) >> 6)]; + pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol-> dw[offsetof(struct amap_sol_cqe, wrb_index) / 32] & SOL_WRB_INDEX_MASK) >> 16)]; - - task = pwrb_handle->pio_handle; - io_task = task->dd_data; + task = pwrb_handle->pio_handle; + pwrb = pwrb_handle->pwrb; + type = (pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] & + WRB_TYPE_MASK) >> 28; + } spin_lock_bh(&session->lock); - pwrb = pwrb_handle->pwrb; - switch ((pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] & - WRB_TYPE_MASK) >> 28) { + switch (type) { case HWH_TYPE_IO: case HWH_TYPE_IO_RD: if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == @@ -773,12 +978,21 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn, break; default: - shost_printk(KERN_WARNING, phba->shost, - "wrb_index 0x%x CID 0x%x\n", - ((psol->dw[offsetof(struct amap_iscsi_wrb, type) / - 32] & SOL_WRB_INDEX_MASK) >> 16), - ((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32] - & SOL_CID_MASK) >> 6)); + if (ring_mode) + shost_printk(KERN_WARNING, phba->shost, + "In hwi_complete_cmd, unknown type = %d" + "icd_index 0x%x CID 0x%x\n", type, + ((psol->dw[offsetof(struct amap_sol_cqe_ring, + icd_index) / 32] & SOL_ICD_INDEX_MASK) >> 6), + psgl_handle->cid); + else + shost_printk(KERN_WARNING, phba->shost, + "In hwi_complete_cmd, unknown type = %d" + "wrb_index 0x%x CID 0x%x\n", type, + ((psol->dw[offsetof(struct amap_iscsi_wrb, + type) / 32] & SOL_WRB_INDEX_MASK) >> 16), + ((psol->dw[offsetof(struct amap_sol_cqe, + cid) / 32] & SOL_CID_MASK) >> 6)); break; } @@ -1208,40 +1422,55 @@ static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn, hwi_post_async_buffers(phba, pasync_handle->is_header); } -static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba) + +static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq) { - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; struct be_queue_info *cq; struct sol_cqe *sol; struct dmsg_cqe *dmsg; unsigned int num_processed = 0; unsigned int tot_nump = 0; struct beiscsi_conn *beiscsi_conn; + struct sgl_handle *psgl_handle = NULL; + struct beiscsi_hba *phba; - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - cq = &phwi_context->be_cq; + cq = pbe_eq->cq; sol = queue_tail_node(cq); + phba = pbe_eq->phba; while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] & CQE_VALID_MASK) { be_dws_le_to_cpu(sol, sizeof(struct sol_cqe)); - beiscsi_conn = phba->conn_table[(u32) (sol-> + if (ring_mode) { + psgl_handle = phba->sgl_hndl_array[((sol-> + dw[offsetof(struct amap_sol_cqe_ring, + icd_index) / 32] & SOL_ICD_INDEX_MASK) + >> 6)]; + beiscsi_conn = phba->conn_table[psgl_handle->cid]; + if (!beiscsi_conn || !beiscsi_conn->ep) { + shost_printk(KERN_WARNING, phba->shost, + "Connection table empty for cid = %d\n", + psgl_handle->cid); + return 0; + } + + } else { + beiscsi_conn = phba->conn_table[(u32) (sol-> dw[offsetof(struct amap_sol_cqe, cid) / 32] & SOL_CID_MASK) >> 6]; - if (!beiscsi_conn || !beiscsi_conn->ep) { - shost_printk(KERN_WARNING, phba->shost, + if (!beiscsi_conn || !beiscsi_conn->ep) { + shost_printk(KERN_WARNING, phba->shost, "Connection table empty for cid = %d\n", (u32)(sol->dw[offsetof(struct amap_sol_cqe, cid) / 32] & SOL_CID_MASK) >> 6); - return 0; + return 0; + } } if (num_processed >= 32) { - hwi_ring_cq_db(phba, phwi_context->be_cq.id, + hwi_ring_cq_db(phba, cq->id, num_processed, 0, 0); tot_nump += num_processed; num_processed = 0; @@ -1258,8 +1487,12 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba) hwi_complete_drvr_msgs(beiscsi_conn, phba, sol); break; case UNSOL_HDR_NOTIFY: + SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR_ NOTIFY\n"); + hwi_process_default_pdu_ring(beiscsi_conn, phba, + (struct i_t_dpdu_cqe *)sol); + break; case UNSOL_DATA_NOTIFY: - SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR/DATA_NOTIFY\n"); + SE_DEBUG(DBG_LVL_8, "Received UNSOL_DATA_NOTIFY\n"); hwi_process_default_pdu_ring(beiscsi_conn, phba, (struct i_t_dpdu_cqe *)sol); break; @@ -1278,13 +1511,21 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba) case CMD_CXN_KILLED_ITT_INVALID: case CMD_CXN_KILLED_SEQ_OUTOFORDER: case CMD_CXN_KILLED_INVALID_DATASN_RCVD: - SE_DEBUG(DBG_LVL_1, + if (ring_mode) { + SE_DEBUG(DBG_LVL_1, + "CQ Error notification for cmd.. " + "code %d cid 0x%x\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, psgl_handle->cid); + } else { + SE_DEBUG(DBG_LVL_1, "CQ Error notification for cmd.. " "code %d cid 0x%x\n", sol->dw[offsetof(struct amap_sol_cqe, code) / 32] & CQE_CODE_MASK, (sol->dw[offsetof(struct amap_sol_cqe, cid) / 32] & SOL_CID_MASK)); + } break; case UNSOL_DATA_DIGEST_ERROR_NOTIFY: SE_DEBUG(DBG_LVL_1, @@ -1306,23 +1547,37 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba) case CXN_KILLED_OVER_RUN_RESIDUAL: case CXN_KILLED_UNDER_RUN_RESIDUAL: case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN: - SE_DEBUG(DBG_LVL_1, "CQ Error %d, resetting CID " + if (ring_mode) { + SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID " + "0x%x...\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, psgl_handle->cid); + } else { + SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID " "0x%x...\n", sol->dw[offsetof(struct amap_sol_cqe, code) / 32] & CQE_CODE_MASK, sol->dw[offsetof(struct amap_sol_cqe, cid) / 32] & CQE_CID_MASK); + } iscsi_conn_failure(beiscsi_conn->conn, ISCSI_ERR_CONN_FAILED); break; case CXN_KILLED_RST_SENT: case CXN_KILLED_RST_RCVD: - SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset received/sent " - "on CID 0x%x...\n", + if (ring_mode) { + SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset" + "received/sent on CID 0x%x...\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, psgl_handle->cid); + } else { + SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset" + "received/sent on CID 0x%x...\n", sol->dw[offsetof(struct amap_sol_cqe, code) / 32] & CQE_CODE_MASK, sol->dw[offsetof(struct amap_sol_cqe, cid) / 32] & CQE_CID_MASK); + } iscsi_conn_failure(beiscsi_conn->conn, ISCSI_ERR_CONN_FAILED); break; @@ -1344,8 +1599,7 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba) if (num_processed > 0) { tot_nump += num_processed; - hwi_ring_cq_db(phba, phwi_context->be_cq.id, num_processed, - 1, 0); + hwi_ring_cq_db(phba, cq->id, num_processed, 1, 0); } return tot_nump; } @@ -1353,21 +1607,30 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba) static void beiscsi_process_all_cqs(struct work_struct *work) { unsigned long flags; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_eq_obj *pbe_eq; struct beiscsi_hba *phba = container_of(work, struct beiscsi_hba, work_cqs); + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + if (phba->msix_enabled) + pbe_eq = &phwi_context->be_eq[phba->num_cpus]; + else + pbe_eq = &phwi_context->be_eq[0]; + if (phba->todo_mcc_cq) { spin_lock_irqsave(&phba->isr_lock, flags); phba->todo_mcc_cq = 0; spin_unlock_irqrestore(&phba->isr_lock, flags); - SE_DEBUG(DBG_LVL_1, "MCC Interrupt Not expected \n"); } if (phba->todo_cq) { spin_lock_irqsave(&phba->isr_lock, flags); phba->todo_cq = 0; spin_unlock_irqrestore(&phba->isr_lock, flags); - beiscsi_process_cq(phba); + beiscsi_process_cq(pbe_eq); } } @@ -1375,19 +1638,15 @@ static int be_iopoll(struct blk_iopoll *iop, int budget) { static unsigned int ret; struct beiscsi_hba *phba; + struct be_eq_obj *pbe_eq; - phba = container_of(iop, struct beiscsi_hba, iopoll); - - ret = beiscsi_process_cq(phba); + pbe_eq = container_of(iop, struct be_eq_obj, iopoll); + ret = beiscsi_process_cq(pbe_eq); if (ret < budget) { - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; + phba = pbe_eq->phba; blk_iopoll_complete(iop); - hwi_ring_eq_db(phba, phwi_context->be_eq.q.id, 0, - 0, 1, 1); + SE_DEBUG(DBG_LVL_8, "rearm pbe_eq->q.id =%d\n", pbe_eq->q.id); + hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); } return ret; } @@ -1537,14 +1796,12 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task) static void beiscsi_find_mem_req(struct beiscsi_hba *phba) { - unsigned int num_cq_pages, num_eq_pages, num_async_pdu_buf_pages; + unsigned int num_cq_pages, num_async_pdu_buf_pages; unsigned int num_async_pdu_data_pages, wrb_sz_per_cxn; unsigned int num_async_pdu_buf_sgl_pages, num_async_pdu_data_sgl_pages; num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \ sizeof(struct sol_cqe)); - num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \ - sizeof(struct be_eq_entry)); num_async_pdu_buf_pages = PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ phba->params.defpdu_hdr_sz); @@ -1565,8 +1822,6 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba) phba->mem_req[HWI_MEM_ADDN_CONTEXT] = sizeof(struct hwi_context_memory); - phba->mem_req[HWI_MEM_CQ] = num_cq_pages * PAGE_SIZE; - phba->mem_req[HWI_MEM_EQ] = num_eq_pages * PAGE_SIZE; phba->mem_req[HWI_MEM_WRB] = sizeof(struct iscsi_wrb) * (phba->params.wrbs_per_cxn) @@ -1751,8 +2006,6 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba) for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) { pwrb_context = &phwi_ctrlr->wrb_context[index]; - SE_DEBUG(DBG_LVL_8, "cid=%d pwrb_context=%p \n", index, - pwrb_context); pwrb_context->pwrb_handle_base = kzalloc(sizeof(struct wrb_handle *) * phba->params.wrbs_per_cxn, GFP_KERNEL); @@ -1767,6 +2020,7 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba) pwrb_context->pwrb_handle_basestd[j] = pwrb_handle; pwrb_context->wrb_handles_available++; + pwrb_handle->wrb_index = j; pwrb_handle++; } pwrb_context->free_index = 0; @@ -1785,6 +2039,7 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba) pwrb_context->pwrb_handle_basestd[j] = pwrb_handle; pwrb_context->wrb_handles_available++; + pwrb_handle->wrb_index = j; pwrb_handle++; } pwrb_context->free_index = 0; @@ -2042,79 +2297,126 @@ static int be_fill_queue(struct be_queue_info *q, return 0; } -static int beiscsi_create_eq(struct beiscsi_hba *phba, +static int beiscsi_create_eqs(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context) { - unsigned int idx; - int ret; + unsigned int i, num_eq_pages; + int ret, eq_for_mcc; struct be_queue_info *eq; struct be_dma_mem *mem; - struct be_mem_descriptor *mem_descr; void *eq_vaddress; + dma_addr_t paddr; - idx = 0; - eq = &phwi_context->be_eq.q; - mem = &eq->dma_mem; - mem_descr = phba->init_mem; - mem_descr += HWI_MEM_EQ; - eq_vaddress = mem_descr->mem_array[idx].virtual_address; - - ret = be_fill_queue(eq, phba->params.num_eq_entries, - sizeof(struct be_eq_entry), eq_vaddress); - if (ret) { - shost_printk(KERN_ERR, phba->shost, - "be_fill_queue Failed for EQ \n"); - return ret; - } + num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \ + sizeof(struct be_eq_entry)); - mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + if (phba->msix_enabled) + eq_for_mcc = 1; + else + eq_for_mcc = 0; + for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) { + eq = &phwi_context->be_eq[i].q; + mem = &eq->dma_mem; + phwi_context->be_eq[i].phba = phba; + eq_vaddress = pci_alloc_consistent(phba->pcidev, + num_eq_pages * PAGE_SIZE, + &paddr); + if (!eq_vaddress) + goto create_eq_error; + + mem->va = eq_vaddress; + ret = be_fill_queue(eq, phba->params.num_eq_entries, + sizeof(struct be_eq_entry), eq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for EQ \n"); + goto create_eq_error; + } - ret = beiscsi_cmd_eq_create(&phba->ctrl, eq, - phwi_context->be_eq.cur_eqd); - if (ret) { - shost_printk(KERN_ERR, phba->shost, "beiscsi_cmd_eq_create" - "Failedfor EQ \n"); - return ret; + mem->dma = paddr; + ret = beiscsi_cmd_eq_create(&phba->ctrl, eq, + phwi_context->cur_eqd); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "beiscsi_cmd_eq_create" + "Failedfor EQ \n"); + goto create_eq_error; + } + SE_DEBUG(DBG_LVL_8, "eqid = %d\n", phwi_context->be_eq[i].q.id); } - SE_DEBUG(DBG_LVL_8, "eq id is %d\n", phwi_context->be_eq.q.id); return 0; +create_eq_error: + for (i = 0; i < (phba->num_cpus + 1); i++) { + eq = &phwi_context->be_eq[i].q; + mem = &eq->dma_mem; + if (mem->va) + pci_free_consistent(phba->pcidev, num_eq_pages + * PAGE_SIZE, + mem->va, mem->dma); + } + return ret; } -static int beiscsi_create_cq(struct beiscsi_hba *phba, +static int beiscsi_create_cqs(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context) { - unsigned int idx; + unsigned int i, num_cq_pages; int ret; struct be_queue_info *cq, *eq; struct be_dma_mem *mem; - struct be_mem_descriptor *mem_descr; + struct be_eq_obj *pbe_eq; void *cq_vaddress; + dma_addr_t paddr; - idx = 0; - cq = &phwi_context->be_cq; - eq = &phwi_context->be_eq.q; - mem = &cq->dma_mem; - mem_descr = phba->init_mem; - mem_descr += HWI_MEM_CQ; - cq_vaddress = mem_descr->mem_array[idx].virtual_address; - ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2, - sizeof(struct sol_cqe), cq_vaddress); - if (ret) { - shost_printk(KERN_ERR, phba->shost, - "be_fill_queue Failed for ISCSI CQ \n"); - return ret; - } + num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \ + sizeof(struct sol_cqe)); - mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; - ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false, false, 0); - if (ret) { - shost_printk(KERN_ERR, phba->shost, - "beiscsi_cmd_eq_create Failed for ISCSI CQ \n"); - return ret; + for (i = 0; i < phba->num_cpus; i++) { + cq = &phwi_context->be_cq[i]; + eq = &phwi_context->be_eq[i].q; + pbe_eq = &phwi_context->be_eq[i]; + pbe_eq->cq = cq; + pbe_eq->phba = phba; + mem = &cq->dma_mem; + cq_vaddress = pci_alloc_consistent(phba->pcidev, + num_cq_pages * PAGE_SIZE, + &paddr); + if (!cq_vaddress) + goto create_cq_error; + ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2, + sizeof(struct sol_cqe), cq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for ISCSI CQ \n"); + goto create_cq_error; + } + + mem->dma = paddr; + ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false, + false, 0); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "beiscsi_cmd_eq_create" + "Failed for ISCSI CQ \n"); + goto create_cq_error; + } + SE_DEBUG(DBG_LVL_8, "iscsi cq_id is %d for eq_id %d\n", + cq->id, eq->id); + SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n"); } - SE_DEBUG(DBG_LVL_8, "iscsi cq id is %d\n", phwi_context->be_cq.id); - SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n"); return 0; + +create_cq_error: + for (i = 0; i < phba->num_cpus; i++) { + cq = &phwi_context->be_cq[i]; + mem = &cq->dma_mem; + if (mem->va) + pci_free_consistent(phba->pcidev, num_cq_pages + * PAGE_SIZE, + mem->va, mem->dma); + } + return ret; + } static int @@ -2132,7 +2434,7 @@ beiscsi_create_def_hdr(struct beiscsi_hba *phba, idx = 0; dq = &phwi_context->be_def_hdrq; - cq = &phwi_context->be_cq; + cq = &phwi_context->be_cq[0]; mem = &dq->dma_mem; mem_descr = phba->init_mem; mem_descr += HWI_MEM_ASYNC_HEADER_RING; @@ -2176,7 +2478,7 @@ beiscsi_create_def_data(struct beiscsi_hba *phba, idx = 0; dataq = &phwi_context->be_def_dataq; - cq = &phwi_context->be_cq; + cq = &phwi_context->be_cq[0]; mem = &dataq->dma_mem; mem_descr = phba->init_mem; mem_descr += HWI_MEM_ASYNC_DATA_RING; @@ -2239,6 +2541,30 @@ beiscsi_post_pages(struct beiscsi_hba *phba) return 0; } +static void be_queue_free(struct beiscsi_hba *phba, struct be_queue_info *q) +{ + struct be_dma_mem *mem = &q->dma_mem; + if (mem->va) + pci_free_consistent(phba->pcidev, mem->size, + mem->va, mem->dma); +} + +static int be_queue_alloc(struct beiscsi_hba *phba, struct be_queue_info *q, + u16 len, u16 entry_size) +{ + struct be_dma_mem *mem = &q->dma_mem; + + memset(q, 0, sizeof(*q)); + q->len = len; + q->entry_size = entry_size; + mem->size = len * entry_size; + mem->va = pci_alloc_consistent(phba->pcidev, mem->size, &mem->dma); + if (!mem->va) + return -1; + memset(mem->va, 0, mem->size); + return 0; +} + static int beiscsi_create_wrb_rings(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context, @@ -2328,13 +2654,29 @@ static void free_wrb_handles(struct beiscsi_hba *phba) } } +static void be_mcc_queues_destroy(struct beiscsi_hba *phba) +{ + struct be_queue_info *q; + struct be_ctrl_info *ctrl = &phba->ctrl; + + q = &phba->ctrl.mcc_obj.q; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ); + be_queue_free(phba, q); + + q = &phba->ctrl.mcc_obj.cq; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); + be_queue_free(phba, q); +} + static void hwi_cleanup(struct beiscsi_hba *phba) { struct be_queue_info *q; struct be_ctrl_info *ctrl = &phba->ctrl; struct hwi_controller *phwi_ctrlr; struct hwi_context_memory *phwi_context; - int i; + int i, eq_num; phwi_ctrlr = phba->phwi_ctrlr; phwi_context = phwi_ctrlr->phwi_ctxt; @@ -2343,7 +2685,6 @@ static void hwi_cleanup(struct beiscsi_hba *phba) if (q->created) beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ); } - free_wrb_handles(phba); q = &phwi_context->be_def_hdrq; @@ -2356,13 +2697,76 @@ static void hwi_cleanup(struct beiscsi_hba *phba) beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); - q = &phwi_context->be_cq; - if (q->created) - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); + for (i = 0; i < (phba->num_cpus); i++) { + q = &phwi_context->be_cq[i]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); + } + if (phba->msix_enabled) + eq_num = 1; + else + eq_num = 0; + for (i = 0; i < (phba->num_cpus + eq_num); i++) { + q = &phwi_context->be_eq[i].q; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); + } + be_mcc_queues_destroy(phba); +} - q = &phwi_context->be_eq.q; - if (q->created) - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); +static int be_mcc_queues_create(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context) +{ + struct be_queue_info *q, *cq; + struct be_ctrl_info *ctrl = &phba->ctrl; + + /* Alloc MCC compl queue */ + cq = &phba->ctrl.mcc_obj.cq; + if (be_queue_alloc(phba, cq, MCC_CQ_LEN, + sizeof(struct be_mcc_compl))) + goto err; + /* Ask BE to create MCC compl queue; */ + if (phba->msix_enabled) { + if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq + [phba->num_cpus].q, false, true, 0)) + goto mcc_cq_free; + } else { + if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq[0].q, + false, true, 0)) + goto mcc_cq_free; + } + + /* Alloc MCC queue */ + q = &phba->ctrl.mcc_obj.q; + if (be_queue_alloc(phba, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb))) + goto mcc_cq_destroy; + + /* Ask BE to create MCC queue */ + if (beiscsi_cmd_mccq_create(phba, q, cq)) + goto mcc_q_free; + + return 0; + +mcc_q_free: + be_queue_free(phba, q); +mcc_cq_destroy: + beiscsi_cmd_q_destroy(ctrl, cq, QTYPE_CQ); +mcc_cq_free: + be_queue_free(phba, cq); +err: + return -1; +} + +static int find_num_cpus(void) +{ + int num_cpus = 0; + + num_cpus = num_online_cpus(); + if (num_cpus >= MAX_CPUS) + num_cpus = MAX_CPUS - 1; + + SE_DEBUG(DBG_LVL_8, "num_cpus = %d \n", num_cpus); + return num_cpus; } static int hwi_init_port(struct beiscsi_hba *phba) @@ -2376,26 +2780,33 @@ static int hwi_init_port(struct beiscsi_hba *phba) def_pdu_ring_sz = phba->params.asyncpdus_per_ctrl * sizeof(struct phys_addr); phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - phwi_context->be_eq.max_eqd = 0; - phwi_context->be_eq.min_eqd = 0; - phwi_context->be_eq.cur_eqd = 64; - phwi_context->be_eq.enable_aic = false; + phwi_context->max_eqd = 0; + phwi_context->min_eqd = 0; + phwi_context->cur_eqd = 64; be_cmd_fw_initialize(&phba->ctrl); - status = beiscsi_create_eq(phba, phwi_context); + + status = beiscsi_create_eqs(phba, phwi_context); if (status != 0) { shost_printk(KERN_ERR, phba->shost, "EQ not created \n"); goto error; } - status = mgmt_check_supported_fw(ctrl); + status = be_mcc_queues_create(phba, phwi_context); + if (status != 0) + goto error; + + status = mgmt_check_supported_fw(ctrl, phba); if (status != 0) { shost_printk(KERN_ERR, phba->shost, "Unsupported fw version \n"); goto error; } + if (phba->fw_config.iscsi_features == 0x1) + ring_mode = 1; + else + ring_mode = 0; status = mgmt_get_fw_config(ctrl, phba); if (status != 0) { shost_printk(KERN_ERR, phba->shost, @@ -2403,7 +2814,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) goto error; } - status = beiscsi_create_cq(phba, phwi_context); + status = beiscsi_create_cqs(phba, phwi_context); if (status != 0) { shost_printk(KERN_ERR, phba->shost, "CQ not created\n"); goto error; @@ -2447,7 +2858,6 @@ error: return -ENOMEM; } - static int hwi_init_controller(struct beiscsi_hba *phba) { struct hwi_controller *phwi_ctrlr; @@ -2530,6 +2940,18 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba) phba->io_sgl_hndl_avbl = 0; phba->eh_sgl_hndl_avbl = 0; + + if (ring_mode) { + phba->sgl_hndl_array = kzalloc(sizeof(struct sgl_handle *) * + phba->params.icds_per_ctrl, + GFP_KERNEL); + if (!phba->sgl_hndl_array) { + shost_printk(KERN_ERR, phba->shost, + "Mem Alloc Failed. Failing to load\n"); + return -ENOMEM; + } + } + mem_descr_sglh = phba->init_mem; mem_descr_sglh += HWI_MEM_SGLH; if (1 == mem_descr_sglh->num_elements) { @@ -2537,6 +2959,8 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba) phba->params.ios_per_ctrl, GFP_KERNEL); if (!phba->io_sgl_hndl_base) { + if (ring_mode) + kfree(phba->sgl_hndl_array); shost_printk(KERN_ERR, phba->shost, "Mem Alloc Failed. Failing to load\n"); return -ENOMEM; @@ -2656,13 +3080,12 @@ static unsigned char hwi_enable_intr(struct beiscsi_hba *phba) struct hwi_context_memory *phwi_context; struct be_queue_info *eq; u8 __iomem *addr; - u32 reg; + u32 reg, i; u32 enabled; phwi_ctrlr = phba->phwi_ctrlr; phwi_context = phwi_ctrlr->phwi_ctxt; - eq = &phwi_context->be_eq.q; addr = (u8 __iomem *) ((u8 __iomem *) ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET); reg = ioread32(addr); @@ -2673,9 +3096,11 @@ static unsigned char hwi_enable_intr(struct beiscsi_hba *phba) reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p \n", reg, addr); iowrite32(reg, addr); - SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id); - - hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1); + for (i = 0; i <= phba->num_cpus; i++) { + eq = &phwi_context->be_eq[i].q; + SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id); + hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1); + } } else shost_printk(KERN_WARNING, phba->shost, "In hwi_enable_intr, Not Enabled \n"); @@ -2720,6 +3145,8 @@ static int beiscsi_init_port(struct beiscsi_hba *phba) if (hba_setup_cid_tbls(phba)) { shost_printk(KERN_ERR, phba->shost, "Failed in hba_setup_cid_tbls\n"); + if (ring_mode) + kfree(phba->sgl_hndl_array); kfree(phba->io_sgl_hndl_base); kfree(phba->eh_sgl_hndl_base); goto do_cleanup_ctrlr; @@ -2738,17 +3165,25 @@ static void hwi_purge_eq(struct beiscsi_hba *phba) struct hwi_context_memory *phwi_context; struct be_queue_info *eq; struct be_eq_entry *eqe = NULL; + int i, eq_msix; phwi_ctrlr = phba->phwi_ctrlr; phwi_context = phwi_ctrlr->phwi_ctxt; - eq = &phwi_context->be_eq.q; - eqe = queue_tail_node(eq); + if (phba->msix_enabled) + eq_msix = 1; + else + eq_msix = 0; - while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] - & EQE_VALID_MASK) { - AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); - queue_tail_inc(eq); + for (i = 0; i < (phba->num_cpus + eq_msix); i++) { + eq = &phwi_context->be_eq[i].q; eqe = queue_tail_node(eq); + + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + } } } @@ -2762,6 +3197,8 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba) "mgmt_epfw_cleanup FAILED \n"); hwi_cleanup(phba); hwi_purge_eq(phba); + if (ring_mode) + kfree(phba->sgl_hndl_array); kfree(phba->io_sgl_hndl_base); kfree(phba->eh_sgl_hndl_base); kfree(phba->cid_array); @@ -2846,8 +3283,9 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_target_context_update_wrb)); doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; - doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK) << - DB_DEF_PDU_WRB_INDEX_SHIFT; + if (!ring_mode) + doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK) + << DB_DEF_PDU_WRB_INDEX_SHIFT; doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET); @@ -2856,7 +3294,7 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, static void beiscsi_parse_pdu(struct iscsi_conn *conn, itt_t itt, int *index, int *age) { - *index = be32_to_cpu(itt) >> 16; + *index = (int)itt; if (age) *age = conn->session->age; } @@ -2885,15 +3323,13 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) io_task->cmd_bhs = pci_pool_alloc(beiscsi_sess->bhs_pool, GFP_KERNEL, &paddr); - if (!io_task->cmd_bhs) return -ENOMEM; - io_task->bhs_pa.u.a64.address = paddr; + io_task->libiscsi_itt = (itt_t)task->itt; io_task->pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid, task->itt); - io_task->pwrb_handle->pio_handle = task; io_task->conn = beiscsi_conn; task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr; @@ -2905,7 +3341,6 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) spin_unlock(&phba->io_sgl_lock); if (!io_task->psgl_handle) goto free_hndls; - } else { io_task->scsi_cmnd = NULL; if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) { @@ -2932,8 +3367,18 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) goto free_hndls; } } - itt = (itt_t) cpu_to_be32(((unsigned int)task->itt << 16) | - (unsigned int)(io_task->psgl_handle->sgl_index)); + itt = (itt_t) cpu_to_be32(((unsigned int)io_task->pwrb_handle-> + wrb_index << 16) | (unsigned int) + (io_task->psgl_handle->sgl_index)); + if (ring_mode) { + phba->sgl_hndl_array[io_task->psgl_handle->sgl_index - + phba->fw_config.iscsi_cid_start] = + io_task->psgl_handle; + io_task->psgl_handle->task = task; + io_task->psgl_handle->cid = beiscsi_conn->beiscsi_conn_cid; + } else + io_task->pwrb_handle->pio_handle = task; + io_task->cmd_bhs->iscsi_hdr.itt = itt; return 0; @@ -3006,7 +3451,6 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg, io_task->bhs_len = sizeof(struct be_cmd_bhs); if (writedir) { - SE_DEBUG(DBG_LVL_4, " WRITE Command \t"); memset(&io_task->cmd_bhs->iscsi_data_pdu, 0, 48); AMAP_SET_BITS(struct amap_pdu_data_out, itt, &io_task->cmd_bhs->iscsi_data_pdu, @@ -3016,11 +3460,18 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg, ISCSI_OPCODE_SCSI_DATA_OUT); AMAP_SET_BITS(struct amap_pdu_data_out, final_bit, &io_task->cmd_bhs->iscsi_data_pdu, 1); - AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD); + if (ring_mode) + io_task->psgl_handle->type = INI_WR_CMD; + else + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, + INI_WR_CMD); AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); } else { - SE_DEBUG(DBG_LVL_4, "READ Command \t"); - AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD); + if (ring_mode) + io_task->psgl_handle->type = INI_RD_CMD; + else + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, + INI_RD_CMD); AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0); } memcpy(&io_task->cmd_bhs->iscsi_data_pdu. @@ -3045,7 +3496,8 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg, be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb)); doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; - doorbell |= (io_task->pwrb_handle->wrb_index & + if (!ring_mode) + doorbell |= (io_task->pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT; doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; @@ -3059,10 +3511,16 @@ static int beiscsi_mtask(struct iscsi_task *task) struct iscsi_conn *conn = task->conn; struct beiscsi_conn *beiscsi_conn = conn->dd_data; struct beiscsi_hba *phba = beiscsi_conn->phba; + struct iscsi_session *session; struct iscsi_wrb *pwrb = NULL; + struct hwi_controller *phwi_ctrlr; + struct hwi_wrb_context *pwrb_context; + struct wrb_handle *pwrb_handle; unsigned int doorbell = 0; + unsigned int i, cid; struct iscsi_task *aborted_task; + cid = beiscsi_conn->beiscsi_conn_cid; pwrb = io_task->pwrb_handle->pwrb; AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, be32_to_cpu(task->cmdsn)); @@ -3073,38 +3531,63 @@ static int beiscsi_mtask(struct iscsi_task *task) switch (task->hdr->opcode & ISCSI_OPCODE_MASK) { case ISCSI_OP_LOGIN: - AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, TGT_DM_CMD); + if (ring_mode) + io_task->psgl_handle->type = TGT_DM_CMD; + else + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, + TGT_DM_CMD); AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, 1); hwi_write_buffer(pwrb, task); break; case ISCSI_OP_NOOP_OUT: - AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD); + if (ring_mode) + io_task->psgl_handle->type = INI_RD_CMD; + else + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, + INI_RD_CMD); hwi_write_buffer(pwrb, task); break; case ISCSI_OP_TEXT: - AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD); + if (ring_mode) + io_task->psgl_handle->type = INI_WR_CMD; + else + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, + INI_WR_CMD); AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); hwi_write_buffer(pwrb, task); break; case ISCSI_OP_SCSI_TMFUNC: - aborted_task = iscsi_itt_to_task(conn, - ((struct iscsi_tm *)task->hdr)->rtt); + session = conn->session; + i = ((struct iscsi_tm *)task->hdr)->rtt; + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[cid]; + pwrb_handle = pwrb_context->pwrb_handle_basestd[be32_to_cpu(i) + >> 16]; + aborted_task = pwrb_handle->pio_handle; if (!aborted_task) return 0; + aborted_io_task = aborted_task->dd_data; if (!aborted_io_task->scsi_cmnd) return 0; mgmt_invalidate_icds(phba, aborted_io_task->psgl_handle->sgl_index, - beiscsi_conn->beiscsi_conn_cid); - AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_TMF_CMD); + cid); + if (ring_mode) + io_task->psgl_handle->type = INI_TMF_CMD; + else + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, + INI_TMF_CMD); AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); hwi_write_buffer(pwrb, task); break; case ISCSI_OP_LOGOUT: AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); + if (ring_mode) + io_task->psgl_handle->type = HWH_TYPE_LOGOUT; + else AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, HWH_TYPE_LOGOUT); hwi_write_buffer(pwrb, task); @@ -3122,8 +3605,9 @@ static int beiscsi_mtask(struct iscsi_task *task) io_task->pwrb_handle->nxt_wrb_index); be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb)); - doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; - doorbell |= (io_task->pwrb_handle->wrb_index & + doorbell |= cid & DB_WRB_POST_CID_MASK; + if (!ring_mode) + doorbell |= (io_task->pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT; doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET); @@ -3165,9 +3649,14 @@ static int beiscsi_task_xmit(struct iscsi_task *task) return beiscsi_iotask(task, sg, num_sg, xferlen, writedir); } + static void beiscsi_remove(struct pci_dev *pcidev) { struct beiscsi_hba *phba = NULL; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_eq_obj *pbe_eq; + unsigned int i, msix_vec; phba = (struct beiscsi_hba *)pci_get_drvdata(pcidev); if (!phba) { @@ -3175,12 +3664,24 @@ static void beiscsi_remove(struct pci_dev *pcidev) return; } + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; hwi_disable_intr(phba); - if (phba->pcidev->irq) - free_irq(phba->pcidev->irq, phba); + if (phba->msix_enabled) { + for (i = 0; i <= phba->num_cpus; i++) { + msix_vec = phba->msix_entries[i].vector; + free_irq(msix_vec, &phwi_context->be_eq[i]); + } + } else + if (phba->pcidev->irq) + free_irq(phba->pcidev->irq, phba); + pci_disable_msix(phba->pcidev); destroy_workqueue(phba->wq); if (blk_iopoll_enabled) - blk_iopoll_disable(&phba->iopoll); + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + blk_iopoll_disable(&pbe_eq->iopoll); + } beiscsi_clean_port(phba); beiscsi_free_mem(phba); @@ -3194,11 +3695,29 @@ static void beiscsi_remove(struct pci_dev *pcidev) iscsi_host_free(phba->shost); } +static void beiscsi_msix_enable(struct beiscsi_hba *phba) +{ + int i, status; + + for (i = 0; i <= phba->num_cpus; i++) + phba->msix_entries[i].entry = i; + + status = pci_enable_msix(phba->pcidev, phba->msix_entries, + (phba->num_cpus + 1)); + if (!status) + phba->msix_enabled = true; + + return; +} + static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { struct beiscsi_hba *phba = NULL; - int ret; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_eq_obj *pbe_eq; + int ret, msix_vec, num_cpus, i; ret = beiscsi_enable_pci(pcidev); if (ret < 0) { @@ -3213,8 +3732,18 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev, " Failed in beiscsi_hba_alloc \n"); goto disable_pci; } + SE_DEBUG(DBG_LVL_8, " phba = %p \n", phba); pci_set_drvdata(pcidev, phba); + if (enable_msix) + num_cpus = find_num_cpus(); + else + num_cpus = 1; + phba->num_cpus = num_cpus; + SE_DEBUG(DBG_LVL_8, "num_cpus = %d \n", phba->num_cpus); + + if (enable_msix) + beiscsi_msix_enable(phba); ret = be_ctrl_init(phba, pcidev); if (ret) { shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" @@ -3235,7 +3764,7 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev, snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u", phba->shost->host_no); - phba->wq = create_singlethread_workqueue(phba->wq_name); + phba->wq = create_workqueue(phba->wq_name); if (!phba->wq) { shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" "Failed to allocate work queue\n"); @@ -3244,11 +3773,16 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev, INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs); + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; if (blk_iopoll_enabled) { - blk_iopoll_init(&phba->iopoll, be_iopoll_budget, be_iopoll); - blk_iopoll_enable(&phba->iopoll); + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + blk_iopoll_init(&pbe_eq->iopoll, be_iopoll_budget, + be_iopoll); + blk_iopoll_enable(&pbe_eq->iopoll); + } } - ret = beiscsi_init_irqs(phba); if (ret < 0) { shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" @@ -3261,17 +3795,26 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev, "Failed to hwi_enable_intr\n"); goto free_ctrlr; } - SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED \n\n\n"); return 0; free_ctrlr: - if (phba->pcidev->irq) - free_irq(phba->pcidev->irq, phba); + if (phba->msix_enabled) { + for (i = 0; i <= phba->num_cpus; i++) { + msix_vec = phba->msix_entries[i].vector; + free_irq(msix_vec, &phwi_context->be_eq[i]); + } + } else + if (phba->pcidev->irq) + free_irq(phba->pcidev->irq, phba); + pci_disable_msix(phba->pcidev); free_blkenbld: destroy_workqueue(phba->wq); if (blk_iopoll_enabled) - blk_iopoll_disable(&phba->iopoll); + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + blk_iopoll_disable(&pbe_eq->iopoll); + } free_twq: beiscsi_clean_port(phba); beiscsi_free_mem(phba); @@ -3316,7 +3859,7 @@ struct iscsi_transport beiscsi_iscsi_transport = { ISCSI_USERNAME | ISCSI_PASSWORD | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | - ISCSI_LU_RESET_TMO | + ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO | ISCSI_PING_TMO | ISCSI_RECV_TMO | ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | @@ -3351,6 +3894,7 @@ static struct pci_driver beiscsi_pci_driver = { .id_table = beiscsi_pci_id_table }; + static int __init beiscsi_module_init(void) { int ret; @@ -3373,6 +3917,7 @@ static int __init beiscsi_module_init(void) "beiscsi pci driver.\n"); goto unregister_iscsi_transport; } + ring_mode = 0; return 0; unregister_iscsi_transport: diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 53c9b70ac7ac..25e6b208b771 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -21,11 +21,9 @@ #ifndef _BEISCSI_MAIN_ #define _BEISCSI_MAIN_ - #include <linux/kernel.h> #include <linux/pci.h> #include <linux/in.h> -#include <linux/blk-iopoll.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -35,12 +33,8 @@ #include <scsi/scsi_transport_iscsi.h> #include "be.h" - - - #define DRV_NAME "be2iscsi" #define BUILD_STR "2.0.527.0" - #define BE_NAME "ServerEngines BladeEngine2" \ "Linux iSCSI Driver version" BUILD_STR #define DRV_DESC BE_NAME " " "Driver" @@ -49,6 +43,8 @@ #define BE_DEVICE_ID1 0x212 #define OC_DEVICE_ID1 0x702 #define OC_DEVICE_ID2 0x703 +#define OC_DEVICE_ID3 0x712 +#define OC_DEVICE_ID4 0x222 #define BE2_MAX_SESSIONS 64 #define BE2_CMDS_PER_CXN 128 @@ -63,6 +59,7 @@ #define BE2_IO_DEPTH \ (BE2_MAX_ICDS / 2 - (BE2_LOGOUTS + BE2_TMFS + BE2_NOPOUT_REQ)) +#define MAX_CPUS 31 #define BEISCSI_SGLIST_ELEMENTS BE2_SGE #define BEISCSI_MAX_CMNDS 1024 /* Max IO's per Ctrlr sht->can_queue */ @@ -79,7 +76,7 @@ #define BE_SENSE_INFO_SIZE 258 #define BE_ISCSI_PDU_HEADER_SIZE 64 #define BE_MIN_MEM_SIZE 16384 - +#define MAX_CMD_SZ 65536 #define IIOC_SCSI_DATA 0x05 /* Write Operation */ #define DBG_LVL 0x00000001 @@ -100,6 +97,8 @@ do { \ } \ } while (0); +#define BE_ADAPTER_UP 0x00000000 +#define BE_ADAPTER_LINK_DOWN 0x00000001 /** * hardware needs the async PDU buffers to be posted in multiples of 8 * So have atleast 8 of them by default @@ -160,21 +159,19 @@ do { \ enum be_mem_enum { HWI_MEM_ADDN_CONTEXT, - HWI_MEM_CQ, - HWI_MEM_EQ, HWI_MEM_WRB, HWI_MEM_WRBH, - HWI_MEM_SGLH, /* 5 */ + HWI_MEM_SGLH, HWI_MEM_SGE, - HWI_MEM_ASYNC_HEADER_BUF, + HWI_MEM_ASYNC_HEADER_BUF, /* 5 */ HWI_MEM_ASYNC_DATA_BUF, HWI_MEM_ASYNC_HEADER_RING, - HWI_MEM_ASYNC_DATA_RING, /* 10 */ + HWI_MEM_ASYNC_DATA_RING, HWI_MEM_ASYNC_HEADER_HANDLE, - HWI_MEM_ASYNC_DATA_HANDLE, + HWI_MEM_ASYNC_DATA_HANDLE, /* 10 */ HWI_MEM_ASYNC_PDU_CONTEXT, ISCSI_MEM_GLOBAL_HEADER, - SE_MEM_MAX /* 15 */ + SE_MEM_MAX }; struct be_bus_address32 { @@ -212,6 +209,9 @@ struct be_mem_descriptor { struct sgl_handle { unsigned int sgl_index; + unsigned int type; + unsigned int cid; + struct iscsi_task *task; struct iscsi_sge *pfrag; }; @@ -274,13 +274,17 @@ struct beiscsi_hba { struct pci_dev *pcidev; unsigned int state; unsigned short asic_revision; - struct blk_iopoll iopoll; + unsigned int num_cpus; + unsigned int nxt_cqid; + struct msix_entry msix_entries[MAX_CPUS + 1]; + bool msix_enabled; struct be_mem_descriptor *init_mem; unsigned short io_sgl_alloc_index; unsigned short io_sgl_free_index; unsigned short io_sgl_hndl_avbl; struct sgl_handle **io_sgl_hndl_base; + struct sgl_handle **sgl_hndl_array; unsigned short eh_sgl_alloc_index; unsigned short eh_sgl_free_index; @@ -315,6 +319,7 @@ struct beiscsi_hba { unsigned short cid_alloc; unsigned short cid_free; unsigned short avlbl_cids; + unsigned short iscsi_features; spinlock_t cid_lock; } fw_config; @@ -343,6 +348,7 @@ struct beiscsi_conn { unsigned short login_in_progress; struct sgl_handle *plogin_sgl_handle; struct beiscsi_session *beiscsi_sess; + struct iscsi_task *task; }; /* This structure is used by the chip */ @@ -390,7 +396,7 @@ struct beiscsi_io_task { unsigned int flags; unsigned short cid; unsigned short header_len; - + itt_t libiscsi_itt; struct be_cmd_bhs *cmd_bhs; struct be_bus_address bhs_pa; unsigned short bhs_len; @@ -599,7 +605,6 @@ struct amap_cq_db { void beiscsi_process_eq(struct beiscsi_hba *phba); - struct iscsi_wrb { u32 dw[16]; } __packed; @@ -820,10 +825,12 @@ struct wrb_handle { }; struct hwi_context_memory { - struct be_eq_obj be_eq; - struct be_queue_info be_cq; - struct be_queue_info be_mcc_cq; - struct be_queue_info be_mcc; + /* Adaptive interrupt coalescing (AIC) info */ + u16 min_eqd; /* in usecs */ + u16 max_eqd; /* in usecs */ + u16 cur_eqd; /* in usecs */ + struct be_eq_obj be_eq[MAX_CPUS]; + struct be_queue_info be_cq[MAX_CPUS]; struct be_queue_info be_def_hdrq; struct be_queue_info be_def_dataq; diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 12e644fc746e..79c2bd525a84 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -35,7 +35,6 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl, be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req)); - status = be_mbox_notify(ctrl); if (!status) { struct be_fw_cfg *pfw_cfg; @@ -58,7 +57,8 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl, return status; } -unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl) +unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba) { struct be_dma_mem nonemb_cmd; struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); @@ -85,7 +85,6 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl) sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); sge->len = cpu_to_le32(nonemb_cmd.size); - status = be_mbox_notify(ctrl); if (!status) { struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va; @@ -95,21 +94,25 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl) resp->params.hba_attribs.firmware_version_string); SE_DEBUG(DBG_LVL_8, "Developer Build, not performing version check...\n"); - + phba->fw_config.iscsi_features = + resp->params.hba_attribs.iscsi_features; + SE_DEBUG(DBG_LVL_8, " phba->fw_config.iscsi_features = %d\n", + phba->fw_config.iscsi_features); } else SE_DEBUG(DBG_LVL_1, " Failed in mgmt_check_supported_fw\n"); + spin_unlock(&ctrl->mbox_lock); if (nonemb_cmd.va) pci_free_consistent(ctrl->pdev, nonemb_cmd.size, nonemb_cmd.va, nonemb_cmd.dma); - spin_unlock(&ctrl->mbox_lock); return status; } + unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute) { struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mcc_wrb *wrb = wrb_from_mccq(phba); struct iscsi_cleanup_req *req = embedded_payload(wrb); int status = 0; @@ -124,7 +127,7 @@ unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute) req->hdr_ring_id = 0; req->data_ring_id = 0; - status = be_mbox_notify(ctrl); + status = be_mcc_notify_wait(phba); if (status) shost_printk(KERN_WARNING, phba->shost, " mgmt_epfw_cleanup , FAILED\n"); @@ -137,7 +140,7 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba, { struct be_dma_mem nonemb_cmd; struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mcc_wrb *wrb = wrb_from_mccq(phba); struct be_sge *sge = nonembedded_sgl(wrb); struct invalidate_commands_params_in *req; int status = 0; @@ -169,7 +172,7 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba, sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); sge->len = cpu_to_le32(nonemb_cmd.size); - status = be_mbox_notify(ctrl); + status = be_mcc_notify_wait(phba); if (status) SE_DEBUG(DBG_LVL_1, "ICDS Invalidation Failed\n"); spin_unlock(&ctrl->mbox_lock); @@ -186,7 +189,7 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba, unsigned short savecfg_flag) { struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mcc_wrb *wrb = wrb_from_mccq(phba); struct iscsi_invalidate_connection_params_in *req = embedded_payload(wrb); int status = 0; @@ -205,7 +208,7 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba, else req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE; req->save_cfg = savecfg_flag; - status = be_mbox_notify(ctrl); + status = be_mcc_notify_wait(phba); if (status) SE_DEBUG(DBG_LVL_1, "Invalidation Failed\n"); @@ -217,7 +220,7 @@ unsigned char mgmt_upload_connection(struct beiscsi_hba *phba, unsigned short cid, unsigned int upload_flag) { struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mcc_wrb *wrb = wrb_from_mccq(phba); struct tcp_upload_params_in *req = embedded_payload(wrb); int status = 0; @@ -229,7 +232,7 @@ unsigned char mgmt_upload_connection(struct beiscsi_hba *phba, OPCODE_COMMON_TCP_UPLOAD, sizeof(*req)); req->id = (unsigned short)cid; req->upload_type = (unsigned char)upload_flag; - status = be_mbox_notify(ctrl); + status = be_mcc_notify_wait(phba); if (status) SE_DEBUG(DBG_LVL_1, "mgmt_upload_connection Failed\n"); spin_unlock(&ctrl->mbox_lock); @@ -245,13 +248,14 @@ int mgmt_open_connection(struct beiscsi_hba *phba, struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr; struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr; struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mcc_wrb *wrb = wrb_from_mccq(phba); struct tcp_connect_and_offload_in *req = embedded_payload(wrb); unsigned short def_hdr_id; unsigned short def_data_id; struct phys_addr template_address = { 0, 0 }; struct phys_addr *ptemplate_address; int status = 0; + unsigned int i; unsigned short cid = beiscsi_ep->ep_cid; phwi_ctrlr = phba->phwi_ctrlr; @@ -296,14 +300,18 @@ int mgmt_open_connection(struct beiscsi_hba *phba, } req->cid = cid; - req->cq_id = phwi_context->be_cq.id; + i = phba->nxt_cqid++; + if (phba->nxt_cqid == phba->num_cpus) + phba->nxt_cqid = 0; + req->cq_id = phwi_context->be_cq[i].id; + SE_DEBUG(DBG_LVL_8, "i=%d cq_id=%d \n", i, req->cq_id); req->defq_id = def_hdr_id; req->hdr_ring_id = def_hdr_id; req->data_ring_id = def_data_id; req->do_offload = 1; req->dataout_template_pa.lo = ptemplate_address->lo; req->dataout_template_pa.hi = ptemplate_address->hi; - status = be_mbox_notify(ctrl); + status = be_mcc_notify_wait(phba); if (!status) { struct iscsi_endpoint *ep; struct tcp_connect_and_offload_out *ptcpcnct_out = @@ -311,7 +319,7 @@ int mgmt_open_connection(struct beiscsi_hba *phba, ep = phba->ep_array[ptcpcnct_out->cid]; beiscsi_ep = ep->dd_data; - beiscsi_ep->fw_handle = 0; + beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle; beiscsi_ep->cid_vld = 1; SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n"); } else @@ -319,3 +327,30 @@ int mgmt_open_connection(struct beiscsi_hba *phba, spin_unlock(&ctrl->mbox_lock); return status; } + +int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mccq(phba); + struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb); + int status; + + SE_DEBUG(DBG_LVL_8, "In be_cmd_get_mac_addr\n"); + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG, + sizeof(*req)); + + status = be_mcc_notify_wait(phba); + if (!status) { + struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb); + + memcpy(mac_addr, resp->mac_address, ETH_ALEN); + } + + spin_unlock(&ctrl->mbox_lock); + return status; +} + diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index 00e816ee8070..24eaff923f85 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -175,7 +175,9 @@ struct mgmt_hba_attributes { u8 phy_port; u32 firmware_post_status; u32 hba_mtu[8]; - u32 future_u32[4]; + u8 iscsi_features; + u8 future_u8[3]; + u32 future_u32[3]; } __packed; struct mgmt_controller_attributes { @@ -246,4 +248,8 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba, unsigned short cid, unsigned short issue_reset, unsigned short savecfg_flag); + +unsigned char mgmt_fw_cmd(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba, + char *buf, unsigned int len); #endif diff --git a/drivers/scsi/bfa/bfa_cb_ioim_macros.h b/drivers/scsi/bfa/bfa_cb_ioim_macros.h index 0050c838c358..961fe439daad 100644 --- a/drivers/scsi/bfa/bfa_cb_ioim_macros.h +++ b/drivers/scsi/bfa/bfa_cb_ioim_macros.h @@ -51,7 +51,7 @@ bfad_int_to_lun(u32 luno) lun.bfa_lun = 0; lun.scsi_lun[0] = bfa_os_htons(luno); - return (lun.bfa_lun); + return lun.bfa_lun; } /** @@ -68,7 +68,7 @@ bfa_cb_ioim_get_cdb(struct bfad_ioim_s *dio) { struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; - return ((u8 *) cmnd->cmnd); + return (u8 *) cmnd->cmnd; } /** @@ -97,7 +97,7 @@ bfa_cb_ioim_get_size(struct bfad_ioim_s *dio) { struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; - return (scsi_bufflen(cmnd)); + return scsi_bufflen(cmnd); } /** @@ -129,7 +129,7 @@ bfa_cb_ioim_get_sgaddr(struct bfad_ioim_s *dio, int sgeid) sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid; addr = (u64) sg_dma_address(sge); - return (*(union bfi_addr_u *) &addr); + return *((union bfi_addr_u *) &addr); } static inline u32 @@ -197,7 +197,7 @@ bfa_cb_ioim_get_cdblen(struct bfad_ioim_s *dio) { struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; - return (cmnd->cmd_len); + return cmnd->cmd_len; } diff --git a/drivers/scsi/bfa/bfa_cee.c b/drivers/scsi/bfa/bfa_cee.c index 7a959c34e789..2b917792c6bc 100644 --- a/drivers/scsi/bfa/bfa_cee.c +++ b/drivers/scsi/bfa/bfa_cee.c @@ -228,7 +228,7 @@ bfa_cee_reset_stats_isr(struct bfa_cee_s *cee, bfa_status_t status) u32 bfa_cee_meminfo(void) { - return (bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo()); + return bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo(); } /** diff --git a/drivers/scsi/bfa/bfa_csdebug.c b/drivers/scsi/bfa/bfa_csdebug.c index 1b71d349451a..caeb1143a4e6 100644 --- a/drivers/scsi/bfa/bfa_csdebug.c +++ b/drivers/scsi/bfa/bfa_csdebug.c @@ -47,12 +47,12 @@ bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe) tqe = bfa_q_next(q); while (tqe != q) { if (tqe == qe) - return (1); + return 1; tqe = bfa_q_next(tqe); if (tqe == NULL) break; } - return (0); + return 0; } diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c index 401babe3494e..790c945aeae6 100644 --- a/drivers/scsi/bfa/bfa_fcpim.c +++ b/drivers/scsi/bfa/bfa_fcpim.c @@ -131,7 +131,7 @@ bfa_fcpim_path_tov_get(struct bfa_s *bfa) { struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); - return (fcpim->path_tov / 1000); + return fcpim->path_tov / 1000; } bfa_status_t @@ -169,7 +169,7 @@ bfa_fcpim_qdepth_get(struct bfa_s *bfa) { struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); - return (fcpim->q_depth); + return fcpim->q_depth; } diff --git a/drivers/scsi/bfa/bfa_fcpim_priv.h b/drivers/scsi/bfa/bfa_fcpim_priv.h index 153206cfb37a..5cf418460f75 100644 --- a/drivers/scsi/bfa/bfa_fcpim_priv.h +++ b/drivers/scsi/bfa/bfa_fcpim_priv.h @@ -35,7 +35,7 @@ #define BFA_FCPIM_PATHTOV_MAX (90 * 1000) /* in millisecs */ #define bfa_fcpim_stats(__fcpim, __stats) \ - (__fcpim)->stats.__stats ++ + ((__fcpim)->stats.__stats++) struct bfa_fcpim_mod_s { struct bfa_s *bfa; @@ -143,7 +143,7 @@ struct bfa_itnim_s { struct bfa_itnim_hal_stats_s stats; }; -#define bfa_itnim_is_online(_itnim) (_itnim)->is_online +#define bfa_itnim_is_online(_itnim) ((_itnim)->is_online) #define BFA_FCPIM_MOD(_hal) (&(_hal)->modules.fcpim_mod) #define BFA_IOIM_FROM_TAG(_fcpim, _iotag) \ (&fcpim->ioim_arr[_iotag]) diff --git a/drivers/scsi/bfa/bfa_fcport.c b/drivers/scsi/bfa/bfa_fcport.c index 992435987deb..aef648b55dfc 100644 --- a/drivers/scsi/bfa/bfa_fcport.c +++ b/drivers/scsi/bfa/bfa_fcport.c @@ -388,32 +388,29 @@ bfa_pport_sm_linkup(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) bfa_pport_callback(pport, BFA_PPORT_LINKDOWN); bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkdown"); - if (BFA_PORT_IS_DISABLED(pport->bfa)) { + if (BFA_PORT_IS_DISABLED(pport->bfa)) bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); - } else { + else bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); - } break; case BFA_PPORT_SM_STOP: bfa_sm_set_state(pport, bfa_pport_sm_stopped); bfa_pport_reset_linkinfo(pport); - if (BFA_PORT_IS_DISABLED(pport->bfa)) { + if (BFA_PORT_IS_DISABLED(pport->bfa)) bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); - } else { + else bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); - } break; case BFA_PPORT_SM_HWFAIL: bfa_sm_set_state(pport, bfa_pport_sm_iocdown); bfa_pport_reset_linkinfo(pport); bfa_pport_callback(pport, BFA_PPORT_LINKDOWN); - if (BFA_PORT_IS_DISABLED(pport->bfa)) { + if (BFA_PORT_IS_DISABLED(pport->bfa)) bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); - } else { + else bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); - } break; default: @@ -999,10 +996,10 @@ bfa_pport_enable(struct bfa_s *bfa) struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); if (pport->diag_busy) - return (BFA_STATUS_DIAG_BUSY); + return BFA_STATUS_DIAG_BUSY; else if (bfa_sm_cmp_state (BFA_PORT_MOD(bfa), bfa_pport_sm_disabling_qwait)) - return (BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_ENABLE); return BFA_STATUS_OK; @@ -1032,7 +1029,7 @@ bfa_pport_cfg_speed(struct bfa_s *bfa, enum bfa_pport_speed speed) pport->cfg.speed = speed; - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } /** @@ -1068,7 +1065,7 @@ bfa_pport_cfg_topology(struct bfa_s *bfa, enum bfa_pport_topology topology) } pport->cfg.topology = topology; - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } /** @@ -1094,7 +1091,7 @@ bfa_pport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa) pport->cfg.cfg_hardalpa = BFA_TRUE; pport->cfg.hardalpa = alpa; - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } bfa_status_t @@ -1106,7 +1103,7 @@ bfa_pport_clr_hardalpa(struct bfa_s *bfa) bfa_trc(bfa, pport->cfg.hardalpa); pport->cfg.cfg_hardalpa = BFA_FALSE; - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } bfa_boolean_t @@ -1138,16 +1135,16 @@ bfa_pport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxfrsize) * with in range */ if ((maxfrsize > FC_MAX_PDUSZ) || (maxfrsize < FC_MIN_PDUSZ)) - return (BFA_STATUS_INVLD_DFSZ); + return BFA_STATUS_INVLD_DFSZ; /* * power of 2, if not the max frame size of 2112 */ if ((maxfrsize != FC_MAX_PDUSZ) && (maxfrsize & (maxfrsize - 1))) - return (BFA_STATUS_INVLD_DFSZ); + return BFA_STATUS_INVLD_DFSZ; pport->cfg.maxfrsize = maxfrsize; - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } u16 @@ -1415,7 +1412,7 @@ bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats, if (port->stats_busy) { bfa_trc(bfa, port->stats_busy); - return (BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; } port->stats_busy = BFA_TRUE; @@ -1427,7 +1424,7 @@ bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats, bfa_timer_start(bfa, &port->timer, bfa_port_stats_timeout, port, BFA_PORT_STATS_TOV); - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } bfa_status_t @@ -1437,7 +1434,7 @@ bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) if (port->stats_busy) { bfa_trc(bfa, port->stats_busy); - return (BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; } port->stats_busy = BFA_TRUE; @@ -1448,7 +1445,7 @@ bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port, BFA_PORT_STATS_TOV); - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } bfa_status_t @@ -1515,7 +1512,7 @@ bfa_pport_get_qos_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats, /* * QoS stats is embedded in port stats */ - return (bfa_pport_get_stats(bfa, stats, cbfn, cbarg)); + return bfa_pport_get_stats(bfa, stats, cbfn, cbarg); } bfa_status_t @@ -1525,7 +1522,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) if (port->stats_busy) { bfa_trc(bfa, port->stats_busy); - return (BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; } port->stats_busy = BFA_TRUE; @@ -1536,7 +1533,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port, BFA_PORT_STATS_TOV); - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } /** @@ -1545,7 +1542,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) bfa_status_t bfa_pport_trunk_disable(struct bfa_s *bfa) { - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } bfa_boolean_t @@ -1562,8 +1559,8 @@ bfa_pport_is_disabled(struct bfa_s *bfa) { struct bfa_pport_s *port = BFA_PORT_MOD(bfa); - return (bfa_sm_to_state(hal_pport_sm_table, port->sm) == - BFA_PPORT_ST_DISABLED); + return bfa_sm_to_state(hal_pport_sm_table, port->sm) == + BFA_PPORT_ST_DISABLED; } @@ -1572,7 +1569,7 @@ bfa_pport_is_ratelim(struct bfa_s *bfa) { struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); -return (pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE); + return pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE; } @@ -1620,7 +1617,7 @@ bfa_pport_cfg_ratelim_speed(struct bfa_s *bfa, enum bfa_pport_speed speed) pport->cfg.trl_def_speed = speed; - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } /** @@ -1632,7 +1629,7 @@ bfa_pport_get_ratelim_speed(struct bfa_s *bfa) struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); bfa_trc(bfa, pport->cfg.trl_def_speed); - return (pport->cfg.trl_def_speed); + return pport->cfg.trl_def_speed; } diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c index 8975ed041dc0..c7ab257f10a7 100644 --- a/drivers/scsi/bfa/bfa_fcs_lport.c +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -568,11 +568,10 @@ bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port) __port_action[port->fabric->fab_type].offline(port); - if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE) { + if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE) bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DISCONNECT); - } else { + else bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_OFFLINE); - } bfa_fcb_port_offline(port->fcs->bfad, port->port_cfg.roles, port->fabric->vf_drv, (port->vport == NULL) ? NULL : port->vport->vport_drv); @@ -777,7 +776,7 @@ bfa_fcs_port_get_rport_by_pwwn(struct bfa_fcs_port_s *port, wwn_t pwwn) } bfa_trc(port->fcs, pwwn); - return (NULL); + return NULL; } /** @@ -796,7 +795,7 @@ bfa_fcs_port_get_rport_by_nwwn(struct bfa_fcs_port_s *port, wwn_t nwwn) } bfa_trc(port->fcs, nwwn); - return (NULL); + return NULL; } /** @@ -870,7 +869,7 @@ bfa_fcs_port_lip(struct bfa_fcs_port_s *port) bfa_boolean_t bfa_fcs_port_is_online(struct bfa_fcs_port_s *port) { - return (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online)); + return bfa_sm_cmp_state(port, bfa_fcs_port_sm_online); } /** diff --git a/drivers/scsi/bfa/bfa_fcxp.c b/drivers/scsi/bfa/bfa_fcxp.c index 4754a0e9006a..cf0ad6782686 100644 --- a/drivers/scsi/bfa/bfa_fcxp.c +++ b/drivers/scsi/bfa/bfa_fcxp.c @@ -199,7 +199,7 @@ bfa_fcxp_get(struct bfa_fcxp_mod_s *fm) if (fcxp) list_add_tail(&fcxp->qe, &fm->fcxp_active_q); - return (fcxp); + return fcxp; } static void @@ -503,7 +503,7 @@ bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles, fcxp = bfa_fcxp_get(BFA_FCXP_MOD(bfa)); if (fcxp == NULL) - return (NULL); + return NULL; bfa_trc(bfa, fcxp->fcxp_tag); @@ -568,7 +568,7 @@ bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles, } } - return (fcxp); + return fcxp; } /** @@ -709,7 +709,7 @@ bfa_status_t bfa_fcxp_abort(struct bfa_fcxp_s *fcxp) { bfa_assert(0); - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } void diff --git a/drivers/scsi/bfa/bfa_intr.c b/drivers/scsi/bfa/bfa_intr.c index 0ca125712a04..b36540e4ed76 100644 --- a/drivers/scsi/bfa/bfa_intr.c +++ b/drivers/scsi/bfa/bfa_intr.c @@ -59,7 +59,7 @@ bfa_intx(struct bfa_s *bfa) qintr = intr & __HFN_INT_RME_MASK; bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, qintr); - for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue ++) { + for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue++) { if (intr & (__HFN_INT_RME_Q0 << queue)) bfa_msix_rspq(bfa, queue & (BFI_IOC_MAX_CQS - 1)); } diff --git a/drivers/scsi/bfa/bfa_intr_priv.h b/drivers/scsi/bfa/bfa_intr_priv.h index 8ce6e6b105c8..5fc301cf4d1b 100644 --- a/drivers/scsi/bfa/bfa_intr_priv.h +++ b/drivers/scsi/bfa/bfa_intr_priv.h @@ -26,9 +26,9 @@ void bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m); void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func); -#define bfa_reqq_pi(__bfa, __reqq) (__bfa)->iocfc.req_cq_pi[__reqq] +#define bfa_reqq_pi(__bfa, __reqq) ((__bfa)->iocfc.req_cq_pi[__reqq]) #define bfa_reqq_ci(__bfa, __reqq) \ - *(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva) + (*(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva)) #define bfa_reqq_full(__bfa, __reqq) \ (((bfa_reqq_pi(__bfa, __reqq) + 1) & \ @@ -50,14 +50,16 @@ void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func); } while (0) #define bfa_rspq_pi(__bfa, __rspq) \ - *(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva) + (*(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva)) -#define bfa_rspq_ci(__bfa, __rspq) (__bfa)->iocfc.rsp_cq_ci[__rspq] +#define bfa_rspq_ci(__bfa, __rspq) ((__bfa)->iocfc.rsp_cq_ci[__rspq]) #define bfa_rspq_elem(__bfa, __rspq, __ci) \ - &((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci] + (&((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci]) -#define CQ_INCR(__index, __size) \ - (__index)++; (__index) &= ((__size) - 1) +#define CQ_INCR(__index, __size) do { \ + (__index)++; \ + (__index) &= ((__size) - 1); \ +} while (0) /** * Queue element to wait for room in request queue. FIFO order is @@ -94,7 +96,7 @@ bfa_reqq_winit(struct bfa_reqq_wait_s *wqe, void (*qresume) (void *cbarg), wqe->cbarg = cbarg; } -#define bfa_reqq(__bfa, __reqq) &(__bfa)->reqq_waitq[__reqq] +#define bfa_reqq(__bfa, __reqq) (&(__bfa)->reqq_waitq[__reqq]) /** * static inline void diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c index 149348934ce3..397d7e9eade5 100644 --- a/drivers/scsi/bfa/bfa_ioc.c +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -51,7 +51,7 @@ BFA_TRC_FILE(HAL, IOC); (sizeof(struct bfa_trc_mod_s) - \ BFA_TRC_MAX * sizeof(struct bfa_trc_s))) #define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn)) -#define bfa_ioc_stats(_ioc, _stats) (_ioc)->stats._stats ++ +#define bfa_ioc_stats(_ioc, _stats) ((_ioc)->stats._stats++) #define BFA_FLASH_CHUNK_NO(off) (off / BFI_FLASH_CHUNK_SZ_WORDS) #define BFA_FLASH_OFFSET_IN_CHUNK(off) (off % BFI_FLASH_CHUNK_SZ_WORDS) @@ -1953,8 +1953,8 @@ bfa_ioc_error_isr(struct bfa_ioc_s *ioc) bfa_boolean_t bfa_ioc_is_disabled(struct bfa_ioc_s *ioc) { - return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling) - || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled)); + return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling) + || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled); } /** @@ -1963,9 +1963,9 @@ bfa_ioc_is_disabled(struct bfa_ioc_s *ioc) bfa_boolean_t bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc) { - return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset) + return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset) || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_fwcheck) - || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch)); + || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch); } #define bfa_ioc_state_disabled(__sm) \ diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h index 58efd4b13143..7c30f05ab137 100644 --- a/drivers/scsi/bfa/bfa_ioc.h +++ b/drivers/scsi/bfa/bfa_ioc.h @@ -179,16 +179,16 @@ struct bfa_ioc_s { struct bfa_ioc_mbox_mod_s mbox_mod; }; -#define bfa_ioc_pcifn(__ioc) (__ioc)->pcidev.pci_func -#define bfa_ioc_devid(__ioc) (__ioc)->pcidev.device_id -#define bfa_ioc_bar0(__ioc) (__ioc)->pcidev.pci_bar_kva +#define bfa_ioc_pcifn(__ioc) ((__ioc)->pcidev.pci_func) +#define bfa_ioc_devid(__ioc) ((__ioc)->pcidev.device_id) +#define bfa_ioc_bar0(__ioc) ((__ioc)->pcidev.pci_bar_kva) #define bfa_ioc_portid(__ioc) ((__ioc)->port_id) #define bfa_ioc_fetch_stats(__ioc, __stats) \ - ((__stats)->drv_stats) = (__ioc)->stats + (((__stats)->drv_stats) = (__ioc)->stats) #define bfa_ioc_clr_stats(__ioc) \ bfa_os_memset(&(__ioc)->stats, 0, sizeof((__ioc)->stats)) -#define bfa_ioc_maxfrsize(__ioc) (__ioc)->attr->maxfrsize -#define bfa_ioc_rx_bbcredit(__ioc) (__ioc)->attr->rx_bbcredit +#define bfa_ioc_maxfrsize(__ioc) ((__ioc)->attr->maxfrsize) +#define bfa_ioc_rx_bbcredit(__ioc) ((__ioc)->attr->rx_bbcredit) #define bfa_ioc_speed_sup(__ioc) \ BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop) diff --git a/drivers/scsi/bfa/bfa_iocfc.c b/drivers/scsi/bfa/bfa_iocfc.c index 12350b022d63..d7ab792a9e54 100644 --- a/drivers/scsi/bfa/bfa_iocfc.c +++ b/drivers/scsi/bfa/bfa_iocfc.c @@ -794,7 +794,7 @@ bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats, if (iocfc->stats_busy) { bfa_trc(bfa, iocfc->stats_busy); - return (BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; } iocfc->stats_busy = BFA_TRUE; @@ -804,7 +804,7 @@ bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats, bfa_iocfc_stats_query(bfa); - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } bfa_status_t @@ -814,7 +814,7 @@ bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg) if (iocfc->stats_busy) { bfa_trc(bfa, iocfc->stats_busy); - return (BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; } iocfc->stats_busy = BFA_TRUE; @@ -822,7 +822,7 @@ bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg) iocfc->stats_cbarg = cbarg; bfa_iocfc_stats_clear(bfa); - return (BFA_STATUS_OK); + return BFA_STATUS_OK; } /** diff --git a/drivers/scsi/bfa/bfa_iocfc.h b/drivers/scsi/bfa/bfa_iocfc.h index 7ad177ed4cfc..ce9a830a4207 100644 --- a/drivers/scsi/bfa/bfa_iocfc.h +++ b/drivers/scsi/bfa/bfa_iocfc.h @@ -107,13 +107,13 @@ struct bfa_iocfc_s { #define bfa_lpuid(__bfa) bfa_ioc_portid(&(__bfa)->ioc) #define bfa_msix_init(__bfa, __nvecs) \ - (__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs) + ((__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs)) #define bfa_msix_install(__bfa) \ - (__bfa)->iocfc.hwif.hw_msix_install(__bfa) + ((__bfa)->iocfc.hwif.hw_msix_install(__bfa)) #define bfa_msix_uninstall(__bfa) \ - (__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa) + ((__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa)) #define bfa_isr_mode_set(__bfa, __msix) \ - (__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix) + ((__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix)) #define bfa_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) \ (__bfa)->iocfc.hwif.hw_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) diff --git a/drivers/scsi/bfa/bfa_ioim.c b/drivers/scsi/bfa/bfa_ioim.c index 7ae2552e1e14..f81d359b7089 100644 --- a/drivers/scsi/bfa/bfa_ioim.c +++ b/drivers/scsi/bfa/bfa_ioim.c @@ -105,13 +105,13 @@ bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); list_del(&ioim->qe); list_add_tail(&ioim->qe, - &ioim->fcpim->ioim_comp_q); + &ioim->fcpim->ioim_comp_q); bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_pathtov, ioim); } else { list_del(&ioim->qe); list_add_tail(&ioim->qe, - &ioim->itnim->pending_q); + &ioim->itnim->pending_q); } break; } diff --git a/drivers/scsi/bfa/bfa_itnim.c b/drivers/scsi/bfa/bfa_itnim.c index 4d5c61a4f85c..eabf7d38bd09 100644 --- a/drivers/scsi/bfa/bfa_itnim.c +++ b/drivers/scsi/bfa/bfa_itnim.c @@ -1029,7 +1029,7 @@ bfa_itnim_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void *ditn) bfa_stats(itnim, creates); bfa_sm_send_event(itnim, BFA_ITNIM_SM_CREATE); - return (itnim); + return itnim; } void @@ -1061,7 +1061,7 @@ bfa_itnim_offline(struct bfa_itnim_s *itnim) bfa_boolean_t bfa_itnim_hold_io(struct bfa_itnim_s *itnim) { - return ( + return itnim->fcpim->path_tov && itnim->iotov_active && (bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwcreate) || bfa_sm_cmp_state(itnim, bfa_itnim_sm_sler) || @@ -1069,7 +1069,7 @@ bfa_itnim_hold_io(struct bfa_itnim_s *itnim) bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwdelete) || bfa_sm_cmp_state(itnim, bfa_itnim_sm_offline) || bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable)) -); + ; } void diff --git a/drivers/scsi/bfa/bfa_log.c b/drivers/scsi/bfa/bfa_log.c index c2735e55cf03..e7514016c9c6 100644 --- a/drivers/scsi/bfa/bfa_log.c +++ b/drivers/scsi/bfa/bfa_log.c @@ -231,9 +231,9 @@ bfa_log_get_level(struct bfa_log_mod_s *log_mod, int mod_id) return BFA_LOG_INVALID; if (log_mod) - return (log_mod->log_level[mod_id]); + return log_mod->log_level[mod_id]; else - return (bfa_log_info[mod_id].level); + return bfa_log_info[mod_id].level; } enum bfa_log_severity diff --git a/drivers/scsi/bfa/bfa_port_priv.h b/drivers/scsi/bfa/bfa_port_priv.h index 4b97e2759908..51f698a06b6d 100644 --- a/drivers/scsi/bfa/bfa_port_priv.h +++ b/drivers/scsi/bfa/bfa_port_priv.h @@ -59,8 +59,8 @@ struct bfa_pport_s { u8 *stats_kva; u64 stats_pa; union bfa_pport_stats_u *stats; /* pport stats */ - u32 mypid : 24; - u32 rsvd_b : 8; + u32 mypid:24; + u32 rsvd_b:8; struct bfa_timer_s timer; /* timer */ union bfa_pport_stats_u *stats_ret; /* driver stats location */ diff --git a/drivers/scsi/bfa/bfa_rport.c b/drivers/scsi/bfa/bfa_rport.c index 16da77a8db28..3e1990a74258 100644 --- a/drivers/scsi/bfa/bfa_rport.c +++ b/drivers/scsi/bfa/bfa_rport.c @@ -677,7 +677,7 @@ bfa_rport_alloc(struct bfa_rport_mod_s *mod) if (rport) list_add_tail(&rport->qe, &mod->rp_active_q); - return (rport); + return rport; } static void @@ -834,7 +834,7 @@ bfa_rport_create(struct bfa_s *bfa, void *rport_drv) rp = bfa_rport_alloc(BFA_RPORT_MOD(bfa)); if (rp == NULL) - return (NULL); + return NULL; rp->bfa = bfa; rp->rport_drv = rport_drv; @@ -843,7 +843,7 @@ bfa_rport_create(struct bfa_s *bfa, void *rport_drv) bfa_assert(bfa_sm_cmp_state(rp, bfa_rport_sm_uninit)); bfa_sm_send_event(rp, BFA_RPORT_SM_CREATE); - return (rp); + return rp; } void diff --git a/drivers/scsi/bfa/bfa_tskim.c b/drivers/scsi/bfa/bfa_tskim.c index 010d40d1e5d3..ff7a4dc0bf3c 100644 --- a/drivers/scsi/bfa/bfa_tskim.c +++ b/drivers/scsi/bfa/bfa_tskim.c @@ -23,13 +23,14 @@ BFA_TRC_FILE(HAL, TSKIM); /** * task management completion handling */ -#define bfa_tskim_qcomp(__tskim, __cbfn) do { \ - bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, __cbfn, (__tskim)); \ +#define bfa_tskim_qcomp(__tskim, __cbfn) do { \ + bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, \ + __cbfn, (__tskim)); \ bfa_tskim_notify_comp(__tskim); \ } while (0) -#define bfa_tskim_notify_comp(__tskim) do { \ - if ((__tskim)->notify) \ +#define bfa_tskim_notify_comp(__tskim) do { \ + if ((__tskim)->notify) \ bfa_itnim_tskdone((__tskim)->itnim); \ } while (0) diff --git a/drivers/scsi/bfa/bfa_uf.c b/drivers/scsi/bfa/bfa_uf.c index ff5f9deb1b22..4b3c2417d180 100644 --- a/drivers/scsi/bfa/bfa_uf.c +++ b/drivers/scsi/bfa/bfa_uf.c @@ -185,7 +185,7 @@ bfa_uf_get(struct bfa_uf_mod_s *uf_mod) struct bfa_uf_s *uf; bfa_q_deq(&uf_mod->uf_free_q, &uf); - return (uf); + return uf; } static void diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c index 6f2be5abf561..b52b773d49d9 100644 --- a/drivers/scsi/bfa/bfad.c +++ b/drivers/scsi/bfa/bfad.c @@ -188,8 +188,8 @@ static struct bfad_port_s * bfad_get_drv_port(struct bfad_s *bfad, struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv) { - return ((vp_drv) ? (&(vp_drv)->drv_port) - : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport))); + return (vp_drv) ? (&(vp_drv)->drv_port) + : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport)); } struct bfad_port_s * @@ -716,7 +716,7 @@ bfad_drv_init(struct bfad_s *bfad) if ((bfad->bfad_flags & BFAD_MSIX_ON) && bfad_install_msix_handler(bfad)) { printk(KERN_WARNING "%s: install_msix failed, bfad%d\n", - __FUNCTION__, bfad->inst_no); + __func__, bfad->inst_no); } bfad_init_timer(bfad); diff --git a/drivers/scsi/bfa/bfad_fwimg.c b/drivers/scsi/bfa/bfad_fwimg.c index bd34b0db2d6b..2ad65f275a92 100644 --- a/drivers/scsi/bfa/bfad_fwimg.c +++ b/drivers/scsi/bfa/bfad_fwimg.c @@ -65,10 +65,10 @@ bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image, memcpy(*bfi_image, fw->data, fw->size); *bfi_image_size = fw->size/sizeof(u32); - return(*bfi_image); + return *bfi_image; error: - return(NULL); + return NULL; } u32 * @@ -78,12 +78,12 @@ bfad_get_firmware_buf(struct pci_dev *pdev) if (bfi_image_ct_size == 0) bfad_read_firmware(pdev, &bfi_image_ct, &bfi_image_ct_size, BFAD_FW_FILE_CT); - return(bfi_image_ct); + return bfi_image_ct; } else { if (bfi_image_cb_size == 0) bfad_read_firmware(pdev, &bfi_image_cb, &bfi_image_cb_size, BFAD_FW_FILE_CB); - return(bfi_image_cb); + return bfi_image_cb; } } diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c index 55d012a9a668..f788c2a0ab07 100644 --- a/drivers/scsi/bfa/bfad_im.c +++ b/drivers/scsi/bfa/bfad_im.c @@ -1050,7 +1050,7 @@ bfad_im_itnim_work_handler(struct work_struct *work) } else { printk(KERN_WARNING "%s: itnim %llx is already in online state\n", - __FUNCTION__, + __func__, bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); } diff --git a/drivers/scsi/bfa/bfad_im_compat.h b/drivers/scsi/bfa/bfad_im_compat.h index 1d3e74ec338c..b36be15044a4 100644 --- a/drivers/scsi/bfa/bfad_im_compat.h +++ b/drivers/scsi/bfa/bfad_im_compat.h @@ -31,7 +31,7 @@ u32 *bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image, static inline u32 * bfad_load_fwimg(struct pci_dev *pdev) { - return(bfad_get_firmware_buf(pdev)); + return bfad_get_firmware_buf(pdev); } static inline void diff --git a/drivers/scsi/bfa/bfad_intr.c b/drivers/scsi/bfa/bfad_intr.c index f104e029cac9..7de8832f6fee 100644 --- a/drivers/scsi/bfa/bfad_intr.c +++ b/drivers/scsi/bfa/bfad_intr.c @@ -23,13 +23,12 @@ BFA_TRC_FILE(LDRV, INTR); /** * bfa_isr BFA driver interrupt functions */ -irqreturn_t bfad_intx(int irq, void *dev_id); static int msix_disable; module_param(msix_disable, int, S_IRUGO | S_IWUSR); /** * Line based interrupt handler. */ -irqreturn_t +static irqreturn_t bfad_intx(int irq, void *dev_id) { struct bfad_s *bfad = dev_id; diff --git a/drivers/scsi/bfa/fabric.c b/drivers/scsi/bfa/fabric.c index a8b14c47b009..a4b5dd449573 100644 --- a/drivers/scsi/bfa/fabric.c +++ b/drivers/scsi/bfa/fabric.c @@ -36,12 +36,12 @@ BFA_TRC_FILE(FCS, FABRIC); #define BFA_FCS_FABRIC_RETRY_DELAY (2000) /* Milliseconds */ #define BFA_FCS_FABRIC_CLEANUP_DELAY (10000) /* Milliseconds */ -#define bfa_fcs_fabric_set_opertype(__fabric) do { \ - if (bfa_pport_get_topology((__fabric)->fcs->bfa) \ - == BFA_PPORT_TOPOLOGY_P2P) \ - (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \ - else \ - (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \ +#define bfa_fcs_fabric_set_opertype(__fabric) do { \ + if (bfa_pport_get_topology((__fabric)->fcs->bfa) \ + == BFA_PPORT_TOPOLOGY_P2P) \ + (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \ + else \ + (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \ } while (0) /* @@ -887,7 +887,7 @@ bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs) bfa_boolean_t bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric) { - return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback)); + return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback); } enum bfa_pport_type @@ -974,7 +974,7 @@ bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric) int bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric) { - return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online)); + return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online); } @@ -1015,7 +1015,7 @@ bfa_fcs_fabric_vport_lookup(struct bfa_fcs_fabric_s *fabric, wwn_t pwwn) u16 bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric) { - return (fabric->num_vports); + return fabric->num_vports; } /** diff --git a/drivers/scsi/bfa/fcbuild.c b/drivers/scsi/bfa/fcbuild.c index d174706b9caa..fee5456451cb 100644 --- a/drivers/scsi/bfa/fcbuild.c +++ b/drivers/scsi/bfa/fcbuild.c @@ -188,14 +188,14 @@ fc_els_rsp_parse(struct fchs_s *fchs, int len) switch (els_cmd->els_code) { case FC_ELS_LS_RJT: if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY) - return (FC_PARSE_BUSY); + return FC_PARSE_BUSY; else - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; case FC_ELS_ACC: - return (FC_PARSE_OK); + return FC_PARSE_OK; } - return (FC_PARSE_OK); + return FC_PARSE_OK; } static void @@ -228,7 +228,7 @@ fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, bfa_os_memcpy(&plogi->port_name, &port_name, sizeof(wwn_t)); bfa_os_memcpy(&plogi->node_name, &node_name, sizeof(wwn_t)); - return (sizeof(struct fc_logi_s)); + return sizeof(struct fc_logi_s); } u16 @@ -267,7 +267,7 @@ fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, flogi->csp.npiv_supp = 1; /* @todo. field name is not correct */ vvl_info[0] = bfa_os_htonl(FLOGI_VVL_BRCD); - return (sizeof(struct fc_logi_s)); + return sizeof(struct fc_logi_s); } u16 @@ -287,7 +287,7 @@ fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, flogi->csp.bbcred = bfa_os_htons(local_bb_credits); - return (sizeof(struct fc_logi_s)); + return sizeof(struct fc_logi_s); } u16 @@ -306,7 +306,7 @@ fc_fdisc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, flogi->port_name = port_name; flogi->node_name = node_name; - return (sizeof(struct fc_logi_s)); + return sizeof(struct fc_logi_s); } u16 @@ -338,26 +338,26 @@ fc_plogi_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name) case FC_ELS_LS_RJT: ls_rjt = (struct fc_ls_rjt_s *) (fchs + 1); if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY) - return (FC_PARSE_BUSY); + return FC_PARSE_BUSY; else - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; case FC_ELS_ACC: plogi = (struct fc_logi_s *) (fchs + 1); if (len < sizeof(struct fc_logi_s)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (!wwn_is_equal(plogi->port_name, port_name)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (!plogi->class3.class_valid) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (bfa_os_ntohs(plogi->class3.rxsz) < (FC_MIN_PDUSZ)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; - return (FC_PARSE_OK); + return FC_PARSE_OK; default: - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; } } @@ -372,7 +372,7 @@ fc_plogi_parse(struct fchs_s *fchs) if ((bfa_os_ntohs(plogi->class3.rxsz) < FC_MIN_PDUSZ) || (bfa_os_ntohs(plogi->class3.rxsz) > FC_MAX_PDUSZ) || (plogi->class3.rxsz == 0)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; return FC_PARSE_OK; } @@ -393,7 +393,7 @@ fc_prli_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, prli->parampage.servparams.task_retry_id = 0; prli->parampage.servparams.confirm = 1; - return (sizeof(struct fc_prli_s)); + return sizeof(struct fc_prli_s); } u16 @@ -414,41 +414,41 @@ fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, prli->parampage.rspcode = FC_PRLI_ACC_XQTD; - return (sizeof(struct fc_prli_s)); + return sizeof(struct fc_prli_s); } enum fc_parse_status fc_prli_rsp_parse(struct fc_prli_s *prli, int len) { if (len < sizeof(struct fc_prli_s)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (prli->command != FC_ELS_ACC) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if ((prli->parampage.rspcode != FC_PRLI_ACC_XQTD) && (prli->parampage.rspcode != FC_PRLI_ACC_PREDEF_IMG)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (prli->parampage.servparams.target != 1) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; - return (FC_PARSE_OK); + return FC_PARSE_OK; } enum fc_parse_status fc_prli_parse(struct fc_prli_s *prli) { if (prli->parampage.type != FC_TYPE_FCP) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (!prli->parampage.imagepair) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (!prli->parampage.servparams.initiator) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; - return (FC_PARSE_OK); + return FC_PARSE_OK; } u16 @@ -462,7 +462,7 @@ fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, u32 d_id, logo->nport_id = (s_id); logo->orig_port_name = port_name; - return (sizeof(struct fc_logo_s)); + return sizeof(struct fc_logo_s); } static u16 @@ -484,7 +484,7 @@ fc_adisc_x_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id, adisc->orig_node_name = node_name; adisc->nport_id = (s_id); - return (sizeof(struct fc_adisc_s)); + return sizeof(struct fc_adisc_s); } u16 @@ -511,15 +511,15 @@ fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, wwn_t port_name, { if (len < sizeof(struct fc_adisc_s)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (adisc->els_cmd.els_code != FC_ELS_ACC) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (!wwn_is_equal(adisc->orig_port_name, port_name)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; - return (FC_PARSE_OK); + return FC_PARSE_OK; } enum fc_parse_status @@ -529,14 +529,14 @@ fc_adisc_parse(struct fchs_s *fchs, void *pld, u32 host_dap, struct fc_adisc_s *adisc = (struct fc_adisc_s *) pld; if (adisc->els_cmd.els_code != FC_ELS_ACC) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if ((adisc->nport_id == (host_dap)) && wwn_is_equal(adisc->orig_port_name, port_name) && wwn_is_equal(adisc->orig_node_name, node_name)) - return (FC_PARSE_OK); + return FC_PARSE_OK; - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; } enum fc_parse_status @@ -550,13 +550,13 @@ fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, wwn_t port_name) if ((bfa_os_ntohs(pdisc->class3.rxsz) < (FC_MIN_PDUSZ - sizeof(struct fchs_s))) || (pdisc->class3.rxsz == 0)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (!wwn_is_equal(pdisc->port_name, port_name)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; if (!wwn_is_equal(pdisc->node_name, node_name)) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; return FC_PARSE_OK; } @@ -570,7 +570,7 @@ fc_abts_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id) fchs->s_id = (s_id); fchs->ox_id = bfa_os_htons(ox_id); - return (sizeof(struct fchs_s)); + return sizeof(struct fchs_s); } enum fc_parse_status @@ -578,9 +578,9 @@ fc_abts_rsp_parse(struct fchs_s *fchs, int len) { if ((fchs->cat_info == FC_CAT_BA_ACC) || (fchs->cat_info == FC_CAT_BA_RJT)) - return (FC_PARSE_OK); + return FC_PARSE_OK; - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; } u16 @@ -597,7 +597,7 @@ fc_rrq_build(struct fchs_s *fchs, struct fc_rrq_s *rrq, u32 d_id, rrq->ox_id = bfa_os_htons(rrq_oxid); rrq->rx_id = FC_RXID_ANY; - return (sizeof(struct fc_rrq_s)); + return sizeof(struct fc_rrq_s); } u16 @@ -611,7 +611,7 @@ fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, memset(acc, 0, sizeof(struct fc_els_cmd_s)); acc->els_code = FC_ELS_ACC; - return (sizeof(struct fc_els_cmd_s)); + return sizeof(struct fc_els_cmd_s); } u16 @@ -627,7 +627,7 @@ fc_ls_rjt_build(struct fchs_s *fchs, struct fc_ls_rjt_s *ls_rjt, u32 d_id, ls_rjt->reason_code_expl = reason_code_expl; ls_rjt->vendor_unique = 0x00; - return (sizeof(struct fc_ls_rjt_s)); + return sizeof(struct fc_ls_rjt_s); } u16 @@ -643,7 +643,7 @@ fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, u32 d_id, ba_acc->ox_id = fchs->ox_id; ba_acc->rx_id = fchs->rx_id; - return (sizeof(struct fc_ba_acc_s)); + return sizeof(struct fc_ba_acc_s); } u16 @@ -654,7 +654,7 @@ fc_ls_acc_build(struct fchs_s *fchs, struct fc_els_cmd_s *els_cmd, memset(els_cmd, 0, sizeof(struct fc_els_cmd_s)); els_cmd->els_code = FC_ELS_ACC; - return (sizeof(struct fc_els_cmd_s)); + return sizeof(struct fc_els_cmd_s); } int @@ -696,7 +696,7 @@ fc_tprlo_acc_build(struct fchs_s *fchs, struct fc_tprlo_acc_s *tprlo_acc, tprlo_acc->tprlo_acc_params[page].orig_process_assc = 0; tprlo_acc->tprlo_acc_params[page].resp_process_assc = 0; } - return (bfa_os_ntohs(tprlo_acc->payload_len)); + return bfa_os_ntohs(tprlo_acc->payload_len); } u16 @@ -721,7 +721,7 @@ fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc, prlo_acc->prlo_acc_params[page].resp_process_assc = 0; } - return (bfa_os_ntohs(prlo_acc->payload_len)); + return bfa_os_ntohs(prlo_acc->payload_len); } u16 @@ -735,7 +735,7 @@ fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, u32 d_id, rnid->els_cmd.els_code = FC_ELS_RNID; rnid->node_id_data_format = data_format; - return (sizeof(struct fc_rnid_cmd_s)); + return sizeof(struct fc_rnid_cmd_s); } u16 @@ -759,10 +759,10 @@ fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc, rnid_acc->specific_id_data_length = sizeof(struct fc_rnid_general_topology_data_s); bfa_os_assign(rnid_acc->gen_topology_data, *gen_topo_data); - return (sizeof(struct fc_rnid_acc_s)); + return sizeof(struct fc_rnid_acc_s); } else { - return (sizeof(struct fc_rnid_acc_s) - - sizeof(struct fc_rnid_general_topology_data_s)); + return sizeof(struct fc_rnid_acc_s) - + sizeof(struct fc_rnid_general_topology_data_s); } } @@ -776,7 +776,7 @@ fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, u32 d_id, memset(rpsc, 0, sizeof(struct fc_rpsc_cmd_s)); rpsc->els_cmd.els_code = FC_ELS_RPSC; - return (sizeof(struct fc_rpsc_cmd_s)); + return sizeof(struct fc_rpsc_cmd_s); } u16 @@ -797,8 +797,8 @@ fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rpsc2, for (i = 0; i < npids; i++) rpsc2->pid_list[i].pid = pid_list[i]; - return (sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) * - (sizeof(u32)))); + return sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) * + (sizeof(u32))); } u16 @@ -819,7 +819,7 @@ fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc, rpsc_acc->speed_info[0].port_op_speed = bfa_os_htons(oper_speed->port_op_speed); - return (sizeof(struct fc_rpsc_acc_s)); + return sizeof(struct fc_rpsc_acc_s); } @@ -856,7 +856,7 @@ fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id, pdisc->port_name = port_name; pdisc->node_name = node_name; - return (sizeof(struct fc_logi_s)); + return sizeof(struct fc_logi_s); } u16 @@ -865,21 +865,21 @@ fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name) struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1); if (len < sizeof(struct fc_logi_s)) - return (FC_PARSE_LEN_INVAL); + return FC_PARSE_LEN_INVAL; if (pdisc->els_cmd.els_code != FC_ELS_ACC) - return (FC_PARSE_ACC_INVAL); + return FC_PARSE_ACC_INVAL; if (!wwn_is_equal(pdisc->port_name, port_name)) - return (FC_PARSE_PWWN_NOT_EQUAL); + return FC_PARSE_PWWN_NOT_EQUAL; if (!pdisc->class3.class_valid) - return (FC_PARSE_NWWN_NOT_EQUAL); + return FC_PARSE_NWWN_NOT_EQUAL; if (bfa_os_ntohs(pdisc->class3.rxsz) < (FC_MIN_PDUSZ)) - return (FC_PARSE_RXSZ_INVAL); + return FC_PARSE_RXSZ_INVAL; - return (FC_PARSE_OK); + return FC_PARSE_OK; } u16 @@ -903,7 +903,7 @@ fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id, prlo->prlo_params[page].resp_process_assc = 0; } - return (bfa_os_ntohs(prlo->payload_len)); + return bfa_os_ntohs(prlo->payload_len); } u16 @@ -916,7 +916,7 @@ fc_prlo_rsp_parse(struct fchs_s *fchs, int len) len = len; if (prlo->command != FC_ELS_ACC) - return (FC_PARSE_FAILURE); + return FC_PARSE_FAILURE; num_pages = ((bfa_os_ntohs(prlo->payload_len)) - 4) / 16; @@ -936,7 +936,7 @@ fc_prlo_rsp_parse(struct fchs_s *fchs, int len) if (prlo->prlo_acc_params[page].resp_process_assc != 0) return FC_PARSE_FAILURE; } - return (FC_PARSE_OK); + return FC_PARSE_OK; } @@ -968,7 +968,7 @@ fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, } } - return (bfa_os_ntohs(tprlo->payload_len)); + return bfa_os_ntohs(tprlo->payload_len); } u16 @@ -981,23 +981,23 @@ fc_tprlo_rsp_parse(struct fchs_s *fchs, int len) len = len; if (tprlo->command != FC_ELS_ACC) - return (FC_PARSE_ACC_INVAL); + return FC_PARSE_ACC_INVAL; num_pages = (bfa_os_ntohs(tprlo->payload_len) - 4) / 16; for (page = 0; page < num_pages; page++) { if (tprlo->tprlo_acc_params[page].type != FC_TYPE_FCP) - return (FC_PARSE_NOT_FCP); + return FC_PARSE_NOT_FCP; if (tprlo->tprlo_acc_params[page].opa_valid != 0) - return (FC_PARSE_OPAFLAG_INVAL); + return FC_PARSE_OPAFLAG_INVAL; if (tprlo->tprlo_acc_params[page].rpa_valid != 0) - return (FC_PARSE_RPAFLAG_INVAL); + return FC_PARSE_RPAFLAG_INVAL; if (tprlo->tprlo_acc_params[page].orig_process_assc != 0) - return (FC_PARSE_OPA_INVAL); + return FC_PARSE_OPA_INVAL; if (tprlo->tprlo_acc_params[page].resp_process_assc != 0) - return (FC_PARSE_RPA_INVAL); + return FC_PARSE_RPA_INVAL; } - return (FC_PARSE_OK); + return FC_PARSE_OK; } enum fc_parse_status @@ -1024,7 +1024,7 @@ fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id, fchs->cat_info = FC_CAT_BA_RJT; ba_rjt->reason_code = reason_code; ba_rjt->reason_expl = reason_expl; - return (sizeof(struct fc_ba_rjt_s)); + return sizeof(struct fc_ba_rjt_s); } static void @@ -1073,7 +1073,7 @@ fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, bfa_os_memset(gidpn, 0, sizeof(struct fcgs_gidpn_req_s)); gidpn->port_name = port_name; - return (sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1090,7 +1090,7 @@ fc_gpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, bfa_os_memset(gpnid, 0, sizeof(fcgs_gpnid_req_t)); gpnid->dap = port_id; - return (sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s)); + return sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s); } u16 @@ -1107,7 +1107,7 @@ fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, bfa_os_memset(gnnid, 0, sizeof(fcgs_gnnid_req_t)); gnnid->dap = port_id; - return (sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s)); + return sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s); } u16 @@ -1137,7 +1137,7 @@ fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr, u8 set_br_reg, if (set_br_reg) scr->vu_reg_func = FC_VU_SCR_REG_FUNC_FABRIC_NAME_CHANGE; - return (sizeof(struct fc_scr_s)); + return sizeof(struct fc_scr_s); } u16 @@ -1157,7 +1157,7 @@ fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, u32 s_id, rscn->event[0].format = FC_RSCN_FORMAT_PORTID; rscn->event[0].portid = s_id; - return (sizeof(struct fc_rscn_pl_s)); + return sizeof(struct fc_rscn_pl_s); } u16 @@ -1188,7 +1188,7 @@ fc_rftid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, rftid->fc4_type[index] |= bfa_os_htonl(type_value); } - return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1210,7 +1210,7 @@ fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id, bfa_os_memcpy((void *)rftid->fc4_type, (void *)fc4_bitmap, (bitmap_size < 32 ? bitmap_size : 32)); - return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1231,7 +1231,7 @@ fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, rffid->fc4ftr_bits = fc4_ftrs; rffid->fc4_type = fc4_type; - return (sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1253,7 +1253,7 @@ fc_rspnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, rspnid->spn_len = (u8) strlen((char *)name); strncpy((char *)rspnid->spn, (char *)name, rspnid->spn_len); - return (sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1275,7 +1275,7 @@ fc_gid_ft_build(struct fchs_s *fchs, void *pyld, u32 s_id, gidft->domain_id = 0; gidft->area_id = 0; - return (sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1294,7 +1294,7 @@ fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, rpnid->port_id = port_id; rpnid->port_name = port_name; - return (sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1313,7 +1313,7 @@ fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, rnnid->port_id = port_id; rnnid->node_name = node_name; - return (sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1332,7 +1332,7 @@ fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, rcsid->port_id = port_id; rcsid->cos = cos; - return (sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1351,7 +1351,7 @@ fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, rptid->port_id = port_id; rptid->port_type = port_type; - return (sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s)); + return sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s); } u16 @@ -1368,7 +1368,7 @@ fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id) bfa_os_memset(ganxt, 0, sizeof(struct fcgs_ganxt_req_s)); ganxt->port_id = port_id; - return (sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s)); + return sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s); } /* @@ -1385,7 +1385,7 @@ fc_fdmi_reqhdr_build(struct fchs_s *fchs, void *pyld, u32 s_id, fc_gs_fchdr_build(fchs, d_id, s_id, 0); fc_gs_fdmi_cthdr_build(cthdr, s_id, cmd_code); - return (sizeof(struct ct_hdr_s)); + return sizeof(struct ct_hdr_s); } /* @@ -1425,7 +1425,7 @@ fc_gmal_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn) bfa_os_memset(gmal, 0, sizeof(fcgs_gmal_req_t)); gmal->wwn = wwn; - return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t)); + return sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t); } /* @@ -1445,5 +1445,5 @@ fc_gfn_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn) bfa_os_memset(gfn, 0, sizeof(fcgs_gfn_req_t)); gfn->wwn = wwn; - return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t)); + return sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t); } diff --git a/drivers/scsi/bfa/fcbuild.h b/drivers/scsi/bfa/fcbuild.h index 4d248424f7b3..8fa7f270ef7b 100644 --- a/drivers/scsi/bfa/fcbuild.h +++ b/drivers/scsi/bfa/fcbuild.h @@ -32,8 +32,8 @@ * Utility Macros/functions */ -#define fcif_sof_set(_ifhdr, _sof) (_ifhdr)->sof = FC_ ## _sof -#define fcif_eof_set(_ifhdr, _eof) (_ifhdr)->eof = FC_ ## _eof +#define fcif_sof_set(_ifhdr, _sof) ((_ifhdr)->sof = FC_ ## _sof) +#define fcif_eof_set(_ifhdr, _eof) ((_ifhdr)->eof = FC_ ## _eof) #define wwn_is_equal(_wwn1, _wwn2) \ (memcmp(&(_wwn1), &(_wwn2), sizeof(wwn_t)) == 0) @@ -49,7 +49,7 @@ static inline u32 fc_get_ctresp_pyld_len(u32 resp_len) { - return (resp_len - sizeof(struct ct_hdr_s)); + return resp_len - sizeof(struct ct_hdr_s); } /* diff --git a/drivers/scsi/bfa/fcpim.c b/drivers/scsi/bfa/fcpim.c index 8ce5d8934677..1f3c06efaa9e 100644 --- a/drivers/scsi/bfa/fcpim.c +++ b/drivers/scsi/bfa/fcpim.c @@ -286,11 +286,10 @@ bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline); bfa_fcb_itnim_offline(itnim->itnim_drv); bfa_itnim_offline(itnim->bfa_itnim); - if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE) { + if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE) bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT); - } else { + else bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE); - } break; case BFA_FCS_ITNIM_SM_DELETE: @@ -732,7 +731,7 @@ bfa_fcs_itnim_lookup(struct bfa_fcs_port_s *port, wwn_t rpwwn) return NULL; bfa_assert(rport->itnim != NULL); - return (rport->itnim); + return rport->itnim; } bfa_status_t diff --git a/drivers/scsi/bfa/fcs.h b/drivers/scsi/bfa/fcs.h index deee685e8478..8d08230e6295 100644 --- a/drivers/scsi/bfa/fcs.h +++ b/drivers/scsi/bfa/fcs.h @@ -23,7 +23,7 @@ #ifndef __FCS_H__ #define __FCS_H__ -#define __fcs_min_cfg(__fcs) (__fcs)->min_cfg +#define __fcs_min_cfg(__fcs) ((__fcs)->min_cfg) void bfa_fcs_modexit_comp(struct bfa_fcs_s *fcs); diff --git a/drivers/scsi/bfa/fdmi.c b/drivers/scsi/bfa/fdmi.c index b845eb272c78..df2a1e54e16b 100644 --- a/drivers/scsi/bfa/fdmi.c +++ b/drivers/scsi/bfa/fdmi.c @@ -72,9 +72,9 @@ static u16 bfa_fcs_port_fdmi_build_rpa_pyld( struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); static u16 bfa_fcs_port_fdmi_build_portattr_block( struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); -void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi, +static void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi, struct bfa_fcs_fdmi_hba_attr_s *hba_attr); -void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi, +static void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi, struct bfa_fcs_fdmi_port_attr_s *port_attr); /** * fcs_fdmi_sm FCS FDMI state machine @@ -1091,7 +1091,7 @@ bfa_fcs_port_fdmi_timeout(void *arg) bfa_sm_send_event(fdmi, FDMISM_EVENT_TIMEOUT); } -void +static void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi, struct bfa_fcs_fdmi_hba_attr_s *hba_attr) { @@ -1145,7 +1145,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi, } -void +static void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi, struct bfa_fcs_fdmi_port_attr_s *port_attr) { diff --git a/drivers/scsi/bfa/include/aen/bfa_aen.h b/drivers/scsi/bfa/include/aen/bfa_aen.h index da8cac093d3d..d9cbc2a783d4 100644 --- a/drivers/scsi/bfa/include/aen/bfa_aen.h +++ b/drivers/scsi/bfa/include/aen/bfa_aen.h @@ -54,7 +54,7 @@ bfa_aen_get_max_cfg_entry(void) static inline s32 bfa_aen_get_meminfo(void) { - return (sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry()); + return sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry(); } static inline s32 diff --git a/drivers/scsi/bfa/include/bfa.h b/drivers/scsi/bfa/include/bfa.h index 64c1412c5703..d4bc0d9fa42c 100644 --- a/drivers/scsi/bfa/include/bfa.h +++ b/drivers/scsi/bfa/include/bfa.h @@ -76,11 +76,11 @@ struct bfa_meminfo_s { struct bfa_mem_elem_s meminfo[BFA_MEM_TYPE_MAX]; }; #define bfa_meminfo_kva(_m) \ - (_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp + ((_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp) #define bfa_meminfo_dma_virt(_m) \ - (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp + ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp) #define bfa_meminfo_dma_phys(_m) \ - (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp + ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp) /** * Generic Scatter Gather Element used by driver @@ -100,7 +100,7 @@ struct bfa_sge_s { /* * bfa stats interfaces */ -#define bfa_stats(_mod, _stats) (_mod)->stats._stats ++ +#define bfa_stats(_mod, _stats) ((_mod)->stats._stats++) #define bfa_ioc_get_stats(__bfa, __ioc_stats) \ bfa_ioc_fetch_stats(&(__bfa)->ioc, __ioc_stats) @@ -136,7 +136,7 @@ void bfa_isr_enable(struct bfa_s *bfa); void bfa_isr_disable(struct bfa_s *bfa); void bfa_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap, u32 *num_vecs, u32 *max_vec_bit); -#define bfa_msix(__bfa, __vec) (__bfa)->msix.handler[__vec](__bfa, __vec) +#define bfa_msix(__bfa, __vec) ((__bfa)->msix.handler[__vec](__bfa, __vec)) void bfa_comp_deq(struct bfa_s *bfa, struct list_head *comp_q); void bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q); diff --git a/drivers/scsi/bfa/include/bfa_svc.h b/drivers/scsi/bfa/include/bfa_svc.h index 0c80b74f72ef..268d956bad89 100644 --- a/drivers/scsi/bfa/include/bfa_svc.h +++ b/drivers/scsi/bfa/include/bfa_svc.h @@ -34,10 +34,10 @@ struct bfa_fcxp_s; */ struct bfa_rport_info_s { u16 max_frmsz; /* max rcv pdu size */ - u32 pid : 24, /* remote port ID */ - lp_tag : 8; - u32 local_pid : 24, /* local port ID */ - cisc : 8; /* CIRO supported */ + u32 pid:24, /* remote port ID */ + lp_tag:8; + u32 local_pid:24, /* local port ID */ + cisc:8; /* CIRO supported */ u8 fc_class; /* supported FC classes. enum fc_cos */ u8 vf_en; /* virtual fabric enable */ u16 vf_id; /* virtual fabric ID */ diff --git a/drivers/scsi/bfa/include/bfi/bfi.h b/drivers/scsi/bfa/include/bfi/bfi.h index 6cadfe0d4ba1..7042c18e542d 100644 --- a/drivers/scsi/bfa/include/bfi/bfi.h +++ b/drivers/scsi/bfa/include/bfi/bfi.h @@ -93,13 +93,13 @@ union bfi_addr_u { */ struct bfi_sge_s { #ifdef __BIGENDIAN - u32 flags : 2, - rsvd : 2, - sg_len : 28; + u32 flags:2, + rsvd:2, + sg_len:28; #else - u32 sg_len : 28, - rsvd : 2, - flags : 2; + u32 sg_len:28, + rsvd:2, + flags:2; #endif union bfi_addr_u sga; }; diff --git a/drivers/scsi/bfa/include/bfi/bfi_ioc.h b/drivers/scsi/bfa/include/bfi/bfi_ioc.h index 026e9c06ae97..96ef05670659 100644 --- a/drivers/scsi/bfa/include/bfi/bfi_ioc.h +++ b/drivers/scsi/bfa/include/bfi/bfi_ioc.h @@ -142,7 +142,7 @@ enum { BFI_ADAPTER_UNSUPP = 0x400000, /* unknown adapter type */ }; -#define BFI_ADAPTER_GETP(__prop,__adap_prop) \ +#define BFI_ADAPTER_GETP(__prop, __adap_prop) \ (((__adap_prop) & BFI_ADAPTER_ ## __prop ## _MK) >> \ BFI_ADAPTER_ ## __prop ## _SH) #define BFI_ADAPTER_SETP(__prop, __val) \ diff --git a/drivers/scsi/bfa/include/bfi/bfi_lps.h b/drivers/scsi/bfa/include/bfi/bfi_lps.h index 414b0e30f6ef..c59d47badb4b 100644 --- a/drivers/scsi/bfa/include/bfi/bfi_lps.h +++ b/drivers/scsi/bfa/include/bfi/bfi_lps.h @@ -55,8 +55,8 @@ struct bfi_lps_login_rsp_s { u16 bb_credit; u8 f_port; u8 npiv_en; - u32 lp_pid : 24; - u32 auth_req : 8; + u32 lp_pid:24; + u32 auth_req:8; mac_t lp_mac; mac_t fcf_mac; u8 ext_status; diff --git a/drivers/scsi/bfa/include/bfi/bfi_rport.h b/drivers/scsi/bfa/include/bfi/bfi_rport.h index 3520f55f09d7..e1cd83b56ec6 100644 --- a/drivers/scsi/bfa/include/bfi/bfi_rport.h +++ b/drivers/scsi/bfa/include/bfi/bfi_rport.h @@ -38,10 +38,10 @@ struct bfi_rport_create_req_s { struct bfi_mhdr_s mh; /* common msg header */ u16 bfa_handle; /* host rport handle */ u16 max_frmsz; /* max rcv pdu size */ - u32 pid : 24, /* remote port ID */ - lp_tag : 8; /* local port tag */ - u32 local_pid : 24, /* local port ID */ - cisc : 8; + u32 pid:24, /* remote port ID */ + lp_tag:8; /* local port tag */ + u32 local_pid:24, /* local port ID */ + cisc:8; u8 fc_class; /* supported FC classes */ u8 vf_en; /* virtual fabric enable */ u16 vf_id; /* virtual fabric ID */ diff --git a/drivers/scsi/bfa/include/cs/bfa_checksum.h b/drivers/scsi/bfa/include/cs/bfa_checksum.h index af8c1d533ba8..650f8d0aaff9 100644 --- a/drivers/scsi/bfa/include/cs/bfa_checksum.h +++ b/drivers/scsi/bfa/include/cs/bfa_checksum.h @@ -31,7 +31,7 @@ bfa_checksum_u32(u32 *buf, int sz) for (i = 0; i < m; i++) sum ^= buf[i]; - return (sum); + return sum; } static inline u16 @@ -43,7 +43,7 @@ bfa_checksum_u16(u16 *buf, int sz) for (i = 0; i < m; i++) sum ^= buf[i]; - return (sum); + return sum; } static inline u8 @@ -55,6 +55,6 @@ bfa_checksum_u8(u8 *buf, int sz) for (i = 0; i < sz; i++) sum ^= buf[i]; - return (sum); + return sum; } #endif diff --git a/drivers/scsi/bfa/include/cs/bfa_sm.h b/drivers/scsi/bfa/include/cs/bfa_sm.h index 9877066680a6..b0a92baf6657 100644 --- a/drivers/scsi/bfa/include/cs/bfa_sm.h +++ b/drivers/scsi/bfa/include/cs/bfa_sm.h @@ -24,8 +24,8 @@ typedef void (*bfa_sm_t)(void *sm, int event); -#define bfa_sm_set_state(_sm, _state) (_sm)->sm = (bfa_sm_t)(_state) -#define bfa_sm_send_event(_sm, _event) (_sm)->sm((_sm), (_event)) +#define bfa_sm_set_state(_sm, _state) ((_sm)->sm = (bfa_sm_t)(_state)) +#define bfa_sm_send_event(_sm, _event) ((_sm)->sm((_sm), (_event))) #define bfa_sm_get_state(_sm) ((_sm)->sm) #define bfa_sm_cmp_state(_sm, _state) ((_sm)->sm == (bfa_sm_t)(_state)) @@ -62,7 +62,7 @@ typedef void (*bfa_fsm_t)(void *fsm, int event); } while (0) #define bfa_fsm_send_event(_fsm, _event) \ - (_fsm)->fsm((_fsm), (_event)) + ((_fsm)->fsm((_fsm), (_event))) #define bfa_fsm_cmp_state(_fsm, _state) \ ((_fsm)->fsm == (bfa_fsm_t)(_state)) diff --git a/drivers/scsi/bfa/include/cs/bfa_trc.h b/drivers/scsi/bfa/include/cs/bfa_trc.h index 3e743928c74c..310771c888e7 100644 --- a/drivers/scsi/bfa/include/cs/bfa_trc.h +++ b/drivers/scsi/bfa/include/cs/bfa_trc.h @@ -24,7 +24,7 @@ #endif #ifndef BFA_TRC_TS -#define BFA_TRC_TS(_trcm) ((_trcm)->ticks ++) +#define BFA_TRC_TS(_trcm) ((_trcm)->ticks++) #endif struct bfa_trc_s { diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pport.h b/drivers/scsi/bfa/include/defs/bfa_defs_pport.h index a000bc4e2d4a..bf320412ee24 100644 --- a/drivers/scsi/bfa/include/defs/bfa_defs_pport.h +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pport.h @@ -61,7 +61,7 @@ enum bfa_pport_speed { * Port operational type (in sync with SNIA port type). */ enum bfa_pport_type { - BFA_PPORT_TYPE_UNKNOWN = 1, /* port type is unkown */ + BFA_PPORT_TYPE_UNKNOWN = 1, /* port type is unknown */ BFA_PPORT_TYPE_TRUNKED = 2, /* Trunked mode */ BFA_PPORT_TYPE_NPORT = 5, /* P2P with switched fabric */ BFA_PPORT_TYPE_NLPORT = 6, /* public loop */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h b/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h index 31881d218515..ade763dbc8ce 100644 --- a/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h +++ b/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h @@ -25,7 +25,7 @@ * Temperature sensor status values */ enum bfa_tsensor_status { - BFA_TSENSOR_STATUS_UNKNOWN = 1, /* unkown status */ + BFA_TSENSOR_STATUS_UNKNOWN = 1, /* unknown status */ BFA_TSENSOR_STATUS_FAULTY = 2, /* sensor is faulty */ BFA_TSENSOR_STATUS_BELOW_MIN = 3, /* temperature below mininum */ BFA_TSENSOR_STATUS_NOMINAL = 4, /* normal temperature */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h index 4ffd2242d3de..08b79d5e46f3 100644 --- a/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h @@ -75,7 +75,7 @@ struct bfa_fcs_fabric_s { */ }; -#define bfa_fcs_fabric_npiv_capable(__f) (__f)->is_npiv +#define bfa_fcs_fabric_npiv_capable(__f) ((__f)->is_npiv) #define bfa_fcs_fabric_is_switched(__f) \ ((__f)->fab_type == BFA_FCS_FABRIC_SWITCHED) diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h index b85cba884b96..967ceb0eb074 100644 --- a/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h @@ -125,12 +125,12 @@ union bfa_fcs_port_topo_u { struct bfa_fcs_port_s { struct list_head qe; /* used by port/vport */ bfa_sm_t sm; /* state machine */ - struct bfa_fcs_fabric_s *fabric; /* parent fabric */ - struct bfa_port_cfg_s port_cfg; /* port configuration */ + struct bfa_fcs_fabric_s *fabric;/* parent fabric */ + struct bfa_port_cfg_s port_cfg;/* port configuration */ struct bfa_timer_s link_timer; /* timer for link offline */ - u32 pid : 24; /* FC address */ - u8 lp_tag; /* lport tag */ - u16 num_rports; /* Num of r-ports */ + u32 pid:24; /* FC address */ + u8 lp_tag; /* lport tag */ + u16 num_rports; /* Num of r-ports */ struct list_head rport_q; /* queue of discovered r-ports */ struct bfa_fcs_s *fcs; /* FCS instance */ union bfa_fcs_port_topo_u port_topo; /* fabric/loop/n2n details */ @@ -188,13 +188,14 @@ bfa_fcs_port_get_drvport(struct bfa_fcs_port_s *port) } -#define bfa_fcs_port_get_opertype(_lport) (_lport)->fabric->oper_type +#define bfa_fcs_port_get_opertype(_lport) ((_lport)->fabric->oper_type) -#define bfa_fcs_port_get_fabric_name(_lport) (_lport)->fabric->fabric_name +#define bfa_fcs_port_get_fabric_name(_lport) ((_lport)->fabric->fabric_name) -#define bfa_fcs_port_get_fabric_ipaddr(_lport) (_lport)->fabric->fabric_ip_addr +#define bfa_fcs_port_get_fabric_ipaddr(_lport) \ + ((_lport)->fabric->fabric_ip_addr) /** * bfa fcs port public functions diff --git a/drivers/scsi/bfa/include/protocol/ct.h b/drivers/scsi/bfa/include/protocol/ct.h index c59d6630b070..b82540a230c4 100644 --- a/drivers/scsi/bfa/include/protocol/ct.h +++ b/drivers/scsi/bfa/include/protocol/ct.h @@ -82,7 +82,7 @@ enum { }; /* - * defintions for CT reason code + * definitions for CT reason code */ enum { CT_RSN_INV_CMD = 0x01, @@ -129,7 +129,7 @@ enum { }; /* - * defintions for the explanation code for all servers + * definitions for the explanation code for all servers */ enum { CT_EXP_AUTH_EXCEPTION = 0xF1, @@ -193,11 +193,11 @@ struct fcgs_rftid_req_s { #define FC_GS_FCP_FC4_FEATURE_TARGET 0x01 struct fcgs_rffid_req_s{ - u32 rsvd :8; - u32 dap :24; /* port identifier */ - u32 rsvd1 :16; - u32 fc4ftr_bits :8; /* fc4 feature bits */ - u32 fc4_type :8; /* corresponding FC4 Type */ + u32 rsvd:8; + u32 dap:24; /* port identifier */ + u32 rsvd1:16; + u32 fc4ftr_bits:8; /* fc4 feature bits */ + u32 fc4_type:8; /* corresponding FC4 Type */ }; /** diff --git a/drivers/scsi/bfa/include/protocol/fc.h b/drivers/scsi/bfa/include/protocol/fc.h index 3e39ba58cfb5..14969eecf6a9 100644 --- a/drivers/scsi/bfa/include/protocol/fc.h +++ b/drivers/scsi/bfa/include/protocol/fc.h @@ -486,14 +486,14 @@ struct fc_rsi_s { * see FC-PH-X table 113 & 115 for explanation also FCP table 8 */ struct fc_prli_params_s{ - u32 reserved: 16; + u32 reserved:16; #ifdef __BIGENDIAN - u32 reserved1: 5; - u32 rec_support : 1; - u32 task_retry_id : 1; - u32 retry : 1; + u32 reserved1:5; + u32 rec_support:1; + u32 task_retry_id:1; + u32 retry:1; - u32 confirm : 1; + u32 confirm:1; u32 doverlay:1; u32 initiator:1; u32 target:1; @@ -502,10 +502,10 @@ struct fc_prli_params_s{ u32 rxrdisab:1; u32 wxrdisab:1; #else - u32 retry : 1; - u32 task_retry_id : 1; - u32 rec_support : 1; - u32 reserved1: 5; + u32 retry:1; + u32 task_retry_id:1; + u32 rec_support:1; + u32 reserved1:5; u32 wxrdisab:1; u32 rxrdisab:1; @@ -514,7 +514,7 @@ struct fc_prli_params_s{ u32 target:1; u32 initiator:1; u32 doverlay:1; - u32 confirm : 1; + u32 confirm:1; #endif }; diff --git a/drivers/scsi/bfa/loop.c b/drivers/scsi/bfa/loop.c index a418dedebe9e..f7c7f4f3c640 100644 --- a/drivers/scsi/bfa/loop.c +++ b/drivers/scsi/bfa/loop.c @@ -58,49 +58,16 @@ static const u8 port_loop_alpa_map[] = { /* * Local Functions */ -bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, - u8 alpa); - -void bfa_fcs_port_loop_plogi_response(void *fcsarg, - struct bfa_fcxp_s *fcxp, - void *cbarg, - bfa_status_t req_status, - u32 rsp_len, - u32 resid_len, - struct fchs_s *rsp_fchs); - -bfa_status_t bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, - u8 alpa); - -void bfa_fcs_port_loop_adisc_response(void *fcsarg, - struct bfa_fcxp_s *fcxp, - void *cbarg, - bfa_status_t req_status, - u32 rsp_len, - u32 resid_len, - struct fchs_s *rsp_fchs); - -bfa_status_t bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, - u8 alpa); - -void bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, - struct bfa_fcxp_s *fcxp, - void *cbarg, - bfa_status_t req_status, - u32 rsp_len, - u32 resid_len, - struct fchs_s *rsp_fchs); - -bfa_status_t bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, - u8 alpa); - -void bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, - struct bfa_fcxp_s *fcxp, - void *cbarg, - bfa_status_t req_status, - u32 rsp_len, - u32 resid_len, - struct fchs_s *rsp_fchs); +static bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, + u8 alpa); + +static void bfa_fcs_port_loop_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); /** * Called by port to initializar in provate LOOP topology. */ @@ -179,7 +146,7 @@ bfa_fcs_port_loop_lip(struct bfa_fcs_port_s *port) /** * Local Functions. */ -bfa_status_t +static bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa) { struct fchs_s fchs; @@ -208,7 +175,7 @@ bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa) /** * Called by fcxp to notify the Plogi response */ -void +static void bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, bfa_status_t req_status, u32 rsp_len, u32 resid_len, @@ -244,179 +211,3 @@ bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, bfa_assert(0); } } - -bfa_status_t -bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, u8 alpa) -{ - struct fchs_s fchs; - struct bfa_fcxp_s *fcxp; - int len; - - bfa_trc(port->fcs, alpa); - - fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, - NULL); - bfa_assert(fcxp); - - len = fc_plogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, - bfa_fcs_port_get_fcid(port), 0, - port->port_cfg.pwwn, port->port_cfg.nwwn, - bfa_pport_get_maxfrsize(port->fcs->bfa)); - - bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, - FC_CLASS_3, len, &fchs, - bfa_fcs_port_loop_plogi_acc_response, - (void *)port, FC_MAX_PDUSZ, 0); /* No response - * expected - */ - - return BFA_STATUS_OK; -} - -/* - * Plogi Acc Response - * We donot do any processing here. - */ -void -bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, - void *cbarg, bfa_status_t req_status, - u32 rsp_len, u32 resid_len, - struct fchs_s *rsp_fchs) -{ - - struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; - - bfa_trc(port->fcs, port->pid); - - /* - * Sanity Checks - */ - if (req_status != BFA_STATUS_OK) { - bfa_trc(port->fcs, req_status); - return; - } -} - -bfa_status_t -bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, u8 alpa) -{ - struct fchs_s fchs; - struct bfa_fcxp_s *fcxp; - int len; - - bfa_trc(port->fcs, alpa); - - fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, - NULL); - bfa_assert(fcxp); - - len = fc_adisc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, - bfa_fcs_port_get_fcid(port), 0, - port->port_cfg.pwwn, port->port_cfg.nwwn); - - bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, - FC_CLASS_3, len, &fchs, - bfa_fcs_port_loop_adisc_response, (void *)port, - FC_MAX_PDUSZ, FC_RA_TOV); - - return BFA_STATUS_OK; -} - -/** - * Called by fcxp to notify the ADISC response - */ -void -bfa_fcs_port_loop_adisc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, - void *cbarg, bfa_status_t req_status, - u32 rsp_len, u32 resid_len, - struct fchs_s *rsp_fchs) -{ - struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; - struct bfa_fcs_rport_s *rport; - struct fc_adisc_s *adisc_resp; - struct fc_els_cmd_s *els_cmd; - u32 pid = rsp_fchs->s_id; - - bfa_trc(port->fcs, req_status); - - /* - * Sanity Checks - */ - if (req_status != BFA_STATUS_OK) { - /* - * TBD : we may need to retry certain requests - */ - bfa_fcxp_free(fcxp); - return; - } - - els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); - adisc_resp = (struct fc_adisc_s *) els_cmd; - - if (els_cmd->els_code == FC_ELS_ACC) { - } else { - bfa_trc(port->fcs, adisc_resp->els_cmd.els_code); - - /* - * TBD: we may need to check for reject codes and retry - */ - rport = bfa_fcs_port_get_rport_by_pid(port, pid); - if (rport) { - list_del(&rport->qe); - bfa_fcs_rport_delete(rport); - } - - } - return; -} - -bfa_status_t -bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, u8 alpa) -{ - struct fchs_s fchs; - struct bfa_fcxp_s *fcxp; - int len; - - bfa_trc(port->fcs, alpa); - - fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, - NULL); - bfa_assert(fcxp); - - len = fc_adisc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, - bfa_fcs_port_get_fcid(port), 0, - port->port_cfg.pwwn, port->port_cfg.nwwn); - - bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, - FC_CLASS_3, len, &fchs, - bfa_fcs_port_loop_adisc_acc_response, - (void *)port, FC_MAX_PDUSZ, 0); /* no reponse - * expected - */ - - return BFA_STATUS_OK; -} - -/* - * Adisc Acc Response - * We donot do any processing here. - */ -void -bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, - void *cbarg, bfa_status_t req_status, - u32 rsp_len, u32 resid_len, - struct fchs_s *rsp_fchs) -{ - - struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; - - bfa_trc(port->fcs, port->pid); - - /* - * Sanity Checks - */ - if (req_status != BFA_STATUS_OK) { - bfa_trc(port->fcs, req_status); - return; - } -} diff --git a/drivers/scsi/bfa/lport_api.c b/drivers/scsi/bfa/lport_api.c index 8f51a83f1834..1e06792cd4c2 100644 --- a/drivers/scsi/bfa/lport_api.c +++ b/drivers/scsi/bfa/lport_api.c @@ -43,7 +43,7 @@ bfa_fcs_cfg_base_port(struct bfa_fcs_s *fcs, struct bfa_port_cfg_s *port_cfg) struct bfa_fcs_port_s * bfa_fcs_get_base_port(struct bfa_fcs_s *fcs) { - return (&fcs->fabric.bport); + return &fcs->fabric.bport; } wwn_t @@ -88,11 +88,10 @@ bfa_fcs_port_get_rport(struct bfa_fcs_port_s *port, wwn_t wwn, int index, } bfa_trc(fcs, i); - if (rport) { + if (rport) return rport->pwwn; - } else { + else return (wwn_t) 0; - } } void @@ -198,17 +197,17 @@ bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn) vf = bfa_fcs_vf_lookup(fcs, vf_id); if (vf == NULL) { bfa_trc(fcs, vf_id); - return (NULL); + return NULL; } if (!lpwwn || (vf->bport.port_cfg.pwwn == lpwwn)) - return (&vf->bport); + return &vf->bport; vport = bfa_fcs_fabric_vport_lookup(vf, lpwwn); if (vport) - return (&vport->lport); + return &vport->lport; - return (NULL); + return NULL; } /* diff --git a/drivers/scsi/bfa/ns.c b/drivers/scsi/bfa/ns.c index 59fea99d67a4..2f8b880060bb 100644 --- a/drivers/scsi/bfa/ns.c +++ b/drivers/scsi/bfa/ns.c @@ -932,11 +932,10 @@ bfa_fcs_port_ns_send_rff_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) } ns->fcxp = fcxp; - if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) { + if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) fc4_ftrs = FC_GS_FCP_FC4_FEATURE_INITIATOR; - } else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) { + else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) fc4_ftrs = FC_GS_FCP_FC4_FEATURE_TARGET; - } len = fc_rffid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), bfa_fcs_port_get_fcid(port), 0, FC_TYPE_FCP, diff --git a/drivers/scsi/bfa/plog.c b/drivers/scsi/bfa/plog.c index 86af818d17bb..fcb8864d3276 100644 --- a/drivers/scsi/bfa/plog.c +++ b/drivers/scsi/bfa/plog.c @@ -180,5 +180,5 @@ bfa_plog_disable(struct bfa_plog_s *plog) bfa_boolean_t bfa_plog_get_setting(struct bfa_plog_s *plog) { - return((bfa_boolean_t)plog->plog_enabled); + return (bfa_boolean_t)plog->plog_enabled; } diff --git a/drivers/scsi/bfa/rport_ftrs.c b/drivers/scsi/bfa/rport_ftrs.c index 8a1f59d596c1..e1932c885ac2 100644 --- a/drivers/scsi/bfa/rport_ftrs.c +++ b/drivers/scsi/bfa/rport_ftrs.c @@ -79,7 +79,7 @@ bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) bfa_trc(rport->fcs, event); switch (event) { - case RPFSM_EVENT_RPORT_ONLINE : + case RPFSM_EVENT_RPORT_ONLINE: if (!BFA_FCS_PID_IS_WKA(rport->pid)) { bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); rpf->rpsc_retries = 0; @@ -87,7 +87,7 @@ bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) break; }; - case RPFSM_EVENT_RPORT_OFFLINE : + case RPFSM_EVENT_RPORT_OFFLINE: break; default: @@ -107,7 +107,7 @@ bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc); break; - case RPFSM_EVENT_RPORT_OFFLINE : + case RPFSM_EVENT_RPORT_OFFLINE: bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rpf->fcxp_wqe); rpf->rpsc_retries = 0; @@ -130,11 +130,10 @@ bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) case RPFSM_EVENT_RPSC_COMP: bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); /* Update speed info in f/w via BFA */ - if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN) { + if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN) bfa_rport_speed(rport->bfa_rport, rpf->rpsc_speed); - } else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN) { + else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN) bfa_rport_speed(rport->bfa_rport, rpf->assigned_speed); - } break; case RPFSM_EVENT_RPSC_FAIL: @@ -154,7 +153,7 @@ bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) } break; - case RPFSM_EVENT_RPORT_OFFLINE : + case RPFSM_EVENT_RPORT_OFFLINE: bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); bfa_fcxp_discard(rpf->fcxp); rpf->rpsc_retries = 0; @@ -174,13 +173,13 @@ bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) bfa_trc(rport->fcs, event); switch (event) { - case RPFSM_EVENT_TIMEOUT : + case RPFSM_EVENT_TIMEOUT: /* re-send the RPSC */ bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); bfa_fcs_rpf_send_rpsc2(rpf, NULL); break; - case RPFSM_EVENT_RPORT_OFFLINE : + case RPFSM_EVENT_RPORT_OFFLINE: bfa_timer_stop(&rpf->timer); bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); rpf->rpsc_retries = 0; @@ -201,7 +200,7 @@ bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) bfa_trc(rport->fcs, event); switch (event) { - case RPFSM_EVENT_RPORT_OFFLINE : + case RPFSM_EVENT_RPORT_OFFLINE: bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); rpf->rpsc_retries = 0; break; @@ -221,12 +220,12 @@ bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) bfa_trc(rport->fcs, event); switch (event) { - case RPFSM_EVENT_RPORT_ONLINE : + case RPFSM_EVENT_RPORT_ONLINE: bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); bfa_fcs_rpf_send_rpsc2(rpf, NULL); break; - case RPFSM_EVENT_RPORT_OFFLINE : + case RPFSM_EVENT_RPORT_OFFLINE: break; default: @@ -366,10 +365,9 @@ bfa_fcs_rpf_rpsc2_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, bfa_trc(rport->fcs, ls_rjt->reason_code); bfa_trc(rport->fcs, ls_rjt->reason_code_expl); rport->stats.rpsc_rejects++; - if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) { + if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_FAIL); - } else { + else bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); - } } } diff --git a/drivers/scsi/bfa/vfapi.c b/drivers/scsi/bfa/vfapi.c index 31d81fe2fc48..391a4790bebd 100644 --- a/drivers/scsi/bfa/vfapi.c +++ b/drivers/scsi/bfa/vfapi.c @@ -189,7 +189,7 @@ bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id) { bfa_trc(fcs, vf_id); if (vf_id == FC_VF_ID_NULL) - return (&fcs->fabric); + return &fcs->fabric; /** * @todo vf support diff --git a/drivers/scsi/bfa/vport.c b/drivers/scsi/bfa/vport.c index c10af06c5714..e90f1e38c32d 100644 --- a/drivers/scsi/bfa/vport.c +++ b/drivers/scsi/bfa/vport.c @@ -31,13 +31,13 @@ BFA_TRC_FILE(FCS, VPORT); -#define __vport_fcs(__vp) (__vp)->lport.fcs -#define __vport_pwwn(__vp) (__vp)->lport.port_cfg.pwwn -#define __vport_nwwn(__vp) (__vp)->lport.port_cfg.nwwn -#define __vport_bfa(__vp) (__vp)->lport.fcs->bfa -#define __vport_fcid(__vp) (__vp)->lport.pid -#define __vport_fabric(__vp) (__vp)->lport.fabric -#define __vport_vfid(__vp) (__vp)->lport.fabric->vf_id +#define __vport_fcs(__vp) ((__vp)->lport.fcs) +#define __vport_pwwn(__vp) ((__vp)->lport.port_cfg.pwwn) +#define __vport_nwwn(__vp) ((__vp)->lport.port_cfg.nwwn) +#define __vport_bfa(__vp) ((__vp)->lport.fcs->bfa) +#define __vport_fcid(__vp) ((__vp)->lport.pid) +#define __vport_fabric(__vp) ((__vp)->lport.fabric) +#define __vport_vfid(__vp) ((__vp)->lport.fabric->vf_id) #define BFA_FCS_VPORT_MAX_RETRIES 5 /* @@ -641,9 +641,9 @@ bfa_fcs_vport_get_max(struct bfa_fcs_s *fcs) bfa_get_attr(fcs->bfa, &ioc_attr); if (ioc_attr.pci_attr.device_id == BFA_PCI_DEVICE_ID_CT) - return (BFA_FCS_MAX_VPORTS_SUPP_CT); + return BFA_FCS_MAX_VPORTS_SUPP_CT; else - return (BFA_FCS_MAX_VPORTS_SUPP_CB); + return BFA_FCS_MAX_VPORTS_SUPP_CB; } @@ -675,7 +675,7 @@ bfa_fcs_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs, struct bfad_vport_s *vport_drv) { if (vport_cfg->pwwn == 0) - return (BFA_STATUS_INVALID_WWN); + return BFA_STATUS_INVALID_WWN; if (bfa_fcs_port_get_pwwn(&fcs->fabric.bport) == vport_cfg->pwwn) return BFA_STATUS_VPORT_WWN_BP; diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h index 5edde1a8c04d..2b973f3c2eb2 100644 --- a/drivers/scsi/bnx2i/bnx2i.h +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -232,7 +232,6 @@ struct bnx2i_conn { struct iscsi_cls_conn *cls_conn; struct bnx2i_hba *hba; struct completion cmd_cleanup_cmpl; - int is_bound; u32 iscsi_conn_cid; #define BNX2I_CID_RESERVED 0x5AFF diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index cafb888c2376..132898c88d5e 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -1161,9 +1161,6 @@ static int bnx2i_task_xmit(struct iscsi_task *task) struct bnx2i_cmd *cmd = task->dd_data; struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr; - if (!bnx2i_conn->is_bound) - return -ENOTCONN; - /* * If there is no scsi_cmnd this must be a mgmt task */ @@ -1371,7 +1368,6 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session, bnx2i_conn->ep = bnx2i_ep; bnx2i_conn->iscsi_conn_cid = bnx2i_ep->ep_iscsi_cid; bnx2i_conn->fw_cid = bnx2i_ep->ep_cid; - bnx2i_conn->is_bound = 1; ret_code = bnx2i_bind_conn_to_iscsi_cid(hba, bnx2i_conn, bnx2i_ep->ep_iscsi_cid); @@ -1883,7 +1879,7 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep) bnx2i_ep = ep->dd_data; - /* driver should not attempt connection cleanup untill TCP_CONNECT + /* driver should not attempt connection cleanup until TCP_CONNECT * completes either successfully or fails. Timeout is 9-secs, so * wait for it to complete */ @@ -1896,9 +1892,7 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep) conn = bnx2i_conn->cls_conn->dd_data; session = conn->session; - spin_lock_bh(&session->lock); - bnx2i_conn->is_bound = 0; - spin_unlock_bh(&session->lock); + iscsi_suspend_queue(conn); } hba = bnx2i_ep->hba; @@ -2034,7 +2028,7 @@ struct iscsi_transport bnx2i_iscsi_transport = { ISCSI_USERNAME | ISCSI_PASSWORD | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | - ISCSI_LU_RESET_TMO | + ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO | ISCSI_PING_TMO | ISCSI_RECV_TMO | ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_NETDEV_NAME, diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index 63abb06c4edb..9129bcf117cf 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -141,6 +141,7 @@ static const struct value_name_pair serv_out12_arr[] = { static const struct value_name_pair serv_in16_arr[] = { {0x10, "Read capacity(16)"}, {0x11, "Read long(16)"}, + {0x12, "Get LBA status"}, }; #define SERV_IN16_SZ ARRAY_SIZE(serv_in16_arr) diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c index 2631bddd255e..969c83162cc4 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c +++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c @@ -937,7 +937,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = { ISCSI_USERNAME | ISCSI_PASSWORD | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | - ISCSI_LU_RESET_TMO | + ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO | ISCSI_PING_TMO | ISCSI_RECV_TMO | ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index 075e2397273c..6c59c02c1ed9 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -1509,7 +1509,7 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb, * Try anyway? * * We could, BUT: Sometimes the TRM_S1040 misses to produce a Selection - * Timeout, a Disconnect or a Reselction IRQ, so we would be screwed! + * Timeout, a Disconnect or a Reselection IRQ, so we would be screwed! * (This is likely to be a bug in the hardware. Obviously, most people * only have one initiator per SCSI bus.) * Instead let this fail and have the timer make sure the command is diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 3ee1cbc89479..e19a1a55270c 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -226,7 +226,7 @@ store_dh_state(struct device *dev, struct device_attribute *attr, * Activate a device handler */ if (scsi_dh->activate) - err = scsi_dh->activate(sdev); + err = scsi_dh->activate(sdev, NULL, NULL); else err = 0; } @@ -304,18 +304,15 @@ static int scsi_dh_notifier(struct notifier_block *nb, sdev = to_scsi_device(dev); if (action == BUS_NOTIFY_ADD_DEVICE) { + err = device_create_file(dev, &scsi_dh_state_attr); + /* don't care about err */ devinfo = device_handler_match(NULL, sdev); - if (!devinfo) - goto out; - - err = scsi_dh_handler_attach(sdev, devinfo); - if (!err) - err = device_create_file(dev, &scsi_dh_state_attr); + if (devinfo) + err = scsi_dh_handler_attach(sdev, devinfo); } else if (action == BUS_NOTIFY_DEL_DEVICE) { device_remove_file(dev, &scsi_dh_state_attr); scsi_dh_handler_detach(sdev, NULL); } -out: return err; } @@ -423,10 +420,17 @@ EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); /* * scsi_dh_activate - activate the path associated with the scsi_device * corresponding to the given request queue. - * @q - Request queue that is associated with the scsi_device to be - * activated. + * Returns immediately without waiting for activation to be completed. + * @q - Request queue that is associated with the scsi_device to be + * activated. + * @fn - Function to be called upon completion of the activation. + * Function fn is called with data (below) and the error code. + * Function fn may be called from the same calling context. So, + * do not hold the lock in the caller which may be needed in fn. + * @data - data passed to the function fn upon completion. + * */ -int scsi_dh_activate(struct request_queue *q) +int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) { int err = 0; unsigned long flags; @@ -445,7 +449,7 @@ int scsi_dh_activate(struct request_queue *q) return err; if (scsi_dh->activate) - err = scsi_dh->activate(sdev); + err = scsi_dh->activate(sdev, fn, data); put_device(&sdev->sdev_gendev); return err; } diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index b5cdefaf2608..4f0d0138f48b 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -60,11 +60,17 @@ struct alua_dh_data { int bufflen; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int senselen; + struct scsi_device *sdev; + activate_complete callback_fn; + void *callback_data; }; #define ALUA_POLICY_SWITCH_CURRENT 0 #define ALUA_POLICY_SWITCH_ALL 1 +static char print_alua_state(int); +static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *); + static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) { struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; @@ -231,18 +237,71 @@ done: } /* + * alua_stpg - Evaluate SET TARGET GROUP STATES + * @sdev: the device to be evaluated + * @state: the new target group state + * + * Send a SET TARGET GROUP STATES command to the device. + * We only have to test here if we should resubmit the command; + * any other error is assumed as a failure. + */ +static void stpg_endio(struct request *req, int error) +{ + struct alua_dh_data *h = req->end_io_data; + struct scsi_sense_hdr sense_hdr; + unsigned err = SCSI_DH_IO; + + if (error || host_byte(req->errors) != DID_OK || + msg_byte(req->errors) != COMMAND_COMPLETE) + goto done; + + if (err == SCSI_DH_IO && h->senselen > 0) { + err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, + &sense_hdr); + if (!err) { + err = SCSI_DH_IO; + goto done; + } + err = alua_check_sense(h->sdev, &sense_hdr); + if (err == ADD_TO_MLQUEUE) { + err = SCSI_DH_RETRY; + goto done; + } + sdev_printk(KERN_INFO, h->sdev, + "%s: stpg sense code: %02x/%02x/%02x\n", + ALUA_DH_NAME, sense_hdr.sense_key, + sense_hdr.asc, sense_hdr.ascq); + err = SCSI_DH_IO; + } + if (err == SCSI_DH_OK) { + h->state = TPGS_STATE_OPTIMIZED; + sdev_printk(KERN_INFO, h->sdev, + "%s: port group %02x switched to state %c\n", + ALUA_DH_NAME, h->group_id, + print_alua_state(h->state)); + } +done: + blk_put_request(req); + if (h->callback_fn) { + h->callback_fn(h->callback_data, err); + h->callback_fn = h->callback_data = NULL; + } + return; +} + +/* * submit_stpg - Issue a SET TARGET GROUP STATES command - * @sdev: sdev the command should be sent to * * Currently we're only setting the current target port group state * to 'active/optimized' and let the array firmware figure out * the states of the remaining groups. */ -static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) +static unsigned submit_stpg(struct alua_dh_data *h) { struct request *rq; int err = SCSI_DH_RES_TEMP_UNAVAIL; int stpg_len = 8; + struct scsi_device *sdev = h->sdev; /* Prepare the data buffer */ memset(h->buff, 0, stpg_len); @@ -252,7 +311,7 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) rq = get_alua_req(sdev, h->buff, stpg_len, WRITE); if (!rq) - goto done; + return SCSI_DH_RES_TEMP_UNAVAIL; /* Prepare the command. */ rq->cmd[0] = MAINTENANCE_OUT; @@ -266,17 +325,9 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) rq->sense = h->sense; memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); rq->sense_len = h->senselen = 0; + rq->end_io_data = h; - err = blk_execute_rq(rq->q, NULL, rq, 1); - if (err == -EIO) { - sdev_printk(KERN_INFO, sdev, - "%s: stpg failed with %x\n", - ALUA_DH_NAME, rq->errors); - h->senselen = rq->sense_len; - err = SCSI_DH_IO; - } - blk_put_request(rq); -done: + blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio); return err; } @@ -477,50 +528,6 @@ static int alua_check_sense(struct scsi_device *sdev, } /* - * alua_stpg - Evaluate SET TARGET GROUP STATES - * @sdev: the device to be evaluated - * @state: the new target group state - * - * Send a SET TARGET GROUP STATES command to the device. - * We only have to test here if we should resubmit the command; - * any other error is assumed as a failure. - */ -static int alua_stpg(struct scsi_device *sdev, int state, - struct alua_dh_data *h) -{ - struct scsi_sense_hdr sense_hdr; - unsigned err; - int retry = ALUA_FAILOVER_RETRIES; - - retry: - err = submit_stpg(sdev, h); - if (err == SCSI_DH_IO && h->senselen > 0) { - err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, - &sense_hdr); - if (!err) - return SCSI_DH_IO; - err = alua_check_sense(sdev, &sense_hdr); - if (retry > 0 && err == ADD_TO_MLQUEUE) { - retry--; - goto retry; - } - sdev_printk(KERN_INFO, sdev, - "%s: stpg sense code: %02x/%02x/%02x\n", - ALUA_DH_NAME, sense_hdr.sense_key, - sense_hdr.asc, sense_hdr.ascq); - err = SCSI_DH_IO; - } - if (err == SCSI_DH_OK) { - h->state = state; - sdev_printk(KERN_INFO, sdev, - "%s: port group %02x switched to state %c\n", - ALUA_DH_NAME, h->group_id, - print_alua_state(h->state) ); - } - return err; -} - -/* * alua_rtpg - Evaluate REPORT TARGET GROUP STATES * @sdev: the device to be evaluated. * @@ -652,7 +659,8 @@ out: * based on a certain policy. But until we actually encounter them it * should be okay. */ -static int alua_activate(struct scsi_device *sdev) +static int alua_activate(struct scsi_device *sdev, + activate_complete fn, void *data) { struct alua_dh_data *h = get_alua_data(sdev); int err = SCSI_DH_OK; @@ -663,11 +671,19 @@ static int alua_activate(struct scsi_device *sdev) goto out; } - if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) - err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h); + if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) { + h->callback_fn = fn; + h->callback_data = data; + err = submit_stpg(h); + if (err == SCSI_DH_OK) + return 0; + h->callback_fn = h->callback_data = NULL; + } out: - return err; + if (fn) + fn(data, err); + return 0; } /* @@ -745,6 +761,7 @@ static int alua_bus_attach(struct scsi_device *sdev) h->rel_port = -1; h->buff = h->inq; h->bufflen = ALUA_INQUIRY_SIZE; + h->sdev = sdev; err = alua_initialize(sdev, h); if (err != SCSI_DH_OK) diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 0cffe84976fe..61966750bd60 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -528,7 +528,8 @@ retry: return err; } -static int clariion_activate(struct scsi_device *sdev) +static int clariion_activate(struct scsi_device *sdev, + activate_complete fn, void *data) { struct clariion_dh_data *csdev = get_clariion_data(sdev); int result; @@ -559,7 +560,9 @@ done: csdev->port, lun_state[csdev->lun_state], csdev->default_sp + 'A'); - return result; + if (fn) + fn(data, result); + return 0; } /* * params - parameters in the following format diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index f7da7530875e..857fdd6032b2 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -39,8 +39,14 @@ struct hp_sw_dh_data { unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int path_state; int retries; + int retry_cnt; + struct scsi_device *sdev; + activate_complete callback_fn; + void *callback_data; }; +static int hp_sw_start_stop(struct hp_sw_dh_data *); + static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) { struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; @@ -191,19 +197,53 @@ static int start_done(struct scsi_device *sdev, unsigned char *sense) return rc; } +static void start_stop_endio(struct request *req, int error) +{ + struct hp_sw_dh_data *h = req->end_io_data; + unsigned err = SCSI_DH_OK; + + if (error || host_byte(req->errors) != DID_OK || + msg_byte(req->errors) != COMMAND_COMPLETE) { + sdev_printk(KERN_WARNING, h->sdev, + "%s: sending start_stop_unit failed with %x\n", + HP_SW_NAME, req->errors); + err = SCSI_DH_IO; + goto done; + } + + if (req->sense_len > 0) { + err = start_done(h->sdev, h->sense); + if (err == SCSI_DH_RETRY) { + err = SCSI_DH_IO; + if (--h->retry_cnt) { + blk_put_request(req); + err = hp_sw_start_stop(h); + if (err == SCSI_DH_OK) + return; + } + } + } +done: + blk_put_request(req); + if (h->callback_fn) { + h->callback_fn(h->callback_data, err); + h->callback_fn = h->callback_data = NULL; + } + return; + +} + /* * hp_sw_start_stop - Send START STOP UNIT command * @sdev: sdev command should be sent to * * Sending START STOP UNIT activates the SP. */ -static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h) +static int hp_sw_start_stop(struct hp_sw_dh_data *h) { struct request *req; - int ret, retry; -retry: - req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO); + req = blk_get_request(h->sdev->request_queue, WRITE, GFP_ATOMIC); if (!req) return SCSI_DH_RES_TEMP_UNAVAIL; @@ -217,32 +257,10 @@ retry: req->sense = h->sense; memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); req->sense_len = 0; - retry = h->retries; - - ret = blk_execute_rq(req->q, NULL, req, 1); - if (ret == -EIO) { - if (req->sense_len > 0) { - ret = start_done(sdev, h->sense); - } else { - sdev_printk(KERN_WARNING, sdev, - "%s: sending start_stop_unit failed with %x\n", - HP_SW_NAME, req->errors); - ret = SCSI_DH_IO; - } - } else - ret = SCSI_DH_OK; + req->end_io_data = h; - if (ret == SCSI_DH_RETRY) { - if (--retry) { - blk_put_request(req); - goto retry; - } - ret = SCSI_DH_IO; - } - - blk_put_request(req); - - return ret; + blk_execute_rq_nowait(req->q, NULL, req, 1, start_stop_endio); + return SCSI_DH_OK; } static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) @@ -268,7 +286,8 @@ static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) * activate the passive path (and deactivate the * previously active one). */ -static int hp_sw_activate(struct scsi_device *sdev) +static int hp_sw_activate(struct scsi_device *sdev, + activate_complete fn, void *data) { int ret = SCSI_DH_OK; struct hp_sw_dh_data *h = get_hp_sw_data(sdev); @@ -276,14 +295,18 @@ static int hp_sw_activate(struct scsi_device *sdev) ret = hp_sw_tur(sdev, h); if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) { - ret = hp_sw_start_stop(sdev, h); + h->retry_cnt = h->retries; + h->callback_fn = fn; + h->callback_data = data; + ret = hp_sw_start_stop(h); if (ret == SCSI_DH_OK) - sdev_printk(KERN_INFO, sdev, - "%s: activated path\n", - HP_SW_NAME); + return 0; + h->callback_fn = h->callback_data = NULL; } - return ret; + if (fn) + fn(data, ret); + return 0; } static const struct scsi_dh_devlist hp_sw_dh_data_list[] = { @@ -326,6 +349,7 @@ static int hp_sw_bus_attach(struct scsi_device *sdev) h = (struct hp_sw_dh_data *) scsi_dh_data->buf; h->path_state = HP_SW_PATH_UNINITIALIZED; h->retries = HP_SW_RETRIES; + h->sdev = sdev; ret = hp_sw_tur(sdev, h); if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED) diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 268189d31d9c..47cfe1c49c3e 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -22,6 +22,7 @@ #include <scsi/scsi.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_dh.h> +#include <linux/workqueue.h> #define RDAC_NAME "rdac" #define RDAC_RETRY_COUNT 5 @@ -138,7 +139,13 @@ struct rdac_controller { } mode_select; u8 index; u8 array_name[ARRAY_LABEL_LEN]; + spinlock_t ms_lock; + int ms_queued; + struct work_struct ms_work; + struct scsi_device *ms_sdev; + struct list_head ms_head; }; + struct c8_inquiry { u8 peripheral_info; u8 page_code; /* 0xC8 */ @@ -198,8 +205,17 @@ static const char *lun_state[] = "owned (AVT mode)", }; +struct rdac_queue_data { + struct list_head entry; + struct rdac_dh_data *h; + activate_complete callback_fn; + void *callback_data; +}; + static LIST_HEAD(ctlr_list); static DEFINE_SPINLOCK(list_lock); +static struct workqueue_struct *kmpath_rdacd; +static void send_mode_select(struct work_struct *work); /* * module parameter to enable rdac debug logging. @@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev, rdac_pg->subpage_code = 0x1; rdac_pg->page_len[0] = 0x01; rdac_pg->page_len[1] = 0x28; - rdac_pg->lun_table[h->lun] = 0x81; } else { struct rdac_pg_legacy *rdac_pg; @@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev, common = &rdac_pg->common; rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER; rdac_pg->page_len = 0x68; - rdac_pg->lun_table[h->lun] = 0x81; } common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS; common->quiescence_timeout = RDAC_QUIESCENCE_TIME; @@ -325,6 +339,7 @@ static void release_controller(struct kref *kref) struct rdac_controller *ctlr; ctlr = container_of(kref, struct rdac_controller, kref); + flush_workqueue(kmpath_rdacd); spin_lock(&list_lock); list_del(&ctlr->node); spin_unlock(&list_lock); @@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id, kref_init(&ctlr->kref); ctlr->use_ms10 = -1; + ctlr->ms_queued = 0; + ctlr->ms_sdev = NULL; + spin_lock_init(&ctlr->ms_lock); + INIT_WORK(&ctlr->ms_work, send_mode_select); + INIT_LIST_HEAD(&ctlr->ms_head); list_add(&ctlr->node, &ctlr_list); done: spin_unlock(&list_lock); @@ -490,7 +510,7 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) } static int mode_select_handle_sense(struct scsi_device *sdev, - unsigned char *sensebuf) + unsigned char *sensebuf) { struct scsi_sense_hdr sense_hdr; int err = SCSI_DH_IO, ret; @@ -533,11 +553,29 @@ done: return err; } -static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) +static void send_mode_select(struct work_struct *work) { + struct rdac_controller *ctlr = + container_of(work, struct rdac_controller, ms_work); struct request *rq; + struct scsi_device *sdev = ctlr->ms_sdev; + struct rdac_dh_data *h = get_rdac_data(sdev); struct request_queue *q = sdev->request_queue; int err, retry_cnt = RDAC_RETRY_COUNT; + struct rdac_queue_data *tmp, *qdata; + LIST_HEAD(list); + u8 *lun_table; + + spin_lock(&ctlr->ms_lock); + list_splice_init(&ctlr->ms_head, &list); + ctlr->ms_queued = 0; + ctlr->ms_sdev = NULL; + spin_unlock(&ctlr->ms_lock); + + if (ctlr->use_ms10) + lun_table = ctlr->mode_select.expanded.lun_table; + else + lun_table = ctlr->mode_select.legacy.lun_table; retry: err = SCSI_DH_RES_TEMP_UNAVAIL; @@ -545,6 +583,10 @@ retry: if (!rq) goto done; + list_for_each_entry(qdata, &list, entry) { + lun_table[qdata->h->lun] = 0x81; + } + RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, " "%s MODE_SELECT command", (char *) h->ctlr->array_name, h->ctlr->index, @@ -565,10 +607,45 @@ retry: } done: - return err; + list_for_each_entry_safe(qdata, tmp, &list, entry) { + list_del(&qdata->entry); + if (err == SCSI_DH_OK) + qdata->h->state = RDAC_STATE_ACTIVE; + if (qdata->callback_fn) + qdata->callback_fn(qdata->callback_data, err); + kfree(qdata); + } + return; +} + +static int queue_mode_select(struct scsi_device *sdev, + activate_complete fn, void *data) +{ + struct rdac_queue_data *qdata; + struct rdac_controller *ctlr; + + qdata = kzalloc(sizeof(*qdata), GFP_KERNEL); + if (!qdata) + return SCSI_DH_RETRY; + + qdata->h = get_rdac_data(sdev); + qdata->callback_fn = fn; + qdata->callback_data = data; + + ctlr = qdata->h->ctlr; + spin_lock(&ctlr->ms_lock); + list_add_tail(&qdata->entry, &ctlr->ms_head); + if (!ctlr->ms_queued) { + ctlr->ms_queued = 1; + ctlr->ms_sdev = sdev; + queue_work(kmpath_rdacd, &ctlr->ms_work); + } + spin_unlock(&ctlr->ms_lock); + return SCSI_DH_OK; } -static int rdac_activate(struct scsi_device *sdev) +static int rdac_activate(struct scsi_device *sdev, + activate_complete fn, void *data) { struct rdac_dh_data *h = get_rdac_data(sdev); int err = SCSI_DH_OK; @@ -577,10 +654,15 @@ static int rdac_activate(struct scsi_device *sdev) if (err != SCSI_DH_OK) goto done; - if (h->lun_state == RDAC_LUN_UNOWNED) - err = send_mode_select(sdev, h); + if (h->lun_state == RDAC_LUN_UNOWNED) { + err = queue_mode_select(sdev, fn, data); + if (err == SCSI_DH_OK) + return 0; + } done: - return err; + if (fn) + fn(data, err); + return 0; } static int rdac_prep_fn(struct scsi_device *sdev, struct request *req) @@ -790,13 +872,26 @@ static int __init rdac_init(void) int r; r = scsi_register_device_handler(&rdac_dh); - if (r != 0) + if (r != 0) { printk(KERN_ERR "Failed to register scsi device handler."); + goto done; + } + + /* + * Create workqueue to handle mode selects for rdac + */ + kmpath_rdacd = create_singlethread_workqueue("kmpath_rdacd"); + if (!kmpath_rdacd) { + scsi_unregister_device_handler(&rdac_dh); + printk(KERN_ERR "kmpath_rdacd creation failed.\n"); + } +done: return r; } static void __exit rdac_exit(void) { + destroy_workqueue(kmpath_rdacd); scsi_unregister_device_handler(&rdac_dh); } diff --git a/drivers/scsi/dmx3191d.c b/drivers/scsi/dmx3191d.c index fa738ec8692a..207352cc70cc 100644 --- a/drivers/scsi/dmx3191d.c +++ b/drivers/scsi/dmx3191d.c @@ -31,7 +31,7 @@ #include <scsi/scsi_host.h> /* - * Defintions for the generic 5380 driver. + * Definitions for the generic 5380 driver. */ #define AUTOSENSE diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 704b8e034946..a30ffaa1222c 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -66,14 +66,14 @@ LIST_HEAD(fcoe_hostlist); DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu); /* Function Prototypes */ -static int fcoe_reset(struct Scsi_Host *shost); +static int fcoe_reset(struct Scsi_Host *); static int fcoe_xmit(struct fc_lport *, struct fc_frame *); static int fcoe_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); -static int fcoe_percpu_receive_thread(void *arg); -static void fcoe_clean_pending_queue(struct fc_lport *lp); -static void fcoe_percpu_clean(struct fc_lport *lp); -static int fcoe_link_ok(struct fc_lport *lp); +static int fcoe_percpu_receive_thread(void *); +static void fcoe_clean_pending_queue(struct fc_lport *); +static void fcoe_percpu_clean(struct fc_lport *); +static int fcoe_link_ok(struct fc_lport *); static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); static int fcoe_hostlist_add(const struct fc_lport *); @@ -82,15 +82,69 @@ static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *); static int fcoe_device_notification(struct notifier_block *, ulong, void *); static void fcoe_dev_setup(void); static void fcoe_dev_cleanup(void); -static struct fcoe_interface * - fcoe_hostlist_lookup_port(const struct net_device *dev); +static struct fcoe_interface +*fcoe_hostlist_lookup_port(const struct net_device *); + +static int fcoe_fip_recv(struct sk_buff *, struct net_device *, + struct packet_type *, struct net_device *); + +static void fcoe_fip_send(struct fcoe_ctlr *, struct sk_buff *); +static void fcoe_update_src_mac(struct fc_lport *, u8 *); +static u8 *fcoe_get_src_mac(struct fc_lport *); +static void fcoe_destroy_work(struct work_struct *); + +static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *, + unsigned int); +static int fcoe_ddp_done(struct fc_lport *, u16); + +static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *); + +static int fcoe_create(const char *, struct kernel_param *); +static int fcoe_destroy(const char *, struct kernel_param *); + +static struct fc_seq *fcoe_elsct_send(struct fc_lport *, + u32 did, struct fc_frame *, + unsigned int op, + void (*resp)(struct fc_seq *, + struct fc_frame *, + void *), + void *, u32 timeout); +static void fcoe_recv_frame(struct sk_buff *skb); + +static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *); + +module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); +__MODULE_PARM_TYPE(create, "string"); +MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in."); +module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); +__MODULE_PARM_TYPE(destroy, "string"); +MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); -/* notification function from net device */ +/* notification function for packets from net device */ static struct notifier_block fcoe_notifier = { .notifier_call = fcoe_device_notification, }; -static struct scsi_transport_template *scsi_transport_fcoe_sw; +/* notification function for CPU hotplug events */ +static struct notifier_block fcoe_cpu_notifier = { + .notifier_call = fcoe_cpu_callback, +}; + +static struct scsi_transport_template *fcoe_transport_template; +static struct scsi_transport_template *fcoe_vport_transport_template; + +static int fcoe_vport_destroy(struct fc_vport *); +static int fcoe_vport_create(struct fc_vport *, bool disabled); +static int fcoe_vport_disable(struct fc_vport *, bool disable); +static void fcoe_set_vport_symbolic_name(struct fc_vport *); + +static struct libfc_function_template fcoe_libfc_fcn_templ = { + .frame_send = fcoe_xmit, + .ddp_setup = fcoe_ddp_setup, + .ddp_done = fcoe_ddp_done, + .elsct_send = fcoe_elsct_send, + .get_lesb = fcoe_get_lesb, +}; struct fc_function_template fcoe_transport_function = { .show_host_node_name = 1, @@ -123,6 +177,48 @@ struct fc_function_template fcoe_transport_function = { .issue_fc_host_lip = fcoe_reset, .terminate_rport_io = fc_rport_terminate_io, + + .vport_create = fcoe_vport_create, + .vport_delete = fcoe_vport_destroy, + .vport_disable = fcoe_vport_disable, + .set_vport_symbolic_name = fcoe_set_vport_symbolic_name, + + .bsg_request = fc_lport_bsg_request, +}; + +struct fc_function_template fcoe_vport_transport_function = { + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_active_fc4s = 1, + .show_host_maxframe_size = 1, + + .show_host_port_id = 1, + .show_host_supported_speeds = 1, + .get_host_speed = fc_get_host_speed, + .show_host_speed = 1, + .show_host_port_type = 1, + .get_host_port_state = fc_get_host_port_state, + .show_host_port_state = 1, + .show_host_symbolic_name = 1, + + .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv), + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + + .show_host_fabric_name = 1, + .show_starget_node_name = 1, + .show_starget_port_name = 1, + .show_starget_port_id = 1, + .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, + .show_rport_dev_loss_tmo = 1, + .get_fc_host_stats = fc_get_host_stats, + .issue_fc_host_lip = fcoe_reset, + + .terminate_rport_io = fc_rport_terminate_io, + + .bsg_request = fc_lport_bsg_request, }; static struct scsi_host_template fcoe_shost_template = { @@ -137,20 +233,17 @@ static struct scsi_host_template fcoe_shost_template = { .change_queue_depth = fc_change_queue_depth, .change_queue_type = fc_change_queue_type, .this_id = -1, - .cmd_per_lun = 32, + .cmd_per_lun = 3, .can_queue = FCOE_MAX_OUTSTANDING_COMMANDS, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = SG_ALL, .max_sectors = 0xffff, }; -static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, - struct net_device *orig_dev); /** - * fcoe_interface_setup() - * @fcoe: new fcoe_interface - * @netdev : ptr to the associated netdevice struct + * fcoe_interface_setup() - Setup a FCoE interface + * @fcoe: The new FCoE interface + * @netdev: The net device that the fcoe interface is on * * Returns : 0 for success * Locking: must be called with the RTNL mutex held @@ -160,23 +253,36 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, { struct fcoe_ctlr *fip = &fcoe->ctlr; struct netdev_hw_addr *ha; + struct net_device *real_dev; u8 flogi_maddr[ETH_ALEN]; + const struct net_device_ops *ops; fcoe->netdev = netdev; + /* Let LLD initialize for FCoE */ + ops = netdev->netdev_ops; + if (ops->ndo_fcoe_enable) { + if (ops->ndo_fcoe_enable(netdev)) + FCOE_NETDEV_DBG(netdev, "Failed to enable FCoE" + " specific feature for LLD.\n"); + } + /* Do not support for bonding device */ if ((netdev->priv_flags & IFF_MASTER_ALB) || (netdev->priv_flags & IFF_SLAVE_INACTIVE) || (netdev->priv_flags & IFF_MASTER_8023AD)) { + FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n"); return -EOPNOTSUPP; } /* look for SAN MAC address, if multiple SAN MACs exist, only * use the first one for SPMA */ + real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ? + vlan_dev_real_dev(netdev) : netdev; rcu_read_lock(); - for_each_dev_addr(netdev, ha) { + for_each_dev_addr(real_dev, ha) { if ((ha->type == NETDEV_HW_ADDR_T_SAN) && - (is_valid_ether_addr(fip->ctl_src_addr))) { + (is_valid_ether_addr(ha->addr))) { memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN); fip->spma = 1; break; @@ -216,19 +322,16 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, return 0; } -static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb); -static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new); -static void fcoe_destroy_work(struct work_struct *work); - /** - * fcoe_interface_create() - * @netdev: network interface + * fcoe_interface_create() - Create a FCoE interface on a net device + * @netdev: The net device to create the FCoE interface on * * Returns: pointer to a struct fcoe_interface or NULL on error */ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) { struct fcoe_interface *fcoe; + int err; fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL); if (!fcoe) { @@ -245,15 +348,22 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) fcoe_ctlr_init(&fcoe->ctlr); fcoe->ctlr.send = fcoe_fip_send; fcoe->ctlr.update_mac = fcoe_update_src_mac; + fcoe->ctlr.get_src_addr = fcoe_get_src_mac; - fcoe_interface_setup(fcoe, netdev); + err = fcoe_interface_setup(fcoe, netdev); + if (err) { + fcoe_ctlr_destroy(&fcoe->ctlr); + kfree(fcoe); + dev_put(netdev); + return NULL; + } return fcoe; } /** - * fcoe_interface_cleanup() - clean up netdev configurations - * @fcoe: + * fcoe_interface_cleanup() - Clean up a FCoE interface + * @fcoe: The FCoE interface to be cleaned up * * Caller must be holding the RTNL mutex */ @@ -262,6 +372,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) struct net_device *netdev = fcoe->netdev; struct fcoe_ctlr *fip = &fcoe->ctlr; u8 flogi_maddr[ETH_ALEN]; + const struct net_device_ops *ops; /* * Don't listen for Ethernet packets anymore. @@ -276,16 +387,22 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) /* Delete secondary MAC addresses */ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); dev_unicast_delete(netdev, flogi_maddr); - if (!is_zero_ether_addr(fip->data_src_addr)) - dev_unicast_delete(netdev, fip->data_src_addr); if (fip->spma) dev_unicast_delete(netdev, fip->ctl_src_addr); dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + + /* Tell the LLD we are done w/ FCoE */ + ops = netdev->netdev_ops; + if (ops->ndo_fcoe_disable) { + if (ops->ndo_fcoe_disable(netdev)) + FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE" + " specific feature for LLD.\n"); + } } /** * fcoe_interface_release() - fcoe_port kref release function - * @kref: embedded reference count in an fcoe_interface struct + * @kref: Embedded reference count in an fcoe_interface struct */ static void fcoe_interface_release(struct kref *kref) { @@ -301,8 +418,8 @@ static void fcoe_interface_release(struct kref *kref) } /** - * fcoe_interface_get() - * @fcoe: + * fcoe_interface_get() - Get a reference to a FCoE interface + * @fcoe: The FCoE interface to be held */ static inline void fcoe_interface_get(struct fcoe_interface *fcoe) { @@ -310,8 +427,8 @@ static inline void fcoe_interface_get(struct fcoe_interface *fcoe) } /** - * fcoe_interface_put() - * @fcoe: + * fcoe_interface_put() - Put a reference to a FCoE interface + * @fcoe: The FCoE interface to be released */ static inline void fcoe_interface_put(struct fcoe_interface *fcoe) { @@ -319,15 +436,16 @@ static inline void fcoe_interface_put(struct fcoe_interface *fcoe) } /** - * fcoe_fip_recv - handle a received FIP frame. - * @skb: the receive skb - * @dev: associated &net_device - * @ptype: the &packet_type structure which was used to register this handler. - * @orig_dev: original receive &net_device, in case @dev is a bond. + * fcoe_fip_recv() - Handler for received FIP frames + * @skb: The receive skb + * @netdev: The associated net device + * @ptype: The packet_type structure which was used to register this handler + * @orig_dev: The original net_device the the skb was received on. + * (in case dev is a bond) * * Returns: 0 for success */ -static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, +static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev, struct packet_type *ptype, struct net_device *orig_dev) { @@ -339,9 +457,9 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, } /** - * fcoe_fip_send() - send an Ethernet-encapsulated FIP frame. - * @fip: FCoE controller. - * @skb: FIP Packet. + * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame + * @fip: The FCoE controller + * @skb: The FIP packet to be sent */ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) { @@ -350,88 +468,101 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) } /** - * fcoe_update_src_mac() - Update Ethernet MAC filters. - * @fip: FCoE controller. - * @old: Unicast MAC address to delete if the MAC is non-zero. - * @new: Unicast MAC address to add. + * fcoe_update_src_mac() - Update the Ethernet MAC filters + * @lport: The local port to update the source MAC on + * @addr: Unicast MAC address to add * * Remove any previously-set unicast MAC filter. * Add secondary FCoE MAC address filter for our OUI. */ -static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new) +static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr) { - struct fcoe_interface *fcoe; + struct fcoe_port *port = lport_priv(lport); + struct fcoe_interface *fcoe = port->fcoe; - fcoe = fcoe_from_ctlr(fip); rtnl_lock(); - if (!is_zero_ether_addr(old)) - dev_unicast_delete(fcoe->netdev, old); - dev_unicast_add(fcoe->netdev, new); + if (!is_zero_ether_addr(port->data_src_addr)) + dev_unicast_delete(fcoe->netdev, port->data_src_addr); + if (!is_zero_ether_addr(addr)) + dev_unicast_add(fcoe->netdev, addr); + memcpy(port->data_src_addr, addr, ETH_ALEN); rtnl_unlock(); } /** - * fcoe_lport_config() - sets up the fc_lport - * @lp: ptr to the fc_lport + * fcoe_get_src_mac() - return the Ethernet source address for an lport + * @lport: libfc lport + */ +static u8 *fcoe_get_src_mac(struct fc_lport *lport) +{ + struct fcoe_port *port = lport_priv(lport); + + return port->data_src_addr; +} + +/** + * fcoe_lport_config() - Set up a local port + * @lport: The local port to be setup * * Returns: 0 for success */ -static int fcoe_lport_config(struct fc_lport *lp) +static int fcoe_lport_config(struct fc_lport *lport) { - lp->link_up = 0; - lp->qfull = 0; - lp->max_retry_count = 3; - lp->max_rport_retry_count = 3; - lp->e_d_tov = 2 * 1000; /* FC-FS default */ - lp->r_a_tov = 2 * 2 * 1000; - lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | - FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); - - fc_lport_init_stats(lp); + lport->link_up = 0; + lport->qfull = 0; + lport->max_retry_count = 3; + lport->max_rport_retry_count = 3; + lport->e_d_tov = 2 * 1000; /* FC-FS default */ + lport->r_a_tov = 2 * 2 * 1000; + lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | + FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); + lport->does_npiv = 1; + + fc_lport_init_stats(lport); /* lport fc_lport related configuration */ - fc_lport_config(lp); + fc_lport_config(lport); /* offload related configuration */ - lp->crc_offload = 0; - lp->seq_offload = 0; - lp->lro_enabled = 0; - lp->lro_xid = 0; - lp->lso_max = 0; + lport->crc_offload = 0; + lport->seq_offload = 0; + lport->lro_enabled = 0; + lport->lro_xid = 0; + lport->lso_max = 0; return 0; } /** - * fcoe_queue_timer() - fcoe queue timer - * @lp: the fc_lport pointer + * fcoe_queue_timer() - The fcoe queue timer + * @lport: The local port * * Calls fcoe_check_wait_queue on timeout - * */ -static void fcoe_queue_timer(ulong lp) +static void fcoe_queue_timer(ulong lport) { - fcoe_check_wait_queue((struct fc_lport *)lp, NULL); + fcoe_check_wait_queue((struct fc_lport *)lport, NULL); } /** - * fcoe_netdev_config() - Set up netdev for SW FCoE - * @lp : ptr to the fc_lport - * @netdev : ptr to the associated netdevice struct + * fcoe_netdev_config() - Set up net devive for SW FCoE + * @lport: The local port that is associated with the net device + * @netdev: The associated net device * - * Must be called after fcoe_lport_config() as it will use lport mutex + * Must be called after fcoe_lport_config() as it will use local port mutex * - * Returns : 0 for success + * Returns: 0 for success */ -static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) +static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) { u32 mfs; u64 wwnn, wwpn; struct fcoe_interface *fcoe; struct fcoe_port *port; + int vid = 0; /* Setup lport private data to point to fcoe softc */ - port = lport_priv(lp); + port = lport_priv(lport); fcoe = port->fcoe; /* @@ -439,86 +570,112 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) * user-configured limit. If the MFS is too low, fcoe_link_ok() * will return 0, so do this first. */ - mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + - sizeof(struct fcoe_crc_eof)); - if (fc_set_mfs(lp, mfs)) + mfs = netdev->mtu; + if (netdev->features & NETIF_F_FCOE_MTU) { + mfs = FCOE_MTU; + FCOE_NETDEV_DBG(netdev, "Supports FCOE_MTU of %d bytes\n", mfs); + } + mfs -= (sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof)); + if (fc_set_mfs(lport, mfs)) return -EINVAL; /* offload features support */ if (netdev->features & NETIF_F_SG) - lp->sg_supp = 1; + lport->sg_supp = 1; if (netdev->features & NETIF_F_FCOE_CRC) { - lp->crc_offload = 1; + lport->crc_offload = 1; FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n"); } if (netdev->features & NETIF_F_FSO) { - lp->seq_offload = 1; - lp->lso_max = netdev->gso_max_size; + lport->seq_offload = 1; + lport->lso_max = netdev->gso_max_size; FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n", - lp->lso_max); + lport->lso_max); } if (netdev->fcoe_ddp_xid) { - lp->lro_enabled = 1; - lp->lro_xid = netdev->fcoe_ddp_xid; + lport->lro_enabled = 1; + lport->lro_xid = netdev->fcoe_ddp_xid; FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n", - lp->lro_xid); + lport->lro_xid); } skb_queue_head_init(&port->fcoe_pending_queue); port->fcoe_pending_queue_active = 0; - setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp); + setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport); - wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0); - fc_set_wwnn(lp, wwnn); - /* XXX - 3rd arg needs to be vlan id */ - wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0); - fc_set_wwpn(lp, wwpn); + if (!lport->vport) { + /* + * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN: + * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0. + * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID + */ + if (netdev->priv_flags & IFF_802_1Q_VLAN) + vid = vlan_dev_vlan_id(netdev); + wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0); + fc_set_wwnn(lport, wwnn); + wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 2, vid); + fc_set_wwpn(lport, wwpn); + } return 0; } /** - * fcoe_shost_config() - Sets up fc_lport->host - * @lp : ptr to the fc_lport - * @shost : ptr to the associated scsi host - * @dev : device associated to scsi host + * fcoe_shost_config() - Set up the SCSI host associated with a local port + * @lport: The local port + * @shost: The SCSI host to associate with the local port + * @dev: The device associated with the SCSI host * * Must be called after fcoe_lport_config() and fcoe_netdev_config() * - * Returns : 0 for success + * Returns: 0 for success */ -static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, - struct device *dev) +static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost, + struct device *dev) { int rc = 0; /* lport scsi host config */ - lp->host = shost; - - lp->host->max_lun = FCOE_MAX_LUN; - lp->host->max_id = FCOE_MAX_FCP_TARGET; - lp->host->max_channel = 0; - lp->host->transportt = scsi_transport_fcoe_sw; + lport->host->max_lun = FCOE_MAX_LUN; + lport->host->max_id = FCOE_MAX_FCP_TARGET; + lport->host->max_channel = 0; + if (lport->vport) + lport->host->transportt = fcoe_vport_transport_template; + else + lport->host->transportt = fcoe_transport_template; /* add the new host to the SCSI-ml */ - rc = scsi_add_host(lp->host, dev); + rc = scsi_add_host(lport->host, dev); if (rc) { - FCOE_NETDEV_DBG(fcoe_netdev(lp), "fcoe_shost_config: " + FCOE_NETDEV_DBG(fcoe_netdev(lport), "fcoe_shost_config: " "error on scsi_add_host\n"); return rc; } - sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s", - FCOE_NAME, FCOE_VERSION, - fcoe_netdev(lp)->name); + + if (!lport->vport) + fc_host_max_npiv_vports(lport->host) = USHORT_MAX; + + snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE, + "%s v%s over %s", FCOE_NAME, FCOE_VERSION, + fcoe_netdev(lport)->name); return 0; } -/* - * fcoe_oem_match() - match for read types IO - * @fp: the fc_frame for new IO. +/** + * fcoe_oem_match() - The match routine for the offloaded exchange manager + * @fp: The I/O frame + * + * This routine will be associated with an exchange manager (EM). When + * the libfc exchange handling code is looking for an EM to use it will + * call this routine and pass it the frame that it wishes to send. This + * routine will return True if the associated EM is to be used and False + * if the echange code should continue looking for an EM. * - * Returns : true for read types IO, otherwise returns false. + * The offload EM that this routine is associated with will handle any + * packets that are for SCSI read requests. + * + * Returns: True for read types I/O, otherwise returns false. */ bool fcoe_oem_match(struct fc_frame *fp) { @@ -527,14 +684,14 @@ bool fcoe_oem_match(struct fc_frame *fp) } /** - * fcoe_em_config() - allocates em for this lport - * @lp: the fcoe that em is to allocated for + * fcoe_em_config() - Allocate and configure an exchange manager + * @lport: The local port that the new EM will be associated with * - * Returns : 0 on success + * Returns: 0 on success */ -static inline int fcoe_em_config(struct fc_lport *lp) +static inline int fcoe_em_config(struct fc_lport *lport) { - struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->fcoe; struct fcoe_interface *oldfcoe = NULL; struct net_device *old_real_dev, *cur_real_dev; @@ -545,8 +702,9 @@ static inline int fcoe_em_config(struct fc_lport *lp) * Check if need to allocate an em instance for * offload exchange ids to be shared across all VN_PORTs/lport. */ - if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= max_xid)) { - lp->lro_xid = 0; + if (!lport->lro_enabled || !lport->lro_xid || + (lport->lro_xid >= max_xid)) { + lport->lro_xid = 0; goto skip_oem; } @@ -572,16 +730,16 @@ static inline int fcoe_em_config(struct fc_lport *lp) } if (fcoe->oem) { - if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) { + if (!fc_exch_mgr_add(lport, fcoe->oem, fcoe_oem_match)) { printk(KERN_ERR "fcoe_em_config: failed to add " "offload em:%p on interface:%s\n", fcoe->oem, fcoe->netdev->name); return -ENOMEM; } } else { - fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3, - FCOE_MIN_XID, lp->lro_xid, - fcoe_oem_match); + fcoe->oem = fc_exch_mgr_alloc(lport, FC_CLASS_3, + FCOE_MIN_XID, lport->lro_xid, + fcoe_oem_match); if (!fcoe->oem) { printk(KERN_ERR "fcoe_em_config: failed to allocate " "em for offload exches on interface:%s\n", @@ -593,10 +751,10 @@ static inline int fcoe_em_config(struct fc_lport *lp) /* * Exclude offload EM xid range from next EM xid range. */ - min_xid += lp->lro_xid + 1; + min_xid += lport->lro_xid + 1; skip_oem: - if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) { + if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, min_xid, max_xid, NULL)) { printk(KERN_ERR "fcoe_em_config: failed to " "allocate em on interface %s\n", fcoe->netdev->name); return -ENOMEM; @@ -606,8 +764,8 @@ skip_oem: } /** - * fcoe_if_destroy() - FCoE software HBA tear-down function - * @lport: fc_lport to destroy + * fcoe_if_destroy() - Tear down a SW FCoE instance + * @lport: The local port to be destroyed */ static void fcoe_if_destroy(struct fc_lport *lport) { @@ -630,6 +788,11 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* Free existing transmit skbs */ fcoe_clean_pending_queue(lport); + rtnl_lock(); + if (!is_zero_ether_addr(port->data_src_addr)) + dev_unicast_delete(netdev, port->data_src_addr); + rtnl_unlock(); + /* receives may not be stopped until after this */ fcoe_interface_put(fcoe); @@ -650,82 +813,89 @@ static void fcoe_if_destroy(struct fc_lport *lport) scsi_host_put(lport->host); } -/* - * fcoe_ddp_setup - calls LLD's ddp_setup through net_device - * @lp: the corresponding fc_lport - * @xid: the exchange id for this ddp transfer - * @sgl: the scatterlist describing this transfer - * @sgc: number of sg items +/** + * fcoe_ddp_setup() - Call a LLD's ddp_setup through the net device + * @lport: The local port to setup DDP for + * @xid: The exchange ID for this DDP transfer + * @sgl: The scatterlist describing this transfer + * @sgc: The number of sg items * - * Returns : 0 no ddp + * Returns: 0 if the DDP context was not configured */ -static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid, - struct scatterlist *sgl, unsigned int sgc) +static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid, + struct scatterlist *sgl, unsigned int sgc) { - struct net_device *n = fcoe_netdev(lp); + struct net_device *netdev = fcoe_netdev(lport); - if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup) - return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc); + if (netdev->netdev_ops->ndo_fcoe_ddp_setup) + return netdev->netdev_ops->ndo_fcoe_ddp_setup(netdev, + xid, sgl, + sgc); return 0; } -/* - * fcoe_ddp_done - calls LLD's ddp_done through net_device - * @lp: the corresponding fc_lport - * @xid: the exchange id for this ddp transfer +/** + * fcoe_ddp_done() - Call a LLD's ddp_done through the net device + * @lport: The local port to complete DDP on + * @xid: The exchange ID for this DDP transfer * - * Returns : the length of data that have been completed by ddp + * Returns: the length of data that have been completed by DDP */ -static int fcoe_ddp_done(struct fc_lport *lp, u16 xid) +static int fcoe_ddp_done(struct fc_lport *lport, u16 xid) { - struct net_device *n = fcoe_netdev(lp); + struct net_device *netdev = fcoe_netdev(lport); - if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done) - return n->netdev_ops->ndo_fcoe_ddp_done(n, xid); + if (netdev->netdev_ops->ndo_fcoe_ddp_done) + return netdev->netdev_ops->ndo_fcoe_ddp_done(netdev, xid); return 0; } -static struct libfc_function_template fcoe_libfc_fcn_templ = { - .frame_send = fcoe_xmit, - .ddp_setup = fcoe_ddp_setup, - .ddp_done = fcoe_ddp_done, -}; - /** - * fcoe_if_create() - this function creates the fcoe port - * @fcoe: fcoe_interface structure to create an fc_lport instance on - * @parent: device pointer to be the parent in sysfs for the SCSI host + * fcoe_if_create() - Create a FCoE instance on an interface + * @fcoe: The FCoE interface to create a local port on + * @parent: The device pointer to be the parent in sysfs for the SCSI host + * @npiv: Indicates if the port is a vport or not * - * Creates fc_lport struct and scsi_host for lport, configures lport. + * Creates a fc_lport instance and a Scsi_Host instance and configure them. * - * Returns : The allocated fc_lport or an error pointer + * Returns: The allocated fc_lport or an error pointer */ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, - struct device *parent) + struct device *parent, int npiv) { - int rc; + struct net_device *netdev = fcoe->netdev; struct fc_lport *lport = NULL; struct fcoe_port *port; struct Scsi_Host *shost; - struct net_device *netdev = fcoe->netdev; + int rc; + /* + * parent is only a vport if npiv is 1, + * but we'll only use vport in that case so go ahead and set it + */ + struct fc_vport *vport = dev_to_vport(parent); FCOE_NETDEV_DBG(netdev, "Create Interface\n"); - shost = libfc_host_alloc(&fcoe_shost_template, - sizeof(struct fcoe_port)); - if (!shost) { + if (!npiv) { + lport = libfc_host_alloc(&fcoe_shost_template, + sizeof(struct fcoe_port)); + } else { + lport = libfc_vport_create(vport, + sizeof(struct fcoe_port)); + } + if (!lport) { FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n"); rc = -ENOMEM; goto out; } - lport = shost_priv(shost); + shost = lport->host; port = lport_priv(lport); port->lport = lport; port->fcoe = fcoe; INIT_WORK(&port->destroy_work, fcoe_destroy_work); - /* configure fc_lport, e.g., em */ + /* configure a fc_lport including the exchange manager */ rc = fcoe_lport_config(lport); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure lport for the " @@ -733,6 +903,13 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, goto out_host_put; } + if (npiv) { + FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n", + vport->node_name, vport->port_name); + fc_set_wwnn(lport, vport->node_name); + fc_set_wwpn(lport, vport->port_name); + } + /* configure lport network properties */ rc = fcoe_netdev_config(lport, netdev); if (rc) { @@ -757,21 +934,24 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, goto out_lp_destroy; } - /* - * fcoe_em_alloc() and fcoe_hostlist_add() both - * need to be atomic with respect to other changes to the hostlist - * since fcoe_em_alloc() looks for an existing EM - * instance on host list updated by fcoe_hostlist_add(). - * - * This is currently handled through the fcoe_config_mutex begin held. - */ + if (!npiv) { + /* + * fcoe_em_alloc() and fcoe_hostlist_add() both + * need to be atomic with respect to other changes to the + * hostlist since fcoe_em_alloc() looks for an existing EM + * instance on host list updated by fcoe_hostlist_add(). + * + * This is currently handled through the fcoe_config_mutex + * begin held. + */ - /* lport exch manager allocation */ - rc = fcoe_em_config(lport); - if (rc) { - FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " - "interface\n"); - goto out_lp_destroy; + /* lport exch manager allocation */ + rc = fcoe_em_config(lport); + if (rc) { + FCOE_NETDEV_DBG(netdev, "Could not configure the EM " + "for the interface\n"); + goto out_lp_destroy; + } } fcoe_interface_get(fcoe); @@ -786,17 +966,20 @@ out: } /** - * fcoe_if_init() - attach to scsi transport + * fcoe_if_init() - Initialization routine for fcoe.ko + * + * Attaches the SW FCoE transport to the FC transport * - * Returns : 0 on success + * Returns: 0 on success */ static int __init fcoe_if_init(void) { /* attach to scsi transport */ - scsi_transport_fcoe_sw = - fc_attach_transport(&fcoe_transport_function); + fcoe_transport_template = fc_attach_transport(&fcoe_transport_function); + fcoe_vport_transport_template = + fc_attach_transport(&fcoe_vport_transport_function); - if (!scsi_transport_fcoe_sw) { + if (!fcoe_transport_template) { printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n"); return -ENODEV; } @@ -805,20 +988,24 @@ static int __init fcoe_if_init(void) } /** - * fcoe_if_exit() - detach from scsi transport + * fcoe_if_exit() - Tear down fcoe.ko + * + * Detaches the SW FCoE transport from the FC transport * - * Returns : 0 on success + * Returns: 0 on success */ int __exit fcoe_if_exit(void) { - fc_release_transport(scsi_transport_fcoe_sw); - scsi_transport_fcoe_sw = NULL; + fc_release_transport(fcoe_transport_template); + fc_release_transport(fcoe_vport_transport_template); + fcoe_transport_template = NULL; + fcoe_vport_transport_template = NULL; return 0; } /** - * fcoe_percpu_thread_create() - Create a receive thread for an online cpu - * @cpu: cpu index for the online cpu + * fcoe_percpu_thread_create() - Create a receive thread for an online CPU + * @cpu: The CPU index of the CPU to create a receive thread for */ static void fcoe_percpu_thread_create(unsigned int cpu) { @@ -841,8 +1028,8 @@ static void fcoe_percpu_thread_create(unsigned int cpu) } /** - * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu - * @cpu: cpu index the rx thread is to be removed + * fcoe_percpu_thread_destroy() - Remove the receive thread of a CPU + * @cpu: The CPU index of the CPU whose receive thread is to be destroyed * * Destroys a per-CPU Rx thread. Any pending skbs are moved to the * current CPU's Rx thread. If the thread being destroyed is bound to @@ -890,7 +1077,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) } else { /* * The targeted CPU is not initialized and cannot accept - * new skbs. Unlock the targeted CPU and drop the skbs + * new skbs. Unlock the targeted CPU and drop the skbs * on the CPU that is going offline. */ while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL) @@ -931,12 +1118,12 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) } /** - * fcoe_cpu_callback() - fcoe cpu hotplug event callback - * @nfb: callback data block - * @action: event triggering the callback - * @hcpu: index for the cpu of this event + * fcoe_cpu_callback() - Handler for CPU hotplug events + * @nfb: The callback data block + * @action: The event triggering the callback + * @hcpu: The index of the CPU that the event is for * - * This creates or destroys per cpu data for fcoe + * This creates or destroys per-CPU data for fcoe * * Returns NOTIFY_OK always. */ @@ -962,25 +1149,22 @@ static int fcoe_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } -static struct notifier_block fcoe_cpu_notifier = { - .notifier_call = fcoe_cpu_callback, -}; - /** - * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ - * @skb: the receive skb - * @dev: associated net device - * @ptype: context - * @olddev: last device + * fcoe_rcv() - Receive packets from a net device + * @skb: The received packet + * @netdev: The net device that the packet was received on + * @ptype: The packet type context + * @olddev: The last device net device * - * this function will receive the packet and build fc frame and pass it up + * This routine is called by NET_RX_SOFTIRQ. It receives a packet, builds a + * FC frame and passes the frame to libfc. * * Returns: 0 for success */ -int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, +int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, struct packet_type *ptype, struct net_device *olddev) { - struct fc_lport *lp; + struct fc_lport *lport; struct fcoe_rcv_info *fr; struct fcoe_interface *fcoe; struct fc_frame_header *fh; @@ -988,15 +1172,15 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, unsigned int cpu; fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); - lp = fcoe->ctlr.lp; - if (unlikely(lp == NULL)) { - FCOE_NETDEV_DBG(dev, "Cannot find hba structure"); + lport = fcoe->ctlr.lp; + if (unlikely(!lport)) { + FCOE_NETDEV_DBG(netdev, "Cannot find hba structure"); goto err2; } - if (!lp->link_up) + if (!lport->link_up) goto err2; - FCOE_NETDEV_DBG(dev, "skb_info: len:%d data_len:%d head:%p " + FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p " "data:%p tail:%p end:%p sum:%d dev:%s", skb->len, skb->data_len, skb->head, skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), @@ -1004,7 +1188,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, /* check for FCOE packet type */ if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { - FCOE_NETDEV_DBG(dev, "Wrong FC type frame"); + FCOE_NETDEV_DBG(netdev, "Wrong FC type frame"); goto err; } @@ -1013,14 +1197,14 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, * and FC headers are pulled into the linear data area. */ if (unlikely((skb->len < FCOE_MIN_FRAME) || - !pskb_may_pull(skb, FCOE_HEADER_LEN))) + !pskb_may_pull(skb, FCOE_HEADER_LEN))) goto err; skb_set_transport_header(skb, sizeof(struct fcoe_hdr)); fh = (struct fc_frame_header *) skb_transport_header(skb); fr = fcoe_dev_from_skb(skb); - fr->fr_dev = lp; + fr->fr_dev = lport; fr->ptype = ptype; /* @@ -1042,7 +1226,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, * the first CPU now. For non-SMP systems this * will check the same CPU twice. */ - FCOE_NETDEV_DBG(dev, "CPU is online, but no receive thread " + FCOE_NETDEV_DBG(netdev, "CPU is online, but no receive thread " "ready for incoming skb- using first online " "CPU.\n"); @@ -1061,15 +1245,29 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, * this skb. We also have this receive thread locked, * so we're free to queue skbs into it's queue. */ - __skb_queue_tail(&fps->fcoe_rx_list, skb); - if (fps->fcoe_rx_list.qlen == 1) - wake_up_process(fps->thread); - spin_unlock_bh(&fps->fcoe_rx_list.lock); + /* If this is a SCSI-FCP frame, and this is already executing on the + * correct CPU, and the queue for this CPU is empty, then go ahead + * and process the frame directly in the softirq context. + * This lets us process completions without context switching from the + * NET_RX softirq, to our receive processing thread, and then back to + * BLOCK softirq context. + */ + if (fh->fh_type == FC_TYPE_FCP && + cpu == smp_processor_id() && + skb_queue_empty(&fps->fcoe_rx_list)) { + spin_unlock_bh(&fps->fcoe_rx_list.lock); + fcoe_recv_frame(skb); + } else { + __skb_queue_tail(&fps->fcoe_rx_list, skb); + if (fps->fcoe_rx_list.qlen == 1) + wake_up_process(fps->thread); + spin_unlock_bh(&fps->fcoe_rx_list.lock); + } return 0; err: - fc_lport_get_stats(lp)->ErrorFrames++; + fc_lport_get_stats(lport)->ErrorFrames++; err2: kfree_skb(skb); @@ -1077,17 +1275,21 @@ err2: } /** - * fcoe_start_io() - pass to netdev to start xmit for fcoe - * @skb: the skb to be xmitted + * fcoe_start_io() - Start FCoE I/O + * @skb: The packet to be transmitted + * + * This routine is called from the net device to start transmitting + * FCoE packets. * * Returns: 0 for success */ static inline int fcoe_start_io(struct sk_buff *skb) { + struct sk_buff *nskb; int rc; - skb_get(skb); - rc = dev_queue_xmit(skb); + nskb = skb_clone(skb, GFP_ATOMIC); + rc = dev_queue_xmit(nskb); if (rc != 0) return rc; kfree_skb(skb); @@ -1095,9 +1297,15 @@ static inline int fcoe_start_io(struct sk_buff *skb) } /** - * fcoe_get_paged_crc_eof() - in case we need to alloc a page for crc_eof - * @skb: the skb to be xmitted - * @tlen: total len + * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC + * @skb: The packet to be transmitted + * @tlen: The total length of the trailer + * + * This routine allocates a page for frame trailers. The page is re-used if + * there is enough room left on it for the current trailer. If there isn't + * enough buffer left a new page is allocated for the trailer. Reference to + * the page from this function as well as the skbs using the page fragments + * ensure that the page is freed at the appropriate time. * * Returns: 0 for success */ @@ -1136,11 +1344,12 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen) } /** - * fcoe_fc_crc() - calculates FC CRC in this fcoe skb - * @fp: the fc_frame containing data to be checksummed + * fcoe_fc_crc() - Calculates the CRC for a given frame + * @fp: The frame to be checksumed + * + * This uses crc32() routine to calculate the CRC for a frame * - * This uses crc32() to calculate the crc for port frame - * Return : 32 bit crc + * Return: The 32 bit CRC value */ u32 fcoe_fc_crc(struct fc_frame *fp) { @@ -1171,13 +1380,13 @@ u32 fcoe_fc_crc(struct fc_frame *fp) } /** - * fcoe_xmit() - FCoE frame transmit function - * @lp: the associated local fcoe - * @fp: the fc_frame to be transmitted + * fcoe_xmit() - Transmit a FCoE frame + * @lport: The local port that the frame is to be transmitted for + * @fp: The frame to be transmitted * - * Return : 0 for success + * Return: 0 for success */ -int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) +int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) { int wlen; u32 crc; @@ -1189,7 +1398,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) unsigned int hlen; /* header length implies the version */ unsigned int tlen; /* trailer length */ unsigned int elen; /* eth header, may include vlan */ - struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->fcoe; u8 sof, eof; struct fcoe_hdr *hp; @@ -1200,13 +1409,13 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb = fp_skb(fp); wlen = skb->len / FCOE_WORD_TO_BYTE; - if (!lp->link_up) { + if (!lport->link_up) { kfree_skb(skb); return 0; } if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && - fcoe_ctlr_els_send(&fcoe->ctlr, skb)) + fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb)) return 0; sof = fr_sof(fp); @@ -1218,7 +1427,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE; /* crc offload */ - if (likely(lp->crc_offload)) { + if (likely(lport->crc_offload)) { skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_headroom(skb); skb->csum_offset = skb->len; @@ -1271,7 +1480,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN)) memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN); else - memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN); + memcpy(eh->h_source, port->data_src_addr, ETH_ALEN); hp = (struct fcoe_hdr *)(eh + 1); memset(hp, 0, sizeof(*hp)); @@ -1280,7 +1489,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) hp->fcoe_sof = sof; /* fcoe lso, mss is in max_payload which is non-zero for FCP data */ - if (lp->seq_offload && fr_max_payload(fp)) { + if (lport->seq_offload && fr_max_payload(fp)) { skb_shinfo(skb)->gso_type = SKB_GSO_FCOE; skb_shinfo(skb)->gso_size = fr_max_payload(fp); } else { @@ -1288,23 +1497,23 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb_shinfo(skb)->gso_size = 0; } /* update tx stats: regardless if LLD fails */ - stats = fc_lport_get_stats(lp); + stats = fc_lport_get_stats(lport); stats->TxFrames++; stats->TxWords += wlen; /* send down to lld */ - fr_dev(fp) = lp; + fr_dev(fp) = lport; if (port->fcoe_pending_queue.qlen) - fcoe_check_wait_queue(lp, skb); + fcoe_check_wait_queue(lport, skb); else if (fcoe_start_io(skb)) - fcoe_check_wait_queue(lp, skb); + fcoe_check_wait_queue(lport, skb); return 0; } /** - * fcoe_percpu_flush_done() - Indicate percpu queue flush completion. - * @skb: the skb being completed. + * fcoe_percpu_flush_done() - Indicate per-CPU queue flush completion + * @skb: The completed skb (argument required by destructor) */ static void fcoe_percpu_flush_done(struct sk_buff *skb) { @@ -1312,26 +1521,134 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb) } /** - * fcoe_percpu_receive_thread() - recv thread per cpu - * @arg: ptr to the fcoe per cpu struct - * - * Return: 0 for success + * fcoe_recv_frame() - process a single received frame + * @skb: frame to process */ -int fcoe_percpu_receive_thread(void *arg) +static void fcoe_recv_frame(struct sk_buff *skb) { - struct fcoe_percpu_s *p = arg; u32 fr_len; - struct fc_lport *lp; + struct fc_lport *lport; struct fcoe_rcv_info *fr; struct fcoe_dev_stats *stats; struct fc_frame_header *fh; - struct sk_buff *skb; struct fcoe_crc_eof crc_eof; struct fc_frame *fp; u8 *mac = NULL; struct fcoe_port *port; struct fcoe_hdr *hp; + fr = fcoe_dev_from_skb(skb); + lport = fr->fr_dev; + if (unlikely(!lport)) { + if (skb->destructor != fcoe_percpu_flush_done) + FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb"); + kfree_skb(skb); + return; + } + + FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " + "head:%p data:%p tail:%p end:%p sum:%d dev:%s", + skb->len, skb->data_len, + skb->head, skb->data, skb_tail_pointer(skb), + skb_end_pointer(skb), skb->csum, + skb->dev ? skb->dev->name : "<NULL>"); + + /* + * Save source MAC address before discarding header. + */ + port = lport_priv(lport); + if (skb_is_nonlinear(skb)) + skb_linearize(skb); /* not ideal */ + mac = eth_hdr(skb)->h_source; + + /* + * Frame length checks and setting up the header pointers + * was done in fcoe_rcv already. + */ + hp = (struct fcoe_hdr *) skb_network_header(skb); + fh = (struct fc_frame_header *) skb_transport_header(skb); + + stats = fc_lport_get_stats(lport); + if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { + if (stats->ErrorFrames < 5) + printk(KERN_WARNING "fcoe: FCoE version " + "mismatch: The frame has " + "version %x, but the " + "initiator supports version " + "%x\n", FC_FCOE_DECAPS_VER(hp), + FC_FCOE_VER); + stats->ErrorFrames++; + kfree_skb(skb); + return; + } + + skb_pull(skb, sizeof(struct fcoe_hdr)); + fr_len = skb->len - sizeof(struct fcoe_crc_eof); + + stats->RxFrames++; + stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; + + fp = (struct fc_frame *)skb; + fc_frame_init(fp); + fr_dev(fp) = lport; + fr_sof(fp) = hp->fcoe_sof; + + /* Copy out the CRC and EOF trailer for access */ + if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { + kfree_skb(skb); + return; + } + fr_eof(fp) = crc_eof.fcoe_eof; + fr_crc(fp) = crc_eof.fcoe_crc32; + if (pskb_trim(skb, fr_len)) { + kfree_skb(skb); + return; + } + + /* + * We only check CRC if no offload is available and if it is + * it's solicited data, in which case, the FCP layer would + * check it during the copy. + */ + if (lport->crc_offload && + skb->ip_summed == CHECKSUM_UNNECESSARY) + fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; + else + fr_flags(fp) |= FCPHF_CRC_UNCHECKED; + + fh = fc_frame_header_get(fp); + if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && + fh->fh_type == FC_TYPE_FCP) { + fc_exch_recv(lport, fp); + return; + } + if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { + if (le32_to_cpu(fr_crc(fp)) != + ~crc32(~0, skb->data, fr_len)) { + if (stats->InvalidCRCCount < 5) + printk(KERN_WARNING "fcoe: dropping " + "frame with CRC error\n"); + stats->InvalidCRCCount++; + stats->ErrorFrames++; + fc_frame_free(fp); + return; + } + fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; + } + fc_exch_recv(lport, fp); +} + +/** + * fcoe_percpu_receive_thread() - The per-CPU packet receive thread + * @arg: The per-CPU context + * + * Return: 0 for success + */ +int fcoe_percpu_receive_thread(void *arg) +{ + struct fcoe_percpu_s *p = arg; + struct sk_buff *skb; + set_user_nice(current, -20); while (!kthread_should_stop()) { @@ -1347,129 +1664,27 @@ int fcoe_percpu_receive_thread(void *arg) spin_lock_bh(&p->fcoe_rx_list.lock); } spin_unlock_bh(&p->fcoe_rx_list.lock); - fr = fcoe_dev_from_skb(skb); - lp = fr->fr_dev; - if (unlikely(lp == NULL)) { - if (skb->destructor != fcoe_percpu_flush_done) - FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb"); - kfree_skb(skb); - continue; - } - - FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " - "head:%p data:%p tail:%p end:%p sum:%d dev:%s", - skb->len, skb->data_len, - skb->head, skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->csum, - skb->dev ? skb->dev->name : "<NULL>"); - - /* - * Save source MAC address before discarding header. - */ - port = lport_priv(lp); - if (skb_is_nonlinear(skb)) - skb_linearize(skb); /* not ideal */ - mac = eth_hdr(skb)->h_source; - - /* - * Frame length checks and setting up the header pointers - * was done in fcoe_rcv already. - */ - hp = (struct fcoe_hdr *) skb_network_header(skb); - fh = (struct fc_frame_header *) skb_transport_header(skb); - - stats = fc_lport_get_stats(lp); - if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { - if (stats->ErrorFrames < 5) - printk(KERN_WARNING "fcoe: FCoE version " - "mismatch: The frame has " - "version %x, but the " - "initiator supports version " - "%x\n", FC_FCOE_DECAPS_VER(hp), - FC_FCOE_VER); - stats->ErrorFrames++; - kfree_skb(skb); - continue; - } - - skb_pull(skb, sizeof(struct fcoe_hdr)); - fr_len = skb->len - sizeof(struct fcoe_crc_eof); - - stats->RxFrames++; - stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; - - fp = (struct fc_frame *)skb; - fc_frame_init(fp); - fr_dev(fp) = lp; - fr_sof(fp) = hp->fcoe_sof; - - /* Copy out the CRC and EOF trailer for access */ - if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { - kfree_skb(skb); - continue; - } - fr_eof(fp) = crc_eof.fcoe_eof; - fr_crc(fp) = crc_eof.fcoe_crc32; - if (pskb_trim(skb, fr_len)) { - kfree_skb(skb); - continue; - } - - /* - * We only check CRC if no offload is available and if it is - * it's solicited data, in which case, the FCP layer would - * check it during the copy. - */ - if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY) - fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; - else - fr_flags(fp) |= FCPHF_CRC_UNCHECKED; - - fh = fc_frame_header_get(fp); - if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && - fh->fh_type == FC_TYPE_FCP) { - fc_exch_recv(lp, fp); - continue; - } - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { - if (le32_to_cpu(fr_crc(fp)) != - ~crc32(~0, skb->data, fr_len)) { - if (stats->InvalidCRCCount < 5) - printk(KERN_WARNING "fcoe: dropping " - "frame with CRC error\n"); - stats->InvalidCRCCount++; - stats->ErrorFrames++; - fc_frame_free(fp); - continue; - } - fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; - } - if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) && - fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) { - fc_frame_free(fp); - continue; - } - fc_exch_recv(lp, fp); + fcoe_recv_frame(skb); } return 0; } /** - * fcoe_check_wait_queue() - attempt to clear the transmit backlog - * @lp: the fc_lport + * fcoe_check_wait_queue() - Attempt to clear the transmit backlog + * @lport: The local port whose backlog is to be cleared * - * This empties the wait_queue, dequeue the head of the wait_queue queue - * and calls fcoe_start_io() for each packet, if all skb have been - * transmitted, return qlen or -1 if a error occurs, then restore - * wait_queue and try again later. + * This empties the wait_queue, dequeues the head of the wait_queue queue + * and calls fcoe_start_io() for each packet. If all skb have been + * transmitted it returns the qlen. If an error occurs it restores + * wait_queue (to try again later) and returns -1. * - * The wait_queue is used when the skb transmit fails. skb will go - * in the wait_queue which will be emptied by the timer function or + * The wait_queue is used when the skb transmit fails. The failed skb + * will go in the wait_queue which will be emptied by the timer function or * by the next skb transmit. */ -static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb) +static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) { - struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *port = lport_priv(lport); int rc; spin_lock_bh(&port->fcoe_pending_queue.lock); @@ -1501,19 +1716,19 @@ static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb) } if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) - lp->qfull = 0; + lport->qfull = 0; if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) mod_timer(&port->timer, jiffies + 2); port->fcoe_pending_queue_active = 0; out: if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) - lp->qfull = 1; + lport->qfull = 1; spin_unlock_bh(&port->fcoe_pending_queue.lock); return; } /** - * fcoe_dev_setup() - setup link change notification interface + * fcoe_dev_setup() - Setup the link change notification interface */ static void fcoe_dev_setup(void) { @@ -1521,7 +1736,7 @@ static void fcoe_dev_setup(void) } /** - * fcoe_dev_cleanup() - cleanup link change notification interface + * fcoe_dev_cleanup() - Cleanup the link change notification interface */ static void fcoe_dev_cleanup(void) { @@ -1529,19 +1744,19 @@ static void fcoe_dev_cleanup(void) } /** - * fcoe_device_notification() - netdev event notification callback - * @notifier: context of the notification - * @event: type of event - * @ptr: fixed array for output parsed ifname + * fcoe_device_notification() - Handler for net device events + * @notifier: The context of the notification + * @event: The type of event + * @ptr: The net device that the event was on * - * This function is called by the ethernet driver in case of link change event + * This function is called by the Ethernet driver in case of link change event. * * Returns: 0 for success */ static int fcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr) { - struct fc_lport *lp = NULL; + struct fc_lport *lport = NULL; struct net_device *netdev = ptr; struct fcoe_interface *fcoe; struct fcoe_port *port; @@ -1552,11 +1767,11 @@ static int fcoe_device_notification(struct notifier_block *notifier, list_for_each_entry(fcoe, &fcoe_hostlist, list) { if (fcoe->netdev == netdev) { - lp = fcoe->ctlr.lp; + lport = fcoe->ctlr.lp; break; } } - if (lp == NULL) { + if (!lport) { rc = NOTIFY_DONE; goto out; } @@ -1570,10 +1785,12 @@ static int fcoe_device_notification(struct notifier_block *notifier, case NETDEV_CHANGE: break; case NETDEV_CHANGEMTU: + if (netdev->features & NETIF_F_FCOE_MTU) + break; mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof)); if (mfs >= FC_MIN_MAX_FRAME) - fc_set_mfs(lp, mfs); + fc_set_mfs(lport, mfs); break; case NETDEV_REGISTER: break; @@ -1588,22 +1805,22 @@ static int fcoe_device_notification(struct notifier_block *notifier, FCOE_NETDEV_DBG(netdev, "Unknown event %ld " "from netdev netlink\n", event); } - if (link_possible && !fcoe_link_ok(lp)) + if (link_possible && !fcoe_link_ok(lport)) fcoe_ctlr_link_up(&fcoe->ctlr); else if (fcoe_ctlr_link_down(&fcoe->ctlr)) { - stats = fc_lport_get_stats(lp); + stats = fc_lport_get_stats(lport); stats->LinkFailureCount++; - fcoe_clean_pending_queue(lp); + fcoe_clean_pending_queue(lport); } out: return rc; } /** - * fcoe_if_to_netdev() - parse a name buffer to get netdev - * @buffer: incoming buffer to be copied + * fcoe_if_to_netdev() - Parse a name buffer to get a net device + * @buffer: The name of the net device * - * Returns: NULL or ptr to net_device + * Returns: NULL or a ptr to net_device */ static struct net_device *fcoe_if_to_netdev(const char *buffer) { @@ -1621,9 +1838,11 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer) } /** - * fcoe_destroy() - handles the destroy from sysfs - * @buffer: expected to be an eth if name - * @kp: associated kernel param + * fcoe_destroy() - Destroy a FCoE interface + * @buffer: The name of the Ethernet interface to be destroyed + * @kp: The associated kernel parameter + * + * Called from sysfs. * * Returns: 0 for success */ @@ -1631,7 +1850,7 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) { struct fcoe_interface *fcoe; struct net_device *netdev; - int rc; + int rc = 0; mutex_lock(&fcoe_config_mutex); #ifdef CONFIG_FCOE_MODULE @@ -1670,6 +1889,10 @@ out_nodev: return rc; } +/** + * fcoe_destroy_work() - Destroy a FCoE port in a deferred work context + * @work: Handle to the FCoE port to be destroyed + */ static void fcoe_destroy_work(struct work_struct *work) { struct fcoe_port *port; @@ -1681,9 +1904,11 @@ static void fcoe_destroy_work(struct work_struct *work) } /** - * fcoe_create() - Handles the create call from sysfs - * @buffer: expected to be an eth if name - * @kp: associated kernel param + * fcoe_create() - Create a fcoe interface + * @buffer: The name of the Ethernet interface to create on + * @kp: The associated kernel param + * + * Called from sysfs. * * Returns: 0 for success */ @@ -1726,7 +1951,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) goto out_putdev; } - lport = fcoe_if_create(fcoe, &netdev->dev); + lport = fcoe_if_create(fcoe, &netdev->dev, 0); if (IS_ERR(lport)) { printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", netdev->name); @@ -1762,16 +1987,9 @@ out_nodev: return rc; } -module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(create, "string"); -MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in."); -module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(destroy, "string"); -MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); - /** - * fcoe_link_ok() - Check if link is ok for the fc_lport - * @lp: ptr to the fc_lport + * fcoe_link_ok() - Check if the link is OK for a local port + * @lport: The local port to check link on * * Any permanently-disqualifying conditions have been previously checked. * This also updates the speed setting, which may change with link for 100/1000. @@ -1783,26 +2001,26 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); * Returns: 0 if link is OK for use by FCoE. * */ -int fcoe_link_ok(struct fc_lport *lp) +int fcoe_link_ok(struct fc_lport *lport) { - struct fcoe_port *port = lport_priv(lp); - struct net_device *dev = port->fcoe->netdev; + struct fcoe_port *port = lport_priv(lport); + struct net_device *netdev = port->fcoe->netdev; struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && - (!dev_ethtool_get_settings(dev, &ecmd))) { - lp->link_supported_speeds &= + if ((netdev->flags & IFF_UP) && netif_carrier_ok(netdev) && + (!dev_ethtool_get_settings(netdev, &ecmd))) { + lport->link_supported_speeds &= ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); if (ecmd.supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) - lp->link_supported_speeds |= FC_PORTSPEED_1GBIT; + lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; if (ecmd.supported & SUPPORTED_10000baseT_Full) - lp->link_supported_speeds |= + lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; if (ecmd.speed == SPEED_1000) - lp->link_speed = FC_PORTSPEED_1GBIT; + lport->link_speed = FC_PORTSPEED_1GBIT; if (ecmd.speed == SPEED_10000) - lp->link_speed = FC_PORTSPEED_10GBIT; + lport->link_speed = FC_PORTSPEED_10GBIT; return 0; } @@ -1810,8 +2028,8 @@ int fcoe_link_ok(struct fc_lport *lp) } /** - * fcoe_percpu_clean() - Clear the pending skbs for an lport - * @lp: the fc_lport + * fcoe_percpu_clean() - Clear all pending skbs for an local port + * @lport: The local port whose skbs are to be cleared * * Must be called with fcoe_create_mutex held to single-thread completion. * @@ -1820,7 +2038,7 @@ int fcoe_link_ok(struct fc_lport *lp) * there no packets that will be handled by the lport, but also that any * threads already handling packet have returned. */ -void fcoe_percpu_clean(struct fc_lport *lp) +void fcoe_percpu_clean(struct fc_lport *lport) { struct fcoe_percpu_s *pp; struct fcoe_rcv_info *fr; @@ -1838,7 +2056,7 @@ void fcoe_percpu_clean(struct fc_lport *lp) skb = next) { next = skb->next; fr = fcoe_dev_from_skb(skb); - if (fr->fr_dev == lp) { + if (fr->fr_dev == lport) { __skb_unlink(skb, list); kfree_skb(skb); } @@ -1867,13 +2085,11 @@ void fcoe_percpu_clean(struct fc_lport *lp) /** * fcoe_clean_pending_queue() - Dequeue a skb and free it - * @lp: the corresponding fc_lport - * - * Returns: none + * @lport: The local port to dequeue a skb on */ -void fcoe_clean_pending_queue(struct fc_lport *lp) +void fcoe_clean_pending_queue(struct fc_lport *lport) { - struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *port = lport_priv(lport); struct sk_buff *skb; spin_lock_bh(&port->fcoe_pending_queue.lock); @@ -1886,10 +2102,10 @@ void fcoe_clean_pending_queue(struct fc_lport *lp) } /** - * fcoe_reset() - Resets the fcoe - * @shost: shost the reset is from + * fcoe_reset() - Reset a local port + * @shost: The SCSI host associated with the local port to be reset * - * Returns: always 0 + * Returns: Always 0 (return value required by FC transport template) */ int fcoe_reset(struct Scsi_Host *shost) { @@ -1899,30 +2115,33 @@ int fcoe_reset(struct Scsi_Host *shost) } /** - * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device - * @dev: this is currently ptr to net_device + * fcoe_hostlist_lookup_port() - Find the FCoE interface associated with a net device + * @netdev: The net device used as a key + * + * Locking: Must be called with the RNL mutex held. * - * Returns: NULL or the located fcoe_port - * Locking: must be called with the RNL mutex held + * Returns: NULL or the FCoE interface */ static struct fcoe_interface * -fcoe_hostlist_lookup_port(const struct net_device *dev) +fcoe_hostlist_lookup_port(const struct net_device *netdev) { struct fcoe_interface *fcoe; list_for_each_entry(fcoe, &fcoe_hostlist, list) { - if (fcoe->netdev == dev) + if (fcoe->netdev == netdev) return fcoe; } return NULL; } /** - * fcoe_hostlist_lookup() - Find the corresponding lport by netdev - * @netdev: ptr to net_device + * fcoe_hostlist_lookup() - Find the local port associated with a + * given net device + * @netdev: The netdevice used as a key * - * Returns: 0 for success - * Locking: must be called with the RTNL mutex held + * Locking: Must be called with the RTNL mutex held + * + * Returns: NULL or the local port */ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) { @@ -1933,11 +2152,13 @@ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) } /** - * fcoe_hostlist_add() - Add a lport to lports list - * @lp: ptr to the fc_lport to be added + * fcoe_hostlist_add() - Add the FCoE interface identified by a local + * port to the hostlist + * @lport: The local port that identifies the FCoE interface to be added * - * Returns: 0 for success * Locking: must be called with the RTNL mutex held + * + * Returns: 0 for success */ static int fcoe_hostlist_add(const struct fc_lport *lport) { @@ -1954,15 +2175,15 @@ static int fcoe_hostlist_add(const struct fc_lport *lport) } /** - * fcoe_init() - fcoe module loading initialization + * fcoe_init() - Initialize fcoe.ko * - * Returns 0 on success, negative on failure + * Returns: 0 on success, or a negative value on failure */ static int __init fcoe_init(void) { + struct fcoe_percpu_s *p; unsigned int cpu; int rc = 0; - struct fcoe_percpu_s *p; mutex_lock(&fcoe_config_mutex); @@ -1999,15 +2220,15 @@ out_free: module_init(fcoe_init); /** - * fcoe_exit() - fcoe module unloading cleanup + * fcoe_exit() - Clean up fcoe.ko * - * Returns 0 on success, negative on failure + * Returns: 0 on success or a negative value on failure */ static void __exit fcoe_exit(void) { - unsigned int cpu; struct fcoe_interface *fcoe, *tmp; struct fcoe_port *port; + unsigned int cpu; mutex_lock(&fcoe_config_mutex); @@ -2033,9 +2254,238 @@ static void __exit fcoe_exit(void) /* flush any asyncronous interface destroys, * this should happen after the netdev notifier is unregistered */ flush_scheduled_work(); + /* That will flush out all the N_Ports on the hostlist, but now we + * may have NPIV VN_Ports scheduled for destruction */ + flush_scheduled_work(); /* detach from scsi transport * must happen after all destroys are done, therefor after the flush */ fcoe_if_exit(); } module_exit(fcoe_exit); + +/** + * fcoe_flogi_resp() - FCoE specific FLOGI and FDISC response handler + * @seq: active sequence in the FLOGI or FDISC exchange + * @fp: response frame, or error encoded in a pointer (timeout) + * @arg: pointer the the fcoe_ctlr structure + * + * This handles MAC address managment for FCoE, then passes control on to + * the libfc FLOGI response handler. + */ +static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) +{ + struct fcoe_ctlr *fip = arg; + struct fc_exch *exch = fc_seq_exch(seq); + struct fc_lport *lport = exch->lp; + u8 *mac; + + if (IS_ERR(fp)) + goto done; + + mac = fr_cb(fp)->granted_mac; + if (is_zero_ether_addr(mac)) { + /* pre-FIP */ + if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { + fc_frame_free(fp); + return; + } + } + fcoe_update_src_mac(lport, mac); +done: + fc_lport_flogi_resp(seq, fp, lport); +} + +/** + * fcoe_logo_resp() - FCoE specific LOGO response handler + * @seq: active sequence in the LOGO exchange + * @fp: response frame, or error encoded in a pointer (timeout) + * @arg: pointer the the fcoe_ctlr structure + * + * This handles MAC address managment for FCoE, then passes control on to + * the libfc LOGO response handler. + */ +static void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) +{ + struct fc_lport *lport = arg; + static u8 zero_mac[ETH_ALEN] = { 0 }; + + if (!IS_ERR(fp)) + fcoe_update_src_mac(lport, zero_mac); + fc_lport_logo_resp(seq, fp, lport); +} + +/** + * fcoe_elsct_send - FCoE specific ELS handler + * + * This does special case handling of FIP encapsualted ELS exchanges for FCoE, + * using FCoE specific response handlers and passing the FIP controller as + * the argument (the lport is still available from the exchange). + * + * Most of the work here is just handed off to the libfc routine. + */ +static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did, + struct fc_frame *fp, unsigned int op, + void (*resp)(struct fc_seq *, + struct fc_frame *, + void *), + void *arg, u32 timeout) +{ + struct fcoe_port *port = lport_priv(lport); + struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_ctlr *fip = &fcoe->ctlr; + struct fc_frame_header *fh = fc_frame_header_get(fp); + + switch (op) { + case ELS_FLOGI: + case ELS_FDISC: + return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp, + fip, timeout); + case ELS_LOGO: + /* only hook onto fabric logouts, not port logouts */ + if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) + break; + return fc_elsct_send(lport, did, fp, op, fcoe_logo_resp, + lport, timeout); + } + return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); +} + +/** + * fcoe_vport_create() - create an fc_host/scsi_host for a vport + * @vport: fc_vport object to create a new fc_host for + * @disabled: start the new fc_host in a disabled state by default? + * + * Returns: 0 for success + */ +static int fcoe_vport_create(struct fc_vport *vport, bool disabled) +{ + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + struct fcoe_port *port = lport_priv(n_port); + struct fcoe_interface *fcoe = port->fcoe; + struct net_device *netdev = fcoe->netdev; + struct fc_lport *vn_port; + + mutex_lock(&fcoe_config_mutex); + vn_port = fcoe_if_create(fcoe, &vport->dev, 1); + mutex_unlock(&fcoe_config_mutex); + + if (IS_ERR(vn_port)) { + printk(KERN_ERR "fcoe: fcoe_vport_create(%s) failed\n", + netdev->name); + return -EIO; + } + + if (disabled) { + fc_vport_set_state(vport, FC_VPORT_DISABLED); + } else { + vn_port->boot_time = jiffies; + fc_fabric_login(vn_port); + fc_vport_setlink(vn_port); + } + return 0; +} + +/** + * fcoe_vport_destroy() - destroy the fc_host/scsi_host for a vport + * @vport: fc_vport object that is being destroyed + * + * Returns: 0 for success + */ +static int fcoe_vport_destroy(struct fc_vport *vport) +{ + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + struct fc_lport *vn_port = vport->dd_data; + struct fcoe_port *port = lport_priv(vn_port); + + mutex_lock(&n_port->lp_mutex); + list_del(&vn_port->list); + mutex_unlock(&n_port->lp_mutex); + schedule_work(&port->destroy_work); + return 0; +} + +/** + * fcoe_vport_disable() - change vport state + * @vport: vport to bring online/offline + * @disable: should the vport be disabled? + */ +static int fcoe_vport_disable(struct fc_vport *vport, bool disable) +{ + struct fc_lport *lport = vport->dd_data; + + if (disable) { + fc_vport_set_state(vport, FC_VPORT_DISABLED); + fc_fabric_logoff(lport); + } else { + lport->boot_time = jiffies; + fc_fabric_login(lport); + fc_vport_setlink(lport); + } + + return 0; +} + +/** + * fcoe_vport_set_symbolic_name() - append vport string to symbolic name + * @vport: fc_vport with a new symbolic name string + * + * After generating a new symbolic name string, a new RSPN_ID request is + * sent to the name server. There is no response handler, so if it fails + * for some reason it will not be retried. + */ +static void fcoe_set_vport_symbolic_name(struct fc_vport *vport) +{ + struct fc_lport *lport = vport->dd_data; + struct fc_frame *fp; + size_t len; + + snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE, + "%s v%s over %s : %s", FCOE_NAME, FCOE_VERSION, + fcoe_netdev(lport)->name, vport->symbolic_name); + + if (lport->state != LPORT_ST_READY) + return; + + len = strnlen(fc_host_symbolic_name(lport->host), 255); + fp = fc_frame_alloc(lport, + sizeof(struct fc_ct_hdr) + + sizeof(struct fc_ns_rspn) + len); + if (!fp) + return; + lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RSPN_ID, + NULL, NULL, 3 * lport->r_a_tov); +} + +/** + * fcoe_get_lesb() - Fill the FCoE Link Error Status Block + * @lport: the local port + * @fc_lesb: the link error status block + */ +static void fcoe_get_lesb(struct fc_lport *lport, + struct fc_els_lesb *fc_lesb) +{ + unsigned int cpu; + u32 lfc, vlfc, mdac; + struct fcoe_dev_stats *devst; + struct fcoe_fc_els_lesb *lesb; + struct net_device *netdev = fcoe_netdev(lport); + + lfc = 0; + vlfc = 0; + mdac = 0; + lesb = (struct fcoe_fc_els_lesb *)fc_lesb; + memset(lesb, 0, sizeof(*lesb)); + for_each_possible_cpu(cpu) { + devst = per_cpu_ptr(lport->dev_stats, cpu); + lfc += devst->LinkFailureCount; + vlfc += devst->VLinkFailureCount; + mdac += devst->MissDiscAdvCount; + } + lesb->lesb_link_fail = htonl(lfc); + lesb->lesb_vlink_fail = htonl(vlfc); + lesb->lesb_miss_fka = htonl(mdac); + lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors); +} diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index ce7f60fb1bc0..c69b2c56c2d1 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -32,7 +32,7 @@ #define FCOE_NAME "fcoe" #define FCOE_VENDOR "Open-FCoE.org" -#define FCOE_MAX_LUN 255 +#define FCOE_MAX_LUN 0xFFFF #define FCOE_MAX_FCP_TARGET 256 #define FCOE_MAX_OUTSTANDING_COMMANDS 1024 @@ -40,11 +40,17 @@ #define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */ #define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */ +/* + * Max MTU for FCoE: 14 (FCoE header) + 24 (FC header) + 2112 (max FC payload) + * + 4 (FC CRC) + 4 (FCoE trailer) = 2158 bytes + */ +#define FCOE_MTU 2158 + unsigned int fcoe_debug_logging; module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); -#define FCOE_LOGGING 0x01 /* General logging, not categorized */ +#define FCOE_LOGGING 0x01 /* General logging, not categorized */ #define FCOE_NETDEV_LOGGING 0x02 /* Netdevice logging */ #define FCOE_CHECK_LOGGING(LEVEL, CMD) \ @@ -64,8 +70,13 @@ do { \ printk(KERN_INFO "fcoe: %s: " fmt, \ netdev->name, ##args);) -/* - * this percpu struct for fcoe +/** + * struct fcoe_percpu_s - The per-CPU context for FCoE receive threads + * @thread: The thread context + * @fcoe_rx_list: The queue of pending packets to process + * @page: The memory page for calculating frame trailer CRCs + * @crc_eof_offset: The offset into the CRC page pointing to available + * memory for a new trailer */ struct fcoe_percpu_s { struct task_struct *thread; @@ -74,37 +85,62 @@ struct fcoe_percpu_s { int crc_eof_offset; }; -/* - * an FCoE interface, 1:1 with netdev +/** + * struct fcoe_interface - A FCoE interface + * @list: Handle for a list of FCoE interfaces + * @netdev: The associated net device + * @fcoe_packet_type: FCoE packet type + * @fip_packet_type: FIP packet type + * @ctlr: The FCoE controller (for FIP) + * @oem: The offload exchange manager for all local port + * instances associated with this port + * @kref: The kernel reference + * + * This structure is 1:1 with a net devive. */ struct fcoe_interface { - struct list_head list; - struct net_device *netdev; - struct packet_type fcoe_packet_type; - struct packet_type fip_packet_type; - struct fcoe_ctlr ctlr; - struct fc_exch_mgr *oem; /* offload exchange manager */ - struct kref kref; + struct list_head list; + struct net_device *netdev; + struct packet_type fcoe_packet_type; + struct packet_type fip_packet_type; + struct fcoe_ctlr ctlr; + struct fc_exch_mgr *oem; + struct kref kref; }; -/* - * the FCoE private structure that's allocated along with the - * Scsi_Host and libfc fc_lport structures +/** + * struct fcoe_port - The FCoE private structure + * @fcoe: The associated fcoe interface + * @lport: The associated local port + * @fcoe_pending_queue: The pending Rx queue of skbs + * @fcoe_pending_queue_active: Indicates if the pending queue is active + * @timer: The queue timer + * @destroy_work: Handle for work context + * (to prevent RTNL deadlocks) + * @data_srt_addr: Source address for data + * + * An instance of this structure is to be allocated along with the + * Scsi_Host and libfc fc_lport structures. */ struct fcoe_port { struct fcoe_interface *fcoe; - struct fc_lport *lport; - struct sk_buff_head fcoe_pending_queue; - u8 fcoe_pending_queue_active; - struct timer_list timer; /* queue timer */ - struct work_struct destroy_work; /* to prevent rtnl deadlocks */ + struct fc_lport *lport; + struct sk_buff_head fcoe_pending_queue; + u8 fcoe_pending_queue_active; + struct timer_list timer; + struct work_struct destroy_work; + u8 data_src_addr[ETH_ALEN]; }; #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr) -static inline struct net_device *fcoe_netdev(const struct fc_lport *lp) +/** + * fcoe_netdev() - Return the net device associated with a local port + * @lport: The local port to get the net device from + */ +static inline struct net_device *fcoe_netdev(const struct fc_lport *lport) { - return ((struct fcoe_port *)lport_priv(lp))->fcoe->netdev; + return ((struct fcoe_port *)lport_priv(lport))->fcoe->netdev; } #endif /* _FCOE_H_ */ diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 11ae5c94608b..9823291395ad 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -59,26 +59,30 @@ unsigned int libfcoe_debug_logging; module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); -#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ +#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ #define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */ -#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \ -do { \ - if (unlikely(libfcoe_debug_logging & LEVEL)) \ - do { \ - CMD; \ - } while (0); \ +#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \ +do { \ + if (unlikely(libfcoe_debug_logging & LEVEL)) \ + do { \ + CMD; \ + } while (0); \ } while (0) #define LIBFCOE_DBG(fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \ printk(KERN_INFO "libfcoe: " fmt, ##args);) -#define LIBFCOE_FIP_DBG(fmt, args...) \ +#define LIBFCOE_FIP_DBG(fip, fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \ - printk(KERN_INFO "fip: " fmt, ##args);) + printk(KERN_INFO "host%d: fip: " fmt, \ + (fip)->lp->host->host_no, ##args);) -/* +/** + * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid + * @fcf: The FCF to check + * * Return non-zero if FCF fcoe_size has been validated. */ static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf) @@ -86,7 +90,10 @@ static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf) return (fcf->flags & FIP_FL_SOL) != 0; } -/* +/** + * fcoe_ctlr_fcf_usable() - Check if a FCF is usable + * @fcf: The FCF to check + * * Return non-zero if the FCF is usable. */ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) @@ -97,12 +104,13 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) } /** - * fcoe_ctlr_init() - Initialize the FCoE Controller instance. - * @fip: FCoE controller. + * fcoe_ctlr_init() - Initialize the FCoE Controller instance + * @fip: The FCoE controller to initialize */ void fcoe_ctlr_init(struct fcoe_ctlr *fip) { fip->state = FIP_ST_LINK_WAIT; + fip->mode = FIP_ST_AUTO; INIT_LIST_HEAD(&fip->fcfs); spin_lock_init(&fip->lock); fip->flogi_oxid = FC_XID_UNKNOWN; @@ -114,8 +122,8 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip) EXPORT_SYMBOL(fcoe_ctlr_init); /** - * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller. - * @fip: FCoE controller. + * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller + * @fip: The FCoE controller whose FCFs are to be reset * * Called with &fcoe_ctlr lock held. */ @@ -134,8 +142,8 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip) } /** - * fcoe_ctlr_destroy() - Disable and tear-down the FCoE controller. - * @fip: FCoE controller. + * fcoe_ctlr_destroy() - Disable and tear down a FCoE controller + * @fip: The FCoE controller to tear down * * This is called by FCoE drivers before freeing the &fcoe_ctlr. * @@ -148,9 +156,7 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip) void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) { cancel_work_sync(&fip->recv_work); - spin_lock_bh(&fip->fip_recv_list.lock); - __skb_queue_purge(&fip->fip_recv_list); - spin_unlock_bh(&fip->fip_recv_list.lock); + skb_queue_purge(&fip->fip_recv_list); spin_lock_bh(&fip->lock); fip->state = FIP_ST_DISABLED; @@ -162,8 +168,8 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) EXPORT_SYMBOL(fcoe_ctlr_destroy); /** - * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port. - * @fip: FCoE controller. + * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port + * @fip: The FCoE controller to get the maximum FCoE size from * * Returns the maximum packet size including the FCoE header and trailer, * but not including any Ethernet or VLAN headers. @@ -180,9 +186,9 @@ static inline u32 fcoe_ctlr_fcoe_size(struct fcoe_ctlr *fip) } /** - * fcoe_ctlr_solicit() - Send a solicitation. - * @fip: FCoE controller. - * @fcf: Destination FCF. If NULL, a multicast solicitation is sent. + * fcoe_ctlr_solicit() - Send a FIP solicitation + * @fip: The FCoE controller to send the solicitation on + * @fcf: The destination FCF (if NULL, a multicast solicitation is sent) */ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf) { @@ -241,8 +247,8 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf) } /** - * fcoe_ctlr_link_up() - Start FCoE controller. - * @fip: FCoE controller. + * fcoe_ctlr_link_up() - Start FCoE controller + * @fip: The FCoE controller to start * * Called from the LLD when the network link is ready. */ @@ -255,11 +261,12 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) spin_unlock_bh(&fip->lock); fc_linkup(fip->lp); } else if (fip->state == FIP_ST_LINK_WAIT) { - fip->state = FIP_ST_AUTO; + fip->state = fip->mode; fip->last_link = 1; fip->link = 1; spin_unlock_bh(&fip->lock); - LIBFCOE_FIP_DBG("%s", "setting AUTO mode.\n"); + if (fip->state == FIP_ST_AUTO) + LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n"); fc_linkup(fip->lp); fcoe_ctlr_solicit(fip, NULL); } else @@ -268,45 +275,23 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) EXPORT_SYMBOL(fcoe_ctlr_link_up); /** - * fcoe_ctlr_reset() - Reset FIP. - * @fip: FCoE controller. - * @new_state: FIP state to be entered. - * - * Returns non-zero if the link was up and now isn't. + * fcoe_ctlr_reset() - Reset a FCoE controller + * @fip: The FCoE controller to reset */ -static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state) +static void fcoe_ctlr_reset(struct fcoe_ctlr *fip) { - struct fc_lport *lp = fip->lp; - int link_dropped; - - spin_lock_bh(&fip->lock); fcoe_ctlr_reset_fcfs(fip); del_timer(&fip->timer); - fip->state = new_state; fip->ctlr_ka_time = 0; fip->port_ka_time = 0; fip->sol_time = 0; fip->flogi_oxid = FC_XID_UNKNOWN; fip->map_dest = 0; - fip->last_link = 0; - link_dropped = fip->link; - fip->link = 0; - spin_unlock_bh(&fip->lock); - - if (link_dropped) - fc_linkdown(lp); - - if (new_state == FIP_ST_ENABLED) { - fcoe_ctlr_solicit(fip, NULL); - fc_linkup(lp); - link_dropped = 0; - } - return link_dropped; } /** - * fcoe_ctlr_link_down() - Stop FCoE controller. - * @fip: FCoE controller. + * fcoe_ctlr_link_down() - Stop a FCoE controller + * @fip: The FCoE controller to be stopped * * Returns non-zero if the link was up and now isn't. * @@ -315,15 +300,29 @@ static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state) */ int fcoe_ctlr_link_down(struct fcoe_ctlr *fip) { - return fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT); + int link_dropped; + + LIBFCOE_FIP_DBG(fip, "link down.\n"); + spin_lock_bh(&fip->lock); + fcoe_ctlr_reset(fip); + link_dropped = fip->link; + fip->link = 0; + fip->last_link = 0; + fip->state = FIP_ST_LINK_WAIT; + spin_unlock_bh(&fip->lock); + + if (link_dropped) + fc_linkdown(fip->lp); + return link_dropped; } EXPORT_SYMBOL(fcoe_ctlr_link_down); /** - * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF. - * @fip: FCoE controller. - * @ports: 0 for controller keep-alive, 1 for port keep-alive. - * @sa: source MAC address. + * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF + * @fip: The FCoE controller to send the FKA on + * @lport: libfc fc_lport to send from + * @ports: 0 for controller keep-alive, 1 for port keep-alive + * @sa: The source MAC address * * A controller keep-alive is sent every fka_period (typically 8 seconds). * The source MAC is the native MAC address. @@ -332,7 +331,9 @@ EXPORT_SYMBOL(fcoe_ctlr_link_down); * The source MAC is the assigned mapped source address. * The destination is the FCF's F-port. */ -static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) +static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, + struct fc_lport *lport, + int ports, u8 *sa) { struct sk_buff *skb; struct fip_kal { @@ -350,8 +351,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) if (!fcf || !fc_host_port_id(lp->host)) return; - len = fcoe_ctlr_fcoe_size(fip) + sizeof(struct ethhdr); - BUG_ON(len < sizeof(*kal) + sizeof(*vn)); + len = sizeof(*kal) + ports * sizeof(*vn); skb = dev_alloc_skb(len); if (!skb) return; @@ -366,7 +366,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) kal->fip.fip_op = htons(FIP_OP_CTRL); kal->fip.fip_subcode = FIP_SC_KEEP_ALIVE; kal->fip.fip_dl_len = htons((sizeof(kal->mac) + - ports * sizeof(*vn)) / FIP_BPW); + ports * sizeof(*vn)) / FIP_BPW); kal->fip.fip_flags = htons(FIP_FL_FPMA); if (fip->spma) kal->fip.fip_flags |= htons(FIP_FL_SPMA); @@ -374,16 +374,14 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) kal->mac.fd_desc.fip_dtype = FIP_DT_MAC; kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW; memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN); - if (ports) { vn = (struct fip_vn_desc *)(kal + 1); vn->fd_desc.fip_dtype = FIP_DT_VN_ID; vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW; - memcpy(vn->fd_mac, fip->data_src_addr, ETH_ALEN); + memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN); hton24(vn->fd_fc_id, fc_host_port_id(lp->host)); put_unaligned_be64(lp->wwpn, &vn->fd_wwpn); } - skb_put(skb, len); skb->protocol = htons(ETH_P_FIP); skb_reset_mac_header(skb); @@ -392,10 +390,10 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) } /** - * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it. - * @fip: FCoE controller. - * @dtype: FIP descriptor type for the frame. - * @skb: FCoE ELS frame including FC header but no FCoE headers. + * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it + * @fip: The FCoE controller for the ELS frame + * @dtype: The FIP descriptor type for the frame + * @skb: The FCoE ELS frame including FC header but no FCoE headers * * Returns non-zero error code on failure. * @@ -405,7 +403,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) * Headroom includes the FIP encapsulation description, FIP header, and * Ethernet header. The tailroom is for the FIP MAC descriptor. */ -static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, +static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, u8 dtype, struct sk_buff *skb) { struct fip_encaps_head { @@ -449,8 +447,8 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, memset(mac, 0, sizeof(mac)); mac->fd_desc.fip_dtype = FIP_DT_MAC; mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW; - if (dtype != FIP_DT_FLOGI) - memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN); + if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) + memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN); else if (fip->spma) memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN); @@ -463,6 +461,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, /** * fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate. * @fip: FCoE controller. + * @lport: libfc fc_lport to send from * @skb: FCoE ELS frame including FC header but no FCoE headers. * * Returns a non-zero error code if the frame should not be sent. @@ -471,11 +470,13 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, * The caller must check that the length is a multiple of 4. * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes). */ -int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb) +int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, + struct sk_buff *skb) { struct fc_frame_header *fh; u16 old_xid; u8 op; + u8 mac[ETH_ALEN]; fh = (struct fc_frame_header *)skb->data; op = *(u8 *)(fh + 1); @@ -498,6 +499,8 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb) if (fip->state == FIP_ST_NON_FIP) return 0; + if (!fip->sel_fcf) + goto drop; switch (op) { case ELS_FLOGI: @@ -530,14 +533,15 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb) * FLOGI. */ fip->flogi_oxid = FC_XID_UNKNOWN; - fc_fcoe_set_mac(fip->data_src_addr, fh->fh_s_id); + fc_fcoe_set_mac(mac, fh->fh_d_id); + fip->update_mac(lport, mac); return 0; default: if (fip->state != FIP_ST_ENABLED) goto drop; return 0; } - if (fcoe_ctlr_encaps(fip, op, skb)) + if (fcoe_ctlr_encaps(fip, lport, op, skb)) goto drop; fip->send(fip, skb); return -EINPROGRESS; @@ -547,9 +551,9 @@ drop: } EXPORT_SYMBOL(fcoe_ctlr_els_send); -/* - * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller. - * @fip: FCoE controller. +/** + * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller + * @fip: The FCoE controller to free FCFs on * * Called with lock held. * @@ -558,14 +562,28 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send); * times its keep-alive period including fuzz. * * In addition, determine the time when an FCF selection can occur. + * + * Also, increment the MissDiscAdvCount when no advertisement is received + * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB). */ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) { struct fcoe_fcf *fcf; struct fcoe_fcf *next; unsigned long sel_time = 0; + unsigned long mda_time = 0; list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { + mda_time = fcf->fka_period + (fcf->fka_period >> 1); + if ((fip->sel_fcf == fcf) && + (time_after(jiffies, fcf->time + mda_time))) { + mod_timer(&fip->timer, jiffies + mda_time); + fc_lport_get_stats(fip->lp)->MissDiscAdvCount++; + printk(KERN_INFO "libfcoe: host%d: Missing Discovery " + "Advertisement for fab %llx count %lld\n", + fip->lp->host->host_no, fcf->fabric_name, + fc_lport_get_stats(fip->lp)->MissDiscAdvCount); + } if (time_after(jiffies, fcf->time + fcf->fka_period * 3 + msecs_to_jiffies(FIP_FCF_FUZZ * 3))) { if (fip->sel_fcf == fcf) @@ -574,6 +592,7 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) WARN_ON(!fip->fcf_count); fip->fcf_count--; kfree(fcf); + fc_lport_get_stats(fip->lp)->VLinkFailureCount++; } else if (fcoe_ctlr_mtu_valid(fcf) && (!sel_time || time_before(sel_time, fcf->time))) { sel_time = fcf->time; @@ -590,14 +609,16 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) } /** - * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry. - * @skb: received FIP advertisement frame - * @fcf: resulting FCF entry. + * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry + * @fip: The FCoE controller receiving the advertisement + * @skb: The received FIP advertisement frame + * @fcf: The resulting FCF entry * * Returns zero on a valid parsed advertisement, * otherwise returns non zero value. */ -static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) +static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, + struct sk_buff *skb, struct fcoe_fcf *fcf) { struct fip_header *fiph; struct fip_desc *desc = NULL; @@ -636,7 +657,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) ((struct fip_mac_desc *)desc)->fd_mac, ETH_ALEN); if (!is_valid_ether_addr(fcf->fcf_mac)) { - LIBFCOE_FIP_DBG("Invalid MAC address " + LIBFCOE_FIP_DBG(fip, "Invalid MAC address " "in FIP adv\n"); return -EINVAL; } @@ -659,6 +680,8 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) if (dlen != sizeof(struct fip_fka_desc)) goto len_err; fka = (struct fip_fka_desc *)desc; + if (fka->fd_flags & FIP_FKA_ADV_D) + fcf->fd_flags = 1; t = ntohl(fka->fd_fka_period); if (t >= FCOE_CTLR_MIN_FKA) fcf->fka_period = msecs_to_jiffies(t); @@ -670,7 +693,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) case FIP_DT_LOGO: case FIP_DT_ELP: default: - LIBFCOE_FIP_DBG("unexpected descriptor type %x " + LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x " "in FIP adv\n", desc->fip_dtype); /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) @@ -687,15 +710,15 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) return 0; len_err: - LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n", + LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n", desc->fip_dtype, dlen); return -EINVAL; } /** - * fcoe_ctlr_recv_adv() - Handle an incoming advertisement. - * @fip: FCoE controller. - * @skb: Received FIP packet. + * fcoe_ctlr_recv_adv() - Handle an incoming advertisement + * @fip: The FCoE controller receiving the advertisement + * @skb: The received FIP packet */ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) { @@ -706,7 +729,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) int first = 0; int mtu_valid; - if (fcoe_ctlr_parse_adv(skb, &new)) + if (fcoe_ctlr_parse_adv(fip, skb, &new)) return; spin_lock_bh(&fip->lock); @@ -752,7 +775,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) mtu_valid = fcoe_ctlr_mtu_valid(fcf); fcf->time = jiffies; if (!found) { - LIBFCOE_FIP_DBG("New FCF for fab %llx map %x val %d\n", + LIBFCOE_FIP_DBG(fip, "New FCF for fab %llx map %x val %d\n", fcf->fabric_name, fcf->fc_map, mtu_valid); } @@ -778,7 +801,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) */ if (mtu_valid && !fip->sel_time && fcoe_ctlr_fcf_usable(fcf)) { fip->sel_time = jiffies + - msecs_to_jiffies(FCOE_CTLR_START_DELAY); + msecs_to_jiffies(FCOE_CTLR_START_DELAY); if (!timer_pending(&fip->timer) || time_before(fip->sel_time, fip->timer.expires)) mod_timer(&fip->timer, fip->sel_time); @@ -788,15 +811,15 @@ out: } /** - * fcoe_ctlr_recv_els() - Handle an incoming FIP-encapsulated ELS frame. - * @fip: FCoE controller. - * @skb: Received FIP packet. + * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame + * @fip: The FCoE controller which received the packet + * @skb: The received FIP packet */ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) { - struct fc_lport *lp = fip->lp; + struct fc_lport *lport = fip->lp; struct fip_header *fiph; - struct fc_frame *fp; + struct fc_frame *fp = (struct fc_frame *)skb; struct fc_frame_header *fh = NULL; struct fip_desc *desc; struct fip_encaps *els; @@ -831,10 +854,11 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) ((struct fip_mac_desc *)desc)->fd_mac, ETH_ALEN); if (!is_valid_ether_addr(granted_mac)) { - LIBFCOE_FIP_DBG("Invalid MAC address " + LIBFCOE_FIP_DBG(fip, "Invalid MAC address " "in FIP ELS\n"); goto drop; } + memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN); break; case FIP_DT_FLOGI: case FIP_DT_FDISC: @@ -850,7 +874,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) els_dtype = desc->fip_dtype; break; default: - LIBFCOE_FIP_DBG("unexpected descriptor type %x " + LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x " "in FIP adv\n", desc->fip_dtype); /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) @@ -867,11 +891,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP && fip->flogi_oxid == ntohs(fh->fh_ox_id) && - els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) { + els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) fip->flogi_oxid = FC_XID_UNKNOWN; - fip->update_mac(fip, fip->data_src_addr, granted_mac); - memcpy(fip->data_src_addr, granted_mac, ETH_ALEN); - } /* * Convert skb into an fc_frame containing only the ELS. @@ -882,32 +903,32 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) fc_frame_init(fp); fr_sof(fp) = FC_SOF_I3; fr_eof(fp) = FC_EOF_T; - fr_dev(fp) = lp; + fr_dev(fp) = lport; - stats = fc_lport_get_stats(lp); + stats = fc_lport_get_stats(lport); stats->RxFrames++; stats->RxWords += skb->len / FIP_BPW; - fc_exch_recv(lp, fp); + fc_exch_recv(lport, fp); return; len_err: - LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n", + LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n", desc->fip_dtype, dlen); drop: kfree_skb(skb); } /** - * fcoe_ctlr_recv_els() - Handle an incoming link reset frame. - * @fip: FCoE controller. - * @fh: Received FIP header. + * fcoe_ctlr_recv_els() - Handle an incoming link reset frame + * @fip: The FCoE controller that received the frame + * @fh: The received FIP header * * There may be multiple VN_Port descriptors. * The overall length has already been checked. */ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, - struct fip_header *fh) + struct fip_header *fh) { struct fip_desc *desc; struct fip_mac_desc *mp; @@ -916,13 +937,13 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, size_t rlen; size_t dlen; struct fcoe_fcf *fcf = fip->sel_fcf; - struct fc_lport *lp = fip->lp; + struct fc_lport *lport = fip->lp; u32 desc_mask; - LIBFCOE_FIP_DBG("Clear Virtual Link received\n"); + LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); if (!fcf) return; - if (!fcf || !fc_host_port_id(lp->host)) + if (!fcf || !fc_host_port_id(lport->host)) return; /* @@ -958,9 +979,10 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, if (dlen < sizeof(*vp)) return; if (compare_ether_addr(vp->fd_mac, - fip->data_src_addr) == 0 && - get_unaligned_be64(&vp->fd_wwpn) == lp->wwpn && - ntoh24(vp->fd_fc_id) == fc_host_port_id(lp->host)) + fip->get_src_addr(lport)) == 0 && + get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn && + ntoh24(vp->fd_fc_id) == + fc_host_port_id(lport->host)) desc_mask &= ~BIT(FIP_DT_VN_ID); break; default: @@ -977,33 +999,39 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, * reset only if all required descriptors were present and valid. */ if (desc_mask) { - LIBFCOE_FIP_DBG("missing descriptors mask %x\n", desc_mask); + LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n", + desc_mask); } else { - LIBFCOE_FIP_DBG("performing Clear Virtual Link\n"); - fcoe_ctlr_reset(fip, FIP_ST_ENABLED); + LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); + + spin_lock_bh(&fip->lock); + fc_lport_get_stats(lport)->VLinkFailureCount++; + fcoe_ctlr_reset(fip); + spin_unlock_bh(&fip->lock); + + fc_lport_reset(fip->lp); + fcoe_ctlr_solicit(fip, NULL); } } /** - * fcoe_ctlr_recv() - Receive a FIP frame. - * @fip: FCoE controller. - * @skb: Received FIP packet. + * fcoe_ctlr_recv() - Receive a FIP packet + * @fip: The FCoE controller that received the packet + * @skb: The received FIP packet * - * This is called from NET_RX_SOFTIRQ. + * This may be called from either NET_RX_SOFTIRQ or IRQ. */ void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb) { - spin_lock_bh(&fip->fip_recv_list.lock); - __skb_queue_tail(&fip->fip_recv_list, skb); - spin_unlock_bh(&fip->fip_recv_list.lock); + skb_queue_tail(&fip->fip_recv_list, skb); schedule_work(&fip->recv_work); } EXPORT_SYMBOL(fcoe_ctlr_recv); /** - * fcoe_ctlr_recv_handler() - Receive a FIP frame. - * @fip: FCoE controller. - * @skb: Received FIP packet. + * fcoe_ctlr_recv_handler() - Receive a FIP frame + * @fip: The FCoE controller that received the frame + * @skb: The received FIP frame * * Returns non-zero if the frame is dropped. */ @@ -1038,7 +1066,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) fip->map_dest = 0; fip->state = FIP_ST_ENABLED; state = FIP_ST_ENABLED; - LIBFCOE_FIP_DBG("Using FIP mode\n"); + LIBFCOE_FIP_DBG(fip, "Using FIP mode\n"); } spin_unlock_bh(&fip->lock); if (state != FIP_ST_ENABLED) @@ -1060,8 +1088,8 @@ drop: } /** - * fcoe_ctlr_select() - Select the best FCF, if possible. - * @fip: FCoE controller. + * fcoe_ctlr_select() - Select the best FCF (if possible) + * @fip: The FCoE controller * * If there are conflicting advertisements, no FCF can be chosen. * @@ -1073,11 +1101,11 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) struct fcoe_fcf *best = NULL; list_for_each_entry(fcf, &fip->fcfs, list) { - LIBFCOE_FIP_DBG("consider FCF for fab %llx VFID %d map %x " + LIBFCOE_FIP_DBG(fip, "consider FCF for fab %llx VFID %d map %x " "val %d\n", fcf->fabric_name, fcf->vfid, fcf->fc_map, fcoe_ctlr_mtu_valid(fcf)); if (!fcoe_ctlr_fcf_usable(fcf)) { - LIBFCOE_FIP_DBG("FCF for fab %llx map %x %svalid " + LIBFCOE_FIP_DBG(fip, "FCF for fab %llx map %x %svalid " "%savailable\n", fcf->fabric_name, fcf->fc_map, (fcf->flags & FIP_FL_SOL) ? "" : "in", (fcf->flags & FIP_FL_AVAIL) @@ -1091,7 +1119,7 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) if (fcf->fabric_name != best->fabric_name || fcf->vfid != best->vfid || fcf->fc_map != best->fc_map) { - LIBFCOE_FIP_DBG("Conflicting fabric, VFID, " + LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, " "or FC-MAP\n"); return; } @@ -1102,8 +1130,8 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) } /** - * fcoe_ctlr_timeout() - FIP timer function. - * @arg: &fcoe_ctlr pointer. + * fcoe_ctlr_timeout() - FIP timeout handler + * @arg: The FCoE controller that timed out * * Ages FCFs. Triggers FCF selection if possible. Sends keep-alives. */ @@ -1113,8 +1141,6 @@ static void fcoe_ctlr_timeout(unsigned long arg) struct fcoe_fcf *sel; struct fcoe_fcf *fcf; unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); - u8 send_ctlr_ka; - u8 send_port_ka; spin_lock_bh(&fip->lock); if (fip->state == FIP_ST_DISABLED) { @@ -1140,53 +1166,47 @@ static void fcoe_ctlr_timeout(unsigned long arg) fip->lp->host->host_no, sel->fcf_mac); memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN); fip->port_ka_time = jiffies + - msecs_to_jiffies(FIP_VN_KA_PERIOD); + msecs_to_jiffies(FIP_VN_KA_PERIOD); fip->ctlr_ka_time = jiffies + sel->fka_period; - fip->link = 1; } else { printk(KERN_NOTICE "libfcoe: host%d: " - "FIP Fibre-Channel Forwarder timed out. " + "FIP Fibre-Channel Forwarder timed out. " "Starting FCF discovery.\n", fip->lp->host->host_no); - fip->link = 0; + fip->reset_req = 1; + schedule_work(&fip->link_work); } - schedule_work(&fip->link_work); } - send_ctlr_ka = 0; - send_port_ka = 0; - if (sel) { + if (sel && !sel->fd_flags) { if (time_after_eq(jiffies, fip->ctlr_ka_time)) { fip->ctlr_ka_time = jiffies + sel->fka_period; - send_ctlr_ka = 1; + fip->send_ctlr_ka = 1; } if (time_after(next_timer, fip->ctlr_ka_time)) next_timer = fip->ctlr_ka_time; if (time_after_eq(jiffies, fip->port_ka_time)) { fip->port_ka_time += jiffies + - msecs_to_jiffies(FIP_VN_KA_PERIOD); - send_port_ka = 1; + msecs_to_jiffies(FIP_VN_KA_PERIOD); + fip->send_port_ka = 1; } if (time_after(next_timer, fip->port_ka_time)) next_timer = fip->port_ka_time; mod_timer(&fip->timer, next_timer); } else if (fip->sel_time) { next_timer = fip->sel_time + - msecs_to_jiffies(FCOE_CTLR_START_DELAY); + msecs_to_jiffies(FCOE_CTLR_START_DELAY); mod_timer(&fip->timer, next_timer); } + if (fip->send_ctlr_ka || fip->send_port_ka) + schedule_work(&fip->link_work); spin_unlock_bh(&fip->lock); - - if (send_ctlr_ka) - fcoe_ctlr_send_keep_alive(fip, 0, fip->ctl_src_addr); - if (send_port_ka) - fcoe_ctlr_send_keep_alive(fip, 1, fip->data_src_addr); } /** - * fcoe_ctlr_link_work() - worker thread function for link changes. - * @work: pointer to link_work member inside &fcoe_ctlr. + * fcoe_ctlr_link_work() - Worker thread function for link changes + * @work: Handle to a FCoE controller * * See if the link status has changed and if so, report it. * @@ -1196,27 +1216,49 @@ static void fcoe_ctlr_timeout(unsigned long arg) static void fcoe_ctlr_link_work(struct work_struct *work) { struct fcoe_ctlr *fip; + struct fc_lport *vport; + u8 *mac; int link; int last_link; + int reset; fip = container_of(work, struct fcoe_ctlr, link_work); spin_lock_bh(&fip->lock); last_link = fip->last_link; link = fip->link; fip->last_link = link; + reset = fip->reset_req; + fip->reset_req = 0; spin_unlock_bh(&fip->lock); if (last_link != link) { if (link) fc_linkup(fip->lp); else - fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT); + fc_linkdown(fip->lp); + } else if (reset && link) + fc_lport_reset(fip->lp); + + if (fip->send_ctlr_ka) { + fip->send_ctlr_ka = 0; + fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr); + } + if (fip->send_port_ka) { + fip->send_port_ka = 0; + mutex_lock(&fip->lp->lp_mutex); + mac = fip->get_src_addr(fip->lp); + fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac); + list_for_each_entry(vport, &fip->lp->vports, list) { + mac = fip->get_src_addr(vport); + fcoe_ctlr_send_keep_alive(fip, vport, 1, mac); + } + mutex_unlock(&fip->lp->lp_mutex); } } /** - * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames. - * @recv_work: pointer to recv_work member inside &fcoe_ctlr. + * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames + * @recv_work: Handle to a FCoE controller */ static void fcoe_ctlr_recv_work(struct work_struct *recv_work) { @@ -1224,20 +1266,14 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work) struct sk_buff *skb; fip = container_of(recv_work, struct fcoe_ctlr, recv_work); - spin_lock_bh(&fip->fip_recv_list.lock); - while ((skb = __skb_dequeue(&fip->fip_recv_list))) { - spin_unlock_bh(&fip->fip_recv_list.lock); + while ((skb = skb_dequeue(&fip->fip_recv_list))) fcoe_ctlr_recv_handler(fip, skb); - spin_lock_bh(&fip->fip_recv_list.lock); - } - spin_unlock_bh(&fip->fip_recv_list.lock); } /** - * fcoe_ctlr_recv_flogi() - snoop Pre-FIP receipt of FLOGI response or request. - * @fip: FCoE controller. - * @fp: FC frame. - * @sa: Ethernet source MAC address from received FCoE frame. + * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response + * @fip: The FCoE controller + * @fp: The FC frame to snoop * * Snoop potential response to FLOGI or even incoming FLOGI. * @@ -1245,15 +1281,18 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work) * by fip->flogi_oxid != FC_XID_UNKNOWN. * * The caller is responsible for freeing the frame. + * Fill in the granted_mac address. * * Return non-zero if the frame should not be delivered to libfc. */ -int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) +int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, + struct fc_frame *fp) { struct fc_frame_header *fh; u8 op; - u8 mac[ETH_ALEN]; + u8 *sa; + sa = eth_hdr(&fp->skb)->h_source; fh = fc_frame_header_get(fp); if (fh->fh_type != FC_TYPE_ELS) return 0; @@ -1268,7 +1307,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) return -EINVAL; } fip->state = FIP_ST_NON_FIP; - LIBFCOE_FIP_DBG("received FLOGI LS_ACC using non-FIP mode\n"); + LIBFCOE_FIP_DBG(fip, + "received FLOGI LS_ACC using non-FIP mode\n"); /* * FLOGI accepted. @@ -1283,11 +1323,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) fip->map_dest = 0; } fip->flogi_oxid = FC_XID_UNKNOWN; - memcpy(mac, fip->data_src_addr, ETH_ALEN); - fc_fcoe_set_mac(fip->data_src_addr, fh->fh_d_id); spin_unlock_bh(&fip->lock); - - fip->update_mac(fip, mac, fip->data_src_addr); + fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id); } else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) { /* * Save source MAC for point-to-point responses. @@ -1297,7 +1334,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) memcpy(fip->dest_addr, sa, ETH_ALEN); fip->map_dest = 0; if (fip->state == FIP_ST_NON_FIP) - LIBFCOE_FIP_DBG("received FLOGI REQ, " + LIBFCOE_FIP_DBG(fip, "received FLOGI REQ, " "using non-FIP mode\n"); fip->state = FIP_ST_NON_FIP; } @@ -1308,10 +1345,10 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) EXPORT_SYMBOL(fcoe_ctlr_recv_flogi); /** - * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN. - * @mac: mac address - * @scheme: check port - * @port: port indicator for converting + * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN + * @mac: The MAC address to convert + * @scheme: The scheme to use when converting + * @port: The port indicator for converting * * Returns: u64 fc world wide name */ @@ -1349,24 +1386,26 @@ u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN], EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac); /** - * fcoe_libfc_config() - sets up libfc related properties for lport - * @lp: ptr to the fc_lport - * @tt: libfc function template + * fcoe_libfc_config() - Sets up libfc related properties for local port + * @lp: The local port to configure libfc for + * @tt: The libfc function template * * Returns : 0 for success */ -int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt) +int fcoe_libfc_config(struct fc_lport *lport, + struct libfc_function_template *tt) { /* Set the function pointers set by the LLDD */ - memcpy(&lp->tt, tt, sizeof(*tt)); - if (fc_fcp_init(lp)) + memcpy(&lport->tt, tt, sizeof(*tt)); + if (fc_fcp_init(lport)) return -ENOMEM; - fc_exch_init(lp); - fc_elsct_init(lp); - fc_lport_init(lp); - fc_rport_init(lp); - fc_disc_init(lp); + fc_exch_init(lport); + fc_elsct_init(lport); + fc_lport_init(lport); + fc_rport_init(lport); + fc_disc_init(lport); return 0; } EXPORT_SYMBOL_GPL(fcoe_libfc_config); + diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index e4c0a3d7d87b..bb208a6091e7 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -22,6 +22,7 @@ #include <linux/netdevice.h> #include <linux/workqueue.h> #include <scsi/libfc.h> +#include <scsi/libfcoe.h> #include "fnic_io.h" #include "fnic_res.h" #include "vnic_dev.h" @@ -44,7 +45,7 @@ #define FNIC_IO_LOCKS 64 /* IO locks: power of 2 */ #define FNIC_DFLT_QUEUE_DEPTH 32 #define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */ - +#define FNIC_MAX_CMD_LEN 16 /* Supported CDB length */ /* * Tag bits used for special requests. */ @@ -145,6 +146,7 @@ struct mempool; /* Per-instance private data structure */ struct fnic { struct fc_lport *lport; + struct fcoe_ctlr ctlr; /* FIP FCoE controller structure */ struct vnic_dev_bar bar0; struct msix_entry msix_entry[FNIC_MSIX_INTR_MAX]; @@ -162,23 +164,16 @@ struct fnic { unsigned int wq_count; unsigned int cq_count; - u32 fcoui_mode:1; /* use fcoui address*/ u32 vlan_hw_insert:1; /* let hw insert the tag */ u32 in_remove:1; /* fnic device in removal */ u32 stop_rx_link_events:1; /* stop proc. rx frames, link events */ struct completion *remove_wait; /* device remove thread blocks */ - struct fc_frame *flogi; - struct fc_frame *flogi_resp; - u16 flogi_oxid; - unsigned long s_id; enum fnic_state state; spinlock_t fnic_lock; u16 vlan_id; /* VLAN tag including priority */ - u8 mac_addr[ETH_ALEN]; - u8 dest_addr[ETH_ALEN]; u8 data_src_addr[ETH_ALEN]; u64 fcp_input_bytes; /* internal statistic */ u64 fcp_output_bytes; /* internal statistic */ @@ -205,6 +200,7 @@ struct fnic { struct work_struct link_work; struct work_struct frame_work; struct sk_buff_head frame_queue; + struct sk_buff_head tx_queue; /* copy work queue cache line section */ ____cacheline_aligned struct vnic_wq_copy wq_copy[FNIC_WQ_COPY_MAX]; @@ -224,6 +220,11 @@ struct fnic { ____cacheline_aligned struct vnic_intr intr[FNIC_MSIX_INTR_MAX]; }; +static inline struct fnic *fnic_from_ctlr(struct fcoe_ctlr *fip) +{ + return container_of(fip, struct fnic, ctlr); +} + extern struct workqueue_struct *fnic_event_queue; extern struct device_attribute *fnic_attrs[]; @@ -239,7 +240,11 @@ void fnic_handle_link(struct work_struct *work); int fnic_rq_cmpl_handler(struct fnic *fnic, int); int fnic_alloc_rq_frame(struct vnic_rq *rq); void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf); -int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp); +void fnic_flush_tx(struct fnic *); +void fnic_eth_send(struct fcoe_ctlr *, struct sk_buff *skb); +void fnic_set_port_id(struct fc_lport *, u32, struct fc_frame *); +void fnic_update_mac(struct fc_lport *, u8 *new); +void fnic_update_mac_locked(struct fnic *, u8 *new); int fnic_queuecommand(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *)); int fnic_abort_cmd(struct scsi_cmnd *); @@ -252,7 +257,7 @@ void fnic_empty_scsi_cleanup(struct fc_lport *); void fnic_exch_mgr_reset(struct fc_lport *, u32, u32); int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int); int fnic_wq_cmpl_handler(struct fnic *fnic, int); -int fnic_flogi_reg_handler(struct fnic *fnic); +int fnic_flogi_reg_handler(struct fnic *fnic, u32); void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq, struct fcpio_host_req *desc); int fnic_fw_reset_handler(struct fnic *fnic); diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 50db3e36a619..54f8d0e5407f 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -23,6 +23,7 @@ #include <linux/if_ether.h> #include <linux/if_vlan.h> #include <linux/workqueue.h> +#include <scsi/fc/fc_fip.h> #include <scsi/fc/fc_els.h> #include <scsi/fc/fc_fcoe.h> #include <scsi/fc_frame.h> @@ -34,6 +35,8 @@ struct workqueue_struct *fnic_event_queue; +static void fnic_set_eth_mode(struct fnic *); + void fnic_handle_link(struct work_struct *work) { struct fnic *fnic = container_of(work, struct fnic, link_work); @@ -64,10 +67,10 @@ void fnic_handle_link(struct work_struct *work) spin_unlock_irqrestore(&fnic->fnic_lock, flags); FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n"); - fc_linkdown(fnic->lport); + fcoe_ctlr_link_down(&fnic->ctlr); FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n"); - fc_linkup(fnic->lport); + fcoe_ctlr_link_up(&fnic->ctlr); } else /* UP -> UP */ spin_unlock_irqrestore(&fnic->fnic_lock, flags); @@ -76,13 +79,13 @@ void fnic_handle_link(struct work_struct *work) /* DOWN -> UP */ spin_unlock_irqrestore(&fnic->fnic_lock, flags); FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n"); - fc_linkup(fnic->lport); + fcoe_ctlr_link_up(&fnic->ctlr); } else { /* UP -> DOWN */ fnic->lport->host_stats.link_failure_count++; spin_unlock_irqrestore(&fnic->fnic_lock, flags); FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n"); - fc_linkdown(fnic->lport); + fcoe_ctlr_link_down(&fnic->ctlr); } } @@ -107,197 +110,179 @@ void fnic_handle_frame(struct work_struct *work) return; } fp = (struct fc_frame *)skb; - /* if Flogi resp frame, register the address */ - if (fr_flags(fp)) { - vnic_dev_add_addr(fnic->vdev, - fnic->data_src_addr); - fr_flags(fp) = 0; + + /* + * If we're in a transitional state, just re-queue and return. + * The queue will be serviced when we get to a stable state. + */ + if (fnic->state != FNIC_IN_FC_MODE && + fnic->state != FNIC_IN_ETH_MODE) { + skb_queue_head(&fnic->frame_queue, skb); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; } spin_unlock_irqrestore(&fnic->fnic_lock, flags); fc_exch_recv(lp, fp); } - -} - -static inline void fnic_import_rq_fc_frame(struct sk_buff *skb, - u32 len, u8 sof, u8 eof) -{ - struct fc_frame *fp = (struct fc_frame *)skb; - - skb_trim(skb, len); - fr_eof(fp) = eof; - fr_sof(fp) = sof; } - -static inline int fnic_import_rq_eth_pkt(struct sk_buff *skb, u32 len) +/** + * fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame. + * @fnic: fnic instance. + * @skb: Ethernet Frame. + */ +static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb) { struct fc_frame *fp; struct ethhdr *eh; - struct vlan_ethhdr *vh; struct fcoe_hdr *fcoe_hdr; struct fcoe_crc_eof *ft; - u32 transport_len = 0; + /* + * Undo VLAN encapsulation if present. + */ eh = (struct ethhdr *)skb->data; - vh = (struct vlan_ethhdr *)skb->data; - if (vh->h_vlan_proto == htons(ETH_P_8021Q) && - vh->h_vlan_encapsulated_proto == htons(ETH_P_FCOE)) { - skb_pull(skb, sizeof(struct vlan_ethhdr)); - transport_len += sizeof(struct vlan_ethhdr); - } else if (eh->h_proto == htons(ETH_P_FCOE)) { - transport_len += sizeof(struct ethhdr); - skb_pull(skb, sizeof(struct ethhdr)); - } else - return -1; + if (eh->h_proto == htons(ETH_P_8021Q)) { + memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2); + eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN); + skb_reset_mac_header(skb); + } + if (eh->h_proto == htons(ETH_P_FIP)) { + skb_pull(skb, sizeof(*eh)); + fcoe_ctlr_recv(&fnic->ctlr, skb); + return 1; /* let caller know packet was used */ + } + if (eh->h_proto != htons(ETH_P_FCOE)) + goto drop; + skb_set_network_header(skb, sizeof(*eh)); + skb_pull(skb, sizeof(*eh)); fcoe_hdr = (struct fcoe_hdr *)skb->data; if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER) - return -1; + goto drop; fp = (struct fc_frame *)skb; fc_frame_init(fp); fr_sof(fp) = fcoe_hdr->fcoe_sof; skb_pull(skb, sizeof(struct fcoe_hdr)); - transport_len += sizeof(struct fcoe_hdr); + skb_reset_transport_header(skb); - ft = (struct fcoe_crc_eof *)(skb->data + len - - transport_len - sizeof(*ft)); + ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft)); fr_eof(fp) = ft->fcoe_eof; - skb_trim(skb, len - transport_len - sizeof(*ft)); + skb_trim(skb, skb->len - sizeof(*ft)); return 0; +drop: + dev_kfree_skb_irq(skb); + return -1; } -static inline int fnic_handle_flogi_resp(struct fnic *fnic, - struct fc_frame *fp) +/** + * fnic_update_mac_locked() - set data MAC address and filters. + * @fnic: fnic instance. + * @new: newly-assigned FCoE MAC address. + * + * Called with the fnic lock held. + */ +void fnic_update_mac_locked(struct fnic *fnic, u8 *new) { - u8 mac[ETH_ALEN] = FC_FCOE_FLOGI_MAC; - struct ethhdr *eth_hdr; - struct fc_frame_header *fh; - int ret = 0; - unsigned long flags; - struct fc_frame *old_flogi_resp = NULL; + u8 *ctl = fnic->ctlr.ctl_src_addr; + u8 *data = fnic->data_src_addr; - fh = (struct fc_frame_header *)fr_hdr(fp); + if (is_zero_ether_addr(new)) + new = ctl; + if (!compare_ether_addr(data, new)) + return; + FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new); + if (!is_zero_ether_addr(data) && compare_ether_addr(data, ctl)) + vnic_dev_del_addr(fnic->vdev, data); + memcpy(data, new, ETH_ALEN); + if (compare_ether_addr(new, ctl)) + vnic_dev_add_addr(fnic->vdev, new); +} - spin_lock_irqsave(&fnic->fnic_lock, flags); +/** + * fnic_update_mac() - set data MAC address and filters. + * @lport: local port. + * @new: newly-assigned FCoE MAC address. + */ +void fnic_update_mac(struct fc_lport *lport, u8 *new) +{ + struct fnic *fnic = lport_priv(lport); - if (fnic->state == FNIC_IN_ETH_MODE) { + spin_lock_irq(&fnic->fnic_lock); + fnic_update_mac_locked(fnic, new); + spin_unlock_irq(&fnic->fnic_lock); +} - /* - * Check if oxid matches on taking the lock. A new Flogi - * issued by libFC might have changed the fnic cached oxid - */ - if (fnic->flogi_oxid != ntohs(fh->fh_ox_id)) { - FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, - "Flogi response oxid not" - " matching cached oxid, dropping frame" - "\n"); - ret = -1; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - dev_kfree_skb_irq(fp_skb(fp)); - goto handle_flogi_resp_end; - } +/** + * fnic_set_port_id() - set the port_ID after successful FLOGI. + * @lport: local port. + * @port_id: assigned FC_ID. + * @fp: received frame containing the FLOGI accept or NULL. + * + * This is called from libfc when a new FC_ID has been assigned. + * This causes us to reset the firmware to FC_MODE and setup the new MAC + * address and FC_ID. + * + * It is also called with FC_ID 0 when we're logged off. + * + * If the FC_ID is due to point-to-point, fp may be NULL. + */ +void fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp) +{ + struct fnic *fnic = lport_priv(lport); + u8 *mac; + int ret; - /* Drop older cached flogi response frame, cache this frame */ - old_flogi_resp = fnic->flogi_resp; - fnic->flogi_resp = fp; - fnic->flogi_oxid = FC_XID_UNKNOWN; + FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n", + port_id, fp); - /* - * this frame is part of flogi get the src mac addr from this - * frame if the src mac is fcoui based then we mark the - * address mode flag to use fcoui base for dst mac addr - * otherwise we have to store the fcoe gateway addr - */ - eth_hdr = (struct ethhdr *)skb_mac_header(fp_skb(fp)); - memcpy(mac, eth_hdr->h_source, ETH_ALEN); + /* + * If we're clearing the FC_ID, change to use the ctl_src_addr. + * Set ethernet mode to send FLOGI. + */ + if (!port_id) { + fnic_update_mac(lport, fnic->ctlr.ctl_src_addr); + fnic_set_eth_mode(fnic); + return; + } - if (ntoh24(mac) == FC_FCOE_OUI) - fnic->fcoui_mode = 1; - else { - fnic->fcoui_mode = 0; - memcpy(fnic->dest_addr, mac, ETH_ALEN); + if (fp) { + mac = fr_cb(fp)->granted_mac; + if (is_zero_ether_addr(mac)) { + /* non-FIP - FLOGI already accepted - ignore return */ + fcoe_ctlr_recv_flogi(&fnic->ctlr, lport, fp); } + fnic_update_mac(lport, mac); + } - /* - * Except for Flogi frame, all outbound frames from us have the - * Eth Src address as FC_FCOE_OUI"our_sid". Flogi frame uses - * the vnic MAC address as the Eth Src address - */ - fc_fcoe_set_mac(fnic->data_src_addr, fh->fh_d_id); - - /* We get our s_id from the d_id of the flogi resp frame */ - fnic->s_id = ntoh24(fh->fh_d_id); - - /* Change state to reflect transition from Eth to FC mode */ + /* Change state to reflect transition to FC mode */ + spin_lock_irq(&fnic->fnic_lock); + if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE) fnic->state = FNIC_IN_ETH_TRANS_FC_MODE; - - } else { + else { FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "Unexpected fnic state %s while" " processing flogi resp\n", fnic_state_to_str(fnic->state)); - ret = -1; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - dev_kfree_skb_irq(fp_skb(fp)); - goto handle_flogi_resp_end; + spin_unlock_irq(&fnic->fnic_lock); + return; } - - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - - /* Drop older cached frame */ - if (old_flogi_resp) - dev_kfree_skb_irq(fp_skb(old_flogi_resp)); + spin_unlock_irq(&fnic->fnic_lock); /* - * send flogi reg request to firmware, this will put the fnic in - * in FC mode + * Send FLOGI registration to firmware to set up FC mode. + * The new address will be set up when registration completes. */ - ret = fnic_flogi_reg_handler(fnic); + ret = fnic_flogi_reg_handler(fnic, port_id); if (ret < 0) { - int free_fp = 1; - spin_lock_irqsave(&fnic->fnic_lock, flags); - /* - * free the frame is some other thread is not - * pointing to it - */ - if (fnic->flogi_resp != fp) - free_fp = 0; - else - fnic->flogi_resp = NULL; - + spin_lock_irq(&fnic->fnic_lock); if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE) fnic->state = FNIC_IN_ETH_MODE; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - if (free_fp) - dev_kfree_skb_irq(fp_skb(fp)); + spin_unlock_irq(&fnic->fnic_lock); } - - handle_flogi_resp_end: - return ret; -} - -/* Returns 1 for a response that matches cached flogi oxid */ -static inline int is_matching_flogi_resp_frame(struct fnic *fnic, - struct fc_frame *fp) -{ - struct fc_frame_header *fh; - int ret = 0; - u32 f_ctl; - - fh = fc_frame_header_get(fp); - f_ctl = ntoh24(fh->fh_f_ctl); - - if (fnic->flogi_oxid == ntohs(fh->fh_ox_id) && - fh->fh_r_ctl == FC_RCTL_ELS_REP && - (f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) == FC_FC_EX_CTX && - fh->fh_type == FC_TYPE_ELS) - ret = 1; - - return ret; } static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc @@ -326,6 +311,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len, PCI_DMA_FROMDEVICE); skb = buf->os_buf; + fp = (struct fc_frame *)skb; buf->os_buf = NULL; cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index); @@ -338,6 +324,9 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc &fcoe_enc_error, &fcs_ok, &vlan_stripped, &vlan); eth_hdrs_stripped = 1; + skb_trim(skb, fcp_bytes_written); + fr_sof(fp) = sof; + fr_eof(fp) = eof; } else if (type == CQ_DESC_TYPE_RQ_ENET) { cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc, @@ -352,6 +341,14 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc &ipv4_csum_ok, &ipv6, &ipv4, &ipv4_fragment, &fcs_ok); eth_hdrs_stripped = 0; + skb_trim(skb, bytes_written); + if (!fcs_ok) { + FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, + "fcs error. dropping packet.\n"); + goto drop; + } + if (fnic_import_rq_eth_pkt(fnic, skb)) + return; } else { /* wrong CQ type*/ @@ -370,43 +367,11 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc goto drop; } - if (eth_hdrs_stripped) - fnic_import_rq_fc_frame(skb, fcp_bytes_written, sof, eof); - else if (fnic_import_rq_eth_pkt(skb, bytes_written)) - goto drop; - - fp = (struct fc_frame *)skb; - - /* - * If frame is an ELS response that matches the cached FLOGI OX_ID, - * and is accept, issue flogi_reg_request copy wq request to firmware - * to register the S_ID and determine whether FC_OUI mode or GW mode. - */ - if (is_matching_flogi_resp_frame(fnic, fp)) { - if (!eth_hdrs_stripped) { - if (fc_frame_payload_op(fp) == ELS_LS_ACC) { - fnic_handle_flogi_resp(fnic, fp); - return; - } - /* - * Recd. Flogi reject. No point registering - * with fw, but forward to libFC - */ - goto forward; - } - goto drop; - } - if (!eth_hdrs_stripped) - goto drop; - -forward: spin_lock_irqsave(&fnic->fnic_lock, flags); if (fnic->stop_rx_link_events) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); goto drop; } - /* Use fr_flags to indicate whether succ. flogi resp or not */ - fr_flags(fp) = 0; fr_dev(fp) = fnic->lport; spin_unlock_irqrestore(&fnic->fnic_lock, flags); @@ -494,12 +459,49 @@ void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) buf->os_buf = NULL; } -static inline int is_flogi_frame(struct fc_frame_header *fh) +/** + * fnic_eth_send() - Send Ethernet frame. + * @fip: fcoe_ctlr instance. + * @skb: Ethernet Frame, FIP, without VLAN encapsulation. + */ +void fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb) { - return fh->fh_r_ctl == FC_RCTL_ELS_REQ && *(u8 *)(fh + 1) == ELS_FLOGI; + struct fnic *fnic = fnic_from_ctlr(fip); + struct vnic_wq *wq = &fnic->wq[0]; + dma_addr_t pa; + struct ethhdr *eth_hdr; + struct vlan_ethhdr *vlan_hdr; + unsigned long flags; + + if (!fnic->vlan_hw_insert) { + eth_hdr = (struct ethhdr *)skb_mac_header(skb); + vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, + sizeof(*vlan_hdr) - sizeof(*eth_hdr)); + memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN); + vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q); + vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto; + vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id); + } + + pa = pci_map_single(fnic->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + + spin_lock_irqsave(&fnic->wq_lock[0], flags); + if (!vnic_wq_desc_avail(wq)) { + pci_unmap_single(fnic->pdev, pa, skb->len, PCI_DMA_TODEVICE); + spin_unlock_irqrestore(&fnic->wq_lock[0], flags); + kfree_skb(skb); + return; + } + + fnic_queue_wq_eth_desc(wq, skb, pa, skb->len, + fnic->vlan_hw_insert, fnic->vlan_id, 1); + spin_unlock_irqrestore(&fnic->wq_lock[0], flags); } -int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp) +/* + * Send FC frame. + */ +static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp) { struct vnic_wq *wq = &fnic->wq[0]; struct sk_buff *skb; @@ -515,6 +517,10 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp) fh = fc_frame_header_get(fp); skb = fp_skb(fp); + if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && + fcoe_ctlr_els_send(&fnic->ctlr, fnic->lport, skb)) + return 0; + if (!fnic->vlan_hw_insert) { eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr); vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, eth_hdr_len); @@ -530,16 +536,11 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp) fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1); } - if (is_flogi_frame(fh)) { + if (fnic->ctlr.map_dest) fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id); - memcpy(eth_hdr->h_source, fnic->mac_addr, ETH_ALEN); - } else { - if (fnic->fcoui_mode) - fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id); - else - memcpy(eth_hdr->h_dest, fnic->dest_addr, ETH_ALEN); - memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN); - } + else + memcpy(eth_hdr->h_dest, fnic->ctlr.dest_addr, ETH_ALEN); + memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN); tot_len = skb->len; BUG_ON(tot_len % 4); @@ -578,109 +579,85 @@ fnic_send_frame_end: int fnic_send(struct fc_lport *lp, struct fc_frame *fp) { struct fnic *fnic = lport_priv(lp); - struct fc_frame_header *fh; - int ret = 0; - enum fnic_state old_state; unsigned long flags; - struct fc_frame *old_flogi = NULL; - struct fc_frame *old_flogi_resp = NULL; if (fnic->in_remove) { dev_kfree_skb(fp_skb(fp)); - ret = -1; - goto fnic_send_end; + return -1; } - fh = fc_frame_header_get(fp); - /* if not an Flogi frame, send it out, this is the common case */ - if (!is_flogi_frame(fh)) - return fnic_send_frame(fnic, fp); + /* + * Queue frame if in a transitional state. + * This occurs while registering the Port_ID / MAC address after FLOGI. + */ + spin_lock_irqsave(&fnic->fnic_lock, flags); + if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) { + skb_queue_tail(&fnic->tx_queue, fp_skb(fp)); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return 0; + } + spin_unlock_irqrestore(&fnic->fnic_lock, flags); - /* Flogi frame, now enter the state machine */ + return fnic_send_frame(fnic, fp); +} - spin_lock_irqsave(&fnic->fnic_lock, flags); -again: - /* Get any old cached frames, free them after dropping lock */ - old_flogi = fnic->flogi; - fnic->flogi = NULL; - old_flogi_resp = fnic->flogi_resp; - fnic->flogi_resp = NULL; +/** + * fnic_flush_tx() - send queued frames. + * @fnic: fnic device + * + * Send frames that were waiting to go out in FC or Ethernet mode. + * Whenever changing modes we purge queued frames, so these frames should + * be queued for the stable mode that we're in, either FC or Ethernet. + * + * Called without fnic_lock held. + */ +void fnic_flush_tx(struct fnic *fnic) +{ + struct sk_buff *skb; + struct fc_frame *fp; - fnic->flogi_oxid = FC_XID_UNKNOWN; + while ((skb = skb_dequeue(&fnic->frame_queue))) { + fp = (struct fc_frame *)skb; + fnic_send_frame(fnic, fp); + } +} +/** + * fnic_set_eth_mode() - put fnic into ethernet mode. + * @fnic: fnic device + * + * Called without fnic lock held. + */ +static void fnic_set_eth_mode(struct fnic *fnic) +{ + unsigned long flags; + enum fnic_state old_state; + int ret; + + spin_lock_irqsave(&fnic->fnic_lock, flags); +again: old_state = fnic->state; switch (old_state) { case FNIC_IN_FC_MODE: case FNIC_IN_ETH_TRANS_FC_MODE: default: fnic->state = FNIC_IN_FC_TRANS_ETH_MODE; - vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr); spin_unlock_irqrestore(&fnic->fnic_lock, flags); - if (old_flogi) { - dev_kfree_skb(fp_skb(old_flogi)); - old_flogi = NULL; - } - if (old_flogi_resp) { - dev_kfree_skb(fp_skb(old_flogi_resp)); - old_flogi_resp = NULL; - } - ret = fnic_fw_reset_handler(fnic); spin_lock_irqsave(&fnic->fnic_lock, flags); if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE) goto again; - if (ret) { + if (ret) fnic->state = old_state; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - dev_kfree_skb(fp_skb(fp)); - goto fnic_send_end; - } - old_flogi = fnic->flogi; - fnic->flogi = fp; - fnic->flogi_oxid = ntohs(fh->fh_ox_id); - old_flogi_resp = fnic->flogi_resp; - fnic->flogi_resp = NULL; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); break; case FNIC_IN_FC_TRANS_ETH_MODE: - /* - * A reset is pending with the firmware. Store the flogi - * and its oxid. The transition out of this state happens - * only when Firmware completes the reset, either with - * success or failed. If success, transition to - * FNIC_IN_ETH_MODE, if fail, then transition to - * FNIC_IN_FC_MODE - */ - fnic->flogi = fp; - fnic->flogi_oxid = ntohs(fh->fh_ox_id); - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - break; - case FNIC_IN_ETH_MODE: - /* - * The fw/hw is already in eth mode. Store the oxid, - * and send the flogi frame out. The transition out of this - * state happens only we receive flogi response from the - * network, and the oxid matches the cached oxid when the - * flogi frame was sent out. If they match, then we issue - * a flogi_reg request and transition to state - * FNIC_IN_ETH_TRANS_FC_MODE - */ - fnic->flogi_oxid = ntohs(fh->fh_ox_id); - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - ret = fnic_send_frame(fnic, fp); break; } - -fnic_send_end: - if (old_flogi) - dev_kfree_skb(fp_skb(old_flogi)); - if (old_flogi_resp) - dev_kfree_skb(fp_skb(old_flogi_resp)); - return ret; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); } static void fnic_wq_complete_frame_send(struct vnic_wq *wq, diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c index 2b3064828aea..5c1f223cabce 100644 --- a/drivers/scsi/fnic/fnic_isr.c +++ b/drivers/scsi/fnic/fnic_isr.c @@ -48,9 +48,9 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data) } if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) { - work_done += fnic_wq_copy_cmpl_handler(fnic, 8); - work_done += fnic_wq_cmpl_handler(fnic, 4); - work_done += fnic_rq_cmpl_handler(fnic, 4); + work_done += fnic_wq_copy_cmpl_handler(fnic, -1); + work_done += fnic_wq_cmpl_handler(fnic, -1); + work_done += fnic_rq_cmpl_handler(fnic, -1); vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ], work_done, @@ -66,9 +66,9 @@ static irqreturn_t fnic_isr_msi(int irq, void *data) struct fnic *fnic = data; unsigned long work_done = 0; - work_done += fnic_wq_copy_cmpl_handler(fnic, 8); - work_done += fnic_wq_cmpl_handler(fnic, 4); - work_done += fnic_rq_cmpl_handler(fnic, 4); + work_done += fnic_wq_copy_cmpl_handler(fnic, -1); + work_done += fnic_wq_cmpl_handler(fnic, -1); + work_done += fnic_rq_cmpl_handler(fnic, -1); vnic_intr_return_credits(&fnic->intr[0], work_done, @@ -83,7 +83,7 @@ static irqreturn_t fnic_isr_msix_rq(int irq, void *data) struct fnic *fnic = data; unsigned long rq_work_done = 0; - rq_work_done = fnic_rq_cmpl_handler(fnic, 4); + rq_work_done = fnic_rq_cmpl_handler(fnic, -1); vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ], rq_work_done, 1 /* unmask intr */, @@ -97,7 +97,7 @@ static irqreturn_t fnic_isr_msix_wq(int irq, void *data) struct fnic *fnic = data; unsigned long wq_work_done = 0; - wq_work_done = fnic_wq_cmpl_handler(fnic, 4); + wq_work_done = fnic_wq_cmpl_handler(fnic, -1); vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ], wq_work_done, 1 /* unmask intr */, @@ -110,7 +110,7 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data) struct fnic *fnic = data; unsigned long wq_copy_work_done = 0; - wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, 8); + wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1); vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY], wq_copy_work_done, 1 /* unmask intr */, diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 71c7bbe26d05..fe1b1031f7ab 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -25,6 +25,8 @@ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/workqueue.h> +#include <linux/if_ether.h> +#include <scsi/fc/fc_fip.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_fc.h> @@ -68,6 +70,7 @@ MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels"); static struct libfc_function_template fnic_transport_template = { .frame_send = fnic_send, + .lport_set_port_id = fnic_set_port_id, .fcp_abort_io = fnic_empty_scsi_cleanup, .fcp_cleanup = fnic_empty_scsi_cleanup, .exch_mgr_reset = fnic_exch_mgr_reset @@ -140,6 +143,7 @@ static struct fc_function_template fnic_fc_functions = { .get_fc_host_stats = fnic_get_stats, .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv), .terminate_rport_io = fnic_terminate_rport_io, + .bsg_request = fc_lport_bsg_request, }; static void fnic_get_host_speed(struct Scsi_Host *shost) @@ -324,9 +328,6 @@ static int fnic_cleanup(struct fnic *fnic) { unsigned int i; int err; - unsigned long flags; - struct fc_frame *flogi = NULL; - struct fc_frame *flogi_resp = NULL; vnic_dev_disable(fnic->vdev); for (i = 0; i < fnic->intr_count; i++) @@ -367,24 +368,6 @@ static int fnic_cleanup(struct fnic *fnic) for (i = 0; i < fnic->intr_count; i++) vnic_intr_clean(&fnic->intr[i]); - /* - * Remove cached flogi and flogi resp frames if any - * These frames are not in any queue, and therefore queue - * cleanup does not clean them. So clean them explicitly - */ - spin_lock_irqsave(&fnic->fnic_lock, flags); - flogi = fnic->flogi; - fnic->flogi = NULL; - flogi_resp = fnic->flogi_resp; - fnic->flogi_resp = NULL; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - - if (flogi) - dev_kfree_skb(fp_skb(flogi)); - - if (flogi_resp) - dev_kfree_skb(fp_skb(flogi_resp)); - mempool_destroy(fnic->io_req_pool); for (i = 0; i < FNIC_SGL_NUM_CACHES; i++) mempool_destroy(fnic->io_sgl_pool[i]); @@ -409,6 +392,17 @@ static void *fnic_alloc_slab_dma(gfp_t gfp_mask, void *pool_data) return kmem_cache_alloc(mem, gfp_mask | GFP_ATOMIC | GFP_DMA); } +/** + * fnic_get_mac() - get assigned data MAC address for FIP code. + * @lport: local port. + */ +static u8 *fnic_get_mac(struct fc_lport *lport) +{ + struct fnic *fnic = lport_priv(lport); + + return fnic->data_src_addr; +} + static int __devinit fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -424,17 +418,16 @@ static int __devinit fnic_probe(struct pci_dev *pdev, * Allocate SCSI Host and set up association between host, * local port, and fnic */ - host = scsi_host_alloc(&fnic_host_template, - sizeof(struct fc_lport) + sizeof(struct fnic)); - if (!host) { - printk(KERN_ERR PFX "Unable to alloc SCSI host\n"); + lp = libfc_host_alloc(&fnic_host_template, sizeof(struct fnic)); + if (!lp) { + printk(KERN_ERR PFX "Unable to alloc libfc local port\n"); err = -ENOMEM; goto err_out; } - lp = shost_priv(host); - lp->host = host; + host = lp->host; fnic = lport_priv(lp); fnic->lport = lp; + fnic->ctlr.lp = lp; snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME, host->host_no); @@ -543,12 +536,14 @@ static int __devinit fnic_probe(struct pci_dev *pdev, goto err_out_dev_close; } - err = vnic_dev_mac_addr(fnic->vdev, fnic->mac_addr); + err = vnic_dev_mac_addr(fnic->vdev, fnic->ctlr.ctl_src_addr); if (err) { shost_printk(KERN_ERR, fnic->lport->host, "vNIC get MAC addr failed \n"); goto err_out_dev_close; } + /* set data_src for point-to-point mode and to keep it non-zero */ + memcpy(fnic->data_src_addr, fnic->ctlr.ctl_src_addr, ETH_ALEN); /* Get vNIC configuration */ err = fnic_get_vnic_config(fnic); @@ -560,6 +555,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev, } host->max_lun = fnic->config.luns_per_tgt; host->max_id = FNIC_MAX_FCP_TARGET; + host->max_cmd_len = FNIC_MAX_CMD_LEN; fnic_get_res_counts(fnic); @@ -571,19 +567,12 @@ static int __devinit fnic_probe(struct pci_dev *pdev, goto err_out_dev_close; } - err = fnic_request_intr(fnic); - if (err) { - shost_printk(KERN_ERR, fnic->lport->host, - "Unable to request irq.\n"); - goto err_out_clear_intr; - } - err = fnic_alloc_vnic_resources(fnic); if (err) { shost_printk(KERN_ERR, fnic->lport->host, "Failed to alloc vNIC resources, " "aborting.\n"); - goto err_out_free_intr; + goto err_out_clear_intr; } @@ -623,9 +612,21 @@ static int __devinit fnic_probe(struct pci_dev *pdev, fnic->vlan_hw_insert = 1; fnic->vlan_id = 0; - fnic->flogi_oxid = FC_XID_UNKNOWN; - fnic->flogi = NULL; - fnic->flogi_resp = NULL; + /* Initialize the FIP fcoe_ctrl struct */ + fnic->ctlr.send = fnic_eth_send; + fnic->ctlr.update_mac = fnic_update_mac; + fnic->ctlr.get_src_addr = fnic_get_mac; + fcoe_ctlr_init(&fnic->ctlr); + if (fnic->config.flags & VFCF_FIP_CAPABLE) { + shost_printk(KERN_INFO, fnic->lport->host, + "firmware supports FIP\n"); + vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS); + vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr); + } else { + shost_printk(KERN_INFO, fnic->lport->host, + "firmware uses non-FIP mode\n"); + fnic->ctlr.mode = FIP_ST_NON_FIP; + } fnic->state = FNIC_IN_FC_MODE; /* Enable hardware stripping of vlan header on ingress */ @@ -716,6 +717,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev, INIT_WORK(&fnic->link_work, fnic_handle_link); INIT_WORK(&fnic->frame_work, fnic_handle_frame); skb_queue_head_init(&fnic->frame_queue); + skb_queue_head_init(&fnic->tx_queue); /* Enable all queues */ for (i = 0; i < fnic->raw_wq_count; i++) @@ -728,6 +730,14 @@ static int __devinit fnic_probe(struct pci_dev *pdev, fc_fabric_login(lp); vnic_dev_enable(fnic->vdev); + + err = fnic_request_intr(fnic); + if (err) { + shost_printk(KERN_ERR, fnic->lport->host, + "Unable to request irq.\n"); + goto err_out_free_exch_mgr; + } + for (i = 0; i < fnic->intr_count; i++) vnic_intr_unmask(&fnic->intr[i]); @@ -738,8 +748,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev, err_out_free_exch_mgr: fc_exch_mgr_free(lp); err_out_remove_scsi_host: - fc_remove_host(fnic->lport->host); - scsi_remove_host(fnic->lport->host); + fc_remove_host(lp->host); + scsi_remove_host(lp->host); err_out_free_rq_buf: for (i = 0; i < fnic->rq_count; i++) vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf); @@ -752,8 +762,6 @@ err_out_free_ioreq_pool: mempool_destroy(fnic->io_req_pool); err_out_free_resources: fnic_free_vnic_resources(fnic); -err_out_free_intr: - fnic_free_intr(fnic); err_out_clear_intr: fnic_clear_intr_mode(fnic); err_out_dev_close: @@ -775,6 +783,7 @@ err_out: static void __devexit fnic_remove(struct pci_dev *pdev) { struct fnic *fnic = pci_get_drvdata(pdev); + struct fc_lport *lp = fnic->lport; unsigned long flags; /* @@ -796,6 +805,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev) */ flush_workqueue(fnic_event_queue); skb_queue_purge(&fnic->frame_queue); + skb_queue_purge(&fnic->tx_queue); /* * Log off the fabric. This stops all remote ports, dns port, @@ -808,7 +818,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev) fnic->in_remove = 1; spin_unlock_irqrestore(&fnic->fnic_lock, flags); - fc_lport_destroy(fnic->lport); + fcoe_ctlr_destroy(&fnic->ctlr); + fc_lport_destroy(lp); /* * This stops the fnic device, masks all interrupts. Completed @@ -818,6 +829,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev) fnic_cleanup(fnic); BUG_ON(!skb_queue_empty(&fnic->frame_queue)); + BUG_ON(!skb_queue_empty(&fnic->tx_queue)); spin_lock_irqsave(&fnic_list_lock, flags); list_del(&fnic->list); @@ -827,8 +839,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev) scsi_remove_host(fnic->lport->host); fc_exch_mgr_free(fnic->lport); vnic_dev_notify_unset(fnic->vdev); - fnic_free_vnic_resources(fnic); fnic_free_intr(fnic); + fnic_free_vnic_resources(fnic); fnic_clear_intr_mode(fnic); vnic_dev_close(fnic->vdev); vnic_dev_unregister(fnic->vdev); @@ -836,7 +848,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); - scsi_host_put(fnic->lport->host); + scsi_host_put(lp->host); } static struct pci_driver fnic_driver = { diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c index 7ba61ec715d2..50488f8e169d 100644 --- a/drivers/scsi/fnic/fnic_res.c +++ b/drivers/scsi/fnic/fnic_res.c @@ -144,10 +144,9 @@ int fnic_get_vnic_config(struct fnic *fnic) c->intr_timer_type = c->intr_timer_type; shost_printk(KERN_INFO, fnic->lport->host, - "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x " + "vNIC MAC addr %pM " "wq/wq_copy/rq %d/%d/%d\n", - fnic->mac_addr[0], fnic->mac_addr[1], fnic->mac_addr[2], - fnic->mac_addr[3], fnic->mac_addr[4], fnic->mac_addr[5], + fnic->ctlr.ctl_src_addr, c->wq_enet_desc_count, c->wq_copy_desc_count, c->rq_desc_count); shost_printk(KERN_INFO, fnic->lport->host, diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h index b6f310262534..ef8aaf2156dd 100644 --- a/drivers/scsi/fnic/fnic_res.h +++ b/drivers/scsi/fnic/fnic_res.h @@ -51,6 +51,31 @@ static inline void fnic_queue_wq_desc(struct vnic_wq *wq, vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop); } +static inline void fnic_queue_wq_eth_desc(struct vnic_wq *wq, + void *os_buf, dma_addr_t dma_addr, + unsigned int len, + int vlan_tag_insert, + unsigned int vlan_tag, + int cq_entry) +{ + struct wq_enet_desc *desc = vnic_wq_next_desc(wq); + + wq_enet_desc_enc(desc, + (u64)dma_addr | VNIC_PADDR_TARGET, + (u16)len, + 0, /* mss_or_csum_offset */ + 0, /* fc_eof */ + 0, /* offload_mode */ + 1, /* eop */ + (u8)cq_entry, + 0, /* fcoe_encap */ + (u8)vlan_tag_insert, + (u16)vlan_tag, + 0 /* loopback */); + + vnic_wq_post(wq, os_buf, dma_addr, len, 1, 1); +} + static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq, u32 req_id, u32 lunmap_id, u8 spl_flags, @@ -58,6 +83,7 @@ static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq, u64 sgl_addr, u64 sns_addr, u8 crn, u8 pri_ta, u8 flags, u8 *scsi_cdb, + u8 cdb_len, u32 data_len, u8 *lun, u32 d_id, u16 mss, u32 ratov, u32 edtov) @@ -82,7 +108,8 @@ static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq, desc->u.icmnd_16.pri_ta = pri_ta; /* SCSI Pri & Task attribute */ desc->u.icmnd_16._resvd1 = 0; /* reserved: should be 0 */ desc->u.icmnd_16.flags = flags; /* command flags */ - memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, CDB_16); /* SCSI CDB */ + memset(desc->u.icmnd_16.scsi_cdb, 0, CDB_16); + memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, cdb_len); /* SCSI CDB */ desc->u.icmnd_16.data_len = data_len; /* length of data expected */ memcpy(desc->u.icmnd_16.lun, lun, LUN_ADDRESS); /* LUN address */ desc->u.icmnd_16._resvd2 = 0; /* reserved */ @@ -132,12 +159,37 @@ static inline void fnic_queue_wq_copy_desc_flogi_reg(struct vnic_wq_copy *wq, desc->hdr.tag.u.req_id = req_id; /* id for this request */ desc->u.flogi_reg.format = format; + desc->u.flogi_reg._resvd = 0; hton24(desc->u.flogi_reg.s_id, s_id); memcpy(desc->u.flogi_reg.gateway_mac, gw_mac, ETH_ALEN); vnic_wq_copy_post(wq); } +static inline void fnic_queue_wq_copy_desc_fip_reg(struct vnic_wq_copy *wq, + u32 req_id, u32 s_id, + u8 *fcf_mac, u8 *ha_mac, + u32 r_a_tov, u32 e_d_tov) +{ + struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq); + + desc->hdr.type = FCPIO_FLOGI_FIP_REG; /* enum fcpio_type */ + desc->hdr.status = 0; /* header status entry */ + desc->hdr._resvd = 0; /* reserved */ + desc->hdr.tag.u.req_id = req_id; /* id for this request */ + + desc->u.flogi_fip_reg._resvd0 = 0; + hton24(desc->u.flogi_fip_reg.s_id, s_id); + memcpy(desc->u.flogi_fip_reg.fcf_mac, fcf_mac, ETH_ALEN); + desc->u.flogi_fip_reg._resvd1 = 0; + desc->u.flogi_fip_reg.r_a_tov = r_a_tov; + desc->u.flogi_fip_reg.e_d_tov = e_d_tov; + memcpy(desc->u.flogi_fip_reg.ha_mac, ha_mac, ETH_ALEN); + desc->u.flogi_fip_reg._resvd2 = 0; + + vnic_wq_copy_post(wq); +} + static inline void fnic_queue_wq_copy_desc_fw_reset(struct vnic_wq_copy *wq, u32 req_id) { diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index bfc996971b81..65a39b0f6dc2 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -174,6 +174,9 @@ int fnic_fw_reset_handler(struct fnic *fnic) int ret = 0; unsigned long flags; + skb_queue_purge(&fnic->frame_queue); + skb_queue_purge(&fnic->tx_queue); + spin_lock_irqsave(&fnic->wq_copy_lock[0], flags); if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0]) @@ -200,9 +203,11 @@ int fnic_fw_reset_handler(struct fnic *fnic) * fnic_flogi_reg_handler * Routine to send flogi register msg to fw */ -int fnic_flogi_reg_handler(struct fnic *fnic) +int fnic_flogi_reg_handler(struct fnic *fnic, u32 fc_id) { struct vnic_wq_copy *wq = &fnic->wq_copy[0]; + enum fcpio_flogi_reg_format_type format; + struct fc_lport *lp = fnic->lport; u8 gw_mac[ETH_ALEN]; int ret = 0; unsigned long flags; @@ -217,23 +222,32 @@ int fnic_flogi_reg_handler(struct fnic *fnic) goto flogi_reg_ioreq_end; } - if (fnic->fcoui_mode) + if (fnic->ctlr.map_dest) { memset(gw_mac, 0xff, ETH_ALEN); - else - memcpy(gw_mac, fnic->dest_addr, ETH_ALEN); + format = FCPIO_FLOGI_REG_DEF_DEST; + } else { + memcpy(gw_mac, fnic->ctlr.dest_addr, ETH_ALEN); + format = FCPIO_FLOGI_REG_GW_DEST; + } - fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG, - FCPIO_FLOGI_REG_GW_DEST, - fnic->s_id, - gw_mac); + if ((fnic->config.flags & VFCF_FIP_CAPABLE) && !fnic->ctlr.map_dest) { + fnic_queue_wq_copy_desc_fip_reg(wq, SCSI_NO_TAG, + fc_id, gw_mac, + fnic->data_src_addr, + lp->r_a_tov, lp->e_d_tov); + FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, + "FLOGI FIP reg issued fcid %x src %pM dest %pM\n", + fc_id, fnic->data_src_addr, gw_mac); + } else { + fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG, + format, fc_id, gw_mac); + FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, + "FLOGI reg issued fcid %x map %d dest %pM\n", + fc_id, fnic->ctlr.map_dest, gw_mac); + } flogi_reg_ioreq_end: spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags); - - if (!ret) - FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, - "flog reg issued\n"); - return ret; } @@ -319,7 +333,8 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic, 0, /* scsi cmd ref, always 0 */ pri_tag, /* scsi pri and tag */ flags, /* command flags */ - sc->cmnd, scsi_bufflen(sc), + sc->cmnd, sc->cmd_len, + scsi_bufflen(sc), fc_lun.scsi_lun, io_req->port_id, rport->maxframe_size, rp->r_a_tov, rp->e_d_tov); @@ -452,7 +467,6 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic, u8 hdr_status; struct fcpio_tag tag; int ret = 0; - struct fc_frame *flogi; unsigned long flags; fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag); @@ -462,9 +476,6 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic, spin_lock_irqsave(&fnic->fnic_lock, flags); - flogi = fnic->flogi; - fnic->flogi = NULL; - /* fnic should be in FC_TRANS_ETH_MODE */ if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) { /* Check status of reset completion */ @@ -505,17 +516,14 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic, * free the flogi frame. Else, send it out */ if (fnic->remove_wait || ret) { - fnic->flogi_oxid = FC_XID_UNKNOWN; spin_unlock_irqrestore(&fnic->fnic_lock, flags); - if (flogi) - dev_kfree_skb_irq(fp_skb(flogi)); + skb_queue_purge(&fnic->tx_queue); goto reset_cmpl_handler_end; } spin_unlock_irqrestore(&fnic->fnic_lock, flags); - if (flogi) - ret = fnic_send_frame(fnic, flogi); + fnic_flush_tx(fnic); reset_cmpl_handler_end: return ret; @@ -532,18 +540,13 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic, u8 hdr_status; struct fcpio_tag tag; int ret = 0; - struct fc_frame *flogi_resp = NULL; unsigned long flags; - struct sk_buff *skb; fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag); /* Update fnic state based on status of flogi reg completion */ spin_lock_irqsave(&fnic->fnic_lock, flags); - flogi_resp = fnic->flogi_resp; - fnic->flogi_resp = NULL; - if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE) { /* Check flogi registration completion status */ @@ -567,25 +570,17 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic, ret = -1; } - /* Successful flogi reg cmpl, pass frame to LibFC */ - if (!ret && flogi_resp) { + if (!ret) { if (fnic->stop_rx_link_events) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); goto reg_cmpl_handler_end; } - skb = (struct sk_buff *)flogi_resp; - /* Use fr_flags to indicate whether flogi resp or not */ - fr_flags(flogi_resp) = 1; - fr_dev(flogi_resp) = fnic->lport; spin_unlock_irqrestore(&fnic->fnic_lock, flags); - skb_queue_tail(&fnic->frame_queue, skb); + fnic_flush_tx(fnic); queue_work(fnic_event_queue, &fnic->frame_work); - } else { spin_unlock_irqrestore(&fnic->fnic_lock, flags); - if (flogi_resp) - dev_kfree_skb_irq(fp_skb(flogi_resp)); } reg_cmpl_handler_end: @@ -907,6 +902,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev, break; case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */ + case FCPIO_FLOGI_FIP_REG_CMPL: /* fw completed flogi_fip_reg */ ret = fnic_fcpio_flogi_reg_cmpl_handler(fnic, desc); break; @@ -1224,22 +1220,6 @@ void fnic_terminate_rport_io(struct fc_rport *rport) } -static void fnic_block_error_handler(struct scsi_cmnd *sc) -{ - struct Scsi_Host *shost = sc->device->host; - struct fc_rport *rport = starget_to_rport(scsi_target(sc->device)); - unsigned long flags; - - spin_lock_irqsave(shost->host_lock, flags); - while (rport->port_state == FC_PORTSTATE_BLOCKED) { - spin_unlock_irqrestore(shost->host_lock, flags); - msleep(1000); - spin_lock_irqsave(shost->host_lock, flags); - } - spin_unlock_irqrestore(shost->host_lock, flags); - -} - /* * This function is exported to SCSI for sending abort cmnds. * A SCSI IO is represented by a io_req in the driver. @@ -1259,7 +1239,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) DECLARE_COMPLETION_ONSTACK(tm_done); /* Wait for rport to unblock */ - fnic_block_error_handler(sc); + fc_block_scsi_eh(sc); /* Get local-port, check ready and link up */ lp = shost_priv(sc->device->host); @@ -1541,7 +1521,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) DECLARE_COMPLETION_ONSTACK(tm_done); /* Wait for rport to unblock */ - fnic_block_error_handler(sc); + fc_block_scsi_eh(sc); /* Get local-port, check ready and link up */ lp = shost_priv(sc->device->host); @@ -1762,7 +1742,7 @@ void fnic_scsi_abort_io(struct fc_lport *lp) fnic->remove_wait = &remove_wait; old_state = fnic->state; fnic->state = FNIC_IN_FC_TRANS_ETH_MODE; - vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr); + fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr); spin_unlock_irqrestore(&fnic->fnic_lock, flags); err = fnic_fw_reset_handler(fnic); @@ -1802,7 +1782,7 @@ void fnic_scsi_cleanup(struct fc_lport *lp) spin_lock_irqsave(&fnic->fnic_lock, flags); old_state = fnic->state; fnic->state = FNIC_IN_FC_TRANS_ETH_MODE; - vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr); + fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr); spin_unlock_irqrestore(&fnic->fnic_lock, flags); if (fnic_fw_reset_handler(fnic)) { diff --git a/drivers/scsi/fnic/vnic_scsi.h b/drivers/scsi/fnic/vnic_scsi.h index 46baa5254001..fbb55364e272 100644 --- a/drivers/scsi/fnic/vnic_scsi.h +++ b/drivers/scsi/fnic/vnic_scsi.h @@ -95,5 +95,6 @@ struct vnic_fc_config { #define VFCF_FCP_SEQ_LVL_ERR 0x1 /* Enable FCP-2 Error Recovery */ #define VFCF_PERBI 0x2 /* persistent binding info available */ +#define VFCF_FIP_CAPABLE 0x4 /* firmware can handle FIP */ #endif /* _VNIC_SCSI_H_ */ diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index c968cc31cd86..554626e18062 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -180,14 +180,20 @@ void scsi_remove_host(struct Scsi_Host *shost) EXPORT_SYMBOL(scsi_remove_host); /** - * scsi_add_host - add a scsi host + * scsi_add_host_with_dma - add a scsi host with dma device * @shost: scsi host pointer to add * @dev: a struct device of type scsi class + * @dma_dev: dma device for the host + * + * Note: You rarely need to worry about this unless you're in a + * virtualised host environments, so use the simpler scsi_add_host() + * function instead. * * Return value: * 0 on success / != 0 for error **/ -int scsi_add_host(struct Scsi_Host *shost, struct device *dev) +int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, + struct device *dma_dev) { struct scsi_host_template *sht = shost->hostt; int error = -EINVAL; @@ -207,6 +213,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) if (!shost->shost_gendev.parent) shost->shost_gendev.parent = dev ? dev : &platform_bus; + shost->dma_dev = dma_dev; error = device_add(&shost->shost_gendev); if (error) @@ -262,7 +269,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) fail: return error; } -EXPORT_SYMBOL(scsi_add_host); +EXPORT_SYMBOL(scsi_add_host_with_dma); static void scsi_host_dev_release(struct device *dev) { diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c index a0e7e711ff9d..4f0556571f80 100644 --- a/drivers/scsi/hptiop.c +++ b/drivers/scsi/hptiop.c @@ -834,7 +834,7 @@ static int hptiop_reset_hba(struct hptiop_hba *hba) atomic_read(&hba->resetting) == 0, 60 * HZ); if (atomic_read(&hba->resetting)) { - /* IOP is in unkown state, abort reset */ + /* IOP is in unknown state, abort reset */ printk(KERN_ERR "scsi%d: reset failed\n", hba->host->host_no); return -1; } @@ -861,10 +861,13 @@ static int hptiop_reset(struct scsi_cmnd *scp) } static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev, - int queue_depth) + int queue_depth, int reason) { struct hptiop_hba *hba = (struct hptiop_hba *)sdev->host->hostdata; + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (queue_depth > hba->max_requests) queue_depth = hba->max_requests; scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index bb2c696c006a..87b536a97cb4 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -39,6 +39,7 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/scsi_bsg_fc.h> #include "ibmvfc.h" static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT; @@ -558,12 +559,11 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost, /** * ibmvfc_init_host - Start host initialization * @vhost: ibmvfc host struct - * @relogin: is this a re-login? * * Return value: * nothing **/ -static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin) +static void ibmvfc_init_host(struct ibmvfc_host *vhost) { struct ibmvfc_target *tgt; @@ -577,10 +577,8 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin) } if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { - if (!relogin) { - memset(vhost->async_crq.msgs, 0, PAGE_SIZE); - vhost->async_crq.cur = 0; - } + memset(vhost->async_crq.msgs, 0, PAGE_SIZE); + vhost->async_crq.cur = 0; list_for_each_entry(tgt, &vhost->targets, queue) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); @@ -1678,6 +1676,276 @@ static void ibmvfc_sync_completion(struct ibmvfc_event *evt) } /** + * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands + * @evt: struct ibmvfc_event + * + **/ +static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt) +{ + struct ibmvfc_host *vhost = evt->vhost; + + ibmvfc_free_event(evt); + vhost->aborting_passthru = 0; + dev_info(vhost->dev, "Passthru command cancelled\n"); +} + +/** + * ibmvfc_bsg_timeout - Handle a BSG timeout + * @job: struct fc_bsg_job that timed out + * + * Returns: + * 0 on success / other on failure + **/ +static int ibmvfc_bsg_timeout(struct fc_bsg_job *job) +{ + struct ibmvfc_host *vhost = shost_priv(job->shost); + unsigned long port_id = (unsigned long)job->dd_data; + struct ibmvfc_event *evt; + struct ibmvfc_tmf *tmf; + unsigned long flags; + int rc; + + ENTER; + spin_lock_irqsave(vhost->host->host_lock, flags); + if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) { + __ibmvfc_reset_host(vhost); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + return 0; + } + + vhost->aborting_passthru = 1; + evt = ibmvfc_get_event(vhost); + ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT); + + tmf = &evt->iu.tmf; + memset(tmf, 0, sizeof(*tmf)); + tmf->common.version = 1; + tmf->common.opcode = IBMVFC_TMF_MAD; + tmf->common.length = sizeof(*tmf); + tmf->scsi_id = port_id; + tmf->cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY; + tmf->my_cancel_key = IBMVFC_INTERNAL_CANCEL_KEY; + rc = ibmvfc_send_event(evt, vhost, default_timeout); + + if (rc != 0) { + vhost->aborting_passthru = 0; + dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc); + rc = -EIO; + } else + dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n", + port_id); + + spin_unlock_irqrestore(vhost->host->host_lock, flags); + + LEAVE; + return rc; +} + +/** + * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command + * @vhost: struct ibmvfc_host to send command + * @port_id: port ID to send command + * + * Returns: + * 0 on success / other on failure + **/ +static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id) +{ + struct ibmvfc_port_login *plogi; + struct ibmvfc_target *tgt; + struct ibmvfc_event *evt; + union ibmvfc_iu rsp_iu; + unsigned long flags; + int rc = 0, issue_login = 1; + + ENTER; + spin_lock_irqsave(vhost->host->host_lock, flags); + list_for_each_entry(tgt, &vhost->targets, queue) { + if (tgt->scsi_id == port_id) { + issue_login = 0; + break; + } + } + + if (!issue_login) + goto unlock_out; + if (unlikely((rc = ibmvfc_host_chkready(vhost)))) + goto unlock_out; + + evt = ibmvfc_get_event(vhost); + ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); + plogi = &evt->iu.plogi; + memset(plogi, 0, sizeof(*plogi)); + plogi->common.version = 1; + plogi->common.opcode = IBMVFC_PORT_LOGIN; + plogi->common.length = sizeof(*plogi); + plogi->scsi_id = port_id; + evt->sync_iu = &rsp_iu; + init_completion(&evt->comp); + + rc = ibmvfc_send_event(evt, vhost, default_timeout); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + + if (rc) + return -EIO; + + wait_for_completion(&evt->comp); + + if (rsp_iu.plogi.common.status) + rc = -EIO; + + spin_lock_irqsave(vhost->host->host_lock, flags); + ibmvfc_free_event(evt); +unlock_out: + spin_unlock_irqrestore(vhost->host->host_lock, flags); + LEAVE; + return rc; +} + +/** + * ibmvfc_bsg_request - Handle a BSG request + * @job: struct fc_bsg_job to be executed + * + * Returns: + * 0 on success / other on failure + **/ +static int ibmvfc_bsg_request(struct fc_bsg_job *job) +{ + struct ibmvfc_host *vhost = shost_priv(job->shost); + struct fc_rport *rport = job->rport; + struct ibmvfc_passthru_mad *mad; + struct ibmvfc_event *evt; + union ibmvfc_iu rsp_iu; + unsigned long flags, port_id = -1; + unsigned int code = job->request->msgcode; + int rc = 0, req_seg, rsp_seg, issue_login = 0; + u32 fc_flags, rsp_len; + + ENTER; + job->reply->reply_payload_rcv_len = 0; + if (rport) + port_id = rport->port_id; + + switch (code) { + case FC_BSG_HST_ELS_NOLOGIN: + port_id = (job->request->rqst_data.h_els.port_id[0] << 16) | + (job->request->rqst_data.h_els.port_id[1] << 8) | + job->request->rqst_data.h_els.port_id[2]; + case FC_BSG_RPT_ELS: + fc_flags = IBMVFC_FC_ELS; + break; + case FC_BSG_HST_CT: + issue_login = 1; + port_id = (job->request->rqst_data.h_ct.port_id[0] << 16) | + (job->request->rqst_data.h_ct.port_id[1] << 8) | + job->request->rqst_data.h_ct.port_id[2]; + case FC_BSG_RPT_CT: + fc_flags = IBMVFC_FC_CT_IU; + break; + default: + return -ENOTSUPP; + }; + + if (port_id == -1) + return -EINVAL; + if (!mutex_trylock(&vhost->passthru_mutex)) + return -EBUSY; + + job->dd_data = (void *)port_id; + req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + + if (!req_seg) { + mutex_unlock(&vhost->passthru_mutex); + return -ENOMEM; + } + + rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list, + job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + + if (!rsp_seg) { + dma_unmap_sg(vhost->dev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + mutex_unlock(&vhost->passthru_mutex); + return -ENOMEM; + } + + if (req_seg > 1 || rsp_seg > 1) { + rc = -EINVAL; + goto out; + } + + if (issue_login) + rc = ibmvfc_bsg_plogi(vhost, port_id); + + spin_lock_irqsave(vhost->host->host_lock, flags); + + if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) || + unlikely((rc = ibmvfc_host_chkready(vhost)))) { + spin_unlock_irqrestore(vhost->host->host_lock, flags); + goto out; + } + + evt = ibmvfc_get_event(vhost); + ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); + mad = &evt->iu.passthru; + + memset(mad, 0, sizeof(*mad)); + mad->common.version = 1; + mad->common.opcode = IBMVFC_PASSTHRU; + mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu); + + mad->cmd_ioba.va = (u64)evt->crq.ioba + + offsetof(struct ibmvfc_passthru_mad, iu); + mad->cmd_ioba.len = sizeof(mad->iu); + + mad->iu.cmd_len = job->request_payload.payload_len; + mad->iu.rsp_len = job->reply_payload.payload_len; + mad->iu.flags = fc_flags; + mad->iu.cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY; + + mad->iu.cmd.va = sg_dma_address(job->request_payload.sg_list); + mad->iu.cmd.len = sg_dma_len(job->request_payload.sg_list); + mad->iu.rsp.va = sg_dma_address(job->reply_payload.sg_list); + mad->iu.rsp.len = sg_dma_len(job->reply_payload.sg_list); + mad->iu.scsi_id = port_id; + mad->iu.tag = (u64)evt; + rsp_len = mad->iu.rsp.len; + + evt->sync_iu = &rsp_iu; + init_completion(&evt->comp); + rc = ibmvfc_send_event(evt, vhost, 0); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + + if (rc) { + rc = -EIO; + goto out; + } + + wait_for_completion(&evt->comp); + + if (rsp_iu.passthru.common.status) + rc = -EIO; + else + job->reply->reply_payload_rcv_len = rsp_len; + + spin_lock_irqsave(vhost->host->host_lock, flags); + ibmvfc_free_event(evt); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + job->reply->result = rc; + job->job_done(job); + rc = 0; +out: + dma_unmap_sg(vhost->dev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + dma_unmap_sg(vhost->dev, job->reply_payload.sg_list, + job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + mutex_unlock(&vhost->passthru_mutex); + LEAVE; + return rc; +} + +/** * ibmvfc_reset_device - Reset the device with the specified reset type * @sdev: scsi device to reset * @type: reset type @@ -1731,7 +1999,10 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc) sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc); wait_for_completion(&evt->comp); - if (rsp_iu.cmd.status) { + if (rsp_iu.cmd.status) + rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd); + + if (rsp_code) { if (fc_rsp->flags & FCP_RSP_LEN_VALID) rsp_code = fc_rsp->data.info.rsp_code; @@ -1820,7 +2091,10 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev) sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n"); wait_for_completion(&evt->comp); - if (rsp_iu.cmd.status) { + if (rsp_iu.cmd.status) + rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd); + + if (rsp_code) { if (fc_rsp->flags & FCP_RSP_LEN_VALID) rsp_code = fc_rsp->data.info.rsp_code; @@ -2061,12 +2335,24 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd) } /** - * ibmvfc_dev_cancel_all - Device iterated cancel all function + * ibmvfc_dev_cancel_all_abts - Device iterated cancel all function * @sdev: scsi device struct * @data: return code * **/ -static void ibmvfc_dev_cancel_all(struct scsi_device *sdev, void *data) +static void ibmvfc_dev_cancel_all_abts(struct scsi_device *sdev, void *data) +{ + unsigned long *rc = data; + *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET); +} + +/** + * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function + * @sdev: scsi device struct + * @data: return code + * + **/ +static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data) { unsigned long *rc = data; *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET); @@ -2102,7 +2388,7 @@ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd) ENTER; ibmvfc_wait_while_resetting(vhost); - starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all); + starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset); reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target"); if (!cancel_rc && !reset_rc) @@ -2144,7 +2430,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport) int rc = FAILED; ENTER; - starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all); + starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts); starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all); if (!cancel_rc && !abort_rc) @@ -2297,13 +2583,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost) /* Send back a response */ rc = ibmvfc_send_crq_init_complete(vhost); if (rc == 0) - ibmvfc_init_host(vhost, 0); + ibmvfc_init_host(vhost); else dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc); break; case IBMVFC_CRQ_INIT_COMPLETE: dev_info(vhost->dev, "Partner initialization complete\n"); - ibmvfc_init_host(vhost, 0); + ibmvfc_init_host(vhost); break; default: dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format); @@ -2478,12 +2764,17 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev) * ibmvfc_change_queue_depth - Change the device's queue depth * @sdev: scsi device struct * @qdepth: depth to set + * @reason: calling context * * Return value: * actual depth set **/ -static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth) +static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (qdepth > IBMVFC_MAX_CMDS_PER_LUN) qdepth = IBMVFC_MAX_CMDS_PER_LUN; @@ -3725,7 +4016,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt) case IBMVFC_MAD_SUCCESS: if (list_empty(&vhost->sent) && vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) { - ibmvfc_init_host(vhost, 0); + ibmvfc_init_host(vhost); return; } break; @@ -3903,6 +4194,8 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt) rport->supported_classes |= FC_COS_CLASS2; if (tgt->service_parms.class3_parms[0] & 0x80000000) rport->supported_classes |= FC_COS_CLASS3; + if (rport->rqst_q) + blk_queue_max_hw_segments(rport->rqst_q, 1); } else tgt_dbg(tgt, "rport add failed\n"); spin_unlock_irqrestore(vhost->host->host_lock, flags); @@ -4342,6 +4635,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) init_waitqueue_head(&vhost->work_wait_q); init_waitqueue_head(&vhost->init_wait_q); INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread); + mutex_init(&vhost->passthru_mutex); if ((rc = ibmvfc_alloc_mem(vhost))) goto free_scsi_host; @@ -4374,6 +4668,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) goto remove_shost; } + if (shost_to_fc_host(shost)->rqst_q) + blk_queue_max_hw_segments(shost_to_fc_host(shost)->rqst_q, 1); dev_set_drvdata(dev, vhost); spin_lock(&ibmvfc_driver_lock); list_add_tail(&vhost->queue, &ibmvfc_head); @@ -4414,7 +4710,11 @@ static int ibmvfc_remove(struct vio_dev *vdev) ENTER; ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr); + + spin_lock_irqsave(vhost->host->host_lock, flags); ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + ibmvfc_wait_while_resetting(vhost); ibmvfc_release_crq_queue(vhost); kthread_stop(vhost->work_thread); @@ -4498,6 +4798,9 @@ static struct fc_function_template ibmvfc_transport_functions = { .get_starget_port_id = ibmvfc_get_starget_port_id, .show_starget_port_id = 1, + + .bsg_request = ibmvfc_bsg_request, + .bsg_timeout = ibmvfc_bsg_timeout, }; /** diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index 007fa1c9ef14..d25106a958d7 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -29,8 +29,8 @@ #include "viosrp.h" #define IBMVFC_NAME "ibmvfc" -#define IBMVFC_DRIVER_VERSION "1.0.6" -#define IBMVFC_DRIVER_DATE "(May 28, 2009)" +#define IBMVFC_DRIVER_VERSION "1.0.7" +#define IBMVFC_DRIVER_DATE "(October 16, 2009)" #define IBMVFC_DEFAULT_TIMEOUT 60 #define IBMVFC_ADISC_CANCEL_TIMEOUT 45 @@ -58,9 +58,10 @@ * 1 for ERP * 1 for initialization * 1 for NPIV Logout + * 2 for BSG passthru * 2 for each discovery thread */ -#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + (disc_threads * 2)) +#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + 2 + (disc_threads * 2)) #define IBMVFC_MAD_SUCCESS 0x00 #define IBMVFC_MAD_NOT_SUPPORTED 0xF1 @@ -466,7 +467,10 @@ struct ibmvfc_passthru_iu { u16 error; u32 flags; #define IBMVFC_FC_ELS 0x01 +#define IBMVFC_FC_CT_IU 0x02 u32 cancel_key; +#define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000 +#define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001 u32 reserved; struct srp_direct_buf cmd; struct srp_direct_buf rsp; @@ -693,6 +697,7 @@ struct ibmvfc_host { int disc_buf_sz; int log_level; struct ibmvfc_discover_targets_buf *disc_buf; + struct mutex passthru_mutex; int task_set; int init_retries; int discovery_threads; @@ -702,6 +707,7 @@ struct ibmvfc_host { int delay_init; int scan_complete; int logged_in; + int aborting_passthru; int events_to_log; #define IBMVFC_AE_LINKUP 0x0001 #define IBMVFC_AE_LINKDOWN 0x0002 diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index d9b0e9d31983..e475b7957c2d 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -1637,12 +1637,17 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev) * ibmvscsi_change_queue_depth - Change the device's queue depth * @sdev: scsi device struct * @qdepth: depth to set + * @reason: calling context * * Return value: * actual depth set **/ -static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth) +static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (qdepth > IBMVSCSI_MAX_CMDS_PER_LUN) qdepth = IBMVSCSI_MAX_CMDS_PER_LUN; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 76d294fc7846..206c2fa8c1ba 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3367,16 +3367,21 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; }; * ipr_change_queue_depth - Change the device's queue depth * @sdev: scsi device struct * @qdepth: depth to set + * @reason: calling context * * Return value: * actual depth set **/ -static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth) +static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth, + int reason) { struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata; struct ipr_resource_entry *res; unsigned long lock_flags = 0; + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); res = (struct ipr_resource_entry *)sdev->hostdata; diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index edc49ca49cea..517da3fd89d3 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -903,7 +903,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = { ISCSI_USERNAME | ISCSI_PASSWORD | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | - ISCSI_LU_RESET_TMO | + ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO | ISCSI_PING_TMO | ISCSI_RECV_TMO | ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile index 55f982de3a9a..4bb23ac86a5c 100644 --- a/drivers/scsi/libfc/Makefile +++ b/drivers/scsi/libfc/Makefile @@ -3,10 +3,12 @@ obj-$(CONFIG_LIBFC) += libfc.o libfc-objs := \ + fc_libfc.o \ fc_disc.o \ fc_exch.o \ fc_elsct.o \ fc_frame.o \ fc_lport.o \ fc_rport.o \ - fc_fcp.o + fc_fcp.o \ + fc_npiv.o diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index c48799e9dd8e..9b0a5192a965 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -40,6 +40,8 @@ #include <scsi/libfc.h> +#include "fc_libfc.h" + #define FC_DISC_RETRY_LIMIT 3 /* max retries */ #define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */ @@ -51,8 +53,8 @@ static int fc_disc_single(struct fc_lport *, struct fc_disc_port *); static void fc_disc_restart(struct fc_disc *); /** - * fc_disc_stop_rports() - delete all the remote ports associated with the lport - * @disc: The discovery job to stop rports on + * fc_disc_stop_rports() - Delete all the remote ports associated with the lport + * @disc: The discovery job to stop remote ports on * * Locking Note: This function expects that the lport mutex is locked before * calling it. @@ -72,9 +74,9 @@ void fc_disc_stop_rports(struct fc_disc *disc) /** * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN) - * @sp: Current sequence of the RSCN exchange - * @fp: RSCN Frame - * @lport: Fibre Channel host port instance + * @sp: The sequence of the RSCN exchange + * @fp: The RSCN frame + * @lport: The local port that the request will be sent on * * Locking Note: This function expects that the disc_mutex is locked * before it is called. @@ -183,9 +185,9 @@ reject: /** * fc_disc_recv_req() - Handle incoming requests - * @sp: Current sequence of the request exchange - * @fp: The frame - * @lport: The FC local port + * @sp: The sequence of the request exchange + * @fp: The request frame + * @lport: The local port receiving the request * * Locking Note: This function is called from the EM and will lock * the disc_mutex before calling the handler for the @@ -213,7 +215,7 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp, /** * fc_disc_restart() - Restart discovery - * @lport: FC discovery context + * @disc: The discovery object to be restarted * * Locking Note: This function expects that the disc mutex * is already locked. @@ -240,9 +242,9 @@ static void fc_disc_restart(struct fc_disc *disc) } /** - * fc_disc_start() - Fibre Channel Target discovery - * @lport: FC local port - * @disc_callback: function to be called when discovery is complete + * fc_disc_start() - Start discovery on a local port + * @lport: The local port to have discovery started on + * @disc_callback: Callback function to be called when discovery is complete */ static void fc_disc_start(void (*disc_callback)(struct fc_lport *, enum fc_disc_event), @@ -263,8 +265,8 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *, /** * fc_disc_done() - Discovery has been completed - * @disc: FC discovery context - * @event: discovery completion status + * @disc: The discovery context + * @event: The discovery completion status * * Locking Note: This function expects that the disc mutex is locked before * it is called. The discovery callback is then made with the lock released, @@ -284,8 +286,8 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event) } /* - * Go through all remote ports. If they were found in the latest - * discovery, reverify or log them in. Otherwise, log them out. + * Go through all remote ports. If they were found in the latest + * discovery, reverify or log them in. Otherwise, log them out. * Skip ports which were never discovered. These are the dNS port * and ports which were created by PLOGI. */ @@ -305,8 +307,8 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event) /** * fc_disc_error() - Handle error on dNS request - * @disc: FC discovery context - * @fp: The frame pointer + * @disc: The discovery context + * @fp: The error code encoded as a frame pointer */ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp) { @@ -342,7 +344,7 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp) /** * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request - * @lport: FC discovery context + * @lport: The discovery context * * Locking Note: This function expects that the disc_mutex is locked * before it is called. @@ -368,17 +370,17 @@ static void fc_disc_gpn_ft_req(struct fc_disc *disc) if (lport->tt.elsct_send(lport, 0, fp, FC_NS_GPN_FT, fc_disc_gpn_ft_resp, - disc, lport->e_d_tov)) + disc, 3 * lport->r_a_tov)) return; err: - fc_disc_error(disc, fp); + fc_disc_error(disc, NULL); } /** * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response. - * @lport: Fibre Channel host port instance - * @buf: GPN_FT response buffer - * @len: size of response buffer + * @lport: The local port the GPN_FT was received on + * @buf: The GPN_FT response buffer + * @len: The size of response buffer * * Goes through the list of IDs and names resulting from a request. */ @@ -477,10 +479,8 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) } /** - * fc_disc_timeout() - Retry handler for the disc component - * @work: Structure holding disc obj that needs retry discovery - * - * Handle retry of memory allocation for remote ports. + * fc_disc_timeout() - Handler for discovery timeouts + * @work: Structure holding discovery context that needs to retry discovery */ static void fc_disc_timeout(struct work_struct *work) { @@ -494,9 +494,9 @@ static void fc_disc_timeout(struct work_struct *work) /** * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT) - * @sp: Current sequence of GPN_FT exchange - * @fp: response frame - * @lp_arg: Fibre Channel host port instance + * @sp: The sequence that the GPN_FT response was received on + * @fp: The GPN_FT response frame + * @lp_arg: The discovery context * * Locking Note: This function is called without disc mutex held, and * should do all its processing with the mutex held @@ -567,9 +567,9 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, /** * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID) - * @sp: exchange sequence - * @fp: response frame - * @rdata_arg: remote port private data + * @sp: The sequence the GPN_ID is on + * @fp: The response frame + * @rdata_arg: The remote port that sent the GPN_ID response * * Locking Note: This function is called without disc mutex held. */ @@ -637,7 +637,7 @@ out: /** * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request - * @lport: local port + * @lport: The local port to initiate discovery on * @rdata: remote port private data * * Locking Note: This function expects that the disc_mutex is locked @@ -654,7 +654,8 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport, if (!fp) return -ENOMEM; if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID, - fc_disc_gpn_id_resp, rdata, lport->e_d_tov)) + fc_disc_gpn_id_resp, rdata, + 3 * lport->r_a_tov)) return -ENOMEM; kref_get(&rdata->kref); return 0; @@ -662,8 +663,8 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport, /** * fc_disc_single() - Discover the directory information for a single target - * @lport: local port - * @dp: The port to rediscover + * @lport: The local port the remote port is associated with + * @dp: The port to rediscover * * Locking Note: This function expects that the disc_mutex is locked * before it is called. @@ -681,7 +682,7 @@ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp) /** * fc_disc_stop() - Stop discovery for a given lport - * @lport: The lport that discovery should stop for + * @lport: The local port that discovery should stop on */ void fc_disc_stop(struct fc_lport *lport) { @@ -695,7 +696,7 @@ void fc_disc_stop(struct fc_lport *lport) /** * fc_disc_stop_final() - Stop discovery for a given lport - * @lport: The lport that discovery should stop for + * @lport: The lport that discovery should stop on * * This function will block until discovery has been * completely stopped and all rports have been deleted. @@ -707,8 +708,8 @@ void fc_disc_stop_final(struct fc_lport *lport) } /** - * fc_disc_init() - Initialize the discovery block - * @lport: FC local port + * fc_disc_init() - Initialize the discovery layer for a local port + * @lport: The local port that needs the discovery layer to be initialized */ int fc_disc_init(struct fc_lport *lport) { diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c index 5cfa68732e9d..53748724f2c5 100644 --- a/drivers/scsi/libfc/fc_elsct.c +++ b/drivers/scsi/libfc/fc_elsct.c @@ -28,17 +28,22 @@ #include <scsi/libfc.h> #include <scsi/fc_encode.h> -/* - * fc_elsct_send - sends ELS/CT frame +/** + * fc_elsct_send() - Send an ELS or CT frame + * @lport: The local port to send the frame on + * @did: The destination ID for the frame + * @fp: The frame to be sent + * @op: The operational code + * @resp: The callback routine when the response is received + * @arg: The argument to pass to the response callback routine + * @timer_msec: The timeout period for the frame (in msecs) */ -static struct fc_seq *fc_elsct_send(struct fc_lport *lport, - u32 did, - struct fc_frame *fp, - unsigned int op, - void (*resp)(struct fc_seq *, - struct fc_frame *fp, - void *arg), - void *arg, u32 timer_msec) +struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did, + struct fc_frame *fp, unsigned int op, + void (*resp)(struct fc_seq *, + struct fc_frame *, + void *), + void *arg, u32 timer_msec) { enum fc_rctl r_ctl; enum fc_fh_type fh_type; @@ -53,15 +58,22 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport, did = FC_FID_DIR_SERV; } - if (rc) + if (rc) { + fc_frame_free(fp); return NULL; + } fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec); } +EXPORT_SYMBOL(fc_elsct_send); +/** + * fc_elsct_init() - Initialize the ELS/CT layer + * @lport: The local port to initialize the ELS/CT layer for + */ int fc_elsct_init(struct fc_lport *lport) { if (!lport->tt.elsct_send) @@ -72,12 +84,15 @@ int fc_elsct_init(struct fc_lport *lport) EXPORT_SYMBOL(fc_elsct_init); /** - * fc_els_resp_type() - return string describing ELS response for debug. - * @fp: frame pointer with possible error code. + * fc_els_resp_type() - Return a string describing the ELS response + * @fp: The frame pointer or possible error code */ const char *fc_els_resp_type(struct fc_frame *fp) { const char *msg; + struct fc_frame_header *fh; + struct fc_ct_hdr *ct; + if (IS_ERR(fp)) { switch (-PTR_ERR(fp)) { case FC_NO_ERR: @@ -94,15 +109,41 @@ const char *fc_els_resp_type(struct fc_frame *fp) break; } } else { - switch (fc_frame_payload_op(fp)) { - case ELS_LS_ACC: - msg = "accept"; + fh = fc_frame_header_get(fp); + switch (fh->fh_type) { + case FC_TYPE_ELS: + switch (fc_frame_payload_op(fp)) { + case ELS_LS_ACC: + msg = "accept"; + break; + case ELS_LS_RJT: + msg = "reject"; + break; + default: + msg = "response unknown ELS"; + break; + } break; - case ELS_LS_RJT: - msg = "reject"; + case FC_TYPE_CT: + ct = fc_frame_payload_get(fp, sizeof(*ct)); + if (ct) { + switch (ntohs(ct->ct_cmd)) { + case FC_FS_ACC: + msg = "CT accept"; + break; + case FC_FS_RJT: + msg = "CT reject"; + break; + default: + msg = "response unknown CT"; + break; + } + } else { + msg = "short CT response"; + } break; default: - msg = "response unknown ELS"; + msg = "response not ELS or CT"; break; } } diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index c1c15748220c..19d711cb938c 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -32,10 +32,13 @@ #include <scsi/libfc.h> #include <scsi/fc_encode.h> +#include "fc_libfc.h" + u16 fc_cpu_mask; /* cpu mask for possible cpus */ EXPORT_SYMBOL(fc_cpu_mask); static u16 fc_cpu_order; /* 2's power to represent total possible cpus */ -static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ +static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ +struct workqueue_struct *fc_exch_workqueue; /* * Structure and function definitions for managing Fibre Channel Exchanges @@ -50,35 +53,46 @@ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ * fc_seq holds the state for an individual sequence. */ -/* - * Per cpu exchange pool +/** + * struct fc_exch_pool - Per cpu exchange pool + * @next_index: Next possible free exchange index + * @total_exches: Total allocated exchanges + * @lock: Exch pool lock + * @ex_list: List of exchanges * * This structure manages per cpu exchanges in array of exchange pointers. * This array is allocated followed by struct fc_exch_pool memory for * assigned range of exchanges to per cpu pool. */ struct fc_exch_pool { - u16 next_index; /* next possible free exchange index */ - u16 total_exches; /* total allocated exchanges */ - spinlock_t lock; /* exch pool lock */ - struct list_head ex_list; /* allocated exchanges list */ + u16 next_index; + u16 total_exches; + spinlock_t lock; + struct list_head ex_list; }; -/* - * Exchange manager. +/** + * struct fc_exch_mgr - The Exchange Manager (EM). + * @class: Default class for new sequences + * @kref: Reference counter + * @min_xid: Minimum exchange ID + * @max_xid: Maximum exchange ID + * @ep_pool: Reserved exchange pointers + * @pool_max_index: Max exch array index in exch pool + * @pool: Per cpu exch pool + * @stats: Statistics structure * * This structure is the center for creating exchanges and sequences. * It manages the allocation of exchange IDs. */ struct fc_exch_mgr { - enum fc_class class; /* default class for sequences */ - struct kref kref; /* exchange mgr reference count */ - u16 min_xid; /* min exchange ID */ - u16 max_xid; /* max exchange ID */ - struct list_head ex_list; /* allocated exchanges list */ - mempool_t *ep_pool; /* reserve ep's */ - u16 pool_max_index; /* max exch array index in exch pool */ - struct fc_exch_pool *pool; /* per cpu exch pool */ + enum fc_class class; + struct kref kref; + u16 min_xid; + u16 max_xid; + mempool_t *ep_pool; + u16 pool_max_index; + struct fc_exch_pool *pool; /* * currently exchange mgr stats are updated but not used. @@ -96,6 +110,18 @@ struct fc_exch_mgr { }; #define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq) +/** + * struct fc_exch_mgr_anchor - primary structure for list of EMs + * @ema_list: Exchange Manager Anchor list + * @mp: Exchange Manager associated with this anchor + * @match: Routine to determine if this anchor's EM should be used + * + * When walking the list of anchors the match routine will be called + * for each anchor to determine if that EM should be used. The last + * anchor in the list will always match to handle any exchanges not + * handled by other EMs. The non-default EMs would be added to the + * anchor list by HW that provides FCoE offloads. + */ struct fc_exch_mgr_anchor { struct list_head ema_list; struct fc_exch_mgr *mp; @@ -108,7 +134,6 @@ static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason, enum fc_els_rjt_explan); static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *); static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *); -static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp); /* * Internal implementation notes. @@ -196,6 +221,15 @@ static char *fc_exch_rctl_names[] = FC_RCTL_NAMES_INIT; #define FC_TABLE_SIZE(x) (sizeof(x) / sizeof(x[0])) +/** + * fc_exch_name_lookup() - Lookup name by opcode + * @op: Opcode to be looked up + * @table: Opcode/name table + * @max_index: Index not to be exceeded + * + * This routine is used to determine a human-readable string identifying + * a R_CTL opcode. + */ static inline const char *fc_exch_name_lookup(unsigned int op, char **table, unsigned int max_index) { @@ -208,25 +242,34 @@ static inline const char *fc_exch_name_lookup(unsigned int op, char **table, return name; } +/** + * fc_exch_rctl_name() - Wrapper routine for fc_exch_name_lookup() + * @op: The opcode to be looked up + */ static const char *fc_exch_rctl_name(unsigned int op) { return fc_exch_name_lookup(op, fc_exch_rctl_names, FC_TABLE_SIZE(fc_exch_rctl_names)); } -/* - * Hold an exchange - keep it from being freed. +/** + * fc_exch_hold() - Increment an exchange's reference count + * @ep: Echange to be held */ -static void fc_exch_hold(struct fc_exch *ep) +static inline void fc_exch_hold(struct fc_exch *ep) { atomic_inc(&ep->ex_refcnt); } -/* - * setup fc hdr by initializing few more FC header fields and sof/eof. - * Initialized fields by this func: - * - fh_ox_id, fh_rx_id, fh_seq_id, fh_seq_cnt - * - sof and eof +/** + * fc_exch_setup_hdr() - Initialize a FC header by initializing some fields + * and determine SOF and EOF. + * @ep: The exchange to that will use the header + * @fp: The frame whose header is to be modified + * @f_ctl: F_CTL bits that will be used for the frame header + * + * The fields initialized by this routine are: fh_ox_id, fh_rx_id, + * fh_seq_id, fh_seq_cnt and the SOF and EOF. */ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp, u32 f_ctl) @@ -243,7 +286,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp, if (fc_sof_needs_ack(ep->class)) fr_eof(fp) = FC_EOF_N; /* - * Form f_ctl. + * From F_CTL. * The number of fill bytes to make the length a 4-byte * multiple is the low order 2-bits of the f_ctl. * The fill itself will have been cleared by the frame @@ -273,10 +316,12 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp, fh->fh_seq_cnt = htons(ep->seq.cnt); } - -/* - * Release a reference to an exchange. - * If the refcnt goes to zero and the exchange is complete, it is freed. +/** + * fc_exch_release() - Decrement an exchange's reference count + * @ep: Exchange to be released + * + * If the reference count reaches zero and the exchange is complete, + * it is freed. */ static void fc_exch_release(struct fc_exch *ep) { @@ -291,6 +336,10 @@ static void fc_exch_release(struct fc_exch *ep) } } +/** + * fc_exch_done_locked() - Complete an exchange with the exchange lock held + * @ep: The exchange that is complete + */ static int fc_exch_done_locked(struct fc_exch *ep) { int rc = 1; @@ -315,6 +364,15 @@ static int fc_exch_done_locked(struct fc_exch *ep) return rc; } +/** + * fc_exch_ptr_get() - Return an exchange from an exchange pool + * @pool: Exchange Pool to get an exchange from + * @index: Index of the exchange within the pool + * + * Use the index to get an exchange from within an exchange pool. exches + * will point to an array of exchange pointers. The index will select + * the exchange within the array. + */ static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool, u16 index) { @@ -322,12 +380,22 @@ static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool, return exches[index]; } +/** + * fc_exch_ptr_set() - Assign an exchange to a slot in an exchange pool + * @pool: The pool to assign the exchange to + * @index: The index in the pool where the exchange will be assigned + * @ep: The exchange to assign to the pool + */ static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index, struct fc_exch *ep) { ((struct fc_exch **)(pool + 1))[index] = ep; } +/** + * fc_exch_delete() - Delete an exchange + * @ep: The exchange to be deleted + */ static void fc_exch_delete(struct fc_exch *ep) { struct fc_exch_pool *pool; @@ -343,8 +411,14 @@ static void fc_exch_delete(struct fc_exch *ep) fc_exch_release(ep); /* drop hold for exch in mp */ } -/* - * Internal version of fc_exch_timer_set - used with lock held. +/** + * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the + * the exchange lock held + * @ep: The exchange whose timer will start + * @timer_msec: The timeout period + * + * Used for upper level protocols to time out the exchange. + * The timer is cancelled when it fires or when the exchange completes. */ static inline void fc_exch_timer_set_locked(struct fc_exch *ep, unsigned int timer_msec) @@ -354,17 +428,15 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep, FC_EXCH_DBG(ep, "Exchange timer armed\n"); - if (schedule_delayed_work(&ep->timeout_work, - msecs_to_jiffies(timer_msec))) + if (queue_delayed_work(fc_exch_workqueue, &ep->timeout_work, + msecs_to_jiffies(timer_msec))) fc_exch_hold(ep); /* hold for timer */ } -/* - * Set timer for an exchange. - * The time is a minimum delay in milliseconds until the timer fires. - * Used for upper level protocols to time out the exchange. - * The timer is cancelled when it fires or when the exchange completes. - * Returns non-zero if a timer couldn't be allocated. +/** + * fc_exch_timer_set() - Lock the exchange and set the timer + * @ep: The exchange whose timer will start + * @timer_msec: The timeout period */ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec) { @@ -373,7 +445,115 @@ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec) spin_unlock_bh(&ep->ex_lock); } -int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec) +/** + * fc_seq_send() - Send a frame using existing sequence/exchange pair + * @lport: The local port that the exchange will be sent on + * @sp: The sequence to be sent + * @fp: The frame to be sent on the exchange + */ +static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, + struct fc_frame *fp) +{ + struct fc_exch *ep; + struct fc_frame_header *fh = fc_frame_header_get(fp); + int error; + u32 f_ctl; + + ep = fc_seq_exch(sp); + WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT); + + f_ctl = ntoh24(fh->fh_f_ctl); + fc_exch_setup_hdr(ep, fp, f_ctl); + + /* + * update sequence count if this frame is carrying + * multiple FC frames when sequence offload is enabled + * by LLD. + */ + if (fr_max_payload(fp)) + sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)), + fr_max_payload(fp)); + else + sp->cnt++; + + /* + * Send the frame. + */ + error = lport->tt.frame_send(lport, fp); + + /* + * Update the exchange and sequence flags, + * assuming all frames for the sequence have been sent. + * We can only be called to send once for each sequence. + */ + spin_lock_bh(&ep->ex_lock); + ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */ + if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT)) + ep->esb_stat &= ~ESB_ST_SEQ_INIT; + spin_unlock_bh(&ep->ex_lock); + return error; +} + +/** + * fc_seq_alloc() - Allocate a sequence for a given exchange + * @ep: The exchange to allocate a new sequence for + * @seq_id: The sequence ID to be used + * + * We don't support multiple originated sequences on the same exchange. + * By implication, any previously originated sequence on this exchange + * is complete, and we reallocate the same sequence. + */ +static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id) +{ + struct fc_seq *sp; + + sp = &ep->seq; + sp->ssb_stat = 0; + sp->cnt = 0; + sp->id = seq_id; + return sp; +} + +/** + * fc_seq_start_next_locked() - Allocate a new sequence on the same + * exchange as the supplied sequence + * @sp: The sequence/exchange to get a new sequence for + */ +static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp) +{ + struct fc_exch *ep = fc_seq_exch(sp); + + sp = fc_seq_alloc(ep, ep->seq_id++); + FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n", + ep->f_ctl, sp->id); + return sp; +} + +/** + * fc_seq_start_next() - Lock the exchange and get a new sequence + * for a given sequence/exchange pair + * @sp: The sequence/exchange to get a new exchange for + */ +static struct fc_seq *fc_seq_start_next(struct fc_seq *sp) +{ + struct fc_exch *ep = fc_seq_exch(sp); + + spin_lock_bh(&ep->ex_lock); + sp = fc_seq_start_next_locked(sp); + spin_unlock_bh(&ep->ex_lock); + + return sp; +} + +/** + * fc_seq_exch_abort() - Abort an exchange and sequence + * @req_sp: The sequence to be aborted + * @timer_msec: The period of time to wait before aborting + * + * Generally called because of a timeout or an abort from the upper layer. + */ +static int fc_seq_exch_abort(const struct fc_seq *req_sp, + unsigned int timer_msec) { struct fc_seq *sp; struct fc_exch *ep; @@ -422,11 +602,10 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec) error = -ENOBUFS; return error; } -EXPORT_SYMBOL(fc_seq_exch_abort); -/* - * Exchange timeout - handle exchange timer expiration. - * The timer will have been cancelled before this is called. +/** + * fc_exch_timeout() - Handle exchange timer expiration + * @work: The work_struct identifying the exchange that timed out */ static void fc_exch_timeout(struct work_struct *work) { @@ -474,28 +653,10 @@ done: fc_exch_release(ep); } -/* - * Allocate a sequence. - * - * We don't support multiple originated sequences on the same exchange. - * By implication, any previously originated sequence on this exchange - * is complete, and we reallocate the same sequence. - */ -static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id) -{ - struct fc_seq *sp; - - sp = &ep->seq; - sp->ssb_stat = 0; - sp->cnt = 0; - sp->id = seq_id; - return sp; -} - /** - * fc_exch_em_alloc() - allocate an exchange from a specified EM. - * @lport: ptr to the local port - * @mp: ptr to the exchange manager + * fc_exch_em_alloc() - Allocate an exchange from a specified EM. + * @lport: The local port that the exchange is for + * @mp: The exchange manager that will allocate the exchange * * Returns pointer to allocated fc_exch with exch lock held. */ @@ -563,16 +724,18 @@ err: } /** - * fc_exch_alloc() - allocate an exchange. - * @lport: ptr to the local port - * @fp: ptr to the FC frame + * fc_exch_alloc() - Allocate an exchange from an EM on a + * local port's list of EMs. + * @lport: The local port that will own the exchange + * @fp: The FC frame that the exchange will be for * - * This function walks the list of the exchange manager(EM) - * anchors to select a EM for new exchange allocation. The - * EM is selected having either a NULL match function pointer - * or call to match function returning true. + * This function walks the list of exchange manager(EM) + * anchors to select an EM for a new exchange allocation. The + * EM is selected when a NULL match function pointer is encountered + * or when a call to a match function returns true. */ -struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp) +static struct fc_exch *fc_exch_alloc(struct fc_lport *lport, + struct fc_frame *fp) { struct fc_exch_mgr_anchor *ema; struct fc_exch *ep; @@ -586,10 +749,11 @@ struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp) } return NULL; } -EXPORT_SYMBOL(fc_exch_alloc); -/* - * Lookup and hold an exchange. +/** + * fc_exch_find() - Lookup and hold an exchange + * @mp: The exchange manager to lookup the exchange from + * @xid: The XID of the exchange to look up */ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid) { @@ -609,7 +773,13 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid) return ep; } -void fc_exch_done(struct fc_seq *sp) + +/** + * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and + * the memory allocated for the related objects may be freed. + * @sp: The sequence that has completed + */ +static void fc_exch_done(struct fc_seq *sp) { struct fc_exch *ep = fc_seq_exch(sp); int rc; @@ -620,10 +790,13 @@ void fc_exch_done(struct fc_seq *sp) if (!rc) fc_exch_delete(ep); } -EXPORT_SYMBOL(fc_exch_done); -/* - * Allocate a new exchange as responder. +/** + * fc_exch_resp() - Allocate a new exchange for a response frame + * @lport: The local port that the exchange was for + * @mp: The exchange manager to allocate the exchange from + * @fp: The response frame + * * Sets the responder ID in the frame header. */ static struct fc_exch *fc_exch_resp(struct fc_lport *lport, @@ -664,8 +837,13 @@ static struct fc_exch *fc_exch_resp(struct fc_lport *lport, return ep; } -/* - * Find a sequence for receive where the other end is originating the sequence. +/** + * fc_seq_lookup_recip() - Find a sequence where the other end + * originated the sequence + * @lport: The local port that the frame was sent to + * @mp: The Exchange Manager to lookup the exchange from + * @fp: The frame associated with the sequence we're looking for + * * If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold * on the ep that should be released by the caller. */ @@ -771,10 +949,12 @@ rel: return reject; } -/* - * Find the sequence for a frame being received. - * We originated the sequence, so it should be found. - * We may or may not have originated the exchange. +/** + * fc_seq_lookup_orig() - Find a sequence where this end + * originated the sequence + * @mp: The Exchange Manager to lookup the exchange from + * @fp: The frame associated with the sequence we're looking for + * * Does not hold the sequence for the caller. */ static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp, @@ -806,8 +986,12 @@ static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp, return sp; } -/* - * Set addresses for an exchange. +/** + * fc_exch_set_addr() - Set the source and destination IDs for an exchange + * @ep: The exchange to set the addresses for + * @orig_id: The originator's ID + * @resp_id: The responder's ID + * * Note this must be done before the first sequence of the exchange is sent. */ static void fc_exch_set_addr(struct fc_exch *ep, @@ -823,76 +1007,15 @@ static void fc_exch_set_addr(struct fc_exch *ep, } } -static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp) -{ - struct fc_exch *ep = fc_seq_exch(sp); - - sp = fc_seq_alloc(ep, ep->seq_id++); - FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n", - ep->f_ctl, sp->id); - return sp; -} -/* - * Allocate a new sequence on the same exchange as the supplied sequence. - * This will never return NULL. +/** + * fc_seq_els_rsp_send() - Send an ELS response using infomation from + * the existing sequence/exchange. + * @sp: The sequence/exchange to get information from + * @els_cmd: The ELS command to be sent + * @els_data: The ELS data to be sent */ -struct fc_seq *fc_seq_start_next(struct fc_seq *sp) -{ - struct fc_exch *ep = fc_seq_exch(sp); - - spin_lock_bh(&ep->ex_lock); - sp = fc_seq_start_next_locked(sp); - spin_unlock_bh(&ep->ex_lock); - - return sp; -} -EXPORT_SYMBOL(fc_seq_start_next); - -int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp) -{ - struct fc_exch *ep; - struct fc_frame_header *fh = fc_frame_header_get(fp); - int error; - u32 f_ctl; - - ep = fc_seq_exch(sp); - WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT); - - f_ctl = ntoh24(fh->fh_f_ctl); - fc_exch_setup_hdr(ep, fp, f_ctl); - - /* - * update sequence count if this frame is carrying - * multiple FC frames when sequence offload is enabled - * by LLD. - */ - if (fr_max_payload(fp)) - sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)), - fr_max_payload(fp)); - else - sp->cnt++; - - /* - * Send the frame. - */ - error = lp->tt.frame_send(lp, fp); - - /* - * Update the exchange and sequence flags, - * assuming all frames for the sequence have been sent. - * We can only be called to send once for each sequence. - */ - spin_lock_bh(&ep->ex_lock); - ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */ - if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT)) - ep->esb_stat &= ~ESB_ST_SEQ_INIT; - spin_unlock_bh(&ep->ex_lock); - return error; -} -EXPORT_SYMBOL(fc_seq_send); - -void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd, - struct fc_seq_els_data *els_data) +static void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd, + struct fc_seq_els_data *els_data) { switch (els_cmd) { case ELS_LS_RJT: @@ -911,10 +1034,13 @@ void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd, FC_EXCH_DBG(fc_seq_exch(sp), "Invalid ELS CMD:%x\n", els_cmd); } } -EXPORT_SYMBOL(fc_seq_els_rsp_send); -/* - * Send a sequence, which is also the last sequence in the exchange. +/** + * fc_seq_send_last() - Send a sequence that is the last in the exchange + * @sp: The sequence that is to be sent + * @fp: The frame that will be sent on the sequence + * @rctl: The R_CTL information to be sent + * @fh_type: The frame header type */ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp, enum fc_rctl rctl, enum fc_fh_type fh_type) @@ -928,9 +1054,12 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp, fc_seq_send(ep->lp, sp, fp); } -/* +/** + * fc_seq_send_ack() - Send an acknowledgement that we've received a frame + * @sp: The sequence to send the ACK on + * @rx_fp: The received frame that is being acknoledged + * * Send ACK_1 (or equiv.) indicating we received something. - * The frame we're acking is supplied. */ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp) { @@ -938,14 +1067,14 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp) struct fc_frame_header *rx_fh; struct fc_frame_header *fh; struct fc_exch *ep = fc_seq_exch(sp); - struct fc_lport *lp = ep->lp; + struct fc_lport *lport = ep->lp; unsigned int f_ctl; /* * Don't send ACKs for class 3. */ if (fc_sof_needs_ack(fr_sof(rx_fp))) { - fp = fc_frame_alloc(lp, 0); + fp = fc_frame_alloc(lport, 0); if (!fp) return; @@ -980,12 +1109,16 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp) else fr_eof(fp) = FC_EOF_N; - (void) lp->tt.frame_send(lp, fp); + lport->tt.frame_send(lport, fp); } } -/* - * Send BLS Reject. +/** + * fc_exch_send_ba_rjt() - Send BLS Reject + * @rx_fp: The frame being rejected + * @reason: The reason the frame is being rejected + * @explan: The explaination for the rejection + * * This is for rejecting BA_ABTS only. */ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp, @@ -996,11 +1129,11 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp, struct fc_frame_header *rx_fh; struct fc_frame_header *fh; struct fc_ba_rjt *rp; - struct fc_lport *lp; + struct fc_lport *lport; unsigned int f_ctl; - lp = fr_dev(rx_fp); - fp = fc_frame_alloc(lp, sizeof(*rp)); + lport = fr_dev(rx_fp); + fp = fc_frame_alloc(lport, sizeof(*rp)); if (!fp) return; fh = fc_frame_header_get(fp); @@ -1045,13 +1178,17 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp, if (fc_sof_needs_ack(fr_sof(fp))) fr_eof(fp) = FC_EOF_N; - (void) lp->tt.frame_send(lp, fp); + lport->tt.frame_send(lport, fp); } -/* - * Handle an incoming ABTS. This would be for target mode usually, - * but could be due to lost FCP transfer ready, confirm or RRQ. - * We always handle this as an exchange abort, ignoring the parameter. +/** + * fc_exch_recv_abts() - Handle an incoming ABTS + * @ep: The exchange the abort was on + * @rx_fp: The ABTS frame + * + * This would be for target mode usually, but could be due to lost + * FCP transfer ready, confirm or RRQ. We always handle this as an + * exchange abort, ignoring the parameter. */ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp) { @@ -1100,10 +1237,14 @@ free: fc_frame_free(rx_fp); } -/* - * Handle receive where the other end is originating the sequence. +/** + * fc_exch_recv_req() - Handler for an incoming request where is other + * end is originating the sequence + * @lport: The local port that received the request + * @mp: The EM that the exchange is on + * @fp: The request frame */ -static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp, +static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp, struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); @@ -1114,8 +1255,17 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp, u32 f_ctl; enum fc_pf_rjt_reason reject; + /* We can have the wrong fc_lport at this point with NPIV, which is a + * problem now that we know a new exchange needs to be allocated + */ + lport = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id)); + if (!lport) { + fc_frame_free(fp); + return; + } + fr_seq(fp) = NULL; - reject = fc_seq_lookup_recip(lp, mp, fp); + reject = fc_seq_lookup_recip(lport, mp, fp); if (reject == FC_RJT_NONE) { sp = fr_seq(fp); /* sequence will be held */ ep = fc_seq_exch(sp); @@ -1138,17 +1288,21 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp, if (ep->resp) ep->resp(sp, fp, ep->arg); else - lp->tt.lport_recv(lp, sp, fp); + lport->tt.lport_recv(lport, sp, fp); fc_exch_release(ep); /* release from lookup */ } else { - FC_LPORT_DBG(lp, "exch/seq lookup failed: reject %x\n", reject); + FC_LPORT_DBG(lport, "exch/seq lookup failed: reject %x\n", + reject); fc_frame_free(fp); } } -/* - * Handle receive where the other end is originating the sequence in - * response to our exchange. +/** + * fc_exch_recv_seq_resp() - Handler for an incoming response where the other + * end is the originator of the sequence that is a + * response to our initial exchange + * @mp: The EM that the exchange is on + * @fp: The response frame */ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) { @@ -1239,8 +1393,11 @@ out: fc_frame_free(fp); } -/* - * Handle receive for a sequence where other end is responding to our sequence. +/** + * fc_exch_recv_resp() - Handler for a sequence where other end is + * responding to our sequence + * @mp: The EM that the exchange is on + * @fp: The response frame */ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) { @@ -1256,9 +1413,13 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) fc_frame_free(fp); } -/* - * Handle the response to an ABTS for exchange or sequence. - * This can be BA_ACC or BA_RJT. +/** + * fc_exch_abts_resp() - Handler for a response to an ABT + * @ep: The exchange that the frame is on + * @fp: The response frame + * + * This response would be to an ABTS cancelling an exchange or sequence. + * The response can be either BA_ACC or BA_RJT */ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) { @@ -1333,9 +1494,12 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) } -/* - * Receive BLS sequence. - * This is always a sequence initiated by the remote side. +/** + * fc_exch_recv_bls() - Handler for a BLS sequence + * @mp: The EM that the exchange is on + * @fp: The request frame + * + * The BLS frame is always a sequence initiated by the remote side. * We may be either the originator or recipient of the exchange. */ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp) @@ -1392,8 +1556,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp) fc_exch_release(ep); /* release hold taken by fc_exch_find */ } -/* - * Accept sequence with LS_ACC. +/** + * fc_seq_ls_acc() - Accept sequence with LS_ACC + * @req_sp: The request sequence + * * If this fails due to allocation or transmit congestion, assume the * originator will repeat the sequence. */ @@ -1413,8 +1579,12 @@ static void fc_seq_ls_acc(struct fc_seq *req_sp) } } -/* - * Reject sequence with ELS LS_RJT. +/** + * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT + * @req_sp: The request sequence + * @reason: The reason the sequence is being rejected + * @explan: The explaination for the rejection + * * If this fails due to allocation or transmit congestion, assume the * originator will repeat the sequence. */ @@ -1437,6 +1607,10 @@ static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason, } } +/** + * fc_exch_reset() - Reset an exchange + * @ep: The exchange to be reset + */ static void fc_exch_reset(struct fc_exch *ep) { struct fc_seq *sp; @@ -1446,12 +1620,6 @@ static void fc_exch_reset(struct fc_exch *ep) spin_lock_bh(&ep->ex_lock); ep->state |= FC_EX_RST_CLEANUP; - /* - * we really want to call del_timer_sync, but cannot due - * to the lport calling with the lport lock held (some resp - * functions can also grab the lport lock which could cause - * a deadlock). - */ if (cancel_delayed_work(&ep->timeout_work)) atomic_dec(&ep->ex_refcnt); /* drop hold for timer */ resp = ep->resp; @@ -1471,16 +1639,16 @@ static void fc_exch_reset(struct fc_exch *ep) } /** - * fc_exch_pool_reset() - Resets an per cpu exches pool. - * @lport: ptr to the local port - * @pool: ptr to the per cpu exches pool - * @sid: source FC ID - * @did: destination FC ID + * fc_exch_pool_reset() - Reset a per cpu exchange pool + * @lport: The local port that the exchange pool is on + * @pool: The exchange pool to be reset + * @sid: The source ID + * @did: The destination ID * - * Resets an per cpu exches pool, releasing its all sequences - * and exchanges. If sid is non-zero, then reset only exchanges - * we sourced from that FID. If did is non-zero, reset only - * exchanges destined to that FID. + * Resets a per cpu exches pool, releasing all of its sequences + * and exchanges. If sid is non-zero then reset only exchanges + * we sourced from the local port's FID. If did is non-zero then + * only reset exchanges destined for the local port's FID. */ static void fc_exch_pool_reset(struct fc_lport *lport, struct fc_exch_pool *pool, @@ -1514,15 +1682,15 @@ restart: } /** - * fc_exch_mgr_reset() - Resets all EMs of a lport - * @lport: ptr to the local port - * @sid: source FC ID - * @did: destination FC ID + * fc_exch_mgr_reset() - Reset all EMs of a local port + * @lport: The local port whose EMs are to be reset + * @sid: The source ID + * @did: The destination ID * - * Reset all EMs of a lport, releasing its all sequences and - * exchanges. If sid is non-zero, then reset only exchanges - * we sourced from that FID. If did is non-zero, reset only - * exchanges destined to that FID. + * Reset all EMs associated with a given local port. Release all + * sequences and exchanges. If sid is non-zero then reset only the + * exchanges sent from the local port's FID. If did is non-zero then + * reset only exchanges destined for the local port's FID. */ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did) { @@ -1538,8 +1706,11 @@ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did) } EXPORT_SYMBOL(fc_exch_mgr_reset); -/* - * Handle incoming ELS REC - Read Exchange Concise. +/** + * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests + * @sp: The sequence the REC is on + * @rfp: The REC frame + * * Note that the requesting port may be different than the S_ID in the request. */ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp) @@ -1621,10 +1792,11 @@ reject: fc_frame_free(rfp); } -/* - * Handle response from RRQ. - * Not much to do here, really. - * Should report errors. +/** + * fc_exch_rrq_resp() - Handler for RRQ responses + * @sp: The sequence that the RRQ is on + * @fp: The RRQ frame + * @arg: The exchange that the RRQ is on * * TODO: fix error handler. */ @@ -1664,21 +1836,99 @@ cleanup: fc_exch_release(aborted_ep); } -/* - * Send ELS RRQ - Reinstate Recovery Qualifier. + +/** + * fc_exch_seq_send() - Send a frame using a new exchange and sequence + * @lport: The local port to send the frame on + * @fp: The frame to be sent + * @resp: The response handler for this request + * @destructor: The destructor for the exchange + * @arg: The argument to be passed to the response handler + * @timer_msec: The timeout period for the exchange + * + * The frame pointer with some of the header's fields must be + * filled before calling this routine, those fields are: + * + * - routing control + * - FC port did + * - FC port sid + * - FC header type + * - frame control + * - parameter or relative offset + */ +static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport, + struct fc_frame *fp, + void (*resp)(struct fc_seq *, + struct fc_frame *fp, + void *arg), + void (*destructor)(struct fc_seq *, + void *), + void *arg, u32 timer_msec) +{ + struct fc_exch *ep; + struct fc_seq *sp = NULL; + struct fc_frame_header *fh; + int rc = 1; + + ep = fc_exch_alloc(lport, fp); + if (!ep) { + fc_frame_free(fp); + return NULL; + } + ep->esb_stat |= ESB_ST_SEQ_INIT; + fh = fc_frame_header_get(fp); + fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id)); + ep->resp = resp; + ep->destructor = destructor; + ep->arg = arg; + ep->r_a_tov = FC_DEF_R_A_TOV; + ep->lp = lport; + sp = &ep->seq; + + ep->fh_type = fh->fh_type; /* save for possbile timeout handling */ + ep->f_ctl = ntoh24(fh->fh_f_ctl); + fc_exch_setup_hdr(ep, fp, ep->f_ctl); + sp->cnt++; + + if (ep->xid <= lport->lro_xid) + fc_fcp_ddp_setup(fr_fsp(fp), ep->xid); + + if (unlikely(lport->tt.frame_send(lport, fp))) + goto err; + + if (timer_msec) + fc_exch_timer_set_locked(ep, timer_msec); + ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not first seq */ + + if (ep->f_ctl & FC_FC_SEQ_INIT) + ep->esb_stat &= ~ESB_ST_SEQ_INIT; + spin_unlock_bh(&ep->ex_lock); + return sp; +err: + rc = fc_exch_done_locked(ep); + spin_unlock_bh(&ep->ex_lock); + if (!rc) + fc_exch_delete(ep); + return NULL; +} + +/** + * fc_exch_rrq() - Send an ELS RRQ (Reinstate Recovery Qualifier) command + * @ep: The exchange to send the RRQ on + * * This tells the remote port to stop blocking the use of * the exchange and the seq_cnt range. */ static void fc_exch_rrq(struct fc_exch *ep) { - struct fc_lport *lp; + struct fc_lport *lport; struct fc_els_rrq *rrq; struct fc_frame *fp; u32 did; - lp = ep->lp; + lport = ep->lp; - fp = fc_frame_alloc(lp, sizeof(*rrq)); + fp = fc_frame_alloc(lport, sizeof(*rrq)); if (!fp) goto retry; @@ -1694,10 +1944,11 @@ static void fc_exch_rrq(struct fc_exch *ep) did = ep->sid; fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did, - fc_host_port_id(lp->host), FC_TYPE_ELS, + fc_host_port_id(lport->host), FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - if (fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep, lp->e_d_tov)) + if (fc_exch_seq_send(lport, fp, fc_exch_rrq_resp, NULL, ep, + lport->e_d_tov)) return; retry: @@ -1714,12 +1965,14 @@ retry: } -/* - * Handle incoming ELS RRQ - Reset Recovery Qualifier. +/** + * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests + * @sp: The sequence that the RRQ is on + * @fp: The RRQ frame */ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp) { - struct fc_exch *ep; /* request or subject exchange */ + struct fc_exch *ep = NULL; /* request or subject exchange */ struct fc_els_rrq *rp; u32 sid; u16 xid; @@ -1769,17 +2022,24 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp) * Send LS_ACC. */ fc_seq_ls_acc(sp); - fc_frame_free(fp); - return; + goto out; unlock_reject: spin_unlock_bh(&ep->ex_lock); - fc_exch_release(ep); /* drop hold from fc_exch_find */ reject: fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan); +out: fc_frame_free(fp); + if (ep) + fc_exch_release(ep); /* drop hold from fc_exch_find */ } +/** + * fc_exch_mgr_add() - Add an exchange manager to a local port's list of EMs + * @lport: The local port to add the exchange manager to + * @mp: The exchange manager to be added to the local port + * @match: The match routine that indicates when this EM should be used + */ struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport, struct fc_exch_mgr *mp, bool (*match)(struct fc_frame *)) @@ -1799,6 +2059,10 @@ struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport, } EXPORT_SYMBOL(fc_exch_mgr_add); +/** + * fc_exch_mgr_destroy() - Destroy an exchange manager + * @kref: The reference to the EM to be destroyed + */ static void fc_exch_mgr_destroy(struct kref *kref) { struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref); @@ -1808,6 +2072,10 @@ static void fc_exch_mgr_destroy(struct kref *kref) kfree(mp); } +/** + * fc_exch_mgr_del() - Delete an EM from a local port's list + * @ema: The exchange manager anchor identifying the EM to be deleted + */ void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema) { /* remove EM anchor from EM anchors list */ @@ -1817,7 +2085,35 @@ void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema) } EXPORT_SYMBOL(fc_exch_mgr_del); -struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, +/** + * fc_exch_mgr_list_clone() - Share all exchange manager objects + * @src: Source lport to clone exchange managers from + * @dst: New lport that takes references to all the exchange managers + */ +int fc_exch_mgr_list_clone(struct fc_lport *src, struct fc_lport *dst) +{ + struct fc_exch_mgr_anchor *ema, *tmp; + + list_for_each_entry(ema, &src->ema_list, ema_list) { + if (!fc_exch_mgr_add(dst, ema->mp, ema->match)) + goto err; + } + return 0; +err: + list_for_each_entry_safe(ema, tmp, &dst->ema_list, ema_list) + fc_exch_mgr_del(ema); + return -ENOMEM; +} + +/** + * fc_exch_mgr_alloc() - Allocate an exchange manager + * @lport: The local port that the new EM will be associated with + * @class: The default FC class for new exchanges + * @min_xid: The minimum XID for exchanges from the new EM + * @max_xid: The maximum XID for exchanges from the new EM + * @match: The match routine for the new EM + */ +struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport, enum fc_class class, u16 min_xid, u16 max_xid, bool (*match)(struct fc_frame *)) @@ -1830,7 +2126,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN || (min_xid & fc_cpu_mask) != 0) { - FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n", + FC_LPORT_DBG(lport, "Invalid min_xid 0x:%x and max_xid 0x:%x\n", min_xid, max_xid); return NULL; } @@ -1873,7 +2169,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, } kref_init(&mp->kref); - if (!fc_exch_mgr_add(lp, mp, match)) { + if (!fc_exch_mgr_add(lport, mp, match)) { free_percpu(mp->pool); goto free_mempool; } @@ -1894,76 +2190,26 @@ free_mp: } EXPORT_SYMBOL(fc_exch_mgr_alloc); +/** + * fc_exch_mgr_free() - Free all exchange managers on a local port + * @lport: The local port whose EMs are to be freed + */ void fc_exch_mgr_free(struct fc_lport *lport) { struct fc_exch_mgr_anchor *ema, *next; + flush_workqueue(fc_exch_workqueue); list_for_each_entry_safe(ema, next, &lport->ema_list, ema_list) fc_exch_mgr_del(ema); } EXPORT_SYMBOL(fc_exch_mgr_free); - -struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, - struct fc_frame *fp, - void (*resp)(struct fc_seq *, - struct fc_frame *fp, - void *arg), - void (*destructor)(struct fc_seq *, void *), - void *arg, u32 timer_msec) -{ - struct fc_exch *ep; - struct fc_seq *sp = NULL; - struct fc_frame_header *fh; - int rc = 1; - - ep = fc_exch_alloc(lp, fp); - if (!ep) { - fc_frame_free(fp); - return NULL; - } - ep->esb_stat |= ESB_ST_SEQ_INIT; - fh = fc_frame_header_get(fp); - fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id)); - ep->resp = resp; - ep->destructor = destructor; - ep->arg = arg; - ep->r_a_tov = FC_DEF_R_A_TOV; - ep->lp = lp; - sp = &ep->seq; - - ep->fh_type = fh->fh_type; /* save for possbile timeout handling */ - ep->f_ctl = ntoh24(fh->fh_f_ctl); - fc_exch_setup_hdr(ep, fp, ep->f_ctl); - sp->cnt++; - - if (ep->xid <= lp->lro_xid) - fc_fcp_ddp_setup(fr_fsp(fp), ep->xid); - - if (unlikely(lp->tt.frame_send(lp, fp))) - goto err; - - if (timer_msec) - fc_exch_timer_set_locked(ep, timer_msec); - ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not first seq */ - - if (ep->f_ctl & FC_FC_SEQ_INIT) - ep->esb_stat &= ~ESB_ST_SEQ_INIT; - spin_unlock_bh(&ep->ex_lock); - return sp; -err: - rc = fc_exch_done_locked(ep); - spin_unlock_bh(&ep->ex_lock); - if (!rc) - fc_exch_delete(ep); - return NULL; -} -EXPORT_SYMBOL(fc_exch_seq_send); - -/* - * Receive a frame +/** + * fc_exch_recv() - Handler for received frames + * @lport: The local port the frame was received on + * @fp: The received frame */ -void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp) +void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); struct fc_exch_mgr_anchor *ema; @@ -1971,8 +2217,8 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp) u16 oxid; /* lport lock ? */ - if (!lp || lp->state == LPORT_ST_DISABLED) { - FC_LPORT_DBG(lp, "Receiving frames for an lport that " + if (!lport || lport->state == LPORT_ST_DISABLED) { + FC_LPORT_DBG(lport, "Receiving frames for an lport that " "has not been initialized correctly\n"); fc_frame_free(fp); return; @@ -1981,7 +2227,7 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp) f_ctl = ntoh24(fh->fh_f_ctl); oxid = ntohs(fh->fh_ox_id); if (f_ctl & FC_FC_EX_CTX) { - list_for_each_entry(ema, &lp->ema_list, ema_list) { + list_for_each_entry(ema, &lport->ema_list, ema_list) { if ((oxid >= ema->mp->min_xid) && (oxid <= ema->mp->max_xid)) { found = 1; @@ -1990,13 +2236,13 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp) } if (!found) { - FC_LPORT_DBG(lp, "Received response for out " + FC_LPORT_DBG(lport, "Received response for out " "of range oxid:%hx\n", oxid); fc_frame_free(fp); return; } } else - ema = list_entry(lp->ema_list.prev, typeof(*ema), ema_list); + ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list); /* * If frame is marked invalid, just drop it. @@ -2015,37 +2261,56 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp) else if (f_ctl & FC_FC_SEQ_CTX) fc_exch_recv_resp(ema->mp, fp); else - fc_exch_recv_req(lp, ema->mp, fp); + fc_exch_recv_req(lport, ema->mp, fp); break; default: - FC_LPORT_DBG(lp, "dropping invalid frame (eof %x)", fr_eof(fp)); + FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)", + fr_eof(fp)); fc_frame_free(fp); } } EXPORT_SYMBOL(fc_exch_recv); -int fc_exch_init(struct fc_lport *lp) +/** + * fc_exch_init() - Initialize the exchange layer for a local port + * @lport: The local port to initialize the exchange layer for + */ +int fc_exch_init(struct fc_lport *lport) { - if (!lp->tt.seq_start_next) - lp->tt.seq_start_next = fc_seq_start_next; + if (!lport->tt.seq_start_next) + lport->tt.seq_start_next = fc_seq_start_next; - if (!lp->tt.exch_seq_send) - lp->tt.exch_seq_send = fc_exch_seq_send; + if (!lport->tt.exch_seq_send) + lport->tt.exch_seq_send = fc_exch_seq_send; - if (!lp->tt.seq_send) - lp->tt.seq_send = fc_seq_send; + if (!lport->tt.seq_send) + lport->tt.seq_send = fc_seq_send; - if (!lp->tt.seq_els_rsp_send) - lp->tt.seq_els_rsp_send = fc_seq_els_rsp_send; + if (!lport->tt.seq_els_rsp_send) + lport->tt.seq_els_rsp_send = fc_seq_els_rsp_send; - if (!lp->tt.exch_done) - lp->tt.exch_done = fc_exch_done; + if (!lport->tt.exch_done) + lport->tt.exch_done = fc_exch_done; - if (!lp->tt.exch_mgr_reset) - lp->tt.exch_mgr_reset = fc_exch_mgr_reset; + if (!lport->tt.exch_mgr_reset) + lport->tt.exch_mgr_reset = fc_exch_mgr_reset; - if (!lp->tt.seq_exch_abort) - lp->tt.seq_exch_abort = fc_seq_exch_abort; + if (!lport->tt.seq_exch_abort) + lport->tt.seq_exch_abort = fc_seq_exch_abort; + + return 0; +} +EXPORT_SYMBOL(fc_exch_init); + +/** + * fc_setup_exch_mgr() - Setup an exchange manager + */ +int fc_setup_exch_mgr() +{ + fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!fc_em_cachep) + return -ENOMEM; /* * Initialize fc_cpu_mask and fc_cpu_order. The @@ -2069,20 +2334,17 @@ int fc_exch_init(struct fc_lport *lp) } fc_cpu_mask--; - return 0; -} -EXPORT_SYMBOL(fc_exch_init); - -int fc_setup_exch_mgr(void) -{ - fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!fc_em_cachep) + fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue"); + if (!fc_exch_workqueue) return -ENOMEM; return 0; } -void fc_destroy_exch_mgr(void) +/** + * fc_destroy_exch_mgr() - Destroy an exchange manager + */ +void fc_destroy_exch_mgr() { + destroy_workqueue(fc_exch_workqueue); kmem_cache_destroy(fc_em_cachep); } diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 59a4408b27b5..c4b58d042f6f 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -39,15 +39,9 @@ #include <scsi/libfc.h> #include <scsi/fc_encode.h> -MODULE_AUTHOR("Open-FCoE.org"); -MODULE_DESCRIPTION("libfc"); -MODULE_LICENSE("GPL v2"); +#include "fc_libfc.h" -unsigned int fc_debug_logging; -module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); - -static struct kmem_cache *scsi_pkt_cachep; +struct kmem_cache *scsi_pkt_cachep; /* SRB state definitions */ #define FC_SRB_FREE 0 /* cmd is free */ @@ -58,7 +52,6 @@ static struct kmem_cache *scsi_pkt_cachep; #define FC_SRB_DISCONTIG (1 << 4) /* non-sequential data recvd */ #define FC_SRB_COMPL (1 << 5) /* fc_io_compl has been run */ #define FC_SRB_FCP_PROCESSING_TMO (1 << 6) /* timer function processing */ -#define FC_SRB_NOMEM (1 << 7) /* dropped to out of mem */ #define FC_SRB_READ (1 << 1) #define FC_SRB_WRITE (1 << 0) @@ -73,10 +66,20 @@ static struct kmem_cache *scsi_pkt_cachep; #define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status) #define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual) +/** + * struct fc_fcp_internal - FCP layer internal data + * @scsi_pkt_pool: Memory pool to draw FCP packets from + * @scsi_pkt_queue: Current FCP packets + * @last_can_queue_ramp_down_time: ramp down time + * @last_can_queue_ramp_up_time: ramp up time + * @max_can_queue: max can_queue size + */ struct fc_fcp_internal { - mempool_t *scsi_pkt_pool; + mempool_t *scsi_pkt_pool; struct list_head scsi_pkt_queue; - u8 throttled; + unsigned long last_can_queue_ramp_down_time; + unsigned long last_can_queue_ramp_up_time; + int max_can_queue; }; #define fc_get_scsi_internal(x) ((struct fc_fcp_internal *)(x)->scsi_priv) @@ -90,9 +93,9 @@ static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *); static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *); static void fc_fcp_complete_locked(struct fc_fcp_pkt *); static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *); -static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp); +static void fc_fcp_error(struct fc_fcp_pkt *, struct fc_frame *); static void fc_timeout_error(struct fc_fcp_pkt *); -static void fc_fcp_timeout(unsigned long data); +static void fc_fcp_timeout(unsigned long); static void fc_fcp_rec(struct fc_fcp_pkt *); static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *); static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *); @@ -124,6 +127,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *); #define FC_SCSI_TM_TOV (10 * HZ) #define FC_SCSI_REC_TOV (2 * HZ) #define FC_HOST_RESET_TIMEOUT (30 * HZ) +#define FC_CAN_QUEUE_PERIOD (60 * HZ) #define FC_MAX_ERROR_CNT 5 #define FC_MAX_RECOV_RETRY 3 @@ -131,23 +135,22 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *); #define FC_FCP_DFLT_QUEUE_DEPTH 32 /** - * fc_fcp_pkt_alloc - allocation routine for scsi_pkt packet - * @lp: fc lport struct - * @gfp: gfp flags for allocation + * fc_fcp_pkt_alloc() - Allocate a fcp_pkt + * @lport: The local port that the FCP packet is for + * @gfp: GFP flags for allocation * - * This is used by upper layer scsi driver. - * Return Value : scsi_pkt structure or null on allocation failure. - * Context : call from process context. no locking required. + * Return value: fcp_pkt structure or null on allocation failure. + * Context: Can be called from process context, no lock is required. */ -static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp) +static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp) { - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); + struct fc_fcp_internal *si = fc_get_scsi_internal(lport); struct fc_fcp_pkt *fsp; fsp = mempool_alloc(si->scsi_pkt_pool, gfp); if (fsp) { memset(fsp, 0, sizeof(*fsp)); - fsp->lp = lp; + fsp->lp = lport; atomic_set(&fsp->ref_cnt, 1); init_timer(&fsp->timer); INIT_LIST_HEAD(&fsp->list); @@ -157,12 +160,11 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp) } /** - * fc_fcp_pkt_release() - release hold on scsi_pkt packet - * @fsp: fcp packet struct + * fc_fcp_pkt_release() - Release hold on a fcp_pkt + * @fsp: The FCP packet to be released * - * This is used by upper layer scsi driver. - * Context : call from process and interrupt context. - * no locking required + * Context: Can be called from process or interrupt context, + * no lock is required. */ static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp) { @@ -173,20 +175,25 @@ static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp) } } +/** + * fc_fcp_pkt_hold() - Hold a fcp_pkt + * @fsp: The FCP packet to be held + */ static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp) { atomic_inc(&fsp->ref_cnt); } /** - * fc_fcp_pkt_destory() - release hold on scsi_pkt packet - * @seq: exchange sequence - * @fsp: fcp packet struct + * fc_fcp_pkt_destory() - Release hold on a fcp_pkt + * @seq: The sequence that the FCP packet is on (required by destructor API) + * @fsp: The FCP packet to be released + * + * This routine is called by a destructor callback in the exch_seq_send() + * routine of the libfc Transport Template. The 'struct fc_seq' is a required + * argument even though it is not used by this routine. * - * Release hold on scsi_pkt packet set to keep scsi_pkt - * till EM layer exch resource is not freed. - * Context : called from from EM layer. - * no locking required + * Context: No locking required. */ static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp) { @@ -194,10 +201,10 @@ static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp) } /** - * fc_fcp_lock_pkt() - lock a packet and get a ref to it. - * @fsp: fcp packet + * fc_fcp_lock_pkt() - Lock a fcp_pkt and increase its reference count + * @fsp: The FCP packet to be locked and incremented * - * We should only return error if we return a command to scsi-ml before + * We should only return error if we return a command to SCSI-ml before * getting a response. This can happen in cases where we send a abort, but * do not wait for the response and the abort and command can be passing * each other on the wire/network-layer. @@ -222,18 +229,33 @@ static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp) return 0; } +/** + * fc_fcp_unlock_pkt() - Release a fcp_pkt's lock and decrement its + * reference count + * @fsp: The FCP packet to be unlocked and decremented + */ static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp) { spin_unlock_bh(&fsp->scsi_pkt_lock); fc_fcp_pkt_release(fsp); } +/** + * fc_fcp_timer_set() - Start a timer for a fcp_pkt + * @fsp: The FCP packet to start a timer for + * @delay: The timeout period for the timer + */ static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay) { if (!(fsp->state & FC_SRB_COMPL)) mod_timer(&fsp->timer, jiffies + delay); } +/** + * fc_fcp_send_abort() - Send an abort for exchanges associated with a + * fcp_pkt + * @fsp: The FCP packet to abort exchanges on + */ static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp) { if (!fsp->seq_ptr) @@ -243,9 +265,14 @@ static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp) return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0); } -/* - * Retry command. - * An abort isn't needed. +/** + * fc_fcp_retry_cmd() - Retry a fcp_pkt + * @fsp: The FCP packet to be retried + * + * Sets the status code to be FC_ERROR and then calls + * fc_fcp_complete_locked() which in turn calls fc_io_compl(). + * fc_io_compl() will notify the SCSI-ml that the I/O is done. + * The SCSI-ml will retry the command. */ static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp) { @@ -260,64 +287,146 @@ static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp) fc_fcp_complete_locked(fsp); } -/* - * fc_fcp_ddp_setup - calls to LLD's ddp_setup to set up DDP - * transfer for a read I/O indicated by the fc_fcp_pkt. - * @fsp: ptr to the fc_fcp_pkt - * - * This is called in exch_seq_send() when we have a newly allocated - * exchange with a valid exchange id to setup ddp. - * - * returns: none +/** + * fc_fcp_ddp_setup() - Calls a LLD's ddp_setup routine to set up DDP context + * @fsp: The FCP packet that will manage the DDP frames + * @xid: The XID that will be used for the DDP exchange */ void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid) { - struct fc_lport *lp; + struct fc_lport *lport; if (!fsp) return; - lp = fsp->lp; + lport = fsp->lp; if ((fsp->req_flags & FC_SRB_READ) && - (lp->lro_enabled) && (lp->tt.ddp_setup)) { - if (lp->tt.ddp_setup(lp, xid, scsi_sglist(fsp->cmd), - scsi_sg_count(fsp->cmd))) + (lport->lro_enabled) && (lport->tt.ddp_setup)) { + if (lport->tt.ddp_setup(lport, xid, scsi_sglist(fsp->cmd), + scsi_sg_count(fsp->cmd))) fsp->xfer_ddp = xid; } } -EXPORT_SYMBOL(fc_fcp_ddp_setup); -/* - * fc_fcp_ddp_done - calls to LLD's ddp_done to release any - * DDP related resources for this I/O if it is initialized - * as a ddp transfer - * @fsp: ptr to the fc_fcp_pkt - * - * returns: none +/** + * fc_fcp_ddp_done() - Calls a LLD's ddp_done routine to release any + * DDP related resources for a fcp_pkt + * @fsp: The FCP packet that DDP had been used on */ static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp) { - struct fc_lport *lp; + struct fc_lport *lport; if (!fsp) return; - lp = fsp->lp; - if (fsp->xfer_ddp && lp->tt.ddp_done) { - fsp->xfer_len = lp->tt.ddp_done(lp, fsp->xfer_ddp); - fsp->xfer_ddp = 0; + if (fsp->xfer_ddp == FC_XID_UNKNOWN) + return; + + lport = fsp->lp; + if (lport->tt.ddp_done) { + fsp->xfer_len = lport->tt.ddp_done(lport, fsp->xfer_ddp); + fsp->xfer_ddp = FC_XID_UNKNOWN; } } +/** + * fc_fcp_can_queue_ramp_up() - increases can_queue + * @lport: lport to ramp up can_queue + * + * Locking notes: Called with Scsi_Host lock held + */ +static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport) +{ + struct fc_fcp_internal *si = fc_get_scsi_internal(lport); + int can_queue; + + if (si->last_can_queue_ramp_up_time && + (time_before(jiffies, si->last_can_queue_ramp_up_time + + FC_CAN_QUEUE_PERIOD))) + return; + + if (time_before(jiffies, si->last_can_queue_ramp_down_time + + FC_CAN_QUEUE_PERIOD)) + return; + + si->last_can_queue_ramp_up_time = jiffies; + + can_queue = lport->host->can_queue << 1; + if (can_queue >= si->max_can_queue) { + can_queue = si->max_can_queue; + si->last_can_queue_ramp_down_time = 0; + } + lport->host->can_queue = can_queue; + shost_printk(KERN_ERR, lport->host, "libfc: increased " + "can_queue to %d.\n", can_queue); +} + +/** + * fc_fcp_can_queue_ramp_down() - reduces can_queue + * @lport: lport to reduce can_queue + * + * If we are getting memory allocation failures, then we may + * be trying to execute too many commands. We let the running + * commands complete or timeout, then try again with a reduced + * can_queue. Eventually we will hit the point where we run + * on all reserved structs. + * + * Locking notes: Called with Scsi_Host lock held + */ +static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport) +{ + struct fc_fcp_internal *si = fc_get_scsi_internal(lport); + int can_queue; + + if (si->last_can_queue_ramp_down_time && + (time_before(jiffies, si->last_can_queue_ramp_down_time + + FC_CAN_QUEUE_PERIOD))) + return; + + si->last_can_queue_ramp_down_time = jiffies; + + can_queue = lport->host->can_queue; + can_queue >>= 1; + if (!can_queue) + can_queue = 1; + lport->host->can_queue = can_queue; + shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame.\n" + "Reducing can_queue to %d.\n", can_queue); +} /* - * Receive SCSI data from target. - * Called after receiving solicited data. + * fc_fcp_frame_alloc() - Allocates fc_frame structure and buffer. + * @lport: fc lport struct + * @len: payload length + * + * Allocates fc_frame structure and buffer but if fails to allocate + * then reduce can_queue. + */ +static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport, + size_t len) +{ + struct fc_frame *fp; + unsigned long flags; + + fp = fc_frame_alloc(lport, len); + if (!fp) { + spin_lock_irqsave(lport->host->host_lock, flags); + fc_fcp_can_queue_ramp_down(lport); + spin_unlock_irqrestore(lport->host->host_lock, flags); + } + return fp; +} + +/** + * fc_fcp_recv_data() - Handler for receiving SCSI-FCP data from a target + * @fsp: The FCP packet the data is on + * @fp: The data frame */ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { struct scsi_cmnd *sc = fsp->cmd; - struct fc_lport *lp = fsp->lp; + struct fc_lport *lport = fsp->lp; struct fcoe_dev_stats *stats; struct fc_frame_header *fh; size_t start_offset; @@ -327,7 +436,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) size_t len; void *buf; struct scatterlist *sg; - size_t remaining; + u32 nents; fh = fc_frame_header_get(fp); offset = ntohl(fh->fh_parm_offset); @@ -351,65 +460,29 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) if (offset != fsp->xfer_len) fsp->state |= FC_SRB_DISCONTIG; - crc = 0; - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) - crc = crc32(~0, (u8 *) fh, sizeof(*fh)); - sg = scsi_sglist(sc); - remaining = len; - - while (remaining > 0 && sg) { - size_t off; - void *page_addr; - size_t sg_bytes; + nents = scsi_sg_count(sc); - if (offset >= sg->length) { - offset -= sg->length; - sg = sg_next(sg); - continue; - } - sg_bytes = min(remaining, sg->length - offset); - - /* - * The scatterlist item may be bigger than PAGE_SIZE, - * but we are limited to mapping PAGE_SIZE at a time. - */ - off = offset + sg->offset; - sg_bytes = min(sg_bytes, (size_t) - (PAGE_SIZE - (off & ~PAGE_MASK))); - page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT), - KM_SOFTIRQ0); - if (!page_addr) - break; /* XXX panic? */ - - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) - crc = crc32(crc, buf, sg_bytes); - memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, - sg_bytes); - - kunmap_atomic(page_addr, KM_SOFTIRQ0); - buf += sg_bytes; - offset += sg_bytes; - remaining -= sg_bytes; - copy_len += sg_bytes; - } - - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { + if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED)) { + copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents, + &offset, KM_SOFTIRQ0, NULL); + } else { + crc = crc32(~0, (u8 *) fh, sizeof(*fh)); + copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents, + &offset, KM_SOFTIRQ0, &crc); buf = fc_frame_payload_get(fp, 0); - if (len % 4) { + if (len % 4) crc = crc32(crc, buf + len, 4 - (len % 4)); - len += 4 - (len % 4); - } if (~crc != le32_to_cpu(fr_crc(fp))) { crc_err: - stats = fc_lport_get_stats(lp); + stats = fc_lport_get_stats(lport); stats->ErrorFrames++; /* FIXME - per cpu count, not total count! */ if (stats->InvalidCRCCount++ < 5) printk(KERN_WARNING "libfc: CRC error on data " "frame for port (%6x)\n", - fc_host_port_id(lp->host)); + fc_host_port_id(lport->host)); /* * Assume the frame is total garbage. * We may have copied it over the good part @@ -437,18 +510,17 @@ crc_err: } /** - * fc_fcp_send_data() - Send SCSI data to target. - * @fsp: ptr to fc_fcp_pkt - * @sp: ptr to this sequence - * @offset: starting offset for this data request - * @seq_blen: the burst length for this data request + * fc_fcp_send_data() - Send SCSI data to a target + * @fsp: The FCP packet the data is on + * @sp: The sequence the data is to be sent on + * @offset: The starting offset for this data request + * @seq_blen: The burst length for this data request * * Called after receiving a Transfer Ready data descriptor. - * if LLD is capable of seq offload then send down seq_blen - * size of data in single frame, otherwise send multiple FC - * frames of max FC frame payload supported by target port. - * - * Returns : 0 for success. + * If the LLD is capable of sequence offload then send down the + * seq_blen ammount of data in single frame, otherwise send + * multiple frames of the maximum frame payload supported by + * the target port. */ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, size_t offset, size_t seq_blen) @@ -457,16 +529,18 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, struct scsi_cmnd *sc; struct scatterlist *sg; struct fc_frame *fp = NULL; - struct fc_lport *lp = fsp->lp; + struct fc_lport *lport = fsp->lp; + struct page *page; size_t remaining; size_t t_blen; size_t tlen; size_t sg_bytes; size_t frame_offset, fh_parm_offset; + size_t off; int error; void *data = NULL; void *page_addr; - int using_sg = lp->sg_supp; + int using_sg = lport->sg_supp; u32 f_ctl; WARN_ON(seq_blen <= 0); @@ -488,10 +562,10 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, * to max FC frame payload previously set in fsp->max_payload. */ t_blen = fsp->max_payload; - if (lp->seq_offload) { - t_blen = min(seq_blen, (size_t)lp->lso_max); + if (lport->seq_offload) { + t_blen = min(seq_blen, (size_t)lport->lso_max); FC_FCP_DBG(fsp, "fsp=%p:lso:blen=%zx lso_max=0x%x t_blen=%zx\n", - fsp, seq_blen, lp->lso_max, t_blen); + fsp, seq_blen, lport->lso_max, t_blen); } WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD); @@ -503,7 +577,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, remaining = seq_blen; fh_parm_offset = frame_offset = offset; tlen = 0; - seq = lp->tt.seq_start_next(seq); + seq = lport->tt.seq_start_next(seq); f_ctl = FC_FC_REL_OFF; WARN_ON(!seq); @@ -525,43 +599,34 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, */ if (tlen % 4) using_sg = 0; - if (using_sg) { - fp = _fc_frame_alloc(lp, 0); - if (!fp) - return -ENOMEM; - } else { - fp = fc_frame_alloc(lp, tlen); - if (!fp) - return -ENOMEM; + fp = fc_frame_alloc(lport, using_sg ? 0 : tlen); + if (!fp) + return -ENOMEM; - data = (void *)(fr_hdr(fp)) + - sizeof(struct fc_frame_header); - } + data = fc_frame_header_get(fp) + 1; fh_parm_offset = frame_offset; fr_max_payload(fp) = fsp->max_payload; } + + off = offset + sg->offset; sg_bytes = min(tlen, sg->length - offset); + sg_bytes = min(sg_bytes, + (size_t) (PAGE_SIZE - (off & ~PAGE_MASK))); + page = sg_page(sg) + (off >> PAGE_SHIFT); if (using_sg) { - get_page(sg_page(sg)); + get_page(page); skb_fill_page_desc(fp_skb(fp), skb_shinfo(fp_skb(fp))->nr_frags, - sg_page(sg), sg->offset + offset, - sg_bytes); + page, off & ~PAGE_MASK, sg_bytes); fp_skb(fp)->data_len += sg_bytes; fr_len(fp) += sg_bytes; fp_skb(fp)->truesize += PAGE_SIZE; } else { - size_t off = offset + sg->offset; - /* * The scatterlist item may be bigger than PAGE_SIZE, * but we must not cross pages inside the kmap. */ - sg_bytes = min(sg_bytes, (size_t) (PAGE_SIZE - - (off & ~PAGE_MASK))); - page_addr = kmap_atomic(sg_page(sg) + - (off >> PAGE_SHIFT), - KM_SOFTIRQ0); + page_addr = kmap_atomic(page, KM_SOFTIRQ0); memcpy(data, (char *)page_addr + (off & ~PAGE_MASK), sg_bytes); kunmap_atomic(page_addr, KM_SOFTIRQ0); @@ -572,7 +637,8 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, tlen -= sg_bytes; remaining -= sg_bytes; - if (tlen) + if ((skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN) && + (tlen)) continue; /* @@ -589,7 +655,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, /* * send fragment using for a sequence. */ - error = lp->tt.seq_send(lp, seq, fp); + error = lport->tt.seq_send(lport, seq, fp); if (error) { WARN_ON(1); /* send error should be rare */ fc_fcp_retry_cmd(fsp); @@ -601,6 +667,11 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, return 0; } +/** + * fc_fcp_abts_resp() - Send an ABTS response + * @fsp: The FCP packet that is being aborted + * @fp: The response frame + */ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { int ba_done = 1; @@ -637,46 +708,13 @@ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) } /** - * fc_fcp_reduce_can_queue() - drop can_queue - * @lp: lport to drop queueing for - * - * If we are getting memory allocation failures, then we may - * be trying to execute too many commands. We let the running - * commands complete or timeout, then try again with a reduced - * can_queue. Eventually we will hit the point where we run - * on all reserved structs. - */ -static void fc_fcp_reduce_can_queue(struct fc_lport *lp) -{ - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); - unsigned long flags; - int can_queue; - - spin_lock_irqsave(lp->host->host_lock, flags); - if (si->throttled) - goto done; - si->throttled = 1; - - can_queue = lp->host->can_queue; - can_queue >>= 1; - if (!can_queue) - can_queue = 1; - lp->host->can_queue = can_queue; - shost_printk(KERN_ERR, lp->host, "libfc: Could not allocate frame.\n" - "Reducing can_queue to %d.\n", can_queue); -done: - spin_unlock_irqrestore(lp->host->host_lock, flags); -} - -/** - * fc_fcp_recv() - Reveive FCP frames + * fc_fcp_recv() - Reveive an FCP frame * @seq: The sequence the frame is on - * @fp: The FC frame + * @fp: The received frame * @arg: The related FCP packet * - * Return : None - * Context : called from Soft IRQ context - * can not called holding list lock + * Context: Called from Soft IRQ context. Can not be called + * holding the FCP packet list lock. */ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg) { @@ -687,8 +725,10 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg) u8 r_ctl; int rc = 0; - if (IS_ERR(fp)) - goto errout; + if (IS_ERR(fp)) { + fc_fcp_error(fsp, fp); + return; + } fh = fc_frame_header_get(fp); r_ctl = fh->fh_r_ctl; @@ -721,8 +761,6 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg) (size_t) ntohl(dd->ft_burst_len)); if (!rc) seq->rec_data = fsp->xfer_len; - else if (rc == -ENOMEM) - fsp->state |= FC_SRB_NOMEM; } else if (r_ctl == FC_RCTL_DD_SOL_DATA) { /* * received a DATA frame @@ -742,13 +780,13 @@ unlock: fc_fcp_unlock_pkt(fsp); out: fc_frame_free(fp); -errout: - if (IS_ERR(fp)) - fc_fcp_error(fsp, fp); - else if (rc == -ENOMEM) - fc_fcp_reduce_can_queue(lport); } +/** + * fc_fcp_resp() - Handler for FCP responses + * @fsp: The FCP packet the response is for + * @fp: The response frame + */ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { struct fc_frame_header *fh; @@ -862,15 +900,16 @@ err: } /** - * fc_fcp_complete_locked() - complete processing of a fcp packet - * @fsp: fcp packet + * fc_fcp_complete_locked() - Complete processing of a fcp_pkt with the + * fcp_pkt lock held + * @fsp: The FCP packet to be completed * * This function may sleep if a timer is pending. The packet lock must be * held, and the host lock must not be held. */ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp) { - struct fc_lport *lp = fsp->lp; + struct fc_lport *lport = fsp->lp; struct fc_seq *seq; struct fc_exch *ep; u32 f_ctl; @@ -901,8 +940,8 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp) struct fc_frame *conf_frame; struct fc_seq *csp; - csp = lp->tt.seq_start_next(seq); - conf_frame = fc_frame_alloc(fsp->lp, 0); + csp = lport->tt.seq_start_next(seq); + conf_frame = fc_fcp_frame_alloc(fsp->lp, 0); if (conf_frame) { f_ctl = FC_FC_SEQ_INIT; f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ; @@ -910,43 +949,48 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp) fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL, ep->did, ep->sid, FC_TYPE_FCP, f_ctl, 0); - lp->tt.seq_send(lp, csp, conf_frame); + lport->tt.seq_send(lport, csp, conf_frame); } } - lp->tt.exch_done(seq); + lport->tt.exch_done(seq); } fc_io_compl(fsp); } +/** + * fc_fcp_cleanup_cmd() - Cancel the active exchange on a fcp_pkt + * @fsp: The FCP packet whose exchanges should be canceled + * @error: The reason for the cancellation + */ static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error) { - struct fc_lport *lp = fsp->lp; + struct fc_lport *lport = fsp->lp; if (fsp->seq_ptr) { - lp->tt.exch_done(fsp->seq_ptr); + lport->tt.exch_done(fsp->seq_ptr); fsp->seq_ptr = NULL; } fsp->status_code = error; } /** - * fc_fcp_cleanup_each_cmd() - Cleanup active commads - * @lp: logical port - * @id: target id - * @lun: lun - * @error: fsp status code + * fc_fcp_cleanup_each_cmd() - Cancel all exchanges on a local port + * @lport: The local port whose exchanges should be canceled + * @id: The target's ID + * @lun: The LUN + * @error: The reason for cancellation * * If lun or id is -1, they are ignored. */ -static void fc_fcp_cleanup_each_cmd(struct fc_lport *lp, unsigned int id, +static void fc_fcp_cleanup_each_cmd(struct fc_lport *lport, unsigned int id, unsigned int lun, int error) { - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); + struct fc_fcp_internal *si = fc_get_scsi_internal(lport); struct fc_fcp_pkt *fsp; struct scsi_cmnd *sc_cmd; unsigned long flags; - spin_lock_irqsave(lp->host->host_lock, flags); + spin_lock_irqsave(lport->host->host_lock, flags); restart: list_for_each_entry(fsp, &si->scsi_pkt_queue, list) { sc_cmd = fsp->cmd; @@ -957,7 +1001,7 @@ restart: continue; fc_fcp_pkt_hold(fsp); - spin_unlock_irqrestore(lp->host->host_lock, flags); + spin_unlock_irqrestore(lport->host->host_lock, flags); if (!fc_fcp_lock_pkt(fsp)) { fc_fcp_cleanup_cmd(fsp, error); @@ -966,35 +1010,36 @@ restart: } fc_fcp_pkt_release(fsp); - spin_lock_irqsave(lp->host->host_lock, flags); + spin_lock_irqsave(lport->host->host_lock, flags); /* * while we dropped the lock multiple pkts could * have been released, so we have to start over. */ goto restart; } - spin_unlock_irqrestore(lp->host->host_lock, flags); + spin_unlock_irqrestore(lport->host->host_lock, flags); } -static void fc_fcp_abort_io(struct fc_lport *lp) +/** + * fc_fcp_abort_io() - Abort all FCP-SCSI exchanges on a local port + * @lport: The local port whose exchanges are to be aborted + */ +static void fc_fcp_abort_io(struct fc_lport *lport) { - fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_HRD_ERROR); + fc_fcp_cleanup_each_cmd(lport, -1, -1, FC_HRD_ERROR); } /** - * fc_fcp_pkt_send() - send a fcp packet to the lower level. - * @lp: fc lport - * @fsp: fc packet. + * fc_fcp_pkt_send() - Send a fcp_pkt + * @lport: The local port to send the FCP packet on + * @fsp: The FCP packet to send * - * This is called by upper layer protocol. - * Return : zero for success and -1 for failure - * Context : called from queuecommand which can be called from process - * or scsi soft irq. - * Locks : called with the host lock and irqs disabled. + * Return: Zero for success and -1 for failure + * Locks: Called with the host lock and irqs disabled. */ -static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp) +static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp) { - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); + struct fc_fcp_internal *si = fc_get_scsi_internal(lport); int rc; fsp->cmd->SCp.ptr = (char *)fsp; @@ -1006,16 +1051,22 @@ static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp) memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len); list_add_tail(&fsp->list, &si->scsi_pkt_queue); - spin_unlock_irq(lp->host->host_lock); - rc = lp->tt.fcp_cmd_send(lp, fsp, fc_fcp_recv); - spin_lock_irq(lp->host->host_lock); + spin_unlock_irq(lport->host->host_lock); + rc = lport->tt.fcp_cmd_send(lport, fsp, fc_fcp_recv); + spin_lock_irq(lport->host->host_lock); if (rc) list_del(&fsp->list); return rc; } -static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp, +/** + * fc_fcp_cmd_send() - Send a FCP command + * @lport: The local port to send the command on + * @fsp: The FCP packet the command is on + * @resp: The handler for the response + */ +static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp, void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg)) @@ -1023,14 +1074,14 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp, struct fc_frame *fp; struct fc_seq *seq; struct fc_rport *rport; - struct fc_rport_libfc_priv *rp; + struct fc_rport_libfc_priv *rpriv; const size_t len = sizeof(fsp->cdb_cmd); int rc = 0; if (fc_fcp_lock_pkt(fsp)) return 0; - fp = fc_frame_alloc(lp, sizeof(fsp->cdb_cmd)); + fp = fc_fcp_frame_alloc(lport, sizeof(fsp->cdb_cmd)); if (!fp) { rc = -1; goto unlock; @@ -1040,15 +1091,15 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp, fr_fsp(fp) = fsp; rport = fsp->rport; fsp->max_payload = rport->maxframe_size; - rp = rport->dd_data; + rpriv = rport->dd_data; fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id, - fc_host_port_id(rp->local_port->host), FC_TYPE_FCP, + fc_host_port_id(rpriv->local_port->host), FC_TYPE_FCP, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0); + seq = lport->tt.exch_seq_send(lport, fp, resp, fc_fcp_pkt_destroy, + fsp, 0); if (!seq) { - fc_frame_free(fp); rc = -1; goto unlock; } @@ -1065,8 +1116,10 @@ unlock: return rc; } -/* - * transport error handler +/** + * fc_fcp_error() - Handler for FCP layer errors + * @fsp: The FCP packet the error is on + * @fp: The frame that has errored */ static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { @@ -1091,11 +1144,13 @@ unlock: fc_fcp_unlock_pkt(fsp); } -/* - * Scsi abort handler- calls to send an abort - * and then wait for abort completion +/** + * fc_fcp_pkt_abort() - Abort a fcp_pkt + * @fsp: The FCP packet to abort on + * + * Called to send an abort and then wait for abort completion */ -static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp) +static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp) { int rc = FAILED; @@ -1122,14 +1177,15 @@ static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp) return rc; } -/* - * Retry LUN reset after resource allocation failed. +/** + * fc_lun_reset_send() - Send LUN reset command + * @data: The FCP packet that identifies the LUN to be reset */ static void fc_lun_reset_send(unsigned long data) { struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data; - struct fc_lport *lp = fsp->lp; - if (lp->tt.fcp_cmd_send(lp, fsp, fc_tm_done)) { + struct fc_lport *lport = fsp->lp; + if (lport->tt.fcp_cmd_send(lport, fsp, fc_tm_done)) { if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY) return; if (fc_fcp_lock_pkt(fsp)) @@ -1140,11 +1196,15 @@ static void fc_lun_reset_send(unsigned long data) } } -/* - * Scsi device reset handler- send a LUN RESET to the device - * and wait for reset reply +/** + * fc_lun_reset() - Send a LUN RESET command to a device + * and wait for the reply + * @lport: The local port to sent the comand on + * @fsp: The FCP packet that identifies the LUN to be reset + * @id: The SCSI command ID + * @lun: The LUN ID to be reset */ -static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp, +static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp, unsigned int id, unsigned int lun) { int rc; @@ -1172,14 +1232,14 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp, spin_lock_bh(&fsp->scsi_pkt_lock); if (fsp->seq_ptr) { - lp->tt.exch_done(fsp->seq_ptr); + lport->tt.exch_done(fsp->seq_ptr); fsp->seq_ptr = NULL; } fsp->wait_for_comp = 0; spin_unlock_bh(&fsp->scsi_pkt_lock); if (!rc) { - FC_SCSI_DBG(lp, "lun reset failed\n"); + FC_SCSI_DBG(lport, "lun reset failed\n"); return FAILED; } @@ -1187,13 +1247,16 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp, if (fsp->cdb_status != FCP_TMF_CMPL) return FAILED; - FC_SCSI_DBG(lp, "lun reset to lun %u completed\n", lun); - fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED); + FC_SCSI_DBG(lport, "lun reset to lun %u completed\n", lun); + fc_fcp_cleanup_each_cmd(lport, id, lun, FC_CMD_ABORTED); return SUCCESS; } -/* - * Task Managment response handler +/** + * fc_tm_done() - Task Managment response handler + * @seq: The sequence that the response is on + * @fp: The response frame + * @arg: The FCP packet the response is for */ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg) { @@ -1230,34 +1293,31 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg) fc_fcp_unlock_pkt(fsp); } -static void fc_fcp_cleanup(struct fc_lport *lp) +/** + * fc_fcp_cleanup() - Cleanup all FCP exchanges on a local port + * @lport: The local port to be cleaned up + */ +static void fc_fcp_cleanup(struct fc_lport *lport) { - fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_ERROR); + fc_fcp_cleanup_each_cmd(lport, -1, -1, FC_ERROR); } -/* - * fc_fcp_timeout: called by OS timer function. - * - * The timer has been inactivated and must be reactivated if desired - * using fc_fcp_timer_set(). - * - * Algorithm: - * - * If REC is supported, just issue it, and return. The REC exchange will - * complete or time out, and recovery can continue at that point. - * - * Otherwise, if the response has been received without all the data, - * it has been ER_TIMEOUT since the response was received. +/** + * fc_fcp_timeout() - Handler for fcp_pkt timeouts + * @data: The FCP packet that has timed out * - * If the response has not been received, - * we see if data was received recently. If it has been, we continue waiting, - * otherwise, we abort the command. + * If REC is supported then just issue it and return. The REC exchange will + * complete or time out and recovery can continue at that point. Otherwise, + * if the response has been received without all the data it has been + * ER_TIMEOUT since the response was received. If the response has not been + * received we see if data was received recently. If it has been then we + * continue waiting, otherwise, we abort the command. */ static void fc_fcp_timeout(unsigned long data) { struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data; struct fc_rport *rport = fsp->rport; - struct fc_rport_libfc_priv *rp = rport->dd_data; + struct fc_rport_libfc_priv *rpriv = rport->dd_data; if (fc_fcp_lock_pkt(fsp)) return; @@ -1267,7 +1327,7 @@ static void fc_fcp_timeout(unsigned long data) fsp->state |= FC_SRB_FCP_PROCESSING_TMO; - if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED) + if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED) fc_fcp_rec(fsp); else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2), jiffies)) @@ -1281,39 +1341,40 @@ unlock: fc_fcp_unlock_pkt(fsp); } -/* - * Send a REC ELS request +/** + * fc_fcp_rec() - Send a REC ELS request + * @fsp: The FCP packet to send the REC request on */ static void fc_fcp_rec(struct fc_fcp_pkt *fsp) { - struct fc_lport *lp; + struct fc_lport *lport; struct fc_frame *fp; struct fc_rport *rport; - struct fc_rport_libfc_priv *rp; + struct fc_rport_libfc_priv *rpriv; - lp = fsp->lp; + lport = fsp->lp; rport = fsp->rport; - rp = rport->dd_data; - if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) { + rpriv = rport->dd_data; + if (!fsp->seq_ptr || rpriv->rp_state != RPORT_ST_READY) { fsp->status_code = FC_HRD_ERROR; fsp->io_status = 0; fc_fcp_complete_locked(fsp); return; } - fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec)); + fp = fc_fcp_frame_alloc(lport, sizeof(struct fc_els_rec)); if (!fp) goto retry; fr_seq(fp) = fsp->seq_ptr; fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id, - fc_host_port_id(rp->local_port->host), FC_TYPE_ELS, + fc_host_port_id(rpriv->local_port->host), FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - if (lp->tt.elsct_send(lp, rport->port_id, fp, ELS_REC, fc_fcp_rec_resp, - fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) { + if (lport->tt.elsct_send(lport, rport->port_id, fp, ELS_REC, + fc_fcp_rec_resp, fsp, + jiffies_to_msecs(FC_SCSI_REC_TOV))) { fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */ return; } - fc_frame_free(fp); retry: if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); @@ -1321,12 +1382,16 @@ retry: fc_timeout_error(fsp); } -/* - * Receive handler for REC ELS frame - * if it is a reject then let the scsi layer to handle - * the timeout. if it is a LS_ACC then if the io was not completed - * then set the timeout and return otherwise complete the exchange - * and tell the scsi layer to restart the I/O. +/** + * fc_fcp_rec_resp() - Handler for REC ELS responses + * @seq: The sequence the response is on + * @fp: The response frame + * @arg: The FCP packet the response is on + * + * If the response is a reject then the scsi layer will handle + * the timeout. If the response is a LS_ACC then if the I/O was not completed + * set the timeout and return. If the I/O was completed then complete the + * exchange and tell the SCSI layer. */ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) { @@ -1338,7 +1403,7 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) u32 offset; enum dma_data_direction data_dir; enum fc_rctl r_ctl; - struct fc_rport_libfc_priv *rp; + struct fc_rport_libfc_priv *rpriv; if (IS_ERR(fp)) { fc_fcp_rec_error(fsp, fp); @@ -1361,13 +1426,13 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) /* fall through */ case ELS_RJT_UNSUP: FC_FCP_DBG(fsp, "device does not support REC\n"); - rp = fsp->rport->dd_data; + rpriv = fsp->rport->dd_data; /* * if we do not spport RECs or got some bogus * reason then resetup timer so we check for * making progress. */ - rp->flags &= ~FC_RP_FLAGS_REC_SUPPORTED; + rpriv->flags &= ~FC_RP_FLAGS_REC_SUPPORTED; fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT); break; case ELS_RJT_LOGIC: @@ -1464,8 +1529,10 @@ out: fc_frame_free(fp); } -/* - * Handle error response or timeout for REC exchange. +/** + * fc_fcp_rec_error() - Handler for REC errors + * @fsp: The FCP packet the error is on + * @fp: The REC frame */ static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { @@ -1504,10 +1571,9 @@ out: fc_fcp_pkt_release(fsp); /* drop hold for outstanding REC */ } -/* - * Time out error routine: - * abort's the I/O close the exchange and - * send completion notification to scsi layer +/** + * fc_timeout_error() - Handler for fcp_pkt timeouts + * @fsp: The FCP packt that has timed out */ static void fc_timeout_error(struct fc_fcp_pkt *fsp) { @@ -1521,16 +1587,18 @@ static void fc_timeout_error(struct fc_fcp_pkt *fsp) fc_fcp_send_abort(fsp); } -/* - * Sequence retransmission request. +/** + * fc_fcp_srr() - Send a SRR request (Sequence Retransmission Request) + * @fsp: The FCP packet the SRR is to be sent on + * @r_ctl: The R_CTL field for the SRR request * This is called after receiving status but insufficient data, or * when expecting status but the request has timed out. */ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset) { - struct fc_lport *lp = fsp->lp; + struct fc_lport *lport = fsp->lp; struct fc_rport *rport; - struct fc_rport_libfc_priv *rp; + struct fc_rport_libfc_priv *rpriv; struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr); struct fc_seq *seq; struct fcp_srr *srr; @@ -1538,12 +1606,13 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset) u8 cdb_op; rport = fsp->rport; - rp = rport->dd_data; + rpriv = rport->dd_data; cdb_op = fsp->cdb_cmd.fc_cdb[0]; - if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY) + if (!(rpriv->flags & FC_RP_FLAGS_RETRY) || + rpriv->rp_state != RPORT_ST_READY) goto retry; /* shouldn't happen */ - fp = fc_frame_alloc(lp, sizeof(*srr)); + fp = fc_fcp_frame_alloc(lport, sizeof(*srr)); if (!fp) goto retry; @@ -1556,15 +1625,14 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset) srr->srr_rel_off = htonl(offset); fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id, - fc_host_port_id(rp->local_port->host), FC_TYPE_FCP, + fc_host_port_id(rpriv->local_port->host), FC_TYPE_FCP, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL, - fsp, jiffies_to_msecs(FC_SCSI_REC_TOV)); - if (!seq) { - fc_frame_free(fp); + seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL, + fsp, jiffies_to_msecs(FC_SCSI_REC_TOV)); + if (!seq) goto retry; - } + fsp->recov_seq = seq; fsp->xfer_len = offset; fsp->xfer_contig_end = offset; @@ -1575,8 +1643,11 @@ retry: fc_fcp_retry_cmd(fsp); } -/* - * Handle response from SRR. +/** + * fc_fcp_srr_resp() - Handler for SRR response + * @seq: The sequence the SRR is on + * @fp: The SRR frame + * @arg: The FCP packet the SRR is on */ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) { @@ -1622,6 +1693,11 @@ out: fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */ } +/** + * fc_fcp_srr_error() - Handler for SRR errors + * @fsp: The FCP packet that the SRR error is on + * @fp: The SRR frame + */ static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { if (fc_fcp_lock_pkt(fsp)) @@ -1646,31 +1722,36 @@ out: fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */ } -static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp) +/** + * fc_fcp_lport_queue_ready() - Determine if the lport and it's queue is ready + * @lport: The local port to be checked + */ +static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport) { /* lock ? */ - return (lp->state == LPORT_ST_READY) && lp->link_up && !lp->qfull; + return (lport->state == LPORT_ST_READY) && + lport->link_up && !lport->qfull; } /** - * fc_queuecommand - The queuecommand function of the scsi template - * @cmd: struct scsi_cmnd to be executed - * @done: Callback function to be called when cmd is completed + * fc_queuecommand() - The queuecommand function of the SCSI template + * @cmd: The scsi_cmnd to be executed + * @done: The callback function to be called when the scsi_cmnd is complete * - * this is the i/o strategy routine, called by the scsi layer - * this routine is called with holding the host_lock. + * This is the i/o strategy routine, called by the SCSI layer. This routine + * is called with the host_lock held. */ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) { - struct fc_lport *lp; + struct fc_lport *lport; struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); struct fc_fcp_pkt *fsp; - struct fc_rport_libfc_priv *rp; + struct fc_rport_libfc_priv *rpriv; int rval; int rc = 0; struct fcoe_dev_stats *stats; - lp = shost_priv(sc_cmd->device->host); + lport = shost_priv(sc_cmd->device->host); rval = fc_remote_port_chkready(rport); if (rval) { @@ -1689,14 +1770,16 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) goto out; } - rp = rport->dd_data; + rpriv = rport->dd_data; - if (!fc_fcp_lport_queue_ready(lp)) { + if (!fc_fcp_lport_queue_ready(lport)) { + if (lport->qfull) + fc_fcp_can_queue_ramp_down(lport); rc = SCSI_MLQUEUE_HOST_BUSY; goto out; } - fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC); + fsp = fc_fcp_pkt_alloc(lport, GFP_ATOMIC); if (fsp == NULL) { rc = SCSI_MLQUEUE_HOST_BUSY; goto out; @@ -1706,8 +1789,9 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) * build the libfc request pkt */ fsp->cmd = sc_cmd; /* save the cmd */ - fsp->lp = lp; /* save the softc ptr */ + fsp->lp = lport; /* save the softc ptr */ fsp->rport = rport; /* set the remote port ptr */ + fsp->xfer_ddp = FC_XID_UNKNOWN; sc_cmd->scsi_done = done; /* @@ -1719,7 +1803,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) /* * setup the data direction */ - stats = fc_lport_get_stats(lp); + stats = fc_lport_get_stats(lport); if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) { fsp->req_flags = FC_SRB_READ; stats->InputRequests++; @@ -1733,7 +1817,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) stats->ControlRequests++; } - fsp->tgt_flags = rp->flags; + fsp->tgt_flags = rpriv->flags; init_timer(&fsp->timer); fsp->timer.data = (unsigned long)fsp; @@ -1743,7 +1827,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) * if we get -1 return then put the request in the pending * queue. */ - rval = fc_fcp_pkt_send(lp, fsp); + rval = fc_fcp_pkt_send(lport, fsp); if (rval != 0) { fsp->state = FC_SRB_FREE; fc_fcp_pkt_release(fsp); @@ -1755,18 +1839,17 @@ out: EXPORT_SYMBOL(fc_queuecommand); /** - * fc_io_compl() - Handle responses for completed commands - * @fsp: scsi packet - * - * Translates a error to a Linux SCSI error. + * fc_io_compl() - Handle responses for completed commands + * @fsp: The FCP packet that is complete * + * Translates fcp_pkt errors to a Linux SCSI errors. * The fcp packet lock must be held when calling. */ static void fc_io_compl(struct fc_fcp_pkt *fsp) { struct fc_fcp_internal *si; struct scsi_cmnd *sc_cmd; - struct fc_lport *lp; + struct fc_lport *lport; unsigned long flags; /* release outstanding ddp context */ @@ -1779,28 +1862,26 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp) spin_lock_bh(&fsp->scsi_pkt_lock); } - lp = fsp->lp; - si = fc_get_scsi_internal(lp); - spin_lock_irqsave(lp->host->host_lock, flags); + lport = fsp->lp; + si = fc_get_scsi_internal(lport); + spin_lock_irqsave(lport->host->host_lock, flags); if (!fsp->cmd) { - spin_unlock_irqrestore(lp->host->host_lock, flags); + spin_unlock_irqrestore(lport->host->host_lock, flags); return; } /* - * if a command timed out while we had to try and throttle IO - * and it is now getting cleaned up, then we are about to - * try again so clear the throttled flag incase we get more - * time outs. + * if can_queue ramp down is done then try can_queue ramp up + * since commands are completing now. */ - if (si->throttled && fsp->state & FC_SRB_NOMEM) - si->throttled = 0; + if (si->last_can_queue_ramp_down_time) + fc_fcp_can_queue_ramp_up(lport); sc_cmd = fsp->cmd; fsp->cmd = NULL; if (!sc_cmd->SCp.ptr) { - spin_unlock_irqrestore(lp->host->host_lock, flags); + spin_unlock_irqrestore(lport->host->host_lock, flags); return; } @@ -1814,21 +1895,6 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp) sc_cmd->result = DID_OK << 16; if (fsp->scsi_resid) CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid; - } else if (fsp->cdb_status == QUEUE_FULL) { - struct scsi_device *tmp_sdev; - struct scsi_device *sdev = sc_cmd->device; - - shost_for_each_device(tmp_sdev, sdev->host) { - if (tmp_sdev->id != sdev->id) - continue; - - if (tmp_sdev->queue_depth > 1) { - scsi_track_queue_full(tmp_sdev, - tmp_sdev-> - queue_depth - 1); - } - } - sc_cmd->result = (DID_OK << 16) | fsp->cdb_status; } else { /* * transport level I/O was ok but scsi @@ -1846,7 +1912,8 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp) * scsi status is good but transport level * underrun. */ - sc_cmd->result = DID_OK << 16; + sc_cmd->result = (fsp->state & FC_SRB_RCV_STATUS ? + DID_OK : DID_ERROR) << 16; } else { /* * scsi got underrun, this is an error @@ -1881,60 +1948,42 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp) list_del(&fsp->list); sc_cmd->SCp.ptr = NULL; sc_cmd->scsi_done(sc_cmd); - spin_unlock_irqrestore(lp->host->host_lock, flags); + spin_unlock_irqrestore(lport->host->host_lock, flags); /* release ref from initial allocation in queue command */ fc_fcp_pkt_release(fsp); } /** - * fc_fcp_complete() - complete processing of a fcp packet - * @fsp: fcp packet - * - * This function may sleep if a fsp timer is pending. - * The host lock must not be held by caller. - */ -void fc_fcp_complete(struct fc_fcp_pkt *fsp) -{ - if (fc_fcp_lock_pkt(fsp)) - return; - - fc_fcp_complete_locked(fsp); - fc_fcp_unlock_pkt(fsp); -} -EXPORT_SYMBOL(fc_fcp_complete); - -/** * fc_eh_abort() - Abort a command - * @sc_cmd: scsi command to abort + * @sc_cmd: The SCSI command to abort * - * From scsi host template. - * send ABTS to the target device and wait for the response - * sc_cmd is the pointer to the command to be aborted. + * From SCSI host template. + * Send an ABTS to the target device and wait for the response. */ int fc_eh_abort(struct scsi_cmnd *sc_cmd) { struct fc_fcp_pkt *fsp; - struct fc_lport *lp; + struct fc_lport *lport; int rc = FAILED; unsigned long flags; - lp = shost_priv(sc_cmd->device->host); - if (lp->state != LPORT_ST_READY) + lport = shost_priv(sc_cmd->device->host); + if (lport->state != LPORT_ST_READY) return rc; - else if (!lp->link_up) + else if (!lport->link_up) return rc; - spin_lock_irqsave(lp->host->host_lock, flags); + spin_lock_irqsave(lport->host->host_lock, flags); fsp = CMD_SP(sc_cmd); if (!fsp) { /* command completed while scsi eh was setting up */ - spin_unlock_irqrestore(lp->host->host_lock, flags); + spin_unlock_irqrestore(lport->host->host_lock, flags); return SUCCESS; } /* grab a ref so the fsp and sc_cmd cannot be relased from under us */ fc_fcp_pkt_hold(fsp); - spin_unlock_irqrestore(lp->host->host_lock, flags); + spin_unlock_irqrestore(lport->host->host_lock, flags); if (fc_fcp_lock_pkt(fsp)) { /* completed while we were waiting for timer to be deleted */ @@ -1942,7 +1991,7 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd) goto release_pkt; } - rc = fc_fcp_pkt_abort(lp, fsp); + rc = fc_fcp_pkt_abort(fsp); fc_fcp_unlock_pkt(fsp); release_pkt: @@ -1952,37 +2001,34 @@ release_pkt: EXPORT_SYMBOL(fc_eh_abort); /** - * fc_eh_device_reset() Reset a single LUN - * @sc_cmd: scsi command + * fc_eh_device_reset() - Reset a single LUN + * @sc_cmd: The SCSI command which identifies the device whose + * LUN is to be reset * - * Set from scsi host template to send tm cmd to the target and wait for the - * response. + * Set from SCSI host template. */ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd) { - struct fc_lport *lp; + struct fc_lport *lport; struct fc_fcp_pkt *fsp; struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); int rc = FAILED; - struct fc_rport_libfc_priv *rp; int rval; rval = fc_remote_port_chkready(rport); if (rval) goto out; - rp = rport->dd_data; - lp = shost_priv(sc_cmd->device->host); + lport = shost_priv(sc_cmd->device->host); - if (lp->state != LPORT_ST_READY) + if (lport->state != LPORT_ST_READY) return rc; - FC_SCSI_DBG(lp, "Resetting rport (%6x)\n", rport->port_id); + FC_SCSI_DBG(lport, "Resetting rport (%6x)\n", rport->port_id); - fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO); + fsp = fc_fcp_pkt_alloc(lport, GFP_NOIO); if (fsp == NULL) { printk(KERN_WARNING "libfc: could not allocate scsi_pkt\n"); - sc_cmd->result = DID_NO_CONNECT << 16; goto out; } @@ -1991,13 +2037,13 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd) * the sc passed in is not setup for execution like when sent * through the queuecommand callout. */ - fsp->lp = lp; /* save the softc ptr */ + fsp->lp = lport; /* save the softc ptr */ fsp->rport = rport; /* set the remote port ptr */ /* * flush outstanding commands */ - rc = fc_lun_reset(lp, fsp, scmd_id(sc_cmd), sc_cmd->device->lun); + rc = fc_lun_reset(lport, fsp, scmd_id(sc_cmd), sc_cmd->device->lun); fsp->state = FC_SRB_FREE; fc_fcp_pkt_release(fsp); @@ -2007,38 +2053,39 @@ out: EXPORT_SYMBOL(fc_eh_device_reset); /** - * fc_eh_host_reset() - The reset function will reset the ports on the host. - * @sc_cmd: scsi command + * fc_eh_host_reset() - Reset a Scsi_Host. + * @sc_cmd: The SCSI command that identifies the SCSI host to be reset */ int fc_eh_host_reset(struct scsi_cmnd *sc_cmd) { struct Scsi_Host *shost = sc_cmd->device->host; - struct fc_lport *lp = shost_priv(shost); + struct fc_lport *lport = shost_priv(shost); unsigned long wait_tmo; - FC_SCSI_DBG(lp, "Resetting host\n"); + FC_SCSI_DBG(lport, "Resetting host\n"); - lp->tt.lport_reset(lp); + lport->tt.lport_reset(lport); wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT; - while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo)) + while (!fc_fcp_lport_queue_ready(lport) && time_before(jiffies, + wait_tmo)) msleep(1000); - if (fc_fcp_lport_queue_ready(lp)) { + if (fc_fcp_lport_queue_ready(lport)) { shost_printk(KERN_INFO, shost, "libfc: Host reset succeeded " - "on port (%6x)\n", fc_host_port_id(lp->host)); + "on port (%6x)\n", fc_host_port_id(lport->host)); return SUCCESS; } else { shost_printk(KERN_INFO, shost, "libfc: Host reset failed, " "port (%6x) is not ready.\n", - fc_host_port_id(lp->host)); + fc_host_port_id(lport->host)); return FAILED; } } EXPORT_SYMBOL(fc_eh_host_reset); /** - * fc_slave_alloc() - configure queue depth - * @sdev: scsi device + * fc_slave_alloc() - Configure the queue depth of a Scsi_Host + * @sdev: The SCSI device that identifies the SCSI host * * Configures queue depth based on host's cmd_per_len. If not set * then we use the libfc default. @@ -2046,29 +2093,50 @@ EXPORT_SYMBOL(fc_eh_host_reset); int fc_slave_alloc(struct scsi_device *sdev) { struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); - int queue_depth; if (!rport || fc_remote_port_chkready(rport)) return -ENXIO; - if (sdev->tagged_supported) { - if (sdev->host->hostt->cmd_per_lun) - queue_depth = sdev->host->hostt->cmd_per_lun; - else - queue_depth = FC_FCP_DFLT_QUEUE_DEPTH; - scsi_activate_tcq(sdev, queue_depth); - } + if (sdev->tagged_supported) + scsi_activate_tcq(sdev, FC_FCP_DFLT_QUEUE_DEPTH); + else + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), + FC_FCP_DFLT_QUEUE_DEPTH); + return 0; } EXPORT_SYMBOL(fc_slave_alloc); -int fc_change_queue_depth(struct scsi_device *sdev, int qdepth) +/** + * fc_change_queue_depth() - Change a device's queue depth + * @sdev: The SCSI device whose queue depth is to change + * @qdepth: The new queue depth + * @reason: The resason for the change + */ +int fc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) { - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + switch (reason) { + case SCSI_QDEPTH_DEFAULT: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + break; + case SCSI_QDEPTH_QFULL: + scsi_track_queue_full(sdev, qdepth); + break; + case SCSI_QDEPTH_RAMP_UP: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + break; + default: + return -EOPNOTSUPP; + } return sdev->queue_depth; } EXPORT_SYMBOL(fc_change_queue_depth); +/** + * fc_change_queue_type() - Change a device's queue type + * @sdev: The SCSI device whose queue depth is to change + * @tag_type: Identifier for queue type + */ int fc_change_queue_type(struct scsi_device *sdev, int tag_type) { if (sdev->tagged_supported) { @@ -2084,38 +2152,69 @@ int fc_change_queue_type(struct scsi_device *sdev, int tag_type) } EXPORT_SYMBOL(fc_change_queue_type); -void fc_fcp_destroy(struct fc_lport *lp) +/** + * fc_fcp_destory() - Tear down the FCP layer for a given local port + * @lport: The local port that no longer needs the FCP layer + */ +void fc_fcp_destroy(struct fc_lport *lport) { - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); + struct fc_fcp_internal *si = fc_get_scsi_internal(lport); if (!list_empty(&si->scsi_pkt_queue)) printk(KERN_ERR "libfc: Leaked SCSI packets when destroying " - "port (%6x)\n", fc_host_port_id(lp->host)); + "port (%6x)\n", fc_host_port_id(lport->host)); mempool_destroy(si->scsi_pkt_pool); kfree(si); - lp->scsi_priv = NULL; + lport->scsi_priv = NULL; } EXPORT_SYMBOL(fc_fcp_destroy); -int fc_fcp_init(struct fc_lport *lp) +int fc_setup_fcp() +{ + int rc = 0; + + scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt", + sizeof(struct fc_fcp_pkt), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!scsi_pkt_cachep) { + printk(KERN_ERR "libfc: Unable to allocate SRB cache, " + "module load failed!"); + rc = -ENOMEM; + } + + return rc; +} + +void fc_destroy_fcp() +{ + if (scsi_pkt_cachep) + kmem_cache_destroy(scsi_pkt_cachep); +} + +/** + * fc_fcp_init() - Initialize the FCP layer for a local port + * @lport: The local port to initialize the exchange layer for + */ +int fc_fcp_init(struct fc_lport *lport) { int rc; struct fc_fcp_internal *si; - if (!lp->tt.fcp_cmd_send) - lp->tt.fcp_cmd_send = fc_fcp_cmd_send; + if (!lport->tt.fcp_cmd_send) + lport->tt.fcp_cmd_send = fc_fcp_cmd_send; - if (!lp->tt.fcp_cleanup) - lp->tt.fcp_cleanup = fc_fcp_cleanup; + if (!lport->tt.fcp_cleanup) + lport->tt.fcp_cleanup = fc_fcp_cleanup; - if (!lp->tt.fcp_abort_io) - lp->tt.fcp_abort_io = fc_fcp_abort_io; + if (!lport->tt.fcp_abort_io) + lport->tt.fcp_abort_io = fc_fcp_abort_io; si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL); if (!si) return -ENOMEM; - lp->scsi_priv = si; + lport->scsi_priv = si; + si->max_can_queue = lport->host->can_queue; INIT_LIST_HEAD(&si->scsi_pkt_queue); si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep); @@ -2130,42 +2229,3 @@ free_internal: return rc; } EXPORT_SYMBOL(fc_fcp_init); - -static int __init libfc_init(void) -{ - int rc; - - scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt", - sizeof(struct fc_fcp_pkt), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (scsi_pkt_cachep == NULL) { - printk(KERN_ERR "libfc: Unable to allocate SRB cache, " - "module load failed!"); - return -ENOMEM; - } - - rc = fc_setup_exch_mgr(); - if (rc) - goto destroy_pkt_cache; - - rc = fc_setup_rport(); - if (rc) - goto destroy_em; - - return rc; -destroy_em: - fc_destroy_exch_mgr(); -destroy_pkt_cache: - kmem_cache_destroy(scsi_pkt_cachep); - return rc; -} - -static void __exit libfc_exit(void) -{ - kmem_cache_destroy(scsi_pkt_cachep); - fc_destroy_exch_mgr(); - fc_destroy_rport(); -} - -module_init(libfc_init); -module_exit(libfc_exit); diff --git a/drivers/scsi/libfc/fc_frame.c b/drivers/scsi/libfc/fc_frame.c index 63fe00cfe667..6da01c616964 100644 --- a/drivers/scsi/libfc/fc_frame.c +++ b/drivers/scsi/libfc/fc_frame.c @@ -51,24 +51,24 @@ EXPORT_SYMBOL(fc_frame_crc_check); * Allocate a frame intended to be sent via fcoe_xmit. * Get an sk_buff for the frame and set the length. */ -struct fc_frame *__fc_frame_alloc(size_t len) +struct fc_frame *_fc_frame_alloc(size_t len) { struct fc_frame *fp; struct sk_buff *skb; WARN_ON((len % sizeof(u32)) != 0); len += sizeof(struct fc_frame_header); - skb = dev_alloc_skb(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM); + skb = alloc_skb_fclone(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM + + NET_SKB_PAD, GFP_ATOMIC); if (!skb) return NULL; + skb_reserve(skb, NET_SKB_PAD + FC_FRAME_HEADROOM); fp = (struct fc_frame *) skb; fc_frame_init(fp); - skb_reserve(skb, FC_FRAME_HEADROOM); skb_put(skb, len); return fp; } -EXPORT_SYMBOL(__fc_frame_alloc); - +EXPORT_SYMBOL(_fc_frame_alloc); struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len) { @@ -78,7 +78,7 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len) fill = payload_len % 4; if (fill != 0) fill = 4 - fill; - fp = __fc_frame_alloc(payload_len + fill); + fp = _fc_frame_alloc(payload_len + fill); if (fp) { memset((char *) fr_hdr(fp) + payload_len, 0, fill); /* trim is OK, we just allocated it so there are no fragments */ @@ -87,3 +87,4 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len) } return fp; } +EXPORT_SYMBOL(fc_frame_alloc_fill); diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c new file mode 100644 index 000000000000..39f4b6ab04b4 --- /dev/null +++ b/drivers/scsi/libfc/fc_libfc.c @@ -0,0 +1,134 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/scatterlist.h> +#include <linux/crc32.h> + +#include <scsi/libfc.h> + +#include "fc_libfc.h" + +MODULE_AUTHOR("Open-FCoE.org"); +MODULE_DESCRIPTION("libfc"); +MODULE_LICENSE("GPL v2"); + +unsigned int fc_debug_logging; +module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); + +/** + * libfc_init() - Initialize libfc.ko + */ +static int __init libfc_init(void) +{ + int rc = 0; + + rc = fc_setup_fcp(); + if (rc) + return rc; + + rc = fc_setup_exch_mgr(); + if (rc) + goto destroy_pkt_cache; + + rc = fc_setup_rport(); + if (rc) + goto destroy_em; + + return rc; +destroy_em: + fc_destroy_exch_mgr(); +destroy_pkt_cache: + fc_destroy_fcp(); + return rc; +} +module_init(libfc_init); + +/** + * libfc_exit() - Tear down libfc.ko + */ +static void __exit libfc_exit(void) +{ + fc_destroy_fcp(); + fc_destroy_exch_mgr(); + fc_destroy_rport(); +} +module_exit(libfc_exit); + +/** + * fc_copy_buffer_to_sglist() - This routine copies the data of a buffer + * into a scatter-gather list (SG list). + * + * @buf: pointer to the data buffer. + * @len: the byte-length of the data buffer. + * @sg: pointer to the pointer of the SG list. + * @nents: pointer to the remaining number of entries in the SG list. + * @offset: pointer to the current offset in the SG list. + * @km_type: dedicated page table slot type for kmap_atomic. + * @crc: pointer to the 32-bit crc value. + * If crc is NULL, CRC is not calculated. + */ +u32 fc_copy_buffer_to_sglist(void *buf, size_t len, + struct scatterlist *sg, + u32 *nents, size_t *offset, + enum km_type km_type, u32 *crc) +{ + size_t remaining = len; + u32 copy_len = 0; + + while (remaining > 0 && sg) { + size_t off, sg_bytes; + void *page_addr; + + if (*offset >= sg->length) { + /* + * Check for end and drop resources + * from the last iteration. + */ + if (!(*nents)) + break; + --(*nents); + *offset -= sg->length; + sg = sg_next(sg); + continue; + } + sg_bytes = min(remaining, sg->length - *offset); + + /* + * The scatterlist item may be bigger than PAGE_SIZE, + * but we are limited to mapping PAGE_SIZE at a time. + */ + off = *offset + sg->offset; + sg_bytes = min(sg_bytes, + (size_t)(PAGE_SIZE - (off & ~PAGE_MASK))); + page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT), + km_type); + if (crc) + *crc = crc32(*crc, buf, sg_bytes); + memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes); + kunmap_atomic(page_addr, km_type); + buf += sg_bytes; + *offset += sg_bytes; + remaining -= sg_bytes; + copy_len += sg_bytes; + } + return copy_len; +} diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h new file mode 100644 index 000000000000..741fd5c72e13 --- /dev/null +++ b/drivers/scsi/libfc/fc_libfc.h @@ -0,0 +1,112 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FC_LIBFC_H_ +#define _FC_LIBFC_H_ + +#define FC_LIBFC_LOGGING 0x01 /* General logging, not categorized */ +#define FC_LPORT_LOGGING 0x02 /* lport layer logging */ +#define FC_DISC_LOGGING 0x04 /* discovery layer logging */ +#define FC_RPORT_LOGGING 0x08 /* rport layer logging */ +#define FC_FCP_LOGGING 0x10 /* I/O path logging */ +#define FC_EM_LOGGING 0x20 /* Exchange Manager logging */ +#define FC_EXCH_LOGGING 0x40 /* Exchange/Sequence logging */ +#define FC_SCSI_LOGGING 0x80 /* SCSI logging (mostly error handling) */ + +extern unsigned int fc_debug_logging; + +#define FC_CHECK_LOGGING(LEVEL, CMD) \ + do { \ + if (unlikely(fc_debug_logging & LEVEL)) \ + do { \ + CMD; \ + } while (0); \ + } while (0) + +#define FC_LIBFC_DBG(fmt, args...) \ + FC_CHECK_LOGGING(FC_LIBFC_LOGGING, \ + printk(KERN_INFO "libfc: " fmt, ##args)) + +#define FC_LPORT_DBG(lport, fmt, args...) \ + FC_CHECK_LOGGING(FC_LPORT_LOGGING, \ + printk(KERN_INFO "host%u: lport %6x: " fmt, \ + (lport)->host->host_no, \ + fc_host_port_id((lport)->host), ##args)) + +#define FC_DISC_DBG(disc, fmt, args...) \ + FC_CHECK_LOGGING(FC_DISC_LOGGING, \ + printk(KERN_INFO "host%u: disc: " fmt, \ + (disc)->lport->host->host_no, \ + ##args)) + +#define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \ + FC_CHECK_LOGGING(FC_RPORT_LOGGING, \ + printk(KERN_INFO "host%u: rport %6x: " fmt, \ + (lport)->host->host_no, \ + (port_id), ##args)) + +#define FC_RPORT_DBG(rdata, fmt, args...) \ + FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args) + +#define FC_FCP_DBG(pkt, fmt, args...) \ + FC_CHECK_LOGGING(FC_FCP_LOGGING, \ + printk(KERN_INFO "host%u: fcp: %6x: " fmt, \ + (pkt)->lp->host->host_no, \ + pkt->rport->port_id, ##args)) + +#define FC_EXCH_DBG(exch, fmt, args...) \ + FC_CHECK_LOGGING(FC_EXCH_LOGGING, \ + printk(KERN_INFO "host%u: xid %4x: " fmt, \ + (exch)->lp->host->host_no, \ + exch->xid, ##args)) + +#define FC_SCSI_DBG(lport, fmt, args...) \ + FC_CHECK_LOGGING(FC_SCSI_LOGGING, \ + printk(KERN_INFO "host%u: scsi: " fmt, \ + (lport)->host->host_no, ##args)) + +/* + * Set up direct-data placement for this I/O request + */ +void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid); + +/* + * Module setup functions + */ +int fc_setup_exch_mgr(void); +void fc_destroy_exch_mgr(void); +int fc_setup_rport(void); +void fc_destroy_rport(void); +int fc_setup_fcp(void); +void fc_destroy_fcp(void); + +/* + * Internal libfc functions + */ +const char *fc_els_resp_type(struct fc_frame *); + +/* + * Copies a buffer into an sg list + */ +u32 fc_copy_buffer_to_sglist(void *buf, size_t len, + struct scatterlist *sg, + u32 *nents, size_t *offset, + enum km_type km_type, u32 *crc); + +#endif /* _FC_LIBFC_H_ */ diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index bd2f77197447..74338c83ad0a 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -56,7 +56,7 @@ * at the same time. * * When discovery succeeds or fails a callback is made to the lport as - * notification. Currently, succesful discovery causes the lport to take no + * notification. Currently, successful discovery causes the lport to take no * action. A failure will cause the lport to reset. There is likely a circular * locking problem with this implementation. */ @@ -94,6 +94,9 @@ #include <scsi/libfc.h> #include <scsi/fc_encode.h> +#include <linux/scatterlist.h> + +#include "fc_libfc.h" /* Fabric IDs to use for point-to-point mode, chosen on whims. */ #define FC_LOCAL_PTP_FID_LO 0x010101 @@ -106,8 +109,7 @@ static void fc_lport_error(struct fc_lport *, struct fc_frame *); static void fc_lport_enter_reset(struct fc_lport *); static void fc_lport_enter_flogi(struct fc_lport *); static void fc_lport_enter_dns(struct fc_lport *); -static void fc_lport_enter_rpn_id(struct fc_lport *); -static void fc_lport_enter_rft_id(struct fc_lport *); +static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state); static void fc_lport_enter_scr(struct fc_lport *); static void fc_lport_enter_ready(struct fc_lport *); static void fc_lport_enter_logo(struct fc_lport *); @@ -116,14 +118,40 @@ static const char *fc_lport_state_names[] = { [LPORT_ST_DISABLED] = "disabled", [LPORT_ST_FLOGI] = "FLOGI", [LPORT_ST_DNS] = "dNS", - [LPORT_ST_RPN_ID] = "RPN_ID", + [LPORT_ST_RNN_ID] = "RNN_ID", + [LPORT_ST_RSNN_NN] = "RSNN_NN", + [LPORT_ST_RSPN_ID] = "RSPN_ID", [LPORT_ST_RFT_ID] = "RFT_ID", + [LPORT_ST_RFF_ID] = "RFF_ID", [LPORT_ST_SCR] = "SCR", [LPORT_ST_READY] = "Ready", [LPORT_ST_LOGO] = "LOGO", [LPORT_ST_RESET] = "reset", }; +/** + * struct fc_bsg_info - FC Passthrough managemet structure + * @job: The passthrough job + * @lport: The local port to pass through a command + * @rsp_code: The expected response code + * @sg: job->reply_payload.sg_list + * @nents: job->reply_payload.sg_cnt + * @offset: The offset into the response data + */ +struct fc_bsg_info { + struct fc_bsg_job *job; + struct fc_lport *lport; + u16 rsp_code; + struct scatterlist *sg; + u32 nents; + size_t offset; +}; + +/** + * fc_frame_drop() - Dummy frame handler + * @lport: The local port the frame was received on + * @fp: The received frame + */ static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp) { fc_frame_free(fp); @@ -150,8 +178,8 @@ static void fc_lport_rport_callback(struct fc_lport *lport, switch (event) { case RPORT_EV_READY: if (lport->state == LPORT_ST_DNS) { - lport->dns_rp = rdata; - fc_lport_enter_rpn_id(lport); + lport->dns_rdata = rdata; + fc_lport_enter_ns(lport, LPORT_ST_RNN_ID); } else { FC_LPORT_DBG(lport, "Received an READY event " "on port (%6x) for the directory " @@ -165,7 +193,7 @@ static void fc_lport_rport_callback(struct fc_lport *lport, case RPORT_EV_LOGO: case RPORT_EV_FAILED: case RPORT_EV_STOP: - lport->dns_rp = NULL; + lport->dns_rdata = NULL; break; case RPORT_EV_NONE: break; @@ -189,8 +217,8 @@ static const char *fc_lport_state(struct fc_lport *lport) /** * fc_lport_ptp_setup() - Create an rport for point-to-point mode - * @lport: The lport to attach the ptp rport to - * @fid: The FID of the ptp rport + * @lport: The lport to attach the ptp rport to + * @remote_fid: The FID of the ptp rport * @remote_wwpn: The WWPN of the ptp rport * @remote_wwnn: The WWNN of the ptp rport */ @@ -199,18 +227,22 @@ static void fc_lport_ptp_setup(struct fc_lport *lport, u64 remote_wwnn) { mutex_lock(&lport->disc.disc_mutex); - if (lport->ptp_rp) - lport->tt.rport_logoff(lport->ptp_rp); - lport->ptp_rp = lport->tt.rport_create(lport, remote_fid); - lport->ptp_rp->ids.port_name = remote_wwpn; - lport->ptp_rp->ids.node_name = remote_wwnn; + if (lport->ptp_rdata) + lport->tt.rport_logoff(lport->ptp_rdata); + lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid); + lport->ptp_rdata->ids.port_name = remote_wwpn; + lport->ptp_rdata->ids.node_name = remote_wwnn; mutex_unlock(&lport->disc.disc_mutex); - lport->tt.rport_login(lport->ptp_rp); + lport->tt.rport_login(lport->ptp_rdata); fc_lport_enter_ready(lport); } +/** + * fc_get_host_port_type() - Return the port type of the given Scsi_Host + * @shost: The SCSI host whose port type is to be determined + */ void fc_get_host_port_type(struct Scsi_Host *shost) { /* TODO - currently just NPORT */ @@ -218,17 +250,33 @@ void fc_get_host_port_type(struct Scsi_Host *shost) } EXPORT_SYMBOL(fc_get_host_port_type); +/** + * fc_get_host_port_state() - Return the port state of the given Scsi_Host + * @shost: The SCSI host whose port state is to be determined + */ void fc_get_host_port_state(struct Scsi_Host *shost) { - struct fc_lport *lp = shost_priv(shost); + struct fc_lport *lport = shost_priv(shost); - if (lp->link_up) - fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; + mutex_lock(&lport->lp_mutex); + if (!lport->link_up) + fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; else - fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; + switch (lport->state) { + case LPORT_ST_READY: + fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; + break; + default: + fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; + } + mutex_unlock(&lport->lp_mutex); } EXPORT_SYMBOL(fc_get_host_port_state); +/** + * fc_get_host_speed() - Return the speed of the given Scsi_Host + * @shost: The SCSI host whose port speed is to be determined + */ void fc_get_host_speed(struct Scsi_Host *shost) { struct fc_lport *lport = shost_priv(shost); @@ -237,24 +285,28 @@ void fc_get_host_speed(struct Scsi_Host *shost) } EXPORT_SYMBOL(fc_get_host_speed); +/** + * fc_get_host_stats() - Return the Scsi_Host's statistics + * @shost: The SCSI host whose statistics are to be returned + */ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost) { struct fc_host_statistics *fcoe_stats; - struct fc_lport *lp = shost_priv(shost); + struct fc_lport *lport = shost_priv(shost); struct timespec v0, v1; unsigned int cpu; - fcoe_stats = &lp->host_stats; + fcoe_stats = &lport->host_stats; memset(fcoe_stats, 0, sizeof(struct fc_host_statistics)); jiffies_to_timespec(jiffies, &v0); - jiffies_to_timespec(lp->boot_time, &v1); + jiffies_to_timespec(lport->boot_time, &v1); fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec); for_each_possible_cpu(cpu) { struct fcoe_dev_stats *stats; - stats = per_cpu_ptr(lp->dev_stats, cpu); + stats = per_cpu_ptr(lport->dev_stats, cpu); fcoe_stats->tx_frames += stats->TxFrames; fcoe_stats->tx_words += stats->TxWords; @@ -279,12 +331,15 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost) } EXPORT_SYMBOL(fc_get_host_stats); -/* - * Fill in FLOGI command for request. +/** + * fc_lport_flogi_fill() - Fill in FLOGI command for request + * @lport: The local port the FLOGI is for + * @flogi: The FLOGI command + * @op: The opcode */ -static void -fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi, - unsigned int op) +static void fc_lport_flogi_fill(struct fc_lport *lport, + struct fc_els_flogi *flogi, + unsigned int op) { struct fc_els_csp *sp; struct fc_els_cssp *cp; @@ -312,8 +367,10 @@ fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi, } } -/* - * Add a supported FC-4 type. +/** + * fc_lport_add_fc4_type() - Add a supported FC-4 type to a local port + * @lport: The local port to add a new FC-4 type to + * @type: The new FC-4 type */ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type) { @@ -325,11 +382,11 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type) /** * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report. + * @sp: The sequence in the RLIR exchange + * @fp: The RLIR request frame * @lport: Fibre Channel local port recieving the RLIR - * @sp: current sequence in the RLIR exchange - * @fp: RLIR request frame * - * Locking Note: The lport lock is exected to be held before calling + * Locking Note: The lport lock is expected to be held before calling * this function. */ static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp, @@ -344,11 +401,11 @@ static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp, /** * fc_lport_recv_echo_req() - Handle received ECHO request - * @lport: Fibre Channel local port recieving the ECHO - * @sp: current sequence in the ECHO exchange - * @fp: ECHO request frame + * @sp: The sequence in the ECHO exchange + * @fp: ECHO request frame + * @lport: The local port recieving the ECHO * - * Locking Note: The lport lock is exected to be held before calling + * Locking Note: The lport lock is expected to be held before calling * this function. */ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, @@ -361,7 +418,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, void *dp; u32 f_ctl; - FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n", + FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n", fc_lport_state(lport)); len = fr_len(in_fp) - sizeof(struct fc_frame_header); @@ -374,7 +431,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, if (fp) { dp = fc_frame_payload_get(fp, len); memcpy(dp, pp, len); - *((u32 *)dp) = htonl(ELS_LS_ACC << 24); + *((__be32 *)dp) = htonl(ELS_LS_ACC << 24); sp = lport->tt.seq_start_next(sp); f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ; fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, @@ -385,12 +442,12 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, } /** - * fc_lport_recv_echo_req() - Handle received Request Node ID data request - * @lport: Fibre Channel local port recieving the RNID - * @sp: current sequence in the RNID exchange - * @fp: RNID request frame + * fc_lport_recv_rnid_req() - Handle received Request Node ID data request + * @sp: The sequence in the RNID exchange + * @fp: The RNID request frame + * @lport: The local port recieving the RNID * - * Locking Note: The lport lock is exected to be held before calling + * Locking Note: The lport lock is expected to be held before calling * this function. */ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, @@ -453,9 +510,9 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, /** * fc_lport_recv_logo_req() - Handle received fabric LOGO request - * @lport: Fibre Channel local port recieving the LOGO - * @sp: current sequence in the LOGO exchange - * @fp: LOGO request frame + * @sp: The sequence in the LOGO exchange + * @fp: The LOGO request frame + * @lport: The local port recieving the LOGO * * Locking Note: The lport lock is exected to be held before calling * this function. @@ -470,7 +527,7 @@ static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp, /** * fc_fabric_login() - Start the lport state machine - * @lport: The lport that should log into the fabric + * @lport: The local port that should log into the fabric * * Locking Note: This function should not be called * with the lport lock held. @@ -491,47 +548,69 @@ int fc_fabric_login(struct fc_lport *lport) EXPORT_SYMBOL(fc_fabric_login); /** - * fc_linkup() - Handler for transport linkup events + * __fc_linkup() - Handler for transport linkup events * @lport: The lport whose link is up + * + * Locking: must be called with the lp_mutex held */ -void fc_linkup(struct fc_lport *lport) +void __fc_linkup(struct fc_lport *lport) { - printk(KERN_INFO "libfc: Link up on port (%6x)\n", - fc_host_port_id(lport->host)); - - mutex_lock(&lport->lp_mutex); if (!lport->link_up) { lport->link_up = 1; if (lport->state == LPORT_ST_RESET) fc_lport_enter_flogi(lport); } +} + +/** + * fc_linkup() - Handler for transport linkup events + * @lport: The local port whose link is up + */ +void fc_linkup(struct fc_lport *lport) +{ + printk(KERN_INFO "host%d: libfc: Link up on port (%6x)\n", + lport->host->host_no, fc_host_port_id(lport->host)); + + mutex_lock(&lport->lp_mutex); + __fc_linkup(lport); mutex_unlock(&lport->lp_mutex); } EXPORT_SYMBOL(fc_linkup); /** - * fc_linkdown() - Handler for transport linkdown events + * __fc_linkdown() - Handler for transport linkdown events * @lport: The lport whose link is down + * + * Locking: must be called with the lp_mutex held */ -void fc_linkdown(struct fc_lport *lport) +void __fc_linkdown(struct fc_lport *lport) { - mutex_lock(&lport->lp_mutex); - printk(KERN_INFO "libfc: Link down on port (%6x)\n", - fc_host_port_id(lport->host)); - if (lport->link_up) { lport->link_up = 0; fc_lport_enter_reset(lport); lport->tt.fcp_cleanup(lport); } +} + +/** + * fc_linkdown() - Handler for transport linkdown events + * @lport: The local port whose link is down + */ +void fc_linkdown(struct fc_lport *lport) +{ + printk(KERN_INFO "host%d: libfc: Link down on port (%6x)\n", + lport->host->host_no, fc_host_port_id(lport->host)); + + mutex_lock(&lport->lp_mutex); + __fc_linkdown(lport); mutex_unlock(&lport->lp_mutex); } EXPORT_SYMBOL(fc_linkdown); /** * fc_fabric_logoff() - Logout of the fabric - * @lport: fc_lport pointer to logoff the fabric + * @lport: The local port to logoff the fabric * * Return value: * 0 for success, -1 for failure @@ -540,8 +619,8 @@ int fc_fabric_logoff(struct fc_lport *lport) { lport->tt.disc_stop_final(lport); mutex_lock(&lport->lp_mutex); - if (lport->dns_rp) - lport->tt.rport_logoff(lport->dns_rp); + if (lport->dns_rdata) + lport->tt.rport_logoff(lport->dns_rdata); mutex_unlock(&lport->lp_mutex); lport->tt.rport_flush_queue(); mutex_lock(&lport->lp_mutex); @@ -553,11 +632,9 @@ int fc_fabric_logoff(struct fc_lport *lport) EXPORT_SYMBOL(fc_fabric_logoff); /** - * fc_lport_destroy() - unregister a fc_lport - * @lport: fc_lport pointer to unregister + * fc_lport_destroy() - Unregister a fc_lport + * @lport: The local port to unregister * - * Return value: - * None * Note: * exit routine for fc_lport instance * clean-up all the allocated memory @@ -580,13 +657,9 @@ int fc_lport_destroy(struct fc_lport *lport) EXPORT_SYMBOL(fc_lport_destroy); /** - * fc_set_mfs() - sets up the mfs for the corresponding fc_lport - * @lport: fc_lport pointer to unregister - * @mfs: the new mfs for fc_lport - * - * Set mfs for the given fc_lport to the new mfs. - * - * Return: 0 for success + * fc_set_mfs() - Set the maximum frame size for a local port + * @lport: The local port to set the MFS for + * @mfs: The new MFS */ int fc_set_mfs(struct fc_lport *lport, u32 mfs) { @@ -617,7 +690,7 @@ EXPORT_SYMBOL(fc_set_mfs); /** * fc_lport_disc_callback() - Callback for discovery events - * @lport: FC local port + * @lport: The local port receiving the event * @event: The discovery event */ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) @@ -627,8 +700,9 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) FC_LPORT_DBG(lport, "Discovery succeeded\n"); break; case DISC_EV_FAILED: - printk(KERN_ERR "libfc: Discovery failed for port (%6x)\n", - fc_host_port_id(lport->host)); + printk(KERN_ERR "host%d: libfc: " + "Discovery failed for port (%6x)\n", + lport->host->host_no, fc_host_port_id(lport->host)); mutex_lock(&lport->lp_mutex); fc_lport_enter_reset(lport); mutex_unlock(&lport->lp_mutex); @@ -641,7 +715,7 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) /** * fc_rport_enter_ready() - Enter the ready state and start discovery - * @lport: Fibre Channel local port that is ready + * @lport: The local port that is ready * * Locking Note: The lport lock is expected to be held before calling * this routine. @@ -652,22 +726,46 @@ static void fc_lport_enter_ready(struct fc_lport *lport) fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_READY); + if (lport->vport) + fc_vport_set_state(lport->vport, FC_VPORT_ACTIVE); + fc_vports_linkchange(lport); - if (!lport->ptp_rp) + if (!lport->ptp_rdata) lport->tt.disc_start(fc_lport_disc_callback, lport); } /** + * fc_lport_set_port_id() - set the local port Port ID + * @lport: The local port which will have its Port ID set. + * @port_id: The new port ID. + * @fp: The frame containing the incoming request, or NULL. + * + * Locking Note: The lport lock is expected to be held before calling + * this function. + */ +static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id, + struct fc_frame *fp) +{ + if (port_id) + printk(KERN_INFO "host%d: Assigned Port ID %6x\n", + lport->host->host_no, port_id); + + fc_host_port_id(lport->host) = port_id; + if (lport->tt.lport_set_port_id) + lport->tt.lport_set_port_id(lport, port_id, fp); +} + +/** * fc_lport_recv_flogi_req() - Receive a FLOGI request * @sp_in: The sequence the FLOGI is on - * @rx_fp: The frame the FLOGI is in - * @lport: The lport that recieved the request + * @rx_fp: The FLOGI frame + * @lport: The local port that recieved the request * * A received FLOGI request indicates a point-to-point connection. * Accept it with the common service parameters indicating our N port. * Set up to do a PLOGI if we have the higher-number WWPN. * - * Locking Note: The lport lock is exected to be held before calling + * Locking Note: The lport lock is expected to be held before calling * this function. */ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, @@ -695,8 +793,9 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, goto out; remote_wwpn = get_unaligned_be64(&flp->fl_wwpn); if (remote_wwpn == lport->wwpn) { - printk(KERN_WARNING "libfc: Received FLOGI from port " - "with same WWPN %llx\n", remote_wwpn); + printk(KERN_WARNING "host%d: libfc: Received FLOGI from port " + "with same WWPN %llx\n", + lport->host->host_no, remote_wwpn); goto out; } FC_LPORT_DBG(lport, "FLOGI from port WWPN %llx\n", remote_wwpn); @@ -715,7 +814,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, remote_fid = FC_LOCAL_PTP_FID_HI; } - fc_host_port_id(lport->host) = local_fid; + fc_lport_set_port_id(lport, local_fid, rx_fp); fp = fc_frame_alloc(lport, sizeof(*flp)); if (fp) { @@ -747,9 +846,9 @@ out: /** * fc_lport_recv_req() - The generic lport request handler - * @lport: The lport that received the request - * @sp: The sequence the request is on - * @fp: The frame the request is in + * @lport: The local port that received the request + * @sp: The sequence the request is on + * @fp: The request frame * * This function will see if the lport handles the request or * if an rport should handle the request. @@ -817,8 +916,8 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp, } /** - * fc_lport_reset() - Reset an lport - * @lport: The lport which should be reset + * fc_lport_reset() - Reset a local port + * @lport: The local port which should be reset * * Locking Note: This functions should not be called with the * lport lock held. @@ -834,29 +933,31 @@ int fc_lport_reset(struct fc_lport *lport) EXPORT_SYMBOL(fc_lport_reset); /** - * fc_lport_reset_locked() - Reset the local port - * @lport: Fibre Channel local port to be reset + * fc_lport_reset_locked() - Reset the local port w/ the lport lock held + * @lport: The local port to be reset * * Locking Note: The lport lock is expected to be held before calling * this routine. */ static void fc_lport_reset_locked(struct fc_lport *lport) { - if (lport->dns_rp) - lport->tt.rport_logoff(lport->dns_rp); + if (lport->dns_rdata) + lport->tt.rport_logoff(lport->dns_rdata); - lport->ptp_rp = NULL; + lport->ptp_rdata = NULL; lport->tt.disc_stop(lport); lport->tt.exch_mgr_reset(lport, 0, 0); fc_host_fabric_name(lport->host) = 0; - fc_host_port_id(lport->host) = 0; + + if (fc_host_port_id(lport->host)) + fc_lport_set_port_id(lport, 0, NULL); } /** * fc_lport_enter_reset() - Reset the local port - * @lport: Fibre Channel local port to be reset + * @lport: The local port to be reset * * Locking Note: The lport lock is expected to be held before calling * this routine. @@ -866,15 +967,22 @@ static void fc_lport_enter_reset(struct fc_lport *lport) FC_LPORT_DBG(lport, "Entered RESET state from %s state\n", fc_lport_state(lport)); + if (lport->vport) { + if (lport->link_up) + fc_vport_set_state(lport->vport, FC_VPORT_INITIALIZING); + else + fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN); + } fc_lport_state_enter(lport, LPORT_ST_RESET); + fc_vports_linkchange(lport); fc_lport_reset_locked(lport); if (lport->link_up) fc_lport_enter_flogi(lport); } /** - * fc_lport_enter_disabled() - disable the local port - * @lport: Fibre Channel local port to be reset + * fc_lport_enter_disabled() - Disable the local port + * @lport: The local port to be reset * * Locking Note: The lport lock is expected to be held before calling * this routine. @@ -885,13 +993,14 @@ static void fc_lport_enter_disabled(struct fc_lport *lport) fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_DISABLED); + fc_vports_linkchange(lport); fc_lport_reset_locked(lport); } /** * fc_lport_error() - Handler for any errors - * @lport: The fc_lport object - * @fp: The frame pointer + * @lport: The local port that the error was on + * @fp: The error code encoded in a frame pointer * * If the error was caused by a resource allocation failure * then wait for half a second and retry, otherwise retry @@ -922,8 +1031,11 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp) case LPORT_ST_DISABLED: case LPORT_ST_READY: case LPORT_ST_RESET: - case LPORT_ST_RPN_ID: + case LPORT_ST_RNN_ID: + case LPORT_ST_RSNN_NN: + case LPORT_ST_RSPN_ID: case LPORT_ST_RFT_ID: + case LPORT_ST_RFF_ID: case LPORT_ST_SCR: case LPORT_ST_DNS: case LPORT_ST_FLOGI: @@ -936,33 +1048,33 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp) } /** - * fc_lport_rft_id_resp() - Handle response to Register Fibre - * Channel Types by ID (RPN_ID) request - * @sp: current sequence in RPN_ID exchange - * @fp: response frame + * fc_lport_ns_resp() - Handle response to a name server + * registration exchange + * @sp: current sequence in exchange + * @fp: response frame * @lp_arg: Fibre Channel host port instance * * Locking Note: This function will be called without the lport lock - * held, but it will lock, call an _enter_* function or fc_lport_error + * held, but it will lock, call an _enter_* function or fc_lport_error() * and then unlock the lport. */ -static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp, - void *lp_arg) +static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) { struct fc_lport *lport = lp_arg; struct fc_frame_header *fh; struct fc_ct_hdr *ct; - FC_LPORT_DBG(lport, "Received a RFT_ID %s\n", fc_els_resp_type(fp)); + FC_LPORT_DBG(lport, "Received a ns %s\n", fc_els_resp_type(fp)); if (fp == ERR_PTR(-FC_EX_CLOSED)) return; mutex_lock(&lport->lp_mutex); - if (lport->state != LPORT_ST_RFT_ID) { - FC_LPORT_DBG(lport, "Received a RFT_ID response, but in state " - "%s\n", fc_lport_state(lport)); + if (lport->state < LPORT_ST_RNN_ID || lport->state > LPORT_ST_RFF_ID) { + FC_LPORT_DBG(lport, "Received a name server response, " + "but in state %s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out; @@ -980,63 +1092,28 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp, ct->ct_fs_type == FC_FST_DIR && ct->ct_fs_subtype == FC_NS_SUBTYPE && ntohs(ct->ct_cmd) == FC_FS_ACC) - fc_lport_enter_scr(lport); - else - fc_lport_error(lport, fp); -out: - fc_frame_free(fp); -err: - mutex_unlock(&lport->lp_mutex); -} - -/** - * fc_lport_rpn_id_resp() - Handle response to Register Port - * Name by ID (RPN_ID) request - * @sp: current sequence in RPN_ID exchange - * @fp: response frame - * @lp_arg: Fibre Channel host port instance - * - * Locking Note: This function will be called without the lport lock - * held, but it will lock, call an _enter_* function or fc_lport_error - * and then unlock the lport. - */ -static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, - void *lp_arg) -{ - struct fc_lport *lport = lp_arg; - struct fc_frame_header *fh; - struct fc_ct_hdr *ct; - - FC_LPORT_DBG(lport, "Received a RPN_ID %s\n", fc_els_resp_type(fp)); - - if (fp == ERR_PTR(-FC_EX_CLOSED)) - return; - - mutex_lock(&lport->lp_mutex); - - if (lport->state != LPORT_ST_RPN_ID) { - FC_LPORT_DBG(lport, "Received a RPN_ID response, but in state " - "%s\n", fc_lport_state(lport)); - if (IS_ERR(fp)) - goto err; - goto out; - } - - if (IS_ERR(fp)) { - fc_lport_error(lport, fp); - goto err; - } - - fh = fc_frame_header_get(fp); - ct = fc_frame_payload_get(fp, sizeof(*ct)); - if (fh && ct && fh->fh_type == FC_TYPE_CT && - ct->ct_fs_type == FC_FST_DIR && - ct->ct_fs_subtype == FC_NS_SUBTYPE && - ntohs(ct->ct_cmd) == FC_FS_ACC) - fc_lport_enter_rft_id(lport); + switch (lport->state) { + case LPORT_ST_RNN_ID: + fc_lport_enter_ns(lport, LPORT_ST_RSNN_NN); + break; + case LPORT_ST_RSNN_NN: + fc_lport_enter_ns(lport, LPORT_ST_RSPN_ID); + break; + case LPORT_ST_RSPN_ID: + fc_lport_enter_ns(lport, LPORT_ST_RFT_ID); + break; + case LPORT_ST_RFT_ID: + fc_lport_enter_ns(lport, LPORT_ST_RFF_ID); + break; + case LPORT_ST_RFF_ID: + fc_lport_enter_scr(lport); + break; + default: + /* should have already been caught by state checks */ + break; + } else fc_lport_error(lport, fp); - out: fc_frame_free(fp); err: @@ -1045,8 +1122,8 @@ err: /** * fc_lport_scr_resp() - Handle response to State Change Register (SCR) request - * @sp: current sequence in SCR exchange - * @fp: response frame + * @sp: current sequence in SCR exchange + * @fp: response frame * @lp_arg: Fibre Channel lport port instance that sent the registration request * * Locking Note: This function will be called without the lport lock @@ -1092,8 +1169,8 @@ err: } /** - * fc_lport_enter_scr() - Send a State Change Register (SCR) request - * @lport: Fibre Channel local port to register for state changes + * fc_lport_enter_scr() - Send a SCR (State Change Register) request + * @lport: The local port to register for state changes * * Locking Note: The lport lock is expected to be held before calling * this routine. @@ -1114,78 +1191,74 @@ static void fc_lport_enter_scr(struct fc_lport *lport) } if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR, - fc_lport_scr_resp, lport, lport->e_d_tov)) - fc_lport_error(lport, fp); + fc_lport_scr_resp, lport, + 2 * lport->r_a_tov)) + fc_lport_error(lport, NULL); } /** - * fc_lport_enter_rft_id() - Register FC4-types with the name server + * fc_lport_enter_ns() - register some object with the name server * @lport: Fibre Channel local port to register * * Locking Note: The lport lock is expected to be held before calling * this routine. */ -static void fc_lport_enter_rft_id(struct fc_lport *lport) +static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state) { struct fc_frame *fp; - struct fc_ns_fts *lps; - int i; + enum fc_ns_req cmd; + int size = sizeof(struct fc_ct_hdr); + size_t len; - FC_LPORT_DBG(lport, "Entered RFT_ID state from %s state\n", + FC_LPORT_DBG(lport, "Entered %s state from %s state\n", + fc_lport_state_names[state], fc_lport_state(lport)); - fc_lport_state_enter(lport, LPORT_ST_RFT_ID); - - lps = &lport->fcts; - i = sizeof(lps->ff_type_map) / sizeof(lps->ff_type_map[0]); - while (--i >= 0) - if (ntohl(lps->ff_type_map[i]) != 0) - break; - if (i < 0) { - /* nothing to register, move on to SCR */ - fc_lport_enter_scr(lport); - return; - } + fc_lport_state_enter(lport, state); - fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + - sizeof(struct fc_ns_rft)); - if (!fp) { - fc_lport_error(lport, fp); + switch (state) { + case LPORT_ST_RNN_ID: + cmd = FC_NS_RNN_ID; + size += sizeof(struct fc_ns_rn_id); + break; + case LPORT_ST_RSNN_NN: + len = strnlen(fc_host_symbolic_name(lport->host), 255); + /* if there is no symbolic name, skip to RFT_ID */ + if (!len) + return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID); + cmd = FC_NS_RSNN_NN; + size += sizeof(struct fc_ns_rsnn) + len; + break; + case LPORT_ST_RSPN_ID: + len = strnlen(fc_host_symbolic_name(lport->host), 255); + /* if there is no symbolic name, skip to RFT_ID */ + if (!len) + return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID); + cmd = FC_NS_RSPN_ID; + size += sizeof(struct fc_ns_rspn) + len; + break; + case LPORT_ST_RFT_ID: + cmd = FC_NS_RFT_ID; + size += sizeof(struct fc_ns_rft); + break; + case LPORT_ST_RFF_ID: + cmd = FC_NS_RFF_ID; + size += sizeof(struct fc_ns_rff_id); + break; + default: + fc_lport_error(lport, NULL); return; } - if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RFT_ID, - fc_lport_rft_id_resp, - lport, lport->e_d_tov)) - fc_lport_error(lport, fp); -} - -/** - * fc_rport_enter_rft_id() - Register port name with the name server - * @lport: Fibre Channel local port to register - * - * Locking Note: The lport lock is expected to be held before calling - * this routine. - */ -static void fc_lport_enter_rpn_id(struct fc_lport *lport) -{ - struct fc_frame *fp; - - FC_LPORT_DBG(lport, "Entered RPN_ID state from %s state\n", - fc_lport_state(lport)); - - fc_lport_state_enter(lport, LPORT_ST_RPN_ID); - - fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + - sizeof(struct fc_ns_rn_id)); + fp = fc_frame_alloc(lport, size); if (!fp) { fc_lport_error(lport, fp); return; } - if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RPN_ID, - fc_lport_rpn_id_resp, - lport, lport->e_d_tov)) + if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, cmd, + fc_lport_ns_resp, + lport, 3 * lport->r_a_tov)) fc_lport_error(lport, fp); } @@ -1194,8 +1267,8 @@ static struct fc_rport_operations fc_lport_rport_ops = { }; /** - * fc_rport_enter_dns() - Create a rport to the name server - * @lport: Fibre Channel local port requesting a rport for the name server + * fc_rport_enter_dns() - Create a fc_rport for the name server + * @lport: The local port requesting a remote port for the name server * * Locking Note: The lport lock is expected to be held before calling * this routine. @@ -1224,8 +1297,8 @@ err: } /** - * fc_lport_timeout() - Handler for the retry_work timer. - * @work: The work struct of the fc_lport + * fc_lport_timeout() - Handler for the retry_work timer + * @work: The work struct of the local port */ static void fc_lport_timeout(struct work_struct *work) { @@ -1237,21 +1310,25 @@ static void fc_lport_timeout(struct work_struct *work) switch (lport->state) { case LPORT_ST_DISABLED: + WARN_ON(1); + break; case LPORT_ST_READY: - case LPORT_ST_RESET: WARN_ON(1); break; + case LPORT_ST_RESET: + break; case LPORT_ST_FLOGI: fc_lport_enter_flogi(lport); break; case LPORT_ST_DNS: fc_lport_enter_dns(lport); break; - case LPORT_ST_RPN_ID: - fc_lport_enter_rpn_id(lport); - break; + case LPORT_ST_RNN_ID: + case LPORT_ST_RSNN_NN: + case LPORT_ST_RSPN_ID: case LPORT_ST_RFT_ID: - fc_lport_enter_rft_id(lport); + case LPORT_ST_RFF_ID: + fc_lport_enter_ns(lport, lport->state); break; case LPORT_ST_SCR: fc_lport_enter_scr(lport); @@ -1266,16 +1343,16 @@ static void fc_lport_timeout(struct work_struct *work) /** * fc_lport_logo_resp() - Handle response to LOGO request - * @sp: current sequence in LOGO exchange - * @fp: response frame - * @lp_arg: Fibre Channel lport port instance that sent the LOGO request + * @sp: The sequence that the LOGO was on + * @fp: The LOGO frame + * @lp_arg: The lport port that received the LOGO request * * Locking Note: This function will be called without the lport lock - * held, but it will lock, call an _enter_* function or fc_lport_error + * held, but it will lock, call an _enter_* function or fc_lport_error() * and then unlock the lport. */ -static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, - void *lp_arg) +void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) { struct fc_lport *lport = lp_arg; u8 op; @@ -1311,10 +1388,11 @@ out: err: mutex_unlock(&lport->lp_mutex); } +EXPORT_SYMBOL(fc_lport_logo_resp); /** * fc_rport_enter_logo() - Logout of the fabric - * @lport: Fibre Channel local port to be logged out + * @lport: The local port to be logged out * * Locking Note: The lport lock is expected to be held before calling * this routine. @@ -1328,6 +1406,7 @@ static void fc_lport_enter_logo(struct fc_lport *lport) fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_LOGO); + fc_vports_linkchange(lport); fp = fc_frame_alloc(lport, sizeof(*logo)); if (!fp) { @@ -1336,22 +1415,23 @@ static void fc_lport_enter_logo(struct fc_lport *lport) } if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO, - fc_lport_logo_resp, lport, lport->e_d_tov)) - fc_lport_error(lport, fp); + fc_lport_logo_resp, lport, + 2 * lport->r_a_tov)) + fc_lport_error(lport, NULL); } /** * fc_lport_flogi_resp() - Handle response to FLOGI request - * @sp: current sequence in FLOGI exchange - * @fp: response frame - * @lp_arg: Fibre Channel lport port instance that sent the FLOGI request + * @sp: The sequence that the FLOGI was on + * @fp: The FLOGI response frame + * @lp_arg: The lport port that received the FLOGI response * * Locking Note: This function will be called without the lport lock - * held, but it will lock, call an _enter_* function or fc_lport_error + * held, but it will lock, call an _enter_* function or fc_lport_error() * and then unlock the lport. */ -static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, - void *lp_arg) +void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) { struct fc_lport *lport = lp_arg; struct fc_frame_header *fh; @@ -1385,11 +1465,6 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, fh = fc_frame_header_get(fp); did = ntoh24(fh->fh_d_id); if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) { - - printk(KERN_INFO "libfc: Assigned FID (%6x) in FLOGI response\n", - did); - fc_host_port_id(lport->host) = did; - flp = fc_frame_payload_get(fp, sizeof(*flp)); if (flp) { mfs = ntohs(flp->fl_csp.sp_bb_data) & @@ -1402,12 +1477,18 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); if (csp_flags & FC_SP_FT_EDTR) e_d_tov /= 1000000; + + lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC); + if ((csp_flags & FC_SP_FT_FPORT) == 0) { if (e_d_tov > lport->e_d_tov) lport->e_d_tov = e_d_tov; lport->r_a_tov = 2 * e_d_tov; - printk(KERN_INFO "libfc: Port (%6x) entered " - "point to point mode\n", did); + fc_lport_set_port_id(lport, did, fp); + printk(KERN_INFO "host%d: libfc: " + "Port (%6x) entered " + "point-to-point mode\n", + lport->host->host_no, did); fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id), get_unaligned_be64( &flp->fl_wwpn), @@ -1418,6 +1499,7 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, lport->r_a_tov = r_a_tov; fc_host_fabric_name(lport->host) = get_unaligned_be64(&flp->fl_wwnn); + fc_lport_set_port_id(lport, did, fp); fc_lport_enter_dns(lport); } } @@ -1430,6 +1512,7 @@ out: err: mutex_unlock(&lport->lp_mutex); } +EXPORT_SYMBOL(fc_lport_flogi_resp); /** * fc_rport_enter_flogi() - Send a FLOGI request to the fabric manager @@ -1451,12 +1534,18 @@ void fc_lport_enter_flogi(struct fc_lport *lport) if (!fp) return fc_lport_error(lport, fp); - if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_FLOGI, - fc_lport_flogi_resp, lport, lport->e_d_tov)) - fc_lport_error(lport, fp); + if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, + lport->vport ? ELS_FDISC : ELS_FLOGI, + fc_lport_flogi_resp, lport, + lport->vport ? 2 * lport->r_a_tov : + lport->e_d_tov)) + fc_lport_error(lport, NULL); } -/* Configure a fc_lport */ +/** + * fc_lport_config() - Configure a fc_lport + * @lport: The local port to be configured + */ int fc_lport_config(struct fc_lport *lport) { INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout); @@ -1471,6 +1560,10 @@ int fc_lport_config(struct fc_lport *lport) } EXPORT_SYMBOL(fc_lport_config); +/** + * fc_lport_init() - Initialize the lport layer for a local port + * @lport: The local port to initialize the exchange layer for + */ int fc_lport_init(struct fc_lport *lport) { if (!lport->tt.lport_recv) @@ -1500,7 +1593,253 @@ int fc_lport_init(struct fc_lport *lport) if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT) fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT; - INIT_LIST_HEAD(&lport->ema_list); return 0; } EXPORT_SYMBOL(fc_lport_init); + +/** + * fc_lport_bsg_resp() - The common response handler for FC Passthrough requests + * @sp: The sequence for the FC Passthrough response + * @fp: The response frame + * @info_arg: The BSG info that the response is for + */ +static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp, + void *info_arg) +{ + struct fc_bsg_info *info = info_arg; + struct fc_bsg_job *job = info->job; + struct fc_lport *lport = info->lport; + struct fc_frame_header *fh; + size_t len; + void *buf; + + if (IS_ERR(fp)) { + job->reply->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ? + -ECONNABORTED : -ETIMEDOUT; + job->reply_len = sizeof(uint32_t); + job->state_flags |= FC_RQST_STATE_DONE; + job->job_done(job); + kfree(info); + return; + } + + mutex_lock(&lport->lp_mutex); + fh = fc_frame_header_get(fp); + len = fr_len(fp) - sizeof(*fh); + buf = fc_frame_payload_get(fp, 0); + + if (fr_sof(fp) == FC_SOF_I3 && !ntohs(fh->fh_seq_cnt)) { + /* Get the response code from the first frame payload */ + unsigned short cmd = (info->rsp_code == FC_FS_ACC) ? + ntohs(((struct fc_ct_hdr *)buf)->ct_cmd) : + (unsigned short)fc_frame_payload_op(fp); + + /* Save the reply status of the job */ + job->reply->reply_data.ctels_reply.status = + (cmd == info->rsp_code) ? + FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT; + } + + job->reply->reply_payload_rcv_len += + fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents, + &info->offset, KM_BIO_SRC_IRQ, NULL); + + if (fr_eof(fp) == FC_EOF_T && + (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == + (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) { + if (job->reply->reply_payload_rcv_len > + job->reply_payload.payload_len) + job->reply->reply_payload_rcv_len = + job->reply_payload.payload_len; + job->reply->result = 0; + job->state_flags |= FC_RQST_STATE_DONE; + job->job_done(job); + kfree(info); + } + fc_frame_free(fp); + mutex_unlock(&lport->lp_mutex); +} + +/** + * fc_lport_els_request() - Send ELS passthrough request + * @job: The BSG Passthrough job + * @lport: The local port sending the request + * @did: The destination port id + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static int fc_lport_els_request(struct fc_bsg_job *job, + struct fc_lport *lport, + u32 did, u32 tov) +{ + struct fc_bsg_info *info; + struct fc_frame *fp; + struct fc_frame_header *fh; + char *pp; + int len; + + fp = fc_frame_alloc(lport, job->request_payload.payload_len); + if (!fp) + return -ENOMEM; + + len = job->request_payload.payload_len; + pp = fc_frame_payload_get(fp, len); + + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, + pp, len); + + fh = fc_frame_header_get(fp); + fh->fh_r_ctl = FC_RCTL_ELS_REQ; + hton24(fh->fh_d_id, did); + hton24(fh->fh_s_id, fc_host_port_id(lport->host)); + fh->fh_type = FC_TYPE_ELS; + hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | + FC_FC_END_SEQ | FC_FC_SEQ_INIT); + fh->fh_cs_ctl = 0; + fh->fh_df_ctl = 0; + fh->fh_parm_offset = 0; + + info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL); + if (!info) { + fc_frame_free(fp); + return -ENOMEM; + } + + info->job = job; + info->lport = lport; + info->rsp_code = ELS_LS_ACC; + info->nents = job->reply_payload.sg_cnt; + info->sg = job->reply_payload.sg_list; + + if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, + NULL, info, tov)) + return -ECOMM; + return 0; +} + +/** + * fc_lport_ct_request() - Send CT Passthrough request + * @job: The BSG Passthrough job + * @lport: The local port sending the request + * @did: The destination FC-ID + * @tov: The timeout period to wait for the response + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static int fc_lport_ct_request(struct fc_bsg_job *job, + struct fc_lport *lport, u32 did, u32 tov) +{ + struct fc_bsg_info *info; + struct fc_frame *fp; + struct fc_frame_header *fh; + struct fc_ct_req *ct; + size_t len; + + fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + + job->request_payload.payload_len); + if (!fp) + return -ENOMEM; + + len = job->request_payload.payload_len; + ct = fc_frame_payload_get(fp, len); + + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, + ct, len); + + fh = fc_frame_header_get(fp); + fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL; + hton24(fh->fh_d_id, did); + hton24(fh->fh_s_id, fc_host_port_id(lport->host)); + fh->fh_type = FC_TYPE_CT; + hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | + FC_FC_END_SEQ | FC_FC_SEQ_INIT); + fh->fh_cs_ctl = 0; + fh->fh_df_ctl = 0; + fh->fh_parm_offset = 0; + + info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL); + if (!info) { + fc_frame_free(fp); + return -ENOMEM; + } + + info->job = job; + info->lport = lport; + info->rsp_code = FC_FS_ACC; + info->nents = job->reply_payload.sg_cnt; + info->sg = job->reply_payload.sg_list; + + if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, + NULL, info, tov)) + return -ECOMM; + return 0; +} + +/** + * fc_lport_bsg_request() - The common entry point for sending + * FC Passthrough requests + * @job: The BSG passthrough job + */ +int fc_lport_bsg_request(struct fc_bsg_job *job) +{ + struct request *rsp = job->req->next_rq; + struct Scsi_Host *shost = job->shost; + struct fc_lport *lport = shost_priv(shost); + struct fc_rport *rport; + struct fc_rport_priv *rdata; + int rc = -EINVAL; + u32 did; + + job->reply->reply_payload_rcv_len = 0; + rsp->resid_len = job->reply_payload.payload_len; + + mutex_lock(&lport->lp_mutex); + + switch (job->request->msgcode) { + case FC_BSG_RPT_ELS: + rport = job->rport; + if (!rport) + break; + + rdata = rport->dd_data; + rc = fc_lport_els_request(job, lport, rport->port_id, + rdata->e_d_tov); + break; + + case FC_BSG_RPT_CT: + rport = job->rport; + if (!rport) + break; + + rdata = rport->dd_data; + rc = fc_lport_ct_request(job, lport, rport->port_id, + rdata->e_d_tov); + break; + + case FC_BSG_HST_CT: + did = ntoh24(job->request->rqst_data.h_ct.port_id); + if (did == FC_FID_DIR_SERV) + rdata = lport->dns_rdata; + else + rdata = lport->tt.rport_lookup(lport, did); + + if (!rdata) + break; + + rc = fc_lport_ct_request(job, lport, did, rdata->e_d_tov); + break; + + case FC_BSG_HST_ELS_NOLOGIN: + did = ntoh24(job->request->rqst_data.h_els.port_id); + rc = fc_lport_els_request(job, lport, did, lport->e_d_tov); + break; + } + + mutex_unlock(&lport->lp_mutex); + return rc; +} +EXPORT_SYMBOL(fc_lport_bsg_request); diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c new file mode 100644 index 000000000000..c68f6c7341c2 --- /dev/null +++ b/drivers/scsi/libfc/fc_npiv.c @@ -0,0 +1,161 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +/* + * NPIV VN_Port helper functions for libfc + */ + +#include <scsi/libfc.h> + +/** + * fc_vport_create() - Create a new NPIV vport instance + * @vport: fc_vport structure from scsi_transport_fc + * @privsize: driver private data size to allocate along with the Scsi_Host + */ + +struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize) +{ + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + struct fc_lport *vn_port; + + vn_port = libfc_host_alloc(shost->hostt, privsize); + if (!vn_port) + goto err_out; + if (fc_exch_mgr_list_clone(n_port, vn_port)) + goto err_put; + + vn_port->vport = vport; + vport->dd_data = vn_port; + + mutex_lock(&n_port->lp_mutex); + list_add_tail(&vn_port->list, &n_port->vports); + mutex_unlock(&n_port->lp_mutex); + + return vn_port; + +err_put: + scsi_host_put(vn_port->host); +err_out: + return NULL; +} +EXPORT_SYMBOL(libfc_vport_create); + +/** + * fc_vport_id_lookup() - find NPIV lport that matches a given fabric ID + * @n_port: Top level N_Port which may have multiple NPIV VN_Ports + * @port_id: Fabric ID to find a match for + * + * Returns: matching lport pointer or NULL if there is no match + */ +struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id) +{ + struct fc_lport *lport = NULL; + struct fc_lport *vn_port; + + if (fc_host_port_id(n_port->host) == port_id) + return n_port; + + mutex_lock(&n_port->lp_mutex); + list_for_each_entry(vn_port, &n_port->vports, list) { + if (fc_host_port_id(vn_port->host) == port_id) { + lport = vn_port; + break; + } + } + mutex_unlock(&n_port->lp_mutex); + + return lport; +} + +/* + * When setting the link state of vports during an lport state change, it's + * necessary to hold the lp_mutex of both the N_Port and the VN_Port. + * This tells the lockdep engine to treat the nested locking of the VN_Port + * as a different lock class. + */ +enum libfc_lport_mutex_class { + LPORT_MUTEX_NORMAL = 0, + LPORT_MUTEX_VN_PORT = 1, +}; + +/** + * __fc_vport_setlink() - update link and status on a VN_Port + * @n_port: parent N_Port + * @vn_port: VN_Port to update + * + * Locking: must be called with both the N_Port and VN_Port lp_mutex held + */ +static void __fc_vport_setlink(struct fc_lport *n_port, + struct fc_lport *vn_port) +{ + struct fc_vport *vport = vn_port->vport; + + if (vn_port->state == LPORT_ST_DISABLED) + return; + + if (n_port->state == LPORT_ST_READY) { + if (n_port->npiv_enabled) { + fc_vport_set_state(vport, FC_VPORT_INITIALIZING); + __fc_linkup(vn_port); + } else { + fc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP); + __fc_linkdown(vn_port); + } + } else { + fc_vport_set_state(vport, FC_VPORT_LINKDOWN); + __fc_linkdown(vn_port); + } +} + +/** + * fc_vport_setlink() - update link and status on a VN_Port + * @vn_port: virtual port to update + */ +void fc_vport_setlink(struct fc_lport *vn_port) +{ + struct fc_vport *vport = vn_port->vport; + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + + mutex_lock(&n_port->lp_mutex); + mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT); + __fc_vport_setlink(n_port, vn_port); + mutex_unlock(&vn_port->lp_mutex); + mutex_unlock(&n_port->lp_mutex); +} +EXPORT_SYMBOL(fc_vport_setlink); + +/** + * fc_vports_linkchange() - change the link state of all vports + * @n_port: Parent N_Port that has changed state + * + * Locking: called with the n_port lp_mutex held + */ +void fc_vports_linkchange(struct fc_lport *n_port) +{ + struct fc_lport *vn_port; + + list_for_each_entry(vn_port, &n_port->vports, list) { + mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT); + __fc_vport_setlink(n_port, vn_port); + mutex_unlock(&vn_port->lp_mutex); + } +} + diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 03ea6748e7ee..35ca0e72df46 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -55,6 +55,8 @@ #include <scsi/libfc.h> #include <scsi/fc_encode.h> +#include "fc_libfc.h" + struct workqueue_struct *rport_event_queue; static void fc_rport_enter_plogi(struct fc_rport_priv *); @@ -86,12 +88,13 @@ static const char *fc_rport_state_names[] = { [RPORT_ST_LOGO] = "LOGO", [RPORT_ST_ADISC] = "ADISC", [RPORT_ST_DELETE] = "Delete", + [RPORT_ST_RESTART] = "Restart", }; /** - * fc_rport_lookup() - lookup a remote port by port_id - * @lport: Fibre Channel host port instance - * @port_id: remote port port_id to match + * fc_rport_lookup() - Lookup a remote port by port_id + * @lport: The local port to lookup the remote port on + * @port_id: The remote port ID to look up */ static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport, u32 port_id) @@ -99,16 +102,17 @@ static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport, struct fc_rport_priv *rdata; list_for_each_entry(rdata, &lport->disc.rports, peers) - if (rdata->ids.port_id == port_id && - rdata->rp_state != RPORT_ST_DELETE) + if (rdata->ids.port_id == port_id) return rdata; return NULL; } /** * fc_rport_create() - Create a new remote port - * @lport: The local port that the new remote port is for - * @port_id: The port ID for the new remote port + * @lport: The local port this remote port will be associated with + * @ids: The identifiers for the new remote port + * + * The remote port will start in the INIT state. * * Locking note: must be called with the disc_mutex held. */ @@ -147,8 +151,8 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport, } /** - * fc_rport_destroy() - free a remote port after last reference is released. - * @kref: pointer to kref inside struct fc_rport_priv + * fc_rport_destroy() - Free a remote port after last reference is released + * @kref: The remote port's kref */ static void fc_rport_destroy(struct kref *kref) { @@ -159,8 +163,8 @@ static void fc_rport_destroy(struct kref *kref) } /** - * fc_rport_state() - return a string for the state the rport is in - * @rdata: remote port private data + * fc_rport_state() - Return a string identifying the remote port's state + * @rdata: The remote port */ static const char *fc_rport_state(struct fc_rport_priv *rdata) { @@ -173,9 +177,9 @@ static const char *fc_rport_state(struct fc_rport_priv *rdata) } /** - * fc_set_rport_loss_tmo() - Set the remote port loss timeout in seconds. - * @rport: Pointer to Fibre Channel remote port structure - * @timeout: timeout in seconds + * fc_set_rport_loss_tmo() - Set the remote port loss timeout + * @rport: The remote port that gets a new timeout value + * @timeout: The new timeout value (in seconds) */ void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) { @@ -187,9 +191,11 @@ void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) EXPORT_SYMBOL(fc_set_rport_loss_tmo); /** - * fc_plogi_get_maxframe() - Get max payload from the common service parameters - * @flp: FLOGI payload structure - * @maxval: upper limit, may be less than what is in the service parameters + * fc_plogi_get_maxframe() - Get the maximum payload from the common service + * parameters in a FLOGI frame + * @flp: The FLOGI payload + * @maxval: The maximum frame size upper limit; this may be less than what + * is in the service parameters */ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp, unsigned int maxval) @@ -210,9 +216,9 @@ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp, } /** - * fc_rport_state_enter() - Change the rport's state - * @rdata: The rport whose state should change - * @new: The new state of the rport + * fc_rport_state_enter() - Change the state of a remote port + * @rdata: The remote port whose state should change + * @new: The new state * * Locking Note: Called with the rport lock held */ @@ -224,17 +230,22 @@ static void fc_rport_state_enter(struct fc_rport_priv *rdata, rdata->rp_state = new; } +/** + * fc_rport_work() - Handler for remote port events in the rport_event_queue + * @work: Handle to the remote port being dequeued + */ static void fc_rport_work(struct work_struct *work) { u32 port_id; struct fc_rport_priv *rdata = container_of(work, struct fc_rport_priv, event_work); - struct fc_rport_libfc_priv *rp; + struct fc_rport_libfc_priv *rpriv; enum fc_rport_event event; struct fc_lport *lport = rdata->local_port; struct fc_rport_operations *rport_ops; struct fc_rport_identifiers ids; struct fc_rport *rport; + int restart = 0; mutex_lock(&rdata->rp_mutex); event = rdata->event; @@ -265,12 +276,12 @@ static void fc_rport_work(struct work_struct *work) rport->maxframe_size = rdata->maxframe_size; rport->supported_classes = rdata->supported_classes; - rp = rport->dd_data; - rp->local_port = lport; - rp->rp_state = rdata->rp_state; - rp->flags = rdata->flags; - rp->e_d_tov = rdata->e_d_tov; - rp->r_a_tov = rdata->r_a_tov; + rpriv = rport->dd_data; + rpriv->local_port = lport; + rpriv->rp_state = rdata->rp_state; + rpriv->flags = rdata->flags; + rpriv->e_d_tov = rdata->e_d_tov; + rpriv->r_a_tov = rdata->r_a_tov; mutex_unlock(&rdata->rp_mutex); if (rport_ops && rport_ops->event_callback) { @@ -287,8 +298,19 @@ static void fc_rport_work(struct work_struct *work) mutex_unlock(&rdata->rp_mutex); if (port_id != FC_FID_DIR_SERV) { + /* + * We must drop rp_mutex before taking disc_mutex. + * Re-evaluate state to allow for restart. + * A transition to RESTART state must only happen + * while disc_mutex is held and rdata is on the list. + */ mutex_lock(&lport->disc.disc_mutex); - list_del(&rdata->peers); + mutex_lock(&rdata->rp_mutex); + if (rdata->rp_state == RPORT_ST_RESTART) + restart = 1; + else + list_del(&rdata->peers); + mutex_unlock(&rdata->rp_mutex); mutex_unlock(&lport->disc.disc_mutex); } @@ -305,14 +327,20 @@ static void fc_rport_work(struct work_struct *work) lport->tt.exch_mgr_reset(lport, port_id, 0); if (rport) { - rp = rport->dd_data; - rp->rp_state = RPORT_ST_DELETE; + rpriv = rport->dd_data; + rpriv->rp_state = RPORT_ST_DELETE; mutex_lock(&rdata->rp_mutex); rdata->rport = NULL; mutex_unlock(&rdata->rp_mutex); fc_remote_port_delete(rport); } - kref_put(&rdata->kref, lport->tt.rport_destroy); + if (restart) { + mutex_lock(&rdata->rp_mutex); + FC_RPORT_DBG(rdata, "work restart\n"); + fc_rport_enter_plogi(rdata); + mutex_unlock(&rdata->rp_mutex); + } else + kref_put(&rdata->kref, lport->tt.rport_destroy); break; default: @@ -323,7 +351,7 @@ static void fc_rport_work(struct work_struct *work) /** * fc_rport_login() - Start the remote port login state machine - * @rdata: private remote port + * @rdata: The remote port to be logged in to * * Locking Note: Called without the rport lock held. This * function will hold the rport lock, call an _enter_* @@ -342,6 +370,12 @@ int fc_rport_login(struct fc_rport_priv *rdata) FC_RPORT_DBG(rdata, "ADISC port\n"); fc_rport_enter_adisc(rdata); break; + case RPORT_ST_RESTART: + break; + case RPORT_ST_DELETE: + FC_RPORT_DBG(rdata, "Restart deleted port\n"); + fc_rport_state_enter(rdata, RPORT_ST_RESTART); + break; default: FC_RPORT_DBG(rdata, "Login to port\n"); fc_rport_enter_plogi(rdata); @@ -353,9 +387,9 @@ int fc_rport_login(struct fc_rport_priv *rdata) } /** - * fc_rport_enter_delete() - schedule a remote port to be deleted. - * @rdata: private remote port - * @event: event to report as the reason for deletion + * fc_rport_enter_delete() - Schedule a remote port to be deleted + * @rdata: The remote port to be deleted + * @event: The event to report as the reason for deletion * * Locking Note: Called with the rport lock held. * @@ -382,8 +416,8 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata, } /** - * fc_rport_logoff() - Logoff and remove an rport - * @rdata: private remote port + * fc_rport_logoff() - Logoff and remove a remote port + * @rdata: The remote port to be logged off of * * Locking Note: Called without the rport lock held. This * function will hold the rport lock, call an _enter_* @@ -397,26 +431,27 @@ int fc_rport_logoff(struct fc_rport_priv *rdata) if (rdata->rp_state == RPORT_ST_DELETE) { FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n"); - mutex_unlock(&rdata->rp_mutex); goto out; } - fc_rport_enter_logo(rdata); + if (rdata->rp_state == RPORT_ST_RESTART) + FC_RPORT_DBG(rdata, "Port in Restart state, deleting\n"); + else + fc_rport_enter_logo(rdata); /* * Change the state to Delete so that we discard * the response. */ fc_rport_enter_delete(rdata, RPORT_EV_STOP); - mutex_unlock(&rdata->rp_mutex); - out: + mutex_unlock(&rdata->rp_mutex); return 0; } /** - * fc_rport_enter_ready() - The rport is ready - * @rdata: private remote port + * fc_rport_enter_ready() - Transition to the RPORT_ST_READY state + * @rdata: The remote port that is ready * * Locking Note: The rport lock is expected to be held before calling * this routine. @@ -433,8 +468,8 @@ static void fc_rport_enter_ready(struct fc_rport_priv *rdata) } /** - * fc_rport_timeout() - Handler for the retry_work timer. - * @work: The work struct of the fc_rport_priv + * fc_rport_timeout() - Handler for the retry_work timer + * @work: Handle to the remote port that has timed out * * Locking Note: Called without the rport lock held. This * function will hold the rport lock, call an _enter_* @@ -466,6 +501,7 @@ static void fc_rport_timeout(struct work_struct *work) case RPORT_ST_READY: case RPORT_ST_INIT: case RPORT_ST_DELETE: + case RPORT_ST_RESTART: break; } @@ -474,8 +510,8 @@ static void fc_rport_timeout(struct work_struct *work) /** * fc_rport_error() - Error handler, called once retries have been exhausted - * @rdata: private remote port - * @fp: The frame pointer + * @rdata: The remote port the error is happened on + * @fp: The error code encapsulated in a frame pointer * * Locking Note: The rport lock is expected to be held before * calling this routine @@ -499,6 +535,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp) fc_rport_enter_logo(rdata); break; case RPORT_ST_DELETE: + case RPORT_ST_RESTART: case RPORT_ST_READY: case RPORT_ST_INIT: break; @@ -506,9 +543,9 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp) } /** - * fc_rport_error_retry() - Error handler when retries are desired - * @rdata: private remote port data - * @fp: The frame pointer + * fc_rport_error_retry() - Handler for remote port state retries + * @rdata: The remote port whose state is to be retried + * @fp: The error code encapsulated in a frame pointer * * If the error was an exchange timeout retry immediately, * otherwise wait for E_D_TOV. @@ -540,10 +577,10 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata, } /** - * fc_rport_plogi_recv_resp() - Handle incoming ELS PLOGI response - * @sp: current sequence in the PLOGI exchange - * @fp: response frame - * @rdata_arg: private remote port data + * fc_rport_plogi_recv_resp() - Handler for ELS PLOGI responses + * @sp: The sequence the PLOGI is on + * @fp: The PLOGI response frame + * @rdata_arg: The remote port that sent the PLOGI response * * Locking Note: This function will be called without the rport lock * held, but it will lock, call an _enter_* function or fc_rport_error @@ -606,8 +643,8 @@ err: } /** - * fc_rport_enter_plogi() - Send Port Login (PLOGI) request to peer - * @rdata: private remote port data + * fc_rport_enter_plogi() - Send Port Login (PLOGI) request + * @rdata: The remote port to send a PLOGI to * * Locking Note: The rport lock is expected to be held before calling * this routine. @@ -631,17 +668,18 @@ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata) rdata->e_d_tov = lport->e_d_tov; if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI, - fc_rport_plogi_resp, rdata, lport->e_d_tov)) - fc_rport_error_retry(rdata, fp); + fc_rport_plogi_resp, rdata, + 2 * lport->r_a_tov)) + fc_rport_error_retry(rdata, NULL); else kref_get(&rdata->kref); } /** * fc_rport_prli_resp() - Process Login (PRLI) response handler - * @sp: current sequence in the PRLI exchange - * @fp: response frame - * @rdata_arg: private remote port data + * @sp: The sequence the PRLI response was on + * @fp: The PRLI response frame + * @rdata_arg: The remote port that sent the PRLI response * * Locking Note: This function will be called without the rport lock * held, but it will lock, call an _enter_* function or fc_rport_error @@ -710,10 +748,10 @@ err: } /** - * fc_rport_logo_resp() - Logout (LOGO) response handler - * @sp: current sequence in the LOGO exchange - * @fp: response frame - * @rdata_arg: private remote port data + * fc_rport_logo_resp() - Handler for logout (LOGO) responses + * @sp: The sequence the LOGO was on + * @fp: The LOGO response frame + * @rdata_arg: The remote port that sent the LOGO response * * Locking Note: This function will be called without the rport lock * held, but it will lock, call an _enter_* function or fc_rport_error @@ -756,8 +794,8 @@ err: } /** - * fc_rport_enter_prli() - Send Process Login (PRLI) request to peer - * @rdata: private remote port data + * fc_rport_enter_prli() - Send Process Login (PRLI) request + * @rdata: The remote port to send the PRLI request to * * Locking Note: The rport lock is expected to be held before calling * this routine. @@ -792,17 +830,18 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata) } if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI, - fc_rport_prli_resp, rdata, lport->e_d_tov)) - fc_rport_error_retry(rdata, fp); + fc_rport_prli_resp, rdata, + 2 * lport->r_a_tov)) + fc_rport_error_retry(rdata, NULL); else kref_get(&rdata->kref); } /** - * fc_rport_els_rtv_resp() - Request Timeout Value response handler - * @sp: current sequence in the RTV exchange - * @fp: response frame - * @rdata_arg: private remote port data + * fc_rport_els_rtv_resp() - Handler for Request Timeout Value (RTV) responses + * @sp: The sequence the RTV was on + * @fp: The RTV response frame + * @rdata_arg: The remote port that sent the RTV response * * Many targets don't seem to support this. * @@ -865,8 +904,8 @@ err: } /** - * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request to peer - * @rdata: private remote port data + * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request + * @rdata: The remote port to send the RTV request to * * Locking Note: The rport lock is expected to be held before calling * this routine. @@ -888,15 +927,16 @@ static void fc_rport_enter_rtv(struct fc_rport_priv *rdata) } if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV, - fc_rport_rtv_resp, rdata, lport->e_d_tov)) - fc_rport_error_retry(rdata, fp); + fc_rport_rtv_resp, rdata, + 2 * lport->r_a_tov)) + fc_rport_error_retry(rdata, NULL); else kref_get(&rdata->kref); } /** - * fc_rport_enter_logo() - Send Logout (LOGO) request to peer - * @rdata: private remote port data + * fc_rport_enter_logo() - Send a logout (LOGO) request + * @rdata: The remote port to send the LOGO request to * * Locking Note: The rport lock is expected to be held before calling * this routine. @@ -918,24 +958,25 @@ static void fc_rport_enter_logo(struct fc_rport_priv *rdata) } if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO, - fc_rport_logo_resp, rdata, lport->e_d_tov)) - fc_rport_error_retry(rdata, fp); + fc_rport_logo_resp, rdata, + 2 * lport->r_a_tov)) + fc_rport_error_retry(rdata, NULL); else kref_get(&rdata->kref); } /** - * fc_rport_els_adisc_resp() - Address Discovery response handler - * @sp: current sequence in the ADISC exchange - * @fp: response frame - * @rdata_arg: remote port private. + * fc_rport_els_adisc_resp() - Handler for Address Discovery (ADISC) responses + * @sp: The sequence the ADISC response was on + * @fp: The ADISC response frame + * @rdata_arg: The remote port that sent the ADISC response * * Locking Note: This function will be called without the rport lock * held, but it will lock, call an _enter_* function or fc_rport_error * and then unlock the rport. */ static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp, - void *rdata_arg) + void *rdata_arg) { struct fc_rport_priv *rdata = rdata_arg; struct fc_els_adisc *adisc; @@ -983,8 +1024,8 @@ err: } /** - * fc_rport_enter_adisc() - Send Address Discover (ADISC) request to peer - * @rdata: remote port private data + * fc_rport_enter_adisc() - Send Address Discover (ADISC) request + * @rdata: The remote port to send the ADISC request to * * Locking Note: The rport lock is expected to be held before calling * this routine. @@ -1005,17 +1046,18 @@ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata) return; } if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC, - fc_rport_adisc_resp, rdata, lport->e_d_tov)) - fc_rport_error_retry(rdata, fp); + fc_rport_adisc_resp, rdata, + 2 * lport->r_a_tov)) + fc_rport_error_retry(rdata, NULL); else kref_get(&rdata->kref); } /** - * fc_rport_recv_adisc_req() - Handle incoming Address Discovery (ADISC) Request - * @rdata: remote port private - * @sp: current sequence in the ADISC exchange - * @in_fp: ADISC request frame + * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests + * @rdata: The remote port that sent the ADISC request + * @sp: The sequence the ADISC request was on + * @in_fp: The ADISC request frame * * Locking Note: Called with the lport and rport locks held. */ @@ -1056,10 +1098,82 @@ drop: } /** - * fc_rport_recv_els_req() - handle a validated ELS request. - * @lport: Fibre Channel local port - * @sp: current sequence in the PLOGI exchange - * @fp: response frame + * fc_rport_recv_rls_req() - Handle received Read Link Status request + * @rdata: The remote port that sent the RLS request + * @sp: The sequence that the RLS was on + * @rx_fp: The PRLI request frame + * + * Locking Note: The rport lock is expected to be held before calling + * this function. + */ +static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata, + struct fc_seq *sp, struct fc_frame *rx_fp) + +{ + struct fc_lport *lport = rdata->local_port; + struct fc_frame *fp; + struct fc_exch *ep = fc_seq_exch(sp); + struct fc_els_rls *rls; + struct fc_els_rls_resp *rsp; + struct fc_els_lesb *lesb; + struct fc_seq_els_data rjt_data; + struct fc_host_statistics *hst; + u32 f_ctl; + + FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n", + fc_rport_state(rdata)); + + rls = fc_frame_payload_get(rx_fp, sizeof(*rls)); + if (!rls) { + rjt_data.reason = ELS_RJT_PROT; + rjt_data.explan = ELS_EXPL_INV_LEN; + goto out_rjt; + } + + fp = fc_frame_alloc(lport, sizeof(*rsp)); + if (!fp) { + rjt_data.reason = ELS_RJT_UNAB; + rjt_data.explan = ELS_EXPL_INSUF_RES; + goto out_rjt; + } + + rsp = fc_frame_payload_get(fp, sizeof(*rsp)); + memset(rsp, 0, sizeof(*rsp)); + rsp->rls_cmd = ELS_LS_ACC; + lesb = &rsp->rls_lesb; + if (lport->tt.get_lesb) { + /* get LESB from LLD if it supports it */ + lport->tt.get_lesb(lport, lesb); + } else { + fc_get_host_stats(lport->host); + hst = &lport->host_stats; + lesb->lesb_link_fail = htonl(hst->link_failure_count); + lesb->lesb_sync_loss = htonl(hst->loss_of_sync_count); + lesb->lesb_sig_loss = htonl(hst->loss_of_signal_count); + lesb->lesb_prim_err = htonl(hst->prim_seq_protocol_err_count); + lesb->lesb_inv_word = htonl(hst->invalid_tx_word_count); + lesb->lesb_inv_crc = htonl(hst->invalid_crc_count); + } + + sp = lport->tt.seq_start_next(sp); + f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ; + fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, + FC_TYPE_ELS, f_ctl, 0); + lport->tt.seq_send(lport, sp, fp); + goto out; + +out_rjt: + rjt_data.fp = NULL; + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); +out: + fc_frame_free(rx_fp); +} + +/** + * fc_rport_recv_els_req() - Handler for validated ELS requests + * @lport: The local port that received the ELS request + * @sp: The sequence that the ELS request was on + * @fp: The ELS request frame * * Handle incoming ELS requests that require port login. * The ELS opcode has already been validated by the caller. @@ -1117,6 +1231,9 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, els_data.fp = fp; lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data); break; + case ELS_RLS: + fc_rport_recv_rls_req(rdata, sp, fp); + break; default: fc_frame_free(fp); /* can't happen */ break; @@ -1131,10 +1248,10 @@ reject: } /** - * fc_rport_recv_req() - Handle a received ELS request from a rport - * @sp: current sequence in the PLOGI exchange - * @fp: response frame - * @lport: Fibre Channel local port + * fc_rport_recv_req() - Handler for requests + * @sp: The sequence the request was on + * @fp: The request frame + * @lport: The local port that received the request * * Locking Note: Called with the lport lock held. */ @@ -1161,6 +1278,7 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, case ELS_ADISC: case ELS_RRQ: case ELS_REC: + case ELS_RLS: fc_rport_recv_els_req(lport, sp, fp); break; default: @@ -1174,10 +1292,10 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, } /** - * fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request - * @lport: local port - * @sp: current sequence in the PLOGI exchange - * @fp: PLOGI request frame + * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests + * @lport: The local port that received the PLOGI request + * @sp: The sequence that the PLOGI request was on + * @rx_fp: The PLOGI request frame * * Locking Note: The rport lock is held before calling this function. */ @@ -1248,6 +1366,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport, } break; case RPORT_ST_PRLI: + case RPORT_ST_RTV: case RPORT_ST_READY: case RPORT_ST_ADISC: FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d " @@ -1255,11 +1374,14 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport, /* XXX TBD - should reset */ break; case RPORT_ST_DELETE: - default: - FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n", - rdata->rp_state); - fc_frame_free(rx_fp); - goto out; + case RPORT_ST_LOGO: + case RPORT_ST_RESTART: + FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n", + fc_rport_state(rdata)); + mutex_unlock(&rdata->rp_mutex); + rjt_data.reason = ELS_RJT_BUSY; + rjt_data.explan = ELS_EXPL_NONE; + goto reject; } /* @@ -1295,10 +1417,10 @@ reject: } /** - * fc_rport_recv_prli_req() - Handle incoming Process Login (PRLI) request - * @rdata: private remote port data - * @sp: current sequence in the PRLI exchange - * @fp: PRLI request frame + * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests + * @rdata: The remote port that sent the PRLI request + * @sp: The sequence that the PRLI was on + * @rx_fp: The PRLI request frame * * Locking Note: The rport lock is exected to be held before calling * this function. @@ -1402,7 +1524,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, break; case FC_TYPE_FCP: fcp_parm = ntohl(rspp->spp_params); - if (fcp_parm * FCP_SPPF_RETRY) + if (fcp_parm & FCP_SPPF_RETRY) rdata->flags |= FC_RP_FLAGS_RETRY; rdata->supported_classes = FC_COS_CLASS3; if (fcp_parm & FCP_SPPF_INIT_FCN) @@ -1452,10 +1574,10 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, } /** - * fc_rport_recv_prlo_req() - Handle incoming Process Logout (PRLO) request - * @rdata: private remote port data - * @sp: current sequence in the PRLO exchange - * @fp: PRLO request frame + * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests + * @rdata: The remote port that sent the PRLO request + * @sp: The sequence that the PRLO was on + * @fp: The PRLO request frame * * Locking Note: The rport lock is exected to be held before calling * this function. @@ -1482,10 +1604,10 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, } /** - * fc_rport_recv_logo_req() - Handle incoming Logout (LOGO) request - * @lport: local port. - * @sp: current sequence in the LOGO exchange - * @fp: LOGO request frame + * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests + * @lport: The local port that received the LOGO request + * @sp: The sequence that the LOGO request was on + * @fp: The LOGO request frame * * Locking Note: The rport lock is exected to be held before calling * this function. @@ -1510,14 +1632,14 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport, FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n", fc_rport_state(rdata)); + fc_rport_enter_delete(rdata, RPORT_EV_LOGO); + /* - * If the remote port was created due to discovery, - * log back in. It may have seen a stale RSCN about us. + * If the remote port was created due to discovery, set state + * to log back in. It may have seen a stale RSCN about us. */ - if (rdata->rp_state != RPORT_ST_DELETE && rdata->disc_id) - fc_rport_enter_plogi(rdata); - else - fc_rport_enter_delete(rdata, RPORT_EV_LOGO); + if (rdata->disc_id) + fc_rport_state_enter(rdata, RPORT_ST_RESTART); mutex_unlock(&rdata->rp_mutex); } else FC_RPORT_ID_DBG(lport, sid, @@ -1526,11 +1648,18 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport, fc_frame_free(fp); } +/** + * fc_rport_flush_queue() - Flush the rport_event_queue + */ static void fc_rport_flush_queue(void) { flush_workqueue(rport_event_queue); } +/** + * fc_rport_init() - Initialize the remote port layer for a local port + * @lport: The local port to initialize the remote port layer for + */ int fc_rport_init(struct fc_lport *lport) { if (!lport->tt.rport_lookup) @@ -1558,25 +1687,33 @@ int fc_rport_init(struct fc_lport *lport) } EXPORT_SYMBOL(fc_rport_init); -int fc_setup_rport(void) +/** + * fc_setup_rport() - Initialize the rport_event_queue + */ +int fc_setup_rport() { rport_event_queue = create_singlethread_workqueue("fc_rport_eq"); if (!rport_event_queue) return -ENOMEM; return 0; } -EXPORT_SYMBOL(fc_setup_rport); -void fc_destroy_rport(void) +/** + * fc_destroy_rport() - Destroy the rport_event_queue + */ +void fc_destroy_rport() { destroy_workqueue(rport_event_queue); } -EXPORT_SYMBOL(fc_destroy_rport); +/** + * fc_rport_terminate_io() - Stop all outstanding I/O on a remote port + * @rport: The remote port whose I/O should be terminated + */ void fc_rport_terminate_io(struct fc_rport *rport) { - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lport = rp->local_port; + struct fc_rport_libfc_priv *rpriv = rport->dd_data; + struct fc_lport *lport = rpriv->local_port; lport->tt.exch_mgr_reset(lport, 0, rport->port_id); lport->tt.exch_mgr_reset(lport, rport->port_id, 0); diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index f1a4246f890c..b7689f3d05f5 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -266,6 +266,88 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task) } /** + * iscsi_check_tmf_restrictions - check if a task is affected by TMF + * @task: iscsi task + * @opcode: opcode to check for + * + * During TMF a task has to be checked if it's affected. + * All unrelated I/O can be passed through, but I/O to the + * affected LUN should be restricted. + * If 'fast_abort' is set we won't be sending any I/O to the + * affected LUN. + * Otherwise the target is waiting for all TTTs to be completed, + * so we have to send all outstanding Data-Out PDUs to the target. + */ +static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) +{ + struct iscsi_conn *conn = task->conn; + struct iscsi_tm *tmf = &conn->tmhdr; + unsigned int hdr_lun; + + if (conn->tmf_state == TMF_INITIAL) + return 0; + + if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC) + return 0; + + switch (ISCSI_TM_FUNC_VALUE(tmf)) { + case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: + /* + * Allow PDUs for unrelated LUNs + */ + hdr_lun = scsilun_to_int((struct scsi_lun *)tmf->lun); + if (hdr_lun != task->sc->device->lun) + return 0; + /* fall through */ + case ISCSI_TM_FUNC_TARGET_WARM_RESET: + /* + * Fail all SCSI cmd PDUs + */ + if (opcode != ISCSI_OP_SCSI_DATA_OUT) { + iscsi_conn_printk(KERN_INFO, conn, + "task [op %x/%x itt " + "0x%x/0x%x] " + "rejected.\n", + task->hdr->opcode, opcode, + task->itt, task->hdr_itt); + return -EACCES; + } + /* + * And also all data-out PDUs in response to R2T + * if fast_abort is set. + */ + if (conn->session->fast_abort) { + iscsi_conn_printk(KERN_INFO, conn, + "task [op %x/%x itt " + "0x%x/0x%x] fast abort.\n", + task->hdr->opcode, opcode, + task->itt, task->hdr_itt); + return -EACCES; + } + break; + case ISCSI_TM_FUNC_ABORT_TASK: + /* + * the caller has already checked if the task + * they want to abort was in the pending queue so if + * we are here the cmd pdu has gone out already, and + * we will only hit this for data-outs + */ + if (opcode == ISCSI_OP_SCSI_DATA_OUT && + task->hdr_itt == tmf->rtt) { + ISCSI_DBG_SESSION(conn->session, + "Preventing task %x/%x from sending " + "data-out due to abort task in " + "progress\n", task->itt, + task->hdr_itt); + return -EACCES; + } + break; + } + + return 0; +} + +/** * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu * @task: iscsi task * @@ -282,6 +364,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) itt_t itt; int rc; + rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD); + if (rc) + return rc; + if (conn->session->tt->alloc_pdu) { rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD); if (rc) @@ -577,12 +663,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, struct iscsi_session *session = conn->session; struct iscsi_hdr *hdr = task->hdr; struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr; + uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; if (conn->session->state == ISCSI_STATE_LOGGING_OUT) return -ENOTCONN; - if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) && - hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) + if (opcode != ISCSI_OP_LOGIN && opcode != ISCSI_OP_TEXT) nop->exp_statsn = cpu_to_be32(conn->exp_statsn); /* * pre-format CmdSN for outgoing PDU. @@ -590,9 +676,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, nop->cmdsn = cpu_to_be32(session->cmdsn); if (hdr->itt != RESERVED_ITT) { /* - * TODO: We always use immediate, so we never hit this. + * TODO: We always use immediate for normal session pdus. * If we start to send tmfs or nops as non-immediate then * we should start checking the cmdsn numbers for mgmt tasks. + * + * During discovery sessions iscsid sends TEXT as non immediate, + * but we always only send one PDU at a time. */ if (conn->c_stage == ISCSI_CONN_STARTED && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { @@ -620,22 +709,28 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, { struct iscsi_session *session = conn->session; struct iscsi_host *ihost = shost_priv(session->host); + uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; struct iscsi_task *task; itt_t itt; if (session->state == ISCSI_STATE_TERMINATE) return NULL; - if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) || - hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) + if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) { /* * Login and Text are sent serially, in * request-followed-by-response sequence. * Same task can be used. Same ITT must be used. * Note that login_task is preallocated at conn_create(). */ + if (conn->login_task->state != ISCSI_TASK_FREE) { + iscsi_conn_printk(KERN_ERR, conn, "Login/Text in " + "progress. Cannot start new task.\n"); + return NULL; + } + task = conn->login_task; - else { + } else { if (session->state != ISCSI_STATE_LOGGED_IN) return NULL; @@ -1357,6 +1452,7 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_task); **/ static int iscsi_data_xmit(struct iscsi_conn *conn) { + struct iscsi_task *task; int rc = 0; spin_lock_bh(&conn->session->lock); @@ -1394,11 +1490,8 @@ check_mgmt: /* process pending command queue */ while (!list_empty(&conn->cmdqueue)) { - if (conn->tmf_state == TMF_QUEUED) - break; - - conn->task = list_entry(conn->cmdqueue.next, - struct iscsi_task, running); + conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task, + running); list_del_init(&conn->task->running); if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { fail_scsi_task(conn->task, DID_IMM_RETRY); @@ -1406,7 +1499,7 @@ check_mgmt: } rc = iscsi_prep_scsi_cmd_pdu(conn->task); if (rc) { - if (rc == -ENOMEM) { + if (rc == -ENOMEM || rc == -EACCES) { list_add_tail(&conn->task->running, &conn->cmdqueue); conn->task = NULL; @@ -1428,17 +1521,18 @@ check_mgmt: } while (!list_empty(&conn->requeue)) { - if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL) - break; - /* * we always do fastlogout - conn stop code will clean up. */ if (conn->session->state == ISCSI_STATE_LOGGING_OUT) break; - conn->task = list_entry(conn->requeue.next, - struct iscsi_task, running); + task = list_entry(conn->requeue.next, struct iscsi_task, + running); + if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT)) + break; + + conn->task = task; list_del_init(&conn->task->running); conn->task->state = ISCSI_TASK_RUNNING; rc = iscsi_xmit_task(conn); @@ -1591,7 +1685,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) if (!ihost->workq) { reason = iscsi_prep_scsi_cmd_pdu(task); if (reason) { - if (reason == -ENOMEM) { + if (reason == -ENOMEM || reason == -EACCES) { reason = FAILURE_OOM; goto prepd_reject; } else { @@ -1643,9 +1737,21 @@ fault: } EXPORT_SYMBOL_GPL(iscsi_queuecommand); -int iscsi_change_queue_depth(struct scsi_device *sdev, int depth) +int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason) { - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + switch (reason) { + case SCSI_QDEPTH_DEFAULT: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + case SCSI_QDEPTH_QFULL: + scsi_track_queue_full(sdev, depth); + break; + case SCSI_QDEPTH_RAMP_UP: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + default: + return -EOPNOTSUPP; + } return sdev->queue_depth; } EXPORT_SYMBOL_GPL(iscsi_change_queue_depth); @@ -1660,72 +1766,6 @@ int iscsi_target_alloc(struct scsi_target *starget) } EXPORT_SYMBOL_GPL(iscsi_target_alloc); -void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) -{ - struct iscsi_session *session = cls_session->dd_data; - - spin_lock_bh(&session->lock); - if (session->state != ISCSI_STATE_LOGGED_IN) { - session->state = ISCSI_STATE_RECOVERY_FAILED; - if (session->leadconn) - wake_up(&session->leadconn->ehwait); - } - spin_unlock_bh(&session->lock); -} -EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); - -int iscsi_eh_target_reset(struct scsi_cmnd *sc) -{ - struct iscsi_cls_session *cls_session; - struct iscsi_session *session; - struct iscsi_conn *conn; - - cls_session = starget_to_session(scsi_target(sc->device)); - session = cls_session->dd_data; - conn = session->leadconn; - - mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_TERMINATE) { -failed: - ISCSI_DBG_EH(session, - "failing target reset: Could not log back into " - "target [age %d]\n", - session->age); - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - return FAILED; - } - - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - /* - * we drop the lock here but the leadconn cannot be destoyed while - * we are in the scsi eh - */ - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - - ISCSI_DBG_EH(session, "wait for relogin\n"); - wait_event_interruptible(conn->ehwait, - session->state == ISCSI_STATE_TERMINATE || - session->state == ISCSI_STATE_LOGGED_IN || - session->state == ISCSI_STATE_RECOVERY_FAILED); - if (signal_pending(current)) - flush_signals(current); - - mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_LOGGED_IN) { - ISCSI_DBG_EH(session, - "target reset succeeded\n"); - } else - goto failed; - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - return SUCCESS; -} -EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); - static void iscsi_tmf_timedout(unsigned long data) { struct iscsi_conn *conn = (struct iscsi_conn *)data; @@ -2108,6 +2148,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) spin_lock_bh(&session->lock); fail_scsi_task(task, DID_ABORT); conn->tmf_state = TMF_INITIAL; + memset(hdr, 0, sizeof(*hdr)); spin_unlock_bh(&session->lock); iscsi_start_tx(conn); goto success_unlocked; @@ -2118,6 +2159,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) case TMF_NOT_FOUND: if (!sc->SCp.ptr) { conn->tmf_state = TMF_INITIAL; + memset(hdr, 0, sizeof(*hdr)); /* task completed before tmf abort response */ ISCSI_DBG_EH(session, "sc completed while abort in " "progress\n"); @@ -2212,6 +2254,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) iscsi_suspend_tx(conn); spin_lock_bh(&session->lock); + memset(hdr, 0, sizeof(*hdr)); fail_scsi_tasks(conn, sc->device->lun, DID_ERROR); conn->tmf_state = TMF_INITIAL; spin_unlock_bh(&session->lock); @@ -2229,6 +2272,172 @@ done: } EXPORT_SYMBOL_GPL(iscsi_eh_device_reset); +void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) +{ + struct iscsi_session *session = cls_session->dd_data; + + spin_lock_bh(&session->lock); + if (session->state != ISCSI_STATE_LOGGED_IN) { + session->state = ISCSI_STATE_RECOVERY_FAILED; + if (session->leadconn) + wake_up(&session->leadconn->ehwait); + } + spin_unlock_bh(&session->lock); +} +EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); + +/** + * iscsi_eh_session_reset - drop session and attempt relogin + * @sc: scsi command + * + * This function will wait for a relogin, session termination from + * userspace, or a recovery/replacement timeout. + */ +static int iscsi_eh_session_reset(struct scsi_cmnd *sc) +{ + struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + + cls_session = starget_to_session(scsi_target(sc->device)); + session = cls_session->dd_data; + conn = session->leadconn; + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->lock); + if (session->state == ISCSI_STATE_TERMINATE) { +failed: + ISCSI_DBG_EH(session, + "failing session reset: Could not log back into " + "%s, %s [age %d]\n", session->targetname, + conn->persistent_address, session->age); + spin_unlock_bh(&session->lock); + mutex_unlock(&session->eh_mutex); + return FAILED; + } + + spin_unlock_bh(&session->lock); + mutex_unlock(&session->eh_mutex); + /* + * we drop the lock here but the leadconn cannot be destoyed while + * we are in the scsi eh + */ + iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + + ISCSI_DBG_EH(session, "wait for relogin\n"); + wait_event_interruptible(conn->ehwait, + session->state == ISCSI_STATE_TERMINATE || + session->state == ISCSI_STATE_LOGGED_IN || + session->state == ISCSI_STATE_RECOVERY_FAILED); + if (signal_pending(current)) + flush_signals(current); + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->lock); + if (session->state == ISCSI_STATE_LOGGED_IN) { + ISCSI_DBG_EH(session, + "session reset succeeded for %s,%s\n", + session->targetname, conn->persistent_address); + } else + goto failed; + spin_unlock_bh(&session->lock); + mutex_unlock(&session->eh_mutex); + return SUCCESS; +} + +static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) +{ + memset(hdr, 0, sizeof(*hdr)); + hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; + hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK; + hdr->flags |= ISCSI_FLAG_CMD_FINAL; + hdr->rtt = RESERVED_ITT; +} + +/** + * iscsi_eh_target_reset - reset target + * @sc: scsi command + * + * This will attempt to send a warm target reset. If that fails + * then we will drop the session and attempt ERL0 recovery. + */ +int iscsi_eh_target_reset(struct scsi_cmnd *sc) +{ + struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + struct iscsi_tm *hdr; + int rc = FAILED; + + cls_session = starget_to_session(scsi_target(sc->device)); + session = cls_session->dd_data; + + ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc, + session->targetname); + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->lock); + /* + * Just check if we are not logged in. We cannot check for + * the phase because the reset could come from a ioctl. + */ + if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) + goto unlock; + conn = session->leadconn; + + /* only have one tmf outstanding at a time */ + if (conn->tmf_state != TMF_INITIAL) + goto unlock; + conn->tmf_state = TMF_QUEUED; + + hdr = &conn->tmhdr; + iscsi_prep_tgt_reset_pdu(sc, hdr); + + if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, + session->tgt_reset_timeout)) { + rc = FAILED; + goto unlock; + } + + switch (conn->tmf_state) { + case TMF_SUCCESS: + break; + case TMF_TIMEDOUT: + spin_unlock_bh(&session->lock); + iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + goto done; + default: + conn->tmf_state = TMF_INITIAL; + goto unlock; + } + + rc = SUCCESS; + spin_unlock_bh(&session->lock); + + iscsi_suspend_tx(conn); + + spin_lock_bh(&session->lock); + memset(hdr, 0, sizeof(*hdr)); + fail_scsi_tasks(conn, -1, DID_ERROR); + conn->tmf_state = TMF_INITIAL; + spin_unlock_bh(&session->lock); + + iscsi_start_tx(conn); + goto done; + +unlock: + spin_unlock_bh(&session->lock); +done: + ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname, + rc == SUCCESS ? "SUCCESS" : "FAILED"); + mutex_unlock(&session->eh_mutex); + + if (rc == FAILED) + rc = iscsi_eh_session_reset(sc); + return rc; +} +EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); + /* * Pre-allocate a pool of @max items of @item_size. By default, the pool * should be accessed via kfifo_{get,put} on q->queue. @@ -2495,6 +2704,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, session->host = shost; session->state = ISCSI_STATE_FREE; session->fast_abort = 1; + session->tgt_reset_timeout = 30; session->lu_reset_timeout = 15; session->abort_timeout = 10; session->scsi_cmds_max = scsi_cmds; @@ -2856,6 +3066,7 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, spin_lock_bh(&session->lock); fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED); fail_mgmt_tasks(session, conn); + memset(&conn->tmhdr, 0, sizeof(conn->tmhdr)); spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); } @@ -2932,6 +3143,9 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, case ISCSI_PARAM_LU_RESET_TMO: sscanf(buf, "%d", &session->lu_reset_timeout); break; + case ISCSI_PARAM_TGT_RESET_TMO: + sscanf(buf, "%d", &session->tgt_reset_timeout); + break; case ISCSI_PARAM_PING_TMO: sscanf(buf, "%d", &conn->ping_timeout); break; @@ -3031,6 +3245,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, case ISCSI_PARAM_LU_RESET_TMO: len = sprintf(buf, "%d\n", session->lu_reset_timeout); break; + case ISCSI_PARAM_TGT_RESET_TMO: + len = sprintf(buf, "%d\n", session->tgt_reset_timeout); + break; case ISCSI_PARAM_INITIAL_R2T_EN: len = sprintf(buf, "%d\n", session->initial_r2t_en); break; diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 2e0746d70303..ca25ee5190b0 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -1004,7 +1004,7 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task) * iscsi_tcp_task_xmit - xmit normal PDU task * @task: iscsi command task * - * We're expected to return 0 when everything was transmitted succesfully, + * We're expected to return 0 when everything was transmitted successfully, * -EAGAIN if there's still data in the queue, or != 0 for any other kind * of error. */ diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 1c558d3bce18..14b13196b22d 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -820,10 +820,14 @@ void sas_slave_destroy(struct scsi_device *scsi_dev) ata_port_disable(dev->sata_dev.ap); } -int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth) +int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth, + int reason) { int res = min(new_depth, SAS_MAX_QD); + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (scsi_dev->tagged_supported) scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), res); diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index aa10f7951634..1cc23a69db5e 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -109,7 +109,8 @@ struct hbq_dmabuf { struct lpfc_dmabuf dbuf; uint32_t size; uint32_t tag; - struct lpfc_rcqe rcqe; + struct lpfc_cq_event cq_event; + unsigned long time_stamp; }; /* Priority bit. Set value to exceed low water mark in lpfc_mem. */ @@ -201,6 +202,7 @@ struct lpfc_stats { uint32_t elsRcvLIRR; uint32_t elsRcvRPS; uint32_t elsRcvRPL; + uint32_t elsRcvRRQ; uint32_t elsXmitFLOGI; uint32_t elsXmitFDISC; uint32_t elsXmitPLOGI; @@ -289,8 +291,8 @@ struct lpfc_vport { uint16_t vpi; uint16_t vfi; - uint8_t vfi_state; -#define LPFC_VFI_REGISTERED 0x1 + uint8_t vpi_state; +#define LPFC_VPI_REGISTERED 0x1 uint32_t fc_flag; /* FC flags */ /* Several of these flags are HBA centric and should be moved to @@ -405,6 +407,7 @@ struct lpfc_vport { uint8_t stat_data_enabled; uint8_t stat_data_blocked; struct list_head rcv_buffer_list; + unsigned long rcv_buffer_time_stamp; uint32_t vport_flag; #define STATIC_VPORT 1 }; @@ -527,13 +530,16 @@ struct lpfc_hba { #define HBA_ERATT_HANDLED 0x1 /* This flag is set when eratt handled */ #define DEFER_ERATT 0x2 /* Deferred error attention in progress */ #define HBA_FCOE_SUPPORT 0x4 /* HBA function supports FCOE */ -#define HBA_RECEIVE_BUFFER 0x8 /* Rcv buffer posted to worker thread */ +#define HBA_SP_QUEUE_EVT 0x8 /* Slow-path qevt posted to worker thread*/ #define HBA_POST_RECEIVE_BUFFER 0x10 /* Rcv buffers need to be posted */ #define FCP_XRI_ABORT_EVENT 0x20 #define ELS_XRI_ABORT_EVENT 0x40 #define ASYNC_EVENT 0x80 #define LINK_DISABLED 0x100 /* Link disabled by user */ #define FCF_DISC_INPROGRESS 0x200 /* FCF discovery in progress */ +#define HBA_FIP_SUPPORT 0x400 /* FIP support in HBA */ +#define HBA_AER_ENABLED 0x800 /* AER enabled with HBA */ + uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/ struct lpfc_dmabuf slim2p; MAILBOX_t *mbox; @@ -551,6 +557,7 @@ struct lpfc_hba { uint8_t fc_linkspeed; /* Link speed after last READ_LA */ uint32_t fc_eventTag; /* event tag for link attention */ + uint32_t link_events; /* These fields used to be binfo */ uint32_t fc_pref_DID; /* preferred D_ID */ @@ -604,8 +611,8 @@ struct lpfc_hba { uint32_t cfg_enable_hba_reset; uint32_t cfg_enable_hba_heartbeat; uint32_t cfg_enable_bg; - uint32_t cfg_enable_fip; uint32_t cfg_log_verbose; + uint32_t cfg_aer_support; lpfc_vpd_t vpd; /* vital product data */ diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index e1a30a16a9fa..91542f786edf 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -23,12 +23,14 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/aer.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/fc/fc_fs.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -98,6 +100,28 @@ lpfc_drvr_version_show(struct device *dev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n"); } +/** + * lpfc_enable_fip_show - Return the fip mode of the HBA + * @dev: class unused variable. + * @attr: device attribute, not used. + * @buf: on return contains the module description text. + * + * Returns: size of formatted string. + **/ +static ssize_t +lpfc_enable_fip_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; + struct lpfc_hba *phba = vport->phba; + + if (phba->hba_flag & HBA_FIP_SUPPORT) + return snprintf(buf, PAGE_SIZE, "1\n"); + else + return snprintf(buf, PAGE_SIZE, "0\n"); +} + static ssize_t lpfc_bg_info_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -654,7 +678,7 @@ lpfc_selective_reset(struct lpfc_hba *phba) * Notes: * Assumes any error from lpfc_selective_reset() will be negative. * If lpfc_selective_reset() returns zero then the length of the buffer - * is returned which indicates succcess + * is returned which indicates success * * Returns: * -EINVAL if the buffer does not contain the string "selective" @@ -762,9 +786,15 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr, } else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0) status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); else if (strncmp(buf, "warm", sizeof("warm") - 1) == 0) - status = lpfc_do_offline(phba, LPFC_EVT_WARM_START); + if (phba->sli_rev == LPFC_SLI_REV4) + return -EINVAL; + else + status = lpfc_do_offline(phba, LPFC_EVT_WARM_START); else if (strncmp(buf, "error", sizeof("error") - 1) == 0) - status = lpfc_do_offline(phba, LPFC_EVT_KILL); + if (phba->sli_rev == LPFC_SLI_REV4) + return -EINVAL; + else + status = lpfc_do_offline(phba, LPFC_EVT_KILL); else return -EINVAL; @@ -1126,6 +1156,9 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr, if ((val & 0x3) != val) return -EINVAL; + if (phba->sli_rev == LPFC_SLI_REV4) + val = 0; + spin_lock_irq(&phba->hbalock); old_val = phba->cfg_poll; @@ -1589,6 +1622,7 @@ static DEVICE_ATTR(num_discovered_ports, S_IRUGO, static DEVICE_ATTR(menlo_mgmt_mode, S_IRUGO, lpfc_mlomgmt_show, NULL); static DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL); static DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, NULL); +static DEVICE_ATTR(lpfc_enable_fip, S_IRUGO, lpfc_enable_fip_show, NULL); static DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR, lpfc_board_mode_show, lpfc_board_mode_store); static DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); @@ -2759,6 +2793,196 @@ static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR, lpfc_link_speed_show, lpfc_link_speed_store); /* +# lpfc_aer_support: Support PCIe device Advanced Error Reporting (AER) +# 0 = aer disabled or not supported +# 1 = aer supported and enabled (default) +# Value range is [0,1]. Default value is 1. +*/ + +/** + * lpfc_aer_support_store - Set the adapter for aer support + * + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: containing the string "selective". + * @count: unused variable. + * + * Description: + * If the val is 1 and currently the device's AER capability was not + * enabled, invoke the kernel's enable AER helper routine, trying to + * enable the device's AER capability. If the helper routine enabling + * AER returns success, update the device's cfg_aer_support flag to + * indicate AER is supported by the device; otherwise, if the device + * AER capability is already enabled to support AER, then do nothing. + * + * If the val is 0 and currently the device's AER support was enabled, + * invoke the kernel's disable AER helper routine. After that, update + * the device's cfg_aer_support flag to indicate AER is not supported + * by the device; otherwise, if the device AER capability is already + * disabled from supporting AER, then do nothing. + * + * Returns: + * length of the buf on success if val is in range the intended mode + * is supported. + * -EINVAL if val out of range or intended mode is not supported. + **/ +static ssize_t +lpfc_aer_support_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; + struct lpfc_hba *phba = vport->phba; + int val = 0, rc = -EINVAL; + + /* AER not supported on OC devices yet */ + if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) + return -EPERM; + if (!isdigit(buf[0])) + return -EINVAL; + if (sscanf(buf, "%i", &val) != 1) + return -EINVAL; + + switch (val) { + case 0: + if (phba->hba_flag & HBA_AER_ENABLED) { + rc = pci_disable_pcie_error_reporting(phba->pcidev); + if (!rc) { + spin_lock_irq(&phba->hbalock); + phba->hba_flag &= ~HBA_AER_ENABLED; + spin_unlock_irq(&phba->hbalock); + phba->cfg_aer_support = 0; + rc = strlen(buf); + } else + rc = -EPERM; + } else { + phba->cfg_aer_support = 0; + rc = strlen(buf); + } + break; + case 1: + if (!(phba->hba_flag & HBA_AER_ENABLED)) { + rc = pci_enable_pcie_error_reporting(phba->pcidev); + if (!rc) { + spin_lock_irq(&phba->hbalock); + phba->hba_flag |= HBA_AER_ENABLED; + spin_unlock_irq(&phba->hbalock); + phba->cfg_aer_support = 1; + rc = strlen(buf); + } else + rc = -EPERM; + } else { + phba->cfg_aer_support = 1; + rc = strlen(buf); + } + break; + default: + rc = -EINVAL; + break; + } + return rc; +} + +static int lpfc_aer_support = 1; +module_param(lpfc_aer_support, int, 1); +MODULE_PARM_DESC(lpfc_aer_support, "Enable PCIe device AER support"); +lpfc_param_show(aer_support) + +/** + * lpfc_aer_support_init - Set the initial adapters aer support flag + * @phba: lpfc_hba pointer. + * @val: link speed value. + * + * Description: + * If val is in a valid range [0,1], then set the adapter's initial + * cfg_aer_support field. It will be up to the driver's probe_one + * routine to determine whether the device's AER support can be set + * or not. + * + * Notes: + * If the value is not in range log a kernel error message, and + * choose the default value of setting AER support and return. + * + * Returns: + * zero if val saved. + * -EINVAL val out of range + **/ +static int +lpfc_aer_support_init(struct lpfc_hba *phba, int val) +{ + /* AER not supported on OC devices yet */ + if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) { + phba->cfg_aer_support = 0; + return -EPERM; + } + + if (val == 0 || val == 1) { + phba->cfg_aer_support = val; + return 0; + } + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2712 lpfc_aer_support attribute value %d out " + "of range, allowed values are 0|1, setting it " + "to default value of 1\n", val); + /* By default, try to enable AER on a device */ + phba->cfg_aer_support = 1; + return -EINVAL; +} + +static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR, + lpfc_aer_support_show, lpfc_aer_support_store); + +/** + * lpfc_aer_cleanup_state - Clean up aer state to the aer enabled device + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: containing the string "selective". + * @count: unused variable. + * + * Description: + * If the @buf contains 1 and the device currently has the AER support + * enabled, then invokes the kernel AER helper routine + * pci_cleanup_aer_uncorrect_error_status to clean up the uncorrectable + * error status register. + * + * Notes: + * + * Returns: + * -EINVAL if the buf does not contain the 1 or the device is not currently + * enabled with the AER support. + **/ +static ssize_t +lpfc_aer_cleanup_state(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; + struct lpfc_hba *phba = vport->phba; + int val, rc = -1; + + /* AER not supported on OC devices yet */ + if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) + return -EPERM; + if (!isdigit(buf[0])) + return -EINVAL; + if (sscanf(buf, "%i", &val) != 1) + return -EINVAL; + if (val != 1) + return -EINVAL; + + if (phba->hba_flag & HBA_AER_ENABLED) + rc = pci_cleanup_aer_uncorrect_error_status(phba->pcidev); + + if (rc == 0) + return strlen(buf); + else + return -EPERM; +} + +static DEVICE_ATTR(lpfc_aer_state_cleanup, S_IWUSR, NULL, + lpfc_aer_cleanup_state); + +/* # lpfc_fcp_class: Determines FC class to use for the FCP protocol. # Value range is [2,3]. Default value is 3. */ @@ -2846,7 +3070,7 @@ LPFC_ATTR_R(multi_ring_support, 1, 1, 2, "Determines number of primary " # identifies what rctl value to configure the additional ring for. # Value range is [1,0xff]. Default value is 4 (Unsolicated Data). */ -LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1, +LPFC_ATTR_R(multi_ring_rctl, FC_RCTL_DD_UNSOL_DATA, 1, 255, "Identifies RCTL for additional ring configuration"); /* @@ -2854,7 +3078,7 @@ LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1, # identifies what type value to configure the additional ring for. # Value range is [1,0xff]. Default value is 5 (LLC/SNAP). */ -LPFC_ATTR_R(multi_ring_type, FC_LLC_SNAP, 1, +LPFC_ATTR_R(multi_ring_type, FC_TYPE_IP, 1, 255, "Identifies TYPE for additional ring configuration"); /* @@ -2947,15 +3171,6 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat."); LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support"); /* -# lpfc_enable_fip: When set, FIP is required to start discovery. If not -# set, the driver will add an FCF record manually if the port has no -# FCF records available and start discovery. -# Value range is [0,1]. Default value is 1 (enabled) -*/ -LPFC_ATTR_RW(enable_fip, 0, 0, 1, "Enable FIP Discovery"); - - -/* # lpfc_prot_mask: i # - Bit mask of host protection capabilities used to register with the # SCSI mid-layer @@ -3013,6 +3228,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_num_discovered_ports, &dev_attr_menlo_mgmt_mode, &dev_attr_lpfc_drvr_version, + &dev_attr_lpfc_enable_fip, &dev_attr_lpfc_temp_sensor, &dev_attr_lpfc_log_verbose, &dev_attr_lpfc_lun_queue_depth, @@ -3020,7 +3236,6 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_peer_port_login, &dev_attr_lpfc_nodev_tmo, &dev_attr_lpfc_devloss_tmo, - &dev_attr_lpfc_enable_fip, &dev_attr_lpfc_fcp_class, &dev_attr_lpfc_use_adisc, &dev_attr_lpfc_ack0, @@ -3061,6 +3276,8 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_max_scsicmpl_time, &dev_attr_lpfc_stat_data_ctrl, &dev_attr_lpfc_prot_sg_seg_cnt, + &dev_attr_lpfc_aer_support, + &dev_attr_lpfc_aer_state_cleanup, NULL, }; @@ -3073,7 +3290,6 @@ struct device_attribute *lpfc_vport_attrs[] = { &dev_attr_lpfc_lun_queue_depth, &dev_attr_lpfc_nodev_tmo, &dev_attr_lpfc_devloss_tmo, - &dev_attr_lpfc_enable_fip, &dev_attr_lpfc_hba_queue_depth, &dev_attr_lpfc_peer_port_login, &dev_attr_lpfc_restrict_login, @@ -3147,7 +3363,7 @@ sysfs_ctlreg_write(struct kobject *kobj, struct bin_attribute *bin_attr, * sysfs_ctlreg_read - Read method for reading from ctlreg * @kobj: kernel kobject that contains the kernel class device. * @bin_attr: kernel attributes passed to us. - * @buf: if succesful contains the data from the adapter IOREG space. + * @buf: if successful contains the data from the adapter IOREG space. * @off: offset into buffer to beginning of data. * @count: bytes to transfer. * @@ -3815,7 +4031,11 @@ lpfc_get_stats(struct Scsi_Host *shost) hs->invalid_crc_count -= lso->invalid_crc_count; hs->error_frames -= lso->error_frames; - if (phba->fc_topology == TOPOLOGY_LOOP) { + if (phba->hba_flag & HBA_FCOE_SUPPORT) { + hs->lip_count = -1; + hs->nos_count = (phba->link_events >> 1); + hs->nos_count -= lso->link_events; + } else if (phba->fc_topology == TOPOLOGY_LOOP) { hs->lip_count = (phba->fc_eventTag >> 1); hs->lip_count -= lso->link_events; hs->nos_count = -1; @@ -3906,7 +4126,10 @@ lpfc_reset_stats(struct Scsi_Host *shost) lso->invalid_tx_word_count = pmb->un.varRdLnk.invalidXmitWord; lso->invalid_crc_count = pmb->un.varRdLnk.crcCnt; lso->error_frames = pmb->un.varRdLnk.crcCnt; - lso->link_events = (phba->fc_eventTag >> 1); + if (phba->hba_flag & HBA_FCOE_SUPPORT) + lso->link_events = (phba->link_events >> 1); + else + lso->link_events = (phba->fc_eventTag >> 1); psli->stats_start = get_seconds(); @@ -4222,14 +4445,17 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset); lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat); lpfc_enable_bg_init(phba, lpfc_enable_bg); + if (phba->sli_rev == LPFC_SLI_REV4) + phba->cfg_poll = 0; + else phba->cfg_poll = lpfc_poll; phba->cfg_soft_wwnn = 0L; phba->cfg_soft_wwpn = 0L; lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt); lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt); lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth); - lpfc_enable_fip_init(phba, lpfc_enable_fip); lpfc_hba_log_verbose_init(phba, lpfc_log_verbose); + lpfc_aer_support_init(phba, lpfc_aer_support); return; } diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index da6bf5aac9dd..a5d9048235d9 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -26,6 +26,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> #include <scsi/scsi_bsg_fc.h> +#include <scsi/fc/fc_fs.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -148,8 +149,8 @@ lpfc_bsg_rport_ct(struct fc_bsg_job *job) cmd->ulpCommand = CMD_GEN_REQUEST64_CR; cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); cmd->un.genreq64.w5.hcsw.Dfctl = 0; - cmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL; - cmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP; + cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL; + cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT; cmd->ulpBdeCount = 1; cmd->ulpLe = 1; cmd->ulpClass = CLASS3; diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 0830f37409a3..650494d622c1 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -49,6 +49,8 @@ void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *); struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t); +void lpfc_cleanup_rcv_buffers(struct lpfc_vport *); +void lpfc_rcv_seq_check_edtov(struct lpfc_vport *); void lpfc_cleanup_rpis(struct lpfc_vport *, int); int lpfc_linkdown(struct lpfc_hba *); void lpfc_linkdown_port(struct lpfc_vport *); @@ -144,6 +146,8 @@ void lpfc_hb_timeout_handler(struct lpfc_hba *); void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, struct lpfc_iocbq *); +void lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t); int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int); void lpfc_fdmi_tmo(unsigned long); @@ -188,7 +192,7 @@ int lpfc_mbox_tmo_val(struct lpfc_hba *, int); void lpfc_init_vfi(struct lpfcMboxq *, struct lpfc_vport *); void lpfc_reg_vfi(struct lpfcMboxq *, struct lpfc_vport *, dma_addr_t); void lpfc_init_vpi(struct lpfc_hba *, struct lpfcMboxq *, uint16_t); -void lpfc_unreg_vfi(struct lpfcMboxq *, uint16_t); +void lpfc_unreg_vfi(struct lpfcMboxq *, struct lpfc_vport *); void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *); void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t); void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *); @@ -212,7 +216,10 @@ void lpfc_stop_vport_timers(struct lpfc_vport *); void lpfc_poll_timeout(unsigned long ptr); void lpfc_poll_start_timer(struct lpfc_hba *); void lpfc_poll_eratt(unsigned long); -void lpfc_sli_poll_fcp_ring(struct lpfc_hba *); +int +lpfc_sli_handle_fast_ring_event(struct lpfc_hba *, + struct lpfc_sli_ring *, uint32_t); + struct lpfc_iocbq * lpfc_sli_get_iocbq(struct lpfc_hba *); void lpfc_sli_release_iocbq(struct lpfc_hba *, struct lpfc_iocbq *); uint16_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_iocbq *); @@ -235,7 +242,7 @@ void lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *); int lpfc_sli_check_eratt(struct lpfc_hba *); void lpfc_sli_handle_slow_ring_event(struct lpfc_hba *, struct lpfc_sli_ring *, uint32_t); -int lpfc_sli4_handle_received_buffer(struct lpfc_hba *); +void lpfc_sli4_handle_received_buffer(struct lpfc_hba *, struct hbq_dmabuf *); void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *); int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, uint32_t); @@ -361,6 +368,7 @@ void lpfc_stop_port(struct lpfc_hba *); void lpfc_parse_fcoe_conf(struct lpfc_hba *, uint8_t *, uint32_t); int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int); void lpfc_start_fdiscs(struct lpfc_hba *phba); +struct lpfc_vport *lpfc_find_vport_by_vpid(struct lpfc_hba *, uint16_t); #define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) #define HBA_EVENT_RSCN 5 diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 9a1bd9534d74..0ebcd9baca79 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -31,6 +31,7 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/fc/fc_fs.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -87,7 +88,6 @@ void lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct lpfc_iocbq *piocbq) { - struct lpfc_dmabuf *mp = NULL; IOCB_t *icmd = &piocbq->iocb; int i; @@ -160,6 +160,39 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, } } +/** + * lpfc_sli4_ct_abort_unsol_event - Default handle for sli4 unsol abort + * @phba: Pointer to HBA context object. + * @pring: Pointer to the driver internal I/O ring. + * @piocbq: Pointer to the IOCBQ. + * + * This function serves as the default handler for the sli4 unsolicited + * abort event. It shall be invoked when there is no application interface + * registered unsolicited abort handler. This handler does nothing but + * just simply releases the dma buffer used by the unsol abort event. + **/ +void +lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *phba, + struct lpfc_sli_ring *pring, + struct lpfc_iocbq *piocbq) +{ + IOCB_t *icmd = &piocbq->iocb; + struct lpfc_dmabuf *bdeBuf; + uint32_t size; + + /* Forward abort event to any process registered to receive ct event */ + lpfc_bsg_ct_unsol_event(phba, pring, piocbq); + + /* If there is no BDE associated with IOCB, there is nothing to do */ + if (icmd->ulpBdeCount == 0) + return; + bdeBuf = piocbq->context2; + piocbq->context2 = NULL; + size = icmd->un.cont64[0].tus.f.bdeSize; + lpfc_ct_unsol_buffer(phba, piocbq, bdeBuf, size); + lpfc_in_buf_free(phba, bdeBuf); +} + static void lpfc_free_ct_rsp(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist) { @@ -304,8 +337,8 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp, /* Fill in rest of iocb */ icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); icmd->un.genreq64.w5.hcsw.Dfctl = 0; - icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL; - icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP; + icmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL; + icmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT; if (!tmo) { /* FC spec states we need 3 * ratov for CT requests */ @@ -363,9 +396,14 @@ lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp, outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt); if (!outmp) return -ENOMEM; - + /* + * Form the CT IOCB. The total number of BDEs in this IOCB + * is the single command plus response count from + * lpfc_alloc_ct_rsp. + */ + cnt += 1; status = lpfc_gen_req(vport, bmp, inmp, outmp, cmpl, ndlp, 0, - cnt+1, 0, retry); + cnt, 0, retry); if (status) { lpfc_free_ct_rsp(phba, outmp); return -ENOMEM; @@ -501,6 +539,9 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size) SLI_CTNS_GFF_ID, 0, Did) == 0) vport->num_disc_nodes++; + else + lpfc_setup_disc_node + (vport, Did); } else { lpfc_debugfs_disc_trc(vport, @@ -1209,7 +1250,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, be16_to_cpu(SLI_CTNS_RFF_ID); CtReq->un.rff.PortId = cpu_to_be32(vport->fc_myDID); CtReq->un.rff.fbits = FC4_FEATURE_INIT; - CtReq->un.rff.type_code = FC_FCP_DATA; + CtReq->un.rff.type_code = FC_TYPE_FCP; cmpl = lpfc_cmpl_ct_cmd_rff_id; break; } diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 8d0f0de76b63..391584183d81 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -926,7 +926,7 @@ lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file) goto out; /* Round to page boundry */ - printk(KERN_ERR "BLKGRD %s: _dump_buf_data=0x%p\n", + printk(KERN_ERR "9059 BLKGRD: %s: _dump_buf_data=0x%p\n", __func__, _dump_buf_data); debug->buffer = _dump_buf_data; if (!debug->buffer) { @@ -956,8 +956,8 @@ lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file) goto out; /* Round to page boundry */ - printk(KERN_ERR "BLKGRD %s: _dump_buf_dif=0x%p file=%s\n", __func__, - _dump_buf_dif, file->f_dentry->d_name.name); + printk(KERN_ERR "9060 BLKGRD: %s: _dump_buf_dif=0x%p file=%s\n", + __func__, _dump_buf_dif, file->f_dentry->d_name.name); debug->buffer = _dump_buf_dif; if (!debug->buffer) { kfree(debug); @@ -1377,7 +1377,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_dir(name, phba->hba_debugfs_root); if (!vport->vport_debugfs_root) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0417 Cant create debugfs"); + "0417 Cant create debugfs\n"); goto debug_failed; } atomic_inc(&phba->debugfs_vport_count); @@ -1430,7 +1430,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) vport, &lpfc_debugfs_op_nodelist); if (!vport->debug_nodelist) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0409 Cant create debugfs nodelist"); + "0409 Cant create debugfs nodelist\n"); goto debug_failed; } debug_failed: diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index 1142070e9484..2851d75ffc6f 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -19,7 +19,7 @@ *******************************************************************/ #define FC_MAX_HOLD_RSCN 32 /* max number of deferred RSCNs */ -#define FC_MAX_NS_RSP 65536 /* max size NameServer rsp */ +#define FC_MAX_NS_RSP 64512 /* max size NameServer rsp */ #define FC_MAXLOOP 126 /* max devices supported on a fc loop */ #define LPFC_DISC_FLOGI_TMO 10 /* Discovery FLOGI ratov */ @@ -105,8 +105,6 @@ struct lpfc_nodelist { struct lpfc_vport *vport; struct lpfc_work_evt els_retry_evt; struct lpfc_work_evt dev_loss_evt; - unsigned long last_ramp_up_time; /* jiffy of last ramp up */ - unsigned long last_q_full_time; /* jiffy of last queue full */ struct kref kref; atomic_t cmd_pending; uint32_t cmd_qdepth; diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 45337cd23feb..ce522702a6c1 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -173,13 +173,26 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp, * in FIP mode send FLOGI, FDISC and LOGO as FIP frames. */ if ((did == Fabric_DID) && - bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags) && + (phba->hba_flag & HBA_FIP_SUPPORT) && ((elscmd == ELS_CMD_FLOGI) || (elscmd == ELS_CMD_FDISC) || (elscmd == ELS_CMD_LOGO))) - elsiocb->iocb_flag |= LPFC_FIP_ELS; + switch (elscmd) { + case ELS_CMD_FLOGI: + elsiocb->iocb_flag |= ((ELS_ID_FLOGI << LPFC_FIP_ELS_ID_SHIFT) + & LPFC_FIP_ELS_ID_MASK); + break; + case ELS_CMD_FDISC: + elsiocb->iocb_flag |= ((ELS_ID_FDISC << LPFC_FIP_ELS_ID_SHIFT) + & LPFC_FIP_ELS_ID_MASK); + break; + case ELS_CMD_LOGO: + elsiocb->iocb_flag |= ((ELS_ID_LOGO << LPFC_FIP_ELS_ID_SHIFT) + & LPFC_FIP_ELS_ID_MASK); + break; + } else - elsiocb->iocb_flag &= ~LPFC_FIP_ELS; + elsiocb->iocb_flag &= ~LPFC_FIP_ELS_ID_MASK; icmd = &elsiocb->iocb; @@ -591,7 +604,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } else { ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); - if (vport->vfi_state & LPFC_VFI_REGISTERED) { + if (vport->vpi_state & LPFC_VPI_REGISTERED) { lpfc_start_fdiscs(phba); lpfc_do_scr_ns_plogi(phba, vport); } else @@ -802,7 +815,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* FLOGI completes successfully */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, - "0101 FLOGI completes sucessfully " + "0101 FLOGI completes successfully " "Data: x%x x%x x%x x%x\n", irsp->un.ulpWord[4], sp->cmn.e_d_tov, sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution); @@ -2452,6 +2465,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp) */ del_timer_sync(&ndlp->nlp_delayfunc); retry = ndlp->nlp_retry; + ndlp->nlp_retry = 0; switch (cmd) { case ELS_CMD_FLOGI: @@ -2711,12 +2725,16 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, !lpfc_error_lost_link(irsp)) { /* FLOGI retry policy */ retry = 1; - maxretry = 48; - if (cmdiocb->retry >= 32) + /* retry forever */ + maxretry = 0; + if (cmdiocb->retry >= 100) + delay = 5000; + else if (cmdiocb->retry >= 32) delay = 1000; } - if ((++cmdiocb->retry) >= maxretry) { + cmdiocb->retry++; + if (maxretry && (cmdiocb->retry >= maxretry)) { phba->fc_stat.elsRetryExceeded++; retry = 0; } @@ -4133,7 +4151,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, /* Indicate we are walking fc_rscn_id_list on this vport */ vport->fc_rscn_flush = 1; spin_unlock_irq(shost->host_lock); - /* Get the array count after sucessfully have the token */ + /* Get the array count after successfully have the token */ rscn_cnt = vport->fc_rscn_id_cnt; /* If we are already processing an RSCN, save the received * RSCN payload buffer, cmdiocb->context2 to process later. @@ -4503,6 +4521,29 @@ lpfc_els_rcv_lirr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, } /** + * lpfc_els_rcv_rrq - Process an unsolicited rrq iocb + * @vport: pointer to a host virtual N_Port data structure. + * @cmdiocb: pointer to lpfc command iocb data structure. + * @ndlp: pointer to a node-list data structure. + * + * This routine processes a Reinstate Recovery Qualifier (RRQ) IOCB + * received as an ELS unsolicited event. A request to RRQ shall only + * be accepted if the Originator Nx_Port N_Port_ID or the Responder + * Nx_Port N_Port_ID of the target Exchange is the same as the + * N_Port_ID of the Nx_Port that makes the request. If the RRQ is + * not accepted, an LS_RJT with reason code "Unable to perform + * command request" and reason code explanation "Invalid Originator + * S_ID" shall be returned. For now, we just unconditionally accept + * RRQ from the target. + **/ +static void +lpfc_els_rcv_rrq(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, + struct lpfc_nodelist *ndlp) +{ + lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); +} + +/** * lpfc_els_rsp_rps_acc - Completion callbk func for MBX_READ_LNK_STAT mbox cmd * @phba: pointer to lpfc hba data structure. * @pmb: pointer to the driver internal queue element for mailbox command. @@ -5396,7 +5437,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, if (lpfc_els_chk_latt(vport)) goto dropit; - /* Ignore traffic recevied during vport shutdown. */ + /* Ignore traffic received during vport shutdown. */ if (vport->load_flag & FC_UNLOADING) goto dropit; @@ -5618,6 +5659,16 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, if (newnode) lpfc_nlp_put(ndlp); break; + case ELS_CMD_RRQ: + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, + "RCV RRQ: did:x%x/ste:x%x flg:x%x", + did, vport->port_state, ndlp->nlp_flag); + + phba->fc_stat.elsRcvRRQ++; + lpfc_els_rcv_rrq(vport, elsiocb, ndlp); + if (newnode) + lpfc_nlp_put(ndlp); + break; default: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, "RCV ELS cmd: cmd:x%x did:x%x/ste:x%x", @@ -5670,7 +5721,7 @@ dropit: * NULL - No vport with the matching @vpi found * Otherwise - Address to the vport with the matching @vpi. **/ -static struct lpfc_vport * +struct lpfc_vport * lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi) { struct lpfc_vport *vport; @@ -6024,11 +6075,6 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, irsp->ulpStatus, irsp->un.ulpWord[4]); goto fdisc_failed; } - if (vport->fc_vport->vport_state == FC_VPORT_INITIALIZING) - lpfc_vport_set_state(vport, FC_VPORT_FAILED); - lpfc_nlp_put(ndlp); - /* giving up on FDISC. Cancel discovery timer */ - lpfc_can_disctmo(vport); spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_FABRIC; if (vport->phba->fc_topology == TOPOLOGY_LOOP) @@ -6107,6 +6153,7 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int did = ndlp->nlp_DID; int rc; + vport->port_state = LPFC_FDISC; cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm)); elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, did, ELS_CMD_FDISC); @@ -6172,7 +6219,6 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, return 1; } lpfc_vport_set_state(vport, FC_VPORT_INITIALIZING); - vport->port_state = LPFC_FDISC; return 0; } diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index e6a47e25b218..3b9424427652 100644..100755 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -525,8 +525,6 @@ lpfc_work_done(struct lpfc_hba *phba) spin_unlock_irq(&phba->hbalock); lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ); } - if (phba->hba_flag & HBA_RECEIVE_BUFFER) - lpfc_sli4_handle_received_buffer(phba); } vports = lpfc_create_vport_work_array(phba); @@ -568,8 +566,9 @@ lpfc_work_done(struct lpfc_hba *phba) pring = &phba->sli.ring[LPFC_ELS_RING]; status = (ha_copy & (HA_RXMASK << (4*LPFC_ELS_RING))); status >>= (4*LPFC_ELS_RING); - if ((status & HA_RXMASK) - || (pring->flag & LPFC_DEFERRED_RING_EVENT)) { + if ((status & HA_RXMASK) || + (pring->flag & LPFC_DEFERRED_RING_EVENT) || + (phba->hba_flag & HBA_SP_QUEUE_EVT)) { if (pring->flag & LPFC_STOP_IOCB_EVENT) { pring->flag |= LPFC_DEFERRED_RING_EVENT; /* Set the lpfc data pending flag */ @@ -688,7 +687,8 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove) lpfc_unreg_rpi(vport, ndlp); /* Leave Fabric nodes alone on link down */ - if (!remove && ndlp->nlp_type & NLP_FABRIC) + if ((phba->sli_rev < LPFC_SLI_REV4) && + (!remove && ndlp->nlp_type & NLP_FABRIC)) continue; rc = lpfc_disc_state_machine(vport, ndlp, NULL, remove @@ -706,6 +706,9 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove) void lpfc_port_link_failure(struct lpfc_vport *vport) { + /* Cleanup any outstanding received buffers */ + lpfc_cleanup_rcv_buffers(vport); + /* Cleanup any outstanding RSCN activity */ lpfc_els_flush_rscn(vport); @@ -1015,13 +1018,12 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) mempool_free(mboxq, phba->mbox_mem_pool); return; } - if (vport->port_state != LPFC_FLOGI) { - spin_lock_irqsave(&phba->hbalock, flags); - phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE); - phba->hba_flag &= ~FCF_DISC_INPROGRESS; - spin_unlock_irqrestore(&phba->hbalock, flags); + spin_lock_irqsave(&phba->hbalock, flags); + phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE); + phba->hba_flag &= ~FCF_DISC_INPROGRESS; + spin_unlock_irqrestore(&phba->hbalock, flags); + if (vport->port_state != LPFC_FLOGI) lpfc_initial_flogi(vport); - } mempool_free(mboxq, phba->mbox_mem_pool); return; @@ -1199,6 +1201,7 @@ lpfc_register_fcf(struct lpfc_hba *phba) /* If the FCF is not availabe do nothing. */ if (!(phba->fcf.fcf_flag & FCF_AVAILABLE)) { + phba->hba_flag &= ~FCF_DISC_INPROGRESS; spin_unlock_irqrestore(&phba->hbalock, flags); return; } @@ -1216,15 +1219,23 @@ lpfc_register_fcf(struct lpfc_hba *phba) fcf_mbxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); - if (!fcf_mbxq) + if (!fcf_mbxq) { + spin_lock_irqsave(&phba->hbalock, flags); + phba->hba_flag &= ~FCF_DISC_INPROGRESS; + spin_unlock_irqrestore(&phba->hbalock, flags); return; + } lpfc_reg_fcfi(phba, fcf_mbxq); fcf_mbxq->vport = phba->pport; fcf_mbxq->mbox_cmpl = lpfc_mbx_cmpl_reg_fcfi; rc = lpfc_sli_issue_mbox(phba, fcf_mbxq, MBX_NOWAIT); - if (rc == MBX_NOT_FINISHED) + if (rc == MBX_NOT_FINISHED) { + spin_lock_irqsave(&phba->hbalock, flags); + phba->hba_flag &= ~FCF_DISC_INPROGRESS; + spin_unlock_irqrestore(&phba->hbalock, flags); mempool_free(fcf_mbxq, phba->mbox_mem_pool); + } return; } @@ -1253,13 +1264,27 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba, uint16_t *vlan_id) { struct lpfc_fcf_conn_entry *conn_entry; + int i, j, fcf_vlan_id = 0; + + /* Find the lowest VLAN id in the FCF record */ + for (i = 0; i < 512; i++) { + if (new_fcf_record->vlan_bitmap[i]) { + fcf_vlan_id = i * 8; + j = 0; + while (!((new_fcf_record->vlan_bitmap[i] >> j) & 1)) { + j++; + fcf_vlan_id++; + } + break; + } + } /* If FCF not available return 0 */ if (!bf_get(lpfc_fcf_record_fcf_avail, new_fcf_record) || !bf_get(lpfc_fcf_record_fcf_valid, new_fcf_record)) return 0; - if (!phba->cfg_enable_fip) { + if (!(phba->hba_flag & HBA_FIP_SUPPORT)) { *boot_flag = 0; *addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov, new_fcf_record); @@ -1286,7 +1311,11 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba, if (*addr_mode & LPFC_FCF_FPMA) *addr_mode = LPFC_FCF_FPMA; - *vlan_id = 0xFFFF; + /* If FCF record report a vlan id use that vlan id */ + if (fcf_vlan_id) + *vlan_id = fcf_vlan_id; + else + *vlan_id = 0xFFFF; return 1; } @@ -1384,8 +1413,15 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba, (*addr_mode & LPFC_FCF_FPMA)) *addr_mode = LPFC_FCF_FPMA; + /* If matching connect list has a vlan id, use it */ if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID) *vlan_id = conn_entry->conn_rec.vlan_tag; + /* + * If no vlan id is specified in connect list, use the vlan id + * in the FCF record + */ + else if (fcf_vlan_id) + *vlan_id = fcf_vlan_id; else *vlan_id = 0xFFFF; @@ -1423,6 +1459,15 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf) if (phba->link_state >= LPFC_LINK_UP) lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST); + else { + /* + * Do not continue FCF discovery and clear FCF_DISC_INPROGRESS + * flag + */ + spin_lock_irq(&phba->hbalock); + phba->hba_flag &= ~FCF_DISC_INPROGRESS; + spin_unlock_irq(&phba->hbalock); + } if (unreg_fcf) { spin_lock_irq(&phba->hbalock); @@ -1659,9 +1704,8 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) lpfc_initial_fdisc(vport); else { lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP); - lpfc_printf_vlog(vport, KERN_ERR, - LOG_ELS, - "2606 No NPIV Fabric support\n"); + lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, + "2606 No NPIV Fabric support\n"); } return; } @@ -1756,8 +1800,8 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) lpfc_vport_set_state(vport, FC_VPORT_FAILED); goto fail_free_mem; } - /* Mark the vport has registered with its VFI */ - vport->vfi_state |= LPFC_VFI_REGISTERED; + /* The VPI is implicitly registered when the VFI is registered */ + vport->vpi_state |= LPFC_VPI_REGISTERED; if (vport->port_state == LPFC_FABRIC_CFG_LINK) { lpfc_start_fdiscs(phba); @@ -1861,7 +1905,10 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la) if (phba->fc_topology == TOPOLOGY_LOOP) { phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED; - if (phba->cfg_enable_npiv) + /* if npiv is enabled and this adapter supports npiv log + * a message that npiv is not supported in this topology + */ + if (phba->cfg_enable_npiv && phba->max_vpi) lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, "1309 Link Up Event npiv not supported in loop " "topology\n"); @@ -1955,7 +2002,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la) * is phase 1 implementation that support FCF index 0 and driver * defaults. */ - if (phba->cfg_enable_fip == 0) { + if (!(phba->hba_flag & HBA_FIP_SUPPORT)) { fcf_record = kzalloc(sizeof(struct fcf_record), GFP_KERNEL); if (unlikely(!fcf_record)) { @@ -2085,6 +2132,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) else phba->sli.sli_flag &= ~LPFC_MENLO_MAINT; + phba->link_events++; if (la->attType == AT_LINK_UP && (!la->mm)) { phba->fc_stat.LinkUp++; if (phba->link_flag & LS_LOOPBACK_MODE) { @@ -2211,13 +2259,14 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) mb->mbxStatus); break; } + vport->vpi_state &= ~LPFC_VPI_REGISTERED; vport->unreg_vpi_cmpl = VPORT_OK; mempool_free(pmb, phba->mbox_mem_pool); /* * This shost reference might have been taken at the beginning of * lpfc_vport_delete() */ - if (vport->load_flag & FC_UNLOADING) + if ((vport->load_flag & FC_UNLOADING) && (vport != phba->pport)) scsi_host_put(shost); } @@ -2268,6 +2317,7 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) goto out; } + vport->vpi_state |= LPFC_VPI_REGISTERED; vport->num_disc_nodes = 0; /* go thru NPR list and issue ELS PLOGIs */ if (vport->fc_npr_cnt) @@ -3077,7 +3127,7 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) struct lpfc_sli *psli; struct lpfc_sli_ring *pring; struct lpfc_iocbq *iocb, *next_iocb; - uint32_t rpi, i; + uint32_t i; lpfc_fabric_abort_nport(ndlp); @@ -3086,7 +3136,6 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) * by firmware with a no rpi error. */ psli = &phba->sli; - rpi = ndlp->nlp_rpi; if (ndlp->nlp_flag & NLP_RPI_VALID) { /* Now process each ring */ for (i = 0; i < psli->num_rings; i++) { @@ -4322,6 +4371,14 @@ lpfc_fcf_inuse(struct lpfc_hba *phba) ret = 1; spin_unlock_irq(shost->host_lock); goto out; + } else { + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "2624 RPI %x DID %x flg %x still " + "logged in\n", + ndlp->nlp_rpi, ndlp->nlp_DID, + ndlp->nlp_flag); + if (ndlp->nlp_flag & NLP_RPI_VALID) + ret = 1; } } spin_unlock_irq(shost->host_lock); @@ -4400,7 +4457,7 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba) */ if (!(phba->hba_flag & HBA_FCOE_SUPPORT) || !(phba->fcf.fcf_flag & FCF_REGISTERED) || - (phba->cfg_enable_fip == 0)) { + (!(phba->hba_flag & HBA_FIP_SUPPORT))) { spin_unlock_irq(&phba->hbalock); return; } @@ -4409,6 +4466,8 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba) if (lpfc_fcf_inuse(phba)) return; + /* At this point, all discovery is aborted */ + phba->pport->port_state = LPFC_VPORT_UNKNOWN; /* Unregister VPIs */ vports = lpfc_create_vport_work_array(phba); @@ -4416,8 +4475,8 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba) (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { lpfc_mbx_unreg_vpi(vports[i]); - vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI; - vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED; + vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI; + vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED; } lpfc_destroy_vport_work_array(phba, vports); @@ -4431,7 +4490,7 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba) return; } - lpfc_unreg_vfi(mbox, phba->pport->vfi); + lpfc_unreg_vfi(mbox, phba->pport); mbox->vport = phba->pport; mbox->mbox_cmpl = lpfc_unregister_vfi_cmpl; @@ -4512,8 +4571,10 @@ lpfc_read_fcf_conn_tbl(struct lpfc_hba *phba, /* Free the current connect table */ list_for_each_entry_safe(conn_entry, next_conn_entry, - &phba->fcf_conn_rec_list, list) + &phba->fcf_conn_rec_list, list) { + list_del_init(&conn_entry->list); kfree(conn_entry); + } conn_hdr = (struct lpfc_fcf_conn_hdr *) buff; record_count = conn_hdr->length * sizeof(uint32_t)/ @@ -4569,14 +4630,6 @@ lpfc_read_fcoe_param(struct lpfc_hba *phba, (fcoe_param_hdr->length != FCOE_PARAM_LENGTH)) return; - if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) == - FIPP_MODE_ON) - phba->cfg_enable_fip = 1; - - if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) == - FIPP_MODE_OFF) - phba->cfg_enable_fip = 0; - if (fcoe_param_hdr->parm_flags & FIPP_VLAN_VALID) { phba->valid_vlan = 1; phba->vlan_id = le16_to_cpu(fcoe_param->vlan_tag) & diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index ccb26724dc53..c9faa1d8c3c8 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1124,21 +1124,6 @@ typedef struct { /* Number of 4-byte words in an IOCB. */ #define IOCB_WORD_SZ 8 -/* defines for type field in fc header */ -#define FC_ELS_DATA 0x1 -#define FC_LLC_SNAP 0x5 -#define FC_FCP_DATA 0x8 -#define FC_COMMON_TRANSPORT_ULP 0x20 - -/* defines for rctl field in fc header */ -#define FC_DEV_DATA 0x0 -#define FC_UNSOL_CTL 0x2 -#define FC_SOL_CTL 0x3 -#define FC_UNSOL_DATA 0x4 -#define FC_FCP_CMND 0x6 -#define FC_ELS_REQ 0x22 -#define FC_ELS_RSP 0x23 - /* network headers for Dfctl field */ #define FC_NET_HDR 0x20 @@ -1183,6 +1168,8 @@ typedef struct { #define PCI_DEVICE_ID_ZEPHYR_DCSP 0xfe12 #define PCI_VENDOR_ID_SERVERENGINE 0x19a2 #define PCI_DEVICE_ID_TIGERSHARK 0x0704 +#define PCI_DEVICE_ID_TOMCAT 0x0714 +#define PCI_DEVICE_ID_FALCON 0xf180 #define JEDEC_ID_ADDRESS 0x0080001c #define FIREFLY_JEDEC_ID 0x1ACC @@ -1444,6 +1431,7 @@ typedef struct { /* FireFly BIU registers */ #define CMD_ABORT_MXRI64_CN 0x8C #define CMD_RCV_ELS_REQ64_CX 0x8D #define CMD_XMIT_ELS_RSP64_CX 0x95 +#define CMD_XMIT_BLS_RSP64_CX 0x97 #define CMD_FCP_IWRITE64_CR 0x98 #define CMD_FCP_IWRITE64_CX 0x99 #define CMD_FCP_IREAD64_CR 0x9A @@ -2306,8 +2294,7 @@ typedef struct { uint32_t rsvd1; uint32_t rsvd2:8; uint32_t sid:24; - uint32_t rsvd3; - uint32_t rsvd4; + uint32_t wwn[2]; uint32_t rsvd5; uint16_t vfi; uint16_t vpi; @@ -2315,8 +2302,7 @@ typedef struct { uint32_t rsvd1; uint32_t sid:24; uint32_t rsvd2:8; - uint32_t rsvd3; - uint32_t rsvd4; + uint32_t wwn[2]; uint32_t rsvd5; uint16_t vpi; uint16_t vfi; @@ -2326,7 +2312,13 @@ typedef struct { /* Structure for MB Command UNREG_VPI (0x97) */ typedef struct { uint32_t rsvd1; - uint32_t rsvd2; +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd2; + uint16_t sli4_vpi; +#else /* __LITTLE_ENDIAN */ + uint16_t sli4_vpi; + uint16_t rsvd2; +#endif uint32_t rsvd3; uint32_t rsvd4; uint32_t rsvd5; @@ -3547,7 +3539,7 @@ typedef struct _IOCB { /* IOCB structure */ ASYNCSTAT_FIELDS asyncstat; /* async_status iocb */ QUE_XRI64_CX_FIELDS quexri64cx; /* que_xri64_cx fields */ struct rcv_seq64 rcvseq64; /* RCV_SEQ64 and RCV_CONT64 */ - + struct sli4_bls_acc bls_acc; /* UNSOL ABTS BLS_ACC params */ uint32_t ulpWord[IOCB_WORD_SZ - 2]; /* generic 6 'words' */ } un; union { diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 3689eee04535..1585148a17e5 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -194,6 +194,26 @@ struct lpfc_sli4_flags { #define lpfc_fip_flag_WORD word0 }; +struct sli4_bls_acc { + uint32_t word0_rsvd; /* Word0 must be reserved */ + uint32_t word1; +#define lpfc_abts_orig_SHIFT 0 +#define lpfc_abts_orig_MASK 0x00000001 +#define lpfc_abts_orig_WORD word1 +#define LPFC_ABTS_UNSOL_RSP 1 +#define LPFC_ABTS_UNSOL_INT 0 + uint32_t word2; +#define lpfc_abts_rxid_SHIFT 0 +#define lpfc_abts_rxid_MASK 0x0000FFFF +#define lpfc_abts_rxid_WORD word2 +#define lpfc_abts_oxid_SHIFT 16 +#define lpfc_abts_oxid_MASK 0x0000FFFF +#define lpfc_abts_oxid_WORD word2 + uint32_t word3; + uint32_t word4; + uint32_t word5_rsvd; /* Word5 must be reserved */ +}; + /* event queue entry structure */ struct lpfc_eqe { uint32_t word0; @@ -425,7 +445,7 @@ struct lpfc_wqe_generic{ #define lpfc_wqe_gen_status_MASK 0x0000000F #define lpfc_wqe_gen_status_WORD word7 #define lpfc_wqe_gen_ct_SHIFT 2 -#define lpfc_wqe_gen_ct_MASK 0x00000007 +#define lpfc_wqe_gen_ct_MASK 0x00000003 #define lpfc_wqe_gen_ct_WORD word7 uint32_t abort_tag; uint32_t word9; @@ -453,6 +473,13 @@ struct lpfc_wqe_generic{ #define lpfc_wqe_gen_wqec_SHIFT 7 #define lpfc_wqe_gen_wqec_MASK 0x00000001 #define lpfc_wqe_gen_wqec_WORD word11 +#define ELS_ID_FLOGI 3 +#define ELS_ID_FDISC 2 +#define ELS_ID_LOGO 1 +#define ELS_ID_DEFAULT 0 +#define lpfc_wqe_gen_els_id_SHIFT 4 +#define lpfc_wqe_gen_els_id_MASK 0x00000003 +#define lpfc_wqe_gen_els_id_WORD word11 #define lpfc_wqe_gen_cmd_type_SHIFT 0 #define lpfc_wqe_gen_cmd_type_MASK 0x0000000F #define lpfc_wqe_gen_cmd_type_WORD word11 @@ -487,8 +514,8 @@ struct lpfc_register { #define LPFC_UERR_STATUS_HI 0x00A4 #define LPFC_UERR_STATUS_LO 0x00A0 -#define LPFC_ONLINE0 0x00B0 -#define LPFC_ONLINE1 0x00B4 +#define LPFC_UE_MASK_HI 0x00AC +#define LPFC_UE_MASK_LO 0x00A8 #define LPFC_SCRATCHPAD 0x0058 /* BAR0 Registers */ @@ -760,6 +787,7 @@ struct mbox_header { #define LPFC_MBOX_OPCODE_MQ_DESTROY 0x35 #define LPFC_MBOX_OPCODE_CQ_DESTROY 0x36 #define LPFC_MBOX_OPCODE_EQ_DESTROY 0x37 +#define LPFC_MBOX_OPCODE_QUERY_FW_CFG 0x3A #define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D /* FCoE Opcodes */ @@ -1273,6 +1301,51 @@ struct lpfc_mbx_del_fcf_tbl_entry { #define lpfc_mbx_del_fcf_tbl_index_WORD word10 }; +struct lpfc_mbx_query_fw_cfg { + struct mbox_header header; + uint32_t config_number; + uint32_t asic_rev; + uint32_t phys_port; + uint32_t function_mode; +/* firmware Function Mode */ +#define lpfc_function_mode_toe_SHIFT 0 +#define lpfc_function_mode_toe_MASK 0x00000001 +#define lpfc_function_mode_toe_WORD function_mode +#define lpfc_function_mode_nic_SHIFT 1 +#define lpfc_function_mode_nic_MASK 0x00000001 +#define lpfc_function_mode_nic_WORD function_mode +#define lpfc_function_mode_rdma_SHIFT 2 +#define lpfc_function_mode_rdma_MASK 0x00000001 +#define lpfc_function_mode_rdma_WORD function_mode +#define lpfc_function_mode_vm_SHIFT 3 +#define lpfc_function_mode_vm_MASK 0x00000001 +#define lpfc_function_mode_vm_WORD function_mode +#define lpfc_function_mode_iscsi_i_SHIFT 4 +#define lpfc_function_mode_iscsi_i_MASK 0x00000001 +#define lpfc_function_mode_iscsi_i_WORD function_mode +#define lpfc_function_mode_iscsi_t_SHIFT 5 +#define lpfc_function_mode_iscsi_t_MASK 0x00000001 +#define lpfc_function_mode_iscsi_t_WORD function_mode +#define lpfc_function_mode_fcoe_i_SHIFT 6 +#define lpfc_function_mode_fcoe_i_MASK 0x00000001 +#define lpfc_function_mode_fcoe_i_WORD function_mode +#define lpfc_function_mode_fcoe_t_SHIFT 7 +#define lpfc_function_mode_fcoe_t_MASK 0x00000001 +#define lpfc_function_mode_fcoe_t_WORD function_mode +#define lpfc_function_mode_dal_SHIFT 8 +#define lpfc_function_mode_dal_MASK 0x00000001 +#define lpfc_function_mode_dal_WORD function_mode +#define lpfc_function_mode_lro_SHIFT 9 +#define lpfc_function_mode_lro_MASK 0x00000001 +#define lpfc_function_mode_lro_WORD function_mode9 +#define lpfc_function_mode_flex10_SHIFT 10 +#define lpfc_function_mode_flex10_MASK 0x00000001 +#define lpfc_function_mode_flex10_WORD function_mode +#define lpfc_function_mode_ncsi_SHIFT 11 +#define lpfc_function_mode_ncsi_MASK 0x00000001 +#define lpfc_function_mode_ncsi_WORD function_mode +}; + /* Status field for embedded SLI_CONFIG mailbox command */ #define STATUS_SUCCESS 0x0 #define STATUS_FAILED 0x1 @@ -1349,8 +1422,7 @@ struct lpfc_mbx_reg_vfi { #define lpfc_reg_vfi_fcfi_SHIFT 0 #define lpfc_reg_vfi_fcfi_MASK 0x0000FFFF #define lpfc_reg_vfi_fcfi_WORD word2 - uint32_t word3_rsvd; - uint32_t word4_rsvd; + uint32_t wwn[2]; struct ulp_bde64 bde; uint32_t word8_rsvd; uint32_t word9_rsvd; @@ -1555,6 +1627,11 @@ struct lpfc_mbx_read_rev { #define lpfc_mbx_rd_rev_fcoe_SHIFT 20 #define lpfc_mbx_rd_rev_fcoe_MASK 0x00000001 #define lpfc_mbx_rd_rev_fcoe_WORD word1 +#define lpfc_mbx_rd_rev_cee_ver_SHIFT 21 +#define lpfc_mbx_rd_rev_cee_ver_MASK 0x00000003 +#define lpfc_mbx_rd_rev_cee_ver_WORD word1 +#define LPFC_PREDCBX_CEE_MODE 0 +#define LPFC_DCBX_CEE_MODE 1 #define lpfc_mbx_rd_rev_vpd_SHIFT 29 #define lpfc_mbx_rd_rev_vpd_MASK 0x00000001 #define lpfc_mbx_rd_rev_vpd_WORD word1 @@ -1804,6 +1881,7 @@ struct lpfc_mqe { struct lpfc_mbx_read_config rd_config; struct lpfc_mbx_request_features req_ftrs; struct lpfc_mbx_post_hdr_tmpl hdr_tmpl; + struct lpfc_mbx_query_fw_cfg query_fw_cfg; struct lpfc_mbx_nop nop; } un; }; @@ -1885,7 +1963,7 @@ struct lpfc_acqe_link { }; struct lpfc_acqe_fcoe { - uint32_t fcf_index; + uint32_t index; uint32_t word1; #define lpfc_acqe_fcoe_fcf_count_SHIFT 0 #define lpfc_acqe_fcoe_fcf_count_MASK 0x0000FFFF @@ -1896,6 +1974,7 @@ struct lpfc_acqe_fcoe { #define LPFC_FCOE_EVENT_TYPE_NEW_FCF 0x1 #define LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL 0x2 #define LPFC_FCOE_EVENT_TYPE_FCF_DEAD 0x3 +#define LPFC_FCOE_EVENT_TYPE_CVL 0x4 uint32_t event_tag; uint32_t trailer; }; @@ -1921,12 +2000,13 @@ struct lpfc_bmbx_create { #define SGL_ALIGN_SZ 64 #define SGL_PAGE_SIZE 4096 /* align SGL addr on a size boundary - adjust address up */ -#define NO_XRI ((uint16_t)-1) +#define NO_XRI ((uint16_t)-1) + struct wqe_common { uint32_t word6; -#define wqe_xri_SHIFT 0 -#define wqe_xri_MASK 0x0000FFFF -#define wqe_xri_WORD word6 +#define wqe_xri_tag_SHIFT 0 +#define wqe_xri_tag_MASK 0x0000FFFF +#define wqe_xri_tag_WORD word6 #define wqe_ctxt_tag_SHIFT 16 #define wqe_ctxt_tag_MASK 0x0000FFFF #define wqe_ctxt_tag_WORD word6 @@ -1987,7 +2067,7 @@ struct wqe_common { #define wqe_wqec_MASK 0x00000001 #define wqe_wqec_WORD word11 #define wqe_cqid_SHIFT 16 -#define wqe_cqid_MASK 0x000003ff +#define wqe_cqid_MASK 0x0000ffff #define wqe_cqid_WORD word11 }; @@ -1996,6 +2076,9 @@ struct wqe_did { #define wqe_els_did_SHIFT 0 #define wqe_els_did_MASK 0x00FFFFFF #define wqe_els_did_WORD word5 +#define wqe_xmit_bls_pt_SHIFT 28 +#define wqe_xmit_bls_pt_MASK 0x00000003 +#define wqe_xmit_bls_pt_WORD word5 #define wqe_xmit_bls_ar_SHIFT 30 #define wqe_xmit_bls_ar_MASK 0x00000001 #define wqe_xmit_bls_ar_WORD word5 @@ -2044,6 +2127,23 @@ struct xmit_els_rsp64_wqe { struct xmit_bls_rsp64_wqe { uint32_t payload0; +/* Payload0 for BA_ACC */ +#define xmit_bls_rsp64_acc_seq_id_SHIFT 16 +#define xmit_bls_rsp64_acc_seq_id_MASK 0x000000ff +#define xmit_bls_rsp64_acc_seq_id_WORD payload0 +#define xmit_bls_rsp64_acc_seq_id_vald_SHIFT 24 +#define xmit_bls_rsp64_acc_seq_id_vald_MASK 0x000000ff +#define xmit_bls_rsp64_acc_seq_id_vald_WORD payload0 +/* Payload0 for BA_RJT */ +#define xmit_bls_rsp64_rjt_vspec_SHIFT 0 +#define xmit_bls_rsp64_rjt_vspec_MASK 0x000000ff +#define xmit_bls_rsp64_rjt_vspec_WORD payload0 +#define xmit_bls_rsp64_rjt_expc_SHIFT 8 +#define xmit_bls_rsp64_rjt_expc_MASK 0x000000ff +#define xmit_bls_rsp64_rjt_expc_WORD payload0 +#define xmit_bls_rsp64_rjt_rsnc_SHIFT 16 +#define xmit_bls_rsp64_rjt_rsnc_MASK 0x000000ff +#define xmit_bls_rsp64_rjt_rsnc_WORD payload0 uint32_t word1; #define xmit_bls_rsp64_rxid_SHIFT 0 #define xmit_bls_rsp64_rxid_MASK 0x0000ffff @@ -2052,18 +2152,19 @@ struct xmit_bls_rsp64_wqe { #define xmit_bls_rsp64_oxid_MASK 0x0000ffff #define xmit_bls_rsp64_oxid_WORD word1 uint32_t word2; -#define xmit_bls_rsp64_seqcntlo_SHIFT 0 -#define xmit_bls_rsp64_seqcntlo_MASK 0x0000ffff -#define xmit_bls_rsp64_seqcntlo_WORD word2 -#define xmit_bls_rsp64_seqcnthi_SHIFT 16 +#define xmit_bls_rsp64_seqcnthi_SHIFT 0 #define xmit_bls_rsp64_seqcnthi_MASK 0x0000ffff #define xmit_bls_rsp64_seqcnthi_WORD word2 +#define xmit_bls_rsp64_seqcntlo_SHIFT 16 +#define xmit_bls_rsp64_seqcntlo_MASK 0x0000ffff +#define xmit_bls_rsp64_seqcntlo_WORD word2 uint32_t rsrvd3; uint32_t rsrvd4; struct wqe_did wqe_dest; struct wqe_common wqe_com; /* words 6-11 */ uint32_t rsvd_12_15[4]; }; + struct wqe_rctl_dfctl { uint32_t word5; #define wqe_si_SHIFT 2 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 562d8cee874b..226920d15ea1 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -28,6 +28,7 @@ #include <linux/pci.h> #include <linux/spinlock.h> #include <linux/ctype.h> +#include <linux/aer.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> @@ -645,7 +646,7 @@ lpfc_hba_down_prep(struct lpfc_hba *phba) * down the SLI Layer. * * Return codes - * 0 - sucess. + * 0 - success. * Any other value - error. **/ static int @@ -700,7 +701,7 @@ lpfc_hba_down_post_s3(struct lpfc_hba *phba) * down the SLI Layer. * * Return codes - * 0 - sucess. + * 0 - success. * Any other value - error. **/ static int @@ -755,7 +756,7 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) * uninitialization after the HBA is reset when bring down the SLI Layer. * * Return codes - * 0 - sucess. + * 0 - success. * Any other value - error. **/ int @@ -852,12 +853,19 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq) void lpfc_hb_timeout_handler(struct lpfc_hba *phba) { + struct lpfc_vport **vports; LPFC_MBOXQ_t *pmboxq; struct lpfc_dmabuf *buf_ptr; - int retval; + int retval, i; struct lpfc_sli *psli = &phba->sli; LIST_HEAD(completions); + vports = lpfc_create_vport_work_array(phba); + if (vports != NULL) + for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) + lpfc_rcv_seq_check_edtov(vports[i]); + lpfc_destroy_vport_work_array(phba, vports); + if ((phba->link_state == LPFC_HBA_ERROR) || (phba->pport->load_flag & FC_UNLOADING) || (phba->pport->fc_flag & FC_OFFLINE_MODE)) @@ -1254,7 +1262,7 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba) * routine from the API jump table function pointer from the lpfc_hba struct. * * Return codes - * 0 - sucess. + * 0 - success. * Any other value - error. **/ void @@ -1521,10 +1529,10 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) int GE = 0; int oneConnect = 0; /* default is not a oneConnect */ struct { - char * name; - int max_speed; - char * bus; - } m = {"<Unknown>", 0, ""}; + char *name; + char *bus; + char *function; + } m = {"<Unknown>", "", ""}; if (mdp && mdp[0] != '\0' && descp && descp[0] != '\0') @@ -1545,132 +1553,155 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) switch (dev_id) { case PCI_DEVICE_ID_FIREFLY: - m = (typeof(m)){"LP6000", max_speed, "PCI"}; + m = (typeof(m)){"LP6000", "PCI", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SUPERFLY: if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3) - m = (typeof(m)){"LP7000", max_speed, "PCI"}; + m = (typeof(m)){"LP7000", "PCI", + "Fibre Channel Adapter"}; else - m = (typeof(m)){"LP7000E", max_speed, "PCI"}; + m = (typeof(m)){"LP7000E", "PCI", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_DRAGONFLY: - m = (typeof(m)){"LP8000", max_speed, "PCI"}; + m = (typeof(m)){"LP8000", "PCI", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_CENTAUR: if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID) - m = (typeof(m)){"LP9002", max_speed, "PCI"}; + m = (typeof(m)){"LP9002", "PCI", + "Fibre Channel Adapter"}; else - m = (typeof(m)){"LP9000", max_speed, "PCI"}; + m = (typeof(m)){"LP9000", "PCI", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_RFLY: - m = (typeof(m)){"LP952", max_speed, "PCI"}; + m = (typeof(m)){"LP952", "PCI", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_PEGASUS: - m = (typeof(m)){"LP9802", max_speed, "PCI-X"}; + m = (typeof(m)){"LP9802", "PCI-X", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_THOR: - m = (typeof(m)){"LP10000", max_speed, "PCI-X"}; + m = (typeof(m)){"LP10000", "PCI-X", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_VIPER: - m = (typeof(m)){"LPX1000", max_speed, "PCI-X"}; + m = (typeof(m)){"LPX1000", "PCI-X", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_PFLY: - m = (typeof(m)){"LP982", max_speed, "PCI-X"}; + m = (typeof(m)){"LP982", "PCI-X", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_TFLY: - m = (typeof(m)){"LP1050", max_speed, "PCI-X"}; + m = (typeof(m)){"LP1050", "PCI-X", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_HELIOS: - m = (typeof(m)){"LP11000", max_speed, "PCI-X2"}; + m = (typeof(m)){"LP11000", "PCI-X2", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_HELIOS_SCSP: - m = (typeof(m)){"LP11000-SP", max_speed, "PCI-X2"}; + m = (typeof(m)){"LP11000-SP", "PCI-X2", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_HELIOS_DCSP: - m = (typeof(m)){"LP11002-SP", max_speed, "PCI-X2"}; + m = (typeof(m)){"LP11002-SP", "PCI-X2", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_NEPTUNE: - m = (typeof(m)){"LPe1000", max_speed, "PCIe"}; + m = (typeof(m)){"LPe1000", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_NEPTUNE_SCSP: - m = (typeof(m)){"LPe1000-SP", max_speed, "PCIe"}; + m = (typeof(m)){"LPe1000-SP", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_NEPTUNE_DCSP: - m = (typeof(m)){"LPe1002-SP", max_speed, "PCIe"}; + m = (typeof(m)){"LPe1002-SP", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_BMID: - m = (typeof(m)){"LP1150", max_speed, "PCI-X2"}; + m = (typeof(m)){"LP1150", "PCI-X2", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_BSMB: - m = (typeof(m)){"LP111", max_speed, "PCI-X2"}; + m = (typeof(m)){"LP111", "PCI-X2", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_ZEPHYR: - m = (typeof(m)){"LPe11000", max_speed, "PCIe"}; + m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_ZEPHYR_SCSP: - m = (typeof(m)){"LPe11000", max_speed, "PCIe"}; + m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_ZEPHYR_DCSP: - m = (typeof(m)){"LP2105", max_speed, "PCIe"}; + m = (typeof(m)){"LP2105", "PCIe", "FCoE Adapter"}; GE = 1; break; case PCI_DEVICE_ID_ZMID: - m = (typeof(m)){"LPe1150", max_speed, "PCIe"}; + m = (typeof(m)){"LPe1150", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_ZSMB: - m = (typeof(m)){"LPe111", max_speed, "PCIe"}; + m = (typeof(m)){"LPe111", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LP101: - m = (typeof(m)){"LP101", max_speed, "PCI-X"}; + m = (typeof(m)){"LP101", "PCI-X", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LP10000S: - m = (typeof(m)){"LP10000-S", max_speed, "PCI"}; + m = (typeof(m)){"LP10000-S", "PCI", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LP11000S: - m = (typeof(m)){"LP11000-S", max_speed, - "PCI-X2"}; + m = (typeof(m)){"LP11000-S", "PCI-X2", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LPE11000S: - m = (typeof(m)){"LPe11000-S", max_speed, - "PCIe"}; + m = (typeof(m)){"LPe11000-S", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SAT: - m = (typeof(m)){"LPe12000", max_speed, "PCIe"}; + m = (typeof(m)){"LPe12000", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SAT_MID: - m = (typeof(m)){"LPe1250", max_speed, "PCIe"}; + m = (typeof(m)){"LPe1250", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SAT_SMB: - m = (typeof(m)){"LPe121", max_speed, "PCIe"}; + m = (typeof(m)){"LPe121", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SAT_DCSP: - m = (typeof(m)){"LPe12002-SP", max_speed, "PCIe"}; + m = (typeof(m)){"LPe12002-SP", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SAT_SCSP: - m = (typeof(m)){"LPe12000-SP", max_speed, "PCIe"}; + m = (typeof(m)){"LPe12000-SP", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_SAT_S: - m = (typeof(m)){"LPe12000-S", max_speed, "PCIe"}; + m = (typeof(m)){"LPe12000-S", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_HORNET: - m = (typeof(m)){"LP21000", max_speed, "PCIe"}; + m = (typeof(m)){"LP21000", "PCIe", "FCoE Adapter"}; GE = 1; break; case PCI_DEVICE_ID_PROTEUS_VF: - m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"}; + m = (typeof(m)){"LPev12000", "PCIe IOV", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_PROTEUS_PF: - m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"}; + m = (typeof(m)){"LPev12000", "PCIe IOV", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_PROTEUS_S: - m = (typeof(m)) {"LPemv12002-S", max_speed, "PCIe IOV"}; + m = (typeof(m)){"LPemv12002-S", "PCIe IOV", + "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_TIGERSHARK: oneConnect = 1; - m = (typeof(m)) {"OCe10100-F", max_speed, "PCIe"}; + m = (typeof(m)){"OCe10100", "PCIe", "FCoE"}; + break; + case PCI_DEVICE_ID_TOMCAT: + oneConnect = 1; + m = (typeof(m)){"OCe11100", "PCIe", "FCoE"}; + break; + case PCI_DEVICE_ID_FALCON: + m = (typeof(m)){"LPSe12002-ML1-E", "PCIe", + "EmulexSecure Fibre"}; break; default: - m = (typeof(m)){ NULL }; + m = (typeof(m)){"Unknown", "", ""}; break; } @@ -1682,17 +1713,14 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) if (descp && descp[0] == '\0') { if (oneConnect) snprintf(descp, 255, - "Emulex OneConnect %s, FCoE Initiator, Port %s", - m.name, + "Emulex OneConnect %s, %s Initiator, Port %s", + m.name, m.function, phba->Port); else snprintf(descp, 255, "Emulex %s %d%s %s %s", - m.name, m.max_speed, - (GE) ? "GE" : "Gb", - m.bus, - (GE) ? "FCoE Adapter" : - "Fibre Channel Adapter"); + m.name, max_speed, (GE) ? "GE" : "Gb", + m.bus, m.function); } } @@ -2217,7 +2245,7 @@ lpfc_offline_prep(struct lpfc_hba * phba) if (vports[i]->load_flag & FC_UNLOADING) continue; - vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED; + vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED; shost = lpfc_shost_from_vport(vports[i]); list_for_each_entry_safe(ndlp, next_ndlp, &vports[i]->fc_nodes, @@ -2308,6 +2336,7 @@ lpfc_scsi_free(struct lpfc_hba *phba) spin_lock_irq(&phba->hbalock); /* Release all the lpfc_scsi_bufs maintained by this host. */ + spin_lock(&phba->scsi_buf_list_lock); list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) { list_del(&sb->list); pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data, @@ -2315,6 +2344,7 @@ lpfc_scsi_free(struct lpfc_hba *phba) kfree(sb); phba->total_scsi_bufs--; } + spin_unlock(&phba->scsi_buf_list_lock); /* Release all the lpfc_iocbq entries maintained by this host. */ list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) { @@ -2322,9 +2352,7 @@ lpfc_scsi_free(struct lpfc_hba *phba) kfree(io); phba->total_iocbq_bufs--; } - spin_unlock_irq(&phba->hbalock); - return 0; } @@ -2408,7 +2436,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) vport->els_tmofunc.function = lpfc_els_timeout; vport->els_tmofunc.data = (unsigned long)vport; - error = scsi_add_host(shost, dev); + error = scsi_add_host_with_dma(shost, dev, &phba->pcidev->dev); if (error) goto out_put_shost; @@ -2699,6 +2727,63 @@ lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba) } /** + * lpfc_sli4_fw_cfg_check - Read the firmware config and verify FCoE support + * @phba: pointer to lpfc hba data structure. + * + * This function uses the QUERY_FW_CFG mailbox command to determine if the + * firmware loaded supports FCoE. A return of zero indicates that the mailbox + * was successful and the firmware supports FCoE. Any other return indicates + * a error. It is assumed that this function will be called before interrupts + * are enabled. + **/ +static int +lpfc_sli4_fw_cfg_check(struct lpfc_hba *phba) +{ + int rc = 0; + LPFC_MBOXQ_t *mboxq; + struct lpfc_mbx_query_fw_cfg *query_fw_cfg; + uint32_t length; + uint32_t shdr_status, shdr_add_status; + + mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mboxq) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2621 Failed to allocate mbox for " + "query firmware config cmd\n"); + return -ENOMEM; + } + query_fw_cfg = &mboxq->u.mqe.un.query_fw_cfg; + length = (sizeof(struct lpfc_mbx_query_fw_cfg) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_QUERY_FW_CFG, + length, LPFC_SLI4_MBX_EMBED); + rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); + /* The IOCTL status is embedded in the mailbox subheader. */ + shdr_status = bf_get(lpfc_mbox_hdr_status, + &query_fw_cfg->header.cfg_shdr.response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, + &query_fw_cfg->header.cfg_shdr.response); + if (shdr_status || shdr_add_status || rc != MBX_SUCCESS) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2622 Query Firmware Config failed " + "mbx status x%x, status x%x add_status x%x\n", + rc, shdr_status, shdr_add_status); + return -EINVAL; + } + if (!bf_get(lpfc_function_mode_fcoe_i, query_fw_cfg)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2623 FCoE Function not supported by firmware. " + "Function mode = %08x\n", + query_fw_cfg->function_mode); + return -EINVAL; + } + if (rc != MBX_TIMEOUT) + mempool_free(mboxq, phba->mbox_mem_pool); + return 0; +} + +/** * lpfc_sli4_parse_latt_fault - Parse sli4 link-attention link fault code * @phba: pointer to lpfc hba data structure. * @acqe_link: pointer to the async link completion queue entry. @@ -2918,13 +3003,17 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba, { uint8_t event_type = bf_get(lpfc_acqe_fcoe_event_type, acqe_fcoe); int rc; + struct lpfc_vport *vport; + struct lpfc_nodelist *ndlp; + struct Scsi_Host *shost; + phba->fc_eventTag = acqe_fcoe->event_tag; phba->fcoe_eventtag = acqe_fcoe->event_tag; switch (event_type) { case LPFC_FCOE_EVENT_TYPE_NEW_FCF: lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, "2546 New FCF found index 0x%x tag 0x%x\n", - acqe_fcoe->fcf_index, + acqe_fcoe->index, acqe_fcoe->event_tag); /* * If the current FCF is in discovered state, or @@ -2939,12 +3028,11 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba, spin_unlock_irq(&phba->hbalock); /* Read the FCF table and re-discover SAN. */ - rc = lpfc_sli4_read_fcf_record(phba, - LPFC_FCOE_FCF_GET_FIRST); + rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST); if (rc) lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, - "2547 Read FCF record failed 0x%x\n", - rc); + "2547 Read FCF record failed 0x%x\n", + rc); break; case LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL: @@ -2956,11 +3044,11 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba, case LPFC_FCOE_EVENT_TYPE_FCF_DEAD: lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, - "2549 FCF disconnected fron network index 0x%x" - " tag 0x%x\n", acqe_fcoe->fcf_index, + "2549 FCF disconnected from network index 0x%x" + " tag 0x%x\n", acqe_fcoe->index, acqe_fcoe->event_tag); /* If the event is not for currently used fcf do nothing */ - if (phba->fcf.fcf_indx != acqe_fcoe->fcf_index) + if (phba->fcf.fcf_indx != acqe_fcoe->index) break; /* * Currently, driver support only one FCF - so treat this as @@ -2970,7 +3058,28 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba, /* Unregister FCF if no devices connected to it */ lpfc_unregister_unused_fcf(phba); break; - + case LPFC_FCOE_EVENT_TYPE_CVL: + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "2718 Clear Virtual Link Received for VPI 0x%x" + " tag 0x%x\n", acqe_fcoe->index, acqe_fcoe->event_tag); + vport = lpfc_find_vport_by_vpid(phba, + acqe_fcoe->index - phba->vpi_base); + if (!vport) + break; + ndlp = lpfc_findnode_did(vport, Fabric_DID); + if (!ndlp) + break; + shost = lpfc_shost_from_vport(vport); + lpfc_linkdown_port(vport); + if (vport->port_type != LPFC_NPIV_PORT) { + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + spin_lock_irq(shost->host_lock); + ndlp->nlp_flag |= NLP_DELAY_TMO; + spin_unlock_irq(shost->host_lock); + ndlp->nlp_last_elscmd = ELS_CMD_FLOGI; + vport->port_state = LPFC_FLOGI; + } + break; default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0288 Unknown FCoE event type 0x%x event tag " @@ -2990,6 +3099,7 @@ static void lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba, struct lpfc_acqe_dcbx *acqe_dcbx) { + phba->fc_eventTag = acqe_dcbx->event_tag; lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0290 The SLI4 DCBX asynchronous event is not " "handled yet\n"); @@ -3124,7 +3234,7 @@ static void lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode) * PCI devices. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -3220,7 +3330,7 @@ lpfc_reset_hba(struct lpfc_hba *phba) * support the SLI-3 HBA device it attached to. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -3321,7 +3431,7 @@ lpfc_sli_driver_resource_unset(struct lpfc_hba *phba) * support the SLI-4 HBA device it attached to. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -3432,7 +3542,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) /* Driver internel slow-path CQ Event pool */ INIT_LIST_HEAD(&phba->sli4_hba.sp_cqe_event_pool); /* Response IOCB work queue list */ - INIT_LIST_HEAD(&phba->sli4_hba.sp_rspiocb_work_queue); + INIT_LIST_HEAD(&phba->sli4_hba.sp_queue_event); /* Asynchronous event CQ Event work queue list */ INIT_LIST_HEAD(&phba->sli4_hba.sp_asynce_work_queue); /* Fast-path XRI aborted CQ Event work queue list */ @@ -3461,6 +3571,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) if (unlikely(rc)) goto out_free_bsmbx; + rc = lpfc_sli4_fw_cfg_check(phba); + if (unlikely(rc)) + goto out_free_bsmbx; + /* Set up the hba's configuration parameters. */ rc = lpfc_sli4_read_config(phba); if (unlikely(rc)) @@ -3594,8 +3708,10 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba) /* Free the current connect table */ list_for_each_entry_safe(conn_entry, next_conn_entry, - &phba->fcf_conn_rec_list, list) + &phba->fcf_conn_rec_list, list) { + list_del_init(&conn_entry->list); kfree(conn_entry); + } return; } @@ -3642,7 +3758,7 @@ lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) * device specific resource setup to support the HBA device it attached to. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -3688,7 +3804,7 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) * device specific resource setup to support the HBA device it attached to. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -3753,7 +3869,7 @@ lpfc_free_iocb_list(struct lpfc_hba *phba) * list and set up the IOCB tag array accordingly. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -3824,7 +3940,7 @@ lpfc_free_sgl_list(struct lpfc_hba *phba) rc = lpfc_sli4_remove_all_sgl_pages(phba); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "2005 Unable to deregister pages from HBA: %x", rc); + "2005 Unable to deregister pages from HBA: %x\n", rc); } kfree(phba->sli4_hba.lpfc_els_sgl_array); } @@ -3872,7 +3988,7 @@ lpfc_free_active_sgl(struct lpfc_hba *phba) * list and set up the sgl xritag tag array accordingly. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -3986,7 +4102,7 @@ out_free_mem: * enabled and the driver is reinitializing the device. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No availble memory * EIO - The mailbox failed to complete successfully. **/ @@ -4146,7 +4262,7 @@ lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *phba) * PCI device data structure is set. * * Return codes - * pointer to @phba - sucessful + * pointer to @phba - successful * NULL - error **/ static struct lpfc_hba * @@ -4202,7 +4318,7 @@ lpfc_hba_free(struct lpfc_hba *phba) * host with it. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -4273,7 +4389,8 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost) _dump_buf_data = (char *) __get_free_pages(GFP_KERNEL, pagecnt); if (_dump_buf_data) { - printk(KERN_ERR "BLKGRD allocated %d pages for " + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9043 BLKGRD: allocated %d pages for " "_dump_buf_data at 0x%p\n", (1 << pagecnt), _dump_buf_data); _dump_buf_data_order = pagecnt; @@ -4284,17 +4401,20 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost) --pagecnt; } if (!_dump_buf_data_order) - printk(KERN_ERR "BLKGRD ERROR unable to allocate " + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9044 BLKGRD: ERROR unable to allocate " "memory for hexdump\n"); } else - printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p" + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9045 BLKGRD: already allocated _dump_buf_data=0x%p" "\n", _dump_buf_data); if (!_dump_buf_dif) { while (pagecnt) { _dump_buf_dif = (char *) __get_free_pages(GFP_KERNEL, pagecnt); if (_dump_buf_dif) { - printk(KERN_ERR "BLKGRD allocated %d pages for " + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9046 BLKGRD: allocated %d pages for " "_dump_buf_dif at 0x%p\n", (1 << pagecnt), _dump_buf_dif); _dump_buf_dif_order = pagecnt; @@ -4305,10 +4425,12 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost) --pagecnt; } if (!_dump_buf_dif_order) - printk(KERN_ERR "BLKGRD ERROR unable to allocate " + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9047 BLKGRD: ERROR unable to allocate " "memory for hexdump\n"); } else - printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9048 BLKGRD: already allocated _dump_buf_dif=0x%p\n", _dump_buf_dif); } @@ -4365,7 +4487,7 @@ lpfc_post_init_setup(struct lpfc_hba *phba) * with SLI-3 interface spec. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -4512,7 +4634,6 @@ int lpfc_sli4_post_status_check(struct lpfc_hba *phba) { struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg, scratchpad; - uint32_t onlnreg0, onlnreg1; int i, port_error = -ENODEV; if (!phba->sli4_hba.STAregaddr) @@ -4556,21 +4677,20 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba) bf_get(lpfc_scratchpad_slirev, &scratchpad), bf_get(lpfc_scratchpad_featurelevel1, &scratchpad), bf_get(lpfc_scratchpad_featurelevel2, &scratchpad)); - + phba->sli4_hba.ue_mask_lo = readl(phba->sli4_hba.UEMASKLOregaddr); + phba->sli4_hba.ue_mask_hi = readl(phba->sli4_hba.UEMASKHIregaddr); /* With uncoverable error, log the error message and return error */ - onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr); - onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr); - if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) { - uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr); - uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr); - if (uerrlo_reg.word0 || uerrhi_reg.word0) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "1422 HBA Unrecoverable error: " - "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, " - "online0_reg=0x%x, online1_reg=0x%x\n", - uerrlo_reg.word0, uerrhi_reg.word0, - onlnreg0, onlnreg1); - } + uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr); + uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr); + if ((~phba->sli4_hba.ue_mask_lo & uerrlo_reg.word0) || + (~phba->sli4_hba.ue_mask_hi & uerrhi_reg.word0)) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "1422 HBA Unrecoverable error: " + "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, " + "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n", + uerrlo_reg.word0, uerrhi_reg.word0, + phba->sli4_hba.ue_mask_lo, + phba->sli4_hba.ue_mask_hi); return -ENODEV; } @@ -4591,10 +4711,10 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba) LPFC_UERR_STATUS_LO; phba->sli4_hba.UERRHIregaddr = phba->sli4_hba.conf_regs_memmap_p + LPFC_UERR_STATUS_HI; - phba->sli4_hba.ONLINE0regaddr = phba->sli4_hba.conf_regs_memmap_p + - LPFC_ONLINE0; - phba->sli4_hba.ONLINE1regaddr = phba->sli4_hba.conf_regs_memmap_p + - LPFC_ONLINE1; + phba->sli4_hba.UEMASKLOregaddr = phba->sli4_hba.conf_regs_memmap_p + + LPFC_UE_MASK_LO; + phba->sli4_hba.UEMASKHIregaddr = phba->sli4_hba.conf_regs_memmap_p + + LPFC_UE_MASK_HI; phba->sli4_hba.SCRATCHPADregaddr = phba->sli4_hba.conf_regs_memmap_p + LPFC_SCRATCHPAD; } @@ -4662,7 +4782,7 @@ lpfc_sli4_bar2_register_memmap(struct lpfc_hba *phba, uint32_t vf) * this routine. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - could not allocated memory. **/ static int @@ -4761,7 +4881,7 @@ lpfc_destroy_bootstrap_mbox(struct lpfc_hba *phba) * allocation for the port. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No availble memory * EIO - The mailbox failed to complete successfully. **/ @@ -4825,7 +4945,8 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) phba->vpi_base = phba->sli4_hba.max_cfg_param.vpi_base; phba->vfi_base = phba->sli4_hba.max_cfg_param.vfi_base; phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base; - phba->max_vpi = phba->sli4_hba.max_cfg_param.max_vpi; + phba->max_vpi = (phba->sli4_hba.max_cfg_param.max_vpi > 0) ? + (phba->sli4_hba.max_cfg_param.max_vpi - 1) : 0; phba->max_vports = phba->max_vpi; lpfc_printf_log(phba, KERN_INFO, LOG_SLI, "2003 cfg params XRI(B:%d M:%d), " @@ -4861,7 +4982,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) * HBA consistent with the SLI-4 interface spec. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No availble memory * EIO - The mailbox failed to complete successfully. **/ @@ -4910,7 +5031,7 @@ lpfc_setup_endian_order(struct lpfc_hba *phba) * we just use some constant number as place holder. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No availble memory * EIO - The mailbox failed to complete successfully. **/ @@ -4979,10 +5100,9 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) /* It does not make sense to have more EQs than WQs */ if (cfg_fcp_eq_count > phba->cfg_fcp_wq_count) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, - "2593 The number of FCP EQs (%d) is more " - "than the number of FCP WQs (%d), take " - "the number of FCP EQs same as than of " - "WQs (%d)\n", cfg_fcp_eq_count, + "2593 The FCP EQ count(%d) cannot be greater " + "than the FCP WQ count(%d), limiting the " + "FCP EQ count to %d\n", cfg_fcp_eq_count, phba->cfg_fcp_wq_count, phba->cfg_fcp_wq_count); cfg_fcp_eq_count = phba->cfg_fcp_wq_count; @@ -5058,15 +5178,6 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) } phba->sli4_hba.els_cq = qdesc; - /* Create slow-path Unsolicited Receive Complete Queue */ - qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize, - phba->sli4_hba.cq_ecount); - if (!qdesc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0502 Failed allocate slow-path USOL RX CQ\n"); - goto out_free_els_cq; - } - phba->sli4_hba.rxq_cq = qdesc; /* Create fast-path FCP Completion Queue(s), one-to-one with EQs */ phba->sli4_hba.fcp_cq = kzalloc((sizeof(struct lpfc_queue *) * @@ -5075,7 +5186,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2577 Failed allocate memory for fast-path " "CQ record array\n"); - goto out_free_rxq_cq; + goto out_free_els_cq; } for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) { qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize, @@ -5188,9 +5299,6 @@ out_free_fcp_cq: phba->sli4_hba.fcp_cq[fcp_cqidx] = NULL; } kfree(phba->sli4_hba.fcp_cq); -out_free_rxq_cq: - lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq); - phba->sli4_hba.rxq_cq = NULL; out_free_els_cq: lpfc_sli4_queue_free(phba->sli4_hba.els_cq); phba->sli4_hba.els_cq = NULL; @@ -5218,7 +5326,7 @@ out_error: * operation. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No availble memory * EIO - The mailbox failed to complete successfully. **/ @@ -5247,10 +5355,6 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba) lpfc_sli4_queue_free(phba->sli4_hba.dat_rq); phba->sli4_hba.dat_rq = NULL; - /* Release unsolicited receive complete queue */ - lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq); - phba->sli4_hba.rxq_cq = NULL; - /* Release ELS complete queue */ lpfc_sli4_queue_free(phba->sli4_hba.els_cq); phba->sli4_hba.els_cq = NULL; @@ -5286,7 +5390,7 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba) * operation. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No availble memory * EIO - The mailbox failed to complete successfully. **/ @@ -5383,25 +5487,6 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) phba->sli4_hba.els_cq->queue_id, phba->sli4_hba.sp_eq->queue_id); - /* Set up slow-path Unsolicited Receive Complete Queue */ - if (!phba->sli4_hba.rxq_cq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0532 USOL RX CQ not allocated\n"); - goto out_destroy_els_cq; - } - rc = lpfc_cq_create(phba, phba->sli4_hba.rxq_cq, phba->sli4_hba.sp_eq, - LPFC_RCQ, LPFC_USOL); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0533 Failed setup of slow-path USOL RX CQ: " - "rc = 0x%x\n", rc); - goto out_destroy_els_cq; - } - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2587 USL CQ setup: cq-id=%d, parent eq-id=%d\n", - phba->sli4_hba.rxq_cq->queue_id, - phba->sli4_hba.sp_eq->queue_id); - /* Set up fast-path FCP Response Complete Queue */ for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) { if (!phba->sli4_hba.fcp_cq[fcp_cqidx]) { @@ -5507,7 +5592,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) goto out_destroy_fcp_wq; } rc = lpfc_rq_create(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq, - phba->sli4_hba.rxq_cq, LPFC_USOL); + phba->sli4_hba.els_cq, LPFC_USOL); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0541 Failed setup of Receive Queue: " @@ -5519,7 +5604,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) "parent cq-id=%d\n", phba->sli4_hba.hdr_rq->queue_id, phba->sli4_hba.dat_rq->queue_id, - phba->sli4_hba.rxq_cq->queue_id); + phba->sli4_hba.els_cq->queue_id); return 0; out_destroy_fcp_wq: @@ -5531,8 +5616,6 @@ out_destroy_mbx_wq: out_destroy_fcp_cq: for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--) lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_cqidx]); - lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq); -out_destroy_els_cq: lpfc_cq_destroy(phba, phba->sli4_hba.els_cq); out_destroy_mbx_cq: lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq); @@ -5552,7 +5635,7 @@ out_error: * operation. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No availble memory * EIO - The mailbox failed to complete successfully. **/ @@ -5574,8 +5657,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba) lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq); /* Unset ELS complete queue */ lpfc_cq_destroy(phba, phba->sli4_hba.els_cq); - /* Unset unsolicited receive complete queue */ - lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq); /* Unset FCP response complete queue */ for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++) lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_qidx]); @@ -5599,7 +5680,7 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba) * Later, this can be used for all the slow-path events. * * Return codes - * 0 - sucessful + * 0 - successful * -ENOMEM - No availble memory **/ static int @@ -5760,7 +5841,7 @@ lpfc_sli4_cq_event_release_all(struct lpfc_hba *phba) * all resources assigned to the PCI function which originates this request. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No availble memory * EIO - The mailbox failed to complete successfully. **/ @@ -5923,7 +6004,7 @@ lpfc_sli4_fcfi_unreg(struct lpfc_hba *phba, uint16_t fcfi) * with SLI-4 interface spec. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -6052,7 +6133,7 @@ lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba) * will be left with MSI-X enabled and leaks its vectors. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -6184,7 +6265,7 @@ lpfc_sli_disable_msix(struct lpfc_hba *phba) * is done in this function. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error */ static int @@ -6243,7 +6324,7 @@ lpfc_sli_disable_msi(struct lpfc_hba *phba) * MSI-X -> MSI -> IRQ. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static uint32_t @@ -6333,7 +6414,7 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba) * enabled and leaks its vectors. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -6443,7 +6524,7 @@ lpfc_sli4_disable_msix(struct lpfc_hba *phba) * which is done in this function. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static int @@ -6508,7 +6589,7 @@ lpfc_sli4_disable_msi(struct lpfc_hba *phba) * MSI-X -> MSI -> IRQ. * * Return codes - * 0 - sucessful + * 0 - successful * other values - error **/ static uint32_t @@ -6722,6 +6803,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid) { struct lpfc_hba *phba; struct lpfc_vport *vport = NULL; + struct Scsi_Host *shost = NULL; int error; uint32_t cfg_mode, intr_mode; @@ -6800,6 +6882,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid) goto out_destroy_shost; } + shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */ /* Now, trying to enable interrupt and bring up the device */ cfg_mode = phba->cfg_use_msi; while (true) { @@ -6866,6 +6949,8 @@ out_unset_pci_mem_s3: lpfc_sli_pci_mem_unset(phba); out_disable_pci_dev: lpfc_disable_pci_dev(phba); + if (shost) + scsi_host_put(shost); out_free_phba: lpfc_hba_free(phba); return error; @@ -7036,6 +7121,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev) /* Restore device state from PCI config space */ pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); + if (pdev->is_busmaster) pci_set_master(pdev); @@ -7070,6 +7156,75 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev) } /** + * lpfc_sli_prep_dev_for_recover - Prepare SLI3 device for pci slot recover + * @phba: pointer to lpfc hba data structure. + * + * This routine is called to prepare the SLI3 device for PCI slot recover. It + * aborts and stops all the on-going I/Os on the pci device. + **/ +static void +lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba) +{ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2723 PCI channel I/O abort preparing for recovery\n"); + /* Prepare for bringing HBA offline */ + lpfc_offline_prep(phba); + /* Clear sli active flag to prevent sysfs access to HBA */ + spin_lock_irq(&phba->hbalock); + phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE; + spin_unlock_irq(&phba->hbalock); + /* Stop and flush all I/Os and bring HBA offline */ + lpfc_offline(phba); +} + +/** + * lpfc_sli_prep_dev_for_reset - Prepare SLI3 device for pci slot reset + * @phba: pointer to lpfc hba data structure. + * + * This routine is called to prepare the SLI3 device for PCI slot reset. It + * disables the device interrupt and pci device, and aborts the internal FCP + * pending I/Os. + **/ +static void +lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2710 PCI channel disable preparing for reset\n"); + /* Disable interrupt and pci device */ + lpfc_sli_disable_intr(phba); + pci_disable_device(phba->pcidev); + /* + * There may be I/Os dropped by the firmware. + * Error iocb (I/O) on txcmplq and let the SCSI layer + * retry it after re-establishing link. + */ + pring = &psli->ring[psli->fcp_ring]; + lpfc_sli_abort_iocb_ring(phba, pring); +} + +/** + * lpfc_sli_prep_dev_for_perm_failure - Prepare SLI3 dev for pci slot disable + * @phba: pointer to lpfc hba data structure. + * + * This routine is called to prepare the SLI3 device for PCI slot permanently + * disabling. It blocks the SCSI transport layer traffic and flushes the FCP + * pending I/Os. + **/ +static void +lpfc_prep_dev_for_perm_failure(struct lpfc_hba *phba) +{ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2711 PCI channel permanent disable for failure\n"); + /* Block all SCSI devices' I/Os on the host */ + lpfc_scsi_dev_block(phba); + /* Clean up all driver's outstanding SCSI I/Os */ + lpfc_sli_flush_fcp_rings(phba); +} + +/** * lpfc_io_error_detected_s3 - Method for handling SLI-3 device PCI I/O error * @pdev: pointer to PCI device. * @state: the current PCI connection state. @@ -7083,6 +7238,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev) * as desired. * * Return codes + * PCI_ERS_RESULT_CAN_RECOVER - can be recovered with reset_link * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery * PCI_ERS_RESULT_DISCONNECT - device could not be recovered **/ @@ -7091,33 +7247,27 @@ lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring; - if (state == pci_channel_io_perm_failure) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0472 PCI channel I/O permanent failure\n"); - /* Block all SCSI devices' I/Os on the host */ - lpfc_scsi_dev_block(phba); - /* Clean up all driver's outstanding SCSI I/Os */ - lpfc_sli_flush_fcp_rings(phba); + switch (state) { + case pci_channel_io_normal: + /* Non-fatal error, prepare for recovery */ + lpfc_sli_prep_dev_for_recover(phba); + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + /* Fatal error, prepare for slot reset */ + lpfc_sli_prep_dev_for_reset(phba); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + /* Permanent failure, prepare for device down */ + lpfc_prep_dev_for_perm_failure(phba); return PCI_ERS_RESULT_DISCONNECT; + default: + /* Unknown state, prepare and request slot reset */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0472 Unknown PCI error state: x%x\n", state); + lpfc_sli_prep_dev_for_reset(phba); + return PCI_ERS_RESULT_NEED_RESET; } - - pci_disable_device(pdev); - /* - * There may be I/Os dropped by the firmware. - * Error iocb (I/O) on txcmplq and let the SCSI layer - * retry it after re-establishing link. - */ - pring = &psli->ring[psli->fcp_ring]; - lpfc_sli_abort_iocb_ring(phba, pring); - - /* Disable interrupt */ - lpfc_sli_disable_intr(phba); - - /* Request a slot reset. */ - return PCI_ERS_RESULT_NEED_RESET; } /** @@ -7197,7 +7347,12 @@ lpfc_io_resume_s3(struct pci_dev *pdev) struct Scsi_Host *shost = pci_get_drvdata(pdev); struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + /* Bring the device online */ lpfc_online(phba); + + /* Clean up Advanced Error Reporting (AER) if needed */ + if (phba->hba_flag & HBA_AER_ENABLED) + pci_cleanup_aer_uncorrect_error_status(pdev); } /** @@ -7213,15 +7368,15 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba) if (phba->sli_rev == LPFC_SLI_REV4) { if (max_xri <= 100) - return 4; + return 10; else if (max_xri <= 256) - return 8; + return 25; else if (max_xri <= 512) - return 16; + return 50; else if (max_xri <= 1024) - return 32; + return 100; else - return 48; + return 150; } else return 0; } @@ -7249,6 +7404,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) { struct lpfc_hba *phba; struct lpfc_vport *vport = NULL; + struct Scsi_Host *shost = NULL; int error; uint32_t cfg_mode, intr_mode; int mcnt; @@ -7329,6 +7485,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) goto out_destroy_shost; } + shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */ /* Now, trying to enable interrupt and bring up the device */ cfg_mode = phba->cfg_use_msi; while (true) { @@ -7397,6 +7554,8 @@ out_unset_pci_mem_s4: lpfc_sli4_pci_mem_unset(phba); out_disable_pci_dev: lpfc_disable_pci_dev(phba); + if (shost) + scsi_host_put(shost); out_free_phba: lpfc_hba_free(phba); return error; @@ -7971,6 +8130,10 @@ static struct pci_device_id lpfc_id_table[] = { PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK, PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TOMCAT, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FALCON, + PCI_ANY_ID, PCI_ANY_ID, }, { 0 } }; @@ -8053,15 +8216,15 @@ lpfc_exit(void) if (lpfc_enable_npiv) fc_release_transport(lpfc_vport_transport_template); if (_dump_buf_data) { - printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_data " - "at 0x%p\n", + printk(KERN_ERR "9062 BLKGRD: freeing %lu pages for " + "_dump_buf_data at 0x%p\n", (1L << _dump_buf_data_order), _dump_buf_data); free_pages((unsigned long)_dump_buf_data, _dump_buf_data_order); } if (_dump_buf_dif) { - printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_dif " - "at 0x%p\n", + printk(KERN_ERR "9049 BLKGRD: freeing %lu pages for " + "_dump_buf_dif at 0x%p\n", (1L << _dump_buf_dif_order), _dump_buf_dif); free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order); } diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index 1ab405902a18..a9afd8b94b6a 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -25,8 +25,8 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_transport_fc.h> - #include <scsi/scsi.h> +#include <scsi/fc/fc_fs.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -820,6 +820,10 @@ lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb) mb->un.varRegVpi.vpi = vport->vpi + vport->phba->vpi_base; mb->un.varRegVpi.sid = vport->fc_myDID; mb->un.varRegVpi.vfi = vport->vfi + vport->phba->vfi_base; + memcpy(mb->un.varRegVpi.wwn, &vport->fc_portname, + sizeof(struct lpfc_name)); + mb->un.varRegVpi.wwn[0] = cpu_to_le32(mb->un.varRegVpi.wwn[0]); + mb->un.varRegVpi.wwn[1] = cpu_to_le32(mb->un.varRegVpi.wwn[1]); mb->mbxCommand = MBX_REG_VPI; mb->mbxOwner = OWN_HOST; @@ -849,7 +853,10 @@ lpfc_unreg_vpi(struct lpfc_hba *phba, uint16_t vpi, LPFC_MBOXQ_t *pmb) MAILBOX_t *mb = &pmb->u.mb; memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); - mb->un.varUnregVpi.vpi = vpi + phba->vpi_base; + if (phba->sli_rev < LPFC_SLI_REV4) + mb->un.varUnregVpi.vpi = vpi + phba->vpi_base; + else + mb->un.varUnregVpi.sli4_vpi = vpi + phba->vpi_base; mb->mbxCommand = MBX_UNREG_VPI; mb->mbxOwner = OWN_HOST; @@ -1132,7 +1139,7 @@ lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb) /* Otherwise we setup specific rctl / type masks for this ring */ for (i = 0; i < pring->num_mask; i++) { mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl; - if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ) + if (mb->un.varCfgRing.rrRegs[i].rval != FC_RCTL_ELS_REQ) mb->un.varCfgRing.rrRegs[i].rmask = 0xff; else mb->un.varCfgRing.rrRegs[i].rmask = 0xfe; @@ -1654,9 +1661,12 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox, /* Allocate record for keeping SGE virtual addresses */ mbox->sge_array = kmalloc(sizeof(struct lpfc_mbx_nembed_sge_virt), GFP_KERNEL); - if (!mbox->sge_array) + if (!mbox->sge_array) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "2527 Failed to allocate non-embedded SGE " + "array.\n"); return 0; - + } for (pagen = 0, alloc_len = 0; pagen < pcount; pagen++) { /* The DMA memory is always allocated in the length of a * page even though the last SGE might not fill up to a @@ -1753,11 +1763,6 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq) /* Set up host requested features. */ bf_set(lpfc_mbx_rq_ftr_rq_fcpi, &mboxq->u.mqe.un.req_ftrs, 1); - if (phba->cfg_enable_fip) - bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 0); - else - bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 1); - /* Enable DIF (block guard) only if configured to do so. */ if (phba->cfg_enable_bg) bf_set(lpfc_mbx_rq_ftr_rq_dif, &mboxq->u.mqe.un.req_ftrs, 1); @@ -1817,6 +1822,9 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys) bf_set(lpfc_reg_vfi_vfi, reg_vfi, vport->vfi + vport->phba->vfi_base); bf_set(lpfc_reg_vfi_fcfi, reg_vfi, vport->phba->fcf.fcfi); bf_set(lpfc_reg_vfi_vpi, reg_vfi, vport->vpi + vport->phba->vpi_base); + memcpy(reg_vfi->wwn, &vport->fc_portname, sizeof(struct lpfc_name)); + reg_vfi->wwn[0] = cpu_to_le32(reg_vfi->wwn[0]); + reg_vfi->wwn[1] = cpu_to_le32(reg_vfi->wwn[1]); reg_vfi->bde.addrHigh = putPaddrHigh(phys); reg_vfi->bde.addrLow = putPaddrLow(phys); reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam); @@ -1850,7 +1858,7 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi) /** * lpfc_unreg_vfi - Initialize the UNREG_VFI mailbox command * @mbox: pointer to lpfc mbox command to initialize. - * @vfi: VFI to be unregistered. + * @vport: vport associated with the VF. * * The UNREG_VFI mailbox command causes the SLI Host to put a virtual fabric * (logical NPort) into the inactive state. The SLI Host must have logged out @@ -1859,11 +1867,12 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi) * fabric inactive. **/ void -lpfc_unreg_vfi(struct lpfcMboxq *mbox, uint16_t vfi) +lpfc_unreg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport) { memset(mbox, 0, sizeof(*mbox)); bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_UNREG_VFI); - bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi, vfi); + bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi, + vport->vfi + vport->phba->vfi_base); } /** diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 3e74136f1ede..2ed6af194932 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1223,6 +1223,12 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport, list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) { if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) && (ndlp == (struct lpfc_nodelist *) mb->context2)) { + if (phba->sli_rev == LPFC_SLI_REV4) { + spin_unlock_irq(&phba->hbalock); + lpfc_sli4_free_rpi(phba, + mb->u.mb.un.varRegLogin.rpi); + spin_lock_irq(&phba->hbalock); + } mp = (struct lpfc_dmabuf *) (mb->context1); if (mp) { __lpfc_mbuf_free(phba, mp->virt, mp->phys); @@ -1230,6 +1236,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport, } lpfc_nlp_put(ndlp); list_del(&mb->list); + phba->sli.mboxq_cnt--; mempool_free(mb, phba->mbox_mem_pool); } } diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index c88f59f0ce30..a246410ce9df 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -59,22 +59,26 @@ static char *dif_op_str[] = { }; static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); +static void +lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); static void -lpfc_debug_save_data(struct scsi_cmnd *cmnd) +lpfc_debug_save_data(struct lpfc_hba *phba, struct scsi_cmnd *cmnd) { void *src, *dst; struct scatterlist *sgde = scsi_sglist(cmnd); if (!_dump_buf_data) { - printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9050 BLKGRD: ERROR %s _dump_buf_data is NULL\n", __func__); return; } if (!sgde) { - printk(KERN_ERR "BLKGRD ERROR: data scatterlist is null\n"); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9051 BLKGRD: ERROR: data scatterlist is null\n"); return; } @@ -88,19 +92,21 @@ lpfc_debug_save_data(struct scsi_cmnd *cmnd) } static void -lpfc_debug_save_dif(struct scsi_cmnd *cmnd) +lpfc_debug_save_dif(struct lpfc_hba *phba, struct scsi_cmnd *cmnd) { void *src, *dst; struct scatterlist *sgde = scsi_prot_sglist(cmnd); if (!_dump_buf_dif) { - printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9052 BLKGRD: ERROR %s _dump_buf_data is NULL\n", __func__); return; } if (!sgde) { - printk(KERN_ERR "BLKGRD ERROR: prot scatterlist is null\n"); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9053 BLKGRD: ERROR: prot scatterlist is null\n"); return; } @@ -242,6 +248,36 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba, } /** + * lpfc_change_queue_depth - Alter scsi device queue depth + * @sdev: Pointer the scsi device on which to change the queue depth. + * @qdepth: New queue depth to set the sdev to. + * @reason: The reason for the queue depth change. + * + * This function is called by the midlayer and the LLD to alter the queue + * depth for a scsi device. This function sets the queue depth to the new + * value and sends an event out to log the queue depth change. + **/ +int +lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) +{ + struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata; + struct lpfc_hba *phba = vport->phba; + struct lpfc_rport_data *rdata; + unsigned long new_queue_depth, old_queue_depth; + + old_queue_depth = sdev->queue_depth; + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + new_queue_depth = sdev->queue_depth; + rdata = sdev->hostdata; + if (rdata) + lpfc_send_sdev_queuedepth_change_event(phba, vport, + rdata->pnode, sdev->lun, + old_queue_depth, + new_queue_depth); + return sdev->queue_depth; +} + +/** * lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread * @phba: The Hba for which this call is being executed. * @@ -305,8 +341,10 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport, if (vport->cfg_lun_queue_depth <= queue_depth) return; spin_lock_irqsave(&phba->hbalock, flags); - if (((phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) > jiffies) || - ((phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL ) > jiffies)) { + if (time_before(jiffies, + phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) || + time_before(jiffies, + phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL)) { spin_unlock_irqrestore(&phba->hbalock, flags); return; } @@ -338,10 +376,9 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba) struct lpfc_vport **vports; struct Scsi_Host *shost; struct scsi_device *sdev; - unsigned long new_queue_depth, old_queue_depth; + unsigned long new_queue_depth; unsigned long num_rsrc_err, num_cmd_success; int i; - struct lpfc_rport_data *rdata; num_rsrc_err = atomic_read(&phba->num_rsrc_err); num_cmd_success = atomic_read(&phba->num_cmd_success); @@ -359,22 +396,8 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba) else new_queue_depth = sdev->queue_depth - new_queue_depth; - old_queue_depth = sdev->queue_depth; - if (sdev->ordered_tags) - scsi_adjust_queue_depth(sdev, - MSG_ORDERED_TAG, - new_queue_depth); - else - scsi_adjust_queue_depth(sdev, - MSG_SIMPLE_TAG, - new_queue_depth); - rdata = sdev->hostdata; - if (rdata) - lpfc_send_sdev_queuedepth_change_event( - phba, vports[i], - rdata->pnode, - sdev->lun, old_queue_depth, - new_queue_depth); + lpfc_change_queue_depth(sdev, new_queue_depth, + SCSI_QDEPTH_DEFAULT); } } lpfc_destroy_vport_work_array(phba, vports); @@ -398,7 +421,6 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba) struct Scsi_Host *shost; struct scsi_device *sdev; int i; - struct lpfc_rport_data *rdata; vports = lpfc_create_vport_work_array(phba); if (vports != NULL) @@ -408,22 +430,9 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba) if (vports[i]->cfg_lun_queue_depth <= sdev->queue_depth) continue; - if (sdev->ordered_tags) - scsi_adjust_queue_depth(sdev, - MSG_ORDERED_TAG, - sdev->queue_depth+1); - else - scsi_adjust_queue_depth(sdev, - MSG_SIMPLE_TAG, - sdev->queue_depth+1); - rdata = sdev->hostdata; - if (rdata) - lpfc_send_sdev_queuedepth_change_event( - phba, vports[i], - rdata->pnode, - sdev->lun, - sdev->queue_depth - 1, - sdev->queue_depth); + lpfc_change_queue_depth(sdev, + sdev->queue_depth+1, + SCSI_QDEPTH_RAMP_UP); } } lpfc_destroy_vport_work_array(phba, vports); @@ -589,7 +598,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) iocb->ulpClass = CLASS3; psb->status = IOSTAT_SUCCESS; /* Put it back into the SCSI buffer list */ - lpfc_release_scsi_buf_s4(phba, psb); + lpfc_release_scsi_buf_s3(phba, psb); } @@ -1024,7 +1033,8 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) lpfc_cmd->seg_cnt = nseg; if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) { - printk(KERN_ERR "%s: Too many sg segments from " + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9064 BLKGRD: %s: Too many sg segments from " "dma_map_sg. Config %d, seg_cnt %d\n", __func__, phba->cfg_sg_seg_cnt, lpfc_cmd->seg_cnt); @@ -1112,7 +1122,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) * with the cmd */ static int -lpfc_sc_to_sli_prof(struct scsi_cmnd *sc) +lpfc_sc_to_sli_prof(struct lpfc_hba *phba, struct scsi_cmnd *sc) { uint8_t guard_type = scsi_host_get_guard(sc->device->host); uint8_t ret_prof = LPFC_PROF_INVALID; @@ -1136,7 +1146,8 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc) case SCSI_PROT_NORMAL: default: - printk(KERN_ERR "Bad op/guard:%d/%d combination\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9063 BLKGRD:Bad op/guard:%d/%d combination\n", scsi_get_prot_op(sc), guard_type); break; @@ -1157,7 +1168,8 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc) case SCSI_PROT_WRITE_STRIP: case SCSI_PROT_NORMAL: default: - printk(KERN_ERR "Bad op/guard:%d/%d combination\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9075 BLKGRD: Bad op/guard:%d/%d combination\n", scsi_get_prot_op(sc), guard_type); break; } @@ -1259,7 +1271,7 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint16_t apptagmask, apptagval; pde1 = (struct lpfc_pde *) bpl; - prof = lpfc_sc_to_sli_prof(sc); + prof = lpfc_sc_to_sli_prof(phba, sc); if (prof == LPFC_PROF_INVALID) goto out; @@ -1359,7 +1371,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, return 0; } - prof = lpfc_sc_to_sli_prof(sc); + prof = lpfc_sc_to_sli_prof(phba, sc); if (prof == LPFC_PROF_INVALID) goto out; @@ -1408,7 +1420,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, subtotal = 0; /* total bytes processed for current prot grp */ while (!pgdone) { if (!sgde) { - printk(KERN_ERR "%s Invalid data segment\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9065 BLKGRD:%s Invalid data segment\n", __func__); return 0; } @@ -1462,7 +1475,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, reftag += protgrp_blks; } else { /* if we're here, we have a bug */ - printk(KERN_ERR "BLKGRD: bug in %s\n", __func__); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9054 BLKGRD: bug in %s\n", __func__); } } while (!alldone); @@ -1544,8 +1558,10 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, lpfc_cmd->seg_cnt = datasegcnt; if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) { - printk(KERN_ERR "%s: Too many sg segments from " - "dma_map_sg. Config %d, seg_cnt %d\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9067 BLKGRD: %s: Too many sg segments" + " from dma_map_sg. Config %d, seg_cnt" + " %d\n", __func__, phba->cfg_sg_seg_cnt, lpfc_cmd->seg_cnt); scsi_dma_unmap(scsi_cmnd); @@ -1579,8 +1595,9 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, lpfc_cmd->prot_seg_cnt = protsegcnt; if (lpfc_cmd->prot_seg_cnt > phba->cfg_prot_sg_seg_cnt) { - printk(KERN_ERR "%s: Too many prot sg segments " - "from dma_map_sg. Config %d," + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9068 BLKGRD: %s: Too many prot sg " + "segments from dma_map_sg. Config %d," "prot_seg_cnt %d\n", __func__, phba->cfg_prot_sg_seg_cnt, lpfc_cmd->prot_seg_cnt); @@ -1671,23 +1688,26 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd, uint32_t bgstat = bgf->bgstat; uint64_t failing_sector = 0; - printk(KERN_ERR "BG ERROR in cmd 0x%x lba 0x%llx blk cnt 0x%x " + lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9069 BLKGRD: BG ERROR in cmd" + " 0x%x lba 0x%llx blk cnt 0x%x " "bgstat=0x%x bghm=0x%x\n", cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd), blk_rq_sectors(cmd->request), bgstat, bghm); spin_lock(&_dump_buf_lock); if (!_dump_buf_done) { - printk(KERN_ERR "Saving Data for %u blocks to debugfs\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9070 BLKGRD: Saving" + " Data for %u blocks to debugfs\n", (cmd->cmnd[7] << 8 | cmd->cmnd[8])); - lpfc_debug_save_data(cmd); + lpfc_debug_save_data(phba, cmd); /* If we have a prot sgl, save the DIF buffer */ if (lpfc_prot_group_type(phba, cmd) == LPFC_PG_TYPE_DIF_BUF) { - printk(KERN_ERR "Saving DIF for %u blocks to debugfs\n", - (cmd->cmnd[7] << 8 | cmd->cmnd[8])); - lpfc_debug_save_dif(cmd); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9071 BLKGRD: " + "Saving DIF for %u blocks to debugfs\n", + (cmd->cmnd[7] << 8 | cmd->cmnd[8])); + lpfc_debug_save_dif(phba, cmd); } _dump_buf_done = 1; @@ -1696,15 +1716,17 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd, if (lpfc_bgs_get_invalid_prof(bgstat)) { cmd->result = ScsiResult(DID_ERROR, 0); - printk(KERN_ERR "Invalid BlockGuard profile. bgstat:0x%x\n", - bgstat); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9072 BLKGRD: Invalid" + " BlockGuard profile. bgstat:0x%x\n", + bgstat); ret = (-1); goto out; } if (lpfc_bgs_get_uninit_dif_block(bgstat)) { cmd->result = ScsiResult(DID_ERROR, 0); - printk(KERN_ERR "Invalid BlockGuard DIF Block. bgstat:0x%x\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9073 BLKGRD: " + "Invalid BlockGuard DIF Block. bgstat:0x%x\n", bgstat); ret = (-1); goto out; @@ -1718,7 +1740,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd, cmd->result = DRIVER_SENSE << 24 | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION); phba->bg_guard_err_cnt++; - printk(KERN_ERR "BLKGRD: guard_tag error\n"); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9055 BLKGRD: guard_tag error\n"); } if (lpfc_bgs_get_reftag_err(bgstat)) { @@ -1730,7 +1753,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd, | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION); phba->bg_reftag_err_cnt++; - printk(KERN_ERR "BLKGRD: ref_tag error\n"); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9056 BLKGRD: ref_tag error\n"); } if (lpfc_bgs_get_apptag_err(bgstat)) { @@ -1742,7 +1766,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd, | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION); phba->bg_apptag_err_cnt++; - printk(KERN_ERR "BLKGRD: app_tag error\n"); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9061 BLKGRD: app_tag error\n"); } if (lpfc_bgs_get_hi_water_mark_present(bgstat)) { @@ -1763,7 +1788,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd, if (!ret) { /* No error was reported - problem in FW? */ cmd->result = ScsiResult(DID_ERROR, 0); - printk(KERN_ERR "BLKGRD: no errors reported!\n"); + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9057 BLKGRD: no errors reported!\n"); } out: @@ -1822,9 +1848,10 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) lpfc_cmd->seg_cnt = nseg; if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) { - printk(KERN_ERR "%s: Too many sg segments from " - "dma_map_sg. Config %d, seg_cnt %d\n", - __func__, phba->cfg_sg_seg_cnt, + lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9074 BLKGRD:" + " %s: Too many sg segments from " + "dma_map_sg. Config %d, seg_cnt %d\n", + __func__, phba->cfg_sg_seg_cnt, lpfc_cmd->seg_cnt); scsi_dma_unmap(scsi_cmnd); return 1; @@ -2050,6 +2077,21 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, goto out; } + if (resp_info & RSP_LEN_VALID) { + rsplen = be32_to_cpu(fcprsp->rspRspLen); + if ((rsplen != 0 && rsplen != 4 && rsplen != 8) || + (fcprsp->rspInfo3 != RSP_NO_FAILURE)) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, + "2719 Invalid response length: " + "tgt x%x lun x%x cmnd x%x rsplen x%x\n", + cmnd->device->id, + cmnd->device->lun, cmnd->cmnd[0], + rsplen); + host_status = DID_ERROR; + goto out; + } + } + if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) { uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen); if (snslen > SCSI_SENSE_BUFFERSIZE) @@ -2074,15 +2116,6 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, be32_to_cpu(fcprsp->rspRspLen), fcprsp->rspInfo3); - if (resp_info & RSP_LEN_VALID) { - rsplen = be32_to_cpu(fcprsp->rspRspLen); - if ((rsplen != 0 && rsplen != 4 && rsplen != 8) || - (fcprsp->rspInfo3 != RSP_NO_FAILURE)) { - host_status = DID_ERROR; - goto out; - } - } - scsi_set_resid(cmnd, 0); if (resp_info & RESID_UNDER) { scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId)); @@ -2180,7 +2213,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, struct scsi_cmnd *cmd = lpfc_cmd->pCmd; int result; struct scsi_device *tmp_sdev; - int depth = 0; + int depth; unsigned long flags; struct lpfc_fast_path_event *fast_path_evt; struct Scsi_Host *shost = cmd->device->host; @@ -2264,7 +2297,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, "9031 non-zero BGSTAT " - "on unprotected cmd"); + "on unprotected cmd\n"); } } @@ -2347,67 +2380,29 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, return; } - if (!result) lpfc_rampup_queue_depth(vport, queue_depth); - if (!result && pnode && NLP_CHK_NODE_ACT(pnode) && - ((jiffies - pnode->last_ramp_up_time) > - LPFC_Q_RAMP_UP_INTERVAL * HZ) && - ((jiffies - pnode->last_q_full_time) > - LPFC_Q_RAMP_UP_INTERVAL * HZ) && - (vport->cfg_lun_queue_depth > queue_depth)) { - shost_for_each_device(tmp_sdev, shost) { - if (vport->cfg_lun_queue_depth > tmp_sdev->queue_depth){ - if (tmp_sdev->id != scsi_id) - continue; - if (tmp_sdev->ordered_tags) - scsi_adjust_queue_depth(tmp_sdev, - MSG_ORDERED_TAG, - tmp_sdev->queue_depth+1); - else - scsi_adjust_queue_depth(tmp_sdev, - MSG_SIMPLE_TAG, - tmp_sdev->queue_depth+1); - - pnode->last_ramp_up_time = jiffies; - } - } - lpfc_send_sdev_queuedepth_change_event(phba, vport, pnode, - 0xFFFFFFFF, - queue_depth , queue_depth + 1); - } - /* * Check for queue full. If the lun is reporting queue full, then * back off the lun queue depth to prevent target overloads. */ if (result == SAM_STAT_TASK_SET_FULL && pnode && NLP_CHK_NODE_ACT(pnode)) { - pnode->last_q_full_time = jiffies; - shost_for_each_device(tmp_sdev, shost) { if (tmp_sdev->id != scsi_id) continue; depth = scsi_track_queue_full(tmp_sdev, - tmp_sdev->queue_depth - 1); - } - /* - * The queue depth cannot be lowered any more. - * Modify the returned error code to store - * the final depth value set by - * scsi_track_queue_full. - */ - if (depth == -1) - depth = shost->cmd_per_lun; - - if (depth) { + tmp_sdev->queue_depth-1); + if (depth <= 0) + continue; lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "0711 detected queue full - lun queue " "depth adjusted to %d.\n", depth); lpfc_send_sdev_queuedepth_change_event(phba, vport, - pnode, 0xFFFFFFFF, - depth+1, depth); + pnode, + tmp_sdev->lun, + depth+1, depth); } } @@ -2745,7 +2740,9 @@ void lpfc_poll_timeout(unsigned long ptr) struct lpfc_hba *phba = (struct lpfc_hba *) ptr; if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { - lpfc_sli_poll_fcp_ring (phba); + lpfc_sli_handle_fast_ring_event(phba, + &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); + if (phba->cfg_poll & DISABLE_FCP_RING_INT) lpfc_poll_rearm_timer(phba); } @@ -2771,7 +2768,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; struct lpfc_rport_data *rdata = cmnd->device->hostdata; - struct lpfc_nodelist *ndlp = rdata->pnode; + struct lpfc_nodelist *ndlp; struct lpfc_scsi_buf *lpfc_cmd; struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); int err; @@ -2781,13 +2778,15 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) cmnd->result = err; goto out_fail_command; } + ndlp = rdata->pnode; if (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) && scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) { - printk(KERN_ERR "BLKGRD ERROR: rcvd protected cmd:%02x op:%02x " - "str=%s without registering for BlockGuard - " - "Rejecting command\n", + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9058 BLKGRD: ERROR: rcvd protected cmd:%02x" + " op:%02x str=%s without registering for" + " BlockGuard - Rejecting command\n", cmnd->cmnd[0], scsi_get_prot_op(cmnd), dif_op_str[scsi_get_prot_op(cmnd)]); goto out_fail_command; @@ -2827,61 +2826,66 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) cmnd->scsi_done = done; if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) { - lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, + if (vport->phba->cfg_enable_bg) { + lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, "9033 BLKGRD: rcvd protected cmd:%02x op:%02x " "str=%s\n", cmnd->cmnd[0], scsi_get_prot_op(cmnd), dif_op_str[scsi_get_prot_op(cmnd)]); - lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, + lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, "9034 BLKGRD: CDB: %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x\n", cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2], cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5], cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8], cmnd->cmnd[9]); - if (cmnd->cmnd[0] == READ_10) - lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, + if (cmnd->cmnd[0] == READ_10) + lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, "9035 BLKGRD: READ @ sector %llu, " "count %u\n", (unsigned long long)scsi_get_lba(cmnd), blk_rq_sectors(cmnd->request)); - else if (cmnd->cmnd[0] == WRITE_10) - lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, + else if (cmnd->cmnd[0] == WRITE_10) + lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, "9036 BLKGRD: WRITE @ sector %llu, " "count %u cmd=%p\n", (unsigned long long)scsi_get_lba(cmnd), blk_rq_sectors(cmnd->request), cmnd); + } err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd); } else { - lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, - "9038 BLKGRD: rcvd unprotected cmd:%02x op:%02x" - " str=%s\n", - cmnd->cmnd[0], scsi_get_prot_op(cmnd), - dif_op_str[scsi_get_prot_op(cmnd)]); - lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, - "9039 BLKGRD: CDB: %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x\n", - cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2], - cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5], - cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8], - cmnd->cmnd[9]); - if (cmnd->cmnd[0] == READ_10) + if (vport->phba->cfg_enable_bg) { lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, - "9040 dbg: READ @ sector %llu, " - "count %u\n", - (unsigned long long)scsi_get_lba(cmnd), + "9038 BLKGRD: rcvd unprotected cmd:" + "%02x op:%02x str=%s\n", + cmnd->cmnd[0], scsi_get_prot_op(cmnd), + dif_op_str[scsi_get_prot_op(cmnd)]); + lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, + "9039 BLKGRD: CDB: %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x\n", + cmnd->cmnd[0], cmnd->cmnd[1], + cmnd->cmnd[2], cmnd->cmnd[3], + cmnd->cmnd[4], cmnd->cmnd[5], + cmnd->cmnd[6], cmnd->cmnd[7], + cmnd->cmnd[8], cmnd->cmnd[9]); + if (cmnd->cmnd[0] == READ_10) + lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, + "9040 dbg: READ @ sector %llu, " + "count %u\n", + (unsigned long long)scsi_get_lba(cmnd), blk_rq_sectors(cmnd->request)); - else if (cmnd->cmnd[0] == WRITE_10) - lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, + else if (cmnd->cmnd[0] == WRITE_10) + lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, "9041 dbg: WRITE @ sector %llu, " "count %u cmd=%p\n", (unsigned long long)scsi_get_lba(cmnd), blk_rq_sectors(cmnd->request), cmnd); - else - lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, + else + lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG, "9042 dbg: parser not implemented\n"); + } err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd); } @@ -2898,7 +2902,11 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) goto out_host_busy_free_buf; } if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { - lpfc_sli_poll_fcp_ring(phba); + spin_unlock(shost->host_lock); + lpfc_sli_handle_fast_ring_event(phba, + &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); + + spin_lock(shost->host_lock); if (phba->cfg_poll & DISABLE_FCP_RING_INT) lpfc_poll_rearm_timer(phba); } @@ -2917,28 +2925,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) } /** - * lpfc_block_error_handler - Routine to block error handler - * @cmnd: Pointer to scsi_cmnd data structure. - * - * This routine blocks execution till fc_rport state is not FC_PORSTAT_BLCOEKD. - **/ -static void -lpfc_block_error_handler(struct scsi_cmnd *cmnd) -{ - struct Scsi_Host *shost = cmnd->device->host; - struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); - - spin_lock_irq(shost->host_lock); - while (rport->port_state == FC_PORTSTATE_BLOCKED) { - spin_unlock_irq(shost->host_lock); - msleep(1000); - spin_lock_irq(shost->host_lock); - } - spin_unlock_irq(shost->host_lock); - return; -} - -/** * lpfc_abort_handler - scsi_host_template eh_abort_handler entry point * @cmnd: Pointer to scsi_cmnd data structure. * @@ -2961,7 +2947,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) int ret = SUCCESS; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq); - lpfc_block_error_handler(cmnd); + fc_block_scsi_eh(cmnd); lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble; BUG_ON(!lpfc_cmd); @@ -3001,6 +2987,10 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) icmd->ulpLe = 1; icmd->ulpClass = cmd->ulpClass; + + /* ABTS WQE must go to the same WQ as the WQE to be aborted */ + abtsiocb->fcp_wqidx = iocb->fcp_wqidx; + if (lpfc_is_link_up(phba)) icmd->ulpCommand = CMD_ABORT_XRI_CN; else @@ -3016,7 +3006,8 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) } if (phba->cfg_poll & DISABLE_FCP_RING_INT) - lpfc_sli_poll_fcp_ring (phba); + lpfc_sli_handle_fast_ring_event(phba, + &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); lpfc_cmd->waitq = &waitq; /* Wait for abort to complete */ @@ -3166,9 +3157,15 @@ static int lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd) { struct lpfc_rport_data *rdata = cmnd->device->hostdata; - struct lpfc_nodelist *pnode = rdata->pnode; + struct lpfc_nodelist *pnode; unsigned long later; + if (!rdata) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + "0797 Tgt Map rport failure: rdata x%p\n", rdata); + return FAILED; + } + pnode = rdata->pnode; /* * If target is not in a MAPPED state, delay until * target is rediscovered or devloss timeout expires. @@ -3253,13 +3250,19 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) struct Scsi_Host *shost = cmnd->device->host; struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_rport_data *rdata = cmnd->device->hostdata; - struct lpfc_nodelist *pnode = rdata->pnode; + struct lpfc_nodelist *pnode; unsigned tgt_id = cmnd->device->id; unsigned int lun_id = cmnd->device->lun; struct lpfc_scsi_event_header scsi_event; int status; - lpfc_block_error_handler(cmnd); + if (!rdata) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, + "0798 Device Reset rport failure: rdata x%p\n", rdata); + return FAILED; + } + pnode = rdata->pnode; + fc_block_scsi_eh(cmnd); status = lpfc_chk_tgt_mapped(vport, cmnd); if (status == FAILED) { @@ -3312,13 +3315,19 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd) struct Scsi_Host *shost = cmnd->device->host; struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_rport_data *rdata = cmnd->device->hostdata; - struct lpfc_nodelist *pnode = rdata->pnode; + struct lpfc_nodelist *pnode; unsigned tgt_id = cmnd->device->id; unsigned int lun_id = cmnd->device->lun; struct lpfc_scsi_event_header scsi_event; int status; - lpfc_block_error_handler(cmnd); + if (!rdata) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, + "0799 Target Reset rport failure: rdata x%p\n", rdata); + return FAILED; + } + pnode = rdata->pnode; + fc_block_scsi_eh(cmnd); status = lpfc_chk_tgt_mapped(vport, cmnd); if (status == FAILED) { @@ -3384,7 +3393,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) fc_host_post_vendor_event(shost, fc_get_event_number(), sizeof(scsi_event), (char *)&scsi_event, LPFC_NL_VENDOR_ID); - lpfc_block_error_handler(cmnd); + fc_block_scsi_eh(cmnd); /* * Since the driver manages a single bus device, reset all @@ -3498,6 +3507,8 @@ lpfc_slave_alloc(struct scsi_device *sdev) "Allocated %d buffers.\n", num_to_alloc, num_allocated); } + if (num_allocated > 0) + phba->total_scsi_bufs += num_allocated; return 0; } @@ -3534,7 +3545,8 @@ lpfc_slave_configure(struct scsi_device *sdev) rport->dev_loss_tmo = vport->cfg_devloss_tmo; if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { - lpfc_sli_poll_fcp_ring(phba); + lpfc_sli_handle_fast_ring_event(phba, + &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); if (phba->cfg_poll & DISABLE_FCP_RING_INT) lpfc_poll_rearm_timer(phba); } @@ -3576,6 +3588,7 @@ struct scsi_host_template lpfc_template = { .shost_attrs = lpfc_hba_attrs, .max_sectors = 0xFFFF, .vendor_id = LPFC_NL_VENDOR_ID, + .change_queue_depth = lpfc_change_queue_depth, }; struct scsi_host_template lpfc_vport_template = { @@ -3597,4 +3610,5 @@ struct scsi_host_template lpfc_vport_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = lpfc_vport_attrs, .max_sectors = 0xFFFF, + .change_queue_depth = lpfc_change_queue_depth, }; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 43cbe336f1f8..7935667b81a5 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -30,6 +30,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> #include <scsi/fc/fc_fs.h> +#include <linux/aer.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -58,8 +59,11 @@ typedef enum _lpfc_iocb_type { static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t); static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *, - uint8_t *, uint32_t *); - + uint8_t *, uint32_t *); +static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *, + struct lpfc_iocbq *); +static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *, + struct hbq_dmabuf *); static IOCB_t * lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq) { @@ -259,6 +263,9 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm) bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT); bf_set(lpfc_eqcq_doorbell_eqid, &doorbell, q->queue_id); writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr); + /* PCI read to flush PCI pipeline on re-arming for INTx mode */ + if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM)) + readl(q->phba->sli4_hba.EQCQDBregaddr); return released; } @@ -515,6 +522,8 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba) struct lpfc_sglq *sglq = NULL; uint16_t adj_xri; list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list); + if (!sglq) + return NULL; adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base; phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq; return sglq; @@ -572,9 +581,9 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag); if (sglq) { if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED - || ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT) + && ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT) && (iocbq->iocb.un.ulpWord[4] - == IOERR_SLI_ABORTED))) { + == IOERR_ABORT_REQUESTED))) { spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock, iflag); list_add(&sglq->list, @@ -767,6 +776,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd) case CMD_CLOSE_XRI_CX: case CMD_XRI_ABORTED_CX: case CMD_ABORT_MXRI64_CN: + case CMD_XMIT_BLS_RSP64_CX: type = LPFC_ABORT_IOCB; break; case CMD_RCV_SEQUENCE_CX: @@ -1794,7 +1804,7 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba) */ if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) == MBX_SHUTDOWN) { - /* Unknow mailbox command compl */ + /* Unknown mailbox command compl */ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "(%d):0323 Unknown Mailbox command " "x%x (x%x) Cmpl\n", @@ -2068,8 +2078,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX) || (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX) || (irsp->ulpCommand == CMD_IOCB_RCV_ELS64_CX)) { - Rctl = FC_ELS_REQ; - Type = FC_ELS_DATA; + Rctl = FC_RCTL_ELS_REQ; + Type = FC_TYPE_ELS; } else { w5p = (WORD5 *)&(saveq->iocb.un.ulpWord[5]); Rctl = w5p->hcsw.Rctl; @@ -2079,8 +2089,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, if ((Rctl == 0) && (pring->ringno == LPFC_ELS_RING) && (irsp->ulpCommand == CMD_RCV_SEQUENCE64_CX || irsp->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) { - Rctl = FC_ELS_REQ; - Type = FC_ELS_DATA; + Rctl = FC_RCTL_ELS_REQ; + Type = FC_TYPE_ELS; w5p->hcsw.Rctl = Rctl; w5p->hcsw.Type = Type; } @@ -2324,168 +2334,6 @@ void lpfc_poll_eratt(unsigned long ptr) return; } -/** - * lpfc_sli_poll_fcp_ring - Handle FCP ring completion in polling mode - * @phba: Pointer to HBA context object. - * - * This function is called from lpfc_queuecommand, lpfc_poll_timeout, - * lpfc_abort_handler and lpfc_slave_configure when FCP_RING_POLLING - * is enabled. - * - * The caller does not hold any lock. - * The function processes each response iocb in the response ring until it - * finds an iocb with LE bit set and chains all the iocbs upto the iocb with - * LE bit set. The function will call the completion handler of the command iocb - * if the response iocb indicates a completion for a command iocb or it is - * an abort completion. - **/ -void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba) -{ - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING]; - IOCB_t *irsp = NULL; - IOCB_t *entry = NULL; - struct lpfc_iocbq *cmdiocbq = NULL; - struct lpfc_iocbq rspiocbq; - struct lpfc_pgp *pgp = &phba->port_gp[pring->ringno]; - uint32_t status; - uint32_t portRspPut, portRspMax; - int type; - uint32_t rsp_cmpl = 0; - uint32_t ha_copy; - unsigned long iflags; - - pring->stats.iocb_event++; - - /* - * The next available response entry should never exceed the maximum - * entries. If it does, treat it as an adapter hardware error. - */ - portRspMax = pring->numRiocb; - portRspPut = le32_to_cpu(pgp->rspPutInx); - if (unlikely(portRspPut >= portRspMax)) { - lpfc_sli_rsp_pointers_error(phba, pring); - return; - } - - rmb(); - while (pring->rspidx != portRspPut) { - entry = lpfc_resp_iocb(phba, pring); - if (++pring->rspidx >= portRspMax) - pring->rspidx = 0; - - lpfc_sli_pcimem_bcopy((uint32_t *) entry, - (uint32_t *) &rspiocbq.iocb, - phba->iocb_rsp_size); - irsp = &rspiocbq.iocb; - type = lpfc_sli_iocb_cmd_type(irsp->ulpCommand & CMD_IOCB_MASK); - pring->stats.iocb_rsp++; - rsp_cmpl++; - - if (unlikely(irsp->ulpStatus)) { - /* Rsp ring <ringno> error: IOCB */ - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "0326 Rsp Ring %d error: IOCB Data: " - "x%x x%x x%x x%x x%x x%x x%x x%x\n", - pring->ringno, - irsp->un.ulpWord[0], - irsp->un.ulpWord[1], - irsp->un.ulpWord[2], - irsp->un.ulpWord[3], - irsp->un.ulpWord[4], - irsp->un.ulpWord[5], - *(uint32_t *)&irsp->un1, - *((uint32_t *)&irsp->un1 + 1)); - } - - switch (type) { - case LPFC_ABORT_IOCB: - case LPFC_SOL_IOCB: - /* - * Idle exchange closed via ABTS from port. No iocb - * resources need to be recovered. - */ - if (unlikely(irsp->ulpCommand == CMD_XRI_ABORTED_CX)) { - lpfc_printf_log(phba, KERN_INFO, LOG_SLI, - "0314 IOCB cmd 0x%x " - "processed. Skipping " - "completion", - irsp->ulpCommand); - break; - } - - spin_lock_irqsave(&phba->hbalock, iflags); - cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring, - &rspiocbq); - spin_unlock_irqrestore(&phba->hbalock, iflags); - if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) { - (cmdiocbq->iocb_cmpl)(phba, cmdiocbq, - &rspiocbq); - } - break; - default: - if (irsp->ulpCommand == CMD_ADAPTER_MSG) { - char adaptermsg[LPFC_MAX_ADPTMSG]; - memset(adaptermsg, 0, LPFC_MAX_ADPTMSG); - memcpy(&adaptermsg[0], (uint8_t *) irsp, - MAX_MSG_DATA); - dev_warn(&((phba->pcidev)->dev), - "lpfc%d: %s\n", - phba->brd_no, adaptermsg); - } else { - /* Unknown IOCB command */ - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0321 Unknown IOCB command " - "Data: x%x, x%x x%x x%x x%x\n", - type, irsp->ulpCommand, - irsp->ulpStatus, - irsp->ulpIoTag, - irsp->ulpContext); - } - break; - } - - /* - * The response IOCB has been processed. Update the ring - * pointer in SLIM. If the port response put pointer has not - * been updated, sync the pgp->rspPutInx and fetch the new port - * response put pointer. - */ - writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx); - - if (pring->rspidx == portRspPut) - portRspPut = le32_to_cpu(pgp->rspPutInx); - } - - ha_copy = readl(phba->HAregaddr); - ha_copy >>= (LPFC_FCP_RING * 4); - - if ((rsp_cmpl > 0) && (ha_copy & HA_R0RE_REQ)) { - spin_lock_irqsave(&phba->hbalock, iflags); - pring->stats.iocb_rsp_full++; - status = ((CA_R0ATT | CA_R0RE_RSP) << (LPFC_FCP_RING * 4)); - writel(status, phba->CAregaddr); - readl(phba->CAregaddr); - spin_unlock_irqrestore(&phba->hbalock, iflags); - } - if ((ha_copy & HA_R0CE_RSP) && - (pring->flag & LPFC_CALL_RING_AVAILABLE)) { - spin_lock_irqsave(&phba->hbalock, iflags); - pring->flag &= ~LPFC_CALL_RING_AVAILABLE; - pring->stats.iocb_cmd_empty++; - - /* Force update of the local copy of cmdGetInx */ - pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); - lpfc_sli_resume_iocb(phba, pring); - - if ((pring->lpfc_sli_cmd_available)) - (pring->lpfc_sli_cmd_available) (phba, pring); - - spin_unlock_irqrestore(&phba->hbalock, iflags); - } - - return; -} /** * lpfc_sli_handle_fast_ring_event - Handle ring events on FCP ring @@ -2502,9 +2350,9 @@ void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba) * an abort completion. The function will call lpfc_sli_process_unsol_iocb * function if this is an unsolicited iocb. * This routine presumes LPFC_FCP_RING handling and doesn't bother - * to check it explicitly. This function always returns 1. - **/ -static int + * to check it explicitly. + */ +int lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, uint32_t mask) { @@ -2534,6 +2382,11 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba, spin_unlock_irqrestore(&phba->hbalock, iflag); return 1; } + if (phba->fcp_ring_in_use) { + spin_unlock_irqrestore(&phba->hbalock, iflag); + return 1; + } else + phba->fcp_ring_in_use = 1; rmb(); while (pring->rspidx != portRspPut) { @@ -2604,10 +2457,6 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba, cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring, &rspiocbq); if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) { - if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { - (cmdiocbq->iocb_cmpl)(phba, cmdiocbq, - &rspiocbq); - } else { spin_unlock_irqrestore(&phba->hbalock, iflag); (cmdiocbq->iocb_cmpl)(phba, cmdiocbq, @@ -2615,7 +2464,6 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba, spin_lock_irqsave(&phba->hbalock, iflag); } - } break; case LPFC_UNSOL_IOCB: spin_unlock_irqrestore(&phba->hbalock, iflag); @@ -2675,6 +2523,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba, } + phba->fcp_ring_in_use = 0; spin_unlock_irqrestore(&phba->hbalock, iflag); return rc; } @@ -3018,16 +2867,39 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, uint32_t mask) { struct lpfc_iocbq *irspiocbq; + struct hbq_dmabuf *dmabuf; + struct lpfc_cq_event *cq_event; unsigned long iflag; - while (!list_empty(&phba->sli4_hba.sp_rspiocb_work_queue)) { + spin_lock_irqsave(&phba->hbalock, iflag); + phba->hba_flag &= ~HBA_SP_QUEUE_EVT; + spin_unlock_irqrestore(&phba->hbalock, iflag); + while (!list_empty(&phba->sli4_hba.sp_queue_event)) { /* Get the response iocb from the head of work queue */ spin_lock_irqsave(&phba->hbalock, iflag); - list_remove_head(&phba->sli4_hba.sp_rspiocb_work_queue, - irspiocbq, struct lpfc_iocbq, list); + list_remove_head(&phba->sli4_hba.sp_queue_event, + cq_event, struct lpfc_cq_event, list); spin_unlock_irqrestore(&phba->hbalock, iflag); - /* Process the response iocb */ - lpfc_sli_sp_handle_rspiocb(phba, pring, irspiocbq); + + switch (bf_get(lpfc_wcqe_c_code, &cq_event->cqe.wcqe_cmpl)) { + case CQE_CODE_COMPL_WQE: + irspiocbq = container_of(cq_event, struct lpfc_iocbq, + cq_event); + /* Translate ELS WCQE to response IOCBQ */ + irspiocbq = lpfc_sli4_els_wcqe_to_rspiocbq(phba, + irspiocbq); + if (irspiocbq) + lpfc_sli_sp_handle_rspiocb(phba, pring, + irspiocbq); + break; + case CQE_CODE_RECEIVE: + dmabuf = container_of(cq_event, struct hbq_dmabuf, + cq_event); + lpfc_sli4_handle_received_buffer(phba, dmabuf); + break; + default: + break; + } } } @@ -3416,6 +3288,7 @@ lpfc_sli_brdreset(struct lpfc_hba *phba) /* perform board reset */ phba->fc_eventTag = 0; + phba->link_events = 0; phba->pport->fc_myDID = 0; phba->pport->fc_prevDID = 0; @@ -3476,6 +3349,7 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba) /* perform board reset */ phba->fc_eventTag = 0; + phba->link_events = 0; phba->pport->fc_myDID = 0; phba->pport->fc_prevDID = 0; @@ -3495,7 +3369,6 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba) list_del_init(&phba->sli4_hba.dat_rq->list); list_del_init(&phba->sli4_hba.mbx_cq->list); list_del_init(&phba->sli4_hba.els_cq->list); - list_del_init(&phba->sli4_hba.rxq_cq->list); for (qindx = 0; qindx < phba->cfg_fcp_wq_count; qindx++) list_del_init(&phba->sli4_hba.fcp_wq[qindx]->list); for (qindx = 0; qindx < phba->cfg_fcp_eq_count; qindx++) @@ -3531,9 +3404,13 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba) struct lpfc_sli *psli; volatile uint32_t word0; void __iomem *to_slim; + uint32_t hba_aer_enabled; spin_lock_irq(&phba->hbalock); + /* Take PCIe device Advanced Error Reporting (AER) state */ + hba_aer_enabled = phba->hba_flag & HBA_AER_ENABLED; + psli = &phba->sli; /* Restart HBA */ @@ -3573,6 +3450,10 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba) /* Give the INITFF and Post time to settle. */ mdelay(100); + /* Reset HBA AER if it was enabled, note hba_flag was reset above */ + if (hba_aer_enabled) + pci_disable_pcie_error_reporting(phba->pcidev); + lpfc_hba_down_post(phba); return 0; @@ -4042,6 +3923,24 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba) if (rc) goto lpfc_sli_hba_setup_error; + /* Enable PCIe device Advanced Error Reporting (AER) if configured */ + if (phba->cfg_aer_support == 1 && !(phba->hba_flag & HBA_AER_ENABLED)) { + rc = pci_enable_pcie_error_reporting(phba->pcidev); + if (!rc) { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "2709 This device supports " + "Advanced Error Reporting (AER)\n"); + spin_lock_irq(&phba->hbalock); + phba->hba_flag |= HBA_AER_ENABLED; + spin_unlock_irq(&phba->hbalock); + } else { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "2708 This device does not support " + "Advanced Error Reporting (AER)\n"); + phba->cfg_aer_support = 0; + } + } + if (phba->sli_rev == 3) { phba->iocb_cmd_size = SLI3_IOCB_CMD_SIZE; phba->iocb_rsp_size = SLI3_IOCB_RSP_SIZE; @@ -4163,7 +4062,7 @@ lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba, * addition, this routine gets the port vpd data. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - could not allocated memory. **/ static int @@ -4243,7 +4142,6 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM); lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM); - lpfc_sli4_cq_release(phba->sli4_hba.rxq_cq, LPFC_QUEUE_REARM); for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[fcp_eqidx], LPFC_QUEUE_REARM); @@ -4322,6 +4220,13 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) phba->sli_rev = bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev); if (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev)) phba->hba_flag |= HBA_FCOE_SUPPORT; + + if (bf_get(lpfc_mbx_rd_rev_cee_ver, &mqe->un.read_rev) == + LPFC_DCBX_CEE_MODE) + phba->hba_flag |= HBA_FIP_SUPPORT; + else + phba->hba_flag &= ~HBA_FIP_SUPPORT; + if (phba->sli_rev != LPFC_SLI_REV4 || !(phba->hba_flag & HBA_FCOE_SUPPORT)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, @@ -4468,7 +4373,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) rc = lpfc_sli4_post_sgl_list(phba); if (unlikely(rc)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, - "0582 Error %d during sgl post operation", rc); + "0582 Error %d during sgl post operation\n", + rc); rc = -ENODEV; goto out_free_vpd; } @@ -4477,8 +4383,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) rc = lpfc_sli4_repost_scsi_sgl_list(phba); if (unlikely(rc)) { lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI, - "0383 Error %d during scsi sgl post opeation", - rc); + "0383 Error %d during scsi sgl post " + "operation\n", rc); /* Some Scsi buffers were moved to the abort scsi list */ /* A pci function reset will repost them */ rc = -ENODEV; @@ -4494,10 +4400,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) rc = -ENODEV; goto out_free_vpd; } - if (phba->cfg_enable_fip) - bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 1); - else - bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 0); /* Set up all the queues to the device */ rc = lpfc_sli4_queue_setup(phba); @@ -5669,7 +5571,7 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number, case CMD_GEN_REQUEST64_CX: if (!(phba->sli.sli_flag & LPFC_MENLO_MAINT) || (piocb->iocb.un.genreq64.w5.hcsw.Rctl != - FC_FCP_CMND) || + FC_RCTL_DD_UNSOL_CMD) || (piocb->iocb.un.genreq64.w5.hcsw.Type != MENLO_TRANSPORT_TYPE)) @@ -5849,7 +5751,7 @@ static int lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, union lpfc_wqe *wqe) { - uint32_t payload_len = 0; + uint32_t xmit_len = 0, total_len = 0; uint8_t ct = 0; uint32_t fip; uint32_t abort_tag; @@ -5857,12 +5759,15 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, uint8_t cmnd; uint16_t xritag; struct ulp_bde64 *bpl = NULL; + uint32_t els_id = ELS_ID_DEFAULT; + int numBdes, i; + struct ulp_bde64 bde; - fip = bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags); + fip = phba->hba_flag & HBA_FIP_SUPPORT; /* The fcp commands will set command type */ if (iocbq->iocb_flag & LPFC_IO_FCP) command_type = FCP_COMMAND; - else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS)) + else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK)) command_type = ELS_COMMAND_FIP; else command_type = ELS_COMMAND_NON_FIP; @@ -5874,6 +5779,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, wqe->words[7] = 0; /* The ct field has moved so reset */ /* words0-2 bpl convert bde */ if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) { + numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize / + sizeof(struct ulp_bde64); bpl = (struct ulp_bde64 *) ((struct lpfc_dmabuf *)iocbq->context3)->virt; if (!bpl) @@ -5886,9 +5793,14 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, * can assign it to the sgl. */ wqe->generic.bde.tus.w = le32_to_cpu(bpl->tus.w); - payload_len = wqe->generic.bde.tus.f.bdeSize; + xmit_len = wqe->generic.bde.tus.f.bdeSize; + total_len = 0; + for (i = 0; i < numBdes; i++) { + bde.tus.w = le32_to_cpu(bpl[i].tus.w); + total_len += bde.tus.f.bdeSize; + } } else - payload_len = iocbq->iocb.un.fcpi64.bdl.bdeSize; + xmit_len = iocbq->iocb.un.fcpi64.bdl.bdeSize; iocbq->iocb.ulpIoTag = iocbq->iotag; cmnd = iocbq->iocb.ulpCommand; @@ -5902,7 +5814,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, iocbq->iocb.ulpCommand); return IOCB_ERROR; } - wqe->els_req.payload_len = payload_len; + wqe->els_req.payload_len = xmit_len; /* Els_reguest64 has a TMO */ bf_set(wqe_tmo, &wqe->els_req.wqe_com, iocbq->iocb.ulpTimeout); @@ -5923,7 +5835,23 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(lpfc_wqe_gen_ct, &wqe->generic, ct); bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0); /* CCP CCPE PV PRI in word10 were set in the memcpy */ + + if (command_type == ELS_COMMAND_FIP) { + els_id = ((iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK) + >> LPFC_FIP_ELS_ID_SHIFT); + } + bf_set(lpfc_wqe_gen_els_id, &wqe->generic, els_id); + break; + case CMD_XMIT_SEQUENCE64_CX: + bf_set(lpfc_wqe_gen_context, &wqe->generic, + iocbq->iocb.un.ulpWord[3]); + wqe->generic.word3 = 0; + bf_set(wqe_rcvoxid, &wqe->generic, iocbq->iocb.ulpContext); + bf_set(wqe_xc, &wqe->generic, 1); + /* The entire sequence is transmitted for this IOCB */ + xmit_len = total_len; + cmnd = CMD_XMIT_SEQUENCE64_CR; case CMD_XMIT_SEQUENCE64_CR: /* word3 iocb=io_tag32 wqe=payload_offset */ /* payload offset used for multilpe outstanding @@ -5933,7 +5861,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, /* word4 relative_offset memcpy */ /* word5 r_ctl/df_ctl memcpy */ bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0); - wqe->xmit_sequence.xmit_len = payload_len; + wqe->xmit_sequence.xmit_len = xmit_len; + command_type = OTHER_COMMAND; break; case CMD_XMIT_BCAST64_CN: /* word3 iocb=iotag32 wqe=payload_len */ @@ -5962,7 +5891,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, case CMD_FCP_IREAD64_CR: /* FCP_CMD is always the 1st sgl entry */ wqe->fcp_iread.payload_len = - payload_len + sizeof(struct fcp_rsp); + xmit_len + sizeof(struct fcp_rsp); /* word 4 (xfer length) should have been set on the memcpy */ @@ -5999,7 +5928,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, * sgl[1] = rsp. * */ - wqe->gen_req.command_len = payload_len; + wqe->gen_req.command_len = xmit_len; /* Word4 parameter copied in the memcpy */ /* Word5 [rctl, type, df_ctl, la] copied in memcpy */ /* word6 context tag copied in memcpy */ @@ -6066,6 +5995,38 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, command_type = OTHER_COMMAND; xritag = 0; break; + case CMD_XMIT_BLS_RSP64_CX: + /* As BLS ABTS-ACC WQE is very different from other WQEs, + * we re-construct this WQE here based on information in + * iocbq from scratch. + */ + memset(wqe, 0, sizeof(union lpfc_wqe)); + /* OX_ID is invariable to who sent ABTS to CT exchange */ + bf_set(xmit_bls_rsp64_oxid, &wqe->xmit_bls_rsp, + bf_get(lpfc_abts_oxid, &iocbq->iocb.un.bls_acc)); + if (bf_get(lpfc_abts_orig, &iocbq->iocb.un.bls_acc) == + LPFC_ABTS_UNSOL_INT) { + /* ABTS sent by initiator to CT exchange, the + * RX_ID field will be filled with the newly + * allocated responder XRI. + */ + bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp, + iocbq->sli4_xritag); + } else { + /* ABTS sent by responder to CT exchange, the + * RX_ID field will be filled with the responder + * RX_ID from ABTS. + */ + bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp, + bf_get(lpfc_abts_rxid, &iocbq->iocb.un.bls_acc)); + } + bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff); + bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1); + bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com, + iocbq->iocb.ulpContext); + /* Overwrite the pre-set comnd type with OTHER_COMMAND */ + command_type = OTHER_COMMAND; + break; case CMD_XRI_ABORTED_CX: case CMD_CREATE_XRI_CR: /* Do we expect to use this? */ /* words0-2 are all 0's no bde */ @@ -6120,11 +6081,10 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, uint16_t xritag; union lpfc_wqe wqe; struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number]; - uint32_t fcp_wqidx; if (piocb->sli4_xritag == NO_XRI) { if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN || - piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN) + piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN) sglq = NULL; else { sglq = __lpfc_sli_get_sglq(phba); @@ -6155,8 +6115,17 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, return IOCB_ERROR; if (piocb->iocb_flag & LPFC_IO_FCP) { - fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba); - if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[fcp_wqidx], &wqe)) + /* + * For FCP command IOCB, get a new WQ index to distribute + * WQE across the WQsr. On the other hand, for abort IOCB, + * it carries the same WQ index to the original command + * IOCB. + */ + if ((piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) && + (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN)) + piocb->fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba); + if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx], + &wqe)) return IOCB_ERROR; } else { if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe)) @@ -6449,31 +6418,37 @@ lpfc_sli_setup(struct lpfc_hba *phba) pring->iotag_max = 4096; pring->lpfc_sli_rcv_async_status = lpfc_sli_async_event_handler; - pring->num_mask = 4; + pring->num_mask = LPFC_MAX_RING_MASK; pring->prt[0].profile = 0; /* Mask 0 */ - pring->prt[0].rctl = FC_ELS_REQ; - pring->prt[0].type = FC_ELS_DATA; + pring->prt[0].rctl = FC_RCTL_ELS_REQ; + pring->prt[0].type = FC_TYPE_ELS; pring->prt[0].lpfc_sli_rcv_unsol_event = lpfc_els_unsol_event; pring->prt[1].profile = 0; /* Mask 1 */ - pring->prt[1].rctl = FC_ELS_RSP; - pring->prt[1].type = FC_ELS_DATA; + pring->prt[1].rctl = FC_RCTL_ELS_REP; + pring->prt[1].type = FC_TYPE_ELS; pring->prt[1].lpfc_sli_rcv_unsol_event = lpfc_els_unsol_event; pring->prt[2].profile = 0; /* Mask 2 */ /* NameServer Inquiry */ - pring->prt[2].rctl = FC_UNSOL_CTL; + pring->prt[2].rctl = FC_RCTL_DD_UNSOL_CTL; /* NameServer */ - pring->prt[2].type = FC_COMMON_TRANSPORT_ULP; + pring->prt[2].type = FC_TYPE_CT; pring->prt[2].lpfc_sli_rcv_unsol_event = lpfc_ct_unsol_event; pring->prt[3].profile = 0; /* Mask 3 */ /* NameServer response */ - pring->prt[3].rctl = FC_SOL_CTL; + pring->prt[3].rctl = FC_RCTL_DD_SOL_CTL; /* NameServer */ - pring->prt[3].type = FC_COMMON_TRANSPORT_ULP; + pring->prt[3].type = FC_TYPE_CT; pring->prt[3].lpfc_sli_rcv_unsol_event = lpfc_ct_unsol_event; + /* abort unsolicited sequence */ + pring->prt[4].profile = 0; /* Mask 4 */ + pring->prt[4].rctl = FC_RCTL_BA_ABTS; + pring->prt[4].type = FC_TYPE_BLS; + pring->prt[4].lpfc_sli_rcv_unsol_event = + lpfc_sli4_ct_abort_unsol_event; break; } totiocbsize += (pring->numCiocb * pring->sizeCiocb) + @@ -6976,8 +6951,18 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, abort_iotag = cmdiocb->iocb.un.acxri.abortIoTag; spin_lock_irq(&phba->hbalock); - if (abort_iotag != 0 && abort_iotag <= phba->sli.last_iotag) - abort_iocb = phba->sli.iocbq_lookup[abort_iotag]; + if (phba->sli_rev < LPFC_SLI_REV4) { + if (abort_iotag != 0 && + abort_iotag <= phba->sli.last_iotag) + abort_iocb = + phba->sli.iocbq_lookup[abort_iotag]; + } else + /* For sli4 the abort_tag is the XRI, + * so the abort routine puts the iotag of the iocb + * being aborted in the context field of the abort + * IOCB. + */ + abort_iocb = phba->sli.iocbq_lookup[abort_context]; lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_SLI, "0327 Cannot abort els iocb %p " @@ -6991,9 +6976,18 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * might have completed already. Do not free it again. */ if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) { - spin_unlock_irq(&phba->hbalock); - lpfc_sli_release_iocbq(phba, cmdiocb); - return; + if (irsp->un.ulpWord[4] != IOERR_NO_XRI) { + spin_unlock_irq(&phba->hbalock); + lpfc_sli_release_iocbq(phba, cmdiocb); + return; + } + /* For SLI4 the ulpContext field for abort IOCB + * holds the iotag of the IOCB being aborted so + * the local abort_context needs to be reset to + * match the aborted IOCBs ulpContext. + */ + if (abort_iocb && phba->sli_rev == LPFC_SLI_REV4) + abort_context = abort_iocb->iocb.ulpContext; } /* * make sure we have the right iocbq before taking it @@ -7112,13 +7106,18 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, iabt = &abtsiocbp->iocb; iabt->un.acxri.abortType = ABORT_TYPE_ABTS; iabt->un.acxri.abortContextTag = icmd->ulpContext; - if (phba->sli_rev == LPFC_SLI_REV4) + if (phba->sli_rev == LPFC_SLI_REV4) { iabt->un.acxri.abortIoTag = cmdiocb->sli4_xritag; + iabt->un.acxri.abortContextTag = cmdiocb->iotag; + } else iabt->un.acxri.abortIoTag = icmd->ulpIoTag; iabt->ulpLe = 1; iabt->ulpClass = icmd->ulpClass; + /* ABTS WQE must go to the same WQ as the WQE to be aborted */ + abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx; + if (phba->link_state >= LPFC_LINK_UP) iabt->ulpCommand = CMD_ABORT_XRI_CN; else @@ -7322,6 +7321,9 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, abtsiocb->iocb.ulpClass = cmd->ulpClass; abtsiocb->vport = phba->pport; + /* ABTS WQE must go to the same WQ as the WQE to be aborted */ + abtsiocb->fcp_wqidx = iocbq->fcp_wqidx; + if (lpfc_is_link_up(phba)) abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN; else @@ -7687,31 +7689,28 @@ static int lpfc_sli4_eratt_read(struct lpfc_hba *phba) { uint32_t uerr_sta_hi, uerr_sta_lo; - uint32_t onlnreg0, onlnreg1; /* For now, use the SLI4 device internal unrecoverable error * registers for error attention. This can be changed later. */ - onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr); - onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr); - if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) { - uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr); - uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr); - if (uerr_sta_lo || uerr_sta_hi) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "1423 HBA Unrecoverable error: " - "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, " - "online0_reg=0x%x, online1_reg=0x%x\n", - uerr_sta_lo, uerr_sta_hi, - onlnreg0, onlnreg1); - phba->work_status[0] = uerr_sta_lo; - phba->work_status[1] = uerr_sta_hi; - /* Set the driver HA work bitmap */ - phba->work_ha |= HA_ERATT; - /* Indicate polling handles this ERATT */ - phba->hba_flag |= HBA_ERATT_HANDLED; - return 1; - } + uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr); + uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr); + if ((~phba->sli4_hba.ue_mask_lo & uerr_sta_lo) || + (~phba->sli4_hba.ue_mask_hi & uerr_sta_hi)) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "1423 HBA Unrecoverable error: " + "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, " + "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n", + uerr_sta_lo, uerr_sta_hi, + phba->sli4_hba.ue_mask_lo, + phba->sli4_hba.ue_mask_hi); + phba->work_status[0] = uerr_sta_lo; + phba->work_status[1] = uerr_sta_hi; + /* Set the driver HA work bitmap */ + phba->work_ha |= HA_ERATT; + /* Indicate polling handles this ERATT */ + phba->hba_flag |= HBA_ERATT_HANDLED; + return 1; } return 0; } @@ -7834,7 +7833,7 @@ irqreturn_t lpfc_sli_sp_intr_handler(int irq, void *dev_id) { struct lpfc_hba *phba; - uint32_t ha_copy; + uint32_t ha_copy, hc_copy; uint32_t work_ha_copy; unsigned long status; unsigned long iflag; @@ -7892,8 +7891,13 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) } /* Clear up only attention source related to slow-path */ + hc_copy = readl(phba->HCregaddr); + writel(hc_copy & ~(HC_MBINT_ENA | HC_R2INT_ENA | + HC_LAINT_ENA | HC_ERINT_ENA), + phba->HCregaddr); writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)), phba->HAregaddr); + writel(hc_copy, phba->HCregaddr); readl(phba->HAregaddr); /* flush */ spin_unlock_irqrestore(&phba->hbalock, iflag); } else @@ -8049,7 +8053,7 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) KERN_ERR, LOG_MBOX | LOG_SLI, "0350 rc should have" - "been MBX_BUSY"); + "been MBX_BUSY\n"); if (rc != MBX_NOT_FINISHED) goto send_current_mbox; } @@ -8078,7 +8082,7 @@ send_current_mbox: if (rc != MBX_SUCCESS) lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "0349 rc should be " - "MBX_SUCCESS"); + "MBX_SUCCESS\n"); } spin_lock_irqsave(&phba->hbalock, iflag); @@ -8203,6 +8207,7 @@ lpfc_sli_intr_handler(int irq, void *dev_id) struct lpfc_hba *phba; irqreturn_t sp_irq_rc, fp_irq_rc; unsigned long status1, status2; + uint32_t hc_copy; /* * Get the driver's phba structure from the dev_id and @@ -8240,7 +8245,12 @@ lpfc_sli_intr_handler(int irq, void *dev_id) } /* Clear attention sources except link and error attentions */ + hc_copy = readl(phba->HCregaddr); + writel(hc_copy & ~(HC_MBINT_ENA | HC_R0INT_ENA | HC_R1INT_ENA + | HC_R2INT_ENA | HC_LAINT_ENA | HC_ERINT_ENA), + phba->HCregaddr); writel((phba->ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr); + writel(hc_copy, phba->HCregaddr); readl(phba->HAregaddr); /* flush */ spin_unlock(&phba->hbalock); @@ -8351,8 +8361,6 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn, memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset, sizeof(struct lpfc_iocbq) - offset); - memset(&pIocbIn->sli4_info, 0, - sizeof(struct lpfc_sli4_rspiocb_info)); /* Map WCQE parameters into irspiocb parameters */ pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe); if (pIocbOut->iocb_flag & LPFC_IO_FCP) @@ -8364,16 +8372,49 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn, pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter; else pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter; - /* Load in additional WCQE parameters */ - pIocbIn->sli4_info.hw_status = bf_get(lpfc_wcqe_c_hw_status, wcqe); - pIocbIn->sli4_info.bfield = 0; - if (bf_get(lpfc_wcqe_c_xb, wcqe)) - pIocbIn->sli4_info.bfield |= LPFC_XB; - if (bf_get(lpfc_wcqe_c_pv, wcqe)) { - pIocbIn->sli4_info.bfield |= LPFC_PV; - pIocbIn->sli4_info.priority = - bf_get(lpfc_wcqe_c_priority, wcqe); +} + +/** + * lpfc_sli4_els_wcqe_to_rspiocbq - Get response iocbq from els wcqe + * @phba: Pointer to HBA context object. + * @wcqe: Pointer to work-queue completion queue entry. + * + * This routine handles an ELS work-queue completion event and construct + * a pseudo response ELS IODBQ from the SLI4 ELS WCQE for the common + * discovery engine to handle. + * + * Return: Pointer to the receive IOCBQ, NULL otherwise. + **/ +static struct lpfc_iocbq * +lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba, + struct lpfc_iocbq *irspiocbq) +{ + struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_iocbq *cmdiocbq; + struct lpfc_wcqe_complete *wcqe; + unsigned long iflags; + + wcqe = &irspiocbq->cq_event.cqe.wcqe_cmpl; + spin_lock_irqsave(&phba->hbalock, iflags); + pring->stats.iocb_event++; + /* Look up the ELS command IOCB and create pseudo response IOCB */ + cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring, + bf_get(lpfc_wcqe_c_request_tag, wcqe)); + spin_unlock_irqrestore(&phba->hbalock, iflags); + + if (unlikely(!cmdiocbq)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, + "0386 ELS complete with no corresponding " + "cmdiocb: iotag (%d)\n", + bf_get(lpfc_wcqe_c_request_tag, wcqe)); + lpfc_sli_release_iocbq(phba, irspiocbq); + return NULL; } + + /* Fake the irspiocbq and copy necessary response information */ + lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe); + + return irspiocbq; } /** @@ -8566,45 +8607,26 @@ static bool lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba, struct lpfc_wcqe_complete *wcqe) { - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; - struct lpfc_iocbq *cmdiocbq; struct lpfc_iocbq *irspiocbq; unsigned long iflags; - bool workposted = false; - - spin_lock_irqsave(&phba->hbalock, iflags); - pring->stats.iocb_event++; - /* Look up the ELS command IOCB and create pseudo response IOCB */ - cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring, - bf_get(lpfc_wcqe_c_request_tag, wcqe)); - spin_unlock_irqrestore(&phba->hbalock, iflags); - if (unlikely(!cmdiocbq)) { - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "0386 ELS complete with no corresponding " - "cmdiocb: iotag (%d)\n", - bf_get(lpfc_wcqe_c_request_tag, wcqe)); - return workposted; - } - - /* Fake the irspiocbq and copy necessary response information */ + /* Get an irspiocbq for later ELS response processing use */ irspiocbq = lpfc_sli_get_iocbq(phba); if (!irspiocbq) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0387 Failed to allocate an iocbq\n"); - return workposted; + return false; } - lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe); - /* Add the irspiocb to the response IOCB work list */ + /* Save off the slow-path queue event for work thread to process */ + memcpy(&irspiocbq->cq_event.cqe.wcqe_cmpl, wcqe, sizeof(*wcqe)); spin_lock_irqsave(&phba->hbalock, iflags); - list_add_tail(&irspiocbq->list, &phba->sli4_hba.sp_rspiocb_work_queue); - /* Indicate ELS ring attention */ - phba->work_ha |= (HA_R0ATT << (4*LPFC_ELS_RING)); + list_add_tail(&irspiocbq->cq_event.list, + &phba->sli4_hba.sp_queue_event); + phba->hba_flag |= HBA_SP_QUEUE_EVT; spin_unlock_irqrestore(&phba->hbalock, iflags); - workposted = true; - return workposted; + return true; } /** @@ -8690,52 +8712,6 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba, } /** - * lpfc_sli4_sp_handle_wcqe - Process a work-queue completion queue entry - * @phba: Pointer to HBA context object. - * @cq: Pointer to the completion queue. - * @wcqe: Pointer to a completion queue entry. - * - * This routine process a slow-path work-queue completion queue entry. - * - * Return: true if work posted to worker thread, otherwise false. - **/ -static bool -lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, - struct lpfc_cqe *cqe) -{ - struct lpfc_wcqe_complete wcqe; - bool workposted = false; - - /* Copy the work queue CQE and convert endian order if needed */ - lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe)); - - /* Check and process for different type of WCQE and dispatch */ - switch (bf_get(lpfc_wcqe_c_code, &wcqe)) { - case CQE_CODE_COMPL_WQE: - /* Process the WQ complete event */ - workposted = lpfc_sli4_sp_handle_els_wcqe(phba, - (struct lpfc_wcqe_complete *)&wcqe); - break; - case CQE_CODE_RELEASE_WQE: - /* Process the WQ release event */ - lpfc_sli4_sp_handle_rel_wcqe(phba, - (struct lpfc_wcqe_release *)&wcqe); - break; - case CQE_CODE_XRI_ABORTED: - /* Process the WQ XRI abort event */ - workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq, - (struct sli4_wcqe_xri_aborted *)&wcqe); - break; - default: - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0388 Not a valid WCQE code: x%x\n", - bf_get(lpfc_wcqe_c_code, &wcqe)); - break; - } - return workposted; -} - -/** * lpfc_sli4_sp_handle_rcqe - Process a receive-queue completion queue entry * @phba: Pointer to HBA context object. * @rcqe: Pointer to receive-queue completion queue entry. @@ -8745,9 +8721,8 @@ lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, * Return: true if work posted to worker thread, otherwise false. **/ static bool -lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe) +lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe) { - struct lpfc_rcqe rcqe; bool workposted = false; struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq; struct lpfc_queue *drq = phba->sli4_hba.dat_rq; @@ -8755,31 +8730,28 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe) uint32_t status; unsigned long iflags; - /* Copy the receive queue CQE and convert endian order if needed */ - lpfc_sli_pcimem_bcopy(cqe, &rcqe, sizeof(struct lpfc_rcqe)); - lpfc_sli4_rq_release(hrq, drq); - if (bf_get(lpfc_rcqe_code, &rcqe) != CQE_CODE_RECEIVE) - goto out; - if (bf_get(lpfc_rcqe_rq_id, &rcqe) != hrq->queue_id) + if (bf_get(lpfc_rcqe_rq_id, rcqe) != hrq->queue_id) goto out; - status = bf_get(lpfc_rcqe_status, &rcqe); + status = bf_get(lpfc_rcqe_status, rcqe); switch (status) { case FC_STATUS_RQ_BUF_LEN_EXCEEDED: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "2537 Receive Frame Truncated!!\n"); case FC_STATUS_RQ_SUCCESS: + lpfc_sli4_rq_release(hrq, drq); spin_lock_irqsave(&phba->hbalock, iflags); dma_buf = lpfc_sli_hbqbuf_get(&phba->hbqs[0].hbq_buffer_list); if (!dma_buf) { spin_unlock_irqrestore(&phba->hbalock, iflags); goto out; } - memcpy(&dma_buf->rcqe, &rcqe, sizeof(rcqe)); + memcpy(&dma_buf->cq_event.cqe.rcqe_cmpl, rcqe, sizeof(*rcqe)); /* save off the frame for the word thread to process */ - list_add_tail(&dma_buf->dbuf.list, &phba->rb_pend_list); + list_add_tail(&dma_buf->cq_event.list, + &phba->sli4_hba.sp_queue_event); /* Frame received */ - phba->hba_flag |= HBA_RECEIVE_BUFFER; + phba->hba_flag |= HBA_SP_QUEUE_EVT; spin_unlock_irqrestore(&phba->hbalock, iflags); workposted = true; break; @@ -8794,7 +8766,58 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe) } out: return workposted; +} + +/** + * lpfc_sli4_sp_handle_cqe - Process a slow path completion queue entry + * @phba: Pointer to HBA context object. + * @cq: Pointer to the completion queue. + * @wcqe: Pointer to a completion queue entry. + * + * This routine process a slow-path work-queue or recieve queue completion queue + * entry. + * + * Return: true if work posted to worker thread, otherwise false. + **/ +static bool +lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, + struct lpfc_cqe *cqe) +{ + struct lpfc_cqe cqevt; + bool workposted = false; + + /* Copy the work queue CQE and convert endian order if needed */ + lpfc_sli_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe)); + /* Check and process for different type of WCQE and dispatch */ + switch (bf_get(lpfc_cqe_code, &cqevt)) { + case CQE_CODE_COMPL_WQE: + /* Process the WQ/RQ complete event */ + workposted = lpfc_sli4_sp_handle_els_wcqe(phba, + (struct lpfc_wcqe_complete *)&cqevt); + break; + case CQE_CODE_RELEASE_WQE: + /* Process the WQ release event */ + lpfc_sli4_sp_handle_rel_wcqe(phba, + (struct lpfc_wcqe_release *)&cqevt); + break; + case CQE_CODE_XRI_ABORTED: + /* Process the WQ XRI abort event */ + workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq, + (struct sli4_wcqe_xri_aborted *)&cqevt); + break; + case CQE_CODE_RECEIVE: + /* Process the RQ event */ + workposted = lpfc_sli4_sp_handle_rcqe(phba, + (struct lpfc_rcqe *)&cqevt); + break; + default: + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "0388 Not a valid WCQE code: x%x\n", + bf_get(lpfc_cqe_code, &cqevt)); + break; + } + return workposted; } /** @@ -8858,14 +8881,7 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe) break; case LPFC_WCQ: while ((cqe = lpfc_sli4_cq_get(cq))) { - workposted |= lpfc_sli4_sp_handle_wcqe(phba, cq, cqe); - if (!(++ecount % LPFC_GET_QE_REL_INT)) - lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM); - } - break; - case LPFC_RCQ: - while ((cqe = lpfc_sli4_cq_get(cq))) { - workposted |= lpfc_sli4_sp_handle_rcqe(phba, cqe); + workposted |= lpfc_sli4_sp_handle_cqe(phba, cq, cqe); if (!(++ecount % LPFC_GET_QE_REL_INT)) lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM); } @@ -10427,8 +10443,7 @@ lpfc_sli4_next_xritag(struct lpfc_hba *phba) return xritag; } spin_unlock_irq(&phba->hbalock); - - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, "2004 Failed to allocate XRI.last XRITAG is %d" " Max XRI is %d, Used XRI is %d\n", phba->sli4_hba.next_xri, @@ -10492,15 +10507,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) lpfc_sli4_mbox_cmd_free(phba, mbox); return -ENOMEM; } - /* Get the first SGE entry from the non-embedded DMA memory */ - if (unlikely(!mbox->sge_array)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, - "2525 Failed to get the non-embedded SGE " - "virtual address\n"); - lpfc_sli4_mbox_cmd_free(phba, mbox); - return -ENOMEM; - } viraddr = mbox->sge_array->addr[0]; /* Set up the SGL pages in the non-embedded DMA pages */ @@ -10524,8 +10531,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) sgl_pg_pairs++; } bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start); - pg_pairs = (pg_pairs > 0) ? (pg_pairs - 1) : pg_pairs; - bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs); + bf_set(lpfc_post_sgl_pages_xricnt, sgl, els_xri_cnt); /* Perform endian conversion if necessary */ sgl->word0 = cpu_to_le32(sgl->word0); @@ -10607,15 +10613,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist, lpfc_sli4_mbox_cmd_free(phba, mbox); return -ENOMEM; } - /* Get the first SGE entry from the non-embedded DMA memory */ - if (unlikely(!mbox->sge_array)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, - "2565 Failed to get the non-embedded SGE " - "virtual address\n"); - lpfc_sli4_mbox_cmd_free(phba, mbox); - return -ENOMEM; - } viraddr = mbox->sge_array->addr[0]; /* Set up the SGL pages in the non-embedded DMA pages */ @@ -10802,6 +10800,105 @@ lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr, } /** + * lpfc_update_rcv_time_stamp - Update vport's rcv seq time stamp + * @vport: The vport to work on. + * + * This function updates the receive sequence time stamp for this vport. The + * receive sequence time stamp indicates the time that the last frame of the + * the sequence that has been idle for the longest amount of time was received. + * the driver uses this time stamp to indicate if any received sequences have + * timed out. + **/ +void +lpfc_update_rcv_time_stamp(struct lpfc_vport *vport) +{ + struct lpfc_dmabuf *h_buf; + struct hbq_dmabuf *dmabuf = NULL; + + /* get the oldest sequence on the rcv list */ + h_buf = list_get_first(&vport->rcv_buffer_list, + struct lpfc_dmabuf, list); + if (!h_buf) + return; + dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf); + vport->rcv_buffer_time_stamp = dmabuf->time_stamp; +} + +/** + * lpfc_cleanup_rcv_buffers - Cleans up all outstanding receive sequences. + * @vport: The vport that the received sequences were sent to. + * + * This function cleans up all outstanding received sequences. This is called + * by the driver when a link event or user action invalidates all the received + * sequences. + **/ +void +lpfc_cleanup_rcv_buffers(struct lpfc_vport *vport) +{ + struct lpfc_dmabuf *h_buf, *hnext; + struct lpfc_dmabuf *d_buf, *dnext; + struct hbq_dmabuf *dmabuf = NULL; + + /* start with the oldest sequence on the rcv list */ + list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) { + dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf); + list_del_init(&dmabuf->hbuf.list); + list_for_each_entry_safe(d_buf, dnext, + &dmabuf->dbuf.list, list) { + list_del_init(&d_buf->list); + lpfc_in_buf_free(vport->phba, d_buf); + } + lpfc_in_buf_free(vport->phba, &dmabuf->dbuf); + } +} + +/** + * lpfc_rcv_seq_check_edtov - Cleans up timed out receive sequences. + * @vport: The vport that the received sequences were sent to. + * + * This function determines whether any received sequences have timed out by + * first checking the vport's rcv_buffer_time_stamp. If this time_stamp + * indicates that there is at least one timed out sequence this routine will + * go through the received sequences one at a time from most inactive to most + * active to determine which ones need to be cleaned up. Once it has determined + * that a sequence needs to be cleaned up it will simply free up the resources + * without sending an abort. + **/ +void +lpfc_rcv_seq_check_edtov(struct lpfc_vport *vport) +{ + struct lpfc_dmabuf *h_buf, *hnext; + struct lpfc_dmabuf *d_buf, *dnext; + struct hbq_dmabuf *dmabuf = NULL; + unsigned long timeout; + int abort_count = 0; + + timeout = (msecs_to_jiffies(vport->phba->fc_edtov) + + vport->rcv_buffer_time_stamp); + if (list_empty(&vport->rcv_buffer_list) || + time_before(jiffies, timeout)) + return; + /* start with the oldest sequence on the rcv list */ + list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) { + dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf); + timeout = (msecs_to_jiffies(vport->phba->fc_edtov) + + dmabuf->time_stamp); + if (time_before(jiffies, timeout)) + break; + abort_count++; + list_del_init(&dmabuf->hbuf.list); + list_for_each_entry_safe(d_buf, dnext, + &dmabuf->dbuf.list, list) { + list_del_init(&d_buf->list); + lpfc_in_buf_free(vport->phba, d_buf); + } + lpfc_in_buf_free(vport->phba, &dmabuf->dbuf); + } + if (abort_count) + lpfc_update_rcv_time_stamp(vport); +} + +/** * lpfc_fc_frame_add - Adds a frame to the vport's list of received sequences * @dmabuf: pointer to a dmabuf that describes the hdr and data of the FC frame * @@ -10823,6 +10920,8 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf) struct hbq_dmabuf *seq_dmabuf = NULL; struct hbq_dmabuf *temp_dmabuf = NULL; + INIT_LIST_HEAD(&dmabuf->dbuf.list); + dmabuf->time_stamp = jiffies; new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt; /* Use the hdr_buf to find the sequence that this frame belongs to */ list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) { @@ -10841,13 +10940,21 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf) * Queue the buffer on the vport's rcv_buffer_list. */ list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list); + lpfc_update_rcv_time_stamp(vport); return dmabuf; } temp_hdr = seq_dmabuf->hbuf.virt; if (new_hdr->fh_seq_cnt < temp_hdr->fh_seq_cnt) { - list_add(&seq_dmabuf->dbuf.list, &dmabuf->dbuf.list); + list_del_init(&seq_dmabuf->hbuf.list); + list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list); + list_add_tail(&dmabuf->dbuf.list, &seq_dmabuf->dbuf.list); + lpfc_update_rcv_time_stamp(vport); return dmabuf; } + /* move this sequence to the tail to indicate a young sequence */ + list_move_tail(&seq_dmabuf->hbuf.list, &vport->rcv_buffer_list); + seq_dmabuf->time_stamp = jiffies; + lpfc_update_rcv_time_stamp(vport); /* find the correct place in the sequence to insert this frame */ list_for_each_entry_reverse(d_buf, &seq_dmabuf->dbuf.list, list) { temp_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf); @@ -10865,6 +10972,210 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf) } /** + * lpfc_sli4_abort_partial_seq - Abort partially assembled unsol sequence + * @vport: pointer to a vitural port + * @dmabuf: pointer to a dmabuf that describes the FC sequence + * + * This function tries to abort from the partially assembed sequence, described + * by the information from basic abbort @dmabuf. It checks to see whether such + * partially assembled sequence held by the driver. If so, it shall free up all + * the frames from the partially assembled sequence. + * + * Return + * true -- if there is matching partially assembled sequence present and all + * the frames freed with the sequence; + * false -- if there is no matching partially assembled sequence present so + * nothing got aborted in the lower layer driver + **/ +static bool +lpfc_sli4_abort_partial_seq(struct lpfc_vport *vport, + struct hbq_dmabuf *dmabuf) +{ + struct fc_frame_header *new_hdr; + struct fc_frame_header *temp_hdr; + struct lpfc_dmabuf *d_buf, *n_buf, *h_buf; + struct hbq_dmabuf *seq_dmabuf = NULL; + + /* Use the hdr_buf to find the sequence that matches this frame */ + INIT_LIST_HEAD(&dmabuf->dbuf.list); + INIT_LIST_HEAD(&dmabuf->hbuf.list); + new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt; + list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) { + temp_hdr = (struct fc_frame_header *)h_buf->virt; + if ((temp_hdr->fh_seq_id != new_hdr->fh_seq_id) || + (temp_hdr->fh_ox_id != new_hdr->fh_ox_id) || + (memcmp(&temp_hdr->fh_s_id, &new_hdr->fh_s_id, 3))) + continue; + /* found a pending sequence that matches this frame */ + seq_dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf); + break; + } + + /* Free up all the frames from the partially assembled sequence */ + if (seq_dmabuf) { + list_for_each_entry_safe(d_buf, n_buf, + &seq_dmabuf->dbuf.list, list) { + list_del_init(&d_buf->list); + lpfc_in_buf_free(vport->phba, d_buf); + } + return true; + } + return false; +} + +/** + * lpfc_sli4_seq_abort_acc_cmpl - Accept seq abort iocb complete handler + * @phba: Pointer to HBA context object. + * @cmd_iocbq: pointer to the command iocbq structure. + * @rsp_iocbq: pointer to the response iocbq structure. + * + * This function handles the sequence abort accept iocb command complete + * event. It properly releases the memory allocated to the sequence abort + * accept iocb. + **/ +static void +lpfc_sli4_seq_abort_acc_cmpl(struct lpfc_hba *phba, + struct lpfc_iocbq *cmd_iocbq, + struct lpfc_iocbq *rsp_iocbq) +{ + if (cmd_iocbq) + lpfc_sli_release_iocbq(phba, cmd_iocbq); +} + +/** + * lpfc_sli4_seq_abort_acc - Accept sequence abort + * @phba: Pointer to HBA context object. + * @fc_hdr: pointer to a FC frame header. + * + * This function sends a basic accept to a previous unsol sequence abort + * event after aborting the sequence handling. + **/ +static void +lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba, + struct fc_frame_header *fc_hdr) +{ + struct lpfc_iocbq *ctiocb = NULL; + struct lpfc_nodelist *ndlp; + uint16_t oxid, rxid; + uint32_t sid, fctl; + IOCB_t *icmd; + + if (!lpfc_is_link_up(phba)) + return; + + sid = sli4_sid_from_fc_hdr(fc_hdr); + oxid = be16_to_cpu(fc_hdr->fh_ox_id); + rxid = be16_to_cpu(fc_hdr->fh_rx_id); + + ndlp = lpfc_findnode_did(phba->pport, sid); + if (!ndlp) { + lpfc_printf_log(phba, KERN_WARNING, LOG_ELS, + "1268 Find ndlp returned NULL for oxid:x%x " + "SID:x%x\n", oxid, sid); + return; + } + + /* Allocate buffer for acc iocb */ + ctiocb = lpfc_sli_get_iocbq(phba); + if (!ctiocb) + return; + + /* Extract the F_CTL field from FC_HDR */ + fctl = sli4_fctl_from_fc_hdr(fc_hdr); + + icmd = &ctiocb->iocb; + icmd->un.xseq64.bdl.bdeSize = 0; + icmd->un.xseq64.bdl.ulpIoTag32 = 0; + icmd->un.xseq64.w5.hcsw.Dfctl = 0; + icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_ACC; + icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_BLS; + + /* Fill in the rest of iocb fields */ + icmd->ulpCommand = CMD_XMIT_BLS_RSP64_CX; + icmd->ulpBdeCount = 0; + icmd->ulpLe = 1; + icmd->ulpClass = CLASS3; + icmd->ulpContext = ndlp->nlp_rpi; + + ctiocb->iocb_cmpl = NULL; + ctiocb->vport = phba->pport; + ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_acc_cmpl; + + if (fctl & FC_FC_EX_CTX) { + /* ABTS sent by responder to CT exchange, construction + * of BA_ACC will use OX_ID from ABTS for the XRI_TAG + * field and RX_ID from ABTS for RX_ID field. + */ + bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_RSP); + bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, rxid); + ctiocb->sli4_xritag = oxid; + } else { + /* ABTS sent by initiator to CT exchange, construction + * of BA_ACC will need to allocate a new XRI as for the + * XRI_TAG and RX_ID fields. + */ + bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_INT); + bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, NO_XRI); + ctiocb->sli4_xritag = NO_XRI; + } + bf_set(lpfc_abts_oxid, &icmd->un.bls_acc, oxid); + + /* Xmit CT abts accept on exchange <xid> */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "1200 Xmit CT ABTS ACC on exchange x%x Data: x%x\n", + CMD_XMIT_BLS_RSP64_CX, phba->link_state); + lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0); +} + +/** + * lpfc_sli4_handle_unsol_abort - Handle sli-4 unsolicited abort event + * @vport: Pointer to the vport on which this sequence was received + * @dmabuf: pointer to a dmabuf that describes the FC sequence + * + * This function handles an SLI-4 unsolicited abort event. If the unsolicited + * receive sequence is only partially assembed by the driver, it shall abort + * the partially assembled frames for the sequence. Otherwise, if the + * unsolicited receive sequence has been completely assembled and passed to + * the Upper Layer Protocol (UPL), it then mark the per oxid status for the + * unsolicited sequence has been aborted. After that, it will issue a basic + * accept to accept the abort. + **/ +void +lpfc_sli4_handle_unsol_abort(struct lpfc_vport *vport, + struct hbq_dmabuf *dmabuf) +{ + struct lpfc_hba *phba = vport->phba; + struct fc_frame_header fc_hdr; + uint32_t fctl; + bool abts_par; + + /* Make a copy of fc_hdr before the dmabuf being released */ + memcpy(&fc_hdr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header)); + fctl = sli4_fctl_from_fc_hdr(&fc_hdr); + + if (fctl & FC_FC_EX_CTX) { + /* + * ABTS sent by responder to exchange, just free the buffer + */ + lpfc_in_buf_free(phba, &dmabuf->dbuf); + } else { + /* + * ABTS sent by initiator to exchange, need to do cleanup + */ + /* Try to abort partially assembled seq */ + abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf); + + /* Send abort to ULP if partially seq abort failed */ + if (abts_par == false) + lpfc_sli4_send_seq_to_ulp(vport, dmabuf); + else + lpfc_in_buf_free(phba, &dmabuf->dbuf); + } + /* Send basic accept (BA_ACC) to the abort requester */ + lpfc_sli4_seq_abort_acc(phba, &fc_hdr); +} + +/** * lpfc_seq_complete - Indicates if a sequence is complete * @dmabuf: pointer to a dmabuf that describes the FC sequence * @@ -10935,10 +11246,9 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt; /* remove from receive buffer list */ list_del_init(&seq_dmabuf->hbuf.list); + lpfc_update_rcv_time_stamp(vport); /* get the Remote Port's SID */ - sid = (fc_hdr->fh_s_id[0] << 16 | - fc_hdr->fh_s_id[1] << 8 | - fc_hdr->fh_s_id[2]); + sid = sli4_sid_from_fc_hdr(fc_hdr); /* Get an iocbq struct to fill in. */ first_iocbq = lpfc_sli_get_iocbq(vport->phba); if (first_iocbq) { @@ -10957,7 +11267,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) LPFC_DATA_BUF_SIZE; first_iocbq->iocb.un.rcvels.remoteID = sid; first_iocbq->iocb.unsli3.rcvsli3.acc_len += - bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe); + bf_get(lpfc_rcqe_length, + &seq_dmabuf->cq_event.cqe.rcqe_cmpl); } iocbq = first_iocbq; /* @@ -10975,7 +11286,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) iocbq->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize = LPFC_DATA_BUF_SIZE; first_iocbq->iocb.unsli3.rcvsli3.acc_len += - bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe); + bf_get(lpfc_rcqe_length, + &seq_dmabuf->cq_event.cqe.rcqe_cmpl); } else { iocbq = lpfc_sli_get_iocbq(vport->phba); if (!iocbq) { @@ -10994,7 +11306,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) iocbq->iocb.un.cont64[0].tus.f.bdeSize = LPFC_DATA_BUF_SIZE; first_iocbq->iocb.unsli3.rcvsli3.acc_len += - bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe); + bf_get(lpfc_rcqe_length, + &seq_dmabuf->cq_event.cqe.rcqe_cmpl); iocbq->iocb.un.rcvels.remoteID = sid; list_add_tail(&iocbq->list, &first_iocbq->list); } @@ -11002,6 +11315,43 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) return first_iocbq; } +static void +lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport, + struct hbq_dmabuf *seq_dmabuf) +{ + struct fc_frame_header *fc_hdr; + struct lpfc_iocbq *iocbq, *curr_iocb, *next_iocb; + struct lpfc_hba *phba = vport->phba; + + fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt; + iocbq = lpfc_prep_seq(vport, seq_dmabuf); + if (!iocbq) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2707 Ring %d handler: Failed to allocate " + "iocb Rctl x%x Type x%x received\n", + LPFC_ELS_RING, + fc_hdr->fh_r_ctl, fc_hdr->fh_type); + return; + } + if (!lpfc_complete_unsol_iocb(phba, + &phba->sli.ring[LPFC_ELS_RING], + iocbq, fc_hdr->fh_r_ctl, + fc_hdr->fh_type)) + lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, + "2540 Ring %d handler: unexpected Rctl " + "x%x Type x%x received\n", + LPFC_ELS_RING, + fc_hdr->fh_r_ctl, fc_hdr->fh_type); + + /* Free iocb created in lpfc_prep_seq */ + list_for_each_entry_safe(curr_iocb, next_iocb, + &iocbq->list, list) { + list_del_init(&curr_iocb->list); + lpfc_sli_release_iocbq(phba, curr_iocb); + } + lpfc_sli_release_iocbq(phba, iocbq); +} + /** * lpfc_sli4_handle_received_buffer - Handle received buffers from firmware * @phba: Pointer to HBA context object. @@ -11014,67 +11364,54 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) * Worker thread calls lpfc_sli4_handle_received_buffer, which will call the * appropriate receive function when the final frame in a sequence is received. **/ -int -lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba) +void +lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba, + struct hbq_dmabuf *dmabuf) { - LIST_HEAD(cmplq); - struct hbq_dmabuf *dmabuf, *seq_dmabuf; + struct hbq_dmabuf *seq_dmabuf; struct fc_frame_header *fc_hdr; struct lpfc_vport *vport; uint32_t fcfi; - struct lpfc_iocbq *iocbq; - - /* Clear hba flag and get all received buffers into the cmplq */ - spin_lock_irq(&phba->hbalock); - phba->hba_flag &= ~HBA_RECEIVE_BUFFER; - list_splice_init(&phba->rb_pend_list, &cmplq); - spin_unlock_irq(&phba->hbalock); /* Process each received buffer */ - while ((dmabuf = lpfc_sli_hbqbuf_get(&cmplq)) != NULL) { - fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt; - /* check to see if this a valid type of frame */ - if (lpfc_fc_frame_check(phba, fc_hdr)) { - lpfc_in_buf_free(phba, &dmabuf->dbuf); - continue; - } - fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->rcqe); - vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi); - if (!vport) { - /* throw out the frame */ - lpfc_in_buf_free(phba, &dmabuf->dbuf); - continue; - } - /* Link this frame */ - seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf); - if (!seq_dmabuf) { - /* unable to add frame to vport - throw it out */ - lpfc_in_buf_free(phba, &dmabuf->dbuf); - continue; - } - /* If not last frame in sequence continue processing frames. */ - if (!lpfc_seq_complete(seq_dmabuf)) { - /* - * When saving off frames post a new one and mark this - * frame to be freed when it is finished. - **/ - lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1); - dmabuf->tag = -1; - continue; - } - fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt; - iocbq = lpfc_prep_seq(vport, seq_dmabuf); - if (!lpfc_complete_unsol_iocb(phba, - &phba->sli.ring[LPFC_ELS_RING], - iocbq, fc_hdr->fh_r_ctl, - fc_hdr->fh_type)) - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "2540 Ring %d handler: unexpected Rctl " - "x%x Type x%x received\n", - LPFC_ELS_RING, - fc_hdr->fh_r_ctl, fc_hdr->fh_type); - }; - return 0; + fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt; + /* check to see if this a valid type of frame */ + if (lpfc_fc_frame_check(phba, fc_hdr)) { + lpfc_in_buf_free(phba, &dmabuf->dbuf); + return; + } + fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl); + vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi); + if (!vport || !(vport->vpi_state & LPFC_VPI_REGISTERED)) { + /* throw out the frame */ + lpfc_in_buf_free(phba, &dmabuf->dbuf); + return; + } + /* Handle the basic abort sequence (BA_ABTS) event */ + if (fc_hdr->fh_r_ctl == FC_RCTL_BA_ABTS) { + lpfc_sli4_handle_unsol_abort(vport, dmabuf); + return; + } + + /* Link this frame */ + seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf); + if (!seq_dmabuf) { + /* unable to add frame to vport - throw it out */ + lpfc_in_buf_free(phba, &dmabuf->dbuf); + return; + } + /* If not last frame in sequence continue processing frames. */ + if (!lpfc_seq_complete(seq_dmabuf)) { + /* + * When saving off frames post a new one and mark this + * frame to be freed when it is finished. + **/ + lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1); + dmabuf->tag = -1; + return; + } + /* Send the complete sequence to the upper layer protocol */ + lpfc_sli4_send_seq_to_ulp(vport, seq_dmabuf); } /** @@ -11091,7 +11428,7 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba) * sequential. * * Return codes - * 0 - sucessful + * 0 - successful * EIO - The mailbox failed to complete successfully. * When this error occurs, the driver is not guaranteed * to have any rpi regions posted to the device and @@ -11129,7 +11466,7 @@ lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *phba) * maps up to 64 rpi context regions. * * Return codes - * 0 - sucessful + * 0 - successful * ENOMEM - No available memory * EIO - The mailbox failed to complete successfully. **/ @@ -11191,7 +11528,7 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page) * PAGE_SIZE modulo 64 rpi context headers. * * Returns - * A nonzero rpi defined as rpi_base <= rpi < max_rpi if sucessful + * A nonzero rpi defined as rpi_base <= rpi < max_rpi if successful * LPFC_RPI_ALLOC_ERROR if no rpis are available. **/ int @@ -11334,6 +11671,7 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi) { LPFC_MBOXQ_t *mboxq; int rc = 0; + int retval = MBX_SUCCESS; uint32_t mbox_tmo; if (vpi == 0) @@ -11344,16 +11682,17 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi) lpfc_init_vpi(phba, mboxq, vpi); mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_INIT_VPI); rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo); - if (rc != MBX_TIMEOUT) - mempool_free(mboxq, phba->mbox_mem_pool); if (rc != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "2022 INIT VPI Mailbox failed " "status %d, mbxStatus x%x\n", rc, bf_get(lpfc_mqe_status, &mboxq->u.mqe)); - rc = -EIO; + retval = -EIO; } - return rc; + if (rc != MBX_TIMEOUT) + mempool_free(mboxq, phba->mbox_mem_pool); + + return retval; } /** @@ -11438,13 +11777,6 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record) */ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge); phys_addr = getPaddr(sge.pa_hi, sge.pa_lo); - if (unlikely(!mboxq->sge_array)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, - "2526 Failed to get the non-embedded SGE " - "virtual address\n"); - lpfc_sli4_mbox_cmd_free(phba, mboxq); - return -ENOMEM; - } virt_addr = mboxq->sge_array->addr[0]; /* * Configure the FCF record for FCFI 0. This is the driver's @@ -11542,7 +11874,8 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2000 Failed to allocate mbox for " "READ_FCF cmd\n"); - return -ENOMEM; + error = -ENOMEM; + goto fail_fcfscan; } req_len = sizeof(struct fcf_record) + @@ -11558,8 +11891,8 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index) "0291 Allocated DMA memory size (x%x) is " "less than the requested DMA memory " "size (x%x)\n", alloc_len, req_len); - lpfc_sli4_mbox_cmd_free(phba, mboxq); - return -ENOMEM; + error = -ENOMEM; + goto fail_fcfscan; } /* Get the first SGE entry from the non-embedded DMA memory. This @@ -11567,13 +11900,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index) */ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge); phys_addr = getPaddr(sge.pa_hi, sge.pa_lo); - if (unlikely(!mboxq->sge_array)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, - "2527 Failed to get the non-embedded SGE " - "virtual address\n"); - lpfc_sli4_mbox_cmd_free(phba, mboxq); - return -ENOMEM; - } virt_addr = mboxq->sge_array->addr[0]; read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr; @@ -11586,7 +11912,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index) mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record; rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT); if (rc == MBX_NOT_FINISHED) { - lpfc_sli4_mbox_cmd_free(phba, mboxq); error = -EIO; } else { spin_lock_irq(&phba->hbalock); @@ -11594,6 +11919,15 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index) spin_unlock_irq(&phba->hbalock); error = 0; } +fail_fcfscan: + if (error) { + if (mboxq) + lpfc_sli4_mbox_cmd_free(phba, mboxq); + /* FCF scan failed, clear FCF_DISC_INPROGRESS flag */ + spin_lock_irq(&phba->hbalock); + phba->hba_flag &= ~FCF_DISC_INPROGRESS; + spin_unlock_irq(&phba->hbalock); + } return error; } diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 3c53316cf6d0..ba38de3c28f1 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -29,14 +29,17 @@ typedef enum _lpfc_ctx_cmd { LPFC_CTX_HOST } lpfc_ctx_cmd; -/* This structure is used to carry the needed response IOCB states */ -struct lpfc_sli4_rspiocb_info { - uint8_t hw_status; - uint8_t bfield; -#define LPFC_XB 0x1 -#define LPFC_PV 0x2 - uint8_t priority; - uint8_t reserved; +struct lpfc_cq_event { + struct list_head list; + union { + struct lpfc_mcqe mcqe_cmpl; + struct lpfc_acqe_link acqe_link; + struct lpfc_acqe_fcoe acqe_fcoe; + struct lpfc_acqe_dcbx acqe_dcbx; + struct lpfc_rcqe rcqe_cmpl; + struct sli4_wcqe_xri_aborted wcqe_axri; + struct lpfc_wcqe_complete wcqe_cmpl; + } cqe; }; /* This structure is used to handle IOCB requests / responses */ @@ -46,6 +49,7 @@ struct lpfc_iocbq { struct list_head clist; uint16_t iotag; /* pre-assigned IO tag */ uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */ + struct lpfc_cq_event cq_event; IOCB_t iocb; /* IOCB cmd */ uint8_t retry; /* retry counter for IOCB cmd - if needed */ @@ -56,11 +60,13 @@ struct lpfc_iocbq { #define LPFC_DRIVER_ABORTED 8 /* driver aborted this request */ #define LPFC_IO_FABRIC 0x10 /* Iocb send using fabric scheduler */ #define LPFC_DELAY_MEM_FREE 0x20 /* Defer free'ing of FC data */ -#define LPFC_FIP_ELS 0x40 +#define LPFC_FIP_ELS_ID_MASK 0xc0 /* ELS_ID range 0-3 */ +#define LPFC_FIP_ELS_ID_SHIFT 6 uint8_t abort_count; uint8_t rsvd2; uint32_t drvrTimeout; /* driver timeout in seconds */ + uint32_t fcp_wqidx; /* index to FCP work queue */ struct lpfc_vport *vport;/* virtual port pointer */ void *context1; /* caller context information */ void *context2; /* caller context information */ @@ -76,7 +82,6 @@ struct lpfc_iocbq { struct lpfc_iocbq *); void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *); - struct lpfc_sli4_rspiocb_info sli4_info; }; #define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */ @@ -110,7 +115,7 @@ typedef struct lpfcMboxq { return */ #define MBX_NOWAIT 2 /* issue command then return immediately */ -#define LPFC_MAX_RING_MASK 4 /* max num of rctl/type masks allowed per +#define LPFC_MAX_RING_MASK 5 /* max num of rctl/type masks allowed per ring */ #define LPFC_MAX_RING 4 /* max num of SLI rings used by driver */ diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index b5f4ba1a5c27..25d66d070cf8 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -58,6 +58,16 @@ #define LPFC_FCOE_FKA_ADV_PER 0 #define LPFC_FCOE_FIP_PRIORITY 0x80 +#define sli4_sid_from_fc_hdr(fc_hdr) \ + ((fc_hdr)->fh_s_id[0] << 16 | \ + (fc_hdr)->fh_s_id[1] << 8 | \ + (fc_hdr)->fh_s_id[2]) + +#define sli4_fctl_from_fc_hdr(fc_hdr) \ + ((fc_hdr)->fh_f_ctl[0] << 16 | \ + (fc_hdr)->fh_f_ctl[1] << 8 | \ + (fc_hdr)->fh_f_ctl[2]) + enum lpfc_sli4_queue_type { LPFC_EQ, LPFC_GCQ, @@ -110,18 +120,6 @@ struct lpfc_queue { union sli4_qe qe[1]; /* array to index entries (must be last) */ }; -struct lpfc_cq_event { - struct list_head list; - union { - struct lpfc_mcqe mcqe_cmpl; - struct lpfc_acqe_link acqe_link; - struct lpfc_acqe_fcoe acqe_fcoe; - struct lpfc_acqe_dcbx acqe_dcbx; - struct lpfc_rcqe rcqe_cmpl; - struct sli4_wcqe_xri_aborted wcqe_axri; - } cqe; -}; - struct lpfc_sli4_link { uint8_t speed; uint8_t duplex; @@ -166,7 +164,7 @@ struct lpfc_fip_param_hdr { #define lpfc_fip_param_hdr_fipp_mode_SHIFT 6 #define lpfc_fip_param_hdr_fipp_mode_MASK 0x3 #define lpfc_fip_param_hdr_fipp_mode_WORD parm_flags -#define FIPP_MODE_ON 0x2 +#define FIPP_MODE_ON 0x1 #define FIPP_MODE_OFF 0x0 #define FIPP_VLAN_VALID 0x1 }; @@ -295,9 +293,8 @@ struct lpfc_sli4_hba { /* BAR0 PCI config space register memory map */ void __iomem *UERRLOregaddr; /* Address to UERR_STATUS_LO register */ void __iomem *UERRHIregaddr; /* Address to UERR_STATUS_HI register */ - void __iomem *ONLINE0regaddr; /* Address to components of internal UE */ - void __iomem *ONLINE1regaddr; /* Address to components of internal UE */ -#define LPFC_ONLINE_NERR 0xFFFFFFFF + void __iomem *UEMASKLOregaddr; /* Address to UE_MASK_LO register */ + void __iomem *UEMASKHIregaddr; /* Address to UE_MASK_HI register */ void __iomem *SCRATCHPADregaddr; /* Address to scratchpad register */ /* BAR1 FCoE function CSR register memory map */ void __iomem *STAregaddr; /* Address to HST_STATE register */ @@ -311,6 +308,8 @@ struct lpfc_sli4_hba { void __iomem *MQDBregaddr; /* Address to MQ_DOORBELL register */ void __iomem *BMBXregaddr; /* Address to BootStrap MBX register */ + uint32_t ue_mask_lo; + uint32_t ue_mask_hi; struct msix_entry *msix_entries; uint32_t cfg_eqn; struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */ @@ -325,7 +324,6 @@ struct lpfc_sli4_hba { struct lpfc_queue **fcp_cq;/* Fast-path FCP compl queue */ struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */ struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */ - struct lpfc_queue *rxq_cq; /* Slow-path unsolicited complete queue */ /* Setup information for various queue parameters */ int eq_esize; @@ -360,7 +358,7 @@ struct lpfc_sli4_hba { unsigned long *rpi_bmask; uint16_t rpi_count; struct lpfc_sli4_flags sli4_flags; - struct list_head sp_rspiocb_work_queue; + struct list_head sp_queue_event; struct list_head sp_cqe_event_pool; struct list_head sp_asynce_work_queue; struct list_head sp_fcp_xri_aborted_work_queue; diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 9ae20af4bdb7..c7f3aed2aab8 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,8 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.3.4" - +#define LPFC_DRIVER_VERSION "8.3.6" #define LPFC_DRIVER_NAME "lpfc" #define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp" #define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp" diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index 606efa767548..7d6dd83d3592 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -389,7 +389,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable) * by the port. */ if ((phba->sli_rev == LPFC_SLI_REV4) && - (pport->vfi_state & LPFC_VFI_REGISTERED)) { + (pport->vpi_state & LPFC_VPI_REGISTERED)) { rc = lpfc_sli4_init_vpi(phba, vpi); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_VPORT, @@ -700,6 +700,8 @@ lpfc_vport_delete(struct fc_vport *fc_vport) } spin_unlock_irq(&phba->ndlp_lock); } + if (vport->vpi_state != LPFC_VPI_REGISTERED) + goto skip_logo; vport->unreg_vpi_cmpl = VPORT_INVAL; timeout = msecs_to_jiffies(phba->fc_ratov * 2000); if (!lpfc_issue_els_npiv_logo(vport, ndlp)) diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h index 512c2cc1a33f..d310f49d077e 100644 --- a/drivers/scsi/megaraid.h +++ b/drivers/scsi/megaraid.h @@ -381,7 +381,7 @@ typedef struct { u8 battery_status; /* * BIT 0: battery module missing * BIT 1: VBAD - * BIT 2: temprature high + * BIT 2: temperature high * BIT 3: battery pack missing * BIT 4,5: * 00 - charge complete diff --git a/drivers/scsi/megaraid/mbox_defs.h b/drivers/scsi/megaraid/mbox_defs.h index b25b74764ec3..ce2487a888ed 100644 --- a/drivers/scsi/megaraid/mbox_defs.h +++ b/drivers/scsi/megaraid/mbox_defs.h @@ -497,7 +497,7 @@ typedef struct { * @inserted_drive : channel:Id of inserted drive * @battery_status : bit 0: battery module missing * bit 1: VBAD - * bit 2: temprature high + * bit 2: temperature high * bit 3: battery pack missing * bit 4,5: * 00 - charge complete diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index 234f0b7eb21c..7f977967b884 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -335,12 +335,17 @@ static struct device_attribute *megaraid_sdev_attrs[] = { * megaraid_change_queue_depth - Change the device's queue depth * @sdev: scsi device struct * @qdepth: depth to set + * @reason: calling context * * Return value: * actual depth set */ -static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth) +static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (qdepth > MBOX_MAX_SCSI_CMDS) qdepth = MBOX_MAX_SCSI_CMDS; scsi_adjust_queue_depth(sdev, 0, qdepth); @@ -2704,7 +2709,7 @@ megaraid_reset_handler(struct scsi_cmnd *scp) } else { con_log(CL_ANN, (KERN_NOTICE - "megaraid mbox: reset sequence completed sucessfully\n")); + "megaraid mbox: reset sequence completed successfully\n")); } diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index a39addc3a596..134c63ef6d38 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -10,7 +10,7 @@ * 2 of the License, or (at your option) any later version. * * FILE : megaraid_sas.c - * Version : v00.00.04.01-rc1 + * Version : v00.00.04.12-rc1 * * Authors: * (email-id : megaraidlinux@lsi.com) @@ -40,6 +40,7 @@ #include <linux/compat.h> #include <linux/blkdev.h> #include <linux/mutex.h> +#include <linux/poll.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -75,6 +76,10 @@ static struct pci_device_id megasas_pci_table[] = { /* gen2*/ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0079GEN2)}, /* gen2*/ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0073SKINNY)}, + /* skinny*/ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0071SKINNY)}, + /* skinny*/ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)}, /* xscale IOP, vega */ {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)}, @@ -89,8 +94,14 @@ static struct megasas_mgmt_info megasas_mgmt_info; static struct fasync_struct *megasas_async_queue; static DEFINE_MUTEX(megasas_async_queue_mutex); +static int megasas_poll_wait_aen; +static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait); +static u32 support_poll_for_event; static u32 megasas_dbg_lvl; +/* define lock for aen poll */ +spinlock_t poll_aen_lock; + static void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status); @@ -215,7 +226,10 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs) * @regs : MFI register set */ static inline void -megasas_fire_cmd_xscale(dma_addr_t frame_phys_addr,u32 frame_count, struct megasas_register_set __iomem *regs) +megasas_fire_cmd_xscale(struct megasas_instance *instance, + dma_addr_t frame_phys_addr, + u32 frame_count, + struct megasas_register_set __iomem *regs) { writel((frame_phys_addr >> 3)|(frame_count), &(regs)->inbound_queue_port); @@ -312,7 +326,10 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs) * @regs : MFI register set */ static inline void -megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) +megasas_fire_cmd_ppc(struct megasas_instance *instance, + dma_addr_t frame_phys_addr, + u32 frame_count, + struct megasas_register_set __iomem *regs) { writel((frame_phys_addr | (frame_count<<1))|1, &(regs)->inbound_queue_port); @@ -328,6 +345,104 @@ static struct megasas_instance_template megasas_instance_template_ppc = { }; /** + * megasas_enable_intr_skinny - Enables interrupts + * @regs: MFI register set + */ +static inline void +megasas_enable_intr_skinny(struct megasas_register_set __iomem *regs) +{ + writel(0xFFFFFFFF, &(regs)->outbound_intr_mask); + + writel(~MFI_SKINNY_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask); + + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** + * megasas_disable_intr_skinny - Disables interrupt + * @regs: MFI register set + */ +static inline void +megasas_disable_intr_skinny(struct megasas_register_set __iomem *regs) +{ + u32 mask = 0xFFFFFFFF; + writel(mask, ®s->outbound_intr_mask); + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** + * megasas_read_fw_status_reg_skinny - returns the current FW status value + * @regs: MFI register set + */ +static u32 +megasas_read_fw_status_reg_skinny(struct megasas_register_set __iomem *regs) +{ + return readl(&(regs)->outbound_scratch_pad); +} + +/** + * megasas_clear_interrupt_skinny - Check & clear interrupt + * @regs: MFI register set + */ +static int +megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs) +{ + u32 status; + /* + * Check if it is our interrupt + */ + status = readl(®s->outbound_intr_status); + + if (!(status & MFI_SKINNY_ENABLE_INTERRUPT_MASK)) { + return 1; + } + + /* + * Clear the interrupt by writing back the same value + */ + writel(status, ®s->outbound_intr_status); + + /* + * dummy read to flush PCI + */ + readl(®s->outbound_intr_status); + + return 0; +} + +/** + * megasas_fire_cmd_skinny - Sends command to the FW + * @frame_phys_addr : Physical address of cmd + * @frame_count : Number of frames for the command + * @regs : MFI register set + */ +static inline void +megasas_fire_cmd_skinny(struct megasas_instance *instance, + dma_addr_t frame_phys_addr, + u32 frame_count, + struct megasas_register_set __iomem *regs) +{ + unsigned long flags; + spin_lock_irqsave(&instance->fire_lock, flags); + writel(0, &(regs)->inbound_high_queue_port); + writel((frame_phys_addr | (frame_count<<1))|1, + &(regs)->inbound_low_queue_port); + spin_unlock_irqrestore(&instance->fire_lock, flags); +} + +static struct megasas_instance_template megasas_instance_template_skinny = { + + .fire_cmd = megasas_fire_cmd_skinny, + .enable_intr = megasas_enable_intr_skinny, + .disable_intr = megasas_disable_intr_skinny, + .clear_intr = megasas_clear_intr_skinny, + .read_fw_status_reg = megasas_read_fw_status_reg_skinny, +}; + + +/** * The following functions are defined for gen2 (deviceid : 0x78 0x79) * controllers */ @@ -404,7 +519,9 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs) * @regs : MFI register set */ static inline void -megasas_fire_cmd_gen2(dma_addr_t frame_phys_addr, u32 frame_count, +megasas_fire_cmd_gen2(struct megasas_instance *instance, + dma_addr_t frame_phys_addr, + u32 frame_count, struct megasas_register_set __iomem *regs) { writel((frame_phys_addr | (frame_count<<1))|1, @@ -446,7 +563,8 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) /* * Issue the frame using inbound queue port */ - instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); + instance->instancet->fire_cmd(instance, + cmd->frame_phys_addr, 0, instance->reg_set); /* * Wait for cmd_status to change @@ -477,7 +595,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, { cmd->cmd_status = ENODATA; - instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); + instance->instancet->fire_cmd(instance, + cmd->frame_phys_addr, 0, instance->reg_set); wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA), MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); @@ -522,7 +641,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, cmd->sync_cmd = 1; cmd->cmd_status = 0xFF; - instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); + instance->instancet->fire_cmd(instance, + cmd->frame_phys_addr, 0, instance->reg_set); /* * Wait for this cmd to complete @@ -592,6 +712,35 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, return sge_count; } +/** + * megasas_make_sgl_skinny - Prepares IEEE SGL + * @instance: Adapter soft state + * @scp: SCSI command from the mid-layer + * @mfi_sgl: SGL to be filled in + * + * If successful, this function returns the number of SG elements. Otherwise, + * it returnes -1. + */ +static int +megasas_make_sgl_skinny(struct megasas_instance *instance, + struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl) +{ + int i; + int sge_count; + struct scatterlist *os_sgl; + + sge_count = scsi_dma_map(scp); + + if (sge_count) { + scsi_for_each_sg(scp, os_sgl, sge_count, i) { + mfi_sgl->sge_skinny[i].length = sg_dma_len(os_sgl); + mfi_sgl->sge_skinny[i].phys_addr = + sg_dma_address(os_sgl); + } + } + return sge_count; +} + /** * megasas_get_frame_count - Computes the number of frames * @frame_type : type of frame- io or pthru frame @@ -600,7 +749,8 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, * Returns the number of frames required for numnber of sge's (sge_count) */ -static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type) +static u32 megasas_get_frame_count(struct megasas_instance *instance, + u8 sge_count, u8 frame_type) { int num_cnt; int sge_bytes; @@ -610,6 +760,10 @@ static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type) sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : sizeof(struct megasas_sge32); + if (instance->flag_ieee) { + sge_sz = sizeof(struct megasas_sge_skinny); + } + /* * Main frame can contain 2 SGEs for 64-bit SGLs and * 3 SGEs for 32-bit SGLs for ldio & @@ -617,12 +771,16 @@ static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type) * 2 SGEs for 32-bit SGLs for pthru frame */ if (unlikely(frame_type == PTHRU_FRAME)) { - if (IS_DMA64) + if (instance->flag_ieee == 1) { + num_cnt = sge_count - 1; + } else if (IS_DMA64) num_cnt = sge_count - 1; else num_cnt = sge_count - 2; } else { - if (IS_DMA64) + if (instance->flag_ieee == 1) { + num_cnt = sge_count - 1; + } else if (IS_DMA64) num_cnt = sge_count - 2; else num_cnt = sge_count - 3; @@ -671,6 +829,10 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, else if (scp->sc_data_direction == PCI_DMA_NONE) flags = MFI_FRAME_DIR_NONE; + if (instance->flag_ieee == 1) { + flags |= MFI_FRAME_IEEE; + } + /* * Prepare the DCDB frame */ @@ -687,9 +849,24 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); /* + * If the command is for the tape device, set the + * pthru timeout to the os layer timeout value. + */ + if (scp->device->type == TYPE_TAPE) { + if ((scp->request->timeout / HZ) > 0xFFFF) + pthru->timeout = 0xFFFF; + else + pthru->timeout = scp->request->timeout / HZ; + } + + /* * Construct SGL */ - if (IS_DMA64) { + if (instance->flag_ieee == 1) { + pthru->flags |= MFI_FRAME_SGL64; + pthru->sge_count = megasas_make_sgl_skinny(instance, scp, + &pthru->sgl); + } else if (IS_DMA64) { pthru->flags |= MFI_FRAME_SGL64; pthru->sge_count = megasas_make_sgl64(instance, scp, &pthru->sgl); @@ -708,7 +885,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, * Compute the total number of frames this command consumes. FW uses * this number to pull sufficient number of frames from host memory. */ - cmd->frame_count = megasas_get_frame_count(pthru->sge_count, + cmd->frame_count = megasas_get_frame_count(instance, pthru->sge_count, PTHRU_FRAME); return cmd->frame_count; @@ -739,6 +916,10 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) flags = MFI_FRAME_DIR_READ; + if (instance->flag_ieee == 1) { + flags |= MFI_FRAME_IEEE; + } + /* * Prepare the Logical IO frame: 2nd bit is zero for all read cmds */ @@ -809,7 +990,11 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, /* * Construct SGL */ - if (IS_DMA64) { + if (instance->flag_ieee) { + ldio->flags |= MFI_FRAME_SGL64; + ldio->sge_count = megasas_make_sgl_skinny(instance, scp, + &ldio->sgl); + } else if (IS_DMA64) { ldio->flags |= MFI_FRAME_SGL64; ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl); } else @@ -826,7 +1011,8 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, * Compute the total number of frames this command consumes. FW uses * this number to pull sufficient number of frames from host memory. */ - cmd->frame_count = megasas_get_frame_count(ldio->sge_count, IO_FRAME); + cmd->frame_count = megasas_get_frame_count(instance, + ldio->sge_count, IO_FRAME); return cmd->frame_count; } @@ -983,7 +1169,8 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)) */ atomic_inc(&instance->fw_outstanding); - instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set); + instance->instancet->fire_cmd(instance, cmd->frame_phys_addr, + cmd->frame_count-1, instance->reg_set); /* * Check if we have pend cmds to be completed */ @@ -1000,24 +1187,76 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)) return 0; } +static struct megasas_instance *megasas_lookup_instance(u16 host_no) +{ + int i; + + for (i = 0; i < megasas_mgmt_info.max_index; i++) { + + if ((megasas_mgmt_info.instance[i]) && + (megasas_mgmt_info.instance[i]->host->host_no == host_no)) + return megasas_mgmt_info.instance[i]; + } + + return NULL; +} + static int megasas_slave_configure(struct scsi_device *sdev) { + u16 pd_index = 0; + struct megasas_instance *instance ; + + instance = megasas_lookup_instance(sdev->host->host_no); + /* - * Don't export physical disk devices to the disk driver. - * - * FIXME: Currently we don't export them to the midlayer at all. - * That will be fixed once LSI engineers have audited the - * firmware for possible issues. - */ - if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK) + * Don't export physical disk devices to the disk driver. + * + * FIXME: Currently we don't export them to the midlayer at all. + * That will be fixed once LSI engineers have audited the + * firmware for possible issues. + */ + if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && + sdev->type == TYPE_DISK) { + pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + + sdev->id; + if (instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM) { + blk_queue_rq_timeout(sdev->request_queue, + MEGASAS_DEFAULT_CMD_TIMEOUT * HZ); + return 0; + } return -ENXIO; + } /* - * The RAID firmware may require extended timeouts. - */ - if (sdev->channel >= MEGASAS_MAX_PD_CHANNELS) - blk_queue_rq_timeout(sdev->request_queue, - MEGASAS_DEFAULT_CMD_TIMEOUT * HZ); + * The RAID firmware may require extended timeouts. + */ + blk_queue_rq_timeout(sdev->request_queue, + MEGASAS_DEFAULT_CMD_TIMEOUT * HZ); + return 0; +} + +static int megasas_slave_alloc(struct scsi_device *sdev) +{ + u16 pd_index = 0; + struct megasas_instance *instance ; + instance = megasas_lookup_instance(sdev->host->host_no); + if ((sdev->channel < MEGASAS_MAX_PD_CHANNELS) && + (sdev->type == TYPE_DISK)) { + /* + * Open the OS scan to the SYSTEM PD + */ + pd_index = + (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + + sdev->id; + if ((instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM) && + (instance->pd_list[pd_index].driveType == + TYPE_DISK)) { + return 0; + } + return -ENXIO; + } return 0; } @@ -1072,7 +1311,14 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr) spin_lock_irqsave(instance->host->host_lock, flags); instance->flag &= ~MEGASAS_FW_BUSY; - instance->host->can_queue = + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + instance->host->can_queue = + instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS; + } else + instance->host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS; spin_unlock_irqrestore(instance->host->host_lock, flags); @@ -1117,8 +1363,16 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) * Send signal to FW to stop processing any pending cmds. * The controller will be taken offline by the OS now. */ - writel(MFI_STOP_ADP, + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + writel(MFI_STOP_ADP, + &instance->reg_set->reserved_0[0]); + } else { + writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell); + } megasas_dump_pending_frames(instance); instance->hw_crit_error = 1; return FAILED; @@ -1266,6 +1520,8 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, return 0; } +static void megasas_aen_polling(struct work_struct *work); + /** * megasas_service_aen - Processes an event notification * @instance: Adapter soft state @@ -1281,16 +1537,36 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, static void megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) { + unsigned long flags; /* * Don't signal app if it is just an aborted previously registered aen */ - if (!cmd->abort_aen) + if ((!cmd->abort_aen) && (instance->unload == 0)) { + spin_lock_irqsave(&poll_aen_lock, flags); + megasas_poll_wait_aen = 1; + spin_unlock_irqrestore(&poll_aen_lock, flags); + wake_up(&megasas_poll_wait); kill_fasync(&megasas_async_queue, SIGIO, POLL_IN); + } else cmd->abort_aen = 0; instance->aen_cmd = NULL; megasas_return_cmd(instance, cmd); + + if (instance->unload == 0) { + struct megasas_aen_event *ev; + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) { + printk(KERN_ERR "megasas_service_aen: out of memory\n"); + } else { + ev->instance = instance; + instance->ev = ev; + INIT_WORK(&ev->hotplug_work, megasas_aen_polling); + schedule_delayed_work( + (struct delayed_work *)&ev->hotplug_work, 0); + } + } } /* @@ -1302,6 +1578,7 @@ static struct scsi_host_template megasas_template = { .name = "LSI SAS based MegaRAID driver", .proc_name = "megaraid_sas", .slave_configure = megasas_slave_configure, + .slave_alloc = megasas_slave_alloc, .queuecommand = megasas_queue_command, .eh_device_reset_handler = megasas_reset_device, .eh_bus_reset_handler = megasas_reset_bus_host, @@ -1370,6 +1647,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, { int exception = 0; struct megasas_header *hdr = &cmd->frame->hdr; + unsigned long flags; if (cmd->scmd) cmd->scmd->SCp.ptr = NULL; @@ -1459,6 +1737,12 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, case MFI_CMD_SMP: case MFI_CMD_STP: case MFI_CMD_DCMD: + if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET_INFO || + cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET) { + spin_lock_irqsave(&poll_aen_lock, flags); + megasas_poll_wait_aen = 0; + spin_unlock_irqrestore(&poll_aen_lock, flags); + } /* * See if got an event notification @@ -1536,6 +1820,7 @@ megasas_transition_to_ready(struct megasas_instance* instance) u8 max_wait; u32 fw_state; u32 cur_state; + u32 abs_state, curr_abs_state; fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; @@ -1545,6 +1830,9 @@ megasas_transition_to_ready(struct megasas_instance* instance) while (fw_state != MFI_STATE_READY) { + abs_state = + instance->instancet->read_fw_status_reg(instance->reg_set); + switch (fw_state) { case MFI_STATE_FAULT: @@ -1556,18 +1844,36 @@ megasas_transition_to_ready(struct megasas_instance* instance) /* * Set the CLR bit in inbound doorbell */ - writel(MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, - &instance->reg_set->inbound_doorbell); + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + + writel( + MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, + &instance->reg_set->reserved_0[0]); + } else { + writel( + MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, + &instance->reg_set->inbound_doorbell); + } - max_wait = 2; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_WAIT_HANDSHAKE; break; case MFI_STATE_BOOT_MESSAGE_PENDING: - writel(MFI_INIT_HOTPLUG, - &instance->reg_set->inbound_doorbell); + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + writel(MFI_INIT_HOTPLUG, + &instance->reg_set->reserved_0[0]); + } else + writel(MFI_INIT_HOTPLUG, + &instance->reg_set->inbound_doorbell); - max_wait = 10; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_BOOT_MESSAGE_PENDING; break; @@ -1576,9 +1882,17 @@ megasas_transition_to_ready(struct megasas_instance* instance) * Bring it to READY state; assuming max wait 10 secs */ instance->instancet->disable_intr(instance->reg_set); - writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell); + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + writel(MFI_RESET_FLAGS, + &instance->reg_set->reserved_0[0]); + } else + writel(MFI_RESET_FLAGS, + &instance->reg_set->inbound_doorbell); - max_wait = 60; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_OPERATIONAL; break; @@ -1586,32 +1900,32 @@ megasas_transition_to_ready(struct megasas_instance* instance) /* * This state should not last for more than 2 seconds */ - max_wait = 2; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_UNDEFINED; break; case MFI_STATE_BB_INIT: - max_wait = 2; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_BB_INIT; break; case MFI_STATE_FW_INIT: - max_wait = 20; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_FW_INIT; break; case MFI_STATE_FW_INIT_2: - max_wait = 20; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_FW_INIT_2; break; case MFI_STATE_DEVICE_SCAN: - max_wait = 20; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_DEVICE_SCAN; break; case MFI_STATE_FLUSH_CACHE: - max_wait = 20; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_FLUSH_CACHE; break; @@ -1627,8 +1941,10 @@ megasas_transition_to_ready(struct megasas_instance* instance) for (i = 0; i < (max_wait * 1000); i++) { fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK ; + curr_abs_state = + instance->instancet->read_fw_status_reg(instance->reg_set); - if (fw_state == cur_state) { + if (abs_state == curr_abs_state) { msleep(1); } else break; @@ -1637,7 +1953,7 @@ megasas_transition_to_ready(struct megasas_instance* instance) /* * Return error if fw_state hasn't changed after max_wait */ - if (fw_state == cur_state) { + if (curr_abs_state == abs_state) { printk(KERN_DEBUG "FW state [%d] hasn't changed " "in %d secs\n", fw_state, max_wait); return -ENODEV; @@ -1715,6 +2031,10 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : sizeof(struct megasas_sge32); + if (instance->flag_ieee) { + sge_sz = sizeof(struct megasas_sge_skinny); + } + /* * Calculated the number of 64byte frames required for SGL */ @@ -1777,6 +2097,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) } cmd->frame->io.context = cmd->index; + cmd->frame->io.pad_0 = 0; } return 0; @@ -1882,6 +2203,97 @@ static int megasas_alloc_cmds(struct megasas_instance *instance) return 0; } +/* + * megasas_get_pd_list_info - Returns FW's pd_list structure + * @instance: Adapter soft state + * @pd_list: pd_list structure + * + * Issues an internal command (DCMD) to get the FW's controller PD + * list structure. This information is mainly used to find out SYSTEM + * supported by the FW. + */ +static int +megasas_get_pd_list(struct megasas_instance *instance) +{ + int ret = 0, pd_index = 0; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct MR_PD_LIST *ci; + struct MR_PD_ADDRESS *pd_addr; + dma_addr_t ci_h = 0; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + printk(KERN_DEBUG "megasas (get_pd_list): Failed to get cmd\n"); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + + ci = pci_alloc_consistent(instance->pdev, + MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), &ci_h); + + if (!ci) { + printk(KERN_DEBUG "Failed to alloc mem for pd_list\n"); + megasas_return_cmd(instance, cmd); + return -ENOMEM; + } + + memset(ci, 0, sizeof(*ci)); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST; + dcmd->mbox.b[1] = 0; + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0xFF; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_READ; + dcmd->timeout = 0; + dcmd->data_xfer_len = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST); + dcmd->opcode = MR_DCMD_PD_LIST_QUERY; + dcmd->sgl.sge32[0].phys_addr = ci_h; + dcmd->sgl.sge32[0].length = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST); + + if (!megasas_issue_polled(instance, cmd)) { + ret = 0; + } else { + ret = -1; + } + + /* + * the following function will get the instance PD LIST. + */ + + pd_addr = ci->addr; + + if ( ret == 0 && + (ci->count < + (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) { + + memset(instance->pd_list, 0, + MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)); + + for (pd_index = 0; pd_index < ci->count; pd_index++) { + + instance->pd_list[pd_addr->deviceId].tid = + pd_addr->deviceId; + instance->pd_list[pd_addr->deviceId].driveType = + pd_addr->scsiDevType; + instance->pd_list[pd_addr->deviceId].driveState = + MR_PD_STATE_SYSTEM; + pd_addr++; + } + } + + pci_free_consistent(instance->pdev, + MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), + ci, ci_h); + megasas_return_cmd(instance, cmd); + + return ret; +} + /** * megasas_get_controller_info - Returns FW's controller structure * @instance: Adapter soft state @@ -2081,6 +2493,8 @@ static int megasas_init_mfi(struct megasas_instance *instance) * Map the message registers */ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1078GEN2) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0079GEN2)) { instance->base_addr = pci_resource_start(instance->pdev, 1); } else { @@ -2111,6 +2525,10 @@ static int megasas_init_mfi(struct megasas_instance *instance) case PCI_DEVICE_ID_LSI_SAS0079GEN2: instance->instancet = &megasas_instance_template_gen2; break; + case PCI_DEVICE_ID_LSI_SAS0073SKINNY: + case PCI_DEVICE_ID_LSI_SAS0071SKINNY: + instance->instancet = &megasas_instance_template_skinny; + break; case PCI_DEVICE_ID_LSI_SAS1064R: case PCI_DEVICE_ID_DELL_PERC5: default: @@ -2166,6 +2584,10 @@ static int megasas_init_mfi(struct megasas_instance *instance) if (megasas_issue_init_mfi(instance)) goto fail_fw_init; + memset(instance->pd_list, 0 , + (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list))); + megasas_get_pd_list(instance); + ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL); /* @@ -2409,6 +2831,11 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num, dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h; dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail); + if (instance->aen_cmd != NULL) { + megasas_return_cmd(instance, cmd); + return 0; + } + /* * Store reference to the cmd used to register for AEN. When an * application wants us to register for AEN, we have to abort this @@ -2419,7 +2846,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num, /* * Issue the aen registration frame */ - instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); + instance->instancet->fire_cmd(instance, + cmd->frame_phys_addr, 0, instance->reg_set); return 0; } @@ -2465,7 +2893,13 @@ static int megasas_io_attach(struct megasas_instance *instance) */ host->irq = instance->pdev->irq; host->unique_id = instance->unique_id; - host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS; + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + host->can_queue = + instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS; + } else + host->can_queue = + instance->max_fw_cmds - MEGASAS_INT_CMDS; host->this_id = instance->init_id; host->sg_tablesize = instance->max_num_sge; host->max_sectors = instance->max_sectors_per_req; @@ -2572,6 +3006,9 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) *instance->producer = 0; *instance->consumer = 0; + megasas_poll_wait_aen = 0; + instance->flag_ieee = 0; + instance->ev = NULL; instance->evt_detail = pci_alloc_consistent(pdev, sizeof(struct @@ -2595,10 +3032,11 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) init_waitqueue_head(&instance->abort_cmd_wait_q); spin_lock_init(&instance->cmd_pool_lock); + spin_lock_init(&instance->fire_lock); spin_lock_init(&instance->completion_lock); + spin_lock_init(&poll_aen_lock); mutex_init(&instance->aen_mutex); - sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS); /* * Initialize PCI related and misc parameters @@ -2608,8 +3046,16 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) instance->unique_id = pdev->bus->number << 8 | pdev->devfn; instance->init_id = MEGASAS_DEFAULT_INIT_ID; + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + instance->flag_ieee = 1; + sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS); + } else + sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS); + megasas_dbg_lvl = 0; instance->flag = 0; + instance->unload = 1; instance->last_time = 0; /* @@ -2655,6 +3101,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (megasas_io_attach(instance)) goto fail_io_attach; + instance->unload = 0; return 0; fail_start_aen: @@ -2778,12 +3225,23 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) instance = pci_get_drvdata(pdev); host = instance->host; + instance->unload = 1; if (poll_mode_io) del_timer_sync(&instance->io_completion_timer); megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN); + + /* cancel the delayed work if this work still in queue */ + if (instance->ev != NULL) { + struct megasas_aen_event *ev = instance->ev; + cancel_delayed_work( + (struct delayed_work *)&ev->hotplug_work); + flush_scheduled_work(); + instance->ev = NULL; + } + tasklet_kill(&instance->isr_tasklet); pci_set_drvdata(instance->pdev, instance); @@ -2873,6 +3331,8 @@ megasas_resume(struct pci_dev *pdev) megasas_start_timer(instance, &instance->io_completion_timer, megasas_io_completion_timer, MEGASAS_COMPLETION_TIMER_INTERVAL); + instance->unload = 0; + return 0; fail_irq: @@ -2913,6 +3373,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) struct megasas_instance *instance; instance = pci_get_drvdata(pdev); + instance->unload = 1; host = instance->host; if (poll_mode_io) @@ -2921,6 +3382,16 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) scsi_remove_host(instance->host); megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); + + /* cancel the delayed work if this work still in queue*/ + if (instance->ev != NULL) { + struct megasas_aen_event *ev = instance->ev; + cancel_delayed_work( + (struct delayed_work *)&ev->hotplug_work); + flush_scheduled_work(); + instance->ev = NULL; + } + tasklet_kill(&instance->isr_tasklet); /* @@ -2969,6 +3440,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) static void megasas_shutdown(struct pci_dev *pdev) { struct megasas_instance *instance = pci_get_drvdata(pdev); + instance->unload = 1; megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); } @@ -3016,6 +3488,23 @@ static int megasas_mgmt_fasync(int fd, struct file *filep, int mode) } /** + * megasas_mgmt_poll - char node "poll" entry point + * */ +static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait) +{ + unsigned int mask; + unsigned long flags; + poll_wait(file, &megasas_poll_wait, wait); + spin_lock_irqsave(&poll_aen_lock, flags); + if (megasas_poll_wait_aen) + mask = (POLLIN | POLLRDNORM); + else + mask = 0; + spin_unlock_irqrestore(&poll_aen_lock, flags); + return mask; +} + +/** * megasas_mgmt_fw_ioctl - Issues management ioctls to FW * @instance: Adapter soft state * @argp: User's ioctl packet @@ -3032,7 +3521,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, int error = 0, i; void *sense = NULL; dma_addr_t sense_handle; - u32 *sense_ptr; + unsigned long *sense_ptr; memset(kbuff_arr, 0, sizeof(kbuff_arr)); @@ -3056,6 +3545,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, */ memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE); cmd->frame->hdr.context = cmd->index; + cmd->frame->hdr.pad_0 = 0; /* * The management interface between applications and the fw uses @@ -3109,7 +3599,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, } sense_ptr = - (u32 *) ((unsigned long)cmd->frame + ioc->sense_off); + (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off); *sense_ptr = sense_handle; } @@ -3140,8 +3630,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, * sense_ptr points to the location that has the user * sense buffer address */ - sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw + - ioc->sense_off); + sense_ptr = (unsigned long *) ((unsigned long)ioc->frame.raw + + ioc->sense_off); if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)), sense, ioc->sense_len)) { @@ -3177,20 +3667,6 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, return error; } -static struct megasas_instance *megasas_lookup_instance(u16 host_no) -{ - int i; - - for (i = 0; i < megasas_mgmt_info.max_index; i++) { - - if ((megasas_mgmt_info.instance[i]) && - (megasas_mgmt_info.instance[i]->host->host_no == host_no)) - return megasas_mgmt_info.instance[i]; - } - - return NULL; -} - static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) { struct megasas_iocpacket __user *user_ioc = @@ -3214,6 +3690,17 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) goto out_kfree_ioc; } + if (instance->hw_crit_error == 1) { + printk(KERN_DEBUG "Controller in Crit ERROR\n"); + error = -ENODEV; + goto out_kfree_ioc; + } + + if (instance->unload == 1) { + error = -ENODEV; + goto out_kfree_ioc; + } + /* * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds */ @@ -3249,6 +3736,14 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) if (!instance) return -ENODEV; + if (instance->hw_crit_error == 1) { + error = -ENODEV; + } + + if (instance->unload == 1) { + return -ENODEV; + } + mutex_lock(&instance->aen_mutex); error = megasas_register_aen(instance, aen.seq_num, aen.class_locale_word); @@ -3337,6 +3832,7 @@ static const struct file_operations megasas_mgmt_fops = { .open = megasas_mgmt_open, .fasync = megasas_mgmt_fasync, .unlocked_ioctl = megasas_mgmt_ioctl, + .poll = megasas_mgmt_poll, #ifdef CONFIG_COMPAT .compat_ioctl = megasas_mgmt_compat_ioctl, #endif @@ -3378,6 +3874,15 @@ static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date, NULL); static ssize_t +megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf) +{ + return sprintf(buf, "%u\n", support_poll_for_event); +} + +static DRIVER_ATTR(support_poll_for_event, S_IRUGO, + megasas_sysfs_show_support_poll_for_event, NULL); + +static ssize_t megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf) { return sprintf(buf, "%u\n", megasas_dbg_lvl); @@ -3451,6 +3956,92 @@ out: return retval; } +static void +megasas_aen_polling(struct work_struct *work) +{ + struct megasas_aen_event *ev = + container_of(work, struct megasas_aen_event, hotplug_work); + struct megasas_instance *instance = ev->instance; + union megasas_evt_class_locale class_locale; + struct Scsi_Host *host; + struct scsi_device *sdev1; + u16 pd_index = 0; + int i, j, doscan = 0; + u32 seq_num; + int error; + + if (!instance) { + printk(KERN_ERR "invalid instance!\n"); + kfree(ev); + return; + } + instance->ev = NULL; + host = instance->host; + if (instance->evt_detail) { + + switch (instance->evt_detail->code) { + case MR_EVT_PD_INSERTED: + case MR_EVT_PD_REMOVED: + case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED: + doscan = 1; + break; + default: + doscan = 0; + break; + } + } else { + printk(KERN_ERR "invalid evt_detail!\n"); + kfree(ev); + return; + } + + if (doscan) { + printk(KERN_INFO "scanning ...\n"); + megasas_get_pd_list(instance); + for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j; + sdev1 = scsi_device_lookup(host, i, j, 0); + if (instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM) { + if (!sdev1) { + scsi_add_device(host, i, j, 0); + } + if (sdev1) + scsi_device_put(sdev1); + } else { + if (sdev1) { + scsi_remove_device(sdev1); + scsi_device_put(sdev1); + } + } + } + } + } + + if ( instance->aen_cmd != NULL ) { + kfree(ev); + return ; + } + + seq_num = instance->evt_detail->seq_num + 1; + + /* Register AEN with FW for latest sequence number plus 1 */ + class_locale.members.reserved = 0; + class_locale.members.locale = MR_EVT_LOCALE_ALL; + class_locale.members.class = MR_EVT_CLASS_DEBUG; + mutex_lock(&instance->aen_mutex); + error = megasas_register_aen(instance, seq_num, + class_locale.word); + mutex_unlock(&instance->aen_mutex); + + if (error) + printk(KERN_ERR "register aen failed error %x\n", error); + + kfree(ev); +} + + static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUGO, megasas_sysfs_show_poll_mode_io, megasas_sysfs_set_poll_mode_io); @@ -3468,6 +4059,8 @@ static int __init megasas_init(void) printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION, MEGASAS_EXT_VERSION); + support_poll_for_event = 2; + memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info)); /* @@ -3500,6 +4093,12 @@ static int __init megasas_init(void) &driver_attr_release_date); if (rval) goto err_dcf_rel_date; + + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_support_poll_for_event); + if (rval) + goto err_dcf_support_poll_for_event; + rval = driver_create_file(&megasas_pci_driver.driver, &driver_attr_dbg_lvl); if (rval) @@ -3516,7 +4115,12 @@ err_dcf_poll_mode_io: &driver_attr_dbg_lvl); err_dcf_dbg_lvl: driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_support_poll_for_event); + +err_dcf_support_poll_for_event: + driver_remove_file(&megasas_pci_driver.driver, &driver_attr_release_date); + err_dcf_rel_date: driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); err_dcf_attr_ver: diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 0d033248fdf1..72b28e436e32 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -18,9 +18,9 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.04.01" -#define MEGASAS_RELDATE "July 24, 2008" -#define MEGASAS_EXT_VERSION "Thu July 24 11:41:51 PST 2008" +#define MEGASAS_VERSION "00.00.04.12-rc1" +#define MEGASAS_RELDATE "Sep. 17, 2009" +#define MEGASAS_EXT_VERSION "Thu Sep. 17 11:41:51 PST 2009" /* * Device IDs @@ -30,6 +30,8 @@ #define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413 #define PCI_DEVICE_ID_LSI_SAS1078GEN2 0x0078 #define PCI_DEVICE_ID_LSI_SAS0079GEN2 0x0079 +#define PCI_DEVICE_ID_LSI_SAS0073SKINNY 0x0073 +#define PCI_DEVICE_ID_LSI_SAS0071SKINNY 0x0071 /* * ===================================== @@ -94,6 +96,7 @@ #define MFI_FRAME_DIR_WRITE 0x0008 #define MFI_FRAME_DIR_READ 0x0010 #define MFI_FRAME_DIR_BOTH 0x0018 +#define MFI_FRAME_IEEE 0x0020 /* * Definition for cmd_status @@ -131,6 +134,7 @@ #define MR_DCMD_CLUSTER 0x08000000 #define MR_DCMD_CLUSTER_RESET_ALL 0x08010100 #define MR_DCMD_CLUSTER_RESET_LD 0x08010200 +#define MR_DCMD_PD_LIST_QUERY 0x02010100 /* * MFI command completion codes @@ -251,9 +255,100 @@ enum MR_EVT_ARGS { MR_EVT_ARGS_STR, MR_EVT_ARGS_TIME, MR_EVT_ARGS_ECC, + MR_EVT_ARGS_LD_PROP, + MR_EVT_ARGS_PD_SPARE, + MR_EVT_ARGS_PD_INDEX, + MR_EVT_ARGS_DIAG_PASS, + MR_EVT_ARGS_DIAG_FAIL, + MR_EVT_ARGS_PD_LBA_LBA, + MR_EVT_ARGS_PORT_PHY, + MR_EVT_ARGS_PD_MISSING, + MR_EVT_ARGS_PD_ADDRESS, + MR_EVT_ARGS_BITMAP, + MR_EVT_ARGS_CONNECTOR, + MR_EVT_ARGS_PD_PD, + MR_EVT_ARGS_PD_FRU, + MR_EVT_ARGS_PD_PATHINFO, + MR_EVT_ARGS_PD_POWER_STATE, + MR_EVT_ARGS_GENERIC, +}; +/* + * define constants for device list query options + */ +enum MR_PD_QUERY_TYPE { + MR_PD_QUERY_TYPE_ALL = 0, + MR_PD_QUERY_TYPE_STATE = 1, + MR_PD_QUERY_TYPE_POWER_STATE = 2, + MR_PD_QUERY_TYPE_MEDIA_TYPE = 3, + MR_PD_QUERY_TYPE_SPEED = 4, + MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5, }; +#define MR_EVT_CFG_CLEARED 0x0004 +#define MR_EVT_LD_STATE_CHANGE 0x0051 +#define MR_EVT_PD_INSERTED 0x005b +#define MR_EVT_PD_REMOVED 0x0070 +#define MR_EVT_LD_CREATED 0x008a +#define MR_EVT_LD_DELETED 0x008b +#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db +#define MR_EVT_LD_OFFLINE 0x00fc +#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152 +#define MAX_LOGICAL_DRIVES 64 + +enum MR_PD_STATE { + MR_PD_STATE_UNCONFIGURED_GOOD = 0x00, + MR_PD_STATE_UNCONFIGURED_BAD = 0x01, + MR_PD_STATE_HOT_SPARE = 0x02, + MR_PD_STATE_OFFLINE = 0x10, + MR_PD_STATE_FAILED = 0x11, + MR_PD_STATE_REBUILD = 0x14, + MR_PD_STATE_ONLINE = 0x18, + MR_PD_STATE_COPYBACK = 0x20, + MR_PD_STATE_SYSTEM = 0x40, + }; + + + /* + * defines the physical drive address structure + */ +struct MR_PD_ADDRESS { + u16 deviceId; + u16 enclDeviceId; + + union { + struct { + u8 enclIndex; + u8 slotNumber; + } mrPdAddress; + struct { + u8 enclPosition; + u8 enclConnectorIndex; + } mrEnclAddress; + }; + u8 scsiDevType; + union { + u8 connectedPortBitmap; + u8 connectedPortNumbers; + }; + u64 sasAddr[2]; +} __packed; + +/* + * defines the physical drive list structure + */ +struct MR_PD_LIST { + u32 size; + u32 count; + struct MR_PD_ADDRESS addr[1]; +} __packed; + +struct megasas_pd_list { + u16 tid; + u8 driveType; + u8 driveState; +} __packed; + /* * SAS controller properties */ @@ -282,7 +377,7 @@ struct megasas_ctrl_prop { u8 expose_encl_devices; u8 reserved[38]; -} __attribute__ ((packed)); +} __packed; /* * SAS controller information @@ -525,7 +620,7 @@ struct megasas_ctrl_info { u8 pad[0x800 - 0x6a0]; -} __attribute__ ((packed)); +} __packed; /* * =============================== @@ -540,6 +635,8 @@ struct megasas_ctrl_info { #define MEGASAS_DEFAULT_INIT_ID -1 #define MEGASAS_MAX_LUN 8 #define MEGASAS_MAX_LD 64 +#define MEGASAS_MAX_PD (MEGASAS_MAX_PD_CHANNELS * \ + MEGASAS_MAX_DEV_PER_CHANNEL) #define MEGASAS_DBG_LVL 1 @@ -570,6 +667,7 @@ struct megasas_ctrl_info { * is shown below */ #define MEGASAS_INT_CMDS 32 +#define MEGASAS_SKINNY_INT_CMDS 5 /* * FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit @@ -584,6 +682,8 @@ struct megasas_ctrl_info { #define MFI_REPLY_1078_MESSAGE_INTERRUPT 0x80000000 #define MFI_REPLY_GEN2_MESSAGE_INTERRUPT 0x00000001 #define MFI_GEN2_ENABLE_INTERRUPT_MASK (0x00000001 | 0x00000004) +#define MFI_REPLY_SKINNY_MESSAGE_INTERRUPT 0x40000000 +#define MFI_SKINNY_ENABLE_INTERRUPT_MASK (0x00000001) /* * register set for both 1068 and 1078 controllers @@ -644,10 +744,17 @@ struct megasas_sge64 { } __attribute__ ((packed)); +struct megasas_sge_skinny { + u64 phys_addr; + u32 length; + u32 flag; +} __packed; + union megasas_sgl { struct megasas_sge32 sge32[1]; struct megasas_sge64 sge64[1]; + struct megasas_sge_skinny sge_skinny[1]; } __attribute__ ((packed)); @@ -1061,16 +1168,10 @@ struct megasas_evt_detail { } __attribute__ ((packed)); - struct megasas_instance_template { - void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *); - - void (*enable_intr)(struct megasas_register_set __iomem *) ; - void (*disable_intr)(struct megasas_register_set __iomem *); - - int (*clear_intr)(struct megasas_register_set __iomem *); - - u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *); - }; +struct megasas_aen_event { + struct work_struct hotplug_work; + struct megasas_instance *instance; +}; struct megasas_instance { @@ -1085,17 +1186,21 @@ struct megasas_instance { unsigned long base_addr; struct megasas_register_set __iomem *reg_set; + struct megasas_pd_list pd_list[MEGASAS_MAX_PD]; s8 init_id; u16 max_num_sge; u16 max_fw_cmds; u32 max_sectors_per_req; + struct megasas_aen_event *ev; struct megasas_cmd **cmd_list; struct list_head cmd_pool; spinlock_t cmd_pool_lock; /* used to synch producer, consumer ptrs in dpc */ spinlock_t completion_lock; + /* used to sync fire the cmd to fw */ + spinlock_t fire_lock; struct dma_pool *frame_dma_pool; struct dma_pool *sense_dma_pool; @@ -1120,11 +1225,25 @@ struct megasas_instance { struct tasklet_struct isr_tasklet; u8 flag; + u8 unload; + u8 flag_ieee; unsigned long last_time; struct timer_list io_completion_timer; }; +struct megasas_instance_template { + void (*fire_cmd)(struct megasas_instance *, dma_addr_t, \ + u32, struct megasas_register_set __iomem *); + + void (*enable_intr)(struct megasas_register_set __iomem *) ; + void (*disable_intr)(struct megasas_register_set __iomem *); + + int (*clear_intr)(struct megasas_register_set __iomem *); + + u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *); +}; + #define MEGASAS_IS_LOGICAL(scp) \ (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h index f9f6c0839276..914168105297 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -8,7 +8,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.12 + * mpi2.h Version: 02.00.13 * * Version History * --------------- @@ -52,6 +52,7 @@ * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those * bytes reserved. * Added RAID Accelerator functionality. + * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT. * -------------------------------------------------------------------------- */ @@ -77,7 +78,7 @@ #define MPI2_VERSION_02_00 (0x0200) /* versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x0C) +#define MPI2_HEADER_VERSION_UNIT (0x0D) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h index ab47c4679640..1611c57a6fdf 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -6,7 +6,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.11 + * mpi2_cnfg.h Version: 02.00.12 * * Version History * --------------- @@ -100,6 +100,13 @@ * Added expander reduced functionality data to SAS * Expander Page 0. * Added SAS PHY Page 2 and SAS PHY Page 3. + * 07-30-09 02.00.12 Added IO Unit Page 7. + * Added new device ids. + * Added SAS IO Unit Page 5. + * Added partial and slumber power management capable flags + * to SAS Device Page 0 Flags field. + * Added PhyInfo defines for power condition. + * Added Ethernet configuration pages. * -------------------------------------------------------------------------- */ @@ -182,6 +189,7 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION #define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16) #define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17) #define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18) +#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19) /***************************************************************************** @@ -268,6 +276,14 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION #define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF) +/* Ethernet PageAddress format */ +#define MPI2_ETHERNET_PGAD_FORM_MASK (0xF0000000) +#define MPI2_ETHERNET_PGAD_FORM_IF_NUM (0x00000000) + +#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF) + + + /**************************************************************************** * Configuration messages ****************************************************************************/ @@ -349,6 +365,15 @@ typedef struct _MPI2_CONFIG_REPLY #define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064) #define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065) +#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080) +#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081) +#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082) +#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083) +#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084) +#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085) +#define MPI2_MFGPAGE_DEVID_SAS2208_7 (0x0086) +#define MPI2_MFGPAGE_DEVID_SAS2208_8 (0x0087) + /* Manufacturing Page 0 */ @@ -787,6 +812,56 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 { #define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001) +/* IO Unit Page 7 */ + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U16 Reserved1; /* 0x04 */ + U8 PCIeWidth; /* 0x06 */ + U8 PCIeSpeed; /* 0x07 */ + U32 ProcessorState; /* 0x08 */ + U32 Reserved2; /* 0x0C */ + U16 IOCTemperature; /* 0x10 */ + U8 IOCTemperatureUnits; /* 0x12 */ + U8 IOCSpeed; /* 0x13 */ + U32 Reserved3; /* 0x14 */ +} MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7, + Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t; + +#define MPI2_IOUNITPAGE7_PAGEVERSION (0x00) + +/* defines for IO Unit Page 7 PCIeWidth field */ +#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01) +#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02) +#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04) +#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08) + +/* defines for IO Unit Page 7 PCIeSpeed field */ +#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00) +#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01) +#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02) + +/* defines for IO Unit Page 7 ProcessorState field */ +#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F) +#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND (0) + +#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT (0x00) +#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01) +#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02) + +/* defines for IO Unit Page 7 IOCTemperatureUnits field */ +#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00) +#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01) +#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02) + +/* defines for IO Unit Page 7 IOCSpeed field */ +#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL (0x01) +#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF (0x02) +#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04) +#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08) + + + /**************************************************************************** * IOC Config Pages ****************************************************************************/ @@ -1470,6 +1545,12 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 /* values for PhyInfo fields */ #define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000) + +#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000) +#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000) +#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000) +#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000) + #define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000) #define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000) #define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000) @@ -1682,11 +1763,11 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 /* values for SAS IO Unit Page 1 PortFlags */ #define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01) -/* values for SAS IO Unit Page 2 PhyFlags */ +/* values for SAS IO Unit Page 1 PhyFlags */ #define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10) #define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08) -/* values for SAS IO Unit Page 0 MaxMinLinkRate */ +/* values for SAS IO Unit Page 1 MaxMinLinkRate */ #define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0) #define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80) #define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90) @@ -1745,6 +1826,74 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4 #define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03) +/* SAS IO Unit Page 5 */ + +typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS { + U8 ControlFlags; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 InactivityTimerExponent; /* 0x02 */ + U8 SATAPartialTimeout; /* 0x04 */ + U8 Reserved2; /* 0x05 */ + U8 SATASlumberTimeout; /* 0x06 */ + U8 Reserved3; /* 0x07 */ + U8 SASPartialTimeout; /* 0x08 */ + U8 Reserved4; /* 0x09 */ + U8 SASSlumberTimeout; /* 0x0A */ + U8 Reserved5; /* 0x0B */ +} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS, + MPI2_POINTER PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS, + Mpi2SasIOUnit5PhyPmSettings_t, MPI2_POINTER pMpi2SasIOUnit5PhyPmSettings_t; + +/* defines for ControlFlags field */ +#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE (0x08) +#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE (0x04) +#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02) +#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01) + +/* defines for InactivityTimerExponent field */ +#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000) +#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12) +#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL (0x0700) +#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL (8) +#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER (0x0070) +#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER (4) +#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL (0x0007) +#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL (0) + +#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS (7) +#define MPI2_SASIOUNIT5_ITE_ONE_SECOND (6) +#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS (5) +#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS (4) +#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND (3) +#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS (2) +#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS (1) +#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhys at runtime. + */ +#ifndef MPI2_SAS_IOUNIT5_PHY_MAX +#define MPI2_SAS_IOUNIT5_PHY_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 NumPhys; /* 0x08 */ + U8 Reserved1; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U32 Reserved3; /* 0x0C */ + MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS SASPhyPowerManagementSettings + [MPI2_SAS_IOUNIT5_PHY_MAX]; /* 0x10 */ +} MPI2_CONFIG_PAGE_SASIOUNIT_5, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5, + Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t; + +#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x00) + + + + /**************************************************************************** * SAS Expander Config Pages ****************************************************************************/ @@ -1927,6 +2076,8 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 /* see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */ /* values for SAS Device Page 0 Flags field */ +#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000) +#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800) #define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400) #define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200) #define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100) @@ -2343,5 +2494,122 @@ typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 #define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F) +/**************************************************************************** +* Ethernet Config Pages +****************************************************************************/ + +/* Ethernet Page 0 */ + +/* IP address (union of IPv4 and IPv6) */ +typedef union _MPI2_ETHERNET_IP_ADDR { + U32 IPv4Addr; + U32 IPv6Addr[4]; +} MPI2_ETHERNET_IP_ADDR, MPI2_POINTER PTR_MPI2_ETHERNET_IP_ADDR, + Mpi2EthernetIpAddr_t, MPI2_POINTER pMpi2EthernetIpAddr_t; + +#define MPI2_ETHERNET_HOST_NAME_LENGTH (32) + +typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 NumInterfaces; /* 0x08 */ + U8 Reserved0; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U32 Status; /* 0x0C */ + U8 MediaState; /* 0x10 */ + U8 Reserved2; /* 0x11 */ + U16 Reserved3; /* 0x12 */ + U8 MacAddress[6]; /* 0x14 */ + U8 Reserved4; /* 0x1A */ + U8 Reserved5; /* 0x1B */ + MPI2_ETHERNET_IP_ADDR IpAddress; /* 0x1C */ + MPI2_ETHERNET_IP_ADDR SubnetMask; /* 0x2C */ + MPI2_ETHERNET_IP_ADDR GatewayIpAddress; /* 0x3C */ + MPI2_ETHERNET_IP_ADDR DNS1IpAddress; /* 0x4C */ + MPI2_ETHERNET_IP_ADDR DNS2IpAddress; /* 0x5C */ + MPI2_ETHERNET_IP_ADDR DhcpIpAddress; /* 0x6C */ + U8 HostName + [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */ +} MPI2_CONFIG_PAGE_ETHERNET_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_0, + Mpi2EthernetPage0_t, MPI2_POINTER pMpi2EthernetPage0_t; + +#define MPI2_ETHERNETPAGE0_PAGEVERSION (0x00) + +/* values for Ethernet Page 0 Status field */ +#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE (0x80000000) +#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE (0x40000000) +#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED (0x20000000) +#define MPI2_ETHPG0_STATUS_DEFAULT_IF (0x00000100) +#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED (0x00000080) +#define MPI2_ETHPG0_STATUS_TELNET_ENABLED (0x00000040) +#define MPI2_ETHPG0_STATUS_SSH2_ENABLED (0x00000020) +#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED (0x00000010) +#define MPI2_ETHPG0_STATUS_IPV6_ENABLED (0x00000008) +#define MPI2_ETHPG0_STATUS_IPV4_ENABLED (0x00000004) +#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES (0x00000002) +#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED (0x00000001) + +/* values for Ethernet Page 0 MediaState field */ +#define MPI2_ETHPG0_MS_DUPLEX_MASK (0x80) +#define MPI2_ETHPG0_MS_HALF_DUPLEX (0x00) +#define MPI2_ETHPG0_MS_FULL_DUPLEX (0x80) + +#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK (0x07) +#define MPI2_ETHPG0_MS_NOT_CONNECTED (0x00) +#define MPI2_ETHPG0_MS_10MBIT (0x01) +#define MPI2_ETHPG0_MS_100MBIT (0x02) +#define MPI2_ETHPG0_MS_1GBIT (0x03) + + +/* Ethernet Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved0; /* 0x08 */ + U32 Flags; /* 0x0C */ + U8 MediaState; /* 0x10 */ + U8 Reserved1; /* 0x11 */ + U16 Reserved2; /* 0x12 */ + U8 MacAddress[6]; /* 0x14 */ + U8 Reserved3; /* 0x1A */ + U8 Reserved4; /* 0x1B */ + MPI2_ETHERNET_IP_ADDR StaticIpAddress; /* 0x1C */ + MPI2_ETHERNET_IP_ADDR StaticSubnetMask; /* 0x2C */ + MPI2_ETHERNET_IP_ADDR StaticGatewayIpAddress; /* 0x3C */ + MPI2_ETHERNET_IP_ADDR StaticDNS1IpAddress; /* 0x4C */ + MPI2_ETHERNET_IP_ADDR StaticDNS2IpAddress; /* 0x5C */ + U32 Reserved5; /* 0x6C */ + U32 Reserved6; /* 0x70 */ + U32 Reserved7; /* 0x74 */ + U32 Reserved8; /* 0x78 */ + U8 HostName + [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */ +} MPI2_CONFIG_PAGE_ETHERNET_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_1, + Mpi2EthernetPage1_t, MPI2_POINTER pMpi2EthernetPage1_t; + +#define MPI2_ETHERNETPAGE1_PAGEVERSION (0x00) + +/* values for Ethernet Page 1 Flags field */ +#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF (0x00000100) +#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD (0x00000080) +#define MPI2_ETHPG1_FLAG_ENABLE_TELNET (0x00000040) +#define MPI2_ETHPG1_FLAG_ENABLE_SSH2 (0x00000020) +#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT (0x00000010) +#define MPI2_ETHPG1_FLAG_ENABLE_IPV6 (0x00000008) +#define MPI2_ETHPG1_FLAG_ENABLE_IPV4 (0x00000004) +#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES (0x00000002) +#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF (0x00000001) + +/* values for Ethernet Page 1 MediaState field */ +#define MPI2_ETHPG1_MS_DUPLEX_MASK (0x80) +#define MPI2_ETHPG1_MS_HALF_DUPLEX (0x00) +#define MPI2_ETHPG1_MS_FULL_DUPLEX (0x80) + +#define MPI2_ETHPG1_MS_DATA_RATE_MASK (0x07) +#define MPI2_ETHPG1_MS_DATA_RATE_AUTO (0x00) +#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT (0x01) +#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT (0x02) +#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03) + + #endif diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h index c294128bdeb4..ea51ce868690 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -6,7 +6,7 @@ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.11 + * mpi2_ioc.h Version: 02.00.12 * * Version History * --------------- @@ -84,6 +84,9 @@ * Added two new reason codes for SAS Device Status Change * Event. * Added new event: SAS PHY Counter. + * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure. + * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. + * Added new product id family for 2208. * -------------------------------------------------------------------------- */ @@ -274,6 +277,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY #define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100) #define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080) #define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040) +#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020) #define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010) #define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008) #define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004) @@ -448,6 +452,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY #define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020) #define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021) #define MPI2_EVENT_SAS_PHY_COUNTER (0x0022) +#define MPI2_EVENT_GPIO_INTERRUPT (0x0023) /* Log Entry Added Event data */ @@ -469,6 +474,16 @@ typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED MPI2_POINTER PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED, Mpi2EventDataLogEntryAdded_t, MPI2_POINTER pMpi2EventDataLogEntryAdded_t; +/* GPIO Interrupt Event data */ + +typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT { + U8 GPIONum; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ +} MPI2_EVENT_DATA_GPIO_INTERRUPT, + MPI2_POINTER PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT, + Mpi2EventDataGpioInterrupt_t, MPI2_POINTER pMpi2EventDataGpioInterrupt_t; + /* Hard Reset Received Event data */ typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED @@ -1117,6 +1132,7 @@ typedef struct _MPI2_FW_IMAGE_HEADER #define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF) /* SAS */ #define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0010) +#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0011) /* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h index 7134816d9046..5160c33d2a00 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h @@ -6,7 +6,7 @@ * Title: MPI Integrated RAID messages and structures * Creation Date: April 26, 2007 * - * mpi2_raid.h Version: 02.00.03 + * mpi2_raid.h Version: 02.00.04 * * Version History * --------------- @@ -20,6 +20,8 @@ * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT * can be sized by the build environment. + * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of + * VolumeCreationFlags and marked the old one as obsolete. * -------------------------------------------------------------------------- */ @@ -217,10 +219,14 @@ typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT /* use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */ /* defines for the VolumeCreationFlags field */ +#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS (0x80000000) +#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x00000004) +#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x00000002) +#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x00000001) +/* The following is an obsolete define. + * It must be shifted left 24 bits in order to set the proper bit. + */ #define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80) -#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x04) -#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x02) -#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x01) /* RAID Online Capacity Expansion Structure */ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h index 007e950f7bfa..73fcdbf92632 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h @@ -6,7 +6,7 @@ * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.03 + * mpi2_tool.h Version: 02.00.04 * * Version History * --------------- @@ -18,6 +18,10 @@ * structures and defines. * 02-29-08 02.00.02 Modified various names to make them 32-character unique. * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. + * 07-30-09 02.00.04 Added ExtendedType field to DiagnosticBufferPost request + * and reply messages. + * Added MPI2_DIAG_BUF_TYPE_EXTENDED. + * Incremented MPI2_DIAG_BUF_TYPE_COUNT. * -------------------------------------------------------------------------- */ @@ -282,7 +286,7 @@ typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY { typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST { - U8 Reserved1; /* 0x00 */ + U8 ExtendedType; /* 0x00 */ U8 BufferType; /* 0x01 */ U8 ChainOffset; /* 0x02 */ U8 Function; /* 0x03 */ @@ -301,11 +305,15 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST } MPI2_DIAG_BUFFER_POST_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REQUEST, Mpi2DiagBufferPostRequest_t, MPI2_POINTER pMpi2DiagBufferPostRequest_t; +/* values for the ExtendedType field */ +#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION (0x02) + /* values for the BufferType field */ #define MPI2_DIAG_BUF_TYPE_TRACE (0x00) #define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01) +#define MPI2_DIAG_BUF_TYPE_EXTENDED (0x02) /* count of the number of buffer types */ -#define MPI2_DIAG_BUF_TYPE_COUNT (0x02) +#define MPI2_DIAG_BUF_TYPE_COUNT (0x03) /**************************************************************************** @@ -314,7 +322,7 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST typedef struct _MPI2_DIAG_BUFFER_POST_REPLY { - U8 Reserved1; /* 0x00 */ + U8 ExtendedType; /* 0x00 */ U8 BufferType; /* 0x01 */ U8 MsgLength; /* 0x02 */ U8 Function; /* 0x03 */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 670241efa4b5..6422e258fd52 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -57,6 +57,7 @@ #include <linux/dma-mapping.h> #include <linux/sort.h> #include <linux/io.h> +#include <linux/time.h> #include "mpt2sas_base.h" @@ -77,6 +78,44 @@ static int msix_disable = -1; module_param(msix_disable, int, 0); MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); +/* diag_buffer_enable is bitwise + * bit 0 set = TRACE + * bit 1 set = SNAPSHOT + * bit 2 set = EXTENDED + * + * Either bit can be set, or both + */ +static int diag_buffer_enable; +module_param(diag_buffer_enable, int, 0); +MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers " + "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); + +int mpt2sas_fwfault_debug; +MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault " + "and halt firmware - (default=0)"); + +/** + * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. + * + */ +static int +_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) +{ + int ret = param_set_int(val, kp); + struct MPT2SAS_ADAPTER *ioc; + + if (ret) + return ret; + + printk(KERN_INFO "setting logging_level(0x%08x)\n", + mpt2sas_fwfault_debug); + list_for_each_entry(ioc, &mpt2sas_ioc_list, list) + ioc->fwfault_debug = mpt2sas_fwfault_debug; + return 0; +} +module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug, + param_get_int, &mpt2sas_fwfault_debug, 0644); + /** * _base_fault_reset_work - workq handling ioc fault conditions * @work: input argument, used to derive ioc @@ -121,7 +160,7 @@ _base_fault_reset_work(struct work_struct *work) /** * mpt2sas_base_start_watchdog - start the fault_reset_work_q - * @ioc: pointer to scsi command object + * @ioc: per adapter object * Context: sleep. * * Return nothing. @@ -155,7 +194,7 @@ mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc) /** * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q - * @ioc: pointer to scsi command object + * @ioc: per adapter object * Context: sleep. * * Return nothing. @@ -177,10 +216,55 @@ mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc) } } +/** + * mpt2sas_base_fault_info - verbose translation of firmware FAULT code + * @ioc: per adapter object + * @fault_code: fault code + * + * Return nothing. + */ +void +mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code) +{ + printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n", + ioc->name, fault_code); +} + +/** + * mpt2sas_halt_firmware - halt's mpt controller firmware + * @ioc: per adapter object + * + * For debugging timeout related issues. Writing 0xCOFFEE00 + * to the doorbell register will halt controller firmware. With + * the purpose to stop both driver and firmware, the enduser can + * obtain a ring buffer from controller UART. + */ +void +mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc) +{ + u32 doorbell; + + if (!ioc->fwfault_debug) + return; + + dump_stack(); + + doorbell = readl(&ioc->chip->Doorbell); + if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) + mpt2sas_base_fault_info(ioc , doorbell); + else { + writel(0xC0FFEE00, &ioc->chip->Doorbell); + printk(MPT2SAS_ERR_FMT "Firmware is halted due to command " + "timeout\n", ioc->name); + } + + panic("panic in %s\n", __func__); +} + #ifdef CONFIG_SCSI_MPT2SAS_LOGGING /** * _base_sas_ioc_info - verbose translation of the ioc status - * @ioc: pointer to scsi command object + * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware * @request_hdr: request mf * @@ -394,7 +478,7 @@ _base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, /** * _base_display_event_data - verbose translation of firmware asyn events - * @ioc: pointer to scsi command object + * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware * * Return nothing. @@ -474,7 +558,7 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc, /** * _base_sas_log_info - verbose translation of firmware log info - * @ioc: pointer to scsi command object + * @ioc: per adapter object * @log_info: log info * * Return nothing. @@ -526,22 +610,8 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info) } /** - * mpt2sas_base_fault_info - verbose translation of firmware FAULT code - * @ioc: pointer to scsi command object - * @fault_code: fault code - * - * Return nothing. - */ -void -mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code) -{ - printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n", - ioc->name, fault_code); -} - -/** * _base_display_reply_info - - * @ioc: pointer to scsi command object + * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) @@ -570,7 +640,7 @@ _base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, /** * mpt2sas_base_done - base internal command completion routine - * @ioc: pointer to scsi command object + * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) @@ -603,7 +673,7 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, /** * _base_async_event - main callback handler for firmware asyn events - * @ioc: pointer to scsi command object + * @ioc: per adapter object * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * @@ -684,7 +754,7 @@ _base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid) /** * _base_mask_interrupts - disable interrupts - * @ioc: pointer to scsi command object + * @ioc: per adapter object * * Disabling ResetIRQ, Reply and Doorbell Interrupts * @@ -704,7 +774,7 @@ _base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc) /** * _base_unmask_interrupts - enable interrupts - * @ioc: pointer to scsi command object + * @ioc: per adapter object * * Enabling only Reply Interrupts * @@ -1258,12 +1328,13 @@ mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid) * @ioc: per adapter object * @smid: system request message index * - * Returns phys pointer to sense buffer. + * Returns phys pointer to the low 32bit address of the sense buffer. */ -dma_addr_t +__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid) { - return ioc->sense_dma + ((smid - 1) * SCSI_SENSE_BUFFERSIZE); + return cpu_to_le32(ioc->sense_dma + + ((smid - 1) * SCSI_SENSE_BUFFERSIZE)); } /** @@ -1697,6 +1768,12 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc) } if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) { + printk(KERN_INFO "%sDiag Extended Buffer", i ? "," : ""); + i++; + } + + if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) { printk("%sTask Set Full", i ? "," : ""); i++; @@ -2871,6 +2948,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) Mpi2IOCInitRequest_t mpi_request; Mpi2IOCInitReply_t mpi_reply; int r; + struct timeval current_time; + u16 ioc_status; dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, __func__)); @@ -2921,6 +3000,13 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) cpu_to_le32(ioc->reply_post_free_dma); #endif + /* This time stamp specifies number of milliseconds + * since epoch ~ midnight January 1, 1970. + */ + do_gettimeofday(¤t_time); + mpi_request.TimeStamp = (current_time.tv_sec * 1000) + + (current_time.tv_usec >> 3); + if (ioc->logging_level & MPT_DEBUG_INIT) { u32 *mfp; int i; @@ -2943,7 +3029,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) return r; } - if (mpi_reply.IOCStatus != MPI2_IOCSTATUS_SUCCESS || + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS || mpi_reply.IOCLogInfo) { printk(MPT2SAS_ERR_FMT "%s: failed\n", ioc->name, __func__); r = -EIO; @@ -3461,11 +3548,11 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) return r; pci_set_drvdata(ioc->pdev, ioc->shost); - r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + r = _base_get_ioc_facts(ioc, CAN_SLEEP); if (r) goto out_free_resources; - r = _base_get_ioc_facts(ioc, CAN_SLEEP); + r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); if (r) goto out_free_resources; @@ -3531,6 +3618,8 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) goto out_free_resources; mpt2sas_base_start_watchdog(ioc); + if (diag_buffer_enable != 0) + mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable); return 0; out_free_resources: @@ -3684,6 +3773,9 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, __func__)); + if (mpt2sas_fwfault_debug) + mpt2sas_halt_firmware(ioc); + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); if (ioc->shost_recovery) { spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 0cf6bc236e4d..bb4f14656afa 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -69,8 +69,8 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>" #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "02.100.03.00" -#define MPT2SAS_MAJOR_VERSION 02 +#define MPT2SAS_DRIVER_VERSION "03.100.03.00" +#define MPT2SAS_MAJOR_VERSION 03 #define MPT2SAS_MINOR_VERSION 100 #define MPT2SAS_BUILD_VERSION 03 #define MPT2SAS_RELEASE_VERSION 00 @@ -278,7 +278,7 @@ struct _internal_cmd { * @sas_address: device sas address * @device_name: retrieved from the SAS IDENTIFY frame. * @handle: device handle - * @parent_handle: handle to parent device + * @sas_address_parent: sas address of parent expander or sas host * @enclosure_handle: enclosure handle * @enclosure_logical_id: enclosure logical identifier * @volume_handle: volume handle (valid when hidden raid member) @@ -296,7 +296,7 @@ struct _sas_device { u64 sas_address; u64 device_name; u16 handle; - u16 parent_handle; + u64 sas_address_parent; u16 enclosure_handle; u64 enclosure_logical_id; u16 volume_handle; @@ -352,8 +352,6 @@ struct _boot_device { /** * struct _sas_port - wide/narrow sas port information * @port_list: list of ports belonging to expander - * @handle: device handle for this port - * @sas_address: sas address of this port * @num_phys: number of phys belonging to this port * @remote_identify: attached device identification * @rphy: sas transport rphy object @@ -362,8 +360,6 @@ struct _boot_device { */ struct _sas_port { struct list_head port_list; - u16 handle; - u64 sas_address; u8 num_phys; struct sas_identify remote_identify; struct sas_rphy *rphy; @@ -398,7 +394,7 @@ struct _sas_phy { * @num_phys: number phys belonging to this sas_host/expander * @sas_address: sas address of this sas_host/expander * @handle: handle for this sas_host/expander - * @parent_handle: parent handle + * @sas_address_parent: sas address of parent expander or sas host * @enclosure_handle: handle for this a member of an enclosure * @device_info: bitwise defining capabilities of this sas_host/expander * @responding: used in _scsih_expander_device_mark_responding @@ -411,7 +407,7 @@ struct _sas_node { u8 num_phys; u64 sas_address; u16 handle; - u16 parent_handle; + u64 sas_address_parent; u16 enclosure_handle; u64 enclosure_logical_id; u8 responding; @@ -470,6 +466,7 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @chip_phys: physical addrss prior to mapping * @pio_chip: I/O mapped register space * @logging_level: see mpt2sas_debug.h + * @fwfault_debug: debuging FW timeouts * @ir_firmware: IR firmware present * @bars: bitmask of BAR's that must be configured * @mask_interrupts: ignore interrupt @@ -495,12 +492,14 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @msix_table_backup: backup msix table * @scsi_io_cb_idx: shost generated commands * @tm_cb_idx: task management commands + * @scsih_cb_idx: scsih internal commands * @transport_cb_idx: transport internal commands * @ctl_cb_idx: clt internal commands * @base_cb_idx: base internal commands * @config_cb_idx: base internal commands * @base_cmds: * @transport_cmds: + * @scsih_cmds: * @tm_cmds: * @ctl_cmds: * @config_cmds: @@ -591,6 +590,7 @@ struct MPT2SAS_ADAPTER { unsigned long chip_phys; unsigned long pio_chip; int logging_level; + int fwfault_debug; u8 ir_firmware; int bars; u8 mask_interrupts; @@ -626,6 +626,7 @@ struct MPT2SAS_ADAPTER { u8 scsi_io_cb_idx; u8 tm_cb_idx; u8 transport_cb_idx; + u8 scsih_cb_idx; u8 ctl_cb_idx; u8 base_cb_idx; u8 config_cb_idx; @@ -633,6 +634,7 @@ struct MPT2SAS_ADAPTER { u8 tm_sas_control_cb_idx; struct _internal_cmd base_cmds; struct _internal_cmd transport_cmds; + struct _internal_cmd scsih_cmds; struct _internal_cmd tm_cmds; struct _internal_cmd ctl_cmds; struct _internal_cmd config_cmds; @@ -773,7 +775,7 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid); void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr); -dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, +__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); /* hi-priority queue */ @@ -807,6 +809,8 @@ int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request); void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type); +void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc); + /* scsih shared API */ u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply); @@ -886,19 +890,22 @@ u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, Mpi2EventNotificationReply_t *mpi_reply); +void mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, + u8 bits_to_regsiter); + /* transport shared API */ u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, - u16 handle, u16 parent_handle); + u16 handle, u64 sas_address); void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, - u16 parent_handle); + u64 sas_address_parent); int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev); int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev); -void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, u16 handle, - u16 attached_handle, u8 phy_number, u8 link_rate); +void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, + u64 sas_address, u16 handle, u8 phy_number, u8 link_rate); extern struct sas_function_template mpt2sas_transport_functions; extern struct scsi_transport_template *mpt2sas_transport_template; extern int scsi_internal_device_block(struct scsi_device *sdev); diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 57d724633906..84a124f8e21f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -740,7 +740,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, Mpi2SCSIIORequest_t *scsiio_request = (Mpi2SCSIIORequest_t *)mpi_request; scsiio_request->SenseBufferLowAddress = - (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid); + mpt2sas_base_get_sense_buffer_dma(ioc, smid); priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid); memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE); mpt2sas_base_put_smid_scsi_io(ioc, smid, @@ -848,8 +848,9 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: " "IOCStatus(0x%04x), IOCLogInfo(0x%08x), " "TerminationCount(0x%08x)\n", ioc->name, - tm_reply->IOCStatus, tm_reply->IOCLogInfo, - tm_reply->TerminationCount); + le16_to_cpu(tm_reply->IOCStatus), + le32_to_cpu(tm_reply->IOCLogInfo), + le32_to_cpu(tm_reply->TerminationCount)); } #endif /* copy out xdata to user */ @@ -896,6 +897,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, printk(MPT2SAS_INFO_FMT "issue target reset: handle " "= (0x%04x)\n", ioc->name, mpi_request->FunctionDependent1); + mpt2sas_halt_firmware(ioc); mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_issue_tm(ioc, mpi_request->FunctionDependent1, 0, @@ -1229,7 +1231,7 @@ _ctl_btdh_mapping(void __user *arg) /** * _ctl_diag_capability - return diag buffer capability * @ioc: per adapter object - * @buffer_type: specifies either TRACE or SNAPSHOT + * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED * * returns 1 when diag buffer support is enabled in firmware */ @@ -1249,24 +1251,25 @@ _ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type) MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) rc = 1; break; + case MPI2_DIAG_BUF_TYPE_EXTENDED: + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) + rc = 1; } return rc; } /** - * _ctl_diag_register - application register with driver - * @arg - user space buffer containing ioctl content - * @state - NON_BLOCKING or BLOCKING + * _ctl_diag_register_2 - wrapper for registering diag buffer support + * @ioc: per adapter object + * @diag_register: the diag_register struct passed in from user space * - * This will allow the driver to setup any required buffers that will be - * needed by firmware to communicate with the driver. */ static long -_ctl_diag_register(void __user *arg, enum block_state state) +_ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc, + struct mpt2_diag_register *diag_register) { - struct mpt2_diag_register karg; - struct MPT2SAS_ADAPTER *ioc; int rc, i; void *request_data = NULL; dma_addr_t request_data_dma; @@ -1279,18 +1282,17 @@ _ctl_diag_register(void __user *arg, enum block_state state) u16 ioc_status; u8 issue_reset = 0; - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, __func__)); - buffer_type = karg.buffer_type; + if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + buffer_type = diag_register->buffer_type; if (!_ctl_diag_capability(ioc, buffer_type)) { printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); @@ -1305,24 +1307,12 @@ _ctl_diag_register(void __user *arg, enum block_state state) return -EINVAL; } - if (karg.requested_buffer_size % 4) { + if (diag_register->requested_buffer_size % 4) { printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size " "is not 4 byte aligned\n", ioc->name, __func__); return -EINVAL; } - if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; - else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) - return -ERESTARTSYS; - - if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", @@ -1338,12 +1328,12 @@ _ctl_diag_register(void __user *arg, enum block_state state) ioc->ctl_cmds.smid = smid; request_data = ioc->diag_buffer[buffer_type]; - request_data_sz = karg.requested_buffer_size; - ioc->unique_id[buffer_type] = karg.unique_id; + request_data_sz = diag_register->requested_buffer_size; + ioc->unique_id[buffer_type] = diag_register->unique_id; ioc->diag_buffer_status[buffer_type] = 0; - memcpy(ioc->product_specific[buffer_type], karg.product_specific, - MPT2_PRODUCT_SPECIFIC_DWORDS); - ioc->diagnostic_flags[buffer_type] = karg.diagnostic_flags; + memcpy(ioc->product_specific[buffer_type], + diag_register->product_specific, MPT2_PRODUCT_SPECIFIC_DWORDS); + ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags; if (request_data) { request_data_dma = ioc->diag_buffer_dma[buffer_type]; @@ -1373,8 +1363,8 @@ _ctl_diag_register(void __user *arg, enum block_state state) } mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; - mpi_request->BufferType = karg.buffer_type; - mpi_request->Flags = cpu_to_le32(karg.diagnostic_flags); + mpi_request->BufferType = diag_register->buffer_type; + mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags); mpi_request->BufferAddress = cpu_to_le64(request_data_dma); mpi_request->BufferLength = cpu_to_le32(request_data_sz); mpi_request->VF_ID = 0; /* TODO */ @@ -1422,7 +1412,7 @@ _ctl_diag_register(void __user *arg, enum block_state state) } else { printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, - ioc_status, mpi_reply->IOCLogInfo); + ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; } @@ -1438,6 +1428,83 @@ _ctl_diag_register(void __user *arg, enum block_state state) request_data, request_data_dma); ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; + return rc; +} + +/** + * mpt2sas_enable_diag_buffer - enabling diag_buffers support driver load time + * @ioc: per adapter object + * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1 + * + * This is called when command line option diag_buffer_enable is enabled + * at driver load time. + */ +void +mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, u8 bits_to_register) +{ + struct mpt2_diag_register diag_register; + + memset(&diag_register, 0, sizeof(struct mpt2_diag_register)); + + if (bits_to_register & 1) { + printk(MPT2SAS_INFO_FMT "registering trace buffer support\n", + ioc->name); + diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; + /* register for 1MB buffers */ + diag_register.requested_buffer_size = (1024 * 1024); + diag_register.unique_id = 0x7075900; + _ctl_diag_register_2(ioc, &diag_register); + } + + if (bits_to_register & 2) { + printk(MPT2SAS_INFO_FMT "registering snapshot buffer support\n", + ioc->name); + diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT; + /* register for 2MB buffers */ + diag_register.requested_buffer_size = 2 * (1024 * 1024); + diag_register.unique_id = 0x7075901; + _ctl_diag_register_2(ioc, &diag_register); + } + + if (bits_to_register & 4) { + printk(MPT2SAS_INFO_FMT "registering extended buffer support\n", + ioc->name); + diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED; + /* register for 2MB buffers */ + diag_register.requested_buffer_size = 2 * (1024 * 1024); + diag_register.unique_id = 0x7075901; + _ctl_diag_register_2(ioc, &diag_register); + } +} + +/** + * _ctl_diag_register - application register with driver + * @arg - user space buffer containing ioctl content + * @state - NON_BLOCKING or BLOCKING + * + * This will allow the driver to setup any required buffers that will be + * needed by firmware to communicate with the driver. + */ +static long +_ctl_diag_register(void __user *arg, enum block_state state) +{ + struct mpt2_diag_register karg; + struct MPT2SAS_ADAPTER *ioc; + long rc; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) + return -EAGAIN; + else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) + return -ERESTARTSYS; + rc = _ctl_diag_register_2(ioc, &karg); mutex_unlock(&ioc->ctl_cmds.mutex); return rc; } @@ -1600,7 +1667,7 @@ _ctl_diag_query(void __user *arg) /** * _ctl_send_release - Diag Release Message * @ioc: per adapter object - * @buffer_type - specifies either TRACE or SNAPSHOT + * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED * @issue_reset - specifies whether host reset is required. * */ @@ -1690,7 +1757,7 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) } else { printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, - ioc_status, mpi_reply->IOCLogInfo); + ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; } @@ -1951,7 +2018,7 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) } else { printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, - ioc_status, mpi_reply->IOCLogInfo); + ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; } @@ -2474,6 +2541,43 @@ _ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, _ctl_logging_level_show, _ctl_logging_level_store); +/* device attributes */ +/* + * _ctl_fwfault_debug_show - show/store fwfault_debug + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * mpt2sas_fwfault_debug is command line option + * A sysfs 'read/write' shost attribute. + */ +static ssize_t +_ctl_fwfault_debug_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug); +} +static ssize_t +_ctl_fwfault_debug_store(struct device *cdev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + int val = 0; + + if (sscanf(buf, "%d", &val) != 1) + return -EINVAL; + + ioc->fwfault_debug = val; + printk(MPT2SAS_INFO_FMT "fwfault_debug=%d\n", ioc->name, + ioc->fwfault_debug); + return strlen(buf); +} +static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR, + _ctl_fwfault_debug_show, _ctl_fwfault_debug_store); + struct device_attribute *mpt2sas_host_attrs[] = { &dev_attr_version_fw, &dev_attr_version_bios, @@ -2487,13 +2591,12 @@ struct device_attribute *mpt2sas_host_attrs[] = { &dev_attr_io_delay, &dev_attr_device_delay, &dev_attr_logging_level, + &dev_attr_fwfault_debug, &dev_attr_fw_queue_depth, &dev_attr_host_sas_address, NULL, }; -/* device attributes */ - /** * _ctl_device_sas_address_show - sas address * @cdev - pointer to embedded class device diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h index 211f296dd191..8a5eeb1a5c84 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h @@ -313,7 +313,7 @@ struct mpt2_ioctl_btdh_mapping { * struct mpt2_diag_register - application register with driver * @hdr - generic header * @reserved - - * @buffer_type - specifies either TRACE or SNAPSHOT + * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED * @application_flags - misc flags * @diagnostic_flags - specifies flags affecting command processing * @product_specific - product specific information @@ -352,7 +352,7 @@ struct mpt2_diag_unregister { * struct mpt2_diag_query - query relevant info associated with diag buffers * @hdr - generic header * @reserved - - * @buffer_type - specifies either TRACE or SNAPSHOT + * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED * @application_flags - misc flags * @diagnostic_flags - specifies flags affecting command processing * @product_specific - product specific information diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 86ab32d7ab15..efabea1a3ce4 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -76,6 +76,7 @@ static u8 tm_cb_idx = -1; static u8 ctl_cb_idx = -1; static u8 base_cb_idx = -1; static u8 transport_cb_idx = -1; +static u8 scsih_cb_idx = -1; static u8 config_cb_idx = -1; static int mpt_ids; @@ -196,10 +197,28 @@ static struct pci_device_id scsih_pci_table[] = { PCI_ANY_ID, PCI_ANY_ID }, { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3, PCI_ANY_ID, PCI_ANY_ID }, + /* Meteor ~ 2116 */ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1, PCI_ANY_ID, PCI_ANY_ID }, { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2, PCI_ANY_ID, PCI_ANY_ID }, + /* Thunderbolt ~ 2208 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_7, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_8, + PCI_ANY_ID, PCI_ANY_ID }, {0} /* Terminating entry */ }; MODULE_DEVICE_TABLE(pci, scsih_pci_table); @@ -317,6 +336,47 @@ _scsih_is_boot_device(u64 sas_address, u64 device_name, } /** + * _scsih_get_sas_address - set the sas_address for given device handle + * @handle: device handle + * @sas_address: sas address + * + * Returns 0 success, non-zero when failure + */ +static int +_scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle, + u64 *sas_address) +{ + Mpi2SasDevicePage0_t sas_device_pg0; + Mpi2ConfigReply_t mpi_reply; + u32 ioc_status; + + if (handle <= ioc->sas_hba.num_phys) { + *sas_address = ioc->sas_hba.sas_address; + return 0; + } else + *sas_address = 0; + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -ENXIO; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)" + "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status, + __FILE__, __LINE__, __func__); + return -EIO; + } + + *sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + return 0; +} + +/** * _scsih_determine_boot_device - determine boot device. * @ioc: per adapter object * @device: either sas_device or raid_device object @@ -510,8 +570,6 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc, struct _sas_device *sas_device) { unsigned long flags; - u16 handle, parent_handle; - u64 sas_address; dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle" "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, @@ -521,10 +579,8 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc, list_add_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - handle = sas_device->handle; - parent_handle = sas_device->parent_handle; - sas_address = sas_device->sas_address; - if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) + if (!mpt2sas_transport_port_add(ioc, sas_device->handle, + sas_device->sas_address_parent)) _scsih_sas_device_remove(ioc, sas_device); } @@ -553,31 +609,6 @@ _scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc, } /** - * mpt2sas_scsih_expander_find_by_handle - expander device search - * @ioc: per adapter object - * @handle: expander handle (assigned by firmware) - * Context: Calling function should acquire ioc->sas_device_lock - * - * This searches for expander device based on handle, then returns the - * sas_node object. - */ -struct _sas_node * -mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _sas_node *sas_expander, *r; - - r = NULL; - list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { - if (sas_expander->handle != handle) - continue; - r = sas_expander; - goto out; - } - out: - return r; -} - -/** * _scsih_raid_device_find_by_id - raid device search * @ioc: per adapter object * @id: sas device target id @@ -699,6 +730,31 @@ _scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc, } /** + * mpt2sas_scsih_expander_find_by_handle - expander device search + * @ioc: per adapter object + * @handle: expander handle (assigned by firmware) + * Context: Calling function should acquire ioc->sas_device_lock + * + * This searches for expander device based on handle, then returns the + * sas_node object. + */ +struct _sas_node * +mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _sas_node *sas_expander, *r; + + r = NULL; + list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { + if (sas_expander->handle != handle) + continue; + r = sas_expander; + goto out; + } + out: + return r; +} + +/** * mpt2sas_scsih_expander_find_by_sas_address - expander device search * @ioc: per adapter object * @sas_address: sas address @@ -1043,17 +1099,46 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, * _scsih_change_queue_depth - setting device queue depth * @sdev: scsi device struct * @qdepth: requested queue depth + * @reason: calling context * * Returns queue depth. */ static int -_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) +_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) { struct Scsi_Host *shost = sdev->host; int max_depth; int tag_type; + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct MPT2SAS_TARGET *sas_target_priv_data; + struct _sas_device *sas_device; + unsigned long flags; + + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; max_depth = shost->can_queue; + + /* limit max device queue for SATA to 32 */ + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + goto not_sata; + sas_target_priv_data = sas_device_priv_data->sas_target; + if (!sas_target_priv_data) + goto not_sata; + if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) + goto not_sata; + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device_priv_data->sas_target->sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device && sas_device->device_info & + MPI2_SAS_DEVICE_INFO_SATA_DEVICE) + max_depth = MPT2SAS_SATA_QUEUE_DEPTH; + + not_sata: + if (!sdev->tagged_supported) max_depth = 1; if (qdepth > max_depth) @@ -1488,7 +1573,7 @@ _scsih_slave_configure(struct scsi_device *sdev) r_level, raid_device->handle, (unsigned long long)raid_device->wwid, raid_device->num_pds, ds); - _scsih_change_queue_depth(sdev, qdepth); + _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); return 0; } @@ -1534,7 +1619,7 @@ _scsih_slave_configure(struct scsi_device *sdev) _scsih_display_sata_capabilities(ioc, sas_device, sdev); } - _scsih_change_queue_depth(sdev, qdepth); + _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); if (ssp_target) sas_read_port_mode_page(sdev); @@ -1874,6 +1959,8 @@ _scsih_abort(struct scsi_cmnd *scmd) goto out; } + mpt2sas_halt_firmware(ioc); + mutex_lock(&ioc->tm_cmds.mutex); handle = sas_device_priv_data->sas_target->handle; mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun, @@ -2297,7 +2384,6 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc, u16 handle; u16 reason_code; u8 phy_number; - u8 link_rate; for (i = 0; i < event_data->NumEntries; i++) { handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); @@ -2308,11 +2394,6 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc, MPI2_EVENT_SAS_TOPO_RC_MASK; if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING) _scsih_block_io_device(ioc, handle); - if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) { - link_rate = event_data->PHY[i].LinkRate >> 4; - if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5) - _scsih_ublock_io_device(ioc, handle); - } } } @@ -2349,16 +2430,10 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", - ioc->name, __func__); - return; - } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); /* skip is hidden raid component */ - if (sas_device->hidden_raid_component) + if (sas_device && sas_device->hidden_raid_component) return; smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); @@ -2371,18 +2446,31 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL; list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); - if (sas_device->starget) + if (sas_device && sas_device->starget) { dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, "DELAYED:tr:handle(0x%04x), " - "(open)\n", sas_device->handle)); + "(open)\n", handle)); + } else { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "DELAYED:tr:handle(0x%04x), (open)\n", + ioc->name, handle)); + } return; } - if (sas_device->starget && sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->tm_busy = 1; - dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, - "tr:handle(0x%04x), (open)\n", sas_device->handle)); + if (sas_device) { + sas_device->state |= MPTSAS_STATE_TR_SEND; + sas_device->state |= MPT2SAS_REQ_SAS_CNTRL; + if (sas_device->starget && sas_device->starget->hostdata) { + sas_target_priv_data = sas_device->starget->hostdata; + sas_target_priv_data->tm_busy = 1; + dewtprintk(ioc, starget_printk(KERN_INFO, + sas_device->starget, "tr:handle(0x%04x), (open)\n", + handle)); + } + } else { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "tr:handle(0x%04x), (open)\n", ioc->name, handle)); } mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); @@ -2390,8 +2478,6 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; - sas_device->state |= MPTSAS_STATE_TR_SEND; - sas_device->state |= MPT2SAS_REQ_SAS_CNTRL; mpt2sas_base_put_smid_hi_priority(ioc, smid); } @@ -2426,21 +2512,25 @@ _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", - ioc->name, __func__); - return 1; - } - sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE; spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device->starget) - dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, + if (sas_device) { + sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE; + if (sas_device->starget) + dewtprintk(ioc, starget_printk(KERN_INFO, + sas_device->starget, + "sc_complete:handle(0x%04x), " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + handle, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo))); + } else { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sc_complete:handle(0x%04x), " "ioc_status(0x%04x), loginfo(0x%08x)\n", - handle, le16_to_cpu(mpi_reply->IOCStatus), + ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo))); + } + return 1; } @@ -2478,28 +2568,33 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, handle = le16_to_cpu(mpi_reply->DevHandle); spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", - ioc->name, __func__); - return 1; - } - sas_device->state |= MPTSAS_STATE_TR_COMPLETE; spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device->starget) - dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, - "tr_complete:handle(0x%04x), (%s) ioc_status(0x%04x), " - "loginfo(0x%08x), completed(%d)\n", - sas_device->handle, (sas_device->state & - MPT2SAS_REQ_SAS_CNTRL) ? "open" : "active", - le16_to_cpu(mpi_reply->IOCStatus), + if (sas_device) { + sas_device->state |= MPTSAS_STATE_TR_COMPLETE; + if (sas_device->starget) { + dewtprintk(ioc, starget_printk(KERN_INFO, + sas_device->starget, "tr_complete:handle(0x%04x), " + "(%s) ioc_status(0x%04x), loginfo(0x%08x), " + "completed(%d)\n", sas_device->handle, + (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ? + "open" : "active", + le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo), + le32_to_cpu(mpi_reply->TerminationCount))); + if (sas_device->starget->hostdata) { + sas_target_priv_data = + sas_device->starget->hostdata; + sas_target_priv_data->tm_busy = 0; + } + } + } else { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), " + "loginfo(0x%08x), completed(%d)\n", ioc->name, + handle, le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo), le32_to_cpu(mpi_reply->TerminationCount))); - - if (sas_device->starget && sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->tm_busy = 0; } if (!list_empty(&ioc->delayed_tr_list)) { @@ -2514,8 +2609,7 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, } else rc = 1; - - if (!(sas_device->state & MPT2SAS_REQ_SAS_CNTRL)) + if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL)) return rc; if (ioc->shost_recovery) { @@ -2531,12 +2625,14 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, return rc; } + if (sas_device) + sas_device->state |= MPTSAS_STATE_CNTRL_SEND; + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; mpi_request->DevHandle = mpi_reply->DevHandle; - sas_device->state |= MPTSAS_STATE_CNTRL_SEND; mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); return rc; } @@ -2678,8 +2774,6 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) else return; - mpi_request->EEDPBlockSize = scmd->device->sector_size; - switch (prot_type) { case SCSI_PROT_DIF_TYPE1: @@ -2687,8 +2781,7 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) * enable ref/guard checking * auto increment ref tag */ - mpi_request->EEDPFlags = eedp_flags | - MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | + eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; mpi_request->CDB.EEDP32.PrimaryReferenceTag = @@ -2701,11 +2794,11 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) /* * enable guard checking */ - mpi_request->EEDPFlags = eedp_flags | - MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; - + eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; break; } + mpi_request->EEDPBlockSize = cpu_to_le32(scmd->device->sector_size); + mpi_request->EEDPFlags = cpu_to_le16(eedp_flags); } /** @@ -2788,7 +2881,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) } /* see if we are busy with task managment stuff */ - if (sas_target_priv_data->tm_busy) + if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) return SCSI_MLQUEUE_DEVICE_BUSY; else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) return SCSI_MLQUEUE_HOST_BUSY; @@ -2842,7 +2935,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR; mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; mpi_request->SenseBufferLowAddress = - (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid); + mpt2sas_base_get_sense_buffer_dma(ioc, smid); mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI + MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR); @@ -2894,7 +2987,7 @@ _scsih_normalize_sense(char *sense_buffer, struct sense_info *data) #ifdef CONFIG_SCSI_MPT2SAS_LOGGING /** - * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request + * _scsih_scsi_ioc_info - translated non-successfull SCSI_IO request * @ioc: per adapter object * @scmd: pointer to scsi command object * @mpi_reply: reply mf payload returned from firmware @@ -3059,7 +3152,7 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { response_info = le32_to_cpu(mpi_reply->ResponseInfo); response_bytes = (u8 *)&response_info; - _scsih_response_code(ioc, response_bytes[3]); + _scsih_response_code(ioc, response_bytes[0]); } } #endif @@ -3177,7 +3270,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) u8 scsi_status; u32 log_info; struct MPT2SAS_DEVICE *sas_device_priv_data; - u32 response_code; + u32 response_code = 0; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); scmd = _scsih_scsi_lookup_get(ioc, smid); @@ -3199,16 +3292,16 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) } /* turning off TLR */ + scsi_state = mpi_reply->SCSIState; + if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) + response_code = + le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF; if (!sas_device_priv_data->tlr_snoop_check) { sas_device_priv_data->tlr_snoop_check++; - if (sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) { - response_code = (le32_to_cpu(mpi_reply->ResponseInfo) - >> 24); - if (response_code == - MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) - sas_device_priv_data->flags &= - ~MPT_DEVICE_TLR_ON; - } + if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) && + response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) + sas_device_priv_data->flags &= + ~MPT_DEVICE_TLR_ON; } xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); @@ -3219,7 +3312,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) else log_info = 0; ioc_status &= MPI2_IOCSTATUS_MASK; - scsi_state = mpi_reply->SCSIState; scsi_status = mpi_reply->SCSIStatus; if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 && @@ -3255,10 +3347,9 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: if (sas_device_priv_data->block) { - scmd->result = (DID_BUS_BUSY << 16); - break; + scmd->result = DID_TRANSPORT_DISRUPTED << 16; + goto out; } - case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: scmd->result = DID_RESET << 16; @@ -3304,8 +3395,10 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: case MPI2_IOCSTATUS_SUCCESS: scmd->result = (DID_OK << 16) | scsi_status; - if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | - MPI2_SCSI_STATE_NO_SCSI_STATUS)) + if (response_code == + MPI2_SCSITASKMGMT_RSP_INVALID_FRAME || + (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | + MPI2_SCSI_STATE_NO_SCSI_STATUS))) scmd->result = DID_SOFT_ERROR << 16; else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) scmd->result = DID_RESET << 16; @@ -3344,7 +3437,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) /** * _scsih_sas_host_refresh - refreshing sas host object contents * @ioc: per adapter object - * @update: update link information * Context: user * * During port enable, fw will send topology events for every device. Its @@ -3354,13 +3446,14 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) * Return nothing. */ static void -_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update) +_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc) { u16 sz; u16 ioc_status; int i; Mpi2ConfigReply_t mpi_reply; Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; + u16 attached_handle; dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "updating handles for sas_host(0x%016llx)\n", @@ -3374,27 +3467,24 @@ _scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update) ioc->name, __FILE__, __LINE__, __func__); return; } - if (!(mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, - sas_iounit_pg0, sz))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - goto out; - for (i = 0; i < ioc->sas_hba.num_phys ; i++) { - ioc->sas_hba.phy[i].handle = - le16_to_cpu(sas_iounit_pg0->PhyData[i]. - ControllerDevHandle); - if (update) - mpt2sas_transport_update_links( - ioc, - ioc->sas_hba.phy[i].handle, - le16_to_cpu(sas_iounit_pg0->PhyData[i]. - AttachedDevHandle), i, - sas_iounit_pg0->PhyData[i]. - NegotiatedLinkRate >> 4); - } - } + if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, + sas_iounit_pg0, sz)) != 0) + goto out; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + goto out; + for (i = 0; i < ioc->sas_hba.num_phys ; i++) { + if (i == 0) + ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> + PhyData[0].ControllerDevHandle); + ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; + attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i]. + AttachedDevHandle); + mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address, + attached_handle, i, sas_iounit_pg0->PhyData[i]. + NegotiatedLinkRate >> 4); + } out: kfree(sas_iounit_pg0); } @@ -3507,19 +3597,21 @@ _scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc) ioc->name, __FILE__, __LINE__, __func__); goto out; } - ioc->sas_hba.phy[i].handle = - le16_to_cpu(sas_iounit_pg0->PhyData[i].ControllerDevHandle); + + if (i == 0) + ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> + PhyData[0].ControllerDevHandle); + ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; ioc->sas_hba.phy[i].phy_id = i; mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i], phy_pg0, ioc->sas_hba.parent_dev); } if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.phy[0].handle))) { + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); goto out; } - ioc->sas_hba.handle = le16_to_cpu(sas_device_pg0.DevHandle); ioc->sas_hba.enclosure_handle = le16_to_cpu(sas_device_pg0.EnclosureHandle); ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress); @@ -3562,7 +3654,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) Mpi2SasEnclosurePage0_t enclosure_pg0; u32 ioc_status; u16 parent_handle; - __le64 sas_address; + __le64 sas_address, sas_address_parent = 0; int i; unsigned long flags; struct _sas_port *mpt2sas_port = NULL; @@ -3591,10 +3683,16 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) /* handle out of order topology events */ parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle); - if (parent_handle >= ioc->sas_hba.num_phys) { + if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent) + != 0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + if (sas_address_parent != ioc->sas_hba.sas_address) { spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, - parent_handle); + sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, + sas_address_parent); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (!sas_expander) { rc = _scsih_expander_add(ioc, parent_handle); @@ -3622,14 +3720,12 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) sas_expander->handle = handle; sas_expander->num_phys = expander_pg0.NumPhys; - sas_expander->parent_handle = parent_handle; - sas_expander->enclosure_handle = - le16_to_cpu(expander_pg0.EnclosureHandle); + sas_expander->sas_address_parent = sas_address_parent; sas_expander->sas_address = sas_address; printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x)," " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name, - handle, sas_expander->parent_handle, (unsigned long long) + handle, parent_handle, (unsigned long long) sas_expander->sas_address, sas_expander->num_phys); if (!sas_expander->num_phys) @@ -3645,7 +3741,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) INIT_LIST_HEAD(&sas_expander->sas_port_list); mpt2sas_port = mpt2sas_transport_port_add(ioc, handle, - sas_expander->parent_handle); + sas_address_parent); if (!mpt2sas_port) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); @@ -3691,20 +3787,54 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) if (mpt2sas_port) mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, - sas_expander->parent_handle); + sas_address_parent); kfree(sas_expander); return rc; } /** + * _scsih_done - scsih callback handler. + * @ioc: per adapter object + * @smid: system request message index + * @msix_index: MSIX table index supplied by the OS + * @reply: reply message frame(lower 32bit addr) + * + * Callback handler when sending internal generated message frames. + * The callback index passed is `ioc->scsih_cb_idx` + * + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. + */ +static u8 +_scsih_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) +{ + MPI2DefaultReply_t *mpi_reply; + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (ioc->scsih_cmds.status == MPT2_CMD_NOT_USED) + return 1; + if (ioc->scsih_cmds.smid != smid) + return 1; + ioc->scsih_cmds.status |= MPT2_CMD_COMPLETE; + if (mpi_reply) { + memcpy(ioc->scsih_cmds.reply, mpi_reply, + mpi_reply->MsgLength*4); + ioc->scsih_cmds.status |= MPT2_CMD_REPLY_VALID; + } + ioc->scsih_cmds.status &= ~MPT2_CMD_PENDING; + complete(&ioc->scsih_cmds.done); + return 1; +} + +/** * _scsih_expander_remove - removing expander object * @ioc: per adapter object - * @handle: expander handle + * @sas_address: expander sas_address * * Return nothing. */ static void -_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle) +_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) { struct _sas_node *sas_expander; unsigned long flags; @@ -3713,7 +3843,8 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle) return; spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, handle); + sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, + sas_address); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); _scsih_expander_node_remove(ioc, sas_expander); } @@ -3805,8 +3936,11 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) } sas_device->handle = handle; - sas_device->parent_handle = - le16_to_cpu(sas_device_pg0.ParentDevHandle); + if (_scsih_get_sas_address(ioc, le16_to_cpu + (sas_device_pg0.ParentDevHandle), + &sas_device->sas_address_parent) != 0) + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); sas_device->enclosure_handle = le16_to_cpu(sas_device_pg0.EnclosureHandle); sas_device->slot = @@ -3836,43 +3970,39 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) /** * _scsih_remove_device - removing sas device object * @ioc: per adapter object - * @handle: sas device handle + * @sas_device: the sas_device object * * Return nothing. */ static void -_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device + *sas_device) { struct MPT2SAS_TARGET *sas_target_priv_data; - struct _sas_device *sas_device; - unsigned long flags; Mpi2SasIoUnitControlReply_t mpi_reply; Mpi2SasIoUnitControlRequest_t mpi_request; - u16 device_handle; + u16 device_handle, handle; - /* lookup sas_device */ - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!sas_device) return; - } - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle" - "(0x%04x)\n", ioc->name, __func__, handle)); + handle = sas_device->handle; + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x)," + " sas_addr(0x%016llx)\n", ioc->name, __func__, handle, + (unsigned long long) sas_device->sas_address)); if (sas_device->starget && sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; sas_target_priv_data->deleted = 1; } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (ioc->remove_host) + if (ioc->remove_host || ioc->shost_recovery || !handle) goto out; if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) { dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " - "target_reset handle(0x%04x)\n", ioc->name, handle)); + "target_reset handle(0x%04x)\n", ioc->name, + handle)); goto skip_tr; } @@ -3925,10 +4055,10 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) _scsih_ublock_io_device(ioc, handle); mpt2sas_transport_port_remove(ioc, sas_device->sas_address, - sas_device->parent_handle); + sas_device->sas_address_parent); printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" - "(0x%016llx)\n", ioc->name, sas_device->handle, + "(0x%016llx)\n", ioc->name, handle, (unsigned long long) sas_device->sas_address); _scsih_sas_device_remove(ioc, sas_device); @@ -3952,7 +4082,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, u16 reason_code; u8 phy_number; char *status_str = NULL; - char link_rate[25]; + u8 link_rate, prev_link_rate; switch (event_data->ExpStatus) { case MPI2_EVENT_SAS_TOPO_ES_ADDED: @@ -3962,6 +4092,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, status_str = "remove"; break; case MPI2_EVENT_SAS_TOPO_ES_RESPONDING: + case 0: status_str = "responding"; break; case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING: @@ -3987,30 +4118,30 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, MPI2_EVENT_SAS_TOPO_RC_MASK; switch (reason_code) { case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: - snprintf(link_rate, 25, ": add, link(0x%02x)", - (event_data->PHY[i].LinkRate >> 4)); - status_str = link_rate; + status_str = "target add"; break; case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: - status_str = ": remove"; + status_str = "target remove"; break; case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING: - status_str = ": remove_delay"; + status_str = "delay target remove"; break; case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: - snprintf(link_rate, 25, ": link(0x%02x)", - (event_data->PHY[i].LinkRate >> 4)); - status_str = link_rate; + status_str = "link rate change"; break; case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE: - status_str = ": responding"; + status_str = "target responding"; break; default: - status_str = ": unknown"; + status_str = "unknown"; break; } - printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x)%s\n", - phy_number, handle, status_str); + link_rate = event_data->PHY[i].LinkRate >> 4; + prev_link_rate = event_data->PHY[i].LinkRate & 0xF; + printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x): %s:" + " link rate: new(0x%02x), old(0x%02x)\n", phy_number, + handle, status_str, link_rate, prev_link_rate); + } } #endif @@ -4031,8 +4162,10 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u16 reason_code; u8 phy_number; struct _sas_node *sas_expander; + struct _sas_device *sas_device; + u64 sas_address; unsigned long flags; - u8 link_rate_; + u8 link_rate, prev_link_rate; Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING @@ -4040,10 +4173,13 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, _scsih_sas_topology_change_event_debug(ioc, event_data); #endif + if (ioc->shost_recovery) + return; + if (!ioc->sas_hba.num_phys) _scsih_sas_host_add(ioc); else - _scsih_sas_host_refresh(ioc, 0); + _scsih_sas_host_refresh(ioc); if (fw_event->ignore) { dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring expander " @@ -4058,6 +4194,17 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, if (_scsih_expander_add(ioc, parent_handle) != 0) return; + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, + parent_handle); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + if (sas_expander) + sas_address = sas_expander->sas_address; + else if (parent_handle < ioc->sas_hba.num_phys) + sas_address = ioc->sas_hba.sas_address; + else + return; + /* handle siblings events */ for (i = 0; i < event_data->NumEntries; i++) { if (fw_event->ignore) { @@ -4077,48 +4224,47 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); if (!handle) continue; - link_rate_ = event_data->PHY[i].LinkRate >> 4; + link_rate = event_data->PHY[i].LinkRate >> 4; + prev_link_rate = event_data->PHY[i].LinkRate & 0xF; switch (reason_code) { case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: + + if (link_rate == prev_link_rate) + break; + + mpt2sas_transport_update_links(ioc, sas_address, + handle, phy_number, link_rate); + + if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5) + _scsih_ublock_io_device(ioc, handle); + break; case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: - if (!parent_handle) { - if (phy_number < ioc->sas_hba.num_phys) - mpt2sas_transport_update_links( - ioc, - ioc->sas_hba.phy[phy_number].handle, - handle, phy_number, link_rate_); - } else { - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_expander = - mpt2sas_scsih_expander_find_by_handle(ioc, - parent_handle); - spin_unlock_irqrestore(&ioc->sas_node_lock, - flags); - if (sas_expander) { - if (phy_number < sas_expander->num_phys) - mpt2sas_transport_update_links( - ioc, - sas_expander-> - phy[phy_number].handle, - handle, phy_number, - link_rate_); - } - } - if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) { - if (link_rate_ < MPI2_SAS_NEG_LINK_RATE_1_5) - break; - _scsih_add_device(ioc, handle, phy_number, 0); - } + + mpt2sas_transport_update_links(ioc, sas_address, + handle, phy_number, link_rate); + + _scsih_add_device(ioc, handle, phy_number, 0); break; case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: - _scsih_remove_device(ioc, handle); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, + handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, + flags); + break; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + _scsih_remove_device(ioc, sas_device); break; } } /* handle expander removal */ - if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) - _scsih_expander_remove(ioc, parent_handle); + if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING && + sas_expander) + _scsih_expander_remove(ioc, sas_address); } @@ -4170,6 +4316,12 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION: reason_str = "internal async notification"; break; + case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY: + reason_str = "expander reduced functionality"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY: + reason_str = "expander reduced functionality complete"; + break; default: reason_str = "unknown reason"; break; @@ -4197,11 +4349,43 @@ static void _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { + struct MPT2SAS_TARGET *target_priv_data; + struct _sas_device *sas_device; + __le64 sas_address; + unsigned long flags; + Mpi2EventDataSasDeviceStatusChange_t *event_data = + fw_event->event_data; + #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) _scsih_sas_device_status_change_event_debug(ioc, - fw_event->event_data); + event_data); #endif + + if (!(event_data->ReasonCode == + MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && + event_data->ReasonCode == + MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)) + return; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_address = le64_to_cpu(event_data->SASAddress); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (!sas_device || !sas_device->starget) + return; + + target_priv_data = sas_device->starget->hostdata; + if (!target_priv_data) + return; + + if (event_data->ReasonCode == + MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET) + target_priv_data->tm_busy = 1; + else + target_priv_data->tm_busy = 0; } #ifdef CONFIG_SCSI_MPT2SAS_LOGGING @@ -4281,6 +4465,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, #ifdef CONFIG_SCSI_MPT2SAS_LOGGING Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data; #endif + u16 ioc_status; dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: " "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum, event_data->PortWidth)); @@ -4314,8 +4499,9 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, mpt2sas_scsih_issue_tm(ioc, handle, lun, MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30); ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - - if ((mpi_reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) && + ioc_status = le16_to_cpu(mpi_reply->IOCStatus) + & MPI2_IOCSTATUS_MASK; + if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) && (mpi_reply->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED || mpi_reply->ResponseCode == @@ -4570,7 +4756,7 @@ _scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc, spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!sas_device) return; - _scsih_remove_device(ioc, handle); + _scsih_remove_device(ioc, sas_device); } /** @@ -4591,6 +4777,8 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; u32 ioc_status; + u64 sas_address; + u16 parent_handle; spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); @@ -4615,9 +4803,10 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc, return; } - mpt2sas_transport_update_links(ioc, - le16_to_cpu(sas_device_pg0.ParentDevHandle), - handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); + if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) + mpt2sas_transport_update_links(ioc, sas_address, handle, + sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); _scsih_add_device(ioc, handle, 0, 1); } @@ -4857,7 +5046,7 @@ static void _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { - u16 handle; + u16 handle, parent_handle; u32 state; struct _sas_device *sas_device; unsigned long flags; @@ -4865,6 +5054,7 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, Mpi2SasDevicePage0_t sas_device_pg0; u32 ioc_status; Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data; + u64 sas_address; if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) return; @@ -4906,9 +5096,10 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, return; } - mpt2sas_transport_update_links(ioc, - le16_to_cpu(sas_device_pg0.ParentDevHandle), - handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); + if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) + mpt2sas_transport_update_links(ioc, sas_address, handle, + sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); _scsih_add_device(ioc, handle, 0, 1); @@ -4948,11 +5139,17 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc, case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK: reason_str = "consistency check"; break; - default: - reason_str = "unknown reason"; + case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT: + reason_str = "background init"; + break; + case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT: + reason_str = "make data consistent"; break; } + if (!reason_str) + return; + printk(MPT2SAS_INFO_FMT "raid operational status: (%s)" "\thandle(0x%04x), percent complete(%d)\n", ioc->name, reason_str, @@ -5252,18 +5449,23 @@ _scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, { struct _sas_node *sas_expander; unsigned long flags; + int i; spin_lock_irqsave(&ioc->sas_node_lock, flags); list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { - if (sas_expander->sas_address == sas_address) { - sas_expander->responding = 1; - if (sas_expander->handle != handle) { - printk(KERN_INFO "old handle(0x%04x)\n", - sas_expander->handle); - sas_expander->handle = handle; - } + if (sas_expander->sas_address != sas_address) + continue; + sas_expander->responding = 1; + if (sas_expander->handle == handle) goto out; - } + printk(KERN_INFO "\texpander(0x%016llx): handle changed" + " from(0x%04x) to (0x%04x)!!!\n", + (unsigned long long)sas_expander->sas_address, + sas_expander->handle, handle); + sas_expander->handle = handle; + for (i = 0 ; i < sas_expander->num_phys ; i++) + sas_expander->phy[i].handle = handle; + goto out; } out: spin_unlock_irqrestore(&ioc->sas_node_lock, flags); @@ -5340,7 +5542,9 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) (unsigned long long) sas_device->enclosure_logical_id, sas_device->slot); - _scsih_remove_device(ioc, sas_device->handle); + /* invalidate the device handle */ + sas_device->handle = 0; + _scsih_remove_device(ioc, sas_device); } list_for_each_entry_safe(raid_device, raid_device_next, @@ -5366,7 +5570,7 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) sas_expander->responding = 0; continue; } - _scsih_expander_remove(ioc, sas_expander->handle); + _scsih_expander_remove(ioc, sas_expander->sas_address); goto retry_expander_search; } } @@ -5406,7 +5610,7 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) case MPT2_IOC_DONE_RESET: dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); - _scsih_sas_host_refresh(ioc, 0); + _scsih_sas_host_refresh(ioc); _scsih_search_responding_sas_devices(ioc); _scsih_search_responding_raid_devices(ioc); _scsih_search_responding_expanders(ioc); @@ -5646,7 +5850,7 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!sas_device) continue; - _scsih_remove_device(ioc, sas_device->handle); + _scsih_remove_device(ioc, sas_device); if (ioc->shost_recovery) return; goto retry_device_search; @@ -5669,7 +5873,8 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (!expander_sibling) continue; - _scsih_expander_remove(ioc, expander_sibling->handle); + _scsih_expander_remove(ioc, + expander_sibling->sas_address); if (ioc->shost_recovery) return; goto retry_expander_search; @@ -5677,7 +5882,7 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, } mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, - sas_expander->parent_handle); + sas_expander->sas_address_parent); printk(MPT2SAS_INFO_FMT "expander_remove: handle" "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, @@ -5690,9 +5895,99 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, } /** + * _scsih_ir_shutdown - IR shutdown notification + * @ioc: per adapter object + * + * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that + * the host system is shutting down. + * + * Return nothing. + */ +static void +_scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2RaidActionRequest_t *mpi_request; + Mpi2RaidActionReply_t *mpi_reply; + u16 smid; + + /* is IR firmware build loaded ? */ + if (!ioc->ir_firmware) + return; + + /* are there any volumes ? */ + if (list_empty(&ioc->raid_device_list)) + return; + + mutex_lock(&ioc->scsih_cmds.mutex); + + if (ioc->scsih_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: scsih_cmd in use\n", + ioc->name, __func__); + goto out; + } + ioc->scsih_cmds.status = MPT2_CMD_PENDING; + + smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + ioc->scsih_cmds.status = MPT2_CMD_NOT_USED; + goto out; + } + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->scsih_cmds.smid = smid; + memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t)); + + mpi_request->Function = MPI2_FUNCTION_RAID_ACTION; + mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED; + + printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name); + init_completion(&ioc->scsih_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); + wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); + + if (!(ioc->scsih_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + goto out; + } + + if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) { + mpi_reply = ioc->scsih_cmds.reply; + + printk(MPT2SAS_INFO_FMT "IR shutdown (complete): " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo)); + } + + out: + ioc->scsih_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->scsih_cmds.mutex); +} + +/** + * _scsih_shutdown - routine call during system shutdown + * @pdev: PCI device struct + * + * Return nothing. + */ +static void +_scsih_shutdown(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + _scsih_ir_shutdown(ioc); + mpt2sas_base_detach(ioc); +} + +/** * _scsih_remove - detach and remove add host * @pdev: PCI device struct * + * Routine called when unloading the driver. * Return nothing. */ static void __devexit @@ -5726,7 +6021,7 @@ _scsih_remove(struct pci_dev *pdev) mpt2sas_scsih_sas_device_find_by_sas_address(ioc, mpt2sas_port->remote_identify.sas_address); if (sas_device) { - _scsih_remove_device(ioc, sas_device->handle); + _scsih_remove_device(ioc, sas_device); goto retry_again; } } else { @@ -5735,7 +6030,7 @@ _scsih_remove(struct pci_dev *pdev) mpt2sas_port->remote_identify.sas_address); if (expander_sibling) { _scsih_expander_remove(ioc, - expander_sibling->handle); + expander_sibling->sas_address); goto retry_again; } } @@ -5749,7 +6044,7 @@ _scsih_remove(struct pci_dev *pdev) } sas_remove_host(shost); - mpt2sas_base_detach(ioc); + _scsih_shutdown(pdev); list_del(&ioc->list); scsi_remove_host(shost); scsi_host_put(shost); @@ -5770,7 +6065,8 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc) void *device; struct _sas_device *sas_device; struct _raid_device *raid_device; - u16 handle, parent_handle; + u16 handle; + u64 sas_address_parent; u64 sas_address; unsigned long flags; int rc; @@ -5799,17 +6095,17 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc) } else { sas_device = device; handle = sas_device->handle; - parent_handle = sas_device->parent_handle; + sas_address_parent = sas_device->sas_address_parent; sas_address = sas_device->sas_address; spin_lock_irqsave(&ioc->sas_device_lock, flags); list_move_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!mpt2sas_transport_port_add(ioc, sas_device->handle, - sas_device->parent_handle)) { + sas_device->sas_address_parent)) { _scsih_sas_device_remove(ioc, sas_device); } else if (!sas_device->starget) { mpt2sas_transport_port_remove(ioc, sas_address, - parent_handle); + sas_address_parent); _scsih_sas_device_remove(ioc, sas_device); } } @@ -5849,8 +6145,6 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc) { struct _sas_device *sas_device, *next; unsigned long flags; - u16 handle, parent_handle; - u64 sas_address; /* SAS Device List */ list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list, @@ -5859,14 +6153,13 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc) list_move_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - handle = sas_device->handle; - parent_handle = sas_device->parent_handle; - sas_address = sas_device->sas_address; - if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) { + if (!mpt2sas_transport_port_add(ioc, sas_device->handle, + sas_device->sas_address_parent)) { _scsih_sas_device_remove(ioc, sas_device); } else if (!sas_device->starget) { - mpt2sas_transport_port_remove(ioc, sas_address, - parent_handle); + mpt2sas_transport_port_remove(ioc, + sas_device->sas_address, + sas_device->sas_address_parent); _scsih_sas_device_remove(ioc, sas_device); } } @@ -5935,6 +6228,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->ctl_cb_idx = ctl_cb_idx; ioc->base_cb_idx = base_cb_idx; ioc->transport_cb_idx = transport_cb_idx; + ioc->scsih_cb_idx = scsih_cb_idx; ioc->config_cb_idx = config_cb_idx; ioc->tm_tr_cb_idx = tm_tr_cb_idx; ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; @@ -6072,6 +6366,7 @@ static struct pci_driver scsih_driver = { .id_table = scsih_pci_table, .probe = _scsih_probe, .remove = __devexit_p(_scsih_remove), + .shutdown = _scsih_shutdown, #ifdef CONFIG_PM .suspend = _scsih_suspend, .resume = _scsih_resume, @@ -6113,6 +6408,9 @@ _scsih_init(void) transport_cb_idx = mpt2sas_base_register_callback_handler( mpt2sas_transport_done); + /* scsih internal commands callback handler */ + scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done); + /* configuration page API internal commands callback handler */ config_cb_idx = mpt2sas_base_register_callback_handler( mpt2sas_config_done); @@ -6152,6 +6450,7 @@ _scsih_exit(void) mpt2sas_base_release_callback_handler(tm_cb_idx); mpt2sas_base_release_callback_handler(base_cb_idx); mpt2sas_base_release_callback_handler(transport_cb_idx); + mpt2sas_base_release_callback_handler(scsih_cb_idx); mpt2sas_base_release_callback_handler(config_cb_idx); mpt2sas_base_release_callback_handler(ctl_cb_idx); diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index eb98188c7f3f..3a82872bad44 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -59,24 +59,23 @@ #include "mpt2sas_base.h" /** - * _transport_sas_node_find_by_handle - sas node search + * _transport_sas_node_find_by_sas_address - sas node search * @ioc: per adapter object - * @handle: expander or hba handle (assigned by firmware) + * @sas_address: sas address of expander or sas host * Context: Calling function should acquire ioc->sas_node_lock. * * Search for either hba phys or expander device based on handle, then returns * the sas_node object. */ static struct _sas_node * -_transport_sas_node_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +_transport_sas_node_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, + u64 sas_address) { - int i; - - for (i = 0; i < ioc->sas_hba.num_phys; i++) - if (ioc->sas_hba.phy[i].handle == handle) - return &ioc->sas_hba; - - return mpt2sas_scsih_expander_find_by_handle(ioc, handle); + if (ioc->sas_hba.sas_address == sas_address) + return &ioc->sas_hba; + else + return mpt2sas_scsih_expander_find_by_sas_address(ioc, + sas_address); } /** @@ -259,8 +258,7 @@ struct rep_manu_reply{ u8 response_length; u16 expander_change_count; u8 reserved0[2]; - u8 sas_format:1; - u8 reserved1:7; + u8 sas_format; u8 reserved2[3]; u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN]; u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN]; @@ -375,7 +373,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, mpi_request->VP_ID = 0; sas_address_le = (u64 *)&mpi_request->SASAddress; *sas_address_le = cpu_to_le64(sas_address); - mpi_request->RequestDataLength = sizeof(struct rep_manu_request); + mpi_request->RequestDataLength = + cpu_to_le16(sizeof(struct rep_manu_request)); psge = &mpi_request->SGL; /* WRITE sgel first */ @@ -438,8 +437,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, SAS_EXPANDER_PRODUCT_ID_LEN); strncpy(edev->product_rev, manufacture_reply->product_rev, SAS_EXPANDER_PRODUCT_REV_LEN); - edev->level = manufacture_reply->sas_format; - if (manufacture_reply->sas_format) { + edev->level = manufacture_reply->sas_format & 1; + if (edev->level) { strncpy(edev->component_vendor_id, manufacture_reply->component_vendor_id, SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); @@ -469,7 +468,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, * mpt2sas_transport_port_add - insert port to the list * @ioc: per adapter object * @handle: handle of attached device - * @parent_handle: parent handle(either hba or expander) + * @sas_address: sas address of parent expander or sas host * Context: This function will acquire ioc->sas_node_lock. * * Adding new port object to the sas_node->sas_port_list. @@ -478,7 +477,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, */ struct _sas_port * mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, - u16 parent_handle) + u64 sas_address) { struct _sas_phy *mpt2sas_phy, *next; struct _sas_port *mpt2sas_port; @@ -488,9 +487,6 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, int i; struct sas_port *port; - if (!parent_handle) - return NULL; - mpt2sas_port = kzalloc(sizeof(struct _sas_port), GFP_KERNEL); if (!mpt2sas_port) { @@ -502,17 +498,16 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, INIT_LIST_HEAD(&mpt2sas_port->port_list); INIT_LIST_HEAD(&mpt2sas_port->phy_list); spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle); + sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (!sas_node) { - printk(MPT2SAS_ERR_FMT "%s: Could not find parent(0x%04x)!\n", - ioc->name, __func__, parent_handle); + printk(MPT2SAS_ERR_FMT "%s: Could not find " + "parent sas_address(0x%016llx)!\n", ioc->name, + __func__, (unsigned long long)sas_address); goto out_fail; } - mpt2sas_port->handle = parent_handle; - mpt2sas_port->sas_address = sas_node->sas_address; if ((_transport_set_identify(ioc, handle, &mpt2sas_port->remote_identify))) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", @@ -604,7 +599,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, * mpt2sas_transport_port_remove - remove port from the list * @ioc: per adapter object * @sas_address: sas address of attached device - * @parent_handle: handle to the upstream parent(either hba or expander) + * @sas_address_parent: sas address of parent expander or sas host * Context: This function will acquire ioc->sas_node_lock. * * Removing object and freeing associated memory from the @@ -614,7 +609,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, */ void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, - u16 parent_handle) + u64 sas_address_parent) { int i; unsigned long flags; @@ -624,7 +619,8 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, struct _sas_phy *mpt2sas_phy, *next_phy; spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle); + sas_node = _transport_sas_node_find_by_sas_address(ioc, + sas_address_parent); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (!sas_node) return; @@ -650,8 +646,7 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, &mpt2sas_port->phy_list, port_siblings) { if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) dev_printk(KERN_INFO, &mpt2sas_port->port->dev, - "remove: parent_handle(0x%04x), " - "sas_addr(0x%016llx), phy(%d)\n", parent_handle, + "remove: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long) mpt2sas_port->remote_identify.sas_address, mpt2sas_phy->phy_id); @@ -799,8 +794,8 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy /** * mpt2sas_transport_update_links - refreshing phy link changes * @ioc: per adapter object - * @handle: handle to sas_host or expander - * @attached_handle: attached device handle + * @sas_address: sas address of parent expander or sas host + * @handle: attached device handle * @phy_numberv: phy number * @link_rate: new link rate * @@ -808,28 +803,25 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy */ void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, - u16 handle, u16 attached_handle, u8 phy_number, u8 link_rate) + u64 sas_address, u16 handle, u8 phy_number, u8 link_rate) { unsigned long flags; struct _sas_node *sas_node; struct _sas_phy *mpt2sas_phy; - if (ioc->shost_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); + if (ioc->shost_recovery) return; - } spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_node = _transport_sas_node_find_by_handle(ioc, handle); + sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (!sas_node) return; mpt2sas_phy = &sas_node->phy[phy_number]; - mpt2sas_phy->attached_handle = attached_handle; - if (attached_handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) - _transport_set_identify(ioc, mpt2sas_phy->attached_handle, + mpt2sas_phy->attached_handle = handle; + if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) + _transport_set_identify(ioc, handle, &mpt2sas_phy->remote_identify); else memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct @@ -841,13 +833,11 @@ mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, - "refresh: handle(0x%04x), sas_addr(0x%016llx),\n" + "refresh: parent sas_addr(0x%016llx),\n" "\tlink_rate(0x%02x), phy(%d)\n" "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", - handle, (unsigned long long) - mpt2sas_phy->identify.sas_address, link_rate, - phy_number, attached_handle, - (unsigned long long) + (unsigned long long)sas_address, + link_rate, phy_number, handle, (unsigned long long) mpt2sas_phy->remote_identify.sas_address); } @@ -1126,7 +1116,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); if (!dma_addr_out) { - mpt2sas_base_free_smid(ioc, le16_to_cpu(smid)); + mpt2sas_base_free_smid(ioc, smid); goto unmap; } @@ -1144,7 +1134,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); if (!dma_addr_in) { - mpt2sas_base_free_smid(ioc, le16_to_cpu(smid)); + mpt2sas_base_free_smid(ioc, smid); goto unmap; } diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index e3c482aa87b5..a2d569828308 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -6495,7 +6495,7 @@ static void ncr_int_ma (struct ncb *np) ** we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids ** bloat for such a should_not_happen situation). ** In all other situation, we reset the BUS. - ** Are these assumptions reasonnable ? (Wait and see ...) + ** Are these assumptions reasonable ? (Wait and see ...) */ unexpected_phase: dsp -= 8; diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c index 2be7d5b018d2..2c98a6ee973b 100644 --- a/drivers/scsi/nsp32.c +++ b/drivers/scsi/nsp32.c @@ -1419,7 +1419,7 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id) nsp32_msg(KERN_ERR, "Received unexpected BMCNTERR IRQ! "); /* * TODO: To be implemented improving bus master - * transfer reliablity when BMCNTERR is occurred in + * transfer reliability when BMCNTERR is occurred in * AutoSCSI phase described in specification. */ } diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index 7a117c18114c..950202a70bcf 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c @@ -73,7 +73,8 @@ static const char *_osd_ver_desc(struct osd_request *or) #define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len) -static int _osd_print_system_info(struct osd_dev *od, void *caps) +static int _osd_get_print_system_info(struct osd_dev *od, + void *caps, struct osd_dev_info *odi) { struct osd_request *or; struct osd_attr get_attrs[] = { @@ -137,8 +138,12 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps) OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n", (char *)pFirst); - pFirst = get_attrs[a].val_ptr; - OSD_INFO("OSD_NAME [%s]\n", (char *)pFirst); + odi->osdname_len = get_attrs[a].len; + /* Avoid NULL for memcmp optimization 0-length is good enough */ + odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL); + if (odi->osdname_len) + memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len); + OSD_INFO("OSD_NAME [%s]\n", odi->osdname); a++; pFirst = get_attrs[a++].val_ptr; @@ -171,6 +176,14 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps) sid_dump, sizeof(sid_dump), true); OSD_INFO("OSD_SYSTEM_ID(%d)\n" " [%s]\n", len, sid_dump); + + if (unlikely(len > sizeof(odi->systemid))) { + OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). " + "device idetification might not work\n", len); + len = sizeof(odi->systemid); + } + odi->systemid_len = len; + memcpy(odi->systemid, get_attrs[a].val_ptr, len); a++; } out: @@ -178,16 +191,17 @@ out: return ret; } -int osd_auto_detect_ver(struct osd_dev *od, void *caps) +int osd_auto_detect_ver(struct osd_dev *od, + void *caps, struct osd_dev_info *odi) { int ret; /* Auto-detect the osd version */ - ret = _osd_print_system_info(od, caps); + ret = _osd_get_print_system_info(od, caps, odi); if (ret) { osd_dev_set_ver(od, OSD_VER1); OSD_DEBUG("converting to OSD1\n"); - ret = _osd_print_system_info(od, caps); + ret = _osd_get_print_system_info(od, caps, odi); } return ret; @@ -461,7 +475,8 @@ EXPORT_SYMBOL(osd_end_request); int osd_execute_request(struct osd_request *or) { - return blk_execute_rq(or->request->q, NULL, or->request, 0); + return or->async_error = + blk_execute_rq(or->request->q, NULL, or->request, 0); } EXPORT_SYMBOL(osd_execute_request); @@ -471,8 +486,12 @@ static void osd_request_async_done(struct request *req, int error) or->async_error = error; - if (error) - OSD_DEBUG("osd_request_async_done error recieved %d\n", error); + if (unlikely(error)) { + OSD_DEBUG("osd_request_async_done error recieved %d " + "errors 0x%x\n", error, req->errors); + if (!req->errors) /* don't miss out on this one */ + req->errors = error; + } if (or->async_done) or->async_done(or, or->async_private); @@ -1153,6 +1172,7 @@ int osd_req_decode_get_attr_list(struct osd_request *or, "c=%d r=%d n=%d\n", cur_bytes, returned_bytes, n); oa->val_ptr = NULL; + cur_bytes = returned_bytes; /* break the caller loop */ break; } @@ -1436,6 +1456,15 @@ int osd_finalize_request(struct osd_request *or, } EXPORT_SYMBOL(osd_finalize_request); +static bool _is_osd_security_code(int code) +{ + return (code == osd_security_audit_value_frozen) || + (code == osd_security_working_key_frozen) || + (code == osd_nonce_not_unique) || + (code == osd_nonce_timestamp_out_of_range) || + (code == osd_invalid_dataout_buffer_integrity_check_value); +} + #define OSD_SENSE_PRINT1(fmt, a...) \ do { \ if (__cur_sense_need_output) \ @@ -1458,9 +1487,16 @@ int osd_req_decode_sense_full(struct osd_request *or, #else bool __cur_sense_need_output = !silent; #endif + int ret; - if (!or->request->errors) + if (likely(!or->request->errors)) { + osi->out_resid = 0; + osi->in_resid = 0; return 0; + } + + osi = osi ? : &local_osi; + memset(osi, 0, sizeof(*osi)); ssdb = or->request->sense; sense_len = or->request->sense_len; @@ -1468,17 +1504,15 @@ int osd_req_decode_sense_full(struct osd_request *or, OSD_ERR("Block-layer returned error(0x%x) but " "sense_len(%u) || key(%d) is empty\n", or->request->errors, sense_len, ssdb->sense_key); - return -EIO; + goto analyze; } if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) { OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n", ssdb->response_code, sense_len); - return -EIO; + goto analyze; } - osi = osi ? : &local_osi; - memset(osi, 0, sizeof(*osi)); osi->key = ssdb->sense_key; osi->additional_code = be16_to_cpu(ssdb->additional_sense_code); original_sense_len = ssdb->additional_sense_length + 8; @@ -1488,9 +1522,10 @@ int osd_req_decode_sense_full(struct osd_request *or, __cur_sense_need_output = (osi->key > scsi_sk_recovered_error); #endif OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) " - "additional_code=0x%x\n", + "additional_code=0x%x async_error=%d errors=0x%x\n", osi->key, original_sense_len, sense_len, - osi->additional_code); + osi->additional_code, or->async_error, + or->request->errors); if (original_sense_len < sense_len) sense_len = original_sense_len; @@ -1569,15 +1604,14 @@ int osd_req_decode_sense_full(struct osd_request *or, { struct osd_sense_attributes_data_descriptor *osadd = cur_descriptor; - int len = min(cur_len, sense_len); - int i = 0; + unsigned len = min(cur_len, sense_len); struct osd_sense_attr *pattr = osadd->sense_attrs; - while (len < 0) { + while (len >= sizeof(*pattr)) { u32 attr_page = be32_to_cpu(pattr->attr_page); u32 attr_id = be32_to_cpu(pattr->attr_id); - if (i++ == 0) { + if (!osi->attr.attr_page) { osi->attr.attr_page = attr_page; osi->attr.attr_id = attr_id; } @@ -1588,6 +1622,8 @@ int osd_req_decode_sense_full(struct osd_request *or, bad_attr_list++; max_attr--; } + + len -= sizeof(*pattr); OSD_SENSE_PRINT2( "osd_sense_attribute_identification" "attr_page=0x%x attr_id=0x%x\n", @@ -1621,7 +1657,50 @@ int osd_req_decode_sense_full(struct osd_request *or, cur_descriptor += cur_len; } - return (osi->key > scsi_sk_recovered_error) ? -EIO : 0; +analyze: + if (!osi->key) { + /* scsi sense is Empty, the request was never issued to target + * linux return code might tell us what happened. + */ + if (or->async_error == -ENOMEM) + osi->osd_err_pri = OSD_ERR_PRI_RESOURCE; + else + osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE; + ret = or->async_error; + } else if (osi->key <= scsi_sk_recovered_error) { + osi->osd_err_pri = 0; + ret = 0; + } else if (osi->additional_code == scsi_invalid_field_in_cdb) { + if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) { + osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES; + ret = -EFAULT; /* caller should recover from this */ + } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) { + osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND; + ret = -ENOENT; + } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) { + osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS; + ret = -EACCES; + } else { + osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED; + ret = -EINVAL; + } + } else if (osi->additional_code == osd_quota_error) { + osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE; + ret = -ENOSPC; + } else if (_is_osd_security_code(osi->additional_code)) { + osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED; + ret = -EINVAL; + } else { + osi->osd_err_pri = OSD_ERR_PRI_EIO; + ret = -EIO; + } + + if (or->out.req) + osi->out_resid = or->out.req->resid_len ?: or->out.total_bytes; + if (or->in.req) + osi->in_resid = or->in.req->resid_len ?: or->in.total_bytes; + + return ret; } EXPORT_SYMBOL(osd_req_decode_sense_full); diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index 0bdef3390902..0a90702b3d71 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c @@ -71,8 +71,7 @@ #define SCSI_OSD_MAX_MINOR 64 static const char osd_name[] = "osd"; -static const char *osd_version_string = "open-osd 0.1.0"; -const char osd_symlink[] = "scsi_osd"; +static const char *osd_version_string = "open-osd 0.2.0"; MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); @@ -82,15 +81,25 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD); struct osd_uld_device { int minor; - struct kref kref; + struct device class_dev; struct cdev cdev; struct osd_dev od; + struct osd_dev_info odi; struct gendisk *disk; - struct device *class_member; }; -static void __uld_get(struct osd_uld_device *oud); -static void __uld_put(struct osd_uld_device *oud); +struct osd_dev_handle { + struct osd_dev od; + struct file *file; + struct osd_uld_device *oud; +} ; + +static DEFINE_IDA(osd_minor_ida); + +static struct class osd_uld_class = { + .owner = THIS_MODULE, + .name = "scsi_osd", +}; /* * Char Device operations @@ -101,7 +110,7 @@ static int osd_uld_open(struct inode *inode, struct file *file) struct osd_uld_device *oud = container_of(inode->i_cdev, struct osd_uld_device, cdev); - __uld_get(oud); + get_device(&oud->class_dev); /* cache osd_uld_device on file handle */ file->private_data = oud; OSD_DEBUG("osd_uld_open %p\n", oud); @@ -114,7 +123,7 @@ static int osd_uld_release(struct inode *inode, struct file *file) OSD_DEBUG("osd_uld_release %p\n", file->private_data); file->private_data = NULL; - __uld_put(oud); + put_device(&oud->class_dev); return 0; } @@ -177,7 +186,7 @@ static const struct file_operations osd_fops = { struct osd_dev *osduld_path_lookup(const char *name) { struct osd_uld_device *oud; - struct osd_dev *od; + struct osd_dev_handle *odh; struct file *file; int error; @@ -186,8 +195,8 @@ struct osd_dev *osduld_path_lookup(const char *name) return ERR_PTR(-EINVAL); } - od = kzalloc(sizeof(*od), GFP_KERNEL); - if (!od) + odh = kzalloc(sizeof(*odh), GFP_KERNEL); + if (unlikely(!odh)) return ERR_PTR(-ENOMEM); file = filp_open(name, O_RDWR, 0); @@ -203,33 +212,134 @@ struct osd_dev *osduld_path_lookup(const char *name) oud = file->private_data; - *od = oud->od; - od->file = file; + odh->od = oud->od; + odh->file = file; + odh->oud = oud; - return od; + return &odh->od; close_file: fput(file); free_od: - kfree(od); + kfree(odh); return ERR_PTR(error); } EXPORT_SYMBOL(osduld_path_lookup); -void osduld_put_device(struct osd_dev *od) +static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len, + const u8 *a2, unsigned a2_len) { + if (!a2_len) /* User string is Empty means don't care */ + return true; + + if (a1_len != a2_len) + return false; + + return 0 == memcmp(a1, a2, a1_len); +} + +struct find_oud_t { + const struct osd_dev_info *odi; + struct device *dev; + struct osd_uld_device *oud; +} ; + +int _mach_odi(struct device *dev, void *find_data) +{ + struct osd_uld_device *oud = container_of(dev, struct osd_uld_device, + class_dev); + struct find_oud_t *fot = find_data; + const struct osd_dev_info *odi = fot->odi; + + if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len, + odi->systemid, odi->systemid_len) && + _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len, + odi->osdname, odi->osdname_len)) { + OSD_DEBUG("found device sysid_len=%d osdname=%d\n", + odi->systemid_len, odi->osdname_len); + fot->oud = oud; + return 1; + } else { + return 0; + } +} + +/* osduld_info_lookup - Loop through all devices, return the requested osd_dev. + * + * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't + * care. .e.g if they're both zero /dev/osd0 is returned. + */ +struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi) +{ + struct find_oud_t find = {.odi = odi}; + + find.dev = class_find_device(&osd_uld_class, NULL, &find, _mach_odi); + if (likely(find.dev)) { + struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL); + + if (unlikely(!odh)) { + put_device(find.dev); + return ERR_PTR(-ENOMEM); + } + odh->od = find.oud->od; + odh->oud = find.oud; + + return &odh->od; + } + + return ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL(osduld_info_lookup); + +void osduld_put_device(struct osd_dev *od) +{ if (od && !IS_ERR(od)) { - struct osd_uld_device *oud = od->file->private_data; + struct osd_dev_handle *odh = + container_of(od, struct osd_dev_handle, od); + struct osd_uld_device *oud = odh->oud; BUG_ON(od->scsi_device != oud->od.scsi_device); - fput(od->file); - kfree(od); + /* If scsi has released the device (logout), and exofs has last + * reference on oud it will be freed by above osd_uld_release + * within fput below. But this will oops in cdev_release which + * is called after the fops->release. A get_/put_ pair makes + * sure we have a cdev for the duration of fput + */ + if (odh->file) { + get_device(&oud->class_dev); + fput(odh->file); + } + put_device(&oud->class_dev); + kfree(odh); } } EXPORT_SYMBOL(osduld_put_device); +const struct osd_dev_info *osduld_device_info(struct osd_dev *od) +{ + struct osd_dev_handle *odh = + container_of(od, struct osd_dev_handle, od); + return &odh->oud->odi; +} +EXPORT_SYMBOL(osduld_device_info); + +bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi) +{ + struct osd_dev_handle *odh = + container_of(od, struct osd_dev_handle, od); + struct osd_uld_device *oud = odh->oud; + + return (oud->odi.systemid_len == odi->systemid_len) && + _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len, + odi->systemid, odi->systemid_len) && + (oud->odi.osdname_len == odi->osdname_len) && + _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len, + odi->osdname, odi->osdname_len); +} +EXPORT_SYMBOL(osduld_device_same); + /* * Scsi Device operations */ @@ -250,14 +360,35 @@ static int __detect_osd(struct osd_uld_device *oud) OSD_ERR("warning: scsi_test_unit_ready failed\n"); osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true); - if (osd_auto_detect_ver(&oud->od, caps)) + if (osd_auto_detect_ver(&oud->od, caps, &oud->odi)) return -ENODEV; return 0; } -static struct class *osd_sysfs_class; -static DEFINE_IDA(osd_minor_ida); +static void __remove(struct device *dev) +{ + struct osd_uld_device *oud = container_of(dev, struct osd_uld_device, + class_dev); + struct scsi_device *scsi_device = oud->od.scsi_device; + + kfree(oud->odi.osdname); + + if (oud->cdev.owner) + cdev_del(&oud->cdev); + + osd_dev_fini(&oud->od); + scsi_device_put(scsi_device); + + OSD_INFO("osd_remove %s\n", + oud->disk ? oud->disk->disk_name : NULL); + + if (oud->disk) + put_disk(oud->disk); + ida_remove(&osd_minor_ida, oud->minor); + + kfree(oud); +} static int osd_probe(struct device *dev) { @@ -289,7 +420,6 @@ static int osd_probe(struct device *dev) if (NULL == oud) goto err_retract_minor; - kref_init(&oud->kref); dev_set_drvdata(dev, oud); oud->minor = minor; @@ -327,18 +457,25 @@ static int osd_probe(struct device *dev) OSD_ERR("cdev_add failed\n"); goto err_put_disk; } - kobject_get(&oud->cdev.kobj); /* 2nd ref see osd_remove() */ - - /* class_member */ - oud->class_member = device_create(osd_sysfs_class, dev, - MKDEV(SCSI_OSD_MAJOR, oud->minor), "%s", disk->disk_name); - if (IS_ERR(oud->class_member)) { - OSD_ERR("class_device_create failed\n"); - error = PTR_ERR(oud->class_member); + + /* class device member */ + oud->class_dev.devt = oud->cdev.dev; + oud->class_dev.class = &osd_uld_class; + oud->class_dev.parent = dev; + oud->class_dev.release = __remove; + error = dev_set_name(&oud->class_dev, disk->disk_name); + if (error) { + OSD_ERR("dev_set_name failed => %d\n", error); + goto err_put_cdev; + } + + error = device_register(&oud->class_dev); + if (error) { + OSD_ERR("device_register failed => %d\n", error); goto err_put_cdev; } - dev_set_drvdata(oud->class_member, oud); + get_device(&oud->class_dev); OSD_INFO("osd_probe %s\n", disk->disk_name); return 0; @@ -367,54 +504,12 @@ static int osd_remove(struct device *dev) scsi_device); } - if (oud->class_member) - device_destroy(osd_sysfs_class, - MKDEV(SCSI_OSD_MAJOR, oud->minor)); + device_unregister(&oud->class_dev); - /* We have 2 references to the cdev. One is released here - * and also takes down the /dev/osdX mapping. The second - * Will be released in __remove() after all users have released - * the osd_uld_device. - */ - if (oud->cdev.owner) - cdev_del(&oud->cdev); - - __uld_put(oud); + put_device(&oud->class_dev); return 0; } -static void __remove(struct kref *kref) -{ - struct osd_uld_device *oud = container_of(kref, - struct osd_uld_device, kref); - struct scsi_device *scsi_device = oud->od.scsi_device; - - /* now let delete the char_dev */ - kobject_put(&oud->cdev.kobj); - - osd_dev_fini(&oud->od); - scsi_device_put(scsi_device); - - OSD_INFO("osd_remove %s\n", - oud->disk ? oud->disk->disk_name : NULL); - - if (oud->disk) - put_disk(oud->disk); - - ida_remove(&osd_minor_ida, oud->minor); - kfree(oud); -} - -static void __uld_get(struct osd_uld_device *oud) -{ - kref_get(&oud->kref); -} - -static void __uld_put(struct osd_uld_device *oud) -{ - kref_put(&oud->kref, __remove); -} - /* * Global driver and scsi registration */ @@ -432,11 +527,10 @@ static int __init osd_uld_init(void) { int err; - osd_sysfs_class = class_create(THIS_MODULE, osd_symlink); - if (IS_ERR(osd_sysfs_class)) { - OSD_ERR("Unable to register sysfs class => %ld\n", - PTR_ERR(osd_sysfs_class)); - return PTR_ERR(osd_sysfs_class); + err = class_register(&osd_uld_class); + if (err) { + OSD_ERR("Unable to register sysfs class => %d\n", err); + return err; } err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), @@ -459,7 +553,7 @@ static int __init osd_uld_init(void) err_out_chrdev: unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); err_out: - class_destroy(osd_sysfs_class); + class_unregister(&osd_uld_class); return err; } @@ -467,7 +561,7 @@ static void __exit osd_uld_exit(void) { scsi_unregister_driver(&osd_driver.gendrv); unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); - class_destroy(osd_sysfs_class); + class_unregister(&osd_uld_class); OSD_INFO("UNLOADED %s\n", osd_version_string); } diff --git a/drivers/scsi/pm8001/Makefile b/drivers/scsi/pm8001/Makefile new file mode 100644 index 000000000000..52f04296171c --- /dev/null +++ b/drivers/scsi/pm8001/Makefile @@ -0,0 +1,12 @@ +# +# Kernel configuration file for the PM8001 SAS/SATA 8x6G based HBA driver +# +# Copyright (C) 2008-2009 USI Co., Ltd. + + +obj-$(CONFIG_SCSI_PM8001) += pm8001.o +pm8001-y += pm8001_init.o \ + pm8001_sas.o \ + pm8001_ctl.o \ + pm8001_hwi.o + diff --git a/drivers/scsi/pm8001/pm8001_chips.h b/drivers/scsi/pm8001/pm8001_chips.h new file mode 100644 index 000000000000..4efa4d0950e5 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_chips.h @@ -0,0 +1,89 @@ +/* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifndef _PM8001_CHIPS_H_ +#define _PM8001_CHIPS_H_ + +static inline u32 pm8001_read_32(void *virt_addr) +{ + return *((u32 *)virt_addr); +} + +static inline void pm8001_write_32(void *addr, u32 offset, u32 val) +{ + *((u32 *)(addr + offset)) = val; +} + +static inline u32 pm8001_cr32(struct pm8001_hba_info *pm8001_ha, u32 bar, + u32 offset) +{ + return readl(pm8001_ha->io_mem[bar].memvirtaddr + offset); +} + +static inline void pm8001_cw32(struct pm8001_hba_info *pm8001_ha, u32 bar, + u32 addr, u32 val) +{ + writel(val, pm8001_ha->io_mem[bar].memvirtaddr + addr); +} +static inline u32 pm8001_mr32(void __iomem *addr, u32 offset) +{ + return readl(addr + offset); +} +static inline void pm8001_mw32(void __iomem *addr, u32 offset, u32 val) +{ + writel(val, addr + offset); +} +static inline u32 get_pci_bar_index(u32 pcibar) +{ + switch (pcibar) { + case 0x18: + case 0x1C: + return 1; + case 0x20: + return 2; + case 0x24: + return 3; + default: + return 0; + } +} + +#endif /* _PM8001_CHIPS_H_ */ + diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c new file mode 100644 index 000000000000..14b13acae6dd --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -0,0 +1,573 @@ +/* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ +#include <linux/firmware.h> +#include "pm8001_sas.h" +#include "pm8001_ctl.h" + +/* scsi host attributes */ + +/** + * pm8001_ctl_mpi_interface_rev_show - MPI interface revision number + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_mpi_interface_rev_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%d\n", + pm8001_ha->main_cfg_tbl.interface_rev); +} +static +DEVICE_ATTR(interface_rev, S_IRUGO, pm8001_ctl_mpi_interface_rev_show, NULL); + +/** + * pm8001_ctl_fw_version_show - firmware version + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_fw_version_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n", + (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 24), + (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 16), + (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 8), + (u8)(pm8001_ha->main_cfg_tbl.firmware_rev)); +} +static DEVICE_ATTR(fw_version, S_IRUGO, pm8001_ctl_fw_version_show, NULL); +/** + * pm8001_ctl_max_out_io_show - max outstanding io supported + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_max_out_io_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%d\n", + pm8001_ha->main_cfg_tbl.max_out_io); +} +static DEVICE_ATTR(max_out_io, S_IRUGO, pm8001_ctl_max_out_io_show, NULL); +/** + * pm8001_ctl_max_devices_show - max devices support + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_max_devices_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%04d\n", + (u16)(pm8001_ha->main_cfg_tbl.max_sgl >> 16)); +} +static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL); +/** + * pm8001_ctl_max_sg_list_show - max sg list supported iff not 0.0 for no + * hardware limitation + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_max_sg_list_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%04d\n", + pm8001_ha->main_cfg_tbl.max_sgl & 0x0000FFFF); +} +static DEVICE_ATTR(max_sg_list, S_IRUGO, pm8001_ctl_max_sg_list_show, NULL); + +#define SAS_1_0 0x1 +#define SAS_1_1 0x2 +#define SAS_2_0 0x4 + +static ssize_t +show_sas_spec_support_status(unsigned int mode, char *buf) +{ + ssize_t len = 0; + + if (mode & SAS_1_1) + len = sprintf(buf, "%s", "SAS1.1"); + if (mode & SAS_2_0) + len += sprintf(buf + len, "%s%s", len ? ", " : "", "SAS2.0"); + len += sprintf(buf + len, "\n"); + + return len; +} + +/** + * pm8001_ctl_sas_spec_support_show - sas spec supported + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_sas_spec_support_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + unsigned int mode; + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + mode = (pm8001_ha->main_cfg_tbl.ctrl_cap_flag & 0xfe000000)>>25; + return show_sas_spec_support_status(mode, buf); +} +static DEVICE_ATTR(sas_spec_support, S_IRUGO, + pm8001_ctl_sas_spec_support_show, NULL); + +/** + * pm8001_ctl_sas_address_show - sas address + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * This is the controller sas address + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + return snprintf(buf, PAGE_SIZE, "0x%016llx\n", + be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr)); +} +static DEVICE_ATTR(host_sas_address, S_IRUGO, + pm8001_ctl_host_sas_address_show, NULL); + +/** + * pm8001_ctl_logging_level_show - logging level + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read/write' shost attribute. + */ +static ssize_t pm8001_ctl_logging_level_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%08xh\n", pm8001_ha->logging_level); +} +static ssize_t pm8001_ctl_logging_level_store(struct device *cdev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + int val = 0; + + if (sscanf(buf, "%x", &val) != 1) + return -EINVAL; + + pm8001_ha->logging_level = val; + return strlen(buf); +} + +static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, + pm8001_ctl_logging_level_show, pm8001_ctl_logging_level_store); +/** + * pm8001_ctl_aap_log_show - aap1 event log + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_aap_log_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + int i; +#define AAP1_MEMMAP(r, c) \ + (*(u32 *)((u8*)pm8001_ha->memoryMap.region[AAP1].virt_ptr + (r) * 32 \ + + (c))) + + char *str = buf; + int max = 2; + for (i = 0; i < max; i++) { + str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" + "0x%08x 0x%08x\n", + AAP1_MEMMAP(i, 0), + AAP1_MEMMAP(i, 4), + AAP1_MEMMAP(i, 8), + AAP1_MEMMAP(i, 12), + AAP1_MEMMAP(i, 16), + AAP1_MEMMAP(i, 20), + AAP1_MEMMAP(i, 24), + AAP1_MEMMAP(i, 28)); + } + + return str - buf; +} +static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL); +/** + * pm8001_ctl_aap_log_show - IOP event log + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_iop_log_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; +#define IOP_MEMMAP(r, c) \ + (*(u32 *)((u8*)pm8001_ha->memoryMap.region[IOP].virt_ptr + (r) * 32 \ + + (c))) + int i; + char *str = buf; + int max = 2; + for (i = 0; i < max; i++) { + str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" + "0x%08x 0x%08x\n", + IOP_MEMMAP(i, 0), + IOP_MEMMAP(i, 4), + IOP_MEMMAP(i, 8), + IOP_MEMMAP(i, 12), + IOP_MEMMAP(i, 16), + IOP_MEMMAP(i, 20), + IOP_MEMMAP(i, 24), + IOP_MEMMAP(i, 28)); + } + + return str - buf; +} +static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL); + +#define FLASH_CMD_NONE 0x00 +#define FLASH_CMD_UPDATE 0x01 +#define FLASH_CMD_SET_NVMD 0x02 + +struct flash_command { + u8 command[8]; + int code; +}; + +static struct flash_command flash_command_table[] = +{ + {"set_nvmd", FLASH_CMD_SET_NVMD}, + {"update", FLASH_CMD_UPDATE}, + {"", FLASH_CMD_NONE} /* Last entry should be NULL. */ +}; + +struct error_fw { + char *reason; + int err_code; +}; + +static struct error_fw flash_error_table[] = +{ + {"Failed to open fw image file", FAIL_OPEN_BIOS_FILE}, + {"image header mismatch", FLASH_UPDATE_HDR_ERR}, + {"image offset mismatch", FLASH_UPDATE_OFFSET_ERR}, + {"image CRC Error", FLASH_UPDATE_CRC_ERR}, + {"image length Error.", FLASH_UPDATE_LENGTH_ERR}, + {"Failed to program flash chip", FLASH_UPDATE_HW_ERR}, + {"Flash chip not supported.", FLASH_UPDATE_DNLD_NOT_SUPPORTED}, + {"Flash update disabled.", FLASH_UPDATE_DISABLED}, + {"Flash in progress", FLASH_IN_PROGRESS}, + {"Image file size Error", FAIL_FILE_SIZE}, + {"Input parameter error", FAIL_PARAMETERS}, + {"Out of memory", FAIL_OUT_MEMORY}, + {"OK", 0} /* Last entry err_code = 0. */ +}; + +static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha) +{ + struct pm8001_ioctl_payload *payload; + DECLARE_COMPLETION_ONSTACK(completion); + u8 *ioctlbuffer = NULL; + u32 length = 0; + u32 ret = 0; + + length = 1024 * 5 + sizeof(*payload) - 1; + ioctlbuffer = kzalloc(length, GFP_KERNEL); + if (!ioctlbuffer) + return -ENOMEM; + if ((pm8001_ha->fw_image->size <= 0) || + (pm8001_ha->fw_image->size > 4096)) { + ret = FAIL_FILE_SIZE; + goto out; + } + payload = (struct pm8001_ioctl_payload *)ioctlbuffer; + memcpy((u8 *)payload->func_specific, (u8 *)pm8001_ha->fw_image->data, + pm8001_ha->fw_image->size); + payload->length = pm8001_ha->fw_image->size; + payload->id = 0; + pm8001_ha->nvmd_completion = &completion; + ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload); + wait_for_completion(&completion); +out: + kfree(ioctlbuffer); + return ret; +} + +static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha) +{ + struct pm8001_ioctl_payload *payload; + DECLARE_COMPLETION_ONSTACK(completion); + u8 *ioctlbuffer = NULL; + u32 length = 0; + struct fw_control_info *fwControl; + u32 loopNumber, loopcount = 0; + u32 sizeRead = 0; + u32 partitionSize, partitionSizeTmp; + u32 ret = 0; + u32 partitionNumber = 0; + struct pm8001_fw_image_header *image_hdr; + + length = 1024 * 16 + sizeof(*payload) - 1; + ioctlbuffer = kzalloc(length, GFP_KERNEL); + image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data; + if (!ioctlbuffer) + return -ENOMEM; + if (pm8001_ha->fw_image->size < 28) { + ret = FAIL_FILE_SIZE; + goto out; + } + + while (sizeRead < pm8001_ha->fw_image->size) { + partitionSizeTmp = + *(u32 *)((u8 *)&image_hdr->image_length + sizeRead); + partitionSize = be32_to_cpu(partitionSizeTmp); + loopcount = (partitionSize + HEADER_LEN)/IOCTL_BUF_SIZE; + if (loopcount % IOCTL_BUF_SIZE) + loopcount++; + if (loopcount == 0) + loopcount++; + for (loopNumber = 0; loopNumber < loopcount; loopNumber++) { + payload = (struct pm8001_ioctl_payload *)ioctlbuffer; + payload->length = 1024*16; + payload->id = 0; + fwControl = + (struct fw_control_info *)payload->func_specific; + fwControl->len = IOCTL_BUF_SIZE; /* IN */ + fwControl->size = partitionSize + HEADER_LEN;/* IN */ + fwControl->retcode = 0;/* OUT */ + fwControl->offset = loopNumber * IOCTL_BUF_SIZE;/*OUT */ + + /* for the last chunk of data in case file size is not even with + 4k, load only the rest*/ + if (((loopcount-loopNumber) == 1) && + ((partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE)) { + fwControl->len = + (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE; + memcpy((u8 *)fwControl->buffer, + (u8 *)pm8001_ha->fw_image->data + sizeRead, + (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE); + sizeRead += + (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE; + } else { + memcpy((u8 *)fwControl->buffer, + (u8 *)pm8001_ha->fw_image->data + sizeRead, + IOCTL_BUF_SIZE); + sizeRead += IOCTL_BUF_SIZE; + } + + pm8001_ha->nvmd_completion = &completion; + ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload); + wait_for_completion(&completion); + if (ret || (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS)) { + ret = fwControl->retcode; + kfree(ioctlbuffer); + ioctlbuffer = NULL; + break; + } + } + if (ret) + break; + partitionNumber++; +} +out: + kfree(ioctlbuffer); + return ret; +} +static ssize_t pm8001_store_update_fw(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + char *cmd_ptr, *filename_ptr; + int res, i; + int flash_command = FLASH_CMD_NONE; + int err = 0; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + cmd_ptr = kzalloc(count*2, GFP_KERNEL); + + if (!cmd_ptr) { + err = FAIL_OUT_MEMORY; + goto out; + } + + filename_ptr = cmd_ptr + count; + res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr); + if (res != 2) { + err = FAIL_PARAMETERS; + goto out1; + } + + for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) { + if (!memcmp(flash_command_table[i].command, + cmd_ptr, strlen(cmd_ptr))) { + flash_command = flash_command_table[i].code; + break; + } + } + if (flash_command == FLASH_CMD_NONE) { + err = FAIL_PARAMETERS; + goto out1; + } + + if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) { + err = FLASH_IN_PROGRESS; + goto out1; + } + err = request_firmware(&pm8001_ha->fw_image, + filename_ptr, + pm8001_ha->dev); + + if (err) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Failed to load firmware image file %s," + " error %d\n", filename_ptr, err)); + err = FAIL_OPEN_BIOS_FILE; + goto out1; + } + + switch (flash_command) { + case FLASH_CMD_UPDATE: + pm8001_ha->fw_status = FLASH_IN_PROGRESS; + err = pm8001_update_flash(pm8001_ha); + break; + case FLASH_CMD_SET_NVMD: + pm8001_ha->fw_status = FLASH_IN_PROGRESS; + err = pm8001_set_nvmd(pm8001_ha); + break; + default: + pm8001_ha->fw_status = FAIL_PARAMETERS; + err = FAIL_PARAMETERS; + break; + } + release_firmware(pm8001_ha->fw_image); +out1: + kfree(cmd_ptr); +out: + pm8001_ha->fw_status = err; + + if (!err) + return count; + else + return -err; +} + +static ssize_t pm8001_show_update_fw(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + int i; + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + for (i = 0; flash_error_table[i].err_code != 0; i++) { + if (flash_error_table[i].err_code == pm8001_ha->fw_status) + break; + } + if (pm8001_ha->fw_status != FLASH_IN_PROGRESS) + pm8001_ha->fw_status = FLASH_OK; + + return snprintf(buf, PAGE_SIZE, "status=%x %s\n", + flash_error_table[i].err_code, + flash_error_table[i].reason); +} + +static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUGO, + pm8001_show_update_fw, pm8001_store_update_fw); +struct device_attribute *pm8001_host_attrs[] = { + &dev_attr_interface_rev, + &dev_attr_fw_version, + &dev_attr_update_fw, + &dev_attr_aap_log, + &dev_attr_iop_log, + &dev_attr_max_out_io, + &dev_attr_max_devices, + &dev_attr_max_sg_list, + &dev_attr_sas_spec_support, + &dev_attr_logging_level, + &dev_attr_host_sas_address, + NULL, +}; + diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h new file mode 100644 index 000000000000..22644de26399 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_ctl.h @@ -0,0 +1,67 @@ + /* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifndef PM8001_CTL_H_INCLUDED +#define PM8001_CTL_H_INCLUDED + +#define IOCTL_BUF_SIZE 4096 +#define HEADER_LEN 28 +#define SIZE_OFFSET 16 + +struct pm8001_ioctl_payload { + u32 signature; + u16 major_function; + u16 minor_function; + u16 length; + u16 status; + u16 offset; + u16 id; + u8 func_specific[1]; +}; + +#define FLASH_OK 0x000000 +#define FAIL_OPEN_BIOS_FILE 0x000100 +#define FAIL_FILE_SIZE 0x000a00 +#define FAIL_PARAMETERS 0x000b00 +#define FAIL_OUT_MEMORY 0x000c00 +#define FLASH_IN_PROGRESS 0x001000 + +#endif /* PM8001_CTL_H_INCLUDED */ + diff --git a/drivers/scsi/pm8001/pm8001_defs.h b/drivers/scsi/pm8001/pm8001_defs.h new file mode 100644 index 000000000000..944afada61ee --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_defs.h @@ -0,0 +1,112 @@ +/* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifndef _PM8001_DEFS_H_ +#define _PM8001_DEFS_H_ + +enum chip_flavors { + chip_8001, +}; +#define USI_MAX_MEMCNT 9 +#define PM8001_MAX_DMA_SG SG_ALL +enum phy_speed { + PHY_SPEED_15 = 0x01, + PHY_SPEED_30 = 0x02, + PHY_SPEED_60 = 0x04, +}; + +enum data_direction { + DATA_DIR_NONE = 0x0, /* NO TRANSFER */ + DATA_DIR_IN = 0x01, /* INBOUND */ + DATA_DIR_OUT = 0x02, /* OUTBOUND */ + DATA_DIR_BYRECIPIENT = 0x04, /* UNSPECIFIED */ +}; + +enum port_type { + PORT_TYPE_SAS = (1L << 1), + PORT_TYPE_SATA = (1L << 0), +}; + +/* driver compile-time configuration */ +#define PM8001_MAX_CCB 512 /* max ccbs supported */ +#define PM8001_MAX_INB_NUM 1 +#define PM8001_MAX_OUTB_NUM 1 +#define PM8001_CAN_QUEUE 128 /* SCSI Queue depth */ + +/* unchangeable hardware details */ +#define PM8001_MAX_PHYS 8 /* max. possible phys */ +#define PM8001_MAX_PORTS 8 /* max. possible ports */ +#define PM8001_MAX_DEVICES 1024 /* max supported device */ + +enum memory_region_num { + AAP1 = 0x0, /* application acceleration processor */ + IOP, /* IO processor */ + CI, /* consumer index */ + PI, /* producer index */ + IB, /* inbound queue */ + OB, /* outbound queue */ + NVMD, /* NVM device */ + DEV_MEM, /* memory for devices */ + CCB_MEM, /* memory for command control block */ +}; +#define PM8001_EVENT_LOG_SIZE (128 * 1024) + +/*error code*/ +enum mpi_err { + MPI_IO_STATUS_SUCCESS = 0x0, + MPI_IO_STATUS_BUSY = 0x01, + MPI_IO_STATUS_FAIL = 0x02, +}; + +/** + * Phy Control constants + */ +enum phy_control_type { + PHY_LINK_RESET = 0x01, + PHY_HARD_RESET = 0x02, + PHY_NOTIFY_ENABLE_SPINUP = 0x10, +}; + +enum pm8001_hba_info_flags { + PM8001F_INIT_TIME = (1U << 0), + PM8001F_RUN_TIME = (1U << 1), +}; + +#endif diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c new file mode 100644 index 000000000000..a3de306b9045 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -0,0 +1,4458 @@ +/* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ + #include "pm8001_sas.h" + #include "pm8001_hwi.h" + #include "pm8001_chips.h" + #include "pm8001_ctl.h" + +/** + * read_main_config_table - read the configure table and save it. + * @pm8001_ha: our hba card information + */ +static void __devinit read_main_config_table(struct pm8001_hba_info *pm8001_ha) +{ + void __iomem *address = pm8001_ha->main_cfg_tbl_addr; + pm8001_ha->main_cfg_tbl.signature = pm8001_mr32(address, 0x00); + pm8001_ha->main_cfg_tbl.interface_rev = pm8001_mr32(address, 0x04); + pm8001_ha->main_cfg_tbl.firmware_rev = pm8001_mr32(address, 0x08); + pm8001_ha->main_cfg_tbl.max_out_io = pm8001_mr32(address, 0x0C); + pm8001_ha->main_cfg_tbl.max_sgl = pm8001_mr32(address, 0x10); + pm8001_ha->main_cfg_tbl.ctrl_cap_flag = pm8001_mr32(address, 0x14); + pm8001_ha->main_cfg_tbl.gst_offset = pm8001_mr32(address, 0x18); + pm8001_ha->main_cfg_tbl.inbound_queue_offset = + pm8001_mr32(address, MAIN_IBQ_OFFSET); + pm8001_ha->main_cfg_tbl.outbound_queue_offset = + pm8001_mr32(address, MAIN_OBQ_OFFSET); + pm8001_ha->main_cfg_tbl.hda_mode_flag = + pm8001_mr32(address, MAIN_HDA_FLAGS_OFFSET); + + /* read analog Setting offset from the configuration table */ + pm8001_ha->main_cfg_tbl.anolog_setup_table_offset = + pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET); + + /* read Error Dump Offset and Length */ + pm8001_ha->main_cfg_tbl.fatal_err_dump_offset0 = + pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_OFFSET); + pm8001_ha->main_cfg_tbl.fatal_err_dump_length0 = + pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_LENGTH); + pm8001_ha->main_cfg_tbl.fatal_err_dump_offset1 = + pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_OFFSET); + pm8001_ha->main_cfg_tbl.fatal_err_dump_length1 = + pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_LENGTH); +} + +/** + * read_general_status_table - read the general status table and save it. + * @pm8001_ha: our hba card information + */ +static void __devinit +read_general_status_table(struct pm8001_hba_info *pm8001_ha) +{ + void __iomem *address = pm8001_ha->general_stat_tbl_addr; + pm8001_ha->gs_tbl.gst_len_mpistate = pm8001_mr32(address, 0x00); + pm8001_ha->gs_tbl.iq_freeze_state0 = pm8001_mr32(address, 0x04); + pm8001_ha->gs_tbl.iq_freeze_state1 = pm8001_mr32(address, 0x08); + pm8001_ha->gs_tbl.msgu_tcnt = pm8001_mr32(address, 0x0C); + pm8001_ha->gs_tbl.iop_tcnt = pm8001_mr32(address, 0x10); + pm8001_ha->gs_tbl.reserved = pm8001_mr32(address, 0x14); + pm8001_ha->gs_tbl.phy_state[0] = pm8001_mr32(address, 0x18); + pm8001_ha->gs_tbl.phy_state[1] = pm8001_mr32(address, 0x1C); + pm8001_ha->gs_tbl.phy_state[2] = pm8001_mr32(address, 0x20); + pm8001_ha->gs_tbl.phy_state[3] = pm8001_mr32(address, 0x24); + pm8001_ha->gs_tbl.phy_state[4] = pm8001_mr32(address, 0x28); + pm8001_ha->gs_tbl.phy_state[5] = pm8001_mr32(address, 0x2C); + pm8001_ha->gs_tbl.phy_state[6] = pm8001_mr32(address, 0x30); + pm8001_ha->gs_tbl.phy_state[7] = pm8001_mr32(address, 0x34); + pm8001_ha->gs_tbl.reserved1 = pm8001_mr32(address, 0x38); + pm8001_ha->gs_tbl.reserved2 = pm8001_mr32(address, 0x3C); + pm8001_ha->gs_tbl.reserved3 = pm8001_mr32(address, 0x40); + pm8001_ha->gs_tbl.recover_err_info[0] = pm8001_mr32(address, 0x44); + pm8001_ha->gs_tbl.recover_err_info[1] = pm8001_mr32(address, 0x48); + pm8001_ha->gs_tbl.recover_err_info[2] = pm8001_mr32(address, 0x4C); + pm8001_ha->gs_tbl.recover_err_info[3] = pm8001_mr32(address, 0x50); + pm8001_ha->gs_tbl.recover_err_info[4] = pm8001_mr32(address, 0x54); + pm8001_ha->gs_tbl.recover_err_info[5] = pm8001_mr32(address, 0x58); + pm8001_ha->gs_tbl.recover_err_info[6] = pm8001_mr32(address, 0x5C); + pm8001_ha->gs_tbl.recover_err_info[7] = pm8001_mr32(address, 0x60); +} + +/** + * read_inbnd_queue_table - read the inbound queue table and save it. + * @pm8001_ha: our hba card information + */ +static void __devinit +read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha) +{ + int inbQ_num = 1; + int i; + void __iomem *address = pm8001_ha->inbnd_q_tbl_addr; + for (i = 0; i < inbQ_num; i++) { + u32 offset = i * 0x20; + pm8001_ha->inbnd_q_tbl[i].pi_pci_bar = + get_pci_bar_index(pm8001_mr32(address, (offset + 0x14))); + pm8001_ha->inbnd_q_tbl[i].pi_offset = + pm8001_mr32(address, (offset + 0x18)); + } +} + +/** + * read_outbnd_queue_table - read the outbound queue table and save it. + * @pm8001_ha: our hba card information + */ +static void __devinit +read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha) +{ + int outbQ_num = 1; + int i; + void __iomem *address = pm8001_ha->outbnd_q_tbl_addr; + for (i = 0; i < outbQ_num; i++) { + u32 offset = i * 0x24; + pm8001_ha->outbnd_q_tbl[i].ci_pci_bar = + get_pci_bar_index(pm8001_mr32(address, (offset + 0x14))); + pm8001_ha->outbnd_q_tbl[i].ci_offset = + pm8001_mr32(address, (offset + 0x18)); + } +} + +/** + * init_default_table_values - init the default table. + * @pm8001_ha: our hba card information + */ +static void __devinit +init_default_table_values(struct pm8001_hba_info *pm8001_ha) +{ + int qn = 1; + int i; + u32 offsetib, offsetob; + void __iomem *addressib = pm8001_ha->inbnd_q_tbl_addr; + void __iomem *addressob = pm8001_ha->outbnd_q_tbl_addr; + + pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd = 0; + pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3 = 0; + pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7 = 0; + pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3 = 0; + pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7 = 0; + pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3 = 0; + pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7 = 0; + pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3 = 0; + pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7 = 0; + pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3 = 0; + pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7 = 0; + + pm8001_ha->main_cfg_tbl.upper_event_log_addr = + pm8001_ha->memoryMap.region[AAP1].phys_addr_hi; + pm8001_ha->main_cfg_tbl.lower_event_log_addr = + pm8001_ha->memoryMap.region[AAP1].phys_addr_lo; + pm8001_ha->main_cfg_tbl.event_log_size = PM8001_EVENT_LOG_SIZE; + pm8001_ha->main_cfg_tbl.event_log_option = 0x01; + pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr = + pm8001_ha->memoryMap.region[IOP].phys_addr_hi; + pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr = + pm8001_ha->memoryMap.region[IOP].phys_addr_lo; + pm8001_ha->main_cfg_tbl.iop_event_log_size = PM8001_EVENT_LOG_SIZE; + pm8001_ha->main_cfg_tbl.iop_event_log_option = 0x01; + pm8001_ha->main_cfg_tbl.fatal_err_interrupt = 0x01; + for (i = 0; i < qn; i++) { + pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt = + 0x00000100 | (0x00000040 << 16) | (0x00<<30); + pm8001_ha->inbnd_q_tbl[i].upper_base_addr = + pm8001_ha->memoryMap.region[IB].phys_addr_hi; + pm8001_ha->inbnd_q_tbl[i].lower_base_addr = + pm8001_ha->memoryMap.region[IB].phys_addr_lo; + pm8001_ha->inbnd_q_tbl[i].base_virt = + (u8 *)pm8001_ha->memoryMap.region[IB].virt_ptr; + pm8001_ha->inbnd_q_tbl[i].total_length = + pm8001_ha->memoryMap.region[IB].total_len; + pm8001_ha->inbnd_q_tbl[i].ci_upper_base_addr = + pm8001_ha->memoryMap.region[CI].phys_addr_hi; + pm8001_ha->inbnd_q_tbl[i].ci_lower_base_addr = + pm8001_ha->memoryMap.region[CI].phys_addr_lo; + pm8001_ha->inbnd_q_tbl[i].ci_virt = + pm8001_ha->memoryMap.region[CI].virt_ptr; + offsetib = i * 0x20; + pm8001_ha->inbnd_q_tbl[i].pi_pci_bar = + get_pci_bar_index(pm8001_mr32(addressib, + (offsetib + 0x14))); + pm8001_ha->inbnd_q_tbl[i].pi_offset = + pm8001_mr32(addressib, (offsetib + 0x18)); + pm8001_ha->inbnd_q_tbl[i].producer_idx = 0; + pm8001_ha->inbnd_q_tbl[i].consumer_index = 0; + } + for (i = 0; i < qn; i++) { + pm8001_ha->outbnd_q_tbl[i].element_size_cnt = + 256 | (64 << 16) | (1<<30); + pm8001_ha->outbnd_q_tbl[i].upper_base_addr = + pm8001_ha->memoryMap.region[OB].phys_addr_hi; + pm8001_ha->outbnd_q_tbl[i].lower_base_addr = + pm8001_ha->memoryMap.region[OB].phys_addr_lo; + pm8001_ha->outbnd_q_tbl[i].base_virt = + (u8 *)pm8001_ha->memoryMap.region[OB].virt_ptr; + pm8001_ha->outbnd_q_tbl[i].total_length = + pm8001_ha->memoryMap.region[OB].total_len; + pm8001_ha->outbnd_q_tbl[i].pi_upper_base_addr = + pm8001_ha->memoryMap.region[PI].phys_addr_hi; + pm8001_ha->outbnd_q_tbl[i].pi_lower_base_addr = + pm8001_ha->memoryMap.region[PI].phys_addr_lo; + pm8001_ha->outbnd_q_tbl[i].interrup_vec_cnt_delay = + 0 | (10 << 16) | (0 << 24); + pm8001_ha->outbnd_q_tbl[i].pi_virt = + pm8001_ha->memoryMap.region[PI].virt_ptr; + offsetob = i * 0x24; + pm8001_ha->outbnd_q_tbl[i].ci_pci_bar = + get_pci_bar_index(pm8001_mr32(addressob, + offsetob + 0x14)); + pm8001_ha->outbnd_q_tbl[i].ci_offset = + pm8001_mr32(addressob, (offsetob + 0x18)); + pm8001_ha->outbnd_q_tbl[i].consumer_idx = 0; + pm8001_ha->outbnd_q_tbl[i].producer_index = 0; + } +} + +/** + * update_main_config_table - update the main default table to the HBA. + * @pm8001_ha: our hba card information + */ +static void __devinit +update_main_config_table(struct pm8001_hba_info *pm8001_ha) +{ + void __iomem *address = pm8001_ha->main_cfg_tbl_addr; + pm8001_mw32(address, 0x24, + pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd); + pm8001_mw32(address, 0x28, + pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3); + pm8001_mw32(address, 0x2C, + pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7); + pm8001_mw32(address, 0x30, + pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3); + pm8001_mw32(address, 0x34, + pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7); + pm8001_mw32(address, 0x38, + pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3); + pm8001_mw32(address, 0x3C, + pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7); + pm8001_mw32(address, 0x40, + pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3); + pm8001_mw32(address, 0x44, + pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7); + pm8001_mw32(address, 0x48, + pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3); + pm8001_mw32(address, 0x4C, + pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7); + pm8001_mw32(address, 0x50, + pm8001_ha->main_cfg_tbl.upper_event_log_addr); + pm8001_mw32(address, 0x54, + pm8001_ha->main_cfg_tbl.lower_event_log_addr); + pm8001_mw32(address, 0x58, pm8001_ha->main_cfg_tbl.event_log_size); + pm8001_mw32(address, 0x5C, pm8001_ha->main_cfg_tbl.event_log_option); + pm8001_mw32(address, 0x60, + pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr); + pm8001_mw32(address, 0x64, + pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr); + pm8001_mw32(address, 0x68, pm8001_ha->main_cfg_tbl.iop_event_log_size); + pm8001_mw32(address, 0x6C, + pm8001_ha->main_cfg_tbl.iop_event_log_option); + pm8001_mw32(address, 0x70, + pm8001_ha->main_cfg_tbl.fatal_err_interrupt); +} + +/** + * update_inbnd_queue_table - update the inbound queue table to the HBA. + * @pm8001_ha: our hba card information + */ +static void __devinit +update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number) +{ + void __iomem *address = pm8001_ha->inbnd_q_tbl_addr; + u16 offset = number * 0x20; + pm8001_mw32(address, offset + 0x00, + pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt); + pm8001_mw32(address, offset + 0x04, + pm8001_ha->inbnd_q_tbl[number].upper_base_addr); + pm8001_mw32(address, offset + 0x08, + pm8001_ha->inbnd_q_tbl[number].lower_base_addr); + pm8001_mw32(address, offset + 0x0C, + pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr); + pm8001_mw32(address, offset + 0x10, + pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr); +} + +/** + * update_outbnd_queue_table - update the outbound queue table to the HBA. + * @pm8001_ha: our hba card information + */ +static void __devinit +update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number) +{ + void __iomem *address = pm8001_ha->outbnd_q_tbl_addr; + u16 offset = number * 0x24; + pm8001_mw32(address, offset + 0x00, + pm8001_ha->outbnd_q_tbl[number].element_size_cnt); + pm8001_mw32(address, offset + 0x04, + pm8001_ha->outbnd_q_tbl[number].upper_base_addr); + pm8001_mw32(address, offset + 0x08, + pm8001_ha->outbnd_q_tbl[number].lower_base_addr); + pm8001_mw32(address, offset + 0x0C, + pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr); + pm8001_mw32(address, offset + 0x10, + pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr); + pm8001_mw32(address, offset + 0x1C, + pm8001_ha->outbnd_q_tbl[number].interrup_vec_cnt_delay); +} + +/** + * bar4_shift - function is called to shift BAR base address + * @pm8001_ha : our hba card infomation + * @shiftValue : shifting value in memory bar. + */ +static int bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue) +{ + u32 regVal; + u32 max_wait_count; + + /* program the inbound AXI translation Lower Address */ + pm8001_cw32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW, shiftValue); + + /* confirm the setting is written */ + max_wait_count = 1 * 1000 * 1000; /* 1 sec */ + do { + udelay(1); + regVal = pm8001_cr32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW); + } while ((regVal != shiftValue) && (--max_wait_count)); + + if (!max_wait_count) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW" + " = 0x%x\n", regVal)); + return -1; + } + return 0; +} + +/** + * mpi_set_phys_g3_with_ssc + * @pm8001_ha: our hba card information + * @SSCbit: set SSCbit to 0 to disable all phys ssc; 1 to enable all phys ssc. + */ +static void __devinit +mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit) +{ + u32 offset; + u32 value; + u32 i, j; + u32 bit_cnt; + +#define SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR 0x00030000 +#define SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR 0x00040000 +#define SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET 0x1074 +#define SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET 0x1074 +#define PHY_G3_WITHOUT_SSC_BIT_SHIFT 12 +#define PHY_G3_WITH_SSC_BIT_SHIFT 13 +#define SNW3_PHY_CAPABILITIES_PARITY 31 + + /* + * Using shifted destination address 0x3_0000:0x1074 + 0x4000*N (N=0:3) + * Using shifted destination address 0x4_0000:0x1074 + 0x4000*(N-4) (N=4:7) + */ + if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR)) + return; + /* set SSC bit of PHY 0 - 3 */ + for (i = 0; i < 4; i++) { + offset = SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET + 0x4000 * i; + value = pm8001_cr32(pm8001_ha, 2, offset); + if (SSCbit) { + value |= 0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT; + value &= ~(0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT); + } else { + value |= 0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT; + value &= ~(0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT); + } + bit_cnt = 0; + for (j = 0; j < 31; j++) + if ((value >> j) & 0x00000001) + bit_cnt++; + if (bit_cnt % 2) + value &= ~(0x00000001 << SNW3_PHY_CAPABILITIES_PARITY); + else + value |= 0x00000001 << SNW3_PHY_CAPABILITIES_PARITY; + + pm8001_cw32(pm8001_ha, 2, offset, value); + } + + /* shift membase 3 for SAS2_SETTINGS_LOCAL_PHY 4 - 7 */ + if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR)) + return; + + /* set SSC bit of PHY 4 - 7 */ + for (i = 4; i < 8; i++) { + offset = SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET + 0x4000 * (i-4); + value = pm8001_cr32(pm8001_ha, 2, offset); + if (SSCbit) { + value |= 0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT; + value &= ~(0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT); + } else { + value |= 0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT; + value &= ~(0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT); + } + bit_cnt = 0; + for (j = 0; j < 31; j++) + if ((value >> j) & 0x00000001) + bit_cnt++; + if (bit_cnt % 2) + value &= ~(0x00000001 << SNW3_PHY_CAPABILITIES_PARITY); + else + value |= 0x00000001 << SNW3_PHY_CAPABILITIES_PARITY; + + pm8001_cw32(pm8001_ha, 2, offset, value); + } + + /*set the shifted destination address to 0x0 to avoid error operation */ + bar4_shift(pm8001_ha, 0x0); + return; +} + +/** + * mpi_set_open_retry_interval_reg + * @pm8001_ha: our hba card information + * @interval - interval time for each OPEN_REJECT (RETRY). The units are in 1us. + */ +static void __devinit +mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha, + u32 interval) +{ + u32 offset; + u32 value; + u32 i; + +#define OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR 0x00030000 +#define OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR 0x00040000 +#define OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET 0x30B4 +#define OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET 0x30B4 +#define OPEN_RETRY_INTERVAL_REG_MASK 0x0000FFFF + + value = interval & OPEN_RETRY_INTERVAL_REG_MASK; + /* shift bar and set the OPEN_REJECT(RETRY) interval time of PHY 0 -3.*/ + if (-1 == bar4_shift(pm8001_ha, + OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR)) + return; + for (i = 0; i < 4; i++) { + offset = OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET + 0x4000 * i; + pm8001_cw32(pm8001_ha, 2, offset, value); + } + + if (-1 == bar4_shift(pm8001_ha, + OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR)) + return; + for (i = 4; i < 8; i++) { + offset = OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET + 0x4000 * (i-4); + pm8001_cw32(pm8001_ha, 2, offset, value); + } + /*set the shifted destination address to 0x0 to avoid error operation */ + bar4_shift(pm8001_ha, 0x0); + return; +} + +/** + * mpi_init_check - check firmware initialization status. + * @pm8001_ha: our hba card information + */ +static int mpi_init_check(struct pm8001_hba_info *pm8001_ha) +{ + u32 max_wait_count; + u32 value; + u32 gst_len_mpistate; + /* Write bit0=1 to Inbound DoorBell Register to tell the SPC FW the + table is updated */ + pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_UPDATE); + /* wait until Inbound DoorBell Clear Register toggled */ + max_wait_count = 1 * 1000 * 1000;/* 1 sec */ + do { + udelay(1); + value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET); + value &= SPC_MSGU_CFG_TABLE_UPDATE; + } while ((value != 0) && (--max_wait_count)); + + if (!max_wait_count) + return -1; + /* check the MPI-State for initialization */ + gst_len_mpistate = + pm8001_mr32(pm8001_ha->general_stat_tbl_addr, + GST_GSTLEN_MPIS_OFFSET); + if (GST_MPI_STATE_INIT != (gst_len_mpistate & GST_MPI_STATE_MASK)) + return -1; + /* check MPI Initialization error */ + gst_len_mpistate = gst_len_mpistate >> 16; + if (0x0000 != gst_len_mpistate) + return -1; + return 0; +} + +/** + * check_fw_ready - The LLDD check if the FW is ready, if not, return error. + * @pm8001_ha: our hba card information + */ +static int check_fw_ready(struct pm8001_hba_info *pm8001_ha) +{ + u32 value, value1; + u32 max_wait_count; + /* check error state */ + value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); + /* check AAP error */ + if (SCRATCH_PAD1_ERR == (value & SCRATCH_PAD_STATE_MASK)) { + /* error state */ + value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0); + return -1; + } + + /* check IOP error */ + if (SCRATCH_PAD2_ERR == (value1 & SCRATCH_PAD_STATE_MASK)) { + /* error state */ + value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3); + return -1; + } + + /* bit 4-31 of scratch pad1 should be zeros if it is not + in error state*/ + if (value & SCRATCH_PAD1_STATE_MASK) { + /* error case */ + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0); + return -1; + } + + /* bit 2, 4-31 of scratch pad2 should be zeros if it is not + in error state */ + if (value1 & SCRATCH_PAD2_STATE_MASK) { + /* error case */ + return -1; + } + + max_wait_count = 1 * 1000 * 1000;/* 1 sec timeout */ + + /* wait until scratch pad 1 and 2 registers in ready state */ + do { + udelay(1); + value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) + & SCRATCH_PAD1_RDY; + value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) + & SCRATCH_PAD2_RDY; + if ((--max_wait_count) == 0) + return -1; + } while ((value != SCRATCH_PAD1_RDY) || (value1 != SCRATCH_PAD2_RDY)); + return 0; +} + +static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha) +{ + void __iomem *base_addr; + u32 value; + u32 offset; + u32 pcibar; + u32 pcilogic; + + value = pm8001_cr32(pm8001_ha, 0, 0x44); + offset = value & 0x03FFFFFF; + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Scratchpad 0 Offset: %x \n", offset)); + pcilogic = (value & 0xFC000000) >> 26; + pcibar = get_pci_bar_index(pcilogic); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Scratchpad 0 PCI BAR: %d \n", pcibar)); + pm8001_ha->main_cfg_tbl_addr = base_addr = + pm8001_ha->io_mem[pcibar].memvirtaddr + offset; + pm8001_ha->general_stat_tbl_addr = + base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x18); + pm8001_ha->inbnd_q_tbl_addr = + base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x1C); + pm8001_ha->outbnd_q_tbl_addr = + base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x20); +} + +/** + * pm8001_chip_init - the main init function that initialize whole PM8001 chip. + * @pm8001_ha: our hba card information + */ +static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha) +{ + /* check the firmware status */ + if (-1 == check_fw_ready(pm8001_ha)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Firmware is not ready!\n")); + return -EBUSY; + } + + /* Initialize pci space address eg: mpi offset */ + init_pci_device_addresses(pm8001_ha); + init_default_table_values(pm8001_ha); + read_main_config_table(pm8001_ha); + read_general_status_table(pm8001_ha); + read_inbnd_queue_table(pm8001_ha); + read_outbnd_queue_table(pm8001_ha); + /* update main config table ,inbound table and outbound table */ + update_main_config_table(pm8001_ha); + update_inbnd_queue_table(pm8001_ha, 0); + update_outbnd_queue_table(pm8001_ha, 0); + mpi_set_phys_g3_with_ssc(pm8001_ha, 0); + mpi_set_open_retry_interval_reg(pm8001_ha, 7); + /* notify firmware update finished and check initialization status */ + if (0 == mpi_init_check(pm8001_ha)) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("MPI initialize successful!\n")); + } else + return -EBUSY; + /*This register is a 16-bit timer with a resolution of 1us. This is the + timer used for interrupt delay/coalescing in the PCIe Application Layer. + Zero is not a valid value. A value of 1 in the register will cause the + interrupts to be normal. A value greater than 1 will cause coalescing + delays.*/ + pm8001_cw32(pm8001_ha, 1, 0x0033c0, 0x1); + pm8001_cw32(pm8001_ha, 1, 0x0033c4, 0x0); + return 0; +} + +static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha) +{ + u32 max_wait_count; + u32 value; + u32 gst_len_mpistate; + init_pci_device_addresses(pm8001_ha); + /* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the + table is stop */ + pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_RESET); + + /* wait until Inbound DoorBell Clear Register toggled */ + max_wait_count = 1 * 1000 * 1000;/* 1 sec */ + do { + udelay(1); + value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET); + value &= SPC_MSGU_CFG_TABLE_RESET; + } while ((value != 0) && (--max_wait_count)); + + if (!max_wait_count) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TIMEOUT:IBDB value/=0x%x\n", value)); + return -1; + } + + /* check the MPI-State for termination in progress */ + /* wait until Inbound DoorBell Clear Register toggled */ + max_wait_count = 1 * 1000 * 1000; /* 1 sec */ + do { + udelay(1); + gst_len_mpistate = + pm8001_mr32(pm8001_ha->general_stat_tbl_addr, + GST_GSTLEN_MPIS_OFFSET); + if (GST_MPI_STATE_UNINIT == + (gst_len_mpistate & GST_MPI_STATE_MASK)) + break; + } while (--max_wait_count); + if (!max_wait_count) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk(" TIME OUT MPI State = 0x%x\n", + gst_len_mpistate & GST_MPI_STATE_MASK)); + return -1; + } + return 0; +} + +/** + * soft_reset_ready_check - Function to check FW is ready for soft reset. + * @pm8001_ha: our hba card information + */ +static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha) +{ + u32 regVal, regVal1, regVal2; + if (mpi_uninit_check(pm8001_ha) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("MPI state is not ready\n")); + return -1; + } + /* read the scratch pad 2 register bit 2 */ + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) + & SCRATCH_PAD2_FWRDY_RST; + if (regVal == SCRATCH_PAD2_FWRDY_RST) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Firmware is ready for reset .\n")); + } else { + /* Trigger NMI twice via RB6 */ + if (-1 == bar4_shift(pm8001_ha, RB6_ACCESS_REG)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Shift Bar4 to 0x%x failed\n", + RB6_ACCESS_REG)); + return -1; + } + pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, + RB6_MAGIC_NUMBER_RST); + pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, RB6_MAGIC_NUMBER_RST); + /* wait for 100 ms */ + mdelay(100); + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) & + SCRATCH_PAD2_FWRDY_RST; + if (regVal != SCRATCH_PAD2_FWRDY_RST) { + regVal1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + regVal2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TIMEOUT:MSGU_SCRATCH_PAD1" + "=0x%x, MSGU_SCRATCH_PAD2=0x%x\n", + regVal1, regVal2)); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD0 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0))); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD3 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3))); + return -1; + } + } + return 0; +} + +/** + * pm8001_chip_soft_rst - soft reset the PM8001 chip, so that the clear all + * the FW register status to the originated status. + * @pm8001_ha: our hba card information + * @signature: signature in host scratch pad0 register. + */ +static int +pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature) +{ + u32 regVal, toggleVal; + u32 max_wait_count; + u32 regVal1, regVal2, regVal3; + + /* step1: Check FW is ready for soft reset */ + if (soft_reset_ready_check(pm8001_ha) != 0) { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("FW is not ready\n")); + return -1; + } + + /* step 2: clear NMI status register on AAP1 and IOP, write the same + value to clear */ + /* map 0x60000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Shift Bar4 to 0x%x failed\n", + MBIC_AAP1_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("MBIC - NMI Enable VPE0 (IOP)= 0x%x\n", regVal)); + pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP, 0x0); + /* map 0x70000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Shift Bar4 to 0x%x failed\n", + MBIC_IOP_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("MBIC - NMI Enable VPE0 (AAP1)= 0x%x\n", regVal)); + pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1, 0x0); + + regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCIE -Event Interrupt Enable = 0x%x\n", regVal)); + pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE, 0x0); + + regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCIE - Event Interrupt = 0x%x\n", regVal)); + pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT, regVal); + + regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCIE -Error Interrupt Enable = 0x%x\n", regVal)); + pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE, 0x0); + + regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCIE - Error Interrupt = 0x%x\n", regVal)); + pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT, regVal); + + /* read the scratch pad 1 register bit 2 */ + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) + & SCRATCH_PAD1_RST; + toggleVal = regVal ^ SCRATCH_PAD1_RST; + + /* set signature in host scratch pad0 register to tell SPC that the + host performs the soft reset */ + pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0, signature); + + /* read required registers for confirmming */ + /* map 0x0700000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Shift Bar4 to 0x%x failed\n", + GSM_ADDR_BASE)); + return -1; + } + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x0(0x00007b88)-GSM Configuration and" + " Reset = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET))); + + /* step 3: host read GSM Configuration and Reset register */ + regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET); + /* Put those bits to low */ + /* GSM XCBI offset = 0x70 0000 + 0x00 Bit 13 COM_SLV_SW_RSTB 1 + 0x00 Bit 12 QSSP_SW_RSTB 1 + 0x00 Bit 11 RAAE_SW_RSTB 1 + 0x00 Bit 9 RB_1_SW_RSTB 1 + 0x00 Bit 8 SM_SW_RSTB 1 + */ + regVal &= ~(0x00003b00); + /* host write GSM Configuration and Reset register */ + pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x0 (0x00007b88 ==> 0x00004088) - GSM " + "Configuration and Reset is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET))); + + /* step 4: */ + /* disable GSM - Read Address Parity Check */ + regVal1 = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700038 - Read Address Parity Check " + "Enable = 0x%x\n", regVal1)); + pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, 0x0); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable" + "is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK))); + + /* disable GSM - Write Address Parity Check */ + regVal2 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700040 - Write Address Parity Check" + " Enable = 0x%x\n", regVal2)); + pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, 0x0); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700040 - Write Address Parity Check " + "Enable is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK))); + + /* disable GSM - Write Data Parity Check */ + regVal3 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x300048 - Write Data Parity Check" + " Enable = 0x%x\n", regVal3)); + pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, 0x0); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x300048 - Write Data Parity Check Enable" + "is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK))); + + /* step 5: delay 10 usec */ + udelay(10); + /* step 5-b: set GPIO-0 output control to tristate anyway */ + if (-1 == bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Shift Bar4 to 0x%x failed\n", + GPIO_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GPIO Output Control Register:" + " = 0x%x\n", regVal)); + /* set GPIO-0 output control to tri-state */ + regVal &= 0xFFFFFFFC; + pm8001_cw32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET, regVal); + + /* Step 6: Reset the IOP and AAP1 */ + /* map 0x00000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + SPC_TOP_LEVEL_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Top Register before resetting IOP/AAP1" + ":= 0x%x\n", regVal)); + regVal &= ~(SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS); + pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); + + /* step 7: Reset the BDMA/OSSP */ + regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Top Register before resetting BDMA/OSSP" + ": = 0x%x\n", regVal)); + regVal &= ~(SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP); + pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); + + /* step 8: delay 10 usec */ + udelay(10); + + /* step 9: bring the BDMA and OSSP out of reset */ + regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Top Register before bringing up BDMA/OSSP" + ":= 0x%x\n", regVal)); + regVal |= (SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP); + pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); + + /* step 10: delay 10 usec */ + udelay(10); + + /* step 11: reads and sets the GSM Configuration and Reset Register */ + /* map 0x0700000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + GSM_ADDR_BASE)); + return -1; + } + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x0 (0x00007b88)-GSM Configuration and " + "Reset = 0x%x\n", pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET))); + regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET); + /* Put those bits to high */ + /* GSM XCBI offset = 0x70 0000 + 0x00 Bit 13 COM_SLV_SW_RSTB 1 + 0x00 Bit 12 QSSP_SW_RSTB 1 + 0x00 Bit 11 RAAE_SW_RSTB 1 + 0x00 Bit 9 RB_1_SW_RSTB 1 + 0x00 Bit 8 SM_SW_RSTB 1 + */ + regVal |= (GSM_CONFIG_RESET_VALUE); + pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM (0x00004088 ==> 0x00007b88) - GSM" + " Configuration and Reset is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET))); + + /* step 12: Restore GSM - Read Address Parity Check */ + regVal = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK); + /* just for debugging */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable" + " = 0x%x\n", regVal)); + pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, regVal1); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700038 - Read Address Parity" + " Check Enable is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK))); + /* Restore GSM - Write Address Parity Check */ + regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK); + pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, regVal2); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700040 - Write Address Parity Check" + " Enable is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK))); + /* Restore GSM - Write Data Parity Check */ + regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK); + pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, regVal3); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700048 - Write Data Parity Check Enable" + "is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK))); + + /* step 13: bring the IOP and AAP1 out of reset */ + /* map 0x00000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Shift Bar4 to 0x%x failed\n", + SPC_TOP_LEVEL_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET); + regVal |= (SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS); + pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); + + /* step 14: delay 10 usec - Normal Mode */ + udelay(10); + /* check Soft Reset Normal mode or Soft Reset HDA mode */ + if (signature == SPC_SOFT_RESET_SIGNATURE) { + /* step 15 (Normal Mode): wait until scratch pad1 register + bit 2 toggled */ + max_wait_count = 2 * 1000 * 1000;/* 2 sec */ + do { + udelay(1); + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) & + SCRATCH_PAD1_RST; + } while ((regVal != toggleVal) && (--max_wait_count)); + + if (!max_wait_count) { + regVal = pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_1); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TIMEOUT : ToggleVal 0x%x," + "MSGU_SCRATCH_PAD1 = 0x%x\n", + toggleVal, regVal)); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD0 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_0))); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD2 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_2))); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD3 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_3))); + return -1; + } + + /* step 16 (Normal) - Clear ODMR and ODCR */ + pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL); + pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL); + + /* step 17 (Normal Mode): wait for the FW and IOP to get + ready - 1 sec timeout */ + /* Wait for the SPC Configuration Table to be ready */ + if (check_fw_ready(pm8001_ha) == -1) { + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + /* return error if MPI Configuration Table not ready */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("FW not ready SCRATCH_PAD1" + " = 0x%x\n", regVal)); + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); + /* return error if MPI Configuration Table not ready */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("FW not ready SCRATCH_PAD2" + " = 0x%x\n", regVal)); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD0 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_0))); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD3 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_3))); + return -1; + } + } + + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SPC soft reset Complete\n")); + return 0; +} + +static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha) +{ + u32 i; + u32 regVal; + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("chip reset start\n")); + + /* do SPC chip reset. */ + regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET); + regVal &= ~(SPC_REG_RESET_DEVICE); + pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal); + + /* delay 10 usec */ + udelay(10); + + /* bring chip reset out of reset */ + regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET); + regVal |= SPC_REG_RESET_DEVICE; + pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal); + + /* delay 10 usec */ + udelay(10); + + /* wait for 20 msec until the firmware gets reloaded */ + i = 20; + do { + mdelay(1); + } while ((--i) != 0); + + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("chip reset finished\n")); +} + +/** + * pm8001_chip_iounmap - which maped when initilized. + * @pm8001_ha: our hba card information + */ +static void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha) +{ + s8 bar, logical = 0; + for (bar = 0; bar < 6; bar++) { + /* + ** logical BARs for SPC: + ** bar 0 and 1 - logical BAR0 + ** bar 2 and 3 - logical BAR1 + ** bar4 - logical BAR2 + ** bar5 - logical BAR3 + ** Skip the appropriate assignments: + */ + if ((bar == 1) || (bar == 3)) + continue; + if (pm8001_ha->io_mem[logical].memvirtaddr) { + iounmap(pm8001_ha->io_mem[logical].memvirtaddr); + logical++; + } + } +} + +/** + * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt + * @pm8001_ha: our hba card information + */ +static void +pm8001_chip_intx_interrupt_enable(struct pm8001_hba_info *pm8001_ha) +{ + pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL); + pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL); +} + + /** + * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt + * @pm8001_ha: our hba card information + */ +static void +pm8001_chip_intx_interrupt_disable(struct pm8001_hba_info *pm8001_ha) +{ + pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_MASK_ALL); +} + +/** + * pm8001_chip_msix_interrupt_enable - enable PM8001 chip interrupt + * @pm8001_ha: our hba card information + */ +static void +pm8001_chip_msix_interrupt_enable(struct pm8001_hba_info *pm8001_ha, + u32 int_vec_idx) +{ + u32 msi_index; + u32 value; + msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE; + msi_index += MSIX_TABLE_BASE; + pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_ENABLE); + value = (1 << int_vec_idx); + pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, value); + +} + +/** + * pm8001_chip_msix_interrupt_disable - disable PM8001 chip interrupt + * @pm8001_ha: our hba card information + */ +static void +pm8001_chip_msix_interrupt_disable(struct pm8001_hba_info *pm8001_ha, + u32 int_vec_idx) +{ + u32 msi_index; + msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE; + msi_index += MSIX_TABLE_BASE; + pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_DISABLE); + +} +/** + * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt + * @pm8001_ha: our hba card information + */ +static void +pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha) +{ +#ifdef PM8001_USE_MSIX + pm8001_chip_msix_interrupt_enable(pm8001_ha, 0); + return; +#endif + pm8001_chip_intx_interrupt_enable(pm8001_ha); + +} + +/** + * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt + * @pm8001_ha: our hba card information + */ +static void +pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha) +{ +#ifdef PM8001_USE_MSIX + pm8001_chip_msix_interrupt_disable(pm8001_ha, 0); + return; +#endif + pm8001_chip_intx_interrupt_disable(pm8001_ha); + +} + +/** + * mpi_msg_free_get- get the free message buffer for transfer inbound queue. + * @circularQ: the inbound queue we want to transfer to HBA. + * @messageSize: the message size of this transfer, normally it is 64 bytes + * @messagePtr: the pointer to message. + */ +static int mpi_msg_free_get(struct inbound_queue_table *circularQ, + u16 messageSize, void **messagePtr) +{ + u32 offset, consumer_index; + struct mpi_msg_hdr *msgHeader; + u8 bcCount = 1; /* only support single buffer */ + + /* Checks is the requested message size can be allocated in this queue*/ + if (messageSize > 64) { + *messagePtr = NULL; + return -1; + } + + /* Stores the new consumer index */ + consumer_index = pm8001_read_32(circularQ->ci_virt); + circularQ->consumer_index = cpu_to_le32(consumer_index); + if (((circularQ->producer_idx + bcCount) % 256) == + circularQ->consumer_index) { + *messagePtr = NULL; + return -1; + } + /* get memory IOMB buffer address */ + offset = circularQ->producer_idx * 64; + /* increment to next bcCount element */ + circularQ->producer_idx = (circularQ->producer_idx + bcCount) % 256; + /* Adds that distance to the base of the region virtual address plus + the message header size*/ + msgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt + offset); + *messagePtr = ((void *)msgHeader) + sizeof(struct mpi_msg_hdr); + return 0; +} + +/** + * mpi_build_cmd- build the message queue for transfer, update the PI to FW + * to tell the fw to get this message from IOMB. + * @pm8001_ha: our hba card information + * @circularQ: the inbound queue we want to transfer to HBA. + * @opCode: the operation code represents commands which LLDD and fw recognized. + * @payload: the command payload of each operation command. + */ +static int mpi_build_cmd(struct pm8001_hba_info *pm8001_ha, + struct inbound_queue_table *circularQ, + u32 opCode, void *payload) +{ + u32 Header = 0, hpriority = 0, bc = 1, category = 0x02; + u32 responseQueue = 0; + void *pMessage; + + if (mpi_msg_free_get(circularQ, 64, &pMessage) < 0) { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("No free mpi buffer \n")); + return -1; + } + BUG_ON(!payload); + /*Copy to the payload*/ + memcpy(pMessage, payload, (64 - sizeof(struct mpi_msg_hdr))); + + /*Build the header*/ + Header = ((1 << 31) | (hpriority << 30) | ((bc & 0x1f) << 24) + | ((responseQueue & 0x3F) << 16) + | ((category & 0xF) << 12) | (opCode & 0xFFF)); + + pm8001_write_32((pMessage - 4), 0, cpu_to_le32(Header)); + /*Update the PI to the firmware*/ + pm8001_cw32(pm8001_ha, circularQ->pi_pci_bar, + circularQ->pi_offset, circularQ->producer_idx); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("after PI= %d CI= %d \n", circularQ->producer_idx, + circularQ->consumer_index)); + return 0; +} + +static u32 mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg, + struct outbound_queue_table *circularQ, u8 bc) +{ + u32 producer_index; + struct mpi_msg_hdr *msgHeader; + struct mpi_msg_hdr *pOutBoundMsgHeader; + + msgHeader = (struct mpi_msg_hdr *)(pMsg - sizeof(struct mpi_msg_hdr)); + pOutBoundMsgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt + + circularQ->consumer_idx * 64); + if (pOutBoundMsgHeader != msgHeader) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("consumer_idx = %d msgHeader = %p\n", + circularQ->consumer_idx, msgHeader)); + + /* Update the producer index from SPC */ + producer_index = pm8001_read_32(circularQ->pi_virt); + circularQ->producer_index = cpu_to_le32(producer_index); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("consumer_idx = %d producer_index = %d" + "msgHeader = %p\n", circularQ->consumer_idx, + circularQ->producer_index, msgHeader)); + return 0; + } + /* free the circular queue buffer elements associated with the message*/ + circularQ->consumer_idx = (circularQ->consumer_idx + bc) % 256; + /* update the CI of outbound queue */ + pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar, circularQ->ci_offset, + circularQ->consumer_idx); + /* Update the producer index from SPC*/ + producer_index = pm8001_read_32(circularQ->pi_virt); + circularQ->producer_index = cpu_to_le32(producer_index); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" CI=%d PI=%d\n", circularQ->consumer_idx, + circularQ->producer_index)); + return 0; +} + +/** + * mpi_msg_consume- get the MPI message from outbound queue message table. + * @pm8001_ha: our hba card information + * @circularQ: the outbound queue table. + * @messagePtr1: the message contents of this outbound message. + * @pBC: the message size. + */ +static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha, + struct outbound_queue_table *circularQ, + void **messagePtr1, u8 *pBC) +{ + struct mpi_msg_hdr *msgHeader; + __le32 msgHeader_tmp; + u32 header_tmp; + do { + /* If there are not-yet-delivered messages ... */ + if (circularQ->producer_index != circularQ->consumer_idx) { + /*Get the pointer to the circular queue buffer element*/ + msgHeader = (struct mpi_msg_hdr *) + (circularQ->base_virt + + circularQ->consumer_idx * 64); + /* read header */ + header_tmp = pm8001_read_32(msgHeader); + msgHeader_tmp = cpu_to_le32(header_tmp); + if (0 != (msgHeader_tmp & 0x80000000)) { + if (OPC_OUB_SKIP_ENTRY != + (msgHeader_tmp & 0xfff)) { + *messagePtr1 = + ((u8 *)msgHeader) + + sizeof(struct mpi_msg_hdr); + *pBC = (u8)((msgHeader_tmp >> 24) & + 0x1f); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(": CI=%d PI=%d " + "msgHeader=%x\n", + circularQ->consumer_idx, + circularQ->producer_index, + msgHeader_tmp)); + return MPI_IO_STATUS_SUCCESS; + } else { + circularQ->consumer_idx = + (circularQ->consumer_idx + + ((msgHeader_tmp >> 24) & 0x1f)) + % 256; + msgHeader_tmp = 0; + pm8001_write_32(msgHeader, 0, 0); + /* update the CI of outbound queue */ + pm8001_cw32(pm8001_ha, + circularQ->ci_pci_bar, + circularQ->ci_offset, + circularQ->consumer_idx); + } + } else { + circularQ->consumer_idx = + (circularQ->consumer_idx + + ((msgHeader_tmp >> 24) & 0x1f)) % 256; + msgHeader_tmp = 0; + pm8001_write_32(msgHeader, 0, 0); + /* update the CI of outbound queue */ + pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar, + circularQ->ci_offset, + circularQ->consumer_idx); + return MPI_IO_STATUS_FAIL; + } + } else { + u32 producer_index; + void *pi_virt = circularQ->pi_virt; + /* Update the producer index from SPC */ + producer_index = pm8001_read_32(pi_virt); + circularQ->producer_index = cpu_to_le32(producer_index); + } + } while (circularQ->producer_index != circularQ->consumer_idx); + /* while we don't have any more not-yet-delivered message */ + /* report empty */ + return MPI_IO_STATUS_BUSY; +} + +static void pm8001_work_queue(struct work_struct *work) +{ + struct delayed_work *dw = container_of(work, struct delayed_work, work); + struct pm8001_wq *wq = container_of(dw, struct pm8001_wq, work_q); + struct pm8001_device *pm8001_dev; + struct domain_device *dev; + + switch (wq->handler) { + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + pm8001_dev = wq->data; + dev = pm8001_dev->sas_device; + pm8001_I_T_nexus_reset(dev); + break; + case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: + pm8001_dev = wq->data; + dev = pm8001_dev->sas_device; + pm8001_I_T_nexus_reset(dev); + break; + case IO_DS_IN_ERROR: + pm8001_dev = wq->data; + dev = pm8001_dev->sas_device; + pm8001_I_T_nexus_reset(dev); + break; + case IO_DS_NON_OPERATIONAL: + pm8001_dev = wq->data; + dev = pm8001_dev->sas_device; + pm8001_I_T_nexus_reset(dev); + break; + } + list_del(&wq->entry); + kfree(wq); +} + +static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data, + int handler) +{ + struct pm8001_wq *wq; + int ret = 0; + + wq = kmalloc(sizeof(struct pm8001_wq), GFP_ATOMIC); + if (wq) { + wq->pm8001_ha = pm8001_ha; + wq->data = data; + wq->handler = handler; + INIT_DELAYED_WORK(&wq->work_q, pm8001_work_queue); + list_add_tail(&wq->entry, &pm8001_ha->wq_list); + schedule_delayed_work(&wq->work_q, 0); + } else + ret = -ENOMEM; + + return ret; +} + +/** + * mpi_ssp_completion- process the event that FW response to the SSP request. + * @pm8001_ha: our hba card information + * @piomb: the message contents of this outbound message. + * + * When FW has completed a ssp request for example a IO request, after it has + * filled the SG data with the data, it will trigger this event represent + * that he has finished the job,please check the coresponding buffer. + * So we will tell the caller who maybe waiting the result to tell upper layer + * that the task has been finished. + */ +static void +mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb) +{ + struct sas_task *t; + struct pm8001_ccb_info *ccb; + unsigned long flags; + u32 status; + u32 param; + u32 tag; + struct ssp_completion_resp *psspPayload; + struct task_status_struct *ts; + struct ssp_response_iu *iu; + struct pm8001_device *pm8001_dev; + psspPayload = (struct ssp_completion_resp *)(piomb + 4); + status = le32_to_cpu(psspPayload->status); + tag = le32_to_cpu(psspPayload->tag); + ccb = &pm8001_ha->ccb_info[tag]; + pm8001_dev = ccb->device; + param = le32_to_cpu(psspPayload->param); + + t = ccb->task; + + if (status && status != IO_UNDERFLOW) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("sas IO status 0x%x\n", status)); + if (unlikely(!t || !t->lldd_task || !t->dev)) + return; + ts = &t->task_status; + switch (status) { + case IO_SUCCESS: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS" + ",param = %d \n", param)); + if (param == 0) { + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; + } else { + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_PROTO_RESPONSE; + ts->residual = param; + iu = &psspPayload->ssp_resp_iu; + sas_ssp_task_response(pm8001_ha->dev, t, iu); + } + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_ABORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ABORTED IOMB Tag \n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + break; + case IO_UNDERFLOW: + /* SSP Completion with error */ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW" + ",param = %d \n", param)); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_UNDERRUN; + ts->residual = param; + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_NO_DEVICE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_NO_DEVICE\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_PHY_DOWN; + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_EPROTO; + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + if (!t->uldd_task) + pm8001_handle_event(pm8001_ha, + pm8001_dev, + IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS); + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_BAD_DEST; + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_" + "NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_CONN_RATE; + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_WRONG_DEST; + break; + case IO_XFER_ERROR_NAK_RECEIVED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_XFER_ERROR_ACK_NAK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_DMA: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_DMA\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_XFER_ERROR_OFFSET_MISMATCH: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + break; + case IO_PORT_IN_RESET: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_PORT_IN_RESET\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + break; + case IO_DS_NON_OPERATIONAL: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_NON_OPERATIONAL\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + if (!t->uldd_task) + pm8001_handle_event(pm8001_ha, + pm8001_dev, + IO_DS_NON_OPERATIONAL); + break; + case IO_DS_IN_RECOVERY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_IN_RECOVERY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + break; + case IO_TM_TAG_NOT_FOUND: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_TM_TAG_NOT_FOUND\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + break; + case IO_SSP_EXT_IU_ZERO_LEN_ERROR: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_SSP_EXT_IU_ZERO_LEN_ERROR\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + break; + case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", status)); + /* not allowed case. Therefore, return failed status */ + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + break; + } + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("scsi_status = %x \n ", + psspPayload->ssp_resp_iu.status)); + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&t->task_state_lock, flags); + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with" + " io_status 0x%x resp 0x%x " + "stat 0x%x but aborted by upper layer!\n", + t, status, ts->resp, ts->stat)); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + } else { + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/* in order to force CPU ordering */ + t->task_done(t); + } +} + +/*See the comments for mpi_ssp_completion */ +static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb) +{ + struct sas_task *t; + unsigned long flags; + struct task_status_struct *ts; + struct pm8001_ccb_info *ccb; + struct pm8001_device *pm8001_dev; + struct ssp_event_resp *psspPayload = + (struct ssp_event_resp *)(piomb + 4); + u32 event = le32_to_cpu(psspPayload->event); + u32 tag = le32_to_cpu(psspPayload->tag); + u32 port_id = le32_to_cpu(psspPayload->port_id); + u32 dev_id = le32_to_cpu(psspPayload->device_id); + + ccb = &pm8001_ha->ccb_info[tag]; + t = ccb->task; + pm8001_dev = ccb->device; + if (event) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("sas IO status 0x%x\n", event)); + if (unlikely(!t || !t->lldd_task || !t->dev)) + return; + ts = &t->task_status; + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("port_id = %x,device_id = %x\n", + port_id, dev_id)); + switch (event) { + case IO_OVERFLOW: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n");) + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + ts->residual = 0; + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_INTERRUPTED; + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT" + "_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_EPROTO; + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + if (!t->uldd_task) + pm8001_handle_event(pm8001_ha, + pm8001_dev, + IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS); + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_BAD_DEST; + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_" + "NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_CONN_RATE; + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_WRONG_DEST; + break; + case IO_XFER_ERROR_NAK_RECEIVED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_XFER_ERROR_ACK_NAK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_XFER_ERROR_UNEXPECTED_PHASE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + break; + case IO_XFER_ERROR_XFER_RDY_OVERRUN: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + break; + case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + break; + case IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + break; + case IO_XFER_ERROR_OFFSET_MISMATCH: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + break; + case IO_XFER_ERROR_XFER_ZERO_DATA_LEN: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + break; + case IO_XFER_CMD_FRAME_ISSUED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" IO_XFER_CMD_FRAME_ISSUED\n")); + return; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", event)); + /* not allowed case. Therefore, return failed status */ + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + break; + } + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&t->task_state_lock, flags); + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with" + " event 0x%x resp 0x%x " + "stat 0x%x but aborted by upper layer!\n", + t, event, ts->resp, ts->stat)); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + } else { + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/* in order to force CPU ordering */ + t->task_done(t); + } +} + +/*See the comments for mpi_ssp_completion */ +static void +mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct sas_task *t; + struct pm8001_ccb_info *ccb; + unsigned long flags; + u32 param; + u32 status; + u32 tag; + struct sata_completion_resp *psataPayload; + struct task_status_struct *ts; + struct ata_task_resp *resp ; + u32 *sata_resp; + struct pm8001_device *pm8001_dev; + + psataPayload = (struct sata_completion_resp *)(piomb + 4); + status = le32_to_cpu(psataPayload->status); + tag = le32_to_cpu(psataPayload->tag); + + ccb = &pm8001_ha->ccb_info[tag]; + param = le32_to_cpu(psataPayload->param); + t = ccb->task; + ts = &t->task_status; + pm8001_dev = ccb->device; + if (status) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("sata IO status 0x%x\n", status)); + if (unlikely(!t || !t->lldd_task || !t->dev)) + return; + + switch (status) { + case IO_SUCCESS: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n")); + if (param == 0) { + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; + } else { + u8 len; + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_PROTO_RESPONSE; + ts->residual = param; + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("SAS_PROTO_RESPONSE len = %d\n", + param)); + sata_resp = &psataPayload->sata_resp[0]; + resp = (struct ata_task_resp *)ts->buf; + if (t->ata_task.dma_xfer == 0 && + t->data_dir == PCI_DMA_FROMDEVICE) { + len = sizeof(struct pio_setup_fis); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("PIO read len = %d\n", len)); + } else if (t->ata_task.use_ncq) { + len = sizeof(struct set_dev_bits_fis); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("FPDMA len = %d\n", len)); + } else { + len = sizeof(struct dev_to_host_fis); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("other len = %d\n", len)); + } + if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) { + resp->frame_len = len; + memcpy(&resp->ending_fis[0], sata_resp, len); + ts->buf_valid_size = sizeof(*resp); + } else + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("response to large \n")); + } + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_ABORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ABORTED IOMB Tag \n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + if (pm8001_dev) + pm8001_dev->running_req--; + break; + /* following cases are to do cases */ + case IO_UNDERFLOW: + /* SATA Completion with error */ + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_UNDERFLOW param = %d\n", param)); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_UNDERRUN; + ts->residual = param; + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_NO_DEVICE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_NO_DEVICE\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_PHY_DOWN; + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_INTERRUPTED; + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT" + "_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_EPROTO; + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_CONT0; + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + if (!t->uldd_task) { + pm8001_handle_event(pm8001_ha, + pm8001_dev, + IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_QUEUE_FULL; + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/*in order to force CPU ordering*/ + t->task_done(t); + return; + } + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_BAD_DEST; + if (!t->uldd_task) { + pm8001_handle_event(pm8001_ha, + pm8001_dev, + IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_QUEUE_FULL; + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/*ditto*/ + t->task_done(t); + return; + } + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_" + "NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_CONN_RATE; + break; + case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_STP_RESOURCES" + "_BUSY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + if (!t->uldd_task) { + pm8001_handle_event(pm8001_ha, + pm8001_dev, + IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_QUEUE_FULL; + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/* ditto*/ + t->task_done(t); + return; + } + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_WRONG_DEST; + break; + case IO_XFER_ERROR_NAK_RECEIVED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_ACK_NAK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_DMA: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_DMA\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + break; + case IO_XFER_ERROR_SATA_LINK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_SATA_LINK_TIMEOUT\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_XFER_ERROR_REJECTED_NCQ_MODE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_UNDERRUN; + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + case IO_PORT_IN_RESET: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_PORT_IN_RESET\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_DS_NON_OPERATIONAL: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_NON_OPERATIONAL\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + if (!t->uldd_task) { + pm8001_handle_event(pm8001_ha, pm8001_dev, + IO_DS_NON_OPERATIONAL); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_QUEUE_FULL; + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/*ditto*/ + t->task_done(t); + return; + } + break; + case IO_DS_IN_RECOVERY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" IO_DS_IN_RECOVERY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_DS_IN_ERROR: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_IN_ERROR\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + if (!t->uldd_task) { + pm8001_handle_event(pm8001_ha, pm8001_dev, + IO_DS_IN_ERROR); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_QUEUE_FULL; + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/*ditto*/ + t->task_done(t); + return; + } + break; + case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", status)); + /* not allowed case. Therefore, return failed status */ + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + } + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&t->task_state_lock, flags); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("task 0x%p done with io_status 0x%x" + " resp 0x%x stat 0x%x but aborted by upper layer!\n", + t, status, ts->resp, ts->stat)); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + } else { + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/* ditto */ + t->task_done(t); + } +} + +/*See the comments for mpi_ssp_completion */ +static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb) +{ + struct sas_task *t; + unsigned long flags; + struct task_status_struct *ts; + struct pm8001_ccb_info *ccb; + struct pm8001_device *pm8001_dev; + struct sata_event_resp *psataPayload = + (struct sata_event_resp *)(piomb + 4); + u32 event = le32_to_cpu(psataPayload->event); + u32 tag = le32_to_cpu(psataPayload->tag); + u32 port_id = le32_to_cpu(psataPayload->port_id); + u32 dev_id = le32_to_cpu(psataPayload->device_id); + + ccb = &pm8001_ha->ccb_info[tag]; + t = ccb->task; + pm8001_dev = ccb->device; + if (event) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("sata IO status 0x%x\n", event)); + if (unlikely(!t || !t->lldd_task || !t->dev)) + return; + ts = &t->task_status; + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("port_id = %x,device_id = %x\n", + port_id, dev_id)); + switch (event) { + case IO_OVERFLOW: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + ts->residual = 0; + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_INTERRUPTED; + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT" + "_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_EPROTO; + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_CONT0; + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + if (!t->uldd_task) { + pm8001_handle_event(pm8001_ha, + pm8001_dev, + IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_QUEUE_FULL; + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/*ditto*/ + t->task_done(t); + return; + } + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_BAD_DEST; + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_" + "NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_CONN_RATE; + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_WRONG_DEST; + break; + case IO_XFER_ERROR_NAK_RECEIVED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_PEER_ABORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PEER_ABORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_REJECTED_NCQ_MODE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_UNDERRUN; + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + case IO_XFER_ERROR_UNEXPECTED_PHASE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + case IO_XFER_ERROR_XFER_RDY_OVERRUN: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + case IO_XFER_ERROR_OFFSET_MISMATCH: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + case IO_XFER_ERROR_XFER_ZERO_DATA_LEN: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + case IO_XFER_CMD_FRAME_ISSUED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_CMD_FRAME_ISSUED\n")); + break; + case IO_XFER_PIO_SETUP_ERROR: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_PIO_SETUP_ERROR\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", event)); + /* not allowed case. Therefore, return failed status */ + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_TO; + break; + } + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&t->task_state_lock, flags); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("task 0x%p done with io_status 0x%x" + " resp 0x%x stat 0x%x but aborted by upper layer!\n", + t, event, ts->resp, ts->stat)); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + } else { + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/* in order to force CPU ordering */ + t->task_done(t); + } +} + +/*See the comments for mpi_ssp_completion */ +static void +mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 param; + struct sas_task *t; + struct pm8001_ccb_info *ccb; + unsigned long flags; + u32 status; + u32 tag; + struct smp_completion_resp *psmpPayload; + struct task_status_struct *ts; + struct pm8001_device *pm8001_dev; + + psmpPayload = (struct smp_completion_resp *)(piomb + 4); + status = le32_to_cpu(psmpPayload->status); + tag = le32_to_cpu(psmpPayload->tag); + + ccb = &pm8001_ha->ccb_info[tag]; + param = le32_to_cpu(psmpPayload->param); + t = ccb->task; + ts = &t->task_status; + pm8001_dev = ccb->device; + if (status) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("smp IO status 0x%x\n", status)); + if (unlikely(!t || !t->lldd_task || !t->dev)) + return; + + switch (status) { + case IO_SUCCESS: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_ABORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ABORTED IOMB\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_OVERFLOW: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + ts->residual = 0; + if (pm8001_dev) + pm8001_dev->running_req--; + break; + case IO_NO_DEVICE: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_NO_DEVICE\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_PHY_DOWN; + break; + case IO_ERROR_HW_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ERROR_HW_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_BUSY; + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_BUSY; + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_BUSY; + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_CONT0; + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + pm8001_handle_event(pm8001_ha, + pm8001_dev, + IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS); + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_BAD_DEST; + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_" + "NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_CONN_RATE; + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_WRONG_DEST; + break; + case IO_XFER_ERROR_RX_FRAME: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_RX_FRAME\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_ERROR_INTERNAL_SMP_RESOURCE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ERROR_INTERNAL_SMP_RESOURCE\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_QUEUE_FULL; + break; + case IO_PORT_IN_RESET: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_PORT_IN_RESET\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_DS_NON_OPERATIONAL: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_NON_OPERATIONAL\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_DS_IN_RECOVERY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_IN_RECOVERY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", status)); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DEV_NO_RESPONSE; + /* not allowed case. Therefore, return failed status */ + break; + } + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&t->task_state_lock, flags); + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with" + " io_status 0x%x resp 0x%x " + "stat 0x%x but aborted by upper layer!\n", + t, status, ts->resp, ts->stat)); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + } else { + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + mb();/* in order to force CPU ordering */ + t->task_done(t); + } +} + +static void +mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct set_dev_state_resp *pPayload = + (struct set_dev_state_resp *)(piomb + 4); + u32 tag = le32_to_cpu(pPayload->tag); + struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag]; + struct pm8001_device *pm8001_dev = ccb->device; + u32 status = le32_to_cpu(pPayload->status); + u32 device_id = le32_to_cpu(pPayload->device_id); + u8 pds = le32_to_cpu(pPayload->pds_nds) | PDS_BITS; + u8 nds = le32_to_cpu(pPayload->pds_nds) | NDS_BITS; + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set device id = 0x%x state " + "from 0x%x to 0x%x status = 0x%x!\n", + device_id, pds, nds, status)); + complete(pm8001_dev->setds_completion); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, tag); +} + +static void +mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct get_nvm_data_resp *pPayload = + (struct get_nvm_data_resp *)(piomb + 4); + u32 tag = le32_to_cpu(pPayload->tag); + struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag]; + u32 dlen_status = le32_to_cpu(pPayload->dlen_status); + complete(pm8001_ha->nvmd_completion); + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set nvm data complete!\n")); + if ((dlen_status & NVMD_STAT) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Set nvm data error!\n")); + return; + } + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, tag); +} + +static void +mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct fw_control_ex *fw_control_context; + struct get_nvm_data_resp *pPayload = + (struct get_nvm_data_resp *)(piomb + 4); + u32 tag = le32_to_cpu(pPayload->tag); + struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag]; + u32 dlen_status = le32_to_cpu(pPayload->dlen_status); + u32 ir_tds_bn_dps_das_nvm = + le32_to_cpu(pPayload->ir_tda_bn_dps_das_nvm); + void *virt_addr = pm8001_ha->memoryMap.region[NVMD].virt_ptr; + fw_control_context = ccb->fw_control_context; + + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Get nvm data complete!\n")); + if ((dlen_status & NVMD_STAT) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Get nvm data error!\n")); + complete(pm8001_ha->nvmd_completion); + return; + } + + if (ir_tds_bn_dps_das_nvm & IPMode) { + /* indirect mode - IR bit set */ + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Get NVMD success, IR=1\n")); + if ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == TWI_DEVICE) { + if (ir_tds_bn_dps_das_nvm == 0x80a80200) { + memcpy(pm8001_ha->sas_addr, + ((u8 *)virt_addr + 4), + SAS_ADDR_SIZE); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Get SAS address" + " from VPD successfully!\n")); + } + } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == C_SEEPROM) + || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == VPD_FLASH) || + ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == EXPAN_ROM)) { + ; + } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == AAP1_RDUMP) + || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == IOP_RDUMP)) { + ; + } else { + /* Should not be happened*/ + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("(IR=1)Wrong Device type 0x%x\n", + ir_tds_bn_dps_das_nvm)); + } + } else /* direct mode */{ + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Get NVMD success, IR=0, dataLen=%d\n", + (dlen_status & NVMD_LEN) >> 24)); + } + memcpy(fw_control_context->usrAddr, + pm8001_ha->memoryMap.region[NVMD].virt_ptr, + fw_control_context->len); + complete(pm8001_ha->nvmd_completion); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, tag); +} + +static int mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct local_phy_ctl_resp *pPayload = + (struct local_phy_ctl_resp *)(piomb + 4); + u32 status = le32_to_cpu(pPayload->status); + u32 phy_id = le32_to_cpu(pPayload->phyop_phyid) & ID_BITS; + u32 phy_op = le32_to_cpu(pPayload->phyop_phyid) & OP_BITS; + if (status != 0) { + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("%x phy execute %x phy op failed! \n", + phy_id, phy_op)); + } else + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("%x phy execute %x phy op success! \n", + phy_id, phy_op)); + return 0; +} + +/** + * pm8001_bytes_dmaed - one of the interface function communication with libsas + * @pm8001_ha: our hba card information + * @i: which phy that received the event. + * + * when HBA driver received the identify done event or initiate FIS received + * event(for SATA), it will invoke this function to notify the sas layer that + * the sas toplogy has formed, please discover the the whole sas domain, + * while receive a broadcast(change) primitive just tell the sas + * layer to discover the changed domain rather than the whole domain. + */ +static void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i) +{ + struct pm8001_phy *phy = &pm8001_ha->phy[i]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + struct sas_ha_struct *sas_ha; + if (!phy->phy_attached) + return; + + sas_ha = pm8001_ha->sas; + if (sas_phy->phy) { + struct sas_phy *sphy = sas_phy->phy; + sphy->negotiated_linkrate = sas_phy->linkrate; + sphy->minimum_linkrate = phy->minimum_linkrate; + sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + sphy->maximum_linkrate = phy->maximum_linkrate; + sphy->maximum_linkrate_hw = phy->maximum_linkrate; + } + + if (phy->phy_type & PORT_TYPE_SAS) { + struct sas_identify_frame *id; + id = (struct sas_identify_frame *)phy->frame_rcvd; + id->dev_type = phy->identify.device_type; + id->initiator_bits = SAS_PROTOCOL_ALL; + id->target_bits = phy->identify.target_port_protocols; + } else if (phy->phy_type & PORT_TYPE_SATA) { + /*Nothing*/ + } + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("phy %d byte dmaded.\n", i)); + + sas_phy->frame_rcvd_size = phy->frame_rcvd_size; + pm8001_ha->sas->notify_port_event(sas_phy, PORTE_BYTES_DMAED); +} + +/* Get the link rate speed */ +static void get_lrate_mode(struct pm8001_phy *phy, u8 link_rate) +{ + struct sas_phy *sas_phy = phy->sas_phy.phy; + + switch (link_rate) { + case PHY_SPEED_60: + phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; + break; + case PHY_SPEED_30: + phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + case PHY_SPEED_15: + phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + } + sas_phy->negotiated_linkrate = phy->sas_phy.linkrate; + sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_6_0_GBPS; + sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + sas_phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS; + sas_phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; +} + +/** + * asd_get_attached_sas_addr -- extract/generate attached SAS address + * @phy: pointer to asd_phy + * @sas_addr: pointer to buffer where the SAS address is to be written + * + * This function extracts the SAS address from an IDENTIFY frame + * received. If OOB is SATA, then a SAS address is generated from the + * HA tables. + * + * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame + * buffer. + */ +static void pm8001_get_attached_sas_addr(struct pm8001_phy *phy, + u8 *sas_addr) +{ + if (phy->sas_phy.frame_rcvd[0] == 0x34 + && phy->sas_phy.oob_mode == SATA_OOB_MODE) { + struct pm8001_hba_info *pm8001_ha = phy->sas_phy.ha->lldd_ha; + /* FIS device-to-host */ + u64 addr = be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr); + addr += phy->sas_phy.id; + *(__be64 *)sas_addr = cpu_to_be64(addr); + } else { + struct sas_identify_frame *idframe = + (void *) phy->sas_phy.frame_rcvd; + memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE); + } +} + +/** + * pm8001_hw_event_ack_req- For PM8001,some events need to acknowage to FW. + * @pm8001_ha: our hba card information + * @Qnum: the outbound queue message number. + * @SEA: source of event to ack + * @port_id: port id. + * @phyId: phy id. + * @param0: parameter 0. + * @param1: parameter 1. + */ +static void pm8001_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha, + u32 Qnum, u32 SEA, u32 port_id, u32 phyId, u32 param0, u32 param1) +{ + struct hw_event_ack_req payload; + u32 opc = OPC_INB_SAS_HW_EVENT_ACK; + + struct inbound_queue_table *circularQ; + + memset((u8 *)&payload, 0, sizeof(payload)); + circularQ = &pm8001_ha->inbnd_q_tbl[Qnum]; + payload.tag = 1; + payload.sea_phyid_portid = cpu_to_le32(((SEA & 0xFFFF) << 8) | + ((phyId & 0x0F) << 4) | (port_id & 0x0F)); + payload.param0 = cpu_to_le32(param0); + payload.param1 = cpu_to_le32(param1); + mpi_build_cmd(pm8001_ha, circularQ, opc, &payload); +} + +static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha, + u32 phyId, u32 phy_op); + +/** + * hw_event_sas_phy_up -FW tells me a SAS phy up event. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static void +hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct hw_event_resp *pPayload = + (struct hw_event_resp *)(piomb + 4); + u32 lr_evt_status_phyid_portid = + le32_to_cpu(pPayload->lr_evt_status_phyid_portid); + u8 link_rate = + (u8)((lr_evt_status_phyid_portid & 0xF0000000) >> 28); + u8 phy_id = + (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4); + struct sas_ha_struct *sas_ha = pm8001_ha->sas; + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + unsigned long flags; + u8 deviceType = pPayload->sas_identify.dev_type; + + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_SAS_PHY_UP \n")); + + switch (deviceType) { + case SAS_PHY_UNUSED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("device type no device.\n")); + break; + case SAS_END_DEVICE: + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("end device.\n")); + pm8001_chip_phy_ctl_req(pm8001_ha, phy_id, + PHY_NOTIFY_ENABLE_SPINUP); + get_lrate_mode(phy, link_rate); + break; + case SAS_EDGE_EXPANDER_DEVICE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("expander device.\n")); + get_lrate_mode(phy, link_rate); + break; + case SAS_FANOUT_EXPANDER_DEVICE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("fanout expander device.\n")); + get_lrate_mode(phy, link_rate); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("unkown device type(%x)\n", deviceType)); + break; + } + phy->phy_type |= PORT_TYPE_SAS; + phy->identify.device_type = deviceType; + phy->phy_attached = 1; + if (phy->identify.device_type == SAS_END_DEV) + phy->identify.target_port_protocols = SAS_PROTOCOL_SSP; + else if (phy->identify.device_type != NO_DEVICE) + phy->identify.target_port_protocols = SAS_PROTOCOL_SMP; + phy->sas_phy.oob_mode = SAS_OOB_MODE; + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE); + spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags); + memcpy(phy->frame_rcvd, &pPayload->sas_identify, + sizeof(struct sas_identify_frame)-4); + phy->frame_rcvd_size = sizeof(struct sas_identify_frame) - 4; + pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr); + spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags); + if (pm8001_ha->flags == PM8001F_RUN_TIME) + mdelay(200);/*delay a moment to wait disk to spinup*/ + pm8001_bytes_dmaed(pm8001_ha, phy_id); +} + +/** + * hw_event_sata_phy_up -FW tells me a SATA phy up event. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static void +hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct hw_event_resp *pPayload = + (struct hw_event_resp *)(piomb + 4); + u32 lr_evt_status_phyid_portid = + le32_to_cpu(pPayload->lr_evt_status_phyid_portid); + u8 link_rate = + (u8)((lr_evt_status_phyid_portid & 0xF0000000) >> 28); + u8 phy_id = + (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4); + struct sas_ha_struct *sas_ha = pm8001_ha->sas; + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + unsigned long flags; + get_lrate_mode(phy, link_rate); + phy->phy_type |= PORT_TYPE_SATA; + phy->phy_attached = 1; + phy->sas_phy.oob_mode = SATA_OOB_MODE; + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE); + spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags); + memcpy(phy->frame_rcvd, ((u8 *)&pPayload->sata_fis - 4), + sizeof(struct dev_to_host_fis)); + phy->frame_rcvd_size = sizeof(struct dev_to_host_fis); + phy->identify.target_port_protocols = SAS_PROTOCOL_SATA; + phy->identify.device_type = SATA_DEV; + pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr); + spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags); + pm8001_bytes_dmaed(pm8001_ha, phy_id); +} + +/** + * hw_event_phy_down -we should notify the libsas the phy is down. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static void +hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct hw_event_resp *pPayload = + (struct hw_event_resp *)(piomb + 4); + u32 lr_evt_status_phyid_portid = + le32_to_cpu(pPayload->lr_evt_status_phyid_portid); + u8 port_id = (u8)(lr_evt_status_phyid_portid & 0x0000000F); + u8 phy_id = + (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4); + u32 npip_portstate = le32_to_cpu(pPayload->npip_portstate); + u8 portstate = (u8)(npip_portstate & 0x0000000F); + + switch (portstate) { + case PORT_VALID: + break; + case PORT_INVALID: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" PortInvalid portID %d \n", port_id)); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" Last phy Down and port invalid\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN, + port_id, phy_id, 0, 0); + break; + case PORT_IN_RESET: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" PortInReset portID %d \n", port_id)); + break; + case PORT_NOT_ESTABLISHED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" phy Down and PORT_NOT_ESTABLISHED\n")); + break; + case PORT_LOSTCOMM: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" phy Down and PORT_LOSTCOMM\n")); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" Last phy Down and port invalid\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN, + port_id, phy_id, 0, 0); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" phy Down and(default) = %x\n", + portstate)); + break; + + } +} + +/** + * mpi_reg_resp -process register device ID response. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + * + * when sas layer find a device it will notify LLDD, then the driver register + * the domain device to FW, this event is the return device ID which the FW + * has assigned, from now,inter-communication with FW is no longer using the + * SAS address, use device ID which FW assigned. + */ +static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 status; + u32 device_id; + u32 htag; + struct pm8001_ccb_info *ccb; + struct pm8001_device *pm8001_dev; + struct dev_reg_resp *registerRespPayload = + (struct dev_reg_resp *)(piomb + 4); + + htag = le32_to_cpu(registerRespPayload->tag); + ccb = &pm8001_ha->ccb_info[registerRespPayload->tag]; + pm8001_dev = ccb->device; + status = le32_to_cpu(registerRespPayload->status); + device_id = le32_to_cpu(registerRespPayload->device_id); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" register device is status = %d\n", status)); + switch (status) { + case DEVREG_SUCCESS: + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("DEVREG_SUCCESS\n")); + pm8001_dev->device_id = device_id; + break; + case DEVREG_FAILURE_OUT_OF_RESOURCE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_OUT_OF_RESOURCE\n")); + break; + case DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED\n")); + break; + case DEVREG_FAILURE_INVALID_PHY_ID: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_INVALID_PHY_ID\n")); + break; + case DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED\n")); + break; + case DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE\n")); + break; + case DEVREG_FAILURE_PORT_NOT_VALID_STATE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_PORT_NOT_VALID_STATE\n")); + break; + case DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID\n")); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_UNSORPORTED\n")); + break; + } + complete(pm8001_dev->dcompletion); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, htag); + return 0; +} + +static int mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 status; + u32 device_id; + struct dev_reg_resp *registerRespPayload = + (struct dev_reg_resp *)(piomb + 4); + + status = le32_to_cpu(registerRespPayload->status); + device_id = le32_to_cpu(registerRespPayload->device_id); + if (status != 0) + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" deregister device failed ,status = %x" + ", device_id = %x\n", status, device_id)); + return 0; +} + +static int +mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 status; + struct fw_control_ex fw_control_context; + struct fw_flash_Update_resp *ppayload = + (struct fw_flash_Update_resp *)(piomb + 4); + u32 tag = le32_to_cpu(ppayload->tag); + struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag]; + status = le32_to_cpu(ppayload->status); + memcpy(&fw_control_context, + ccb->fw_control_context, + sizeof(fw_control_context)); + switch (status) { + case FLASH_UPDATE_COMPLETE_PENDING_REBOOT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_COMPLETE_PENDING_REBOOT\n")); + break; + case FLASH_UPDATE_IN_PROGRESS: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_IN_PROGRESS\n")); + break; + case FLASH_UPDATE_HDR_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_HDR_ERR\n")); + break; + case FLASH_UPDATE_OFFSET_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_OFFSET_ERR\n")); + break; + case FLASH_UPDATE_CRC_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_CRC_ERR\n")); + break; + case FLASH_UPDATE_LENGTH_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_LENGTH_ERR\n")); + break; + case FLASH_UPDATE_HW_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_HW_ERR\n")); + break; + case FLASH_UPDATE_DNLD_NOT_SUPPORTED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_DNLD_NOT_SUPPORTED\n")); + break; + case FLASH_UPDATE_DISABLED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_DISABLED\n")); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("No matched status = %d\n", status)); + break; + } + ccb->fw_control_context->fw_control->retcode = status; + pci_free_consistent(pm8001_ha->pdev, + fw_control_context.len, + fw_control_context.virtAddr, + fw_control_context.phys_addr); + complete(pm8001_ha->nvmd_completion); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, tag); + return 0; +} + +static int +mpi_general_event(struct pm8001_hba_info *pm8001_ha , void *piomb) +{ + u32 status; + int i; + struct general_event_resp *pPayload = + (struct general_event_resp *)(piomb + 4); + status = le32_to_cpu(pPayload->status); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" status = 0x%x\n", status)); + for (i = 0; i < GENERAL_EVENT_PAYLOAD; i++) + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("inb_IOMB_payload[0x%x] 0x%x, \n", i, + pPayload->inb_IOMB_payload[i])); + return 0; +} + +static int +mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct sas_task *t; + struct pm8001_ccb_info *ccb; + unsigned long flags; + u32 status ; + u32 tag, scp; + struct task_status_struct *ts; + + struct task_abort_resp *pPayload = + (struct task_abort_resp *)(piomb + 4); + ccb = &pm8001_ha->ccb_info[pPayload->tag]; + t = ccb->task; + + + status = le32_to_cpu(pPayload->status); + tag = le32_to_cpu(pPayload->tag); + scp = le32_to_cpu(pPayload->scp); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" status = 0x%x\n", status)); + if (t == NULL) + return -1; + ts = &t->task_status; + if (status != 0) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("task abort failed status 0x%x ," + "tag = 0x%x, scp= 0x%x\n", status, tag, scp)); + switch (status) { + case IO_SUCCESS: + PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; + break; + case IO_NOT_VALID: + PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_NOT_VALID\n")); + ts->resp = TMF_RESP_FUNC_FAILED; + break; + } + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, pPayload->tag); + mb(); + t->task_done(t); + return 0; +} + +/** + * mpi_hw_event -The hw event has come. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb) +{ + unsigned long flags; + struct hw_event_resp *pPayload = + (struct hw_event_resp *)(piomb + 4); + u32 lr_evt_status_phyid_portid = + le32_to_cpu(pPayload->lr_evt_status_phyid_portid); + u8 port_id = (u8)(lr_evt_status_phyid_portid & 0x0000000F); + u8 phy_id = + (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4); + u16 eventType = + (u16)((lr_evt_status_phyid_portid & 0x00FFFF00) >> 8); + u8 status = + (u8)((lr_evt_status_phyid_portid & 0x0F000000) >> 24); + struct sas_ha_struct *sas_ha = pm8001_ha->sas; + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("outbound queue HW event & event type : ")); + switch (eventType) { + case HW_EVENT_PHY_START_STATUS: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_START_STATUS" + " status = %x\n", status)); + if (status == 0) { + phy->phy_state = 1; + if (pm8001_ha->flags == PM8001F_RUN_TIME) + complete(phy->enable_completion); + } + break; + case HW_EVENT_SAS_PHY_UP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_START_STATUS \n")); + hw_event_sas_phy_up(pm8001_ha, piomb); + break; + case HW_EVENT_SATA_PHY_UP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_SATA_PHY_UP \n")); + hw_event_sata_phy_up(pm8001_ha, piomb); + break; + case HW_EVENT_PHY_STOP_STATUS: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_STOP_STATUS " + "status = %x\n", status)); + if (status == 0) + phy->phy_state = 0; + break; + case HW_EVENT_SATA_SPINUP_HOLD: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_SATA_SPINUP_HOLD \n")); + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD); + break; + case HW_EVENT_PHY_DOWN: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_DOWN \n")); + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL); + phy->phy_attached = 0; + phy->phy_state = 0; + hw_event_phy_down(pm8001_ha, piomb); + break; + case HW_EVENT_PORT_INVALID: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_INVALID\n")); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + /* the broadcast change primitive received, tell the LIBSAS this event + to revalidate the sas domain*/ + case HW_EVENT_BROADCAST_CHANGE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_BROADCAST_CHANGE\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_BROADCAST_CHANGE, + port_id, phy_id, 1, 0); + spin_lock_irqsave(&sas_phy->sas_prim_lock, flags); + sas_phy->sas_prim = HW_EVENT_BROADCAST_CHANGE; + spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags); + sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); + break; + case HW_EVENT_PHY_ERROR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_ERROR\n")); + sas_phy_disconnected(&phy->sas_phy); + phy->phy_attached = 0; + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR); + break; + case HW_EVENT_BROADCAST_EXP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_BROADCAST_EXP\n")); + spin_lock_irqsave(&sas_phy->sas_prim_lock, flags); + sas_phy->sas_prim = HW_EVENT_BROADCAST_EXP; + spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags); + sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); + break; + case HW_EVENT_LINK_ERR_INVALID_DWORD: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_LINK_ERR_DISPARITY_ERROR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_DISPARITY_ERROR\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_DISPARITY_ERROR, + port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_LINK_ERR_CODE_VIOLATION: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_CODE_VIOLATION\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_CODE_VIOLATION, + port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH, + port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_MALFUNCTION: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_MALFUNCTION\n")); + break; + case HW_EVENT_BROADCAST_SES: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_BROADCAST_SES\n")); + spin_lock_irqsave(&sas_phy->sas_prim_lock, flags); + sas_phy->sas_prim = HW_EVENT_BROADCAST_SES; + spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags); + sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); + break; + case HW_EVENT_INBOUND_CRC_ERROR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_INBOUND_CRC_ERROR\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_INBOUND_CRC_ERROR, + port_id, phy_id, 0, 0); + break; + case HW_EVENT_HARD_RESET_RECEIVED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_HARD_RESET_RECEIVED\n")); + sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET); + break; + case HW_EVENT_ID_FRAME_TIMEOUT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_ID_FRAME_TIMEOUT\n")); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_LINK_ERR_PHY_RESET_FAILED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_PHY_RESET_FAILED \n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_PHY_RESET_FAILED, + port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_PORT_RESET_TIMER_TMO: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_RESET_TIMER_TMO \n")); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_PORT_RECOVERY_TIMER_TMO: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_RECOVERY_TIMER_TMO \n")); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_PORT_RECOVER: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_RECOVER \n")); + break; + case HW_EVENT_PORT_RESET_COMPLETE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_RESET_COMPLETE \n")); + break; + case EVENT_BROADCAST_ASYNCH_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("EVENT_BROADCAST_ASYNCH_EVENT\n")); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Unknown event type = %x\n", eventType)); + break; + } + return 0; +} + +/** + * process_one_iomb - process one outbound Queue memory block + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 pHeader = (u32)*(u32 *)piomb; + u8 opc = (u8)((le32_to_cpu(pHeader)) & 0xFFF); + + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("process_one_iomb:")); + + switch (opc) { + case OPC_OUB_ECHO: + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("OPC_OUB_ECHO \n")); + break; + case OPC_OUB_HW_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_HW_EVENT \n")); + mpi_hw_event(pm8001_ha, piomb); + break; + case OPC_OUB_SSP_COMP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SSP_COMP \n")); + mpi_ssp_completion(pm8001_ha, piomb); + break; + case OPC_OUB_SMP_COMP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SMP_COMP \n")); + mpi_smp_completion(pm8001_ha, piomb); + break; + case OPC_OUB_LOCAL_PHY_CNTRL: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_LOCAL_PHY_CNTRL\n")); + mpi_local_phy_ctl(pm8001_ha, piomb); + break; + case OPC_OUB_DEV_REGIST: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_DEV_REGIST \n")); + mpi_reg_resp(pm8001_ha, piomb); + break; + case OPC_OUB_DEREG_DEV: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("unresgister the deviece \n")); + mpi_dereg_resp(pm8001_ha, piomb); + break; + case OPC_OUB_GET_DEV_HANDLE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GET_DEV_HANDLE \n")); + break; + case OPC_OUB_SATA_COMP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SATA_COMP \n")); + mpi_sata_completion(pm8001_ha, piomb); + break; + case OPC_OUB_SATA_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SATA_EVENT \n")); + mpi_sata_event(pm8001_ha, piomb); + break; + case OPC_OUB_SSP_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SSP_EVENT\n")); + mpi_ssp_event(pm8001_ha, piomb); + break; + case OPC_OUB_DEV_HANDLE_ARRIV: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_DEV_HANDLE_ARRIV\n")); + /*This is for target*/ + break; + case OPC_OUB_SSP_RECV_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SSP_RECV_EVENT\n")); + /*This is for target*/ + break; + case OPC_OUB_DEV_INFO: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_DEV_INFO\n")); + break; + case OPC_OUB_FW_FLASH_UPDATE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_FW_FLASH_UPDATE\n")); + mpi_fw_flash_update_resp(pm8001_ha, piomb); + break; + case OPC_OUB_GPIO_RESPONSE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GPIO_RESPONSE\n")); + break; + case OPC_OUB_GPIO_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GPIO_EVENT\n")); + break; + case OPC_OUB_GENERAL_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GENERAL_EVENT\n")); + mpi_general_event(pm8001_ha, piomb); + break; + case OPC_OUB_SSP_ABORT_RSP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SSP_ABORT_RSP\n")); + mpi_task_abort_resp(pm8001_ha, piomb); + break; + case OPC_OUB_SATA_ABORT_RSP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SATA_ABORT_RSP\n")); + mpi_task_abort_resp(pm8001_ha, piomb); + break; + case OPC_OUB_SAS_DIAG_MODE_START_END: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SAS_DIAG_MODE_START_END\n")); + break; + case OPC_OUB_SAS_DIAG_EXECUTE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SAS_DIAG_EXECUTE\n")); + break; + case OPC_OUB_GET_TIME_STAMP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GET_TIME_STAMP\n")); + break; + case OPC_OUB_SAS_HW_EVENT_ACK: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SAS_HW_EVENT_ACK\n")); + break; + case OPC_OUB_PORT_CONTROL: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_PORT_CONTROL\n")); + break; + case OPC_OUB_SMP_ABORT_RSP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SMP_ABORT_RSP\n")); + mpi_task_abort_resp(pm8001_ha, piomb); + break; + case OPC_OUB_GET_NVMD_DATA: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GET_NVMD_DATA\n")); + mpi_get_nvmd_resp(pm8001_ha, piomb); + break; + case OPC_OUB_SET_NVMD_DATA: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SET_NVMD_DATA\n")); + mpi_set_nvmd_resp(pm8001_ha, piomb); + break; + case OPC_OUB_DEVICE_HANDLE_REMOVAL: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_DEVICE_HANDLE_REMOVAL\n")); + break; + case OPC_OUB_SET_DEVICE_STATE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SET_DEVICE_STATE\n")); + mpi_set_dev_state_resp(pm8001_ha, piomb); + break; + case OPC_OUB_GET_DEVICE_STATE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GET_DEVICE_STATE\n")); + break; + case OPC_OUB_SET_DEV_INFO: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SET_DEV_INFO\n")); + break; + case OPC_OUB_SAS_RE_INITIALIZE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SAS_RE_INITIALIZE\n")); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Unknown outbound Queue IOMB OPC = %x\n", + opc)); + break; + } +} + +static int process_oq(struct pm8001_hba_info *pm8001_ha) +{ + struct outbound_queue_table *circularQ; + void *pMsg1 = NULL; + u8 bc = 0; + u32 ret = MPI_IO_STATUS_FAIL; + + circularQ = &pm8001_ha->outbnd_q_tbl[0]; + do { + ret = mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc); + if (MPI_IO_STATUS_SUCCESS == ret) { + /* process the outbound message */ + process_one_iomb(pm8001_ha, (void *)(pMsg1 - 4)); + /* free the message from the outbound circular buffer */ + mpi_msg_free_set(pm8001_ha, pMsg1, circularQ, bc); + } + if (MPI_IO_STATUS_BUSY == ret) { + u32 producer_idx; + /* Update the producer index from SPC */ + producer_idx = pm8001_read_32(circularQ->pi_virt); + circularQ->producer_index = cpu_to_le32(producer_idx); + if (circularQ->producer_index == + circularQ->consumer_idx) + /* OQ is empty */ + break; + } + } while (1); + return ret; +} + +/* PCI_DMA_... to our direction translation. */ +static const u8 data_dir_flags[] = { + [PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT,/* UNSPECIFIED */ + [PCI_DMA_TODEVICE] = DATA_DIR_OUT,/* OUTBOUND */ + [PCI_DMA_FROMDEVICE] = DATA_DIR_IN,/* INBOUND */ + [PCI_DMA_NONE] = DATA_DIR_NONE,/* NO TRANSFER */ +}; +static void +pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd) +{ + int i; + struct scatterlist *sg; + struct pm8001_prd *buf_prd = prd; + + for_each_sg(scatter, sg, nr, i) { + buf_prd->addr = cpu_to_le64(sg_dma_address(sg)); + buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg)); + buf_prd->im_len.e = 0; + buf_prd++; + } +} + +static void build_smp_cmd(u32 deviceID, u32 hTag, struct smp_req *psmp_cmd) +{ + psmp_cmd->tag = cpu_to_le32(hTag); + psmp_cmd->device_id = cpu_to_le32(deviceID); + psmp_cmd->len_ip_ir = cpu_to_le32(1|(1 << 1)); +} + +/** + * pm8001_chip_smp_req - send a SMP task to FW + * @pm8001_ha: our hba card information. + * @ccb: the ccb information this request used. + */ +static int pm8001_chip_smp_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + int elem, rc; + struct sas_task *task = ccb->task; + struct domain_device *dev = task->dev; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct scatterlist *sg_req, *sg_resp; + u32 req_len, resp_len; + struct smp_req smp_cmd; + u32 opc; + struct inbound_queue_table *circularQ; + + memset(&smp_cmd, 0, sizeof(smp_cmd)); + /* + * DMA-map SMP request, response buffers + */ + sg_req = &task->smp_task.smp_req; + elem = dma_map_sg(pm8001_ha->dev, sg_req, 1, PCI_DMA_TODEVICE); + if (!elem) + return -ENOMEM; + req_len = sg_dma_len(sg_req); + + sg_resp = &task->smp_task.smp_resp; + elem = dma_map_sg(pm8001_ha->dev, sg_resp, 1, PCI_DMA_FROMDEVICE); + if (!elem) { + rc = -ENOMEM; + goto err_out; + } + resp_len = sg_dma_len(sg_resp); + /* must be in dwords */ + if ((req_len & 0x3) || (resp_len & 0x3)) { + rc = -EINVAL; + goto err_out_2; + } + + opc = OPC_INB_SMP_REQUEST; + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + smp_cmd.tag = cpu_to_le32(ccb->ccb_tag); + smp_cmd.long_smp_req.long_req_addr = + cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req)); + smp_cmd.long_smp_req.long_req_size = + cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4); + smp_cmd.long_smp_req.long_resp_addr = + cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp)); + smp_cmd.long_smp_req.long_resp_size = + cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4); + build_smp_cmd(pm8001_dev->device_id, smp_cmd.tag, &smp_cmd); + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&smp_cmd); + return 0; + +err_out_2: + dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_resp, 1, + PCI_DMA_FROMDEVICE); +err_out: + dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_req, 1, + PCI_DMA_TODEVICE); + return rc; +} + +/** + * pm8001_chip_ssp_io_req - send a SSP task to FW + * @pm8001_ha: our hba card information. + * @ccb: the ccb information this request used. + */ +static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + struct sas_task *task = ccb->task; + struct domain_device *dev = task->dev; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct ssp_ini_io_start_req ssp_cmd; + u32 tag = ccb->ccb_tag; + int ret; + __le64 phys_addr; + struct inbound_queue_table *circularQ; + u32 opc = OPC_INB_SSPINIIOSTART; + memset(&ssp_cmd, 0, sizeof(ssp_cmd)); + memcpy(ssp_cmd.ssp_iu.lun, task->ssp_task.LUN, 8); + ssp_cmd.dir_m_tlr = data_dir_flags[task->data_dir] << 8 | 0x0;/*0 for + SAS 1.1 compatible TLR*/ + ssp_cmd.data_len = cpu_to_le32(task->total_xfer_len); + ssp_cmd.device_id = cpu_to_le32(pm8001_dev->device_id); + ssp_cmd.tag = cpu_to_le32(tag); + if (task->ssp_task.enable_first_burst) + ssp_cmd.ssp_iu.efb_prio_attr |= 0x80; + ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_prio << 3); + ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7); + memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cdb, 16); + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + + /* fill in PRD (scatter/gather) table, if any */ + if (task->num_scatter > 1) { + pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd); + phys_addr = cpu_to_le64(ccb->ccb_dma_handle + + offsetof(struct pm8001_ccb_info, buf_prd[0])); + ssp_cmd.addr_low = lower_32_bits(phys_addr); + ssp_cmd.addr_high = upper_32_bits(phys_addr); + ssp_cmd.esgl = cpu_to_le32(1<<31); + } else if (task->num_scatter == 1) { + __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter)); + ssp_cmd.addr_low = lower_32_bits(dma_addr); + ssp_cmd.addr_high = upper_32_bits(dma_addr); + ssp_cmd.len = cpu_to_le32(task->total_xfer_len); + ssp_cmd.esgl = 0; + } else if (task->num_scatter == 0) { + ssp_cmd.addr_low = 0; + ssp_cmd.addr_high = 0; + ssp_cmd.len = cpu_to_le32(task->total_xfer_len); + ssp_cmd.esgl = 0; + } + ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &ssp_cmd); + return ret; +} + +static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + struct sas_task *task = ccb->task; + struct domain_device *dev = task->dev; + struct pm8001_device *pm8001_ha_dev = dev->lldd_dev; + u32 tag = ccb->ccb_tag; + int ret; + struct sata_start_req sata_cmd; + u32 hdr_tag, ncg_tag = 0; + __le64 phys_addr; + u32 ATAP = 0x0; + u32 dir; + struct inbound_queue_table *circularQ; + u32 opc = OPC_INB_SATA_HOST_OPSTART; + memset(&sata_cmd, 0, sizeof(sata_cmd)); + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + if (task->data_dir == PCI_DMA_NONE) { + ATAP = 0x04; /* no data*/ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("no data \n")); + } else if (likely(!task->ata_task.device_control_reg_update)) { + if (task->ata_task.dma_xfer) { + ATAP = 0x06; /* DMA */ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("DMA \n")); + } else { + ATAP = 0x05; /* PIO*/ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("PIO \n")); + } + if (task->ata_task.use_ncq && + dev->sata_dev.command_set != ATAPI_COMMAND_SET) { + ATAP = 0x07; /* FPDMA */ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA \n")); + } + } + if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag)) + ncg_tag = cpu_to_le32(hdr_tag); + dir = data_dir_flags[task->data_dir] << 8; + sata_cmd.tag = cpu_to_le32(tag); + sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id); + sata_cmd.data_len = cpu_to_le32(task->total_xfer_len); + sata_cmd.ncqtag_atap_dir_m = + cpu_to_le32(((ncg_tag & 0xff)<<16)|((ATAP & 0x3f) << 10) | dir); + sata_cmd.sata_fis = task->ata_task.fis; + if (likely(!task->ata_task.device_control_reg_update)) + sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */ + sata_cmd.sata_fis.flags &= 0xF0;/* PM_PORT field shall be 0 */ + /* fill in PRD (scatter/gather) table, if any */ + if (task->num_scatter > 1) { + pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd); + phys_addr = cpu_to_le64(ccb->ccb_dma_handle + + offsetof(struct pm8001_ccb_info, buf_prd[0])); + sata_cmd.addr_low = lower_32_bits(phys_addr); + sata_cmd.addr_high = upper_32_bits(phys_addr); + sata_cmd.esgl = cpu_to_le32(1 << 31); + } else if (task->num_scatter == 1) { + __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter)); + sata_cmd.addr_low = lower_32_bits(dma_addr); + sata_cmd.addr_high = upper_32_bits(dma_addr); + sata_cmd.len = cpu_to_le32(task->total_xfer_len); + sata_cmd.esgl = 0; + } else if (task->num_scatter == 0) { + sata_cmd.addr_low = 0; + sata_cmd.addr_high = 0; + sata_cmd.len = cpu_to_le32(task->total_xfer_len); + sata_cmd.esgl = 0; + } + ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd); + return ret; +} + +/** + * pm8001_chip_phy_start_req - start phy via PHY_START COMMAND + * @pm8001_ha: our hba card information. + * @num: the inbound queue number + * @phy_id: the phy id which we wanted to start up. + */ +static int +pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id) +{ + struct phy_start_req payload; + struct inbound_queue_table *circularQ; + int ret; + u32 tag = 0x01; + u32 opcode = OPC_INB_PHYSTART; + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + memset(&payload, 0, sizeof(payload)); + payload.tag = cpu_to_le32(tag); + /* + ** [0:7] PHY Identifier + ** [8:11] link rate 1.5G, 3G, 6G + ** [12:13] link mode 01b SAS mode; 10b SATA mode; 11b both + ** [14] 0b disable spin up hold; 1b enable spin up hold + */ + payload.ase_sh_lm_slr_phyid = cpu_to_le32(SPINHOLD_DISABLE | + LINKMODE_AUTO | LINKRATE_15 | + LINKRATE_30 | LINKRATE_60 | phy_id); + payload.sas_identify.dev_type = SAS_END_DEV; + payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL; + memcpy(payload.sas_identify.sas_addr, + pm8001_ha->sas_addr, SAS_ADDR_SIZE); + payload.sas_identify.phy_id = phy_id; + ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload); + return ret; +} + +/** + * pm8001_chip_phy_stop_req - start phy via PHY_STOP COMMAND + * @pm8001_ha: our hba card information. + * @num: the inbound queue number + * @phy_id: the phy id which we wanted to start up. + */ +static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha, + u8 phy_id) +{ + struct phy_stop_req payload; + struct inbound_queue_table *circularQ; + int ret; + u32 tag = 0x01; + u32 opcode = OPC_INB_PHYSTOP; + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + memset(&payload, 0, sizeof(payload)); + payload.tag = cpu_to_le32(tag); + payload.phy_id = cpu_to_le32(phy_id); + ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload); + return ret; +} + +/** + * see comments on mpi_reg_resp. + */ +static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u32 flag) +{ + struct reg_dev_req payload; + u32 opc; + u32 stp_sspsmp_sata = 0x4; + struct inbound_queue_table *circularQ; + u32 linkrate, phy_id; + int rc, tag = 0xdeadbeef; + struct pm8001_ccb_info *ccb; + u8 retryFlag = 0x1; + u16 firstBurstSize = 0; + u16 ITNT = 2000; + struct domain_device *dev = pm8001_dev->sas_device; + struct domain_device *parent_dev = dev->parent; + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + + memset(&payload, 0, sizeof(payload)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->device = pm8001_dev; + ccb->ccb_tag = tag; + payload.tag = cpu_to_le32(tag); + if (flag == 1) + stp_sspsmp_sata = 0x02; /*direct attached sata */ + else { + if (pm8001_dev->dev_type == SATA_DEV) + stp_sspsmp_sata = 0x00; /* stp*/ + else if (pm8001_dev->dev_type == SAS_END_DEV || + pm8001_dev->dev_type == EDGE_DEV || + pm8001_dev->dev_type == FANOUT_DEV) + stp_sspsmp_sata = 0x01; /*ssp or smp*/ + } + if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) + phy_id = parent_dev->ex_dev.ex_phy->phy_id; + else + phy_id = pm8001_dev->attached_phy; + opc = OPC_INB_REG_DEV; + linkrate = (pm8001_dev->sas_device->linkrate < dev->port->linkrate) ? + pm8001_dev->sas_device->linkrate : dev->port->linkrate; + payload.phyid_portid = + cpu_to_le32(((pm8001_dev->sas_device->port->id) & 0x0F) | + ((phy_id & 0x0F) << 4)); + payload.dtype_dlr_retry = cpu_to_le32((retryFlag & 0x01) | + ((linkrate & 0x0F) * 0x1000000) | + ((stp_sspsmp_sata & 0x03) * 0x10000000)); + payload.firstburstsize_ITNexustimeout = + cpu_to_le32(ITNT | (firstBurstSize * 0x10000)); + memcpy(&payload.sas_addr_hi, pm8001_dev->sas_device->sas_addr, + SAS_ADDR_SIZE); + rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload); + return rc; +} + +/** + * see comments on mpi_reg_resp. + */ +static int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha, + u32 device_id) +{ + struct dereg_dev_req payload; + u32 opc = OPC_INB_DEREG_DEV_HANDLE; + int ret; + struct inbound_queue_table *circularQ; + + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + memset(&payload, 0, sizeof(payload)); + payload.tag = 1; + payload.device_id = cpu_to_le32(device_id); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("unregister device device_id = %d\n", device_id)); + ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload); + return ret; +} + +/** + * pm8001_chip_phy_ctl_req - support the local phy operation + * @pm8001_ha: our hba card information. + * @num: the inbound queue number + * @phy_id: the phy id which we wanted to operate + * @phy_op: + */ +static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha, + u32 phyId, u32 phy_op) +{ + struct local_phy_ctl_req payload; + struct inbound_queue_table *circularQ; + int ret; + u32 opc = OPC_INB_LOCAL_PHY_CONTROL; + memset((u8 *)&payload, 0, sizeof(payload)); + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + payload.tag = 1; + payload.phyop_phyid = + cpu_to_le32(((phy_op & 0xff) << 8) | (phyId & 0x0F)); + ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload); + return ret; +} + +static u32 pm8001_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha) +{ + u32 value; +#ifdef PM8001_USE_MSIX + return 1; +#endif + value = pm8001_cr32(pm8001_ha, 0, MSGU_ODR); + if (value) + return 1; + return 0; + +} + +/** + * pm8001_chip_isr - PM8001 isr handler. + * @pm8001_ha: our hba card information. + * @irq: irq number. + * @stat: stat. + */ +static irqreturn_t +pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha) +{ + unsigned long flags; + spin_lock_irqsave(&pm8001_ha->lock, flags); + pm8001_chip_interrupt_disable(pm8001_ha); + process_oq(pm8001_ha); + pm8001_chip_interrupt_enable(pm8001_ha); + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return IRQ_HANDLED; +} + +static int send_task_abort(struct pm8001_hba_info *pm8001_ha, u32 opc, + u32 dev_id, u8 flag, u32 task_tag, u32 cmd_tag) +{ + struct task_abort_req task_abort; + struct inbound_queue_table *circularQ; + int ret; + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + memset(&task_abort, 0, sizeof(task_abort)); + if (ABORT_SINGLE == (flag & ABORT_MASK)) { + task_abort.abort_all = 0; + task_abort.device_id = cpu_to_le32(dev_id); + task_abort.tag_to_abort = cpu_to_le32(task_tag); + task_abort.tag = cpu_to_le32(cmd_tag); + } else if (ABORT_ALL == (flag & ABORT_MASK)) { + task_abort.abort_all = cpu_to_le32(1); + task_abort.device_id = cpu_to_le32(dev_id); + task_abort.tag = cpu_to_le32(cmd_tag); + } + ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort); + return ret; +} + +/** + * pm8001_chip_abort_task - SAS abort task when error or exception happened. + * @task: the task we wanted to aborted. + * @flag: the abort flag. + */ +static int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, u32 cmd_tag) +{ + u32 opc, device_id; + int rc = TMF_RESP_FUNC_FAILED; + PM8001_EH_DBG(pm8001_ha, pm8001_printk("cmd_tag = %x, abort task tag" + " = %x", cmd_tag, task_tag)); + if (pm8001_dev->dev_type == SAS_END_DEV) + opc = OPC_INB_SSP_ABORT; + else if (pm8001_dev->dev_type == SATA_DEV) + opc = OPC_INB_SATA_ABORT; + else + opc = OPC_INB_SMP_ABORT;/* SMP */ + device_id = pm8001_dev->device_id; + rc = send_task_abort(pm8001_ha, opc, device_id, flag, + task_tag, cmd_tag); + if (rc != TMF_RESP_FUNC_COMPLETE) + PM8001_EH_DBG(pm8001_ha, pm8001_printk("rc= %d\n", rc)); + return rc; +} + +/** + * pm8001_chip_ssp_tm_req - built the task managment command. + * @pm8001_ha: our hba card information. + * @ccb: the ccb information. + * @tmf: task management function. + */ +static int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf) +{ + struct sas_task *task = ccb->task; + struct domain_device *dev = task->dev; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + u32 opc = OPC_INB_SSPINITMSTART; + struct inbound_queue_table *circularQ; + struct ssp_ini_tm_start_req sspTMCmd; + int ret; + + memset(&sspTMCmd, 0, sizeof(sspTMCmd)); + sspTMCmd.device_id = cpu_to_le32(pm8001_dev->device_id); + sspTMCmd.relate_tag = cpu_to_le32(tmf->tag_of_task_to_be_managed); + sspTMCmd.tmf = cpu_to_le32(tmf->tmf); + memcpy(sspTMCmd.lun, task->ssp_task.LUN, 8); + sspTMCmd.tag = cpu_to_le32(ccb->ccb_tag); + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sspTMCmd); + return ret; +} + +static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha, + void *payload) +{ + u32 opc = OPC_INB_GET_NVMD_DATA; + u32 nvmd_type; + int rc; + u32 tag; + struct pm8001_ccb_info *ccb; + struct inbound_queue_table *circularQ; + struct get_nvm_data_req nvmd_req; + struct fw_control_ex *fw_control_context; + struct pm8001_ioctl_payload *ioctl_payload = payload; + + nvmd_type = ioctl_payload->minor_function; + fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL); + fw_control_context->usrAddr = (u8 *)&ioctl_payload->func_specific[0]; + fw_control_context->len = ioctl_payload->length; + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + memset(&nvmd_req, 0, sizeof(nvmd_req)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->ccb_tag = tag; + ccb->fw_control_context = fw_control_context; + nvmd_req.tag = cpu_to_le32(tag); + + switch (nvmd_type) { + case TWI_DEVICE: { + u32 twi_addr, twi_page_size; + twi_addr = 0xa8; + twi_page_size = 2; + + nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 | + twi_page_size << 8 | TWI_DEVICE); + nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length); + nvmd_req.resp_addr_hi = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + break; + } + case C_SEEPROM: { + nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM); + nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length); + nvmd_req.resp_addr_hi = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + break; + } + case VPD_FLASH: { + nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH); + nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length); + nvmd_req.resp_addr_hi = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + break; + } + case EXPAN_ROM: { + nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM); + nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length); + nvmd_req.resp_addr_hi = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + break; + } + default: + break; + } + rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req); + return rc; +} + +static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha, + void *payload) +{ + u32 opc = OPC_INB_SET_NVMD_DATA; + u32 nvmd_type; + int rc; + u32 tag; + struct pm8001_ccb_info *ccb; + struct inbound_queue_table *circularQ; + struct set_nvm_data_req nvmd_req; + struct fw_control_ex *fw_control_context; + struct pm8001_ioctl_payload *ioctl_payload = payload; + + nvmd_type = ioctl_payload->minor_function; + fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL); + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr, + ioctl_payload->func_specific, + ioctl_payload->length); + memset(&nvmd_req, 0, sizeof(nvmd_req)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->fw_control_context = fw_control_context; + ccb->ccb_tag = tag; + nvmd_req.tag = cpu_to_le32(tag); + switch (nvmd_type) { + case TWI_DEVICE: { + u32 twi_addr, twi_page_size; + twi_addr = 0xa8; + twi_page_size = 2; + nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98); + nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 | + twi_page_size << 8 | TWI_DEVICE); + nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length); + nvmd_req.resp_addr_hi = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + break; + } + case C_SEEPROM: + nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM); + nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length); + nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98); + nvmd_req.resp_addr_hi = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + break; + case VPD_FLASH: + nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH); + nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length); + nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98); + nvmd_req.resp_addr_hi = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + break; + case EXPAN_ROM: + nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM); + nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length); + nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98); + nvmd_req.resp_addr_hi = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = + cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + break; + default: + break; + } + rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req); + return rc; +} + +/** + * pm8001_chip_fw_flash_update_build - support the firmware update operation + * @pm8001_ha: our hba card information. + * @fw_flash_updata_info: firmware flash update param + */ +static int +pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha, + void *fw_flash_updata_info, u32 tag) +{ + struct fw_flash_Update_req payload; + struct fw_flash_updata_info *info; + struct inbound_queue_table *circularQ; + int ret; + u32 opc = OPC_INB_FW_FLASH_UPDATE; + + memset(&payload, 0, sizeof(struct fw_flash_Update_req)); + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + info = fw_flash_updata_info; + payload.tag = cpu_to_le32(tag); + payload.cur_image_len = cpu_to_le32(info->cur_image_len); + payload.cur_image_offset = cpu_to_le32(info->cur_image_offset); + payload.total_image_len = cpu_to_le32(info->total_image_len); + payload.len = info->sgl.im_len.len ; + payload.sgl_addr_lo = lower_32_bits(info->sgl.addr); + payload.sgl_addr_hi = upper_32_bits(info->sgl.addr); + ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload); + return ret; +} + +static int +pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha, + void *payload) +{ + struct fw_flash_updata_info flash_update_info; + struct fw_control_info *fw_control; + struct fw_control_ex *fw_control_context; + int rc; + u32 tag; + struct pm8001_ccb_info *ccb; + void *buffer = NULL; + dma_addr_t phys_addr; + u32 phys_addr_hi; + u32 phys_addr_lo; + struct pm8001_ioctl_payload *ioctl_payload = payload; + + fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL); + fw_control = (struct fw_control_info *)&ioctl_payload->func_specific[0]; + if (fw_control->len != 0) { + if (pm8001_mem_alloc(pm8001_ha->pdev, + (void **)&buffer, + &phys_addr, + &phys_addr_hi, + &phys_addr_lo, + fw_control->len, 0) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Mem alloc failure\n")); + return -ENOMEM; + } + } + memset(buffer, 0, fw_control->len); + memcpy(buffer, fw_control->buffer, fw_control->len); + flash_update_info.sgl.addr = cpu_to_le64(phys_addr); + flash_update_info.sgl.im_len.len = cpu_to_le32(fw_control->len); + flash_update_info.sgl.im_len.e = 0; + flash_update_info.cur_image_offset = fw_control->offset; + flash_update_info.cur_image_len = fw_control->len; + flash_update_info.total_image_len = fw_control->size; + fw_control_context->fw_control = fw_control; + fw_control_context->virtAddr = buffer; + fw_control_context->len = fw_control->len; + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->fw_control_context = fw_control_context; + ccb->ccb_tag = tag; + rc = pm8001_chip_fw_flash_update_build(pm8001_ha, &flash_update_info, + tag); + return rc; +} + +static int +pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u32 state) +{ + struct set_dev_state_req payload; + struct inbound_queue_table *circularQ; + struct pm8001_ccb_info *ccb; + int rc; + u32 tag; + u32 opc = OPC_INB_SET_DEVICE_STATE; + memset(&payload, 0, sizeof(payload)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return -1; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->ccb_tag = tag; + ccb->device = pm8001_dev; + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + payload.tag = cpu_to_le32(tag); + payload.device_id = cpu_to_le32(pm8001_dev->device_id); + payload.nds = cpu_to_le32(state); + rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload); + return rc; + +} + +static int +pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha) +{ + struct sas_re_initialization_req payload; + struct inbound_queue_table *circularQ; + struct pm8001_ccb_info *ccb; + int rc; + u32 tag; + u32 opc = OPC_INB_SAS_RE_INITIALIZE; + memset(&payload, 0, sizeof(payload)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return -1; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->ccb_tag = tag; + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + payload.tag = cpu_to_le32(tag); + payload.SSAHOLT = cpu_to_le32(0xd << 25); + payload.sata_hol_tmo = cpu_to_le32(80); + payload.open_reject_cmdretries_data_retries = cpu_to_le32(0xff00ff); + rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload); + return rc; + +} + +const struct pm8001_dispatch pm8001_8001_dispatch = { + .name = "pmc8001", + .chip_init = pm8001_chip_init, + .chip_soft_rst = pm8001_chip_soft_rst, + .chip_rst = pm8001_hw_chip_rst, + .chip_iounmap = pm8001_chip_iounmap, + .isr = pm8001_chip_isr, + .is_our_interupt = pm8001_chip_is_our_interupt, + .isr_process_oq = process_oq, + .interrupt_enable = pm8001_chip_interrupt_enable, + .interrupt_disable = pm8001_chip_interrupt_disable, + .make_prd = pm8001_chip_make_sg, + .smp_req = pm8001_chip_smp_req, + .ssp_io_req = pm8001_chip_ssp_io_req, + .sata_req = pm8001_chip_sata_req, + .phy_start_req = pm8001_chip_phy_start_req, + .phy_stop_req = pm8001_chip_phy_stop_req, + .reg_dev_req = pm8001_chip_reg_dev_req, + .dereg_dev_req = pm8001_chip_dereg_dev_req, + .phy_ctl_req = pm8001_chip_phy_ctl_req, + .task_abort = pm8001_chip_abort_task, + .ssp_tm_req = pm8001_chip_ssp_tm_req, + .get_nvmd_req = pm8001_chip_get_nvmd_req, + .set_nvmd_req = pm8001_chip_set_nvmd_req, + .fw_flash_update_req = pm8001_chip_fw_flash_update_req, + .set_dev_state_req = pm8001_chip_set_dev_state_req, + .sas_re_init_req = pm8001_chip_sas_re_initialization, +}; + diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h new file mode 100644 index 000000000000..96e4daa68b8f --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_hwi.h @@ -0,0 +1,1030 @@ +/* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ +#ifndef _PMC8001_REG_H_ +#define _PMC8001_REG_H_ + +#include <linux/types.h> +#include <scsi/libsas.h> + + +/* for Request Opcode of IOMB */ +#define OPC_INB_ECHO 1 /* 0x000 */ +#define OPC_INB_PHYSTART 4 /* 0x004 */ +#define OPC_INB_PHYSTOP 5 /* 0x005 */ +#define OPC_INB_SSPINIIOSTART 6 /* 0x006 */ +#define OPC_INB_SSPINITMSTART 7 /* 0x007 */ +#define OPC_INB_SSPINIEXTIOSTART 8 /* 0x008 */ +#define OPC_INB_DEV_HANDLE_ACCEPT 9 /* 0x009 */ +#define OPC_INB_SSPTGTIOSTART 10 /* 0x00A */ +#define OPC_INB_SSPTGTRSPSTART 11 /* 0x00B */ +#define OPC_INB_SSPINIEDCIOSTART 12 /* 0x00C */ +#define OPC_INB_SSPINIEXTEDCIOSTART 13 /* 0x00D */ +#define OPC_INB_SSPTGTEDCIOSTART 14 /* 0x00E */ +#define OPC_INB_SSP_ABORT 15 /* 0x00F */ +#define OPC_INB_DEREG_DEV_HANDLE 16 /* 0x010 */ +#define OPC_INB_GET_DEV_HANDLE 17 /* 0x011 */ +#define OPC_INB_SMP_REQUEST 18 /* 0x012 */ +/* SMP_RESPONSE is removed */ +#define OPC_INB_SMP_RESPONSE 19 /* 0x013 */ +#define OPC_INB_SMP_ABORT 20 /* 0x014 */ +#define OPC_INB_REG_DEV 22 /* 0x016 */ +#define OPC_INB_SATA_HOST_OPSTART 23 /* 0x017 */ +#define OPC_INB_SATA_ABORT 24 /* 0x018 */ +#define OPC_INB_LOCAL_PHY_CONTROL 25 /* 0x019 */ +#define OPC_INB_GET_DEV_INFO 26 /* 0x01A */ +#define OPC_INB_FW_FLASH_UPDATE 32 /* 0x020 */ +#define OPC_INB_GPIO 34 /* 0x022 */ +#define OPC_INB_SAS_DIAG_MODE_START_END 35 /* 0x023 */ +#define OPC_INB_SAS_DIAG_EXECUTE 36 /* 0x024 */ +#define OPC_INB_SAS_HW_EVENT_ACK 37 /* 0x025 */ +#define OPC_INB_GET_TIME_STAMP 38 /* 0x026 */ +#define OPC_INB_PORT_CONTROL 39 /* 0x027 */ +#define OPC_INB_GET_NVMD_DATA 40 /* 0x028 */ +#define OPC_INB_SET_NVMD_DATA 41 /* 0x029 */ +#define OPC_INB_SET_DEVICE_STATE 42 /* 0x02A */ +#define OPC_INB_GET_DEVICE_STATE 43 /* 0x02B */ +#define OPC_INB_SET_DEV_INFO 44 /* 0x02C */ +#define OPC_INB_SAS_RE_INITIALIZE 45 /* 0x02D */ + +/* for Response Opcode of IOMB */ +#define OPC_OUB_ECHO 1 /* 0x001 */ +#define OPC_OUB_HW_EVENT 4 /* 0x004 */ +#define OPC_OUB_SSP_COMP 5 /* 0x005 */ +#define OPC_OUB_SMP_COMP 6 /* 0x006 */ +#define OPC_OUB_LOCAL_PHY_CNTRL 7 /* 0x007 */ +#define OPC_OUB_DEV_REGIST 10 /* 0x00A */ +#define OPC_OUB_DEREG_DEV 11 /* 0x00B */ +#define OPC_OUB_GET_DEV_HANDLE 12 /* 0x00C */ +#define OPC_OUB_SATA_COMP 13 /* 0x00D */ +#define OPC_OUB_SATA_EVENT 14 /* 0x00E */ +#define OPC_OUB_SSP_EVENT 15 /* 0x00F */ +#define OPC_OUB_DEV_HANDLE_ARRIV 16 /* 0x010 */ +/* SMP_RECEIVED Notification is removed */ +#define OPC_OUB_SMP_RECV_EVENT 17 /* 0x011 */ +#define OPC_OUB_SSP_RECV_EVENT 18 /* 0x012 */ +#define OPC_OUB_DEV_INFO 19 /* 0x013 */ +#define OPC_OUB_FW_FLASH_UPDATE 20 /* 0x014 */ +#define OPC_OUB_GPIO_RESPONSE 22 /* 0x016 */ +#define OPC_OUB_GPIO_EVENT 23 /* 0x017 */ +#define OPC_OUB_GENERAL_EVENT 24 /* 0x018 */ +#define OPC_OUB_SSP_ABORT_RSP 26 /* 0x01A */ +#define OPC_OUB_SATA_ABORT_RSP 27 /* 0x01B */ +#define OPC_OUB_SAS_DIAG_MODE_START_END 28 /* 0x01C */ +#define OPC_OUB_SAS_DIAG_EXECUTE 29 /* 0x01D */ +#define OPC_OUB_GET_TIME_STAMP 30 /* 0x01E */ +#define OPC_OUB_SAS_HW_EVENT_ACK 31 /* 0x01F */ +#define OPC_OUB_PORT_CONTROL 32 /* 0x020 */ +#define OPC_OUB_SKIP_ENTRY 33 /* 0x021 */ +#define OPC_OUB_SMP_ABORT_RSP 34 /* 0x022 */ +#define OPC_OUB_GET_NVMD_DATA 35 /* 0x023 */ +#define OPC_OUB_SET_NVMD_DATA 36 /* 0x024 */ +#define OPC_OUB_DEVICE_HANDLE_REMOVAL 37 /* 0x025 */ +#define OPC_OUB_SET_DEVICE_STATE 38 /* 0x026 */ +#define OPC_OUB_GET_DEVICE_STATE 39 /* 0x027 */ +#define OPC_OUB_SET_DEV_INFO 40 /* 0x028 */ +#define OPC_OUB_SAS_RE_INITIALIZE 41 /* 0x029 */ + +/* for phy start*/ +#define SPINHOLD_DISABLE (0x00 << 14) +#define SPINHOLD_ENABLE (0x01 << 14) +#define LINKMODE_SAS (0x01 << 12) +#define LINKMODE_DSATA (0x02 << 12) +#define LINKMODE_AUTO (0x03 << 12) +#define LINKRATE_15 (0x01 << 8) +#define LINKRATE_30 (0x02 << 8) +#define LINKRATE_60 (0x04 << 8) + +struct mpi_msg_hdr{ + __le32 header; /* Bits [11:0] - Message operation code */ + /* Bits [15:12] - Message Category */ + /* Bits [21:16] - Outboundqueue ID for the + operation completion message */ + /* Bits [23:22] - Reserved */ + /* Bits [28:24] - Buffer Count, indicates how + many buffer are allocated for the massage */ + /* Bits [30:29] - Reserved */ + /* Bits [31] - Message Valid bit */ +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of PHY Start Command + * use to describe enable the phy (64 bytes) + */ +struct phy_start_req { + __le32 tag; + __le32 ase_sh_lm_slr_phyid; + struct sas_identify_frame sas_identify; + u32 reserved[5]; +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of PHY Start Command + * use to disable the phy (64 bytes) + */ +struct phy_stop_req { + __le32 tag; + __le32 phy_id; + u32 reserved[13]; +} __attribute__((packed, aligned(4))); + + +/* set device bits fis - device to host */ +struct set_dev_bits_fis { + u8 fis_type; /* 0xA1*/ + u8 n_i_pmport; + /* b7 : n Bit. Notification bit. If set device needs attention. */ + /* b6 : i Bit. Interrupt Bit */ + /* b5-b4: reserved2 */ + /* b3-b0: PM Port */ + u8 status; + u8 error; + u32 _r_a; +} __attribute__ ((packed)); +/* PIO setup FIS - device to host */ +struct pio_setup_fis { + u8 fis_type; /* 0x5f */ + u8 i_d_pmPort; + /* b7 : reserved */ + /* b6 : i bit. Interrupt bit */ + /* b5 : d bit. data transfer direction. set to 1 for device to host + xfer */ + /* b4 : reserved */ + /* b3-b0: PM Port */ + u8 status; + u8 error; + u8 lbal; + u8 lbam; + u8 lbah; + u8 device; + u8 lbal_exp; + u8 lbam_exp; + u8 lbah_exp; + u8 _r_a; + u8 sector_count; + u8 sector_count_exp; + u8 _r_b; + u8 e_status; + u8 _r_c[2]; + u8 transfer_count; +} __attribute__ ((packed)); + +/* + * brief the data structure of SATA Completion Response + * use to discribe the sata task response (64 bytes) + */ +struct sata_completion_resp { + __le32 tag; + __le32 status; + __le32 param; + u32 sata_resp[12]; +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of SAS HW Event Notification + * use to alert the host about the hardware event(64 bytes) + */ +struct hw_event_resp { + __le32 lr_evt_status_phyid_portid; + __le32 evt_param; + __le32 npip_portstate; + struct sas_identify_frame sas_identify; + struct dev_to_host_fis sata_fis; +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of REGISTER DEVICE Command + * use to describe MPI REGISTER DEVICE Command (64 bytes) + */ + +struct reg_dev_req { + __le32 tag; + __le32 phyid_portid; + __le32 dtype_dlr_retry; + __le32 firstburstsize_ITNexustimeout; + u32 sas_addr_hi; + u32 sas_addr_low; + __le32 upper_device_id; + u32 reserved[8]; +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of DEREGISTER DEVICE Command + * use to request spc to remove all internal resources associated + * with the device id (64 bytes) + */ + +struct dereg_dev_req { + __le32 tag; + __le32 device_id; + u32 reserved[13]; +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of DEVICE_REGISTRATION Response + * use to notify the completion of the device registration (64 bytes) + */ + +struct dev_reg_resp { + __le32 tag; + __le32 status; + __le32 device_id; + u32 reserved[12]; +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of Local PHY Control Command + * use to issue PHY CONTROL to local phy (64 bytes) + */ +struct local_phy_ctl_req { + __le32 tag; + __le32 phyop_phyid; + u32 reserved1[13]; +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of Local Phy Control Response + * use to describe MPI Local Phy Control Response (64 bytes) + */ +struct local_phy_ctl_resp { + __le32 tag; + __le32 phyop_phyid; + __le32 status; + u32 reserved[12]; +} __attribute__((packed, aligned(4))); + + +#define OP_BITS 0x0000FF00 +#define ID_BITS 0x0000000F + +/* + * brief the data structure of PORT Control Command + * use to control port properties (64 bytes) + */ + +struct port_ctl_req { + __le32 tag; + __le32 portop_portid; + __le32 param0; + __le32 param1; + u32 reserved1[11]; +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of HW Event Ack Command + * use to acknowledge receive HW event (64 bytes) + */ + +struct hw_event_ack_req { + __le32 tag; + __le32 sea_phyid_portid; + __le32 param0; + __le32 param1; + u32 reserved1[11]; +} __attribute__((packed, aligned(4))); + + +/* + * brief the data structure of SSP Completion Response + * use to indicate a SSP Completion (n bytes) + */ +struct ssp_completion_resp { + __le32 tag; + __le32 status; + __le32 param; + __le32 ssptag_rescv_rescpad; + struct ssp_response_iu ssp_resp_iu; + __le32 residual_count; +} __attribute__((packed, aligned(4))); + + +#define SSP_RESCV_BIT 0x00010000 + +/* + * brief the data structure of SATA EVNET esponse + * use to indicate a SATA Completion (64 bytes) + */ + +struct sata_event_resp { + __le32 tag; + __le32 event; + __le32 port_id; + __le32 device_id; + u32 reserved[11]; +} __attribute__((packed, aligned(4))); + +/* + * brief the data structure of SSP EVNET esponse + * use to indicate a SSP Completion (64 bytes) + */ + +struct ssp_event_resp { + __le32 tag; + __le32 event; + __le32 port_id; + __le32 device_id; + u32 reserved[11]; +} __attribute__((packed, aligned(4))); + +/** + * brief the data structure of General Event Notification Response + * use to describe MPI General Event Notification Response (64 bytes) + */ +struct general_event_resp { + __le32 status; + __le32 inb_IOMB_payload[14]; +} __attribute__((packed, aligned(4))); + + +#define GENERAL_EVENT_PAYLOAD 14 +#define OPCODE_BITS 0x00000fff + +/* + * brief the data structure of SMP Request Command + * use to describe MPI SMP REQUEST Command (64 bytes) + */ +struct smp_req { + __le32 tag; + __le32 device_id; + __le32 len_ip_ir; + /* Bits [0] - Indirect response */ + /* Bits [1] - Indirect Payload */ + /* Bits [15:2] - Reserved */ + /* Bits [23:16] - direct payload Len */ + /* Bits [31:24] - Reserved */ + u8 smp_req16[16]; + union { + u8 smp_req[32]; + struct { + __le64 long_req_addr;/* sg dma address, LE */ + __le32 long_req_size;/* LE */ + u32 _r_a; + __le64 long_resp_addr;/* sg dma address, LE */ + __le32 long_resp_size;/* LE */ + u32 _r_b; + } long_smp_req;/* sequencer extension */ + }; +} __attribute__((packed, aligned(4))); +/* + * brief the data structure of SMP Completion Response + * use to describe MPI SMP Completion Response (64 bytes) + */ +struct smp_completion_resp { + __le32 tag; + __le32 status; + __le32 param; + __le32 _r_a[12]; +} __attribute__((packed, aligned(4))); + +/* + *brief the data structure of SSP SMP SATA Abort Command + * use to describe MPI SSP SMP & SATA Abort Command (64 bytes) + */ +struct task_abort_req { + __le32 tag; + __le32 device_id; + __le32 tag_to_abort; + __le32 abort_all; + u32 reserved[11]; +} __attribute__((packed, aligned(4))); + +/* These flags used for SSP SMP & SATA Abort */ +#define ABORT_MASK 0x3 +#define ABORT_SINGLE 0x0 +#define ABORT_ALL 0x1 + +/** + * brief the data structure of SSP SATA SMP Abort Response + * use to describe SSP SMP & SATA Abort Response ( 64 bytes) + */ +struct task_abort_resp { + __le32 tag; + __le32 status; + __le32 scp; + u32 reserved[12]; +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of SAS Diagnostic Start/End Command + * use to describe MPI SAS Diagnostic Start/End Command (64 bytes) + */ +struct sas_diag_start_end_req { + __le32 tag; + __le32 operation_phyid; + u32 reserved[13]; +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of SAS Diagnostic Execute Command + * use to describe MPI SAS Diagnostic Execute Command (64 bytes) + */ +struct sas_diag_execute_req{ + __le32 tag; + __le32 cmdtype_cmddesc_phyid; + __le32 pat1_pat2; + __le32 threshold; + __le32 codepat_errmsk; + __le32 pmon; + __le32 pERF1CTL; + u32 reserved[8]; +} __attribute__((packed, aligned(4))); + + +#define SAS_DIAG_PARAM_BYTES 24 + +/* + * brief the data structure of Set Device State Command + * use to describe MPI Set Device State Command (64 bytes) + */ +struct set_dev_state_req { + __le32 tag; + __le32 device_id; + __le32 nds; + u32 reserved[12]; +} __attribute__((packed, aligned(4))); + +/* + * brief the data structure of sas_re_initialization + */ +struct sas_re_initialization_req { + + __le32 tag; + __le32 SSAHOLT;/* bit29-set max port; + ** bit28-set open reject cmd retries. + ** bit27-set open reject data retries. + ** bit26-set open reject option, remap:1 or not:0. + ** bit25-set sata head of line time out. + */ + __le32 reserved_maxPorts; + __le32 open_reject_cmdretries_data_retries;/* cmd retries: 31-bit16; + * data retries: bit15-bit0. + */ + __le32 sata_hol_tmo; + u32 reserved1[10]; +} __attribute__((packed, aligned(4))); + +/* + * brief the data structure of SATA Start Command + * use to describe MPI SATA IO Start Command (64 bytes) + */ + +struct sata_start_req { + __le32 tag; + __le32 device_id; + __le32 data_len; + __le32 ncqtag_atap_dir_m; + struct host_to_dev_fis sata_fis; + u32 reserved1; + u32 reserved2; + u32 addr_low; + u32 addr_high; + __le32 len; + __le32 esgl; +} __attribute__((packed, aligned(4))); + +/** + * brief the data structure of SSP INI TM Start Command + * use to describe MPI SSP INI TM Start Command (64 bytes) + */ +struct ssp_ini_tm_start_req { + __le32 tag; + __le32 device_id; + __le32 relate_tag; + __le32 tmf; + u8 lun[8]; + __le32 ds_ads_m; + u32 reserved[8]; +} __attribute__((packed, aligned(4))); + + +struct ssp_info_unit { + u8 lun[8];/* SCSI Logical Unit Number */ + u8 reserved1;/* reserved */ + u8 efb_prio_attr; + /* B7 : enabledFirstBurst */ + /* B6-3 : taskPriority */ + /* B2-0 : taskAttribute */ + u8 reserved2; /* reserved */ + u8 additional_cdb_len; + /* B7-2 : additional_cdb_len */ + /* B1-0 : reserved */ + u8 cdb[16];/* The SCSI CDB up to 16 bytes length */ +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of SSP INI IO Start Command + * use to describe MPI SSP INI IO Start Command (64 bytes) + */ +struct ssp_ini_io_start_req { + __le32 tag; + __le32 device_id; + __le32 data_len; + __le32 dir_m_tlr; + struct ssp_info_unit ssp_iu; + __le32 addr_low; + __le32 addr_high; + __le32 len; + __le32 esgl; +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of Firmware download + * use to describe MPI FW DOWNLOAD Command (64 bytes) + */ +struct fw_flash_Update_req { + __le32 tag; + __le32 cur_image_offset; + __le32 cur_image_len; + __le32 total_image_len; + u32 reserved0[7]; + __le32 sgl_addr_lo; + __le32 sgl_addr_hi; + __le32 len; + __le32 ext_reserved; +} __attribute__((packed, aligned(4))); + + +#define FWFLASH_IOMB_RESERVED_LEN 0x07 +/** + * brief the data structure of FW_FLASH_UPDATE Response + * use to describe MPI FW_FLASH_UPDATE Response (64 bytes) + * + */ +struct fw_flash_Update_resp { + dma_addr_t tag; + __le32 status; + u32 reserved[13]; +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of Get NVM Data Command + * use to get data from NVM in HBA(64 bytes) + */ +struct get_nvm_data_req { + __le32 tag; + __le32 len_ir_vpdd; + __le32 vpd_offset; + u32 reserved[8]; + __le32 resp_addr_lo; + __le32 resp_addr_hi; + __le32 resp_len; + u32 reserved1; +} __attribute__((packed, aligned(4))); + + +struct set_nvm_data_req { + __le32 tag; + __le32 len_ir_vpdd; + __le32 vpd_offset; + u32 reserved[8]; + __le32 resp_addr_lo; + __le32 resp_addr_hi; + __le32 resp_len; + u32 reserved1; +} __attribute__((packed, aligned(4))); + + +#define TWI_DEVICE 0x0 +#define C_SEEPROM 0x1 +#define VPD_FLASH 0x4 +#define AAP1_RDUMP 0x5 +#define IOP_RDUMP 0x6 +#define EXPAN_ROM 0x7 + +#define IPMode 0x80000000 +#define NVMD_TYPE 0x0000000F +#define NVMD_STAT 0x0000FFFF +#define NVMD_LEN 0xFF000000 +/** + * brief the data structure of Get NVMD Data Response + * use to describe MPI Get NVMD Data Response (64 bytes) + */ +struct get_nvm_data_resp { + __le32 tag; + __le32 ir_tda_bn_dps_das_nvm; + __le32 dlen_status; + __le32 nvm_data[12]; +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of SAS Diagnostic Start/End Response + * use to describe MPI SAS Diagnostic Start/End Response (64 bytes) + * + */ +struct sas_diag_start_end_resp { + __le32 tag; + __le32 status; + u32 reserved[13]; +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of SAS Diagnostic Execute Response + * use to describe MPI SAS Diagnostic Execute Response (64 bytes) + * + */ +struct sas_diag_execute_resp { + __le32 tag; + __le32 cmdtype_cmddesc_phyid; + __le32 Status; + __le32 ReportData; + u32 reserved[11]; +} __attribute__((packed, aligned(4))); + + +/** + * brief the data structure of Set Device State Response + * use to describe MPI Set Device State Response (64 bytes) + * + */ +struct set_dev_state_resp { + __le32 tag; + __le32 status; + __le32 device_id; + __le32 pds_nds; + u32 reserved[11]; +} __attribute__((packed, aligned(4))); + + +#define NDS_BITS 0x0F +#define PDS_BITS 0xF0 + +/* + * HW Events type + */ + +#define HW_EVENT_RESET_START 0x01 +#define HW_EVENT_CHIP_RESET_COMPLETE 0x02 +#define HW_EVENT_PHY_STOP_STATUS 0x03 +#define HW_EVENT_SAS_PHY_UP 0x04 +#define HW_EVENT_SATA_PHY_UP 0x05 +#define HW_EVENT_SATA_SPINUP_HOLD 0x06 +#define HW_EVENT_PHY_DOWN 0x07 +#define HW_EVENT_PORT_INVALID 0x08 +#define HW_EVENT_BROADCAST_CHANGE 0x09 +#define HW_EVENT_PHY_ERROR 0x0A +#define HW_EVENT_BROADCAST_SES 0x0B +#define HW_EVENT_INBOUND_CRC_ERROR 0x0C +#define HW_EVENT_HARD_RESET_RECEIVED 0x0D +#define HW_EVENT_MALFUNCTION 0x0E +#define HW_EVENT_ID_FRAME_TIMEOUT 0x0F +#define HW_EVENT_BROADCAST_EXP 0x10 +#define HW_EVENT_PHY_START_STATUS 0x11 +#define HW_EVENT_LINK_ERR_INVALID_DWORD 0x12 +#define HW_EVENT_LINK_ERR_DISPARITY_ERROR 0x13 +#define HW_EVENT_LINK_ERR_CODE_VIOLATION 0x14 +#define HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH 0x15 +#define HW_EVENT_LINK_ERR_PHY_RESET_FAILED 0x16 +#define HW_EVENT_PORT_RECOVERY_TIMER_TMO 0x17 +#define HW_EVENT_PORT_RECOVER 0x18 +#define HW_EVENT_PORT_RESET_TIMER_TMO 0x19 +#define HW_EVENT_PORT_RESET_COMPLETE 0x20 +#define EVENT_BROADCAST_ASYNCH_EVENT 0x21 + +/* port state */ +#define PORT_NOT_ESTABLISHED 0x00 +#define PORT_VALID 0x01 +#define PORT_LOSTCOMM 0x02 +#define PORT_IN_RESET 0x04 +#define PORT_INVALID 0x08 + +/* + * SSP/SMP/SATA IO Completion Status values + */ + +#define IO_SUCCESS 0x00 +#define IO_ABORTED 0x01 +#define IO_OVERFLOW 0x02 +#define IO_UNDERFLOW 0x03 +#define IO_FAILED 0x04 +#define IO_ABORT_RESET 0x05 +#define IO_NOT_VALID 0x06 +#define IO_NO_DEVICE 0x07 +#define IO_ILLEGAL_PARAMETER 0x08 +#define IO_LINK_FAILURE 0x09 +#define IO_PROG_ERROR 0x0A +#define IO_EDC_IN_ERROR 0x0B +#define IO_EDC_OUT_ERROR 0x0C +#define IO_ERROR_HW_TIMEOUT 0x0D +#define IO_XFER_ERROR_BREAK 0x0E +#define IO_XFER_ERROR_PHY_NOT_READY 0x0F +#define IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED 0x10 +#define IO_OPEN_CNX_ERROR_ZONE_VIOLATION 0x11 +#define IO_OPEN_CNX_ERROR_BREAK 0x12 +#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS 0x13 +#define IO_OPEN_CNX_ERROR_BAD_DESTINATION 0x14 +#define IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED 0x15 +#define IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY 0x16 +#define IO_OPEN_CNX_ERROR_WRONG_DESTINATION 0x17 +#define IO_OPEN_CNX_ERROR_UNKNOWN_ERROR 0x18 +#define IO_XFER_ERROR_NAK_RECEIVED 0x19 +#define IO_XFER_ERROR_ACK_NAK_TIMEOUT 0x1A +#define IO_XFER_ERROR_PEER_ABORTED 0x1B +#define IO_XFER_ERROR_RX_FRAME 0x1C +#define IO_XFER_ERROR_DMA 0x1D +#define IO_XFER_ERROR_CREDIT_TIMEOUT 0x1E +#define IO_XFER_ERROR_SATA_LINK_TIMEOUT 0x1F +#define IO_XFER_ERROR_SATA 0x20 +#define IO_XFER_ERROR_ABORTED_DUE_TO_SRST 0x22 +#define IO_XFER_ERROR_REJECTED_NCQ_MODE 0x21 +#define IO_XFER_ERROR_ABORTED_NCQ_MODE 0x23 +#define IO_XFER_OPEN_RETRY_TIMEOUT 0x24 +#define IO_XFER_SMP_RESP_CONNECTION_ERROR 0x25 +#define IO_XFER_ERROR_UNEXPECTED_PHASE 0x26 +#define IO_XFER_ERROR_XFER_RDY_OVERRUN 0x27 +#define IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED 0x28 + +#define IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT 0x30 +#define IO_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NAK 0x31 +#define IO_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK 0x32 + +#define IO_XFER_ERROR_OFFSET_MISMATCH 0x34 +#define IO_XFER_ERROR_XFER_ZERO_DATA_LEN 0x35 +#define IO_XFER_CMD_FRAME_ISSUED 0x36 +#define IO_ERROR_INTERNAL_SMP_RESOURCE 0x37 +#define IO_PORT_IN_RESET 0x38 +#define IO_DS_NON_OPERATIONAL 0x39 +#define IO_DS_IN_RECOVERY 0x3A +#define IO_TM_TAG_NOT_FOUND 0x3B +#define IO_XFER_PIO_SETUP_ERROR 0x3C +#define IO_SSP_EXT_IU_ZERO_LEN_ERROR 0x3D +#define IO_DS_IN_ERROR 0x3E +#define IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY 0x3F +#define IO_ABORT_IN_PROGRESS 0x40 +#define IO_ABORT_DELAYED 0x41 +#define IO_INVALID_LENGTH 0x42 + +/* WARNING: This error code must always be the last number. + * If you add error code, modify this code also + * It is used as an index + */ +#define IO_ERROR_UNKNOWN_GENERIC 0x43 + +/* MSGU CONFIGURATION TABLE*/ + +#define SPC_MSGU_CFG_TABLE_UPDATE 0x01/* Inbound doorbell bit0 */ +#define SPC_MSGU_CFG_TABLE_RESET 0x02/* Inbound doorbell bit1 */ +#define SPC_MSGU_CFG_TABLE_FREEZE 0x04/* Inbound doorbell bit2 */ +#define SPC_MSGU_CFG_TABLE_UNFREEZE 0x08/* Inbound doorbell bit4 */ +#define MSGU_IBDB_SET 0x04 +#define MSGU_HOST_INT_STATUS 0x08 +#define MSGU_HOST_INT_MASK 0x0C +#define MSGU_IOPIB_INT_STATUS 0x18 +#define MSGU_IOPIB_INT_MASK 0x1C +#define MSGU_IBDB_CLEAR 0x20/* RevB - Host not use */ +#define MSGU_MSGU_CONTROL 0x24 +#define MSGU_ODR 0x3C/* RevB */ +#define MSGU_ODCR 0x40/* RevB */ +#define MSGU_SCRATCH_PAD_0 0x44 +#define MSGU_SCRATCH_PAD_1 0x48 +#define MSGU_SCRATCH_PAD_2 0x4C +#define MSGU_SCRATCH_PAD_3 0x50 +#define MSGU_HOST_SCRATCH_PAD_0 0x54 +#define MSGU_HOST_SCRATCH_PAD_1 0x58 +#define MSGU_HOST_SCRATCH_PAD_2 0x5C +#define MSGU_HOST_SCRATCH_PAD_3 0x60 +#define MSGU_HOST_SCRATCH_PAD_4 0x64 +#define MSGU_HOST_SCRATCH_PAD_5 0x68 +#define MSGU_HOST_SCRATCH_PAD_6 0x6C +#define MSGU_HOST_SCRATCH_PAD_7 0x70 +#define MSGU_ODMR 0x74/* RevB */ + +/* bit definition for ODMR register */ +#define ODMR_MASK_ALL 0xFFFFFFFF/* mask all + interrupt vector */ +#define ODMR_CLEAR_ALL 0/* clear all + interrupt vector */ +/* bit definition for ODCR register */ +#define ODCR_CLEAR_ALL 0xFFFFFFFF /* mask all + interrupt vector*/ +/* MSIX Interupts */ +#define MSIX_TABLE_OFFSET 0x2000 +#define MSIX_TABLE_ELEMENT_SIZE 0x10 +#define MSIX_INTERRUPT_CONTROL_OFFSET 0xC +#define MSIX_TABLE_BASE (MSIX_TABLE_OFFSET + MSIX_INTERRUPT_CONTROL_OFFSET) +#define MSIX_INTERRUPT_DISABLE 0x1 +#define MSIX_INTERRUPT_ENABLE 0x0 + + +/* state definition for Scratch Pad1 register */ +#define SCRATCH_PAD1_POR 0x00 /* power on reset state */ +#define SCRATCH_PAD1_SFR 0x01 /* soft reset state */ +#define SCRATCH_PAD1_ERR 0x02 /* error state */ +#define SCRATCH_PAD1_RDY 0x03 /* ready state */ +#define SCRATCH_PAD1_RST 0x04 /* soft reset toggle flag */ +#define SCRATCH_PAD1_AAP1RDY_RST 0x08 /* AAP1 ready for soft reset */ +#define SCRATCH_PAD1_STATE_MASK 0xFFFFFFF0 /* ScratchPad1 + Mask, bit1-0 State, bit2 Soft Reset, bit3 FW RDY for Soft Reset */ +#define SCRATCH_PAD1_RESERVED 0x000003F8 /* Scratch Pad1 + Reserved bit 3 to 9 */ + + /* state definition for Scratch Pad2 register */ +#define SCRATCH_PAD2_POR 0x00 /* power on state */ +#define SCRATCH_PAD2_SFR 0x01 /* soft reset state */ +#define SCRATCH_PAD2_ERR 0x02 /* error state */ +#define SCRATCH_PAD2_RDY 0x03 /* ready state */ +#define SCRATCH_PAD2_FWRDY_RST 0x04 /* FW ready for soft reset flag*/ +#define SCRATCH_PAD2_IOPRDY_RST 0x08 /* IOP ready for soft reset */ +#define SCRATCH_PAD2_STATE_MASK 0xFFFFFFF4 /* ScratchPad 2 + Mask, bit1-0 State */ +#define SCRATCH_PAD2_RESERVED 0x000003FC /* Scratch Pad1 + Reserved bit 2 to 9 */ + +#define SCRATCH_PAD_ERROR_MASK 0xFFFFFC00 /* Error mask bits */ +#define SCRATCH_PAD_STATE_MASK 0x00000003 /* State Mask bits */ + +/* main configuration offset - byte offset */ +#define MAIN_SIGNATURE_OFFSET 0x00/* DWORD 0x00 */ +#define MAIN_INTERFACE_REVISION 0x04/* DWORD 0x01 */ +#define MAIN_FW_REVISION 0x08/* DWORD 0x02 */ +#define MAIN_MAX_OUTSTANDING_IO_OFFSET 0x0C/* DWORD 0x03 */ +#define MAIN_MAX_SGL_OFFSET 0x10/* DWORD 0x04 */ +#define MAIN_CNTRL_CAP_OFFSET 0x14/* DWORD 0x05 */ +#define MAIN_GST_OFFSET 0x18/* DWORD 0x06 */ +#define MAIN_IBQ_OFFSET 0x1C/* DWORD 0x07 */ +#define MAIN_OBQ_OFFSET 0x20/* DWORD 0x08 */ +#define MAIN_IQNPPD_HPPD_OFFSET 0x24/* DWORD 0x09 */ +#define MAIN_OB_HW_EVENT_PID03_OFFSET 0x28/* DWORD 0x0A */ +#define MAIN_OB_HW_EVENT_PID47_OFFSET 0x2C/* DWORD 0x0B */ +#define MAIN_OB_NCQ_EVENT_PID03_OFFSET 0x30/* DWORD 0x0C */ +#define MAIN_OB_NCQ_EVENT_PID47_OFFSET 0x34/* DWORD 0x0D */ +#define MAIN_TITNX_EVENT_PID03_OFFSET 0x38/* DWORD 0x0E */ +#define MAIN_TITNX_EVENT_PID47_OFFSET 0x3C/* DWORD 0x0F */ +#define MAIN_OB_SSP_EVENT_PID03_OFFSET 0x40/* DWORD 0x10 */ +#define MAIN_OB_SSP_EVENT_PID47_OFFSET 0x44/* DWORD 0x11 */ +#define MAIN_OB_SMP_EVENT_PID03_OFFSET 0x48/* DWORD 0x12 */ +#define MAIN_OB_SMP_EVENT_PID47_OFFSET 0x4C/* DWORD 0x13 */ +#define MAIN_EVENT_LOG_ADDR_HI 0x50/* DWORD 0x14 */ +#define MAIN_EVENT_LOG_ADDR_LO 0x54/* DWORD 0x15 */ +#define MAIN_EVENT_LOG_BUFF_SIZE 0x58/* DWORD 0x16 */ +#define MAIN_EVENT_LOG_OPTION 0x5C/* DWORD 0x17 */ +#define MAIN_IOP_EVENT_LOG_ADDR_HI 0x60/* DWORD 0x18 */ +#define MAIN_IOP_EVENT_LOG_ADDR_LO 0x64/* DWORD 0x19 */ +#define MAIN_IOP_EVENT_LOG_BUFF_SIZE 0x68/* DWORD 0x1A */ +#define MAIN_IOP_EVENT_LOG_OPTION 0x6C/* DWORD 0x1B */ +#define MAIN_FATAL_ERROR_INTERRUPT 0x70/* DWORD 0x1C */ +#define MAIN_FATAL_ERROR_RDUMP0_OFFSET 0x74/* DWORD 0x1D */ +#define MAIN_FATAL_ERROR_RDUMP0_LENGTH 0x78/* DWORD 0x1E */ +#define MAIN_FATAL_ERROR_RDUMP1_OFFSET 0x7C/* DWORD 0x1F */ +#define MAIN_FATAL_ERROR_RDUMP1_LENGTH 0x80/* DWORD 0x20 */ +#define MAIN_HDA_FLAGS_OFFSET 0x84/* DWORD 0x21 */ +#define MAIN_ANALOG_SETUP_OFFSET 0x88/* DWORD 0x22 */ + +/* Gereral Status Table offset - byte offset */ +#define GST_GSTLEN_MPIS_OFFSET 0x00 +#define GST_IQ_FREEZE_STATE0_OFFSET 0x04 +#define GST_IQ_FREEZE_STATE1_OFFSET 0x08 +#define GST_MSGUTCNT_OFFSET 0x0C +#define GST_IOPTCNT_OFFSET 0x10 +#define GST_PHYSTATE_OFFSET 0x18 +#define GST_PHYSTATE0_OFFSET 0x18 +#define GST_PHYSTATE1_OFFSET 0x1C +#define GST_PHYSTATE2_OFFSET 0x20 +#define GST_PHYSTATE3_OFFSET 0x24 +#define GST_PHYSTATE4_OFFSET 0x28 +#define GST_PHYSTATE5_OFFSET 0x2C +#define GST_PHYSTATE6_OFFSET 0x30 +#define GST_PHYSTATE7_OFFSET 0x34 +#define GST_RERRINFO_OFFSET 0x44 + +/* General Status Table - MPI state */ +#define GST_MPI_STATE_UNINIT 0x00 +#define GST_MPI_STATE_INIT 0x01 +#define GST_MPI_STATE_TERMINATION 0x02 +#define GST_MPI_STATE_ERROR 0x03 +#define GST_MPI_STATE_MASK 0x07 + +#define MBIC_NMI_ENABLE_VPE0_IOP 0x000418 +#define MBIC_NMI_ENABLE_VPE0_AAP1 0x000418 +/* PCIE registers - BAR2(0x18), BAR1(win) 0x010000 */ +#define PCIE_EVENT_INTERRUPT_ENABLE 0x003040 +#define PCIE_EVENT_INTERRUPT 0x003044 +#define PCIE_ERROR_INTERRUPT_ENABLE 0x003048 +#define PCIE_ERROR_INTERRUPT 0x00304C +/* signature defintion for host scratch pad0 register */ +#define SPC_SOFT_RESET_SIGNATURE 0x252acbcd +/* Signature for Soft Reset */ + +/* SPC Reset register - BAR4(0x20), BAR2(win) (need dynamic mapping) */ +#define SPC_REG_RESET 0x000000/* reset register */ + +/* bit difination for SPC_RESET register */ +#define SPC_REG_RESET_OSSP 0x00000001 +#define SPC_REG_RESET_RAAE 0x00000002 +#define SPC_REG_RESET_PCS_SPBC 0x00000004 +#define SPC_REG_RESET_PCS_IOP_SS 0x00000008 +#define SPC_REG_RESET_PCS_AAP1_SS 0x00000010 +#define SPC_REG_RESET_PCS_AAP2_SS 0x00000020 +#define SPC_REG_RESET_PCS_LM 0x00000040 +#define SPC_REG_RESET_PCS 0x00000080 +#define SPC_REG_RESET_GSM 0x00000100 +#define SPC_REG_RESET_DDR2 0x00010000 +#define SPC_REG_RESET_BDMA_CORE 0x00020000 +#define SPC_REG_RESET_BDMA_SXCBI 0x00040000 +#define SPC_REG_RESET_PCIE_AL_SXCBI 0x00080000 +#define SPC_REG_RESET_PCIE_PWR 0x00100000 +#define SPC_REG_RESET_PCIE_SFT 0x00200000 +#define SPC_REG_RESET_PCS_SXCBI 0x00400000 +#define SPC_REG_RESET_LMS_SXCBI 0x00800000 +#define SPC_REG_RESET_PMIC_SXCBI 0x01000000 +#define SPC_REG_RESET_PMIC_CORE 0x02000000 +#define SPC_REG_RESET_PCIE_PC_SXCBI 0x04000000 +#define SPC_REG_RESET_DEVICE 0x80000000 + +/* registers for BAR Shifting - BAR2(0x18), BAR1(win) */ +#define SPC_IBW_AXI_TRANSLATION_LOW 0x003258 + +#define MBIC_AAP1_ADDR_BASE 0x060000 +#define MBIC_IOP_ADDR_BASE 0x070000 +#define GSM_ADDR_BASE 0x0700000 +/* Dynamic map through Bar4 - 0x00700000 */ +#define GSM_CONFIG_RESET 0x00000000 +#define RAM_ECC_DB_ERR 0x00000018 +#define GSM_READ_ADDR_PARITY_INDIC 0x00000058 +#define GSM_WRITE_ADDR_PARITY_INDIC 0x00000060 +#define GSM_WRITE_DATA_PARITY_INDIC 0x00000068 +#define GSM_READ_ADDR_PARITY_CHECK 0x00000038 +#define GSM_WRITE_ADDR_PARITY_CHECK 0x00000040 +#define GSM_WRITE_DATA_PARITY_CHECK 0x00000048 + +#define RB6_ACCESS_REG 0x6A0000 +#define HDAC_EXEC_CMD 0x0002 +#define HDA_C_PA 0xcb +#define HDA_SEQ_ID_BITS 0x00ff0000 +#define HDA_GSM_OFFSET_BITS 0x00FFFFFF +#define MBIC_AAP1_ADDR_BASE 0x060000 +#define MBIC_IOP_ADDR_BASE 0x070000 +#define GSM_ADDR_BASE 0x0700000 +#define SPC_TOP_LEVEL_ADDR_BASE 0x000000 +#define GSM_CONFIG_RESET_VALUE 0x00003b00 +#define GPIO_ADDR_BASE 0x00090000 +#define GPIO_GPIO_0_0UTPUT_CTL_OFFSET 0x0000010c + +/* RB6 offset */ +#define SPC_RB6_OFFSET 0x80C0 +/* Magic number of soft reset for RB6 */ +#define RB6_MAGIC_NUMBER_RST 0x1234 + +/* Device Register status */ +#define DEVREG_SUCCESS 0x00 +#define DEVREG_FAILURE_OUT_OF_RESOURCE 0x01 +#define DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED 0x02 +#define DEVREG_FAILURE_INVALID_PHY_ID 0x03 +#define DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED 0x04 +#define DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE 0x05 +#define DEVREG_FAILURE_PORT_NOT_VALID_STATE 0x06 +#define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID 0x07 + +#endif + diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c new file mode 100644 index 000000000000..42ebe725d5a5 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -0,0 +1,891 @@ +/* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include "pm8001_sas.h" +#include "pm8001_chips.h" + +static struct scsi_transport_template *pm8001_stt; + +static const struct pm8001_chip_info pm8001_chips[] = { + [chip_8001] = { 8, &pm8001_8001_dispatch,}, +}; +static int pm8001_id; + +LIST_HEAD(hba_list); + +/** + * The main structure which LLDD must register for scsi core. + */ +static struct scsi_host_template pm8001_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .queuecommand = sas_queuecommand, + .target_alloc = sas_target_alloc, + .slave_configure = pm8001_slave_configure, + .slave_destroy = sas_slave_destroy, + .scan_finished = pm8001_scan_finished, + .scan_start = pm8001_scan_start, + .change_queue_depth = sas_change_queue_depth, + .change_queue_type = sas_change_queue_type, + .bios_param = sas_bios_param, + .can_queue = 1, + .cmd_per_lun = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .max_sectors = SCSI_DEFAULT_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = sas_eh_device_reset_handler, + .eh_bus_reset_handler = sas_eh_bus_reset_handler, + .slave_alloc = pm8001_slave_alloc, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, + .shost_attrs = pm8001_host_attrs, +}; + +/** + * Sas layer call this function to execute specific task. + */ +static struct sas_domain_function_template pm8001_transport_ops = { + .lldd_dev_found = pm8001_dev_found, + .lldd_dev_gone = pm8001_dev_gone, + + .lldd_execute_task = pm8001_queue_command, + .lldd_control_phy = pm8001_phy_control, + + .lldd_abort_task = pm8001_abort_task, + .lldd_abort_task_set = pm8001_abort_task_set, + .lldd_clear_aca = pm8001_clear_aca, + .lldd_clear_task_set = pm8001_clear_task_set, + .lldd_I_T_nexus_reset = pm8001_I_T_nexus_reset, + .lldd_lu_reset = pm8001_lu_reset, + .lldd_query_task = pm8001_query_task, +}; + +/** + *pm8001_phy_init - initiate our adapter phys + *@pm8001_ha: our hba structure. + *@phy_id: phy id. + */ +static void __devinit pm8001_phy_init(struct pm8001_hba_info *pm8001_ha, + int phy_id) +{ + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + phy->phy_state = 0; + phy->pm8001_ha = pm8001_ha; + sas_phy->enabled = (phy_id < pm8001_ha->chip->n_phy) ? 1 : 0; + sas_phy->class = SAS; + sas_phy->iproto = SAS_PROTOCOL_ALL; + sas_phy->tproto = 0; + sas_phy->type = PHY_TYPE_PHYSICAL; + sas_phy->role = PHY_ROLE_INITIATOR; + sas_phy->oob_mode = OOB_NOT_CONNECTED; + sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; + sas_phy->id = phy_id; + sas_phy->sas_addr = &pm8001_ha->sas_addr[0]; + sas_phy->frame_rcvd = &phy->frame_rcvd[0]; + sas_phy->ha = (struct sas_ha_struct *)pm8001_ha->shost->hostdata; + sas_phy->lldd_phy = phy; +} + +/** + *pm8001_free - free hba + *@pm8001_ha: our hba structure. + * + */ +static void pm8001_free(struct pm8001_hba_info *pm8001_ha) +{ + int i; + struct pm8001_wq *wq; + + if (!pm8001_ha) + return; + + for (i = 0; i < USI_MAX_MEMCNT; i++) { + if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) { + pci_free_consistent(pm8001_ha->pdev, + pm8001_ha->memoryMap.region[i].element_size, + pm8001_ha->memoryMap.region[i].virt_ptr, + pm8001_ha->memoryMap.region[i].phys_addr); + } + } + PM8001_CHIP_DISP->chip_iounmap(pm8001_ha); + if (pm8001_ha->shost) + scsi_host_put(pm8001_ha->shost); + list_for_each_entry(wq, &pm8001_ha->wq_list, entry) + cancel_delayed_work(&wq->work_q); + kfree(pm8001_ha->tags); + kfree(pm8001_ha); +} + +#ifdef PM8001_USE_TASKLET +static void pm8001_tasklet(unsigned long opaque) +{ + struct pm8001_hba_info *pm8001_ha; + pm8001_ha = (struct pm8001_hba_info *)opaque;; + if (unlikely(!pm8001_ha)) + BUG_ON(1); + PM8001_CHIP_DISP->isr(pm8001_ha); +} +#endif + + + /** + * pm8001_interrupt - when HBA originate a interrupt,we should invoke this + * dispatcher to handle each case. + * @irq: irq number. + * @opaque: the passed general host adapter struct + */ +static irqreturn_t pm8001_interrupt(int irq, void *opaque) +{ + struct pm8001_hba_info *pm8001_ha; + irqreturn_t ret = IRQ_HANDLED; + struct sas_ha_struct *sha = opaque; + pm8001_ha = sha->lldd_ha; + if (unlikely(!pm8001_ha)) + return IRQ_NONE; + if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha)) + return IRQ_NONE; +#ifdef PM8001_USE_TASKLET + tasklet_schedule(&pm8001_ha->tasklet); +#else + ret = PM8001_CHIP_DISP->isr(pm8001_ha); +#endif + return ret; +} + +/** + * pm8001_alloc - initiate our hba structure and 6 DMAs area. + * @pm8001_ha:our hba structure. + * + */ +static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha) +{ + int i; + spin_lock_init(&pm8001_ha->lock); + for (i = 0; i < pm8001_ha->chip->n_phy; i++) + pm8001_phy_init(pm8001_ha, i); + + pm8001_ha->tags = kzalloc(PM8001_MAX_CCB, GFP_KERNEL); + if (!pm8001_ha->tags) + goto err_out; + /* MPI Memory region 1 for AAP Event Log for fw */ + pm8001_ha->memoryMap.region[AAP1].num_elements = 1; + pm8001_ha->memoryMap.region[AAP1].element_size = PM8001_EVENT_LOG_SIZE; + pm8001_ha->memoryMap.region[AAP1].total_len = PM8001_EVENT_LOG_SIZE; + pm8001_ha->memoryMap.region[AAP1].alignment = 32; + + /* MPI Memory region 2 for IOP Event Log for fw */ + pm8001_ha->memoryMap.region[IOP].num_elements = 1; + pm8001_ha->memoryMap.region[IOP].element_size = PM8001_EVENT_LOG_SIZE; + pm8001_ha->memoryMap.region[IOP].total_len = PM8001_EVENT_LOG_SIZE; + pm8001_ha->memoryMap.region[IOP].alignment = 32; + + /* MPI Memory region 3 for consumer Index of inbound queues */ + pm8001_ha->memoryMap.region[CI].num_elements = 1; + pm8001_ha->memoryMap.region[CI].element_size = 4; + pm8001_ha->memoryMap.region[CI].total_len = 4; + pm8001_ha->memoryMap.region[CI].alignment = 4; + + /* MPI Memory region 4 for producer Index of outbound queues */ + pm8001_ha->memoryMap.region[PI].num_elements = 1; + pm8001_ha->memoryMap.region[PI].element_size = 4; + pm8001_ha->memoryMap.region[PI].total_len = 4; + pm8001_ha->memoryMap.region[PI].alignment = 4; + + /* MPI Memory region 5 inbound queues */ + pm8001_ha->memoryMap.region[IB].num_elements = 256; + pm8001_ha->memoryMap.region[IB].element_size = 64; + pm8001_ha->memoryMap.region[IB].total_len = 256 * 64; + pm8001_ha->memoryMap.region[IB].alignment = 64; + + /* MPI Memory region 6 inbound queues */ + pm8001_ha->memoryMap.region[OB].num_elements = 256; + pm8001_ha->memoryMap.region[OB].element_size = 64; + pm8001_ha->memoryMap.region[OB].total_len = 256 * 64; + pm8001_ha->memoryMap.region[OB].alignment = 64; + + /* Memory region write DMA*/ + pm8001_ha->memoryMap.region[NVMD].num_elements = 1; + pm8001_ha->memoryMap.region[NVMD].element_size = 4096; + pm8001_ha->memoryMap.region[NVMD].total_len = 4096; + /* Memory region for devices*/ + pm8001_ha->memoryMap.region[DEV_MEM].num_elements = 1; + pm8001_ha->memoryMap.region[DEV_MEM].element_size = PM8001_MAX_DEVICES * + sizeof(struct pm8001_device); + pm8001_ha->memoryMap.region[DEV_MEM].total_len = PM8001_MAX_DEVICES * + sizeof(struct pm8001_device); + + /* Memory region for ccb_info*/ + pm8001_ha->memoryMap.region[CCB_MEM].num_elements = 1; + pm8001_ha->memoryMap.region[CCB_MEM].element_size = PM8001_MAX_CCB * + sizeof(struct pm8001_ccb_info); + pm8001_ha->memoryMap.region[CCB_MEM].total_len = PM8001_MAX_CCB * + sizeof(struct pm8001_ccb_info); + + for (i = 0; i < USI_MAX_MEMCNT; i++) { + if (pm8001_mem_alloc(pm8001_ha->pdev, + &pm8001_ha->memoryMap.region[i].virt_ptr, + &pm8001_ha->memoryMap.region[i].phys_addr, + &pm8001_ha->memoryMap.region[i].phys_addr_hi, + &pm8001_ha->memoryMap.region[i].phys_addr_lo, + pm8001_ha->memoryMap.region[i].total_len, + pm8001_ha->memoryMap.region[i].alignment) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Mem%d alloc failed\n", + i)); + goto err_out; + } + } + + pm8001_ha->devices = pm8001_ha->memoryMap.region[DEV_MEM].virt_ptr; + for (i = 0; i < PM8001_MAX_DEVICES; i++) { + pm8001_ha->devices[i].dev_type = NO_DEVICE; + pm8001_ha->devices[i].id = i; + pm8001_ha->devices[i].device_id = PM8001_MAX_DEVICES; + pm8001_ha->devices[i].running_req = 0; + } + pm8001_ha->ccb_info = pm8001_ha->memoryMap.region[CCB_MEM].virt_ptr; + for (i = 0; i < PM8001_MAX_CCB; i++) { + pm8001_ha->ccb_info[i].ccb_dma_handle = + pm8001_ha->memoryMap.region[CCB_MEM].phys_addr + + i * sizeof(struct pm8001_ccb_info); + pm8001_ha->ccb_info[i].task = NULL; + pm8001_ha->ccb_info[i].ccb_tag = 0xffffffff; + pm8001_ha->ccb_info[i].device = NULL; + ++pm8001_ha->tags_num; + } + pm8001_ha->flags = PM8001F_INIT_TIME; + /* Initialize tags */ + pm8001_tag_init(pm8001_ha); + return 0; +err_out: + return 1; +} + +/** + * pm8001_ioremap - remap the pci high physical address to kernal virtual + * address so that we can access them. + * @pm8001_ha:our hba structure. + */ +static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha) +{ + u32 bar; + u32 logicalBar = 0; + struct pci_dev *pdev; + + pdev = pm8001_ha->pdev; + /* map pci mem (PMC pci base 0-3)*/ + for (bar = 0; bar < 6; bar++) { + /* + ** logical BARs for SPC: + ** bar 0 and 1 - logical BAR0 + ** bar 2 and 3 - logical BAR1 + ** bar4 - logical BAR2 + ** bar5 - logical BAR3 + ** Skip the appropriate assignments: + */ + if ((bar == 1) || (bar == 3)) + continue; + if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { + pm8001_ha->io_mem[logicalBar].membase = + pci_resource_start(pdev, bar); + pm8001_ha->io_mem[logicalBar].membase &= + (u32)PCI_BASE_ADDRESS_MEM_MASK; + pm8001_ha->io_mem[logicalBar].memsize = + pci_resource_len(pdev, bar); + pm8001_ha->io_mem[logicalBar].memvirtaddr = + ioremap(pm8001_ha->io_mem[logicalBar].membase, + pm8001_ha->io_mem[logicalBar].memsize); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCI: bar %d, logicalBar %d " + "virt_addr=%lx,len=%d\n", bar, logicalBar, + (unsigned long) + pm8001_ha->io_mem[logicalBar].memvirtaddr, + pm8001_ha->io_mem[logicalBar].memsize)); + } else { + pm8001_ha->io_mem[logicalBar].membase = 0; + pm8001_ha->io_mem[logicalBar].memsize = 0; + pm8001_ha->io_mem[logicalBar].memvirtaddr = 0; + } + logicalBar++; + } + return 0; +} + +/** + * pm8001_pci_alloc - initialize our ha card structure + * @pdev: pci device. + * @ent: ent + * @shost: scsi host struct which has been initialized before. + */ +static struct pm8001_hba_info *__devinit +pm8001_pci_alloc(struct pci_dev *pdev, u32 chip_id, struct Scsi_Host *shost) +{ + struct pm8001_hba_info *pm8001_ha; + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + + pm8001_ha = sha->lldd_ha; + if (!pm8001_ha) + return NULL; + + pm8001_ha->pdev = pdev; + pm8001_ha->dev = &pdev->dev; + pm8001_ha->chip_id = chip_id; + pm8001_ha->chip = &pm8001_chips[pm8001_ha->chip_id]; + pm8001_ha->irq = pdev->irq; + pm8001_ha->sas = sha; + pm8001_ha->shost = shost; + pm8001_ha->id = pm8001_id++; + INIT_LIST_HEAD(&pm8001_ha->wq_list); + pm8001_ha->logging_level = 0x01; + sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id); +#ifdef PM8001_USE_TASKLET + tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, + (unsigned long)pm8001_ha); +#endif + pm8001_ioremap(pm8001_ha); + if (!pm8001_alloc(pm8001_ha)) + return pm8001_ha; + pm8001_free(pm8001_ha); + return NULL; +} + +/** + * pci_go_44 - pm8001 specified, its DMA is 44 bit rather than 64 bit + * @pdev: pci device. + */ +static int pci_go_44(struct pci_dev *pdev) +{ + int rc; + + if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(44))) { + rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(44)); + if (rc) { + rc = pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(32)); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "44-bit DMA enable failed\n"); + return rc; + } + } + } else { + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + return rc; + } + rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + return rc; + } + } + return rc; +} + +/** + * pm8001_prep_sas_ha_init - allocate memory in general hba struct && init them. + * @shost: scsi host which has been allocated outside. + * @chip_info: our ha struct. + */ +static int __devinit pm8001_prep_sas_ha_init(struct Scsi_Host * shost, + const struct pm8001_chip_info *chip_info) +{ + int phy_nr, port_nr; + struct asd_sas_phy **arr_phy; + struct asd_sas_port **arr_port; + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + phy_nr = chip_info->n_phy; + port_nr = phy_nr; + memset(sha, 0x00, sizeof(*sha)); + arr_phy = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL); + if (!arr_phy) + goto exit; + arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL); + if (!arr_port) + goto exit_free2; + + sha->sas_phy = arr_phy; + sha->sas_port = arr_port; + sha->lldd_ha = kzalloc(sizeof(struct pm8001_hba_info), GFP_KERNEL); + if (!sha->lldd_ha) + goto exit_free1; + + shost->transportt = pm8001_stt; + shost->max_id = PM8001_MAX_DEVICES; + shost->max_lun = 8; + shost->max_channel = 0; + shost->unique_id = pm8001_id; + shost->max_cmd_len = 16; + shost->can_queue = PM8001_CAN_QUEUE; + shost->cmd_per_lun = 32; + return 0; +exit_free1: + kfree(arr_port); +exit_free2: + kfree(arr_phy); +exit: + return -1; +} + +/** + * pm8001_post_sas_ha_init - initialize general hba struct defined in libsas + * @shost: scsi host which has been allocated outside + * @chip_info: our ha struct. + */ +static void __devinit pm8001_post_sas_ha_init(struct Scsi_Host *shost, + const struct pm8001_chip_info *chip_info) +{ + int i = 0; + struct pm8001_hba_info *pm8001_ha; + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + pm8001_ha = sha->lldd_ha; + for (i = 0; i < chip_info->n_phy; i++) { + sha->sas_phy[i] = &pm8001_ha->phy[i].sas_phy; + sha->sas_port[i] = &pm8001_ha->port[i].sas_port; + } + sha->sas_ha_name = DRV_NAME; + sha->dev = pm8001_ha->dev; + + sha->lldd_module = THIS_MODULE; + sha->sas_addr = &pm8001_ha->sas_addr[0]; + sha->num_phys = chip_info->n_phy; + sha->lldd_max_execute_num = 1; + sha->lldd_queue_size = PM8001_CAN_QUEUE; + sha->core.shost = shost; +} + +/** + * pm8001_init_sas_add - initialize sas address + * @chip_info: our ha struct. + * + * Currently we just set the fixed SAS address to our HBA,for manufacture, + * it should read from the EEPROM + */ +static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) +{ + u8 i; +#ifdef PM8001_READ_VPD + DECLARE_COMPLETION_ONSTACK(completion); + pm8001_ha->nvmd_completion = &completion; + PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, 0, 0); + wait_for_completion(&completion); + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + memcpy(&pm8001_ha->phy[i].dev_sas_addr, pm8001_ha->sas_addr, + SAS_ADDR_SIZE); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("phy %d sas_addr = %x \n", i, + (u64)pm8001_ha->phy[i].dev_sas_addr)); + } +#else + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + pm8001_ha->phy[i].dev_sas_addr = 0x500e004010000004ULL; + pm8001_ha->phy[i].dev_sas_addr = + cpu_to_be64((u64) + (*(u64 *)&pm8001_ha->phy[i].dev_sas_addr)); + } + memcpy(pm8001_ha->sas_addr, &pm8001_ha->phy[0].dev_sas_addr, + SAS_ADDR_SIZE); +#endif +} + +#ifdef PM8001_USE_MSIX +/** + * pm8001_setup_msix - enable MSI-X interrupt + * @chip_info: our ha struct. + * @irq_handler: irq_handler + */ +static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha, + irq_handler_t irq_handler) +{ + u32 i = 0, j = 0; + u32 number_of_intr = 1; + int flag = 0; + u32 max_entry; + int rc; + max_entry = sizeof(pm8001_ha->msix_entries) / + sizeof(pm8001_ha->msix_entries[0]); + flag |= IRQF_DISABLED; + for (i = 0; i < max_entry ; i++) + pm8001_ha->msix_entries[i].entry = i; + rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries, + number_of_intr); + pm8001_ha->number_of_intr = number_of_intr; + if (!rc) { + for (i = 0; i < number_of_intr; i++) { + if (request_irq(pm8001_ha->msix_entries[i].vector, + irq_handler, flag, DRV_NAME, + SHOST_TO_SAS_HA(pm8001_ha->shost))) { + for (j = 0; j < i; j++) + free_irq( + pm8001_ha->msix_entries[j].vector, + SHOST_TO_SAS_HA(pm8001_ha->shost)); + pci_disable_msix(pm8001_ha->pdev); + break; + } + } + } + return rc; +} +#endif + +/** + * pm8001_request_irq - register interrupt + * @chip_info: our ha struct. + */ +static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) +{ + struct pci_dev *pdev; + irq_handler_t irq_handler = pm8001_interrupt; + int rc; + + pdev = pm8001_ha->pdev; + +#ifdef PM8001_USE_MSIX + if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) + return pm8001_setup_msix(pm8001_ha, irq_handler); + else + goto intx; +#endif + +intx: + /* intialize the INT-X interrupt */ + rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, + SHOST_TO_SAS_HA(pm8001_ha->shost)); + return rc; +} + +/** + * pm8001_pci_probe - probe supported device + * @pdev: pci device which kernel has been prepared for. + * @ent: pci device id + * + * This function is the main initialization function, when register a new + * pci driver it is invoked, all struct an hardware initilization should be done + * here, also, register interrupt + */ +static int __devinit pm8001_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + unsigned int rc; + u32 pci_reg; + struct pm8001_hba_info *pm8001_ha; + struct Scsi_Host *shost = NULL; + const struct pm8001_chip_info *chip; + + dev_printk(KERN_INFO, &pdev->dev, + "pm8001: driver version %s\n", DRV_VERSION); + rc = pci_enable_device(pdev); + if (rc) + goto err_out_enable; + pci_set_master(pdev); + /* + * Enable pci slot busmaster by setting pci command register. + * This is required by FW for Cyclone card. + */ + + pci_read_config_dword(pdev, PCI_COMMAND, &pci_reg); + pci_reg |= 0x157; + pci_write_config_dword(pdev, PCI_COMMAND, pci_reg); + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out_disable; + rc = pci_go_44(pdev); + if (rc) + goto err_out_regions; + + shost = scsi_host_alloc(&pm8001_sht, sizeof(void *)); + if (!shost) { + rc = -ENOMEM; + goto err_out_regions; + } + chip = &pm8001_chips[ent->driver_data]; + SHOST_TO_SAS_HA(shost) = + kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL); + if (!SHOST_TO_SAS_HA(shost)) { + rc = -ENOMEM; + goto err_out_free_host; + } + + rc = pm8001_prep_sas_ha_init(shost, chip); + if (rc) { + rc = -ENOMEM; + goto err_out_free; + } + pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost)); + pm8001_ha = pm8001_pci_alloc(pdev, chip_8001, shost); + if (!pm8001_ha) { + rc = -ENOMEM; + goto err_out_free; + } + list_add_tail(&pm8001_ha->list, &hba_list); + PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); + rc = PM8001_CHIP_DISP->chip_init(pm8001_ha); + if (rc) + goto err_out_ha_free; + + rc = scsi_add_host(shost, &pdev->dev); + if (rc) + goto err_out_ha_free; + rc = pm8001_request_irq(pm8001_ha); + if (rc) + goto err_out_shost; + + PM8001_CHIP_DISP->interrupt_enable(pm8001_ha); + pm8001_init_sas_add(pm8001_ha); + pm8001_post_sas_ha_init(shost, chip); + rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); + if (rc) + goto err_out_shost; + scsi_scan_host(pm8001_ha->shost); + return 0; + +err_out_shost: + scsi_remove_host(pm8001_ha->shost); +err_out_ha_free: + pm8001_free(pm8001_ha); +err_out_free: + kfree(SHOST_TO_SAS_HA(shost)); +err_out_free_host: + kfree(shost); +err_out_regions: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_enable: + return rc; +} + +static void __devexit pm8001_pci_remove(struct pci_dev *pdev) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct pm8001_hba_info *pm8001_ha; + int i; + pm8001_ha = sha->lldd_ha; + pci_set_drvdata(pdev, NULL); + sas_unregister_ha(sha); + sas_remove_host(pm8001_ha->shost); + list_del(&pm8001_ha->list); + scsi_remove_host(pm8001_ha->shost); + PM8001_CHIP_DISP->interrupt_disable(pm8001_ha); + PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); + +#ifdef PM8001_USE_MSIX + for (i = 0; i < pm8001_ha->number_of_intr; i++) + synchronize_irq(pm8001_ha->msix_entries[i].vector); + for (i = 0; i < pm8001_ha->number_of_intr; i++) + free_irq(pm8001_ha->msix_entries[i].vector, sha); + pci_disable_msix(pdev); +#else + free_irq(pm8001_ha->irq, sha); +#endif +#ifdef PM8001_USE_TASKLET + tasklet_kill(&pm8001_ha->tasklet); +#endif + pm8001_free(pm8001_ha); + kfree(sha->sas_phy); + kfree(sha->sas_port); + kfree(sha); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +/** + * pm8001_pci_suspend - power management suspend main entry point + * @pdev: PCI device struct + * @state: PM state change to (usually PCI_D3) + * + * Returns 0 success, anything else error. + */ +static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct pm8001_hba_info *pm8001_ha; + int i , pos; + u32 device_state; + pm8001_ha = sha->lldd_ha; + flush_scheduled_work(); + scsi_block_requests(pm8001_ha->shost); + pos = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pos == 0) { + printk(KERN_ERR " PCI PM not supported\n"); + return -ENODEV; + } + PM8001_CHIP_DISP->interrupt_disable(pm8001_ha); + PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); +#ifdef PM8001_USE_MSIX + for (i = 0; i < pm8001_ha->number_of_intr; i++) + synchronize_irq(pm8001_ha->msix_entries[i].vector); + for (i = 0; i < pm8001_ha->number_of_intr; i++) + free_irq(pm8001_ha->msix_entries[i].vector, sha); + pci_disable_msix(pdev); +#else + free_irq(pm8001_ha->irq, sha); +#endif +#ifdef PM8001_USE_TASKLET + tasklet_kill(&pm8001_ha->tasklet); +#endif + device_state = pci_choose_state(pdev, state); + pm8001_printk("pdev=0x%p, slot=%s, entering " + "operating state [D%d]\n", pdev, + pm8001_ha->name, device_state); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, device_state); + return 0; +} + +/** + * pm8001_pci_resume - power management resume main entry point + * @pdev: PCI device struct + * + * Returns 0 success, anything else error. + */ +static int pm8001_pci_resume(struct pci_dev *pdev) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct pm8001_hba_info *pm8001_ha; + int rc; + u32 device_state; + pm8001_ha = sha->lldd_ha; + device_state = pdev->current_state; + + pm8001_printk("pdev=0x%p, slot=%s, resuming from previous " + "operating state [D%d]\n", pdev, pm8001_ha->name, device_state); + + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + rc = pci_enable_device(pdev); + if (rc) { + pm8001_printk("slot=%s Enable device failed during resume\n", + pm8001_ha->name); + goto err_out_enable; + } + + pci_set_master(pdev); + rc = pci_go_44(pdev); + if (rc) + goto err_out_disable; + + PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); + rc = PM8001_CHIP_DISP->chip_init(pm8001_ha); + if (rc) + goto err_out_disable; + PM8001_CHIP_DISP->interrupt_disable(pm8001_ha); + rc = pm8001_request_irq(pm8001_ha); + if (rc) + goto err_out_disable; + #ifdef PM8001_USE_TASKLET + tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, + (unsigned long)pm8001_ha); + #endif + PM8001_CHIP_DISP->interrupt_enable(pm8001_ha); + scsi_unblock_requests(pm8001_ha->shost); + return 0; + +err_out_disable: + scsi_remove_host(pm8001_ha->shost); + pci_disable_device(pdev); +err_out_enable: + return rc; +} + +static struct pci_device_id __devinitdata pm8001_pci_table[] = { + { + PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001 + }, + { + PCI_DEVICE(0x117c, 0x0042), + .driver_data = chip_8001 + }, + {} /* terminate list */ +}; + +static struct pci_driver pm8001_pci_driver = { + .name = DRV_NAME, + .id_table = pm8001_pci_table, + .probe = pm8001_pci_probe, + .remove = __devexit_p(pm8001_pci_remove), + .suspend = pm8001_pci_suspend, + .resume = pm8001_pci_resume, +}; + +/** + * pm8001_init - initialize scsi transport template + */ +static int __init pm8001_init(void) +{ + int rc; + pm8001_id = 0; + pm8001_stt = sas_domain_attach_transport(&pm8001_transport_ops); + if (!pm8001_stt) + return -ENOMEM; + rc = pci_register_driver(&pm8001_pci_driver); + if (rc) + goto err_out; + return 0; +err_out: + sas_release_transport(pm8001_stt); + return rc; +} + +static void __exit pm8001_exit(void) +{ + pci_unregister_driver(&pm8001_pci_driver); + sas_release_transport(pm8001_stt); +} + +module_init(pm8001_init); +module_exit(pm8001_exit); + +MODULE_AUTHOR("Jack Wang <jack_wang@usish.com>"); +MODULE_DESCRIPTION("PMC-Sierra PM8001 SAS/SATA controller driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, pm8001_pci_table); + diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c new file mode 100644 index 000000000000..1f767a0e727a --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -0,0 +1,1103 @@ +/* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include "pm8001_sas.h" + +/** + * pm8001_find_tag - from sas task to find out tag that belongs to this task + * @task: the task sent to the LLDD + * @tag: the found tag associated with the task + */ +static int pm8001_find_tag(struct sas_task *task, u32 *tag) +{ + if (task->lldd_task) { + struct pm8001_ccb_info *ccb; + ccb = task->lldd_task; + *tag = ccb->ccb_tag; + return 1; + } + return 0; +} + +/** + * pm8001_tag_clear - clear the tags bitmap + * @pm8001_ha: our hba struct + * @tag: the found tag associated with the task + */ +static void pm8001_tag_clear(struct pm8001_hba_info *pm8001_ha, u32 tag) +{ + void *bitmap = pm8001_ha->tags; + clear_bit(tag, bitmap); +} + +static void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag) +{ + pm8001_tag_clear(pm8001_ha, tag); +} + +static void pm8001_tag_set(struct pm8001_hba_info *pm8001_ha, u32 tag) +{ + void *bitmap = pm8001_ha->tags; + set_bit(tag, bitmap); +} + +/** + * pm8001_tag_alloc - allocate a empty tag for task used. + * @pm8001_ha: our hba struct + * @tag_out: the found empty tag . + */ +inline int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out) +{ + unsigned int index, tag; + void *bitmap = pm8001_ha->tags; + + index = find_first_zero_bit(bitmap, pm8001_ha->tags_num); + tag = index; + if (tag >= pm8001_ha->tags_num) + return -SAS_QUEUE_FULL; + pm8001_tag_set(pm8001_ha, tag); + *tag_out = tag; + return 0; +} + +void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha) +{ + int i; + for (i = 0; i < pm8001_ha->tags_num; ++i) + pm8001_tag_clear(pm8001_ha, i); +} + + /** + * pm8001_mem_alloc - allocate memory for pm8001. + * @pdev: pci device. + * @virt_addr: the allocated virtual address + * @pphys_addr_hi: the physical address high byte address. + * @pphys_addr_lo: the physical address low byte address. + * @mem_size: memory size. + */ +int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr, + dma_addr_t *pphys_addr, u32 *pphys_addr_hi, + u32 *pphys_addr_lo, u32 mem_size, u32 align) +{ + caddr_t mem_virt_alloc; + dma_addr_t mem_dma_handle; + u64 phys_align; + u64 align_offset = 0; + if (align) + align_offset = (dma_addr_t)align - 1; + mem_virt_alloc = + pci_alloc_consistent(pdev, mem_size + align, &mem_dma_handle); + if (!mem_virt_alloc) { + pm8001_printk("memory allocation error\n"); + return -1; + } + memset((void *)mem_virt_alloc, 0, mem_size+align); + *pphys_addr = mem_dma_handle; + phys_align = (*pphys_addr + align_offset) & ~align_offset; + *virt_addr = (void *)mem_virt_alloc + phys_align - *pphys_addr; + *pphys_addr_hi = upper_32_bits(phys_align); + *pphys_addr_lo = lower_32_bits(phys_align); + return 0; +} +/** + * pm8001_find_ha_by_dev - from domain device which come from sas layer to + * find out our hba struct. + * @dev: the domain device which from sas layer. + */ +static +struct pm8001_hba_info *pm8001_find_ha_by_dev(struct domain_device *dev) +{ + struct sas_ha_struct *sha = dev->port->ha; + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + return pm8001_ha; +} + +/** + * pm8001_phy_control - this function should be registered to + * sas_domain_function_template to provide libsas used, note: this is just + * control the HBA phy rather than other expander phy if you want control + * other phy, you should use SMP command. + * @sas_phy: which phy in HBA phys. + * @func: the operation. + * @funcdata: always NULL. + */ +int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, + void *funcdata) +{ + int rc = 0, phy_id = sas_phy->id; + struct pm8001_hba_info *pm8001_ha = NULL; + struct sas_phy_linkrates *rates; + DECLARE_COMPLETION_ONSTACK(completion); + pm8001_ha = sas_phy->ha->lldd_ha; + pm8001_ha->phy[phy_id].enable_completion = &completion; + switch (func) { + case PHY_FUNC_SET_LINK_RATE: + rates = funcdata; + if (rates->minimum_linkrate) { + pm8001_ha->phy[phy_id].minimum_linkrate = + rates->minimum_linkrate; + } + if (rates->maximum_linkrate) { + pm8001_ha->phy[phy_id].maximum_linkrate = + rates->maximum_linkrate; + } + if (pm8001_ha->phy[phy_id].phy_state == 0) { + PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id); + wait_for_completion(&completion); + } + PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id, + PHY_LINK_RESET); + break; + case PHY_FUNC_HARD_RESET: + if (pm8001_ha->phy[phy_id].phy_state == 0) { + PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id); + wait_for_completion(&completion); + } + PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id, + PHY_HARD_RESET); + break; + case PHY_FUNC_LINK_RESET: + if (pm8001_ha->phy[phy_id].phy_state == 0) { + PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id); + wait_for_completion(&completion); + } + PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id, + PHY_LINK_RESET); + break; + case PHY_FUNC_RELEASE_SPINUP_HOLD: + PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id, + PHY_LINK_RESET); + break; + case PHY_FUNC_DISABLE: + PM8001_CHIP_DISP->phy_stop_req(pm8001_ha, phy_id); + break; + default: + rc = -EOPNOTSUPP; + } + msleep(300); + return rc; +} + +int pm8001_slave_alloc(struct scsi_device *scsi_dev) +{ + struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + if (dev_is_sata(dev)) { + /* We don't need to rescan targets + * if REPORT_LUNS request is failed + */ + if (scsi_dev->lun > 0) + return -ENXIO; + scsi_dev->tagged_supported = 1; + } + return sas_slave_alloc(scsi_dev); +} + +/** + * pm8001_scan_start - we should enable all HBA phys by sending the phy_start + * command to HBA. + * @shost: the scsi host data. + */ +void pm8001_scan_start(struct Scsi_Host *shost) +{ + int i; + struct pm8001_hba_info *pm8001_ha; + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + pm8001_ha = sha->lldd_ha; + PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha); + for (i = 0; i < pm8001_ha->chip->n_phy; ++i) + PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i); +} + +int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time) +{ + /* give the phy enabling interrupt event time to come in (1s + * is empirically about all it takes) */ + if (time < HZ) + return 0; + /* Wait for discovery to finish */ + scsi_flush_work(shost); + return 1; +} + +/** + * pm8001_task_prep_smp - the dispatcher function, prepare data for smp task + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to smp task + */ +static int pm8001_task_prep_smp(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + return PM8001_CHIP_DISP->smp_req(pm8001_ha, ccb); +} + +u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag) +{ + struct ata_queued_cmd *qc = task->uldd_task; + if (qc) { + if (qc->tf.command == ATA_CMD_FPDMA_WRITE || + qc->tf.command == ATA_CMD_FPDMA_READ) { + *tag = qc->tag; + return 1; + } + } + return 0; +} + +/** + * pm8001_task_prep_ata - the dispatcher function, prepare data for sata task + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to sata task + */ +static int pm8001_task_prep_ata(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + return PM8001_CHIP_DISP->sata_req(pm8001_ha, ccb); +} + +/** + * pm8001_task_prep_ssp_tm - the dispatcher function, prepare task management data + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to TM + * @tmf: the task management IU + */ +static int pm8001_task_prep_ssp_tm(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf) +{ + return PM8001_CHIP_DISP->ssp_tm_req(pm8001_ha, ccb, tmf); +} + +/** + * pm8001_task_prep_ssp - the dispatcher function,prepare ssp data for ssp task + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to ssp task + */ +static int pm8001_task_prep_ssp(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + return PM8001_CHIP_DISP->ssp_io_req(pm8001_ha, ccb); +} +int pm8001_slave_configure(struct scsi_device *sdev) +{ + struct domain_device *dev = sdev_to_domain_dev(sdev); + int ret = sas_slave_configure(sdev); + if (ret) + return ret; + if (dev_is_sata(dev)) { + #ifdef PM8001_DISABLE_NCQ + struct ata_port *ap = dev->sata_dev.ap; + struct ata_device *adev = ap->link.device; + adev->flags |= ATA_DFLAG_NCQ_OFF; + scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1); + #endif + } + return 0; +} +/** + * pm8001_task_exec - queue the task(ssp, smp && ata) to the hardware. + * @task: the task to be execute. + * @num: if can_queue great than 1, the task can be queued up. for SMP task, + * we always execute one one time. + * @gfp_flags: gfp_flags. + * @is_tmf: if it is task management task. + * @tmf: the task management IU + */ +#define DEV_IS_GONE(pm8001_dev) \ + ((!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE))) +static int pm8001_task_exec(struct sas_task *task, const int num, + gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf) +{ + struct domain_device *dev = task->dev; + struct pm8001_hba_info *pm8001_ha; + struct pm8001_device *pm8001_dev; + struct sas_task *t = task; + struct pm8001_ccb_info *ccb; + u32 tag = 0xdeadbeef, rc, n_elem = 0; + u32 n = num; + unsigned long flags = 0; + + if (!dev->port) { + struct task_status_struct *tsm = &t->task_status; + tsm->resp = SAS_TASK_UNDELIVERED; + tsm->stat = SAS_PHY_DOWN; + if (dev->dev_type != SATA_DEV) + t->task_done(t); + return 0; + } + pm8001_ha = pm8001_find_ha_by_dev(task->dev); + PM8001_IO_DBG(pm8001_ha, pm8001_printk("pm8001_task_exec device \n ")); + spin_lock_irqsave(&pm8001_ha->lock, flags); + do { + dev = t->dev; + pm8001_dev = dev->lldd_dev; + if (DEV_IS_GONE(pm8001_dev)) { + if (pm8001_dev) { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("device %d not ready.\n", + pm8001_dev->device_id)); + } else { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("device %016llx not " + "ready.\n", SAS_ADDR(dev->sas_addr))); + } + rc = SAS_PHY_DOWN; + goto out_done; + } + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + goto err_out; + ccb = &pm8001_ha->ccb_info[tag]; + + if (!sas_protocol_ata(t->task_proto)) { + if (t->num_scatter) { + n_elem = dma_map_sg(pm8001_ha->dev, + t->scatter, + t->num_scatter, + t->data_dir); + if (!n_elem) { + rc = -ENOMEM; + goto err_out_tag; + } + } + } else { + n_elem = t->num_scatter; + } + + t->lldd_task = ccb; + ccb->n_elem = n_elem; + ccb->ccb_tag = tag; + ccb->task = t; + switch (t->task_proto) { + case SAS_PROTOCOL_SMP: + rc = pm8001_task_prep_smp(pm8001_ha, ccb); + break; + case SAS_PROTOCOL_SSP: + if (is_tmf) + rc = pm8001_task_prep_ssp_tm(pm8001_ha, + ccb, tmf); + else + rc = pm8001_task_prep_ssp(pm8001_ha, ccb); + break; + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: + rc = pm8001_task_prep_ata(pm8001_ha, ccb); + break; + default: + dev_printk(KERN_ERR, pm8001_ha->dev, + "unknown sas_task proto: 0x%x\n", + t->task_proto); + rc = -EINVAL; + break; + } + + if (rc) { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("rc is %x\n", rc)); + goto err_out_tag; + } + /* TODO: select normal or high priority */ + spin_lock(&t->task_state_lock); + t->task_state_flags |= SAS_TASK_AT_INITIATOR; + spin_unlock(&t->task_state_lock); + pm8001_dev->running_req++; + if (n > 1) + t = list_entry(t->list.next, struct sas_task, list); + } while (--n); + rc = 0; + goto out_done; + +err_out_tag: + pm8001_tag_free(pm8001_ha, tag); +err_out: + dev_printk(KERN_ERR, pm8001_ha->dev, "pm8001 exec failed[%d]!\n", rc); + if (!sas_protocol_ata(t->task_proto)) + if (n_elem) + dma_unmap_sg(pm8001_ha->dev, t->scatter, n_elem, + t->data_dir); +out_done: + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return rc; +} + +/** + * pm8001_queue_command - register for upper layer used, all IO commands sent + * to HBA are from this interface. + * @task: the task to be execute. + * @num: if can_queue great than 1, the task can be queued up. for SMP task, + * we always execute one one time + * @gfp_flags: gfp_flags + */ +int pm8001_queue_command(struct sas_task *task, const int num, + gfp_t gfp_flags) +{ + return pm8001_task_exec(task, num, gfp_flags, 0, NULL); +} + +void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx) +{ + pm8001_tag_clear(pm8001_ha, ccb_idx); +} + +/** + * pm8001_ccb_task_free - free the sg for ssp and smp command, free the ccb. + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to ssp task + * @task: the task to be free. + * @ccb_idx: ccb index. + */ +void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha, + struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx) +{ + if (!ccb->task) + return; + if (!sas_protocol_ata(task->task_proto)) + if (ccb->n_elem) + dma_unmap_sg(pm8001_ha->dev, task->scatter, + task->num_scatter, task->data_dir); + + switch (task->task_proto) { + case SAS_PROTOCOL_SMP: + dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_resp, 1, + PCI_DMA_FROMDEVICE); + dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_req, 1, + PCI_DMA_TODEVICE); + break; + + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SSP: + default: + /* do nothing */ + break; + } + task->lldd_task = NULL; + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, ccb_idx); +} + + /** + * pm8001_alloc_dev - find a empty pm8001_device + * @pm8001_ha: our hba card information + */ +struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha) +{ + u32 dev; + for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) { + if (pm8001_ha->devices[dev].dev_type == NO_DEVICE) { + pm8001_ha->devices[dev].id = dev; + return &pm8001_ha->devices[dev]; + } + } + if (dev == PM8001_MAX_DEVICES) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("max support %d devices, ignore ..\n", + PM8001_MAX_DEVICES)); + } + return NULL; +} + +static void pm8001_free_dev(struct pm8001_device *pm8001_dev) +{ + u32 id = pm8001_dev->id; + memset(pm8001_dev, 0, sizeof(*pm8001_dev)); + pm8001_dev->id = id; + pm8001_dev->dev_type = NO_DEVICE; + pm8001_dev->device_id = PM8001_MAX_DEVICES; + pm8001_dev->sas_device = NULL; +} + +/** + * pm8001_dev_found_notify - libsas notify a device is found. + * @dev: the device structure which sas layer used. + * + * when libsas find a sas domain device, it should tell the LLDD that + * device is found, and then LLDD register this device to HBA firmware + * by the command "OPC_INB_REG_DEV", after that the HBA will assign a + * device ID(according to device's sas address) and returned it to LLDD. From + * now on, we communicate with HBA FW with the device ID which HBA assigned + * rather than sas address. it is the neccessary step for our HBA but it is + * the optional for other HBA driver. + */ +static int pm8001_dev_found_notify(struct domain_device *dev) +{ + unsigned long flags = 0; + int res = 0; + struct pm8001_hba_info *pm8001_ha = NULL; + struct domain_device *parent_dev = dev->parent; + struct pm8001_device *pm8001_device; + DECLARE_COMPLETION_ONSTACK(completion); + u32 flag = 0; + pm8001_ha = pm8001_find_ha_by_dev(dev); + spin_lock_irqsave(&pm8001_ha->lock, flags); + + pm8001_device = pm8001_alloc_dev(pm8001_ha); + pm8001_device->sas_device = dev; + if (!pm8001_device) { + res = -1; + goto found_out; + } + dev->lldd_dev = pm8001_device; + pm8001_device->dev_type = dev->dev_type; + pm8001_device->dcompletion = &completion; + if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) { + int phy_id; + struct ex_phy *phy; + for (phy_id = 0; phy_id < parent_dev->ex_dev.num_phys; + phy_id++) { + phy = &parent_dev->ex_dev.ex_phy[phy_id]; + if (SAS_ADDR(phy->attached_sas_addr) + == SAS_ADDR(dev->sas_addr)) { + pm8001_device->attached_phy = phy_id; + break; + } + } + if (phy_id == parent_dev->ex_dev.num_phys) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Error: no attached dev:%016llx" + " at ex:%016llx.\n", SAS_ADDR(dev->sas_addr), + SAS_ADDR(parent_dev->sas_addr))); + res = -1; + } + } else { + if (dev->dev_type == SATA_DEV) { + pm8001_device->attached_phy = + dev->rphy->identify.phy_identifier; + flag = 1; /* directly sata*/ + } + } /*register this device to HBA*/ + PM8001_DISC_DBG(pm8001_ha, pm8001_printk("Found device \n")); + PM8001_CHIP_DISP->reg_dev_req(pm8001_ha, pm8001_device, flag); + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + wait_for_completion(&completion); + if (dev->dev_type == SAS_END_DEV) + msleep(50); + pm8001_ha->flags = PM8001F_RUN_TIME ; + return 0; +found_out: + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return res; +} + +int pm8001_dev_found(struct domain_device *dev) +{ + return pm8001_dev_found_notify(dev); +} + +/** + * pm8001_alloc_task - allocate a task structure for TMF + */ +static struct sas_task *pm8001_alloc_task(void) +{ + struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL); + if (task) { + INIT_LIST_HEAD(&task->list); + spin_lock_init(&task->task_state_lock); + task->task_state_flags = SAS_TASK_STATE_PENDING; + init_timer(&task->timer); + init_completion(&task->completion); + } + return task; +} + +static void pm8001_free_task(struct sas_task *task) +{ + if (task) { + BUG_ON(!list_empty(&task->list)); + kfree(task); + } +} + +static void pm8001_task_done(struct sas_task *task) +{ + if (!del_timer(&task->timer)) + return; + complete(&task->completion); +} + +static void pm8001_tmf_timedout(unsigned long data) +{ + struct sas_task *task = (struct sas_task *)data; + + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + complete(&task->completion); +} + +#define PM8001_TASK_TIMEOUT 20 +/** + * pm8001_exec_internal_tmf_task - execute some task management commands. + * @dev: the wanted device. + * @tmf: which task management wanted to be take. + * @para_len: para_len. + * @parameter: ssp task parameter. + * + * when errors or exception happened, we may want to do something, for example + * abort the issued task which result in this execption, it is done by calling + * this function, note it is also with the task execute interface. + */ +static int pm8001_exec_internal_tmf_task(struct domain_device *dev, + void *parameter, u32 para_len, struct pm8001_tmf_task *tmf) +{ + int res, retry; + struct sas_task *task = NULL; + struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); + + for (retry = 0; retry < 3; retry++) { + task = pm8001_alloc_task(); + if (!task) + return -ENOMEM; + + task->dev = dev; + task->task_proto = dev->tproto; + memcpy(&task->ssp_task, parameter, para_len); + task->task_done = pm8001_task_done; + task->timer.data = (unsigned long)task; + task->timer.function = pm8001_tmf_timedout; + task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ; + add_timer(&task->timer); + + res = pm8001_task_exec(task, 1, GFP_KERNEL, 1, tmf); + + if (res) { + del_timer(&task->timer); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Executing internal task " + "failed\n")); + goto ex_err; + } + wait_for_completion(&task->completion); + res = -TMF_RESP_FUNC_FAILED; + /* Even TMF timed out, return direct. */ + if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TMF task[%x]timeout.\n", + tmf->tmf)); + goto ex_err; + } + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAM_GOOD) { + res = TMF_RESP_FUNC_COMPLETE; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_UNDERRUN) { + /* no error, but return the number of bytes of + * underrun */ + res = task->task_status.residual; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_OVERRUN) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Blocked task error.\n")); + res = -EMSGSIZE; + break; + } else { + PM8001_EH_DBG(pm8001_ha, + pm8001_printk(" Task to dev %016llx response:" + "0x%x status 0x%x\n", + SAS_ADDR(dev->sas_addr), + task->task_status.resp, + task->task_status.stat)); + pm8001_free_task(task); + task = NULL; + } + } +ex_err: + BUG_ON(retry == 3 && task != NULL); + if (task != NULL) + pm8001_free_task(task); + return res; +} + +static int +pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, struct domain_device *dev, u32 flag, + u32 task_tag) +{ + int res, retry; + u32 ccb_tag; + struct pm8001_ccb_info *ccb; + struct sas_task *task = NULL; + + for (retry = 0; retry < 3; retry++) { + task = pm8001_alloc_task(); + if (!task) + return -ENOMEM; + + task->dev = dev; + task->task_proto = dev->tproto; + task->task_done = pm8001_task_done; + task->timer.data = (unsigned long)task; + task->timer.function = pm8001_tmf_timedout; + task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ; + add_timer(&task->timer); + + res = pm8001_tag_alloc(pm8001_ha, &ccb_tag); + if (res) + return res; + ccb = &pm8001_ha->ccb_info[ccb_tag]; + ccb->device = pm8001_dev; + ccb->ccb_tag = ccb_tag; + ccb->task = task; + + res = PM8001_CHIP_DISP->task_abort(pm8001_ha, + pm8001_dev, flag, task_tag, ccb_tag); + + if (res) { + del_timer(&task->timer); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Executing internal task " + "failed\n")); + goto ex_err; + } + wait_for_completion(&task->completion); + res = TMF_RESP_FUNC_FAILED; + /* Even TMF timed out, return direct. */ + if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TMF task timeout.\n")); + goto ex_err; + } + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAM_GOOD) { + res = TMF_RESP_FUNC_COMPLETE; + break; + + } else { + PM8001_EH_DBG(pm8001_ha, + pm8001_printk(" Task to dev %016llx response: " + "0x%x status 0x%x\n", + SAS_ADDR(dev->sas_addr), + task->task_status.resp, + task->task_status.stat)); + pm8001_free_task(task); + task = NULL; + } + } +ex_err: + BUG_ON(retry == 3 && task != NULL); + if (task != NULL) + pm8001_free_task(task); + return res; +} + +/** + * pm8001_dev_gone_notify - see the comments for "pm8001_dev_found_notify" + * @dev: the device structure which sas layer used. + */ +static void pm8001_dev_gone_notify(struct domain_device *dev) +{ + unsigned long flags = 0; + u32 tag; + struct pm8001_hba_info *pm8001_ha; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + u32 device_id = pm8001_dev->device_id; + pm8001_ha = pm8001_find_ha_by_dev(dev); + spin_lock_irqsave(&pm8001_ha->lock, flags); + pm8001_tag_alloc(pm8001_ha, &tag); + if (pm8001_dev) { + PM8001_DISC_DBG(pm8001_ha, + pm8001_printk("found dev[%d:%x] is gone.\n", + pm8001_dev->device_id, pm8001_dev->dev_type)); + if (pm8001_dev->running_req) { + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , + dev, 1, 0); + spin_lock_irqsave(&pm8001_ha->lock, flags); + } + PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, device_id); + pm8001_free_dev(pm8001_dev); + } else { + PM8001_DISC_DBG(pm8001_ha, + pm8001_printk("Found dev has gone.\n")); + } + dev->lldd_dev = NULL; + spin_unlock_irqrestore(&pm8001_ha->lock, flags); +} + +void pm8001_dev_gone(struct domain_device *dev) +{ + pm8001_dev_gone_notify(dev); +} + +static int pm8001_issue_ssp_tmf(struct domain_device *dev, + u8 *lun, struct pm8001_tmf_task *tmf) +{ + struct sas_ssp_task ssp_task; + if (!(dev->tproto & SAS_PROTOCOL_SSP)) + return TMF_RESP_FUNC_ESUPP; + + strncpy((u8 *)&ssp_task.LUN, lun, 8); + return pm8001_exec_internal_tmf_task(dev, &ssp_task, sizeof(ssp_task), + tmf); +} + +/** + * Standard mandates link reset for ATA (type 0) and hard reset for + * SSP (type 1) , only for RECOVERY + */ +int pm8001_I_T_nexus_reset(struct domain_device *dev) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_device *pm8001_dev; + struct pm8001_hba_info *pm8001_ha; + struct sas_phy *phy; + if (!dev || !dev->lldd_dev) + return -1; + + pm8001_dev = dev->lldd_dev; + pm8001_ha = pm8001_find_ha_by_dev(dev); + phy = sas_find_local_phy(dev); + + if (dev_is_sata(dev)) { + DECLARE_COMPLETION_ONSTACK(completion_setstate); + rc = sas_phy_reset(phy, 1); + msleep(2000); + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , + dev, 1, 0); + pm8001_dev->setds_completion = &completion_setstate; + rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, + pm8001_dev, 0x01); + wait_for_completion(&completion_setstate); + } else{ + rc = sas_phy_reset(phy, 1); + msleep(2000); + } + PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n", + pm8001_dev->device_id, rc)); + return rc; +} + +/* mandatory SAM-3, the task reset the specified LUN*/ +int pm8001_lu_reset(struct domain_device *dev, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_tmf_task tmf_task; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); + if (dev_is_sata(dev)) { + struct sas_phy *phy = sas_find_local_phy(dev); + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , + dev, 1, 0); + rc = sas_phy_reset(phy, 1); + rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, + pm8001_dev, 0x01); + msleep(2000); + } else { + tmf_task.tmf = TMF_LU_RESET; + rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task); + } + /* If failed, fall-through I_T_Nexus reset */ + PM8001_EH_DBG(pm8001_ha, pm8001_printk("for device[%x]:rc=%d\n", + pm8001_dev->device_id, rc)); + return rc; +} + +/* optional SAM-3 */ +int pm8001_query_task(struct sas_task *task) +{ + u32 tag = 0xdeadbeef; + int i = 0; + struct scsi_lun lun; + struct pm8001_tmf_task tmf_task; + int rc = TMF_RESP_FUNC_FAILED; + if (unlikely(!task || !task->lldd_task || !task->dev)) + return rc; + + if (task->task_proto & SAS_PROTOCOL_SSP) { + struct scsi_cmnd *cmnd = task->uldd_task; + struct domain_device *dev = task->dev; + struct pm8001_hba_info *pm8001_ha = + pm8001_find_ha_by_dev(dev); + + int_to_scsilun(cmnd->device->lun, &lun); + rc = pm8001_find_tag(task, &tag); + if (rc == 0) { + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + PM8001_EH_DBG(pm8001_ha, pm8001_printk("Query:[")); + for (i = 0; i < 16; i++) + printk(KERN_INFO "%02x ", cmnd->cmnd[i]); + printk(KERN_INFO "]\n"); + tmf_task.tmf = TMF_QUERY_TASK; + tmf_task.tag_of_task_to_be_managed = tag; + + rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task); + switch (rc) { + /* The task is still in Lun, release it then */ + case TMF_RESP_FUNC_SUCC: + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("The task is still in Lun \n")); + /* The task is not in Lun or failed, reset the phy */ + case TMF_RESP_FUNC_FAILED: + case TMF_RESP_FUNC_COMPLETE: + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("The task is not in Lun or failed," + " reset the phy \n")); + break; + } + } + pm8001_printk(":rc= %d\n", rc); + return rc; +} + +/* mandatory SAM-3, still need free task/ccb info, abord the specified task */ +int pm8001_abort_task(struct sas_task *task) +{ + unsigned long flags; + u32 tag = 0xdeadbeef; + u32 device_id; + struct domain_device *dev ; + struct pm8001_hba_info *pm8001_ha = NULL; + struct pm8001_ccb_info *ccb; + struct scsi_lun lun; + struct pm8001_device *pm8001_dev; + struct pm8001_tmf_task tmf_task; + int rc = TMF_RESP_FUNC_FAILED; + if (unlikely(!task || !task->lldd_task || !task->dev)) + return rc; + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + rc = TMF_RESP_FUNC_COMPLETE; + goto out; + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + if (task->task_proto & SAS_PROTOCOL_SSP) { + struct scsi_cmnd *cmnd = task->uldd_task; + dev = task->dev; + ccb = task->lldd_task; + pm8001_dev = dev->lldd_dev; + pm8001_ha = pm8001_find_ha_by_dev(dev); + int_to_scsilun(cmnd->device->lun, &lun); + rc = pm8001_find_tag(task, &tag); + if (rc == 0) { + printk(KERN_INFO "No such tag in %s\n", __func__); + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + device_id = pm8001_dev->device_id; + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("abort io to deviceid= %d\n", device_id)); + tmf_task.tmf = TMF_ABORT_TASK; + tmf_task.tag_of_task_to_be_managed = tag; + rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task); + pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev, + pm8001_dev->sas_device, 0, tag); + } else if (task->task_proto & SAS_PROTOCOL_SATA || + task->task_proto & SAS_PROTOCOL_STP) { + dev = task->dev; + pm8001_dev = dev->lldd_dev; + pm8001_ha = pm8001_find_ha_by_dev(dev); + rc = pm8001_find_tag(task, &tag); + if (rc == 0) { + printk(KERN_INFO "No such tag in %s\n", __func__); + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev, + pm8001_dev->sas_device, 0, tag); + } else if (task->task_proto & SAS_PROTOCOL_SMP) { + /* SMP */ + dev = task->dev; + pm8001_dev = dev->lldd_dev; + pm8001_ha = pm8001_find_ha_by_dev(dev); + rc = pm8001_find_tag(task, &tag); + if (rc == 0) { + printk(KERN_INFO "No such tag in %s\n", __func__); + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev, + pm8001_dev->sas_device, 0, tag); + + } +out: + if (rc != TMF_RESP_FUNC_COMPLETE) + pm8001_printk("rc= %d\n", rc); + return rc; +} + +int pm8001_abort_task_set(struct domain_device *dev, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_tmf_task tmf_task; + + tmf_task.tmf = TMF_ABORT_TASK_SET; + rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task); + return rc; +} + +int pm8001_clear_aca(struct domain_device *dev, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_tmf_task tmf_task; + + tmf_task.tmf = TMF_CLEAR_ACA; + rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task); + + return rc; +} + +int pm8001_clear_task_set(struct domain_device *dev, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_tmf_task tmf_task; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); + + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("I_T_L_Q clear task set[%x]\n", + pm8001_dev->device_id)); + tmf_task.tmf = TMF_CLEAR_TASK_SET; + rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task); + return rc; +} + diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h new file mode 100644 index 000000000000..30f2ede55a75 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -0,0 +1,481 @@ +/* + * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver + * + * Copyright (c) 2008-2009 USI Co., Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifndef _PM8001_SAS_H_ +#define _PM8001_SAS_H_ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/ctype.h> +#include <linux/dma-mapping.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/smp_lock.h> +#include <scsi/libsas.h> +#include <scsi/scsi_tcq.h> +#include <scsi/sas_ata.h> +#include <asm/atomic.h> +#include "pm8001_defs.h" + +#define DRV_NAME "pm8001" +#define DRV_VERSION "0.1.36" +#define PM8001_FAIL_LOGGING 0x01 /* libsas EH function logging */ +#define PM8001_INIT_LOGGING 0x02 /* driver init logging */ +#define PM8001_DISC_LOGGING 0x04 /* discovery layer logging */ +#define PM8001_IO_LOGGING 0x08 /* I/O path logging */ +#define PM8001_EH_LOGGING 0x10 /* Error message logging */ +#define PM8001_IOCTL_LOGGING 0x20 /* IOCTL message logging */ +#define PM8001_MSG_LOGGING 0x40 /* misc message logging */ +#define pm8001_printk(format, arg...) printk(KERN_INFO "%s %d:" format,\ + __func__, __LINE__, ## arg) +#define PM8001_CHECK_LOGGING(HBA, LEVEL, CMD) \ +do { \ + if (unlikely(HBA->logging_level & LEVEL)) \ + do { \ + CMD; \ + } while (0); \ +} while (0); + +#define PM8001_EH_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_EH_LOGGING, CMD) + +#define PM8001_INIT_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_INIT_LOGGING, CMD) + +#define PM8001_DISC_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_DISC_LOGGING, CMD) + +#define PM8001_IO_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_IO_LOGGING, CMD) + +#define PM8001_FAIL_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_FAIL_LOGGING, CMD) + +#define PM8001_IOCTL_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_IOCTL_LOGGING, CMD) + +#define PM8001_MSG_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_MSG_LOGGING, CMD) + + +#define PM8001_USE_TASKLET +#define PM8001_USE_MSIX + + +#define DEV_IS_EXPANDER(type) ((type == EDGE_DEV) || (type == FANOUT_DEV)) + +#define PM8001_NAME_LENGTH 32/* generic length of strings */ +extern struct list_head hba_list; +extern const struct pm8001_dispatch pm8001_8001_dispatch; + +struct pm8001_hba_info; +struct pm8001_ccb_info; +struct pm8001_device; +struct pm8001_tmf_task; +struct pm8001_dispatch { + char *name; + int (*chip_init)(struct pm8001_hba_info *pm8001_ha); + int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha, u32 signature); + void (*chip_rst)(struct pm8001_hba_info *pm8001_ha); + int (*chip_ioremap)(struct pm8001_hba_info *pm8001_ha); + void (*chip_iounmap)(struct pm8001_hba_info *pm8001_ha); + irqreturn_t (*isr)(struct pm8001_hba_info *pm8001_ha); + u32 (*is_our_interupt)(struct pm8001_hba_info *pm8001_ha); + int (*isr_process_oq)(struct pm8001_hba_info *pm8001_ha); + void (*interrupt_enable)(struct pm8001_hba_info *pm8001_ha); + void (*interrupt_disable)(struct pm8001_hba_info *pm8001_ha); + void (*make_prd)(struct scatterlist *scatter, int nr, void *prd); + int (*smp_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb); + int (*ssp_io_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb); + int (*sata_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb); + int (*phy_start_req)(struct pm8001_hba_info *pm8001_ha, u8 phy_id); + int (*phy_stop_req)(struct pm8001_hba_info *pm8001_ha, u8 phy_id); + int (*reg_dev_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u32 flag); + int (*dereg_dev_req)(struct pm8001_hba_info *pm8001_ha, u32 device_id); + int (*phy_ctl_req)(struct pm8001_hba_info *pm8001_ha, + u32 phy_id, u32 phy_op); + int (*task_abort)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, + u32 cmd_tag); + int (*ssp_tm_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf); + int (*get_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload); + int (*set_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload); + int (*fw_flash_update_req)(struct pm8001_hba_info *pm8001_ha, + void *payload); + int (*set_dev_state_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u32 state); + int (*sas_diag_start_end_req)(struct pm8001_hba_info *pm8001_ha, + u32 state); + int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha, + u32 state); + int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha); +}; + +struct pm8001_chip_info { + u32 n_phy; + const struct pm8001_dispatch *dispatch; +}; +#define PM8001_CHIP_DISP (pm8001_ha->chip->dispatch) + +struct pm8001_port { + struct asd_sas_port sas_port; +}; + +struct pm8001_phy { + struct pm8001_hba_info *pm8001_ha; + struct pm8001_port *port; + struct asd_sas_phy sas_phy; + struct sas_identify identify; + struct scsi_device *sdev; + u64 dev_sas_addr; + u32 phy_type; + struct completion *enable_completion; + u32 frame_rcvd_size; + u8 frame_rcvd[32]; + u8 phy_attached; + u8 phy_state; + enum sas_linkrate minimum_linkrate; + enum sas_linkrate maximum_linkrate; +}; + +struct pm8001_device { + enum sas_dev_type dev_type; + struct domain_device *sas_device; + u32 attached_phy; + u32 id; + struct completion *dcompletion; + struct completion *setds_completion; + u32 device_id; + u32 running_req; +}; + +struct pm8001_prd_imt { + __le32 len; + __le32 e; +}; + +struct pm8001_prd { + __le64 addr; /* 64-bit buffer address */ + struct pm8001_prd_imt im_len; /* 64-bit length */ +} __attribute__ ((packed)); +/* + * CCB(Command Control Block) + */ +struct pm8001_ccb_info { + struct list_head entry; + struct sas_task *task; + u32 n_elem; + u32 ccb_tag; + dma_addr_t ccb_dma_handle; + struct pm8001_device *device; + struct pm8001_prd buf_prd[PM8001_MAX_DMA_SG]; + struct fw_control_ex *fw_control_context; +}; + +struct mpi_mem { + void *virt_ptr; + dma_addr_t phys_addr; + u32 phys_addr_hi; + u32 phys_addr_lo; + u32 total_len; + u32 num_elements; + u32 element_size; + u32 alignment; +}; + +struct mpi_mem_req { + /* The number of element in the mpiMemory array */ + u32 count; + /* The array of structures that define memroy regions*/ + struct mpi_mem region[USI_MAX_MEMCNT]; +}; + +struct main_cfg_table { + u32 signature; + u32 interface_rev; + u32 firmware_rev; + u32 max_out_io; + u32 max_sgl; + u32 ctrl_cap_flag; + u32 gst_offset; + u32 inbound_queue_offset; + u32 outbound_queue_offset; + u32 inbound_q_nppd_hppd; + u32 outbound_hw_event_pid0_3; + u32 outbound_hw_event_pid4_7; + u32 outbound_ncq_event_pid0_3; + u32 outbound_ncq_event_pid4_7; + u32 outbound_tgt_ITNexus_event_pid0_3; + u32 outbound_tgt_ITNexus_event_pid4_7; + u32 outbound_tgt_ssp_event_pid0_3; + u32 outbound_tgt_ssp_event_pid4_7; + u32 outbound_tgt_smp_event_pid0_3; + u32 outbound_tgt_smp_event_pid4_7; + u32 upper_event_log_addr; + u32 lower_event_log_addr; + u32 event_log_size; + u32 event_log_option; + u32 upper_iop_event_log_addr; + u32 lower_iop_event_log_addr; + u32 iop_event_log_size; + u32 iop_event_log_option; + u32 fatal_err_interrupt; + u32 fatal_err_dump_offset0; + u32 fatal_err_dump_length0; + u32 fatal_err_dump_offset1; + u32 fatal_err_dump_length1; + u32 hda_mode_flag; + u32 anolog_setup_table_offset; +}; +struct general_status_table { + u32 gst_len_mpistate; + u32 iq_freeze_state0; + u32 iq_freeze_state1; + u32 msgu_tcnt; + u32 iop_tcnt; + u32 reserved; + u32 phy_state[8]; + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 recover_err_info[8]; +}; +struct inbound_queue_table { + u32 element_pri_size_cnt; + u32 upper_base_addr; + u32 lower_base_addr; + u32 ci_upper_base_addr; + u32 ci_lower_base_addr; + u32 pi_pci_bar; + u32 pi_offset; + u32 total_length; + void *base_virt; + void *ci_virt; + u32 reserved; + __le32 consumer_index; + u32 producer_idx; +}; +struct outbound_queue_table { + u32 element_size_cnt; + u32 upper_base_addr; + u32 lower_base_addr; + void *base_virt; + u32 pi_upper_base_addr; + u32 pi_lower_base_addr; + u32 ci_pci_bar; + u32 ci_offset; + u32 total_length; + void *pi_virt; + u32 interrup_vec_cnt_delay; + u32 dinterrup_to_pci_offset; + __le32 producer_index; + u32 consumer_idx; +}; +struct pm8001_hba_memspace { + void __iomem *memvirtaddr; + u64 membase; + u32 memsize; +}; +struct pm8001_hba_info { + char name[PM8001_NAME_LENGTH]; + struct list_head list; + unsigned long flags; + spinlock_t lock;/* host-wide lock */ + struct pci_dev *pdev;/* our device */ + struct device *dev; + struct pm8001_hba_memspace io_mem[6]; + struct mpi_mem_req memoryMap; + void __iomem *msg_unit_tbl_addr;/*Message Unit Table Addr*/ + void __iomem *main_cfg_tbl_addr;/*Main Config Table Addr*/ + void __iomem *general_stat_tbl_addr;/*General Status Table Addr*/ + void __iomem *inbnd_q_tbl_addr;/*Inbound Queue Config Table Addr*/ + void __iomem *outbnd_q_tbl_addr;/*Outbound Queue Config Table Addr*/ + struct main_cfg_table main_cfg_tbl; + struct general_status_table gs_tbl; + struct inbound_queue_table inbnd_q_tbl[PM8001_MAX_INB_NUM]; + struct outbound_queue_table outbnd_q_tbl[PM8001_MAX_OUTB_NUM]; + u8 sas_addr[SAS_ADDR_SIZE]; + struct sas_ha_struct *sas;/* SCSI/SAS glue */ + struct Scsi_Host *shost; + u32 chip_id; + const struct pm8001_chip_info *chip; + struct completion *nvmd_completion; + int tags_num; + unsigned long *tags; + struct pm8001_phy phy[PM8001_MAX_PHYS]; + struct pm8001_port port[PM8001_MAX_PHYS]; + u32 id; + u32 irq; + struct pm8001_device *devices; + struct pm8001_ccb_info *ccb_info; +#ifdef PM8001_USE_MSIX + struct msix_entry msix_entries[16];/*for msi-x interrupt*/ + int number_of_intr;/*will be used in remove()*/ +#endif +#ifdef PM8001_USE_TASKLET + struct tasklet_struct tasklet; +#endif + struct list_head wq_list; + u32 logging_level; + u32 fw_status; + const struct firmware *fw_image; +}; + +struct pm8001_wq { + struct delayed_work work_q; + struct pm8001_hba_info *pm8001_ha; + void *data; + int handler; + struct list_head entry; +}; + +struct pm8001_fw_image_header { + u8 vender_id[8]; + u8 product_id; + u8 hardware_rev; + u8 dest_partition; + u8 reserved; + u8 fw_rev[4]; + __be32 image_length; + __be32 image_crc; + __be32 startup_entry; +} __attribute__((packed, aligned(4))); + +/* define task management IU */ +struct pm8001_tmf_task { + u8 tmf; + u32 tag_of_task_to_be_managed; +}; +/** + * FW Flash Update status values + */ +#define FLASH_UPDATE_COMPLETE_PENDING_REBOOT 0x00 +#define FLASH_UPDATE_IN_PROGRESS 0x01 +#define FLASH_UPDATE_HDR_ERR 0x02 +#define FLASH_UPDATE_OFFSET_ERR 0x03 +#define FLASH_UPDATE_CRC_ERR 0x04 +#define FLASH_UPDATE_LENGTH_ERR 0x05 +#define FLASH_UPDATE_HW_ERR 0x06 +#define FLASH_UPDATE_DNLD_NOT_SUPPORTED 0x10 +#define FLASH_UPDATE_DISABLED 0x11 + +/** + * brief param structure for firmware flash update. + */ +struct fw_flash_updata_info { + u32 cur_image_offset; + u32 cur_image_len; + u32 total_image_len; + struct pm8001_prd sgl; +}; + +struct fw_control_info { + u32 retcode;/*ret code (status)*/ + u32 phase;/*ret code phase*/ + u32 phaseCmplt;/*percent complete for the current + update phase */ + u32 version;/*Hex encoded firmware version number*/ + u32 offset;/*Used for downloading firmware */ + u32 len; /*len of buffer*/ + u32 size;/* Used in OS VPD and Trace get size + operations.*/ + u32 reserved;/* padding required for 64 bit + alignment */ + u8 buffer[1];/* Start of buffer */ +}; +struct fw_control_ex { + struct fw_control_info *fw_control; + void *buffer;/* keep buffer pointer to be + freed when the responce comes*/ + void *virtAddr;/* keep virtual address of the data */ + void *usrAddr;/* keep virtual address of the + user data */ + dma_addr_t phys_addr; + u32 len; /* len of buffer */ + void *payload; /* pointer to IOCTL Payload */ + u8 inProgress;/*if 1 - the IOCTL request is in + progress */ + void *param1; + void *param2; + void *param3; +}; + +/******************** function prototype *********************/ +int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out); +void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha); +u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag); +void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx); +void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha, + struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx); +int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, + void *funcdata); +int pm8001_slave_alloc(struct scsi_device *scsi_dev); +int pm8001_slave_configure(struct scsi_device *sdev); +void pm8001_scan_start(struct Scsi_Host *shost); +int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time); +int pm8001_queue_command(struct sas_task *task, const int num, + gfp_t gfp_flags); +int pm8001_abort_task(struct sas_task *task); +int pm8001_abort_task_set(struct domain_device *dev, u8 *lun); +int pm8001_clear_aca(struct domain_device *dev, u8 *lun); +int pm8001_clear_task_set(struct domain_device *dev, u8 *lun); +int pm8001_dev_found(struct domain_device *dev); +void pm8001_dev_gone(struct domain_device *dev); +int pm8001_lu_reset(struct domain_device *dev, u8 *lun); +int pm8001_I_T_nexus_reset(struct domain_device *dev); +int pm8001_query_task(struct sas_task *task); +int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr, + dma_addr_t *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo, + u32 mem_size, u32 align); + + +/* ctl shared API */ +extern struct device_attribute *pm8001_host_attrs[]; + +#endif + diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 0a97bc9074bb..34c6b896a91b 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -278,12 +278,17 @@ static void pmcraid_slave_destroy(struct scsi_device *scsi_dev) * pmcraid_change_queue_depth - Change the device's queue depth * @scsi_dev: scsi device struct * @depth: depth to set + * @reason: calling context * * Return value * actual depth set */ -static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth) +static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (depth > PMCRAID_MAX_CMD_PER_LUN) depth = PMCRAID_MAX_CMD_PER_LUN; @@ -3342,7 +3347,7 @@ static int pmcraid_chr_fasync(int fd, struct file *filep, int mode) * @direction : data transfer direction * * Return value - * 0 on sucess, non-zero error code on failure + * 0 on success, non-zero error code on failure */ static int pmcraid_build_passthrough_ioadls( struct pmcraid_cmd *cmd, @@ -3401,7 +3406,7 @@ static int pmcraid_build_passthrough_ioadls( * @direction: data transfer direction * * Return value - * 0 on sucess, non-zero error code on failure + * 0 on success, non-zero error code on failure */ static void pmcraid_release_passthrough_ioadls( struct pmcraid_cmd *cmd, @@ -3429,7 +3434,7 @@ static void pmcraid_release_passthrough_ioadls( * @arg: pointer to pmcraid_passthrough_buffer user buffer * * Return value - * 0 on sucess, non-zero error code on failure + * 0 on success, non-zero error code on failure */ static long pmcraid_ioctl_passthrough( struct pmcraid_instance *pinstance, diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index 3441b3f90827..2752b56cad56 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -771,11 +771,11 @@ static struct pmcraid_ioasc_error pmcraid_ioasc_error_table[] = { {0x01180600, IOASC_LOG_LEVEL_MUST, "Recovered Error, soft media error, sector reassignment suggested"}, {0x015D0000, IOASC_LOG_LEVEL_MUST, - "Recovered Error, failure prediction thresold exceeded"}, + "Recovered Error, failure prediction threshold exceeded"}, {0x015D9200, IOASC_LOG_LEVEL_MUST, - "Recovered Error, soft Cache Card Battery error thresold"}, + "Recovered Error, soft Cache Card Battery error threshold"}, {0x015D9200, IOASC_LOG_LEVEL_MUST, - "Recovered Error, soft Cache Card Battery error thresold"}, + "Recovered Error, soft Cache Card Battery error threshold"}, {0x02048000, IOASC_LOG_LEVEL_MUST, "Not Ready, IOA Reset Required"}, {0x02408500, IOASC_LOG_LEVEL_MUST, diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index fbcb82a2f7f4..21e2bc4d7401 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1654,7 +1654,8 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN); } - if (scsi_add_host(vha->host, &fc_vport->dev)) { + if (scsi_add_host_with_dma(vha->host, &fc_vport->dev, + &ha->pdev->dev)) { DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n", vha->host_no, vha->vp_idx)); goto vport_create_failed_2; diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index cca8e4ab0372..cb2eca4c26d8 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -377,6 +377,24 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) return ptr + sizeof(struct qla2xxx_mq_chain); } +static void +qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval) +{ + struct qla_hw_data *ha = vha->hw; + + if (rval != QLA_SUCCESS) { + qla_printk(KERN_WARNING, ha, + "Failed to dump firmware (%x)!!!\n", rval); + ha->fw_dumped = 0; + } else { + qla_printk(KERN_INFO, ha, + "Firmware dump saved to temp buffer (%ld/%p).\n", + vha->host_no, ha->fw_dump); + ha->fw_dumped = 1; + qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP); + } +} + /** * qla2300_fw_dump() - Dumps binary data from the 2300 firmware. * @ha: HA context @@ -530,17 +548,7 @@ qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked) if (rval == QLA_SUCCESS) qla2xxx_copy_queues(ha, nxt); - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - base_vha->host_no, ha->fw_dump); - ha->fw_dumped = 1; - } + qla2xxx_dump_post_process(base_vha, rval); qla2300_fw_dump_failed: if (!hardware_locked) @@ -737,17 +745,7 @@ qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked) if (rval == QLA_SUCCESS) qla2xxx_copy_queues(ha, &fw->risc_ram[cnt]); - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - base_vha->host_no, ha->fw_dump); - ha->fw_dumped = 1; - } + qla2xxx_dump_post_process(base_vha, rval); qla2100_fw_dump_failed: if (!hardware_locked) @@ -984,17 +982,7 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) qla24xx_copy_eft(ha, nxt); qla24xx_fw_dump_failed_0: - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - base_vha->host_no, ha->fw_dump); - ha->fw_dumped = 1; - } + qla2xxx_dump_post_process(base_vha, rval); qla24xx_fw_dump_failed: if (!hardware_locked) @@ -1305,17 +1293,7 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) } qla25xx_fw_dump_failed_0: - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - base_vha->host_no, ha->fw_dump); - ha->fw_dumped = 1; - } + qla2xxx_dump_post_process(base_vha, rval); qla25xx_fw_dump_failed: if (!hardware_locked) @@ -1628,17 +1606,7 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) } qla81xx_fw_dump_failed_0: - if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); - ha->fw_dumped = 0; - - } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - base_vha->host_no, ha->fw_dump); - ha->fw_dumped = 1; - } + qla2xxx_dump_post_process(base_vha, rval); qla81xx_fw_dump_failed: if (!hardware_locked) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 215061861794..6b9bf23c7735 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2123,6 +2123,7 @@ enum qla_work_type { QLA_EVT_ASYNC_LOGIN_DONE, QLA_EVT_ASYNC_LOGOUT, QLA_EVT_ASYNC_LOGOUT_DONE, + QLA_EVT_UEVENT, }; @@ -2146,6 +2147,10 @@ struct qla_work_evt { #define QLA_LOGIO_LOGIN_RETRIED BIT_0 u16 data[2]; } logio; + struct { + u32 code; +#define QLA_UEVENT_CODE_FW_DUMP 0 + } uevent; } u; }; @@ -2435,11 +2440,11 @@ struct qla_hw_data { dma_addr_t edc_data_dma; uint16_t edc_data_len; -#define XGMAC_DATA_SIZE PAGE_SIZE +#define XGMAC_DATA_SIZE 4096 void *xgmac_data; dma_addr_t xgmac_data_dma; -#define DCBX_TLV_DATA_SIZE PAGE_SIZE +#define DCBX_TLV_DATA_SIZE 4096 void *dcbx_tlv; dma_addr_t dcbx_tlv_dma; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index f3d1d1afa95b..e21851358509 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -92,6 +92,7 @@ extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); +extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *); @@ -246,7 +247,7 @@ qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *); extern int qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *, - uint16_t *, uint16_t *, uint16_t *); + uint16_t *, uint16_t *, uint16_t *, uint16_t *); extern int qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 9e3eaac25596..b74924b279ef 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -277,7 +277,6 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) vha->marker_needed = 0; ha->isp_abort_cnt = 0; ha->beacon_blink_led = 0; - set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); set_bit(0, ha->req_qid_map); set_bit(0, ha->rsp_qid_map); @@ -1203,7 +1202,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) } qla2x00_get_resource_cnts(vha, NULL, &ha->fw_xcb_count, NULL, NULL, - &ha->max_npiv_vports); + &ha->max_npiv_vports, NULL); if (!fw_major_version && ql2xallocfwdump) qla2x00_alloc_fw_dump(vha); @@ -3573,6 +3572,15 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) ha->isp_abort_cnt = 0; clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + if (IS_QLA81XX(ha)) + qla2x00_get_fw_version(vha, + &ha->fw_major_version, + &ha->fw_minor_version, + &ha->fw_subminor_version, + &ha->fw_attributes, &ha->fw_memory_size, + ha->mpi_version, &ha->mpi_capabilities, + ha->phy_version); + if (ha->fce) { ha->flags.fce_enabled = 1; memset(ha->fce, 0, diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index b20a7169aac2..804987397b77 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -313,10 +313,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" }; char *link_speed; uint16_t handle_cnt; - uint16_t cnt; + uint16_t cnt, mbx; uint32_t handles[5]; struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24; uint32_t rscn_entry, host_pid; uint8_t rscn_queue_index; unsigned long flags; @@ -395,9 +396,10 @@ skip_rio: break; case MBA_SYSTEM_ERR: /* System Error */ + mbx = IS_QLA81XX(ha) ? RD_REG_WORD(®24->mailbox7) : 0; qla_printk(KERN_INFO, ha, - "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n", - mb[1], mb[2], mb[3]); + "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh " + "mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx); ha->isp_ops->fw_dump(vha, 1); @@ -419,9 +421,10 @@ skip_rio: break; case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */ - DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n", - vha->host_no)); - qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n"); + DEBUG2(printk("scsi(%ld): ISP Request Transfer Error (%x).\n", + vha->host_no, mb[1])); + qla_printk(KERN_WARNING, ha, + "ISP Request Transfer Error (%x).\n", mb[1]); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; @@ -485,10 +488,13 @@ skip_rio: break; case MBA_LOOP_DOWN: /* Loop Down Event */ + mbx = IS_QLA81XX(ha) ? RD_REG_WORD(®24->mailbox4) : 0; DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN " - "(%x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3])); - qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n", - mb[1], mb[2], mb[3]); + "(%x %x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3], + mbx)); + qla_printk(KERN_INFO, ha, + "LOOP DOWN detected (%x %x %x %x).\n", mb[1], mb[2], mb[3], + mbx); if (atomic_read(&vha->loop_state) != LOOP_DOWN) { atomic_set(&vha->loop_state, LOOP_DOWN); @@ -1347,16 +1353,22 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) sense_len = rsp_info_len = resid_len = fw_resid_len = 0; if (IS_FWI2_CAPABLE(ha)) { - sense_len = le32_to_cpu(sts24->sense_len); - rsp_info_len = le32_to_cpu(sts24->rsp_data_len); - resid_len = le32_to_cpu(sts24->rsp_residual_count); - fw_resid_len = le32_to_cpu(sts24->residual_len); + if (scsi_status & SS_SENSE_LEN_VALID) + sense_len = le32_to_cpu(sts24->sense_len); + if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) + rsp_info_len = le32_to_cpu(sts24->rsp_data_len); + if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) + resid_len = le32_to_cpu(sts24->rsp_residual_count); + if (comp_status == CS_DATA_UNDERRUN) + fw_resid_len = le32_to_cpu(sts24->residual_len); rsp_info = sts24->data; sense_data = sts24->data; host_to_fcp_swap(sts24->data, sizeof(sts24->data)); } else { - sense_len = le16_to_cpu(sts->req_sense_length); - rsp_info_len = le16_to_cpu(sts->rsp_info_len); + if (scsi_status & SS_SENSE_LEN_VALID) + sense_len = le16_to_cpu(sts->req_sense_length); + if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) + rsp_info_len = le16_to_cpu(sts->rsp_info_len); resid_len = le32_to_cpu(sts->residual_length); rsp_info = sts->rsp_info; sense_data = sts->req_sense_data; @@ -1443,38 +1455,62 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) break; case CS_DATA_UNDERRUN: - resid = resid_len; + DEBUG2(printk(KERN_INFO + "scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x. " + "resid=0x%x fw_resid=0x%x cdb=0x%x os_underflow=0x%x\n", + vha->host_no, cp->device->id, cp->device->lun, comp_status, + scsi_status, resid_len, fw_resid_len, cp->cmnd[0], + cp->underflow)); + /* Use F/W calculated residual length. */ - if (IS_FWI2_CAPABLE(ha)) { - if (!(scsi_status & SS_RESIDUAL_UNDER)) { - lscsi_status = 0; - } else if (resid != fw_resid_len) { - scsi_status &= ~SS_RESIDUAL_UNDER; - lscsi_status = 0; + resid = IS_FWI2_CAPABLE(ha) ? fw_resid_len : resid_len; + scsi_set_resid(cp, resid); + if (scsi_status & SS_RESIDUAL_UNDER) { + if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) { + DEBUG2(printk( + "scsi(%ld:%d:%d:%d) Dropped frame(s) " + "detected (%x of %x bytes)...residual " + "length mismatch...retrying command.\n", + vha->host_no, cp->device->channel, + cp->device->id, cp->device->lun, resid, + scsi_bufflen(cp))); + + cp->result = DID_ERROR << 16 | lscsi_status; + break; } - resid = fw_resid_len; - } - if (scsi_status & SS_RESIDUAL_UNDER) { - scsi_set_resid(cp, resid); - } else { - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%d) UNDERRUN status detected " - "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x " - "os_underflow=0x%x\n", vha->host_no, - cp->device->id, cp->device->lun, comp_status, - scsi_status, resid_len, resid, cp->cmnd[0], - cp->underflow)); + if (!lscsi_status && + ((unsigned)(scsi_bufflen(cp) - resid) < + cp->underflow)) { + qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d:%d): Mid-layer underflow " + "detected (%x of %x bytes)...returning " + "error status.\n", vha->host_no, + cp->device->channel, cp->device->id, + cp->device->lun, resid, scsi_bufflen(cp)); + + cp->result = DID_ERROR << 16; + break; + } + } else if (!lscsi_status) { + DEBUG2(printk( + "scsi(%ld:%d:%d:%d) Dropped frame(s) detected " + "(%x of %x bytes)...firmware reported underrun..." + "retrying command.\n", vha->host_no, + cp->device->channel, cp->device->id, + cp->device->lun, resid, scsi_bufflen(cp))); + cp->result = DID_ERROR << 16; + break; } + cp->result = DID_OK << 16 | lscsi_status; + /* * Check to see if SCSI Status is non zero. If so report SCSI * Status. */ if (lscsi_status != 0) { - cp->result = DID_OK << 16 | lscsi_status; - if (lscsi_status == SAM_STAT_TASK_SET_FULL) { DEBUG2(printk(KERN_INFO "scsi(%ld): QUEUE FULL status detected " @@ -1501,42 +1537,6 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) break; qla2x00_handle_sense(sp, sense_data, sense_len, rsp); - } else { - /* - * If RISC reports underrun and target does not report - * it then we must have a lost frame, so tell upper - * layer to retry it by reporting an error. - */ - if (!(scsi_status & SS_RESIDUAL_UNDER)) { - DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped " - "frame(s) detected (%x of %x bytes)..." - "retrying command.\n", - vha->host_no, cp->device->channel, - cp->device->id, cp->device->lun, resid, - scsi_bufflen(cp))); - - scsi_set_resid(cp, resid); - cp->result = DID_ERROR << 16; - break; - } - - /* Handle mid-layer underflow */ - if ((unsigned)(scsi_bufflen(cp) - resid) < - cp->underflow) { - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%d): Mid-layer underflow " - "detected (%x of %x bytes)...returning " - "error status.\n", vha->host_no, - cp->device->channel, cp->device->id, - cp->device->lun, resid, - scsi_bufflen(cp)); - - cp->result = DID_ERROR << 16; - break; - } - - /* Everybody online, looking good... */ - cp->result = DID_OK << 16; } break; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index b6202fe118ac..05d595d9a7ef 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -2006,7 +2006,7 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma, int qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt, - uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports) + uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports, uint16_t *max_fcfs) { int rval; mbx_cmd_t mc; @@ -2017,6 +2017,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, mcp->mb[0] = MBC_GET_RESOURCE_COUNTS; mcp->out_mb = MBX_0; mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + if (IS_QLA81XX(vha->hw)) + mcp->in_mb |= MBX_12; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); @@ -2027,9 +2029,10 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, vha->host_no, mcp->mb[0])); } else { DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x " - "mb7=%x mb10=%x mb11=%x.\n", __func__, vha->host_no, - mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7], - mcp->mb[10], mcp->mb[11])); + "mb7=%x mb10=%x mb11=%x mb12=%x.\n", __func__, + vha->host_no, mcp->mb[1], mcp->mb[2], mcp->mb[3], + mcp->mb[6], mcp->mb[7], mcp->mb[10], mcp->mb[11], + mcp->mb[12])); if (cur_xchg_cnt) *cur_xchg_cnt = mcp->mb[3]; @@ -2041,6 +2044,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, *orig_iocb_cnt = mcp->mb[10]; if (vha->hw->flags.npiv_supported && max_npiv_vports) *max_npiv_vports = mcp->mb[11]; + if (IS_QLA81XX(vha->hw) && max_fcfs) + *max_fcfs = mcp->mb[12]; } return (rval); @@ -2313,6 +2318,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, { int rval, rval2; struct tsk_mgmt_cmd *tsk; + struct sts_entry_24xx *sts; dma_addr_t tsk_dma; scsi_qla_host_t *vha; struct qla_hw_data *ha; @@ -2352,20 +2358,37 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, sizeof(tsk->p.tsk.lun)); } + sts = &tsk->p.sts; rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB " "(%x).\n", __func__, vha->host_no, name, rval)); - } else if (tsk->p.sts.entry_status != 0) { + } else if (sts->entry_status != 0) { DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " "-- error status (%x).\n", __func__, vha->host_no, - tsk->p.sts.entry_status)); + sts->entry_status)); rval = QLA_FUNCTION_FAILED; - } else if (tsk->p.sts.comp_status != + } else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " "-- completion status (%x).\n", __func__, - vha->host_no, le16_to_cpu(tsk->p.sts.comp_status))); + vha->host_no, le16_to_cpu(sts->comp_status))); + rval = QLA_FUNCTION_FAILED; + } else if (!(le16_to_cpu(sts->scsi_status) & + SS_RESPONSE_INFO_LEN_VALID)) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- no response info (%x).\n", __func__, vha->host_no, + le16_to_cpu(sts->scsi_status))); + rval = QLA_FUNCTION_FAILED; + } else if (le32_to_cpu(sts->rsp_data_len) < 4) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- not enough response info (%d).\n", __func__, + vha->host_no, le32_to_cpu(sts->rsp_data_len))); + rval = QLA_FUNCTION_FAILED; + } else if (sts->data[3]) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- response (%x).\n", __func__, + vha->host_no, sts->data[3])); rval = QLA_FUNCTION_FAILED; } @@ -2759,8 +2782,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, vp_idx, MSB(stat), rptid_entry->port_id[2], rptid_entry->port_id[1], rptid_entry->port_id[0])); - if (vp_idx == 0) - return; + + vp = vha; + if (vp_idx == 0 && (MSB(stat) != 1)) + goto reg_needed; if (MSB(stat) == 1) { DEBUG2(printk("scsi(%ld): Could not acquire ID for " @@ -2783,8 +2808,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, * response queue. Handle it in dpc context. */ set_bit(VP_IDX_ACQUIRED, &vp->vp_flags); - set_bit(VP_DPC_NEEDED, &vha->dpc_flags); +reg_needed: + set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags); + set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags); + set_bit(VP_DPC_NEEDED, &vha->dpc_flags); qla2xxx_wake_dpc(vha); } } diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index e07b3617f019..a47d34308a3a 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -382,8 +382,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->mgmt_svr_loop_id = 10 + vha->vp_idx; vha->dpc_flags = 0L; - set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); - set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); /* * To fix the issue of processing a parent's RSCN for the vport before diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index b79fca7d461b..41669357b186 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -11,6 +11,7 @@ #include <linux/delay.h> #include <linux/kthread.h> #include <linux/mutex.h> +#include <linux/kobject.h> #include <scsi/scsi_tcq.h> #include <scsi/scsicam.h> @@ -137,7 +138,7 @@ static int qla2xxx_eh_target_reset(struct scsi_cmnd *); static int qla2xxx_eh_bus_reset(struct scsi_cmnd *); static int qla2xxx_eh_host_reset(struct scsi_cmnd *); -static int qla2x00_change_queue_depth(struct scsi_device *, int); +static int qla2x00_change_queue_depth(struct scsi_device *, int, int); static int qla2x00_change_queue_type(struct scsi_device *, int); struct scsi_host_template qla2xxx_driver_template = { @@ -727,23 +728,6 @@ qla2x00_abort_fcport_cmds(fc_port_t *fcport) spin_unlock_irqrestore(&ha->hardware_lock, flags); } -static void -qla2x00_block_error_handler(struct scsi_cmnd *cmnd) -{ - struct Scsi_Host *shost = cmnd->device->host; - struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); - unsigned long flags; - - spin_lock_irqsave(shost->host_lock, flags); - while (rport->port_state == FC_PORTSTATE_BLOCKED) { - spin_unlock_irqrestore(shost->host_lock, flags); - msleep(1000); - spin_lock_irqsave(shost->host_lock, flags); - } - spin_unlock_irqrestore(shost->host_lock, flags); - return; -} - /************************************************************************** * qla2xxx_eh_abort * @@ -773,7 +757,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) struct req_que *req = vha->req; srb_t *spt; - qla2x00_block_error_handler(cmd); + fc_block_scsi_eh(cmd); if (!CMD_SP(cmd)) return SUCCESS; @@ -904,7 +888,7 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; int err; - qla2x00_block_error_handler(cmd); + fc_block_scsi_eh(cmd); if (!fcport) return FAILED; @@ -984,7 +968,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) unsigned long serial; srb_t *sp = (srb_t *) CMD_SP(cmd); - qla2x00_block_error_handler(cmd); + fc_block_scsi_eh(cmd); id = cmd->device->id; lun = cmd->device->lun; @@ -1047,7 +1031,7 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) srb_t *sp = (srb_t *) CMD_SP(cmd); scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - qla2x00_block_error_handler(cmd); + fc_block_scsi_eh(cmd); id = cmd->device->id; lun = cmd->device->lun; @@ -1234,8 +1218,11 @@ qla2xxx_slave_destroy(struct scsi_device *sdev) } static int -qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth) +qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); return sdev->queue_depth; } @@ -2653,6 +2640,37 @@ qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE); qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT); qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE); +int +qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_UEVENT); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.uevent.code = code; + return qla2x00_post_work(vha, e); +} + +static void +qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code) +{ + char event_string[40]; + char *envp[] = { event_string, NULL }; + + switch (code) { + case QLA_UEVENT_CODE_FW_DUMP: + snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld", + vha->host_no); + break; + default: + /* do nothing */ + break; + } + kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp); +} + void qla2x00_do_work(struct scsi_qla_host *vha) { @@ -2690,6 +2708,9 @@ qla2x00_do_work(struct scsi_qla_host *vha) qla2x00_async_logout_done(vha, e->u.logio.fcport, e->u.logio.data); break; + case QLA_EVT_UEVENT: + qla2x00_uevent_emit(vha, e->u.uevent.code); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index ac107a2c34a4..807e0dbc67fa 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.03.01-k6" +#define QLA2XXX_VERSION "8.03.01-k7" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 3 diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h index 9c053bbaa877..e3c74d1ee2db 100644 --- a/drivers/scsi/qlogicpti.h +++ b/drivers/scsi/qlogicpti.h @@ -43,7 +43,7 @@ * determined for each queue request anew. */ #define QLOGICPTI_REQ_QUEUE_LEN 255 /* must be power of two - 1 */ -#define QLOGICPTI_MAX_SG(ql) (4 + ((ql) > 0) ? 7*((ql) - 1) : 0) +#define QLOGICPTI_MAX_SG(ql) (4 + (((ql) > 0) ? 7*((ql) - 1) : 0)) /* mailbox command complete status codes */ #define MBOX_COMMAND_COMPLETE 0x4000 diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index dd098cad337b..a60da5555577 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -940,10 +940,16 @@ EXPORT_SYMBOL(scsi_adjust_queue_depth); */ int scsi_track_queue_full(struct scsi_device *sdev, int depth) { - if ((jiffies >> 4) == sdev->last_queue_full_time) + + /* + * Don't let QUEUE_FULLs on the same + * jiffies count, they could all be from + * same event. + */ + if ((jiffies >> 4) == (sdev->last_queue_full_time >> 4)) return 0; - sdev->last_queue_full_time = (jiffies >> 4); + sdev->last_queue_full_time = jiffies; if (sdev->last_queue_full_depth != depth) { sdev->last_queue_full_count = 1; sdev->last_queue_full_depth = depth; diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index c4103bef41b5..0b575c871007 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -44,6 +44,8 @@ #include <net/checksum.h> +#include <asm/unaligned.h> + #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -105,6 +107,10 @@ static const char * scsi_debug_version_date = "20070104"; #define DEF_ATO 1 #define DEF_PHYSBLK_EXP 0 #define DEF_LOWEST_ALIGNED 0 +#define DEF_UNMAP_MAX_BLOCKS 0 +#define DEF_UNMAP_MAX_DESC 0 +#define DEF_UNMAP_GRANULARITY 0 +#define DEF_UNMAP_ALIGNMENT 0 /* bit mask values for scsi_debug_opts */ #define SCSI_DEBUG_OPT_NOISE 1 @@ -162,6 +168,10 @@ static int scsi_debug_guard = DEF_GUARD; static int scsi_debug_ato = DEF_ATO; static int scsi_debug_physblk_exp = DEF_PHYSBLK_EXP; static int scsi_debug_lowest_aligned = DEF_LOWEST_ALIGNED; +static int scsi_debug_unmap_max_desc = DEF_UNMAP_MAX_DESC; +static int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS; +static int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY; +static int scsi_debug_unmap_alignment = DEF_UNMAP_ALIGNMENT; static int scsi_debug_cmnd_count = 0; @@ -223,7 +233,9 @@ static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE]; static unsigned char * fake_storep; /* ramdisk storage */ static unsigned char *dif_storep; /* protection info */ +static void *map_storep; /* provisioning map */ +static unsigned long map_size; static int num_aborts = 0; static int num_dev_resets = 0; static int num_bus_resets = 0; @@ -317,6 +329,7 @@ static void get_data_transfer_info(unsigned char *cmd, (u32)cmd[28] << 24; break; + case WRITE_SAME_16: case WRITE_16: case READ_16: *lba = (u64)cmd[9] | (u64)cmd[8] << 8 | @@ -335,6 +348,7 @@ static void get_data_transfer_info(unsigned char *cmd, *num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 | (u32)cmd[6] << 24; break; + case WRITE_SAME: case WRITE_10: case READ_10: case XDWRITEREAD_10: @@ -671,10 +685,12 @@ static int inquiry_evpd_89(unsigned char * arr) } +/* Block limits VPD page (SBC-3) */ static unsigned char vpdb0_data[] = { - /* from 4th byte */ 0,0,0,4, - 0,0,0x4,0, - 0,0,0,64, + /* from 4th byte */ 0,0,0,4, 0,0,0x4,0, 0,0,0,64, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; static int inquiry_evpd_b0(unsigned char * arr) @@ -691,14 +707,40 @@ static int inquiry_evpd_b0(unsigned char * arr) arr[6] = (sdebug_store_sectors >> 8) & 0xff; arr[7] = sdebug_store_sectors & 0xff; } + + if (scsi_debug_unmap_max_desc) { + unsigned int blocks; + + if (scsi_debug_unmap_max_blocks) + blocks = scsi_debug_unmap_max_blocks; + else + blocks = 0xffffffff; + + put_unaligned_be32(blocks, &arr[16]); + put_unaligned_be32(scsi_debug_unmap_max_desc, &arr[20]); + } + + if (scsi_debug_unmap_alignment) { + put_unaligned_be32(scsi_debug_unmap_alignment, &arr[28]); + arr[28] |= 0x80; /* UGAVALID */ + } + + if (scsi_debug_unmap_granularity) { + put_unaligned_be32(scsi_debug_unmap_granularity, &arr[24]); + return 0x3c; /* Mandatory page length for thin provisioning */ + } + return sizeof(vpdb0_data); } +/* Block device characteristics VPD page (SBC-3) */ static int inquiry_evpd_b1(unsigned char *arr) { memset(arr, 0, 0x3c); arr[0] = 0; - arr[1] = 1; + arr[1] = 1; /* non rotating medium (e.g. solid state) */ + arr[2] = 0; + arr[3] = 5; /* less than 1.8" */ return 0x3c; } @@ -974,6 +1016,10 @@ static int resp_readcap16(struct scsi_cmnd * scp, arr[11] = scsi_debug_sector_size & 0xff; arr[13] = scsi_debug_physblk_exp & 0xf; arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f; + + if (scsi_debug_unmap_granularity) + arr[14] |= 0x80; /* TPE */ + arr[15] = scsi_debug_lowest_aligned & 0xff; if (scsi_debug_dif) { @@ -1887,6 +1933,70 @@ out: return ret; } +static unsigned int map_state(sector_t lba, unsigned int *num) +{ + unsigned int granularity, alignment, mapped; + sector_t block, next, end; + + granularity = scsi_debug_unmap_granularity; + alignment = granularity - scsi_debug_unmap_alignment; + block = lba + alignment; + do_div(block, granularity); + + mapped = test_bit(block, map_storep); + + if (mapped) + next = find_next_zero_bit(map_storep, map_size, block); + else + next = find_next_bit(map_storep, map_size, block); + + end = next * granularity - scsi_debug_unmap_alignment; + *num = end - lba; + + return mapped; +} + +static void map_region(sector_t lba, unsigned int len) +{ + unsigned int granularity, alignment; + sector_t end = lba + len; + + granularity = scsi_debug_unmap_granularity; + alignment = granularity - scsi_debug_unmap_alignment; + + while (lba < end) { + sector_t block, rem; + + block = lba + alignment; + rem = do_div(block, granularity); + + set_bit(block, map_storep); + + lba += granularity - rem; + } +} + +static void unmap_region(sector_t lba, unsigned int len) +{ + unsigned int granularity, alignment; + sector_t end = lba + len; + + granularity = scsi_debug_unmap_granularity; + alignment = granularity - scsi_debug_unmap_alignment; + + while (lba < end) { + sector_t block, rem; + + block = lba + alignment; + rem = do_div(block, granularity); + + if (rem == 0 && lba + granularity <= end) + clear_bit(block, map_storep); + + lba += granularity - rem; + } +} + static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, unsigned int num, struct sdebug_dev_info *devip, u32 ei_lba) @@ -1910,6 +2020,8 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, write_lock_irqsave(&atomic_rw, iflags); ret = do_device_access(SCpnt, devip, lba, num, 1); + if (scsi_debug_unmap_granularity) + map_region(lba, num); write_unlock_irqrestore(&atomic_rw, iflags); if (-1 == ret) return (DID_ERROR << 16); @@ -1917,9 +2029,143 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, " " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret); + + return 0; +} + +static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba, + unsigned int num, struct sdebug_dev_info *devip, + u32 ei_lba, unsigned int unmap) +{ + unsigned long iflags; + unsigned long long i; + int ret; + + ret = check_device_access_params(devip, lba, num); + if (ret) + return ret; + + write_lock_irqsave(&atomic_rw, iflags); + + if (unmap && scsi_debug_unmap_granularity) { + unmap_region(lba, num); + goto out; + } + + /* Else fetch one logical block */ + ret = fetch_to_dev_buffer(scmd, + fake_storep + (lba * scsi_debug_sector_size), + scsi_debug_sector_size); + + if (-1 == ret) { + write_unlock_irqrestore(&atomic_rw, iflags); + return (DID_ERROR << 16); + } else if ((ret < (num * scsi_debug_sector_size)) && + (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) + printk(KERN_INFO "scsi_debug: write same: cdb indicated=%u, " + " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret); + + /* Copy first sector to remaining blocks */ + for (i = 1 ; i < num ; i++) + memcpy(fake_storep + ((lba + i) * scsi_debug_sector_size), + fake_storep + (lba * scsi_debug_sector_size), + scsi_debug_sector_size); + + if (scsi_debug_unmap_granularity) + map_region(lba, num); +out: + write_unlock_irqrestore(&atomic_rw, iflags); + return 0; } +struct unmap_block_desc { + __be64 lba; + __be32 blocks; + __be32 __reserved; +}; + +static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip) +{ + unsigned char *buf; + struct unmap_block_desc *desc; + unsigned int i, payload_len, descriptors; + int ret; + + ret = check_readiness(scmd, 1, devip); + if (ret) + return ret; + + payload_len = get_unaligned_be16(&scmd->cmnd[7]); + BUG_ON(scsi_bufflen(scmd) != payload_len); + + descriptors = (payload_len - 8) / 16; + + buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC); + if (!buf) + return check_condition_result; + + scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd)); + + BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2); + BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16); + + desc = (void *)&buf[8]; + + for (i = 0 ; i < descriptors ; i++) { + unsigned long long lba = get_unaligned_be64(&desc[i].lba); + unsigned int num = get_unaligned_be32(&desc[i].blocks); + + ret = check_device_access_params(devip, lba, num); + if (ret) + goto out; + + unmap_region(lba, num); + } + + ret = 0; + +out: + kfree(buf); + + return ret; +} + +#define SDEBUG_GET_LBA_STATUS_LEN 32 + +static int resp_get_lba_status(struct scsi_cmnd * scmd, + struct sdebug_dev_info * devip) +{ + unsigned long long lba; + unsigned int alloc_len, mapped, num; + unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN]; + int ret; + + ret = check_readiness(scmd, 1, devip); + if (ret) + return ret; + + lba = get_unaligned_be64(&scmd->cmnd[2]); + alloc_len = get_unaligned_be32(&scmd->cmnd[10]); + + if (alloc_len < 24) + return 0; + + ret = check_device_access_params(devip, lba, 1); + if (ret) + return ret; + + mapped = map_state(lba, &num); + + memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN); + put_unaligned_be32(16, &arr[0]); /* Parameter Data Length */ + put_unaligned_be64(lba, &arr[8]); /* LBA */ + put_unaligned_be32(num, &arr[16]); /* Number of blocks */ + arr[20] = !mapped; /* mapped = 0, unmapped = 1 */ + + return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN); +} + #define SDEBUG_RLUN_ARR_SZ 256 static int resp_report_luns(struct scsi_cmnd * scp, @@ -2430,6 +2676,10 @@ module_param_named(guard, scsi_debug_guard, int, S_IRUGO); module_param_named(ato, scsi_debug_ato, int, S_IRUGO); module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO); module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO); +module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO); +module_param_named(unmap_max_desc, scsi_debug_unmap_max_desc, int, S_IRUGO); +module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO); +module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO); MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); MODULE_DESCRIPTION("SCSI debug adapter driver"); @@ -2458,6 +2708,10 @@ MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)"); MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)"); MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)"); MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)"); +MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0)"); +MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=0)"); +MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=0)"); +MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)"); static char sdebug_info[256]; @@ -2816,6 +3070,23 @@ static ssize_t sdebug_ato_show(struct device_driver *ddp, char *buf) } DRIVER_ATTR(ato, S_IRUGO, sdebug_ato_show, NULL); +static ssize_t sdebug_map_show(struct device_driver *ddp, char *buf) +{ + ssize_t count; + + if (scsi_debug_unmap_granularity == 0) + return scnprintf(buf, PAGE_SIZE, "0-%u\n", + sdebug_store_sectors); + + count = bitmap_scnlistprintf(buf, PAGE_SIZE, map_storep, map_size); + + buf[count++] = '\n'; + buf[count++] = 0; + + return count; +} +DRIVER_ATTR(map, S_IRUGO, sdebug_map_show, NULL); + /* Note: The following function creates attribute files in the /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these @@ -2847,11 +3118,13 @@ static int do_create_driverfs_files(void) ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dif); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_guard); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ato); + ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_map); return ret; } static void do_remove_driverfs_files(void) { + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_map); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ato); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_guard); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dif); @@ -2989,6 +3262,36 @@ static int __init scsi_debug_init(void) memset(dif_storep, 0xff, dif_size); } + if (scsi_debug_unmap_granularity) { + unsigned int map_bytes; + + if (scsi_debug_unmap_granularity < scsi_debug_unmap_alignment) { + printk(KERN_ERR + "%s: ERR: unmap_granularity < unmap_alignment\n", + __func__); + return -EINVAL; + } + + map_size = (sdebug_store_sectors / scsi_debug_unmap_granularity); + map_bytes = map_size >> 3; + map_storep = vmalloc(map_bytes); + + printk(KERN_INFO "scsi_debug_init: %lu provisioning blocks\n", + map_size); + + if (map_storep == NULL) { + printk(KERN_ERR "scsi_debug_init: out of mem. (MAP)\n"); + ret = -ENOMEM; + goto free_vm; + } + + memset(map_storep, 0x0, map_bytes); + + /* Map first 1KB for partition table */ + if (scsi_debug_num_parts) + map_region(0, 2); + } + ret = device_register(&pseudo_primary); if (ret < 0) { printk(KERN_WARNING "scsi_debug: device_register error: %d\n", @@ -3041,6 +3344,8 @@ bus_unreg: dev_unreg: device_unregister(&pseudo_primary); free_vm: + if (map_storep) + vfree(map_storep); if (dif_storep) vfree(dif_storep); vfree(fake_storep); @@ -3167,6 +3472,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) int inj_dif = 0; int inj_dix = 0; int delay_override = 0; + int unmap = 0; scsi_set_resid(SCpnt, 0); if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) { @@ -3272,13 +3578,21 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) errsts = resp_readcap(SCpnt, devip); break; case SERVICE_ACTION_IN: - if (SAI_READ_CAPACITY_16 != cmd[1]) { + if (cmd[1] == SAI_READ_CAPACITY_16) + errsts = resp_readcap16(SCpnt, devip); + else if (cmd[1] == SAI_GET_LBA_STATUS) { + + if (scsi_debug_unmap_max_desc == 0) { + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_COMMAND_OPCODE, 0); + errsts = check_condition_result; + } else + errsts = resp_get_lba_status(SCpnt, devip); + } else { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0); errsts = check_condition_result; - break; } - errsts = resp_readcap16(SCpnt, devip); break; case MAINTENANCE_IN: if (MI_REPORT_TARGET_PGS != cmd[1]) { @@ -3378,6 +3692,29 @@ write: errsts = illegal_condition_result; } break; + case WRITE_SAME_16: + if (cmd[1] & 0x8) + unmap = 1; + /* fall through */ + case WRITE_SAME: + errsts = check_readiness(SCpnt, 0, devip); + if (errsts) + break; + get_data_transfer_info(cmd, &lba, &num, &ei_lba); + errsts = resp_write_same(SCpnt, lba, num, devip, ei_lba, unmap); + break; + case UNMAP: + errsts = check_readiness(SCpnt, 0, devip); + if (errsts) + break; + + if (scsi_debug_unmap_max_desc == 0) { + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_COMMAND_OPCODE, 0); + errsts = check_condition_result; + } else + errsts = resp_unmap(SCpnt, devip); + break; case MODE_SENSE: case MODE_SENSE_10: errsts = resp_mode_sense(SCpnt, target, devip); diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 93c2622cb969..37af178b2d17 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -168,11 +168,10 @@ static struct { {"Generic", "USB SD Reader", "1.00", BLIST_FORCELUN | BLIST_INQUIRY_36}, {"Generic", "USB Storage-SMC", "0180", BLIST_FORCELUN | BLIST_INQUIRY_36}, {"Generic", "USB Storage-SMC", "0207", BLIST_FORCELUN | BLIST_INQUIRY_36}, - {"HITACHI", "DF400", "*", BLIST_SPARSELUN}, - {"HITACHI", "DF500", "*", BLIST_SPARSELUN}, - {"HITACHI", "DF600", "*", BLIST_SPARSELUN}, - {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN}, - {"HITACHI", "OPEN-E", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HITACHI", "DF400", "*", BLIST_REPORTLUN2}, + {"HITACHI", "DF500", "*", BLIST_REPORTLUN2}, + {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_REPORTLUN2}, + {"HITACHI", "OPEN-", "*", BLIST_REPORTLUN2}, {"HITACHI", "OP-C-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"HITACHI", "3380-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"HITACHI", "3390-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, @@ -454,7 +453,7 @@ int scsi_get_device_flags(struct scsi_device *sdev, /** - * get_device_flags_keyed - get device specific flags from the dynamic device list. + * scsi_get_device_flags_keyed - get device specific flags from the dynamic device list * @sdev: &scsi_device to get flags for * @vendor: vendor name * @model: model name @@ -685,7 +684,7 @@ MODULE_PARM_DESC(default_dev_flags, "scsi default device flag integer value"); /** - * scsi_dev_info_list_delete - called from scsi.c:exit_scsi to remove the scsi_dev_info_list. + * scsi_exit_devinfo - remove /proc/scsi/device_info & the scsi_dev_info_list **/ void scsi_exit_devinfo(void) { diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 1b0060b791e8..08ed506e6059 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -331,6 +331,64 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) } } +static void scsi_handle_queue_ramp_up(struct scsi_device *sdev) +{ + struct scsi_host_template *sht = sdev->host->hostt; + struct scsi_device *tmp_sdev; + + if (!sht->change_queue_depth || + sdev->queue_depth >= sdev->max_queue_depth) + return; + + if (time_before(jiffies, + sdev->last_queue_ramp_up + sdev->queue_ramp_up_period)) + return; + + if (time_before(jiffies, + sdev->last_queue_full_time + sdev->queue_ramp_up_period)) + return; + + /* + * Walk all devices of a target and do + * ramp up on them. + */ + shost_for_each_device(tmp_sdev, sdev->host) { + if (tmp_sdev->channel != sdev->channel || + tmp_sdev->id != sdev->id || + tmp_sdev->queue_depth == sdev->max_queue_depth) + continue; + /* + * call back into LLD to increase queue_depth by one + * with ramp up reason code. + */ + sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1, + SCSI_QDEPTH_RAMP_UP); + sdev->last_queue_ramp_up = jiffies; + } +} + +static void scsi_handle_queue_full(struct scsi_device *sdev) +{ + struct scsi_host_template *sht = sdev->host->hostt; + struct scsi_device *tmp_sdev; + + if (!sht->change_queue_depth) + return; + + shost_for_each_device(tmp_sdev, sdev->host) { + if (tmp_sdev->channel != sdev->channel || + tmp_sdev->id != sdev->id) + continue; + /* + * We do not know the number of commands that were at + * the device when we got the queue full so we start + * from the highest possible value and work our way down. + */ + sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth - 1, + SCSI_QDEPTH_QFULL); + } +} + /** * scsi_eh_completed_normally - Disposition a eh cmd on return from LLD. * @scmd: SCSI cmd to examine. @@ -371,6 +429,7 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd) */ switch (status_byte(scmd->result)) { case GOOD: + scsi_handle_queue_ramp_up(scmd->device); case COMMAND_TERMINATED: return SUCCESS; case CHECK_CONDITION: @@ -387,8 +446,10 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd) * let issuer deal with this, it could be just fine */ return SUCCESS; - case BUSY: case QUEUE_FULL: + scsi_handle_queue_full(scmd->device); + /* fall through */ + case BUSY: default: return FAILED; } @@ -1387,6 +1448,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) */ switch (status_byte(scmd->result)) { case QUEUE_FULL: + scsi_handle_queue_full(scmd->device); /* * the case of trying to send too many commands to a * tagged queueing device. @@ -1400,6 +1462,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) */ return ADD_TO_MLQUEUE; case GOOD: + scsi_handle_queue_ramp_up(scmd->device); case COMMAND_TERMINATED: return SUCCESS; case TASK_ABORTED: diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index b98f763931c5..d9564fb04f62 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -308,6 +308,9 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, case SG_SCSI_RESET_DEVICE: val = SCSI_TRY_RESET_DEVICE; break; + case SG_SCSI_RESET_TARGET: + val = SCSI_TRY_RESET_TARGET; + break; case SG_SCSI_RESET_BUS: val = SCSI_TRY_RESET_BUS; break; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 5987da857103..e495d3813948 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -898,7 +898,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) scsi_print_sense("", cmd); scsi_print_command(cmd); } - if (blk_end_request_err(req, -EIO)) + if (blk_end_request_err(req, error)) scsi_requeue_command(q, cmd); else scsi_next_command(cmd); @@ -1359,9 +1359,9 @@ static int scsi_lld_busy(struct request_queue *q) static void scsi_kill_request(struct request *req, struct request_queue *q) { struct scsi_cmnd *cmd = req->special; - struct scsi_device *sdev = cmd->device; - struct scsi_target *starget = scsi_target(sdev); - struct Scsi_Host *shost = sdev->host; + struct scsi_device *sdev; + struct scsi_target *starget; + struct Scsi_Host *shost; blk_start_request(req); @@ -1371,6 +1371,9 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) BUG(); } + sdev = cmd->device; + starget = scsi_target(sdev); + shost = sdev->host; scsi_init_cmd_errh(cmd); cmd->result = DID_NO_CONNECT << 16; atomic_inc(&cmd->device->iorequest_cnt); diff --git a/drivers/scsi/scsi_lib_dma.c b/drivers/scsi/scsi_lib_dma.c index ac6855cd2657..dcd128583b89 100644 --- a/drivers/scsi/scsi_lib_dma.c +++ b/drivers/scsi/scsi_lib_dma.c @@ -23,7 +23,7 @@ int scsi_dma_map(struct scsi_cmnd *cmd) int nseg = 0; if (scsi_sg_count(cmd)) { - struct device *dev = cmd->device->host->shost_gendev.parent; + struct device *dev = cmd->device->host->dma_dev; nseg = dma_map_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), cmd->sc_data_direction); @@ -41,7 +41,7 @@ EXPORT_SYMBOL(scsi_dma_map); void scsi_dma_unmap(struct scsi_cmnd *cmd) { if (scsi_sg_count(cmd)) { - struct device *dev = cmd->device->host->shost_gendev.parent; + struct device *dev = cmd->device->host->dma_dev; dma_unmap_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), cmd->sc_data_direction); diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index 723fdecd91bd..0fd6ae6911ad 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -613,7 +613,7 @@ EXPORT_SYMBOL_GPL(scsi_nl_send_transport_msg); * @data_buf: pointer to vendor unique data buffer * * Returns: - * 0 on succesful return + * 0 on successful return * otherwise, failing error code * * Notes: diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 47291bcff0d5..012f73a96880 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -251,6 +251,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, sdev->model = scsi_null_device_strs; sdev->rev = scsi_null_device_strs; sdev->host = shost; + sdev->queue_ramp_up_period = SCSI_DEFAULT_RAMP_UP_PERIOD; sdev->id = starget->id; sdev->lun = lun; sdev->channel = starget->channel; @@ -941,6 +942,8 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, } } + sdev->max_queue_depth = sdev->queue_depth; + /* * Ok, the device is now all set up, we can * register it and tell the rest of the kernel diff --git a/drivers/scsi/scsi_sysctl.c b/drivers/scsi/scsi_sysctl.c index 63a30f566f3a..2b6b93f7d8ef 100644 --- a/drivers/scsi/scsi_sysctl.c +++ b/drivers/scsi/scsi_sysctl.c @@ -13,26 +13,23 @@ static ctl_table scsi_table[] = { - { .ctl_name = DEV_SCSI_LOGGING_LEVEL, - .procname = "logging_level", + { .procname = "logging_level", .data = &scsi_logging_level, .maxlen = sizeof(scsi_logging_level), .mode = 0644, - .proc_handler = &proc_dointvec }, + .proc_handler = proc_dointvec }, { } }; static ctl_table scsi_dir_table[] = { - { .ctl_name = DEV_SCSI, - .procname = "scsi", + { .procname = "scsi", .mode = 0555, .child = scsi_table }, { } }; static ctl_table scsi_root_table[] = { - { .ctl_name = CTL_DEV, - .procname = "dev", + { .procname = "dev", .mode = 0555, .child = scsi_dir_table }, { } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 392d8db33905..5a065055e68a 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -766,10 +766,13 @@ sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr, if (depth < 1) return -EINVAL; - retval = sht->change_queue_depth(sdev, depth); + retval = sht->change_queue_depth(sdev, depth, + SCSI_QDEPTH_DEFAULT); if (retval < 0) return retval; + sdev->max_queue_depth = sdev->queue_depth; + return count; } @@ -778,6 +781,37 @@ static struct device_attribute sdev_attr_queue_depth_rw = sdev_store_queue_depth_rw); static ssize_t +sdev_show_queue_ramp_up_period(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev; + sdev = to_scsi_device(dev); + return snprintf(buf, 20, "%u\n", + jiffies_to_msecs(sdev->queue_ramp_up_period)); +} + +static ssize_t +sdev_store_queue_ramp_up_period(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + unsigned long period; + + if (strict_strtoul(buf, 10, &period)) + return -EINVAL; + + sdev->queue_ramp_up_period = msecs_to_jiffies(period); + return period; +} + +static struct device_attribute sdev_attr_queue_ramp_up_period = + __ATTR(queue_ramp_up_period, S_IRUGO | S_IWUSR, + sdev_show_queue_ramp_up_period, + sdev_store_queue_ramp_up_period); + +static ssize_t sdev_store_queue_type_rw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -867,8 +901,12 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) sdev->is_visible = 1; /* create queue files, which may be writable, depending on the host */ - if (sdev->host->hostt->change_queue_depth) - error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_depth_rw); + if (sdev->host->hostt->change_queue_depth) { + error = device_create_file(&sdev->sdev_gendev, + &sdev_attr_queue_depth_rw); + error = device_create_file(&sdev->sdev_gendev, + &sdev_attr_queue_ramp_up_period); + } else error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth); if (error) diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index c6f70dae9b2e..6531c91501be 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -27,6 +27,7 @@ */ #include <linux/module.h> #include <linux/init.h> +#include <linux/delay.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport.h> @@ -2384,6 +2385,7 @@ fc_rport_final_delete(struct work_struct *work) struct Scsi_Host *shost = rport_to_shost(rport); struct fc_internal *i = to_fc_internal(shost->transportt); unsigned long flags; + int do_callback = 0; /* * if a scan is pending, flush the SCSI Host work_q so that @@ -2422,8 +2424,15 @@ fc_rport_final_delete(struct work_struct *work) * Avoid this call if we already called it when we preserved the * rport for the binding. */ + spin_lock_irqsave(shost->host_lock, flags); if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) && - (i->f->dev_loss_tmo_callbk)) + (i->f->dev_loss_tmo_callbk)) { + rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE; + do_callback = 1; + } + spin_unlock_irqrestore(shost->host_lock, flags); + + if (do_callback) i->f->dev_loss_tmo_callbk(rport); fc_bsg_remove(rport->rqst_q); @@ -2970,6 +2979,7 @@ fc_timeout_deleted_rport(struct work_struct *work) struct fc_internal *i = to_fc_internal(shost->transportt); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); unsigned long flags; + int do_callback = 0; spin_lock_irqsave(shost->host_lock, flags); @@ -3035,7 +3045,6 @@ fc_timeout_deleted_rport(struct work_struct *work) rport->roles = FC_PORT_ROLE_UNKNOWN; rport->port_state = FC_PORTSTATE_NOTPRESENT; rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT; - rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE; /* * Pre-emptively kill I/O rather than waiting for the work queue @@ -3045,32 +3054,40 @@ fc_timeout_deleted_rport(struct work_struct *work) spin_unlock_irqrestore(shost->host_lock, flags); fc_terminate_rport_io(rport); - BUG_ON(rport->port_state != FC_PORTSTATE_NOTPRESENT); + spin_lock_irqsave(shost->host_lock, flags); + + if (rport->port_state == FC_PORTSTATE_NOTPRESENT) { /* still missing */ - /* remove the identifiers that aren't used in the consisting binding */ - switch (fc_host->tgtid_bind_type) { - case FC_TGTID_BIND_BY_WWPN: - rport->node_name = -1; - rport->port_id = -1; - break; - case FC_TGTID_BIND_BY_WWNN: - rport->port_name = -1; - rport->port_id = -1; - break; - case FC_TGTID_BIND_BY_ID: - rport->node_name = -1; - rport->port_name = -1; - break; - case FC_TGTID_BIND_NONE: /* to keep compiler happy */ - break; + /* remove the identifiers that aren't used in the consisting binding */ + switch (fc_host->tgtid_bind_type) { + case FC_TGTID_BIND_BY_WWPN: + rport->node_name = -1; + rport->port_id = -1; + break; + case FC_TGTID_BIND_BY_WWNN: + rport->port_name = -1; + rport->port_id = -1; + break; + case FC_TGTID_BIND_BY_ID: + rport->node_name = -1; + rport->port_name = -1; + break; + case FC_TGTID_BIND_NONE: /* to keep compiler happy */ + break; + } + + /* + * As this only occurs if the remote port (scsi target) + * went away and didn't come back - we'll remove + * all attached scsi devices. + */ + rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE; + fc_queue_work(shost, &rport->stgt_delete_work); + + do_callback = 1; } - /* - * As this only occurs if the remote port (scsi target) - * went away and didn't come back - we'll remove - * all attached scsi devices. - */ - fc_queue_work(shost, &rport->stgt_delete_work); + spin_unlock_irqrestore(shost->host_lock, flags); /* * Notify the driver that the rport is now dead. The LLDD will @@ -3078,7 +3095,7 @@ fc_timeout_deleted_rport(struct work_struct *work) * * Note: we set the CALLBK_DONE flag above to correspond */ - if (i->f->dev_loss_tmo_callbk) + if (do_callback && i->f->dev_loss_tmo_callbk) i->f->dev_loss_tmo_callbk(rport); } @@ -3128,6 +3145,31 @@ fc_scsi_scan_rport(struct work_struct *work) spin_unlock_irqrestore(shost->host_lock, flags); } +/** + * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport + * @cmnd: SCSI command that scsi_eh is trying to recover + * + * This routine can be called from a FC LLD scsi_eh callback. It + * blocks the scsi_eh thread until the fc_rport leaves the + * FC_PORTSTATE_BLOCKED. This is necessary to avoid the scsi_eh + * failing recovery actions for blocked rports which would lead to + * offlined SCSI devices. + */ +void fc_block_scsi_eh(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); + unsigned long flags; + + spin_lock_irqsave(shost->host_lock, flags); + while (rport->port_state == FC_PORTSTATE_BLOCKED) { + spin_unlock_irqrestore(shost->host_lock, flags); + msleep(1000); + spin_lock_irqsave(shost->host_lock, flags); + } + spin_unlock_irqrestore(shost->host_lock, flags); +} +EXPORT_SYMBOL(fc_block_scsi_eh); /** * fc_vport_setup - allocates and creates a FC virtual port. @@ -3769,8 +3811,9 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost, return; while (!blk_queue_plugged(q)) { - if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED)) - break; + if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED) && + !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)) + break; req = blk_fetch_request(q); if (!req) diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index ad897df36615..ea3892e7e0f7 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -30,7 +30,7 @@ #include <scsi/scsi_transport_iscsi.h> #include <scsi/iscsi_if.h> -#define ISCSI_SESSION_ATTRS 21 +#define ISCSI_SESSION_ATTRS 22 #define ISCSI_CONN_ATTRS 13 #define ISCSI_HOST_ATTRS 4 @@ -627,8 +627,10 @@ static void __iscsi_block_session(struct work_struct *work) spin_unlock_irqrestore(&session->lock, flags); scsi_target_block(&session->dev); ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n"); - queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, - session->recovery_tmo * HZ); + if (session->recovery_tmo >= 0) + queue_delayed_work(iscsi_eh_timer_workq, + &session->recovery_work, + session->recovery_tmo * HZ); } void iscsi_block_session(struct iscsi_cls_session *session) @@ -1348,8 +1350,7 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) switch (ev->u.set_param.param) { case ISCSI_PARAM_SESS_RECOVERY_TMO: sscanf(data, "%d", &value); - if (value != 0) - session->recovery_tmo = value; + session->recovery_tmo = value; break; default: err = transport->set_param(conn, ev->u.set_param.param, @@ -1759,6 +1760,7 @@ iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1); iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0); iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0); iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0); +iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0); iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0); iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0) @@ -2000,6 +2002,7 @@ iscsi_register_transport(struct iscsi_transport *tt) SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT); SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO); SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO); + SETUP_SESSION_RD_ATTR(tgt_reset_tmo,ISCSI_TGT_RESET_TMO); SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME); SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME); SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo); diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index fd47cb1bee1b..f27e52d963d3 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -666,7 +666,7 @@ EXPORT_SYMBOL(sas_phy_add); * * Note: * This function must only be called on a PHY that has not - * sucessfully been added using sas_phy_add(). + * successfully been added using sas_phy_add(). */ void sas_phy_free(struct sas_phy *phy) { @@ -896,7 +896,7 @@ EXPORT_SYMBOL(sas_port_add); * * Note: * This function must only be called on a PORT that has not - * sucessfully been added using sas_port_add(). + * successfully been added using sas_port_add(). */ void sas_port_free(struct sas_port *port) { @@ -1476,7 +1476,7 @@ EXPORT_SYMBOL(sas_rphy_add); * * Note: * This function must only be called on a remote - * PHY that has not sucessfully been added using + * PHY that has not successfully been added using * sas_rphy_add() (or has been sas_rphy_remove()'d) */ void sas_rphy_free(struct sas_rphy *rphy) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 12d58a7ed6bc..ad59abb47722 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -2280,7 +2280,8 @@ static int st_set_options(struct scsi_tape *STp, long options) } else if (code == MT_ST_SET_CLN) { value = (options & ~MT_ST_OPTIONS) & 0xff; if (value != 0 && - value < EXTENDED_SENSE_START && value >= SCSI_SENSE_BUFFERSIZE) + (value < EXTENDED_SENSE_START || + value >= SCSI_SENSE_BUFFERSIZE)) return (-EINVAL); STp->cln_mode = value; STp->cln_sense_mask = (options >> 8) & 0xff; diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 09fa8861fc58..3058bb1aff95 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -36,11 +36,11 @@ #include <scsi/scsi_eh.h> #define DRV_NAME "stex" -#define ST_DRIVER_VERSION "4.6.0000.3" +#define ST_DRIVER_VERSION "4.6.0000.4" #define ST_VER_MAJOR 4 #define ST_VER_MINOR 6 #define ST_OEM 0 -#define ST_BUILD_VER 3 +#define ST_BUILD_VER 4 enum { /* MU register offset */ @@ -64,24 +64,24 @@ enum { YH2I_REQ_HI = 0xc4, /* MU register value */ - MU_INBOUND_DOORBELL_HANDSHAKE = 1, - MU_INBOUND_DOORBELL_REQHEADCHANGED = 2, - MU_INBOUND_DOORBELL_STATUSTAILCHANGED = 4, - MU_INBOUND_DOORBELL_HMUSTOPPED = 8, - MU_INBOUND_DOORBELL_RESET = 16, - - MU_OUTBOUND_DOORBELL_HANDSHAKE = 1, - MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = 2, - MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = 4, - MU_OUTBOUND_DOORBELL_BUSCHANGE = 8, - MU_OUTBOUND_DOORBELL_HASEVENT = 16, + MU_INBOUND_DOORBELL_HANDSHAKE = (1 << 0), + MU_INBOUND_DOORBELL_REQHEADCHANGED = (1 << 1), + MU_INBOUND_DOORBELL_STATUSTAILCHANGED = (1 << 2), + MU_INBOUND_DOORBELL_HMUSTOPPED = (1 << 3), + MU_INBOUND_DOORBELL_RESET = (1 << 4), + + MU_OUTBOUND_DOORBELL_HANDSHAKE = (1 << 0), + MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = (1 << 1), + MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = (1 << 2), + MU_OUTBOUND_DOORBELL_BUSCHANGE = (1 << 3), + MU_OUTBOUND_DOORBELL_HASEVENT = (1 << 4), + MU_OUTBOUND_DOORBELL_REQUEST_RESET = (1 << 27), /* MU status code */ MU_STATE_STARTING = 1, - MU_STATE_FMU_READY_FOR_HANDSHAKE = 2, - MU_STATE_SEND_HANDSHAKE_FRAME = 3, - MU_STATE_STARTED = 4, - MU_STATE_RESETTING = 5, + MU_STATE_STARTED = 2, + MU_STATE_RESETTING = 3, + MU_STATE_FAILED = 4, MU_MAX_DELAY = 120, MU_HANDSHAKE_SIGNATURE = 0x55aaaa55, @@ -111,6 +111,8 @@ enum { SS_H2I_INT_RESET = 0x100, + SS_I2H_REQUEST_RESET = 0x2000, + SS_MU_OPERATIONAL = 0x80000000, STEX_CDB_LENGTH = 16, @@ -160,6 +162,7 @@ enum { INQUIRY_EVPD = 0x01, ST_ADDITIONAL_MEM = 0x200000, + ST_ADDITIONAL_MEM_MIN = 0x80000, }; struct st_sgitem { @@ -311,6 +314,10 @@ struct st_hba { struct st_ccb *wait_ccb; __le32 *scratch; + char work_q_name[20]; + struct workqueue_struct *work_q; + struct work_struct reset_work; + wait_queue_head_t reset_waitq; unsigned int mu_status; unsigned int cardtype; int msi_enabled; @@ -577,6 +584,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) lun = cmd->device->lun; hba = (struct st_hba *) &host->hostdata[0]; + if (unlikely(hba->mu_status == MU_STATE_RESETTING)) + return SCSI_MLQUEUE_HOST_BUSY; + switch (cmd->cmnd[0]) { case MODE_SENSE_10: { @@ -841,7 +851,6 @@ static irqreturn_t stex_intr(int irq, void *__hba) void __iomem *base = hba->mmio_base; u32 data; unsigned long flags; - int handled = 0; spin_lock_irqsave(hba->host->host_lock, flags); @@ -852,12 +861,16 @@ static irqreturn_t stex_intr(int irq, void *__hba) writel(data, base + ODBL); readl(base + ODBL); /* flush */ stex_mu_intr(hba, data); - handled = 1; + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (unlikely(data & MU_OUTBOUND_DOORBELL_REQUEST_RESET && + hba->cardtype == st_shasta)) + queue_work(hba->work_q, &hba->reset_work); + return IRQ_HANDLED; } spin_unlock_irqrestore(hba->host->host_lock, flags); - return IRQ_RETVAL(handled); + return IRQ_NONE; } static void stex_ss_mu_intr(struct st_hba *hba) @@ -939,7 +952,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba) void __iomem *base = hba->mmio_base; u32 data; unsigned long flags; - int handled = 0; spin_lock_irqsave(hba->host->host_lock, flags); @@ -948,12 +960,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba) /* clear the interrupt */ writel(data, base + YI2H_INT_C); stex_ss_mu_intr(hba); - handled = 1; + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (unlikely(data & SS_I2H_REQUEST_RESET)) + queue_work(hba->work_q, &hba->reset_work); + return IRQ_HANDLED; } spin_unlock_irqrestore(hba->host->host_lock, flags); - return IRQ_RETVAL(handled); + return IRQ_NONE; } static int stex_common_handshake(struct st_hba *hba) @@ -1001,7 +1016,7 @@ static int stex_common_handshake(struct st_hba *hba) h->partner_type = HMU_PARTNER_TYPE; if (hba->extra_offset) { h->extra_offset = cpu_to_le32(hba->extra_offset); - h->extra_size = cpu_to_le32(ST_ADDITIONAL_MEM); + h->extra_size = cpu_to_le32(hba->dma_size - hba->extra_offset); } else h->extra_offset = h->extra_size = 0; @@ -1046,7 +1061,7 @@ static int stex_ss_handshake(struct st_hba *hba) struct st_msg_header *msg_h; struct handshake_frame *h; __le32 *scratch; - u32 data; + u32 data, scratch_size; unsigned long before; int ret = 0; @@ -1074,13 +1089,16 @@ static int stex_ss_handshake(struct st_hba *hba) stex_gettime(&h->hosttime); h->partner_type = HMU_PARTNER_TYPE; h->extra_offset = h->extra_size = 0; - h->scratch_size = cpu_to_le32((hba->sts_count+1)*sizeof(u32)); + scratch_size = (hba->sts_count+1)*sizeof(u32); + h->scratch_size = cpu_to_le32(scratch_size); data = readl(base + YINT_EN); data &= ~4; writel(data, base + YINT_EN); writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI); + readl(base + YH2I_REQ_HI); writel(hba->dma_handle, base + YH2I_REQ); + readl(base + YH2I_REQ); /* flush */ scratch = hba->scratch; before = jiffies; @@ -1096,7 +1114,7 @@ static int stex_ss_handshake(struct st_hba *hba) msleep(1); } - *scratch = 0; + memset(scratch, 0, scratch_size); msg_h->flag = 0; return ret; } @@ -1105,19 +1123,24 @@ static int stex_handshake(struct st_hba *hba) { int err; unsigned long flags; + unsigned int mu_status; err = (hba->cardtype == st_yel) ? stex_ss_handshake(hba) : stex_common_handshake(hba); + spin_lock_irqsave(hba->host->host_lock, flags); + mu_status = hba->mu_status; if (err == 0) { - spin_lock_irqsave(hba->host->host_lock, flags); hba->req_head = 0; hba->req_tail = 0; hba->status_head = 0; hba->status_tail = 0; hba->out_req_cnt = 0; hba->mu_status = MU_STATE_STARTED; - spin_unlock_irqrestore(hba->host->host_lock, flags); - } + } else + hba->mu_status = MU_STATE_FAILED; + if (mu_status == MU_STATE_RESETTING) + wake_up_all(&hba->reset_waitq); + spin_unlock_irqrestore(hba->host->host_lock, flags); return err; } @@ -1137,17 +1160,11 @@ static int stex_abort(struct scsi_cmnd *cmd) base = hba->mmio_base; spin_lock_irqsave(host->host_lock, flags); - if (tag < host->can_queue && hba->ccb[tag].cmd == cmd) + if (tag < host->can_queue && + hba->ccb[tag].req && hba->ccb[tag].cmd == cmd) hba->wait_ccb = &hba->ccb[tag]; - else { - for (tag = 0; tag < host->can_queue; tag++) - if (hba->ccb[tag].cmd == cmd) { - hba->wait_ccb = &hba->ccb[tag]; - break; - } - if (tag >= host->can_queue) - goto out; - } + else + goto out; if (hba->cardtype == st_yel) { data = readl(base + YI2H_INT); @@ -1221,6 +1238,37 @@ static void stex_hard_reset(struct st_hba *hba) hba->pdev->saved_config_space[i]); } +static int stex_yos_reset(struct st_hba *hba) +{ + void __iomem *base; + unsigned long flags, before; + int ret = 0; + + base = hba->mmio_base; + writel(MU_INBOUND_DOORBELL_RESET, base + IDBL); + readl(base + IDBL); /* flush */ + before = jiffies; + while (hba->out_req_cnt > 0) { + if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) { + printk(KERN_WARNING DRV_NAME + "(%s): reset timeout\n", pci_name(hba->pdev)); + ret = -1; + break; + } + msleep(1); + } + + spin_lock_irqsave(hba->host->host_lock, flags); + if (ret == -1) + hba->mu_status = MU_STATE_FAILED; + else + hba->mu_status = MU_STATE_STARTED; + wake_up_all(&hba->reset_waitq); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + return ret; +} + static void stex_ss_reset(struct st_hba *hba) { writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT); @@ -1228,66 +1276,86 @@ static void stex_ss_reset(struct st_hba *hba) ssleep(5); } -static int stex_reset(struct scsi_cmnd *cmd) +static int stex_do_reset(struct st_hba *hba) { - struct st_hba *hba; - void __iomem *base; - unsigned long flags, before; + struct st_ccb *ccb; + unsigned long flags; + unsigned int mu_status = MU_STATE_RESETTING; + u16 tag; - hba = (struct st_hba *) &cmd->device->host->hostdata[0]; + spin_lock_irqsave(hba->host->host_lock, flags); + if (hba->mu_status == MU_STATE_STARTING) { + spin_unlock_irqrestore(hba->host->host_lock, flags); + printk(KERN_INFO DRV_NAME "(%s): request reset during init\n", + pci_name(hba->pdev)); + return 0; + } + while (hba->mu_status == MU_STATE_RESETTING) { + spin_unlock_irqrestore(hba->host->host_lock, flags); + wait_event_timeout(hba->reset_waitq, + hba->mu_status != MU_STATE_RESETTING, + MU_MAX_DELAY * HZ); + spin_lock_irqsave(hba->host->host_lock, flags); + mu_status = hba->mu_status; + } - printk(KERN_INFO DRV_NAME - "(%s): resetting host\n", pci_name(hba->pdev)); - scsi_print_command(cmd); + if (mu_status != MU_STATE_RESETTING) { + spin_unlock_irqrestore(hba->host->host_lock, flags); + return (mu_status == MU_STATE_STARTED) ? 0 : -1; + } hba->mu_status = MU_STATE_RESETTING; + spin_unlock_irqrestore(hba->host->host_lock, flags); + + if (hba->cardtype == st_yosemite) + return stex_yos_reset(hba); if (hba->cardtype == st_shasta) stex_hard_reset(hba); else if (hba->cardtype == st_yel) stex_ss_reset(hba); - if (hba->cardtype != st_yosemite) { - if (stex_handshake(hba)) { - printk(KERN_WARNING DRV_NAME - "(%s): resetting: handshake failed\n", - pci_name(hba->pdev)); - return FAILED; + spin_lock_irqsave(hba->host->host_lock, flags); + for (tag = 0; tag < hba->host->can_queue; tag++) { + ccb = &hba->ccb[tag]; + if (ccb->req == NULL) + continue; + ccb->req = NULL; + if (ccb->cmd) { + scsi_dma_unmap(ccb->cmd); + ccb->cmd->result = DID_RESET << 16; + ccb->cmd->scsi_done(ccb->cmd); + ccb->cmd = NULL; } - return SUCCESS; } + spin_unlock_irqrestore(hba->host->host_lock, flags); - /* st_yosemite */ - writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL); - readl(hba->mmio_base + IDBL); /* flush */ - before = jiffies; - while (hba->out_req_cnt > 0) { - if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) { - printk(KERN_WARNING DRV_NAME - "(%s): reset timeout\n", pci_name(hba->pdev)); - return FAILED; - } - msleep(1); - } + if (stex_handshake(hba) == 0) + return 0; - base = hba->mmio_base; - writel(0, base + IMR0); - readl(base + IMR0); - writel(0, base + OMR0); - readl(base + OMR0); - writel(0, base + IMR1); - readl(base + IMR1); - writel(0, base + OMR1); - readl(base + OMR1); /* flush */ - spin_lock_irqsave(hba->host->host_lock, flags); - hba->req_head = 0; - hba->req_tail = 0; - hba->status_head = 0; - hba->status_tail = 0; - hba->out_req_cnt = 0; - hba->mu_status = MU_STATE_STARTED; - spin_unlock_irqrestore(hba->host->host_lock, flags); - return SUCCESS; + printk(KERN_WARNING DRV_NAME "(%s): resetting: handshake failed\n", + pci_name(hba->pdev)); + return -1; +} + +static int stex_reset(struct scsi_cmnd *cmd) +{ + struct st_hba *hba; + + hba = (struct st_hba *) &cmd->device->host->hostdata[0]; + + printk(KERN_INFO DRV_NAME + "(%s): resetting host\n", pci_name(hba->pdev)); + scsi_print_command(cmd); + + return stex_do_reset(hba) ? FAILED : SUCCESS; +} + +static void stex_reset_work(struct work_struct *work) +{ + struct st_hba *hba = container_of(work, struct st_hba, reset_work); + + stex_do_reset(hba); } static int stex_biosparam(struct scsi_device *sdev, @@ -1420,8 +1488,8 @@ static int stex_set_dma_mask(struct pci_dev * pdev) { int ret; - if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) - && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) + if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) + && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) return 0; ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (!ret) @@ -1528,10 +1596,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) hba->dma_mem = dma_alloc_coherent(&pdev->dev, hba->dma_size, &hba->dma_handle, GFP_KERNEL); if (!hba->dma_mem) { - err = -ENOMEM; - printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n", - pci_name(pdev)); - goto out_iounmap; + /* Retry minimum coherent mapping for st_seq and st_vsc */ + if (hba->cardtype == st_seq || + (hba->cardtype == st_vsc && (pdev->subsystem_device & 1))) { + printk(KERN_WARNING DRV_NAME + "(%s): allocating min buffer for controller\n", + pci_name(pdev)); + hba->dma_size = hba->extra_offset + + ST_ADDITIONAL_MEM_MIN; + hba->dma_mem = dma_alloc_coherent(&pdev->dev, + hba->dma_size, &hba->dma_handle, GFP_KERNEL); + } + + if (!hba->dma_mem) { + err = -ENOMEM; + printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n", + pci_name(pdev)); + goto out_iounmap; + } } hba->ccb = kcalloc(ci->rq_count, sizeof(struct st_ccb), GFP_KERNEL); @@ -1568,12 +1650,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) hba->host = host; hba->pdev = pdev; + init_waitqueue_head(&hba->reset_waitq); + + snprintf(hba->work_q_name, sizeof(hba->work_q_name), + "stex_wq_%d", host->host_no); + hba->work_q = create_singlethread_workqueue(hba->work_q_name); + if (!hba->work_q) { + printk(KERN_ERR DRV_NAME "(%s): create workqueue failed\n", + pci_name(pdev)); + err = -ENOMEM; + goto out_ccb_free; + } + INIT_WORK(&hba->reset_work, stex_reset_work); err = stex_request_irq(hba); if (err) { printk(KERN_ERR DRV_NAME "(%s): request irq failed\n", pci_name(pdev)); - goto out_ccb_free; + goto out_free_wq; } err = stex_handshake(hba); @@ -1602,6 +1696,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_free_irq: stex_free_irq(hba); +out_free_wq: + destroy_workqueue(hba->work_q); out_ccb_free: kfree(hba->ccb); out_pci_free: @@ -1669,6 +1765,8 @@ static void stex_hba_free(struct st_hba *hba) { stex_free_irq(hba); + destroy_workqueue(hba->work_q); + iounmap(hba->mmio_base); pci_release_regions(hba->pdev); diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index 45374d66d26a..2b38f6ad6e11 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -1864,7 +1864,7 @@ static pci_ers_result_t sym2_io_slot_dump(struct pci_dev *pdev) * * This routine is similar to sym_set_workarounds(), except * that, at this point, we already know that the device was - * succesfully intialized at least once before, and so most + * successfully intialized at least once before, and so most * of the steps taken there are un-needed here. */ static void sym2_reset_workarounds(struct pci_dev *pdev) diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index 297deb817a5d..a7bc8b7b09ac 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c @@ -2692,7 +2692,7 @@ static void sym_int_ma (struct sym_hcb *np) * we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids * bloat for such a should_not_happen situation). * In all other situation, we reset the BUS. - * Are these assumptions reasonnable ? (Wait and see ...) + * Are these assumptions reasonable ? (Wait and see ...) */ unexpected_phase: dsp -= 8; diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.h b/drivers/scsi/sym53c8xx_2/sym_hipd.h index 053e63c86822..5a80cbac3f92 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.h +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.h @@ -54,7 +54,7 @@ * * SYM_OPT_LIMIT_COMMAND_REORDERING * When this option is set, the driver tries to limit tagged - * command reordering to some reasonnable value. + * command reordering to some reasonable value. * (set for Linux) */ #if 0 diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c new file mode 100644 index 000000000000..d2604c813a20 --- /dev/null +++ b/drivers/scsi/vmw_pvscsi.c @@ -0,0 +1,1407 @@ +/* + * Linux driver for VMware's para-virtualized SCSI HBA. + * + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained by: Alok N Kataria <akataria@vmware.com> + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/pci.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> + +#include "vmw_pvscsi.h" + +#define PVSCSI_LINUX_DRIVER_DESC "VMware PVSCSI driver" + +MODULE_DESCRIPTION(PVSCSI_LINUX_DRIVER_DESC); +MODULE_AUTHOR("VMware, Inc."); +MODULE_LICENSE("GPL"); +MODULE_VERSION(PVSCSI_DRIVER_VERSION_STRING); + +#define PVSCSI_DEFAULT_NUM_PAGES_PER_RING 8 +#define PVSCSI_DEFAULT_NUM_PAGES_MSG_RING 1 +#define PVSCSI_DEFAULT_QUEUE_DEPTH 64 +#define SGL_SIZE PAGE_SIZE + +struct pvscsi_sg_list { + struct PVSCSISGElement sge[PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT]; +}; + +struct pvscsi_ctx { + /* + * The index of the context in cmd_map serves as the context ID for a + * 1-to-1 mapping completions back to requests. + */ + struct scsi_cmnd *cmd; + struct pvscsi_sg_list *sgl; + struct list_head list; + dma_addr_t dataPA; + dma_addr_t sensePA; + dma_addr_t sglPA; +}; + +struct pvscsi_adapter { + char *mmioBase; + unsigned int irq; + u8 rev; + bool use_msi; + bool use_msix; + bool use_msg; + + spinlock_t hw_lock; + + struct workqueue_struct *workqueue; + struct work_struct work; + + struct PVSCSIRingReqDesc *req_ring; + unsigned req_pages; + unsigned req_depth; + dma_addr_t reqRingPA; + + struct PVSCSIRingCmpDesc *cmp_ring; + unsigned cmp_pages; + dma_addr_t cmpRingPA; + + struct PVSCSIRingMsgDesc *msg_ring; + unsigned msg_pages; + dma_addr_t msgRingPA; + + struct PVSCSIRingsState *rings_state; + dma_addr_t ringStatePA; + + struct pci_dev *dev; + struct Scsi_Host *host; + + struct list_head cmd_pool; + struct pvscsi_ctx *cmd_map; +}; + + +/* Command line parameters */ +static int pvscsi_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_PER_RING; +static int pvscsi_msg_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_MSG_RING; +static int pvscsi_cmd_per_lun = PVSCSI_DEFAULT_QUEUE_DEPTH; +static bool pvscsi_disable_msi; +static bool pvscsi_disable_msix; +static bool pvscsi_use_msg = true; + +#define PVSCSI_RW (S_IRUSR | S_IWUSR) + +module_param_named(ring_pages, pvscsi_ring_pages, int, PVSCSI_RW); +MODULE_PARM_DESC(ring_pages, "Number of pages per req/cmp ring - (default=" + __stringify(PVSCSI_DEFAULT_NUM_PAGES_PER_RING) ")"); + +module_param_named(msg_ring_pages, pvscsi_msg_ring_pages, int, PVSCSI_RW); +MODULE_PARM_DESC(msg_ring_pages, "Number of pages for the msg ring - (default=" + __stringify(PVSCSI_DEFAULT_NUM_PAGES_MSG_RING) ")"); + +module_param_named(cmd_per_lun, pvscsi_cmd_per_lun, int, PVSCSI_RW); +MODULE_PARM_DESC(cmd_per_lun, "Maximum commands per lun - (default=" + __stringify(PVSCSI_MAX_REQ_QUEUE_DEPTH) ")"); + +module_param_named(disable_msi, pvscsi_disable_msi, bool, PVSCSI_RW); +MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)"); + +module_param_named(disable_msix, pvscsi_disable_msix, bool, PVSCSI_RW); +MODULE_PARM_DESC(disable_msix, "Disable MSI-X use in driver - (default=0)"); + +module_param_named(use_msg, pvscsi_use_msg, bool, PVSCSI_RW); +MODULE_PARM_DESC(use_msg, "Use msg ring when available - (default=1)"); + +static const struct pci_device_id pvscsi_pci_tbl[] = { + { PCI_VDEVICE(VMWARE, PCI_DEVICE_ID_VMWARE_PVSCSI) }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, pvscsi_pci_tbl); + +static struct device * +pvscsi_dev(const struct pvscsi_adapter *adapter) +{ + return &(adapter->dev->dev); +} + +static struct pvscsi_ctx * +pvscsi_find_context(const struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd) +{ + struct pvscsi_ctx *ctx, *end; + + end = &adapter->cmd_map[adapter->req_depth]; + for (ctx = adapter->cmd_map; ctx < end; ctx++) + if (ctx->cmd == cmd) + return ctx; + + return NULL; +} + +static struct pvscsi_ctx * +pvscsi_acquire_context(struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd) +{ + struct pvscsi_ctx *ctx; + + if (list_empty(&adapter->cmd_pool)) + return NULL; + + ctx = list_first_entry(&adapter->cmd_pool, struct pvscsi_ctx, list); + ctx->cmd = cmd; + list_del(&ctx->list); + + return ctx; +} + +static void pvscsi_release_context(struct pvscsi_adapter *adapter, + struct pvscsi_ctx *ctx) +{ + ctx->cmd = NULL; + list_add(&ctx->list, &adapter->cmd_pool); +} + +/* + * Map a pvscsi_ctx struct to a context ID field value; we map to a simple + * non-zero integer. ctx always points to an entry in cmd_map array, hence + * the return value is always >=1. + */ +static u64 pvscsi_map_context(const struct pvscsi_adapter *adapter, + const struct pvscsi_ctx *ctx) +{ + return ctx - adapter->cmd_map + 1; +} + +static struct pvscsi_ctx * +pvscsi_get_context(const struct pvscsi_adapter *adapter, u64 context) +{ + return &adapter->cmd_map[context - 1]; +} + +static void pvscsi_reg_write(const struct pvscsi_adapter *adapter, + u32 offset, u32 val) +{ + writel(val, adapter->mmioBase + offset); +} + +static u32 pvscsi_reg_read(const struct pvscsi_adapter *adapter, u32 offset) +{ + return readl(adapter->mmioBase + offset); +} + +static u32 pvscsi_read_intr_status(const struct pvscsi_adapter *adapter) +{ + return pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_INTR_STATUS); +} + +static void pvscsi_write_intr_status(const struct pvscsi_adapter *adapter, + u32 val) +{ + pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_STATUS, val); +} + +static void pvscsi_unmask_intr(const struct pvscsi_adapter *adapter) +{ + u32 intr_bits; + + intr_bits = PVSCSI_INTR_CMPL_MASK; + if (adapter->use_msg) + intr_bits |= PVSCSI_INTR_MSG_MASK; + + pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, intr_bits); +} + +static void pvscsi_mask_intr(const struct pvscsi_adapter *adapter) +{ + pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, 0); +} + +static void pvscsi_write_cmd_desc(const struct pvscsi_adapter *adapter, + u32 cmd, const void *desc, size_t len) +{ + const u32 *ptr = desc; + size_t i; + + len /= sizeof(*ptr); + pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND, cmd); + for (i = 0; i < len; i++) + pvscsi_reg_write(adapter, + PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]); +} + +static void pvscsi_abort_cmd(const struct pvscsi_adapter *adapter, + const struct pvscsi_ctx *ctx) +{ + struct PVSCSICmdDescAbortCmd cmd = { 0 }; + + cmd.target = ctx->cmd->device->id; + cmd.context = pvscsi_map_context(adapter, ctx); + + pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ABORT_CMD, &cmd, sizeof(cmd)); +} + +static void pvscsi_kick_rw_io(const struct pvscsi_adapter *adapter) +{ + pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_RW_IO, 0); +} + +static void pvscsi_process_request_ring(const struct pvscsi_adapter *adapter) +{ + pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_NON_RW_IO, 0); +} + +static int scsi_is_rw(unsigned char op) +{ + return op == READ_6 || op == WRITE_6 || + op == READ_10 || op == WRITE_10 || + op == READ_12 || op == WRITE_12 || + op == READ_16 || op == WRITE_16; +} + +static void pvscsi_kick_io(const struct pvscsi_adapter *adapter, + unsigned char op) +{ + if (scsi_is_rw(op)) + pvscsi_kick_rw_io(adapter); + else + pvscsi_process_request_ring(adapter); +} + +static void ll_adapter_reset(const struct pvscsi_adapter *adapter) +{ + dev_dbg(pvscsi_dev(adapter), "Adapter Reset on %p\n", adapter); + + pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ADAPTER_RESET, NULL, 0); +} + +static void ll_bus_reset(const struct pvscsi_adapter *adapter) +{ + dev_dbg(pvscsi_dev(adapter), "Reseting bus on %p\n", adapter); + + pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_BUS, NULL, 0); +} + +static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target) +{ + struct PVSCSICmdDescResetDevice cmd = { 0 }; + + dev_dbg(pvscsi_dev(adapter), "Reseting device: target=%u\n", target); + + cmd.target = target; + + pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_DEVICE, + &cmd, sizeof(cmd)); +} + +static void pvscsi_create_sg(struct pvscsi_ctx *ctx, + struct scatterlist *sg, unsigned count) +{ + unsigned i; + struct PVSCSISGElement *sge; + + BUG_ON(count > PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT); + + sge = &ctx->sgl->sge[0]; + for (i = 0; i < count; i++, sg++) { + sge[i].addr = sg_dma_address(sg); + sge[i].length = sg_dma_len(sg); + sge[i].flags = 0; + } +} + +/* + * Map all data buffers for a command into PCI space and + * setup the scatter/gather list if needed. + */ +static void pvscsi_map_buffers(struct pvscsi_adapter *adapter, + struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd, + struct PVSCSIRingReqDesc *e) +{ + unsigned count; + unsigned bufflen = scsi_bufflen(cmd); + struct scatterlist *sg; + + e->dataLen = bufflen; + e->dataAddr = 0; + if (bufflen == 0) + return; + + sg = scsi_sglist(cmd); + count = scsi_sg_count(cmd); + if (count != 0) { + int segs = scsi_dma_map(cmd); + if (segs > 1) { + pvscsi_create_sg(ctx, sg, segs); + + e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST; + ctx->sglPA = pci_map_single(adapter->dev, ctx->sgl, + SGL_SIZE, PCI_DMA_TODEVICE); + e->dataAddr = ctx->sglPA; + } else + e->dataAddr = sg_dma_address(sg); + } else { + /* + * In case there is no S/G list, scsi_sglist points + * directly to the buffer. + */ + ctx->dataPA = pci_map_single(adapter->dev, sg, bufflen, + cmd->sc_data_direction); + e->dataAddr = ctx->dataPA; + } +} + +static void pvscsi_unmap_buffers(const struct pvscsi_adapter *adapter, + struct pvscsi_ctx *ctx) +{ + struct scsi_cmnd *cmd; + unsigned bufflen; + + cmd = ctx->cmd; + bufflen = scsi_bufflen(cmd); + + if (bufflen != 0) { + unsigned count = scsi_sg_count(cmd); + + if (count != 0) { + scsi_dma_unmap(cmd); + if (ctx->sglPA) { + pci_unmap_single(adapter->dev, ctx->sglPA, + SGL_SIZE, PCI_DMA_TODEVICE); + ctx->sglPA = 0; + } + } else + pci_unmap_single(adapter->dev, ctx->dataPA, bufflen, + cmd->sc_data_direction); + } + if (cmd->sense_buffer) + pci_unmap_single(adapter->dev, ctx->sensePA, + SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE); +} + +static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter) +{ + adapter->rings_state = pci_alloc_consistent(adapter->dev, PAGE_SIZE, + &adapter->ringStatePA); + if (!adapter->rings_state) + return -ENOMEM; + + adapter->req_pages = min(PVSCSI_MAX_NUM_PAGES_REQ_RING, + pvscsi_ring_pages); + adapter->req_depth = adapter->req_pages + * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; + adapter->req_ring = pci_alloc_consistent(adapter->dev, + adapter->req_pages * PAGE_SIZE, + &adapter->reqRingPA); + if (!adapter->req_ring) + return -ENOMEM; + + adapter->cmp_pages = min(PVSCSI_MAX_NUM_PAGES_CMP_RING, + pvscsi_ring_pages); + adapter->cmp_ring = pci_alloc_consistent(adapter->dev, + adapter->cmp_pages * PAGE_SIZE, + &adapter->cmpRingPA); + if (!adapter->cmp_ring) + return -ENOMEM; + + BUG_ON(!IS_ALIGNED(adapter->ringStatePA, PAGE_SIZE)); + BUG_ON(!IS_ALIGNED(adapter->reqRingPA, PAGE_SIZE)); + BUG_ON(!IS_ALIGNED(adapter->cmpRingPA, PAGE_SIZE)); + + if (!adapter->use_msg) + return 0; + + adapter->msg_pages = min(PVSCSI_MAX_NUM_PAGES_MSG_RING, + pvscsi_msg_ring_pages); + adapter->msg_ring = pci_alloc_consistent(adapter->dev, + adapter->msg_pages * PAGE_SIZE, + &adapter->msgRingPA); + if (!adapter->msg_ring) + return -ENOMEM; + BUG_ON(!IS_ALIGNED(adapter->msgRingPA, PAGE_SIZE)); + + return 0; +} + +static void pvscsi_setup_all_rings(const struct pvscsi_adapter *adapter) +{ + struct PVSCSICmdDescSetupRings cmd = { 0 }; + dma_addr_t base; + unsigned i; + + cmd.ringsStatePPN = adapter->ringStatePA >> PAGE_SHIFT; + cmd.reqRingNumPages = adapter->req_pages; + cmd.cmpRingNumPages = adapter->cmp_pages; + + base = adapter->reqRingPA; + for (i = 0; i < adapter->req_pages; i++) { + cmd.reqRingPPNs[i] = base >> PAGE_SHIFT; + base += PAGE_SIZE; + } + + base = adapter->cmpRingPA; + for (i = 0; i < adapter->cmp_pages; i++) { + cmd.cmpRingPPNs[i] = base >> PAGE_SHIFT; + base += PAGE_SIZE; + } + + memset(adapter->rings_state, 0, PAGE_SIZE); + memset(adapter->req_ring, 0, adapter->req_pages * PAGE_SIZE); + memset(adapter->cmp_ring, 0, adapter->cmp_pages * PAGE_SIZE); + + pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_RINGS, + &cmd, sizeof(cmd)); + + if (adapter->use_msg) { + struct PVSCSICmdDescSetupMsgRing cmd_msg = { 0 }; + + cmd_msg.numPages = adapter->msg_pages; + + base = adapter->msgRingPA; + for (i = 0; i < adapter->msg_pages; i++) { + cmd_msg.ringPPNs[i] = base >> PAGE_SHIFT; + base += PAGE_SIZE; + } + memset(adapter->msg_ring, 0, adapter->msg_pages * PAGE_SIZE); + + pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_MSG_RING, + &cmd_msg, sizeof(cmd_msg)); + } +} + +/* + * Pull a completion descriptor off and pass the completion back + * to the SCSI mid layer. + */ +static void pvscsi_complete_request(struct pvscsi_adapter *adapter, + const struct PVSCSIRingCmpDesc *e) +{ + struct pvscsi_ctx *ctx; + struct scsi_cmnd *cmd; + u32 btstat = e->hostStatus; + u32 sdstat = e->scsiStatus; + + ctx = pvscsi_get_context(adapter, e->context); + cmd = ctx->cmd; + pvscsi_unmap_buffers(adapter, ctx); + pvscsi_release_context(adapter, ctx); + cmd->result = 0; + + if (sdstat != SAM_STAT_GOOD && + (btstat == BTSTAT_SUCCESS || + btstat == BTSTAT_LINKED_COMMAND_COMPLETED || + btstat == BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG)) { + cmd->result = (DID_OK << 16) | sdstat; + if (sdstat == SAM_STAT_CHECK_CONDITION && cmd->sense_buffer) + cmd->result |= (DRIVER_SENSE << 24); + } else + switch (btstat) { + case BTSTAT_SUCCESS: + case BTSTAT_LINKED_COMMAND_COMPLETED: + case BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG: + /* If everything went fine, let's move on.. */ + cmd->result = (DID_OK << 16); + break; + + case BTSTAT_DATARUN: + case BTSTAT_DATA_UNDERRUN: + /* Report residual data in underruns */ + scsi_set_resid(cmd, scsi_bufflen(cmd) - e->dataLen); + cmd->result = (DID_ERROR << 16); + break; + + case BTSTAT_SELTIMEO: + /* Our emulation returns this for non-connected devs */ + cmd->result = (DID_BAD_TARGET << 16); + break; + + case BTSTAT_LUNMISMATCH: + case BTSTAT_TAGREJECT: + case BTSTAT_BADMSG: + cmd->result = (DRIVER_INVALID << 24); + /* fall through */ + + case BTSTAT_HAHARDWARE: + case BTSTAT_INVPHASE: + case BTSTAT_HATIMEOUT: + case BTSTAT_NORESPONSE: + case BTSTAT_DISCONNECT: + case BTSTAT_HASOFTWARE: + case BTSTAT_BUSFREE: + case BTSTAT_SENSFAILED: + cmd->result |= (DID_ERROR << 16); + break; + + case BTSTAT_SENTRST: + case BTSTAT_RECVRST: + case BTSTAT_BUSRESET: + cmd->result = (DID_RESET << 16); + break; + + case BTSTAT_ABORTQUEUE: + cmd->result = (DID_ABORT << 16); + break; + + case BTSTAT_SCSIPARITY: + cmd->result = (DID_PARITY << 16); + break; + + default: + cmd->result = (DID_ERROR << 16); + scmd_printk(KERN_DEBUG, cmd, + "Unknown completion status: 0x%x\n", + btstat); + } + + dev_dbg(&cmd->device->sdev_gendev, + "cmd=%p %x ctx=%p result=0x%x status=0x%x,%x\n", + cmd, cmd->cmnd[0], ctx, cmd->result, btstat, sdstat); + + cmd->scsi_done(cmd); +} + +/* + * barrier usage : Since the PVSCSI device is emulated, there could be cases + * where we may want to serialize some accesses between the driver and the + * emulation layer. We use compiler barriers instead of the more expensive + * memory barriers because PVSCSI is only supported on X86 which has strong + * memory access ordering. + */ +static void pvscsi_process_completion_ring(struct pvscsi_adapter *adapter) +{ + struct PVSCSIRingsState *s = adapter->rings_state; + struct PVSCSIRingCmpDesc *ring = adapter->cmp_ring; + u32 cmp_entries = s->cmpNumEntriesLog2; + + while (s->cmpConsIdx != s->cmpProdIdx) { + struct PVSCSIRingCmpDesc *e = ring + (s->cmpConsIdx & + MASK(cmp_entries)); + /* + * This barrier() ensures that *e is not dereferenced while + * the device emulation still writes data into the slot. + * Since the device emulation advances s->cmpProdIdx only after + * updating the slot we want to check it first. + */ + barrier(); + pvscsi_complete_request(adapter, e); + /* + * This barrier() ensures that compiler doesn't reorder write + * to s->cmpConsIdx before the read of (*e) inside + * pvscsi_complete_request. Otherwise, device emulation may + * overwrite *e before we had a chance to read it. + */ + barrier(); + s->cmpConsIdx++; + } +} + +/* + * Translate a Linux SCSI request into a request ring entry. + */ +static int pvscsi_queue_ring(struct pvscsi_adapter *adapter, + struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd) +{ + struct PVSCSIRingsState *s; + struct PVSCSIRingReqDesc *e; + struct scsi_device *sdev; + u32 req_entries; + + s = adapter->rings_state; + sdev = cmd->device; + req_entries = s->reqNumEntriesLog2; + + /* + * If this condition holds, we might have room on the request ring, but + * we might not have room on the completion ring for the response. + * However, we have already ruled out this possibility - we would not + * have successfully allocated a context if it were true, since we only + * have one context per request entry. Check for it anyway, since it + * would be a serious bug. + */ + if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) { + scmd_printk(KERN_ERR, cmd, "vmw_pvscsi: " + "ring full: reqProdIdx=%d cmpConsIdx=%d\n", + s->reqProdIdx, s->cmpConsIdx); + return -1; + } + + e = adapter->req_ring + (s->reqProdIdx & MASK(req_entries)); + + e->bus = sdev->channel; + e->target = sdev->id; + memset(e->lun, 0, sizeof(e->lun)); + e->lun[1] = sdev->lun; + + if (cmd->sense_buffer) { + ctx->sensePA = pci_map_single(adapter->dev, cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + PCI_DMA_FROMDEVICE); + e->senseAddr = ctx->sensePA; + e->senseLen = SCSI_SENSE_BUFFERSIZE; + } else { + e->senseLen = 0; + e->senseAddr = 0; + } + e->cdbLen = cmd->cmd_len; + e->vcpuHint = smp_processor_id(); + memcpy(e->cdb, cmd->cmnd, e->cdbLen); + + e->tag = SIMPLE_QUEUE_TAG; + if (sdev->tagged_supported && + (cmd->tag == HEAD_OF_QUEUE_TAG || + cmd->tag == ORDERED_QUEUE_TAG)) + e->tag = cmd->tag; + + if (cmd->sc_data_direction == DMA_FROM_DEVICE) + e->flags = PVSCSI_FLAG_CMD_DIR_TOHOST; + else if (cmd->sc_data_direction == DMA_TO_DEVICE) + e->flags = PVSCSI_FLAG_CMD_DIR_TODEVICE; + else if (cmd->sc_data_direction == DMA_NONE) + e->flags = PVSCSI_FLAG_CMD_DIR_NONE; + else + e->flags = 0; + + pvscsi_map_buffers(adapter, ctx, cmd, e); + + e->context = pvscsi_map_context(adapter, ctx); + + barrier(); + + s->reqProdIdx++; + + return 0; +} + +static int pvscsi_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + struct Scsi_Host *host = cmd->device->host; + struct pvscsi_adapter *adapter = shost_priv(host); + struct pvscsi_ctx *ctx; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + + ctx = pvscsi_acquire_context(adapter, cmd); + if (!ctx || pvscsi_queue_ring(adapter, ctx, cmd) != 0) { + if (ctx) + pvscsi_release_context(adapter, ctx); + spin_unlock_irqrestore(&adapter->hw_lock, flags); + return SCSI_MLQUEUE_HOST_BUSY; + } + + cmd->scsi_done = done; + + dev_dbg(&cmd->device->sdev_gendev, + "queued cmd %p, ctx %p, op=%x\n", cmd, ctx, cmd->cmnd[0]); + + spin_unlock_irqrestore(&adapter->hw_lock, flags); + + pvscsi_kick_io(adapter, cmd->cmnd[0]); + + return 0; +} + +static int pvscsi_abort(struct scsi_cmnd *cmd) +{ + struct pvscsi_adapter *adapter = shost_priv(cmd->device->host); + struct pvscsi_ctx *ctx; + unsigned long flags; + + scmd_printk(KERN_DEBUG, cmd, "task abort on host %u, %p\n", + adapter->host->host_no, cmd); + + spin_lock_irqsave(&adapter->hw_lock, flags); + + /* + * Poll the completion ring first - we might be trying to abort + * a command that is waiting to be dispatched in the completion ring. + */ + pvscsi_process_completion_ring(adapter); + + /* + * If there is no context for the command, it either already succeeded + * or else was never properly issued. Not our problem. + */ + ctx = pvscsi_find_context(adapter, cmd); + if (!ctx) { + scmd_printk(KERN_DEBUG, cmd, "Failed to abort cmd %p\n", cmd); + goto out; + } + + pvscsi_abort_cmd(adapter, ctx); + + pvscsi_process_completion_ring(adapter); + +out: + spin_unlock_irqrestore(&adapter->hw_lock, flags); + return SUCCESS; +} + +/* + * Abort all outstanding requests. This is only safe to use if the completion + * ring will never be walked again or the device has been reset, because it + * destroys the 1-1 mapping between context field passed to emulation and our + * request structure. + */ +static void pvscsi_reset_all(struct pvscsi_adapter *adapter) +{ + unsigned i; + + for (i = 0; i < adapter->req_depth; i++) { + struct pvscsi_ctx *ctx = &adapter->cmd_map[i]; + struct scsi_cmnd *cmd = ctx->cmd; + if (cmd) { + scmd_printk(KERN_ERR, cmd, + "Forced reset on cmd %p\n", cmd); + pvscsi_unmap_buffers(adapter, ctx); + pvscsi_release_context(adapter, ctx); + cmd->result = (DID_RESET << 16); + cmd->scsi_done(cmd); + } + } +} + +static int pvscsi_host_reset(struct scsi_cmnd *cmd) +{ + struct Scsi_Host *host = cmd->device->host; + struct pvscsi_adapter *adapter = shost_priv(host); + unsigned long flags; + bool use_msg; + + scmd_printk(KERN_INFO, cmd, "SCSI Host reset\n"); + + spin_lock_irqsave(&adapter->hw_lock, flags); + + use_msg = adapter->use_msg; + + if (use_msg) { + adapter->use_msg = 0; + spin_unlock_irqrestore(&adapter->hw_lock, flags); + + /* + * Now that we know that the ISR won't add more work on the + * workqueue we can safely flush any outstanding work. + */ + flush_workqueue(adapter->workqueue); + spin_lock_irqsave(&adapter->hw_lock, flags); + } + + /* + * We're going to tear down the entire ring structure and set it back + * up, so stalling new requests until all completions are flushed and + * the rings are back in place. + */ + + pvscsi_process_request_ring(adapter); + + ll_adapter_reset(adapter); + + /* + * Now process any completions. Note we do this AFTER adapter reset, + * which is strange, but stops races where completions get posted + * between processing the ring and issuing the reset. The backend will + * not touch the ring memory after reset, so the immediately pre-reset + * completion ring state is still valid. + */ + pvscsi_process_completion_ring(adapter); + + pvscsi_reset_all(adapter); + adapter->use_msg = use_msg; + pvscsi_setup_all_rings(adapter); + pvscsi_unmask_intr(adapter); + + spin_unlock_irqrestore(&adapter->hw_lock, flags); + + return SUCCESS; +} + +static int pvscsi_bus_reset(struct scsi_cmnd *cmd) +{ + struct Scsi_Host *host = cmd->device->host; + struct pvscsi_adapter *adapter = shost_priv(host); + unsigned long flags; + + scmd_printk(KERN_INFO, cmd, "SCSI Bus reset\n"); + + /* + * We don't want to queue new requests for this bus after + * flushing all pending requests to emulation, since new + * requests could then sneak in during this bus reset phase, + * so take the lock now. + */ + spin_lock_irqsave(&adapter->hw_lock, flags); + + pvscsi_process_request_ring(adapter); + ll_bus_reset(adapter); + pvscsi_process_completion_ring(adapter); + + spin_unlock_irqrestore(&adapter->hw_lock, flags); + + return SUCCESS; +} + +static int pvscsi_device_reset(struct scsi_cmnd *cmd) +{ + struct Scsi_Host *host = cmd->device->host; + struct pvscsi_adapter *adapter = shost_priv(host); + unsigned long flags; + + scmd_printk(KERN_INFO, cmd, "SCSI device reset on scsi%u:%u\n", + host->host_no, cmd->device->id); + + /* + * We don't want to queue new requests for this device after flushing + * all pending requests to emulation, since new requests could then + * sneak in during this device reset phase, so take the lock now. + */ + spin_lock_irqsave(&adapter->hw_lock, flags); + + pvscsi_process_request_ring(adapter); + ll_device_reset(adapter, cmd->device->id); + pvscsi_process_completion_ring(adapter); + + spin_unlock_irqrestore(&adapter->hw_lock, flags); + + return SUCCESS; +} + +static struct scsi_host_template pvscsi_template; + +static const char *pvscsi_info(struct Scsi_Host *host) +{ + struct pvscsi_adapter *adapter = shost_priv(host); + static char buf[256]; + + sprintf(buf, "VMware PVSCSI storage adapter rev %d, req/cmp/msg rings: " + "%u/%u/%u pages, cmd_per_lun=%u", adapter->rev, + adapter->req_pages, adapter->cmp_pages, adapter->msg_pages, + pvscsi_template.cmd_per_lun); + + return buf; +} + +static struct scsi_host_template pvscsi_template = { + .module = THIS_MODULE, + .name = "VMware PVSCSI Host Adapter", + .proc_name = "vmw_pvscsi", + .info = pvscsi_info, + .queuecommand = pvscsi_queue, + .this_id = -1, + .sg_tablesize = PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT, + .dma_boundary = UINT_MAX, + .max_sectors = 0xffff, + .use_clustering = ENABLE_CLUSTERING, + .eh_abort_handler = pvscsi_abort, + .eh_device_reset_handler = pvscsi_device_reset, + .eh_bus_reset_handler = pvscsi_bus_reset, + .eh_host_reset_handler = pvscsi_host_reset, +}; + +static void pvscsi_process_msg(const struct pvscsi_adapter *adapter, + const struct PVSCSIRingMsgDesc *e) +{ + struct PVSCSIRingsState *s = adapter->rings_state; + struct Scsi_Host *host = adapter->host; + struct scsi_device *sdev; + + printk(KERN_INFO "vmw_pvscsi: msg type: 0x%x - MSG RING: %u/%u (%u) \n", + e->type, s->msgProdIdx, s->msgConsIdx, s->msgNumEntriesLog2); + + BUILD_BUG_ON(PVSCSI_MSG_LAST != 2); + + if (e->type == PVSCSI_MSG_DEV_ADDED) { + struct PVSCSIMsgDescDevStatusChanged *desc; + desc = (struct PVSCSIMsgDescDevStatusChanged *)e; + + printk(KERN_INFO + "vmw_pvscsi: msg: device added at scsi%u:%u:%u\n", + desc->bus, desc->target, desc->lun[1]); + + if (!scsi_host_get(host)) + return; + + sdev = scsi_device_lookup(host, desc->bus, desc->target, + desc->lun[1]); + if (sdev) { + printk(KERN_INFO "vmw_pvscsi: device already exists\n"); + scsi_device_put(sdev); + } else + scsi_add_device(adapter->host, desc->bus, + desc->target, desc->lun[1]); + + scsi_host_put(host); + } else if (e->type == PVSCSI_MSG_DEV_REMOVED) { + struct PVSCSIMsgDescDevStatusChanged *desc; + desc = (struct PVSCSIMsgDescDevStatusChanged *)e; + + printk(KERN_INFO + "vmw_pvscsi: msg: device removed at scsi%u:%u:%u\n", + desc->bus, desc->target, desc->lun[1]); + + if (!scsi_host_get(host)) + return; + + sdev = scsi_device_lookup(host, desc->bus, desc->target, + desc->lun[1]); + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + } else + printk(KERN_INFO + "vmw_pvscsi: failed to lookup scsi%u:%u:%u\n", + desc->bus, desc->target, desc->lun[1]); + + scsi_host_put(host); + } +} + +static int pvscsi_msg_pending(const struct pvscsi_adapter *adapter) +{ + struct PVSCSIRingsState *s = adapter->rings_state; + + return s->msgProdIdx != s->msgConsIdx; +} + +static void pvscsi_process_msg_ring(const struct pvscsi_adapter *adapter) +{ + struct PVSCSIRingsState *s = adapter->rings_state; + struct PVSCSIRingMsgDesc *ring = adapter->msg_ring; + u32 msg_entries = s->msgNumEntriesLog2; + + while (pvscsi_msg_pending(adapter)) { + struct PVSCSIRingMsgDesc *e = ring + (s->msgConsIdx & + MASK(msg_entries)); + + barrier(); + pvscsi_process_msg(adapter, e); + barrier(); + s->msgConsIdx++; + } +} + +static void pvscsi_msg_workqueue_handler(struct work_struct *data) +{ + struct pvscsi_adapter *adapter; + + adapter = container_of(data, struct pvscsi_adapter, work); + + pvscsi_process_msg_ring(adapter); +} + +static int pvscsi_setup_msg_workqueue(struct pvscsi_adapter *adapter) +{ + char name[32]; + + if (!pvscsi_use_msg) + return 0; + + pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND, + PVSCSI_CMD_SETUP_MSG_RING); + + if (pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_COMMAND_STATUS) == -1) + return 0; + + snprintf(name, sizeof(name), + "vmw_pvscsi_wq_%u", adapter->host->host_no); + + adapter->workqueue = create_singlethread_workqueue(name); + if (!adapter->workqueue) { + printk(KERN_ERR "vmw_pvscsi: failed to create work queue\n"); + return 0; + } + INIT_WORK(&adapter->work, pvscsi_msg_workqueue_handler); + + return 1; +} + +static irqreturn_t pvscsi_isr(int irq, void *devp) +{ + struct pvscsi_adapter *adapter = devp; + int handled; + + if (adapter->use_msi || adapter->use_msix) + handled = true; + else { + u32 val = pvscsi_read_intr_status(adapter); + handled = (val & PVSCSI_INTR_ALL_SUPPORTED) != 0; + if (handled) + pvscsi_write_intr_status(devp, val); + } + + if (handled) { + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + + pvscsi_process_completion_ring(adapter); + if (adapter->use_msg && pvscsi_msg_pending(adapter)) + queue_work(adapter->workqueue, &adapter->work); + + spin_unlock_irqrestore(&adapter->hw_lock, flags); + } + + return IRQ_RETVAL(handled); +} + +static void pvscsi_free_sgls(const struct pvscsi_adapter *adapter) +{ + struct pvscsi_ctx *ctx = adapter->cmd_map; + unsigned i; + + for (i = 0; i < adapter->req_depth; ++i, ++ctx) + free_pages((unsigned long)ctx->sgl, get_order(SGL_SIZE)); +} + +static int pvscsi_setup_msix(const struct pvscsi_adapter *adapter, int *irq) +{ + struct msix_entry entry = { 0, PVSCSI_VECTOR_COMPLETION }; + int ret; + + ret = pci_enable_msix(adapter->dev, &entry, 1); + if (ret) + return ret; + + *irq = entry.vector; + + return 0; +} + +static void pvscsi_shutdown_intr(struct pvscsi_adapter *adapter) +{ + if (adapter->irq) { + free_irq(adapter->irq, adapter); + adapter->irq = 0; + } + if (adapter->use_msi) { + pci_disable_msi(adapter->dev); + adapter->use_msi = 0; + } else if (adapter->use_msix) { + pci_disable_msix(adapter->dev); + adapter->use_msix = 0; + } +} + +static void pvscsi_release_resources(struct pvscsi_adapter *adapter) +{ + pvscsi_shutdown_intr(adapter); + + if (adapter->workqueue) + destroy_workqueue(adapter->workqueue); + + if (adapter->mmioBase) + pci_iounmap(adapter->dev, adapter->mmioBase); + + pci_release_regions(adapter->dev); + + if (adapter->cmd_map) { + pvscsi_free_sgls(adapter); + kfree(adapter->cmd_map); + } + + if (adapter->rings_state) + pci_free_consistent(adapter->dev, PAGE_SIZE, + adapter->rings_state, adapter->ringStatePA); + + if (adapter->req_ring) + pci_free_consistent(adapter->dev, + adapter->req_pages * PAGE_SIZE, + adapter->req_ring, adapter->reqRingPA); + + if (adapter->cmp_ring) + pci_free_consistent(adapter->dev, + adapter->cmp_pages * PAGE_SIZE, + adapter->cmp_ring, adapter->cmpRingPA); + + if (adapter->msg_ring) + pci_free_consistent(adapter->dev, + adapter->msg_pages * PAGE_SIZE, + adapter->msg_ring, adapter->msgRingPA); +} + +/* + * Allocate scatter gather lists. + * + * These are statically allocated. Trying to be clever was not worth it. + * + * Dynamic allocation can fail, and we can't go deeep into the memory + * allocator, since we're a SCSI driver, and trying too hard to allocate + * memory might generate disk I/O. We also don't want to fail disk I/O + * in that case because we can't get an allocation - the I/O could be + * trying to swap out data to free memory. Since that is pathological, + * just use a statically allocated scatter list. + * + */ +static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter) +{ + struct pvscsi_ctx *ctx; + int i; + + ctx = adapter->cmd_map; + BUILD_BUG_ON(sizeof(struct pvscsi_sg_list) > SGL_SIZE); + + for (i = 0; i < adapter->req_depth; ++i, ++ctx) { + ctx->sgl = (void *)__get_free_pages(GFP_KERNEL, + get_order(SGL_SIZE)); + ctx->sglPA = 0; + BUG_ON(!IS_ALIGNED(((unsigned long)ctx->sgl), PAGE_SIZE)); + if (!ctx->sgl) { + for (; i >= 0; --i, --ctx) { + free_pages((unsigned long)ctx->sgl, + get_order(SGL_SIZE)); + ctx->sgl = NULL; + } + return -ENOMEM; + } + } + + return 0; +} + +static int __devinit pvscsi_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pvscsi_adapter *adapter; + struct Scsi_Host *host; + unsigned int i; + unsigned long flags = 0; + int error; + + error = -ENODEV; + + if (pci_enable_device(pdev)) + return error; + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0 && + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) { + printk(KERN_INFO "vmw_pvscsi: using 64bit dma\n"); + } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) == 0 && + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) == 0) { + printk(KERN_INFO "vmw_pvscsi: using 32bit dma\n"); + } else { + printk(KERN_ERR "vmw_pvscsi: failed to set DMA mask\n"); + goto out_disable_device; + } + + pvscsi_template.can_queue = + min(PVSCSI_MAX_NUM_PAGES_REQ_RING, pvscsi_ring_pages) * + PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; + pvscsi_template.cmd_per_lun = + min(pvscsi_template.can_queue, pvscsi_cmd_per_lun); + host = scsi_host_alloc(&pvscsi_template, sizeof(struct pvscsi_adapter)); + if (!host) { + printk(KERN_ERR "vmw_pvscsi: failed to allocate host\n"); + goto out_disable_device; + } + + adapter = shost_priv(host); + memset(adapter, 0, sizeof(*adapter)); + adapter->dev = pdev; + adapter->host = host; + + spin_lock_init(&adapter->hw_lock); + + host->max_channel = 0; + host->max_id = 16; + host->max_lun = 1; + host->max_cmd_len = 16; + + adapter->rev = pdev->revision; + + if (pci_request_regions(pdev, "vmw_pvscsi")) { + printk(KERN_ERR "vmw_pvscsi: pci memory selection failed\n"); + goto out_free_host; + } + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + if ((pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO)) + continue; + + if (pci_resource_len(pdev, i) < PVSCSI_MEM_SPACE_SIZE) + continue; + + break; + } + + if (i == DEVICE_COUNT_RESOURCE) { + printk(KERN_ERR + "vmw_pvscsi: adapter has no suitable MMIO region\n"); + goto out_release_resources; + } + + adapter->mmioBase = pci_iomap(pdev, i, PVSCSI_MEM_SPACE_SIZE); + + if (!adapter->mmioBase) { + printk(KERN_ERR + "vmw_pvscsi: can't iomap for BAR %d memsize %lu\n", + i, PVSCSI_MEM_SPACE_SIZE); + goto out_release_resources; + } + + pci_set_master(pdev); + pci_set_drvdata(pdev, host); + + ll_adapter_reset(adapter); + + adapter->use_msg = pvscsi_setup_msg_workqueue(adapter); + + error = pvscsi_allocate_rings(adapter); + if (error) { + printk(KERN_ERR "vmw_pvscsi: unable to allocate ring memory\n"); + goto out_release_resources; + } + + /* + * From this point on we should reset the adapter if anything goes + * wrong. + */ + pvscsi_setup_all_rings(adapter); + + adapter->cmd_map = kcalloc(adapter->req_depth, + sizeof(struct pvscsi_ctx), GFP_KERNEL); + if (!adapter->cmd_map) { + printk(KERN_ERR "vmw_pvscsi: failed to allocate memory.\n"); + error = -ENOMEM; + goto out_reset_adapter; + } + + INIT_LIST_HEAD(&adapter->cmd_pool); + for (i = 0; i < adapter->req_depth; i++) { + struct pvscsi_ctx *ctx = adapter->cmd_map + i; + list_add(&ctx->list, &adapter->cmd_pool); + } + + error = pvscsi_allocate_sg(adapter); + if (error) { + printk(KERN_ERR "vmw_pvscsi: unable to allocate s/g table\n"); + goto out_reset_adapter; + } + + if (!pvscsi_disable_msix && + pvscsi_setup_msix(adapter, &adapter->irq) == 0) { + printk(KERN_INFO "vmw_pvscsi: using MSI-X\n"); + adapter->use_msix = 1; + } else if (!pvscsi_disable_msi && pci_enable_msi(pdev) == 0) { + printk(KERN_INFO "vmw_pvscsi: using MSI\n"); + adapter->use_msi = 1; + adapter->irq = pdev->irq; + } else { + printk(KERN_INFO "vmw_pvscsi: using INTx\n"); + adapter->irq = pdev->irq; + flags = IRQF_SHARED; + } + + error = request_irq(adapter->irq, pvscsi_isr, flags, + "vmw_pvscsi", adapter); + if (error) { + printk(KERN_ERR + "vmw_pvscsi: unable to request IRQ: %d\n", error); + adapter->irq = 0; + goto out_reset_adapter; + } + + error = scsi_add_host(host, &pdev->dev); + if (error) { + printk(KERN_ERR + "vmw_pvscsi: scsi_add_host failed: %d\n", error); + goto out_reset_adapter; + } + + dev_info(&pdev->dev, "VMware PVSCSI rev %d host #%u\n", + adapter->rev, host->host_no); + + pvscsi_unmask_intr(adapter); + + scsi_scan_host(host); + + return 0; + +out_reset_adapter: + ll_adapter_reset(adapter); +out_release_resources: + pvscsi_release_resources(adapter); +out_free_host: + scsi_host_put(host); +out_disable_device: + pci_set_drvdata(pdev, NULL); + pci_disable_device(pdev); + + return error; +} + +static void __pvscsi_shutdown(struct pvscsi_adapter *adapter) +{ + pvscsi_mask_intr(adapter); + + if (adapter->workqueue) + flush_workqueue(adapter->workqueue); + + pvscsi_shutdown_intr(adapter); + + pvscsi_process_request_ring(adapter); + pvscsi_process_completion_ring(adapter); + ll_adapter_reset(adapter); +} + +static void pvscsi_shutdown(struct pci_dev *dev) +{ + struct Scsi_Host *host = pci_get_drvdata(dev); + struct pvscsi_adapter *adapter = shost_priv(host); + + __pvscsi_shutdown(adapter); +} + +static void pvscsi_remove(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + struct pvscsi_adapter *adapter = shost_priv(host); + + scsi_remove_host(host); + + __pvscsi_shutdown(adapter); + pvscsi_release_resources(adapter); + + scsi_host_put(host); + + pci_set_drvdata(pdev, NULL); + pci_disable_device(pdev); +} + +static struct pci_driver pvscsi_pci_driver = { + .name = "vmw_pvscsi", + .id_table = pvscsi_pci_tbl, + .probe = pvscsi_probe, + .remove = __devexit_p(pvscsi_remove), + .shutdown = pvscsi_shutdown, +}; + +static int __init pvscsi_init(void) +{ + pr_info("%s - version %s\n", + PVSCSI_LINUX_DRIVER_DESC, PVSCSI_DRIVER_VERSION_STRING); + return pci_register_driver(&pvscsi_pci_driver); +} + +static void __exit pvscsi_exit(void) +{ + pci_unregister_driver(&pvscsi_pci_driver); +} + +module_init(pvscsi_init); +module_exit(pvscsi_exit); diff --git a/drivers/scsi/vmw_pvscsi.h b/drivers/scsi/vmw_pvscsi.h new file mode 100644 index 000000000000..62e36e75715e --- /dev/null +++ b/drivers/scsi/vmw_pvscsi.h @@ -0,0 +1,397 @@ +/* + * VMware PVSCSI header file + * + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained by: Alok N Kataria <akataria@vmware.com> + * + */ + +#ifndef _VMW_PVSCSI_H_ +#define _VMW_PVSCSI_H_ + +#include <linux/types.h> + +#define PVSCSI_DRIVER_VERSION_STRING "1.0.1.0-k" + +#define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128 + +#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */ + +#define PCI_VENDOR_ID_VMWARE 0x15AD +#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0 + +/* + * host adapter status/error codes + */ +enum HostBusAdapterStatus { + BTSTAT_SUCCESS = 0x00, /* CCB complete normally with no errors */ + BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a, + BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b, + BTSTAT_DATA_UNDERRUN = 0x0c, + BTSTAT_SELTIMEO = 0x11, /* SCSI selection timeout */ + BTSTAT_DATARUN = 0x12, /* data overrun/underrun */ + BTSTAT_BUSFREE = 0x13, /* unexpected bus free */ + BTSTAT_INVPHASE = 0x14, /* invalid bus phase or sequence requested by target */ + BTSTAT_LUNMISMATCH = 0x17, /* linked CCB has different LUN from first CCB */ + BTSTAT_SENSFAILED = 0x1b, /* auto request sense failed */ + BTSTAT_TAGREJECT = 0x1c, /* SCSI II tagged queueing message rejected by target */ + BTSTAT_BADMSG = 0x1d, /* unsupported message received by the host adapter */ + BTSTAT_HAHARDWARE = 0x20, /* host adapter hardware failed */ + BTSTAT_NORESPONSE = 0x21, /* target did not respond to SCSI ATN, sent a SCSI RST */ + BTSTAT_SENTRST = 0x22, /* host adapter asserted a SCSI RST */ + BTSTAT_RECVRST = 0x23, /* other SCSI devices asserted a SCSI RST */ + BTSTAT_DISCONNECT = 0x24, /* target device reconnected improperly (w/o tag) */ + BTSTAT_BUSRESET = 0x25, /* host adapter issued BUS device reset */ + BTSTAT_ABORTQUEUE = 0x26, /* abort queue generated */ + BTSTAT_HASOFTWARE = 0x27, /* host adapter software error */ + BTSTAT_HATIMEOUT = 0x30, /* host adapter hardware timeout error */ + BTSTAT_SCSIPARITY = 0x34, /* SCSI parity error detected */ +}; + +/* + * Register offsets. + * + * These registers are accessible both via i/o space and mm i/o. + */ + +enum PVSCSIRegOffset { + PVSCSI_REG_OFFSET_COMMAND = 0x0, + PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4, + PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8, + PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100, + PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104, + PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108, + PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c, + PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c, + PVSCSI_REG_OFFSET_INTR_MASK = 0x2010, + PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014, + PVSCSI_REG_OFFSET_DEBUG = 0x3018, + PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018, +}; + +/* + * Virtual h/w commands. + */ + +enum PVSCSICommands { + PVSCSI_CMD_FIRST = 0, /* has to be first */ + + PVSCSI_CMD_ADAPTER_RESET = 1, + PVSCSI_CMD_ISSUE_SCSI = 2, + PVSCSI_CMD_SETUP_RINGS = 3, + PVSCSI_CMD_RESET_BUS = 4, + PVSCSI_CMD_RESET_DEVICE = 5, + PVSCSI_CMD_ABORT_CMD = 6, + PVSCSI_CMD_CONFIG = 7, + PVSCSI_CMD_SETUP_MSG_RING = 8, + PVSCSI_CMD_DEVICE_UNPLUG = 9, + + PVSCSI_CMD_LAST = 10 /* has to be last */ +}; + +/* + * Command descriptor for PVSCSI_CMD_RESET_DEVICE -- + */ + +struct PVSCSICmdDescResetDevice { + u32 target; + u8 lun[8]; +} __packed; + +/* + * Command descriptor for PVSCSI_CMD_ABORT_CMD -- + * + * - currently does not support specifying the LUN. + * - _pad should be 0. + */ + +struct PVSCSICmdDescAbortCmd { + u64 context; + u32 target; + u32 _pad; +} __packed; + +/* + * Command descriptor for PVSCSI_CMD_SETUP_RINGS -- + * + * Notes: + * - reqRingNumPages and cmpRingNumPages need to be power of two. + * - reqRingNumPages and cmpRingNumPages need to be different from 0, + * - reqRingNumPages and cmpRingNumPages need to be inferior to + * PVSCSI_SETUP_RINGS_MAX_NUM_PAGES. + */ + +#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32 +struct PVSCSICmdDescSetupRings { + u32 reqRingNumPages; + u32 cmpRingNumPages; + u64 ringsStatePPN; + u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; + u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; +} __packed; + +/* + * Command descriptor for PVSCSI_CMD_SETUP_MSG_RING -- + * + * Notes: + * - this command was not supported in the initial revision of the h/w + * interface. Before using it, you need to check that it is supported by + * writing PVSCSI_CMD_SETUP_MSG_RING to the 'command' register, then + * immediately after read the 'command status' register: + * * a value of -1 means that the cmd is NOT supported, + * * a value != -1 means that the cmd IS supported. + * If it's supported the 'command status' register should return: + * sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(u32). + * - this command should be issued _after_ the usual SETUP_RINGS so that the + * RingsState page is already setup. If not, the command is a nop. + * - numPages needs to be a power of two, + * - numPages needs to be different from 0, + * - _pad should be zero. + */ + +#define PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES 16 + +struct PVSCSICmdDescSetupMsgRing { + u32 numPages; + u32 _pad; + u64 ringPPNs[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES]; +} __packed; + +enum PVSCSIMsgType { + PVSCSI_MSG_DEV_ADDED = 0, + PVSCSI_MSG_DEV_REMOVED = 1, + PVSCSI_MSG_LAST = 2, +}; + +/* + * Msg descriptor. + * + * sizeof(struct PVSCSIRingMsgDesc) == 128. + * + * - type is of type enum PVSCSIMsgType. + * - the content of args depend on the type of event being delivered. + */ + +struct PVSCSIRingMsgDesc { + u32 type; + u32 args[31]; +} __packed; + +struct PVSCSIMsgDescDevStatusChanged { + u32 type; /* PVSCSI_MSG_DEV _ADDED / _REMOVED */ + u32 bus; + u32 target; + u8 lun[8]; + u32 pad[27]; +} __packed; + +/* + * Rings state. + * + * - the fields: + * . msgProdIdx, + * . msgConsIdx, + * . msgNumEntriesLog2, + * .. are only used once the SETUP_MSG_RING cmd has been issued. + * - '_pad' helps to ensure that the msg related fields are on their own + * cache-line. + */ + +struct PVSCSIRingsState { + u32 reqProdIdx; + u32 reqConsIdx; + u32 reqNumEntriesLog2; + + u32 cmpProdIdx; + u32 cmpConsIdx; + u32 cmpNumEntriesLog2; + + u8 _pad[104]; + + u32 msgProdIdx; + u32 msgConsIdx; + u32 msgNumEntriesLog2; +} __packed; + +/* + * Request descriptor. + * + * sizeof(RingReqDesc) = 128 + * + * - context: is a unique identifier of a command. It could normally be any + * 64bit value, however we currently store it in the serialNumber variable + * of struct SCSI_Command, so we have the following restrictions due to the + * way this field is handled in the vmkernel storage stack: + * * this value can't be 0, + * * the upper 32bit need to be 0 since serialNumber is as a u32. + * Currently tracked as PR 292060. + * - dataLen: contains the total number of bytes that need to be transferred. + * - dataAddr: + * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is set: dataAddr is the PA of the first + * s/g table segment, each s/g segment is entirely contained on a single + * page of physical memory, + * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is NOT set, then dataAddr is the PA of + * the buffer used for the DMA transfer, + * - flags: + * * PVSCSI_FLAG_CMD_WITH_SG_LIST: see dataAddr above, + * * PVSCSI_FLAG_CMD_DIR_NONE: no DMA involved, + * * PVSCSI_FLAG_CMD_DIR_TOHOST: transfer from device to main memory, + * * PVSCSI_FLAG_CMD_DIR_TODEVICE: transfer from main memory to device, + * * PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB: reserved to handle CDBs larger than + * 16bytes. To be specified. + * - vcpuHint: vcpuId of the processor that will be most likely waiting for the + * completion of the i/o. For guest OSes that use lowest priority message + * delivery mode (such as windows), we use this "hint" to deliver the + * completion action to the proper vcpu. For now, we can use the vcpuId of + * the processor that initiated the i/o as a likely candidate for the vcpu + * that will be waiting for the completion.. + * - bus should be 0: we currently only support bus 0 for now. + * - unused should be zero'd. + */ + +#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0) +#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1) +#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2) +#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3) +#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4) + +struct PVSCSIRingReqDesc { + u64 context; + u64 dataAddr; + u64 dataLen; + u64 senseAddr; + u32 senseLen; + u32 flags; + u8 cdb[16]; + u8 cdbLen; + u8 lun[8]; + u8 tag; + u8 bus; + u8 target; + u8 vcpuHint; + u8 unused[59]; +} __packed; + +/* + * Scatter-gather list management. + * + * As described above, when PVSCSI_FLAG_CMD_WITH_SG_LIST is set in the + * RingReqDesc.flags, then RingReqDesc.dataAddr is the PA of the first s/g + * table segment. + * + * - each segment of the s/g table contain a succession of struct + * PVSCSISGElement. + * - each segment is entirely contained on a single physical page of memory. + * - a "chain" s/g element has the flag PVSCSI_SGE_FLAG_CHAIN_ELEMENT set in + * PVSCSISGElement.flags and in this case: + * * addr is the PA of the next s/g segment, + * * length is undefined, assumed to be 0. + */ + +struct PVSCSISGElement { + u64 addr; + u32 length; + u32 flags; +} __packed; + +/* + * Completion descriptor. + * + * sizeof(RingCmpDesc) = 32 + * + * - context: identifier of the command. The same thing that was specified + * under "context" as part of struct RingReqDesc at initiation time, + * - dataLen: number of bytes transferred for the actual i/o operation, + * - senseLen: number of bytes written into the sense buffer, + * - hostStatus: adapter status, + * - scsiStatus: device status, + * - _pad should be zero. + */ + +struct PVSCSIRingCmpDesc { + u64 context; + u64 dataLen; + u32 senseLen; + u16 hostStatus; + u16 scsiStatus; + u32 _pad[2]; +} __packed; + +/* + * Interrupt status / IRQ bits. + */ + +#define PVSCSI_INTR_CMPL_0 (1 << 0) +#define PVSCSI_INTR_CMPL_1 (1 << 1) +#define PVSCSI_INTR_CMPL_MASK MASK(2) + +#define PVSCSI_INTR_MSG_0 (1 << 2) +#define PVSCSI_INTR_MSG_1 (1 << 3) +#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2) + +#define PVSCSI_INTR_ALL_SUPPORTED MASK(4) + +/* + * Number of MSI-X vectors supported. + */ +#define PVSCSI_MAX_INTRS 24 + +/* + * Enumeration of supported MSI-X vectors + */ +#define PVSCSI_VECTOR_COMPLETION 0 + +/* + * Misc constants for the rings. + */ + +#define PVSCSI_MAX_NUM_PAGES_REQ_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES +#define PVSCSI_MAX_NUM_PAGES_CMP_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES +#define PVSCSI_MAX_NUM_PAGES_MSG_RING PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES + +#define PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \ + (PAGE_SIZE / sizeof(struct PVSCSIRingReqDesc)) + +#define PVSCSI_MAX_REQ_QUEUE_DEPTH \ + (PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE) + +#define PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES 1 +#define PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES 1 +#define PVSCSI_MEM_SPACE_MISC_NUM_PAGES 2 +#define PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES 2 +#define PVSCSI_MEM_SPACE_MSIX_NUM_PAGES 2 + +enum PVSCSIMemSpace { + PVSCSI_MEM_SPACE_COMMAND_PAGE = 0, + PVSCSI_MEM_SPACE_INTR_STATUS_PAGE = 1, + PVSCSI_MEM_SPACE_MISC_PAGE = 2, + PVSCSI_MEM_SPACE_KICK_IO_PAGE = 4, + PVSCSI_MEM_SPACE_MSIX_TABLE_PAGE = 6, + PVSCSI_MEM_SPACE_MSIX_PBA_PAGE = 7, +}; + +#define PVSCSI_MEM_SPACE_NUM_PAGES \ + (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES + \ + PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES + \ + PVSCSI_MEM_SPACE_MISC_NUM_PAGES + \ + PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES + \ + PVSCSI_MEM_SPACE_MSIX_NUM_PAGES) + +#define PVSCSI_MEM_SPACE_SIZE (PVSCSI_MEM_SPACE_NUM_PAGES * PAGE_SIZE) + +#endif /* _VMW_PVSCSI_H_ */ diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c index 093610bcfcce..2f6e9d8eaf71 100644 --- a/drivers/scsi/wd7000.c +++ b/drivers/scsi/wd7000.c @@ -161,7 +161,7 @@ * * 2003/02/12 - Christoph Hellwig <hch@infradead.org> * - * Cleaned up host template defintion + * Cleaned up host template definition * Removed now obsolete wd7000.h */ diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index d71dfe398940..36ede02ceacf 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -361,9 +361,9 @@ static const struct pnp_device_id pnp_dev_table[] = { { "LTS0001", 0 }, /* Rockwell's (PORALiNK) 33600 INT PNP */ { "WCI0003", 0 }, - /* Unkown PnP modems */ + /* Unknown PnP modems */ { "PNPCXXX", UNKNOWN_DEV }, - /* More unkown PnP modems */ + /* More unknown PnP modems */ { "PNPDXXX", UNKNOWN_DEV }, { "", 0 } }; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index e52257257279..9ff47db0b2ce 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -996,7 +996,7 @@ config SERIAL_IP22_ZILOG_CONSOLE config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" - depends on SUPERH || H8300 + depends on HAVE_CLK && (SUPERH || H8300) select SERIAL_CORE config SERIAL_SH_SCI_NR_UARTS @@ -1477,4 +1477,17 @@ config SERIAL_BCM63XX_CONSOLE If you have enabled the serial port on the bcm63xx CPU you can make it the console by answering Y to this option. +config SERIAL_GRLIB_GAISLER_APBUART + tristate "GRLIB APBUART serial support" + depends on OF + ---help--- + Add support for the GRLIB APBUART serial port. + +config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE + bool "Console on GRLIB APBUART serial port" + depends on SERIAL_GRLIB_GAISLER_APBUART=y + select SERIAL_CORE_CONSOLE + help + Support for running a console on the GRLIB APBUART + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index d21d5dd5d048..5548fe7df61d 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -81,3 +81,4 @@ obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o +obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o diff --git a/drivers/serial/apbuart.c b/drivers/serial/apbuart.c new file mode 100644 index 000000000000..fe91319b5f65 --- /dev/null +++ b/drivers/serial/apbuart.c @@ -0,0 +1,710 @@ +/* + * Driver for GRLIB serial ports (APBUART) + * + * Based on linux/drivers/serial/amba.c + * + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * Copyright (C) 2003 Konrad Eisele <eiselekd@web.de> + * Copyright (C) 2006 Daniel Hellstrom <daniel@gaisler.com>, Aeroflex Gaisler AB + * Copyright (C) 2008 Gilead Kutnick <kutnickg@zin-tech.com> + * Copyright (C) 2009 Kristoffer Glembo <kristoffer@gaisler.com>, Aeroflex Gaisler AB + */ + +#if defined(CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/kthread.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/serial_core.h> +#include <asm/irq.h> + +#include "apbuart.h" + +#define SERIAL_APBUART_MAJOR TTY_MAJOR +#define SERIAL_APBUART_MINOR 64 +#define UART_DUMMY_RSR_RX 0x8000 /* for ignore all read */ + +static void apbuart_tx_chars(struct uart_port *port); + +static void apbuart_stop_tx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr &= ~UART_CTRL_TI; + UART_PUT_CTRL(port, cr); +} + +static void apbuart_start_tx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr |= UART_CTRL_TI; + UART_PUT_CTRL(port, cr); + + if (UART_GET_STATUS(port) & UART_STATUS_THE) + apbuart_tx_chars(port); +} + +static void apbuart_stop_rx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr &= ~(UART_CTRL_RI); + UART_PUT_CTRL(port, cr); +} + +static void apbuart_enable_ms(struct uart_port *port) +{ + /* No modem status change interrupts for APBUART */ +} + +static void apbuart_rx_chars(struct uart_port *port) +{ + struct tty_struct *tty = port->state->port.tty; + unsigned int status, ch, rsr, flag; + unsigned int max_chars = port->fifosize; + + status = UART_GET_STATUS(port); + + while (UART_RX_DATA(status) && (max_chars--)) { + + ch = UART_GET_CHAR(port); + flag = TTY_NORMAL; + + port->icount.rx++; + + rsr = UART_GET_STATUS(port) | UART_DUMMY_RSR_RX; + UART_PUT_STATUS(port, 0); + if (rsr & UART_STATUS_ERR) { + + if (rsr & UART_STATUS_BR) { + rsr &= ~(UART_STATUS_FE | UART_STATUS_PE); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } else if (rsr & UART_STATUS_PE) { + port->icount.parity++; + } else if (rsr & UART_STATUS_FE) { + port->icount.frame++; + } + if (rsr & UART_STATUS_OE) + port->icount.overrun++; + + rsr &= port->read_status_mask; + + if (rsr & UART_STATUS_PE) + flag = TTY_PARITY; + else if (rsr & UART_STATUS_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, rsr, UART_STATUS_OE, ch, flag); + + + ignore_char: + status = UART_GET_STATUS(port); + } + + tty_flip_buffer_push(tty); +} + +static void apbuart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int count; + + if (port->x_char) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + apbuart_stop_tx(port); + return; + } + + /* amba: fill FIFO */ + count = port->fifosize >> 1; + do { + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + apbuart_stop_tx(port); +} + +static irqreturn_t apbuart_int(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int status; + + spin_lock(&port->lock); + + status = UART_GET_STATUS(port); + if (status & UART_STATUS_DR) + apbuart_rx_chars(port); + if (status & UART_STATUS_THE) + apbuart_tx_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static unsigned int apbuart_tx_empty(struct uart_port *port) +{ + unsigned int status = UART_GET_STATUS(port); + return status & UART_STATUS_THE ? TIOCSER_TEMT : 0; +} + +static unsigned int apbuart_get_mctrl(struct uart_port *port) +{ + /* The GRLIB APBUART handles flow control in hardware */ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void apbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* The GRLIB APBUART handles flow control in hardware */ +} + +static void apbuart_break_ctl(struct uart_port *port, int break_state) +{ + /* We don't support sending break */ +} + +static int apbuart_startup(struct uart_port *port) +{ + int retval; + unsigned int cr; + + /* Allocate the IRQ */ + retval = request_irq(port->irq, apbuart_int, 0, "apbuart", port); + if (retval) + return retval; + + /* Finally, enable interrupts */ + cr = UART_GET_CTRL(port); + UART_PUT_CTRL(port, + cr | UART_CTRL_RE | UART_CTRL_TE | + UART_CTRL_RI | UART_CTRL_TI); + + return 0; +} + +static void apbuart_shutdown(struct uart_port *port) +{ + unsigned int cr; + + /* disable all interrupts, disable the port */ + cr = UART_GET_CTRL(port); + UART_PUT_CTRL(port, + cr & ~(UART_CTRL_RE | UART_CTRL_TE | + UART_CTRL_RI | UART_CTRL_TI)); + + /* Free the interrupt */ + free_irq(port->irq, port); +} + +static void apbuart_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + unsigned int cr; + unsigned long flags; + unsigned int baud, quot; + + /* Ask the core to calculate the divisor for us. */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + if (baud == 0) + panic("invalid baudrate %i\n", port->uartclk / 16); + + /* uart_get_divisor calc a *16 uart freq, apbuart is *8 */ + quot = (uart_get_divisor(port, baud)) * 2; + cr = UART_GET_CTRL(port); + cr &= ~(UART_CTRL_PE | UART_CTRL_PS); + + if (termios->c_cflag & PARENB) { + cr |= UART_CTRL_PE; + if ((termios->c_cflag & PARODD)) + cr |= UART_CTRL_PS; + } + + /* Enable flow control. */ + if (termios->c_cflag & CRTSCTS) + cr |= UART_CTRL_FL; + + spin_lock_irqsave(&port->lock, flags); + + /* Update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = UART_STATUS_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_STATUS_FE | UART_STATUS_PE; + + /* Characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_STATUS_FE | UART_STATUS_PE; + + /* Ignore all characters if CREAD is not set. */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_RSR_RX; + + /* Set baud rate */ + quot -= 1; + UART_PUT_SCAL(port, quot); + UART_PUT_CTRL(port, cr); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *apbuart_type(struct uart_port *port) +{ + return port->type == PORT_APBUART ? "GRLIB/APBUART" : NULL; +} + +static void apbuart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, 0x100); +} + +static int apbuart_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, 0x100, "grlib-apbuart") + != NULL ? 0 : -EBUSY; + return 0; +} + +/* Configure/autoconfigure the port */ +static void apbuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_APBUART; + apbuart_request_port(port); + } +} + +/* Verify the new serial_struct (for TIOCSSERIAL) */ +static int apbuart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_APBUART) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops grlib_apbuart_ops = { + .tx_empty = apbuart_tx_empty, + .set_mctrl = apbuart_set_mctrl, + .get_mctrl = apbuart_get_mctrl, + .stop_tx = apbuart_stop_tx, + .start_tx = apbuart_start_tx, + .stop_rx = apbuart_stop_rx, + .enable_ms = apbuart_enable_ms, + .break_ctl = apbuart_break_ctl, + .startup = apbuart_startup, + .shutdown = apbuart_shutdown, + .set_termios = apbuart_set_termios, + .type = apbuart_type, + .release_port = apbuart_release_port, + .request_port = apbuart_request_port, + .config_port = apbuart_config_port, + .verify_port = apbuart_verify_port, +}; + +static struct uart_port grlib_apbuart_ports[UART_NR]; +static struct device_node *grlib_apbuart_nodes[UART_NR]; + +static int apbuart_scan_fifo_size(struct uart_port *port, int portnumber) +{ + int ctrl, loop = 0; + int status; + int fifosize; + unsigned long flags; + + ctrl = UART_GET_CTRL(port); + + /* + * Enable the transceiver and wait for it to be ready to send data. + * Clear interrupts so that this process will not be externally + * interrupted in the middle (which can cause the transceiver to + * drain prematurely). + */ + + local_irq_save(flags); + + UART_PUT_CTRL(port, ctrl | UART_CTRL_TE); + + while (!UART_TX_READY(UART_GET_STATUS(port))) + loop++; + + /* + * Disable the transceiver so data isn't actually sent during the + * actual test. + */ + + UART_PUT_CTRL(port, ctrl & ~(UART_CTRL_TE)); + + fifosize = 1; + UART_PUT_CHAR(port, 0); + + /* + * So long as transmitting a character increments the tranceivier FIFO + * length the FIFO must be at least that big. These bytes will + * automatically drain off of the FIFO. + */ + + status = UART_GET_STATUS(port); + while (((status >> 20) & 0x3F) == fifosize) { + fifosize++; + UART_PUT_CHAR(port, 0); + status = UART_GET_STATUS(port); + } + + fifosize--; + + UART_PUT_CTRL(port, ctrl); + local_irq_restore(flags); + + if (fifosize == 0) + fifosize = 1; + + return fifosize; +} + +static void apbuart_flush_fifo(struct uart_port *port) +{ + int i; + + for (i = 0; i < port->fifosize; i++) + UART_GET_CHAR(port); +} + + +/* ======================================================================== */ +/* Console driver, if enabled */ +/* ======================================================================== */ + +#ifdef CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE + +static void apbuart_console_putchar(struct uart_port *port, int ch) +{ + unsigned int status; + do { + status = UART_GET_STATUS(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, ch); +} + +static void +apbuart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &grlib_apbuart_ports[co->index]; + unsigned int status, old_cr, new_cr; + + /* First save the CR then disable the interrupts */ + old_cr = UART_GET_CTRL(port); + new_cr = old_cr & ~(UART_CTRL_RI | UART_CTRL_TI); + UART_PUT_CTRL(port, new_cr); + + uart_console_write(port, s, count, apbuart_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = UART_GET_STATUS(port); + } while (!UART_TX_READY(status)); + UART_PUT_CTRL(port, old_cr); +} + +static void __init +apbuart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (UART_GET_CTRL(port) & (UART_CTRL_RE | UART_CTRL_TE)) { + + unsigned int quot, status; + status = UART_GET_STATUS(port); + + *parity = 'n'; + if (status & UART_CTRL_PE) { + if ((status & UART_CTRL_PS) == 0) + *parity = 'e'; + else + *parity = 'o'; + } + + *bits = 8; + quot = UART_GET_SCAL(port) / 8; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init apbuart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + pr_debug("apbuart_console_setup co=%p, co->index=%i, options=%s\n", + co, co->index, options); + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= grlib_apbuart_port_nr) + co->index = 0; + + port = &grlib_apbuart_ports[co->index]; + + spin_lock_init(&port->lock); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + apbuart_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver grlib_apbuart_driver; + +static struct console grlib_apbuart_console = { + .name = "ttyS", + .write = apbuart_console_write, + .device = uart_console_device, + .setup = apbuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &grlib_apbuart_driver, +}; + + +static void grlib_apbuart_configure(void); + +static int __init apbuart_console_init(void) +{ + grlib_apbuart_configure(); + register_console(&grlib_apbuart_console); + return 0; +} + +console_initcall(apbuart_console_init); + +#define APBUART_CONSOLE (&grlib_apbuart_console) +#else +#define APBUART_CONSOLE NULL +#endif + +static struct uart_driver grlib_apbuart_driver = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = SERIAL_APBUART_MAJOR, + .minor = SERIAL_APBUART_MINOR, + .nr = UART_NR, + .cons = APBUART_CONSOLE, +}; + + +/* ======================================================================== */ +/* OF Platform Driver */ +/* ======================================================================== */ + +static int __devinit apbuart_probe(struct of_device *op, + const struct of_device_id *match) +{ + int i = -1; + struct uart_port *port = NULL; + + i = 0; + for (i = 0; i < grlib_apbuart_port_nr; i++) { + if (op->node == grlib_apbuart_nodes[i]) + break; + } + + port = &grlib_apbuart_ports[i]; + port->dev = &op->dev; + + uart_add_one_port(&grlib_apbuart_driver, (struct uart_port *) port); + + apbuart_flush_fifo((struct uart_port *) port); + + printk(KERN_INFO "grlib-apbuart at 0x%llx, irq %d\n", + (unsigned long long) port->mapbase, port->irq); + return 0; + +} + +static struct of_device_id __initdata apbuart_match[] = { + { + .name = "GAISLER_APBUART", + }, + {}, +}; + +static struct of_platform_driver grlib_apbuart_of_driver = { + .match_table = apbuart_match, + .probe = apbuart_probe, + .driver = { + .owner = THIS_MODULE, + .name = "grlib-apbuart", + }, +}; + + +static void grlib_apbuart_configure(void) +{ + static int enum_done; + struct device_node *np, *rp; + struct uart_port *port = NULL; + const u32 *prop; + int freq_khz; + int v = 0, d = 0; + unsigned int addr; + int irq, line; + struct amba_prom_registers *regs; + + if (enum_done) + return; + + /* Get bus frequency */ + rp = of_find_node_by_path("/"); + rp = of_get_next_child(rp, NULL); + prop = of_get_property(rp, "clock-frequency", NULL); + freq_khz = *prop; + + line = 0; + for_each_matching_node(np, apbuart_match) { + + int *vendor = (int *) of_get_property(np, "vendor", NULL); + int *device = (int *) of_get_property(np, "device", NULL); + int *irqs = (int *) of_get_property(np, "interrupts", NULL); + regs = (struct amba_prom_registers *) + of_get_property(np, "reg", NULL); + + if (vendor) + v = *vendor; + if (device) + d = *device; + + if (!irqs || !regs) + return; + + grlib_apbuart_nodes[line] = np; + + addr = regs->phys_addr; + irq = *irqs; + + port = &grlib_apbuart_ports[line]; + + port->mapbase = addr; + port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map)); + port->irq = irq; + port->iotype = UPIO_MEM; + port->ops = &grlib_apbuart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->line = line; + port->uartclk = freq_khz * 1000; + port->fifosize = apbuart_scan_fifo_size((struct uart_port *) port, line); + line++; + + /* We support maximum UART_NR uarts ... */ + if (line == UART_NR) + break; + + } + + enum_done = 1; + + grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line; +} + +static int __init grlib_apbuart_init(void) +{ + int ret; + + /* Find all APBUARTS in device the tree and initialize their ports */ + grlib_apbuart_configure(); + + printk(KERN_INFO "Serial: GRLIB APBUART driver\n"); + + ret = uart_register_driver(&grlib_apbuart_driver); + + if (ret) { + printk(KERN_ERR "%s: uart_register_driver failed (%i)\n", + __FILE__, ret); + return ret; + } + + ret = of_register_platform_driver(&grlib_apbuart_of_driver); + if (ret) { + printk(KERN_ERR + "%s: of_register_platform_driver failed (%i)\n", + __FILE__, ret); + uart_unregister_driver(&grlib_apbuart_driver); + return ret; + } + + return ret; +} + +static void __exit grlib_apbuart_exit(void) +{ + int i; + + for (i = 0; i < grlib_apbuart_port_nr; i++) + uart_remove_one_port(&grlib_apbuart_driver, + &grlib_apbuart_ports[i]); + + uart_unregister_driver(&grlib_apbuart_driver); + of_unregister_platform_driver(&grlib_apbuart_of_driver); +} + +module_init(grlib_apbuart_init); +module_exit(grlib_apbuart_exit); + +MODULE_AUTHOR("Aeroflex Gaisler AB"); +MODULE_DESCRIPTION("GRLIB APBUART serial driver"); +MODULE_VERSION("2.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/apbuart.h b/drivers/serial/apbuart.h new file mode 100644 index 000000000000..5faf87c8d2bc --- /dev/null +++ b/drivers/serial/apbuart.h @@ -0,0 +1,64 @@ +#ifndef __GRLIB_APBUART_H__ +#define __GRLIB_APBUART_H__ + +#include <asm/io.h> + +#define UART_NR 8 +static int grlib_apbuart_port_nr; + +struct grlib_apbuart_regs_map { + u32 data; + u32 status; + u32 ctrl; + u32 scaler; +}; + +struct amba_prom_registers { + unsigned int phys_addr; + unsigned int reg_size; +}; + +/* + * The following defines the bits in the APBUART Status Registers. + */ +#define UART_STATUS_DR 0x00000001 /* Data Ready */ +#define UART_STATUS_TSE 0x00000002 /* TX Send Register Empty */ +#define UART_STATUS_THE 0x00000004 /* TX Hold Register Empty */ +#define UART_STATUS_BR 0x00000008 /* Break Error */ +#define UART_STATUS_OE 0x00000010 /* RX Overrun Error */ +#define UART_STATUS_PE 0x00000020 /* RX Parity Error */ +#define UART_STATUS_FE 0x00000040 /* RX Framing Error */ +#define UART_STATUS_ERR 0x00000078 /* Error Mask */ + +/* + * The following defines the bits in the APBUART Ctrl Registers. + */ +#define UART_CTRL_RE 0x00000001 /* Receiver enable */ +#define UART_CTRL_TE 0x00000002 /* Transmitter enable */ +#define UART_CTRL_RI 0x00000004 /* Receiver interrupt enable */ +#define UART_CTRL_TI 0x00000008 /* Transmitter irq */ +#define UART_CTRL_PS 0x00000010 /* Parity select */ +#define UART_CTRL_PE 0x00000020 /* Parity enable */ +#define UART_CTRL_FL 0x00000040 /* Flow control enable */ +#define UART_CTRL_LB 0x00000080 /* Loopback enable */ + +#define APBBASE(port) ((struct grlib_apbuart_regs_map *)((port)->membase)) + +#define APBBASE_DATA_P(port) (&(APBBASE(port)->data)) +#define APBBASE_STATUS_P(port) (&(APBBASE(port)->status)) +#define APBBASE_CTRL_P(port) (&(APBBASE(port)->ctrl)) +#define APBBASE_SCALAR_P(port) (&(APBBASE(port)->scaler)) + +#define UART_GET_CHAR(port) (__raw_readl(APBBASE_DATA_P(port))) +#define UART_PUT_CHAR(port, v) (__raw_writel(v, APBBASE_DATA_P(port))) +#define UART_GET_STATUS(port) (__raw_readl(APBBASE_STATUS_P(port))) +#define UART_PUT_STATUS(port, v)(__raw_writel(v, APBBASE_STATUS_P(port))) +#define UART_GET_CTRL(port) (__raw_readl(APBBASE_CTRL_P(port))) +#define UART_PUT_CTRL(port, v) (__raw_writel(v, APBBASE_CTRL_P(port))) +#define UART_GET_SCAL(port) (__raw_readl(APBBASE_SCALAR_P(port))) +#define UART_PUT_SCAL(port, v) (__raw_writel(v, APBBASE_SCALAR_P(port))) + +#define UART_RX_DATA(s) (((s) & UART_STATUS_DR) != 0) +#define UART_TX_READY(s) (((s) & UART_STATUS_THE) != 0) + +#endif /* __GRLIB_APBUART_H__ */ diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c index b44382442bf1..7bb5fee639e3 100644 --- a/drivers/serial/mcf.c +++ b/drivers/serial/mcf.c @@ -602,7 +602,7 @@ static int __devinit mcf_probe(struct platform_device *pdev) /****************************************************************************/ -static int mcf_remove(struct platform_device *pdev) +static int __devexit mcf_remove(struct platform_device *pdev) { struct uart_port *port; int i; diff --git a/drivers/serial/pmac_zilog.h b/drivers/serial/pmac_zilog.h index 570b0d925e83..f6e77f12acd5 100644 --- a/drivers/serial/pmac_zilog.h +++ b/drivers/serial/pmac_zilog.h @@ -73,7 +73,7 @@ static inline struct uart_pmac_port *pmz_get_port_A(struct uart_pmac_port *uap) } /* - * Register acessors. Note that we don't need to enforce a recovery + * Register accessors. Note that we don't need to enforce a recovery * delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip, * though if we try to use this driver on older machines, we might have * to add it back diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index c99f0821cae3..73f089d3efd6 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -2,7 +2,7 @@ * * Driver for Samsung S3C2410 SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/serial/s3c2412.c b/drivers/serial/s3c2412.c index 6e057d8809d3..ce75e28e36ef 100644 --- a/drivers/serial/s3c2412.c +++ b/drivers/serial/s3c2412.c @@ -2,7 +2,7 @@ * * Driver for Samsung S3C2412 and S3C2413 SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/serial/s3c2440.c b/drivers/serial/s3c2440.c index 69ff5d340f04..094cc3904b13 100644 --- a/drivers/serial/s3c2440.c +++ b/drivers/serial/s3c2440.c @@ -2,7 +2,7 @@ * * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c index 26c49e18bdd1..fad6083ca427 100644 --- a/drivers/serial/s3c24a0.c +++ b/drivers/serial/s3c24a0.c @@ -6,7 +6,7 @@ * * Author: Sandeep Patil <sandeep.patil@azingo.com> * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index 1523e8d9ae77..52e3df113ec0 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -2,7 +2,7 @@ * * Driver core for Samsung SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h index d3fe315969f6..1fb22343df42 100644 --- a/drivers/serial/samsung.h +++ b/drivers/serial/samsung.h @@ -2,7 +2,7 @@ * * Driver for Samsung SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 6498bd1fb6dd..ff38dbdb5c6e 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -50,7 +50,6 @@ #include <linux/list.h> #ifdef CONFIG_SUPERH -#include <asm/clock.h> #include <asm/sh_bios.h> #endif @@ -79,22 +78,18 @@ struct sci_port { struct timer_list break_timer; int break_flag; -#ifdef CONFIG_HAVE_CLK /* Interface clock */ struct clk *iclk; /* Data clock */ struct clk *dclk; -#endif + struct list_head node; }; struct sh_sci_priv { spinlock_t lock; struct list_head ports; - -#ifdef CONFIG_HAVE_CLK struct notifier_block clk_nb; -#endif }; /* Function prototypes */ @@ -156,32 +151,6 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ -#if defined(__H8300S__) -enum { sci_disable, sci_enable }; - -static void h8300_sci_config(struct uart_port *port, unsigned int ctrl) -{ - volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL; - int ch = (port->mapbase - SMR0) >> 3; - unsigned char mask = 1 << (ch+1); - - if (ctrl == sci_disable) - *mstpcrl |= mask; - else - *mstpcrl &= ~mask; -} - -static void h8300_sci_enable(struct uart_port *port) -{ - h8300_sci_config(port, sci_enable); -} - -static void h8300_sci_disable(struct uart_port *port) -{ - h8300_sci_config(port, sci_disable); -} -#endif - #if defined(__H8300H__) || defined(__H8300S__) static void sci_init_pins(struct uart_port *port, unsigned int cflag) { @@ -733,7 +702,6 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) return ret; } -#ifdef CONFIG_HAVE_CLK /* * Here we define a transistion notifier so that we can update all of our * ports' baud rate when the peripheral clock changes. @@ -751,7 +719,6 @@ static int sci_notifier(struct notifier_block *self, spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(sci_port, &priv->ports, node) sci_port->port.uartclk = clk_get_rate(sci_port->dclk); - spin_unlock_irqrestore(&priv->lock, flags); } @@ -778,7 +745,6 @@ static void sci_clk_disable(struct uart_port *port) clk_disable(sci_port->dclk); } -#endif static int sci_request_irq(struct sci_port *port) { @@ -833,8 +799,8 @@ static void sci_free_irq(struct sci_port *port) static unsigned int sci_tx_empty(struct uart_port *port) { - /* Can't detect */ - return TIOCSER_TEMT; + unsigned short status = sci_in(port, SCxSR); + return status & SCxSR_TEND(port) ? TIOCSER_TEMT : 0; } static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -1077,21 +1043,10 @@ static void __devinit sci_init_single(struct platform_device *dev, sci_port->port.iotype = UPIO_MEM; sci_port->port.line = index; sci_port->port.fifosize = 1; - -#if defined(__H8300H__) || defined(__H8300S__) -#ifdef __H8300S__ - sci_port->enable = h8300_sci_enable; - sci_port->disable = h8300_sci_disable; -#endif - sci_port->port.uartclk = CONFIG_CPU_CLOCK; -#elif defined(CONFIG_HAVE_CLK) sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL; sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); sci_port->enable = sci_clk_enable; sci_port->disable = sci_clk_disable; -#else -#error "Need a valid uartclk" -#endif sci_port->break_timer.data = (unsigned long)sci_port; sci_port->break_timer.function = sci_break_timer; @@ -1106,7 +1061,6 @@ static void __devinit sci_init_single(struct platform_device *dev, sci_port->type = sci_port->port.type = p->type; memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); - } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE @@ -1239,14 +1193,11 @@ static int sci_remove(struct platform_device *dev) struct sci_port *p; unsigned long flags; -#ifdef CONFIG_HAVE_CLK cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); -#endif spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(p, &priv->ports, node) uart_remove_one_port(&sci_uart_driver, &p->port); - spin_unlock_irqrestore(&priv->lock, flags); kfree(priv); @@ -1307,10 +1258,8 @@ static int __devinit sci_probe(struct platform_device *dev) spin_lock_init(&priv->lock); platform_set_drvdata(dev, priv); -#ifdef CONFIG_HAVE_CLK priv->clk_nb.notifier_call = sci_notifier; cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); -#endif if (dev->id != -1) { ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]); @@ -1370,7 +1319,7 @@ static struct dev_pm_ops sci_dev_pm_ops = { static struct platform_driver sci_driver = { .probe = sci_probe, - .remove = __devexit_p(sci_remove), + .remove = sci_remove, .driver = { .name = "sh-sci", .owner = THIS_MODULE, diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 3e2fcf93b42e..a32094eeb42b 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -1,5 +1,5 @@ #include <linux/serial_core.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/gpio.h> #if defined(CONFIG_H83007) || defined(CONFIG_H83068) diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c index 0c08f286a2ef..46de564aaea0 100644 --- a/drivers/serial/ucc_uart.c +++ b/drivers/serial/ucc_uart.c @@ -313,7 +313,7 @@ static void qe_uart_stop_tx(struct uart_port *port) * This function will attempt to stuff of all the characters from the * kernel's transmit buffer into TX BDs. * - * A return value of non-zero indicates that it sucessfully stuffed all + * A return value of non-zero indicates that it successfully stuffed all * characters from the kernel buffer. * * A return value of zero indicates that there are still characters in the diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 6a025cefe6dc..4956bf1f2134 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_MAPLE) += maple/ +obj-$(CONFIG_GENERIC_GPIO) += pfc.o obj-y += intc.o diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 559b5fe9dc0f..a7e5c2e9986c 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -2,6 +2,7 @@ * Shared interrupt handling code for IPR and INTC2 types of IRQs. * * Copyright (C) 2007, 2008 Magnus Damm + * Copyright (C) 2009 Paul Mundt * * Based on intc2.c and ipr.c * @@ -24,6 +25,7 @@ #include <linux/sysdev.h> #include <linux/list.h> #include <linux/topology.h> +#include <linux/bitmap.h> #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ @@ -59,6 +61,20 @@ struct intc_desc_int { static LIST_HEAD(intc_list); +/* + * The intc_irq_map provides a global map of bound IRQ vectors for a + * given platform. Allocation of IRQs are either static through the CPU + * vector map, or dynamic in the case of board mux vectors or MSI. + * + * As this is a central point for all IRQ controllers on the system, + * each of the available sources are mapped out here. This combined with + * sparseirq makes it quite trivial to keep the vector map tightly packed + * when dynamically creating IRQs, as well as tying in to otherwise + * unused irq_desc positions in the sparse array. + */ +static DECLARE_BITMAP(intc_irq_map, NR_IRQS); +static DEFINE_SPINLOCK(vector_lock); + #ifdef CONFIG_SMP #define IS_SMP(x) x.smp #define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) @@ -70,9 +86,7 @@ static LIST_HEAD(intc_list); #endif static unsigned int intc_prio_level[NR_IRQS]; /* for now */ -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static unsigned long ack_handle[NR_IRQS]; -#endif static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { @@ -250,7 +264,6 @@ static int intc_set_wake(unsigned int irq, unsigned int on) return 0; /* allow wakeup, but setup hardware in intc_suspend() */ } -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static void intc_mask_ack(unsigned int irq) { struct intc_desc_int *d = get_intc_desc(irq); @@ -282,7 +295,6 @@ static void intc_mask_ack(unsigned int irq) } } } -#endif static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, unsigned int nr_hp, @@ -501,7 +513,6 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc, return 0; } -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static unsigned int __init intc_ack_data(struct intc_desc *desc, struct intc_desc_int *d, intc_enum enum_id) @@ -533,7 +544,6 @@ static unsigned int __init intc_ack_data(struct intc_desc *desc, return 0; } -#endif static unsigned int __init intc_sense_data(struct intc_desc *desc, struct intc_desc_int *d, @@ -572,6 +582,11 @@ static void __init intc_register_irq(struct intc_desc *desc, struct intc_handle_int *hp; unsigned int data[2], primary; + /* + * Register the IRQ position with the global IRQ map + */ + set_bit(irq, intc_irq_map); + /* Prefer single interrupt source bitmap over other combinations: * 1. bitmap, single interrupt source * 2. priority, single interrupt source @@ -641,10 +656,8 @@ static void __init intc_register_irq(struct intc_desc *desc, /* irq should be disabled by default */ d->chip.mask(irq); -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) if (desc->ack_regs) ack_handle[irq] = intc_ack_data(desc, d, enum_id); -#endif } static unsigned int __init save_reg(struct intc_desc_int *d, @@ -681,10 +694,8 @@ void __init register_intc_controller(struct intc_desc *desc) d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0; d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0; d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0; - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0; -#endif + d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); #ifdef CONFIG_SMP d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); @@ -727,14 +738,12 @@ void __init register_intc_controller(struct intc_desc *desc) d->chip.set_type = intc_set_sense; d->chip.set_wake = intc_set_wake; -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) if (desc->ack_regs) { for (i = 0; i < desc->nr_ack_regs; i++) k += save_reg(d, k, desc->ack_regs[i].set_reg, 0); d->chip.mask_ack = intc_mask_ack; } -#endif BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ @@ -856,5 +865,91 @@ static int __init register_intc_sysdevs(void) return error; } - device_initcall(register_intc_sysdevs); + +/* + * Dynamic IRQ allocation and deallocation + */ +static unsigned int create_irq_on_node(unsigned int irq_want, int node) +{ + unsigned int irq = 0, new; + unsigned long flags; + struct irq_desc *desc; + + spin_lock_irqsave(&vector_lock, flags); + + /* + * First try the wanted IRQ, then scan. + */ + if (test_and_set_bit(irq_want, intc_irq_map)) { + new = find_first_zero_bit(intc_irq_map, nr_irqs); + if (unlikely(new == nr_irqs)) + goto out_unlock; + + desc = irq_to_desc_alloc_node(new, node); + if (unlikely(!desc)) { + pr_info("can't get irq_desc for %d\n", new); + goto out_unlock; + } + + desc = move_irq_desc(desc, node); + __set_bit(new, intc_irq_map); + irq = new; + } + +out_unlock: + spin_unlock_irqrestore(&vector_lock, flags); + + if (irq > 0) + dynamic_irq_init(irq); + + return irq; +} + +int create_irq(void) +{ + int nid = cpu_to_node(smp_processor_id()); + int irq; + + irq = create_irq_on_node(NR_IRQS_LEGACY, nid); + if (irq == 0) + irq = -1; + + return irq; +} + +void destroy_irq(unsigned int irq) +{ + unsigned long flags; + + dynamic_irq_cleanup(irq); + + spin_lock_irqsave(&vector_lock, flags); + __clear_bit(irq, intc_irq_map); + spin_unlock_irqrestore(&vector_lock, flags); +} + +int reserve_irq_vector(unsigned int irq) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vector_lock, flags); + if (test_and_set_bit(irq, intc_irq_map)) + ret = -EBUSY; + spin_unlock_irqrestore(&vector_lock, flags); + + return ret; +} + +void reserve_irq_legacy(void) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&vector_lock, flags); + j = find_first_bit(intc_irq_map, nr_irqs); + for (i = 0; i < j; i++) + __set_bit(i, intc_irq_map); + spin_unlock_irqrestore(&vector_lock, flags); +} diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index 93c20e135ee1..4e8f57d4131f 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c @@ -106,7 +106,7 @@ static void maple_dma_reset(void) * max delay is 11 */ ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED); - ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); + ctrl_outl(virt_to_phys(maple_sendbuf), MAPLE_DMAADDR); ctrl_outl(1, MAPLE_ENABLE); } @@ -258,7 +258,7 @@ static void maple_build_block(struct mapleq *mq) maple_lastptr = maple_sendptr; *maple_sendptr++ = (port << 16) | len | 0x80000000; - *maple_sendptr++ = PHYSADDR(mq->recvbuf->buf); + *maple_sendptr++ = virt_to_phys(mq->recvbuf->buf); *maple_sendptr++ = mq->command | (to << 8) | (from << 16) | (len << 24); while (len-- > 0) diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c new file mode 100644 index 000000000000..841ed5030c8f --- /dev/null +++ b/drivers/sh/pfc.c @@ -0,0 +1,577 @@ +/* + * Pinmuxed GPIO support for SuperH. + * + * Copyright (C) 2008 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/bitops.h> +#include <linux/gpio.h> + +static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) +{ + if (enum_id < r->begin) + return 0; + + if (enum_id > r->end) + return 0; + + return 1; +} + +static unsigned long gpio_read_raw_reg(unsigned long reg, + unsigned long reg_width) +{ + switch (reg_width) { + case 8: + return __raw_readb(reg); + case 16: + return __raw_readw(reg); + case 32: + return __raw_readl(reg); + } + + BUG(); + return 0; +} + +static void gpio_write_raw_reg(unsigned long reg, + unsigned long reg_width, + unsigned long data) +{ + switch (reg_width) { + case 8: + __raw_writeb(data, reg); + return; + case 16: + __raw_writew(data, reg); + return; + case 32: + __raw_writel(data, reg); + return; + } + + BUG(); +} + +static void gpio_write_bit(struct pinmux_data_reg *dr, + unsigned long in_pos, unsigned long value) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + + pr_debug("write_bit addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld\n", + dr->reg, !!value, pos, dr->reg_width); + + if (value) + set_bit(pos, &dr->reg_shadow); + else + clear_bit(pos, &dr->reg_shadow); + + gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow); +} + +static int gpio_read_reg(unsigned long reg, unsigned long reg_width, + unsigned long field_width, unsigned long in_pos) +{ + unsigned long data, mask, pos; + + data = 0; + mask = (1 << field_width) - 1; + pos = reg_width - ((in_pos + 1) * field_width); + + pr_debug("read_reg: addr = %lx, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, pos, reg_width, field_width); + + data = gpio_read_raw_reg(reg, reg_width); + return (data >> pos) & mask; +} + +static void gpio_write_reg(unsigned long reg, unsigned long reg_width, + unsigned long field_width, unsigned long in_pos, + unsigned long value) +{ + unsigned long mask, pos; + + mask = (1 << field_width) - 1; + pos = reg_width - ((in_pos + 1) * field_width); + + pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, value, pos, reg_width, field_width); + + mask = ~(mask << pos); + value = value << pos; + + switch (reg_width) { + case 8: + __raw_writeb((__raw_readb(reg) & mask) | value, reg); + break; + case 16: + __raw_writew((__raw_readw(reg) & mask) | value, reg); + break; + case 32: + __raw_writel((__raw_readl(reg) & mask) | value, reg); + break; + } +} + +static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) +{ + struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_data_reg *data_reg; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + return -1; + + k = 0; + while (1) { + data_reg = gpioc->data_regs + k; + + if (!data_reg->reg_width) + break; + + for (n = 0; n < data_reg->reg_width; n++) { + if (data_reg->enum_ids[n] == gpiop->enum_id) { + gpiop->flags &= ~PINMUX_FLAG_DREG; + gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); + gpiop->flags &= ~PINMUX_FLAG_DBIT; + gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); + return 0; + } + } + k++; + } + + BUG(); + + return -1; +} + +static void setup_data_regs(struct pinmux_info *gpioc) +{ + struct pinmux_data_reg *drp; + int k; + + for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) + setup_data_reg(gpioc, k); + + k = 0; + while (1) { + drp = gpioc->data_regs + k; + + if (!drp->reg_width) + break; + + drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width); + k++; + } +} + +static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, + struct pinmux_data_reg **drp, int *bitp) +{ + struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + return -1; + + k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; + n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; + *drp = gpioc->data_regs + k; + *bitp = n; + return 0; +} + +static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, + struct pinmux_cfg_reg **crp, int *indexp, + unsigned long **cntp) +{ + struct pinmux_cfg_reg *config_reg; + unsigned long r_width, f_width; + int k, n; + + k = 0; + while (1) { + config_reg = gpioc->cfg_regs + k; + + r_width = config_reg->reg_width; + f_width = config_reg->field_width; + + if (!r_width) + break; + for (n = 0; n < (r_width / f_width) * 1 << f_width; n++) { + if (config_reg->enum_ids[n] == enum_id) { + *crp = config_reg; + *indexp = n; + *cntp = &config_reg->cnt[n / (1 << f_width)]; + return 0; + } + } + k++; + } + + return -1; +} + +static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, + int pos, pinmux_enum_t *enum_idp) +{ + pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; + pinmux_enum_t *data = gpioc->gpio_data; + int k; + + if (!enum_in_range(enum_id, &gpioc->data)) { + if (!enum_in_range(enum_id, &gpioc->mark)) { + pr_err("non data/mark enum_id for gpio %d\n", gpio); + return -1; + } + } + + if (pos) { + *enum_idp = data[pos + 1]; + return pos + 1; + } + + for (k = 0; k < gpioc->gpio_data_size; k++) { + if (data[k] == enum_id) { + *enum_idp = data[k + 1]; + return k + 1; + } + } + + pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); + return -1; +} + +static void write_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + int index) +{ + unsigned long ncomb, pos, value; + + ncomb = 1 << crp->field_width; + pos = index / ncomb; + value = index % ncomb; + + gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value); +} + +static int check_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + int index) +{ + unsigned long ncomb, pos, value; + + ncomb = 1 << crp->field_width; + pos = index / ncomb; + value = index % ncomb; + + if (gpio_read_reg(crp->reg, crp->reg_width, + crp->field_width, pos) == value) + return 0; + + return -1; +} + +enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; + +static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, + int pinmux_type, int cfg_mode) +{ + struct pinmux_cfg_reg *cr = NULL; + pinmux_enum_t enum_id; + struct pinmux_range *range; + int in_range, pos, index; + unsigned long *cntp; + + switch (pinmux_type) { + + case PINMUX_TYPE_FUNCTION: + range = NULL; + break; + + case PINMUX_TYPE_OUTPUT: + range = &gpioc->output; + break; + + case PINMUX_TYPE_INPUT: + range = &gpioc->input; + break; + + case PINMUX_TYPE_INPUT_PULLUP: + range = &gpioc->input_pu; + break; + + case PINMUX_TYPE_INPUT_PULLDOWN: + range = &gpioc->input_pd; + break; + + default: + goto out_err; + } + + pos = 0; + enum_id = 0; + index = 0; + while (1) { + pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); + if (pos <= 0) + goto out_err; + + if (!enum_id) + break; + + in_range = enum_in_range(enum_id, &gpioc->function); + if (!in_range && range) { + in_range = enum_in_range(enum_id, range); + + if (in_range && enum_id == range->force) + continue; + } + + if (!in_range) + continue; + + if (get_config_reg(gpioc, enum_id, &cr, &index, &cntp) != 0) + goto out_err; + + switch (cfg_mode) { + case GPIO_CFG_DRYRUN: + if (!*cntp || !check_config_reg(gpioc, cr, index)) + continue; + break; + + case GPIO_CFG_REQ: + write_config_reg(gpioc, cr, index); + *cntp = *cntp + 1; + break; + + case GPIO_CFG_FREE: + *cntp = *cntp - 1; + break; + } + } + + return 0; + out_err: + return -1; +} + +static DEFINE_SPINLOCK(gpio_lock); + +static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip) +{ + return container_of(chip, struct pinmux_info, chip); +} + +static int sh_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + if (!gpioc) + goto err_out; + + spin_lock_irqsave(&gpio_lock, flags); + + if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_unlock; + + /* setup pin function here if no data is associated with pin */ + + if (get_data_reg(gpioc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (pinmux_config_gpio(gpioc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_unlock; + + if (pinmux_config_gpio(gpioc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[offset].flags |= pinmux_type; + + ret = 0; + err_unlock: + spin_unlock_irqrestore(&gpio_lock, flags); + err_out: + return ret; +} + +static void sh_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int pinmux_type; + + if (!gpioc) + return; + + spin_lock_irqsave(&gpio_lock, flags); + + pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE; + pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE); + gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static int pinmux_direction(struct pinmux_info *gpioc, + unsigned gpio, int new_pinmux_type) +{ + int pinmux_type; + int ret = -EINVAL; + + if (!gpioc) + goto err_out; + + pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err_out; + } + + if (pinmux_config_gpio(gpioc, gpio, + new_pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_out; + + if (pinmux_config_gpio(gpioc, gpio, + new_pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + + gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[gpio].flags |= new_pinmux_type; + + ret = 0; + err_out: + return ret; +} + +static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int ret; + + spin_lock_irqsave(&gpio_lock, flags); + ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT); + spin_unlock_irqrestore(&gpio_lock, flags); + + return ret; +} + +static void sh_gpio_set_value(struct pinmux_info *gpioc, + unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) + BUG(); + else + gpio_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int ret; + + sh_gpio_set_value(gpioc, offset, value); + spin_lock_irqsave(&gpio_lock, flags); + ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); + spin_unlock_irqrestore(&gpio_lock, flags); + + return ret; +} + +static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) { + BUG(); + return 0; + } + + return gpio_read_reg(dr->reg, dr->reg_width, 1, bit); +} + +static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + return sh_gpio_get_value(chip_to_pinmux(chip), offset); +} + +static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + sh_gpio_set_value(chip_to_pinmux(chip), offset, value); +} + +int register_pinmux(struct pinmux_info *pip) +{ + struct gpio_chip *chip = &pip->chip; + + pr_info("sh pinmux: %s handling gpio %d -> %d\n", + pip->name, pip->first_gpio, pip->last_gpio); + + setup_data_regs(pip); + + chip->request = sh_gpio_request; + chip->free = sh_gpio_free; + chip->direction_input = sh_gpio_direction_input; + chip->get = sh_gpio_get; + chip->direction_output = sh_gpio_direction_output; + chip->set = sh_gpio_set; + + WARN_ON(pip->first_gpio != 0); /* needs testing */ + + chip->label = pip->name; + chip->owner = THIS_MODULE; + chip->base = pip->first_gpio; + chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; + + return gpiochip_add(chip); +} diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index ba1a872b221e..bf5f95a19413 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -35,8 +35,8 @@ #include <linux/spi/spi.h> -#include <mach/dma.h> -#include <mach/clock.h> +#include <plat/dma.h> +#include <plat/clock.h> #define OMAP2_MCSPI_MAX_FREQ 48000000 diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/omap_uwire.c index e75ba9b28898..6c3a8557db27 100644 --- a/drivers/spi/omap_uwire.c +++ b/drivers/spi/omap_uwire.c @@ -51,8 +51,8 @@ #include <asm/io.h> #include <asm/mach-types.h> -#include <mach/mux.h> -#include <mach/omap730.h> /* OMAP730_IO_CONF registers */ +#include <plat/mux.h> +#include <plat/omap7xx.h> /* OMAP7XX_IO_CONF registers */ /* FIXME address is now a platform device resource, @@ -504,7 +504,7 @@ static int __init uwire_probe(struct platform_device *pdev) } clk_enable(uwire->ck); - if (cpu_is_omap730()) + if (cpu_is_omap7xx()) uwire_idx_shift = 1; else uwire_idx_shift = 2; @@ -573,8 +573,8 @@ static int __init omap_uwire_init(void) } if (machine_is_omap_perseus2()) { /* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */ - int val = omap_readl(OMAP730_IO_CONF_9) & ~0x00EEE000; - omap_writel(val | 0x00AAA000, OMAP730_IO_CONF_9); + int val = omap_readl(OMAP7XX_IO_CONF_9) & ~0x00EEE000; + omap_writel(val | 0x00AAA000, OMAP7XX_IO_CONF_9); } return platform_driver_probe(&uwire_driver, uwire_probe); diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 5d23983f02fc..20d7322e2f71 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -30,7 +30,6 @@ #include <linux/errno.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/spi/spi.h> #include <linux/spi/spidev.h> @@ -42,7 +41,7 @@ * This supports acccess to SPI devices using normal userspace I/O calls. * Note that while traditional UNIX/POSIX I/O semantics are half duplex, * and often mask message boundaries, full SPI support requires full duplex - * transfers. There are several kinds of of internal message boundaries to + * transfers. There are several kinds of internal message boundaries to * handle chipselect management and other protocol options. * * SPI has a character major number assigned. We allocate minor numbers @@ -477,7 +476,6 @@ static int spidev_open(struct inode *inode, struct file *filp) struct spidev_data *spidev; int status = -ENXIO; - lock_kernel(); mutex_lock(&device_list_lock); list_for_each_entry(spidev, &device_list, device_entry) { @@ -503,7 +501,6 @@ static int spidev_open(struct inode *inode, struct file *filp) pr_debug("spidev: nothing for minor %d\n", iminor(inode)); mutex_unlock(&device_list_lock); - unlock_kernel(); return status; } diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c index 538c570df337..f1dcd7969a5c 100644 --- a/drivers/ssb/driver_pcicore.c +++ b/drivers/ssb/driver_pcicore.c @@ -551,13 +551,13 @@ int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, might_sleep_if(pdev->id.coreid != SSB_DEV_PCI); /* Enable interrupts for this device. */ - if (bus->host_pci && - ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE))) { + if ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE)) { u32 coremask; /* Calculate the "coremask" for the device. */ coremask = (1 << dev->core_index); + SSB_WARN_ON(bus->bustype != SSB_BUSTYPE_PCI); err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp); if (err) goto out; diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 579b114be412..5681ebed9c65 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -140,6 +140,19 @@ static void ssb_device_put(struct ssb_device *dev) put_device(dev->dev); } +static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv) +{ + if (drv) + get_driver(&drv->drv); + return drv; +} + +static inline void ssb_driver_put(struct ssb_driver *drv) +{ + if (drv) + put_driver(&drv->drv); +} + static int ssb_device_resume(struct device *dev) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); @@ -210,90 +223,81 @@ int ssb_bus_suspend(struct ssb_bus *bus) EXPORT_SYMBOL(ssb_bus_suspend); #ifdef CONFIG_SSB_SPROM -int ssb_devices_freeze(struct ssb_bus *bus) +/** ssb_devices_freeze - Freeze all devices on the bus. + * + * After freezing no device driver will be handling a device + * on this bus anymore. ssb_devices_thaw() must be called after + * a successful freeze to reactivate the devices. + * + * @bus: The bus. + * @ctx: Context structure. Pass this to ssb_devices_thaw(). + */ +int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx) { - struct ssb_device *dev; - struct ssb_driver *drv; - int err = 0; - int i; - pm_message_t state = PMSG_FREEZE; + struct ssb_device *sdev; + struct ssb_driver *sdrv; + unsigned int i; + + memset(ctx, 0, sizeof(*ctx)); + ctx->bus = bus; + SSB_WARN_ON(bus->nr_devices > ARRAY_SIZE(ctx->device_frozen)); - /* First check that we are capable to freeze all devices. */ for (i = 0; i < bus->nr_devices; i++) { - dev = &(bus->devices[i]); - if (!dev->dev || - !dev->dev->driver || - !device_is_registered(dev->dev)) - continue; - drv = drv_to_ssb_drv(dev->dev->driver); - if (!drv) + sdev = ssb_device_get(&bus->devices[i]); + + if (!sdev->dev || !sdev->dev->driver || + !device_is_registered(sdev->dev)) { + ssb_device_put(sdev); continue; - if (!drv->suspend) { - /* Nope, can't suspend this one. */ - return -EOPNOTSUPP; } - } - /* Now suspend all devices */ - for (i = 0; i < bus->nr_devices; i++) { - dev = &(bus->devices[i]); - if (!dev->dev || - !dev->dev->driver || - !device_is_registered(dev->dev)) - continue; - drv = drv_to_ssb_drv(dev->dev->driver); - if (!drv) + sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver)); + if (!sdrv || SSB_WARN_ON(!sdrv->remove)) { + ssb_device_put(sdev); continue; - err = drv->suspend(dev, state); - if (err) { - ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n", - dev_name(dev->dev)); - goto err_unwind; } + sdrv->remove(sdev); + ctx->device_frozen[i] = 1; } return 0; -err_unwind: - for (i--; i >= 0; i--) { - dev = &(bus->devices[i]); - if (!dev->dev || - !dev->dev->driver || - !device_is_registered(dev->dev)) - continue; - drv = drv_to_ssb_drv(dev->dev->driver); - if (!drv) - continue; - if (drv->resume) - drv->resume(dev); - } - return err; } -int ssb_devices_thaw(struct ssb_bus *bus) +/** ssb_devices_thaw - Unfreeze all devices on the bus. + * + * This will re-attach the device drivers and re-init the devices. + * + * @ctx: The context structure from ssb_devices_freeze() + */ +int ssb_devices_thaw(struct ssb_freeze_context *ctx) { - struct ssb_device *dev; - struct ssb_driver *drv; - int err; - int i; + struct ssb_bus *bus = ctx->bus; + struct ssb_device *sdev; + struct ssb_driver *sdrv; + unsigned int i; + int err, result = 0; for (i = 0; i < bus->nr_devices; i++) { - dev = &(bus->devices[i]); - if (!dev->dev || - !dev->dev->driver || - !device_is_registered(dev->dev)) + if (!ctx->device_frozen[i]) continue; - drv = drv_to_ssb_drv(dev->dev->driver); - if (!drv) + sdev = &bus->devices[i]; + + if (SSB_WARN_ON(!sdev->dev || !sdev->dev->driver)) continue; - if (SSB_WARN_ON(!drv->resume)) + sdrv = drv_to_ssb_drv(sdev->dev->driver); + if (SSB_WARN_ON(!sdrv || !sdrv->probe)) continue; - err = drv->resume(dev); + + err = sdrv->probe(sdev, &sdev->id); if (err) { ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n", - dev_name(dev->dev)); + dev_name(sdev->dev)); + result = err; } + ssb_driver_put(sdrv); + ssb_device_put(sdev); } - return 0; + return result; } #endif /* CONFIG_SSB_SPROM */ diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c index e8b89e8ac9bd..0d6c0280eb34 100644 --- a/drivers/ssb/scan.c +++ b/drivers/ssb/scan.c @@ -354,7 +354,7 @@ int ssb_bus_scan(struct ssb_bus *bus, dev->bus = bus; dev->ops = bus->ops; - ssb_dprintk(KERN_INFO PFX + printk(KERN_DEBUG PFX "Core %d found: %s " "(cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n", i, ssb_core_name(dev->id.coreid), diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c index 8943015a3eef..d0e6762fec50 100644 --- a/drivers/ssb/sprom.c +++ b/drivers/ssb/sprom.c @@ -13,6 +13,8 @@ #include "ssb_private.h" +#include <linux/ctype.h> + static const struct ssb_sprom *fallback_sprom; @@ -33,17 +35,27 @@ static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, static int hex2sprom(u16 *sprom, const char *dump, size_t len, size_t sprom_size_words) { - char tmp[5] = { 0 }; - int cnt = 0; + char c, tmp[5] = { 0 }; + int err, cnt = 0; unsigned long parsed; - if (len < sprom_size_words * 2) + /* Strip whitespace at the end. */ + while (len) { + c = dump[len - 1]; + if (!isspace(c) && c != '\0') + break; + len--; + } + /* Length must match exactly. */ + if (len != sprom_size_words * 4) return -EINVAL; while (cnt < sprom_size_words) { memcpy(tmp, dump, 4); dump += 4; - parsed = simple_strtoul(tmp, NULL, 16); + err = strict_strtoul(tmp, 16, &parsed); + if (err) + return err; sprom[cnt++] = swab16((u16)parsed); } @@ -90,6 +102,7 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, u16 *sprom; int res = 0, err = -ENOMEM; size_t sprom_size_words = bus->sprom_size; + struct ssb_freeze_context freeze; sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); if (!sprom) @@ -111,18 +124,13 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, err = -ERESTARTSYS; if (mutex_lock_interruptible(&bus->sprom_mutex)) goto out_kfree; - err = ssb_devices_freeze(bus); - if (err == -EOPNOTSUPP) { - ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " - "No suspend support. Is CONFIG_PM enabled?\n"); - goto out_unlock; - } + err = ssb_devices_freeze(bus, &freeze); if (err) { ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); goto out_unlock; } res = sprom_write(bus, sprom); - err = ssb_devices_thaw(bus); + err = ssb_devices_thaw(&freeze); if (err) ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); out_unlock: diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 25433565dfda..56054be4d113 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -176,13 +176,21 @@ extern const struct ssb_sprom *ssb_get_fallback_sprom(void); /* core.c */ extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); -extern int ssb_devices_freeze(struct ssb_bus *bus); -extern int ssb_devices_thaw(struct ssb_bus *bus); extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); int ssb_for_each_bus_call(unsigned long data, int (*func)(struct ssb_bus *bus, unsigned long data)); extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev); +struct ssb_freeze_context { + /* Pointer to the bus */ + struct ssb_bus *bus; + /* Boolean list to indicate whether a device is frozen on this bus. */ + bool device_frozen[SSB_MAX_NR_CORES]; +}; +extern int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx); +extern int ssb_devices_thaw(struct ssb_freeze_context *ctx); + + /* b43_pci_bridge.c */ #ifdef CONFIG_SSB_B43_PCI_BRIDGE diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index d21b3469f6d7..dfcd75cf4907 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -125,5 +125,13 @@ source "drivers/staging/sep/Kconfig" source "drivers/staging/iio/Kconfig" +source "drivers/staging/strip/Kconfig" + +source "drivers/staging/arlan/Kconfig" + +source "drivers/staging/wavelan/Kconfig" + +source "drivers/staging/netwave/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 8cbf1aebea2e..7719d04a4a86 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -44,3 +44,8 @@ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_RAR_REGISTER) += rar/ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ +obj-$(CONFIG_STRIP) += strip/ +obj-$(CONFIG_ARLAN) += arlan/ +obj-$(CONFIG_WAVELAN) += wavelan/ +obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan/ +obj-$(CONFIG_PCMCIA_NETWAVE) += netwave/ diff --git a/drivers/staging/arlan/Kconfig b/drivers/staging/arlan/Kconfig new file mode 100644 index 000000000000..5e42b81f97b0 --- /dev/null +++ b/drivers/staging/arlan/Kconfig @@ -0,0 +1,15 @@ +config ARLAN + tristate "Aironet Arlan 655 & IC2200 DS support" + depends on ISA && !64BIT && WLAN + select WIRELESS_EXT + ---help--- + Aironet makes Arlan, a class of wireless LAN adapters. These use the + www.Telxon.com chip, which is also used on several similar cards. + This driver is tested on the 655 and IC2200 series cards. Look at + <http://www.ylenurme.ee/~elmer/655/> for the latest information. + + The driver is built as two modules, arlan and arlan-proc. The latter + is the /proc interface and is not needed most of time. + + On some computers the card ends up in non-valid state after some + time. Use a ping-reset script to clear it. diff --git a/drivers/staging/arlan/Makefile b/drivers/staging/arlan/Makefile new file mode 100644 index 000000000000..9e58e5fae7b9 --- /dev/null +++ b/drivers/staging/arlan/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_ARLAN) += arlan.o + +arlan-objs := arlan-main.o arlan-proc.o diff --git a/drivers/staging/arlan/TODO b/drivers/staging/arlan/TODO new file mode 100644 index 000000000000..9bd15a2f6d9e --- /dev/null +++ b/drivers/staging/arlan/TODO @@ -0,0 +1,7 @@ +TODO: + - step up and maintain this driver to ensure that it continues + to work. Having the hardware for this is pretty much a + requirement. If this does not happen, the will be removed in + the 2.6.35 kernel release. + +Please send patches to Greg Kroah-Hartman <greg@kroah.com>. diff --git a/drivers/net/wireless/arlan-main.c b/drivers/staging/arlan/arlan-main.c index 921a082487a1..921a082487a1 100644 --- a/drivers/net/wireless/arlan-main.c +++ b/drivers/staging/arlan/arlan-main.c diff --git a/drivers/net/wireless/arlan-proc.c b/drivers/staging/arlan/arlan-proc.c index a8b689635a3b..b22983e6c0cf 100644 --- a/drivers/net/wireless/arlan-proc.c +++ b/drivers/staging/arlan/arlan-proc.c @@ -816,84 +816,83 @@ static int arlan_sysctl_reset(ctl_table * ctl, int write, /* Place files in /proc/sys/dev/arlan */ -#define CTBLN(num,card,nam) \ - { .ctl_name = num,\ - .procname = #nam,\ +#define CTBLN(card,nam) \ + { .procname = #nam,\ .data = &(arlan_conf[card].nam),\ - .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec} + .maxlen = sizeof(int), .mode = 0600, .proc_handler = proc_dointvec} #ifdef ARLAN_DEBUGGING #define ARLAN_PROC_DEBUG_ENTRIES \ - { .ctl_name = 48, .procname = "entry_exit_debug",\ + { .procname = "entry_exit_debug",\ .data = &arlan_entry_and_exit_debug,\ - .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec},\ - { .ctl_name = 49, .procname = "debug", .data = &arlan_debug,\ - .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec}, + .maxlen = sizeof(int), .mode = 0600, .proc_handler = proc_dointvec},\ + { .procname = "debug", .data = &arlan_debug,\ + .maxlen = sizeof(int), .mode = 0600, .proc_handler = proc_dointvec}, #else #define ARLAN_PROC_DEBUG_ENTRIES #endif #define ARLAN_SYSCTL_TABLE_TOTAL(cardNo)\ - CTBLN(1,cardNo,spreadingCode),\ - CTBLN(2,cardNo, channelNumber),\ - CTBLN(3,cardNo, scramblingDisable),\ - CTBLN(4,cardNo, txAttenuation),\ - CTBLN(5,cardNo, systemId), \ - CTBLN(6,cardNo, maxDatagramSize),\ - CTBLN(7,cardNo, maxFrameSize),\ - CTBLN(8,cardNo, maxRetries),\ - CTBLN(9,cardNo, receiveMode),\ - CTBLN(10,cardNo, priority),\ - CTBLN(11,cardNo, rootOrRepeater),\ - CTBLN(12,cardNo, SID),\ - CTBLN(13,cardNo, registrationMode),\ - CTBLN(14,cardNo, registrationFill),\ - CTBLN(15,cardNo, localTalkAddress),\ - CTBLN(16,cardNo, codeFormat),\ - CTBLN(17,cardNo, numChannels),\ - CTBLN(18,cardNo, channel1),\ - CTBLN(19,cardNo, channel2),\ - CTBLN(20,cardNo, channel3),\ - CTBLN(21,cardNo, channel4),\ - CTBLN(22,cardNo, txClear),\ - CTBLN(23,cardNo, txRetries),\ - CTBLN(24,cardNo, txRouting),\ - CTBLN(25,cardNo, txScrambled),\ - CTBLN(26,cardNo, rxParameter),\ - CTBLN(27,cardNo, txTimeoutMs),\ - CTBLN(28,cardNo, waitCardTimeout),\ - CTBLN(29,cardNo, channelSet), \ - {.ctl_name = 30, .procname = "name",\ + CTBLN(cardNo,spreadingCode),\ + CTBLN(cardNo, channelNumber),\ + CTBLN(cardNo, scramblingDisable),\ + CTBLN(cardNo, txAttenuation),\ + CTBLN(cardNo, systemId), \ + CTBLN(cardNo, maxDatagramSize),\ + CTBLN(cardNo, maxFrameSize),\ + CTBLN(cardNo, maxRetries),\ + CTBLN(cardNo, receiveMode),\ + CTBLN(cardNo, priority),\ + CTBLN(cardNo, rootOrRepeater),\ + CTBLN(cardNo, SID),\ + CTBLN(cardNo, registrationMode),\ + CTBLN(cardNo, registrationFill),\ + CTBLN(cardNo, localTalkAddress),\ + CTBLN(cardNo, codeFormat),\ + CTBLN(cardNo, numChannels),\ + CTBLN(cardNo, channel1),\ + CTBLN(cardNo, channel2),\ + CTBLN(cardNo, channel3),\ + CTBLN(cardNo, channel4),\ + CTBLN(cardNo, txClear),\ + CTBLN(cardNo, txRetries),\ + CTBLN(cardNo, txRouting),\ + CTBLN(cardNo, txScrambled),\ + CTBLN(cardNo, rxParameter),\ + CTBLN(cardNo, txTimeoutMs),\ + CTBLN(cardNo, waitCardTimeout),\ + CTBLN(cardNo, channelSet), \ + { .procname = "name",\ .data = arlan_conf[cardNo].siteName,\ - .maxlen = 16, .mode = 0600, .proc_handler = &proc_dostring},\ - CTBLN(31,cardNo,waitTime),\ - CTBLN(32,cardNo,lParameter),\ - CTBLN(33,cardNo,_15),\ - CTBLN(34,cardNo,headerSize),\ - CTBLN(36,cardNo,tx_delay_ms),\ - CTBLN(37,cardNo,retries),\ - CTBLN(38,cardNo,ReTransmitPacketMaxSize),\ - CTBLN(39,cardNo,waitReTransmitPacketMaxSize),\ - CTBLN(40,cardNo,fastReTransCount),\ - CTBLN(41,cardNo,driverRetransmissions),\ - CTBLN(42,cardNo,txAckTimeoutMs),\ - CTBLN(43,cardNo,registrationInterrupts),\ - CTBLN(44,cardNo,hardwareType),\ - CTBLN(45,cardNo,radioType),\ - CTBLN(46,cardNo,writeEEPROM),\ - CTBLN(47,cardNo,writeRadioType),\ + .maxlen = 16, .mode = 0600, .proc_handler = proc_dostring},\ + CTBLN(cardNo,waitTime),\ + CTBLN(cardNo,lParameter),\ + CTBLN(cardNo,_15),\ + CTBLN(cardNo,headerSize),\ + CTBLN(cardNo,tx_delay_ms),\ + CTBLN(cardNo,retries),\ + CTBLN(cardNo,ReTransmitPacketMaxSize),\ + CTBLN(cardNo,waitReTransmitPacketMaxSize),\ + CTBLN(cardNo,fastReTransCount),\ + CTBLN(cardNo,driverRetransmissions),\ + CTBLN(cardNo,txAckTimeoutMs),\ + CTBLN(cardNo,registrationInterrupts),\ + CTBLN(cardNo,hardwareType),\ + CTBLN(cardNo,radioType),\ + CTBLN(cardNo,writeEEPROM),\ + CTBLN(cardNo,writeRadioType),\ ARLAN_PROC_DEBUG_ENTRIES\ - CTBLN(50,cardNo,in_speed),\ - CTBLN(51,cardNo,out_speed),\ - CTBLN(52,cardNo,in_speed10),\ - CTBLN(53,cardNo,out_speed10),\ - CTBLN(54,cardNo,in_speed_max),\ - CTBLN(55,cardNo,out_speed_max),\ - CTBLN(56,cardNo,measure_rate),\ - CTBLN(57,cardNo,pre_Command_Wait),\ - CTBLN(58,cardNo,rx_tweak1),\ - CTBLN(59,cardNo,rx_tweak2),\ - CTBLN(60,cardNo,tx_queue_len),\ + CTBLN(cardNo,in_speed),\ + CTBLN(cardNo,out_speed),\ + CTBLN(cardNo,in_speed10),\ + CTBLN(cardNo,out_speed10),\ + CTBLN(cardNo,in_speed_max),\ + CTBLN(cardNo,out_speed_max),\ + CTBLN(cardNo,measure_rate),\ + CTBLN(cardNo,pre_Command_Wait),\ + CTBLN(cardNo,rx_tweak1),\ + CTBLN(cardNo,rx_tweak2),\ + CTBLN(cardNo,tx_queue_len),\ @@ -903,63 +902,56 @@ static ctl_table arlan_conf_table0[] = #ifdef ARLAN_PROC_SHM_DUMP { - .ctl_name = 150, .procname = "arlan0-txRing", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_infotxRing, + .proc_handler = arlan_sysctl_infotxRing, }, { - .ctl_name = 151, .procname = "arlan0-rxRing", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_inforxRing, + .proc_handler = arlan_sysctl_inforxRing, }, { - .ctl_name = 152, .procname = "arlan0-18", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info18, + .proc_handler = arlan_sysctl_info18, }, { - .ctl_name = 153, .procname = "arlan0-ring", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info161719, + .proc_handler = arlan_sysctl_info161719, }, { - .ctl_name = 154, .procname = "arlan0-shm-cpy", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info, + .proc_handler = arlan_sysctl_info, }, #endif { - .ctl_name = 155, .procname = "config0", .data = &conf_reset_result, .maxlen = 100, .mode = 0400, - .proc_handler = &arlan_configure + .proc_handler = arlan_configure }, { - .ctl_name = 156, .procname = "reset0", .data = &conf_reset_result, .maxlen = 100, .mode = 0400, - .proc_handler = &arlan_sysctl_reset, + .proc_handler = arlan_sysctl_reset, }, - { .ctl_name = 0 } + { } }; static ctl_table arlan_conf_table1[] = @@ -969,63 +961,56 @@ static ctl_table arlan_conf_table1[] = #ifdef ARLAN_PROC_SHM_DUMP { - .ctl_name = 150, .procname = "arlan1-txRing", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_infotxRing, + .proc_handler = arlan_sysctl_infotxRing, }, { - .ctl_name = 151, .procname = "arlan1-rxRing", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_inforxRing, + .proc_handler = arlan_sysctl_inforxRing, }, { - .ctl_name = 152, .procname = "arlan1-18", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info18, + .proc_handler = arlan_sysctl_info18, }, { - .ctl_name = 153, .procname = "arlan1-ring", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info161719, + .proc_handler = arlan_sysctl_info161719, }, { - .ctl_name = 154, .procname = "arlan1-shm-cpy", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info, + .proc_handler = arlan_sysctl_info, }, #endif { - .ctl_name = 155, .procname = "config1", .data = &conf_reset_result, .maxlen = 100, .mode = 0400, - .proc_handler = &arlan_configure, + .proc_handler = arlan_configure, }, { - .ctl_name = 156, .procname = "reset1", .data = &conf_reset_result, .maxlen = 100, .mode = 0400, - .proc_handler = &arlan_sysctl_reset, + .proc_handler = arlan_sysctl_reset, }, - { .ctl_name = 0 } + { } }; static ctl_table arlan_conf_table2[] = @@ -1035,63 +1020,56 @@ static ctl_table arlan_conf_table2[] = #ifdef ARLAN_PROC_SHM_DUMP { - .ctl_name = 150, .procname = "arlan2-txRing", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_infotxRing, + .proc_handler = arlan_sysctl_infotxRing, }, { - .ctl_name = 151, .procname = "arlan2-rxRing", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_inforxRing, + .proc_handler = arlan_sysctl_inforxRing, }, { - .ctl_name = 152, .procname = "arlan2-18", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info18, + .proc_handler = arlan_sysctl_info18, }, { - .ctl_name = 153, .procname = "arlan2-ring", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info161719, + .proc_handler = arlan_sysctl_info161719, }, { - .ctl_name = 154, .procname = "arlan2-shm-cpy", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info, + .proc_handler = arlan_sysctl_info, }, #endif { - .ctl_name = 155, .procname = "config2", .data = &conf_reset_result, .maxlen = 100, .mode = 0400, - .proc_handler = &arlan_configure, + .proc_handler = arlan_configure, }, { - .ctl_name = 156, .procname = "reset2", .data = &conf_reset_result, .maxlen = 100, .mode = 0400, - .proc_handler = &arlan_sysctl_reset, + .proc_handler = arlan_sysctl_reset, }, - { .ctl_name = 0 } + { } }; static ctl_table arlan_conf_table3[] = @@ -1101,63 +1079,56 @@ static ctl_table arlan_conf_table3[] = #ifdef ARLAN_PROC_SHM_DUMP { - .ctl_name = 150, .procname = "arlan3-txRing", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_infotxRing, + .proc_handler = arlan_sysctl_infotxRing, }, { - .ctl_name = 151, .procname = "arlan3-rxRing", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_inforxRing, + .proc_handler = arlan_sysctl_inforxRing, }, { - .ctl_name = 152, .procname = "arlan3-18", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info18, + .proc_handler = arlan_sysctl_info18, }, { - .ctl_name = 153, .procname = "arlan3-ring", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info161719, + .proc_handler = arlan_sysctl_info161719, }, { - .ctl_name = 154, .procname = "arlan3-shm-cpy", .data = &arlan_drive_info, .maxlen = ARLAN_STR_SIZE, .mode = 0400, - .proc_handler = &arlan_sysctl_info, + .proc_handler = arlan_sysctl_info, }, #endif { - .ctl_name = 155, .procname = "config3", .data = &conf_reset_result, .maxlen = 100, .mode = 0400, - .proc_handler = &arlan_configure, + .proc_handler = arlan_configure, }, { - .ctl_name = 156, .procname = "reset3", .data = &conf_reset_result, .maxlen = 100, .mode = 0400, - .proc_handler = &arlan_sysctl_reset, + .proc_handler = arlan_sysctl_reset, }, - { .ctl_name = 0 } + { } }; @@ -1165,41 +1136,37 @@ static ctl_table arlan_conf_table3[] = static ctl_table arlan_table[] = { { - .ctl_name = 0, .procname = "arlan0", .maxlen = 0, .mode = 0600, .child = arlan_conf_table0, }, { - .ctl_name = 0, .procname = "arlan1", .maxlen = 0, .mode = 0600, .child = arlan_conf_table1, }, { - .ctl_name = 0, .procname = "arlan2", .maxlen = 0, .mode = 0600, .child = arlan_conf_table2, }, { - .ctl_name = 0, .procname = "arlan3", .maxlen = 0, .mode = 0600, .child = arlan_conf_table3, }, - { .ctl_name = 0 } + { } }; #else -static ctl_table arlan_table[MAX_ARLANS + 1] = +static ctl_table arlan_table[] = { - { .ctl_name = 0 } + { } }; #endif @@ -1209,22 +1176,14 @@ static ctl_table arlan_table[MAX_ARLANS + 1] = static ctl_table arlan_root_table[] = { { - .ctl_name = CTL_ARLAN, .procname = "arlan", .maxlen = 0, .mode = 0555, .child = arlan_table, }, - { .ctl_name = 0 } + { } }; -/* Make sure that /proc/sys/dev is there */ -//static ctl_table arlan_device_root_table[] = -//{ -// {CTL_DEV, "dev", NULL, 0, 0555, arlan_root_table}, -// {0} -//}; - static struct ctl_table_header *arlan_device_sysctl_header; @@ -1234,8 +1193,6 @@ int __init init_arlan_proc(void) int i = 0; if (arlan_device_sysctl_header) return 0; - for (i = 0; i < MAX_ARLANS && arlan_device[i]; i++) - arlan_table[i].ctl_name = i + 1; arlan_device_sysctl_header = register_sysctl_table(arlan_root_table); if (!arlan_device_sysctl_header) return -1; diff --git a/drivers/net/wireless/arlan.h b/drivers/staging/arlan/arlan.h index fb3ad51a1caf..fb3ad51a1caf 100644 --- a/drivers/net/wireless/arlan.h +++ b/drivers/staging/arlan/arlan.h diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/go7007/Makefile index 1301caa7495d..d37b7edc2793 100644 --- a/drivers/staging/go7007/Makefile +++ b/drivers/staging/go7007/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007.o obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o -obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o +obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o @@ -17,7 +17,7 @@ obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ snd-go7007.o -s2250-objs += s2250-board.o s2250-loader.o +s2250-objs += s2250-board.o # Uncomment when the saa7134 patches get into upstream #ifneq ($(CONFIG_VIDEO_SAA7134),) diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/go7007/go7007-driver.c index 472f4bb08fdc..fb1345ffb858 100644 --- a/drivers/staging/go7007/go7007-driver.c +++ b/drivers/staging/go7007/go7007-driver.c @@ -49,7 +49,7 @@ int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data) go->hpi_ops->read_interrupt(go); if (wait_event_timeout(go->interrupt_waitq, go->interrupt_available, 5*HZ) < 0) { - v4l2_err(go->video_dev, "timeout waiting for read interrupt\n"); + v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n"); return -1; } if (!go->interrupt_available) @@ -193,7 +193,8 @@ int go7007_reset_encoder(struct go7007 *go) static int init_i2c_module(struct i2c_adapter *adapter, const char *type, int id, int addr) { - struct i2c_board_info info; + struct go7007 *go = i2c_get_adapdata(adapter); + struct v4l2_device *v4l2_dev = &go->v4l2_dev; char *modname; switch (id) { @@ -219,20 +220,16 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type, modname = "wis-ov7640"; break; case I2C_DRIVERID_S2250: - modname = "s2250-board"; + modname = "s2250"; break; default: modname = NULL; break; } - if (modname != NULL) - request_module(modname); - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = addr; - strlcpy(info.type, type, I2C_NAME_SIZE); - if (!i2c_new_device(adapter, &info)) + if (v4l2_i2c_new_subdev(v4l2_dev, adapter, modname, type, addr, NULL)) return 0; + if (modname != NULL) printk(KERN_INFO "go7007: probing for module %s failed\n", modname); @@ -262,6 +259,11 @@ int go7007_register_encoder(struct go7007 *go) if (ret < 0) return -1; + /* v4l2 init must happen before i2c subdevs */ + ret = go7007_v4l2_init(go); + if (ret < 0) + return ret; + if (!go->i2c_adapter_online && go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { if (go7007_i2c_init(go) < 0) @@ -282,7 +284,7 @@ int go7007_register_encoder(struct go7007 *go) go->audio_enabled = 1; go7007_snd_init(go); } - return go7007_v4l2_init(go); + return 0; } EXPORT_SYMBOL(go7007_register_encoder); @@ -315,7 +317,7 @@ int go7007_start_encoder(struct go7007 *go) if (go7007_send_firmware(go, fw, fw_len) < 0 || go7007_read_interrupt(go, &intr_val, &intr_data) < 0) { - v4l2_err(go->video_dev, "error transferring firmware\n"); + v4l2_err(&go->v4l2_dev, "error transferring firmware\n"); rv = -1; goto start_error; } @@ -324,7 +326,7 @@ int go7007_start_encoder(struct go7007 *go) go->parse_length = 0; go->seen_frame = 0; if (go7007_stream_start(go) < 0) { - v4l2_err(go->video_dev, "error starting stream transfer\n"); + v4l2_err(&go->v4l2_dev, "error starting stream transfer\n"); rv = -1; goto start_error; } @@ -420,7 +422,7 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) for (i = 0; i < length; ++i) { if (go->active_buf != NULL && go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) { - v4l2_info(go->video_dev, "dropping oversized frame\n"); + v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); go->active_buf->offset -= go->active_buf->bytesused; go->active_buf->bytesused = 0; go->active_buf->modet_active = 0; @@ -668,7 +670,7 @@ void go7007_remove(struct go7007 *go) if (i2c_del_adapter(&go->i2c_adapter) == 0) go->i2c_adapter_online = 0; else - v4l2_err(go->video_dev, + v4l2_err(&go->v4l2_dev, "error removing I2C adapter!\n"); } diff --git a/drivers/staging/go7007/go7007-priv.h b/drivers/staging/go7007/go7007-priv.h index ce9307e3e186..b58c394c6555 100644 --- a/drivers/staging/go7007/go7007-priv.h +++ b/drivers/staging/go7007/go7007-priv.h @@ -21,6 +21,8 @@ * user-space applications. */ +#include <media/v4l2-device.h> + struct go7007; /* IDs to activate board-specific support code */ @@ -167,6 +169,7 @@ struct go7007 { int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */ char name[64]; struct video_device *video_dev; + struct v4l2_device v4l2_dev; int ref_count; enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status; spinlock_t spinlock; @@ -240,6 +243,11 @@ struct go7007 { unsigned short interrupt_data; }; +static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct go7007, v4l2_dev); +} + /* All of these must be called with the hpi_lock mutex held! */ #define go7007_interface_reset(go) \ ((go)->hpi_ops->interface_reset(go)) diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/go7007/go7007-usb.c index ecaa3c989cf4..1e89dc04ec23 100644 --- a/drivers/staging/go7007/go7007-usb.c +++ b/drivers/staging/go7007/go7007-usb.c @@ -425,7 +425,7 @@ static struct go7007_usb_board board_sensoray_2250 = { .num_i2c_devs = 1, .i2c_devs = { { - .type = "s2250_board", + .type = "s2250", .id = I2C_DRIVERID_S2250, .addr = 0x43, }, @@ -1057,7 +1057,7 @@ static int go7007_usb_probe(struct usb_interface *intf, usb_rcvintpipe(usb->usbdev, 4), usb->intr_urb->transfer_buffer, 2*sizeof(u16), go7007_usb_readinterrupt_complete, go, 8); - usb_set_intfdata(intf, go); + usb_set_intfdata(intf, &go->v4l2_dev); /* Boot the GO7007 */ if (go7007_boot_encoder(go, go->board_info->flags & @@ -1233,7 +1233,7 @@ allocfail: static void go7007_usb_disconnect(struct usb_interface *intf) { - struct go7007 *go = usb_get_intfdata(intf); + struct go7007 *go = to_go7007(usb_get_intfdata(intf)); struct go7007_usb *usb = go->hpi_context; struct urb *vurb, *aurb; int i; diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c index 4bd353afa596..b18d8e2d4c5e 100644 --- a/drivers/staging/go7007/go7007-v4l2.c +++ b/drivers/staging/go7007/go7007-v4l2.c @@ -29,6 +29,7 @@ #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-subdev.h> #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/uaccess.h> @@ -46,6 +47,9 @@ #define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3 #endif +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(dev, 0, o, f, ##args) + static void deactivate_buffer(struct go7007_buffer *gobuf) { int i; @@ -247,19 +251,23 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) go->modet_map[i] = 0; if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { - struct video_decoder_resolution res; + struct v4l2_format res; + + if (fmt != NULL) { + res = *fmt; + } else { + res.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + res.fmt.pix.width = width; + } - res.width = width; if (height > sensor_height / 2) { - res.height = height / 2; + res.fmt.pix.height = height / 2; go->encoder_v_halve = 0; } else { - res.height = height; + res.fmt.pix.height = height; go->encoder_v_halve = 1; } - if (go->i2c_adapter_online) - i2c_clients_command(&go->i2c_adapter, - DECODER_SET_RESOLUTION, &res); + call_all(&go->v4l2_dev, video, s_fmt, &res); } else { if (width <= sensor_width / 4) { go->encoder_h_halve = 1; @@ -385,7 +393,7 @@ static int clip_to_modet_map(struct go7007 *go, int region, } #endif -static int mpeg_queryctrl(struct v4l2_queryctrl *ctrl) +static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl) { static const u32 mpeg_ctrls[] = { V4L2_CID_MPEG_CLASS, @@ -973,51 +981,35 @@ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *query) { struct go7007 *go = ((struct go7007_file *) priv)->go; + int id = query->id; - if (!go->i2c_adapter_online) - return -EIO; - - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, query); + if (0 == call_all(&go->v4l2_dev, core, queryctrl, query)) + return 0; - return (!query->name[0]) ? mpeg_queryctrl(query) : 0; + query->id = id; + return mpeg_query_ctrl(query); } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct go7007 *go = ((struct go7007_file *) priv)->go; - struct v4l2_queryctrl query; - if (!go->i2c_adapter_online) - return -EIO; - - memset(&query, 0, sizeof(query)); - query.id = ctrl->id; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); - if (query.name[0] == 0) - return mpeg_g_ctrl(ctrl, go); - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, ctrl); + if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl)) + return 0; - return 0; + return mpeg_g_ctrl(ctrl, go); } static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct go7007 *go = ((struct go7007_file *) priv)->go; - struct v4l2_queryctrl query; - if (!go->i2c_adapter_online) - return -EIO; - - memset(&query, 0, sizeof(query)); - query.id = ctrl->id; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); - if (query.name[0] == 0) - return mpeg_s_ctrl(ctrl, go); - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, ctrl); + if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl)) + return 0; - return 0; + return mpeg_s_ctrl(ctrl, go); } static int vidioc_g_parm(struct file *filp, void *priv, @@ -1135,8 +1127,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) if (go->streaming) return -EBUSY; - if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && - *std != 0) + if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0) return -EINVAL; if (*std == 0) @@ -1146,9 +1137,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) go->input == go->board_info->num_inputs - 1) { if (!go->i2c_adapter_online) return -EIO; - i2c_clients_command(&go->i2c_adapter, - VIDIOC_S_STD, std); - if (!*std) /* hack to indicate EINVAL from tuner */ + if (call_all(&go->v4l2_dev, core, s_std, *std) < 0) return -EINVAL; } @@ -1164,9 +1153,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) } else return -EINVAL; - if (go->i2c_adapter_online) - i2c_clients_command(&go->i2c_adapter, - VIDIOC_S_STD, std); + call_all(&go->v4l2_dev, core, s_std, *std); set_capture_size(go, NULL, 0); return 0; @@ -1180,7 +1167,7 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) go->input == go->board_info->num_inputs - 1) { if (!go->i2c_adapter_online) return -EIO; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYSTD, std); + return call_all(&go->v4l2_dev, video, querystd, std); } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) *std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; else @@ -1238,14 +1225,8 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int input) return -EBUSY; go->input = input; - if (go->i2c_adapter_online) { - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_INPUT, - &go->board_info->inputs[input].video_input); - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO, - &go->board_info->inputs[input].audio_input); - } - return 0; + return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0); } static int vidioc_g_tuner(struct file *file, void *priv, @@ -1260,10 +1241,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, if (!go->i2c_adapter_online) return -EIO; - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, t); - - t->index = 0; - return 0; + return call_all(&go->v4l2_dev, tuner, g_tuner, t); } static int vidioc_s_tuner(struct file *file, void *priv, @@ -1287,9 +1265,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, break; } - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, t); - - return 0; + return call_all(&go->v4l2_dev, tuner, s_tuner, t); } static int vidioc_g_frequency(struct file *file, void *priv, @@ -1303,8 +1279,8 @@ static int vidioc_g_frequency(struct file *file, void *priv, return -EIO; f->type = V4L2_TUNER_ANALOG_TV; - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, f); - return 0; + + return call_all(&go->v4l2_dev, tuner, g_frequency, f); } static int vidioc_s_frequency(struct file *file, void *priv, @@ -1317,9 +1293,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (!go->i2c_adapter_online) return -EIO; - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, f); - - return 0; + return call_all(&go->v4l2_dev, tuner, s_frequency, f); } static int vidioc_cropcap(struct file *file, void *priv, @@ -1827,7 +1801,7 @@ int go7007_v4l2_init(struct go7007 *go) go->video_dev = video_device_alloc(); if (go->video_dev == NULL) return -ENOMEM; - memcpy(go->video_dev, &go7007_template, sizeof(go7007_template)); + *go->video_dev = go7007_template; go->video_dev->parent = go->dev; rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1); if (rv < 0) { @@ -1835,6 +1809,12 @@ int go7007_v4l2_init(struct go7007 *go) go->video_dev = NULL; return rv; } + rv = v4l2_device_register(go->dev, &go->v4l2_dev); + if (rv < 0) { + video_device_release(go->video_dev); + go->video_dev = NULL; + return rv; + } video_set_drvdata(go->video_dev, go); ++go->ref_count; printk(KERN_INFO "%s: registered device video%d [v4l2]\n", @@ -1858,4 +1838,5 @@ void go7007_v4l2_remove(struct go7007 *go) mutex_unlock(&go->hw_lock); if (go->video_dev) video_unregister_device(go->video_dev); + v4l2_device_unregister(&go->v4l2_dev); } diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c index f4a6541c3e60..8cf7f2750b3f 100644 --- a/drivers/staging/go7007/s2250-board.c +++ b/drivers/staging/go7007/s2250-board.c @@ -20,10 +20,14 @@ #include <linux/usb.h> #include <linux/i2c.h> #include <linux/videodev2.h> +#include <media/v4l2-device.h> #include <media/v4l2-common.h> -#include "s2250-loader.h" +#include <media/v4l2-i2c-drv.h> +#include <media/v4l2-subdev.h> #include "go7007-priv.h" -#include "wis-i2c.h" + +MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver"); +MODULE_LICENSE("GPL v2"); #define TLV320_ADDRESS 0x34 #define VPX322_ADDR_ANALOGCONTROL1 0x02 @@ -112,6 +116,7 @@ static u16 vid_regs_fp_pal[] = }; struct s2250 { + struct v4l2_subdev sd; v4l2_std_id std; int input; int brightness; @@ -123,6 +128,11 @@ struct s2250 { struct i2c_client *audio; }; +static inline struct s2250 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s2250, sd); +} + /* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ static int go7007_usb_vendor_request(struct go7007 *go, u16 request, u16 value, u16 index, void *transfer_buffer, int length, int in) @@ -306,253 +316,262 @@ static int write_regs_fp(struct i2c_client *client, u16 *regs) } -static int s2250_command(struct i2c_client *client, - unsigned int cmd, void *arg) +/* ------------------------------------------------------------------------- */ + +static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) { - struct s2250 *dec = i2c_get_clientdata(client); + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int vidsys; + + vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00; + if (input == 0) { + /* composite */ + write_reg_fp(client, 0x20, 0x020 | vidsys); + write_reg_fp(client, 0x21, 0x662); + write_reg_fp(client, 0x140, 0x060); + } else if (input == 1) { + /* S-Video */ + write_reg_fp(client, 0x20, 0x040 | vidsys); + write_reg_fp(client, 0x21, 0x666); + write_reg_fp(client, 0x140, 0x060); + } else { + return -EINVAL; + } + state->input = input; + return 0; +} - switch (cmd) { - case VIDIOC_S_INPUT: - { - int vidsys; - int *input = arg; - - vidsys = (dec->std == V4L2_STD_NTSC) ? 0x01 : 0x00; - if (*input == 0) { - /* composite */ - write_reg_fp(client, 0x20, 0x020 | vidsys); - write_reg_fp(client, 0x21, 0x662); - write_reg_fp(client, 0x140, 0x060); - } else { - /* S-Video */ - write_reg_fp(client, 0x20, 0x040 | vidsys); - write_reg_fp(client, 0x21, 0x666); - write_reg_fp(client, 0x140, 0x060); - } - dec->input = *input; +static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 vidsource; + + vidsource = (state->input == 1) ? 0x040 : 0x020; + switch (norm) { + case V4L2_STD_NTSC: + write_regs_fp(client, vid_regs_fp); + write_reg_fp(client, 0x20, vidsource | 1); break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *std = arg; - u16 vidsource; - - vidsource = (dec->input == 1) ? 0x040 : 0x020; - dec->std = *std; - switch (dec->std) { - case V4L2_STD_NTSC: - write_regs_fp(client, vid_regs_fp); - write_reg_fp(client, 0x20, vidsource | 1); - break; - case V4L2_STD_PAL: - write_regs_fp(client, vid_regs_fp); - write_regs_fp(client, vid_regs_fp_pal); - write_reg_fp(client, 0x20, vidsource); - break; - default: - return -EINVAL; - } + case V4L2_STD_PAL: + write_regs_fp(client, vid_regs_fp); + write_regs_fp(client, vid_regs_fp_pal); + write_reg_fp(client, 0x20, vidsource); break; + default: + return -EINVAL; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - static const u32 user_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - 0 - }; - static const u32 *ctrl_classes[] = { - user_ctrls, - NULL - }; - - ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); - break; - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); - break; - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); - break; - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0); - break; - default: - ctrl->name[0] = '\0'; - return -EINVAL; - } - break; + state->std = norm; + return 0; +} + +static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query) +{ + switch (query->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(query, -50, 50, 1, 0); + default: + return -EINVAL; } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - int value1; - u16 oldvalue; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 100) - dec->brightness = 100; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - value1 = (dec->brightness - 50) * 255 / 100; - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, - value1 | (oldvalue & ~0xff)); - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, - value1 | (oldvalue & ~0xff)); - write_reg_fp(client, 0x140, 0x60); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 100) - dec->contrast = 100; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - value1 = dec->contrast * 0x40 / 100; - if (value1 > 0x3f) - value1 = 0x3f; /* max */ - read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST0, - value1 | (oldvalue & ~0x3f)); - read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST1, - value1 | (oldvalue & ~0x3f)); - write_reg_fp(client, 0x140, 0x60); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 127) - dec->saturation = 127; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - - value1 = dec->saturation * 4140 / 100; - if (value1 > 4094) - value1 = 4094; - write_reg_fp(client, VPX322_ADDR_SAT, value1); - break; - case V4L2_CID_HUE: - if (ctrl->value > 50) - dec->hue = 50; - else if (ctrl->value < -50) - dec->hue = -50; - else - dec->hue = ctrl->value; - /* clamp the hue range */ - value1 = dec->hue * 280 / 50; - write_reg_fp(client, VPX322_ADDR_HUE, value1); - break; - } + return 0; +} + +static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int value1; + u16 oldvalue; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value > 100) + state->brightness = 100; + else if (ctrl->value < 0) + state->brightness = 0; + else + state->brightness = ctrl->value; + value1 = (state->brightness - 50) * 255 / 100; + read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); + write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, + value1 | (oldvalue & ~0xff)); + read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); + write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, + value1 | (oldvalue & ~0xff)); + write_reg_fp(client, 0x140, 0x60); break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } + case V4L2_CID_CONTRAST: + if (ctrl->value > 100) + state->contrast = 100; + else if (ctrl->value < 0) + state->contrast = 0; + else + state->contrast = ctrl->value; + value1 = state->contrast * 0x40 / 100; + if (value1 > 0x3f) + value1 = 0x3f; /* max */ + read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); + write_reg_fp(client, VPX322_ADDR_CONTRAST0, + value1 | (oldvalue & ~0x3f)); + read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); + write_reg_fp(client, VPX322_ADDR_CONTRAST1, + value1 | (oldvalue & ~0x3f)); + write_reg_fp(client, 0x140, 0x60); break; + case V4L2_CID_SATURATION: + if (ctrl->value > 100) + state->saturation = 100; + else if (ctrl->value < 0) + state->saturation = 0; + else + state->saturation = ctrl->value; + value1 = state->saturation * 4140 / 100; + if (value1 > 4094) + value1 = 4094; + write_reg_fp(client, VPX322_ADDR_SAT, value1); + break; + case V4L2_CID_HUE: + if (ctrl->value > 50) + state->hue = 50; + else if (ctrl->value < -50) + state->hue = -50; + else + state->hue = ctrl->value; + /* clamp the hue range */ + value1 = state->hue * 280 / 50; + write_reg_fp(client, VPX322_ADDR_HUE, value1); + break; + default: + return -EINVAL; } - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt = arg; - if (fmt->fmt.pix.height < 640) { - write_reg_fp(client, 0x12b, dec->reg12b_val | 0x400); - write_reg_fp(client, 0x140, 0x060); - } else { - write_reg_fp(client, 0x12b, dec->reg12b_val & ~0x400); - write_reg_fp(client, 0x140, 0x060); - } - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *audio = arg; + return 0; +} - memset(audio, 0, sizeof(*audio)); - audio->index = dec->audio_input; - /* fall through */ - } - case VIDIOC_ENUMAUDIO: - { - struct v4l2_audio *audio = arg; - - switch (audio->index) { - case 0: - strcpy(audio->name, "Line In"); - break; - case 1: - strcpy(audio->name, "Mic"); - break; - case 2: - strcpy(audio->name, "Mic Boost"); - break; - default: - audio->name[0] = '\0'; - return 0; - } - audio->capability = V4L2_AUDCAP_STEREO; - audio->mode = 0; - return 0; +static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct s2250 *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = state->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + default: + return -EINVAL; } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *audio = arg; - - switch (audio->index) { - case 0: - write_reg(dec->audio, 0x08, 0x02); /* Line In */ - break; - case 1: - write_reg(dec->audio, 0x08, 0x04); /* Mic */ - break; - case 2: - write_reg(dec->audio, 0x08, 0x05); /* Mic Boost */ - break; - default: - return -EINVAL; - } - dec->audio_input = audio->index; - return 0; + return 0; +} + +static int s2250_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (fmt->fmt.pix.height < 640) { + write_reg_fp(client, 0x12b, state->reg12b_val | 0x400); + write_reg_fp(client, 0x140, 0x060); + } else { + write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400); + write_reg_fp(client, 0x140, 0x060); } + return 0; +} - default: - printk(KERN_INFO "s2250: unknown command 0x%x\n", cmd); +static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct s2250 *state = to_state(sd); + + switch (input) { + case 0: + write_reg(state->audio, 0x08, 0x02); /* Line In */ + break; + case 1: + write_reg(state->audio, 0x08, 0x04); /* Mic */ break; + case 2: + write_reg(state->audio, 0x08, 0x05); /* Mic Boost */ + break; + default: + return -EINVAL; } + state->audio_input = input; + return 0; +} + + +static int s2250_log_status(struct v4l2_subdev *sd) +{ + struct s2250 *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" : + state->std == V4L2_STD_PAL ? "PAL" : + state->std == V4L2_STD_SECAM ? "SECAM" : + "unknown"); + v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" : + state->input == 1 ? "S-video" : + "error"); + v4l2_info(sd, "Brightness: %d\n", state->brightness); + v4l2_info(sd, "Contrast: %d\n", state->contrast); + v4l2_info(sd, "Saturation: %d\n", state->saturation); + v4l2_info(sd, "Hue: %d\n", state->hue); return 0; + v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : + state->audio_input == 1 ? "Mic" : + state->audio_input == 2 ? "Mic Boost" : + "error"); return 0; } +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_subdev_core_ops s2250_core_ops = { + .log_status = s2250_log_status, + .g_ctrl = s2250_g_ctrl, + .s_ctrl = s2250_s_ctrl, + .queryctrl = s2250_queryctrl, + .s_std = s2250_s_std, +}; + +static const struct v4l2_subdev_audio_ops s2250_audio_ops = { + .s_routing = s2250_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops s2250_video_ops = { + .s_routing = s2250_s_video_routing, + .s_fmt = s2250_s_fmt, +}; + +static const struct v4l2_subdev_ops s2250_ops = { + .core = &s2250_core_ops, + .audio = &s2250_audio_ops, + .video = &s2250_video_ops, +}; + +/* --------------------------------------------------------------------------*/ + static int s2250_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_client *audio; struct i2c_adapter *adapter = client->adapter; - struct s2250 *dec; + struct s2250 *state; + struct v4l2_subdev *sd; u8 *data; struct go7007 *go = i2c_get_adapdata(adapter); struct go7007_usb *usb = go->hpi_context; @@ -561,30 +580,31 @@ static int s2250_probe(struct i2c_client *client, if (audio == NULL) return -ENOMEM; - dec = kmalloc(sizeof(struct s2250), GFP_KERNEL); - if (dec == NULL) { + state = kmalloc(sizeof(struct s2250), GFP_KERNEL); + if (state == NULL) { i2c_unregister_device(audio); return -ENOMEM; } - dec->std = V4L2_STD_NTSC; - dec->brightness = 50; - dec->contrast = 50; - dec->saturation = 50; - dec->hue = 0; - dec->audio = audio; - i2c_set_clientdata(client, dec); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &s2250_ops); + + v4l2_info(sd, "initializing %s at address 0x%x on %s\n", + "Sensoray 2250/2251", client->addr, client->adapter->name); - printk(KERN_DEBUG - "s2250: initializing video decoder on %s\n", - adapter->name); + state->std = V4L2_STD_NTSC; + state->brightness = 50; + state->contrast = 50; + state->saturation = 50; + state->hue = 0; + state->audio = audio; /* initialize the audio */ if (write_regs(audio, aud_regs) < 0) { printk(KERN_ERR "s2250: error initializing audio\n"); i2c_unregister_device(audio); - kfree(dec); + kfree(state); return 0; } @@ -592,14 +612,14 @@ static int s2250_probe(struct i2c_client *client, printk(KERN_ERR "s2250: error initializing decoder\n"); i2c_unregister_device(audio); - kfree(dec); + kfree(state); return 0; } if (write_regs_fp(client, vid_regs_fp) < 0) { printk(KERN_ERR "s2250: error initializing decoder\n"); i2c_unregister_device(audio); - kfree(dec); + kfree(state); return 0; } /* set default channel */ @@ -609,7 +629,7 @@ static int s2250_probe(struct i2c_client *client, write_reg_fp(client, 0x140, 0x060); /* set default audio input */ - dec->audio_input = 0; + state->audio_input = 0; write_reg(client, 0x08, 0x02); /* Line In */ if (mutex_lock_interruptible(&usb->i2c_lock) == 0) { @@ -634,60 +654,28 @@ static int s2250_probe(struct i2c_client *client, mutex_unlock(&usb->i2c_lock); } - printk("s2250: initialized successfully\n"); + v4l2_info(sd, "initialized successfully\n"); return 0; } static int s2250_remove(struct i2c_client *client) { - struct s2250 *dec = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); - i2c_set_clientdata(client, NULL); - i2c_unregister_device(dec->audio); - kfree(dec); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } static struct i2c_device_id s2250_id[] = { - { "s2250_board", 0 }, + { "s2250", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, s2250_id); -static struct i2c_driver s2250_driver = { - .driver = { - .name = "Sensoray 2250 board driver", - }, - .probe = s2250_probe, - .remove = s2250_remove, - .command = s2250_command, - .id_table = s2250_id, +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "s2250", + .probe = s2250_probe, + .remove = s2250_remove, + .id_table = s2250_id, }; - -static int __init s2250_init(void) -{ - int r; - - r = s2250loader_init(); - if (r < 0) - return r; - - r = i2c_add_driver(&s2250_driver); - if (r < 0) - s2250loader_cleanup(); - - return r; -} - -static void __exit s2250_cleanup(void) -{ - i2c_del_driver(&s2250_driver); - - s2250loader_cleanup(); -} - -module_init(s2250_init); -module_exit(s2250_cleanup); - -MODULE_AUTHOR(""); -MODULE_DESCRIPTION("Board driver for Sensoryray 2250"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/go7007/s2250-loader.c index d7bf82983274..c152ab9be2fb 100644 --- a/drivers/staging/go7007/s2250-loader.c +++ b/drivers/staging/go7007/s2250-loader.c @@ -162,7 +162,7 @@ static struct usb_driver s2250loader_driver = { .id_table = s2250loader_ids, }; -int s2250loader_init(void) +static int __init s2250loader_init(void) { int r; unsigned i = 0; @@ -179,11 +179,15 @@ int s2250loader_init(void) printk(KERN_INFO "s2250loader_init: driver registered\n"); return 0; } -EXPORT_SYMBOL(s2250loader_init); +module_init(s2250loader_init); -void s2250loader_cleanup(void) +static void __exit s2250loader_cleanup(void) { printk(KERN_INFO "s2250loader_cleanup\n"); usb_deregister(&s2250loader_driver); } -EXPORT_SYMBOL(s2250loader_cleanup); +module_exit(s2250loader_cleanup); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/netwave/Kconfig b/drivers/staging/netwave/Kconfig new file mode 100644 index 000000000000..8033e8171f9e --- /dev/null +++ b/drivers/staging/netwave/Kconfig @@ -0,0 +1,11 @@ +config PCMCIA_NETWAVE + tristate "Xircom Netwave AirSurfer Pcmcia wireless support" + depends on PCMCIA && WLAN + select WIRELESS_EXT + select WEXT_PRIV + help + Say Y here if you intend to attach this type of PCMCIA (PC-card) + wireless Ethernet networking card to your computer. + + To compile this driver as a module, choose M here: the module will be + called netwave_cs. If unsure, say N. diff --git a/drivers/staging/netwave/Makefile b/drivers/staging/netwave/Makefile new file mode 100644 index 000000000000..2ab89de59b9b --- /dev/null +++ b/drivers/staging/netwave/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o diff --git a/drivers/staging/netwave/TODO b/drivers/staging/netwave/TODO new file mode 100644 index 000000000000..9bd15a2f6d9e --- /dev/null +++ b/drivers/staging/netwave/TODO @@ -0,0 +1,7 @@ +TODO: + - step up and maintain this driver to ensure that it continues + to work. Having the hardware for this is pretty much a + requirement. If this does not happen, the will be removed in + the 2.6.35 kernel release. + +Please send patches to Greg Kroah-Hartman <greg@kroah.com>. diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/staging/netwave/netwave_cs.c index e61e6b9440ab..e61e6b9440ab 100644 --- a/drivers/net/wireless/netwave_cs.c +++ b/drivers/staging/netwave/netwave_cs.c diff --git a/drivers/staging/otus/80211core/pub_zfi.h b/drivers/staging/otus/80211core/pub_zfi.h index a35bd5d41d2c..60b7d1c56dee 100644 --- a/drivers/staging/otus/80211core/pub_zfi.h +++ b/drivers/staging/otus/80211core/pub_zfi.h @@ -20,7 +20,7 @@ #include "../oal_dt.h" /***** Section 1 : Tunable Parameters *****/ -/* The defintions in this section are tunabel parameters */ +/* The definitions in this section are tunabel parameters */ /* Maximum number of BSS that could be scaned */ #define ZM_MAX_BSS 128 diff --git a/drivers/staging/otus/zdcompat.h b/drivers/staging/otus/zdcompat.h index 84ac43356b77..cdcaef54afcd 100644 --- a/drivers/staging/otus/zdcompat.h +++ b/drivers/staging/otus/zdcompat.h @@ -17,7 +17,7 @@ /* Module Name : zdcompat.h */ /* */ /* Abstract */ -/* This module contains function defintion for compatibility. */ +/* This module contains function definition for compatibility. */ /* */ /* NOTES */ /* Platform dependent. */ diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c index c94de3139223..f69b7783027f 100644 --- a/drivers/staging/pohmelfs/inode.c +++ b/drivers/staging/pohmelfs/inode.c @@ -143,7 +143,6 @@ static int pohmelfs_writepages(struct address_space *mapping, struct writeback_c struct inode *inode = mapping->host; struct pohmelfs_inode *pi = POHMELFS_I(inode); struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - struct backing_dev_info *bdi = mapping->backing_dev_info; int err = 0; int done = 0; int nr_pages; @@ -152,11 +151,6 @@ static int pohmelfs_writepages(struct address_space *mapping, struct writeback_c int scanned = 0; int range_whole = 0; - if (wbc->nonblocking && bdi_write_congested(bdi)) { - wbc->encountered_congestion = 1; - return 0; - } - if (wbc->range_cyclic) { index = mapping->writeback_index; /* Start from prev offset */ end = -1; @@ -248,10 +242,6 @@ retry: if (wbc->nr_to_write <= 0) done = 1; - if (wbc->nonblocking && bdi_write_congested(bdi)) { - wbc->encountered_congestion = 1; - done = 1; - } continue; out_continue: diff --git a/drivers/staging/rtl8187se/Kconfig b/drivers/staging/rtl8187se/Kconfig index 203c79b8180f..3211dd3765a0 100644 --- a/drivers/staging/rtl8187se/Kconfig +++ b/drivers/staging/rtl8187se/Kconfig @@ -1,6 +1,7 @@ config RTL8187SE tristate "RealTek RTL8187SE Wireless LAN NIC driver" depends on PCI && WLAN - depends on WIRELESS_EXT + select WIRELESS_EXT + select WEXT_PRIV default N ---help--- diff --git a/drivers/staging/rtl8187se/r8180.h b/drivers/staging/rtl8187se/r8180.h index 8216d7e96d60..35ed60be891a 100644 --- a/drivers/staging/rtl8187se/r8180.h +++ b/drivers/staging/rtl8187se/r8180.h @@ -521,7 +521,7 @@ typedef struct r8180_priv //u32 NumTxOkInPeriod; //YJ,del,080828 u8 TxPollingTimes; - bool bApBufOurFrame;// TRUE if AP buffer our unicast data , we will keep eAwake untill receive data or timeout. + bool bApBufOurFrame;// TRUE if AP buffer our unicast data , we will keep eAwake until receive data or timeout. u8 WaitBufDataBcnCount; u8 WaitBufDataTimeOut; diff --git a/drivers/staging/rtl8192e/Kconfig b/drivers/staging/rtl8192e/Kconfig index 37e4fde45073..2ae3745f775f 100644 --- a/drivers/staging/rtl8192e/Kconfig +++ b/drivers/staging/rtl8192e/Kconfig @@ -1,6 +1,7 @@ config RTL8192E tristate "RealTek RTL8192E Wireless LAN NIC driver" depends on PCI && WLAN - depends on WIRELESS_EXT + select WIRELESS_EXT + select WEXT_PRIV default N ---help--- diff --git a/drivers/staging/rtl8192su/r8192S_phyreg.h b/drivers/staging/rtl8192su/r8192S_phyreg.h index 96c7cfa92542..acf644f430aa 100644 --- a/drivers/staging/rtl8192su/r8192S_phyreg.h +++ b/drivers/staging/rtl8192su/r8192S_phyreg.h @@ -38,7 +38,7 @@ // 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00 // 3. RF register 0x00-2E // 4. Bit Mask for BB/RF register -// 5. Other defintion for BB/RF R/W +// 5. Other definition for BB/RF R/W // diff --git a/drivers/staging/strip/Kconfig b/drivers/staging/strip/Kconfig new file mode 100644 index 000000000000..36257b5cd6e1 --- /dev/null +++ b/drivers/staging/strip/Kconfig @@ -0,0 +1,22 @@ +config STRIP + tristate "STRIP (Metricom starmode radio IP)" + depends on INET + select WIRELESS_EXT + ---help--- + Say Y if you have a Metricom radio and intend to use Starmode Radio + IP. STRIP is a radio protocol developed for the MosquitoNet project + to send Internet traffic using Metricom radios. Metricom radios are + small, battery powered, 100kbit/sec packet radio transceivers, about + the size and weight of a cellular telephone. (You may also have heard + them called "Metricom modems" but we avoid the term "modem" because + it misleads many people into thinking that you can plug a Metricom + modem into a phone line and use it as a modem.) + + You can use STRIP on any Linux machine with a serial port, although + it is obviously most useful for people with laptop computers. If you + think you might get a Metricom radio in the future, there is no harm + in saying Y to STRIP now, except that it makes the kernel a bit + bigger. + + To compile this as a module, choose M here: the module will be + called strip. diff --git a/drivers/staging/strip/Makefile b/drivers/staging/strip/Makefile new file mode 100644 index 000000000000..6417bdcac2fb --- /dev/null +++ b/drivers/staging/strip/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_STRIP) += strip.o diff --git a/drivers/staging/strip/TODO b/drivers/staging/strip/TODO new file mode 100644 index 000000000000..9bd15a2f6d9e --- /dev/null +++ b/drivers/staging/strip/TODO @@ -0,0 +1,7 @@ +TODO: + - step up and maintain this driver to ensure that it continues + to work. Having the hardware for this is pretty much a + requirement. If this does not happen, the will be removed in + the 2.6.35 kernel release. + +Please send patches to Greg Kroah-Hartman <greg@kroah.com>. diff --git a/drivers/net/wireless/strip.c b/drivers/staging/strip/strip.c index ea6a87c19319..698aade79d40 100644 --- a/drivers/net/wireless/strip.c +++ b/drivers/staging/strip/strip.c @@ -106,6 +106,7 @@ static const char StripVersion[] = "1.3A-STUART.CHESHIRE"; #include <linux/serial.h> #include <linux/serialP.h> #include <linux/rcupdate.h> +#include <linux/compat.h> #include <net/arp.h> #include <net/net_namespace.h> @@ -2725,6 +2726,19 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file, return 0; } +#ifdef CONFIG_COMPAT +static long strip_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCGIFNAME: + case SIOCSIFHWADDR: + return strip_ioctl(tty, file, cmd, + (unsigned long)compat_ptr(arg)); + } + return -ENOIOCTLCMD; +} +#endif /************************************************************************/ /* Initialization */ @@ -2736,6 +2750,9 @@ static struct tty_ldisc_ops strip_ldisc = { .open = strip_open, .close = strip_close, .ioctl = strip_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = strip_compat_ioctl, +#endif .receive_buf = strip_receive_buf, .write_wakeup = strip_write_some_more, }; diff --git a/drivers/staging/vt6655/Kconfig b/drivers/staging/vt6655/Kconfig index 9bec95adcce2..825bbc4fc3fa 100644 --- a/drivers/staging/vt6655/Kconfig +++ b/drivers/staging/vt6655/Kconfig @@ -1,6 +1,8 @@ config VT6655 tristate "VIA Technologies VT6655 support" - depends on WIRELESS_EXT && PCI + depends on PCI + select WIRELESS_EXT + select WEXT_PRIV ---help--- This is a vendor-written driver for VIA VT6655. diff --git a/drivers/staging/vt6656/Kconfig b/drivers/staging/vt6656/Kconfig index 3165f2c42079..87bcd269310c 100644 --- a/drivers/staging/vt6656/Kconfig +++ b/drivers/staging/vt6656/Kconfig @@ -1,6 +1,8 @@ config VT6656 tristate "VIA Technologies VT6656 support" - depends on WIRELESS_EXT && USB + depends on USB + select WIRELESS_EXT + select WEXT_PRIV ---help--- This is a vendor-written driver for VIA VT6656. diff --git a/drivers/staging/wavelan/Kconfig b/drivers/staging/wavelan/Kconfig new file mode 100644 index 000000000000..af655668c2a7 --- /dev/null +++ b/drivers/staging/wavelan/Kconfig @@ -0,0 +1,38 @@ +config WAVELAN + tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" + depends on ISA && WLAN + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + ---help--- + The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is + a Radio LAN (wireless Ethernet-like Local Area Network) using the + radio frequencies 900 MHz and 2.4 GHz. + + If you want to use an ISA WaveLAN card under Linux, say Y and read + the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. Some more specific + information is contained in + <file:Documentation/networking/wavelan.txt> and in the source code + <file:drivers/net/wireless/wavelan.p.h>. + + You will also need the wireless tools package available from + <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>. + Please read the man pages contained therein. + + To compile this driver as a module, choose M here: the module will be + called wavelan. + +config PCMCIA_WAVELAN + tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" + depends on PCMCIA && WLAN + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + help + Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA + (PC-card) wireless Ethernet networking card to your computer. This + driver is for the non-IEEE-802.11 Wavelan cards. + + To compile this driver as a module, choose M here: the module will be + called wavelan_cs. If unsure, say N. diff --git a/drivers/staging/wavelan/Makefile b/drivers/staging/wavelan/Makefile new file mode 100644 index 000000000000..1cde17c69a43 --- /dev/null +++ b/drivers/staging/wavelan/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_WAVELAN) += wavelan.o +obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o diff --git a/drivers/staging/wavelan/TODO b/drivers/staging/wavelan/TODO new file mode 100644 index 000000000000..9bd15a2f6d9e --- /dev/null +++ b/drivers/staging/wavelan/TODO @@ -0,0 +1,7 @@ +TODO: + - step up and maintain this driver to ensure that it continues + to work. Having the hardware for this is pretty much a + requirement. If this does not happen, the will be removed in + the 2.6.35 kernel release. + +Please send patches to Greg Kroah-Hartman <greg@kroah.com>. diff --git a/drivers/net/wireless/i82586.h b/drivers/staging/wavelan/i82586.h index 5f65b250646f..5f65b250646f 100644 --- a/drivers/net/wireless/i82586.h +++ b/drivers/staging/wavelan/i82586.h diff --git a/drivers/net/wireless/wavelan.c b/drivers/staging/wavelan/wavelan.c index d634b2da3b84..d634b2da3b84 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/staging/wavelan/wavelan.c diff --git a/drivers/net/wireless/wavelan.h b/drivers/staging/wavelan/wavelan.h index 9ab360558ffd..9ab360558ffd 100644 --- a/drivers/net/wireless/wavelan.h +++ b/drivers/staging/wavelan/wavelan.h diff --git a/drivers/net/wireless/wavelan.p.h b/drivers/staging/wavelan/wavelan.p.h index dbe8de6e5f52..dbe8de6e5f52 100644 --- a/drivers/net/wireless/wavelan.p.h +++ b/drivers/staging/wavelan/wavelan.p.h diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/staging/wavelan/wavelan_cs.c index 33918fd5b231..10c702b5be4a 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/staging/wavelan/wavelan_cs.c @@ -3987,7 +3987,7 @@ wavelan_interrupt(int irq, #endif /* Prevent reentrancy. We need to do that because we may have - * multiple interrupt handler running concurently. + * multiple interrupt handler running concurrently. * It is safe because interrupts are disabled before aquiring * the spinlock. */ spin_lock(&lp->spinlock); diff --git a/drivers/net/wireless/wavelan_cs.h b/drivers/staging/wavelan/wavelan_cs.h index 2e4bfe4147c6..2e4bfe4147c6 100644 --- a/drivers/net/wireless/wavelan_cs.h +++ b/drivers/staging/wavelan/wavelan_cs.h diff --git a/drivers/net/wireless/wavelan_cs.p.h b/drivers/staging/wavelan/wavelan_cs.p.h index 81d91531c4f9..8fbfaa8a5a67 100644 --- a/drivers/net/wireless/wavelan_cs.p.h +++ b/drivers/staging/wavelan/wavelan_cs.p.h @@ -446,7 +446,7 @@ #include <pcmcia/ds.h> /* Wavelan declarations */ -#include "i82593.h" /* Definitions for the Intel chip */ +#include <linux/i82593.h> /* Definitions for the Intel chip */ #include "wavelan_cs.h" /* Others bits of the hardware */ diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c index 40de151f2789..e89304c72568 100644 --- a/drivers/telephony/ixj.c +++ b/drivers/telephony/ixj.c @@ -4190,7 +4190,7 @@ static void ixj_aec_start(IXJ *j, int level) ixj_WriteDSPCommand(0x1224, j); ixj_WriteDSPCommand(0xE014, j); - ixj_WriteDSPCommand(0x0003, j); /* Lock threashold at 3dB */ + ixj_WriteDSPCommand(0x0003, j); /* Lock threshold at 3dB */ ixj_WriteDSPCommand(0xE338, j); /* Set Echo Suppresser Attenuation to 0dB */ @@ -4235,7 +4235,7 @@ static void ixj_aec_start(IXJ *j, int level) ixj_WriteDSPCommand(0x1224, j); ixj_WriteDSPCommand(0xE014, j); - ixj_WriteDSPCommand(0x0003, j); /* Lock threashold at 3dB */ + ixj_WriteDSPCommand(0x0003, j); /* Lock threshold at 3dB */ ixj_WriteDSPCommand(0xE338, j); /* Set Echo Suppresser Attenuation to 0dB */ diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index d171b563e94c..bba4d3eabe0f 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -1958,7 +1958,7 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) goto bad1; /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to - * the first MEMACESS cmv. Ignore it... + * the first MEMACCESS cmv. Ignore it... */ if (cmv->bFunction != dsc->function) { if (UEA_CHIP_VERSION(sc) == ADI930 diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 2473cf0c6b1d..b4bd2411c666 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1,5 +1,5 @@ /** - * drivers/usb/class/usbtmc.c - USB Test & Measurment class driver + * drivers/usb/class/usbtmc.c - USB Test & Measurement class driver * * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany * Copyright (C) 2008 Novell, Inc. diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index da718e84d58d..e80f1af438c8 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1890,7 +1890,7 @@ static void cancel_async_set_config(struct usb_device *udev) * routine gets around the normal restrictions by using a work thread to * submit the change-config request. * - * Returns 0 if the request was succesfully queued, error code otherwise. + * Returns 0 if the request was successfully queued, error code otherwise. * The caller has no way to know whether the queued request will eventually * succeed. */ diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 7953948bfe4a..4e3657808b0f 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -432,7 +432,7 @@ static void acm_disable(struct usb_function *f) * @length: size of data * Context: irqs blocked, acm->lock held, acm_notify_req non-null * - * Returns zero on sucess or a negative errno. + * Returns zero on success or a negative errno. * * See section 6.3.5 of the CDC 1.1 specification for information * about the only notification we issue: SerialState change. diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index a2db0e174f2c..f81e4f025f23 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -52,9 +52,9 @@ #include <asm/unaligned.h> #include <asm/mach-types.h> -#include <mach/dma.h> -#include <mach/usb.h> -#include <mach/control.h> +#include <plat/dma.h> +#include <plat/usb.h> +#include <plat/control.h> #include "omap_udc.h" @@ -2098,6 +2098,7 @@ static inline int machine_without_vbus_sense(void) || machine_is_omap_h4() #endif || machine_is_sx1() + || cpu_is_omap7xx() /* No known omap7xx boards with vbus sense */ ); } @@ -2838,6 +2839,16 @@ static int __init omap_udc_probe(struct platform_device *pdev) udelay(100); } + if (cpu_is_omap7xx()) { + dc_clk = clk_get(&pdev->dev, "usb_dc_ck"); + hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck"); + BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk)); + /* can't use omap_udc_enable_clock yet */ + clk_enable(dc_clk); + clk_enable(hhc_clk); + udelay(100); + } + INFO("OMAP UDC rev %d.%d%s\n", omap_readw(UDC_REV) >> 4, omap_readw(UDC_REV) & 0xf, config->otg ? ", Mini-AB" : ""); @@ -2970,7 +2981,7 @@ known: goto cleanup3; } #endif - if (cpu_is_omap16xx()) { + if (cpu_is_omap16xx() || cpu_is_omap7xx()) { udc->dc_clk = dc_clk; udc->hhc_clk = hhc_clk; clk_disable(hhc_clk); @@ -3008,7 +3019,7 @@ cleanup0: if (xceiv) otg_put_transceiver(xceiv); - if (cpu_is_omap16xx() || cpu_is_omap24xx()) { + if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) { clk_disable(hhc_clk); clk_disable(dc_clk); clk_put(hhc_clk); @@ -3115,6 +3126,10 @@ static struct platform_driver udc_driver = { static int __init udc_init(void) { + /* Disable DMA for omap7xx -- it doesn't work right. */ + if (cpu_is_omap7xx()) + use_dma = 0; + INFO("%s, version: " DRIVER_VERSION #ifdef USE_ISO " (iso)" diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 1937d8c7b433..adda1208a1ec 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1524,7 +1524,7 @@ static int pxa_udc_get_frame(struct usb_gadget *_gadget) * pxa_udc_wakeup - Force udc device out of suspend * @_gadget: usb gadget * - * Returns 0 if succesfull, error code otherwise + * Returns 0 if successfull, error code otherwise */ static int pxa_udc_wakeup(struct usb_gadget *_gadget) { diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f5f5601701c9..d8f4aaa616f2 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -210,7 +210,7 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, if (error) { ehci_halt(ehci); ehci_to_hcd(ehci)->state = HC_STATE_HALT; - ehci_err(ehci, "force halt; handhake %p %08x %08x -> %d\n", + ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n", ptr, mask, done, error); } diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index 62a226b61670..00a29855d0c4 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -627,7 +627,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) /* - * Process normal completions(error or sucess) and clean the schedule. + * Process normal completions(error or success) and clean the schedule. * * This is the main path for handing urbs back to drivers. The only other patth * is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 83cbecd2a1ed..5645f70b9214 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -24,10 +24,10 @@ #include <asm/io.h> #include <asm/mach-types.h> -#include <mach/mux.h> +#include <plat/mux.h> #include <mach/irqs.h> -#include <mach/fpga.h> -#include <mach/usb.h> +#include <plat/fpga.h> +#include <plat/usb.h> /* OMAP-1510 OHCI has its own MMU for DMA */ diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 34875201ee04..6761d2088db8 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -35,7 +35,7 @@ #include <asm/mach-types.h> #include <mach/hardware.h> -#include <mach/mux.h> +#include <plat/mux.h> #include "musb_core.h" #include "omap2430.h" diff --git a/drivers/usb/musb/omap2430.h b/drivers/usb/musb/omap2430.h index dc7670718cd2..fbede7798aed 100644 --- a/drivers/usb/musb/omap2430.h +++ b/drivers/usb/musb/omap2430.h @@ -12,7 +12,7 @@ #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) #include <mach/hardware.h> -#include <mach/usb.h> +#include <plat/usb.h> /* * OMAP2430-specific definitions diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 7e073a0d7ac9..e13c77052e5e 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -15,8 +15,8 @@ #include <linux/usb.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> -#include <mach/dma.h> -#include <mach/mux.h> +#include <plat/dma.h> +#include <plat/mux.h> #include "musb_core.h" diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 77a5f4188999..d54460a88173 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -36,8 +36,8 @@ #include <asm/irq.h> #include <asm/mach-types.h> -#include <mach/usb.h> -#include <mach/mux.h> +#include <plat/usb.h> +#include <plat/mux.h> #ifndef DEBUG diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c index 9ec7fd5da489..9579cf4c38bf 100644 --- a/drivers/usb/wusbcore/crypto.c +++ b/drivers/usb/wusbcore/crypto.c @@ -111,7 +111,7 @@ struct aes_ccm_b1 { * * CCM uses Ax blocks to generate a keystream with which the MIC and * the message's payload are encoded. A0 always encrypts/decrypts the - * MIC. Ax (x>0) are used for the sucesive payload blocks. + * MIC. Ax (x>0) are used for the successive payload blocks. * * The x is the counter, and is increased for each block. */ diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 613a5fc490d3..489b47833e2c 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -558,7 +558,7 @@ static void wa_seg_dto_cb(struct urb *urb) /* * Callback for the segment request * - * If succesful transition state (unless already transitioned or + * If successful transition state (unless already transitioned or * outbound transfer); otherwise, take a note of the error, mark this * segment done and try completion. * @@ -1364,7 +1364,7 @@ segment_aborted: /* * Callback for the IN data phase * - * If succesful transition state; otherwise, take a note of the + * If successful transition state; otherwise, take a note of the * error, mark this segment done and try completion. * * Note we don't access until we are sure that the transfer hasn't diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c index c7080d497311..0bb665a0c024 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/uwb/i1480/dfu/usb.c @@ -229,7 +229,7 @@ void i1480_usb_neep_cb(struct urb *urb) * will verify it. * * Set i1480->evt_result with the result of getting the event or its - * size (if succesful). + * size (if successful). * * Delivers the data directly to i1480->evt_buf */ diff --git a/drivers/uwb/neh.c b/drivers/uwb/neh.c index 0af8916d9bef..78510a1f410d 100644 --- a/drivers/uwb/neh.c +++ b/drivers/uwb/neh.c @@ -150,7 +150,7 @@ void uwb_rc_neh_put(struct uwb_rc_neh *neh) * 0xff is invalid, so they must not be used. Initialization * fills up those two in the bitmap so they are not allocated. * - * We spread the allocation around to reduce the posiblity of two + * We spread the allocation around to reduce the possibility of two * consecutive opened @neh's getting the same context ID assigned (to * avoid surprises with late events that timed out long time ago). So * first we search from where @rc->ctx_roll is, if not found, we diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c index 86a853b84119..7350ed6909f8 100644 --- a/drivers/uwb/wlp/txrx.c +++ b/drivers/uwb/wlp/txrx.c @@ -282,7 +282,7 @@ EXPORT_SYMBOL_GPL(wlp_receive_frame); * and transmission will be done by the calling function. * @dst: On return this will contain the device address to which the * frame is destined. - * @returns: 0 on success no tx : WLP header sucessfully applied to skb buffer, + * @returns: 0 on success no tx : WLP header successfully applied to skb buffer, * calling function can proceed with tx * 1 on success with tx : WLP will take over transmission of this * frame diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 188e1ba3b69f..bb5fbed89e7f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -5,6 +5,9 @@ menu "Graphics support" depends on HAS_IOMEM +config HAVE_FB_ATMEL + bool + source "drivers/char/agp/Kconfig" source "drivers/gpu/vga/Kconfig" @@ -937,7 +940,7 @@ config FB_S1D13XXX config FB_ATMEL tristate "AT91/AT32 LCD Controller support" - depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9G10 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 || ARCH_AT91CAP9 || AVR32) + depends on FB && HAVE_FB_ATMEL select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2063,6 +2066,7 @@ config XEN_FBDEV_FRONTEND select FB_SYS_IMAGEBLIT select FB_SYS_FOPS select FB_DEFERRED_IO + select XEN_XENBUS_FRONTEND default y help This driver implements the front-end of the Xen virtual @@ -2161,6 +2165,7 @@ config FB_BROADSHEET a bridge adapter. source "drivers/video/omap/Kconfig" +source "drivers/video/omap2/Kconfig" source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 80232e124889..0f8da331ba0f 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -124,6 +124,7 @@ obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ +obj-y += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index 37624f74e88b..b7687c55fe16 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -2242,6 +2242,9 @@ static int ext_setcolreg(unsigned int regno, unsigned int red, if (!external_vgaiobase) return 1; + if (regno > 255) + return 1; + switch (external_card_type) { case IS_VGA: OUTB(0x3c8, regno); diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index d5e801076d33..3d886c6902f9 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -964,7 +964,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(1); - dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n", + dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %d\n", info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base); return 0; diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 913b4a47ae52..1ddeb4c34763 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -3276,7 +3276,7 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) txtformat = "24 bit interface"; break; default: - txtformat = "unkown format"; + txtformat = "unknown format"; } } else { switch (format & 7) { @@ -3299,7 +3299,7 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) txtformat = "262144 colours (FDPI-2 mode)"; break; default: - txtformat = "unkown format"; + txtformat = "unknown format"; } } PRINTKI("%s%s %s monitor detected: %s\n", diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c index 505c0823a105..2cf7ba52f67c 100644 --- a/drivers/video/backlight/atmel-pwm-bl.c +++ b/drivers/video/backlight/atmel-pwm-bl.c @@ -158,7 +158,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev) goto err_free_pwm; } - /* Turn display off by defatult. */ + /* Turn display off by default. */ retval = gpio_direction_output(pwmbl->gpio_on, 0 ^ pdata->on_active_low); if (retval) diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c index 701a1081e199..7fcb0eb54c60 100644 --- a/drivers/video/backlight/da903x_bl.c +++ b/drivers/video/backlight/da903x_bl.c @@ -25,6 +25,7 @@ #define DA9034_WLED_CONTROL1 0x3C #define DA9034_WLED_CONTROL2 0x3D +#define DA9034_WLED_ISET(x) ((x) & 0x1f) #define DA9034_WLED_BOOST_EN (1 << 5) @@ -101,6 +102,7 @@ static struct backlight_ops da903x_backlight_ops = { static int da903x_backlight_probe(struct platform_device *pdev) { + struct da9034_backlight_pdata *pdata = pdev->dev.platform_data; struct da903x_backlight_data *data; struct backlight_device *bl; int max_brightness; @@ -127,6 +129,11 @@ static int da903x_backlight_probe(struct platform_device *pdev) data->da903x_dev = pdev->dev.parent; data->current_brightness = 0; + /* adjust the WLED output current */ + if (pdata) + da903x_write(data->da903x_dev, DA9034_WLED_CONTROL2, + DA9034_WLED_ISET(pdata->output_current)); + bl = backlight_device_register(pdev->name, data->da903x_dev, data, &da903x_backlight_ops); if (IS_ERR(bl)) { diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c index cbad67e89826..8693e5fcd2eb 100644 --- a/drivers/video/backlight/omap1_bl.c +++ b/drivers/video/backlight/omap1_bl.c @@ -26,8 +26,8 @@ #include <linux/backlight.h> #include <mach/hardware.h> -#include <mach/board.h> -#include <mach/mux.h> +#include <plat/board.h> +#include <plat/mux.h> #define OMAPBL_MAX_INTENSITY 0xff diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index bbfb502add67..4a3d46e08016 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -367,6 +367,7 @@ static int __devinit tdo24m_probe(struct spi_device *spi) spi_message_init(m); + x->cs_change = 1; x->tx_buf = &lcd->buf[0]; spi_message_add_tail(x, m); diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 50ec17dfc517..fa32b94a4546 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -177,7 +177,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi) if (!data) return -ENOMEM; - data->is_vga = true; /* defaut to VGA mode */ + data->is_vga = true; /* default to VGA mode */ /* * bits_per_word cannot be configured in platform data diff --git a/drivers/video/broadsheetfb.c b/drivers/video/broadsheetfb.c index 509cb92e8731..df9ccb901d86 100644 --- a/drivers/video/broadsheetfb.c +++ b/drivers/video/broadsheetfb.c @@ -470,7 +470,7 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev) par->read_reg = broadsheet_read_reg; init_waitqueue_head(&par->waitq); - info->flags = FBINFO_FLAG_DEFAULT; + info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; info->fbdefio = &broadsheetfb_defio; fb_deferred_io_init(info); diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c index 857b3668b3ba..6468a297e341 100644 --- a/drivers/video/console/sticore.c +++ b/drivers/video/console/sticore.c @@ -436,7 +436,7 @@ sti_init_glob_cfg(struct sti_struct *sti, (offs < PCI_BASE_ADDRESS_0 || offs > PCI_BASE_ADDRESS_5)) { printk (KERN_WARNING - "STI pci region maping for region %d (%02x) can't be mapped\n", + "STI pci region mapping for region %d (%02x) can't be mapped\n", i,sti->rm_entry[i]); continue; } diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index da55ccaf4d55..cc4bbbe44aca 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -585,6 +585,11 @@ static void vgacon_init(struct vc_data *c, int init) vgacon_uni_pagedir[1]++; if (!vgacon_uni_pagedir[0] && p) con_set_default_unimap(c); + + /* Only set the default if the user didn't deliberately override it */ + if (global_cursor_default == -1) + global_cursor_default = + !(screen_info.flags & VIDEO_FLAGS_NOCURSOR); } static void vgacon_deinit(struct vc_data *c) diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index c27ab1ed9604..44ce908a478b 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -71,7 +71,7 @@ int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync) { struct fb_info *info = file->private_data; - /* Skip if deferred io is complied-in but disabled on this fbdev */ + /* Skip if deferred io is compiled-in but disabled on this fbdev */ if (!info->fbdefio) return 0; @@ -144,7 +144,9 @@ static const struct address_space_operations fb_deferred_io_aops = { static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) { vma->vm_ops = &fb_deferred_io_vm_ops; - vma->vm_flags |= ( VM_IO | VM_RESERVED | VM_DONTEXPAND ); + vma->vm_flags |= ( VM_RESERVED | VM_DONTEXPAND ); + if (!(info->flags & FBINFO_VIRTFB)) + vma->vm_flags |= VM_IO; vma->vm_private_data = info; return 0; } diff --git a/drivers/video/gbefb.c b/drivers/video/gbefb.c index f67db4268374..695fa013fe7e 100644 --- a/drivers/video/gbefb.c +++ b/drivers/video/gbefb.c @@ -701,7 +701,7 @@ static int gbefb_set_par(struct fb_info *info) blocks of 512x128, 256x128 or 128x128 pixels, respectively for 8bit, 16bit and 32 bit modes (64 kB). They cover the screen with partial tiles on the right and/or bottom of the screen if needed. - For exemple in 640x480 8 bit mode the mapping is: + For example in 640x480 8 bit mode the mapping is: <-------- 640 -----> <---- 512 ----><128|384 offscreen> diff --git a/drivers/video/hecubafb.c b/drivers/video/hecubafb.c index 0b4bffbe67c8..f9d77adf035d 100644 --- a/drivers/video/hecubafb.c +++ b/drivers/video/hecubafb.c @@ -253,7 +253,7 @@ static int __devinit hecubafb_probe(struct platform_device *dev) par->send_command = apollo_send_command; par->send_data = apollo_send_data; - info->flags = FBINFO_FLAG_DEFAULT; + info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; info->fbdefio = &hecubafb_defio; fb_deferred_io_init(info); diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c index df1f757a6161..661bfd20d194 100644 --- a/drivers/video/metronomefb.c +++ b/drivers/video/metronomefb.c @@ -700,7 +700,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) if (retval < 0) goto err_free_irq; - info->flags = FBINFO_FLAG_DEFAULT; + info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; info->fbdefio = &metronomefb_defio; fb_deferred_io_init(info); diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 551e3e9c4cbe..455c6055325d 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -1,6 +1,7 @@ config FB_OMAP tristate "OMAP frame buffer support (EXPERIMENTAL)" - depends on FB && ARCH_OMAP + depends on FB && ARCH_OMAP && (OMAP2_DSS = "n") + select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -72,7 +73,7 @@ config FB_OMAP_LCD_MIPID config FB_OMAP_BOOTLOADER_INIT bool "Check bootloader initialization" - depends on FB_OMAP + depends on FB_OMAP || FB_OMAP2 help Say Y here if you want to enable checking if the bootloader has already initialized the display controller. In this case the diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index b63b198d1f03..49226a1b909e 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -35,6 +35,7 @@ objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o objs-y$(CONFIG_MACH_OVERO) += lcd_overo.o +objs-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o omapfb-objs := $(objs-yy) diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c index 70dadf9d2334..2ffb34af4c59 100644 --- a/drivers/video/omap/blizzard.c +++ b/drivers/video/omap/blizzard.c @@ -26,10 +26,10 @@ #include <linux/delay.h> #include <linux/clk.h> -#include <mach/dma.h> -#include <mach/omapfb.h> -#include <mach/blizzard.h> +#include <plat/dma.h> +#include <plat/blizzard.h> +#include "omapfb.h" #include "dispc.h" #define MODULE_NAME "blizzard" diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index f16e42154229..c7c6455f1fa8 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -24,11 +24,12 @@ #include <linux/vmalloc.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/platform_device.h> -#include <mach/sram.h> -#include <mach/omapfb.h> -#include <mach/board.h> +#include <plat/sram.h> +#include <plat/board.h> +#include "omapfb.h" #include "dispc.h" #define MODULE_NAME "dispc" @@ -188,6 +189,11 @@ static struct { struct omapfb_color_key color_key; } dispc; +static struct platform_device omapdss_device = { + .name = "omapdss", + .id = -1, +}; + static void enable_lcd_clocks(int enable); static void inline dispc_write_reg(int idx, u32 val) @@ -204,6 +210,7 @@ static u32 inline dispc_read_reg(int idx) /* Select RFBI or bypass mode */ static void enable_rfbi_mode(int enable) { + void __iomem *rfbi_control; u32 l; l = dispc_read_reg(DISPC_CONTROL); @@ -216,9 +223,15 @@ static void enable_rfbi_mode(int enable) dispc_write_reg(DISPC_CONTROL, l); /* Set bypass mode in RFBI module */ - l = __raw_readl(OMAP2_IO_ADDRESS(RFBI_CONTROL)); + rfbi_control = ioremap(RFBI_CONTROL, SZ_1K); + if (!rfbi_control) { + pr_err("Unable to ioremap rfbi_control\n"); + return; + } + l = __raw_readl(rfbi_control); l |= enable ? 0 : (1 << 1); - __raw_writel(l, OMAP2_IO_ADDRESS(RFBI_CONTROL)); + __raw_writel(l, rfbi_control); + iounmap(rfbi_control); } static void set_lcd_data_lines(int data_lines) @@ -907,20 +920,20 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) static int get_dss_clocks(void) { - dispc.dss_ick = clk_get(dispc.fbdev->dev, "ick"); + dispc.dss_ick = clk_get(&omapdss_device.dev, "ick"); if (IS_ERR(dispc.dss_ick)) { dev_err(dispc.fbdev->dev, "can't get ick\n"); return PTR_ERR(dispc.dss_ick); } - dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck"); + dispc.dss1_fck = clk_get(&omapdss_device.dev, "dss1_fck"); if (IS_ERR(dispc.dss1_fck)) { dev_err(dispc.fbdev->dev, "can't get dss1_fck\n"); clk_put(dispc.dss_ick); return PTR_ERR(dispc.dss1_fck); } - dispc.dss_54m_fck = clk_get(dispc.fbdev->dev, "tv_fck"); + dispc.dss_54m_fck = clk_get(&omapdss_device.dev, "tv_fck"); if (IS_ERR(dispc.dss_54m_fck)) { dev_err(dispc.fbdev->dev, "can't get tv_fck\n"); clk_put(dispc.dss_ick); @@ -1367,10 +1380,17 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, int r; u32 l; struct lcd_panel *panel = fbdev->panel; + void __iomem *ram_fw_base; int tmo = 10000; int skip_init = 0; int i; + r = platform_device_register(&omapdss_device); + if (r) { + dev_err(fbdev->dev, "can't register omapdss device\n"); + return r; + } + memset(&dispc, 0, sizeof(dispc)); dispc.base = ioremap(DISPC_BASE, SZ_1K); @@ -1441,7 +1461,13 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, } /* L3 firewall setting: enable access to OCM RAM */ - __raw_writel(0x402000b0, OMAP2_IO_ADDRESS(0x680050a0)); + ram_fw_base = ioremap(0x68005000, SZ_1K); + if (!ram_fw_base) { + dev_err(dispc.fbdev->dev, "Cannot ioremap to enable OCM RAM\n"); + goto fail1; + } + __raw_writel(0x402000b0, ram_fw_base + 0xa0); + iounmap(ram_fw_base); if ((r = alloc_palette_ram()) < 0) goto fail2; @@ -1508,6 +1534,7 @@ static void omap_dispc_cleanup(void) free_irq(INT_24XX_DSS_IRQ, dispc.fbdev); put_dss_clocks(); iounmap(dispc.base); + platform_device_unregister(&omapdss_device); } const struct lcd_ctrl omap2_int_ctrl = { diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c index ca51583ec98a..0016f77cd13f 100644 --- a/drivers/video/omap/hwa742.c +++ b/drivers/video/omap/hwa742.c @@ -25,10 +25,11 @@ #include <linux/fb.h> #include <linux/delay.h> #include <linux/clk.h> +#include <linux/interrupt.h> -#include <mach/dma.h> -#include <mach/omapfb.h> -#include <mach/hwa742.h> +#include <plat/dma.h> +#include <plat/hwa742.h> +#include "omapfb.h" #define HWA742_REV_CODE_REG 0x0 #define HWA742_CONFIG_REG 0x2 diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c index 393712b6f369..760645d9dbb6 100644 --- a/drivers/video/omap/lcd_2430sdp.c +++ b/drivers/video/omap/lcd_2430sdp.c @@ -27,10 +27,11 @@ #include <linux/gpio.h> #include <linux/i2c/twl4030.h> -#include <mach/mux.h> -#include <mach/omapfb.h> +#include <plat/mux.h> #include <asm/mach-types.h> +#include "omapfb.h" + #define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91 #define SDP2430_LCD_PANEL_ENABLE_GPIO 154 #define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24 diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c index 1f7439955e02..567db6ac32c8 100644 --- a/drivers/video/omap/lcd_ams_delta.c +++ b/drivers/video/omap/lcd_ams_delta.c @@ -25,9 +25,10 @@ #include <linux/io.h> #include <linux/delay.h> -#include <mach/board-ams-delta.h> +#include <plat/board-ams-delta.h> #include <mach/hardware.h> -#include <mach/omapfb.h> + +#include "omapfb.h" #define AMS_DELTA_DEFAULT_CONTRAST 112 @@ -123,12 +124,12 @@ struct platform_driver ams_delta_panel_driver = { }, }; -static int ams_delta_panel_drv_init(void) +static int __init ams_delta_panel_drv_init(void) { return platform_driver_register(&ams_delta_panel_driver); } -static void ams_delta_panel_drv_cleanup(void) +static void __exit ams_delta_panel_drv_cleanup(void) { platform_driver_unregister(&ams_delta_panel_driver); } diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c index 626ae3a532ff..2be94eb3bbf5 100644 --- a/drivers/video/omap/lcd_apollon.c +++ b/drivers/video/omap/lcd_apollon.c @@ -25,8 +25,9 @@ #include <linux/platform_device.h> #include <mach/gpio.h> -#include <mach/mux.h> -#include <mach/omapfb.h> +#include <plat/mux.h> + +#include "omapfb.h" /* #define USE_35INCH_LCD 1 */ diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c index 417ae5efa8bb..8df688748b5a 100644 --- a/drivers/video/omap/lcd_h3.c +++ b/drivers/video/omap/lcd_h3.c @@ -24,7 +24,7 @@ #include <linux/i2c/tps65010.h> #include <mach/gpio.h> -#include <mach/omapfb.h> +#include "omapfb.h" #define MODULE_NAME "omapfb-lcd_h3" diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c index 0c398bda7601..03a06a982750 100644 --- a/drivers/video/omap/lcd_h4.c +++ b/drivers/video/omap/lcd_h4.c @@ -22,7 +22,7 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <mach/omapfb.h> +#include "omapfb.h" static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) { diff --git a/drivers/video/omap/lcd_htcherald.c b/drivers/video/omap/lcd_htcherald.c new file mode 100644 index 000000000000..a9007c5d1fad --- /dev/null +++ b/drivers/video/omap/lcd_htcherald.c @@ -0,0 +1,130 @@ +/* + * File: drivers/video/omap/lcd-htcherald.c + * + * LCD panel support for the HTC Herald + * + * Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com> + * Copyright (C) 2009 Wing Linux + * + * Based on the lcd_htcwizard.c file from the linwizard project: + * Copyright (C) linwizard.sourceforge.net + * Author: Angelo Arrifano <miknix@gmail.com> + * Based on lcd_h4 by Imre Deak <imre.deak@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "omapfb.h" + +static int htcherald_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + return 0; +} + +static void htcherald_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int htcherald_panel_enable(struct lcd_panel *panel) +{ + return 0; +} + +static void htcherald_panel_disable(struct lcd_panel *panel) +{ +} + +static unsigned long htcherald_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +/* Found on WIZ200 (miknix) and some HERA110 models (darkstar62) */ +struct lcd_panel htcherald_panel_1 = { + .name = "lcd_herald", + .config = OMAP_LCDC_PANEL_TFT | + OMAP_LCDC_INV_HSYNC | + OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_PIX_CLOCK, + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 6093, + .pcd = 0, /* 15 */ + .hsw = 10, + .hfp = 10, + .hbp = 20, + .vsw = 3, + .vfp = 2, + .vbp = 2, + + .init = htcherald_panel_init, + .cleanup = htcherald_panel_cleanup, + .enable = htcherald_panel_enable, + .disable = htcherald_panel_disable, + .get_caps = htcherald_panel_get_caps, +}; + +static int htcherald_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&htcherald_panel_1); + return 0; +} + +static int htcherald_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int htcherald_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int htcherald_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver htcherald_panel_driver = { + .probe = htcherald_panel_probe, + .remove = htcherald_panel_remove, + .suspend = htcherald_panel_suspend, + .resume = htcherald_panel_resume, + .driver = { + .name = "lcd_htcherald", + .owner = THIS_MODULE, + }, +}; + +static int htcherald_panel_drv_init(void) +{ + return platform_driver_register(&htcherald_panel_driver); +} + +static void htcherald_panel_drv_cleanup(void) +{ + platform_driver_unregister(&htcherald_panel_driver); +} + +module_init(htcherald_panel_drv_init); +module_exit(htcherald_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_inn1510.c b/drivers/video/omap/lcd_inn1510.c index cdbd8bb607be..3271f1643b26 100644 --- a/drivers/video/omap/lcd_inn1510.c +++ b/drivers/video/omap/lcd_inn1510.c @@ -23,8 +23,8 @@ #include <linux/platform_device.h> #include <linux/io.h> -#include <mach/fpga.h> -#include <mach/omapfb.h> +#include <plat/fpga.h> +#include "omapfb.h" static int innovator1510_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c index 268f7f808a4e..9fff86f67bde 100644 --- a/drivers/video/omap/lcd_inn1610.c +++ b/drivers/video/omap/lcd_inn1610.c @@ -23,7 +23,7 @@ #include <linux/platform_device.h> #include <mach/gpio.h> -#include <mach/omapfb.h> +#include "omapfb.h" #define MODULE_NAME "omapfb-lcd_h3" diff --git a/drivers/video/omap/lcd_ldp.c b/drivers/video/omap/lcd_ldp.c index dbfe8974fb94..5bb7f6f14601 100644 --- a/drivers/video/omap/lcd_ldp.c +++ b/drivers/video/omap/lcd_ldp.c @@ -27,10 +27,11 @@ #include <linux/i2c/twl4030.h> #include <mach/gpio.h> -#include <mach/mux.h> -#include <mach/omapfb.h> +#include <plat/mux.h> #include <asm/mach-types.h> +#include "omapfb.h" + #define LCD_PANEL_BACKLIGHT_GPIO (15 + OMAP_MAX_GPIO_LINES) #define LCD_PANEL_ENABLE_GPIO (7 + OMAP_MAX_GPIO_LINES) diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c index 918ee8934196..abe1c76a3257 100644 --- a/drivers/video/omap/lcd_mipid.c +++ b/drivers/video/omap/lcd_mipid.c @@ -23,8 +23,9 @@ #include <linux/workqueue.h> #include <linux/spi/spi.h> -#include <mach/omapfb.h> -#include <mach/lcd_mipid.h> +#include <plat/lcd_mipid.h> + +#include "omapfb.h" #define MIPID_MODULE_NAME "lcd_mipid" @@ -607,7 +608,7 @@ static struct spi_driver mipid_spi_driver = { .remove = __devexit_p(mipid_spi_remove), }; -static int mipid_drv_init(void) +static int __init mipid_drv_init(void) { spi_register_driver(&mipid_spi_driver); @@ -615,7 +616,7 @@ static int mipid_drv_init(void) } module_init(mipid_drv_init); -static void mipid_drv_cleanup(void) +static void __exit mipid_drv_cleanup(void) { spi_unregister_driver(&mipid_spi_driver); } diff --git a/drivers/video/omap/lcd_omap2evm.c b/drivers/video/omap/lcd_omap2evm.c index 7a2bbe2ecec3..006c2fe7360e 100644 --- a/drivers/video/omap/lcd_omap2evm.c +++ b/drivers/video/omap/lcd_omap2evm.c @@ -26,10 +26,11 @@ #include <linux/gpio.h> #include <linux/i2c/twl4030.h> -#include <mach/mux.h> -#include <mach/omapfb.h> +#include <plat/mux.h> #include <asm/mach-types.h> +#include "omapfb.h" + #define LCD_PANEL_ENABLE_GPIO 154 #define LCD_PANEL_LR 128 #define LCD_PANEL_UD 129 diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c index 4011910123bf..fc503d8f3c24 100644 --- a/drivers/video/omap/lcd_omap3beagle.c +++ b/drivers/video/omap/lcd_omap3beagle.c @@ -25,10 +25,12 @@ #include <linux/gpio.h> #include <linux/i2c/twl4030.h> -#include <mach/mux.h> -#include <mach/omapfb.h> +#include <plat/mux.h> +#include <plat/mux.h> #include <asm/mach-types.h> +#include "omapfb.h" + #define LCD_PANEL_ENABLE_GPIO 170 static int omap3beagle_panel_init(struct lcd_panel *panel, diff --git a/drivers/video/omap/lcd_omap3evm.c b/drivers/video/omap/lcd_omap3evm.c index b6a4c2c57a2f..ae2edc4081a8 100644 --- a/drivers/video/omap/lcd_omap3evm.c +++ b/drivers/video/omap/lcd_omap3evm.c @@ -25,10 +25,11 @@ #include <linux/gpio.h> #include <linux/i2c/twl4030.h> -#include <mach/mux.h> -#include <mach/omapfb.h> +#include <plat/mux.h> #include <asm/mach-types.h> +#include "omapfb.h" + #define LCD_PANEL_ENABLE_GPIO 153 #define LCD_PANEL_LR 2 #define LCD_PANEL_UD 3 diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c index b3fa88bc6269..b87e8b83f29c 100644 --- a/drivers/video/omap/lcd_osk.c +++ b/drivers/video/omap/lcd_osk.c @@ -24,8 +24,8 @@ #include <linux/platform_device.h> #include <mach/gpio.h> -#include <mach/mux.h> -#include <mach/omapfb.h> +#include <plat/mux.h> +#include "omapfb.h" static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) { diff --git a/drivers/video/omap/lcd_overo.c b/drivers/video/omap/lcd_overo.c index 2bc5c9268e5e..56ee192e9ee2 100644 --- a/drivers/video/omap/lcd_overo.c +++ b/drivers/video/omap/lcd_overo.c @@ -24,10 +24,11 @@ #include <linux/i2c/twl4030.h> #include <mach/gpio.h> -#include <mach/mux.h> -#include <mach/omapfb.h> +#include <plat/mux.h> #include <asm/mach-types.h> +#include "omapfb.h" + #define LCD_ENABLE 144 static int overo_panel_init(struct lcd_panel *panel, diff --git a/drivers/video/omap/lcd_palmte.c b/drivers/video/omap/lcd_palmte.c index 4bf3c79f3cc7..4cb301750d02 100644 --- a/drivers/video/omap/lcd_palmte.c +++ b/drivers/video/omap/lcd_palmte.c @@ -23,8 +23,8 @@ #include <linux/platform_device.h> #include <linux/io.h> -#include <mach/fpga.h> -#include <mach/omapfb.h> +#include <plat/fpga.h> +#include "omapfb.h" static int palmte_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/lcd_palmtt.c b/drivers/video/omap/lcd_palmtt.c index 48ea1f9f2cbf..ff0e6d7ab3a2 100644 --- a/drivers/video/omap/lcd_palmtt.c +++ b/drivers/video/omap/lcd_palmtt.c @@ -30,7 +30,7 @@ GPIO13 - screen blanking #include <linux/io.h> #include <mach/gpio.h> -#include <mach/omapfb.h> +#include "omapfb.h" static int palmtt_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/lcd_palmz71.c b/drivers/video/omap/lcd_palmz71.c index 0697d29b4d3b..2334e56536bc 100644 --- a/drivers/video/omap/lcd_palmz71.c +++ b/drivers/video/omap/lcd_palmz71.c @@ -24,7 +24,7 @@ #include <linux/platform_device.h> #include <linux/io.h> -#include <mach/omapfb.h> +#include "omapfb.h" static int palmz71_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/lcdc.c b/drivers/video/omap/lcdc.c index ab3949256677..b831e1df629e 100644 --- a/drivers/video/omap/lcdc.c +++ b/drivers/video/omap/lcdc.c @@ -29,11 +29,12 @@ #include <linux/vmalloc.h> #include <linux/clk.h> -#include <mach/dma.h> -#include <mach/omapfb.h> +#include <plat/dma.h> #include <asm/mach-types.h> +#include "omapfb.h" + #include "lcdc.h" #define MODULE_NAME "lcdc" diff --git a/drivers/video/omap/omapfb.h b/drivers/video/omap/omapfb.h new file mode 100644 index 000000000000..46e4714014e8 --- /dev/null +++ b/drivers/video/omap/omapfb.h @@ -0,0 +1,227 @@ +/* + * File: drivers/video/omap/omapfb.h + * + * Framebuffer driver for TI OMAP boards + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __OMAPFB_H +#define __OMAPFB_H + +#include <linux/fb.h> +#include <linux/mutex.h> +#include <linux/omapfb.h> + +#define OMAPFB_EVENT_READY 1 +#define OMAPFB_EVENT_DISABLED 2 + +#define OMAP_LCDC_INV_VSYNC 0x0001 +#define OMAP_LCDC_INV_HSYNC 0x0002 +#define OMAP_LCDC_INV_PIX_CLOCK 0x0004 +#define OMAP_LCDC_INV_OUTPUT_EN 0x0008 +#define OMAP_LCDC_HSVS_RISING_EDGE 0x0010 +#define OMAP_LCDC_HSVS_OPPOSITE 0x0020 + +#define OMAP_LCDC_SIGNAL_MASK 0x003f + +#define OMAP_LCDC_PANEL_TFT 0x0100 + +#define OMAPFB_PLANE_XRES_MIN 8 +#define OMAPFB_PLANE_YRES_MIN 8 + +struct omapfb_device; + +struct lcd_panel { + const char *name; + int config; /* TFT/STN, signal inversion */ + int bpp; /* Pixel format in fb mem */ + int data_lines; /* Lines on LCD HW interface */ + + int x_res, y_res; + int pixel_clock; /* In kHz */ + int hsw; /* Horizontal synchronization + pulse width */ + int hfp; /* Horizontal front porch */ + int hbp; /* Horizontal back porch */ + int vsw; /* Vertical synchronization + pulse width */ + int vfp; /* Vertical front porch */ + int vbp; /* Vertical back porch */ + int acb; /* ac-bias pin frequency */ + int pcd; /* pixel clock divider. + Obsolete use pixel_clock instead */ + + int (*init) (struct lcd_panel *panel, + struct omapfb_device *fbdev); + void (*cleanup) (struct lcd_panel *panel); + int (*enable) (struct lcd_panel *panel); + void (*disable) (struct lcd_panel *panel); + unsigned long (*get_caps) (struct lcd_panel *panel); + int (*set_bklight_level)(struct lcd_panel *panel, + unsigned int level); + unsigned int (*get_bklight_level)(struct lcd_panel *panel); + unsigned int (*get_bklight_max) (struct lcd_panel *panel); + int (*run_test) (struct lcd_panel *panel, int test_num); +}; + +struct extif_timings { + int cs_on_time; + int cs_off_time; + int we_on_time; + int we_off_time; + int re_on_time; + int re_off_time; + int we_cycle_time; + int re_cycle_time; + int cs_pulse_width; + int access_time; + + int clk_div; + + u32 tim[5]; /* set by extif->convert_timings */ + + int converted; +}; + +struct lcd_ctrl_extif { + int (*init) (struct omapfb_device *fbdev); + void (*cleanup) (void); + void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div); + unsigned long (*get_max_tx_rate)(void); + int (*convert_timings) (struct extif_timings *timings); + void (*set_timings) (const struct extif_timings *timings); + void (*set_bits_per_cycle)(int bpc); + void (*write_command) (const void *buf, unsigned int len); + void (*read_data) (void *buf, unsigned int len); + void (*write_data) (const void *buf, unsigned int len); + void (*transfer_area) (int width, int height, + void (callback)(void *data), void *data); + int (*setup_tearsync) (unsigned pin_cnt, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int div); + int (*enable_tearsync) (int enable, unsigned line); + + unsigned long max_transmit_size; +}; + +struct omapfb_notifier_block { + struct notifier_block nb; + void *data; + int plane_idx; +}; + +typedef int (*omapfb_notifier_callback_t)(struct notifier_block *, + unsigned long event, + void *fbi); + +struct lcd_ctrl { + const char *name; + void *data; + + int (*init) (struct omapfb_device *fbdev, + int ext_mode, + struct omapfb_mem_desc *req_md); + void (*cleanup) (void); + void (*bind_client) (struct omapfb_notifier_block *nb); + void (*get_caps) (int plane, struct omapfb_caps *caps); + int (*set_update_mode)(enum omapfb_update_mode mode); + enum omapfb_update_mode (*get_update_mode)(void); + int (*setup_plane) (int plane, int channel_out, + unsigned long offset, + int screen_width, + int pos_x, int pos_y, int width, + int height, int color_mode); + int (*set_rotate) (int angle); + int (*setup_mem) (int plane, size_t size, + int mem_type, unsigned long *paddr); + int (*mmap) (struct fb_info *info, + struct vm_area_struct *vma); + int (*set_scale) (int plane, + int orig_width, int orig_height, + int out_width, int out_height); + int (*enable_plane) (int plane, int enable); + int (*update_window) (struct fb_info *fbi, + struct omapfb_update_window *win, + void (*callback)(void *), + void *callback_data); + void (*sync) (void); + void (*suspend) (void); + void (*resume) (void); + int (*run_test) (int test_num); + int (*setcolreg) (u_int regno, u16 red, u16 green, + u16 blue, u16 transp, + int update_hw_mem); + int (*set_color_key) (struct omapfb_color_key *ck); + int (*get_color_key) (struct omapfb_color_key *ck); +}; + +enum omapfb_state { + OMAPFB_DISABLED = 0, + OMAPFB_SUSPENDED = 99, + OMAPFB_ACTIVE = 100 +}; + +struct omapfb_plane_struct { + int idx; + struct omapfb_plane_info info; + enum omapfb_color_format color_mode; + struct omapfb_device *fbdev; +}; + +struct omapfb_device { + int state; + int ext_lcdc; /* Using external + LCD controller */ + struct mutex rqueue_mutex; + + int palette_size; + u32 pseudo_palette[17]; + + struct lcd_panel *panel; /* LCD panel */ + const struct lcd_ctrl *ctrl; /* LCD controller */ + const struct lcd_ctrl *int_ctrl; /* internal LCD ctrl */ + struct lcd_ctrl_extif *ext_if; /* LCD ctrl external + interface */ + struct device *dev; + struct fb_var_screeninfo new_var; /* for mode changes */ + + struct omapfb_mem_desc mem_desc; + struct fb_info *fb_info[OMAPFB_PLANE_NUM]; +}; + +#ifdef CONFIG_ARCH_OMAP1 +extern struct lcd_ctrl omap1_lcd_ctrl; +#else +extern struct lcd_ctrl omap2_disp_ctrl; +#endif + +extern void omapfb_register_panel(struct lcd_panel *panel); +extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); +extern void omapfb_notify_clients(struct omapfb_device *fbdev, + unsigned long event); +extern int omapfb_register_client(struct omapfb_notifier_block *nb, + omapfb_notifier_callback_t callback, + void *callback_data); +extern int omapfb_unregister_client(struct omapfb_notifier_block *nb); +extern int omapfb_update_window_async(struct fb_info *fbi, + struct omapfb_update_window *win, + void (*callback)(void *), + void *callback_data); + +#endif /* __OMAPFB_H */ diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 0d0c8c8b9b56..c7f59a5ccdbc 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -28,9 +28,9 @@ #include <linux/mm.h> #include <linux/uaccess.h> -#include <mach/dma.h> -#include <mach/omapfb.h> +#include <plat/dma.h> +#include "omapfb.h" #include "lcdc.h" #include "dispc.h" diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c index ee01e84e19c1..fed7b1bda19c 100644 --- a/drivers/video/omap/rfbi.c +++ b/drivers/video/omap/rfbi.c @@ -27,8 +27,7 @@ #include <linux/clk.h> #include <linux/io.h> -#include <mach/omapfb.h> - +#include "omapfb.h" #include "dispc.h" /* To work around an RFBI transfer rate limitation */ diff --git a/drivers/video/omap/sossi.c b/drivers/video/omap/sossi.c index a76946220249..8fb7c708f563 100644 --- a/drivers/video/omap/sossi.c +++ b/drivers/video/omap/sossi.c @@ -23,10 +23,11 @@ #include <linux/clk.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/interrupt.h> -#include <mach/dma.h> -#include <mach/omapfb.h> +#include <plat/dma.h> +#include "omapfb.h" #include "lcdc.h" #define MODULE_NAME "omapfb-sossi" diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig new file mode 100644 index 000000000000..d877c361abda --- /dev/null +++ b/drivers/video/omap2/Kconfig @@ -0,0 +1,9 @@ +config OMAP2_VRAM + bool + +config OMAP2_VRFB + bool + +source "drivers/video/omap2/dss/Kconfig" +source "drivers/video/omap2/omapfb/Kconfig" +source "drivers/video/omap2/displays/Kconfig" diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile new file mode 100644 index 000000000000..d853d05dad31 --- /dev/null +++ b/drivers/video/omap2/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_OMAP2_VRAM) += vram.o +obj-$(CONFIG_OMAP2_VRFB) += vrfb.o + +obj-y += dss/ +obj-y += omapfb/ +obj-y += displays/ diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig new file mode 100644 index 000000000000..b12a59c9c50a --- /dev/null +++ b/drivers/video/omap2/displays/Kconfig @@ -0,0 +1,22 @@ +menu "OMAP2/3 Display Device Drivers" + depends on OMAP2_DSS + +config PANEL_GENERIC + tristate "Generic Panel" + help + Generic panel driver. + Used for DVI output for Beagle and OMAP3 SDP. + +config PANEL_SHARP_LS037V7DW01 + tristate "Sharp LS037V7DW01 LCD Panel" + depends on OMAP2_DSS + help + LCD Panel used in TI's SDP3430 and EVM boards + +config PANEL_TAAL + tristate "Taal DSI Panel" + depends on OMAP2_DSS_DSI + help + Taal DSI command mode panel from TPO. + +endmenu diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile new file mode 100644 index 000000000000..955646440b3a --- /dev/null +++ b/drivers/video/omap2/displays/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o +obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o + +obj-$(CONFIG_PANEL_TAAL) += panel-taal.o diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c new file mode 100644 index 000000000000..eb48d1afd800 --- /dev/null +++ b/drivers/video/omap2/displays/panel-generic.c @@ -0,0 +1,104 @@ +/* + * Generic panel support + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> + +#include <plat/display.h> + +static struct omap_video_timings generic_panel_timings = { + /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ + .x_res = 640, + .y_res = 480, + .pixel_clock = 23500, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 7, +}; + +static int generic_panel_probe(struct omap_dss_device *dssdev) +{ + dssdev->panel.config = OMAP_DSS_LCD_TFT; + dssdev->panel.timings = generic_panel_timings; + + return 0; +} + +static void generic_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int generic_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + if (dssdev->platform_enable) + r = dssdev->platform_enable(dssdev); + + return r; +} + +static void generic_panel_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); +} + +static int generic_panel_suspend(struct omap_dss_device *dssdev) +{ + generic_panel_disable(dssdev); + return 0; +} + +static int generic_panel_resume(struct omap_dss_device *dssdev) +{ + return generic_panel_enable(dssdev); +} + +static struct omap_dss_driver generic_driver = { + .probe = generic_panel_probe, + .remove = generic_panel_remove, + + .enable = generic_panel_enable, + .disable = generic_panel_disable, + .suspend = generic_panel_suspend, + .resume = generic_panel_resume, + + .driver = { + .name = "generic_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init generic_panel_drv_init(void) +{ + return omap_dss_register_driver(&generic_driver); +} + +static void __exit generic_panel_drv_exit(void) +{ + omap_dss_unregister_driver(&generic_driver); +} + +module_init(generic_panel_drv_init); +module_exit(generic_panel_drv_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c new file mode 100644 index 000000000000..bbe880bbe795 --- /dev/null +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -0,0 +1,153 @@ +/* + * LCD panel driver for Sharp LS037V7DW01 + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> + +#include <plat/display.h> + +struct sharp_data { + /* XXX This regulator should actually be in SDP board file, not here, + * as it doesn't actually power the LCD, but something else that + * affects the output to LCD (I think. Somebody clarify). It doesn't do + * harm here, as SDP is the only board using this currently */ + struct regulator *vdvi_reg; +}; + +static struct omap_video_timings sharp_ls_timings = { + .x_res = 480, + .y_res = 640, + + .pixel_clock = 19200, + + .hsw = 2, + .hfp = 1, + .hbp = 28, + + .vsw = 1, + .vfp = 1, + .vbp = 1, +}; + +static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) +{ + struct sharp_data *sd; + + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS; + dssdev->panel.acb = 0x28; + dssdev->panel.timings = sharp_ls_timings; + + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) + return -ENOMEM; + + dev_set_drvdata(&dssdev->dev, sd); + + sd->vdvi_reg = regulator_get(&dssdev->dev, "vdvi"); + if (IS_ERR(sd->vdvi_reg)) { + kfree(sd); + pr_err("failed to get VDVI regulator\n"); + return PTR_ERR(sd->vdvi_reg); + } + + return 0; +} + +static void sharp_ls_panel_remove(struct omap_dss_device *dssdev) +{ + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); + + regulator_put(sd->vdvi_reg); + + kfree(sd); +} + +static int sharp_ls_panel_enable(struct omap_dss_device *dssdev) +{ + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); + int r = 0; + + /* wait couple of vsyncs until enabling the LCD */ + msleep(50); + + regulator_enable(sd->vdvi_reg); + + if (dssdev->platform_enable) + r = dssdev->platform_enable(dssdev); + + return r; +} + +static void sharp_ls_panel_disable(struct omap_dss_device *dssdev) +{ + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + regulator_disable(sd->vdvi_reg); + + /* wait at least 5 vsyncs after disabling the LCD */ + + msleep(100); +} + +static int sharp_ls_panel_suspend(struct omap_dss_device *dssdev) +{ + sharp_ls_panel_disable(dssdev); + return 0; +} + +static int sharp_ls_panel_resume(struct omap_dss_device *dssdev) +{ + return sharp_ls_panel_enable(dssdev); +} + +static struct omap_dss_driver sharp_ls_driver = { + .probe = sharp_ls_panel_probe, + .remove = sharp_ls_panel_remove, + + .enable = sharp_ls_panel_enable, + .disable = sharp_ls_panel_disable, + .suspend = sharp_ls_panel_suspend, + .resume = sharp_ls_panel_resume, + + .driver = { + .name = "sharp_ls_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init sharp_ls_panel_drv_init(void) +{ + return omap_dss_register_driver(&sharp_ls_driver); +} + +static void __exit sharp_ls_panel_drv_exit(void) +{ + omap_dss_unregister_driver(&sharp_ls_driver); +} + +module_init(sharp_ls_panel_drv_init); +module_exit(sharp_ls_panel_drv_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c new file mode 100644 index 000000000000..1f01dfc5e52e --- /dev/null +++ b/drivers/video/omap2/displays/panel-taal.c @@ -0,0 +1,1003 @@ +/* + * Taal DSI command mode panel + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*#define DEBUG*/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/backlight.h> +#include <linux/fb.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/completion.h> +#include <linux/workqueue.h> + +#include <plat/display.h> + +/* DSI Virtual channel. Hardcoded for now. */ +#define TCH 0 + +#define DCS_READ_NUM_ERRORS 0x05 +#define DCS_READ_POWER_MODE 0x0a +#define DCS_READ_MADCTL 0x0b +#define DCS_READ_PIXEL_FORMAT 0x0c +#define DCS_RDDSDR 0x0f +#define DCS_SLEEP_IN 0x10 +#define DCS_SLEEP_OUT 0x11 +#define DCS_DISPLAY_OFF 0x28 +#define DCS_DISPLAY_ON 0x29 +#define DCS_COLUMN_ADDR 0x2a +#define DCS_PAGE_ADDR 0x2b +#define DCS_MEMORY_WRITE 0x2c +#define DCS_TEAR_OFF 0x34 +#define DCS_TEAR_ON 0x35 +#define DCS_MEM_ACC_CTRL 0x36 +#define DCS_PIXEL_FORMAT 0x3a +#define DCS_BRIGHTNESS 0x51 +#define DCS_CTRL_DISPLAY 0x53 +#define DCS_WRITE_CABC 0x55 +#define DCS_READ_CABC 0x56 +#define DCS_GET_ID1 0xda +#define DCS_GET_ID2 0xdb +#define DCS_GET_ID3 0xdc + +/* #define TAAL_USE_ESD_CHECK */ +#define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000) + +struct taal_data { + struct backlight_device *bldev; + + unsigned long hw_guard_end; /* next value of jiffies when we can + * issue the next sleep in/out command + */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + struct omap_dss_device *dssdev; + + bool enabled; + u8 rotate; + bool mirror; + + bool te_enabled; + bool use_ext_te; + struct completion te_completion; + + bool use_dsi_bl; + + bool cabc_broken; + unsigned cabc_mode; + + bool intro_printed; + + struct workqueue_struct *esd_wq; + struct delayed_work esd_work; +}; + +static void taal_esd_work(struct work_struct *work); + +static void hw_guard_start(struct taal_data *td, int guard_msec) +{ + td->hw_guard_wait = msecs_to_jiffies(guard_msec); + td->hw_guard_end = jiffies + td->hw_guard_wait; +} + +static void hw_guard_wait(struct taal_data *td) +{ + unsigned long wait = td->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= td->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static int taal_dcs_read_1(u8 dcs_cmd, u8 *data) +{ + int r; + u8 buf[1]; + + r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1); + + if (r < 0) + return r; + + *data = buf[0]; + + return 0; +} + +static int taal_dcs_write_0(u8 dcs_cmd) +{ + return dsi_vc_dcs_write(TCH, &dcs_cmd, 1); +} + +static int taal_dcs_write_1(u8 dcs_cmd, u8 param) +{ + u8 buf[2]; + buf[0] = dcs_cmd; + buf[1] = param; + return dsi_vc_dcs_write(TCH, buf, 2); +} + +static int taal_sleep_in(struct taal_data *td) + +{ + u8 cmd; + int r; + + hw_guard_wait(td); + + cmd = DCS_SLEEP_IN; + r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1); + if (r) + return r; + + hw_guard_start(td, 120); + + msleep(5); + + return 0; +} + +static int taal_sleep_out(struct taal_data *td) +{ + int r; + + hw_guard_wait(td); + + r = taal_dcs_write_0(DCS_SLEEP_OUT); + if (r) + return r; + + hw_guard_start(td, 120); + + msleep(5); + + return 0; +} + +static int taal_get_id(u8 *id1, u8 *id2, u8 *id3) +{ + int r; + + r = taal_dcs_read_1(DCS_GET_ID1, id1); + if (r) + return r; + r = taal_dcs_read_1(DCS_GET_ID2, id2); + if (r) + return r; + r = taal_dcs_read_1(DCS_GET_ID3, id3); + if (r) + return r; + + return 0; +} + +static int taal_set_addr_mode(u8 rotate, bool mirror) +{ + int r; + u8 mode; + int b5, b6, b7; + + r = taal_dcs_read_1(DCS_READ_MADCTL, &mode); + if (r) + return r; + + switch (rotate) { + default: + case 0: + b7 = 0; + b6 = 0; + b5 = 0; + break; + case 1: + b7 = 0; + b6 = 1; + b5 = 1; + break; + case 2: + b7 = 1; + b6 = 1; + b5 = 0; + break; + case 3: + b7 = 1; + b6 = 0; + b5 = 1; + break; + } + + if (mirror) + b6 = !b6; + + mode &= ~((1<<7) | (1<<6) | (1<<5)); + mode |= (b7 << 7) | (b6 << 6) | (b5 << 5); + + return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode); +} + +static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h) +{ + int r; + u16 x1 = x; + u16 x2 = x + w - 1; + u16 y1 = y; + u16 y2 = y + h - 1; + + u8 buf[5]; + buf[0] = DCS_COLUMN_ADDR; + buf[1] = (x1 >> 8) & 0xff; + buf[2] = (x1 >> 0) & 0xff; + buf[3] = (x2 >> 8) & 0xff; + buf[4] = (x2 >> 0) & 0xff; + + r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf)); + if (r) + return r; + + buf[0] = DCS_PAGE_ADDR; + buf[1] = (y1 >> 8) & 0xff; + buf[2] = (y1 >> 0) & 0xff; + buf[3] = (y2 >> 8) & 0xff; + buf[4] = (y2 >> 0) & 0xff; + + r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf)); + if (r) + return r; + + dsi_vc_send_bta_sync(TCH); + + return r; +} + +static int taal_bl_update_status(struct backlight_device *dev) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + int level; + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + level = dev->props.brightness; + else + level = 0; + + dev_dbg(&dssdev->dev, "update brightness to %d\n", level); + + if (td->use_dsi_bl) { + if (td->enabled) { + dsi_bus_lock(); + r = taal_dcs_write_1(DCS_BRIGHTNESS, level); + dsi_bus_unlock(); + if (r) + return r; + } + } else { + if (!dssdev->set_backlight) + return -EINVAL; + + r = dssdev->set_backlight(dssdev, level); + if (r) + return r; + } + + return 0; +} + +static int taal_bl_get_intensity(struct backlight_device *dev) +{ + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + return dev->props.brightness; + + return 0; +} + +static struct backlight_ops taal_bl_ops = { + .get_brightness = taal_bl_get_intensity, + .update_status = taal_bl_update_status, +}; + +static void taal_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static void taal_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + if (td->rotate == 0 || td->rotate == 2) { + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; + } else { + *yres = dssdev->panel.timings.x_res; + *xres = dssdev->panel.timings.y_res; + } +} + +static irqreturn_t taal_te_isr(int irq, void *data) +{ + struct omap_dss_device *dssdev = data; + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + complete_all(&td->te_completion); + + return IRQ_HANDLED; +} + +static ssize_t taal_num_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + u8 errors; + int r; + + if (td->enabled) { + dsi_bus_lock(); + r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors); + dsi_bus_unlock(); + } else { + r = -ENODEV; + } + + if (r) + return r; + + return snprintf(buf, PAGE_SIZE, "%d\n", errors); +} + +static ssize_t taal_hw_revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + u8 id1, id2, id3; + int r; + + if (td->enabled) { + dsi_bus_lock(); + r = taal_get_id(&id1, &id2, &id3); + dsi_bus_unlock(); + } else { + r = -ENODEV; + } + + if (r) + return r; + + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); +} + +static const char *cabc_modes[] = { + "off", /* used also always when CABC is not supported */ + "ui", + "still-image", + "moving-image", +}; + +static ssize_t show_cabc_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + const char *mode_str; + int mode; + int len; + + mode = td->cabc_mode; + + mode_str = "unknown"; + if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) + mode_str = cabc_modes[mode]; + len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); + + return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; +} + +static ssize_t store_cabc_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { + if (sysfs_streq(cabc_modes[i], buf)) + break; + } + + if (i == ARRAY_SIZE(cabc_modes)) + return -EINVAL; + + if (td->enabled) { + dsi_bus_lock(); + if (!td->cabc_broken) + taal_dcs_write_1(DCS_WRITE_CABC, i); + dsi_bus_unlock(); + } + + td->cabc_mode = i; + + return count; +} + +static ssize_t show_cabc_available_modes(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int len; + int i; + + for (i = 0, len = 0; + len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) + len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", + i ? " " : "", cabc_modes[i], + i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); + + return len < PAGE_SIZE ? len : PAGE_SIZE - 1; +} + +static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL); +static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL); +static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, + show_cabc_mode, store_cabc_mode); +static DEVICE_ATTR(cabc_available_modes, S_IRUGO, + show_cabc_available_modes, NULL); + +static struct attribute *taal_attrs[] = { + &dev_attr_num_dsi_errors.attr, + &dev_attr_hw_revision.attr, + &dev_attr_cabc_mode.attr, + &dev_attr_cabc_available_modes.attr, + NULL, +}; + +static struct attribute_group taal_attr_group = { + .attrs = taal_attrs, +}; + +static int taal_probe(struct omap_dss_device *dssdev) +{ + struct taal_data *td; + struct backlight_device *bldev; + int r; + + const struct omap_video_timings taal_panel_timings = { + .x_res = 864, + .y_res = 480, + }; + + dev_dbg(&dssdev->dev, "probe\n"); + + dssdev->panel.config = OMAP_DSS_LCD_TFT; + dssdev->panel.timings = taal_panel_timings; + dssdev->ctrl.pixel_size = 24; + + td = kzalloc(sizeof(*td), GFP_KERNEL); + if (!td) { + r = -ENOMEM; + goto err0; + } + td->dssdev = dssdev; + + td->esd_wq = create_singlethread_workqueue("taal_esd"); + if (td->esd_wq == NULL) { + dev_err(&dssdev->dev, "can't create ESD workqueue\n"); + r = -ENOMEM; + goto err2; + } + INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work); + + dev_set_drvdata(&dssdev->dev, td); + + dssdev->get_timings = taal_get_timings; + dssdev->get_resolution = taal_get_resolution; + + /* if no platform set_backlight() defined, presume DSI backlight + * control */ + if (!dssdev->set_backlight) + td->use_dsi_bl = true; + + bldev = backlight_device_register("taal", &dssdev->dev, dssdev, + &taal_bl_ops); + if (IS_ERR(bldev)) { + r = PTR_ERR(bldev); + goto err1; + } + + td->bldev = bldev; + + bldev->props.fb_blank = FB_BLANK_UNBLANK; + bldev->props.power = FB_BLANK_UNBLANK; + if (td->use_dsi_bl) { + bldev->props.max_brightness = 255; + bldev->props.brightness = 255; + } else { + bldev->props.max_brightness = 127; + bldev->props.brightness = 127; + } + + taal_bl_update_status(bldev); + + if (dssdev->phy.dsi.ext_te) { + int gpio = dssdev->phy.dsi.ext_te_gpio; + + r = gpio_request(gpio, "taal irq"); + if (r) { + dev_err(&dssdev->dev, "GPIO request failed\n"); + goto err3; + } + + gpio_direction_input(gpio); + + r = request_irq(gpio_to_irq(gpio), taal_te_isr, + IRQF_DISABLED | IRQF_TRIGGER_RISING, + "taal vsync", dssdev); + + if (r) { + dev_err(&dssdev->dev, "IRQ request failed\n"); + gpio_free(gpio); + goto err3; + } + + init_completion(&td->te_completion); + + td->use_ext_te = true; + } + + r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group); + if (r) { + dev_err(&dssdev->dev, "failed to create sysfs files\n"); + goto err4; + } + + return 0; +err4: + if (td->use_ext_te) { + int gpio = dssdev->phy.dsi.ext_te_gpio; + free_irq(gpio_to_irq(gpio), dssdev); + gpio_free(gpio); + } +err3: + backlight_device_unregister(bldev); +err2: + cancel_delayed_work_sync(&td->esd_work); + destroy_workqueue(td->esd_wq); +err1: + kfree(td); +err0: + return r; +} + +static void taal_remove(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bldev; + + dev_dbg(&dssdev->dev, "remove\n"); + + sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group); + + if (td->use_ext_te) { + int gpio = dssdev->phy.dsi.ext_te_gpio; + free_irq(gpio_to_irq(gpio), dssdev); + gpio_free(gpio); + } + + bldev = td->bldev; + bldev->props.power = FB_BLANK_POWERDOWN; + taal_bl_update_status(bldev); + backlight_device_unregister(bldev); + + cancel_delayed_work_sync(&td->esd_work); + destroy_workqueue(td->esd_wq); + + kfree(td); +} + +static int taal_enable(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + u8 id1, id2, id3; + int r; + + dev_dbg(&dssdev->dev, "enable\n"); + + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + return r; + } + + /* it seems we have to wait a bit until taal is ready */ + msleep(5); + + r = taal_sleep_out(td); + if (r) + goto err; + + r = taal_get_id(&id1, &id2, &id3); + if (r) + goto err; + + /* on early revisions CABC is broken */ + if (id2 == 0x00 || id2 == 0xff || id2 == 0x81) + td->cabc_broken = true; + + taal_dcs_write_1(DCS_BRIGHTNESS, 0xff); + taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */ + + taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ + + taal_set_addr_mode(td->rotate, td->mirror); + if (!td->cabc_broken) + taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode); + + taal_dcs_write_0(DCS_DISPLAY_ON); + +#ifdef TAAL_USE_ESD_CHECK + queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); +#endif + + td->enabled = 1; + + if (!td->intro_printed) { + dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n", + id1, id2, id3); + if (td->cabc_broken) + dev_info(&dssdev->dev, + "old Taal version, CABC disabled\n"); + td->intro_printed = true; + } + + return 0; +err: + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + return r; +} + +static void taal_disable(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + dev_dbg(&dssdev->dev, "disable\n"); + + cancel_delayed_work(&td->esd_work); + + taal_dcs_write_0(DCS_DISPLAY_OFF); + taal_sleep_in(td); + + /* wait a bit so that the message goes through */ + msleep(10); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + td->enabled = 0; +} + +static int taal_suspend(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bldev = td->bldev; + + bldev->props.power = FB_BLANK_POWERDOWN; + taal_bl_update_status(bldev); + + return 0; +} + +static int taal_resume(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bldev = td->bldev; + + bldev->props.power = FB_BLANK_UNBLANK; + taal_bl_update_status(bldev); + + return 0; +} + +static void taal_setup_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + taal_set_update_window(x, y, w, h); +} + +static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + + td->te_enabled = enable; + + if (enable) + r = taal_dcs_write_1(DCS_TEAR_ON, 0); + else + r = taal_dcs_write_0(DCS_TEAR_OFF); + + return r; +} + +static int taal_wait_te(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + long wait = msecs_to_jiffies(500); + + if (!td->use_ext_te || !td->te_enabled) + return 0; + + INIT_COMPLETION(td->te_completion); + wait = wait_for_completion_timeout(&td->te_completion, wait); + if (wait == 0) { + dev_err(&dssdev->dev, "timeout waiting TE\n"); + return -ETIME; + } + + return 0; +} + +static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + + dev_dbg(&dssdev->dev, "rotate %d\n", rotate); + + if (td->enabled) { + r = taal_set_addr_mode(rotate, td->mirror); + + if (r) + return r; + } + + td->rotate = rotate; + + return 0; +} + +static u8 taal_get_rotate(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + return td->rotate; +} + +static int taal_mirror(struct omap_dss_device *dssdev, bool enable) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + + dev_dbg(&dssdev->dev, "mirror %d\n", enable); + + if (td->enabled) { + r = taal_set_addr_mode(td->rotate, enable); + + if (r) + return r; + } + + td->mirror = enable; + + return 0; +} + +static bool taal_get_mirror(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + return td->mirror; +} + +static int taal_run_test(struct omap_dss_device *dssdev, int test_num) +{ + u8 id1, id2, id3; + int r; + + r = taal_dcs_read_1(DCS_GET_ID1, &id1); + if (r) + return r; + r = taal_dcs_read_1(DCS_GET_ID2, &id2); + if (r) + return r; + r = taal_dcs_read_1(DCS_GET_ID3, &id3); + if (r) + return r; + + return 0; +} + +static int taal_memory_read(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h) +{ + int r; + int first = 1; + int plen; + unsigned buf_used = 0; + + if (size < w * h * 3) + return -ENOMEM; + + size = min(w * h * 3, + dssdev->panel.timings.x_res * + dssdev->panel.timings.y_res * 3); + + /* plen 1 or 2 goes into short packet. until checksum error is fixed, + * use short packets. plen 32 works, but bigger packets seem to cause + * an error. */ + if (size % 2) + plen = 1; + else + plen = 2; + + taal_setup_update(dssdev, x, y, w, h); + + r = dsi_vc_set_max_rx_packet_size(TCH, plen); + if (r) + return r; + + while (buf_used < size) { + u8 dcs_cmd = first ? 0x2e : 0x3e; + first = 0; + + r = dsi_vc_dcs_read(TCH, dcs_cmd, + buf + buf_used, size - buf_used); + + if (r < 0) { + dev_err(&dssdev->dev, "read error\n"); + goto err; + } + + buf_used += r; + + if (r < plen) { + dev_err(&dssdev->dev, "short read\n"); + break; + } + + if (signal_pending(current)) { + dev_err(&dssdev->dev, "signal pending, " + "aborting memory read\n"); + r = -ERESTARTSYS; + goto err; + } + } + + r = buf_used; + +err: + dsi_vc_set_max_rx_packet_size(TCH, 1); + + return r; +} + +static void taal_esd_work(struct work_struct *work) +{ + struct taal_data *td = container_of(work, struct taal_data, + esd_work.work); + struct omap_dss_device *dssdev = td->dssdev; + u8 state1, state2; + int r; + + if (!td->enabled) + return; + + dsi_bus_lock(); + + r = taal_dcs_read_1(DCS_RDDSDR, &state1); + if (r) { + dev_err(&dssdev->dev, "failed to read Taal status\n"); + goto err; + } + + /* Run self diagnostics */ + r = taal_sleep_out(td); + if (r) { + dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n"); + goto err; + } + + r = taal_dcs_read_1(DCS_RDDSDR, &state2); + if (r) { + dev_err(&dssdev->dev, "failed to read Taal status\n"); + goto err; + } + + /* Each sleep out command will trigger a self diagnostic and flip + * Bit6 if the test passes. + */ + if (!((state1 ^ state2) & (1 << 6))) { + dev_err(&dssdev->dev, "LCD self diagnostics failed\n"); + goto err; + } + /* Self-diagnostics result is also shown on TE GPIO line. We need + * to re-enable TE after self diagnostics */ + if (td->use_ext_te && td->te_enabled) + taal_enable_te(dssdev, true); + + dsi_bus_unlock(); + + queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); + + return; +err: + dev_err(&dssdev->dev, "performing LCD reset\n"); + + taal_disable(dssdev); + taal_enable(dssdev); + + dsi_bus_unlock(); + + queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); +} + +static struct omap_dss_driver taal_driver = { + .probe = taal_probe, + .remove = taal_remove, + + .enable = taal_enable, + .disable = taal_disable, + .suspend = taal_suspend, + .resume = taal_resume, + + .setup_update = taal_setup_update, + .enable_te = taal_enable_te, + .wait_for_te = taal_wait_te, + .set_rotate = taal_rotate, + .get_rotate = taal_get_rotate, + .set_mirror = taal_mirror, + .get_mirror = taal_get_mirror, + .run_test = taal_run_test, + .memory_read = taal_memory_read, + + .driver = { + .name = "taal", + .owner = THIS_MODULE, + }, +}; + +static int __init taal_init(void) +{ + omap_dss_register_driver(&taal_driver); + + return 0; +} + +static void __exit taal_exit(void) +{ + omap_dss_unregister_driver(&taal_driver); +} + +module_init(taal_init); +module_exit(taal_exit); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("Taal Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig new file mode 100644 index 000000000000..71d8dec30635 --- /dev/null +++ b/drivers/video/omap2/dss/Kconfig @@ -0,0 +1,89 @@ +menuconfig OMAP2_DSS + tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)" + depends on ARCH_OMAP2 || ARCH_OMAP3 + help + OMAP2/3 Display Subsystem support. + +if OMAP2_DSS + +config OMAP2_VRAM_SIZE + int "VRAM size (MB)" + range 0 32 + default 0 + help + The amount of SDRAM to reserve at boot time for video RAM use. + This VRAM will be used by omapfb and other drivers that need + large continuous RAM area for video use. + + You can also set this with "vram=<bytes>" kernel argument, or + in the board file. + +config OMAP2_DSS_DEBUG_SUPPORT + bool "Debug support" + default y + help + This enables debug messages. You need to enable printing + with 'debug' module parameter. + +config OMAP2_DSS_RFBI + bool "RFBI support" + default n + help + MIPI DBI, or RFBI (Remote Framebuffer Interface), support. + +config OMAP2_DSS_VENC + bool "VENC support" + default y + help + OMAP Video Encoder support. + +config OMAP2_DSS_SDI + bool "SDI support" + depends on ARCH_OMAP3 + default n + help + SDI (Serial Display Interface) support. + +config OMAP2_DSS_DSI + bool "DSI support" + depends on ARCH_OMAP3 + default n + help + MIPI DSI support. + +config OMAP2_DSS_USE_DSI_PLL + bool "Use DSI PLL for PCLK (EXPERIMENTAL)" + default n + depends on OMAP2_DSS_DSI + help + Use DSI PLL to generate pixel clock. Currently only for DPI output. + DSI PLL can be used to generate higher and more precise pixel clocks. + +config OMAP2_DSS_FAKE_VSYNC + bool "Fake VSYNC irq from manual update displays" + default n + help + If this is selected, DSI will generate a fake DISPC VSYNC interrupt + when DSI has sent a frame. This is only needed with DSI or RFBI + displays using manual mode, and you want VSYNC to, for example, + time animation. + +config OMAP2_DSS_MIN_FCK_PER_PCK + int "Minimum FCK/PCK ratio (for scaling)" + range 0 32 + default 0 + help + This can be used to adjust the minimum FCK/PCK ratio. + + With this you can make sure that DISPC FCK is at least + n x PCK. Video plane scaling requires higher FCK than + normally. + + If this is set to 0, there's no extra constraint on the + DISPC FCK. However, the FCK will at minimum be + 2xPCK (if active matrix) or 3xPCK (if passive matrix). + + Max FCK is 173MHz, so this doesn't work if your PCK + is very high. + +endif diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile new file mode 100644 index 000000000000..980c72c2db98 --- /dev/null +++ b/drivers/video/omap2/dss/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_OMAP2_DSS) += omapdss.o +omapdss-y := core.o dss.o dispc.o dpi.o display.o manager.o overlay.o +omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o +omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o +omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o +omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c new file mode 100644 index 000000000000..29497a0c9a91 --- /dev/null +++ b/drivers/video/omap2/dss/core.c @@ -0,0 +1,919 @@ +/* + * linux/drivers/video/omap2/dss/core.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "CORE" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/device.h> + +#include <plat/display.h> +#include <plat/clock.h> + +#include "dss.h" + +static struct { + struct platform_device *pdev; + int ctx_id; + + struct clk *dss_ick; + struct clk *dss1_fck; + struct clk *dss2_fck; + struct clk *dss_54m_fck; + struct clk *dss_96m_fck; + unsigned num_clks_enabled; +} core; + +static void dss_clk_enable_all_no_ctx(void); +static void dss_clk_disable_all_no_ctx(void); +static void dss_clk_enable_no_ctx(enum dss_clock clks); +static void dss_clk_disable_no_ctx(enum dss_clock clks); + +static char *def_disp_name; +module_param_named(def_disp, def_disp_name, charp, 0); +MODULE_PARM_DESC(def_disp_name, "default display name"); + +#ifdef DEBUG +unsigned int dss_debug; +module_param_named(debug, dss_debug, bool, 0644); +#endif + +/* CONTEXT */ +static int dss_get_ctx_id(void) +{ + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + int r; + + if (!pdata->get_last_off_on_transaction_id) + return 0; + r = pdata->get_last_off_on_transaction_id(&core.pdev->dev); + if (r < 0) { + dev_err(&core.pdev->dev, "getting transaction ID failed, " + "will force context restore\n"); + r = -1; + } + return r; +} + +int dss_need_ctx_restore(void) +{ + int id = dss_get_ctx_id(); + + if (id < 0 || id != core.ctx_id) { + DSSDBG("ctx id %d -> id %d\n", + core.ctx_id, id); + core.ctx_id = id; + return 1; + } else { + return 0; + } +} + +static void save_all_ctx(void) +{ + DSSDBG("save context\n"); + + dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1); + + dss_save_context(); + dispc_save_context(); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_save_context(); +#endif + + dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +static void restore_all_ctx(void) +{ + DSSDBG("restore context\n"); + + dss_clk_enable_all_no_ctx(); + + dss_restore_context(); + dispc_restore_context(); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_restore_context(); +#endif + + dss_clk_disable_all_no_ctx(); +} + +/* CLOCKS */ +static void core_dump_clocks(struct seq_file *s) +{ + int i; + struct clk *clocks[5] = { + core.dss_ick, + core.dss1_fck, + core.dss2_fck, + core.dss_54m_fck, + core.dss_96m_fck + }; + + seq_printf(s, "- CORE -\n"); + + seq_printf(s, "internal clk count\t\t%u\n", core.num_clks_enabled); + + for (i = 0; i < 5; i++) { + if (!clocks[i]) + continue; + seq_printf(s, "%-15s\t%lu\t%d\n", + clocks[i]->name, + clk_get_rate(clocks[i]), + clocks[i]->usecount); + } +} + +static int dss_get_clock(struct clk **clock, const char *clk_name) +{ + struct clk *clk; + + clk = clk_get(&core.pdev->dev, clk_name); + + if (IS_ERR(clk)) { + DSSERR("can't get clock %s", clk_name); + return PTR_ERR(clk); + } + + *clock = clk; + + DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk)); + + return 0; +} + +static int dss_get_clocks(void) +{ + int r; + + core.dss_ick = NULL; + core.dss1_fck = NULL; + core.dss2_fck = NULL; + core.dss_54m_fck = NULL; + core.dss_96m_fck = NULL; + + r = dss_get_clock(&core.dss_ick, "ick"); + if (r) + goto err; + + r = dss_get_clock(&core.dss1_fck, "dss1_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss2_fck, "dss2_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss_54m_fck, "tv_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss_96m_fck, "video_fck"); + if (r) + goto err; + + return 0; + +err: + if (core.dss_ick) + clk_put(core.dss_ick); + if (core.dss1_fck) + clk_put(core.dss1_fck); + if (core.dss2_fck) + clk_put(core.dss2_fck); + if (core.dss_54m_fck) + clk_put(core.dss_54m_fck); + if (core.dss_96m_fck) + clk_put(core.dss_96m_fck); + + return r; +} + +static void dss_put_clocks(void) +{ + if (core.dss_96m_fck) + clk_put(core.dss_96m_fck); + clk_put(core.dss_54m_fck); + clk_put(core.dss1_fck); + clk_put(core.dss2_fck); + clk_put(core.dss_ick); +} + +unsigned long dss_clk_get_rate(enum dss_clock clk) +{ + switch (clk) { + case DSS_CLK_ICK: + return clk_get_rate(core.dss_ick); + case DSS_CLK_FCK1: + return clk_get_rate(core.dss1_fck); + case DSS_CLK_FCK2: + return clk_get_rate(core.dss2_fck); + case DSS_CLK_54M: + return clk_get_rate(core.dss_54m_fck); + case DSS_CLK_96M: + return clk_get_rate(core.dss_96m_fck); + } + + BUG(); + return 0; +} + +static unsigned count_clk_bits(enum dss_clock clks) +{ + unsigned num_clks = 0; + + if (clks & DSS_CLK_ICK) + ++num_clks; + if (clks & DSS_CLK_FCK1) + ++num_clks; + if (clks & DSS_CLK_FCK2) + ++num_clks; + if (clks & DSS_CLK_54M) + ++num_clks; + if (clks & DSS_CLK_96M) + ++num_clks; + + return num_clks; +} + +static void dss_clk_enable_no_ctx(enum dss_clock clks) +{ + unsigned num_clks = count_clk_bits(clks); + + if (clks & DSS_CLK_ICK) + clk_enable(core.dss_ick); + if (clks & DSS_CLK_FCK1) + clk_enable(core.dss1_fck); + if (clks & DSS_CLK_FCK2) + clk_enable(core.dss2_fck); + if (clks & DSS_CLK_54M) + clk_enable(core.dss_54m_fck); + if (clks & DSS_CLK_96M) + clk_enable(core.dss_96m_fck); + + core.num_clks_enabled += num_clks; +} + +void dss_clk_enable(enum dss_clock clks) +{ + dss_clk_enable_no_ctx(clks); + + if (cpu_is_omap34xx() && dss_need_ctx_restore()) + restore_all_ctx(); +} + +static void dss_clk_disable_no_ctx(enum dss_clock clks) +{ + unsigned num_clks = count_clk_bits(clks); + + if (clks & DSS_CLK_ICK) + clk_disable(core.dss_ick); + if (clks & DSS_CLK_FCK1) + clk_disable(core.dss1_fck); + if (clks & DSS_CLK_FCK2) + clk_disable(core.dss2_fck); + if (clks & DSS_CLK_54M) + clk_disable(core.dss_54m_fck); + if (clks & DSS_CLK_96M) + clk_disable(core.dss_96m_fck); + + core.num_clks_enabled -= num_clks; +} + +void dss_clk_disable(enum dss_clock clks) +{ + if (cpu_is_omap34xx()) { + unsigned num_clks = count_clk_bits(clks); + + BUG_ON(core.num_clks_enabled < num_clks); + + if (core.num_clks_enabled == num_clks) + save_all_ctx(); + } + + dss_clk_disable_no_ctx(clks); +} + +static void dss_clk_enable_all_no_ctx(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx()) + clks |= DSS_CLK_96M; + dss_clk_enable_no_ctx(clks); +} + +static void dss_clk_disable_all_no_ctx(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx()) + clks |= DSS_CLK_96M; + dss_clk_disable_no_ctx(clks); +} + +static void dss_clk_disable_all(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx()) + clks |= DSS_CLK_96M; + dss_clk_disable(clks); +} + +/* DEBUGFS */ +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) +static void dss_debug_dump_clocks(struct seq_file *s) +{ + core_dump_clocks(s); + dss_dump_clocks(s); + dispc_dump_clocks(s); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_dump_clocks(s); +#endif +} + +static int dss_debug_show(struct seq_file *s, void *unused) +{ + void (*func)(struct seq_file *) = s->private; + func(s); + return 0; +} + +static int dss_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, dss_debug_show, inode->i_private); +} + +static const struct file_operations dss_debug_fops = { + .open = dss_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *dss_debugfs_dir; + +static int dss_initialize_debugfs(void) +{ + dss_debugfs_dir = debugfs_create_dir("omapdss", NULL); + if (IS_ERR(dss_debugfs_dir)) { + int err = PTR_ERR(dss_debugfs_dir); + dss_debugfs_dir = NULL; + return err; + } + + debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, + &dss_debug_dump_clocks, &dss_debug_fops); + + debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, + &dss_dump_regs, &dss_debug_fops); + debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir, + &dispc_dump_regs, &dss_debug_fops); +#ifdef CONFIG_OMAP2_DSS_RFBI + debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir, + &rfbi_dump_regs, &dss_debug_fops); +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + debugfs_create_file("dsi", S_IRUGO, dss_debugfs_dir, + &dsi_dump_regs, &dss_debug_fops); +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, + &venc_dump_regs, &dss_debug_fops); +#endif + return 0; +} + +static void dss_uninitialize_debugfs(void) +{ + if (dss_debugfs_dir) + debugfs_remove_recursive(dss_debugfs_dir); +} +#endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ + +/* PLATFORM DEVICE */ +static int omap_dss_probe(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int skip_init = 0; + int r; + int i; + + core.pdev = pdev; + + dss_init_overlay_managers(pdev); + dss_init_overlays(pdev); + + r = dss_get_clocks(); + if (r) + goto fail0; + + dss_clk_enable_all_no_ctx(); + + core.ctx_id = dss_get_ctx_id(); + DSSDBG("initial ctx id %u\n", core.ctx_id); + +#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT + /* DISPC_CONTROL */ + if (omap_readl(0x48050440) & 1) /* LCD enabled? */ + skip_init = 1; +#endif + + r = dss_init(skip_init); + if (r) { + DSSERR("Failed to initialize DSS\n"); + goto fail0; + } + +#ifdef CONFIG_OMAP2_DSS_RFBI + r = rfbi_init(); + if (r) { + DSSERR("Failed to initialize rfbi\n"); + goto fail0; + } +#endif + + r = dpi_init(); + if (r) { + DSSERR("Failed to initialize dpi\n"); + goto fail0; + } + + r = dispc_init(); + if (r) { + DSSERR("Failed to initialize dispc\n"); + goto fail0; + } +#ifdef CONFIG_OMAP2_DSS_VENC + r = venc_init(pdev); + if (r) { + DSSERR("Failed to initialize venc\n"); + goto fail0; + } +#endif + if (cpu_is_omap34xx()) { +#ifdef CONFIG_OMAP2_DSS_SDI + r = sdi_init(skip_init); + if (r) { + DSSERR("Failed to initialize SDI\n"); + goto fail0; + } +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + r = dsi_init(pdev); + if (r) { + DSSERR("Failed to initialize DSI\n"); + goto fail0; + } +#endif + } + +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) + r = dss_initialize_debugfs(); + if (r) + goto fail0; +#endif + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + r = omap_dss_register_device(dssdev); + if (r) + DSSERR("device reg failed %d\n", i); + + if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0) + pdata->default_device = dssdev; + } + + dss_clk_disable_all(); + + return 0; + + /* XXX fail correctly */ +fail0: + return r; +} + +static int omap_dss_remove(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i; + int c; + +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) + dss_uninitialize_debugfs(); +#endif + +#ifdef CONFIG_OMAP2_DSS_VENC + venc_exit(); +#endif + dispc_exit(); + dpi_exit(); +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_exit(); +#endif + if (cpu_is_omap34xx()) { +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_exit(); +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_exit(); +#endif + } + + dss_exit(); + + /* these should be removed at some point */ + c = core.dss_ick->usecount; + if (c > 0) { + DSSERR("warning: dss_ick usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss_ick); + } + + c = core.dss1_fck->usecount; + if (c > 0) { + DSSERR("warning: dss1_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss1_fck); + } + + c = core.dss2_fck->usecount; + if (c > 0) { + DSSERR("warning: dss2_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss2_fck); + } + + c = core.dss_54m_fck->usecount; + if (c > 0) { + DSSERR("warning: dss_54m_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss_54m_fck); + } + + if (core.dss_96m_fck) { + c = core.dss_96m_fck->usecount; + if (c > 0) { + DSSERR("warning: dss_96m_fck usecount %d, disabling\n", + c); + while (c-- > 0) + clk_disable(core.dss_96m_fck); + } + } + + dss_put_clocks(); + + dss_uninit_overlays(pdev); + dss_uninit_overlay_managers(pdev); + + for (i = 0; i < pdata->num_devices; ++i) + omap_dss_unregister_device(pdata->devices[i]); + + return 0; +} + +static void omap_dss_shutdown(struct platform_device *pdev) +{ + DSSDBG("shutdown\n"); + dss_disable_all_devices(); +} + +static int omap_dss_suspend(struct platform_device *pdev, pm_message_t state) +{ + DSSDBG("suspend %d\n", state.event); + + return dss_suspend_all_devices(); +} + +static int omap_dss_resume(struct platform_device *pdev) +{ + DSSDBG("resume\n"); + + return dss_resume_all_devices(); +} + +static struct platform_driver omap_dss_driver = { + .probe = omap_dss_probe, + .remove = omap_dss_remove, + .shutdown = omap_dss_shutdown, + .suspend = omap_dss_suspend, + .resume = omap_dss_resume, + .driver = { + .name = "omapdss", + .owner = THIS_MODULE, + }, +}; + +/* BUS */ +static int dss_bus_match(struct device *dev, struct device_driver *driver) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + + DSSDBG("bus_match. dev %s/%s, drv %s\n", + dev_name(dev), dssdev->driver_name, driver->name); + + return strcmp(dssdev->driver_name, driver->name) == 0; +} + +static ssize_t device_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", + dssdev->name ? + dssdev->name : ""); +} + +static struct device_attribute default_dev_attrs[] = { + __ATTR(name, S_IRUGO, device_name_show, NULL), + __ATTR_NULL, +}; + +static ssize_t driver_name_show(struct device_driver *drv, char *buf) +{ + struct omap_dss_driver *dssdrv = to_dss_driver(drv); + return snprintf(buf, PAGE_SIZE, "%s\n", + dssdrv->driver.name ? + dssdrv->driver.name : ""); +} +static struct driver_attribute default_drv_attrs[] = { + __ATTR(name, S_IRUGO, driver_name_show, NULL), + __ATTR_NULL, +}; + +static struct bus_type dss_bus_type = { + .name = "omapdss", + .match = dss_bus_match, + .dev_attrs = default_dev_attrs, + .drv_attrs = default_drv_attrs, +}; + +static void dss_bus_release(struct device *dev) +{ + DSSDBG("bus_release\n"); +} + +static struct device dss_bus = { + .release = dss_bus_release, +}; + +struct bus_type *dss_get_bus(void) +{ + return &dss_bus_type; +} + +/* DRIVER */ +static int dss_driver_probe(struct device *dev) +{ + int r; + struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + bool force; + + DSSDBG("driver_probe: dev %s/%s, drv %s\n", + dev_name(dev), dssdev->driver_name, + dssdrv->driver.name); + + dss_init_device(core.pdev, dssdev); + + /* skip this if the device is behind a ctrl */ + if (!dssdev->panel.ctrl) { + force = pdata->default_device == dssdev; + dss_recheck_connections(dssdev, force); + } + + r = dssdrv->probe(dssdev); + + if (r) { + DSSERR("driver probe failed: %d\n", r); + return r; + } + + DSSDBG("probe done for device %s\n", dev_name(dev)); + + dssdev->driver = dssdrv; + + return 0; +} + +static int dss_driver_remove(struct device *dev) +{ + struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); + struct omap_dss_device *dssdev = to_dss_device(dev); + + DSSDBG("driver_remove: dev %s/%s\n", dev_name(dev), + dssdev->driver_name); + + dssdrv->remove(dssdev); + + dss_uninit_device(core.pdev, dssdev); + + dssdev->driver = NULL; + + return 0; +} + +int omap_dss_register_driver(struct omap_dss_driver *dssdriver) +{ + dssdriver->driver.bus = &dss_bus_type; + dssdriver->driver.probe = dss_driver_probe; + dssdriver->driver.remove = dss_driver_remove; + return driver_register(&dssdriver->driver); +} +EXPORT_SYMBOL(omap_dss_register_driver); + +void omap_dss_unregister_driver(struct omap_dss_driver *dssdriver) +{ + driver_unregister(&dssdriver->driver); +} +EXPORT_SYMBOL(omap_dss_unregister_driver); + +/* DEVICE */ +static void reset_device(struct device *dev, int check) +{ + u8 *dev_p = (u8 *)dev; + u8 *dev_end = dev_p + sizeof(*dev); + void *saved_pdata; + + saved_pdata = dev->platform_data; + if (check) { + /* + * Check if there is any other setting than platform_data + * in struct device; warn that these will be reset by our + * init. + */ + dev->platform_data = NULL; + while (dev_p < dev_end) { + if (*dev_p) { + WARN("%s: struct device fields will be " + "discarded\n", + __func__); + break; + } + dev_p++; + } + } + memset(dev, 0, sizeof(*dev)); + dev->platform_data = saved_pdata; +} + + +static void omap_dss_dev_release(struct device *dev) +{ + reset_device(dev, 0); +} + +int omap_dss_register_device(struct omap_dss_device *dssdev) +{ + static int dev_num; + static int panel_num; + int r; + + WARN_ON(!dssdev->driver_name); + + reset_device(&dssdev->dev, 1); + dssdev->dev.bus = &dss_bus_type; + dssdev->dev.parent = &dss_bus; + dssdev->dev.release = omap_dss_dev_release; + dev_set_name(&dssdev->dev, "display%d", dev_num++); + r = device_register(&dssdev->dev); + if (r) + return r; + + if (dssdev->ctrl.panel) { + struct omap_dss_device *panel = dssdev->ctrl.panel; + + panel->panel.ctrl = dssdev; + + reset_device(&panel->dev, 1); + panel->dev.bus = &dss_bus_type; + panel->dev.parent = &dssdev->dev; + panel->dev.release = omap_dss_dev_release; + dev_set_name(&panel->dev, "panel%d", panel_num++); + r = device_register(&panel->dev); + if (r) + return r; + } + + return 0; +} + +void omap_dss_unregister_device(struct omap_dss_device *dssdev) +{ + device_unregister(&dssdev->dev); + + if (dssdev->ctrl.panel) { + struct omap_dss_device *panel = dssdev->ctrl.panel; + device_unregister(&panel->dev); + } +} + +/* BUS */ +static int omap_dss_bus_register(void) +{ + int r; + + r = bus_register(&dss_bus_type); + if (r) { + DSSERR("bus register failed\n"); + return r; + } + + dev_set_name(&dss_bus, "omapdss"); + r = device_register(&dss_bus); + if (r) { + DSSERR("bus driver register failed\n"); + bus_unregister(&dss_bus_type); + return r; + } + + return 0; +} + +/* INIT */ + +#ifdef CONFIG_OMAP2_DSS_MODULE +static void omap_dss_bus_unregister(void) +{ + device_unregister(&dss_bus); + + bus_unregister(&dss_bus_type); +} + +static int __init omap_dss_init(void) +{ + int r; + + r = omap_dss_bus_register(); + if (r) + return r; + + r = platform_driver_register(&omap_dss_driver); + if (r) { + omap_dss_bus_unregister(); + return r; + } + + return 0; +} + +static void __exit omap_dss_exit(void) +{ + platform_driver_unregister(&omap_dss_driver); + + omap_dss_bus_unregister(); +} + +module_init(omap_dss_init); +module_exit(omap_dss_exit); +#else +static int __init omap_dss_init(void) +{ + return omap_dss_bus_register(); +} + +static int __init omap_dss_init2(void) +{ + return platform_driver_register(&omap_dss_driver); +} + +core_initcall(omap_dss_init); +device_initcall(omap_dss_init2); +#endif + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("OMAP2/3 Display Subsystem"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c new file mode 100644 index 000000000000..6dabf4b2f005 --- /dev/null +++ b/drivers/video/omap2/dss/dispc.c @@ -0,0 +1,3091 @@ +/* + * linux/drivers/video/omap2/dss/dispc.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DISPC" + +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/seq_file.h> +#include <linux/delay.h> +#include <linux/workqueue.h> + +#include <plat/sram.h> +#include <plat/clock.h> + +#include <plat/display.h> + +#include "dss.h" + +/* DISPC */ +#define DISPC_BASE 0x48050400 + +#define DISPC_SZ_REGS SZ_1K + +struct dispc_reg { u16 idx; }; + +#define DISPC_REG(idx) ((const struct dispc_reg) { idx }) + +/* DISPC common */ +#define DISPC_REVISION DISPC_REG(0x0000) +#define DISPC_SYSCONFIG DISPC_REG(0x0010) +#define DISPC_SYSSTATUS DISPC_REG(0x0014) +#define DISPC_IRQSTATUS DISPC_REG(0x0018) +#define DISPC_IRQENABLE DISPC_REG(0x001C) +#define DISPC_CONTROL DISPC_REG(0x0040) +#define DISPC_CONFIG DISPC_REG(0x0044) +#define DISPC_CAPABLE DISPC_REG(0x0048) +#define DISPC_DEFAULT_COLOR0 DISPC_REG(0x004C) +#define DISPC_DEFAULT_COLOR1 DISPC_REG(0x0050) +#define DISPC_TRANS_COLOR0 DISPC_REG(0x0054) +#define DISPC_TRANS_COLOR1 DISPC_REG(0x0058) +#define DISPC_LINE_STATUS DISPC_REG(0x005C) +#define DISPC_LINE_NUMBER DISPC_REG(0x0060) +#define DISPC_TIMING_H DISPC_REG(0x0064) +#define DISPC_TIMING_V DISPC_REG(0x0068) +#define DISPC_POL_FREQ DISPC_REG(0x006C) +#define DISPC_DIVISOR DISPC_REG(0x0070) +#define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074) +#define DISPC_SIZE_DIG DISPC_REG(0x0078) +#define DISPC_SIZE_LCD DISPC_REG(0x007C) + +/* DISPC GFX plane */ +#define DISPC_GFX_BA0 DISPC_REG(0x0080) +#define DISPC_GFX_BA1 DISPC_REG(0x0084) +#define DISPC_GFX_POSITION DISPC_REG(0x0088) +#define DISPC_GFX_SIZE DISPC_REG(0x008C) +#define DISPC_GFX_ATTRIBUTES DISPC_REG(0x00A0) +#define DISPC_GFX_FIFO_THRESHOLD DISPC_REG(0x00A4) +#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8) +#define DISPC_GFX_ROW_INC DISPC_REG(0x00AC) +#define DISPC_GFX_PIXEL_INC DISPC_REG(0x00B0) +#define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4) +#define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8) + +#define DISPC_DATA_CYCLE1 DISPC_REG(0x01D4) +#define DISPC_DATA_CYCLE2 DISPC_REG(0x01D8) +#define DISPC_DATA_CYCLE3 DISPC_REG(0x01DC) + +#define DISPC_CPR_COEF_R DISPC_REG(0x0220) +#define DISPC_CPR_COEF_G DISPC_REG(0x0224) +#define DISPC_CPR_COEF_B DISPC_REG(0x0228) + +#define DISPC_GFX_PRELOAD DISPC_REG(0x022C) + +/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */ +#define DISPC_VID_REG(n, idx) DISPC_REG(0x00BC + (n)*0x90 + idx) + +#define DISPC_VID_BA0(n) DISPC_VID_REG(n, 0x0000) +#define DISPC_VID_BA1(n) DISPC_VID_REG(n, 0x0004) +#define DISPC_VID_POSITION(n) DISPC_VID_REG(n, 0x0008) +#define DISPC_VID_SIZE(n) DISPC_VID_REG(n, 0x000C) +#define DISPC_VID_ATTRIBUTES(n) DISPC_VID_REG(n, 0x0010) +#define DISPC_VID_FIFO_THRESHOLD(n) DISPC_VID_REG(n, 0x0014) +#define DISPC_VID_FIFO_SIZE_STATUS(n) DISPC_VID_REG(n, 0x0018) +#define DISPC_VID_ROW_INC(n) DISPC_VID_REG(n, 0x001C) +#define DISPC_VID_PIXEL_INC(n) DISPC_VID_REG(n, 0x0020) +#define DISPC_VID_FIR(n) DISPC_VID_REG(n, 0x0024) +#define DISPC_VID_PICTURE_SIZE(n) DISPC_VID_REG(n, 0x0028) +#define DISPC_VID_ACCU0(n) DISPC_VID_REG(n, 0x002C) +#define DISPC_VID_ACCU1(n) DISPC_VID_REG(n, 0x0030) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_H(n, i) DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8) +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_HV(n, i) DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8) +/* coef index i = {0, 1, 2, 3, 4} */ +#define DISPC_VID_CONV_COEF(n, i) DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4) +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_V(n, i) DISPC_REG(0x01E0 + (n)*0x20 + (i)*0x4) + +#define DISPC_VID_PRELOAD(n) DISPC_REG(0x230 + (n)*0x04) + + +#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ + DISPC_IRQ_OCP_ERR | \ + DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ + DISPC_IRQ_SYNC_LOST | \ + DISPC_IRQ_SYNC_LOST_DIGIT) + +#define DISPC_MAX_NR_ISRS 8 + +struct omap_dispc_isr_data { + omap_dispc_isr_t isr; + void *arg; + u32 mask; +}; + +#define REG_GET(idx, start, end) \ + FLD_GET(dispc_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) + +static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES, + DISPC_VID_ATTRIBUTES(0), + DISPC_VID_ATTRIBUTES(1) }; + +static struct { + void __iomem *base; + + u32 fifo_size[3]; + + spinlock_t irq_lock; + u32 irq_error_mask; + struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; + u32 error_irqs; + struct work_struct error_work; + + u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; +} dispc; + +static void _omap_dispc_set_irqs(void); + +static inline void dispc_write_reg(const struct dispc_reg idx, u32 val) +{ + __raw_writel(val, dispc.base + idx.idx); +} + +static inline u32 dispc_read_reg(const struct dispc_reg idx) +{ + return __raw_readl(dispc.base + idx.idx); +} + +#define SR(reg) \ + dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg) +#define RR(reg) \ + dispc_write_reg(DISPC_##reg, dispc.ctx[(DISPC_##reg).idx / sizeof(u32)]) + +void dispc_save_context(void) +{ + if (cpu_is_omap24xx()) + return; + + SR(SYSCONFIG); + SR(IRQENABLE); + SR(CONTROL); + SR(CONFIG); + SR(DEFAULT_COLOR0); + SR(DEFAULT_COLOR1); + SR(TRANS_COLOR0); + SR(TRANS_COLOR1); + SR(LINE_NUMBER); + SR(TIMING_H); + SR(TIMING_V); + SR(POL_FREQ); + SR(DIVISOR); + SR(GLOBAL_ALPHA); + SR(SIZE_DIG); + SR(SIZE_LCD); + + SR(GFX_BA0); + SR(GFX_BA1); + SR(GFX_POSITION); + SR(GFX_SIZE); + SR(GFX_ATTRIBUTES); + SR(GFX_FIFO_THRESHOLD); + SR(GFX_ROW_INC); + SR(GFX_PIXEL_INC); + SR(GFX_WINDOW_SKIP); + SR(GFX_TABLE_BA); + + SR(DATA_CYCLE1); + SR(DATA_CYCLE2); + SR(DATA_CYCLE3); + + SR(CPR_COEF_R); + SR(CPR_COEF_G); + SR(CPR_COEF_B); + + SR(GFX_PRELOAD); + + /* VID1 */ + SR(VID_BA0(0)); + SR(VID_BA1(0)); + SR(VID_POSITION(0)); + SR(VID_SIZE(0)); + SR(VID_ATTRIBUTES(0)); + SR(VID_FIFO_THRESHOLD(0)); + SR(VID_ROW_INC(0)); + SR(VID_PIXEL_INC(0)); + SR(VID_FIR(0)); + SR(VID_PICTURE_SIZE(0)); + SR(VID_ACCU0(0)); + SR(VID_ACCU1(0)); + + SR(VID_FIR_COEF_H(0, 0)); + SR(VID_FIR_COEF_H(0, 1)); + SR(VID_FIR_COEF_H(0, 2)); + SR(VID_FIR_COEF_H(0, 3)); + SR(VID_FIR_COEF_H(0, 4)); + SR(VID_FIR_COEF_H(0, 5)); + SR(VID_FIR_COEF_H(0, 6)); + SR(VID_FIR_COEF_H(0, 7)); + + SR(VID_FIR_COEF_HV(0, 0)); + SR(VID_FIR_COEF_HV(0, 1)); + SR(VID_FIR_COEF_HV(0, 2)); + SR(VID_FIR_COEF_HV(0, 3)); + SR(VID_FIR_COEF_HV(0, 4)); + SR(VID_FIR_COEF_HV(0, 5)); + SR(VID_FIR_COEF_HV(0, 6)); + SR(VID_FIR_COEF_HV(0, 7)); + + SR(VID_CONV_COEF(0, 0)); + SR(VID_CONV_COEF(0, 1)); + SR(VID_CONV_COEF(0, 2)); + SR(VID_CONV_COEF(0, 3)); + SR(VID_CONV_COEF(0, 4)); + + SR(VID_FIR_COEF_V(0, 0)); + SR(VID_FIR_COEF_V(0, 1)); + SR(VID_FIR_COEF_V(0, 2)); + SR(VID_FIR_COEF_V(0, 3)); + SR(VID_FIR_COEF_V(0, 4)); + SR(VID_FIR_COEF_V(0, 5)); + SR(VID_FIR_COEF_V(0, 6)); + SR(VID_FIR_COEF_V(0, 7)); + + SR(VID_PRELOAD(0)); + + /* VID2 */ + SR(VID_BA0(1)); + SR(VID_BA1(1)); + SR(VID_POSITION(1)); + SR(VID_SIZE(1)); + SR(VID_ATTRIBUTES(1)); + SR(VID_FIFO_THRESHOLD(1)); + SR(VID_ROW_INC(1)); + SR(VID_PIXEL_INC(1)); + SR(VID_FIR(1)); + SR(VID_PICTURE_SIZE(1)); + SR(VID_ACCU0(1)); + SR(VID_ACCU1(1)); + + SR(VID_FIR_COEF_H(1, 0)); + SR(VID_FIR_COEF_H(1, 1)); + SR(VID_FIR_COEF_H(1, 2)); + SR(VID_FIR_COEF_H(1, 3)); + SR(VID_FIR_COEF_H(1, 4)); + SR(VID_FIR_COEF_H(1, 5)); + SR(VID_FIR_COEF_H(1, 6)); + SR(VID_FIR_COEF_H(1, 7)); + + SR(VID_FIR_COEF_HV(1, 0)); + SR(VID_FIR_COEF_HV(1, 1)); + SR(VID_FIR_COEF_HV(1, 2)); + SR(VID_FIR_COEF_HV(1, 3)); + SR(VID_FIR_COEF_HV(1, 4)); + SR(VID_FIR_COEF_HV(1, 5)); + SR(VID_FIR_COEF_HV(1, 6)); + SR(VID_FIR_COEF_HV(1, 7)); + + SR(VID_CONV_COEF(1, 0)); + SR(VID_CONV_COEF(1, 1)); + SR(VID_CONV_COEF(1, 2)); + SR(VID_CONV_COEF(1, 3)); + SR(VID_CONV_COEF(1, 4)); + + SR(VID_FIR_COEF_V(1, 0)); + SR(VID_FIR_COEF_V(1, 1)); + SR(VID_FIR_COEF_V(1, 2)); + SR(VID_FIR_COEF_V(1, 3)); + SR(VID_FIR_COEF_V(1, 4)); + SR(VID_FIR_COEF_V(1, 5)); + SR(VID_FIR_COEF_V(1, 6)); + SR(VID_FIR_COEF_V(1, 7)); + + SR(VID_PRELOAD(1)); +} + +void dispc_restore_context(void) +{ + RR(SYSCONFIG); + RR(IRQENABLE); + /*RR(CONTROL);*/ + RR(CONFIG); + RR(DEFAULT_COLOR0); + RR(DEFAULT_COLOR1); + RR(TRANS_COLOR0); + RR(TRANS_COLOR1); + RR(LINE_NUMBER); + RR(TIMING_H); + RR(TIMING_V); + RR(POL_FREQ); + RR(DIVISOR); + RR(GLOBAL_ALPHA); + RR(SIZE_DIG); + RR(SIZE_LCD); + + RR(GFX_BA0); + RR(GFX_BA1); + RR(GFX_POSITION); + RR(GFX_SIZE); + RR(GFX_ATTRIBUTES); + RR(GFX_FIFO_THRESHOLD); + RR(GFX_ROW_INC); + RR(GFX_PIXEL_INC); + RR(GFX_WINDOW_SKIP); + RR(GFX_TABLE_BA); + + RR(DATA_CYCLE1); + RR(DATA_CYCLE2); + RR(DATA_CYCLE3); + + RR(CPR_COEF_R); + RR(CPR_COEF_G); + RR(CPR_COEF_B); + + RR(GFX_PRELOAD); + + /* VID1 */ + RR(VID_BA0(0)); + RR(VID_BA1(0)); + RR(VID_POSITION(0)); + RR(VID_SIZE(0)); + RR(VID_ATTRIBUTES(0)); + RR(VID_FIFO_THRESHOLD(0)); + RR(VID_ROW_INC(0)); + RR(VID_PIXEL_INC(0)); + RR(VID_FIR(0)); + RR(VID_PICTURE_SIZE(0)); + RR(VID_ACCU0(0)); + RR(VID_ACCU1(0)); + + RR(VID_FIR_COEF_H(0, 0)); + RR(VID_FIR_COEF_H(0, 1)); + RR(VID_FIR_COEF_H(0, 2)); + RR(VID_FIR_COEF_H(0, 3)); + RR(VID_FIR_COEF_H(0, 4)); + RR(VID_FIR_COEF_H(0, 5)); + RR(VID_FIR_COEF_H(0, 6)); + RR(VID_FIR_COEF_H(0, 7)); + + RR(VID_FIR_COEF_HV(0, 0)); + RR(VID_FIR_COEF_HV(0, 1)); + RR(VID_FIR_COEF_HV(0, 2)); + RR(VID_FIR_COEF_HV(0, 3)); + RR(VID_FIR_COEF_HV(0, 4)); + RR(VID_FIR_COEF_HV(0, 5)); + RR(VID_FIR_COEF_HV(0, 6)); + RR(VID_FIR_COEF_HV(0, 7)); + + RR(VID_CONV_COEF(0, 0)); + RR(VID_CONV_COEF(0, 1)); + RR(VID_CONV_COEF(0, 2)); + RR(VID_CONV_COEF(0, 3)); + RR(VID_CONV_COEF(0, 4)); + + RR(VID_FIR_COEF_V(0, 0)); + RR(VID_FIR_COEF_V(0, 1)); + RR(VID_FIR_COEF_V(0, 2)); + RR(VID_FIR_COEF_V(0, 3)); + RR(VID_FIR_COEF_V(0, 4)); + RR(VID_FIR_COEF_V(0, 5)); + RR(VID_FIR_COEF_V(0, 6)); + RR(VID_FIR_COEF_V(0, 7)); + + RR(VID_PRELOAD(0)); + + /* VID2 */ + RR(VID_BA0(1)); + RR(VID_BA1(1)); + RR(VID_POSITION(1)); + RR(VID_SIZE(1)); + RR(VID_ATTRIBUTES(1)); + RR(VID_FIFO_THRESHOLD(1)); + RR(VID_ROW_INC(1)); + RR(VID_PIXEL_INC(1)); + RR(VID_FIR(1)); + RR(VID_PICTURE_SIZE(1)); + RR(VID_ACCU0(1)); + RR(VID_ACCU1(1)); + + RR(VID_FIR_COEF_H(1, 0)); + RR(VID_FIR_COEF_H(1, 1)); + RR(VID_FIR_COEF_H(1, 2)); + RR(VID_FIR_COEF_H(1, 3)); + RR(VID_FIR_COEF_H(1, 4)); + RR(VID_FIR_COEF_H(1, 5)); + RR(VID_FIR_COEF_H(1, 6)); + RR(VID_FIR_COEF_H(1, 7)); + + RR(VID_FIR_COEF_HV(1, 0)); + RR(VID_FIR_COEF_HV(1, 1)); + RR(VID_FIR_COEF_HV(1, 2)); + RR(VID_FIR_COEF_HV(1, 3)); + RR(VID_FIR_COEF_HV(1, 4)); + RR(VID_FIR_COEF_HV(1, 5)); + RR(VID_FIR_COEF_HV(1, 6)); + RR(VID_FIR_COEF_HV(1, 7)); + + RR(VID_CONV_COEF(1, 0)); + RR(VID_CONV_COEF(1, 1)); + RR(VID_CONV_COEF(1, 2)); + RR(VID_CONV_COEF(1, 3)); + RR(VID_CONV_COEF(1, 4)); + + RR(VID_FIR_COEF_V(1, 0)); + RR(VID_FIR_COEF_V(1, 1)); + RR(VID_FIR_COEF_V(1, 2)); + RR(VID_FIR_COEF_V(1, 3)); + RR(VID_FIR_COEF_V(1, 4)); + RR(VID_FIR_COEF_V(1, 5)); + RR(VID_FIR_COEF_V(1, 6)); + RR(VID_FIR_COEF_V(1, 7)); + + RR(VID_PRELOAD(1)); + + /* enable last, because LCD & DIGIT enable are here */ + RR(CONTROL); +} + +#undef SR +#undef RR + +static inline void enable_clocks(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +bool dispc_go_busy(enum omap_channel channel) +{ + int bit; + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + + return REG_GET(DISPC_CONTROL, bit, bit) == 1; +} + +void dispc_go(enum omap_channel channel) +{ + int bit; + + enable_clocks(1); + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 0; /* LCDENABLE */ + else + bit = 1; /* DIGITALENABLE */ + + /* if the channel is not enabled, we don't need GO */ + if (REG_GET(DISPC_CONTROL, bit, bit) == 0) + goto end; + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + + if (REG_GET(DISPC_CONTROL, bit, bit) == 1) { + DSSERR("GO bit not down for channel %d\n", channel); + goto end; + } + + DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT"); + + REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); +end: + enable_clocks(0); +} + +static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); +} + +static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); +} + +static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); +} + +static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, + int vscaleup, int five_taps) +{ + /* Coefficients for horizontal up-sampling */ + static const u32 coef_hup[8] = { + 0x00800000, + 0x0D7CF800, + 0x1E70F5FF, + 0x335FF5FE, + 0xF74949F7, + 0xF55F33FB, + 0xF5701EFE, + 0xF87C0DFF, + }; + + /* Coefficients for horizontal down-sampling */ + static const u32 coef_hdown[8] = { + 0x24382400, + 0x28371FFE, + 0x2C361BFB, + 0x303516F9, + 0x11343311, + 0x1635300C, + 0x1B362C08, + 0x1F372804, + }; + + /* Coefficients for horizontal and vertical up-sampling */ + static const u32 coef_hvup[2][8] = { + { + 0x00800000, + 0x037B02FF, + 0x0C6F05FE, + 0x205907FB, + 0x00404000, + 0x075920FE, + 0x056F0CFF, + 0x027B0300, + }, + { + 0x00800000, + 0x0D7CF8FF, + 0x1E70F5FE, + 0x335FF5FB, + 0xF7404000, + 0xF55F33FE, + 0xF5701EFF, + 0xF87C0D00, + }, + }; + + /* Coefficients for horizontal and vertical down-sampling */ + static const u32 coef_hvdown[2][8] = { + { + 0x24382400, + 0x28391F04, + 0x2D381B08, + 0x3237170C, + 0x123737F7, + 0x173732F9, + 0x1B382DFB, + 0x1F3928FE, + }, + { + 0x24382400, + 0x28371F04, + 0x2C361B08, + 0x3035160C, + 0x113433F7, + 0x163530F9, + 0x1B362CFB, + 0x1F3728FE, + }, + }; + + /* Coefficients for vertical up-sampling */ + static const u32 coef_vup[8] = { + 0x00000000, + 0x0000FF00, + 0x0000FEFF, + 0x0000FBFE, + 0x000000F7, + 0x0000FEFB, + 0x0000FFFE, + 0x000000FF, + }; + + + /* Coefficients for vertical down-sampling */ + static const u32 coef_vdown[8] = { + 0x00000000, + 0x000004FE, + 0x000008FB, + 0x00000CF9, + 0x0000F711, + 0x0000F90C, + 0x0000FB08, + 0x0000FE04, + }; + + const u32 *h_coef; + const u32 *hv_coef; + const u32 *hv_coef_mod; + const u32 *v_coef; + int i; + + if (hscaleup) + h_coef = coef_hup; + else + h_coef = coef_hdown; + + if (vscaleup) { + hv_coef = coef_hvup[five_taps]; + v_coef = coef_vup; + + if (hscaleup) + hv_coef_mod = NULL; + else + hv_coef_mod = coef_hvdown[five_taps]; + } else { + hv_coef = coef_hvdown[five_taps]; + v_coef = coef_vdown; + + if (hscaleup) + hv_coef_mod = coef_hvup[five_taps]; + else + hv_coef_mod = NULL; + } + + for (i = 0; i < 8; i++) { + u32 h, hv; + + h = h_coef[i]; + + hv = hv_coef[i]; + + if (hv_coef_mod) { + hv &= 0xffffff00; + hv |= (hv_coef_mod[i] & 0xff); + } + + _dispc_write_firh_reg(plane, i, h); + _dispc_write_firhv_reg(plane, i, hv); + } + + if (!five_taps) + return; + + for (i = 0; i < 8; i++) { + u32 v; + v = v_coef[i]; + _dispc_write_firv_reg(plane, i, v); + } +} + +static void _dispc_setup_color_conv_coef(void) +{ + const struct color_conv_coef { + int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; + int full_range; + } ctbl_bt601_5 = { + 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, + }; + + const struct color_conv_coef *ct; + +#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) + + ct = &ctbl_bt601_5; + + dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0, ct->bcb)); + + dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb)); + +#undef CVAL + + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11); + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11); +} + + +static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0, + DISPC_VID_BA0(0), + DISPC_VID_BA0(1) }; + + dispc_write_reg(ba0_reg[plane], paddr); +} + +static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1, + DISPC_VID_BA1(0), + DISPC_VID_BA1(1) }; + + dispc_write_reg(ba1_reg[plane], paddr); +} + +static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +{ + const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION, + DISPC_VID_POSITION(0), + DISPC_VID_POSITION(1) }; + + u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); + dispc_write_reg(pos_reg[plane], val); +} + +static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) +{ + const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE, + DISPC_VID_PICTURE_SIZE(0), + DISPC_VID_PICTURE_SIZE(1) }; + u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + dispc_write_reg(siz_reg[plane], val); +} + +static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) +{ + u32 val; + const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0), + DISPC_VID_SIZE(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + dispc_write_reg(vsi_reg[plane-1], val); +} + +static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) +{ + + BUG_ON(plane == OMAP_DSS_VIDEO1); + + if (cpu_is_omap24xx()) + return; + + if (plane == OMAP_DSS_GFX) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0); + else if (plane == OMAP_DSS_VIDEO2) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16); +} + +static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc) +{ + const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC, + DISPC_VID_PIXEL_INC(0), + DISPC_VID_PIXEL_INC(1) }; + + dispc_write_reg(ri_reg[plane], inc); +} + +static void _dispc_set_row_inc(enum omap_plane plane, s32 inc) +{ + const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC, + DISPC_VID_ROW_INC(0), + DISPC_VID_ROW_INC(1) }; + + dispc_write_reg(ri_reg[plane], inc); +} + +static void _dispc_set_color_mode(enum omap_plane plane, + enum omap_color_mode color_mode) +{ + u32 m = 0; + + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + m = 0x0; break; + case OMAP_DSS_COLOR_CLUT2: + m = 0x1; break; + case OMAP_DSS_COLOR_CLUT4: + m = 0x2; break; + case OMAP_DSS_COLOR_CLUT8: + m = 0x3; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + default: + BUG(); break; + } + + REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1); +} + +static void _dispc_set_channel_out(enum omap_plane plane, + enum omap_channel channel) +{ + int shift; + u32 val; + + switch (plane) { + case OMAP_DSS_GFX: + shift = 8; + break; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + shift = 16; + break; + default: + BUG(); + return; + } + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, channel, shift, shift); + dispc_write_reg(dispc_reg_att[plane], val); +} + +void dispc_set_burst_size(enum omap_plane plane, + enum omap_burst_size burst_size) +{ + int shift; + u32 val; + + enable_clocks(1); + + switch (plane) { + case OMAP_DSS_GFX: + shift = 6; + break; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + shift = 14; + break; + default: + BUG(); + return; + } + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, burst_size, shift+1, shift); + dispc_write_reg(dispc_reg_att[plane], val); + + enable_clocks(0); +} + +static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) +{ + u32 val; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, enable, 9, 9); + dispc_write_reg(dispc_reg_att[plane], val); +} + +void dispc_enable_replication(enum omap_plane plane, bool enable) +{ + int bit; + + if (plane == OMAP_DSS_GFX) + bit = 5; + else + bit = 10; + + enable_clocks(1); + REG_FLD_MOD(dispc_reg_att[plane], enable, bit, bit); + enable_clocks(0); +} + +void dispc_set_lcd_size(u16 width, u16 height) +{ + u32 val; + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + enable_clocks(1); + dispc_write_reg(DISPC_SIZE_LCD, val); + enable_clocks(0); +} + +void dispc_set_digit_size(u16 width, u16 height) +{ + u32 val; + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + enable_clocks(1); + dispc_write_reg(DISPC_SIZE_DIG, val); + enable_clocks(0); +} + +static void dispc_read_plane_fifo_sizes(void) +{ + const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, + DISPC_VID_FIFO_SIZE_STATUS(0), + DISPC_VID_FIFO_SIZE_STATUS(1) }; + u32 size; + int plane; + + enable_clocks(1); + + for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { + if (cpu_is_omap24xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0); + else if (cpu_is_omap34xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0); + else + BUG(); + + dispc.fifo_size[plane] = size; + } + + enable_clocks(0); +} + +u32 dispc_get_plane_fifo_size(enum omap_plane plane) +{ + return dispc.fifo_size[plane]; +} + +void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) +{ + const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, + DISPC_VID_FIFO_THRESHOLD(0), + DISPC_VID_FIFO_THRESHOLD(1) }; + enable_clocks(1); + + DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", + plane, + REG_GET(ftrs_reg[plane], 11, 0), + REG_GET(ftrs_reg[plane], 27, 16), + low, high); + + if (cpu_is_omap24xx()) + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0)); + else + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0)); + + enable_clocks(0); +} + +void dispc_enable_fifomerge(bool enable) +{ + enable_clocks(1); + + DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); + + enable_clocks(0); +} + +static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) +{ + u32 val; + const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), + DISPC_VID_FIR(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + if (cpu_is_omap24xx()) + val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0); + else + val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); + dispc_write_reg(fir_reg[plane-1], val); +} + +static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0), + DISPC_VID_ACCU0(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac0_reg[plane-1], val); +} + +static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0), + DISPC_VID_ACCU1(1) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac1_reg[plane-1], val); +} + + +static void _dispc_set_scaling(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode) +{ + int fir_hinc; + int fir_vinc; + int hscaleup, vscaleup; + int accu0 = 0; + int accu1 = 0; + u32 l; + + BUG_ON(plane == OMAP_DSS_GFX); + + hscaleup = orig_width <= out_width; + vscaleup = orig_height <= out_height; + + _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps); + + if (!orig_width || orig_width == out_width) + fir_hinc = 0; + else + fir_hinc = 1024 * orig_width / out_width; + + if (!orig_height || orig_height == out_height) + fir_vinc = 0; + else + fir_vinc = 1024 * orig_height / out_height; + + _dispc_set_fir(plane, fir_hinc, fir_vinc); + + l = dispc_read_reg(dispc_reg_att[plane]); + l &= ~((0x0f << 5) | (0x3 << 21)); + + l |= fir_hinc ? (1 << 5) : 0; + l |= fir_vinc ? (1 << 6) : 0; + + l |= hscaleup ? 0 : (1 << 7); + l |= vscaleup ? 0 : (1 << 8); + + l |= five_taps ? (1 << 21) : 0; + l |= five_taps ? (1 << 22) : 0; + + dispc_write_reg(dispc_reg_att[plane], l); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + if (ilace && !fieldmode) { + accu1 = 0; + accu0 = (fir_vinc / 2) & 0x3ff; + if (accu0 >= 1024/2) { + accu1 = 1024/2; + accu0 -= accu1; + } + } + + _dispc_set_vid_accu0(plane, 0, accu0); + _dispc_set_vid_accu1(plane, 0, accu1); +} + +static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, + bool mirroring, enum omap_color_mode color_mode) +{ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) { + int vidrot = 0; + + if (mirroring) { + switch (rotation) { + case OMAP_DSS_ROT_0: + vidrot = 2; + break; + case OMAP_DSS_ROT_90: + vidrot = 1; + break; + case OMAP_DSS_ROT_180: + vidrot = 0; + break; + case OMAP_DSS_ROT_270: + vidrot = 3; + break; + } + } else { + switch (rotation) { + case OMAP_DSS_ROT_0: + vidrot = 0; + break; + case OMAP_DSS_ROT_90: + vidrot = 1; + break; + case OMAP_DSS_ROT_180: + vidrot = 2; + break; + case OMAP_DSS_ROT_270: + vidrot = 3; + break; + } + } + + REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); + + if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270) + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18); + else + REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18); + } else { + REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12); + REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18); + } +} + +static int color_mode_to_bpp(enum omap_color_mode color_mode) +{ + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + return 1; + case OMAP_DSS_COLOR_CLUT2: + return 2; + case OMAP_DSS_COLOR_CLUT4: + return 4; + case OMAP_DSS_COLOR_CLUT8: + return 8; + case OMAP_DSS_COLOR_RGB12U: + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + return 16; + case OMAP_DSS_COLOR_RGB24P: + return 24; + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + return 32; + default: + BUG(); + } +} + +static s32 pixinc(int pixels, u8 ps) +{ + if (pixels == 1) + return 1; + else if (pixels > 1) + return 1 + (pixels - 1) * ps; + else if (pixels < 0) + return 1 - (-pixels + 1) * ps; + else + BUG(); +} + +static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, + u16 screen_width, + u16 width, u16 height, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, + unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc) +{ + u8 ps; + + /* FIXME CLUT formats */ + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + case OMAP_DSS_COLOR_CLUT2: + case OMAP_DSS_COLOR_CLUT4: + case OMAP_DSS_COLOR_CLUT8: + BUG(); + return; + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ps = 4; + break; + default: + ps = color_mode_to_bpp(color_mode) / 8; + break; + } + + DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, + width, height); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + switch (rotation + mirror * 4) { + case OMAP_DSS_ROT_0: + case OMAP_DSS_ROT_180: + /* + * If the pixel format is YUV or UYVY divide the width + * of the image by 2 for 0 and 180 degree rotation. + */ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + width = width >> 1; + case OMAP_DSS_ROT_90: + case OMAP_DSS_ROT_270: + *offset1 = 0; + if (field_offset) + *offset0 = field_offset * screen_width * ps; + else + *offset0 = 0; + + *row_inc = pixinc(1 + (screen_width - width) + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + case OMAP_DSS_ROT_0 + 4: + case OMAP_DSS_ROT_180 + 4: + /* If the pixel format is YUV or UYVY divide the width + * of the image by 2 for 0 degree and 180 degree + */ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + width = width >> 1; + case OMAP_DSS_ROT_90 + 4: + case OMAP_DSS_ROT_270 + 4: + *offset1 = 0; + if (field_offset) + *offset0 = field_offset * screen_width * ps; + else + *offset0 = 0; + *row_inc = pixinc(1 - (screen_width + width) - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + default: + BUG(); + } +} + +static void calc_dma_rotation_offset(u8 rotation, bool mirror, + u16 screen_width, + u16 width, u16 height, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, + unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc) +{ + u8 ps; + u16 fbw, fbh; + + /* FIXME CLUT formats */ + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + case OMAP_DSS_COLOR_CLUT2: + case OMAP_DSS_COLOR_CLUT4: + case OMAP_DSS_COLOR_CLUT8: + BUG(); + return; + default: + ps = color_mode_to_bpp(color_mode) / 8; + break; + } + + DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, + width, height); + + /* width & height are overlay sizes, convert to fb sizes */ + + if (rotation == OMAP_DSS_ROT_0 || rotation == OMAP_DSS_ROT_180) { + fbw = width; + fbh = height; + } else { + fbw = height; + fbh = width; + } + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + switch (rotation + mirror * 4) { + case OMAP_DSS_ROT_0: + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 + (screen_width - fbw) + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + case OMAP_DSS_ROT_90: + *offset1 = screen_width * (fbh - 1) * ps; + if (field_offset) + *offset0 = *offset1 + field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * (fbh - 1) + 1 + + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(-screen_width, ps); + break; + case OMAP_DSS_ROT_180: + *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-1 - + (screen_width - fbw) - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(-1, ps); + break; + case OMAP_DSS_ROT_270: + *offset1 = (fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-screen_width * (fbh - 1) - 1 - + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(screen_width, ps); + break; + + /* mirroring */ + case OMAP_DSS_ROT_0 + 4: + *offset1 = (fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * 2 - 1 + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(-1, ps); + break; + + case OMAP_DSS_ROT_90 + 4: + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-screen_width * (fbh - 1) + 1 + + (fieldmode ? 1 : 0), + ps); + *pix_inc = pixinc(screen_width, ps); + break; + + case OMAP_DSS_ROT_180 + 4: + *offset1 = screen_width * (fbh - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 - screen_width * 2 - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + case OMAP_DSS_ROT_270 + 4: + *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * (fbh - 1) - 1 - + (fieldmode ? 1 : 0), + ps); + *pix_inc = pixinc(-screen_width, ps); + break; + + default: + BUG(); + } +} + +static unsigned long calc_fclk_five_taps(u16 width, u16 height, + u16 out_width, u16 out_height, enum omap_color_mode color_mode) +{ + u32 fclk = 0; + /* FIXME venc pclk? */ + u64 tmp, pclk = dispc_pclk_rate(); + + if (height > out_height) { + /* FIXME get real display PPL */ + unsigned int ppl = 800; + + tmp = pclk * height * out_width; + do_div(tmp, 2 * out_height * ppl); + fclk = tmp; + + if (height > 2 * out_height && ppl != out_width) { + tmp = pclk * (height - 2 * out_height) * out_width; + do_div(tmp, 2 * out_height * (ppl - out_width)); + fclk = max(fclk, (u32) tmp); + } + } + + if (width > out_width) { + tmp = pclk * width; + do_div(tmp, out_width); + fclk = max(fclk, (u32) tmp); + + if (color_mode == OMAP_DSS_COLOR_RGB24U) + fclk <<= 1; + } + + return fclk; +} + +static unsigned long calc_fclk(u16 width, u16 height, + u16 out_width, u16 out_height) +{ + unsigned int hf, vf; + + /* + * FIXME how to determine the 'A' factor + * for the no downscaling case ? + */ + + if (width > 3 * out_width) + hf = 4; + else if (width > 2 * out_width) + hf = 3; + else if (width > out_width) + hf = 2; + else + hf = 1; + + if (height > out_height) + vf = 2; + else + vf = 1; + + /* FIXME venc pclk? */ + return dispc_pclk_rate() * vf * hf; +} + +void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out) +{ + enable_clocks(1); + _dispc_set_channel_out(plane, channel_out); + enable_clocks(0); +} + +static int _dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, int mirror, + u8 global_alpha) +{ + const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; + bool five_taps = 0; + bool fieldmode = 0; + int cconv = 0; + unsigned offset0, offset1; + s32 row_inc; + s32 pix_inc; + u16 frame_height = height; + unsigned int field_offset = 0; + + if (paddr == 0) + return -EINVAL; + + if (ilace && height == out_height) + fieldmode = 1; + + if (ilace) { + if (fieldmode) + height /= 2; + pos_y /= 2; + out_height /= 2; + + DSSDBG("adjusting for ilace: height %d, pos_y %d, " + "out_height %d\n", + height, pos_y, out_height); + } + + if (plane == OMAP_DSS_GFX) { + if (width != out_width || height != out_height) + return -EINVAL; + + switch (color_mode) { + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + if (cpu_is_omap24xx()) + return -EINVAL; + /* fall through */ + case OMAP_DSS_COLOR_RGB12U: + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + break; + + default: + return -EINVAL; + } + } else { + /* video plane */ + + unsigned long fclk = 0; + + if (out_width < width / maxdownscale || + out_width > width * 8) + return -EINVAL; + + if (out_height < height / maxdownscale || + out_height > height * 8) + return -EINVAL; + + switch (color_mode) { + case OMAP_DSS_COLOR_RGBX32: + case OMAP_DSS_COLOR_RGB12U: + if (cpu_is_omap24xx()) + return -EINVAL; + /* fall through */ + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + break; + + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + if (cpu_is_omap24xx()) + return -EINVAL; + if (plane == OMAP_DSS_VIDEO1) + return -EINVAL; + break; + + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + cconv = 1; + break; + + default: + return -EINVAL; + } + + /* Must use 5-tap filter? */ + five_taps = height > out_height * 2; + + if (!five_taps) { + fclk = calc_fclk(width, height, + out_width, out_height); + + /* Try 5-tap filter if 3-tap fclk is too high */ + if (cpu_is_omap34xx() && height > out_height && + fclk > dispc_fclk_rate()) + five_taps = true; + } + + if (width > (2048 >> five_taps)) { + DSSERR("failed to set up scaling, fclk too low\n"); + return -EINVAL; + } + + if (five_taps) + fclk = calc_fclk_five_taps(width, height, + out_width, out_height, color_mode); + + DSSDBG("required fclk rate = %lu Hz\n", fclk); + DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); + + if (fclk > dispc_fclk_rate()) { + DSSERR("failed to set up scaling, " + "required fclk rate = %lu Hz, " + "current fclk rate = %lu Hz\n", + fclk, dispc_fclk_rate()); + return -EINVAL; + } + } + + if (ilace && !fieldmode) { + /* + * when downscaling the bottom field may have to start several + * source lines below the top field. Unfortunately ACCUI + * registers will only hold the fractional part of the offset + * so the integer part must be added to the base address of the + * bottom field. + */ + if (!height || height == out_height) + field_offset = 0; + else + field_offset = height / out_height / 2; + } + + /* Fields are independent but interleaved in memory. */ + if (fieldmode) + field_offset = 1; + + if (rotation_type == OMAP_DSS_ROT_DMA) + calc_dma_rotation_offset(rotation, mirror, + screen_width, width, frame_height, color_mode, + fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc); + else + calc_vrfb_rotation_offset(rotation, mirror, + screen_width, width, frame_height, color_mode, + fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc); + + DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", + offset0, offset1, row_inc, pix_inc); + + _dispc_set_color_mode(plane, color_mode); + + _dispc_set_plane_ba0(plane, paddr + offset0); + _dispc_set_plane_ba1(plane, paddr + offset1); + + _dispc_set_row_inc(plane, row_inc); + _dispc_set_pix_inc(plane, pix_inc); + + DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height, + out_width, out_height); + + _dispc_set_plane_pos(plane, pos_x, pos_y); + + _dispc_set_pic_size(plane, width, height); + + if (plane != OMAP_DSS_GFX) { + _dispc_set_scaling(plane, width, height, + out_width, out_height, + ilace, five_taps, fieldmode); + _dispc_set_vid_size(plane, out_width, out_height); + _dispc_set_vid_color_conv(plane, cconv); + } + + _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode); + + if (plane != OMAP_DSS_VIDEO1) + _dispc_setup_global_alpha(plane, global_alpha); + + return 0; +} + +static void _dispc_enable_plane(enum omap_plane plane, bool enable) +{ + REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0); +} + +static void dispc_disable_isr(void *data, u32 mask) +{ + struct completion *compl = data; + complete(compl); +} + +static void _enable_lcd_out(bool enable) +{ + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); +} + +void dispc_enable_lcd_out(bool enable) +{ + struct completion frame_done_completion; + bool is_on; + int r; + + enable_clocks(1); + + /* When we disable LCD output, we need to wait until frame is done. + * Otherwise the DSS is still working, and turning off the clocks + * prevents DSS from going to OFF mode */ + is_on = REG_GET(DISPC_CONTROL, 0, 0); + + if (!enable && is_on) { + init_completion(&frame_done_completion); + + r = omap_dispc_register_isr(dispc_disable_isr, + &frame_done_completion, + DISPC_IRQ_FRAMEDONE); + + if (r) + DSSERR("failed to register FRAMEDONE isr\n"); + } + + _enable_lcd_out(enable); + + if (!enable && is_on) { + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for FRAME DONE\n"); + + r = omap_dispc_unregister_isr(dispc_disable_isr, + &frame_done_completion, + DISPC_IRQ_FRAMEDONE); + + if (r) + DSSERR("failed to unregister FRAMEDONE isr\n"); + } + + enable_clocks(0); +} + +static void _enable_digit_out(bool enable) +{ + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1); +} + +void dispc_enable_digit_out(bool enable) +{ + struct completion frame_done_completion; + int r; + + enable_clocks(1); + + if (REG_GET(DISPC_CONTROL, 1, 1) == enable) { + enable_clocks(0); + return; + } + + if (enable) { + unsigned long flags; + /* When we enable digit output, we'll get an extra digit + * sync lost interrupt, that we need to ignore */ + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask &= ~DISPC_IRQ_SYNC_LOST_DIGIT; + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); + } + + /* When we disable digit output, we need to wait until fields are done. + * Otherwise the DSS is still working, and turning off the clocks + * prevents DSS from going to OFF mode. And when enabling, we need to + * wait for the extra sync losts */ + init_completion(&frame_done_completion); + + r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion, + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); + if (r) + DSSERR("failed to register EVSYNC isr\n"); + + _enable_digit_out(enable); + + /* XXX I understand from TRM that we should only wait for the + * current field to complete. But it seems we have to wait + * for both fields */ + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for EVSYNC\n"); + + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for EVSYNC\n"); + + r = omap_dispc_unregister_isr(dispc_disable_isr, + &frame_done_completion, + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); + if (r) + DSSERR("failed to unregister EVSYNC isr\n"); + + if (enable) { + unsigned long flags; + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; + dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); + } + + enable_clocks(0); +} + +void dispc_lcd_enable_signal_polarity(bool act_high) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); + enable_clocks(0); +} + +void dispc_lcd_enable_signal(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); + enable_clocks(0); +} + +void dispc_pck_free_enable(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); + enable_clocks(0); +} + +void dispc_enable_fifohandcheck(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); + enable_clocks(0); +} + + +void dispc_set_lcd_display_type(enum omap_lcd_display_type type) +{ + int mode; + + switch (type) { + case OMAP_DSS_LCD_DISPLAY_STN: + mode = 0; + break; + + case OMAP_DSS_LCD_DISPLAY_TFT: + mode = 1; + break; + + default: + BUG(); + return; + } + + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); + enable_clocks(0); +} + +void dispc_set_loadmode(enum omap_dss_load_mode mode) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); + enable_clocks(0); +} + + +void dispc_set_default_color(enum omap_channel channel, u32 color) +{ + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, + DISPC_DEFAULT_COLOR1 }; + + enable_clocks(1); + dispc_write_reg(def_reg[channel], color); + enable_clocks(0); +} + +u32 dispc_get_default_color(enum omap_channel channel) +{ + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, + DISPC_DEFAULT_COLOR1 }; + u32 l; + + BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT && + channel != OMAP_DSS_CHANNEL_LCD); + + enable_clocks(1); + l = dispc_read_reg(def_reg[channel]); + enable_clocks(0); + + return l; +} + +void dispc_set_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type type, + u32 trans_key) +{ + const struct dispc_reg tr_reg[] = { + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; + + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, type, 11, 11); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, type, 13, 13); + + dispc_write_reg(tr_reg[ch], trans_key); + enable_clocks(0); +} + +void dispc_get_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type *type, + u32 *trans_key) +{ + const struct dispc_reg tr_reg[] = { + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; + + enable_clocks(1); + if (type) { + if (ch == OMAP_DSS_CHANNEL_LCD) + *type = REG_GET(DISPC_CONFIG, 11, 11); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + *type = REG_GET(DISPC_CONFIG, 13, 13); + else + BUG(); + } + + if (trans_key) + *trans_key = dispc_read_reg(tr_reg[ch]); + enable_clocks(0); +} + +void dispc_enable_trans_key(enum omap_channel ch, bool enable) +{ + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12); + enable_clocks(0); +} +void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) +{ + if (cpu_is_omap24xx()) + return; + + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); + enable_clocks(0); +} +bool dispc_alpha_blending_enabled(enum omap_channel ch) +{ + bool enabled; + + if (cpu_is_omap24xx()) + return false; + + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + enabled = REG_GET(DISPC_CONFIG, 18, 18); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + enabled = REG_GET(DISPC_CONFIG, 18, 18); + else + BUG(); + enable_clocks(0); + + return enabled; + +} + + +bool dispc_trans_key_enabled(enum omap_channel ch) +{ + bool enabled; + + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + enabled = REG_GET(DISPC_CONFIG, 10, 10); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + enabled = REG_GET(DISPC_CONFIG, 12, 12); + else + BUG(); + enable_clocks(0); + + return enabled; +} + + +void dispc_set_tft_data_lines(u8 data_lines) +{ + int code; + + switch (data_lines) { + case 12: + code = 0; + break; + case 16: + code = 1; + break; + case 18: + code = 2; + break; + case 24: + code = 3; + break; + default: + BUG(); + return; + } + + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); + enable_clocks(0); +} + +void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode) +{ + u32 l; + int stallmode; + int gpout0 = 1; + int gpout1; + + switch (mode) { + case OMAP_DSS_PARALLELMODE_BYPASS: + stallmode = 0; + gpout1 = 1; + break; + + case OMAP_DSS_PARALLELMODE_RFBI: + stallmode = 1; + gpout1 = 0; + break; + + case OMAP_DSS_PARALLELMODE_DSI: + stallmode = 1; + gpout1 = 1; + break; + + default: + BUG(); + return; + } + + enable_clocks(1); + + l = dispc_read_reg(DISPC_CONTROL); + + l = FLD_MOD(l, stallmode, 11, 11); + l = FLD_MOD(l, gpout0, 15, 15); + l = FLD_MOD(l, gpout1, 16, 16); + + dispc_write_reg(DISPC_CONTROL, l); + + enable_clocks(0); +} + +static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, + int vsw, int vfp, int vbp) +{ + if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { + if (hsw < 1 || hsw > 64 || + hfp < 1 || hfp > 256 || + hbp < 1 || hbp > 256 || + vsw < 1 || vsw > 64 || + vfp < 0 || vfp > 255 || + vbp < 0 || vbp > 255) + return false; + } else { + if (hsw < 1 || hsw > 256 || + hfp < 1 || hfp > 4096 || + hbp < 1 || hbp > 4096 || + vsw < 1 || vsw > 256 || + vfp < 0 || vfp > 4095 || + vbp < 0 || vbp > 4095) + return false; + } + + return true; +} + +bool dispc_lcd_timings_ok(struct omap_video_timings *timings) +{ + return _dispc_lcd_timings_ok(timings->hsw, timings->hfp, + timings->hbp, timings->vsw, + timings->vfp, timings->vbp); +} + +static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp, + int vsw, int vfp, int vbp) +{ + u32 timing_h, timing_v; + + if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { + timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) | + FLD_VAL(hbp-1, 27, 20); + + timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) | + FLD_VAL(vbp, 27, 20); + } else { + timing_h = FLD_VAL(hsw-1, 7, 0) | FLD_VAL(hfp-1, 19, 8) | + FLD_VAL(hbp-1, 31, 20); + + timing_v = FLD_VAL(vsw-1, 7, 0) | FLD_VAL(vfp, 19, 8) | + FLD_VAL(vbp, 31, 20); + } + + enable_clocks(1); + dispc_write_reg(DISPC_TIMING_H, timing_h); + dispc_write_reg(DISPC_TIMING_V, timing_v); + enable_clocks(0); +} + +/* change name to mode? */ +void dispc_set_lcd_timings(struct omap_video_timings *timings) +{ + unsigned xtot, ytot; + unsigned long ht, vt; + + if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp, + timings->hbp, timings->vsw, + timings->vfp, timings->vbp)) + BUG(); + + _dispc_set_lcd_timings(timings->hsw, timings->hfp, timings->hbp, + timings->vsw, timings->vfp, timings->vbp); + + dispc_set_lcd_size(timings->x_res, timings->y_res); + + xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; + ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; + + ht = (timings->pixel_clock * 1000) / xtot; + vt = (timings->pixel_clock * 1000) / xtot / ytot; + + DSSDBG("xres %u yres %u\n", timings->x_res, timings->y_res); + DSSDBG("pck %u\n", timings->pixel_clock); + DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", + timings->hsw, timings->hfp, timings->hbp, + timings->vsw, timings->vfp, timings->vbp); + + DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); +} + +static void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div) +{ + BUG_ON(lck_div < 1); + BUG_ON(pck_div < 2); + + enable_clocks(1); + dispc_write_reg(DISPC_DIVISOR, + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); + enable_clocks(0); +} + +static void dispc_get_lcd_divisor(int *lck_div, int *pck_div) +{ + u32 l; + l = dispc_read_reg(DISPC_DIVISOR); + *lck_div = FLD_GET(l, 23, 16); + *pck_div = FLD_GET(l, 7, 0); +} + +unsigned long dispc_fclk_rate(void) +{ + unsigned long r = 0; + + if (dss_get_dispc_clk_source() == 0) + r = dss_clk_get_rate(DSS_CLK_FCK1); + else +#ifdef CONFIG_OMAP2_DSS_DSI + r = dsi_get_dsi1_pll_rate(); +#else + BUG(); +#endif + return r; +} + +unsigned long dispc_lclk_rate(void) +{ + int lcd; + unsigned long r; + u32 l; + + l = dispc_read_reg(DISPC_DIVISOR); + + lcd = FLD_GET(l, 23, 16); + + r = dispc_fclk_rate(); + + return r / lcd; +} + +unsigned long dispc_pclk_rate(void) +{ + int lcd, pcd; + unsigned long r; + u32 l; + + l = dispc_read_reg(DISPC_DIVISOR); + + lcd = FLD_GET(l, 23, 16); + pcd = FLD_GET(l, 7, 0); + + r = dispc_fclk_rate(); + + return r / lcd / pcd; +} + +void dispc_dump_clocks(struct seq_file *s) +{ + int lcd, pcd; + + enable_clocks(1); + + dispc_get_lcd_divisor(&lcd, &pcd); + + seq_printf(s, "- DISPC -\n"); + + seq_printf(s, "dispc fclk source = %s\n", + dss_get_dispc_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi1_pll_fclk"); + + seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate()); + seq_printf(s, "lck\t\t%-16lulck div\t%u\n", dispc_lclk_rate(), lcd); + seq_printf(s, "pck\t\t%-16lupck div\t%u\n", dispc_pclk_rate(), pcd); + + enable_clocks(0); +} + +void dispc_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(DISPC_REVISION); + DUMPREG(DISPC_SYSCONFIG); + DUMPREG(DISPC_SYSSTATUS); + DUMPREG(DISPC_IRQSTATUS); + DUMPREG(DISPC_IRQENABLE); + DUMPREG(DISPC_CONTROL); + DUMPREG(DISPC_CONFIG); + DUMPREG(DISPC_CAPABLE); + DUMPREG(DISPC_DEFAULT_COLOR0); + DUMPREG(DISPC_DEFAULT_COLOR1); + DUMPREG(DISPC_TRANS_COLOR0); + DUMPREG(DISPC_TRANS_COLOR1); + DUMPREG(DISPC_LINE_STATUS); + DUMPREG(DISPC_LINE_NUMBER); + DUMPREG(DISPC_TIMING_H); + DUMPREG(DISPC_TIMING_V); + DUMPREG(DISPC_POL_FREQ); + DUMPREG(DISPC_DIVISOR); + DUMPREG(DISPC_GLOBAL_ALPHA); + DUMPREG(DISPC_SIZE_DIG); + DUMPREG(DISPC_SIZE_LCD); + + DUMPREG(DISPC_GFX_BA0); + DUMPREG(DISPC_GFX_BA1); + DUMPREG(DISPC_GFX_POSITION); + DUMPREG(DISPC_GFX_SIZE); + DUMPREG(DISPC_GFX_ATTRIBUTES); + DUMPREG(DISPC_GFX_FIFO_THRESHOLD); + DUMPREG(DISPC_GFX_FIFO_SIZE_STATUS); + DUMPREG(DISPC_GFX_ROW_INC); + DUMPREG(DISPC_GFX_PIXEL_INC); + DUMPREG(DISPC_GFX_WINDOW_SKIP); + DUMPREG(DISPC_GFX_TABLE_BA); + + DUMPREG(DISPC_DATA_CYCLE1); + DUMPREG(DISPC_DATA_CYCLE2); + DUMPREG(DISPC_DATA_CYCLE3); + + DUMPREG(DISPC_CPR_COEF_R); + DUMPREG(DISPC_CPR_COEF_G); + DUMPREG(DISPC_CPR_COEF_B); + + DUMPREG(DISPC_GFX_PRELOAD); + + DUMPREG(DISPC_VID_BA0(0)); + DUMPREG(DISPC_VID_BA1(0)); + DUMPREG(DISPC_VID_POSITION(0)); + DUMPREG(DISPC_VID_SIZE(0)); + DUMPREG(DISPC_VID_ATTRIBUTES(0)); + DUMPREG(DISPC_VID_FIFO_THRESHOLD(0)); + DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(0)); + DUMPREG(DISPC_VID_ROW_INC(0)); + DUMPREG(DISPC_VID_PIXEL_INC(0)); + DUMPREG(DISPC_VID_FIR(0)); + DUMPREG(DISPC_VID_PICTURE_SIZE(0)); + DUMPREG(DISPC_VID_ACCU0(0)); + DUMPREG(DISPC_VID_ACCU1(0)); + + DUMPREG(DISPC_VID_BA0(1)); + DUMPREG(DISPC_VID_BA1(1)); + DUMPREG(DISPC_VID_POSITION(1)); + DUMPREG(DISPC_VID_SIZE(1)); + DUMPREG(DISPC_VID_ATTRIBUTES(1)); + DUMPREG(DISPC_VID_FIFO_THRESHOLD(1)); + DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1)); + DUMPREG(DISPC_VID_ROW_INC(1)); + DUMPREG(DISPC_VID_PIXEL_INC(1)); + DUMPREG(DISPC_VID_FIR(1)); + DUMPREG(DISPC_VID_PICTURE_SIZE(1)); + DUMPREG(DISPC_VID_ACCU0(1)); + DUMPREG(DISPC_VID_ACCU1(1)); + + DUMPREG(DISPC_VID_FIR_COEF_H(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 7)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 7)); + DUMPREG(DISPC_VID_CONV_COEF(0, 0)); + DUMPREG(DISPC_VID_CONV_COEF(0, 1)); + DUMPREG(DISPC_VID_CONV_COEF(0, 2)); + DUMPREG(DISPC_VID_CONV_COEF(0, 3)); + DUMPREG(DISPC_VID_CONV_COEF(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 7)); + + DUMPREG(DISPC_VID_FIR_COEF_H(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 7)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 7)); + DUMPREG(DISPC_VID_CONV_COEF(1, 0)); + DUMPREG(DISPC_VID_CONV_COEF(1, 1)); + DUMPREG(DISPC_VID_CONV_COEF(1, 2)); + DUMPREG(DISPC_VID_CONV_COEF(1, 3)); + DUMPREG(DISPC_VID_CONV_COEF(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 7)); + + DUMPREG(DISPC_VID_PRELOAD(0)); + DUMPREG(DISPC_VID_PRELOAD(1)); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc, + bool ihs, bool ivs, u8 acbi, u8 acb) +{ + u32 l = 0; + + DSSDBG("onoff %d rf %d ieo %d ipc %d ihs %d ivs %d acbi %d acb %d\n", + onoff, rf, ieo, ipc, ihs, ivs, acbi, acb); + + l |= FLD_VAL(onoff, 17, 17); + l |= FLD_VAL(rf, 16, 16); + l |= FLD_VAL(ieo, 15, 15); + l |= FLD_VAL(ipc, 14, 14); + l |= FLD_VAL(ihs, 13, 13); + l |= FLD_VAL(ivs, 12, 12); + l |= FLD_VAL(acbi, 11, 8); + l |= FLD_VAL(acb, 7, 0); + + enable_clocks(1); + dispc_write_reg(DISPC_POL_FREQ, l); + enable_clocks(0); +} + +void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb) +{ + _dispc_set_pol_freq((config & OMAP_DSS_LCD_ONOFF) != 0, + (config & OMAP_DSS_LCD_RF) != 0, + (config & OMAP_DSS_LCD_IEO) != 0, + (config & OMAP_DSS_LCD_IPC) != 0, + (config & OMAP_DSS_LCD_IHS) != 0, + (config & OMAP_DSS_LCD_IVS) != 0, + acbi, acb); +} + +/* with fck as input clock rate, find dispc dividers that produce req_pck */ +void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, + struct dispc_clock_info *cinfo) +{ + u16 pcd_min = is_tft ? 2 : 3; + unsigned long best_pck; + u16 best_ld, cur_ld; + u16 best_pd, cur_pd; + + best_pck = 0; + best_ld = 0; + best_pd = 0; + + for (cur_ld = 1; cur_ld <= 255; ++cur_ld) { + unsigned long lck = fck / cur_ld; + + for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) { + unsigned long pck = lck / cur_pd; + long old_delta = abs(best_pck - req_pck); + long new_delta = abs(pck - req_pck); + + if (best_pck == 0 || new_delta < old_delta) { + best_pck = pck; + best_ld = cur_ld; + best_pd = cur_pd; + + if (pck == req_pck) + goto found; + } + + if (pck < req_pck) + break; + } + + if (lck / pcd_min < req_pck) + break; + } + +found: + cinfo->lck_div = best_ld; + cinfo->pck_div = best_pd; + cinfo->lck = fck / cinfo->lck_div; + cinfo->pck = cinfo->lck / cinfo->pck_div; +} + +/* calculate clock rates using dividers in cinfo */ +int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, + struct dispc_clock_info *cinfo) +{ + if (cinfo->lck_div > 255 || cinfo->lck_div == 0) + return -EINVAL; + if (cinfo->pck_div < 2 || cinfo->pck_div > 255) + return -EINVAL; + + cinfo->lck = dispc_fclk_rate / cinfo->lck_div; + cinfo->pck = cinfo->lck / cinfo->pck_div; + + return 0; +} + +int dispc_set_clock_div(struct dispc_clock_info *cinfo) +{ + DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div); + DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div); + + dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div); + + return 0; +} + +int dispc_get_clock_div(struct dispc_clock_info *cinfo) +{ + unsigned long fck; + + fck = dispc_fclk_rate(); + + cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0); + + cinfo->lck = fck / cinfo->lck_div; + cinfo->pck = cinfo->lck / cinfo->pck_div; + + return 0; +} + +/* dispc.irq_lock has to be locked by the caller */ +static void _omap_dispc_set_irqs(void) +{ + u32 mask; + u32 old_mask; + int i; + struct omap_dispc_isr_data *isr_data; + + mask = dispc.irq_error_mask; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + + if (isr_data->isr == NULL) + continue; + + mask |= isr_data->mask; + } + + enable_clocks(1); + + old_mask = dispc_read_reg(DISPC_IRQENABLE); + /* clear the irqstatus for newly enabled irqs */ + dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask); + + dispc_write_reg(DISPC_IRQENABLE, mask); + + enable_clocks(0); +} + +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) +{ + int i; + int ret; + unsigned long flags; + struct omap_dispc_isr_data *isr_data; + + if (isr == NULL) + return -EINVAL; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + /* check for duplicate entry */ + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + if (isr_data->isr == isr && isr_data->arg == arg && + isr_data->mask == mask) { + ret = -EINVAL; + goto err; + } + } + + isr_data = NULL; + ret = -EBUSY; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + + if (isr_data->isr != NULL) + continue; + + isr_data->isr = isr; + isr_data->arg = arg; + isr_data->mask = mask; + ret = 0; + + break; + } + + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return 0; +err: + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return ret; +} +EXPORT_SYMBOL(omap_dispc_register_isr); + +int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask) +{ + int i; + unsigned long flags; + int ret = -EINVAL; + struct omap_dispc_isr_data *isr_data; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + if (isr_data->isr != isr || isr_data->arg != arg || + isr_data->mask != mask) + continue; + + /* found the correct isr */ + + isr_data->isr = NULL; + isr_data->arg = NULL; + isr_data->mask = 0; + + ret = 0; + break; + } + + if (ret == 0) + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return ret; +} +EXPORT_SYMBOL(omap_dispc_unregister_isr); + +#ifdef DEBUG +static void print_irq_status(u32 status) +{ + if ((status & dispc.irq_error_mask) == 0) + return; + + printk(KERN_DEBUG "DISPC IRQ: 0x%x: ", status); + +#define PIS(x) \ + if (status & DISPC_IRQ_##x) \ + printk(#x " "); + PIS(GFX_FIFO_UNDERFLOW); + PIS(OCP_ERR); + PIS(VID1_FIFO_UNDERFLOW); + PIS(VID2_FIFO_UNDERFLOW); + PIS(SYNC_LOST); + PIS(SYNC_LOST_DIGIT); +#undef PIS + + printk("\n"); +} +#endif + +/* Called from dss.c. Note that we don't touch clocks here, + * but we presume they are on because we got an IRQ. However, + * an irq handler may turn the clocks off, so we may not have + * clock later in the function. */ +void dispc_irq_handler(void) +{ + int i; + u32 irqstatus; + u32 handledirqs = 0; + u32 unhandled_errors; + struct omap_dispc_isr_data *isr_data; + struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; + + spin_lock(&dispc.irq_lock); + + irqstatus = dispc_read_reg(DISPC_IRQSTATUS); + +#ifdef DEBUG + if (dss_debug) + print_irq_status(irqstatus); +#endif + /* Ack the interrupt. Do it here before clocks are possibly turned + * off */ + dispc_write_reg(DISPC_IRQSTATUS, irqstatus); + /* flush posted write */ + dispc_read_reg(DISPC_IRQSTATUS); + + /* make a copy and unlock, so that isrs can unregister + * themselves */ + memcpy(registered_isr, dispc.registered_isr, + sizeof(registered_isr)); + + spin_unlock(&dispc.irq_lock); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = ®istered_isr[i]; + + if (!isr_data->isr) + continue; + + if (isr_data->mask & irqstatus) { + isr_data->isr(isr_data->arg, irqstatus); + handledirqs |= isr_data->mask; + } + } + + spin_lock(&dispc.irq_lock); + + unhandled_errors = irqstatus & ~handledirqs & dispc.irq_error_mask; + + if (unhandled_errors) { + dispc.error_irqs |= unhandled_errors; + + dispc.irq_error_mask &= ~unhandled_errors; + _omap_dispc_set_irqs(); + + schedule_work(&dispc.error_work); + } + + spin_unlock(&dispc.irq_lock); +} + +static void dispc_error_worker(struct work_struct *work) +{ + int i; + u32 errors; + unsigned long flags; + + spin_lock_irqsave(&dispc.irq_lock, flags); + errors = dispc.error_irqs; + dispc.error_irqs = 0; + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) { + DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 0) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_VID1_FIFO_UNDERFLOW) { + DSSERR("VID1_FIFO_UNDERFLOW, disabling VID1\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 1) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_VID2_FIFO_UNDERFLOW) { + DSSERR("VID2_FIFO_UNDERFLOW, disabling VID2\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 2) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_SYNC_LOST) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST, disabling LCD\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->id == OMAP_DSS_CHANNEL_LCD) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->disable(mgr->device); + break; + } + } + + if (manager) { + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + manager->device->enable(manager->device); + } + } + + if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST_DIGIT, disabling TV\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->disable(mgr->device); + break; + } + } + + if (manager) { + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + manager->device->enable(manager->device); + } + } + + if (errors & DISPC_IRQ_OCP_ERR) { + DSSERR("OCP_ERR\n"); + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC) + mgr->device->disable(mgr->device); + } + } + + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask |= errors; + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); +} + +int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout) +{ + void dispc_irq_wait_handler(void *data, u32 mask) + { + complete((struct completion *)data); + } + + int r; + DECLARE_COMPLETION_ONSTACK(completion); + + r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, + irqmask); + + if (r) + return r; + + timeout = wait_for_completion_timeout(&completion, timeout); + + omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); + + if (timeout == 0) + return -ETIMEDOUT; + + if (timeout == -ERESTARTSYS) + return -ERESTARTSYS; + + return 0; +} + +int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, + unsigned long timeout) +{ + void dispc_irq_wait_handler(void *data, u32 mask) + { + complete((struct completion *)data); + } + + int r; + DECLARE_COMPLETION_ONSTACK(completion); + + r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, + irqmask); + + if (r) + return r; + + timeout = wait_for_completion_interruptible_timeout(&completion, + timeout); + + omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); + + if (timeout == 0) + return -ETIMEDOUT; + + if (timeout == -ERESTARTSYS) + return -ERESTARTSYS; + + return 0; +} + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC +void dispc_fake_vsync_irq(void) +{ + u32 irqstatus = DISPC_IRQ_VSYNC; + int i; + + local_irq_disable(); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + struct omap_dispc_isr_data *isr_data; + isr_data = &dispc.registered_isr[i]; + + if (!isr_data->isr) + continue; + + if (isr_data->mask & irqstatus) + isr_data->isr(isr_data->arg, irqstatus); + } + + local_irq_enable(); +} +#endif + +static void _omap_dispc_initialize_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + memset(dispc.registered_isr, 0, sizeof(dispc.registered_isr)); + + dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; + + /* there's SYNC_LOST_DIGIT waiting after enabling the DSS, + * so clear it */ + dispc_write_reg(DISPC_IRQSTATUS, dispc_read_reg(DISPC_IRQSTATUS)); + + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); +} + +void dispc_enable_sidle(void) +{ + REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3); /* SIDLEMODE: smart idle */ +} + +void dispc_disable_sidle(void) +{ + REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ +} + +static void _omap_dispc_initial_config(void) +{ + u32 l; + + l = dispc_read_reg(DISPC_SYSCONFIG); + l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */ + l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */ + l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */ + l = FLD_MOD(l, 1, 0, 0); /* AUTOIDLE */ + dispc_write_reg(DISPC_SYSCONFIG, l); + + /* FUNCGATED */ + REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); + + /* L3 firewall setting: enable access to OCM RAM */ + /* XXX this should be somewhere in plat-omap */ + if (cpu_is_omap24xx()) + __raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0)); + + _dispc_setup_color_conv_coef(); + + dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); + + dispc_read_plane_fifo_sizes(); +} + +int dispc_init(void) +{ + u32 rev; + + spin_lock_init(&dispc.irq_lock); + + INIT_WORK(&dispc.error_work, dispc_error_worker); + + dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS); + if (!dispc.base) { + DSSERR("can't ioremap DISPC\n"); + return -ENOMEM; + } + + enable_clocks(1); + + _omap_dispc_initial_config(); + + _omap_dispc_initialize_irq(); + + dispc_save_context(); + + rev = dispc_read_reg(DISPC_REVISION); + printk(KERN_INFO "OMAP DISPC rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + enable_clocks(0); + + return 0; +} + +void dispc_exit(void) +{ + iounmap(dispc.base); +} + +int dispc_enable_plane(enum omap_plane plane, bool enable) +{ + DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); + + enable_clocks(1); + _dispc_enable_plane(plane, enable); + enable_clocks(0); + + return 0; +} + +int dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, bool mirror, u8 global_alpha) +{ + int r = 0; + + DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " + "%dx%d, ilace %d, cmode %x, rot %d, mir %d\n", + plane, paddr, screen_width, pos_x, pos_y, + width, height, + out_width, out_height, + ilace, color_mode, + rotation, mirror); + + enable_clocks(1); + + r = _dispc_setup_plane(plane, + paddr, screen_width, + pos_x, pos_y, + width, height, + out_width, out_height, + color_mode, ilace, + rotation_type, + rotation, mirror, + global_alpha); + + enable_clocks(0); + + return r; +} diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c new file mode 100644 index 000000000000..3b92b84b9560 --- /dev/null +++ b/drivers/video/omap2/dss/display.c @@ -0,0 +1,671 @@ +/* + * linux/drivers/video/omap2/dss/display.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DISPLAY" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/list.h> +#include <linux/platform_device.h> + +#include <plat/display.h> +#include "dss.h" + +static LIST_HEAD(display_list); + +static ssize_t display_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED; + + return snprintf(buf, PAGE_SIZE, "%d\n", enabled); +} + +static ssize_t display_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + bool enabled, r; + + enabled = simple_strtoul(buf, NULL, 10); + + if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { + if (enabled) { + r = dssdev->enable(dssdev); + if (r) + return r; + } else { + dssdev->disable(dssdev); + } + } + + return size; +} + +static ssize_t display_upd_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO; + if (dssdev->get_update_mode) + mode = dssdev->get_update_mode(dssdev); + return snprintf(buf, PAGE_SIZE, "%d\n", mode); +} + +static ssize_t display_upd_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int val, r; + enum omap_dss_update_mode mode; + + val = simple_strtoul(buf, NULL, 10); + + switch (val) { + case OMAP_DSS_UPDATE_DISABLED: + case OMAP_DSS_UPDATE_AUTO: + case OMAP_DSS_UPDATE_MANUAL: + mode = (enum omap_dss_update_mode)val; + break; + default: + return -EINVAL; + } + + r = dssdev->set_update_mode(dssdev, mode); + if (r) + return r; + + return size; +} + +static ssize_t display_tear_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", + dssdev->get_te ? dssdev->get_te(dssdev) : 0); +} + +static ssize_t display_tear_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long te; + int r; + + if (!dssdev->enable_te || !dssdev->get_te) + return -ENOENT; + + te = simple_strtoul(buf, NULL, 0); + + r = dssdev->enable_te(dssdev, te); + if (r) + return r; + + return size; +} + +static ssize_t display_timings_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_video_timings t; + + if (!dssdev->get_timings) + return -ENOENT; + + dssdev->get_timings(dssdev, &t); + + return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", + t.pixel_clock, + t.x_res, t.hfp, t.hbp, t.hsw, + t.y_res, t.vfp, t.vbp, t.vsw); +} + +static ssize_t display_timings_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_video_timings t; + int r, found; + + if (!dssdev->set_timings || !dssdev->check_timings) + return -ENOENT; + + found = 0; +#ifdef CONFIG_OMAP2_DSS_VENC + if (strncmp("pal", buf, 3) == 0) { + t = omap_dss_pal_timings; + found = 1; + } else if (strncmp("ntsc", buf, 4) == 0) { + t = omap_dss_ntsc_timings; + found = 1; + } +#endif + if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu", + &t.pixel_clock, + &t.x_res, &t.hfp, &t.hbp, &t.hsw, + &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9) + return -EINVAL; + + r = dssdev->check_timings(dssdev, &t); + if (r) + return r; + + dssdev->set_timings(dssdev, &t); + + return size; +} + +static ssize_t display_rotate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int rotate; + if (!dssdev->get_rotate) + return -ENOENT; + rotate = dssdev->get_rotate(dssdev); + return snprintf(buf, PAGE_SIZE, "%u\n", rotate); +} + +static ssize_t display_rotate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long rot; + int r; + + if (!dssdev->set_rotate || !dssdev->get_rotate) + return -ENOENT; + + rot = simple_strtoul(buf, NULL, 0); + + r = dssdev->set_rotate(dssdev, rot); + if (r) + return r; + + return size; +} + +static ssize_t display_mirror_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int mirror; + if (!dssdev->get_mirror) + return -ENOENT; + mirror = dssdev->get_mirror(dssdev); + return snprintf(buf, PAGE_SIZE, "%u\n", mirror); +} + +static ssize_t display_mirror_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long mirror; + int r; + + if (!dssdev->set_mirror || !dssdev->get_mirror) + return -ENOENT; + + mirror = simple_strtoul(buf, NULL, 0); + + r = dssdev->set_mirror(dssdev, mirror); + if (r) + return r; + + return size; +} + +static ssize_t display_wss_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned int wss; + + if (!dssdev->get_wss) + return -ENOENT; + + wss = dssdev->get_wss(dssdev); + + return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); +} + +static ssize_t display_wss_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long wss; + int r; + + if (!dssdev->get_wss || !dssdev->set_wss) + return -ENOENT; + + if (strict_strtoul(buf, 0, &wss)) + return -EINVAL; + + if (wss > 0xfffff) + return -EINVAL; + + r = dssdev->set_wss(dssdev, wss); + if (r) + return r; + + return size; +} + +static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, + display_enabled_show, display_enabled_store); +static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR, + display_upd_mode_show, display_upd_mode_store); +static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, + display_tear_show, display_tear_store); +static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, + display_timings_show, display_timings_store); +static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR, + display_rotate_show, display_rotate_store); +static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, + display_mirror_show, display_mirror_store); +static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, + display_wss_show, display_wss_store); + +static struct device_attribute *display_sysfs_attrs[] = { + &dev_attr_enabled, + &dev_attr_update_mode, + &dev_attr_tear_elim, + &dev_attr_timings, + &dev_attr_rotate, + &dev_attr_mirror, + &dev_attr_wss, + NULL +}; + +static void default_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; +} + +void default_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high) +{ + unsigned burst_size_bytes; + + *burst_size = OMAP_DSS_BURST_16x32; + burst_size_bytes = 16 * 32 / 8; + + *fifo_high = fifo_size - 1; + *fifo_low = fifo_size - burst_size_bytes; +} + +static int default_wait_vsync(struct omap_dss_device *dssdev) +{ + unsigned long timeout = msecs_to_jiffies(500); + u32 irq; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) + irq = DISPC_IRQ_EVSYNC_ODD; + else + irq = DISPC_IRQ_VSYNC; + + return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); +} + +static int default_get_recommended_bpp(struct omap_dss_device *dssdev) +{ + if (dssdev->panel.recommended_bpp) + return dssdev->panel.recommended_bpp; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + if (dssdev->phy.dpi.data_lines == 24) + return 24; + else + return 16; + + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_DSI: + if (dssdev->ctrl.pixel_size == 24) + return 24; + else + return 16; + case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_SDI: + return 24; + return 24; + default: + BUG(); + } +} + +/* Checks if replication logic should be used. Only use for active matrix, + * when overlay is in RGB12U or RGB16 mode, and LCD interface is + * 18bpp or 24bpp */ +bool dss_use_replication(struct omap_dss_device *dssdev, + enum omap_color_mode mode) +{ + int bpp; + + if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) + return false; + + if (dssdev->type == OMAP_DISPLAY_TYPE_DPI && + (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) + return false; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + bpp = dssdev->phy.dpi.data_lines; + break; + case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_SDI: + bpp = 24; + break; + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_DSI: + bpp = dssdev->ctrl.pixel_size; + break; + default: + BUG(); + } + + return bpp > 16; +} + +void dss_init_device(struct platform_device *pdev, + struct omap_dss_device *dssdev) +{ + struct device_attribute *attr; + int i; + int r; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: +#ifdef CONFIG_OMAP2_DSS_RFBI + case OMAP_DISPLAY_TYPE_DBI: +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + case OMAP_DISPLAY_TYPE_SDI: +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + case OMAP_DISPLAY_TYPE_VENC: +#endif + break; + default: + DSSERR("Support for display '%s' not compiled in.\n", + dssdev->name); + return; + } + + dssdev->get_resolution = default_get_resolution; + dssdev->get_recommended_bpp = default_get_recommended_bpp; + dssdev->wait_vsync = default_wait_vsync; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + r = dpi_init_display(dssdev); + break; +#ifdef CONFIG_OMAP2_DSS_RFBI + case OMAP_DISPLAY_TYPE_DBI: + r = rfbi_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + case OMAP_DISPLAY_TYPE_VENC: + r = venc_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + case OMAP_DISPLAY_TYPE_SDI: + r = sdi_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: + r = dsi_init_display(dssdev); + break; +#endif + default: + BUG(); + } + + if (r) { + DSSERR("failed to init display %s\n", dssdev->name); + return; + } + + /* create device sysfs files */ + i = 0; + while ((attr = display_sysfs_attrs[i++]) != NULL) { + r = device_create_file(&dssdev->dev, attr); + if (r) + DSSERR("failed to create sysfs file\n"); + } + + /* create display? sysfs links */ + r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj, + dev_name(&dssdev->dev)); + if (r) + DSSERR("failed to create sysfs display link\n"); +} + +void dss_uninit_device(struct platform_device *pdev, + struct omap_dss_device *dssdev) +{ + struct device_attribute *attr; + int i = 0; + + sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev)); + + while ((attr = display_sysfs_attrs[i++]) != NULL) + device_remove_file(&dssdev->dev, attr); + + if (dssdev->manager) + dssdev->manager->unset_device(dssdev->manager); +} + +static int dss_suspend_device(struct device *dev, void *data) +{ + int r; + struct omap_dss_device *dssdev = to_dss_device(dev); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + dssdev->activate_after_resume = false; + return 0; + } + + if (!dssdev->suspend) { + DSSERR("display '%s' doesn't implement suspend\n", + dssdev->name); + return -ENOSYS; + } + + r = dssdev->suspend(dssdev); + if (r) + return r; + + dssdev->activate_after_resume = true; + + return 0; +} + +int dss_suspend_all_devices(void) +{ + int r; + struct bus_type *bus = dss_get_bus(); + + r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device); + if (r) { + /* resume all displays that were suspended */ + dss_resume_all_devices(); + return r; + } + + return 0; +} + +static int dss_resume_device(struct device *dev, void *data) +{ + int r; + struct omap_dss_device *dssdev = to_dss_device(dev); + + if (dssdev->activate_after_resume && dssdev->resume) { + r = dssdev->resume(dssdev); + if (r) + return r; + } + + dssdev->activate_after_resume = false; + + return 0; +} + +int dss_resume_all_devices(void) +{ + struct bus_type *bus = dss_get_bus(); + + return bus_for_each_dev(bus, NULL, NULL, dss_resume_device); +} + +static int dss_disable_device(struct device *dev, void *data) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + dssdev->disable(dssdev); + return 0; +} + +void dss_disable_all_devices(void) +{ + struct bus_type *bus = dss_get_bus(); + bus_for_each_dev(bus, NULL, NULL, dss_disable_device); +} + + +void omap_dss_get_device(struct omap_dss_device *dssdev) +{ + get_device(&dssdev->dev); +} +EXPORT_SYMBOL(omap_dss_get_device); + +void omap_dss_put_device(struct omap_dss_device *dssdev) +{ + put_device(&dssdev->dev); +} +EXPORT_SYMBOL(omap_dss_put_device); + +/* ref count of the found device is incremented. ref count + * of from-device is decremented. */ +struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) +{ + struct device *dev; + struct device *dev_start = NULL; + struct omap_dss_device *dssdev = NULL; + + int match(struct device *dev, void *data) + { + /* skip panels connected to controllers */ + if (to_dss_device(dev)->panel.ctrl) + return 0; + + return 1; + } + + if (from) + dev_start = &from->dev; + dev = bus_find_device(dss_get_bus(), dev_start, NULL, match); + if (dev) + dssdev = to_dss_device(dev); + if (from) + put_device(&from->dev); + + return dssdev; +} +EXPORT_SYMBOL(omap_dss_get_next_device); + +struct omap_dss_device *omap_dss_find_device(void *data, + int (*match)(struct omap_dss_device *dssdev, void *data)) +{ + struct omap_dss_device *dssdev = NULL; + + while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { + if (match(dssdev, data)) + return dssdev; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_find_device); + +int omap_dss_start_device(struct omap_dss_device *dssdev) +{ + int r; + + if (!dssdev->driver) { + DSSDBG("no driver\n"); + r = -ENODEV; + goto err0; + } + + if (dssdev->ctrl.panel && !dssdev->ctrl.panel->driver) { + DSSDBG("no panel driver\n"); + r = -ENODEV; + goto err0; + } + + if (!try_module_get(dssdev->dev.driver->owner)) { + r = -ENODEV; + goto err0; + } + + if (dssdev->ctrl.panel) { + if (!try_module_get(dssdev->ctrl.panel->dev.driver->owner)) { + r = -ENODEV; + goto err1; + } + } + + return 0; +err1: + module_put(dssdev->dev.driver->owner); +err0: + return r; +} +EXPORT_SYMBOL(omap_dss_start_device); + +void omap_dss_stop_device(struct omap_dss_device *dssdev) +{ + if (dssdev->ctrl.panel) + module_put(dssdev->ctrl.panel->dev.driver->owner); + + module_put(dssdev->dev.driver->owner); +} +EXPORT_SYMBOL(omap_dss_stop_device); + diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c new file mode 100644 index 000000000000..2d71031baa25 --- /dev/null +++ b/drivers/video/omap2/dss/dpi.c @@ -0,0 +1,399 @@ +/* + * linux/drivers/video/omap2/dss/dpi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DPI" + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/errno.h> + +#include <plat/display.h> +#include <plat/cpu.h> + +#include "dss.h" + +static struct { + int update_enabled; +} dpi; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL +static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, + unsigned long *fck, int *lck_div, int *pck_div) +{ + struct dsi_clock_info dsi_cinfo; + struct dispc_clock_info dispc_cinfo; + int r; + + r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo, + &dispc_cinfo); + if (r) + return r; + + r = dsi_pll_set_clock_div(&dsi_cinfo); + if (r) + return r; + + dss_select_clk_source(0, 1); + + r = dispc_set_clock_div(&dispc_cinfo); + if (r) + return r; + + *fck = dsi_cinfo.dsi1_pll_fclk; + *lck_div = dispc_cinfo.lck_div; + *pck_div = dispc_cinfo.pck_div; + + return 0; +} +#else +static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req, + unsigned long *fck, int *lck_div, int *pck_div) +{ + struct dss_clock_info dss_cinfo; + struct dispc_clock_info dispc_cinfo; + int r; + + r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo); + if (r) + return r; + + r = dss_set_clock_div(&dss_cinfo); + if (r) + return r; + + r = dispc_set_clock_div(&dispc_cinfo); + if (r) + return r; + + *fck = dss_cinfo.fck; + *lck_div = dispc_cinfo.lck_div; + *pck_div = dispc_cinfo.pck_div; + + return 0; +} +#endif + +static int dpi_set_mode(struct omap_dss_device *dssdev) +{ + struct omap_video_timings *t = &dssdev->panel.timings; + int lck_div, pck_div; + unsigned long fck; + unsigned long pck; + bool is_tft; + int r = 0; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, + dssdev->panel.acb); + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); +#else + r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); +#endif + if (r) + goto err0; + + pck = fck / lck_div / pck_div / 1000; + + if (pck != t->pixel_clock) { + DSSWARN("Could not find exact pixel clock. " + "Requested %d kHz, got %lu kHz\n", + t->pixel_clock, pck); + + t->pixel_clock = pck; + } + + dispc_set_lcd_timings(t); + +err0: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + return r; +} + +static int dpi_basic_init(struct omap_dss_device *dssdev) +{ + bool is_tft; + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); + dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : + OMAP_DSS_LCD_DISPLAY_STN); + dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines); + + return 0; +} + +static int dpi_display_enable(struct omap_dss_device *dssdev) +{ + int r; + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + DSSERR("display already enabled\n"); + r = -EINVAL; + goto err1; + } + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + r = dpi_basic_init(dssdev); + if (r) + goto err2; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dss_clk_enable(DSS_CLK_FCK2); + r = dsi_pll_init(dssdev, 0, 1); + if (r) + goto err3; +#endif + r = dpi_set_mode(dssdev); + if (r) + goto err4; + + mdelay(2); + + dispc_enable_lcd_out(1); + + r = dssdev->driver->enable(dssdev); + if (r) + goto err5; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; + +err5: + dispc_enable_lcd_out(0); +err4: +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dsi_pll_uninit(); +err3: + dss_clk_disable(DSS_CLK_FCK2); +#endif +err2: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +err1: + omap_dss_stop_device(dssdev); +err0: + return r; +} + +static int dpi_display_resume(struct omap_dss_device *dssdev); + +static void dpi_display_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + return; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + dpi_display_resume(dssdev); + + dssdev->driver->disable(dssdev); + + dispc_enable_lcd_out(0); + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dss_select_clk_source(0, 0); + dsi_pll_uninit(); + dss_clk_disable(DSS_CLK_FCK2); +#endif + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + omap_dss_stop_device(dssdev); +} + +static int dpi_display_suspend(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EINVAL; + + DSSDBG("dpi_display_suspend\n"); + + if (dssdev->driver->suspend) + dssdev->driver->suspend(dssdev); + + dispc_enable_lcd_out(0); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int dpi_display_resume(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) + return -EINVAL; + + DSSDBG("dpi_display_resume\n"); + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dispc_enable_lcd_out(1); + + if (dssdev->driver->resume) + dssdev->driver->resume(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void dpi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("dpi_set_timings\n"); + dssdev->panel.timings = *timings; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + dpi_set_mode(dssdev); + dispc_go(OMAP_DSS_CHANNEL_LCD); + } +} + +static int dpi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + bool is_tft; + int r; + int lck_div, pck_div; + unsigned long fck; + unsigned long pck; + + if (!dispc_lcd_timings_ok(timings)) + return -EINVAL; + + if (timings->pixel_clock == 0) + return -EINVAL; + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + { + struct dsi_clock_info dsi_cinfo; + struct dispc_clock_info dispc_cinfo; + r = dsi_pll_calc_clock_div_pck(is_tft, + timings->pixel_clock * 1000, + &dsi_cinfo, &dispc_cinfo); + + if (r) + return r; + + fck = dsi_cinfo.dsi1_pll_fclk; + lck_div = dispc_cinfo.lck_div; + pck_div = dispc_cinfo.pck_div; + } +#else + { + struct dss_clock_info dss_cinfo; + struct dispc_clock_info dispc_cinfo; + r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000, + &dss_cinfo, &dispc_cinfo); + + if (r) + return r; + + fck = dss_cinfo.fck; + lck_div = dispc_cinfo.lck_div; + pck_div = dispc_cinfo.pck_div; + } +#endif + + pck = fck / lck_div / pck_div / 1000; + + timings->pixel_clock = pck; + + return 0; +} + +static void dpi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static int dpi_display_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + if (mode == OMAP_DSS_UPDATE_MANUAL) + return -EINVAL; + + if (mode == OMAP_DSS_UPDATE_DISABLED) { + dispc_enable_lcd_out(0); + dpi.update_enabled = 0; + } else { + dispc_enable_lcd_out(1); + dpi.update_enabled = 1; + } + + return 0; +} + +static enum omap_dss_update_mode dpi_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO : + OMAP_DSS_UPDATE_DISABLED; +} + +int dpi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("init_display\n"); + + dssdev->enable = dpi_display_enable; + dssdev->disable = dpi_display_disable; + dssdev->suspend = dpi_display_suspend; + dssdev->resume = dpi_display_resume; + dssdev->set_timings = dpi_set_timings; + dssdev->check_timings = dpi_check_timings; + dssdev->get_timings = dpi_get_timings; + dssdev->set_update_mode = dpi_display_set_update_mode; + dssdev->get_update_mode = dpi_display_get_update_mode; + + return 0; +} + +int dpi_init(void) +{ + return 0; +} + +void dpi_exit(void) +{ +} + diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c new file mode 100644 index 000000000000..5936487b5def --- /dev/null +++ b/drivers/video/omap2/dss/dsi.c @@ -0,0 +1,3710 @@ +/* + * linux/drivers/video/omap2/dss/dsi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DSI" + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/seq_file.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/kthread.h> +#include <linux/wait.h> + +#include <plat/display.h> +#include <plat/clock.h> + +#include "dss.h" + +/*#define VERBOSE_IRQ*/ +#define DSI_CATCH_MISSING_TE + +#define DSI_BASE 0x4804FC00 + +struct dsi_reg { u16 idx; }; + +#define DSI_REG(idx) ((const struct dsi_reg) { idx }) + +#define DSI_SZ_REGS SZ_1K +/* DSI Protocol Engine */ + +#define DSI_REVISION DSI_REG(0x0000) +#define DSI_SYSCONFIG DSI_REG(0x0010) +#define DSI_SYSSTATUS DSI_REG(0x0014) +#define DSI_IRQSTATUS DSI_REG(0x0018) +#define DSI_IRQENABLE DSI_REG(0x001C) +#define DSI_CTRL DSI_REG(0x0040) +#define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048) +#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C) +#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050) +#define DSI_CLK_CTRL DSI_REG(0x0054) +#define DSI_TIMING1 DSI_REG(0x0058) +#define DSI_TIMING2 DSI_REG(0x005C) +#define DSI_VM_TIMING1 DSI_REG(0x0060) +#define DSI_VM_TIMING2 DSI_REG(0x0064) +#define DSI_VM_TIMING3 DSI_REG(0x0068) +#define DSI_CLK_TIMING DSI_REG(0x006C) +#define DSI_TX_FIFO_VC_SIZE DSI_REG(0x0070) +#define DSI_RX_FIFO_VC_SIZE DSI_REG(0x0074) +#define DSI_COMPLEXIO_CFG2 DSI_REG(0x0078) +#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(0x007C) +#define DSI_VM_TIMING4 DSI_REG(0x0080) +#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(0x0084) +#define DSI_VM_TIMING5 DSI_REG(0x0088) +#define DSI_VM_TIMING6 DSI_REG(0x008C) +#define DSI_VM_TIMING7 DSI_REG(0x0090) +#define DSI_STOPCLK_TIMING DSI_REG(0x0094) +#define DSI_VC_CTRL(n) DSI_REG(0x0100 + (n * 0x20)) +#define DSI_VC_TE(n) DSI_REG(0x0104 + (n * 0x20)) +#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(0x0108 + (n * 0x20)) +#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(0x010C + (n * 0x20)) +#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(0x0110 + (n * 0x20)) +#define DSI_VC_IRQSTATUS(n) DSI_REG(0x0118 + (n * 0x20)) +#define DSI_VC_IRQENABLE(n) DSI_REG(0x011C + (n * 0x20)) + +/* DSIPHY_SCP */ + +#define DSI_DSIPHY_CFG0 DSI_REG(0x200 + 0x0000) +#define DSI_DSIPHY_CFG1 DSI_REG(0x200 + 0x0004) +#define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008) +#define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014) + +/* DSI_PLL_CTRL_SCP */ + +#define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000) +#define DSI_PLL_STATUS DSI_REG(0x300 + 0x0004) +#define DSI_PLL_GO DSI_REG(0x300 + 0x0008) +#define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C) +#define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010) + +#define REG_GET(idx, start, end) \ + FLD_GET(dsi_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end)) + +/* Global interrupts */ +#define DSI_IRQ_VC0 (1 << 0) +#define DSI_IRQ_VC1 (1 << 1) +#define DSI_IRQ_VC2 (1 << 2) +#define DSI_IRQ_VC3 (1 << 3) +#define DSI_IRQ_WAKEUP (1 << 4) +#define DSI_IRQ_RESYNC (1 << 5) +#define DSI_IRQ_PLL_LOCK (1 << 7) +#define DSI_IRQ_PLL_UNLOCK (1 << 8) +#define DSI_IRQ_PLL_RECALL (1 << 9) +#define DSI_IRQ_COMPLEXIO_ERR (1 << 10) +#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14) +#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15) +#define DSI_IRQ_TE_TRIGGER (1 << 16) +#define DSI_IRQ_ACK_TRIGGER (1 << 17) +#define DSI_IRQ_SYNC_LOST (1 << 18) +#define DSI_IRQ_LDO_POWER_GOOD (1 << 19) +#define DSI_IRQ_TA_TIMEOUT (1 << 20) +#define DSI_IRQ_ERROR_MASK \ + (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \ + DSI_IRQ_TA_TIMEOUT) +#define DSI_IRQ_CHANNEL_MASK 0xf + +/* Virtual channel interrupts */ +#define DSI_VC_IRQ_CS (1 << 0) +#define DSI_VC_IRQ_ECC_CORR (1 << 1) +#define DSI_VC_IRQ_PACKET_SENT (1 << 2) +#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3) +#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4) +#define DSI_VC_IRQ_BTA (1 << 5) +#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6) +#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7) +#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8) +#define DSI_VC_IRQ_ERROR_MASK \ + (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \ + DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \ + DSI_VC_IRQ_FIFO_TX_UDF) + +/* ComplexIO interrupts */ +#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) +#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) +#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) +#define DSI_CIO_IRQ_ERRESC1 (1 << 5) +#define DSI_CIO_IRQ_ERRESC2 (1 << 6) +#define DSI_CIO_IRQ_ERRESC3 (1 << 7) +#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) +#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) +#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) +#define DSI_CIO_IRQ_STATEULPS1 (1 << 15) +#define DSI_CIO_IRQ_STATEULPS2 (1 << 16) +#define DSI_CIO_IRQ_STATEULPS3 (1 << 17) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) + +#define DSI_DT_DCS_SHORT_WRITE_0 0x05 +#define DSI_DT_DCS_SHORT_WRITE_1 0x15 +#define DSI_DT_DCS_READ 0x06 +#define DSI_DT_SET_MAX_RET_PKG_SIZE 0x37 +#define DSI_DT_NULL_PACKET 0x09 +#define DSI_DT_DCS_LONG_WRITE 0x39 + +#define DSI_DT_RX_ACK_WITH_ERR 0x02 +#define DSI_DT_RX_DCS_LONG_READ 0x1c +#define DSI_DT_RX_SHORT_READ_1 0x21 +#define DSI_DT_RX_SHORT_READ_2 0x22 + +#define FINT_MAX 2100000 +#define FINT_MIN 750000 +#define REGN_MAX (1 << 7) +#define REGM_MAX ((1 << 11) - 1) +#define REGM3_MAX (1 << 4) +#define REGM4_MAX (1 << 4) +#define LP_DIV_MAX ((1 << 13) - 1) + +enum fifo_size { + DSI_FIFO_SIZE_0 = 0, + DSI_FIFO_SIZE_32 = 1, + DSI_FIFO_SIZE_64 = 2, + DSI_FIFO_SIZE_96 = 3, + DSI_FIFO_SIZE_128 = 4, +}; + +enum dsi_vc_mode { + DSI_VC_MODE_L4 = 0, + DSI_VC_MODE_VP, +}; + +struct dsi_update_region { + bool dirty; + u16 x, y, w, h; + struct omap_dss_device *device; +}; + +static struct +{ + void __iomem *base; + + struct dsi_clock_info current_cinfo; + + struct regulator *vdds_dsi_reg; + + struct { + enum dsi_vc_mode mode; + struct omap_dss_device *dssdev; + enum fifo_size fifo_size; + int dest_per; /* destination peripheral 0-3 */ + } vc[4]; + + struct mutex lock; + struct mutex bus_lock; + + unsigned pll_locked; + + struct completion bta_completion; + + struct task_struct *thread; + wait_queue_head_t waitqueue; + + spinlock_t update_lock; + bool framedone_received; + struct dsi_update_region update_region; + struct dsi_update_region active_update_region; + struct completion update_completion; + + enum omap_dss_update_mode user_update_mode; + enum omap_dss_update_mode update_mode; + bool te_enabled; + bool use_ext_te; + +#ifdef DSI_CATCH_MISSING_TE + struct timer_list te_timer; +#endif + + unsigned long cache_req_pck; + unsigned long cache_clk_freq; + struct dsi_clock_info cache_cinfo; + + u32 errors; + spinlock_t errors_lock; +#ifdef DEBUG + ktime_t perf_setup_time; + ktime_t perf_start_time; + ktime_t perf_start_time_auto; + int perf_measure_frames; +#endif + int debug_read; + int debug_write; +} dsi; + +#ifdef DEBUG +static unsigned int dsi_perf; +module_param_named(dsi_perf, dsi_perf, bool, 0644); +#endif + +static inline void dsi_write_reg(const struct dsi_reg idx, u32 val) +{ + __raw_writel(val, dsi.base + idx.idx); +} + +static inline u32 dsi_read_reg(const struct dsi_reg idx) +{ + return __raw_readl(dsi.base + idx.idx); +} + + +void dsi_save_context(void) +{ +} + +void dsi_restore_context(void) +{ +} + +void dsi_bus_lock(void) +{ + mutex_lock(&dsi.bus_lock); +} +EXPORT_SYMBOL(dsi_bus_lock); + +void dsi_bus_unlock(void) +{ + mutex_unlock(&dsi.bus_lock); +} +EXPORT_SYMBOL(dsi_bus_unlock); + +static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, + int value) +{ + int t = 100000; + + while (REG_GET(idx, bitnum, bitnum) != value) { + if (--t == 0) + return !value; + } + + return value; +} + +#ifdef DEBUG +static void dsi_perf_mark_setup(void) +{ + dsi.perf_setup_time = ktime_get(); +} + +static void dsi_perf_mark_start(void) +{ + dsi.perf_start_time = ktime_get(); +} + +static void dsi_perf_mark_start_auto(void) +{ + dsi.perf_measure_frames = 0; + dsi.perf_start_time_auto = ktime_get(); +} + +static void dsi_perf_show(const char *name) +{ + ktime_t t, setup_time, trans_time; + u32 total_bytes; + u32 setup_us, trans_us, total_us; + + if (!dsi_perf) + return; + + if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED) + return; + + t = ktime_get(); + + setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time); + setup_us = (u32)ktime_to_us(setup_time); + if (setup_us == 0) + setup_us = 1; + + trans_time = ktime_sub(t, dsi.perf_start_time); + trans_us = (u32)ktime_to_us(trans_time); + if (trans_us == 0) + trans_us = 1; + + total_us = setup_us + trans_us; + + total_bytes = dsi.active_update_region.w * + dsi.active_update_region.h * + dsi.active_update_region.device->ctrl.pixel_size / 8; + + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { + static u32 s_total_trans_us, s_total_setup_us; + static u32 s_min_trans_us = 0xffffffff, s_min_setup_us; + static u32 s_max_trans_us, s_max_setup_us; + const int numframes = 100; + ktime_t total_time_auto; + u32 total_time_auto_us; + + dsi.perf_measure_frames++; + + if (setup_us < s_min_setup_us) + s_min_setup_us = setup_us; + + if (setup_us > s_max_setup_us) + s_max_setup_us = setup_us; + + s_total_setup_us += setup_us; + + if (trans_us < s_min_trans_us) + s_min_trans_us = trans_us; + + if (trans_us > s_max_trans_us) + s_max_trans_us = trans_us; + + s_total_trans_us += trans_us; + + if (dsi.perf_measure_frames < numframes) + return; + + total_time_auto = ktime_sub(t, dsi.perf_start_time_auto); + total_time_auto_us = (u32)ktime_to_us(total_time_auto); + + printk(KERN_INFO "DSI(%s): %u fps, setup %u/%u/%u, " + "trans %u/%u/%u\n", + name, + 1000 * 1000 * numframes / total_time_auto_us, + s_min_setup_us, + s_max_setup_us, + s_total_setup_us / numframes, + s_min_trans_us, + s_max_trans_us, + s_total_trans_us / numframes); + + s_total_setup_us = 0; + s_min_setup_us = 0xffffffff; + s_max_setup_us = 0; + s_total_trans_us = 0; + s_min_trans_us = 0xffffffff; + s_max_trans_us = 0; + dsi_perf_mark_start_auto(); + } else { + printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), " + "%u bytes, %u kbytes/sec\n", + name, + setup_us, + trans_us, + total_us, + 1000*1000 / total_us, + total_bytes, + total_bytes * 1000 / total_us); + } +} +#else +#define dsi_perf_mark_setup() +#define dsi_perf_mark_start() +#define dsi_perf_mark_start_auto() +#define dsi_perf_show(x) +#endif + +static void print_irq_status(u32 status) +{ +#ifndef VERBOSE_IRQ + if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0) + return; +#endif + printk(KERN_DEBUG "DSI IRQ: 0x%x: ", status); + +#define PIS(x) \ + if (status & DSI_IRQ_##x) \ + printk(#x " "); +#ifdef VERBOSE_IRQ + PIS(VC0); + PIS(VC1); + PIS(VC2); + PIS(VC3); +#endif + PIS(WAKEUP); + PIS(RESYNC); + PIS(PLL_LOCK); + PIS(PLL_UNLOCK); + PIS(PLL_RECALL); + PIS(COMPLEXIO_ERR); + PIS(HS_TX_TIMEOUT); + PIS(LP_RX_TIMEOUT); + PIS(TE_TRIGGER); + PIS(ACK_TRIGGER); + PIS(SYNC_LOST); + PIS(LDO_POWER_GOOD); + PIS(TA_TIMEOUT); +#undef PIS + + printk("\n"); +} + +static void print_irq_status_vc(int channel, u32 status) +{ +#ifndef VERBOSE_IRQ + if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0) + return; +#endif + printk(KERN_DEBUG "DSI VC(%d) IRQ 0x%x: ", channel, status); + +#define PIS(x) \ + if (status & DSI_VC_IRQ_##x) \ + printk(#x " "); + PIS(CS); + PIS(ECC_CORR); +#ifdef VERBOSE_IRQ + PIS(PACKET_SENT); +#endif + PIS(FIFO_TX_OVF); + PIS(FIFO_RX_OVF); + PIS(BTA); + PIS(ECC_NO_CORR); + PIS(FIFO_TX_UDF); + PIS(PP_BUSY_CHANGE); +#undef PIS + printk("\n"); +} + +static void print_irq_status_cio(u32 status) +{ + printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status); + +#define PIS(x) \ + if (status & DSI_CIO_IRQ_##x) \ + printk(#x " "); + PIS(ERRSYNCESC1); + PIS(ERRSYNCESC2); + PIS(ERRSYNCESC3); + PIS(ERRESC1); + PIS(ERRESC2); + PIS(ERRESC3); + PIS(ERRCONTROL1); + PIS(ERRCONTROL2); + PIS(ERRCONTROL3); + PIS(STATEULPS1); + PIS(STATEULPS2); + PIS(STATEULPS3); + PIS(ERRCONTENTIONLP0_1); + PIS(ERRCONTENTIONLP1_1); + PIS(ERRCONTENTIONLP0_2); + PIS(ERRCONTENTIONLP1_2); + PIS(ERRCONTENTIONLP0_3); + PIS(ERRCONTENTIONLP1_3); + PIS(ULPSACTIVENOT_ALL0); + PIS(ULPSACTIVENOT_ALL1); +#undef PIS + + printk("\n"); +} + +static int debug_irq; + +/* called from dss */ +void dsi_irq_handler(void) +{ + u32 irqstatus, vcstatus, ciostatus; + int i; + + irqstatus = dsi_read_reg(DSI_IRQSTATUS); + + if (irqstatus & DSI_IRQ_ERROR_MASK) { + DSSERR("DSI error, irqstatus %x\n", irqstatus); + print_irq_status(irqstatus); + spin_lock(&dsi.errors_lock); + dsi.errors |= irqstatus & DSI_IRQ_ERROR_MASK; + spin_unlock(&dsi.errors_lock); + } else if (debug_irq) { + print_irq_status(irqstatus); + } + +#ifdef DSI_CATCH_MISSING_TE + if (irqstatus & DSI_IRQ_TE_TRIGGER) + del_timer(&dsi.te_timer); +#endif + + for (i = 0; i < 4; ++i) { + if ((irqstatus & (1<<i)) == 0) + continue; + + vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + + if (vcstatus & DSI_VC_IRQ_BTA) + complete(&dsi.bta_completion); + + if (vcstatus & DSI_VC_IRQ_ERROR_MASK) { + DSSERR("DSI VC(%d) error, vc irqstatus %x\n", + i, vcstatus); + print_irq_status_vc(i, vcstatus); + } else if (debug_irq) { + print_irq_status_vc(i, vcstatus); + } + + dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus); + /* flush posted write */ + dsi_read_reg(DSI_VC_IRQSTATUS(i)); + } + + if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { + ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + + dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus); + /* flush posted write */ + dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + + DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); + print_irq_status_cio(ciostatus); + } + + dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); + /* flush posted write */ + dsi_read_reg(DSI_IRQSTATUS); +} + + +static void _dsi_initialize_irq(void) +{ + u32 l; + int i; + + /* disable all interrupts */ + dsi_write_reg(DSI_IRQENABLE, 0); + for (i = 0; i < 4; ++i) + dsi_write_reg(DSI_VC_IRQENABLE(i), 0); + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0); + + /* clear interrupt status */ + l = dsi_read_reg(DSI_IRQSTATUS); + dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK); + + for (i = 0; i < 4; ++i) { + l = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + dsi_write_reg(DSI_VC_IRQSTATUS(i), l); + } + + l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l); + + /* enable error irqs */ + l = DSI_IRQ_ERROR_MASK; +#ifdef DSI_CATCH_MISSING_TE + l |= DSI_IRQ_TE_TRIGGER; +#endif + dsi_write_reg(DSI_IRQENABLE, l); + + l = DSI_VC_IRQ_ERROR_MASK; + for (i = 0; i < 4; ++i) + dsi_write_reg(DSI_VC_IRQENABLE(i), l); + + /* XXX zonda responds incorrectly, causing control error: + Exit from LP-ESC mode to LP11 uses wrong transition states on the + data lines LP0 and LN0. */ + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, + -1 & (~DSI_CIO_IRQ_ERRCONTROL2)); +} + +static u32 dsi_get_errors(void) +{ + unsigned long flags; + u32 e; + spin_lock_irqsave(&dsi.errors_lock, flags); + e = dsi.errors; + dsi.errors = 0; + spin_unlock_irqrestore(&dsi.errors_lock, flags); + return e; +} + +static void dsi_vc_enable_bta_irq(int channel) +{ + u32 l; + + dsi_write_reg(DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA); + + l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); + l |= DSI_VC_IRQ_BTA; + dsi_write_reg(DSI_VC_IRQENABLE(channel), l); +} + +static void dsi_vc_disable_bta_irq(int channel) +{ + u32 l; + + l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); + l &= ~DSI_VC_IRQ_BTA; + dsi_write_reg(DSI_VC_IRQENABLE(channel), l); +} + +/* DSI func clock. this could also be DSI2_PLL_FCLK */ +static inline void enable_clocks(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +/* source clock for DSI PLL. this could also be PCLKFREE */ +static inline void dsi_enable_pll_clock(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_FCK2); + else + dss_clk_disable(DSS_CLK_FCK2); + + if (enable && dsi.pll_locked) { + if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) + DSSERR("cannot lock PLL when enabling clocks\n"); + } +} + +#ifdef DEBUG +static void _dsi_print_reset_status(void) +{ + u32 l; + + if (!dss_debug) + return; + + /* A dummy read using the SCP interface to any DSIPHY register is + * required after DSIPHY reset to complete the reset of the DSI complex + * I/O. */ + l = dsi_read_reg(DSI_DSIPHY_CFG5); + + printk(KERN_DEBUG "DSI resets: "); + + l = dsi_read_reg(DSI_PLL_STATUS); + printk("PLL (%d) ", FLD_GET(l, 0, 0)); + + l = dsi_read_reg(DSI_COMPLEXIO_CFG1); + printk("CIO (%d) ", FLD_GET(l, 29, 29)); + + l = dsi_read_reg(DSI_DSIPHY_CFG5); + printk("PHY (%x, %d, %d, %d)\n", + FLD_GET(l, 28, 26), + FLD_GET(l, 29, 29), + FLD_GET(l, 30, 30), + FLD_GET(l, 31, 31)); +} +#else +#define _dsi_print_reset_status() +#endif + +static inline int dsi_if_enable(bool enable) +{ + DSSDBG("dsi_if_enable(%d)\n", enable); + + enable = enable ? 1 : 0; + REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */ + + if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) { + DSSERR("Failed to set dsi_if_enable to %d\n", enable); + return -EIO; + } + + return 0; +} + +unsigned long dsi_get_dsi1_pll_rate(void) +{ + return dsi.current_cinfo.dsi1_pll_fclk; +} + +static unsigned long dsi_get_dsi2_pll_rate(void) +{ + return dsi.current_cinfo.dsi2_pll_fclk; +} + +static unsigned long dsi_get_txbyteclkhs(void) +{ + return dsi.current_cinfo.clkin4ddr / 16; +} + +static unsigned long dsi_fclk_rate(void) +{ + unsigned long r; + + if (dss_get_dsi_clk_source() == 0) { + /* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */ + r = dss_clk_get_rate(DSS_CLK_FCK1); + } else { + /* DSI FCLK source is DSI2_PLL_FCLK */ + r = dsi_get_dsi2_pll_rate(); + } + + return r; +} + +static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev) +{ + unsigned long dsi_fclk; + unsigned lp_clk_div; + unsigned long lp_clk; + + lp_clk_div = dssdev->phy.dsi.div.lp_clk_div; + + if (lp_clk_div == 0 || lp_clk_div > LP_DIV_MAX) + return -EINVAL; + + dsi_fclk = dsi_fclk_rate(); + + lp_clk = dsi_fclk / 2 / lp_clk_div; + + DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); + dsi.current_cinfo.lp_clk = lp_clk; + dsi.current_cinfo.lp_clk_div = lp_clk_div; + + REG_FLD_MOD(DSI_CLK_CTRL, lp_clk_div, 12, 0); /* LP_CLK_DIVISOR */ + + REG_FLD_MOD(DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, + 21, 21); /* LP_RX_SYNCHRO_ENABLE */ + + return 0; +} + + +enum dsi_pll_power_state { + DSI_PLL_POWER_OFF = 0x0, + DSI_PLL_POWER_ON_HSCLK = 0x1, + DSI_PLL_POWER_ON_ALL = 0x2, + DSI_PLL_POWER_ON_DIV = 0x3, +}; + +static int dsi_pll_power(enum dsi_pll_power_state state) +{ + int t = 0; + + REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */ + + /* PLL_PWR_STATUS */ + while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) { + udelay(1); + if (t++ > 1000) { + DSSERR("Failed to set DSI PLL power mode to %d\n", + state); + return -ENODEV; + } + } + + return 0; +} + +/* calculate clock rates using dividers in cinfo */ +static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo) +{ + if (cinfo->regn == 0 || cinfo->regn > REGN_MAX) + return -EINVAL; + + if (cinfo->regm == 0 || cinfo->regm > REGM_MAX) + return -EINVAL; + + if (cinfo->regm3 > REGM3_MAX) + return -EINVAL; + + if (cinfo->regm4 > REGM4_MAX) + return -EINVAL; + + if (cinfo->use_dss2_fck) { + cinfo->clkin = dss_clk_get_rate(DSS_CLK_FCK2); + /* XXX it is unclear if highfreq should be used + * with DSS2_FCK source also */ + cinfo->highfreq = 0; + } else { + cinfo->clkin = dispc_pclk_rate(); + + if (cinfo->clkin < 32000000) + cinfo->highfreq = 0; + else + cinfo->highfreq = 1; + } + + cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1)); + + if (cinfo->fint > FINT_MAX || cinfo->fint < FINT_MIN) + return -EINVAL; + + cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint; + + if (cinfo->clkin4ddr > 1800 * 1000 * 1000) + return -EINVAL; + + if (cinfo->regm3 > 0) + cinfo->dsi1_pll_fclk = cinfo->clkin4ddr / cinfo->regm3; + else + cinfo->dsi1_pll_fclk = 0; + + if (cinfo->regm4 > 0) + cinfo->dsi2_pll_fclk = cinfo->clkin4ddr / cinfo->regm4; + else + cinfo->dsi2_pll_fclk = 0; + + return 0; +} + +int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, + struct dsi_clock_info *dsi_cinfo, + struct dispc_clock_info *dispc_cinfo) +{ + struct dsi_clock_info cur, best; + struct dispc_clock_info best_dispc; + int min_fck_per_pck; + int match = 0; + unsigned long dss_clk_fck2; + + dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2); + + if (req_pck == dsi.cache_req_pck && + dsi.cache_cinfo.clkin == dss_clk_fck2) { + DSSDBG("DSI clock info found from cache\n"); + *dsi_cinfo = dsi.cache_cinfo; + dispc_find_clk_divs(is_tft, req_pck, dsi_cinfo->dsi1_pll_fclk, + dispc_cinfo); + return 0; + } + + min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; + + if (min_fck_per_pck && + req_pck * min_fck_per_pck > DISPC_MAX_FCK) { + DSSERR("Requested pixel clock not possible with the current " + "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " + "the constraint off.\n"); + min_fck_per_pck = 0; + } + + DSSDBG("dsi_pll_calc\n"); + +retry: + memset(&best, 0, sizeof(best)); + memset(&best_dispc, 0, sizeof(best_dispc)); + + memset(&cur, 0, sizeof(cur)); + cur.clkin = dss_clk_fck2; + cur.use_dss2_fck = 1; + cur.highfreq = 0; + + /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ + /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ + /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ + for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) { + if (cur.highfreq == 0) + cur.fint = cur.clkin / cur.regn; + else + cur.fint = cur.clkin / (2 * cur.regn); + + if (cur.fint > FINT_MAX || cur.fint < FINT_MIN) + continue; + + /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ + for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) { + unsigned long a, b; + + a = 2 * cur.regm * (cur.clkin/1000); + b = cur.regn * (cur.highfreq + 1); + cur.clkin4ddr = a / b * 1000; + + if (cur.clkin4ddr > 1800 * 1000 * 1000) + break; + + /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */ + for (cur.regm3 = 1; cur.regm3 < REGM3_MAX; + ++cur.regm3) { + struct dispc_clock_info cur_dispc; + cur.dsi1_pll_fclk = cur.clkin4ddr / cur.regm3; + + /* this will narrow down the search a bit, + * but still give pixclocks below what was + * requested */ + if (cur.dsi1_pll_fclk < req_pck) + break; + + if (cur.dsi1_pll_fclk > DISPC_MAX_FCK) + continue; + + if (min_fck_per_pck && + cur.dsi1_pll_fclk < + req_pck * min_fck_per_pck) + continue; + + match = 1; + + dispc_find_clk_divs(is_tft, req_pck, + cur.dsi1_pll_fclk, + &cur_dispc); + + if (abs(cur_dispc.pck - req_pck) < + abs(best_dispc.pck - req_pck)) { + best = cur; + best_dispc = cur_dispc; + + if (cur_dispc.pck == req_pck) + goto found; + } + } + } + } +found: + if (!match) { + if (min_fck_per_pck) { + DSSERR("Could not find suitable clock settings.\n" + "Turning FCK/PCK constraint off and" + "trying again.\n"); + min_fck_per_pck = 0; + goto retry; + } + + DSSERR("Could not find suitable clock settings.\n"); + + return -EINVAL; + } + + /* DSI2_PLL_FCLK (regm4) is not used */ + best.regm4 = 0; + best.dsi2_pll_fclk = 0; + + if (dsi_cinfo) + *dsi_cinfo = best; + if (dispc_cinfo) + *dispc_cinfo = best_dispc; + + dsi.cache_req_pck = req_pck; + dsi.cache_clk_freq = 0; + dsi.cache_cinfo = best; + + return 0; +} + +int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) +{ + int r = 0; + u32 l; + int f; + + DSSDBGF(); + + dsi.current_cinfo.fint = cinfo->fint; + dsi.current_cinfo.clkin4ddr = cinfo->clkin4ddr; + dsi.current_cinfo.dsi1_pll_fclk = cinfo->dsi1_pll_fclk; + dsi.current_cinfo.dsi2_pll_fclk = cinfo->dsi2_pll_fclk; + + dsi.current_cinfo.regn = cinfo->regn; + dsi.current_cinfo.regm = cinfo->regm; + dsi.current_cinfo.regm3 = cinfo->regm3; + dsi.current_cinfo.regm4 = cinfo->regm4; + + DSSDBG("DSI Fint %ld\n", cinfo->fint); + + DSSDBG("clkin (%s) rate %ld, highfreq %d\n", + cinfo->use_dss2_fck ? "dss2_fck" : "pclkfree", + cinfo->clkin, + cinfo->highfreq); + + /* DSIPHY == CLKIN4DDR */ + DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu / %d = %lu\n", + cinfo->regm, + cinfo->regn, + cinfo->clkin, + cinfo->highfreq + 1, + cinfo->clkin4ddr); + + DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", + cinfo->clkin4ddr / 1000 / 1000 / 2); + + DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4); + + DSSDBG("regm3 = %d, dsi1_pll_fclk = %lu\n", + cinfo->regm3, cinfo->dsi1_pll_fclk); + DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n", + cinfo->regm4, cinfo->dsi2_pll_fclk); + + REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */ + + l = dsi_read_reg(DSI_PLL_CONFIGURATION1); + l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */ + l = FLD_MOD(l, cinfo->regn - 1, 7, 1); /* DSI_PLL_REGN */ + l = FLD_MOD(l, cinfo->regm, 18, 8); /* DSI_PLL_REGM */ + l = FLD_MOD(l, cinfo->regm3 > 0 ? cinfo->regm3 - 1 : 0, + 22, 19); /* DSI_CLOCK_DIV */ + l = FLD_MOD(l, cinfo->regm4 > 0 ? cinfo->regm4 - 1 : 0, + 26, 23); /* DSIPROTO_CLOCK_DIV */ + dsi_write_reg(DSI_PLL_CONFIGURATION1, l); + + BUG_ON(cinfo->fint < 750000 || cinfo->fint > 2100000); + if (cinfo->fint < 1000000) + f = 0x3; + else if (cinfo->fint < 1250000) + f = 0x4; + else if (cinfo->fint < 1500000) + f = 0x5; + else if (cinfo->fint < 1750000) + f = 0x6; + else + f = 0x7; + + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ + l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1, + 11, 11); /* DSI_PLL_CLKSEL */ + l = FLD_MOD(l, cinfo->highfreq, + 12, 12); /* DSI_PLL_HIGHFREQ */ + l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ + l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ + l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ + dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + + REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ + + if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) { + DSSERR("dsi pll go bit not going down.\n"); + r = -EIO; + goto err; + } + + if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) { + DSSERR("cannot lock PLL\n"); + r = -EIO; + goto err; + } + + dsi.pll_locked = 1; + + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */ + l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */ + l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */ + l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */ + l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */ + l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */ + l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ + l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */ + l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */ + l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */ + l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */ + l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */ + l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */ + l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */ + dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + + DSSDBG("PLL config done\n"); +err: + return r; +} + +int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, + bool enable_hsdiv) +{ + int r = 0; + enum dsi_pll_power_state pwstate; + + DSSDBG("PLL init\n"); + + enable_clocks(1); + dsi_enable_pll_clock(1); + + r = regulator_enable(dsi.vdds_dsi_reg); + if (r) + goto err0; + + /* XXX PLL does not come out of reset without this... */ + dispc_pck_free_enable(1); + + if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { + DSSERR("PLL not coming out of reset.\n"); + r = -ENODEV; + goto err1; + } + + /* XXX ... but if left on, we get problems when planes do not + * fill the whole display. No idea about this */ + dispc_pck_free_enable(0); + + if (enable_hsclk && enable_hsdiv) + pwstate = DSI_PLL_POWER_ON_ALL; + else if (enable_hsclk) + pwstate = DSI_PLL_POWER_ON_HSCLK; + else if (enable_hsdiv) + pwstate = DSI_PLL_POWER_ON_DIV; + else + pwstate = DSI_PLL_POWER_OFF; + + r = dsi_pll_power(pwstate); + + if (r) + goto err1; + + DSSDBG("PLL init done\n"); + + return 0; +err1: + regulator_disable(dsi.vdds_dsi_reg); +err0: + enable_clocks(0); + dsi_enable_pll_clock(0); + return r; +} + +void dsi_pll_uninit(void) +{ + enable_clocks(0); + dsi_enable_pll_clock(0); + + dsi.pll_locked = 0; + dsi_pll_power(DSI_PLL_POWER_OFF); + regulator_disable(dsi.vdds_dsi_reg); + DSSDBG("PLL uninit done\n"); +} + +void dsi_dump_clocks(struct seq_file *s) +{ + int clksel; + struct dsi_clock_info *cinfo = &dsi.current_cinfo; + + enable_clocks(1); + + clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11); + + seq_printf(s, "- DSI PLL -\n"); + + seq_printf(s, "dsi pll source = %s\n", + clksel == 0 ? + "dss2_alwon_fclk" : "pclkfree"); + + seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn); + + seq_printf(s, "CLKIN4DDR\t%-16luregm %u\n", + cinfo->clkin4ddr, cinfo->regm); + + seq_printf(s, "dsi1_pll_fck\t%-16luregm3 %u\t(%s)\n", + cinfo->dsi1_pll_fclk, + cinfo->regm3, + dss_get_dispc_clk_source() == 0 ? "off" : "on"); + + seq_printf(s, "dsi2_pll_fck\t%-16luregm4 %u\t(%s)\n", + cinfo->dsi2_pll_fclk, + cinfo->regm4, + dss_get_dsi_clk_source() == 0 ? "off" : "on"); + + seq_printf(s, "- DSI -\n"); + + seq_printf(s, "dsi fclk source = %s\n", + dss_get_dsi_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi2_pll_fclk"); + + seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate()); + + seq_printf(s, "DDR_CLK\t\t%lu\n", + cinfo->clkin4ddr / 4); + + seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs()); + + seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk); + + seq_printf(s, "VP_CLK\t\t%lu\n" + "VP_PCLK\t\t%lu\n", + dispc_lclk_rate(), + dispc_pclk_rate()); + + enable_clocks(0); +} + +void dsi_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(DSI_REVISION); + DUMPREG(DSI_SYSCONFIG); + DUMPREG(DSI_SYSSTATUS); + DUMPREG(DSI_IRQSTATUS); + DUMPREG(DSI_IRQENABLE); + DUMPREG(DSI_CTRL); + DUMPREG(DSI_COMPLEXIO_CFG1); + DUMPREG(DSI_COMPLEXIO_IRQ_STATUS); + DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE); + DUMPREG(DSI_CLK_CTRL); + DUMPREG(DSI_TIMING1); + DUMPREG(DSI_TIMING2); + DUMPREG(DSI_VM_TIMING1); + DUMPREG(DSI_VM_TIMING2); + DUMPREG(DSI_VM_TIMING3); + DUMPREG(DSI_CLK_TIMING); + DUMPREG(DSI_TX_FIFO_VC_SIZE); + DUMPREG(DSI_RX_FIFO_VC_SIZE); + DUMPREG(DSI_COMPLEXIO_CFG2); + DUMPREG(DSI_RX_FIFO_VC_FULLNESS); + DUMPREG(DSI_VM_TIMING4); + DUMPREG(DSI_TX_FIFO_VC_EMPTINESS); + DUMPREG(DSI_VM_TIMING5); + DUMPREG(DSI_VM_TIMING6); + DUMPREG(DSI_VM_TIMING7); + DUMPREG(DSI_STOPCLK_TIMING); + + DUMPREG(DSI_VC_CTRL(0)); + DUMPREG(DSI_VC_TE(0)); + DUMPREG(DSI_VC_LONG_PACKET_HEADER(0)); + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0)); + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0)); + DUMPREG(DSI_VC_IRQSTATUS(0)); + DUMPREG(DSI_VC_IRQENABLE(0)); + + DUMPREG(DSI_VC_CTRL(1)); + DUMPREG(DSI_VC_TE(1)); + DUMPREG(DSI_VC_LONG_PACKET_HEADER(1)); + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1)); + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1)); + DUMPREG(DSI_VC_IRQSTATUS(1)); + DUMPREG(DSI_VC_IRQENABLE(1)); + + DUMPREG(DSI_VC_CTRL(2)); + DUMPREG(DSI_VC_TE(2)); + DUMPREG(DSI_VC_LONG_PACKET_HEADER(2)); + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2)); + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2)); + DUMPREG(DSI_VC_IRQSTATUS(2)); + DUMPREG(DSI_VC_IRQENABLE(2)); + + DUMPREG(DSI_VC_CTRL(3)); + DUMPREG(DSI_VC_TE(3)); + DUMPREG(DSI_VC_LONG_PACKET_HEADER(3)); + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3)); + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3)); + DUMPREG(DSI_VC_IRQSTATUS(3)); + DUMPREG(DSI_VC_IRQENABLE(3)); + + DUMPREG(DSI_DSIPHY_CFG0); + DUMPREG(DSI_DSIPHY_CFG1); + DUMPREG(DSI_DSIPHY_CFG2); + DUMPREG(DSI_DSIPHY_CFG5); + + DUMPREG(DSI_PLL_CONTROL); + DUMPREG(DSI_PLL_STATUS); + DUMPREG(DSI_PLL_GO); + DUMPREG(DSI_PLL_CONFIGURATION1); + DUMPREG(DSI_PLL_CONFIGURATION2); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +enum dsi_complexio_power_state { + DSI_COMPLEXIO_POWER_OFF = 0x0, + DSI_COMPLEXIO_POWER_ON = 0x1, + DSI_COMPLEXIO_POWER_ULPS = 0x2, +}; + +static int dsi_complexio_power(enum dsi_complexio_power_state state) +{ + int t = 0; + + /* PWR_CMD */ + REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27); + + /* PWR_STATUS */ + while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) { + udelay(1); + if (t++ > 1000) { + DSSERR("failed to set complexio power state to " + "%d\n", state); + return -ENODEV; + } + } + + return 0; +} + +static void dsi_complexio_config(struct omap_dss_device *dssdev) +{ + u32 r; + + int clk_lane = dssdev->phy.dsi.clk_lane; + int data1_lane = dssdev->phy.dsi.data1_lane; + int data2_lane = dssdev->phy.dsi.data2_lane; + int clk_pol = dssdev->phy.dsi.clk_pol; + int data1_pol = dssdev->phy.dsi.data1_pol; + int data2_pol = dssdev->phy.dsi.data2_pol; + + r = dsi_read_reg(DSI_COMPLEXIO_CFG1); + r = FLD_MOD(r, clk_lane, 2, 0); + r = FLD_MOD(r, clk_pol, 3, 3); + r = FLD_MOD(r, data1_lane, 6, 4); + r = FLD_MOD(r, data1_pol, 7, 7); + r = FLD_MOD(r, data2_lane, 10, 8); + r = FLD_MOD(r, data2_pol, 11, 11); + dsi_write_reg(DSI_COMPLEXIO_CFG1, r); + + /* The configuration of the DSI complex I/O (number of data lanes, + position, differential order) should not be changed while + DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. In order for + the hardware to take into account a new configuration of the complex + I/O (done in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to + follow this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, + then reset the DSS.DSI_CTRL[0] IF_EN to 0, then set + DSS.DSI_CLK_CTRL[20] LP_CLK_ENABLE to 1 and finally set again the + DSS.DSI_CTRL[0] IF_EN bit to 1. If the sequence is not followed, the + DSI complex I/O configuration is unknown. */ + + /* + REG_FLD_MOD(DSI_CTRL, 1, 0, 0); + REG_FLD_MOD(DSI_CTRL, 0, 0, 0); + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); + REG_FLD_MOD(DSI_CTRL, 1, 0, 0); + */ +} + +static inline unsigned ns2ddr(unsigned ns) +{ + /* convert time in ns to ddr ticks, rounding up */ + unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000; +} + +static inline unsigned ddr2ns(unsigned ddr) +{ + unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + return ddr * 1000 * 1000 / (ddr_clk / 1000); +} + +static void dsi_complexio_timings(void) +{ + u32 r; + u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; + u32 tlpx_half, tclk_trail, tclk_zero; + u32 tclk_prepare; + + /* calculate timings */ + + /* 1 * DDR_CLK = 2 * UI */ + + /* min 40ns + 4*UI max 85ns + 6*UI */ + ths_prepare = ns2ddr(70) + 2; + + /* min 145ns + 10*UI */ + ths_prepare_ths_zero = ns2ddr(175) + 2; + + /* min max(8*UI, 60ns+4*UI) */ + ths_trail = ns2ddr(60) + 5; + + /* min 100ns */ + ths_exit = ns2ddr(145); + + /* tlpx min 50n */ + tlpx_half = ns2ddr(25); + + /* min 60ns */ + tclk_trail = ns2ddr(60) + 2; + + /* min 38ns, max 95ns */ + tclk_prepare = ns2ddr(65); + + /* min tclk-prepare + tclk-zero = 300ns */ + tclk_zero = ns2ddr(260); + + DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", + ths_prepare, ddr2ns(ths_prepare), + ths_prepare_ths_zero, ddr2ns(ths_prepare_ths_zero)); + DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", + ths_trail, ddr2ns(ths_trail), + ths_exit, ddr2ns(ths_exit)); + + DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " + "tclk_zero %u (%uns)\n", + tlpx_half, ddr2ns(tlpx_half), + tclk_trail, ddr2ns(tclk_trail), + tclk_zero, ddr2ns(tclk_zero)); + DSSDBG("tclk_prepare %u (%uns)\n", + tclk_prepare, ddr2ns(tclk_prepare)); + + /* program timings */ + + r = dsi_read_reg(DSI_DSIPHY_CFG0); + r = FLD_MOD(r, ths_prepare, 31, 24); + r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); + r = FLD_MOD(r, ths_trail, 15, 8); + r = FLD_MOD(r, ths_exit, 7, 0); + dsi_write_reg(DSI_DSIPHY_CFG0, r); + + r = dsi_read_reg(DSI_DSIPHY_CFG1); + r = FLD_MOD(r, tlpx_half, 22, 16); + r = FLD_MOD(r, tclk_trail, 15, 8); + r = FLD_MOD(r, tclk_zero, 7, 0); + dsi_write_reg(DSI_DSIPHY_CFG1, r); + + r = dsi_read_reg(DSI_DSIPHY_CFG2); + r = FLD_MOD(r, tclk_prepare, 7, 0); + dsi_write_reg(DSI_DSIPHY_CFG2, r); +} + + +static int dsi_complexio_init(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("dsi_complexio_init\n"); + + /* CIO_CLK_ICG, enable L3 clk to CIO */ + REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14); + + /* A dummy read using the SCP interface to any DSIPHY register is + * required after DSIPHY reset to complete the reset of the DSI complex + * I/O. */ + dsi_read_reg(DSI_DSIPHY_CFG5); + + if (wait_for_bit_change(DSI_DSIPHY_CFG5, 30, 1) != 1) { + DSSERR("ComplexIO PHY not coming out of reset.\n"); + r = -ENODEV; + goto err; + } + + dsi_complexio_config(dssdev); + + r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); + + if (r) + goto err; + + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) { + DSSERR("ComplexIO not coming out of reset.\n"); + r = -ENODEV; + goto err; + } + + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) { + DSSERR("ComplexIO LDO power down.\n"); + r = -ENODEV; + goto err; + } + + dsi_complexio_timings(); + + /* + The configuration of the DSI complex I/O (number of data lanes, + position, differential order) should not be changed while + DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the + hardware to recognize a new configuration of the complex I/O (done + in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow + this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next + reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20] + LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN + bit to 1. If the sequence is not followed, the DSi complex I/O + configuration is undetermined. + */ + dsi_if_enable(1); + dsi_if_enable(0); + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ + dsi_if_enable(1); + dsi_if_enable(0); + + DSSDBG("CIO init done\n"); +err: + return r; +} + +static void dsi_complexio_uninit(void) +{ + dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF); +} + +static int _dsi_wait_reset(void) +{ + int i = 0; + + while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) { + if (i++ > 5) { + DSSERR("soft reset failed\n"); + return -ENODEV; + } + udelay(1); + } + + return 0; +} + +static int _dsi_reset(void) +{ + /* Soft reset */ + REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1); + return _dsi_wait_reset(); +} + +static void dsi_reset_tx_fifo(int channel) +{ + u32 mask; + u32 l; + + /* set fifosize of the channel to 0, then return the old size */ + l = dsi_read_reg(DSI_TX_FIFO_VC_SIZE); + + mask = FLD_MASK((8 * channel) + 7, (8 * channel) + 4); + dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l & ~mask); + + dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l); +} + +static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) +{ + u32 r = 0; + int add = 0; + int i; + + dsi.vc[0].fifo_size = size1; + dsi.vc[1].fifo_size = size2; + dsi.vc[2].fifo_size = size3; + dsi.vc[3].fifo_size = size4; + + for (i = 0; i < 4; i++) { + u8 v; + int size = dsi.vc[i].fifo_size; + + if (add + size > 4) { + DSSERR("Illegal FIFO configuration\n"); + BUG(); + } + + v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); + r |= v << (8 * i); + /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */ + add += size; + } + + dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r); +} + +static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) +{ + u32 r = 0; + int add = 0; + int i; + + dsi.vc[0].fifo_size = size1; + dsi.vc[1].fifo_size = size2; + dsi.vc[2].fifo_size = size3; + dsi.vc[3].fifo_size = size4; + + for (i = 0; i < 4; i++) { + u8 v; + int size = dsi.vc[i].fifo_size; + + if (add + size > 4) { + DSSERR("Illegal FIFO configuration\n"); + BUG(); + } + + v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); + r |= v << (8 * i); + /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */ + add += size; + } + + dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r); +} + +static int dsi_force_tx_stop_mode_io(void) +{ + u32 r; + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + dsi_write_reg(DSI_TIMING1, r); + + if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) { + DSSERR("TX_STOP bit not going down\n"); + return -EIO; + } + + return 0; +} + +static void dsi_vc_print_status(int channel) +{ + u32 r; + + r = dsi_read_reg(DSI_VC_CTRL(channel)); + DSSDBG("vc %d: TX_FIFO_NOT_EMPTY %d, BTA_EN %d, VC_BUSY %d, " + "TX_FIFO_FULL %d, RX_FIFO_NOT_EMPTY %d, ", + channel, + FLD_GET(r, 5, 5), + FLD_GET(r, 6, 6), + FLD_GET(r, 15, 15), + FLD_GET(r, 16, 16), + FLD_GET(r, 20, 20)); + + r = dsi_read_reg(DSI_TX_FIFO_VC_EMPTINESS); + DSSDBG("EMPTINESS %d\n", (r >> (8 * channel)) & 0xff); +} + +static int dsi_vc_enable(int channel, bool enable) +{ + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) + DSSDBG("dsi_vc_enable channel %d, enable %d\n", + channel, enable); + + enable = enable ? 1 : 0; + + REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0); + + if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) { + DSSERR("Failed to set dsi_vc_enable to %d\n", enable); + return -EIO; + } + + return 0; +} + +static void dsi_vc_initial_config(int channel) +{ + u32 r; + + DSSDBGF("%d", channel); + + r = dsi_read_reg(DSI_VC_CTRL(channel)); + + if (FLD_GET(r, 15, 15)) /* VC_BUSY */ + DSSERR("VC(%d) busy when trying to configure it!\n", + channel); + + r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */ + r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ + r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */ + r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */ + r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ + r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ + r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ + + r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ + r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ + + dsi_write_reg(DSI_VC_CTRL(channel), r); + + dsi.vc[channel].mode = DSI_VC_MODE_L4; +} + +static void dsi_vc_config_l4(int channel) +{ + if (dsi.vc[channel].mode == DSI_VC_MODE_L4) + return; + + DSSDBGF("%d", channel); + + dsi_vc_enable(channel, 0); + + if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ + DSSERR("vc(%d) busy when trying to config for L4\n", channel); + + REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ + + dsi_vc_enable(channel, 1); + + dsi.vc[channel].mode = DSI_VC_MODE_L4; +} + +static void dsi_vc_config_vp(int channel) +{ + if (dsi.vc[channel].mode == DSI_VC_MODE_VP) + return; + + DSSDBGF("%d", channel); + + dsi_vc_enable(channel, 0); + + if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ + DSSERR("vc(%d) busy when trying to config for VP\n", channel); + + REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */ + + dsi_vc_enable(channel, 1); + + dsi.vc[channel].mode = DSI_VC_MODE_VP; +} + + +static void dsi_vc_enable_hs(int channel, bool enable) +{ + DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); + + dsi_vc_enable(channel, 0); + dsi_if_enable(0); + + REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9); + + dsi_vc_enable(channel, 1); + dsi_if_enable(1); + + dsi_force_tx_stop_mode_io(); +} + +static void dsi_vc_flush_long_data(int channel) +{ + while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + u32 val; + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", + (val >> 0) & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff); + } +} + +static void dsi_show_rx_ack_with_err(u16 err) +{ + DSSERR("\tACK with ERROR (%#x):\n", err); + if (err & (1 << 0)) + DSSERR("\t\tSoT Error\n"); + if (err & (1 << 1)) + DSSERR("\t\tSoT Sync Error\n"); + if (err & (1 << 2)) + DSSERR("\t\tEoT Sync Error\n"); + if (err & (1 << 3)) + DSSERR("\t\tEscape Mode Entry Command Error\n"); + if (err & (1 << 4)) + DSSERR("\t\tLP Transmit Sync Error\n"); + if (err & (1 << 5)) + DSSERR("\t\tHS Receive Timeout Error\n"); + if (err & (1 << 6)) + DSSERR("\t\tFalse Control Error\n"); + if (err & (1 << 7)) + DSSERR("\t\t(reserved7)\n"); + if (err & (1 << 8)) + DSSERR("\t\tECC Error, single-bit (corrected)\n"); + if (err & (1 << 9)) + DSSERR("\t\tECC Error, multi-bit (not corrected)\n"); + if (err & (1 << 10)) + DSSERR("\t\tChecksum Error\n"); + if (err & (1 << 11)) + DSSERR("\t\tData type not recognized\n"); + if (err & (1 << 12)) + DSSERR("\t\tInvalid VC ID\n"); + if (err & (1 << 13)) + DSSERR("\t\tInvalid Transmission Length\n"); + if (err & (1 << 14)) + DSSERR("\t\t(reserved14)\n"); + if (err & (1 << 15)) + DSSERR("\t\tDSI Protocol Violation\n"); +} + +static u16 dsi_vc_flush_receive_data(int channel) +{ + /* RX_FIFO_NOT_EMPTY */ + while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + u32 val; + u8 dt; + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + DSSDBG("\trawval %#08x\n", val); + dt = FLD_GET(val, 5, 0); + if (dt == DSI_DT_RX_ACK_WITH_ERR) { + u16 err = FLD_GET(val, 23, 8); + dsi_show_rx_ack_with_err(err); + } else if (dt == DSI_DT_RX_SHORT_READ_1) { + DSSDBG("\tDCS short response, 1 byte: %#x\n", + FLD_GET(val, 23, 8)); + } else if (dt == DSI_DT_RX_SHORT_READ_2) { + DSSDBG("\tDCS short response, 2 byte: %#x\n", + FLD_GET(val, 23, 8)); + } else if (dt == DSI_DT_RX_DCS_LONG_READ) { + DSSDBG("\tDCS long response, len %d\n", + FLD_GET(val, 23, 8)); + dsi_vc_flush_long_data(channel); + } else { + DSSERR("\tunknown datatype 0x%02x\n", dt); + } + } + return 0; +} + +static int dsi_vc_send_bta(int channel) +{ + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO && + (dsi.debug_write || dsi.debug_read)) + DSSDBG("dsi_vc_send_bta %d\n", channel); + + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); + + if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ + DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); + dsi_vc_flush_receive_data(channel); + } + + REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ + + return 0; +} + +int dsi_vc_send_bta_sync(int channel) +{ + int r = 0; + u32 err; + + INIT_COMPLETION(dsi.bta_completion); + + dsi_vc_enable_bta_irq(channel); + + r = dsi_vc_send_bta(channel); + if (r) + goto err; + + if (wait_for_completion_timeout(&dsi.bta_completion, + msecs_to_jiffies(500)) == 0) { + DSSERR("Failed to receive BTA\n"); + r = -EIO; + goto err; + } + + err = dsi_get_errors(); + if (err) { + DSSERR("Error while sending BTA: %x\n", err); + r = -EIO; + goto err; + } +err: + dsi_vc_disable_bta_irq(channel); + + return r; +} +EXPORT_SYMBOL(dsi_vc_send_bta_sync); + +static inline void dsi_vc_write_long_header(int channel, u8 data_type, + u16 len, u8 ecc) +{ + u32 val; + u8 data_id; + + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); + + /*data_id = data_type | channel << 6; */ + data_id = data_type | dsi.vc[channel].dest_per << 6; + + val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | + FLD_VAL(ecc, 31, 24); + + dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val); +} + +static inline void dsi_vc_write_long_payload(int channel, + u8 b1, u8 b2, u8 b3, u8 b4) +{ + u32 val; + + val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0; + +/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", + b1, b2, b3, b4, val); */ + + dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val); +} + +static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, + u8 ecc) +{ + /*u32 val; */ + int i; + u8 *p; + int r = 0; + u8 b1, b2, b3, b4; + + if (dsi.debug_write) + DSSDBG("dsi_vc_send_long, %d bytes\n", len); + + /* len + header */ + if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) { + DSSERR("unable to send long packet: packet too long.\n"); + return -EINVAL; + } + + dsi_vc_config_l4(channel); + + dsi_vc_write_long_header(channel, data_type, len, ecc); + + /*dsi_vc_print_status(0); */ + + p = data; + for (i = 0; i < len >> 2; i++) { + if (dsi.debug_write) + DSSDBG("\tsending full packet %d\n", i); + /*dsi_vc_print_status(0); */ + + b1 = *p++; + b2 = *p++; + b3 = *p++; + b4 = *p++; + + dsi_vc_write_long_payload(channel, b1, b2, b3, b4); + } + + i = len % 4; + if (i) { + b1 = 0; b2 = 0; b3 = 0; + + if (dsi.debug_write) + DSSDBG("\tsending remainder bytes %d\n", i); + + switch (i) { + case 3: + b1 = *p++; + b2 = *p++; + b3 = *p++; + break; + case 2: + b1 = *p++; + b2 = *p++; + break; + case 1: + b1 = *p++; + break; + } + + dsi_vc_write_long_payload(channel, b1, b2, b3, 0); + } + + return r; +} + +static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc) +{ + u32 r; + u8 data_id; + + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); + + if (dsi.debug_write) + DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", + channel, + data_type, data & 0xff, (data >> 8) & 0xff); + + dsi_vc_config_l4(channel); + + if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) { + DSSERR("ERROR FIFO FULL, aborting transfer\n"); + return -EINVAL; + } + + data_id = data_type | channel << 6; + + r = (data_id << 0) | (data << 8) | (ecc << 24); + + dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r); + + return 0; +} + +int dsi_vc_send_null(int channel) +{ + u8 nullpkg[] = {0, 0, 0, 0}; + return dsi_vc_send_long(0, DSI_DT_NULL_PACKET, nullpkg, 4, 0); +} +EXPORT_SYMBOL(dsi_vc_send_null); + +int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) +{ + int r; + + BUG_ON(len == 0); + + if (len == 1) { + r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0, + data[0], 0); + } else if (len == 2) { + r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1, + data[0] | (data[1] << 8), 0); + } else { + /* 0x39 = DCS Long Write */ + r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE, + data, len, 0); + } + + return r; +} +EXPORT_SYMBOL(dsi_vc_dcs_write_nosync); + +int dsi_vc_dcs_write(int channel, u8 *data, int len) +{ + int r; + + r = dsi_vc_dcs_write_nosync(channel, data, len); + if (r) + return r; + + r = dsi_vc_send_bta_sync(channel); + + return r; +} +EXPORT_SYMBOL(dsi_vc_dcs_write); + +int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) +{ + u32 val; + u8 dt; + int r; + + if (dsi.debug_read) + DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %u)\n", channel, dcs_cmd); + + r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0); + if (r) + return r; + + r = dsi_vc_send_bta_sync(channel); + if (r) + return r; + + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) { + DSSERR("RX fifo empty when trying to read.\n"); + return -EIO; + } + + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + if (dsi.debug_read) + DSSDBG("\theader: %08x\n", val); + dt = FLD_GET(val, 5, 0); + if (dt == DSI_DT_RX_ACK_WITH_ERR) { + u16 err = FLD_GET(val, 23, 8); + dsi_show_rx_ack_with_err(err); + return -EIO; + + } else if (dt == DSI_DT_RX_SHORT_READ_1) { + u8 data = FLD_GET(val, 15, 8); + if (dsi.debug_read) + DSSDBG("\tDCS short response, 1 byte: %02x\n", data); + + if (buflen < 1) + return -EIO; + + buf[0] = data; + + return 1; + } else if (dt == DSI_DT_RX_SHORT_READ_2) { + u16 data = FLD_GET(val, 23, 8); + if (dsi.debug_read) + DSSDBG("\tDCS short response, 2 byte: %04x\n", data); + + if (buflen < 2) + return -EIO; + + buf[0] = data & 0xff; + buf[1] = (data >> 8) & 0xff; + + return 2; + } else if (dt == DSI_DT_RX_DCS_LONG_READ) { + int w; + int len = FLD_GET(val, 23, 8); + if (dsi.debug_read) + DSSDBG("\tDCS long response, len %d\n", len); + + if (len > buflen) + return -EIO; + + /* two byte checksum ends the packet, not included in len */ + for (w = 0; w < len + 2;) { + int b; + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + if (dsi.debug_read) + DSSDBG("\t\t%02x %02x %02x %02x\n", + (val >> 0) & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff); + + for (b = 0; b < 4; ++b) { + if (w < len) + buf[w] = (val >> (b * 8)) & 0xff; + /* we discard the 2 byte checksum */ + ++w; + } + } + + return len; + + } else { + DSSERR("\tunknown datatype 0x%02x\n", dt); + return -EIO; + } +} +EXPORT_SYMBOL(dsi_vc_dcs_read); + + +int dsi_vc_set_max_rx_packet_size(int channel, u16 len) +{ + int r; + r = dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, + len, 0); + + if (r) + return r; + + r = dsi_vc_send_bta_sync(channel); + + return r; +} +EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); + +static void dsi_set_lp_rx_timeout(unsigned long ns) +{ + u32 r; + unsigned x4, x16; + unsigned long fck; + unsigned long ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + x4 = 0; + x16 = 0; + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 4; + x4 = 1; + x16 = 0; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 16; + x4 = 0; + x16 = 1; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); + x4 = 1; + x16 = 1; + } + + if (ticks > 0x1fff) { + DSSWARN("LP_TX_TO over limit, setting it to max\n"); + ticks = 0x1fff; + x4 = 1; + x16 = 1; + } + + r = dsi_read_reg(DSI_TIMING2); + r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ + r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */ + r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */ + r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ + dsi_write_reg(DSI_TIMING2, r); + + DSSDBG("LP_RX_TO %lu ns (%#lx ticks%s%s)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks, x4 ? " x4" : "", x16 ? " x16" : ""); +} + +static void dsi_set_ta_timeout(unsigned long ns) +{ + u32 r; + unsigned x8, x16; + unsigned long fck; + unsigned long ticks; + + /* ticks in DSI_FCK */ + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + x8 = 0; + x16 = 0; + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 8; + x8 = 1; + x16 = 0; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 16; + x8 = 0; + x16 = 1; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / (8 * 16); + x8 = 1; + x16 = 1; + } + + if (ticks > 0x1fff) { + DSSWARN("TA_TO over limit, setting it to max\n"); + ticks = 0x1fff; + x8 = 1; + x16 = 1; + } + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ + r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */ + r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */ + r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ + dsi_write_reg(DSI_TIMING1, r); + + DSSDBG("TA_TO %lu ns (%#lx ticks%s%s)\n", + (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) / + (fck / 1000 / 1000), + ticks, x8 ? " x8" : "", x16 ? " x16" : ""); +} + +static void dsi_set_stop_state_counter(unsigned long ns) +{ + u32 r; + unsigned x4, x16; + unsigned long fck; + unsigned long ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + x4 = 0; + x16 = 0; + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 4; + x4 = 1; + x16 = 0; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 16; + x4 = 0; + x16 = 1; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); + x4 = 1; + x16 = 1; + } + + if (ticks > 0x1fff) { + DSSWARN("STOP_STATE_COUNTER_IO over limit, " + "setting it to max\n"); + ticks = 0x1fff; + x4 = 1; + x16 = 1; + } + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */ + r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */ + r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ + dsi_write_reg(DSI_TIMING1, r); + + DSSDBG("STOP_STATE_COUNTER %lu ns (%#lx ticks%s%s)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks, x4 ? " x4" : "", x16 ? " x16" : ""); +} + +static void dsi_set_hs_tx_timeout(unsigned long ns) +{ + u32 r; + unsigned x4, x16; + unsigned long fck; + unsigned long ticks; + + /* ticks in TxByteClkHS */ + + fck = dsi_get_txbyteclkhs(); + ticks = (fck / 1000 / 1000) * ns / 1000; + x4 = 0; + x16 = 0; + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 4; + x4 = 1; + x16 = 0; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 16; + x4 = 0; + x16 = 1; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); + x4 = 1; + x16 = 1; + } + + if (ticks > 0x1fff) { + DSSWARN("HS_TX_TO over limit, setting it to max\n"); + ticks = 0x1fff; + x4 = 1; + x16 = 1; + } + + r = dsi_read_reg(DSI_TIMING2); + r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ + r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */ + r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */ + r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ + dsi_write_reg(DSI_TIMING2, r); + + DSSDBG("HS_TX_TO %lu ns (%#lx ticks%s%s)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks, x4 ? " x4" : "", x16 ? " x16" : ""); +} +static int dsi_proto_config(struct omap_dss_device *dssdev) +{ + u32 r; + int buswidth = 0; + + dsi_config_tx_fifo(DSI_FIFO_SIZE_128, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0); + + dsi_config_rx_fifo(DSI_FIFO_SIZE_128, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0); + + /* XXX what values for the timeouts? */ + dsi_set_stop_state_counter(1000); + dsi_set_ta_timeout(6400000); + dsi_set_lp_rx_timeout(48000); + dsi_set_hs_tx_timeout(1000000); + + switch (dssdev->ctrl.pixel_size) { + case 16: + buswidth = 0; + break; + case 18: + buswidth = 1; + break; + case 24: + buswidth = 2; + break; + default: + BUG(); + } + + r = dsi_read_reg(DSI_CTRL); + r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ + r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ + r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ + r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/ + r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */ + r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */ + r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */ + r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ + r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ + r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ + r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */ + + dsi_write_reg(DSI_CTRL, r); + + dsi_vc_initial_config(0); + + /* set all vc targets to peripheral 0 */ + dsi.vc[0].dest_per = 0; + dsi.vc[1].dest_per = 0; + dsi.vc[2].dest_per = 0; + dsi.vc[3].dest_per = 0; + + return 0; +} + +static void dsi_proto_timings(struct omap_dss_device *dssdev) +{ + unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail; + unsigned tclk_pre, tclk_post; + unsigned ths_prepare, ths_prepare_ths_zero, ths_zero; + unsigned ths_trail, ths_exit; + unsigned ddr_clk_pre, ddr_clk_post; + unsigned enter_hs_mode_lat, exit_hs_mode_lat; + unsigned ths_eot; + u32 r; + + r = dsi_read_reg(DSI_DSIPHY_CFG0); + ths_prepare = FLD_GET(r, 31, 24); + ths_prepare_ths_zero = FLD_GET(r, 23, 16); + ths_zero = ths_prepare_ths_zero - ths_prepare; + ths_trail = FLD_GET(r, 15, 8); + ths_exit = FLD_GET(r, 7, 0); + + r = dsi_read_reg(DSI_DSIPHY_CFG1); + tlpx = FLD_GET(r, 22, 16) * 2; + tclk_trail = FLD_GET(r, 15, 8); + tclk_zero = FLD_GET(r, 7, 0); + + r = dsi_read_reg(DSI_DSIPHY_CFG2); + tclk_prepare = FLD_GET(r, 7, 0); + + /* min 8*UI */ + tclk_pre = 20; + /* min 60ns + 52*UI */ + tclk_post = ns2ddr(60) + 26; + + /* ths_eot is 2 for 2 datalanes and 4 for 1 datalane */ + if (dssdev->phy.dsi.data1_lane != 0 && + dssdev->phy.dsi.data2_lane != 0) + ths_eot = 2; + else + ths_eot = 4; + + ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare, + 4); + ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot; + + BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255); + BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255); + + r = dsi_read_reg(DSI_CLK_TIMING); + r = FLD_MOD(r, ddr_clk_pre, 15, 8); + r = FLD_MOD(r, ddr_clk_post, 7, 0); + dsi_write_reg(DSI_CLK_TIMING, r); + + DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", + ddr_clk_pre, + ddr_clk_post); + + enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) + + DIV_ROUND_UP(ths_prepare, 4) + + DIV_ROUND_UP(ths_zero + 3, 4); + + exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot; + + r = FLD_VAL(enter_hs_mode_lat, 31, 16) | + FLD_VAL(exit_hs_mode_lat, 15, 0); + dsi_write_reg(DSI_VM_TIMING7, r); + + DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", + enter_hs_mode_lat, exit_hs_mode_lat); +} + + +#define DSI_DECL_VARS \ + int __dsi_cb = 0; u32 __dsi_cv = 0; + +#define DSI_FLUSH(ch) \ + if (__dsi_cb > 0) { \ + /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \ + dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ + __dsi_cb = __dsi_cv = 0; \ + } + +#define DSI_PUSH(ch, data) \ + do { \ + __dsi_cv |= (data) << (__dsi_cb * 8); \ + /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \ + if (++__dsi_cb > 3) \ + DSI_FLUSH(ch); \ + } while (0) + +static int dsi_update_screen_l4(struct omap_dss_device *dssdev, + int x, int y, int w, int h) +{ + /* Note: supports only 24bit colors in 32bit container */ + int first = 1; + int fifo_stalls = 0; + int max_dsi_packet_size; + int max_data_per_packet; + int max_pixels_per_packet; + int pixels_left; + int bytespp = dssdev->ctrl.pixel_size / 8; + int scr_width; + u32 __iomem *data; + int start_offset; + int horiz_inc; + int current_x; + struct omap_overlay *ovl; + + debug_irq = 0; + + DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n", + x, y, w, h); + + ovl = dssdev->manager->overlays[0]; + + if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U) + return -EINVAL; + + if (dssdev->ctrl.pixel_size != 24) + return -EINVAL; + + scr_width = ovl->info.screen_width; + data = ovl->info.vaddr; + + start_offset = scr_width * y + x; + horiz_inc = scr_width - w; + current_x = x; + + /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes + * in fifo */ + + /* When using CPU, max long packet size is TX buffer size */ + max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4; + + /* we seem to get better perf if we divide the tx fifo to half, + and while the other half is being sent, we fill the other half + max_dsi_packet_size /= 2; */ + + max_data_per_packet = max_dsi_packet_size - 4 - 1; + + max_pixels_per_packet = max_data_per_packet / bytespp; + + DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet); + + pixels_left = w * h; + + DSSDBG("total pixels %d\n", pixels_left); + + data += start_offset; + + while (pixels_left > 0) { + /* 0x2c = write_memory_start */ + /* 0x3c = write_memory_continue */ + u8 dcs_cmd = first ? 0x2c : 0x3c; + int pixels; + DSI_DECL_VARS; + first = 0; + +#if 1 + /* using fifo not empty */ + /* TX_FIFO_NOT_EMPTY */ + while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) { + udelay(1); + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + return -EIO; + } + } +#elif 1 + /* using fifo emptiness */ + while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < + max_dsi_packet_size) { + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + return -EIO; + } + } +#else + while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) { + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + return -EIO; + } + } +#endif + pixels = min(max_pixels_per_packet, pixels_left); + + pixels_left -= pixels; + + dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE, + 1 + pixels * bytespp, 0); + + DSI_PUSH(0, dcs_cmd); + + while (pixels-- > 0) { + u32 pix = __raw_readl(data++); + + DSI_PUSH(0, (pix >> 16) & 0xff); + DSI_PUSH(0, (pix >> 8) & 0xff); + DSI_PUSH(0, (pix >> 0) & 0xff); + + current_x++; + if (current_x == x+w) { + current_x = x; + data += horiz_inc; + } + } + + DSI_FLUSH(0); + } + + return 0; +} + +static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + unsigned bytespp; + unsigned bytespl; + unsigned bytespf; + unsigned total_len; + unsigned packet_payload; + unsigned packet_len; + u32 l; + bool use_te_trigger; + const unsigned channel = 0; + /* line buffer is 1024 x 24bits */ + /* XXX: for some reason using full buffer size causes considerable TX + * slowdown with update sizes that fill the whole buffer */ + const unsigned line_buf_size = 1023 * 3; + + use_te_trigger = dsi.te_enabled && !dsi.use_ext_te; + + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) + DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n", + x, y, w, h); + + bytespp = dssdev->ctrl.pixel_size / 8; + bytespl = w * bytespp; + bytespf = bytespl * h; + + /* NOTE: packet_payload has to be equal to N * bytespl, where N is + * number of lines in a packet. See errata about VP_CLK_RATIO */ + + if (bytespf < line_buf_size) + packet_payload = bytespf; + else + packet_payload = (line_buf_size) / bytespl * bytespl; + + packet_len = packet_payload + 1; /* 1 byte for DCS cmd */ + total_len = (bytespf / packet_payload) * packet_len; + + if (bytespf % packet_payload) + total_len += (bytespf % packet_payload) + 1; + + if (0) + dsi_vc_print_status(1); + + l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ + dsi_write_reg(DSI_VC_TE(channel), l); + + dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0); + + if (use_te_trigger) + l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ + else + l = FLD_MOD(l, 1, 31, 31); /* TE_START */ + dsi_write_reg(DSI_VC_TE(channel), l); + + /* We put SIDLEMODE to no-idle for the duration of the transfer, + * because DSS interrupts are not capable of waking up the CPU and the + * framedone interrupt could be delayed for quite a long time. I think + * the same goes for any DSS interrupts, but for some reason I have not + * seen the problem anywhere else than here. + */ + dispc_disable_sidle(); + + dss_start_update(dssdev); + + if (use_te_trigger) { + /* disable LP_RX_TO, so that we can receive TE. Time to wait + * for TE is longer than the timer allows */ + REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ + + dsi_vc_send_bta(channel); + +#ifdef DSI_CATCH_MISSING_TE + mod_timer(&dsi.te_timer, jiffies + msecs_to_jiffies(250)); +#endif + } +} + +#ifdef DSI_CATCH_MISSING_TE +static void dsi_te_timeout(unsigned long arg) +{ + DSSERR("TE not received for 250ms!\n"); +} +#endif + +static void dsi_framedone_irq_callback(void *data, u32 mask) +{ + /* Note: We get FRAMEDONE when DISPC has finished sending pixels and + * turns itself off. However, DSI still has the pixels in its buffers, + * and is sending the data. + */ + + /* SIDLEMODE back to smart-idle */ + dispc_enable_sidle(); + + dsi.framedone_received = true; + wake_up(&dsi.waitqueue); +} + +static void dsi_set_update_region(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + spin_lock(&dsi.update_lock); + if (dsi.update_region.dirty) { + dsi.update_region.x = min(x, dsi.update_region.x); + dsi.update_region.y = min(y, dsi.update_region.y); + dsi.update_region.w = max(w, dsi.update_region.w); + dsi.update_region.h = max(h, dsi.update_region.h); + } else { + dsi.update_region.x = x; + dsi.update_region.y = y; + dsi.update_region.w = w; + dsi.update_region.h = h; + } + + dsi.update_region.device = dssdev; + dsi.update_region.dirty = true; + + spin_unlock(&dsi.update_lock); + +} + +static int dsi_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + int r = 0; + int i; + + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); + + if (dsi.update_mode != mode) { + dsi.update_mode = mode; + + /* Mark the overlays dirty, and do apply(), so that we get the + * overlays configured properly after update mode change. */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + if (ovl->manager == dssdev->manager) + ovl->info_dirty = true; + } + + r = dssdev->manager->apply(dssdev->manager); + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE && + mode == OMAP_DSS_UPDATE_AUTO) { + u16 w, h; + + DSSDBG("starting auto update\n"); + + dssdev->get_resolution(dssdev, &w, &h); + + dsi_set_update_region(dssdev, 0, 0, w, h); + + dsi_perf_mark_start_auto(); + + wake_up(&dsi.waitqueue); + } + } + + return r; +} + +static int dsi_set_te(struct omap_dss_device *dssdev, bool enable) +{ + int r; + r = dssdev->driver->enable_te(dssdev, enable); + /* XXX for some reason, DSI TE breaks if we don't wait here. + * Panel bug? Needs more studying */ + msleep(100); + return r; +} + +static void dsi_handle_framedone(void) +{ + int r; + const int channel = 0; + bool use_te_trigger; + + use_te_trigger = dsi.te_enabled && !dsi.use_ext_te; + + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) + DSSDBG("FRAMEDONE\n"); + + if (use_te_trigger) { + /* enable LP_RX_TO again after the TE */ + REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ + } + + /* Send BTA after the frame. We need this for the TE to work, as TE + * trigger is only sent for BTAs without preceding packet. Thus we need + * to BTA after the pixel packets so that next BTA will cause TE + * trigger. + * + * This is not needed when TE is not in use, but we do it anyway to + * make sure that the transfer has been completed. It would be more + * optimal, but more complex, to wait only just before starting next + * transfer. */ + r = dsi_vc_send_bta_sync(channel); + if (r) + DSSERR("BTA after framedone failed\n"); + + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + DSSERR("Received error during frame transfer:\n"); + dsi_vc_flush_receive_data(0); + } + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC + dispc_fake_vsync_irq(); +#endif +} + +static int dsi_update_thread(void *data) +{ + unsigned long timeout; + struct omap_dss_device *device; + u16 x, y, w, h; + + while (1) { + bool sched; + + wait_event_interruptible(dsi.waitqueue, + dsi.update_mode == OMAP_DSS_UPDATE_AUTO || + (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL && + dsi.update_region.dirty == true) || + kthread_should_stop()); + + if (kthread_should_stop()) + break; + + dsi_bus_lock(); + + if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED || + kthread_should_stop()) { + dsi_bus_unlock(); + break; + } + + dsi_perf_mark_setup(); + + if (dsi.update_region.dirty) { + spin_lock(&dsi.update_lock); + dsi.active_update_region = dsi.update_region; + dsi.update_region.dirty = false; + spin_unlock(&dsi.update_lock); + } + + device = dsi.active_update_region.device; + x = dsi.active_update_region.x; + y = dsi.active_update_region.y; + w = dsi.active_update_region.w; + h = dsi.active_update_region.h; + + if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + + if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL) + dss_setup_partial_planes(device, + &x, &y, &w, &h); + + dispc_set_lcd_size(w, h); + } + + if (dsi.active_update_region.dirty) { + dsi.active_update_region.dirty = false; + /* XXX TODO we don't need to send the coords, if they + * are the same that are already programmed to the + * panel. That should speed up manual update a bit */ + device->driver->setup_update(device, x, y, w, h); + } + + dsi_perf_mark_start(); + + if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + dsi_vc_config_vp(0); + + if (dsi.te_enabled && dsi.use_ext_te) + device->driver->wait_for_te(device); + + dsi.framedone_received = false; + + dsi_update_screen_dispc(device, x, y, w, h); + + /* wait for framedone */ + timeout = msecs_to_jiffies(1000); + wait_event_timeout(dsi.waitqueue, + dsi.framedone_received == true, + timeout); + + if (!dsi.framedone_received) { + DSSERR("framedone timeout\n"); + DSSERR("failed update %d,%d %dx%d\n", + x, y, w, h); + + dispc_enable_sidle(); + dispc_enable_lcd_out(0); + + dsi_reset_tx_fifo(0); + } else { + dsi_handle_framedone(); + dsi_perf_show("DISPC"); + } + } else { + dsi_update_screen_l4(device, x, y, w, h); + dsi_perf_show("L4"); + } + + sched = atomic_read(&dsi.bus_lock.count) < 0; + + complete_all(&dsi.update_completion); + + dsi_bus_unlock(); + + /* XXX We need to give others chance to get the bus lock. Is + * there a better way for this? */ + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO && sched) + schedule_timeout_interruptible(1); + } + + DSSDBG("update thread exiting\n"); + + return 0; +} + + + +/* Display funcs */ + +static int dsi_display_init_dispc(struct omap_dss_device *dssdev) +{ + int r; + + r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL, + DISPC_IRQ_FRAMEDONE); + if (r) { + DSSERR("can't get FRAMEDONE irq\n"); + return r; + } + + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_DSI); + dispc_enable_fifohandcheck(1); + + dispc_set_tft_data_lines(dssdev->ctrl.pixel_size); + + { + struct omap_video_timings timings = { + .hsw = 1, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + }; + + dispc_set_lcd_timings(&timings); + } + + return 0; +} + +static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev) +{ + omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL, + DISPC_IRQ_FRAMEDONE); +} + +static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) +{ + struct dsi_clock_info cinfo; + int r; + + /* we always use DSS2_FCK as input clock */ + cinfo.use_dss2_fck = true; + cinfo.regn = dssdev->phy.dsi.div.regn; + cinfo.regm = dssdev->phy.dsi.div.regm; + cinfo.regm3 = dssdev->phy.dsi.div.regm3; + cinfo.regm4 = dssdev->phy.dsi.div.regm4; + r = dsi_calc_clock_rates(&cinfo); + if (r) + return r; + + r = dsi_pll_set_clock_div(&cinfo); + if (r) { + DSSERR("Failed to set dsi clocks\n"); + return r; + } + + return 0; +} + +static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) +{ + struct dispc_clock_info dispc_cinfo; + int r; + unsigned long long fck; + + fck = dsi_get_dsi1_pll_rate(); + + dispc_cinfo.lck_div = dssdev->phy.dsi.div.lck_div; + dispc_cinfo.pck_div = dssdev->phy.dsi.div.pck_div; + + r = dispc_calc_clock_rates(fck, &dispc_cinfo); + if (r) { + DSSERR("Failed to calc dispc clocks\n"); + return r; + } + + r = dispc_set_clock_div(&dispc_cinfo); + if (r) { + DSSERR("Failed to set dispc clocks\n"); + return r; + } + + return 0; +} + +static int dsi_display_init_dsi(struct omap_dss_device *dssdev) +{ + int r; + + _dsi_print_reset_status(); + + r = dsi_pll_init(dssdev, true, true); + if (r) + goto err0; + + r = dsi_configure_dsi_clocks(dssdev); + if (r) + goto err1; + + dss_select_clk_source(true, true); + + DSSDBG("PLL OK\n"); + + r = dsi_configure_dispc_clocks(dssdev); + if (r) + goto err2; + + r = dsi_complexio_init(dssdev); + if (r) + goto err2; + + _dsi_print_reset_status(); + + dsi_proto_timings(dssdev); + dsi_set_lp_clk_divisor(dssdev); + + if (1) + _dsi_print_reset_status(); + + r = dsi_proto_config(dssdev); + if (r) + goto err3; + + /* enable interface */ + dsi_vc_enable(0, 1); + dsi_if_enable(1); + dsi_force_tx_stop_mode_io(); + + if (dssdev->driver->enable) { + r = dssdev->driver->enable(dssdev); + if (r) + goto err4; + } + + /* enable high-speed after initial config */ + dsi_vc_enable_hs(0, 1); + + return 0; +err4: + dsi_if_enable(0); +err3: + dsi_complexio_uninit(); +err2: + dss_select_clk_source(false, false); +err1: + dsi_pll_uninit(); +err0: + return r; +} + +static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) +{ + if (dssdev->driver->disable) + dssdev->driver->disable(dssdev); + + dss_select_clk_source(false, false); + dsi_complexio_uninit(); + dsi_pll_uninit(); +} + +static int dsi_core_init(void) +{ + /* Autoidle */ + REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0); + + /* ENWAKEUP */ + REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2); + + /* SIDLEMODE smart-idle */ + REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3); + + _dsi_initialize_irq(); + + return 0; +} + +static int dsi_display_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("dsi_display_enable\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + DSSERR("dssdev already enabled\n"); + r = -EINVAL; + goto err1; + } + + enable_clocks(1); + dsi_enable_pll_clock(1); + + r = _dsi_reset(); + if (r) + goto err2; + + dsi_core_init(); + + r = dsi_display_init_dispc(dssdev); + if (r) + goto err2; + + r = dsi_display_init_dsi(dssdev); + if (r) + goto err3; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + dsi.use_ext_te = dssdev->phy.dsi.ext_te; + r = dsi_set_te(dssdev, dsi.te_enabled); + if (r) + goto err4; + + dsi_set_update_mode(dssdev, dsi.user_update_mode); + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return 0; + +err4: + + dsi_display_uninit_dsi(dssdev); +err3: + dsi_display_uninit_dispc(dssdev); +err2: + enable_clocks(0); + dsi_enable_pll_clock(0); +err1: + omap_dss_stop_device(dssdev); +err0: + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + DSSDBG("dsi_display_enable FAILED\n"); + return r; +} + +static void dsi_display_disable(struct omap_dss_device *dssdev) +{ + DSSDBG("dsi_display_disable\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || + dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + goto end; + + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + dsi_display_uninit_dispc(dssdev); + + dsi_display_uninit_dsi(dssdev); + + enable_clocks(0); + dsi_enable_pll_clock(0); + + omap_dss_stop_device(dssdev); +end: + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); +} + +static int dsi_display_suspend(struct omap_dss_device *dssdev) +{ + DSSDBG("dsi_display_suspend\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || + dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + goto end; + + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + dsi_display_uninit_dispc(dssdev); + + dsi_display_uninit_dsi(dssdev); + + enable_clocks(0); + dsi_enable_pll_clock(0); +end: + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return 0; +} + +static int dsi_display_resume(struct omap_dss_device *dssdev) +{ + int r; + + DSSDBG("dsi_display_resume\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + DSSERR("dssdev not suspended\n"); + r = -EINVAL; + goto err0; + } + + enable_clocks(1); + dsi_enable_pll_clock(1); + + r = _dsi_reset(); + if (r) + goto err1; + + dsi_core_init(); + + r = dsi_display_init_dispc(dssdev); + if (r) + goto err1; + + r = dsi_display_init_dsi(dssdev); + if (r) + goto err2; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + r = dsi_set_te(dssdev, dsi.te_enabled); + if (r) + goto err2; + + dsi_set_update_mode(dssdev, dsi.user_update_mode); + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return 0; + +err2: + dsi_display_uninit_dispc(dssdev); +err1: + enable_clocks(0); + dsi_enable_pll_clock(0); +err0: + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + DSSDBG("dsi_display_resume FAILED\n"); + return r; +} + +static int dsi_display_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + int r = 0; + u16 dw, dh; + + DSSDBG("dsi_display_update(%d,%d %dx%d)\n", x, y, w, h); + + mutex_lock(&dsi.lock); + + if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL) + goto end; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + goto end; + + dssdev->get_resolution(dssdev, &dw, &dh); + + if (x > dw || y > dh) + goto end; + + if (x + w > dw) + w = dw - x; + + if (y + h > dh) + h = dh - y; + + if (w == 0 || h == 0) + goto end; + + if (w == 1) { + r = -EINVAL; + goto end; + } + + dsi_set_update_region(dssdev, x, y, w, h); + + wake_up(&dsi.waitqueue); + +end: + mutex_unlock(&dsi.lock); + + return r; +} + +static int dsi_display_sync(struct omap_dss_device *dssdev) +{ + bool wait; + + DSSDBG("dsi_display_sync()\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL && + dsi.update_region.dirty) { + INIT_COMPLETION(dsi.update_completion); + wait = true; + } else { + wait = false; + } + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + if (wait) + wait_for_completion_interruptible(&dsi.update_completion); + + DSSDBG("dsi_display_sync() done\n"); + return 0; +} + +static int dsi_display_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + int r = 0; + + DSSDBGF("%d", mode); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + dsi.user_update_mode = mode; + r = dsi_set_update_mode(dssdev, mode); + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return r; +} + +static enum omap_dss_update_mode dsi_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + return dsi.update_mode; +} + + +static int dsi_display_enable_te(struct omap_dss_device *dssdev, bool enable) +{ + int r = 0; + + DSSDBGF("%d", enable); + + if (!dssdev->driver->enable_te) + return -ENOENT; + + dsi_bus_lock(); + + dsi.te_enabled = enable; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + goto end; + + r = dsi_set_te(dssdev, enable); +end: + dsi_bus_unlock(); + + return r; +} + +static int dsi_display_get_te(struct omap_dss_device *dssdev) +{ + return dsi.te_enabled; +} + +static int dsi_display_set_rotate(struct omap_dss_device *dssdev, u8 rotate) +{ + + DSSDBGF("%d", rotate); + + if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) + return -EINVAL; + + dsi_bus_lock(); + dssdev->driver->set_rotate(dssdev, rotate); + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { + u16 w, h; + /* the display dimensions may have changed, so set a new + * update region */ + dssdev->get_resolution(dssdev, &w, &h); + dsi_set_update_region(dssdev, 0, 0, w, h); + } + dsi_bus_unlock(); + + return 0; +} + +static u8 dsi_display_get_rotate(struct omap_dss_device *dssdev) +{ + if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) + return 0; + + return dssdev->driver->get_rotate(dssdev); +} + +static int dsi_display_set_mirror(struct omap_dss_device *dssdev, bool mirror) +{ + DSSDBGF("%d", mirror); + + if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) + return -EINVAL; + + dsi_bus_lock(); + dssdev->driver->set_mirror(dssdev, mirror); + dsi_bus_unlock(); + + return 0; +} + +static bool dsi_display_get_mirror(struct omap_dss_device *dssdev) +{ + if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) + return 0; + + return dssdev->driver->get_mirror(dssdev); +} + +static int dsi_display_run_test(struct omap_dss_device *dssdev, int test_num) +{ + int r; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EIO; + + DSSDBGF("%d", test_num); + + dsi_bus_lock(); + + /* run test first in low speed mode */ + dsi_vc_enable_hs(0, 0); + + if (dssdev->driver->run_test) { + r = dssdev->driver->run_test(dssdev, test_num); + if (r) + goto end; + } + + /* then in high speed */ + dsi_vc_enable_hs(0, 1); + + if (dssdev->driver->run_test) { + r = dssdev->driver->run_test(dssdev, test_num); + if (r) + goto end; + } + +end: + dsi_vc_enable_hs(0, 1); + + dsi_bus_unlock(); + + return r; +} + +static int dsi_display_memory_read(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h) +{ + int r; + + DSSDBGF(""); + + if (!dssdev->driver->memory_read) + return -EINVAL; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EIO; + + dsi_bus_lock(); + + r = dssdev->driver->memory_read(dssdev, buf, size, + x, y, w, h); + + /* Memory read usually changes the update area. This will + * force the next update to re-set the update area */ + dsi.active_update_region.dirty = true; + + dsi_bus_unlock(); + + return r; +} + +void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high) +{ + unsigned burst_size_bytes; + + *burst_size = OMAP_DSS_BURST_16x32; + burst_size_bytes = 16 * 32 / 8; + + *fifo_high = fifo_size - burst_size_bytes; + *fifo_low = fifo_size - burst_size_bytes * 8; +} + +int dsi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("DSI init\n"); + + dssdev->enable = dsi_display_enable; + dssdev->disable = dsi_display_disable; + dssdev->suspend = dsi_display_suspend; + dssdev->resume = dsi_display_resume; + dssdev->update = dsi_display_update; + dssdev->sync = dsi_display_sync; + dssdev->set_update_mode = dsi_display_set_update_mode; + dssdev->get_update_mode = dsi_display_get_update_mode; + dssdev->enable_te = dsi_display_enable_te; + dssdev->get_te = dsi_display_get_te; + + dssdev->get_rotate = dsi_display_get_rotate; + dssdev->set_rotate = dsi_display_set_rotate; + + dssdev->get_mirror = dsi_display_get_mirror; + dssdev->set_mirror = dsi_display_set_mirror; + + dssdev->run_test = dsi_display_run_test; + dssdev->memory_read = dsi_display_memory_read; + + /* XXX these should be figured out dynamically */ + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | + OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; + + dsi.vc[0].dssdev = dssdev; + dsi.vc[1].dssdev = dssdev; + + return 0; +} + +int dsi_init(struct platform_device *pdev) +{ + u32 rev; + int r; + struct sched_param param = { + .sched_priority = MAX_USER_RT_PRIO-1 + }; + + spin_lock_init(&dsi.errors_lock); + dsi.errors = 0; + + init_completion(&dsi.bta_completion); + init_completion(&dsi.update_completion); + + dsi.thread = kthread_create(dsi_update_thread, NULL, "dsi"); + if (IS_ERR(dsi.thread)) { + DSSERR("cannot create kthread\n"); + r = PTR_ERR(dsi.thread); + goto err0; + } + sched_setscheduler(dsi.thread, SCHED_FIFO, ¶m); + + init_waitqueue_head(&dsi.waitqueue); + spin_lock_init(&dsi.update_lock); + + mutex_init(&dsi.lock); + mutex_init(&dsi.bus_lock); + +#ifdef DSI_CATCH_MISSING_TE + init_timer(&dsi.te_timer); + dsi.te_timer.function = dsi_te_timeout; + dsi.te_timer.data = 0; +#endif + + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; + dsi.user_update_mode = OMAP_DSS_UPDATE_DISABLED; + + dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS); + if (!dsi.base) { + DSSERR("can't ioremap DSI\n"); + r = -ENOMEM; + goto err1; + } + + dsi.vdds_dsi_reg = regulator_get(&pdev->dev, "vdds_dsi"); + if (IS_ERR(dsi.vdds_dsi_reg)) { + iounmap(dsi.base); + DSSERR("can't get VDDS_DSI regulator\n"); + r = PTR_ERR(dsi.vdds_dsi_reg); + goto err2; + } + + enable_clocks(1); + + rev = dsi_read_reg(DSI_REVISION); + printk(KERN_INFO "OMAP DSI rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + enable_clocks(0); + + wake_up_process(dsi.thread); + + return 0; +err2: + iounmap(dsi.base); +err1: + kthread_stop(dsi.thread); +err0: + return r; +} + +void dsi_exit(void) +{ + kthread_stop(dsi.thread); + + regulator_put(dsi.vdds_dsi_reg); + + iounmap(dsi.base); + + DSSDBG("omap_dsi_exit\n"); +} + diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c new file mode 100644 index 000000000000..9b05ee65a15d --- /dev/null +++ b/drivers/video/omap2/dss/dss.c @@ -0,0 +1,596 @@ +/* + * linux/drivers/video/omap2/dss/dss.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DSS" + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/seq_file.h> +#include <linux/clk.h> + +#include <plat/display.h> +#include "dss.h" + +#define DSS_BASE 0x48050000 + +#define DSS_SZ_REGS SZ_512 + +struct dss_reg { + u16 idx; +}; + +#define DSS_REG(idx) ((const struct dss_reg) { idx }) + +#define DSS_REVISION DSS_REG(0x0000) +#define DSS_SYSCONFIG DSS_REG(0x0010) +#define DSS_SYSSTATUS DSS_REG(0x0014) +#define DSS_IRQSTATUS DSS_REG(0x0018) +#define DSS_CONTROL DSS_REG(0x0040) +#define DSS_SDI_CONTROL DSS_REG(0x0044) +#define DSS_PLL_CONTROL DSS_REG(0x0048) +#define DSS_SDI_STATUS DSS_REG(0x005C) + +#define REG_GET(idx, start, end) \ + FLD_GET(dss_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) + +static struct { + void __iomem *base; + + struct clk *dpll4_m4_ck; + + unsigned long cache_req_pck; + unsigned long cache_prate; + struct dss_clock_info cache_dss_cinfo; + struct dispc_clock_info cache_dispc_cinfo; + + u32 ctx[DSS_SZ_REGS / sizeof(u32)]; +} dss; + +static int _omap_dss_wait_reset(void); + +static inline void dss_write_reg(const struct dss_reg idx, u32 val) +{ + __raw_writel(val, dss.base + idx.idx); +} + +static inline u32 dss_read_reg(const struct dss_reg idx) +{ + return __raw_readl(dss.base + idx.idx); +} + +#define SR(reg) \ + dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg) +#define RR(reg) \ + dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) + +void dss_save_context(void) +{ + if (cpu_is_omap24xx()) + return; + + SR(SYSCONFIG); + SR(CONTROL); + +#ifdef CONFIG_OMAP2_DSS_SDI + SR(SDI_CONTROL); + SR(PLL_CONTROL); +#endif +} + +void dss_restore_context(void) +{ + if (_omap_dss_wait_reset()) + DSSERR("DSS not coming out of reset after sleep\n"); + + RR(SYSCONFIG); + RR(CONTROL); + +#ifdef CONFIG_OMAP2_DSS_SDI + RR(SDI_CONTROL); + RR(PLL_CONTROL); +#endif +} + +#undef SR +#undef RR + +void dss_sdi_init(u8 datapairs) +{ + u32 l; + + BUG_ON(datapairs > 3 || datapairs < 1); + + l = dss_read_reg(DSS_SDI_CONTROL); + l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */ + l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */ + l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */ + dss_write_reg(DSS_SDI_CONTROL, l); + + l = dss_read_reg(DSS_PLL_CONTROL); + l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */ + l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */ + l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */ + dss_write_reg(DSS_PLL_CONTROL, l); +} + +int dss_sdi_enable(void) +{ + unsigned long timeout; + + dispc_pck_free_enable(1); + + /* Reset SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */ + udelay(1); /* wait 2x PCLK */ + + /* Lock SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */ + + /* Waiting for PLL lock request to complete */ + timeout = jiffies + msecs_to_jiffies(500); + while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) { + if (time_after_eq(jiffies, timeout)) { + DSSERR("PLL lock request timed out\n"); + goto err1; + } + } + + /* Clearing PLL_GO bit */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28); + + /* Waiting for PLL to lock */ + timeout = jiffies + msecs_to_jiffies(500); + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) { + if (time_after_eq(jiffies, timeout)) { + DSSERR("PLL lock timed out\n"); + goto err1; + } + } + + dispc_lcd_enable_signal(1); + + /* Waiting for SDI reset to complete */ + timeout = jiffies + msecs_to_jiffies(500); + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) { + if (time_after_eq(jiffies, timeout)) { + DSSERR("SDI reset timed out\n"); + goto err2; + } + } + + return 0; + + err2: + dispc_lcd_enable_signal(0); + err1: + /* Reset SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ + + dispc_pck_free_enable(0); + + return -ETIMEDOUT; +} + +void dss_sdi_disable(void) +{ + dispc_lcd_enable_signal(0); + + dispc_pck_free_enable(0); + + /* Reset SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ +} + +void dss_dump_clocks(struct seq_file *s) +{ + unsigned long dpll4_ck_rate; + unsigned long dpll4_m4_ck_rate; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck); + + seq_printf(s, "- DSS -\n"); + + seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate); + + seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n", + dpll4_ck_rate, + dpll4_ck_rate / dpll4_m4_ck_rate, + dss_clk_get_rate(DSS_CLK_FCK1)); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +void dss_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(DSS_REVISION); + DUMPREG(DSS_SYSCONFIG); + DUMPREG(DSS_SYSSTATUS); + DUMPREG(DSS_IRQSTATUS); + DUMPREG(DSS_CONTROL); + DUMPREG(DSS_SDI_CONTROL); + DUMPREG(DSS_PLL_CONTROL); + DUMPREG(DSS_SDI_STATUS); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +void dss_select_clk_source(bool dsi, bool dispc) +{ + u32 r; + r = dss_read_reg(DSS_CONTROL); + r = FLD_MOD(r, dsi, 1, 1); /* DSI_CLK_SWITCH */ + r = FLD_MOD(r, dispc, 0, 0); /* DISPC_CLK_SWITCH */ + dss_write_reg(DSS_CONTROL, r); +} + +int dss_get_dsi_clk_source(void) +{ + return FLD_GET(dss_read_reg(DSS_CONTROL), 1, 1); +} + +int dss_get_dispc_clk_source(void) +{ + return FLD_GET(dss_read_reg(DSS_CONTROL), 0, 0); +} + +/* calculate clock rates using dividers in cinfo */ +int dss_calc_clock_rates(struct dss_clock_info *cinfo) +{ + unsigned long prate; + + if (cinfo->fck_div > 16 || cinfo->fck_div == 0) + return -EINVAL; + + prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + + cinfo->fck = prate / cinfo->fck_div; + + return 0; +} + +int dss_set_clock_div(struct dss_clock_info *cinfo) +{ + unsigned long prate; + int r; + + if (cpu_is_omap34xx()) { + prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + DSSDBG("dpll4_m4 = %ld\n", prate); + + r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div); + if (r) + return r; + } + + DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div); + + return 0; +} + +int dss_get_clock_div(struct dss_clock_info *cinfo) +{ + cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK1); + + if (cpu_is_omap34xx()) { + unsigned long prate; + prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + cinfo->fck_div = prate / (cinfo->fck / 2); + } else { + cinfo->fck_div = 0; + } + + return 0; +} + +unsigned long dss_get_dpll4_rate(void) +{ + if (cpu_is_omap34xx()) + return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + else + return 0; +} + +int dss_calc_clock_div(bool is_tft, unsigned long req_pck, + struct dss_clock_info *dss_cinfo, + struct dispc_clock_info *dispc_cinfo) +{ + unsigned long prate; + struct dss_clock_info best_dss; + struct dispc_clock_info best_dispc; + + unsigned long fck; + + u16 fck_div; + + int match = 0; + int min_fck_per_pck; + + prate = dss_get_dpll4_rate(); + + fck = dss_clk_get_rate(DSS_CLK_FCK1); + if (req_pck == dss.cache_req_pck && + ((cpu_is_omap34xx() && prate == dss.cache_prate) || + dss.cache_dss_cinfo.fck == fck)) { + DSSDBG("dispc clock info found from cache.\n"); + *dss_cinfo = dss.cache_dss_cinfo; + *dispc_cinfo = dss.cache_dispc_cinfo; + return 0; + } + + min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; + + if (min_fck_per_pck && + req_pck * min_fck_per_pck > DISPC_MAX_FCK) { + DSSERR("Requested pixel clock not possible with the current " + "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " + "the constraint off.\n"); + min_fck_per_pck = 0; + } + +retry: + memset(&best_dss, 0, sizeof(best_dss)); + memset(&best_dispc, 0, sizeof(best_dispc)); + + if (cpu_is_omap24xx()) { + struct dispc_clock_info cur_dispc; + /* XXX can we change the clock on omap2? */ + fck = dss_clk_get_rate(DSS_CLK_FCK1); + fck_div = 1; + + dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); + match = 1; + + best_dss.fck = fck; + best_dss.fck_div = fck_div; + + best_dispc = cur_dispc; + + goto found; + } else if (cpu_is_omap34xx()) { + for (fck_div = 16; fck_div > 0; --fck_div) { + struct dispc_clock_info cur_dispc; + + fck = prate / fck_div * 2; + + if (fck > DISPC_MAX_FCK) + continue; + + if (min_fck_per_pck && + fck < req_pck * min_fck_per_pck) + continue; + + match = 1; + + dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); + + if (abs(cur_dispc.pck - req_pck) < + abs(best_dispc.pck - req_pck)) { + + best_dss.fck = fck; + best_dss.fck_div = fck_div; + + best_dispc = cur_dispc; + + if (cur_dispc.pck == req_pck) + goto found; + } + } + } else { + BUG(); + } + +found: + if (!match) { + if (min_fck_per_pck) { + DSSERR("Could not find suitable clock settings.\n" + "Turning FCK/PCK constraint off and" + "trying again.\n"); + min_fck_per_pck = 0; + goto retry; + } + + DSSERR("Could not find suitable clock settings.\n"); + + return -EINVAL; + } + + if (dss_cinfo) + *dss_cinfo = best_dss; + if (dispc_cinfo) + *dispc_cinfo = best_dispc; + + dss.cache_req_pck = req_pck; + dss.cache_prate = prate; + dss.cache_dss_cinfo = best_dss; + dss.cache_dispc_cinfo = best_dispc; + + return 0; +} + + + +static irqreturn_t dss_irq_handler_omap2(int irq, void *arg) +{ + dispc_irq_handler(); + + return IRQ_HANDLED; +} + +static irqreturn_t dss_irq_handler_omap3(int irq, void *arg) +{ + u32 irqstatus; + + irqstatus = dss_read_reg(DSS_IRQSTATUS); + + if (irqstatus & (1<<0)) /* DISPC_IRQ */ + dispc_irq_handler(); +#ifdef CONFIG_OMAP2_DSS_DSI + if (irqstatus & (1<<1)) /* DSI_IRQ */ + dsi_irq_handler(); +#endif + + return IRQ_HANDLED; +} + +static int _omap_dss_wait_reset(void) +{ + unsigned timeout = 1000; + + while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) { + udelay(1); + if (!--timeout) { + DSSERR("soft reset failed\n"); + return -ENODEV; + } + } + + return 0; +} + +static int _omap_dss_reset(void) +{ + /* Soft reset */ + REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1); + return _omap_dss_wait_reset(); +} + +void dss_set_venc_output(enum omap_dss_venc_type type) +{ + int l = 0; + + if (type == OMAP_DSS_VENC_TYPE_COMPOSITE) + l = 0; + else if (type == OMAP_DSS_VENC_TYPE_SVIDEO) + l = 1; + else + BUG(); + + /* venc out selection. 0 = comp, 1 = svideo */ + REG_FLD_MOD(DSS_CONTROL, l, 6, 6); +} + +void dss_set_dac_pwrdn_bgz(bool enable) +{ + REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ +} + +int dss_init(bool skip_init) +{ + int r; + u32 rev; + + dss.base = ioremap(DSS_BASE, DSS_SZ_REGS); + if (!dss.base) { + DSSERR("can't ioremap DSS\n"); + r = -ENOMEM; + goto fail0; + } + + if (!skip_init) { + /* disable LCD and DIGIT output. This seems to fix the synclost + * problem that we get, if the bootloader starts the DSS and + * the kernel resets it */ + omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440); + + /* We need to wait here a bit, otherwise we sometimes start to + * get synclost errors, and after that only power cycle will + * restore DSS functionality. I have no idea why this happens. + * And we have to wait _before_ resetting the DSS, but after + * enabling clocks. + */ + msleep(50); + + _omap_dss_reset(); + } + + /* autoidle */ + REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0); + + /* Select DPLL */ + REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); + +#ifdef CONFIG_OMAP2_DSS_VENC + REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */ + REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ + REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ +#endif + + r = request_irq(INT_24XX_DSS_IRQ, + cpu_is_omap24xx() + ? dss_irq_handler_omap2 + : dss_irq_handler_omap3, + 0, "OMAP DSS", NULL); + + if (r < 0) { + DSSERR("omap2 dss: request_irq failed\n"); + goto fail1; + } + + if (cpu_is_omap34xx()) { + dss.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); + if (IS_ERR(dss.dpll4_m4_ck)) { + DSSERR("Failed to get dpll4_m4_ck\n"); + r = PTR_ERR(dss.dpll4_m4_ck); + goto fail2; + } + } + + dss_save_context(); + + rev = dss_read_reg(DSS_REVISION); + printk(KERN_INFO "OMAP DSS rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + return 0; + +fail2: + free_irq(INT_24XX_DSS_IRQ, NULL); +fail1: + iounmap(dss.base); +fail0: + return r; +} + +void dss_exit(void) +{ + if (cpu_is_omap34xx()) + clk_put(dss.dpll4_m4_ck); + + free_irq(INT_24XX_DSS_IRQ, NULL); + + iounmap(dss.base); +} + diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h new file mode 100644 index 000000000000..8da5ac42151b --- /dev/null +++ b/drivers/video/omap2/dss/dss.h @@ -0,0 +1,370 @@ +/* + * linux/drivers/video/omap2/dss/dss.h + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAP2_DSS_H +#define __OMAP2_DSS_H + +#ifdef CONFIG_OMAP2_DSS_DEBUG_SUPPORT +#define DEBUG +#endif + +#ifdef DEBUG +extern unsigned int dss_debug; +#ifdef DSS_SUBSYS_NAME +#define DSSDBG(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSDBG(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSDBGF(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME \ + ": %s(" format ")\n", \ + __func__, \ + ## __VA_ARGS__) +#else +#define DSSDBGF(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss: " \ + ": %s(" format ")\n", \ + __func__, \ + ## __VA_ARGS__) +#endif + +#else /* DEBUG */ +#define DSSDBG(format, ...) +#define DSSDBGF(format, ...) +#endif + + +#ifdef DSS_SUBSYS_NAME +#define DSSERR(format, ...) \ + printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \ + ## __VA_ARGS__) +#else +#define DSSERR(format, ...) \ + printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSINFO(format, ...) \ + printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSINFO(format, ...) \ + printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSWARN(format, ...) \ + printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSWARN(format, ...) \ + printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__) +#endif + +/* OMAP TRM gives bitfields as start:end, where start is the higher bit + number. For example 7:0 */ +#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) +#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) +#define FLD_MOD(orig, val, start, end) \ + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) + +#define DISPC_MAX_FCK 173000000 + +enum omap_burst_size { + OMAP_DSS_BURST_4x32 = 0, + OMAP_DSS_BURST_8x32 = 1, + OMAP_DSS_BURST_16x32 = 2, +}; + +enum omap_parallel_interface_mode { + OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */ + OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */ + OMAP_DSS_PARALLELMODE_DSI, +}; + +enum dss_clock { + DSS_CLK_ICK = 1 << 0, + DSS_CLK_FCK1 = 1 << 1, + DSS_CLK_FCK2 = 1 << 2, + DSS_CLK_54M = 1 << 3, + DSS_CLK_96M = 1 << 4, +}; + +struct dss_clock_info { + /* rates that we get with dividers below */ + unsigned long fck; + + /* dividers */ + u16 fck_div; +}; + +struct dispc_clock_info { + /* rates that we get with dividers below */ + unsigned long lck; + unsigned long pck; + + /* dividers */ + u16 lck_div; + u16 pck_div; +}; + +struct dsi_clock_info { + /* rates that we get with dividers below */ + unsigned long fint; + unsigned long clkin4ddr; + unsigned long clkin; + unsigned long dsi1_pll_fclk; + unsigned long dsi2_pll_fclk; + + unsigned long lp_clk; + + /* dividers */ + u16 regn; + u16 regm; + u16 regm3; + u16 regm4; + + u16 lp_clk_div; + + u8 highfreq; + bool use_dss2_fck; +}; + +struct seq_file; +struct platform_device; + +/* core */ +void dss_clk_enable(enum dss_clock clks); +void dss_clk_disable(enum dss_clock clks); +unsigned long dss_clk_get_rate(enum dss_clock clk); +int dss_need_ctx_restore(void); +void dss_dump_clocks(struct seq_file *s); +struct bus_type *dss_get_bus(void); + +/* display */ +int dss_suspend_all_devices(void); +int dss_resume_all_devices(void); +void dss_disable_all_devices(void); + +void dss_init_device(struct platform_device *pdev, + struct omap_dss_device *dssdev); +void dss_uninit_device(struct platform_device *pdev, + struct omap_dss_device *dssdev); +bool dss_use_replication(struct omap_dss_device *dssdev, + enum omap_color_mode mode); +void default_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high); + +/* manager */ +int dss_init_overlay_managers(struct platform_device *pdev); +void dss_uninit_overlay_managers(struct platform_device *pdev); +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl); +void dss_setup_partial_planes(struct omap_dss_device *dssdev, + u16 *x, u16 *y, u16 *w, u16 *h); +void dss_start_update(struct omap_dss_device *dssdev); + +/* overlay */ +void dss_init_overlays(struct platform_device *pdev); +void dss_uninit_overlays(struct platform_device *pdev); +int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev); +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); +#ifdef L4_EXAMPLE +void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr); +#endif +void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); + +/* DSS */ +int dss_init(bool skip_init); +void dss_exit(void); + +void dss_save_context(void); +void dss_restore_context(void); + +void dss_dump_regs(struct seq_file *s); + +void dss_sdi_init(u8 datapairs); +int dss_sdi_enable(void); +void dss_sdi_disable(void); + +void dss_select_clk_source(bool dsi, bool dispc); +int dss_get_dsi_clk_source(void); +int dss_get_dispc_clk_source(void); +void dss_set_venc_output(enum omap_dss_venc_type type); +void dss_set_dac_pwrdn_bgz(bool enable); + +unsigned long dss_get_dpll4_rate(void); +int dss_calc_clock_rates(struct dss_clock_info *cinfo); +int dss_set_clock_div(struct dss_clock_info *cinfo); +int dss_get_clock_div(struct dss_clock_info *cinfo); +int dss_calc_clock_div(bool is_tft, unsigned long req_pck, + struct dss_clock_info *dss_cinfo, + struct dispc_clock_info *dispc_cinfo); + +/* SDI */ +int sdi_init(bool skip_init); +void sdi_exit(void); +int sdi_init_display(struct omap_dss_device *display); + +/* DSI */ +int dsi_init(struct platform_device *pdev); +void dsi_exit(void); + +void dsi_dump_clocks(struct seq_file *s); +void dsi_dump_regs(struct seq_file *s); + +void dsi_save_context(void); +void dsi_restore_context(void); + +int dsi_init_display(struct omap_dss_device *display); +void dsi_irq_handler(void); +unsigned long dsi_get_dsi1_pll_rate(void); +int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo); +int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, + struct dsi_clock_info *cinfo, + struct dispc_clock_info *dispc_cinfo); +int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, + bool enable_hsdiv); +void dsi_pll_uninit(void); +void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high); + +/* DPI */ +int dpi_init(void); +void dpi_exit(void); +int dpi_init_display(struct omap_dss_device *dssdev); + +/* DISPC */ +int dispc_init(void); +void dispc_exit(void); +void dispc_dump_clocks(struct seq_file *s); +void dispc_dump_regs(struct seq_file *s); +void dispc_irq_handler(void); +void dispc_fake_vsync_irq(void); + +void dispc_save_context(void); +void dispc_restore_context(void); + +void dispc_enable_sidle(void); +void dispc_disable_sidle(void); + +void dispc_lcd_enable_signal_polarity(bool act_high); +void dispc_lcd_enable_signal(bool enable); +void dispc_pck_free_enable(bool enable); +void dispc_enable_fifohandcheck(bool enable); + +void dispc_set_lcd_size(u16 width, u16 height); +void dispc_set_digit_size(u16 width, u16 height); +u32 dispc_get_plane_fifo_size(enum omap_plane plane); +void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high); +void dispc_enable_fifomerge(bool enable); +void dispc_set_burst_size(enum omap_plane plane, + enum omap_burst_size burst_size); + +void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr); +void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr); +void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y); +void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height); +void dispc_set_channel_out(enum omap_plane plane, + enum omap_channel channel_out); + +int dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, bool mirror, + u8 global_alpha); + +bool dispc_go_busy(enum omap_channel channel); +void dispc_go(enum omap_channel channel); +void dispc_enable_lcd_out(bool enable); +void dispc_enable_digit_out(bool enable); +int dispc_enable_plane(enum omap_plane plane, bool enable); +void dispc_enable_replication(enum omap_plane plane, bool enable); + +void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode); +void dispc_set_tft_data_lines(u8 data_lines); +void dispc_set_lcd_display_type(enum omap_lcd_display_type type); +void dispc_set_loadmode(enum omap_dss_load_mode mode); + +void dispc_set_default_color(enum omap_channel channel, u32 color); +u32 dispc_get_default_color(enum omap_channel channel); +void dispc_set_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type type, + u32 trans_key); +void dispc_get_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type *type, + u32 *trans_key); +void dispc_enable_trans_key(enum omap_channel ch, bool enable); +void dispc_enable_alpha_blending(enum omap_channel ch, bool enable); +bool dispc_trans_key_enabled(enum omap_channel ch); +bool dispc_alpha_blending_enabled(enum omap_channel ch); + +bool dispc_lcd_timings_ok(struct omap_video_timings *timings); +void dispc_set_lcd_timings(struct omap_video_timings *timings); +unsigned long dispc_fclk_rate(void); +unsigned long dispc_lclk_rate(void); +unsigned long dispc_pclk_rate(void); +void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb); +void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, + struct dispc_clock_info *cinfo); +int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, + struct dispc_clock_info *cinfo); +int dispc_set_clock_div(struct dispc_clock_info *cinfo); +int dispc_get_clock_div(struct dispc_clock_info *cinfo); + + +/* VENC */ +int venc_init(struct platform_device *pdev); +void venc_exit(void); +void venc_dump_regs(struct seq_file *s); +int venc_init_display(struct omap_dss_device *display); + +/* RFBI */ +int rfbi_init(void); +void rfbi_exit(void); +void rfbi_dump_regs(struct seq_file *s); + +int rfbi_configure(int rfbi_module, int bpp, int lines); +void rfbi_enable_rfbi(bool enable); +void rfbi_transfer_area(u16 width, u16 height, + void (callback)(void *data), void *data); +void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t); +unsigned long rfbi_get_max_tx_rate(void); +int rfbi_init_display(struct omap_dss_device *display); + +#endif diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c new file mode 100644 index 000000000000..27d9c465c851 --- /dev/null +++ b/drivers/video/omap2/dss/manager.c @@ -0,0 +1,1487 @@ +/* + * linux/drivers/video/omap2/dss/manager.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "MANAGER" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/jiffies.h> + +#include <plat/display.h> +#include <plat/cpu.h> + +#include "dss.h" + +static int num_managers; +static struct list_head manager_list; + +static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); +} + +static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + mgr->device ? mgr->device->name : "<none>"); +} + +static ssize_t manager_display_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + int r = 0; + size_t len = size; + struct omap_dss_device *dssdev = NULL; + + int match(struct omap_dss_device *dssdev, void *data) + { + const char *str = data; + return sysfs_streq(dssdev->name, str); + } + + if (buf[size-1] == '\n') + --len; + + if (len > 0) + dssdev = omap_dss_find_device((void *)buf, match); + + if (len > 0 && dssdev == NULL) + return -EINVAL; + + if (dssdev) + DSSDBG("display %s found\n", dssdev->name); + + if (mgr->device) { + r = mgr->unset_device(mgr); + if (r) { + DSSERR("failed to unset display\n"); + goto put_device; + } + } + + if (dssdev) { + r = mgr->set_device(mgr, dssdev); + if (r) { + DSSERR("failed to set manager\n"); + goto put_device; + } + + r = mgr->apply(mgr); + if (r) { + DSSERR("failed to apply dispc config\n"); + goto put_device; + } + } + +put_device: + if (dssdev) + omap_dss_put_device(dssdev); + + return r ? r : size; +} + +static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color); +} + +static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + u32 color; + int r; + + if (sscanf(buf, "%d", &color) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.default_color = color; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static const char *trans_key_type_str[] = { + "gfx-destination", + "video-source", +}; + +static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, + char *buf) +{ + enum omap_dss_trans_key_type key_type; + + key_type = mgr->info.trans_key_type; + BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); + + return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); +} + +static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + enum omap_dss_trans_key_type key_type; + struct omap_overlay_manager_info info; + int r; + + for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { + if (sysfs_streq(buf, trans_key_type_str[key_type])) + break; + } + + if (key_type == ARRAY_SIZE(trans_key_type_str)) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_key_type = key_type; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key); +} + +static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + u32 key_value; + int r; + + if (sscanf(buf, "%d", &key_value) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_key = key_value; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled); +} + +static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + int enable; + int r; + + if (sscanf(buf, "%d", &enable) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_enabled = enable ? true : false; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_alpha_blending_enabled_show( + struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled); +} + +static ssize_t manager_alpha_blending_enabled_store( + struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + int enable; + int r; + + if (sscanf(buf, "%d", &enable) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.alpha_enabled = enable ? true : false; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +struct manager_attribute { + struct attribute attr; + ssize_t (*show)(struct omap_overlay_manager *, char *); + ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t); +}; + +#define MANAGER_ATTR(_name, _mode, _show, _store) \ + struct manager_attribute manager_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); +static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, + manager_display_show, manager_display_store); +static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, + manager_default_color_show, manager_default_color_store); +static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, + manager_trans_key_type_show, manager_trans_key_type_store); +static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, + manager_trans_key_value_show, manager_trans_key_value_store); +static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, + manager_trans_key_enabled_show, + manager_trans_key_enabled_store); +static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, + manager_alpha_blending_enabled_show, + manager_alpha_blending_enabled_store); + + +static struct attribute *manager_sysfs_attrs[] = { + &manager_attr_name.attr, + &manager_attr_display.attr, + &manager_attr_default_color.attr, + &manager_attr_trans_key_type.attr, + &manager_attr_trans_key_value.attr, + &manager_attr_trans_key_enabled.attr, + &manager_attr_alpha_blending_enabled.attr, + NULL +}; + +static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct omap_overlay_manager *manager; + struct manager_attribute *manager_attr; + + manager = container_of(kobj, struct omap_overlay_manager, kobj); + manager_attr = container_of(attr, struct manager_attribute, attr); + + if (!manager_attr->show) + return -ENOENT; + + return manager_attr->show(manager, buf); +} + +static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ + struct omap_overlay_manager *manager; + struct manager_attribute *manager_attr; + + manager = container_of(kobj, struct omap_overlay_manager, kobj); + manager_attr = container_of(attr, struct manager_attribute, attr); + + if (!manager_attr->store) + return -ENOENT; + + return manager_attr->store(manager, buf, size); +} + +static struct sysfs_ops manager_sysfs_ops = { + .show = manager_attr_show, + .store = manager_attr_store, +}; + +static struct kobj_type manager_ktype = { + .sysfs_ops = &manager_sysfs_ops, + .default_attrs = manager_sysfs_attrs, +}; + +/* + * We have 4 levels of cache for the dispc settings. First two are in SW and + * the latter two in HW. + * + * +--------------------+ + * |overlay/manager_info| + * +--------------------+ + * v + * apply() + * v + * +--------------------+ + * | dss_cache | + * +--------------------+ + * v + * configure() + * v + * +--------------------+ + * | shadow registers | + * +--------------------+ + * v + * VFP or lcd/digit_enable + * v + * +--------------------+ + * | registers | + * +--------------------+ + */ + +struct overlay_cache_data { + /* If true, cache changed, but not written to shadow registers. Set + * in apply(), cleared when registers written. */ + bool dirty; + /* If true, shadow registers contain changed values not yet in real + * registers. Set when writing to shadow registers, cleared at + * VSYNC/EVSYNC */ + bool shadow_dirty; + + bool enabled; + + u32 paddr; + void __iomem *vaddr; + u16 screen_width; + u16 width; + u16 height; + enum omap_color_mode color_mode; + u8 rotation; + enum omap_dss_rotation_type rotation_type; + bool mirror; + + u16 pos_x; + u16 pos_y; + u16 out_width; /* if 0, out_width == width */ + u16 out_height; /* if 0, out_height == height */ + u8 global_alpha; + + enum omap_channel channel; + bool replication; + bool ilace; + + enum omap_burst_size burst_size; + u32 fifo_low; + u32 fifo_high; + + bool manual_update; +}; + +struct manager_cache_data { + /* If true, cache changed, but not written to shadow registers. Set + * in apply(), cleared when registers written. */ + bool dirty; + /* If true, shadow registers contain changed values not yet in real + * registers. Set when writing to shadow registers, cleared at + * VSYNC/EVSYNC */ + bool shadow_dirty; + + u32 default_color; + + enum omap_dss_trans_key_type trans_key_type; + u32 trans_key; + bool trans_enabled; + + bool alpha_enabled; + + bool manual_upd_display; + bool manual_update; + bool do_manual_update; + + /* manual update region */ + u16 x, y, w, h; +}; + +static struct { + spinlock_t lock; + struct overlay_cache_data overlay_cache[3]; + struct manager_cache_data manager_cache[2]; + + bool irq_enabled; +} dss_cache; + + + +static int omap_dss_set_device(struct omap_overlay_manager *mgr, + struct omap_dss_device *dssdev) +{ + int i; + int r; + + if (dssdev->manager) { + DSSERR("display '%s' already has a manager '%s'\n", + dssdev->name, dssdev->manager->name); + return -EINVAL; + } + + if ((mgr->supported_displays & dssdev->type) == 0) { + DSSERR("display '%s' does not support manager '%s'\n", + dssdev->name, mgr->name); + return -EINVAL; + } + + for (i = 0; i < mgr->num_overlays; i++) { + struct omap_overlay *ovl = mgr->overlays[i]; + + if (ovl->manager != mgr || !ovl->info.enabled) + continue; + + r = dss_check_overlay(ovl, dssdev); + if (r) + return r; + } + + dssdev->manager = mgr; + mgr->device = dssdev; + mgr->device_changed = true; + + return 0; +} + +static int omap_dss_unset_device(struct omap_overlay_manager *mgr) +{ + if (!mgr->device) { + DSSERR("failed to unset display, display not set.\n"); + return -EINVAL; + } + + mgr->device->manager = NULL; + mgr->device = NULL; + mgr->device_changed = true; + + return 0; +} + +static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) +{ + unsigned long timeout = msecs_to_jiffies(500); + struct manager_cache_data *mc; + enum omap_channel channel; + u32 irq; + int r; + int i; + + if (!mgr->device) + return 0; + + if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; + channel = OMAP_DSS_CHANNEL_DIGIT; + } else { + if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { + enum omap_dss_update_mode mode; + mode = mgr->device->get_update_mode(mgr->device); + if (mode != OMAP_DSS_UPDATE_AUTO) + return 0; + + irq = DISPC_IRQ_FRAMEDONE; + } else { + irq = DISPC_IRQ_VSYNC; + } + channel = OMAP_DSS_CHANNEL_LCD; + } + + mc = &dss_cache.manager_cache[mgr->id]; + i = 0; + while (1) { + unsigned long flags; + bool shadow_dirty, dirty; + + spin_lock_irqsave(&dss_cache.lock, flags); + dirty = mc->dirty; + shadow_dirty = mc->shadow_dirty; + spin_unlock_irqrestore(&dss_cache.lock, flags); + + if (!dirty && !shadow_dirty) { + r = 0; + break; + } + + /* 4 iterations is the worst case: + * 1 - initial iteration, dirty = true (between VFP and VSYNC) + * 2 - first VSYNC, dirty = true + * 3 - dirty = false, shadow_dirty = true + * 4 - shadow_dirty = false */ + if (i++ == 3) { + DSSERR("mgr(%d)->wait_for_go() not finishing\n", + mgr->id); + r = 0; + break; + } + + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + if (r == -ERESTARTSYS) + break; + + if (r) { + DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id); + break; + } + } + + return r; +} + +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) +{ + unsigned long timeout = msecs_to_jiffies(500); + enum omap_channel channel; + struct overlay_cache_data *oc; + struct omap_dss_device *dssdev; + u32 irq; + int r; + int i; + + if (!ovl->manager || !ovl->manager->device) + return 0; + + dssdev = ovl->manager->device; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; + channel = OMAP_DSS_CHANNEL_DIGIT; + } else { + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { + enum omap_dss_update_mode mode; + mode = dssdev->get_update_mode(dssdev); + if (mode != OMAP_DSS_UPDATE_AUTO) + return 0; + + irq = DISPC_IRQ_FRAMEDONE; + } else { + irq = DISPC_IRQ_VSYNC; + } + channel = OMAP_DSS_CHANNEL_LCD; + } + + oc = &dss_cache.overlay_cache[ovl->id]; + i = 0; + while (1) { + unsigned long flags; + bool shadow_dirty, dirty; + + spin_lock_irqsave(&dss_cache.lock, flags); + dirty = oc->dirty; + shadow_dirty = oc->shadow_dirty; + spin_unlock_irqrestore(&dss_cache.lock, flags); + + if (!dirty && !shadow_dirty) { + r = 0; + break; + } + + /* 4 iterations is the worst case: + * 1 - initial iteration, dirty = true (between VFP and VSYNC) + * 2 - first VSYNC, dirty = true + * 3 - dirty = false, shadow_dirty = true + * 4 - shadow_dirty = false */ + if (i++ == 3) { + DSSERR("ovl(%d)->wait_for_go() not finishing\n", + ovl->id); + r = 0; + break; + } + + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + if (r == -ERESTARTSYS) + break; + + if (r) { + DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id); + break; + } + } + + return r; +} + +static int overlay_enabled(struct omap_overlay *ovl) +{ + return ovl->info.enabled && ovl->manager && ovl->manager->device; +} + +/* Is rect1 a subset of rect2? */ +static bool rectangle_subset(int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2) +{ + if (x1 < x2 || y1 < y2) + return false; + + if (x1 + w1 > x2 + w2) + return false; + + if (y1 + h1 > y2 + h2) + return false; + + return true; +} + +/* Do rect1 and rect2 overlap? */ +static bool rectangle_intersects(int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2) +{ + if (x1 >= x2 + w2) + return false; + + if (x2 >= x1 + w1) + return false; + + if (y1 >= y2 + h2) + return false; + + if (y2 >= y1 + h1) + return false; + + return true; +} + +static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc) +{ + if (oc->out_width != 0 && oc->width != oc->out_width) + return true; + + if (oc->out_height != 0 && oc->height != oc->out_height) + return true; + + return false; +} + +static int configure_overlay(enum omap_plane plane) +{ + struct overlay_cache_data *c; + struct manager_cache_data *mc; + u16 outw, outh; + u16 x, y, w, h; + u32 paddr; + int r; + + DSSDBGF("%d", plane); + + c = &dss_cache.overlay_cache[plane]; + + if (!c->enabled) { + dispc_enable_plane(plane, 0); + return 0; + } + + mc = &dss_cache.manager_cache[c->channel]; + + x = c->pos_x; + y = c->pos_y; + w = c->width; + h = c->height; + outw = c->out_width == 0 ? c->width : c->out_width; + outh = c->out_height == 0 ? c->height : c->out_height; + paddr = c->paddr; + + if (c->manual_update && mc->do_manual_update) { + unsigned bpp; + /* If the overlay is outside the update region, disable it */ + if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, + x, y, outw, outh)) { + dispc_enable_plane(plane, 0); + return 0; + } + + switch (c->color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + bpp = 16; + break; + + case OMAP_DSS_COLOR_RGB24P: + bpp = 24; + break; + + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + bpp = 32; + break; + + default: + BUG(); + } + + if (dispc_is_overlay_scaled(c)) { + /* If the overlay is scaled, the update area has + * already been enlarged to cover the whole overlay. We + * only need to adjust x/y here */ + x = c->pos_x - mc->x; + y = c->pos_y - mc->y; + } else { + if (mc->x > c->pos_x) { + x = 0; + w -= (mc->x - c->pos_x); + paddr += (mc->x - c->pos_x) * bpp / 8; + } else { + x = c->pos_x - mc->x; + } + + if (mc->y > c->pos_y) { + y = 0; + h -= (mc->y - c->pos_y); + paddr += (mc->y - c->pos_y) * c->screen_width * + bpp / 8; + } else { + y = c->pos_y - mc->y; + } + + if (mc->w < (x+w)) + w -= (x+w) - (mc->w); + + if (mc->h < (y+h)) + h -= (y+h) - (mc->h); + + outw = w; + outh = h; + } + } + + r = dispc_setup_plane(plane, + paddr, + c->screen_width, + x, y, + w, h, + outw, outh, + c->color_mode, + c->ilace, + c->rotation_type, + c->rotation, + c->mirror, + c->global_alpha); + + if (r) { + /* this shouldn't happen */ + DSSERR("dispc_setup_plane failed for ovl %d\n", plane); + dispc_enable_plane(plane, 0); + return r; + } + + dispc_enable_replication(plane, c->replication); + + dispc_set_burst_size(plane, c->burst_size); + dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); + + dispc_enable_plane(plane, 1); + + return 0; +} + +static void configure_manager(enum omap_channel channel) +{ + struct manager_cache_data *c; + + DSSDBGF("%d", channel); + + c = &dss_cache.manager_cache[channel]; + + dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); + dispc_enable_trans_key(channel, c->trans_enabled); + dispc_enable_alpha_blending(channel, c->alpha_enabled); +} + +/* configure_dispc() tries to write values from cache to shadow registers. + * It writes only to those managers/overlays that are not busy. + * returns 0 if everything could be written to shadow registers. + * returns 1 if not everything could be written to shadow registers. */ +static int configure_dispc(void) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + int i; + int r; + bool mgr_busy[2]; + bool mgr_go[2]; + bool busy; + + r = 0; + busy = false; + + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + mgr_go[0] = false; + mgr_go[1] = false; + + /* Commit overlay settings */ + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + mc = &dss_cache.manager_cache[oc->channel]; + + if (!oc->dirty) + continue; + + if (oc->manual_update && !mc->do_manual_update) + continue; + + if (mgr_busy[oc->channel]) { + busy = true; + continue; + } + + r = configure_overlay(i); + if (r) + DSSERR("configure_overlay %d failed\n", i); + + oc->dirty = false; + oc->shadow_dirty = true; + mgr_go[oc->channel] = true; + } + + /* Commit manager settings */ + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + + if (!mc->dirty) + continue; + + if (mc->manual_update && !mc->do_manual_update) + continue; + + if (mgr_busy[i]) { + busy = true; + continue; + } + + configure_manager(i); + mc->dirty = false; + mc->shadow_dirty = true; + mgr_go[i] = true; + } + + /* set GO */ + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + + if (!mgr_go[i]) + continue; + + /* We don't need GO with manual update display. LCD iface will + * always be turned off after frame, and new settings will be + * taken in to use at next update */ + if (!mc->manual_upd_display) + dispc_go(i); + } + + if (busy) + r = 1; + else + r = 0; + + return r; +} + +/* Configure dispc for partial update. Return possibly modified update + * area */ +void dss_setup_partial_planes(struct omap_dss_device *dssdev, + u16 *xi, u16 *yi, u16 *wi, u16 *hi) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + struct omap_overlay_manager *mgr; + int i; + u16 x, y, w, h; + unsigned long flags; + + x = *xi; + y = *yi; + w = *wi; + h = *hi; + + DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n", + *xi, *yi, *wi, *hi); + + mgr = dssdev->manager; + + if (!mgr) { + DSSDBG("no manager\n"); + return; + } + + spin_lock_irqsave(&dss_cache.lock, flags); + + /* We need to show the whole overlay if it is scaled. So look for + * those, and make the update area larger if found. + * Also mark the overlay cache dirty */ + for (i = 0; i < num_ovls; ++i) { + unsigned x1, y1, x2, y2; + unsigned outw, outh; + + oc = &dss_cache.overlay_cache[i]; + + if (oc->channel != mgr->id) + continue; + + oc->dirty = true; + + if (!oc->enabled) + continue; + + if (!dispc_is_overlay_scaled(oc)) + continue; + + outw = oc->out_width == 0 ? oc->width : oc->out_width; + outh = oc->out_height == 0 ? oc->height : oc->out_height; + + /* is the overlay outside the update region? */ + if (!rectangle_intersects(x, y, w, h, + oc->pos_x, oc->pos_y, + outw, outh)) + continue; + + /* if the overlay totally inside the update region? */ + if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, + x, y, w, h)) + continue; + + if (x > oc->pos_x) + x1 = oc->pos_x; + else + x1 = x; + + if (y > oc->pos_y) + y1 = oc->pos_y; + else + y1 = y; + + if ((x + w) < (oc->pos_x + outw)) + x2 = oc->pos_x + outw; + else + x2 = x + w; + + if ((y + h) < (oc->pos_y + outh)) + y2 = oc->pos_y + outh; + else + y2 = y + h; + + x = x1; + y = y1; + w = x2 - x1; + h = y2 - y1; + + DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", + i, x, y, w, h); + } + + mc = &dss_cache.manager_cache[mgr->id]; + mc->do_manual_update = true; + mc->x = x; + mc->y = y; + mc->w = w; + mc->h = h; + + configure_dispc(); + + mc->do_manual_update = false; + + spin_unlock_irqrestore(&dss_cache.lock, flags); + + *xi = x; + *yi = y; + *wi = w; + *hi = h; +} + +void dss_start_update(struct omap_dss_device *dssdev) +{ + struct manager_cache_data *mc; + struct overlay_cache_data *oc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + struct omap_overlay_manager *mgr; + int i; + + mgr = dssdev->manager; + + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + if (oc->channel != mgr->id) + continue; + + oc->shadow_dirty = false; + } + + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + if (mgr->id != i) + continue; + + mc->shadow_dirty = false; + } + + dispc_enable_lcd_out(1); +} + +static void dss_apply_irq_handler(void *data, u32 mask) +{ + struct manager_cache_data *mc; + struct overlay_cache_data *oc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + int i, r; + bool mgr_busy[2]; + + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + + spin_lock(&dss_cache.lock); + + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + if (!mgr_busy[oc->channel]) + oc->shadow_dirty = false; + } + + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + if (!mgr_busy[i]) + mc->shadow_dirty = false; + } + + r = configure_dispc(); + if (r == 1) + goto end; + + /* re-read busy flags */ + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + + /* keep running as long as there are busy managers, so that + * we can collect overlay-applied information */ + for (i = 0; i < num_mgrs; ++i) { + if (mgr_busy[i]) + goto end; + } + + omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN); + dss_cache.irq_enabled = false; + +end: + spin_unlock(&dss_cache.lock); +} + +static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + int i; + struct omap_overlay *ovl; + int num_planes_enabled = 0; + bool use_fifomerge; + unsigned long flags; + int r; + + DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); + + spin_lock_irqsave(&dss_cache.lock, flags); + + /* Configure overlays */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_dss_device *dssdev; + + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + oc = &dss_cache.overlay_cache[ovl->id]; + + if (!overlay_enabled(ovl)) { + if (oc->enabled) { + oc->enabled = false; + oc->dirty = true; + } + continue; + } + + if (!ovl->info_dirty) { + if (oc->enabled) + ++num_planes_enabled; + continue; + } + + dssdev = ovl->manager->device; + + if (dss_check_overlay(ovl, dssdev)) { + if (oc->enabled) { + oc->enabled = false; + oc->dirty = true; + } + continue; + } + + ovl->info_dirty = false; + oc->dirty = true; + + oc->paddr = ovl->info.paddr; + oc->vaddr = ovl->info.vaddr; + oc->screen_width = ovl->info.screen_width; + oc->width = ovl->info.width; + oc->height = ovl->info.height; + oc->color_mode = ovl->info.color_mode; + oc->rotation = ovl->info.rotation; + oc->rotation_type = ovl->info.rotation_type; + oc->mirror = ovl->info.mirror; + oc->pos_x = ovl->info.pos_x; + oc->pos_y = ovl->info.pos_y; + oc->out_width = ovl->info.out_width; + oc->out_height = ovl->info.out_height; + oc->global_alpha = ovl->info.global_alpha; + + oc->replication = + dss_use_replication(dssdev, ovl->info.color_mode); + + oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC; + + oc->channel = ovl->manager->id; + + oc->enabled = true; + + oc->manual_update = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && + dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; + + ++num_planes_enabled; + } + + /* Configure managers */ + list_for_each_entry(mgr, &manager_list, list) { + struct omap_dss_device *dssdev; + + if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) + continue; + + mc = &dss_cache.manager_cache[mgr->id]; + + if (mgr->device_changed) { + mgr->device_changed = false; + mgr->info_dirty = true; + } + + if (!mgr->info_dirty) + continue; + + if (!mgr->device) + continue; + + dssdev = mgr->device; + + mgr->info_dirty = false; + mc->dirty = true; + + mc->default_color = mgr->info.default_color; + mc->trans_key_type = mgr->info.trans_key_type; + mc->trans_key = mgr->info.trans_key; + mc->trans_enabled = mgr->info.trans_enabled; + mc->alpha_enabled = mgr->info.alpha_enabled; + + mc->manual_upd_display = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; + + mc->manual_update = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && + dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; + } + + /* XXX TODO: Try to get fifomerge working. The problem is that it + * affects both managers, not individually but at the same time. This + * means the change has to be well synchronized. I guess the proper way + * is to have a two step process for fifo merge: + * fifomerge enable: + * 1. disable other planes, leaving one plane enabled + * 2. wait until the planes are disabled on HW + * 3. config merged fifo thresholds, enable fifomerge + * fifomerge disable: + * 1. config unmerged fifo thresholds, disable fifomerge + * 2. wait until fifo changes are in HW + * 3. enable planes + */ + use_fifomerge = false; + + /* Configure overlay fifos */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_dss_device *dssdev; + u32 size; + + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + oc = &dss_cache.overlay_cache[ovl->id]; + + if (!oc->enabled) + continue; + + dssdev = ovl->manager->device; + + size = dispc_get_plane_fifo_size(ovl->id); + if (use_fifomerge) + size *= 3; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_SDI: + case OMAP_DISPLAY_TYPE_VENC: + default_get_overlay_fifo_thresholds(ovl->id, size, + &oc->burst_size, &oc->fifo_low, + &oc->fifo_high); + break; +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: + dsi_get_overlay_fifo_thresholds(ovl->id, size, + &oc->burst_size, &oc->fifo_low, + &oc->fifo_high); + break; +#endif + default: + BUG(); + } + } + + r = 0; + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (!dss_cache.irq_enabled) { + r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN); + dss_cache.irq_enabled = true; + } + configure_dispc(); + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + spin_unlock_irqrestore(&dss_cache.lock, flags); + + return r; +} + +static int dss_check_manager(struct omap_overlay_manager *mgr) +{ + /* OMAP supports only graphics source transparency color key and alpha + * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */ + + if (mgr->info.alpha_enabled && mgr->info.trans_enabled && + mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) + return -EINVAL; + + return 0; +} + +static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info) +{ + int r; + struct omap_overlay_manager_info old_info; + + old_info = mgr->info; + mgr->info = *info; + + r = dss_check_manager(mgr); + if (r) { + mgr->info = old_info; + return r; + } + + mgr->info_dirty = true; + + return 0; +} + +static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info) +{ + *info = mgr->info; +} + +static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager) +{ + ++num_managers; + list_add_tail(&manager->list, &manager_list); +} + +int dss_init_overlay_managers(struct platform_device *pdev) +{ + int i, r; + + spin_lock_init(&dss_cache.lock); + + INIT_LIST_HEAD(&manager_list); + + num_managers = 0; + + for (i = 0; i < 2; ++i) { + struct omap_overlay_manager *mgr; + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + BUG_ON(mgr == NULL); + + switch (i) { + case 0: + mgr->name = "lcd"; + mgr->id = OMAP_DSS_CHANNEL_LCD; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | + OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI; + break; + case 1: + mgr->name = "tv"; + mgr->id = OMAP_DSS_CHANNEL_DIGIT; + mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC; + break; + } + + mgr->set_device = &omap_dss_set_device; + mgr->unset_device = &omap_dss_unset_device; + mgr->apply = &omap_dss_mgr_apply; + mgr->set_manager_info = &omap_dss_mgr_set_info; + mgr->get_manager_info = &omap_dss_mgr_get_info; + mgr->wait_for_go = &dss_mgr_wait_for_go; + + mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; + + dss_overlay_setup_dispc_manager(mgr); + + omap_dss_add_overlay_manager(mgr); + + r = kobject_init_and_add(&mgr->kobj, &manager_ktype, + &pdev->dev.kobj, "manager%d", i); + + if (r) { + DSSERR("failed to create sysfs file\n"); + continue; + } + } + +#ifdef L4_EXAMPLE + { + int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr) + { + DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); + + return 0; + } + + struct omap_overlay_manager *mgr; + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + BUG_ON(mgr == NULL); + + mgr->name = "l4"; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI; + + mgr->set_device = &omap_dss_set_device; + mgr->unset_device = &omap_dss_unset_device; + mgr->apply = &omap_dss_mgr_apply_l4; + mgr->set_manager_info = &omap_dss_mgr_set_info; + mgr->get_manager_info = &omap_dss_mgr_get_info; + + dss_overlay_setup_l4_manager(mgr); + + omap_dss_add_overlay_manager(mgr); + + r = kobject_init_and_add(&mgr->kobj, &manager_ktype, + &pdev->dev.kobj, "managerl4"); + + if (r) + DSSERR("failed to create sysfs file\n"); + } +#endif + + return 0; +} + +void dss_uninit_overlay_managers(struct platform_device *pdev) +{ + struct omap_overlay_manager *mgr; + + while (!list_empty(&manager_list)) { + mgr = list_first_entry(&manager_list, + struct omap_overlay_manager, list); + list_del(&mgr->list); + kobject_del(&mgr->kobj); + kobject_put(&mgr->kobj); + kfree(mgr); + } + + num_managers = 0; +} + +int omap_dss_get_num_overlay_managers(void) +{ + return num_managers; +} +EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); + +struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) +{ + int i = 0; + struct omap_overlay_manager *mgr; + + list_for_each_entry(mgr, &manager_list, list) { + if (i++ == num) + return mgr; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_get_overlay_manager); + diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c new file mode 100644 index 000000000000..b7f9a7339842 --- /dev/null +++ b/drivers/video/omap2/dss/overlay.c @@ -0,0 +1,680 @@ +/* + * linux/drivers/video/omap2/dss/overlay.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "OVERLAY" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/platform_device.h> +#include <linux/delay.h> + +#include <plat/display.h> +#include <plat/cpu.h> + +#include "dss.h" + +static int num_overlays; +static struct list_head overlay_list; + +static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); +} + +static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + ovl->manager ? ovl->manager->name : "<none>"); +} + +static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, + size_t size) +{ + int i, r; + struct omap_overlay_manager *mgr = NULL; + struct omap_overlay_manager *old_mgr; + int len = size; + + if (buf[size-1] == '\n') + --len; + + if (len > 0) { + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + mgr = omap_dss_get_overlay_manager(i); + + if (strncmp(buf, mgr->name, len) == 0) + break; + + mgr = NULL; + } + } + + if (len > 0 && mgr == NULL) + return -EINVAL; + + if (mgr) + DSSDBG("manager %s found\n", mgr->name); + + if (mgr == ovl->manager) + return size; + + old_mgr = ovl->manager; + + /* detach old manager */ + if (old_mgr) { + r = ovl->unset_manager(ovl); + if (r) { + DSSERR("detach failed\n"); + return r; + } + + r = old_mgr->apply(old_mgr); + if (r) + return r; + } + + if (mgr) { + r = ovl->set_manager(ovl, mgr); + if (r) { + DSSERR("Failed to attach overlay\n"); + return r; + } + + r = mgr->apply(mgr); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.width, ovl->info.height); +} + +static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width); +} + +static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.pos_x, ovl->info.pos_y); +} + +static ssize_t overlay_position_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + char *last; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.pos_x = simple_strtoul(buf, &last, 10); + ++last; + if (last - buf >= size) + return -EINVAL; + + info.pos_y = simple_strtoul(last, &last, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.out_width, ovl->info.out_height); +} + +static ssize_t overlay_output_size_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + char *last; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.out_width = simple_strtoul(buf, &last, 10); + ++last; + if (last - buf >= size) + return -EINVAL; + + info.out_height = simple_strtoul(last, &last, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled); +} + +static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, + size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.enabled = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + ovl->info.global_alpha); +} + +static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + /* Video1 plane does not support global alpha + * to always make it 255 completely opaque + */ + if (ovl->id == OMAP_DSS_VIDEO1) + info.global_alpha = 255; + else + info.global_alpha = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +struct overlay_attribute { + struct attribute attr; + ssize_t (*show)(struct omap_overlay *, char *); + ssize_t (*store)(struct omap_overlay *, const char *, size_t); +}; + +#define OVERLAY_ATTR(_name, _mode, _show, _store) \ + struct overlay_attribute overlay_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL); +static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR, + overlay_manager_show, overlay_manager_store); +static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL); +static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL); +static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR, + overlay_position_show, overlay_position_store); +static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR, + overlay_output_size_show, overlay_output_size_store); +static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, + overlay_enabled_show, overlay_enabled_store); +static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, + overlay_global_alpha_show, overlay_global_alpha_store); + +static struct attribute *overlay_sysfs_attrs[] = { + &overlay_attr_name.attr, + &overlay_attr_manager.attr, + &overlay_attr_input_size.attr, + &overlay_attr_screen_width.attr, + &overlay_attr_position.attr, + &overlay_attr_output_size.attr, + &overlay_attr_enabled.attr, + &overlay_attr_global_alpha.attr, + NULL +}; + +static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct omap_overlay *overlay; + struct overlay_attribute *overlay_attr; + + overlay = container_of(kobj, struct omap_overlay, kobj); + overlay_attr = container_of(attr, struct overlay_attribute, attr); + + if (!overlay_attr->show) + return -ENOENT; + + return overlay_attr->show(overlay, buf); +} + +static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ + struct omap_overlay *overlay; + struct overlay_attribute *overlay_attr; + + overlay = container_of(kobj, struct omap_overlay, kobj); + overlay_attr = container_of(attr, struct overlay_attribute, attr); + + if (!overlay_attr->store) + return -ENOENT; + + return overlay_attr->store(overlay, buf, size); +} + +static struct sysfs_ops overlay_sysfs_ops = { + .show = overlay_attr_show, + .store = overlay_attr_store, +}; + +static struct kobj_type overlay_ktype = { + .sysfs_ops = &overlay_sysfs_ops, + .default_attrs = overlay_sysfs_attrs, +}; + +/* Check if overlay parameters are compatible with display */ +int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) +{ + struct omap_overlay_info *info; + u16 outw, outh; + u16 dw, dh; + + if (!dssdev) + return 0; + + if (!ovl->info.enabled) + return 0; + + info = &ovl->info; + + if (info->paddr == 0) { + DSSDBG("check_overlay failed: paddr 0\n"); + return -EINVAL; + } + + dssdev->get_resolution(dssdev, &dw, &dh); + + DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n", + ovl->id, + info->pos_x, info->pos_y, + info->width, info->height, + info->out_width, info->out_height, + dw, dh); + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + outw = info->width; + outh = info->height; + } else { + if (info->out_width == 0) + outw = info->width; + else + outw = info->out_width; + + if (info->out_height == 0) + outh = info->height; + else + outh = info->out_height; + } + + if (dw < info->pos_x + outw) { + DSSDBG("check_overlay failed 1: %d < %d + %d\n", + dw, info->pos_x, outw); + return -EINVAL; + } + + if (dh < info->pos_y + outh) { + DSSDBG("check_overlay failed 2: %d < %d + %d\n", + dh, info->pos_y, outh); + return -EINVAL; + } + + if ((ovl->supported_modes & info->color_mode) == 0) { + DSSERR("overlay doesn't support mode %d\n", info->color_mode); + return -EINVAL; + } + + return 0; +} + +static int dss_ovl_set_overlay_info(struct omap_overlay *ovl, + struct omap_overlay_info *info) +{ + int r; + struct omap_overlay_info old_info; + + old_info = ovl->info; + ovl->info = *info; + + if (ovl->manager) { + r = dss_check_overlay(ovl, ovl->manager->device); + if (r) { + ovl->info = old_info; + return r; + } + } + + ovl->info_dirty = true; + + return 0; +} + +static void dss_ovl_get_overlay_info(struct omap_overlay *ovl, + struct omap_overlay_info *info) +{ + *info = ovl->info; +} + +static int dss_ovl_wait_for_go(struct omap_overlay *ovl) +{ + return dss_mgr_wait_for_go_ovl(ovl); +} + +static int omap_dss_set_manager(struct omap_overlay *ovl, + struct omap_overlay_manager *mgr) +{ + if (!mgr) + return -EINVAL; + + if (ovl->manager) { + DSSERR("overlay '%s' already has a manager '%s'\n", + ovl->name, ovl->manager->name); + return -EINVAL; + } + + if (ovl->info.enabled) { + DSSERR("overlay has to be disabled to change the manager\n"); + return -EINVAL; + } + + ovl->manager = mgr; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + /* XXX: on manual update display, in auto update mode, a bug happens + * here. When an overlay is first enabled on LCD, then it's disabled, + * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT + * errors. Waiting before changing the channel_out fixes it. I'm + * guessing that the overlay is still somehow being used for the LCD, + * but I don't understand how or why. */ + msleep(40); + dispc_set_channel_out(ovl->id, mgr->id); + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + return 0; +} + +static int omap_dss_unset_manager(struct omap_overlay *ovl) +{ + int r; + + if (!ovl->manager) { + DSSERR("failed to detach overlay: manager not set\n"); + return -EINVAL; + } + + if (ovl->info.enabled) { + DSSERR("overlay has to be disabled to unset the manager\n"); + return -EINVAL; + } + + r = ovl->wait_for_go(ovl); + if (r) + return r; + + ovl->manager = NULL; + + return 0; +} + +int omap_dss_get_num_overlays(void) +{ + return num_overlays; +} +EXPORT_SYMBOL(omap_dss_get_num_overlays); + +struct omap_overlay *omap_dss_get_overlay(int num) +{ + int i = 0; + struct omap_overlay *ovl; + + list_for_each_entry(ovl, &overlay_list, list) { + if (i++ == num) + return ovl; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_get_overlay); + +static void omap_dss_add_overlay(struct omap_overlay *overlay) +{ + ++num_overlays; + list_add_tail(&overlay->list, &overlay_list); +} + +static struct omap_overlay *dispc_overlays[3]; + +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) +{ + mgr->num_overlays = 3; + mgr->overlays = dispc_overlays; +} + +#ifdef L4_EXAMPLE +static struct omap_overlay *l4_overlays[1]; +void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr) +{ + mgr->num_overlays = 1; + mgr->overlays = l4_overlays; +} +#endif + +void dss_init_overlays(struct platform_device *pdev) +{ + int i, r; + + INIT_LIST_HEAD(&overlay_list); + + num_overlays = 0; + + for (i = 0; i < 3; ++i) { + struct omap_overlay *ovl; + ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); + + BUG_ON(ovl == NULL); + + switch (i) { + case 0: + ovl->name = "gfx"; + ovl->id = OMAP_DSS_GFX; + ovl->supported_modes = cpu_is_omap34xx() ? + OMAP_DSS_COLOR_GFX_OMAP3 : + OMAP_DSS_COLOR_GFX_OMAP2; + ovl->caps = OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + case 1: + ovl->name = "vid1"; + ovl->id = OMAP_DSS_VIDEO1; + ovl->supported_modes = cpu_is_omap34xx() ? + OMAP_DSS_COLOR_VID1_OMAP3 : + OMAP_DSS_COLOR_VID_OMAP2; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + case 2: + ovl->name = "vid2"; + ovl->id = OMAP_DSS_VIDEO2; + ovl->supported_modes = cpu_is_omap34xx() ? + OMAP_DSS_COLOR_VID2_OMAP3 : + OMAP_DSS_COLOR_VID_OMAP2; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + } + + ovl->set_manager = &omap_dss_set_manager; + ovl->unset_manager = &omap_dss_unset_manager; + ovl->set_overlay_info = &dss_ovl_set_overlay_info; + ovl->get_overlay_info = &dss_ovl_get_overlay_info; + ovl->wait_for_go = &dss_ovl_wait_for_go; + + omap_dss_add_overlay(ovl); + + r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, + &pdev->dev.kobj, "overlay%d", i); + + if (r) { + DSSERR("failed to create sysfs file\n"); + continue; + } + + dispc_overlays[i] = ovl; + } + +#ifdef L4_EXAMPLE + { + struct omap_overlay *ovl; + ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); + + BUG_ON(ovl == NULL); + + ovl->name = "l4"; + ovl->supported_modes = OMAP_DSS_COLOR_RGB24U; + + ovl->set_manager = &omap_dss_set_manager; + ovl->unset_manager = &omap_dss_unset_manager; + ovl->set_overlay_info = &dss_ovl_set_overlay_info; + ovl->get_overlay_info = &dss_ovl_get_overlay_info; + + omap_dss_add_overlay(ovl); + + r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, + &pdev->dev.kobj, "overlayl4"); + + if (r) + DSSERR("failed to create sysfs file\n"); + + l4_overlays[0] = ovl; + } +#endif +} + +/* connect overlays to the new device, if not already connected. if force + * selected, connect always. */ +void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) +{ + int i; + struct omap_overlay_manager *lcd_mgr; + struct omap_overlay_manager *tv_mgr; + struct omap_overlay_manager *mgr = NULL; + + lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); + tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); + + if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { + if (!lcd_mgr->device || force) { + if (lcd_mgr->device) + lcd_mgr->unset_device(lcd_mgr); + lcd_mgr->set_device(lcd_mgr, dssdev); + mgr = lcd_mgr; + } + } + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + if (!tv_mgr->device || force) { + if (tv_mgr->device) + tv_mgr->unset_device(tv_mgr); + tv_mgr->set_device(tv_mgr, dssdev); + mgr = tv_mgr; + } + } + + if (mgr) { + for (i = 0; i < 3; i++) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + if (!ovl->manager || force) { + if (ovl->manager) + omap_dss_unset_manager(ovl); + omap_dss_set_manager(ovl, mgr); + } + } + } +} + +void dss_uninit_overlays(struct platform_device *pdev) +{ + struct omap_overlay *ovl; + + while (!list_empty(&overlay_list)) { + ovl = list_first_entry(&overlay_list, + struct omap_overlay, list); + list_del(&ovl->list); + kobject_del(&ovl->kobj); + kobject_put(&ovl->kobj); + kfree(ovl); + } + + num_overlays = 0; +} + diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c new file mode 100644 index 000000000000..d0b3006ad8a5 --- /dev/null +++ b/drivers/video/omap2/dss/rfbi.c @@ -0,0 +1,1309 @@ +/* + * linux/drivers/video/omap2/dss/rfbi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "RFBI" + +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/kfifo.h> +#include <linux/ktime.h> +#include <linux/hrtimer.h> +#include <linux/seq_file.h> + +#include <plat/display.h> +#include "dss.h" + +/*#define MEASURE_PERF*/ + +#define RFBI_BASE 0x48050800 + +struct rfbi_reg { u16 idx; }; + +#define RFBI_REG(idx) ((const struct rfbi_reg) { idx }) + +#define RFBI_REVISION RFBI_REG(0x0000) +#define RFBI_SYSCONFIG RFBI_REG(0x0010) +#define RFBI_SYSSTATUS RFBI_REG(0x0014) +#define RFBI_CONTROL RFBI_REG(0x0040) +#define RFBI_PIXEL_CNT RFBI_REG(0x0044) +#define RFBI_LINE_NUMBER RFBI_REG(0x0048) +#define RFBI_CMD RFBI_REG(0x004c) +#define RFBI_PARAM RFBI_REG(0x0050) +#define RFBI_DATA RFBI_REG(0x0054) +#define RFBI_READ RFBI_REG(0x0058) +#define RFBI_STATUS RFBI_REG(0x005c) + +#define RFBI_CONFIG(n) RFBI_REG(0x0060 + (n)*0x18) +#define RFBI_ONOFF_TIME(n) RFBI_REG(0x0064 + (n)*0x18) +#define RFBI_CYCLE_TIME(n) RFBI_REG(0x0068 + (n)*0x18) +#define RFBI_DATA_CYCLE1(n) RFBI_REG(0x006c + (n)*0x18) +#define RFBI_DATA_CYCLE2(n) RFBI_REG(0x0070 + (n)*0x18) +#define RFBI_DATA_CYCLE3(n) RFBI_REG(0x0074 + (n)*0x18) + +#define RFBI_VSYNC_WIDTH RFBI_REG(0x0090) +#define RFBI_HSYNC_WIDTH RFBI_REG(0x0094) + +#define RFBI_CMD_FIFO_LEN_BYTES (16 * sizeof(struct update_param)) + +#define REG_FLD_MOD(idx, val, start, end) \ + rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end)) + +/* To work around an RFBI transfer rate limitation */ +#define OMAP_RFBI_RATE_LIMIT 1 + +enum omap_rfbi_cycleformat { + OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0, + OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1, + OMAP_DSS_RFBI_CYCLEFORMAT_3_1 = 2, + OMAP_DSS_RFBI_CYCLEFORMAT_3_2 = 3, +}; + +enum omap_rfbi_datatype { + OMAP_DSS_RFBI_DATATYPE_12 = 0, + OMAP_DSS_RFBI_DATATYPE_16 = 1, + OMAP_DSS_RFBI_DATATYPE_18 = 2, + OMAP_DSS_RFBI_DATATYPE_24 = 3, +}; + +enum omap_rfbi_parallelmode { + OMAP_DSS_RFBI_PARALLELMODE_8 = 0, + OMAP_DSS_RFBI_PARALLELMODE_9 = 1, + OMAP_DSS_RFBI_PARALLELMODE_12 = 2, + OMAP_DSS_RFBI_PARALLELMODE_16 = 3, +}; + +enum update_cmd { + RFBI_CMD_UPDATE = 0, + RFBI_CMD_SYNC = 1, +}; + +static int rfbi_convert_timings(struct rfbi_timings *t); +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div); +static void process_cmd_fifo(void); + +static struct { + void __iomem *base; + + unsigned long l4_khz; + + enum omap_rfbi_datatype datatype; + enum omap_rfbi_parallelmode parallelmode; + + enum omap_rfbi_te_mode te_mode; + int te_enabled; + + void (*framedone_callback)(void *data); + void *framedone_callback_data; + + struct omap_dss_device *dssdev[2]; + + struct kfifo *cmd_fifo; + spinlock_t cmd_lock; + struct completion cmd_done; + atomic_t cmd_fifo_full; + atomic_t cmd_pending; +#ifdef MEASURE_PERF + unsigned perf_bytes; + ktime_t perf_setup_time; + ktime_t perf_start_time; +#endif +} rfbi; + +struct update_region { + u16 x; + u16 y; + u16 w; + u16 h; +}; + +struct update_param { + u8 rfbi_module; + u8 cmd; + + union { + struct update_region r; + struct completion *sync; + } par; +}; + +static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) +{ + __raw_writel(val, rfbi.base + idx.idx); +} + +static inline u32 rfbi_read_reg(const struct rfbi_reg idx) +{ + return __raw_readl(rfbi.base + idx.idx); +} + +static void rfbi_enable_clocks(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +void omap_rfbi_write_command(const void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + const u8 *b = buf; + for (; len; len--) + rfbi_write_reg(RFBI_CMD, *b++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_CMD, *w++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_command); + +void omap_rfbi_read_data(void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + u8 *b = buf; + for (; len; len--) { + rfbi_write_reg(RFBI_READ, 0); + *b++ = rfbi_read_reg(RFBI_READ); + } + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + u16 *w = buf; + BUG_ON(len & ~1); + for (; len; len -= 2) { + rfbi_write_reg(RFBI_READ, 0); + *w++ = rfbi_read_reg(RFBI_READ); + } + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_read_data); + +void omap_rfbi_write_data(const void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + const u8 *b = buf; + for (; len; len--) + rfbi_write_reg(RFBI_PARAM, *b++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_PARAM, *w++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_data); + +void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, + u16 x, u16 y, + u16 w, u16 h) +{ + int start_offset = scr_width * y + x; + int horiz_offset = scr_width - w; + int i; + + rfbi_enable_clocks(1); + + if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { + const u16 __iomem *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + const u8 __iomem *b = (const u8 __iomem *)pd; + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); + ++pd; + } + pd += horiz_offset; + } + } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_24 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { + const u32 __iomem *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + const u8 __iomem *b = (const u8 __iomem *)pd; + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+2)); + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); + ++pd; + } + pd += horiz_offset; + } + } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_16) { + const u16 __iomem *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + rfbi_write_reg(RFBI_PARAM, __raw_readw(pd)); + ++pd; + } + pd += horiz_offset; + } + } else { + BUG(); + } + + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_pixels); + +#ifdef MEASURE_PERF +static void perf_mark_setup(void) +{ + rfbi.perf_setup_time = ktime_get(); +} + +static void perf_mark_start(void) +{ + rfbi.perf_start_time = ktime_get(); +} + +static void perf_show(const char *name) +{ + ktime_t t, setup_time, trans_time; + u32 total_bytes; + u32 setup_us, trans_us, total_us; + + t = ktime_get(); + + setup_time = ktime_sub(rfbi.perf_start_time, rfbi.perf_setup_time); + setup_us = (u32)ktime_to_us(setup_time); + if (setup_us == 0) + setup_us = 1; + + trans_time = ktime_sub(t, rfbi.perf_start_time); + trans_us = (u32)ktime_to_us(trans_time); + if (trans_us == 0) + trans_us = 1; + + total_us = setup_us + trans_us; + + total_bytes = rfbi.perf_bytes; + + DSSINFO("%s update %u us + %u us = %u us (%uHz), %u bytes, " + "%u kbytes/sec\n", + name, + setup_us, + trans_us, + total_us, + 1000*1000 / total_us, + total_bytes, + total_bytes * 1000 / total_us); +} +#else +#define perf_mark_setup() +#define perf_mark_start() +#define perf_show(x) +#endif + +void rfbi_transfer_area(u16 width, u16 height, + void (callback)(void *data), void *data) +{ + u32 l; + + /*BUG_ON(callback == 0);*/ + BUG_ON(rfbi.framedone_callback != NULL); + + DSSDBG("rfbi_transfer_area %dx%d\n", width, height); + + dispc_set_lcd_size(width, height); + + dispc_enable_lcd_out(1); + + rfbi.framedone_callback = callback; + rfbi.framedone_callback_data = data; + + rfbi_enable_clocks(1); + + rfbi_write_reg(RFBI_PIXEL_CNT, width * height); + + l = rfbi_read_reg(RFBI_CONTROL); + l = FLD_MOD(l, 1, 0, 0); /* enable */ + if (!rfbi.te_enabled) + l = FLD_MOD(l, 1, 4, 4); /* ITE */ + + perf_mark_start(); + + rfbi_write_reg(RFBI_CONTROL, l); +} + +static void framedone_callback(void *data, u32 mask) +{ + void (*callback)(void *data); + + DSSDBG("FRAMEDONE\n"); + + perf_show("DISPC"); + + REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0); + + rfbi_enable_clocks(0); + + callback = rfbi.framedone_callback; + rfbi.framedone_callback = NULL; + + /*callback(rfbi.framedone_callback_data);*/ + + atomic_set(&rfbi.cmd_pending, 0); + + process_cmd_fifo(); +} + +#if 1 /* VERBOSE */ +static void rfbi_print_timings(void) +{ + u32 l; + u32 time; + + l = rfbi_read_reg(RFBI_CONFIG(0)); + time = 1000000000 / rfbi.l4_khz; + if (l & (1 << 4)) + time *= 2; + + DSSDBG("Tick time %u ps\n", time); + l = rfbi_read_reg(RFBI_ONOFF_TIME(0)); + DSSDBG("CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " + "REONTIME %d, REOFFTIME %d\n", + l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, + (l >> 20) & 0x0f, (l >> 24) & 0x3f); + + l = rfbi_read_reg(RFBI_CYCLE_TIME(0)); + DSSDBG("WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " + "ACCESSTIME %d\n", + (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, + (l >> 22) & 0x3f); +} +#else +static void rfbi_print_timings(void) {} +#endif + + + + +static u32 extif_clk_period; + +static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) +{ + int bus_tick = extif_clk_period * div; + return (ps + bus_tick - 1) / bus_tick * bus_tick; +} + +static int calc_reg_timing(struct rfbi_timings *t, int div) +{ + t->clk_div = div; + + t->cs_on_time = round_to_extif_ticks(t->cs_on_time, div); + + t->we_on_time = round_to_extif_ticks(t->we_on_time, div); + t->we_off_time = round_to_extif_ticks(t->we_off_time, div); + t->we_cycle_time = round_to_extif_ticks(t->we_cycle_time, div); + + t->re_on_time = round_to_extif_ticks(t->re_on_time, div); + t->re_off_time = round_to_extif_ticks(t->re_off_time, div); + t->re_cycle_time = round_to_extif_ticks(t->re_cycle_time, div); + + t->access_time = round_to_extif_ticks(t->access_time, div); + t->cs_off_time = round_to_extif_ticks(t->cs_off_time, div); + t->cs_pulse_width = round_to_extif_ticks(t->cs_pulse_width, div); + + DSSDBG("[reg]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + DSSDBG("[reg]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + DSSDBG("[reg]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return rfbi_convert_timings(t); +} + +static int calc_extif_timings(struct rfbi_timings *t) +{ + u32 max_clk_div; + int div; + + rfbi_get_clk_info(&extif_clk_period, &max_clk_div); + for (div = 1; div <= max_clk_div; div++) { + if (calc_reg_timing(t, div) == 0) + break; + } + + if (div <= max_clk_div) + return 0; + + DSSERR("can't setup timings\n"); + return -1; +} + + +void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) +{ + int r; + + if (!t->converted) { + r = calc_extif_timings(t); + if (r < 0) + DSSERR("Failed to calc timings\n"); + } + + BUG_ON(!t->converted); + + rfbi_enable_clocks(1); + rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]); + rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]); + + /* TIMEGRANULARITY */ + REG_FLD_MOD(RFBI_CONFIG(rfbi_module), + (t->tim[2] ? 1 : 0), 4, 4); + + rfbi_print_timings(); + rfbi_enable_clocks(0); +} + +static int ps_to_rfbi_ticks(int time, int div) +{ + unsigned long tick_ps; + int ret; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = 1000000000 / (rfbi.l4_khz) * div; + + ret = (time + tick_ps - 1) / tick_ps; + + return ret; +} + +#ifdef OMAP_RFBI_RATE_LIMIT +unsigned long rfbi_get_max_tx_rate(void) +{ + unsigned long l4_rate, dss1_rate; + int min_l4_ticks = 0; + int i; + + /* According to TI this can't be calculated so make the + * adjustments for a couple of known frequencies and warn for + * others. + */ + static const struct { + unsigned long l4_clk; /* HZ */ + unsigned long dss1_clk; /* HZ */ + unsigned long min_l4_ticks; + } ftab[] = { + { 55, 132, 7, }, /* 7.86 MPix/s */ + { 110, 110, 12, }, /* 9.16 MPix/s */ + { 110, 132, 10, }, /* 11 Mpix/s */ + { 120, 120, 10, }, /* 12 Mpix/s */ + { 133, 133, 10, }, /* 13.3 Mpix/s */ + }; + + l4_rate = rfbi.l4_khz / 1000; + dss1_rate = dss_clk_get_rate(DSS_CLK_FCK1) / 1000000; + + for (i = 0; i < ARRAY_SIZE(ftab); i++) { + /* Use a window instead of an exact match, to account + * for different DPLL multiplier / divider pairs. + */ + if (abs(ftab[i].l4_clk - l4_rate) < 3 && + abs(ftab[i].dss1_clk - dss1_rate) < 3) { + min_l4_ticks = ftab[i].min_l4_ticks; + break; + } + } + if (i == ARRAY_SIZE(ftab)) { + /* Can't be sure, return anyway the maximum not + * rate-limited. This might cause a problem only for the + * tearing synchronisation. + */ + DSSERR("can't determine maximum RFBI transfer rate\n"); + return rfbi.l4_khz * 1000; + } + return rfbi.l4_khz * 1000 / min_l4_ticks; +} +#else +int rfbi_get_max_tx_rate(void) +{ + return rfbi.l4_khz * 1000; +} +#endif + +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) +{ + *clk_period = 1000000000 / rfbi.l4_khz; + *max_clk_div = 2; +} + +static int rfbi_convert_timings(struct rfbi_timings *t) +{ + u32 l; + int reon, reoff, weon, weoff, cson, csoff, cs_pulse; + int actim, recyc, wecyc; + int div = t->clk_div; + + if (div <= 0 || div > 2) + return -1; + + /* Make sure that after conversion it still holds that: + * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, + * csoff > cson, csoff >= max(weoff, reoff), actim > reon + */ + weon = ps_to_rfbi_ticks(t->we_on_time, div); + weoff = ps_to_rfbi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + if (weon > 0x0f) + return -1; + if (weoff > 0x3f) + return -1; + + reon = ps_to_rfbi_ticks(t->re_on_time, div); + reoff = ps_to_rfbi_ticks(t->re_off_time, div); + if (reoff <= reon) + reoff = reon + 1; + if (reon > 0x0f) + return -1; + if (reoff > 0x3f) + return -1; + + cson = ps_to_rfbi_ticks(t->cs_on_time, div); + csoff = ps_to_rfbi_ticks(t->cs_off_time, div); + if (csoff <= cson) + csoff = cson + 1; + if (csoff < max(weoff, reoff)) + csoff = max(weoff, reoff); + if (cson > 0x0f) + return -1; + if (csoff > 0x3f) + return -1; + + l = cson; + l |= csoff << 4; + l |= weon << 10; + l |= weoff << 14; + l |= reon << 20; + l |= reoff << 24; + + t->tim[0] = l; + + actim = ps_to_rfbi_ticks(t->access_time, div); + if (actim <= reon) + actim = reon + 1; + if (actim > 0x3f) + return -1; + + wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); + if (wecyc < weoff) + wecyc = weoff; + if (wecyc > 0x3f) + return -1; + + recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); + if (recyc < reoff) + recyc = reoff; + if (recyc > 0x3f) + return -1; + + cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); + if (cs_pulse > 0x3f) + return -1; + + l = wecyc; + l |= recyc << 6; + l |= cs_pulse << 12; + l |= actim << 22; + + t->tim[1] = l; + + t->tim[2] = div - 1; + + t->converted = 1; + + return 0; +} + +/* xxx FIX module selection missing */ +int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int extif_div) +{ + int hs, vs; + int min; + u32 l; + + hs = ps_to_rfbi_ticks(hs_pulse_time, 1); + vs = ps_to_rfbi_ticks(vs_pulse_time, 1); + if (hs < 2) + return -EDOM; + if (mode == OMAP_DSS_RFBI_TE_MODE_2) + min = 2; + else /* OMAP_DSS_RFBI_TE_MODE_1 */ + min = 4; + if (vs < min) + return -EDOM; + if (vs == hs) + return -EINVAL; + rfbi.te_mode = mode; + DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n", + mode, hs, vs, hs_pol_inv, vs_pol_inv); + + rfbi_enable_clocks(1); + rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); + rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); + + l = rfbi_read_reg(RFBI_CONFIG(0)); + if (hs_pol_inv) + l &= ~(1 << 21); + else + l |= 1 << 21; + if (vs_pol_inv) + l &= ~(1 << 20); + else + l |= 1 << 20; + rfbi_enable_clocks(0); + + return 0; +} +EXPORT_SYMBOL(omap_rfbi_setup_te); + +/* xxx FIX module selection missing */ +int omap_rfbi_enable_te(bool enable, unsigned line) +{ + u32 l; + + DSSDBG("te %d line %d mode %d\n", enable, line, rfbi.te_mode); + if (line > (1 << 11) - 1) + return -EINVAL; + + rfbi_enable_clocks(1); + l = rfbi_read_reg(RFBI_CONFIG(0)); + l &= ~(0x3 << 2); + if (enable) { + rfbi.te_enabled = 1; + l |= rfbi.te_mode << 2; + } else + rfbi.te_enabled = 0; + rfbi_write_reg(RFBI_CONFIG(0), l); + rfbi_write_reg(RFBI_LINE_NUMBER, line); + rfbi_enable_clocks(0); + + return 0; +} +EXPORT_SYMBOL(omap_rfbi_enable_te); + +#if 0 +static void rfbi_enable_config(int enable1, int enable2) +{ + u32 l; + int cs = 0; + + if (enable1) + cs |= 1<<0; + if (enable2) + cs |= 1<<1; + + rfbi_enable_clocks(1); + + l = rfbi_read_reg(RFBI_CONTROL); + + l = FLD_MOD(l, cs, 3, 2); + l = FLD_MOD(l, 0, 1, 1); + + rfbi_write_reg(RFBI_CONTROL, l); + + + l = rfbi_read_reg(RFBI_CONFIG(0)); + l = FLD_MOD(l, 0, 3, 2); /* TRIGGERMODE: ITE */ + /*l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ + /*l |= FLD_VAL(0, 8, 7); */ /* L4FORMAT, 1pix/L4 */ + + l = FLD_MOD(l, 0, 16, 16); /* A0POLARITY */ + l = FLD_MOD(l, 1, 20, 20); /* TE_VSYNC_POLARITY */ + l = FLD_MOD(l, 1, 21, 21); /* HSYNCPOLARITY */ + + l = FLD_MOD(l, OMAP_DSS_RFBI_PARALLELMODE_8, 1, 0); + rfbi_write_reg(RFBI_CONFIG(0), l); + + rfbi_enable_clocks(0); +} +#endif + +int rfbi_configure(int rfbi_module, int bpp, int lines) +{ + u32 l; + int cycle1 = 0, cycle2 = 0, cycle3 = 0; + enum omap_rfbi_cycleformat cycleformat; + enum omap_rfbi_datatype datatype; + enum omap_rfbi_parallelmode parallelmode; + + switch (bpp) { + case 12: + datatype = OMAP_DSS_RFBI_DATATYPE_12; + break; + case 16: + datatype = OMAP_DSS_RFBI_DATATYPE_16; + break; + case 18: + datatype = OMAP_DSS_RFBI_DATATYPE_18; + break; + case 24: + datatype = OMAP_DSS_RFBI_DATATYPE_24; + break; + default: + BUG(); + return 1; + } + rfbi.datatype = datatype; + + switch (lines) { + case 8: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_8; + break; + case 9: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_9; + break; + case 12: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_12; + break; + case 16: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_16; + break; + default: + BUG(); + return 1; + } + rfbi.parallelmode = parallelmode; + + if ((bpp % lines) == 0) { + switch (bpp / lines) { + case 1: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_1_1; + break; + case 2: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_2_1; + break; + case 3: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_1; + break; + default: + BUG(); + return 1; + } + } else if ((2 * bpp % lines) == 0) { + if ((2 * bpp / lines) == 3) + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_2; + else { + BUG(); + return 1; + } + } else { + BUG(); + return 1; + } + + switch (cycleformat) { + case OMAP_DSS_RFBI_CYCLEFORMAT_1_1: + cycle1 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_2_1: + cycle1 = lines; + cycle2 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_3_1: + cycle1 = lines; + cycle2 = lines; + cycle3 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_3_2: + cycle1 = lines; + cycle2 = (lines / 2) | ((lines / 2) << 16); + cycle3 = (lines << 16); + break; + } + + rfbi_enable_clocks(1); + + REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */ + + l = 0; + l |= FLD_VAL(parallelmode, 1, 0); + l |= FLD_VAL(0, 3, 2); /* TRIGGERMODE: ITE */ + l |= FLD_VAL(0, 4, 4); /* TIMEGRANULARITY */ + l |= FLD_VAL(datatype, 6, 5); + /* l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ + l |= FLD_VAL(0, 8, 7); /* L4FORMAT, 1pix/L4 */ + l |= FLD_VAL(cycleformat, 10, 9); + l |= FLD_VAL(0, 12, 11); /* UNUSEDBITS */ + l |= FLD_VAL(0, 16, 16); /* A0POLARITY */ + l |= FLD_VAL(0, 17, 17); /* REPOLARITY */ + l |= FLD_VAL(0, 18, 18); /* WEPOLARITY */ + l |= FLD_VAL(0, 19, 19); /* CSPOLARITY */ + l |= FLD_VAL(1, 20, 20); /* TE_VSYNC_POLARITY */ + l |= FLD_VAL(1, 21, 21); /* HSYNCPOLARITY */ + rfbi_write_reg(RFBI_CONFIG(rfbi_module), l); + + rfbi_write_reg(RFBI_DATA_CYCLE1(rfbi_module), cycle1); + rfbi_write_reg(RFBI_DATA_CYCLE2(rfbi_module), cycle2); + rfbi_write_reg(RFBI_DATA_CYCLE3(rfbi_module), cycle3); + + + l = rfbi_read_reg(RFBI_CONTROL); + l = FLD_MOD(l, rfbi_module+1, 3, 2); /* Select CSx */ + l = FLD_MOD(l, 0, 1, 1); /* clear bypass */ + rfbi_write_reg(RFBI_CONTROL, l); + + + DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n", + bpp, lines, cycle1, cycle2, cycle3); + + rfbi_enable_clocks(0); + + return 0; +} +EXPORT_SYMBOL(rfbi_configure); + +static int rfbi_find_display(struct omap_dss_device *dssdev) +{ + if (dssdev == rfbi.dssdev[0]) + return 0; + + if (dssdev == rfbi.dssdev[1]) + return 1; + + BUG(); + return -1; +} + + +static void signal_fifo_waiters(void) +{ + if (atomic_read(&rfbi.cmd_fifo_full) > 0) { + /* DSSDBG("SIGNALING: Fifo not full for waiter!\n"); */ + complete(&rfbi.cmd_done); + atomic_dec(&rfbi.cmd_fifo_full); + } +} + +/* returns 1 for async op, and 0 for sync op */ +static int do_update(struct omap_dss_device *dssdev, struct update_region *upd) +{ + u16 x = upd->x; + u16 y = upd->y; + u16 w = upd->w; + u16 h = upd->h; + + perf_mark_setup(); + + if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + /*dssdev->driver->enable_te(dssdev, 1); */ + dss_setup_partial_planes(dssdev, &x, &y, &w, &h); + } + +#ifdef MEASURE_PERF + rfbi.perf_bytes = w * h * 2; /* XXX always 16bit */ +#endif + + dssdev->driver->setup_update(dssdev, x, y, w, h); + + if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + rfbi_transfer_area(w, h, NULL, NULL); + return 1; + } else { + struct omap_overlay *ovl; + void __iomem *addr; + int scr_width; + + ovl = dssdev->manager->overlays[0]; + scr_width = ovl->info.screen_width; + addr = ovl->info.vaddr; + + omap_rfbi_write_pixels(addr, scr_width, x, y, w, h); + + perf_show("L4"); + + return 0; + } +} + +static void process_cmd_fifo(void) +{ + int len; + struct update_param p; + struct omap_dss_device *dssdev; + unsigned long flags; + + if (atomic_inc_return(&rfbi.cmd_pending) != 1) + return; + + while (true) { + spin_lock_irqsave(rfbi.cmd_fifo->lock, flags); + + len = __kfifo_get(rfbi.cmd_fifo, (unsigned char *)&p, + sizeof(struct update_param)); + if (len == 0) { + DSSDBG("nothing more in fifo\n"); + atomic_set(&rfbi.cmd_pending, 0); + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + break; + } + + /* DSSDBG("fifo full %d\n", rfbi.cmd_fifo_full.counter);*/ + + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + + BUG_ON(len != sizeof(struct update_param)); + BUG_ON(p.rfbi_module > 1); + + dssdev = rfbi.dssdev[p.rfbi_module]; + + if (p.cmd == RFBI_CMD_UPDATE) { + if (do_update(dssdev, &p.par.r)) + break; /* async op */ + } else if (p.cmd == RFBI_CMD_SYNC) { + DSSDBG("Signaling SYNC done!\n"); + complete(p.par.sync); + } else + BUG(); + } + + signal_fifo_waiters(); +} + +static void rfbi_push_cmd(struct update_param *p) +{ + int ret; + + while (1) { + unsigned long flags; + int available; + + spin_lock_irqsave(rfbi.cmd_fifo->lock, flags); + available = RFBI_CMD_FIFO_LEN_BYTES - + __kfifo_len(rfbi.cmd_fifo); + +/* DSSDBG("%d bytes left in fifo\n", available); */ + if (available < sizeof(struct update_param)) { + DSSDBG("Going to wait because FIFO FULL..\n"); + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + atomic_inc(&rfbi.cmd_fifo_full); + wait_for_completion(&rfbi.cmd_done); + /*DSSDBG("Woke up because fifo not full anymore\n");*/ + continue; + } + + ret = __kfifo_put(rfbi.cmd_fifo, (unsigned char *)p, + sizeof(struct update_param)); +/* DSSDBG("pushed %d bytes\n", ret);*/ + + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + + BUG_ON(ret != sizeof(struct update_param)); + + break; + } +} + +static void rfbi_push_update(int rfbi_module, int x, int y, int w, int h) +{ + struct update_param p; + + p.rfbi_module = rfbi_module; + p.cmd = RFBI_CMD_UPDATE; + + p.par.r.x = x; + p.par.r.y = y; + p.par.r.w = w; + p.par.r.h = h; + + DSSDBG("RFBI pushed %d,%d %dx%d\n", x, y, w, h); + + rfbi_push_cmd(&p); + + process_cmd_fifo(); +} + +static void rfbi_push_sync(int rfbi_module, struct completion *sync_comp) +{ + struct update_param p; + + p.rfbi_module = rfbi_module; + p.cmd = RFBI_CMD_SYNC; + p.par.sync = sync_comp; + + rfbi_push_cmd(&p); + + DSSDBG("RFBI sync pushed to cmd fifo\n"); + + process_cmd_fifo(); +} + +void rfbi_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(RFBI_REVISION); + DUMPREG(RFBI_SYSCONFIG); + DUMPREG(RFBI_SYSSTATUS); + DUMPREG(RFBI_CONTROL); + DUMPREG(RFBI_PIXEL_CNT); + DUMPREG(RFBI_LINE_NUMBER); + DUMPREG(RFBI_CMD); + DUMPREG(RFBI_PARAM); + DUMPREG(RFBI_DATA); + DUMPREG(RFBI_READ); + DUMPREG(RFBI_STATUS); + + DUMPREG(RFBI_CONFIG(0)); + DUMPREG(RFBI_ONOFF_TIME(0)); + DUMPREG(RFBI_CYCLE_TIME(0)); + DUMPREG(RFBI_DATA_CYCLE1(0)); + DUMPREG(RFBI_DATA_CYCLE2(0)); + DUMPREG(RFBI_DATA_CYCLE3(0)); + + DUMPREG(RFBI_CONFIG(1)); + DUMPREG(RFBI_ONOFF_TIME(1)); + DUMPREG(RFBI_CYCLE_TIME(1)); + DUMPREG(RFBI_DATA_CYCLE1(1)); + DUMPREG(RFBI_DATA_CYCLE2(1)); + DUMPREG(RFBI_DATA_CYCLE3(1)); + + DUMPREG(RFBI_VSYNC_WIDTH); + DUMPREG(RFBI_HSYNC_WIDTH); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +int rfbi_init(void) +{ + u32 rev; + u32 l; + + spin_lock_init(&rfbi.cmd_lock); + rfbi.cmd_fifo = kfifo_alloc(RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL, + &rfbi.cmd_lock); + if (IS_ERR(rfbi.cmd_fifo)) + return -ENOMEM; + + init_completion(&rfbi.cmd_done); + atomic_set(&rfbi.cmd_fifo_full, 0); + atomic_set(&rfbi.cmd_pending, 0); + + rfbi.base = ioremap(RFBI_BASE, SZ_256); + if (!rfbi.base) { + DSSERR("can't ioremap RFBI\n"); + return -ENOMEM; + } + + rfbi_enable_clocks(1); + + msleep(10); + + rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000; + + /* Enable autoidle and smart-idle */ + l = rfbi_read_reg(RFBI_SYSCONFIG); + l |= (1 << 0) | (2 << 3); + rfbi_write_reg(RFBI_SYSCONFIG, l); + + rev = rfbi_read_reg(RFBI_REVISION); + printk(KERN_INFO "OMAP RFBI rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + rfbi_enable_clocks(0); + + return 0; +} + +void rfbi_exit(void) +{ + DSSDBG("rfbi_exit\n"); + + kfifo_free(rfbi.cmd_fifo); + + iounmap(rfbi.base); +} + +/* struct omap_display support */ +static int rfbi_display_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + int rfbi_module; + + if (w == 0 || h == 0) + return 0; + + rfbi_module = rfbi_find_display(dssdev); + + rfbi_push_update(rfbi_module, x, y, w, h); + + return 0; +} + +static int rfbi_display_sync(struct omap_dss_device *dssdev) +{ + struct completion sync_comp; + int rfbi_module; + + rfbi_module = rfbi_find_display(dssdev); + + init_completion(&sync_comp); + rfbi_push_sync(rfbi_module, &sync_comp); + DSSDBG("Waiting for SYNC to happen...\n"); + wait_for_completion(&sync_comp); + DSSDBG("Released from SYNC\n"); + return 0; +} + +static int rfbi_display_enable_te(struct omap_dss_device *dssdev, bool enable) +{ + dssdev->driver->enable_te(dssdev, enable); + return 0; +} + +static int rfbi_display_enable(struct omap_dss_device *dssdev) +{ + int r; + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + r = omap_dispc_register_isr(framedone_callback, NULL, + DISPC_IRQ_FRAMEDONE); + if (r) { + DSSERR("can't get FRAMEDONE irq\n"); + goto err1; + } + + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_RFBI); + + dispc_set_tft_data_lines(dssdev->ctrl.pixel_size); + + rfbi_configure(dssdev->phy.rfbi.channel, + dssdev->ctrl.pixel_size, + dssdev->phy.rfbi.data_lines); + + rfbi_set_timings(dssdev->phy.rfbi.channel, + &dssdev->ctrl.rfbi_timings); + + + if (dssdev->driver->enable) { + r = dssdev->driver->enable(dssdev); + if (r) + goto err2; + } + + return 0; +err2: + omap_dispc_unregister_isr(framedone_callback, NULL, + DISPC_IRQ_FRAMEDONE); +err1: + omap_dss_stop_device(dssdev); +err0: + return r; +} + +static void rfbi_display_disable(struct omap_dss_device *dssdev) +{ + dssdev->driver->disable(dssdev); + omap_dispc_unregister_isr(framedone_callback, NULL, + DISPC_IRQ_FRAMEDONE); + omap_dss_stop_device(dssdev); +} + +int rfbi_init_display(struct omap_dss_device *dssdev) +{ + dssdev->enable = rfbi_display_enable; + dssdev->disable = rfbi_display_disable; + dssdev->update = rfbi_display_update; + dssdev->sync = rfbi_display_sync; + dssdev->enable_te = rfbi_display_enable_te; + + rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev; + + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; + + return 0; +} diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c new file mode 100644 index 000000000000..c24f307d3da1 --- /dev/null +++ b/drivers/video/omap2/dss/sdi.c @@ -0,0 +1,277 @@ +/* + * linux/drivers/video/omap2/dss/sdi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "SDI" + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <plat/display.h> +#include "dss.h" + +static struct { + bool skip_init; + bool update_enabled; +} sdi; + +static void sdi_basic_init(void) +{ + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); + + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + dispc_set_tft_data_lines(24); + dispc_lcd_enable_signal_polarity(1); +} + +static int sdi_display_enable(struct omap_dss_device *dssdev) +{ + struct omap_video_timings *t = &dssdev->panel.timings; + struct dss_clock_info dss_cinfo; + struct dispc_clock_info dispc_cinfo; + u16 lck_div, pck_div; + unsigned long fck; + unsigned long pck; + int r; + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + DSSERR("dssdev already enabled\n"); + r = -EINVAL; + goto err1; + } + + /* In case of skip_init sdi_init has already enabled the clocks */ + if (!sdi.skip_init) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + sdi_basic_init(); + + /* 15.5.9.1.2 */ + dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; + + dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, + dssdev->panel.acb); + + if (!sdi.skip_init) { + r = dss_calc_clock_div(1, t->pixel_clock * 1000, + &dss_cinfo, &dispc_cinfo); + } else { + r = dss_get_clock_div(&dss_cinfo); + r = dispc_get_clock_div(&dispc_cinfo); + } + + if (r) + goto err2; + + fck = dss_cinfo.fck; + lck_div = dispc_cinfo.lck_div; + pck_div = dispc_cinfo.pck_div; + + pck = fck / lck_div / pck_div / 1000; + + if (pck != t->pixel_clock) { + DSSWARN("Could not find exact pixel clock. Requested %d kHz, " + "got %lu kHz\n", + t->pixel_clock, pck); + + t->pixel_clock = pck; + } + + + dispc_set_lcd_timings(t); + + r = dss_set_clock_div(&dss_cinfo); + if (r) + goto err2; + + r = dispc_set_clock_div(&dispc_cinfo); + if (r) + goto err2; + + if (!sdi.skip_init) { + dss_sdi_init(dssdev->phy.sdi.datapairs); + r = dss_sdi_enable(); + if (r) + goto err1; + mdelay(2); + } + + dispc_enable_lcd_out(1); + + if (dssdev->driver->enable) { + r = dssdev->driver->enable(dssdev); + if (r) + goto err3; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + sdi.skip_init = 0; + + return 0; +err3: + dispc_enable_lcd_out(0); +err2: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +err1: + omap_dss_stop_device(dssdev); +err0: + return r; +} + +static int sdi_display_resume(struct omap_dss_device *dssdev); + +static void sdi_display_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + return; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + if (sdi_display_resume(dssdev)) + return; + + if (dssdev->driver->disable) + dssdev->driver->disable(dssdev); + + dispc_enable_lcd_out(0); + + dss_sdi_disable(); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + omap_dss_stop_device(dssdev); +} + +static int sdi_display_suspend(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EINVAL; + + if (dssdev->driver->suspend) + dssdev->driver->suspend(dssdev); + + dispc_enable_lcd_out(0); + + dss_sdi_disable(); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int sdi_display_resume(struct omap_dss_device *dssdev) +{ + int r; + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) + return -EINVAL; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + r = dss_sdi_enable(); + if (r) + goto err; + mdelay(2); + + dispc_enable_lcd_out(1); + + if (dssdev->driver->resume) + dssdev->driver->resume(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +err: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + return r; +} + +static int sdi_display_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + if (mode == OMAP_DSS_UPDATE_MANUAL) + return -EINVAL; + + if (mode == OMAP_DSS_UPDATE_DISABLED) { + dispc_enable_lcd_out(0); + sdi.update_enabled = 0; + } else { + dispc_enable_lcd_out(1); + sdi.update_enabled = 1; + } + + return 0; +} + +static enum omap_dss_update_mode sdi_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO : + OMAP_DSS_UPDATE_DISABLED; +} + +static void sdi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +int sdi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("SDI init\n"); + + dssdev->enable = sdi_display_enable; + dssdev->disable = sdi_display_disable; + dssdev->suspend = sdi_display_suspend; + dssdev->resume = sdi_display_resume; + dssdev->set_update_mode = sdi_display_set_update_mode; + dssdev->get_update_mode = sdi_display_get_update_mode; + dssdev->get_timings = sdi_get_timings; + + return 0; +} + +int sdi_init(bool skip_init) +{ + /* we store this for first display enable, then clear it */ + sdi.skip_init = skip_init; + + /* + * Enable clocks already here, otherwise there would be a toggle + * of them until sdi_display_enable is called. + */ + if (skip_init) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + return 0; +} + +void sdi_exit(void) +{ +} diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c new file mode 100644 index 000000000000..749a5a0f5be4 --- /dev/null +++ b/drivers/video/omap2/dss/venc.c @@ -0,0 +1,797 @@ +/* + * linux/drivers/video/omap2/dss/venc.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * VENC settings from TI's DSS driver + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "VENC" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/seq_file.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +#include <plat/display.h> +#include <plat/cpu.h> + +#include "dss.h" + +#define VENC_BASE 0x48050C00 + +/* Venc registers */ +#define VENC_REV_ID 0x00 +#define VENC_STATUS 0x04 +#define VENC_F_CONTROL 0x08 +#define VENC_VIDOUT_CTRL 0x10 +#define VENC_SYNC_CTRL 0x14 +#define VENC_LLEN 0x1C +#define VENC_FLENS 0x20 +#define VENC_HFLTR_CTRL 0x24 +#define VENC_CC_CARR_WSS_CARR 0x28 +#define VENC_C_PHASE 0x2C +#define VENC_GAIN_U 0x30 +#define VENC_GAIN_V 0x34 +#define VENC_GAIN_Y 0x38 +#define VENC_BLACK_LEVEL 0x3C +#define VENC_BLANK_LEVEL 0x40 +#define VENC_X_COLOR 0x44 +#define VENC_M_CONTROL 0x48 +#define VENC_BSTAMP_WSS_DATA 0x4C +#define VENC_S_CARR 0x50 +#define VENC_LINE21 0x54 +#define VENC_LN_SEL 0x58 +#define VENC_L21__WC_CTL 0x5C +#define VENC_HTRIGGER_VTRIGGER 0x60 +#define VENC_SAVID__EAVID 0x64 +#define VENC_FLEN__FAL 0x68 +#define VENC_LAL__PHASE_RESET 0x6C +#define VENC_HS_INT_START_STOP_X 0x70 +#define VENC_HS_EXT_START_STOP_X 0x74 +#define VENC_VS_INT_START_X 0x78 +#define VENC_VS_INT_STOP_X__VS_INT_START_Y 0x7C +#define VENC_VS_INT_STOP_Y__VS_EXT_START_X 0x80 +#define VENC_VS_EXT_STOP_X__VS_EXT_START_Y 0x84 +#define VENC_VS_EXT_STOP_Y 0x88 +#define VENC_AVID_START_STOP_X 0x90 +#define VENC_AVID_START_STOP_Y 0x94 +#define VENC_FID_INT_START_X__FID_INT_START_Y 0xA0 +#define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X 0xA4 +#define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y 0xA8 +#define VENC_TVDETGP_INT_START_STOP_X 0xB0 +#define VENC_TVDETGP_INT_START_STOP_Y 0xB4 +#define VENC_GEN_CTRL 0xB8 +#define VENC_OUTPUT_CONTROL 0xC4 +#define VENC_OUTPUT_TEST 0xC8 +#define VENC_DAC_B__DAC_C 0xC8 + +struct venc_config { + u32 f_control; + u32 vidout_ctrl; + u32 sync_ctrl; + u32 llen; + u32 flens; + u32 hfltr_ctrl; + u32 cc_carr_wss_carr; + u32 c_phase; + u32 gain_u; + u32 gain_v; + u32 gain_y; + u32 black_level; + u32 blank_level; + u32 x_color; + u32 m_control; + u32 bstamp_wss_data; + u32 s_carr; + u32 line21; + u32 ln_sel; + u32 l21__wc_ctl; + u32 htrigger_vtrigger; + u32 savid__eavid; + u32 flen__fal; + u32 lal__phase_reset; + u32 hs_int_start_stop_x; + u32 hs_ext_start_stop_x; + u32 vs_int_start_x; + u32 vs_int_stop_x__vs_int_start_y; + u32 vs_int_stop_y__vs_ext_start_x; + u32 vs_ext_stop_x__vs_ext_start_y; + u32 vs_ext_stop_y; + u32 avid_start_stop_x; + u32 avid_start_stop_y; + u32 fid_int_start_x__fid_int_start_y; + u32 fid_int_offset_y__fid_ext_start_x; + u32 fid_ext_start_y__fid_ext_offset_y; + u32 tvdetgp_int_start_stop_x; + u32 tvdetgp_int_start_stop_y; + u32 gen_ctrl; +}; + +/* from TRM */ +static const struct venc_config venc_config_pal_trm = { + .f_control = 0, + .vidout_ctrl = 1, + .sync_ctrl = 0x40, + .llen = 0x35F, /* 863 */ + .flens = 0x270, /* 624 */ + .hfltr_ctrl = 0, + .cc_carr_wss_carr = 0x2F7225ED, + .c_phase = 0, + .gain_u = 0x111, + .gain_v = 0x181, + .gain_y = 0x140, + .black_level = 0x3B, + .blank_level = 0x3B, + .x_color = 0x7, + .m_control = 0x2, + .bstamp_wss_data = 0x3F, + .s_carr = 0x2A098ACB, + .line21 = 0, + .ln_sel = 0x01290015, + .l21__wc_ctl = 0x0000F603, + .htrigger_vtrigger = 0, + + .savid__eavid = 0x06A70108, + .flen__fal = 0x00180270, + .lal__phase_reset = 0x00040135, + .hs_int_start_stop_x = 0x00880358, + .hs_ext_start_stop_x = 0x000F035F, + .vs_int_start_x = 0x01A70000, + .vs_int_stop_x__vs_int_start_y = 0x000001A7, + .vs_int_stop_y__vs_ext_start_x = 0x01AF0000, + .vs_ext_stop_x__vs_ext_start_y = 0x000101AF, + .vs_ext_stop_y = 0x00000025, + .avid_start_stop_x = 0x03530083, + .avid_start_stop_y = 0x026C002E, + .fid_int_start_x__fid_int_start_y = 0x0001008A, + .fid_int_offset_y__fid_ext_start_x = 0x002E0138, + .fid_ext_start_y__fid_ext_offset_y = 0x01380001, + + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00FF0000, +}; + +/* from TRM */ +static const struct venc_config venc_config_ntsc_trm = { + .f_control = 0, + .vidout_ctrl = 1, + .sync_ctrl = 0x8040, + .llen = 0x359, + .flens = 0x20C, + .hfltr_ctrl = 0, + .cc_carr_wss_carr = 0x043F2631, + .c_phase = 0, + .gain_u = 0x102, + .gain_v = 0x16C, + .gain_y = 0x12F, + .black_level = 0x43, + .blank_level = 0x38, + .x_color = 0x7, + .m_control = 0x1, + .bstamp_wss_data = 0x38, + .s_carr = 0x21F07C1F, + .line21 = 0, + .ln_sel = 0x01310011, + .l21__wc_ctl = 0x0000F003, + .htrigger_vtrigger = 0, + + .savid__eavid = 0x069300F4, + .flen__fal = 0x0016020C, + .lal__phase_reset = 0x00060107, + .hs_int_start_stop_x = 0x008E0350, + .hs_ext_start_stop_x = 0x000F0359, + .vs_int_start_x = 0x01A00000, + .vs_int_stop_x__vs_int_start_y = 0x020701A0, + .vs_int_stop_y__vs_ext_start_x = 0x01AC0024, + .vs_ext_stop_x__vs_ext_start_y = 0x020D01AC, + .vs_ext_stop_y = 0x00000006, + .avid_start_stop_x = 0x03480078, + .avid_start_stop_y = 0x02060024, + .fid_int_start_x__fid_int_start_y = 0x0001008A, + .fid_int_offset_y__fid_ext_start_x = 0x01AC0106, + .fid_ext_start_y__fid_ext_offset_y = 0x01060006, + + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00F90000, +}; + +static const struct venc_config venc_config_pal_bdghi = { + .f_control = 0, + .vidout_ctrl = 0, + .sync_ctrl = 0, + .hfltr_ctrl = 0, + .x_color = 0, + .line21 = 0, + .ln_sel = 21, + .htrigger_vtrigger = 0, + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00FB0000, + + .llen = 864-1, + .flens = 625-1, + .cc_carr_wss_carr = 0x2F7625ED, + .c_phase = 0xDF, + .gain_u = 0x111, + .gain_v = 0x181, + .gain_y = 0x140, + .black_level = 0x3e, + .blank_level = 0x3e, + .m_control = 0<<2 | 1<<1, + .bstamp_wss_data = 0x42, + .s_carr = 0x2a098acb, + .l21__wc_ctl = 0<<13 | 0x16<<8 | 0<<0, + .savid__eavid = 0x06A70108, + .flen__fal = 23<<16 | 624<<0, + .lal__phase_reset = 2<<17 | 310<<0, + .hs_int_start_stop_x = 0x00920358, + .hs_ext_start_stop_x = 0x000F035F, + .vs_int_start_x = 0x1a7<<16, + .vs_int_stop_x__vs_int_start_y = 0x000601A7, + .vs_int_stop_y__vs_ext_start_x = 0x01AF0036, + .vs_ext_stop_x__vs_ext_start_y = 0x27101af, + .vs_ext_stop_y = 0x05, + .avid_start_stop_x = 0x03530082, + .avid_start_stop_y = 0x0270002E, + .fid_int_start_x__fid_int_start_y = 0x0005008A, + .fid_int_offset_y__fid_ext_start_x = 0x002E0138, + .fid_ext_start_y__fid_ext_offset_y = 0x01380005, +}; + +const struct omap_video_timings omap_dss_pal_timings = { + .x_res = 720, + .y_res = 574, + .pixel_clock = 13500, + .hsw = 64, + .hfp = 12, + .hbp = 68, + .vsw = 5, + .vfp = 5, + .vbp = 41, +}; +EXPORT_SYMBOL(omap_dss_pal_timings); + +const struct omap_video_timings omap_dss_ntsc_timings = { + .x_res = 720, + .y_res = 482, + .pixel_clock = 13500, + .hsw = 64, + .hfp = 16, + .hbp = 58, + .vsw = 6, + .vfp = 6, + .vbp = 31, +}; +EXPORT_SYMBOL(omap_dss_ntsc_timings); + +static struct { + void __iomem *base; + struct mutex venc_lock; + u32 wss_data; + struct regulator *vdda_dac_reg; +} venc; + +static inline void venc_write_reg(int idx, u32 val) +{ + __raw_writel(val, venc.base + idx); +} + +static inline u32 venc_read_reg(int idx) +{ + u32 l = __raw_readl(venc.base + idx); + return l; +} + +static void venc_write_config(const struct venc_config *config) +{ + DSSDBG("write venc conf\n"); + + venc_write_reg(VENC_LLEN, config->llen); + venc_write_reg(VENC_FLENS, config->flens); + venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr); + venc_write_reg(VENC_C_PHASE, config->c_phase); + venc_write_reg(VENC_GAIN_U, config->gain_u); + venc_write_reg(VENC_GAIN_V, config->gain_v); + venc_write_reg(VENC_GAIN_Y, config->gain_y); + venc_write_reg(VENC_BLACK_LEVEL, config->black_level); + venc_write_reg(VENC_BLANK_LEVEL, config->blank_level); + venc_write_reg(VENC_M_CONTROL, config->m_control); + venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | + venc.wss_data); + venc_write_reg(VENC_S_CARR, config->s_carr); + venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl); + venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid); + venc_write_reg(VENC_FLEN__FAL, config->flen__fal); + venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset); + venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x); + venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x); + venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x); + venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y, + config->vs_int_stop_x__vs_int_start_y); + venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X, + config->vs_int_stop_y__vs_ext_start_x); + venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y, + config->vs_ext_stop_x__vs_ext_start_y); + venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y); + venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x); + venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y); + venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y, + config->fid_int_start_x__fid_int_start_y); + venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X, + config->fid_int_offset_y__fid_ext_start_x); + venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y, + config->fid_ext_start_y__fid_ext_offset_y); + + venc_write_reg(VENC_DAC_B__DAC_C, venc_read_reg(VENC_DAC_B__DAC_C)); + venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl); + venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl); + venc_write_reg(VENC_X_COLOR, config->x_color); + venc_write_reg(VENC_LINE21, config->line21); + venc_write_reg(VENC_LN_SEL, config->ln_sel); + venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger); + venc_write_reg(VENC_TVDETGP_INT_START_STOP_X, + config->tvdetgp_int_start_stop_x); + venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y, + config->tvdetgp_int_start_stop_y); + venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl); + venc_write_reg(VENC_F_CONTROL, config->f_control); + venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl); +} + +static void venc_reset(void) +{ + int t = 1000; + + venc_write_reg(VENC_F_CONTROL, 1<<8); + while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) { + if (--t == 0) { + DSSERR("Failed to reset venc\n"); + return; + } + } + + /* the magical sleep that makes things work */ + msleep(20); +} + +static void venc_enable_clocks(int enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | + DSS_CLK_96M); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | + DSS_CLK_96M); +} + +static const struct venc_config *venc_timings_to_config( + struct omap_video_timings *timings) +{ + if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0) + return &venc_config_pal_trm; + + if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0) + return &venc_config_ntsc_trm; + + BUG(); +} + + + + + +/* driver */ +static int venc_panel_probe(struct omap_dss_device *dssdev) +{ + dssdev->panel.timings = omap_dss_pal_timings; + + return 0; +} + +static void venc_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int venc_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + /* wait couple of vsyncs until enabling the LCD */ + msleep(50); + + if (dssdev->platform_enable) + r = dssdev->platform_enable(dssdev); + + return r; +} + +static void venc_panel_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + /* wait at least 5 vsyncs after disabling the LCD */ + + msleep(100); +} + +static int venc_panel_suspend(struct omap_dss_device *dssdev) +{ + venc_panel_disable(dssdev); + return 0; +} + +static int venc_panel_resume(struct omap_dss_device *dssdev) +{ + return venc_panel_enable(dssdev); +} + +static struct omap_dss_driver venc_driver = { + .probe = venc_panel_probe, + .remove = venc_panel_remove, + + .enable = venc_panel_enable, + .disable = venc_panel_disable, + .suspend = venc_panel_suspend, + .resume = venc_panel_resume, + + .driver = { + .name = "venc", + .owner = THIS_MODULE, + }, +}; +/* driver end */ + + + +int venc_init(struct platform_device *pdev) +{ + u8 rev_id; + + mutex_init(&venc.venc_lock); + + venc.wss_data = 0; + + venc.base = ioremap(VENC_BASE, SZ_1K); + if (!venc.base) { + DSSERR("can't ioremap VENC\n"); + return -ENOMEM; + } + + venc.vdda_dac_reg = regulator_get(&pdev->dev, "vdda_dac"); + if (IS_ERR(venc.vdda_dac_reg)) { + iounmap(venc.base); + DSSERR("can't get VDDA_DAC regulator\n"); + return PTR_ERR(venc.vdda_dac_reg); + } + + venc_enable_clocks(1); + + rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); + printk(KERN_INFO "OMAP VENC rev %d\n", rev_id); + + venc_enable_clocks(0); + + return omap_dss_register_driver(&venc_driver); +} + +void venc_exit(void) +{ + omap_dss_unregister_driver(&venc_driver); + + regulator_put(venc.vdda_dac_reg); + + iounmap(venc.base); +} + +static void venc_power_on(struct omap_dss_device *dssdev) +{ + u32 l; + + venc_enable_clocks(1); + + venc_reset(); + venc_write_config(venc_timings_to_config(&dssdev->panel.timings)); + + dss_set_venc_output(dssdev->phy.venc.type); + dss_set_dac_pwrdn_bgz(1); + + l = 0; + + if (dssdev->phy.venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE) + l |= 1 << 1; + else /* S-Video */ + l |= (1 << 0) | (1 << 2); + + if (dssdev->phy.venc.invert_polarity == false) + l |= 1 << 3; + + venc_write_reg(VENC_OUTPUT_CONTROL, l); + + dispc_set_digit_size(dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res/2); + + regulator_enable(venc.vdda_dac_reg); + + if (dssdev->platform_enable) + dssdev->platform_enable(dssdev); + + dispc_enable_digit_out(1); +} + +static void venc_power_off(struct omap_dss_device *dssdev) +{ + venc_write_reg(VENC_OUTPUT_CONTROL, 0); + dss_set_dac_pwrdn_bgz(0); + + dispc_enable_digit_out(0); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + regulator_disable(venc.vdda_dac_reg); + + venc_enable_clocks(0); +} + +static int venc_enable_display(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("venc_enable_display\n"); + + mutex_lock(&venc.venc_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + goto err; + } + + venc_power_on(dssdev); + + venc.wss_data = 0; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; +err: + mutex_unlock(&venc.venc_lock); + + return r; +} + +static void venc_disable_display(struct omap_dss_device *dssdev) +{ + DSSDBG("venc_disable_display\n"); + + mutex_lock(&venc.venc_lock); + + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + goto end; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) { + /* suspended is the same as disabled with venc */ + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + goto end; + } + + venc_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +end: + mutex_unlock(&venc.venc_lock); +} + +static int venc_display_suspend(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("venc_display_suspend\n"); + + mutex_lock(&venc.venc_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + goto err; + } + + venc_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; +err: + mutex_unlock(&venc.venc_lock); + + return r; +} + +static int venc_display_resume(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("venc_display_resume\n"); + + mutex_lock(&venc.venc_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + r = -EINVAL; + goto err; + } + + venc_power_on(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; +err: + mutex_unlock(&venc.venc_lock); + + return r; +} + +static void venc_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static void venc_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("venc_set_timings\n"); + + /* Reset WSS data when the TV standard changes. */ + if (memcmp(&dssdev->panel.timings, timings, sizeof(*timings))) + venc.wss_data = 0; + + dssdev->panel.timings = *timings; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* turn the venc off and on to get new timings to use */ + venc_disable_display(dssdev); + venc_enable_display(dssdev); + } +} + +static int venc_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("venc_check_timings\n"); + + if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0) + return 0; + + if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0) + return 0; + + return -EINVAL; +} + +static u32 venc_get_wss(struct omap_dss_device *dssdev) +{ + /* Invert due to VENC_L21_WC_CTL:INV=1 */ + return (venc.wss_data >> 8) ^ 0xfffff; +} + +static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss) +{ + const struct venc_config *config; + + DSSDBG("venc_set_wss\n"); + + mutex_lock(&venc.venc_lock); + + config = venc_timings_to_config(&dssdev->panel.timings); + + /* Invert due to VENC_L21_WC_CTL:INV=1 */ + venc.wss_data = (wss ^ 0xfffff) << 8; + + venc_enable_clocks(1); + + venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | + venc.wss_data); + + venc_enable_clocks(0); + + mutex_unlock(&venc.venc_lock); + + return 0; +} + +static enum omap_dss_update_mode venc_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return OMAP_DSS_UPDATE_AUTO; + else + return OMAP_DSS_UPDATE_DISABLED; +} + +int venc_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("init_display\n"); + + dssdev->enable = venc_enable_display; + dssdev->disable = venc_disable_display; + dssdev->suspend = venc_display_suspend; + dssdev->resume = venc_display_resume; + dssdev->get_timings = venc_get_timings; + dssdev->set_timings = venc_set_timings; + dssdev->check_timings = venc_check_timings; + dssdev->get_wss = venc_get_wss; + dssdev->set_wss = venc_set_wss; + dssdev->get_update_mode = venc_display_get_update_mode; + + return 0; +} + +void venc_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) + + venc_enable_clocks(1); + + DUMPREG(VENC_F_CONTROL); + DUMPREG(VENC_VIDOUT_CTRL); + DUMPREG(VENC_SYNC_CTRL); + DUMPREG(VENC_LLEN); + DUMPREG(VENC_FLENS); + DUMPREG(VENC_HFLTR_CTRL); + DUMPREG(VENC_CC_CARR_WSS_CARR); + DUMPREG(VENC_C_PHASE); + DUMPREG(VENC_GAIN_U); + DUMPREG(VENC_GAIN_V); + DUMPREG(VENC_GAIN_Y); + DUMPREG(VENC_BLACK_LEVEL); + DUMPREG(VENC_BLANK_LEVEL); + DUMPREG(VENC_X_COLOR); + DUMPREG(VENC_M_CONTROL); + DUMPREG(VENC_BSTAMP_WSS_DATA); + DUMPREG(VENC_S_CARR); + DUMPREG(VENC_LINE21); + DUMPREG(VENC_LN_SEL); + DUMPREG(VENC_L21__WC_CTL); + DUMPREG(VENC_HTRIGGER_VTRIGGER); + DUMPREG(VENC_SAVID__EAVID); + DUMPREG(VENC_FLEN__FAL); + DUMPREG(VENC_LAL__PHASE_RESET); + DUMPREG(VENC_HS_INT_START_STOP_X); + DUMPREG(VENC_HS_EXT_START_STOP_X); + DUMPREG(VENC_VS_INT_START_X); + DUMPREG(VENC_VS_INT_STOP_X__VS_INT_START_Y); + DUMPREG(VENC_VS_INT_STOP_Y__VS_EXT_START_X); + DUMPREG(VENC_VS_EXT_STOP_X__VS_EXT_START_Y); + DUMPREG(VENC_VS_EXT_STOP_Y); + DUMPREG(VENC_AVID_START_STOP_X); + DUMPREG(VENC_AVID_START_STOP_Y); + DUMPREG(VENC_FID_INT_START_X__FID_INT_START_Y); + DUMPREG(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X); + DUMPREG(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y); + DUMPREG(VENC_TVDETGP_INT_START_STOP_X); + DUMPREG(VENC_TVDETGP_INT_START_STOP_Y); + DUMPREG(VENC_GEN_CTRL); + DUMPREG(VENC_OUTPUT_CONTROL); + DUMPREG(VENC_OUTPUT_TEST); + + venc_enable_clocks(0); + +#undef DUMPREG +} diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig new file mode 100644 index 000000000000..bb694cc52a50 --- /dev/null +++ b/drivers/video/omap2/omapfb/Kconfig @@ -0,0 +1,37 @@ +menuconfig FB_OMAP2 + tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)" + depends on FB && OMAP2_DSS + + select OMAP2_VRAM + select OMAP2_VRFB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Frame buffer driver for OMAP2/3 based boards. + +config FB_OMAP2_DEBUG_SUPPORT + bool "Debug support for OMAP2/3 FB" + default y + depends on FB_OMAP2 + help + Support for debug output. You have to enable the actual printing + with debug module parameter. + +config FB_OMAP2_FORCE_AUTO_UPDATE + bool "Force main display to automatic update mode" + depends on FB_OMAP2 + help + Forces main display to automatic update mode (if possible), + and also enables tearsync (if possible). By default + displays that support manual update are started in manual + update mode. + +config FB_OMAP2_NUM_FBS + int "Number of framebuffers" + range 1 10 + default 3 + depends on FB_OMAP2 + help + Select the number of framebuffers created. OMAP2/3 has 3 overlays + so normally this would be 3. diff --git a/drivers/video/omap2/omapfb/Makefile b/drivers/video/omap2/omapfb/Makefile new file mode 100644 index 000000000000..51c2e00d9bf8 --- /dev/null +++ b/drivers/video/omap2/omapfb/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_FB_OMAP2) += omapfb.o +omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c new file mode 100644 index 000000000000..4c4bafdfaa43 --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -0,0 +1,755 @@ +/* + * linux/drivers/video/omap2/omapfb-ioctl.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/fb.h> +#include <linux/device.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/mm.h> +#include <linux/omapfb.h> +#include <linux/vmalloc.h> + +#include <plat/display.h> +#include <plat/vrfb.h> +#include <plat/vram.h> + +#include "omapfb.h" + +static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_overlay *ovl; + struct omap_overlay_info info; + int r = 0; + + DBG("omapfb_setup_plane\n"); + + if (ofbi->num_overlays != 1) { + r = -EINVAL; + goto out; + } + + /* XXX uses only the first overlay */ + ovl = ofbi->overlays[0]; + + if (pi->enabled && !ofbi->region.size) { + /* + * This plane's memory was freed, can't enable it + * until it's reallocated. + */ + r = -EINVAL; + goto out; + } + + ovl->get_overlay_info(ovl, &info); + + info.pos_x = pi->pos_x; + info.pos_y = pi->pos_y; + info.out_width = pi->out_width; + info.out_height = pi->out_height; + info.enabled = pi->enabled; + + r = ovl->set_overlay_info(ovl, &info); + if (r) + goto out; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + goto out; + } + +out: + if (r) + dev_err(fbdev->dev, "setup_plane failed\n"); + return r; +} + +static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + + if (ofbi->num_overlays != 1) { + memset(pi, 0, sizeof(*pi)); + } else { + struct omap_overlay_info *ovli; + struct omap_overlay *ovl; + + ovl = ofbi->overlays[0]; + ovli = &ovl->info; + + pi->pos_x = ovli->pos_x; + pi->pos_y = ovli->pos_y; + pi->enabled = ovli->enabled; + pi->channel_out = 0; /* xxx */ + pi->mirror = 0; + pi->out_width = ovli->out_width; + pi->out_height = ovli->out_height; + } + + return 0; +} + +static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb2_mem_region *rg; + int r, i; + size_t size; + + if (mi->type > OMAPFB_MEMTYPE_MAX) + return -EINVAL; + + size = PAGE_ALIGN(mi->size); + + rg = &ofbi->region; + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->info.enabled) + return -EBUSY; + } + + if (rg->size != size || rg->type != mi->type) { + r = omapfb_realloc_fbmem(fbi, size, mi->type); + if (r) { + dev_err(fbdev->dev, "realloc fbmem failed\n"); + return r; + } + } + + return 0; +} + +static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_mem_region *rg; + + rg = &ofbi->region; + memset(mi, 0, sizeof(*mi)); + + mi->size = rg->size; + mi->type = rg->type; + + return 0; +} + +static int omapfb_update_window_nolock(struct fb_info *fbi, + u32 x, u32 y, u32 w, u32 h) +{ + struct omap_dss_device *display = fb2display(fbi); + u16 dw, dh; + + if (!display) + return 0; + + if (w == 0 || h == 0) + return 0; + + display->get_resolution(display, &dw, &dh); + + if (x + w > dw || y + h > dh) + return -EINVAL; + + return display->update(display, x, y, w, h); +} + +/* This function is exported for SGX driver use */ +int omapfb_update_window(struct fb_info *fbi, + u32 x, u32 y, u32 w, u32 h) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + int r; + + omapfb_lock(fbdev); + lock_fb_info(fbi); + + r = omapfb_update_window_nolock(fbi, x, y, w, h); + + unlock_fb_info(fbi); + omapfb_unlock(fbdev); + + return r; +} +EXPORT_SYMBOL(omapfb_update_window); + +static int omapfb_set_update_mode(struct fb_info *fbi, + enum omapfb_update_mode mode) +{ + struct omap_dss_device *display = fb2display(fbi); + enum omap_dss_update_mode um; + int r; + + if (!display || !display->set_update_mode) + return -EINVAL; + + switch (mode) { + case OMAPFB_UPDATE_DISABLED: + um = OMAP_DSS_UPDATE_DISABLED; + break; + + case OMAPFB_AUTO_UPDATE: + um = OMAP_DSS_UPDATE_AUTO; + break; + + case OMAPFB_MANUAL_UPDATE: + um = OMAP_DSS_UPDATE_MANUAL; + break; + + default: + return -EINVAL; + } + + r = display->set_update_mode(display, um); + + return r; +} + +static int omapfb_get_update_mode(struct fb_info *fbi, + enum omapfb_update_mode *mode) +{ + struct omap_dss_device *display = fb2display(fbi); + enum omap_dss_update_mode m; + + if (!display || !display->get_update_mode) + return -EINVAL; + + m = display->get_update_mode(display); + + switch (m) { + case OMAP_DSS_UPDATE_DISABLED: + *mode = OMAPFB_UPDATE_DISABLED; + break; + case OMAP_DSS_UPDATE_AUTO: + *mode = OMAPFB_AUTO_UPDATE; + break; + case OMAP_DSS_UPDATE_MANUAL: + *mode = OMAPFB_MANUAL_UPDATE; + break; + default: + BUG(); + } + + return 0; +} + +/* XXX this color key handling is a hack... */ +static struct omapfb_color_key omapfb_color_keys[2]; + +static int _omapfb_set_color_key(struct omap_overlay_manager *mgr, + struct omapfb_color_key *ck) +{ + struct omap_overlay_manager_info info; + enum omap_dss_trans_key_type kt; + int r; + + mgr->get_manager_info(mgr, &info); + + if (ck->key_type == OMAPFB_COLOR_KEY_DISABLED) { + info.trans_enabled = false; + omapfb_color_keys[mgr->id] = *ck; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + + return r; + } + + switch (ck->key_type) { + case OMAPFB_COLOR_KEY_GFX_DST: + kt = OMAP_DSS_COLOR_KEY_GFX_DST; + break; + case OMAPFB_COLOR_KEY_VID_SRC: + kt = OMAP_DSS_COLOR_KEY_VID_SRC; + break; + default: + return -EINVAL; + } + + info.default_color = ck->background; + info.trans_key = ck->trans_key; + info.trans_key_type = kt; + info.trans_enabled = true; + + omapfb_color_keys[mgr->id] = *ck; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + + return r; +} + +static int omapfb_set_color_key(struct fb_info *fbi, + struct omapfb_color_key *ck) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + int r; + int i; + struct omap_overlay_manager *mgr = NULL; + + omapfb_lock(fbdev); + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->manager) { + mgr = ofbi->overlays[i]->manager; + break; + } + } + + if (!mgr) { + r = -EINVAL; + goto err; + } + + r = _omapfb_set_color_key(mgr, ck); +err: + omapfb_unlock(fbdev); + + return r; +} + +static int omapfb_get_color_key(struct fb_info *fbi, + struct omapfb_color_key *ck) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_overlay_manager *mgr = NULL; + int r = 0; + int i; + + omapfb_lock(fbdev); + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->manager) { + mgr = ofbi->overlays[i]->manager; + break; + } + } + + if (!mgr) { + r = -EINVAL; + goto err; + } + + *ck = omapfb_color_keys[mgr->id]; +err: + omapfb_unlock(fbdev); + + return r; +} + +static int omapfb_memory_read(struct fb_info *fbi, + struct omapfb_memory_read *mr) +{ + struct omap_dss_device *display = fb2display(fbi); + void *buf; + int r; + + if (!display || !display->memory_read) + return -ENOENT; + + if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size)) + return -EFAULT; + + if (mr->w * mr->h * 3 > mr->buffer_size) + return -EINVAL; + + buf = vmalloc(mr->buffer_size); + if (!buf) { + DBG("vmalloc failed\n"); + return -ENOMEM; + } + + r = display->memory_read(display, buf, mr->buffer_size, + mr->x, mr->y, mr->w, mr->h); + + if (r > 0) { + if (copy_to_user(mr->buffer, buf, mr->buffer_size)) + r = -EFAULT; + } + + vfree(buf); + + return r; +} + +static int omapfb_get_ovl_colormode(struct omapfb2_device *fbdev, + struct omapfb_ovl_colormode *mode) +{ + int ovl_idx = mode->overlay_idx; + int mode_idx = mode->mode_idx; + struct omap_overlay *ovl; + enum omap_color_mode supported_modes; + struct fb_var_screeninfo var; + int i; + + if (ovl_idx >= fbdev->num_overlays) + return -ENODEV; + ovl = fbdev->overlays[ovl_idx]; + supported_modes = ovl->supported_modes; + + mode_idx = mode->mode_idx; + + for (i = 0; i < sizeof(supported_modes) * 8; i++) { + if (!(supported_modes & (1 << i))) + continue; + /* + * It's possible that the FB doesn't support a mode + * that is supported by the overlay, so call the + * following here. + */ + if (dss_mode_to_fb_mode(1 << i, &var) < 0) + continue; + + mode_idx--; + if (mode_idx < 0) + break; + } + + if (i == sizeof(supported_modes) * 8) + return -ENOENT; + + mode->bits_per_pixel = var.bits_per_pixel; + mode->nonstd = var.nonstd; + mode->red = var.red; + mode->green = var.green; + mode->blue = var.blue; + mode->transp = var.transp; + + return 0; +} + +static int omapfb_wait_for_go(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + int r = 0; + int i; + + for (i = 0; i < ofbi->num_overlays; ++i) { + struct omap_overlay *ovl = ofbi->overlays[i]; + r = ovl->wait_for_go(ovl); + if (r) + break; + } + + return r; +} + +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + + union { + struct omapfb_update_window_old uwnd_o; + struct omapfb_update_window uwnd; + struct omapfb_plane_info plane_info; + struct omapfb_caps caps; + struct omapfb_mem_info mem_info; + struct omapfb_color_key color_key; + struct omapfb_ovl_colormode ovl_colormode; + enum omapfb_update_mode update_mode; + int test_num; + struct omapfb_memory_read memory_read; + struct omapfb_vram_info vram_info; + struct omapfb_tearsync_info tearsync_info; + } p; + + int r = 0; + + switch (cmd) { + case OMAPFB_SYNC_GFX: + DBG("ioctl SYNC_GFX\n"); + if (!display || !display->sync) { + /* DSS1 never returns an error here, so we neither */ + /*r = -EINVAL;*/ + break; + } + + r = display->sync(display); + break; + + case OMAPFB_UPDATE_WINDOW_OLD: + DBG("ioctl UPDATE_WINDOW_OLD\n"); + if (!display || !display->update) { + r = -EINVAL; + break; + } + + if (copy_from_user(&p.uwnd_o, + (void __user *)arg, + sizeof(p.uwnd_o))) { + r = -EFAULT; + break; + } + + r = omapfb_update_window_nolock(fbi, p.uwnd_o.x, p.uwnd_o.y, + p.uwnd_o.width, p.uwnd_o.height); + break; + + case OMAPFB_UPDATE_WINDOW: + DBG("ioctl UPDATE_WINDOW\n"); + if (!display || !display->update) { + r = -EINVAL; + break; + } + + if (copy_from_user(&p.uwnd, (void __user *)arg, + sizeof(p.uwnd))) { + r = -EFAULT; + break; + } + + r = omapfb_update_window_nolock(fbi, p.uwnd.x, p.uwnd.y, + p.uwnd.width, p.uwnd.height); + break; + + case OMAPFB_SETUP_PLANE: + DBG("ioctl SETUP_PLANE\n"); + if (copy_from_user(&p.plane_info, (void __user *)arg, + sizeof(p.plane_info))) + r = -EFAULT; + else + r = omapfb_setup_plane(fbi, &p.plane_info); + break; + + case OMAPFB_QUERY_PLANE: + DBG("ioctl QUERY_PLANE\n"); + r = omapfb_query_plane(fbi, &p.plane_info); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.plane_info, + sizeof(p.plane_info))) + r = -EFAULT; + break; + + case OMAPFB_SETUP_MEM: + DBG("ioctl SETUP_MEM\n"); + if (copy_from_user(&p.mem_info, (void __user *)arg, + sizeof(p.mem_info))) + r = -EFAULT; + else + r = omapfb_setup_mem(fbi, &p.mem_info); + break; + + case OMAPFB_QUERY_MEM: + DBG("ioctl QUERY_MEM\n"); + r = omapfb_query_mem(fbi, &p.mem_info); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.mem_info, + sizeof(p.mem_info))) + r = -EFAULT; + break; + + case OMAPFB_GET_CAPS: + DBG("ioctl GET_CAPS\n"); + if (!display) { + r = -EINVAL; + break; + } + + memset(&p.caps, 0, sizeof(p.caps)); + if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) + p.caps.ctrl |= OMAPFB_CAPS_MANUAL_UPDATE; + if (display->caps & OMAP_DSS_DISPLAY_CAP_TEAR_ELIM) + p.caps.ctrl |= OMAPFB_CAPS_TEARSYNC; + + if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps))) + r = -EFAULT; + break; + + case OMAPFB_GET_OVERLAY_COLORMODE: + DBG("ioctl GET_OVERLAY_COLORMODE\n"); + if (copy_from_user(&p.ovl_colormode, (void __user *)arg, + sizeof(p.ovl_colormode))) { + r = -EFAULT; + break; + } + r = omapfb_get_ovl_colormode(fbdev, &p.ovl_colormode); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.ovl_colormode, + sizeof(p.ovl_colormode))) + r = -EFAULT; + break; + + case OMAPFB_SET_UPDATE_MODE: + DBG("ioctl SET_UPDATE_MODE\n"); + if (get_user(p.update_mode, (int __user *)arg)) + r = -EFAULT; + else + r = omapfb_set_update_mode(fbi, p.update_mode); + break; + + case OMAPFB_GET_UPDATE_MODE: + DBG("ioctl GET_UPDATE_MODE\n"); + r = omapfb_get_update_mode(fbi, &p.update_mode); + if (r) + break; + if (put_user(p.update_mode, + (enum omapfb_update_mode __user *)arg)) + r = -EFAULT; + break; + + case OMAPFB_SET_COLOR_KEY: + DBG("ioctl SET_COLOR_KEY\n"); + if (copy_from_user(&p.color_key, (void __user *)arg, + sizeof(p.color_key))) + r = -EFAULT; + else + r = omapfb_set_color_key(fbi, &p.color_key); + break; + + case OMAPFB_GET_COLOR_KEY: + DBG("ioctl GET_COLOR_KEY\n"); + r = omapfb_get_color_key(fbi, &p.color_key); + if (r) + break; + if (copy_to_user((void __user *)arg, &p.color_key, + sizeof(p.color_key))) + r = -EFAULT; + break; + + case OMAPFB_WAITFORVSYNC: + DBG("ioctl WAITFORVSYNC\n"); + if (!display) { + r = -EINVAL; + break; + } + + r = display->wait_vsync(display); + break; + + case OMAPFB_WAITFORGO: + DBG("ioctl WAITFORGO\n"); + if (!display) { + r = -EINVAL; + break; + } + + r = omapfb_wait_for_go(fbi); + break; + + /* LCD and CTRL tests do the same thing for backward + * compatibility */ + case OMAPFB_LCD_TEST: + DBG("ioctl LCD_TEST\n"); + if (get_user(p.test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!display || !display->run_test) { + r = -EINVAL; + break; + } + + r = display->run_test(display, p.test_num); + + break; + + case OMAPFB_CTRL_TEST: + DBG("ioctl CTRL_TEST\n"); + if (get_user(p.test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!display || !display->run_test) { + r = -EINVAL; + break; + } + + r = display->run_test(display, p.test_num); + + break; + + case OMAPFB_MEMORY_READ: + DBG("ioctl MEMORY_READ\n"); + + if (copy_from_user(&p.memory_read, (void __user *)arg, + sizeof(p.memory_read))) { + r = -EFAULT; + break; + } + + r = omapfb_memory_read(fbi, &p.memory_read); + + break; + + case OMAPFB_GET_VRAM_INFO: { + unsigned long vram, free, largest; + + DBG("ioctl GET_VRAM_INFO\n"); + + omap_vram_get_info(&vram, &free, &largest); + p.vram_info.total = vram; + p.vram_info.free = free; + p.vram_info.largest_free_block = largest; + + if (copy_to_user((void __user *)arg, &p.vram_info, + sizeof(p.vram_info))) + r = -EFAULT; + break; + } + + case OMAPFB_SET_TEARSYNC: { + DBG("ioctl SET_TEARSYNC\n"); + + if (copy_from_user(&p.tearsync_info, (void __user *)arg, + sizeof(p.tearsync_info))) { + r = -EFAULT; + break; + } + + if (!display->enable_te) { + r = -ENODEV; + break; + } + + r = display->enable_te(display, !!p.tearsync_info.enabled); + + break; + } + + default: + dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd); + r = -EINVAL; + } + + if (r < 0) + DBG("ioctl failed: %d\n", r); + + return r; +} + + diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c new file mode 100644 index 000000000000..ef299839858a --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -0,0 +1,2261 @@ +/* + * linux/drivers/video/omap2/omapfb-main.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/omapfb.h> + +#include <plat/display.h> +#include <plat/vram.h> +#include <plat/vrfb.h> + +#include "omapfb.h" + +#define MODULE_NAME "omapfb" + +#define OMAPFB_PLANE_XRES_MIN 8 +#define OMAPFB_PLANE_YRES_MIN 8 + +static char *def_mode; +static char *def_vram; +static int def_vrfb; +static int def_rotate; +static int def_mirror; + +#ifdef DEBUG +unsigned int omapfb_debug; +module_param_named(debug, omapfb_debug, bool, 0644); +static unsigned int omapfb_test_pattern; +module_param_named(test, omapfb_test_pattern, bool, 0644); +#endif + +static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi); + +#ifdef DEBUG +static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color) +{ + struct fb_var_screeninfo *var = &fbi->var; + struct fb_fix_screeninfo *fix = &fbi->fix; + void __iomem *addr = fbi->screen_base; + const unsigned bytespp = var->bits_per_pixel >> 3; + const unsigned line_len = fix->line_length / bytespp; + + int r = (color >> 16) & 0xff; + int g = (color >> 8) & 0xff; + int b = (color >> 0) & 0xff; + + if (var->bits_per_pixel == 16) { + u16 __iomem *p = (u16 __iomem *)addr; + p += y * line_len + x; + + r = r * 32 / 256; + g = g * 64 / 256; + b = b * 32 / 256; + + __raw_writew((r << 11) | (g << 5) | (b << 0), p); + } else if (var->bits_per_pixel == 24) { + u8 __iomem *p = (u8 __iomem *)addr; + p += (y * line_len + x) * 3; + + __raw_writeb(b, p + 0); + __raw_writeb(g, p + 1); + __raw_writeb(r, p + 2); + } else if (var->bits_per_pixel == 32) { + u32 __iomem *p = (u32 __iomem *)addr; + p += y * line_len + x; + __raw_writel(color, p); + } +} + +static void fill_fb(struct fb_info *fbi) +{ + struct fb_var_screeninfo *var = &fbi->var; + const short w = var->xres_virtual; + const short h = var->yres_virtual; + void __iomem *addr = fbi->screen_base; + int y, x; + + if (!addr) + return; + + DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length); + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + if (x < 20 && y < 20) + draw_pixel(fbi, x, y, 0xffffff); + else if (x < 20 && (y > 20 && y < h - 20)) + draw_pixel(fbi, x, y, 0xff); + else if (y < 20 && (x > 20 && x < w - 20)) + draw_pixel(fbi, x, y, 0xff00); + else if (x > w - 20 && (y > 20 && y < h - 20)) + draw_pixel(fbi, x, y, 0xff0000); + else if (y > h - 20 && (x > 20 && x < w - 20)) + draw_pixel(fbi, x, y, 0xffff00); + else if (x == 20 || x == w - 20 || + y == 20 || y == h - 20) + draw_pixel(fbi, x, y, 0xffffff); + else if (x == y || w - x == h - y) + draw_pixel(fbi, x, y, 0xff00ff); + else if (w - x == y || x == h - y) + draw_pixel(fbi, x, y, 0x00ffff); + else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) { + int t = x * 3 / w; + unsigned r = 0, g = 0, b = 0; + unsigned c; + if (var->bits_per_pixel == 16) { + if (t == 0) + b = (y % 32) * 256 / 32; + else if (t == 1) + g = (y % 64) * 256 / 64; + else if (t == 2) + r = (y % 32) * 256 / 32; + } else { + if (t == 0) + b = (y % 256); + else if (t == 1) + g = (y % 256); + else if (t == 2) + r = (y % 256); + } + c = (r << 16) | (g << 8) | (b << 0); + draw_pixel(fbi, x, y, c); + } else { + draw_pixel(fbi, x, y, 0); + } + } + } +} +#endif + +static unsigned omapfb_get_vrfb_offset(struct omapfb_info *ofbi, int rot) +{ + struct vrfb *vrfb = &ofbi->region.vrfb; + unsigned offset; + + switch (rot) { + case FB_ROTATE_UR: + offset = 0; + break; + case FB_ROTATE_CW: + offset = vrfb->yoffset; + break; + case FB_ROTATE_UD: + offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset; + break; + case FB_ROTATE_CCW: + offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN; + break; + default: + BUG(); + } + + offset *= vrfb->bytespp; + + return offset; +} + +static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi, int rot) +{ + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + return ofbi->region.vrfb.paddr[rot] + + omapfb_get_vrfb_offset(ofbi, rot); + } else { + return ofbi->region.paddr; + } +} + +static u32 omapfb_get_region_paddr(struct omapfb_info *ofbi) +{ + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + return ofbi->region.vrfb.paddr[0]; + else + return ofbi->region.paddr; +} + +static void __iomem *omapfb_get_region_vaddr(struct omapfb_info *ofbi) +{ + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + return ofbi->region.vrfb.vaddr[0]; + else + return ofbi->region.vaddr; +} + +static struct omapfb_colormode omapfb_colormodes[] = { + { + .dssmode = OMAP_DSS_COLOR_UYVY, + .bits_per_pixel = 16, + .nonstd = OMAPFB_COLOR_YUV422, + }, { + .dssmode = OMAP_DSS_COLOR_YUV2, + .bits_per_pixel = 16, + .nonstd = OMAPFB_COLOR_YUY422, + }, { + .dssmode = OMAP_DSS_COLOR_ARGB16, + .bits_per_pixel = 16, + .red = { .length = 4, .offset = 8, .msb_right = 0 }, + .green = { .length = 4, .offset = 4, .msb_right = 0 }, + .blue = { .length = 4, .offset = 0, .msb_right = 0 }, + .transp = { .length = 4, .offset = 12, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGB16, + .bits_per_pixel = 16, + .red = { .length = 5, .offset = 11, .msb_right = 0 }, + .green = { .length = 6, .offset = 5, .msb_right = 0 }, + .blue = { .length = 5, .offset = 0, .msb_right = 0 }, + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGB24P, + .bits_per_pixel = 24, + .red = { .length = 8, .offset = 16, .msb_right = 0 }, + .green = { .length = 8, .offset = 8, .msb_right = 0 }, + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGB24U, + .bits_per_pixel = 32, + .red = { .length = 8, .offset = 16, .msb_right = 0 }, + .green = { .length = 8, .offset = 8, .msb_right = 0 }, + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_ARGB32, + .bits_per_pixel = 32, + .red = { .length = 8, .offset = 16, .msb_right = 0 }, + .green = { .length = 8, .offset = 8, .msb_right = 0 }, + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, + .transp = { .length = 8, .offset = 24, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGBA32, + .bits_per_pixel = 32, + .red = { .length = 8, .offset = 24, .msb_right = 0 }, + .green = { .length = 8, .offset = 16, .msb_right = 0 }, + .blue = { .length = 8, .offset = 8, .msb_right = 0 }, + .transp = { .length = 8, .offset = 0, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGBX32, + .bits_per_pixel = 32, + .red = { .length = 8, .offset = 24, .msb_right = 0 }, + .green = { .length = 8, .offset = 16, .msb_right = 0 }, + .blue = { .length = 8, .offset = 8, .msb_right = 0 }, + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, + }, +}; + +static bool cmp_var_to_colormode(struct fb_var_screeninfo *var, + struct omapfb_colormode *color) +{ + bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2) + { + return f1->length == f2->length && + f1->offset == f2->offset && + f1->msb_right == f2->msb_right; + } + + if (var->bits_per_pixel == 0 || + var->red.length == 0 || + var->blue.length == 0 || + var->green.length == 0) + return 0; + + return var->bits_per_pixel == color->bits_per_pixel && + cmp_component(&var->red, &color->red) && + cmp_component(&var->green, &color->green) && + cmp_component(&var->blue, &color->blue) && + cmp_component(&var->transp, &color->transp); +} + +static void assign_colormode_to_var(struct fb_var_screeninfo *var, + struct omapfb_colormode *color) +{ + var->bits_per_pixel = color->bits_per_pixel; + var->nonstd = color->nonstd; + var->red = color->red; + var->green = color->green; + var->blue = color->blue; + var->transp = color->transp; +} + +static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var, + enum omap_color_mode *mode) +{ + enum omap_color_mode dssmode; + int i; + + /* first match with nonstd field */ + if (var->nonstd) { + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *m = &omapfb_colormodes[i]; + if (var->nonstd == m->nonstd) { + assign_colormode_to_var(var, m); + *mode = m->dssmode; + return 0; + } + } + + return -EINVAL; + } + + /* then try exact match of bpp and colors */ + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *m = &omapfb_colormodes[i]; + if (cmp_var_to_colormode(var, m)) { + assign_colormode_to_var(var, m); + *mode = m->dssmode; + return 0; + } + } + + /* match with bpp if user has not filled color fields + * properly */ + switch (var->bits_per_pixel) { + case 1: + dssmode = OMAP_DSS_COLOR_CLUT1; + break; + case 2: + dssmode = OMAP_DSS_COLOR_CLUT2; + break; + case 4: + dssmode = OMAP_DSS_COLOR_CLUT4; + break; + case 8: + dssmode = OMAP_DSS_COLOR_CLUT8; + break; + case 12: + dssmode = OMAP_DSS_COLOR_RGB12U; + break; + case 16: + dssmode = OMAP_DSS_COLOR_RGB16; + break; + case 24: + dssmode = OMAP_DSS_COLOR_RGB24P; + break; + case 32: + dssmode = OMAP_DSS_COLOR_RGB24U; + break; + default: + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *m = &omapfb_colormodes[i]; + if (dssmode == m->dssmode) { + assign_colormode_to_var(var, m); + *mode = m->dssmode; + return 0; + } + } + + return -EINVAL; +} + +static int check_fb_res_bounds(struct fb_var_screeninfo *var) +{ + int xres_min = OMAPFB_PLANE_XRES_MIN; + int xres_max = 2048; + int yres_min = OMAPFB_PLANE_YRES_MIN; + int yres_max = 2048; + + /* XXX: some applications seem to set virtual res to 0. */ + if (var->xres_virtual == 0) + var->xres_virtual = var->xres; + + if (var->yres_virtual == 0) + var->yres_virtual = var->yres; + + if (var->xres_virtual < xres_min || var->yres_virtual < yres_min) + return -EINVAL; + + if (var->xres < xres_min) + var->xres = xres_min; + if (var->yres < yres_min) + var->yres = yres_min; + if (var->xres > xres_max) + var->xres = xres_max; + if (var->yres > yres_max) + var->yres = yres_max; + + if (var->xres > var->xres_virtual) + var->xres = var->xres_virtual; + if (var->yres > var->yres_virtual) + var->yres = var->yres_virtual; + + return 0; +} + +static void shrink_height(unsigned long max_frame_size, + struct fb_var_screeninfo *var) +{ + DBG("can't fit FB into memory, reducing y\n"); + var->yres_virtual = max_frame_size / + (var->xres_virtual * var->bits_per_pixel >> 3); + + if (var->yres_virtual < OMAPFB_PLANE_YRES_MIN) + var->yres_virtual = OMAPFB_PLANE_YRES_MIN; + + if (var->yres > var->yres_virtual) + var->yres = var->yres_virtual; +} + +static void shrink_width(unsigned long max_frame_size, + struct fb_var_screeninfo *var) +{ + DBG("can't fit FB into memory, reducing x\n"); + var->xres_virtual = max_frame_size / var->yres_virtual / + (var->bits_per_pixel >> 3); + + if (var->xres_virtual < OMAPFB_PLANE_XRES_MIN) + var->xres_virtual = OMAPFB_PLANE_XRES_MIN; + + if (var->xres > var->xres_virtual) + var->xres = var->xres_virtual; +} + +static int check_vrfb_fb_size(unsigned long region_size, + const struct fb_var_screeninfo *var) +{ + unsigned long min_phys_size = omap_vrfb_min_phys_size(var->xres_virtual, + var->yres_virtual, var->bits_per_pixel >> 3); + + return min_phys_size > region_size ? -EINVAL : 0; +} + +static int check_fb_size(const struct omapfb_info *ofbi, + struct fb_var_screeninfo *var) +{ + unsigned long max_frame_size = ofbi->region.size; + int bytespp = var->bits_per_pixel >> 3; + unsigned long line_size = var->xres_virtual * bytespp; + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + /* One needs to check for both VRFB and OMAPFB limitations. */ + if (check_vrfb_fb_size(max_frame_size, var)) + shrink_height(omap_vrfb_max_height( + max_frame_size, var->xres_virtual, bytespp) * + line_size, var); + + if (check_vrfb_fb_size(max_frame_size, var)) { + DBG("cannot fit FB to memory\n"); + return -EINVAL; + } + + return 0; + } + + DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size); + + if (line_size * var->yres_virtual > max_frame_size) + shrink_height(max_frame_size, var); + + if (line_size * var->yres_virtual > max_frame_size) { + shrink_width(max_frame_size, var); + line_size = var->xres_virtual * bytespp; + } + + if (line_size * var->yres_virtual > max_frame_size) { + DBG("cannot fit FB to memory\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Consider if VRFB assisted rotation is in use and if the virtual space for + * the zero degree view needs to be mapped. The need for mapping also acts as + * the trigger for setting up the hardware on the context in question. This + * ensures that one does not attempt to access the virtual view before the + * hardware is serving the address translations. + */ +static int setup_vrfb_rotation(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_mem_region *rg = &ofbi->region; + struct vrfb *vrfb = &rg->vrfb; + struct fb_var_screeninfo *var = &fbi->var; + struct fb_fix_screeninfo *fix = &fbi->fix; + unsigned bytespp; + bool yuv_mode; + enum omap_color_mode mode; + int r; + bool reconf; + + if (!rg->size || ofbi->rotation_type != OMAP_DSS_ROT_VRFB) + return 0; + + DBG("setup_vrfb_rotation\n"); + + r = fb_mode_to_dss_mode(var, &mode); + if (r) + return r; + + bytespp = var->bits_per_pixel >> 3; + + yuv_mode = mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY; + + /* We need to reconfigure VRFB if the resolution changes, if yuv mode + * is enabled/disabled, or if bytes per pixel changes */ + + /* XXX we shouldn't allow this when framebuffer is mmapped */ + + reconf = false; + + if (yuv_mode != vrfb->yuv_mode) + reconf = true; + else if (bytespp != vrfb->bytespp) + reconf = true; + else if (vrfb->xres != var->xres_virtual || + vrfb->yres != var->yres_virtual) + reconf = true; + + if (vrfb->vaddr[0] && reconf) { + fbi->screen_base = NULL; + fix->smem_start = 0; + fix->smem_len = 0; + iounmap(vrfb->vaddr[0]); + vrfb->vaddr[0] = NULL; + DBG("setup_vrfb_rotation: reset fb\n"); + } + + if (vrfb->vaddr[0]) + return 0; + + omap_vrfb_setup(&rg->vrfb, rg->paddr, + var->xres_virtual, + var->yres_virtual, + bytespp, yuv_mode); + + /* Now one can ioremap the 0 angle view */ + r = omap_vrfb_map_angle(vrfb, var->yres_virtual, 0); + if (r) + return r; + + /* used by open/write in fbmem.c */ + fbi->screen_base = ofbi->region.vrfb.vaddr[0]; + + fix->smem_start = ofbi->region.vrfb.paddr[0]; + + switch (var->nonstd) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUY422: + fix->line_length = + (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; + break; + default: + fix->line_length = + (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; + break; + } + + fix->smem_len = var->yres_virtual * fix->line_length; + + return 0; +} + +int dss_mode_to_fb_mode(enum omap_color_mode dssmode, + struct fb_var_screeninfo *var) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *mode = &omapfb_colormodes[i]; + if (dssmode == mode->dssmode) { + assign_colormode_to_var(var, mode); + return 0; + } + } + return -ENOENT; +} + +void set_fb_fix(struct fb_info *fbi) +{ + struct fb_fix_screeninfo *fix = &fbi->fix; + struct fb_var_screeninfo *var = &fbi->var; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_mem_region *rg = &ofbi->region; + + DBG("set_fb_fix\n"); + + /* used by open/write in fbmem.c */ + fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi); + + /* used by mmap in fbmem.c */ + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + switch (var->nonstd) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUY422: + fix->line_length = + (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; + break; + default: + fix->line_length = + (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; + break; + } + + fix->smem_len = var->yres_virtual * fix->line_length; + } else { + fix->line_length = + (var->xres_virtual * var->bits_per_pixel) >> 3; + fix->smem_len = rg->size; + } + + fix->smem_start = omapfb_get_region_paddr(ofbi); + + fix->type = FB_TYPE_PACKED_PIXELS; + + if (var->nonstd) + fix->visual = FB_VISUAL_PSEUDOCOLOR; + else { + switch (var->bits_per_pixel) { + case 32: + case 24: + case 16: + case 12: + fix->visual = FB_VISUAL_TRUECOLOR; + /* 12bpp is stored in 16 bits */ + break; + case 1: + case 2: + case 4: + case 8: + fix->visual = FB_VISUAL_PSEUDOCOLOR; + break; + } + } + + fix->accel = FB_ACCEL_NONE; + + fix->xpanstep = 1; + fix->ypanstep = 1; +} + +/* check new var and possibly modify it to be ok */ +int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omap_dss_device *display = fb2display(fbi); + enum omap_color_mode mode = 0; + int i; + int r; + + DBG("check_fb_var %d\n", ofbi->id); + + if (ofbi->region.size == 0) + return 0; + + r = fb_mode_to_dss_mode(var, &mode); + if (r) { + DBG("cannot convert var to omap dss mode\n"); + return r; + } + + for (i = 0; i < ofbi->num_overlays; ++i) { + if ((ofbi->overlays[i]->supported_modes & mode) == 0) { + DBG("invalid mode\n"); + return -EINVAL; + } + } + + if (var->rotate < 0 || var->rotate > 3) + return -EINVAL; + + if (check_fb_res_bounds(var)) + return -EINVAL; + + if (check_fb_size(ofbi, var)) + return -EINVAL; + + if (var->xres + var->xoffset > var->xres_virtual) + var->xoffset = var->xres_virtual - var->xres; + if (var->yres + var->yoffset > var->yres_virtual) + var->yoffset = var->yres_virtual - var->yres; + + DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n", + var->xres, var->yres, + var->xres_virtual, var->yres_virtual); + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + if (display && display->get_timings) { + struct omap_video_timings timings; + display->get_timings(display, &timings); + + /* pixclock in ps, the rest in pixclock */ + var->pixclock = timings.pixel_clock != 0 ? + KHZ2PICOS(timings.pixel_clock) : + 0; + var->left_margin = timings.hfp; + var->right_margin = timings.hbp; + var->upper_margin = timings.vfp; + var->lower_margin = timings.vbp; + var->hsync_len = timings.hsw; + var->vsync_len = timings.vsw; + } else { + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + } + + /* TODO: get these from panel->config */ + var->vmode = FB_VMODE_NONINTERLACED; + var->sync = 0; + + return 0; +} + +/* + * --------------------------------------------------------------------------- + * fbdev framework callbacks + * --------------------------------------------------------------------------- + */ +static int omapfb_open(struct fb_info *fbi, int user) +{ + return 0; +} + +static int omapfb_release(struct fb_info *fbi, int user) +{ +#if 0 + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + + DBG("Closing fb with plane index %d\n", ofbi->id); + + omapfb_lock(fbdev); + + if (display && display->get_update_mode && display->update) { + /* XXX this update should be removed, I think. But it's + * good for debugging */ + if (display->get_update_mode(display) == + OMAP_DSS_UPDATE_MANUAL) { + u16 w, h; + + if (display->sync) + display->sync(display); + + display->get_resolution(display, &w, &h); + display->update(display, 0, 0, w, h); + } + } + + if (display && display->sync) + display->sync(display); + + omapfb_unlock(fbdev); +#endif + return 0; +} + +static unsigned calc_rotation_offset_dma(struct fb_var_screeninfo *var, + struct fb_fix_screeninfo *fix, int rotation) +{ + unsigned offset; + + offset = var->yoffset * fix->line_length + + var->xoffset * (var->bits_per_pixel >> 3); + + return offset; +} + +static unsigned calc_rotation_offset_vrfb(struct fb_var_screeninfo *var, + struct fb_fix_screeninfo *fix, int rotation) +{ + unsigned offset; + + if (rotation == FB_ROTATE_UD) + offset = (var->yres_virtual - var->yres) * + fix->line_length; + else if (rotation == FB_ROTATE_CW) + offset = (var->yres_virtual - var->yres) * + (var->bits_per_pixel >> 3); + else + offset = 0; + + if (rotation == FB_ROTATE_UR) + offset += var->yoffset * fix->line_length + + var->xoffset * (var->bits_per_pixel >> 3); + else if (rotation == FB_ROTATE_UD) + offset -= var->yoffset * fix->line_length + + var->xoffset * (var->bits_per_pixel >> 3); + else if (rotation == FB_ROTATE_CW) + offset -= var->xoffset * fix->line_length + + var->yoffset * (var->bits_per_pixel >> 3); + else if (rotation == FB_ROTATE_CCW) + offset += var->xoffset * fix->line_length + + var->yoffset * (var->bits_per_pixel >> 3); + + return offset; +} + + +/* setup overlay according to the fb */ +static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, + u16 posx, u16 posy, u16 outw, u16 outh) +{ + int r = 0; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_var_screeninfo *var = &fbi->var; + struct fb_fix_screeninfo *fix = &fbi->fix; + enum omap_color_mode mode = 0; + int offset; + u32 data_start_p; + void __iomem *data_start_v; + struct omap_overlay_info info; + int xres, yres; + int screen_width; + int mirror; + int rotation = var->rotate; + int i; + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ovl != ofbi->overlays[i]) + continue; + + rotation = (rotation + ofbi->rotation[i]) % 4; + break; + } + + DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id, + posx, posy, outw, outh); + + if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { + xres = var->yres; + yres = var->xres; + } else { + xres = var->xres; + yres = var->yres; + } + + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); + data_start_v = NULL; + } else { + data_start_p = omapfb_get_region_paddr(ofbi); + data_start_v = omapfb_get_region_vaddr(ofbi); + } + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + offset = calc_rotation_offset_vrfb(var, fix, rotation); + else + offset = calc_rotation_offset_dma(var, fix, rotation); + + data_start_p += offset; + data_start_v += offset; + + if (offset) + DBG("offset %d, %d = %d\n", + var->xoffset, var->yoffset, offset); + + DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v); + + r = fb_mode_to_dss_mode(var, &mode); + if (r) { + DBG("fb_mode_to_dss_mode failed"); + goto err; + } + + switch (var->nonstd) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUY422: + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + screen_width = fix->line_length + / (var->bits_per_pixel >> 2); + break; + } + default: + screen_width = fix->line_length / (var->bits_per_pixel >> 3); + break; + } + + ovl->get_overlay_info(ovl, &info); + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + mirror = 0; + else + mirror = ofbi->mirror; + + info.paddr = data_start_p; + info.vaddr = data_start_v; + info.screen_width = screen_width; + info.width = xres; + info.height = yres; + info.color_mode = mode; + info.rotation_type = ofbi->rotation_type; + info.rotation = rotation; + info.mirror = mirror; + + info.pos_x = posx; + info.pos_y = posy; + info.out_width = outw; + info.out_height = outh; + + r = ovl->set_overlay_info(ovl, &info); + if (r) { + DBG("ovl->setup_overlay_info failed\n"); + goto err; + } + + return 0; + +err: + DBG("setup_overlay failed\n"); + return r; +} + +/* apply var to the overlay */ +int omapfb_apply_changes(struct fb_info *fbi, int init) +{ + int r = 0; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_var_screeninfo *var = &fbi->var; + struct omap_overlay *ovl; + u16 posx, posy; + u16 outw, outh; + int i; + +#ifdef DEBUG + if (omapfb_test_pattern) + fill_fb(fbi); +#endif + + for (i = 0; i < ofbi->num_overlays; i++) { + ovl = ofbi->overlays[i]; + + DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); + + if (ofbi->region.size == 0) { + /* the fb is not available. disable the overlay */ + omapfb_overlay_enable(ovl, 0); + if (!init && ovl->manager) + ovl->manager->apply(ovl->manager); + continue; + } + + if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + int rotation = (var->rotate + ofbi->rotation[i]) % 4; + if (rotation == FB_ROTATE_CW || + rotation == FB_ROTATE_CCW) { + outw = var->yres; + outh = var->xres; + } else { + outw = var->xres; + outh = var->yres; + } + } else { + outw = ovl->info.out_width; + outh = ovl->info.out_height; + } + + if (init) { + posx = 0; + posy = 0; + } else { + posx = ovl->info.pos_x; + posy = ovl->info.pos_y; + } + + r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh); + if (r) + goto err; + + if (!init && ovl->manager) + ovl->manager->apply(ovl->manager); + } + return 0; +err: + DBG("apply_changes failed\n"); + return r; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) +{ + int r; + + DBG("check_var(%d)\n", FB2OFB(fbi)->id); + + r = check_fb_var(fbi, var); + + return r; +} + +/* set the video mode according to info->var */ +static int omapfb_set_par(struct fb_info *fbi) +{ + int r; + + DBG("set_par(%d)\n", FB2OFB(fbi)->id); + + set_fb_fix(fbi); + + r = setup_vrfb_rotation(fbi); + if (r) + return r; + + r = omapfb_apply_changes(fbi, 0); + + return r; +} + +static int omapfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct fb_var_screeninfo new_var; + int r; + + DBG("pan_display(%d)\n", FB2OFB(fbi)->id); + + if (var->xoffset == fbi->var.xoffset && + var->yoffset == fbi->var.yoffset) + return 0; + + new_var = fbi->var; + new_var.xoffset = var->xoffset; + new_var.yoffset = var->yoffset; + + fbi->var = new_var; + + r = omapfb_apply_changes(fbi, 0); + + return r; +} + +static void mmap_user_open(struct vm_area_struct *vma) +{ + struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; + + atomic_inc(&ofbi->map_count); +} + +static void mmap_user_close(struct vm_area_struct *vma) +{ + struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; + + atomic_dec(&ofbi->map_count); +} + +static struct vm_operations_struct mmap_user_ops = { + .open = mmap_user_open, + .close = mmap_user_close, +}; + +static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_fix_screeninfo *fix = &fbi->fix; + unsigned long off; + unsigned long start; + u32 len; + + if (vma->vm_end - vma->vm_start == 0) + return 0; + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + off = vma->vm_pgoff << PAGE_SHIFT; + + start = omapfb_get_region_paddr(ofbi); + len = fix->smem_len; + if (off >= len) + return -EINVAL; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + + off += start; + + DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off); + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO | VM_RESERVED; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &mmap_user_ops; + vma->vm_private_data = ofbi; + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) + return -EAGAIN; + /* vm_ops.open won't be called for mmap itself. */ + atomic_inc(&ofbi->map_count); + return 0; +} + +/* Store a single color palette entry into a pseudo palette or the hardware + * palette if one is available. For now we support only 16bpp and thus store + * the entry only to the pseudo palette. + */ +static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, + u_int blue, u_int transp, int update_hw_pal) +{ + /*struct omapfb_info *ofbi = FB2OFB(fbi);*/ + /*struct omapfb2_device *fbdev = ofbi->fbdev;*/ + struct fb_var_screeninfo *var = &fbi->var; + int r = 0; + + enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */ + + /*switch (plane->color_mode) {*/ + switch (mode) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUV420: + case OMAPFB_COLOR_YUY422: + r = -EINVAL; + break; + case OMAPFB_COLOR_CLUT_8BPP: + case OMAPFB_COLOR_CLUT_4BPP: + case OMAPFB_COLOR_CLUT_2BPP: + case OMAPFB_COLOR_CLUT_1BPP: + /* + if (fbdev->ctrl->setcolreg) + r = fbdev->ctrl->setcolreg(regno, red, green, blue, + transp, update_hw_pal); + */ + /* Fallthrough */ + r = -EINVAL; + break; + case OMAPFB_COLOR_RGB565: + case OMAPFB_COLOR_RGB444: + case OMAPFB_COLOR_RGB24P: + case OMAPFB_COLOR_RGB24U: + if (r != 0) + break; + + if (regno < 0) { + r = -EINVAL; + break; + } + + if (regno < 16) { + u16 pal; + pal = ((red >> (16 - var->red.length)) << + var->red.offset) | + ((green >> (16 - var->green.length)) << + var->green.offset) | + (blue >> (16 - var->blue.length)); + ((u32 *)(fbi->pseudo_palette))[regno] = pal; + } + break; + default: + BUG(); + } + return r; +} + +static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + DBG("setcolreg\n"); + + return _setcolreg(info, regno, red, green, blue, transp, 1); +} + +static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + int count, index, r; + u16 *red, *green, *blue, *transp; + u16 trans = 0xffff; + + DBG("setcmap\n"); + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + index = cmap->start; + + for (count = 0; count < cmap->len; count++) { + if (transp) + trans = *transp++; + r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, + count == cmap->len - 1); + if (r != 0) + return r; + } + + return 0; +} + +static int omapfb_blank(int blank, struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + int do_update = 0; + int r = 0; + + omapfb_lock(fbdev); + + switch (blank) { + case FB_BLANK_UNBLANK: + if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) + goto exit; + + if (display->resume) + r = display->resume(display); + + if (r == 0 && display->get_update_mode && + display->get_update_mode(display) == + OMAP_DSS_UPDATE_MANUAL) + do_update = 1; + + break; + + case FB_BLANK_NORMAL: + /* FB_BLANK_NORMAL could be implemented. + * Needs DSS additions. */ + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + if (display->state != OMAP_DSS_DISPLAY_ACTIVE) + goto exit; + + if (display->suspend) + r = display->suspend(display); + + break; + + default: + r = -EINVAL; + } + +exit: + omapfb_unlock(fbdev); + + if (r == 0 && do_update && display->update) { + u16 w, h; + display->get_resolution(display, &w, &h); + + r = display->update(display, 0, 0, w, h); + } + + return r; +} + +#if 0 +/* XXX fb_read and fb_write are needed for VRFB */ +ssize_t omapfb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos); + /* XXX needed for VRFB */ + return count; +} +#endif + +static struct fb_ops omapfb_ops = { + .owner = THIS_MODULE, + .fb_open = omapfb_open, + .fb_release = omapfb_release, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = omapfb_blank, + .fb_ioctl = omapfb_ioctl, + .fb_check_var = omapfb_check_var, + .fb_set_par = omapfb_set_par, + .fb_pan_display = omapfb_pan_display, + .fb_mmap = omapfb_mmap, + .fb_setcolreg = omapfb_setcolreg, + .fb_setcmap = omapfb_setcmap, + /*.fb_write = omapfb_write,*/ +}; + +static void omapfb_free_fbmem(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb2_mem_region *rg; + + rg = &ofbi->region; + + if (rg->paddr) + if (omap_vram_free(rg->paddr, rg->size)) + dev_err(fbdev->dev, "VRAM FREE failed\n"); + + if (rg->vaddr) + iounmap(rg->vaddr); + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + /* unmap the 0 angle rotation */ + if (rg->vrfb.vaddr[0]) { + iounmap(rg->vrfb.vaddr[0]); + omap_vrfb_release_ctx(&rg->vrfb); + } + } + + rg->vaddr = NULL; + rg->paddr = 0; + rg->alloc = 0; + rg->size = 0; +} + +static void clear_fb_info(struct fb_info *fbi) +{ + memset(&fbi->var, 0, sizeof(fbi->var)); + memset(&fbi->fix, 0, sizeof(fbi->fix)); + strlcpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id)); +} + +static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev) +{ + int i; + + DBG("free all fbmem\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + struct fb_info *fbi = fbdev->fbs[i]; + omapfb_free_fbmem(fbi); + clear_fb_info(fbi); + } + + return 0; +} + +static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, + unsigned long paddr) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb2_mem_region *rg; + void __iomem *vaddr; + int r; + + rg = &ofbi->region; + memset(rg, 0, sizeof(*rg)); + + size = PAGE_ALIGN(size); + + if (!paddr) { + DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); + r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr); + } else { + DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, + ofbi->id); + r = omap_vram_reserve(paddr, size); + } + + if (r) { + dev_err(fbdev->dev, "failed to allocate framebuffer\n"); + return -ENOMEM; + } + + if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) { + vaddr = ioremap_wc(paddr, size); + + if (!vaddr) { + dev_err(fbdev->dev, "failed to ioremap framebuffer\n"); + omap_vram_free(paddr, size); + return -ENOMEM; + } + + DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr); + } else { + r = omap_vrfb_request_ctx(&rg->vrfb); + if (r) { + dev_err(fbdev->dev, "vrfb create ctx failed\n"); + return r; + } + + vaddr = NULL; + } + + rg->paddr = paddr; + rg->vaddr = vaddr; + rg->size = size; + rg->alloc = 1; + + return 0; +} + +/* allocate fbmem using display resolution as reference */ +static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, + unsigned long paddr) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omap_dss_device *display; + int bytespp; + + display = fb2display(fbi); + + if (!display) + return 0; + + switch (display->get_recommended_bpp(display)) { + case 16: + bytespp = 2; + break; + case 24: + bytespp = 4; + break; + default: + bytespp = 4; + break; + } + + if (!size) { + u16 w, h; + + display->get_resolution(display, &w, &h); + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + size = max(omap_vrfb_min_phys_size(w, h, bytespp), + omap_vrfb_min_phys_size(h, w, bytespp)); + + DBG("adjusting fb mem size for VRFB, %u -> %lu\n", + w * h * bytespp, size); + } else { + size = w * h * bytespp; + } + } + + if (!size) + return 0; + + return omapfb_alloc_fbmem(fbi, size, paddr); +} + +static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt) +{ + enum omap_color_mode mode; + + switch (fmt) { + case OMAPFB_COLOR_RGB565: + mode = OMAP_DSS_COLOR_RGB16; + break; + case OMAPFB_COLOR_YUV422: + mode = OMAP_DSS_COLOR_YUV2; + break; + case OMAPFB_COLOR_CLUT_8BPP: + mode = OMAP_DSS_COLOR_CLUT8; + break; + case OMAPFB_COLOR_CLUT_4BPP: + mode = OMAP_DSS_COLOR_CLUT4; + break; + case OMAPFB_COLOR_CLUT_2BPP: + mode = OMAP_DSS_COLOR_CLUT2; + break; + case OMAPFB_COLOR_CLUT_1BPP: + mode = OMAP_DSS_COLOR_CLUT1; + break; + case OMAPFB_COLOR_RGB444: + mode = OMAP_DSS_COLOR_RGB12U; + break; + case OMAPFB_COLOR_YUY422: + mode = OMAP_DSS_COLOR_UYVY; + break; + case OMAPFB_COLOR_ARGB16: + mode = OMAP_DSS_COLOR_ARGB16; + break; + case OMAPFB_COLOR_RGB24U: + mode = OMAP_DSS_COLOR_RGB24U; + break; + case OMAPFB_COLOR_RGB24P: + mode = OMAP_DSS_COLOR_RGB24P; + break; + case OMAPFB_COLOR_ARGB32: + mode = OMAP_DSS_COLOR_ARGB32; + break; + case OMAPFB_COLOR_RGBA32: + mode = OMAP_DSS_COLOR_RGBA32; + break; + case OMAPFB_COLOR_RGBX32: + mode = OMAP_DSS_COLOR_RGBX32; + break; + default: + mode = -EINVAL; + } + + return mode; +} + +static int omapfb_parse_vram_param(const char *param, int max_entries, + unsigned long *sizes, unsigned long *paddrs) +{ + int fbnum; + unsigned long size; + unsigned long paddr = 0; + char *p, *start; + + start = (char *)param; + + while (1) { + p = start; + + fbnum = simple_strtoul(p, &p, 10); + + if (p == param) + return -EINVAL; + + if (*p != ':') + return -EINVAL; + + if (fbnum >= max_entries) + return -EINVAL; + + size = memparse(p + 1, &p); + + if (!size) + return -EINVAL; + + paddr = 0; + + if (*p == '@') { + paddr = simple_strtoul(p + 1, &p, 16); + + if (!paddr) + return -EINVAL; + + } + + paddrs[fbnum] = paddr; + sizes[fbnum] = size; + + if (*p == 0) + break; + + if (*p != ',') + return -EINVAL; + + ++p; + + start = p; + } + + return 0; +} + +static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) +{ + int i, r; + unsigned long vram_sizes[10]; + unsigned long vram_paddrs[10]; + + memset(&vram_sizes, 0, sizeof(vram_sizes)); + memset(&vram_paddrs, 0, sizeof(vram_paddrs)); + + if (def_vram && omapfb_parse_vram_param(def_vram, 10, + vram_sizes, vram_paddrs)) { + dev_err(fbdev->dev, "failed to parse vram parameter\n"); + + memset(&vram_sizes, 0, sizeof(vram_sizes)); + memset(&vram_paddrs, 0, sizeof(vram_paddrs)); + } + + if (fbdev->dev->platform_data) { + struct omapfb_platform_data *opd; + opd = fbdev->dev->platform_data; + for (i = 0; i < opd->mem_desc.region_cnt; ++i) { + if (!vram_sizes[i]) { + unsigned long size; + unsigned long paddr; + + size = opd->mem_desc.region[i].size; + paddr = opd->mem_desc.region[i].paddr; + + vram_sizes[i] = size; + vram_paddrs[i] = paddr; + } + } + } + + for (i = 0; i < fbdev->num_fbs; i++) { + /* allocate memory automatically only for fb0, or if + * excplicitly defined with vram or plat data option */ + if (i == 0 || vram_sizes[i] != 0) { + r = omapfb_alloc_fbmem_display(fbdev->fbs[i], + vram_sizes[i], vram_paddrs[i]); + + if (r) + return r; + } + } + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + struct omapfb2_mem_region *rg; + rg = &ofbi->region; + + DBG("region%d phys %08x virt %p size=%lu\n", + i, + rg->paddr, + rg->vaddr, + rg->size); + } + + return 0; +} + +int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + struct omapfb2_mem_region *rg = &ofbi->region; + unsigned long old_size = rg->size; + unsigned long old_paddr = rg->paddr; + int old_type = rg->type; + int r; + + if (type > OMAPFB_MEMTYPE_MAX) + return -EINVAL; + + size = PAGE_ALIGN(size); + + if (old_size == size && old_type == type) + return 0; + + if (display && display->sync) + display->sync(display); + + omapfb_free_fbmem(fbi); + + if (size == 0) { + clear_fb_info(fbi); + return 0; + } + + r = omapfb_alloc_fbmem(fbi, size, 0); + + if (r) { + if (old_size) + omapfb_alloc_fbmem(fbi, old_size, old_paddr); + + if (rg->size == 0) + clear_fb_info(fbi); + + return r; + } + + if (old_size == size) + return 0; + + if (old_size == 0) { + DBG("initializing fb %d\n", ofbi->id); + r = omapfb_fb_init(fbdev, fbi); + if (r) { + DBG("omapfb_fb_init failed\n"); + goto err; + } + r = omapfb_apply_changes(fbi, 1); + if (r) { + DBG("omapfb_apply_changes failed\n"); + goto err; + } + } else { + struct fb_var_screeninfo new_var; + memcpy(&new_var, &fbi->var, sizeof(new_var)); + r = check_fb_var(fbi, &new_var); + if (r) + goto err; + memcpy(&fbi->var, &new_var, sizeof(fbi->var)); + set_fb_fix(fbi); + r = setup_vrfb_rotation(fbi); + if (r) + goto err; + } + + return 0; +err: + omapfb_free_fbmem(fbi); + clear_fb_info(fbi); + return r; +} + +/* initialize fb_info, var, fix to something sane based on the display */ +static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) +{ + struct fb_var_screeninfo *var = &fbi->var; + struct omap_dss_device *display = fb2display(fbi); + struct omapfb_info *ofbi = FB2OFB(fbi); + int r = 0; + + fbi->fbops = &omapfb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = fbdev->pseudo_palette; + + if (ofbi->region.size == 0) { + clear_fb_info(fbi); + return 0; + } + + var->nonstd = 0; + var->bits_per_pixel = 0; + + var->rotate = def_rotate; + + /* + * Check if there is a default color format set in the board file, + * and use this format instead the default deducted from the + * display bpp. + */ + if (fbdev->dev->platform_data) { + struct omapfb_platform_data *opd; + int id = ofbi->id; + + opd = fbdev->dev->platform_data; + if (opd->mem_desc.region[id].format_used) { + enum omap_color_mode mode; + enum omapfb_color_format format; + + format = opd->mem_desc.region[id].format; + mode = fb_format_to_dss_mode(format); + if (mode < 0) { + r = mode; + goto err; + } + r = dss_mode_to_fb_mode(mode, var); + if (r < 0) + goto err; + } + } + + if (display) { + u16 w, h; + int rotation = (var->rotate + ofbi->rotation[0]) % 4; + + display->get_resolution(display, &w, &h); + + if (rotation == FB_ROTATE_CW || + rotation == FB_ROTATE_CCW) { + var->xres = h; + var->yres = w; + } else { + var->xres = w; + var->yres = h; + } + + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + + if (!var->bits_per_pixel) { + switch (display->get_recommended_bpp(display)) { + case 16: + var->bits_per_pixel = 16; + break; + case 24: + var->bits_per_pixel = 32; + break; + default: + dev_err(fbdev->dev, "illegal display " + "bpp\n"); + return -EINVAL; + } + } + } else { + /* if there's no display, let's just guess some basic values */ + var->xres = 320; + var->yres = 240; + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + if (!var->bits_per_pixel) + var->bits_per_pixel = 16; + } + + r = check_fb_var(fbi, var); + if (r) + goto err; + + set_fb_fix(fbi); + r = setup_vrfb_rotation(fbi); + if (r) + goto err; + + r = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (r) + dev_err(fbdev->dev, "unable to allocate color map memory\n"); + +err: + return r; +} + +static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi) +{ + fb_dealloc_cmap(&fbi->cmap); +} + + +static void omapfb_free_resources(struct omapfb2_device *fbdev) +{ + int i; + + DBG("free_resources\n"); + + if (fbdev == NULL) + return; + + for (i = 0; i < fbdev->num_fbs; i++) + unregister_framebuffer(fbdev->fbs[i]); + + /* free the reserved fbmem */ + omapfb_free_all_fbmem(fbdev); + + for (i = 0; i < fbdev->num_fbs; i++) { + fbinfo_cleanup(fbdev, fbdev->fbs[i]); + framebuffer_release(fbdev->fbs[i]); + } + + for (i = 0; i < fbdev->num_displays; i++) { + if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED) + fbdev->displays[i]->disable(fbdev->displays[i]); + + omap_dss_put_device(fbdev->displays[i]); + } + + dev_set_drvdata(fbdev->dev, NULL); + kfree(fbdev); +} + +static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) +{ + int r, i; + + fbdev->num_fbs = 0; + + DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS); + + /* allocate fb_infos */ + for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) { + struct fb_info *fbi; + struct omapfb_info *ofbi; + + fbi = framebuffer_alloc(sizeof(struct omapfb_info), + fbdev->dev); + + if (fbi == NULL) { + dev_err(fbdev->dev, + "unable to allocate memory for plane info\n"); + return -ENOMEM; + } + + clear_fb_info(fbi); + + fbdev->fbs[i] = fbi; + + ofbi = FB2OFB(fbi); + ofbi->fbdev = fbdev; + ofbi->id = i; + + /* assign these early, so that fb alloc can use them */ + ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : + OMAP_DSS_ROT_DMA; + ofbi->mirror = def_mirror; + + fbdev->num_fbs++; + } + + DBG("fb_infos allocated\n"); + + /* assign overlays for the fbs */ + for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + + ofbi->overlays[0] = fbdev->overlays[i]; + ofbi->num_overlays = 1; + } + + /* allocate fb memories */ + r = omapfb_allocate_all_fbs(fbdev); + if (r) { + dev_err(fbdev->dev, "failed to allocate fbmem\n"); + return r; + } + + DBG("fbmems allocated\n"); + + /* setup fb_infos */ + for (i = 0; i < fbdev->num_fbs; i++) { + r = omapfb_fb_init(fbdev, fbdev->fbs[i]); + if (r) { + dev_err(fbdev->dev, "failed to setup fb_info\n"); + return r; + } + } + + DBG("fb_infos initialized\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + r = register_framebuffer(fbdev->fbs[i]); + if (r != 0) { + dev_err(fbdev->dev, + "registering framebuffer %d failed\n", i); + return r; + } + } + + DBG("framebuffers registered\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + r = omapfb_apply_changes(fbdev->fbs[i], 1); + if (r) { + dev_err(fbdev->dev, "failed to change mode\n"); + return r; + } + } + + DBG("create sysfs for fbs\n"); + r = omapfb_create_sysfs(fbdev); + if (r) { + dev_err(fbdev->dev, "failed to create sysfs entries\n"); + return r; + } + + /* Enable fb0 */ + if (fbdev->num_fbs > 0) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]); + + if (ofbi->num_overlays > 0) { + struct omap_overlay *ovl = ofbi->overlays[0]; + + r = omapfb_overlay_enable(ovl, 1); + + if (r) { + dev_err(fbdev->dev, + "failed to enable overlay\n"); + return r; + } + } + } + + DBG("create_framebuffers done\n"); + + return 0; +} + +static int omapfb_mode_to_timings(const char *mode_str, + struct omap_video_timings *timings, u8 *bpp) +{ + struct fb_info fbi; + struct fb_var_screeninfo var; + struct fb_ops fbops; + int r; + +#ifdef CONFIG_OMAP2_DSS_VENC + if (strcmp(mode_str, "pal") == 0) { + *timings = omap_dss_pal_timings; + *bpp = 0; + return 0; + } else if (strcmp(mode_str, "ntsc") == 0) { + *timings = omap_dss_ntsc_timings; + *bpp = 0; + return 0; + } +#endif + + /* this is quite a hack, but I wanted to use the modedb and for + * that we need fb_info and var, so we create dummy ones */ + + memset(&fbi, 0, sizeof(fbi)); + memset(&var, 0, sizeof(var)); + memset(&fbops, 0, sizeof(fbops)); + fbi.fbops = &fbops; + + r = fb_find_mode(&var, &fbi, mode_str, NULL, 0, NULL, 24); + + if (r != 0) { + timings->pixel_clock = PICOS2KHZ(var.pixclock); + timings->hfp = var.left_margin; + timings->hbp = var.right_margin; + timings->vfp = var.upper_margin; + timings->vbp = var.lower_margin; + timings->hsw = var.hsync_len; + timings->vsw = var.vsync_len; + timings->x_res = var.xres; + timings->y_res = var.yres; + + switch (var.bits_per_pixel) { + case 16: + *bpp = 16; + break; + case 24: + case 32: + default: + *bpp = 24; + break; + } + + return 0; + } else { + return -EINVAL; + } +} + +static int omapfb_set_def_mode(struct omap_dss_device *display, char *mode_str) +{ + int r; + u8 bpp; + struct omap_video_timings timings; + + r = omapfb_mode_to_timings(mode_str, &timings, &bpp); + if (r) + return r; + + display->panel.recommended_bpp = bpp; + + if (!display->check_timings || !display->set_timings) + return -EINVAL; + + r = display->check_timings(display, &timings); + if (r) + return r; + + display->set_timings(display, &timings); + + return 0; +} + +static int omapfb_parse_def_modes(struct omapfb2_device *fbdev) +{ + char *str, *options, *this_opt; + int r = 0; + + str = kmalloc(strlen(def_mode) + 1, GFP_KERNEL); + strcpy(str, def_mode); + options = str; + + while (!r && (this_opt = strsep(&options, ",")) != NULL) { + char *p, *display_str, *mode_str; + struct omap_dss_device *display; + int i; + + p = strchr(this_opt, ':'); + if (!p) { + r = -EINVAL; + break; + } + + *p = 0; + display_str = this_opt; + mode_str = p + 1; + + display = NULL; + for (i = 0; i < fbdev->num_displays; ++i) { + if (strcmp(fbdev->displays[i]->name, + display_str) == 0) { + display = fbdev->displays[i]; + break; + } + } + + if (!display) { + r = -EINVAL; + break; + } + + r = omapfb_set_def_mode(display, mode_str); + if (r) + break; + } + + kfree(str); + + return r; +} + +static int omapfb_probe(struct platform_device *pdev) +{ + struct omapfb2_device *fbdev = NULL; + int r = 0; + int i; + struct omap_overlay *ovl; + struct omap_dss_device *def_display; + struct omap_dss_device *dssdev; + + DBG("omapfb_probe\n"); + + if (pdev->num_resources != 0) { + dev_err(&pdev->dev, "probed for an unknown device\n"); + r = -ENODEV; + goto err0; + } + + fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL); + if (fbdev == NULL) { + r = -ENOMEM; + goto err0; + } + + mutex_init(&fbdev->mtx); + + fbdev->dev = &pdev->dev; + platform_set_drvdata(pdev, fbdev); + + fbdev->num_displays = 0; + dssdev = NULL; + for_each_dss_dev(dssdev) { + omap_dss_get_device(dssdev); + fbdev->displays[fbdev->num_displays++] = dssdev; + } + + if (fbdev->num_displays == 0) { + dev_err(&pdev->dev, "no displays\n"); + r = -EINVAL; + goto cleanup; + } + + fbdev->num_overlays = omap_dss_get_num_overlays(); + for (i = 0; i < fbdev->num_overlays; i++) + fbdev->overlays[i] = omap_dss_get_overlay(i); + + fbdev->num_managers = omap_dss_get_num_overlay_managers(); + for (i = 0; i < fbdev->num_managers; i++) + fbdev->managers[i] = omap_dss_get_overlay_manager(i); + + if (def_mode && strlen(def_mode) > 0) { + if (omapfb_parse_def_modes(fbdev)) + dev_warn(&pdev->dev, "cannot parse default modes\n"); + } + + r = omapfb_create_framebuffers(fbdev); + if (r) + goto cleanup; + + for (i = 0; i < fbdev->num_managers; i++) { + struct omap_overlay_manager *mgr; + mgr = fbdev->managers[i]; + r = mgr->apply(mgr); + if (r) + dev_warn(fbdev->dev, "failed to apply dispc config\n"); + } + + DBG("mgr->apply'ed\n"); + + /* gfx overlay should be the default one. find a display + * connected to that, and use it as default display */ + ovl = omap_dss_get_overlay(0); + if (ovl->manager && ovl->manager->device) { + def_display = ovl->manager->device; + } else { + dev_warn(&pdev->dev, "cannot find default display\n"); + def_display = NULL; + } + + if (def_display) { +#ifndef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + u16 w, h; +#endif + r = def_display->enable(def_display); + if (r) + dev_warn(fbdev->dev, "Failed to enable display '%s'\n", + def_display->name); + + /* set the update mode */ + if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { +#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + if (def_display->enable_te) + def_display->enable_te(def_display, 1); + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); +#else /* MANUAL_UPDATE */ + if (def_display->enable_te) + def_display->enable_te(def_display, 0); + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_MANUAL); + + def_display->get_resolution(def_display, &w, &h); + def_display->update(def_display, 0, 0, w, h); +#endif + } else { + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + } + } + + return 0; + +cleanup: + omapfb_free_resources(fbdev); +err0: + dev_err(&pdev->dev, "failed to setup omapfb\n"); + return r; +} + +static int omapfb_remove(struct platform_device *pdev) +{ + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + + /* FIXME: wait till completion of pending events */ + + omapfb_remove_sysfs(fbdev); + + omapfb_free_resources(fbdev); + + return 0; +} + +static struct platform_driver omapfb_driver = { + .probe = omapfb_probe, + .remove = omapfb_remove, + .driver = { + .name = "omapfb", + .owner = THIS_MODULE, + }, +}; + +static int __init omapfb_init(void) +{ + DBG("omapfb_init\n"); + + if (platform_driver_register(&omapfb_driver)) { + printk(KERN_ERR "failed to register omapfb driver\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit omapfb_exit(void) +{ + DBG("omapfb_exit\n"); + platform_driver_unregister(&omapfb_driver); +} + +module_param_named(mode, def_mode, charp, 0); +module_param_named(vram, def_vram, charp, 0); +module_param_named(rotate, def_rotate, int, 0); +module_param_named(vrfb, def_vrfb, bool, 0); +module_param_named(mirror, def_mirror, bool, 0); + +/* late_initcall to let panel/ctrl drivers loaded first. + * I guess better option would be a more dynamic approach, + * so that omapfb reacts to new panels when they are loaded */ +late_initcall(omapfb_init); +/*module_init(omapfb_init);*/ +module_exit(omapfb_exit); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("OMAP2/3 Framebuffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c new file mode 100644 index 000000000000..62bb88f5c192 --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -0,0 +1,507 @@ +/* + * linux/drivers/video/omap2/omapfb-sysfs.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/fb.h> +#include <linux/sysfs.h> +#include <linux/device.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/omapfb.h> + +#include <plat/display.h> +#include <plat/vrfb.h> + +#include "omapfb.h" + +static ssize_t show_rotate_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type); +} + +static ssize_t store_rotate_type(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + enum omap_dss_rotation_type rot_type; + int r; + + rot_type = simple_strtoul(buf, NULL, 0); + + if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB) + return -EINVAL; + + lock_fb_info(fbi); + + r = 0; + if (rot_type == ofbi->rotation_type) + goto out; + + if (ofbi->region.size) { + r = -EBUSY; + goto out; + } + + ofbi->rotation_type = rot_type; + + /* + * Since the VRAM for this FB is not allocated at the moment we don't + * need to do any further parameter checking at this point. + */ +out: + unlock_fb_info(fbi); + + return r ? r : count; +} + + +static ssize_t show_mirror(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror); +} + +static ssize_t store_mirror(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + bool mirror; + int r; + struct fb_var_screeninfo new_var; + + mirror = simple_strtoul(buf, NULL, 0); + + if (mirror != 0 && mirror != 1) + return -EINVAL; + + lock_fb_info(fbi); + + ofbi->mirror = mirror; + + memcpy(&new_var, &fbi->var, sizeof(new_var)); + r = check_fb_var(fbi, &new_var); + if (r) + goto out; + memcpy(&fbi->var, &new_var, sizeof(fbi->var)); + + set_fb_fix(fbi); + + r = omapfb_apply_changes(fbi, 0); + if (r) + goto out; + + r = count; +out: + unlock_fb_info(fbi); + + return r; +} + +static ssize_t show_overlays(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + ssize_t l = 0; + int t; + + omapfb_lock(fbdev); + lock_fb_info(fbi); + + for (t = 0; t < ofbi->num_overlays; t++) { + struct omap_overlay *ovl = ofbi->overlays[t]; + int ovlnum; + + for (ovlnum = 0; ovlnum < fbdev->num_overlays; ++ovlnum) + if (ovl == fbdev->overlays[ovlnum]) + break; + + l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", + t == 0 ? "" : ",", ovlnum); + } + + l += snprintf(buf + l, PAGE_SIZE - l, "\n"); + + unlock_fb_info(fbi); + omapfb_unlock(fbdev); + + return l; +} + +static struct omapfb_info *get_overlay_fb(struct omapfb2_device *fbdev, + struct omap_overlay *ovl) +{ + int i, t; + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + + for (t = 0; t < ofbi->num_overlays; t++) { + if (ofbi->overlays[t] == ovl) + return ofbi; + } + } + + return NULL; +} + +static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB]; + struct omap_overlay *ovl; + int num_ovls, r, i; + int len; + bool added = false; + + num_ovls = 0; + + len = strlen(buf); + if (buf[len - 1] == '\n') + len = len - 1; + + omapfb_lock(fbdev); + lock_fb_info(fbi); + + if (len > 0) { + char *p = (char *)buf; + int ovlnum; + + while (p < buf + len) { + int found; + if (num_ovls == OMAPFB_MAX_OVL_PER_FB) { + r = -EINVAL; + goto out; + } + + ovlnum = simple_strtoul(p, &p, 0); + if (ovlnum > fbdev->num_overlays) { + r = -EINVAL; + goto out; + } + + found = 0; + for (i = 0; i < num_ovls; ++i) { + if (ovls[i] == fbdev->overlays[ovlnum]) { + found = 1; + break; + } + } + + if (!found) + ovls[num_ovls++] = fbdev->overlays[ovlnum]; + + p++; + } + } + + for (i = 0; i < num_ovls; ++i) { + struct omapfb_info *ofbi2 = get_overlay_fb(fbdev, ovls[i]); + if (ofbi2 && ofbi2 != ofbi) { + dev_err(fbdev->dev, "overlay already in use\n"); + r = -EINVAL; + goto out; + } + } + + /* detach unused overlays */ + for (i = 0; i < ofbi->num_overlays; ++i) { + int t, found; + + ovl = ofbi->overlays[i]; + + found = 0; + + for (t = 0; t < num_ovls; ++t) { + if (ovl == ovls[t]) { + found = 1; + break; + } + } + + if (found) + continue; + + DBG("detaching %d\n", ofbi->overlays[i]->id); + + omapfb_overlay_enable(ovl, 0); + + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + for (t = i + 1; t < ofbi->num_overlays; t++) { + ofbi->rotation[t-1] = ofbi->rotation[t]; + ofbi->overlays[t-1] = ofbi->overlays[t]; + } + + ofbi->num_overlays--; + i--; + } + + for (i = 0; i < num_ovls; ++i) { + int t, found; + + ovl = ovls[i]; + + found = 0; + + for (t = 0; t < ofbi->num_overlays; ++t) { + if (ovl == ofbi->overlays[t]) { + found = 1; + break; + } + } + + if (found) + continue; + ofbi->rotation[ofbi->num_overlays] = 0; + ofbi->overlays[ofbi->num_overlays++] = ovl; + + added = true; + } + + if (added) { + r = omapfb_apply_changes(fbi, 0); + if (r) + goto out; + } + + r = count; +out: + unlock_fb_info(fbi); + omapfb_unlock(fbdev); + + return r; +} + +static ssize_t show_overlays_rotate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + ssize_t l = 0; + int t; + + lock_fb_info(fbi); + + for (t = 0; t < ofbi->num_overlays; t++) { + l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", + t == 0 ? "" : ",", ofbi->rotation[t]); + } + + l += snprintf(buf + l, PAGE_SIZE - l, "\n"); + + unlock_fb_info(fbi); + + return l; +} + +static ssize_t store_overlays_rotate(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + int num_ovls = 0, r, i; + int len; + bool changed = false; + u8 rotation[OMAPFB_MAX_OVL_PER_FB]; + + len = strlen(buf); + if (buf[len - 1] == '\n') + len = len - 1; + + lock_fb_info(fbi); + + if (len > 0) { + char *p = (char *)buf; + + while (p < buf + len) { + int rot; + + if (num_ovls == ofbi->num_overlays) { + r = -EINVAL; + goto out; + } + + rot = simple_strtoul(p, &p, 0); + if (rot < 0 || rot > 3) { + r = -EINVAL; + goto out; + } + + if (ofbi->rotation[num_ovls] != rot) + changed = true; + + rotation[num_ovls++] = rot; + + p++; + } + } + + if (num_ovls != ofbi->num_overlays) { + r = -EINVAL; + goto out; + } + + if (changed) { + for (i = 0; i < num_ovls; ++i) + ofbi->rotation[i] = rotation[i]; + + r = omapfb_apply_changes(fbi, 0); + if (r) + goto out; + + /* FIXME error handling? */ + } + + r = count; +out: + unlock_fb_info(fbi); + + return r; +} + +static ssize_t show_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size); +} + +static ssize_t store_size(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + unsigned long size; + int r; + int i; + + size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0)); + + lock_fb_info(fbi); + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->info.enabled) { + r = -EBUSY; + goto out; + } + } + + if (size != ofbi->region.size) { + r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type); + if (r) { + dev_err(dev, "realloc fbmem failed\n"); + goto out; + } + } + + r = count; +out: + unlock_fb_info(fbi); + + return r; +} + +static ssize_t show_phys(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr); +} + +static ssize_t show_virt(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr); +} + +static struct device_attribute omapfb_attrs[] = { + __ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type, + store_rotate_type), + __ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror), + __ATTR(size, S_IRUGO | S_IWUSR, show_size, store_size), + __ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays), + __ATTR(overlays_rotate, S_IRUGO | S_IWUSR, show_overlays_rotate, + store_overlays_rotate), + __ATTR(phys_addr, S_IRUGO, show_phys, NULL), + __ATTR(virt_addr, S_IRUGO, show_virt, NULL), +}; + +int omapfb_create_sysfs(struct omapfb2_device *fbdev) +{ + int i; + int r; + + DBG("create sysfs for fbs\n"); + for (i = 0; i < fbdev->num_fbs; i++) { + int t; + for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) { + r = device_create_file(fbdev->fbs[i]->dev, + &omapfb_attrs[t]); + + if (r) { + dev_err(fbdev->dev, "failed to create sysfs " + "file\n"); + return r; + } + } + } + + return 0; +} + +void omapfb_remove_sysfs(struct omapfb2_device *fbdev) +{ + int i, t; + + DBG("remove sysfs for fbs\n"); + for (i = 0; i < fbdev->num_fbs; i++) { + for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) + device_remove_file(fbdev->fbs[i]->dev, + &omapfb_attrs[t]); + } +} + diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h new file mode 100644 index 000000000000..f7c9c739e5ef --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -0,0 +1,146 @@ +/* + * linux/drivers/video/omap2/omapfb.h + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ +#define __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ + +#ifdef CONFIG_FB_OMAP2_DEBUG_SUPPORT +#define DEBUG +#endif + +#include <plat/display.h> + +#ifdef DEBUG +extern unsigned int omapfb_debug; +#define DBG(format, ...) \ + if (omapfb_debug) \ + printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +#define FB2OFB(fb_info) ((struct omapfb_info *)(fb_info->par)) + +/* max number of overlays to which a framebuffer data can be direct */ +#define OMAPFB_MAX_OVL_PER_FB 3 + +struct omapfb2_mem_region { + u32 paddr; + void __iomem *vaddr; + struct vrfb vrfb; + unsigned long size; + u8 type; /* OMAPFB_PLANE_MEM_* */ + bool alloc; /* allocated by the driver */ + bool map; /* kernel mapped by the driver */ +}; + +/* appended to fb_info */ +struct omapfb_info { + int id; + struct omapfb2_mem_region region; + atomic_t map_count; + int num_overlays; + struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB]; + struct omapfb2_device *fbdev; + enum omap_dss_rotation_type rotation_type; + u8 rotation[OMAPFB_MAX_OVL_PER_FB]; + bool mirror; +}; + +struct omapfb2_device { + struct device *dev; + struct mutex mtx; + + u32 pseudo_palette[17]; + + int state; + + unsigned num_fbs; + struct fb_info *fbs[10]; + + unsigned num_displays; + struct omap_dss_device *displays[10]; + unsigned num_overlays; + struct omap_overlay *overlays[10]; + unsigned num_managers; + struct omap_overlay_manager *managers[10]; +}; + +struct omapfb_colormode { + enum omap_color_mode dssmode; + u32 bits_per_pixel; + u32 nonstd; + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +void set_fb_fix(struct fb_info *fbi); +int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var); +int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type); +int omapfb_apply_changes(struct fb_info *fbi, int init); + +int omapfb_create_sysfs(struct omapfb2_device *fbdev); +void omapfb_remove_sysfs(struct omapfb2_device *fbdev); + +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg); + +int dss_mode_to_fb_mode(enum omap_color_mode dssmode, + struct fb_var_screeninfo *var); + +/* find the display connected to this fb, if any */ +static inline struct omap_dss_device *fb2display(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + int i; + + /* XXX: returns the display connected to first attached overlay */ + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->manager) + return ofbi->overlays[i]->manager->device; + } + + return NULL; +} + +static inline void omapfb_lock(struct omapfb2_device *fbdev) +{ + mutex_lock(&fbdev->mtx); +} + +static inline void omapfb_unlock(struct omapfb2_device *fbdev) +{ + mutex_unlock(&fbdev->mtx); +} + +static inline int omapfb_overlay_enable(struct omap_overlay *ovl, + int enable) +{ + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + info.enabled = enable; + return ovl->set_overlay_info(ovl, &info); +} + +#endif diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c new file mode 100644 index 000000000000..55a4de5e5d10 --- /dev/null +++ b/drivers/video/omap2/vram.c @@ -0,0 +1,655 @@ +/* + * VRAM manager for OMAP + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*#define DEBUG*/ + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/list.h> +#include <linux/seq_file.h> +#include <linux/bootmem.h> +#include <linux/completion.h> +#include <linux/debugfs.h> +#include <linux/jiffies.h> +#include <linux/module.h> + +#include <asm/setup.h> + +#include <plat/sram.h> +#include <plat/vram.h> +#include <plat/dma.h> + +#ifdef DEBUG +#define DBG(format, ...) pr_debug("VRAM: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +#define OMAP2_SRAM_START 0x40200000 +/* Maximum size, in reality this is smaller if SRAM is partially locked. */ +#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ + +/* postponed regions are used to temporarily store region information at boot + * time when we cannot yet allocate the region list */ +#define MAX_POSTPONED_REGIONS 10 + +static bool vram_initialized; +static int postponed_cnt; +static struct { + unsigned long paddr; + size_t size; +} postponed_regions[MAX_POSTPONED_REGIONS]; + +struct vram_alloc { + struct list_head list; + unsigned long paddr; + unsigned pages; +}; + +struct vram_region { + struct list_head list; + struct list_head alloc_list; + unsigned long paddr; + unsigned pages; +}; + +static DEFINE_MUTEX(region_mutex); +static LIST_HEAD(region_list); + +static inline int region_mem_type(unsigned long paddr) +{ + if (paddr >= OMAP2_SRAM_START && + paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) + return OMAP_VRAM_MEMTYPE_SRAM; + else + return OMAP_VRAM_MEMTYPE_SDRAM; +} + +static struct vram_region *omap_vram_create_region(unsigned long paddr, + unsigned pages) +{ + struct vram_region *rm; + + rm = kzalloc(sizeof(*rm), GFP_KERNEL); + + if (rm) { + INIT_LIST_HEAD(&rm->alloc_list); + rm->paddr = paddr; + rm->pages = pages; + } + + return rm; +} + +#if 0 +static void omap_vram_free_region(struct vram_region *vr) +{ + list_del(&vr->list); + kfree(vr); +} +#endif + +static struct vram_alloc *omap_vram_create_allocation(struct vram_region *vr, + unsigned long paddr, unsigned pages) +{ + struct vram_alloc *va; + struct vram_alloc *new; + + new = kzalloc(sizeof(*va), GFP_KERNEL); + + if (!new) + return NULL; + + new->paddr = paddr; + new->pages = pages; + + list_for_each_entry(va, &vr->alloc_list, list) { + if (va->paddr > new->paddr) + break; + } + + list_add_tail(&new->list, &va->list); + + return new; +} + +static void omap_vram_free_allocation(struct vram_alloc *va) +{ + list_del(&va->list); + kfree(va); +} + +int omap_vram_add_region(unsigned long paddr, size_t size) +{ + struct vram_region *rm; + unsigned pages; + + if (vram_initialized) { + DBG("adding region paddr %08lx size %d\n", + paddr, size); + + size &= PAGE_MASK; + pages = size >> PAGE_SHIFT; + + rm = omap_vram_create_region(paddr, pages); + if (rm == NULL) + return -ENOMEM; + + list_add(&rm->list, ®ion_list); + } else { + if (postponed_cnt == MAX_POSTPONED_REGIONS) + return -ENOMEM; + + postponed_regions[postponed_cnt].paddr = paddr; + postponed_regions[postponed_cnt].size = size; + + ++postponed_cnt; + } + return 0; +} + +int omap_vram_free(unsigned long paddr, size_t size) +{ + struct vram_region *rm; + struct vram_alloc *alloc; + unsigned start, end; + + DBG("free mem paddr %08lx size %d\n", paddr, size); + + size = PAGE_ALIGN(size); + + mutex_lock(®ion_mutex); + + list_for_each_entry(rm, ®ion_list, list) { + list_for_each_entry(alloc, &rm->alloc_list, list) { + start = alloc->paddr; + end = alloc->paddr + (alloc->pages >> PAGE_SHIFT); + + if (start >= paddr && end < paddr + size) + goto found; + } + } + + mutex_unlock(®ion_mutex); + return -EINVAL; + +found: + omap_vram_free_allocation(alloc); + + mutex_unlock(®ion_mutex); + return 0; +} +EXPORT_SYMBOL(omap_vram_free); + +static int _omap_vram_reserve(unsigned long paddr, unsigned pages) +{ + struct vram_region *rm; + struct vram_alloc *alloc; + size_t size; + + size = pages << PAGE_SHIFT; + + list_for_each_entry(rm, ®ion_list, list) { + unsigned long start, end; + + DBG("checking region %lx %d\n", rm->paddr, rm->pages); + + if (region_mem_type(rm->paddr) != region_mem_type(paddr)) + continue; + + start = rm->paddr; + end = start + (rm->pages << PAGE_SHIFT) - 1; + if (start > paddr || end < paddr + size - 1) + continue; + + DBG("block ok, checking allocs\n"); + + list_for_each_entry(alloc, &rm->alloc_list, list) { + end = alloc->paddr - 1; + + if (start <= paddr && end >= paddr + size - 1) + goto found; + + start = alloc->paddr + (alloc->pages << PAGE_SHIFT); + } + + end = rm->paddr + (rm->pages << PAGE_SHIFT) - 1; + + if (!(start <= paddr && end >= paddr + size - 1)) + continue; +found: + DBG("found area start %lx, end %lx\n", start, end); + + if (omap_vram_create_allocation(rm, paddr, pages) == NULL) + return -ENOMEM; + + return 0; + } + + return -ENOMEM; +} + +int omap_vram_reserve(unsigned long paddr, size_t size) +{ + unsigned pages; + int r; + + DBG("reserve mem paddr %08lx size %d\n", paddr, size); + + size = PAGE_ALIGN(size); + pages = size >> PAGE_SHIFT; + + mutex_lock(®ion_mutex); + + r = _omap_vram_reserve(paddr, pages); + + mutex_unlock(®ion_mutex); + + return r; +} +EXPORT_SYMBOL(omap_vram_reserve); + +static void _omap_vram_dma_cb(int lch, u16 ch_status, void *data) +{ + struct completion *compl = data; + complete(compl); +} + +static int _omap_vram_clear(u32 paddr, unsigned pages) +{ + struct completion compl; + unsigned elem_count; + unsigned frame_count; + int r; + int lch; + + init_completion(&compl); + + r = omap_request_dma(OMAP_DMA_NO_DEVICE, "VRAM DMA", + _omap_vram_dma_cb, + &compl, &lch); + if (r) { + pr_err("VRAM: request_dma failed for memory clear\n"); + return -EBUSY; + } + + elem_count = pages * PAGE_SIZE / 4; + frame_count = 1; + + omap_set_dma_transfer_params(lch, OMAP_DMA_DATA_TYPE_S32, + elem_count, frame_count, + OMAP_DMA_SYNC_ELEMENT, + 0, 0); + + omap_set_dma_dest_params(lch, 0, OMAP_DMA_AMODE_POST_INC, + paddr, 0, 0); + + omap_set_dma_color_mode(lch, OMAP_DMA_CONSTANT_FILL, 0x000000); + + omap_start_dma(lch); + + if (wait_for_completion_timeout(&compl, msecs_to_jiffies(1000)) == 0) { + omap_stop_dma(lch); + pr_err("VRAM: dma timeout while clearing memory\n"); + r = -EIO; + goto err; + } + + r = 0; +err: + omap_free_dma(lch); + + return r; +} + +static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) +{ + struct vram_region *rm; + struct vram_alloc *alloc; + + list_for_each_entry(rm, ®ion_list, list) { + unsigned long start, end; + + DBG("checking region %lx %d\n", rm->paddr, rm->pages); + + if (region_mem_type(rm->paddr) != mtype) + continue; + + start = rm->paddr; + + list_for_each_entry(alloc, &rm->alloc_list, list) { + end = alloc->paddr; + + if (end - start >= pages << PAGE_SHIFT) + goto found; + + start = alloc->paddr + (alloc->pages << PAGE_SHIFT); + } + + end = rm->paddr + (rm->pages << PAGE_SHIFT); +found: + if (end - start < pages << PAGE_SHIFT) + continue; + + DBG("found %lx, end %lx\n", start, end); + + alloc = omap_vram_create_allocation(rm, start, pages); + if (alloc == NULL) + return -ENOMEM; + + *paddr = start; + + _omap_vram_clear(start, pages); + + return 0; + } + + return -ENOMEM; +} + +int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr) +{ + unsigned pages; + int r; + + BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size); + + DBG("alloc mem type %d size %d\n", mtype, size); + + size = PAGE_ALIGN(size); + pages = size >> PAGE_SHIFT; + + mutex_lock(®ion_mutex); + + r = _omap_vram_alloc(mtype, pages, paddr); + + mutex_unlock(®ion_mutex); + + return r; +} +EXPORT_SYMBOL(omap_vram_alloc); + +void omap_vram_get_info(unsigned long *vram, + unsigned long *free_vram, + unsigned long *largest_free_block) +{ + struct vram_region *vr; + struct vram_alloc *va; + + *vram = 0; + *free_vram = 0; + *largest_free_block = 0; + + mutex_lock(®ion_mutex); + + list_for_each_entry(vr, ®ion_list, list) { + unsigned free; + unsigned long pa; + + pa = vr->paddr; + *vram += vr->pages << PAGE_SHIFT; + + list_for_each_entry(va, &vr->alloc_list, list) { + free = va->paddr - pa; + *free_vram += free; + if (free > *largest_free_block) + *largest_free_block = free; + pa = va->paddr + (va->pages << PAGE_SHIFT); + } + + free = vr->paddr + (vr->pages << PAGE_SHIFT) - pa; + *free_vram += free; + if (free > *largest_free_block) + *largest_free_block = free; + } + + mutex_unlock(®ion_mutex); +} +EXPORT_SYMBOL(omap_vram_get_info); + +#if defined(CONFIG_DEBUG_FS) +static int vram_debug_show(struct seq_file *s, void *unused) +{ + struct vram_region *vr; + struct vram_alloc *va; + unsigned size; + + mutex_lock(®ion_mutex); + + list_for_each_entry(vr, ®ion_list, list) { + size = vr->pages << PAGE_SHIFT; + seq_printf(s, "%08lx-%08lx (%d bytes)\n", + vr->paddr, vr->paddr + size - 1, + size); + + list_for_each_entry(va, &vr->alloc_list, list) { + size = va->pages << PAGE_SHIFT; + seq_printf(s, " %08lx-%08lx (%d bytes)\n", + va->paddr, va->paddr + size - 1, + size); + } + } + + mutex_unlock(®ion_mutex); + + return 0; +} + +static int vram_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, vram_debug_show, inode->i_private); +} + +static const struct file_operations vram_debug_fops = { + .open = vram_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init omap_vram_create_debugfs(void) +{ + struct dentry *d; + + d = debugfs_create_file("vram", S_IRUGO, NULL, + NULL, &vram_debug_fops); + if (IS_ERR(d)) + return PTR_ERR(d); + + return 0; +} +#endif + +static __init int omap_vram_init(void) +{ + int i; + + vram_initialized = 1; + + for (i = 0; i < postponed_cnt; i++) + omap_vram_add_region(postponed_regions[i].paddr, + postponed_regions[i].size); + +#ifdef CONFIG_DEBUG_FS + if (omap_vram_create_debugfs()) + pr_err("VRAM: Failed to create debugfs file\n"); +#endif + + return 0; +} + +arch_initcall(omap_vram_init); + +/* boottime vram alloc stuff */ + +/* set from board file */ +static u32 omap_vram_sram_start __initdata; +static u32 omap_vram_sram_size __initdata; + +/* set from board file */ +static u32 omap_vram_sdram_start __initdata; +static u32 omap_vram_sdram_size __initdata; + +/* set from kernel cmdline */ +static u32 omap_vram_def_sdram_size __initdata; +static u32 omap_vram_def_sdram_start __initdata; + +static void __init omap_vram_early_vram(char **p) +{ + omap_vram_def_sdram_size = memparse(*p, p); + if (**p == ',') + omap_vram_def_sdram_start = simple_strtoul((*p) + 1, p, 16); +} +__early_param("vram=", omap_vram_early_vram); + +/* + * Called from map_io. We need to call to this early enough so that we + * can reserve the fixed SDRAM regions before VM could get hold of them. + */ +void __init omap_vram_reserve_sdram(void) +{ + struct bootmem_data *bdata; + unsigned long sdram_start, sdram_size; + u32 paddr; + u32 size = 0; + + /* cmdline arg overrides the board file definition */ + if (omap_vram_def_sdram_size) { + size = omap_vram_def_sdram_size; + paddr = omap_vram_def_sdram_start; + } + + if (!size) { + size = omap_vram_sdram_size; + paddr = omap_vram_sdram_start; + } + +#ifdef CONFIG_OMAP2_VRAM_SIZE + if (!size) { + size = CONFIG_OMAP2_VRAM_SIZE * 1024 * 1024; + paddr = 0; + } +#endif + + if (!size) + return; + + size = PAGE_ALIGN(size); + + bdata = NODE_DATA(0)->bdata; + sdram_start = bdata->node_min_pfn << PAGE_SHIFT; + sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start; + + if (paddr) { + if ((paddr & ~PAGE_MASK) || paddr < sdram_start || + paddr + size > sdram_start + sdram_size) { + pr_err("Illegal SDRAM region for VRAM\n"); + return; + } + + if (reserve_bootmem(paddr, size, BOOTMEM_EXCLUSIVE) < 0) { + pr_err("FB: failed to reserve VRAM\n"); + return; + } + } else { + if (size > sdram_size) { + pr_err("Illegal SDRAM size for VRAM\n"); + return; + } + + paddr = virt_to_phys(alloc_bootmem_pages(size)); + BUG_ON(paddr & ~PAGE_MASK); + } + + omap_vram_add_region(paddr, size); + + pr_info("Reserving %u bytes SDRAM for VRAM\n", size); +} + +/* + * Called at sram init time, before anything is pushed to the SRAM stack. + * Because of the stack scheme, we will allocate everything from the + * start of the lowest address region to the end of SRAM. This will also + * include padding for page alignment and possible holes between regions. + * + * As opposed to the SDRAM case, we'll also do any dynamic allocations at + * this point, since the driver built as a module would have problem with + * freeing / reallocating the regions. + */ +unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart, + unsigned long sram_vstart, + unsigned long sram_size, + unsigned long pstart_avail, + unsigned long size_avail) +{ + unsigned long pend_avail; + unsigned long reserved; + u32 paddr; + u32 size; + + paddr = omap_vram_sram_start; + size = omap_vram_sram_size; + + if (!size) + return 0; + + reserved = 0; + pend_avail = pstart_avail + size_avail; + + if (!paddr) { + /* Dynamic allocation */ + if ((size_avail & PAGE_MASK) < size) { + pr_err("Not enough SRAM for VRAM\n"); + return 0; + } + size_avail = (size_avail - size) & PAGE_MASK; + paddr = pstart_avail + size_avail; + } + + if (paddr < sram_pstart || + paddr + size > sram_pstart + sram_size) { + pr_err("Illegal SRAM region for VRAM\n"); + return 0; + } + + /* Reserve everything above the start of the region. */ + if (pend_avail - paddr > reserved) + reserved = pend_avail - paddr; + size_avail = pend_avail - reserved - pstart_avail; + + omap_vram_add_region(paddr, size); + + if (reserved) + pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved); + + return reserved; +} + +void __init omap_vram_set_sdram_vram(u32 size, u32 start) +{ + omap_vram_sdram_start = start; + omap_vram_sdram_size = size; +} + +void __init omap_vram_set_sram_vram(u32 size, u32 start) +{ + omap_vram_sram_start = start; + omap_vram_sram_size = size; +} diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c new file mode 100644 index 000000000000..fd2271600370 --- /dev/null +++ b/drivers/video/omap2/vrfb.c @@ -0,0 +1,315 @@ +/* + * VRFB Rotation Engine + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*#define DEBUG*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/bitops.h> +#include <linux/mutex.h> + +#include <mach/io.h> +#include <plat/vrfb.h> +#include <plat/sdrc.h> + +#ifdef DEBUG +#define DBG(format, ...) pr_debug("VRFB: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +#define SMS_ROT_VIRT_BASE(context, rot) \ + (((context >= 4) ? 0xD0000000 : 0x70000000) \ + + (0x4000000 * (context)) \ + + (0x1000000 * (rot))) + +#define OMAP_VRFB_SIZE (2048 * 2048 * 4) + +#define VRFB_PAGE_WIDTH_EXP 5 /* Assuming SDRAM pagesize= 1024 */ +#define VRFB_PAGE_HEIGHT_EXP 5 /* 1024 = 2^5 * 2^5 */ +#define VRFB_PAGE_WIDTH (1 << VRFB_PAGE_WIDTH_EXP) +#define VRFB_PAGE_HEIGHT (1 << VRFB_PAGE_HEIGHT_EXP) +#define SMS_IMAGEHEIGHT_OFFSET 16 +#define SMS_IMAGEWIDTH_OFFSET 0 +#define SMS_PH_OFFSET 8 +#define SMS_PW_OFFSET 4 +#define SMS_PS_OFFSET 0 + +#define VRFB_NUM_CTXS 12 +/* bitmap of reserved contexts */ +static unsigned long ctx_map; + +static DEFINE_MUTEX(ctx_lock); + +/* + * Access to this happens from client drivers or the PM core after wake-up. + * For the first case we require locking at the driver level, for the second + * we don't need locking, since no drivers will run until after the wake-up + * has finished. + */ +static struct { + u32 physical_ba; + u32 control; + u32 size; +} vrfb_hw_context[VRFB_NUM_CTXS]; + +static inline void restore_hw_context(int ctx) +{ + omap2_sms_write_rot_control(vrfb_hw_context[ctx].control, ctx); + omap2_sms_write_rot_size(vrfb_hw_context[ctx].size, ctx); + omap2_sms_write_rot_physical_ba(vrfb_hw_context[ctx].physical_ba, ctx); +} + +static u32 get_image_width_roundup(u16 width, u8 bytespp) +{ + unsigned long stride = width * bytespp; + unsigned long ceil_pages_per_stride = (stride / VRFB_PAGE_WIDTH) + + (stride % VRFB_PAGE_WIDTH != 0); + + return ceil_pages_per_stride * VRFB_PAGE_WIDTH / bytespp; +} + +/* + * This the extra space needed in the VRFB physical area for VRFB to safely wrap + * any memory accesses to the invisible part of the virtual view to the physical + * area. + */ +static inline u32 get_extra_physical_size(u16 image_width_roundup, u8 bytespp) +{ + return (OMAP_VRFB_LINE_LEN - image_width_roundup) * VRFB_PAGE_HEIGHT * + bytespp; +} + +void omap_vrfb_restore_context(void) +{ + int i; + unsigned long map = ctx_map; + + for (i = ffs(map); i; i = ffs(map)) { + /* i=1..32 */ + i--; + map &= ~(1 << i); + restore_hw_context(i); + } +} + +void omap_vrfb_adjust_size(u16 *width, u16 *height, + u8 bytespp) +{ + *width = ALIGN(*width * bytespp, VRFB_PAGE_WIDTH) / bytespp; + *height = ALIGN(*height, VRFB_PAGE_HEIGHT); +} +EXPORT_SYMBOL(omap_vrfb_adjust_size); + +u32 omap_vrfb_min_phys_size(u16 width, u16 height, u8 bytespp) +{ + unsigned long image_width_roundup = get_image_width_roundup(width, + bytespp); + + if (image_width_roundup > OMAP_VRFB_LINE_LEN) + return 0; + + return (width * height * bytespp) + get_extra_physical_size( + image_width_roundup, bytespp); +} +EXPORT_SYMBOL(omap_vrfb_min_phys_size); + +u16 omap_vrfb_max_height(u32 phys_size, u16 width, u8 bytespp) +{ + unsigned long image_width_roundup = get_image_width_roundup(width, + bytespp); + unsigned long height; + unsigned long extra; + + if (image_width_roundup > OMAP_VRFB_LINE_LEN) + return 0; + + extra = get_extra_physical_size(image_width_roundup, bytespp); + + if (phys_size < extra) + return 0; + + height = (phys_size - extra) / (width * bytespp); + + /* Virtual views provided by VRFB are limited to 2048x2048. */ + return min_t(unsigned long, height, 2048); +} +EXPORT_SYMBOL(omap_vrfb_max_height); + +void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr, + u16 width, u16 height, + unsigned bytespp, bool yuv_mode) +{ + unsigned pixel_size_exp; + u16 vrfb_width; + u16 vrfb_height; + u8 ctx = vrfb->context; + u32 size; + u32 control; + + DBG("omapfb_set_vrfb(%d, %lx, %dx%d, %d, %d)\n", ctx, paddr, + width, height, bytespp, yuv_mode); + + /* For YUV2 and UYVY modes VRFB needs to handle pixels a bit + * differently. See TRM. */ + if (yuv_mode) { + bytespp *= 2; + width /= 2; + } + + if (bytespp == 4) + pixel_size_exp = 2; + else if (bytespp == 2) + pixel_size_exp = 1; + else + BUG(); + + vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp; + vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT); + + DBG("vrfb w %u, h %u bytespp %d\n", vrfb_width, vrfb_height, bytespp); + + size = vrfb_width << SMS_IMAGEWIDTH_OFFSET; + size |= vrfb_height << SMS_IMAGEHEIGHT_OFFSET; + + control = pixel_size_exp << SMS_PS_OFFSET; + control |= VRFB_PAGE_WIDTH_EXP << SMS_PW_OFFSET; + control |= VRFB_PAGE_HEIGHT_EXP << SMS_PH_OFFSET; + + vrfb_hw_context[ctx].physical_ba = paddr; + vrfb_hw_context[ctx].size = size; + vrfb_hw_context[ctx].control = control; + + omap2_sms_write_rot_physical_ba(paddr, ctx); + omap2_sms_write_rot_size(size, ctx); + omap2_sms_write_rot_control(control, ctx); + + DBG("vrfb offset pixels %d, %d\n", + vrfb_width - width, vrfb_height - height); + + vrfb->xres = width; + vrfb->yres = height; + vrfb->xoffset = vrfb_width - width; + vrfb->yoffset = vrfb_height - height; + vrfb->bytespp = bytespp; + vrfb->yuv_mode = yuv_mode; +} +EXPORT_SYMBOL(omap_vrfb_setup); + +int omap_vrfb_map_angle(struct vrfb *vrfb, u16 height, u8 rot) +{ + unsigned long size = height * OMAP_VRFB_LINE_LEN * vrfb->bytespp; + + vrfb->vaddr[rot] = ioremap_wc(vrfb->paddr[rot], size); + + if (!vrfb->vaddr[rot]) { + printk(KERN_ERR "vrfb: ioremap failed\n"); + return -ENOMEM; + } + + DBG("ioremapped vrfb area %d of size %lu into %p\n", rot, size, + vrfb->vaddr[rot]); + + return 0; +} +EXPORT_SYMBOL(omap_vrfb_map_angle); + +void omap_vrfb_release_ctx(struct vrfb *vrfb) +{ + int rot; + int ctx = vrfb->context; + + if (ctx == 0xff) + return; + + DBG("release ctx %d\n", ctx); + + mutex_lock(&ctx_lock); + + BUG_ON(!(ctx_map & (1 << ctx))); + + clear_bit(ctx, &ctx_map); + + for (rot = 0; rot < 4; ++rot) { + if (vrfb->paddr[rot]) { + release_mem_region(vrfb->paddr[rot], OMAP_VRFB_SIZE); + vrfb->paddr[rot] = 0; + } + } + + vrfb->context = 0xff; + + mutex_unlock(&ctx_lock); +} +EXPORT_SYMBOL(omap_vrfb_release_ctx); + +int omap_vrfb_request_ctx(struct vrfb *vrfb) +{ + int rot; + u32 paddr; + u8 ctx; + int r; + + DBG("request ctx\n"); + + mutex_lock(&ctx_lock); + + for (ctx = 0; ctx < VRFB_NUM_CTXS; ++ctx) + if ((ctx_map & (1 << ctx)) == 0) + break; + + if (ctx == VRFB_NUM_CTXS) { + pr_err("vrfb: no free contexts\n"); + r = -EBUSY; + goto out; + } + + DBG("found free ctx %d\n", ctx); + + set_bit(ctx, &ctx_map); + + memset(vrfb, 0, sizeof(*vrfb)); + + vrfb->context = ctx; + + for (rot = 0; rot < 4; ++rot) { + paddr = SMS_ROT_VIRT_BASE(ctx, rot); + if (!request_mem_region(paddr, OMAP_VRFB_SIZE, "vrfb")) { + pr_err("vrfb: failed to reserve VRFB " + "area for ctx %d, rotation %d\n", + ctx, rot * 90); + omap_vrfb_release_ctx(vrfb); + r = -ENOMEM; + goto out; + } + + vrfb->paddr[rot] = paddr; + + DBG("VRFB %d/%d: %lx\n", ctx, rot*90, vrfb->paddr[rot]); + } + + r = 0; +out: + mutex_unlock(&ctx_lock); + return r; +} +EXPORT_SYMBOL(omap_vrfb_request_ctx); diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index 84d8327e47db..75285d3f393c 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -687,6 +687,7 @@ static int __init pxa168fb_probe(struct platform_device *pdev) } info->fix.smem_start = (unsigned long)fbi->fb_start_dma; + set_graphics_start(info, 0, 0); /* * Set video mode according to platform data. diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 1820c4a24434..f58a3aae6ea6 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -80,7 +80,8 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); -static void setup_base_frame(struct pxafb_info *fbi, int branch); +static void setup_base_frame(struct pxafb_info *fbi, + struct fb_var_screeninfo *var, int branch); static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, unsigned long offset, size_t size); @@ -397,6 +398,7 @@ static void pxafb_setmode(struct fb_var_screeninfo *var, var->lower_margin = mode->lower_margin; var->sync = mode->sync; var->grayscale = mode->cmap_greyscale; + var->transp.length = mode->transparency; /* set the initial RGBA bitfields */ pxafb_set_pixfmt(var, mode->depth); @@ -531,12 +533,22 @@ static int pxafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { struct pxafb_info *fbi = (struct pxafb_info *)info; + struct fb_var_screeninfo newvar; int dma = DMA_MAX + DMA_BASE; if (fbi->state != C_ENABLE) return 0; - setup_base_frame(fbi, 1); + /* Only take .xoffset, .yoffset and .vmode & FB_VMODE_YWRAP from what + * was passed in and copy the rest from the old screeninfo. + */ + memcpy(&newvar, &fbi->fb.var, sizeof(newvar)); + newvar.xoffset = var->xoffset; + newvar.yoffset = var->yoffset; + newvar.vmode &= ~FB_VMODE_YWRAP; + newvar.vmode |= var->vmode & FB_VMODE_YWRAP; + + setup_base_frame(fbi, &newvar, 1); if (fbi->lccr0 & LCCR0_SDS) lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1); @@ -1052,9 +1064,10 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, return 0; } -static void setup_base_frame(struct pxafb_info *fbi, int branch) +static void setup_base_frame(struct pxafb_info *fbi, + struct fb_var_screeninfo *var, + int branch) { - struct fb_var_screeninfo *var = &fbi->fb.var; struct fb_fix_screeninfo *fix = &fbi->fb.fix; int nbytes, dma, pal, bpp = var->bits_per_pixel; unsigned long offset; @@ -1332,7 +1345,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, #endif setup_parallel_timing(fbi, var); - setup_base_frame(fbi, 0); + setup_base_frame(fbi, var, 0); fbi->reg_lccr0 = fbi->lccr0 | (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c index bba53714a7b1..f86012239bff 100644 --- a/drivers/video/sgivwfb.c +++ b/drivers/video/sgivwfb.c @@ -260,13 +260,13 @@ static int sgivwfb_check_var(struct fb_var_screeninfo *var, var->grayscale = 0; /* No grayscale for now */ /* determine valid resolution and timing */ - for (min_mode = 0; min_mode < DBE_VT_SIZE; min_mode++) { + for (min_mode = 0; min_mode < ARRAY_SIZE(dbeVTimings); min_mode++) { if (dbeVTimings[min_mode].width >= var->xres && dbeVTimings[min_mode].height >= var->yres) break; } - if (min_mode == DBE_VT_SIZE) + if (min_mode == ARRAY_SIZE(dbeVTimings)) return -EINVAL; /* Resolution to high */ /* XXX FIXME - should try to pick best refresh rate */ diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 3ad5157f9899..b4b5de930cf5 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -281,18 +281,34 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; - unsigned int nr_pages; /* enable clocks before accessing hardware */ sh_mobile_lcdc_clk_on(ch->lcdc); - nr_pages = sh_mobile_lcdc_sginit(info, pagelist); - dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); - - /* trigger panel update */ - lcdc_write_chan(ch, LDSM2R, 1); - - dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + /* + * It's possible to get here without anything on the pagelist via + * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync() + * invocation. In the former case, the acceleration routines are + * stepped in to when using the framebuffer console causing the + * workqueue to be scheduled without any dirty pages on the list. + * + * Despite this, a panel update is still needed given that the + * acceleration routines have their own methods for writing in + * that still need to be updated. + * + * The fsync() and empty pagelist case could be optimized for, + * but we don't bother, as any application exhibiting such + * behaviour is fundamentally broken anyways. + */ + if (!list_empty(pagelist)) { + unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); + + /* trigger panel update */ + dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + lcdc_write_chan(ch, LDSM2R, 1); + dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + } else + lcdc_write_chan(ch, LDSM2R, 1); } static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) diff --git a/drivers/video/stifb.c b/drivers/video/stifb.c index 6120f0c526fe..876648e15e9d 100644 --- a/drivers/video/stifb.c +++ b/drivers/video/stifb.c @@ -756,9 +756,9 @@ hyperResetPlanes(struct stifb_info *fb, int enable) if (fb->info.var.bits_per_pixel == 32) controlPlaneReg = 0x04000F00; else - controlPlaneReg = 0x00000F00; /* 0x00000800 should be enought, but lets clear all 4 bits */ + controlPlaneReg = 0x00000F00; /* 0x00000800 should be enough, but lets clear all 4 bits */ else - controlPlaneReg = 0x00000F00; /* 0x00000100 should be enought, but lets clear all 4 bits */ + controlPlaneReg = 0x00000F00; /* 0x00000100 should be enough, but lets clear all 4 bits */ switch (enable) { case ENABLE: diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index ff43c8885028..980548390048 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -52,7 +52,7 @@ * * 0.1.3 (released 1999-11-02) added Attila's panning support, code * reorg, hwcursor address page size alignment - * (for mmaping both frame buffer and regs), + * (for mmapping both frame buffer and regs), * and my changes to get rid of hardcoded * VGA i/o register locations (uses PCI * configuration info now) diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c index c5c32b6b6e6c..67b36932212b 100644 --- a/drivers/video/via/dvi.c +++ b/drivers/video/via/dvi.c @@ -467,7 +467,7 @@ static int dvi_get_panel_size_from_DDCv1(void) default: viaparinfo->tmds_setting_info->dvi_panel_size = VIA_RES_1024X768; - DEBUG_MSG(KERN_INFO "Unknow panel size max resolution = %d !\ + DEBUG_MSG(KERN_INFO "Unknown panel size max resolution = %d !\ set default panel size.\n", max_h); break; } @@ -534,7 +534,7 @@ static int dvi_get_panel_size_from_DDCv2(void) default: viaparinfo->tmds_setting_info->dvi_panel_size = VIA_RES_1024X768; - DEBUG_MSG(KERN_INFO "Unknow panel size max resolution = %d!\ + DEBUG_MSG(KERN_INFO "Unknown panel size max resolution = %d!\ set default panel size.\n", HSize); break; } diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c index 3df17dc8c3d7..65ccd215d496 100644 --- a/drivers/video/vt8623fb.c +++ b/drivers/video/vt8623fb.c @@ -446,7 +446,7 @@ static int vt8623fb_set_par(struct fb_info *info) svga_wseq_mask(0x1E, 0xF0, 0xF0); // DI/DVP bus svga_wseq_mask(0x2A, 0x0F, 0x0F); // DI/DVP bus - svga_wseq_mask(0x16, 0x08, 0xBF); // FIFO read treshold + svga_wseq_mask(0x16, 0x08, 0xBF); // FIFO read threshold vga_wseq(NULL, 0x17, 0x1F); // FIFO depth vga_wseq(NULL, 0x18, 0x4E); svga_wseq_mask(0x1A, 0x08, 0x08); // enable MMIO ? diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 54cd91610174..91a68e9eb66d 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -440,7 +440,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, fb_info->fix.type = FB_TYPE_PACKED_PIXELS; fb_info->fix.accel = FB_ACCEL_NONE; - fb_info->flags = FBINFO_FLAG_DEFAULT; + fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; ret = fb_alloc_cmap(&fb_info->cmap, 256, 0); if (ret < 0) { diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c index 381026c0bd7b..923cc68dba26 100644 --- a/drivers/watchdog/coh901327_wdt.c +++ b/drivers/watchdog/coh901327_wdt.c @@ -508,7 +508,7 @@ void coh901327_watchdog_reset(void) * deactivating the watchdog before it is shut down by it. * * NOTE: on future versions of the watchdog, this restriction is - * gone: the watchdog will be reloaded with a defaul value (1 min) + * gone: the watchdog will be reloaded with a default value (1 min) * instead of last value, and you can conveniently set the watchdog * timeout to 10ms (value = 1) without any problems. */ diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 6a51edde6ea7..e44fbb31bc6f 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -1,5 +1,5 @@ /* - * intel TCO Watchdog Driver (Used in i82801 and i63xxESB chipsets) + * intel TCO Watchdog Driver * * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>. * @@ -14,47 +14,22 @@ * * The TCO watchdog is implemented in the following I/O controller hubs: * (See the intel documentation on http://developer.intel.com.) - * 82801AA (ICH) : document number 290655-003, 290677-014, - * 82801AB (ICHO) : document number 290655-003, 290677-014, - * 82801BA (ICH2) : document number 290687-002, 298242-027, - * 82801BAM (ICH2-M) : document number 290687-002, 298242-027, - * 82801CA (ICH3-S) : document number 290733-003, 290739-013, - * 82801CAM (ICH3-M) : document number 290716-001, 290718-007, - * 82801DB (ICH4) : document number 290744-001, 290745-025, - * 82801DBM (ICH4-M) : document number 252337-001, 252663-008, - * 82801E (C-ICH) : document number 273599-001, 273645-002, - * 82801EB (ICH5) : document number 252516-001, 252517-028, - * 82801ER (ICH5R) : document number 252516-001, 252517-028, - * 6300ESB (6300ESB) : document number 300641-004, 300884-013, - * 82801FB (ICH6) : document number 301473-002, 301474-026, - * 82801FR (ICH6R) : document number 301473-002, 301474-026, - * 82801FBM (ICH6-M) : document number 301473-002, 301474-026, - * 82801FW (ICH6W) : document number 301473-001, 301474-026, - * 82801FRW (ICH6RW) : document number 301473-001, 301474-026, - * 631xESB (631xESB) : document number 313082-001, 313075-006, - * 632xESB (632xESB) : document number 313082-001, 313075-006, - * 82801GB (ICH7) : document number 307013-003, 307014-024, - * 82801GR (ICH7R) : document number 307013-003, 307014-024, - * 82801GDH (ICH7DH) : document number 307013-003, 307014-024, - * 82801GBM (ICH7-M) : document number 307013-003, 307014-024, - * 82801GHM (ICH7-M DH) : document number 307013-003, 307014-024, - * 82801GU (ICH7-U) : document number 307013-003, 307014-024, - * 82801HB (ICH8) : document number 313056-003, 313057-017, - * 82801HR (ICH8R) : document number 313056-003, 313057-017, - * 82801HBM (ICH8M) : document number 313056-003, 313057-017, - * 82801HH (ICH8DH) : document number 313056-003, 313057-017, - * 82801HO (ICH8DO) : document number 313056-003, 313057-017, - * 82801HEM (ICH8M-E) : document number 313056-003, 313057-017, - * 82801IB (ICH9) : document number 316972-004, 316973-012, - * 82801IR (ICH9R) : document number 316972-004, 316973-012, - * 82801IH (ICH9DH) : document number 316972-004, 316973-012, - * 82801IO (ICH9DO) : document number 316972-004, 316973-012, - * 82801IBM (ICH9M) : document number 316972-004, 316973-012, - * 82801IEM (ICH9M-E) : document number 316972-004, 316973-012, - * 82801JIB (ICH10) : document number 319973-002, 319974-002, - * 82801JIR (ICH10R) : document number 319973-002, 319974-002, - * 82801JD (ICH10D) : document number 319973-002, 319974-002, - * 82801JDO (ICH10DO) : document number 319973-002, 319974-002 + * document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO) + * document number 290687-002, 298242-027: 82801BA (ICH2) + * document number 290733-003, 290739-013: 82801CA (ICH3-S) + * document number 290716-001, 290718-007: 82801CAM (ICH3-M) + * document number 290744-001, 290745-025: 82801DB (ICH4) + * document number 252337-001, 252663-008: 82801DBM (ICH4-M) + * document number 273599-001, 273645-002: 82801E (C-ICH) + * document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R) + * document number 300641-004, 300884-013: 6300ESB + * document number 301473-002, 301474-026: 82801F (ICH6) + * document number 313082-001, 313075-006: 631xESB, 632xESB + * document number 307013-003, 307014-024: 82801G (ICH7) + * document number 313056-003, 313057-017: 82801H (ICH8) + * document number 316972-004, 316973-012: 82801I (ICH9) + * document number 319973-002, 319974-002: 82801J (ICH10) + * document number 322169-001, 322170-001: 5 Series, 3400 Series (PCH) */ /* @@ -122,6 +97,9 @@ enum iTCO_chipsets { TCO_ICH10R, /* ICH10R */ TCO_ICH10D, /* ICH10D */ TCO_ICH10DO, /* ICH10DO */ + TCO_PCH, /* PCH Desktop Full Featured */ + TCO_PCHM, /* PCH Mobile Full Featured */ + TCO_PCHMSFF, /* PCH Mobile SFF Full Featured */ }; static struct { @@ -162,6 +140,9 @@ static struct { {"ICH10R", 2}, {"ICH10D", 2}, {"ICH10DO", 2}, + {"PCH Desktop Full Featured", 2}, + {"PCH Mobile Full Featured", 2}, + {"PCH Mobile SFF Full Featured", 2}, {NULL, 0} }; @@ -230,6 +211,9 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = { { ITCO_PCI_DEVICE(0x3a16, TCO_ICH10R)}, { ITCO_PCI_DEVICE(0x3a1a, TCO_ICH10D)}, { ITCO_PCI_DEVICE(0x3a14, TCO_ICH10DO)}, + { ITCO_PCI_DEVICE(0x3b00, TCO_PCH)}, + { ITCO_PCI_DEVICE(0x3b01, TCO_PCHM)}, + { ITCO_PCI_DEVICE(0x3b0d, TCO_PCHMSFF)}, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c index b6b3f59ab446..47d719717a3b 100644 --- a/drivers/watchdog/machzwd.c +++ b/drivers/watchdog/machzwd.c @@ -21,7 +21,7 @@ * wd#1 - 2 seconds; * wd#2 - 7.2 ms; * After the expiration of wd#1, it can generate a NMI, SCI, SMI, or - * a system RESET and it starts wd#2 that unconditionaly will RESET + * a system RESET and it starts wd#2 that unconditionally will RESET * the system when the counter reaches zero. * * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 3ed571a2ab18..429ea99eaee5 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -43,7 +43,7 @@ #include <linux/io.h> #include <linux/uaccess.h> #include <mach/hardware.h> -#include <mach/prcm.h> +#include <plat/prcm.h> #include "omap_wdt.h" diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index d3c824dc2358..c14ae8676903 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -10,7 +10,6 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/miscdevice.h> -#include <linux/smp_lock.h> #include <linux/watchdog.h> #include <linux/of.h> #include <linux/of_device.h> @@ -75,7 +74,6 @@ static void riowd_writereg(struct riowd *p, u8 val, int index) static int riowd_open(struct inode *inode, struct file *filp) { - cycle_kernel_lock(); nonseekable_open(inode, filp); return 0; } @@ -194,6 +192,8 @@ static int __devinit riowd_probe(struct of_device *op, printk(KERN_ERR PFX "Cannot map registers.\n"); goto out_free; } + /* Make miscdev useable right away */ + riowd_device = p; err = misc_register(&riowd_miscdev); if (err) { @@ -205,10 +205,10 @@ static int __devinit riowd_probe(struct of_device *op, "regs at %p\n", riowd_timeout, p->regs); dev_set_drvdata(&op->dev, p); - riowd_device = p; return 0; out_iounmap: + riowd_device = NULL; of_iounmap(&op->resource[0], p->regs, 2); out_free: diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index b57ac6b49147..85b93e15d011 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -36,6 +36,7 @@ #include <linux/clk.h> #include <linux/uaccess.h> #include <linux/io.h> +#include <linux/cpufreq.h> #include <mach/map.h> @@ -142,9 +143,14 @@ static void s3c2410wdt_start(void) spin_unlock(&wdt_lock); } +static inline int s3c2410wdt_is_running(void) +{ + return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; +} + static int s3c2410wdt_set_heartbeat(int timeout) { - unsigned int freq = clk_get_rate(wdt_clock); + unsigned long freq = clk_get_rate(wdt_clock); unsigned int count; unsigned int divisor = 1; unsigned long wtcon; @@ -155,7 +161,7 @@ static int s3c2410wdt_set_heartbeat(int timeout) freq /= 128; count = timeout * freq; - DBG("%s: count=%d, timeout=%d, freq=%d\n", + DBG("%s: count=%d, timeout=%d, freq=%lu\n", __func__, count, timeout, freq); /* if the count is bigger than the watchdog register, @@ -324,6 +330,73 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param) s3c2410wdt_keepalive(); return IRQ_HANDLED; } + + +#ifdef CONFIG_CPU_FREQ + +static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + int ret; + + if (!s3c2410wdt_is_running()) + goto done; + + if (val == CPUFREQ_PRECHANGE) { + /* To ensure that over the change we don't cause the + * watchdog to trigger, we perform an keep-alive if + * the watchdog is running. + */ + + s3c2410wdt_keepalive(); + } else if (val == CPUFREQ_POSTCHANGE) { + s3c2410wdt_stop(); + + ret = s3c2410wdt_set_heartbeat(tmr_margin); + + if (ret >= 0) + s3c2410wdt_start(); + else + goto err; + } + +done: + return 0; + + err: + dev_err(wdt_dev, "cannot set new value for timeout %d\n", tmr_margin); + return ret; +} + +static struct notifier_block s3c2410wdt_cpufreq_transition_nb = { + .notifier_call = s3c2410wdt_cpufreq_transition, +}; + +static inline int s3c2410wdt_cpufreq_register(void) +{ + return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c2410wdt_cpufreq_deregister(void) +{ + cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c2410wdt_cpufreq_register(void) +{ + return 0; +} + +static inline void s3c2410wdt_cpufreq_deregister(void) +{ +} +#endif + + + /* device interface */ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) @@ -387,6 +460,11 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) clk_enable(wdt_clock); + if (s3c2410wdt_cpufreq_register() < 0) { + printk(KERN_ERR PFX "failed to register cpufreq\n"); + goto err_clk; + } + /* see if we can actually set the requested timer margin, and if * not, try the default value */ @@ -407,7 +485,7 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "cannot register miscdev on minor=%d (%d)\n", WATCHDOG_MINOR, ret); - goto err_clk; + goto err_cpufreq; } if (tmr_atboot && started == 0) { @@ -432,6 +510,9 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) return 0; + err_cpufreq: + s3c2410wdt_cpufreq_deregister(); + err_clk: clk_disable(wdt_clock); clk_put(wdt_clock); @@ -451,6 +532,8 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) static int __devexit s3c2410wdt_remove(struct platform_device *dev) { + s3c2410wdt_cpufreq_deregister(); + release_resource(wdt_mem); kfree(wdt_mem); wdt_mem = NULL; diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c index 9748eed73196..c8eadd478175 100644 --- a/drivers/watchdog/sb_wdog.c +++ b/drivers/watchdog/sb_wdog.c @@ -1,7 +1,7 @@ /* * Watchdog driver for SiByte SB1 SoCs * - * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@onstor.com> + * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@lsi.com> * * This driver is intended to make the second of two hardware watchdogs * on the Sibyte 12XX and 11XX SoCs available to the user. There are two @@ -326,7 +326,7 @@ static void __exit sbwdog_exit(void) module_init(sbwdog_init); module_exit(sbwdog_exit); -MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>"); +MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>"); MODULE_DESCRIPTION("SiByte Watchdog"); module_param(timeout, ulong, 0); diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c index 3bde56bce63a..5bfb1f2c5319 100644 --- a/drivers/watchdog/wdrtas.c +++ b/drivers/watchdog/wdrtas.c @@ -542,7 +542,7 @@ static struct notifier_block wdrtas_notifier = { /** * wdrtas_get_tokens - reads in RTAS tokens * - * returns 0 on succes, <0 on failure + * returns 0 on success, <0 on failure * * wdrtas_get_tokens reads in the tokens for the RTAS calls used in * this watchdog driver. It tolerates, if "get-sensor-state" and @@ -598,7 +598,7 @@ static void wdrtas_unregister_devs(void) /** * wdrtas_register_devs - registers the misc dev handlers * - * returns 0 on succes, <0 on failure + * returns 0 on success, <0 on failure * * wdrtas_register_devs registers the watchdog and temperature watchdog * misc devs @@ -630,7 +630,7 @@ static int wdrtas_register_devs(void) /** * wdrtas_init - init function of the watchdog driver * - * returns 0 on succes, <0 on failure + * returns 0 on success, <0 on failure * * registers the file handlers and the reboot notifier */ diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index d31505b6f7a4..420433613584 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -66,8 +66,6 @@ struct balloon_stats { /* We aim for 'current allocation' == 'target allocation'. */ unsigned long current_pages; unsigned long target_pages; - /* We may hit the hard limit in Xen. If we do then we remember it. */ - unsigned long hard_limit; /* * Drivers may alter the memory reservation independently, but they * must inform the balloon driver so we avoid hitting the hard limit. @@ -136,6 +134,8 @@ static void balloon_append(struct page *page) list_add(&page->lru, &ballooned_pages); balloon_stats.balloon_low++; } + + totalram_pages--; } /* balloon_retrieve: rescue a page from the balloon, if it is not empty. */ @@ -156,6 +156,8 @@ static struct page *balloon_retrieve(void) else balloon_stats.balloon_low--; + totalram_pages++; + return page; } @@ -181,7 +183,7 @@ static void balloon_alarm(unsigned long unused) static unsigned long current_target(void) { - unsigned long target = min(balloon_stats.target_pages, balloon_stats.hard_limit); + unsigned long target = balloon_stats.target_pages; target = min(target, balloon_stats.current_pages + @@ -217,23 +219,10 @@ static int increase_reservation(unsigned long nr_pages) set_xen_guest_handle(reservation.extent_start, frame_list); reservation.nr_extents = nr_pages; rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); - if (rc < nr_pages) { - if (rc > 0) { - int ret; - - /* We hit the Xen hard limit: reprobe. */ - reservation.nr_extents = rc; - ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, - &reservation); - BUG_ON(ret != rc); - } - if (rc >= 0) - balloon_stats.hard_limit = (balloon_stats.current_pages + rc - - balloon_stats.driver_pages); + if (rc < 0) goto out; - } - for (i = 0; i < nr_pages; i++) { + for (i = 0; i < rc; i++) { page = balloon_retrieve(); BUG_ON(page == NULL); @@ -259,13 +248,12 @@ static int increase_reservation(unsigned long nr_pages) __free_page(page); } - balloon_stats.current_pages += nr_pages; - totalram_pages = balloon_stats.current_pages; + balloon_stats.current_pages += rc; out: spin_unlock_irqrestore(&balloon_lock, flags); - return 0; + return rc < 0 ? rc : rc != nr_pages; } static int decrease_reservation(unsigned long nr_pages) @@ -323,7 +311,6 @@ static int decrease_reservation(unsigned long nr_pages) BUG_ON(ret != nr_pages); balloon_stats.current_pages -= nr_pages; - totalram_pages = balloon_stats.current_pages; spin_unlock_irqrestore(&balloon_lock, flags); @@ -367,7 +354,6 @@ static void balloon_process(struct work_struct *work) static void balloon_set_new_target(unsigned long target) { /* No need for lock. Not read-modify-write updates. */ - balloon_stats.hard_limit = ~0UL; balloon_stats.target_pages = target; schedule_work(&balloon_worker); } @@ -422,12 +408,10 @@ static int __init balloon_init(void) pr_info("xen_balloon: Initialising balloon driver.\n"); balloon_stats.current_pages = min(xen_start_info->nr_pages, max_pfn); - totalram_pages = balloon_stats.current_pages; balloon_stats.target_pages = balloon_stats.current_pages; balloon_stats.balloon_low = 0; balloon_stats.balloon_high = 0; balloon_stats.driver_pages = 0UL; - balloon_stats.hard_limit = ~0UL; init_timer(&balloon_timer); balloon_timer.data = 0; @@ -472,9 +456,6 @@ module_exit(balloon_exit); BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages)); BALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_low)); BALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_high)); -BALLOON_SHOW(hard_limit_kb, - (balloon_stats.hard_limit!=~0UL) ? "%lu\n" : "???\n", - (balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0); BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages)); static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr, @@ -544,7 +525,6 @@ static struct attribute *balloon_info_attrs[] = { &attr_current_kb.attr, &attr_low_kb.attr, &attr_high_kb.attr, - &attr_hard_limit_kb.attr, &attr_driver_kb.attr, NULL }; diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c index bdfd584ad853..0f765a920189 100644 --- a/drivers/xen/cpu_hotplug.c +++ b/drivers/xen/cpu_hotplug.c @@ -86,7 +86,7 @@ static int setup_cpu_watcher(struct notifier_block *notifier, for_each_possible_cpu(cpu) { if (vcpu_online(cpu) == 0) { (void)cpu_down(cpu); - cpu_clear(cpu, cpu_present_map); + set_cpu_present(cpu, false); } } diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 2f57276e87a2..ce602dd09bc1 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -474,6 +474,9 @@ static void unbind_from_irq(unsigned int irq) bind_evtchn_to_cpu(evtchn, 0); evtchn_to_irq[evtchn] = -1; + } + + if (irq_info[irq].type != IRQT_UNBOUND) { irq_info[irq] = mk_unbound_info(); dynamic_irq_cleanup(irq); diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 10d03d7931c4..c4997930afc7 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -43,7 +43,6 @@ static int xen_suspend(void *data) if (err) { printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n", err); - dpm_resume_noirq(PMSG_RESUME); return err; } @@ -69,7 +68,6 @@ static int xen_suspend(void *data) } sysdev_resume(); - dpm_resume_noirq(PMSG_RESUME); return 0; } @@ -81,6 +79,12 @@ static void do_suspend(void) shutting_down = SHUTDOWN_SUSPEND; + err = stop_machine_create(); + if (err) { + printk(KERN_ERR "xen suspend: failed to setup stop_machine %d\n", err); + goto out; + } + #ifdef CONFIG_PREEMPT /* If the kernel is preemptible, we need to freeze all the processes to prevent them from being in the middle of a pagetable update @@ -88,29 +92,32 @@ static void do_suspend(void) err = freeze_processes(); if (err) { printk(KERN_ERR "xen suspend: freeze failed %d\n", err); - return; + goto out_destroy_sm; } #endif err = dpm_suspend_start(PMSG_SUSPEND); if (err) { printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err); - goto out; + goto out_thaw; } - printk(KERN_DEBUG "suspending xenstore...\n"); - xs_suspend(); - err = dpm_suspend_noirq(PMSG_SUSPEND); if (err) { printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err); - goto resume_devices; + goto out_resume; } + printk(KERN_DEBUG "suspending xenstore...\n"); + xs_suspend(); + err = stop_machine(xen_suspend, &cancelled, cpumask_of(0)); + + dpm_resume_noirq(PMSG_RESUME); + if (err) { printk(KERN_ERR "failed to start xen_suspend: %d\n", err); - goto out; + cancelled = 1; } if (!cancelled) { @@ -119,17 +126,21 @@ static void do_suspend(void) } else xs_suspend_cancel(); - dpm_resume_noirq(PMSG_RESUME); - -resume_devices: +out_resume: dpm_resume_end(PMSG_RESUME); /* Make sure timer events get retriggered on all CPUs */ clock_was_set(); -out: + +out_thaw: #ifdef CONFIG_PREEMPT thaw_processes(); + +out_destroy_sm: #endif + stop_machine_destroy(); + +out: shutting_down = SHUTDOWN_INVALID; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index d42e25d5968d..649fcdf114b7 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -454,21 +454,21 @@ static ssize_t xendev_show_nodename(struct device *dev, { return sprintf(buf, "%s\n", to_xenbus_device(dev)->nodename); } -DEVICE_ATTR(nodename, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_nodename, NULL); +static DEVICE_ATTR(nodename, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_nodename, NULL); static ssize_t xendev_show_devtype(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", to_xenbus_device(dev)->devicetype); } -DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL); +static DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL); static ssize_t xendev_show_modalias(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "xen:%s\n", to_xenbus_device(dev)->devicetype); } -DEVICE_ATTR(modalias, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_modalias, NULL); +static DEVICE_ATTR(modalias, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_modalias, NULL); int xenbus_probe_node(struct xen_bus_type *bus, const char *type, @@ -843,7 +843,7 @@ postcore_initcall(xenbus_probe_init); MODULE_LICENSE("GPL"); -static int is_disconnected_device(struct device *dev, void *data) +static int is_device_connecting(struct device *dev, void *data) { struct xenbus_device *xendev = to_xenbus_device(dev); struct device_driver *drv = data; @@ -861,14 +861,15 @@ static int is_disconnected_device(struct device *dev, void *data) return 0; xendrv = to_xenbus_driver(dev->driver); - return (xendev->state != XenbusStateConnected || - (xendrv->is_ready && !xendrv->is_ready(xendev))); + return (xendev->state < XenbusStateConnected || + (xendev->state == XenbusStateConnected && + xendrv->is_ready && !xendrv->is_ready(xendev))); } -static int exists_disconnected_device(struct device_driver *drv) +static int exists_connecting_device(struct device_driver *drv) { return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, - is_disconnected_device); + is_device_connecting); } static int print_device_status(struct device *dev, void *data) @@ -884,10 +885,13 @@ static int print_device_status(struct device *dev, void *data) /* Information only: is this too noisy? */ printk(KERN_INFO "XENBUS: Device with no driver: %s\n", xendev->nodename); - } else if (xendev->state != XenbusStateConnected) { + } else if (xendev->state < XenbusStateConnected) { + enum xenbus_state rstate = XenbusStateUnknown; + if (xendev->otherend) + rstate = xenbus_read_driver_state(xendev->otherend); printk(KERN_WARNING "XENBUS: Timeout connecting " - "to device: %s (state %d)\n", - xendev->nodename, xendev->state); + "to device: %s (local state %d, remote state %d)\n", + xendev->nodename, xendev->state, rstate); } return 0; @@ -897,7 +901,7 @@ static int print_device_status(struct device *dev, void *data) static int ready_to_wait_for_devices; /* - * On a 10 second timeout, wait for all devices currently configured. We need + * On a 5-minute timeout, wait for all devices currently configured. We need * to do this to guarantee that the filesystems and / or network devices * needed for boot are available, before we can allow the boot to proceed. * @@ -912,18 +916,30 @@ static int ready_to_wait_for_devices; */ static void wait_for_devices(struct xenbus_driver *xendrv) { - unsigned long timeout = jiffies + 10*HZ; + unsigned long start = jiffies; struct device_driver *drv = xendrv ? &xendrv->driver : NULL; + unsigned int seconds_waited = 0; if (!ready_to_wait_for_devices || !xen_domain()) return; - while (exists_disconnected_device(drv)) { - if (time_after(jiffies, timeout)) - break; + while (exists_connecting_device(drv)) { + if (time_after(jiffies, start + (seconds_waited+5)*HZ)) { + if (!seconds_waited) + printk(KERN_WARNING "XENBUS: Waiting for " + "devices to initialise: "); + seconds_waited += 5; + printk("%us...", 300 - seconds_waited); + if (seconds_waited == 300) + break; + } + schedule_timeout_interruptible(HZ/10); } + if (seconds_waited) + printk("\n"); + bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, print_device_status); } diff --git a/drivers/zorro/zorro-driver.c b/drivers/zorro/zorro-driver.c index e6c4390d8bd6..53180a37cc9a 100644 --- a/drivers/zorro/zorro-driver.c +++ b/drivers/zorro/zorro-driver.c @@ -156,5 +156,4 @@ postcore_initcall(zorro_driver_init); EXPORT_SYMBOL(zorro_match_device); EXPORT_SYMBOL(zorro_register_driver); EXPORT_SYMBOL(zorro_unregister_driver); -EXPORT_SYMBOL(zorro_dev_driver); EXPORT_SYMBOL(zorro_bus_type); |